grix-connector 4.6.10 → 4.7.1

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,9 +1,9 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "bridgeVersion": "4.6.10",
4
- "tarball": "grix-dsh-bridge-4.6.10.tgz",
5
- "size": 23091,
6
- "unpackedSize": 92672,
7
- "shasum": "69874b377e05108e06c7521317b2ab8dc9a5c7a2",
8
- "integrity": "sha512-jWTB6Xxhi811yHnQi84/Vh43cphD8Qe7eVkmzmjXaK04i4jLAzsiGAXdHiaEMqXmgrK6kahua3mnuIkffQC46Q=="
3
+ "bridgeVersion": "4.7.1",
4
+ "tarball": "grix-dsh-bridge-4.7.1.tgz",
5
+ "size": 23125,
6
+ "unpackedSize": 92668,
7
+ "shasum": "62c8a884bbe1d9a1852eafaa84f0d9759579075c",
8
+ "integrity": "sha512-dqaxuztUfN3WPXSLpWkrMNF8D9hUJ0EP6m5RRbFLcWnkqVtPy8xJ0RPv8Rk3df6oxZnPcSaXtOL3ZvOQ3dJbeg=="
9
9
  }
@@ -8,6 +8,6 @@ Content-Length: 0\r
8
8
  Connection: close\r
9
9
  Content-Length: 0\r
10
10
  \r
11
- `);return}const i=o.pathname.slice(a.length)||"/",d=ie(O(this.targetBaseUrl,i,o.search)),c=ce(t.headers),l=new B(d,c,{agent:this.transportAgent,...this.transportCaCerts?{ca:this.transportCaCerts}:{},followRedirects:!0,handshakeTimeout:V,headers:de(t.headers),perMessageDeflate:!0});this.upstreamWebSockets.add(l),await new Promise((h,p)=>{let u=!1,m=!1,E=!1,y={};const _=()=>{l.on("error",()=>{})},w=g=>{u||(u=!0,this.upstreamWebSockets.delete(l),_(),p(g))},f=()=>{u||!m||!E||(u=!0,l.off("error",w),l.protocol&&this.websocketResponseProtocols.set(t,l.protocol),e.handleUpgrade(t,r,n,g=>{e.emit("connection",g,t),this.wireWebSocketConnection(g,l,t,i,o.search,y)}),h())};r.once("close",()=>{u||l.terminate()}),l.once("upgrade",g=>{y=g.headers,this.websocketResponseHeaders.set(t,g.headers),E=!0,f()}),l.once("unexpected-response",(g,b)=>{u||(u=!0,this.upstreamWebSockets.delete(l),_(),this.forwardUnexpectedWebSocketResponse(b,r),h())}),l.once("error",w),l.once("open",()=>{m=!0,f()})})}forwardUnexpectedWebSocketResponse(e,t){if(t.destroyed){e.destroy();return}const r=e.statusCode??502,n=e.statusMessage??"Bad Gateway",o=[`HTTP/1.1 ${r} ${n}`];for(const[a,i]of Object.entries(e.headers)){const d=a.toLowerCase();if(!(i===void 0||S.has(d)||d==="content-length"))for(const c of Array.isArray(i)?i:[i])o.push(`${d}: ${c}`)}o.push("connection: close","",""),t.write(o.join(`\r
12
- `)),e.pipe(t)}wireWebSocketConnection(e,t,r,n,o,a){let i;const d=(c,l)=>{const h=i;i=void 0,h&&h.finalize(c,l)};e.on("message",(c,l)=>{const h=H(c);i&&d("failed","websocket_request_overlap"),i=this.beginWebSocketExchange(r,n,o,h,l,a),t.send(c,{binary:l},p=>{p&&d("failed",`upstream_websocket_send_failed:${p.message}`)})}),t.on("message",(c,l)=>{const h=H(c),p=i;if(p){p.responseCapture.write(l?h:Buffer.concat([h,Buffer.from(`
13
- `)]));const u=ue(h,l);u.responseId&&(p.exchange.correlation.providerResponseId=u.responseId),u.terminal&&(i=void 0,p.finalize("completed"))}e.send(c,{binary:l},u=>{u&&d("cancelled",`downstream_websocket_send_failed:${u.message}`)})}),e.once("close",()=>{this.closeWebSocket(t),d("cancelled","downstream_websocket_closed")}),t.once("close",()=>{this.upstreamWebSockets.delete(t),this.closeWebSocket(e),d("cancelled","upstream_websocket_closed")}),e.on("error",c=>{d("failed",`downstream_websocket_failed:${c.message}`)}),t.on("error",c=>{d("failed",`upstream_websocket_failed:${c.message}`)})}closeWebSocket(e){(e.readyState===B.OPEN||e.readyState===B.CONNECTING)&&e.close()}beginWebSocketExchange(e,t,r,n,o,a){const i=this.activeEventId;if(!i)return;const d=this.createExchange(i,this.activeAuditId??this.options.auditId,e,t,r);d.downstream.method="WEBSOCKET",d.upstream.statusCode=101,d.upstream.redactedHeaders=x(a);const c=this.createWritableBodyCapture(v(this.spoolDirectory,`${d.exchangeId}.request.body`),o?"application/octet-stream":"application/json",d.errors),l=this.createWritableBodyCapture(v(this.spoolDirectory,`${d.exchangeId}.response.body`),"application/x-ndjson; charset=utf-8",d.errors);d.downstream.body=c.body,d.upstream.body=l.body,c.write(n),c.end();let h=!1,p;const u=new Promise(y=>{p=y}),m=this.pendingByEvent.get(i)??new Set;return m.add(u),this.pendingByEvent.set(i,m),u.finally(()=>{m.delete(u),m.size===0&&this.pendingByEvent.delete(i)}),{eventId:i,exchange:d,requestCapture:c,responseCapture:l,done:u,finalize:async(y,_)=>{h||(h=!0,_&&!d.errors.includes(_)&&d.errors.push(_),y==="completed"?l.end():(c.dispose(),l.dispose()),await Promise.all([c.done,l.done]),d.status=y,d.completedAt=new Date().toISOString(),p?.(),p=void 0)}}}createWritableBodyCapture(e,t,r){const n=new N,o=this.captureBody(n,e,{"content-type":t},r);let a=!1;return{...o,write(i){a||n.write(i)},end(){a||(a=!0,n.end())},dispose(){if(a){o.dispose();return}a=!0,n.destroy(),o.dispose()}}}async handleRequest(e,t){const r=new URL(e.url??"/","http://localhost"),n=`/${this.token}`;if(r.pathname!==n&&!r.pathname.startsWith(`${n}/`)){t.writeHead(404,{"content-type":"text/plain; charset=utf-8"}),t.end("Not found");return}const o=r.pathname.slice(n.length)||"/",a=O(this.targetBaseUrl,o,r.search),i=this.activeEventId,d=this.activeAuditId,c=i?this.createExchange(i,d??this.options.auditId,e,o,r.search):void 0,l=c?this.captureBody(e,v(this.spoolDirectory,`${c.exchangeId}.request.body`),e.headers,c.errors):void 0;c&&l&&(c.downstream.body=l.body);let h,p,u=!1,m;const E=new Promise(f=>{m=f});if(i&&c){const f=this.pendingByEvent.get(i)??new Set;f.add(E),this.pendingByEvent.set(i,f),E.finally(()=>{f.delete(E),f.size===0&&this.pendingByEvent.delete(i)})}const y=async(f,g)=>{!c||u||(u=!0,g&&!c.errors.includes(g)&&c.errors.push(g),f!=="completed"&&(l?.dispose(),h?.dispose()),await Promise.all([l?.done,h?.done]),c.status=f,c.completedAt=new Date().toISOString(),m?.(),m=void 0)},w=(a.protocol==="https:"?D:W)(a,{method:e.method??"GET",headers:te(e.headers,a),agent:this.transportAgent,...this.transportCaCerts?{ca:this.transportCaCerts}:{}});w.once("response",f=>{if(p=f,c){c.upstream.statusCode=f.statusCode,c.upstream.redactedHeaders=x(f.headers);const g=[f.headers["x-request-id"],f.headers["openai-request-id"],f.headers["x-openai-request-id"],f.headers["request-id"]].find(b=>typeof b=="string");typeof g=="string"&&(c.correlation.providerRequestId=g),h=this.captureBody(f,v(this.spoolDirectory,`${c.exchangeId}.response.body`),f.headers,c.errors),c.upstream.body=h.body,f.once("end",()=>{t.writableEnded||t.end(),y("completed")}),f.once("aborted",()=>{const b=y("cancelled","upstream_response_aborted");t.destroyed||t.destroy(new Error("Provider response aborted"))}),f.on("error",b=>{const $=y("failed",`upstream_response_failed:${b.message}`);t.destroyed||t.destroy(b)})}t.writeHead(f.statusCode??502,f.statusMessage,ae(f.headers)),f.pipe(t,{end:!c})}),w.on("error",f=>{y("failed",`upstream_request_failed:${f.message}`),t.headersSent||t.writeHead(502,{"content-type":"text/plain; charset=utf-8"}),t.writableEnded||t.end("Provider request failed")}),e.once("aborted",()=>{w.destroy(),y("cancelled","downstream_request_aborted")}),t.once("close",()=>{t.writableFinished||!c||u||(w.destroy(),p?.destroy(),y("cancelled","downstream_response_closed"))}),t.on("error",f=>{y("failed",`downstream_response_failed:${f.message}`)}),e.pipe(w)}createExchange(e,t,r,n,o){const a=U(),i={exchangeId:a,auditId:t,sessionId:this.options.sessionId,eventId:e,provider:this.options.provider,startedAt:new Date().toISOString(),status:"running",downstream:{method:r.method??"GET",path:n,...R(o)?{query:R(o)}:{},redactedHeaders:x(r.headers),body:{bytes:0,complete:!1}},upstream:{targetBaseUrl:K(this.targetBaseUrl),redactedHeaders:{},body:{bytes:0,complete:!1}},correlation:{requestId:a},errors:[]},d=this.exchangesByEvent.get(e)??[];return d.push(i),this.exchangesByEvent.set(e,d),i}captureBody(e,t,r,n){const o=C("sha256"),a={sourcePath:t,bytes:0,contentType:Z(r),contentEncoding:ee(r),complete:!1};let i=z(t,{flags:"wx",mode:384}),d=!1,c=!1,l;const h=new Promise(u=>{l=u}),p=u=>{d||(d=!0,a.error=u,a.complete=!1,delete a.sourcePath,n.push(u),i?.destroy(),i=void 0,j(t).catch(()=>{}),l?.(),l=void 0)};return i.once("error",u=>{p(`body_write_failed:${u.message}`)}),i.once("finish",()=>{d||(a.complete=!0),l?.(),l=void 0}),e.on("data",u=>{const m=Buffer.isBuffer(u)?u:Buffer.from(u);if(o.update(m),a.bytes+=m.byteLength,!d){if(a.bytes>this.maxBodyBytes){p(`body_limit_exceeded:${this.maxBodyBytes}`);return}i?.write(m)}}),e.once("end",()=>{a.sha256=o.digest("hex"),!d&&i?.end()}),e.once("error",u=>{p(`body_stream_failed:${u.message}`)}),{body:a,done:h,dispose(){c||(c=!0,!a.complete&&!d&&p("body_capture_incomplete"))}}}}function Ce(s){return{requested:!0,status:"unavailable",exchanges:[],unavailableReason:s}}export{xe as RawApiCaptureRelay,ne as proxyForUrl,se as resolveTransportCaCerts,Ce as unavailableRawApiCaptureBoundary};
11
+ `);return}const i=o.pathname.slice(a.length)||"/",d=ie(O(this.targetBaseUrl,i,o.search)),c=ce(t.headers),l=new B(d,c,{agent:this.transportAgent,...this.transportCaCerts?{ca:this.transportCaCerts}:{},followRedirects:!0,handshakeTimeout:V,headers:de(t.headers),perMessageDeflate:!0});this.upstreamWebSockets.add(l),await new Promise((p,h)=>{let u=!1,y=!1,E=!1,g={};const _=()=>{l.on("error",()=>{})},w=m=>{u||(u=!0,this.upstreamWebSockets.delete(l),_(),h(m))},f=()=>{u||!y||!E||(u=!0,l.off("error",w),l.protocol&&this.websocketResponseProtocols.set(t,l.protocol),e.handleUpgrade(t,r,n,m=>{e.emit("connection",m,t),this.wireWebSocketConnection(m,l,t,i,o.search,g)}),p())};r.once("close",()=>{u||l.terminate()}),l.once("upgrade",m=>{g=m.headers,this.websocketResponseHeaders.set(t,m.headers),E=!0,f()}),l.once("unexpected-response",(m,b)=>{u||(u=!0,this.upstreamWebSockets.delete(l),_(),this.forwardUnexpectedWebSocketResponse(b,r),p())}),l.once("error",w),l.once("open",()=>{y=!0,f()})})}forwardUnexpectedWebSocketResponse(e,t){if(t.destroyed){e.destroy();return}const r=e.statusCode??502,n=e.statusMessage??"Bad Gateway",o=[`HTTP/1.1 ${r} ${n}`];for(const[a,i]of Object.entries(e.headers)){const d=a.toLowerCase();if(!(i===void 0||S.has(d)||d==="content-length"))for(const c of Array.isArray(i)?i:[i])o.push(`${d}: ${c}`)}o.push("connection: close","",""),t.write(o.join(`\r
12
+ `)),e.pipe(t)}wireWebSocketConnection(e,t,r,n,o,a){let i;const d=(c,l)=>{const p=i;i=void 0,p&&p.finalize(c,l)};e.on("message",(c,l)=>{const p=H(c);i&&d("failed","websocket_request_overlap"),i=this.beginWebSocketExchange(r,n,o,p,l,a),t.send(c,{binary:l},h=>{h&&d("failed",`upstream_websocket_send_failed:${h.message}`)})}),t.on("message",(c,l)=>{const p=H(c),h=i;if(h){h.responseCapture.write(l?p:Buffer.concat([p,Buffer.from(`
13
+ `)]));const u=ue(p,l);u.responseId&&(h.exchange.correlation.providerResponseId=u.responseId),u.terminal&&(i=void 0,h.finalize("completed"))}e.send(c,{binary:l},u=>{u&&d("cancelled",`downstream_websocket_send_failed:${u.message}`)})}),e.once("close",()=>{this.closeWebSocket(t),d("cancelled","downstream_websocket_closed")}),t.once("close",()=>{this.upstreamWebSockets.delete(t),this.closeWebSocket(e),d("cancelled","upstream_websocket_closed")}),e.on("error",c=>{d("failed",`downstream_websocket_failed:${c.message}`)}),t.on("error",c=>{d("failed",`upstream_websocket_failed:${c.message}`)})}closeWebSocket(e){(e.readyState===B.OPEN||e.readyState===B.CONNECTING)&&e.close()}beginWebSocketExchange(e,t,r,n,o,a){const i=this.activeEventId;if(!i)return;const d=this.createExchange(i,this.activeAuditId??this.options.auditId,e,t,r);d.downstream.method="WEBSOCKET",d.upstream.statusCode=101,d.upstream.redactedHeaders=x(a);const c=this.createWritableBodyCapture(v(this.spoolDirectory,`${d.exchangeId}.request.body`),o?"application/octet-stream":"application/json",d.errors),l=this.createWritableBodyCapture(v(this.spoolDirectory,`${d.exchangeId}.response.body`),"application/x-ndjson; charset=utf-8",d.errors);d.downstream.body=c.body,d.upstream.body=l.body,c.write(n),c.end();let p=!1,h;const u=new Promise(g=>{h=g}),y=this.pendingByEvent.get(i)??new Set;return y.add(u),this.pendingByEvent.set(i,y),u.finally(()=>{y.delete(u),y.size===0&&this.pendingByEvent.delete(i)}),{eventId:i,exchange:d,requestCapture:c,responseCapture:l,done:u,finalize:async(g,_)=>{p||(p=!0,_&&!d.errors.includes(_)&&d.errors.push(_),g==="completed"?l.end():(c.dispose(),l.dispose()),await Promise.all([c.done,l.done]),d.status=g,d.completedAt=new Date().toISOString(),h?.(),h=void 0)}}}createWritableBodyCapture(e,t,r){const n=new N,o=this.captureBody(n,e,{"content-type":t},r);let a=!1;return{...o,write(i){a||n.write(i)},end(){a||(a=!0,n.end())},dispose(){if(a){o.dispose();return}a=!0,n.destroy(),o.dispose()}}}async handleRequest(e,t){const r=new URL(e.url??"/","http://localhost"),n=`/${this.token}`;if(r.pathname!==n&&!r.pathname.startsWith(`${n}/`)){t.writeHead(404,{"content-type":"text/plain; charset=utf-8"}),t.end("Not found");return}const o=r.pathname.slice(n.length)||"/",a=O(this.targetBaseUrl,o,r.search),i=this.activeEventId,d=this.activeAuditId,c=i?this.createExchange(i,d??this.options.auditId,e,o,r.search):void 0,l=c?this.captureBody(e,v(this.spoolDirectory,`${c.exchangeId}.request.body`),e.headers,c.errors):void 0;c&&l&&(c.downstream.body=l.body);let p,h,u=!1,y;const E=new Promise(f=>{y=f});if(i&&c){const f=this.pendingByEvent.get(i)??new Set;f.add(E),this.pendingByEvent.set(i,f),E.finally(()=>{f.delete(E),f.size===0&&this.pendingByEvent.delete(i)})}const g=async(f,m)=>{!c||u||(u=!0,m&&!c.errors.includes(m)&&c.errors.push(m),f!=="completed"&&(l?.dispose(),p?.dispose()),await Promise.all([l?.done,p?.done]),c.status=f,c.completedAt=new Date().toISOString(),y?.(),y=void 0)},w=(a.protocol==="https:"?D:W)(a,{method:e.method??"GET",headers:te(e.headers,a),agent:this.transportAgent,...this.transportCaCerts?{ca:this.transportCaCerts}:{}});w.once("response",f=>{if(h=f,c){c.upstream.statusCode=f.statusCode,c.upstream.redactedHeaders=x(f.headers);const m=[f.headers["x-request-id"],f.headers["openai-request-id"],f.headers["x-openai-request-id"],f.headers["request-id"]].find(b=>typeof b=="string");typeof m=="string"&&(c.correlation.providerRequestId=m),p=this.captureBody(f,v(this.spoolDirectory,`${c.exchangeId}.response.body`),f.headers,c.errors),c.upstream.body=p.body,f.once("end",()=>{t.writableEnded||t.end(),g("completed")}),f.once("aborted",()=>{const b=g("cancelled","upstream_response_aborted");t.destroyed||t.destroy(new Error("Provider response aborted"))}),f.on("error",b=>{const $=g("failed",`upstream_response_failed:${b.message}`);t.destroyed||t.destroy(b)})}t.writeHead(f.statusCode??502,f.statusMessage,ae(f.headers)),f.pipe(t,{end:!c})}),w.on("error",f=>{const m=h!==void 0;if(g(m?"cancelled":"failed",m?"upstream_response_aborted":`upstream_request_failed:${f.message}`),!t.headersSent){t.writeHead(502,{"content-type":"text/plain; charset=utf-8"}),t.writableEnded||t.end("Provider request failed");return}t.destroyed||t.destroy(f)}),e.once("aborted",()=>{w.destroy(),g("cancelled","downstream_request_aborted")}),t.once("close",()=>{t.writableFinished||!c||u||(w.destroy(),h?.destroy(),g("cancelled","downstream_response_closed"))}),t.on("error",f=>{g("failed",`downstream_response_failed:${f.message}`)}),e.pipe(w)}createExchange(e,t,r,n,o){const a=U(),i={exchangeId:a,auditId:t,sessionId:this.options.sessionId,eventId:e,provider:this.options.provider,startedAt:new Date().toISOString(),status:"running",downstream:{method:r.method??"GET",path:n,...R(o)?{query:R(o)}:{},redactedHeaders:x(r.headers),body:{bytes:0,complete:!1}},upstream:{targetBaseUrl:K(this.targetBaseUrl),redactedHeaders:{},body:{bytes:0,complete:!1}},correlation:{requestId:a},errors:[]},d=this.exchangesByEvent.get(e)??[];return d.push(i),this.exchangesByEvent.set(e,d),i}captureBody(e,t,r,n){const o=C("sha256"),a={sourcePath:t,bytes:0,contentType:Z(r),contentEncoding:ee(r),complete:!1};let i=z(t,{flags:"wx",mode:384}),d=!1,c=!1,l;const p=new Promise(u=>{l=u}),h=u=>{d||(d=!0,a.error=u,a.complete=!1,delete a.sourcePath,n.push(u),i?.destroy(),i=void 0,j(t).catch(()=>{}),l?.(),l=void 0)};return i.once("error",u=>{h(`body_write_failed:${u.message}`)}),i.once("finish",()=>{d||(a.complete=!0),l?.(),l=void 0}),e.on("data",u=>{const y=Buffer.isBuffer(u)?u:Buffer.from(u);if(o.update(y),a.bytes+=y.byteLength,!d){if(a.bytes>this.maxBodyBytes){h(`body_limit_exceeded:${this.maxBodyBytes}`);return}i?.write(y)}}),e.once("end",()=>{a.sha256=o.digest("hex"),!d&&i?.end()}),e.once("error",u=>{h(`body_stream_failed:${u.message}`)}),{body:a,done:p,dispose(){c||(c=!0,!a.complete&&!d&&h("body_capture_incomplete"))}}}}function Ce(s){return{requested:!0,status:"unavailable",exchanges:[],unavailableReason:s}}export{xe as RawApiCaptureRelay,ne as proxyForUrl,se as resolveTransportCaCerts,Ce as unavailableRawApiCaptureBoundary};
@@ -1,4 +1,4 @@
1
- import{execFile as w,execFileSync as $}from"node:child_process";import{existsSync as O}from"node:fs";import{dirname as T,join as y}from"node:path";import{promisify as k}from"node:util";import{log as I}from"../log/logger.js";import{probeUrls as v}from"./speed-test.js";const C=k(w),A=1e4,x=2,M=3e3;function R(t,e={}){if((e.platform??process.platform)!=="win32")return{command:"npm",args:t};const i=e.env??process.env,o=e.nodePath??process.execPath,u=[typeof i.npm_execpath=="string"?i.npm_execpath:"",y(T(o),"node_modules","npm","bin","npm-cli.js")];try{const n=$("where.exe",["npm.cmd"],{encoding:"utf-8",timeout:3e3,windowsHide:!0}).split(/\r?\n/).map(l=>l.trim()).filter(Boolean);for(const l of n)u.push(y(T(l),"node_modules","npm","bin","npm-cli.js"))}catch{}const s=u.find(n=>n&&O(n));if(!s)throw new Error("Unable to locate npm-cli.js for safe Windows execution");return{command:o,args:[s,...t]}}class b extends Error{output;constructor(e,r){super(e),this.name="NpmInstallError",this.output=r}}const f=[{id:"official",label:"npm \u5B98\u65B9",url:"https://registry.npmjs.org"},{id:"npmmirror",label:"npmmirror (\u6DD8\u5B9D\u955C\u50CF)",url:"https://registry.npmmirror.com"}];async function P(){const t=await v(f.map(r=>({url:r.url,label:r.label})),5e3),e=[];for(const r of t)if(r.reachable){const i=f.find(o=>o.url===r.url);i&&e.push(i)}for(const r of t)if(!r.reachable){const i=f.find(o=>o.url===r.url);i&&!e.includes(i)&&e.push(i)}for(const r of f)e.includes(r)||e.push(r);return e}async function B(){try{const t=R(["config","get","registry"]),{stdout:e}=await C(t.command,t.args,{timeout:A,encoding:"utf-8"});return e.trim()}catch{return"https://registry.npmjs.org"}}async function J(t,e,r,i){const o=await P();if(o.length===0)throw new Error("All npm registries are unreachable. Check your network connection.");let u="",s="";for(const n of o)for(let l=0;l<=x;l++)try{return I.info("installer",`npm install ${t} using ${n.label} (${n.url})`),{output:await D(t,n.url,e,r,i),registry:n.label,registryUrl:n.url}}catch(a){const c=a instanceof Error?a.message:String(a),d=L(a);if(u=c,s=F(`${s}
2
- [${n.label}] ${c}
3
- ${d}`.trim(),r),I.info("installer",`${n.label} failed: ${c.slice(0,200)}`),j(c)&&l<x){await new Promise(m=>setTimeout(m,M));continue}if(!U(c))throw new b(c,s);break}throw new b(`npm install failed on all registries. Last error: ${u}`,s)}function U(t){return/ECONNRESET|ETIMEDOUT|ECONNREFUSED|ENOTFOUND|EAI_AGAIN|ERR_SOCKET_TIMEOUT|getaddrinfo|socket hang up|network|fetch failed|timed out|tunneling socket|self.signed|404|ETARGET|No matching version|notarget/i.test(t)}function j(t){return/\bEBUSY\b|resource busy or locked|EPERM[^\n]*\brename\b|\bELOCKED\b/i.test(t)}function D(t,e,r,i,o){return new Promise((u,s)=>{const n=["install","-g",t,"--registry",e,"--prefer-online","--no-audit","--no-fund"];o?.prefix&&n.push("--prefix",o.prefix),o?.ignoreScripts&&n.push("--ignore-scripts");const l=process.platform==="win32",a=R(n,{env:o?.env});let c=!1,d=Promise.resolve(),m;const g=w(a.command,a.args,{maxBuffer:i,windowsHide:!0,env:o?.env},async(p,_,N)=>{clearTimeout(m),await d;const E=`${_??""}
4
- ${N??""}`.trim();if(p){const h=E||p.message,S=c||p.killed===!0?`ETIMEDOUT: npm install exceeded ${r}ms (registry likely unreachable): ${h}`:h;s(new b(S,E||h));return}u(E)});m=setTimeout(()=>{c=!0,l&&g.pid?d=new Promise(p=>{w("taskkill",["/PID",String(g.pid),"/T","/F"],{windowsHide:!0,timeout:1e4},()=>{g.kill("SIGTERM"),p()})}):g.kill("SIGTERM")},r),m.unref?.()})}function L(t){if(t&&typeof t=="object"&&"output"in t){const e=t.output;return typeof e=="string"?e:""}return""}function F(t,e){return t.length<=e?t:t.slice(-e)}function Q(t){return f.map(e=>({label:e.label,command:`npm install -g ${t} --registry ${e.url}`}))}export{f as NPM_REGISTRIES,b as NpmInstallError,B as getCurrentRegistry,Q as getMirrorInstallCommands,U as isRetriableRegistryError,j as isTransientInstallError,J as npmInstallWithMirror,P as probeRegistries,R as resolveNpmInvocation};
1
+ import{execFile as b,execFileSync as C}from"node:child_process";import{existsSync as $,mkdirSync as k}from"node:fs";import{basename as O,dirname as x,join as y}from"node:path";import{promisify as A}from"node:util";import{resolveRuntimePaths as P}from"../config/paths.js";import{log as I}from"../log/logger.js";import{probeUrls as D}from"./speed-test.js";const M=A(b),U=1e4,N=2,j=3e3;function L(e){if(!e)return!1;const t=e.replace(/\\/g,"/").toLowerCase();return O(t)==="npm-cli.js"||t.includes("/node_modules/npm/")}function F(){try{const e=y(P().rootDir,"npm-cache");return k(e,{recursive:!0}),e}catch{return}}function R(e,t={}){if((t.platform??process.platform)!=="win32")return{command:"npm",args:e};const i=t.env??process.env,n=t.nodePath??process.execPath,u=typeof i.npm_execpath=="string"?i.npm_execpath:"",c=[L(u)?u:"",y(x(n),"node_modules","npm","bin","npm-cli.js")];try{const s=C("where.exe",["npm.cmd"],{encoding:"utf-8",timeout:3e3,windowsHide:!0}).split(/\r?\n/).map(l=>l.trim()).filter(Boolean);for(const l of s)c.push(y(x(l),"node_modules","npm","bin","npm-cli.js"))}catch{}const o=c.find(s=>s&&$(s));if(!o)throw new Error("Unable to locate npm-cli.js for safe Windows execution");return{command:n,args:[o,...e]}}class T extends Error{output;constructor(t,r){super(t),this.name="NpmInstallError",this.output=r}}const p=[{id:"official",label:"npm \u5B98\u65B9",url:"https://registry.npmjs.org"},{id:"npmmirror",label:"npmmirror (\u6DD8\u5B9D\u955C\u50CF)",url:"https://registry.npmmirror.com"}];async function G(){const e=await D(p.map(r=>({url:r.url,label:r.label})),5e3),t=[];for(const r of e)if(r.reachable){const i=p.find(n=>n.url===r.url);i&&t.push(i)}for(const r of e)if(!r.reachable){const i=p.find(n=>n.url===r.url);i&&!t.includes(i)&&t.push(i)}for(const r of p)t.includes(r)||t.push(r);return t}async function te(){try{const e=R(["config","get","registry"]),{stdout:t}=await M(e.command,e.args,{timeout:U,encoding:"utf-8"});return t.trim()}catch{return"https://registry.npmjs.org"}}async function re(e,t,r,i){const n=await G();if(n.length===0)throw new Error("All npm registries are unreachable. Check your network connection.");let u="",c="";for(const o of n)for(let s=0;s<=N;s++)try{return I.info("installer",`npm install ${e} using ${o.label} (${o.url})`),{output:await W(e,o.url,t,r,i),registry:o.label,registryUrl:o.url}}catch(l){const a=l instanceof Error?l.message:String(l),f=Y(l);if(u=a,c=z(`${c}
2
+ [${o.label}] ${a}
3
+ ${f}`.trim(),r),I.info("installer",`${o.label} failed: ${a.slice(0,200)}`),K(a)&&s<N){await new Promise(d=>setTimeout(d,j));continue}if(!H(a))throw new T(a,c);break}throw new T(`npm install failed on all registries. Last error: ${u}`,c)}function H(e){return/ECONNRESET|ETIMEDOUT|ECONNREFUSED|ENOTFOUND|EAI_AGAIN|ERR_SOCKET_TIMEOUT|getaddrinfo|socket hang up|network|fetch failed|timed out|tunneling socket|self.signed|404|ETARGET|No matching version|notarget/i.test(e)}function K(e){return/\bEBUSY\b|resource busy or locked|EPERM[^\n]*\brename\b|\bELOCKED\b/i.test(e)}function W(e,t,r,i,n){return new Promise((u,c)=>{const o=["install","-g",e,"--registry",t,"--prefer-online","--no-audit","--no-fund"],s=F();s&&o.push("--cache",s),n?.prefix&&o.push("--prefix",n.prefix),n?.ignoreScripts&&o.push("--ignore-scripts");const l=process.platform==="win32",a=R(o,{env:n?.env});let f=!1,d=Promise.resolve(),E;const g=b(a.command,a.args,{maxBuffer:i,windowsHide:!0,env:n?.env},async(m,_,S)=>{clearTimeout(E),await d;const h=`${_??""}
4
+ ${S??""}`.trim();if(m){const w=h||m.message,v=f||m.killed===!0?`ETIMEDOUT: npm install exceeded ${r}ms (registry likely unreachable): ${w}`:w;c(new T(v,h||w));return}u(h)});E=setTimeout(()=>{f=!0,l&&g.pid?d=new Promise(m=>{b("taskkill",["/PID",String(g.pid),"/T","/F"],{windowsHide:!0,timeout:1e4},()=>{g.kill("SIGTERM"),m()})}):g.kill("SIGTERM")},r),E.unref?.()})}function Y(e){if(e&&typeof e=="object"&&"output"in e){const t=e.output;return typeof t=="string"?t:""}return""}function z(e,t){return e.length<=t?e:e.slice(-t)}function ne(e){return p.map(t=>({label:t.label,command:`npm install -g ${e} --registry ${t.url}`}))}export{p as NPM_REGISTRIES,T as NpmInstallError,te as getCurrentRegistry,ne as getMirrorInstallCommands,L as isNpmCliEntry,H as isRetriableRegistryError,K as isTransientInstallError,re as npmInstallWithMirror,G as probeRegistries,F as resolveNpmCacheDir,R as resolveNpmInvocation};
@@ -1 +1 @@
1
- import{HealthServer as o}from"./health.js";import{writePidFile as t,removePidFile as d,readDaemonPid as m}from"./pidfile.js";import{bindPortOrFail as l}from"./port-bind.js";export{o as HealthServer,l as bindPortOrFail,m as readDaemonPid,d as removePidFile,t as writePidFile};
1
+ import{HealthServer as o}from"./health.js";import{writePidFile as d,removePidFile as l,readDaemonPid as t}from"./pidfile.js";import{bindPortOrFail as m}from"./port-bind.js";import{killOrphanedClaudeProcesses as P}from"./orphan-cleanup.js";export{o as HealthServer,m as bindPortOrFail,P as killOrphanedClaudeProcesses,t as readDaemonPid,l as removePidFile,d as writePidFile};
@@ -0,0 +1,2 @@
1
+ import{execSync as d}from"node:child_process";const c=/(?:^|\/|\s)claude\s+.*--name\s+grix-[0-9a-f-]{8,}/;function a(){const i=d("ps -eo pid,ppid,command",{encoding:"utf8",timeout:1e4,maxBuffer:8388608,windowsHide:!0}),n=[];for(const t of i.split(`
2
+ `).slice(1)){const s=t.match(/^\s*(\d+)\s+(\d+)\s+(.*)$/);s&&n.push({pid:Number(s[1]),ppid:Number(s[2]),command:s[3]})}return n}function u(i,n){if(i.ppid<=1)return!0;const t=n.get(i.ppid);return!t||t.ppid<=1?!0:!n.has(t.ppid)}function l(i){const n={scanned:0,killed:[],skippedLive:[]};if(process.platform==="win32")return n;let t;try{t=a()}catch(e){return i?.warn(`orphan scan skipped: ${e instanceof Error?e.message:String(e)}`),n}const s=new Map(t.map(e=>[e.pid,e])),p=process.pid,r=new Set;for(const e of t){if(!c.test(e.command))continue;if(n.scanned++,e.pid===p||!u(e,s)){n.skippedLive.push(e.pid);continue}r.add(e.pid);const o=s.get(e.ppid);o&&/\bexpect\b/.test(o.command)&&r.add(o.pid)}for(const e of r)try{process.kill(e,"SIGTERM"),n.killed.push(e)}catch{}return n.killed.length>0&&i?.info(`Reclaimed ${n.killed.length} orphaned Claude process(es) left by a previous daemon: ${n.killed.join(", ")}`),n}export{l as killOrphanedClaudeProcesses};
@@ -1,2 +1,2 @@
1
- import{execFile as wt,execFileSync as R}from"node:child_process";import{randomUUID as Q}from"node:crypto";import{promisify as yt}from"node:util";import{uptime as St}from"node:os";import{closeSync as Et,copyFileSync as kt,cpSync as It,existsSync as d,linkSync as Pt,mkdirSync as E,mkdtempSync as tt,openSync as vt,readFileSync as P,renameSync as h,rmSync as p,statfsSync as xt,symlinkSync as $t,unlinkSync as H,writeFileSync as x}from"node:fs";import{basename as v,dirname as w,join as l,relative as rt,resolve as u,sep as b}from"node:path";import{GRIX_PATHS as $}from"../log/index.js";import{appendRotatingFileSync as Dt}from"../log/rotation.js";import{getMachineName as Nt}from"../util/host-info.js";import{resolveInstallId as At}from"../util/install-id.js";import{NPM_REGISTRIES as Rt,npmInstallWithMirror as et,probeRegistries as bt,resolveNpmInvocation as M}from"../installer/npm-registry.js";import{resolveWindowsServiceRuntimeDir as nt}from"../../service/service-paths.js";const it=yt(wt);class f extends Error{code;constructor(r,e){super(e),this.name="UpgradeError",this.code=r}}const Ut=/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;function O(t){const r=String(t??"").trim().replace(/^v(?=\d)/,"");if(!Ut.test(r))throw new f("INVALID_VERSION",`Invalid connector upgrade version: ${String(t)}`);return r}const Y=".grix-upgrade-backup",Ft=".grix-upgrade-failed",ot=".grix-upgrade-backup",at=".grix-upgrade-next",Lt="upgrade-wrapper.pause.json",Tt="upgrade-rollback.intent.json",Mt=".grix-connector-upgrade.lock",ct=[250,750],C=[100,250,750,1500];function st(){return l($.log,"upgrade.log")}function I(){return l($.data,"upgrade-pending.json")}function lr(){let t=null;try{t=_()}catch{return{in_progress:!1}}if(!t)return{in_progress:!1};const r=t.phase??"staged";return{in_progress:!(r==="failed"||r==="rolled_back"),phase:r,from:t.from_version,target:t.target_version}}function dr(){return d(I())}function gr(t,r="failed"){const e=new Date().toISOString().replace(/[:.]/g,""),n=l($.data,`upgrade-pending.${r}-${e}.json`);try{return x(n,JSON.stringify(t,null,2),"utf-8"),n}catch{return null}}function _(){const t=I();if(!d(t))return null;try{return JSON.parse(P(t,"utf-8"))}catch{return null}}function pr(t,r,e){const n={from_version:t,target_version:r,upgraded_at:new Date().toISOString(),crash_count:0,transaction_id:Q(),...e?{phase:e}:{}},i=I();E(w(i),{recursive:!0});let a;try{a=vt(i,"wx",384)}catch(o){throw o?.code==="EEXIST"?new f("UPGRADE_IN_PROGRESS","Another connector upgrade transaction is already active"):o}try{x(a,JSON.stringify(n),"utf-8")}catch(o){try{H(i)}catch{}throw o}finally{Et(a)}return n}function _r(t){const r=t.package_dir;if(!r)return null;const e=u(r);if(!e.includes(`${b}node_modules${b}`))return null;try{const n=JSON.parse(P(l(e,"package.json"),"utf-8"));return n.name!=="grix-connector"||typeof n.version!="string"?null:n.version.trim().replace(/^v/,"")||null}catch{return null}}function Ot(t,r){E(w(t),{recursive:!0});const e=`${t}.tmp-${process.pid}`;x(e,JSON.stringify(r),"utf-8"),h(e,t)}function D(t,r){const e=_();if(!e)throw new f("PENDING_MISSING","Upgrade transaction is missing");if(r&&e.transaction_id!==r)throw new f("UPGRADE_TRANSACTION_CHANGED","Upgrade transaction ownership changed");const n={...e,...t};return Ot(I(),n),n}function j(){return l($.data,Lt)}function Ct(){return l($.data,Tt)}function mr(t,r){const e=_();if(!e?.transaction_id||e.phase!=="staged"||r&&e.transaction_id!==r)throw new f("INVALID_UPGRADE_STATE","Windows upgrade is not staged for activation");const n=j();E(w(n),{recursive:!0});const i=`${n}.tmp-${process.pid}`;return x(i,JSON.stringify({transaction_id:e.transaction_id}),"utf-8"),h(i,n),D({phase:"handoff_ready",old_pid:t,old_command_line:Gt(t)??void 0,old_boot_time:Date.now()-St()*1e3,activation_started_at:new Date().toISOString(),wrapper_pause_file:n},e.transaction_id)}function Gt(t){if(process.platform!=="win32"||!Number.isInteger(t)||t<=0)return null;try{const r=`${process.env.SystemRoot||"C:\\Windows"}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`;return R(r,["-NoProfile","-NonInteractive","-WindowStyle","Hidden","-Command",`(Get-CimInstance Win32_Process -Filter 'ProcessId=${t}').CommandLine`],{encoding:"utf-8",timeout:3e3,windowsHide:!0}).trim()||null}catch{return null}}function hr(t,r){const e=_();if(!e?.transaction_id||e.transaction_id!==t||e.phase!=="verifying")throw new f("INVALID_UPGRADE_STATE","Windows upgrade is not awaiting verification");const n=Ct();E(w(n),{recursive:!0});const i=`${n}.tmp-${process.pid}`;x(i,JSON.stringify({transaction_id:t,message:r}),"utf-8"),h(i,n);const a=j();E(w(a),{recursive:!0});const o=`${a}.tmp-${process.pid}`;return x(o,JSON.stringify({transaction_id:t}),"utf-8"),h(o,a),D({phase:"rolling_back",guardian_pid:void 0,error_code:"STARTUP_CRASH",error_msg:r,wrapper_pause_file:a},t)}function wr(){try{p(j(),{force:!0})}catch{}}function Vt(t=_()){if(z(t)){try{p(u(t.staging_prefix),{recursive:!0,force:!0})}catch{}if(ut(t))try{p(u(t.runtime_next_dir),{recursive:!0,force:!0})}catch{}}}function ut(t){if(!t?.runtime_dir||!t.runtime_backup_dir||!t.runtime_next_dir)return!1;const r=u(t.runtime_dir),e=nt($.base),n=l(w(w(e)),"runtime"),i=r===u(n),a=r===u(e)&&/^GrixConnectorDaemon-[0-9a-f]{12}$/.test(v(r))&&v(w(r))==="runtimes"&&v(w(w(r)))==="grix-connector";return(i||a)&&u(t.runtime_backup_dir)===`${r}${ot}`&&u(t.runtime_next_dir)===`${r}${at}`}function J(t){return l(u(t),Mt)}function K(t){try{const r=d(t)&&!d(l(t,"owner.json"))?t:l(t,"owner.json"),e=JSON.parse(P(r,"utf-8"));return typeof e.transaction_id=="string"&&typeof e.pending_file=="string"?e:null}catch{return null}}function ft(t){if(!t||!d(t.pending_file))return!1;try{return JSON.parse(P(t.pending_file,"utf-8")).transaction_id===t.transaction_id}catch{return!0}}function Wt(t){const r=_();if(!r?.transaction_id)throw new f("PENDING_MISSING","Upgrade transaction disappeared before npm staging");const e=J(t),n={transaction_id:r.transaction_id,pending_file:I()};D({npm_prefix:u(t),npm_lock_file:e},r.transaction_id);const i=`${e}.candidate-${r.transaction_id}-${Q()}`;try{x(i,JSON.stringify(n),{encoding:"utf-8",flag:"wx"}),Pt(i,e),p(i,{force:!0})}catch(a){try{p(i,{force:!0})}catch{}if(!d(e))throw a;const o=K(e);if(o?.transaction_id===r.transaction_id&&u(o.pending_file)===u(I()))return;throw new f(ft(o)?"UPGRADE_IN_PROGRESS":"UPGRADE_LOCK_RECOVERY_REQUIRED",ft(o)?`Another Windows service root is upgrading the shared npm prefix ${u(t)}`:`A stale or incomplete Windows upgrade lock requires recovery at ${e}`)}}function Bt(t=_(),r={}){if(!t?.transaction_id||!t.npm_prefix||!t.npm_lock_file)return!0;const e=J(t.npm_prefix);if(u(t.npm_lock_file)!==e)return!1;if(!d(e))return!0;const n=K(e);if(!n)return!1;if(n.transaction_id!==t.transaction_id||u(n.pending_file)!==u(I()))return!0;const i=r.remove??p,a=r.sleep??(o=>{Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,o)});for(let o=0;;o+=1)try{return i(e,{recursive:!0,force:!0}),!d(e)}catch(c){const s=c?.code;if((s==="EBUSY"||s==="EPERM"||s==="EACCES")&&o<C.length){a(C[o]);continue}return m(`shared npm-prefix lock release failed at ${e}: ${c instanceof Error?c.message:String(c)}`),!1}}function G(t,r){const e=u(t);return u(r)===`${e}${Y}`&&e.includes(`${b}node_modules${b}`)&&v(e)==="grix-connector"}function Ht(t,r){const e=rt(u(t),u(r));return e!==""&&e!==".."&&!e.startsWith(`..${b}`)}function z(t){if(!t?.npm_prefix||!t.staging_prefix||!t.staged_package_dir)return!1;const r=u(t.npm_prefix),e=u(t.staging_prefix),n=u(t.staged_package_dir);return w(e)===r&&v(e).startsWith(".grix-upgrade-stage-")&&Ht(e,n)&&v(w(n))==="node_modules"&&v(n)==="grix-connector"}function Yt(t,r){const e=_();!e||!G(t,r)||D({package_dir:u(t),backup_dir:u(r)},e.transaction_id)}function jt(t){if(!G(t.packageDir,t.backupDir))throw new f("UNSAFE_UPGRADE_PATH","Refusing to record an unsafe global package path");if(!z({npm_prefix:t.npmPrefix,staging_prefix:t.stagingPrefix,staged_package_dir:t.stagedPackageDir}))throw new f("UNSAFE_UPGRADE_PATH","Refusing to record an unsafe npm staging path");const r=_();if(!r)throw new f("PENDING_MISSING","Upgrade transaction disappeared during staging");let e;try{const n=JSON.parse(P(l(t.packageDir,"package.json"),"utf-8"));if(n.name!=="grix-connector"||typeof n.version!="string")throw new Error("invalid metadata");e=O(n.version)}catch(n){throw new f("VERSION_VERIFY_FAILED",`Unable to identify the active global package before staging: ${n instanceof Error?n.message:String(n)}`)}D({phase:"staged",activation_strategy:t.activationStrategy,package_from_version:e,npm_prefix:u(t.npmPrefix),package_dir:u(t.packageDir),backup_dir:u(t.backupDir),staged_package_dir:u(t.stagedPackageDir),staging_prefix:u(t.stagingPrefix)},r.transaction_id)}function Jt(t,r,e={}){const n=_();if(!n?.transaction_id)throw new f("PENDING_MISSING","Upgrade transaction disappeared before runtime staging");const i=nt($.base),a=`${i}${ot}`,o=`${i}${at}`;D({runtime_dir:i,runtime_backup_dir:a,runtime_next_dir:o},n.transaction_id);const c=e.copyTree??It,s=e.copyFile??kt,g=e.sleep??(k=>{Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,k)});for(let k=0;;k+=1)try{E(w(i),{recursive:!0}),p(o,{recursive:!0,force:!0}),E(o,{recursive:!0}),c(l(t.stagedPackageDir,"dist"),l(o,"dist"),{recursive:!0,force:!0}),s(l(t.stagedPackageDir,"package.json"),l(o,"package.json"));const y=l(t.stagedPackageDir,"node_modules");d(y)&&c(y,l(o,"node_modules"),{recursive:!0,force:!0});const S=F(o,"grix-connector",r);L(o,S);return}catch(y){try{p(o,{recursive:!0,force:!0})}catch{}const S=y?.code;if(S==="ENOSPC")throw new f("DISK_FULL",`Insufficient space for the isolated Windows runtime: ${y.message}`);if((S==="EBUSY"||S==="EPERM"||S==="EACCES"||S==="ENOTEMPTY")&&k<ct.length){g(ct[k]);continue}throw new f("RUNTIME_STAGE_FAILED",`Could not prepare the isolated Windows runtime before shutdown: ${y instanceof Error?y.message:String(y)}`)}}function V(t,r){if(!d(t))return!0;const e=r.remove??p,n=r.sleep??(i=>{Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,i)});for(let i=0;;i+=1)try{if(e(t,{recursive:!0,force:!0}),!d(t))return!0;throw Object.assign(new Error(`path still exists after removal: ${t}`),{code:"EBUSY"})}catch(a){const o=a?.code;if((o==="EBUSY"||o==="EPERM"||o==="EACCES"||o==="ENOTEMPTY")&&i<C.length){n(C[i]);continue}return m(`upgrade artifact cleanup failed at ${t}: ${a instanceof Error?a.message:String(a)}`),!1}}function yr(t=_(),r={}){if(t?.transaction_id&&_()?.transaction_id!==t.transaction_id)return!1;let e=!0,n=process.platform!=="win32";if(t?.npm_lock_file||t?.npm_prefix){if(!t.transaction_id||!t.npm_lock_file||!t.npm_prefix)return!1;const o=J(t.npm_prefix);if(u(t.npm_lock_file)!==o)return!1;if(d(o)){const c=K(o);if(c?.transaction_id!==t.transaction_id||u(c.pending_file)!==u(I()))return!1;n=!0}else{if(t.backup_dir&&d(t.backup_dir))return!1;n=!1}}const i=t?.package_dir,a=t?.backup_dir;return n&&i&&a&&G(i,a)&&(e=V(a,r)&&e),z(t)&&(e=V(u(t.staging_prefix),r)&&e),ut(t)&&(e=V(u(t.runtime_backup_dir),r)&&e,e=V(u(t.runtime_next_dir),r)&&e),e}function Sr(t=_()){const r=t?.package_dir,e=t?.backup_dir;if(!r||!e||!G(r,e)||!d(e))return!1;const n=`${r}${Ft}`;try{return p(n,{recursive:!0,force:!0}),d(r)&&h(r,n),h(e,r),p(n,{recursive:!0,force:!0}),m(`atomic rollback restored ${r}`),!0}catch(i){try{!d(r)&&d(n)&&h(n,r)}catch{}return m(`atomic rollback failed: ${i instanceof Error?i.message:String(i)}`),!1}}function lt(t,r={}){const e=I();if(t){const n=_();if(!n)return!0;if(n.transaction_id!==t)return!1}if(d(e))try{(r.unlink??H)(e)}catch{return!1}return!d(e)}function Er(t=_(),r={}){if(!t?.transaction_id)return lt(void 0,{unlink:r.unlinkPending});const e=_();if(e?.transaction_id!==t.transaction_id)return!1;const n=e.phase==="finalizing"?e:D({phase:"finalizing"},t.transaction_id);return Bt(n,{remove:r.removeLock,sleep:r.sleep})?lt(t.transaction_id,{unlink:r.unlinkPending}):!1}function m(t){const r=`[${new Date().toISOString()}] ${t}
2
- `;try{Dt(st(),r)}catch{}}function kr(t=4096){const r=st();if(!d(r))return"";try{const e=P(r,"utf-8");return e.length<=t?e:e.slice(-t)}catch{return""}}function dt(){try{const t=M(["--version"]);return R(t.command,t.args,{encoding:"utf-8",timeout:1e4,windowsHide:!0}).trim()}catch{throw new f("NPM_NOT_FOUND","npm is not available or timed out")}}const U=1e6;function gt(){let t;try{t=X(["prefix","-g"])}catch{return U}try{const r=xt(t),e=Math.floor(Number(r.bavail)*Number(r.bsize)/(1024*1024));return!Number.isFinite(e)||e<0?U:Math.min(e,U)}catch{return U}}function Ir(){let t;try{t=dt()}catch(e){return{ok:!1,errorCode:"NPM_NOT_FOUND",errorMsg:e instanceof Error?e.message:"npm not available"}}const r=gt();return r<100?{ok:!1,errorCode:"DISK_FULL",errorMsg:`Only ${r}MB free disk space (need >= 100MB)`,npmVersion:t,diskFreeMb:r}:{ok:!0,npmVersion:t,diskFreeMb:r}}function Pr(){let t="";try{t=dt()}catch{}let r=U;try{r=gt()}catch{}return{npm_version:t,node_version:process.version,disk_free_mb:r,platform:process.platform,arch:process.arch,host_name:Nt(),install_id:At()}}function F(t,r,e){const n=l(t,"package.json");let i;try{i=JSON.parse(P(n,"utf-8"))}catch{throw new f("VERSION_VERIFY_FAILED",`Unable to read staged package metadata: ${n}`)}if(i.name!==r||i.version?.replace(/^v(?=\d)/,"")!==e)throw new f("VERSION_MISMATCH",`Staged package ${String(i.name)}@${String(i.version)} does not match ${r}@${e}`);return i}function Kt(t){return typeof t.bin=="string"?[[v(t.name),t.bin]]:!t.bin||typeof t.bin!="object"?[]:Object.entries(t.bin).filter(r=>typeof r[1]=="string")}function L(t,r){const e=Kt(r);if(e.length===0)throw new f("VERSION_VERIFY_FAILED",`Staged package ${r.name} has no executable bin`);for(const[,n]of e){const i=l(t,n);if(!d(i))throw new f("VERSION_VERIFY_FAILED",`Staged package entrypoint is missing: ${i}`)}return e}function zt(t,r,e){if(process.platform==="win32")return;const n=l(t,"bin");E(n,{recursive:!0});for(const[i,a]of e){const o=l(n,i);if(d(o))continue;const c=`${o}.grix-upgrade-${process.pid}`;try{H(c)}catch{}$t(rt(n,l(r,a)),c),h(c,o)}}function Xt(t){const r=u(t.activePackageDir),e=u(t.stagedPackageDir),n=`${r}${Y}`,i=F(e,t.packageName,t.version),a=L(e,i);d(n)&&(d(r)?p(n,{recursive:!0,force:!0}):h(n,r)),Yt(r,n);let o=!1;try{d(r)&&(h(r,n),o=!0),h(e,r);const c=F(r,t.packageName,t.version),s=L(r,c);return zt(t.npmPrefix,r,s),{packageDir:r,backupDir:n}}catch(c){try{d(r)&&p(r,{recursive:!0,force:!0}),o&&d(n)&&h(n,r)}catch{}throw c}}function X(t,r=1e4){try{const e=M(t);return R(e.command,e.args,{encoding:"utf-8",timeout:r,windowsHide:!0}).trim()}catch{throw new f("NPM_NOT_FOUND",`Unable to resolve npm ${t.join(" ")}`)}}function W(t){return X(t)}const pt=Rt.find(t=>t.id==="official")?.url??"https://registry.npmjs.org";async function qt(t,r,e){const n=["view",`${t}@${r}`,"dist.integrity","--registry",e,"--no-audit","--no-fund"],i=M(n),{stdout:a}=await it(i.command,i.args,{timeout:1e4,windowsHide:!0});return a.trim()}async function _t(t,r,e,n={}){if(!e)return;let i;try{i=new URL(e).origin}catch{return}if(i===new URL(pt).origin)return;const a=n.fetchIntegrity??qt;let o;try{o=await a(t,r,pt)}catch(s){m(`integrity cross-check skipped: official registry unreachable (${s instanceof Error?s.message:String(s)})`);return}if(!o){m("integrity cross-check skipped: official registry returned no dist.integrity");return}let c;try{c=await a(t,r,e)}catch(s){m(`integrity cross-check skipped: mirror metadata unreadable (${s instanceof Error?s.message:String(s)})`);return}if(!c){m("integrity cross-check skipped: mirror returned no dist.integrity");return}if(c!==o)throw new f("INTEGRITY_MISMATCH",`Mirror ${e} reports dist.integrity ${c} but official npm reports ${o} for ${t}@${r}`);m(`integrity cross-check passed for ${t}@${r} via ${e}`)}async function mt(t,r,e,n={},i=process.platform==="win32"?"staged":"atomic"){const a=`${t}@${r}`,o=W(["prefix","-g"]),c=W(["root","-g"]);E(o,{recursive:!0}),i==="staged"&&Wt(o);const s=tt(l(o,".grix-upgrade-stage-"));try{m(`staging npm install ${a} under ${s}`);const g=n.installWithMirror??et,{registry:k,registryUrl:y}=await g(a,e,10*1024*1024,{prefix:s,...i==="staged"?{env:{...process.env,GRIX_UPGRADE_STAGING:"1"}}:{}});await(n.verifyIntegrityConsensus??_t)(t,r,y);const N=W(["root","-g","--prefix",s]),A=l(N,t),B=F(A,t,r),T=L(A,B),ht=l(A,T[0][1]),q=R(process.execPath,[ht,"--version"],{encoding:"utf-8",timeout:15e3,maxBuffer:2*1024*1024,windowsHide:!0}).trim().replace(/^v(?=\d)/,"");if(q!==r)throw new f("VERSION_MISMATCH",`Staged CLI reported ${q||"<empty>"}, expected ${r}`);const Z=l(c,t);return{npmPrefix:o,packageDir:Z,backupDir:`${u(Z)}${Y}`,stagedPackageDir:A,stagingPrefix:s,registry:k}}catch(g){try{p(s,{recursive:!0,force:!0})}catch{}throw g}}async function Zt(t,r,e,n){const i=await mt(t,r,e,n,"atomic");try{const a=Xt({packageName:t,version:r,npmPrefix:i.npmPrefix,activePackageDir:i.packageDir,stagedPackageDir:i.stagedPackageDir});m(`atomic package switch succeeded via ${i.registry}; backup=${a.backupDir}`)}finally{try{p(i.stagingPrefix,{recursive:!0,force:!0})}catch{}}}async function vr(t,r,e=12e4,n={}){r=O(r);const i=_()?.transaction_id,a=`${t}@${r}`,o=n.activationMode??(process.platform==="win32"?"staged":"atomic");m(`npm install -g ${a} starting (${o} staging with mirror fallback)`);try{if(o==="staged"){const c=await mt(t,r,e,n,o);try{jt({...c,activationStrategy:process.platform==="win32"?"wrapper-runtime":"service-start"}),process.platform==="win32"&&Jt(c,r)}catch(s){try{p(c.stagingPrefix,{recursive:!0,force:!0})}catch{}throw s}return m(`staged package verified via ${c.registry}; waiting for guardian activation`),{activationRequired:!0,packageDir:c.packageDir,backupDir:c.backupDir,stagedPackageDir:c.stagedPackageDir,stagingPrefix:c.stagingPrefix}}else return await Zt(t,r,e,n),{activationRequired:!1}}catch(c){if(o==="staged"){const g=_();i&&g?.transaction_id===i&&Vt(g)}if(c instanceof f)throw c;const s=c instanceof Error?c.message:String(c);throw m(`npm install failed: ${s}`),/timed out|ETIMEDOUT/i.test(s)?new f("NPM_TIMEOUT",`npm install timed out after ${e/1e3}s (tried all mirrors): ${s}`):s.includes("EACCES")||s.includes("permission denied")?new f("NPM_INSTALL_FAILED",`Permission denied: ${s}`):s.includes("ENOSPC")||s.includes("no space left")?new f("DISK_FULL",`Disk full: ${s}`):s.includes("404")||s.includes("not found")?new f("NPM_INSTALL_FAILED",`Package not found: ${s}`):new f("NPM_INSTALL_FAILED",s)}}function xr(t){let r;try{r=X(["root","-g"])}catch{throw new f("VERSION_VERIFY_FAILED","Unable to resolve the global npm package directory")}let e;try{const n=l(r,"grix-connector","package.json"),i=JSON.parse(P(n,"utf-8"));if(i.name!=="grix-connector"||typeof i.version!="string"||!i.version.trim())throw new Error("invalid package metadata");e=i.version.trim().replace(/^v(?=\d)/,"")}catch{throw new f("VERSION_VERIFY_FAILED","Unable to read the installed grix-connector version")}if(e!==t)throw new f("VERSION_MISMATCH",`Installed version ${e} does not match expected ${t}`);return e}async function $r(t,r){r=O(r);let e=!1;for(const n of await bt())try{const i=["view",`${t}@${r}`,"version","--registry",n.url,"--no-audit","--no-fund"],a=M(i),{stdout:o}=await it(a.command,a.args,{timeout:8e3,windowsHide:!0});if(o.trim())return!0}catch(i){const a=i,o=a.stderr||a.message||String(i);/ETARGET|No matching version|notarget|E404/i.test(o)&&(e=!0)}return!e}async function Dr(t,r,e,n=12e4,i={}){e=O(e);const a=`${r}@${e}`,o=l(t,"versions",e);E(l(t,"versions"),{recursive:!0});const c=tt(l(t,"versions",`.stage-${e}-`));m(`installing ${a} into version store under ${c}`);try{const s=i.installWithMirror??et,{registry:g,registryUrl:k}=await s(a,n,10*1024*1024,{prefix:c,env:{...process.env,GRIX_UPGRADE_STAGING:"1"},ignoreScripts:!0});await(i.verifyIntegrityConsensus??_t)(r,e,k);const S=W(["root","-g","--prefix",c]),N=l(S,r),A=F(N,r,e),B=L(N,A),T=R(process.execPath,[l(N,B[0][1]),"--version"],{encoding:"utf-8",timeout:15e3,maxBuffer:2*1024*1024,windowsHide:!0}).trim().replace(/^v(?=\d)/,"");if(T!==e)throw new f("VERSION_MISMATCH",`Installed CLI reported ${T||"<empty>"}, expected ${e}`);return p(o,{recursive:!0,force:!0}),h(N,o),m(`${a} verified via ${g}; placed at ${o}`),{registry:g,versionDir:o}}catch(s){if(s instanceof f)throw s;const g=s instanceof Error?s.message:String(s);throw m(`version store install failed: ${g}`),/timed out|ETIMEDOUT/i.test(g)?new f("NPM_TIMEOUT",`npm install timed out after ${n/1e3}s (tried all mirrors): ${g}`):g.includes("ENOSPC")||g.includes("no space left")?new f("DISK_FULL",`Disk full: ${g}`):new f("NPM_INSTALL_FAILED",g)}finally{try{p(c,{recursive:!0,force:!0})}catch{}}}export{f as UpgradeError,Wt as acquireNpmPrefixUpgradeLock,gr as archivePendingJournal,mr as armWindowsUpgradeHandoff,hr as armWindowsUpgradeRollback,gt as checkDiskSpace,dt as checkNpmAvailable,yr as cleanupPendingBackup,Pr as collectEnvInfo,Xt as commitStagedGlobalPackage,Vt as discardPendingStaging,Er as finalizePendingTransaction,kr as getUpgradeLogTail,Dr as installVersionIntoStore,$r as isVersionAvailable,O as normalizeUpgradeVersion,vr as npmInstall,dr as pendingExists,Ir as preflightCheck,Jt as prepareStagedWindowsRuntime,_ as readPending,_r as readPendingPackageVersion,Bt as releasePendingUpgradeLock,lt as removePending,wr as removeWindowsHandoffFiles,Sr as rollbackPendingInstall,D as updatePending,m as upgradeLog,lr as upgradeStatusSnapshot,xr as verifyInstalledVersion,_t as verifyMirrorIntegrityConsensus,Ct as windowsRollbackIntentPath,j as windowsWrapperPausePath,pr as writePending,Ot as writePendingData};
1
+ import{execFile as yt,execFileSync as R}from"node:child_process";import{randomUUID as Q}from"node:crypto";import{promisify as Et}from"node:util";import{uptime as kt}from"node:os";import{closeSync as It,copyFileSync as Pt,cpSync as vt,existsSync as d,linkSync as xt,mkdirSync as E,mkdtempSync as tt,openSync as $t,readFileSync as P,renameSync as h,rmSync as g,statfsSync as Dt,symlinkSync as Nt,unlinkSync as H,writeFileSync as x}from"node:fs";import{basename as v,dirname as w,join as l,relative as et,resolve as u,sep as b}from"node:path";import{GRIX_PATHS as $}from"../log/index.js";import{appendRotatingFileSync as At}from"../log/rotation.js";import{getMachineName as Rt}from"../util/host-info.js";import{resolveInstallId as bt}from"../util/install-id.js";import{NPM_REGISTRIES as Tt,npmInstallWithMirror as rt,probeRegistries as Ut,resolveNpmCacheDir as nt,resolveNpmInvocation as M}from"../installer/npm-registry.js";import{resolveWindowsServiceRuntimeDir as it}from"../../service/service-paths.js";const ot=Et(yt);class f extends Error{code;constructor(e,r){super(r),this.name="UpgradeError",this.code=e}}const Lt=/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;function O(t){const e=String(t??"").trim().replace(/^v(?=\d)/,"");if(!Lt.test(e))throw new f("INVALID_VERSION",`Invalid connector upgrade version: ${String(t)}`);return e}const Y=".grix-upgrade-backup",Ft=".grix-upgrade-failed",at=".grix-upgrade-backup",ct=".grix-upgrade-next",Mt="upgrade-wrapper.pause.json",Ot="upgrade-rollback.intent.json",Ct=".grix-connector-upgrade.lock",st=[250,750],C=[100,250,750,1500];function ut(){return l($.log,"upgrade.log")}function I(){return l($.data,"upgrade-pending.json")}function pe(){let t=null;try{t=_()}catch{return{in_progress:!1}}if(!t)return{in_progress:!1};const e=t.phase??"staged";return{in_progress:!(e==="failed"||e==="rolled_back"),phase:e,from:t.from_version,target:t.target_version}}function ge(){return d(I())}function _e(t,e="failed"){const r=new Date().toISOString().replace(/[:.]/g,""),n=l($.data,`upgrade-pending.${e}-${r}.json`);try{return x(n,JSON.stringify(t,null,2),"utf-8"),n}catch{return null}}function _(){const t=I();if(!d(t))return null;try{return JSON.parse(P(t,"utf-8"))}catch{return null}}function me(t,e,r){const n={from_version:t,target_version:e,upgraded_at:new Date().toISOString(),crash_count:0,transaction_id:Q(),...r?{phase:r}:{}},i=I();E(w(i),{recursive:!0});let a;try{a=$t(i,"wx",384)}catch(o){throw o?.code==="EEXIST"?new f("UPGRADE_IN_PROGRESS","Another connector upgrade transaction is already active"):o}try{x(a,JSON.stringify(n),"utf-8")}catch(o){try{H(i)}catch{}throw o}finally{It(a)}return n}function he(t){const e=t.package_dir;if(!e)return null;const r=u(e);if(!r.includes(`${b}node_modules${b}`))return null;try{const n=JSON.parse(P(l(r,"package.json"),"utf-8"));return n.name!=="grix-connector"||typeof n.version!="string"?null:n.version.trim().replace(/^v/,"")||null}catch{return null}}function Gt(t,e){E(w(t),{recursive:!0});const r=`${t}.tmp-${process.pid}`;x(r,JSON.stringify(e),"utf-8"),h(r,t)}function D(t,e){const r=_();if(!r)throw new f("PENDING_MISSING","Upgrade transaction is missing");if(e&&r.transaction_id!==e)throw new f("UPGRADE_TRANSACTION_CHANGED","Upgrade transaction ownership changed");const n={...r,...t};return Gt(I(),n),n}function j(){return l($.data,Mt)}function Vt(){return l($.data,Ot)}function we(t,e){const r=_();if(!r?.transaction_id||r.phase!=="staged"||e&&r.transaction_id!==e)throw new f("INVALID_UPGRADE_STATE","Windows upgrade is not staged for activation");const n=j();E(w(n),{recursive:!0});const i=`${n}.tmp-${process.pid}`;return x(i,JSON.stringify({transaction_id:r.transaction_id}),"utf-8"),h(i,n),D({phase:"handoff_ready",old_pid:t,old_command_line:Wt(t)??void 0,old_boot_time:Date.now()-kt()*1e3,activation_started_at:new Date().toISOString(),wrapper_pause_file:n},r.transaction_id)}function Wt(t){if(process.platform!=="win32"||!Number.isInteger(t)||t<=0)return null;try{const e=`${process.env.SystemRoot||"C:\\Windows"}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`;return R(e,["-NoProfile","-NonInteractive","-WindowStyle","Hidden","-Command",`(Get-CimInstance Win32_Process -Filter 'ProcessId=${t}').CommandLine`],{encoding:"utf-8",timeout:3e3,windowsHide:!0}).trim()||null}catch{return null}}function Se(t,e){const r=_();if(!r?.transaction_id||r.transaction_id!==t||r.phase!=="verifying")throw new f("INVALID_UPGRADE_STATE","Windows upgrade is not awaiting verification");const n=Vt();E(w(n),{recursive:!0});const i=`${n}.tmp-${process.pid}`;x(i,JSON.stringify({transaction_id:t,message:e}),"utf-8"),h(i,n);const a=j();E(w(a),{recursive:!0});const o=`${a}.tmp-${process.pid}`;return x(o,JSON.stringify({transaction_id:t}),"utf-8"),h(o,a),D({phase:"rolling_back",guardian_pid:void 0,error_code:"STARTUP_CRASH",error_msg:e,wrapper_pause_file:a},t)}function ye(){try{g(j(),{force:!0})}catch{}}function Bt(t=_()){if(z(t)){try{g(u(t.staging_prefix),{recursive:!0,force:!0})}catch{}if(ft(t))try{g(u(t.runtime_next_dir),{recursive:!0,force:!0})}catch{}}}function ft(t){if(!t?.runtime_dir||!t.runtime_backup_dir||!t.runtime_next_dir)return!1;const e=u(t.runtime_dir),r=it($.base),n=l(w(w(r)),"runtime"),i=e===u(n),a=e===u(r)&&/^GrixConnectorDaemon-[0-9a-f]{12}$/.test(v(e))&&v(w(e))==="runtimes"&&v(w(w(e)))==="grix-connector";return(i||a)&&u(t.runtime_backup_dir)===`${e}${at}`&&u(t.runtime_next_dir)===`${e}${ct}`}function J(t){return l(u(t),Ct)}function K(t){try{const e=d(t)&&!d(l(t,"owner.json"))?t:l(t,"owner.json"),r=JSON.parse(P(e,"utf-8"));return typeof r.transaction_id=="string"&&typeof r.pending_file=="string"?r:null}catch{return null}}function lt(t){if(!t||!d(t.pending_file))return!1;try{return JSON.parse(P(t.pending_file,"utf-8")).transaction_id===t.transaction_id}catch{return!0}}function Ht(t){const e=_();if(!e?.transaction_id)throw new f("PENDING_MISSING","Upgrade transaction disappeared before npm staging");const r=J(t),n={transaction_id:e.transaction_id,pending_file:I()};D({npm_prefix:u(t),npm_lock_file:r},e.transaction_id);const i=`${r}.candidate-${e.transaction_id}-${Q()}`;try{x(i,JSON.stringify(n),{encoding:"utf-8",flag:"wx"}),xt(i,r),g(i,{force:!0})}catch(a){try{g(i,{force:!0})}catch{}if(!d(r))throw a;const o=K(r);if(o?.transaction_id===e.transaction_id&&u(o.pending_file)===u(I()))return;throw new f(lt(o)?"UPGRADE_IN_PROGRESS":"UPGRADE_LOCK_RECOVERY_REQUIRED",lt(o)?`Another Windows service root is upgrading the shared npm prefix ${u(t)}`:`A stale or incomplete Windows upgrade lock requires recovery at ${r}`)}}function Yt(t=_(),e={}){if(!t?.transaction_id||!t.npm_prefix||!t.npm_lock_file)return!0;const r=J(t.npm_prefix);if(u(t.npm_lock_file)!==r)return!1;if(!d(r))return!0;const n=K(r);if(!n)return!1;if(n.transaction_id!==t.transaction_id||u(n.pending_file)!==u(I()))return!0;const i=e.remove??g,a=e.sleep??(o=>{Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,o)});for(let o=0;;o+=1)try{return i(r,{recursive:!0,force:!0}),!d(r)}catch(c){const s=c?.code;if((s==="EBUSY"||s==="EPERM"||s==="EACCES")&&o<C.length){a(C[o]);continue}return m(`shared npm-prefix lock release failed at ${r}: ${c instanceof Error?c.message:String(c)}`),!1}}function G(t,e){const r=u(t);return u(e)===`${r}${Y}`&&r.includes(`${b}node_modules${b}`)&&v(r)==="grix-connector"}function jt(t,e){const r=et(u(t),u(e));return r!==""&&r!==".."&&!r.startsWith(`..${b}`)}function z(t){if(!t?.npm_prefix||!t.staging_prefix||!t.staged_package_dir)return!1;const e=u(t.npm_prefix),r=u(t.staging_prefix),n=u(t.staged_package_dir);return w(r)===e&&v(r).startsWith(".grix-upgrade-stage-")&&jt(r,n)&&v(w(n))==="node_modules"&&v(n)==="grix-connector"}function Jt(t,e){const r=_();!r||!G(t,e)||D({package_dir:u(t),backup_dir:u(e)},r.transaction_id)}function Kt(t){if(!G(t.packageDir,t.backupDir))throw new f("UNSAFE_UPGRADE_PATH","Refusing to record an unsafe global package path");if(!z({npm_prefix:t.npmPrefix,staging_prefix:t.stagingPrefix,staged_package_dir:t.stagedPackageDir}))throw new f("UNSAFE_UPGRADE_PATH","Refusing to record an unsafe npm staging path");const e=_();if(!e)throw new f("PENDING_MISSING","Upgrade transaction disappeared during staging");let r;try{const n=JSON.parse(P(l(t.packageDir,"package.json"),"utf-8"));if(n.name!=="grix-connector"||typeof n.version!="string")throw new Error("invalid metadata");r=O(n.version)}catch(n){throw new f("VERSION_VERIFY_FAILED",`Unable to identify the active global package before staging: ${n instanceof Error?n.message:String(n)}`)}D({phase:"staged",activation_strategy:t.activationStrategy,package_from_version:r,npm_prefix:u(t.npmPrefix),package_dir:u(t.packageDir),backup_dir:u(t.backupDir),staged_package_dir:u(t.stagedPackageDir),staging_prefix:u(t.stagingPrefix)},e.transaction_id)}function zt(t,e,r={}){const n=_();if(!n?.transaction_id)throw new f("PENDING_MISSING","Upgrade transaction disappeared before runtime staging");const i=it($.base),a=`${i}${at}`,o=`${i}${ct}`;D({runtime_dir:i,runtime_backup_dir:a,runtime_next_dir:o},n.transaction_id);const c=r.copyTree??vt,s=r.copyFile??Pt,p=r.sleep??(k=>{Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,k)});for(let k=0;;k+=1)try{E(w(i),{recursive:!0}),g(o,{recursive:!0,force:!0}),E(o,{recursive:!0}),c(l(t.stagedPackageDir,"dist"),l(o,"dist"),{recursive:!0,force:!0}),s(l(t.stagedPackageDir,"package.json"),l(o,"package.json"));const S=l(t.stagedPackageDir,"node_modules");d(S)&&c(S,l(o,"node_modules"),{recursive:!0,force:!0});const y=U(o,"grix-connector",e);L(o,y);return}catch(S){try{g(o,{recursive:!0,force:!0})}catch{}const y=S?.code;if(y==="ENOSPC")throw new f("DISK_FULL",`Insufficient space for the isolated Windows runtime: ${S.message}`);if((y==="EBUSY"||y==="EPERM"||y==="EACCES"||y==="ENOTEMPTY")&&k<st.length){p(st[k]);continue}throw new f("RUNTIME_STAGE_FAILED",`Could not prepare the isolated Windows runtime before shutdown: ${S instanceof Error?S.message:String(S)}`)}}function V(t,e){if(!d(t))return!0;const r=e.remove??g,n=e.sleep??(i=>{Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,i)});for(let i=0;;i+=1)try{if(r(t,{recursive:!0,force:!0}),!d(t))return!0;throw Object.assign(new Error(`path still exists after removal: ${t}`),{code:"EBUSY"})}catch(a){const o=a?.code;if((o==="EBUSY"||o==="EPERM"||o==="EACCES"||o==="ENOTEMPTY")&&i<C.length){n(C[i]);continue}return m(`upgrade artifact cleanup failed at ${t}: ${a instanceof Error?a.message:String(a)}`),!1}}function Ee(t=_(),e={}){if(t?.transaction_id&&_()?.transaction_id!==t.transaction_id)return!1;let r=!0,n=process.platform!=="win32";if(t?.npm_lock_file||t?.npm_prefix){if(!t.transaction_id||!t.npm_lock_file||!t.npm_prefix)return!1;const o=J(t.npm_prefix);if(u(t.npm_lock_file)!==o)return!1;if(d(o)){const c=K(o);if(c?.transaction_id!==t.transaction_id||u(c.pending_file)!==u(I()))return!1;n=!0}else{if(t.backup_dir&&d(t.backup_dir))return!1;n=!1}}const i=t?.package_dir,a=t?.backup_dir;return n&&i&&a&&G(i,a)&&(r=V(a,e)&&r),z(t)&&(r=V(u(t.staging_prefix),e)&&r),ft(t)&&(r=V(u(t.runtime_backup_dir),e)&&r,r=V(u(t.runtime_next_dir),e)&&r),r}function ke(t=_()){const e=t?.package_dir,r=t?.backup_dir;if(!e||!r||!G(e,r)||!d(r))return!1;const n=`${e}${Ft}`;try{return g(n,{recursive:!0,force:!0}),d(e)&&h(e,n),h(r,e),g(n,{recursive:!0,force:!0}),m(`atomic rollback restored ${e}`),!0}catch(i){try{!d(e)&&d(n)&&h(n,e)}catch{}return m(`atomic rollback failed: ${i instanceof Error?i.message:String(i)}`),!1}}function dt(t,e={}){const r=I();if(t){const n=_();if(!n)return!0;if(n.transaction_id!==t)return!1}if(d(r))try{(e.unlink??H)(r)}catch{return!1}return!d(r)}function Ie(t=_(),e={}){if(!t?.transaction_id)return dt(void 0,{unlink:e.unlinkPending});const r=_();if(r?.transaction_id!==t.transaction_id)return!1;const n=r.phase==="finalizing"?r:D({phase:"finalizing"},t.transaction_id);return Yt(n,{remove:e.removeLock,sleep:e.sleep})?dt(t.transaction_id,{unlink:e.unlinkPending}):!1}function m(t){const e=`[${new Date().toISOString()}] ${t}
2
+ `;try{At(ut(),e)}catch{}}function Pe(t=4096){const e=ut();if(!d(e))return"";try{const r=P(e,"utf-8");return r.length<=t?r:r.slice(-t)}catch{return""}}function pt(){try{const t=M(["--version"]);return R(t.command,t.args,{encoding:"utf-8",timeout:1e4,windowsHide:!0}).trim()}catch{throw new f("NPM_NOT_FOUND","npm is not available or timed out")}}const T=1e6;function gt(){let t;try{t=X(["prefix","-g"])}catch{return T}try{const e=Dt(t),r=Math.floor(Number(e.bavail)*Number(e.bsize)/(1024*1024));return!Number.isFinite(r)||r<0?T:Math.min(r,T)}catch{return T}}function ve(){let t;try{t=pt()}catch(r){return{ok:!1,errorCode:"NPM_NOT_FOUND",errorMsg:r instanceof Error?r.message:"npm not available"}}const e=gt();return e<100?{ok:!1,errorCode:"DISK_FULL",errorMsg:`Only ${e}MB free disk space (need >= 100MB)`,npmVersion:t,diskFreeMb:e}:{ok:!0,npmVersion:t,diskFreeMb:e}}function xe(){let t="";try{t=pt()}catch{}let e=T;try{e=gt()}catch{}return{npm_version:t,node_version:process.version,disk_free_mb:e,platform:process.platform,arch:process.arch,host_name:Rt(),install_id:bt()}}function U(t,e,r){const n=l(t,"package.json");let i;try{i=JSON.parse(P(n,"utf-8"))}catch{throw new f("VERSION_VERIFY_FAILED",`Unable to read staged package metadata: ${n}`)}if(i.name!==e||i.version?.replace(/^v(?=\d)/,"")!==r)throw new f("VERSION_MISMATCH",`Staged package ${String(i.name)}@${String(i.version)} does not match ${e}@${r}`);return i}function Xt(t){return typeof t.bin=="string"?[[v(t.name),t.bin]]:!t.bin||typeof t.bin!="object"?[]:Object.entries(t.bin).filter(e=>typeof e[1]=="string")}function L(t,e){const r=Xt(e);if(r.length===0)throw new f("VERSION_VERIFY_FAILED",`Staged package ${e.name} has no executable bin`);for(const[,n]of r){const i=l(t,n);if(!d(i))throw new f("VERSION_VERIFY_FAILED",`Staged package entrypoint is missing: ${i}`)}return r}function qt(t,e,r){if(process.platform==="win32")return;const n=l(t,"bin");E(n,{recursive:!0});for(const[i,a]of r){const o=l(n,i);if(d(o))continue;const c=`${o}.grix-upgrade-${process.pid}`;try{H(c)}catch{}Nt(et(n,l(e,a)),c),h(c,o)}}function Zt(t){const e=u(t.activePackageDir),r=u(t.stagedPackageDir),n=`${e}${Y}`,i=U(r,t.packageName,t.version),a=L(r,i);d(n)&&(d(e)?g(n,{recursive:!0,force:!0}):h(n,e)),Jt(e,n);let o=!1;try{d(e)&&(h(e,n),o=!0),h(r,e);const c=U(e,t.packageName,t.version),s=L(e,c);return qt(t.npmPrefix,e,s),{packageDir:e,backupDir:n}}catch(c){try{d(e)&&g(e,{recursive:!0,force:!0}),o&&d(n)&&h(n,e)}catch{}throw c}}function X(t,e=1e4){try{const r=M(t);return R(r.command,r.args,{encoding:"utf-8",timeout:e,windowsHide:!0}).trim()}catch{throw new f("NPM_NOT_FOUND",`Unable to resolve npm ${t.join(" ")}`)}}function W(t){return X(t)}const _t=3e5,mt=Tt.find(t=>t.id==="official")?.url??"https://registry.npmjs.org";async function Qt(t,e,r){const n=["view",`${t}@${e}`,"dist.integrity","--registry",r,"--no-audit","--no-fund"],i=nt();i&&n.push("--cache",i);const a=M(n),{stdout:o}=await ot(a.command,a.args,{timeout:1e4,windowsHide:!0});return o.trim()}async function ht(t,e,r,n={}){if(!r)return;let i;try{i=new URL(r).origin}catch{return}if(i===new URL(mt).origin)return;const a=n.fetchIntegrity??Qt;let o;try{o=await a(t,e,mt)}catch(s){m(`integrity cross-check skipped: official registry unreachable (${s instanceof Error?s.message:String(s)})`);return}if(!o){m("integrity cross-check skipped: official registry returned no dist.integrity");return}let c;try{c=await a(t,e,r)}catch(s){m(`integrity cross-check skipped: mirror metadata unreadable (${s instanceof Error?s.message:String(s)})`);return}if(!c){m("integrity cross-check skipped: mirror returned no dist.integrity");return}if(c!==o)throw new f("INTEGRITY_MISMATCH",`Mirror ${r} reports dist.integrity ${c} but official npm reports ${o} for ${t}@${e}`);m(`integrity cross-check passed for ${t}@${e} via ${r}`)}async function wt(t,e,r,n={},i=process.platform==="win32"?"staged":"atomic"){const a=`${t}@${e}`,o=W(["prefix","-g"]),c=W(["root","-g"]);E(o,{recursive:!0}),i==="staged"&&Ht(o);const s=tt(l(o,".grix-upgrade-stage-"));try{m(`staging npm install ${a} under ${s}`);const p=n.installWithMirror??rt,{registry:k,registryUrl:S}=await p(a,r,10*1024*1024,{prefix:s,...i==="staged"?{env:{...process.env,GRIX_UPGRADE_STAGING:"1"}}:{}});await(n.verifyIntegrityConsensus??ht)(t,e,S);const N=W(["root","-g","--prefix",s]),A=l(N,t),B=U(A,t,e),F=L(A,B),St=l(A,F[0][1]),q=R(process.execPath,[St,"--version"],{encoding:"utf-8",timeout:15e3,maxBuffer:2*1024*1024,windowsHide:!0}).trim().replace(/^v(?=\d)/,"");if(q!==e)throw new f("VERSION_MISMATCH",`Staged CLI reported ${q||"<empty>"}, expected ${e}`);const Z=l(c,t);return{npmPrefix:o,packageDir:Z,backupDir:`${u(Z)}${Y}`,stagedPackageDir:A,stagingPrefix:s,registry:k}}catch(p){try{g(s,{recursive:!0,force:!0})}catch{}throw p}}async function te(t,e,r,n){const i=await wt(t,e,r,n,"atomic");try{const a=Zt({packageName:t,version:e,npmPrefix:i.npmPrefix,activePackageDir:i.packageDir,stagedPackageDir:i.stagedPackageDir});m(`atomic package switch succeeded via ${i.registry}; backup=${a.backupDir}`)}finally{try{g(i.stagingPrefix,{recursive:!0,force:!0})}catch{}}}async function $e(t,e,r=_t,n={}){e=O(e);const i=_()?.transaction_id,a=`${t}@${e}`,o=n.activationMode??(process.platform==="win32"?"staged":"atomic");m(`npm install -g ${a} starting (${o} staging with mirror fallback)`);try{if(o==="staged"){const c=await wt(t,e,r,n,o);try{Kt({...c,activationStrategy:process.platform==="win32"?"wrapper-runtime":"service-start"}),process.platform==="win32"&&zt(c,e)}catch(s){try{g(c.stagingPrefix,{recursive:!0,force:!0})}catch{}throw s}return m(`staged package verified via ${c.registry}; waiting for guardian activation`),{activationRequired:!0,packageDir:c.packageDir,backupDir:c.backupDir,stagedPackageDir:c.stagedPackageDir,stagingPrefix:c.stagingPrefix}}else return await te(t,e,r,n),{activationRequired:!1}}catch(c){if(o==="staged"){const p=_();i&&p?.transaction_id===i&&Bt(p)}if(c instanceof f)throw c;const s=c instanceof Error?c.message:String(c);throw m(`npm install failed: ${s}`),/timed out|ETIMEDOUT/i.test(s)?new f("NPM_TIMEOUT",`npm install timed out after ${r/1e3}s (tried all mirrors): ${s}`):s.includes("EACCES")||s.includes("permission denied")?new f("NPM_INSTALL_FAILED",`Permission denied: ${s}`):s.includes("ENOSPC")||s.includes("no space left")?new f("DISK_FULL",`Disk full: ${s}`):s.includes("404")||s.includes("not found")?new f("NPM_INSTALL_FAILED",`Package not found: ${s}`):new f("NPM_INSTALL_FAILED",s)}}function De(t){let e;try{e=X(["root","-g"])}catch{throw new f("VERSION_VERIFY_FAILED","Unable to resolve the global npm package directory")}let r;try{const n=l(e,"grix-connector","package.json"),i=JSON.parse(P(n,"utf-8"));if(i.name!=="grix-connector"||typeof i.version!="string"||!i.version.trim())throw new Error("invalid package metadata");r=i.version.trim().replace(/^v(?=\d)/,"")}catch{throw new f("VERSION_VERIFY_FAILED","Unable to read the installed grix-connector version")}if(r!==t)throw new f("VERSION_MISMATCH",`Installed version ${r} does not match expected ${t}`);return r}async function Ne(t,e){e=O(e);let r=!1;for(const n of await Ut())try{const i=["view",`${t}@${e}`,"version","--registry",n.url,"--no-audit","--no-fund"],a=nt();a&&i.push("--cache",a);const o=M(i),{stdout:c}=await ot(o.command,o.args,{timeout:8e3,windowsHide:!0});if(c.trim())return!0}catch(i){const a=i,o=a.stderr||a.message||String(i);/ETARGET|No matching version|notarget|E404/i.test(o)&&(r=!0)}return!r}async function Ae(t,e,r,n=_t,i={}){r=O(r);const a=`${e}@${r}`,o=l(t,"versions",r);E(l(t,"versions"),{recursive:!0});const c=tt(l(t,"versions",`.stage-${r}-`));m(`installing ${a} into version store under ${c}`);try{const s=i.installWithMirror??rt,{registry:p,registryUrl:k}=await s(a,n,10*1024*1024,{prefix:c,env:{...process.env,GRIX_UPGRADE_STAGING:"1"},ignoreScripts:!0});await(i.verifyIntegrityConsensus??ht)(e,r,k);const y=W(["root","-g","--prefix",c]),N=l(y,e),A=U(N,e,r),B=L(N,A),F=R(process.execPath,[l(N,B[0][1]),"--version"],{encoding:"utf-8",timeout:15e3,maxBuffer:2*1024*1024,windowsHide:!0}).trim().replace(/^v(?=\d)/,"");if(F!==r)throw new f("VERSION_MISMATCH",`Installed CLI reported ${F||"<empty>"}, expected ${r}`);return g(o,{recursive:!0,force:!0}),h(N,o),m(`${a} verified via ${p}; placed at ${o}`),{registry:p,versionDir:o}}catch(s){if(s instanceof f)throw s;const p=s instanceof Error?s.message:String(s);throw m(`version store install failed: ${p}`),/timed out|ETIMEDOUT/i.test(p)?new f("NPM_TIMEOUT",`npm install timed out after ${n/1e3}s (tried all mirrors): ${p}`):p.includes("ENOSPC")||p.includes("no space left")?new f("DISK_FULL",`Disk full: ${p}`):new f("NPM_INSTALL_FAILED",p)}finally{try{g(c,{recursive:!0,force:!0})}catch{}}}export{_t as NPM_INSTALL_TIMEOUT_MS,f as UpgradeError,Ht as acquireNpmPrefixUpgradeLock,_e as archivePendingJournal,we as armWindowsUpgradeHandoff,Se as armWindowsUpgradeRollback,gt as checkDiskSpace,pt as checkNpmAvailable,Ee as cleanupPendingBackup,xe as collectEnvInfo,Zt as commitStagedGlobalPackage,Bt as discardPendingStaging,Ie as finalizePendingTransaction,Pe as getUpgradeLogTail,Ae as installVersionIntoStore,Ne as isVersionAvailable,O as normalizeUpgradeVersion,$e as npmInstall,ge as pendingExists,ve as preflightCheck,zt as prepareStagedWindowsRuntime,_ as readPending,he as readPendingPackageVersion,Yt as releasePendingUpgradeLock,dt as removePending,ye as removeWindowsHandoffFiles,ke as rollbackPendingInstall,D as updatePending,m as upgradeLog,pe as upgradeStatusSnapshot,De as verifyInstalledVersion,ht as verifyMirrorIntegrityConsensus,Vt as windowsRollbackIntentPath,j as windowsWrapperPausePath,me as writePending,Gt as writePendingData};
package/dist/grix.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import x from"node:path";import{writeFileSync as oe}from"node:fs";import{Manager as Pe}from"./manager.js";import{ensureGrixDirs as Ie,initLogger as _e,log as i,installProcessLogRotation as Ce,setConsoleOutput as ae}from"./core/log/index.js";import{HealthServer as ke,bindPortOrFail as ie}from"./core/runtime/index.js";import{writePidFile as Te,removePidFile as H,readDaemonPid as $e}from"./core/runtime/index.js";import{resolveRuntimePaths as K}from"./core/config/index.js";import{supportsNonMitmRelayConfig as Ue}from"./core/config/provider-env.js";import{ServiceManager as se}from"./service/service-manager.js";import{RESTART_EXIT_CODE as Le,UNHEALTHY_EXIT_CODE as Oe,markManagedStop as ce,resolveLauncherStore as X,writeCrashNote as He}from"./core/upgrade/version-store.js";import{migrateWindowsWrapper as V}from"./service/windows-wrapper-migration.js";const Fe=300*1e3;import{killProcessesByCommandLine as Me,isWindowsElevated as Ne,shouldSweepWindowsWrappers as Ge,getProcessCommandLine as Be}from"./service/process-control.js";import{buildServiceID as je}from"./service/service-paths.js";import{acquireDaemonLock as We,createHealthzHolderProbe as Ke,DaemonAlreadyServingError as Xe,isLockHolderSameProcess as Ve,readDaemonLock as q,releaseDaemonLock as F}from"./runtime/daemon-lock.js";import{writeDaemonStatus as G,removeDaemonStatus as qe}from"./runtime/service-state.js";import{readDuplicateDaemonRecord as Ye,recordDuplicateDaemon as Je}from"./runtime/duplicate-daemon.js";import{AdminServer as ze,generateToken as Qe,writeTokenFile as Ze}from"./core/admin/index.js";import{initSentry as et,closeSentry as Y,reportFatal as J}from"./core/observability/sentry.js";import{initProxyManager as tt,getProxyManager as m,relayHostsForClientType as le,RelayStateStore as rt,SecretStore as nt}from"./core/proxy/index.js";import{stopProxyIfNobodyNeedsIt as de,switchAgentRelayMode as z,reconcileRelayStates as pe}from"./core/proxy/relay-orchestration.js";import{setAgentRelayCredential as ue,RelayCredentialError as ge}from"./core/proxy/relay-credential.js";import{RelayFetchError as fe,RelayToggleCoordinator as ot}from"./core/proxy/relay-credential-fetch.js";import{applyHermesProfileRelay as at,clearHermesProfileRelay as it,discoverHermesProfiles as st}from"./core/proxy/hermes-profile-relay.js";import{disableRelayCredential as ct,enableRelayCredential as lt,RelayRouteError as me}from"./core/proxy/relay-credential-router.js";import{upgradeStatusSnapshot as dt}from"./core/upgrade/npm-upgrader.js";import{resolveClientVersion as pt}from"./core/util/client-version.js";import{parseCliArgs as ut}from"./core/util/cli-args.js";const{command:b,flags:E,unknownFlags:Q}=ut(process.argv.slice(2));if(Q.length>0&&(console.error(`Unknown option${Q.length>1?"s":""}: ${Q.map(t=>`--${t}`).join(", ")}
3
- Run \`grix-connector --help\` to see the supported commands and options.`),process.exit(1)),E.version&&(console.log(pt()),process.exit(0)),E.help&&(console.log(`grix-connector \u2014 Unified AI Agent Bridge
2
+ import b from"node:path";import{writeFileSync as oe}from"node:fs";import{Manager as Pe}from"./manager.js";import{ensureGrixDirs as Ie,initLogger as Ce,log as i,installProcessLogRotation as _e,setConsoleOutput as ae}from"./core/log/index.js";import{HealthServer as ke,bindPortOrFail as ie}from"./core/runtime/index.js";import{writePidFile as Te,removePidFile as H,readDaemonPid as $e}from"./core/runtime/index.js";import{killOrphanedClaudeProcesses as Ue}from"./core/runtime/index.js";import{resolveRuntimePaths as K}from"./core/config/index.js";import{supportsNonMitmRelayConfig as Le}from"./core/config/provider-env.js";import{ServiceManager as se}from"./service/service-manager.js";import{RESTART_EXIT_CODE as Oe,UNHEALTHY_EXIT_CODE as He,markManagedStop as ce,resolveLauncherStore as X,writeCrashNote as Fe}from"./core/upgrade/version-store.js";import{migrateWindowsWrapper as V}from"./service/windows-wrapper-migration.js";const Me=300*1e3;import{killProcessesByCommandLine as Ne,isWindowsElevated as Ge,shouldSweepWindowsWrappers as Be,getProcessCommandLine as je}from"./service/process-control.js";import{buildServiceID as We}from"./service/service-paths.js";import{acquireDaemonLock as Ke,createHealthzHolderProbe as Xe,DaemonAlreadyServingError as Ve,isLockHolderSameProcess as qe,readDaemonLock as q,releaseDaemonLock as F}from"./runtime/daemon-lock.js";import{writeDaemonStatus as G,removeDaemonStatus as Ye}from"./runtime/service-state.js";import{readDuplicateDaemonRecord as Je,recordDuplicateDaemon as ze}from"./runtime/duplicate-daemon.js";import{AdminServer as Qe,generateToken as Ze,writeTokenFile as et}from"./core/admin/index.js";import{initSentry as tt,closeSentry as Y,reportFatal as J}from"./core/observability/sentry.js";import{initProxyManager as rt,getProxyManager as m,relayHostsForClientType as le,RelayStateStore as nt,SecretStore as ot}from"./core/proxy/index.js";import{stopProxyIfNobodyNeedsIt as de,switchAgentRelayMode as z,reconcileRelayStates as pe}from"./core/proxy/relay-orchestration.js";import{setAgentRelayCredential as ue,RelayCredentialError as ge}from"./core/proxy/relay-credential.js";import{RelayFetchError as fe,RelayToggleCoordinator as at}from"./core/proxy/relay-credential-fetch.js";import{applyHermesProfileRelay as it,clearHermesProfileRelay as st,discoverHermesProfiles as ct}from"./core/proxy/hermes-profile-relay.js";import{disableRelayCredential as lt,enableRelayCredential as dt,RelayRouteError as me}from"./core/proxy/relay-credential-router.js";import{upgradeStatusSnapshot as pt}from"./core/upgrade/npm-upgrader.js";import{resolveClientVersion as ut}from"./core/util/client-version.js";import{parseCliArgs as gt}from"./core/util/cli-args.js";const{command:P,flags:E,unknownFlags:Q}=gt(process.argv.slice(2));if(Q.length>0&&(console.error(`Unknown option${Q.length>1?"s":""}: ${Q.map(t=>`--${t}`).join(", ")}
3
+ Run \`grix-connector --help\` to see the supported commands and options.`),process.exit(1)),E.version&&(console.log(ut()),process.exit(0)),E.help&&(console.log(`grix-connector \u2014 Unified AI Agent Bridge
4
4
 
5
5
  Usage: grix-connector <command> [options]
6
6
 
@@ -34,10 +34,10 @@ Examples:
34
34
  grix-connector start # Start as system service
35
35
  grix-connector status # Check service status
36
36
  grix-connector restart # Restart the service
37
- `),process.exit(0)),b==="reload"){const t=$e();t||(console.error("reload failed: daemon is not running (no pid file)"),process.exit(1));try{process.kill(t,0)}catch{console.error(`reload failed: daemon process ${t} is not running (stale pid file)`),process.exit(1)}{const c=q(K().daemonLockFile);c&&c.pid===t&&!Ve(c)&&(console.error(`reload failed: pid ${t} has been reused by another process (stale pid file)`),process.exit(1))}try{process.kill(t,"SIGHUP"),console.log(JSON.stringify({ok:!0,signaled:t},null,2)),process.exit(0)}catch(c){console.error(`reload failed: ${c instanceof Error?c.message:c}`),process.exit(1)}}const he=["start","stop","restart","status"];if(b&&he.includes(b)){process.platform==="win32"&&["start","stop","restart"].includes(b)&&!Ne()&&console.warn(`Warning: Not running as administrator. Task Scheduler registration is skipped;
37
+ `),process.exit(0)),P==="reload"){const t=$e();t||(console.error("reload failed: daemon is not running (no pid file)"),process.exit(1));try{process.kill(t,0)}catch{console.error(`reload failed: daemon process ${t} is not running (stale pid file)`),process.exit(1)}{const c=q(K().daemonLockFile);c&&c.pid===t&&!qe(c)&&(console.error(`reload failed: pid ${t} has been reused by another process (stale pid file)`),process.exit(1))}try{process.kill(t,"SIGHUP"),console.log(JSON.stringify({ok:!0,signaled:t},null,2)),process.exit(0)}catch(c){console.error(`reload failed: ${c instanceof Error?c.message:c}`),process.exit(1)}}const he=["start","stop","restart","status"];if(P&&he.includes(P)){process.platform==="win32"&&["start","stop","restart"].includes(P)&&!Ge()&&console.warn(`Warning: Not running as administrator. Task Scheduler registration is skipped;
38
38
  using Startup folder auto-start instead. For full Task Scheduler integration,
39
- right-click the terminal and select "Run as administrator".`);const t=K(),c=E["config-dir"]??(E.profile?x.join(t.configDir,E.profile):void 0),f=x.resolve(process.argv[1]||`${t.rootDir}/dist/grix.js`),g=new se({cliPath:f,nodePath:process.execPath});try{let l;switch(b){case"start":(await g.status({rootDir:t.rootDir})).installed?l=await g.start({rootDir:t.rootDir}):l=await g.install({rootDir:t.rootDir,configDir:c});break;case"stop":l=await g.stop({rootDir:t.rootDir});break;case"restart":(await g.status({rootDir:t.rootDir})).installed?l=await g.restart({rootDir:t.rootDir}):l=await g.install({rootDir:t.rootDir,configDir:c});break;case"status":l=await g.status({rootDir:t.rootDir});{const S=Ye(t.dataDir);S&&(l={...l,duplicate_daemon:S})}break}console.log(JSON.stringify(l,null,2)),process.exit(0)}catch(l){console.error(`${b} failed: ${l instanceof Error?l.message:l}`),process.exit(1)}}else b&&(console.error(`Unknown command: ${b}
40
- Valid commands: ${he.join(", ")}`),process.exit(1));const s=K(),gt=E["config-dir"]??(E.profile?`${s.configDir}/${E.profile}`:void 0),n=new Pe({requestGracefulShutdown:t=>{I(t)},onTransactionSettled:()=>{V({rootDir:s.rootDir,requestGracefulShutdown:()=>{I("wrapper-migration")}}).then(t=>{t.handoffStarted&&i.info("main","Wrapper handoff started after the upgrade transaction settled")}).catch(t=>{i.warn("main",`Post-upgrade wrapper migration failed: ${t instanceof Error?t.message:t}`)})}}),M=new ke,ye=Qe(),P=new ze(ye);let B=!1,T;function j(t,c){const f=X(),g=process.env.GRIX_LAUNCHER_VERSION;if(!f||!g)return;const l=c instanceof Error?c.stack??c.message:String(c);He(f,g,`${t}: ${l}`)}function ft(t){const c=`another grix-connector daemon (pid ${t.pid}) is already serving ${s.rootDir} and answering /healthz; this process yields instead of taking over. A second supervisor is starting duplicate daemons \u2014 check for a leftover wrapper, a duplicate logon task, or a desktop app launching its own copy.`;Je(s.dataDir,{holder_pid:t.pid,holder_command_line:t.command_line,yielded_pid:process.pid,yielded_command_line:Be(process.pid)}),process.stderr.write(`${c}
39
+ right-click the terminal and select "Run as administrator".`);const t=K(),c=E["config-dir"]??(E.profile?b.join(t.configDir,E.profile):void 0),f=b.resolve(process.argv[1]||`${t.rootDir}/dist/grix.js`),g=new se({cliPath:f,nodePath:process.execPath});try{let l;switch(P){case"start":{const h=await g.healLegacyState({rootDir:t.rootDir});(await g.status({rootDir:t.rootDir})).installed?l=await g.start({rootDir:t.rootDir}):l=await g.install({rootDir:t.rootDir,configDir:c??h.configDir}),h.healed&&(l={...l,legacy_heal:h.reasons})}break;case"stop":l=await g.stop({rootDir:t.rootDir});break;case"restart":(await g.status({rootDir:t.rootDir})).installed?l=await g.restart({rootDir:t.rootDir}):l=await g.install({rootDir:t.rootDir,configDir:c});break;case"status":l=await g.status({rootDir:t.rootDir});{const h=Je(t.dataDir);h&&(l={...l,duplicate_daemon:h})}break}console.log(JSON.stringify(l,null,2)),process.exit(0)}catch(l){console.error(`${P} failed: ${l instanceof Error?l.message:l}`),process.exit(1)}}else P&&(console.error(`Unknown command: ${P}
40
+ Valid commands: ${he.join(", ")}`),process.exit(1));const s=K(),ft=E["config-dir"]??(E.profile?`${s.configDir}/${E.profile}`:void 0),n=new Pe({requestGracefulShutdown:t=>{C(t)},onTransactionSettled:()=>{V({rootDir:s.rootDir,requestGracefulShutdown:()=>{C("wrapper-migration")}}).then(t=>{t.handoffStarted&&i.info("main","Wrapper handoff started after the upgrade transaction settled")}).catch(t=>{i.warn("main",`Post-upgrade wrapper migration failed: ${t instanceof Error?t.message:t}`)})}}),M=new ke,ye=Ze(),I=new Qe(ye);let B=!1,T;function j(t,c){const f=X(),g=process.env.GRIX_LAUNCHER_VERSION;if(!f||!g)return;const l=c instanceof Error?c.stack??c.message:String(c);Fe(f,g,`${t}: ${l}`)}function mt(t){const c=`another grix-connector daemon (pid ${t.pid}) is already serving ${s.rootDir} and answering /healthz; this process yields instead of taking over. A second supervisor is starting duplicate daemons \u2014 check for a leftover wrapper, a duplicate logon task, or a desktop app launching its own copy.`;ze(s.dataDir,{holder_pid:t.pid,holder_command_line:t.command_line,yielded_pid:process.pid,yielded_command_line:je(process.pid)}),process.stderr.write(`${c}
41
41
  `),i.warn("main",c);const f=X();f&&ce(f),process.exit(0)}async function we(t){process.stderr.write(t.message+`
42
- `),i.error("main",t.message.replace(/\n/g," \u2014 ")),j("port_bind",t.message);const c=q(s.daemonLockFile);c?.pid===process.pid&&c.instance_id===T&&await G(s.daemonStatusFile,{state:"failed",pid:process.pid,instance_id:T,updated_at:Date.now(),reason:`port_bind_${t.kind}:${t.label}:${t.port}`}).catch(()=>{}),await Y(),F(s.daemonLockFile).catch(()=>{}),H(),process.exit(1)}async function I(t){if(B)return;B=!0,i.info("main",`Received ${t}, shutting down...`),M.markShuttingDown();const c=X();c&&ce(c);const f=t==="upgrade-switch"?Le:t==="launcher-unhealthy"?Oe:null,g=f??2,l=t==="uncaughtException"||t==="unhandledRejection",S=setTimeout(()=>{i.error("main","Shutdown timed out, forcing exit"),F(s.daemonLockFile).catch(()=>{}),H(),process.exit(g)},1e4);try{await n.stop(),await m()?.stop().catch(()=>{}),await P.stop(),await M.stop(),await Y(),await F(s.daemonLockFile),await qe(s.daemonStatusFile).catch(()=>{}),clearTimeout(S),H(),i.info("main","Shutdown complete"),process.exit(f??(l?2:0))}catch(_){i.error("main",`Shutdown error: ${_}`),F(s.daemonLockFile).catch(()=>{}),H(),process.exit(g)}}async function mt(){Ie(),_e(),Ce(s.stdoutLogFile,s.stderrLogFile),ae(!1);try{T=(await We(s.daemonLockFile,s.rootDir,2,{isHolderServing:Ke(s.dataDir)})).instance_id}catch(e){e instanceof Xe&&ft(e.holder),console.error(e instanceof Error?e.message:e),process.exit(1)}if(Ge()){const e=je(s.rootDir,"win32");await Me(e,{platform:"win32"})}if(await et(),Te(),i.info("main",`grix-connector starting (PID ${process.pid})`),await G(s.daemonStatusFile,{state:"starting",pid:process.pid,instance_id:T,updated_at:Date.now()}),process.env.GRIX_LAUNCHER_MANAGED!=="1"&&await new se({cliPath:x.resolve(process.argv[1]??""),nodePath:process.execPath}).adoptLauncher(s.rootDir).then(e=>{e&&i.info("main","Service definition now points at the version-store launcher")}).catch(e=>{i.warn("main",`Launcher adoption failed: ${e instanceof Error?e.message:e}`)}),(await V({rootDir:s.rootDir,requestGracefulShutdown:()=>{I("wrapper-migration")}}).catch(e=>(i.warn("main",`Windows wrapper migration failed: ${e instanceof Error?e.message:e}`),null)))?.handoffStarted)return;process.platform==="win32"&&process.env.GRIX_LAUNCHER_MANAGED!=="1"&&setInterval(()=>{B||V({rootDir:s.rootDir,requestGracefulShutdown:()=>{I("wrapper-migration")}}).then(o=>{o.handoffStarted&&i.info("main","Wrapper handoff started by the periodic migration retry")}).catch(o=>{i.warn("main",`Periodic wrapper migration failed: ${o instanceof Error?o.message:o}`)})},Fe).unref();const c=parseInt(E["health-port"]??process.env.GRIX_HEALTH_PORT??"19579",10);{const e=await ie({label:"health",port:c,envVar:"GRIX_HEALTH_PORT",cliFlag:"health-port",start:o=>M.start(o)});e&&await we(e)}const f=x.join(s.dataDir,"health-port");oe(f,String(c),"utf-8"),process.on("SIGINT",()=>I("SIGINT")),process.on("SIGTERM",()=>I("SIGTERM")),process.on("SIGHUP",()=>{B||(i.info("main","Received SIGHUP, reloading config..."),n.reload().then(e=>i.info("main",`reload done: ${JSON.stringify(e)}`)).catch(e=>i.error("main",`reload failed: ${e instanceof Error?e.message:e}`)))});let g="",l=0,S;process.on("uncaughtException",e=>{const o=e instanceof Error?e.stack??e.message:String(e);o===g?(l++,(l<=3||l%100===0)&&i.error("main",`Uncaught exception (x${l}): ${o}`)):(l>3&&i.error("main",`Previous exception repeated ${l} times total`),g=o,l=1,i.error("main",`Uncaught exception: ${e instanceof Error?e.stack:e}`),S||(S=setTimeout(()=>{l>3&&i.error("main",`Previous exception repeated ${l} times total`),g="",l=0,S=void 0},1e4).unref())),!Se(e)&&(J(e,"uncaughtException"),j("uncaughtException",e),I("uncaughtException"))}),process.on("unhandledRejection",e=>{i.error("main",`Unhandled rejection: ${e}`),!Se(e)&&(J(e,"unhandledRejection"),j("unhandledRejection",e),I("unhandledRejection"))});const _=tt(s.dataDir);await _.clearRuntimeState();const C=new rt(x.join(s.dataDir,"proxy","relay-state.json"),{warn:e=>i.warn("main",e)}),N=new nt(x.join(s.dataDir,"secrets"));n.setRelaySecretResolver(e=>N.get(e));const $={hasDirectConfig:e=>n.hasDirectRelayConfig(e),clearDirectConfig:(e,o)=>n.setAgentProvider(e,void 0,{deferRestart:o?.deferRestart===!0})},W=()=>({pm:m(),agents:n,direct:$,store:C,secrets:N}),U=_.getRelayAgents();if(U.length>0)try{await _.startIfAnyRelayAgent(),i.info("main",`mitm proxy started for relay agents: ${U.join(", ")} (per-agent; others stay direct)`)}catch(e){i.error("main",`mitm proxy FAILED to start (relay agents: ${U.join(", ")}): ${e}. These agents will REFUSE to start (they must not silently fall back to your own account). Fix the proxy or turn relay off for them. Other agents are unaffected.`)}else i.info("main",'mitm proxy idle (no agent has Grix relay enabled; enable: PUT /api/proxy/agents/<name>/enabled {"enabled":true})');let L=null;if(m()){const e=async()=>{const r=m();r&&await de(r,n)&&i.info("main","no agent uses Grix relay anymore; mitm proxy stopped")};n.setRelayEnvSettledHandler(()=>{const r=m();r&&pe(r,$,C),e().catch(a=>{i.warn("main",`failed to stop idle mitm proxy: ${a}`)})});const o=()=>{const r=m(),a=r.getRelayAgents(),d=r.getDegradedRelayAgents();return{enabled:a.length>0,relayAgents:a,staleRelayAgents:n.getAgentsWithStaleRelayEnv(),degradedRelayAgents:d,relayState:C.list(),runtime:r.getRuntimeInfo(),config:r.getConfigSnapshot()}},p=new ot,A={status:()=>o(),setAgentRelay:async(r,a,d)=>{const u=m(),R={info:y=>i.info("main",y),warn:y=>i.warn("main",y)},w=y=>n.getAgentsStatus().find(v=>v.name===y),h=w(r);if(!a){p.cancel(r);const y=await p.runExclusive(r,async()=>{const v=await z(W(),R,{agentName:r,target:"off"});return{...o(),restarted:v.restarted,busy:v.busy,...v.pending?{pending:v.pending}:{}}});return n.reportRelayStateLocalChange(r),y}if(!h)throw Object.assign(new Error(`Agent "${r}" not found`),{code:"NOT_FOUND"});if(!Ue(h.clientType)&&le(h.clientType).length===0)throw Object.assign(new Error(`client type "${h.clientType??""}" does not support Grix relay`),{code:"UNSUPPORTED_CLIENT_TYPE"});const O=await p.runExclusive(r,async()=>{const y=p.epoch(r),v=w(r);if(!v)throw Object.assign(new Error(`Agent "${r}" not found`),{code:"NOT_FOUND"});const te=String(v.agentId??"").trim();if(!te)throw new ge(`local agent "${r}" has no agent_id registered`,"MISSING_AGENT_ID");const re=p.waitCancelled(r,y);let k;try{k=await Promise.race([n.fetchRelayCredential(r,{model:d}),re.promise.then(()=>{throw new fe("relay enable cancelled by a later disable","CANCELLED")})])}finally{re.dispose()}p.assertCurrent(r,y);const{restarted:Ae,busy:ve,pending:ne}=await ue(u,n,w,R,{localName:r,agentId:te,virtualKey:k.apiKey,anthropicBaseUrl:k.anthropicBaseUrl,openaiBaseUrl:k.openaiBaseUrl,model:d,...k.directRelay!==void 0?{directRelay:k.directRelay}:{}},(De,xe,be)=>n.setAgentProvider(De,xe,{deferRestart:be?.deferRestart===!0}),{store:C,secrets:N,direct:$});if(p.epoch(r)!==y)throw await z(W(),R,{agentName:r,target:"off"}),new fe("relay enable cancelled by a later disable","CANCELLED");return{...o(),restarted:Ae,busy:ve,...ne?{pending:ne}:{}}});return n.reportRelayStateLocalChange(r),O},setAgentRelayCredential:async(r,a)=>{const d=m(),u=n.getAgentsStatus(),{restarted:R,busy:w,pending:h}=await ue(d,n,D=>u.find(O=>O.name===D),{info:D=>i.info("main",D),warn:D=>i.warn("main",D)},{localName:r,agentId:a.agentId,virtualKey:a.virtualKey,anthropicBaseUrl:a.anthropicBaseUrl,openaiBaseUrl:a.openaiBaseUrl,model:a.model,...a.directRelay!==void 0?{directRelay:a.directRelay}:{}},(D,O,y)=>n.setAgentProvider(D,O,{deferRestart:y?.deferRestart===!0}),{store:C,secrets:N,direct:$});return n.reportRelayStateLocalChange(r),{...o(),restarted:R,busy:w,...h?{pending:h}:{}}},disableAll:async()=>{const r=m(),a={info:u=>i.info("main",u),warn:u=>i.warn("main",u)},d=new Set([...r.getRelayAgents(),...n.getDirectRelayConfigAgentNames(),...Object.keys(C.list())]);for(const u of d)p.cancel(u);for(const u of d)await p.runExclusive(u,()=>z(W(),a,{agentName:u,target:"off"})),n.reportRelayStateLocalChange(u);return await de(r,n),o()},setRoute:async(r,a)=>{const d=a,u=m();return u.setRoute({routeKey:r,targetBaseUrl:d.targetBaseUrl,...d.headers?{headers:d.headers}:{},...d.model?{model:d.model}:{},...d.modelMap?{modelMap:d.modelMap}:{},...d.passthrough?{passthrough:!0}:{},...d.relayOnly?{relayOnly:!0}:{},...d.codexResponsesToChat?{codexResponsesToChat:!0}:{}}),await u.persistConfig(),o()},deleteRoute:async r=>{const a=m();a.deleteRoute(r),await a.persistConfig()},setDefaultRoute:async r=>{const a=m();return a.setDefaultRouteKey(r),await a.persistConfig(),o()},setInterceptHosts:async r=>{const a=m();return a.setInterceptHosts(r),await a.persistConfig(),o()}};L=A,P.setProxyHandler(A),n.setRelayStateApplier({getLocalState:r=>{const a=m();if(a.getRelayAgents().includes(r)){const u=n.getAgentsStatus().find(h=>h.name===r),R=`grix-gateway-${String(u?.clientType??"").trim().toLowerCase()}`;return{enabled:!0,model:a.getConfigSnapshot().routes?.find(h=>h.routeKey===R)?.model?.trim()||void 0}}const d=n.getAgentProvider(r);return d?.api_key?.trim()?{enabled:!0,model:d.model?.trim()||void 0}:{enabled:!1}},applyEnable:async(r,a,d)=>{const u=n.getAgentsStatus().find(h=>h.name===r),R=String(u?.agentId??"").trim();if(!R)throw new ge(`local agent "${r}" has no agent_id registered`,"MISSING_AGENT_ID");const w=await A.setAgentRelayCredential(r,{agentId:R,virtualKey:a.apiKey,anthropicBaseUrl:a.anthropicBaseUrl,openaiBaseUrl:a.openaiBaseUrl,model:d??a.model,...a.directRelay!==void 0?{directRelay:a.directRelay}:{}});return{restarted:w.restarted===!0,busy:w.busy===!0,...w.pending?{pending:w.pending}:{}}},applyDisable:async r=>{const a=await A.setAgentRelay(r,!1);return{restarted:a.restarted===!0,busy:a.busy===!0,...a.pending?{pending:a.pending}:{}}}})}if(M.setStatusProvider(()=>n.getAgentsStatus()),M.setMetaProvider(()=>{const e=n.getAgentsStatus();return{ws:{connected:e.filter(o=>o.wsConnected===!0).length,total:e.length},upgrade:dt()}}),await n.start(gt),U.length>0){const e=n.getAgentsStatus();for(const o of U){const p=e.find(r=>r.name===o);if(!p)continue;const A=le(p.clientType);A.length!==0&&await _.setAgentRelayEnabled(o,!0,{relayHosts:A}).catch(r=>{i.warn("main",`failed to reconcile relay hosts for "${o}": ${r}`)})}}pe(_,$,C);const Z=parseInt(E["admin-port"]??process.env.GRIX_ADMIN_PORT??"19580",10);P.setAgentHandler({list:()=>n.getAgentsStatus(),add:e=>n.addAgent(e),remove:e=>n.removeAgent(e),restart:e=>n.restartAgent(e),reload:()=>n.reload()}),P.setUpgradeHandler({check:()=>n.checkUpgrade(),trigger:()=>n.triggerUpgrade()}),P.setProbeHandler({probeAll:e=>n.probeAll(e),probeOne:(e,o)=>n.probeOne(e,o)}),P.setInstallHandler({listInstallable:()=>n.listInstallable(),installAgent:e=>n.installAgent(e),getInstallProgress:e=>n.getInstallProgress(e)});const ee={listAgents:()=>n.getAgentsStatus(),enableManaged:(e,o,p)=>{if(!L)throw new me(`agent "${e}" needs the MITM proxy for relay, but it is not running`,"PROXY_UNAVAILABLE");return L.setAgentRelayCredential(e,{agentId:o,virtualKey:p.virtualKey,anthropicBaseUrl:p.anthropicBaseUrl,openaiBaseUrl:p.openaiBaseUrl,model:p.model,...p.directRelay!==void 0?{directRelay:p.directRelay}:{}})},disableManaged:e=>{if(!L)throw new me(`agent "${e}" needs the MITM proxy for relay, but it is not running`,"PROXY_UNAVAILABLE");return L.setAgentRelay(e,!1)},enableHermesProfile:(e,o)=>at({agentId:e,baseUrl:o.openaiBaseUrl??"",virtualKey:o.virtualKey,model:o.model??""}),disableHermesProfile:e=>it(e)};P.setRelayCredentialHandler({listHermesProfiles:()=>st(),enable:async(e,o)=>{const{target:p,result:A}=await lt(ee,e,o);return{target:p,...A}},disable:async e=>{const{target:o,result:p}=await ct(ee,e);return{target:o,...p}}});{const e=await ie({label:"admin",port:Z,envVar:"GRIX_ADMIN_PORT",cliFlag:"admin-port",start:o=>P.start(o)});e&&await we(e)}const Re=x.join(s.dataDir,"admin-token"),Ee=x.join(s.dataDir,"admin-port");Ze(Re,ye),oe(Ee,String(Z),"utf-8"),await G(s.daemonStatusFile,{state:"running",pid:process.pid,instance_id:T,updated_at:Date.now()}),process.send&&process.send("ready"),i.info("main","grix-connector ready")}mt().catch(async t=>{const c=`Fatal: ${t instanceof Error?t.stack??t.message:String(t)}`;try{process.stderr.write(`${c}
43
- `)}catch{}ae(!1),i.error("main",c),J(t,"startup"),j("startup",t);const f=q(s.daemonLockFile);f?.pid===process.pid&&f.instance_id===T&&await G(s.daemonStatusFile,{state:"failed",pid:process.pid,instance_id:f.instance_id,updated_at:Date.now(),reason:"startup_error"}).catch(()=>{}),await Y(),F(s.daemonLockFile).catch(()=>{}),H(),process.exit(1)});const ht=new Set(["ECONNRESET","ECONNREFUSED","ETIMEDOUT","EPIPE","EAI_AGAIN","ENOTFOUND","EHOSTUNREACH","ENETUNREACH","EIO"]);function Se(t){return t instanceof Error&&"code"in t?ht.has(t.code):!1}
42
+ `),i.error("main",t.message.replace(/\n/g," \u2014 ")),j("port_bind",t.message);const c=q(s.daemonLockFile);c?.pid===process.pid&&c.instance_id===T&&await G(s.daemonStatusFile,{state:"failed",pid:process.pid,instance_id:T,updated_at:Date.now(),reason:`port_bind_${t.kind}:${t.label}:${t.port}`}).catch(()=>{}),await Y(),F(s.daemonLockFile).catch(()=>{}),H(),process.exit(1)}async function C(t){if(B)return;B=!0,i.info("main",`Received ${t}, shutting down...`),M.markShuttingDown();const c=X();c&&ce(c);const f=t==="upgrade-switch"?Oe:t==="launcher-unhealthy"?He:null,g=f??2,l=t==="uncaughtException"||t==="unhandledRejection",h=setTimeout(()=>{i.error("main","Shutdown timed out, forcing exit"),F(s.daemonLockFile).catch(()=>{}),H(),process.exit(g)},1e4);try{await n.stop(),await m()?.stop().catch(()=>{}),await I.stop(),await M.stop(),await Y(),await F(s.daemonLockFile),await Ye(s.daemonStatusFile).catch(()=>{}),clearTimeout(h),H(),i.info("main","Shutdown complete"),process.exit(f??(l?2:0))}catch(v){i.error("main",`Shutdown error: ${v}`),F(s.daemonLockFile).catch(()=>{}),H(),process.exit(g)}}async function ht(){Ie(),Ce(),_e(s.stdoutLogFile,s.stderrLogFile),ae(!1);try{T=(await Ke(s.daemonLockFile,s.rootDir,2,{isHolderServing:Xe(s.dataDir)})).instance_id}catch(e){e instanceof Ve&&mt(e.holder),console.error(e instanceof Error?e.message:e),process.exit(1)}if(Be()){const e=We(s.rootDir,"win32");await Ne(e,{platform:"win32"})}await tt(),Te(),i.info("main",`grix-connector starting (PID ${process.pid})`);try{Ue({info:e=>i.info("main",e),warn:e=>i.warn("main",e)})}catch(e){i.warn("main",`orphan Claude cleanup failed: ${e instanceof Error?e.message:String(e)}`)}if(await G(s.daemonStatusFile,{state:"starting",pid:process.pid,instance_id:T,updated_at:Date.now()}),process.env.GRIX_LAUNCHER_MANAGED!=="1"&&await new se({cliPath:b.resolve(process.argv[1]??""),nodePath:process.execPath}).adoptLauncher(s.rootDir).then(e=>{e&&i.info("main","Service definition now points at the version-store launcher")}).catch(e=>{i.warn("main",`Launcher adoption failed: ${e instanceof Error?e.message:e}`)}),(await V({rootDir:s.rootDir,requestGracefulShutdown:()=>{C("wrapper-migration")}}).catch(e=>(i.warn("main",`Windows wrapper migration failed: ${e instanceof Error?e.message:e}`),null)))?.handoffStarted)return;process.platform==="win32"&&process.env.GRIX_LAUNCHER_MANAGED!=="1"&&setInterval(()=>{B||V({rootDir:s.rootDir,requestGracefulShutdown:()=>{C("wrapper-migration")}}).then(o=>{o.handoffStarted&&i.info("main","Wrapper handoff started by the periodic migration retry")}).catch(o=>{i.warn("main",`Periodic wrapper migration failed: ${o instanceof Error?o.message:o}`)})},Me).unref();const c=parseInt(E["health-port"]??process.env.GRIX_HEALTH_PORT??"19579",10);{const e=await ie({label:"health",port:c,envVar:"GRIX_HEALTH_PORT",cliFlag:"health-port",start:o=>M.start(o)});e&&await we(e)}const f=b.join(s.dataDir,"health-port");oe(f,String(c),"utf-8"),process.on("SIGINT",()=>C("SIGINT")),process.on("SIGTERM",()=>C("SIGTERM")),process.on("SIGHUP",()=>{B||(i.info("main","Received SIGHUP, reloading config..."),n.reload().then(e=>i.info("main",`reload done: ${JSON.stringify(e)}`)).catch(e=>i.error("main",`reload failed: ${e instanceof Error?e.message:e}`)))});let g="",l=0,h;process.on("uncaughtException",e=>{const o=e instanceof Error?e.stack??e.message:String(e);o===g?(l++,(l<=3||l%100===0)&&i.error("main",`Uncaught exception (x${l}): ${o}`)):(l>3&&i.error("main",`Previous exception repeated ${l} times total`),g=o,l=1,i.error("main",`Uncaught exception: ${e instanceof Error?e.stack:e}`),h||(h=setTimeout(()=>{l>3&&i.error("main",`Previous exception repeated ${l} times total`),g="",l=0,h=void 0},1e4).unref())),!Se(e)&&(J(e,"uncaughtException"),j("uncaughtException",e),C("uncaughtException"))}),process.on("unhandledRejection",e=>{i.error("main",`Unhandled rejection: ${e}`),!Se(e)&&(J(e,"unhandledRejection"),j("unhandledRejection",e),C("unhandledRejection"))});const v=rt(s.dataDir);await v.clearRuntimeState();const _=new nt(b.join(s.dataDir,"proxy","relay-state.json"),{warn:e=>i.warn("main",e)}),N=new ot(b.join(s.dataDir,"secrets"));n.setRelaySecretResolver(e=>N.get(e));const $={hasDirectConfig:e=>n.hasDirectRelayConfig(e),clearDirectConfig:(e,o)=>n.setAgentProvider(e,void 0,{deferRestart:o?.deferRestart===!0})},W=()=>({pm:m(),agents:n,direct:$,store:_,secrets:N}),U=v.getRelayAgents();if(U.length>0)try{await v.startIfAnyRelayAgent(),i.info("main",`mitm proxy started for relay agents: ${U.join(", ")} (per-agent; others stay direct)`)}catch(e){i.error("main",`mitm proxy FAILED to start (relay agents: ${U.join(", ")}): ${e}. These agents will REFUSE to start (they must not silently fall back to your own account). Fix the proxy or turn relay off for them. Other agents are unaffected.`)}else i.info("main",'mitm proxy idle (no agent has Grix relay enabled; enable: PUT /api/proxy/agents/<name>/enabled {"enabled":true})');let L=null;if(m()){const e=async()=>{const r=m();r&&await de(r,n)&&i.info("main","no agent uses Grix relay anymore; mitm proxy stopped")};n.setRelayEnvSettledHandler(()=>{const r=m();r&&pe(r,$,_),e().catch(a=>{i.warn("main",`failed to stop idle mitm proxy: ${a}`)})});const o=()=>{const r=m(),a=r.getRelayAgents(),d=r.getDegradedRelayAgents();return{enabled:a.length>0,relayAgents:a,staleRelayAgents:n.getAgentsWithStaleRelayEnv(),degradedRelayAgents:d,relayState:_.list(),runtime:r.getRuntimeInfo(),config:r.getConfigSnapshot()}},p=new at,D={status:()=>o(),setAgentRelay:async(r,a,d)=>{const u=m(),R={info:w=>i.info("main",w),warn:w=>i.warn("main",w)},S=w=>n.getAgentsStatus().find(A=>A.name===w),y=S(r);if(!a){p.cancel(r);const w=await p.runExclusive(r,async()=>{const A=await z(W(),R,{agentName:r,target:"off"});return{...o(),restarted:A.restarted,busy:A.busy,...A.pending?{pending:A.pending}:{}}});return n.reportRelayStateLocalChange(r),w}if(!y)throw Object.assign(new Error(`Agent "${r}" not found`),{code:"NOT_FOUND"});if(!Le(y.clientType)&&le(y.clientType).length===0)throw Object.assign(new Error(`client type "${y.clientType??""}" does not support Grix relay`),{code:"UNSUPPORTED_CLIENT_TYPE"});const O=await p.runExclusive(r,async()=>{const w=p.epoch(r),A=S(r);if(!A)throw Object.assign(new Error(`Agent "${r}" not found`),{code:"NOT_FOUND"});const te=String(A.agentId??"").trim();if(!te)throw new ge(`local agent "${r}" has no agent_id registered`,"MISSING_AGENT_ID");const re=p.waitCancelled(r,w);let k;try{k=await Promise.race([n.fetchRelayCredential(r,{model:d}),re.promise.then(()=>{throw new fe("relay enable cancelled by a later disable","CANCELLED")})])}finally{re.dispose()}p.assertCurrent(r,w);const{restarted:De,busy:Ae,pending:ne}=await ue(u,n,S,R,{localName:r,agentId:te,virtualKey:k.apiKey,anthropicBaseUrl:k.anthropicBaseUrl,openaiBaseUrl:k.openaiBaseUrl,model:d,...k.directRelay!==void 0?{directRelay:k.directRelay}:{}},(ve,xe,be)=>n.setAgentProvider(ve,xe,{deferRestart:be?.deferRestart===!0}),{store:_,secrets:N,direct:$});if(p.epoch(r)!==w)throw await z(W(),R,{agentName:r,target:"off"}),new fe("relay enable cancelled by a later disable","CANCELLED");return{...o(),restarted:De,busy:Ae,...ne?{pending:ne}:{}}});return n.reportRelayStateLocalChange(r),O},setAgentRelayCredential:async(r,a)=>{const d=m(),u=n.getAgentsStatus(),{restarted:R,busy:S,pending:y}=await ue(d,n,x=>u.find(O=>O.name===x),{info:x=>i.info("main",x),warn:x=>i.warn("main",x)},{localName:r,agentId:a.agentId,virtualKey:a.virtualKey,anthropicBaseUrl:a.anthropicBaseUrl,openaiBaseUrl:a.openaiBaseUrl,model:a.model,...a.directRelay!==void 0?{directRelay:a.directRelay}:{}},(x,O,w)=>n.setAgentProvider(x,O,{deferRestart:w?.deferRestart===!0}),{store:_,secrets:N,direct:$});return n.reportRelayStateLocalChange(r),{...o(),restarted:R,busy:S,...y?{pending:y}:{}}},disableAll:async()=>{const r=m(),a={info:u=>i.info("main",u),warn:u=>i.warn("main",u)},d=new Set([...r.getRelayAgents(),...n.getDirectRelayConfigAgentNames(),...Object.keys(_.list())]);for(const u of d)p.cancel(u);for(const u of d)await p.runExclusive(u,()=>z(W(),a,{agentName:u,target:"off"})),n.reportRelayStateLocalChange(u);return await de(r,n),o()},setRoute:async(r,a)=>{const d=a,u=m();return u.setRoute({routeKey:r,targetBaseUrl:d.targetBaseUrl,...d.headers?{headers:d.headers}:{},...d.model?{model:d.model}:{},...d.modelMap?{modelMap:d.modelMap}:{},...d.passthrough?{passthrough:!0}:{},...d.relayOnly?{relayOnly:!0}:{},...d.codexResponsesToChat?{codexResponsesToChat:!0}:{}}),await u.persistConfig(),o()},deleteRoute:async r=>{const a=m();a.deleteRoute(r),await a.persistConfig()},setDefaultRoute:async r=>{const a=m();return a.setDefaultRouteKey(r),await a.persistConfig(),o()},setInterceptHosts:async r=>{const a=m();return a.setInterceptHosts(r),await a.persistConfig(),o()}};L=D,I.setProxyHandler(D),n.setRelayStateApplier({getLocalState:r=>{const a=m();if(a.getRelayAgents().includes(r)){const u=n.getAgentsStatus().find(y=>y.name===r),R=`grix-gateway-${String(u?.clientType??"").trim().toLowerCase()}`;return{enabled:!0,model:a.getConfigSnapshot().routes?.find(y=>y.routeKey===R)?.model?.trim()||void 0}}const d=n.getAgentProvider(r);return d?.api_key?.trim()?{enabled:!0,model:d.model?.trim()||void 0}:{enabled:!1}},applyEnable:async(r,a,d)=>{const u=n.getAgentsStatus().find(y=>y.name===r),R=String(u?.agentId??"").trim();if(!R)throw new ge(`local agent "${r}" has no agent_id registered`,"MISSING_AGENT_ID");const S=await D.setAgentRelayCredential(r,{agentId:R,virtualKey:a.apiKey,anthropicBaseUrl:a.anthropicBaseUrl,openaiBaseUrl:a.openaiBaseUrl,model:d??a.model,...a.directRelay!==void 0?{directRelay:a.directRelay}:{}});return{restarted:S.restarted===!0,busy:S.busy===!0,...S.pending?{pending:S.pending}:{}}},applyDisable:async r=>{const a=await D.setAgentRelay(r,!1);return{restarted:a.restarted===!0,busy:a.busy===!0,...a.pending?{pending:a.pending}:{}}}})}if(M.setStatusProvider(()=>n.getAgentsStatus()),M.setMetaProvider(()=>{const e=n.getAgentsStatus();return{ws:{connected:e.filter(o=>o.wsConnected===!0).length,total:e.length},upgrade:pt()}}),await n.start(ft),U.length>0){const e=n.getAgentsStatus();for(const o of U){const p=e.find(r=>r.name===o);if(!p)continue;const D=le(p.clientType);D.length!==0&&await v.setAgentRelayEnabled(o,!0,{relayHosts:D}).catch(r=>{i.warn("main",`failed to reconcile relay hosts for "${o}": ${r}`)})}}pe(v,$,_);const Z=parseInt(E["admin-port"]??process.env.GRIX_ADMIN_PORT??"19580",10);I.setAgentHandler({list:()=>n.getAgentsStatus(),add:e=>n.addAgent(e),remove:e=>n.removeAgent(e),restart:e=>n.restartAgent(e),reload:()=>n.reload()}),I.setUpgradeHandler({check:()=>n.checkUpgrade(),trigger:()=>n.triggerUpgrade()}),I.setProbeHandler({probeAll:e=>n.probeAll(e),probeOne:(e,o)=>n.probeOne(e,o)}),I.setInstallHandler({listInstallable:()=>n.listInstallable(),installAgent:e=>n.installAgent(e),getInstallProgress:e=>n.getInstallProgress(e)});const ee={listAgents:()=>n.getAgentsStatus(),enableManaged:(e,o,p)=>{if(!L)throw new me(`agent "${e}" needs the MITM proxy for relay, but it is not running`,"PROXY_UNAVAILABLE");return L.setAgentRelayCredential(e,{agentId:o,virtualKey:p.virtualKey,anthropicBaseUrl:p.anthropicBaseUrl,openaiBaseUrl:p.openaiBaseUrl,model:p.model,...p.directRelay!==void 0?{directRelay:p.directRelay}:{}})},disableManaged:e=>{if(!L)throw new me(`agent "${e}" needs the MITM proxy for relay, but it is not running`,"PROXY_UNAVAILABLE");return L.setAgentRelay(e,!1)},enableHermesProfile:(e,o)=>it({agentId:e,baseUrl:o.openaiBaseUrl??"",virtualKey:o.virtualKey,model:o.model??""}),disableHermesProfile:e=>st(e)};I.setRelayCredentialHandler({listHermesProfiles:()=>ct(),enable:async(e,o)=>{const{target:p,result:D}=await dt(ee,e,o);return{target:p,...D}},disable:async e=>{const{target:o,result:p}=await lt(ee,e);return{target:o,...p}}});{const e=await ie({label:"admin",port:Z,envVar:"GRIX_ADMIN_PORT",cliFlag:"admin-port",start:o=>I.start(o)});e&&await we(e)}const Re=b.join(s.dataDir,"admin-token"),Ee=b.join(s.dataDir,"admin-port");et(Re,ye),oe(Ee,String(Z),"utf-8"),await G(s.daemonStatusFile,{state:"running",pid:process.pid,instance_id:T,updated_at:Date.now()}),process.send&&process.send("ready"),i.info("main","grix-connector ready")}ht().catch(async t=>{const c=`Fatal: ${t instanceof Error?t.stack??t.message:String(t)}`;try{process.stderr.write(`${c}
43
+ `)}catch{}ae(!1),i.error("main",c),J(t,"startup"),j("startup",t);const f=q(s.daemonLockFile);f?.pid===process.pid&&f.instance_id===T&&await G(s.daemonStatusFile,{state:"failed",pid:process.pid,instance_id:f.instance_id,updated_at:Date.now(),reason:"startup_error"}).catch(()=>{}),await Y(),F(s.daemonLockFile).catch(()=>{}),H(),process.exit(1)});const yt=new Set(["ECONNRESET","ECONNREFUSED","ETIMEDOUT","EPIPE","EAI_AGAIN","ENOTFOUND","EHOSTUNREACH","ENETUNREACH","EIO"]);function Se(t){return t instanceof Error&&"code"in t?yt.has(t.code):!1}
@@ -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};
@@ -0,0 +1,2 @@
1
+ import n from"node:fs";import a from"node:path";import{LAUNCHER_FILE as p,launcherPath as l,readPointer as c,versionEntry as h}from"../core/upgrade/version-store.js";import{WINDOWS_WRAPPER_TEMPLATE_VERSION as s}from"./platform-adapter.js";function m(e){const t=e.record;if(!t)return{heal:!1,reasons:[]};if(e.upgradePending)return{heal:!1,reasons:[]};const r=[];return t.node_path&&!e.nodePathExists&&r.push(`node_path no longer exists: ${t.node_path}`),e.expectedLauncherPath?t.cli_path!==e.expectedLauncherPath?r.push(`service entry is not the current launcher: ${t.cli_path||"(empty)"}`):e.launcherStoreUsable||r.push("launcher store is incomplete (missing launcher.mjs or current version)"):t.cli_path&&!e.cliPathExists&&r.push(`cli_path no longer exists: ${t.cli_path}`),e.definitionCliPath!==null&&t.cli_path&&e.definitionCliPath!==t.cli_path&&r.push(`service definition points elsewhere: ${e.definitionCliPath}`),e.platform==="win32"&&(e.windowsWrapperTemplate<0?r.push("windows wrapper is missing"):e.windowsWrapperTemplate<s&&r.push(`windows wrapper is stale (template ${e.windowsWrapperTemplate} < ${s})`),t.definition_path.startsWith("task:")&&e.windowsStartupShortcutExists&&r.push("both a logon task and a Startup shortcut are registered")),{heal:r.length>0,reasons:r}}function g(e){if(!n.existsSync(l(e)))return!1;const t=c(e,"current");return!!t&&n.existsSync(h(e,t))}function w(e){return a.basename(e)===p}function _(e){const t=e.match(/-EncodedCommand\s+([A-Za-z0-9+/=]+)/)?.[1];if(!t)return null;let r="";try{r=Buffer.from(t,"base64").toString("utf16le")}catch{return null}const i=r.match(/cli_path = '((?:[^']|'')*)'/);return i?i[1].replace(/''/g,"'"):null}function S(e,t){try{const r=a.join(e,"log");n.mkdirSync(r,{recursive:!0}),n.appendFileSync(a.join(r,"upgrade.log"),`[${new Date().toISOString()}] legacy-heal: ${t}
2
+ `)}catch{}}export{S as appendLegacyHealLog,w as isLauncherEntry,g as isLauncherStoreUsable,_ as parseWrapperCliPath,m as planLegacyStateHeal};
@@ -52,7 +52,7 @@ StandardError=append:${_(t.stderrPath)}
52
52
 
53
53
  [Install]
54
54
  WantedBy=default.target
55
- `}function x(t,e){const n=t.replace(/[^a-zA-Z0-9._-]/g,"_");return c.join(e,".grix",`${n}-wrapper.vbs`)}function M(t,e){const n=t.replace(/[^a-zA-Z0-9._-]/g,"_"),r=c.join(e,"AppData","Roaming","Microsoft","Windows","Start Menu","Programs","Startup");return c.join(r,`${n}.lnk`)}function h(t){return String(t??"").replace(/"/g,'""')}function A(t){return`'${String(t??"").replace(/'/g,"''")}'`}function lt(t,e){const n=c.join(c.dirname(t),c.basename(e).replace(/\.lnk$/,"-create.vbs")),r=["Option Explicit","Dim shell, shortcut",'Set shell = CreateObject("WScript.Shell")',`Set shortcut = shell.CreateShortcut("${h(e)}")`,'shortcut.TargetPath = "wscript.exe"',`shortcut.Arguments = "//B //NoLogo \\"${h(t)}\\""`,"shortcut.WindowStyle = 7","shortcut.Save",""].join(`\r
55
+ `}function x(t,e){const n=t.replace(/[^a-zA-Z0-9._-]/g,"_");return c.join(e,".grix",`${n}-wrapper.vbs`)}function M(t,e){const n=t.replace(/[^a-zA-Z0-9._-]/g,"_"),r=c.join(e,"AppData","Roaming","Microsoft","Windows","Start Menu","Programs","Startup");return c.join(r,`${n}.lnk`)}function p(t){return String(t??"").replace(/"/g,'""')}function A(t){return`'${String(t??"").replace(/'/g,"''")}'`}function lt(t,e){const n=c.join(c.dirname(t),c.basename(e).replace(/\.lnk$/,"-create.vbs")),r=["Option Explicit","Dim shell, shortcut",'Set shell = CreateObject("WScript.Shell")',`Set shortcut = shell.CreateShortcut("${p(e)}")`,'shortcut.TargetPath = "wscript.exe"',`shortcut.Arguments = "//B //NoLogo \\"${p(t)}\\""`,"shortcut.WindowStyle = 7","shortcut.Save",""].join(`\r
56
56
  `);return{path:n,content:r}}async function ut(t){const{wrapperPath:e,shortcutPath:n,runCommand:r}=t,i=lt(e,n);for(let o=1;o<=3;o+=1){try{await $(i.path,i.content,"utf8"),await r("wscript.exe",[i.path,"//B","//NoLogo"])}catch{}finally{await y(i.path,{force:!0}).catch(()=>{})}if(b(n))return!0}return console.warn(`Warning: failed to create Windows logon-autostart shortcut at ${n}. The daemon will NOT start automatically after the next reboot/logon. Check Controlled Folder Access / antivirus blocking writes to the Startup folder, or create the shortcut manually.`),!1}async function dt(t){try{const e=await t("whoami",["/user","/fo","csv","/nh"],{allowFailure:!0}),n=String(e?.stdout??"").match(/"(S-1-[0-9-]+)"/);if(n)return n[1]}catch{}return null}function mt(t){const e=`//B //NoLogo "${t.wrapperPath}"`;return`<?xml version="1.0" encoding="UTF-16"?>
57
57
  <Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
58
58
  <RegistrationInfo>
@@ -96,13 +96,13 @@ WantedBy=default.target
96
96
  </Exec>
97
97
  </Actions>
98
98
  </Task>
99
- `}async function ft(t){const{serviceID:e,wrapperPath:n,runCommand:r}=t,i=await dt(r)??m.userInfo().username,o=c.join(c.dirname(n),`${e}-task.xml`);try{const a=mt({userId:i,wrapperPath:n});return await $(o,"\uFEFF"+a,"utf16le"),await r("schtasks",["/Create","/TN",e,"/XML",o,"/F"]),!0}catch{return!1}finally{await y(o,{force:!0}).catch(()=>{})}}const ht=3,pt="grix-wrapper-template:";function gt(t){const e=c.join(t.rootDir,"data","upgrade-wrapper-child.json"),n=[`"${t.cliPath}"`,...t.configDir?["--config-dir",`"${t.configDir}"`]:[]],r=["$ErrorActionPreference = 'Stop'",`$recordPath = ${A(e)}`,"$exitCode = 1","try {"," [IO.Directory]::CreateDirectory([IO.Path]::GetDirectoryName($recordPath)) | Out-Null",` $p = Start-Process -FilePath ${A(t.nodePath)} -ArgumentList @(${n.map(A).join(", ")}) -WindowStyle Hidden -PassThru`,` $record = @{ pid = $p.Id; launched_at = [DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds(); boot_time = [DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds() - [Environment]::TickCount64; cli_path = ${A(t.cliPath)} }`," $utf8NoBom = New-Object System.Text.UTF8Encoding($false)"," try {"," [IO.File]::WriteAllText($recordPath, ($record | ConvertTo-Json -Compress), $utf8NoBom)"," } catch {",' & "$env:SystemRoot\\System32\\taskkill.exe" /PID $p.Id /T /F 2>$null | Out-Null'," $p.WaitForExit()"," throw"," }"," $p.WaitForExit()"," $exitCode = $p.ExitCode","} finally {"," Remove-Item -LiteralPath $recordPath -Force -ErrorAction SilentlyContinue","}","exit $exitCode"].join(`
100
- `),i=Buffer.from(r,"utf16le").toString("base64"),l=`"${c.join(process.env.SystemRoot||"C:\\Windows","System32","WindowsPowerShell","v1.0","powershell.exe")}" -NoProfile -NonInteractive -ExecutionPolicy Bypass -EncodedCommand ${i}`,a=c.join(t.rootDir,"bin","upgrade-guardian.mjs"),s=c.join(t.rootDir,"bin","upgrade-guardian.fallback.mjs"),u=c.join(t.rootDir,"data","upgrade-wrapper.pause.json"),d=c.join(t.rootDir,"data","upgrade-pending.json"),p=[`"${h(t.nodePath)}"`,`"${h(a)}"`,"activate","--root",`"${h(t.rootDir)}"`].join(" "),w=`${p} --wrapper-handoff`,O=[`"${h(t.nodePath)}"`,`"${h(s)}"`,"activate","--root",`"${h(t.rootDir)}"`].join(" "),T=`${O} --wrapper-handoff`;return[`' ${pt} ${ht}`,"Option Explicit","Dim shell, processEnv, fso, command, guardianCommand, handoffGuardianCommand, guardianFallbackCommand, handoffGuardianFallbackCommand, guardianPath, guardianFallbackPath, maintenanceFile, pendingFile, delayMs, rapidCount, guardianExit",`command = "${h(l)}"`,`guardianCommand = "${h(p)}"`,`handoffGuardianCommand = "${h(w)}"`,`guardianFallbackCommand = "${h(O)}"`,`handoffGuardianFallbackCommand = "${h(T)}"`,`guardianPath = "${h(a)}"`,`guardianFallbackPath = "${h(s)}"`,`maintenanceFile = "${h(u)}"`,`pendingFile = "${h(d)}"`,'Set shell = CreateObject("WScript.Shell")','Set processEnv = shell.Environment("Process")','Set fso = CreateObject("Scripting.FileSystemObject")',`processEnv("${v}") = "${h(t.rootDir)}"`,'processEnv("GRIX_WRAPPER_SUPERVISED") = "1"',...j().map(([Z,Y])=>`processEnv("${Z}") = "${Y}"`),"delayMs = 5000","rapidCount = 0","Do"," Do While fso.FileExists(maintenanceFile)"," If fso.FileExists(guardianPath) Then"," guardianExit = shell.Run(guardianCommand, 0, True)"," ElseIf fso.FileExists(guardianFallbackPath) Then"," guardianExit = shell.Run(guardianFallbackCommand, 0, True)"," Else"," On Error Resume Next"," fso.DeleteFile maintenanceFile, True"," On Error GoTo 0"," WScript.Sleep 1000"," End If"," If fso.FileExists(maintenanceFile) Then WScript.Sleep 3000"," Loop"," If fso.FileExists(pendingFile) Then"," If fso.FileExists(guardianPath) Then"," guardianExit = shell.Run(guardianCommand, 0, True)"," ElseIf fso.FileExists(guardianFallbackPath) Then"," guardianExit = shell.Run(guardianFallbackCommand, 0, True)"," End If"," End If"," shell.Run command, 0, True"," If fso.FileExists(pendingFile) Then"," If fso.FileExists(guardianPath) Then"," guardianExit = shell.Run(handoffGuardianCommand, 0, True)"," ElseIf fso.FileExists(guardianFallbackPath) Then"," guardianExit = shell.Run(handoffGuardianFallbackCommand, 0, True)"," End If"," End If"," If fso.FileExists(pendingFile) And Not PendingIsTerminal() Then"," rapidCount = 0"," WScript.Sleep delayMs"," ElseIf fso.FileExists(maintenanceFile) Then"," rapidCount = 0"," Else"," rapidCount = rapidCount + 1"," If rapidCount >= 10 Then"," WScript.Sleep 300000"," rapidCount = 0"," Else"," WScript.Sleep delayMs"," End If"," End If","Loop","","Function PendingIsTerminal()"," Dim stream, text"," PendingIsTerminal = False"," On Error Resume Next"," Set stream = fso.OpenTextFile(pendingFile, 1)"," If Err.Number <> 0 Then"," Err.Clear"," On Error GoTo 0"," Exit Function"," End If"," text = stream.ReadAll"," stream.Close"," If Err.Number <> 0 Then"," Err.Clear"," On Error GoTo 0"," Exit Function"," End If"," On Error GoTo 0",' text = Replace(Replace(Replace(text, " ", ""), vbCr, ""), vbLf, "")',' If InStr(text, """phase"":""failed""") > 0 Then PendingIsTerminal = True',' If InStr(text, """phase"":""finalizing""") > 0 Then PendingIsTerminal = True',"End Function",""].join(`\r
101
- `)}function C(t){return`gui/${t??0}`}function B(t){const e=String(t?.stdout??"").trim();return String(t?.stderr??"").trim()||e||`exit=${Number(t?.exitCode??-1)}`}function N(t,e,n,r){if(t?.trim())return c.resolve(t);const i=e||n;return i?c.resolve(c.dirname(c.dirname(i))):c.join(r,".grix")}async function Q(t){try{await tt(t)}catch{throw new Error(`launchd plist missing: ${t}`)}}async function z(t){const{serviceID:e,domain:n,runCommand:r}=t,i=`${n}/${e}`,o=await r("launchctl",["bootout",i],{allowFailure:!0});if(Number(o?.exitCode??-1)!==0){const l=await r("launchctl",["print",i],{allowFailure:!0});if(Number(l?.exitCode??1)===0){await new Promise(s=>setTimeout(s,250));const a=await r("launchctl",["print",i],{allowFailure:!0});if(Number(a?.exitCode??1)===0)throw new Error(`launchctl bootout ${i}: ${B(o)}`)}return}for(let l=0;l<20;l+=1){const a=await r("launchctl",["print",i],{allowFailure:!0});if(Number(a?.exitCode??1)!==0)return;await new Promise(s=>setTimeout(s,250))}throw new Error(`launchctl bootout ${i}: service remains loaded`)}async function J(t){const{serviceID:e,domain:n,definitionPath:r,runCommand:i}=t,o=`${n}/${e}`,l=await i("launchctl",["bootstrap",n,r],{allowFailure:!0});if(Number(l?.exitCode??-1)===0)return;const a=await i("launchctl",["print",o],{allowFailure:!0});Number(a?.exitCode??1)===0&&await z(t);const s=await i("launchctl",["bootstrap",n,r],{allowFailure:!0});if(Number(s?.exitCode??-1)!==0)throw new Error(`launchctl bootstrap ${n} failed: initial=${B(l)}; retry=${B(s)}`)}function wt(){return{platform:"darwin",kind:"launchd",async install({serviceID:t,nodePath:e,cliPath:n,configDir:r,rootDir:i,stdoutPath:o,stderrPath:l,environmentPath:a="",homeDir:s=m.homedir()}){const u=ot(t,s),d=N(i,o,l,s);return await k(c.dirname(u),{recursive:!0}),await $(u,st({serviceID:t,nodePath:e,cliPath:n,configDir:r,rootDir:d,stdoutPath:o,stderrPath:l,environmentPath:a}),{encoding:"utf8",mode:384}),{definitionPath:u}},async start({serviceID:t,definitionPath:e,runCommand:n=f,uid:r=process.getuid?.()??0}){const i=C(r);await Q(e),await J({serviceID:t,domain:i,definitionPath:e,runCommand:n})},async stop({serviceID:t,runCommand:e=f,uid:n=process.getuid?.()??0}){const r=C(n);await e("launchctl",["bootout",`${r}/${t}`],{allowFailure:!0})},async restart({serviceID:t,definitionPath:e,runCommand:n=f,uid:r=process.getuid?.()??0}){const i=C(r),o={serviceID:t,domain:i,definitionPath:e,runCommand:n};await Q(e),await z(o),await J(o)},async uninstall({serviceID:t,definitionPath:e,runCommand:n=f,uid:r=process.getuid?.()??0}){const i=C(r);await n("launchctl",["bootout",`${i}/${t}`],{allowFailure:!0}),await y(e,{force:!0})},async discoverServices({homeDir:t=m.homedir()}={}){const e=c.join(t,"Library","LaunchAgents"),n=await F(e).catch(()=>[]),r=D("darwin"),i=[];for(const o of n){if(!o.startsWith(r)||!o.endsWith(".plist"))continue;const l=o.slice(0,-6),a=c.join(e,o);let s=null;try{const u=await P(a,"utf8");s=nt(u)}catch{}i.push({serviceID:l,definitionPath:a,configDir:s})}return i},async isServiceLoaded({serviceID:t,runCommand:e=f,uid:n=process.getuid?.()??0}){const r=C(n),i=await e("launchctl",["print",`${r}/${t}`],{allowFailure:!0});return Number(i?.exitCode??1)===0}}}function yt(){return{platform:"win32",kind:"task-scheduler",async install({serviceID:t,nodePath:e,cliPath:n,configDir:r,rootDir:i,stdoutPath:o,stderrPath:l,runCommand:a=f,homeDir:s=m.homedir()}){const u=x(t,s),d=N(i,o,l,s);await k(c.dirname(u),{recursive:!0}),await $(u,gt({nodePath:e,cliPath:n,configDir:r,rootDir:d}),"utf8");let p=!1;et()&&(p=await ft({serviceID:t,wrapperPath:u,runCommand:a}));const w=M(t,s);return p?await y(w,{force:!0}).catch(()=>{}):await ut({wrapperPath:u,shortcutPath:w,runCommand:a}),{definitionPath:p?`task:${t}`:`startup:${t}`}},async start({serviceID:t,definitionPath:e,runCommand:n=f,homeDir:r=m.homedir()}){const i=x(t,r);if(e.startsWith("task:"))try{await n("schtasks",["/Run","/TN",t]);return}catch{}L("wscript.exe",["//B","//NoLogo",i])},async stop({serviceID:t,runCommand:e=f,homeDir:n=m.homedir()}){await e("schtasks",["/End","/TN",t],{allowFailure:!0});const r=`${t}-wrapper.vbs`;await G(r,{platform:"win32"})},async restart({serviceID:t,definitionPath:e,runCommand:n=f,homeDir:r=m.homedir()}){await n("schtasks",["/End","/TN",t],{allowFailure:!0});const i=`${t}-wrapper.vbs`;await G(i,{platform:"win32"});const o=x(t,r);if(e?.startsWith("task:"))try{await n("schtasks",["/Run","/TN",t]);return}catch{}L("wscript.exe",["//B","//NoLogo",o])},async uninstall({serviceID:t,runCommand:e=f,homeDir:n=m.homedir()}){await e("schtasks",["/Delete","/TN",t,"/F"],{allowFailure:!0});const r=x(t,n);await y(r,{force:!0});const i=M(t,n);await y(i,{force:!0})},async discoverServices({homeDir:t=m.homedir(),runCommand:e=f}={}){const n=D("win32"),r=await e("schtasks",["/Query","/FO","CSV","/NH"],{allowFailure:!0}),i=[];if(Number(r?.exitCode??-1)===0){const a=String(r.stdout??"").split(/\r?\n/);for(const s of a){const u=s.match(/^"([^"]+)"/);if(!u)continue;const d=u[1];if(!d.startsWith(n))continue;let p=null;try{const w=x(d,t),T=(await P(w,"utf8")).match(/--config-dir\s+""([^""]+)""/);T&&(p=T[1])}catch{}i.push({serviceID:d,definitionPath:`task:${d}`,configDir:p})}}const o=c.join(t,"AppData","Roaming","Microsoft","Windows","Start Menu","Programs","Startup"),l=await F(o).catch(()=>[]);for(const a of l){if(!a.startsWith(n)||!a.endsWith(".lnk"))continue;const s=a.slice(0,-4);if(i.some(d=>d.serviceID===s))continue;let u=null;try{const d=x(s,t),w=(await P(d,"utf8")).match(/--config-dir\s+""([^""]+)""/);w&&(u=w[1])}catch{}i.push({serviceID:s,definitionPath:`startup:${s}`,configDir:u})}return i},async isServiceLoaded({serviceID:t,runCommand:e=f,homeDir:n=m.homedir()}){const r=await e("schtasks",["/Query","/TN",t,"/NH"],{allowFailure:!0});return Number(r?.exitCode??1)===0?!0:b(M(t,n))}}}function $t(){return{platform:"linux",kind:"systemd-user",async install({serviceID:t,nodePath:e,cliPath:n,configDir:r,rootDir:i,stdoutPath:o,stderrPath:l,environmentPath:a="",homeDir:s=m.homedir(),runCommand:u=f}){const d=it(t,s),p=N(i,o,l,s);return await k(c.dirname(d),{recursive:!0}),await $(d,ct({serviceID:t,nodePath:e,cliPath:n,configDir:r,rootDir:p,stdoutPath:o,stderrPath:l,environmentPath:a}),{encoding:"utf8",mode:384}),await u("systemctl",["--user","daemon-reload"]),await u("systemctl",["--user","enable",`${t}.service`]),{definitionPath:d}},async start({serviceID:t,runCommand:e=f}){await e("systemctl",["--user","start",`${t}.service`])},async stop({serviceID:t,runCommand:e=f}){await e("systemctl",["--user","stop",`${t}.service`],{allowFailure:!0})},async restart({serviceID:t,runCommand:e=f}){await e("systemctl",["--user","restart",`${t}.service`])},async uninstall({serviceID:t,definitionPath:e,runCommand:n=f}){await n("systemctl",["--user","stop",`${t}.service`],{allowFailure:!0}),await n("systemctl",["--user","disable",`${t}.service`],{allowFailure:!0}),await y(e,{force:!0}),await n("systemctl",["--user","daemon-reload"])},async discoverServices({homeDir:t=m.homedir()}={}){const e=c.join(t,".config","systemd","user"),n=await F(e).catch(()=>[]),r=D("linux"),i=[];for(const o of n){if(!o.startsWith(r)||!o.endsWith(".service"))continue;const l=o.slice(0,-8),a=c.join(e,o);let s=null;try{const u=await P(a,"utf8");s=rt(u)}catch{}i.push({serviceID:l,definitionPath:a,configDir:s})}return i},async isServiceLoaded({serviceID:t,runCommand:e=f}){const n=await e("systemctl",["--user","is-active",`${t}.service`],{allowFailure:!0});return String(n?.stdout??"").trim()==="active"}}}function E(t){try{const e=U(t,"utf8"),n=JSON.parse(e);if(n&&typeof n.service_id=="string"&&typeof n.node_path=="string"&&typeof n.cli_path=="string"&&typeof n.root_dir=="string")return n}catch{}return null}function Pt(t){return c.join(t,"daemon.lock.json")}function W(t){const e=Pt(t);try{const n=U(e,"utf8"),r=JSON.parse(n);if(typeof r?.pid=="number")return r.pid}catch{}return 0}function q(t){return c.join(t,".profile")}function St(t){return t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function xt(t){const e=[R(t.nodePath),R(t.cliPath)];t.configDir&&e.push("--config-dir",R(t.configDir));const n=`env ${v}=${R(t.rootDir)} ${e.join(" ")}`;return`${n} status >/dev/null 2>&1 || (nohup ${n} start >/dev/null 2>&1 &)`}function Et(t,e){return`# >>> grix-connector autostart [${t}] >>>
99
+ `}async function ft(t){const{serviceID:e,wrapperPath:n,runCommand:r}=t,i=await dt(r)??m.userInfo().username,o=c.join(c.dirname(n),`${e}-task.xml`);try{const a=mt({userId:i,wrapperPath:n});return await $(o,"\uFEFF"+a,"utf16le"),await r("schtasks",["/Create","/TN",e,"/XML",o,"/F"]),!0}catch{return!1}finally{await y(o,{force:!0}).catch(()=>{})}}const pt=3,ht="grix-wrapper-template:";function gt(t){const e=c.join(t.rootDir,"data","upgrade-wrapper-child.json"),n=[`"${t.cliPath}"`,...t.configDir?["--config-dir",`"${t.configDir}"`]:[]],r=["$ErrorActionPreference = 'Stop'",`$recordPath = ${A(e)}`,"$exitCode = 1","try {"," [IO.Directory]::CreateDirectory([IO.Path]::GetDirectoryName($recordPath)) | Out-Null",` $p = Start-Process -FilePath ${A(t.nodePath)} -ArgumentList @(${n.map(A).join(", ")}) -WindowStyle Hidden -PassThru`,` $record = @{ pid = $p.Id; launched_at = [DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds(); boot_time = [DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds() - [Environment]::TickCount64; cli_path = ${A(t.cliPath)} }`," $utf8NoBom = New-Object System.Text.UTF8Encoding($false)"," try {"," [IO.File]::WriteAllText($recordPath, ($record | ConvertTo-Json -Compress), $utf8NoBom)"," } catch {",' & "$env:SystemRoot\\System32\\taskkill.exe" /PID $p.Id /T /F 2>$null | Out-Null'," $p.WaitForExit()"," throw"," }"," $p.WaitForExit()"," $exitCode = $p.ExitCode","} finally {"," Remove-Item -LiteralPath $recordPath -Force -ErrorAction SilentlyContinue","}","exit $exitCode"].join(`
100
+ `),i=Buffer.from(r,"utf16le").toString("base64"),l=`"${c.join(process.env.SystemRoot||"C:\\Windows","System32","WindowsPowerShell","v1.0","powershell.exe")}" -NoProfile -NonInteractive -ExecutionPolicy Bypass -EncodedCommand ${i}`,a=c.join(t.rootDir,"bin","upgrade-guardian.mjs"),s=c.join(t.rootDir,"bin","upgrade-guardian.fallback.mjs"),u=c.join(t.rootDir,"data","upgrade-wrapper.pause.json"),d=c.join(t.rootDir,"data","upgrade-pending.json"),h=[`"${p(t.nodePath)}"`,`"${p(a)}"`,"activate","--root",`"${p(t.rootDir)}"`].join(" "),w=`${h} --wrapper-handoff`,O=[`"${p(t.nodePath)}"`,`"${p(s)}"`,"activate","--root",`"${p(t.rootDir)}"`].join(" "),T=`${O} --wrapper-handoff`;return[`' ${ht} ${pt}`,"Option Explicit","Dim shell, processEnv, fso, command, guardianCommand, handoffGuardianCommand, guardianFallbackCommand, handoffGuardianFallbackCommand, guardianPath, guardianFallbackPath, maintenanceFile, pendingFile, delayMs, rapidCount, guardianExit",`command = "${p(l)}"`,`guardianCommand = "${p(h)}"`,`handoffGuardianCommand = "${p(w)}"`,`guardianFallbackCommand = "${p(O)}"`,`handoffGuardianFallbackCommand = "${p(T)}"`,`guardianPath = "${p(a)}"`,`guardianFallbackPath = "${p(s)}"`,`maintenanceFile = "${p(u)}"`,`pendingFile = "${p(d)}"`,'Set shell = CreateObject("WScript.Shell")','Set processEnv = shell.Environment("Process")','Set fso = CreateObject("Scripting.FileSystemObject")',`processEnv("${v}") = "${p(t.rootDir)}"`,'processEnv("GRIX_WRAPPER_SUPERVISED") = "1"',...j().map(([Z,Y])=>`processEnv("${Z}") = "${Y}"`),"delayMs = 5000","rapidCount = 0","Do"," Do While fso.FileExists(maintenanceFile)"," If fso.FileExists(guardianPath) Then"," guardianExit = shell.Run(guardianCommand, 0, True)"," ElseIf fso.FileExists(guardianFallbackPath) Then"," guardianExit = shell.Run(guardianFallbackCommand, 0, True)"," Else"," On Error Resume Next"," fso.DeleteFile maintenanceFile, True"," On Error GoTo 0"," WScript.Sleep 1000"," End If"," If fso.FileExists(maintenanceFile) Then WScript.Sleep 3000"," Loop"," If fso.FileExists(pendingFile) Then"," If fso.FileExists(guardianPath) Then"," guardianExit = shell.Run(guardianCommand, 0, True)"," ElseIf fso.FileExists(guardianFallbackPath) Then"," guardianExit = shell.Run(guardianFallbackCommand, 0, True)"," End If"," End If"," shell.Run command, 0, True"," If fso.FileExists(pendingFile) Then"," If fso.FileExists(guardianPath) Then"," guardianExit = shell.Run(handoffGuardianCommand, 0, True)"," ElseIf fso.FileExists(guardianFallbackPath) Then"," guardianExit = shell.Run(handoffGuardianFallbackCommand, 0, True)"," End If"," End If"," If fso.FileExists(pendingFile) And Not PendingIsTerminal() Then"," rapidCount = 0"," WScript.Sleep delayMs"," ElseIf fso.FileExists(maintenanceFile) Then"," rapidCount = 0"," Else"," rapidCount = rapidCount + 1"," If rapidCount >= 10 Then"," WScript.Sleep 300000"," rapidCount = 0"," Else"," WScript.Sleep delayMs"," End If"," End If","Loop","","Function PendingIsTerminal()"," Dim stream, text"," PendingIsTerminal = False"," On Error Resume Next"," Set stream = fso.OpenTextFile(pendingFile, 1)"," If Err.Number <> 0 Then"," Err.Clear"," On Error GoTo 0"," Exit Function"," End If"," text = stream.ReadAll"," stream.Close"," If Err.Number <> 0 Then"," Err.Clear"," On Error GoTo 0"," Exit Function"," End If"," On Error GoTo 0",' text = Replace(Replace(Replace(text, " ", ""), vbCr, ""), vbLf, "")',' If InStr(text, """phase"":""failed""") > 0 Then PendingIsTerminal = True',' If InStr(text, """phase"":""finalizing""") > 0 Then PendingIsTerminal = True',"End Function",""].join(`\r
101
+ `)}function C(t){return`gui/${t??0}`}function B(t){const e=String(t?.stdout??"").trim();return String(t?.stderr??"").trim()||e||`exit=${Number(t?.exitCode??-1)}`}function N(t,e,n,r){if(t?.trim())return c.resolve(t);const i=e||n;return i?c.resolve(c.dirname(c.dirname(i))):c.join(r,".grix")}async function Q(t){try{await tt(t)}catch{throw new Error(`launchd plist missing: ${t}`)}}async function z(t){const{serviceID:e,domain:n,runCommand:r}=t,i=`${n}/${e}`,o=await r("launchctl",["bootout",i],{allowFailure:!0});if(Number(o?.exitCode??-1)!==0){const l=await r("launchctl",["print",i],{allowFailure:!0});if(Number(l?.exitCode??1)===0){await new Promise(s=>setTimeout(s,250));const a=await r("launchctl",["print",i],{allowFailure:!0});if(Number(a?.exitCode??1)===0)throw new Error(`launchctl bootout ${i}: ${B(o)}`)}return}for(let l=0;l<20;l+=1){const a=await r("launchctl",["print",i],{allowFailure:!0});if(Number(a?.exitCode??1)!==0)return;await new Promise(s=>setTimeout(s,250))}throw new Error(`launchctl bootout ${i}: service remains loaded`)}async function J(t){const{serviceID:e,domain:n,definitionPath:r,runCommand:i}=t,o=`${n}/${e}`,l=await i("launchctl",["bootstrap",n,r],{allowFailure:!0});if(Number(l?.exitCode??-1)===0)return;const a=await i("launchctl",["print",o],{allowFailure:!0});Number(a?.exitCode??1)===0&&await z(t);const s=await i("launchctl",["bootstrap",n,r],{allowFailure:!0});if(Number(s?.exitCode??-1)!==0)throw new Error(`launchctl bootstrap ${n} failed: initial=${B(l)}; retry=${B(s)}`)}function wt(){return{platform:"darwin",kind:"launchd",async install({serviceID:t,nodePath:e,cliPath:n,configDir:r,rootDir:i,stdoutPath:o,stderrPath:l,environmentPath:a="",homeDir:s=m.homedir()}){const u=ot(t,s),d=N(i,o,l,s);return await k(c.dirname(u),{recursive:!0}),await $(u,st({serviceID:t,nodePath:e,cliPath:n,configDir:r,rootDir:d,stdoutPath:o,stderrPath:l,environmentPath:a}),{encoding:"utf8",mode:384}),{definitionPath:u}},async start({serviceID:t,definitionPath:e,runCommand:n=f,uid:r=process.getuid?.()??0}){const i=C(r);await Q(e),await J({serviceID:t,domain:i,definitionPath:e,runCommand:n})},async stop({serviceID:t,runCommand:e=f,uid:n=process.getuid?.()??0}){const r=C(n);await e("launchctl",["bootout",`${r}/${t}`],{allowFailure:!0})},async restart({serviceID:t,definitionPath:e,runCommand:n=f,uid:r=process.getuid?.()??0}){const i=C(r),o={serviceID:t,domain:i,definitionPath:e,runCommand:n};await Q(e),await z(o),await J(o)},async uninstall({serviceID:t,definitionPath:e,runCommand:n=f,uid:r=process.getuid?.()??0}){const i=C(r);await n("launchctl",["bootout",`${i}/${t}`],{allowFailure:!0}),await y(e,{force:!0})},async discoverServices({homeDir:t=m.homedir()}={}){const e=c.join(t,"Library","LaunchAgents"),n=await F(e).catch(()=>[]),r=D("darwin"),i=[];for(const o of n){if(!o.startsWith(r)||!o.endsWith(".plist"))continue;const l=o.slice(0,-6),a=c.join(e,o);let s=null;try{const u=await P(a,"utf8");s=nt(u)}catch{}i.push({serviceID:l,definitionPath:a,configDir:s})}return i},async isServiceLoaded({serviceID:t,runCommand:e=f,uid:n=process.getuid?.()??0}){const r=C(n),i=await e("launchctl",["print",`${r}/${t}`],{allowFailure:!0});return Number(i?.exitCode??1)===0}}}function yt(){return{platform:"win32",kind:"task-scheduler",async install({serviceID:t,nodePath:e,cliPath:n,configDir:r,rootDir:i,stdoutPath:o,stderrPath:l,runCommand:a=f,homeDir:s=m.homedir()}){const u=x(t,s),d=N(i,o,l,s);await k(c.dirname(u),{recursive:!0}),await $(u,gt({nodePath:e,cliPath:n,configDir:r,rootDir:d}),"utf8");let h=!1;et()&&(h=await ft({serviceID:t,wrapperPath:u,runCommand:a}));const w=M(t,s);return h?await y(w,{force:!0}).catch(()=>{}):await ut({wrapperPath:u,shortcutPath:w,runCommand:a}),{definitionPath:h?`task:${t}`:`startup:${t}`}},async start({serviceID:t,definitionPath:e,runCommand:n=f,homeDir:r=m.homedir()}){const i=x(t,r);if(e.startsWith("task:"))try{await n("schtasks",["/Run","/TN",t]);return}catch{}L("wscript.exe",["//B","//NoLogo",i])},async stop({serviceID:t,runCommand:e=f,homeDir:n=m.homedir()}){await e("schtasks",["/End","/TN",t],{allowFailure:!0});const r=`${t}-wrapper.vbs`;await G(r,{platform:"win32"})},async restart({serviceID:t,definitionPath:e,runCommand:n=f,homeDir:r=m.homedir()}){await n("schtasks",["/End","/TN",t],{allowFailure:!0});const i=`${t}-wrapper.vbs`;await G(i,{platform:"win32"});const o=x(t,r);if(e?.startsWith("task:"))try{await n("schtasks",["/Run","/TN",t]);return}catch{}L("wscript.exe",["//B","//NoLogo",o])},async uninstall({serviceID:t,runCommand:e=f,homeDir:n=m.homedir()}){await e("schtasks",["/Delete","/TN",t,"/F"],{allowFailure:!0});const r=x(t,n);await y(r,{force:!0});const i=M(t,n);await y(i,{force:!0})},async discoverServices({homeDir:t=m.homedir(),runCommand:e=f}={}){const n=D("win32"),r=await e("schtasks",["/Query","/FO","CSV","/NH"],{allowFailure:!0}),i=[];if(Number(r?.exitCode??-1)===0){const a=String(r.stdout??"").split(/\r?\n/);for(const s of a){const u=s.match(/^"([^"]+)"/);if(!u)continue;const d=u[1];if(!d.startsWith(n))continue;let h=null;try{const w=x(d,t),T=(await P(w,"utf8")).match(/--config-dir\s+""([^""]+)""/);T&&(h=T[1])}catch{}i.push({serviceID:d,definitionPath:`task:${d}`,configDir:h})}}const o=c.join(t,"AppData","Roaming","Microsoft","Windows","Start Menu","Programs","Startup"),l=await F(o).catch(()=>[]);for(const a of l){if(!a.startsWith(n)||!a.endsWith(".lnk"))continue;const s=a.slice(0,-4);if(i.some(d=>d.serviceID===s))continue;let u=null;try{const d=x(s,t),w=(await P(d,"utf8")).match(/--config-dir\s+""([^""]+)""/);w&&(u=w[1])}catch{}i.push({serviceID:s,definitionPath:`startup:${s}`,configDir:u})}return i},async isServiceLoaded({serviceID:t,runCommand:e=f,homeDir:n=m.homedir()}){const r=await e("schtasks",["/Query","/TN",t,"/NH"],{allowFailure:!0});return Number(r?.exitCode??1)===0?!0:b(M(t,n))}}}function $t(){return{platform:"linux",kind:"systemd-user",async install({serviceID:t,nodePath:e,cliPath:n,configDir:r,rootDir:i,stdoutPath:o,stderrPath:l,environmentPath:a="",homeDir:s=m.homedir(),runCommand:u=f}){const d=it(t,s),h=N(i,o,l,s);return await k(c.dirname(d),{recursive:!0}),await $(d,ct({serviceID:t,nodePath:e,cliPath:n,configDir:r,rootDir:h,stdoutPath:o,stderrPath:l,environmentPath:a}),{encoding:"utf8",mode:384}),await u("systemctl",["--user","daemon-reload"]),await u("systemctl",["--user","enable",`${t}.service`]),{definitionPath:d}},async start({serviceID:t,runCommand:e=f}){await e("systemctl",["--user","start",`${t}.service`])},async stop({serviceID:t,runCommand:e=f}){await e("systemctl",["--user","stop",`${t}.service`],{allowFailure:!0})},async restart({serviceID:t,runCommand:e=f}){await e("systemctl",["--user","restart",`${t}.service`])},async uninstall({serviceID:t,definitionPath:e,runCommand:n=f}){await n("systemctl",["--user","stop",`${t}.service`],{allowFailure:!0}),await n("systemctl",["--user","disable",`${t}.service`],{allowFailure:!0}),await y(e,{force:!0}),await n("systemctl",["--user","daemon-reload"])},async discoverServices({homeDir:t=m.homedir()}={}){const e=c.join(t,".config","systemd","user"),n=await F(e).catch(()=>[]),r=D("linux"),i=[];for(const o of n){if(!o.startsWith(r)||!o.endsWith(".service"))continue;const l=o.slice(0,-8),a=c.join(e,o);let s=null;try{const u=await P(a,"utf8");s=rt(u)}catch{}i.push({serviceID:l,definitionPath:a,configDir:s})}return i},async isServiceLoaded({serviceID:t,runCommand:e=f}){const n=await e("systemctl",["--user","is-active",`${t}.service`],{allowFailure:!0});return String(n?.stdout??"").trim()==="active"}}}function E(t){try{const e=U(t,"utf8"),n=JSON.parse(e);if(n&&typeof n.service_id=="string"&&typeof n.node_path=="string"&&typeof n.cli_path=="string"&&typeof n.root_dir=="string")return n}catch{}return null}function Pt(t){return c.join(t,"daemon.lock.json")}function W(t){const e=Pt(t);try{const n=U(e,"utf8"),r=JSON.parse(n);if(typeof r?.pid=="number")return r.pid}catch{}return 0}function q(t){return c.join(t,".profile")}function St(t){return t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function xt(t){const e=[R(t.nodePath),R(t.cliPath)];t.configDir&&e.push("--config-dir",R(t.configDir));const n=`env ${v}=${R(t.rootDir)} ${e.join(" ")}`;return`${n} status >/dev/null 2>&1 || (nohup ${n} start >/dev/null 2>&1 &)`}function Et(t,e){return`# >>> grix-connector autostart [${t}] >>>
102
102
  ${e}
103
103
  # <<< grix-connector autostart [${t}] <<<
104
104
  `}function K(t){const e=St(t);return new RegExp(`(?:^|\\n)# >>> grix-connector autostart \\[${e}\\] >>>\\n[\\s\\S]*?\\n# <<< grix-connector autostart \\[${e}\\] <<<\\n?`,"m")}async function kt(t,e,n){let r="";try{r=await P(t,"utf8")}catch{}const i=Et(e,n),o=K(e);let l;if(o.test(r))l=r.replace(o,`
105
105
  ${i}`).replace(/^\n+/,"");else{const a=r.length===0||r.endsWith(`
106
106
  `)?"":`
107
107
  `;l=`${r}${a}${i}`}l!==r&&(await k(c.dirname(t),{recursive:!0}),await $(t,l,{encoding:"utf8"}))}async function vt(t,e){let n="";try{n=await P(t,"utf8")}catch{return}const r=K(e);if(!r.test(n))return;const i=n.replace(r,"").replace(/^\n+/,"");await $(t,i,{encoding:"utf8"})}function Ct(){return{platform:"linux",kind:"bare-daemon",async install({serviceID:t,nodePath:e,cliPath:n,configDir:r,rootDir:i,stdoutPath:o,stderrPath:l,homeDir:a=m.homedir()}){const s=S(t,a);await k(c.dirname(s),{recursive:!0});const u=N(i,o,l,a),d={schema_version:1,service_id:t,node_path:e,cli_path:n,config_dir:r??"",root_dir:u,stdout_path:o,stderr_path:l,installed_at:Date.now()};await $(s,`${JSON.stringify(d,null,2)}
108
- `,{encoding:"utf8",mode:384});const p=xt({nodePath:e,cliPath:n,configDir:r??"",rootDir:u});return await kt(q(a),t,p),{definitionPath:s}},async start({serviceID:t,homeDir:e=m.homedir()}){const n=S(t,e),r=E(n);if(!r)throw new Error(`bare-daemon marker missing: ${n}`);const i=[r.cli_path];r.config_dir&&i.push("--config-dir",r.config_dir),L(r.node_path,i,{env:{...process.env,[v]:r.root_dir}})},async stop({serviceID:t,homeDir:e=m.homedir()}){const n=S(t,e),r=E(n);if(!r)return;const i=W(r.root_dir);if(i>0&&I(i))try{process.kill(i,"SIGTERM")}catch{}},async restart(t){await this.stop(t);const e=Date.now()+5e3,n=S(t.serviceID,t.homeDir??m.homedir()),r=E(n);for(;r&&Date.now()<e;){const i=W(r.root_dir);if(i<=0||!I(i))break;await new Promise(o=>setTimeout(o,100))}await this.start(t)},async uninstall({serviceID:t,homeDir:e=m.homedir()}){const n=S(t,e),r=E(n);if(r){const i=W(r.root_dir);if(i>0&&I(i))try{process.kill(i,"SIGTERM")}catch{}}await y(n,{force:!0}),await vt(q(e),t)},async discoverServices({homeDir:t=m.homedir()}={}){const e=c.join(t,".grix","service"),n=await F(e).catch(()=>[]),r=D("linux"),i=[];for(const o of n){if(!o.startsWith(r)||!o.endsWith(".bare.json"))continue;const l=o.slice(0,-10),a=c.join(e,o),s=E(a);i.push({serviceID:l,definitionPath:a,configDir:s?.config_dir||null})}return i},async isServiceLoaded({serviceID:t,homeDir:e=m.homedir()}){const n=S(t,e),r=E(n);if(!r)return!1;const i=W(r.root_dir);return i>0&&I(i)}}}function Tt(t=process.getuid?.()??0){if(process.env.GRIX_FORCE_BARE_DAEMON==="1")return!1;const e=process.env.XDG_RUNTIME_DIR;return!!(e&&b(c.join(e,"bus"))||b(`/run/user/${t}/bus`))}function Wt(t=process.platform,e={}){return t==="darwin"?wt():t==="win32"?yt():e.systemdUserAvailable??Tt()?$t():Ct()}export{pt as WINDOWS_WRAPPER_TEMPLATE_MARKER,ht as WINDOWS_WRAPPER_TEMPLATE_VERSION,gt as buildWindowsWrapperScript,j as collectServicePortEnv,Wt as getPlatformServiceAdapter,Tt as isSystemdUserBusAvailable,vt as removeProfileAutostart,x as resolveWindowsWrapperPath,kt as upsertProfileAutostart};
108
+ `,{encoding:"utf8",mode:384});const h=xt({nodePath:e,cliPath:n,configDir:r??"",rootDir:u});return await kt(q(a),t,h),{definitionPath:s}},async start({serviceID:t,homeDir:e=m.homedir()}){const n=S(t,e),r=E(n);if(!r)throw new Error(`bare-daemon marker missing: ${n}`);const i=[r.cli_path];r.config_dir&&i.push("--config-dir",r.config_dir),L(r.node_path,i,{env:{...process.env,[v]:r.root_dir}})},async stop({serviceID:t,homeDir:e=m.homedir()}){const n=S(t,e),r=E(n);if(!r)return;const i=W(r.root_dir);if(i>0&&I(i))try{process.kill(i,"SIGTERM")}catch{}},async restart(t){await this.stop(t);const e=Date.now()+5e3,n=S(t.serviceID,t.homeDir??m.homedir()),r=E(n);for(;r&&Date.now()<e;){const i=W(r.root_dir);if(i<=0||!I(i))break;await new Promise(o=>setTimeout(o,100))}await this.start(t)},async uninstall({serviceID:t,homeDir:e=m.homedir()}){const n=S(t,e),r=E(n);if(r){const i=W(r.root_dir);if(i>0&&I(i))try{process.kill(i,"SIGTERM")}catch{}}await y(n,{force:!0}),await vt(q(e),t)},async discoverServices({homeDir:t=m.homedir()}={}){const e=c.join(t,".grix","service"),n=await F(e).catch(()=>[]),r=D("linux"),i=[];for(const o of n){if(!o.startsWith(r)||!o.endsWith(".bare.json"))continue;const l=o.slice(0,-10),a=c.join(e,o),s=E(a);i.push({serviceID:l,definitionPath:a,configDir:s?.config_dir||null})}return i},async isServiceLoaded({serviceID:t,homeDir:e=m.homedir()}){const n=S(t,e),r=E(n);if(!r)return!1;const i=W(r.root_dir);return i>0&&I(i)}}}function Tt(t=process.getuid?.()??0){if(process.env.GRIX_FORCE_BARE_DAEMON==="1")return!1;const e=process.env.XDG_RUNTIME_DIR;return!!(e&&b(c.join(e,"bus"))||b(`/run/user/${t}/bus`))}function Wt(t=process.platform,e={}){return t==="darwin"?wt():t==="win32"?yt():e.systemdUserAvailable??Tt()?$t():Ct()}export{ht as WINDOWS_WRAPPER_TEMPLATE_MARKER,pt as WINDOWS_WRAPPER_TEMPLATE_VERSION,gt as buildWindowsWrapperScript,j as collectServicePortEnv,Wt as getPlatformServiceAdapter,Tt as isSystemdUserBusAvailable,vt as removeProfileAutostart,M as resolveWindowsStartupShortcutPath,x as resolveWindowsWrapperPath,kt as upsertProfileAutostart};