grix-connector 4.11.13 → 4.11.14

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.11.13",
4
- "tarball": "grix-dsh-bridge-4.11.13.tgz",
5
- "size": 23420,
3
+ "bridgeVersion": "4.11.14",
4
+ "tarball": "grix-dsh-bridge-4.11.14.tgz",
5
+ "size": 23419,
6
6
  "unpackedSize": 94155,
7
- "shasum": "de5d0f4e5025e7e12a543763b84aae33215f90f6",
8
- "integrity": "sha512-a4c5ZtzjXHn65NcKRm5fJcIrPMU+j25424dXxkH0mhqy+U/WMIHtdhm8/ekaqaVTKY3bc7QXW323Yjo/FxymqQ=="
7
+ "shasum": "276e924c3a30a3660d3ac7a8a93c5dd1c60fa8eb",
8
+ "integrity": "sha512-a/DBEpVYRzQVtBZJDHqMpMAZf4bnByfaDhYEa5FtW1a2k6H2KACWaMzYwYFu5kq8YxxuHdwg5710HJmWiwsCAg=="
9
9
  }
@@ -0,0 +1 @@
1
+ import{parentPort as c,workerData as l}from"node:worker_threads";import*as r from"@sentry/node";const i=c;if(!i)throw new Error("sentry-worker must run inside a worker thread");const d=e=>i.postMessage(e),s=l;r.init({dsn:s.dsn,release:s.release,environment:s.environment,sendDefaultPii:!1,initialScope:{tags:{component:"grix-connector"}},defaultIntegrations:!1,tracesSampleRate:0});const a=new Map;r.getClient()?.on("afterSendEvent",(e,t)=>{const n=e.event_id===void 0?void 0:a.get(e.event_id);if(n===void 0)return;a.delete(e.event_id);const o=t.statusCode;d({type:"sent",key:n,ok:o!==void 0&&o>=200&&o<300})}),i.on("message",e=>{if(e.type==="capture"){const t=new Error(e.error.message);t.name=e.error.name,e.error.stack&&(t.stack=e.error.stack);const n=r.captureException(t,{level:e.level,tags:e.tags,contexts:e.contexts,fingerprint:e.fingerprint});a.set(n,e.key)}else e.type==="close"&&r.close(e.timeoutMs).finally(()=>d({type:"closed"}))}),d({type:"ready"});
@@ -1 +1,2 @@
1
- import{log as i}from"../log/logger.js";import{resolveClientVersion as f}from"../util/client-version.js";import{PersistentSentryDeduplicator as u}from"./sentry-event-dedup.js";let o=null,a=!1;const c=new u,l="https://e8e202d7625372b1314b3ff4e85a7ff9@o119262.ingest.us.sentry.io/4511410543329280";function E(){if(!process.env.GRIX_SENTRY_DISABLE)return process.env.SENTRY_DSN||process.env.GRIX_SENTRY_DSN||l}async function R(){const e=E();if(e){try{o=await import("@sentry/node")}catch(n){i.warn("sentry",`Sentry \u6A21\u5757\u52A0\u8F7D\u5931\u8D25\uFF08\u5E73\u53F0\u4E0D\u517C\u5BB9\uFF09\uFF0C\u9519\u8BEF\u4E0A\u62A5\u5DF2\u7981\u7528: ${n instanceof Error?n.message:String(n)}`);return}try{o.init({dsn:e,release:`grix-connector@${f()}`,environment:process.env.SENTRY_ENVIRONMENT||process.env.NODE_ENV||"production",sendDefaultPii:!1,initialScope:{tags:{component:"grix-connector"}},defaultIntegrations:!1,tracesSampleRate:0,beforeSend:S}),o.getClient()?.on("afterSendEvent",(n,t)=>{const r=t.statusCode;c.recordSendResult(n,r!==void 0&&r>=200&&r<300)}),a=!0}catch(n){i.error("sentry",`Sentry \u521D\u59CB\u5316\u5931\u8D25: ${n instanceof Error?n.message:String(n)}`)}}}function g(e){if(typeof e.message=="string"&&e.message)return e.message;const n=e.exception?.values;if(!n?.length)return"";const t=n[0],r=typeof t?.value=="string"?t.value:"",s=typeof t?.type=="string"?t.type:"";return r||s}function _(){return a}function S(e,n,t=c){const r=g(e);return r&&p(r)?null:t.shouldSend(e)?e:null}const y=new Set(["INSTALL_FAILED","INSTALL_TIMEOUT","FALLBACK_EXHAUSTED","PREREQ_INSTALL_FAILED","VERIFICATION_FAILED","ENVIRONMENT_UNSUPPORTED","INTERNAL"]);function N(e){if(e.ok)return!1;const n=e.error?.code;return!!n&&y.has(n)}function h(e){if(!a||!o||!N(e))return;const n=e.error?.code??"INTERNAL",t=e.error?.message??"unknown install failure",r=e.environment;try{o.withScope(s=>{s.setLevel("error"),s.setTags({agent_type:e.agentType,error_code:n,phase:e.phase}),s.setContext("install",{agentType:e.agentType,code:n,phase:e.phase,durationMs:e.durationMs,os:r?.platform,osVersion:r?.osVersion,arch:r?.arch,nodeVersion:r?.nodeVersion,npmVersion:r?.npmVersion,isDocker:r?.isDocker,isCI:r?.isCI,outputTail:(e.output??"").slice(-2e3)}),s.setFingerprint(["agent-install-failure",e.agentType,n]),o.captureException(new Error(`Agent install failed [${e.agentType}/${n}]: ${t}`))}),i.info("sentry",`\u5DF2\u63D0\u4EA4\u5B89\u88C5\u5931\u8D25\u7ED9 Sentry SDK: ${e.agentType}/${n}`)}catch(s){i.error("sentry",`\u4E0A\u62A5\u5B89\u88C5\u5931\u8D25\u65F6\u51FA\u9519: ${s instanceof Error?s.message:String(s)}`)}}const d=[/No config files found/,/spawn wmic ENOENT/i];function p(e){const n=e instanceof Error?e.message:String(e);return d.some(t=>t.test(n))}function D(e){const n=e instanceof Error?e.message:String(e);return/spawn wmic ENOENT/i.test(n)}function L(e,n){if(!(!a||!o)&&!p(e))try{o.withScope(t=>{t.setLevel("fatal"),t.setTag("crash_type",n);const r=e instanceof Error?e:new Error(String(e));o.captureException(r)}),i.info("sentry",`\u5DF2\u63D0\u4EA4\u5D29\u6E83\u7ED9 Sentry SDK: ${n}`)}catch(t){i.error("sentry",`\u4E0A\u62A5\u5D29\u6E83\u65F6\u51FA\u9519: ${t instanceof Error?t.message:String(t)}`)}}async function x(e=2e3){if(!(!a||!o))try{await o.close(e)}catch{}}export{x as closeSentry,g as extractEventMessage,S as filterSentryEvent,R as initSentry,p as isNonReportable,_ as isSentryEnabled,D as isWindowsWmicProbeError,L as reportFatal,h as reportInstallFailure,N as shouldReportInstallFailure};
1
+ import{Worker as w}from"node:worker_threads";import{log as a}from"../log/logger.js";import{resolveClientVersion as k}from"../util/client-version.js";import{PersistentSentryDeduplicator as h}from"./sentry-event-dedup.js";let i="off",c=null,m="",f=Promise.resolve();const d=[],l=new Map;let R=1,u=null;const g=new h,I="https://e8e202d7625372b1314b3ff4e85a7ff9@o119262.ingest.us.sentry.io/4511410543329280";function _(){if(!process.env.GRIX_SENTRY_DISABLE)return process.env.SENTRY_DSN||process.env.GRIX_SENTRY_DSN||I}function M(){return i!=="off"||(f=v()),f}function C(){return f}function v(){const e=_();if(!e)return Promise.resolve();const r=performance.now();m=`grix-connector@${k()}`;const t={dsn:e,release:m,environment:process.env.SENTRY_ENVIRONMENT||process.env.NODE_ENV||"production"};return new Promise(n=>{let o;try{o=new w(new URL("./sentry-worker.js",import.meta.url),{workerData:t})}catch(s){a.warn("sentry",`Sentry worker \u542F\u52A8\u5931\u8D25\uFF0C\u9519\u8BEF\u4E0A\u62A5\u5DF2\u7981\u7528: ${p(s)}`),n();return}c=o,i="starting",o.on("message",s=>{if(s.type==="ready"){if(c!==o)return;i="ready",o.unref(),a.info("sentry",`ready in ${Math.round(performance.now()-r)}ms (worker)`);for(const y of d.splice(0))N(y);n()}else if(s.type==="sent"){const y=l.get(s.key);if(!y)return;l.delete(s.key),g.recordSendResult(y,s.ok)}else s.type==="closed"&&u?.()}),o.on("error",s=>{E(o,`Sentry worker \u51FA\u9519\uFF0C\u9519\u8BEF\u4E0A\u62A5\u5DF2\u7981\u7528: ${p(s)}`),n()}),o.on("exit",s=>{E(o,`Sentry worker \u5DF2\u9000\u51FA\uFF08code ${s}\uFF09\uFF0C\u9519\u8BEF\u4E0A\u62A5\u5DF2\u7981\u7528`),n()})})}function E(e,r){u?.(),c===e&&(c=null,i="off",d.length=0,l.clear(),a.warn("sentry",r))}function N(e){try{c?.postMessage(e)}catch(r){l.delete(e.key),a.error("sentry",`\u63D0\u4EA4 Sentry \u4E8B\u4EF6\u5931\u8D25: ${p(r)}`)}}function x(e){const r=[];for(const t of(e??"").split(`
2
+ `)){if(r.length>=50)break;if(t.length>1024)continue;const n=/^\s*at (?:(.+?) \()?(.+?):(\d+):(\d+)\)?$/.exec(t);n&&r.push({function:n[1],filename:n[2],lineno:Number(n[3]),colno:Number(n[4])})}return r.reverse()}function S(e){const r={name:String(e.error.name),message:String(e.error.message),stack:typeof e.error.stack=="string"?e.error.stack:void 0},t={release:m,platform:"node",level:e.level,fingerprint:e.fingerprint,exception:{values:[{type:r.name,value:r.message,stacktrace:{frames:x(r.stack)}}]}};if(!L(t))return!1;const n={...e,type:"capture",key:R++,error:r};return l.set(n.key,t),i==="ready"?N(n):d.push(n),!0}function p(e){return e instanceof Error?e.message:String(e)}function A(e){if(typeof e.message=="string"&&e.message)return e.message;const r=e.exception?.values;if(!r?.length)return"";const t=r[0],n=typeof t?.value=="string"?t.value:"",o=typeof t?.type=="string"?t.type:"";return n||o}function W(){return i==="ready"}function L(e,r,t=g){const n=A(e);return n&&T(n)?null:t.shouldSend(e)?e:null}const D=new Set(["INSTALL_FAILED","INSTALL_TIMEOUT","FALLBACK_EXHAUSTED","PREREQ_INSTALL_FAILED","VERIFICATION_FAILED","ENVIRONMENT_UNSUPPORTED","INTERNAL"]);function $(e){if(e.ok)return!1;const r=e.error?.code;return!!r&&D.has(r)}function U(e){if(i==="off"||!$(e))return;const r=e.error?.code??"INTERNAL",t=e.error?.message??"unknown install failure",n=e.environment;try{S({kind:"install",level:"error",error:new Error(`Agent install failed [${e.agentType}/${r}]: ${t}`),tags:{agent_type:e.agentType,error_code:r,phase:e.phase},contexts:{install:{agentType:e.agentType,code:r,phase:e.phase,durationMs:e.durationMs,os:n?.platform,osVersion:n?.osVersion,arch:n?.arch,nodeVersion:n?.nodeVersion,npmVersion:n?.npmVersion,isDocker:n?.isDocker,isCI:n?.isCI,outputTail:(e.output??"").slice(-2e3)}},fingerprint:["agent-install-failure",e.agentType,r]})&&a.info("sentry",`\u5DF2\u63D0\u4EA4\u5B89\u88C5\u5931\u8D25\u7ED9 Sentry worker: ${e.agentType}/${r}`)}catch(o){a.error("sentry",`\u4E0A\u62A5\u5B89\u88C5\u5931\u8D25\u65F6\u51FA\u9519: ${p(o)}`)}}const b=[/No config files found/,/spawn wmic ENOENT/i];function T(e){const r=e instanceof Error?e.message:String(e);return b.some(t=>t.test(r))}function B(e){const r=e instanceof Error?e.message:String(e);return/spawn wmic ENOENT/i.test(r)}function Y(e,r){if(i!=="off")try{if(T(e))return;const t=e instanceof Error?e:new Error(String(e));S({kind:r,level:"fatal",error:t,tags:{crash_type:r}})&&a.info("sentry",`\u5DF2\u63D0\u4EA4\u5D29\u6E83\u7ED9 Sentry worker: ${r}`)}catch(t){a.error("sentry",`\u4E0A\u62A5\u5D29\u6E83\u65F6\u51FA\u9519: ${p(t)}`)}}async function X(e=2e3){await f;const r=c;i!=="ready"||!r||(c=null,i="off",await new Promise(t=>{const n=setTimeout(t,e);u=()=>{clearTimeout(n),t()};try{const o={type:"close",timeoutMs:e};r.postMessage(o)}catch{u()}}),u=null,r.terminate().catch(()=>{}))}export{X as closeSentry,A as extractEventMessage,L as filterSentryEvent,M as initSentry,T as isNonReportable,W as isSentryEnabled,B as isWindowsWmicProbeError,Y as reportFatal,U as reportInstallFailure,$ as shouldReportInstallFailure,C as whenSentryReady};
package/dist/grix.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import b from"node:path";import{writeFileSync as ie}from"node:fs";import{Manager as Te}from"./manager.js";import{ensureGrixDirs as $e,initLogger as Le,getLoggerStatus as Ue,log as i,installProcessLogRotation as He,setConsoleOutput as se}from"./core/log/index.js";import{HealthServer as Oe,bindPortOrFail as ce}from"./core/runtime/index.js";import{writePidFile as Fe,removePidFile as T,readDaemonPid as le}from"./core/runtime/index.js";import{killOrphanedClaudeProcesses as Ne}from"./core/runtime/index.js";import{resolveRuntimePaths as Y}from"./core/config/index.js";import{supportsNonMitmRelayConfig as Ge}from"./core/config/provider-env.js";import{ServiceManager as de}from"./service/service-manager.js";import{RESTART_EXIT_CODE as Me,UNHEALTHY_EXIT_CODE as Be,markManagedStop as pe,resolveLauncherStore as q,writeCrashNote as je}from"./core/upgrade/version-store.js";import{migrateWindowsWrapper as J}from"./service/windows-wrapper-migration.js";const We=300*1e3;import{killProcessesByCommandLine as Ke,isWindowsElevated as Xe,shouldSweepWindowsWrappers as Ve,getProcessCommandLine as Ye}from"./service/process-control.js";import{buildServiceID as qe}from"./service/service-paths.js";import{isInsideManagedDaemonTree as Je}from"./service/platform-adapter.js";import{acquireDaemonLock as ze,createHealthzHolderProbe as Qe,DaemonAlreadyServingError as Ze,findLiveLockHolder as et,isLockHolderSameProcess as tt,readDaemonLock as z,releaseDaemonLock as N}from"./runtime/daemon-lock.js";import{writeDaemonStatus as B,removeDaemonStatus as rt}from"./runtime/service-state.js";import{readRecentDuplicateDaemonRecord as nt,recordDuplicateDaemon as ot}from"./runtime/duplicate-daemon.js";import{createStartupPhaseTimer as at}from"./runtime/startup-phases.js";import{RemoteAdminSettingsStore as it}from"./core/config/remote-admin-settings.js";import{AdminServer as st,generateToken as ct,writeTokenFile as lt}from"./core/admin/index.js";import{initSentry as dt,closeSentry as j,reportFatal as Q,isWindowsWmicProbeError as pt}from"./core/observability/sentry.js";import{initProxyManager as gt,getProxyManager as h,relayHostsForClientType as ge,RelayStateStore as mt,SecretStore as ut}from"./core/proxy/index.js";import{stopProxyIfNobodyNeedsIt as me,switchAgentRelayMode as Z,reconcileRelayStates as ue}from"./core/proxy/relay-orchestration.js";import{setAgentRelayCredential as fe,RelayCredentialError as he}from"./core/proxy/relay-credential.js";import{RelayFetchError as ye,RelayToggleCoordinator as ft}from"./core/proxy/relay-credential-fetch.js";import{applyHermesProfileRelay as ht,clearHermesProfileRelay as yt,discoverHermesProfiles as wt}from"./core/proxy/hermes-profile-relay.js";import{disableRelayCredential as St,enableRelayCredential as Rt,RelayRouteError as we}from"./core/proxy/relay-credential-router.js";import{upgradeStatusSnapshot as Et}from"./core/upgrade/npm-upgrader.js";import{resolveClientVersion as Dt}from"./core/util/client-version.js";import{parseCliArgs as At}from"./core/util/cli-args.js";const{command:I,flags:E,unknownFlags:ee}=At(process.argv.slice(2));if(ee.length>0&&(console.error(`Unknown option${ee.length>1?"s":""}: ${ee.map(t=>`--${t}`).join(", ")}
3
- Run \`grix-connector --help\` to see the supported commands and options.`),process.exit(1)),E.version&&(console.log(Dt()),process.exit(0)),E.help&&(console.log(`grix-connector \u2014 Unified AI Agent Bridge
2
+ import b from"node:path";import{writeFileSync as se}from"node:fs";import{Manager as $e}from"./manager.js";import{ensureGrixDirs as Le,initLogger as Ue,getLoggerStatus as He,log as i,installProcessLogRotation as Oe,setConsoleOutput as ce}from"./core/log/index.js";import{HealthServer as Fe,bindPortOrFail as le}from"./core/runtime/index.js";import{writePidFile as Ne,removePidFile as T,readDaemonPid as de}from"./core/runtime/index.js";import{killOrphanedClaudeProcesses as Ge}from"./core/runtime/index.js";import{resolveRuntimePaths as Y}from"./core/config/index.js";import{supportsNonMitmRelayConfig as Me}from"./core/config/provider-env.js";import{ServiceManager as pe}from"./service/service-manager.js";import{RESTART_EXIT_CODE as Be,UNHEALTHY_EXIT_CODE as je,markManagedStop as ge,resolveLauncherStore as q,writeCrashNote as We}from"./core/upgrade/version-store.js";import{migrateWindowsWrapper as J}from"./service/windows-wrapper-migration.js";const Ke=300*1e3;import{killProcessesByCommandLine as Xe,isWindowsElevated as Ve,shouldSweepWindowsWrappers as Ye,getProcessCommandLine as qe}from"./service/process-control.js";import{buildServiceID as Je}from"./service/service-paths.js";import{isInsideManagedDaemonTree as ze}from"./service/platform-adapter.js";import{acquireDaemonLock as Qe,createHealthzHolderProbe as Ze,DaemonAlreadyServingError as et,findLiveLockHolder as tt,isLockHolderSameProcess as rt,readDaemonLock as z,releaseDaemonLock as N}from"./runtime/daemon-lock.js";import{writeDaemonStatus as B,removeDaemonStatus as nt}from"./runtime/service-state.js";import{readRecentDuplicateDaemonRecord as ot,recordDuplicateDaemon as at}from"./runtime/duplicate-daemon.js";import{createStartupPhaseTimer as it}from"./runtime/startup-phases.js";import{RemoteAdminSettingsStore as st}from"./core/config/remote-admin-settings.js";import{AdminServer as ct,generateToken as lt,writeTokenFile as dt}from"./core/admin/index.js";import{initSentry as pt,whenSentryReady as Q,closeSentry as j,reportFatal as Z,isWindowsWmicProbeError as gt}from"./core/observability/sentry.js";import{initProxyManager as mt,getProxyManager as h,relayHostsForClientType as me,RelayStateStore as ut,SecretStore as ft}from"./core/proxy/index.js";import{stopProxyIfNobodyNeedsIt as ue,switchAgentRelayMode as ee,reconcileRelayStates as fe}from"./core/proxy/relay-orchestration.js";import{setAgentRelayCredential as he,RelayCredentialError as ye}from"./core/proxy/relay-credential.js";import{RelayFetchError as we,RelayToggleCoordinator as ht}from"./core/proxy/relay-credential-fetch.js";import{applyHermesProfileRelay as yt,clearHermesProfileRelay as wt,discoverHermesProfiles as St}from"./core/proxy/hermes-profile-relay.js";import{disableRelayCredential as Rt,enableRelayCredential as Et,RelayRouteError as Se}from"./core/proxy/relay-credential-router.js";import{upgradeStatusSnapshot as Dt}from"./core/upgrade/npm-upgrader.js";import{resolveClientVersion as At}from"./core/util/client-version.js";import{parseCliArgs as vt}from"./core/util/cli-args.js";const{command:I,flags:E,unknownFlags:te}=vt(process.argv.slice(2));if(te.length>0&&(console.error(`Unknown option${te.length>1?"s":""}: ${te.map(t=>`--${t}`).join(", ")}
3
+ Run \`grix-connector --help\` to see the supported commands and options.`),process.exit(1)),E.version&&(console.log(At()),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)),I==="reload"){const t=le();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 s=z(Y().daemonLockFile);s&&s.pid===t&&!tt(s)&&(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(s){console.error(`reload failed: ${s instanceof Error?s.message:s}`),process.exit(1)}}const Se="detected connector-managed session; restart delegated to launchd, this process may be terminated by the restart",Re=["start","stop","restart","status"];if(I&&Re.includes(I)){process.platform==="win32"&&["start","stop","restart"].includes(I)&&!Xe()&&console.warn(`Warning: Not running as administrator. Task Scheduler registration is skipped;
37
+ `),process.exit(0)),I==="reload"){const t=de();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 s=z(Y().daemonLockFile);s&&s.pid===t&&!rt(s)&&(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(s){console.error(`reload failed: ${s instanceof Error?s.message:s}`),process.exit(1)}}const Re="detected connector-managed session; restart delegated to launchd, this process may be terminated by the restart",Ee=["start","stop","restart","status"];if(I&&Ee.includes(I)){process.platform==="win32"&&["start","stop","restart"].includes(I)&&!Ve()&&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=Y(),s=E["config-dir"]??(E.profile?b.join(t.configDir,E.profile):void 0),g=b.resolve(process.argv[1]||`${t.rootDir}/dist/grix.js`),f=new de({cliPath:g,nodePath:process.execPath});try{let l;switch(I){case"start":{const m=await f.healLegacyState({rootDir:t.rootDir});(await f.status({rootDir:t.rootDir})).installed?l=await f.start({rootDir:t.rootDir}):l=await f.install({rootDir:t.rootDir,configDir:s??m.configDir}),m.healed&&(l={...l,legacy_heal:m.reasons})}break;case"stop":l=await f.stop({rootDir:t.rootDir});break;case"restart":if(!(await f.status({rootDir:t.rootDir})).installed)l=await f.install({rootDir:t.rootDir,configDir:s});else{const D=process.platform==="darwin"&&Je();D&&console.warn(Se),l=await f.restart({rootDir:t.rootDir}),D&&(l={...l,mode:"kickstart",note:Se})}break;case"status":l=await f.status({rootDir:t.rootDir});{const m=nt(t.dataDir);m&&(l={...l,duplicate_daemon:m})}break}console.log(JSON.stringify(l,null,2)),process.exit(0)}catch(l){console.error(`${I} failed: ${l instanceof Error?l.message:l}`),process.exit(1)}}else I&&(console.error(`Unknown command: ${I}
40
- Valid commands: ${Re.join(", ")}`),process.exit(1));const c=Y(),vt=E["config-dir"]??(E.profile?`${c.configDir}/${E.profile}`:void 0),o=new Te({requestGracefulShutdown:t=>{k(t)},onTransactionSettled:()=>{J({rootDir:c.rootDir,requestGracefulShutdown:()=>{k("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}`)})}}),G=new Oe,Ee=ct(),x=new st(Ee);let W=!1,$;function K(t,s){const g=q(),f=process.env.GRIX_LAUNCHER_VERSION;if(!g||!f)return;const l=s instanceof Error?s.stack??s.message:String(s);je(g,f,`${t}: ${l}`)}function De(t,s){ot(c.dataDir,{holder_pid:t.pid,holder_command_line:t.command_line,yielded_pid:process.pid,yielded_command_line:Ye(process.pid)}),process.stderr.write(`${s}
41
- `),i.warn("main",s);const g=q();g&&pe(g)}function bt(t){const s=`another grix-connector daemon (pid ${t.pid}) is already serving ${c.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.`;De(t,s),process.exit(0)}function xt(t,s){const g=`${s.label} \u7AEF\u53E3 ${s.port} \u5DF2\u88AB\u5360\u7528\uFF0C\u4E14 daemon \u9501\u4ECD\u7531\u53E6\u4E00\u4E2A\u5B58\u6D3B\u5B9E\u4F8B (pid ${t.pid}) \u6301\u6709\uFF1A\u672C\u8FDB\u7A0B\u6309\u7BA1\u7406\u6027\u9000\u51FA\u8BA9\u4F4D\uFF0C\u4E0D\u8BA1\u4E3A\u5D29\u6E83\u3002 \u8FD9\u53F0\u673A\u5668\u4E0A\u6709\u7B2C\u4E8C\u4E2A supervisor \u5728\u91CD\u590D\u62C9\u8D77 daemon\u2014\u2014\u68C0\u67E5\u6B8B\u7559\u7684 wrapper\u3001\u91CD\u590D\u7684\u767B\u5F55\u4EFB\u52A1\uFF0C\u6216\u684C\u9762\u7AEF\u53E6\u8D77\u7684\u526F\u672C\u3002`;De(t,g),le()===process.pid&&T(),process.exit(0)}async function Ae(t){if(t.kind==="in_use"){const g=et(c.daemonLockFile);g&&(await j(),xt(g,t))}process.stderr.write(t.message+`
42
- `),i.error("main",t.message.replace(/\n/g," \u2014 ")),K("port_bind",t.message);const s=z(c.daemonLockFile);s?.pid===process.pid&&s.instance_id===$&&await B(c.daemonStatusFile,{state:"failed",pid:process.pid,instance_id:$,updated_at:Date.now(),reason:`port_bind_${t.kind}:${t.label}:${t.port}`}).catch(()=>{}),await j(),N(c.daemonLockFile).catch(()=>{}),T(),process.exit(1)}async function k(t){if(W)return;W=!0,i.info("main",`Received ${t}, shutting down...`),G.markShuttingDown();const s=q();s&&pe(s);const g=t==="upgrade-switch"?Me:t==="launcher-unhealthy"?Be:null,f=g??2,l=t==="uncaughtException"||t==="unhandledRejection",m=setTimeout(()=>{i.error("main","Shutdown timed out, forcing exit"),N(c.daemonLockFile).catch(()=>{}),T(),process.exit(f)},1e4);try{await o.stop(),await h()?.stop().catch(()=>{}),await x.stop(),await G.stop(),await j(),await N(c.daemonLockFile),await rt(c.daemonStatusFile).catch(()=>{}),clearTimeout(m),T(),i.info("main","Shutdown complete"),process.exit(g??(l?2:0))}catch(D){i.error("main",`Shutdown error: ${D}`),N(c.daemonLockFile).catch(()=>{}),T(),process.exit(f)}}async function Pt(){const t=at();t.lap("boot"),$e(),Le(),He(c.stdoutLogFile,c.stderrLogFile),se(!1);try{$=(await ze(c.daemonLockFile,c.rootDir,2,{isHolderServing:Qe(c.dataDir)})).instance_id}catch(e){e instanceof Ze&&bt(e.holder),console.error(e instanceof Error?e.message:e),process.exit(1)}if(t.lap("lock"),Ve()){const e=qe(c.rootDir,"win32");await Ke(e,{platform:"win32"})}t.lap("sweep"),await dt(),Fe(),t.lap("sentry"),i.info("main",`grix-connector starting (PID ${process.pid})`);try{Ne({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)}`)}t.lap("orphan"),await B(c.daemonStatusFile,{state:"starting",pid:process.pid,instance_id:$,updated_at:Date.now()}),process.env.GRIX_LAUNCHER_MANAGED!=="1"&&await new de({cliPath:b.resolve(process.argv[1]??""),nodePath:process.execPath}).adoptLauncher(c.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}`)}),t.lap("adopt");const s=await J({rootDir:c.rootDir,requestGracefulShutdown:()=>{k("wrapper-migration")}}).catch(e=>(i.warn("main",`Windows wrapper migration failed: ${e instanceof Error?e.message:e}`),null));if(t.lap("migrate"),s?.handoffStarted)return;process.platform==="win32"&&process.env.GRIX_LAUNCHER_MANAGED!=="1"&&setInterval(()=>{W||J({rootDir:c.rootDir,requestGracefulShutdown:()=>{k("wrapper-migration")}}).then(n=>{n.handoffStarted&&i.info("main","Wrapper handoff started by the periodic migration retry")}).catch(n=>{i.warn("main",`Periodic wrapper migration failed: ${n instanceof Error?n.message:n}`)})},We).unref();const g=parseInt(E["health-port"]??process.env.GRIX_HEALTH_PORT??"19579",10);{const e=await ce({label:"health",port:g,envVar:"GRIX_HEALTH_PORT",cliFlag:"health-port",start:n=>G.start(n)});e&&await Ae(e)}const f=b.join(c.dataDir,"health-port");ie(f,String(g),"utf-8"),t.lap("health"),process.on("SIGINT",()=>k("SIGINT")),process.on("SIGTERM",()=>k("SIGTERM")),process.on("SIGHUP",()=>{W||(i.info("main","Received SIGHUP, reloading config..."),o.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 l="",m=0,D;process.on("uncaughtException",e=>{const n=e instanceof Error?e.stack??e.message:String(e);n===l?(m++,(m<=3||m%100===0)&&i.error("main",`Uncaught exception (x${m}): ${n}`)):(m>3&&i.error("main",`Previous exception repeated ${m} times total`),l=n,m=1,i.error("main",`Uncaught exception: ${e instanceof Error?e.stack:e}`),D||(D=setTimeout(()=>{m>3&&i.error("main",`Previous exception repeated ${m} times total`),l="",m=0,D=void 0},1e4).unref())),!ve(e)&&(Q(e,"uncaughtException"),K("uncaughtException",e),k("uncaughtException"))}),process.on("unhandledRejection",e=>{i.error("main",`Unhandled rejection: ${e}`),!ve(e)&&(Q(e,"unhandledRejection"),K("unhandledRejection",e),k("unhandledRejection"))});const L=gt(c.dataDir);await L.clearRuntimeState();const _=new mt(b.join(c.dataDir,"proxy","relay-state.json"),{warn:e=>i.warn("main",e)}),M=new ut(b.join(c.dataDir,"secrets"));o.setRelaySecretResolver(e=>M.get(e));const U={hasDirectConfig:e=>o.hasDirectRelayConfig(e),clearDirectConfig:(e,n)=>o.setAgentProvider(e,void 0,{deferRestart:n?.deferRestart===!0})},X=()=>({pm:h(),agents:o,direct:U,store:_,secrets:M}),H=L.getRelayAgents();if(H.length>0)try{await L.startIfAnyRelayAgent(),i.info("main",`mitm proxy started for relay agents: ${H.join(", ")} (per-agent; others stay direct)`)}catch(e){i.error("main",`mitm proxy FAILED to start (relay agents: ${H.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 O=null;if(h()){const e=async()=>{const r=h();r&&await me(r,o)&&i.info("main","no agent uses Grix relay anymore; mitm proxy stopped")};o.setRelayEnvSettledHandler(()=>{const r=h();r&&ue(r,U,_),e().catch(a=>{i.warn("main",`failed to stop idle mitm proxy: ${a}`)})});const n=()=>{const r=h(),a=r.getRelayAgents(),p=r.getDegradedRelayAgents();return{enabled:a.length>0,relayAgents:a,staleRelayAgents:o.getAgentsWithStaleRelayEnv(),degradedRelayAgents:p,relayState:_.list(),runtime:r.getRuntimeInfo(),config:r.getConfigSnapshot()}},d=new ft,A={status:()=>n(),setAgentRelay:async(r,a,p)=>{const u=h(),R={info:w=>i.info("main",w),warn:w=>i.warn("main",w)},S=w=>o.getAgentsStatus().find(v=>v.name===w),y=S(r);if(!a){d.cancel(r);const w=await d.runExclusive(r,async()=>{const v=await Z(X(),R,{agentName:r,target:"off"});return{...n(),restarted:v.restarted,busy:v.busy,...v.pending?{pending:v.pending}:{}}});return o.reportRelayStateLocalChange(r),w}if(!y)throw Object.assign(new Error(`Agent "${r}" not found`),{code:"NOT_FOUND"});if(!Ge(y.clientType)&&ge(y.clientType).length===0)throw Object.assign(new Error(`client type "${y.clientType??""}" does not support Grix relay`),{code:"UNSUPPORTED_CLIENT_TYPE"});const F=await d.runExclusive(r,async()=>{const w=d.epoch(r),v=S(r);if(!v)throw Object.assign(new Error(`Agent "${r}" not found`),{code:"NOT_FOUND"});const ne=String(v.agentId??"").trim();if(!ne)throw new he(`local agent "${r}" has no agent_id registered`,"MISSING_AGENT_ID");const oe=d.waitCancelled(r,w);let C;try{C=await Promise.race([o.fetchRelayCredential(r,{model:p}),oe.promise.then(()=>{throw new ye("relay enable cancelled by a later disable","CANCELLED")})])}finally{oe.dispose()}d.assertCurrent(r,w);const{restarted:Pe,busy:Ie,pending:ae}=await fe(u,o,S,R,{localName:r,agentId:ne,virtualKey:C.apiKey,anthropicBaseUrl:C.anthropicBaseUrl,openaiBaseUrl:C.openaiBaseUrl,model:p,...C.directRelay!==void 0?{directRelay:C.directRelay}:{}},(ke,_e,Ce)=>o.setAgentProvider(ke,_e,{deferRestart:Ce?.deferRestart===!0}),{store:_,secrets:M,direct:U});if(d.epoch(r)!==w)throw await Z(X(),R,{agentName:r,target:"off"}),new ye("relay enable cancelled by a later disable","CANCELLED");return{...n(),restarted:Pe,busy:Ie,...ae?{pending:ae}:{}}});return o.reportRelayStateLocalChange(r),F},setAgentRelayCredential:async(r,a)=>{const p=h(),u=o.getAgentsStatus(),{restarted:R,busy:S,pending:y}=await fe(p,o,P=>u.find(F=>F.name===P),{info:P=>i.info("main",P),warn:P=>i.warn("main",P)},{localName:r,agentId:a.agentId,virtualKey:a.virtualKey,anthropicBaseUrl:a.anthropicBaseUrl,openaiBaseUrl:a.openaiBaseUrl,model:a.model,...a.directRelay!==void 0?{directRelay:a.directRelay}:{}},(P,F,w)=>o.setAgentProvider(P,F,{deferRestart:w?.deferRestart===!0}),{store:_,secrets:M,direct:U});return o.reportRelayStateLocalChange(r),{...n(),restarted:R,busy:S,...y?{pending:y}:{}}},disableAll:async()=>{const r=h(),a={info:u=>i.info("main",u),warn:u=>i.warn("main",u)},p=new Set([...r.getRelayAgents(),...o.getDirectRelayConfigAgentNames(),...Object.keys(_.list())]);for(const u of p)d.cancel(u);for(const u of p)await d.runExclusive(u,()=>Z(X(),a,{agentName:u,target:"off"})),o.reportRelayStateLocalChange(u);return await me(r,o),n()},setRoute:async(r,a)=>{const p=a,u=h();return u.setRoute({routeKey:r,targetBaseUrl:p.targetBaseUrl,...p.headers?{headers:p.headers}:{},...p.model?{model:p.model}:{},...p.modelMap?{modelMap:p.modelMap}:{},...p.passthrough?{passthrough:!0}:{},...p.relayOnly?{relayOnly:!0}:{},...p.codexResponsesToChat?{codexResponsesToChat:!0}:{}}),await u.persistConfig(),n()},deleteRoute:async r=>{const a=h();a.deleteRoute(r),await a.persistConfig()},setDefaultRoute:async r=>{const a=h();return a.setDefaultRouteKey(r),await a.persistConfig(),n()},setInterceptHosts:async r=>{const a=h();return a.setInterceptHosts(r),await a.persistConfig(),n()}};O=A,x.setProxyHandler(A),o.setRelayStateApplier({getLocalState:r=>{const a=h();if(a.getRelayAgents().includes(r)){const u=o.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 p=o.getAgentProvider(r);return p?.api_key?.trim()?{enabled:!0,model:p.model?.trim()||void 0}:{enabled:!1}},applyEnable:async(r,a,p)=>{const u=o.getAgentsStatus().find(y=>y.name===r),R=String(u?.agentId??"").trim();if(!R)throw new he(`local agent "${r}" has no agent_id registered`,"MISSING_AGENT_ID");const S=await A.setAgentRelayCredential(r,{agentId:R,virtualKey:a.apiKey,anthropicBaseUrl:a.anthropicBaseUrl,openaiBaseUrl:a.openaiBaseUrl,model:p??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 A.setAgentRelay(r,!1);return{restarted:a.restarted===!0,busy:a.busy===!0,...a.pending?{pending:a.pending}:{}}}})}G.setStatusProvider(()=>o.getAgentsStatus()),G.setMetaProvider(()=>{const e=o.getAgentsStatus(),n=Ue();return{ws:{connected:e.filter(d=>d.wsConnected===!0).length,total:e.length},upgrade:Et(),log:{dir:n.logDir,file:n.logFile,primary_dir:n.primaryLogDir,degraded:n.degraded,using_fallback:n.usingFallback,...n.degradeReason?{degrade_reason:n.degradeReason}:{}}}});const V=new it(b.join(c.dataDir,"remote-admin.json"));if(o.setRemoteAdminGate(()=>V.isEnabled()),t.lap("setup"),await o.start(vt),t.lap("agents"),H.length>0){const e=o.getAgentsStatus();for(const n of H){const d=e.find(r=>r.name===n);if(!d)continue;const A=ge(d.clientType);A.length!==0&&await L.setAgentRelayEnabled(n,!0,{relayHosts:A}).catch(r=>{i.warn("main",`failed to reconcile relay hosts for "${n}": ${r}`)})}}ue(L,U,_);const te=parseInt(E["admin-port"]??process.env.GRIX_ADMIN_PORT??"19580",10);x.setAgentHandler({list:()=>o.getAgentsStatus(),add:e=>o.addAgent(e),remove:e=>o.removeAgent(e),restart:e=>o.restartAgent(e),reload:()=>o.reload()}),x.setUpgradeHandler({check:()=>o.checkUpgrade(),trigger:()=>o.triggerUpgrade()}),x.setProbeHandler({probeAll:e=>o.probeAll(e),probeOne:(e,n)=>o.probeOne(e,n)}),x.setRemoteAdminHandler({isEnabled:()=>V.isEnabled(),setEnabled:e=>{const n=V.setEnabled(e);return i.info("main",`remote admin ${n?"enabled":"disabled"} via admin API`),n}}),x.setInstallHandler({listInstallable:()=>o.listInstallable(),installAgent:e=>o.installAgent(e),getInstallProgress:e=>o.getInstallProgress(e)});const re={listAgents:()=>o.getAgentsStatus(),enableManaged:(e,n,d)=>{if(!O)throw new we(`agent "${e}" needs the MITM proxy for relay, but it is not running`,"PROXY_UNAVAILABLE");return O.setAgentRelayCredential(e,{agentId:n,virtualKey:d.virtualKey,anthropicBaseUrl:d.anthropicBaseUrl,openaiBaseUrl:d.openaiBaseUrl,model:d.model,...d.directRelay!==void 0?{directRelay:d.directRelay}:{}})},disableManaged:e=>{if(!O)throw new we(`agent "${e}" needs the MITM proxy for relay, but it is not running`,"PROXY_UNAVAILABLE");return O.setAgentRelay(e,!1)},enableHermesProfile:(e,n)=>ht({agentId:e,baseUrl:n.openaiBaseUrl??"",virtualKey:n.virtualKey,model:n.model??""}),disableHermesProfile:e=>yt(e)};x.setRelayCredentialHandler({listHermesProfiles:()=>wt(),enable:async(e,n)=>{const{target:d,result:A}=await Rt(re,e,n);return{target:d,...A}},disable:async e=>{const{target:n,result:d}=await St(re,e);return{target:n,...d}}});{const e=await ce({label:"admin",port:te,envVar:"GRIX_ADMIN_PORT",cliFlag:"admin-port",start:n=>x.start(n)});e&&await Ae(e)}const be=b.join(c.dataDir,"admin-token"),xe=b.join(c.dataDir,"admin-port");lt(be,Ee),ie(xe,String(te),"utf-8"),t.lap("admin"),await B(c.daemonStatusFile,{state:"running",pid:process.pid,instance_id:$,updated_at:Date.now()}),i.info("main",`startup phases: ${t.summary()}`),process.send&&process.send("ready"),i.info("main","grix-connector ready")}Pt().catch(async t=>{const s=`Fatal: ${t instanceof Error?t.stack??t.message:String(t)}`;try{process.stderr.write(`${s}
43
- `)}catch{}se(!1),i.error("main",s),Q(t,"startup"),K("startup",t);const g=z(c.daemonLockFile);g?.pid===process.pid&&g.instance_id===$&&await B(c.daemonStatusFile,{state:"failed",pid:process.pid,instance_id:g.instance_id,updated_at:Date.now(),reason:"startup_error"}).catch(()=>{}),await j(),N(c.daemonLockFile).catch(()=>{}),T(),process.exit(1)});const It=new Set(["ECONNRESET","ECONNREFUSED","ETIMEDOUT","EPIPE","EAI_AGAIN","ENOTFOUND","EHOSTUNREACH","ENETUNREACH","EIO"]);function ve(t){return pt(t)?!0:t instanceof Error&&"code"in t?It.has(t.code):!1}
39
+ right-click the terminal and select "Run as administrator".`);const t=Y(),s=E["config-dir"]??(E.profile?b.join(t.configDir,E.profile):void 0),g=b.resolve(process.argv[1]||`${t.rootDir}/dist/grix.js`),f=new pe({cliPath:g,nodePath:process.execPath});try{let l;switch(I){case"start":{const m=await f.healLegacyState({rootDir:t.rootDir});(await f.status({rootDir:t.rootDir})).installed?l=await f.start({rootDir:t.rootDir}):l=await f.install({rootDir:t.rootDir,configDir:s??m.configDir}),m.healed&&(l={...l,legacy_heal:m.reasons})}break;case"stop":l=await f.stop({rootDir:t.rootDir});break;case"restart":if(!(await f.status({rootDir:t.rootDir})).installed)l=await f.install({rootDir:t.rootDir,configDir:s});else{const D=process.platform==="darwin"&&ze();D&&console.warn(Re),l=await f.restart({rootDir:t.rootDir}),D&&(l={...l,mode:"kickstart",note:Re})}break;case"status":l=await f.status({rootDir:t.rootDir});{const m=ot(t.dataDir);m&&(l={...l,duplicate_daemon:m})}break}console.log(JSON.stringify(l,null,2)),process.exit(0)}catch(l){console.error(`${I} failed: ${l instanceof Error?l.message:l}`),process.exit(1)}}else I&&(console.error(`Unknown command: ${I}
40
+ Valid commands: ${Ee.join(", ")}`),process.exit(1));const c=Y(),bt=E["config-dir"]??(E.profile?`${c.configDir}/${E.profile}`:void 0),o=new $e({requestGracefulShutdown:t=>{k(t)},onTransactionSettled:()=>{J({rootDir:c.rootDir,requestGracefulShutdown:()=>{k("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}`)})}}),G=new Fe,De=lt(),x=new ct(De);let W=!1,$;function K(t,s){const g=q(),f=process.env.GRIX_LAUNCHER_VERSION;if(!g||!f)return;const l=s instanceof Error?s.stack??s.message:String(s);We(g,f,`${t}: ${l}`)}function Ae(t,s){at(c.dataDir,{holder_pid:t.pid,holder_command_line:t.command_line,yielded_pid:process.pid,yielded_command_line:qe(process.pid)}),process.stderr.write(`${s}
41
+ `),i.warn("main",s);const g=q();g&&ge(g)}function xt(t){const s=`another grix-connector daemon (pid ${t.pid}) is already serving ${c.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.`;Ae(t,s),process.exit(0)}function Pt(t,s){const g=`${s.label} \u7AEF\u53E3 ${s.port} \u5DF2\u88AB\u5360\u7528\uFF0C\u4E14 daemon \u9501\u4ECD\u7531\u53E6\u4E00\u4E2A\u5B58\u6D3B\u5B9E\u4F8B (pid ${t.pid}) \u6301\u6709\uFF1A\u672C\u8FDB\u7A0B\u6309\u7BA1\u7406\u6027\u9000\u51FA\u8BA9\u4F4D\uFF0C\u4E0D\u8BA1\u4E3A\u5D29\u6E83\u3002 \u8FD9\u53F0\u673A\u5668\u4E0A\u6709\u7B2C\u4E8C\u4E2A supervisor \u5728\u91CD\u590D\u62C9\u8D77 daemon\u2014\u2014\u68C0\u67E5\u6B8B\u7559\u7684 wrapper\u3001\u91CD\u590D\u7684\u767B\u5F55\u4EFB\u52A1\uFF0C\u6216\u684C\u9762\u7AEF\u53E6\u8D77\u7684\u526F\u672C\u3002`;Ae(t,g),de()===process.pid&&T(),process.exit(0)}async function ve(t){if(t.kind==="in_use"){const g=tt(c.daemonLockFile);g&&(await j(),Pt(g,t))}process.stderr.write(t.message+`
42
+ `),i.error("main",t.message.replace(/\n/g," \u2014 ")),K("port_bind",t.message);const s=z(c.daemonLockFile);s?.pid===process.pid&&s.instance_id===$&&await B(c.daemonStatusFile,{state:"failed",pid:process.pid,instance_id:$,updated_at:Date.now(),reason:`port_bind_${t.kind}:${t.label}:${t.port}`}).catch(()=>{}),await j(),N(c.daemonLockFile).catch(()=>{}),T(),process.exit(1)}async function k(t){if(W)return;W=!0,i.info("main",`Received ${t}, shutting down...`),G.markShuttingDown();const s=q();s&&ge(s);const g=t==="upgrade-switch"?Be:t==="launcher-unhealthy"?je:null,f=g??2,l=t==="uncaughtException"||t==="unhandledRejection",m=setTimeout(()=>{i.error("main","Shutdown timed out, forcing exit"),N(c.daemonLockFile).catch(()=>{}),T(),process.exit(f)},1e4);try{await o.stop(),await h()?.stop().catch(()=>{}),await x.stop(),await G.stop(),await j(),await N(c.daemonLockFile),await nt(c.daemonStatusFile).catch(()=>{}),clearTimeout(m),T(),i.info("main","Shutdown complete"),process.exit(g??(l?2:0))}catch(D){i.error("main",`Shutdown error: ${D}`),N(c.daemonLockFile).catch(()=>{}),T(),process.exit(f)}}async function It(){const t=it();t.lap("boot"),Le(),Ue(),Oe(c.stdoutLogFile,c.stderrLogFile),ce(!1);try{$=(await Qe(c.daemonLockFile,c.rootDir,2,{isHolderServing:Ze(c.dataDir)})).instance_id}catch(e){e instanceof et&&xt(e.holder),console.error(e instanceof Error?e.message:e),process.exit(1)}if(t.lap("lock"),Ye()){const e=Je(c.rootDir,"win32");await Xe(e,{platform:"win32"})}t.lap("sweep"),pt(),Ne(),t.lap("pidfile"),i.info("main",`grix-connector starting (PID ${process.pid})`);try{Ge({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)}`)}t.lap("orphan"),await B(c.daemonStatusFile,{state:"starting",pid:process.pid,instance_id:$,updated_at:Date.now()}),process.env.GRIX_LAUNCHER_MANAGED!=="1"&&await new pe({cliPath:b.resolve(process.argv[1]??""),nodePath:process.execPath}).adoptLauncher(c.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}`)}),t.lap("adopt");const s=await J({rootDir:c.rootDir,requestGracefulShutdown:()=>{k("wrapper-migration")}}).catch(e=>(i.warn("main",`Windows wrapper migration failed: ${e instanceof Error?e.message:e}`),null));if(t.lap("migrate"),s?.handoffStarted)return;process.platform==="win32"&&process.env.GRIX_LAUNCHER_MANAGED!=="1"&&setInterval(()=>{W||J({rootDir:c.rootDir,requestGracefulShutdown:()=>{k("wrapper-migration")}}).then(n=>{n.handoffStarted&&i.info("main","Wrapper handoff started by the periodic migration retry")}).catch(n=>{i.warn("main",`Periodic wrapper migration failed: ${n instanceof Error?n.message:n}`)})},Ke).unref();const g=parseInt(E["health-port"]??process.env.GRIX_HEALTH_PORT??"19579",10);{const e=await le({label:"health",port:g,envVar:"GRIX_HEALTH_PORT",cliFlag:"health-port",start:n=>G.start(n)});e&&await ve(e)}const f=b.join(c.dataDir,"health-port");se(f,String(g),"utf-8"),t.lap("health"),process.on("SIGINT",()=>k("SIGINT")),process.on("SIGTERM",()=>k("SIGTERM")),process.on("SIGHUP",()=>{W||(i.info("main","Received SIGHUP, reloading config..."),o.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 l="",m=0,D;process.on("uncaughtException",e=>{const n=e instanceof Error?e.stack??e.message:String(e);n===l?(m++,(m<=3||m%100===0)&&i.error("main",`Uncaught exception (x${m}): ${n}`)):(m>3&&i.error("main",`Previous exception repeated ${m} times total`),l=n,m=1,i.error("main",`Uncaught exception: ${e instanceof Error?e.stack:e}`),D||(D=setTimeout(()=>{m>3&&i.error("main",`Previous exception repeated ${m} times total`),l="",m=0,D=void 0},1e4).unref())),!be(e)&&(Q().then(()=>Z(e,"uncaughtException")),K("uncaughtException",e),k("uncaughtException"))}),process.on("unhandledRejection",e=>{i.error("main",`Unhandled rejection: ${e}`),!be(e)&&(Q().then(()=>Z(e,"unhandledRejection")),K("unhandledRejection",e),k("unhandledRejection"))});const L=mt(c.dataDir);await L.clearRuntimeState();const _=new ut(b.join(c.dataDir,"proxy","relay-state.json"),{warn:e=>i.warn("main",e)}),M=new ft(b.join(c.dataDir,"secrets"));o.setRelaySecretResolver(e=>M.get(e));const U={hasDirectConfig:e=>o.hasDirectRelayConfig(e),clearDirectConfig:(e,n)=>o.setAgentProvider(e,void 0,{deferRestart:n?.deferRestart===!0})},X=()=>({pm:h(),agents:o,direct:U,store:_,secrets:M}),H=L.getRelayAgents();if(H.length>0)try{await L.startIfAnyRelayAgent(),i.info("main",`mitm proxy started for relay agents: ${H.join(", ")} (per-agent; others stay direct)`)}catch(e){i.error("main",`mitm proxy FAILED to start (relay agents: ${H.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 O=null;if(h()){const e=async()=>{const r=h();r&&await ue(r,o)&&i.info("main","no agent uses Grix relay anymore; mitm proxy stopped")};o.setRelayEnvSettledHandler(()=>{const r=h();r&&fe(r,U,_),e().catch(a=>{i.warn("main",`failed to stop idle mitm proxy: ${a}`)})});const n=()=>{const r=h(),a=r.getRelayAgents(),p=r.getDegradedRelayAgents();return{enabled:a.length>0,relayAgents:a,staleRelayAgents:o.getAgentsWithStaleRelayEnv(),degradedRelayAgents:p,relayState:_.list(),runtime:r.getRuntimeInfo(),config:r.getConfigSnapshot()}},d=new ht,A={status:()=>n(),setAgentRelay:async(r,a,p)=>{const u=h(),R={info:w=>i.info("main",w),warn:w=>i.warn("main",w)},S=w=>o.getAgentsStatus().find(v=>v.name===w),y=S(r);if(!a){d.cancel(r);const w=await d.runExclusive(r,async()=>{const v=await ee(X(),R,{agentName:r,target:"off"});return{...n(),restarted:v.restarted,busy:v.busy,...v.pending?{pending:v.pending}:{}}});return o.reportRelayStateLocalChange(r),w}if(!y)throw Object.assign(new Error(`Agent "${r}" not found`),{code:"NOT_FOUND"});if(!Me(y.clientType)&&me(y.clientType).length===0)throw Object.assign(new Error(`client type "${y.clientType??""}" does not support Grix relay`),{code:"UNSUPPORTED_CLIENT_TYPE"});const F=await d.runExclusive(r,async()=>{const w=d.epoch(r),v=S(r);if(!v)throw Object.assign(new Error(`Agent "${r}" not found`),{code:"NOT_FOUND"});const oe=String(v.agentId??"").trim();if(!oe)throw new ye(`local agent "${r}" has no agent_id registered`,"MISSING_AGENT_ID");const ae=d.waitCancelled(r,w);let C;try{C=await Promise.race([o.fetchRelayCredential(r,{model:p}),ae.promise.then(()=>{throw new we("relay enable cancelled by a later disable","CANCELLED")})])}finally{ae.dispose()}d.assertCurrent(r,w);const{restarted:Ie,busy:ke,pending:ie}=await he(u,o,S,R,{localName:r,agentId:oe,virtualKey:C.apiKey,anthropicBaseUrl:C.anthropicBaseUrl,openaiBaseUrl:C.openaiBaseUrl,model:p,...C.directRelay!==void 0?{directRelay:C.directRelay}:{}},(_e,Ce,Te)=>o.setAgentProvider(_e,Ce,{deferRestart:Te?.deferRestart===!0}),{store:_,secrets:M,direct:U});if(d.epoch(r)!==w)throw await ee(X(),R,{agentName:r,target:"off"}),new we("relay enable cancelled by a later disable","CANCELLED");return{...n(),restarted:Ie,busy:ke,...ie?{pending:ie}:{}}});return o.reportRelayStateLocalChange(r),F},setAgentRelayCredential:async(r,a)=>{const p=h(),u=o.getAgentsStatus(),{restarted:R,busy:S,pending:y}=await he(p,o,P=>u.find(F=>F.name===P),{info:P=>i.info("main",P),warn:P=>i.warn("main",P)},{localName:r,agentId:a.agentId,virtualKey:a.virtualKey,anthropicBaseUrl:a.anthropicBaseUrl,openaiBaseUrl:a.openaiBaseUrl,model:a.model,...a.directRelay!==void 0?{directRelay:a.directRelay}:{}},(P,F,w)=>o.setAgentProvider(P,F,{deferRestart:w?.deferRestart===!0}),{store:_,secrets:M,direct:U});return o.reportRelayStateLocalChange(r),{...n(),restarted:R,busy:S,...y?{pending:y}:{}}},disableAll:async()=>{const r=h(),a={info:u=>i.info("main",u),warn:u=>i.warn("main",u)},p=new Set([...r.getRelayAgents(),...o.getDirectRelayConfigAgentNames(),...Object.keys(_.list())]);for(const u of p)d.cancel(u);for(const u of p)await d.runExclusive(u,()=>ee(X(),a,{agentName:u,target:"off"})),o.reportRelayStateLocalChange(u);return await ue(r,o),n()},setRoute:async(r,a)=>{const p=a,u=h();return u.setRoute({routeKey:r,targetBaseUrl:p.targetBaseUrl,...p.headers?{headers:p.headers}:{},...p.model?{model:p.model}:{},...p.modelMap?{modelMap:p.modelMap}:{},...p.passthrough?{passthrough:!0}:{},...p.relayOnly?{relayOnly:!0}:{},...p.codexResponsesToChat?{codexResponsesToChat:!0}:{}}),await u.persistConfig(),n()},deleteRoute:async r=>{const a=h();a.deleteRoute(r),await a.persistConfig()},setDefaultRoute:async r=>{const a=h();return a.setDefaultRouteKey(r),await a.persistConfig(),n()},setInterceptHosts:async r=>{const a=h();return a.setInterceptHosts(r),await a.persistConfig(),n()}};O=A,x.setProxyHandler(A),o.setRelayStateApplier({getLocalState:r=>{const a=h();if(a.getRelayAgents().includes(r)){const u=o.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 p=o.getAgentProvider(r);return p?.api_key?.trim()?{enabled:!0,model:p.model?.trim()||void 0}:{enabled:!1}},applyEnable:async(r,a,p)=>{const u=o.getAgentsStatus().find(y=>y.name===r),R=String(u?.agentId??"").trim();if(!R)throw new ye(`local agent "${r}" has no agent_id registered`,"MISSING_AGENT_ID");const S=await A.setAgentRelayCredential(r,{agentId:R,virtualKey:a.apiKey,anthropicBaseUrl:a.anthropicBaseUrl,openaiBaseUrl:a.openaiBaseUrl,model:p??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 A.setAgentRelay(r,!1);return{restarted:a.restarted===!0,busy:a.busy===!0,...a.pending?{pending:a.pending}:{}}}})}G.setStatusProvider(()=>o.getAgentsStatus()),G.setMetaProvider(()=>{const e=o.getAgentsStatus(),n=He();return{ws:{connected:e.filter(d=>d.wsConnected===!0).length,total:e.length},upgrade:Dt(),log:{dir:n.logDir,file:n.logFile,primary_dir:n.primaryLogDir,degraded:n.degraded,using_fallback:n.usingFallback,...n.degradeReason?{degrade_reason:n.degradeReason}:{}}}});const V=new st(b.join(c.dataDir,"remote-admin.json"));if(o.setRemoteAdminGate(()=>V.isEnabled()),t.lap("setup"),await o.start(bt),t.lap("agents"),H.length>0){const e=o.getAgentsStatus();for(const n of H){const d=e.find(r=>r.name===n);if(!d)continue;const A=me(d.clientType);A.length!==0&&await L.setAgentRelayEnabled(n,!0,{relayHosts:A}).catch(r=>{i.warn("main",`failed to reconcile relay hosts for "${n}": ${r}`)})}}fe(L,U,_);const re=parseInt(E["admin-port"]??process.env.GRIX_ADMIN_PORT??"19580",10);x.setAgentHandler({list:()=>o.getAgentsStatus(),add:e=>o.addAgent(e),remove:e=>o.removeAgent(e),restart:e=>o.restartAgent(e),reload:()=>o.reload()}),x.setUpgradeHandler({check:()=>o.checkUpgrade(),trigger:()=>o.triggerUpgrade()}),x.setProbeHandler({probeAll:e=>o.probeAll(e),probeOne:(e,n)=>o.probeOne(e,n)}),x.setRemoteAdminHandler({isEnabled:()=>V.isEnabled(),setEnabled:e=>{const n=V.setEnabled(e);return i.info("main",`remote admin ${n?"enabled":"disabled"} via admin API`),n}}),x.setInstallHandler({listInstallable:()=>o.listInstallable(),installAgent:e=>o.installAgent(e),getInstallProgress:e=>o.getInstallProgress(e)});const ne={listAgents:()=>o.getAgentsStatus(),enableManaged:(e,n,d)=>{if(!O)throw new Se(`agent "${e}" needs the MITM proxy for relay, but it is not running`,"PROXY_UNAVAILABLE");return O.setAgentRelayCredential(e,{agentId:n,virtualKey:d.virtualKey,anthropicBaseUrl:d.anthropicBaseUrl,openaiBaseUrl:d.openaiBaseUrl,model:d.model,...d.directRelay!==void 0?{directRelay:d.directRelay}:{}})},disableManaged:e=>{if(!O)throw new Se(`agent "${e}" needs the MITM proxy for relay, but it is not running`,"PROXY_UNAVAILABLE");return O.setAgentRelay(e,!1)},enableHermesProfile:(e,n)=>yt({agentId:e,baseUrl:n.openaiBaseUrl??"",virtualKey:n.virtualKey,model:n.model??""}),disableHermesProfile:e=>wt(e)};x.setRelayCredentialHandler({listHermesProfiles:()=>St(),enable:async(e,n)=>{const{target:d,result:A}=await Et(ne,e,n);return{target:d,...A}},disable:async e=>{const{target:n,result:d}=await Rt(ne,e);return{target:n,...d}}});{const e=await le({label:"admin",port:re,envVar:"GRIX_ADMIN_PORT",cliFlag:"admin-port",start:n=>x.start(n)});e&&await ve(e)}const xe=b.join(c.dataDir,"admin-token"),Pe=b.join(c.dataDir,"admin-port");dt(xe,De),se(Pe,String(re),"utf-8"),t.lap("admin"),await B(c.daemonStatusFile,{state:"running",pid:process.pid,instance_id:$,updated_at:Date.now()}),i.info("main",`startup phases: ${t.summary()}`),process.send&&process.send("ready"),i.info("main","grix-connector ready")}It().catch(async t=>{const s=`Fatal: ${t instanceof Error?t.stack??t.message:String(t)}`;try{process.stderr.write(`${s}
43
+ `)}catch{}ce(!1),i.error("main",s),K("startup",t);const g=z(c.daemonLockFile);g?.pid===process.pid&&g.instance_id===$&&await B(c.daemonStatusFile,{state:"failed",pid:process.pid,instance_id:g.instance_id,updated_at:Date.now(),reason:"startup_error"}).catch(()=>{}),await Q(),Z(t,"startup"),await j(),N(c.daemonLockFile).catch(()=>{}),T(),process.exit(1)});const kt=new Set(["ECONNRESET","ECONNREFUSED","ETIMEDOUT","EPIPE","EAI_AGAIN","ENOTFOUND","EHOSTUNREACH","ENETUNREACH","EIO"]);function be(t){return gt(t)?!0:t instanceof Error&&"code"in t?kt.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(t){const o=new Set([`http://127.0.0.1:${t.serverPort}`,`http://localhost:${t.serverPort}`,...t.allowedOrigins]),e=new Set([`127.0.0.1:${t.serverPort}`,`localhost:${t.serverPort}`,...t.allowedHosts]);return{validateRequest(s){const r=i(s,o);if(!r.ok)return r;const n=l(s,e);return n.ok?{ok:!0}:n}}}function i(t,o){const e=t.headers.origin;return e?o.has(e)?{ok:!0}:{ok:!1,statusCode:403,message:`Origin not allowed: ${e}`}:{ok:!0}}function l(t,o){const e=t.headers.host;return e?o.has(e)?{ok:!0}:{ok:!1,statusCode:403,message:`Host not allowed: ${e}`}:{ok:!1,statusCode:403,message:"Missing Host header"}}export{a as createSecurityPolicy};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grix-connector",
3
- "version": "4.11.13",
3
+ "version": "4.11.14",
4
4
  "description": "Connect local AI coding agents (Claude, Codex, Gemini, Qwen, DeepSeek, Cursor, OpenCode, Pi, OpenHuman, Reasonix) to the Grix scheduling platform. Also serves as an OpenClaw plugin for Grix channel transport.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",