mirror of
http://192.168.0.88:13333/lywsvip/openclaw-zero-token.git
synced 2026-05-08 00:02:20 +08:00
Major upgrade from e26988a38 to upstream v2026.3.28 (f9b107928).
Key changes:
- Upstream src/, ui/, extensions/ (89 bundled extensions)
- Zero-token web providers preserved in src/zero-token/
- AskOnce plugin restored and registered as CLI command
- Added missing packages: @anthropic-ai/vertex-sdk, @modelcontextprotocol/sdk
- Fixed tsconfig rootDir, skipLibCheck for plugin-sdk DTS build
- Added askonce to bundled plugin metadata and package.json exports
- Fixed AskOnce CLI command registration (missing commands metadata)
- Restored AskOnce adapter imports (correct 5-level relative paths)
- Removed stale migration artifacts from root directory
81 lines
2.8 KiB
TypeScript
81 lines
2.8 KiB
TypeScript
import type { ChildProcess } from "node:child_process";
|
|
import { EventEmitter } from "node:events";
|
|
import { PassThrough } from "node:stream";
|
|
import { describe, expect, it, vi } from "vitest";
|
|
import { createRestartIterationHook } from "./restart-recovery.js";
|
|
import { spawnWithFallback } from "./spawn-utils.js";
|
|
|
|
function createStubChild() {
|
|
const child = new EventEmitter() as ChildProcess;
|
|
child.stdin = new PassThrough() as ChildProcess["stdin"];
|
|
child.stdout = new PassThrough() as ChildProcess["stdout"];
|
|
child.stderr = new PassThrough() as ChildProcess["stderr"];
|
|
Object.defineProperty(child, "pid", { value: 1234, configurable: true });
|
|
Object.defineProperty(child, "killed", { value: false, configurable: true, writable: true });
|
|
child.kill = vi.fn(() => true) as ChildProcess["kill"];
|
|
queueMicrotask(() => {
|
|
child.emit("spawn");
|
|
});
|
|
return child;
|
|
}
|
|
|
|
describe("spawnWithFallback", () => {
|
|
it("retries on EBADF using fallback options", async () => {
|
|
const spawnMock = vi
|
|
.fn()
|
|
.mockImplementationOnce(() => {
|
|
const err = new Error("spawn EBADF");
|
|
(err as NodeJS.ErrnoException).code = "EBADF";
|
|
throw err;
|
|
})
|
|
.mockImplementationOnce(() => createStubChild());
|
|
|
|
const result = await spawnWithFallback({
|
|
argv: ["echo", "ok"],
|
|
options: { stdio: ["pipe", "pipe", "pipe"] },
|
|
fallbacks: [{ label: "safe-stdin", options: { stdio: ["ignore", "pipe", "pipe"] } }],
|
|
spawnImpl: spawnMock,
|
|
});
|
|
|
|
expect(result.usedFallback).toBe(true);
|
|
expect(result.fallbackLabel).toBe("safe-stdin");
|
|
expect(spawnMock).toHaveBeenCalledTimes(2);
|
|
expect(spawnMock.mock.calls[0]?.[2]?.stdio).toEqual(["pipe", "pipe", "pipe"]);
|
|
expect(spawnMock.mock.calls[1]?.[2]?.stdio).toEqual(["ignore", "pipe", "pipe"]);
|
|
});
|
|
|
|
it("does not retry on non-EBADF errors", async () => {
|
|
const spawnMock = vi.fn().mockImplementationOnce(() => {
|
|
const err = new Error("spawn ENOENT");
|
|
(err as NodeJS.ErrnoException).code = "ENOENT";
|
|
throw err;
|
|
});
|
|
|
|
await expect(
|
|
spawnWithFallback({
|
|
argv: ["missing"],
|
|
options: { stdio: ["pipe", "pipe", "pipe"] },
|
|
fallbacks: [{ label: "safe-stdin", options: { stdio: ["ignore", "pipe", "pipe"] } }],
|
|
spawnImpl: spawnMock,
|
|
}),
|
|
).rejects.toThrow(/ENOENT/);
|
|
expect(spawnMock).toHaveBeenCalledTimes(1);
|
|
});
|
|
});
|
|
|
|
describe("restart-recovery", () => {
|
|
it("skips recovery on first iteration and runs on subsequent iterations", () => {
|
|
const onRestart = vi.fn();
|
|
const onIteration = createRestartIterationHook(onRestart);
|
|
|
|
expect(onIteration()).toBe(false);
|
|
expect(onRestart).not.toHaveBeenCalled();
|
|
|
|
expect(onIteration()).toBe(true);
|
|
expect(onRestart).toHaveBeenCalledTimes(1);
|
|
|
|
expect(onIteration()).toBe(true);
|
|
expect(onRestart).toHaveBeenCalledTimes(2);
|
|
});
|
|
});
|