import http from "node:http"; import { createCanvas, joinSession } from "@github/copilot-sdk/extension"; // In-memory state (ephemeral per provider process) let currentColor = "#6c63ff"; let logEntries = []; const sseClients = new Set(); function broadcast(event, data) { for (const res of sseClients) { res.write(`event: ${event}\ndata: ${JSON.stringify(data)}\n\n`); } } // --- Loopback HTTP server for the iframe --- const server = http.createServer((req, res) => { if (req.method === "GET" && req.url === "/") { res.writeHead(200, { "Content-Type": "text/html" }); res.end(getHTML()); return; } if (req.method === "GET" && req.url === "/events") { res.writeHead(200, { "Content-Type": "text/event-stream", "Cache-Control": "no-cache", Connection: "keep-alive", }); // Send current state immediately res.write(`event: color\ndata: ${JSON.stringify({ color: currentColor })}\n\n`); res.write(`event: log\ndata: ${JSON.stringify({ entries: logEntries })}\n\n`); sseClients.add(res); req.on("close", () => sseClients.delete(res)); return; } if (req.method === "POST" && req.url === "/request-change") { const entry = { time: new Date().toLocaleTimeString(), message: "🖱️ User clicked — requesting a color change..." }; logEntries.push(entry); broadcast("log", { entries: logEntries }); if (session) { session.send({ prompt: "The user clicked the 'Ask Agent to Change Color' button on the Color Orb canvas. Pick a random, fun color and use the set_color canvas action to change the orb, then use log_message to tell them what color you chose and why.", }); } res.writeHead(200, { "Content-Type": "application/json" }); res.end(JSON.stringify({ ok: true })); return; } if (req.method === "POST" && req.url === "/clear-log") { logEntries = []; broadcast("log", { entries: logEntries }); res.writeHead(200, { "Content-Type": "application/json" }); res.end(JSON.stringify({ ok: true })); return; } res.writeHead(404); res.end("Not found"); }); const port = await new Promise((resolve) => { server.listen(0, "127.0.0.1", () => resolve(server.address().port)); }); let session; const canvas = createCanvas({ id: "color-orb", displayName: "Color Orb", description: "An interactive orb whose color can be changed by the agent. The user clicks a button to request a color change, then the agent sets the new color.", actions: [ { name: "set_color", description: "Set the orb color. Accepts any valid CSS color (hex, named, rgb, hsl).", inputSchema: { type: "object", properties: { color: { type: "string", description: "CSS color value, e.g. '#ff6347' or 'tomato'" }, }, required: ["color"], }, handler({ input }) { currentColor = input.color; broadcast("color", { color: currentColor }); return { color: currentColor }; }, }, { name: "log_message", description: "Append a message to the canvas log area visible to the user.", inputSchema: { type: "object", properties: { message: { type: "string", description: "The message to display in the log" }, }, required: ["message"], }, handler({ input }) { const entry = { time: new Date().toLocaleTimeString(), message: input.message }; logEntries.push(entry); broadcast("log", { entries: logEntries }); return { ok: true }; }, }, { name: "clear_log", description: "Clear all messages from the canvas log.", inputSchema: { type: "object", properties: {} }, handler() { logEntries = []; broadcast("log", { entries: logEntries }); return { ok: true }; }, }, ], open({ instanceId }) { return { url: `http://127.0.0.1:${port}`, title: "Color Orb", status: "ready", }; }, }); session = await joinSession({ canvases: [canvas] }); function getHTML() { return `