mirror of
https://github.com/github/awesome-copilot.git
synced 2026-07-14 01:51:02 +00:00
Add canvas schema validation to extension submission workflow (#2161)
* Add canvas schema and extension submission checks Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * fix: use namespace import for js-yaml Co-authored-by: aaronpowell <434140+aaronpowell@users.noreply.github.com> * Fix contributors page build markup Co-authored-by: aaronpowell <434140+aaronpowell@users.noreply.github.com> * Address PR feedback on canvas schema validation - Add ajv-cli@5 as a pinned devDependency; install via npm ci in CI instead of npx --yes - Fix screenshot path regex to prevent .. traversal segments - Validate canvas.schema.json is parseable JSON even on schema-only PRs Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Harden canvas extension workflow against injection attacks Switch from newline to null-terminated git diff output (git diff -z) so filenames containing newlines are read atomically, matching the existing skill-check.yml pattern. Add an allowlist regex guard on the extracted extension directory name immediately after it is parsed from git diff output. Any name not matching ^[a-z0-9][a-z0-9-]*$ (e.g. names containing dollar signs, parentheses, spaces, or other shell metacharacters) is silently skipped before being used anywhere in the script. Add a matching allowlist guard on each screenshot path extracted from canvas.json before the file-existence check, so a crafted manifest cannot supply a path with shell metacharacters or traversal segments even after the schema check passes. Follows the same defence-in-depth pattern introduced after the injection PoCs in #1236 and #1240. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Replace ajv-cli with in-repo schema validator - Remove ajv-cli to avoid vulnerable/deprecated transitive dependencies - Add eng/validate-json-schema.mjs using ajv + ajv-formats - Update validate-canvas-extensions workflow to use local script - Use npm ci --ignore-scripts in PR validation job Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: aaronpowell <434140+aaronpowell@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -11,6 +11,8 @@ on:
|
||||
- instructions
|
||||
- hooks
|
||||
- workflows
|
||||
- extensions
|
||||
- .schemas/canvas.schema.json
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
@@ -6,130 +6,133 @@ on:
|
||||
types: [opened, synchronize, reopened]
|
||||
paths:
|
||||
- "extensions/**"
|
||||
- ".schemas/canvas.schema.json"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
validate:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
- name: Checkout
|
||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Validate changed canvas extensions
|
||||
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
node-version: "22"
|
||||
cache: "npm"
|
||||
|
||||
// Collect changed extension directories from the PR diff
|
||||
const { execSync } = require('child_process');
|
||||
const changedFiles = execSync(
|
||||
`git diff --name-only origin/${{ github.base_ref }}...HEAD`
|
||||
).toString().trim().split('\n').filter(Boolean);
|
||||
- name: Install dependencies
|
||||
run: npm ci --ignore-scripts
|
||||
|
||||
const EXTENSIONS_DIR = 'extensions';
|
||||
const EXTERNAL_ASSETS_DIR = 'external-assets';
|
||||
- name: Validate changed canvas extensions
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
const changedExtDirs = new Set();
|
||||
for (const file of changedFiles) {
|
||||
const parts = file.split('/');
|
||||
if (parts[0] === EXTENSIONS_DIR && parts.length >= 2) {
|
||||
const extName = parts[1];
|
||||
// Skip the external-assets directory — it's not a canvas extension
|
||||
// Also skip external.json and other files at extensions root level
|
||||
if (extName !== EXTERNAL_ASSETS_DIR && !extName.includes('.')) {
|
||||
changedExtDirs.add(path.join(EXTENSIONS_DIR, extName));
|
||||
}
|
||||
}
|
||||
}
|
||||
# Validate schema structure once, even for schema-only PRs.
|
||||
if ! node ./eng/validate-json-schema.mjs --schema .schemas/canvas.schema.json; then
|
||||
echo "❌ .schemas/canvas.schema.json failed schema compilation"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if (changedExtDirs.size === 0) {
|
||||
console.log('No canvas extension directories changed — skipping validation.');
|
||||
return;
|
||||
}
|
||||
# Collect changed extension directories.
|
||||
# Use null-terminated (-z) output from git diff so filenames containing newlines
|
||||
# or other special characters are read atomically (matches the pattern in skill-check.yml).
|
||||
# Each extracted name is then validated against a strict allowlist regex before use,
|
||||
# rejecting anything containing shell metacharacters ($, (, ), spaces, etc.).
|
||||
declare -A seen_dirs=()
|
||||
changed_extensions=()
|
||||
errors=()
|
||||
|
||||
console.log(`Validating ${changedExtDirs.size} extension(s): ${[...changedExtDirs].join(', ')}`);
|
||||
while IFS= read -r -d '' file; do
|
||||
case "$file" in
|
||||
extensions/*)
|
||||
ext_name="${file#extensions/}"
|
||||
ext_name="${ext_name%%/*}"
|
||||
|
||||
const errors = [];
|
||||
if [ "$ext_name" = "external-assets" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
for (const extDir of changedExtDirs) {
|
||||
if (!fs.existsSync(extDir)) {
|
||||
// Directory was deleted — skip
|
||||
console.log(`${extDir} no longer exists (deleted?), skipping.`);
|
||||
continue;
|
||||
}
|
||||
# Allowlist: extension directory names must be lowercase alphanumeric + hyphens only.
|
||||
# Fail for disallowed names so a PR cannot bypass validation by using a nonconforming folder.
|
||||
if [[ ! "$ext_name" =~ ^[a-z0-9][a-z0-9-]*$ ]]; then
|
||||
errors+=("\`extensions/$ext_name\`: invalid extension directory name (must match ^[a-z0-9][a-z0-9-]*$).")
|
||||
continue
|
||||
fi
|
||||
|
||||
const extName = path.basename(extDir);
|
||||
if [ -z "${seen_dirs[$ext_name]+x}" ]; then
|
||||
seen_dirs["$ext_name"]=1
|
||||
changed_extensions+=("extensions/$ext_name")
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
done < <(git diff --name-only -z "origin/${{ github.base_ref }}...HEAD")
|
||||
|
||||
// Rule 1: must contain extension.mjs
|
||||
const mainFile = path.join(extDir, 'extension.mjs');
|
||||
if (!fs.existsSync(mainFile)) {
|
||||
errors.push(
|
||||
`**\`${extDir}\`**: missing required \`extension.mjs\`. ` +
|
||||
`Canvas extensions must have their entry point named \`extension.mjs\`.`
|
||||
);
|
||||
}
|
||||
if [ "${#changed_extensions[@]}" -eq 0 ]; then
|
||||
if [ "${#errors[@]}" -ne 0 ]; then
|
||||
echo "❌ Canvas extension validation failed:"
|
||||
for error in "${errors[@]}"; do
|
||||
echo "- $error"
|
||||
done
|
||||
exit 1
|
||||
fi
|
||||
echo "No canvas extension directories changed — skipping validation."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
// Rule 2: must contain assets/preview.png
|
||||
const previewFile = path.join(extDir, 'assets', 'preview.png');
|
||||
if (!fs.existsSync(previewFile)) {
|
||||
errors.push(
|
||||
`**\`${extDir}\`**: missing required \`assets/preview.png\`. ` +
|
||||
`Canvas extensions must include a screenshot at \`assets/preview.png\` ` +
|
||||
`so reviewers and users can preview the extension before installing it.`
|
||||
);
|
||||
}
|
||||
}
|
||||
echo "Validating ${#changed_extensions[@]} extension(s): ${changed_extensions[*]}"
|
||||
|
||||
if (errors.length === 0) {
|
||||
console.log('✅ All changed canvas extensions pass validation.');
|
||||
return;
|
||||
}
|
||||
for ext_dir in "${changed_extensions[@]}"; do
|
||||
if [ ! -d "$ext_dir" ]; then
|
||||
echo "$ext_dir no longer exists (deleted?), skipping."
|
||||
continue
|
||||
fi
|
||||
|
||||
const isFork = context.payload.pull_request.head.repo.fork;
|
||||
const body = [
|
||||
'❌ **Canvas extension validation failed**',
|
||||
'',
|
||||
'The following issue(s) were found in changed canvas extension(s):',
|
||||
'',
|
||||
...errors.map(e => `- ${e}`),
|
||||
'',
|
||||
'---',
|
||||
'',
|
||||
'### Required structure for canvas extensions',
|
||||
'',
|
||||
'Each extension folder under `extensions/` must contain:',
|
||||
'',
|
||||
'| Path | Required | Description |',
|
||||
'|------|----------|-------------|',
|
||||
'| `extension.mjs` | ✅ | Entry point for the canvas extension |',
|
||||
'| `assets/preview.png` | ✅ | Screenshot shown on the website and in the marketplace |',
|
||||
'',
|
||||
'Please add the missing file(s) and push an update to this PR.',
|
||||
].join('\n');
|
||||
if [ ! -f "$ext_dir/extension.mjs" ]; then
|
||||
errors+=("\`$ext_dir\`: missing required \`extension.mjs\`.")
|
||||
fi
|
||||
|
||||
if (!isFork) {
|
||||
try {
|
||||
await github.rest.pulls.createReview({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: context.issue.number,
|
||||
event: 'REQUEST_CHANGES',
|
||||
body
|
||||
});
|
||||
} catch (error) {
|
||||
core.warning(`Could not post PR review: ${error.message}`);
|
||||
core.warning(body);
|
||||
}
|
||||
} else {
|
||||
core.warning('PR is from a fork — skipping createReview to avoid permission errors.');
|
||||
core.warning(body);
|
||||
}
|
||||
if [ ! -f "$ext_dir/canvas.json" ]; then
|
||||
errors+=("\`$ext_dir\`: missing required \`canvas.json\`.")
|
||||
continue
|
||||
fi
|
||||
|
||||
core.setFailed(`Canvas extension validation failed with ${errors.length} error(s).`);
|
||||
if [ ! -f "$ext_dir/assets/preview.png" ]; then
|
||||
errors+=("\`$ext_dir\`: missing required \`assets/preview.png\`.")
|
||||
fi
|
||||
|
||||
if ! schema_output="$(node ./eng/validate-json-schema.mjs --schema .schemas/canvas.schema.json --data "$ext_dir/canvas.json" 2>&1)"; then
|
||||
condensed_output="$(echo "$schema_output" | tr '\n' ' ' | sed 's/[[:space:]]\+/ /g')"
|
||||
errors+=("\`$ext_dir/canvas.json\`: schema validation failed against \`.schemas/canvas.schema.json\` ($condensed_output).")
|
||||
continue
|
||||
fi
|
||||
|
||||
mapfile -t screenshot_paths < <(
|
||||
node -e 'const fs = require("fs"); const manifest = JSON.parse(fs.readFileSync(process.argv[1], "utf8")); const paths = [manifest?.screenshots?.icon?.path, manifest?.screenshots?.gallery?.path].filter((value) => typeof value === "string" && value.trim().length > 0); for (const value of [...new Set(paths)]) { console.log(value); }' "$ext_dir/canvas.json"
|
||||
)
|
||||
|
||||
for screenshot_path in "${screenshot_paths[@]}"; do
|
||||
if [[ ! "$screenshot_path" =~ ^assets/([A-Za-z0-9_-]+/)*[A-Za-z0-9_-]+(\.[A-Za-z0-9_-]+)*\.(png|jpe?g|gif|webp|svg)$ ]]; then
|
||||
errors+=("\`$ext_dir/canvas.json\`: screenshot path \`$screenshot_path\` is not a valid assets path.")
|
||||
continue
|
||||
fi
|
||||
if [ ! -f "$ext_dir/$screenshot_path" ]; then
|
||||
errors+=("\`$ext_dir/canvas.json\`: screenshot path \`$screenshot_path\` does not exist in the extension directory.")
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
if [ "${#errors[@]}" -ne 0 ]; then
|
||||
echo "❌ Canvas extension validation failed:"
|
||||
for error in "${errors[@]}"; do
|
||||
echo "- $error"
|
||||
done
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ All changed canvas extensions passed validation."
|
||||
@@ -0,0 +1,121 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "Canvas Extension Manifest",
|
||||
"description": "Schema for extensions/<name>/canvas.json files",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"id",
|
||||
"name",
|
||||
"description",
|
||||
"version",
|
||||
"keywords",
|
||||
"screenshots"
|
||||
],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "Unique identifier for the canvas extension",
|
||||
"pattern": "^[a-z0-9-]+$",
|
||||
"minLength": 1,
|
||||
"maxLength": 100
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Display name for the extension",
|
||||
"minLength": 1,
|
||||
"maxLength": 100
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"description": "Human-friendly description of what the extension does",
|
||||
"minLength": 1,
|
||||
"maxLength": 500
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "Semantic version of the extension metadata",
|
||||
"pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?$"
|
||||
},
|
||||
"author": {
|
||||
"type": "object",
|
||||
"description": "Optional author metadata",
|
||||
"required": [
|
||||
"name"
|
||||
],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 100
|
||||
},
|
||||
"url": {
|
||||
"type": "string",
|
||||
"format": "uri",
|
||||
"maxLength": 2048
|
||||
}
|
||||
}
|
||||
},
|
||||
"keywords": {
|
||||
"type": "array",
|
||||
"description": "Keywords used for search and filtering",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-z0-9-]+$",
|
||||
"minLength": 1,
|
||||
"maxLength": 50
|
||||
},
|
||||
"minItems": 1,
|
||||
"maxItems": 20,
|
||||
"uniqueItems": true
|
||||
},
|
||||
"screenshots": {
|
||||
"type": "object",
|
||||
"description": "Screenshot metadata for icon and gallery cards",
|
||||
"required": [
|
||||
"icon",
|
||||
"gallery"
|
||||
],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"icon": {
|
||||
"$ref": "#/definitions/screenshot"
|
||||
},
|
||||
"gallery": {
|
||||
"$ref": "#/definitions/screenshot"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"screenshot": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"path",
|
||||
"type"
|
||||
],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"path": {
|
||||
"type": "string",
|
||||
"description": "Path relative to the extension root",
|
||||
"pattern": "^assets/(?:[A-Za-z0-9_-]+/)*[A-Za-z0-9_-]+(?:\\.[A-Za-z0-9_-]+)*\\.(png|jpg|jpeg|gif|webp|svg)$",
|
||||
"minLength": 1,
|
||||
"maxLength": 200
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"description": "MIME type for the referenced image",
|
||||
"enum": [
|
||||
"image/png",
|
||||
"image/jpeg",
|
||||
"image/gif",
|
||||
"image/webp",
|
||||
"image/svg+xml"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import Ajv from "ajv";
|
||||
import addFormats from "ajv-formats";
|
||||
|
||||
function parseArgs(argv) {
|
||||
const args = { schema: null, data: null };
|
||||
for (let i = 2; i < argv.length; i += 1) {
|
||||
const arg = argv[i];
|
||||
if (arg === "--schema") {
|
||||
args.schema = argv[i + 1] || null;
|
||||
i += 1;
|
||||
} else if (arg === "--data") {
|
||||
args.data = argv[i + 1] || null;
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
function readJson(filePath) {
|
||||
const content = fs.readFileSync(filePath, "utf8");
|
||||
return JSON.parse(content);
|
||||
}
|
||||
|
||||
const args = parseArgs(process.argv);
|
||||
if (!args.schema) {
|
||||
console.error("Missing required argument: --schema <path>");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const schemaPath = path.resolve(process.cwd(), args.schema);
|
||||
let schema;
|
||||
try {
|
||||
schema = readJson(schemaPath);
|
||||
} catch (error) {
|
||||
console.error(`Invalid schema JSON at ${args.schema}: ${error.message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const ajv = new Ajv({ strict: false, allErrors: true });
|
||||
addFormats(ajv);
|
||||
|
||||
let validate;
|
||||
try {
|
||||
validate = ajv.compile(schema);
|
||||
} catch (error) {
|
||||
console.error(`Invalid schema at ${args.schema}: ${error.message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!args.data) {
|
||||
console.log(`Schema is valid: ${args.schema}`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const dataPath = path.resolve(process.cwd(), args.data);
|
||||
let data;
|
||||
try {
|
||||
data = readJson(dataPath);
|
||||
} catch (error) {
|
||||
console.error(`Invalid data JSON at ${args.data}: ${error.message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const valid = validate(data);
|
||||
if (!valid) {
|
||||
const message = ajv.errorsText(validate.errors, { separator: "; " });
|
||||
console.error(`Schema validation failed for ${args.data}: ${message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(`Schema validation passed: ${args.data}`);
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
// YAML parser for frontmatter parsing using vfile-matter
|
||||
import fs from "fs";
|
||||
import yaml from "js-yaml";
|
||||
import * as yaml from "js-yaml";
|
||||
import path from "path";
|
||||
import { VFile } from "vfile";
|
||||
import { matter } from "vfile-matter";
|
||||
|
||||
Generated
+120
-42
@@ -15,6 +15,8 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@microsoft/vally": "^0.6.0",
|
||||
"ajv": "^8.20.0",
|
||||
"ajv-formats": "^3.0.1",
|
||||
"all-contributors-cli": "^6.26.1"
|
||||
}
|
||||
},
|
||||
@@ -29,9 +31,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@github/copilot": {
|
||||
"version": "1.0.64-3",
|
||||
"resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.64-3.tgz",
|
||||
"integrity": "sha512-Q9nBMYEHX1bJLXzzJocQx2nZvORJ0E9gvK6ly/FCtmtA7ad96BWZvf4EHzkCNDsn56aI3zNaUSfKHUKcmIAzSg==",
|
||||
"version": "1.0.67",
|
||||
"resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.67.tgz",
|
||||
"integrity": "sha512-5YEY9LNXBT9Q8uShjCdYcornJJJhGtdIzSYla2+pjfXYpHsDVibqYubzYjfgffOUKFChyzOpH7n/868+t56iIg==",
|
||||
"dev": true,
|
||||
"license": "SEE LICENSE IN LICENSE.md",
|
||||
"dependencies": {
|
||||
@@ -41,20 +43,20 @@
|
||||
"copilot": "npm-loader.js"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@github/copilot-darwin-arm64": "1.0.64-3",
|
||||
"@github/copilot-darwin-x64": "1.0.64-3",
|
||||
"@github/copilot-linux-arm64": "1.0.64-3",
|
||||
"@github/copilot-linux-x64": "1.0.64-3",
|
||||
"@github/copilot-linuxmusl-arm64": "1.0.64-3",
|
||||
"@github/copilot-linuxmusl-x64": "1.0.64-3",
|
||||
"@github/copilot-win32-arm64": "1.0.64-3",
|
||||
"@github/copilot-win32-x64": "1.0.64-3"
|
||||
"@github/copilot-darwin-arm64": "1.0.67",
|
||||
"@github/copilot-darwin-x64": "1.0.67",
|
||||
"@github/copilot-linux-arm64": "1.0.67",
|
||||
"@github/copilot-linux-x64": "1.0.67",
|
||||
"@github/copilot-linuxmusl-arm64": "1.0.67",
|
||||
"@github/copilot-linuxmusl-x64": "1.0.67",
|
||||
"@github/copilot-win32-arm64": "1.0.67",
|
||||
"@github/copilot-win32-x64": "1.0.67"
|
||||
}
|
||||
},
|
||||
"node_modules/@github/copilot-darwin-arm64": {
|
||||
"version": "1.0.64-3",
|
||||
"resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.64-3.tgz",
|
||||
"integrity": "sha512-wlV6mRoAd/wG2V08TG+BOJ0nyOjroya24FSyA5A49z7PnUUuQXYRpa/GljvI5j3PM8aUl0DyBkXuB/DcFU818g==",
|
||||
"version": "1.0.67",
|
||||
"resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.67.tgz",
|
||||
"integrity": "sha512-CO3mpgFXcN6e7ZsSmjMkt1AKxMfb1+mjdn3yrf2DRnnWIURSK9kGvw+E+E1+YE37D1MBiUn/VOBmhRad5+vl0A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -69,9 +71,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@github/copilot-darwin-x64": {
|
||||
"version": "1.0.64-3",
|
||||
"resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.64-3.tgz",
|
||||
"integrity": "sha512-mk48PIESL2keeemX7tLRmWRDxKwl0q3cFI1ORD2QcrieNK7pSqI3eVbfoB7MqoUUI27yzIkl67xqgl8Qq28IUQ==",
|
||||
"version": "1.0.67",
|
||||
"resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.67.tgz",
|
||||
"integrity": "sha512-M20Hpn3bOJRkVwAIVRK4ZlX66AqtmGfXZRxZBRFQC045QIwcfmVUP45sTSgXDb4uHWeK0cZgdTdniHwKGtMplw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -86,9 +88,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@github/copilot-linux-arm64": {
|
||||
"version": "1.0.64-3",
|
||||
"resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.64-3.tgz",
|
||||
"integrity": "sha512-rCgtK3/rofQW5StSbeU0TwDUlOl2bvS2HGKyapVxow1Nvz3Q/TDB+eFRQc5ocBdv5tNSor+Caw2JGkRx5v508w==",
|
||||
"version": "1.0.67",
|
||||
"resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.67.tgz",
|
||||
"integrity": "sha512-b4ePtFBow+Ior+aVLKA1hHxhR5wF+ql5CD7TSg/NHGYgc1kwD+3a9uKSENy05J5Lit/G/DZ9C6JwowvvdMWSKg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -103,9 +105,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@github/copilot-linux-x64": {
|
||||
"version": "1.0.64-3",
|
||||
"resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.64-3.tgz",
|
||||
"integrity": "sha512-FAiBMw1h07mURSBLi3ztj5yzbP+uTbo9mhxOym1Xysut5LDpO2kYUzTYk2DlIyLGZhmH/HDOZE+b6U7lOUQy0g==",
|
||||
"version": "1.0.67",
|
||||
"resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.67.tgz",
|
||||
"integrity": "sha512-4ynZyfKnWAdvEPAFDDBIz1wpFttcOTJu4Y8Mlz5oXCBA0NM/rwr8K4l7Adp8UzwbfmdrMJ9y+zivqRBMDbPInA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -120,9 +122,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@github/copilot-linuxmusl-arm64": {
|
||||
"version": "1.0.64-3",
|
||||
"resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.64-3.tgz",
|
||||
"integrity": "sha512-vn8P6grPf0y2mNskkdVbAz0i46b1sP9uSXv7z6kgycjprl0CdIYPDf3WEkG60vpyopfQna+iCqCLMWRnNyCk3g==",
|
||||
"version": "1.0.67",
|
||||
"resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.67.tgz",
|
||||
"integrity": "sha512-IjezxBU8fYUr/b5hEiniXqzwoOrJ4egrQSBbG96M+roLTqd9txP0MgxZtcRtKV7phRIdIGE109wwrn4H6hSqmA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -137,9 +139,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@github/copilot-linuxmusl-x64": {
|
||||
"version": "1.0.64-3",
|
||||
"resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.64-3.tgz",
|
||||
"integrity": "sha512-atdHimNd6nzRRwHybXUY6/84bYzXeKbDOeYN/N/DsX23+AQOPSu5BD8MD8166I/5kNHui0XOmeTSydVNBUwcJw==",
|
||||
"version": "1.0.67",
|
||||
"resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.67.tgz",
|
||||
"integrity": "sha512-Zy/rbja1lnhzDoNfn051H0EybCseCvjvH7WmbcHCayjXUjzXeKF6OmAt4hvqFZH87ttT3KbKtQ8/6oDUhhM2YQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -154,13 +156,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@github/copilot-sdk": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@github/copilot-sdk/-/copilot-sdk-1.0.3.tgz",
|
||||
"integrity": "sha512-ujnH2QVw3+xvjgo9cbpY0wik4fNxAmdMDSFnxGScDSvRuK2vUCL2xWW4V2ANc9pWwRHPBpEpMuNJMtmydmLCIQ==",
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@github/copilot-sdk/-/copilot-sdk-1.0.4.tgz",
|
||||
"integrity": "sha512-c6/gpatP7LAuP9stlc2uXXOrB+TJMFSs3Jrzu9FG8lJJYFGmdzbBvGz2UdL1jww84fp6D7cohEZa4UGq1+iuyA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@github/copilot": "^1.0.64-1",
|
||||
"@github/copilot": "^1.0.65",
|
||||
"vscode-jsonrpc": "^8.2.1",
|
||||
"zod": "^4.3.6"
|
||||
},
|
||||
@@ -169,9 +171,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@github/copilot-win32-arm64": {
|
||||
"version": "1.0.64-3",
|
||||
"resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.64-3.tgz",
|
||||
"integrity": "sha512-jUTS9meoHEXQR8nMDOjwC0baqV273lYtLxY46W7TiOFszhsqhbhWxQMkNQBfT3GEfPp+40igzMPq3reaUTuvag==",
|
||||
"version": "1.0.67",
|
||||
"resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.67.tgz",
|
||||
"integrity": "sha512-O3VFRS5v9NXRP8o+N1SvcFbBqECDzZP7XQBeBj2Vcrma80gdJc5GQub/w2mwmr1w5UbwgzJkRasm0Ec/jxbcoA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -186,9 +188,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@github/copilot-win32-x64": {
|
||||
"version": "1.0.64-3",
|
||||
"resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.64-3.tgz",
|
||||
"integrity": "sha512-gHUhS500Q91hjtH9fqKDblaIs0mO09G4ifpZ1woDPXbkdKe/W29uwB7g2fn0+KczNRyPxFSWlqjnOon4Fe8svA==",
|
||||
"version": "1.0.67",
|
||||
"resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.67.tgz",
|
||||
"integrity": "sha512-td5tQ/nve5dB7RPvNglBZwa/6DJqiOBgacXXa1GpYcohqpCzoI8gONNkeaeyr6oF4iu5wXJ9krUNr6QXL4yB5Q==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -221,6 +223,41 @@
|
||||
"integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ajv": {
|
||||
"version": "8.20.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz",
|
||||
"integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fast-uri": "^3.0.1",
|
||||
"json-schema-traverse": "^1.0.0",
|
||||
"require-from-string": "^2.0.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/ajv-formats": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz",
|
||||
"integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ajv": "^8.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"ajv": "^8.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"ajv": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/all-contributors-cli": {
|
||||
"version": "6.26.1",
|
||||
"resolved": "https://registry.npmjs.org/all-contributors-cli/-/all-contributors-cli-6.26.1.tgz",
|
||||
@@ -473,6 +510,30 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/fast-deep-equal": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-uri": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.3.tgz",
|
||||
"integrity": "sha512-i70LwGWUduXqzicKXWshooq+sWL1K3WUU5rKZNG/0i3a1OSoX3HqhH5WbWwTmqWfor4urUakGPiRQcleRZTwOg==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/fastify"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/fastify"
|
||||
}
|
||||
],
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/figures": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
|
||||
@@ -618,6 +679,13 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/json-schema-traverse": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/locate-path": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
|
||||
@@ -632,9 +700,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/lodash": {
|
||||
"version": "4.18.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
|
||||
"integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
@@ -817,6 +885,16 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/require-from-string": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
|
||||
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/require-main-filename": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
|
||||
|
||||
@@ -38,6 +38,8 @@
|
||||
"author": "GitHub",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"ajv": "^8.20.0",
|
||||
"ajv-formats": "^3.0.1",
|
||||
"@microsoft/vally": "^0.6.0",
|
||||
"all-contributors-cli": "^6.26.1"
|
||||
},
|
||||
|
||||
@@ -499,9 +499,8 @@ import PageHeader from '../components/PageHeader.astro';
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td align="center" size="13px" colspan="7">
|
||||
<img src="https://raw.githubusercontent.com/all-contributors/all-contributors-cli/1b8533af435da9854653492b1327a23a4dbd0a10/assets/logo-small.svg">
|
||||
<a href="https://all-contributors.js.org/docs/en/bot/usage">Add your contributions</a>
|
||||
</img>
|
||||
<img src="https://raw.githubusercontent.com/all-contributors/all-contributors-cli/1b8533af435da9854653492b1327a23a4dbd0a10/assets/logo-small.svg" alt="" />
|
||||
<a href="https://all-contributors.js.org/docs/en/bot/usage">Add your contributions</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
|
||||
Reference in New Issue
Block a user