grix-connector 4.4.2-beta.1 → 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.1",
4
- "tarball": "grix-dsh-bridge-4.4.2-beta.1.tgz",
5
- "size": 22635,
3
+ "bridgeVersion": "4.4.2-beta.3",
4
+ "tarball": "grix-dsh-bridge-4.4.2-beta.3.tgz",
5
+ "size": 22636,
6
6
  "unpackedSize": 90343,
7
- "shasum": "f397e00010138d6affce51115deccb95d7ca8a3c",
8
- "integrity": "sha512-7UZp25if7DkfLqOafrDCHqlnRw6htEIHWz7EraK0k2n9jZ4qPUTSwZ9lEVxkT3GqHH8HlhdsMnlyWbQkG8Mp8g=="
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,4 +1,4 @@
1
- import{existsSync as T,readFileSync as M}from"node:fs";import{mkdir as k,readdir as F,readFile as S,rm as y,stat as q,writeFile as $}from"node:fs/promises";import d from"node:os";import c from"node:path";import{GRIX_HOME_ENV as C}from"../core/config/paths.js";import{isProcessRunning as I,runCommand as f,spawnDetached as _,isWindowsElevated as K,killProcessesByCommandLine as U}from"./process-control.js";import{getServicePrefix as D,parseConfigDirFromPlistXML as Z,parseConfigDirFromSystemdUnit as Y,resolveBareDaemonMarkerPath as x,resolveLinuxUserUnitPath as tt,resolveMacOSLaunchAgentPath as et}from"./service-paths.js";function g(t){return String(t??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function A(t){return`'${String(t??"").replace(/'/g,"'\\''")}'`}function G(t){return String(t??"").replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\n/g,"\\n").replace(/\r/g,"\\r")}function R(t){return`"${G(t).replace(/%/g,"%%")}"`}function nt(t){return`"${G(t).replace(/\$/g,()=>"$$").replace(/%/g,"%%")}"`}function H(t){return[t.nodePath,t.cliPath,...t.configDir?["--config-dir",t.configDir]:[]]}function rt(t){const e=H(t);return`<?xml version="1.0" encoding="UTF-8"?>
1
+ import{existsSync as b,readFileSync as U}from"node:fs";import{mkdir as k,readdir as F,readFile as S,rm as y,stat as Y,writeFile as $}from"node:fs/promises";import d from"node:os";import c from"node:path";import{GRIX_HOME_ENV as C}from"../core/config/paths.js";import{isProcessRunning as I,runCommand as f,spawnDetached as _,isWindowsElevated as tt,killProcessesByCommandLine as G}from"./process-control.js";import{getServicePrefix as D,parseConfigDirFromPlistXML as et,parseConfigDirFromSystemdUnit as nt,resolveBareDaemonMarkerPath as x,resolveLinuxUserUnitPath as rt,resolveMacOSLaunchAgentPath as it}from"./service-paths.js";function g(t){return String(t??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function R(t){return`'${String(t??"").replace(/'/g,"'\\''")}'`}function H(t){return String(t??"").replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\n/g,"\\n").replace(/\r/g,"\\r")}function A(t){return`"${H(t).replace(/%/g,"%%")}"`}function at(t){return`"${H(t).replace(/\$/g,()=>"$$").replace(/%/g,"%%")}"`}function j(t=process.env){const e=[];for(const n of["GRIX_HEALTH_PORT","GRIX_ADMIN_PORT"]){const r=String(t[n]??"").trim();/^\d{2,5}$/.test(r)&&e.push([n,r])}return e}function V(t){return[t.nodePath,t.cliPath,...t.configDir?["--config-dir",t.configDir]:[]]}function ot(t){const e=V(t);return`<?xml version="1.0" encoding="UTF-8"?>
2
2
  <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
3
  <plist version="1.0">
4
4
  <dict>
@@ -22,7 +22,9 @@ ${e.map(n=>` <string>${g(n)}</string>`).join(`
22
22
  <dict>
23
23
  <key>${C}</key>
24
24
  <string>${g(t.rootDir)}</string>
25
- ${t.environmentPath?` <key>PATH</key>
25
+ ${j().map(([n,r])=>` <key>${n}</key>
26
+ <string>${r}</string>
27
+ `).join("")}${t.environmentPath?` <key>PATH</key>
26
28
  <string>${g(t.environmentPath)}</string>
27
29
  `:""} </dict>
28
30
  <key>StandardOutPath</key>
@@ -31,24 +33,26 @@ ${t.environmentPath?` <key>PATH</key>
31
33
  <string>${g(t.stderrPath)}</string>
32
34
  </dict>
33
35
  </plist>
34
- `}function it(t){const e=H(t).map(n=>nt(n)).join(" ");return`[Unit]
36
+ `}function st(t){const e=V(t).map(n=>at(n)).join(" ");return`[Unit]
35
37
  Description=grix-connector daemon (${t.serviceID})
36
38
  After=network.target
37
39
 
38
40
  [Service]
39
41
  Type=simple
40
42
  ExecStart=${e}
41
- WorkingDirectory=${R(c.dirname(t.cliPath))}
42
- Environment=${R(`${C}=${t.rootDir}`)}
43
+ WorkingDirectory=${A(c.dirname(t.cliPath))}
44
+ Environment=${A(`${C}=${t.rootDir}`)}
45
+ ${j().map(([n,r])=>`Environment=${n}=${r}
46
+ `).join("")}
43
47
  Restart=always
44
48
  RestartSec=30
45
- StandardOutput=${R(`append:${t.stdoutPath}`)}
46
- StandardError=${R(`append:${t.stderrPath}`)}
49
+ StandardOutput=${A(`append:${t.stdoutPath}`)}
50
+ StandardError=${A(`append:${t.stderrPath}`)}
47
51
 
48
52
  [Install]
49
53
  WantedBy=default.target
50
- `}function P(t,e){const n=t.replace(/[^a-zA-Z0-9._-]/g,"_");return c.join(e,".grix",`${n}-wrapper.vbs`)}function j(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 N(t){return`'${String(t??"").replace(/'/g,"''")}'`}function at(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
51
- `);return{path:n,content:r}}async function ot(t){const{wrapperPath:e,shortcutPath:n,runCommand:r}=t,i=at(e,n);for(let a=1;a<=3;a+=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(T(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 st(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 ct(t){const e=`//B //NoLogo "${t.wrapperPath}"`;return`<?xml version="1.0" encoding="UTF-16"?>
54
+ `}function P(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 N(t){return`'${String(t??"").replace(/'/g,"''")}'`}function ct(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
55
+ `);return{path:n,content:r}}async function lt(t){const{wrapperPath:e,shortcutPath:n,runCommand:r}=t,i=ct(e,n);for(let a=1;a<=3;a+=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 ut(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 dt(t){const e=`//B //NoLogo "${t.wrapperPath}"`;return`<?xml version="1.0" encoding="UTF-16"?>
52
56
  <Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
53
57
  <RegistrationInfo>
54
58
  <Description>grix-connector daemon autostart</Description>
@@ -91,13 +95,13 @@ WantedBy=default.target
91
95
  </Exec>
92
96
  </Actions>
93
97
  </Task>
94
- `}async function lt(t){const{serviceID:e,wrapperPath:n,runCommand:r}=t,i=await st(r)??d.userInfo().username,a=c.join(c.dirname(n),`${e}-task.xml`);try{const o=ct({userId:i,wrapperPath:n});return await $(a,"\uFEFF"+o,"utf16le"),await r("schtasks",["/Create","/TN",e,"/XML",a,"/F"]),!0}catch{return!1}finally{await y(a,{force:!0}).catch(()=>{})}}const ut=3,dt="grix-wrapper-template:";function mt(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 = ${N(e)}`,"$exitCode = 1","try {"," [IO.Directory]::CreateDirectory([IO.Path]::GetDirectoryName($recordPath)) | Out-Null",` $p = Start-Process -FilePath ${N(t.nodePath)} -ArgumentList @(${n.map(N).join(", ")}) -WindowStyle Hidden -PassThru`,` $record = @{ pid = $p.Id; launched_at = [DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds(); boot_time = [DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds() - [Environment]::TickCount64; cli_path = ${N(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(`
95
- `),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}`,o=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"),m=c.join(t.rootDir,"data","upgrade-pending.json"),p=[`"${h(t.nodePath)}"`,`"${h(o)}"`,"activate","--root",`"${h(t.rootDir)}"`].join(" "),w=`${p} --wrapper-handoff`,L=[`"${h(t.nodePath)}"`,`"${h(s)}"`,"activate","--root",`"${h(t.rootDir)}"`].join(" "),b=`${L} --wrapper-handoff`;return[`' ${dt} ${ut}`,"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(L)}"`,`handoffGuardianFallbackCommand = "${h(b)}"`,`guardianPath = "${h(o)}"`,`guardianFallbackPath = "${h(s)}"`,`maintenanceFile = "${h(u)}"`,`pendingFile = "${h(m)}"`,'Set shell = CreateObject("WScript.Shell")','Set processEnv = shell.Environment("Process")','Set fso = CreateObject("Scripting.FileSystemObject")',`processEnv("${C}") = "${h(t.rootDir)}"`,'processEnv("GRIX_WRAPPER_SUPERVISED") = "1"',"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
96
- `)}function v(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 W(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 V(t){try{await q(t)}catch{throw new Error(`launchd plist missing: ${t}`)}}async function Q(t){const{serviceID:e,domain:n,runCommand:r}=t,i=`${n}/${e}`,a=await r("launchctl",["bootout",i],{allowFailure:!0});if(Number(a?.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 o=await r("launchctl",["print",i],{allowFailure:!0});if(Number(o?.exitCode??1)===0)throw new Error(`launchctl bootout ${i}: ${B(a)}`)}return}for(let l=0;l<20;l+=1){const o=await r("launchctl",["print",i],{allowFailure:!0});if(Number(o?.exitCode??1)!==0)return;await new Promise(s=>setTimeout(s,250))}throw new Error(`launchctl bootout ${i}: service remains loaded`)}async function X(t){const{serviceID:e,domain:n,definitionPath:r,runCommand:i}=t,a=`${n}/${e}`,l=await i("launchctl",["bootstrap",n,r],{allowFailure:!0});if(Number(l?.exitCode??-1)===0)return;const o=await i("launchctl",["print",a],{allowFailure:!0});Number(o?.exitCode??1)===0&&await Q(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 ft(){return{platform:"darwin",kind:"launchd",async install({serviceID:t,nodePath:e,cliPath:n,configDir:r,rootDir:i,stdoutPath:a,stderrPath:l,environmentPath:o="",homeDir:s=d.homedir()}){const u=et(t,s),m=W(i,a,l,s);return await k(c.dirname(u),{recursive:!0}),await $(u,rt({serviceID:t,nodePath:e,cliPath:n,configDir:r,rootDir:m,stdoutPath:a,stderrPath:l,environmentPath:o}),{encoding:"utf8",mode:384}),{definitionPath:u}},async start({serviceID:t,definitionPath:e,runCommand:n=f,uid:r=process.getuid?.()??0}){const i=v(r);await V(e),await X({serviceID:t,domain:i,definitionPath:e,runCommand:n})},async stop({serviceID:t,runCommand:e=f,uid:n=process.getuid?.()??0}){const r=v(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=v(r),a={serviceID:t,domain:i,definitionPath:e,runCommand:n};await V(e),await Q(a),await X(a)},async uninstall({serviceID:t,definitionPath:e,runCommand:n=f,uid:r=process.getuid?.()??0}){const i=v(r);await n("launchctl",["bootout",`${i}/${t}`],{allowFailure:!0}),await y(e,{force:!0})},async discoverServices({homeDir:t=d.homedir()}={}){const e=c.join(t,"Library","LaunchAgents"),n=await F(e).catch(()=>[]),r=D("darwin"),i=[];for(const a of n){if(!a.startsWith(r)||!a.endsWith(".plist"))continue;const l=a.slice(0,-6),o=c.join(e,a);let s=null;try{const u=await S(o,"utf8");s=Z(u)}catch{}i.push({serviceID:l,definitionPath:o,configDir:s})}return i},async isServiceLoaded({serviceID:t,runCommand:e=f,uid:n=process.getuid?.()??0}){const r=v(n),i=await e("launchctl",["print",`${r}/${t}`],{allowFailure:!0});return Number(i?.exitCode??1)===0}}}function ht(){return{platform:"win32",kind:"task-scheduler",async install({serviceID:t,nodePath:e,cliPath:n,configDir:r,rootDir:i,stdoutPath:a,stderrPath:l,runCommand:o=f,homeDir:s=d.homedir()}){const u=P(t,s),m=W(i,a,l,s);await k(c.dirname(u),{recursive:!0}),await $(u,mt({nodePath:e,cliPath:n,configDir:r,rootDir:m}),"utf8");let p=!1;K()&&(p=await lt({serviceID:t,wrapperPath:u,runCommand:o}));const w=j(t,s);return p?await y(w,{force:!0}).catch(()=>{}):await ot({wrapperPath:u,shortcutPath:w,runCommand:o}),{definitionPath:p?`task:${t}`:`startup:${t}`}},async start({serviceID:t,definitionPath:e,runCommand:n=f,homeDir:r=d.homedir()}){const i=P(t,r);if(e.startsWith("task:"))try{await n("schtasks",["/Run","/TN",t]);return}catch{}_("wscript.exe",["//B","//NoLogo",i])},async stop({serviceID:t,runCommand:e=f,homeDir:n=d.homedir()}){await e("schtasks",["/End","/TN",t],{allowFailure:!0});const r=`${t}-wrapper.vbs`;await U(r,{platform:"win32"})},async restart({serviceID:t,definitionPath:e,runCommand:n=f,homeDir:r=d.homedir()}){await n("schtasks",["/End","/TN",t],{allowFailure:!0});const i=`${t}-wrapper.vbs`;await U(i,{platform:"win32"});const a=P(t,r);if(e?.startsWith("task:"))try{await n("schtasks",["/Run","/TN",t]);return}catch{}_("wscript.exe",["//B","//NoLogo",a])},async uninstall({serviceID:t,runCommand:e=f,homeDir:n=d.homedir()}){await e("schtasks",["/Delete","/TN",t,"/F"],{allowFailure:!0});const r=P(t,n);await y(r,{force:!0});const i=j(t,n);await y(i,{force:!0})},async discoverServices({homeDir:t=d.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 o=String(r.stdout??"").split(/\r?\n/);for(const s of o){const u=s.match(/^"([^"]+)"/);if(!u)continue;const m=u[1];if(!m.startsWith(n))continue;let p=null;try{const w=P(m,t),b=(await S(w,"utf8")).match(/--config-dir\s+""([^""]+)""/);b&&(p=b[1])}catch{}i.push({serviceID:m,definitionPath:`task:${m}`,configDir:p})}}const a=c.join(t,"AppData","Roaming","Microsoft","Windows","Start Menu","Programs","Startup"),l=await F(a).catch(()=>[]);for(const o of l){if(!o.startsWith(n)||!o.endsWith(".lnk"))continue;const s=o.slice(0,-4);if(i.some(m=>m.serviceID===s))continue;let u=null;try{const m=P(s,t),w=(await S(m,"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=d.homedir()}){const r=await e("schtasks",["/Query","/TN",t,"/NH"],{allowFailure:!0});return Number(r?.exitCode??1)===0?!0:T(j(t,n))}}}function pt(){return{platform:"linux",kind:"systemd-user",async install({serviceID:t,nodePath:e,cliPath:n,configDir:r,rootDir:i,stdoutPath:a,stderrPath:l,homeDir:o=d.homedir(),runCommand:s=f}){const u=tt(t,o),m=W(i,a,l,o);return await k(c.dirname(u),{recursive:!0}),await $(u,it({serviceID:t,nodePath:e,cliPath:n,configDir:r,rootDir:m,stdoutPath:a,stderrPath:l}),{encoding:"utf8",mode:384}),await s("systemctl",["--user","daemon-reload"]),await s("systemctl",["--user","enable",`${t}.service`]),{definitionPath:u}},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=d.homedir()}={}){const e=c.join(t,".config","systemd","user"),n=await F(e).catch(()=>[]),r=D("linux"),i=[];for(const a of n){if(!a.startsWith(r)||!a.endsWith(".service"))continue;const l=a.slice(0,-8),o=c.join(e,a);let s=null;try{const u=await S(o,"utf8");s=Y(u)}catch{}i.push({serviceID:l,definitionPath:o,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=M(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 gt(t){return c.join(t,"daemon.lock.json")}function O(t){const e=gt(t);try{const n=M(e,"utf8"),r=JSON.parse(n);if(typeof r?.pid=="number")return r.pid}catch{}return 0}function z(t){return c.join(t,".profile")}function wt(t){return t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function yt(t){const e=[A(t.nodePath),A(t.cliPath)];t.configDir&&e.push("--config-dir",A(t.configDir));const n=`env ${C}=${A(t.rootDir)} ${e.join(" ")}`;return`${n} status >/dev/null 2>&1 || (nohup ${n} start >/dev/null 2>&1 &)`}function $t(t,e){return`# >>> grix-connector autostart [${t}] >>>
98
+ `}async function mt(t){const{serviceID:e,wrapperPath:n,runCommand:r}=t,i=await ut(r)??d.userInfo().username,a=c.join(c.dirname(n),`${e}-task.xml`);try{const o=dt({userId:i,wrapperPath:n});return await $(a,"\uFEFF"+o,"utf16le"),await r("schtasks",["/Create","/TN",e,"/XML",a,"/F"]),!0}catch{return!1}finally{await y(a,{force:!0}).catch(()=>{})}}const ft=3,pt="grix-wrapper-template:";function ht(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 = ${N(e)}`,"$exitCode = 1","try {"," [IO.Directory]::CreateDirectory([IO.Path]::GetDirectoryName($recordPath)) | Out-Null",` $p = Start-Process -FilePath ${N(t.nodePath)} -ArgumentList @(${n.map(N).join(", ")}) -WindowStyle Hidden -PassThru`,` $record = @{ pid = $p.Id; launched_at = [DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds(); boot_time = [DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds() - [Environment]::TickCount64; cli_path = ${N(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(`
99
+ `),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}`,o=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"),m=c.join(t.rootDir,"data","upgrade-pending.json"),h=[`"${p(t.nodePath)}"`,`"${p(o)}"`,"activate","--root",`"${p(t.rootDir)}"`].join(" "),w=`${h} --wrapper-handoff`,L=[`"${p(t.nodePath)}"`,`"${p(s)}"`,"activate","--root",`"${p(t.rootDir)}"`].join(" "),T=`${L} --wrapper-handoff`;return[`' ${pt} ${ft}`,"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(L)}"`,`handoffGuardianFallbackCommand = "${p(T)}"`,`guardianPath = "${p(o)}"`,`guardianFallbackPath = "${p(s)}"`,`maintenanceFile = "${p(u)}"`,`pendingFile = "${p(m)}"`,'Set shell = CreateObject("WScript.Shell")','Set processEnv = shell.Environment("Process")','Set fso = CreateObject("Scripting.FileSystemObject")',`processEnv("${C}") = "${p(t.rootDir)}"`,'processEnv("GRIX_WRAPPER_SUPERVISED") = "1"',...j().map(([K,Z])=>`processEnv("${K}") = "${Z}"`),"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
100
+ `)}function v(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 W(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 X(t){try{await Y(t)}catch{throw new Error(`launchd plist missing: ${t}`)}}async function Q(t){const{serviceID:e,domain:n,runCommand:r}=t,i=`${n}/${e}`,a=await r("launchctl",["bootout",i],{allowFailure:!0});if(Number(a?.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 o=await r("launchctl",["print",i],{allowFailure:!0});if(Number(o?.exitCode??1)===0)throw new Error(`launchctl bootout ${i}: ${B(a)}`)}return}for(let l=0;l<20;l+=1){const o=await r("launchctl",["print",i],{allowFailure:!0});if(Number(o?.exitCode??1)!==0)return;await new Promise(s=>setTimeout(s,250))}throw new Error(`launchctl bootout ${i}: service remains loaded`)}async function z(t){const{serviceID:e,domain:n,definitionPath:r,runCommand:i}=t,a=`${n}/${e}`,l=await i("launchctl",["bootstrap",n,r],{allowFailure:!0});if(Number(l?.exitCode??-1)===0)return;const o=await i("launchctl",["print",a],{allowFailure:!0});Number(o?.exitCode??1)===0&&await Q(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 gt(){return{platform:"darwin",kind:"launchd",async install({serviceID:t,nodePath:e,cliPath:n,configDir:r,rootDir:i,stdoutPath:a,stderrPath:l,environmentPath:o="",homeDir:s=d.homedir()}){const u=it(t,s),m=W(i,a,l,s);return await k(c.dirname(u),{recursive:!0}),await $(u,ot({serviceID:t,nodePath:e,cliPath:n,configDir:r,rootDir:m,stdoutPath:a,stderrPath:l,environmentPath:o}),{encoding:"utf8",mode:384}),{definitionPath:u}},async start({serviceID:t,definitionPath:e,runCommand:n=f,uid:r=process.getuid?.()??0}){const i=v(r);await X(e),await z({serviceID:t,domain:i,definitionPath:e,runCommand:n})},async stop({serviceID:t,runCommand:e=f,uid:n=process.getuid?.()??0}){const r=v(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=v(r),a={serviceID:t,domain:i,definitionPath:e,runCommand:n};await X(e),await Q(a),await z(a)},async uninstall({serviceID:t,definitionPath:e,runCommand:n=f,uid:r=process.getuid?.()??0}){const i=v(r);await n("launchctl",["bootout",`${i}/${t}`],{allowFailure:!0}),await y(e,{force:!0})},async discoverServices({homeDir:t=d.homedir()}={}){const e=c.join(t,"Library","LaunchAgents"),n=await F(e).catch(()=>[]),r=D("darwin"),i=[];for(const a of n){if(!a.startsWith(r)||!a.endsWith(".plist"))continue;const l=a.slice(0,-6),o=c.join(e,a);let s=null;try{const u=await S(o,"utf8");s=et(u)}catch{}i.push({serviceID:l,definitionPath:o,configDir:s})}return i},async isServiceLoaded({serviceID:t,runCommand:e=f,uid:n=process.getuid?.()??0}){const r=v(n),i=await e("launchctl",["print",`${r}/${t}`],{allowFailure:!0});return Number(i?.exitCode??1)===0}}}function wt(){return{platform:"win32",kind:"task-scheduler",async install({serviceID:t,nodePath:e,cliPath:n,configDir:r,rootDir:i,stdoutPath:a,stderrPath:l,runCommand:o=f,homeDir:s=d.homedir()}){const u=P(t,s),m=W(i,a,l,s);await k(c.dirname(u),{recursive:!0}),await $(u,ht({nodePath:e,cliPath:n,configDir:r,rootDir:m}),"utf8");let h=!1;tt()&&(h=await mt({serviceID:t,wrapperPath:u,runCommand:o}));const w=M(t,s);return h?await y(w,{force:!0}).catch(()=>{}):await lt({wrapperPath:u,shortcutPath:w,runCommand:o}),{definitionPath:h?`task:${t}`:`startup:${t}`}},async start({serviceID:t,definitionPath:e,runCommand:n=f,homeDir:r=d.homedir()}){const i=P(t,r);if(e.startsWith("task:"))try{await n("schtasks",["/Run","/TN",t]);return}catch{}_("wscript.exe",["//B","//NoLogo",i])},async stop({serviceID:t,runCommand:e=f,homeDir:n=d.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=d.homedir()}){await n("schtasks",["/End","/TN",t],{allowFailure:!0});const i=`${t}-wrapper.vbs`;await G(i,{platform:"win32"});const a=P(t,r);if(e?.startsWith("task:"))try{await n("schtasks",["/Run","/TN",t]);return}catch{}_("wscript.exe",["//B","//NoLogo",a])},async uninstall({serviceID:t,runCommand:e=f,homeDir:n=d.homedir()}){await e("schtasks",["/Delete","/TN",t,"/F"],{allowFailure:!0});const r=P(t,n);await y(r,{force:!0});const i=M(t,n);await y(i,{force:!0})},async discoverServices({homeDir:t=d.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 o=String(r.stdout??"").split(/\r?\n/);for(const s of o){const u=s.match(/^"([^"]+)"/);if(!u)continue;const m=u[1];if(!m.startsWith(n))continue;let h=null;try{const w=P(m,t),T=(await S(w,"utf8")).match(/--config-dir\s+""([^""]+)""/);T&&(h=T[1])}catch{}i.push({serviceID:m,definitionPath:`task:${m}`,configDir:h})}}const a=c.join(t,"AppData","Roaming","Microsoft","Windows","Start Menu","Programs","Startup"),l=await F(a).catch(()=>[]);for(const o of l){if(!o.startsWith(n)||!o.endsWith(".lnk"))continue;const s=o.slice(0,-4);if(i.some(m=>m.serviceID===s))continue;let u=null;try{const m=P(s,t),w=(await S(m,"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=d.homedir()}){const r=await e("schtasks",["/Query","/TN",t,"/NH"],{allowFailure:!0});return Number(r?.exitCode??1)===0?!0:b(M(t,n))}}}function yt(){return{platform:"linux",kind:"systemd-user",async install({serviceID:t,nodePath:e,cliPath:n,configDir:r,rootDir:i,stdoutPath:a,stderrPath:l,homeDir:o=d.homedir(),runCommand:s=f}){const u=rt(t,o),m=W(i,a,l,o);return await k(c.dirname(u),{recursive:!0}),await $(u,st({serviceID:t,nodePath:e,cliPath:n,configDir:r,rootDir:m,stdoutPath:a,stderrPath:l}),{encoding:"utf8",mode:384}),await s("systemctl",["--user","daemon-reload"]),await s("systemctl",["--user","enable",`${t}.service`]),{definitionPath:u}},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=d.homedir()}={}){const e=c.join(t,".config","systemd","user"),n=await F(e).catch(()=>[]),r=D("linux"),i=[];for(const a of n){if(!a.startsWith(r)||!a.endsWith(".service"))continue;const l=a.slice(0,-8),o=c.join(e,a);let s=null;try{const u=await S(o,"utf8");s=nt(u)}catch{}i.push({serviceID:l,definitionPath:o,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 $t(t){return c.join(t,"daemon.lock.json")}function O(t){const e=$t(t);try{const n=U(e,"utf8"),r=JSON.parse(n);if(typeof r?.pid=="number")return r.pid}catch{}return 0}function J(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 ${C}=${R(t.rootDir)} ${e.join(" ")}`;return`${n} status >/dev/null 2>&1 || (nohup ${n} start >/dev/null 2>&1 &)`}function Pt(t,e){return`# >>> grix-connector autostart [${t}] >>>
97
101
  ${e}
98
102
  # <<< grix-connector autostart [${t}] <<<
99
- `}function J(t){const e=wt(t);return new RegExp(`(?:^|\\n)# >>> grix-connector autostart \\[${e}\\] >>>\\n[\\s\\S]*?\\n# <<< grix-connector autostart \\[${e}\\] <<<\\n?`,"m")}async function St(t,e,n){let r="";try{r=await S(t,"utf8")}catch{}const i=$t(e,n),a=J(e);let l;if(a.test(r))l=r.replace(a,`
103
+ `}function q(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 Et(t,e,n){let r="";try{r=await S(t,"utf8")}catch{}const i=Pt(e,n),a=q(e);let l;if(a.test(r))l=r.replace(a,`
100
104
  ${i}`).replace(/^\n+/,"");else{const o=r.length===0||r.endsWith(`
101
105
  `)?"":`
102
- `;l=`${r}${o}${i}`}l!==r&&(await k(c.dirname(t),{recursive:!0}),await $(t,l,{encoding:"utf8"}))}async function xt(t,e){let n="";try{n=await S(t,"utf8")}catch{return}const r=J(e);if(!r.test(n))return;const i=n.replace(r,"").replace(/^\n+/,"");await $(t,i,{encoding:"utf8"})}function Pt(){return{platform:"linux",kind:"bare-daemon",async install({serviceID:t,nodePath:e,cliPath:n,configDir:r,rootDir:i,stdoutPath:a,stderrPath:l,homeDir:o=d.homedir()}){const s=x(t,o);await k(c.dirname(s),{recursive:!0});const u=W(i,a,l,o),m={schema_version:1,service_id:t,node_path:e,cli_path:n,config_dir:r??"",root_dir:u,stdout_path:a,stderr_path:l,installed_at:Date.now()};await $(s,`${JSON.stringify(m,null,2)}
103
- `,{encoding:"utf8",mode:384});const p=yt({nodePath:e,cliPath:n,configDir:r??"",rootDir:u});return await St(z(o),t,p),{definitionPath:s}},async start({serviceID:t,homeDir:e=d.homedir()}){const n=x(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),_(r.node_path,i,{env:{...process.env,[C]:r.root_dir}})},async stop({serviceID:t,homeDir:e=d.homedir()}){const n=x(t,e),r=E(n);if(!r)return;const i=O(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=x(t.serviceID,t.homeDir??d.homedir()),r=E(n);for(;r&&Date.now()<e;){const i=O(r.root_dir);if(i<=0||!I(i))break;await new Promise(a=>setTimeout(a,100))}await this.start(t)},async uninstall({serviceID:t,homeDir:e=d.homedir()}){const n=x(t,e),r=E(n);if(r){const i=O(r.root_dir);if(i>0&&I(i))try{process.kill(i,"SIGTERM")}catch{}}await y(n,{force:!0}),await xt(z(e),t)},async discoverServices({homeDir:t=d.homedir()}={}){const e=c.join(t,".grix","service"),n=await F(e).catch(()=>[]),r=D("linux"),i=[];for(const a of n){if(!a.startsWith(r)||!a.endsWith(".bare.json"))continue;const l=a.slice(0,-10),o=c.join(e,a),s=E(o);i.push({serviceID:l,definitionPath:o,configDir:s?.config_dir||null})}return i},async isServiceLoaded({serviceID:t,homeDir:e=d.homedir()}){const n=x(t,e),r=E(n);if(!r)return!1;const i=O(r.root_dir);return i>0&&I(i)}}}function Et(t=process.getuid?.()??0){if(process.env.GRIX_FORCE_BARE_DAEMON==="1")return!1;const e=process.env.XDG_RUNTIME_DIR;return!!(e&&T(c.join(e,"bus"))||T(`/run/user/${t}/bus`))}function Dt(t=process.platform,e={}){return t==="darwin"?ft():t==="win32"?ht():e.systemdUserAvailable??Et()?pt():Pt()}export{dt as WINDOWS_WRAPPER_TEMPLATE_MARKER,ut as WINDOWS_WRAPPER_TEMPLATE_VERSION,mt as buildWindowsWrapperScript,Dt as getPlatformServiceAdapter,Et as isSystemdUserBusAvailable,xt as removeProfileAutostart,P as resolveWindowsWrapperPath,St as upsertProfileAutostart};
106
+ `;l=`${r}${o}${i}`}l!==r&&(await k(c.dirname(t),{recursive:!0}),await $(t,l,{encoding:"utf8"}))}async function kt(t,e){let n="";try{n=await S(t,"utf8")}catch{return}const r=q(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:a,stderrPath:l,homeDir:o=d.homedir()}){const s=x(t,o);await k(c.dirname(s),{recursive:!0});const u=W(i,a,l,o),m={schema_version:1,service_id:t,node_path:e,cli_path:n,config_dir:r??"",root_dir:u,stdout_path:a,stderr_path:l,installed_at:Date.now()};await $(s,`${JSON.stringify(m,null,2)}
107
+ `,{encoding:"utf8",mode:384});const h=xt({nodePath:e,cliPath:n,configDir:r??"",rootDir:u});return await Et(J(o),t,h),{definitionPath:s}},async start({serviceID:t,homeDir:e=d.homedir()}){const n=x(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),_(r.node_path,i,{env:{...process.env,[C]:r.root_dir}})},async stop({serviceID:t,homeDir:e=d.homedir()}){const n=x(t,e),r=E(n);if(!r)return;const i=O(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=x(t.serviceID,t.homeDir??d.homedir()),r=E(n);for(;r&&Date.now()<e;){const i=O(r.root_dir);if(i<=0||!I(i))break;await new Promise(a=>setTimeout(a,100))}await this.start(t)},async uninstall({serviceID:t,homeDir:e=d.homedir()}){const n=x(t,e),r=E(n);if(r){const i=O(r.root_dir);if(i>0&&I(i))try{process.kill(i,"SIGTERM")}catch{}}await y(n,{force:!0}),await kt(J(e),t)},async discoverServices({homeDir:t=d.homedir()}={}){const e=c.join(t,".grix","service"),n=await F(e).catch(()=>[]),r=D("linux"),i=[];for(const a of n){if(!a.startsWith(r)||!a.endsWith(".bare.json"))continue;const l=a.slice(0,-10),o=c.join(e,a),s=E(o);i.push({serviceID:l,definitionPath:o,configDir:s?.config_dir||null})}return i},async isServiceLoaded({serviceID:t,homeDir:e=d.homedir()}){const n=x(t,e),r=E(n);if(!r)return!1;const i=O(r.root_dir);return i>0&&I(i)}}}function vt(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 Nt(t=process.platform,e={}){return t==="darwin"?gt():t==="win32"?wt():e.systemdUserAvailable??vt()?yt():Ct()}export{pt as WINDOWS_WRAPPER_TEMPLATE_MARKER,ft as WINDOWS_WRAPPER_TEMPLATE_VERSION,ht as buildWindowsWrapperScript,j as collectServicePortEnv,Nt as getPlatformServiceAdapter,vt as isSystemdUserBusAvailable,kt as removeProfileAutostart,P as resolveWindowsWrapperPath,Et as upsertProfileAutostart};
@@ -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.1",
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",