@nextclaw/extension-sdk 0.3.25 → 0.4.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2 -2
- package/dist/services/extension-client.service.d.ts +2 -1
- package/dist/services/extension-client.service.d.ts.map +1 -1
- package/dist/services/extension-client.service.js +9 -0
- package/dist/services/extension-client.service.js.map +1 -1
- package/dist/services/extension-observation.service.js +137 -0
- package/dist/services/extension-observation.service.js.map +1 -0
- package/dist/types/extension-sdk.types.d.ts +31 -1
- package/dist/types/extension-sdk.types.d.ts.map +1 -1
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { ChannelCommandExecuteRequest, ChannelCommandExecuteResponse, ChannelCommandListRequest, ChannelCommandListResponse, ChannelCommandOption, ChannelCommandOptionType, ChannelCommandSpec, ChannelConfigGetRequest, ChannelConfigGetResponse, ChannelFileContent, ChannelImageContent, ChannelMessageContent, ChannelSubmittedAttachment, ChannelSubmittedMessage, ChannelTextContent, ExtensionCapabilities, ExtensionCapabilityHandler, ExtensionCapabilityPayload, ExtensionChannel, ExtensionChannelCommands, ExtensionChannelConfig, ExtensionChannels, ExtensionDiagnostics, ExtensionRequest, ExtensionRequestHandler, ExtensionRequestResponse, ExtensionTransportEnvelope, NextClawExtensionOptions, NextClawExtensionWebSocketLike } from "./types/extension-sdk.types.js";
|
|
1
|
+
import { ChannelCommandExecuteRequest, ChannelCommandExecuteResponse, ChannelCommandListRequest, ChannelCommandListResponse, ChannelCommandOption, ChannelCommandOptionType, ChannelCommandSpec, ChannelConfigGetRequest, ChannelConfigGetResponse, ChannelFileContent, ChannelImageContent, ChannelMessageContent, ChannelSubmittedAttachment, ChannelSubmittedMessage, ChannelTextContent, ExtensionCapabilities, ExtensionCapabilityHandler, ExtensionCapabilityPayload, ExtensionChannel, ExtensionChannelCommands, ExtensionChannelConfig, ExtensionChannels, ExtensionDiagnostics, ExtensionObservationEmitInput, ExtensionObservationHandlers, ExtensionObservations, ExtensionRequest, ExtensionRequestHandler, ExtensionRequestResponse, ExtensionTransportEnvelope, NextClawExtensionOptions, NextClawExtensionWebSocketLike } from "./types/extension-sdk.types.js";
|
|
2
2
|
import { NextClawExtension } from "./services/extension-client.service.js";
|
|
3
3
|
import { ChannelTypingController, ChannelTypingControllerOptions } from "./services/channel-typing-controller.service.js";
|
|
4
4
|
import { BusChannelCreateContext, BusChannelExtensionDefinition, BusChannelInboundMessage, BusChannelMessageBus, BusChannelRuntime, ChannelExtensionContext, ChannelExtensionDefinition, ChannelSubmittedMessageInput, ExtensionChannelAdapter, ExtensionChannelController, startBusChannelExtension, startChannelExtension, warnNcpEventError } from "./services/extension-channel-controller.service.js";
|
|
5
5
|
import { DIAGNOSTIC_CORRELATION_METADATA_KEY, DiagnosticErrorClassification, classifyDiagnosticError } from "@nextclaw/shared";
|
|
6
|
-
export { type BusChannelCreateContext, type BusChannelExtensionDefinition, type BusChannelInboundMessage, type BusChannelMessageBus, type BusChannelRuntime, type ChannelCommandExecuteRequest, type ChannelCommandExecuteResponse, type ChannelCommandListRequest, type ChannelCommandListResponse, type ChannelCommandOption, type ChannelCommandOptionType, type ChannelCommandSpec, type ChannelConfigGetRequest, type ChannelConfigGetResponse, type ChannelExtensionContext, type ChannelExtensionDefinition, type ChannelFileContent, type ChannelImageContent, type ChannelMessageContent, type ChannelSubmittedAttachment, type ChannelSubmittedMessage, type ChannelSubmittedMessageInput, type ChannelTextContent, ChannelTypingController, type ChannelTypingControllerOptions, DIAGNOSTIC_CORRELATION_METADATA_KEY, type DiagnosticErrorClassification, type ExtensionCapabilities, type ExtensionCapabilityHandler, type ExtensionCapabilityPayload, type ExtensionChannel, type ExtensionChannelAdapter, type ExtensionChannelCommands, type ExtensionChannelConfig, ExtensionChannelController, type ExtensionChannels, type ExtensionDiagnostics, type ExtensionRequest, type ExtensionRequestHandler, type ExtensionRequestResponse, type ExtensionTransportEnvelope, NextClawExtension, type NextClawExtensionOptions, type NextClawExtensionWebSocketLike, classifyDiagnosticError, startBusChannelExtension, startChannelExtension, warnNcpEventError };
|
|
6
|
+
export { type BusChannelCreateContext, type BusChannelExtensionDefinition, type BusChannelInboundMessage, type BusChannelMessageBus, type BusChannelRuntime, type ChannelCommandExecuteRequest, type ChannelCommandExecuteResponse, type ChannelCommandListRequest, type ChannelCommandListResponse, type ChannelCommandOption, type ChannelCommandOptionType, type ChannelCommandSpec, type ChannelConfigGetRequest, type ChannelConfigGetResponse, type ChannelExtensionContext, type ChannelExtensionDefinition, type ChannelFileContent, type ChannelImageContent, type ChannelMessageContent, type ChannelSubmittedAttachment, type ChannelSubmittedMessage, type ChannelSubmittedMessageInput, type ChannelTextContent, ChannelTypingController, type ChannelTypingControllerOptions, DIAGNOSTIC_CORRELATION_METADATA_KEY, type DiagnosticErrorClassification, type ExtensionCapabilities, type ExtensionCapabilityHandler, type ExtensionCapabilityPayload, type ExtensionChannel, type ExtensionChannelAdapter, type ExtensionChannelCommands, type ExtensionChannelConfig, ExtensionChannelController, type ExtensionChannels, type ExtensionDiagnostics, type ExtensionObservationEmitInput, type ExtensionObservationHandlers, type ExtensionObservations, type ExtensionRequest, type ExtensionRequestHandler, type ExtensionRequestResponse, type ExtensionTransportEnvelope, NextClawExtension, type NextClawExtensionOptions, type NextClawExtensionWebSocketLike, classifyDiagnosticError, startBusChannelExtension, startChannelExtension, warnNcpEventError };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ExtensionCapabilities, ExtensionChannels, ExtensionDiagnostics, ExtensionRequestHandler, NextClawExtensionOptions } from "../types/extension-sdk.types.js";
|
|
1
|
+
import { ExtensionCapabilities, ExtensionChannels, ExtensionDiagnostics, ExtensionObservations, ExtensionRequestHandler, NextClawExtensionOptions } from "../types/extension-sdk.types.js";
|
|
2
2
|
import { EventBus } from "@nextclaw/shared";
|
|
3
3
|
|
|
4
4
|
//#region src/services/extension-client.service.d.ts
|
|
@@ -7,6 +7,7 @@ declare class NextClawExtension {
|
|
|
7
7
|
readonly channels: ExtensionChannels;
|
|
8
8
|
readonly capabilities: ExtensionCapabilities;
|
|
9
9
|
readonly diagnostics: ExtensionDiagnostics;
|
|
10
|
+
readonly observations: ExtensionObservations;
|
|
10
11
|
readonly extensionId: string;
|
|
11
12
|
readonly generation: string;
|
|
12
13
|
private readonly transport;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extension-client.service.d.ts","names":[],"sources":["../../src/services/extension-client.service.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"extension-client.service.d.ts","names":[],"sources":["../../src/services/extension-client.service.ts"],"mappings":";;;;cAyOa,iBAAA;EAAA,SACF,QAAA,EAAU,QAAA;EAAA,SACV,QAAA,EAAU,iBAAA;EAAA,SACV,YAAA,EAAc,qBAAA;EAAA,SACd,WAAA,EAAa,oBAAA;EAAA,SACb,YAAA,EAAc,qBAAA;EAAA,SACd,WAAA;EAAA,SACA,UAAA;EAAA,iBACQ,SAAA;EAAA,QACT,uBAAA;EAAA,QAIA,oBAAA;cAEI,OAAA,GAAS,wBAAA;EAAA,SAqCZ,KAAA;EAAA,SAUA,KAAA,QAAkB,OAAA;EAAA,SAWlB,SAAA,GAAa,OAAA,EAAS,uBAAA;EAAA,iBAgBd,kBAAA;EAAA,iBAQA,yBAAA;AAAA"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ExtensionChannelService } from "./extension-channel.service.js";
|
|
2
2
|
import { ExtensionTransportService } from "./extension-transport.service.js";
|
|
3
|
+
import { ExtensionObservationService } from "./extension-observation.service.js";
|
|
3
4
|
import { EventBus, getKeyId, ingressKeys } from "@nextclaw/shared";
|
|
4
5
|
import { createHash, randomUUID } from "node:crypto";
|
|
5
6
|
//#region src/services/extension-client.service.ts
|
|
@@ -127,6 +128,7 @@ var NextClawExtension = class {
|
|
|
127
128
|
channels;
|
|
128
129
|
capabilities;
|
|
129
130
|
diagnostics;
|
|
131
|
+
observations;
|
|
130
132
|
extensionId;
|
|
131
133
|
generation;
|
|
132
134
|
transport;
|
|
@@ -156,6 +158,12 @@ var NextClawExtension = class {
|
|
|
156
158
|
extensionId: this.extensionId,
|
|
157
159
|
transport: this.transport
|
|
158
160
|
});
|
|
161
|
+
this.observations = new ExtensionObservationService({
|
|
162
|
+
eventBus: this.eventBus,
|
|
163
|
+
extensionId: this.extensionId,
|
|
164
|
+
generation: this.generation,
|
|
165
|
+
transport: this.transport
|
|
166
|
+
});
|
|
159
167
|
this.diagnostics = new ExtensionDiagnosticClient(this.transport, Math.max(50, options.diagnosticTimeoutMs ?? 2e3));
|
|
160
168
|
this.parentProcessWatcher = this.startParentProcessWatcher();
|
|
161
169
|
}
|
|
@@ -166,6 +174,7 @@ var NextClawExtension = class {
|
|
|
166
174
|
}
|
|
167
175
|
this.eventStreamSubscription?.close();
|
|
168
176
|
this.eventStreamSubscription = null;
|
|
177
|
+
this.observations.close();
|
|
169
178
|
};
|
|
170
179
|
ready = async () => {
|
|
171
180
|
const subscription = this.eventStreamSubscription;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extension-client.service.js","names":[],"sources":["../../src/services/extension-client.service.ts"],"sourcesContent":["import { EventBus, getKeyId, ingressKeys } from \"@nextclaw/shared\";\nimport { createHash, randomUUID } from \"node:crypto\";\nimport type {\n ExtensionCapabilities,\n ExtensionCapabilityHandler,\n ExtensionChannels,\n ExtensionDiagnostics,\n ExtensionRequest,\n ExtensionRequestHandler,\n ExtensionTransportEnvelope,\n NextClawExtensionOptions,\n} from \"../types/extension-sdk.types.js\";\nimport { ExtensionChannelService } from \"./extension-channel.service.js\";\nimport { ExtensionTransportService } from \"./extension-transport.service.js\";\n\nconst EXTENSION_PARENT_WATCH_INTERVAL_MS = 1000;\n\nclass ExtensionDiagnosticClient implements ExtensionDiagnostics {\n constructor(\n private readonly transport: ExtensionTransportService,\n private readonly timeoutMs: number,\n ) {}\n\n readonly createTraceId = (stableId?: string): string => {\n const normalizedStableId = stableId?.trim();\n if (!normalizedStableId) {\n return randomUUID();\n }\n return createHash(\"sha256\")\n .update(normalizedStableId)\n .digest(\"hex\")\n .slice(0, 24);\n };\n\n readonly emit: ExtensionDiagnostics[\"emit\"] = async (input) => {\n const controller = new AbortController();\n let timeout: ReturnType<typeof setTimeout> | undefined;\n try {\n await Promise.race([\n this.transport.postIngress(\n getKeyId(ingressKeys.extension.diagnosticEmit),\n input,\n { signal: controller.signal },\n ),\n new Promise<never>((_resolve, reject) => {\n timeout = setTimeout(() => {\n controller.abort();\n reject(new Error(\"Extension diagnostic emission timed out.\"));\n }, this.timeoutMs);\n timeout.unref?.();\n }),\n ]);\n return true;\n } catch {\n return false;\n } finally {\n if (timeout) {\n clearTimeout(timeout);\n }\n }\n };\n}\n\nclass ExtensionChannelRegistry implements ExtensionChannels {\n private readonly channels = new Map<string, ExtensionChannelService>();\n\n constructor(\n private readonly params: {\n eventBus: EventBus;\n transport: ExtensionTransportService;\n },\n ) {}\n\n readonly use = (channelId: string): ExtensionChannelService => {\n const normalizedChannelId = channelId.trim();\n if (!normalizedChannelId) {\n throw new Error(\"channelId is required.\");\n }\n const existing = this.channels.get(normalizedChannelId);\n if (existing) {\n return existing;\n }\n const channel = new ExtensionChannelService({\n channelId: normalizedChannelId,\n eventBus: this.params.eventBus,\n transport: this.params.transport,\n });\n this.channels.set(normalizedChannelId, channel);\n return channel;\n };\n}\n\nfunction readRequest(payload: unknown): ExtensionRequest | null {\n if (!payload || typeof payload !== \"object\" || Array.isArray(payload)) {\n return null;\n }\n const record = payload as Record<string, unknown>;\n if (\n typeof record.requestId !== \"string\" ||\n typeof record.extensionId !== \"string\" ||\n typeof record.generation !== \"string\" ||\n typeof record.kind !== \"string\"\n ) {\n return null;\n }\n return {\n requestId: record.requestId,\n extensionId: record.extensionId,\n generation: record.generation,\n kind: record.kind,\n payload: record.payload && typeof record.payload === \"object\" && !Array.isArray(record.payload)\n ? record.payload as Record<string, unknown>\n : {},\n };\n}\n\nfunction normalizeName(value: string, name: string): string {\n const normalized = value.trim();\n if (!normalized) {\n throw new Error(`${name} is required.`);\n }\n return normalized;\n}\n\nasync function handleRequest(\n transport: ExtensionTransportService,\n request: ExtensionRequest,\n handler: ExtensionRequestHandler,\n): Promise<void> {\n try {\n const data = await handler(request);\n await transport.respondToRequest({\n requestId: request.requestId,\n ok: true,\n data,\n });\n } catch (error) {\n await transport.respondToRequest({\n requestId: request.requestId,\n ok: false,\n error: {\n message: error instanceof Error ? error.message : String(error),\n },\n });\n }\n}\n\nfunction readParentProcessId(): number | null {\n const raw = process.env.NEXTCLAW_EXTENSION_PARENT_PID?.trim();\n if (!raw) {\n return null;\n }\n const pid = Number(raw);\n return Number.isInteger(pid) && pid > 0 ? pid : null;\n}\n\nfunction isProcessRunning(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (error) {\n return (error as NodeJS.ErrnoException).code === \"EPERM\";\n }\n}\n\nclass ExtensionCapabilityRegistry implements ExtensionCapabilities {\n constructor(\n private readonly params: {\n eventBus: EventBus;\n extensionId: string;\n transport: ExtensionTransportService;\n },\n ) {}\n\n readonly provide = (namespace: string, capability: object): (() => void) => {\n const normalizedNamespace = normalizeName(namespace, \"namespace\");\n const unsubscribeHandlers = this.readHandlers(capability).map(([name, handler]) =>\n this.provideHandler(`${normalizedNamespace}.${name}`, handler),\n );\n if (unsubscribeHandlers.length === 0) {\n throw new Error(`capability '${normalizedNamespace}' has no callable methods.`);\n }\n return () => {\n for (const unsubscribe of unsubscribeHandlers) {\n unsubscribe();\n }\n };\n };\n\n readonly provideHandler = (kind: string, handler: ExtensionCapabilityHandler): (() => void) => {\n const normalizedKind = normalizeName(kind, \"kind\");\n return this.params.eventBus.subscribeAll((event) => {\n if (event.type !== \"extension.request\") {\n return;\n }\n const request = readRequest(event.payload);\n if (\n !request ||\n request.extensionId !== this.params.extensionId ||\n request.generation !== this.params.transport.generation ||\n request.kind !== normalizedKind\n ) {\n return;\n }\n void handleRequest(this.params.transport, request, async (matchedRequest) =>\n await handler(matchedRequest.payload ?? {}, matchedRequest),\n );\n });\n };\n\n private readonly readHandlers = (capability: object): Array<[string, ExtensionCapabilityHandler]> =>\n Object.entries(capability).filter(\n (entry): entry is [string, ExtensionCapabilityHandler] => typeof entry[1] === \"function\",\n );\n}\n\nexport class NextClawExtension {\n readonly eventBus: EventBus;\n readonly channels: ExtensionChannels;\n readonly capabilities: ExtensionCapabilities;\n readonly diagnostics: ExtensionDiagnostics;\n readonly extensionId: string;\n readonly generation: string;\n private readonly transport: ExtensionTransportService;\n private eventStreamSubscription: { close: () => void; ready: Promise<void> } | null = null;\n private parentProcessWatcher: ReturnType<typeof setInterval> | null = null;\n\n constructor(options: NextClawExtensionOptions = {}) {\n this.transport = new ExtensionTransportService(options);\n this.extensionId = this.transport.extensionId;\n this.generation = this.transport.generation;\n this.eventBus = new EventBus({\n onFirstSubscriber: () => {\n this.eventStreamSubscription ??= this.transport.subscribe((event) => {\n this.eventBus.emitEnvelope(this.toEventBusEnvelope(event));\n });\n },\n onNoSubscribers: () => {\n this.eventStreamSubscription?.close();\n this.eventStreamSubscription = null;\n },\n });\n this.channels = new ExtensionChannelRegistry({\n eventBus: this.eventBus,\n transport: this.transport,\n });\n this.capabilities = new ExtensionCapabilityRegistry({\n eventBus: this.eventBus,\n extensionId: this.extensionId,\n transport: this.transport,\n });\n this.diagnostics = new ExtensionDiagnosticClient(\n this.transport,\n Math.max(50, options.diagnosticTimeoutMs ?? 2_000),\n );\n this.parentProcessWatcher = this.startParentProcessWatcher();\n }\n\n readonly close = (): void => {\n if (this.parentProcessWatcher) {\n clearInterval(this.parentProcessWatcher);\n this.parentProcessWatcher = null;\n }\n this.eventStreamSubscription?.close();\n this.eventStreamSubscription = null;\n };\n\n readonly ready = async (): Promise<void> => {\n const subscription = this.eventStreamSubscription;\n if (!subscription) {\n throw new Error(`Extension ${this.extensionId} has no event-stream subscription.`);\n }\n await subscription.ready;\n await this.transport.reportReady(process.pid);\n };\n\n readonly onRequest = (handler: ExtensionRequestHandler): (() => void) =>\n this.eventBus.subscribeAll((event) => {\n if (event.type !== \"extension.request\") {\n return;\n }\n const request = readRequest(event.payload);\n if (!request || request.extensionId !== this.extensionId || request.generation !== this.generation) {\n return;\n }\n void handleRequest(this.transport, request, handler);\n });\n\n private readonly toEventBusEnvelope = (event: ExtensionTransportEnvelope): ExtensionTransportEnvelope => ({\n ...event,\n emittedAt: event.emittedAt ?? new Date().toISOString(),\n source: event.source ?? \"event-stream\",\n });\n\n private readonly startParentProcessWatcher = (): ReturnType<typeof setInterval> | null => {\n const parentPid = readParentProcessId();\n if (!parentPid) {\n return null;\n }\n const timer = setInterval(() => {\n if (isProcessRunning(parentPid)) {\n return;\n }\n this.close();\n process.exit(0);\n }, EXTENSION_PARENT_WATCH_INTERVAL_MS);\n (timer as NodeJS.Timeout).unref?.();\n return timer;\n };\n\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"extension-client.service.js","names":[],"sources":["../../src/services/extension-client.service.ts"],"sourcesContent":["import { EventBus, getKeyId, ingressKeys } from \"@nextclaw/shared\";\nimport { createHash, randomUUID } from \"node:crypto\";\nimport type {\n ExtensionCapabilities,\n ExtensionCapabilityHandler,\n ExtensionChannels,\n ExtensionDiagnostics,\n ExtensionRequest,\n ExtensionRequestHandler,\n ExtensionObservations,\n ExtensionTransportEnvelope,\n NextClawExtensionOptions,\n} from \"../types/extension-sdk.types.js\";\nimport { ExtensionChannelService } from \"./extension-channel.service.js\";\nimport { ExtensionTransportService } from \"./extension-transport.service.js\";\nimport { ExtensionObservationService } from \"./extension-observation.service.js\";\n\nconst EXTENSION_PARENT_WATCH_INTERVAL_MS = 1000;\n\nclass ExtensionDiagnosticClient implements ExtensionDiagnostics {\n constructor(\n private readonly transport: ExtensionTransportService,\n private readonly timeoutMs: number,\n ) {}\n\n readonly createTraceId = (stableId?: string): string => {\n const normalizedStableId = stableId?.trim();\n if (!normalizedStableId) {\n return randomUUID();\n }\n return createHash(\"sha256\")\n .update(normalizedStableId)\n .digest(\"hex\")\n .slice(0, 24);\n };\n\n readonly emit: ExtensionDiagnostics[\"emit\"] = async (input) => {\n const controller = new AbortController();\n let timeout: ReturnType<typeof setTimeout> | undefined;\n try {\n await Promise.race([\n this.transport.postIngress(\n getKeyId(ingressKeys.extension.diagnosticEmit),\n input,\n { signal: controller.signal },\n ),\n new Promise<never>((_resolve, reject) => {\n timeout = setTimeout(() => {\n controller.abort();\n reject(new Error(\"Extension diagnostic emission timed out.\"));\n }, this.timeoutMs);\n timeout.unref?.();\n }),\n ]);\n return true;\n } catch {\n return false;\n } finally {\n if (timeout) {\n clearTimeout(timeout);\n }\n }\n };\n}\n\nclass ExtensionChannelRegistry implements ExtensionChannels {\n private readonly channels = new Map<string, ExtensionChannelService>();\n\n constructor(\n private readonly params: {\n eventBus: EventBus;\n transport: ExtensionTransportService;\n },\n ) {}\n\n readonly use = (channelId: string): ExtensionChannelService => {\n const normalizedChannelId = channelId.trim();\n if (!normalizedChannelId) {\n throw new Error(\"channelId is required.\");\n }\n const existing = this.channels.get(normalizedChannelId);\n if (existing) {\n return existing;\n }\n const channel = new ExtensionChannelService({\n channelId: normalizedChannelId,\n eventBus: this.params.eventBus,\n transport: this.params.transport,\n });\n this.channels.set(normalizedChannelId, channel);\n return channel;\n };\n}\n\nfunction readRequest(payload: unknown): ExtensionRequest | null {\n if (!payload || typeof payload !== \"object\" || Array.isArray(payload)) {\n return null;\n }\n const record = payload as Record<string, unknown>;\n if (\n typeof record.requestId !== \"string\" ||\n typeof record.extensionId !== \"string\" ||\n typeof record.generation !== \"string\" ||\n typeof record.kind !== \"string\"\n ) {\n return null;\n }\n return {\n requestId: record.requestId,\n extensionId: record.extensionId,\n generation: record.generation,\n kind: record.kind,\n payload:\n record.payload &&\n typeof record.payload === \"object\" &&\n !Array.isArray(record.payload)\n ? (record.payload as Record<string, unknown>)\n : {},\n };\n}\n\nfunction normalizeName(value: string, name: string): string {\n const normalized = value.trim();\n if (!normalized) {\n throw new Error(`${name} is required.`);\n }\n return normalized;\n}\n\nasync function handleRequest(\n transport: ExtensionTransportService,\n request: ExtensionRequest,\n handler: ExtensionRequestHandler,\n): Promise<void> {\n try {\n const data = await handler(request);\n await transport.respondToRequest({\n requestId: request.requestId,\n ok: true,\n data,\n });\n } catch (error) {\n await transport.respondToRequest({\n requestId: request.requestId,\n ok: false,\n error: {\n message: error instanceof Error ? error.message : String(error),\n },\n });\n }\n}\n\nfunction readParentProcessId(): number | null {\n const raw = process.env.NEXTCLAW_EXTENSION_PARENT_PID?.trim();\n if (!raw) {\n return null;\n }\n const pid = Number(raw);\n return Number.isInteger(pid) && pid > 0 ? pid : null;\n}\n\nfunction isProcessRunning(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (error) {\n return (error as NodeJS.ErrnoException).code === \"EPERM\";\n }\n}\n\nclass ExtensionCapabilityRegistry implements ExtensionCapabilities {\n constructor(\n private readonly params: {\n eventBus: EventBus;\n extensionId: string;\n transport: ExtensionTransportService;\n },\n ) {}\n\n readonly provide = (namespace: string, capability: object): (() => void) => {\n const normalizedNamespace = normalizeName(namespace, \"namespace\");\n const unsubscribeHandlers = this.readHandlers(capability).map(\n ([name, handler]) =>\n this.provideHandler(`${normalizedNamespace}.${name}`, handler),\n );\n if (unsubscribeHandlers.length === 0) {\n throw new Error(\n `capability '${normalizedNamespace}' has no callable methods.`,\n );\n }\n return () => {\n for (const unsubscribe of unsubscribeHandlers) {\n unsubscribe();\n }\n };\n };\n\n readonly provideHandler = (\n kind: string,\n handler: ExtensionCapabilityHandler,\n ): (() => void) => {\n const normalizedKind = normalizeName(kind, \"kind\");\n return this.params.eventBus.subscribeAll((event) => {\n if (event.type !== \"extension.request\") {\n return;\n }\n const request = readRequest(event.payload);\n if (\n !request ||\n request.extensionId !== this.params.extensionId ||\n request.generation !== this.params.transport.generation ||\n request.kind !== normalizedKind\n ) {\n return;\n }\n void handleRequest(\n this.params.transport,\n request,\n async (matchedRequest) =>\n await handler(matchedRequest.payload ?? {}, matchedRequest),\n );\n });\n };\n\n private readonly readHandlers = (\n capability: object,\n ): Array<[string, ExtensionCapabilityHandler]> =>\n Object.entries(capability).filter(\n (entry): entry is [string, ExtensionCapabilityHandler] =>\n typeof entry[1] === \"function\",\n );\n}\n\nexport class NextClawExtension {\n readonly eventBus: EventBus;\n readonly channels: ExtensionChannels;\n readonly capabilities: ExtensionCapabilities;\n readonly diagnostics: ExtensionDiagnostics;\n readonly observations: ExtensionObservations;\n readonly extensionId: string;\n readonly generation: string;\n private readonly transport: ExtensionTransportService;\n private eventStreamSubscription: {\n close: () => void;\n ready: Promise<void>;\n } | null = null;\n private parentProcessWatcher: ReturnType<typeof setInterval> | null = null;\n\n constructor(options: NextClawExtensionOptions = {}) {\n this.transport = new ExtensionTransportService(options);\n this.extensionId = this.transport.extensionId;\n this.generation = this.transport.generation;\n this.eventBus = new EventBus({\n onFirstSubscriber: () => {\n this.eventStreamSubscription ??= this.transport.subscribe((event) => {\n this.eventBus.emitEnvelope(this.toEventBusEnvelope(event));\n });\n },\n onNoSubscribers: () => {\n this.eventStreamSubscription?.close();\n this.eventStreamSubscription = null;\n },\n });\n this.channels = new ExtensionChannelRegistry({\n eventBus: this.eventBus,\n transport: this.transport,\n });\n this.capabilities = new ExtensionCapabilityRegistry({\n eventBus: this.eventBus,\n extensionId: this.extensionId,\n transport: this.transport,\n });\n this.observations = new ExtensionObservationService({\n eventBus: this.eventBus,\n extensionId: this.extensionId,\n generation: this.generation,\n transport: this.transport,\n });\n this.diagnostics = new ExtensionDiagnosticClient(\n this.transport,\n Math.max(50, options.diagnosticTimeoutMs ?? 2_000),\n );\n this.parentProcessWatcher = this.startParentProcessWatcher();\n }\n\n readonly close = (): void => {\n if (this.parentProcessWatcher) {\n clearInterval(this.parentProcessWatcher);\n this.parentProcessWatcher = null;\n }\n this.eventStreamSubscription?.close();\n this.eventStreamSubscription = null;\n void this.observations.close();\n };\n\n readonly ready = async (): Promise<void> => {\n const subscription = this.eventStreamSubscription;\n if (!subscription) {\n throw new Error(\n `Extension ${this.extensionId} has no event-stream subscription.`,\n );\n }\n await subscription.ready;\n await this.transport.reportReady(process.pid);\n };\n\n readonly onRequest = (handler: ExtensionRequestHandler): (() => void) =>\n this.eventBus.subscribeAll((event) => {\n if (event.type !== \"extension.request\") {\n return;\n }\n const request = readRequest(event.payload);\n if (\n !request ||\n request.extensionId !== this.extensionId ||\n request.generation !== this.generation\n ) {\n return;\n }\n void handleRequest(this.transport, request, handler);\n });\n\n private readonly toEventBusEnvelope = (\n event: ExtensionTransportEnvelope,\n ): ExtensionTransportEnvelope => ({\n ...event,\n emittedAt: event.emittedAt ?? new Date().toISOString(),\n source: event.source ?? \"event-stream\",\n });\n\n private readonly startParentProcessWatcher = (): ReturnType<\n typeof setInterval\n > | null => {\n const parentPid = readParentProcessId();\n if (!parentPid) {\n return null;\n }\n const timer = setInterval(() => {\n if (isProcessRunning(parentPid)) {\n return;\n }\n this.close();\n process.exit(0);\n }, EXTENSION_PARENT_WATCH_INTERVAL_MS);\n (timer as NodeJS.Timeout).unref?.();\n return timer;\n };\n}\n"],"mappings":";;;;;;AAiBA,MAAM,qCAAqC;AAE3C,IAAM,4BAAN,MAAgE;CAC9D,YACE,WACA,WACA;AAFiB,OAAA,YAAA;AACA,OAAA,YAAA;;CAGnB,iBAA0B,aAA8B;EACtD,MAAM,qBAAqB,UAAU,MAAM;AAC3C,MAAI,CAAC,mBACH,QAAO,YAAY;AAErB,SAAO,WAAW,SAAS,CACxB,OAAO,mBAAmB,CAC1B,OAAO,MAAM,CACb,MAAM,GAAG,GAAG;;CAGjB,OAA8C,OAAO,UAAU;EAC7D,MAAM,aAAa,IAAI,iBAAiB;EACxC,IAAI;AACJ,MAAI;AACF,SAAM,QAAQ,KAAK,CACjB,KAAK,UAAU,YACb,SAAS,YAAY,UAAU,eAAe,EAC9C,OACA,EAAE,QAAQ,WAAW,QAAQ,CAC9B,EACD,IAAI,SAAgB,UAAU,WAAW;AACvC,cAAU,iBAAiB;AACzB,gBAAW,OAAO;AAClB,4BAAO,IAAI,MAAM,2CAA2C,CAAC;OAC5D,KAAK,UAAU;AAClB,YAAQ,SAAS;KACjB,CACH,CAAC;AACF,UAAO;UACD;AACN,UAAO;YACC;AACR,OAAI,QACF,cAAa,QAAQ;;;;AAM7B,IAAM,2BAAN,MAA4D;CAC1D,2BAA4B,IAAI,KAAsC;CAEtE,YACE,QAIA;AAJiB,OAAA,SAAA;;CAMnB,OAAgB,cAA+C;EAC7D,MAAM,sBAAsB,UAAU,MAAM;AAC5C,MAAI,CAAC,oBACH,OAAM,IAAI,MAAM,yBAAyB;EAE3C,MAAM,WAAW,KAAK,SAAS,IAAI,oBAAoB;AACvD,MAAI,SACF,QAAO;EAET,MAAM,UAAU,IAAI,wBAAwB;GAC1C,WAAW;GACX,UAAU,KAAK,OAAO;GACtB,WAAW,KAAK,OAAO;GACxB,CAAC;AACF,OAAK,SAAS,IAAI,qBAAqB,QAAQ;AAC/C,SAAO;;;AAIX,SAAS,YAAY,SAA2C;AAC9D,KAAI,CAAC,WAAW,OAAO,YAAY,YAAY,MAAM,QAAQ,QAAQ,CACnE,QAAO;CAET,MAAM,SAAS;AACf,KACE,OAAO,OAAO,cAAc,YAC5B,OAAO,OAAO,gBAAgB,YAC9B,OAAO,OAAO,eAAe,YAC7B,OAAO,OAAO,SAAS,SAEvB,QAAO;AAET,QAAO;EACL,WAAW,OAAO;EAClB,aAAa,OAAO;EACpB,YAAY,OAAO;EACnB,MAAM,OAAO;EACb,SACE,OAAO,WACP,OAAO,OAAO,YAAY,YAC1B,CAAC,MAAM,QAAQ,OAAO,QAAQ,GACzB,OAAO,UACR,EAAE;EACT;;AAGH,SAAS,cAAc,OAAe,MAAsB;CAC1D,MAAM,aAAa,MAAM,MAAM;AAC/B,KAAI,CAAC,WACH,OAAM,IAAI,MAAM,GAAG,KAAK,eAAe;AAEzC,QAAO;;AAGT,eAAe,cACb,WACA,SACA,SACe;AACf,KAAI;EACF,MAAM,OAAO,MAAM,QAAQ,QAAQ;AACnC,QAAM,UAAU,iBAAiB;GAC/B,WAAW,QAAQ;GACnB,IAAI;GACJ;GACD,CAAC;UACK,OAAO;AACd,QAAM,UAAU,iBAAiB;GAC/B,WAAW,QAAQ;GACnB,IAAI;GACJ,OAAO,EACL,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,EAChE;GACF,CAAC;;;AAIN,SAAS,sBAAqC;CAC5C,MAAM,MAAM,QAAQ,IAAI,+BAA+B,MAAM;AAC7D,KAAI,CAAC,IACH,QAAO;CAET,MAAM,MAAM,OAAO,IAAI;AACvB,QAAO,OAAO,UAAU,IAAI,IAAI,MAAM,IAAI,MAAM;;AAGlD,SAAS,iBAAiB,KAAsB;AAC9C,KAAI;AACF,UAAQ,KAAK,KAAK,EAAE;AACpB,SAAO;UACA,OAAO;AACd,SAAQ,MAAgC,SAAS;;;AAIrD,IAAM,8BAAN,MAAmE;CACjE,YACE,QAKA;AALiB,OAAA,SAAA;;CAOnB,WAAoB,WAAmB,eAAqC;EAC1E,MAAM,sBAAsB,cAAc,WAAW,YAAY;EACjE,MAAM,sBAAsB,KAAK,aAAa,WAAW,CAAC,KACvD,CAAC,MAAM,aACN,KAAK,eAAe,GAAG,oBAAoB,GAAG,QAAQ,QAAQ,CACjE;AACD,MAAI,oBAAoB,WAAW,EACjC,OAAM,IAAI,MACR,eAAe,oBAAoB,4BACpC;AAEH,eAAa;AACX,QAAK,MAAM,eAAe,oBACxB,cAAa;;;CAKnB,kBACE,MACA,YACiB;EACjB,MAAM,iBAAiB,cAAc,MAAM,OAAO;AAClD,SAAO,KAAK,OAAO,SAAS,cAAc,UAAU;AAClD,OAAI,MAAM,SAAS,oBACjB;GAEF,MAAM,UAAU,YAAY,MAAM,QAAQ;AAC1C,OACE,CAAC,WACD,QAAQ,gBAAgB,KAAK,OAAO,eACpC,QAAQ,eAAe,KAAK,OAAO,UAAU,cAC7C,QAAQ,SAAS,eAEjB;AAEG,iBACH,KAAK,OAAO,WACZ,SACA,OAAO,mBACL,MAAM,QAAQ,eAAe,WAAW,EAAE,EAAE,eAAe,CAC9D;IACD;;CAGJ,gBACE,eAEA,OAAO,QAAQ,WAAW,CAAC,QACxB,UACC,OAAO,MAAM,OAAO,WACvB;;AAGL,IAAa,oBAAb,MAA+B;CAC7B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA,0BAGW;CACX,uBAAsE;CAEtE,YAAY,UAAoC,EAAE,EAAE;AAClD,OAAK,YAAY,IAAI,0BAA0B,QAAQ;AACvD,OAAK,cAAc,KAAK,UAAU;AAClC,OAAK,aAAa,KAAK,UAAU;AACjC,OAAK,WAAW,IAAI,SAAS;GAC3B,yBAAyB;AACvB,SAAK,4BAA4B,KAAK,UAAU,WAAW,UAAU;AACnE,UAAK,SAAS,aAAa,KAAK,mBAAmB,MAAM,CAAC;MAC1D;;GAEJ,uBAAuB;AACrB,SAAK,yBAAyB,OAAO;AACrC,SAAK,0BAA0B;;GAElC,CAAC;AACF,OAAK,WAAW,IAAI,yBAAyB;GAC3C,UAAU,KAAK;GACf,WAAW,KAAK;GACjB,CAAC;AACF,OAAK,eAAe,IAAI,4BAA4B;GAClD,UAAU,KAAK;GACf,aAAa,KAAK;GAClB,WAAW,KAAK;GACjB,CAAC;AACF,OAAK,eAAe,IAAI,4BAA4B;GAClD,UAAU,KAAK;GACf,aAAa,KAAK;GAClB,YAAY,KAAK;GACjB,WAAW,KAAK;GACjB,CAAC;AACF,OAAK,cAAc,IAAI,0BACrB,KAAK,WACL,KAAK,IAAI,IAAI,QAAQ,uBAAuB,IAAM,CACnD;AACD,OAAK,uBAAuB,KAAK,2BAA2B;;CAG9D,cAA6B;AAC3B,MAAI,KAAK,sBAAsB;AAC7B,iBAAc,KAAK,qBAAqB;AACxC,QAAK,uBAAuB;;AAE9B,OAAK,yBAAyB,OAAO;AACrC,OAAK,0BAA0B;AAC1B,OAAK,aAAa,OAAO;;CAGhC,QAAiB,YAA2B;EAC1C,MAAM,eAAe,KAAK;AAC1B,MAAI,CAAC,aACH,OAAM,IAAI,MACR,aAAa,KAAK,YAAY,oCAC/B;AAEH,QAAM,aAAa;AACnB,QAAM,KAAK,UAAU,YAAY,QAAQ,IAAI;;CAG/C,aAAsB,YACpB,KAAK,SAAS,cAAc,UAAU;AACpC,MAAI,MAAM,SAAS,oBACjB;EAEF,MAAM,UAAU,YAAY,MAAM,QAAQ;AAC1C,MACE,CAAC,WACD,QAAQ,gBAAgB,KAAK,eAC7B,QAAQ,eAAe,KAAK,WAE5B;AAEG,gBAAc,KAAK,WAAW,SAAS,QAAQ;GACpD;CAEJ,sBACE,WACgC;EAChC,GAAG;EACH,WAAW,MAAM,8BAAa,IAAI,MAAM,EAAC,aAAa;EACtD,QAAQ,MAAM,UAAU;EACzB;CAED,kCAEY;EACV,MAAM,YAAY,qBAAqB;AACvC,MAAI,CAAC,UACH,QAAO;EAET,MAAM,QAAQ,kBAAkB;AAC9B,OAAI,iBAAiB,UAAU,CAC7B;AAEF,QAAK,OAAO;AACZ,WAAQ,KAAK,EAAE;KACd,mCAAmC;AACrC,QAAyB,SAAS;AACnC,SAAO"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { getKeyId, ingressKeys } from "@nextclaw/shared";
|
|
2
|
+
//#region src/services/extension-observation.service.ts
|
|
3
|
+
function asRecord(value) {
|
|
4
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
5
|
+
}
|
|
6
|
+
function requiredString(value, field) {
|
|
7
|
+
if (typeof value !== "string" || !value.trim()) throw new Error(`${field} is required.`);
|
|
8
|
+
return value.trim();
|
|
9
|
+
}
|
|
10
|
+
var ExtensionObservationService = class {
|
|
11
|
+
subscriptions = /* @__PURE__ */ new Map();
|
|
12
|
+
requestUnsubscribe = null;
|
|
13
|
+
constructor(params) {
|
|
14
|
+
this.params = params;
|
|
15
|
+
}
|
|
16
|
+
provide = (handlers) => {
|
|
17
|
+
if (!handlers.read && !handlers.subscribe) throw new Error("observations.provide requires read or subscribe.");
|
|
18
|
+
this.requestUnsubscribe?.();
|
|
19
|
+
this.closeAll();
|
|
20
|
+
const unsubscribe = this.params.eventBus.subscribeAll((event) => {
|
|
21
|
+
if (event.type !== "extension.request") return;
|
|
22
|
+
const request = asRecord(event.payload);
|
|
23
|
+
if (request.extensionId !== this.params.extensionId || request.generation !== this.params.generation || typeof request.requestId !== "string") return;
|
|
24
|
+
this.handleRequest(request, handlers);
|
|
25
|
+
});
|
|
26
|
+
this.requestUnsubscribe = unsubscribe;
|
|
27
|
+
return () => {
|
|
28
|
+
if (this.requestUnsubscribe !== unsubscribe) return;
|
|
29
|
+
this.requestUnsubscribe = null;
|
|
30
|
+
unsubscribe();
|
|
31
|
+
this.closeAll();
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
close = async () => {
|
|
35
|
+
this.requestUnsubscribe?.();
|
|
36
|
+
this.requestUnsubscribe = null;
|
|
37
|
+
await this.closeAll();
|
|
38
|
+
};
|
|
39
|
+
handleRequest = async (request, handlers) => {
|
|
40
|
+
const requestId = requiredString(request.requestId, "requestId");
|
|
41
|
+
try {
|
|
42
|
+
const payload = asRecord(request.payload);
|
|
43
|
+
let data;
|
|
44
|
+
if (request.kind === "observation.read") {
|
|
45
|
+
if (!handlers.read) throw new Error("Observation read is not provided.");
|
|
46
|
+
const controller = new AbortController();
|
|
47
|
+
data = await handlers.read({
|
|
48
|
+
config: payload.config,
|
|
49
|
+
signal: controller.signal
|
|
50
|
+
});
|
|
51
|
+
} else if (request.kind === "observation.subscribe") {
|
|
52
|
+
if (!handlers.subscribe) throw new Error("Observation events are not provided.");
|
|
53
|
+
const subscriptionId = requiredString(payload.subscriptionId, "subscriptionId");
|
|
54
|
+
await this.replaceSubscription(subscriptionId, handlers.subscribe, payload);
|
|
55
|
+
data = { replay: handlers.replay ?? "unsupported" };
|
|
56
|
+
} else if (request.kind === "observation.unsubscribe") {
|
|
57
|
+
await this.closeSubscription(requiredString(payload.subscriptionId, "subscriptionId"));
|
|
58
|
+
data = { acknowledged: true };
|
|
59
|
+
} else return;
|
|
60
|
+
await this.params.transport.respondToRequest({
|
|
61
|
+
requestId,
|
|
62
|
+
ok: true,
|
|
63
|
+
data
|
|
64
|
+
});
|
|
65
|
+
} catch (error) {
|
|
66
|
+
await this.params.transport.respondToRequest({
|
|
67
|
+
requestId,
|
|
68
|
+
ok: false,
|
|
69
|
+
error: { message: error instanceof Error ? error.message : String(error) }
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
replaceSubscription = async (subscriptionId, subscribe, payload) => {
|
|
74
|
+
const existing = this.subscriptions.get(subscriptionId);
|
|
75
|
+
if (existing) {
|
|
76
|
+
await existing.ready;
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const abort = new AbortController();
|
|
80
|
+
const emit = async (event) => {
|
|
81
|
+
await this.params.transport.postIngress(getKeyId(ingressKeys.extension.observationEvent), {
|
|
82
|
+
subscriptionId,
|
|
83
|
+
event: {
|
|
84
|
+
eventId: requiredString(event.id, "event.id"),
|
|
85
|
+
eventType: requiredString(event.type, "event.type"),
|
|
86
|
+
occurredAt: requiredString(event.occurredAt, "event.occurredAt"),
|
|
87
|
+
...event.observedAt ? { observedAt: event.observedAt } : {},
|
|
88
|
+
...event.cursor ? { cursor: event.cursor } : {},
|
|
89
|
+
...event.dedupeKey ? { dedupeKey: event.dedupeKey } : {},
|
|
90
|
+
payload: event.payload,
|
|
91
|
+
...event.sourceRefs ? { sourceRefs: event.sourceRefs } : {},
|
|
92
|
+
...event.causationId ? { causationId: event.causationId } : {},
|
|
93
|
+
...event.correlationId ? { correlationId: event.correlationId } : {}
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
};
|
|
97
|
+
const subscription = {
|
|
98
|
+
abort,
|
|
99
|
+
ready: Promise.resolve()
|
|
100
|
+
};
|
|
101
|
+
const ready = Promise.resolve(subscribe({
|
|
102
|
+
subscriptionId,
|
|
103
|
+
config: payload.config,
|
|
104
|
+
...typeof payload.cursor === "string" ? { cursor: payload.cursor } : {},
|
|
105
|
+
emit,
|
|
106
|
+
signal: abort.signal
|
|
107
|
+
})).then((cleanup) => {
|
|
108
|
+
if (this.subscriptions.get(subscriptionId) === subscription && cleanup) subscription.cleanup = cleanup;
|
|
109
|
+
});
|
|
110
|
+
subscription.ready = ready;
|
|
111
|
+
this.subscriptions.set(subscriptionId, subscription);
|
|
112
|
+
try {
|
|
113
|
+
await ready;
|
|
114
|
+
} catch (error) {
|
|
115
|
+
if (this.subscriptions.get(subscriptionId) === subscription) {
|
|
116
|
+
this.subscriptions.delete(subscriptionId);
|
|
117
|
+
abort.abort();
|
|
118
|
+
}
|
|
119
|
+
throw error;
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
closeSubscription = async (subscriptionId, expected) => {
|
|
123
|
+
const subscription = this.subscriptions.get(subscriptionId);
|
|
124
|
+
if (!subscription || expected && subscription !== expected) return;
|
|
125
|
+
this.subscriptions.delete(subscriptionId);
|
|
126
|
+
subscription.abort.abort();
|
|
127
|
+
await subscription.ready.catch(() => void 0);
|
|
128
|
+
await subscription.cleanup?.();
|
|
129
|
+
};
|
|
130
|
+
closeAll = async () => {
|
|
131
|
+
await Promise.all([...this.subscriptions.entries()].map(([subscriptionId, subscription]) => this.closeSubscription(subscriptionId, subscription)));
|
|
132
|
+
};
|
|
133
|
+
};
|
|
134
|
+
//#endregion
|
|
135
|
+
export { ExtensionObservationService };
|
|
136
|
+
|
|
137
|
+
//# sourceMappingURL=extension-observation.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extension-observation.service.js","names":[],"sources":["../../src/services/extension-observation.service.ts"],"sourcesContent":["import { getKeyId, ingressKeys, type Unsubscribe } from \"@nextclaw/shared\";\nimport type {\n ExtensionObservationEmitInput,\n ExtensionObservationHandlers,\n ExtensionObservations,\n} from \"../types/extension-sdk.types.js\";\nimport type { ExtensionTransportService } from \"./extension-transport.service.js\";\n\nfunction asRecord(value: unknown): Record<string, unknown> {\n return value && typeof value === \"object\" && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : {};\n}\n\nfunction requiredString(value: unknown, field: string): string {\n if (typeof value !== \"string\" || !value.trim()) {\n throw new Error(`${field} is required.`);\n }\n return value.trim();\n}\n\nexport class ExtensionObservationService implements ExtensionObservations {\n private readonly subscriptions = new Map<\n string,\n {\n abort: AbortController;\n cleanup?: () => void | Promise<void>;\n ready: Promise<void>;\n }\n >();\n private requestUnsubscribe: Unsubscribe | null = null;\n\n constructor(\n private readonly params: {\n eventBus: {\n subscribeAll: (\n handler: (event: { type: string; payload: unknown }) => void,\n ) => Unsubscribe;\n };\n extensionId: string;\n generation: string;\n transport: ExtensionTransportService;\n },\n ) {}\n\n readonly provide = (handlers: ExtensionObservationHandlers): Unsubscribe => {\n if (!handlers.read && !handlers.subscribe) {\n throw new Error(\"observations.provide requires read or subscribe.\");\n }\n this.requestUnsubscribe?.();\n void this.closeAll();\n const unsubscribe = this.params.eventBus.subscribeAll((event) => {\n if (event.type !== \"extension.request\") return;\n const request = asRecord(event.payload);\n if (\n request.extensionId !== this.params.extensionId ||\n request.generation !== this.params.generation ||\n typeof request.requestId !== \"string\"\n )\n return;\n void this.handleRequest(request, handlers);\n });\n this.requestUnsubscribe = unsubscribe;\n return () => {\n if (this.requestUnsubscribe !== unsubscribe) return;\n this.requestUnsubscribe = null;\n unsubscribe();\n void this.closeAll();\n };\n };\n\n readonly close = async (): Promise<void> => {\n this.requestUnsubscribe?.();\n this.requestUnsubscribe = null;\n await this.closeAll();\n };\n\n private readonly handleRequest = async (\n request: Record<string, unknown>,\n handlers: ExtensionObservationHandlers,\n ): Promise<void> => {\n const requestId = requiredString(request.requestId, \"requestId\");\n try {\n const payload = asRecord(request.payload);\n let data: unknown;\n if (request.kind === \"observation.read\") {\n if (!handlers.read)\n throw new Error(\"Observation read is not provided.\");\n const controller = new AbortController();\n data = await handlers.read({\n config: payload.config,\n signal: controller.signal,\n });\n } else if (request.kind === \"observation.subscribe\") {\n if (!handlers.subscribe)\n throw new Error(\"Observation events are not provided.\");\n const subscriptionId = requiredString(\n payload.subscriptionId,\n \"subscriptionId\",\n );\n await this.replaceSubscription(\n subscriptionId,\n handlers.subscribe,\n payload,\n );\n data = { replay: handlers.replay ?? \"unsupported\" };\n } else if (request.kind === \"observation.unsubscribe\") {\n await this.closeSubscription(\n requiredString(payload.subscriptionId, \"subscriptionId\"),\n );\n data = { acknowledged: true };\n } else return;\n await this.params.transport.respondToRequest({\n requestId,\n ok: true,\n data,\n });\n } catch (error) {\n await this.params.transport.respondToRequest({\n requestId,\n ok: false,\n error: {\n message: error instanceof Error ? error.message : String(error),\n },\n });\n }\n };\n\n private readonly replaceSubscription = async (\n subscriptionId: string,\n subscribe: NonNullable<ExtensionObservationHandlers[\"subscribe\"]>,\n payload: Record<string, unknown>,\n ): Promise<void> => {\n const existing = this.subscriptions.get(subscriptionId);\n if (existing) {\n await existing.ready;\n return;\n }\n const abort = new AbortController();\n const emit = async (\n event: ExtensionObservationEmitInput,\n ): Promise<void> => {\n await this.params.transport.postIngress(\n getKeyId(ingressKeys.extension.observationEvent),\n {\n subscriptionId,\n event: {\n eventId: requiredString(event.id, \"event.id\"),\n eventType: requiredString(event.type, \"event.type\"),\n occurredAt: requiredString(event.occurredAt, \"event.occurredAt\"),\n ...(event.observedAt ? { observedAt: event.observedAt } : {}),\n ...(event.cursor ? { cursor: event.cursor } : {}),\n ...(event.dedupeKey ? { dedupeKey: event.dedupeKey } : {}),\n payload: event.payload,\n ...(event.sourceRefs ? { sourceRefs: event.sourceRefs } : {}),\n ...(event.causationId ? { causationId: event.causationId } : {}),\n ...(event.correlationId\n ? { correlationId: event.correlationId }\n : {}),\n },\n },\n );\n };\n const subscription: {\n abort: AbortController;\n cleanup?: () => void | Promise<void>;\n ready: Promise<void>;\n } = {\n abort,\n ready: Promise.resolve(),\n };\n const ready = Promise.resolve(\n subscribe({\n subscriptionId,\n config: payload.config,\n ...(typeof payload.cursor === \"string\"\n ? { cursor: payload.cursor }\n : {}),\n emit,\n signal: abort.signal,\n }),\n ).then((cleanup) => {\n if (this.subscriptions.get(subscriptionId) === subscription && cleanup) {\n subscription.cleanup = cleanup;\n }\n });\n subscription.ready = ready;\n this.subscriptions.set(subscriptionId, subscription);\n try {\n await ready;\n } catch (error) {\n if (this.subscriptions.get(subscriptionId) === subscription) {\n this.subscriptions.delete(subscriptionId);\n abort.abort();\n }\n throw error;\n }\n };\n\n private readonly closeSubscription = async (\n subscriptionId: string,\n expected?: {\n abort: AbortController;\n cleanup?: () => void | Promise<void>;\n ready: Promise<void>;\n },\n ): Promise<void> => {\n const subscription = this.subscriptions.get(subscriptionId);\n if (!subscription || (expected && subscription !== expected)) return;\n this.subscriptions.delete(subscriptionId);\n subscription.abort.abort();\n await subscription.ready.catch(() => undefined);\n await subscription.cleanup?.();\n };\n\n private readonly closeAll = async (): Promise<void> => {\n await Promise.all(\n [...this.subscriptions.entries()].map(([subscriptionId, subscription]) =>\n this.closeSubscription(subscriptionId, subscription),\n ),\n );\n };\n}\n"],"mappings":"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"}
|
|
@@ -78,10 +78,40 @@ type ExtensionChannels = {
|
|
|
78
78
|
type ExtensionRequestHandler = (request: ExtensionRequest) => unknown | Promise<unknown>;
|
|
79
79
|
type ExtensionCapabilityPayload = Record<string, unknown>;
|
|
80
80
|
type ExtensionCapabilityHandler<TPayload extends ExtensionCapabilityPayload = ExtensionCapabilityPayload> = (payload: TPayload, request: ExtensionRequest) => unknown | Promise<unknown>;
|
|
81
|
+
type ExtensionObservationEmitInput = {
|
|
82
|
+
id: string;
|
|
83
|
+
type: string;
|
|
84
|
+
occurredAt: string;
|
|
85
|
+
observedAt?: string;
|
|
86
|
+
cursor?: string;
|
|
87
|
+
dedupeKey?: string;
|
|
88
|
+
payload: unknown;
|
|
89
|
+
sourceRefs?: string[];
|
|
90
|
+
causationId?: string;
|
|
91
|
+
correlationId?: string;
|
|
92
|
+
};
|
|
93
|
+
type ExtensionObservationHandlers = {
|
|
94
|
+
read?: (input: {
|
|
95
|
+
config: unknown;
|
|
96
|
+
signal: AbortSignal;
|
|
97
|
+
}) => unknown | Promise<unknown>;
|
|
98
|
+
subscribe?: (input: {
|
|
99
|
+
subscriptionId: string;
|
|
100
|
+
config: unknown;
|
|
101
|
+
cursor?: string;
|
|
102
|
+
emit: (event: ExtensionObservationEmitInput) => Promise<void>;
|
|
103
|
+
signal: AbortSignal;
|
|
104
|
+
}) => void | (() => void | Promise<void>) | Promise<void | (() => void | Promise<void>)>;
|
|
105
|
+
replay?: "supported" | "unsupported";
|
|
106
|
+
};
|
|
107
|
+
type ExtensionObservations = {
|
|
108
|
+
provide: (handlers: ExtensionObservationHandlers) => Unsubscribe;
|
|
109
|
+
close: () => Promise<void>;
|
|
110
|
+
};
|
|
81
111
|
type ExtensionCapabilities = {
|
|
82
112
|
provide: (namespace: string, capability: object) => Unsubscribe;
|
|
83
113
|
provideHandler: (kind: string, handler: ExtensionCapabilityHandler) => Unsubscribe;
|
|
84
114
|
};
|
|
85
115
|
//#endregion
|
|
86
|
-
export { type ChannelCommandExecuteRequest, type ChannelCommandExecuteResponse, type ChannelCommandListRequest, type ChannelCommandListResponse, type ChannelCommandOption, type ChannelCommandOptionType, type ChannelCommandSpec, type ChannelConfigGetRequest, ChannelConfigGetResponse, type ChannelFileContent, type ChannelImageContent, type ChannelMessageContent, type ChannelSubmittedAttachment, type ChannelSubmittedMessage, type ChannelTextContent, ExtensionCapabilities, ExtensionCapabilityHandler, ExtensionCapabilityPayload, ExtensionChannel, ExtensionChannelCommands, ExtensionChannelConfig, ExtensionChannels, ExtensionDiagnostics, ExtensionRequest, ExtensionRequestHandler, type ExtensionRequestResponse, ExtensionTransportEnvelope, NextClawExtensionOptions, NextClawExtensionWebSocketLike };
|
|
116
|
+
export { type ChannelCommandExecuteRequest, type ChannelCommandExecuteResponse, type ChannelCommandListRequest, type ChannelCommandListResponse, type ChannelCommandOption, type ChannelCommandOptionType, type ChannelCommandSpec, type ChannelConfigGetRequest, ChannelConfigGetResponse, type ChannelFileContent, type ChannelImageContent, type ChannelMessageContent, type ChannelSubmittedAttachment, type ChannelSubmittedMessage, type ChannelTextContent, ExtensionCapabilities, ExtensionCapabilityHandler, ExtensionCapabilityPayload, ExtensionChannel, ExtensionChannelCommands, ExtensionChannelConfig, ExtensionChannels, ExtensionDiagnostics, ExtensionObservationEmitInput, ExtensionObservationHandlers, ExtensionObservations, ExtensionRequest, ExtensionRequestHandler, type ExtensionRequestResponse, ExtensionTransportEnvelope, NextClawExtensionOptions, NextClawExtensionWebSocketLike };
|
|
87
117
|
//# sourceMappingURL=extension-sdk.types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extension-sdk.types.d.ts","names":[],"sources":["../../src/types/extension-sdk.types.ts"],"mappings":";;;;KA2BY,wBAAA;EACV,QAAA;EACA,KAAA;EACA,WAAA;EACA,UAAA;EACA,mBAAA;EACA,KAAA,UAAe,KAAA;EACf,gBAAA,IACE,GAAA,UACA,OAAA,GAAU,wCAAA,KACP,8BAAA;AAAA;AAAA,KAGK,wCAAA;EACV,OAAA,GAAU,MAAA;AAAA;AAAA,KAGA,8BAAA;EACV,MAAA;EACA,SAAA,IAAa,KAAA;IAAS,IAAA;EAAA;EACtB,OAAA,IAAW,KAAA;EACX,OAAA;EACA,KAAA;AAAA;AAAA,KAGU,0BAAA;EACV,IAAA;EACA,WAAA;EACA,UAAA;EACA,OAAA,EAAS,QAAA;EACT,SAAA;EACA,MAAA;AAAA;AAAA,KAGU,gBAAA;EACV,SAAA;EACA,WAAA;EACA,UAAA;EACA,IAAA;EACA,OAAA,GAAU,MAAA;AAAA;AAAA,KAGA,wBAAA;EACV,MAAA,EAAQ,OAAA;AAAA;AAAA,KAGE,sBAAA;EACV,GAAA,2BAA8B,OAAA,CAAQ,OAAA;EACtC,QAAA,sBACE,OAAA,GAAU,MAAA,EAAQ,OAAA,YAAmB,OAAA,WAClC,WAAA;AAAA;AAAA,KAGK,wBAAA;EACV,IAAA,QAAY,OAAA,CAAQ,2BAAA;EACpB,OAAA,GAAU,KAAA;IACR,WAAA;IACA,IAAA,GAAO,MAAA;IACP,cAAA;IACA,QAAA;IACA,QAAA,GAAW,MAAA;EAAA,MACP,OAAA,CAAQ,sCAAA;EACd,WAAA,GAAc,KAAA;IACZ,OAAA;IACA,cAAA;IACA,QAAA;IACA,QAAA,GAAW,MAAA;EAAA,MACP,OAAA,CAAQ,sCAAA;AAAA;AAAA,KAGJ,oBAAA;EACV,aAAA,GAAgB,iBAAA;EAChB,IAAA,GAAO,KAAA,EAAO,iCAAA,KAAsC,OAAA;AAAA;AAAA,KAG1C,gBAAA;EACV,EAAA;EACA,aAAA,
|
|
1
|
+
{"version":3,"file":"extension-sdk.types.d.ts","names":[],"sources":["../../src/types/extension-sdk.types.ts"],"mappings":";;;;KA2BY,wBAAA;EACV,QAAA;EACA,KAAA;EACA,WAAA;EACA,UAAA;EACA,mBAAA;EACA,KAAA,UAAe,KAAA;EACf,gBAAA,IACE,GAAA,UACA,OAAA,GAAU,wCAAA,KACP,8BAAA;AAAA;AAAA,KAGK,wCAAA;EACV,OAAA,GAAU,MAAA;AAAA;AAAA,KAGA,8BAAA;EACV,MAAA;EACA,SAAA,IAAa,KAAA;IAAS,IAAA;EAAA;EACtB,OAAA,IAAW,KAAA;EACX,OAAA;EACA,KAAA;AAAA;AAAA,KAGU,0BAAA;EACV,IAAA;EACA,WAAA;EACA,UAAA;EACA,OAAA,EAAS,QAAA;EACT,SAAA;EACA,MAAA;AAAA;AAAA,KAGU,gBAAA;EACV,SAAA;EACA,WAAA;EACA,UAAA;EACA,IAAA;EACA,OAAA,GAAU,MAAA;AAAA;AAAA,KAGA,wBAAA;EACV,MAAA,EAAQ,OAAA;AAAA;AAAA,KAGE,sBAAA;EACV,GAAA,2BAA8B,OAAA,CAAQ,OAAA;EACtC,QAAA,sBACE,OAAA,GAAU,MAAA,EAAQ,OAAA,YAAmB,OAAA,WAClC,WAAA;AAAA;AAAA,KAGK,wBAAA;EACV,IAAA,QAAY,OAAA,CAAQ,2BAAA;EACpB,OAAA,GAAU,KAAA;IACR,WAAA;IACA,IAAA,GAAO,MAAA;IACP,cAAA;IACA,QAAA;IACA,QAAA,GAAW,MAAA;EAAA,MACP,OAAA,CAAQ,sCAAA;EACd,WAAA,GAAc,KAAA;IACZ,OAAA;IACA,cAAA;IACA,QAAA;IACA,QAAA,GAAW,MAAA;EAAA,MACP,OAAA,CAAQ,sCAAA;AAAA;AAAA,KAGJ,oBAAA;EACV,aAAA,GAAgB,iBAAA;EAChB,IAAA,GAAO,KAAA,EAAO,iCAAA,KAAsC,OAAA;AAAA;AAAA,KAG1C,gBAAA;EACV,EAAA;EACA,aAAA,GACE,KAAA,EAAO,IAAA,CAAK,2CAAA,mBACT,OAAA;EACL,UAAA,GACE,OAAA,GAAU,KAAA,EAAO,gBAAA,YAA4B,OAAA,WAC1C,WAAA;EACL,MAAA,EAAQ,sBAAA;EACR,QAAA,EAAU,wBAAA;AAAA;AAAA,KAKA,iBAAA;EACV,GAAA,GAAM,SAAA,aAAsB,gBAAA;AAAA;AAAA,KAGlB,uBAAA,IACV,OAAA,EAAS,gBAAA,eACI,OAAA;AAAA,KAEH,0BAAA,GAA6B,MAAA;AAAA,KAE7B,0BAAA,kBACO,0BAAA,GAA6B,0BAAA,KAE9C,OAAA,EAAS,QAAA,EACT,OAAA,EAAS,gBAAA,eACI,OAAA;AAAA,KAEH,6BAAA;EACV,EAAA;EACA,IAAA;EACA,UAAA;EACA,UAAA;EACA,MAAA;EACA,SAAA;EACA,OAAA;EACA,UAAA;EACA,WAAA;EACA,aAAA;AAAA;AAAA,KAGU,4BAAA;EACV,IAAA,IAAQ,KAAA;IACN,MAAA;IACA,MAAA,EAAQ,WAAA;EAAA,gBACM,OAAA;EAChB,SAAA,IAAa,KAAA;IACX,cAAA;IACA,MAAA;IACA,MAAA;IACA,IAAA,GAAO,KAAA,EAAO,6BAAA,KAAkC,OAAA;IAChD,MAAA,EAAQ,WAAA;EAAA,2BAGQ,OAAA,UACd,OAAA,sBAA6B,OAAA;EACjC,MAAA;AAAA;AAAA,KAGU,qBAAA;EACV,OAAA,GAAU,QAAA,EAAU,4BAAA,KAAiC,WAAA;EACrD,KAAA,QAAa,OAAA;AAAA;AAAA,KAGH,qBAAA;EACV,OAAA,GAAU,SAAA,UAAmB,UAAA,aAAuB,WAAA;EACpD,cAAA,GACE,IAAA,UACA,OAAA,EAAS,0BAAA,KACN,WAAA;AAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nextclaw/extension-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0-beta.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Backend SDK for NextClaw extension server processes.",
|
|
6
6
|
"type": "module",
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
],
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"ws": "^8.18.0",
|
|
39
|
-
"@nextclaw/
|
|
40
|
-
"@nextclaw/
|
|
39
|
+
"@nextclaw/ncp": "0.9.0-beta.0",
|
|
40
|
+
"@nextclaw/shared": "0.4.27-beta.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@types/node": "^20.17.6",
|