chore: release v1.41.0

This commit is contained in:
coso
2026-05-16 12:11:31 +08:00
parent 403e1f449b
commit 3c4e33b207
81 changed files with 11781 additions and 378 deletions
+255
View File
@@ -0,0 +1,255 @@
#!/usr/bin/env node
import { execFileSync } from "node:child_process";
import crypto from "node:crypto";
import fs from "node:fs";
import path from "node:path";
import process from "node:process";
import { createAgentAppPackageHandoffReport } from "./lib/agent-app-package-handoff-core.mjs";
function parseArgs(argv) {
const result = {
check: false,
format: "summary",
help: false,
packageDir: "",
};
for (let index = 0; index < argv.length; index += 1) {
const arg = argv[index];
if ((arg === "--package-dir" || arg === "--package") && argv[index + 1]) {
result.packageDir = String(argv[index + 1]).trim();
index += 1;
continue;
}
if (arg === "--format" && argv[index + 1]) {
result.format = String(argv[index + 1]).trim();
index += 1;
continue;
}
if (arg === "--check") {
result.check = true;
continue;
}
if (arg === "--help" || arg === "-h") {
result.help = true;
}
}
return result;
}
function printHelp() {
console.log(`
Agent App Package Handoff Check
用法:
node scripts/agent-app-package-handoff-check.mjs --package-dir <agent-app-package>
node scripts/agent-app-package-handoff-check.mjs --package-dir <agent-app-package> --format json
node scripts/agent-app-package-handoff-check.mjs --package-dir <agent-app-package> --check
选项:
--package-dir PATH 要只读检查的 Agent App package 根目录。
--format FMT summary | json,默认 summary。
--check 若 package 仍 blocked / needs_handoff,则以非 0 退出。
-h, --help 显示帮助。
`);
}
function readFileIfExists(filePath) {
try {
return {
exists: fs.existsSync(filePath),
content: fs.existsSync(filePath) ? fs.readFileSync(filePath, "utf8") : "",
};
} catch {
return {
exists: false,
content: "",
};
}
}
function collectGitStatusShort(packageDir) {
try {
return execFileSync("git", ["-C", packageDir, "status", "--short"], {
encoding: "utf8",
stdio: ["ignore", "pipe", "pipe"],
});
} catch {
return "";
}
}
function walkFiles(rootDir) {
if (!fs.existsSync(rootDir)) {
return [];
}
const entries = [];
for (const name of fs.readdirSync(rootDir)) {
const filePath = path.join(rootDir, name);
const stat = fs.statSync(filePath);
if (stat.isDirectory()) {
entries.push(...walkFiles(filePath));
continue;
}
if (stat.isFile()) {
entries.push(filePath);
}
}
return entries;
}
function fileHash(filePath) {
return crypto.createHash("sha256").update(fs.readFileSync(filePath)).digest("hex").slice(0, 12);
}
function collectDistArtifacts(packageDir) {
const entries = [];
const pairs = [
["src/ui", "dist/ui"],
["src/core", "dist/core"],
["src/integrations", "dist/integrations"],
];
for (const [srcRootRelative, distRootRelative] of pairs) {
const srcRoot = path.join(packageDir, srcRootRelative);
const distRoot = path.join(packageDir, distRootRelative);
for (const srcPath of walkFiles(srcRoot)) {
const relativePath = path.relative(srcRoot, srcPath);
const distPath = path.join(distRoot, relativePath);
if (!fs.existsSync(distPath)) {
entries.push({
status: "missing-dist",
src: path.relative(packageDir, srcPath),
dist: path.relative(packageDir, distPath),
srcHash: fileHash(srcPath),
distHash: "",
});
continue;
}
const srcHash = fileHash(srcPath);
const distHash = fileHash(distPath);
entries.push({
status: srcHash === distHash ? "same" : "diff",
src: path.relative(packageDir, srcPath),
dist: path.relative(packageDir, distPath),
srcHash,
distHash,
});
}
for (const distPath of walkFiles(distRoot)) {
const relativePath = path.relative(distRoot, distPath);
const srcPath = path.join(srcRoot, relativePath);
if (!fs.existsSync(srcPath)) {
entries.push({
status: "extra-dist",
src: path.relative(packageDir, srcPath),
dist: path.relative(packageDir, distPath),
srcHash: "",
distHash: fileHash(distPath),
});
}
}
}
const workerSrc = path.join(packageDir, "src", "worker", "index.mjs");
const workerDist = path.join(packageDir, "dist", "worker", "index.mjs");
if (fs.existsSync(workerSrc)) {
if (!fs.existsSync(workerDist)) {
entries.push({
status: "missing-dist",
src: path.relative(packageDir, workerSrc),
dist: path.relative(packageDir, workerDist),
srcHash: fileHash(workerSrc),
distHash: "",
});
} else {
const srcHash = fileHash(workerSrc);
const distHash = fileHash(workerDist);
entries.push({
status: srcHash === distHash ? "same" : "diff",
src: path.relative(packageDir, workerSrc),
dist: path.relative(packageDir, workerDist),
srcHash,
distHash,
});
}
}
return entries;
}
function createReport(packageDir) {
const resolvedPackageDir = path.resolve(packageDir);
const hostBridgePath = path.join(resolvedPackageDir, "src", "ui", "host-bridge.js");
const uiTestPath = path.join(resolvedPackageDir, "tests", "ui.test.mjs");
const packageJsonPath = path.join(resolvedPackageDir, "package.json");
const buildScriptPath = path.join(resolvedPackageDir, "scripts", "build.mjs");
const packageJson = readFileIfExists(packageJsonPath);
return createAgentAppPackageHandoffReport({
packageDir: resolvedPackageDir,
gitStatusShort: collectGitStatusShort(resolvedPackageDir),
files: {
hostBridge: readFileIfExists(hostBridgePath),
uiTest: readFileIfExists(uiTestPath),
buildScript: readFileIfExists(buildScriptPath),
distArtifacts: collectDistArtifacts(resolvedPackageDir),
},
packageJsonText: packageJson.content,
});
}
function renderMarkerSummary(markers) {
if (!markers.length) {
return "none";
}
return markers.map((entry) => `${entry.marker}:${entry.count}`).join(", ");
}
function renderSummary(report) {
const lines = [
`status=${report.verdict.status}`,
`packageDir=${report.packageDir}`,
`dirty=tracked:${report.gitStatus.trackedCount},untracked:${report.gitStatus.untrackedCount},total:${report.gitStatus.totalCount}`,
`hostBridgePrivate=${renderMarkerSummary(report.files.hostBridge.privateMarkers)}`,
`hostBridgeSdk=${renderMarkerSummary(report.files.hostBridge.sdkMarkers)}`,
`uiTestPrivate=${renderMarkerSummary(report.files.uiTest.privateMarkers)}`,
`highRiskScripts=${report.scripts.highRisk.map((entry) => entry.name).join(",") || "none"}`,
`distArtifacts=diff:${report.distArtifacts.diffCount},missing:${report.distArtifacts.missingDistCount},extra:${report.distArtifacts.extraDistCount},total:${report.distArtifacts.totalDeltas}`,
`blockers=${report.verdict.blockers.join(" | ") || "none"}`,
`warnings=${report.verdict.warnings.join(" | ") || "none"}`,
`nextAction=${report.verdict.nextAction}`,
];
return `${lines.join("\n")}\n`;
}
function main() {
const args = parseArgs(process.argv.slice(2));
if (args.help) {
printHelp();
return;
}
if (!args.packageDir) {
console.error("缺少 --package-dir PATH。");
printHelp();
process.exitCode = 2;
return;
}
const report = createReport(args.packageDir);
if (args.format === "json") {
console.log(JSON.stringify(report, null, 2));
} else {
process.stdout.write(renderSummary(report));
}
if (args.check && report.verdict.status !== "ready") {
process.exitCode = 1;
}
}
main();
+60 -15
View File
@@ -15,6 +15,12 @@ const DEFAULTS = {
prefix: "agent-apps-smoke",
};
const ACCOUNT_MENU_BUTTON_SELECTOR = '[data-testid="app-sidebar-account-button"]';
const AGENT_APPS_NAV_SELECTOR =
'button[aria-label="Agent Apps"], button[title="Agent Apps"]';
const AGENT_APP_LAB_NAV_SELECTOR =
'button[aria-label="Agent App Lab"], button[title="Agent App Lab"]';
function parseArgs(argv) {
const options = { ...DEFAULTS };
for (let index = 0; index < argv.length; index += 1) {
@@ -66,6 +72,21 @@ function logStage(stage) {
console.log(`[smoke:agent-apps] stage=${stage}`);
}
async function openAccountMenuForAgentApps(page, timeoutMs) {
if ((await page.locator(AGENT_APPS_NAV_SELECTOR).count()) > 0) {
return;
}
await page.click(ACCOUNT_MENU_BUTTON_SELECTOR);
await page.waitForSelector(AGENT_APPS_NAV_SELECTOR, {
timeout: timeoutMs,
});
}
async function clickAgentAppsNav(page, timeoutMs) {
await openAccountMenuForAgentApps(page, timeoutMs);
await page.locator(AGENT_APPS_NAV_SELECTOR).first().click();
}
async function launchSmokeContext(userDataDir) {
try {
return await chromium.launchPersistentContext(userDataDir, {
@@ -127,8 +148,8 @@ function activeCloudBootstrapPayload() {
registrationState: "active",
enabled: true,
packageUrl: "https://lime.local/agent-apps/content-factory-app/releases/0.3.0/package.zip",
packageHash: "package-fnv1a-smoke-formal",
manifestHash: "manifest-fnv1a-smoke-formal",
packageHash: "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
manifestHash: "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
capabilityRequirements: {},
defaultEntries: ["dashboard", "content_scenario_planning"],
policyDefaults: {},
@@ -158,10 +179,11 @@ async function runFlagOffRegression(options) {
await page.waitForSelector('[data-testid="app-sidebar-main-nav"]', {
timeout: options.timeoutMs,
});
await openAccountMenuForAgentApps(page, options.timeoutMs);
const assertions = {
agentAppsNavVisible: (await page.locator('button[title="Agent Apps"]').count()) > 0,
labNavHidden: (await page.locator('button[title="Agent App Lab"]').count()) === 0,
agentAppsNavVisible: (await page.locator(AGENT_APPS_NAV_SELECTOR).count()) > 0,
labNavHidden: (await page.locator(AGENT_APP_LAB_NAV_SELECTOR).count()) === 0,
noConsoleErrors: consoleErrors.length === 0,
};
@@ -200,6 +222,16 @@ async function main() {
}, bootstrap);
const page = await context.newPage();
await page.route(
"https://user.limeai.run/api/v1/public/tenants/*/client/agent-apps",
async (route) => {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify(bootstrap),
});
},
);
page.on("console", (message) => {
if (message.type() === "error") {
consoleErrors.push(message.text());
@@ -219,17 +251,18 @@ async function main() {
await page.waitForSelector('[data-testid="app-sidebar-main-nav"]', {
timeout: options.timeoutMs,
});
await openAccountMenuForAgentApps(page, options.timeoutMs);
assert(
(await page.locator('button[title="Agent Apps"]').count()) > 0,
(await page.locator(AGENT_APPS_NAV_SELECTOR).count()) > 0,
"Agent Apps nav should be visible",
);
assert(
(await page.locator('button[title="Agent App Lab"]').count()) === 0,
(await page.locator(AGENT_APP_LAB_NAV_SELECTOR).count()) === 0,
"Agent App Lab nav should stay hidden in formal smoke",
);
logStage("open-agent-apps");
await page.click('button[title="Agent Apps"]');
await clickAgentAppsNav(page, options.timeoutMs);
await page.waitForSelector('[data-testid="agent-apps-page"]', {
timeout: options.timeoutMs,
});
@@ -247,20 +280,30 @@ async function main() {
logStage("activate-bootstrap-catalog");
await page.evaluate((payload) => {
window.__LIME_BOOTSTRAP__ = payload;
window.__LIME_OEM_CLOUD__ = {
enabled: true,
baseUrl: "https://user.limeai.run",
tenantId: payload.tenantId,
};
window.__LIME_SESSION_TOKEN__ = "smoke-agent-apps-token";
window.__LIME_BOOTSTRAP__ = { data: { agentAppCatalog: payload } };
}, bootstrap);
await page.click('[data-testid="agent-apps-refresh"]');
await page.waitForFunction(
() =>
!document
.querySelector('[data-testid="agent-apps-install-cloud-content-factory-app"]')
?.hasAttribute("disabled"),
() => {
const button = document.querySelector(
'[data-testid="agent-apps-install-cloud-content-factory-app"]',
);
return button instanceof HTMLButtonElement && !button.disabled;
},
undefined,
{ timeout: options.timeoutMs },
);
logStage("install-cloud-review");
await page.click('[data-testid="agent-apps-install-cloud-content-factory-app"]');
await page.click('[data-testid="agent-apps-install-cloud-content-factory-app"]', {
timeout: options.timeoutMs,
});
await page.waitForSelector('[data-testid="agent-apps-install-review"]', {
timeout: options.timeoutMs,
});
@@ -308,7 +351,7 @@ async function main() {
);
logStage("return-agent-apps");
await page.click('button[title="Agent Apps"]');
await clickAgentAppsNav(page, options.timeoutMs);
await page.waitForSelector('[data-testid="agent-apps-page"]', {
timeout: options.timeoutMs,
});
@@ -348,7 +391,9 @@ async function main() {
runtimeSurfaceVisible: Boolean(runtimeFrameSrc),
cleanupEvidenceSelectedApp: cleanupEvidence.appId === "content-factory-app",
cleanupEvidenceStrategy: cleanupEvidence.strategy === "delete-data",
cleanupEvidenceRehearsalOnly: cleanupEvidence.completionEffect === "rehearsal-only",
cleanupEvidenceDryRunOnly:
Array.isArray(cleanupEvidence.warningCodes) &&
cleanupEvidence.warningCodes.includes("DRY_RUN_ONLY"),
cleanupEvidenceBlockedCount: cleanupEvidence.blockedTargetCount === 0,
flagOffAgentAppsNavVisible: flagOff.assertions.agentAppsNavVisible,
flagOffLabNavHidden: flagOff.assertions.labNavHidden,
@@ -0,0 +1,237 @@
const PRIVATE_BRIDGE_MARKERS = [
"pendingRequests",
"buildMessage",
"requestHostBridge",
"window.parent.postMessage",
"capability:invoke",
"capability:subscribe",
"capability:event",
];
const SDK_FACADE_MARKERS = [
"createLimeHostBridgeCapabilityInvoker",
"createLimeCoreCapabilityAdapters",
];
const HIGH_RISK_SCRIPT_PATTERNS = [
{
scriptName: "build",
pattern: /\brm\s*\(|\brimraf\b|rm\s+-rf\s+dist|remove\s*\(\s*["']dist["']/i,
reason: "build script may delete or rewrite dist artifacts",
},
{
scriptName: "verify",
pattern: /\bnpm\s+run\s+build\b|\bpnpm\s+run\s+build\b|\byarn\s+build\b/i,
reason: "verify script runs build before validation",
},
{
scriptName: "e2e",
pattern: /\bnpm\s+run\s+build\b|\bpnpm\s+run\s+build\b|\byarn\s+build\b/i,
reason: "e2e script runs build before user-flow validation",
},
];
function normalizeText(value) {
return String(value || "");
}
function countMarkerHits(text, markers) {
const content = normalizeText(text);
return markers
.map((marker) => ({
marker,
count: content.split(marker).length - 1,
}))
.filter((entry) => entry.count > 0);
}
function parseGitStatusShort(output) {
const entries = normalizeText(output)
.split(/\r?\n/)
.map((line) => line.trimEnd())
.filter(Boolean)
.map((line) => ({
raw: line,
status: line.slice(0, 2),
path: line.slice(3).trim(),
untracked: line.startsWith("??"),
}));
const tracked = entries.filter((entry) => !entry.untracked);
const untracked = entries.filter((entry) => entry.untracked);
return {
entries,
trackedCount: tracked.length,
untrackedCount: untracked.length,
totalCount: entries.length,
};
}
function parsePackageJson(packageJsonText) {
if (!normalizeText(packageJsonText).trim()) {
return { ok: false, scripts: {}, error: "package_json_missing" };
}
try {
const parsed = JSON.parse(packageJsonText);
return {
ok: true,
scripts: parsed?.scripts && typeof parsed.scripts === "object" ? parsed.scripts : {},
error: "",
};
} catch (error) {
return {
ok: false,
scripts: {},
error: `package_json_invalid:${error instanceof Error ? error.message : String(error)}`,
};
}
}
function analyzeScripts(scripts, scriptFiles = {}) {
const entries = Object.entries(scripts || {}).map(([name, command]) => ({
name,
command: normalizeText(command),
}));
const highRisk = [];
for (const entry of entries) {
for (const rule of HIGH_RISK_SCRIPT_PATTERNS) {
const nameMatches =
rule.scriptName === "e2e"
? entry.name.toLowerCase().includes("e2e")
: entry.name === rule.scriptName;
const inspectedText =
entry.name === "build"
? `${entry.command}\n${normalizeText(scriptFiles.build)}`
: entry.command;
if (nameMatches && rule.pattern.test(inspectedText)) {
highRisk.push({
name: entry.name,
command: entry.command,
reason: rule.reason,
});
}
}
}
return {
entries,
highRisk,
};
}
function createFileMarkerReport({ exists, content }) {
return {
exists: Boolean(exists),
privateMarkers: countMarkerHits(content, PRIVATE_BRIDGE_MARKERS),
sdkMarkers: countMarkerHits(content, SDK_FACADE_MARKERS),
};
}
function createDistArtifactReport(entries = []) {
const normalizedEntries = entries.map((entry) => ({
status: normalizeText(entry.status),
src: normalizeText(entry.src),
dist: normalizeText(entry.dist),
srcHash: normalizeText(entry.srcHash),
distHash: normalizeText(entry.distHash),
}));
const deltas = normalizedEntries.filter((entry) => entry.status !== "same");
return {
entries: normalizedEntries,
deltas,
totalDeltas: deltas.length,
diffCount: deltas.filter((entry) => entry.status === "diff").length,
missingDistCount: deltas.filter((entry) => entry.status === "missing-dist").length,
extraDistCount: deltas.filter((entry) => entry.status === "extra-dist").length,
};
}
function createVerdict({ gitStatus, hostBridge, uiTest, scripts, distArtifacts }) {
const blockers = [];
const warnings = [];
if (!hostBridge.exists) {
blockers.push("src/ui/host-bridge.js is missing");
}
if (!uiTest.exists) {
warnings.push("tests/ui.test.mjs is missing; package-side bridge regression is not visible");
}
if (hostBridge.privateMarkers.length > 0 || uiTest.privateMarkers.length > 0) {
blockers.push("private Host Bridge transport markers are still present");
}
if (hostBridge.sdkMarkers.length === 0) {
blockers.push("SDK facade markers are not present in src/ui/host-bridge.js");
}
if (gitStatus.totalCount > 0) {
warnings.push(
`package worktree is dirty: tracked=${gitStatus.trackedCount}, untracked=${gitStatus.untrackedCount}`,
);
}
if (scripts.highRisk.length > 0) {
warnings.push("build/verify/e2e scripts may rewrite dist artifacts");
}
if (distArtifacts.totalDeltas > 0) {
warnings.push(`dist artifacts are not synchronized: ${distArtifacts.totalDeltas} delta(s)`);
}
const status = blockers.length > 0 ? "blocked" : warnings.length > 0 ? "needs_handoff" : "ready";
const nextAction =
status === "ready"
? "Run package tests and the agreed package verify gate."
: status === "needs_handoff"
? "Confirm owner handoff before changing or rebuilding package artifacts."
: "Do not claim P18.5.3 complete; remove private bridge transport through the SDK facade first.";
return {
status,
blockers,
warnings,
nextAction,
};
}
function createAgentAppPackageHandoffReport({
generatedAt = new Date().toISOString(),
packageDir = "",
gitStatusShort = "",
files = {},
packageJsonText = "",
} = {}) {
const gitStatus = parseGitStatusShort(gitStatusShort);
const hostBridge = createFileMarkerReport(files.hostBridge || {});
const uiTest = createFileMarkerReport(files.uiTest || {});
const packageJson = parsePackageJson(packageJsonText);
const scripts = analyzeScripts(packageJson.scripts, {
build: files.buildScript?.content,
});
const distArtifacts = createDistArtifactReport(files.distArtifacts || []);
const verdict = createVerdict({ gitStatus, hostBridge, uiTest, scripts, distArtifacts });
return {
schemaVersion: "v1",
generatedAt,
packageDir,
gitStatus,
files: {
hostBridge,
uiTest,
},
packageJson,
scripts,
distArtifacts,
verdict,
};
}
export {
PRIVATE_BRIDGE_MARKERS,
SDK_FACADE_MARKERS,
analyzeScripts,
createAgentAppPackageHandoffReport,
createDistArtifactReport,
countMarkerHits,
parseGitStatusShort,
};
@@ -0,0 +1,155 @@
import { describe, expect, it } from "vitest";
import {
analyzeScripts,
createAgentAppPackageHandoffReport,
createDistArtifactReport,
countMarkerHits,
parseGitStatusShort,
} from "./agent-app-package-handoff-core.mjs";
describe("agent-app-package-handoff-core", () => {
it("统计 git status 的 tracked / untracked 条目", () => {
const status = parseGitStatusShort(" M src/ui/app.js\n?? src/ui/host-bridge.js\n");
expect(status.trackedCount).toBe(1);
expect(status.untrackedCount).toBe(1);
expect(status.totalCount).toBe(2);
});
it("识别私有 Host Bridge marker 与 SDK marker", () => {
const markers = countMarkerHits(
"pendingRequests requestHostBridge createLimeHostBridgeCapabilityInvoker",
["pendingRequests", "requestHostBridge", "createLimeHostBridgeCapabilityInvoker"],
);
expect(markers.map((entry) => entry.marker)).toEqual([
"pendingRequests",
"requestHostBridge",
"createLimeHostBridgeCapabilityInvoker",
]);
});
it("把仍保留私有 bridge transport 的 package 判为 blocked", () => {
const report = createAgentAppPackageHandoffReport({
generatedAt: "2026-05-16T00:00:00.000Z",
packageDir: "/tmp/content-factory-app",
gitStatusShort: " M src/ui/app.js\n?? src/ui/host-bridge.js\n",
files: {
hostBridge: {
exists: true,
content: "const pendingRequests = new Map(); window.parent.postMessage({ type: 'capability:invoke' });",
},
uiTest: {
exists: true,
content: "message.type === 'capability:invoke'",
},
},
packageJsonText: JSON.stringify({
scripts: {
test: "node --test tests/*.test.mjs",
verify: "npm run build && npm run test",
},
}),
});
expect(report.verdict.status).toBe("blocked");
expect(report.verdict.blockers).toContain("private Host Bridge transport markers are still present");
expect(report.verdict.warnings).toContain("package worktree is dirty: tracked=1, untracked=1");
});
it("无私有 marker 但 dirty 时要求 handoff", () => {
const report = createAgentAppPackageHandoffReport({
gitStatusShort: " M src/ui/host-bridge.js\n",
files: {
hostBridge: {
exists: true,
content: "createLimeHostBridgeCapabilityInvoker(); createLimeCoreCapabilityAdapters();",
},
uiTest: {
exists: true,
content: "sdkInvokerCallLog",
},
},
packageJsonText: JSON.stringify({
scripts: {
test: "node --test tests/*.test.mjs",
},
}),
});
expect(report.verdict.status).toBe("needs_handoff");
expect(report.verdict.blockers).toEqual([]);
});
it("识别会重建 dist 的 build / verify / e2e 脚本", () => {
const scripts = analyzeScripts(
{
build: "node scripts/build.mjs",
verify: "npm run build && npm run test",
"e2e:user-flow": "npm run build && playwright test",
},
{
build: "await rm('dist', { recursive: true, force: true });",
},
);
expect(scripts.highRisk.map((entry) => entry.name)).toEqual([
"build",
"verify",
"e2e:user-flow",
]);
});
it("统计 src / dist 产物漂移并作为 handoff warning", () => {
const distArtifacts = createDistArtifactReport([
{
status: "same",
src: "src/ui/app.js",
dist: "dist/ui/app.js",
srcHash: "aaa",
distHash: "aaa",
},
{
status: "diff",
src: "src/ui/host-bridge.js",
dist: "dist/ui/host-bridge.js",
srcHash: "bbb",
distHash: "ccc",
},
{
status: "missing-dist",
src: "src/ui/lime-app-sdk.js",
dist: "dist/ui/lime-app-sdk.js",
srcHash: "ddd",
distHash: "",
},
]);
expect(distArtifacts.totalDeltas).toBe(2);
expect(distArtifacts.diffCount).toBe(1);
expect(distArtifacts.missingDistCount).toBe(1);
const report = createAgentAppPackageHandoffReport({
files: {
hostBridge: {
exists: true,
content: "createLimeHostBridgeCapabilityInvoker(); createLimeCoreCapabilityAdapters();",
},
uiTest: {
exists: true,
content: "sdkInvokerCallLog",
},
distArtifacts: distArtifacts.entries,
},
packageJsonText: JSON.stringify({
scripts: {
test: "node --test tests/*.test.mjs",
},
}),
});
expect(report.verdict.status).toBe("needs_handoff");
expect(report.verdict.warnings).toContain("dist artifacts are not synchronized: 2 delta(s)");
});
});
+19
View File
@@ -1591,6 +1591,25 @@ async function main() {
options.timeoutMs + 30_000,
);
await runCommand(
npmCommand,
[
"run",
"smoke:agent-apps",
"--",
"--app-url",
options.appUrl,
"--health-url",
options.healthUrl,
"--timeout-ms",
String(options.timeoutMs),
"--interval-ms",
String(options.intervalMs),
],
"smoke:agent-apps",
options.timeoutMs + 30_000,
);
await runCommand(
npmCommand,
[