mirror of
https://github.com/github/awesome-copilot.git
synced 2026-08-07 09:38:45 +00:00
216 lines
7.3 KiB
JavaScript
216 lines
7.3 KiB
JavaScript
import {
|
|
CanvasError,
|
|
createCanvas,
|
|
joinSession,
|
|
} from "@github/copilot-sdk/extension";
|
|
import { resolveAccounts, invalidateAccounts } from "./accounts.mjs";
|
|
import {
|
|
clearArtifactCache,
|
|
deleteCachedArtifact,
|
|
getCacheSummary,
|
|
inspectArtifact,
|
|
} from "./cache.mjs";
|
|
import {
|
|
broadcastCache,
|
|
closeAllPreviews,
|
|
navigateInstance,
|
|
startInstance,
|
|
stopInstance,
|
|
} from "./server.mjs";
|
|
import { stopStaticPreviewsForArtifact } from "./preview.mjs";
|
|
import { loadPrefs, normalizeRepository, savePrefs } from "./state.mjs";
|
|
|
|
function positiveInteger(value, label) {
|
|
const number = Number.parseInt(value, 10);
|
|
if (!Number.isSafeInteger(number) || number <= 0) {
|
|
throw new CanvasError("invalid_input", `${label} must be a positive integer.`);
|
|
}
|
|
return number;
|
|
}
|
|
|
|
const session = await joinSession({
|
|
canvases: [
|
|
createCanvas({
|
|
id: "pr-artifact-explorer",
|
|
displayName: "Artifact Explorer",
|
|
description:
|
|
"Browse GitHub pull requests and securely preview workflow artifacts, including static sites and asciinema recordings.",
|
|
inputSchema: {
|
|
type: "object",
|
|
properties: {
|
|
repository: {
|
|
type: "string",
|
|
description: "Repository in owner/name format.",
|
|
},
|
|
pullNumber: {
|
|
type: "integer",
|
|
minimum: 1,
|
|
description: "Pull request number to open.",
|
|
},
|
|
url: {
|
|
type: "string",
|
|
description: "GitHub pull request URL to open.",
|
|
},
|
|
},
|
|
additionalProperties: false,
|
|
},
|
|
actions: [
|
|
{
|
|
name: "open_pull_request",
|
|
description: "Navigate an open explorer canvas to a pull request.",
|
|
inputSchema: {
|
|
type: "object",
|
|
properties: {
|
|
repository: { type: "string" },
|
|
pullNumber: { type: "integer", minimum: 1 },
|
|
},
|
|
required: ["repository", "pullNumber"],
|
|
additionalProperties: false,
|
|
},
|
|
handler: (ctx) => {
|
|
const repository = normalizeRepository(ctx.input?.repository);
|
|
const pullNumber = positiveInteger(ctx.input?.pullNumber, "pullNumber");
|
|
const route = `#/pull/${encodeURIComponent(repository)}/${pullNumber}`;
|
|
return {
|
|
navigated: navigateInstance(ctx.instanceId, route),
|
|
repository,
|
|
pullNumber,
|
|
};
|
|
},
|
|
},
|
|
{
|
|
name: "inspect_artifact",
|
|
description: "Download, index, and smart-detect a GitHub Actions artifact.",
|
|
inputSchema: {
|
|
type: "object",
|
|
properties: {
|
|
repository: { type: "string" },
|
|
artifactId: { type: "integer", minimum: 1 },
|
|
},
|
|
required: ["repository", "artifactId"],
|
|
additionalProperties: false,
|
|
},
|
|
handler: async (ctx) => {
|
|
const repository = normalizeRepository(ctx.input?.repository);
|
|
const artifactId = positiveInteger(ctx.input?.artifactId, "artifactId");
|
|
const prefs = await loadPrefs();
|
|
const auth = await resolveAccounts({
|
|
preferredId: prefs.account,
|
|
repository,
|
|
});
|
|
if (!auth.activeToken) {
|
|
throw new CanvasError("github_auth_required", "No usable GitHub account was found.");
|
|
}
|
|
const metadata = await inspectArtifact(auth.activeToken, repository, artifactId);
|
|
await broadcastCache();
|
|
navigateInstance(
|
|
ctx.instanceId,
|
|
`#/artifact/${encodeURIComponent(repository)}/0/${artifactId}`,
|
|
);
|
|
const entries = metadata.analysis.entries ?? [];
|
|
return {
|
|
artifact: metadata.artifact,
|
|
analysis: {
|
|
...metadata.analysis,
|
|
entries: entries.slice(0, 200),
|
|
entriesTruncated: entries.length > 200,
|
|
},
|
|
};
|
|
},
|
|
},
|
|
{
|
|
name: "cache_status",
|
|
description: "Report downloaded artifact cache usage.",
|
|
handler: async () => getCacheSummary(),
|
|
},
|
|
{
|
|
name: "clear_cache",
|
|
description: "Delete one downloaded artifact or clear the entire artifact cache.",
|
|
inputSchema: {
|
|
type: "object",
|
|
properties: {
|
|
artifactId: { type: "integer", minimum: 1 },
|
|
},
|
|
additionalProperties: false,
|
|
},
|
|
handler: async (ctx) => {
|
|
if (ctx.input?.artifactId != null) {
|
|
await stopStaticPreviewsForArtifact(ctx.input.artifactId);
|
|
const result = await deleteCachedArtifact(
|
|
positiveInteger(ctx.input.artifactId, "artifactId"),
|
|
);
|
|
navigateInstance(ctx.instanceId, "#/cache");
|
|
await broadcastCache({ refresh: true });
|
|
return result;
|
|
}
|
|
await closeAllPreviews();
|
|
const result = await clearArtifactCache();
|
|
navigateInstance(ctx.instanceId, "#/cache");
|
|
await broadcastCache({ refresh: true });
|
|
return result;
|
|
},
|
|
},
|
|
{
|
|
name: "accounts",
|
|
description: "List GitHub accounts inherited from the Copilot app and GitHub CLI.",
|
|
handler: async () => {
|
|
const prefs = await loadPrefs();
|
|
const auth = await resolveAccounts({
|
|
preferredId: prefs.account,
|
|
repository: prefs.repository,
|
|
force: true,
|
|
});
|
|
return { active: auth.active, accounts: auth.accounts };
|
|
},
|
|
},
|
|
{
|
|
name: "set_account",
|
|
description: "Select the GitHub account used by the artifact explorer.",
|
|
inputSchema: {
|
|
type: "object",
|
|
properties: { id: { type: "string" } },
|
|
required: ["id"],
|
|
additionalProperties: false,
|
|
},
|
|
handler: async (ctx) => {
|
|
if (typeof ctx.input?.id !== "string" || !ctx.input.id) {
|
|
throw new CanvasError("invalid_account", "Account id is required.");
|
|
}
|
|
const prefs = await loadPrefs();
|
|
invalidateAccounts();
|
|
const auth = await resolveAccounts({
|
|
preferredId: ctx.input.id,
|
|
repository: prefs.repository,
|
|
force: true,
|
|
});
|
|
if (!auth.active || auth.active.id !== ctx.input.id) {
|
|
throw new CanvasError(
|
|
"invalid_account",
|
|
`Account "${ctx.input.id}" was not found or is unavailable.`,
|
|
);
|
|
}
|
|
prefs.account = ctx.input.id;
|
|
await savePrefs(prefs);
|
|
return { active: auth.active, accounts: auth.accounts };
|
|
},
|
|
},
|
|
],
|
|
open: async (ctx) => {
|
|
const entry = await startInstance(
|
|
ctx.instanceId,
|
|
ctx.input,
|
|
(message) => session.log(message, { level: "debug" }),
|
|
);
|
|
return {
|
|
title: "Artifact Explorer",
|
|
status: "GitHub pull request artifacts",
|
|
url: entry.url,
|
|
};
|
|
},
|
|
onClose: async (ctx) => {
|
|
await stopInstance(ctx.instanceId);
|
|
},
|
|
}),
|
|
],
|
|
});
|