grix-connector 3.1.7 → 3.1.9
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
import{existsSync as m,mkdirSync as
|
|
1
|
+
import{existsSync as m,mkdirSync as U,readFileSync as E,renameSync as I,writeFileSync as $}from"node:fs";import{join as _,dirname as M}from"node:path";import{spawn as R}from"node:child_process";import{log as o}from"../log/index.js";import{GRIX_PATHS as y}from"../log/index.js";import{resolveClientVersion as g}from"../util/client-version.js";import{UpgradeError as O,collectEnvInfo as D,getUpgradeLogTail as L,npmInstall as N,pendingExists as v,preflightCheck as x,readPending as B,removePending as u,upgradeLog as n,verifyInstalledVersion as F,writePending as G}from"./npm-upgrader.js";const V=360*60*1e3,j=300*1e3,w=1800*1e3,H=2,K=3,X="grix-connector",S=1e4,T=6,q=2e3;function k(c){return new URL(c.replace(/^wss:/,"https:").replace(/^ws:/,"http:")).origin}function A(){return _(y.data,"upgrade-state.json")}function P(){const c=A();if(!m(c))return{daily_attempts:{},version_attempts:{}};try{return JSON.parse(E(c,"utf-8"))}catch{return{daily_attempts:{},version_attempts:{}}}}function J(c){const t=A();U(M(t),{recursive:!0});const e=t+".tmp";$(e,JSON.stringify(c),"utf-8"),I(e,t)}function b(){return new Date().toISOString().slice(0,10)}class rt{agentConfigs;isBusy;timer=null;initialTimer=null;running=!1;stopped=!1;constructor(t,e){this.agentConfigs=t,this.isBusy=e}async start(){await this.handlePendingOnStartup(),this.initialTimer=setTimeout(()=>{this.stopped||(this.runCheck(),!this.stopped&&(this.timer=setInterval(()=>this.runCheck(),V)))},j)}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(s=>this.queryUpgrade(s)))).find(s=>s.available&&s.release)??{available:!1}}async handlePendingOnStartup(){if(!v())return;const t=B();if(!t){u();return}const e=g(),s=e===t.target_version,r=s?{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(n(s?`daemon startup: version matches target ${t.target_version}, upgrade succeeded`:`daemon startup: version ${e} != target ${t.target_version}, rolled back`),await this.reportUpgrade(r)){u();return}this.retryStartupReport(r)}async retryStartupReport(t){for(let e=2;e<=T;e++){if(this.stopped)return;const s=q*(e-1)+Math.floor(Math.random()*1e3);if(await new Promise(r=>setTimeout(r,s)),this.stopped)return;if(await this.reportUpgrade(t)){u();return}}n(`startup report (${t.status}) not delivered after ${T} attempts; clearing pending to unblock future checks`),u()}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(v()||this.agentConfigs.length===0)return;const s=(await Promise.all(this.agentConfigs.map(i=>this.queryUpgrade(i)))).find(i=>i.available&&i.release)?.release;if(!s)return;const r=s.version,a=g();if(!s.force&&!this.checkRateLimit(r))return;const p=x();if(!p.ok){await this.reportUpgrade({from_version:a,to_version:r,status:"failed",error_code:p.errorCode,error_msg:p.errorMsg});return}n(`upgrade start: ${a} -> ${r}`);const h=Date.now();try{if(G(a,r),await N(X,r),F(r),this.startGuardian(),n("npm install verified, reporting installed"),await this.reportUpgrade({from_version:a,to_version:r,status:"installed",duration_ms:Date.now()-h}),n("shutting down for restart"),this.isBusy?.()){n("active tasks detected, waiting for completion before restart");const i=3600*1e3,d=5e3,f=Date.now()+i;for(;this.isBusy()&&Date.now()<f&&!this.stopped;)await new Promise(C=>setTimeout(C,d));if(this.stopped){n("upgrade aborted: checker stopped during wait");return}this.isBusy()?n("active tasks still running after 1h max wait, forcing restart"):n("all tasks completed, proceeding with restart")}process.kill(process.pid,"SIGTERM")}catch(i){const d=i instanceof O?i.code:"NPM_INSTALL_FAILED",f=i instanceof Error?i.message:String(i);n(`upgrade failed: ${d} ${f}`),u(),this.recordFailure(r),await this.reportUpgrade({from_version:a,to_version:r,status:"failed",error_code:d,error_msg:f,duration_ms:Date.now()-h,upgrade_log:L()})}}checkRateLimit(t){const e=P();if(e.last_failure_at){const l=Date.now()-new Date(e.last_failure_at).getTime();if(l<w)return o.info("upgrade",`In cooldown, ${(w-l)/6e4}m remaining`),!1}const s=e.version_attempts[t]??0;if(s>=K)return o.info("upgrade",`Version ${t} already tried ${s} times, skipping`),!1;const r=b(),a=e.daily_attempts[r]??0;return a>=H?(o.info("upgrade",`Already ${a} attempts today, skipping`),!1):!0}recordFailure(t){const e=P(),s=b();e.last_failure_at=new Date().toISOString(),e.last_failure_version=t,e.daily_attempts[s]=(e.daily_attempts[s]??0)+1,e.version_attempts[t]=(e.version_attempts[t]??0)+1,J(e)}async queryUpgrade(t){const s=`${k(t.wsUrl)}/v1/agent-api/upgrade/check?`+new URLSearchParams({client_type:"grix-connector",client_version:g(),channel:t.channel??"stable",platform:process.platform,arch:process.arch}).toString();try{const r=await fetch(s,{headers:{Authorization:`Bearer ${t.apiKey}`},signal:AbortSignal.timeout(S)});if(!r.ok)return o.warn("upgrade",`Check API returned ${r.status}`),{available:!1};const a=await r.json();return a.code!==0?{available:!1}:a.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 l=`${k(r.wsUrl)}/v1/agent-api/upgrade/report`;try{return(await fetch(l,{method:"POST",headers:{Authorization:`Bearer ${r.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify(e),signal:AbortSignal.timeout(S)})).ok}catch{return!1}}))).some(r=>r)}startGuardian(){const t=_(y.base,"bin","upgrade-guardian.sh");if(process.platform==="win32"||!m(t)){o.info("upgrade","Guardian not available on this platform, skipping");return}try{const e=R(t,[],{detached:!0,stdio:"ignore"});e.unref(),n(`guardian started (pid ${e.pid})`)}catch(e){o.warn("upgrade",`Failed to start guardian: ${e instanceof Error?e.message:e}`)}}}export{rt as UpgradeChecker};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
function a(
|
|
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};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "grix-connector",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.9",
|
|
4
4
|
"description": "Connect local AI coding agents (Claude, Codex, Gemini, Qwen, DeepSeek, Cursor, OpenCode, Pi, OpenHuman, Reasonix) to the Grix scheduling platform. Also serves as an OpenClaw plugin for Grix channel transport.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -61,8 +61,11 @@ sleep 15
|
|
|
61
61
|
ELAPSED=0
|
|
62
62
|
while [ $ELAPSED -lt $HEALTH_TIMEOUT ]; do
|
|
63
63
|
if curl -sf "$HEALTH_URL" > /dev/null 2>&1; then
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
# healthz 通过=新版健康。这里不删 pending:删了会抢在新进程的
|
|
65
|
+
# handlePendingOnStartup 之前,使其读不到 pending、success 回执发不出去
|
|
66
|
+
# (后端只见 installed 不见 success)。交由新进程上报 success 后自行删 pending,
|
|
67
|
+
# guardian 只负责崩溃回滚。
|
|
68
|
+
log "healthz passed, upgrade healthy; leaving pending for in-process success report"
|
|
66
69
|
exit 0
|
|
67
70
|
fi
|
|
68
71
|
sleep 3
|
|
@@ -72,8 +75,8 @@ done
|
|
|
72
75
|
# Extra grace period for slow systems
|
|
73
76
|
sleep 15
|
|
74
77
|
if curl -sf "$HEALTH_URL" > /dev/null 2>&1; then
|
|
75
|
-
|
|
76
|
-
|
|
78
|
+
# 同上:不删 pending,交由进程内 handlePendingOnStartup 上报 success 并删除。
|
|
79
|
+
log "healthz passed (delayed), upgrade healthy; leaving pending for in-process success report"
|
|
77
80
|
exit 0
|
|
78
81
|
fi
|
|
79
82
|
|