import assert from "node:assert/strict"; import fs from "node:fs"; import path from "node:path"; import { fileURLToPath, pathToFileURL } from "node:url"; import { assertCloudWebDistFresh, buildCloudWebDist, readCloudWebAppSource } from "./dist-contract.ts"; const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); const repoRoot = path.resolve(rootDir, "../.."); const requiredFiles = Object.freeze([ "index.html", "styles.css", "app.ts", "app-skills.ts", "app-device-pod.ts", "app-conversation.ts", "app-trace.ts", "app-helpers.ts", "composer-policy.ts", "auth.ts", "code-agent-facts.ts", "code-agent-status.ts", "message-markdown.ts", "live-status.ts", "runtime.ts", "favicon.svg", "favicon.ico", "help.md", "third_party/marked/marked.esm.js", "third_party/marked/LICENSE" ]); for (const file of requiredFiles) { const filePath = path.resolve(rootDir, file); if (!fs.existsSync(filePath)) throw new Error(`missing web asset: ${file}`); } console.log("hwlab-cloud-web check: runtime assets present"); await buildCloudWebDist(rootDir); console.log("hwlab-cloud-web check: dist bundle built"); await assertCloudWebDistFresh(rootDir); console.log("hwlab-cloud-web check: dist freshness verified"); const html = readWeb("index.html"); const styles = readWeb("styles.css"); const app = readCloudWebAppSource(rootDir); assertTraceHelpersBound(readWeb("app-trace.ts")); const liveStatus = readWeb("live-status.ts"); const helpMarkdown = readWeb("help.md"); const faviconSvg = readWeb("favicon.svg"); const faviconIco = readWeb("favicon.ico"); const distContractScript = readWeb("scripts/dist-contract.ts"); const cloudApiServer = readRepo("internal/cloud/server.ts"); const cloudApiPayloads = readRepo("internal/cloud/server-rest-payloads.ts"); const devicePodData = readRepo("internal/device-pod/fake-data.mjs"); const devicePodService = readRepo("cmd/hwlab-device-pod/main.ts"); const protocol = readRepo("internal/protocol/index.mjs"); const deployJson = readRepo("deploy/deploy.json"); const artifactCatalog = readRepo("deploy/artifact-catalog.dev.json"); console.log("hwlab-cloud-web check: source files loaded"); const rightSidebar = sectionById(html, "aside", "device-pod-sidebar"); console.log("hwlab-cloud-web check: right sidebar extracted"); assert.ok(rightSidebar, "Device Pod right sidebar must exist"); assert.match(html, /data-route="skills"/u, "Cloud Web must expose the skills activity route"); assert.match(html, /id="skills"[^>]*data-view="skills"/u, "Cloud Web must expose a skills view"); assert.match(html, /id="skill-upload-input"[^>]*webkitdirectory/u, "Skills view must support directory upload"); assertIncludes(app, "/v1/skills", "skills frontend must call /v1/skills"); assertIncludes(app, "/v1/skills/uploads", "skills frontend must call /v1/skills/uploads"); assert.match(app, /function\s+initSkillsPanel\s*\(/u, "missing initSkillsPanel"); assert.match(app, /function\s+loadSkillsSurface\s*\(/u, "missing loadSkillsSurface"); assert.match(app, /function\s+uploadSelectedSkillFiles\s*\(/u, "missing uploadSelectedSkillFiles"); assert.match(app, /function\s+renderSkillPreview\s*\(/u, "missing renderSkillPreview"); console.log("hwlab-cloud-web check: skills contracts verified"); for (const term of [ "Device Pod", "设备目标看板", "id=\"device-pod-select\"", "id=\"device-pod-summary\"", "data-device-detail=\"pod\"", "id=\"device-pod-interfaces\"", "id=\"device-event-scroll\"", "id=\"device-event-text\"", "id=\"device-detail-dialog\"" ]) { assertIncludes(rightSidebar, term, `right sidebar missing ${term}`); } assert.doesNotMatch(rightSidebar, /]*role="button"[^>]*tabindex="0"/u, "Pod summary is a click/keyboard summary tile"); assert.match(rightSidebar, /