feat: ship lime 1.6.1 rollout

This commit is contained in:
coso
2026-04-09 18:47:33 +08:00
parent db3bfae4ff
commit 182c0d464a
152 changed files with 19877 additions and 2413 deletions
@@ -0,0 +1,56 @@
#!/usr/bin/env node
import { spawnSync } from "node:child_process";
import path from "node:path";
import process from "node:process";
import { fileURLToPath } from "node:url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const rootDir = path.resolve(__dirname, "..");
const npmCommand = process.platform === "win32" ? "npm.cmd" : "npm";
function runVitest(label, args) {
console.log(`\n[smoke:agent-service-skill-entry] > ${label}`);
const result = spawnSync(
npmCommand,
["exec", "--", "vitest", "run", ...args],
{
cwd: rootDir,
stdio: "inherit",
env: process.env,
},
);
if (result.error) {
throw result.error;
}
if (typeof result.status === "number" && result.status !== 0) {
const error = new Error(
`[smoke:agent-service-skill-entry] ${label} 失败`,
);
error.exitCode = result.status;
throw error;
}
}
function main() {
runVitest("服务技能入口路由与挂起参数", [
"src/components/skills/SkillsWorkspacePage.test.tsx",
"src/components/agent/chat/workspace/useWorkspaceServiceSkillEntryActions.test.tsx",
"src/components/agent/chat/index.shell-routing.test.tsx",
"src/components/AppPageContent.test.tsx",
]);
runVitest("Agent 对话内 A2UI 挂起主链", [
"src/components/agent/chat/index.test.tsx",
"--hookTimeout=60000",
"-t",
"AgentChatPage 服务技能 A2UI|AgentChatPage legacy 问卷 A2UI",
]);
console.log("\n[smoke:agent-service-skill-entry] 通过");
}
main();
+37 -12
View File
@@ -7,6 +7,7 @@ const DEFAULTS = {
invokeUrl: "http://127.0.0.1:3030/invoke",
timeoutMs: 60_000,
intervalMs: 1_000,
invokeTimeoutMs: 20_000,
};
function printHelp() {
@@ -24,6 +25,7 @@ Lime Site Adapter Catalog Smoke
--invoke-url <url> DevBridge invoke 地址,默认 http://127.0.0.1:3030/invoke
--timeout-ms <ms> 等待健康检查超时,默认 60000
--interval-ms <ms> 健康检查轮询间隔,默认 1000
--invoke-timeout-ms <ms> 单次 invoke 超时,默认 20000
-h, --help 显示帮助
`);
}
@@ -53,6 +55,11 @@ function parseArgs(argv) {
index += 1;
continue;
}
if (arg === "--invoke-timeout-ms" && argv[index + 1]) {
options.invokeTimeoutMs = Number(argv[index + 1]);
index += 1;
continue;
}
if (arg === "--help" || arg === "-h") {
printHelp();
process.exit(0);
@@ -65,6 +72,12 @@ function parseArgs(argv) {
if (!Number.isFinite(options.intervalMs) || options.intervalMs < 100) {
throw new Error("--interval-ms 必须是 >= 100 的数字");
}
if (
!Number.isFinite(options.invokeTimeoutMs) ||
options.invokeTimeoutMs < 1_000
) {
throw new Error("--invoke-timeout-ms 必须是 >= 1000 的数字");
}
return options;
}
@@ -111,14 +124,26 @@ async function waitForHealth(options) {
);
}
async function invoke(invokeUrl, cmd, args) {
const response = await fetch(invokeUrl, {
method: "POST",
headers: {
"content-type": "application/json",
},
body: JSON.stringify({ cmd, args }),
});
async function invoke(options, cmd, args) {
console.log(`[smoke:site-adapters] invoke ${cmd}`);
let response;
try {
response = await fetch(options.invokeUrl, {
method: "POST",
headers: {
"content-type": "application/json",
},
body: JSON.stringify({ cmd, args }),
signal: AbortSignal.timeout(options.invokeTimeoutMs),
});
} catch (error) {
if (error?.name === "TimeoutError") {
throw new Error(
`[smoke:site-adapters] ${cmd} 超时,${options.invokeTimeoutMs}ms 内未收到 DevBridge 响应`,
);
}
throw error;
}
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
@@ -140,7 +165,7 @@ async function main() {
const options = parseArgs(process.argv.slice(2));
await waitForHealth(options);
const status = await invoke(options.invokeUrl, "site_get_adapter_catalog_status");
const status = await invoke(options, "site_get_adapter_catalog_status");
assert(status && typeof status === "object", "site_get_adapter_catalog_status 返回为空");
assert(
typeof status.adapter_count === "number" && status.adapter_count >= 0,
@@ -151,7 +176,7 @@ async function main() {
"site_get_adapter_catalog_status 返回了未知 source_kind",
);
const adapters = await invoke(options.invokeUrl, "site_list_adapters");
const adapters = await invoke(options, "site_list_adapters");
assert(Array.isArray(adapters), "site_list_adapters 返回不是数组");
assert(adapters.length > 0, "site_list_adapters 返回为空");
@@ -165,7 +190,7 @@ async function main() {
"site_list_adapters 首项缺少 domain",
);
const recommendations = await invoke(options.invokeUrl, "site_recommend_adapters", {
const recommendations = await invoke(options, "site_recommend_adapters", {
request: {
limit: 3,
},
@@ -189,7 +214,7 @@ async function main() {
);
}
const searchResults = await invoke(options.invokeUrl, "site_search_adapters", {
const searchResults = await invoke(options, "site_search_adapters", {
request: {
query: adapter.name,
},
+20 -1
View File
@@ -132,15 +132,23 @@ function formatCommand(command, args) {
return [command, ...args].join(" ");
}
function runCommand(command, args, label) {
function runCommand(command, args, label, timeoutMs) {
console.log(`\n[verify:gui-smoke] > ${formatCommand(command, args)}`);
const result = spawnSync(command, args, {
cwd: rootDir,
stdio: "inherit",
env: process.env,
timeout: timeoutMs,
});
if (result.error) {
if (result.error.code === "ETIMEDOUT") {
const error = new Error(
`[verify:gui-smoke] ${label} 超时(>${timeoutMs}ms)`,
);
error.exitCode = 124;
throw error;
}
throw result.error;
}
@@ -341,6 +349,7 @@ async function main() {
String(options.intervalMs),
],
"bridge:health",
options.timeoutMs + 5_000,
);
await waitForAppShell(options);
@@ -359,6 +368,7 @@ async function main() {
options.sampleProjectName,
],
"smoke:workspace-ready",
options.timeoutMs + 5_000,
);
runCommand(
@@ -374,6 +384,7 @@ async function main() {
"--headless",
],
"smoke:browser-runtime",
options.timeoutMs + 5_000,
);
runCommand(
@@ -388,6 +399,14 @@ async function main() {
String(options.intervalMs),
],
"smoke:site-adapters",
options.timeoutMs + 5_000,
);
runCommand(
npmCommand,
["run", "smoke:agent-service-skill-entry"],
"smoke:agent-service-skill-entry",
options.timeoutMs + 30_000,
);
console.log("\n[verify:gui-smoke] 通过");