mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 08:10:44 +00:00
test(qa): support packaged gateway live lanes
This commit is contained in:
@@ -54,6 +54,13 @@ export type QaGatewayChildStateMutationContext = {
|
||||
tempRoot: string;
|
||||
};
|
||||
|
||||
export type QaGatewayChildCommand = {
|
||||
executablePath: string;
|
||||
argsPrefix?: string[];
|
||||
cwd?: string;
|
||||
usePackagedPlugins?: boolean;
|
||||
};
|
||||
|
||||
async function getFreePort() {
|
||||
return await new Promise<number>((resolve, reject) => {
|
||||
const server = net.createServer();
|
||||
@@ -435,6 +442,7 @@ export function resolveQaControlUiRoot(params: { repoRoot: string; controlUiEnab
|
||||
|
||||
export async function startQaGatewayChild(params: {
|
||||
repoRoot: string;
|
||||
command?: QaGatewayChildCommand;
|
||||
providerBaseUrl?: string;
|
||||
transport: Pick<QaTransportAdapter, "requiredPluginIds" | "createGatewayConfig">;
|
||||
transportBaseUrl: string;
|
||||
@@ -455,6 +463,10 @@ export async function startQaGatewayChild(params: {
|
||||
);
|
||||
const runtimeCwd = tempRoot;
|
||||
const distEntryPath = path.join(params.repoRoot, "dist", "index.js");
|
||||
const gatewayCommand = params.command;
|
||||
const gatewayExecutablePath = gatewayCommand?.executablePath;
|
||||
const gatewayArgsPrefix = gatewayCommand?.argsPrefix ?? [];
|
||||
const gatewayCwd = gatewayCommand?.cwd ?? runtimeCwd;
|
||||
const workspaceDir = path.join(tempRoot, "workspace");
|
||||
const stateDir = path.join(tempRoot, "state");
|
||||
const homeDir = path.join(tempRoot, "home");
|
||||
@@ -558,7 +570,17 @@ export async function startQaGatewayChild(params: {
|
||||
let env: NodeJS.ProcessEnv | null = null;
|
||||
|
||||
try {
|
||||
const nodeExecPath = await resolveQaNodeExecPath();
|
||||
const nodeExecPath = gatewayExecutablePath ?? (await resolveQaNodeExecPath());
|
||||
const buildGatewayArgs = () => [
|
||||
...(gatewayExecutablePath ? gatewayArgsPrefix : [distEntryPath, ...gatewayArgsPrefix]),
|
||||
"gateway",
|
||||
"run",
|
||||
"--port",
|
||||
String(gatewayPort),
|
||||
"--bind",
|
||||
"loopback",
|
||||
"--allow-unconfigured",
|
||||
];
|
||||
for (let attempt = 1; attempt <= QA_GATEWAY_CHILD_STARTUP_MAX_ATTEMPTS; attempt += 1) {
|
||||
gatewayPort = await getFreePort();
|
||||
baseUrl = `http://127.0.0.1:${gatewayPort}`;
|
||||
@@ -574,16 +596,22 @@ export async function startQaGatewayChild(params: {
|
||||
);
|
||||
},
|
||||
);
|
||||
const { bundledPluginsDir, stagedRoot } = await createQaBundledPluginsDir({
|
||||
repoRoot: params.repoRoot,
|
||||
tempRoot,
|
||||
allowedPluginIds,
|
||||
});
|
||||
stagedBundledPluginsRoot = stagedRoot;
|
||||
const runtimeHostVersion = await resolveQaRuntimeHostVersion({
|
||||
repoRoot: params.repoRoot,
|
||||
allowedPluginIds,
|
||||
});
|
||||
const stagedPluginRuntime = gatewayCommand?.usePackagedPlugins
|
||||
? { bundledPluginsDir: undefined, runtimeHostVersion: undefined }
|
||||
: {
|
||||
...(await createQaBundledPluginsDir({
|
||||
repoRoot: params.repoRoot,
|
||||
tempRoot,
|
||||
allowedPluginIds,
|
||||
})),
|
||||
runtimeHostVersion: await resolveQaRuntimeHostVersion({
|
||||
repoRoot: params.repoRoot,
|
||||
allowedPluginIds,
|
||||
}),
|
||||
};
|
||||
if ("stagedRoot" in stagedPluginRuntime) {
|
||||
stagedBundledPluginsRoot = stagedPluginRuntime.stagedRoot;
|
||||
}
|
||||
env = buildQaRuntimeEnv({
|
||||
configPath,
|
||||
gatewayToken,
|
||||
@@ -593,8 +621,8 @@ export async function startQaGatewayChild(params: {
|
||||
xdgConfigHome,
|
||||
xdgDataHome,
|
||||
xdgCacheHome,
|
||||
bundledPluginsDir,
|
||||
compatibilityHostVersion: runtimeHostVersion,
|
||||
bundledPluginsDir: stagedPluginRuntime.bundledPluginsDir,
|
||||
compatibilityHostVersion: stagedPluginRuntime.runtimeHostVersion,
|
||||
providerMode,
|
||||
forwardHostHomeForClaudeCli: liveProviderIds.includes("claude-cli"),
|
||||
claudeCliAuthMode: params.claudeCliAuthMode,
|
||||
@@ -608,25 +636,12 @@ export async function startQaGatewayChild(params: {
|
||||
throw new Error("qa gateway runtime env not initialized");
|
||||
}
|
||||
|
||||
const attemptChild = spawn(
|
||||
nodeExecPath,
|
||||
[
|
||||
distEntryPath,
|
||||
"gateway",
|
||||
"run",
|
||||
"--port",
|
||||
String(gatewayPort),
|
||||
"--bind",
|
||||
"loopback",
|
||||
"--allow-unconfigured",
|
||||
],
|
||||
{
|
||||
cwd: runtimeCwd,
|
||||
env,
|
||||
detached: process.platform !== "win32",
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
},
|
||||
);
|
||||
const attemptChild = spawn(nodeExecPath, buildGatewayArgs(), {
|
||||
cwd: gatewayCwd,
|
||||
env,
|
||||
detached: process.platform !== "win32",
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
});
|
||||
attemptChild.stdout.on("data", (chunk) => {
|
||||
const buffer = Buffer.from(chunk);
|
||||
stdout.push(buffer);
|
||||
@@ -714,25 +729,12 @@ export async function startQaGatewayChild(params: {
|
||||
const runningEnv = env;
|
||||
|
||||
const spawnReplacementGatewayChild = async () => {
|
||||
const nextChild = spawn(
|
||||
nodeExecPath,
|
||||
[
|
||||
distEntryPath,
|
||||
"gateway",
|
||||
"run",
|
||||
"--port",
|
||||
String(gatewayPort),
|
||||
"--bind",
|
||||
"loopback",
|
||||
"--allow-unconfigured",
|
||||
],
|
||||
{
|
||||
cwd: runtimeCwd,
|
||||
env: runningEnv,
|
||||
detached: process.platform !== "win32",
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
},
|
||||
);
|
||||
const nextChild = spawn(nodeExecPath, buildGatewayArgs(), {
|
||||
cwd: gatewayCwd,
|
||||
env: runningEnv,
|
||||
detached: process.platform !== "win32",
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
});
|
||||
nextChild.stdout.on("data", (chunk) => {
|
||||
const buffer = Buffer.from(chunk);
|
||||
stdout.push(buffer);
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
|
||||
import { startQaGatewayChild, type QaCliBackendAuthMode } from "../../gateway-child.js";
|
||||
import {
|
||||
startQaGatewayChild,
|
||||
type QaCliBackendAuthMode,
|
||||
type QaGatewayChildCommand,
|
||||
} from "../../gateway-child.js";
|
||||
import type { QaProviderMode } from "../../model-selection.js";
|
||||
import { startQaProviderServer } from "../../providers/server-runtime.js";
|
||||
import type { QaThinkingLevel } from "../../qa-gateway-config.js";
|
||||
@@ -32,6 +36,7 @@ async function stopQaLiveLaneResources(
|
||||
|
||||
export async function startQaLiveLaneGateway(params: {
|
||||
repoRoot: string;
|
||||
command?: QaGatewayChildCommand;
|
||||
transport: {
|
||||
requiredPluginIds: readonly string[];
|
||||
createGatewayConfig: (params: {
|
||||
@@ -53,6 +58,7 @@ export async function startQaLiveLaneGateway(params: {
|
||||
try {
|
||||
const gateway = await startQaGatewayChild({
|
||||
repoRoot: params.repoRoot,
|
||||
command: params.command,
|
||||
providerBaseUrl: mock ? `${mock.baseUrl}/v1` : undefined,
|
||||
transport: params.transport,
|
||||
transportBaseUrl: params.transportBaseUrl,
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { execFile } from "node:child_process";
|
||||
import { randomUUID } from "node:crypto";
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { promisify } from "node:util";
|
||||
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
|
||||
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
||||
import { fetchWithSsrFGuard } from "openclaw/plugin-sdk/ssrf-runtime";
|
||||
@@ -306,6 +308,7 @@ const QA_REDACT_PUBLIC_METADATA_ENV = "OPENCLAW_QA_REDACT_PUBLIC_METADATA";
|
||||
const QA_SUITE_PROGRESS_ENV = "OPENCLAW_QA_SUITE_PROGRESS";
|
||||
const TELEGRAM_QA_PROGRESS_DETAIL_LIMIT = 240;
|
||||
const TELEGRAM_QA_PROGRESS_PREFIX = "[qa-telegram-live]";
|
||||
const execFileAsync = promisify(execFile);
|
||||
|
||||
const telegramQaCredentialPayloadSchema = z.object({
|
||||
groupId: z.string().trim().min(1),
|
||||
@@ -962,9 +965,63 @@ function canaryFailureMessage(params: {
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
async function runInstalledOpenClawTelegramOnboardingPreflight(params: {
|
||||
openClawCommand: string;
|
||||
sutToken: string;
|
||||
}) {
|
||||
const tempRoot = await fs.mkdtemp(path.join(process.cwd(), ".tmp-openclaw-npm-telegram-"));
|
||||
const homeDir = path.join(tempRoot, "home");
|
||||
const stateDir = path.join(homeDir, ".openclaw");
|
||||
await fs.mkdir(stateDir, { recursive: true });
|
||||
const env = {
|
||||
...process.env,
|
||||
HOME: homeDir,
|
||||
OPENCLAW_HOME: stateDir,
|
||||
OPENCLAW_CONFIG_PATH: path.join(stateDir, "openclaw.json"),
|
||||
OPENCLAW_STATE_DIR: stateDir,
|
||||
OPENCLAW_GATEWAY_TOKEN: "npm-telegram-live-onboard",
|
||||
};
|
||||
try {
|
||||
await execFileAsync(
|
||||
params.openClawCommand,
|
||||
[
|
||||
"onboard",
|
||||
"--non-interactive",
|
||||
"--accept-risk",
|
||||
"--mode",
|
||||
"local",
|
||||
"--auth-choice",
|
||||
"openai-api-key",
|
||||
"--secret-input-mode",
|
||||
"ref",
|
||||
"--gateway-port",
|
||||
"18789",
|
||||
"--gateway-bind",
|
||||
"loopback",
|
||||
"--skip-daemon",
|
||||
"--skip-ui",
|
||||
"--skip-skills",
|
||||
"--skip-health",
|
||||
"--json",
|
||||
],
|
||||
{ env },
|
||||
);
|
||||
await execFileAsync(
|
||||
params.openClawCommand,
|
||||
["channels", "add", "--channel", "telegram", "--token", params.sutToken],
|
||||
{ env },
|
||||
);
|
||||
await execFileAsync(params.openClawCommand, ["doctor", "--non-interactive"], { env });
|
||||
} finally {
|
||||
await fs.rm(tempRoot, { recursive: true, force: true }).catch(() => {});
|
||||
}
|
||||
}
|
||||
|
||||
export async function runTelegramQaLive(params: {
|
||||
repoRoot?: string;
|
||||
outputDir?: string;
|
||||
sutOpenClawCommand?: string;
|
||||
preflightInstalledOnboarding?: boolean;
|
||||
providerMode?: QaProviderModeInput;
|
||||
primaryModel?: string;
|
||||
alternateModel?: string;
|
||||
@@ -1022,6 +1079,15 @@ export async function runTelegramQaLive(params: {
|
||||
const cleanupIssues: string[] = [];
|
||||
let canaryFailure: string | null = null;
|
||||
try {
|
||||
if (params.sutOpenClawCommand && params.preflightInstalledOnboarding === true) {
|
||||
writeTelegramQaProgress(progressEnabled, "installed package onboarding preflight start");
|
||||
await runInstalledOpenClawTelegramOnboardingPreflight({
|
||||
openClawCommand: params.sutOpenClawCommand,
|
||||
sutToken: runtimeEnv.sutToken,
|
||||
});
|
||||
writeTelegramQaProgress(progressEnabled, "installed package onboarding preflight pass");
|
||||
}
|
||||
|
||||
const driverIdentity = await getBotIdentity(runtimeEnv.driverToken);
|
||||
const sutIdentity = await getBotIdentity(runtimeEnv.sutToken);
|
||||
const sutUsername = sutIdentity.username?.trim();
|
||||
@@ -1040,6 +1106,12 @@ export async function runTelegramQaLive(params: {
|
||||
|
||||
const gatewayHarness = await startQaLiveLaneGateway({
|
||||
repoRoot,
|
||||
command: params.sutOpenClawCommand
|
||||
? {
|
||||
executablePath: params.sutOpenClawCommand,
|
||||
usePackagedPlugins: true,
|
||||
}
|
||||
: undefined,
|
||||
transport: {
|
||||
requiredPluginIds: [],
|
||||
createGatewayConfig: () => ({}),
|
||||
|
||||
Reference in New Issue
Block a user