@cortexkit/subc-client
Advanced tools
+1
-1
| { | ||
| "name": "@cortexkit/subc-client", | ||
| "version": "0.1.0", | ||
| "version": "0.2.0", | ||
| "description": "TypeScript client for the subc daemon. Wire-compatible (byte-for-byte) with the Rust subc-transport handshake and subc-protocol envelope.", | ||
@@ -5,0 +5,0 @@ "type": "module", |
+49
-7
@@ -41,2 +41,4 @@ // The consumer-facing subc client. Mirrors the canonical pure consumer | ||
| const DEFAULT_MANAGED_TARGET_KIND: ManagedRouteKind = "management_surface"; | ||
| export const SUBC_MODULE_ID_ENV = "SUBC_MODULE_ID"; | ||
| export const SUBC_LAUNCH_NONCE_ENV = "SUBC_LAUNCH_NONCE"; | ||
@@ -56,2 +58,12 @@ export interface BindIdentity { | ||
| export interface ConsumerIdentity { | ||
| module_id: string; | ||
| launch_nonce: string; | ||
| } | ||
| export interface RouteOpenOptions { | ||
| /** Optional override for the consumer identity; by default the SUBC_MODULE_ID and SUBC_LAUNCH_NONCE environment variables are used when both are non-empty. Set null to send route.open without consumer_identity. */ | ||
| consumerIdentity?: ConsumerIdentity | null; | ||
| } | ||
| export interface CatalogEntry { | ||
@@ -75,2 +87,4 @@ module_id: string; | ||
| targetKind?: ManagedRouteKind; | ||
| /** Optional override for the consumer identity; by default the SUBC_MODULE_ID and SUBC_LAUNCH_NONCE environment variables are used when both are non-empty. Set null to send route.open without consumer_identity. */ | ||
| consumerIdentity?: ConsumerIdentity | null; | ||
| } | ||
@@ -89,2 +103,4 @@ | ||
| drain?: boolean; | ||
| /** Consumer identity to use when looking up the route to close; only needed for routes opened with a non-default consumer identity. */ | ||
| consumerIdentity?: ConsumerIdentity | null; | ||
| } | ||
@@ -206,2 +222,3 @@ | ||
| identity: BindIdentity; | ||
| consumerIdentity?: ConsumerIdentity; | ||
| channel: number | null; | ||
@@ -260,4 +277,10 @@ generation: number; | ||
| /** Open a route to a provider (channel-0 route.open); returns the route channel. */ | ||
| async routeOpen(target: RouteTarget, identity: BindIdentity): Promise<number> { | ||
| const body = this.encode({ op: "route.open", target, identity }); | ||
| async routeOpen(target: RouteTarget, identity: BindIdentity, opts: RouteOpenOptions = {}): Promise<number> { | ||
| const consumerIdentity = routeOpenConsumerIdentity(opts); | ||
| const body = this.encode({ | ||
| op: "route.open", | ||
| target, | ||
| identity, | ||
| ...(consumerIdentity ? { consumer_identity: consumerIdentity } : {}), | ||
| }); | ||
| const reply = await this.controlRpc(body); | ||
@@ -389,3 +412,3 @@ const parsed = this.parseJson(reply) as { op: string; route_channel?: number }; | ||
| ): Promise<void> { | ||
| const key = routeCacheKey(target, identity); | ||
| const key = routeCacheKey(target, identity, routeOpenConsumerIdentity(opts)); | ||
| const cached = this.routes.get(key); | ||
@@ -600,3 +623,4 @@ if (!cached) return; // never opened / already closed — idempotent no-op. | ||
| >; | ||
| const key = routeCacheKey(target, identity); | ||
| const consumerIdentity = routeOpenConsumerIdentity(opts); | ||
| const key = routeCacheKey(target, identity, consumerIdentity); | ||
| let cached = this.routes.get(key); | ||
@@ -609,2 +633,3 @@ if (!cached) { | ||
| identity, | ||
| consumerIdentity, | ||
| channel: null, | ||
@@ -642,3 +667,5 @@ generation: 0, | ||
| try { | ||
| const channel = await this.routeOpen(cached.target, cached.identity); | ||
| const channel = await this.routeOpen(cached.target, cached.identity, { | ||
| consumerIdentity: cached.consumerIdentity ?? null, | ||
| }); | ||
| // Generation guard: a closeRoute may have flipped the tombstone WHILE this | ||
@@ -898,6 +925,21 @@ // route.open was in flight. If so, close wins — do NOT install the channel | ||
| function routeCacheKey(target: Extract<RouteTarget, { kind: ManagedRouteKind }>, identity: BindIdentity): string { | ||
| return `${target.kind}\0${target.module_id}\0${identity.project_root}\0${identity.harness}\0${identity.session}`; | ||
| function routeCacheKey( | ||
| target: Extract<RouteTarget, { kind: ManagedRouteKind }>, | ||
| identity: BindIdentity, | ||
| consumerIdentity?: ConsumerIdentity, | ||
| ): string { | ||
| const consumerPart = consumerIdentity | ||
| ? `${consumerIdentity.module_id}\0${consumerIdentity.launch_nonce}` | ||
| : ""; | ||
| return `${target.kind}\0${target.module_id}\0${identity.project_root}\0${identity.harness}\0${identity.session}\0${consumerPart}`; | ||
| } | ||
| function routeOpenConsumerIdentity(opts: RouteOpenOptions = {}): ConsumerIdentity | undefined { | ||
| if (opts.consumerIdentity !== undefined) return opts.consumerIdentity ?? undefined; | ||
| const moduleId = process.env[SUBC_MODULE_ID_ENV]; | ||
| const launchNonce = process.env[SUBC_LAUNCH_NONCE_ENV]; | ||
| if (!moduleId || !launchNonce) return undefined; | ||
| return { module_id: moduleId, launch_nonce: launchNonce }; | ||
| } | ||
| function errorCode(err: unknown): string | undefined { | ||
@@ -904,0 +946,0 @@ if (typeof err === "object" && err !== null && "code" in err) { |
+5
-0
@@ -6,2 +6,4 @@ export { | ||
| DEFAULT_RECONNECT_BACKOFF, | ||
| SUBC_MODULE_ID_ENV, | ||
| SUBC_LAUNCH_NONCE_ENV, | ||
| isConsumerReconnectTransient, | ||
@@ -12,2 +14,4 @@ connectionFileExists, | ||
| type ManagedRouteKind, | ||
| type ConsumerIdentity, | ||
| type RouteOpenOptions, | ||
| type CatalogEntry, | ||
@@ -62,2 +66,3 @@ type RequestOptions, | ||
| type BindDecision, | ||
| type Principal, | ||
| type BindingsInput, | ||
@@ -64,0 +69,0 @@ type CircuitBreakerInput, |
+7
-0
@@ -205,2 +205,7 @@ import { Buffer } from "node:buffer"; | ||
| export type Principal = | ||
| | { kind: "reserved"; module_id: string } | ||
| | { kind: "direct" } | ||
| | { kind: "unverified" }; | ||
| export interface RouteBindRequest { | ||
@@ -210,2 +215,3 @@ route_channel: number; | ||
| identity: BindIdentity; | ||
| principal?: Principal; | ||
| } | ||
@@ -538,2 +544,3 @@ | ||
| identity: request.identity as BindIdentity, | ||
| principal: request.principal as Principal | undefined, | ||
| }; | ||
@@ -540,0 +547,0 @@ |
97154
2.42%2455
2.08%5
66.67%