openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
130 lines (129 loc) • 5.61 kB
JavaScript
import { a as resolveGlobalSingleton } from "./global-singleton-0m-JziJW.mjs";
import { t as createDeferredCore } from "./deferred-D0La5CRk.mjs";
import { r as getAsyncWorkSignal, t as AsyncWorkScope } from "./async-work-scope-DDiH0P7W.mjs";
import { i as hasRetainedPluginRuntimeCloseError } from "./runtime-close-error-DB6gcV1x.mjs";
import { b as resolvePluginReturnPromise, m as getCanonicalGatewayContextResolver, o as getPluginRuntimeGatewayRequestScope } from "./gateway-request-scope-DV3FHtde.mjs";
import { AsyncLocalStorage } from "node:async_hooks";
//#region src/plugins/legacy-sdk-resource-host.ts
/** Owns resources borrowed by shipped SDK results that have no release method. */
var LegacyPluginSdkResourceHost = class {
constructor() {
this.work = new AsyncWorkScope();
this.claims = /* @__PURE__ */ new Map();
this.providerProjections = /* @__PURE__ */ new WeakMap();
this.pending = /* @__PURE__ */ new Set();
this.failures = [];
}
get scheduler() {
this.assertOpen();
if (!this.gatewayScheduler) throw new Error("Plugin SDK resource host has no Gateway scheduler");
this.gatewayScheduler.signal.throwIfAborted();
return this.gatewayScheduler;
}
bindScheduler(scheduler) {
this.assertOpen();
scheduler.signal.throwIfAborted();
if (this.gatewayScheduler && this.gatewayScheduler !== scheduler) throw new Error("Plugin SDK resource host already belongs to another Gateway scheduler");
this.gatewayScheduler = scheduler;
}
assertOpen() {
if (this.closing || this.work.isClosing) throw new Error("Plugin SDK resource host is closed");
}
run(run) {
return hostContext.run(this, run);
}
track(run) {
this.assertOpen();
return this.work.track(() => this.run(run));
}
/** Preserve synchronous SDK hooks while joining their asynchronous results and descendants. */
invoke(run) {
if (getAsyncWorkSignal() !== this.work.signal) this.assertOpen();
return this.work.run(() => this.run(() => {
const result = run();
const completion = resolvePluginReturnPromise(result);
return completion ? this.work.track(() => completion) : result;
}));
}
adopt(source, claim) {
this.assertOpen();
if (this.claims.has(source)) this.releaseClaim(claim);
else this.claims.set(source, claim);
}
/** View lookup shares identity; only adopted or temporary claims own disposal. */
getProviderProjection(source, create) {
this.assertOpen();
let projection = this.providerProjections.get(source);
if (!projection) {
projection = create();
this.providerProjections.set(source, projection);
}
return projection;
}
forgetProviderProjection(source, projection) {
if (this.providerProjections.get(source) === projection) this.providerProjections.delete(source);
}
/** Projection failures still own their asynchronous release until it settles. */
releaseClaim(claim) {
const operation = createDeferredCore();
const completion = operation.promise.then(() => {
this.pending.delete(completion);
}, (error) => {
this.failures.push(error);
this.pending.delete(completion);
});
this.pending.add(completion);
try {
operation.resolve(this.work.hasPendingWork ? AsyncWorkScope.runWhenAllIdle(() => [this.work], () => claim.release()) : claim.release());
} catch (error) {
operation.reject(error);
}
}
/** Fence new SDK work and join its tails before prepared resources can retire. */
async drainWork() {
await this.work.drain();
await this.drainPendingReleases();
if (this.failures.some(hasRetainedPluginRuntimeCloseError)) throw new AggregateError(this.failures, "Plugin SDK resources could not all be disposed");
}
async drainPendingReleases() {
while (this.pending.size > 0) await Promise.all(this.pending);
}
close() {
if (!this.closing) this.closing = Promise.resolve().then(async () => {
await this.drainWork();
const claims = [...this.claims.values()];
this.claims.clear();
for (const claim of claims) this.releaseClaim(claim);
await this.drainPendingReleases();
if (this.failures.length > 0) throw new AggregateError(this.failures, "Plugin SDK resources could not all be disposed");
});
return this.closing;
}
};
const { hostContext, gatewayHosts } = resolveGlobalSingleton(Symbol.for("openclaw.legacyPluginSdkResourceHosts"), () => ({
hostContext: new AsyncLocalStorage(),
gatewayHosts: /* @__PURE__ */ new WeakMap()
}));
const STANDALONE_HOST_KEY = Symbol.for("openclaw.legacyPluginSdkStandaloneResourceHost");
/** Associate exact host resolvers without calling them after their authority closes. */
function bindLegacyPluginSdkResourceHost(resolver, host) {
gatewayHosts.set(resolver, host);
}
function getBoundLegacyPluginSdkResourceHost() {
const scope = getPluginRuntimeGatewayRequestScope();
const resolver = scope?.resolveGatewayContext ?? scope?.context?.resolveGatewayContext;
if (resolver) {
const owner = getCanonicalGatewayContextResolver(resolver);
const host = owner ? gatewayHosts.get(owner) : void 0;
if (!host) throw new Error("Gateway SDK resource host is not bound");
return host;
}
const host = hostContext.getStore();
return host === globalThis[STANDALONE_HOST_KEY] ? void 0 : host;
}
/** Standalone callers of the shipped bare-result SDK retain their process lifetime. */
function getLegacyPluginSdkResourceHost() {
return getBoundLegacyPluginSdkResourceHost() ?? resolveGlobalSingleton(STANDALONE_HOST_KEY, () => new LegacyPluginSdkResourceHost());
}
//#endregion
export { getLegacyPluginSdkResourceHost as i, bindLegacyPluginSdkResourceHost as n, getBoundLegacyPluginSdkResourceHost as r, LegacyPluginSdkResourceHost as t };