@astrasyncai/verification-gateway 2.0.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapter-interface/interface.d.mts +2 -2
- package/dist/adapter-interface/interface.d.ts +2 -2
- package/dist/adapters/express.d.mts +2 -2
- package/dist/adapters/express.d.ts +2 -2
- package/dist/adapters/express.js +118 -5
- package/dist/adapters/express.js.map +1 -1
- package/dist/adapters/express.mjs +118 -5
- package/dist/adapters/express.mjs.map +1 -1
- package/dist/adapters/nextjs.d.mts +2 -2
- package/dist/adapters/nextjs.d.ts +2 -2
- package/dist/adapters/nextjs.js +189 -9
- package/dist/adapters/nextjs.js.map +1 -1
- package/dist/adapters/nextjs.mjs +189 -9
- package/dist/adapters/nextjs.mjs.map +1 -1
- package/dist/adapters/sdk.d.mts +2 -2
- package/dist/adapters/sdk.d.ts +2 -2
- package/dist/adapters/sdk.js +1 -0
- package/dist/adapters/sdk.js.map +1 -1
- package/dist/adapters/sdk.mjs +1 -0
- package/dist/adapters/sdk.mjs.map +1 -1
- package/dist/agent/index.d.mts +2 -2
- package/dist/agent/index.d.ts +2 -2
- package/dist/browser/background.d.mts +2 -0
- package/dist/browser/background.d.ts +2 -0
- package/dist/browser/background.js +4090 -0
- package/dist/browser/background.js.map +1 -0
- package/dist/browser/background.mjs +4088 -0
- package/dist/browser/background.mjs.map +1 -0
- package/dist/browser/browser-adapter.d.mts +10 -6
- package/dist/browser/browser-adapter.d.ts +10 -6
- package/dist/browser/browser-adapter.js +16 -5
- package/dist/browser/browser-adapter.js.map +1 -1
- package/dist/browser/browser-adapter.mjs +14 -4
- package/dist/browser/browser-adapter.mjs.map +1 -1
- package/dist/cli/index.d.mts +2 -2
- package/dist/cli/index.d.ts +2 -2
- package/dist/cli/index.js +1 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +1 -1
- package/dist/cli/index.mjs.map +1 -1
- package/dist/cursor/cursor-adapter.d.mts +3 -4
- package/dist/cursor/cursor-adapter.d.ts +3 -4
- package/dist/cursor/cursor-adapter.js.map +1 -1
- package/dist/cursor/cursor-adapter.mjs.map +1 -1
- package/dist/cursor/extension.d.mts +27 -0
- package/dist/cursor/extension.d.ts +27 -0
- package/dist/cursor/extension.js +4057 -0
- package/dist/cursor/extension.js.map +1 -0
- package/dist/cursor/extension.mjs +4029 -0
- package/dist/cursor/extension.mjs.map +1 -0
- package/dist/{express-DIEyq1Tz.d.ts → express-Bcl-uBUE.d.ts} +1 -1
- package/dist/{express-Cp4eg77F.d.mts → express-CtwDIZyF.d.mts} +1 -1
- package/dist/gateway/gateway.d.mts +2 -2
- package/dist/gateway/gateway.d.ts +2 -2
- package/dist/gateway/gateway.js +17 -17
- package/dist/gateway/gateway.js.map +1 -1
- package/dist/gateway/gateway.mjs +11 -18
- package/dist/gateway/gateway.mjs.map +1 -1
- package/dist/git-trigger/git-hooks.d.mts +2 -2
- package/dist/git-trigger/git-hooks.d.ts +2 -2
- package/dist/git-trigger/git-hooks.js +1 -2
- package/dist/git-trigger/git-hooks.js.map +1 -1
- package/dist/git-trigger/git-hooks.mjs +1 -9
- package/dist/git-trigger/git-hooks.mjs.map +1 -1
- package/dist/{index-CoLebmwv.d.mts → index-B1ThcGZl.d.mts} +1 -1
- package/dist/{index-BhTbGU-o.d.mts → index-BY8yQ8N8.d.mts} +1 -1
- package/dist/{index-Bhfxq9xI.d.ts → index-CtYSYwn3.d.ts} +1 -1
- package/dist/{index-CNkmHmpi.d.ts → index-DnoXfdFd.d.ts} +1 -1
- package/dist/index.d.mts +7 -7
- package/dist/index.d.ts +7 -7
- package/dist/index.js +201 -14
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +201 -14
- package/dist/index.mjs.map +1 -1
- package/dist/local-evaluator/evaluator.d.mts +2 -2
- package/dist/local-evaluator/evaluator.d.ts +2 -2
- package/dist/local-evaluator/evaluator.js.map +1 -1
- package/dist/local-evaluator/evaluator.mjs.map +1 -1
- package/dist/{nextjs-_C_FcJY5.d.mts → nextjs-BQyMCSx_.d.mts} +1 -1
- package/dist/{nextjs-Cag7libc.d.ts → nextjs-CEldnIJ9.d.ts} +1 -1
- package/dist/{sdk-DAJahT3p.d.mts → sdk-BhvuJSrH.d.mts} +1 -1
- package/dist/{sdk-CMPDFUjo.d.ts → sdk-BlyVSC_S.d.ts} +1 -1
- package/dist/transport/index.d.mts +2 -2
- package/dist/transport/index.d.ts +2 -2
- package/dist/{types-Ce2mFJkO.d.ts → types-79qS7aON.d.ts} +2 -2
- package/dist/{types-Bf8pML07.d.mts → types-CxQwJKbd.d.mts} +17 -2
- package/dist/{types-Bf8pML07.d.ts → types-CxQwJKbd.d.ts} +17 -2
- package/dist/{types-BvpGdsv1.d.mts → types-jJnPXStc.d.mts} +2 -2
- package/dist/ui/index.d.mts +1 -1
- package/dist/ui/index.d.ts +1 -1
- package/package.json +3 -2
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { PlatformAdapter, AdapterConfig } from '../adapter-interface/interface.mjs';
|
|
2
|
-
import { P as PDLSSContext, V as VerificationDecision, A as AgentAction, I as InterceptResult } from '../types-
|
|
2
|
+
import { P as PDLSSContext, V as VerificationDecision, A as AgentAction, I as InterceptResult } from '../types-jJnPXStc.mjs';
|
|
3
3
|
import '../gateway/gateway.mjs';
|
|
4
|
-
import '../types-
|
|
4
|
+
import '../types-CxQwJKbd.mjs';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* @astrasyncai/adapter-cursor
|
|
@@ -56,8 +56,7 @@ interface TerminalLike {
|
|
|
56
56
|
processId: Thenable<number | undefined>;
|
|
57
57
|
sendText(text: string, addNewLine?: boolean): void;
|
|
58
58
|
}
|
|
59
|
-
|
|
60
|
-
}
|
|
59
|
+
type Thenable<T> = PromiseLike<T>;
|
|
61
60
|
interface OutputChannelLike {
|
|
62
61
|
appendLine(value: string): void;
|
|
63
62
|
show(): void;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { PlatformAdapter, AdapterConfig } from '../adapter-interface/interface.js';
|
|
2
|
-
import { P as PDLSSContext, V as VerificationDecision, A as AgentAction, I as InterceptResult } from '../types-
|
|
2
|
+
import { P as PDLSSContext, V as VerificationDecision, A as AgentAction, I as InterceptResult } from '../types-79qS7aON.js';
|
|
3
3
|
import '../gateway/gateway.js';
|
|
4
|
-
import '../types-
|
|
4
|
+
import '../types-CxQwJKbd.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* @astrasyncai/adapter-cursor
|
|
@@ -56,8 +56,7 @@ interface TerminalLike {
|
|
|
56
56
|
processId: Thenable<number | undefined>;
|
|
57
57
|
sendText(text: string, addNewLine?: boolean): void;
|
|
58
58
|
}
|
|
59
|
-
|
|
60
|
-
}
|
|
59
|
+
type Thenable<T> = PromiseLike<T>;
|
|
61
60
|
interface OutputChannelLike {
|
|
62
61
|
appendLine(value: string): void;
|
|
63
62
|
show(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/cursor/cursor-adapter.ts","../../src/adapter-interface/interface.ts","../../src/adapter-interface/purpose-mapping.ts"],"sourcesContent":["/**\n * @astrasyncai/adapter-cursor\n *\n * Layer 4 adapter for Cursor / VS Code AI agents.\n * Intercepts file operations, terminal commands, and network requests\n * made by the AI agent within the editor.\n *\n * The adapter does NOT depend on @types/vscode. Instead, the VS Code\n * extension passes the real `vscode` module via a minimal interface\n * at initialize() time. This keeps the library free of extension host deps.\n *\n * ~250 lines — thin, disposable, platform-specific.\n */\n\nimport type { PlatformAdapter, AdapterConfig } from '../adapter-interface/interface';\nimport type { PDLSSContext, VerificationDecision, AgentAction, InterceptResult } from '../gateway/types';\nimport type { AstraSyncGateway } from '../gateway/gateway';\nimport { ADAPTER_INTERFACE_VERSION } from '../adapter-interface/interface';\nimport { mapToolToPurpose, extractTarget, extractNetworkDomains } from '../adapter-interface/purpose-mapping';\n\n// -----------------------------------------------------------------------\n// Minimal VS Code type stubs (injected at runtime by the extension)\n// -----------------------------------------------------------------------\n\nexport interface Disposable {\n dispose(): void;\n}\n\nexport interface VSCodeAPI {\n workspace: {\n onDidCreateFiles: (listener: (e: FileEvent) => void) => Disposable;\n onDidDeleteFiles: (listener: (e: FileEvent) => void) => Disposable;\n onDidSaveTextDocument: (listener: (doc: TextDocumentLike) => void) => Disposable;\n };\n window: {\n onDidOpenTerminal: (listener: (terminal: TerminalLike) => void) => Disposable;\n showWarningMessage(message: string, ...items: string[]): Thenable<string | undefined>;\n showInformationMessage(message: string, ...items: string[]): Thenable<string | undefined>;\n showErrorMessage(message: string, ...items: string[]): Thenable<string | undefined>;\n createOutputChannel(name: string): OutputChannelLike;\n };\n commands: {\n registerCommand(command: string, callback: (...args: unknown[]) => unknown): Disposable;\n };\n}\n\ninterface FileEvent {\n files: ReadonlyArray<{ fsPath: string; path: string }>;\n}\n\ninterface TextDocumentLike {\n uri: { fsPath: string; path: string };\n languageId: string;\n fileName: string;\n}\n\ninterface TerminalLike {\n name: string;\n processId: Thenable<number | undefined>;\n sendText(text: string, addNewLine?: boolean): void;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-empty-interface\ninterface Thenable<T> extends PromiseLike<T> {}\n\ninterface OutputChannelLike {\n appendLine(value: string): void;\n show(): void;\n dispose(): void;\n}\n\n// -----------------------------------------------------------------------\n// Configuration\n// -----------------------------------------------------------------------\n\nexport interface CursorAdapterOptions {\n /** The VS Code API object (passed by the extension at runtime) */\n vscode: VSCodeAPI;\n /** Callback for MANUAL_REVIEW decisions */\n onApprovalRequired?: (context: PDLSSContext, decision: VerificationDecision) => Promise<boolean>;\n}\n\n// -----------------------------------------------------------------------\n// Adapter implementation\n// -----------------------------------------------------------------------\n\nexport class CursorAdapter implements PlatformAdapter {\n readonly interfaceVersion = ADAPTER_INTERFACE_VERSION;\n\n private gateway!: AstraSyncGateway;\n private vscode!: VSCodeAPI;\n private disposables: Disposable[] = [];\n private outputChannel: OutputChannelLike | null = null;\n private onApprovalRequired: (context: PDLSSContext, decision: VerificationDecision) => Promise<boolean>;\n private _isRunning = false;\n\n constructor(options?: Partial<CursorAdapterOptions>) {\n this.onApprovalRequired = options?.onApprovalRequired ?? (async () => false);\n if (options?.vscode) {\n this.vscode = options.vscode;\n }\n }\n\n get isRunning(): boolean {\n return this._isRunning;\n }\n\n async initialize(config: AdapterConfig): Promise<void> {\n this.gateway = config.gateway as AstraSyncGateway;\n\n if (config.adapterOptions.vscode) {\n this.vscode = config.adapterOptions.vscode as VSCodeAPI;\n }\n\n if (!this.vscode) {\n throw new Error('CursorAdapter requires vscode API — pass it via adapterOptions.vscode');\n }\n\n // Output channel for logging decisions\n this.outputChannel = this.vscode.window.createOutputChannel('AstraSync Local Guard');\n\n // Register file watchers\n this.disposables.push(\n this.vscode.workspace.onDidCreateFiles((e) => {\n for (const file of e.files) {\n this.evaluateFileAction('file_write', file.fsPath);\n }\n }),\n );\n\n this.disposables.push(\n this.vscode.workspace.onDidDeleteFiles((e) => {\n for (const file of e.files) {\n this.evaluateFileAction('file_delete', file.fsPath);\n }\n }),\n );\n\n this.disposables.push(\n this.vscode.workspace.onDidSaveTextDocument((doc) => {\n this.evaluateFileAction('file_write', doc.fileName);\n }),\n );\n\n // Register status command\n this.disposables.push(\n this.vscode.commands.registerCommand('astrasync.status', () => {\n this.outputChannel?.show();\n this.vscode.window.showInformationMessage('AstraSync Local Guard is active');\n }),\n );\n\n this._isRunning = true;\n this.log('AstraSync Local Guard for Cursor — initialized');\n }\n\n async shutdown(): Promise<void> {\n for (const d of this.disposables) {\n d.dispose();\n }\n this.disposables = [];\n this.outputChannel?.dispose();\n this.outputChannel = null;\n this._isRunning = false;\n }\n\n async interceptAction(action: AgentAction): Promise<InterceptResult> {\n const raw = action.raw as { type: string; path?: string; command?: string };\n\n if (!raw.type) {\n return { intercepted: false, skipReason: 'No action type' };\n }\n\n const context = this.extractContext(action);\n return { intercepted: true, context };\n }\n\n extractContext(action: AgentAction): PDLSSContext {\n const raw = action.raw as { type: string; path?: string; command?: string; url?: string };\n\n let toolName: string;\n const args: Record<string, unknown> = {};\n\n switch (raw.type) {\n case 'file.create':\n case 'file.save':\n toolName = 'file_write';\n args.path = raw.path || '';\n break;\n case 'file.delete':\n toolName = 'file_delete';\n args.path = raw.path || '';\n break;\n case 'file.read':\n toolName = 'file_read';\n args.path = raw.path || '';\n break;\n case 'terminal.exec':\n toolName = 'shell_exec';\n args.command = raw.command || '';\n break;\n case 'network.request':\n toolName = 'http_request';\n args.url = raw.url || '';\n break;\n default:\n toolName = raw.type;\n }\n\n const purpose = mapToolToPurpose(toolName);\n const target = extractTarget(toolName, args);\n const networkAccess = extractNetworkDomains(target);\n\n return {\n purpose,\n action: toolName,\n target,\n ...(networkAccess && { networkAccess }),\n };\n }\n\n async enforceDecision(decision: VerificationDecision): Promise<void> {\n switch (decision.recommendation) {\n case 'DENY':\n this.log(`BLOCKED: ${decision.reason}`);\n this.vscode.window.showErrorMessage(\n `AstraSync blocked action: ${decision.reason}`,\n );\n break;\n case 'MANUAL_REVIEW':\n this.log(`REVIEW: ${decision.reason}`);\n break;\n case 'ALLOW':\n // No action needed\n break;\n }\n }\n\n // =====================================================================\n // Internal helpers\n // =====================================================================\n\n private async evaluateFileAction(toolName: string, filePath: string): Promise<void> {\n const action: AgentAction = {\n raw: {\n type: toolName === 'file_delete' ? 'file.delete' : 'file.save',\n path: filePath,\n },\n platform: 'cursor',\n timestamp: new Date(),\n };\n\n const interception = await this.interceptAction(action);\n if (!interception.intercepted || !interception.context) return;\n\n const decision = await this.gateway.evaluate(interception.context);\n\n if (decision.recommendation === 'DENY') {\n await this.enforceDecision(decision);\n } else if (decision.recommendation === 'MANUAL_REVIEW') {\n const approved = await this.handleApproval(interception.context, decision);\n if (!approved) {\n await this.enforceDecision({ ...decision, recommendation: 'DENY' });\n }\n }\n\n this.log(`${decision.recommendation}: ${interception.context.purpose} -> ${interception.context.target}`);\n }\n\n private async handleApproval(context: PDLSSContext, decision: VerificationDecision): Promise<boolean> {\n if (this.onApprovalRequired) {\n return this.onApprovalRequired(context, decision);\n }\n\n // Default: VS Code prompt\n const choice = await this.vscode.window.showWarningMessage(\n `AstraSync: ${decision.reason}. Allow?`,\n 'Allow',\n 'Deny',\n );\n return choice === 'Allow';\n }\n\n private log(message: string): void {\n this.outputChannel?.appendLine(`[${new Date().toISOString()}] ${message}`);\n }\n}\n","/**\n * PlatformAdapter Interface\n *\n * The contract that Layer 4 platform adapters implement.\n * Agent-side interception: governs what agents are allowed to do before they do it.\n *\n * Each adapter is 200-500 lines of platform-specific code.\n * All verification logic lives in the gateway — adapters just translate\n * between the platform's world and AstraSync's world.\n */\n\nimport type { AstraSyncGateway } from '../gateway/gateway';\nimport type { PDLSSContext, VerificationDecision, AgentAction, InterceptResult } from '../gateway/types';\n\nexport interface AdapterConfig {\n /** The AstraSyncGateway instance (handles mode routing) */\n gateway: AstraSyncGateway;\n /** Platform-specific configuration */\n adapterOptions: Record<string, unknown>;\n}\n\nexport interface PlatformAdapter {\n /**\n * Interface version for compatibility checking.\n * Current version: 1.\n */\n readonly interfaceVersion: number;\n\n /**\n * Platform-specific initialization.\n * Load config, register hooks, establish connections.\n */\n initialize(config: AdapterConfig): Promise<void>;\n\n /**\n * Graceful shutdown.\n * Drain in-flight verifications, deregister hooks, close connections.\n */\n shutdown(): Promise<void>;\n\n /**\n * Intercept an agent action before execution.\n *\n * How this works depends on the platform:\n * - CLI adapter: proxy server captures outbound request\n * - Browser adapter: content script intercepts DOM interaction\n * - Express: middleware captures inbound request\n */\n interceptAction(action: AgentAction): Promise<InterceptResult>;\n\n /**\n * Extract PDLSS-compatible context from a platform-specific action.\n * Maps platform-native action format to the universal PDLSSContext.\n */\n extractContext(action: AgentAction): PDLSSContext;\n\n /**\n * Enforce the verification decision in a platform-specific way.\n *\n * How this works depends on the platform:\n * - CLI: block command / allow command / prompt for approval\n * - Browser: block navigation / show confirmation dialog\n * - Express: return 403 / pass through / inject headers\n */\n enforceDecision(decision: VerificationDecision): Promise<void>;\n}\n\n/**\n * Current adapter interface version.\n */\nexport const ADAPTER_INTERFACE_VERSION = 1;\n\n/**\n * Check if an adapter is compatible with the current interface version.\n */\nexport function isCompatibleAdapter(adapter: PlatformAdapter): boolean {\n return adapter.interfaceVersion === ADAPTER_INTERFACE_VERSION;\n}\n","/**\n * Shared purpose mapping utilities for Layer 4 platform adapters.\n *\n * Maps platform-native action names to PDLSS purpose categories.\n * Used by OpenClaw CLI, Cursor, browser, and future adapters.\n */\n\n// -----------------------------------------------------------------------\n// Tool → Purpose mapping\n// -----------------------------------------------------------------------\n\n/** Standard tool name → PDLSS purpose mapping used by all adapters */\nconst TOOL_PURPOSE_MAP: Record<string, string> = {\n // Shell\n shell_exec: 'shell.exec',\n run_command: 'shell.exec',\n execute: 'shell.exec',\n terminal_exec: 'shell.exec',\n run_terminal_command: 'shell.exec',\n\n // File read\n file_read: 'file.read',\n read_file: 'file.read',\n\n // File write\n file_write: 'file.write',\n write_file: 'file.write',\n create_file: 'file.write',\n edit_file: 'file.write',\n\n // File delete\n file_delete: 'file.delete',\n delete_file: 'file.delete',\n\n // Network\n http_request: 'network.request',\n fetch: 'network.request',\n web_request: 'network.request',\n\n // Email\n send_email: 'email.send',\n read_email: 'email.read',\n\n // Calendar\n create_event: 'calendar.create',\n\n // Database\n query_database: 'database.query',\n write_database: 'database.write',\n\n // Payment\n payment_execute: 'payment.execute',\n};\n\n/**\n * Map a tool/action name to a PDLSS purpose category.\n * Returns `tool.<name>` for unmapped tools (denied by default).\n */\nexport function mapToolToPurpose(toolName: string): string {\n return TOOL_PURPOSE_MAP[toolName] || `tool.${toolName}`;\n}\n\n/**\n * Register additional tool → purpose mappings (e.g. from a platform adapter).\n * Does not overwrite existing mappings.\n */\nexport function registerToolMappings(mappings: Record<string, string>): void {\n for (const [tool, purpose] of Object.entries(mappings)) {\n if (!(tool in TOOL_PURPOSE_MAP)) {\n TOOL_PURPOSE_MAP[tool] = purpose;\n }\n }\n}\n\n// -----------------------------------------------------------------------\n// Target extraction\n// -----------------------------------------------------------------------\n\n/**\n * Extract the meaningful target string from tool arguments.\n * Uses the purpose category to determine which argument field is relevant.\n */\nexport function extractTarget(toolName: string, args: Record<string, unknown>): string {\n const purpose = mapToolToPurpose(toolName);\n\n if (purpose.startsWith('shell.')) {\n return String(args.command || args.cmd || args.script || '');\n }\n\n if (purpose.startsWith('file.')) {\n return String(args.path || args.file || args.filename || args.file_path || '');\n }\n\n if (purpose.startsWith('network.')) {\n return String(args.url || args.endpoint || args.uri || '');\n }\n\n if (purpose.startsWith('email.')) {\n return String(args.to || args.recipient || args.address || '');\n }\n\n if (purpose.startsWith('database.')) {\n return String(args.query || args.table || '');\n }\n\n if (purpose.startsWith('payment.')) {\n return String(args.description || args.merchant || args.amount || '');\n }\n\n // Fallback: try common field names\n if (args.command) return String(args.command);\n if (args.path) return String(args.path);\n if (args.url) return String(args.url);\n\n // Default: use first non-empty string argument or tool name\n for (const val of Object.values(args)) {\n if (typeof val === 'string' && val.length > 0) return val;\n }\n return toolName;\n}\n\n// -----------------------------------------------------------------------\n// Network domain extraction\n// -----------------------------------------------------------------------\n\n/**\n * Extract network domains from a URL target.\n * Returns undefined if the target is not a URL.\n */\nexport function extractNetworkDomains(target: string): string[] | undefined {\n try {\n if (target.startsWith('http://') || target.startsWith('https://')) {\n const url = new URL(target);\n return [url.hostname];\n }\n } catch {\n // Not a URL\n }\n return undefined;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/cursor/cursor-adapter.ts","../../src/adapter-interface/interface.ts","../../src/adapter-interface/purpose-mapping.ts"],"sourcesContent":["/**\n * @astrasyncai/adapter-cursor\n *\n * Layer 4 adapter for Cursor / VS Code AI agents.\n * Intercepts file operations, terminal commands, and network requests\n * made by the AI agent within the editor.\n *\n * The adapter does NOT depend on @types/vscode. Instead, the VS Code\n * extension passes the real `vscode` module via a minimal interface\n * at initialize() time. This keeps the library free of extension host deps.\n *\n * ~250 lines — thin, disposable, platform-specific.\n */\n\nimport type { PlatformAdapter, AdapterConfig } from '../adapter-interface/interface';\nimport type { PDLSSContext, VerificationDecision, AgentAction, InterceptResult } from '../gateway/types';\nimport type { AstraSyncGateway } from '../gateway/gateway';\nimport { ADAPTER_INTERFACE_VERSION } from '../adapter-interface/interface';\nimport { mapToolToPurpose, extractTarget, extractNetworkDomains } from '../adapter-interface/purpose-mapping';\n\n// -----------------------------------------------------------------------\n// Minimal VS Code type stubs (injected at runtime by the extension)\n// -----------------------------------------------------------------------\n\nexport interface Disposable {\n dispose(): void;\n}\n\nexport interface VSCodeAPI {\n workspace: {\n onDidCreateFiles: (listener: (e: FileEvent) => void) => Disposable;\n onDidDeleteFiles: (listener: (e: FileEvent) => void) => Disposable;\n onDidSaveTextDocument: (listener: (doc: TextDocumentLike) => void) => Disposable;\n };\n window: {\n onDidOpenTerminal: (listener: (terminal: TerminalLike) => void) => Disposable;\n showWarningMessage(message: string, ...items: string[]): Thenable<string | undefined>;\n showInformationMessage(message: string, ...items: string[]): Thenable<string | undefined>;\n showErrorMessage(message: string, ...items: string[]): Thenable<string | undefined>;\n createOutputChannel(name: string): OutputChannelLike;\n };\n commands: {\n registerCommand(command: string, callback: (...args: unknown[]) => unknown): Disposable;\n };\n}\n\ninterface FileEvent {\n files: ReadonlyArray<{ fsPath: string; path: string }>;\n}\n\ninterface TextDocumentLike {\n uri: { fsPath: string; path: string };\n languageId: string;\n fileName: string;\n}\n\ninterface TerminalLike {\n name: string;\n processId: Thenable<number | undefined>;\n sendText(text: string, addNewLine?: boolean): void;\n}\n\ntype Thenable<T> = PromiseLike<T>;\n\ninterface OutputChannelLike {\n appendLine(value: string): void;\n show(): void;\n dispose(): void;\n}\n\n// -----------------------------------------------------------------------\n// Configuration\n// -----------------------------------------------------------------------\n\nexport interface CursorAdapterOptions {\n /** The VS Code API object (passed by the extension at runtime) */\n vscode: VSCodeAPI;\n /** Callback for MANUAL_REVIEW decisions */\n onApprovalRequired?: (context: PDLSSContext, decision: VerificationDecision) => Promise<boolean>;\n}\n\n// -----------------------------------------------------------------------\n// Adapter implementation\n// -----------------------------------------------------------------------\n\nexport class CursorAdapter implements PlatformAdapter {\n readonly interfaceVersion = ADAPTER_INTERFACE_VERSION;\n\n private gateway!: AstraSyncGateway;\n private vscode!: VSCodeAPI;\n private disposables: Disposable[] = [];\n private outputChannel: OutputChannelLike | null = null;\n private onApprovalRequired: (context: PDLSSContext, decision: VerificationDecision) => Promise<boolean>;\n private _isRunning = false;\n\n constructor(options?: Partial<CursorAdapterOptions>) {\n this.onApprovalRequired = options?.onApprovalRequired ?? (async () => false);\n if (options?.vscode) {\n this.vscode = options.vscode;\n }\n }\n\n get isRunning(): boolean {\n return this._isRunning;\n }\n\n async initialize(config: AdapterConfig): Promise<void> {\n this.gateway = config.gateway as AstraSyncGateway;\n\n if (config.adapterOptions.vscode) {\n this.vscode = config.adapterOptions.vscode as VSCodeAPI;\n }\n\n if (!this.vscode) {\n throw new Error('CursorAdapter requires vscode API — pass it via adapterOptions.vscode');\n }\n\n // Output channel for logging decisions\n this.outputChannel = this.vscode.window.createOutputChannel('AstraSync Local Guard');\n\n // Register file watchers\n this.disposables.push(\n this.vscode.workspace.onDidCreateFiles((e) => {\n for (const file of e.files) {\n this.evaluateFileAction('file_write', file.fsPath);\n }\n }),\n );\n\n this.disposables.push(\n this.vscode.workspace.onDidDeleteFiles((e) => {\n for (const file of e.files) {\n this.evaluateFileAction('file_delete', file.fsPath);\n }\n }),\n );\n\n this.disposables.push(\n this.vscode.workspace.onDidSaveTextDocument((doc) => {\n this.evaluateFileAction('file_write', doc.fileName);\n }),\n );\n\n // Register status command\n this.disposables.push(\n this.vscode.commands.registerCommand('astrasync.status', () => {\n this.outputChannel?.show();\n this.vscode.window.showInformationMessage('AstraSync Local Guard is active');\n }),\n );\n\n this._isRunning = true;\n this.log('AstraSync Local Guard for Cursor — initialized');\n }\n\n async shutdown(): Promise<void> {\n for (const d of this.disposables) {\n d.dispose();\n }\n this.disposables = [];\n this.outputChannel?.dispose();\n this.outputChannel = null;\n this._isRunning = false;\n }\n\n async interceptAction(action: AgentAction): Promise<InterceptResult> {\n const raw = action.raw as { type: string; path?: string; command?: string };\n\n if (!raw.type) {\n return { intercepted: false, skipReason: 'No action type' };\n }\n\n const context = this.extractContext(action);\n return { intercepted: true, context };\n }\n\n extractContext(action: AgentAction): PDLSSContext {\n const raw = action.raw as { type: string; path?: string; command?: string; url?: string };\n\n let toolName: string;\n const args: Record<string, unknown> = {};\n\n switch (raw.type) {\n case 'file.create':\n case 'file.save':\n toolName = 'file_write';\n args.path = raw.path || '';\n break;\n case 'file.delete':\n toolName = 'file_delete';\n args.path = raw.path || '';\n break;\n case 'file.read':\n toolName = 'file_read';\n args.path = raw.path || '';\n break;\n case 'terminal.exec':\n toolName = 'shell_exec';\n args.command = raw.command || '';\n break;\n case 'network.request':\n toolName = 'http_request';\n args.url = raw.url || '';\n break;\n default:\n toolName = raw.type;\n }\n\n const purpose = mapToolToPurpose(toolName);\n const target = extractTarget(toolName, args);\n const networkAccess = extractNetworkDomains(target);\n\n return {\n purpose,\n action: toolName,\n target,\n ...(networkAccess && { networkAccess }),\n };\n }\n\n async enforceDecision(decision: VerificationDecision): Promise<void> {\n switch (decision.recommendation) {\n case 'DENY':\n this.log(`BLOCKED: ${decision.reason}`);\n this.vscode.window.showErrorMessage(\n `AstraSync blocked action: ${decision.reason}`,\n );\n break;\n case 'MANUAL_REVIEW':\n this.log(`REVIEW: ${decision.reason}`);\n break;\n case 'ALLOW':\n // No action needed\n break;\n }\n }\n\n // =====================================================================\n // Internal helpers\n // =====================================================================\n\n private async evaluateFileAction(toolName: string, filePath: string): Promise<void> {\n const action: AgentAction = {\n raw: {\n type: toolName === 'file_delete' ? 'file.delete' : 'file.save',\n path: filePath,\n },\n platform: 'cursor',\n timestamp: new Date(),\n };\n\n const interception = await this.interceptAction(action);\n if (!interception.intercepted || !interception.context) return;\n\n const decision = await this.gateway.evaluate(interception.context);\n\n if (decision.recommendation === 'DENY') {\n await this.enforceDecision(decision);\n } else if (decision.recommendation === 'MANUAL_REVIEW') {\n const approved = await this.handleApproval(interception.context, decision);\n if (!approved) {\n await this.enforceDecision({ ...decision, recommendation: 'DENY' });\n }\n }\n\n this.log(`${decision.recommendation}: ${interception.context.purpose} -> ${interception.context.target}`);\n }\n\n private async handleApproval(context: PDLSSContext, decision: VerificationDecision): Promise<boolean> {\n if (this.onApprovalRequired) {\n return this.onApprovalRequired(context, decision);\n }\n\n // Default: VS Code prompt\n const choice = await this.vscode.window.showWarningMessage(\n `AstraSync: ${decision.reason}. Allow?`,\n 'Allow',\n 'Deny',\n );\n return choice === 'Allow';\n }\n\n private log(message: string): void {\n this.outputChannel?.appendLine(`[${new Date().toISOString()}] ${message}`);\n }\n}\n","/**\n * PlatformAdapter Interface\n *\n * The contract that Layer 4 platform adapters implement.\n * Agent-side interception: governs what agents are allowed to do before they do it.\n *\n * Each adapter is 200-500 lines of platform-specific code.\n * All verification logic lives in the gateway — adapters just translate\n * between the platform's world and AstraSync's world.\n */\n\nimport type { AstraSyncGateway } from '../gateway/gateway';\nimport type { PDLSSContext, VerificationDecision, AgentAction, InterceptResult } from '../gateway/types';\n\nexport interface AdapterConfig {\n /** The AstraSyncGateway instance (handles mode routing) */\n gateway: AstraSyncGateway;\n /** Platform-specific configuration */\n adapterOptions: Record<string, unknown>;\n}\n\nexport interface PlatformAdapter {\n /**\n * Interface version for compatibility checking.\n * Current version: 1.\n */\n readonly interfaceVersion: number;\n\n /**\n * Platform-specific initialization.\n * Load config, register hooks, establish connections.\n */\n initialize(config: AdapterConfig): Promise<void>;\n\n /**\n * Graceful shutdown.\n * Drain in-flight verifications, deregister hooks, close connections.\n */\n shutdown(): Promise<void>;\n\n /**\n * Intercept an agent action before execution.\n *\n * How this works depends on the platform:\n * - CLI adapter: proxy server captures outbound request\n * - Browser adapter: content script intercepts DOM interaction\n * - Express: middleware captures inbound request\n */\n interceptAction(action: AgentAction): Promise<InterceptResult>;\n\n /**\n * Extract PDLSS-compatible context from a platform-specific action.\n * Maps platform-native action format to the universal PDLSSContext.\n */\n extractContext(action: AgentAction): PDLSSContext;\n\n /**\n * Enforce the verification decision in a platform-specific way.\n *\n * How this works depends on the platform:\n * - CLI: block command / allow command / prompt for approval\n * - Browser: block navigation / show confirmation dialog\n * - Express: return 403 / pass through / inject headers\n */\n enforceDecision(decision: VerificationDecision): Promise<void>;\n}\n\n/**\n * Current adapter interface version.\n */\nexport const ADAPTER_INTERFACE_VERSION = 1;\n\n/**\n * Check if an adapter is compatible with the current interface version.\n */\nexport function isCompatibleAdapter(adapter: PlatformAdapter): boolean {\n return adapter.interfaceVersion === ADAPTER_INTERFACE_VERSION;\n}\n","/**\n * Shared purpose mapping utilities for Layer 4 platform adapters.\n *\n * Maps platform-native action names to PDLSS purpose categories.\n * Used by OpenClaw CLI, Cursor, browser, and future adapters.\n */\n\n// -----------------------------------------------------------------------\n// Tool → Purpose mapping\n// -----------------------------------------------------------------------\n\n/** Standard tool name → PDLSS purpose mapping used by all adapters */\nconst TOOL_PURPOSE_MAP: Record<string, string> = {\n // Shell\n shell_exec: 'shell.exec',\n run_command: 'shell.exec',\n execute: 'shell.exec',\n terminal_exec: 'shell.exec',\n run_terminal_command: 'shell.exec',\n\n // File read\n file_read: 'file.read',\n read_file: 'file.read',\n\n // File write\n file_write: 'file.write',\n write_file: 'file.write',\n create_file: 'file.write',\n edit_file: 'file.write',\n\n // File delete\n file_delete: 'file.delete',\n delete_file: 'file.delete',\n\n // Network\n http_request: 'network.request',\n fetch: 'network.request',\n web_request: 'network.request',\n\n // Email\n send_email: 'email.send',\n read_email: 'email.read',\n\n // Calendar\n create_event: 'calendar.create',\n\n // Database\n query_database: 'database.query',\n write_database: 'database.write',\n\n // Payment\n payment_execute: 'payment.execute',\n};\n\n/**\n * Map a tool/action name to a PDLSS purpose category.\n * Returns `tool.<name>` for unmapped tools (denied by default).\n */\nexport function mapToolToPurpose(toolName: string): string {\n return TOOL_PURPOSE_MAP[toolName] || `tool.${toolName}`;\n}\n\n/**\n * Register additional tool → purpose mappings (e.g. from a platform adapter).\n * Does not overwrite existing mappings.\n */\nexport function registerToolMappings(mappings: Record<string, string>): void {\n for (const [tool, purpose] of Object.entries(mappings)) {\n if (!(tool in TOOL_PURPOSE_MAP)) {\n TOOL_PURPOSE_MAP[tool] = purpose;\n }\n }\n}\n\n// -----------------------------------------------------------------------\n// Target extraction\n// -----------------------------------------------------------------------\n\n/**\n * Extract the meaningful target string from tool arguments.\n * Uses the purpose category to determine which argument field is relevant.\n */\nexport function extractTarget(toolName: string, args: Record<string, unknown>): string {\n const purpose = mapToolToPurpose(toolName);\n\n if (purpose.startsWith('shell.')) {\n return String(args.command || args.cmd || args.script || '');\n }\n\n if (purpose.startsWith('file.')) {\n return String(args.path || args.file || args.filename || args.file_path || '');\n }\n\n if (purpose.startsWith('network.')) {\n return String(args.url || args.endpoint || args.uri || '');\n }\n\n if (purpose.startsWith('email.')) {\n return String(args.to || args.recipient || args.address || '');\n }\n\n if (purpose.startsWith('database.')) {\n return String(args.query || args.table || '');\n }\n\n if (purpose.startsWith('payment.')) {\n return String(args.description || args.merchant || args.amount || '');\n }\n\n // Fallback: try common field names\n if (args.command) return String(args.command);\n if (args.path) return String(args.path);\n if (args.url) return String(args.url);\n\n // Default: use first non-empty string argument or tool name\n for (const val of Object.values(args)) {\n if (typeof val === 'string' && val.length > 0) return val;\n }\n return toolName;\n}\n\n// -----------------------------------------------------------------------\n// Network domain extraction\n// -----------------------------------------------------------------------\n\n/**\n * Extract network domains from a URL target.\n * Returns undefined if the target is not a URL.\n */\nexport function extractNetworkDomains(target: string): string[] | undefined {\n try {\n if (target.startsWith('http://') || target.startsWith('https://')) {\n const url = new URL(target);\n return [url.hostname];\n }\n } catch {\n // Not a URL\n }\n return undefined;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/adapter-interface/interface.ts","../../src/adapter-interface/purpose-mapping.ts","../../src/cursor/cursor-adapter.ts"],"sourcesContent":["/**\n * PlatformAdapter Interface\n *\n * The contract that Layer 4 platform adapters implement.\n * Agent-side interception: governs what agents are allowed to do before they do it.\n *\n * Each adapter is 200-500 lines of platform-specific code.\n * All verification logic lives in the gateway — adapters just translate\n * between the platform's world and AstraSync's world.\n */\n\nimport type { AstraSyncGateway } from '../gateway/gateway';\nimport type { PDLSSContext, VerificationDecision, AgentAction, InterceptResult } from '../gateway/types';\n\nexport interface AdapterConfig {\n /** The AstraSyncGateway instance (handles mode routing) */\n gateway: AstraSyncGateway;\n /** Platform-specific configuration */\n adapterOptions: Record<string, unknown>;\n}\n\nexport interface PlatformAdapter {\n /**\n * Interface version for compatibility checking.\n * Current version: 1.\n */\n readonly interfaceVersion: number;\n\n /**\n * Platform-specific initialization.\n * Load config, register hooks, establish connections.\n */\n initialize(config: AdapterConfig): Promise<void>;\n\n /**\n * Graceful shutdown.\n * Drain in-flight verifications, deregister hooks, close connections.\n */\n shutdown(): Promise<void>;\n\n /**\n * Intercept an agent action before execution.\n *\n * How this works depends on the platform:\n * - CLI adapter: proxy server captures outbound request\n * - Browser adapter: content script intercepts DOM interaction\n * - Express: middleware captures inbound request\n */\n interceptAction(action: AgentAction): Promise<InterceptResult>;\n\n /**\n * Extract PDLSS-compatible context from a platform-specific action.\n * Maps platform-native action format to the universal PDLSSContext.\n */\n extractContext(action: AgentAction): PDLSSContext;\n\n /**\n * Enforce the verification decision in a platform-specific way.\n *\n * How this works depends on the platform:\n * - CLI: block command / allow command / prompt for approval\n * - Browser: block navigation / show confirmation dialog\n * - Express: return 403 / pass through / inject headers\n */\n enforceDecision(decision: VerificationDecision): Promise<void>;\n}\n\n/**\n * Current adapter interface version.\n */\nexport const ADAPTER_INTERFACE_VERSION = 1;\n\n/**\n * Check if an adapter is compatible with the current interface version.\n */\nexport function isCompatibleAdapter(adapter: PlatformAdapter): boolean {\n return adapter.interfaceVersion === ADAPTER_INTERFACE_VERSION;\n}\n","/**\n * Shared purpose mapping utilities for Layer 4 platform adapters.\n *\n * Maps platform-native action names to PDLSS purpose categories.\n * Used by OpenClaw CLI, Cursor, browser, and future adapters.\n */\n\n// -----------------------------------------------------------------------\n// Tool → Purpose mapping\n// -----------------------------------------------------------------------\n\n/** Standard tool name → PDLSS purpose mapping used by all adapters */\nconst TOOL_PURPOSE_MAP: Record<string, string> = {\n // Shell\n shell_exec: 'shell.exec',\n run_command: 'shell.exec',\n execute: 'shell.exec',\n terminal_exec: 'shell.exec',\n run_terminal_command: 'shell.exec',\n\n // File read\n file_read: 'file.read',\n read_file: 'file.read',\n\n // File write\n file_write: 'file.write',\n write_file: 'file.write',\n create_file: 'file.write',\n edit_file: 'file.write',\n\n // File delete\n file_delete: 'file.delete',\n delete_file: 'file.delete',\n\n // Network\n http_request: 'network.request',\n fetch: 'network.request',\n web_request: 'network.request',\n\n // Email\n send_email: 'email.send',\n read_email: 'email.read',\n\n // Calendar\n create_event: 'calendar.create',\n\n // Database\n query_database: 'database.query',\n write_database: 'database.write',\n\n // Payment\n payment_execute: 'payment.execute',\n};\n\n/**\n * Map a tool/action name to a PDLSS purpose category.\n * Returns `tool.<name>` for unmapped tools (denied by default).\n */\nexport function mapToolToPurpose(toolName: string): string {\n return TOOL_PURPOSE_MAP[toolName] || `tool.${toolName}`;\n}\n\n/**\n * Register additional tool → purpose mappings (e.g. from a platform adapter).\n * Does not overwrite existing mappings.\n */\nexport function registerToolMappings(mappings: Record<string, string>): void {\n for (const [tool, purpose] of Object.entries(mappings)) {\n if (!(tool in TOOL_PURPOSE_MAP)) {\n TOOL_PURPOSE_MAP[tool] = purpose;\n }\n }\n}\n\n// -----------------------------------------------------------------------\n// Target extraction\n// -----------------------------------------------------------------------\n\n/**\n * Extract the meaningful target string from tool arguments.\n * Uses the purpose category to determine which argument field is relevant.\n */\nexport function extractTarget(toolName: string, args: Record<string, unknown>): string {\n const purpose = mapToolToPurpose(toolName);\n\n if (purpose.startsWith('shell.')) {\n return String(args.command || args.cmd || args.script || '');\n }\n\n if (purpose.startsWith('file.')) {\n return String(args.path || args.file || args.filename || args.file_path || '');\n }\n\n if (purpose.startsWith('network.')) {\n return String(args.url || args.endpoint || args.uri || '');\n }\n\n if (purpose.startsWith('email.')) {\n return String(args.to || args.recipient || args.address || '');\n }\n\n if (purpose.startsWith('database.')) {\n return String(args.query || args.table || '');\n }\n\n if (purpose.startsWith('payment.')) {\n return String(args.description || args.merchant || args.amount || '');\n }\n\n // Fallback: try common field names\n if (args.command) return String(args.command);\n if (args.path) return String(args.path);\n if (args.url) return String(args.url);\n\n // Default: use first non-empty string argument or tool name\n for (const val of Object.values(args)) {\n if (typeof val === 'string' && val.length > 0) return val;\n }\n return toolName;\n}\n\n// -----------------------------------------------------------------------\n// Network domain extraction\n// -----------------------------------------------------------------------\n\n/**\n * Extract network domains from a URL target.\n * Returns undefined if the target is not a URL.\n */\nexport function extractNetworkDomains(target: string): string[] | undefined {\n try {\n if (target.startsWith('http://') || target.startsWith('https://')) {\n const url = new URL(target);\n return [url.hostname];\n }\n } catch {\n // Not a URL\n }\n return undefined;\n}\n","/**\n * @astrasyncai/adapter-cursor\n *\n * Layer 4 adapter for Cursor / VS Code AI agents.\n * Intercepts file operations, terminal commands, and network requests\n * made by the AI agent within the editor.\n *\n * The adapter does NOT depend on @types/vscode. Instead, the VS Code\n * extension passes the real `vscode` module via a minimal interface\n * at initialize() time. This keeps the library free of extension host deps.\n *\n * ~250 lines — thin, disposable, platform-specific.\n */\n\nimport type { PlatformAdapter, AdapterConfig } from '../adapter-interface/interface';\nimport type { PDLSSContext, VerificationDecision, AgentAction, InterceptResult } from '../gateway/types';\nimport type { AstraSyncGateway } from '../gateway/gateway';\nimport { ADAPTER_INTERFACE_VERSION } from '../adapter-interface/interface';\nimport { mapToolToPurpose, extractTarget, extractNetworkDomains } from '../adapter-interface/purpose-mapping';\n\n// -----------------------------------------------------------------------\n// Minimal VS Code type stubs (injected at runtime by the extension)\n// -----------------------------------------------------------------------\n\nexport interface Disposable {\n dispose(): void;\n}\n\nexport interface VSCodeAPI {\n workspace: {\n onDidCreateFiles: (listener: (e: FileEvent) => void) => Disposable;\n onDidDeleteFiles: (listener: (e: FileEvent) => void) => Disposable;\n onDidSaveTextDocument: (listener: (doc: TextDocumentLike) => void) => Disposable;\n };\n window: {\n onDidOpenTerminal: (listener: (terminal: TerminalLike) => void) => Disposable;\n showWarningMessage(message: string, ...items: string[]): Thenable<string | undefined>;\n showInformationMessage(message: string, ...items: string[]): Thenable<string | undefined>;\n showErrorMessage(message: string, ...items: string[]): Thenable<string | undefined>;\n createOutputChannel(name: string): OutputChannelLike;\n };\n commands: {\n registerCommand(command: string, callback: (...args: unknown[]) => unknown): Disposable;\n };\n}\n\ninterface FileEvent {\n files: ReadonlyArray<{ fsPath: string; path: string }>;\n}\n\ninterface TextDocumentLike {\n uri: { fsPath: string; path: string };\n languageId: string;\n fileName: string;\n}\n\ninterface TerminalLike {\n name: string;\n processId: Thenable<number | undefined>;\n sendText(text: string, addNewLine?: boolean): void;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-empty-interface\ninterface Thenable<T> extends PromiseLike<T> {}\n\ninterface OutputChannelLike {\n appendLine(value: string): void;\n show(): void;\n dispose(): void;\n}\n\n// -----------------------------------------------------------------------\n// Configuration\n// -----------------------------------------------------------------------\n\nexport interface CursorAdapterOptions {\n /** The VS Code API object (passed by the extension at runtime) */\n vscode: VSCodeAPI;\n /** Callback for MANUAL_REVIEW decisions */\n onApprovalRequired?: (context: PDLSSContext, decision: VerificationDecision) => Promise<boolean>;\n}\n\n// -----------------------------------------------------------------------\n// Adapter implementation\n// -----------------------------------------------------------------------\n\nexport class CursorAdapter implements PlatformAdapter {\n readonly interfaceVersion = ADAPTER_INTERFACE_VERSION;\n\n private gateway!: AstraSyncGateway;\n private vscode!: VSCodeAPI;\n private disposables: Disposable[] = [];\n private outputChannel: OutputChannelLike | null = null;\n private onApprovalRequired: (context: PDLSSContext, decision: VerificationDecision) => Promise<boolean>;\n private _isRunning = false;\n\n constructor(options?: Partial<CursorAdapterOptions>) {\n this.onApprovalRequired = options?.onApprovalRequired ?? (async () => false);\n if (options?.vscode) {\n this.vscode = options.vscode;\n }\n }\n\n get isRunning(): boolean {\n return this._isRunning;\n }\n\n async initialize(config: AdapterConfig): Promise<void> {\n this.gateway = config.gateway as AstraSyncGateway;\n\n if (config.adapterOptions.vscode) {\n this.vscode = config.adapterOptions.vscode as VSCodeAPI;\n }\n\n if (!this.vscode) {\n throw new Error('CursorAdapter requires vscode API — pass it via adapterOptions.vscode');\n }\n\n // Output channel for logging decisions\n this.outputChannel = this.vscode.window.createOutputChannel('AstraSync Local Guard');\n\n // Register file watchers\n this.disposables.push(\n this.vscode.workspace.onDidCreateFiles((e) => {\n for (const file of e.files) {\n this.evaluateFileAction('file_write', file.fsPath);\n }\n }),\n );\n\n this.disposables.push(\n this.vscode.workspace.onDidDeleteFiles((e) => {\n for (const file of e.files) {\n this.evaluateFileAction('file_delete', file.fsPath);\n }\n }),\n );\n\n this.disposables.push(\n this.vscode.workspace.onDidSaveTextDocument((doc) => {\n this.evaluateFileAction('file_write', doc.fileName);\n }),\n );\n\n // Register status command\n this.disposables.push(\n this.vscode.commands.registerCommand('astrasync.status', () => {\n this.outputChannel?.show();\n this.vscode.window.showInformationMessage('AstraSync Local Guard is active');\n }),\n );\n\n this._isRunning = true;\n this.log('AstraSync Local Guard for Cursor — initialized');\n }\n\n async shutdown(): Promise<void> {\n for (const d of this.disposables) {\n d.dispose();\n }\n this.disposables = [];\n this.outputChannel?.dispose();\n this.outputChannel = null;\n this._isRunning = false;\n }\n\n async interceptAction(action: AgentAction): Promise<InterceptResult> {\n const raw = action.raw as { type: string; path?: string; command?: string };\n\n if (!raw.type) {\n return { intercepted: false, skipReason: 'No action type' };\n }\n\n const context = this.extractContext(action);\n return { intercepted: true, context };\n }\n\n extractContext(action: AgentAction): PDLSSContext {\n const raw = action.raw as { type: string; path?: string; command?: string; url?: string };\n\n let toolName: string;\n const args: Record<string, unknown> = {};\n\n switch (raw.type) {\n case 'file.create':\n case 'file.save':\n toolName = 'file_write';\n args.path = raw.path || '';\n break;\n case 'file.delete':\n toolName = 'file_delete';\n args.path = raw.path || '';\n break;\n case 'file.read':\n toolName = 'file_read';\n args.path = raw.path || '';\n break;\n case 'terminal.exec':\n toolName = 'shell_exec';\n args.command = raw.command || '';\n break;\n case 'network.request':\n toolName = 'http_request';\n args.url = raw.url || '';\n break;\n default:\n toolName = raw.type;\n }\n\n const purpose = mapToolToPurpose(toolName);\n const target = extractTarget(toolName, args);\n const networkAccess = extractNetworkDomains(target);\n\n return {\n purpose,\n action: toolName,\n target,\n ...(networkAccess && { networkAccess }),\n };\n }\n\n async enforceDecision(decision: VerificationDecision): Promise<void> {\n switch (decision.recommendation) {\n case 'DENY':\n this.log(`BLOCKED: ${decision.reason}`);\n this.vscode.window.showErrorMessage(\n `AstraSync blocked action: ${decision.reason}`,\n );\n break;\n case 'MANUAL_REVIEW':\n this.log(`REVIEW: ${decision.reason}`);\n break;\n case 'ALLOW':\n // No action needed\n break;\n }\n }\n\n // =====================================================================\n // Internal helpers\n // =====================================================================\n\n private async evaluateFileAction(toolName: string, filePath: string): Promise<void> {\n const action: AgentAction = {\n raw: {\n type: toolName === 'file_delete' ? 'file.delete' : 'file.save',\n path: filePath,\n },\n platform: 'cursor',\n timestamp: new Date(),\n };\n\n const interception = await this.interceptAction(action);\n if (!interception.intercepted || !interception.context) return;\n\n const decision = await this.gateway.evaluate(interception.context);\n\n if (decision.recommendation === 'DENY') {\n await this.enforceDecision(decision);\n } else if (decision.recommendation === 'MANUAL_REVIEW') {\n const approved = await this.handleApproval(interception.context, decision);\n if (!approved) {\n await this.enforceDecision({ ...decision, recommendation: 'DENY' });\n }\n }\n\n this.log(`${decision.recommendation}: ${interception.context.purpose} -> ${interception.context.target}`);\n }\n\n private async handleApproval(context: PDLSSContext, decision: VerificationDecision): Promise<boolean> {\n if (this.onApprovalRequired) {\n return this.onApprovalRequired(context, decision);\n }\n\n // Default: VS Code prompt\n const choice = await this.vscode.window.showWarningMessage(\n `AstraSync: ${decision.reason}. Allow?`,\n 'Allow',\n 'Deny',\n );\n return choice === 'Allow';\n }\n\n private log(message: string): void {\n this.outputChannel?.appendLine(`[${new Date().toISOString()}] ${message}`);\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/adapter-interface/interface.ts","../../src/adapter-interface/purpose-mapping.ts","../../src/cursor/cursor-adapter.ts"],"sourcesContent":["/**\n * PlatformAdapter Interface\n *\n * The contract that Layer 4 platform adapters implement.\n * Agent-side interception: governs what agents are allowed to do before they do it.\n *\n * Each adapter is 200-500 lines of platform-specific code.\n * All verification logic lives in the gateway — adapters just translate\n * between the platform's world and AstraSync's world.\n */\n\nimport type { AstraSyncGateway } from '../gateway/gateway';\nimport type { PDLSSContext, VerificationDecision, AgentAction, InterceptResult } from '../gateway/types';\n\nexport interface AdapterConfig {\n /** The AstraSyncGateway instance (handles mode routing) */\n gateway: AstraSyncGateway;\n /** Platform-specific configuration */\n adapterOptions: Record<string, unknown>;\n}\n\nexport interface PlatformAdapter {\n /**\n * Interface version for compatibility checking.\n * Current version: 1.\n */\n readonly interfaceVersion: number;\n\n /**\n * Platform-specific initialization.\n * Load config, register hooks, establish connections.\n */\n initialize(config: AdapterConfig): Promise<void>;\n\n /**\n * Graceful shutdown.\n * Drain in-flight verifications, deregister hooks, close connections.\n */\n shutdown(): Promise<void>;\n\n /**\n * Intercept an agent action before execution.\n *\n * How this works depends on the platform:\n * - CLI adapter: proxy server captures outbound request\n * - Browser adapter: content script intercepts DOM interaction\n * - Express: middleware captures inbound request\n */\n interceptAction(action: AgentAction): Promise<InterceptResult>;\n\n /**\n * Extract PDLSS-compatible context from a platform-specific action.\n * Maps platform-native action format to the universal PDLSSContext.\n */\n extractContext(action: AgentAction): PDLSSContext;\n\n /**\n * Enforce the verification decision in a platform-specific way.\n *\n * How this works depends on the platform:\n * - CLI: block command / allow command / prompt for approval\n * - Browser: block navigation / show confirmation dialog\n * - Express: return 403 / pass through / inject headers\n */\n enforceDecision(decision: VerificationDecision): Promise<void>;\n}\n\n/**\n * Current adapter interface version.\n */\nexport const ADAPTER_INTERFACE_VERSION = 1;\n\n/**\n * Check if an adapter is compatible with the current interface version.\n */\nexport function isCompatibleAdapter(adapter: PlatformAdapter): boolean {\n return adapter.interfaceVersion === ADAPTER_INTERFACE_VERSION;\n}\n","/**\n * Shared purpose mapping utilities for Layer 4 platform adapters.\n *\n * Maps platform-native action names to PDLSS purpose categories.\n * Used by OpenClaw CLI, Cursor, browser, and future adapters.\n */\n\n// -----------------------------------------------------------------------\n// Tool → Purpose mapping\n// -----------------------------------------------------------------------\n\n/** Standard tool name → PDLSS purpose mapping used by all adapters */\nconst TOOL_PURPOSE_MAP: Record<string, string> = {\n // Shell\n shell_exec: 'shell.exec',\n run_command: 'shell.exec',\n execute: 'shell.exec',\n terminal_exec: 'shell.exec',\n run_terminal_command: 'shell.exec',\n\n // File read\n file_read: 'file.read',\n read_file: 'file.read',\n\n // File write\n file_write: 'file.write',\n write_file: 'file.write',\n create_file: 'file.write',\n edit_file: 'file.write',\n\n // File delete\n file_delete: 'file.delete',\n delete_file: 'file.delete',\n\n // Network\n http_request: 'network.request',\n fetch: 'network.request',\n web_request: 'network.request',\n\n // Email\n send_email: 'email.send',\n read_email: 'email.read',\n\n // Calendar\n create_event: 'calendar.create',\n\n // Database\n query_database: 'database.query',\n write_database: 'database.write',\n\n // Payment\n payment_execute: 'payment.execute',\n};\n\n/**\n * Map a tool/action name to a PDLSS purpose category.\n * Returns `tool.<name>` for unmapped tools (denied by default).\n */\nexport function mapToolToPurpose(toolName: string): string {\n return TOOL_PURPOSE_MAP[toolName] || `tool.${toolName}`;\n}\n\n/**\n * Register additional tool → purpose mappings (e.g. from a platform adapter).\n * Does not overwrite existing mappings.\n */\nexport function registerToolMappings(mappings: Record<string, string>): void {\n for (const [tool, purpose] of Object.entries(mappings)) {\n if (!(tool in TOOL_PURPOSE_MAP)) {\n TOOL_PURPOSE_MAP[tool] = purpose;\n }\n }\n}\n\n// -----------------------------------------------------------------------\n// Target extraction\n// -----------------------------------------------------------------------\n\n/**\n * Extract the meaningful target string from tool arguments.\n * Uses the purpose category to determine which argument field is relevant.\n */\nexport function extractTarget(toolName: string, args: Record<string, unknown>): string {\n const purpose = mapToolToPurpose(toolName);\n\n if (purpose.startsWith('shell.')) {\n return String(args.command || args.cmd || args.script || '');\n }\n\n if (purpose.startsWith('file.')) {\n return String(args.path || args.file || args.filename || args.file_path || '');\n }\n\n if (purpose.startsWith('network.')) {\n return String(args.url || args.endpoint || args.uri || '');\n }\n\n if (purpose.startsWith('email.')) {\n return String(args.to || args.recipient || args.address || '');\n }\n\n if (purpose.startsWith('database.')) {\n return String(args.query || args.table || '');\n }\n\n if (purpose.startsWith('payment.')) {\n return String(args.description || args.merchant || args.amount || '');\n }\n\n // Fallback: try common field names\n if (args.command) return String(args.command);\n if (args.path) return String(args.path);\n if (args.url) return String(args.url);\n\n // Default: use first non-empty string argument or tool name\n for (const val of Object.values(args)) {\n if (typeof val === 'string' && val.length > 0) return val;\n }\n return toolName;\n}\n\n// -----------------------------------------------------------------------\n// Network domain extraction\n// -----------------------------------------------------------------------\n\n/**\n * Extract network domains from a URL target.\n * Returns undefined if the target is not a URL.\n */\nexport function extractNetworkDomains(target: string): string[] | undefined {\n try {\n if (target.startsWith('http://') || target.startsWith('https://')) {\n const url = new URL(target);\n return [url.hostname];\n }\n } catch {\n // Not a URL\n }\n return undefined;\n}\n","/**\n * @astrasyncai/adapter-cursor\n *\n * Layer 4 adapter for Cursor / VS Code AI agents.\n * Intercepts file operations, terminal commands, and network requests\n * made by the AI agent within the editor.\n *\n * The adapter does NOT depend on @types/vscode. Instead, the VS Code\n * extension passes the real `vscode` module via a minimal interface\n * at initialize() time. This keeps the library free of extension host deps.\n *\n * ~250 lines — thin, disposable, platform-specific.\n */\n\nimport type { PlatformAdapter, AdapterConfig } from '../adapter-interface/interface';\nimport type { PDLSSContext, VerificationDecision, AgentAction, InterceptResult } from '../gateway/types';\nimport type { AstraSyncGateway } from '../gateway/gateway';\nimport { ADAPTER_INTERFACE_VERSION } from '../adapter-interface/interface';\nimport { mapToolToPurpose, extractTarget, extractNetworkDomains } from '../adapter-interface/purpose-mapping';\n\n// -----------------------------------------------------------------------\n// Minimal VS Code type stubs (injected at runtime by the extension)\n// -----------------------------------------------------------------------\n\nexport interface Disposable {\n dispose(): void;\n}\n\nexport interface VSCodeAPI {\n workspace: {\n onDidCreateFiles: (listener: (e: FileEvent) => void) => Disposable;\n onDidDeleteFiles: (listener: (e: FileEvent) => void) => Disposable;\n onDidSaveTextDocument: (listener: (doc: TextDocumentLike) => void) => Disposable;\n };\n window: {\n onDidOpenTerminal: (listener: (terminal: TerminalLike) => void) => Disposable;\n showWarningMessage(message: string, ...items: string[]): Thenable<string | undefined>;\n showInformationMessage(message: string, ...items: string[]): Thenable<string | undefined>;\n showErrorMessage(message: string, ...items: string[]): Thenable<string | undefined>;\n createOutputChannel(name: string): OutputChannelLike;\n };\n commands: {\n registerCommand(command: string, callback: (...args: unknown[]) => unknown): Disposable;\n };\n}\n\ninterface FileEvent {\n files: ReadonlyArray<{ fsPath: string; path: string }>;\n}\n\ninterface TextDocumentLike {\n uri: { fsPath: string; path: string };\n languageId: string;\n fileName: string;\n}\n\ninterface TerminalLike {\n name: string;\n processId: Thenable<number | undefined>;\n sendText(text: string, addNewLine?: boolean): void;\n}\n\ntype Thenable<T> = PromiseLike<T>;\n\ninterface OutputChannelLike {\n appendLine(value: string): void;\n show(): void;\n dispose(): void;\n}\n\n// -----------------------------------------------------------------------\n// Configuration\n// -----------------------------------------------------------------------\n\nexport interface CursorAdapterOptions {\n /** The VS Code API object (passed by the extension at runtime) */\n vscode: VSCodeAPI;\n /** Callback for MANUAL_REVIEW decisions */\n onApprovalRequired?: (context: PDLSSContext, decision: VerificationDecision) => Promise<boolean>;\n}\n\n// -----------------------------------------------------------------------\n// Adapter implementation\n// -----------------------------------------------------------------------\n\nexport class CursorAdapter implements PlatformAdapter {\n readonly interfaceVersion = ADAPTER_INTERFACE_VERSION;\n\n private gateway!: AstraSyncGateway;\n private vscode!: VSCodeAPI;\n private disposables: Disposable[] = [];\n private outputChannel: OutputChannelLike | null = null;\n private onApprovalRequired: (context: PDLSSContext, decision: VerificationDecision) => Promise<boolean>;\n private _isRunning = false;\n\n constructor(options?: Partial<CursorAdapterOptions>) {\n this.onApprovalRequired = options?.onApprovalRequired ?? (async () => false);\n if (options?.vscode) {\n this.vscode = options.vscode;\n }\n }\n\n get isRunning(): boolean {\n return this._isRunning;\n }\n\n async initialize(config: AdapterConfig): Promise<void> {\n this.gateway = config.gateway as AstraSyncGateway;\n\n if (config.adapterOptions.vscode) {\n this.vscode = config.adapterOptions.vscode as VSCodeAPI;\n }\n\n if (!this.vscode) {\n throw new Error('CursorAdapter requires vscode API — pass it via adapterOptions.vscode');\n }\n\n // Output channel for logging decisions\n this.outputChannel = this.vscode.window.createOutputChannel('AstraSync Local Guard');\n\n // Register file watchers\n this.disposables.push(\n this.vscode.workspace.onDidCreateFiles((e) => {\n for (const file of e.files) {\n this.evaluateFileAction('file_write', file.fsPath);\n }\n }),\n );\n\n this.disposables.push(\n this.vscode.workspace.onDidDeleteFiles((e) => {\n for (const file of e.files) {\n this.evaluateFileAction('file_delete', file.fsPath);\n }\n }),\n );\n\n this.disposables.push(\n this.vscode.workspace.onDidSaveTextDocument((doc) => {\n this.evaluateFileAction('file_write', doc.fileName);\n }),\n );\n\n // Register status command\n this.disposables.push(\n this.vscode.commands.registerCommand('astrasync.status', () => {\n this.outputChannel?.show();\n this.vscode.window.showInformationMessage('AstraSync Local Guard is active');\n }),\n );\n\n this._isRunning = true;\n this.log('AstraSync Local Guard for Cursor — initialized');\n }\n\n async shutdown(): Promise<void> {\n for (const d of this.disposables) {\n d.dispose();\n }\n this.disposables = [];\n this.outputChannel?.dispose();\n this.outputChannel = null;\n this._isRunning = false;\n }\n\n async interceptAction(action: AgentAction): Promise<InterceptResult> {\n const raw = action.raw as { type: string; path?: string; command?: string };\n\n if (!raw.type) {\n return { intercepted: false, skipReason: 'No action type' };\n }\n\n const context = this.extractContext(action);\n return { intercepted: true, context };\n }\n\n extractContext(action: AgentAction): PDLSSContext {\n const raw = action.raw as { type: string; path?: string; command?: string; url?: string };\n\n let toolName: string;\n const args: Record<string, unknown> = {};\n\n switch (raw.type) {\n case 'file.create':\n case 'file.save':\n toolName = 'file_write';\n args.path = raw.path || '';\n break;\n case 'file.delete':\n toolName = 'file_delete';\n args.path = raw.path || '';\n break;\n case 'file.read':\n toolName = 'file_read';\n args.path = raw.path || '';\n break;\n case 'terminal.exec':\n toolName = 'shell_exec';\n args.command = raw.command || '';\n break;\n case 'network.request':\n toolName = 'http_request';\n args.url = raw.url || '';\n break;\n default:\n toolName = raw.type;\n }\n\n const purpose = mapToolToPurpose(toolName);\n const target = extractTarget(toolName, args);\n const networkAccess = extractNetworkDomains(target);\n\n return {\n purpose,\n action: toolName,\n target,\n ...(networkAccess && { networkAccess }),\n };\n }\n\n async enforceDecision(decision: VerificationDecision): Promise<void> {\n switch (decision.recommendation) {\n case 'DENY':\n this.log(`BLOCKED: ${decision.reason}`);\n this.vscode.window.showErrorMessage(\n `AstraSync blocked action: ${decision.reason}`,\n );\n break;\n case 'MANUAL_REVIEW':\n this.log(`REVIEW: ${decision.reason}`);\n break;\n case 'ALLOW':\n // No action needed\n break;\n }\n }\n\n // =====================================================================\n // Internal helpers\n // =====================================================================\n\n private async evaluateFileAction(toolName: string, filePath: string): Promise<void> {\n const action: AgentAction = {\n raw: {\n type: toolName === 'file_delete' ? 'file.delete' : 'file.save',\n path: filePath,\n },\n platform: 'cursor',\n timestamp: new Date(),\n };\n\n const interception = await this.interceptAction(action);\n if (!interception.intercepted || !interception.context) return;\n\n const decision = await this.gateway.evaluate(interception.context);\n\n if (decision.recommendation === 'DENY') {\n await this.enforceDecision(decision);\n } else if (decision.recommendation === 'MANUAL_REVIEW') {\n const approved = await this.handleApproval(interception.context, decision);\n if (!approved) {\n await this.enforceDecision({ ...decision, recommendation: 'DENY' });\n }\n }\n\n this.log(`${decision.recommendation}: ${interception.context.purpose} -> ${interception.context.target}`);\n }\n\n private async handleApproval(context: PDLSSContext, decision: VerificationDecision): Promise<boolean> {\n if (this.onApprovalRequired) {\n return this.onApprovalRequired(context, decision);\n }\n\n // Default: VS Code prompt\n const choice = await this.vscode.window.showWarningMessage(\n `AstraSync: ${decision.reason}. Allow?`,\n 'Allow',\n 'Deny',\n );\n return choice === 'Allow';\n }\n\n private log(message: string): void {\n this.outputChannel?.appendLine(`[${new Date().toISOString()}] ${message}`);\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { VSCodeAPI } from './cursor-adapter.mjs';
|
|
2
|
+
import '../adapter-interface/interface.mjs';
|
|
3
|
+
import '../gateway/gateway.mjs';
|
|
4
|
+
import '../types-jJnPXStc.mjs';
|
|
5
|
+
import '../types-CxQwJKbd.mjs';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* VS Code Extension entry point for AstraSync Local Guard (Cursor/VS Code).
|
|
9
|
+
*
|
|
10
|
+
* This file is the `main` referenced in the extension's package.json.
|
|
11
|
+
* It creates a CursorAdapter, wires it to the real `vscode` API,
|
|
12
|
+
* and manages its lifecycle through activate/deactivate.
|
|
13
|
+
*
|
|
14
|
+
* Build: bundled separately via tsup/esbuild with `vscode` marked as external.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Called by VS Code when the extension activates.
|
|
19
|
+
* The `vscode` module is passed in at runtime — never bundled.
|
|
20
|
+
*/
|
|
21
|
+
declare function activate(vscodeApi: VSCodeAPI): Promise<void>;
|
|
22
|
+
/**
|
|
23
|
+
* Called by VS Code when the extension deactivates.
|
|
24
|
+
*/
|
|
25
|
+
declare function deactivate(): Promise<void>;
|
|
26
|
+
|
|
27
|
+
export { activate, deactivate };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { VSCodeAPI } from './cursor-adapter.js';
|
|
2
|
+
import '../adapter-interface/interface.js';
|
|
3
|
+
import '../gateway/gateway.js';
|
|
4
|
+
import '../types-79qS7aON.js';
|
|
5
|
+
import '../types-CxQwJKbd.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* VS Code Extension entry point for AstraSync Local Guard (Cursor/VS Code).
|
|
9
|
+
*
|
|
10
|
+
* This file is the `main` referenced in the extension's package.json.
|
|
11
|
+
* It creates a CursorAdapter, wires it to the real `vscode` API,
|
|
12
|
+
* and manages its lifecycle through activate/deactivate.
|
|
13
|
+
*
|
|
14
|
+
* Build: bundled separately via tsup/esbuild with `vscode` marked as external.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Called by VS Code when the extension activates.
|
|
19
|
+
* The `vscode` module is passed in at runtime — never bundled.
|
|
20
|
+
*/
|
|
21
|
+
declare function activate(vscodeApi: VSCodeAPI): Promise<void>;
|
|
22
|
+
/**
|
|
23
|
+
* Called by VS Code when the extension deactivates.
|
|
24
|
+
*/
|
|
25
|
+
declare function deactivate(): Promise<void>;
|
|
26
|
+
|
|
27
|
+
export { activate, deactivate };
|