grix-connector 3.1.6 → 3.1.8

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 P,readFileSync as U,renameSync as I,writeFileSync as $}from"node:fs";import{join as _,dirname as E}from"node:path";import{spawn as M}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 D,collectEnvInfo as L,getUpgradeLogTail as O,npmInstall as R,pendingExists as v,preflightCheck as N,readPending as x,removePending as f,upgradeLog as n,verifyInstalledVersion as F,writePending as B}from"./npm-upgrader.js";const G=360*60*1e3,V=300*1e3,w=1800*1e3,j=2,H=3,K="grix-connector",S=1e4;function k(c){return new URL(c.replace(/^wss:/,"https:").replace(/^ws:/,"http:")).origin}function T(){return _(y.data,"upgrade-state.json")}function A(){const c=T();if(!m(c))return{daily_attempts:{},version_attempts:{}};try{return JSON.parse(U(c,"utf-8"))}catch{return{daily_attempts:{},version_attempts:{}}}}function q(c){const t=T();P(E(t),{recursive:!0});const e=t+".tmp";$(e,JSON.stringify(c),"utf-8"),I(e,t)}function C(){return new Date().toISOString().slice(0,10)}class tt{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(),G)))},V)}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(a=>this.queryUpgrade(a)))).find(a=>a.available&&a.release)??{available:!1}}async handlePendingOnStartup(){if(!v())return;const t=x();if(!t){f();return}const e=g();e===t.target_version?(n(`daemon startup: version matches target ${t.target_version}, upgrade succeeded`),await this.reportUpgrade({from_version:t.from_version,to_version:t.target_version,status:"success"})):(n(`daemon startup: version ${e} != target ${t.target_version}, rolled back`),await this.reportUpgrade({from_version:t.from_version,to_version:t.target_version,status:"rolled_back",error_code:"STARTUP_CRASH",crash_count:t.crash_count})),f()}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 a=(await Promise.all(this.agentConfigs.map(s=>this.queryUpgrade(s)))).find(s=>s.available&&s.release)?.release;if(!a)return;const r=a.version,i=g();if(!a.force&&!this.checkRateLimit(r))return;const d=N();if(!d.ok){await this.reportUpgrade({from_version:i,to_version:r,status:"failed",error_code:d.errorCode,error_msg:d.errorMsg});return}n(`upgrade start: ${i} -> ${r}`);const h=Date.now();try{if(B(i,r),await R(K,r),F(r),this.startGuardian(),n("npm install verified, reporting installed"),await this.reportUpgrade({from_version:i,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 s=3600*1e3,l=5e3,u=Date.now()+s;for(;this.isBusy()&&Date.now()<u&&!this.stopped;)await new Promise(b=>setTimeout(b,l));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(s){const l=s instanceof D?s.code:"NPM_INSTALL_FAILED",u=s instanceof Error?s.message:String(s);n(`upgrade failed: ${l} ${u}`),f(),this.recordFailure(r),await this.reportUpgrade({from_version:i,to_version:r,status:"failed",error_code:l,error_msg:u,duration_ms:Date.now()-h,upgrade_log:O()})}}checkRateLimit(t){const e=A();if(e.last_failure_at){const p=Date.now()-new Date(e.last_failure_at).getTime();if(p<w)return o.info("upgrade",`In cooldown, ${(w-p)/6e4}m remaining`),!1}const a=e.version_attempts[t]??0;if(a>=H)return o.info("upgrade",`Version ${t} already tried ${a} times, skipping`),!1;const r=C(),i=e.daily_attempts[r]??0;return i>=j?(o.info("upgrade",`Already ${i} attempts today, skipping`),!1):!0}recordFailure(t){const e=A(),a=C();e.last_failure_at=new Date().toISOString(),e.last_failure_version=t,e.daily_attempts[a]=(e.daily_attempts[a]??0)+1,e.version_attempts[t]=(e.version_attempts[t]??0)+1,q(e)}async queryUpgrade(t){const a=`${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(a,{headers:{Authorization:`Bearer ${t.apiKey}`},signal:AbortSignal.timeout(S)});if(!r.ok)return o.warn("upgrade",`Check API returned ${r.status}`),{available:!1};const i=await r.json();return i.code!==0?{available:!1}:i.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;const e={client_type:"grix-connector",...t.npm_version?t:{...t,...L()}};await Promise.all(this.agentConfigs.map(async a=>{const i=`${k(a.wsUrl)}/v1/agent-api/upgrade/report`;try{await fetch(i,{method:"POST",headers:{Authorization:`Bearer ${a.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify(e),signal:AbortSignal.timeout(S)})}catch{}}))}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=M(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{tt as UpgradeChecker};
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(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};
1
+ function a(e){const t=new Set([`http://127.0.0.1:${e.serverPort}`,`http://localhost:${e.serverPort}`,...e.allowedOrigins]),o=new Set([`127.0.0.1:${e.serverPort}`,`localhost:${e.serverPort}`,...e.allowedHosts]);return{validateRequest(s){const r=i(s,t);if(!r.ok)return r;const n=l(s,o);return n.ok?{ok:!0}:n}}}function i(e,t){const o=e.headers.origin;return o?t.has(o)?{ok:!0}:{ok:!1,statusCode:403,message:`Origin not allowed: ${o}`}:{ok:!0}}function l(e,t){const o=e.headers.host;return o?t.has(o)?{ok:!0}:{ok:!1,statusCode:403,message:`Host not allowed: ${o}`}:{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.6",
3
+ "version": "3.1.8",
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",