diff --git a/README.md b/README.md index 77cbea9..c6af39a 100644 --- a/README.md +++ b/README.md @@ -226,7 +226,7 @@ Reduce APort integration time from hours to minutes. | Tool | Description | Status | Maintainer | |------|-------------|--------|------------| | [APort CLI](tools/cli/) | `npx create-aport-integration` scaffolding tool | ✅ Active | Community | -| [VS Code Extension](tools/vscode-extension/) | Policy development with IntelliSense | 🚧 In Progress | Community | +| [VS Code Extension](tools/vscode-extension/) | Snippets, syntax highlighting, and IntelliSense for APort policy development | ✅ Active | Community | | [Postman Collection](tools/postman-collection/) | Complete API testing collection | ✅ Active | Community | ### 🌉 **Protocol Bridges & Standards** diff --git a/tools/vscode-extension/.vscodeignore b/tools/vscode-extension/.vscodeignore new file mode 100644 index 0000000..f25473e --- /dev/null +++ b/tools/vscode-extension/.vscodeignore @@ -0,0 +1,6 @@ +node_modules/** +src/** +tests/** +examples/** +*.log +tsconfig.json diff --git a/tools/vscode-extension/README.md b/tools/vscode-extension/README.md new file mode 100644 index 0000000..d5f01de --- /dev/null +++ b/tools/vscode-extension/README.md @@ -0,0 +1,63 @@ +# APort Developer Tools for VS Code + +VS Code extension template for APort policy and passport development. It adds snippets, syntax highlighting, and IntelliSense helpers for common APort integration files. + +## Features + +- APort policy language mode for `.aport-policy` files and `APortfile` +- APort-specific syntax highlighting injected into JSON policy and passport files +- Syntax highlighting for policy pack IDs, agent IDs, capabilities, and common APort keys +- JSON schema validation for policy and passport fixtures +- Completion items for APort policy keys, sample policy packs, and common capabilities +- JavaScript, TypeScript, JSON, JSONC, and YAML snippets for APort integrations +- `APort: Open Documentation` command from the command palette + +## Local development + +```bash +cd tools/vscode-extension +npm install +npm run compile +npm test +``` + +Launch the extension from VS Code with `Run Extension`, then open one of the files in `examples/` to verify completions and schema validation. + +## Snippets + +| Language | Prefix | Description | +| --- | --- | --- | +| JavaScript | `aport-verify` | Create an APort verification request | +| JavaScript | `aport-express` | Protect an Express route with APort middleware | +| TypeScript | `aport-next-middleware` | Add APort verification to Next.js middleware | +| JSON/JSONC/APort Policy | `aport-policy` | Create an APort policy fixture | +| JSON/JSONC/APort Policy | `aport-passport` | Create an APort passport fixture | +| YAML | `aport-action` | Add an APort verify step to GitHub Actions | + +## File patterns + +The extension contributes JSON schema validation for: + +- `*.aport.json` +- `*.aport-policy.json` +- `aport.policy.json` +- `*.aport-passport.json` +- `aport-passport.json` +- `aport.passport.json` + +## Example + +```json +{ + "policy_pack": "finance.payment.refund.v1", + "agent_id": "agt_inst_refund_bot_123", + "verification_level": "github", + "context": { + "amount": 50, + "currency": "USD" + }, + "limits": { + "refund_amount_max_per_tx": 100 + } +} +``` diff --git a/tools/vscode-extension/examples/nextjs-middleware.ts b/tools/vscode-extension/examples/nextjs-middleware.ts new file mode 100644 index 0000000..e5e342e --- /dev/null +++ b/tools/vscode-extension/examples/nextjs-middleware.ts @@ -0,0 +1,25 @@ +import { NextResponse, type NextRequest } from "next/server"; +import { createAPortMiddleware } from "@aporthq/nextjs-middleware"; + +const verifyAgent = createAPortMiddleware({ + policyPack: "finance.payment.refund.v1", + agentIdHeader: "x-agent-id", + apiKey: process.env.APORT_API_KEY +}); + +export async function middleware(request: NextRequest) { + const verification = await verifyAgent(request); + + if (!verification.allowed) { + return NextResponse.json( + { error: verification.reason ?? "APort verification failed" }, + { status: 403 } + ); + } + + return NextResponse.next(); +} + +export const config = { + matcher: ["/api/refunds/:path*"] +}; diff --git a/tools/vscode-extension/examples/passport.aport-passport.json b/tools/vscode-extension/examples/passport.aport-passport.json new file mode 100644 index 0000000..bfe03e5 --- /dev/null +++ b/tools/vscode-extension/examples/passport.aport-passport.json @@ -0,0 +1,18 @@ +{ + "agent_id": "agt_inst_refund_bot_123", + "name": "Refund Bot", + "description": "Handles customer refund requests with APort verification.", + "verification_level": "github", + "capabilities": [ + { + "name": "refund:create", + "description": "Create refund requests", + "limits": { + "refund_amount_max_per_tx": 100 + } + } + ], + "metadata": { + "framework": "APort VS Code Extension" + } +} diff --git a/tools/vscode-extension/examples/refund-policy.aport.json b/tools/vscode-extension/examples/refund-policy.aport.json new file mode 100644 index 0000000..ec5fc09 --- /dev/null +++ b/tools/vscode-extension/examples/refund-policy.aport.json @@ -0,0 +1,14 @@ +{ + "policy_pack": "finance.payment.refund.v1", + "agent_id": "agt_inst_refund_bot_123", + "verification_level": "github", + "context": { + "amount": 50, + "currency": "USD", + "order_id": "ord_123" + }, + "limits": { + "refund_amount_max_per_tx": 100, + "refunds_per_day": 20 + } +} diff --git a/tools/vscode-extension/language-configuration.json b/tools/vscode-extension/language-configuration.json new file mode 100644 index 0000000..53099db --- /dev/null +++ b/tools/vscode-extension/language-configuration.json @@ -0,0 +1,62 @@ +{ + "comments": { + "lineComment": "//", + "blockComment": [ + "/*", + "*/" + ] + }, + "brackets": [ + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ] + ], + "autoClosingPairs": [ + { + "open": "{", + "close": "}" + }, + { + "open": "[", + "close": "]" + }, + { + "open": "(", + "close": ")" + }, + { + "open": "\"", + "close": "\"", + "notIn": [ + "string" + ] + } + ], + "surroundingPairs": [ + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ], + [ + "\"", + "\"" + ] + ] +} diff --git a/tools/vscode-extension/out/extension.js b/tools/vscode-extension/out/extension.js new file mode 100644 index 0000000..18d2440 --- /dev/null +++ b/tools/vscode-extension/out/extension.js @@ -0,0 +1,159 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.activate = activate; +exports.deactivate = deactivate; +const vscode = __importStar(require("vscode")); +const APORT_DOCS_URL = "https://aport.io/docs"; +const POLICY_PACKS = [ + { + label: "finance.payment.refund.v1", + detail: "Refund processing policy pack", + documentation: "Use for refund approvals, per-transaction limits, and payment workflow checks." + }, + { + label: "data.export.v1", + detail: "Data export policy pack", + documentation: "Use before exporting rows, datasets, or customer records." + }, + { + label: "code.repository.merge.v1", + detail: "Repository merge policy pack", + documentation: "Use before allowing an agent to merge pull requests or update protected branches." + }, + { + label: "admin.access.v1", + detail: "Administrative access policy pack", + documentation: "Use for privileged admin actions and elevated assurance workflows." + } +]; +const APORT_KEYS = [ + ["agent_id", "Agent passport identifier, for example agt_inst_refund_bot_123"], + ["policy_pack", "Policy pack identifier used for verification"], + ["context", "Runtime facts passed into a verification request"], + ["capabilities", "Allowed actions for an agent passport"], + ["limits", "Numeric or scoped limits applied to an agent passport"], + ["verification_level", "Assurance level required for the policy or passport"], + ["expires_at", "ISO-8601 expiry timestamp"], + ["metadata", "Additional integration metadata"] +]; +const CAPABILITIES = [ + "refund:create", + "refund:approve", + "data:export", + "repository:merge", + "admin:access", + "message:send" +]; +function activate(context) { + context.subscriptions.push(vscode.commands.registerCommand("aport.openDocs", async () => { + await vscode.env.openExternal(vscode.Uri.parse(APORT_DOCS_URL)); + })); + const selector = [ + { language: "aport-policy" }, + { language: "json" }, + { language: "jsonc" }, + { language: "yaml" } + ]; + context.subscriptions.push(vscode.languages.registerCompletionItemProvider(selector, new APortCompletionProvider(), "\"", ":", ".")); +} +function deactivate() { + // VS Code disposes all registered subscriptions for this extension. +} +class APortCompletionProvider { + provideCompletionItems(document) { + if (!isLikelyAPortDocument(document)) { + return []; + } + return [ + ...APORT_KEYS.map(([key, detail]) => createKeyCompletion(key, detail)), + ...POLICY_PACKS.map(createPolicyPackCompletion), + ...CAPABILITIES.map(createCapabilityCompletion), + createVerifySnippetCompletion() + ]; + } +} +function isLikelyAPortDocument(document) { + if (document.languageId === "aport-policy") { + return true; + } + const fileName = document.fileName.toLowerCase(); + if (fileName.endsWith(".aport.json") || + fileName.endsWith(".aport-policy.json") || + fileName.endsWith(".aport-passport.json") || + fileName.endsWith("aport.policy.json") || + fileName.endsWith("aport.passport.json")) { + return true; + } + const sample = document.getText(new vscode.Range(0, 0, Math.min(document.lineCount, 80), 0)); + return /policy_pack|agent_id|aport|passport|capabilities/.test(sample.toLowerCase()); +} +function createKeyCompletion(label, detail) { + const item = new vscode.CompletionItem(label, vscode.CompletionItemKind.Property); + item.detail = detail; + item.insertText = new vscode.SnippetString(`"${label}": $1`); + item.documentation = new vscode.MarkdownString(detail); + return item; +} +function createPolicyPackCompletion(policy) { + const item = new vscode.CompletionItem(policy.label, vscode.CompletionItemKind.Value); + item.detail = policy.detail; + item.insertText = `"${policy.label}"`; + item.documentation = new vscode.MarkdownString(policy.documentation); + return item; +} +function createCapabilityCompletion(capability) { + const item = new vscode.CompletionItem(capability, vscode.CompletionItemKind.EnumMember); + item.detail = "APort passport capability"; + item.insertText = `"${capability}"`; + item.documentation = new vscode.MarkdownString(`Adds the \`${capability}\` capability to an APort passport or policy context.`); + return item; +} +function createVerifySnippetCompletion() { + const item = new vscode.CompletionItem("aport verification request", vscode.CompletionItemKind.Snippet); + item.detail = "Create a JSON verification request"; + item.insertText = new vscode.SnippetString([ + "{", + "\t\"agent_id\": \"${1:agt_inst_refund_bot_123}\",", + "\t\"policy_pack\": \"${2:finance.payment.refund.v1}\",", + "\t\"context\": {", + "\t\t\"amount\": ${3:50},", + "\t\t\"currency\": \"${4:USD}\"", + "\t}", + "}" + ].join("\n")); + return item; +} +//# sourceMappingURL=extension.js.map \ No newline at end of file diff --git a/tools/vscode-extension/out/extension.js.map b/tools/vscode-extension/out/extension.js.map new file mode 100644 index 0000000..6e14e9e --- /dev/null +++ b/tools/vscode-extension/out/extension.js.map @@ -0,0 +1 @@ +{"version":3,"file":"extension.js","sourceRoot":"","sources":["../src/extension.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/tools/vscode-extension/package-lock.json b/tools/vscode-extension/package-lock.json new file mode 100644 index 0000000..f7b4037 --- /dev/null +++ b/tools/vscode-extension/package-lock.json @@ -0,0 +1,59 @@ +{ + "name": "aport-vscode-extension", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "aport-vscode-extension", + "version": "0.1.0", + "license": "MIT", + "devDependencies": { + "@types/node": "^20.14.8", + "@types/vscode": "^1.90.0", + "typescript": "^5.5.2" + }, + "engines": { + "vscode": "^1.90.0" + } + }, + "node_modules/@types/node": { + "version": "20.19.40", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.40.tgz", + "integrity": "sha512-xxx6M2IpSTnnKcR0cMvIiohkiCx20/oRPtWGbenFygKCGl3zqUzdNjQ/1V4solq1LU+dgv0nQzeGOuqkqZGg0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/vscode": { + "version": "1.118.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.118.0.tgz", + "integrity": "sha512-Ah6eTlqDcwIMELEVwQMO++rJAFBRz/oLluLD/vWdYrH1KuI9kfpaM+7pg0OvvascgcJy+ghLCERAYouM4QbzGw==", + "dev": true, + "license": "MIT" + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + } + } +} diff --git a/tools/vscode-extension/package.json b/tools/vscode-extension/package.json new file mode 100644 index 0000000..0473969 --- /dev/null +++ b/tools/vscode-extension/package.json @@ -0,0 +1,125 @@ +{ + "name": "aport-vscode-extension", + "displayName": "APort Developer Tools", + "description": "Snippets, syntax highlighting, and IntelliSense helpers for APort integrations.", + "version": "0.1.0", + "publisher": "aport-community", + "license": "MIT", + "engines": { + "vscode": "^1.90.0" + }, + "categories": [ + "Snippets", + "Programming Languages", + "Other" + ], + "keywords": [ + "aport", + "agent-verification", + "passport", + "policy", + "intellisense" + ], + "activationEvents": [ + "onLanguage:aport-policy", + "onLanguage:json", + "onLanguage:jsonc", + "onLanguage:yaml", + "onCommand:aport.openDocs" + ], + "main": "./out/extension.js", + "scripts": { + "compile": "tsc -p ./", + "watch": "tsc -watch -p ./", + "test": "node tests/manifest.test.js", + "vscode:prepublish": "npm run compile" + }, + "contributes": { + "commands": [ + { + "command": "aport.openDocs", + "title": "APort: Open Documentation", + "category": "APort" + } + ], + "languages": [ + { + "id": "aport-policy", + "aliases": [ + "APort Policy", + "aport-policy" + ], + "extensions": [ + ".aport-policy" + ], + "filenames": [ + "APortfile" + ], + "configuration": "./language-configuration.json" + } + ], + "grammars": [ + { + "language": "aport-policy", + "scopeName": "source.aport-policy", + "path": "./syntaxes/aport-policy.tmLanguage.json" + }, + { + "scopeName": "aport.json.injection", + "path": "./syntaxes/aport-json-injection.tmLanguage.json", + "injectTo": [ + "source.json" + ] + } + ], + "jsonValidation": [ + { + "fileMatch": [ + "*.aport.json", + "*.aport-policy.json", + "aport.policy.json" + ], + "url": "./schemas/aport-policy.schema.json" + }, + { + "fileMatch": [ + "*.aport-passport.json", + "aport-passport.json", + "aport.passport.json" + ], + "url": "./schemas/aport-passport.schema.json" + } + ], + "snippets": [ + { + "language": "javascript", + "path": "./snippets/aport-javascript.code-snippets" + }, + { + "language": "typescript", + "path": "./snippets/aport-typescript.code-snippets" + }, + { + "language": "json", + "path": "./snippets/aport-policy.code-snippets" + }, + { + "language": "jsonc", + "path": "./snippets/aport-policy.code-snippets" + }, + { + "language": "aport-policy", + "path": "./snippets/aport-policy.code-snippets" + }, + { + "language": "yaml", + "path": "./snippets/aport-yaml.code-snippets" + } + ] + }, + "devDependencies": { + "@types/node": "^20.14.8", + "@types/vscode": "^1.90.0", + "typescript": "^5.5.2" + } +} diff --git a/tools/vscode-extension/schemas/aport-passport.schema.json b/tools/vscode-extension/schemas/aport-passport.schema.json new file mode 100644 index 0000000..35ce7ec --- /dev/null +++ b/tools/vscode-extension/schemas/aport-passport.schema.json @@ -0,0 +1,84 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://aport.io/schemas/aport-passport.schema.json", + "title": "APort Agent Passport", + "type": "object", + "additionalProperties": true, + "properties": { + "agent_id": { + "type": "string", + "description": "Agent passport identifier.", + "pattern": "^(agt|ap)_[A-Za-z0-9_\\-]+$" + }, + "name": { + "type": "string", + "description": "Human-readable agent name." + }, + "description": { + "type": "string", + "description": "Short explanation of what this agent is allowed to do." + }, + "capabilities": { + "type": "array", + "description": "Capability grants attached to this passport.", + "items": { + "type": "object", + "additionalProperties": true, + "properties": { + "name": { + "type": "string", + "examples": [ + "refund:create", + "data:export", + "repository:merge" + ] + }, + "description": { + "type": "string" + }, + "limits": { + "type": "object", + "additionalProperties": true + } + }, + "required": [ + "name" + ] + } + }, + "limits": { + "type": "object", + "description": "Global limits for this passport.", + "additionalProperties": { + "type": [ + "string", + "number", + "integer", + "boolean" + ] + } + }, + "verification_level": { + "type": "string", + "enum": [ + "email", + "github", + "domain", + "enterprise" + ] + }, + "expires_at": { + "type": "string", + "format": "date-time" + }, + "metadata": { + "type": "object", + "additionalProperties": true + } + }, + "required": [ + "agent_id", + "name", + "capabilities" + ] +} diff --git a/tools/vscode-extension/schemas/aport-policy.schema.json b/tools/vscode-extension/schemas/aport-policy.schema.json new file mode 100644 index 0000000..f5dbccb --- /dev/null +++ b/tools/vscode-extension/schemas/aport-policy.schema.json @@ -0,0 +1,71 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://aport.io/schemas/aport-policy.schema.json", + "title": "APort Policy Configuration", + "type": "object", + "additionalProperties": true, + "properties": { + "policy_pack": { + "type": "string", + "description": "APort policy pack identifier.", + "examples": [ + "finance.payment.refund.v1", + "data.export.v1", + "code.repository.merge.v1", + "admin.access.v1" + ] + }, + "agent_id": { + "type": "string", + "description": "Agent passport identifier.", + "pattern": "^(agt|ap)_[A-Za-z0-9_\\-]+$" + }, + "verification_level": { + "type": "string", + "description": "Required assurance level for this policy.", + "enum": [ + "email", + "github", + "domain", + "enterprise" + ] + }, + "context": { + "type": "object", + "description": "Runtime facts supplied to APort verify calls.", + "additionalProperties": true + }, + "limits": { + "type": "object", + "description": "Policy-specific limits.", + "additionalProperties": { + "type": [ + "string", + "number", + "integer", + "boolean" + ] + } + }, + "capabilities": { + "type": "array", + "description": "Capabilities required by this policy.", + "items": { + "type": "string", + "examples": [ + "refund:create", + "data:export", + "repository:merge" + ] + } + }, + "metadata": { + "type": "object", + "description": "Optional integration metadata.", + "additionalProperties": true + } + }, + "required": [ + "policy_pack" + ] +} diff --git a/tools/vscode-extension/snippets/aport-javascript.code-snippets b/tools/vscode-extension/snippets/aport-javascript.code-snippets new file mode 100644 index 0000000..e93d919 --- /dev/null +++ b/tools/vscode-extension/snippets/aport-javascript.code-snippets @@ -0,0 +1,58 @@ +{ + "APort verify request": { + "prefix": "aport-verify", + "description": "Verify an APort agent against a policy pack.", + "body": [ + "const { APortClient } = require(\"@aporthq/sdk-node\");", + "", + "const aport = new APortClient({", + " apiKey: process.env.APORT_API_KEY,", + "});", + "", + "const result = await aport.verify(", + " \"${1:finance.payment.refund.v1}\",", + " \"${2:agt_inst_refund_bot_123}\",", + " { ${3:amount}: ${4:50} }", + ");", + "", + "if (!result.allowed) {", + " throw new Error(result.reason || \"APort verification failed\");", + "}" + ] + }, + "APort Express middleware": { + "prefix": "aport-express", + "description": "Protect an Express route with APort middleware.", + "body": [ + "const { createAPortMiddleware } = require(\"@aporthq/middleware-express\");", + "", + "const aportMiddleware = createAPortMiddleware({", + " policyPack: \"${1:finance.payment.refund.v1}\",", + " agentId: \"${2:agt_inst_refund_bot_123}\",", + " apiKey: process.env.APORT_API_KEY,", + "});", + "", + "app.post(\"${3:/refunds}\", aportMiddleware, async (req, res) => {", + " res.json({ ok: true });", + "});" + ] + }, + "APort passport object": { + "prefix": "aport-passport", + "description": "Create an APort passport payload.", + "body": [ + "const passport = {", + " name: \"${1:Refund Bot}\",", + " description: \"${2:Handles customer refund requests}\",", + " capabilities: [", + " {", + " name: \"${3:refund:create}\",", + " limits: {", + " refund_amount_max_per_tx: ${4:100}", + " }", + " }", + " ],", + "};" + ] + } +} diff --git a/tools/vscode-extension/snippets/aport-policy.code-snippets b/tools/vscode-extension/snippets/aport-policy.code-snippets new file mode 100644 index 0000000..e404d9a --- /dev/null +++ b/tools/vscode-extension/snippets/aport-policy.code-snippets @@ -0,0 +1,53 @@ +{ + "APort policy file": { + "prefix": "aport-policy", + "description": "Create an APort policy verification file.", + "body": [ + "{", + " \"policy_pack\": \"${1:finance.payment.refund.v1}\",", + " \"agent_id\": \"${2:agt_inst_refund_bot_123}\",", + " \"verification_level\": \"${3:github}\",", + " \"context\": {", + " \"${4:amount}\": ${5:50},", + " \"currency\": \"${6:USD}\"", + " },", + " \"limits\": {", + " \"${7:refund_amount_max_per_tx}\": ${8:100}", + " }", + "}" + ] + }, + "APort passport file": { + "prefix": "aport-passport", + "description": "Create an APort passport fixture.", + "body": [ + "{", + " \"agent_id\": \"${1:agt_inst_refund_bot_123}\",", + " \"name\": \"${2:Refund Bot}\",", + " \"description\": \"${3:Handles customer refund requests}\",", + " \"verification_level\": \"${4:github}\",", + " \"capabilities\": [", + " {", + " \"name\": \"${5:refund:create}\",", + " \"limits\": {", + " \"refund_amount_max_per_tx\": ${6:100}", + " }", + " }", + " ]", + "}" + ] + }, + "APort capability": { + "prefix": "aport-capability", + "description": "Add an APort capability object.", + "body": [ + "{", + " \"name\": \"${1:refund:create}\",", + " \"description\": \"${2:Allows the agent to create refund requests}\",", + " \"limits\": {", + " \"${3:refund_amount_max_per_tx}\": ${4:100}", + " }", + "}" + ] + } +} diff --git a/tools/vscode-extension/snippets/aport-typescript.code-snippets b/tools/vscode-extension/snippets/aport-typescript.code-snippets new file mode 100644 index 0000000..4011ee2 --- /dev/null +++ b/tools/vscode-extension/snippets/aport-typescript.code-snippets @@ -0,0 +1,81 @@ +{ + "APort verify request": { + "prefix": "aport-verify", + "description": "Verify an APort agent against a policy pack.", + "body": [ + "import { APortClient } from \"@aporthq/sdk-node\";", + "", + "const aport = new APortClient({", + " apiKey: process.env.APORT_API_KEY,", + "});", + "", + "const result = await aport.verify(", + " \"${1:finance.payment.refund.v1}\",", + " \"${2:agt_inst_refund_bot_123}\",", + " { ${3:amount}: ${4:50} }", + ");", + "", + "if (!result.allowed) {", + " throw new Error(result.reason ?? \"APort verification failed\");", + "}" + ] + }, + "APort Next.js middleware": { + "prefix": "aport-next-middleware", + "description": "Protect Next.js routes with APort verification.", + "body": [ + "import { NextResponse, type NextRequest } from \"next/server\";", + "import { createAPortMiddleware } from \"@aporthq/nextjs-middleware\";", + "", + "const verifyAgent = createAPortMiddleware({", + " policyPack: \"${1:finance.payment.refund.v1}\",", + " agentIdHeader: \"${2:x-agent-id}\",", + " apiKey: process.env.APORT_API_KEY,", + "});", + "", + "export async function middleware(request: NextRequest) {", + " const verification = await verifyAgent(request);", + "", + " if (!verification.allowed) {", + " return NextResponse.json(", + " { error: verification.reason ?? \"APort verification failed\" },", + " { status: 403 }", + " );", + " }", + "", + " return NextResponse.next();", + "}", + "", + "export const config = {", + " matcher: [\"${3:/api/refunds/:path*}\"],", + "};" + ] + }, + "APort passport type": { + "prefix": "aport-passport-type", + "description": "Create a typed APort passport payload.", + "body": [ + "type APortPassport = {", + " name: string;", + " description: string;", + " capabilities: Array<{", + " name: string;", + " limits?: Record;", + " }>;", + "};", + "", + "const passport: APortPassport = {", + " name: \"${1:Refund Bot}\",", + " description: \"${2:Handles customer refund requests}\",", + " capabilities: [", + " {", + " name: \"${3:refund:create}\",", + " limits: {", + " refund_amount_max_per_tx: ${4:100},", + " },", + " },", + " ],", + "};" + ] + } +} diff --git a/tools/vscode-extension/snippets/aport-yaml.code-snippets b/tools/vscode-extension/snippets/aport-yaml.code-snippets new file mode 100644 index 0000000..5125e8b --- /dev/null +++ b/tools/vscode-extension/snippets/aport-yaml.code-snippets @@ -0,0 +1,28 @@ +{ + "APort GitHub Actions verify step": { + "prefix": "aport-action", + "description": "Add an APort verify step to a GitHub Actions workflow.", + "body": [ + "- name: APort Verify", + " uses: aporthq/policy-verify-action@v1", + " with:", + " agent-id: \\${{ secrets.${1:APORT_AGENT_ID} }}", + " policy-pack: \"${2:code.repository.merge.v1}\"", + " api-key: \\${{ secrets.${3:APORT_API_KEY} }}" + ] + }, + "APort policy YAML": { + "prefix": "aport-policy", + "description": "Create a YAML APort policy fixture.", + "body": [ + "policy_pack: ${1:finance.payment.refund.v1}", + "agent_id: ${2:agt_inst_refund_bot_123}", + "verification_level: ${3:github}", + "context:", + " ${4:amount}: ${5:50}", + " currency: ${6:USD}", + "limits:", + " ${7:refund_amount_max_per_tx}: ${8:100}" + ] + } +} diff --git a/tools/vscode-extension/src/extension.ts b/tools/vscode-extension/src/extension.ts new file mode 100644 index 0000000..a7e3137 --- /dev/null +++ b/tools/vscode-extension/src/extension.ts @@ -0,0 +1,150 @@ +import * as vscode from "vscode"; + +const APORT_DOCS_URL = "https://aport.io/docs"; + +const POLICY_PACKS = [ + { + label: "finance.payment.refund.v1", + detail: "Refund processing policy pack", + documentation: "Use for refund approvals, per-transaction limits, and payment workflow checks." + }, + { + label: "data.export.v1", + detail: "Data export policy pack", + documentation: "Use before exporting rows, datasets, or customer records." + }, + { + label: "code.repository.merge.v1", + detail: "Repository merge policy pack", + documentation: "Use before allowing an agent to merge pull requests or update protected branches." + }, + { + label: "admin.access.v1", + detail: "Administrative access policy pack", + documentation: "Use for privileged admin actions and elevated assurance workflows." + } +]; + +const APORT_KEYS = [ + ["agent_id", "Agent passport identifier, for example agt_inst_refund_bot_123"], + ["policy_pack", "Policy pack identifier used for verification"], + ["context", "Runtime facts passed into a verification request"], + ["capabilities", "Allowed actions for an agent passport"], + ["limits", "Numeric or scoped limits applied to an agent passport"], + ["verification_level", "Assurance level required for the policy or passport"], + ["expires_at", "ISO-8601 expiry timestamp"], + ["metadata", "Additional integration metadata"] +] as const; + +const CAPABILITIES = [ + "refund:create", + "refund:approve", + "data:export", + "repository:merge", + "admin:access", + "message:send" +]; + +export function activate(context: vscode.ExtensionContext): void { + context.subscriptions.push( + vscode.commands.registerCommand("aport.openDocs", async () => { + await vscode.env.openExternal(vscode.Uri.parse(APORT_DOCS_URL)); + }) + ); + + const selector: vscode.DocumentSelector = [ + { language: "aport-policy" }, + { language: "json" }, + { language: "jsonc" }, + { language: "yaml" } + ]; + + context.subscriptions.push( + vscode.languages.registerCompletionItemProvider( + selector, + new APortCompletionProvider(), + "\"", + ":", + "." + ) + ); +} + +export function deactivate(): void { + // VS Code disposes all registered subscriptions for this extension. +} + +class APortCompletionProvider implements vscode.CompletionItemProvider { + provideCompletionItems(document: vscode.TextDocument): vscode.CompletionItem[] { + if (!isLikelyAPortDocument(document)) { + return []; + } + + return [ + ...APORT_KEYS.map(([key, detail]) => createKeyCompletion(key, detail)), + ...POLICY_PACKS.map(createPolicyPackCompletion), + ...CAPABILITIES.map(createCapabilityCompletion), + createVerifySnippetCompletion() + ]; + } +} + +function isLikelyAPortDocument(document: vscode.TextDocument): boolean { + if (document.languageId === "aport-policy") { + return true; + } + + const fileName = document.fileName.toLowerCase(); + if ( + fileName.endsWith(".aport.json") || + fileName.endsWith(".aport-policy.json") || + fileName.endsWith(".aport-passport.json") || + fileName.endsWith("aport.policy.json") || + fileName.endsWith("aport.passport.json") + ) { + return true; + } + + const sample = document.getText(new vscode.Range(0, 0, Math.min(document.lineCount, 80), 0)); + return /policy_pack|agent_id|aport|passport|capabilities/.test(sample.toLowerCase()); +} + +function createKeyCompletion(label: string, detail: string): vscode.CompletionItem { + const item = new vscode.CompletionItem(label, vscode.CompletionItemKind.Property); + item.detail = detail; + item.insertText = new vscode.SnippetString(`"${label}": $1`); + item.documentation = new vscode.MarkdownString(detail); + return item; +} + +function createPolicyPackCompletion(policy: (typeof POLICY_PACKS)[number]): vscode.CompletionItem { + const item = new vscode.CompletionItem(policy.label, vscode.CompletionItemKind.Value); + item.detail = policy.detail; + item.insertText = `"${policy.label}"`; + item.documentation = new vscode.MarkdownString(policy.documentation); + return item; +} + +function createCapabilityCompletion(capability: string): vscode.CompletionItem { + const item = new vscode.CompletionItem(capability, vscode.CompletionItemKind.EnumMember); + item.detail = "APort passport capability"; + item.insertText = `"${capability}"`; + item.documentation = new vscode.MarkdownString(`Adds the \`${capability}\` capability to an APort passport or policy context.`); + return item; +} + +function createVerifySnippetCompletion(): vscode.CompletionItem { + const item = new vscode.CompletionItem("aport verification request", vscode.CompletionItemKind.Snippet); + item.detail = "Create a JSON verification request"; + item.insertText = new vscode.SnippetString([ + "{", + "\t\"agent_id\": \"${1:agt_inst_refund_bot_123}\",", + "\t\"policy_pack\": \"${2:finance.payment.refund.v1}\",", + "\t\"context\": {", + "\t\t\"amount\": ${3:50},", + "\t\t\"currency\": \"${4:USD}\"", + "\t}", + "}" + ].join("\n")); + return item; +} diff --git a/tools/vscode-extension/syntaxes/aport-json-injection.tmLanguage.json b/tools/vscode-extension/syntaxes/aport-json-injection.tmLanguage.json new file mode 100644 index 0000000..ee2cf0e --- /dev/null +++ b/tools/vscode-extension/syntaxes/aport-json-injection.tmLanguage.json @@ -0,0 +1,24 @@ +{ + "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", + "name": "APort JSON Injection", + "scopeName": "aport.json.injection", + "injectionSelector": "L:source.json", + "patterns": [ + { + "name": "support.type.property-name.aport", + "match": "\"(?:agent_id|policy_pack|context|capabilities|limits|verification_level|expires_at|metadata|rules|effect|condition|actions)\"(?=\\s*:)" + }, + { + "name": "entity.name.function.aport.policy-pack", + "match": "\"(?:finance\\.payment\\.refund|data\\.export|code\\.repository\\.merge|admin\\.access)\\.v\\d+\"" + }, + { + "name": "constant.other.aport.agent-id", + "match": "\"(?:agt|ap)_[A-Za-z0-9_-]+\"" + }, + { + "name": "support.constant.aport.capability", + "match": "\"(?:refund|data|repository|admin|message):[A-Za-z0-9_-]+\"" + } + ] +} diff --git a/tools/vscode-extension/syntaxes/aport-policy.tmLanguage.json b/tools/vscode-extension/syntaxes/aport-policy.tmLanguage.json new file mode 100644 index 0000000..37bac95 --- /dev/null +++ b/tools/vscode-extension/syntaxes/aport-policy.tmLanguage.json @@ -0,0 +1,93 @@ +{ + "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", + "name": "APort Policy", + "scopeName": "source.aport-policy", + "patterns": [ + { + "include": "#keys" + }, + { + "include": "#policyPacks" + }, + { + "include": "#agentIds" + }, + { + "include": "#capabilities" + }, + { + "include": "#strings" + }, + { + "include": "#numbers" + }, + { + "include": "#constants" + } + ], + "repository": { + "agentIds": { + "patterns": [ + { + "name": "constant.other.aport.agent-id", + "match": "\"(?:agt|ap)_[A-Za-z0-9_-]+\"" + } + ] + }, + "capabilities": { + "patterns": [ + { + "name": "support.constant.aport.capability", + "match": "\"(?:refund|data|repository|admin|message):[A-Za-z0-9_-]+\"" + } + ] + }, + "constants": { + "patterns": [ + { + "name": "constant.language.json", + "match": "\\b(?:true|false|null)\\b" + } + ] + }, + "keys": { + "patterns": [ + { + "name": "support.type.property-name.aport", + "match": "\"(?:agent_id|policy_pack|context|capabilities|limits|verification_level|expires_at|metadata|rules|effect|condition|actions)\"(?=\\s*:)" + } + ] + }, + "numbers": { + "patterns": [ + { + "name": "constant.numeric.json", + "match": "-?\\b\\d+(?:\\.\\d+)?\\b" + } + ] + }, + "policyPacks": { + "patterns": [ + { + "name": "entity.name.function.aport.policy-pack", + "match": "\"(?:finance\\.payment\\.refund|data\\.export|code\\.repository\\.merge|admin\\.access)\\.v\\d+\"" + } + ] + }, + "strings": { + "patterns": [ + { + "name": "string.quoted.double.json", + "begin": "\"", + "end": "\"", + "patterns": [ + { + "name": "constant.character.escape.json", + "match": "\\\\(?:[\"\\\\/bfnrt]|u[0-9A-Fa-f]{4})" + } + ] + } + ] + } + } +} diff --git a/tools/vscode-extension/tests/manifest.test.js b/tools/vscode-extension/tests/manifest.test.js new file mode 100644 index 0000000..38de802 --- /dev/null +++ b/tools/vscode-extension/tests/manifest.test.js @@ -0,0 +1,41 @@ +const assert = require("node:assert/strict"); +const fs = require("node:fs"); +const path = require("node:path"); + +const root = path.resolve(__dirname, ".."); + +function readJson(relativePath) { + return JSON.parse(fs.readFileSync(path.join(root, relativePath), "utf8")); +} + +const manifest = readJson("package.json"); + +assert.equal(manifest.main, "./out/extension.js"); +assert.ok(manifest.contributes.commands.some((command) => command.command === "aport.openDocs")); +assert.ok(manifest.contributes.languages.some((language) => language.id === "aport-policy")); +assert.ok(manifest.contributes.grammars.some((grammar) => grammar.language === "aport-policy")); +assert.ok(manifest.contributes.jsonValidation.length >= 2); +assert.ok(manifest.contributes.snippets.length >= 5); + +const policySchema = readJson("schemas/aport-policy.schema.json"); +const passportSchema = readJson("schemas/aport-passport.schema.json"); + +assert.equal(policySchema.required[0], "policy_pack"); +assert.deepEqual(passportSchema.required, ["agent_id", "name", "capabilities"]); + +for (const snippet of manifest.contributes.snippets) { + const snippetFile = path.join(root, snippet.path); + assert.ok(fs.existsSync(snippetFile), `${snippet.path} should exist`); + JSON.parse(fs.readFileSync(snippetFile, "utf8")); +} + +const examples = [ + "examples/refund-policy.aport.json", + "examples/passport.aport-passport.json" +]; + +for (const example of examples) { + JSON.parse(fs.readFileSync(path.join(root, example), "utf8")); +} + +console.log("VS Code extension manifest, schemas, snippets, and examples are valid."); diff --git a/tools/vscode-extension/tsconfig.json b/tools/vscode-extension/tsconfig.json new file mode 100644 index 0000000..a10dc34 --- /dev/null +++ b/tools/vscode-extension/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "ES2022", + "lib": [ + "ES2022" + ], + "outDir": "out", + "rootDir": "src", + "strict": true, + "sourceMap": true, + "esModuleInterop": true, + "skipLibCheck": true + }, + "include": [ + "src/**/*.ts" + ], + "exclude": [ + "node_modules", + "out" + ] +}