@heddleagent/execution-host-client 7.0.0 → 8.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -34
- package/dist/cli/hostctl.d.ts +11 -0
- package/dist/cli/hostctl.d.ts.map +1 -0
- package/dist/cli/hostctl.js +62 -0
- package/dist/cli/hostctl.js.map +1 -0
- package/dist/contracts/index.d.ts +2 -0
- package/dist/contracts/index.d.ts.map +1 -1
- package/dist/contracts/index.js +9 -0
- package/dist/contracts/index.js.map +1 -1
- package/dist/coordinator/contracts.d.ts +148 -5
- package/dist/coordinator/contracts.d.ts.map +1 -1
- package/dist/coordinator/contracts.js +74 -3
- package/dist/coordinator/contracts.js.map +1 -1
- package/dist/coordinator/hosted-heartbeat-execution-client.d.ts +30 -0
- package/dist/coordinator/hosted-heartbeat-execution-client.d.ts.map +1 -0
- package/dist/coordinator/hosted-heartbeat-execution-client.js +240 -0
- package/dist/coordinator/hosted-heartbeat-execution-client.js.map +1 -0
- package/dist/coordinator/hosted-heartbeat-execution-service.d.ts +14 -0
- package/dist/coordinator/hosted-heartbeat-execution-service.d.ts.map +1 -0
- package/dist/coordinator/hosted-heartbeat-execution-service.js +100 -0
- package/dist/coordinator/hosted-heartbeat-execution-service.js.map +1 -0
- package/dist/coordinator/index.d.ts +5 -5
- package/dist/coordinator/index.d.ts.map +1 -1
- package/dist/coordinator/index.js +3 -3
- package/dist/coordinator/index.js.map +1 -1
- package/dist/coordinator/node/execution-http-service.d.ts +11 -0
- package/dist/coordinator/node/execution-http-service.d.ts.map +1 -0
- package/dist/coordinator/node/{delegation-http-service.js → execution-http-service.js} +52 -39
- package/dist/coordinator/node/execution-http-service.js.map +1 -0
- package/dist/coordinator/node/index.d.ts +2 -2
- package/dist/coordinator/node/index.d.ts.map +1 -1
- package/dist/coordinator/node/index.js +1 -1
- package/dist/coordinator/node/index.js.map +1 -1
- package/dist/coordinator/node/types.d.ts +10 -9
- package/dist/coordinator/node/types.d.ts.map +1 -1
- package/dist/coordinator/node/types.js.map +1 -1
- package/dist/coordinator/types.d.ts +51 -13
- package/dist/coordinator/types.d.ts.map +1 -1
- package/dist/coordinator/types.js.map +1 -1
- package/dist/host/schemas.d.ts.map +1 -1
- package/dist/host/schemas.js +2 -7
- package/dist/host/schemas.js.map +1 -1
- package/dist/http-sse/direct-http-execution-host.d.ts.map +1 -1
- package/dist/http-sse/direct-http-execution-host.js +6 -5
- package/dist/http-sse/direct-http-execution-host.js.map +1 -1
- package/dist/http-sse/types.d.ts +2 -0
- package/dist/http-sse/types.d.ts.map +1 -1
- package/dist/http-sse/types.js.map +1 -1
- package/dist/node/authority-key.d.ts.map +1 -1
- package/dist/node/authority-key.js +24 -49
- package/dist/node/authority-key.js.map +1 -1
- package/dist/node/environment-value.d.ts +17 -0
- package/dist/node/environment-value.d.ts.map +1 -0
- package/dist/node/environment-value.js +78 -0
- package/dist/node/environment-value.js.map +1 -0
- package/dist/node/index.d.ts +4 -0
- package/dist/node/index.d.ts.map +1 -1
- package/dist/node/index.js +2 -0
- package/dist/node/index.js.map +1 -1
- package/dist/node/local-credential-bundle.d.ts +33 -0
- package/dist/node/local-credential-bundle.d.ts.map +1 -0
- package/dist/node/local-credential-bundle.js +196 -0
- package/dist/node/local-credential-bundle.js.map +1 -0
- package/dist/node/owner-only-file.d.ts +17 -0
- package/dist/node/owner-only-file.d.ts.map +1 -0
- package/dist/node/owner-only-file.js +59 -0
- package/dist/node/owner-only-file.js.map +1 -0
- package/package.json +6 -1
- package/dist/coordinator/hosted-heartbeat-delegation-client.d.ts +0 -21
- package/dist/coordinator/hosted-heartbeat-delegation-client.d.ts.map +0 -1
- package/dist/coordinator/hosted-heartbeat-delegation-client.js +0 -117
- package/dist/coordinator/hosted-heartbeat-delegation-client.js.map +0 -1
- package/dist/coordinator/hosted-heartbeat-delegation-service.d.ts +0 -15
- package/dist/coordinator/hosted-heartbeat-delegation-service.d.ts.map +0 -1
- package/dist/coordinator/hosted-heartbeat-delegation-service.js +0 -81
- package/dist/coordinator/hosted-heartbeat-delegation-service.js.map +0 -1
- package/dist/coordinator/node/delegation-http-service.d.ts +0 -11
- package/dist/coordinator/node/delegation-http-service.d.ts.map +0 -1
- package/dist/coordinator/node/delegation-http-service.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"hosted-heartbeat-delegation-service.js","sourceRoot":"","sources":["../../src/coordinator/hosted-heartbeat-delegation-service.ts"],"names":[],"mappings":"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","sourcesContent":["import dayjs from 'dayjs';\nimport { z } from 'zod';\nimport { HEARTBEAT_TASK_WORKFLOW, OpaqueIdSchema } from '../contracts/index.js';\nimport {\n HostedHeartbeatDelegationAuthorizationSchema,\n HostedHeartbeatDelegationRequestSchema,\n HostedHeartbeatDelegationSchema,\n type HostedHeartbeatDelegation,\n type HostedHeartbeatDelegationRequest,\n} from './contracts.js';\nimport { createHostedRuntimeSessionId } from './runtime-session.js';\nimport type {\n HostedHeartbeatDelegationIssuer,\n HostedHeartbeatDelegationServiceConfig,\n} from './types.js';\n\nconst DelegationServiceConfigSchema = z.object({\n runtimeSessionNamespace: OpaqueIdSchema,\n maxExecutionMs: z.number().int().min(1_000).max(Number.MAX_SAFE_INTEGER),\n}).strict();\n\nexport class HostedHeartbeatDelegationRejectedError extends Error {\n readonly name = 'HostedHeartbeatDelegationRejectedError';\n}\n\n/**\n * Converts one product authorization decision into a fully bound Heddle\n * authority bundle. Product code never constructs the delegation wire shape.\n */\nexport class HostedHeartbeatDelegationService\nimplements HostedHeartbeatDelegationIssuer {\n readonly #authority: HostedHeartbeatDelegationServiceConfig['authority'];\n readonly #authorizer: HostedHeartbeatDelegationServiceConfig['authorizer'];\n readonly #runtimeSessionNamespace: string;\n readonly #maxExecutionMs: number;\n readonly #now: () => Date;\n\n constructor(config: HostedHeartbeatDelegationServiceConfig) {\n const parsed = DelegationServiceConfigSchema.parse({\n runtimeSessionNamespace: config.runtimeSessionNamespace,\n maxExecutionMs: config.maxExecutionMs,\n });\n this.#authority = config.authority;\n this.#authorizer = config.authorizer;\n this.#runtimeSessionNamespace = parsed.runtimeSessionNamespace;\n this.#maxExecutionMs = parsed.maxExecutionMs;\n this.#now = config.now ?? (() => new Date());\n }\n\n async issue(\n rawInput: HostedHeartbeatDelegationRequest,\n rawSignal?: AbortSignal,\n ): Promise<HostedHeartbeatDelegation> {\n const input = HostedHeartbeatDelegationRequestSchema.parse(rawInput);\n const signal = rawSignal ?? new AbortController().signal;\n signal.throwIfAborted();\n const rawAuthorization = await this.#authorizer.authorize({\n taskId: input.taskId,\n executionId: input.executionId,\n signal,\n });\n if (!rawAuthorization) {\n throw new HostedHeartbeatDelegationRejectedError(\n 'The product rejected this heartbeat execution.',\n );\n }\n const authorization = HostedHeartbeatDelegationAuthorizationSchema.parse(\n rawAuthorization,\n );\n const runtimeSessionId = createHostedRuntimeSessionId({\n namespace: this.#runtimeSessionNamespace,\n scope: authorization.scope,\n });\n const deadlineAt = dayjs(this.#now())\n .add(this.#maxExecutionMs, 'millisecond')\n .toISOString();\n const issued = await this.#authority.issue({\n scope: authorization.scope,\n runtimeSessionId,\n invocationId: input.executionId,\n workflow: HEARTBEAT_TASK_WORKFLOW,\n mcp: { allowedTools: authorization.allowedTools },\n });\n signal.throwIfAborted();\n const mcpCapability = issued.mcpCapability();\n if (!mcpCapability || !issued.metadata.mcp) {\n throw new Error(\n 'Hosted heartbeat delegation requires an MCP-capable execution authority.',\n );\n }\n\n return HostedHeartbeatDelegationSchema.parse({\n schemaVersion: 1,\n taskId: input.taskId,\n executionId: input.executionId,\n scope: authorization.scope,\n runtimeSessionId,\n deadlineAt,\n authority: {\n metadata: issued.metadata,\n executionAssertion: issued.executionAssertion(),\n mcpCapability,\n },\n });\n }\n}\n"]}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
|
-
import type { NodeHostedHeartbeatDelegationHttpHandler, NodeHostedHeartbeatDelegationHttpServiceConfig } from './types.js';
|
|
3
|
-
/** Standard authenticated Node HTTP edge for product-owned run authority. */
|
|
4
|
-
export declare class NodeHostedHeartbeatDelegationHttpService implements NodeHostedHeartbeatDelegationHttpHandler {
|
|
5
|
-
#private;
|
|
6
|
-
constructor(config: NodeHostedHeartbeatDelegationHttpServiceConfig);
|
|
7
|
-
handle(request: IncomingMessage, response: ServerResponse): boolean;
|
|
8
|
-
handleDelegation(request: IncomingMessage, response: ServerResponse): void;
|
|
9
|
-
close(): Promise<void>;
|
|
10
|
-
}
|
|
11
|
-
//# sourceMappingURL=delegation-http-service.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"delegation-http-service.d.ts","sourceRoot":"","sources":["../../../src/coordinator/node/delegation-http-service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAoBjE,OAAO,KAAK,EAEV,wCAAwC,EACxC,8CAA8C,EAC/C,MAAM,YAAY,CAAC;AAWpB,6EAA6E;AAC7E,qBAAa,wCACb,YAAW,wCAAwC;;gBAcrC,MAAM,EAAE,8CAA8C;IAclE,MAAM,CAAC,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,cAAc,GAAG,OAAO;IAQnE,gBAAgB,CACd,OAAO,EAAE,eAAe,EACxB,QAAQ,EAAE,cAAc,GACvB,IAAI;IA0DP,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAkEvB"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"delegation-http-service.js","sourceRoot":"","sources":["../../../src/coordinator/node/delegation-http-service.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash, timingSafeEqual } from 'node:crypto';\nimport type { IncomingMessage, ServerResponse } from 'node:http';\nimport { z, ZodError } from 'zod';\nimport {\n HOSTED_HEARTBEAT_DELEGATIONS_PATH,\n HostedHeartbeatDelegationRequestSchema,\n} from '../contracts.js';\nimport {\n HostedHeartbeatDelegationRejectedError,\n} from '../hosted-heartbeat-delegation-service.js';\nimport { HostedHeartbeatServiceTokenSchema } from '../service-token.js';\nimport {\n NodeHttpPathSchema,\n NodeHttpRequestError,\n errorType,\n readJsonBody,\n readPathname,\n takeAuthorization,\n writeJson,\n writeJsonError,\n} from '../../node/http-utils.js';\nimport type {\n NodeHostedHeartbeatDelegationFailure,\n NodeHostedHeartbeatDelegationHttpHandler,\n NodeHostedHeartbeatDelegationHttpServiceConfig,\n} from './types.js';\n\nconst DEFAULT_MAX_BODY_BYTES = 16 * 1_024;\n\ntype ActiveDelegation = {\n abortController: AbortController;\n request: IncomingMessage;\n response: ServerResponse;\n pending: Promise<void>;\n};\n\n/** Standard authenticated Node HTTP edge for product-owned run authority. */\nexport class NodeHostedHeartbeatDelegationHttpService\nimplements NodeHostedHeartbeatDelegationHttpHandler {\n readonly #delegations: NodeHostedHeartbeatDelegationHttpServiceConfig[\n 'delegations'\n ];\n readonly #tokenDigest: Buffer;\n readonly #path: string;\n readonly #maxBodyBytes: number;\n readonly #reportFailure: NodeHostedHeartbeatDelegationHttpServiceConfig[\n 'reportFailure'\n ];\n readonly #active = new Set<ActiveDelegation>();\n #closed = false;\n #closePromise: Promise<void> | undefined;\n\n constructor(config: NodeHostedHeartbeatDelegationHttpServiceConfig) {\n this.#delegations = config.delegations;\n this.#tokenDigest = digest(\n HostedHeartbeatServiceTokenSchema.parse(config.apiToken),\n );\n this.#path = NodeHttpPathSchema.parse(\n config.path ?? HOSTED_HEARTBEAT_DELEGATIONS_PATH,\n );\n this.#maxBodyBytes = z.number().int().min(1).max(1_048_576).parse(\n config.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES,\n );\n this.#reportFailure = config.reportFailure;\n }\n\n handle(request: IncomingMessage, response: ServerResponse): boolean {\n if (readPathname(request.url) !== this.#path) {\n return false;\n }\n this.handleDelegation(request, response);\n return true;\n }\n\n handleDelegation(\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 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 Allow: 'POST',\n });\n return;\n }\n } catch (error) {\n request.resume();\n writeJsonError(\n response,\n error instanceof NodeHttpRequestError ? error.statusCode : 400,\n 'Invalid heartbeat delegation request.',\n );\n return;\n }\n if (!this.#authenticates(authorization)) {\n request.resume();\n writeJsonError(response, 401, 'Authentication is required.', {\n 'WWW-Authenticate': 'Bearer',\n });\n return;\n }\n\n const abortController = new AbortController();\n const abort = () => abortController.abort(\n new Error('The heartbeat delegation request closed.'),\n );\n request.once('aborted', abort);\n response.once('close', abort);\n const active: ActiveDelegation = {\n abortController,\n request,\n response,\n pending: Promise.resolve(),\n };\n active.pending = this.#serve(\n request,\n response,\n abortController.signal,\n ).finally(() => {\n request.removeListener('aborted', abort);\n response.removeListener('close', abort);\n this.#active.delete(active);\n });\n this.#active.add(active);\n }\n\n close(): Promise<void> {\n if (!this.#closePromise) {\n this.#closed = true;\n const active = [...this.#active];\n active.forEach(({ abortController, request, response }) => {\n abortController.abort(\n new Error('The heartbeat delegation service is stopping.'),\n );\n request.destroy();\n response.destroy();\n });\n this.#closePromise = Promise.allSettled(\n active.map(({ pending }) => pending),\n ).then(() => undefined);\n }\n return this.#closePromise;\n }\n\n async #serve(\n request: IncomingMessage,\n response: ServerResponse,\n signal: AbortSignal,\n ): Promise<void> {\n try {\n const input = HostedHeartbeatDelegationRequestSchema.parse(\n await readJsonBody(request, this.#maxBodyBytes, signal),\n );\n const delegation = await this.#delegations.issue(input, signal);\n writeJson(response, 200, delegation);\n } catch (error) {\n if (signal.aborted) {\n return;\n }\n if (error instanceof HostedHeartbeatDelegationRejectedError) {\n writeJsonError(response, 403, error.message);\n return;\n }\n if (error instanceof NodeHttpRequestError || error instanceof ZodError) {\n writeJsonError(\n response,\n error instanceof NodeHttpRequestError ? error.statusCode : 400,\n 'Invalid heartbeat delegation request.',\n );\n return;\n }\n this.#report({ phase: 'delegation', errorType: errorType(error) });\n writeJsonError(response, 500, 'Heartbeat delegation failed.');\n }\n }\n\n #authenticates(authorization: string | undefined): boolean {\n const token = /^Bearer ([^\\s]+)$/i.exec(\n authorization?.trim() ?? '',\n )?.[1];\n return token\n ? timingSafeEqual(digest(token), this.#tokenDigest)\n : false;\n }\n\n #report(failure: NodeHostedHeartbeatDelegationFailure): void {\n try {\n this.#reportFailure?.(Object.freeze({ ...failure }));\n } catch {\n // Observability must not change request settlement.\n }\n }\n}\n\nfunction digest(value: string): Buffer {\n return createHash('sha256').update(value, 'utf8').digest();\n}\n"]}
|