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/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..e143cdc7b32 --- /dev/null +++ b/.github/extensions/agentic-workflows-dashboard/extension.mjs @@ -0,0 +1,293 @@ +import { createServer } from "node:http"; +import { execFile } from "node:child_process"; +import { access, readFile } from "node:fs/promises"; +import { constants as fsConstants } from "node:fs"; +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 servers = new Map(); +const cache = new Map(); // key → { data, expiresAt } +const CACHE_TTL_MS = 60_000; +let workspacePath = process.cwd(); + +// --------------------------------------------------------------------------- +// CLI helpers +// --------------------------------------------------------------------------- + +function execp(bin, args, cwd) { + return new Promise((resolve, reject) => { + execFile(bin, args, { + cwd, + env: { ...process.env, NO_COLOR: "1", GH_NO_UPDATE_NOTIFIER: "1" }, + maxBuffer: 10 * 1024 * 1024, + }, (err, stdout, stderr) => { + if (err) reject(Object.assign(err, { stderr: stderr ?? "" })); + else resolve(stdout); + }); + }); +} + +async function runGhAw(args) { + const cwd = workspacePath; + const isWin = process.platform === "win32"; + const devBin = join(cwd, isWin ? "gh-aw.exe" : "gh-aw"); + try { + await access(devBin, fsConstants.X_OK); + return await execp(devBin, args, cwd); + } catch { + return await execp("gh", ["aw", ...args], cwd); + } +} + +// --------------------------------------------------------------------------- +// Cache +// --------------------------------------------------------------------------- + +function getCached(key) { + const entry = cache.get(key); + return entry && Date.now() < entry.expiresAt ? entry.data : null; +} +function setCached(key, data) { + cache.set(key, { data, expiresAt: Date.now() + CACHE_TTL_MS }); +} + +// --------------------------------------------------------------------------- +// Data fetchers — both call the CLI, never Go code +// --------------------------------------------------------------------------- + +async function getDefinitions() { + const hit = getCached("definitions"); + if (hit) return hit; + const raw = await runGhAw(["status", "--json"]); + const data = JSON.parse(raw); + setCached("definitions", data); + return data; +} + +async function getRuns(count = 50) { + const key = `runs:${count}`; + const hit = getCached(key); + if (hit) return hit; + const raw = await runGhAw(["logs", "--json", "-c", String(count)]); + const logsData = JSON.parse(raw); + const runs = logsData.runs ?? []; + setCached(key, runs); + return runs; +} + +// --------------------------------------------------------------------------- +// Command runner for the Commands panel +// --------------------------------------------------------------------------- + +function parseGhAwArgs(raw) { + const m = raw.trim().match(/^(?:gh\s+aw\s+)(.+)$/); + return m ? m[1].trim().split(/\s+/) : null; +} + +async function execCommand(rawCmd) { + const args = parseGhAwArgs(rawCmd); + if (!args) { + return { command: rawCmd, output: "Only 'gh aw ' commands are supported.", error: true }; + } + try { + const output = await runGhAw(args); + return { command: rawCmd, output }; + } catch (err) { + return { command: rawCmd, output: err.stderr || err.message, error: true }; + } +} + +// --------------------------------------------------------------------------- +// Pagination utility +// --------------------------------------------------------------------------- + +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; + return { + items: items.slice(start, start + pageSize), + page: safePage, + pageSize, + totalItems, + totalPages, + hasNextPage: safePage < totalPages, + hasPreviousPage: safePage > 1, + }; +} + +// --------------------------------------------------------------------------- +// Loopback HTTP server per canvas instance +// --------------------------------------------------------------------------- + +async function startServer() { + const server = createServer(async (req, res) => { + const reqUrl = new URL(req.url ?? "/", "http://localhost"); + const pathname = reqUrl.pathname; + + const sendJson = (payload, status = 200) => { + res.writeHead(status, { "Content-Type": "application/json; charset=utf-8" }); + res.end(JSON.stringify(payload)); + }; + + try { + if (pathname === "/" || pathname === "/index.html") { + const [html, css] = await Promise.all([ + readFile(join(__dirname, "web", "index.html"), "utf8"), + readFile(join(__dirname, "web", "styles.css"), "utf8"), + ]); + res.setHeader("Content-Type", "text/html; charset=utf-8"); + res.end(html.replace("/*__APP_CSS__*/", css)); + } else if (pathname === "/app.js") { + res.setHeader("Content-Type", "application/javascript; charset=utf-8"); + res.end(await readFile(join(__dirname, "web", "app.js"), "utf8")); + } else if (pathname === "/pagination.js") { + res.setHeader("Content-Type", "application/javascript; charset=utf-8"); + res.end(await readFile(join(__dirname, "web", "pagination.js"), "utf8")); + } else if (pathname === "/api/status") { + sendJson(await getDefinitions()); + } else if (pathname === "/api/runs") { + const count = parseInt(reqUrl.searchParams.get("count") ?? "50", 10); + sendJson(await getRuns(count)); + } else if (pathname === "/api/run-command") { + const cmd = reqUrl.searchParams.get("cmd") ?? ""; + sendJson(await execCommand(cmd)); + } else if (pathname === "/api/refresh") { + cache.clear(); + sendJson({ ok: true }); + } else { + res.writeHead(404); + res.end("Not found"); + } + } catch (err) { + sendJson({ error: err.message }, 500); + } + }); + await new Promise(r => server.listen(0, "127.0.0.1", r)); + const { port } = server.address(); + return { server, url: `http://127.0.0.1:${port}/` }; +} + +// --------------------------------------------------------------------------- +// Session +// --------------------------------------------------------------------------- + +const session = await joinSession({ + systemMessage: { + mode: "append", + content: `## Agentic Workflows Dashboard + +This canvas shows live data from the current repository using the gh-aw CLI. +It never calls Go code directly — all data is fetched by running CLI subcommands. + +**CLI commands used by this canvas:** +- \`gh aw status --json\` — list agentic workflow definitions (workflow, engine_id, compiled, labels, status, time_remaining) +- \`gh aw logs --json -c \` — list recent workflow runs (run_id, workflow_name, status, conclusion, duration, token_usage, turns, error_count) + +**Dev build** (when gh-aw is not installed as a gh extension): +1. Run \`make build\` in the repository root to compile \`./gh-aw\` (or \`./gh-aw.exe\` on Windows) +2. The canvas auto-detects the dev binary and uses it before falling back to \`gh aw\` + +**Canvas actions available to the agent:** +- \`listDefinitions\` — calls \`gh aw status --json\`, returns paged results +- \`listRuns\` — calls \`gh aw logs --json\`, returns paged results +- \`getRun\` — looks up a single run by \`run_id\` +- \`runCommand\` — executes any \`gh aw \` and returns stdout +- \`refresh\` — clears the 60-second cache so the next call fetches fresh data +`, + }, + canvases: [ + createCanvas({ + id: "agentic-workflows-dashboard", + displayName: "Agentic Workflows Dashboard", + description: "Live dashboard for agentic workflow definitions and runs, powered by gh aw status and gh aw logs.", + actions: [ + { + name: "listDefinitions", + description: "List workflow definitions via gh aw status --json, with paging.", + inputSchema: { + type: "object", + properties: { + page: { type: "number", minimum: 1 }, + pageSize: { type: "number", minimum: 1, maximum: 100 }, + }, + additionalProperties: false, + }, + handler: async ctx => { + const defs = await getDefinitions(); + return paginate(defs, Number(ctx.input?.page ?? 1), Number(ctx.input?.pageSize ?? 20)); + }, + }, + { + name: "listRuns", + description: "List recent workflow runs via gh aw logs --json, with paging.", + inputSchema: { + type: "object", + properties: { + page: { type: "number", minimum: 1 }, + pageSize: { type: "number", minimum: 1, maximum: 100 }, + count: { type: "number", minimum: 1, maximum: 200, description: "Max runs to fetch from the CLI." }, + }, + additionalProperties: false, + }, + handler: async ctx => { + const runs = await getRuns(Number(ctx.input?.count ?? 50)); + return paginate(runs, Number(ctx.input?.page ?? 1), Number(ctx.input?.pageSize ?? 20)); + }, + }, + { + name: "getRun", + description: "Get a single workflow run by its run_id.", + inputSchema: { + type: "object", + required: ["run_id"], + properties: { run_id: { type: "number" } }, + additionalProperties: false, + }, + handler: async ctx => { + const runs = await getRuns(200); + return { run: runs.find(r => r.run_id === Number(ctx.input?.run_id)) ?? null }; + }, + }, + { + name: "runCommand", + description: "Execute a gh aw subcommand (e.g. 'gh aw status', 'gh aw logs -c 5') and return its stdout.", + inputSchema: { + type: "object", + required: ["command"], + properties: { command: { type: "string", description: "Full command string starting with 'gh aw'." } }, + additionalProperties: false, + }, + handler: async ctx => execCommand(String(ctx.input?.command ?? "")), + }, + { + name: "refresh", + description: "Clear the data cache so the next listDefinitions/listRuns fetches fresh data from the CLI.", + inputSchema: { type: "object", additionalProperties: false }, + handler: () => { cache.clear(); return { ok: true }; }, + }, + ], + open: async ctx => { + let entry = servers.get(ctx.instanceId); + if (!entry) { + entry = await startServer(); + servers.set(ctx.instanceId, entry); + } + return { title: "Agentic Workflows Dashboard", status: "Live · gh aw", url: entry.url }; + }, + onClose: async ctx => { + const entry = servers.get(ctx.instanceId); + if (entry) { + servers.delete(ctx.instanceId); + await new Promise(r => entry.server.close(r)); + } + }, + }), + ], +}); + +workspacePath = session.workspacePath ?? process.cwd(); 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..333892eea13 --- /dev/null +++ b/.github/extensions/agentic-workflows-dashboard/package-lock.json @@ -0,0 +1,1505 @@ +{ + "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", + "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": { + "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/@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": "^20.19.0 || >=22.12.0" + } + }, + "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": "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": "^20.19.0 || >=22.12.0" + } + }, + "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": "^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": { + "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..2704f919b4f --- /dev/null +++ b/.github/extensions/agentic-workflows-dashboard/package.json @@ -0,0 +1,25 @@ +{ + "name": "agentic-workflows-dashboard", + "version": "1.0.0", + "type": "module", + "main": "extension.mjs", + "description": "GitHub Copilot canvas extension for agentic workflows dashboard.", + "dependencies": {}, + "devDependencies": { + "typescript": "6.0.3", + "vitest": "4.1.9" + }, + "scripts": { + "build": "tsc -p tsconfig.json", + "typecheck": "tsc -p tsconfig.json --noEmit", + "test": "vitest run", + "lint": "npm run typecheck && npm test", + "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/screenshots/dashboard-full.png b/.github/extensions/agentic-workflows-dashboard/screenshots/dashboard-full.png new file mode 100644 index 00000000000..0bb002114cb Binary files /dev/null and b/.github/extensions/agentic-workflows-dashboard/screenshots/dashboard-full.png differ diff --git a/.github/extensions/agentic-workflows-dashboard/screenshots/dashboard-viewport.png b/.github/extensions/agentic-workflows-dashboard/screenshots/dashboard-viewport.png new file mode 100644 index 00000000000..5f90c3ff659 Binary files /dev/null and b/.github/extensions/agentic-workflows-dashboard/screenshots/dashboard-viewport.png differ 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..864597f4efe --- /dev/null +++ b/.github/extensions/agentic-workflows-dashboard/src/app.ts @@ -0,0 +1,414 @@ +import type { PagedResult, WorkflowDefinition, WorkflowRun, WorkflowRunStatus, WorkflowStep, WorkflowStepStatus } from "./models.js"; +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; + definitions: WorkflowDefinition[]; + runs: WorkflowRun[]; + definitionsPaged: PagedResult; + runsPaged: PagedResult; + selectedRun: WorkflowRun | null; + selectedDefinitionId: string; + commandInput: string; + commandOutput: string; + 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; + auditDiffQuickFill(): string; + renderMarkdown(markdown: string): string; + formatDate(iso: string): string; + runStatusClass(status: WorkflowRunStatus): string; + stepStatusClass(status: WorkflowStepStatus): string; +} + +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(); +} + +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 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 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; + return { + command: normalized, + 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) ?? []; + 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, + 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) { + 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 compile, gh aw audit, gh aw audit --run , gh aw audit-diff.", + }; +} + +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", () => ({ + tabs: dashboardTabs, + activeTab: "definitions", + 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(); + } + }, + + setActiveTab(tab) { + if (this.tabs.some(item => item.id === 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); + }, + + 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; + }, + + viewRunDetails(id) { + this.selectRun(id); + this.setActiveTab("details"); + }, + + 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.viewRunDetails(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(); + }, + + 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); + }, + + 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/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/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/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 new file mode 100644 index 00000000000..2beb638f60b --- /dev/null +++ b/.github/extensions/agentic-workflows-dashboard/web/app.js @@ -0,0 +1,156 @@ +import Alpine from "https://cdn.jsdelivr.net/npm/alpinejs@3.15.0/+esm"; +import { paginate } from "./pagination.js"; + +const dashboardTabs = [ + { id: "definitions", label: "Workflows", counter: "definitions" }, + { id: "runs", label: "Runs", counter: "runs" }, + { id: "details", label: "Run details" }, + { id: "commands", label: "Commands" }, +]; + +function runStatusClass(run) { + const s = run?.status ?? ""; + const c = run?.conclusion ?? ""; + if (s === "completed" || s === "success") { + return c && c !== "success" ? "Label Label--danger" : "Label Label--success"; + } + if (s === "failure" || s === "failed") return "Label Label--danger"; + if (s === "in_progress" || s === "running") return "Label Label--attention"; + return "Label Label--secondary"; +} + +function runStatusLabel(run) { + if (run?.status === "completed" && run?.conclusion) return run.conclusion; + return run?.status ?? "unknown"; +} + +function definitionStatusClass(def) { + if (def?.status === "disabled") return "Label Label--secondary"; + return def?.compiled === "yes" ? "Label Label--success" : "Label Label--attention"; +} + +function definitionStatusLabel(def) { + if (def?.status === "disabled") return "disabled"; + return def?.compiled === "yes" ? "enabled" : "not compiled"; +} + +function formatDuration(ms) { + if (ms == null) return "—"; + const secs = Math.round(ms / 1000); + if (secs < 60) return `${secs}s`; + return `${Math.floor(secs / 60)}m ${secs % 60}s`; +} + +Alpine.data("dashboardApp", () => ({ + tabs: dashboardTabs, + activeTab: "definitions", + pageSize: 20, + definitions: [], + runs: [], + definitionsPaged: paginate([], 1, 20), + runsPaged: paginate([], 1, 20), + selectedRun: null, + commandInput: "gh aw status", + commandOutput: "", + flashMessage: "", + flashKind: "success", + loadingDefinitions: true, + loadingRuns: true, + errorDefinitions: "", + errorRuns: "", + + async init() { + await Promise.all([this.fetchDefinitions(), this.fetchRuns()]); + }, + + async fetchDefinitions() { + this.loadingDefinitions = true; + this.errorDefinitions = ""; + try { + const resp = await fetch("/api/status"); + const data = await resp.json(); + if (!resp.ok) throw new Error(data.error ?? `HTTP ${resp.status}`); + this.definitions = Array.isArray(data) ? data : []; + this.loadDefinitionPage(1); + } catch (e) { + this.errorDefinitions = `Failed to load workflows: ${e.message}`; + } finally { + this.loadingDefinitions = false; + } + }, + + async fetchRuns() { + this.loadingRuns = true; + this.errorRuns = ""; + try { + const resp = await fetch("/api/runs?count=50"); + const data = await resp.json(); + if (!resp.ok) throw new Error(data.error ?? `HTTP ${resp.status}`); + this.runs = Array.isArray(data) ? data : []; + this.loadRunPage(1); + if (!this.selectedRun && this.runs.length > 0) this.selectedRun = this.runs[0]; + } catch (e) { + this.errorRuns = `Failed to load runs: ${e.message}`; + } finally { + this.loadingRuns = false; + } + }, + + async refresh() { + await fetch("/api/refresh"); + this.flashMessage = "Refreshing…"; + this.flashKind = "success"; + await Promise.all([this.fetchDefinitions(), this.fetchRuns()]); + this.flashMessage = "Refreshed."; + setTimeout(() => { this.flashMessage = ""; }, 3000); + }, + + setActiveTab(tab) { + if (this.tabs.some(t => t.id === 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.definitionsPaged = paginate(this.definitions, page, this.pageSize); + }, + loadRunPage(page) { + this.runsPaged = paginate(this.runs, page, this.pageSize); + }, + + selectRun(runId) { + this.selectedRun = this.runs.find(r => r.run_id === runId) ?? null; + }, + viewRunDetails(runId) { + this.selectRun(runId); + this.setActiveTab("details"); + }, + + runStatusClass, + runStatusLabel, + definitionStatusClass, + definitionStatusLabel, + formatDuration, + + async runCommand() { + const cmd = this.commandInput.trim(); + this.commandOutput = `$ ${cmd}\n(running…)`; + try { + const resp = await fetch(`/api/run-command?cmd=${encodeURIComponent(cmd)}`); + const result = await resp.json(); + this.commandOutput = `$ ${result.command ?? cmd}\n${result.output ?? ""}`; + } catch (e) { + this.commandOutput = `$ ${cmd}\nError: ${e.message}`; + } + }, + commandQuickFill(value) { + this.commandInput = value; + this.runCommand(); + }, +})); + +Alpine.start(); 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..1d8eb4e4b26 --- /dev/null +++ b/.github/extensions/agentic-workflows-dashboard/web/index.html @@ -0,0 +1,191 @@ + + + + + + Agentic Workflows Dashboard + + + + +
    +
    +
    +
    +
    github / gh-aw
    +
    Agentic workflows canvas dashboard
    +
    +
    + Copilot Canvas + +
    +
    + +
    + +
    +
    + + +
    +
    +
    +

    Workflow definitions

    +
    + +
    + Loading workflows… +
    + +
    + +
    + +
    + No workflow definitions found. Run make build then open this canvas again. +
    +
    + +
    +
    + + +
    +
    +
    +

    Workflow runs

    +
    + +
    + Loading runs… +
    + +
    + +
    + +
    + No runs found. Run gh aw logs to check availability. +
    +
    + +
    +
    + + +
    +
    +
    +

    Run details

    +
    +
    +
    + Run # + + +
    +
    +
    +
    Duration
    +
    +
    +
    +
    Tokens
    +
    +
    +
    +
    Turns
    +
    +
    +
    +
    Errors
    +
    +
    +
    +
    Warnings
    +
    +
    +
    +
    Missing tools
    +
    +
    +
    +
    +
    Select a workflow run from the Runs tab to view details.
    +
    +
    + + +
    +
    +
    +

    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 {}; 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/.github/extensions/agentic-workflows-dashboard/web/styles.css b/.github/extensions/agentic-workflows-dashboard/web/styles.css new file mode 100644 index 00000000000..5b1efc52ef0 --- /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: var(--base-size-16, 1rem); +} + +.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 6052f928908..48a79b87368 100644 --- a/Makefile +++ b/Makefile @@ -235,6 +235,19 @@ 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 + +.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 @@ -766,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 @@ -889,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 @@ -1085,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)"