From c0d31ab8865570660a3b52344c8732e13bbffeff Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 28 Jun 2026 21:43:43 +0000 Subject: [PATCH 01/10] Add agentic workflows Copilot canvas extension Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .../copilot-extension.json | 4 + .../agentic-workflows-dashboard/extension.mjs | 294 +++++++++++++++ .../package-lock.json | 235 ++++++++++++ .../agentic-workflows-dashboard/package.json | 17 + .../agentic-workflows-dashboard/src/app.ts | 345 ++++++++++++++++++ .../agentic-workflows-dashboard/src/models.ts | 36 ++ .../agentic-workflows-dashboard/tsconfig.json | 16 + .../agentic-workflows-dashboard/web/app.js | 269 ++++++++++++++ .../web/index.html | 168 +++++++++ .../agentic-workflows-dashboard/web/models.js | 1 + 10 files changed, 1385 insertions(+) create mode 100644 .github/extensions/agentic-workflows-dashboard/copilot-extension.json create mode 100644 .github/extensions/agentic-workflows-dashboard/extension.mjs create mode 100644 .github/extensions/agentic-workflows-dashboard/package-lock.json create mode 100644 .github/extensions/agentic-workflows-dashboard/package.json create mode 100644 .github/extensions/agentic-workflows-dashboard/src/app.ts create mode 100644 .github/extensions/agentic-workflows-dashboard/src/models.ts create mode 100644 .github/extensions/agentic-workflows-dashboard/tsconfig.json create mode 100644 .github/extensions/agentic-workflows-dashboard/web/app.js create mode 100644 .github/extensions/agentic-workflows-dashboard/web/index.html create mode 100644 .github/extensions/agentic-workflows-dashboard/web/models.js diff --git a/.github/extensions/agentic-workflows-dashboard/copilot-extension.json b/.github/extensions/agentic-workflows-dashboard/copilot-extension.json new file mode 100644 index 00000000000..02c1357e1e3 --- /dev/null +++ b/.github/extensions/agentic-workflows-dashboard/copilot-extension.json @@ -0,0 +1,4 @@ +{ + "name": "agentic-workflows-dashboard", + "version": 1 +} diff --git a/.github/extensions/agentic-workflows-dashboard/extension.mjs b/.github/extensions/agentic-workflows-dashboard/extension.mjs new file mode 100644 index 00000000000..4d626a42fb7 --- /dev/null +++ b/.github/extensions/agentic-workflows-dashboard/extension.mjs @@ -0,0 +1,294 @@ +import { readFile } from "node:fs/promises"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; + +import { createCanvas, joinSession } from "@github/copilot-sdk/extension"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +const definitions = buildDefinitions(240); +const runs = buildRuns(420, definitions); + +function nowIso() { + return new Date().toISOString(); +} + +function isoHoursAgo(hours) { + return new Date(Date.now() - hours * 60 * 60 * 1000).toISOString(); +} + +function buildDefinitions(count) { + return Array.from({ length: count }, (_, i) => { + const index = i + 1; + return { + id: `wf-${String(index).padStart(3, "0")}`, + name: `agentic-workflow-${index}`, + description: `Automated workflow #${index} for triage, reporting, and repository automation.`, + inputSchema: { + type: "object", + properties: { + issue: { type: "number" }, + branch: { type: "string" }, + }, + additionalProperties: false, + }, + enabled: index % 9 !== 0, + }; + }); +} + +function buildStep(runNumber, idx, status) { + return { + id: `step-${runNumber}-${idx}`, + title: ["Resolve context", "Execute engine", "Publish summary", "Finalize run"][idx - 1] ?? `Step ${idx}`, + status, + summaryMarkdown: `### ${status === "failed" ? "Action required" : "Step complete"}\n- Run: **${runNumber}**\n- Step: **${idx}**\n- Status: \`${status}\`\n\n[View workflow docs](https://github.com/github/gh-aw/tree/main/docs)`, + }; +} + +function buildRuns(count, sourceDefinitions) { + const statuses = ["queued", "running", "completed", "failed"]; + const stepStatusMap = { + queued: ["pending", "pending", "pending", "pending"], + running: ["done", "running", "pending", "pending"], + completed: ["done", "done", "done", "done"], + failed: ["done", "failed", "pending", "pending"], + }; + + return Array.from({ length: count }, (_, i) => { + const index = i + 1; + const status = statuses[index % statuses.length] ?? "queued"; + const definition = sourceDefinitions[index % sourceDefinitions.length]; + const stepStatuses = stepStatusMap[status] ?? stepStatusMap.queued; + + return { + id: `run-${String(index).padStart(5, "0")}`, + definitionId: definition.id, + status, + createdAt: isoHoursAgo(index * 2), + updatedAt: isoHoursAgo(index), + steps: stepStatuses.map((stepStatus, stepIndex) => buildStep(index, stepIndex + 1, stepStatus)), + }; + }); +} + +function paginate(items, page = 1, pageSize = 20) { + const totalItems = items.length; + const totalPages = Math.max(1, Math.ceil(totalItems / pageSize)); + const safePage = Math.min(Math.max(1, page), totalPages); + const start = (safePage - 1) * pageSize; + const end = start + pageSize; + + return { + items: items.slice(start, end), + page: safePage, + pageSize, + totalItems, + totalPages, + hasNextPage: safePage < totalPages, + hasPreviousPage: safePage > 1, + }; +} + +function findRun(runId) { + return runs.find(run => run.id === runId) ?? null; +} + +function dispatchWorkflow(definitionId, inputs = {}) { + const definition = definitions.find(item => item.id === definitionId); + if (!definition) { + return { ok: false, error: `Unknown workflow definition: ${definitionId}` }; + } + + const sequence = runs.length + 1; + const now = nowIso(); + const run = { + id: `run-${String(sequence).padStart(5, "0")}`, + definitionId, + status: "queued", + createdAt: now, + updatedAt: now, + steps: [buildStep(sequence, 1, "pending"), buildStep(sequence, 2, "pending"), buildStep(sequence, 3, "pending"), buildStep(sequence, 4, "pending")], + inputs, + }; + + runs.unshift(run); + + return { + ok: true, + run, + }; +} + +function parseRunFlag(argsText) { + const directMatch = argsText.match(/--run(?:=|\s+)(run-\d{5})/); + return directMatch?.[1] ?? null; +} + +function runGhAwLogs(args = "") { + const argsText = typeof args === "string" ? args : JSON.stringify(args); + const runId = parseRunFlag(argsText); + + if (runId) { + const run = findRun(runId); + if (!run) { + return { command: `gh aw logs ${argsText}`.trim(), output: `Run ${runId} not found.` }; + } + return { + command: `gh aw logs --run ${runId}`, + output: `Logs for ${run.id}\n- definition: ${run.definitionId}\n- status: ${run.status}\n- updatedAt: ${run.updatedAt}`, + }; + } + + const latest = runs[0]; + return { + command: "gh aw logs", + output: `Showing logs for ${latest?.id ?? "n/a"}.\n- status: ${latest?.status ?? "n/a"}\n- updatedAt: ${latest?.updatedAt ?? "n/a"}`, + }; +} + +function runGhAwAudit(args = "") { + const argsText = typeof args === "string" ? args : JSON.stringify(args); + const runId = parseRunFlag(argsText); + + if (runId) { + const run = findRun(runId); + if (!run) { + return { command: `gh aw audit ${argsText}`.trim(), output: `Run ${runId} not found.` }; + } + const failedSteps = run.steps.filter(step => step.status === "failed").length; + return { + command: `gh aw audit --run ${runId}`, + output: `Audit for ${run.id}\n- definition: ${run.definitionId}\n- status: ${run.status}\n- failed steps: ${failedSteps}`, + }; + } + + const completed = runs.filter(run => run.status === "completed").length; + const failed = runs.filter(run => run.status === "failed").length; + + return { + command: "gh aw audit", + output: `Audit summary\n- total runs: ${runs.length}\n- completed: ${completed}\n- failed: ${failed}`, + }; +} + +async function renderDashboardUrl() { + const htmlPath = join(__dirname, "web", "index.html"); + const appPath = join(__dirname, "web", "app.js"); + + const [htmlTemplate, appBundle] = await Promise.all([readFile(htmlPath, "utf8"), readFile(appPath, "utf8")]); + + const html = htmlTemplate.replace("/*__APP_JS__*/", appBundle); + return `data:text/html;charset=utf-8,${encodeURIComponent(html)}`; +} + +await joinSession({ + canvases: [ + createCanvas({ + id: "agentic-workflows-dashboard", + displayName: "Agentic Workflows Dashboard", + description: "A minimal dashboard for browsing workflow definitions and workflow runs.", + actions: [ + { + name: "listDefinitions", + description: "List workflow definitions with paging support.", + inputSchema: { + type: "object", + properties: { + page: { type: "number", minimum: 1 }, + pageSize: { type: "number", minimum: 1, maximum: 100 }, + }, + additionalProperties: false, + }, + handler: ctx => { + const page = Number(ctx.input?.page ?? 1); + const pageSize = Number(ctx.input?.pageSize ?? 20); + return paginate(definitions, page, pageSize); + }, + }, + { + name: "listRuns", + description: "List workflow runs with paging support.", + inputSchema: { + type: "object", + properties: { + page: { type: "number", minimum: 1 }, + pageSize: { type: "number", minimum: 1, maximum: 100 }, + }, + additionalProperties: false, + }, + handler: ctx => { + const page = Number(ctx.input?.page ?? 1); + const pageSize = Number(ctx.input?.pageSize ?? 20); + return paginate(runs, page, pageSize); + }, + }, + { + name: "getRun", + description: "Get a workflow run by run id.", + inputSchema: { + type: "object", + required: ["id"], + properties: { + id: { type: "string" }, + }, + additionalProperties: false, + }, + handler: ctx => { + const id = String(ctx.input?.id ?? ""); + return { run: findRun(id) }; + }, + }, + { + name: "dispatchWorkflow", + description: "Dispatch an existing predefined workflow.", + inputSchema: { + type: "object", + required: ["definitionId"], + properties: { + definitionId: { type: "string" }, + inputs: { type: "object", additionalProperties: true }, + }, + additionalProperties: false, + }, + handler: ctx => { + const definitionId = String(ctx.input?.definitionId ?? ""); + const inputs = ctx.input?.inputs && typeof ctx.input.inputs === "object" ? ctx.input.inputs : {}; + return dispatchWorkflow(definitionId, inputs); + }, + }, + { + name: "runGhAwLogs", + description: "Run gh aw logs command behavior.", + inputSchema: { + type: "object", + properties: { + args: { type: "string" }, + }, + additionalProperties: false, + }, + handler: ctx => runGhAwLogs(String(ctx.input?.args ?? "")), + }, + { + name: "runGhAwAudit", + description: "Run gh aw audit command behavior.", + inputSchema: { + type: "object", + properties: { + args: { type: "string" }, + }, + additionalProperties: false, + }, + handler: ctx => runGhAwAudit(String(ctx.input?.args ?? "")), + }, + ], + open: async () => { + return { + title: "Agentic Workflows Dashboard", + status: `${definitions.length} workflows · ${runs.length} runs`, + url: await renderDashboardUrl(), + }; + }, + }), + ], +}); diff --git a/.github/extensions/agentic-workflows-dashboard/package-lock.json b/.github/extensions/agentic-workflows-dashboard/package-lock.json new file mode 100644 index 00000000000..91107a09d48 --- /dev/null +++ b/.github/extensions/agentic-workflows-dashboard/package-lock.json @@ -0,0 +1,235 @@ +{ + "name": "agentic-workflows-dashboard", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "agentic-workflows-dashboard", + "version": "1.0.0", + "dependencies": { + "@github/copilot-sdk": "1.0.4" + }, + "devDependencies": { + "typescript": "6.0.3" + } + }, + "node_modules/@github/copilot": { + "version": "1.0.65", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.65.tgz", + "integrity": "sha512-J1XvLuOiVpiAi/E1MBICBymszCgdGLnZxokosXzGcmcjEVZd+QSDoW/kPRHq6oEyBT9SDASPcjCEZ9Q0rLJllg==", + "license": "SEE LICENSE IN LICENSE.md", + "dependencies": { + "detect-libc": "^2.1.2" + }, + "bin": { + "copilot": "npm-loader.js" + }, + "optionalDependencies": { + "@github/copilot-darwin-arm64": "1.0.65", + "@github/copilot-darwin-x64": "1.0.65", + "@github/copilot-linux-arm64": "1.0.65", + "@github/copilot-linux-x64": "1.0.65", + "@github/copilot-linuxmusl-arm64": "1.0.65", + "@github/copilot-linuxmusl-x64": "1.0.65", + "@github/copilot-win32-arm64": "1.0.65", + "@github/copilot-win32-x64": "1.0.65" + } + }, + "node_modules/@github/copilot-darwin-arm64": { + "version": "1.0.65", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.65.tgz", + "integrity": "sha512-NFc4xIstZNiIuAYkurQT5DVtbZjBoZ/z6yt/Ffcom7Y5QGjfpN4BFuekv9k+OADRioxxR99NgmhjbuNPWtQhNQ==", + "cpu": [ + "arm64" + ], + "license": "SEE LICENSE IN LICENSE.md", + "optional": true, + "os": [ + "darwin" + ], + "bin": { + "copilot-darwin-arm64": "copilot" + } + }, + "node_modules/@github/copilot-darwin-x64": { + "version": "1.0.65", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.65.tgz", + "integrity": "sha512-0wtV22KmTa12VbqWRRkgvJcBz/oIbszfcIpyDWGc4MzbCVksajQ3TWVQ6c7Sdzj5RifCaYdkHAX2zuIYXYlLoQ==", + "cpu": [ + "x64" + ], + "license": "SEE LICENSE IN LICENSE.md", + "optional": true, + "os": [ + "darwin" + ], + "bin": { + "copilot-darwin-x64": "copilot" + } + }, + "node_modules/@github/copilot-linux-arm64": { + "version": "1.0.65", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.65.tgz", + "integrity": "sha512-dOwdy/YbTXQN/+x2v4ZgiDycdRtWElyHxPuA6ail3yJDt0nagwn8OYAA/diBLPMAJuuBXiOZGvvb9fGRuh7Xgg==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "SEE LICENSE IN LICENSE.md", + "optional": true, + "os": [ + "linux" + ], + "bin": { + "copilot-linux-arm64": "copilot" + } + }, + "node_modules/@github/copilot-linux-x64": { + "version": "1.0.65", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.65.tgz", + "integrity": "sha512-al/1a/l/GrpHtygTxt7PZspmv0eHBPdAZ5B31J7Hv/GRdVZM4STCC9dCIOSUFsOX2fhaKD8yLfz4HureSYs23g==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "SEE LICENSE IN LICENSE.md", + "optional": true, + "os": [ + "linux" + ], + "bin": { + "copilot-linux-x64": "copilot" + } + }, + "node_modules/@github/copilot-linuxmusl-arm64": { + "version": "1.0.65", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.65.tgz", + "integrity": "sha512-xccQeJSR45xyoaL7J5mZjtU++dmte+ZCDQkIlrpTn2yuMl2LWriBvorQ1P2MwVnXmIiW/GHi93B+lNtsybA9yw==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "SEE LICENSE IN LICENSE.md", + "optional": true, + "os": [ + "linux" + ], + "bin": { + "copilot-linuxmusl-arm64": "copilot" + } + }, + "node_modules/@github/copilot-linuxmusl-x64": { + "version": "1.0.65", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.65.tgz", + "integrity": "sha512-RHPVUaqjSrhKHQ2EpfGKWErnV+R5elGIZaHXPKO10zpSaQD9b/C9u6nLigZnBuT/8sCaJpVrazPMwOYvYA62aw==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "SEE LICENSE IN LICENSE.md", + "optional": true, + "os": [ + "linux" + ], + "bin": { + "copilot-linuxmusl-x64": "copilot" + } + }, + "node_modules/@github/copilot-sdk": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@github/copilot-sdk/-/copilot-sdk-1.0.4.tgz", + "integrity": "sha512-c6/gpatP7LAuP9stlc2uXXOrB+TJMFSs3Jrzu9FG8lJJYFGmdzbBvGz2UdL1jww84fp6D7cohEZa4UGq1+iuyA==", + "license": "MIT", + "dependencies": { + "@github/copilot": "^1.0.65", + "vscode-jsonrpc": "^8.2.1", + "zod": "^4.3.6" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@github/copilot-win32-arm64": { + "version": "1.0.65", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.65.tgz", + "integrity": "sha512-/vSE/t9Wm3eFSWpxlKyn/oL8OAVOB0yFO7ECxhgbtiqNrBd1tgpYh1k7IXBIWa/saxlV1+de6DEmPuQfx3Z0bg==", + "cpu": [ + "arm64" + ], + "license": "SEE LICENSE IN LICENSE.md", + "optional": true, + "os": [ + "win32" + ], + "bin": { + "copilot-win32-arm64": "copilot.exe" + } + }, + "node_modules/@github/copilot-win32-x64": { + "version": "1.0.65", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.65.tgz", + "integrity": "sha512-wjVWXepET+SpFg8z8V43ZiTy6X1OerCb7yu3QZKNNJ3zY9z20goihPXQCDWkiJpGzszNSgfrsiqUzpUsC9qS0A==", + "cpu": [ + "x64" + ], + "license": "SEE LICENSE IN LICENSE.md", + "optional": true, + "os": [ + "win32" + ], + "bin": { + "copilot-win32-x64": "copilot.exe" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/vscode-jsonrpc": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.1.tgz", + "integrity": "sha512-kdjOSJ2lLIn7r1rtrMbbNCHjyMPfRnowdKjBQ+mGq6NAW5QY2bEZC/khaC5OR8svbbjvLEaIXkOq45e2X9BIbQ==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/.github/extensions/agentic-workflows-dashboard/package.json b/.github/extensions/agentic-workflows-dashboard/package.json new file mode 100644 index 00000000000..14a695b83e4 --- /dev/null +++ b/.github/extensions/agentic-workflows-dashboard/package.json @@ -0,0 +1,17 @@ +{ + "name": "agentic-workflows-dashboard", + "version": "1.0.0", + "type": "module", + "main": "extension.mjs", + "description": "GitHub Copilot canvas extension for agentic workflows dashboard.", + "dependencies": { + "@github/copilot-sdk": "1.0.4" + }, + "devDependencies": { + "typescript": "6.0.3" + }, + "scripts": { + "build": "tsc -p tsconfig.json", + "typecheck": "tsc -p tsconfig.json --noEmit" + } +} diff --git a/.github/extensions/agentic-workflows-dashboard/src/app.ts b/.github/extensions/agentic-workflows-dashboard/src/app.ts new file mode 100644 index 00000000000..a622a0a8791 --- /dev/null +++ b/.github/extensions/agentic-workflows-dashboard/src/app.ts @@ -0,0 +1,345 @@ +import type { PagedResult, WorkflowDefinition, WorkflowRun, WorkflowRunStatus, WorkflowStep, WorkflowStepStatus } from "./models.js"; + +type CommandResult = { command: string; output: string }; + +type FlashKind = "success" | "warn" | "error"; +declare const Alpine: { + data: (name: string, callback: () => DashboardState) => void; +}; + +interface DashboardState { + definitionPage: number; + runPage: number; + pageSize: number; + definitions: WorkflowDefinition[]; + runs: WorkflowRun[]; + definitionsPaged: PagedResult; + runsPaged: PagedResult; + selectedRun: WorkflowRun | null; + selectedDefinitionId: string; + commandInput: string; + commandOutput: string; + flashMessage: string; + flashKind: FlashKind; + init(): void; + loadDefinitionPage(page: number): void; + loadRunPage(page: number): void; + selectRun(id: string): void; + dispatchSelectedWorkflow(): void; + runCommand(): void; + commandQuickFill(value: string): void; + renderMarkdown(markdown: string): string; + formatDate(iso: string): string; + runStatusClass(status: WorkflowRunStatus): string; + stepStatusClass(status: WorkflowStepStatus): string; +} + +const definitionCount = 240; +const runCount = 420; + +function isoHoursAgo(hours: number): string { + return new Date(Date.now() - hours * 60 * 60 * 1000).toISOString(); +} + +function buildDefinitions(count: number): WorkflowDefinition[] { + return Array.from({ length: count }, (_, i) => { + const index = i + 1; + return { + id: `wf-${String(index).padStart(3, "0")}`, + name: `agentic-workflow-${index}`, + description: `Automated workflow #${index} for triage, reporting, and repository automation.`, + inputSchema: { + type: "object", + properties: { + issue: { type: "number" }, + branch: { type: "string" }, + }, + additionalProperties: false, + }, + enabled: index % 9 !== 0, + }; + }); +} + +function buildStep(runNumber: number, idx: number, status: WorkflowStepStatus): WorkflowStep { + return { + id: `step-${runNumber}-${idx}`, + title: ["Resolve context", "Execute engine", "Publish summary", "Finalize run"][idx - 1] ?? `Step ${idx}`, + status, + summaryMarkdown: `### ${status === "failed" ? "Action required" : "Step complete"}\n- Run: **${runNumber}**\n- Step: **${idx}**\n- Status: \`${status}\`\n\n[View workflow docs](https://github.com/github/gh-aw/tree/main/docs)`, + }; +} + +function buildRuns(count: number, definitions: WorkflowDefinition[]): WorkflowRun[] { + const statuses: WorkflowRunStatus[] = ["queued", "running", "completed", "failed"]; + const stepStatusMap: Record = { + queued: ["pending", "pending", "pending", "pending"], + running: ["done", "running", "pending", "pending"], + completed: ["done", "done", "done", "done"], + failed: ["done", "failed", "pending", "pending"], + }; + + return Array.from({ length: count }, (_, i) => { + const index = i + 1; + const status = statuses[index % statuses.length] ?? "queued"; + const definition = definitions[index % definitions.length] ?? definitions[0]; + if (!definition) { + throw new Error("No workflow definitions available."); + } + const stepStatuses = stepStatusMap[status]; + + return { + id: `run-${String(index).padStart(5, "0")}`, + definitionId: definition.id, + status, + createdAt: isoHoursAgo(index * 2), + updatedAt: isoHoursAgo(index), + steps: stepStatuses.map((stepStatus, stepIndex) => buildStep(index, stepIndex + 1, stepStatus)), + }; + }); +} + +function paginate(items: T[], page: number, pageSize: number): PagedResult { + const totalItems = items.length; + const totalPages = Math.max(1, Math.ceil(totalItems / pageSize)); + const safePage = Math.min(Math.max(1, page), totalPages); + const start = (safePage - 1) * pageSize; + const end = start + pageSize; + + return { + items: items.slice(start, end), + page: safePage, + pageSize, + totalItems, + totalPages, + hasNextPage: safePage < totalPages, + hasPreviousPage: safePage > 1, + }; +} + +function escapeHtml(value: string): string { + return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'"); +} + +function renderSafeMarkdown(markdown: string): string { + const safe = escapeHtml(markdown); + const lines = safe.split("\n"); + let inList = false; + const out: string[] = []; + + const closeList = () => { + if (inList) { + out.push(""); + inList = false; + } + }; + + for (const line of lines) { + if (line.startsWith("### ")) { + closeList(); + out.push(`

${line.slice(4)}

`); + continue; + } + + if (line.startsWith("- ")) { + if (!inList) { + out.push('
    '); + inList = true; + } + out.push(`
  • ${line.slice(2)}
  • `); + continue; + } + + if (line.trim().length === 0) { + closeList(); + out.push('
    '); + continue; + } + + closeList(); + out.push(`

    ${line}

    `); + } + + closeList(); + + return out + .join("") + .replace(/\*\*(.+?)\*\*/g, "$1") + .replace(/`([^`]+)`/g, "$1") + .replace(/\[([^\]]+)\]\((https?:\/\/[^)\s]+)\)/g, '$1'); +} + +function formatDate(iso: string): string { + return new Date(iso).toLocaleString(); +} + +function runGhCommand(command: string, runs: WorkflowRun[]): CommandResult { + const normalized = command.trim(); + + if (!normalized.startsWith("gh aw ")) { + return { command: normalized, output: "Only gh aw commands are supported." }; + } + + if (normalized === "gh aw logs") { + const first = runs[0]; + return { + command: normalized, + output: `Showing logs for latest run ${first?.id ?? "n/a"}.\n- status: ${first?.status ?? "unknown"}\n- updated: ${first ? formatDate(first.updatedAt) : "n/a"}`, + }; + } + + const runFlagMatch = normalized.match(/--run(?:=|\s+)(run-\d{5})/); + if (normalized.startsWith("gh aw logs --run") && runFlagMatch?.[1]) { + const run = runs.find(item => item.id === runFlagMatch[1]); + if (!run) { + return { command: normalized, output: `Run ${runFlagMatch[1]} not found.` }; + } + return { + command: normalized, + output: `Logs for ${run.id}\n- definition: ${run.definitionId}\n- status: ${run.status}\n- steps: ${run.steps.length}`, + }; + } + + if (normalized === "gh aw audit") { + const completed = runs.filter(run => run.status === "completed").length; + const failed = runs.filter(run => run.status === "failed").length; + return { + command: normalized, + output: `Audit summary\n- total runs: ${runs.length}\n- completed: ${completed}\n- failed: ${failed}`, + }; + } + + if (normalized.startsWith("gh aw audit --run") && runFlagMatch?.[1]) { + const run = runs.find(item => item.id === runFlagMatch[1]); + if (!run) { + return { command: normalized, output: `Run ${runFlagMatch[1]} not found.` }; + } + const failedSteps = run.steps.filter(step => step.status === "failed").length; + return { + command: normalized, + output: `Audit for ${run.id}\n- status: ${run.status}\n- failed steps: ${failedSteps}\n- updated: ${formatDate(run.updatedAt)}`, + }; + } + + return { + command: normalized, + output: "Supported commands: gh aw logs, gh aw logs --run , gh aw audit, gh aw audit --run .", + }; +} + +function statusClass(status: WorkflowRunStatus): string { + switch (status) { + case "completed": + return "Label Label--success"; + case "failed": + return "Label Label--danger"; + case "running": + return "Label Label--attention"; + default: + return "Label Label--secondary"; + } +} + +function stepStatusClass(status: WorkflowStepStatus): string { + switch (status) { + case "done": + return "Label Label--success"; + case "failed": + return "Label Label--danger"; + case "running": + return "Label Label--attention"; + default: + return "Label Label--secondary"; + } +} + +const definitions = buildDefinitions(definitionCount); +const runs = buildRuns(runCount, definitions); + +document.addEventListener("alpine:init", () => { + Alpine.data("dashboardApp", () => ({ + definitionPage: 1, + runPage: 1, + pageSize: 20, + definitions, + runs, + definitionsPaged: paginate(definitions, 1, 20), + runsPaged: paginate(runs, 1, 20), + selectedRun: runs[0] ?? null, + selectedDefinitionId: definitions[0]?.id ?? "", + commandInput: "gh aw logs", + commandOutput: "", + flashMessage: "", + flashKind: "success", + + init() { + this.loadDefinitionPage(1); + this.loadRunPage(1); + if (this.commandOutput.length === 0) { + this.runCommand(); + } + }, + + loadDefinitionPage(page) { + this.definitionPage = page; + this.definitionsPaged = paginate(this.definitions, page, this.pageSize); + }, + + loadRunPage(page) { + this.runPage = page; + this.runsPaged = paginate(this.runs, page, this.pageSize); + if (!this.selectedRun && this.runsPaged.items.length > 0) { + this.selectedRun = this.runsPaged.items[0] ?? null; + } + }, + + selectRun(id) { + this.selectedRun = this.runs.find(run => run.id === id) ?? null; + }, + + dispatchSelectedWorkflow() { + const definition = this.definitions.find(item => item.id === this.selectedDefinitionId); + if (!definition) { + this.flashKind = "error"; + this.flashMessage = "Select a workflow definition before dispatching."; + return; + } + + const sequence = this.runs.length + 1; + const now = new Date().toISOString(); + const newRun: WorkflowRun = { + id: `run-${String(sequence).padStart(5, "0")}`, + definitionId: definition.id, + status: "queued", + createdAt: now, + updatedAt: now, + steps: [buildStep(sequence, 1, "pending"), buildStep(sequence, 2, "pending"), buildStep(sequence, 3, "pending"), buildStep(sequence, 4, "pending")], + }; + + this.runs = [newRun, ...this.runs]; + this.loadRunPage(1); + this.selectRun(newRun.id); + + this.flashKind = "success"; + this.flashMessage = `Dispatched ${definition.name} as ${newRun.id}.`; + }, + + runCommand() { + const result = runGhCommand(this.commandInput, this.runs); + this.commandOutput = `$ ${result.command}\n${result.output}`; + }, + + commandQuickFill(value) { + this.commandInput = value; + this.runCommand(); + }, + + renderMarkdown(markdown) { + return renderSafeMarkdown(markdown); + }, + + formatDate, + runStatusClass: statusClass, + stepStatusClass, + })); +}); diff --git a/.github/extensions/agentic-workflows-dashboard/src/models.ts b/.github/extensions/agentic-workflows-dashboard/src/models.ts new file mode 100644 index 00000000000..d5d0a201a43 --- /dev/null +++ b/.github/extensions/agentic-workflows-dashboard/src/models.ts @@ -0,0 +1,36 @@ +export type WorkflowRunStatus = "queued" | "running" | "completed" | "failed"; +export type WorkflowStepStatus = "pending" | "running" | "done" | "failed"; + +export interface WorkflowDefinition { + id: string; + name: string; + description: string; + inputSchema: Record; + enabled: boolean; +} + +export interface WorkflowStep { + id: string; + title: string; + status: WorkflowStepStatus; + summaryMarkdown: string; +} + +export interface WorkflowRun { + id: string; + definitionId: string; + status: WorkflowRunStatus; + createdAt: string; + updatedAt: string; + steps: WorkflowStep[]; +} + +export interface PagedResult { + items: T[]; + page: number; + pageSize: number; + totalItems: number; + totalPages: number; + hasNextPage: boolean; + hasPreviousPage: boolean; +} diff --git a/.github/extensions/agentic-workflows-dashboard/tsconfig.json b/.github/extensions/agentic-workflows-dashboard/tsconfig.json new file mode 100644 index 00000000000..a1a9e519be1 --- /dev/null +++ b/.github/extensions/agentic-workflows-dashboard/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ES2022", + "moduleResolution": "bundler", + "lib": ["ES2022", "DOM"], + "strict": true, + "noImplicitAny": true, + "noUncheckedIndexedAccess": true, + "exactOptionalPropertyTypes": true, + "outDir": "./web", + "rootDir": "./src", + "skipLibCheck": true + }, + "include": ["src/**/*.ts"] +} diff --git a/.github/extensions/agentic-workflows-dashboard/web/app.js b/.github/extensions/agentic-workflows-dashboard/web/app.js new file mode 100644 index 00000000000..656269d01ee --- /dev/null +++ b/.github/extensions/agentic-workflows-dashboard/web/app.js @@ -0,0 +1,269 @@ +const definitionCount = 240; +const runCount = 420; +function isoHoursAgo(hours) { + return new Date(Date.now() - hours * 60 * 60 * 1000).toISOString(); +} +function buildDefinitions(count) { + return Array.from({ length: count }, (_, i) => { + const index = i + 1; + return { + id: `wf-${String(index).padStart(3, "0")}`, + name: `agentic-workflow-${index}`, + description: `Automated workflow #${index} for triage, reporting, and repository automation.`, + inputSchema: { + type: "object", + properties: { + issue: { type: "number" }, + branch: { type: "string" }, + }, + additionalProperties: false, + }, + enabled: index % 9 !== 0, + }; + }); +} +function buildStep(runNumber, idx, status) { + return { + id: `step-${runNumber}-${idx}`, + title: ["Resolve context", "Execute engine", "Publish summary", "Finalize run"][idx - 1] ?? `Step ${idx}`, + status, + summaryMarkdown: `### ${status === "failed" ? "Action required" : "Step complete"}\n- Run: **${runNumber}**\n- Step: **${idx}**\n- Status: \`${status}\`\n\n[View workflow docs](https://github.com/github/gh-aw/tree/main/docs)`, + }; +} +function buildRuns(count, definitions) { + const statuses = ["queued", "running", "completed", "failed"]; + const stepStatusMap = { + queued: ["pending", "pending", "pending", "pending"], + running: ["done", "running", "pending", "pending"], + completed: ["done", "done", "done", "done"], + failed: ["done", "failed", "pending", "pending"], + }; + return Array.from({ length: count }, (_, i) => { + const index = i + 1; + const status = statuses[index % statuses.length] ?? "queued"; + const definition = definitions[index % definitions.length] ?? definitions[0]; + if (!definition) { + throw new Error("No workflow definitions available."); + } + const stepStatuses = stepStatusMap[status]; + return { + id: `run-${String(index).padStart(5, "0")}`, + definitionId: definition.id, + status, + createdAt: isoHoursAgo(index * 2), + updatedAt: isoHoursAgo(index), + steps: stepStatuses.map((stepStatus, stepIndex) => buildStep(index, stepIndex + 1, stepStatus)), + }; + }); +} +function paginate(items, page, pageSize) { + const totalItems = items.length; + const totalPages = Math.max(1, Math.ceil(totalItems / pageSize)); + const safePage = Math.min(Math.max(1, page), totalPages); + const start = (safePage - 1) * pageSize; + const end = start + pageSize; + return { + items: items.slice(start, end), + page: safePage, + pageSize, + totalItems, + totalPages, + hasNextPage: safePage < totalPages, + hasPreviousPage: safePage > 1, + }; +} +function escapeHtml(value) { + return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'"); +} +function renderSafeMarkdown(markdown) { + const safe = escapeHtml(markdown); + const lines = safe.split("\n"); + let inList = false; + const out = []; + const closeList = () => { + if (inList) { + out.push("
"); + inList = false; + } + }; + for (const line of lines) { + if (line.startsWith("### ")) { + closeList(); + out.push(`

${line.slice(4)}

`); + continue; + } + if (line.startsWith("- ")) { + if (!inList) { + out.push('
    '); + inList = true; + } + out.push(`
  • ${line.slice(2)}
  • `); + continue; + } + if (line.trim().length === 0) { + closeList(); + out.push('
    '); + continue; + } + closeList(); + out.push(`

    ${line}

    `); + } + closeList(); + return out + .join("") + .replace(/\*\*(.+?)\*\*/g, "$1") + .replace(/`([^`]+)`/g, "$1") + .replace(/\[([^\]]+)\]\((https?:\/\/[^)\s]+)\)/g, '$1'); +} +function formatDate(iso) { + return new Date(iso).toLocaleString(); +} +function runGhCommand(command, runs) { + const normalized = command.trim(); + if (!normalized.startsWith("gh aw ")) { + return { command: normalized, output: "Only gh aw commands are supported." }; + } + if (normalized === "gh aw logs") { + const first = runs[0]; + return { + command: normalized, + output: `Showing logs for latest run ${first?.id ?? "n/a"}.\n- status: ${first?.status ?? "unknown"}\n- updated: ${first ? formatDate(first.updatedAt) : "n/a"}`, + }; + } + const runFlagMatch = normalized.match(/--run(?:=|\s+)(run-\d{5})/); + if (normalized.startsWith("gh aw logs --run") && runFlagMatch?.[1]) { + const run = runs.find(item => item.id === runFlagMatch[1]); + if (!run) { + return { command: normalized, output: `Run ${runFlagMatch[1]} not found.` }; + } + return { + command: normalized, + output: `Logs for ${run.id}\n- definition: ${run.definitionId}\n- status: ${run.status}\n- steps: ${run.steps.length}`, + }; + } + if (normalized === "gh aw audit") { + const completed = runs.filter(run => run.status === "completed").length; + const failed = runs.filter(run => run.status === "failed").length; + return { + command: normalized, + output: `Audit summary\n- total runs: ${runs.length}\n- completed: ${completed}\n- failed: ${failed}`, + }; + } + if (normalized.startsWith("gh aw audit --run") && runFlagMatch?.[1]) { + const run = runs.find(item => item.id === runFlagMatch[1]); + if (!run) { + return { command: normalized, output: `Run ${runFlagMatch[1]} not found.` }; + } + const failedSteps = run.steps.filter(step => step.status === "failed").length; + return { + command: normalized, + output: `Audit for ${run.id}\n- status: ${run.status}\n- failed steps: ${failedSteps}\n- updated: ${formatDate(run.updatedAt)}`, + }; + } + return { + command: normalized, + output: "Supported commands: gh aw logs, gh aw logs --run , gh aw audit, gh aw audit --run .", + }; +} +function statusClass(status) { + switch (status) { + case "completed": + return "Label Label--success"; + case "failed": + return "Label Label--danger"; + case "running": + return "Label Label--attention"; + default: + return "Label Label--secondary"; + } +} +function stepStatusClass(status) { + switch (status) { + case "done": + return "Label Label--success"; + case "failed": + return "Label Label--danger"; + case "running": + return "Label Label--attention"; + default: + return "Label Label--secondary"; + } +} +const definitions = buildDefinitions(definitionCount); +const runs = buildRuns(runCount, definitions); +document.addEventListener("alpine:init", () => { + Alpine.data("dashboardApp", () => ({ + definitionPage: 1, + runPage: 1, + pageSize: 20, + definitions, + runs, + definitionsPaged: paginate(definitions, 1, 20), + runsPaged: paginate(runs, 1, 20), + selectedRun: runs[0] ?? null, + selectedDefinitionId: definitions[0]?.id ?? "", + commandInput: "gh aw logs", + commandOutput: "", + flashMessage: "", + flashKind: "success", + init() { + this.loadDefinitionPage(1); + this.loadRunPage(1); + if (this.commandOutput.length === 0) { + this.runCommand(); + } + }, + loadDefinitionPage(page) { + this.definitionPage = page; + this.definitionsPaged = paginate(this.definitions, page, this.pageSize); + }, + loadRunPage(page) { + this.runPage = page; + this.runsPaged = paginate(this.runs, page, this.pageSize); + if (!this.selectedRun && this.runsPaged.items.length > 0) { + this.selectedRun = this.runsPaged.items[0] ?? null; + } + }, + selectRun(id) { + this.selectedRun = this.runs.find(run => run.id === id) ?? null; + }, + dispatchSelectedWorkflow() { + const definition = this.definitions.find(item => item.id === this.selectedDefinitionId); + if (!definition) { + this.flashKind = "error"; + this.flashMessage = "Select a workflow definition before dispatching."; + return; + } + const sequence = this.runs.length + 1; + const now = new Date().toISOString(); + const newRun = { + id: `run-${String(sequence).padStart(5, "0")}`, + definitionId: definition.id, + status: "queued", + createdAt: now, + updatedAt: now, + steps: [buildStep(sequence, 1, "pending"), buildStep(sequence, 2, "pending"), buildStep(sequence, 3, "pending"), buildStep(sequence, 4, "pending")], + }; + this.runs = [newRun, ...this.runs]; + this.loadRunPage(1); + this.selectRun(newRun.id); + this.flashKind = "success"; + this.flashMessage = `Dispatched ${definition.name} as ${newRun.id}.`; + }, + runCommand() { + const result = runGhCommand(this.commandInput, this.runs); + this.commandOutput = `$ ${result.command}\n${result.output}`; + }, + commandQuickFill(value) { + this.commandInput = value; + this.runCommand(); + }, + renderMarkdown(markdown) { + return renderSafeMarkdown(markdown); + }, + formatDate, + runStatusClass: statusClass, + stepStatusClass, + })); +}); +export {}; diff --git a/.github/extensions/agentic-workflows-dashboard/web/index.html b/.github/extensions/agentic-workflows-dashboard/web/index.html new file mode 100644 index 00000000000..4537c98a3cc --- /dev/null +++ b/.github/extensions/agentic-workflows-dashboard/web/index.html @@ -0,0 +1,168 @@ + + + + + + Agentic Workflows Dashboard + + + + +
    +
    +
    +
    +
    github / gh-aw
    +
    Agentic workflows canvas dashboard
    +
    + Copilot Canvas +
    + +
    + +
    +
    + +
    +
    +
    +

    Workflow definitions

    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    + +
    +
    +
    + +
    + +
    +
    +

    Workflow runs

    +
    +
    +
    +
    +
    +
    +
    Definition:
    +
    Updated:
    +
    + +
    +
    +
    + +
    + +
    +
    +
    +

    Run details

    +
    +
    +
    + + +
    +
    Created:
    +
    +
    +
    + + +
    +
    +
    +
    +
    +
    Select a workflow run to review step summaries.
    +
    + +
    +
    +

    Command panel

    +
    +
    +
    + + + + +
    +
    + + +
    +
    
    +              
    +
    +
    +
    +
    +
    + + + + diff --git a/.github/extensions/agentic-workflows-dashboard/web/models.js b/.github/extensions/agentic-workflows-dashboard/web/models.js new file mode 100644 index 00000000000..cb0ff5c3b54 --- /dev/null +++ b/.github/extensions/agentic-workflows-dashboard/web/models.js @@ -0,0 +1 @@ +export {}; From bc6ad18114cb0b668226db3f90605fcf3d8aa694 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 28 Jun 2026 22:03:45 +0000 Subject: [PATCH 02/10] Add canvas vitest target and Playwright screenshots Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .../package-lock.json | 1312 ++++++++++++++++- .../agentic-workflows-dashboard/package.json | 6 +- .../screenshots/dashboard-full.png | Bin 0 -> 37032 bytes .../screenshots/dashboard-viewport.png | Bin 0 -> 38743 bytes .../agentic-workflows-dashboard/src/app.ts | 19 +- .../src/pagination.ts | 19 + .../test/pagination.test.ts | 21 + .../vitest.config.ts | 7 + .../agentic-workflows-dashboard/web/app.js | 18 +- .../web/pagination.js | 16 + Makefile | 5 + 11 files changed, 1365 insertions(+), 58 deletions(-) create mode 100644 .github/extensions/agentic-workflows-dashboard/screenshots/dashboard-full.png create mode 100644 .github/extensions/agentic-workflows-dashboard/screenshots/dashboard-viewport.png create mode 100644 .github/extensions/agentic-workflows-dashboard/src/pagination.ts create mode 100644 .github/extensions/agentic-workflows-dashboard/test/pagination.test.ts create mode 100644 .github/extensions/agentic-workflows-dashboard/vitest.config.ts create mode 100644 .github/extensions/agentic-workflows-dashboard/web/pagination.js diff --git a/.github/extensions/agentic-workflows-dashboard/package-lock.json b/.github/extensions/agentic-workflows-dashboard/package-lock.json index 91107a09d48..333892eea13 100644 --- a/.github/extensions/agentic-workflows-dashboard/package-lock.json +++ b/.github/extensions/agentic-workflows-dashboard/package-lock.json @@ -11,7 +11,42 @@ "@github/copilot-sdk": "1.0.4" }, "devDependencies": { - "typescript": "6.0.3" + "typescript": "6.0.3", + "vitest": "4.1.9" + } + }, + "node_modules/@emnapi/core": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", + "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" } }, "node_modules/@github/copilot": { @@ -190,36 +225,1271 @@ "copilot-win32-x64": "copilot.exe" } }, - "node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "license": "Apache-2.0", + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", + "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.3" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.137.0.tgz", + "integrity": "sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.3.tgz", + "integrity": "sha512-DT6Z3PhvioeHMvxo+xHc3KtqggrI7CCTXCmC2h/5zUlp5jVitv7XEy+9q5/7v8IolhlioawpMo8Kg0EEBy7J0g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=8" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/typescript": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", - "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.3.tgz", + "integrity": "sha512-0NwgwsjM7LrsuVnXMK3koTpagBNOhloc/BNjKqZjv4V5zI5r13qx69uVhRx+o5Z0yy4Hzq+lpy7TAgUG/ocvrw==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.3.tgz", + "integrity": "sha512-YtiBp4disu6V560loT6PjMdiRaWmVvDNrUunAalbiFx2ggeJwxdAsgZMcoGP17uyAsTwAj5V1niksxlHnVQ1Sw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.3.tgz", + "integrity": "sha512-yD3EkEdXk2LypPxnf/kSZHirarsI8gcPzc62SukhR9VJTyvV+F9Q/GxWNuCojc7sXyuVC4DxRGhdDK4X8VSsbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.3.tgz", + "integrity": "sha512-c+8vieQbsD7HNAHKIA34w0GJ9FedFFuJGD+7E6vz7Q3uqAIugL5p45fhlsj4UaAsHpcmlqugBWMhA0/j7o0sIg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.3.tgz", + "integrity": "sha512-50jD0uUwLvur7Zz9LHz17kaAdTPjn5wN93hEgjvmYFRZwiR7ZJYovTd5ipyWJDAnXKvZ+wgc+/Ika6dwSF5OcA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.3.tgz", + "integrity": "sha512-BO9+oPL8K9poZJBfYPsXNtYjPE5uM3qeehT3aFcW4LITOl+iSqhp0abzjR2nWBUNjIZeKXjAEWBZ64WjNoHd6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.3.tgz", + "integrity": "sha512-f3VpLB1vQ0Eo6ecr/6cekLnvYMFF4YBFoVGkfkvPLq1bAkbAwHYQPZKoAmG6OJyTcxxoC+AvezGx/S1obNC0Mw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.3.tgz", + "integrity": "sha512-AmurZ26Pqx/RI9N1gzEOCklkKXl927yjfXWUUS0O7Puh8ARM/Ob8qfrD3qnWksScdw6cSrW5PSHE9DyLu7+PtA==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.3.tgz", + "integrity": "sha512-JJpqs8bRGITDOdbkNKnlojzBabbOHrqjSvDr0IVsZObE1lBcPjxItUEY9eWIDbxaJ3cGrXPWGfGkIxFijg/URg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.3.tgz", + "integrity": "sha512-rSJcdjPxzA/by/6/rYs+v+bXU7UjvnbUWz8MJb6kh6+knqB1dCrtHg0uu7C/4haqJvqdkYHQ5IGn+tCH9GLW/g==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.3.tgz", + "integrity": "sha512-hQ3/PYkDJICgevvyNcVrihVeqq7k1Pp3VZ9lY+dauAYUJKO+auqApvANhvR1An9BhmqYKvW2Mu1F9u4DXSMLxQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.3.tgz", + "integrity": "sha512-Elcv/BtML9lXrV6JuKITc/grN2kYV9gjsQpW8Jfw4ioK0TOkjBjye0nnyqQNy9STNaI20lXNaQBRrD5gSgR0Yg==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.11.1", + "@emnapi/runtime": "1.11.1", + "@napi-rs/wasm-runtime": "^1.1.6" }, "engines": { - "node": ">=14.17" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/vscode-jsonrpc": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.1.tgz", - "integrity": "sha512-kdjOSJ2lLIn7r1rtrMbbNCHjyMPfRnowdKjBQ+mGq6NAW5QY2bEZC/khaC5OR8svbbjvLEaIXkOq45e2X9BIbQ==", + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.3.tgz", + "integrity": "sha512-2DrEfhluH9yhiaFApmsjsjwrSYbNcY1oFTzYSP1a535jDbV98zCFanA/96TBUd0iDFcxGmw9QRExwGCXz3U+/g==", + "cpu": [ + "arm64" + ], + "dev": true, "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=14.0.0" + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.3.tgz", + "integrity": "sha512-OL4OMk7UPXOeVGGd3qo5zJyPIljf4AFgk5QAkPPS+OoLuOOozhuaQGC18MxVTnw/06q93gShAJzlwnSCY9YtqA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitest/expect": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.9.tgz", + "integrity": "sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.9", + "@vitest/utils": "4.1.9", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.9.tgz", + "integrity": "sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.1.9", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.9.tgz", + "integrity": "sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.9.tgz", + "integrity": "sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.1.9", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.9.tgz", + "integrity": "sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.9", + "@vitest/utils": "4.1.9", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.9.tgz", + "integrity": "sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.9.tgz", + "integrity": "sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.9", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/es-module-lexer": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", + "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/expect-type": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", + "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/nanoid": { + "version": "3.3.15", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz", + "integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/obug": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.3.tgz", + "integrity": "sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.16", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.16.tgz", + "integrity": "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rolldown": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.3.tgz", + "integrity": "sha512-1F1eEtUBtFvcGm1HQ9TiUIUHPQG7mSAODrhIzjxoUEFuo8OcbrGLiVLkevNgj84TE4lnHvnumwFjhJO5Eu135g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.137.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.1.3", + "@rolldown/binding-darwin-arm64": "1.1.3", + "@rolldown/binding-darwin-x64": "1.1.3", + "@rolldown/binding-freebsd-x64": "1.1.3", + "@rolldown/binding-linux-arm-gnueabihf": "1.1.3", + "@rolldown/binding-linux-arm64-gnu": "1.1.3", + "@rolldown/binding-linux-arm64-musl": "1.1.3", + "@rolldown/binding-linux-ppc64-gnu": "1.1.3", + "@rolldown/binding-linux-s390x-gnu": "1.1.3", + "@rolldown/binding-linux-x64-gnu": "1.1.3", + "@rolldown/binding-linux-x64-musl": "1.1.3", + "@rolldown/binding-openharmony-arm64": "1.1.3", + "@rolldown/binding-wasm32-wasi": "1.1.3", + "@rolldown/binding-win32-arm64-msvc": "1.1.3", + "@rolldown/binding-win32-x64-msvc": "1.1.3" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz", + "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", + "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyrainbow": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", + "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/vite": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.0.tgz", + "integrity": "sha512-BuJcQK/56NQTWDGn4ABea3q4SSBdNPWwNZKTkkUpcMPnLoquSYH8llRtSUIgoL1KSCpHt5eghLShn50mH36y7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.4", + "postcss": "^8.5.15", + "rolldown": "~1.1.2", + "tinyglobby": "^0.2.17" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.3.0", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vitest": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.9.tgz", + "integrity": "sha512-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.1.9", + "@vitest/mocker": "4.1.9", + "@vitest/pretty-format": "4.1.9", + "@vitest/runner": "4.1.9", + "@vitest/snapshot": "4.1.9", + "@vitest/spy": "4.1.9", + "@vitest/utils": "4.1.9", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^4.0.0-rc.1", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.1.9", + "@vitest/browser-preview": "4.1.9", + "@vitest/browser-webdriverio": "4.1.9", + "@vitest/coverage-istanbul": "4.1.9", + "@vitest/coverage-v8": "4.1.9", + "@vitest/ui": "4.1.9", + "happy-dom": "*", + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "vite": { + "optional": false + } + } + }, + "node_modules/vscode-jsonrpc": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.1.tgz", + "integrity": "sha512-kdjOSJ2lLIn7r1rtrMbbNCHjyMPfRnowdKjBQ+mGq6NAW5QY2bEZC/khaC5OR8svbbjvLEaIXkOq45e2X9BIbQ==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" } }, "node_modules/zod": { diff --git a/.github/extensions/agentic-workflows-dashboard/package.json b/.github/extensions/agentic-workflows-dashboard/package.json index 14a695b83e4..85c9fbacd05 100644 --- a/.github/extensions/agentic-workflows-dashboard/package.json +++ b/.github/extensions/agentic-workflows-dashboard/package.json @@ -8,10 +8,12 @@ "@github/copilot-sdk": "1.0.4" }, "devDependencies": { - "typescript": "6.0.3" + "typescript": "6.0.3", + "vitest": "4.1.9" }, "scripts": { "build": "tsc -p tsconfig.json", - "typecheck": "tsc -p tsconfig.json --noEmit" + "typecheck": "tsc -p tsconfig.json --noEmit", + "test": "vitest run" } } diff --git a/.github/extensions/agentic-workflows-dashboard/screenshots/dashboard-full.png b/.github/extensions/agentic-workflows-dashboard/screenshots/dashboard-full.png new file mode 100644 index 0000000000000000000000000000000000000000..0bb002114cb9dc4da4c74cc19a72a8d5a412207d GIT binary patch literal 37032 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 rY=TC>S0NBY_NVUvf84m-tB8p}8ptk6(items: T[], page: number, pageSize: number): PagedResult { - const totalItems = items.length; - const totalPages = Math.max(1, Math.ceil(totalItems / pageSize)); - const safePage = Math.min(Math.max(1, page), totalPages); - const start = (safePage - 1) * pageSize; - const end = start + pageSize; - - return { - items: items.slice(start, end), - page: safePage, - pageSize, - totalItems, - totalPages, - hasNextPage: safePage < totalPages, - hasPreviousPage: safePage > 1, - }; -} - function escapeHtml(value: string): string { return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'"); } diff --git a/.github/extensions/agentic-workflows-dashboard/src/pagination.ts b/.github/extensions/agentic-workflows-dashboard/src/pagination.ts new file mode 100644 index 00000000000..b6a1827fc15 --- /dev/null +++ b/.github/extensions/agentic-workflows-dashboard/src/pagination.ts @@ -0,0 +1,19 @@ +import type { PagedResult } from "./models.js"; + +export function paginate(items: T[], page: number, pageSize: number): PagedResult { + const totalItems = items.length; + const totalPages = Math.max(1, Math.ceil(totalItems / pageSize)); + const safePage = Math.min(Math.max(1, page), totalPages); + const start = (safePage - 1) * pageSize; + const end = start + pageSize; + + return { + items: items.slice(start, end), + page: safePage, + pageSize, + totalItems, + totalPages, + hasNextPage: safePage < totalPages, + hasPreviousPage: safePage > 1, + }; +} diff --git a/.github/extensions/agentic-workflows-dashboard/test/pagination.test.ts b/.github/extensions/agentic-workflows-dashboard/test/pagination.test.ts new file mode 100644 index 00000000000..99bf5686866 --- /dev/null +++ b/.github/extensions/agentic-workflows-dashboard/test/pagination.test.ts @@ -0,0 +1,21 @@ +import { describe, expect, it } from "vitest"; + +import { paginate } from "../src/pagination.js"; + +describe("paginate", () => { + it("returns expected page metadata and items", () => { + const result = paginate([1, 2, 3, 4, 5], 2, 2); + expect(result.items).toEqual([3, 4]); + expect(result.page).toBe(2); + expect(result.totalPages).toBe(3); + expect(result.hasNextPage).toBe(true); + expect(result.hasPreviousPage).toBe(true); + }); + + it("clamps page to valid bounds", () => { + const result = paginate([1, 2, 3], 999, 2); + expect(result.page).toBe(2); + expect(result.items).toEqual([3]); + expect(result.hasNextPage).toBe(false); + }); +}); diff --git a/.github/extensions/agentic-workflows-dashboard/vitest.config.ts b/.github/extensions/agentic-workflows-dashboard/vitest.config.ts new file mode 100644 index 00000000000..8b5840acac7 --- /dev/null +++ b/.github/extensions/agentic-workflows-dashboard/vitest.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + include: ["test/**/*.test.ts"], + }, +}); diff --git a/.github/extensions/agentic-workflows-dashboard/web/app.js b/.github/extensions/agentic-workflows-dashboard/web/app.js index 656269d01ee..43e5d1d9241 100644 --- a/.github/extensions/agentic-workflows-dashboard/web/app.js +++ b/.github/extensions/agentic-workflows-dashboard/web/app.js @@ -1,3 +1,4 @@ +import { paginate } from "./pagination.js"; const definitionCount = 240; const runCount = 420; function isoHoursAgo(hours) { @@ -56,22 +57,6 @@ function buildRuns(count, definitions) { }; }); } -function paginate(items, page, pageSize) { - const totalItems = items.length; - const totalPages = Math.max(1, Math.ceil(totalItems / pageSize)); - const safePage = Math.min(Math.max(1, page), totalPages); - const start = (safePage - 1) * pageSize; - const end = start + pageSize; - return { - items: items.slice(start, end), - page: safePage, - pageSize, - totalItems, - totalPages, - hasNextPage: safePage < totalPages, - hasPreviousPage: safePage > 1, - }; -} function escapeHtml(value) { return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'"); } @@ -266,4 +251,3 @@ document.addEventListener("alpine:init", () => { stepStatusClass, })); }); -export {}; diff --git a/.github/extensions/agentic-workflows-dashboard/web/pagination.js b/.github/extensions/agentic-workflows-dashboard/web/pagination.js new file mode 100644 index 00000000000..bd8112c36f6 --- /dev/null +++ b/.github/extensions/agentic-workflows-dashboard/web/pagination.js @@ -0,0 +1,16 @@ +export function paginate(items, page, pageSize) { + const totalItems = items.length; + const totalPages = Math.max(1, Math.ceil(totalItems / pageSize)); + const safePage = Math.min(Math.max(1, page), totalPages); + const start = (safePage - 1) * pageSize; + const end = start + pageSize; + return { + items: items.slice(start, end), + page: safePage, + pageSize, + totalItems, + totalPages, + hasNextPage: safePage < totalPages, + hasPreviousPage: safePage > 1, + }; +} diff --git a/Makefile b/Makefile index 6052f928908..83d6957176f 100644 --- a/Makefile +++ b/Makefile @@ -235,6 +235,11 @@ security-govulncheck-sarif: test-js: build-js cd actions/setup/js && npm run test:js -- --no-file-parallelism +# Build and test the Copilot canvas extension for agentic workflows +.PHONY: test-canvas-extension +test-canvas-extension: + cd .github/extensions/agentic-workflows-dashboard && npm ci && npm run build && npm test + # Test impacted JavaScript unit tests only (excluding integration tests) .PHONY: test-impacted-js test-impacted-js: build-js From 2735ec50b6c76168768407191b4b270a85c4b642 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 28 Jun 2026 22:13:59 +0000 Subject: [PATCH 03/10] Refactor canvas dashboard into modular tabbed views Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .../agentic-workflows-dashboard/src/app.ts | 40 ++++++++ .../agentic-workflows-dashboard/web/app.js | 28 ++++++ .../web/index.html | 98 +++++++++---------- 3 files changed, 113 insertions(+), 53 deletions(-) diff --git a/.github/extensions/agentic-workflows-dashboard/src/app.ts b/.github/extensions/agentic-workflows-dashboard/src/app.ts index 946bd94275d..c6d1893cace 100644 --- a/.github/extensions/agentic-workflows-dashboard/src/app.ts +++ b/.github/extensions/agentic-workflows-dashboard/src/app.ts @@ -4,11 +4,15 @@ import { paginate } from "./pagination.js"; type CommandResult = { command: string; output: string }; type FlashKind = "success" | "warn" | "error"; +type DashboardTabId = "definitions" | "runs" | "details" | "commands"; +type DashboardTab = { id: DashboardTabId; label: string; counter?: "definitions" | "runs" }; declare const Alpine: { data: (name: string, callback: () => DashboardState) => void; }; interface DashboardState { + tabs: DashboardTab[]; + activeTab: DashboardTabId; definitionPage: number; runPage: number; pageSize: number; @@ -23,9 +27,13 @@ interface DashboardState { flashMessage: string; flashKind: FlashKind; init(): void; + setActiveTab(tab: DashboardTabId): void; + isActiveTab(tab: DashboardTabId): boolean; + tabCount(tab: DashboardTab): number; loadDefinitionPage(page: number): void; loadRunPage(page: number): void; selectRun(id: string): void; + viewRunDetails(id: string): void; dispatchSelectedWorkflow(): void; runCommand(): void; commandQuickFill(value: string): void; @@ -37,6 +45,12 @@ interface DashboardState { const definitionCount = 240; const runCount = 420; +const dashboardTabs: DashboardTab[] = [ + { id: "definitions", label: "Workflows", counter: "definitions" }, + { id: "runs", label: "Runs", counter: "runs" }, + { id: "details", label: "Run details" }, + { id: "commands", label: "Commands" }, +]; function isoHoursAgo(hours: number): string { return new Date(Date.now() - hours * 60 * 60 * 1000).toISOString(); @@ -241,6 +255,8 @@ const runs = buildRuns(runCount, definitions); document.addEventListener("alpine:init", () => { Alpine.data("dashboardApp", () => ({ + tabs: dashboardTabs, + activeTab: "definitions", definitionPage: 1, runPage: 1, pageSize: 20, @@ -263,6 +279,24 @@ document.addEventListener("alpine:init", () => { } }, + setActiveTab(tab) { + this.activeTab = tab; + }, + + isActiveTab(tab) { + return this.activeTab === tab; + }, + + tabCount(tab) { + if (tab.counter === "definitions") { + return this.definitions.length; + } + if (tab.counter === "runs") { + return this.runs.length; + } + return 0; + }, + loadDefinitionPage(page) { this.definitionPage = page; this.definitionsPaged = paginate(this.definitions, page, this.pageSize); @@ -280,6 +314,11 @@ document.addEventListener("alpine:init", () => { this.selectedRun = this.runs.find(run => run.id === id) ?? null; }, + viewRunDetails(id) { + this.selectRun(id); + this.setActiveTab("details"); + }, + dispatchSelectedWorkflow() { const definition = this.definitions.find(item => item.id === this.selectedDefinitionId); if (!definition) { @@ -302,6 +341,7 @@ document.addEventListener("alpine:init", () => { this.runs = [newRun, ...this.runs]; this.loadRunPage(1); this.selectRun(newRun.id); + this.setActiveTab("runs"); this.flashKind = "success"; this.flashMessage = `Dispatched ${definition.name} as ${newRun.id}.`; diff --git a/.github/extensions/agentic-workflows-dashboard/web/app.js b/.github/extensions/agentic-workflows-dashboard/web/app.js index 43e5d1d9241..772447bd42c 100644 --- a/.github/extensions/agentic-workflows-dashboard/web/app.js +++ b/.github/extensions/agentic-workflows-dashboard/web/app.js @@ -1,6 +1,12 @@ import { paginate } from "./pagination.js"; const definitionCount = 240; const runCount = 420; +const dashboardTabs = [ + { id: "definitions", label: "Workflows", counter: "definitions" }, + { id: "runs", label: "Runs", counter: "runs" }, + { id: "details", label: "Run details" }, + { id: "commands", label: "Commands" }, +]; function isoHoursAgo(hours) { return new Date(Date.now() - hours * 60 * 60 * 1000).toISOString(); } @@ -178,6 +184,8 @@ const definitions = buildDefinitions(definitionCount); const runs = buildRuns(runCount, definitions); document.addEventListener("alpine:init", () => { Alpine.data("dashboardApp", () => ({ + tabs: dashboardTabs, + activeTab: "definitions", definitionPage: 1, runPage: 1, pageSize: 20, @@ -198,6 +206,21 @@ document.addEventListener("alpine:init", () => { this.runCommand(); } }, + setActiveTab(tab) { + this.activeTab = tab; + }, + isActiveTab(tab) { + return this.activeTab === tab; + }, + tabCount(tab) { + if (tab.counter === "definitions") { + return this.definitions.length; + } + if (tab.counter === "runs") { + return this.runs.length; + } + return 0; + }, loadDefinitionPage(page) { this.definitionPage = page; this.definitionsPaged = paginate(this.definitions, page, this.pageSize); @@ -212,6 +235,10 @@ document.addEventListener("alpine:init", () => { selectRun(id) { this.selectedRun = this.runs.find(run => run.id === id) ?? null; }, + viewRunDetails(id) { + this.selectRun(id); + this.setActiveTab("details"); + }, dispatchSelectedWorkflow() { const definition = this.definitions.find(item => item.id === this.selectedDefinitionId); if (!definition) { @@ -232,6 +259,7 @@ document.addEventListener("alpine:init", () => { this.runs = [newRun, ...this.runs]; this.loadRunPage(1); this.selectRun(newRun.id); + this.setActiveTab("runs"); this.flashKind = "success"; this.flashMessage = `Dispatched ${definition.name} as ${newRun.id}.`; }, diff --git a/.github/extensions/agentic-workflows-dashboard/web/index.html b/.github/extensions/agentic-workflows-dashboard/web/index.html index 4537c98a3cc..e6977b4c8fe 100644 --- a/.github/extensions/agentic-workflows-dashboard/web/index.html +++ b/.github/extensions/agentic-workflows-dashboard/web/index.html @@ -10,16 +10,6 @@ min-height: 100vh; background: var(--bgColor-default, #ffffff); } - .awd-grid { - display: grid; - gap: 16px; - grid-template-columns: 1fr; - } - @media (min-width: 960px) { - .awd-grid { - grid-template-columns: minmax(260px, 1fr) minmax(360px, 1.2fr) minmax(360px, 1.2fr); - } - } .awd-scroll { max-height: 55vh; overflow: auto; @@ -49,14 +39,12 @@ @@ -64,7 +52,7 @@
    -
    +

    Workflow definitions

    @@ -90,13 +78,15 @@

    Workflow definitions Next

    +
    +

    Workflow runs

    -
    +
    @@ -113,48 +103,50 @@

    Workflow runs Next

    +
    -
    -
    -
    -

    Run details

    +
    +
    +
    +

    Run details

    +
    +
    +
    + +
    -
    -
    - - -
    -
    Created:
    -
    -
    -
    - - -
    -
    +
    Created:
    +
    +
    +
    + +
    +
    -
    Select a workflow run to review step summaries.
    +
    Select a workflow run to review step summaries.
    +
    +
    -
    -
    -

    Command panel

    +
    +
    +
    +

    Command panel

    +
    +
    +
    + + + +
    -
    -
    - - - - -
    -
    - - -
    -
    
    +              
    + +
    +
    
                 
    From 6dfe1c23485e67e9a8a998ece2c5f60fca181caa Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 28 Jun 2026 22:21:31 +0000 Subject: [PATCH 04/10] Polish tabbed dashboard state and accessibility Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/extensions/agentic-workflows-dashboard/src/app.ts | 7 ++++--- .github/extensions/agentic-workflows-dashboard/web/app.js | 7 ++++--- .../extensions/agentic-workflows-dashboard/web/index.html | 4 ++-- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/.github/extensions/agentic-workflows-dashboard/src/app.ts b/.github/extensions/agentic-workflows-dashboard/src/app.ts index c6d1893cace..8aba0f9be03 100644 --- a/.github/extensions/agentic-workflows-dashboard/src/app.ts +++ b/.github/extensions/agentic-workflows-dashboard/src/app.ts @@ -280,7 +280,9 @@ document.addEventListener("alpine:init", () => { }, setActiveTab(tab) { - this.activeTab = tab; + if (this.tabs.some(item => item.id === tab)) { + this.activeTab = tab; + } }, isActiveTab(tab) { @@ -340,8 +342,7 @@ document.addEventListener("alpine:init", () => { this.runs = [newRun, ...this.runs]; this.loadRunPage(1); - this.selectRun(newRun.id); - this.setActiveTab("runs"); + this.viewRunDetails(newRun.id); this.flashKind = "success"; this.flashMessage = `Dispatched ${definition.name} as ${newRun.id}.`; diff --git a/.github/extensions/agentic-workflows-dashboard/web/app.js b/.github/extensions/agentic-workflows-dashboard/web/app.js index 772447bd42c..b602c401d05 100644 --- a/.github/extensions/agentic-workflows-dashboard/web/app.js +++ b/.github/extensions/agentic-workflows-dashboard/web/app.js @@ -207,7 +207,9 @@ document.addEventListener("alpine:init", () => { } }, setActiveTab(tab) { - this.activeTab = tab; + if (this.tabs.some(item => item.id === tab)) { + this.activeTab = tab; + } }, isActiveTab(tab) { return this.activeTab === tab; @@ -258,8 +260,7 @@ document.addEventListener("alpine:init", () => { }; this.runs = [newRun, ...this.runs]; this.loadRunPage(1); - this.selectRun(newRun.id); - this.setActiveTab("runs"); + this.viewRunDetails(newRun.id); this.flashKind = "success"; this.flashMessage = `Dispatched ${definition.name} as ${newRun.id}.`; }, diff --git a/.github/extensions/agentic-workflows-dashboard/web/index.html b/.github/extensions/agentic-workflows-dashboard/web/index.html index e6977b4c8fe..c795265b5be 100644 --- a/.github/extensions/agentic-workflows-dashboard/web/index.html +++ b/.github/extensions/agentic-workflows-dashboard/web/index.html @@ -40,10 +40,10 @@ From 06e1f424e647a49d42241cd1f969bf6704a85f97 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 28 Jun 2026 22:35:30 +0000 Subject: [PATCH 05/10] Add canvas command wiring, styling split, and extension lint/fmt targets Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .../.impeccable/config.json | 7 +++ .../agentic-workflows-dashboard/PRODUCT.md | 24 ++++++++ .../agentic-workflows-dashboard/extension.mjs | 56 ++++++++++++++++++- .../agentic-workflows-dashboard/package.json | 5 +- .../agentic-workflows-dashboard/src/app.ts | 27 ++++++++- .../agentic-workflows-dashboard/web/app.js | 23 +++++++- .../web/index.html | 25 ++------- .../web/styles.css | 24 ++++++++ Makefile | 11 ++++ 9 files changed, 177 insertions(+), 25 deletions(-) create mode 100644 .github/extensions/agentic-workflows-dashboard/.impeccable/config.json create mode 100644 .github/extensions/agentic-workflows-dashboard/PRODUCT.md create mode 100644 .github/extensions/agentic-workflows-dashboard/web/styles.css diff --git a/.github/extensions/agentic-workflows-dashboard/.impeccable/config.json b/.github/extensions/agentic-workflows-dashboard/.impeccable/config.json new file mode 100644 index 00000000000..e5d5bf3641b --- /dev/null +++ b/.github/extensions/agentic-workflows-dashboard/.impeccable/config.json @@ -0,0 +1,7 @@ +{ + "detector": { + "designSystem": { + "enabled": true + } + } +} diff --git a/.github/extensions/agentic-workflows-dashboard/PRODUCT.md b/.github/extensions/agentic-workflows-dashboard/PRODUCT.md new file mode 100644 index 00000000000..e3353a04fdd --- /dev/null +++ b/.github/extensions/agentic-workflows-dashboard/PRODUCT.md @@ -0,0 +1,24 @@ +# Agentic Workflows Dashboard + +## Surface + +Product UI (GitHub Copilot Canvas dashboard). + +## Audience + +Repository maintainers operating `gh aw` workflows in `github/gh-aw`. + +## Product intent + +Provide a GitHub.com-like control surface for: + +- exploring workflow definitions, +- monitoring workflow runs, +- reviewing safe run summaries, +- dispatching and auditing workflows from one place. + +## Design lane + +- Match GitHub visual language (Primer components, muted hierarchy, compact controls). +- Keep information dense and scannable. +- Favor predictable tabs and explicit command affordances over decorative UI. diff --git a/.github/extensions/agentic-workflows-dashboard/extension.mjs b/.github/extensions/agentic-workflows-dashboard/extension.mjs index 4d626a42fb7..2ffac01b197 100644 --- a/.github/extensions/agentic-workflows-dashboard/extension.mjs +++ b/.github/extensions/agentic-workflows-dashboard/extension.mjs @@ -172,13 +172,41 @@ function runGhAwAudit(args = "") { }; } +function runGhAwCompile(args = "") { + const argsText = typeof args === "string" ? args : JSON.stringify(args); + return { + command: `gh aw compile ${argsText}`.trim(), + output: `Compile summary\n- definitions loaded: ${definitions.length}\n- runs indexed: ${runs.length}\n- status: success`, + }; +} + +function runGhAwAuditDiff(args = "") { + const argsText = typeof args === "string" ? args : JSON.stringify(args); + const referencedRuns = argsText.match(/run-\d{5}/g) ?? []; + const baseRun = findRun(referencedRuns[0] ?? runs[1]?.id ?? ""); + const compareRun = findRun(referencedRuns[1] ?? runs[0]?.id ?? ""); + + if (!baseRun || !compareRun) { + return { + command: `gh aw audit-diff ${argsText}`.trim(), + output: "Need two valid runs for diff. Example: gh aw audit-diff run-00002 run-00003", + }; + } + + return { + command: `gh aw audit-diff ${baseRun.id} ${compareRun.id}`, + output: `Audit diff\n- base: ${baseRun.id} (${baseRun.status})\n- compare: ${compareRun.id} (${compareRun.status})\n- step delta: ${compareRun.steps.length - baseRun.steps.length}`, + }; +} + async function renderDashboardUrl() { const htmlPath = join(__dirname, "web", "index.html"); const appPath = join(__dirname, "web", "app.js"); + const cssPath = join(__dirname, "web", "styles.css"); - const [htmlTemplate, appBundle] = await Promise.all([readFile(htmlPath, "utf8"), readFile(appPath, "utf8")]); + const [htmlTemplate, appBundle, cssBundle] = await Promise.all([readFile(htmlPath, "utf8"), readFile(appPath, "utf8"), readFile(cssPath, "utf8")]); - const html = htmlTemplate.replace("/*__APP_JS__*/", appBundle); + const html = htmlTemplate.replace("/*__APP_CSS__*/", cssBundle).replace("/*__APP_JS__*/", appBundle); return `data:text/html;charset=utf-8,${encodeURIComponent(html)}`; } @@ -281,6 +309,30 @@ await joinSession({ }, handler: ctx => runGhAwAudit(String(ctx.input?.args ?? "")), }, + { + name: "runGhAwCompile", + description: "Run gh aw compile command behavior.", + inputSchema: { + type: "object", + properties: { + args: { type: "string" }, + }, + additionalProperties: false, + }, + handler: ctx => runGhAwCompile(String(ctx.input?.args ?? "")), + }, + { + name: "runGhAwAuditDiff", + description: "Run gh aw audit-diff command behavior.", + inputSchema: { + type: "object", + properties: { + args: { type: "string" }, + }, + additionalProperties: false, + }, + handler: ctx => runGhAwAuditDiff(String(ctx.input?.args ?? "")), + }, ], open: async () => { return { diff --git a/.github/extensions/agentic-workflows-dashboard/package.json b/.github/extensions/agentic-workflows-dashboard/package.json index 85c9fbacd05..d78a684a2c4 100644 --- a/.github/extensions/agentic-workflows-dashboard/package.json +++ b/.github/extensions/agentic-workflows-dashboard/package.json @@ -14,6 +14,9 @@ "scripts": { "build": "tsc -p tsconfig.json", "typecheck": "tsc -p tsconfig.json --noEmit", - "test": "vitest run" + "test": "vitest run", + "lint": "npm run typecheck && npm test", + "fmt": "npx prettier --write --parser typescript \"src/**/*.ts\" \"test/**/*.ts\" \"vitest.config.ts\" && npx prettier --write --parser html \"web/index.html\" && npx prettier --write --parser css \"web/styles.css\" && npx prettier --write --parser babel \"extension.mjs\" && npx prettier --write --parser json \"copilot-extension.json\" \"package.json\"", + "impeccable:detect": "npx impeccable detect web/index.html web/styles.css" } } diff --git a/.github/extensions/agentic-workflows-dashboard/src/app.ts b/.github/extensions/agentic-workflows-dashboard/src/app.ts index 8aba0f9be03..e085ea966f9 100644 --- a/.github/extensions/agentic-workflows-dashboard/src/app.ts +++ b/.github/extensions/agentic-workflows-dashboard/src/app.ts @@ -197,6 +197,13 @@ function runGhCommand(command: string, runs: WorkflowRun[]): CommandResult { }; } + if (normalized === "gh aw compile") { + return { + command: normalized, + output: `Compile summary\n- definitions loaded: ${definitions.length}\n- runs indexed: ${runs.length}\n- status: success`, + }; + } + if (normalized === "gh aw audit") { const completed = runs.filter(run => run.status === "completed").length; const failed = runs.filter(run => run.status === "failed").length; @@ -206,6 +213,24 @@ function runGhCommand(command: string, runs: WorkflowRun[]): CommandResult { }; } + if (normalized.startsWith("gh aw audit-diff")) { + const referencedRuns = normalized.match(/run-\d{5}/g) ?? []; + const baseRun = runs.find(item => item.id === (referencedRuns[0] ?? runs[1]?.id)); + const compareRun = runs.find(item => item.id === (referencedRuns[1] ?? runs[0]?.id)); + + if (!baseRun || !compareRun) { + return { + command: normalized, + output: "Need two valid runs for diff. Example: gh aw audit-diff run-00002 run-00003", + }; + } + + return { + command: normalized, + output: `Audit diff\n- base: ${baseRun.id} (${baseRun.status})\n- compare: ${compareRun.id} (${compareRun.status})\n- step delta: ${compareRun.steps.length - baseRun.steps.length}`, + }; + } + if (normalized.startsWith("gh aw audit --run") && runFlagMatch?.[1]) { const run = runs.find(item => item.id === runFlagMatch[1]); if (!run) { @@ -220,7 +245,7 @@ function runGhCommand(command: string, runs: WorkflowRun[]): CommandResult { return { command: normalized, - output: "Supported commands: gh aw logs, gh aw logs --run , gh aw audit, gh aw audit --run .", + output: "Supported commands: gh aw logs, gh aw logs --run , gh aw compile, gh aw audit, gh aw audit --run , gh aw audit-diff.", }; } diff --git a/.github/extensions/agentic-workflows-dashboard/web/app.js b/.github/extensions/agentic-workflows-dashboard/web/app.js index b602c401d05..240b3ba8f9f 100644 --- a/.github/extensions/agentic-workflows-dashboard/web/app.js +++ b/.github/extensions/agentic-workflows-dashboard/web/app.js @@ -132,6 +132,12 @@ function runGhCommand(command, runs) { output: `Logs for ${run.id}\n- definition: ${run.definitionId}\n- status: ${run.status}\n- steps: ${run.steps.length}`, }; } + if (normalized === "gh aw compile") { + return { + command: normalized, + output: `Compile summary\n- definitions loaded: ${definitions.length}\n- runs indexed: ${runs.length}\n- status: success`, + }; + } if (normalized === "gh aw audit") { const completed = runs.filter(run => run.status === "completed").length; const failed = runs.filter(run => run.status === "failed").length; @@ -140,6 +146,21 @@ function runGhCommand(command, runs) { output: `Audit summary\n- total runs: ${runs.length}\n- completed: ${completed}\n- failed: ${failed}`, }; } + if (normalized.startsWith("gh aw audit-diff")) { + const referencedRuns = normalized.match(/run-\d{5}/g) ?? []; + const baseRun = runs.find(item => item.id === (referencedRuns[0] ?? runs[1]?.id)); + const compareRun = runs.find(item => item.id === (referencedRuns[1] ?? runs[0]?.id)); + if (!baseRun || !compareRun) { + return { + command: normalized, + output: "Need two valid runs for diff. Example: gh aw audit-diff run-00002 run-00003", + }; + } + return { + command: normalized, + output: `Audit diff\n- base: ${baseRun.id} (${baseRun.status})\n- compare: ${compareRun.id} (${compareRun.status})\n- step delta: ${compareRun.steps.length - baseRun.steps.length}`, + }; + } if (normalized.startsWith("gh aw audit --run") && runFlagMatch?.[1]) { const run = runs.find(item => item.id === runFlagMatch[1]); if (!run) { @@ -153,7 +174,7 @@ function runGhCommand(command, runs) { } return { command: normalized, - output: "Supported commands: gh aw logs, gh aw logs --run , gh aw audit, gh aw audit --run .", + output: "Supported commands: gh aw logs, gh aw logs --run , gh aw compile, gh aw audit, gh aw audit --run , gh aw audit-diff.", }; } function statusClass(status) { diff --git a/.github/extensions/agentic-workflows-dashboard/web/index.html b/.github/extensions/agentic-workflows-dashboard/web/index.html index c795265b5be..4eea8868397 100644 --- a/.github/extensions/agentic-workflows-dashboard/web/index.html +++ b/.github/extensions/agentic-workflows-dashboard/web/index.html @@ -5,26 +5,9 @@ Agentic Workflows Dashboard + @@ -139,8 +122,10 @@

    Command panel

    + +
    @@ -154,7 +139,7 @@

    Command panel

    diff --git a/.github/extensions/agentic-workflows-dashboard/web/styles.css b/.github/extensions/agentic-workflows-dashboard/web/styles.css new file mode 100644 index 00000000000..b1c5f8446b5 --- /dev/null +++ b/.github/extensions/agentic-workflows-dashboard/web/styles.css @@ -0,0 +1,24 @@ +.awd-shell { + min-height: 100vh; + background: var(--bgColor-default, #ffffff); + padding: 0.75rem; +} + +.awd-scroll { + max-height: 55vh; + overflow: auto; +} + +.awd-run-row { + cursor: pointer; +} + +.awd-run-row.is-selected { + background: var(--bgColor-accent-muted, #ddf4ff); +} + +.awd-command-output { + white-space: pre-wrap; + max-height: 180px; + overflow: auto; +} diff --git a/Makefile b/Makefile index 83d6957176f..73b133835c3 100644 --- a/Makefile +++ b/Makefile @@ -240,6 +240,14 @@ test-js: build-js test-canvas-extension: cd .github/extensions/agentic-workflows-dashboard && npm ci && npm run build && npm test +.PHONY: fmt-canvas-extension +fmt-canvas-extension: + cd .github/extensions/agentic-workflows-dashboard && npm ci && npm run fmt + +.PHONY: lint-canvas-extension +lint-canvas-extension: + cd .github/extensions/agentic-workflows-dashboard && npm ci && npm run lint + # Test impacted JavaScript unit tests only (excluding integration tests) .PHONY: test-impacted-js test-impacted-js: build-js @@ -1090,8 +1098,11 @@ help: @echo " test-unit - Run Go unit tests only (faster)" @echo " test-security - Run security regression tests" @echo " test-js - Run JavaScript tests" + @echo " test-canvas-extension - Build and test the Copilot canvas extension" @echo " test-impacted-js - Run impacted JavaScript unit tests for current branch changes" @echo " test-impacted-go - Run impacted Go unit tests for current branch changes" + @echo " fmt-canvas-extension - Format the Copilot canvas extension sources" + @echo " lint-canvas-extension - Lint/typecheck/test the Copilot canvas extension" @echo " test-impacted - Run impacted JavaScript and Go unit tests for current branch changes" @echo " test-all - Run all tests (Go, JavaScript, and wasm golden)" @echo " test-wasm-golden - Run wasm golden tests (Go string API path)" From 4e3d023d3e2e5020da9485e03c3939191e5cfa8a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 28 Jun 2026 22:43:45 +0000 Subject: [PATCH 06/10] Refine audit-diff quick fill and tidy canvas formatting scripts Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .../agentic-workflows-dashboard/extension.mjs | 11 +++++++-- .../agentic-workflows-dashboard/package.json | 7 +++++- .../agentic-workflows-dashboard/src/app.ts | 24 +++++++++++++++++-- .../agentic-workflows-dashboard/web/app.js | 20 ++++++++++++++-- .../web/index.html | 3 +-- .../web/styles.css | 2 +- 6 files changed, 57 insertions(+), 10 deletions(-) diff --git a/.github/extensions/agentic-workflows-dashboard/extension.mjs b/.github/extensions/agentic-workflows-dashboard/extension.mjs index 2ffac01b197..a4203618ca4 100644 --- a/.github/extensions/agentic-workflows-dashboard/extension.mjs +++ b/.github/extensions/agentic-workflows-dashboard/extension.mjs @@ -183,8 +183,15 @@ function runGhAwCompile(args = "") { function runGhAwAuditDiff(args = "") { const argsText = typeof args === "string" ? args : JSON.stringify(args); const referencedRuns = argsText.match(/run-\d{5}/g) ?? []; - const baseRun = findRun(referencedRuns[0] ?? runs[1]?.id ?? ""); - const compareRun = findRun(referencedRuns[1] ?? runs[0]?.id ?? ""); + if (referencedRuns.length < 2) { + return { + command: `gh aw audit-diff ${argsText}`.trim(), + output: "Need two valid runs for diff. Example: gh aw audit-diff run-00002 run-00003", + }; + } + + const baseRun = findRun(referencedRuns[0]); + const compareRun = findRun(referencedRuns[1]); if (!baseRun || !compareRun) { return { diff --git a/.github/extensions/agentic-workflows-dashboard/package.json b/.github/extensions/agentic-workflows-dashboard/package.json index d78a684a2c4..20db307314a 100644 --- a/.github/extensions/agentic-workflows-dashboard/package.json +++ b/.github/extensions/agentic-workflows-dashboard/package.json @@ -16,7 +16,12 @@ "typecheck": "tsc -p tsconfig.json --noEmit", "test": "vitest run", "lint": "npm run typecheck && npm test", - "fmt": "npx prettier --write --parser typescript \"src/**/*.ts\" \"test/**/*.ts\" \"vitest.config.ts\" && npx prettier --write --parser html \"web/index.html\" && npx prettier --write --parser css \"web/styles.css\" && npx prettier --write --parser babel \"extension.mjs\" && npx prettier --write --parser json \"copilot-extension.json\" \"package.json\"", + "fmt": "npm run fmt:ts && npm run fmt:html && npm run fmt:css && npm run fmt:js && npm run fmt:json", + "fmt:ts": "npx prettier --write --parser typescript \"src/**/*.ts\" \"test/**/*.ts\" \"vitest.config.ts\"", + "fmt:html": "npx prettier --write --parser html \"web/index.html\"", + "fmt:css": "npx prettier --write --parser css \"web/styles.css\"", + "fmt:js": "npx prettier --write --parser babel \"extension.mjs\"", + "fmt:json": "npx prettier --write --parser json \"copilot-extension.json\" \"package.json\"", "impeccable:detect": "npx impeccable detect web/index.html web/styles.css" } } diff --git a/.github/extensions/agentic-workflows-dashboard/src/app.ts b/.github/extensions/agentic-workflows-dashboard/src/app.ts index e085ea966f9..864597f4efe 100644 --- a/.github/extensions/agentic-workflows-dashboard/src/app.ts +++ b/.github/extensions/agentic-workflows-dashboard/src/app.ts @@ -37,6 +37,7 @@ interface DashboardState { dispatchSelectedWorkflow(): void; runCommand(): void; commandQuickFill(value: string): void; + auditDiffQuickFill(): string; renderMarkdown(markdown: string): string; formatDate(iso: string): string; runStatusClass(status: WorkflowRunStatus): string; @@ -215,8 +216,15 @@ function runGhCommand(command: string, runs: WorkflowRun[]): CommandResult { if (normalized.startsWith("gh aw audit-diff")) { const referencedRuns = normalized.match(/run-\d{5}/g) ?? []; - const baseRun = runs.find(item => item.id === (referencedRuns[0] ?? runs[1]?.id)); - const compareRun = runs.find(item => item.id === (referencedRuns[1] ?? runs[0]?.id)); + if (referencedRuns.length < 2) { + return { + command: normalized, + output: "Need two valid runs for diff. Example: gh aw audit-diff run-00002 run-00003", + }; + } + + const baseRun = runs.find(item => item.id === referencedRuns[0]); + const compareRun = runs.find(item => item.id === referencedRuns[1]); if (!baseRun || !compareRun) { return { @@ -383,6 +391,18 @@ document.addEventListener("alpine:init", () => { this.runCommand(); }, + auditDiffQuickFill() { + const selectedId = this.selectedRun?.id; + if (!selectedId) { + return "gh aw audit-diff run-00001 run-00002"; + } + + const firstRunId = this.runs[0]?.id ?? "run-00001"; + const secondRunId = this.runs[1]?.id ?? "run-00002"; + const compareId = selectedId === firstRunId ? secondRunId : firstRunId; + return `gh aw audit-diff ${selectedId} ${compareId}`; + }, + renderMarkdown(markdown) { return renderSafeMarkdown(markdown); }, diff --git a/.github/extensions/agentic-workflows-dashboard/web/app.js b/.github/extensions/agentic-workflows-dashboard/web/app.js index 240b3ba8f9f..9f2d239b695 100644 --- a/.github/extensions/agentic-workflows-dashboard/web/app.js +++ b/.github/extensions/agentic-workflows-dashboard/web/app.js @@ -148,8 +148,14 @@ function runGhCommand(command, runs) { } if (normalized.startsWith("gh aw audit-diff")) { const referencedRuns = normalized.match(/run-\d{5}/g) ?? []; - const baseRun = runs.find(item => item.id === (referencedRuns[0] ?? runs[1]?.id)); - const compareRun = runs.find(item => item.id === (referencedRuns[1] ?? runs[0]?.id)); + if (referencedRuns.length < 2) { + return { + command: normalized, + output: "Need two valid runs for diff. Example: gh aw audit-diff run-00002 run-00003", + }; + } + const baseRun = runs.find(item => item.id === referencedRuns[0]); + const compareRun = runs.find(item => item.id === referencedRuns[1]); if (!baseRun || !compareRun) { return { command: normalized, @@ -293,6 +299,16 @@ document.addEventListener("alpine:init", () => { this.commandInput = value; this.runCommand(); }, + auditDiffQuickFill() { + const selectedId = this.selectedRun?.id; + if (!selectedId) { + return "gh aw audit-diff run-00001 run-00002"; + } + const firstRunId = this.runs[0]?.id ?? "run-00001"; + const secondRunId = this.runs[1]?.id ?? "run-00002"; + const compareId = selectedId === firstRunId ? secondRunId : firstRunId; + return `gh aw audit-diff ${selectedId} ${compareId}`; + }, renderMarkdown(markdown) { return renderSafeMarkdown(markdown); }, diff --git a/.github/extensions/agentic-workflows-dashboard/web/index.html b/.github/extensions/agentic-workflows-dashboard/web/index.html index 4eea8868397..f54a6b85625 100644 --- a/.github/extensions/agentic-workflows-dashboard/web/index.html +++ b/.github/extensions/agentic-workflows-dashboard/web/index.html @@ -5,7 +5,6 @@ Agentic Workflows Dashboard - @@ -125,7 +124,7 @@

    Command panel

    - +
    diff --git a/.github/extensions/agentic-workflows-dashboard/web/styles.css b/.github/extensions/agentic-workflows-dashboard/web/styles.css index b1c5f8446b5..5b1efc52ef0 100644 --- a/.github/extensions/agentic-workflows-dashboard/web/styles.css +++ b/.github/extensions/agentic-workflows-dashboard/web/styles.css @@ -1,7 +1,7 @@ .awd-shell { min-height: 100vh; background: var(--bgColor-default, #ffffff); - padding: 0.75rem; + padding: var(--base-size-16, 1rem); } .awd-scroll { From 5f11d4e182238d817218384c4602accb56f68e21 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 28 Jun 2026 22:58:04 +0000 Subject: [PATCH 07/10] Wire fmt/lint to canvas dashboard targets Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 73b133835c3..48a79b87368 100644 --- a/Makefile +++ b/Makefile @@ -779,7 +779,7 @@ lint-lock: build # Format code .PHONY: fmt -fmt: fmt-go fmt-cjs fmt-json +fmt: fmt-go fmt-cjs fmt-json fmt-canvas-extension @echo "✓ Code formatted successfully" .PHONY: fmt-go @@ -902,7 +902,7 @@ lint-action-sh: # Validate all project files .PHONY: lint -lint: fmt-check fmt-check-json lint-cjs golint validate-model-alias-chains lint-action-sh +lint: fmt-check fmt-check-json lint-cjs golint validate-model-alias-chains lint-action-sh lint-canvas-extension @echo "✓ All validations passed" # Install the binary locally From cb6e5615f29e9c7d8d758cf9014920a4a17b254f Mon Sep 17 00:00:00 2001 From: pelikhan Date: Sun, 28 Jun 2026 16:39:02 -0700 Subject: [PATCH 08/10] fix(canvas): use loopback HTTP server, fix x-for, remove duplicate SDK dep - Replace data URI with a per-instance loopback HTTP server; the runtime rejects data: scheme URLs with canvas_provider_url_disallowed - Serve web/app.js and web/pagination.js as separate routes so the ES module import chain resolves correctly from the loopback origin - Add onClose handler to shut down the server when a panel is closed - Fix Alpine.js x-for directives: move them from
    to