@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.
Files changed (91) hide show
  1. package/dist/adapter-interface/interface.d.mts +2 -2
  2. package/dist/adapter-interface/interface.d.ts +2 -2
  3. package/dist/adapters/express.d.mts +2 -2
  4. package/dist/adapters/express.d.ts +2 -2
  5. package/dist/adapters/express.js +118 -5
  6. package/dist/adapters/express.js.map +1 -1
  7. package/dist/adapters/express.mjs +118 -5
  8. package/dist/adapters/express.mjs.map +1 -1
  9. package/dist/adapters/nextjs.d.mts +2 -2
  10. package/dist/adapters/nextjs.d.ts +2 -2
  11. package/dist/adapters/nextjs.js +189 -9
  12. package/dist/adapters/nextjs.js.map +1 -1
  13. package/dist/adapters/nextjs.mjs +189 -9
  14. package/dist/adapters/nextjs.mjs.map +1 -1
  15. package/dist/adapters/sdk.d.mts +2 -2
  16. package/dist/adapters/sdk.d.ts +2 -2
  17. package/dist/adapters/sdk.js +1 -0
  18. package/dist/adapters/sdk.js.map +1 -1
  19. package/dist/adapters/sdk.mjs +1 -0
  20. package/dist/adapters/sdk.mjs.map +1 -1
  21. package/dist/agent/index.d.mts +2 -2
  22. package/dist/agent/index.d.ts +2 -2
  23. package/dist/browser/background.d.mts +2 -0
  24. package/dist/browser/background.d.ts +2 -0
  25. package/dist/browser/background.js +4090 -0
  26. package/dist/browser/background.js.map +1 -0
  27. package/dist/browser/background.mjs +4088 -0
  28. package/dist/browser/background.mjs.map +1 -0
  29. package/dist/browser/browser-adapter.d.mts +10 -6
  30. package/dist/browser/browser-adapter.d.ts +10 -6
  31. package/dist/browser/browser-adapter.js +16 -5
  32. package/dist/browser/browser-adapter.js.map +1 -1
  33. package/dist/browser/browser-adapter.mjs +14 -4
  34. package/dist/browser/browser-adapter.mjs.map +1 -1
  35. package/dist/cli/index.d.mts +2 -2
  36. package/dist/cli/index.d.ts +2 -2
  37. package/dist/cli/index.js +1 -1
  38. package/dist/cli/index.js.map +1 -1
  39. package/dist/cli/index.mjs +1 -1
  40. package/dist/cli/index.mjs.map +1 -1
  41. package/dist/cursor/cursor-adapter.d.mts +3 -4
  42. package/dist/cursor/cursor-adapter.d.ts +3 -4
  43. package/dist/cursor/cursor-adapter.js.map +1 -1
  44. package/dist/cursor/cursor-adapter.mjs.map +1 -1
  45. package/dist/cursor/extension.d.mts +27 -0
  46. package/dist/cursor/extension.d.ts +27 -0
  47. package/dist/cursor/extension.js +4057 -0
  48. package/dist/cursor/extension.js.map +1 -0
  49. package/dist/cursor/extension.mjs +4029 -0
  50. package/dist/cursor/extension.mjs.map +1 -0
  51. package/dist/{express-DIEyq1Tz.d.ts → express-Bcl-uBUE.d.ts} +1 -1
  52. package/dist/{express-Cp4eg77F.d.mts → express-CtwDIZyF.d.mts} +1 -1
  53. package/dist/gateway/gateway.d.mts +2 -2
  54. package/dist/gateway/gateway.d.ts +2 -2
  55. package/dist/gateway/gateway.js +17 -17
  56. package/dist/gateway/gateway.js.map +1 -1
  57. package/dist/gateway/gateway.mjs +11 -18
  58. package/dist/gateway/gateway.mjs.map +1 -1
  59. package/dist/git-trigger/git-hooks.d.mts +2 -2
  60. package/dist/git-trigger/git-hooks.d.ts +2 -2
  61. package/dist/git-trigger/git-hooks.js +1 -2
  62. package/dist/git-trigger/git-hooks.js.map +1 -1
  63. package/dist/git-trigger/git-hooks.mjs +1 -9
  64. package/dist/git-trigger/git-hooks.mjs.map +1 -1
  65. package/dist/{index-CoLebmwv.d.mts → index-B1ThcGZl.d.mts} +1 -1
  66. package/dist/{index-BhTbGU-o.d.mts → index-BY8yQ8N8.d.mts} +1 -1
  67. package/dist/{index-Bhfxq9xI.d.ts → index-CtYSYwn3.d.ts} +1 -1
  68. package/dist/{index-CNkmHmpi.d.ts → index-DnoXfdFd.d.ts} +1 -1
  69. package/dist/index.d.mts +7 -7
  70. package/dist/index.d.ts +7 -7
  71. package/dist/index.js +201 -14
  72. package/dist/index.js.map +1 -1
  73. package/dist/index.mjs +201 -14
  74. package/dist/index.mjs.map +1 -1
  75. package/dist/local-evaluator/evaluator.d.mts +2 -2
  76. package/dist/local-evaluator/evaluator.d.ts +2 -2
  77. package/dist/local-evaluator/evaluator.js.map +1 -1
  78. package/dist/local-evaluator/evaluator.mjs.map +1 -1
  79. package/dist/{nextjs-_C_FcJY5.d.mts → nextjs-BQyMCSx_.d.mts} +1 -1
  80. package/dist/{nextjs-Cag7libc.d.ts → nextjs-CEldnIJ9.d.ts} +1 -1
  81. package/dist/{sdk-DAJahT3p.d.mts → sdk-BhvuJSrH.d.mts} +1 -1
  82. package/dist/{sdk-CMPDFUjo.d.ts → sdk-BlyVSC_S.d.ts} +1 -1
  83. package/dist/transport/index.d.mts +2 -2
  84. package/dist/transport/index.d.ts +2 -2
  85. package/dist/{types-Ce2mFJkO.d.ts → types-79qS7aON.d.ts} +2 -2
  86. package/dist/{types-Bf8pML07.d.mts → types-CxQwJKbd.d.mts} +17 -2
  87. package/dist/{types-Bf8pML07.d.ts → types-CxQwJKbd.d.ts} +17 -2
  88. package/dist/{types-BvpGdsv1.d.mts → types-jJnPXStc.d.mts} +2 -2
  89. package/dist/ui/index.d.mts +1 -1
  90. package/dist/ui/index.d.ts +1 -1
  91. package/package.json +3 -2
@@ -1,6 +1,6 @@
1
1
  import { AstraSyncGateway } from '../gateway/gateway.mjs';
2
- import { V as VerificationDecision, P as PDLSSContext } from '../types-BvpGdsv1.mjs';
3
- import '../types-Bf8pML07.mjs';
2
+ import { V as VerificationDecision, P as PDLSSContext } from '../types-jJnPXStc.mjs';
3
+ import '../types-CxQwJKbd.mjs';
4
4
 
5
5
  /**
6
6
  * Git Trigger — Enterprise git push / PR verification
@@ -1,6 +1,6 @@
1
1
  import { AstraSyncGateway } from '../gateway/gateway.js';
2
- import { V as VerificationDecision, P as PDLSSContext } from '../types-Ce2mFJkO.js';
3
- import '../types-Bf8pML07.js';
2
+ import { V as VerificationDecision, P as PDLSSContext } from '../types-79qS7aON.js';
3
+ import '../types-CxQwJKbd.js';
4
4
 
5
5
  /**
6
6
  * Git Trigger — Enterprise git push / PR verification
@@ -149,8 +149,7 @@ function uninstallGitHooks(repoRoot, hooks = ["pre-push"]) {
149
149
  if ((0, import_fs.existsSync)(hookPath)) {
150
150
  const content = (0, import_fs.readFileSync)(hookPath, "utf-8");
151
151
  if (content.includes(HOOK_MARKER)) {
152
- const { unlinkSync } = require("fs");
153
- unlinkSync(hookPath);
152
+ (0, import_fs.unlinkSync)(hookPath);
154
153
  removed.push(hookPath);
155
154
  }
156
155
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/git-trigger/git-hooks.ts","../../src/adapter-interface/purpose-mapping.ts"],"sourcesContent":["/**\n * Git Trigger — Enterprise git push / PR verification\n *\n * Installs git hooks that evaluate changed files against the PDLSS policy\n * before allowing pushes to remote repositories.\n *\n * Two enforcement modes (admin-configurable):\n * - block: non-zero exit prevents the push\n * - warn: push proceeds but warning is printed\n */\n\nimport { existsSync, mkdirSync, writeFileSync, readFileSync, chmodSync } from 'fs';\nimport { join } from 'path';\nimport { execSync } from 'child_process';\nimport type { AstraSyncGateway } from '../gateway/gateway';\nimport { mapToolToPurpose, extractTarget, extractNetworkDomains } from '../adapter-interface/purpose-mapping';\nimport type { PDLSSContext, VerificationDecision } from '../gateway/types';\n\n// -----------------------------------------------------------------------\n// Configuration\n// -----------------------------------------------------------------------\n\nexport interface GitTriggerConfig {\n /** Block the push or just warn (default: 'warn') */\n enforcement: 'block' | 'warn';\n /** Which git hooks to install (default: ['pre-push']) */\n hooks: ('pre-push' | 'pre-commit')[];\n /** Only enforce on these branches (default: all) */\n protectedBranches?: string[];\n /** Custom message for blocked pushes */\n blockMessage?: string;\n}\n\nconst DEFAULT_CONFIG: GitTriggerConfig = {\n enforcement: 'warn',\n hooks: ['pre-push'],\n};\n\n// -----------------------------------------------------------------------\n// Hook script content\n// -----------------------------------------------------------------------\n\nconst HOOK_MARKER = '# AstraSync Local Guard — managed hook';\n\nfunction makeHookScript(hookType: 'pre-push' | 'pre-commit'): string {\n return `#!/bin/sh\n${HOOK_MARKER}\n# Runs AstraSync verification on changed files before ${hookType === 'pre-push' ? 'push' : 'commit'}.\n# Installed by: astrasync guard install-hooks\n# To remove: delete this file or run astrasync guard uninstall-hooks\n\nnpx astrasync guard check --trigger=${hookType} \"$@\"\nexit $?\n`;\n}\n\n// -----------------------------------------------------------------------\n// Hook installer\n// -----------------------------------------------------------------------\n\n/**\n * Install git hooks into the repository.\n * Returns the paths of installed hooks.\n */\nexport function installGitHooks(\n repoRoot: string,\n config: Partial<GitTriggerConfig> = {},\n): { installed: string[]; skipped: string[] } {\n const merged = { ...DEFAULT_CONFIG, ...config };\n const hooksDir = join(repoRoot, '.git', 'hooks');\n const installed: string[] = [];\n const skipped: string[] = [];\n\n if (!existsSync(join(repoRoot, '.git'))) {\n throw new Error(`Not a git repository: ${repoRoot}`);\n }\n\n if (!existsSync(hooksDir)) {\n mkdirSync(hooksDir, { recursive: true });\n }\n\n for (const hookType of merged.hooks) {\n const hookPath = join(hooksDir, hookType);\n\n // Don't overwrite non-AstraSync hooks\n if (existsSync(hookPath)) {\n const content = readFileSync(hookPath, 'utf-8');\n if (!content.includes(HOOK_MARKER)) {\n skipped.push(hookPath);\n continue;\n }\n }\n\n writeFileSync(hookPath, makeHookScript(hookType), 'utf-8');\n chmodSync(hookPath, 0o755);\n installed.push(hookPath);\n }\n\n return { installed, skipped };\n}\n\n/**\n * Remove AstraSync-managed git hooks.\n */\nexport function uninstallGitHooks(\n repoRoot: string,\n hooks: ('pre-push' | 'pre-commit')[] = ['pre-push'],\n): string[] {\n const hooksDir = join(repoRoot, '.git', 'hooks');\n const removed: string[] = [];\n\n for (const hookType of hooks) {\n const hookPath = join(hooksDir, hookType);\n if (existsSync(hookPath)) {\n const content = readFileSync(hookPath, 'utf-8');\n if (content.includes(HOOK_MARKER)) {\n const { unlinkSync } = require('fs');\n unlinkSync(hookPath);\n removed.push(hookPath);\n }\n }\n }\n\n return removed;\n}\n\n// -----------------------------------------------------------------------\n// Evaluation logic\n// -----------------------------------------------------------------------\n\n/**\n * Get list of changed files for the current trigger context.\n */\nexport function getChangedFiles(trigger: 'pre-push' | 'pre-commit', repoRoot: string): string[] {\n try {\n let output: string;\n if (trigger === 'pre-push') {\n // Files changed between HEAD and remote tracking branch\n output = execSync('git diff --name-only @{push}.. 2>/dev/null || git diff --name-only HEAD~1', {\n cwd: repoRoot,\n encoding: 'utf-8',\n });\n } else {\n // Files staged for commit\n output = execSync('git diff --cached --name-only', {\n cwd: repoRoot,\n encoding: 'utf-8',\n });\n }\n return output.split('\\n').filter(f => f.trim().length > 0);\n } catch {\n return [];\n }\n}\n\n/**\n * Map a file change to a PDLSS context for evaluation.\n */\nexport function fileChangeToPDLSSContext(filePath: string): PDLSSContext {\n const purpose = mapToolToPurpose('file_write');\n const target = extractTarget('file_write', { path: filePath });\n\n return {\n purpose,\n action: 'file_write',\n target,\n dataAccess: ['write'],\n resourceType: 'file',\n };\n}\n\n/**\n * Evaluate changed files against the gateway policy.\n * Returns a summary of allow/deny/review decisions.\n */\nexport async function evaluateChangedFiles(\n gateway: AstraSyncGateway,\n files: string[],\n config: Partial<GitTriggerConfig> = {},\n): Promise<GitCheckResult> {\n const merged = { ...DEFAULT_CONFIG, ...config };\n const results: { file: string; decision: VerificationDecision }[] = [];\n let blocked = 0;\n let warned = 0;\n let allowed = 0;\n\n for (const file of files) {\n const context = fileChangeToPDLSSContext(file);\n const decision = await gateway.evaluate(context);\n\n results.push({ file, decision });\n\n if (decision.recommendation === 'DENY') {\n blocked++;\n } else if (decision.recommendation === 'MANUAL_REVIEW') {\n warned++;\n } else {\n allowed++;\n }\n }\n\n const shouldBlock = merged.enforcement === 'block' && (blocked > 0);\n\n return {\n allowed,\n blocked,\n warned,\n total: files.length,\n shouldBlock,\n enforcement: merged.enforcement,\n results,\n blockMessage: shouldBlock\n ? (merged.blockMessage || `AstraSync blocked push: ${blocked} file(s) denied by policy`)\n : undefined,\n };\n}\n\nexport interface GitCheckResult {\n allowed: number;\n blocked: number;\n warned: number;\n total: number;\n shouldBlock: boolean;\n enforcement: 'block' | 'warn';\n results: { file: string; decision: VerificationDecision }[];\n blockMessage?: string;\n}\n\n/**\n * Check if the current branch is protected.\n */\nexport function isProtectedBranch(config: Partial<GitTriggerConfig> = {}): boolean {\n if (!config.protectedBranches || config.protectedBranches.length === 0) {\n return true; // All branches protected by default\n }\n\n try {\n const branch = execSync('git rev-parse --abbrev-ref HEAD', { encoding: 'utf-8' }).trim();\n return config.protectedBranches.some(pattern => {\n if (pattern === branch) return true;\n if (pattern.endsWith('*') && branch.startsWith(pattern.slice(0, -1))) return true;\n return false;\n });\n } catch {\n return true; // Can't determine branch, assume protected\n }\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/git-trigger/git-hooks.ts","../../src/adapter-interface/purpose-mapping.ts"],"sourcesContent":["/**\n * Git Trigger — Enterprise git push / PR verification\n *\n * Installs git hooks that evaluate changed files against the PDLSS policy\n * before allowing pushes to remote repositories.\n *\n * Two enforcement modes (admin-configurable):\n * - block: non-zero exit prevents the push\n * - warn: push proceeds but warning is printed\n */\n\nimport { existsSync, mkdirSync, writeFileSync, readFileSync, chmodSync, unlinkSync } from 'fs';\nimport { join } from 'path';\nimport { execSync } from 'child_process';\nimport type { AstraSyncGateway } from '../gateway/gateway';\nimport { mapToolToPurpose, extractTarget } from '../adapter-interface/purpose-mapping';\nimport type { PDLSSContext, VerificationDecision } from '../gateway/types';\n\n// -----------------------------------------------------------------------\n// Configuration\n// -----------------------------------------------------------------------\n\nexport interface GitTriggerConfig {\n /** Block the push or just warn (default: 'warn') */\n enforcement: 'block' | 'warn';\n /** Which git hooks to install (default: ['pre-push']) */\n hooks: ('pre-push' | 'pre-commit')[];\n /** Only enforce on these branches (default: all) */\n protectedBranches?: string[];\n /** Custom message for blocked pushes */\n blockMessage?: string;\n}\n\nconst DEFAULT_CONFIG: GitTriggerConfig = {\n enforcement: 'warn',\n hooks: ['pre-push'],\n};\n\n// -----------------------------------------------------------------------\n// Hook script content\n// -----------------------------------------------------------------------\n\nconst HOOK_MARKER = '# AstraSync Local Guard — managed hook';\n\nfunction makeHookScript(hookType: 'pre-push' | 'pre-commit'): string {\n return `#!/bin/sh\n${HOOK_MARKER}\n# Runs AstraSync verification on changed files before ${hookType === 'pre-push' ? 'push' : 'commit'}.\n# Installed by: astrasync guard install-hooks\n# To remove: delete this file or run astrasync guard uninstall-hooks\n\nnpx astrasync guard check --trigger=${hookType} \"$@\"\nexit $?\n`;\n}\n\n// -----------------------------------------------------------------------\n// Hook installer\n// -----------------------------------------------------------------------\n\n/**\n * Install git hooks into the repository.\n * Returns the paths of installed hooks.\n */\nexport function installGitHooks(\n repoRoot: string,\n config: Partial<GitTriggerConfig> = {},\n): { installed: string[]; skipped: string[] } {\n const merged = { ...DEFAULT_CONFIG, ...config };\n const hooksDir = join(repoRoot, '.git', 'hooks');\n const installed: string[] = [];\n const skipped: string[] = [];\n\n if (!existsSync(join(repoRoot, '.git'))) {\n throw new Error(`Not a git repository: ${repoRoot}`);\n }\n\n if (!existsSync(hooksDir)) {\n mkdirSync(hooksDir, { recursive: true });\n }\n\n for (const hookType of merged.hooks) {\n const hookPath = join(hooksDir, hookType);\n\n // Don't overwrite non-AstraSync hooks\n if (existsSync(hookPath)) {\n const content = readFileSync(hookPath, 'utf-8');\n if (!content.includes(HOOK_MARKER)) {\n skipped.push(hookPath);\n continue;\n }\n }\n\n writeFileSync(hookPath, makeHookScript(hookType), 'utf-8');\n chmodSync(hookPath, 0o755);\n installed.push(hookPath);\n }\n\n return { installed, skipped };\n}\n\n/**\n * Remove AstraSync-managed git hooks.\n */\nexport function uninstallGitHooks(\n repoRoot: string,\n hooks: ('pre-push' | 'pre-commit')[] = ['pre-push'],\n): string[] {\n const hooksDir = join(repoRoot, '.git', 'hooks');\n const removed: string[] = [];\n\n for (const hookType of hooks) {\n const hookPath = join(hooksDir, hookType);\n if (existsSync(hookPath)) {\n const content = readFileSync(hookPath, 'utf-8');\n if (content.includes(HOOK_MARKER)) {\n unlinkSync(hookPath);\n removed.push(hookPath);\n }\n }\n }\n\n return removed;\n}\n\n// -----------------------------------------------------------------------\n// Evaluation logic\n// -----------------------------------------------------------------------\n\n/**\n * Get list of changed files for the current trigger context.\n */\nexport function getChangedFiles(trigger: 'pre-push' | 'pre-commit', repoRoot: string): string[] {\n try {\n let output: string;\n if (trigger === 'pre-push') {\n // Files changed between HEAD and remote tracking branch\n output = execSync('git diff --name-only @{push}.. 2>/dev/null || git diff --name-only HEAD~1', {\n cwd: repoRoot,\n encoding: 'utf-8',\n });\n } else {\n // Files staged for commit\n output = execSync('git diff --cached --name-only', {\n cwd: repoRoot,\n encoding: 'utf-8',\n });\n }\n return output.split('\\n').filter(f => f.trim().length > 0);\n } catch {\n return [];\n }\n}\n\n/**\n * Map a file change to a PDLSS context for evaluation.\n */\nexport function fileChangeToPDLSSContext(filePath: string): PDLSSContext {\n const purpose = mapToolToPurpose('file_write');\n const target = extractTarget('file_write', { path: filePath });\n\n return {\n purpose,\n action: 'file_write',\n target,\n dataAccess: ['write'],\n resourceType: 'file',\n };\n}\n\n/**\n * Evaluate changed files against the gateway policy.\n * Returns a summary of allow/deny/review decisions.\n */\nexport async function evaluateChangedFiles(\n gateway: AstraSyncGateway,\n files: string[],\n config: Partial<GitTriggerConfig> = {},\n): Promise<GitCheckResult> {\n const merged = { ...DEFAULT_CONFIG, ...config };\n const results: { file: string; decision: VerificationDecision }[] = [];\n let blocked = 0;\n let warned = 0;\n let allowed = 0;\n\n for (const file of files) {\n const context = fileChangeToPDLSSContext(file);\n const decision = await gateway.evaluate(context);\n\n results.push({ file, decision });\n\n if (decision.recommendation === 'DENY') {\n blocked++;\n } else if (decision.recommendation === 'MANUAL_REVIEW') {\n warned++;\n } else {\n allowed++;\n }\n }\n\n const shouldBlock = merged.enforcement === 'block' && (blocked > 0);\n\n return {\n allowed,\n blocked,\n warned,\n total: files.length,\n shouldBlock,\n enforcement: merged.enforcement,\n results,\n blockMessage: shouldBlock\n ? (merged.blockMessage || `AstraSync blocked push: ${blocked} file(s) denied by policy`)\n : undefined,\n };\n}\n\nexport interface GitCheckResult {\n allowed: number;\n blocked: number;\n warned: number;\n total: number;\n shouldBlock: boolean;\n enforcement: 'block' | 'warn';\n results: { file: string; decision: VerificationDecision }[];\n blockMessage?: string;\n}\n\n/**\n * Check if the current branch is protected.\n */\nexport function isProtectedBranch(config: Partial<GitTriggerConfig> = {}): boolean {\n if (!config.protectedBranches || config.protectedBranches.length === 0) {\n return true; // All branches protected by default\n }\n\n try {\n const branch = execSync('git rev-parse --abbrev-ref HEAD', { encoding: 'utf-8' }).trim();\n return config.protectedBranches.some(pattern => {\n if (pattern === branch) return true;\n if (pattern.endsWith('*') && branch.startsWith(pattern.slice(0, -1))) return true;\n return false;\n });\n } catch {\n return true; // Can't determine branch, assume protected\n }\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,12 +1,5 @@
1
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
- }) : x)(function(x) {
4
- if (typeof require !== "undefined") return require.apply(this, arguments);
5
- throw Error('Dynamic require of "' + x + '" is not supported');
6
- });
7
-
8
1
  // src/git-trigger/git-hooks.ts
9
- import { existsSync, mkdirSync, writeFileSync, readFileSync, chmodSync } from "fs";
2
+ import { existsSync, mkdirSync, writeFileSync, readFileSync, chmodSync, unlinkSync } from "fs";
10
3
  import { join } from "path";
11
4
  import { execSync } from "child_process";
12
5
 
@@ -127,7 +120,6 @@ function uninstallGitHooks(repoRoot, hooks = ["pre-push"]) {
127
120
  if (existsSync(hookPath)) {
128
121
  const content = readFileSync(hookPath, "utf-8");
129
122
  if (content.includes(HOOK_MARKER)) {
130
- const { unlinkSync } = __require("fs");
131
123
  unlinkSync(hookPath);
132
124
  removed.push(hookPath);
133
125
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/git-trigger/git-hooks.ts","../../src/adapter-interface/purpose-mapping.ts"],"sourcesContent":["/**\n * Git Trigger — Enterprise git push / PR verification\n *\n * Installs git hooks that evaluate changed files against the PDLSS policy\n * before allowing pushes to remote repositories.\n *\n * Two enforcement modes (admin-configurable):\n * - block: non-zero exit prevents the push\n * - warn: push proceeds but warning is printed\n */\n\nimport { existsSync, mkdirSync, writeFileSync, readFileSync, chmodSync } from 'fs';\nimport { join } from 'path';\nimport { execSync } from 'child_process';\nimport type { AstraSyncGateway } from '../gateway/gateway';\nimport { mapToolToPurpose, extractTarget, extractNetworkDomains } from '../adapter-interface/purpose-mapping';\nimport type { PDLSSContext, VerificationDecision } from '../gateway/types';\n\n// -----------------------------------------------------------------------\n// Configuration\n// -----------------------------------------------------------------------\n\nexport interface GitTriggerConfig {\n /** Block the push or just warn (default: 'warn') */\n enforcement: 'block' | 'warn';\n /** Which git hooks to install (default: ['pre-push']) */\n hooks: ('pre-push' | 'pre-commit')[];\n /** Only enforce on these branches (default: all) */\n protectedBranches?: string[];\n /** Custom message for blocked pushes */\n blockMessage?: string;\n}\n\nconst DEFAULT_CONFIG: GitTriggerConfig = {\n enforcement: 'warn',\n hooks: ['pre-push'],\n};\n\n// -----------------------------------------------------------------------\n// Hook script content\n// -----------------------------------------------------------------------\n\nconst HOOK_MARKER = '# AstraSync Local Guard — managed hook';\n\nfunction makeHookScript(hookType: 'pre-push' | 'pre-commit'): string {\n return `#!/bin/sh\n${HOOK_MARKER}\n# Runs AstraSync verification on changed files before ${hookType === 'pre-push' ? 'push' : 'commit'}.\n# Installed by: astrasync guard install-hooks\n# To remove: delete this file or run astrasync guard uninstall-hooks\n\nnpx astrasync guard check --trigger=${hookType} \"$@\"\nexit $?\n`;\n}\n\n// -----------------------------------------------------------------------\n// Hook installer\n// -----------------------------------------------------------------------\n\n/**\n * Install git hooks into the repository.\n * Returns the paths of installed hooks.\n */\nexport function installGitHooks(\n repoRoot: string,\n config: Partial<GitTriggerConfig> = {},\n): { installed: string[]; skipped: string[] } {\n const merged = { ...DEFAULT_CONFIG, ...config };\n const hooksDir = join(repoRoot, '.git', 'hooks');\n const installed: string[] = [];\n const skipped: string[] = [];\n\n if (!existsSync(join(repoRoot, '.git'))) {\n throw new Error(`Not a git repository: ${repoRoot}`);\n }\n\n if (!existsSync(hooksDir)) {\n mkdirSync(hooksDir, { recursive: true });\n }\n\n for (const hookType of merged.hooks) {\n const hookPath = join(hooksDir, hookType);\n\n // Don't overwrite non-AstraSync hooks\n if (existsSync(hookPath)) {\n const content = readFileSync(hookPath, 'utf-8');\n if (!content.includes(HOOK_MARKER)) {\n skipped.push(hookPath);\n continue;\n }\n }\n\n writeFileSync(hookPath, makeHookScript(hookType), 'utf-8');\n chmodSync(hookPath, 0o755);\n installed.push(hookPath);\n }\n\n return { installed, skipped };\n}\n\n/**\n * Remove AstraSync-managed git hooks.\n */\nexport function uninstallGitHooks(\n repoRoot: string,\n hooks: ('pre-push' | 'pre-commit')[] = ['pre-push'],\n): string[] {\n const hooksDir = join(repoRoot, '.git', 'hooks');\n const removed: string[] = [];\n\n for (const hookType of hooks) {\n const hookPath = join(hooksDir, hookType);\n if (existsSync(hookPath)) {\n const content = readFileSync(hookPath, 'utf-8');\n if (content.includes(HOOK_MARKER)) {\n const { unlinkSync } = require('fs');\n unlinkSync(hookPath);\n removed.push(hookPath);\n }\n }\n }\n\n return removed;\n}\n\n// -----------------------------------------------------------------------\n// Evaluation logic\n// -----------------------------------------------------------------------\n\n/**\n * Get list of changed files for the current trigger context.\n */\nexport function getChangedFiles(trigger: 'pre-push' | 'pre-commit', repoRoot: string): string[] {\n try {\n let output: string;\n if (trigger === 'pre-push') {\n // Files changed between HEAD and remote tracking branch\n output = execSync('git diff --name-only @{push}.. 2>/dev/null || git diff --name-only HEAD~1', {\n cwd: repoRoot,\n encoding: 'utf-8',\n });\n } else {\n // Files staged for commit\n output = execSync('git diff --cached --name-only', {\n cwd: repoRoot,\n encoding: 'utf-8',\n });\n }\n return output.split('\\n').filter(f => f.trim().length > 0);\n } catch {\n return [];\n }\n}\n\n/**\n * Map a file change to a PDLSS context for evaluation.\n */\nexport function fileChangeToPDLSSContext(filePath: string): PDLSSContext {\n const purpose = mapToolToPurpose('file_write');\n const target = extractTarget('file_write', { path: filePath });\n\n return {\n purpose,\n action: 'file_write',\n target,\n dataAccess: ['write'],\n resourceType: 'file',\n };\n}\n\n/**\n * Evaluate changed files against the gateway policy.\n * Returns a summary of allow/deny/review decisions.\n */\nexport async function evaluateChangedFiles(\n gateway: AstraSyncGateway,\n files: string[],\n config: Partial<GitTriggerConfig> = {},\n): Promise<GitCheckResult> {\n const merged = { ...DEFAULT_CONFIG, ...config };\n const results: { file: string; decision: VerificationDecision }[] = [];\n let blocked = 0;\n let warned = 0;\n let allowed = 0;\n\n for (const file of files) {\n const context = fileChangeToPDLSSContext(file);\n const decision = await gateway.evaluate(context);\n\n results.push({ file, decision });\n\n if (decision.recommendation === 'DENY') {\n blocked++;\n } else if (decision.recommendation === 'MANUAL_REVIEW') {\n warned++;\n } else {\n allowed++;\n }\n }\n\n const shouldBlock = merged.enforcement === 'block' && (blocked > 0);\n\n return {\n allowed,\n blocked,\n warned,\n total: files.length,\n shouldBlock,\n enforcement: merged.enforcement,\n results,\n blockMessage: shouldBlock\n ? (merged.blockMessage || `AstraSync blocked push: ${blocked} file(s) denied by policy`)\n : undefined,\n };\n}\n\nexport interface GitCheckResult {\n allowed: number;\n blocked: number;\n warned: number;\n total: number;\n shouldBlock: boolean;\n enforcement: 'block' | 'warn';\n results: { file: string; decision: VerificationDecision }[];\n blockMessage?: string;\n}\n\n/**\n * Check if the current branch is protected.\n */\nexport function isProtectedBranch(config: Partial<GitTriggerConfig> = {}): boolean {\n if (!config.protectedBranches || config.protectedBranches.length === 0) {\n return true; // All branches protected by default\n }\n\n try {\n const branch = execSync('git rev-parse --abbrev-ref HEAD', { encoding: 'utf-8' }).trim();\n return config.protectedBranches.some(pattern => {\n if (pattern === branch) return true;\n if (pattern.endsWith('*') && branch.startsWith(pattern.slice(0, -1))) return true;\n return false;\n });\n } catch {\n return true; // Can't determine branch, assume protected\n }\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/git-trigger/git-hooks.ts","../../src/adapter-interface/purpose-mapping.ts"],"sourcesContent":["/**\n * Git Trigger — Enterprise git push / PR verification\n *\n * Installs git hooks that evaluate changed files against the PDLSS policy\n * before allowing pushes to remote repositories.\n *\n * Two enforcement modes (admin-configurable):\n * - block: non-zero exit prevents the push\n * - warn: push proceeds but warning is printed\n */\n\nimport { existsSync, mkdirSync, writeFileSync, readFileSync, chmodSync, unlinkSync } from 'fs';\nimport { join } from 'path';\nimport { execSync } from 'child_process';\nimport type { AstraSyncGateway } from '../gateway/gateway';\nimport { mapToolToPurpose, extractTarget } from '../adapter-interface/purpose-mapping';\nimport type { PDLSSContext, VerificationDecision } from '../gateway/types';\n\n// -----------------------------------------------------------------------\n// Configuration\n// -----------------------------------------------------------------------\n\nexport interface GitTriggerConfig {\n /** Block the push or just warn (default: 'warn') */\n enforcement: 'block' | 'warn';\n /** Which git hooks to install (default: ['pre-push']) */\n hooks: ('pre-push' | 'pre-commit')[];\n /** Only enforce on these branches (default: all) */\n protectedBranches?: string[];\n /** Custom message for blocked pushes */\n blockMessage?: string;\n}\n\nconst DEFAULT_CONFIG: GitTriggerConfig = {\n enforcement: 'warn',\n hooks: ['pre-push'],\n};\n\n// -----------------------------------------------------------------------\n// Hook script content\n// -----------------------------------------------------------------------\n\nconst HOOK_MARKER = '# AstraSync Local Guard — managed hook';\n\nfunction makeHookScript(hookType: 'pre-push' | 'pre-commit'): string {\n return `#!/bin/sh\n${HOOK_MARKER}\n# Runs AstraSync verification on changed files before ${hookType === 'pre-push' ? 'push' : 'commit'}.\n# Installed by: astrasync guard install-hooks\n# To remove: delete this file or run astrasync guard uninstall-hooks\n\nnpx astrasync guard check --trigger=${hookType} \"$@\"\nexit $?\n`;\n}\n\n// -----------------------------------------------------------------------\n// Hook installer\n// -----------------------------------------------------------------------\n\n/**\n * Install git hooks into the repository.\n * Returns the paths of installed hooks.\n */\nexport function installGitHooks(\n repoRoot: string,\n config: Partial<GitTriggerConfig> = {},\n): { installed: string[]; skipped: string[] } {\n const merged = { ...DEFAULT_CONFIG, ...config };\n const hooksDir = join(repoRoot, '.git', 'hooks');\n const installed: string[] = [];\n const skipped: string[] = [];\n\n if (!existsSync(join(repoRoot, '.git'))) {\n throw new Error(`Not a git repository: ${repoRoot}`);\n }\n\n if (!existsSync(hooksDir)) {\n mkdirSync(hooksDir, { recursive: true });\n }\n\n for (const hookType of merged.hooks) {\n const hookPath = join(hooksDir, hookType);\n\n // Don't overwrite non-AstraSync hooks\n if (existsSync(hookPath)) {\n const content = readFileSync(hookPath, 'utf-8');\n if (!content.includes(HOOK_MARKER)) {\n skipped.push(hookPath);\n continue;\n }\n }\n\n writeFileSync(hookPath, makeHookScript(hookType), 'utf-8');\n chmodSync(hookPath, 0o755);\n installed.push(hookPath);\n }\n\n return { installed, skipped };\n}\n\n/**\n * Remove AstraSync-managed git hooks.\n */\nexport function uninstallGitHooks(\n repoRoot: string,\n hooks: ('pre-push' | 'pre-commit')[] = ['pre-push'],\n): string[] {\n const hooksDir = join(repoRoot, '.git', 'hooks');\n const removed: string[] = [];\n\n for (const hookType of hooks) {\n const hookPath = join(hooksDir, hookType);\n if (existsSync(hookPath)) {\n const content = readFileSync(hookPath, 'utf-8');\n if (content.includes(HOOK_MARKER)) {\n unlinkSync(hookPath);\n removed.push(hookPath);\n }\n }\n }\n\n return removed;\n}\n\n// -----------------------------------------------------------------------\n// Evaluation logic\n// -----------------------------------------------------------------------\n\n/**\n * Get list of changed files for the current trigger context.\n */\nexport function getChangedFiles(trigger: 'pre-push' | 'pre-commit', repoRoot: string): string[] {\n try {\n let output: string;\n if (trigger === 'pre-push') {\n // Files changed between HEAD and remote tracking branch\n output = execSync('git diff --name-only @{push}.. 2>/dev/null || git diff --name-only HEAD~1', {\n cwd: repoRoot,\n encoding: 'utf-8',\n });\n } else {\n // Files staged for commit\n output = execSync('git diff --cached --name-only', {\n cwd: repoRoot,\n encoding: 'utf-8',\n });\n }\n return output.split('\\n').filter(f => f.trim().length > 0);\n } catch {\n return [];\n }\n}\n\n/**\n * Map a file change to a PDLSS context for evaluation.\n */\nexport function fileChangeToPDLSSContext(filePath: string): PDLSSContext {\n const purpose = mapToolToPurpose('file_write');\n const target = extractTarget('file_write', { path: filePath });\n\n return {\n purpose,\n action: 'file_write',\n target,\n dataAccess: ['write'],\n resourceType: 'file',\n };\n}\n\n/**\n * Evaluate changed files against the gateway policy.\n * Returns a summary of allow/deny/review decisions.\n */\nexport async function evaluateChangedFiles(\n gateway: AstraSyncGateway,\n files: string[],\n config: Partial<GitTriggerConfig> = {},\n): Promise<GitCheckResult> {\n const merged = { ...DEFAULT_CONFIG, ...config };\n const results: { file: string; decision: VerificationDecision }[] = [];\n let blocked = 0;\n let warned = 0;\n let allowed = 0;\n\n for (const file of files) {\n const context = fileChangeToPDLSSContext(file);\n const decision = await gateway.evaluate(context);\n\n results.push({ file, decision });\n\n if (decision.recommendation === 'DENY') {\n blocked++;\n } else if (decision.recommendation === 'MANUAL_REVIEW') {\n warned++;\n } else {\n allowed++;\n }\n }\n\n const shouldBlock = merged.enforcement === 'block' && (blocked > 0);\n\n return {\n allowed,\n blocked,\n warned,\n total: files.length,\n shouldBlock,\n enforcement: merged.enforcement,\n results,\n blockMessage: shouldBlock\n ? (merged.blockMessage || `AstraSync blocked push: ${blocked} file(s) denied by policy`)\n : undefined,\n };\n}\n\nexport interface GitCheckResult {\n allowed: number;\n blocked: number;\n warned: number;\n total: number;\n shouldBlock: boolean;\n enforcement: 'block' | 'warn';\n results: { file: string; decision: VerificationDecision }[];\n blockMessage?: string;\n}\n\n/**\n * Check if the current branch is protected.\n */\nexport function isProtectedBranch(config: Partial<GitTriggerConfig> = {}): boolean {\n if (!config.protectedBranches || config.protectedBranches.length === 0) {\n return true; // All branches protected by default\n }\n\n try {\n const branch = execSync('git rev-parse --abbrev-ref HEAD', { encoding: 'utf-8' }).trim();\n return config.protectedBranches.some(pattern => {\n if (pattern === branch) return true;\n if (pattern.endsWith('*') && branch.startsWith(pattern.slice(0, -1))) return true;\n return false;\n });\n } catch {\n return true; // Can't determine branch, assume protected\n }\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,4 +1,4 @@
1
- import { c as AstraSyncCredentials, g as ProtocolTransport } from './types-Bf8pML07.mjs';
1
+ import { A as AstraSyncCredentials, g as ProtocolTransport } from './types-CxQwJKbd.mjs';
2
2
 
3
3
  /**
4
4
  * HTTP Transport Adapter
@@ -1,4 +1,4 @@
1
- import { c as AstraSyncCredentials, g as ProtocolTransport, G as GatewayConfig } from './types-Bf8pML07.mjs';
1
+ import { A as AstraSyncCredentials, g as ProtocolTransport, G as GatewayConfig } from './types-CxQwJKbd.mjs';
2
2
 
3
3
  /**
4
4
  * AgentClient — Credential Presentation
@@ -1,4 +1,4 @@
1
- import { c as AstraSyncCredentials, g as ProtocolTransport, G as GatewayConfig } from './types-Bf8pML07.js';
1
+ import { A as AstraSyncCredentials, g as ProtocolTransport, G as GatewayConfig } from './types-CxQwJKbd.js';
2
2
 
3
3
  /**
4
4
  * AgentClient — Credential Presentation
@@ -1,4 +1,4 @@
1
- import { c as AstraSyncCredentials, g as ProtocolTransport } from './types-Bf8pML07.js';
1
+ import { A as AstraSyncCredentials, g as ProtocolTransport } from './types-CxQwJKbd.js';
2
2
 
3
3
  /**
4
4
  * HTTP Transport Adapter
package/dist/index.d.mts CHANGED
@@ -1,10 +1,10 @@
1
- import { A as AgentCredentials, G as GatewayConfig, a as AccessLevel, V as VerificationRequest, b as VerificationResult } from './types-Bf8pML07.mjs';
2
- export { c as AstraSyncCredentials, C as CommerceShieldProps, d as CounterpartyType, E as EnhancedVerificationResult, e as ExpressMiddlewareOptions, f as GuidanceInfo, N as NextJsMiddlewareOptions, P as PDLSSInfo, g as ProtocolTransport, R as RouteAccessConfig, h as RuntimeChallengeResult, S as SDKOptions, T as TokenGuidance, i as TrustLevel, j as VerifiedAgent, k as VerifiedDeveloper, l as VerifiedOrganization } from './types-Bf8pML07.mjs';
3
- export { A as ACCESS_LEVEL_DESCRIPTIONS, a as ACCESS_LEVEL_HIERARCHY, b as AccessCapabilities, D as DEFAULT_TRUST_THRESHOLDS, T as TRUST_LEVEL_RANGES, d as determineAccessLevel, g as getAccessLevelForScore, c as getCapabilities, e as getTrustLevel, h as hasMinimumAccess, s as sdk } from './sdk-DAJahT3p.mjs';
4
- export { e as express } from './express-Cp4eg77F.mjs';
5
- export { n as nextjs } from './nextjs-_C_FcJY5.mjs';
6
- export { i as transport } from './index-CoLebmwv.mjs';
7
- export { A as AgentClient, C as ChallengeHandler, i as agent, r as recordDecision } from './index-BhTbGU-o.mjs';
1
+ import { b as AgentCredentials, G as GatewayConfig, a as AccessLevel, c as VerificationRequest, V as VerificationResult } from './types-CxQwJKbd.mjs';
2
+ export { A as AstraSyncCredentials, d as CommerceShieldProps, C as CounterpartyType, e as EnhancedVerificationResult, E as ExpressMiddlewareOptions, f as GuidanceInfo, N as NextJsMiddlewareOptions, P as PDLSSInfo, g as ProtocolTransport, R as RouteAccessConfig, h as RuntimeChallengeResult, S as SDKOptions, T as TokenGuidance, i as TrustLevel, j as VerifiedAgent, k as VerifiedDeveloper, l as VerifiedOrganization } from './types-CxQwJKbd.mjs';
3
+ export { A as ACCESS_LEVEL_DESCRIPTIONS, a as ACCESS_LEVEL_HIERARCHY, b as AccessCapabilities, D as DEFAULT_TRUST_THRESHOLDS, T as TRUST_LEVEL_RANGES, d as determineAccessLevel, g as getAccessLevelForScore, c as getCapabilities, e as getTrustLevel, h as hasMinimumAccess, s as sdk } from './sdk-BhvuJSrH.mjs';
4
+ export { e as express } from './express-CtwDIZyF.mjs';
5
+ export { n as nextjs } from './nextjs-BQyMCSx_.mjs';
6
+ export { i as transport } from './index-B1ThcGZl.mjs';
7
+ export { A as AgentClient, C as ChallengeHandler, i as agent, r as recordDecision } from './index-BY8yQ8N8.mjs';
8
8
  import 'express';
9
9
  import 'next/server';
10
10
 
package/dist/index.d.ts CHANGED
@@ -1,10 +1,10 @@
1
- import { A as AgentCredentials, G as GatewayConfig, a as AccessLevel, V as VerificationRequest, b as VerificationResult } from './types-Bf8pML07.js';
2
- export { c as AstraSyncCredentials, C as CommerceShieldProps, d as CounterpartyType, E as EnhancedVerificationResult, e as ExpressMiddlewareOptions, f as GuidanceInfo, N as NextJsMiddlewareOptions, P as PDLSSInfo, g as ProtocolTransport, R as RouteAccessConfig, h as RuntimeChallengeResult, S as SDKOptions, T as TokenGuidance, i as TrustLevel, j as VerifiedAgent, k as VerifiedDeveloper, l as VerifiedOrganization } from './types-Bf8pML07.js';
3
- export { A as ACCESS_LEVEL_DESCRIPTIONS, a as ACCESS_LEVEL_HIERARCHY, b as AccessCapabilities, D as DEFAULT_TRUST_THRESHOLDS, T as TRUST_LEVEL_RANGES, d as determineAccessLevel, g as getAccessLevelForScore, c as getCapabilities, e as getTrustLevel, h as hasMinimumAccess, s as sdk } from './sdk-CMPDFUjo.js';
4
- export { e as express } from './express-DIEyq1Tz.js';
5
- export { n as nextjs } from './nextjs-Cag7libc.js';
6
- export { i as transport } from './index-CNkmHmpi.js';
7
- export { A as AgentClient, C as ChallengeHandler, i as agent, r as recordDecision } from './index-Bhfxq9xI.js';
1
+ import { b as AgentCredentials, G as GatewayConfig, a as AccessLevel, c as VerificationRequest, V as VerificationResult } from './types-CxQwJKbd.js';
2
+ export { A as AstraSyncCredentials, d as CommerceShieldProps, C as CounterpartyType, e as EnhancedVerificationResult, E as ExpressMiddlewareOptions, f as GuidanceInfo, N as NextJsMiddlewareOptions, P as PDLSSInfo, g as ProtocolTransport, R as RouteAccessConfig, h as RuntimeChallengeResult, S as SDKOptions, T as TokenGuidance, i as TrustLevel, j as VerifiedAgent, k as VerifiedDeveloper, l as VerifiedOrganization } from './types-CxQwJKbd.js';
3
+ export { A as ACCESS_LEVEL_DESCRIPTIONS, a as ACCESS_LEVEL_HIERARCHY, b as AccessCapabilities, D as DEFAULT_TRUST_THRESHOLDS, T as TRUST_LEVEL_RANGES, d as determineAccessLevel, g as getAccessLevelForScore, c as getCapabilities, e as getTrustLevel, h as hasMinimumAccess, s as sdk } from './sdk-BlyVSC_S.js';
4
+ export { e as express } from './express-Bcl-uBUE.js';
5
+ export { n as nextjs } from './nextjs-CEldnIJ9.js';
6
+ export { i as transport } from './index-DnoXfdFd.js';
7
+ export { A as AgentClient, C as ChallengeHandler, i as agent, r as recordDecision } from './index-CtYSYwn3.js';
8
8
  import 'express';
9
9
  import 'next/server';
10
10
 
package/dist/index.js CHANGED
@@ -280,6 +280,7 @@ async function callVerifyAccessAPI(config, request) {
280
280
  if (requestData.subAgentDepth !== void 0) body.subAgentDepth = requestData.subAgentDepth;
281
281
  if (requestData.enableRuntimeChallenge) body.enableRuntimeChallenge = requestData.enableRuntimeChallenge;
282
282
  if (requestData.createSession) body.createSession = requestData.createSession;
283
+ if (requestData.durationRequired) body.durationRequired = requestData.durationRequired;
283
284
  if (requestData.counterpartyType) body.counterpartyType = requestData.counterpartyType;
284
285
  if (requestData.counterpartyUrl) body.counterpartyUrl = requestData.counterpartyUrl;
285
286
  if (requestData.runtimeChallengeOptions) body.runtimeChallengeOptions = requestData.runtimeChallengeOptions;
@@ -453,6 +454,24 @@ async function recordDecision(config, sessionId, decision, reason) {
453
454
  }).catch(() => {
454
455
  });
455
456
  }
457
+ async function reportUnregisteredAttempt(config, data) {
458
+ const apiBaseUrl = config.apiBaseUrl || DEFAULT_CONFIG.apiBaseUrl;
459
+ await fetch(`${apiBaseUrl}/verification-activity/unregistered-attempt`, {
460
+ method: "POST",
461
+ headers: { "Content-Type": "application/json" },
462
+ body: JSON.stringify(data)
463
+ }).catch(() => {
464
+ });
465
+ }
466
+ async function reportCounterpartyPreCheckFailure(config, data) {
467
+ const apiBaseUrl = config.apiBaseUrl || DEFAULT_CONFIG.apiBaseUrl;
468
+ await fetch(`${apiBaseUrl}/verification-activity/counterparty-pre-check-failure`, {
469
+ method: "POST",
470
+ headers: { "Content-Type": "application/json" },
471
+ body: JSON.stringify(data)
472
+ }).catch(() => {
473
+ });
474
+ }
456
475
  async function quickVerify(config, credentials) {
457
476
  const result = await verify(config, {
458
477
  credentials,
@@ -534,6 +553,54 @@ function extractHttpCredentials(headers) {
534
553
  return credentials;
535
554
  }
536
555
 
556
+ // src/pdlss-pre-check.ts
557
+ function performCounterpartyPreCheck(routeConfig, astraCreds, purpose) {
558
+ const failures = [];
559
+ if (routeConfig.allowedPurposes && routeConfig.allowedPurposes.length > 0 && purpose) {
560
+ if (!routeConfig.allowedPurposes.includes(purpose)) {
561
+ failures.push({
562
+ field: "purpose",
563
+ requested: purpose,
564
+ limit: routeConfig.allowedPurposes,
565
+ message: `Purpose "${purpose}" is not in the allowed list: [${routeConfig.allowedPurposes.join(", ")}]`
566
+ });
567
+ }
568
+ }
569
+ if (routeConfig.requiredPurposes && routeConfig.requiredPurposes.length > 0 && purpose) {
570
+ if (!routeConfig.requiredPurposes.includes(purpose)) {
571
+ failures.push({
572
+ field: "purpose",
573
+ requested: purpose,
574
+ limit: routeConfig.requiredPurposes,
575
+ message: `Purpose "${purpose}" is not in the required list: [${routeConfig.requiredPurposes.join(", ")}]`
576
+ });
577
+ }
578
+ }
579
+ if (routeConfig.maxDuration && astraCreds?.pdlss?.duration?.maxSessionDuration) {
580
+ const requested = astraCreds.pdlss.duration.maxSessionDuration;
581
+ if (requested > routeConfig.maxDuration) {
582
+ failures.push({
583
+ field: "duration",
584
+ requested,
585
+ limit: routeConfig.maxDuration,
586
+ message: `Requested duration ${requested}s exceeds maximum ${routeConfig.maxDuration}s`
587
+ });
588
+ }
589
+ }
590
+ if (routeConfig.allowedJurisdictions && routeConfig.allowedJurisdictions.length > 0 && astraCreds?.pdlss?.scope?.jurisdiction) {
591
+ const requested = astraCreds.pdlss.scope.jurisdiction;
592
+ if (!routeConfig.allowedJurisdictions.includes(requested)) {
593
+ failures.push({
594
+ field: "jurisdiction",
595
+ requested,
596
+ limit: routeConfig.allowedJurisdictions,
597
+ message: `Jurisdiction "${requested}" is not in the allowed list: [${routeConfig.allowedJurisdictions.join(", ")}]`
598
+ });
599
+ }
600
+ }
601
+ return failures;
602
+ }
603
+
537
604
  // src/adapters/express.ts
538
605
  function defaultExtractCredentials(req) {
539
606
  return extractCredentials(
@@ -545,6 +612,12 @@ function extractAstraSyncCredentials(req) {
545
612
  return extractHttpCredentials(req.headers);
546
613
  }
547
614
  function defaultExtractPurpose(req) {
615
+ const astraPurpose = req.headers["x-astra-purpose"];
616
+ if (astraPurpose) {
617
+ const value = Array.isArray(astraPurpose) ? astraPurpose[0] : astraPurpose;
618
+ const category = value.split(":")[0];
619
+ return category;
620
+ }
548
621
  const purposeHeader = req.headers["x-purpose"] || req.headers["X-Purpose"];
549
622
  if (purposeHeader) {
550
623
  return Array.isArray(purposeHeader) ? purposeHeader[0] : purposeHeader;
@@ -554,14 +627,14 @@ function defaultExtractPurpose(req) {
554
627
  }
555
628
  switch (req.method) {
556
629
  case "GET":
557
- return "read";
630
+ return "read_data";
558
631
  case "POST":
559
- return "create";
632
+ return "write_data";
560
633
  case "PUT":
561
634
  case "PATCH":
562
- return "update";
635
+ return "write_data";
563
636
  case "DELETE":
564
- return "delete";
637
+ return "delete_data";
565
638
  default:
566
639
  return "general";
567
640
  }
@@ -598,6 +671,7 @@ function createMiddleware(options) {
598
671
  skipPaths = [],
599
672
  onDenied = defaultOnDenied,
600
673
  recordDecisions,
674
+ enableRuntimeChallenge = true,
601
675
  ...config
602
676
  } = options;
603
677
  return async (req, res, next) => {
@@ -615,6 +689,16 @@ function createMiddleware(options) {
615
689
  }
616
690
  const credentials = customExtractCredentials ? customExtractCredentials(req) : defaultExtractCredentials(req);
617
691
  if (!hasCredentials(credentials) && routeConfig.minAccessLevel !== "guidance") {
692
+ const counterpartyUrl2 = config.counterpartyUrl || `${req.protocol}://${req.get("host")}`;
693
+ reportUnregisteredAttempt(config, {
694
+ counterpartyUrl: counterpartyUrl2,
695
+ counterpartyType: config.counterpartyType || "api",
696
+ sourceIp: req.ip,
697
+ userAgent: req.headers["user-agent"],
698
+ requestPath: req.path,
699
+ requestMethod: req.method
700
+ }).catch(() => {
701
+ });
618
702
  const result2 = {
619
703
  verified: false,
620
704
  accessLevel: "none",
@@ -631,7 +715,34 @@ function createMiddleware(options) {
631
715
  return;
632
716
  }
633
717
  const purpose = customExtractPurpose ? customExtractPurpose(req) : defaultExtractPurpose(req);
718
+ const astraCreds = extractAstraSyncCredentials(req);
634
719
  const counterpartyUrl = config.counterpartyUrl || `${req.protocol}://${req.get("host")}`;
720
+ const preCheckFailures = performCounterpartyPreCheck(routeConfig, astraCreds, purpose);
721
+ if (preCheckFailures.length > 0) {
722
+ const result2 = {
723
+ verified: false,
724
+ accessLevel: "none",
725
+ denialReasons: preCheckFailures.map((f) => f.message),
726
+ guidance: {
727
+ message: "Request exceeds counterparty-defined PDLSS limits.",
728
+ registrationUrl: `${config.apiBaseUrl?.replace("/api", "")}/register`,
729
+ documentationUrl: `${config.apiBaseUrl?.replace("/api", "")}/docs/pdlss`
730
+ },
731
+ verifiedAt: /* @__PURE__ */ new Date()
732
+ };
733
+ req.agentVerification = result2;
734
+ reportCounterpartyPreCheckFailure(config, {
735
+ agentId: astraCreds?.agentId || credentials.astraId || "unknown",
736
+ counterpartyUrl,
737
+ counterpartyType: config.counterpartyType || "api",
738
+ failures: preCheckFailures,
739
+ requestPath: req.path,
740
+ requestMethod: req.method
741
+ }).catch(() => {
742
+ });
743
+ onDenied(result2, req, res);
744
+ return;
745
+ }
635
746
  const shouldRecordDecisions = recordDecisions !== false;
636
747
  const result = await verify(config, {
637
748
  credentials,
@@ -642,7 +753,9 @@ function createMiddleware(options) {
642
753
  userAgent: req.headers["user-agent"],
643
754
  createSession: shouldRecordDecisions,
644
755
  counterpartyUrl,
645
- counterpartyType: config.counterpartyType || "api"
756
+ counterpartyType: config.counterpartyType || "api",
757
+ enableRuntimeChallenge,
758
+ durationRequired: astraCreds?.pdlss?.duration?.maxSessionDuration
646
759
  });
647
760
  req.agentVerification = result;
648
761
  const sessionId = result.sessionId;
@@ -737,17 +850,32 @@ function findRouteConfig2(routes, path, method) {
737
850
  return methodMatches && pathMatches;
738
851
  });
739
852
  }
740
- function inferPurpose(method) {
741
- switch (method.toUpperCase()) {
853
+ function extractAstraSyncCredentialsFromNextRequest(request) {
854
+ const headers = {};
855
+ request.headers.forEach((value, key) => {
856
+ headers[key] = value;
857
+ });
858
+ return extractHttpCredentials(headers);
859
+ }
860
+ function extractPurpose(request) {
861
+ const astraPurpose = request.headers.get("x-astra-purpose");
862
+ if (astraPurpose) {
863
+ return astraPurpose.split(":")[0];
864
+ }
865
+ const purposeHeader = request.headers.get("x-purpose");
866
+ if (purposeHeader) {
867
+ return purposeHeader;
868
+ }
869
+ switch (request.method.toUpperCase()) {
742
870
  case "GET":
743
- return "read";
871
+ return "read_data";
744
872
  case "POST":
745
- return "create";
873
+ return "write_data";
746
874
  case "PUT":
747
875
  case "PATCH":
748
- return "update";
876
+ return "write_data";
749
877
  case "DELETE":
750
- return "delete";
878
+ return "delete_data";
751
879
  default:
752
880
  return "general";
753
881
  }
@@ -899,7 +1027,7 @@ function generateCommerceShieldHtml(result, options) {
899
1027
  `.trim();
900
1028
  }
901
1029
  function createMiddleware2(options) {
902
- const { routes = [], skipPaths = [], showCommerceShield = true, ...config } = options;
1030
+ const { routes = [], skipPaths = [], showCommerceShield = true, enableRuntimeChallenge = true, ...config } = options;
903
1031
  return async function middleware(request) {
904
1032
  const { NextResponse } = await import("next/server");
905
1033
  const pathname = request.nextUrl.pathname;
@@ -916,6 +1044,16 @@ function createMiddleware2(options) {
916
1044
  }
917
1045
  const credentials = extractCredentialsFromNextRequest(request);
918
1046
  if (!hasCredentials(credentials) && routeConfig.minAccessLevel !== "guidance") {
1047
+ const counterpartyUrl2 = config.counterpartyUrl || request.nextUrl.origin;
1048
+ reportUnregisteredAttempt(config, {
1049
+ counterpartyUrl: counterpartyUrl2,
1050
+ counterpartyType: config.counterpartyType || "website",
1051
+ sourceIp: request.headers.get("x-forwarded-for") || request.headers.get("x-real-ip") || void 0,
1052
+ userAgent: request.headers.get("user-agent") || void 0,
1053
+ requestPath: pathname,
1054
+ requestMethod: request.method
1055
+ }).catch(() => {
1056
+ });
919
1057
  const result2 = {
920
1058
  verified: false,
921
1059
  accessLevel: "none",
@@ -953,7 +1091,54 @@ function createMiddleware2(options) {
953
1091
  return NextResponse.redirect(new URL(registerUrl, request.url));
954
1092
  }
955
1093
  const counterpartyUrl = config.counterpartyUrl || request.nextUrl.origin;
956
- const purpose = request.headers.get("x-purpose") || inferPurpose(request.method);
1094
+ const purpose = extractPurpose(request);
1095
+ const astraCreds = extractAstraSyncCredentialsFromNextRequest(request);
1096
+ const preCheckFailures = performCounterpartyPreCheck(routeConfig, astraCreds, purpose);
1097
+ if (preCheckFailures.length > 0) {
1098
+ const preCheckResult = {
1099
+ verified: false,
1100
+ accessLevel: "none",
1101
+ denialReasons: preCheckFailures.map((f) => f.message),
1102
+ guidance: {
1103
+ message: "Request exceeds counterparty-defined PDLSS limits.",
1104
+ registrationUrl: `${config.apiBaseUrl?.replace("/api", "")}/register`,
1105
+ documentationUrl: `${config.apiBaseUrl?.replace("/api", "")}/docs/pdlss`
1106
+ },
1107
+ verifiedAt: /* @__PURE__ */ new Date()
1108
+ };
1109
+ reportCounterpartyPreCheckFailure(config, {
1110
+ agentId: astraCreds?.agentId || credentials.astraId || "unknown",
1111
+ counterpartyUrl,
1112
+ counterpartyType: config.counterpartyType || "website",
1113
+ failures: preCheckFailures,
1114
+ requestPath: pathname,
1115
+ requestMethod: request.method
1116
+ }).catch(() => {
1117
+ });
1118
+ if (pathname.startsWith("/api/")) {
1119
+ return NextResponse.json(
1120
+ {
1121
+ success: false,
1122
+ error: {
1123
+ code: "PDLSS_PRE_CHECK_FAILED",
1124
+ message: preCheckResult.denialReasons?.[0] || "PDLSS pre-check failed",
1125
+ guidance: preCheckResult.guidance
1126
+ }
1127
+ },
1128
+ { status: 403 }
1129
+ );
1130
+ }
1131
+ if (showCommerceShield) {
1132
+ return new NextResponse(generateCommerceShieldHtml(preCheckResult, options), {
1133
+ status: 200,
1134
+ headers: {
1135
+ "Content-Type": "text/html",
1136
+ "X-AstraSync-Verification": "commerce-shield"
1137
+ }
1138
+ });
1139
+ }
1140
+ return NextResponse.redirect(new URL("/unauthorized", request.url));
1141
+ }
957
1142
  const result = await verify(config, {
958
1143
  credentials,
959
1144
  purpose,
@@ -962,7 +1147,9 @@ function createMiddleware2(options) {
962
1147
  clientIp: request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() || void 0,
963
1148
  userAgent: request.headers.get("user-agent") || void 0,
964
1149
  counterpartyUrl,
965
- counterpartyType: config.counterpartyType || "website"
1150
+ counterpartyType: config.counterpartyType || "website",
1151
+ enableRuntimeChallenge,
1152
+ durationRequired: astraCreds?.pdlss?.duration?.maxSessionDuration
966
1153
  });
967
1154
  if (!hasMinimumAccess(result.accessLevel, routeConfig.minAccessLevel)) {
968
1155
  if (pathname.startsWith("/api/")) {