Files
awesome-copilot/extensions/connector-namespaces/preview/shots.mjs
T
Alex Yang [MSFT] 191309165e Add Azure Connector Namespaces canvas extension 🤖🤖🤖 (#2250)
* Add MCP Connectors (connector-namespaces) canvas extension

A Copilot CLI canvas extension for browsing and adding MCP connectors
from an Azure Connector Namespace into a Copilot session. Sign-in is
dependency-free (OAuth 2.0 auth-code + PKCE via the Azure CLI public
client, loopback redirect); network access is restricted to the public
Azure Resource Manager endpoint. MIT licensed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Update Connector Namespaces canvas extension

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Harden Connector Namespaces canvas extension

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Sanitize connector icon brand colors

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Add Connector Namespace playground actions

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a986299f-86be-46c3-9562-cbf7d25174cf

* Replace sandbox skill with native tool

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a986299f-86be-46c3-9562-cbf7d25174cf

* Clarify connector disconnect action

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a986299f-86be-46c3-9562-cbf7d25174cf

* Color disconnect action red

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a986299f-86be-46c3-9562-cbf7d25174cf

* Add connector extension plugin manifest

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a986299f-86be-46c3-9562-cbf7d25174cf

* Address connector canvas review feedback

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a986299f-86be-46c3-9562-cbf7d25174cf

* Remove legacy connector canvas manifest

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a986299f-86be-46c3-9562-cbf7d25174cf

* Address remaining connector review feedback

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a986299f-86be-46c3-9562-cbf7d25174cf

* Harden connector review fixes

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a986299f-86be-46c3-9562-cbf7d25174cf

* Harden connector convergence

Address the latest connector review batch across config persistence, executable trust, reauthentication, JSON-RPC transport, smoke safety, and accessibility.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: a986299f-86be-46c3-9562-cbf7d25174cf

* Use native HTTP connector configs

Persist Connector Namespace MCP servers as direct HTTPS entries with API-key headers, remove the stdio unwrap proxy, and exercise the native Streamable HTTP path in smoke coverage.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: a986299f-86be-46c3-9562-cbf7d25174cf

* Secure persisted connector state

Create the Connector Namespace artifacts directory and saved gateway config with private permissions, and align the reduced-motion regression notes with the static fallback behavior.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: a986299f-86be-46c3-9562-cbf7d25174cf

---------

Co-authored-by: Alex Yang <yangalex@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-07-16 10:29:48 +10:00

97 lines
3.6 KiB
JavaScript

// agent-browser screenshot driver for the canvas preview server.
//
// Captures every canvas state to ./shots/ and drives the two interaction flows
// that keep regressing:
// 1. catalog -> click Connect -> sign-in modal with the spinning .si-spin
// 2. restart banner visible -> click dismiss -> banner gone
//
// Requires the preview server to be running:
// node extensions/connector-namespaces/preview/server.mjs
// And agent-browser installed:
// npm i -g agent-browser && agent-browser install
//
// If agent-browser is not installed, this script prints how to install it and
// exits 0 (so it never breaks an unattended run). This is a visual-evidence
// helper; the deterministic regression gate is renderer.test.mjs.
import { spawnSync } from "node:child_process";
import { mkdirSync } from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
const HERE = dirname(fileURLToPath(import.meta.url));
const SHOTS = join(HERE, "shots");
const BASE = "http://127.0.0.1:7331";
function hasAgentBrowser() {
const probe = spawnSync("agent-browser", ["--version"], { encoding: "utf8", shell: true });
return probe.status === 0;
}
function ab(args) {
const r = spawnSync("agent-browser", args, { encoding: "utf8", shell: true });
if (r.status !== 0) {
console.error(`agent-browser ${args.join(" ")} failed:\n${r.stderr || r.stdout}`);
}
return r;
}
function serverUp() {
// Node 18+ has global fetch. Confirm the preview server is reachable.
return fetch(`${BASE}/api/state`).then(() => true).catch(() => false);
}
async function main() {
if (!hasAgentBrowser()) {
console.log("agent-browser is not installed -> skipping screenshots.");
console.log("Install it with: npm i -g agent-browser && agent-browser install");
console.log("Then re-run: node extensions/connector-namespaces/preview/shots.mjs");
process.exit(0);
}
if (!(await serverUp())) {
console.error("preview server is not reachable at " + BASE);
console.error("start it first: node extensions/connector-namespaces/preview/server.mjs");
process.exit(1);
}
mkdirSync(SHOTS, { recursive: true });
// Static states.
const states = [
["catalog", `${BASE}/`],
["catalog-restart-banner", `${BASE}/?restart=1`],
["catalog-installed", `${BASE}/?installed=1`],
["setup", `${BASE}/setup`],
["error", `${BASE}/error`],
];
for (const [name, target] of states) {
ab(["open", target]);
ab(["screenshot", join(SHOTS, `${name}.png`)]);
console.log(`captured ${name}`);
}
// Flow 1: connect -> connecting spinner. The preview /api/install returns
// needsConsent and /oauth-status stays pending, so the .si-spin modal
// spinner keeps animating. Best-effort selector; adjust if markup changes.
ab(["open", `${BASE}/`]);
ab(["click", ".item-add[data-api]"]);
ab(["screenshot", join(SHOTS, "connecting-spinner.png")]);
console.log("captured connecting-spinner (verify the spinner is mid-rotation)");
// Flow 2: banner -> dismiss -> gone. Screenshot before and after the click
// so a frozen/broken dismiss button is visible as a diff.
ab(["open", `${BASE}/?restart=1`]);
ab(["screenshot", join(SHOTS, "banner-before-dismiss.png")]);
ab(["click", ".restart-banner .rb-dismiss"]);
ab(["screenshot", join(SHOTS, "banner-after-dismiss.png")]);
console.log("captured banner-before-dismiss / banner-after-dismiss (after should have no banner)");
console.log(`\nshots written to ${SHOTS}`);
}
main().catch((err) => {
console.error(err);
process.exit(1);
});