@kya-os/agentshield-nextjs 0.2.13 → 0.3.0
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/api-client.d.mts +6 -1
- package/dist/api-client.d.ts +6 -1
- package/dist/api-client.js.map +1 -1
- package/dist/api-client.mjs.map +1 -1
- package/dist/api-middleware.d.mts +13 -0
- package/dist/api-middleware.d.ts +13 -0
- package/dist/api-middleware.js +149 -19
- package/dist/api-middleware.js.map +1 -1
- package/dist/api-middleware.mjs +149 -19
- package/dist/api-middleware.mjs.map +1 -1
- package/dist/create-middleware.js +565 -487
- package/dist/create-middleware.js.map +1 -1
- package/dist/create-middleware.mjs +565 -487
- package/dist/create-middleware.mjs.map +1 -1
- package/dist/edge/index.js +69 -46
- package/dist/edge/index.js.map +1 -1
- package/dist/edge/index.mjs +69 -46
- package/dist/edge/index.mjs.map +1 -1
- package/dist/edge-detector-wrapper.js +9 -1
- package/dist/edge-detector-wrapper.js.map +1 -1
- package/dist/edge-detector-wrapper.mjs +9 -1
- package/dist/edge-detector-wrapper.mjs.map +1 -1
- package/dist/edge-runtime-loader.d.mts +1 -0
- package/dist/edge-runtime-loader.d.ts +1 -0
- package/dist/edge-runtime-loader.js +19 -3
- package/dist/edge-runtime-loader.js.map +1 -1
- package/dist/edge-runtime-loader.mjs +19 -3
- package/dist/edge-runtime-loader.mjs.map +1 -1
- package/dist/edge-wasm-middleware.d.mts +1 -0
- package/dist/edge-wasm-middleware.d.ts +1 -0
- package/dist/edge-wasm-middleware.js +10 -2
- package/dist/edge-wasm-middleware.js.map +1 -1
- package/dist/edge-wasm-middleware.mjs +11 -3
- package/dist/edge-wasm-middleware.mjs.map +1 -1
- package/dist/enhanced-middleware.js +48 -20
- package/dist/enhanced-middleware.js.map +1 -1
- package/dist/enhanced-middleware.mjs +49 -21
- package/dist/enhanced-middleware.mjs.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +263 -102
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +264 -103
- package/dist/index.mjs.map +1 -1
- package/dist/middleware.js +565 -487
- package/dist/middleware.js.map +1 -1
- package/dist/middleware.mjs +565 -487
- package/dist/middleware.mjs.map +1 -1
- package/dist/policy.d.mts +16 -4
- package/dist/policy.d.ts +16 -4
- package/dist/policy.js +14 -10
- package/dist/policy.js.map +1 -1
- package/dist/policy.mjs +14 -10
- package/dist/policy.mjs.map +1 -1
- package/dist/session-tracker.js +13 -19
- package/dist/session-tracker.js.map +1 -1
- package/dist/session-tracker.mjs +13 -19
- package/dist/session-tracker.mjs.map +1 -1
- package/dist/signature-verifier.js +9 -1
- package/dist/signature-verifier.js.map +1 -1
- package/dist/signature-verifier.mjs +9 -1
- package/dist/signature-verifier.mjs.map +1 -1
- package/dist/wasm-middleware.d.mts +1 -0
- package/dist/wasm-middleware.d.ts +1 -0
- package/dist/wasm-middleware.js +15 -15
- package/dist/wasm-middleware.js.map +1 -1
- package/dist/wasm-middleware.mjs +15 -15
- package/dist/wasm-middleware.mjs.map +1 -1
- package/package.json +23 -22
- package/wasm/agentshield_wasm.d.ts +379 -21
- package/wasm/agentshield_wasm.js +1409 -506
- package/wasm/agentshield_wasm_bg.wasm +0 -0
package/dist/api-client.d.mts
CHANGED
|
@@ -26,8 +26,13 @@ interface AgentShieldClientConfig {
|
|
|
26
26
|
}
|
|
27
27
|
/**
|
|
28
28
|
* Enforcement action
|
|
29
|
+
*
|
|
30
|
+
* Must stay in sync with `EnforcementConfig.defaultAction` in
|
|
31
|
+
* `packages/agentshield-shared/src/core/enforcement.ts` and the dashboard
|
|
32
|
+
* policy builder. `'instruct'` tells the middleware to emit a 401 with an
|
|
33
|
+
* MCP-I Link header pointing the agent at a connect/consent URL.
|
|
29
34
|
*/
|
|
30
|
-
type EnforcementAction = 'allow' | 'block' | 'redirect' | 'challenge' | 'log';
|
|
35
|
+
type EnforcementAction = 'allow' | 'block' | 'redirect' | 'instruct' | 'challenge' | 'log';
|
|
31
36
|
/**
|
|
32
37
|
* Enforcement decision from the API
|
|
33
38
|
*/
|
package/dist/api-client.d.ts
CHANGED
|
@@ -26,8 +26,13 @@ interface AgentShieldClientConfig {
|
|
|
26
26
|
}
|
|
27
27
|
/**
|
|
28
28
|
* Enforcement action
|
|
29
|
+
*
|
|
30
|
+
* Must stay in sync with `EnforcementConfig.defaultAction` in
|
|
31
|
+
* `packages/agentshield-shared/src/core/enforcement.ts` and the dashboard
|
|
32
|
+
* policy builder. `'instruct'` tells the middleware to emit a 401 with an
|
|
33
|
+
* MCP-I Link header pointing the agent at a connect/consent URL.
|
|
29
34
|
*/
|
|
30
|
-
type EnforcementAction = 'allow' | 'block' | 'redirect' | 'challenge' | 'log';
|
|
35
|
+
type EnforcementAction = 'allow' | 'block' | 'redirect' | 'instruct' | 'challenge' | 'log';
|
|
31
36
|
/**
|
|
32
37
|
* Enforcement decision from the API
|
|
33
38
|
*/
|
package/dist/api-client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/api-client.ts"],"names":[],"mappings":"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","file":"api-client.js","sourcesContent":["/**\n * AgentShield API Client\n *\n * Lightweight client for calling the AgentShield enforce API from middleware.\n * Designed for Edge Runtime compatibility (no Node.js-specific APIs).\n */\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * API client configuration\n */\nexport interface AgentShieldClientConfig {\n /** API key for authentication */\n apiKey: string;\n /** API base URL (defaults to production) */\n baseUrl?: string;\n /**\n * Use edge detection for lower latency (~30-50ms vs ~150ms) and better coverage.\n * Edge detection can identify non-JS clients (curl, Python, Claude Code WebFetch)\n * that the pixel cannot detect since they don't execute JavaScript.\n * @default true\n */\n useEdge?: boolean;\n /** Request timeout in milliseconds (default: 5000) */\n timeout?: number;\n /** Enable debug logging */\n debug?: boolean;\n}\n\n/**\n * Enforcement action\n */\nexport type EnforcementAction = 'allow' | 'block' | 'redirect' | 'challenge' | 'log';\n\n/**\n * Enforcement decision from the API\n */\nexport interface EnforcementDecision {\n action: EnforcementAction;\n reason: string;\n isAgent: boolean;\n confidence: number;\n agentName?: string;\n agentType?: string;\n redirectUrl?: string;\n message?: string;\n metadata?: {\n policyVersion?: string;\n signatureVerified?: boolean;\n denyListMatch?: {\n clientDid?: string;\n agentDid?: string;\n clientName?: string;\n reason?: string;\n };\n };\n}\n\n/**\n * Detection result (optional in response)\n */\nexport interface DetectionResult {\n isAgent: boolean;\n confidence: number;\n agentName?: string;\n agentType?: string;\n /** Detection class: 'human', 'ai_agent', 'bot', 'incomplete_data' */\n detectionClass?: string;\n verificationMethod?: string;\n reasons?: string[];\n /** Detection engine used: 'wasm' or 'javascript-fallback' */\n detectionMethod?: string;\n}\n\n/**\n * Enforce API response\n */\nexport interface EnforceResponse {\n success: boolean;\n data?: {\n decision: EnforcementDecision;\n processingTimeMs: number;\n requestId: string;\n detection?: DetectionResult;\n };\n error?: {\n code: string;\n message: string;\n };\n}\n\n/**\n * Request input for enforce API\n */\nexport interface EnforceInput {\n /** HTTP headers from the incoming request */\n headers?: Record<string, string>;\n /** User-Agent header */\n userAgent?: string;\n /** Client IP address */\n ipAddress?: string;\n /** Request path */\n path?: string;\n /** Request URL */\n url?: string;\n /** HTTP method */\n method?: string;\n /** Request ID for tracing */\n requestId?: string;\n /** Options */\n options?: {\n /** Include full detection result */\n includeDetectionResult?: boolean;\n /** Cache TTL override */\n cacheTTL?: number;\n };\n}\n\n/**\n * Input for logging a detection result\n */\nexport interface LogDetectionInput {\n /** Detection result from Gateway */\n detection: DetectionResult;\n /** Request context */\n context: {\n userAgent?: string;\n ipAddress?: string;\n path?: string;\n url?: string;\n method?: string;\n };\n /** Source of the detection */\n source?: 'gateway' | 'middleware';\n}\n\n// ============================================================================\n// Client Implementation\n// ============================================================================\n\nconst DEFAULT_BASE_URL = 'https://kya.vouched.id';\nconst EDGE_DETECT_URL = 'https://detect.checkpoint-gateway.ai';\nconst DEFAULT_TIMEOUT = 5000;\n\n/**\n * AgentShield API Client\n *\n * @example\n * ```typescript\n * const client = new AgentShieldClient({\n * apiKey: process.env.AGENTSHIELD_API_KEY!,\n * });\n *\n * const result = await client.enforce({\n * headers: Object.fromEntries(request.headers),\n * path: request.nextUrl.pathname,\n * method: request.method,\n * });\n *\n * if (result.decision.action === 'block') {\n * return new Response('Access denied', { status: 403 });\n * }\n * ```\n */\nexport class AgentShieldClient {\n private apiKey: string;\n private baseUrl: string;\n private useEdge: boolean;\n private timeout: number;\n private debug: boolean;\n\n constructor(config: AgentShieldClientConfig) {\n if (!config.apiKey) {\n throw new Error('AgentShield API key is required');\n }\n\n this.apiKey = config.apiKey;\n // Default to edge detection for better coverage (detects non-JS clients)\n this.useEdge = config.useEdge !== false; // true by default\n this.baseUrl = config.baseUrl || (this.useEdge ? EDGE_DETECT_URL : DEFAULT_BASE_URL);\n this.timeout = config.timeout || DEFAULT_TIMEOUT;\n this.debug = config.debug || false;\n }\n\n /**\n * Call the enforce API to check if a request should be allowed\n */\n async enforce(input: EnforceInput): Promise<EnforceResponse> {\n const startTime = Date.now();\n\n try {\n // Create abort controller for timeout\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n // Use edge endpoint or Vercel API based on configuration\n const endpoint = this.useEdge\n ? `${this.baseUrl}/__detect/enforce`\n : `${this.baseUrl}/api/v1/enforce`;\n\n const response = await fetch(endpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n 'X-Request-ID': input.requestId || crypto.randomUUID(),\n },\n body: JSON.stringify(input),\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n // Parse response\n const data = (await response.json()) as EnforceResponse;\n\n if (this.debug) {\n console.log('[AgentShield] Enforce response:', {\n status: response.status,\n action: data.data?.decision.action,\n processingTimeMs: Date.now() - startTime,\n });\n }\n\n // Handle non-2xx responses\n if (!response.ok) {\n return {\n success: false,\n error: {\n code: `HTTP_${response.status}`,\n message: data.error?.message || `HTTP error: ${response.status}`,\n },\n };\n }\n\n return data;\n } catch (error) {\n clearTimeout(timeoutId);\n throw error;\n }\n } catch (error) {\n // Handle timeout\n if (error instanceof Error && error.name === 'AbortError') {\n if (this.debug) {\n console.warn('[AgentShield] Request timed out');\n }\n return {\n success: false,\n error: {\n code: 'TIMEOUT',\n message: `Request timed out after ${this.timeout}ms`,\n },\n };\n }\n\n // Handle network errors\n if (this.debug) {\n console.error('[AgentShield] Request failed:', error);\n }\n\n return {\n success: false,\n error: {\n code: 'NETWORK_ERROR',\n message: error instanceof Error ? error.message : 'Network request failed',\n },\n };\n }\n }\n\n /**\n * Quick check - returns just the action without full response parsing\n * Useful for very fast middleware that just needs allow/block\n */\n async quickCheck(input: EnforceInput): Promise<{\n action: EnforcementAction;\n error?: string;\n }> {\n const result = await this.enforce(input);\n\n if (!result.success || !result.data) {\n // On error, default to allow (fail-open)\n return {\n action: 'allow',\n error: result.error?.message,\n };\n }\n\n return {\n action: result.data.decision.action,\n };\n }\n\n /**\n * Check if this client is using edge detection (Gateway Worker)\n */\n isUsingEdge(): boolean {\n return this.useEdge;\n }\n\n /**\n * Log a detection result to AgentShield database.\n * Use after Gateway Worker detection to persist results.\n * Fire-and-forget - returns immediately without waiting for DB write.\n *\n * @example\n * ```typescript\n * // After receiving Gateway response\n * if (client.isUsingEdge() && response.data?.detection) {\n * client.logDetection({\n * detection: response.data.detection,\n * context: { userAgent, ipAddress, path, url, method }\n * }).catch(err => console.error('Log failed:', err));\n * }\n * ```\n */\n async logDetection(input: LogDetectionInput): Promise<void> {\n // Don't await - fire and forget\n // Use the base URL (not edge) for logging since this goes to the main API\n const logEndpoint = this.useEdge\n ? `${DEFAULT_BASE_URL}/api/v1/log-detection`\n : `${this.baseUrl}/api/v1/log-detection`;\n\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n const response = await fetch(logEndpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify({\n detection: {\n isAgent: input.detection.isAgent,\n confidence: input.detection.confidence,\n agentName: input.detection.agentName,\n agentType: input.detection.agentType,\n detectionClass: input.detection.detectionClass,\n verificationMethod: input.detection.verificationMethod,\n reasons: input.detection.reasons,\n },\n context: input.context,\n source: input.source || 'gateway',\n }),\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok && this.debug) {\n console.warn('[AgentShield] Log detection returned non-2xx:', response.status);\n }\n } catch (error) {\n clearTimeout(timeoutId);\n throw error;\n }\n } catch (error) {\n // Silently fail for fire-and-forget, but log in debug mode\n if (this.debug) {\n console.error('[AgentShield] Log detection failed:', error);\n }\n // Re-throw so caller can catch if needed\n throw error;\n }\n }\n}\n\n/**\n * Create a singleton client instance\n *\n * @example\n * ```typescript\n * // In middleware.ts\n * import { getAgentShieldClient } from '@kya-os/agentshield-nextjs';\n *\n * const client = getAgentShieldClient();\n * ```\n */\nlet clientInstance: AgentShieldClient | null = null;\n\nexport function getAgentShieldClient(config?: Partial<AgentShieldClientConfig>): AgentShieldClient {\n if (!clientInstance) {\n const apiKey = config?.apiKey || process.env.AGENTSHIELD_API_KEY;\n\n if (!apiKey) {\n throw new Error(\n 'AgentShield API key is required. Set AGENTSHIELD_API_KEY environment variable or pass apiKey in config.'\n );\n }\n\n clientInstance = new AgentShieldClient({\n apiKey,\n baseUrl: config?.baseUrl || process.env.AGENTSHIELD_API_URL,\n // Default to edge detection unless explicitly disabled\n useEdge: config?.useEdge ?? process.env.AGENTSHIELD_USE_EDGE !== 'false',\n timeout: config?.timeout,\n debug: config?.debug || process.env.AGENTSHIELD_DEBUG === 'true',\n });\n }\n\n return clientInstance;\n}\n\n/**\n * Reset the singleton client (useful for testing)\n */\nexport function resetAgentShieldClient(): void {\n clientInstance = null;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/api-client.ts"],"names":[],"mappings":"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","file":"api-client.js","sourcesContent":["/**\n * AgentShield API Client\n *\n * Lightweight client for calling the AgentShield enforce API from middleware.\n * Designed for Edge Runtime compatibility (no Node.js-specific APIs).\n */\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * API client configuration\n */\nexport interface AgentShieldClientConfig {\n /** API key for authentication */\n apiKey: string;\n /** API base URL (defaults to production) */\n baseUrl?: string;\n /**\n * Use edge detection for lower latency (~30-50ms vs ~150ms) and better coverage.\n * Edge detection can identify non-JS clients (curl, Python, Claude Code WebFetch)\n * that the pixel cannot detect since they don't execute JavaScript.\n * @default true\n */\n useEdge?: boolean;\n /** Request timeout in milliseconds (default: 5000) */\n timeout?: number;\n /** Enable debug logging */\n debug?: boolean;\n}\n\n/**\n * Enforcement action\n *\n * Must stay in sync with `EnforcementConfig.defaultAction` in\n * `packages/agentshield-shared/src/core/enforcement.ts` and the dashboard\n * policy builder. `'instruct'` tells the middleware to emit a 401 with an\n * MCP-I Link header pointing the agent at a connect/consent URL.\n */\nexport type EnforcementAction = 'allow' | 'block' | 'redirect' | 'instruct' | 'challenge' | 'log';\n\n/**\n * Enforcement decision from the API\n */\nexport interface EnforcementDecision {\n action: EnforcementAction;\n reason: string;\n isAgent: boolean;\n confidence: number;\n agentName?: string;\n agentType?: string;\n redirectUrl?: string;\n message?: string;\n metadata?: {\n policyVersion?: string;\n signatureVerified?: boolean;\n denyListMatch?: {\n clientDid?: string;\n agentDid?: string;\n clientName?: string;\n reason?: string;\n };\n };\n}\n\n/**\n * Detection result (optional in response)\n */\nexport interface DetectionResult {\n isAgent: boolean;\n confidence: number;\n agentName?: string;\n agentType?: string;\n /** Detection class: 'human', 'ai_agent', 'bot', 'incomplete_data' */\n detectionClass?: string;\n verificationMethod?: string;\n reasons?: string[];\n /** Detection engine used: 'wasm' or 'javascript-fallback' */\n detectionMethod?: string;\n}\n\n/**\n * Enforce API response\n */\nexport interface EnforceResponse {\n success: boolean;\n data?: {\n decision: EnforcementDecision;\n processingTimeMs: number;\n requestId: string;\n detection?: DetectionResult;\n };\n error?: {\n code: string;\n message: string;\n };\n}\n\n/**\n * Request input for enforce API\n */\nexport interface EnforceInput {\n /** HTTP headers from the incoming request */\n headers?: Record<string, string>;\n /** User-Agent header */\n userAgent?: string;\n /** Client IP address */\n ipAddress?: string;\n /** Request path */\n path?: string;\n /** Request URL */\n url?: string;\n /** HTTP method */\n method?: string;\n /** Request ID for tracing */\n requestId?: string;\n /** Options */\n options?: {\n /** Include full detection result */\n includeDetectionResult?: boolean;\n /** Cache TTL override */\n cacheTTL?: number;\n };\n}\n\n/**\n * Input for logging a detection result\n */\nexport interface LogDetectionInput {\n /** Detection result from Gateway */\n detection: DetectionResult;\n /** Request context */\n context: {\n userAgent?: string;\n ipAddress?: string;\n path?: string;\n url?: string;\n method?: string;\n };\n /** Source of the detection */\n source?: 'gateway' | 'middleware';\n}\n\n// ============================================================================\n// Client Implementation\n// ============================================================================\n\nconst DEFAULT_BASE_URL = 'https://kya.vouched.id';\nconst EDGE_DETECT_URL = 'https://detect.checkpoint-gateway.ai';\nconst DEFAULT_TIMEOUT = 5000;\n\n/**\n * AgentShield API Client\n *\n * @example\n * ```typescript\n * const client = new AgentShieldClient({\n * apiKey: process.env.AGENTSHIELD_API_KEY!,\n * });\n *\n * const result = await client.enforce({\n * headers: Object.fromEntries(request.headers),\n * path: request.nextUrl.pathname,\n * method: request.method,\n * });\n *\n * if (result.decision.action === 'block') {\n * return new Response('Access denied', { status: 403 });\n * }\n * ```\n */\nexport class AgentShieldClient {\n private apiKey: string;\n private baseUrl: string;\n private useEdge: boolean;\n private timeout: number;\n private debug: boolean;\n\n constructor(config: AgentShieldClientConfig) {\n if (!config.apiKey) {\n throw new Error('AgentShield API key is required');\n }\n\n this.apiKey = config.apiKey;\n // Default to edge detection for better coverage (detects non-JS clients)\n this.useEdge = config.useEdge !== false; // true by default\n this.baseUrl = config.baseUrl || (this.useEdge ? EDGE_DETECT_URL : DEFAULT_BASE_URL);\n this.timeout = config.timeout || DEFAULT_TIMEOUT;\n this.debug = config.debug || false;\n }\n\n /**\n * Call the enforce API to check if a request should be allowed\n */\n async enforce(input: EnforceInput): Promise<EnforceResponse> {\n const startTime = Date.now();\n\n try {\n // Create abort controller for timeout\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n // Use edge endpoint or Vercel API based on configuration\n const endpoint = this.useEdge\n ? `${this.baseUrl}/__detect/enforce`\n : `${this.baseUrl}/api/v1/enforce`;\n\n const response = await fetch(endpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n 'X-Request-ID': input.requestId || crypto.randomUUID(),\n },\n body: JSON.stringify(input),\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n // Parse response\n const data = (await response.json()) as EnforceResponse;\n\n if (this.debug) {\n console.log('[AgentShield] Enforce response:', {\n status: response.status,\n action: data.data?.decision.action,\n processingTimeMs: Date.now() - startTime,\n });\n }\n\n // Handle non-2xx responses\n if (!response.ok) {\n return {\n success: false,\n error: {\n code: `HTTP_${response.status}`,\n message: data.error?.message || `HTTP error: ${response.status}`,\n },\n };\n }\n\n return data;\n } catch (error) {\n clearTimeout(timeoutId);\n throw error;\n }\n } catch (error) {\n // Handle timeout\n if (error instanceof Error && error.name === 'AbortError') {\n if (this.debug) {\n console.warn('[AgentShield] Request timed out');\n }\n return {\n success: false,\n error: {\n code: 'TIMEOUT',\n message: `Request timed out after ${this.timeout}ms`,\n },\n };\n }\n\n // Handle network errors\n if (this.debug) {\n console.error('[AgentShield] Request failed:', error);\n }\n\n return {\n success: false,\n error: {\n code: 'NETWORK_ERROR',\n message: error instanceof Error ? error.message : 'Network request failed',\n },\n };\n }\n }\n\n /**\n * Quick check - returns just the action without full response parsing\n * Useful for very fast middleware that just needs allow/block\n */\n async quickCheck(input: EnforceInput): Promise<{\n action: EnforcementAction;\n error?: string;\n }> {\n const result = await this.enforce(input);\n\n if (!result.success || !result.data) {\n // On error, default to allow (fail-open)\n return {\n action: 'allow',\n error: result.error?.message,\n };\n }\n\n return {\n action: result.data.decision.action,\n };\n }\n\n /**\n * Check if this client is using edge detection (Gateway Worker)\n */\n isUsingEdge(): boolean {\n return this.useEdge;\n }\n\n /**\n * Log a detection result to AgentShield database.\n * Use after Gateway Worker detection to persist results.\n * Fire-and-forget - returns immediately without waiting for DB write.\n *\n * @example\n * ```typescript\n * // After receiving Gateway response\n * if (client.isUsingEdge() && response.data?.detection) {\n * client.logDetection({\n * detection: response.data.detection,\n * context: { userAgent, ipAddress, path, url, method }\n * }).catch(err => console.error('Log failed:', err));\n * }\n * ```\n */\n async logDetection(input: LogDetectionInput): Promise<void> {\n // Don't await - fire and forget\n // Use the base URL (not edge) for logging since this goes to the main API\n const logEndpoint = this.useEdge\n ? `${DEFAULT_BASE_URL}/api/v1/log-detection`\n : `${this.baseUrl}/api/v1/log-detection`;\n\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n const response = await fetch(logEndpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify({\n detection: {\n isAgent: input.detection.isAgent,\n confidence: input.detection.confidence,\n agentName: input.detection.agentName,\n agentType: input.detection.agentType,\n detectionClass: input.detection.detectionClass,\n verificationMethod: input.detection.verificationMethod,\n reasons: input.detection.reasons,\n },\n context: input.context,\n source: input.source || 'gateway',\n }),\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok && this.debug) {\n console.warn('[AgentShield] Log detection returned non-2xx:', response.status);\n }\n } catch (error) {\n clearTimeout(timeoutId);\n throw error;\n }\n } catch (error) {\n // Silently fail for fire-and-forget, but log in debug mode\n if (this.debug) {\n console.error('[AgentShield] Log detection failed:', error);\n }\n // Re-throw so caller can catch if needed\n throw error;\n }\n }\n}\n\n/**\n * Create a singleton client instance\n *\n * @example\n * ```typescript\n * // In middleware.ts\n * import { getAgentShieldClient } from '@kya-os/agentshield-nextjs';\n *\n * const client = getAgentShieldClient();\n * ```\n */\nlet clientInstance: AgentShieldClient | null = null;\n\nexport function getAgentShieldClient(config?: Partial<AgentShieldClientConfig>): AgentShieldClient {\n if (!clientInstance) {\n const apiKey = config?.apiKey || process.env.AGENTSHIELD_API_KEY;\n\n if (!apiKey) {\n throw new Error(\n 'AgentShield API key is required. Set AGENTSHIELD_API_KEY environment variable or pass apiKey in config.'\n );\n }\n\n clientInstance = new AgentShieldClient({\n apiKey,\n baseUrl: config?.baseUrl || process.env.AGENTSHIELD_API_URL,\n // Default to edge detection unless explicitly disabled\n useEdge: config?.useEdge ?? process.env.AGENTSHIELD_USE_EDGE !== 'false',\n timeout: config?.timeout,\n debug: config?.debug || process.env.AGENTSHIELD_DEBUG === 'true',\n });\n }\n\n return clientInstance;\n}\n\n/**\n * Reset the singleton client (useful for testing)\n */\nexport function resetAgentShieldClient(): void {\n clientInstance = null;\n}\n"]}
|
package/dist/api-client.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/api-client.ts"],"names":[],"mappings":"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","file":"api-client.mjs","sourcesContent":["/**\n * AgentShield API Client\n *\n * Lightweight client for calling the AgentShield enforce API from middleware.\n * Designed for Edge Runtime compatibility (no Node.js-specific APIs).\n */\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * API client configuration\n */\nexport interface AgentShieldClientConfig {\n /** API key for authentication */\n apiKey: string;\n /** API base URL (defaults to production) */\n baseUrl?: string;\n /**\n * Use edge detection for lower latency (~30-50ms vs ~150ms) and better coverage.\n * Edge detection can identify non-JS clients (curl, Python, Claude Code WebFetch)\n * that the pixel cannot detect since they don't execute JavaScript.\n * @default true\n */\n useEdge?: boolean;\n /** Request timeout in milliseconds (default: 5000) */\n timeout?: number;\n /** Enable debug logging */\n debug?: boolean;\n}\n\n/**\n * Enforcement action\n */\nexport type EnforcementAction = 'allow' | 'block' | 'redirect' | 'challenge' | 'log';\n\n/**\n * Enforcement decision from the API\n */\nexport interface EnforcementDecision {\n action: EnforcementAction;\n reason: string;\n isAgent: boolean;\n confidence: number;\n agentName?: string;\n agentType?: string;\n redirectUrl?: string;\n message?: string;\n metadata?: {\n policyVersion?: string;\n signatureVerified?: boolean;\n denyListMatch?: {\n clientDid?: string;\n agentDid?: string;\n clientName?: string;\n reason?: string;\n };\n };\n}\n\n/**\n * Detection result (optional in response)\n */\nexport interface DetectionResult {\n isAgent: boolean;\n confidence: number;\n agentName?: string;\n agentType?: string;\n /** Detection class: 'human', 'ai_agent', 'bot', 'incomplete_data' */\n detectionClass?: string;\n verificationMethod?: string;\n reasons?: string[];\n /** Detection engine used: 'wasm' or 'javascript-fallback' */\n detectionMethod?: string;\n}\n\n/**\n * Enforce API response\n */\nexport interface EnforceResponse {\n success: boolean;\n data?: {\n decision: EnforcementDecision;\n processingTimeMs: number;\n requestId: string;\n detection?: DetectionResult;\n };\n error?: {\n code: string;\n message: string;\n };\n}\n\n/**\n * Request input for enforce API\n */\nexport interface EnforceInput {\n /** HTTP headers from the incoming request */\n headers?: Record<string, string>;\n /** User-Agent header */\n userAgent?: string;\n /** Client IP address */\n ipAddress?: string;\n /** Request path */\n path?: string;\n /** Request URL */\n url?: string;\n /** HTTP method */\n method?: string;\n /** Request ID for tracing */\n requestId?: string;\n /** Options */\n options?: {\n /** Include full detection result */\n includeDetectionResult?: boolean;\n /** Cache TTL override */\n cacheTTL?: number;\n };\n}\n\n/**\n * Input for logging a detection result\n */\nexport interface LogDetectionInput {\n /** Detection result from Gateway */\n detection: DetectionResult;\n /** Request context */\n context: {\n userAgent?: string;\n ipAddress?: string;\n path?: string;\n url?: string;\n method?: string;\n };\n /** Source of the detection */\n source?: 'gateway' | 'middleware';\n}\n\n// ============================================================================\n// Client Implementation\n// ============================================================================\n\nconst DEFAULT_BASE_URL = 'https://kya.vouched.id';\nconst EDGE_DETECT_URL = 'https://detect.checkpoint-gateway.ai';\nconst DEFAULT_TIMEOUT = 5000;\n\n/**\n * AgentShield API Client\n *\n * @example\n * ```typescript\n * const client = new AgentShieldClient({\n * apiKey: process.env.AGENTSHIELD_API_KEY!,\n * });\n *\n * const result = await client.enforce({\n * headers: Object.fromEntries(request.headers),\n * path: request.nextUrl.pathname,\n * method: request.method,\n * });\n *\n * if (result.decision.action === 'block') {\n * return new Response('Access denied', { status: 403 });\n * }\n * ```\n */\nexport class AgentShieldClient {\n private apiKey: string;\n private baseUrl: string;\n private useEdge: boolean;\n private timeout: number;\n private debug: boolean;\n\n constructor(config: AgentShieldClientConfig) {\n if (!config.apiKey) {\n throw new Error('AgentShield API key is required');\n }\n\n this.apiKey = config.apiKey;\n // Default to edge detection for better coverage (detects non-JS clients)\n this.useEdge = config.useEdge !== false; // true by default\n this.baseUrl = config.baseUrl || (this.useEdge ? EDGE_DETECT_URL : DEFAULT_BASE_URL);\n this.timeout = config.timeout || DEFAULT_TIMEOUT;\n this.debug = config.debug || false;\n }\n\n /**\n * Call the enforce API to check if a request should be allowed\n */\n async enforce(input: EnforceInput): Promise<EnforceResponse> {\n const startTime = Date.now();\n\n try {\n // Create abort controller for timeout\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n // Use edge endpoint or Vercel API based on configuration\n const endpoint = this.useEdge\n ? `${this.baseUrl}/__detect/enforce`\n : `${this.baseUrl}/api/v1/enforce`;\n\n const response = await fetch(endpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n 'X-Request-ID': input.requestId || crypto.randomUUID(),\n },\n body: JSON.stringify(input),\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n // Parse response\n const data = (await response.json()) as EnforceResponse;\n\n if (this.debug) {\n console.log('[AgentShield] Enforce response:', {\n status: response.status,\n action: data.data?.decision.action,\n processingTimeMs: Date.now() - startTime,\n });\n }\n\n // Handle non-2xx responses\n if (!response.ok) {\n return {\n success: false,\n error: {\n code: `HTTP_${response.status}`,\n message: data.error?.message || `HTTP error: ${response.status}`,\n },\n };\n }\n\n return data;\n } catch (error) {\n clearTimeout(timeoutId);\n throw error;\n }\n } catch (error) {\n // Handle timeout\n if (error instanceof Error && error.name === 'AbortError') {\n if (this.debug) {\n console.warn('[AgentShield] Request timed out');\n }\n return {\n success: false,\n error: {\n code: 'TIMEOUT',\n message: `Request timed out after ${this.timeout}ms`,\n },\n };\n }\n\n // Handle network errors\n if (this.debug) {\n console.error('[AgentShield] Request failed:', error);\n }\n\n return {\n success: false,\n error: {\n code: 'NETWORK_ERROR',\n message: error instanceof Error ? error.message : 'Network request failed',\n },\n };\n }\n }\n\n /**\n * Quick check - returns just the action without full response parsing\n * Useful for very fast middleware that just needs allow/block\n */\n async quickCheck(input: EnforceInput): Promise<{\n action: EnforcementAction;\n error?: string;\n }> {\n const result = await this.enforce(input);\n\n if (!result.success || !result.data) {\n // On error, default to allow (fail-open)\n return {\n action: 'allow',\n error: result.error?.message,\n };\n }\n\n return {\n action: result.data.decision.action,\n };\n }\n\n /**\n * Check if this client is using edge detection (Gateway Worker)\n */\n isUsingEdge(): boolean {\n return this.useEdge;\n }\n\n /**\n * Log a detection result to AgentShield database.\n * Use after Gateway Worker detection to persist results.\n * Fire-and-forget - returns immediately without waiting for DB write.\n *\n * @example\n * ```typescript\n * // After receiving Gateway response\n * if (client.isUsingEdge() && response.data?.detection) {\n * client.logDetection({\n * detection: response.data.detection,\n * context: { userAgent, ipAddress, path, url, method }\n * }).catch(err => console.error('Log failed:', err));\n * }\n * ```\n */\n async logDetection(input: LogDetectionInput): Promise<void> {\n // Don't await - fire and forget\n // Use the base URL (not edge) for logging since this goes to the main API\n const logEndpoint = this.useEdge\n ? `${DEFAULT_BASE_URL}/api/v1/log-detection`\n : `${this.baseUrl}/api/v1/log-detection`;\n\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n const response = await fetch(logEndpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify({\n detection: {\n isAgent: input.detection.isAgent,\n confidence: input.detection.confidence,\n agentName: input.detection.agentName,\n agentType: input.detection.agentType,\n detectionClass: input.detection.detectionClass,\n verificationMethod: input.detection.verificationMethod,\n reasons: input.detection.reasons,\n },\n context: input.context,\n source: input.source || 'gateway',\n }),\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok && this.debug) {\n console.warn('[AgentShield] Log detection returned non-2xx:', response.status);\n }\n } catch (error) {\n clearTimeout(timeoutId);\n throw error;\n }\n } catch (error) {\n // Silently fail for fire-and-forget, but log in debug mode\n if (this.debug) {\n console.error('[AgentShield] Log detection failed:', error);\n }\n // Re-throw so caller can catch if needed\n throw error;\n }\n }\n}\n\n/**\n * Create a singleton client instance\n *\n * @example\n * ```typescript\n * // In middleware.ts\n * import { getAgentShieldClient } from '@kya-os/agentshield-nextjs';\n *\n * const client = getAgentShieldClient();\n * ```\n */\nlet clientInstance: AgentShieldClient | null = null;\n\nexport function getAgentShieldClient(config?: Partial<AgentShieldClientConfig>): AgentShieldClient {\n if (!clientInstance) {\n const apiKey = config?.apiKey || process.env.AGENTSHIELD_API_KEY;\n\n if (!apiKey) {\n throw new Error(\n 'AgentShield API key is required. Set AGENTSHIELD_API_KEY environment variable or pass apiKey in config.'\n );\n }\n\n clientInstance = new AgentShieldClient({\n apiKey,\n baseUrl: config?.baseUrl || process.env.AGENTSHIELD_API_URL,\n // Default to edge detection unless explicitly disabled\n useEdge: config?.useEdge ?? process.env.AGENTSHIELD_USE_EDGE !== 'false',\n timeout: config?.timeout,\n debug: config?.debug || process.env.AGENTSHIELD_DEBUG === 'true',\n });\n }\n\n return clientInstance;\n}\n\n/**\n * Reset the singleton client (useful for testing)\n */\nexport function resetAgentShieldClient(): void {\n clientInstance = null;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/api-client.ts"],"names":[],"mappings":";AAoJA,IAAM,gBAAA,GAAmB,wBAAA;AACzB,IAAM,eAAA,GAAkB,sCAAA;AACxB,IAAM,eAAA,GAAkB,GAAA;AAsBjB,IAAM,oBAAN,MAAwB;AAAA,EACrB,MAAA;AAAA,EACA,OAAA;AAAA,EACA,OAAA;AAAA,EACA,OAAA;AAAA,EACA,KAAA;AAAA,EAER,YAAY,MAAA,EAAiC;AAC3C,IAAA,IAAI,CAAC,OAAO,MAAA,EAAQ;AAClB,MAAA,MAAM,IAAI,MAAM,iCAAiC,CAAA;AAAA,IACnD;AAEA,IAAA,IAAA,CAAK,SAAS,MAAA,CAAO,MAAA;AAErB,IAAA,IAAA,CAAK,OAAA,GAAU,OAAO,OAAA,KAAY,KAAA;AAClC,IAAA,IAAA,CAAK,OAAA,GAAU,MAAA,CAAO,OAAA,KAAY,IAAA,CAAK,UAAU,eAAA,GAAkB,gBAAA,CAAA;AACnE,IAAA,IAAA,CAAK,OAAA,GAAU,OAAO,OAAA,IAAW,eAAA;AACjC,IAAA,IAAA,CAAK,KAAA,GAAQ,OAAO,KAAA,IAAS,KAAA;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAQ,KAAA,EAA+C;AAC3D,IAAA,MAAM,SAAA,GAAY,KAAK,GAAA,EAAI;AAE3B,IAAA,IAAI;AAEF,MAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AACvC,MAAA,MAAM,YAAY,UAAA,CAAW,MAAM,WAAW,KAAA,EAAM,EAAG,KAAK,OAAO,CAAA;AAEnE,MAAA,IAAI;AAEF,QAAA,MAAM,QAAA,GAAW,KAAK,OAAA,GAClB,CAAA,EAAG,KAAK,OAAO,CAAA,iBAAA,CAAA,GACf,CAAA,EAAG,IAAA,CAAK,OAAO,CAAA,eAAA,CAAA;AAEnB,QAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,QAAA,EAAU;AAAA,UACrC,MAAA,EAAQ,MAAA;AAAA,UACR,OAAA,EAAS;AAAA,YACP,cAAA,EAAgB,kBAAA;AAAA,YAChB,aAAA,EAAe,CAAA,OAAA,EAAU,IAAA,CAAK,MAAM,CAAA,CAAA;AAAA,YACpC,cAAA,EAAgB,KAAA,CAAM,SAAA,IAAa,MAAA,CAAO,UAAA;AAAW,WACvD;AAAA,UACA,IAAA,EAAM,IAAA,CAAK,SAAA,CAAU,KAAK,CAAA;AAAA,UAC1B,QAAQ,UAAA,CAAW;AAAA,SACpB,CAAA;AAED,QAAA,YAAA,CAAa,SAAS,CAAA;AAGtB,QAAA,MAAM,IAAA,GAAQ,MAAM,QAAA,CAAS,IAAA,EAAK;AAElC,QAAA,IAAI,KAAK,KAAA,EAAO;AACd,UAAA,OAAA,CAAQ,IAAI,iCAAA,EAAmC;AAAA,YAC7C,QAAQ,QAAA,CAAS,MAAA;AAAA,YACjB,MAAA,EAAQ,IAAA,CAAK,IAAA,EAAM,QAAA,CAAS,MAAA;AAAA,YAC5B,gBAAA,EAAkB,IAAA,CAAK,GAAA,EAAI,GAAI;AAAA,WAChC,CAAA;AAAA,QACH;AAGA,QAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,UAAA,OAAO;AAAA,YACL,OAAA,EAAS,KAAA;AAAA,YACT,KAAA,EAAO;AAAA,cACL,IAAA,EAAM,CAAA,KAAA,EAAQ,QAAA,CAAS,MAAM,CAAA,CAAA;AAAA,cAC7B,SAAS,IAAA,CAAK,KAAA,EAAO,OAAA,IAAW,CAAA,YAAA,EAAe,SAAS,MAAM,CAAA;AAAA;AAChE,WACF;AAAA,QACF;AAEA,QAAA,OAAO,IAAA;AAAA,MACT,SAAS,KAAA,EAAO;AACd,QAAA,YAAA,CAAa,SAAS,CAAA;AACtB,QAAA,MAAM,KAAA;AAAA,MACR;AAAA,IACF,SAAS,KAAA,EAAO;AAEd,MAAA,IAAI,KAAA,YAAiB,KAAA,IAAS,KAAA,CAAM,IAAA,KAAS,YAAA,EAAc;AACzD,QAAA,IAAI,KAAK,KAAA,EAAO;AACd,UAAA,OAAA,CAAQ,KAAK,iCAAiC,CAAA;AAAA,QAChD;AACA,QAAA,OAAO;AAAA,UACL,OAAA,EAAS,KAAA;AAAA,UACT,KAAA,EAAO;AAAA,YACL,IAAA,EAAM,SAAA;AAAA,YACN,OAAA,EAAS,CAAA,wBAAA,EAA2B,IAAA,CAAK,OAAO,CAAA,EAAA;AAAA;AAClD,SACF;AAAA,MACF;AAGA,MAAA,IAAI,KAAK,KAAA,EAAO;AACd,QAAA,OAAA,CAAQ,KAAA,CAAM,iCAAiC,KAAK,CAAA;AAAA,MACtD;AAEA,MAAA,OAAO;AAAA,QACL,OAAA,EAAS,KAAA;AAAA,QACT,KAAA,EAAO;AAAA,UACL,IAAA,EAAM,eAAA;AAAA,UACN,OAAA,EAAS,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU;AAAA;AACpD,OACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,WAAW,KAAA,EAGd;AACD,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,OAAA,CAAQ,KAAK,CAAA;AAEvC,IAAA,IAAI,CAAC,MAAA,CAAO,OAAA,IAAW,CAAC,OAAO,IAAA,EAAM;AAEnC,MAAA,OAAO;AAAA,QACL,MAAA,EAAQ,OAAA;AAAA,QACR,KAAA,EAAO,OAAO,KAAA,EAAO;AAAA,OACvB;AAAA,IACF;AAEA,IAAA,OAAO;AAAA,MACL,MAAA,EAAQ,MAAA,CAAO,IAAA,CAAK,QAAA,CAAS;AAAA,KAC/B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,WAAA,GAAuB;AACrB,IAAA,OAAO,IAAA,CAAK,OAAA;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBA,MAAM,aAAa,KAAA,EAAyC;AAG1D,IAAA,MAAM,WAAA,GAAc,KAAK,OAAA,GACrB,CAAA,EAAG,gBAAgB,CAAA,qBAAA,CAAA,GACnB,CAAA,EAAG,KAAK,OAAO,CAAA,qBAAA,CAAA;AAEnB,IAAA,IAAI;AACF,MAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AACvC,MAAA,MAAM,YAAY,UAAA,CAAW,MAAM,WAAW,KAAA,EAAM,EAAG,KAAK,OAAO,CAAA;AAEnE,MAAA,IAAI;AACF,QAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,WAAA,EAAa;AAAA,UACxC,MAAA,EAAQ,MAAA;AAAA,UACR,OAAA,EAAS;AAAA,YACP,cAAA,EAAgB,kBAAA;AAAA,YAChB,aAAA,EAAe,CAAA,OAAA,EAAU,IAAA,CAAK,MAAM,CAAA;AAAA,WACtC;AAAA,UACA,IAAA,EAAM,KAAK,SAAA,CAAU;AAAA,YACnB,SAAA,EAAW;AAAA,cACT,OAAA,EAAS,MAAM,SAAA,CAAU,OAAA;AAAA,cACzB,UAAA,EAAY,MAAM,SAAA,CAAU,UAAA;AAAA,cAC5B,SAAA,EAAW,MAAM,SAAA,CAAU,SAAA;AAAA,cAC3B,SAAA,EAAW,MAAM,SAAA,CAAU,SAAA;AAAA,cAC3B,cAAA,EAAgB,MAAM,SAAA,CAAU,cAAA;AAAA,cAChC,kBAAA,EAAoB,MAAM,SAAA,CAAU,kBAAA;AAAA,cACpC,OAAA,EAAS,MAAM,SAAA,CAAU;AAAA,aAC3B;AAAA,YACA,SAAS,KAAA,CAAM,OAAA;AAAA,YACf,MAAA,EAAQ,MAAM,MAAA,IAAU;AAAA,WACzB,CAAA;AAAA,UACD,QAAQ,UAAA,CAAW;AAAA,SACpB,CAAA;AAED,QAAA,YAAA,CAAa,SAAS,CAAA;AAEtB,QAAA,IAAI,CAAC,QAAA,CAAS,EAAA,IAAM,IAAA,CAAK,KAAA,EAAO;AAC9B,UAAA,OAAA,CAAQ,IAAA,CAAK,+CAAA,EAAiD,QAAA,CAAS,MAAM,CAAA;AAAA,QAC/E;AAAA,MACF,SAAS,KAAA,EAAO;AACd,QAAA,YAAA,CAAa,SAAS,CAAA;AACtB,QAAA,MAAM,KAAA;AAAA,MACR;AAAA,IACF,SAAS,KAAA,EAAO;AAEd,MAAA,IAAI,KAAK,KAAA,EAAO;AACd,QAAA,OAAA,CAAQ,KAAA,CAAM,uCAAuC,KAAK,CAAA;AAAA,MAC5D;AAEA,MAAA,MAAM,KAAA;AAAA,IACR;AAAA,EACF;AACF;AAaA,IAAI,cAAA,GAA2C,IAAA;AAExC,SAAS,qBAAqB,MAAA,EAA8D;AACjG,EAAA,IAAI,CAAC,cAAA,EAAgB;AACnB,IAAA,MAAM,MAAA,GAAS,MAAA,EAAQ,MAAA,IAAU,OAAA,CAAQ,GAAA,CAAI,mBAAA;AAE7C,IAAA,IAAI,CAAC,MAAA,EAAQ;AACX,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OACF;AAAA,IACF;AAEA,IAAA,cAAA,GAAiB,IAAI,iBAAA,CAAkB;AAAA,MACrC,MAAA;AAAA,MACA,OAAA,EAAS,MAAA,EAAQ,OAAA,IAAW,OAAA,CAAQ,GAAA,CAAI,mBAAA;AAAA;AAAA,MAExC,OAAA,EAAS,MAAA,EAAQ,OAAA,IAAW,OAAA,CAAQ,IAAI,oBAAA,KAAyB,OAAA;AAAA,MACjE,SAAS,MAAA,EAAQ,OAAA;AAAA,MACjB,KAAA,EAAO,MAAA,EAAQ,KAAA,IAAS,OAAA,CAAQ,IAAI,iBAAA,KAAsB;AAAA,KAC3D,CAAA;AAAA,EACH;AAEA,EAAA,OAAO,cAAA;AACT;AAKO,SAAS,sBAAA,GAA+B;AAC7C,EAAA,cAAA,GAAiB,IAAA;AACnB","file":"api-client.mjs","sourcesContent":["/**\n * AgentShield API Client\n *\n * Lightweight client for calling the AgentShield enforce API from middleware.\n * Designed for Edge Runtime compatibility (no Node.js-specific APIs).\n */\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * API client configuration\n */\nexport interface AgentShieldClientConfig {\n /** API key for authentication */\n apiKey: string;\n /** API base URL (defaults to production) */\n baseUrl?: string;\n /**\n * Use edge detection for lower latency (~30-50ms vs ~150ms) and better coverage.\n * Edge detection can identify non-JS clients (curl, Python, Claude Code WebFetch)\n * that the pixel cannot detect since they don't execute JavaScript.\n * @default true\n */\n useEdge?: boolean;\n /** Request timeout in milliseconds (default: 5000) */\n timeout?: number;\n /** Enable debug logging */\n debug?: boolean;\n}\n\n/**\n * Enforcement action\n *\n * Must stay in sync with `EnforcementConfig.defaultAction` in\n * `packages/agentshield-shared/src/core/enforcement.ts` and the dashboard\n * policy builder. `'instruct'` tells the middleware to emit a 401 with an\n * MCP-I Link header pointing the agent at a connect/consent URL.\n */\nexport type EnforcementAction = 'allow' | 'block' | 'redirect' | 'instruct' | 'challenge' | 'log';\n\n/**\n * Enforcement decision from the API\n */\nexport interface EnforcementDecision {\n action: EnforcementAction;\n reason: string;\n isAgent: boolean;\n confidence: number;\n agentName?: string;\n agentType?: string;\n redirectUrl?: string;\n message?: string;\n metadata?: {\n policyVersion?: string;\n signatureVerified?: boolean;\n denyListMatch?: {\n clientDid?: string;\n agentDid?: string;\n clientName?: string;\n reason?: string;\n };\n };\n}\n\n/**\n * Detection result (optional in response)\n */\nexport interface DetectionResult {\n isAgent: boolean;\n confidence: number;\n agentName?: string;\n agentType?: string;\n /** Detection class: 'human', 'ai_agent', 'bot', 'incomplete_data' */\n detectionClass?: string;\n verificationMethod?: string;\n reasons?: string[];\n /** Detection engine used: 'wasm' or 'javascript-fallback' */\n detectionMethod?: string;\n}\n\n/**\n * Enforce API response\n */\nexport interface EnforceResponse {\n success: boolean;\n data?: {\n decision: EnforcementDecision;\n processingTimeMs: number;\n requestId: string;\n detection?: DetectionResult;\n };\n error?: {\n code: string;\n message: string;\n };\n}\n\n/**\n * Request input for enforce API\n */\nexport interface EnforceInput {\n /** HTTP headers from the incoming request */\n headers?: Record<string, string>;\n /** User-Agent header */\n userAgent?: string;\n /** Client IP address */\n ipAddress?: string;\n /** Request path */\n path?: string;\n /** Request URL */\n url?: string;\n /** HTTP method */\n method?: string;\n /** Request ID for tracing */\n requestId?: string;\n /** Options */\n options?: {\n /** Include full detection result */\n includeDetectionResult?: boolean;\n /** Cache TTL override */\n cacheTTL?: number;\n };\n}\n\n/**\n * Input for logging a detection result\n */\nexport interface LogDetectionInput {\n /** Detection result from Gateway */\n detection: DetectionResult;\n /** Request context */\n context: {\n userAgent?: string;\n ipAddress?: string;\n path?: string;\n url?: string;\n method?: string;\n };\n /** Source of the detection */\n source?: 'gateway' | 'middleware';\n}\n\n// ============================================================================\n// Client Implementation\n// ============================================================================\n\nconst DEFAULT_BASE_URL = 'https://kya.vouched.id';\nconst EDGE_DETECT_URL = 'https://detect.checkpoint-gateway.ai';\nconst DEFAULT_TIMEOUT = 5000;\n\n/**\n * AgentShield API Client\n *\n * @example\n * ```typescript\n * const client = new AgentShieldClient({\n * apiKey: process.env.AGENTSHIELD_API_KEY!,\n * });\n *\n * const result = await client.enforce({\n * headers: Object.fromEntries(request.headers),\n * path: request.nextUrl.pathname,\n * method: request.method,\n * });\n *\n * if (result.decision.action === 'block') {\n * return new Response('Access denied', { status: 403 });\n * }\n * ```\n */\nexport class AgentShieldClient {\n private apiKey: string;\n private baseUrl: string;\n private useEdge: boolean;\n private timeout: number;\n private debug: boolean;\n\n constructor(config: AgentShieldClientConfig) {\n if (!config.apiKey) {\n throw new Error('AgentShield API key is required');\n }\n\n this.apiKey = config.apiKey;\n // Default to edge detection for better coverage (detects non-JS clients)\n this.useEdge = config.useEdge !== false; // true by default\n this.baseUrl = config.baseUrl || (this.useEdge ? EDGE_DETECT_URL : DEFAULT_BASE_URL);\n this.timeout = config.timeout || DEFAULT_TIMEOUT;\n this.debug = config.debug || false;\n }\n\n /**\n * Call the enforce API to check if a request should be allowed\n */\n async enforce(input: EnforceInput): Promise<EnforceResponse> {\n const startTime = Date.now();\n\n try {\n // Create abort controller for timeout\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n // Use edge endpoint or Vercel API based on configuration\n const endpoint = this.useEdge\n ? `${this.baseUrl}/__detect/enforce`\n : `${this.baseUrl}/api/v1/enforce`;\n\n const response = await fetch(endpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n 'X-Request-ID': input.requestId || crypto.randomUUID(),\n },\n body: JSON.stringify(input),\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n // Parse response\n const data = (await response.json()) as EnforceResponse;\n\n if (this.debug) {\n console.log('[AgentShield] Enforce response:', {\n status: response.status,\n action: data.data?.decision.action,\n processingTimeMs: Date.now() - startTime,\n });\n }\n\n // Handle non-2xx responses\n if (!response.ok) {\n return {\n success: false,\n error: {\n code: `HTTP_${response.status}`,\n message: data.error?.message || `HTTP error: ${response.status}`,\n },\n };\n }\n\n return data;\n } catch (error) {\n clearTimeout(timeoutId);\n throw error;\n }\n } catch (error) {\n // Handle timeout\n if (error instanceof Error && error.name === 'AbortError') {\n if (this.debug) {\n console.warn('[AgentShield] Request timed out');\n }\n return {\n success: false,\n error: {\n code: 'TIMEOUT',\n message: `Request timed out after ${this.timeout}ms`,\n },\n };\n }\n\n // Handle network errors\n if (this.debug) {\n console.error('[AgentShield] Request failed:', error);\n }\n\n return {\n success: false,\n error: {\n code: 'NETWORK_ERROR',\n message: error instanceof Error ? error.message : 'Network request failed',\n },\n };\n }\n }\n\n /**\n * Quick check - returns just the action without full response parsing\n * Useful for very fast middleware that just needs allow/block\n */\n async quickCheck(input: EnforceInput): Promise<{\n action: EnforcementAction;\n error?: string;\n }> {\n const result = await this.enforce(input);\n\n if (!result.success || !result.data) {\n // On error, default to allow (fail-open)\n return {\n action: 'allow',\n error: result.error?.message,\n };\n }\n\n return {\n action: result.data.decision.action,\n };\n }\n\n /**\n * Check if this client is using edge detection (Gateway Worker)\n */\n isUsingEdge(): boolean {\n return this.useEdge;\n }\n\n /**\n * Log a detection result to AgentShield database.\n * Use after Gateway Worker detection to persist results.\n * Fire-and-forget - returns immediately without waiting for DB write.\n *\n * @example\n * ```typescript\n * // After receiving Gateway response\n * if (client.isUsingEdge() && response.data?.detection) {\n * client.logDetection({\n * detection: response.data.detection,\n * context: { userAgent, ipAddress, path, url, method }\n * }).catch(err => console.error('Log failed:', err));\n * }\n * ```\n */\n async logDetection(input: LogDetectionInput): Promise<void> {\n // Don't await - fire and forget\n // Use the base URL (not edge) for logging since this goes to the main API\n const logEndpoint = this.useEdge\n ? `${DEFAULT_BASE_URL}/api/v1/log-detection`\n : `${this.baseUrl}/api/v1/log-detection`;\n\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n const response = await fetch(logEndpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify({\n detection: {\n isAgent: input.detection.isAgent,\n confidence: input.detection.confidence,\n agentName: input.detection.agentName,\n agentType: input.detection.agentType,\n detectionClass: input.detection.detectionClass,\n verificationMethod: input.detection.verificationMethod,\n reasons: input.detection.reasons,\n },\n context: input.context,\n source: input.source || 'gateway',\n }),\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok && this.debug) {\n console.warn('[AgentShield] Log detection returned non-2xx:', response.status);\n }\n } catch (error) {\n clearTimeout(timeoutId);\n throw error;\n }\n } catch (error) {\n // Silently fail for fire-and-forget, but log in debug mode\n if (this.debug) {\n console.error('[AgentShield] Log detection failed:', error);\n }\n // Re-throw so caller can catch if needed\n throw error;\n }\n }\n}\n\n/**\n * Create a singleton client instance\n *\n * @example\n * ```typescript\n * // In middleware.ts\n * import { getAgentShieldClient } from '@kya-os/agentshield-nextjs';\n *\n * const client = getAgentShieldClient();\n * ```\n */\nlet clientInstance: AgentShieldClient | null = null;\n\nexport function getAgentShieldClient(config?: Partial<AgentShieldClientConfig>): AgentShieldClient {\n if (!clientInstance) {\n const apiKey = config?.apiKey || process.env.AGENTSHIELD_API_KEY;\n\n if (!apiKey) {\n throw new Error(\n 'AgentShield API key is required. Set AGENTSHIELD_API_KEY environment variable or pass apiKey in config.'\n );\n }\n\n clientInstance = new AgentShieldClient({\n apiKey,\n baseUrl: config?.baseUrl || process.env.AGENTSHIELD_API_URL,\n // Default to edge detection unless explicitly disabled\n useEdge: config?.useEdge ?? process.env.AGENTSHIELD_USE_EDGE !== 'false',\n timeout: config?.timeout,\n debug: config?.debug || process.env.AGENTSHIELD_DEBUG === 'true',\n });\n }\n\n return clientInstance;\n}\n\n/**\n * Reset the singleton client (useful for testing)\n */\nexport function resetAgentShieldClient(): void {\n clientInstance = null;\n}\n"]}
|
|
@@ -62,6 +62,19 @@ interface AgentShieldMiddlewareConfig {
|
|
|
62
62
|
* Default: uses redirectUrl from dashboard policy
|
|
63
63
|
*/
|
|
64
64
|
redirectUrl?: string;
|
|
65
|
+
/**
|
|
66
|
+
* How the middleware handles a `redirect` / `instruct` action.
|
|
67
|
+
*
|
|
68
|
+
* - `'instruct'` (default): return HTTP 401 with an MCP-I Link header + JSON
|
|
69
|
+
* body pointing the agent at the redirect URL. LLMs surface the URL as a
|
|
70
|
+
* clickable link for the user. Matches the Cloudflare Gateway contract.
|
|
71
|
+
* - `'http'`: legacy behavior — return HTTP 302 with `Location`. Most LLM
|
|
72
|
+
* fetchers won't follow the redirect, so this is only useful when your
|
|
73
|
+
* traffic is real browsers.
|
|
74
|
+
*
|
|
75
|
+
* @default 'instruct'
|
|
76
|
+
*/
|
|
77
|
+
redirectMode?: 'instruct' | 'http';
|
|
65
78
|
/**
|
|
66
79
|
* Custom blocked response
|
|
67
80
|
*/
|
package/dist/api-middleware.d.ts
CHANGED
|
@@ -62,6 +62,19 @@ interface AgentShieldMiddlewareConfig {
|
|
|
62
62
|
* Default: uses redirectUrl from dashboard policy
|
|
63
63
|
*/
|
|
64
64
|
redirectUrl?: string;
|
|
65
|
+
/**
|
|
66
|
+
* How the middleware handles a `redirect` / `instruct` action.
|
|
67
|
+
*
|
|
68
|
+
* - `'instruct'` (default): return HTTP 401 with an MCP-I Link header + JSON
|
|
69
|
+
* body pointing the agent at the redirect URL. LLMs surface the URL as a
|
|
70
|
+
* clickable link for the user. Matches the Cloudflare Gateway contract.
|
|
71
|
+
* - `'http'`: legacy behavior — return HTTP 302 with `Location`. Most LLM
|
|
72
|
+
* fetchers won't follow the redirect, so this is only useful when your
|
|
73
|
+
* traffic is real browsers.
|
|
74
|
+
*
|
|
75
|
+
* @default 'instruct'
|
|
76
|
+
*/
|
|
77
|
+
redirectMode?: 'instruct' | 'http';
|
|
65
78
|
/**
|
|
66
79
|
* Custom blocked response
|
|
67
80
|
*/
|
package/dist/api-middleware.js
CHANGED
|
@@ -194,6 +194,105 @@ function getAgentShieldClient(config) {
|
|
|
194
194
|
return clientInstance;
|
|
195
195
|
}
|
|
196
196
|
|
|
197
|
+
// src/utils.ts
|
|
198
|
+
function getClientIp(request) {
|
|
199
|
+
const forwardedFor = request.headers.get("x-forwarded-for");
|
|
200
|
+
if (forwardedFor) {
|
|
201
|
+
const ip = forwardedFor.split(",")[0]?.trim();
|
|
202
|
+
if (ip) return ip;
|
|
203
|
+
}
|
|
204
|
+
const realIp = request.headers.get("x-real-ip");
|
|
205
|
+
if (realIp) return realIp;
|
|
206
|
+
const cfIp = request.headers.get("cf-connecting-ip");
|
|
207
|
+
if (cfIp) return cfIp;
|
|
208
|
+
const clientIp = request.headers.get("x-client-ip");
|
|
209
|
+
if (clientIp) return clientIp;
|
|
210
|
+
return void 0;
|
|
211
|
+
}
|
|
212
|
+
function safeHostname(url) {
|
|
213
|
+
try {
|
|
214
|
+
return new URL(url).hostname;
|
|
215
|
+
} catch {
|
|
216
|
+
return "this site";
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// src/responses/agent-instruction.ts
|
|
221
|
+
var MCP_I_DOCS_URL = "https://docs.knowthat.ai/mcp-i/getting-started";
|
|
222
|
+
var DEFAULT_CONNECT_PATH = "/connect";
|
|
223
|
+
function buildAgentInstructionResponse(request, decision, redirectUrl) {
|
|
224
|
+
const resolved = resolveUrl(redirectUrl ?? DEFAULT_CONNECT_PATH, request.url);
|
|
225
|
+
const agentName = decision.agentName || decision.agentType || "unknown";
|
|
226
|
+
if (!resolved.searchParams.has("agent")) {
|
|
227
|
+
resolved.searchParams.set("agent", agentName.toLowerCase());
|
|
228
|
+
}
|
|
229
|
+
const authUrl = resolved.toString();
|
|
230
|
+
const hostname = safeHostname(request.url);
|
|
231
|
+
const body = {
|
|
232
|
+
// Markdown-formatted so clients that render markdown (Claude Desktop,
|
|
233
|
+
// ChatGPT web) surface the URL as a clickable link. Tone mirrors the
|
|
234
|
+
// gateway response so messaging stays consistent across platforms.
|
|
235
|
+
message: `I can't access ${hostname} yet \u2014 this site checks AI assistants at the front door.
|
|
236
|
+
|
|
237
|
+
**To give me access, open this link:**
|
|
238
|
+
[Connect securely to ${hostname}](${authUrl})
|
|
239
|
+
|
|
240
|
+
It only takes a moment and you won't need to do it again. Once you're done, ask me to try again and I'll connect through the verified channel automatically.`,
|
|
241
|
+
user_action_required: {
|
|
242
|
+
action: `Connect securely to ${hostname}`,
|
|
243
|
+
url: authUrl,
|
|
244
|
+
reason: `${hostname} checks AI assistants before they connect. Open the link to give your assistant a verified key.`
|
|
245
|
+
},
|
|
246
|
+
mcp_i: {
|
|
247
|
+
version: "1.0",
|
|
248
|
+
action: "authenticate",
|
|
249
|
+
authorization_url: authUrl,
|
|
250
|
+
flow: {
|
|
251
|
+
type: "oauth2_delegation",
|
|
252
|
+
steps: [
|
|
253
|
+
"1. Direct your user to the authorization_url",
|
|
254
|
+
"2. User reviews requested scopes and grants consent",
|
|
255
|
+
"3. Receive delegation credential (JWT)",
|
|
256
|
+
"4. Include credential in KYA-Delegation header",
|
|
257
|
+
"5. Retry this request with the proof"
|
|
258
|
+
]
|
|
259
|
+
},
|
|
260
|
+
retry_instructions: {
|
|
261
|
+
header: "KYA-Delegation",
|
|
262
|
+
format: "JWT delegation credential from authorization flow"
|
|
263
|
+
},
|
|
264
|
+
documentation: MCP_I_DOCS_URL
|
|
265
|
+
},
|
|
266
|
+
error: "mcp_authentication_required",
|
|
267
|
+
code: "AGENT_REQUIRES_DELEGATION",
|
|
268
|
+
detection: {
|
|
269
|
+
agent_type: decision.agentType || "ai_agent",
|
|
270
|
+
agent_name: decision.agentName || "Unknown Agent",
|
|
271
|
+
confidence: decision.confidence
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
const response = server.NextResponse.json(body, { status: 401 });
|
|
275
|
+
response.headers.set("WWW-Authenticate", `KYA realm="api", authorization_uri="${authUrl}"`);
|
|
276
|
+
response.headers.set(
|
|
277
|
+
"Link",
|
|
278
|
+
`<${authUrl}>; rel="kya-authorize", <${MCP_I_DOCS_URL}>; rel="help"`
|
|
279
|
+
);
|
|
280
|
+
response.headers.set("KYA-Auth-Required", "true");
|
|
281
|
+
response.headers.set("KYA-Auth-Url", authUrl);
|
|
282
|
+
response.headers.set("KYA-Action", "instruct");
|
|
283
|
+
response.headers.set("KYA-Detected-Agent", agentName);
|
|
284
|
+
response.headers.set("KYA-Confidence", decision.confidence.toString());
|
|
285
|
+
response.headers.set("Cache-Control", "no-store");
|
|
286
|
+
return response;
|
|
287
|
+
}
|
|
288
|
+
function resolveUrl(target, baseUrl) {
|
|
289
|
+
try {
|
|
290
|
+
return new URL(target, baseUrl);
|
|
291
|
+
} catch {
|
|
292
|
+
return new URL(DEFAULT_CONNECT_PATH, baseUrl);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
197
296
|
// src/api-middleware.ts
|
|
198
297
|
function matchPath(path, pattern) {
|
|
199
298
|
if (pattern === path) return true;
|
|
@@ -213,27 +312,52 @@ function shouldIncludePath(path, includePaths) {
|
|
|
213
312
|
if (!includePaths || includePaths.length === 0) return true;
|
|
214
313
|
return includePaths.some((pattern) => matchPath(path, pattern));
|
|
215
314
|
}
|
|
216
|
-
function buildBlockedResponse(decision, config) {
|
|
315
|
+
function buildBlockedResponse(request, decision, config) {
|
|
217
316
|
const status = config.blockedResponse?.status ?? 403;
|
|
218
317
|
const message = config.blockedResponse?.message ?? decision.message ?? "Access denied";
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
318
|
+
const recoveryUrl = resolveRecoveryUrl(request, config.redirectUrl || decision.redirectUrl);
|
|
319
|
+
const body = {
|
|
320
|
+
error: message,
|
|
321
|
+
code: "AGENT_BLOCKED",
|
|
322
|
+
reason: decision.reason,
|
|
323
|
+
agentType: decision.agentType
|
|
324
|
+
};
|
|
325
|
+
if (recoveryUrl) {
|
|
326
|
+
const hostname = safeHostname(request.url);
|
|
327
|
+
body.user_action_required = {
|
|
328
|
+
action: `Connect securely to ${hostname}`,
|
|
329
|
+
url: recoveryUrl,
|
|
330
|
+
reason: `${hostname} blocks unverified AI assistants. Open the link to give your assistant a verified key and try again.`
|
|
331
|
+
};
|
|
332
|
+
body.message = `I can't access ${hostname} \u2014 this site blocks unverified AI assistants.
|
|
333
|
+
|
|
334
|
+
**To give me access, open this link:**
|
|
335
|
+
[Connect securely to ${hostname}](${recoveryUrl})
|
|
336
|
+
|
|
337
|
+
Once you're done, ask me to try again.`;
|
|
338
|
+
}
|
|
339
|
+
const response = server.NextResponse.json(body, { status });
|
|
228
340
|
if (config.blockedResponse?.headers) {
|
|
229
341
|
for (const [key, value] of Object.entries(config.blockedResponse.headers)) {
|
|
230
342
|
response.headers.set(key, value);
|
|
231
343
|
}
|
|
232
344
|
}
|
|
233
|
-
|
|
234
|
-
|
|
345
|
+
if (recoveryUrl) {
|
|
346
|
+
response.headers.set("Link", `<${recoveryUrl}>; rel="kya-authorize"`);
|
|
347
|
+
response.headers.set("KYA-Auth-Url", recoveryUrl);
|
|
348
|
+
}
|
|
349
|
+
response.headers.set("KYA-Action", decision.action);
|
|
350
|
+
response.headers.set("KYA-Reason", decision.reason);
|
|
235
351
|
return response;
|
|
236
352
|
}
|
|
353
|
+
function resolveRecoveryUrl(request, target) {
|
|
354
|
+
if (!target) return void 0;
|
|
355
|
+
try {
|
|
356
|
+
return new URL(target, request.url).toString();
|
|
357
|
+
} catch {
|
|
358
|
+
return void 0;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
237
361
|
function buildRedirectResponse(request, decision, config) {
|
|
238
362
|
const redirectUrl = config.redirectUrl || decision.redirectUrl || "/blocked";
|
|
239
363
|
const url = new URL(redirectUrl, request.url);
|
|
@@ -278,7 +402,7 @@ function withAgentShield(config = {}) {
|
|
|
278
402
|
try {
|
|
279
403
|
const client2 = getClient();
|
|
280
404
|
const userAgent = request.headers.get("user-agent") || void 0;
|
|
281
|
-
const ipAddress =
|
|
405
|
+
const ipAddress = getClientIp(request);
|
|
282
406
|
const result = await client2.enforce({
|
|
283
407
|
headers: Object.fromEntries(request.headers.entries()),
|
|
284
408
|
userAgent,
|
|
@@ -329,6 +453,7 @@ function withAgentShield(config = {}) {
|
|
|
329
453
|
if (decision.isAgent && config.onAgentDetected) {
|
|
330
454
|
await config.onAgentDetected(request, decision);
|
|
331
455
|
}
|
|
456
|
+
const redirectMode = config.redirectMode ?? "instruct";
|
|
332
457
|
switch (decision.action) {
|
|
333
458
|
case "block": {
|
|
334
459
|
if (config.customBlockedResponse) {
|
|
@@ -337,10 +462,15 @@ function withAgentShield(config = {}) {
|
|
|
337
462
|
if (config.onBlock === "redirect") {
|
|
338
463
|
return buildRedirectResponse(request, decision, config);
|
|
339
464
|
}
|
|
340
|
-
return buildBlockedResponse(decision, config);
|
|
465
|
+
return buildBlockedResponse(request, decision, config);
|
|
341
466
|
}
|
|
342
|
-
case "redirect":
|
|
343
|
-
|
|
467
|
+
case "redirect":
|
|
468
|
+
case "instruct": {
|
|
469
|
+
if (redirectMode === "http" && decision.action === "redirect") {
|
|
470
|
+
return buildRedirectResponse(request, decision, config);
|
|
471
|
+
}
|
|
472
|
+
const targetUrl = config.redirectUrl || decision.redirectUrl;
|
|
473
|
+
return buildAgentInstructionResponse(request, decision, targetUrl);
|
|
344
474
|
}
|
|
345
475
|
case "challenge": {
|
|
346
476
|
return buildRedirectResponse(request, decision, config);
|
|
@@ -350,10 +480,10 @@ function withAgentShield(config = {}) {
|
|
|
350
480
|
default: {
|
|
351
481
|
const response = server.NextResponse.next();
|
|
352
482
|
if (decision.isAgent) {
|
|
353
|
-
response.headers.set("
|
|
354
|
-
response.headers.set("
|
|
483
|
+
response.headers.set("KYA-Detected", "true");
|
|
484
|
+
response.headers.set("KYA-Confidence", decision.confidence.toString());
|
|
355
485
|
if (decision.agentName) {
|
|
356
|
-
response.headers.set("
|
|
486
|
+
response.headers.set("KYA-Agent", decision.agentName);
|
|
357
487
|
}
|
|
358
488
|
}
|
|
359
489
|
return response;
|