mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-10 18:24:57 +00:00
fix: extend managed-tab cap regressions + changelog (#29724) (thanks @pandego)
This commit is contained in:
@@ -62,6 +62,7 @@ Docs: https://docs.openclaw.ai
|
|||||||
- CLI/Browser start timeout: honor `openclaw browser --timeout <ms> start` and stop by removing the fixed 15000ms override so slower Chrome startups can use caller-provided timeouts. (#22412, #23427) Thanks @vincentkoc.
|
- CLI/Browser start timeout: honor `openclaw browser --timeout <ms> start` and stop by removing the fixed 15000ms override so slower Chrome startups can use caller-provided timeouts. (#22412, #23427) Thanks @vincentkoc.
|
||||||
- Browser/CDP startup diagnostics: include Chrome stderr output and a Linux no-sandbox hint in startup timeout errors so failed launches are easier to diagnose. (#29312) Thanks @veast.
|
- Browser/CDP startup diagnostics: include Chrome stderr output and a Linux no-sandbox hint in startup timeout errors so failed launches are easier to diagnose. (#29312) Thanks @veast.
|
||||||
- Browser/CDP startup readiness: wait for CDP websocket readiness after launching Chrome and cleanly stop/reset when readiness never arrives, reducing follow-up `PortInUseError` races after `browser start`/`open`. (#29538) Thanks @AaronWander.
|
- Browser/CDP startup readiness: wait for CDP websocket readiness after launching Chrome and cleanly stop/reset when readiness never arrives, reducing follow-up `PortInUseError` races after `browser start`/`open`. (#29538) Thanks @AaronWander.
|
||||||
|
- Browser/Managed tab cap: limit loopback managed `openclaw` page tabs to 8 via best-effort cleanup after tab opens to reduce long-running renderer buildup while preserving attach-only and remote profile behavior. (#29724) Thanks @pandego.
|
||||||
- Docker/Image health checks: add Dockerfile `HEALTHCHECK` that probes gateway `GET /healthz` so container runtimes can mark unhealthy instances without requiring auth credentials in the probe command. (#11478) Thanks @U-C4N and @vincentkoc.
|
- Docker/Image health checks: add Dockerfile `HEALTHCHECK` that probes gateway `GET /healthz` so container runtimes can mark unhealthy instances without requiring auth credentials in the probe command. (#11478) Thanks @U-C4N and @vincentkoc.
|
||||||
- Docker/Sandbox bootstrap hardening: make `OPENCLAW_SANDBOX` opt-in parsing explicit (`1|true|yes|on`), support custom Docker socket paths via `OPENCLAW_DOCKER_SOCKET`, defer docker.sock exposure until sandbox prerequisites pass, and reset/roll back persisted sandbox mode to `off` when setup is skipped or partially fails to avoid stale broken sandbox state. (#29974) Thanks @jamtujest and @vincentkoc.
|
- Docker/Sandbox bootstrap hardening: make `OPENCLAW_SANDBOX` opt-in parsing explicit (`1|true|yes|on`), support custom Docker socket paths via `OPENCLAW_DOCKER_SOCKET`, defer docker.sock exposure until sandbox prerequisites pass, and reset/roll back persisted sandbox mode to `off` when setup is skipped or partially fails to avoid stale broken sandbox state. (#29974) Thanks @jamtujest and @vincentkoc.
|
||||||
- Daemon/systemd checks in containers: treat missing `systemctl` invocations (including `spawn systemctl ENOENT`/`EACCES`) as unavailable service state during `is-enabled` checks, preventing container flows from failing with `Gateway service check failed` before install/status handling can continue. (#26089) Thanks @sahilsatralkar and @vincentkoc.
|
- Daemon/systemd checks in containers: treat missing `systemctl` invocations (including `spawn systemctl ENOENT`/`EACCES`) as unavailable service state during `is-enabled` checks, preventing container flows from failing with `Gateway service check failed` before install/status handling can continue. (#26089) Thanks @sahilsatralkar and @vincentkoc.
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ function makeBrowserState(): BrowserServerState {
|
|||||||
cdpProtocol: "http",
|
cdpProtocol: "http",
|
||||||
cdpHost: "127.0.0.1",
|
cdpHost: "127.0.0.1",
|
||||||
cdpIsLoopback: true,
|
cdpIsLoopback: true,
|
||||||
cdpPortRangeStart: null,
|
cdpPortRangeStart: 18800,
|
||||||
cdpPortRangeEnd: null,
|
cdpPortRangeEnd: 18810,
|
||||||
evaluateEnabled: false,
|
evaluateEnabled: false,
|
||||||
remoteCdpTimeoutMs: 1500,
|
remoteCdpTimeoutMs: 1500,
|
||||||
remoteCdpHandshakeTimeoutMs: 3000,
|
remoteCdpHandshakeTimeoutMs: 3000,
|
||||||
|
|||||||
@@ -494,6 +494,113 @@ describe("browser server-context tab selection state", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("never closes the just-opened managed tab during cap cleanup", async () => {
|
||||||
|
vi.spyOn(cdpModule, "createTargetViaCdp").mockResolvedValue({ targetId: "NEW" });
|
||||||
|
|
||||||
|
const existingTabs = [
|
||||||
|
{
|
||||||
|
id: "NEW",
|
||||||
|
title: "9",
|
||||||
|
url: "http://127.0.0.1:3009",
|
||||||
|
webSocketDebuggerUrl: "ws://127.0.0.1/devtools/page/NEW",
|
||||||
|
type: "page",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "OLD1",
|
||||||
|
title: "1",
|
||||||
|
url: "http://127.0.0.1:3001",
|
||||||
|
webSocketDebuggerUrl: "ws://127.0.0.1/devtools/page/OLD1",
|
||||||
|
type: "page",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "OLD2",
|
||||||
|
title: "2",
|
||||||
|
url: "http://127.0.0.1:3002",
|
||||||
|
webSocketDebuggerUrl: "ws://127.0.0.1/devtools/page/OLD2",
|
||||||
|
type: "page",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "OLD3",
|
||||||
|
title: "3",
|
||||||
|
url: "http://127.0.0.1:3003",
|
||||||
|
webSocketDebuggerUrl: "ws://127.0.0.1/devtools/page/OLD3",
|
||||||
|
type: "page",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "OLD4",
|
||||||
|
title: "4",
|
||||||
|
url: "http://127.0.0.1:3004",
|
||||||
|
webSocketDebuggerUrl: "ws://127.0.0.1/devtools/page/OLD4",
|
||||||
|
type: "page",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "OLD5",
|
||||||
|
title: "5",
|
||||||
|
url: "http://127.0.0.1:3005",
|
||||||
|
webSocketDebuggerUrl: "ws://127.0.0.1/devtools/page/OLD5",
|
||||||
|
type: "page",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "OLD6",
|
||||||
|
title: "6",
|
||||||
|
url: "http://127.0.0.1:3006",
|
||||||
|
webSocketDebuggerUrl: "ws://127.0.0.1/devtools/page/OLD6",
|
||||||
|
type: "page",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "OLD7",
|
||||||
|
title: "7",
|
||||||
|
url: "http://127.0.0.1:3007",
|
||||||
|
webSocketDebuggerUrl: "ws://127.0.0.1/devtools/page/OLD7",
|
||||||
|
type: "page",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "OLD8",
|
||||||
|
title: "8",
|
||||||
|
url: "http://127.0.0.1:3008",
|
||||||
|
webSocketDebuggerUrl: "ws://127.0.0.1/devtools/page/OLD8",
|
||||||
|
type: "page",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const fetchMock = vi.fn(async (url: unknown) => {
|
||||||
|
const value = String(url);
|
||||||
|
if (value.includes("/json/list")) {
|
||||||
|
return { ok: true, json: async () => existingTabs } as unknown as Response;
|
||||||
|
}
|
||||||
|
if (value.includes("/json/close/OLD1")) {
|
||||||
|
return { ok: true, json: async () => ({}) } as unknown as Response;
|
||||||
|
}
|
||||||
|
if (value.includes("/json/close/NEW")) {
|
||||||
|
throw new Error("cleanup must not close NEW");
|
||||||
|
}
|
||||||
|
throw new Error(`unexpected fetch: ${value}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
global.fetch = withFetchPreconnect(fetchMock);
|
||||||
|
const state = makeState("openclaw");
|
||||||
|
(state.profiles as Map<string, unknown>).set("openclaw", {
|
||||||
|
profile: { name: "openclaw" },
|
||||||
|
running: { pid: 1234, proc: { on: vi.fn() } },
|
||||||
|
lastTargetId: null,
|
||||||
|
});
|
||||||
|
const ctx = createBrowserRouteContext({ getState: () => state });
|
||||||
|
const openclaw = ctx.forProfile("openclaw");
|
||||||
|
|
||||||
|
const opened = await openclaw.openTab("http://127.0.0.1:3009");
|
||||||
|
expect(opened.targetId).toBe("NEW");
|
||||||
|
await vi.waitFor(() => {
|
||||||
|
expect(fetchMock).toHaveBeenCalledWith(
|
||||||
|
expect.stringContaining("/json/close/OLD1"),
|
||||||
|
expect.any(Object),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
expect(fetchMock).not.toHaveBeenCalledWith(
|
||||||
|
expect.stringContaining("/json/close/NEW"),
|
||||||
|
expect.anything(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it("does not fail tab open when managed-tab cleanup list fails", async () => {
|
it("does not fail tab open when managed-tab cleanup list fails", async () => {
|
||||||
vi.spyOn(cdpModule, "createTargetViaCdp").mockResolvedValue({ targetId: "NEW" });
|
vi.spyOn(cdpModule, "createTargetViaCdp").mockResolvedValue({ targetId: "NEW" });
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user