@heddleagent/execution-host-client 6.3.0 → 6.5.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/README.md +121 -20
- package/dist/coordinator/contracts.d.ts +130 -0
- package/dist/coordinator/contracts.d.ts.map +1 -0
- package/dist/coordinator/contracts.js +112 -0
- package/dist/coordinator/contracts.js.map +1 -0
- package/dist/coordinator/hosted-heartbeat-coordinator-client.d.ts +20 -0
- package/dist/coordinator/hosted-heartbeat-coordinator-client.d.ts.map +1 -0
- package/dist/coordinator/hosted-heartbeat-coordinator-client.js +77 -0
- package/dist/coordinator/hosted-heartbeat-coordinator-client.js.map +1 -0
- package/dist/coordinator/hosted-heartbeat-delegation-client.d.ts +21 -0
- package/dist/coordinator/hosted-heartbeat-delegation-client.d.ts.map +1 -0
- package/dist/coordinator/hosted-heartbeat-delegation-client.js +117 -0
- package/dist/coordinator/hosted-heartbeat-delegation-client.js.map +1 -0
- package/dist/coordinator/hosted-heartbeat-delegation-service.d.ts +15 -0
- package/dist/coordinator/hosted-heartbeat-delegation-service.d.ts.map +1 -0
- package/dist/coordinator/hosted-heartbeat-delegation-service.js +81 -0
- package/dist/coordinator/hosted-heartbeat-delegation-service.js.map +1 -0
- package/dist/coordinator/hosted-heartbeat-task-reconciler.d.ts +11 -0
- package/dist/coordinator/hosted-heartbeat-task-reconciler.d.ts.map +1 -0
- package/dist/coordinator/hosted-heartbeat-task-reconciler.js +38 -0
- package/dist/coordinator/hosted-heartbeat-task-reconciler.js.map +1 -0
- package/dist/coordinator/index.d.ts +9 -0
- package/dist/coordinator/index.d.ts.map +1 -0
- package/dist/coordinator/index.js +7 -0
- package/dist/coordinator/index.js.map +1 -0
- package/dist/coordinator/node/delegation-http-service.d.ts +11 -0
- package/dist/coordinator/node/delegation-http-service.d.ts.map +1 -0
- package/dist/coordinator/node/delegation-http-service.js +131 -0
- package/dist/coordinator/node/delegation-http-service.js.map +1 -0
- package/dist/coordinator/node/environment.d.ts +3 -0
- package/dist/coordinator/node/environment.d.ts.map +1 -0
- package/dist/coordinator/node/environment.js +13 -0
- package/dist/coordinator/node/environment.js.map +1 -0
- package/dist/coordinator/node/index.d.ts +4 -0
- package/dist/coordinator/node/index.d.ts.map +1 -0
- package/dist/coordinator/node/index.js +3 -0
- package/dist/coordinator/node/index.js.map +1 -0
- package/dist/coordinator/node/types.d.ts +20 -0
- package/dist/coordinator/node/types.d.ts.map +1 -0
- package/dist/coordinator/node/types.js +2 -0
- package/dist/coordinator/node/types.js.map +1 -0
- package/dist/coordinator/runtime-session.d.ts +13 -0
- package/dist/coordinator/runtime-session.d.ts.map +1 -0
- package/dist/coordinator/runtime-session.js +23 -0
- package/dist/coordinator/runtime-session.js.map +1 -0
- package/dist/coordinator/service-token.d.ts +3 -0
- package/dist/coordinator/service-token.d.ts.map +1 -0
- package/dist/coordinator/service-token.js +7 -0
- package/dist/coordinator/service-token.js.map +1 -0
- package/dist/coordinator/types.d.ts +63 -0
- package/dist/coordinator/types.d.ts.map +1 -0
- package/dist/coordinator/types.js +2 -0
- package/dist/coordinator/types.js.map +1 -0
- package/dist/host/errors.d.ts +23 -0
- package/dist/host/errors.d.ts.map +1 -0
- package/dist/host/errors.js +32 -0
- package/dist/host/errors.js.map +1 -0
- package/dist/host/index.d.ts +7 -0
- package/dist/host/index.d.ts.map +1 -0
- package/dist/host/index.js +6 -0
- package/dist/host/index.js.map +1 -0
- package/dist/host/jwt-execution-host-mcp-capability-verifier.d.ts +8 -0
- package/dist/host/jwt-execution-host-mcp-capability-verifier.d.ts.map +1 -0
- package/dist/host/jwt-execution-host-mcp-capability-verifier.js +86 -0
- package/dist/host/jwt-execution-host-mcp-capability-verifier.js.map +1 -0
- package/dist/host/jwt-execution-identity-verifier.d.ts +8 -0
- package/dist/host/jwt-execution-identity-verifier.d.ts.map +1 -0
- package/dist/host/jwt-execution-identity-verifier.js +76 -0
- package/dist/host/jwt-execution-identity-verifier.js.map +1 -0
- package/dist/host/schemas.d.ts +37 -0
- package/dist/host/schemas.d.ts.map +1 -0
- package/dist/host/schemas.js +24 -0
- package/dist/host/schemas.js.map +1 -0
- package/dist/host/types.d.ts +62 -0
- package/dist/host/types.d.ts.map +1 -0
- package/dist/host/types.js +7 -0
- package/dist/host/types.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/internal/jwt-verification.d.ts +5 -0
- package/dist/internal/jwt-verification.d.ts.map +1 -0
- package/dist/internal/jwt-verification.js +25 -0
- package/dist/internal/jwt-verification.js.map +1 -0
- package/dist/mcp/jwt-capability-verifier.d.ts.map +1 -1
- package/dist/mcp/jwt-capability-verifier.js +6 -25
- package/dist/mcp/jwt-capability-verifier.js.map +1 -1
- package/dist/node/http-service.d.ts.map +1 -1
- package/dist/node/http-service.js +5 -108
- package/dist/node/http-service.js.map +1 -1
- package/dist/node/http-utils.d.ts +15 -0
- package/dist/node/http-utils.d.ts.map +1 -0
- package/dist/node/http-utils.js +109 -0
- package/dist/node/http-utils.js.map +1 -0
- package/package.json +13 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/host/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,MAAM,CAAC;AAC5C,OAAO,KAAK,EACV,cAAc,EACd,uBAAuB,EACxB,MAAM,uBAAuB,CAAC;AAE/B,eAAO,MAAM,uCAAuC,+CAK1C,CAAC;AAEX,MAAM,MAAM,yBAAyB,GACnC,OAAO,uCAAuC,CAAC,MAAM,CAAC,CAAC;AAEzD,+EAA+E;AAC/E,MAAM,MAAM,yBAAyB,GAAG,QAAQ,CAAC;IAC/C,KAAK,EAAE,QAAQ,CAAC,cAAc,CAAC,CAAC;IAChC,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,uBAAuB,CAAC;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC,CAAC;AAEH,MAAM,MAAM,oCAAoC,GAAG,QAAQ,CAAC;IAC1D,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,uBAAuB,CAAC;CACnC,CAAC,CAAC;AAEH,MAAM,WAAW,yBAAyB;IACxC,MAAM,CACJ,OAAO,EAAE,oCAAoC,GAC5C,OAAO,CAAC,yBAAyB,CAAC,CAAC;CACvC;AAED,MAAM,MAAM,kCAAkC,GAAG,QAAQ,CAAC;IACxD,eAAe,EAAE,MAAM,CAAC;IACxB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gBAAgB,EAAE,GAAG,CAAC;IACtB,sBAAsB,EAAE,SAAS,yBAAyB,EAAE,CAAC;IAC7D,gBAAgB,EAAE,MAAM,CAAC;IACzB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,8BAA8B,EAAE,MAAM,CAAC;CACxC,CAAC,CAAC;AAEH,iFAAiF;AACjF,MAAM,MAAM,kCAAkC,GAAG,QAAQ,CAAC;IACxD,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC,CAAC;AAEH,MAAM,MAAM,6CAA6C,GAAG,QAAQ,CAAC;IACnE,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,yBAAyB,CAAC;CACrC,CAAC,CAAC;AAEH,MAAM,WAAW,kCAAkC;IACjD,MAAM,CACJ,OAAO,EAAE,6CAA6C,GACrD,OAAO,CAAC,kCAAkC,CAAC,CAAC;CAChD;AAED,MAAM,MAAM,2CAA2C,GAAG,QAAQ,CAAC;IACjE,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,GAAG,CAAC;IACb,aAAa,EAAE,SAAS,yBAAyB,EAAE,CAAC;IACpD,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,uBAAuB,EAAE,MAAM,CAAC;IAChC,qBAAqB,EAAE,MAAM,CAAC;CAC/B,CAAC,CAAC;AAEH,MAAM,MAAM,+BAA+B,GAAG,QAAQ,CAAC;IACrD,WAAW,CAAC,EAAE,eAAe,CAAC;IAC9B,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/host/types.ts"],"names":[],"mappings":"AAMA,MAAM,CAAC,MAAM,uCAAuC,GAAG;IACrD,OAAO;IACP,OAAO;IACP,OAAO;IACP,OAAO;CACC,CAAC","sourcesContent":["import type { JWTVerifyGetKey } from 'jose';\nimport type {\n ExecutionScope,\n HostedExecutionWorkflow,\n} from '../contracts/index.js';\n\nexport const EXECUTION_HOST_SUPPORTED_JWT_ALGORITHMS = [\n 'RS256',\n 'PS256',\n 'ES256',\n 'EdDSA',\n] as const;\n\nexport type ExecutionHostJwtAlgorithm =\n typeof EXECUTION_HOST_SUPPORTED_JWT_ALGORITHMS[number];\n\n/** Authority established from one signed assertion and its request binding. */\nexport type VerifiedExecutionIdentity = Readonly<{\n scope: Readonly<ExecutionScope>;\n runtimeSessionId: string;\n invocationId: string;\n workflow: HostedExecutionWorkflow;\n issuedAt: string;\n expiresAt: string;\n}>;\n\nexport type ExecutionIdentityVerificationRequest = Readonly<{\n assertion: string;\n runtimeSessionId: string;\n invocationId: string;\n workflow: HostedExecutionWorkflow;\n}>;\n\nexport interface ExecutionIdentityVerifier {\n verify(\n request: ExecutionIdentityVerificationRequest,\n ): Promise<VerifiedExecutionIdentity>;\n}\n\nexport type JwtExecutionIdentityVerifierConfig = Readonly<{\n executionIssuer: string;\n executionAudience: string;\n executionJwksUrl: URL;\n executionJwtAlgorithms: readonly ExecutionHostJwtAlgorithm[];\n trustedAdopterId: string;\n maxAssertionAgeSeconds: number;\n assertionClockToleranceSeconds: number;\n}>;\n\n/** Opaque MCP bearer whose claims match the independently verified execution. */\nexport type VerifiedExecutionHostMcpCapability = Readonly<{\n assertion: string;\n capabilityId: string;\n serverId: string;\n allowedTools: readonly string[];\n issuedAt: string;\n expiresAt: string;\n}>;\n\nexport type ExecutionHostMcpCapabilityVerificationRequest = Readonly<{\n assertion: string;\n identity: VerifiedExecutionIdentity;\n}>;\n\nexport interface ExecutionHostMcpCapabilityVerifier {\n verify(\n request: ExecutionHostMcpCapabilityVerificationRequest,\n ): Promise<VerifiedExecutionHostMcpCapability>;\n}\n\nexport type JwtExecutionHostMcpCapabilityVerifierConfig = Readonly<{\n issuer: string;\n audience: string;\n jwksUrl: URL;\n jwtAlgorithms: readonly ExecutionHostJwtAlgorithm[];\n trustedAdopterId: string;\n serverId: string;\n maxCapabilityAgeSeconds: number;\n clockToleranceSeconds: number;\n}>;\n\nexport type ExecutionHostJwtVerifierOptions = Readonly<{\n keyResolver?: JWTVerifyGetKey;\n now?: () => Date;\n}>;\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export * from './contracts/index.js';
|
|
|
2
2
|
export * from './authority/index.js';
|
|
3
3
|
export * from './conversation/index.js';
|
|
4
4
|
export * from './heartbeat/index.js';
|
|
5
|
+
export * from './coordinator/index.js';
|
|
5
6
|
export * from './mcp/index.js';
|
|
6
7
|
export * from './http-sse/index.js';
|
|
7
8
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC;AACrC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@ export * from './contracts/index.js';
|
|
|
2
2
|
export * from './authority/index.js';
|
|
3
3
|
export * from './conversation/index.js';
|
|
4
4
|
export * from './heartbeat/index.js';
|
|
5
|
+
export * from './coordinator/index.js';
|
|
5
6
|
export * from './mcp/index.js';
|
|
6
7
|
export * from './http-sse/index.js';
|
|
7
8
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC;AACrC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC","sourcesContent":["export * from './contracts/index.js';\nexport * from './authority/index.js';\nexport * from './conversation/index.js';\nexport * from './heartbeat/index.js';\nexport * from './mcp/index.js';\nexport * from './http-sse/index.js';\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC","sourcesContent":["export * from './contracts/index.js';\nexport * from './authority/index.js';\nexport * from './conversation/index.js';\nexport * from './heartbeat/index.js';\nexport * from './coordinator/index.js';\nexport * from './mcp/index.js';\nexport * from './http-sse/index.js';\n"]}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type JWTVerifyGetKey } from 'jose';
|
|
2
|
+
export type JwtVerificationUnavailableCategory = 'network' | 'jwks_response' | 'jwks_timeout';
|
|
3
|
+
export declare function createJwtKeyResolver(jwksUrl: URL): JWTVerifyGetKey;
|
|
4
|
+
export declare function resolveJwtVerificationUnavailableCategory(error: unknown): JwtVerificationUnavailableCategory | undefined;
|
|
5
|
+
//# sourceMappingURL=jwt-verification.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwt-verification.d.ts","sourceRoot":"","sources":["../../src/internal/jwt-verification.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,eAAe,EACrB,MAAM,MAAM,CAAC;AAgBd,MAAM,MAAM,kCAAkC,GAC1C,SAAS,GACT,eAAe,GACf,cAAc,CAAC;AAEnB,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,GAAG,GAAG,eAAe,CAElE;AAED,wBAAgB,yCAAyC,CACvD,KAAK,EAAE,OAAO,GACb,kCAAkC,GAAG,SAAS,CAOhD"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { createRemoteJWKSet, errors, } from 'jose';
|
|
2
|
+
const REMOTE_JWKS_OPTIONS = Object.freeze({
|
|
3
|
+
cacheMaxAge: 10 * 60_000,
|
|
4
|
+
cooldownDuration: 30_000,
|
|
5
|
+
timeoutDuration: 3_000,
|
|
6
|
+
});
|
|
7
|
+
const UNAVAILABLE_JOSE_CATEGORIES = new Map([
|
|
8
|
+
['ERR_JOSE_GENERIC', 'jwks_response'],
|
|
9
|
+
['ERR_JWK_INVALID', 'jwks_response'],
|
|
10
|
+
['ERR_JWKS_INVALID', 'jwks_response'],
|
|
11
|
+
['ERR_JWKS_MULTIPLE_MATCHING_KEYS', 'jwks_response'],
|
|
12
|
+
['ERR_JWKS_TIMEOUT', 'jwks_timeout'],
|
|
13
|
+
]);
|
|
14
|
+
export function createJwtKeyResolver(jwksUrl) {
|
|
15
|
+
return createRemoteJWKSet(jwksUrl, REMOTE_JWKS_OPTIONS);
|
|
16
|
+
}
|
|
17
|
+
export function resolveJwtVerificationUnavailableCategory(error) {
|
|
18
|
+
if (error instanceof TypeError) {
|
|
19
|
+
return 'network';
|
|
20
|
+
}
|
|
21
|
+
return error instanceof errors.JOSEError
|
|
22
|
+
? UNAVAILABLE_JOSE_CATEGORIES.get(error.code)
|
|
23
|
+
: undefined;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=jwt-verification.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwt-verification.js","sourceRoot":"","sources":["../../src/internal/jwt-verification.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,MAAM,GAEP,MAAM,MAAM,CAAC;AAEd,MAAM,mBAAmB,GAAG,MAAM,CAAC,MAAM,CAAC;IACxC,WAAW,EAAE,EAAE,GAAG,MAAM;IACxB,gBAAgB,EAAE,MAAM;IACxB,eAAe,EAAE,KAAK;CACvB,CAAC,CAAC;AAEH,MAAM,2BAA2B,GAAG,IAAI,GAAG,CAA6C;IACtF,CAAC,kBAAkB,EAAE,eAAe,CAAC;IACrC,CAAC,iBAAiB,EAAE,eAAe,CAAC;IACpC,CAAC,kBAAkB,EAAE,eAAe,CAAC;IACrC,CAAC,iCAAiC,EAAE,eAAe,CAAC;IACpD,CAAC,kBAAkB,EAAE,cAAc,CAAC;CACrC,CAAC,CAAC;AAOH,MAAM,UAAU,oBAAoB,CAAC,OAAY;IAC/C,OAAO,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAAC;AAC1D,CAAC;AAED,MAAM,UAAU,yCAAyC,CACvD,KAAc;IAEd,IAAI,KAAK,YAAY,SAAS,EAAE,CAAC;QAC/B,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,KAAK,YAAY,MAAM,CAAC,SAAS;QACtC,CAAC,CAAC,2BAA2B,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC;QAC7C,CAAC,CAAC,SAAS,CAAC;AAChB,CAAC","sourcesContent":["import {\n createRemoteJWKSet,\n errors,\n type JWTVerifyGetKey,\n} from 'jose';\n\nconst REMOTE_JWKS_OPTIONS = Object.freeze({\n cacheMaxAge: 10 * 60_000,\n cooldownDuration: 30_000,\n timeoutDuration: 3_000,\n});\n\nconst UNAVAILABLE_JOSE_CATEGORIES = new Map<string, JwtVerificationUnavailableCategory>([\n ['ERR_JOSE_GENERIC', 'jwks_response'],\n ['ERR_JWK_INVALID', 'jwks_response'],\n ['ERR_JWKS_INVALID', 'jwks_response'],\n ['ERR_JWKS_MULTIPLE_MATCHING_KEYS', 'jwks_response'],\n ['ERR_JWKS_TIMEOUT', 'jwks_timeout'],\n]);\n\nexport type JwtVerificationUnavailableCategory =\n | 'network'\n | 'jwks_response'\n | 'jwks_timeout';\n\nexport function createJwtKeyResolver(jwksUrl: URL): JWTVerifyGetKey {\n return createRemoteJWKSet(jwksUrl, REMOTE_JWKS_OPTIONS);\n}\n\nexport function resolveJwtVerificationUnavailableCategory(\n error: unknown,\n): JwtVerificationUnavailableCategory | undefined {\n if (error instanceof TypeError) {\n return 'network';\n }\n return error instanceof errors.JOSEError\n ? UNAVAILABLE_JOSE_CATEGORIES.get(error.code)\n : undefined;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jwt-capability-verifier.d.ts","sourceRoot":"","sources":["../../src/mcp/jwt-capability-verifier.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EACV,8BAA8B,EAC9B,+BAA+B,EAC/B,qBAAqB,EACrB,qBAAqB,EACtB,MAAM,YAAY,CAAC;AAqBpB;;;GAGG;AACH,qBAAa,wBAAwB,CAAC,SAAS,SAAS,MAAM,CAC9D,YAAW,qBAAqB,CAAC,SAAS,CAAC;;gBAOvC,MAAM,EAAE,8BAA8B,CAAC,SAAS,CAAC,EACjD,OAAO,GAAE,+BAAoC;
|
|
1
|
+
{"version":3,"file":"jwt-capability-verifier.d.ts","sourceRoot":"","sources":["../../src/mcp/jwt-capability-verifier.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EACV,8BAA8B,EAC9B,+BAA+B,EAC/B,qBAAqB,EACrB,qBAAqB,EACtB,MAAM,YAAY,CAAC;AAqBpB;;;GAGG;AACH,qBAAa,wBAAwB,CAAC,SAAS,SAAS,MAAM,CAC9D,YAAW,qBAAqB,CAAC,SAAS,CAAC;;gBAOvC,MAAM,EAAE,8BAA8B,CAAC,SAAS,CAAC,EACjD,OAAO,GAAE,+BAAoC;IAczC,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC,SAAS,CAAC,CAAC;CAkF3E;AAED,wBAAgB,yBAAyB,CACvC,UAAU,EAAE,qBAAqB,EACjC,GAAG,OAAa,GACf,IAAI,CAKN"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jwtVerify } from 'jose';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import { JwtAudienceSchema, JwtIssuerSchema, MCP_CAPABILITY_TYPE, McpAllowedToolsSchema, McpCapabilityClaimsSchema, McpServerIdSchema, OpaqueIdSchema, isSafeWebUrl, } from '../contracts/index.js';
|
|
4
4
|
import { McpCapabilityUnavailableError, McpCapabilityVerificationError, } from './errors.js';
|
|
5
|
+
import { createJwtKeyResolver, resolveJwtVerificationUnavailableCategory, } from '../internal/jwt-verification.js';
|
|
5
6
|
const CapabilityAssertionSchema = z.string().min(32).max(4_096);
|
|
6
7
|
const ConfigSchema = z.object({
|
|
7
8
|
issuer: JwtIssuerSchema,
|
|
@@ -30,11 +31,8 @@ export class JwtMcpCapabilityVerifier {
|
|
|
30
31
|
supportedTools: Object.freeze([...parsed.supportedTools]),
|
|
31
32
|
});
|
|
32
33
|
this.#supportedTools = new Set(this.#config.supportedTools);
|
|
33
|
-
this.#keyResolver = options.keyResolver
|
|
34
|
-
|
|
35
|
-
cooldownDuration: 30_000,
|
|
36
|
-
timeoutDuration: 3_000,
|
|
37
|
-
});
|
|
34
|
+
this.#keyResolver = options.keyResolver
|
|
35
|
+
?? createJwtKeyResolver(this.#config.jwksUrl);
|
|
38
36
|
this.#now = options.now ?? (() => new Date());
|
|
39
37
|
}
|
|
40
38
|
async verify(assertion) {
|
|
@@ -76,9 +74,9 @@ export class JwtMcpCapabilityVerifier {
|
|
|
76
74
|
|| error instanceof McpCapabilityUnavailableError) {
|
|
77
75
|
throw error;
|
|
78
76
|
}
|
|
79
|
-
const unavailableCategory =
|
|
77
|
+
const unavailableCategory = resolveJwtVerificationUnavailableCategory(error);
|
|
80
78
|
if (unavailableCategory) {
|
|
81
|
-
throw new McpCapabilityUnavailableError(unavailableCategory);
|
|
79
|
+
throw new McpCapabilityUnavailableError(unavailableCategory === 'network' ? 'network' : 'jwks');
|
|
82
80
|
}
|
|
83
81
|
throw new McpCapabilityVerificationError();
|
|
84
82
|
}
|
|
@@ -116,21 +114,4 @@ function freezeVerifiedCapability(capability) {
|
|
|
116
114
|
scope: Object.freeze({ ...capability.scope }),
|
|
117
115
|
});
|
|
118
116
|
}
|
|
119
|
-
const UNAVAILABLE_JOSE_CODES = new Set([
|
|
120
|
-
'ERR_JOSE_GENERIC',
|
|
121
|
-
'ERR_JWK_INVALID',
|
|
122
|
-
'ERR_JWKS_INVALID',
|
|
123
|
-
'ERR_JWKS_MULTIPLE_MATCHING_KEYS',
|
|
124
|
-
'ERR_JWKS_TIMEOUT',
|
|
125
|
-
]);
|
|
126
|
-
function resolveUnavailableCategory(error) {
|
|
127
|
-
if (error instanceof TypeError) {
|
|
128
|
-
return 'network';
|
|
129
|
-
}
|
|
130
|
-
if (error instanceof errors.JOSEError
|
|
131
|
-
&& UNAVAILABLE_JOSE_CODES.has(error.code)) {
|
|
132
|
-
return 'jwks';
|
|
133
|
-
}
|
|
134
|
-
return undefined;
|
|
135
|
-
}
|
|
136
117
|
//# sourceMappingURL=jwt-capability-verifier.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jwt-capability-verifier.js","sourceRoot":"","sources":["../../src/mcp/jwt-capability-verifier.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n createRemoteJWKSet,\n errors,\n jwtVerify,\n} from 'jose';\nimport { z } from 'zod';\nimport {\n JwtAudienceSchema,\n JwtIssuerSchema,\n MCP_CAPABILITY_TYPE,\n McpAllowedToolsSchema,\n McpCapabilityClaimsSchema,\n McpServerIdSchema,\n OpaqueIdSchema,\n isSafeWebUrl,\n} from '../contracts/index.js';\nimport {\n McpCapabilityUnavailableError,\n McpCapabilityVerificationError,\n} from './errors.js';\nimport type {\n JwtMcpCapabilityVerifierConfig,\n JwtMcpCapabilityVerifierOptions,\n McpCapabilityVerifier,\n VerifiedMcpCapability,\n} from './types.js';\n\nconst CapabilityAssertionSchema = z.string().min(32).max(4_096);\nconst ConfigSchema = z.object({\n issuer: JwtIssuerSchema,\n audience: JwtAudienceSchema,\n jwksUrl: z.instanceof(URL).refine(\n isSafeWebUrl,\n 'must use HTTPS or loopback HTTP and contain no credentials, query, or fragment',\n ),\n trustedAdopterId: OpaqueIdSchema,\n serverId: McpServerIdSchema,\n supportedTools: McpAllowedToolsSchema,\n maxCapabilityAgeSeconds: z.number().int().min(1).max(15 * 60),\n clockToleranceSeconds: z.number().int().min(0).max(60).default(5),\n}).strict();\n\ntype ParsedConfig = Omit<z.infer<typeof ConfigSchema>, 'supportedTools'> & {\n supportedTools: readonly string[];\n};\n\n/**\n * Independently verifies an adopter-signed capability at the adopter's MCP\n * edge. Execution Host verification is defense in depth, not forwarded trust.\n */\nexport class JwtMcpCapabilityVerifier<TToolName extends string>\nimplements McpCapabilityVerifier<TToolName> {\n readonly #config: Readonly<ParsedConfig>;\n readonly #supportedTools: ReadonlySet<string>;\n readonly #keyResolver: NonNullable<JwtMcpCapabilityVerifierOptions['keyResolver']>;\n readonly #now: () => Date;\n\n constructor(\n config: JwtMcpCapabilityVerifierConfig<TToolName>,\n options: JwtMcpCapabilityVerifierOptions = {},\n ) {\n const parsed = ConfigSchema.parse(config);\n this.#config = Object.freeze({\n ...parsed,\n jwksUrl: new URL(parsed.jwksUrl),\n supportedTools: Object.freeze([...parsed.supportedTools]),\n });\n this.#supportedTools = new Set(this.#config.supportedTools);\n this.#keyResolver = options.keyResolver ?? createRemoteJWKSet(\n this.#config.jwksUrl,\n {\n cacheMaxAge: 10 * 60_000,\n cooldownDuration: 30_000,\n timeoutDuration: 3_000,\n },\n );\n this.#now = options.now ?? (() => new Date());\n }\n\n async verify(assertion: string): Promise<VerifiedMcpCapability<TToolName>> {\n try {\n const compactJwt = CapabilityAssertionSchema.parse(assertion);\n const { payload } = await jwtVerify(compactJwt, this.#keyResolver, {\n algorithms: ['ES256'],\n audience: this.#config.audience,\n clockTolerance: this.#config.clockToleranceSeconds,\n currentDate: this.#now(),\n issuer: this.#config.issuer,\n maxTokenAge: this.#config.maxCapabilityAgeSeconds,\n requiredClaims: ['exp', 'iat', 'jti', 'sub'],\n typ: MCP_CAPABILITY_TYPE,\n });\n const claims = McpCapabilityClaimsSchema.parse(payload);\n this.#assertDeploymentBinding(claims);\n this.#assertBoundedLifetime(claims);\n this.#assertSupportedTools(claims.allowedTools);\n\n return freezeVerifiedCapability({\n capabilityId: claims.jti,\n serverId: claims.serverId,\n allowedTools: claims.allowedTools as TToolName[],\n scope: {\n adopterId: claims.adopterId,\n tenantId: claims.tenantId,\n subjectId: claims.sub,\n productSessionId: claims.productSessionId,\n runtimeSessionId: claims.runtimeSessionId,\n invocationId: claims.invocationId,\n workflow: claims.workflow,\n },\n issuedAt: new Date(claims.iat * 1_000).toISOString(),\n expiresAt: new Date(claims.exp * 1_000).toISOString(),\n });\n } catch (error) {\n if (\n error instanceof McpCapabilityVerificationError\n || error instanceof McpCapabilityUnavailableError\n ) {\n throw error;\n }\n const unavailableCategory = resolveUnavailableCategory(error);\n if (unavailableCategory) {\n throw new McpCapabilityUnavailableError(unavailableCategory);\n }\n throw new McpCapabilityVerificationError();\n }\n }\n\n #assertDeploymentBinding(\n claims: z.infer<typeof McpCapabilityClaimsSchema>,\n ): void {\n if (\n claims.adopterId !== this.#config.trustedAdopterId\n || claims.serverId !== this.#config.serverId\n || claims.jti === claims.invocationId\n ) {\n throw new McpCapabilityVerificationError();\n }\n }\n\n #assertBoundedLifetime(\n claims: z.infer<typeof McpCapabilityClaimsSchema>,\n ): void {\n const lifetimeSeconds = claims.exp - claims.iat;\n if (\n lifetimeSeconds <= 0\n || lifetimeSeconds > this.#config.maxCapabilityAgeSeconds\n ) {\n throw new McpCapabilityVerificationError();\n }\n }\n\n #assertSupportedTools(tools: readonly string[]): void {\n if (!tools.every((tool) => this.#supportedTools.has(tool))) {\n throw new McpCapabilityVerificationError();\n }\n }\n}\n\nexport function assertMcpCapabilityActive(\n capability: VerifiedMcpCapability,\n now = new Date(),\n): void {\n const expiresAt = Date.parse(capability.expiresAt);\n if (!Number.isFinite(expiresAt) || now.getTime() >= expiresAt) {\n throw new McpCapabilityVerificationError();\n }\n}\n\nfunction freezeVerifiedCapability<TToolName extends string>(\n capability: VerifiedMcpCapability<TToolName>,\n): VerifiedMcpCapability<TToolName> {\n return Object.freeze({\n ...capability,\n allowedTools: Object.freeze([...capability.allowedTools]),\n scope: Object.freeze({ ...capability.scope }),\n });\n}\n\nconst UNAVAILABLE_JOSE_CODES = new Set([\n 'ERR_JOSE_GENERIC',\n 'ERR_JWK_INVALID',\n 'ERR_JWKS_INVALID',\n 'ERR_JWKS_MULTIPLE_MATCHING_KEYS',\n 'ERR_JWKS_TIMEOUT',\n]);\n\nfunction resolveUnavailableCategory(\n error: unknown,\n): 'network' | 'jwks' | undefined {\n if (error instanceof TypeError) {\n return 'network';\n }\n if (\n error instanceof errors.JOSEError\n && UNAVAILABLE_JOSE_CODES.has(error.code)\n ) {\n return 'jwks';\n }\n return undefined;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"jwt-capability-verifier.js","sourceRoot":"","sources":["../../src/mcp/jwt-capability-verifier.ts"],"names":[],"mappings":"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","sourcesContent":["import { jwtVerify } from 'jose';\nimport { z } from 'zod';\nimport {\n JwtAudienceSchema,\n JwtIssuerSchema,\n MCP_CAPABILITY_TYPE,\n McpAllowedToolsSchema,\n McpCapabilityClaimsSchema,\n McpServerIdSchema,\n OpaqueIdSchema,\n isSafeWebUrl,\n} from '../contracts/index.js';\nimport {\n McpCapabilityUnavailableError,\n McpCapabilityVerificationError,\n} from './errors.js';\nimport {\n createJwtKeyResolver,\n resolveJwtVerificationUnavailableCategory,\n} from '../internal/jwt-verification.js';\nimport type {\n JwtMcpCapabilityVerifierConfig,\n JwtMcpCapabilityVerifierOptions,\n McpCapabilityVerifier,\n VerifiedMcpCapability,\n} from './types.js';\n\nconst CapabilityAssertionSchema = z.string().min(32).max(4_096);\nconst ConfigSchema = z.object({\n issuer: JwtIssuerSchema,\n audience: JwtAudienceSchema,\n jwksUrl: z.instanceof(URL).refine(\n isSafeWebUrl,\n 'must use HTTPS or loopback HTTP and contain no credentials, query, or fragment',\n ),\n trustedAdopterId: OpaqueIdSchema,\n serverId: McpServerIdSchema,\n supportedTools: McpAllowedToolsSchema,\n maxCapabilityAgeSeconds: z.number().int().min(1).max(15 * 60),\n clockToleranceSeconds: z.number().int().min(0).max(60).default(5),\n}).strict();\n\ntype ParsedConfig = Omit<z.infer<typeof ConfigSchema>, 'supportedTools'> & {\n supportedTools: readonly string[];\n};\n\n/**\n * Independently verifies an adopter-signed capability at the adopter's MCP\n * edge. Execution Host verification is defense in depth, not forwarded trust.\n */\nexport class JwtMcpCapabilityVerifier<TToolName extends string>\nimplements McpCapabilityVerifier<TToolName> {\n readonly #config: Readonly<ParsedConfig>;\n readonly #supportedTools: ReadonlySet<string>;\n readonly #keyResolver: NonNullable<JwtMcpCapabilityVerifierOptions['keyResolver']>;\n readonly #now: () => Date;\n\n constructor(\n config: JwtMcpCapabilityVerifierConfig<TToolName>,\n options: JwtMcpCapabilityVerifierOptions = {},\n ) {\n const parsed = ConfigSchema.parse(config);\n this.#config = Object.freeze({\n ...parsed,\n jwksUrl: new URL(parsed.jwksUrl),\n supportedTools: Object.freeze([...parsed.supportedTools]),\n });\n this.#supportedTools = new Set(this.#config.supportedTools);\n this.#keyResolver = options.keyResolver\n ?? createJwtKeyResolver(this.#config.jwksUrl);\n this.#now = options.now ?? (() => new Date());\n }\n\n async verify(assertion: string): Promise<VerifiedMcpCapability<TToolName>> {\n try {\n const compactJwt = CapabilityAssertionSchema.parse(assertion);\n const { payload } = await jwtVerify(compactJwt, this.#keyResolver, {\n algorithms: ['ES256'],\n audience: this.#config.audience,\n clockTolerance: this.#config.clockToleranceSeconds,\n currentDate: this.#now(),\n issuer: this.#config.issuer,\n maxTokenAge: this.#config.maxCapabilityAgeSeconds,\n requiredClaims: ['exp', 'iat', 'jti', 'sub'],\n typ: MCP_CAPABILITY_TYPE,\n });\n const claims = McpCapabilityClaimsSchema.parse(payload);\n this.#assertDeploymentBinding(claims);\n this.#assertBoundedLifetime(claims);\n this.#assertSupportedTools(claims.allowedTools);\n\n return freezeVerifiedCapability({\n capabilityId: claims.jti,\n serverId: claims.serverId,\n allowedTools: claims.allowedTools as TToolName[],\n scope: {\n adopterId: claims.adopterId,\n tenantId: claims.tenantId,\n subjectId: claims.sub,\n productSessionId: claims.productSessionId,\n runtimeSessionId: claims.runtimeSessionId,\n invocationId: claims.invocationId,\n workflow: claims.workflow,\n },\n issuedAt: new Date(claims.iat * 1_000).toISOString(),\n expiresAt: new Date(claims.exp * 1_000).toISOString(),\n });\n } catch (error) {\n if (\n error instanceof McpCapabilityVerificationError\n || error instanceof McpCapabilityUnavailableError\n ) {\n throw error;\n }\n const unavailableCategory = resolveJwtVerificationUnavailableCategory(\n error,\n );\n if (unavailableCategory) {\n throw new McpCapabilityUnavailableError(\n unavailableCategory === 'network' ? 'network' : 'jwks',\n );\n }\n throw new McpCapabilityVerificationError();\n }\n }\n\n #assertDeploymentBinding(\n claims: z.infer<typeof McpCapabilityClaimsSchema>,\n ): void {\n if (\n claims.adopterId !== this.#config.trustedAdopterId\n || claims.serverId !== this.#config.serverId\n || claims.jti === claims.invocationId\n ) {\n throw new McpCapabilityVerificationError();\n }\n }\n\n #assertBoundedLifetime(\n claims: z.infer<typeof McpCapabilityClaimsSchema>,\n ): void {\n const lifetimeSeconds = claims.exp - claims.iat;\n if (\n lifetimeSeconds <= 0\n || lifetimeSeconds > this.#config.maxCapabilityAgeSeconds\n ) {\n throw new McpCapabilityVerificationError();\n }\n }\n\n #assertSupportedTools(tools: readonly string[]): void {\n if (!tools.every((tool) => this.#supportedTools.has(tool))) {\n throw new McpCapabilityVerificationError();\n }\n }\n}\n\nexport function assertMcpCapabilityActive(\n capability: VerifiedMcpCapability,\n now = new Date(),\n): void {\n const expiresAt = Date.parse(capability.expiresAt);\n if (!Number.isFinite(expiresAt) || now.getTime() >= expiresAt) {\n throw new McpCapabilityVerificationError();\n }\n}\n\nfunction freezeVerifiedCapability<TToolName extends string>(\n capability: VerifiedMcpCapability<TToolName>,\n): VerifiedMcpCapability<TToolName> {\n return Object.freeze({\n ...capability,\n allowedTools: Object.freeze([...capability.allowedTools]),\n scope: Object.freeze({ ...capability.scope }),\n });\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http-service.d.ts","sourceRoot":"","sources":["../../src/node/http-service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,eAAe,EACf,cAAc,EACf,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"http-service.d.ts","sourceRoot":"","sources":["../../src/node/http-service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,eAAe,EACf,cAAc,EACf,MAAM,WAAW,CAAC;AAcnB,OAAO,KAAK,EAEV,+BAA+B,EAE/B,qCAAqC,EAEtC,MAAM,YAAY,CAAC;AAEpB,eAAO,MAAM,yBAAyB,2BAA2B,CAAC;AAClE,eAAO,MAAM,uCAAuC,yCACZ,CAAC;AAgBzC;;;;;;;GAOG;AACH,qBAAa,+BAA+B,CAAC,SAAS,CACtD,YAAW,+BAA+B;;gBAwB5B,MAAM,EAAE,qCAAqC,CAAC,SAAS,CAAC;IAkBpE,MAAM,CAAC,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,cAAc,GAAG,OAAO;IAanE,UAAU,CAAC,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,cAAc,GAAG,IAAI;IAwBpE,sBAAsB,CACpB,OAAO,EAAE,eAAe,EACxB,QAAQ,EAAE,cAAc,GACvB,IAAI;IA0CP,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAmJvB"}
|
|
@@ -2,24 +2,15 @@ import { once } from 'node:events';
|
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import { ExecutionHostStreamEventSchema } from '../contracts/index.js';
|
|
4
4
|
import { ExecutionHostInvocationCancelledError } from '../http-sse/index.js';
|
|
5
|
+
import { NodeHttpPathSchema, NodeHttpRequestError, errorType, readJsonBody, readPathname, takeAuthorization, writeJson, writeJsonError, } from './http-utils.js';
|
|
5
6
|
export const DEFAULT_ADOPTER_JWKS_PATH = '/.well-known/jwks.json';
|
|
6
7
|
export const DEFAULT_ADOPTER_CONVERSATION_TURNS_PATH = '/hosted-execution/conversation-turns';
|
|
7
8
|
const DEFAULT_MAX_BODY_BYTES = 64 * 1_024;
|
|
8
9
|
const DEFAULT_MAX_PROMPT_CHARACTERS = 20_000;
|
|
9
|
-
const REDACTED_HEADER_VALUE = '[REDACTED]';
|
|
10
|
-
const MAX_AUTHORIZATION_CHARACTERS = 8_192;
|
|
11
|
-
const PathSchema = z.string().min(1).max(256).regex(/^\/(?:[^?#\s]*)$/, 'must be an absolute path without query, fragment, or whitespace');
|
|
12
10
|
const PublicErrorSchema = z.object({
|
|
13
11
|
statusCode: z.number().int().min(400).max(599),
|
|
14
12
|
message: z.string().min(1).max(1_600),
|
|
15
13
|
}).strict();
|
|
16
|
-
class AdopterHttpRequestError extends Error {
|
|
17
|
-
statusCode;
|
|
18
|
-
constructor(statusCode) {
|
|
19
|
-
super('Invalid adopter HTTP request.');
|
|
20
|
-
this.statusCode = statusCode;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
14
|
/**
|
|
24
15
|
* Standard Node HTTP edge for adopter-side JWKS and hosted conversations.
|
|
25
16
|
*
|
|
@@ -151,7 +142,7 @@ export class NodeExecutionAdopterHttpService {
|
|
|
151
142
|
}
|
|
152
143
|
catch (error) {
|
|
153
144
|
request.resume();
|
|
154
|
-
writeJsonError(response, error instanceof
|
|
145
|
+
writeJsonError(response, error instanceof NodeHttpRequestError ? error.statusCode : 400, 'Invalid conversation request.');
|
|
155
146
|
return;
|
|
156
147
|
}
|
|
157
148
|
let principal;
|
|
@@ -187,7 +178,7 @@ export class NodeExecutionAdopterHttpService {
|
|
|
187
178
|
}).strict().parse(body).prompt;
|
|
188
179
|
}
|
|
189
180
|
catch (error) {
|
|
190
|
-
const statusCode = error instanceof
|
|
181
|
+
const statusCode = error instanceof NodeHttpRequestError
|
|
191
182
|
? error.statusCode
|
|
192
183
|
: 400;
|
|
193
184
|
writeJsonError(response, statusCode, 'Invalid conversation request.');
|
|
@@ -263,85 +254,14 @@ export class NodeExecutionAdopterHttpService {
|
|
|
263
254
|
}
|
|
264
255
|
function parsePaths(paths) {
|
|
265
256
|
const parsed = {
|
|
266
|
-
jwks:
|
|
267
|
-
conversationTurns:
|
|
257
|
+
jwks: NodeHttpPathSchema.parse(paths?.jwks ?? DEFAULT_ADOPTER_JWKS_PATH),
|
|
258
|
+
conversationTurns: NodeHttpPathSchema.parse(paths?.conversationTurns ?? DEFAULT_ADOPTER_CONVERSATION_TURNS_PATH),
|
|
268
259
|
};
|
|
269
260
|
if (parsed.jwks === parsed.conversationTurns) {
|
|
270
261
|
throw new Error('Adopter HTTP paths must be distinct.');
|
|
271
262
|
}
|
|
272
263
|
return Object.freeze(parsed);
|
|
273
264
|
}
|
|
274
|
-
function readPathname(rawUrl) {
|
|
275
|
-
try {
|
|
276
|
-
return new URL(rawUrl ?? '/', 'http://localhost').pathname;
|
|
277
|
-
}
|
|
278
|
-
catch {
|
|
279
|
-
return undefined;
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
function takeAuthorization(request) {
|
|
283
|
-
const occurrences = request.rawHeaders.reduce((count, headerName, index) => count
|
|
284
|
-
+ (index % 2 === 0 && headerName.toLowerCase() === 'authorization' ? 1 : 0), 0);
|
|
285
|
-
const raw = request.headers.authorization;
|
|
286
|
-
request.headers.authorization = raw === undefined
|
|
287
|
-
? undefined
|
|
288
|
-
: REDACTED_HEADER_VALUE;
|
|
289
|
-
for (let index = 0; index < request.rawHeaders.length; index += 2) {
|
|
290
|
-
if (request.rawHeaders[index]?.toLowerCase() === 'authorization') {
|
|
291
|
-
request.rawHeaders[index + 1] = REDACTED_HEADER_VALUE;
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
if (occurrences > 1 || Array.isArray(raw)) {
|
|
295
|
-
throw new AdopterHttpRequestError(400);
|
|
296
|
-
}
|
|
297
|
-
if (raw && raw.length > MAX_AUTHORIZATION_CHARACTERS) {
|
|
298
|
-
throw new AdopterHttpRequestError(400);
|
|
299
|
-
}
|
|
300
|
-
return raw;
|
|
301
|
-
}
|
|
302
|
-
async function readJsonBody(request, maxBodyBytes, signal) {
|
|
303
|
-
const contentType = request.headers['content-type']
|
|
304
|
-
?.split(';', 1)[0]
|
|
305
|
-
?.trim()
|
|
306
|
-
.toLowerCase();
|
|
307
|
-
if (contentType !== 'application/json') {
|
|
308
|
-
request.resume();
|
|
309
|
-
throw new AdopterHttpRequestError(415);
|
|
310
|
-
}
|
|
311
|
-
const contentLength = request.headers['content-length'];
|
|
312
|
-
if (contentLength !== undefined) {
|
|
313
|
-
const declaredLength = Number(contentLength);
|
|
314
|
-
if (!Number.isSafeInteger(declaredLength) || declaredLength < 0) {
|
|
315
|
-
request.resume();
|
|
316
|
-
throw new AdopterHttpRequestError(400);
|
|
317
|
-
}
|
|
318
|
-
if (declaredLength > maxBodyBytes) {
|
|
319
|
-
request.resume();
|
|
320
|
-
throw new AdopterHttpRequestError(413);
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
const chunks = [];
|
|
324
|
-
let receivedBytes = 0;
|
|
325
|
-
for await (const chunk of request) {
|
|
326
|
-
signal.throwIfAborted();
|
|
327
|
-
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
328
|
-
receivedBytes += buffer.byteLength;
|
|
329
|
-
if (receivedBytes > maxBodyBytes) {
|
|
330
|
-
request.resume();
|
|
331
|
-
throw new AdopterHttpRequestError(413);
|
|
332
|
-
}
|
|
333
|
-
chunks.push(buffer);
|
|
334
|
-
}
|
|
335
|
-
if (!request.complete || receivedBytes === 0) {
|
|
336
|
-
throw new AdopterHttpRequestError(400);
|
|
337
|
-
}
|
|
338
|
-
try {
|
|
339
|
-
return JSON.parse(Buffer.concat(chunks, receivedBytes).toString('utf8'));
|
|
340
|
-
}
|
|
341
|
-
catch {
|
|
342
|
-
throw new AdopterHttpRequestError(400);
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
265
|
function writeEventStreamHeaders(response) {
|
|
346
266
|
response.writeHead(200, {
|
|
347
267
|
'Cache-Control': 'no-store',
|
|
@@ -363,27 +283,4 @@ async function writeSseEvent(response, event, signal) {
|
|
|
363
283
|
await once(response, 'drain', { signal });
|
|
364
284
|
}
|
|
365
285
|
}
|
|
366
|
-
function writeJsonError(response, statusCode, message, headers = {}) {
|
|
367
|
-
writeJson(response, statusCode, { error: { message } }, {
|
|
368
|
-
'Cache-Control': 'no-store',
|
|
369
|
-
...headers,
|
|
370
|
-
});
|
|
371
|
-
}
|
|
372
|
-
function writeJson(response, statusCode, value, headers = {}) {
|
|
373
|
-
if (response.headersSent || response.destroyed) {
|
|
374
|
-
return;
|
|
375
|
-
}
|
|
376
|
-
const body = JSON.stringify(value);
|
|
377
|
-
response.writeHead(statusCode, {
|
|
378
|
-
'Cache-Control': 'no-store',
|
|
379
|
-
'Content-Length': Buffer.byteLength(body),
|
|
380
|
-
'Content-Type': 'application/json; charset=utf-8',
|
|
381
|
-
'X-Content-Type-Options': 'nosniff',
|
|
382
|
-
...headers,
|
|
383
|
-
});
|
|
384
|
-
response.end(body);
|
|
385
|
-
}
|
|
386
|
-
function errorType(error) {
|
|
387
|
-
return error instanceof Error ? error.name : 'unknown';
|
|
388
|
-
}
|
|
389
286
|
//# sourceMappingURL=http-service.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http-service.js","sourceRoot":"","sources":["../../src/node/http-service.ts"],"names":[],"mappings":"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","sourcesContent":["import { once } from 'node:events';\nimport type {\n IncomingMessage,\n ServerResponse,\n} from 'node:http';\nimport { z } from 'zod';\nimport { ExecutionHostStreamEventSchema } from '../contracts/index.js';\nimport { ExecutionHostInvocationCancelledError } from '../http-sse/index.js';\nimport type {\n NodeExecutionAdopterFailure,\n NodeExecutionAdopterHttpHandler,\n NodeExecutionAdopterHttpPaths,\n NodeExecutionAdopterHttpServiceConfig,\n NodeExecutionAdopterPublicError,\n} from './types.js';\n\nexport const DEFAULT_ADOPTER_JWKS_PATH = '/.well-known/jwks.json';\nexport const DEFAULT_ADOPTER_CONVERSATION_TURNS_PATH =\n '/hosted-execution/conversation-turns';\n\nconst DEFAULT_MAX_BODY_BYTES = 64 * 1_024;\nconst DEFAULT_MAX_PROMPT_CHARACTERS = 20_000;\nconst REDACTED_HEADER_VALUE = '[REDACTED]';\nconst MAX_AUTHORIZATION_CHARACTERS = 8_192;\nconst PathSchema = z.string().min(1).max(256).regex(\n /^\\/(?:[^?#\\s]*)$/,\n 'must be an absolute path without query, fragment, or whitespace',\n);\nconst PublicErrorSchema = z.object({\n statusCode: z.number().int().min(400).max(599),\n message: z.string().min(1).max(1_600),\n}).strict();\n\ntype ActiveConversation = {\n abortController: AbortController;\n request: IncomingMessage;\n response: ServerResponse;\n pending: Promise<void>;\n};\n\nclass AdopterHttpRequestError extends Error {\n constructor(readonly statusCode: 400 | 413 | 415) {\n super('Invalid adopter HTTP request.');\n }\n}\n\n/**\n * Standard Node HTTP edge for adopter-side JWKS and hosted conversations.\n *\n * The service owns bounded parsing, credential-header scrubbing, SSE framing,\n * disconnect cancellation, safe failures, and graceful shutdown. Product\n * authentication, authorization, identity selection, and persistence stay in\n * adopter callbacks.\n */\nexport class NodeExecutionAdopterHttpService<Principal>\nimplements NodeExecutionAdopterHttpHandler {\n readonly #authority: NodeExecutionAdopterHttpServiceConfig<Principal>[\n 'authority'\n ];\n readonly #authenticator: NodeExecutionAdopterHttpServiceConfig<Principal>[\n 'authenticator'\n ];\n readonly #conversations: NodeExecutionAdopterHttpServiceConfig<\n Principal\n >['conversations'];\n readonly #projectError: NodeExecutionAdopterHttpServiceConfig<Principal>[\n 'projectError'\n ];\n readonly #reportFailure: NodeExecutionAdopterHttpServiceConfig<Principal>[\n 'reportFailure'\n ];\n readonly #paths: Readonly<NodeExecutionAdopterHttpPaths>;\n readonly #maxBodyBytes: number;\n readonly #maxPromptCharacters: number;\n readonly #jwksMaxAgeSeconds: number;\n readonly #activeConversations = new Set<ActiveConversation>();\n #closed = false;\n #closePromise: Promise<void> | undefined;\n\n constructor(config: NodeExecutionAdopterHttpServiceConfig<Principal>) {\n this.#authority = config.authority;\n this.#authenticator = config.authenticator;\n this.#conversations = config.conversations;\n this.#projectError = config.projectError;\n this.#reportFailure = config.reportFailure;\n this.#paths = parsePaths(config.paths);\n this.#maxBodyBytes = z.number().int().min(1).max(1_048_576).parse(\n config.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES,\n );\n this.#maxPromptCharacters = z.number().int().min(1).max(200_000).parse(\n config.maxPromptCharacters ?? DEFAULT_MAX_PROMPT_CHARACTERS,\n );\n this.#jwksMaxAgeSeconds = z.number().int().min(0).max(86_400).parse(\n config.jwksMaxAgeSeconds ?? 60,\n );\n }\n\n handle(request: IncomingMessage, response: ServerResponse): boolean {\n const pathname = readPathname(request.url);\n if (pathname === this.#paths.jwks) {\n this.handleJwks(request, response);\n return true;\n }\n if (pathname === this.#paths.conversationTurns) {\n this.handleConversationTurn(request, response);\n return true;\n }\n return false;\n }\n\n handleJwks(request: IncomingMessage, response: ServerResponse): void {\n request.resume();\n if (this.#closed) {\n writeJsonError(response, 503, 'Hosted execution is unavailable.');\n return;\n }\n if (request.method !== 'GET') {\n writeJsonError(response, 405, 'Method not allowed.');\n return;\n }\n try {\n writeJson(response, 200, this.#authority.publicJwks(), {\n 'Cache-Control': `public, max-age=${this.#jwksMaxAgeSeconds}`,\n });\n } catch (error) {\n this.#report({\n phase: 'jwks',\n errorType: errorType(error),\n streamAccepted: false,\n });\n writeJsonError(response, 500, 'Verification keys are unavailable.');\n }\n }\n\n handleConversationTurn(\n request: IncomingMessage,\n response: ServerResponse,\n ): void {\n if (this.#closed) {\n request.resume();\n writeJsonError(response, 503, 'Hosted execution is unavailable.');\n return;\n }\n\n const abortController = new AbortController();\n const abort = () => abortController.abort(\n new Error('The owning adopter HTTP request closed.'),\n );\n request.once('aborted', abort);\n response.once('close', abort);\n const active: ActiveConversation = {\n abortController,\n request,\n response,\n pending: Promise.resolve(),\n };\n active.pending = this.#serveConversation(\n request,\n response,\n abortController.signal,\n ).catch((error) => {\n this.#report({\n phase: 'conversation',\n errorType: errorType(error),\n streamAccepted: response.headersSent,\n });\n if (!response.headersSent) {\n writeJsonError(response, 500, 'Hosted execution request failed.');\n } else if (!response.destroyed && !response.writableEnded) {\n response.end();\n }\n }).finally(() => {\n request.removeListener('aborted', abort);\n response.removeListener('close', abort);\n this.#activeConversations.delete(active);\n });\n this.#activeConversations.add(active);\n }\n\n close(): Promise<void> {\n if (!this.#closePromise) {\n this.#closed = true;\n this.#closePromise = this.#closeActiveConversations();\n }\n return this.#closePromise;\n }\n\n async #closeActiveConversations(): Promise<void> {\n const active = [...this.#activeConversations];\n active.forEach(({ abortController, request, response }) => {\n abortController.abort(new Error('The adopter HTTP service is stopping.'));\n request.destroy();\n response.destroy();\n });\n await Promise.allSettled(active.map(({ pending }) => pending));\n }\n\n async #serveConversation(\n request: IncomingMessage,\n response: ServerResponse,\n signal: AbortSignal,\n ): Promise<void> {\n let authorization: string | undefined;\n try {\n authorization = takeAuthorization(request);\n if (request.method !== 'POST') {\n request.resume();\n writeJsonError(response, 405, 'Method not allowed.');\n return;\n }\n } catch (error) {\n request.resume();\n writeJsonError(\n response,\n error instanceof AdopterHttpRequestError ? error.statusCode : 400,\n 'Invalid conversation request.',\n );\n return;\n }\n\n let principal: Principal | undefined;\n try {\n principal = await this.#authenticator.authenticate({\n authorization,\n remoteAddress: request.socket.remoteAddress,\n signal,\n });\n } catch (error) {\n this.#report({\n phase: 'authentication',\n errorType: errorType(error),\n streamAccepted: false,\n });\n request.resume();\n writeJsonError(response, 500, 'Authentication could not be completed.');\n return;\n }\n if (!principal) {\n request.resume();\n writeJsonError(response, 401, 'Authentication is required.', {\n 'WWW-Authenticate': 'Bearer',\n });\n return;\n }\n\n let prompt: string;\n try {\n const body = await readJsonBody(request, this.#maxBodyBytes, signal);\n prompt = z.object({\n prompt: z.string().trim().min(1).max(this.#maxPromptCharacters),\n }).strict().parse(body).prompt;\n } catch (error) {\n const statusCode = error instanceof AdopterHttpRequestError\n ? error.statusCode\n : 400;\n writeJsonError(response, statusCode, 'Invalid conversation request.');\n return;\n }\n\n let streamed = false;\n try {\n for await (const rawEvent of this.#conversations.streamTurn({\n principal,\n prompt,\n signal,\n })) {\n const event = ExecutionHostStreamEventSchema.parse(rawEvent);\n if (!streamed) {\n writeEventStreamHeaders(response);\n streamed = true;\n }\n await writeSseEvent(response, event, signal);\n }\n if (!streamed) {\n writeJsonError(response, 502, 'Execution Host returned no events.');\n return;\n }\n response.end();\n } catch (error) {\n const publicError = !streamed ? this.#project(error) : undefined;\n if (publicError) {\n writeJsonError(response, publicError.statusCode, publicError.message);\n return;\n }\n if (\n error instanceof ExecutionHostInvocationCancelledError\n || signal.aborted\n ) {\n if (!response.headersSent && !response.destroyed) {\n writeJsonError(response, 499, 'Hosted conversation was cancelled.');\n }\n return;\n }\n this.#report({\n phase: 'conversation',\n errorType: errorType(error),\n streamAccepted: streamed,\n });\n if (!response.headersSent) {\n writeJsonError(response, 502, 'Execution Host conversation failed.');\n } else if (!response.destroyed && !response.writableEnded) {\n // A post-acceptance failure deliberately ends without a terminal event.\n response.end();\n }\n }\n }\n\n #project(error: unknown): NodeExecutionAdopterPublicError | undefined {\n if (!this.#projectError) {\n return undefined;\n }\n try {\n const projected = this.#projectError(error);\n return projected ? PublicErrorSchema.parse(projected) : undefined;\n } catch {\n return undefined;\n }\n }\n\n #report(failure: NodeExecutionAdopterFailure): void {\n try {\n this.#reportFailure?.(Object.freeze({ ...failure }));\n } catch {\n // Observability must not change request settlement.\n }\n }\n}\n\nfunction parsePaths(\n paths: Partial<NodeExecutionAdopterHttpPaths> | undefined,\n): Readonly<NodeExecutionAdopterHttpPaths> {\n const parsed = {\n jwks: PathSchema.parse(paths?.jwks ?? DEFAULT_ADOPTER_JWKS_PATH),\n conversationTurns: PathSchema.parse(\n paths?.conversationTurns ?? DEFAULT_ADOPTER_CONVERSATION_TURNS_PATH,\n ),\n };\n if (parsed.jwks === parsed.conversationTurns) {\n throw new Error('Adopter HTTP paths must be distinct.');\n }\n return Object.freeze(parsed);\n}\n\nfunction readPathname(rawUrl: string | undefined): string | undefined {\n try {\n return new URL(rawUrl ?? '/', 'http://localhost').pathname;\n } catch {\n return undefined;\n }\n}\n\nfunction takeAuthorization(request: IncomingMessage): string | undefined {\n const occurrences = request.rawHeaders.reduce(\n (count, headerName, index) => count\n + (index % 2 === 0 && headerName.toLowerCase() === 'authorization' ? 1 : 0),\n 0,\n );\n const raw = request.headers.authorization;\n request.headers.authorization = raw === undefined\n ? undefined\n : REDACTED_HEADER_VALUE;\n for (let index = 0; index < request.rawHeaders.length; index += 2) {\n if (request.rawHeaders[index]?.toLowerCase() === 'authorization') {\n request.rawHeaders[index + 1] = REDACTED_HEADER_VALUE;\n }\n }\n if (occurrences > 1 || Array.isArray(raw)) {\n throw new AdopterHttpRequestError(400);\n }\n if (raw && raw.length > MAX_AUTHORIZATION_CHARACTERS) {\n throw new AdopterHttpRequestError(400);\n }\n return raw;\n}\n\nasync function readJsonBody(\n request: IncomingMessage,\n maxBodyBytes: number,\n signal: AbortSignal,\n): Promise<unknown> {\n const contentType = request.headers['content-type']\n ?.split(';', 1)[0]\n ?.trim()\n .toLowerCase();\n if (contentType !== 'application/json') {\n request.resume();\n throw new AdopterHttpRequestError(415);\n }\n const contentLength = request.headers['content-length'];\n if (contentLength !== undefined) {\n const declaredLength = Number(contentLength);\n if (!Number.isSafeInteger(declaredLength) || declaredLength < 0) {\n request.resume();\n throw new AdopterHttpRequestError(400);\n }\n if (declaredLength > maxBodyBytes) {\n request.resume();\n throw new AdopterHttpRequestError(413);\n }\n }\n\n const chunks: Buffer[] = [];\n let receivedBytes = 0;\n for await (const chunk of request) {\n signal.throwIfAborted();\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);\n receivedBytes += buffer.byteLength;\n if (receivedBytes > maxBodyBytes) {\n request.resume();\n throw new AdopterHttpRequestError(413);\n }\n chunks.push(buffer);\n }\n if (!request.complete || receivedBytes === 0) {\n throw new AdopterHttpRequestError(400);\n }\n try {\n return JSON.parse(\n Buffer.concat(chunks, receivedBytes).toString('utf8'),\n ) as unknown;\n } catch {\n throw new AdopterHttpRequestError(400);\n }\n}\n\nfunction writeEventStreamHeaders(response: ServerResponse): void {\n response.writeHead(200, {\n 'Cache-Control': 'no-store',\n 'Content-Type': 'text/event-stream; charset=utf-8',\n Connection: 'keep-alive',\n 'X-Accel-Buffering': 'no',\n 'X-Content-Type-Options': 'nosniff',\n });\n}\n\nasync function writeSseEvent(\n response: ServerResponse,\n event: z.infer<typeof ExecutionHostStreamEventSchema>,\n signal: AbortSignal,\n): Promise<void> {\n const frame = [\n `event: ${event.kind}`,\n `id: ${event.sequence}`,\n `data: ${JSON.stringify(event)}`,\n '',\n '',\n ].join('\\n');\n if (!response.write(frame)) {\n await once(response, 'drain', { signal });\n }\n}\n\nfunction writeJsonError(\n response: ServerResponse,\n statusCode: number,\n message: string,\n headers: Record<string, string> = {},\n): void {\n writeJson(response, statusCode, { error: { message } }, {\n 'Cache-Control': 'no-store',\n ...headers,\n });\n}\n\nfunction writeJson(\n response: ServerResponse,\n statusCode: number,\n value: unknown,\n headers: Record<string, string> = {},\n): void {\n if (response.headersSent || response.destroyed) {\n return;\n }\n const body = JSON.stringify(value);\n response.writeHead(statusCode, {\n 'Cache-Control': 'no-store',\n 'Content-Length': Buffer.byteLength(body),\n 'Content-Type': 'application/json; charset=utf-8',\n 'X-Content-Type-Options': 'nosniff',\n ...headers,\n });\n response.end(body);\n}\n\nfunction errorType(error: unknown): string {\n return error instanceof Error ? error.name : 'unknown';\n}\n"]}
|
|
1
|
+
{"version":3,"file":"http-service.js","sourceRoot":"","sources":["../../src/node/http-service.ts"],"names":[],"mappings":"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","sourcesContent":["import { once } from 'node:events';\nimport type {\n IncomingMessage,\n ServerResponse,\n} from 'node:http';\nimport { z } from 'zod';\nimport { ExecutionHostStreamEventSchema } from '../contracts/index.js';\nimport { ExecutionHostInvocationCancelledError } from '../http-sse/index.js';\nimport {\n NodeHttpPathSchema,\n NodeHttpRequestError,\n errorType,\n readJsonBody,\n readPathname,\n takeAuthorization,\n writeJson,\n writeJsonError,\n} from './http-utils.js';\nimport type {\n NodeExecutionAdopterFailure,\n NodeExecutionAdopterHttpHandler,\n NodeExecutionAdopterHttpPaths,\n NodeExecutionAdopterHttpServiceConfig,\n NodeExecutionAdopterPublicError,\n} from './types.js';\n\nexport const DEFAULT_ADOPTER_JWKS_PATH = '/.well-known/jwks.json';\nexport const DEFAULT_ADOPTER_CONVERSATION_TURNS_PATH =\n '/hosted-execution/conversation-turns';\n\nconst DEFAULT_MAX_BODY_BYTES = 64 * 1_024;\nconst DEFAULT_MAX_PROMPT_CHARACTERS = 20_000;\nconst PublicErrorSchema = z.object({\n statusCode: z.number().int().min(400).max(599),\n message: z.string().min(1).max(1_600),\n}).strict();\n\ntype ActiveConversation = {\n abortController: AbortController;\n request: IncomingMessage;\n response: ServerResponse;\n pending: Promise<void>;\n};\n\n/**\n * Standard Node HTTP edge for adopter-side JWKS and hosted conversations.\n *\n * The service owns bounded parsing, credential-header scrubbing, SSE framing,\n * disconnect cancellation, safe failures, and graceful shutdown. Product\n * authentication, authorization, identity selection, and persistence stay in\n * adopter callbacks.\n */\nexport class NodeExecutionAdopterHttpService<Principal>\nimplements NodeExecutionAdopterHttpHandler {\n readonly #authority: NodeExecutionAdopterHttpServiceConfig<Principal>[\n 'authority'\n ];\n readonly #authenticator: NodeExecutionAdopterHttpServiceConfig<Principal>[\n 'authenticator'\n ];\n readonly #conversations: NodeExecutionAdopterHttpServiceConfig<\n Principal\n >['conversations'];\n readonly #projectError: NodeExecutionAdopterHttpServiceConfig<Principal>[\n 'projectError'\n ];\n readonly #reportFailure: NodeExecutionAdopterHttpServiceConfig<Principal>[\n 'reportFailure'\n ];\n readonly #paths: Readonly<NodeExecutionAdopterHttpPaths>;\n readonly #maxBodyBytes: number;\n readonly #maxPromptCharacters: number;\n readonly #jwksMaxAgeSeconds: number;\n readonly #activeConversations = new Set<ActiveConversation>();\n #closed = false;\n #closePromise: Promise<void> | undefined;\n\n constructor(config: NodeExecutionAdopterHttpServiceConfig<Principal>) {\n this.#authority = config.authority;\n this.#authenticator = config.authenticator;\n this.#conversations = config.conversations;\n this.#projectError = config.projectError;\n this.#reportFailure = config.reportFailure;\n this.#paths = parsePaths(config.paths);\n this.#maxBodyBytes = z.number().int().min(1).max(1_048_576).parse(\n config.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES,\n );\n this.#maxPromptCharacters = z.number().int().min(1).max(200_000).parse(\n config.maxPromptCharacters ?? DEFAULT_MAX_PROMPT_CHARACTERS,\n );\n this.#jwksMaxAgeSeconds = z.number().int().min(0).max(86_400).parse(\n config.jwksMaxAgeSeconds ?? 60,\n );\n }\n\n handle(request: IncomingMessage, response: ServerResponse): boolean {\n const pathname = readPathname(request.url);\n if (pathname === this.#paths.jwks) {\n this.handleJwks(request, response);\n return true;\n }\n if (pathname === this.#paths.conversationTurns) {\n this.handleConversationTurn(request, response);\n return true;\n }\n return false;\n }\n\n handleJwks(request: IncomingMessage, response: ServerResponse): void {\n request.resume();\n if (this.#closed) {\n writeJsonError(response, 503, 'Hosted execution is unavailable.');\n return;\n }\n if (request.method !== 'GET') {\n writeJsonError(response, 405, 'Method not allowed.');\n return;\n }\n try {\n writeJson(response, 200, this.#authority.publicJwks(), {\n 'Cache-Control': `public, max-age=${this.#jwksMaxAgeSeconds}`,\n });\n } catch (error) {\n this.#report({\n phase: 'jwks',\n errorType: errorType(error),\n streamAccepted: false,\n });\n writeJsonError(response, 500, 'Verification keys are unavailable.');\n }\n }\n\n handleConversationTurn(\n request: IncomingMessage,\n response: ServerResponse,\n ): void {\n if (this.#closed) {\n request.resume();\n writeJsonError(response, 503, 'Hosted execution is unavailable.');\n return;\n }\n\n const abortController = new AbortController();\n const abort = () => abortController.abort(\n new Error('The owning adopter HTTP request closed.'),\n );\n request.once('aborted', abort);\n response.once('close', abort);\n const active: ActiveConversation = {\n abortController,\n request,\n response,\n pending: Promise.resolve(),\n };\n active.pending = this.#serveConversation(\n request,\n response,\n abortController.signal,\n ).catch((error) => {\n this.#report({\n phase: 'conversation',\n errorType: errorType(error),\n streamAccepted: response.headersSent,\n });\n if (!response.headersSent) {\n writeJsonError(response, 500, 'Hosted execution request failed.');\n } else if (!response.destroyed && !response.writableEnded) {\n response.end();\n }\n }).finally(() => {\n request.removeListener('aborted', abort);\n response.removeListener('close', abort);\n this.#activeConversations.delete(active);\n });\n this.#activeConversations.add(active);\n }\n\n close(): Promise<void> {\n if (!this.#closePromise) {\n this.#closed = true;\n this.#closePromise = this.#closeActiveConversations();\n }\n return this.#closePromise;\n }\n\n async #closeActiveConversations(): Promise<void> {\n const active = [...this.#activeConversations];\n active.forEach(({ abortController, request, response }) => {\n abortController.abort(new Error('The adopter HTTP service is stopping.'));\n request.destroy();\n response.destroy();\n });\n await Promise.allSettled(active.map(({ pending }) => pending));\n }\n\n async #serveConversation(\n request: IncomingMessage,\n response: ServerResponse,\n signal: AbortSignal,\n ): Promise<void> {\n let authorization: string | undefined;\n try {\n authorization = takeAuthorization(request);\n if (request.method !== 'POST') {\n request.resume();\n writeJsonError(response, 405, 'Method not allowed.');\n return;\n }\n } catch (error) {\n request.resume();\n writeJsonError(\n response,\n error instanceof NodeHttpRequestError ? error.statusCode : 400,\n 'Invalid conversation request.',\n );\n return;\n }\n\n let principal: Principal | undefined;\n try {\n principal = await this.#authenticator.authenticate({\n authorization,\n remoteAddress: request.socket.remoteAddress,\n signal,\n });\n } catch (error) {\n this.#report({\n phase: 'authentication',\n errorType: errorType(error),\n streamAccepted: false,\n });\n request.resume();\n writeJsonError(response, 500, 'Authentication could not be completed.');\n return;\n }\n if (!principal) {\n request.resume();\n writeJsonError(response, 401, 'Authentication is required.', {\n 'WWW-Authenticate': 'Bearer',\n });\n return;\n }\n\n let prompt: string;\n try {\n const body = await readJsonBody(request, this.#maxBodyBytes, signal);\n prompt = z.object({\n prompt: z.string().trim().min(1).max(this.#maxPromptCharacters),\n }).strict().parse(body).prompt;\n } catch (error) {\n const statusCode = error instanceof NodeHttpRequestError\n ? error.statusCode\n : 400;\n writeJsonError(response, statusCode, 'Invalid conversation request.');\n return;\n }\n\n let streamed = false;\n try {\n for await (const rawEvent of this.#conversations.streamTurn({\n principal,\n prompt,\n signal,\n })) {\n const event = ExecutionHostStreamEventSchema.parse(rawEvent);\n if (!streamed) {\n writeEventStreamHeaders(response);\n streamed = true;\n }\n await writeSseEvent(response, event, signal);\n }\n if (!streamed) {\n writeJsonError(response, 502, 'Execution Host returned no events.');\n return;\n }\n response.end();\n } catch (error) {\n const publicError = !streamed ? this.#project(error) : undefined;\n if (publicError) {\n writeJsonError(response, publicError.statusCode, publicError.message);\n return;\n }\n if (\n error instanceof ExecutionHostInvocationCancelledError\n || signal.aborted\n ) {\n if (!response.headersSent && !response.destroyed) {\n writeJsonError(response, 499, 'Hosted conversation was cancelled.');\n }\n return;\n }\n this.#report({\n phase: 'conversation',\n errorType: errorType(error),\n streamAccepted: streamed,\n });\n if (!response.headersSent) {\n writeJsonError(response, 502, 'Execution Host conversation failed.');\n } else if (!response.destroyed && !response.writableEnded) {\n // A post-acceptance failure deliberately ends without a terminal event.\n response.end();\n }\n }\n }\n\n #project(error: unknown): NodeExecutionAdopterPublicError | undefined {\n if (!this.#projectError) {\n return undefined;\n }\n try {\n const projected = this.#projectError(error);\n return projected ? PublicErrorSchema.parse(projected) : undefined;\n } catch {\n return undefined;\n }\n }\n\n #report(failure: NodeExecutionAdopterFailure): void {\n try {\n this.#reportFailure?.(Object.freeze({ ...failure }));\n } catch {\n // Observability must not change request settlement.\n }\n }\n}\n\nfunction parsePaths(\n paths: Partial<NodeExecutionAdopterHttpPaths> | undefined,\n): Readonly<NodeExecutionAdopterHttpPaths> {\n const parsed = {\n jwks: NodeHttpPathSchema.parse(paths?.jwks ?? DEFAULT_ADOPTER_JWKS_PATH),\n conversationTurns: NodeHttpPathSchema.parse(\n paths?.conversationTurns ?? DEFAULT_ADOPTER_CONVERSATION_TURNS_PATH,\n ),\n };\n if (parsed.jwks === parsed.conversationTurns) {\n throw new Error('Adopter HTTP paths must be distinct.');\n }\n return Object.freeze(parsed);\n}\n\nfunction writeEventStreamHeaders(response: ServerResponse): void {\n response.writeHead(200, {\n 'Cache-Control': 'no-store',\n 'Content-Type': 'text/event-stream; charset=utf-8',\n Connection: 'keep-alive',\n 'X-Accel-Buffering': 'no',\n 'X-Content-Type-Options': 'nosniff',\n });\n}\n\nasync function writeSseEvent(\n response: ServerResponse,\n event: z.infer<typeof ExecutionHostStreamEventSchema>,\n signal: AbortSignal,\n): Promise<void> {\n const frame = [\n `event: ${event.kind}`,\n `id: ${event.sequence}`,\n `data: ${JSON.stringify(event)}`,\n '',\n '',\n ].join('\\n');\n if (!response.write(frame)) {\n await once(response, 'drain', { signal });\n }\n}\n"]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
export declare const NodeHttpPathSchema: z.ZodString;
|
|
4
|
+
export declare class NodeHttpRequestError extends Error {
|
|
5
|
+
readonly statusCode: 400 | 413 | 415;
|
|
6
|
+
readonly name = "NodeHttpRequestError";
|
|
7
|
+
constructor(statusCode: 400 | 413 | 415);
|
|
8
|
+
}
|
|
9
|
+
export declare function readPathname(rawUrl: string | undefined): string | undefined;
|
|
10
|
+
export declare function takeAuthorization(request: IncomingMessage): string | undefined;
|
|
11
|
+
export declare function readJsonBody(request: IncomingMessage, maxBodyBytes: number, signal: AbortSignal): Promise<unknown>;
|
|
12
|
+
export declare function writeJsonError(response: ServerResponse, statusCode: number, message: string, headers?: Record<string, string>): void;
|
|
13
|
+
export declare function writeJson(response: ServerResponse, statusCode: number, value: unknown, headers?: Record<string, string>): void;
|
|
14
|
+
export declare function errorType(error: unknown): string;
|
|
15
|
+
//# sourceMappingURL=http-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-utils.d.ts","sourceRoot":"","sources":["../../src/node/http-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACjE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,eAAO,MAAM,kBAAkB,aAG9B,CAAC;AAEF,qBAAa,oBAAqB,SAAQ,KAAK;IAGjC,QAAQ,CAAC,UAAU,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG;IAFhD,QAAQ,CAAC,IAAI,0BAA0B;gBAElB,UAAU,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG;CAGjD;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAM3E;AAED,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,eAAe,GACvB,MAAM,GAAG,SAAS,CAwBpB;AAED,wBAAsB,YAAY,CAChC,OAAO,EAAE,eAAe,EACxB,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,OAAO,CAAC,CA4ClB;AAED,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,cAAc,EACxB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GACnC,IAAI,CAKN;AAED,wBAAgB,SAAS,CACvB,QAAQ,EAAE,cAAc,EACxB,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,OAAO,EACd,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GACnC,IAAI,CAaN;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEhD"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
const REDACTED_HEADER_VALUE = '[REDACTED]';
|
|
3
|
+
const MAX_AUTHORIZATION_CHARACTERS = 8_192;
|
|
4
|
+
export const NodeHttpPathSchema = z.string().min(1).max(256).regex(/^\/(?:[^?#\s]*)$/, 'must be an absolute path without query, fragment, or whitespace');
|
|
5
|
+
export class NodeHttpRequestError extends Error {
|
|
6
|
+
statusCode;
|
|
7
|
+
name = 'NodeHttpRequestError';
|
|
8
|
+
constructor(statusCode) {
|
|
9
|
+
super('Invalid Node HTTP request.');
|
|
10
|
+
this.statusCode = statusCode;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export function readPathname(rawUrl) {
|
|
14
|
+
try {
|
|
15
|
+
return new URL(rawUrl ?? '/', 'http://localhost').pathname;
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export function takeAuthorization(request) {
|
|
22
|
+
const occurrences = request.rawHeaders.reduce((count, headerName, index) => count
|
|
23
|
+
+ (index % 2 === 0 && headerName.toLowerCase() === 'authorization'
|
|
24
|
+
? 1
|
|
25
|
+
: 0), 0);
|
|
26
|
+
const raw = request.headers.authorization;
|
|
27
|
+
request.headers.authorization = raw === undefined
|
|
28
|
+
? undefined
|
|
29
|
+
: REDACTED_HEADER_VALUE;
|
|
30
|
+
for (let index = 0; index < request.rawHeaders.length; index += 2) {
|
|
31
|
+
if (request.rawHeaders[index]?.toLowerCase() === 'authorization') {
|
|
32
|
+
request.rawHeaders[index + 1] = REDACTED_HEADER_VALUE;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (occurrences > 1 || Array.isArray(raw)) {
|
|
36
|
+
throw new NodeHttpRequestError(400);
|
|
37
|
+
}
|
|
38
|
+
if (raw && raw.length > MAX_AUTHORIZATION_CHARACTERS) {
|
|
39
|
+
throw new NodeHttpRequestError(400);
|
|
40
|
+
}
|
|
41
|
+
return raw;
|
|
42
|
+
}
|
|
43
|
+
export async function readJsonBody(request, maxBodyBytes, signal) {
|
|
44
|
+
const contentType = request.headers['content-type']
|
|
45
|
+
?.split(';', 1)[0]
|
|
46
|
+
?.trim()
|
|
47
|
+
.toLowerCase();
|
|
48
|
+
if (contentType !== 'application/json') {
|
|
49
|
+
request.resume();
|
|
50
|
+
throw new NodeHttpRequestError(415);
|
|
51
|
+
}
|
|
52
|
+
const contentLength = request.headers['content-length'];
|
|
53
|
+
if (contentLength !== undefined) {
|
|
54
|
+
const declaredLength = Number(contentLength);
|
|
55
|
+
if (!Number.isSafeInteger(declaredLength) || declaredLength < 0) {
|
|
56
|
+
request.resume();
|
|
57
|
+
throw new NodeHttpRequestError(400);
|
|
58
|
+
}
|
|
59
|
+
if (declaredLength > maxBodyBytes) {
|
|
60
|
+
request.resume();
|
|
61
|
+
throw new NodeHttpRequestError(413);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const chunks = [];
|
|
65
|
+
let receivedBytes = 0;
|
|
66
|
+
for await (const chunk of request) {
|
|
67
|
+
signal.throwIfAborted();
|
|
68
|
+
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
69
|
+
receivedBytes += buffer.byteLength;
|
|
70
|
+
if (receivedBytes > maxBodyBytes) {
|
|
71
|
+
request.resume();
|
|
72
|
+
throw new NodeHttpRequestError(413);
|
|
73
|
+
}
|
|
74
|
+
chunks.push(buffer);
|
|
75
|
+
}
|
|
76
|
+
if (!request.complete || receivedBytes === 0) {
|
|
77
|
+
throw new NodeHttpRequestError(400);
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
return JSON.parse(Buffer.concat(chunks, receivedBytes).toString('utf8'));
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
throw new NodeHttpRequestError(400);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
export function writeJsonError(response, statusCode, message, headers = {}) {
|
|
87
|
+
writeJson(response, statusCode, { error: { message } }, {
|
|
88
|
+
'Cache-Control': 'no-store',
|
|
89
|
+
...headers,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
export function writeJson(response, statusCode, value, headers = {}) {
|
|
93
|
+
if (response.headersSent || response.destroyed) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const body = JSON.stringify(value);
|
|
97
|
+
response.writeHead(statusCode, {
|
|
98
|
+
'Cache-Control': 'no-store',
|
|
99
|
+
'Content-Length': Buffer.byteLength(body),
|
|
100
|
+
'Content-Type': 'application/json; charset=utf-8',
|
|
101
|
+
'X-Content-Type-Options': 'nosniff',
|
|
102
|
+
...headers,
|
|
103
|
+
});
|
|
104
|
+
response.end(body);
|
|
105
|
+
}
|
|
106
|
+
export function errorType(error) {
|
|
107
|
+
return error instanceof Error ? error.name : 'unknown';
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=http-utils.js.map
|