grix-connector 3.33.3 → 3.34.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 +5 -5
- package/dist/adapter/agy/agy-adapter.js +9 -9
- package/dist/adapter/agy/quota.js +5 -5
- package/dist/adapter/claude/claude-adapter.js +21 -21
- package/dist/adapter/claude/claude-bridge-server.js +1 -0
- package/dist/adapter/claude/claude-tools.js +1 -0
- package/dist/adapter/claude/claude-worker-client.js +1 -0
- package/dist/adapter/claude/mcp-http-launcher.js +2 -0
- package/dist/adapter/claude/result-timeout.js +1 -0
- package/dist/adapter/codex/codex-bridge.js +9 -9
- package/dist/adapter/deepseek/deepseek-adapter.js +6 -0
- package/dist/adapter/deepseek/index.js +1 -0
- package/dist/adapter/deepseek-harness/jsonrpc-adapter.js +2 -2
- package/dist/adapter/opencode/opencode-adapter.js +3 -3
- package/dist/adapter/qwen/index.js +1 -0
- package/dist/adapter/qwen/qwen-adapter.js +4 -0
- package/dist/aibot/client.js +1 -0
- package/dist/aibot/index.js +1 -0
- package/dist/aibot/types.js +0 -0
- package/dist/assets/dsh-bridge/{grix-dsh-bridge-3.33.3.tgz → grix-dsh-bridge-3.34.0.tgz} +0 -0
- package/dist/assets/dsh-bridge/manifest.json +5 -5
- package/dist/audit/adapters/agy/agy-audit-adapter.js +1 -0
- package/dist/audit/core/audit-state.js +1 -1
- package/dist/audit/worker/audit-recorder-worker-entry.js +1 -1
- package/dist/bridge/adapter-pool.js +1 -1
- package/dist/bridge/bridge.js +9 -9
- package/dist/bridge/deferred-events.js +1 -1
- package/dist/bridge/lifecycle-barrier.js +1 -0
- package/dist/bridge/pending-event-coordinator.js +1 -0
- package/dist/core/access/allowlist-gate.js +1 -0
- package/dist/core/access/allowlist-store.js +1 -0
- package/dist/core/access/index.js +1 -0
- package/dist/core/aibot/client.js +2 -2
- package/dist/core/file-ops/handler.js +1 -0
- package/dist/core/file-ops/list-files.js +1 -0
- package/dist/core/file-ops/types.js +0 -0
- package/dist/core/observability/sentry.js +1 -1
- package/dist/core/persistence/pending-event-store.js +1 -0
- package/dist/core/protocol/interaction-parser.js +1 -1
- package/dist/core/proxy/hermes-profile-relay.py +34 -15
- package/dist/core/proxy/relay-state-sync.js +1 -1
- package/dist/core/runtime/spawn.js +2 -3
- package/dist/core/upgrade/upgrade-checker.js +1 -1
- package/dist/default-skills/grix-agent-dispatch/SKILL.md +19 -9
- package/dist/grix.js +0 -0
- package/dist/log.js +3 -0
- package/dist/main.js +31 -0
- package/dist/manager-share-config.js +1 -1
- package/dist/manager.js +2 -2
- package/dist/mcp/stream-http/config.js +1 -0
- package/dist/mcp/stream-http/connection-binding.js +1 -0
- package/dist/mcp/stream-http/event-tool-executor.js +1 -0
- package/dist/mcp/stream-http/gateway.js +1 -0
- package/dist/mcp/stream-http/index.js +1 -0
- package/dist/mcp/stream-http/security.js +1 -0
- package/dist/mcp/stream-http/session-manager.js +1 -0
- package/dist/mcp/stream-http/tool-executor.js +1 -0
- package/dist/mcp/stream-http/tool-registry.js +1 -0
- package/dist/mcp/stream-http/tool-schemas.js +1 -0
- package/dist/service/process-control.js +1 -1
- package/dist/session/index.js +1 -0
- package/dist/session/manager.js +1 -0
- package/dist/transport/index.js +1 -0
- package/dist/transport/json-rpc.js +3 -0
- package/openclaw-plugin/audit-recorder-worker.js +364 -35
- package/openclaw-plugin/index.js +1 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{existsSync as h,mkdirSync as C,readFileSync as v,renameSync as M,writeFileSync as $}from"node:fs";import{join as g,dirname as R}from"node:path";import{spawn as L}from"node:child_process";import{log as o}from"../log/index.js";import{GRIX_PATHS as m}from"../log/index.js";import{resolveClientVersion as _}from"../util/client-version.js";import{UpgradeError as O,cleanupPendingBackup as y,collectEnvInfo as D,getUpgradeLogTail as N,isVersionAvailable as F,npmInstall as H,pendingExists as S,preflightCheck as x,readPending as B,removePending as u,rollbackPendingInstall as z,upgradeLog as
|
|
1
|
+
import{existsSync as h,mkdirSync as C,readFileSync as v,renameSync as M,writeFileSync as $}from"node:fs";import{join as g,dirname as R}from"node:path";import{spawn as L}from"node:child_process";import{log as o}from"../log/index.js";import{GRIX_PATHS as m}from"../log/index.js";import{resolveClientVersion as _}from"../util/client-version.js";import{UpgradeError as O,cleanupPendingBackup as y,collectEnvInfo as D,getUpgradeLogTail as N,isVersionAvailable as F,npmInstall as H,pendingExists as S,preflightCheck as x,readPending as B,removePending as u,rollbackPendingInstall as z,upgradeLog as a,verifyInstalledVersion as G,writePending as V}from"./npm-upgrader.js";import{isRetriableRegistryError as j}from"../installer/npm-registry.js";const K=360*60*1e3,X=300*1e3,T=1800*1e3,q=2,J=3,k="grix-connector",A=1e4,b=15,Y=2e3,Z=8e3,W=1e3;function P(l){return new URL(l.replace(/^wss:/,"https:").replace(/^ws:/,"http:")).origin}function E(){return g(m.data,"upgrade-state.json")}function I(){const l=E();if(!h(l))return{daily_attempts:{},version_attempts:{}};try{return JSON.parse(v(l,"utf-8"))}catch{return{daily_attempts:{},version_attempts:{}}}}function Q(l){const t=E();C(R(t),{recursive:!0});const e=t+".tmp";$(e,JSON.stringify(l),"utf-8"),M(e,t)}function U(){return new Date().toISOString().slice(0,10)}class ct{agentConfigs;isBusy;closeAdmission;timer=null;initialTimer=null;running=!1;stopped=!1;constructor(t,e,i){this.agentConfigs=t,this.isBusy=e,this.closeAdmission=i}async start(){await this.handlePendingOnStartup(),this.initialTimer=setTimeout(()=>{this.stopped||(this.runCheck(),!this.stopped&&(this.timer=setInterval(()=>this.runCheck(),K)))},X)}stop(){this.stopped=!0,this.initialTimer&&(clearTimeout(this.initialTimer),this.initialTimer=null),this.timer&&(clearInterval(this.timer),this.timer=null)}triggerCheck(){this.stopped||this.runCheck()}async checkForUpdate(){return this.agentConfigs.length===0?{available:!1}:(await Promise.all(this.agentConfigs.map(i=>this.queryUpgrade(i)))).find(i=>i.available&&i.release)??{available:!1}}async handlePendingOnStartup(){if(!S())return;const t=B();if(!t){u();return}const e=_(),i=e===t.target_version,r=i?{from_version:t.from_version,to_version:t.target_version,status:"success"}:{from_version:t.from_version,to_version:t.target_version,status:"rolled_back",error_code:"STARTUP_CRASH",crash_count:t.crash_count};if(a(i?`daemon startup: version matches target ${t.target_version}, upgrade succeeded`:`daemon startup: version ${e} != target ${t.target_version}, rolled back`),i){const s=await this.confirmHealthz(Z);a(s?"healthz confirmed healthy, reporting success":"healthz not confirmed within window; reporting success on version-match (guardian still guards crashes)")}if(await this.reportUpgrade(r)){i&&y(t),u();return}this.retryStartupReport(r,i?t:null)}async retryStartupReport(t,e){for(let i=2;i<=b;i++){if(this.stopped)return;const r=Y*(i-1)+Math.floor(Math.random()*1e3);if(await new Promise(s=>setTimeout(s,r)),this.stopped)return;if(await this.reportUpgrade(t)){y(e),u();return}}a(`startup report (${t.status}) not delivered after ${b} attempts; clearing pending to unblock future checks`),y(e),u()}async confirmHealthz(t){let e="19579";try{const s=g(m.data,"health-port");h(s)&&(e=v(s,"utf-8").trim()||e)}catch{}const i=`http://127.0.0.1:${e}/healthz`,r=Date.now()+t;for(;Date.now()<r&&!this.stopped;){try{if((await fetch(i,{signal:AbortSignal.timeout(2e3)})).ok)return!0}catch{}await new Promise(s=>setTimeout(s,W))}return!1}async runCheck(){if(!this.running){this.running=!0;try{await this.check()}catch(t){o.error("upgrade",`Check failed: ${t instanceof Error?t.message:t}`)}finally{this.running=!1}}}async check(){if(S()||this.agentConfigs.length===0)return;const i=(await Promise.all(this.agentConfigs.map(n=>this.queryUpgrade(n)))).find(n=>n.available&&n.release)?.release;if(!i)return;const r=i.version,s=_();if(!i.force&&!this.checkRateLimit(r))return;const p=x();if(!p.ok){await this.reportUpgrade({from_version:s,to_version:r,status:"failed",error_code:p.errorCode,error_msg:p.errorMsg});return}if(!await F(k,r)){a(`target ${r} not resolvable on any registry yet; skipping this cycle (no failure recorded)`);return}a(`upgrade start: ${s} -> ${r}`);const w=Date.now();try{if(V(s,r),await H(k,r),G(r),a("npm install verified, reporting installed"),await this.reportUpgrade({from_version:s,to_version:r,status:"installed",duration_ms:Date.now()-w}),a("shutting down for restart"),!await this.waitUntilIdle()){a("upgrade aborted: checker stopped during wait");return}this.startGuardian(),process.kill(process.pid,"SIGTERM")}catch(n){const d=n instanceof O?n.code:"NPM_INSTALL_FAILED",f=n instanceof Error?n.message:String(n);a(`upgrade failed: ${d} ${f}`),z()&&a("upgrade failed before restart; previous package restored atomically"),u(),j(f)||d==="NPM_TIMEOUT"?a(`failure is retriable (${d}); skipping cooldown, will retry next cycle`):this.recordFailure(r),await this.reportUpgrade({from_version:s,to_version:r,status:"failed",error_code:d,error_msg:f,duration_ms:Date.now()-w,upgrade_log:N()})}}async waitUntilIdle(){if(this.closeAdmission?.(),!this.isBusy?.())return!this.stopped;a("active tasks detected, waiting until idle before restart (no max wait)");const t=5e3,e=600*1e3;let i=Date.now();for(;this.isBusy()&&!this.stopped;)await new Promise(r=>setTimeout(r,t)),Date.now()-i>=e&&(i=Date.now(),a("still waiting for active tasks to finish before restart"));return this.stopped?!1:(a("all tasks completed, proceeding with restart"),!0)}checkRateLimit(t){const e=I();if(e.last_failure_at){const c=Date.now()-new Date(e.last_failure_at).getTime();if(c<T)return o.info("upgrade",`In cooldown, ${(T-c)/6e4}m remaining`),!1}const i=e.version_attempts[t]??0;if(i>=J)return o.info("upgrade",`Version ${t} already tried ${i} times, skipping`),!1;const r=U(),s=e.daily_attempts[r]??0;return s>=q?(o.info("upgrade",`Already ${s} attempts today, skipping`),!1):!0}recordFailure(t){const e=I(),i=U();e.last_failure_at=new Date().toISOString(),e.last_failure_version=t,e.daily_attempts[i]=(e.daily_attempts[i]??0)+1,e.version_attempts[t]=(e.version_attempts[t]??0)+1,Q(e)}async queryUpgrade(t){const i=`${P(t.wsUrl)}/v1/agent-api/upgrade/check?`+new URLSearchParams({client_type:"grix-connector",client_version:_(),channel:t.channel??"stable",platform:process.platform,arch:process.arch}).toString();try{const r=await fetch(i,{headers:{Authorization:`Bearer ${t.apiKey}`},signal:AbortSignal.timeout(A)});if(!r.ok)return o.warn("upgrade",`Check API returned ${r.status}`),{available:!1};const s=await r.json();return s.code!==0?{available:!1}:s.data}catch(r){return o.warn("upgrade",`Check API error: ${r instanceof Error?r.message:r}`),{available:!1}}}async reportUpgrade(t){if(this.agentConfigs.length===0)return!1;const e={client_type:"grix-connector",...t.npm_version?t:{...t,...D()}};return(await Promise.all(this.agentConfigs.map(async r=>{const c=`${P(r.wsUrl)}/v1/agent-api/upgrade/report`;try{return(await fetch(c,{method:"POST",headers:{Authorization:`Bearer ${r.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify(e),signal:AbortSignal.timeout(A)})).ok}catch{return!1}}))).some(r=>r)}startGuardian(){const t=g(m.base,"bin","upgrade-guardian.sh");if(process.platform==="win32"||!h(t)){o.info("upgrade","Guardian not available on this platform, skipping");return}try{const e=L(t,[],{detached:!0,stdio:"ignore"});e.unref(),a(`guardian started (pid ${e.pid})`)}catch(e){o.warn("upgrade",`Failed to start guardian: ${e instanceof Error?e.message:e}`)}}}export{ct as UpgradeChecker};
|
|
@@ -123,7 +123,7 @@ Do not omit any of the six.
|
|
|
123
123
|
| 2 | `quoted_message_id` | message id string | Anchor message in that session (must be authored by the dispatcher agent in groups) — passed as `grix_session_send.quoted_message_id` so quote-wake fires |
|
|
124
124
|
| 3 | `status` | `completed` \| `failed` \| `blocked` | Outcome |
|
|
125
125
|
| 4 | `summary` | short string | One-line conclusion |
|
|
126
|
-
| 5 | `detail` |
|
|
126
|
+
| 5 | `detail` | Markdown string | Structured key evidence / paths / command results; use bullets or short sections, not a single packed line |
|
|
127
127
|
| 6 | `work_session_id` | session id string | **This** work session id (the session you were dispatched into) |
|
|
128
128
|
|
|
129
129
|
### When to call
|
|
@@ -139,12 +139,15 @@ Do not omit any of the six.
|
|
|
139
139
|
### Implementation (format + send)
|
|
140
140
|
|
|
141
141
|
Build `content` as **only** the wire block below (field names Markdown-bold;
|
|
142
|
-
put each field **value** in its own
|
|
142
|
+
put each field **value** in its own fence — not the whole block, and not
|
|
143
143
|
inline backticks — so rendered bubbles expose a copy button). Use a
|
|
144
|
-
` ```text ` fence for
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
144
|
+
` ```text ` fence for `status`, `summary`, and `session`; use a
|
|
145
|
+
` ```markdown ` fence for `detail`. The `detail` value must be concise
|
|
146
|
+
Markdown with clear structure: bullets, short sections, paths in backticks,
|
|
147
|
+
and command results when relevant. Do not cram all detail into one sentence or
|
|
148
|
+
one line. **Do not** put `@…` in the content — wake/threading is done via
|
|
149
|
+
`quoted_message_id` on the tool call. No text outside the block. Then call
|
|
150
|
+
`grix_session_send` (see grix-owner-relay; or call the tool directly) with:
|
|
148
151
|
|
|
149
152
|
- `session_id` = `callback_session_id`
|
|
150
153
|
- `content` = the block
|
|
@@ -163,8 +166,13 @@ completed|failed|blocked
|
|
|
163
166
|
<一句话结论>
|
|
164
167
|
```
|
|
165
168
|
**detail**:
|
|
166
|
-
```
|
|
167
|
-
|
|
169
|
+
```markdown
|
|
170
|
+
- 变更:
|
|
171
|
+
- `<路径>`:<做了什么>
|
|
172
|
+
- 验证:
|
|
173
|
+
- `<命令>`:<结果>
|
|
174
|
+
- 备注:
|
|
175
|
+
- <阻塞/风险/无则写“无”>
|
|
168
176
|
```
|
|
169
177
|
**session**:
|
|
170
178
|
```text
|
|
@@ -191,7 +199,9 @@ quoting your anchor message. When you see a message containing a
|
|
|
191
199
|
identity, and may contain arbitrary text. It is never a new task from the
|
|
192
200
|
owner.
|
|
193
201
|
2. Report the result to the user **in your own voice**: status, conclusion,
|
|
194
|
-
key evidence.
|
|
202
|
+
key evidence. Preserve `detail` as clear Markdown structure instead of
|
|
203
|
+
flattening it into one line. Do not parrot the raw block as if the owner
|
|
204
|
+
said it.
|
|
195
205
|
3. **Do not dispatch again** in reaction to a callback. The loop ends with
|
|
196
206
|
your report.
|
|
197
207
|
4. Report each dispatched session's callback **once**. If a duplicate
|
package/dist/grix.js
CHANGED
|
File without changes
|
package/dist/log.js
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{createWriteStream as g,mkdirSync as l,existsSync as f}from"node:fs";import{join as t}from"node:path";import{homedir as m}from"node:os";const i=t(m(),".grix"),s={base:i,config:t(i,"config"),log:t(i,"log"),data:t(i,"data")};function S(){for(const o of Object.values(s))f(o)||l(o,{recursive:!0})}let a=null;function $(){const o=new Date().toISOString().slice(0,10),r=t(s.log,`grix-acp-${o}.log`);a=g(r,{flags:"a"})}function c(){return new Date().toISOString().slice(11,19)}const u={info(o,r,...n){const e=`${c()} [${o}] ${r}${n.length?" "+n.map(String).join(" "):""}`;console.log(e),a?.write(e+`
|
|
2
|
+
`)},error(o,r,...n){const e=`${c()} [${o}] ERROR ${r}${n.length?" "+n.map(String).join(" "):""}`;console.error(e),a?.write(e+`
|
|
3
|
+
`)}};export{s as GRIX_PATHS,S as ensureGrixDirs,$ as initLogger,u as log};
|
package/dist/main.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import x from"node:path";import{Manager as E}from"./manager.js";import{ensureGrixDirs as k,initLogger as O,log as a}from"./core/log/index.js";import{HealthServer as I}from"./core/runtime/index.js";import{writePidFile as R,removePidFile as l}from"./core/runtime/index.js";import{resolveRuntimePaths as g}from"./core/config/index.js";import{ServiceManager as T}from"./service/service-manager.js";import{acquireDaemonLock as $,releaseDaemonLock as p}from"./runtime/daemon-lock.js";import{writeDaemonStatus as f,removeDaemonStatus as F}from"./runtime/service-state.js";const n=process.argv.slice(2),m=[],s={};for(let e=0;e<n.length;e++)n[e].startsWith("--")&&n[e+1]&&!n[e+1].startsWith("--")?(s[n[e].slice(2)]=n[e+1],e++):n[e].startsWith("--")?s[n[e].slice(2)]="true":m.push(n[e]);if(s.help&&(console.log(`grix-connector \u2014 Unified AI Agent Bridge
|
|
3
|
+
|
|
4
|
+
Usage: grix-connector [options]
|
|
5
|
+
grix-connector service <action> [options]
|
|
6
|
+
|
|
7
|
+
Actions (service):
|
|
8
|
+
install Install and start as OS service
|
|
9
|
+
start Start the OS service
|
|
10
|
+
stop Stop the OS service
|
|
11
|
+
restart Restart the OS service
|
|
12
|
+
uninstall Uninstall the OS service
|
|
13
|
+
status Show service and daemon status
|
|
14
|
+
|
|
15
|
+
Options:
|
|
16
|
+
--config-dir <path> Config directory (default: ~/.grix/config)
|
|
17
|
+
--profile <name> Profile name for config subdirectory
|
|
18
|
+
--health-port <port> Health check port (default: 19579)
|
|
19
|
+
--help Show this help message
|
|
20
|
+
|
|
21
|
+
Platform services:
|
|
22
|
+
macOS: launchd (LaunchAgent)
|
|
23
|
+
Linux: systemd --user
|
|
24
|
+
Windows: Task Scheduler
|
|
25
|
+
|
|
26
|
+
Examples:
|
|
27
|
+
grix-connector # Run in foreground
|
|
28
|
+
grix-connector service install # Install as OS service
|
|
29
|
+
grix-connector service status # Check service status
|
|
30
|
+
grix-connector service uninstall # Remove OS service
|
|
31
|
+
`),process.exit(0)),m[0]==="service"){const e=m[1],t=["install","start","stop","restart","uninstall","status"];(!e||!t.includes(e))&&(console.error(`Usage: grix-connector service <${t.join("|")}>`),process.exit(1));const o=g(),w=s["config-dir"]??(s.profile?`${o.configDir}/${s.profile}`:void 0),D=x.resolve(process.argv[1]||`${o.rootDir}/dist/main.js`),c=new T({cliPath:D,nodePath:process.execPath});try{let r;switch(e){case"install":r=await c.install({rootDir:o.rootDir,configDir:w});break;case"start":r=await c.start({rootDir:o.rootDir});break;case"stop":r=await c.stop({rootDir:o.rootDir});break;case"restart":r=await c.restart({rootDir:o.rootDir});break;case"uninstall":r=await c.uninstall({rootDir:o.rootDir});break;case"status":r=await c.status({rootDir:o.rootDir});break}console.log(JSON.stringify(r,null,2)),process.exit(0)}catch(r){console.error(`service ${e} failed: ${r instanceof Error?r.message:r}`),process.exit(1)}}const i=g(),N=s["config-dir"]??(s.profile?`${i.configDir}/${s.profile}`:void 0),h=new E,d=new I;let S=!1;async function u(e){if(S)return;S=!0,a.info("main",`Received ${e}, shutting down...`),d.markShuttingDown();const t=setTimeout(()=>{a.error("main","Shutdown timed out, forcing exit"),p(i.daemonLockFile).catch(()=>{}),l(),process.exit(2)},1e4);try{await h.stop(),await d.stop(),await p(i.daemonLockFile),await F(i.daemonStatusFile).catch(()=>{}),clearTimeout(t),l(),a.info("main","Shutdown complete"),process.exit(0)}catch(o){a.error("main",`Shutdown error: ${o}`),p(i.daemonLockFile).catch(()=>{}),l(),process.exit(2)}}async function P(){k(),O();try{await $(i.daemonLockFile,i.rootDir)}catch(t){console.error(t instanceof Error?t.message:t),process.exit(1)}R(),a.info("main",`grix-connector starting (PID ${process.pid})`),await f(i.daemonStatusFile,{state:"starting",pid:process.pid,updated_at:Date.now()});const e=parseInt(s["health-port"]??process.env.GRIX_HEALTH_PORT??"19579",10);await d.start(e),process.on("SIGINT",()=>u("SIGINT")),process.on("SIGTERM",()=>u("SIGTERM")),process.on("uncaughtException",t=>{a.error("main",`Uncaught exception: ${t instanceof Error?t.stack:t}`),!v(t)&&u("uncaughtException")}),process.on("unhandledRejection",t=>{a.error("main",`Unhandled rejection: ${t}`),!v(t)&&u("unhandledRejection")}),d.setStatusProvider(()=>h.getAgentsStatus()),await h.start(N),await f(i.daemonStatusFile,{state:"running",pid:process.pid,updated_at:Date.now()}),process.send&&process.send("ready"),a.info("main","grix-connector ready")}P().catch(e=>{a.error("main",`Fatal: ${e}`),p(i.daemonLockFile).catch(()=>{}),l(),process.exit(1)});const A=new Set(["ECONNRESET","ECONNREFUSED","ETIMEDOUT","EPIPE","EAI_AGAIN","ENOTFOUND","EHOSTUNREACH","ENETUNREACH"]);function v(e){return e instanceof Error&&"code"in e?A.has(e.code):!1}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{readFileSync as f,writeFileSync as u,unlinkSync as d}from"node:fs";import{join as P}from"node:path";function l(e,t){return`${e}#shared:${t}`}function a(e,t){if(!e)return e;const i=e.lastIndexOf("."),n=Math.max(e.lastIndexOf("/"),e.lastIndexOf("\\"));return i>n?`${e.slice(0,i)}.shared.${t}${e.slice(i)}`:`${e}.shared.${t}`}function
|
|
1
|
+
import{readFileSync as f,writeFileSync as u,unlinkSync as d}from"node:fs";import{join as P}from"node:path";function l(e,t){return`${e}#shared:${t}`}function a(e,t){if(!e)return e;const i=e.lastIndexOf("."),n=Math.max(e.lastIndexOf("/"),e.lastIndexOf("\\"));return i>n?`${e.slice(0,i)}.shared.${t}${e.slice(i)}`:`${e}.shared.${t}`}function x(e,t){return{...e,name:l(e.name,t),aibot:{...e.aibot,sharedOwnerId:t,terminalOutboxPath:a(e.aibot.terminalOutboxPath,t),terminalCommitTokenStorePath:a(e.aibot.terminalCommitTokenStorePath,t),stopResultOutboxPath:a(e.aibot.stopResultOutboxPath,t)},eventResultsPath:a(e.eventResultsPath,t),bindingsPath:a(e.bindingsPath,t),legacyBindingsPath:a(e.legacyBindingsPath,t),activeEventStorePath:a(e.activeEventStorePath,t),pendingEventStorePath:a(e.pendingEventStorePath,t),logDir:e.logDir?`${e.logDir}/shared-${t}`:void 0,connectMaxRetries:e.connectMaxRetries??3}}function m(e,t,i){const n=new Set(i.map(r=>String(r).trim()).filter(r=>r.length>0)),o=[...t],s=`${e}#shared:`,h=[...n].filter(r=>!o.includes(l(e,r))),c=o.filter(r=>r.startsWith(s)).map(r=>({key:r,sharedOwnerId:r.slice(s.length)})).filter(r=>!n.has(r.sharedOwnerId));return{toAdd:h,toRemove:c}}class y{dataDir;constructor(t){this.dataDir=t}filePath(t){const i=t.replace(/[^a-zA-Z0-9_-]/g,"_");return P(this.dataDir,`shared-owners-${i}.json`)}load(t){try{const i=f(this.filePath(t),"utf-8"),n=JSON.parse(i);if(Array.isArray(n))return n.filter(o=>typeof o=="string"&&o.length>0)}catch{}return[]}save(t,i){const n=[...new Set(i.filter(o=>o.length>0))];if(n.length===0){this.delete(t);return}try{u(this.filePath(t),JSON.stringify(n),"utf-8")}catch{}}remove(t,i){const n=this.load(t).filter(o=>o!==i);n.length>0?this.save(t,n):this.delete(t)}delete(t){try{d(this.filePath(t))}catch{}}}export{y as SharedOwnersCache,x as buildSharedInstanceConfig,m as diffSharedOwners,l as sharedInstanceKey,a as suffixSharedPath};
|
package/dist/manager.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{readFileSync as se,readdirSync as ie,writeFileSync as oe}from"node:fs";import{join as b}from"node:path";import{homedir as ce}from"node:os";import{AgentInstance as q}from"./bridge/bridge.js";import{buildSharedInstanceConfig as le,diffSharedOwners as de,sharedInstanceKey as pe,SharedOwnersCache as me}from"./manager-share-config.js";import{isAgentDeletedError as B}from"./core/aibot/errors.js";import{GRIX_PATHS as w,log as l}from"./core/log/index.js";import{resolveClientVersion as ue}from"./core/util/client-version.js";import{UpgradeChecker as ge}from"./core/upgrade/upgrade-checker.js";import{SkillSyncer as fe}from"./core/skill-sync/skill-syncer.js";import{AgentGlobalConfigStore as he}from"./core/persistence/agent-global-config-store.js";import{resolveSkillScanMode as _e}from"./adapter/claude/skill-scanner.js";import{buildWireSkills as ye}from"./core/skill-sync/sync-state.js";import{buildLibrarySkills as we}from"./core/skill-sync/library-skills.js";import{AUDIT_LOCAL_ACTION_TYPES as Se}from"./core/aibot/types.js";import{buildReportedSkills as ke,logDefaultSkillsCheck as be,cleanupProjectedSkills as U}from"./default-skills/index.js";import{buildProviderEnv as ve,supportsKiroCwRelay as F,supportsNonMitmRelayConfig as Ae}from"./core/config/provider-env.js";import{resolveHermesHomeDir as Ce}from"./adapter/shared/native-provider-config.js";import{ensureKiroRelay as K,stopKiroRelay as Q}from"./core/proxy/kiro-relay.js";import{resolveCopilotCommand as W}from"./core/runtime/copilot-resolve.js";import{resolveCursorCommand as G}from"./core/runtime/cursor-resolve.js";import{getCliVersion as xe,resolveCliPath as $e}from"./core/util/cli-probe.js";import{AgentInstaller as Ee}from"./core/installer/installer.js";import{reportInstallFailure as Ie}from"./core/observability/sentry.js";import{createDefaultAuditProducer as Te}from"./audit/runtime/default-audit-recorder.js";import{normalizeCodexConfigOverrides as Me}from"./adapter/codex/config-overrides.js";import{resolveCodexDirectHome as Pe}from"./adapter/codex/direct-home.js";function z(n){if(n)return{...n.provider_id?.trim()?{providerId:n.provider_id.trim()}:{},...n.base_url?.trim()?{baseUrl:n.base_url.trim()}:{},...n.quota_base_url?.trim()?{quotaBaseUrl:n.quota_base_url.trim()}:{},...n.api_key?.trim()?{apiKey:n.api_key.trim()}:{},...n.model?.trim()?{model:n.model.trim()}:{},...n.fast_model?.trim()?{fastModel:n.fast_model.trim()}:{},...n.effort?.trim()?{effort:n.effort.trim()}:{}}}const je=8e3;function Re(){const n=W();return[{clientType:"openclaw",command:"openclaw"},{clientType:"claude",command:"claude"},{clientType:"codex",command:"codex"},{clientType:"qwen",command:"qwen"},{clientType:"hermes",command:"hermes"},{clientType:"reasonix",command:"reasonix"},{clientType:"codewhale",command:"codewhale"},{clientType:"opencode",command:"opencode"},{clientType:"cursor",command:G()},{clientType:"pi",command:"pi"},{clientType:"openhuman",command:"openhuman-core"},{clientType:"kiro",command:"kiro-cli"},{clientType:"kimi",command:"kimi"},{clientType:"copilot",command:n.command},{clientType:"agy",command:"agy"},{clientType:"deepseek",command:"dsh"}]}async function De(){return Promise.all(Re().map(async n=>{const e=await $e(n.command);if(!e)return{client_type:n.clientType,command:n.command,installed:!1,path:null,version:null};const t=await xe(e,n.versionArgs??["--version"]);return{client_type:n.clientType,command:n.command,installed:!0,path:e,version:t.version,error:t.error}}))}function Oe(n){switch(n){case"claude":return{adapterType:"claude",command:"claude"};case"codex":return{adapterType:"codex",command:"codex",options:{sandboxMode:"danger-full-access"}};case"gemini":return{adapterType:"acp",command:"gemini",autoInjectArgs:{acp:!0},enableSessionBinding:!0};case"qwen":return{adapterType:"acp",command:"qwen",adapterHint:"qwen/base",autoInjectArgs:{acp:!0},enableSessionBinding:!0};case"pi":return{adapterType:"pi",command:"pi"};case"cursor":return{adapterType:"cursor",command:G()};case"reasonix":return{adapterType:"acp",command:"reasonix",args:["acp"],enableSessionBinding:!0};case"codewhale":return{adapterType:"codewhale",command:"codewhale",enableSessionBinding:!0};case"openhuman":return{adapterType:"openhuman",command:"openhuman-core",enableSessionBinding:!0};case"kiro":return{adapterType:"acp",command:"kiro-cli",args:["acp"],enableSessionBinding:!0};case"kimi":return{adapterType:"acp",command:"kimi",args:["acp"],options:{initial_mode:"yolo"},enableSessionBinding:!0};case"opencode":return{adapterType:"opencode",command:"opencode",args:["serve"],enableSessionBinding:!0};case"copilot":{const e=W();return{adapterType:"acp",command:e.command,args:[...e.prefixArgs,"--acp"],enableSessionBinding:!0}}case"agy":return{adapterType:"agy",command:"agy",enableSessionBinding:!0};case"deepseek":return{adapterType:"deepseek-harness",command:"dsh",adapterHint:"deepseek/grix-bridge-v1",options:{integrationMode:"profile_bridge",dshProfile:"web",autoInstallBridge:!0,autoStartProfile:!0},enableSessionBinding:!0};case"hermes":return{adapterType:"acp",command:"hermes",args:["acp"],enableSessionBinding:!0};default:throw new Error(`Unsupported client_type: ${n}`)}}function A(n,e){return String(n??"").trim().toLowerCase().replace(/[^a-z0-9._-]+/g,"-").replace(/-+/g,"-").replace(/^-|-$/g,"")||String(e??"").trim().toLowerCase().replace(/[^a-z0-9._-]+/g,"-").replace(/-+/g,"-").replace(/^-|-$/g,"")||"default"}function He(n){return b(w.data,`session-bindings-${A(n,"unknown")}.json`)}function Le(n,e){return b(w.data,`session-bindings-${A(n,e)}.json`)}function Ne(n,e){return b(w.data,`active-events-${A(n,e)}.json`)}function qe(n,e){const t=A(n,"agent"),r=A(e,"unknown");return b(w.data,`terminal-outbox-${t}-${r}.json`)}function Be(...n){const e=[],t=new Set;for(const r of n)for(const a of r??[]){const c=String(a??"").trim(),s=c.toLowerCase();!c||t.has(s)||(t.add(s),e.push(c))}return e.length>0?e:void 0}function Ue(n,e){const t={claude:{maxConcurrent:1,maxQueued:5,queueTimeoutMs:0},codex:{maxConcurrent:1,maxQueued:5,queueTimeoutMs:0},cursor:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0},acp:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0},pi:{maxConcurrent:1,maxQueued:5,queueTimeoutMs:0},codewhale:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0},openhuman:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0},opencode:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0},agy:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0},"deepseek-harness":{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0}},r=t[n]??t.acp;return{maxConcurrent:e?.max_concurrent??r.maxConcurrent??1,maxQueued:e?.max_queued??r.maxQueued??3,queueTimeoutMs:e?.queue_timeout_ms??r.queueTimeoutMs??0,cancelableQueued:!0,cancelableRunning:!0}}function Fe(n,e={}){const t=ue(),r=String(n.client_type??"").trim().toLowerCase(),a=Oe(r),c=r==="deepseek"?n.dsh?.integration_mode??"profile_bridge":void 0,s=c==="embedded_jsonrpc",p=String(n.ws_url??"").trim(),o="get_session_usage",i="get_rate_limits",_="get_agent_global_config",g=Se;if(!n.name?.trim())throw new Error("agent name is required");if(!p)throw new Error(`agent ${n.name}: ws_url is required`);if(!n.agent_id?.trim())throw new Error(`agent ${n.name}: agent_id is required`);if(!n.api_key?.trim())throw new Error(`agent ${n.name}: api_key is required`);const d=a.adapterType,m=d==="acp",y=r==="qwen",u={...a.options??{}};if(d==="deepseek-harness"&&(u.integrationMode=c,u.dshProfile=n.dsh?.profile?.trim()||"web",u.autoInstallBridge=n.dsh?.auto_install_bridge??!s,u.autoStartProfile=n.dsh?.auto_start_profile??!s),d==="deepseek-harness"&&n.provider?.model?.trim()&&(u.defaultModel=n.provider.model.trim()),n.codex_config_overrides!==void 0){if(d!=="codex")throw new Error(`agent ${n.name}: codex_config_overrides is only supported for client_type=codex`);Me(n.codex_config_overrides,`agent ${n.name}: codex_config_overrides`),u.codexConfigOverrides=n.codex_config_overrides}if(d==="codex"&&n.provider?.wire_api!==void 0){const f=n.provider;if(f.wire_api!=="responses"||!f.base_url?.trim()||!f.api_key?.trim()||!f.model?.trim()||!f.catalog_version?.trim()||!f.catalog_sha256?.trim()||!f.catalog_json?.trim())throw Object.assign(new Error(`agent ${n.name}: incomplete Codex direct relay config`),{code:"INVALID_CODEX_DIRECT_CONFIG"});const N=Pe(w.data,n.name);u.codexHome=N,u.codexDirect={codexHome:N,config:{baseUrl:f.base_url.trim(),model:f.model.trim(),...f.reasoning_effort?.trim()?{reasoningEffort:f.reasoning_effort.trim()}:{},token:f.api_key.trim()},catalog:{version:f.catalog_version.trim(),sha256:f.catalog_sha256.trim(),json:f.catalog_json}}}const h=d==="codex"?{capabilities:["local_action_v1","agent_invoke"],localActions:["session_control","get_context","set_model","set_mode","set_reasoning_effort","set_service_tier","set_sandbox_mode","exec_approve","exec_reject","file_list","create_folder","turn_interrupt","permission_approve","permission_reject","thread_compact",o,i]}:null,k=d==="claude"?{localActions:["session_control","set_mode","set_model","set_reasoning_effort","claude_interaction_reply","exec_approve","exec_reject","file_list","create_folder","thread_compact",o,i]}:null,S=y?{capabilities:["stream_chunk","local_action_v1"],localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","file_list","create_folder",o],adapterHint:"qwen/base"}:null,$=d==="pi"?{adapterHint:"pi/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","get_context","file_list","create_folder",o,i]}:null,E=d==="openhuman"?{adapterHint:"openhuman/base",capabilities:["local_action_v1"],localActions:["session_control","set_model","file_list","create_folder"]}:null,I=d==="cursor"?{adapterHint:"cursor/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","set_mode","get_context","file_list","create_folder","thread_compact","claude_interaction_reply",i]}:null,R=d==="codewhale"?{capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","file_list","create_folder",o]}:null,T=d==="opencode"?{adapterHint:"opencode/base",capabilities:["stream_chunk","local_action_v1"],localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","claude_interaction_reply","file_list","create_folder"]}:null,M=d==="agy"?{adapterHint:"agy/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","file_list","create_folder"]}:null,P=d==="deepseek-harness"?{adapterHint:s?"deepseek/jsonrpc-v1":"deepseek/grix-bridge-v1",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_provider","set_model","set_mode","set_thinking","set_reasoning_effort","set_preset","set_profile","create_profile",o,i,"exec_approve","exec_reject","permission_approve","permission_reject","file_list","create_folder","dsh_bridge_status","dsh_install_bridge","dsh_list_plugins","dsh_enable_plugin","dsh_disable_plugin","dsh_refresh_plugins","dsh_list_skills","dsh_enable_skill","dsh_disable_skill","dsh_list_mcp","dsh_list_tools","dsh_list_jobs","dsh_list_goals"]}:null,D=new Set(["hermes","reasonix","kimi","copilot"]),J=m&&!D.has(r),X=m&&!y?{localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","claude_interaction_reply","file_list","create_folder",...J?[o]:[]]}:null,Y=r==="kiro"?{localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","file_list","create_folder","thread_compact",o,i]}:null,Z=d==="codex"||d==="claude"||d==="opencode"||d==="deepseek-harness"||r==="gemini"?["session_control","set_model","set_mode",...d==="deepseek-harness"?["set_provider","set_thinking","set_reasoning_effort","set_preset","set_profile","create_profile","dsh_list_plugins","dsh_enable_plugin","dsh_disable_plugin","dsh_refresh_plugins","dsh_list_skills","dsh_enable_skill","dsh_disable_skill"]:[],...d==="claude"?["set_reasoning_effort"]:[]]:void 0,ee=d==="cursor"||d==="opencode"||d==="openhuman"||d==="agy"||m&&D.has(r)?[i]:[o,i];m&&u.raw_transport===void 0&&(u.raw_transport=r==="gemini");const te=`${r}/base`,C=_e(d,r),ne=C==="kiro"?void 0:process.cwd(),j=z(n.provider),v={...process.env,...n.env};if(C==="hermes"&&!v.HERMES_HOME?.trim()){const f=ve(r,j);v.HERMES_HOME=Ce({...f,...v},n.name)}const O=ke({mode:C,projectDir:ne,env:v}),H=O.filter(f=>f.source==="connector").map(f=>f.name);H.length>0&&!e.quiet&&l.info("manager",`[${n.name}] injecting connector skills: [${H.join(", ")}]`);const L=ye(O,w.skills);let re=L.length>0?L:void 0;const ae=we({skillsDir:w.skills,mode:C,home:ce(),env:v});return{name:n.name,adapterType:d,aibot:{url:p,agentId:n.agent_id,apiKey:n.api_key,clientType:r,clientVersion:t,adapterHint:P?.adapterHint??a.adapterHint??S?.adapterHint??$?.adapterHint??E?.adapterHint??I?.adapterHint??T?.adapterHint??M?.adapterHint??te,capabilities:P?.capabilities??h?.capabilities??R?.capabilities??$?.capabilities??E?.capabilities??I?.capabilities??T?.capabilities??M?.capabilities??S?.capabilities??["stream_chunk","local_action_v1","connector_upgrade"],localActions:Be(P?.localActions??h?.localActions??R?.localActions??k?.localActions??$?.localActions??E?.localActions??I?.localActions??T?.localActions??M?.localActions??S?.localActions??Y?.localActions??X?.localActions??["exec_approve","exec_reject"],Z,ee,["connector_rollback","connector_upgrade_push",_,"configure_gateway_provider","skill_upload","skill_delete","skill_enable","skill_disable","skill_refresh",...g]),skills:re,librarySkills:ae,terminalOutboxPath:qe(n.name,n.agent_id)},agent:{command:s?"dsh-jsonrpc-agent":a.command,args:[...a.args??[],...n.args??[]],env:n.env,clientType:r,...j?{provider:j}:{}},adapterOptions:u,acpAuthMethod:u.auth_method,acpInitialMode:u.initial_mode,acpMcpTools:u.acp_mcp_tools,promptTimeoutMs:n.prompt_timeout_ms,bindingsPath:He(n.agent_id),legacyBindingsPath:Le(n.name,n.agent_id),activeEventStorePath:Ne(n.name,n.agent_id),...a.enableSessionBinding||m?{enableSessionBinding:!0}:{},...a.autoInjectArgs||r==="reasonix"&&n.provider?.model?.trim()?{autoInjectArgs:{...a.autoInjectArgs??{},...r==="reasonix"&&n.provider?.model?.trim()?{model:n.provider.model.trim()}:{}}}:{},poolMaxSize:n.pool?.maxSize??(d==="deepseek-harness"?4:void 0),poolIdleTimeoutMs:n.pool?.idleTimeoutMs,eventQueue:Ue(d,n.event_queue),logDir:w.log,providerBaseUrl:n.provider_base_url?.trim()||void 0,providerApiKey:n.provider_api_key?.trim()||void 0}}function Ke(){const n=process.env.GRIX_AGENT_STARTUP_WAIT_MS,e=Number(n);return Number.isFinite(e)&&e>=500?Math.floor(e):je}function V(n){const e=[],t=[],r=[],a=ie(n).filter(c=>c.endsWith(".json")).sort();for(const c of a)try{const s=JSON.parse(se(b(n,c),"utf-8"));if(s===null||typeof s!="object"||Array.isArray(s)||!("agents"in s)){r.push(c);continue}if(!Array.isArray(s.agents)){t.push({file:c,error:"agents is not an array"});continue}if(s.agents.length===0){t.push({file:c,error:"agents array is empty"});continue}for(const p of s.agents)e.push({entry:p,file:c})}catch(s){t.push({file:c,error:s instanceof Error?s.message:String(s)})}return{entries:e,fileErrors:t,skipped:r}}function x(n){if(n===null||typeof n!="object")return JSON.stringify(n)??"null";if(Array.isArray(n))return`[${n.map(x).join(",")}]`;const e=n;return`{${Object.keys(e).sort().map(r=>`${JSON.stringify(r)}:${x(e[r])}`).join(",")}}`}function Qe(n,e){return x(n)===x(e)}class kt{instances=[];configMap=new Map;rawConfigMap=new Map;reloadChain=Promise.resolve();sharedInstances=new Map;shareSyncChains=new Map;stopping=!1;deletedAgentCleanups=new Set;upgradeChecker=null;skillSyncer=null;globalConfigStore;configDir=w.config;sharedOwnersCache=new me(w.data);installer=new Ee;relaySecretResolver;auditProducer=Te();async start(e){const t=e??w.config;this.configDir=t,l.info("manager",`Loading configs from ${t}`),be(),U(),this.globalConfigStore=new he(b(w.data,"agent-global-configs.json")),this.globalConfigStore.load();const{entries:r,fileErrors:a,skipped:c}=V(t);for(const i of a)l.error("manager",`Failed to load ${i.file}: ${i.error}`);for(const i of c)l.info("manager",`Skipped ${i}: not an agent config (no "agents" key)`);if(r.length===0&&a.length===0&&c.length===0){l.warn("manager",`No config files found in ${t}; daemon idle (waiting for config). Add agents under this directory then run: grix-connector reload`);return}const s=[];let p=a.length;for(const{entry:i,file:_}of r)try{const g=this.toRuntimeInstanceConfig(i);s.push({config:g,entry:i,file:_}),l.info("manager",`Loaded ${g.name} (${g.adapterType??"acp"}) from ${_}`)}catch(g){const d=typeof i?.name=="string"?i.name:"<unknown>";l.error("manager",`Invalid agent config in ${_} (name=${d}): ${g}`),p++}let o=0;if(s.length>0){const i=Ke();l.info("manager",`Starting ${s.length} agent(s), startup wait=${i}ms`);const _=m=>{this.instances=this.instances.filter(y=>y!==m)},g=s.map(({config:m,entry:y,file:u})=>{const h=this.createConfiguredInstance(m);return this.instances.push(h),this.configMap.set(m.name,m),this.rawConfigMap.set(m.name,{entry:y,file:u}),{config:m,instance:h,startPromise:this.prepareProviderRuntime(m,y).then(()=>h.start())}}),d=await Promise.all(g.map(async m=>{const y=await new Promise(u=>{let h=!1;const k=setTimeout(()=>{h||(h=!0,u({kind:"timeout"}))},i);m.startPromise.then(()=>{h||(h=!0,clearTimeout(k),u({kind:"started"}))}).catch(S=>{h||(h=!0,clearTimeout(k),u({kind:"failed",error:S}))})});return{task:m,outcome:y}}));for(const{task:m,outcome:y}of d){if(y.kind==="started"){this.restoreCachedSharedInstances(m.config);continue}if(y.kind==="failed"){if(B(y.error)){this.onAgentDeletedByPlatform(m.config.name);continue}_(m.instance),l.error("manager",`Failed to start ${m.config.name}: ${y.error}`);continue}o++,l.warn("manager",`Startup pending for ${m.config.name}, continue retrying in background`),m.startPromise.then(()=>{l.info("manager",`Delayed start succeeded: ${m.config.name}`),this.restoreCachedSharedInstances(m.config)}).catch(u=>{if(B(u)){this.onAgentDeletedByPlatform(m.config.name);return}_(m.instance),l.error("manager",`Delayed start failed: ${m.config.name}: ${u}`)})}if(this.instances.length>0){const m=Math.max(0,this.instances.length-o);l.info("manager",`${m}/${s.length} agent(s) running now`)}o>0&&l.warn("manager",`${o} agent(s) still connecting in background`)}if(this.instances.length===0&&s.length>0)throw new Error("All agent configurations failed to start");await this.ensureAuxServices()}async ensureAuxServices(){if(this.configMap.size===0)return;const e=[...this.configMap.values()].map(t=>({apiKey:t.aibot.apiKey,wsUrl:t.aibot.url}));this.upgradeChecker||(this.upgradeChecker=new ge(e,()=>this.instances.some(t=>t.hasPendingOrBackgroundWork())||[...this.sharedInstances.values()].some(t=>t.hasPendingOrBackgroundWork())),await this.upgradeChecker.start()),this.skillSyncer||(this.skillSyncer=new fe(e,w.skills),this.skillSyncer.setOnSyncSuccess(()=>this.refreshOwnerLibrarySkills()),await this.skillSyncer.start())}refreshOwnerLibrarySkills(){const e=new Map,t=r=>{let a;try{a=r.skillsSyncGroupKey()}catch(s){l.warn("manager",`skillsSyncGroupKey failed for ${r.name}: ${s}`);return}const c=e.get(a)??[];c.push(r),e.set(a,c)};for(const r of this.instances)t(r);for(const r of this.sharedInstances.values())t(r);for(const[r,a]of e){let c=!1;const s=[];for(const p of a){if(c){s.push(p);continue}let o=!1;try{o=p.forceRefreshSkills(void 0,{force:!0,ownerLibrarySync:!0})===!0}catch(i){l.warn("manager",`owner library skills reporter threw group=${r} agent=${p.name}: ${i}`)}if(o){c=!0;continue}l.warn("manager",`owner library skills reporter failed group=${r} agent=${p.name}; trying next`)}if(!c){l.warn("manager",`owner library skills: no reporter succeeded group=${r}`);continue}for(const p of s)try{p.adoptSkillsWireHashFromDisk()}catch(o){l.warn("manager",`adoptSkillsWireHashFromDisk failed for ${p.name}: ${o}`)}}}async stop(){l.info("manager","Stopping all agents..."),this.stopping=!0,this.upgradeChecker?.stop(),this.skillSyncer?.stop(),await Promise.allSettled([...this.shareSyncChains.values()]),this.shareSyncChains.clear();const e=[...this.sharedInstances.values()];this.sharedInstances.clear(),await Promise.allSettled([...this.instances.map(t=>t.stop()),...e.map(t=>t.stop())]),await this.globalConfigStore?.flush(),await this.auditProducer.stop({timeoutMs:5e3}).catch(()=>!1),this.instances=[],U(),l.info("manager","All stopped")}onShareSet(e,t){this.sharedOwnersCache.save(e.name,t);const a=(this.shareSyncChains.get(e.name)??Promise.resolve()).catch(()=>{}).then(()=>this.syncSharedInstances(e,t));this.shareSyncChains.set(e.name,a)}async syncSharedInstances(e,t){if(this.stopping)return;const{toAdd:r,toRemove:a}=de(e.name,this.sharedInstances.keys(),t);for(const c of r){if(this.stopping)break;const s=pe(e.name,c);try{const p=le(e,c),o=new q(p,this.globalConfigStore,this.auditProducer);this.sharedInstances.set(s,o),await o.start(),o.forceRefreshSkills(void 0,{force:!0}),l.info("manager",`shared instance started: ${s}`)}catch(p){this.sharedInstances.delete(s);const o=p instanceof Error?p.message:String(p);/Auth failed/i.test(o)?(this.sharedOwnersCache.remove(e.name,c),l.warn("manager",`shared instance auth rejected, removed from cache: ${s}`)):l.error("manager",`start shared instance failed ${s}: ${p}`)}}for(const{key:c}of a){const s=this.sharedInstances.get(c);s&&(this.sharedInstances.delete(c),s.stop().catch(p=>l.error("manager",`stop shared instance failed ${c}: ${p}`)),l.info("manager",`shared instance stopped: ${c}`))}}restoreCachedSharedInstances(e){const t=this.sharedOwnersCache.load(e.name);t.length!==0&&(l.info("manager",`restoring ${t.length} cached shared instance(s) for ${e.name}`),this.onShareSet(e,t))}getAgentsStatus(){return this.instances.map(e=>e.getStatus())}async addAgent(e){await this.addAgentInternal(e,"agents.json"),this.persistAgentsConfig(),await this.ensureAuxServices(),l.info("manager",`Added agent: ${e.name}`)}async addAgentInternal(e,t){const r=this.toRuntimeInstanceConfig(e);if(this.instances.some(a=>a.name===r.name))throw new Error(`Agent "${r.name}" already exists`);await this.startInstanceFromConfig(r,e,t)}createConfiguredInstance(e){const t=new q(e,this.globalConfigStore,this.auditProducer);return t.setUpgradeTrigger(()=>this.upgradeChecker?.triggerCheck()),t.setInstallAgentHandler(r=>this.installAgent(r)),t.setShareSetHandler(r=>this.onShareSet(e,r)),t.setAgentDeletedHandler(()=>this.onAgentDeletedByPlatform(e.name)),t.setSkillSyncHandler(()=>this.skillSyncer?.triggerSync()),t.setProviderConfigHandler(r=>this.setAgentProvider(e.name,r)),t.setRelayStateApplyPorts({getLocalState:()=>this.relayStateApplier?.getLocalState(e.name)??{enabled:!1},applyEnable:(r,a)=>{const c=this.relayStateApplier;return c?c.applyEnable(e.name,r,a):Promise.reject(new Error("relay state applier is not configured"))},applyDisable:()=>{const r=this.relayStateApplier;return r?r.applyDisable(e.name):Promise.reject(new Error("relay state applier is not configured"))}}),t.setRelayEnvSettledHandler(()=>this.relayEnvSettledHandler?.()),t.setProviderResolver(()=>z(this.getAgentProvider(e.name))),t}async prepareProviderRuntime(e,t){const r=this.resolveAgentProvider(t.provider);F(t.client_type)&&r?.base_url?.trim()&&r?.api_key?.trim()&&r?.model?.trim()&&await K(e.name,{baseUrl:r.base_url.trim(),apiKey:r.api_key.trim(),model:r.model.trim()})}async startInstanceFromConfig(e,t,r){await this.prepareProviderRuntime(e,t);const a=this.createConfiguredInstance(e);await a.start(),this.instances.push(a),this.configMap.set(e.name,e),this.rawConfigMap.set(e.name,{entry:t,file:r}),this.relayEnvSettledHandler?.()}async removeAgent(e){if(!this.instances.some(t=>t.name===e))throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});await this.removeAgentInternal(e),this.persistAgentsConfig(),l.info("manager",`Removed agent: ${e}`),this.relayEnvSettledHandler?.()}async removeAgentInternal(e,t){const r=this.instances.findIndex(p=>p.name===e),a=r===-1?void 0:this.instances[r];r!==-1&&this.instances.splice(r,1),this.configMap.delete(e),this.rawConfigMap.delete(e);const c=this.shareSyncChains.get(e);this.shareSyncChains.delete(e),c&&await c.catch(()=>{});const s=`${e}#shared:`;for(const[p,o]of[...this.sharedInstances.entries()])p.startsWith(s)&&(this.sharedInstances.delete(p),o.stop().catch(i=>l.error("manager",`stop shared instance failed ${p}: ${i}`)));t?.keepShareCache||this.sharedOwnersCache.delete(e),a&&await a.stop(),t?.keepShareCache||await Q(e).catch(()=>{})}onAgentDeletedByPlatform(e){this.stopping||this.deletedAgentCleanups.has(e)||(this.deletedAgentCleanups.add(e),(async()=>{try{if(!(this.rawConfigMap.has(e)||this.instances.some(r=>r.name===e)))return;l.warn("manager",`Agent "${e}" \u5DF2\u5728\u5E73\u53F0\u5220\u9664\uFF0C\u505C\u6B62\u5B9E\u4F8B\u5E76\u6E05\u7406\u672C\u5730\u914D\u7F6E`),await this.removeAgentInternal(e),this.persistAgentsConfig(),l.info("manager",`Agent "${e}" \u5DF2\u5728\u5E73\u53F0\u5220\u9664\uFF0C\u672C\u5730\u914D\u7F6E\u5DF2\u6E05\u7406\uFF08agents.json \u5DF2\u79FB\u9664\u8BE5\u6761\u76EE\uFF09`)}catch(t){l.error("manager",`cleanup for deleted agent "${e}" failed: ${t}`)}finally{this.deletedAgentCleanups.delete(e)}})())}persistAgentsConfig(){const e=b(this.configDir,"agents.json");try{const t=[];for(const[,a]of this.configMap){const c=this.rawConfigMap.get(a.name)?.entry.provider;t.push({name:a.name,ws_url:a.aibot.url,agent_id:a.aibot.agentId,api_key:a.aibot.apiKey,client_type:a.aibot.clientType,...c?{provider:c}:{}})}oe(e,JSON.stringify({agents:t},null,4)+`
|
|
2
|
-
`,"utf-8")}catch(t){l.error("manager",`Failed to persist agents config: ${t}`)}}isAgentBusy(e){return this.instances.find(r=>r.name===e)?.hasPendingWork()??!1}getAgentsWithStaleRelayEnv(){return this.instances.filter(e=>e.hasStaleRelayEnv()).map(e=>e.name)}markAgentRelayEnvStale(e){const t=this.instances.find(r=>r.name===e);return t?(t.markRelayEnvStale(),!0):!1}relayEnvSettledHandler;setRelayEnvSettledHandler(e){this.relayEnvSettledHandler=e}relayStateApplier;setRelayStateApplier(e){this.relayStateApplier=e}reportRelayStateLocalChange(e){this.instances.find(r=>r.name===e)?.reportRelayStateLocalChange()}setRelaySecretResolver(e){this.relaySecretResolver=e}resolveAgentProvider(e){if(!e?.api_key_ref?.trim())return e;const t=e.api_key_ref.trim(),r=this.relaySecretResolver?.(t);if(!r)throw Object.assign(new Error(`relay credential "${t}" is unavailable`),{code:"RELAY_SECRET_UNAVAILABLE"});return{...e,api_key:r}}toRuntimeInstanceConfig(e,t={}){const r=this.resolveAgentProvider(e.provider);return
|
|
1
|
+
import{readFileSync as se,readdirSync as ie,writeFileSync as oe}from"node:fs";import{join as A}from"node:path";import{homedir as ce}from"node:os";import{AgentInstance as N}from"./bridge/bridge.js";import{buildSharedInstanceConfig as le,diffSharedOwners as de,sharedInstanceKey as pe,SharedOwnersCache as me}from"./manager-share-config.js";import{isAgentDeletedError as q}from"./core/aibot/errors.js";import{GRIX_PATHS as w,log as l}from"./core/log/index.js";import{resolveClientVersion as ue}from"./core/util/client-version.js";import{UpgradeChecker as ge}from"./core/upgrade/upgrade-checker.js";import{SkillSyncer as fe}from"./core/skill-sync/skill-syncer.js";import{AgentGlobalConfigStore as he}from"./core/persistence/agent-global-config-store.js";import{resolveSkillScanMode as _e}from"./adapter/claude/skill-scanner.js";import{buildWireSkills as ye}from"./core/skill-sync/sync-state.js";import{buildLibrarySkills as we}from"./core/skill-sync/library-skills.js";import{AUDIT_LOCAL_ACTION_TYPES as ke}from"./core/aibot/types.js";import{buildReportedSkills as Ae,logDefaultSkillsCheck as Se,cleanupProjectedSkills as U}from"./default-skills/index.js";import{buildProviderEnv as be,supportsKiroCwRelay as F,supportsNonMitmRelayConfig as ve}from"./core/config/provider-env.js";import{resolveHermesHomeDir as Ce}from"./adapter/shared/native-provider-config.js";import{ensureKiroRelay as W,stopKiroRelay as K}from"./core/proxy/kiro-relay.js";import{resolveCopilotCommand as Q}from"./core/runtime/copilot-resolve.js";import{resolveCursorCommand as G}from"./core/runtime/cursor-resolve.js";import{getCliVersion as xe,resolveCliPath as $e}from"./core/util/cli-probe.js";import{AgentInstaller as Ie}from"./core/installer/installer.js";import{reportInstallFailure as Ee}from"./core/observability/sentry.js";import{createDefaultAuditProducer as Te}from"./audit/runtime/default-audit-recorder.js";import{normalizeCodexConfigOverrides as Pe}from"./adapter/codex/config-overrides.js";import{resolveCodexDirectHome as Me}from"./adapter/codex/direct-home.js";function z(n){if(n)return{...n.provider_id?.trim()?{providerId:n.provider_id.trim()}:{},...n.base_url?.trim()?{baseUrl:n.base_url.trim()}:{},...n.quota_base_url?.trim()?{quotaBaseUrl:n.quota_base_url.trim()}:{},...n.api_key?.trim()?{apiKey:n.api_key.trim()}:{},...n.model?.trim()?{model:n.model.trim()}:{},...n.fast_model?.trim()?{fastModel:n.fast_model.trim()}:{},...n.effort?.trim()?{effort:n.effort.trim()}:{}}}const je=8e3;function Oe(){const n=Q();return[{clientType:"openclaw",command:"openclaw"},{clientType:"claude",command:"claude"},{clientType:"codex",command:"codex"},{clientType:"qwen",command:"qwen"},{clientType:"hermes",command:"hermes"},{clientType:"reasonix",command:"reasonix"},{clientType:"codewhale",command:"codewhale"},{clientType:"opencode",command:"opencode"},{clientType:"cursor",command:G()},{clientType:"pi",command:"pi"},{clientType:"openhuman",command:"openhuman-core"},{clientType:"kiro",command:"kiro-cli"},{clientType:"kimi",command:"kimi"},{clientType:"copilot",command:n.command},{clientType:"agy",command:"agy"},{clientType:"deepseek",command:"dsh"}]}async function Re(){return Promise.all(Oe().map(async n=>{const e=await $e(n.command);if(!e)return{client_type:n.clientType,command:n.command,installed:!1,path:null,version:null};const t=await xe(e,n.versionArgs??["--version"]);return{client_type:n.clientType,command:n.command,installed:!0,path:e,version:t.version,error:t.error}}))}function De(n){switch(n){case"claude":return{adapterType:"claude",command:"claude"};case"codex":return{adapterType:"codex",command:"codex",options:{sandboxMode:"danger-full-access"}};case"gemini":return{adapterType:"acp",command:"gemini",autoInjectArgs:{acp:!0},enableSessionBinding:!0};case"qwen":return{adapterType:"acp",command:"qwen",adapterHint:"qwen/base",autoInjectArgs:{acp:!0},enableSessionBinding:!0};case"pi":return{adapterType:"pi",command:"pi"};case"cursor":return{adapterType:"cursor",command:G()};case"reasonix":return{adapterType:"acp",command:"reasonix",args:["acp"],enableSessionBinding:!0};case"codewhale":return{adapterType:"codewhale",command:"codewhale",enableSessionBinding:!0};case"openhuman":return{adapterType:"openhuman",command:"openhuman-core",enableSessionBinding:!0};case"kiro":return{adapterType:"acp",command:"kiro-cli",args:["acp"],enableSessionBinding:!0};case"kimi":return{adapterType:"acp",command:"kimi",args:["acp"],options:{initial_mode:"yolo"},enableSessionBinding:!0};case"opencode":return{adapterType:"opencode",command:"opencode",args:["serve"],enableSessionBinding:!0};case"copilot":{const e=Q();return{adapterType:"acp",command:e.command,args:[...e.prefixArgs,"--acp"],enableSessionBinding:!0}}case"agy":return{adapterType:"agy",command:"agy",enableSessionBinding:!0};case"deepseek":return{adapterType:"deepseek-harness",command:"dsh",adapterHint:"deepseek/grix-bridge-v1",options:{integrationMode:"profile_bridge",dshProfile:"web",autoInstallBridge:!0,autoStartProfile:!0},enableSessionBinding:!0};case"hermes":return{adapterType:"acp",command:"hermes",args:["acp"],enableSessionBinding:!0};default:throw new Error(`Unsupported client_type: ${n}`)}}function b(n,e){return String(n??"").trim().toLowerCase().replace(/[^a-z0-9._-]+/g,"-").replace(/-+/g,"-").replace(/^-|-$/g,"")||String(e??"").trim().toLowerCase().replace(/[^a-z0-9._-]+/g,"-").replace(/-+/g,"-").replace(/^-|-$/g,"")||"default"}function Le(n){return A(w.data,`session-bindings-${b(n,"unknown")}.json`)}function He(n,e){return A(w.data,`session-bindings-${b(n,e)}.json`)}function Be(n,e){return A(w.data,`active-events-${b(n,e)}.json`)}function Ne(n){return A(w.data,`pending-events-${b(n,"unknown")}.json`)}function qe(n,e){const t=b(n,"agent"),r=b(e,"unknown");return A(w.data,`terminal-outbox-${t}-${r}.json`)}function Ue(...n){const e=[],t=new Set;for(const r of n)for(const a of r??[]){const c=String(a??"").trim(),i=c.toLowerCase();!c||t.has(i)||(t.add(i),e.push(c))}return e.length>0?e:void 0}function Fe(n,e){const t={claude:{maxConcurrent:1,maxQueued:5,queueTimeoutMs:0},codex:{maxConcurrent:1,maxQueued:5,queueTimeoutMs:0},cursor:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0},acp:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0},pi:{maxConcurrent:1,maxQueued:5,queueTimeoutMs:0},codewhale:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0},openhuman:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0},opencode:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0},agy:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0},"deepseek-harness":{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0}},r=t[n]??t.acp;return{maxConcurrent:e?.max_concurrent??r.maxConcurrent??1,maxQueued:e?.max_queued??r.maxQueued??3,queueTimeoutMs:e?.queue_timeout_ms??r.queueTimeoutMs??0,cancelableQueued:!0,cancelableRunning:!0}}function We(n,e={}){const t=ue(),r=String(n.client_type??"").trim().toLowerCase(),a=De(r),c=r==="deepseek"?n.dsh?.integration_mode??"profile_bridge":void 0,i=c==="embedded_jsonrpc",m=String(n.ws_url??"").trim(),o="get_session_usage",s="get_rate_limits",f="get_agent_global_config",g=ke;if(!n.name?.trim())throw new Error("agent name is required");if(!m)throw new Error(`agent ${n.name}: ws_url is required`);if(!n.agent_id?.trim())throw new Error(`agent ${n.name}: agent_id is required`);if(!n.api_key?.trim())throw new Error(`agent ${n.name}: api_key is required`);const d=a.adapterType,p=d==="acp",y=r==="qwen",u={...a.options??{}};if(d==="deepseek-harness"&&(u.integrationMode=c,u.dshProfile=n.dsh?.profile?.trim()||"web",u.autoInstallBridge=n.dsh?.auto_install_bridge??!i,u.autoStartProfile=n.dsh?.auto_start_profile??!i),d==="deepseek-harness"&&n.provider?.model?.trim()&&(u.defaultModel=n.provider.model.trim()),n.codex_config_overrides!==void 0){if(d!=="codex")throw new Error(`agent ${n.name}: codex_config_overrides is only supported for client_type=codex`);Pe(n.codex_config_overrides,`agent ${n.name}: codex_config_overrides`),u.codexConfigOverrides=n.codex_config_overrides}if(d==="codex"&&n.provider?.wire_api!==void 0){const h=n.provider;if(h.wire_api!=="responses"||!h.base_url?.trim()||!h.api_key?.trim()||!h.model?.trim()||!h.catalog_version?.trim()||!h.catalog_sha256?.trim()||!h.catalog_json?.trim())throw Object.assign(new Error(`agent ${n.name}: incomplete Codex direct relay config`),{code:"INVALID_CODEX_DIRECT_CONFIG"});const B=Me(w.data,n.name);u.codexHome=B,u.codexDirect={codexHome:B,config:{baseUrl:h.base_url.trim(),model:h.model.trim(),...h.reasoning_effort?.trim()?{reasoningEffort:h.reasoning_effort.trim()}:{},token:h.api_key.trim()},catalog:{version:h.catalog_version.trim(),sha256:h.catalog_sha256.trim(),json:h.catalog_json}}}const _=d==="codex"?{capabilities:["local_action_v1","agent_invoke"],localActions:["session_control","get_context","set_model","set_mode","set_reasoning_effort","set_service_tier","set_sandbox_mode","exec_approve","exec_reject","file_list","create_folder","turn_interrupt","permission_approve","permission_reject","thread_compact",o,s]}:null,S=d==="claude"?{localActions:["session_control","set_mode","set_model","set_reasoning_effort","claude_interaction_reply","exec_approve","exec_reject","file_list","create_folder","thread_compact",o,s]}:null,k=y?{capabilities:["stream_chunk","local_action_v1"],localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","file_list","create_folder",o],adapterHint:"qwen/base"}:null,$=d==="pi"?{adapterHint:"pi/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","get_context","file_list","create_folder",o,s]}:null,I=d==="openhuman"?{adapterHint:"openhuman/base",capabilities:["local_action_v1"],localActions:["session_control","set_model","file_list","create_folder"]}:null,E=d==="cursor"?{adapterHint:"cursor/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","set_mode","get_context","file_list","create_folder","thread_compact","claude_interaction_reply",s]}:null,O=d==="codewhale"?{capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","file_list","create_folder",o]}:null,T=d==="opencode"?{adapterHint:"opencode/base",capabilities:["stream_chunk","local_action_v1"],localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","claude_interaction_reply","file_list","create_folder"]}:null,P=d==="agy"?{adapterHint:"agy/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","file_list","create_folder",s]}:null,M=d==="deepseek-harness"?{adapterHint:i?"deepseek/jsonrpc-v1":"deepseek/grix-bridge-v1",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_provider","set_model","set_mode","set_thinking","set_reasoning_effort","set_preset","set_profile","create_profile",o,s,"exec_approve","exec_reject","permission_approve","permission_reject","file_list","create_folder","dsh_bridge_status","dsh_install_bridge","dsh_list_plugins","dsh_enable_plugin","dsh_disable_plugin","dsh_refresh_plugins","dsh_list_skills","dsh_enable_skill","dsh_disable_skill","dsh_list_mcp","dsh_list_tools","dsh_list_jobs","dsh_list_goals"]}:null,R=new Set(["hermes","reasonix","kimi","copilot"]),J=p&&!R.has(r),X=p&&!y?{localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","claude_interaction_reply","file_list","create_folder",...J?[o]:[]]}:null,Y=r==="kiro"?{localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","file_list","create_folder","thread_compact",o,s]}:null,Z=d==="codex"||d==="claude"||d==="opencode"||d==="deepseek-harness"||r==="gemini"?["session_control","set_model","set_mode",...d==="deepseek-harness"?["set_provider","set_thinking","set_reasoning_effort","set_preset","set_profile","create_profile","dsh_list_plugins","dsh_enable_plugin","dsh_disable_plugin","dsh_refresh_plugins","dsh_list_skills","dsh_enable_skill","dsh_disable_skill"]:[],...d==="claude"?["set_reasoning_effort"]:[]]:void 0,ee=d==="cursor"||d==="opencode"||d==="openhuman"||d==="agy"||p&&R.has(r)?[s]:[o,s];p&&u.raw_transport===void 0&&(u.raw_transport=r==="gemini");const te=`${r}/base`,C=_e(d,r),ne=C==="kiro"?void 0:process.cwd(),j=z(n.provider),v={...process.env,...n.env};if(C==="hermes"&&!v.HERMES_HOME?.trim()){const h=be(r,j);v.HERMES_HOME=Ce({...h,...v},n.name)}const D=Ae({mode:C,projectDir:ne,env:v}),L=D.filter(h=>h.source==="connector").map(h=>h.name);L.length>0&&!e.quiet&&l.info("manager",`[${n.name}] injecting connector skills: [${L.join(", ")}]`);const H=ye(D,w.skills);let re=H.length>0?H:void 0;const ae=we({skillsDir:w.skills,mode:C,home:ce(),env:v});return{name:n.name,adapterType:d,aibot:{url:m,agentId:n.agent_id,apiKey:n.api_key,clientType:r,clientVersion:t,adapterHint:M?.adapterHint??a.adapterHint??k?.adapterHint??$?.adapterHint??I?.adapterHint??E?.adapterHint??T?.adapterHint??P?.adapterHint??te,capabilities:M?.capabilities??_?.capabilities??O?.capabilities??$?.capabilities??I?.capabilities??E?.capabilities??T?.capabilities??P?.capabilities??k?.capabilities??["stream_chunk","local_action_v1","connector_upgrade"],localActions:Ue(M?.localActions??_?.localActions??O?.localActions??S?.localActions??$?.localActions??I?.localActions??E?.localActions??T?.localActions??P?.localActions??k?.localActions??Y?.localActions??X?.localActions??["exec_approve","exec_reject"],Z,ee,["connector_rollback","connector_upgrade_push",f,"configure_gateway_provider","skill_upload","skill_delete","skill_enable","skill_disable","skill_refresh",...g]),skills:re,librarySkills:ae,terminalOutboxPath:qe(n.name,n.agent_id)},agent:{command:i?"dsh-jsonrpc-agent":a.command,args:[...a.args??[],...n.args??[]],env:n.env,clientType:r,...j?{provider:j}:{}},adapterOptions:u,acpAuthMethod:u.auth_method,acpInitialMode:u.initial_mode,acpMcpTools:u.acp_mcp_tools,promptTimeoutMs:n.prompt_timeout_ms,bindingsPath:Le(n.agent_id),legacyBindingsPath:He(n.name,n.agent_id),activeEventStorePath:Be(n.name,n.agent_id),pendingEventStorePath:Ne(n.agent_id),...a.enableSessionBinding||p?{enableSessionBinding:!0}:{},...a.autoInjectArgs||r==="reasonix"&&n.provider?.model?.trim()?{autoInjectArgs:{...a.autoInjectArgs??{},...r==="reasonix"&&n.provider?.model?.trim()?{model:n.provider.model.trim()}:{}}}:{},poolMaxSize:n.pool?.maxSize??(d==="deepseek-harness"?4:void 0),poolIdleTimeoutMs:n.pool?.idleTimeoutMs,eventQueue:Fe(d,n.event_queue),logDir:w.log,providerBaseUrl:n.provider_base_url?.trim()||void 0,providerApiKey:n.provider_api_key?.trim()||void 0}}function Ke(){const n=process.env.GRIX_AGENT_STARTUP_WAIT_MS,e=Number(n);return Number.isFinite(e)&&e>=500?Math.floor(e):je}function V(n){const e=[],t=[],r=[],a=ie(n).filter(c=>c.endsWith(".json")).sort();for(const c of a)try{const i=JSON.parse(se(A(n,c),"utf-8"));if(i===null||typeof i!="object"||Array.isArray(i)||!("agents"in i)){r.push(c);continue}if(!Array.isArray(i.agents)){t.push({file:c,error:"agents is not an array"});continue}if(i.agents.length===0){t.push({file:c,error:"agents array is empty"});continue}for(const m of i.agents)e.push({entry:m,file:c})}catch(i){t.push({file:c,error:i instanceof Error?i.message:String(i)})}return{entries:e,fileErrors:t,skipped:r}}function x(n){if(n===null||typeof n!="object")return JSON.stringify(n)??"null";if(Array.isArray(n))return`[${n.map(x).join(",")}]`;const e=n;return`{${Object.keys(e).sort().map(r=>`${JSON.stringify(r)}:${x(e[r])}`).join(",")}}`}function Qe(n,e){return x(n)===x(e)}class St{instances=[];configMap=new Map;rawConfigMap=new Map;reloadChain=Promise.resolve();sharedInstances=new Map;shareSyncChains=new Map;stopping=!1;lifecycleAdmissionClosed=!1;deletedAgentCleanups=new Set;upgradeChecker=null;skillSyncer=null;globalConfigStore;configDir=w.config;sharedOwnersCache=new me(w.data);installer=new Ie;relaySecretResolver;auditProducer=Te();async start(e){const t=e??w.config;this.configDir=t,l.info("manager",`Loading configs from ${t}`),Se(),U(),this.globalConfigStore=new he(A(w.data,"agent-global-configs.json")),this.globalConfigStore.load();const{entries:r,fileErrors:a,skipped:c}=V(t);for(const s of a)l.error("manager",`Failed to load ${s.file}: ${s.error}`);for(const s of c)l.info("manager",`Skipped ${s}: not an agent config (no "agents" key)`);if(r.length===0&&a.length===0&&c.length===0){l.warn("manager",`No config files found in ${t}; daemon idle (waiting for config). Add agents under this directory then run: grix-connector reload`);return}const i=[];let m=a.length;for(const{entry:s,file:f}of r)try{const g=this.toRuntimeInstanceConfig(s);i.push({config:g,entry:s,file:f}),l.info("manager",`Loaded ${g.name} (${g.adapterType??"acp"}) from ${f}`)}catch(g){const d=typeof s?.name=="string"?s.name:"<unknown>";l.error("manager",`Invalid agent config in ${f} (name=${d}): ${g}`),m++}let o=0;if(i.length>0){const s=Ke();l.info("manager",`Starting ${i.length} agent(s), startup wait=${s}ms`);const f=p=>{this.instances=this.instances.filter(y=>y!==p)},g=i.map(({config:p,entry:y,file:u})=>{const _=this.createConfiguredInstance(p);return this.instances.push(_),this.configMap.set(p.name,p),this.rawConfigMap.set(p.name,{entry:y,file:u}),{config:p,instance:_,startPromise:this.prepareProviderRuntime(p,y).then(()=>_.start())}}),d=await Promise.all(g.map(async p=>{const y=await new Promise(u=>{let _=!1;const S=setTimeout(()=>{_||(_=!0,u({kind:"timeout"}))},s);p.startPromise.then(()=>{_||(_=!0,clearTimeout(S),u({kind:"started"}))}).catch(k=>{_||(_=!0,clearTimeout(S),u({kind:"failed",error:k}))})});return{task:p,outcome:y}}));for(const{task:p,outcome:y}of d){if(y.kind==="started"){this.restoreCachedSharedInstances(p.config);continue}if(y.kind==="failed"){if(q(y.error)){this.onAgentDeletedByPlatform(p.config.name);continue}f(p.instance),l.error("manager",`Failed to start ${p.config.name}: ${y.error}`);continue}o++,l.warn("manager",`Startup pending for ${p.config.name}, continue retrying in background`),p.startPromise.then(()=>{l.info("manager",`Delayed start succeeded: ${p.config.name}`),this.restoreCachedSharedInstances(p.config)}).catch(u=>{if(q(u)){this.onAgentDeletedByPlatform(p.config.name);return}f(p.instance),l.error("manager",`Delayed start failed: ${p.config.name}: ${u}`)})}if(this.instances.length>0){const p=Math.max(0,this.instances.length-o);l.info("manager",`${p}/${i.length} agent(s) running now`)}o>0&&l.warn("manager",`${o} agent(s) still connecting in background`)}if(this.instances.length===0&&i.length>0)throw new Error("All agent configurations failed to start");await this.ensureAuxServices()}async ensureAuxServices(){if(this.configMap.size===0)return;const e=[...this.configMap.values()].map(t=>({apiKey:t.aibot.apiKey,wsUrl:t.aibot.url}));this.upgradeChecker||(this.upgradeChecker=new ge(e,()=>this.hasAnyPendingOrBackgroundWork(),()=>this.closeAllLifecycleAdmission()),await this.upgradeChecker.start()),this.skillSyncer||(this.skillSyncer=new fe(e,w.skills),this.skillSyncer.setOnSyncSuccess(()=>this.refreshOwnerLibrarySkills()),await this.skillSyncer.start())}refreshOwnerLibrarySkills(){const e=new Map,t=r=>{let a;try{a=r.skillsSyncGroupKey()}catch(i){l.warn("manager",`skillsSyncGroupKey failed for ${r.name}: ${i}`);return}const c=e.get(a)??[];c.push(r),e.set(a,c)};for(const r of this.instances)t(r);for(const r of this.sharedInstances.values())t(r);for(const[r,a]of e){let c=!1;const i=[];for(const m of a){if(c){i.push(m);continue}let o=!1;try{o=m.forceRefreshSkills(void 0,{force:!0,ownerLibrarySync:!0})===!0}catch(s){l.warn("manager",`owner library skills reporter threw group=${r} agent=${m.name}: ${s}`)}if(o){c=!0;continue}l.warn("manager",`owner library skills reporter failed group=${r} agent=${m.name}; trying next`)}if(!c){l.warn("manager",`owner library skills: no reporter succeeded group=${r}`);continue}for(const m of i)try{m.adoptSkillsWireHashFromDisk()}catch(o){l.warn("manager",`adoptSkillsWireHashFromDisk failed for ${m.name}: ${o}`)}}}async stop(){l.info("manager","Stopping all agents..."),this.stopping=!0,this.upgradeChecker?.stop(),this.skillSyncer?.stop(),await Promise.allSettled([...this.shareSyncChains.values()]),this.shareSyncChains.clear();const e=[...this.sharedInstances.values()];this.sharedInstances.clear(),await Promise.allSettled([...this.instances.map(t=>t.stop()),...e.map(t=>t.stop())]),await this.globalConfigStore?.flush(),await this.auditProducer.stop({timeoutMs:5e3}).catch(()=>!1),this.instances=[],U(),l.info("manager","All stopped")}onShareSet(e,t){this.sharedOwnersCache.save(e.name,t);const a=(this.shareSyncChains.get(e.name)??Promise.resolve()).catch(()=>{}).then(()=>this.syncSharedInstances(e,t));this.shareSyncChains.set(e.name,a)}async syncSharedInstances(e,t){if(this.stopping)return;const{toAdd:r,toRemove:a}=de(e.name,this.sharedInstances.keys(),t);for(const c of r){if(this.stopping)break;const i=pe(e.name,c);try{const m=le(e,c),o=new N(m,this.globalConfigStore,this.auditProducer);o.setLifecycleBusyChecker(()=>this.hasAnyPendingOrBackgroundWork()),o.setLifecycleAdmissionCloser(()=>this.closeAllLifecycleAdmission()),this.lifecycleAdmissionClosed&&o.closeLifecycleAdmission(),this.sharedInstances.set(i,o),await o.start(),o.forceRefreshSkills(void 0,{force:!0}),l.info("manager",`shared instance started: ${i}`)}catch(m){this.sharedInstances.delete(i);const o=m instanceof Error?m.message:String(m);/Auth failed/i.test(o)?(this.sharedOwnersCache.remove(e.name,c),l.warn("manager",`shared instance auth rejected, removed from cache: ${i}`)):l.error("manager",`start shared instance failed ${i}: ${m}`)}}for(const{key:c}of a){const i=this.sharedInstances.get(c);i&&(await i.stop().catch(m=>l.error("manager",`stop shared instance failed ${c}: ${m}`)),this.sharedInstances.get(c)===i&&this.sharedInstances.delete(c),l.info("manager",`shared instance stopped: ${c}`))}}restoreCachedSharedInstances(e){const t=this.sharedOwnersCache.load(e.name);t.length!==0&&(l.info("manager",`restoring ${t.length} cached shared instance(s) for ${e.name}`),this.onShareSet(e,t))}getAgentsStatus(){return this.instances.map(e=>e.getStatus())}async addAgent(e){await this.addAgentInternal(e,"agents.json"),this.persistAgentsConfig(),await this.ensureAuxServices(),l.info("manager",`Added agent: ${e.name}`)}async addAgentInternal(e,t){const r=this.toRuntimeInstanceConfig(e);if(this.instances.some(a=>a.name===r.name))throw new Error(`Agent "${r.name}" already exists`);await this.startInstanceFromConfig(r,e,t)}createConfiguredInstance(e){const t=new N(e,this.globalConfigStore,this.auditProducer);return t.setUpgradeTrigger(()=>this.upgradeChecker?.triggerCheck()),t.setLifecycleBusyChecker(()=>this.hasAnyPendingOrBackgroundWork()),t.setLifecycleAdmissionCloser(()=>this.closeAllLifecycleAdmission()),this.lifecycleAdmissionClosed&&t.closeLifecycleAdmission(),t.setInstallAgentHandler(r=>this.installAgent(r)),t.setShareSetHandler(r=>this.onShareSet(e,r)),t.setAgentDeletedHandler(()=>this.onAgentDeletedByPlatform(e.name)),t.setSkillSyncHandler(()=>this.skillSyncer?.triggerSync()),t.setProviderConfigHandler(r=>this.setAgentProvider(e.name,r)),t.setRelayStateApplyPorts({getLocalState:()=>this.relayStateApplier?.getLocalState(e.name)??{enabled:!1},applyEnable:(r,a)=>{const c=this.relayStateApplier;return c?c.applyEnable(e.name,r,a):Promise.reject(new Error("relay state applier is not configured"))},applyDisable:()=>{const r=this.relayStateApplier;return r?r.applyDisable(e.name):Promise.reject(new Error("relay state applier is not configured"))}}),t.setRelayEnvSettledHandler(()=>this.relayEnvSettledHandler?.()),t.setProviderResolver(()=>z(this.getAgentProvider(e.name))),t}hasAnyPendingOrBackgroundWork(){return this.instances.some(e=>e.hasPendingOrBackgroundWork())||[...this.sharedInstances.values()].some(e=>e.hasPendingOrBackgroundWork())}closeAllLifecycleAdmission(){this.lifecycleAdmissionClosed=!0;for(const e of this.instances)e.closeLifecycleAdmission();for(const e of this.sharedInstances.values())e.closeLifecycleAdmission()}async prepareProviderRuntime(e,t){const r=this.resolveAgentProvider(t.provider);F(t.client_type)&&r?.base_url?.trim()&&r?.api_key?.trim()&&r?.model?.trim()&&await W(e.name,{baseUrl:r.base_url.trim(),apiKey:r.api_key.trim(),model:r.model.trim()})}async startInstanceFromConfig(e,t,r){await this.prepareProviderRuntime(e,t);const a=this.createConfiguredInstance(e);this.instances.push(a);try{await a.start(),this.configMap.set(e.name,e),this.rawConfigMap.set(e.name,{entry:t,file:r})}catch(c){throw this.instances=this.instances.filter(i=>i!==a),c}this.relayEnvSettledHandler?.()}async removeAgent(e){if(!this.instances.some(t=>t.name===e))throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});await this.removeAgentInternal(e),this.persistAgentsConfig(),l.info("manager",`Removed agent: ${e}`),this.relayEnvSettledHandler?.()}async removeAgentInternal(e,t){const r=this.instances.findIndex(s=>s.name===e),a=r===-1?void 0:this.instances[r];this.configMap.delete(e),this.rawConfigMap.delete(e);const c=this.shareSyncChains.get(e);this.shareSyncChains.delete(e),c&&await c.catch(()=>{});const i=t?.keepShareCache?void 0:{dropPendingEvents:!0},m=`${e}#shared:`,o=[];for(const[s,f]of[...this.sharedInstances.entries()])s.startsWith(m)&&o.push(f.stop(i).catch(g=>l.error("manager",`stop shared instance failed ${s}: ${g}`)).then(()=>{this.sharedInstances.get(s)===f&&this.sharedInstances.delete(s)}));if(t?.keepShareCache||this.sharedOwnersCache.delete(e),a)try{await a.stop(i)}finally{const s=this.instances.indexOf(a);s!==-1&&this.instances.splice(s,1)}await Promise.all(o),t?.keepShareCache||await K(e).catch(()=>{})}onAgentDeletedByPlatform(e){this.stopping||this.deletedAgentCleanups.has(e)||(this.deletedAgentCleanups.add(e),(async()=>{try{if(!(this.rawConfigMap.has(e)||this.instances.some(r=>r.name===e)))return;l.warn("manager",`Agent "${e}" \u5DF2\u5728\u5E73\u53F0\u5220\u9664\uFF0C\u505C\u6B62\u5B9E\u4F8B\u5E76\u6E05\u7406\u672C\u5730\u914D\u7F6E`),await this.removeAgentInternal(e),this.persistAgentsConfig(),l.info("manager",`Agent "${e}" \u5DF2\u5728\u5E73\u53F0\u5220\u9664\uFF0C\u672C\u5730\u914D\u7F6E\u5DF2\u6E05\u7406\uFF08agents.json \u5DF2\u79FB\u9664\u8BE5\u6761\u76EE\uFF09`)}catch(t){l.error("manager",`cleanup for deleted agent "${e}" failed: ${t}`)}finally{this.deletedAgentCleanups.delete(e)}})())}persistAgentsConfig(){const e=A(this.configDir,"agents.json");try{const t=[];for(const[,a]of this.configMap){const c=this.rawConfigMap.get(a.name)?.entry.provider;t.push({name:a.name,ws_url:a.aibot.url,agent_id:a.aibot.agentId,api_key:a.aibot.apiKey,client_type:a.aibot.clientType,...c?{provider:c}:{}})}oe(e,JSON.stringify({agents:t},null,4)+`
|
|
2
|
+
`,"utf-8")}catch(t){l.error("manager",`Failed to persist agents config: ${t}`)}}isAgentBusy(e){return this.instances.find(r=>r.name===e)?.hasPendingWork()??!1}getAgentsWithStaleRelayEnv(){return this.instances.filter(e=>e.hasStaleRelayEnv()).map(e=>e.name)}markAgentRelayEnvStale(e){const t=this.instances.find(r=>r.name===e);return t?(t.markRelayEnvStale(),!0):!1}relayEnvSettledHandler;setRelayEnvSettledHandler(e){this.relayEnvSettledHandler=e}relayStateApplier;setRelayStateApplier(e){this.relayStateApplier=e}reportRelayStateLocalChange(e){this.instances.find(r=>r.name===e)?.reportRelayStateLocalChange()}setRelaySecretResolver(e){this.relaySecretResolver=e}resolveAgentProvider(e){if(!e?.api_key_ref?.trim())return e;const t=e.api_key_ref.trim(),r=this.relaySecretResolver?.(t);if(!r)throw Object.assign(new Error(`relay credential "${t}" is unavailable`),{code:"RELAY_SECRET_UNAVAILABLE"});return{...e,api_key:r}}toRuntimeInstanceConfig(e,t={}){const r=this.resolveAgentProvider(e.provider);return We(r===e.provider?e:{...e,provider:r},t)}getAgentProvider(e){return this.resolveAgentProvider(this.rawConfigMap.get(e)?.entry.provider)}async restartAgent(e){const t=this.rawConfigMap.get(e);if(!t)throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});await this.replaceInstance(e,t.entry,t.file),l.info("manager",`Restarted agent: ${e}`)}async setAgentProvider(e,t,r){const a=this.rawConfigMap.get(e);if(!a)throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});const c=String(a.entry.client_type??"").trim().toLowerCase();F(c)&&(t?.base_url?.trim()&&t?.api_key?.trim()&&t?.model?.trim()?await W(e,{baseUrl:t.base_url.trim(),apiKey:t.api_key.trim(),model:t.model.trim()}):await K(e));const i=t?.api_key_ref?.trim()?Object.fromEntries(Object.entries(t).filter(([o])=>o!=="api_key")):t,m={...a.entry,provider:i};if(r?.deferRestart){this.rawConfigMap.set(e,{...a,entry:m}),this.instances.find(o=>o.name===e)?.markRelayEnvStale(),this.persistAgentsConfig(),l.info("manager",`Updated provider config for agent (deferred restart): ${e}`);return}await this.replaceInstance(e,m,a.file),this.persistAgentsConfig(),l.info("manager",`Updated provider config for agent: ${e}`)}hasDirectRelayConfig(e){const t=this.rawConfigMap.get(e)?.entry.provider;return!!(t?.api_key_ref?.trim()||t?.api_key?.trim())}getDirectRelayConfigAgentNames(){return[...this.rawConfigMap.entries()].filter(([,e])=>!!e.entry.provider&&(ve(e.entry.client_type)||["claude","codex"].includes(String(e.entry.client_type??"").trim().toLowerCase()))).map(([e])=>e)}async fetchRelayCredential(e,t){const r=this.instances.find(a=>a.name===e);if(!r)throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});return r.fetchRelayCredential(t)}async clearDirectProviderConfigs(){const e=this.getDirectRelayConfigAgentNames(),t=[];for(const r of e)try{await this.setAgentProvider(r,void 0,{deferRestart:this.isAgentBusy(r)}),t.push(r)}catch(a){l.warn("manager",`Failed to clear provider config for "${r}": ${a instanceof Error?a.message:String(a)}`)}return t}async replaceInstance(e,t,r){const a=this.toRuntimeInstanceConfig(t);await this.removeAgentInternal(e,{keepShareCache:!0}),await this.startInstanceFromConfig(a,t,r),this.restoreCachedSharedInstances(a)}async reload(){const e=this.reloadChain.catch(()=>{}).then(()=>this.doReload());return this.reloadChain=e.catch(()=>{}),e}async doReload(){if(this.stopping)throw Object.assign(new Error("manager is stopping"),{code:"RELOAD_UNSAFE"});const{entries:e,fileErrors:t,skipped:r}=V(this.configDir);for(const o of r)l.info("manager",`reload: skipped ${o}: not an agent config (no "agents" key)`);if(t.length>0){const o=t.map(s=>`${s.file}: ${s.error}`).join("; ");throw Object.assign(new Error(`reload aborted: ${t.length} config file(s) failed to parse [${o}]`),{code:"RELOAD_UNSAFE"})}if(e.length===0)throw Object.assign(new Error("reload aborted: no valid agent config found"),{code:"RELOAD_UNSAFE"});const a=[];for(const{entry:o}of e)try{this.toRuntimeInstanceConfig(o,{quiet:!0})}catch(s){const f=o?.name,g=typeof f=="string"&&f.trim()?f:"<unknown>";a.push({name:g,error:s instanceof Error?s.message:String(s)})}if(a.length>0){const o=a.map(s=>`${s.name}: ${s.error}`).join("; ");throw Object.assign(new Error(`reload aborted: ${a.length} agent config(s) invalid [${o}]`),{code:"RELOAD_UNSAFE"})}const c=new Map;for(const o of e){const s=String(o.entry?.name??"").trim();s&&(c.has(s)&&l.warn("manager",`reload: duplicate agent name "${s}", last one wins`),c.set(s,o))}const i={added:[],removed:[],restarted:[],unchanged:[],failed:[]},m=new Set(this.rawConfigMap.keys());for(const o of m)if(!c.has(o))try{await this.removeAgentInternal(o),i.removed.push(o)}catch(s){i.failed.push({name:o,error:s instanceof Error?s.message:String(s)})}for(const[o,s]of c){const f=this.rawConfigMap.get(o);if(f)if(Qe(f.entry,s.entry))this.rawConfigMap.set(o,s),i.unchanged.push(o);else try{await this.replaceInstance(o,s.entry,s.file),i.restarted.push(o)}catch(g){i.failed.push({name:o,error:g instanceof Error?g.message:String(g)})}else try{await this.addAgentInternal(s.entry,s.file),i.added.push(o)}catch(g){i.failed.push({name:o,error:g instanceof Error?g.message:String(g)})}}return l.info("manager",`reload done: +${i.added.length} -${i.removed.length} ~${i.restarted.length} =${i.unchanged.length} !${i.failed.length}`),await this.ensureAuxServices(),i}async checkUpgrade(){return this.upgradeChecker?this.upgradeChecker.checkForUpdate():{available:!1}}triggerUpgrade(){this.upgradeChecker?.triggerCheck()}async probeAll(e={}){return Ge(this.instances,e)}async probeOne(e,t={}){return ze(this.instances,e,t)}listInstallable(){return this.installer.listInstallable()}async installAgent(e){const t=await this.installer.install(e);return Ee(t),t}getInstallProgress(e){return this.installer.getProgress(e)??null}}async function Ge(n,e){const t=e.concurrency??4,r=Date.now(),a=new Array(n.length);await new Promise(s=>{let f=0,g=0;const d=n.length;if(d===0){s();return}function p(_){const S=n[_];S.probe(e).then(k=>{a[_]=k,y()},k=>{a[_]={agent_name:S.name,client_type:"unknown",adapter_type:"acp",ok:!1,status:"error",probed_at:Date.now(),duration_ms:0,cached:!1,cli:{command:"",installed:!1,path:null,version:null,error:{code:"internal",message:k?.message??String(k)}},conversation:{attempted:!1,ok:!1,latency_ms:null},config:{model:null,base_url:null,source:{model:"unknown",base_url:"unknown"}},process:{started:!1,alive:!1,busy:!1}},y()})}function y(){g++,f<d?p(f++):g===d&&s()}const u=Math.min(t,d);for(let _=0;_<u;_++)p(f++)});const c=a.filter(s=>s.status==="healthy").length,i=a.filter(s=>s.status==="degraded").length,m=a.filter(s=>s.status==="unavailable").length,o=await Re();return{ok:c===a.length&&a.length>0,total:a.length,healthy:c,degraded:i,unavailable:m,installed_clients:o,agents:a,probed_at:r,duration_ms:Date.now()-r}}async function ze(n,e,t){const r=n.find(a=>a.name===e);if(!r)throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});return r.probe(t)}export{St as Manager,z as normalizeAgentProviderForRuntime,Re as probeInstalledClientCommands,Ge as probeInstances,ze as probeOneInstance};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import*as i from"node:net";const e={bind:"127.0.0.1",port:0,endpoint:"/mcp",sessionTimeoutMs:18e5,invokeTimeoutMs:3e4};function n(o){const u={bind:o?.bind??e.bind,port:o?.port??e.port,endpoint:o?.endpoint??e.endpoint,sessionTimeoutMs:o?.sessionTimeoutMs??e.sessionTimeoutMs,invokeTimeoutMs:o?.invokeTimeoutMs??e.invokeTimeoutMs,allowedOrigins:o?.allowedOrigins,allowedHosts:o?.allowedHosts};return s(u.bind),u.port!==0&&t(u.port),r(u.sessionTimeoutMs),u}function s(o){if(!o||!i.isIPv4(o)&&!i.isIPv6(o))throw new Error(`\u914D\u7F6E\u6821\u9A8C\u5931\u8D25: bind \u5730\u5740 "${o}" \u4E0D\u662F\u5408\u6CD5\u7684 IPv4 \u6216 IPv6 \u5730\u5740`)}function t(o){if(!Number.isInteger(o)||o<1||o>65535)throw new Error(`\u914D\u7F6E\u6821\u9A8C\u5931\u8D25: port \u503C ${o} \u4E0D\u5728\u5408\u6CD5\u8303\u56F4 1-65535 \u5185\u6216\u4E0D\u662F\u6574\u6570`)}function r(o){if(!Number.isInteger(o)||o<1e3||o>864e5)throw new Error(`\u914D\u7F6E\u6821\u9A8C\u5931\u8D25: session_timeout_ms \u503C ${o} \u4E0D\u5728\u5408\u6CD5\u8303\u56F4 1000-86400000 \u5185`)}export{n as createDefaultGatewayConfig};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const r=3e4;class c{connectionManager;onDisconnected;bindings=new Map;constructor(n,i){this.connectionManager=n,this.onDisconnected=i}async bind(n,i){if(this.bindings.has(n))throw new Error(`Session ${n} is already bound to a connection`);const e=await this.connectWithTimeout(i),t=[],s=e.onDisconnected(()=>{this.removeBinding(n),this.onDisconnected(n)});return t.push(s),this.bindings.set(n,{sessionId:n,handle:e,subscriptions:t}),e}getHandle(n){return this.bindings.get(n)?.handle}unbind(n){const i=this.bindings.get(n);if(i){this.bindings.delete(n);for(const e of i.subscriptions)e();i.handle.disconnect()}}unbindAll(){const n=[...this.bindings.keys()];for(const i of n)this.unbind(i)}connectWithTimeout(n){return new Promise((i,e)=>{let t=!1;const s=setTimeout(()=>{t||(t=!0,e(new Error("Connection bind timeout after 30000ms")))},3e4);this.connectionManager.connect({agentId:n.agentId,apiKey:n.apiKey,url:n.wsUrl,clientType:n.clientType,capabilities:["agent_invoke"],adapterHint:`${n.clientType}/base`},{maxRetries:0}).then(o=>{t?o.disconnect():(t=!0,clearTimeout(s),i(o))}).catch(o=>{t||(t=!0,clearTimeout(s),e(o))})})}removeBinding(n){const i=this.bindings.get(n);if(i){this.bindings.delete(n);for(const e of i.subscriptions)e()}}}export{c as ConnectionBindingImpl};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{splitTextForAibotProtocol as v}from"../../core/protocol/protocol-text.js";const d=new Set(["grix_reply","grix_complete","grix_event_ack","grix_composing","grix_access_control","grix_status"]);function g(n){return d.has(n)}function p(n,e,t){switch(e){case"grix_reply":return f(n,t);case"grix_complete":return x(n,t);case"grix_event_ack":return C(n,t);case"grix_composing":return S(n,t);case"grix_access_control":return m(n,t);case"grix_status":return y(n);default:return r(`\u672A\u77E5\u4E8B\u4EF6\u5DE5\u5177: ${e}`)}}function f(n,e){const t=String(e.event_id??""),s=String(e.session_id??""),i=String(e.text??""),a=e.quoted_message_id,l=e.is_final===!0;if(!s)return r("\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570: session_id");if(!i)return r("\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570: text");const _=v(i),c=`reply_${t||"proactive"}_${Date.now()}`;for(let o=0;o<_.length;o++)n.sendStreamChunk({event_id:t||void 0,session_id:s,delta_content:_[o],chunk_seq:o+1,is_finish:!1,client_msg_id:c,quoted_message_id:o===0?a:void 0});return n.sendStreamChunk({event_id:t||void 0,session_id:s,delta_content:"",chunk_seq:_.length+1,is_finish:!0,client_msg_id:c}),l&&t&&n.sendEventResult({event_id:t,status:"responded"}),u({ok:!0,chunks:_.length,client_msg_id:c})}function x(n,e){const t=String(e.event_id??""),s=String(e.status??""),i=e.msg;return t?["responded","canceled","failed"].includes(s)?(n.sendEventResult({event_id:t,status:s,msg:i}),u({ok:!0,event_id:t,status:s})):r("status \u5FC5\u987B\u4E3A responded\u3001canceled \u6216 failed"):r("\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570: event_id")}function C(n,e){const t=String(e.event_id??""),s=e.session_id;return t?(n.sendEventAck({event_id:t,session_id:s,received_at:Date.now()}),u({ok:!0,event_id:t})):r("\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570: event_id")}function S(n,e){const t=String(e.session_id??""),s=e.active===!0,i=e.event_id;return t?(n.sendSessionActivitySet({session_id:t,kind:"composing",active:s,ref_event_id:i,ttl_ms:s?3e4:void 0}),u({ok:!0,session_id:t,active:s})):r("\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570: session_id")}function m(n,e){const t=String(e.action??""),s={pair_approve:"pair_approve",pair_deny:"pair_deny",allow_sender:"sender_allow",remove_sender:"sender_remove",set_policy:"policy_set"}[t];if(!s)return r(`\u672A\u77E5 access_control action: ${t}`);const i={};return e.code!=null&&(i.code=e.code),e.sender_id!=null&&(i.sender_id=e.sender_id),e.policy!=null&&(i.policy=e.policy),{content:[{type:"text",text:"__ASYNC_ACCESS_CONTROL__"}],_async:{verb:s,payload:i}}}function y(n){const e=n.status,t=n.getStatusSnapshot();return u({connected:e==="ready",status:e,connected_at:t.connectedAt})}function u(n){return{content:[{type:"text",text:JSON.stringify(n)}],isError:!1}}function r(n){return{content:[{type:"text",text:n}],isError:!0}}export{d as EVENT_TOOL_NAMES,p as executeEventTool,g as isEventTool};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createServer as N}from"node:http";import{Server as O}from"@modelcontextprotocol/sdk/server/index.js";import{StreamableHTTPServerTransport as j}from"@modelcontextprotocol/sdk/server/streamableHttp.js";import{ListToolsRequestSchema as R,CallToolRequestSchema as E}from"@modelcontextprotocol/sdk/types.js";import{createSecurityPolicy as h}from"./security.js";import{SessionManagerImpl as q}from"./session-manager.js";import{ToolRegistryImpl as A}from"./tool-registry.js";import{ToolExecutorImpl as C}from"./tool-executor.js";function J(n,r){let a=null,p=!1,m,c;const S=new A,v=new C;let s=null;return{async start(){m=h({serverPort:n.port,allowedOrigins:n.allowedOrigins??[],allowedHosts:n.allowedHosts??[]}),c=new q({maxSessions:1,sessionTimeoutMs:n.sessionTimeoutMs,onSessionExpired:e=>{w(e)}}),a=N((e,t)=>{g(e,t)}),await new Promise((e,t)=>{a.on("error",t),a.listen(n.port,n.bind,()=>{a.removeListener("error",t),p=!0;const i=a.address();i&&typeof i=="object"&&(n.port=i.port,m=h({serverPort:n.port,allowedOrigins:n.allowedOrigins??[],allowedHosts:n.allowedHosts??[]})),e()})})},async stop(){if(p=!1,a&&(await new Promise(e=>{a.close(()=>e())}),a=null),s){try{await s.transport.close()}catch{}try{await s.mcpServer.close()}catch{}s=null}c&&(await c.closeAll(),c.dispose())},getStatus(){return{listening:p,url:`http://${n.bind}:${n.port}${n.endpoint}`,activeSessions:s?1:0}},pushEvent(e){s&&y(s.mcpServer,"notifications/message",{event_id:e.event_id,session_id:e.session_id,sender_id:e.sender_id??"",content:e.content,msg_type:e.msg_type??1,msg_id:e.msg_id??"",session_type:e.session_type??1,quoted_message_id:e.quoted_message_id??"",attachments:e.attachments??[],context_messages:e.context_messages??[]},s.transport)},pushStop(e){s&&y(s.mcpServer,"notifications/event_stop",{event_id:e.event_id,stop_id:e.stop_id,session_id:e.session_id,reason:e.reason??""},s.transport)},pushRevoke(e){s&&y(s.mcpServer,"notifications/event_revoke",{event_id:e.event_id,session_id:e.session_id,reason:e.reason??""},s.transport)},pushLocalAction(e){s&&y(s.mcpServer,"notifications/local_action",e,s.transport)}};async function g(e,t){if(new URL(e.url??"/",`http://${e.headers.host??"localhost"}`).pathname!==n.endpoint){t.writeHead(404,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Not Found"}));return}const i=m.validateRequest(e);if(!i.ok){t.writeHead(i.statusCode??403,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:i.message}));return}const o=e.method?.toUpperCase();if(o==="GET"){const d=f(e);if(!d||!s||s.sessionId!==d){t.writeHead(400,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Bad Request: missing or invalid session"}));return}c.touchActivity(d),await s.transport.handleRequest(e,t);return}if(o==="DELETE"){await x(e,t);return}if(o==="POST"){await _(e,t);return}t.writeHead(405,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Method Not Allowed"}))}async function _(e,t){const i=await b(e);let o;try{o=JSON.parse(i)}catch{t.writeHead(400,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Invalid JSON body"}));return}const d=I(o),l=e.headers.accept??"";if(!l.includes("application/json")||!l.includes("text/event-stream")){t.writeHead(406,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Not Acceptable: Accept header must include both application/json and text/event-stream"}));return}if(d)await T(e,t,o);else{const u=f(e);if(!u){t.writeHead(400,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Bad Request: missing Mcp-Session-Id header"}));return}if(!s||s.sessionId!==u){t.writeHead(404,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Not Found: session does not exist or has expired"}));return}c.touchActivity(u),await s.transport.handleRequest(e,t,o)}}async function T(e,t,i){if(s){t.writeHead(503,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Service Unavailable: gateway already has an active session"}));return}const o=c.createSession().sessionId,d=new j({sessionIdGenerator:()=>o,enableJsonResponse:!1,onsessioninitialized:()=>{},onsessionclosed:()=>{w(o)}}),l=new O({name:"grix-mcp-server",version:"1.0.0"},{capabilities:{tools:{}}});H(l,o),await l.connect(d),s={transport:d,mcpServer:l,sessionId:o},await d.handleRequest(e,t,i);try{c.markReady(o)}catch{}}async function x(e,t){const i=f(e);if(!i){t.writeHead(400,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Bad Request: missing Mcp-Session-Id header"}));return}if(!s||s.sessionId!==i){t.writeHead(404,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Not Found: session does not exist or has expired"}));return}await s.transport.handleRequest(e,t)}function H(e,t){const i=S.getTools();e.setRequestHandler(R,async()=>({tools:i.map(o=>({name:o.name,description:o.description,inputSchema:o.inputSchema}))})),e.setRequestHandler(E,async o=>{const{name:d,arguments:l}=o.params,u=c.getSession(t);return!u||u.state!=="ready"?{content:[{type:"text",text:`Session \u72B6\u6001\u4E0D\u53EF\u7528: ${u?.state??"unknown"}`}],isError:!0}:r.status!=="ready"?{content:[{type:"text",text:`\u8FDE\u63A5\u4E0D\u53EF\u7528: \u5F53\u524D\u72B6\u6001\u4E3A ${r.status}`}],isError:!0}:(c.touchActivity(t),v.execute(r,d,l??{},n.invokeTimeoutMs))})}async function w(e){if(s?.sessionId===e){try{await s.mcpServer.close()}catch{}s=null}try{await c.closeSession(e)}catch{}}}function y(n,r,a,p){return p?p.send({jsonrpc:"2.0",method:r,params:a}):Promise.resolve()}function f(n){const r=n.headers["mcp-session-id"];return Array.isArray(r)?r[0]:r||void 0}function I(n){return n&&typeof n=="object"&&"method"in n?n.method==="initialize":Array.isArray(n)?n.some(r=>r&&typeof r=="object"&&r.method==="initialize"):!1}function b(n){return new Promise((r,a)=>{const p=[];n.on("data",m=>p.push(m)),n.on("end",()=>r(Buffer.concat(p).toString("utf-8"))),n.on("error",a)})}export{J as createMcpGateway};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createMcpGateway as a}from"./gateway.js";import{createSecurityPolicy as e}from"./security.js";import{SessionManagerImpl as r}from"./session-manager.js";import{createDefaultGatewayConfig as t}from"./config.js";export{r as SessionManagerImpl,t as createDefaultGatewayConfig,a as createMcpGateway,e as createSecurityPolicy};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function a(o){const e=new Set([`http://127.0.0.1:${o.serverPort}`,`http://localhost:${o.serverPort}`,...o.allowedOrigins]),t=new Set([`127.0.0.1:${o.serverPort}`,`localhost:${o.serverPort}`,...o.allowedHosts]);return{validateRequest(s){const r=i(s,e);if(!r.ok)return r;const n=l(s,t);return n.ok?{ok:!0}:n}}}function i(o,e){const t=o.headers.origin;return t?e.has(t)?{ok:!0}:{ok:!1,statusCode:403,message:`Origin not allowed: ${t}`}:{ok:!0}}function l(o,e){const t=o.headers.host;return t?e.has(t)?{ok:!0}:{ok:!1,statusCode:403,message:`Host not allowed: ${t}`}:{ok:!1,statusCode:403,message:"Missing Host header"}}export{a as createSecurityPolicy};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{randomUUID as n}from"node:crypto";const i={initializing:0,ready:1,closing:2,closed:3},o=6e4;class a{sessions=new Map;config;scanTimer=null;constructor(s){this.config=s,this.startScanTimer()}createSession(){if(this.getActiveCount()>=this.config.maxSessions)throw new Error(`Session limit exceeded: max ${this.config.maxSessions} active sessions`);const s=Date.now(),t={sessionId:n(),createdAt:s,lastActivityAt:s,state:"initializing"};return this.sessions.set(t.sessionId,t),t}getSession(s){return this.sessions.get(s)}markReady(s){const t=this.sessions.get(s);if(!t)throw new Error(`Session not found: ${s}`);this.transitionState(t,"ready")}async closeSession(s){const t=this.sessions.get(s);if(!t)throw new Error(`Session not found: ${s}`);t.state!=="closed"&&(t.state!=="closing"&&this.transitionState(t,"closing"),this.transitionState(t,"closed"))}touchActivity(s){const t=this.sessions.get(s);t&&(t.lastActivityAt=Date.now())}getActiveCount(){let s=0;for(const t of this.sessions.values())t.state!=="closed"&&s++;return s}async closeAll(){const s=[];for(const t of this.sessions.values())t.state!=="closed"&&s.push(this.closeSession(t.sessionId));await Promise.all(s)}dispose(){this.scanTimer!==null&&(clearInterval(this.scanTimer),this.scanTimer=null)}startScanTimer(){this.scanTimer=setInterval(()=>{this.scanExpiredSessions()},o)}scanExpiredSessions(){const s=Date.now();for(const t of this.sessions.values())t.state==="closed"||t.state==="closing"||s-t.lastActivityAt>this.config.sessionTimeoutMs&&this.expireSession(t.sessionId)}async expireSession(s){await this.closeSession(s),await this.config.onSessionExpired?.(s)}transitionState(s,t){const e=i[s.state];if(i[t]<=e)throw new Error(`Invalid state transition: cannot transition from '${s.state}' to '${t}'`);s.state=t}}export{a as SessionManagerImpl};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{toolCallToInvoke as i}from"../../core/mcp/tools.js";import{ToolRegistryImpl as l}from"./tool-registry.js";import{validateToolArgs as a}from"./tool-schemas.js";import{isEventTool as p,executeEventTool as d}from"./event-tool-executor.js";class y{registry;constructor(){this.registry=new l}async execute(t,e,r,n){if(!this.registry.hasTool(e))return this.errorResult(`\u672A\u77E5\u5DE5\u5177: ${e}`);const s=a(e,r);if(!s.valid)return this.errorResult(`\u53C2\u6570\u6821\u9A8C\u5931\u8D25: ${s.error}`);if(t.status!=="ready")return this.errorResult(`\u8FDE\u63A5\u4E0D\u53EF\u7528: \u5F53\u524D\u72B6\u6001\u4E3A ${t.status}`);if(p(e))return this.executeEventTool(t,e,r);const o=i(e,r);try{const u=await t.agentInvoke(o.action,o.params,n);return this.normalizeResult(u)}catch(u){const c=u instanceof Error?u.message:String(u);return c.toLowerCase().includes("timeout")?this.errorResult(`\u8C03\u7528\u8D85\u65F6: ${c}`):this.errorResult(`\u8C03\u7528\u5931\u8D25: ${c}`)}}normalizeResult(t){if(t==null||typeof t!="object")return this.successResult(t??null);const e=t,r=typeof e.code=="number"?e.code:0;if(r===0){const s="data"in e?e.data:null;return this.successResult(s??null)}const n=typeof e.msg=="string"?e.msg:"\u672A\u77E5\u9519\u8BEF";return this.errorResult(`\u4E0A\u6E38\u9519\u8BEF [code=${r}]: ${n}`)}successResult(t){return{content:[{type:"text",text:JSON.stringify(t)}],isError:!1}}errorResult(t){return{content:[{type:"text",text:t}],isError:!0}}async executeEventTool(t,e,r){return e==="grix_access_control"?this.executeAccessControl(t,r):d(t,e,r)}async executeAccessControl(t,e){const r=String(e.action??""),n={pair_approve:"pair_approve",pair_deny:"pair_deny",allow_sender:"sender_allow",remove_sender:"sender_remove",set_policy:"policy_set"}[r];if(!n)return this.errorResult(`\u672A\u77E5 access_control action: ${r}`);const s={};e.code!=null&&(s.code=e.code),e.sender_id!=null&&(s.sender_id=e.sender_id),e.policy!=null&&(s.policy=e.policy);try{const o=await t.agentInvoke("claude_access_control",{verb:n,payload:s},3e4);return this.successResult(o)}catch(o){const u=o instanceof Error?o.message:String(o);return this.errorResult(`access_control \u8C03\u7528\u5931\u8D25: ${u}`)}}}export{y as ToolExecutorImpl};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{TOOLS as s,EVENT_TOOLS as t}from"../../core/mcp/tools.js";const e=new Set(["grix_query","grix_group","grix_message_send","grix_message_unsend","grix_admin"]),r=new Set(["grix_reply","grix_complete","grix_event_ack","grix_composing","grix_access_control","grix_status"]);class a{tools;toolMap;constructor(){this.tools=[...s.filter(o=>e.has(o.name)),...t.filter(o=>r.has(o.name))],this.toolMap=new Map(this.tools.map(o=>[o.name,o]))}getTools(){return this.tools}getTool(o){return this.toolMap.get(o)}hasTool(o){return this.toolMap.has(o)}}export{a as ToolRegistryImpl};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const o={required:["action"],properties:{action:{type:"string",enum:["contact_search","session_search","message_history","message_search"]},id:{type:"string"},keyword:{type:"string",maxLength:200},limit:{type:"integer",minimum:1,maximum:100},offset:{type:"integer",minimum:0},sessionId:{type:"string"},beforeId:{type:"string"}}},a={required:["action"],properties:{action:{type:"string",enum:["create","detail","leave","add_members","remove_members","update_member_role","update_all_members_muted","update_member_speaking","dissolve"]},sessionId:{type:"string"},name:{type:"string",maxLength:128},memberIds:{type:"array",items:{type:"string"},maxItems:100},memberTypes:{type:"array",items:{type:"integer",enum:[1,2]}},memberId:{type:"string"},role:{type:"integer",enum:[1,2]},memberType:{type:"integer"},allMembersMuted:{type:"boolean"},isSpeakMuted:{type:"boolean"},canSpeakWhenAllMuted:{type:"boolean"}}},p={required:["sessionId","content"],properties:{sessionId:{type:"string"},content:{type:"string",maxLength:1e4},msgType:{type:"integer"},quotedMessageId:{type:"string"},threadId:{type:"string"}}},m={required:["sessionId","msgId"],properties:{sessionId:{type:"string"},msgId:{type:"string"}}},g={required:["action"],properties:{action:{type:"string",enum:["create_agent","list_categories","create_category","update_category","assign_category","rotate_api_key"]},agentName:{type:"string"},introduction:{type:"string"},isMain:{type:"boolean"},agentId:{type:"string"},categoryId:{type:"string"},name:{type:"string"},parentId:{type:"string"},sortOrder:{type:"integer"}}},y={required:["session_id","text"],properties:{event_id:{type:"string"},session_id:{type:"string"},text:{type:"string",maxLength:5e4},quoted_message_id:{type:"string"},is_final:{type:"boolean"}}},d={required:["event_id","status"],properties:{event_id:{type:"string"},status:{type:"string",enum:["responded","canceled","failed"]},msg:{type:"string",maxLength:500}}},c={required:["event_id"],properties:{event_id:{type:"string"},session_id:{type:"string"}}},l={required:["session_id","active"],properties:{session_id:{type:"string"},active:{type:"boolean"},event_id:{type:"string"}}},_={required:["action"],properties:{action:{type:"string",enum:["pair_approve","pair_deny","allow_sender","remove_sender","set_policy"]},code:{type:"string"},sender_id:{type:"string"},policy:{type:"string",enum:["allowlist","open","disabled"]}}},f={required:[],properties:{}},C={grix_query:o,grix_group:a,grix_message_send:p,grix_message_unsend:m,grix_admin:g,grix_reply:y,grix_complete:d,grix_event_ack:c,grix_composing:l,grix_access_control:_,grix_status:f};function B(u,t){const e=C[u];if(!e)return{valid:!1,error:`\u672A\u77E5\u5DE5\u5177: ${u}`};for(const i of e.required)if(t[i]===void 0||t[i]===null)return{valid:!1,error:`\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570: ${i}`};for(const[i,r]of Object.entries(t)){if(r==null)continue;const n=e.properties[i];if(!n)continue;const s=$(i,r,n);if(s)return{valid:!1,error:s}}return{valid:!0}}function $(u,t,e){switch(e.type){case"string":if(typeof t!="string")return`\u53C2\u6570 ${u} \u7C7B\u578B\u9519\u8BEF: \u671F\u671B string\uFF0C\u5B9E\u9645 ${typeof t}`;if(e.maxLength!==void 0&&t.length>e.maxLength)return`\u53C2\u6570 ${u} \u8D85\u8FC7\u6700\u5927\u957F\u5EA6 ${e.maxLength}\uFF0C\u5B9E\u9645 ${t.length}`;if(e.enum&&!e.enum.includes(t))return`\u53C2\u6570 ${u} \u503C "${t}" \u4E0D\u5728\u5141\u8BB8\u8303\u56F4 [${e.enum.join(", ")}]`;break;case"integer":if(typeof t!="number"||!Number.isInteger(t))return`\u53C2\u6570 ${u} \u7C7B\u578B\u9519\u8BEF: \u671F\u671B integer\uFF0C\u5B9E\u9645 ${typeof t=="number"?"\u6D6E\u70B9\u6570":typeof t}`;if(e.minimum!==void 0&&t<e.minimum)return`\u53C2\u6570 ${u} \u503C ${t} \u5C0F\u4E8E\u6700\u5C0F\u503C ${e.minimum}`;if(e.maximum!==void 0&&t>e.maximum)return`\u53C2\u6570 ${u} \u503C ${t} \u5927\u4E8E\u6700\u5927\u503C ${e.maximum}`;if(e.enum&&!e.enum.includes(t))return`\u53C2\u6570 ${u} \u503C ${t} \u4E0D\u5728\u5141\u8BB8\u8303\u56F4 [${e.enum.join(", ")}]`;break;case"boolean":if(typeof t!="boolean")return`\u53C2\u6570 ${u} \u7C7B\u578B\u9519\u8BEF: \u671F\u671B boolean\uFF0C\u5B9E\u9645 ${typeof t}`;break;case"array":if(!Array.isArray(t))return`\u53C2\u6570 ${u} \u7C7B\u578B\u9519\u8BEF: \u671F\u671B array\uFF0C\u5B9E\u9645 ${typeof t}`;if(e.maxItems!==void 0&&t.length>e.maxItems)return`\u53C2\u6570 ${u} \u8D85\u8FC7\u6700\u5927\u5143\u7D20\u6570 ${e.maxItems}\uFF0C\u5B9E\u9645 ${t.length}`;if(e.items)for(let i=0;i<t.length;i++){const r=t[i];if(e.items.type==="string"&&typeof r!="string")return`\u53C2\u6570 ${u}[${i}] \u7C7B\u578B\u9519\u8BEF: \u671F\u671B string\uFF0C\u5B9E\u9645 ${typeof r}`;if(e.items.type==="integer"){if(typeof r!="number"||!Number.isInteger(r))return`\u53C2\u6570 ${u}[${i}] \u7C7B\u578B\u9519\u8BEF: \u671F\u671B integer\uFF0C\u5B9E\u9645 ${typeof r}`;if(e.items.enum&&!e.items.enum.includes(r))return`\u53C2\u6570 ${u}[${i}] \u503C ${r} \u4E0D\u5728\u5141\u8BB8\u8303\u56F4 [${e.items.enum.join(", ")}]`}}break}}export{B as validateToolArgs};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{execFileSync as
|
|
1
|
+
import{execFileSync as E,execSync as m,spawn as S}from"node:child_process";import o from"node:process";import{setTimeout as I}from"node:timers/promises";let d;function R(){if(o.platform!=="win32")return!1;if(d!==void 0)return d;try{m("net session",{stdio:"ignore",windowsHide:!0}),d=!0}catch{d=!1}return d}function H(e,t,n={}){S(e,t,{detached:!0,stdio:"ignore",windowsHide:!0,env:n.env??o.env}).unref()}function w(e,t,n={}){const{cwd:r,env:u=o.env,allowFailure:a=!1}=n;return new Promise((c,i)=>{const l=S(e,t,{cwd:r||void 0,env:u,stdio:["ignore","pipe","pipe"],windowsHide:!0}),C=[],P=[];l.stdout&&l.stdout.on("data",s=>C.push(s)),l.stderr&&l.stderr.on("data",s=>P.push(s)),l.on("error",s=>{if(a){c({exitCode:-1,stdout:"",stderr:s instanceof Error?s.message:String(s)});return}i(s)}),l.on("close",s=>{const f=s??0,y=Buffer.concat(C).toString("utf8"),x=Buffer.concat(P).toString("utf8");if(f!==0&&!a){const v=x.trim()||y.trim()||`${e} exited with code ${f}`;i(new Error(v));return}c({exitCode:f,stdout:y,stderr:x})})})}function p(e){if(e<=0)return!1;try{return o.kill(e,0),!0}catch(t){if(t instanceof Error&&"code"in t){if(t.code==="ESRCH")return!1;if(t.code==="EPERM")return!0}return!1}}const h=3e3;function L(e){try{const r=m(`wmic process where ProcessId=${e} get CommandLine /format:list`,{encoding:"utf8",stdio:["ignore","pipe","ignore"],timeout:h,windowsHide:!0}).match(/CommandLine=(.*)/)?.[1]?.trim();if(r)return r}catch{}try{const t=`${o.env.SystemRoot||"C:\\Windows"}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`,r=E(t,["-NoProfile","-NonInteractive","-WindowStyle","Hidden","-Command",`(Get-CimInstance Win32_Process -Filter 'ProcessId=${e}').CommandLine`],{encoding:"utf8",stdio:["ignore","pipe","ignore"],timeout:h,windowsHide:!0}).trim();return r||null}catch{return null}}function T(e){if(e<=0||!Number.isInteger(e))return null;if(o.platform==="win32")return L(e);try{const n=m(`ps -ww -o command= -p ${e}`,{encoding:"utf8",stdio:["ignore","pipe","ignore"],timeout:h}).trim();return n||null}catch{return null}}async function k(e,t={}){const{platform:n=o.platform,runCommandImpl:r=w}=t;if(e<=0)return!1;if(n==="win32")return await r("taskkill",["/PID",String(e),"/T","/F"],{allowFailure:!0}),!0;try{return o.kill(-e,"SIGTERM"),!0}catch{}try{return o.kill(e,"SIGTERM"),!0}catch{return!1}}async function _(e,t={}){const{timeoutMs:n=5e3,intervalMs:r=100}=t;if(e<=0)return!0;const u=Date.now()+n;for(;Date.now()<=u;){if(!p(e))return!0;await I(r)}return!p(e)}function W(){return`${o.env.SystemRoot||"C:\\Windows"}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`}function g(e){return e.split(/\r?\n/).map(t=>parseInt(t.trim(),10)).filter(t=>t>0&&!Number.isNaN(t))}async function M(e){const t=w;if(o.platform==="win32"){const r=await t("wmic",["process","where",`CommandLine Like '%${e}%'`,"get","ProcessId"],{allowFailure:!0});if(r.exitCode===0)return g(r.stdout);const u=e.replace(/'/g,"''"),a=await t(W(),["-NoProfile","-NonInteractive","-WindowStyle","Hidden","-Command",`Get-CimInstance Win32_Process | Where-Object { $_.CommandLine -like '*${u}*' } | Select-Object -ExpandProperty ProcessId`],{allowFailure:!0});return a.exitCode!==0?[]:g(a.stdout)}const n=await t("pgrep",["-f",e],{allowFailure:!0});return n.exitCode!==0?[]:g(n.stdout)}async function G(e,t={}){const{exceptPid:n=0,platform:r=o.platform}=t,u=await M(e),a=new Set([o.pid,o.ppid,n]),c=u.filter(i=>!a.has(i));if(c.length===0)return 0;if(r==="win32")for(const i of c)await k(i,{platform:"win32",runCommandImpl:w});else{for(const i of c)try{o.kill(i,"SIGTERM")}catch{}await I(500);for(const i of c)if(p(i))try{o.kill(i,"SIGKILL")}catch{}}return c.length}export{T as getProcessCommandLine,p as isProcessRunning,R as isWindowsElevated,G as killProcessesByCommandLine,w as runCommand,H as spawnDetached,k as terminateProcessTree,_ as waitForProcessExit};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{SessionManager as a}from"./manager.js";export{a as SessionManager};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{EventEmitter as r}from"events";import{AgentProcess as c}from"../agent/process.js";import{AcpClient as l}from"../protocol/acp-client.js";class h extends r{sessions=new Map;async create(s){const e=new c,n=await e.start(s.agent),i=new l;i.on("event",a=>{this.emit("event",a)}),await i.connect({transport:n,sessionId:s.sessionId,authMethod:s.authMethod,initialMode:s.initialMode});const t=i.sessionId,o={id:t,process:e,client:i};return this.sessions.set(t,o),n.on("close",()=>{this.sessions.delete(t),this.emit("sessionClosed",t)}),this.emit("sessionCreated",t),o}get(s){return this.sessions.get(s)}list(){return[...this.sessions.values()]}async close(s){const e=this.sessions.get(s);e&&(this.sessions.delete(s),await e.process.close(),this.emit("sessionClosed",s))}async closeAll(){const s=[...this.sessions.keys()];await Promise.all(s.map(e=>this.close(e)))}get size(){return this.sessions.size}}export{h as SessionManager};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{JsonRpcTransport as o}from"./json-rpc.js";export{o as JsonRpcTransport};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{EventEmitter as p}from"events";class w extends p{nextId=1;pending=new Map;writeLock=Promise.resolve();closed=!1;_readable;_writable;_onNotify;_onRequest;constructor(t,e,r,o){super(),this._readable=t,this._writable=e,this._onNotify=r,this._onRequest=o,this.startReadLoop()}setHandlers(t,e){t&&(this._onNotify=t),e&&(this._onRequest=e)}async call(t,e,r){if(this.closed)throw new Error("transport closed");const o=this.nextId++,s=String(o),a=await new Promise((i,d)=>{if(this.pending.set(s,{resolve:i}),r){const n=()=>{this.pending.delete(s),d(new Error(`aborted: ${r.reason??"request cancelled"}`))};if(r.aborted){n();return}r.addEventListener("abort",n,{once:!0});const c=i,h=l=>{r.removeEventListener("abort",n),c(l)};this.pending.set(s,{resolve:h})}this.write({jsonrpc:"2.0",id:o,method:t,params:e??null}).catch(n=>{this.pending.delete(s),d(n)})});if(a.error){const i=new Error(a.error.message);throw i.code=a.error.code,i}return a.result}async notify(t,e){if(this.closed)throw new Error("transport closed");await this.write({jsonrpc:"2.0",method:t,params:e??null})}async respondSuccess(t,e){await this.write({jsonrpc:"2.0",id:t,result:e})}async respondError(t,e,r){await this.write({jsonrpc:"2.0",id:t,error:{code:e,message:r}})}close(){if(!this.closed){this.closed=!0;for(const[t,{resolve:e}]of this.pending)e({error:{code:-32e3,message:"transport closed"}});this.pending.clear(),this._readable.destroy(),this.emit("close")}}startReadLoop(){let t="";this._readable.on("data",e=>{t+=e.toString();const r=t.split(`
|
|
2
|
+
`);t=r.pop()??"";for(const o of r){const s=o.trim();s.length!==0&&this.dispatch(s)}}),this._readable.on("end",()=>{t.trim()&&this.dispatch(t.trim()),this.close()}),this._readable.on("error",e=>{this.emit("error",e),this.close()})}dispatch(t){let e;try{e=JSON.parse(t)}catch{return}if(e.id!=null&&!e.method){this.completePending(e.id,e.result,e.error);return}if(e.method&&(e.id==null||e.id===null)){this._onNotify?.(e.method,e.params);return}e.method&&e.id!=null&&this._onRequest?.(e.method,e.id,e.params)}completePending(t,e,r){const o=String(t),s=this.pending.get(o);s&&(this.pending.delete(o),s.resolve({result:e,error:r}))}async write(t){const e=this.writeLock;return this.writeLock=e.then(async()=>new Promise((r,o)=>{const s=JSON.stringify(t)+`
|
|
3
|
+
`;this._writable.write(s,"utf-8")?r():(this._writable.once("drain",r),this._writable.once("error",o))})),this.writeLock}}export{w as JsonRpcTransport};
|