grix-connector 4.4.2-beta.2 → 4.4.2-beta.3

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.4.2-beta.2",
4
- "tarball": "grix-dsh-bridge-4.4.2-beta.2.tgz",
3
+ "bridgeVersion": "4.4.2-beta.3",
4
+ "tarball": "grix-dsh-bridge-4.4.2-beta.3.tgz",
5
5
  "size": 22636,
6
6
  "unpackedSize": 90343,
7
- "shasum": "086f2d649887e8be03eedce3dc7d5486e4bb2746",
8
- "integrity": "sha512-d5G5Xu9IypdO1rZkcz0J+dWmscTyDGbYnLSNydzd3ggkY7pU03hlrYzfclvTZ/njP5DycmR6WxaDhRRymxJ5MA=="
7
+ "shasum": "fc27473752a752d639e8deb490ea13d7ac9089ab",
8
+ "integrity": "sha512-Yh0AsY/7hzensJwwbOAaXOJsbgkxarP1ErLadmDNWhnEojrao0NJTdEgG80SNKGB1S2sUgVsRT9+4m/LpcpRBQ=="
9
9
  }
package/dist/grix.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- process.stderr.write("GRIX TEST BUILD: deliberate startup crash\n"); process.exit(1);
2
+ if (!process.argv.slice(2).some((a) => a === "--version" || a === "-v")) { process.stderr.write("GRIX TEST BUILD: deliberate daemon startup crash\n"); process.exit(1); }
3
3
  import x from"node:path";import{writeFileSync as te}from"node:fs";import{Manager as xe}from"./manager.js";import{ensureGrixDirs as De,initLogger as be,log as s,installProcessLogRotation as Pe,setConsoleOutput as re}from"./core/log/index.js";import{HealthServer as Ie,bindPortOrFail as ne}from"./core/runtime/index.js";import{writePidFile as ke,removePidFile as O,readDaemonPid as Ce}from"./core/runtime/index.js";import{resolveRuntimePaths as j}from"./core/config/index.js";import{supportsNonMitmRelayConfig as Te}from"./core/config/provider-env.js";import{ServiceManager as oe}from"./service/service-manager.js";import{RESTART_EXIT_CODE as ae}from"./core/upgrade/version-store.js";import{migrateWindowsWrapper as Ue}from"./service/windows-wrapper-migration.js";import{killProcessesByCommandLine as _e,isWindowsElevated as $e,shouldSweepWindowsWrappers as Le}from"./service/process-control.js";import{buildServiceID as Fe}from"./service/service-paths.js";import{acquireDaemonLock as Oe,isLockHolderSameProcess as He,readDaemonLock as K,releaseDaemonLock as H}from"./runtime/daemon-lock.js";import{writeDaemonStatus as N,removeDaemonStatus as Me}from"./runtime/service-state.js";import{AdminServer as Be,generateToken as Ne,writeTokenFile as Ge}from"./core/admin/index.js";import{initSentry as je,closeSentry as W,reportFatal as X}from"./core/observability/sentry.js";import{initProxyManager as Ke,getProxyManager as f,relayHostsForClientType as se,RelayStateStore as We,SecretStore as Xe}from"./core/proxy/index.js";import{stopProxyIfNobodyNeedsIt as ie,switchAgentRelayMode as V,reconcileRelayStates as le}from"./core/proxy/relay-orchestration.js";import{setAgentRelayCredential as ce,RelayCredentialError as de}from"./core/proxy/relay-credential.js";import{RelayFetchError as pe,RelayToggleCoordinator as Ve}from"./core/proxy/relay-credential-fetch.js";import{applyHermesProfileRelay as qe,clearHermesProfileRelay as Je,discoverHermesProfiles as Ye}from"./core/proxy/hermes-profile-relay.js";import{disableRelayCredential as ze,enableRelayCredential as Qe,RelayRouteError as ge}from"./core/proxy/relay-credential-router.js";import{upgradeStatusSnapshot as Ze}from"./core/upgrade/npm-upgrader.js";import{resolveClientVersion as et}from"./core/util/client-version.js";import{parseCliArgs as tt}from"./core/util/cli-args.js";const{command:D,flags:R,unknownFlags:q}=tt(process.argv.slice(2));if(q.length>0&&(console.error(`Unknown option${q.length>1?"s":""}: ${q.map(r=>`--${r}`).join(", ")}
4
4
  Run \`grix-connector --help\` to see the supported commands and options.`),process.exit(1)),R.version&&(console.log(et()),process.exit(0)),R.help&&(console.log(`grix-connector \u2014 Unified AI Agent Bridge
5
5
 
@@ -1,3 +1,3 @@
1
- import{createHash as L}from"node:crypto";import h from"node:fs";import E from"node:os";import o from"node:path";import D from"node:process";import{mkdir as _}from"node:fs/promises";import{setTimeout as H}from"node:timers/promises";import{resolveRuntimePaths as S}from"../core/config/paths.js";import{inspectDaemonState as g}from"../runtime/service-state.js";import{isProcessRunning as T,runCommand as u,terminateProcessTree as $,waitForProcessExit as b}from"./process-control.js";import{isLockHolderIdentityVerified as F,readDaemonLock as P}from"../runtime/daemon-lock.js";import{ServiceInstallStore as B}from"./service-install-store.js";import{getPlatformServiceAdapter as N}from"./platform-adapter.js";import{captureLogCursor as v,formatLogDelta as w}from"./log-diagnostics.js";import{LAUNCHER_FILE as C,bootstrapVersionFromPackage as q,compareVersions as j,ensureLauncher as M,launcherPath as O,packagedLauncherPath as V,readLauncherTemplateVersion as G,readPackageVersionAt as z,readPointer as x,switchCurrent as J,versionEntry as A}from"../core/upgrade/version-store.js";import{buildServiceID as y,resolveWindowsServiceRuntimeDir as U,resolveServiceInstallRecordPath as W,resolveServiceStderrPath as K,resolveServiceStdoutPath as Q}from"./service-paths.js";class ft{adapter;platform;homeDir;uid;nodePath;cliPath;waitForExit;terminateTree;constructor(e){this.platform=e.platform??D.platform,this.homeDir=e.homeDir??E.homedir(),this.uid=e.uid??D.getuid?.()??0,this.nodePath=e.nodePath??D.execPath,this.cliPath=e.cliPath,this.adapter=N(this.platform),this.waitForExit=e.processControl?.waitForExit??b,this.terminateTree=e.processControl?.terminateTree??$}normalizeRootDir(e){return o.resolve(String(e??"").trim())}resolveLocalServiceBaseDir(){if(this.platform==="darwin")return o.join(this.homeDir,"Library","Application Support","grix-connector");if(this.platform==="win32"){const e=String(D.env.LOCALAPPDATA??"").trim();return o.join(e&&o.isAbsolute(e)?e:o.join(this.homeDir,"AppData","Local"),"grix-connector")}return null}resolveServiceRuntimeDir(e){const t=this.resolveLocalServiceBaseDir();return t?this.platform==="win32"?U(this.normalizeRootDir(e),D.env,this.homeDir):o.join(t,"runtime"):null}resolveServiceCliPath(e){const t=this.resolveServiceRuntimeDir(e);return t?O(t):this.cliPath}resolveSourcePackageDir(){return o.dirname(o.dirname(this.cliPath))}async adoptLauncher(e){const t=this.normalizeRootDir(e);if(!this.resolveServiceRuntimeDir(t))return!1;const r=await this.createStore(t).load();return!r||r.cli_path===this.resolveServiceCliPath(t)?!1:(await this.refreshDescriptor(t,r),!0)}prepareServiceRuntime(e){const t=this.resolveServiceRuntimeDir(e);if(!t)return;const r=this.resolveSourcePackageDir();h.mkdirSync(t,{recursive:!0});const s=z(r);if(s){if(!M(t,r))throw new Error(`Package at ${r} does not ship ${C}; refusing to point the service at it`);const i=x(t,"current");(!i||!h.existsSync(A(t,i))||j(s,i)>0)&&(q(t,r,s),J(t,s))}if(this.platform==="win32"||this.platform==="darwin"){const i=o.join(r,"scripts","upgrade-guardian.mjs");if(h.existsSync(i)){const a=o.join(this.normalizeRootDir(e),"bin"),d=o.join(a,"upgrade-guardian.mjs"),c=o.join(a,"upgrade-guardian.fallback.mjs"),n=`${d}.tmp-${D.pid}`;h.mkdirSync(a,{recursive:!0});try{h.copyFileSync(i,n),h.renameSync(n,d),h.copyFileSync(i,c)}finally{h.rmSync(n,{force:!0})}}if(this.platform==="darwin"){const a=o.join(r,"scripts","upgrade-guardian.sh");if(h.existsSync(a)){const d=o.join(this.normalizeRootDir(e),"bin","upgrade-guardian.sh"),c=`${d}.tmp-${D.pid}`;try{h.copyFileSync(a,c),h.chmodSync(c,493),h.renameSync(c,d)}finally{h.rmSync(c,{force:!0})}}}}}buildDescriptor(e,t,r=""){const s=this.normalizeRootDir(e),i=this.resolveServiceCliPath(s);return{schema_version:1,platform:this.platform,service_id:y(s,this.platform),node_path:this.nodePath,cli_path:i,cli_hash:this.computeServiceHash(i)??"",definition_path:r,config_dir:t??"",installed_at:0,updated_at:0}}createStore(e){return new B(W(this.normalizeRootDir(e)))}toAdapterPayload(e,t){return{serviceID:e.service_id,nodePath:e.node_path,cliPath:e.cli_path,configDir:e.config_dir||void 0,rootDir:this.normalizeRootDir(t),environmentPath:this.buildServiceEnvironmentPath()}}async resolveServiceLogPaths(e){const t=this.resolveLocalServiceBaseDir();if(t){const r=o.join(this.platform==="darwin"?o.join(this.homeDir,"Library","Logs","grix-connector"):o.join(t,"logs"),y(this.normalizeRootDir(e),this.platform));return await _(r,{recursive:!0}),{stdoutPath:o.join(r,"daemon.out.log"),stderrPath:o.join(r,"daemon.err.log")}}return await _(o.join(e,"service"),{recursive:!0}),{stdoutPath:Q(e),stderrPath:K(e)}}buildServiceEnvironmentPath(){const e=[o.dirname(this.nodePath),"/opt/homebrew/bin","/usr/local/bin","/usr/bin","/bin","/usr/sbin","/sbin"],t=String(D.env.PATH??"").split(o.delimiter).filter(i=>i&&o.isAbsolute(i)).filter(i=>!i.includes("/node_modules/.bin")).filter(i=>!i.includes("/node-gyp-bin")),r=[],s=new Set;for(const i of[...e,...t]){if(s.has(i))continue;if(s.add(i),[...r,i].join(o.delimiter).length>2048)break;r.push(i)}return r.join(o.delimiter)}toStartParams(e){return{serviceID:e.service_id,definitionPath:e.definition_path,uid:this.uid,homeDir:this.homeDir,runCommand:u}}toStopParams(e){return{serviceID:e.service_id,uid:this.uid,homeDir:this.homeDir,runCommand:u}}toRestartParams(e){return{serviceID:e.service_id,definitionPath:e.definition_path,uid:this.uid,homeDir:this.homeDir,runCommand:u}}toUninstallParams(e){return{serviceID:e.service_id,definitionPath:e.definition_path,uid:this.uid,homeDir:this.homeDir,runCommand:u}}async loadDescriptor(e){const t=this.createStore(e),r=await t.load();if(!r)throw new Error("\u540E\u53F0\u670D\u52A1\u8FD8\u6CA1\u6709\u5B89\u88C5\uFF0C\u8BF7\u5148\u6267\u884C service install\u3002");return{store:t,descriptor:r}}isDescriptorCurrent(e,t){if(t.node_path!==this.nodePath||t.cli_path!==this.resolveServiceCliPath(e)||!t.cli_hash)return!1;const r=this.computeServiceHash(t.cli_path),s=this.computeServiceHash(this.cliPath);return r!==null&&s!==null&&r===t.cli_hash&&s===t.cli_hash}computeFileHash(e){try{const t=h.readFileSync(e);return L("sha256").update(t).digest("hex")}catch{return null}}computeServiceHash(e){return this.resolveLocalServiceBaseDir()?this.computeLauncherHash(e):this.computeFileHash(e)}computeLauncherHash(e){try{const t=this.resolveSourcePackageDir(),r=z(t)??"",s=o.basename(e)===C,i=s?o.dirname(e):null,a=s?e:V(t);if(!a)return null;const d=G(a);if(d<0)return null;let c=!0;if(i){const n=x(i,"current");c=!!n&&h.existsSync(A(i,n))&&j(n,r)>=0}return c?L("sha256").update(`launcher:${d}`).update(`source:${r}`).digest("hex"):null}catch{return null}}computeRuntimeDirHash(e){try{const t=o.dirname(e),r=L("sha256"),s=a=>{const d=h.readdirSync(a,{withFileTypes:!0}).sort((c,n)=>c.name.localeCompare(n.name));for(const c of d){const n=o.join(a,c.name);r.update(o.relative(t,n)),c.isDirectory()?s(n):c.isFile()&&r.update(h.readFileSync(n))}};s(t);const i=o.join(o.dirname(t),"package.json");return h.existsSync(i)&&(r.update("package.json"),r.update(h.readFileSync(i))),r.digest("hex")}catch{return null}}async refreshDescriptor(e,t){const r=this.normalizeRootDir(e);await _(o.join(r,"service"),{recursive:!0}),this.prepareServiceRuntime(r);const s=this.resolveServiceCliPath(r),i={...t,platform:this.platform,service_id:t.service_id||y(r,this.platform),node_path:this.nodePath,cli_path:s,cli_hash:this.computeServiceHash(s)??"",config_dir:t.config_dir},a=await this.resolveServiceLogPaths(r),d=await this.adapter.install({...this.toAdapterPayload(i,r),stdoutPath:a.stdoutPath,stderrPath:a.stderrPath,homeDir:this.homeDir,uid:this.uid,runCommand:u}),c={...i,installed_at:t.installed_at,definition_path:d?.definitionPath||i.definition_path,updated_at:Date.now()};return await this.createStore(r).save(c),c}async resolveActiveDescriptor(e){const{descriptor:t}=await this.loadDescriptor(e);return this.isDescriptorCurrent(e,t)?t:this.refreshDescriptor(e,t)}async discoverConflictingServices(e){const t=this.normalizeRootDir(e),r=y(t,this.platform);return(await this.adapter.discoverServices({homeDir:this.homeDir,runCommand:u})).filter(i=>i.serviceID!==r)}async cleanupConflictingServices(e){const t=await this.discoverConflictingServices(e);if(t.length>0){const s=t.map(i=>i.serviceID).join(", ");console.log(`\u53D1\u73B0 ${t.length} \u4E2A\u6B8B\u7559\u670D\u52A1\uFF0C\u6B63\u5728\u68C0\u67E5: ${s}`)}let r=0;for(const s of t){if(await this.adapter.isServiceLoaded({serviceID:s.serviceID,uid:this.uid,homeDir:this.homeDir,runCommand:u})){console.log(`\u8DF3\u8FC7\u6B63\u5728\u8FD0\u884C\u7684\u670D\u52A1: ${s.serviceID}`);continue}try{await this.adapter.stop({serviceID:s.serviceID,uid:this.uid,homeDir:this.homeDir,runCommand:u})}catch{}try{await this.adapter.uninstall({serviceID:s.serviceID,definitionPath:s.definitionPath,uid:this.uid,homeDir:this.homeDir,runCommand:u}),r++}catch{}}return r}captureDaemonBaseline(e){return{status:g(e.daemonStatusFile),lock:P(e.daemonLockFile)}}isDaemonReady(e,t,r){const s=g(e.daemonStatusFile),i=P(e.daemonLockFile);if(!s.running||s.state!=="running"||s.pid<=0||!i||i.pid!==s.pid||!T(i.pid)||typeof i.root_dir!="string"||o.resolve(i.root_dir)!==o.resolve(e.rootDir)||typeof i.instance_id!="string"||i.instance_id.length===0||s.instance_id!==i.instance_id)return!1;const a=s.pid!==t.status.pid||s.state!==t.status.state||s.updated_at!==t.status.updated_at||s.instance_id!==t.status.instance_id,d=!t.lock||i.pid!==t.lock.pid||i.acquired_at!==t.lock.acquired_at||i.instance_id!==t.lock.instance_id,c=a&&d,n=t.status.state==="starting"&&t.status.instance_id===i.instance_id&&t.lock?.pid===i.pid&&t.lock.instance_id===i.instance_id&&s.instance_id===i.instance_id&&s.updated_at!==t.status.updated_at,m=t.lock?.pid===i.pid&&t.lock.instance_id===i.instance_id&&t.lock.acquired_at===i.acquired_at&&(t.status.pid!==i.pid||t.status.instance_id!==i.instance_id||t.status.state!=="running")&&s.pid===i.pid&&s.instance_id===i.instance_id&&s.state==="running"&&s.updated_at!==t.status.updated_at;return!F(i)&&!c&&!n&&!m?!1:r?c||n||m:!0}canForceTerminateDaemon(e,t){const r=P(e.daemonLockFile);return t>0&&r!==null&&r.pid===t&&typeof r.root_dir=="string"&&o.resolve(r.root_dir)===o.resolve(e.rootDir)&&F(r)}defaultDaemonStartTimeoutMs(){return this.platform==="darwin"?5e4:2e4}async waitForDaemonStarted(e,t={}){const r=t.timeoutMs??this.defaultDaemonStartTimeoutMs(),s=t.baseline??this.captureDaemonBaseline(e),i=t.requireNewDaemon??!1,a=t.stdoutLogFile??e.stdoutLogFile,d=t.stderrLogFile??e.stderrLogFile,c=t.stdoutLogCursor??v(a),n=t.stderrLogCursor??v(d),m=Date.now();for(;Date.now()-m<r;){if(this.isDaemonReady(e,s,i))return;const f=g(e.daemonStatusFile);if(f.state==="failed"&&(s.status.state!=="failed"||f.pid!==s.status.pid||f.updated_at!==s.status.updated_at||f.instance_id!==s.status.instance_id)){const R=P(e.daemonLockFile),I=["daemon start failed",`status=${JSON.stringify(f)}`,`lock=${R?JSON.stringify(R):"missing"}`,w("stderr",d,n),w("stdout",a,c)].filter(Boolean);throw new Error(I.join(`
2
- `))}await H(100)}const p=P(e.daemonLockFile),l=g(e.daemonStatusFile),k=[`daemon start timeout (${r}ms)`,`status=${JSON.stringify(l)}`,`lock=${p?JSON.stringify(p):"missing"}`,w("stderr",d,n),w("stdout",a,c)].filter(Boolean);throw new Error(k.join(`
3
- `))}async install(e){const t=this.normalizeRootDir(e.rootDir);await this.cleanupConflictingServices(t),await _(o.join(t,"service"),{recursive:!0}),this.prepareServiceRuntime(t);const r=S(t),s=this.captureDaemonBaseline(r),i=this.buildDescriptor(t,e.configDir),a=await this.resolveServiceLogPaths(t),d=v(a.stdoutPath),c=v(a.stderrPath),n=await this.adapter.isServiceLoaded({serviceID:i.service_id,uid:this.uid,homeDir:this.homeDir,runCommand:u}),m=await this.adapter.install({...this.toAdapterPayload(i,t),stdoutPath:a.stdoutPath,stderrPath:a.stderrPath,homeDir:this.homeDir,uid:this.uid,runCommand:u}),p={...i,definition_path:m?.definitionPath??"",installed_at:Date.now(),updated_at:Date.now()};return await this.createStore(t).save(p),n?await this.adapter.restart(this.toRestartParams(p)):await this.adapter.start(this.toStartParams(p)),await this.waitForDaemonStarted(r,{baseline:s,requireNewDaemon:!0,stdoutLogFile:a.stdoutPath,stderrLogFile:a.stderrPath,stdoutLogCursor:d,stderrLogCursor:c}),this.status({rootDir:t})}async start(e){const t=this.normalizeRootDir(e.rootDir);await this.cleanupConflictingServices(t);const r=S(t),s=this.captureDaemonBaseline(r),i=s.status,a=v(r.stdoutLogFile),d=v(r.stderrLogFile),n=await this.createStore(t).load();if(i.running&&n&&this.isDescriptorCurrent(t,n)){if(i.state==="starting"){const l=s.lock;if(l!==null&&l.pid===i.pid&&typeof l.root_dir=="string"&&o.resolve(l.root_dir)===o.resolve(r.rootDir)&&typeof l.instance_id=="string"&&l.instance_id.length>0&&i.instance_id===l.instance_id&&F(l)){const f=await this.resolveServiceLogPaths(t);return await this.waitForDaemonStarted(r,{baseline:s,stdoutLogFile:f.stdoutPath,stderrLogFile:f.stderrPath,stdoutLogCursor:a,stderrLogCursor:d}),this.status({rootDir:t})}}if(this.isDaemonReady(r,s,!1))return this.status({rootDir:t})}const m=n?this.isDescriptorCurrent(t,n)&&i.running?n:await this.refreshDescriptor(t,n):await this.resolveActiveDescriptor(t),p=await this.resolveServiceLogPaths(t);try{i.running?await this.adapter.restart(this.toRestartParams(m)):await this.adapter.start(this.toStartParams(m))}catch(l){if(l instanceof Error&&l.message.includes("plist missing"))await this.adapter.install({...this.toAdapterPayload(m,t),stdoutPath:p.stdoutPath,stderrPath:p.stderrPath,homeDir:this.homeDir,uid:this.uid,runCommand:u}),await this.adapter.start(this.toStartParams(m));else throw l}return await this.waitForDaemonStarted(r,{baseline:s,requireNewDaemon:!0,stdoutLogFile:p.stdoutPath,stderrLogFile:p.stderrPath,stdoutLogCursor:a,stderrLogCursor:d}),this.status({rootDir:t})}async stop(e){const t=this.normalizeRootDir(e.rootDir),{descriptor:r}=await this.loadDescriptor(t),s=S(t),i=g(s.daemonStatusFile);return await this.adapter.stop(this.toStopParams(r)),i.running&&i.pid&&(await this.waitForExit(i.pid,{timeoutMs:5e3})||this.canForceTerminateDaemon(s,i.pid)&&(await this.terminateTree(i.pid,{platform:this.platform,runCommandImpl:u}),await this.waitForExit(i.pid,{timeoutMs:5e3}))),this.status({rootDir:t})}async restart(e){const t=this.normalizeRootDir(e.rootDir);await this.cleanupConflictingServices(t);const r=await this.resolveActiveDescriptor(t),s=S(t),i=this.captureDaemonBaseline(s),a=await this.resolveServiceLogPaths(t),d=v(a.stdoutPath),c=v(a.stderrPath);try{await this.adapter.restart(this.toRestartParams(r))}catch(n){if(n instanceof Error&&n.message.includes("plist missing"))await this.adapter.install({...this.toAdapterPayload(r,t),stdoutPath:a.stdoutPath,stderrPath:a.stderrPath,homeDir:this.homeDir,uid:this.uid,runCommand:u}),await this.adapter.start(this.toStartParams(r));else throw n}return await this.waitForDaemonStarted(s,{baseline:i,requireNewDaemon:!0,stdoutLogFile:a.stdoutPath,stderrLogFile:a.stderrPath,stdoutLogCursor:d,stderrLogCursor:c}),this.status({rootDir:t})}async uninstall(e){const t=this.normalizeRootDir(e.rootDir),r=this.createStore(t),s=await r.load();return s&&(await this.adapter.uninstall(this.toUninstallParams(s)),await r.clear()),this.status({rootDir:t})}async status(e){const t=this.normalizeRootDir(e.rootDir),s=await this.createStore(t).load(),i=S(t),a=g(i.daemonStatusFile);return s?{installed:!0,install_state:this.isDescriptorCurrent(t,s)?"current":"stale",service_kind:this.adapter.kind,service_id:s.service_id,definition_path:s.definition_path,root_dir:t,daemon_state:a.state,pid:a.pid,connection_state:a.connection_state,updated_at:a.updated_at,reason:a.reason}:{installed:!1,install_state:"missing",service_kind:this.adapter.kind,root_dir:t,daemon_state:a.state,pid:a.pid,reason:a.reason}}}export{ft as ServiceManager};
1
+ import{createHash as F}from"node:crypto";import h from"node:fs";import $ from"node:os";import o from"node:path";import D from"node:process";import{mkdir as _}from"node:fs/promises";import{setTimeout as H}from"node:timers/promises";import{resolveRuntimePaths as S}from"../core/config/paths.js";import{inspectDaemonState as g}from"../runtime/service-state.js";import{isProcessRunning as T,runCommand as m,terminateProcessTree as b,waitForProcessExit as B}from"./process-control.js";import{isLockHolderIdentityVerified as k,readDaemonLock as P}from"../runtime/daemon-lock.js";import{ServiceInstallStore as N}from"./service-install-store.js";import{getPlatformServiceAdapter as q}from"./platform-adapter.js";import{captureLogCursor as v,formatLogDelta as w}from"./log-diagnostics.js";import{LAUNCHER_FILE as j,bootstrapVersionFromPackage as M,compareVersions as z,ensureLauncher as O,launcherPath as V,packagedLauncherPath as G,readLauncherTemplateVersion as J,readPackageVersionAt as x,readPointer as A,switchCurrent as U,versionEntry as I}from"../core/upgrade/version-store.js";import{buildServiceID as y,resolveWindowsServiceRuntimeDir as W,resolveServiceInstallRecordPath as K,resolveServiceStderrPath as Q,resolveServiceStdoutPath as X}from"./service-paths.js";function Y(L){try{return h.realpathSync(L)}catch{return L}}class Dt{adapter;platform;homeDir;uid;nodePath;cliPath;waitForExit;terminateTree;constructor(e){this.platform=e.platform??D.platform,this.homeDir=e.homeDir??$.homedir(),this.uid=e.uid??D.getuid?.()??0,this.nodePath=e.nodePath??D.execPath,this.cliPath=Y(e.cliPath),this.adapter=q(this.platform),this.waitForExit=e.processControl?.waitForExit??B,this.terminateTree=e.processControl?.terminateTree??b}normalizeRootDir(e){return o.resolve(String(e??"").trim())}resolveLocalServiceBaseDir(){if(this.platform==="darwin")return o.join(this.homeDir,"Library","Application Support","grix-connector");if(this.platform==="win32"){const e=String(D.env.LOCALAPPDATA??"").trim();return o.join(e&&o.isAbsolute(e)?e:o.join(this.homeDir,"AppData","Local"),"grix-connector")}return null}resolveServiceRuntimeDir(e){const t=this.resolveLocalServiceBaseDir();return t?this.platform==="win32"?W(this.normalizeRootDir(e),D.env,this.homeDir):o.join(t,"runtime"):null}resolveServiceCliPath(e){const t=this.resolveServiceRuntimeDir(e);return t?V(t):this.cliPath}resolveSourcePackageDir(){return o.dirname(o.dirname(this.cliPath))}async adoptLauncher(e){const t=this.normalizeRootDir(e);if(!this.resolveServiceRuntimeDir(t))return!1;const r=await this.createStore(t).load();return!r||r.cli_path===this.resolveServiceCliPath(t)?!1:(await this.refreshDescriptor(t,r),!0)}prepareServiceRuntime(e){const t=this.resolveServiceRuntimeDir(e);if(!t)return;const r=this.resolveSourcePackageDir();h.mkdirSync(t,{recursive:!0});const s=x(r);if(!s)throw new Error(`Cannot read package version at ${r} (entry ${this.cliPath}); refusing to install the service`);if(!O(t,r))throw new Error(`Package at ${r} does not ship ${j}; refusing to point the service at it`);const i=A(t,"current");if((!i||!h.existsSync(I(t,i))||z(s,i)>0)&&(M(t,r,s),U(t,s)),this.platform==="win32"||this.platform==="darwin"){const a=o.join(r,"scripts","upgrade-guardian.mjs");if(h.existsSync(a)){const d=o.join(this.normalizeRootDir(e),"bin"),c=o.join(d,"upgrade-guardian.mjs"),n=o.join(d,"upgrade-guardian.fallback.mjs"),l=`${c}.tmp-${D.pid}`;h.mkdirSync(d,{recursive:!0});try{h.copyFileSync(a,l),h.renameSync(l,c),h.copyFileSync(a,n)}finally{h.rmSync(l,{force:!0})}}if(this.platform==="darwin"){const d=o.join(r,"scripts","upgrade-guardian.sh");if(h.existsSync(d)){const c=o.join(this.normalizeRootDir(e),"bin","upgrade-guardian.sh"),n=`${c}.tmp-${D.pid}`;try{h.copyFileSync(d,n),h.chmodSync(n,493),h.renameSync(n,c)}finally{h.rmSync(n,{force:!0})}}}}}buildDescriptor(e,t,r=""){const s=this.normalizeRootDir(e),i=this.resolveServiceCliPath(s);return{schema_version:1,platform:this.platform,service_id:y(s,this.platform),node_path:this.nodePath,cli_path:i,cli_hash:this.computeServiceHash(i)??"",definition_path:r,config_dir:t??"",installed_at:0,updated_at:0}}createStore(e){return new N(K(this.normalizeRootDir(e)))}toAdapterPayload(e,t){return{serviceID:e.service_id,nodePath:e.node_path,cliPath:e.cli_path,configDir:e.config_dir||void 0,rootDir:this.normalizeRootDir(t),environmentPath:this.buildServiceEnvironmentPath()}}async resolveServiceLogPaths(e){const t=this.resolveLocalServiceBaseDir();if(t){const r=o.join(this.platform==="darwin"?o.join(this.homeDir,"Library","Logs","grix-connector"):o.join(t,"logs"),y(this.normalizeRootDir(e),this.platform));return await _(r,{recursive:!0}),{stdoutPath:o.join(r,"daemon.out.log"),stderrPath:o.join(r,"daemon.err.log")}}return await _(o.join(e,"service"),{recursive:!0}),{stdoutPath:X(e),stderrPath:Q(e)}}buildServiceEnvironmentPath(){const e=[o.dirname(this.nodePath),"/opt/homebrew/bin","/usr/local/bin","/usr/bin","/bin","/usr/sbin","/sbin"],t=String(D.env.PATH??"").split(o.delimiter).filter(i=>i&&o.isAbsolute(i)).filter(i=>!i.includes("/node_modules/.bin")).filter(i=>!i.includes("/node-gyp-bin")),r=[],s=new Set;for(const i of[...e,...t]){if(s.has(i))continue;if(s.add(i),[...r,i].join(o.delimiter).length>2048)break;r.push(i)}return r.join(o.delimiter)}toStartParams(e){return{serviceID:e.service_id,definitionPath:e.definition_path,uid:this.uid,homeDir:this.homeDir,runCommand:m}}toStopParams(e){return{serviceID:e.service_id,uid:this.uid,homeDir:this.homeDir,runCommand:m}}toRestartParams(e){return{serviceID:e.service_id,definitionPath:e.definition_path,uid:this.uid,homeDir:this.homeDir,runCommand:m}}toUninstallParams(e){return{serviceID:e.service_id,definitionPath:e.definition_path,uid:this.uid,homeDir:this.homeDir,runCommand:m}}async loadDescriptor(e){const t=this.createStore(e),r=await t.load();if(!r)throw new Error("\u540E\u53F0\u670D\u52A1\u8FD8\u6CA1\u6709\u5B89\u88C5\uFF0C\u8BF7\u5148\u6267\u884C service install\u3002");return{store:t,descriptor:r}}isDescriptorCurrent(e,t){if(t.node_path!==this.nodePath||t.cli_path!==this.resolveServiceCliPath(e)||!t.cli_hash)return!1;const r=this.computeServiceHash(t.cli_path),s=this.computeServiceHash(this.cliPath);return r!==null&&s!==null&&r===t.cli_hash&&s===t.cli_hash}computeFileHash(e){try{const t=h.readFileSync(e);return F("sha256").update(t).digest("hex")}catch{return null}}computeServiceHash(e){return this.resolveLocalServiceBaseDir()?this.computeLauncherHash(e):this.computeFileHash(e)}computeLauncherHash(e){try{const t=this.resolveSourcePackageDir(),r=x(t)??"",s=o.basename(e)===j,i=s?o.dirname(e):null,a=s?e:G(t);if(!a)return null;const d=J(a);if(d<0)return null;let c=!0;if(i){const n=A(i,"current");c=!!n&&h.existsSync(I(i,n))&&z(n,r)>=0}return c?F("sha256").update(`launcher:${d}`).update(`source:${r}`).digest("hex"):null}catch{return null}}computeRuntimeDirHash(e){try{const t=o.dirname(e),r=F("sha256"),s=a=>{const d=h.readdirSync(a,{withFileTypes:!0}).sort((c,n)=>c.name.localeCompare(n.name));for(const c of d){const n=o.join(a,c.name);r.update(o.relative(t,n)),c.isDirectory()?s(n):c.isFile()&&r.update(h.readFileSync(n))}};s(t);const i=o.join(o.dirname(t),"package.json");return h.existsSync(i)&&(r.update("package.json"),r.update(h.readFileSync(i))),r.digest("hex")}catch{return null}}async refreshDescriptor(e,t){const r=this.normalizeRootDir(e);await _(o.join(r,"service"),{recursive:!0}),this.prepareServiceRuntime(r);const s=this.resolveServiceCliPath(r),i={...t,platform:this.platform,service_id:t.service_id||y(r,this.platform),node_path:this.nodePath,cli_path:s,cli_hash:this.computeServiceHash(s)??"",config_dir:t.config_dir},a=await this.resolveServiceLogPaths(r),d=await this.adapter.install({...this.toAdapterPayload(i,r),stdoutPath:a.stdoutPath,stderrPath:a.stderrPath,homeDir:this.homeDir,uid:this.uid,runCommand:m}),c={...i,installed_at:t.installed_at,definition_path:d?.definitionPath||i.definition_path,updated_at:Date.now()};return await this.createStore(r).save(c),c}async resolveActiveDescriptor(e){const{descriptor:t}=await this.loadDescriptor(e);return this.isDescriptorCurrent(e,t)?t:this.refreshDescriptor(e,t)}async discoverConflictingServices(e){const t=this.normalizeRootDir(e),r=y(t,this.platform);return(await this.adapter.discoverServices({homeDir:this.homeDir,runCommand:m})).filter(i=>i.serviceID!==r)}async cleanupConflictingServices(e){const t=await this.discoverConflictingServices(e);if(t.length>0){const s=t.map(i=>i.serviceID).join(", ");console.log(`\u53D1\u73B0 ${t.length} \u4E2A\u6B8B\u7559\u670D\u52A1\uFF0C\u6B63\u5728\u68C0\u67E5: ${s}`)}let r=0;for(const s of t){if(await this.adapter.isServiceLoaded({serviceID:s.serviceID,uid:this.uid,homeDir:this.homeDir,runCommand:m})){console.log(`\u8DF3\u8FC7\u6B63\u5728\u8FD0\u884C\u7684\u670D\u52A1: ${s.serviceID}`);continue}try{await this.adapter.stop({serviceID:s.serviceID,uid:this.uid,homeDir:this.homeDir,runCommand:m})}catch{}try{await this.adapter.uninstall({serviceID:s.serviceID,definitionPath:s.definitionPath,uid:this.uid,homeDir:this.homeDir,runCommand:m}),r++}catch{}}return r}captureDaemonBaseline(e){return{status:g(e.daemonStatusFile),lock:P(e.daemonLockFile)}}isDaemonReady(e,t,r){const s=g(e.daemonStatusFile),i=P(e.daemonLockFile);if(!s.running||s.state!=="running"||s.pid<=0||!i||i.pid!==s.pid||!T(i.pid)||typeof i.root_dir!="string"||o.resolve(i.root_dir)!==o.resolve(e.rootDir)||typeof i.instance_id!="string"||i.instance_id.length===0||s.instance_id!==i.instance_id)return!1;const a=s.pid!==t.status.pid||s.state!==t.status.state||s.updated_at!==t.status.updated_at||s.instance_id!==t.status.instance_id,d=!t.lock||i.pid!==t.lock.pid||i.acquired_at!==t.lock.acquired_at||i.instance_id!==t.lock.instance_id,c=a&&d,n=t.status.state==="starting"&&t.status.instance_id===i.instance_id&&t.lock?.pid===i.pid&&t.lock.instance_id===i.instance_id&&s.instance_id===i.instance_id&&s.updated_at!==t.status.updated_at,l=t.lock?.pid===i.pid&&t.lock.instance_id===i.instance_id&&t.lock.acquired_at===i.acquired_at&&(t.status.pid!==i.pid||t.status.instance_id!==i.instance_id||t.status.state!=="running")&&s.pid===i.pid&&s.instance_id===i.instance_id&&s.state==="running"&&s.updated_at!==t.status.updated_at;return!k(i)&&!c&&!n&&!l?!1:r?c||n||l:!0}canForceTerminateDaemon(e,t){const r=P(e.daemonLockFile);return t>0&&r!==null&&r.pid===t&&typeof r.root_dir=="string"&&o.resolve(r.root_dir)===o.resolve(e.rootDir)&&k(r)}defaultDaemonStartTimeoutMs(){return this.platform==="darwin"?5e4:2e4}async waitForDaemonStarted(e,t={}){const r=t.timeoutMs??this.defaultDaemonStartTimeoutMs(),s=t.baseline??this.captureDaemonBaseline(e),i=t.requireNewDaemon??!1,a=t.stdoutLogFile??e.stdoutLogFile,d=t.stderrLogFile??e.stderrLogFile,c=t.stdoutLogCursor??v(a),n=t.stderrLogCursor??v(d),l=Date.now();for(;Date.now()-l<r;){if(this.isDaemonReady(e,s,i))return;const f=g(e.daemonStatusFile);if(f.state==="failed"&&(s.status.state!=="failed"||f.pid!==s.status.pid||f.updated_at!==s.status.updated_at||f.instance_id!==s.status.instance_id)){const C=P(e.daemonLockFile),E=["daemon start failed",`status=${JSON.stringify(f)}`,`lock=${C?JSON.stringify(C):"missing"}`,w("stderr",d,n),w("stdout",a,c)].filter(Boolean);throw new Error(E.join(`
2
+ `))}await H(100)}const p=P(e.daemonLockFile),u=g(e.daemonStatusFile),R=[`daemon start timeout (${r}ms)`,`status=${JSON.stringify(u)}`,`lock=${p?JSON.stringify(p):"missing"}`,w("stderr",d,n),w("stdout",a,c)].filter(Boolean);throw new Error(R.join(`
3
+ `))}async install(e){const t=this.normalizeRootDir(e.rootDir);await this.cleanupConflictingServices(t),await _(o.join(t,"service"),{recursive:!0}),this.prepareServiceRuntime(t);const r=S(t),s=this.captureDaemonBaseline(r),i=this.buildDescriptor(t,e.configDir),a=await this.resolveServiceLogPaths(t),d=v(a.stdoutPath),c=v(a.stderrPath),n=await this.adapter.isServiceLoaded({serviceID:i.service_id,uid:this.uid,homeDir:this.homeDir,runCommand:m}),l=await this.adapter.install({...this.toAdapterPayload(i,t),stdoutPath:a.stdoutPath,stderrPath:a.stderrPath,homeDir:this.homeDir,uid:this.uid,runCommand:m}),p={...i,definition_path:l?.definitionPath??"",installed_at:Date.now(),updated_at:Date.now()};return await this.createStore(t).save(p),n?await this.adapter.restart(this.toRestartParams(p)):await this.adapter.start(this.toStartParams(p)),await this.waitForDaemonStarted(r,{baseline:s,requireNewDaemon:!0,stdoutLogFile:a.stdoutPath,stderrLogFile:a.stderrPath,stdoutLogCursor:d,stderrLogCursor:c}),this.status({rootDir:t})}async start(e){const t=this.normalizeRootDir(e.rootDir);await this.cleanupConflictingServices(t);const r=S(t),s=this.captureDaemonBaseline(r),i=s.status,a=v(r.stdoutLogFile),d=v(r.stderrLogFile),n=await this.createStore(t).load();if(i.running&&n&&this.isDescriptorCurrent(t,n)){if(i.state==="starting"){const u=s.lock;if(u!==null&&u.pid===i.pid&&typeof u.root_dir=="string"&&o.resolve(u.root_dir)===o.resolve(r.rootDir)&&typeof u.instance_id=="string"&&u.instance_id.length>0&&i.instance_id===u.instance_id&&k(u)){const f=await this.resolveServiceLogPaths(t);return await this.waitForDaemonStarted(r,{baseline:s,stdoutLogFile:f.stdoutPath,stderrLogFile:f.stderrPath,stdoutLogCursor:a,stderrLogCursor:d}),this.status({rootDir:t})}}if(this.isDaemonReady(r,s,!1))return this.status({rootDir:t})}const l=n?this.isDescriptorCurrent(t,n)&&i.running?n:await this.refreshDescriptor(t,n):await this.resolveActiveDescriptor(t),p=await this.resolveServiceLogPaths(t);try{i.running?await this.adapter.restart(this.toRestartParams(l)):await this.adapter.start(this.toStartParams(l))}catch(u){if(u instanceof Error&&u.message.includes("plist missing"))await this.adapter.install({...this.toAdapterPayload(l,t),stdoutPath:p.stdoutPath,stderrPath:p.stderrPath,homeDir:this.homeDir,uid:this.uid,runCommand:m}),await this.adapter.start(this.toStartParams(l));else throw u}return await this.waitForDaemonStarted(r,{baseline:s,requireNewDaemon:!0,stdoutLogFile:p.stdoutPath,stderrLogFile:p.stderrPath,stdoutLogCursor:a,stderrLogCursor:d}),this.status({rootDir:t})}async stop(e){const t=this.normalizeRootDir(e.rootDir),{descriptor:r}=await this.loadDescriptor(t),s=S(t),i=g(s.daemonStatusFile);return await this.adapter.stop(this.toStopParams(r)),i.running&&i.pid&&(await this.waitForExit(i.pid,{timeoutMs:5e3})||this.canForceTerminateDaemon(s,i.pid)&&(await this.terminateTree(i.pid,{platform:this.platform,runCommandImpl:m}),await this.waitForExit(i.pid,{timeoutMs:5e3}))),this.status({rootDir:t})}async restart(e){const t=this.normalizeRootDir(e.rootDir);await this.cleanupConflictingServices(t);const r=await this.resolveActiveDescriptor(t),s=S(t),i=this.captureDaemonBaseline(s),a=await this.resolveServiceLogPaths(t),d=v(a.stdoutPath),c=v(a.stderrPath);try{await this.adapter.restart(this.toRestartParams(r))}catch(n){if(n instanceof Error&&n.message.includes("plist missing"))await this.adapter.install({...this.toAdapterPayload(r,t),stdoutPath:a.stdoutPath,stderrPath:a.stderrPath,homeDir:this.homeDir,uid:this.uid,runCommand:m}),await this.adapter.start(this.toStartParams(r));else throw n}return await this.waitForDaemonStarted(s,{baseline:i,requireNewDaemon:!0,stdoutLogFile:a.stdoutPath,stderrLogFile:a.stderrPath,stdoutLogCursor:d,stderrLogCursor:c}),this.status({rootDir:t})}async uninstall(e){const t=this.normalizeRootDir(e.rootDir),r=this.createStore(t),s=await r.load();return s&&(await this.adapter.uninstall(this.toUninstallParams(s)),await r.clear()),this.status({rootDir:t})}async status(e){const t=this.normalizeRootDir(e.rootDir),s=await this.createStore(t).load(),i=S(t),a=g(i.daemonStatusFile);return s?{installed:!0,install_state:this.isDescriptorCurrent(t,s)?"current":"stale",service_kind:this.adapter.kind,service_id:s.service_id,definition_path:s.definition_path,root_dir:t,daemon_state:a.state,pid:a.pid,connection_state:a.connection_state,updated_at:a.updated_at,reason:a.reason}:{installed:!1,install_state:"missing",service_kind:this.adapter.kind,root_dir:t,daemon_state:a.state,pid:a.pid,reason:a.reason}}}export{Dt as ServiceManager};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grix-connector",
3
- "version": "4.4.2-beta.2",
3
+ "version": "4.4.2-beta.3",
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",