grix-connector 3.31.2 → 3.32.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/adapter/acp/acp-adapter.js +10 -10
- package/dist/adapter/claude/protocol-contract.js +1 -1
- package/dist/adapter/claude/skill-scanner.js +2 -2
- package/dist/adapter/codex/codex-bridge.js +8 -8
- package/dist/adapter/cursor/cursor-adapter.js +7 -7
- package/dist/adapter/cursor/resolve-command.js +1 -0
- package/dist/adapter/deepseek-harness/jsonrpc-adapter.js +2 -2
- package/dist/adapter/deepseek-harness/skill-manager.js +1 -1
- package/dist/adapter/shared/native-provider-config.js +5 -5
- package/dist/assets/dsh-bridge/{grix-dsh-bridge-3.31.2.tgz → grix-dsh-bridge-3.32.0.tgz} +0 -0
- package/dist/assets/dsh-bridge/manifest.json +5 -5
- package/dist/bridge/adapter-pool.js +1 -1
- package/dist/bridge/bridge.js +7 -7
- package/dist/bridge/session-controller.js +6 -6
- package/dist/core/aibot/binding-card-meta-cache.js +1 -1
- package/dist/core/aibot/connection-handle.js +1 -1
- package/dist/core/skill-sync/enable-roots.js +1 -1
- package/dist/core/skill-sync/hermes-trust.js +1 -0
- package/dist/core/skill-sync/skill-delete.js +1 -0
- package/dist/core/skill-sync/skill-enable.js +1 -1
- package/dist/core/skill-sync/sync-state.js +1 -1
- package/dist/core/util/display-path.js +1 -0
- package/dist/core/util/index.js +1 -1
- package/dist/default-skills/index.js +1 -1
- package/dist/grix.js +0 -0
- package/dist/manager.js +2 -2
- package/openclaw-plugin/index.js +727 -2606
- package/openclaw.plugin.json +14 -0
- package/package.json +4 -3
- package/dist/adapter/claude/claude-bridge-server.js +0 -1
- package/dist/adapter/claude/claude-tools.js +0 -1
- package/dist/adapter/claude/claude-worker-client.js +0 -1
- package/dist/adapter/claude/mcp-http-launcher.js +0 -2
- package/dist/adapter/claude/result-timeout.js +0 -1
- package/dist/adapter/deepseek/deepseek-adapter.js +0 -6
- package/dist/adapter/deepseek/index.js +0 -1
- package/dist/adapter/qwen/index.js +0 -1
- package/dist/adapter/qwen/qwen-adapter.js +0 -4
- package/dist/aibot/client.js +0 -1
- package/dist/aibot/index.js +0 -1
- package/dist/aibot/types.js +0 -0
- package/dist/core/access/allowlist-gate.js +0 -1
- package/dist/core/access/allowlist-store.js +0 -1
- package/dist/core/access/index.js +0 -1
- package/dist/core/file-ops/handler.js +0 -1
- package/dist/core/file-ops/list-files.js +0 -1
- package/dist/core/file-ops/types.js +0 -0
- package/dist/log.js +0 -3
- package/dist/main.js +0 -31
- package/dist/mcp/stream-http/config.js +0 -1
- package/dist/mcp/stream-http/connection-binding.js +0 -1
- package/dist/mcp/stream-http/event-tool-executor.js +0 -1
- package/dist/mcp/stream-http/gateway.js +0 -1
- package/dist/mcp/stream-http/index.js +0 -1
- package/dist/mcp/stream-http/security.js +0 -1
- package/dist/mcp/stream-http/session-manager.js +0 -1
- package/dist/mcp/stream-http/tool-executor.js +0 -1
- package/dist/mcp/stream-http/tool-registry.js +0 -1
- package/dist/mcp/stream-http/tool-schemas.js +0 -1
- package/dist/session/index.js +0 -1
- package/dist/session/manager.js +0 -1
- package/dist/transport/index.js +0 -1
- package/dist/transport/json-rpc.js +0 -3
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import{mkdir as m,readFile as
|
|
2
|
-
`,"utf8"),await
|
|
3
|
-
`,"utf8")}async function
|
|
4
|
-
`,"utf8"),await
|
|
5
|
-
`);await m(n,{recursive:!0}),await
|
|
1
|
+
import{mkdir as m,readFile as w,rm as O,writeFile as p}from"node:fs/promises";import{homedir as y}from"node:os";import{basename as M}from"node:path";import{join as a}from"node:path";function E(e){return M((e??"").trim()).toLowerCase()}async function d(e){try{const r=await w(e,"utf8"),t=JSON.parse(r);return t&&typeof t=="object"&&!Array.isArray(t)?t:{}}catch{return{}}}function f(e){return e&&typeof e=="object"&&!Array.isArray(e)?e:{}}const g=".grix-provider-state.json";async function S(e,r){const t=a(e,".qwen"),i=a(t,"settings.json"),o=a(t,g);if(!r.OPENAI_BASE_URL){await _(i,o);return}const n=await d(i),s=f(n.security),c=f(s.auth),u=f(n.model),l=await d(o),N={previousSelectedType:Object.keys(l).length>0?typeof l.previousSelectedType=="string"?l.previousSelectedType:null:typeof c.selectedType=="string"?c.selectedType:null,previousModelName:Object.keys(l).length>0?typeof l.previousModelName=="string"?l.previousModelName:null:typeof u.name=="string"?u.name:null,writtenModelName:r.OPENAI_MODEL??null};c.selectedType="openai",s.auth=c,n.security=s,r.OPENAI_MODEL&&(u.name=r.OPENAI_MODEL,n.model=u),await m(t,{recursive:!0}),await p(i,`${JSON.stringify(n,null,2)}
|
|
2
|
+
`,"utf8"),await p(o,`${JSON.stringify(N,null,2)}
|
|
3
|
+
`,"utf8")}async function _(e,r){const t=await d(r);if(Object.keys(t).length===0)return!1;const i={previousSelectedType:typeof t.previousSelectedType=="string"?t.previousSelectedType:null,previousModelName:typeof t.previousModelName=="string"?t.previousModelName:null,writtenModelName:typeof t.writtenModelName=="string"?t.writtenModelName:null},o=await d(e),n=f(o.security),s=f(n.auth),c=f(o.model);let u=!1;return s.selectedType==="openai"&&(i.previousSelectedType?s.selectedType=i.previousSelectedType:delete s.selectedType,n.auth=s,o.security=n,u=!0),i.writtenModelName&&c.name===i.writtenModelName&&(i.previousModelName?c.name=i.previousModelName:delete c.name,o.model=c,u=!0),u&&await p(e,`${JSON.stringify(o,null,2)}
|
|
4
|
+
`,"utf8"),await O(r,{force:!0}),!0}async function C(e,r){if(!r||E(e)!=="qwen")return!1;const t=a(r,".qwen");return _(a(t,"settings.json"),a(t,g))}function R(e){return(e??"default").trim().toLowerCase().replace(/[^a-z0-9._-]+/g,"-").replace(/^-+|-+$/g,"")||"default"}function I(e,r){const t=e.HERMES_HOME?.trim()||e.GRIX_HERMES_HOME?.trim();if(t)return t;if(e.GRIX_HERMES_BASE_URL?.trim()&&e.HERMES_INFERENCE_MODEL?.trim()&&e.GRIX_PROVIDER_API_KEY?.trim()){const o=e.GRIX_CONNECTOR_HOME?.trim()||a(y(),".grix");return a(o,"runtime","native-provider",R(r),"hermes")}return a(y(),".hermes")}async function h(e,r){const t=e.GRIX_HERMES_BASE_URL?.trim(),i=e.HERMES_INFERENCE_MODEL?.trim(),o=e.GRIX_PROVIDER_API_KEY?.trim();if(!t||!i||!o)return;const n=I(e,r),s=["model:"," provider: grix",` default: ${JSON.stringify(i)}`,"providers:"," grix:",` api: ${JSON.stringify(t)}`,` default_model: ${JSON.stringify(i)}`," key_env: GRIX_PROVIDER_API_KEY"," name: Grix"," transport: openai_chat",""].join(`
|
|
5
|
+
`);await m(n,{recursive:!0}),await p(a(n,"config.yaml"),s,{encoding:"utf8",mode:384}),e.HERMES_HOME=n}async function P(e,r,t,i){if(!(!r||!t))switch(E(e)){case"qwen":await S(r,t);return;case"hermes":await h(t,i);return;default:return}}export{P as applyNativeProviderConfig,I as resolveHermesHomeDir,C as restoreNativeProviderConfig};
|
|
Binary file
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"bridgeVersion": "3.
|
|
4
|
-
"tarball": "grix-dsh-bridge-3.
|
|
5
|
-
"size":
|
|
3
|
+
"bridgeVersion": "3.32.0",
|
|
4
|
+
"tarball": "grix-dsh-bridge-3.32.0.tgz",
|
|
5
|
+
"size": 22011,
|
|
6
6
|
"unpackedSize": 87635,
|
|
7
|
-
"shasum": "
|
|
8
|
-
"integrity": "sha512-
|
|
7
|
+
"shasum": "1903fc951a46feedb1a30833b083b190eb0735ad",
|
|
8
|
+
"integrity": "sha512-eOaVCeiZ/zb4/KQNWYGGlfSuJjFeL+ln4BjEBTWzZ7KB6DPOLQ1qFzBq3vKap+nAhAuDH8DNmczdR6ItqaTQzQ=="
|
|
9
9
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{log as o}from"../core/log/index.js";import{RespawnManager as d}from"./respawn-manager.js";import{EventQueue as p}from"./event-queue.js";class c extends Error{constructor(e){super(`adapter pool full (maxSize=${e})`),this.name="PoolFullError"}}const v=6e4;class m{slots=new Map;config;factory;sendAck;onEventState=null;onQueueComposing=null;onInternalError=null;onEventStarted=null;onEventDone=null;onSessionActivity=null;stopped=!1;sweepTimer=null;pendingRemovals=new Map;constructor(e,t,s){this.config=e,this.factory=t,this.sendAck=s}setEventStateHandler(e){this.onEventState=e}setQueueComposingHandler(e){this.onQueueComposing=e}setInternalErrorHandler(e){this.onInternalError=e}setEventStartedHandler(e){this.onEventStarted=e}setEventDoneHandler(e){this.onEventDone=e}setSessionActivityHandler(e){this.onSessionActivity=e}async deliverInboundEvent(e){const t=e.session_id,s=this.slots.get(t);if(s){if(s.stopping)return await this.pendingRemovals.get(t)?.catch(()=>{}),this.deliverInboundEvent(e);s.startPromise&&await s.startPromise,s.lastActivityAt=Date.now(),this.sendAck(e.event_id,e.session_id),s.state==="stopped"&&o.info("adapter-pool",`Holding event ${e.event_id} in paused queue for restarting slot ${t}`),this.submitToSlotQueue(s,e);return}if(this.slots.size>=this.config.maxPoolSize&&!this.evictDeadSlot()&&!this.evictIdleSlot())throw new c(this.config.maxPoolSize);this.sendAck(e.event_id,e.session_id);const n=this.createSlot(t);try{await n.startPromise}catch(r){throw r}this.submitToSlotQueue(n,e)}submitToSlotQueue(e,t){e.eventQueue.submit(t)==="rejected"&&o.info("adapter-pool",`Event ${t.event_id} rejected by EventQueue (queue full)`)}eventComplete(e,t){const s=this.slots.get(t);if(!s)return o.error("adapter-pool",`Cannot complete event ${e}: session slot not found (${t})`),!1;const n=s.eventQueue.complete(e);return n||o.error("adapter-pool",`Cannot complete event ${e}: not running in session ${t}`),n}cancelEvent(e,t){const s=this.slots.get(t);return s?s.eventQueue.cancel(e):!1}removeQueuedEvent(e,t){const s=this.slots.get(e);return s?s.eventQueue.removeQueued(t):!1}clearQueue(e){const t=this.slots.get(e);return t?t.eventQueue.clear(e):[]}reorderQueue(e,t){const s=this.slots.get(e);return s?s.eventQueue.reorder(e,t):[]}holdEvent(e,t,s,n,r){const a=this.slots.get(e);return a?s?a.eventQueue.hold(t,n,r):a.eventQueue.release(t):"not_found"}editQueuedEvent(e,t,s){const n=this.slots.get(e);return n?n.eventQueue.editQueued(t,s):"not_found"}drainAllQueuedEvents(){const e=[];for(const t of this.slots.values())e.push(...t.eventQueue.drainQueuedForSession(t.sessionId));return e}drainQueuedForSession(e){if(!this.config.eventQueue)return[];const t=this.slots.get(e);return t?t.eventQueue.drainQueuedForSession(e):[]}getQueueSnapshot(e){if(!this.config.eventQueue)return null;const t=this.slots.get(e);return t?t.eventQueue.snapshot(e):null}deliverStopEvent(e,t){if(t){const s=this.slots.get(t);o.info("adapter-pool",`[stop-trace] pool.deliverStopEvent session=${t} event=${e} slotExists=${!!s} -> adapter.deliverStopEvent`),s?.adapter.deliverStopEvent(e,t);return}o.info("adapter-pool",`[stop-trace] pool.deliverStopEvent (broadcast, no session) event=${e} slots=${this.slots.size}`);for(const s of this.slots.values())s.adapter.deliverStopEvent(e)}collectActiveEventIds(){const e=[];for(const t of this.slots.values()){const s=t.adapter.getActiveEventIds;typeof s=="function"&&e.push(...s.call(t.adapter))}return e}clearActiveEventsForShutdown(){for(const e of this.slots.values()){const t=e.adapter.clearActiveEventForShutdown;typeof t=="function"&&t.call(e.adapter)}}async deliverLocalAction(e,t){const s=String((e.params??{}).session_id??"");let n=s?this.slots.get(s):void 0;if(!n&&s&&t?.autoCreateSlot&&!this.stopped&&(this.slots.size>=this.config.maxPoolSize&&(this.evictDeadSlot()||this.evictIdleSlotSync()),this.slots.size<this.config.maxPoolSize)){o.info("adapter-pool",`Auto-creating slot for session ${s} (local_action trigger)`);const i=this.createSlot(s);try{await i.startPromise,n=i}catch(l){o.error("adapter-pool",`Failed to auto-create slot for ${s}: ${l}`)}}return n?.adapter?.handleLocalAction?(n.lastActivityAt=Date.now(),n.adapter.handleLocalAction(e)):(await Promise.allSettled([...this.slots.values()].map(i=>i.adapter.handleLocalAction?.(e)??Promise.resolve({handled:!1,kind:""})))).find(i=>i.status==="fulfilled"&&i.value.handled)?.value??{handled:!1,kind:""}}getOrCreateSlot(e){const t=this.slots.get(e);if(t)return t;if(!this.stopped)return this.createSlot(e)}getSlot(e){return this.slots.get(e)}getAllSlots(){return[...this.slots.values()]}async removeSlot(e){const t=this.slots.get(e);if(!t){await this.pendingRemovals.get(e)?.catch(()=>{});return}if(t.stopping){await this.pendingRemovals.get(e)?.catch(()=>{});return}t.stopping=!0;const s=this.doRemoveSlot(t);this.pendingRemovals.set(e,s);try{await s}finally{this.pendingRemovals.get(e)===s&&this.pendingRemovals.delete(e)}}async doRemoveSlot(e){const t=e.sessionId;this.onSessionActivity?.(t,!1),e.eventQueue.clear(t,"session slot removed"),e.eventQueue.destroy(),e.respawn.stopAll(),this.detachSlotEventListenersExceptDone(e),await e.adapter.stop().catch(()=>{}),e.adapter.removeAllListeners("eventDone"),this.slots.delete(t)}async stop(){this.stopped=!0,this.stopIdleSweep(),await Promise.allSettled([...this.pendingRemovals.values()]);const e=[...this.slots.values()];this.slots.clear(),await Promise.allSettled(e.map(async t=>{t.eventQueue.destroy(),t.respawn.stopAll(),this.detachAllSlotEventListeners(t),await t.adapter.stop().catch(()=>{})}))}startIdleSweep(){this.stopIdleSweep(),this.sweepTimer=setInterval(()=>this.sweepIdle(),v)}stopIdleSweep(){this.sweepTimer&&(clearInterval(this.sweepTimer),this.sweepTimer=null)}sweepIdle(){const e=Date.now();for(const[t,s]of this.slots){if(s.stopping)continue;if(s.state==="stopped"&&s.respawn.exhausted){o.info("adapter-pool",`Removing dead slot for session ${t} (sweep)`),this.removeSlot(t).catch(()=>{});continue}if(s.state!=="ready")continue;if(s.eventQueue.runningCount>0||s.eventQueue.queuedCount>0){s.lastActivityAt=e;continue}const n=s.adapter.getStatus();if(n.busy||n.backgroundBusy){s.lastActivityAt=e;continue}e-s.lastActivityAt<=this.config.idleTimeoutMs||(s.adapter.hasBackgroundWork?s.adapter.hasBackgroundWork().then(r=>{r?(o.info("adapter-pool",`Deferred eviction for session ${t}: background grandchild processes detected`),s.lastActivityAt=Date.now()):this.evictAfterProbe(t,s,"probe idle")}).catch(()=>{this.evictAfterProbe(t,s,"probe failed")}):(o.info("adapter-pool",`Evicting idle slot for session ${t}`),this.removeSlot(t).catch(r=>{o.error("adapter-pool",`Failed to evict slot ${t}: ${r}`)})))}}evictAfterProbe(e,t,s){if(this.slots.get(e)!==t||t.stopping)return;if(t.eventQueue.runningCount>0||t.eventQueue.queuedCount>0){t.lastActivityAt=Date.now();return}const n=t.adapter.getStatus();if(n.busy||n.backgroundBusy){t.lastActivityAt=Date.now();return}Date.now()-t.lastActivityAt<=this.config.idleTimeoutMs||(o.info("adapter-pool",`Evicting idle slot for session ${e} (${s})`),this.removeSlot(e).catch(r=>{o.error("adapter-pool",`Failed to evict slot ${e}: ${r}`)}))}evictDeadSlot(){for(const[e,t]of this.slots)if(t.state==="stopped"&&t.respawn.exhausted)return o.info("adapter-pool",`Removing dead slot for session ${e} (exhausted respawn)`),this.removeSlot(e).catch(()=>{}),!0;return!1}evictIdleSlot(){let e=null;for(const t of this.slots.values()){if(t.stopping||t.state!=="ready"||t.eventQueue.runningCount>0||t.eventQueue.queuedCount>0)continue;const s=t.adapter.getStatus();s.busy||s.backgroundBusy||(!e||t.lastActivityAt<e.lastActivityAt)&&(e=t)}return e?(o.info("adapter-pool",`Evicting idle slot for session ${e.sessionId} (pool full)`),this.removeSlot(e.sessionId).catch(()=>{}),!0):!1}evictIdleSlotSync(){let e=null,t="";for(const[s,n]of this.slots){if(n.stopping||n.state!=="ready"||n.eventQueue.runningCount>0||n.eventQueue.queuedCount>0)continue;const r=n.adapter.getStatus();r.busy||r.backgroundBusy||(!e||n.lastActivityAt<e.lastActivityAt)&&(e=n,t=s)}return!e||!t?!1:(o.info("adapter-pool",`Evicting idle slot for session ${t} (pool full, sync)`),this.slots.delete(t),e.eventQueue.clear(t,"session slot removed"),e.eventQueue.destroy(),e.respawn.stopAll(),this.detachAllSlotEventListeners(e),e.adapter.stop().catch(()=>{}),!0)}getStatus(){let e=0,t=0;for(const s of this.slots.values())s.state==="ready"&&(e++,s.adapter.getStatus().busy&&t++);return{total:this.slots.size,ready:e,busy:t}}hasPendingWork(){for(const e of this.slots.values())if(e.eventQueue.runningCount>0||e.eventQueue.queuedCount>0||e.state==="ready"&&e.adapter.getStatus().busy)return!0;return!1}hasPendingOrBackgroundWork(){for(const e of this.slots.values()){if(e.eventQueue.runningCount>0||e.eventQueue.queuedCount>0)return!0;if(e.state==="ready"){const t=e.adapter.getStatus();if(t.busy||t.backgroundBusy)return!0}}return!1}createSlot(e){const t=this.factory(e),s=new d,n=this.createEventQueue(e),r={sessionId:e,adapter:t,respawn:s,state:"starting",lastActivityAt:Date.now(),startPromise:null,eventQueue:n,stopping:!1};this.slots.set(e,r);const a=(async()=>{try{this.wireSlotEvents(r),await t.start(),r.state="ready"}catch(i){throw this.detachAllSlotEventListeners(r),this.slots.delete(e),i}finally{r.startPromise=null}})();return r.startPromise=a,s.startHealthCheck(this.slotRespawnCtx(r)),r}wireSlotEvents(e){e.adapter.on("exit",t=>{this.stopped||(o.error("adapter-pool",`Slot ${e.sessionId} adapter exited (code=${t})`),e.state="stopped",e.eventQueue.pause("restart"),e.respawn.scheduleRespawn(this.slotRespawnCtx(e)))}),e.adapter.on("stuck",()=>{this.stopped||this.slots.get(e.sessionId)===e&&(o.error("adapter-pool",`Slot ${e.sessionId} adapter stuck, triggering respawn`),e.state="stopped",e.eventQueue.pause("restart"),e.respawn.scheduleRespawn(this.slotRespawnCtx(e)))}),e.adapter.on?.("error",t=>{if(this.stopped)return;const s=t instanceof Error?t.message:String(t);o.error("adapter-pool",`Slot ${e.sessionId} adapter error: ${s}`)}),e.adapter.on("internalError",t=>{if(!this.stopped){if(this.slots.get(e.sessionId)!==e){o.warn("adapter-pool",`Ignored internalError from detached slot session=${e.sessionId} event=${t.eventId}`);return}if(!this.onInternalError){o.warn("adapter-pool",`Slot ${e.sessionId} emitted internalError but no handler registered: event=${t.eventId} msg=${t.errorMsg}`);return}this.onInternalError(t)}}),e.adapter.on("eventStarted",(t,s)=>{this.stopped||this.slots.get(e.sessionId)===e&&this.onEventStarted?.(t,s)}),e.adapter.on("eventDone",t=>{this.stopped||this.slots.get(e.sessionId)===e&&(this.onEventDone?.(t,e.sessionId),e.eventQueue.markAdapterDone(t))}),e.adapter.on("sessionActivity",(t,s,n)=>{this.stopped||this.slots.get(e.sessionId)===e&&this.onSessionActivity?.(t||e.sessionId,s,n)}),e.adapter.on("pauseIntake",t=>{this.stopped||this.slots.get(e.sessionId)===e&&e.eventQueue.pause(t)}),e.adapter.on("resumeIntake",t=>{this.stopped||this.slots.get(e.sessionId)===e&&e.eventQueue.resume(t)})}detachAllSlotEventListeners(e){e.adapter.removeAllListeners("exit"),e.adapter.removeAllListeners("error"),e.adapter.removeAllListeners("stuck"),e.adapter.removeAllListeners("internalError"),e.adapter.removeAllListeners("eventStarted"),e.adapter.removeAllListeners("eventDone"),e.adapter.removeAllListeners("sessionActivity"),e.adapter.removeAllListeners("pauseIntake"),e.adapter.removeAllListeners("resumeIntake")}detachSlotEventListenersExceptDone(e){e.adapter.removeAllListeners("exit"),e.adapter.removeAllListeners("error"),e.adapter.removeAllListeners("stuck"),e.adapter.removeAllListeners("internalError"),e.adapter.removeAllListeners("eventStarted"),e.adapter.removeAllListeners("sessionActivity"),e.adapter.removeAllListeners("pauseIntake"),e.adapter.removeAllListeners("resumeIntake")}slotRespawnCtx(e){const t=this,s=e.sessionId;return{name:`slot-${s}`,get stopped(){return t.stopped||!t.slots.has(s)},get agentAlive(){return e.adapter.isAlive()},pingAgent:n=>e.adapter.ping(n),onRespawnNeeded:async()=>{e.eventQueue.pause("restart"),this.detachAllSlotEventListeners(e),await e.adapter.stop().catch(()=>{}),e.adapter=t.factory(s),e.state="starting",t.wireSlotEvents(e),await e.adapter.start(),e.state="ready",e.respawn.resetAttempts(),e.respawn.resetHealthFailures(),e.respawn.stopSlowRetry(),e.respawn.startHealthCheck(t.slotRespawnCtx(e)),e.eventQueue.resume("compaction"),e.eventQueue.resume("barrier"),e.eventQueue.resume("restart")},onCleanupAgent:()=>{"cleanup"in e.adapter&&e.adapter.cleanup()},onAbortActiveRun:n=>{}}}createEventQueue(e){const t=this.config.eventQueue??{maxConcurrent:1,maxQueued:5,queueTimeoutMs:3e5,cancelableQueued:!0,cancelableRunning:!0},s={onDeliver:n=>{const r=this.slots.get(e);r&&r.adapter.deliverInboundEvent(n)},onStateChange:(n,r,a,i)=>{this.onEventState?.(n,r,a,i)},onCancelRunning:n=>{this.deliverStopEvent(n,e)},onRejected:(n,r)=>{this.onEventState?.(n.event_id,n.session_id,"failed",{reason:r})},onComposing:(n,r,a)=>{this.onQueueComposing?.(n,r,a)}};return new p(t,s)}}export{m as AdapterPool,c as PoolFullError};
|
|
1
|
+
import{log as o}from"../core/log/index.js";import{RespawnManager as d}from"./respawn-manager.js";import{EventQueue as p}from"./event-queue.js";class c extends Error{constructor(e){super(`adapter pool full (maxSize=${e})`),this.name="PoolFullError"}}const v=6e4;class m{slots=new Map;config;factory;sendAck;onEventState=null;onQueueComposing=null;onInternalError=null;onEventStarted=null;onEventDone=null;onSessionActivity=null;stopped=!1;sweepTimer=null;pendingRemovals=new Map;constructor(e,t,s){this.config=e,this.factory=t,this.sendAck=s}setEventStateHandler(e){this.onEventState=e}setQueueComposingHandler(e){this.onQueueComposing=e}setInternalErrorHandler(e){this.onInternalError=e}setEventStartedHandler(e){this.onEventStarted=e}setEventDoneHandler(e){this.onEventDone=e}setSessionActivityHandler(e){this.onSessionActivity=e}async deliverInboundEvent(e){const t=e.session_id,s=this.slots.get(t);if(s){if(s.stopping)return await this.pendingRemovals.get(t)?.catch(()=>{}),this.deliverInboundEvent(e);s.startPromise&&await s.startPromise,s.lastActivityAt=Date.now(),this.sendAck(e.event_id,e.session_id),s.state==="stopped"&&o.info("adapter-pool",`Holding event ${e.event_id} in paused queue for restarting slot ${t}`),this.submitToSlotQueue(s,e);return}if(this.slots.size>=this.config.maxPoolSize&&!this.evictDeadSlot()&&!this.evictIdleSlot())throw new c(this.config.maxPoolSize);this.sendAck(e.event_id,e.session_id);const n=this.createSlot(t);try{await n.startPromise}catch(r){throw r}this.submitToSlotQueue(n,e)}submitToSlotQueue(e,t){e.eventQueue.submit(t)==="rejected"&&o.info("adapter-pool",`Event ${t.event_id} rejected by EventQueue (queue full)`)}eventComplete(e,t){const s=this.slots.get(t);if(!s)return o.error("adapter-pool",`Cannot complete event ${e}: session slot not found (${t})`),!1;const n=s.eventQueue.complete(e);return n||o.error("adapter-pool",`Cannot complete event ${e}: not running in session ${t}`),n}cancelEvent(e,t){const s=this.slots.get(t);return s?s.eventQueue.cancel(e):!1}removeQueuedEvent(e,t){const s=this.slots.get(e);return s?s.eventQueue.removeQueued(t):!1}clearQueue(e){const t=this.slots.get(e);return t?t.eventQueue.clear(e):[]}reorderQueue(e,t){const s=this.slots.get(e);return s?s.eventQueue.reorder(e,t):[]}holdEvent(e,t,s,n,r){const a=this.slots.get(e);return a?s?a.eventQueue.hold(t,n,r):a.eventQueue.release(t):"not_found"}editQueuedEvent(e,t,s){const n=this.slots.get(e);return n?n.eventQueue.editQueued(t,s):"not_found"}drainAllQueuedEvents(){const e=[];for(const t of this.slots.values())e.push(...t.eventQueue.drainQueuedForSession(t.sessionId));return e}drainQueuedForSession(e){if(!this.config.eventQueue)return[];const t=this.slots.get(e);return t?t.eventQueue.drainQueuedForSession(e):[]}getQueueSnapshot(e){if(!this.config.eventQueue)return null;const t=this.slots.get(e);return t?t.eventQueue.snapshot(e):null}deliverStopEvent(e,t){if(t){const s=this.slots.get(t);o.info("adapter-pool",`[stop-trace] pool.deliverStopEvent session=${t} event=${e} slotExists=${!!s} -> adapter.deliverStopEvent`),s?.adapter.deliverStopEvent(e,t);return}o.info("adapter-pool",`[stop-trace] pool.deliverStopEvent (broadcast, no session) event=${e} slots=${this.slots.size}`);for(const s of this.slots.values())s.adapter.deliverStopEvent(e)}collectActiveEventIds(){const e=[];for(const t of this.slots.values()){const s=t.adapter.getActiveEventIds;typeof s=="function"&&e.push(...s.call(t.adapter))}return e}clearActiveEventsForShutdown(){for(const e of this.slots.values()){const t=e.adapter.clearActiveEventForShutdown;typeof t=="function"&&t.call(e.adapter)}}async deliverLocalAction(e,t){const s=String((e.params??{}).session_id??"");let n=s?this.slots.get(s):void 0;if(n?.stopping&&(await this.pendingRemovals.get(s)?.catch(()=>{}),n=this.slots.get(s)),!n&&s&&t?.autoCreateSlot&&!this.stopped&&(this.slots.size>=this.config.maxPoolSize&&(this.evictDeadSlot()||this.evictIdleSlotSync()),this.slots.size<this.config.maxPoolSize)){o.info("adapter-pool",`Auto-creating slot for session ${s} (local_action trigger)`);const i=this.createSlot(s);try{await i.startPromise,n=i}catch(l){o.error("adapter-pool",`Failed to auto-create slot for ${s}: ${l}`)}}return n?.adapter?.handleLocalAction?(n.lastActivityAt=Date.now(),n.adapter.handleLocalAction(e)):s?{handled:!1,kind:""}:(await Promise.allSettled([...this.slots.values()].filter(i=>!i.stopping).map(i=>i.adapter.handleLocalAction?.(e)??Promise.resolve({handled:!1,kind:""})))).find(i=>i.status==="fulfilled"&&i.value.handled)?.value??{handled:!1,kind:""}}async waitForSlotRemoval(e){await this.pendingRemovals.get(e)?.catch(()=>{})}async getOrCreateSlot(e){let t=this.slots.get(e);if(t?.stopping&&(await this.pendingRemovals.get(e)?.catch(()=>{}),t=this.slots.get(e)),t)return t;if(!this.stopped)return this.createSlot(e)}getSlot(e){return this.slots.get(e)}getAllSlots(){return[...this.slots.values()]}async removeSlot(e){const t=this.slots.get(e);if(!t){await this.pendingRemovals.get(e)?.catch(()=>{});return}if(t.stopping){await this.pendingRemovals.get(e)?.catch(()=>{});return}t.stopping=!0;const s=this.doRemoveSlot(t);this.pendingRemovals.set(e,s);try{await s}finally{this.pendingRemovals.get(e)===s&&this.pendingRemovals.delete(e)}}async doRemoveSlot(e){const t=e.sessionId;this.onSessionActivity?.(t,!1),e.eventQueue.clear(t,"session slot removed"),e.eventQueue.destroy(),e.respawn.stopAll(),this.detachSlotEventListenersExceptDone(e),await e.adapter.stop().catch(()=>{}),e.adapter.removeAllListeners("eventDone"),this.slots.delete(t)}async stop(){this.stopped=!0,this.stopIdleSweep(),await Promise.allSettled([...this.pendingRemovals.values()]);const e=[...this.slots.values()];this.slots.clear(),await Promise.allSettled(e.map(async t=>{t.eventQueue.destroy(),t.respawn.stopAll(),this.detachAllSlotEventListeners(t),await t.adapter.stop().catch(()=>{})}))}startIdleSweep(){this.stopIdleSweep(),this.sweepTimer=setInterval(()=>this.sweepIdle(),v)}stopIdleSweep(){this.sweepTimer&&(clearInterval(this.sweepTimer),this.sweepTimer=null)}sweepIdle(){const e=Date.now();for(const[t,s]of this.slots){if(s.stopping)continue;if(s.state==="stopped"&&s.respawn.exhausted){o.info("adapter-pool",`Removing dead slot for session ${t} (sweep)`),this.removeSlot(t).catch(()=>{});continue}if(s.state!=="ready")continue;if(s.eventQueue.runningCount>0||s.eventQueue.queuedCount>0){s.lastActivityAt=e;continue}const n=s.adapter.getStatus();if(n.busy||n.backgroundBusy){s.lastActivityAt=e;continue}e-s.lastActivityAt<=this.config.idleTimeoutMs||(s.adapter.hasBackgroundWork?s.adapter.hasBackgroundWork().then(r=>{r?(o.info("adapter-pool",`Deferred eviction for session ${t}: background grandchild processes detected`),s.lastActivityAt=Date.now()):this.evictAfterProbe(t,s,"probe idle")}).catch(()=>{this.evictAfterProbe(t,s,"probe failed")}):(o.info("adapter-pool",`Evicting idle slot for session ${t}`),this.removeSlot(t).catch(r=>{o.error("adapter-pool",`Failed to evict slot ${t}: ${r}`)})))}}evictAfterProbe(e,t,s){if(this.slots.get(e)!==t||t.stopping)return;if(t.eventQueue.runningCount>0||t.eventQueue.queuedCount>0){t.lastActivityAt=Date.now();return}const n=t.adapter.getStatus();if(n.busy||n.backgroundBusy){t.lastActivityAt=Date.now();return}Date.now()-t.lastActivityAt<=this.config.idleTimeoutMs||(o.info("adapter-pool",`Evicting idle slot for session ${e} (${s})`),this.removeSlot(e).catch(r=>{o.error("adapter-pool",`Failed to evict slot ${e}: ${r}`)}))}evictDeadSlot(){for(const[e,t]of this.slots)if(t.state==="stopped"&&t.respawn.exhausted)return o.info("adapter-pool",`Removing dead slot for session ${e} (exhausted respawn)`),this.removeSlot(e).catch(()=>{}),!0;return!1}evictIdleSlot(){let e=null;for(const t of this.slots.values()){if(t.stopping||t.state!=="ready"||t.eventQueue.runningCount>0||t.eventQueue.queuedCount>0)continue;const s=t.adapter.getStatus();s.busy||s.backgroundBusy||(!e||t.lastActivityAt<e.lastActivityAt)&&(e=t)}return e?(o.info("adapter-pool",`Evicting idle slot for session ${e.sessionId} (pool full)`),this.removeSlot(e.sessionId).catch(()=>{}),!0):!1}evictIdleSlotSync(){let e=null,t="";for(const[s,n]of this.slots){if(n.stopping||n.state!=="ready"||n.eventQueue.runningCount>0||n.eventQueue.queuedCount>0)continue;const r=n.adapter.getStatus();r.busy||r.backgroundBusy||(!e||n.lastActivityAt<e.lastActivityAt)&&(e=n,t=s)}return!e||!t?!1:(o.info("adapter-pool",`Evicting idle slot for session ${t} (pool full, sync)`),this.slots.delete(t),e.eventQueue.clear(t,"session slot removed"),e.eventQueue.destroy(),e.respawn.stopAll(),this.detachAllSlotEventListeners(e),e.adapter.stop().catch(()=>{}),!0)}getStatus(){let e=0,t=0;for(const s of this.slots.values())s.state==="ready"&&(e++,s.adapter.getStatus().busy&&t++);return{total:this.slots.size,ready:e,busy:t}}hasPendingWork(){for(const e of this.slots.values())if(e.eventQueue.runningCount>0||e.eventQueue.queuedCount>0||e.state==="ready"&&e.adapter.getStatus().busy)return!0;return!1}hasPendingOrBackgroundWork(){for(const e of this.slots.values()){if(e.eventQueue.runningCount>0||e.eventQueue.queuedCount>0)return!0;if(e.state==="ready"){const t=e.adapter.getStatus();if(t.busy||t.backgroundBusy)return!0}}return!1}createSlot(e){const t=this.factory(e),s=new d,n=this.createEventQueue(e),r={sessionId:e,adapter:t,respawn:s,state:"starting",lastActivityAt:Date.now(),startPromise:null,eventQueue:n,stopping:!1};this.slots.set(e,r);const a=(async()=>{try{this.wireSlotEvents(r),await t.start(),r.state="ready"}catch(i){throw this.detachAllSlotEventListeners(r),this.slots.delete(e),i}finally{r.startPromise=null}})();return r.startPromise=a,s.startHealthCheck(this.slotRespawnCtx(r)),r}wireSlotEvents(e){e.adapter.on("exit",t=>{this.stopped||(o.error("adapter-pool",`Slot ${e.sessionId} adapter exited (code=${t})`),e.state="stopped",e.eventQueue.pause("restart"),e.respawn.scheduleRespawn(this.slotRespawnCtx(e)))}),e.adapter.on("stuck",()=>{this.stopped||this.slots.get(e.sessionId)===e&&(o.error("adapter-pool",`Slot ${e.sessionId} adapter stuck, triggering respawn`),e.state="stopped",e.eventQueue.pause("restart"),e.respawn.scheduleRespawn(this.slotRespawnCtx(e)))}),e.adapter.on?.("error",t=>{if(this.stopped)return;const s=t instanceof Error?t.message:String(t);o.error("adapter-pool",`Slot ${e.sessionId} adapter error: ${s}`)}),e.adapter.on("internalError",t=>{if(!this.stopped){if(this.slots.get(e.sessionId)!==e){o.warn("adapter-pool",`Ignored internalError from detached slot session=${e.sessionId} event=${t.eventId}`);return}if(!this.onInternalError){o.warn("adapter-pool",`Slot ${e.sessionId} emitted internalError but no handler registered: event=${t.eventId} msg=${t.errorMsg}`);return}this.onInternalError(t)}}),e.adapter.on("eventStarted",(t,s)=>{this.stopped||this.slots.get(e.sessionId)===e&&this.onEventStarted?.(t,s)}),e.adapter.on("eventDone",t=>{this.stopped||this.slots.get(e.sessionId)===e&&(this.onEventDone?.(t,e.sessionId),e.eventQueue.markAdapterDone(t))}),e.adapter.on("sessionActivity",(t,s,n)=>{this.stopped||this.slots.get(e.sessionId)===e&&this.onSessionActivity?.(t||e.sessionId,s,n)}),e.adapter.on("pauseIntake",t=>{this.stopped||this.slots.get(e.sessionId)===e&&e.eventQueue.pause(t)}),e.adapter.on("resumeIntake",t=>{this.stopped||this.slots.get(e.sessionId)===e&&e.eventQueue.resume(t)})}detachAllSlotEventListeners(e){e.adapter.removeAllListeners("exit"),e.adapter.removeAllListeners("error"),e.adapter.removeAllListeners("stuck"),e.adapter.removeAllListeners("internalError"),e.adapter.removeAllListeners("eventStarted"),e.adapter.removeAllListeners("eventDone"),e.adapter.removeAllListeners("sessionActivity"),e.adapter.removeAllListeners("pauseIntake"),e.adapter.removeAllListeners("resumeIntake")}detachSlotEventListenersExceptDone(e){e.adapter.removeAllListeners("exit"),e.adapter.removeAllListeners("error"),e.adapter.removeAllListeners("stuck"),e.adapter.removeAllListeners("internalError"),e.adapter.removeAllListeners("eventStarted"),e.adapter.removeAllListeners("sessionActivity"),e.adapter.removeAllListeners("pauseIntake"),e.adapter.removeAllListeners("resumeIntake")}slotRespawnCtx(e){const t=this,s=e.sessionId;return{name:`slot-${s}`,get stopped(){return t.stopped||!t.slots.has(s)},get agentAlive(){return e.adapter.isAlive()},pingAgent:n=>e.adapter.ping(n),onRespawnNeeded:async()=>{e.eventQueue.pause("restart"),this.detachAllSlotEventListeners(e),await e.adapter.stop().catch(()=>{}),e.adapter=t.factory(s),e.state="starting",t.wireSlotEvents(e),await e.adapter.start(),e.state="ready",e.respawn.resetAttempts(),e.respawn.resetHealthFailures(),e.respawn.stopSlowRetry(),e.respawn.startHealthCheck(t.slotRespawnCtx(e)),e.eventQueue.resume("compaction"),e.eventQueue.resume("barrier"),e.eventQueue.resume("restart")},onCleanupAgent:()=>{"cleanup"in e.adapter&&e.adapter.cleanup()},onAbortActiveRun:n=>{}}}createEventQueue(e){const t=this.config.eventQueue??{maxConcurrent:1,maxQueued:5,queueTimeoutMs:3e5,cancelableQueued:!0,cancelableRunning:!0},s={onDeliver:n=>{const r=this.slots.get(e);r&&r.adapter.deliverInboundEvent(n)},onStateChange:(n,r,a,i)=>{this.onEventState?.(n,r,a,i)},onCancelRunning:n=>{this.deliverStopEvent(n,e)},onRejected:(n,r)=>{this.onEventState?.(n.event_id,n.session_id,"failed",{reason:r})},onComposing:(n,r,a)=>{this.onQueueComposing?.(n,r,a)}};return new p(t,s)}}export{m as AdapterPool,c as PoolFullError};
|