grix-connector 4.9.1 → 4.9.2-beta.2

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.
Files changed (48) hide show
  1. package/dist/adapter/claude/claude-adapter.js +13 -13
  2. package/dist/adapter/claude/usage-parser.js +8 -7
  3. package/dist/assets/dsh-bridge/grix-dsh-bridge-4.9.2-beta.2.tgz +0 -0
  4. package/dist/assets/dsh-bridge/manifest.json +6 -6
  5. package/dist/grix.js +8 -8
  6. package/dist/launcher.mjs +103 -1
  7. package/dist/runtime/daemon-lock.js +2 -2
  8. package/dist/service/platform-adapter.js +30 -30
  9. package/dist/service/process-control.js +1 -1
  10. package/dist/service/service-manager.js +3 -3
  11. package/openclaw-plugin/index.js +1 -1
  12. package/package.json +1 -1
  13. package/scripts/launcher.mjs +103 -1
  14. package/dist/adapter/claude/claude-bridge-server.js +0 -1
  15. package/dist/adapter/claude/claude-tools.js +0 -1
  16. package/dist/adapter/claude/claude-worker-client.js +0 -1
  17. package/dist/adapter/claude/mcp-http-launcher.js +0 -2
  18. package/dist/adapter/claude/result-timeout.js +0 -1
  19. package/dist/adapter/deepseek/deepseek-adapter.js +0 -6
  20. package/dist/adapter/deepseek/index.js +0 -1
  21. package/dist/adapter/qwen/index.js +0 -1
  22. package/dist/adapter/qwen/qwen-adapter.js +0 -4
  23. package/dist/aibot/client.js +0 -1
  24. package/dist/aibot/index.js +0 -1
  25. package/dist/aibot/types.js +0 -0
  26. package/dist/assets/dsh-bridge/grix-dsh-bridge-4.9.1.tgz +0 -0
  27. package/dist/core/access/allowlist-gate.js +0 -1
  28. package/dist/core/access/allowlist-store.js +0 -1
  29. package/dist/core/access/index.js +0 -1
  30. package/dist/core/file-ops/handler.js +0 -1
  31. package/dist/core/file-ops/list-files.js +0 -1
  32. package/dist/core/file-ops/types.js +0 -0
  33. package/dist/log.js +0 -3
  34. package/dist/main.js +0 -31
  35. package/dist/mcp/stream-http/config.js +0 -1
  36. package/dist/mcp/stream-http/connection-binding.js +0 -1
  37. package/dist/mcp/stream-http/event-tool-executor.js +0 -1
  38. package/dist/mcp/stream-http/gateway.js +0 -1
  39. package/dist/mcp/stream-http/index.js +0 -1
  40. package/dist/mcp/stream-http/security.js +0 -1
  41. package/dist/mcp/stream-http/session-manager.js +0 -1
  42. package/dist/mcp/stream-http/tool-executor.js +0 -1
  43. package/dist/mcp/stream-http/tool-registry.js +0 -1
  44. package/dist/mcp/stream-http/tool-schemas.js +0 -1
  45. package/dist/session/index.js +0 -1
  46. package/dist/session/manager.js +0 -1
  47. package/dist/transport/index.js +0 -1
  48. package/dist/transport/json-rpc.js +0 -3
@@ -1,12 +1,12 @@
1
- import{existsSync as b,readFileSync as U}from"node:fs";import{mkdir as k,readdir as F,readFile as P,rm as y,stat as tt,writeFile as $}from"node:fs/promises";import m from"node:os";import c from"node:path";import{GRIX_HOME_ENV as v}from"../core/config/paths.js";import{isProcessRunning as I,runCommand as f,spawnDetached as L,isWindowsElevated as et,killProcessesByCommandLine as G}from"./process-control.js";import{getServicePrefix as D,parseConfigDirFromPlistXML as nt,parseConfigDirFromSystemdUnit as rt,resolveBareDaemonMarkerPath as S,resolveLinuxUserUnitPath as it,resolveMacOSLaunchAgentPath as ot}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 V(t){return`"${H(t).replace(/%/g,"%%")}"`}function _(t){return String(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 X(t){return[t.nodePath,t.cliPath,...t.configDir?["--config-dir",t.configDir]:[]]}function st(t){const e=X(t);return`<?xml version="1.0" encoding="UTF-8"?>
1
+ import{existsSync as C,readFileSync as V}from"node:fs";import{mkdir as v,readdir as I,readFile as P,rm as y,stat as it,writeFile as $}from"node:fs/promises";import p from"node:os";import c from"node:path";import{fileURLToPath as ot,pathToFileURL as at}from"node:url";import{GRIX_HOME_ENV as T}from"../core/config/paths.js";import{isProcessRunning as R,runCommand as f,spawnDetached as _,isWindowsElevated as st,killProcessesByCommandLine as X,findProcessesByCommandLine as ct}from"./process-control.js";import{getServicePrefix as L,parseConfigDirFromPlistXML as lt,parseConfigDirFromSystemdUnit as ut,resolveBareDaemonMarkerPath as S,resolveLinuxUserUnitPath as dt,resolveMacOSLaunchAgentPath as mt}from"./service-paths.js";import{LAUNCHER_FILE as pt,readPointer as ft,versionDir as ht}from"../core/upgrade/version-store.js";function w(t){return String(t??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function D(t){return`'${String(t??"").replace(/'/g,"'\\''")}'`}function Q(t){return String(t??"").replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\n/g,"\\n").replace(/\r/g,"\\r")}function z(t){return`"${Q(t).replace(/%/g,"%%")}"`}function O(t){return String(t??"").replace(/%/g,"%%")}function gt(t){return`"${Q(t).replace(/\$/g,()=>"$$").replace(/%/g,"%%")}"`}function j(t=process.env){const n=[];for(const e of["GRIX_HEALTH_PORT","GRIX_ADMIN_PORT"]){const r=String(t[e]??"").trim();/^\d{2,5}$/.test(r)&&n.push([e,r])}return n}function J(t){return[t.nodePath,t.cliPath,...t.configDir?["--config-dir",t.configDir]:[]]}function wt(t){const n=J(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>
5
5
  <key>Label</key>
6
- <string>${g(t.serviceID)}</string>
6
+ <string>${w(t.serviceID)}</string>
7
7
  <key>ProgramArguments</key>
8
8
  <array>
9
- ${e.map(n=>` <string>${g(n)}</string>`).join(`
9
+ ${n.map(e=>` <string>${w(e)}</string>`).join(`
10
10
  `)}
11
11
  </array>
12
12
  <key>RunAtLoad</key>
@@ -17,43 +17,43 @@ ${e.map(n=>` <string>${g(n)}</string>`).join(`
17
17
  <key>ThrottleInterval</key>
18
18
  <integer>30</integer>
19
19
  <key>WorkingDirectory</key>
20
- <string>${g(c.dirname(t.cliPath))}</string>
20
+ <string>${w(c.dirname(t.cliPath))}</string>
21
21
  <key>EnvironmentVariables</key>
22
22
  <dict>
23
- <key>${v}</key>
24
- <string>${g(t.rootDir)}</string>
25
- ${j().map(([n,r])=>` <key>${n}</key>
23
+ <key>${T}</key>
24
+ <string>${w(t.rootDir)}</string>
25
+ ${j().map(([e,r])=>` <key>${e}</key>
26
26
  <string>${r}</string>
27
27
  `).join("")}${t.environmentPath?` <key>PATH</key>
28
- <string>${g(t.environmentPath)}</string>
28
+ <string>${w(t.environmentPath)}</string>
29
29
  `:""} </dict>
30
30
  <key>StandardOutPath</key>
31
- <string>${g(t.stdoutPath)}</string>
31
+ <string>${w(t.stdoutPath)}</string>
32
32
  <key>StandardErrorPath</key>
33
- <string>${g(t.stderrPath)}</string>
33
+ <string>${w(t.stderrPath)}</string>
34
34
  </dict>
35
35
  </plist>
36
- `}function ct(t){const e=X(t).map(n=>at(n)).join(" ");return`[Unit]
36
+ `}function yt(t){const n=J(t).map(e=>gt(e)).join(" ");return`[Unit]
37
37
  Description=grix-connector daemon (${t.serviceID})
38
38
  After=network.target
39
39
 
40
40
  [Service]
41
41
  Type=simple
42
- ExecStart=${e}
43
- WorkingDirectory=${_(c.dirname(t.cliPath))}
44
- Environment=${V(`${v}=${t.rootDir}`)}
45
- ${j().map(([n,r])=>`Environment=${n}=${r}
46
- `).join("")}${t.environmentPath?`Environment=${V(`PATH=${t.environmentPath}`)}
42
+ ExecStart=${n}
43
+ WorkingDirectory=${O(c.dirname(t.cliPath))}
44
+ Environment=${z(`${T}=${t.rootDir}`)}
45
+ ${j().map(([e,r])=>`Environment=${e}=${r}
46
+ `).join("")}${t.environmentPath?`Environment=${z(`PATH=${t.environmentPath}`)}
47
47
  `:""}
48
48
  Restart=always
49
49
  RestartSec=30
50
- StandardOutput=append:${_(t.stdoutPath)}
51
- StandardError=append:${_(t.stderrPath)}
50
+ StandardOutput=append:${O(t.stdoutPath)}
51
+ StandardError=append:${O(t.stderrPath)}
52
52
 
53
53
  [Install]
54
54
  WantedBy=default.target
55
- `}function x(t,e){const n=t.replace(/[^a-zA-Z0-9._-]/g,"_");return c.join(e,".grix",`${n}-wrapper.vbs`)}function M(t,e){const n=t.replace(/[^a-zA-Z0-9._-]/g,"_"),r=c.join(e,"AppData","Roaming","Microsoft","Windows","Start Menu","Programs","Startup");return c.join(r,`${n}.lnk`)}function p(t){return String(t??"").replace(/"/g,'""')}function A(t){return`'${String(t??"").replace(/'/g,"''")}'`}function lt(t,e){const n=c.join(c.dirname(t),c.basename(e).replace(/\.lnk$/,"-create.vbs")),r=["Option Explicit","Dim shell, shortcut",'Set shell = CreateObject("WScript.Shell")',`Set shortcut = shell.CreateShortcut("${p(e)}")`,'shortcut.TargetPath = "wscript.exe"',`shortcut.Arguments = "//B //NoLogo \\"${p(t)}\\""`,"shortcut.WindowStyle = 7","shortcut.Save",""].join(`\r
56
- `);return{path:n,content:r}}async function ut(t){const{wrapperPath:e,shortcutPath:n,runCommand:r}=t,i=lt(e,n);for(let o=1;o<=3;o+=1){try{await $(i.path,i.content,"utf8"),await r("wscript.exe",[i.path,"//B","//NoLogo"])}catch{}finally{await y(i.path,{force:!0}).catch(()=>{})}if(b(n))return!0}return console.warn(`Warning: failed to create Windows logon-autostart shortcut at ${n}. The daemon will NOT start automatically after the next reboot/logon. Check Controlled Folder Access / antivirus blocking writes to the Startup folder, or create the shortcut manually.`),!1}async function dt(t){try{const e=await t("whoami",["/user","/fo","csv","/nh"],{allowFailure:!0}),n=String(e?.stdout??"").match(/"(S-1-[0-9-]+)"/);if(n)return n[1]}catch{}return null}function mt(t){const e=`//B //NoLogo "${t.wrapperPath}"`;return`<?xml version="1.0" encoding="UTF-16"?>
55
+ `}function x(t,n){const e=t.replace(/[^a-zA-Z0-9._-]/g,"_");return c.join(n,".grix",`${e}-wrapper.vbs`)}function M(t,n){const e=t.replace(/[^a-zA-Z0-9._-]/g,"_"),r=c.join(n,"AppData","Roaming","Microsoft","Windows","Start Menu","Programs","Startup");return c.join(r,`${e}.lnk`)}function m(t){return String(t??"").replace(/"/g,'""')}function A(t){return`'${String(t??"").replace(/'/g,"''")}'`}function $t(t,n){const e=c.join(c.dirname(t),c.basename(n).replace(/\.lnk$/,"-create.vbs")),r=["Option Explicit","Dim shell, shortcut",'Set shell = CreateObject("WScript.Shell")',`Set shortcut = shell.CreateShortcut("${m(n)}")`,'shortcut.TargetPath = "wscript.exe"',`shortcut.Arguments = "//B //NoLogo \\"${m(t)}\\""`,"shortcut.WindowStyle = 7","shortcut.Save",""].join(`\r
56
+ `);return{path:e,content:r}}async function Et(t){const{wrapperPath:n,shortcutPath:e,runCommand:r}=t,i=$t(n,e);for(let o=1;o<=3;o+=1){try{await $(i.path,i.content,"utf8"),await r("wscript.exe",[i.path,"//B","//NoLogo"])}catch{}finally{await y(i.path,{force:!0}).catch(()=>{})}if(C(e))return!0}return console.warn(`Warning: failed to create Windows logon-autostart shortcut at ${e}. 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 Pt(t){try{const n=await t("whoami",["/user","/fo","csv","/nh"],{allowFailure:!0}),e=String(n?.stdout??"").match(/"(S-1-[0-9-]+)"/);if(e)return e[1]}catch{}return null}function St(t){const n=`//B //NoLogo "${t.wrapperPath}"`;return`<?xml version="1.0" encoding="UTF-16"?>
57
57
  <Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
58
58
  <RegistrationInfo>
59
59
  <Description>grix-connector daemon autostart</Description>
@@ -61,12 +61,12 @@ WantedBy=default.target
61
61
  <Triggers>
62
62
  <LogonTrigger>
63
63
  <Enabled>true</Enabled>
64
- <UserId>${g(t.userId)}</UserId>
64
+ <UserId>${w(t.userId)}</UserId>
65
65
  </LogonTrigger>
66
66
  </Triggers>
67
67
  <Principals>
68
68
  <Principal id="Author">
69
- <UserId>${g(t.userId)}</UserId>
69
+ <UserId>${w(t.userId)}</UserId>
70
70
  <LogonType>InteractiveToken</LogonType>
71
71
  <RunLevel>LeastPrivilege</RunLevel>
72
72
  </Principal>
@@ -92,17 +92,17 @@ WantedBy=default.target
92
92
  <Actions Context="Author">
93
93
  <Exec>
94
94
  <Command>wscript.exe</Command>
95
- <Arguments>${g(e)}</Arguments>
95
+ <Arguments>${w(n)}</Arguments>
96
96
  </Exec>
97
97
  </Actions>
98
98
  </Task>
99
- `}async function ft(t){const{serviceID:e,wrapperPath:n,runCommand:r}=t,i=await dt(r)??m.userInfo().username,o=c.join(c.dirname(n),`${e}-task.xml`);try{const a=mt({userId:i,wrapperPath:n});return await $(o,"\uFEFF"+a,"utf16le"),await r("schtasks",["/Create","/TN",e,"/XML",o,"/F"]),!0}catch{return!1}finally{await y(o,{force:!0}).catch(()=>{})}}const pt=3,ht="grix-wrapper-template:";function gt(t){const e=c.join(t.rootDir,"data","upgrade-wrapper-child.json"),n=[`"${t.cliPath}"`,...t.configDir?["--config-dir",`"${t.configDir}"`]:[]],r=["$ErrorActionPreference = 'Stop'",`$recordPath = ${A(e)}`,"$exitCode = 1","try {"," [IO.Directory]::CreateDirectory([IO.Path]::GetDirectoryName($recordPath)) | Out-Null",` $p = Start-Process -FilePath ${A(t.nodePath)} -ArgumentList @(${n.map(A).join(", ")}) -WindowStyle Hidden -PassThru`,` $record = @{ pid = $p.Id; launched_at = [DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds(); boot_time = [DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds() - [Environment]::TickCount64; cli_path = ${A(t.cliPath)} }`," $utf8NoBom = New-Object System.Text.UTF8Encoding($false)"," try {"," [IO.File]::WriteAllText($recordPath, ($record | ConvertTo-Json -Compress), $utf8NoBom)"," } catch {",' & "$env:SystemRoot\\System32\\taskkill.exe" /PID $p.Id /T /F 2>$null | Out-Null'," $p.WaitForExit()"," throw"," }"," $p.WaitForExit()"," $exitCode = $p.ExitCode","} finally {"," Remove-Item -LiteralPath $recordPath -Force -ErrorAction SilentlyContinue","}","exit $exitCode"].join(`
100
- `),i=Buffer.from(r,"utf16le").toString("base64"),l=`"${c.join(process.env.SystemRoot||"C:\\Windows","System32","WindowsPowerShell","v1.0","powershell.exe")}" -NoProfile -NonInteractive -ExecutionPolicy Bypass -EncodedCommand ${i}`,a=c.join(t.rootDir,"bin","upgrade-guardian.mjs"),s=c.join(t.rootDir,"bin","upgrade-guardian.fallback.mjs"),u=c.join(t.rootDir,"data","upgrade-wrapper.pause.json"),d=c.join(t.rootDir,"data","upgrade-pending.json"),h=[`"${p(t.nodePath)}"`,`"${p(a)}"`,"activate","--root",`"${p(t.rootDir)}"`].join(" "),w=`${h} --wrapper-handoff`,O=[`"${p(t.nodePath)}"`,`"${p(s)}"`,"activate","--root",`"${p(t.rootDir)}"`].join(" "),T=`${O} --wrapper-handoff`;return[`' ${ht} ${pt}`,"Option Explicit","Dim shell, processEnv, fso, command, guardianCommand, handoffGuardianCommand, guardianFallbackCommand, handoffGuardianFallbackCommand, guardianPath, guardianFallbackPath, maintenanceFile, pendingFile, delayMs, rapidCount, guardianExit",`command = "${p(l)}"`,`guardianCommand = "${p(h)}"`,`handoffGuardianCommand = "${p(w)}"`,`guardianFallbackCommand = "${p(O)}"`,`handoffGuardianFallbackCommand = "${p(T)}"`,`guardianPath = "${p(a)}"`,`guardianFallbackPath = "${p(s)}"`,`maintenanceFile = "${p(u)}"`,`pendingFile = "${p(d)}"`,'Set shell = CreateObject("WScript.Shell")','Set processEnv = shell.Environment("Process")','Set fso = CreateObject("Scripting.FileSystemObject")',`processEnv("${v}") = "${p(t.rootDir)}"`,'processEnv("GRIX_WRAPPER_SUPERVISED") = "1"',...j().map(([Z,Y])=>`processEnv("${Z}") = "${Y}"`),"delayMs = 5000","rapidCount = 0","Do"," Do While fso.FileExists(maintenanceFile)"," If fso.FileExists(guardianPath) Then"," guardianExit = shell.Run(guardianCommand, 0, True)"," ElseIf fso.FileExists(guardianFallbackPath) Then"," guardianExit = shell.Run(guardianFallbackCommand, 0, True)"," Else"," On Error Resume Next"," fso.DeleteFile maintenanceFile, True"," On Error GoTo 0"," WScript.Sleep 1000"," End If"," If fso.FileExists(maintenanceFile) Then WScript.Sleep 3000"," Loop"," If fso.FileExists(pendingFile) Then"," If fso.FileExists(guardianPath) Then"," guardianExit = shell.Run(guardianCommand, 0, True)"," ElseIf fso.FileExists(guardianFallbackPath) Then"," guardianExit = shell.Run(guardianFallbackCommand, 0, True)"," End If"," End If"," shell.Run command, 0, True"," If fso.FileExists(pendingFile) Then"," If fso.FileExists(guardianPath) Then"," guardianExit = shell.Run(handoffGuardianCommand, 0, True)"," ElseIf fso.FileExists(guardianFallbackPath) Then"," guardianExit = shell.Run(handoffGuardianFallbackCommand, 0, True)"," End If"," End If"," If fso.FileExists(pendingFile) And Not PendingIsTerminal() Then"," rapidCount = 0"," WScript.Sleep delayMs"," ElseIf fso.FileExists(maintenanceFile) Then"," rapidCount = 0"," Else"," rapidCount = rapidCount + 1"," If rapidCount >= 10 Then"," WScript.Sleep 300000"," rapidCount = 0"," Else"," WScript.Sleep delayMs"," End If"," End If","Loop","","Function PendingIsTerminal()"," Dim stream, text"," PendingIsTerminal = False"," On Error Resume Next"," Set stream = fso.OpenTextFile(pendingFile, 1)"," If Err.Number <> 0 Then"," Err.Clear"," On Error GoTo 0"," Exit Function"," End If"," text = stream.ReadAll"," stream.Close"," If Err.Number <> 0 Then"," Err.Clear"," On Error GoTo 0"," Exit Function"," End If"," On Error GoTo 0",' text = Replace(Replace(Replace(text, " ", ""), vbCr, ""), vbLf, "")',' If InStr(text, """phase"":""failed""") > 0 Then PendingIsTerminal = True',' If InStr(text, """phase"":""finalizing""") > 0 Then PendingIsTerminal = True',"End Function",""].join(`\r
101
- `)}function C(t){return`gui/${t??0}`}function B(t){const e=String(t?.stdout??"").trim();return String(t?.stderr??"").trim()||e||`exit=${Number(t?.exitCode??-1)}`}function N(t,e,n,r){if(t?.trim())return c.resolve(t);const i=e||n;return i?c.resolve(c.dirname(c.dirname(i))):c.join(r,".grix")}async function Q(t){try{await tt(t)}catch{throw new Error(`launchd plist missing: ${t}`)}}async function z(t){const{serviceID:e,domain:n,runCommand:r}=t,i=`${n}/${e}`,o=await r("launchctl",["bootout",i],{allowFailure:!0});if(Number(o?.exitCode??-1)!==0){const l=await r("launchctl",["print",i],{allowFailure:!0});if(Number(l?.exitCode??1)===0){await new Promise(s=>setTimeout(s,250));const a=await r("launchctl",["print",i],{allowFailure:!0});if(Number(a?.exitCode??1)===0)throw new Error(`launchctl bootout ${i}: ${B(o)}`)}return}for(let l=0;l<20;l+=1){const a=await r("launchctl",["print",i],{allowFailure:!0});if(Number(a?.exitCode??1)!==0)return;await new Promise(s=>setTimeout(s,250))}throw new Error(`launchctl bootout ${i}: service remains loaded`)}async function J(t){const{serviceID:e,domain:n,definitionPath:r,runCommand:i}=t,o=`${n}/${e}`,l=await i("launchctl",["bootstrap",n,r],{allowFailure:!0});if(Number(l?.exitCode??-1)===0)return;const a=await i("launchctl",["print",o],{allowFailure:!0});Number(a?.exitCode??1)===0&&await z(t);const s=await i("launchctl",["bootstrap",n,r],{allowFailure:!0});if(Number(s?.exitCode??-1)!==0)throw new Error(`launchctl bootstrap ${n} failed: initial=${B(l)}; retry=${B(s)}`)}function wt(){return{platform:"darwin",kind:"launchd",async install({serviceID:t,nodePath:e,cliPath:n,configDir:r,rootDir:i,stdoutPath:o,stderrPath:l,environmentPath:a="",homeDir:s=m.homedir()}){const u=ot(t,s),d=N(i,o,l,s);return await k(c.dirname(u),{recursive:!0}),await $(u,st({serviceID:t,nodePath:e,cliPath:n,configDir:r,rootDir:d,stdoutPath:o,stderrPath:l,environmentPath:a}),{encoding:"utf8",mode:384}),{definitionPath:u}},async start({serviceID:t,definitionPath:e,runCommand:n=f,uid:r=process.getuid?.()??0}){const i=C(r);await Q(e),await J({serviceID:t,domain:i,definitionPath:e,runCommand:n})},async stop({serviceID:t,runCommand:e=f,uid:n=process.getuid?.()??0}){const r=C(n);await e("launchctl",["bootout",`${r}/${t}`],{allowFailure:!0})},async restart({serviceID:t,definitionPath:e,runCommand:n=f,uid:r=process.getuid?.()??0}){const i=C(r),o={serviceID:t,domain:i,definitionPath:e,runCommand:n};await Q(e),await z(o),await J(o)},async uninstall({serviceID:t,definitionPath:e,runCommand:n=f,uid:r=process.getuid?.()??0}){const i=C(r);await n("launchctl",["bootout",`${i}/${t}`],{allowFailure:!0}),await y(e,{force:!0})},async discoverServices({homeDir:t=m.homedir()}={}){const e=c.join(t,"Library","LaunchAgents"),n=await F(e).catch(()=>[]),r=D("darwin"),i=[];for(const o of n){if(!o.startsWith(r)||!o.endsWith(".plist"))continue;const l=o.slice(0,-6),a=c.join(e,o);let s=null;try{const u=await P(a,"utf8");s=nt(u)}catch{}i.push({serviceID:l,definitionPath:a,configDir:s})}return i},async isServiceLoaded({serviceID:t,runCommand:e=f,uid:n=process.getuid?.()??0}){const r=C(n),i=await e("launchctl",["print",`${r}/${t}`],{allowFailure:!0});return Number(i?.exitCode??1)===0}}}function yt(){return{platform:"win32",kind:"task-scheduler",async install({serviceID:t,nodePath:e,cliPath:n,configDir:r,rootDir:i,stdoutPath:o,stderrPath:l,runCommand:a=f,homeDir:s=m.homedir()}){const u=x(t,s),d=N(i,o,l,s);await k(c.dirname(u),{recursive:!0}),await $(u,gt({nodePath:e,cliPath:n,configDir:r,rootDir:d}),"utf8");let h=!1;et()&&(h=await ft({serviceID:t,wrapperPath:u,runCommand:a}));const w=M(t,s);return h?await y(w,{force:!0}).catch(()=>{}):await ut({wrapperPath:u,shortcutPath:w,runCommand:a}),{definitionPath:h?`task:${t}`:`startup:${t}`}},async start({serviceID:t,definitionPath:e,runCommand:n=f,homeDir:r=m.homedir()}){const i=x(t,r);if(e.startsWith("task:"))try{await n("schtasks",["/Run","/TN",t]);return}catch{}L("wscript.exe",["//B","//NoLogo",i])},async stop({serviceID:t,runCommand:e=f,homeDir:n=m.homedir()}){await e("schtasks",["/End","/TN",t],{allowFailure:!0});const r=`${t}-wrapper.vbs`;await G(r,{platform:"win32"})},async restart({serviceID:t,definitionPath:e,runCommand:n=f,homeDir:r=m.homedir()}){await n("schtasks",["/End","/TN",t],{allowFailure:!0});const i=`${t}-wrapper.vbs`;await G(i,{platform:"win32"});const o=x(t,r);if(e?.startsWith("task:"))try{await n("schtasks",["/Run","/TN",t]);return}catch{}L("wscript.exe",["//B","//NoLogo",o])},async uninstall({serviceID:t,runCommand:e=f,homeDir:n=m.homedir()}){await e("schtasks",["/Delete","/TN",t,"/F"],{allowFailure:!0});const r=x(t,n);await y(r,{force:!0});const i=M(t,n);await y(i,{force:!0})},async discoverServices({homeDir:t=m.homedir(),runCommand:e=f}={}){const n=D("win32"),r=await e("schtasks",["/Query","/FO","CSV","/NH"],{allowFailure:!0}),i=[];if(Number(r?.exitCode??-1)===0){const a=String(r.stdout??"").split(/\r?\n/);for(const s of a){const u=s.match(/^"([^"]+)"/);if(!u)continue;const d=u[1];if(!d.startsWith(n))continue;let h=null;try{const w=x(d,t),T=(await P(w,"utf8")).match(/--config-dir\s+""([^""]+)""/);T&&(h=T[1])}catch{}i.push({serviceID:d,definitionPath:`task:${d}`,configDir:h})}}const o=c.join(t,"AppData","Roaming","Microsoft","Windows","Start Menu","Programs","Startup"),l=await F(o).catch(()=>[]);for(const a of l){if(!a.startsWith(n)||!a.endsWith(".lnk"))continue;const s=a.slice(0,-4);if(i.some(d=>d.serviceID===s))continue;let u=null;try{const d=x(s,t),w=(await P(d,"utf8")).match(/--config-dir\s+""([^""]+)""/);w&&(u=w[1])}catch{}i.push({serviceID:s,definitionPath:`startup:${s}`,configDir:u})}return i},async isServiceLoaded({serviceID:t,runCommand:e=f,homeDir:n=m.homedir()}){const r=await e("schtasks",["/Query","/TN",t,"/NH"],{allowFailure:!0});return Number(r?.exitCode??1)===0?!0:b(M(t,n))}}}function $t(){return{platform:"linux",kind:"systemd-user",async install({serviceID:t,nodePath:e,cliPath:n,configDir:r,rootDir:i,stdoutPath:o,stderrPath:l,environmentPath:a="",homeDir:s=m.homedir(),runCommand:u=f}){const d=it(t,s),h=N(i,o,l,s);return await k(c.dirname(d),{recursive:!0}),await $(d,ct({serviceID:t,nodePath:e,cliPath:n,configDir:r,rootDir:h,stdoutPath:o,stderrPath:l,environmentPath:a}),{encoding:"utf8",mode:384}),await u("systemctl",["--user","daemon-reload"]),await u("systemctl",["--user","enable",`${t}.service`]),{definitionPath:d}},async start({serviceID:t,runCommand:e=f}){await e("systemctl",["--user","start",`${t}.service`])},async stop({serviceID:t,runCommand:e=f}){await e("systemctl",["--user","stop",`${t}.service`],{allowFailure:!0})},async restart({serviceID:t,runCommand:e=f}){await e("systemctl",["--user","restart",`${t}.service`])},async uninstall({serviceID:t,definitionPath:e,runCommand:n=f}){await n("systemctl",["--user","stop",`${t}.service`],{allowFailure:!0}),await n("systemctl",["--user","disable",`${t}.service`],{allowFailure:!0}),await y(e,{force:!0}),await n("systemctl",["--user","daemon-reload"])},async discoverServices({homeDir:t=m.homedir()}={}){const e=c.join(t,".config","systemd","user"),n=await F(e).catch(()=>[]),r=D("linux"),i=[];for(const o of n){if(!o.startsWith(r)||!o.endsWith(".service"))continue;const l=o.slice(0,-8),a=c.join(e,o);let s=null;try{const u=await P(a,"utf8");s=rt(u)}catch{}i.push({serviceID:l,definitionPath:a,configDir:s})}return i},async isServiceLoaded({serviceID:t,runCommand:e=f}){const n=await e("systemctl",["--user","is-active",`${t}.service`],{allowFailure:!0});return String(n?.stdout??"").trim()==="active"}}}function E(t){try{const e=U(t,"utf8"),n=JSON.parse(e);if(n&&typeof n.service_id=="string"&&typeof n.node_path=="string"&&typeof n.cli_path=="string"&&typeof n.root_dir=="string")return n}catch{}return null}function Pt(t){return c.join(t,"daemon.lock.json")}function W(t){const e=Pt(t);try{const n=U(e,"utf8"),r=JSON.parse(n);if(typeof r?.pid=="number")return r.pid}catch{}return 0}function q(t){return c.join(t,".profile")}function St(t){return t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function xt(t){const e=[R(t.nodePath),R(t.cliPath)];t.configDir&&e.push("--config-dir",R(t.configDir));const n=`env ${v}=${R(t.rootDir)} ${e.join(" ")}`;return`${n} status >/dev/null 2>&1 || (nohup ${n} start >/dev/null 2>&1 &)`}function Et(t,e){return`# >>> grix-connector autostart [${t}] >>>
102
- ${e}
99
+ `}async function xt(t){const{serviceID:n,wrapperPath:e,runCommand:r}=t,i=await Pt(r)??p.userInfo().username,o=c.join(c.dirname(e),`${n}-task.xml`);try{const a=St({userId:i,wrapperPath:e});return await $(o,"\uFEFF"+a,"utf16le"),await r("schtasks",["/Create","/TN",n,"/XML",o,"/F"]),!0}catch{return!1}finally{await y(o,{force:!0}).catch(()=>{})}}const K=4,kt="grix-wrapper-template:";function Ct(t){const n=c.join(t.rootDir,"data","upgrade-wrapper-child.json"),e=[`"${t.cliPath}"`,...t.configDir?["--config-dir",`"${t.configDir}"`]:[]],r=["$ErrorActionPreference = 'Stop'",`$recordPath = ${A(n)}`,"$exitCode = 1","try {"," [IO.Directory]::CreateDirectory([IO.Path]::GetDirectoryName($recordPath)) | Out-Null",` $p = Start-Process -FilePath ${A(t.nodePath)} -ArgumentList @(${e.map(A).join(", ")}) -WindowStyle Hidden -PassThru`,` $record = @{ pid = $p.Id; launched_at = [DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds(); boot_time = [DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds() - [Environment]::TickCount64; cli_path = ${A(t.cliPath)} }`," $utf8NoBom = New-Object System.Text.UTF8Encoding($false)"," try {"," [IO.File]::WriteAllText($recordPath, ($record | ConvertTo-Json -Compress), $utf8NoBom)"," } catch {",' & "$env:SystemRoot\\System32\\taskkill.exe" /PID $p.Id /T /F 2>$null | Out-Null'," $p.WaitForExit()"," throw"," }"," $p.WaitForExit()"," $exitCode = $p.ExitCode","} finally {"," Remove-Item -LiteralPath $recordPath -Force -ErrorAction SilentlyContinue","}","exit $exitCode"].join(`
100
+ `),i=Buffer.from(r,"utf16le").toString("base64"),l=`"${c.join(process.env.SystemRoot||"C:\\Windows","System32","WindowsPowerShell","v1.0","powershell.exe")}" -NoProfile -NonInteractive -ExecutionPolicy Bypass -EncodedCommand ${i}`,a=c.join(t.rootDir,"bin","upgrade-guardian.mjs"),s=c.join(t.rootDir,"bin","upgrade-guardian.fallback.mjs"),u=c.join(t.rootDir,"data","upgrade-wrapper.pause.json"),d=c.join(t.rootDir,"data","upgrade-pending.json"),h=c.join(t.rootDir,"data"),g=c.join(h,"wrapper.lock"),E=[`"${m(t.nodePath)}"`,`"${m(a)}"`,"activate","--root",`"${m(t.rootDir)}"`].join(" "),b=`${E} --wrapper-handoff`,H=[`"${m(t.nodePath)}"`,`"${m(s)}"`,"activate","--root",`"${m(t.rootDir)}"`].join(" "),et=`${H} --wrapper-handoff`;return[`' ${kt} ${K}`,"Option Explicit","Dim shell, processEnv, fso, command, guardianCommand, handoffGuardianCommand, guardianFallbackCommand, handoffGuardianFallbackCommand, guardianPath, guardianFallbackPath, maintenanceFile, pendingFile, delayMs, rapidCount, guardianExit, wrapperLockDir, wrapperLockFile, wrapperLock",`command = "${m(l)}"`,`guardianCommand = "${m(E)}"`,`handoffGuardianCommand = "${m(b)}"`,`guardianFallbackCommand = "${m(H)}"`,`handoffGuardianFallbackCommand = "${m(et)}"`,`guardianPath = "${m(a)}"`,`guardianFallbackPath = "${m(s)}"`,`maintenanceFile = "${m(u)}"`,`pendingFile = "${m(d)}"`,`wrapperLockDir = "${m(h)}"`,`wrapperLockFile = "${m(g)}"`,'Set shell = CreateObject("WScript.Shell")','Set processEnv = shell.Environment("Process")','Set fso = CreateObject("Scripting.FileSystemObject")',"On Error Resume Next","If Not fso.FolderExists(wrapperLockDir) Then fso.CreateFolder wrapperLockDir","Err.Clear","Set wrapperLock = fso.OpenTextFile(wrapperLockFile, 2, True)","If Err.Number <> 0 Then"," Err.Clear"," If fso.FileExists(wrapperLockFile) Then"," On Error GoTo 0"," WScript.Quit 0"," End If","Else"," wrapperLock.Write CStr(Now())"," Err.Clear","End If","On Error GoTo 0",`processEnv("${T}") = "${m(t.rootDir)}"`,'processEnv("GRIX_WRAPPER_SUPERVISED") = "1"',...j().map(([nt,rt])=>`processEnv("${nt}") = "${rt}"`),"delayMs = 5000","rapidCount = 0","Do"," Do While fso.FileExists(maintenanceFile)"," If fso.FileExists(guardianPath) Then"," guardianExit = shell.Run(guardianCommand, 0, True)"," ElseIf fso.FileExists(guardianFallbackPath) Then"," guardianExit = shell.Run(guardianFallbackCommand, 0, True)"," Else"," On Error Resume Next"," fso.DeleteFile maintenanceFile, True"," On Error GoTo 0"," WScript.Sleep 1000"," End If"," If fso.FileExists(maintenanceFile) Then WScript.Sleep 3000"," Loop"," If fso.FileExists(pendingFile) Then"," If fso.FileExists(guardianPath) Then"," guardianExit = shell.Run(guardianCommand, 0, True)"," ElseIf fso.FileExists(guardianFallbackPath) Then"," guardianExit = shell.Run(guardianFallbackCommand, 0, True)"," End If"," End If"," shell.Run command, 0, True"," If fso.FileExists(pendingFile) Then"," If fso.FileExists(guardianPath) Then"," guardianExit = shell.Run(handoffGuardianCommand, 0, True)"," ElseIf fso.FileExists(guardianFallbackPath) Then"," guardianExit = shell.Run(handoffGuardianFallbackCommand, 0, True)"," End If"," End If"," If fso.FileExists(pendingFile) And Not PendingIsTerminal() Then"," rapidCount = 0"," WScript.Sleep delayMs"," ElseIf fso.FileExists(maintenanceFile) Then"," rapidCount = 0"," Else"," rapidCount = rapidCount + 1"," If rapidCount >= 10 Then"," WScript.Sleep 300000"," rapidCount = 0"," Else"," WScript.Sleep delayMs"," End If"," End If","Loop","","Function PendingIsTerminal()"," Dim stream, text"," PendingIsTerminal = False"," On Error Resume Next"," Set stream = fso.OpenTextFile(pendingFile, 1)"," If Err.Number <> 0 Then"," Err.Clear"," On Error GoTo 0"," Exit Function"," End If"," text = stream.ReadAll"," stream.Close"," If Err.Number <> 0 Then"," Err.Clear"," On Error GoTo 0"," Exit Function"," End If"," On Error GoTo 0",' text = Replace(Replace(Replace(text, " ", ""), vbCr, ""), vbLf, "")',' If InStr(text, """phase"":""failed""") > 0 Then PendingIsTerminal = True',' If InStr(text, """phase"":""finalizing""") > 0 Then PendingIsTerminal = True',"End Function",""].join(`\r
101
+ `)}function vt(t,n){return c.join(ht(t,n),"dist","service","platform-adapter.js")}async function Tt(t,n={}){const e={version:K,build:Ct};try{if(c.basename(t)!==pt)return e;const r=c.dirname(t),i=ft(r,"current");if(!i)return e;const o=vt(r,i),l=n.selfModulePath??ot(import.meta.url);if(c.resolve(o)===c.resolve(l)||!C(o))return e;const a=await import(at(o).href),s=Number(a.WINDOWS_WRAPPER_TEMPLATE_VERSION),u=a.buildWindowsWrapperScript;return typeof u!="function"||!Number.isFinite(s)||s<=e.version?e:{version:s,build:u}}catch{return e}}function F(t){return`gui/${t??0}`}function Ft(t=process.env){return t.GRIX_LAUNCHER_MANAGED==="1"||!!t.GRIX_CLAUDE_INTERNAL_API_URL}function U(t){const n=String(t?.stdout??"").trim();return String(t?.stderr??"").trim()||n||`exit=${Number(t?.exitCode??-1)}`}function N(t,n,e,r){if(t?.trim())return c.resolve(t);const i=n||e;return i?c.resolve(c.dirname(c.dirname(i))):c.join(r,".grix")}async function q(t){try{await it(t)}catch{throw new Error(`launchd plist missing: ${t}`)}}async function Y(t){const{serviceID:n,domain:e,runCommand:r}=t,i=`${e}/${n}`,o=await r("launchctl",["bootout",i],{allowFailure:!0});if(Number(o?.exitCode??-1)!==0){const l=await r("launchctl",["print",i],{allowFailure:!0});if(Number(l?.exitCode??1)===0){await new Promise(s=>setTimeout(s,250));const a=await r("launchctl",["print",i],{allowFailure:!0});if(Number(a?.exitCode??1)===0)throw new Error(`launchctl bootout ${i}: ${U(o)}`)}return}for(let l=0;l<20;l+=1){const a=await r("launchctl",["print",i],{allowFailure:!0});if(Number(a?.exitCode??1)!==0)return;await new Promise(s=>setTimeout(s,250))}throw new Error(`launchctl bootout ${i}: service remains loaded`)}async function B(t){const{serviceID:n,domain:e,definitionPath:r,runCommand:i}=t,o=`${e}/${n}`,l=await i("launchctl",["bootstrap",e,r],{allowFailure:!0});if(Number(l?.exitCode??-1)===0)return;const a=await i("launchctl",["print",o],{allowFailure:!0});Number(a?.exitCode??1)===0&&await Y(t);const s=await i("launchctl",["bootstrap",e,r],{allowFailure:!0});if(Number(s?.exitCode??-1)!==0)throw new Error(`launchctl bootstrap ${e} failed: initial=${U(l)}; retry=${U(s)}`)}function bt(){return{platform:"darwin",kind:"launchd",async install({serviceID:t,nodePath:n,cliPath:e,configDir:r,rootDir:i,stdoutPath:o,stderrPath:l,environmentPath:a="",homeDir:s=p.homedir()}){const u=mt(t,s),d=N(i,o,l,s);return await v(c.dirname(u),{recursive:!0}),await $(u,wt({serviceID:t,nodePath:n,cliPath:e,configDir:r,rootDir:d,stdoutPath:o,stderrPath:l,environmentPath:a}),{encoding:"utf8",mode:384}),{definitionPath:u}},async start({serviceID:t,definitionPath:n,runCommand:e=f,uid:r=process.getuid?.()??0}){const i=F(r);await q(n),await B({serviceID:t,domain:i,definitionPath:n,runCommand:e})},async stop({serviceID:t,runCommand:n=f,uid:e=process.getuid?.()??0}){const r=F(e);await n("launchctl",["bootout",`${r}/${t}`],{allowFailure:!0})},async restart({serviceID:t,definitionPath:n,runCommand:e=f,uid:r=process.getuid?.()??0,env:i=process.env,reloadDefinition:o=!1}){const l=F(r),a={serviceID:t,domain:l,definitionPath:n,runCommand:e};if(await q(n),!o&&Ft(i)){const s=await e("launchctl",["kickstart","-k",`${l}/${t}`],{allowFailure:!0});if(Number(s?.exitCode??-1)===0)return;await B(a);return}await Y(a),await B(a)},async uninstall({serviceID:t,definitionPath:n,runCommand:e=f,uid:r=process.getuid?.()??0}){const i=F(r);await e("launchctl",["bootout",`${i}/${t}`],{allowFailure:!0}),await y(n,{force:!0})},async discoverServices({homeDir:t=p.homedir()}={}){const n=c.join(t,"Library","LaunchAgents"),e=await I(n).catch(()=>[]),r=L("darwin"),i=[];for(const o of e){if(!o.startsWith(r)||!o.endsWith(".plist"))continue;const l=o.slice(0,-6),a=c.join(n,o);let s=null;try{const u=await P(a,"utf8");s=lt(u)}catch{}i.push({serviceID:l,definitionPath:a,configDir:s})}return i},async isServiceLoaded({serviceID:t,runCommand:n=f,uid:e=process.getuid?.()??0}){const r=F(e),i=await n("launchctl",["print",`${r}/${t}`],{allowFailure:!0});return Number(i?.exitCode??1)===0}}}function It(t){return`${t}-wrapper.vbs`}const Rt=5e3;async function G(t,n){const e=await ct(It(t),{runCommandImpl:n,platform:"win32"}).catch(()=>[]),r=new Set([process.pid,process.ppid]);return e.filter(i=>!r.has(i))}async function Lt(t,n,e=Rt){const r=Date.now()+e;let i=await G(t,n);for(;i.length>0&&Date.now()<r;)await new Promise(o=>setTimeout(o,250)),i=await G(t,n);return i}function Dt(){return{platform:"win32",kind:"task-scheduler",async install({serviceID:t,nodePath:n,cliPath:e,configDir:r,rootDir:i,stdoutPath:o,stderrPath:l,runCommand:a=f,homeDir:s=p.homedir()}){const u=x(t,s),d=N(i,o,l,s);await v(c.dirname(u),{recursive:!0});const h=await Tt(e);await $(u,h.build({nodePath:n,cliPath:e,configDir:r,rootDir:d}),"utf8");let g=!1;st()&&(g=await xt({serviceID:t,wrapperPath:u,runCommand:a}));const E=M(t,s);return g?await y(E,{force:!0}).catch(()=>{}):await Et({wrapperPath:u,shortcutPath:E,runCommand:a}),{definitionPath:g?`task:${t}`:`startup:${t}`}},async start({serviceID:t,definitionPath:n,runCommand:e=f,homeDir:r=p.homedir()}){const i=x(t,r);if(n.startsWith("task:"))try{await e("schtasks",["/Run","/TN",t]);return}catch{}(await G(t,e)).length>0||_("wscript.exe",["//B","//NoLogo",i])},async stop({serviceID:t,runCommand:n=f,homeDir:e=p.homedir()}){await n("schtasks",["/End","/TN",t],{allowFailure:!0});const r=`${t}-wrapper.vbs`;await X(r,{platform:"win32"})},async restart({serviceID:t,definitionPath:n,runCommand:e=f,homeDir:r=p.homedir()}){await e("schtasks",["/End","/TN",t],{allowFailure:!0});const i=`${t}-wrapper.vbs`;await X(i,{platform:"win32"});const o=x(t,r);if(n?.startsWith("task:"))try{await e("schtasks",["/Run","/TN",t]);return}catch{}(await Lt(t,e)).length>0||_("wscript.exe",["//B","//NoLogo",o])},async uninstall({serviceID:t,runCommand:n=f,homeDir:e=p.homedir()}){await n("schtasks",["/Delete","/TN",t,"/F"],{allowFailure:!0});const r=x(t,e);await y(r,{force:!0});const i=M(t,e);await y(i,{force:!0})},async discoverServices({homeDir:t=p.homedir(),runCommand:n=f}={}){const e=L("win32"),r=await n("schtasks",["/Query","/FO","CSV","/NH"],{allowFailure:!0}),i=[];if(Number(r?.exitCode??-1)===0){const a=String(r.stdout??"").split(/\r?\n/);for(const s of a){const u=s.match(/^"([^"]+)"/);if(!u)continue;const d=u[1];if(!d.startsWith(e))continue;let h=null;try{const g=x(d,t),b=(await P(g,"utf8")).match(/--config-dir\s+""([^""]+)""/);b&&(h=b[1])}catch{}i.push({serviceID:d,definitionPath:`task:${d}`,configDir:h})}}const o=c.join(t,"AppData","Roaming","Microsoft","Windows","Start Menu","Programs","Startup"),l=await I(o).catch(()=>[]);for(const a of l){if(!a.startsWith(e)||!a.endsWith(".lnk"))continue;const s=a.slice(0,-4);if(i.some(d=>d.serviceID===s))continue;let u=null;try{const d=x(s,t),g=(await P(d,"utf8")).match(/--config-dir\s+""([^""]+)""/);g&&(u=g[1])}catch{}i.push({serviceID:s,definitionPath:`startup:${s}`,configDir:u})}return i},async isServiceLoaded({serviceID:t,runCommand:n=f,homeDir:e=p.homedir()}){const r=await n("schtasks",["/Query","/TN",t,"/NH"],{allowFailure:!0});return Number(r?.exitCode??1)===0?!0:C(M(t,e))}}}function At(){return{platform:"linux",kind:"systemd-user",async install({serviceID:t,nodePath:n,cliPath:e,configDir:r,rootDir:i,stdoutPath:o,stderrPath:l,environmentPath:a="",homeDir:s=p.homedir(),runCommand:u=f}){const d=dt(t,s),h=N(i,o,l,s);return await v(c.dirname(d),{recursive:!0}),await $(d,yt({serviceID:t,nodePath:n,cliPath:e,configDir:r,rootDir:h,stdoutPath:o,stderrPath:l,environmentPath:a}),{encoding:"utf8",mode:384}),await u("systemctl",["--user","daemon-reload"]),await u("systemctl",["--user","enable",`${t}.service`]),{definitionPath:d}},async start({serviceID:t,runCommand:n=f}){await n("systemctl",["--user","start",`${t}.service`])},async stop({serviceID:t,runCommand:n=f}){await n("systemctl",["--user","stop",`${t}.service`],{allowFailure:!0})},async restart({serviceID:t,runCommand:n=f}){await n("systemctl",["--user","restart",`${t}.service`])},async uninstall({serviceID:t,definitionPath:n,runCommand:e=f}){await e("systemctl",["--user","stop",`${t}.service`],{allowFailure:!0}),await e("systemctl",["--user","disable",`${t}.service`],{allowFailure:!0}),await y(n,{force:!0}),await e("systemctl",["--user","daemon-reload"])},async discoverServices({homeDir:t=p.homedir()}={}){const n=c.join(t,".config","systemd","user"),e=await I(n).catch(()=>[]),r=L("linux"),i=[];for(const o of e){if(!o.startsWith(r)||!o.endsWith(".service"))continue;const l=o.slice(0,-8),a=c.join(n,o);let s=null;try{const u=await P(a,"utf8");s=ut(u)}catch{}i.push({serviceID:l,definitionPath:a,configDir:s})}return i},async isServiceLoaded({serviceID:t,runCommand:n=f}){const e=await n("systemctl",["--user","is-active",`${t}.service`],{allowFailure:!0});return String(e?.stdout??"").trim()==="active"}}}function k(t){try{const n=V(t,"utf8"),e=JSON.parse(n);if(e&&typeof e.service_id=="string"&&typeof e.node_path=="string"&&typeof e.cli_path=="string"&&typeof e.root_dir=="string")return e}catch{}return null}function Nt(t){return c.join(t,"daemon.lock.json")}function W(t){const n=Nt(t);try{const e=V(n,"utf8"),r=JSON.parse(e);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 _t(t){const n=[D(t.nodePath),D(t.cliPath)];t.configDir&&n.push("--config-dir",D(t.configDir));const e=`env ${T}=${D(t.rootDir)} ${n.join(" ")}`;return`${e} status >/dev/null 2>&1 || (nohup ${e} start >/dev/null 2>&1 &)`}function Ot(t,n){return`# >>> grix-connector autostart [${t}] >>>
102
+ ${n}
103
103
  # <<< grix-connector autostart [${t}] <<<
104
- `}function K(t){const e=St(t);return new RegExp(`(?:^|\\n)# >>> grix-connector autostart \\[${e}\\] >>>\\n[\\s\\S]*?\\n# <<< grix-connector autostart \\[${e}\\] <<<\\n?`,"m")}async function kt(t,e,n){let r="";try{r=await P(t,"utf8")}catch{}const i=Et(e,n),o=K(e);let l;if(o.test(r))l=r.replace(o,`
104
+ `}function tt(t){const n=Wt(t);return new RegExp(`(?:^|\\n)# >>> grix-connector autostart \\[${n}\\] >>>\\n[\\s\\S]*?\\n# <<< grix-connector autostart \\[${n}\\] <<<\\n?`,"m")}async function jt(t,n,e){let r="";try{r=await P(t,"utf8")}catch{}const i=Ot(n,e),o=tt(n);let l;if(o.test(r))l=r.replace(o,`
105
105
  ${i}`).replace(/^\n+/,"");else{const a=r.length===0||r.endsWith(`
106
106
  `)?"":`
107
- `;l=`${r}${a}${i}`}l!==r&&(await k(c.dirname(t),{recursive:!0}),await $(t,l,{encoding:"utf8"}))}async function vt(t,e){let n="";try{n=await P(t,"utf8")}catch{return}const r=K(e);if(!r.test(n))return;const i=n.replace(r,"").replace(/^\n+/,"");await $(t,i,{encoding:"utf8"})}function Ct(){return{platform:"linux",kind:"bare-daemon",async install({serviceID:t,nodePath:e,cliPath:n,configDir:r,rootDir:i,stdoutPath:o,stderrPath:l,homeDir:a=m.homedir()}){const s=S(t,a);await k(c.dirname(s),{recursive:!0});const u=N(i,o,l,a),d={schema_version:1,service_id:t,node_path:e,cli_path:n,config_dir:r??"",root_dir:u,stdout_path:o,stderr_path:l,installed_at:Date.now()};await $(s,`${JSON.stringify(d,null,2)}
108
- `,{encoding:"utf8",mode:384});const h=xt({nodePath:e,cliPath:n,configDir:r??"",rootDir:u});return await kt(q(a),t,h),{definitionPath:s}},async start({serviceID:t,homeDir:e=m.homedir()}){const n=S(t,e),r=E(n);if(!r)throw new Error(`bare-daemon marker missing: ${n}`);const i=[r.cli_path];r.config_dir&&i.push("--config-dir",r.config_dir),L(r.node_path,i,{env:{...process.env,[v]:r.root_dir}})},async stop({serviceID:t,homeDir:e=m.homedir()}){const n=S(t,e),r=E(n);if(!r)return;const i=W(r.root_dir);if(i>0&&I(i))try{process.kill(i,"SIGTERM")}catch{}},async restart(t){await this.stop(t);const e=Date.now()+5e3,n=S(t.serviceID,t.homeDir??m.homedir()),r=E(n);for(;r&&Date.now()<e;){const i=W(r.root_dir);if(i<=0||!I(i))break;await new Promise(o=>setTimeout(o,100))}await this.start(t)},async uninstall({serviceID:t,homeDir:e=m.homedir()}){const n=S(t,e),r=E(n);if(r){const i=W(r.root_dir);if(i>0&&I(i))try{process.kill(i,"SIGTERM")}catch{}}await y(n,{force:!0}),await vt(q(e),t)},async discoverServices({homeDir:t=m.homedir()}={}){const e=c.join(t,".grix","service"),n=await F(e).catch(()=>[]),r=D("linux"),i=[];for(const o of n){if(!o.startsWith(r)||!o.endsWith(".bare.json"))continue;const l=o.slice(0,-10),a=c.join(e,o),s=E(a);i.push({serviceID:l,definitionPath:a,configDir:s?.config_dir||null})}return i},async isServiceLoaded({serviceID:t,homeDir:e=m.homedir()}){const n=S(t,e),r=E(n);if(!r)return!1;const i=W(r.root_dir);return i>0&&I(i)}}}function Tt(t=process.getuid?.()??0){if(process.env.GRIX_FORCE_BARE_DAEMON==="1")return!1;const e=process.env.XDG_RUNTIME_DIR;return!!(e&&b(c.join(e,"bus"))||b(`/run/user/${t}/bus`))}function Wt(t=process.platform,e={}){return t==="darwin"?wt():t==="win32"?yt():e.systemdUserAvailable??Tt()?$t():Ct()}export{ht as WINDOWS_WRAPPER_TEMPLATE_MARKER,pt as WINDOWS_WRAPPER_TEMPLATE_VERSION,gt as buildWindowsWrapperScript,j as collectServicePortEnv,Wt as getPlatformServiceAdapter,Tt as isSystemdUserBusAvailable,vt as removeProfileAutostart,M as resolveWindowsStartupShortcutPath,x as resolveWindowsWrapperPath,kt as upsertProfileAutostart};
107
+ `;l=`${r}${a}${i}`}l!==r&&(await v(c.dirname(t),{recursive:!0}),await $(t,l,{encoding:"utf8"}))}async function Mt(t,n){let e="";try{e=await P(t,"utf8")}catch{return}const r=tt(n);if(!r.test(e))return;const i=e.replace(r,"").replace(/^\n+/,"");await $(t,i,{encoding:"utf8"})}function Ut(){return{platform:"linux",kind:"bare-daemon",async install({serviceID:t,nodePath:n,cliPath:e,configDir:r,rootDir:i,stdoutPath:o,stderrPath:l,homeDir:a=p.homedir()}){const s=S(t,a);await v(c.dirname(s),{recursive:!0});const u=N(i,o,l,a),d={schema_version:1,service_id:t,node_path:n,cli_path:e,config_dir:r??"",root_dir:u,stdout_path:o,stderr_path:l,installed_at:Date.now()};await $(s,`${JSON.stringify(d,null,2)}
108
+ `,{encoding:"utf8",mode:384});const h=_t({nodePath:n,cliPath:e,configDir:r??"",rootDir:u});return await jt(Z(a),t,h),{definitionPath:s}},async start({serviceID:t,homeDir:n=p.homedir()}){const e=S(t,n),r=k(e);if(!r)throw new Error(`bare-daemon marker missing: ${e}`);const i=[r.cli_path];r.config_dir&&i.push("--config-dir",r.config_dir),_(r.node_path,i,{env:{...process.env,[T]:r.root_dir}})},async stop({serviceID:t,homeDir:n=p.homedir()}){const e=S(t,n),r=k(e);if(!r)return;const i=W(r.root_dir);if(i>0&&R(i))try{process.kill(i,"SIGTERM")}catch{}},async restart(t){await this.stop(t);const n=Date.now()+5e3,e=S(t.serviceID,t.homeDir??p.homedir()),r=k(e);for(;r&&Date.now()<n;){const i=W(r.root_dir);if(i<=0||!R(i))break;await new Promise(o=>setTimeout(o,100))}await this.start(t)},async uninstall({serviceID:t,homeDir:n=p.homedir()}){const e=S(t,n),r=k(e);if(r){const i=W(r.root_dir);if(i>0&&R(i))try{process.kill(i,"SIGTERM")}catch{}}await y(e,{force:!0}),await Mt(Z(n),t)},async discoverServices({homeDir:t=p.homedir()}={}){const n=c.join(t,".grix","service"),e=await I(n).catch(()=>[]),r=L("linux"),i=[];for(const o of e){if(!o.startsWith(r)||!o.endsWith(".bare.json"))continue;const l=o.slice(0,-10),a=c.join(n,o),s=k(a);i.push({serviceID:l,definitionPath:a,configDir:s?.config_dir||null})}return i},async isServiceLoaded({serviceID:t,homeDir:n=p.homedir()}){const e=S(t,n),r=k(e);if(!r)return!1;const i=W(r.root_dir);return i>0&&R(i)}}}function Bt(t=process.getuid?.()??0){if(process.env.GRIX_FORCE_BARE_DAEMON==="1")return!1;const n=process.env.XDG_RUNTIME_DIR;return!!(n&&C(c.join(n,"bus"))||C(`/run/user/${t}/bus`))}function Yt(t=process.platform,n={}){return t==="darwin"?bt():t==="win32"?Dt():n.systemdUserAvailable??Bt()?At():Ut()}export{kt as WINDOWS_WRAPPER_TEMPLATE_MARKER,K as WINDOWS_WRAPPER_TEMPLATE_VERSION,Ct as buildWindowsWrapperScript,j as collectServicePortEnv,Yt as getPlatformServiceAdapter,Ft as isInsideManagedDaemonTree,Bt as isSystemdUserBusAvailable,Mt as removeProfileAutostart,M as resolveWindowsStartupShortcutPath,x as resolveWindowsWrapperPath,Tt as resolveWindowsWrapperTemplateSource,jt as upsertProfileAutostart};
@@ -1 +1 @@
1
- import{execFileSync as v,execSync as m,spawn as y}from"node:child_process";import n from"node:process";import{setTimeout as I}from"node:timers/promises";let d;function F(e=n.platform,t=n.env){return e==="win32"&&t.GRIX_WRAPPER_SUPERVISED!=="1"}function _(){if(n.platform!=="win32")return!1;if(d!==void 0)return d;try{m("net session",{stdio:"ignore",windowsHide:!0}),d=!0}catch{d=!1}return d}function H(e,t,r={}){y(e,t,{detached:!0,stdio:"ignore",windowsHide:!0,env:r.env??n.env}).unref()}function w(e,t,r={}){const{cwd:o,env:u=n.env,allowFailure:a=!1}=r;return new Promise((c,i)=>{const l=y(e,t,{cwd:o||void 0,env:u,stdio:["ignore","pipe","pipe"],windowsHide:!0}),g=[],C=[];l.stdout&&l.stdout.on("data",s=>g.push(s)),l.stderr&&l.stderr.on("data",s=>C.push(s)),l.on("error",s=>{if(a){c({exitCode:-1,stdout:"",stderr:s instanceof Error?s.message:String(s)});return}i(s)}),l.on("close",s=>{const f=s??0,S=Buffer.concat(g).toString("utf8"),x=Buffer.concat(C).toString("utf8");if(f!==0&&!a){const E=x.trim()||S.trim()||`${e} exited with code ${f}`;i(new Error(E));return}c({exitCode:f,stdout:S,stderr:x})})})}function p(e){if(e<=0)return!1;try{return n.kill(e,0),!0}catch(t){if(t instanceof Error&&"code"in t){if(t.code==="ESRCH")return!1;if(t.code==="EPERM")return!0}return!1}}const h=3e3;function L(e){try{const o=m(`wmic process where ProcessId=${e} get CommandLine /format:list`,{encoding:"utf8",stdio:["ignore","pipe","ignore"],timeout:h,windowsHide:!0}).match(/CommandLine=(.*)/)?.[1]?.trim();if(o)return o}catch{}try{const t=`${n.env.SystemRoot||"C:\\Windows"}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`,o=v(t,["-NoProfile","-NonInteractive","-WindowStyle","Hidden","-Command",`(Get-CimInstance Win32_Process -Filter 'ProcessId=${e}').CommandLine`],{encoding:"utf8",stdio:["ignore","pipe","ignore"],timeout:h,windowsHide:!0}).trim();return o||null}catch{return null}}function T(e){if(e<=0||!Number.isInteger(e))return null;if(n.platform==="win32")return L(e);try{const r=m(`ps -ww -o command= -p ${e}`,{encoding:"utf8",stdio:["ignore","pipe","ignore"],timeout:h}).trim();return r||null}catch{return null}}async function W(e,t={}){const{platform:r=n.platform,runCommandImpl:o=w}=t;if(e<=0)return!1;if(r==="win32")return await o("taskkill",["/PID",String(e),"/T","/F"],{allowFailure:!0}),!0;try{return n.kill(-e,"SIGTERM"),!0}catch{}try{return n.kill(e,"SIGTERM"),!0}catch{return!1}}async function G(e,t={}){const{timeoutMs:r=5e3,intervalMs:o=100}=t;if(e<=0)return!0;const u=Date.now()+r;for(;Date.now()<=u;){if(!p(e))return!0;await I(o)}return!p(e)}function R(){return`${n.env.SystemRoot||"C:\\Windows"}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`}function P(e){return e.split(/\r?\n/).map(t=>parseInt(t.trim(),10)).filter(t=>t>0&&!Number.isNaN(t))}async function k(e){const t=w;if(n.platform==="win32"){const o=await t("wmic",["process","where",`CommandLine Like '%${e}%'`,"get","ProcessId"],{allowFailure:!0});if(o.exitCode===0)return P(o.stdout);const u=e.replace(/'/g,"''"),a=await t(R(),["-NoProfile","-NonInteractive","-WindowStyle","Hidden","-Command",`Get-CimInstance Win32_Process | Where-Object { $_.CommandLine -like '*${u}*' } | Select-Object -ExpandProperty ProcessId`],{allowFailure:!0});return a.exitCode!==0?[]:P(a.stdout)}const r=await t("pgrep",["-f",e],{allowFailure:!0});return r.exitCode!==0?[]:P(r.stdout)}async function D(e,t={}){const{exceptPid:r=0,platform:o=n.platform}=t,u=await k(e),a=new Set([n.pid,n.ppid,r]),c=u.filter(i=>!a.has(i));if(c.length===0)return 0;if(o==="win32")for(const i of c)await W(i,{platform:"win32",runCommandImpl:w});else{for(const i of c)try{n.kill(i,"SIGTERM")}catch{}await I(500);for(const i of c)if(p(i))try{n.kill(i,"SIGKILL")}catch{}}return c.length}export{k as findProcessesByCommandLine,T as getProcessCommandLine,p as isProcessRunning,_ as isWindowsElevated,D as killProcessesByCommandLine,w as runCommand,F as shouldSweepWindowsWrappers,H as spawnDetached,W as terminateProcessTree,G as waitForProcessExit};
1
+ import{execFileSync as v,execSync as m,spawn as y}from"node:child_process";import r from"node:process";import{setTimeout as I}from"node:timers/promises";let d;function F(e=r.platform,t=r.env){return e==="win32"&&t.GRIX_WRAPPER_SUPERVISED!=="1"}function _(){if(r.platform!=="win32")return!1;if(d!==void 0)return d;try{m("net session",{stdio:"ignore",windowsHide:!0}),d=!0}catch{d=!1}return d}function H(e,t,n={}){y(e,t,{detached:!0,stdio:"ignore",windowsHide:!0,env:n.env??r.env}).unref()}function w(e,t,n={}){const{cwd:o,env:u=r.env,allowFailure:a=!1}=n;return new Promise((i,s)=>{const l=y(e,t,{cwd:o||void 0,env:u,stdio:["ignore","pipe","pipe"],windowsHide:!0}),P=[],g=[];l.stdout&&l.stdout.on("data",c=>P.push(c)),l.stderr&&l.stderr.on("data",c=>g.push(c)),l.on("error",c=>{if(a){i({exitCode:-1,stdout:"",stderr:c instanceof Error?c.message:String(c)});return}s(c)}),l.on("close",c=>{const f=c??0,S=Buffer.concat(P).toString("utf8"),x=Buffer.concat(g).toString("utf8");if(f!==0&&!a){const E=x.trim()||S.trim()||`${e} exited with code ${f}`;s(new Error(E));return}i({exitCode:f,stdout:S,stderr:x})})})}function p(e){if(e<=0)return!1;try{return r.kill(e,0),!0}catch(t){if(t instanceof Error&&"code"in t){if(t.code==="ESRCH")return!1;if(t.code==="EPERM")return!0}return!1}}const h=3e3;function L(e){try{const o=m(`wmic process where ProcessId=${e} get CommandLine /format:list`,{encoding:"utf8",stdio:["ignore","pipe","ignore"],timeout:h,windowsHide:!0}).match(/CommandLine=(.*)/)?.[1]?.trim();if(o)return o}catch{}try{const t=`${r.env.SystemRoot||"C:\\Windows"}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`,o=v(t,["-NoProfile","-NonInteractive","-WindowStyle","Hidden","-Command",`(Get-CimInstance Win32_Process -Filter 'ProcessId=${e}').CommandLine`],{encoding:"utf8",stdio:["ignore","pipe","ignore"],timeout:h,windowsHide:!0}).trim();return o||null}catch{return null}}function T(e){if(e<=0||!Number.isInteger(e))return null;if(r.platform==="win32")return L(e);try{const n=m(`ps -ww -o command= -p ${e}`,{encoding:"utf8",stdio:["ignore","pipe","ignore"],timeout:h}).trim();return n||null}catch{return null}}async function W(e,t={}){const{platform:n=r.platform,runCommandImpl:o=w}=t;if(e<=0)return!1;if(n==="win32")return await o("taskkill",["/PID",String(e),"/T","/F"],{allowFailure:!0}),!0;try{return r.kill(-e,"SIGTERM"),!0}catch{}try{return r.kill(e,"SIGTERM"),!0}catch{return!1}}async function G(e,t={}){const{timeoutMs:n=5e3,intervalMs:o=100}=t;if(e<=0)return!0;const u=Date.now()+n;for(;Date.now()<=u;){if(!p(e))return!0;await I(o)}return!p(e)}function R(){return`${r.env.SystemRoot||"C:\\Windows"}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`}function C(e){return e.split(/\r?\n/).map(t=>parseInt(t.trim(),10)).filter(t=>t>0&&!Number.isNaN(t))}async function k(e,t={}){const n=t.runCommandImpl??w;if((t.platform??r.platform)==="win32"){const u=await n("wmic",["process","where",`CommandLine Like '%${e}%'`,"get","ProcessId"],{allowFailure:!0});if(u.exitCode===0)return C(u.stdout);const a=e.replace(/'/g,"''"),i=await n(R(),["-NoProfile","-NonInteractive","-WindowStyle","Hidden","-Command",`Get-CimInstance Win32_Process | Where-Object { $_.CommandLine -like '*${a}*' } | Select-Object -ExpandProperty ProcessId`],{allowFailure:!0});return i.exitCode!==0?[]:C(i.stdout)}const o=await n("pgrep",["-f",e],{allowFailure:!0});return o.exitCode!==0?[]:C(o.stdout)}async function D(e,t={}){const{exceptPid:n=0,platform:o=r.platform}=t,u=await k(e),a=new Set([r.pid,r.ppid,n]),i=u.filter(s=>!a.has(s));if(i.length===0)return 0;if(o==="win32")for(const s of i)await W(s,{platform:"win32",runCommandImpl:w});else{for(const s of i)try{r.kill(s,"SIGTERM")}catch{}await I(500);for(const s of i)if(p(s))try{r.kill(s,"SIGKILL")}catch{}}return i.length}export{k as findProcessesByCommandLine,T as getProcessCommandLine,p as isProcessRunning,_ as isWindowsElevated,D as killProcessesByCommandLine,w as runCommand,F as shouldSweepWindowsWrappers,H as spawnDetached,W as terminateProcessTree,G as waitForProcessExit};
@@ -1,3 +1,3 @@
1
- import{createHash as F}from"node:crypto";import l from"node:fs";import T from"node:os";import o from"node:path";import D from"node:process";import{mkdir as _}from"node:fs/promises";import{setTimeout as B}from"node:timers/promises";import{resolveRuntimePaths as P}from"../core/config/paths.js";import{inspectDaemonState as g}from"../runtime/service-state.js";import{isProcessRunning as N,runCommand as m,terminateProcessTree as M,waitForProcessExit as W}from"./process-control.js";import{isLockHolderIdentityVerified as k,readDaemonLock as w}from"../runtime/daemon-lock.js";import{ServiceInstallStore as q}from"./service-install-store.js";import{getPlatformServiceAdapter as O}from"./platform-adapter.js";import{captureLogCursor as v,formatLogDelta as y}from"./log-diagnostics.js";import{LAUNCHER_FILE as x,bootstrapVersionFromPackage as U,compareVersions as j,ensureLauncher as V,launcherPath as z,packagedLauncherPath as G,readLauncherTemplateVersion as J,readPackageVersionAt as A,readPointer as E,switchCurrent as X,versionEntry as I}from"../core/upgrade/version-store.js";import{buildServiceID as S,parseExecStartWords as K,parseProgramArgumentsFromPlistXML as Q,resolveLinuxUserUnitPath as Y,resolveMacOSLaunchAgentPath as Z,resolveWindowsServiceRuntimeDir as tt,resolveServiceInstallRecordPath as et,resolveServiceStderrPath as it,resolveServiceStdoutPath as rt}from"./service-paths.js";import{resolveWindowsStartupShortcutPath as st,resolveWindowsWrapperPath as $}from"./platform-adapter.js";import{readInstalledWrapperTemplate as at}from"./windows-wrapper-migration.js";import{appendLegacyHealLog as ot,isLauncherStoreUsable as b,parseWrapperCliPath as nt,planLegacyStateHeal as ct}from"./legacy-state-heal.js";function dt(L){try{return l.realpathSync(L)}catch{return L}}class jt{adapter;platform;homeDir;uid;nodePath;cliPath;waitForExit;terminateTree;constructor(e){this.platform=e.platform??D.platform,this.homeDir=e.homeDir??T.homedir(),this.uid=e.uid??D.getuid?.()??0,this.nodePath=e.nodePath??D.execPath,this.cliPath=dt(e.cliPath),this.adapter=O(this.platform),this.waitForExit=e.processControl?.waitForExit??W,this.terminateTree=e.processControl?.terminateTree??M}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"?tt(this.normalizeRootDir(e),D.env,this.homeDir):o.join(t,"runtime"):null}resolveServiceCliPath(e){const t=this.resolveServiceRuntimeDir(e);return t?z(t):this.cliPath}resolveSourcePackageDir(){return o.dirname(o.dirname(this.cliPath))}async adoptLauncher(e){const t=this.normalizeRootDir(e),r=this.resolveServiceRuntimeDir(t);if(!r)return!1;const i=await this.createStore(t).load();return!i||i.cli_path===this.resolveServiceCliPath(t)&&b(r)?!1:(await this.refreshDescriptor(t,i),!0)}prepareServiceRuntime(e){const t=this.resolveServiceRuntimeDir(e);if(!t)return;const r=this.resolveSourcePackageDir();l.mkdirSync(t,{recursive:!0});const i=A(r);if(!i)throw new Error(`Cannot read package version at ${r} (entry ${this.cliPath}); refusing to install the service`);if(!V(t,r))throw new Error(`Package at ${r} does not ship ${x}; refusing to point the service at it`);const s=E(t,"current");if((!s||!l.existsSync(I(t,s))||j(i,s)>0)&&(U(t,r,i),X(t,i)),this.platform==="win32"||this.platform==="darwin"){const a=o.join(r,"scripts","upgrade-guardian.mjs");if(l.existsSync(a)){const n=o.join(this.normalizeRootDir(e),"bin"),d=o.join(n,"upgrade-guardian.mjs"),c=o.join(n,"upgrade-guardian.fallback.mjs"),h=`${d}.tmp-${D.pid}`;l.mkdirSync(n,{recursive:!0});try{l.copyFileSync(a,h),l.renameSync(h,d),l.copyFileSync(a,c)}finally{l.rmSync(h,{force:!0})}}if(this.platform==="darwin"){const n=o.join(r,"scripts","upgrade-guardian.sh");if(l.existsSync(n)){const d=o.join(this.normalizeRootDir(e),"bin","upgrade-guardian.sh"),c=`${d}.tmp-${D.pid}`;try{l.copyFileSync(n,c),l.chmodSync(c,493),l.renameSync(c,d)}finally{l.rmSync(c,{force:!0})}}}}}buildDescriptor(e,t,r=""){const i=this.normalizeRootDir(e),s=this.resolveServiceCliPath(i);return{schema_version:1,platform:this.platform,service_id:S(i,this.platform),node_path:this.nodePath,cli_path:s,cli_hash:this.computeServiceHash(s)??"",definition_path:r,config_dir:t??"",installed_at:0,updated_at:0}}createStore(e){return new q(et(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"),S(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:rt(e),stderrPath:it(e)}}buildServiceEnvironmentPath(){const e=[o.dirname(this.nodePath),...this.platform==="darwin"?["/opt/homebrew/bin"]:[],"/usr/local/bin","/usr/bin","/bin","/usr/sbin","/sbin"],t=String(D.env.PATH??"").split(o.delimiter).filter(s=>s&&o.isAbsolute(s)).filter(s=>!s.includes("/node_modules/.bin")).filter(s=>!s.includes("/node-gyp-bin")),r=[],i=new Set;for(const s of[...e,...t]){if(i.has(s))continue;if(i.add(s),[...r,s].join(o.delimiter).length>2048)break;r.push(s)}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 grix-connector start\uFF08\u4F1A\u81EA\u52A8\u5B89\u88C5\u5E76\u542F\u52A8\u670D\u52A1\uFF09\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),i=this.computeServiceHash(this.cliPath);return r!==null&&i!==null&&r===t.cli_hash&&i===t.cli_hash}computeFileHash(e){try{const t=l.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=A(t)??"",i=o.basename(e)===x,s=i?o.dirname(e):null,a=i?e:G(t);if(!a)return null;const n=J(a);if(n<0)return null;let d=!0;if(s){const c=E(s,"current");d=!!c&&l.existsSync(I(s,c))&&j(c,r)>=0}return d?F("sha256").update(`launcher:${n}`).update(`source:${r}`).digest("hex"):null}catch{return null}}computeRuntimeDirHash(e){try{const t=o.dirname(e),r=F("sha256"),i=a=>{const n=l.readdirSync(a,{withFileTypes:!0}).sort((d,c)=>d.name.localeCompare(c.name));for(const d of n){const c=o.join(a,d.name);r.update(o.relative(t,c)),d.isDirectory()?i(c):d.isFile()&&r.update(l.readFileSync(c))}};i(t);const s=o.join(o.dirname(t),"package.json");return l.existsSync(s)&&(r.update("package.json"),r.update(l.readFileSync(s))),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 i=this.resolveServiceCliPath(r),s={...t,platform:this.platform,service_id:t.service_id||S(r,this.platform),node_path:this.nodePath,cli_path:i,cli_hash:this.computeServiceHash(i)??"",config_dir:t.config_dir},a=await this.resolveServiceLogPaths(r),n=await this.adapter.install({...this.toAdapterPayload(s,r),stdoutPath:a.stdoutPath,stderrPath:a.stderrPath,homeDir:this.homeDir,uid:this.uid,runCommand:m}),d={...s,installed_at:t.installed_at,definition_path:n?.definitionPath||s.definition_path,updated_at:Date.now()};return await this.createStore(r).save(d),d}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=S(t,this.platform);return(await this.adapter.discoverServices({homeDir:this.homeDir,runCommand:m})).filter(s=>s.serviceID!==r)}async cleanupConflictingServices(e){const t=await this.discoverConflictingServices(e);if(t.length>0){const i=t.map(s=>s.serviceID).join(", ");console.log(`\u53D1\u73B0 ${t.length} \u4E2A\u6B8B\u7559\u670D\u52A1\uFF0C\u6B63\u5728\u68C0\u67E5: ${i}`)}let r=0;for(const i of t){if(await this.adapter.isServiceLoaded({serviceID:i.serviceID,uid:this.uid,homeDir:this.homeDir,runCommand:m})){console.log(`\u8DF3\u8FC7\u6B63\u5728\u8FD0\u884C\u7684\u670D\u52A1: ${i.serviceID}`);continue}try{await this.adapter.stop({serviceID:i.serviceID,uid:this.uid,homeDir:this.homeDir,runCommand:m})}catch{}try{await this.adapter.uninstall({serviceID:i.serviceID,definitionPath:i.definitionPath,uid:this.uid,homeDir:this.homeDir,runCommand:m}),r++}catch{}}return r}captureDaemonBaseline(e){return{status:g(e.daemonStatusFile),lock:w(e.daemonLockFile)}}isDaemonReady(e,t,r){const i=g(e.daemonStatusFile),s=w(e.daemonLockFile);if(!i.running||i.state!=="running"||i.pid<=0||!s||s.pid!==i.pid||!N(s.pid)||typeof s.root_dir!="string"||o.resolve(s.root_dir)!==o.resolve(e.rootDir)||typeof s.instance_id!="string"||s.instance_id.length===0||i.instance_id!==s.instance_id)return!1;const a=i.pid!==t.status.pid||i.state!==t.status.state||i.updated_at!==t.status.updated_at||i.instance_id!==t.status.instance_id,n=!t.lock||s.pid!==t.lock.pid||s.acquired_at!==t.lock.acquired_at||s.instance_id!==t.lock.instance_id,d=a&&n,c=t.status.state==="starting"&&t.status.instance_id===s.instance_id&&t.lock?.pid===s.pid&&t.lock.instance_id===s.instance_id&&i.instance_id===s.instance_id&&i.updated_at!==t.status.updated_at,h=t.lock?.pid===s.pid&&t.lock.instance_id===s.instance_id&&t.lock.acquired_at===s.acquired_at&&(t.status.pid!==s.pid||t.status.instance_id!==s.instance_id||t.status.state!=="running")&&i.pid===s.pid&&i.instance_id===s.instance_id&&i.state==="running"&&i.updated_at!==t.status.updated_at;return!k(s)&&!d&&!c&&!h?!1:r?d||c||h:!0}canForceTerminateDaemon(e,t){const r=w(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(),i=t.baseline??this.captureDaemonBaseline(e),s=t.requireNewDaemon??!1,a=t.stdoutLogFile??e.stdoutLogFile,n=t.stderrLogFile??e.stderrLogFile,d=t.stdoutLogCursor??v(a),c=t.stderrLogCursor??v(n),h=Date.now();for(;Date.now()-h<r;){if(this.isDaemonReady(e,i,s))return;const f=g(e.daemonStatusFile);if(f.state==="failed"&&(i.status.state!=="failed"||f.pid!==i.status.pid||f.updated_at!==i.status.updated_at||f.instance_id!==i.status.instance_id)){const R=w(e.daemonLockFile),H=["daemon start failed",`status=${JSON.stringify(f)}`,`lock=${R?JSON.stringify(R):"missing"}`,y("stderr",n,c),y("stdout",a,d)].filter(Boolean);throw new Error(H.join(`
2
- `))}await B(100)}const p=w(e.daemonLockFile),u=g(e.daemonStatusFile),C=[`daemon start timeout (${r}ms)`,`status=${JSON.stringify(u)}`,`lock=${p?JSON.stringify(p):"missing"}`,y("stderr",n,c),y("stdout",a,d)].filter(Boolean);throw new Error(C.join(`
3
- `))}readDefinitionCliPath(e,t){const r=t.service_id||S(e,this.platform),i=s=>{try{return l.readFileSync(s,"utf8")}catch{return null}};if(this.adapter.kind==="launchd"){const s=i(t.definition_path||Z(r,this.homeDir));return s?Q(s)[1]??null:null}if(this.adapter.kind==="systemd-user"){const s=i(t.definition_path||Y(r,this.homeDir));return s?K(s)[1]??null:null}if(this.adapter.kind==="task-scheduler"){const s=i($(r,this.homeDir));return s?nt(s):null}return null}observeLegacyState(e,t){const r=this.resolveServiceRuntimeDir(e),i=t?.service_id||S(e,this.platform);return{platform:this.platform,record:t,expectedLauncherPath:r?z(r):null,launcherStoreUsable:r?b(r):!1,nodePathExists:!!t?.node_path&&l.existsSync(t.node_path),cliPathExists:!!t?.cli_path&&l.existsSync(t.cli_path),definitionCliPath:t?this.readDefinitionCliPath(e,t):null,upgradePending:l.existsSync(o.join(e,"data","upgrade-pending.json")),windowsWrapperTemplate:this.platform==="win32"?at($(i,this.homeDir)):-1,windowsStartupShortcutExists:this.platform==="win32"&&l.existsSync(st(i,this.homeDir))}}async healLegacyState(e){const t=this.normalizeRootDir(e.rootDir),r=this.createStore(t),i=await r.load(),s=ct(this.observeLegacyState(t,i));if(!s.heal||!i)return{healed:!1,reasons:[]};const a=n=>{ot(t,n),console.log(`\u9057\u7559\u72B6\u6001\u81EA\u6108: ${n}`)};for(const n of s.reasons)a(`detected ${n}`);try{await this.stopServiceAndDaemon(t,i),a("stopped the legacy service and daemon")}catch(n){a(`stop failed (continuing): ${n instanceof Error?n.message:n}`)}try{await this.adapter.uninstall(this.toUninstallParams(i)),a("removed the legacy service definition")}catch(n){a(`uninstall failed (continuing): ${n instanceof Error?n.message:n}`)}return await r.clear(),a("cleared the install record; rebuilding via install"),{healed:!0,reasons:s.reasons,configDir:i.config_dir||void 0}}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=P(t),i=this.captureDaemonBaseline(r),s=this.buildDescriptor(t,e.configDir),a=await this.resolveServiceLogPaths(t),n=v(a.stdoutPath),d=v(a.stderrPath),c=await this.adapter.isServiceLoaded({serviceID:s.service_id,uid:this.uid,homeDir:this.homeDir,runCommand:m}),h=await this.adapter.install({...this.toAdapterPayload(s,t),stdoutPath:a.stdoutPath,stderrPath:a.stderrPath,homeDir:this.homeDir,uid:this.uid,runCommand:m}),p={...s,definition_path:h?.definitionPath??"",installed_at:Date.now(),updated_at:Date.now()};return await this.createStore(t).save(p),c?await this.adapter.restart(this.toRestartParams(p)):await this.adapter.start(this.toStartParams(p)),await this.waitForDaemonStarted(r,{baseline:i,requireNewDaemon:!0,stdoutLogFile:a.stdoutPath,stderrLogFile:a.stderrPath,stdoutLogCursor:n,stderrLogCursor:d}),this.status({rootDir:t})}async start(e){const t=this.normalizeRootDir(e.rootDir);await this.cleanupConflictingServices(t);const r=P(t),i=this.captureDaemonBaseline(r),s=i.status,a=v(r.stdoutLogFile),n=v(r.stderrLogFile),c=await this.createStore(t).load();if(s.running&&c&&this.isDescriptorCurrent(t,c)){if(s.state==="starting"){const u=i.lock;if(u!==null&&u.pid===s.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&&s.instance_id===u.instance_id&&k(u)){const f=await this.resolveServiceLogPaths(t);return await this.waitForDaemonStarted(r,{baseline:i,stdoutLogFile:f.stdoutPath,stderrLogFile:f.stderrPath,stdoutLogCursor:a,stderrLogCursor:n}),this.status({rootDir:t})}}if(this.isDaemonReady(r,i,!1))return this.status({rootDir:t})}const h=c?this.isDescriptorCurrent(t,c)&&s.running?c:await this.refreshDescriptor(t,c):await this.resolveActiveDescriptor(t),p=await this.resolveServiceLogPaths(t);try{s.running?await this.adapter.restart(this.toRestartParams(h)):await this.adapter.start(this.toStartParams(h))}catch(u){if(u instanceof Error&&u.message.includes("plist missing"))await this.adapter.install({...this.toAdapterPayload(h,t),stdoutPath:p.stdoutPath,stderrPath:p.stderrPath,homeDir:this.homeDir,uid:this.uid,runCommand:m}),await this.adapter.start(this.toStartParams(h));else throw u}return await this.waitForDaemonStarted(r,{baseline:i,requireNewDaemon:!0,stdoutLogFile:p.stdoutPath,stderrLogFile:p.stderrPath,stdoutLogCursor:a,stderrLogCursor:n}),this.status({rootDir:t})}async stopServiceAndDaemon(e,t){const r=P(e),i=g(r.daemonStatusFile);await this.adapter.stop(this.toStopParams(t)),!(!i.running||!i.pid||await this.waitForExit(i.pid,{timeoutMs:5e3}))&&this.canForceTerminateDaemon(r,i.pid)&&(await this.terminateTree(i.pid,{platform:this.platform,runCommandImpl:m}),await this.waitForExit(i.pid,{timeoutMs:5e3}))}async stop(e){const t=this.normalizeRootDir(e.rootDir),{descriptor:r}=await this.loadDescriptor(t);return await this.stopServiceAndDaemon(t,r),this.status({rootDir:t})}async restart(e){const t=this.normalizeRootDir(e.rootDir);await this.cleanupConflictingServices(t);const r=await this.resolveActiveDescriptor(t),i=P(t),s=this.captureDaemonBaseline(i),a=await this.resolveServiceLogPaths(t),n=v(a.stdoutPath),d=v(a.stderrPath);try{await this.adapter.restart(this.toRestartParams(r))}catch(c){if(c instanceof Error&&c.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 c}return await this.waitForDaemonStarted(i,{baseline:s,requireNewDaemon:!0,stdoutLogFile:a.stdoutPath,stderrLogFile:a.stderrPath,stdoutLogCursor:n,stderrLogCursor:d}),this.status({rootDir:t})}async uninstall(e){const t=this.normalizeRootDir(e.rootDir),r=this.createStore(t),i=await r.load();return i&&(await this.adapter.uninstall(this.toUninstallParams(i)),await r.clear()),this.status({rootDir:t})}async status(e){const t=this.normalizeRootDir(e.rootDir),i=await this.createStore(t).load(),s=P(t),a=g(s.daemonStatusFile);return i?{installed:!0,install_state:this.isDescriptorCurrent(t,i)?"current":"stale",service_kind:this.adapter.kind,service_id:i.service_id,definition_path:i.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{jt as ServiceManager};
1
+ import{createHash as R}from"node:crypto";import l from"node:fs";import B from"node:os";import n from"node:path";import f from"node:process";import{mkdir as y}from"node:fs/promises";import{setTimeout as N}from"node:timers/promises";import{resolveRuntimePaths as w}from"../core/config/paths.js";import{inspectDaemonState as S}from"../runtime/service-state.js";import{isProcessRunning as M,runCommand as m,terminateProcessTree as W,waitForProcessExit as q}from"./process-control.js";import{isLockHolderIdentityVerified as C,readDaemonLock as _}from"../runtime/daemon-lock.js";import{ServiceInstallStore as O}from"./service-install-store.js";import{getPlatformServiceAdapter as U}from"./platform-adapter.js";import{captureLogCursor as D,formatLogDelta as L}from"./log-diagnostics.js";import{LAUNCHER_FILE as j,bootstrapVersionFromPackage as V,compareVersions as z,ensureLauncher as G,launcherPath as A,packagedLauncherPath as J,readLauncherTemplateVersion as X,readPackageVersionAt as E,readPointer as I,switchCurrent as K,versionEntry as $}from"../core/upgrade/version-store.js";import{buildServiceID as P,parseExecStartWords as Q,parseProgramArgumentsFromPlistXML as Y,resolveLinuxUserUnitPath as Z,resolveMacOSLaunchAgentPath as tt,resolveWindowsServiceRuntimeDir as et,resolveServiceInstallRecordPath as it,resolveServiceStderrPath as rt,resolveServiceStdoutPath as st}from"./service-paths.js";import{resolveWindowsStartupShortcutPath as at,resolveWindowsWrapperPath as b}from"./platform-adapter.js";import{readInstalledWrapperTemplate as ot}from"./windows-wrapper-migration.js";import{appendLegacyHealLog as nt,isLauncherStoreUsable as H,parseWrapperCliPath as ct,planLegacyStateHeal as dt}from"./legacy-state-heal.js";function lt(F){try{return l.realpathSync(F)}catch{return F}}class jt{adapter;platform;homeDir;uid;nodePath;cliPath;waitForExit;terminateTree;constructor(e){this.platform=e.platform??f.platform,this.homeDir=e.homeDir??B.homedir(),this.uid=e.uid??f.getuid?.()??0,this.nodePath=e.nodePath??f.execPath,this.cliPath=lt(e.cliPath),this.adapter=U(this.platform),this.waitForExit=e.processControl?.waitForExit??q,this.terminateTree=e.processControl?.terminateTree??W}normalizeRootDir(e){return n.resolve(String(e??"").trim())}resolveLocalServiceBaseDir(){if(this.platform==="darwin")return n.join(this.homeDir,"Library","Application Support","grix-connector");if(this.platform==="win32"){const e=String(f.env.LOCALAPPDATA??"").trim();return n.join(e&&n.isAbsolute(e)?e:n.join(this.homeDir,"AppData","Local"),"grix-connector")}return null}resolveServiceRuntimeDir(e){const t=this.resolveLocalServiceBaseDir();return t?this.platform==="win32"?et(this.normalizeRootDir(e),f.env,this.homeDir):n.join(t,"runtime"):null}resolveServiceCliPath(e){const t=this.resolveServiceRuntimeDir(e);return t?A(t):this.cliPath}resolveSourcePackageDir(){return n.dirname(n.dirname(this.cliPath))}async adoptLauncher(e){const t=this.normalizeRootDir(e),i=this.resolveServiceRuntimeDir(t);if(!i)return!1;const r=await this.createStore(t).load();return!r||r.cli_path===this.resolveServiceCliPath(t)&&H(i)?!1:(await this.refreshDescriptor(t,r),!0)}prepareServiceRuntime(e){const t=this.resolveServiceRuntimeDir(e);if(!t)return;const i=this.resolveSourcePackageDir();l.mkdirSync(t,{recursive:!0});const r=E(i);if(!r)throw new Error(`Cannot read package version at ${i} (entry ${this.cliPath}); refusing to install the service`);if(!G(t,i))throw new Error(`Package at ${i} does not ship ${j}; refusing to point the service at it`);const s=I(t,"current");if((!s||!l.existsSync($(t,s))||z(r,s)>0)&&(V(t,i,r),K(t,r)),this.platform==="win32"||this.platform==="darwin"){const a=n.join(i,"scripts","upgrade-guardian.mjs");if(l.existsSync(a)){const o=n.join(this.normalizeRootDir(e),"bin"),d=n.join(o,"upgrade-guardian.mjs"),c=n.join(o,"upgrade-guardian.fallback.mjs"),h=`${d}.tmp-${f.pid}`;l.mkdirSync(o,{recursive:!0});try{l.copyFileSync(a,h),l.renameSync(h,d),l.copyFileSync(a,c)}finally{l.rmSync(h,{force:!0})}}if(this.platform==="darwin"){const o=n.join(i,"scripts","upgrade-guardian.sh");if(l.existsSync(o)){const d=n.join(this.normalizeRootDir(e),"bin","upgrade-guardian.sh"),c=`${d}.tmp-${f.pid}`;try{l.copyFileSync(o,c),l.chmodSync(c,493),l.renameSync(c,d)}finally{l.rmSync(c,{force:!0})}}}}}buildDescriptor(e,t,i=""){const r=this.normalizeRootDir(e),s=this.resolveServiceCliPath(r);return{schema_version:1,platform:this.platform,service_id:P(r,this.platform),node_path:this.nodePath,cli_path:s,cli_hash:this.computeServiceHash(s)??"",definition_path:i,config_dir:t??"",installed_at:0,updated_at:0}}createStore(e){return new O(it(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 i=n.join(this.platform==="darwin"?n.join(this.homeDir,"Library","Logs","grix-connector"):n.join(t,"logs"),P(this.normalizeRootDir(e),this.platform));return await y(i,{recursive:!0}),{stdoutPath:n.join(i,"daemon.out.log"),stderrPath:n.join(i,"daemon.err.log")}}return await y(n.join(e,"service"),{recursive:!0}),{stdoutPath:st(e),stderrPath:rt(e)}}buildServiceEnvironmentPath(){const e=[n.dirname(this.nodePath),...this.platform==="darwin"?["/opt/homebrew/bin"]:[],"/usr/local/bin","/usr/bin","/bin","/usr/sbin","/sbin"],t=String(f.env.PATH??"").split(n.delimiter).filter(s=>s&&n.isAbsolute(s)).filter(s=>!s.includes("/node_modules/.bin")).filter(s=>!s.includes("/node-gyp-bin")),i=[],r=new Set;for(const s of[...e,...t]){if(r.has(s))continue;if(r.add(s),[...i,s].join(n.delimiter).length>2048)break;i.push(s)}return i.join(n.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,t={}){return{serviceID:e.service_id,definitionPath:e.definition_path,uid:this.uid,homeDir:this.homeDir,runCommand:m,reloadDefinition:t.reloadDefinition===!0}}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),i=await t.load();if(!i)throw new Error("\u540E\u53F0\u670D\u52A1\u8FD8\u6CA1\u6709\u5B89\u88C5\uFF0C\u8BF7\u5148\u6267\u884C grix-connector start\uFF08\u4F1A\u81EA\u52A8\u5B89\u88C5\u5E76\u542F\u52A8\u670D\u52A1\uFF09\u3002");return{store:t,descriptor:i}}isDescriptorCurrent(e,t){if(t.node_path!==this.nodePath||t.cli_path!==this.resolveServiceCliPath(e)||!t.cli_hash)return!1;const i=this.computeServiceHash(t.cli_path),r=this.computeServiceHash(this.cliPath);return i!==null&&r!==null&&i===t.cli_hash&&r===t.cli_hash}computeFileHash(e){try{const t=l.readFileSync(e);return R("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(),i=E(t)??"",r=n.basename(e)===j,s=r?n.dirname(e):null,a=r?e:J(t);if(!a)return null;const o=X(a);if(o<0)return null;let d=!0;if(s){const c=I(s,"current");d=!!c&&l.existsSync($(s,c))&&z(c,i)>=0}return d?R("sha256").update(`launcher:${o}`).update(`source:${i}`).digest("hex"):null}catch{return null}}computeRuntimeDirHash(e){try{const t=n.dirname(e),i=R("sha256"),r=a=>{const o=l.readdirSync(a,{withFileTypes:!0}).sort((d,c)=>d.name.localeCompare(c.name));for(const d of o){const c=n.join(a,d.name);i.update(n.relative(t,c)),d.isDirectory()?r(c):d.isFile()&&i.update(l.readFileSync(c))}};r(t);const s=n.join(n.dirname(t),"package.json");return l.existsSync(s)&&(i.update("package.json"),i.update(l.readFileSync(s))),i.digest("hex")}catch{return null}}async refreshDescriptor(e,t){const i=this.normalizeRootDir(e);await y(n.join(i,"service"),{recursive:!0}),this.prepareServiceRuntime(i);const r=this.resolveServiceCliPath(i),s={...t,platform:this.platform,service_id:t.service_id||P(i,this.platform),node_path:this.nodePath,cli_path:r,cli_hash:this.computeServiceHash(r)??"",config_dir:t.config_dir},a=await this.resolveServiceLogPaths(i),o=await this.adapter.install({...this.toAdapterPayload(s,i),stdoutPath:a.stdoutPath,stderrPath:a.stderrPath,homeDir:this.homeDir,uid:this.uid,runCommand:m}),d={...s,installed_at:t.installed_at,definition_path:o?.definitionPath||s.definition_path,updated_at:Date.now()};return await this.createStore(i).save(d),d}async resolveActiveDescriptor(e){return(await this.resolveActiveDescriptorState(e)).descriptor}async resolveActiveDescriptorState(e){const{descriptor:t}=await this.loadDescriptor(e);return this.isDescriptorCurrent(e,t)?{descriptor:t,definitionRewritten:!1}:{descriptor:await this.refreshDescriptor(e,t),definitionRewritten:!0}}async discoverConflictingServices(e){const t=this.normalizeRootDir(e),i=P(t,this.platform);return(await this.adapter.discoverServices({homeDir:this.homeDir,runCommand:m})).filter(s=>s.serviceID!==i)}async cleanupConflictingServices(e){const t=await this.discoverConflictingServices(e);if(t.length>0){const r=t.map(s=>s.serviceID).join(", ");console.log(`\u53D1\u73B0 ${t.length} \u4E2A\u6B8B\u7559\u670D\u52A1\uFF0C\u6B63\u5728\u68C0\u67E5: ${r}`)}let i=0;for(const r of t){if(await this.adapter.isServiceLoaded({serviceID:r.serviceID,uid:this.uid,homeDir:this.homeDir,runCommand:m})){console.log(`\u8DF3\u8FC7\u6B63\u5728\u8FD0\u884C\u7684\u670D\u52A1: ${r.serviceID}`);continue}try{await this.adapter.stop({serviceID:r.serviceID,uid:this.uid,homeDir:this.homeDir,runCommand:m})}catch{}try{await this.adapter.uninstall({serviceID:r.serviceID,definitionPath:r.definitionPath,uid:this.uid,homeDir:this.homeDir,runCommand:m}),i++}catch{}}return i}captureDaemonBaseline(e){return{status:S(e.daemonStatusFile),lock:_(e.daemonLockFile)}}isDaemonReady(e,t,i){const r=S(e.daemonStatusFile),s=_(e.daemonLockFile);if(!r.running||r.state!=="running"||r.pid<=0||!s||s.pid!==r.pid||!M(s.pid)||typeof s.root_dir!="string"||n.resolve(s.root_dir)!==n.resolve(e.rootDir)||typeof s.instance_id!="string"||s.instance_id.length===0||r.instance_id!==s.instance_id)return!1;const a=r.pid!==t.status.pid||r.state!==t.status.state||r.updated_at!==t.status.updated_at||r.instance_id!==t.status.instance_id,o=!t.lock||s.pid!==t.lock.pid||s.acquired_at!==t.lock.acquired_at||s.instance_id!==t.lock.instance_id,d=a&&o,c=t.status.state==="starting"&&t.status.instance_id===s.instance_id&&t.lock?.pid===s.pid&&t.lock.instance_id===s.instance_id&&r.instance_id===s.instance_id&&r.updated_at!==t.status.updated_at,h=t.lock?.pid===s.pid&&t.lock.instance_id===s.instance_id&&t.lock.acquired_at===s.acquired_at&&(t.status.pid!==s.pid||t.status.instance_id!==s.instance_id||t.status.state!=="running")&&r.pid===s.pid&&r.instance_id===s.instance_id&&r.state==="running"&&r.updated_at!==t.status.updated_at;return!C(s)&&!d&&!c&&!h?!1:i?d||c||h:!0}canForceTerminateDaemon(e,t){const i=_(e.daemonLockFile);return t>0&&i!==null&&i.pid===t&&typeof i.root_dir=="string"&&n.resolve(i.root_dir)===n.resolve(e.rootDir)&&C(i)}defaultDaemonStartTimeoutMs(){return this.platform==="darwin"?5e4:2e4}async waitForDaemonStarted(e,t={}){const i=t.timeoutMs??this.defaultDaemonStartTimeoutMs(),r=t.baseline??this.captureDaemonBaseline(e),s=t.requireNewDaemon??!1,a=t.stdoutLogFile??e.stdoutLogFile,o=t.stderrLogFile??e.stderrLogFile,d=t.stdoutLogCursor??D(a),c=t.stderrLogCursor??D(o),h=Date.now();for(;Date.now()-h<i;){if(this.isDaemonReady(e,r,s))return;const g=S(e.daemonStatusFile);if(g.state==="failed"&&(r.status.state!=="failed"||g.pid!==r.status.pid||g.updated_at!==r.status.updated_at||g.instance_id!==r.status.instance_id)){const x=_(e.daemonLockFile),T=["daemon start failed",`status=${JSON.stringify(g)}`,`lock=${x?JSON.stringify(x):"missing"}`,L("stderr",o,c),L("stdout",a,d)].filter(Boolean);throw new Error(T.join(`
2
+ `))}await N(100)}const p=_(e.daemonLockFile),v=S(e.daemonStatusFile),u=[`daemon start timeout (${i}ms)`,`status=${JSON.stringify(v)}`,`lock=${p?JSON.stringify(p):"missing"}`,L("stderr",o,c),L("stdout",a,d)].filter(Boolean);throw new Error(u.join(`
3
+ `))}readDefinitionCliPath(e,t){const i=t.service_id||P(e,this.platform),r=s=>{try{return l.readFileSync(s,"utf8")}catch{return null}};if(this.adapter.kind==="launchd"){const s=r(t.definition_path||tt(i,this.homeDir));return s?Y(s)[1]??null:null}if(this.adapter.kind==="systemd-user"){const s=r(t.definition_path||Z(i,this.homeDir));return s?Q(s)[1]??null:null}if(this.adapter.kind==="task-scheduler"){const s=r(b(i,this.homeDir));return s?ct(s):null}return null}observeLegacyState(e,t){const i=this.resolveServiceRuntimeDir(e),r=t?.service_id||P(e,this.platform);return{platform:this.platform,record:t,expectedLauncherPath:i?A(i):null,launcherStoreUsable:i?H(i):!1,nodePathExists:!!t?.node_path&&l.existsSync(t.node_path),cliPathExists:!!t?.cli_path&&l.existsSync(t.cli_path),definitionCliPath:t?this.readDefinitionCliPath(e,t):null,upgradePending:l.existsSync(n.join(e,"data","upgrade-pending.json")),windowsWrapperTemplate:this.platform==="win32"?ot(b(r,this.homeDir)):-1,windowsStartupShortcutExists:this.platform==="win32"&&l.existsSync(at(r,this.homeDir))}}async healLegacyState(e){const t=this.normalizeRootDir(e.rootDir),i=this.createStore(t),r=await i.load(),s=dt(this.observeLegacyState(t,r));if(!s.heal||!r)return{healed:!1,reasons:[]};const a=o=>{nt(t,o),console.log(`\u9057\u7559\u72B6\u6001\u81EA\u6108: ${o}`)};for(const o of s.reasons)a(`detected ${o}`);try{await this.stopServiceAndDaemon(t,r),a("stopped the legacy service and daemon")}catch(o){a(`stop failed (continuing): ${o instanceof Error?o.message:o}`)}try{await this.adapter.uninstall(this.toUninstallParams(r)),a("removed the legacy service definition")}catch(o){a(`uninstall failed (continuing): ${o instanceof Error?o.message:o}`)}return await i.clear(),a("cleared the install record; rebuilding via install"),{healed:!0,reasons:s.reasons,configDir:r.config_dir||void 0}}async install(e){const t=this.normalizeRootDir(e.rootDir);await this.cleanupConflictingServices(t),await y(n.join(t,"service"),{recursive:!0}),this.prepareServiceRuntime(t);const i=w(t),r=this.captureDaemonBaseline(i),s=this.buildDescriptor(t,e.configDir),a=await this.resolveServiceLogPaths(t),o=D(a.stdoutPath),d=D(a.stderrPath),c=await this.adapter.isServiceLoaded({serviceID:s.service_id,uid:this.uid,homeDir:this.homeDir,runCommand:m}),h=await this.adapter.install({...this.toAdapterPayload(s,t),stdoutPath:a.stdoutPath,stderrPath:a.stderrPath,homeDir:this.homeDir,uid:this.uid,runCommand:m}),p={...s,definition_path:h?.definitionPath??"",installed_at:Date.now(),updated_at:Date.now()};return await this.createStore(t).save(p),c?await this.adapter.restart(this.toRestartParams(p,{reloadDefinition:!0})):await this.adapter.start(this.toStartParams(p)),await this.waitForDaemonStarted(i,{baseline:r,requireNewDaemon:!0,stdoutLogFile:a.stdoutPath,stderrLogFile:a.stderrPath,stdoutLogCursor:o,stderrLogCursor:d}),this.status({rootDir:t})}async start(e){const t=this.normalizeRootDir(e.rootDir);await this.cleanupConflictingServices(t);const i=w(t),r=this.captureDaemonBaseline(i),s=r.status,a=D(i.stdoutLogFile),o=D(i.stderrLogFile),c=await this.createStore(t).load();if(s.running&&c&&this.isDescriptorCurrent(t,c)){if(s.state==="starting"){const u=r.lock;if(u!==null&&u.pid===s.pid&&typeof u.root_dir=="string"&&n.resolve(u.root_dir)===n.resolve(i.rootDir)&&typeof u.instance_id=="string"&&u.instance_id.length>0&&s.instance_id===u.instance_id&&C(u)){const k=await this.resolveServiceLogPaths(t);return await this.waitForDaemonStarted(i,{baseline:r,stdoutLogFile:k.stdoutPath,stderrLogFile:k.stderrPath,stdoutLogCursor:a,stderrLogCursor:o}),this.status({rootDir:t})}}if(this.isDaemonReady(i,r,!1))return this.status({rootDir:t})}let h,p=!1;if(c)this.isDescriptorCurrent(t,c)&&s.running?h=c:(h=await this.refreshDescriptor(t,c),p=!0);else{const u=await this.resolveActiveDescriptorState(t);h=u.descriptor,p=u.definitionRewritten}const v=await this.resolveServiceLogPaths(t);try{s.running?await this.adapter.restart(this.toRestartParams(h,{reloadDefinition:p})):await this.adapter.start(this.toStartParams(h))}catch(u){if(u instanceof Error&&u.message.includes("plist missing"))await this.adapter.install({...this.toAdapterPayload(h,t),stdoutPath:v.stdoutPath,stderrPath:v.stderrPath,homeDir:this.homeDir,uid:this.uid,runCommand:m}),await this.adapter.start(this.toStartParams(h));else throw u}return await this.waitForDaemonStarted(i,{baseline:r,requireNewDaemon:!0,stdoutLogFile:v.stdoutPath,stderrLogFile:v.stderrPath,stdoutLogCursor:a,stderrLogCursor:o}),this.status({rootDir:t})}async stopServiceAndDaemon(e,t){const i=w(e),r=S(i.daemonStatusFile);await this.adapter.stop(this.toStopParams(t)),!(!r.running||!r.pid||await this.waitForExit(r.pid,{timeoutMs:5e3}))&&this.canForceTerminateDaemon(i,r.pid)&&(await this.terminateTree(r.pid,{platform:this.platform,runCommandImpl:m}),await this.waitForExit(r.pid,{timeoutMs:5e3}))}async stop(e){const t=this.normalizeRootDir(e.rootDir),{descriptor:i}=await this.loadDescriptor(t);return await this.stopServiceAndDaemon(t,i),this.status({rootDir:t})}async restart(e){const t=this.normalizeRootDir(e.rootDir);await this.cleanupConflictingServices(t);const{descriptor:i,definitionRewritten:r}=await this.resolveActiveDescriptorState(t),s=w(t),a=this.captureDaemonBaseline(s),o=await this.resolveServiceLogPaths(t),d=D(o.stdoutPath),c=D(o.stderrPath);try{await this.adapter.restart(this.toRestartParams(i,{reloadDefinition:r}))}catch(h){if(h instanceof Error&&h.message.includes("plist missing"))await this.adapter.install({...this.toAdapterPayload(i,t),stdoutPath:o.stdoutPath,stderrPath:o.stderrPath,homeDir:this.homeDir,uid:this.uid,runCommand:m}),await this.adapter.start(this.toStartParams(i));else throw h}return await this.waitForDaemonStarted(s,{baseline:a,requireNewDaemon:!0,stdoutLogFile:o.stdoutPath,stderrLogFile:o.stderrPath,stdoutLogCursor:d,stderrLogCursor:c}),this.status({rootDir:t})}async uninstall(e){const t=this.normalizeRootDir(e.rootDir),i=this.createStore(t),r=await i.load();return r&&(await this.adapter.uninstall(this.toUninstallParams(r)),await i.clear()),this.status({rootDir:t})}async status(e){const t=this.normalizeRootDir(e.rootDir),r=await this.createStore(t).load(),s=w(t),a=S(s.daemonStatusFile);return r?{installed:!0,install_state:this.isDescriptorCurrent(t,r)?"current":"stale",service_kind:this.adapter.kind,service_id:r.service_id,definition_path:r.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{jt as ServiceManager};
@@ -12406,7 +12406,7 @@ async function downloadPackage(params) {
12406
12406
  // src/openclaw/egg/persona-installer.ts
12407
12407
  import { join as join12 } from "node:path";
12408
12408
 
12409
- // node_modules/fflate/esm/index.mjs
12409
+ // ../../grix-connector/node_modules/fflate/esm/index.mjs
12410
12410
  import { createRequire } from "module";
12411
12411
  var require2 = createRequire("/");
12412
12412
  var _a;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grix-connector",
3
- "version": "4.9.1",
3
+ "version": "4.9.2-beta.2",
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",
@@ -15,15 +15,23 @@
15
15
  // 崩溃时间戳落在 `<store>/crashes.json`,launcher 自身被重拉也不会把计数归零。
16
16
  // 收到 SIGTERM/SIGINT/SIGHUP 时转发给子进程并随其退出(服务停止语义)。
17
17
  //
18
+ // 同一个 store 只允许一个 launcher:启动时抢 store 级单例锁,抢不到就退出,
19
+ // 避免两个 launcher 各自拉起一个 daemon、把对方的 daemon 记成崩溃、再用共享的
20
+ // crashes.json 叠加计数,几秒内把健康版本"三振出局"回滚掉。
21
+ //
18
22
  // 本文件由 daemon 在启动时按 LAUNCHER_TEMPLATE_VERSION 比对并原子重写,改动行为时必须 +1。
19
- export const LAUNCHER_TEMPLATE_VERSION = 2;
23
+ export const LAUNCHER_TEMPLATE_VERSION = 3;
20
24
 
21
25
  import { spawn } from 'node:child_process';
26
+ import { createHash } from 'node:crypto';
22
27
  import { existsSync, mkdirSync, readFileSync, realpathSync, renameSync, rmSync, writeFileSync, appendFileSync } from 'node:fs';
28
+ import { connect, createServer } from 'node:net';
23
29
  import { dirname, join } from 'node:path';
24
30
  import { fileURLToPath } from 'node:url';
25
31
 
26
32
  const RESTART_EXIT_CODE = 75;
33
+ const LOCK_RETRY_ATTEMPTS = 20;
34
+ const LOCK_RETRY_INTERVAL_MS = 250;
27
35
  const RAPID_CRASH_WINDOW_MS = 5 * 60 * 1000;
28
36
  const RAPID_CRASH_LIMIT = 3;
29
37
  const MAX_BACKOFF_MS = 30_000;
@@ -35,6 +43,79 @@ function resolveStoreDir() {
35
43
  return dirname(real);
36
44
  }
37
45
 
46
+ /**
47
+ * store 级单例锁的端点。Windows 用命名管道(进程消失即随之消失,不留残留);
48
+ * POSIX 用 store 里的 unix socket 文件——端口派生会和机器上无关服务撞车,
49
+ * 一旦撞上 launcher 就永远起不来,socket 路径没有这个风险。
50
+ */
51
+ function launcherLockEndpoint(store, platform = process.platform) {
52
+ if (platform === 'win32') {
53
+ const digest = createHash('sha256').update(store).digest('hex');
54
+ return `\\\\.\\pipe\\grix-launcher-${digest.slice(0, 24)}`;
55
+ }
56
+ return join(store, 'launcher.lock.sock');
57
+ }
58
+
59
+ function listenOnce(server, endpoint) {
60
+ return new Promise((resolve, reject) => {
61
+ const onError = (error) => {
62
+ if (error?.code === 'EADDRINUSE' || error?.code === 'EACCES') resolve(error.code);
63
+ else reject(error);
64
+ };
65
+ server.once('error', onError);
66
+ server.once('listening', () => {
67
+ server.removeListener('error', onError);
68
+ server.on('error', () => { /* 锁只需活到进程结束,后续错误无需处理 */ });
69
+ // 锁只用监听端点本身表态,探活连上来就断掉,别攒着不放。
70
+ server.on('connection', (socket) => socket.destroy());
71
+ resolve(null);
72
+ });
73
+ server.listen(endpoint);
74
+ });
75
+ }
76
+
77
+ /** unix socket 文件在 launcher 被强杀后会留下,连得上才说明真有 launcher 在跑。 */
78
+ function isEndpointAlive(endpoint) {
79
+ return new Promise((resolve) => {
80
+ let settled = false;
81
+ const socket = connect(endpoint);
82
+ const done = (alive) => {
83
+ if (settled) return;
84
+ settled = true;
85
+ try { socket.destroy(); } catch { /* already gone */ }
86
+ resolve(alive);
87
+ };
88
+ socket.once('connect', () => done(true));
89
+ socket.once('error', () => done(false));
90
+ setTimeout(() => done(false), 1000).unref?.();
91
+ });
92
+ }
93
+
94
+ /**
95
+ * 抢 store 级单例锁。拿到返回持锁的 server(进程退出时由系统释放),
96
+ * 已被别的 launcher 持有返回 null。
97
+ *
98
+ * 锁机制本身出错(既不是占用也不是权限)时向上抛:调用方按放行处理——
99
+ * launcher 起不来比偶尔多一个 launcher 严重得多。
100
+ */
101
+ export async function acquireLauncherStoreLock(store, deps = {}) {
102
+ const endpoint = deps.lockEndpoint ?? launcherLockEndpoint(store, deps.platform);
103
+ const create = deps.createServer ?? createServer;
104
+ const isSocketPath = typeof endpoint === 'string' && !endpoint.startsWith('\\\\');
105
+ let server = create();
106
+ let blocked = await listenOnce(server, endpoint);
107
+ if (blocked && isSocketPath) {
108
+ if (await isEndpointAlive(endpoint)) return null;
109
+ // 空壳 socket:上一轮 launcher 被强杀留下的,删掉再抢一次。
110
+ try { rmSync(endpoint, { force: true }); } catch { return null; }
111
+ server = create();
112
+ blocked = await listenOnce(server, endpoint);
113
+ }
114
+ if (blocked) return null;
115
+ server.unref?.();
116
+ return server;
117
+ }
118
+
38
119
  function readPointer(store, name) {
39
120
  try {
40
121
  const value = readFileSync(join(store, name), 'utf8').trim();
@@ -183,6 +264,27 @@ async function main() {
183
264
  const store = resolveStoreDir();
184
265
  const args = process.argv.slice(2);
185
266
  mkdirSync(join(store, 'health'), { recursive: true });
267
+
268
+ // 升级交接时新旧 launcher 会短暂重叠,退几拍再判定,别把交接当成重复实例。
269
+ let storeLock = null;
270
+ let lockError = null;
271
+ for (let attempt = 0; attempt < LOCK_RETRY_ATTEMPTS && !storeLock; attempt++) {
272
+ try {
273
+ storeLock = await acquireLauncherStoreLock(store);
274
+ } catch (error) {
275
+ lockError = error;
276
+ break;
277
+ }
278
+ if (!storeLock) await sleep(LOCK_RETRY_INTERVAL_MS);
279
+ }
280
+ if (lockError) {
281
+ log(store, `store lock unavailable (${lockError instanceof Error ? lockError.message : lockError}); continuing without it`);
282
+ } else if (!storeLock) {
283
+ // 退出码 0:supervisor 见 0 不会当成崩溃,也不会去动 crashes.json。
284
+ log(store, 'another launcher already holds this store; exiting without starting a daemon');
285
+ process.exit(0);
286
+ }
287
+
186
288
  let backoffMs = 1000;
187
289
  log(store, `started node=${process.version} store=${store} template=${LAUNCHER_TEMPLATE_VERSION}`);
188
290
 
@@ -1 +0,0 @@
1
- import c from"node:http";import{randomUUID as d}from"node:crypto";import{log as o}from"../../core/log/index.js";function l(t){t.writeHead(401,{"content-type":"application/json"}),t.end(JSON.stringify({error:"unauthorized"}))}function u(t){t.writeHead(404,{"content-type":"application/json"}),t.end(JSON.stringify({error:"not_found"}))}function h(t,e){t.writeHead(400,{"content-type":"application/json"}),t.end(JSON.stringify({error:e}))}function p(t,e={ok:!0}){t.writeHead(200,{"content-type":"application/json"}),t.end(JSON.stringify(e))}async function v(t){const e=[];for await(const r of t)e.push(r);const n=Buffer.concat(e).toString("utf8").trim();return n?JSON.parse(n):{}}function k(t){const e=(t.headers.authorization??"").trim();return e.toLowerCase().startsWith("bearer ")?e.slice(7).trim():""}class w{host="127.0.0.1";port=0;token;callbacks;server=null;address=null;constructor(e){this.token=d(),this.callbacks=e}getToken(){return this.token}getURL(){return this.address?`http://${this.address.address}:${this.address.port}`:""}async start(){this.server||(this.server=c.createServer(async(e,n)=>{try{await this.handleRequest(e,n)}catch(r){h(n,r instanceof Error?r.message:String(r))}}),await new Promise((e,n)=>{this.server.once("error",n),this.server.listen(this.port,this.host,()=>{this.server.off("error",n),e()})}),this.address=this.server.address(),o.info("claude-bridge",`Bridge server listening on ${this.getURL()}`))}async stop(){if(!this.server)return;const e=this.server;this.server=null,this.address=null,e.closeIdleConnections?.(),e.closeAllConnections?.(),await new Promise((n,r)=>{e.close(s=>s?r(s):n())})}async handleRequest(e,n){if(k(e)!==this.token){l(n);return}if(e.method!=="POST"){n.writeHead(405,{"content-type":"application/json"}),n.end(JSON.stringify({error:"method_not_allowed"}));return}const r=new URL(e.url,"http://localhost").pathname,s=await v(e),i=f.get(r);if(!i){u(n);return}const a=await i(this.callbacks,s);p(n,a??{ok:!0})}}const f=new Map([["/v1/worker/register",async(t,e)=>(o.info("claude-bridge",`Worker registered: ${e.worker_id} (pid=${e.pid})`),t.onRegisterWorker(e))],["/v1/worker/status",async(t,e)=>(o.info("claude-bridge",`Worker status: ${e.status}`),t.onStatusUpdate(e))],["/v1/worker/send-text",async(t,e)=>t.onSendText(e)],["/v1/worker/send-stream-chunk",async(t,e)=>t.onSendStreamChunk(e)],["/v1/worker/send-media",async(t,e)=>t.onSendMedia(e)],["/v1/worker/delete-message",async(t,e)=>t.onDeleteMessage(e)],["/v1/worker/ack-event",async(t,e)=>t.onAckEvent(e)],["/v1/worker/event-result",async(t,e)=>t.onSendEventResult(e)],["/v1/worker/event-stop-ack",async(t,e)=>t.onSendEventStopAck(e)],["/v1/worker/event-stop-result",async(t,e)=>t.onSendEventStopResult(e)],["/v1/worker/session-composing",async(t,e)=>t.onSetSessionComposing(e)],["/v1/worker/agent-invoke",async(t,e)=>t.onAgentInvoke(e)],["/v1/worker/local-action-result",async(t,e)=>t.onLocalActionResult(e)]]);export{w as ClaudeBridgeServer};
@@ -1 +0,0 @@
1
- import{randomUUID as x}from"node:crypto";import{CallToolRequestSchema as S,ListToolsRequestSchema as w}from"@modelcontextprotocol/sdk/types.js";import{log as f}from"../../core/log/index.js";import{toolCallToInvoke as k}from"../../core/mcp/tools.js";const E=new Set(["contact_search","session_search","message_history","message_search","group_create","group_detail_read","group_leave_self","group_member_add","group_member_remove","group_member_role_update","group_all_members_muted_update","group_member_speaking_update","group_dissolve","send_msg","delete_msg","agent_api_create","agent_category_list","agent_category_create","agent_category_update","agent_category_assign","agent_api_key_rotate"]),y=3e4,I=[{name:"reply",description:"Send a visible message back to the chat for this grix-claude event.",inputSchema:{type:"object",properties:{text:{type:"string",description:"The visible reply text to send."},chat_id:{type:"string",description:"The target chat/session id from the <channel> tag."},event_id:{type:"string",description:"The Aibot event_id from the <channel> tag."},reply_to:{type:"string",description:"Optional message_id to quote instead of the inbound trigger message."},files:{type:"array",items:{type:"string"},description:"Optional absolute local file paths. Each file is uploaded through Agent API OSS presign before sending."},final:{type:"boolean",description:"Whether this is the final reply for the event. Defaults to false \u2014 the event stays open while Claude continues working, and auto-completes after inactivity. Set true only when this is definitively the last message for the event."}},required:["chat_id","event_id"]}},{name:"complete",description:"Finish an event without sending a visible reply so the backend does not time out.",inputSchema:{type:"object",properties:{event_id:{type:"string",description:"The Aibot event_id from the <channel> tag."},status:{type:"string",enum:["responded","canceled","failed"]},code:{type:"string"},msg:{type:"string"}},required:["event_id","status"]}},{name:"delete_message",description:"Delete a previously sent message in the same grix-claude chat.",inputSchema:{type:"object",properties:{chat_id:{type:"string"},message_id:{type:"string"}},required:["chat_id","message_id"]}},{name:"status",description:"Show grix-claude runtime status, upstream access state, bridge health, and startup hints.",inputSchema:{type:"object",properties:{}}},{name:"send",description:"Send a message to a chat session proactively, without requiring an inbound event. Use for notifications or scheduled reports.",inputSchema:{type:"object",properties:{chat_id:{type:"string",description:"The target chat/session id."},text:{type:"string",description:"The message text to send."}},required:["chat_id","text"]}},{name:"access_pair",description:"Forward a sender pairing approval code to upstream access control.",inputSchema:{type:"object",properties:{code:{type:"string"}},required:["code"]}},{name:"access_deny",description:"Forward a sender pairing denial code to upstream access control.",inputSchema:{type:"object",properties:{code:{type:"string"}},required:["code"]}},{name:"allow_sender",description:"Ask upstream access control to allow a sender_id.",inputSchema:{type:"object",properties:{sender_id:{type:"string"}},required:["sender_id"]}},{name:"remove_sender",description:"Ask upstream access control to remove a sender_id.",inputSchema:{type:"object",properties:{sender_id:{type:"string"}},required:["sender_id"]}},{name:"access_policy",description:"Ask upstream access control to update the sender access policy.",inputSchema:{type:"object",properties:{policy:{type:"string",enum:["allowlist","open","disabled"]}},required:["policy"]}},{name:"grix_query",description:"Search contacts, sessions, message history, or messages by keyword in the Grix/AIBot platform.",inputSchema:{type:"object",properties:{action:{type:"string",enum:["contact_search","session_search","message_history","message_search"]},keyword:{type:"string"},id:{type:"string"},sessionId:{type:"string"},limit:{type:"number"},offset:{type:"number"},beforeId:{type:"string"}},required:["action"]}},{name:"grix_group",description:"Manage groups in the Grix/AIBot platform: create, get details, leave, dissolve, manage members and permissions.",inputSchema:{type:"object",properties:{action:{type:"string",enum:["create","detail","leave","add_members","remove_members","update_member_role","update_all_members_muted","update_member_speaking","dissolve"]},sessionId:{type:"string"},name:{type:"string"},memberIds:{type:"array",items:{type:"string"}},memberTypes:{type:"array",items:{type:"integer",enum:[1,2]}},memberId:{type:"string"},role:{type:"integer",enum:[1,2]},memberType:{type:"integer",description:"Member type (for update_member_role / update_member_speaking)."},allMembersMuted:{type:"boolean"},isSpeakMuted:{type:"boolean"},canSpeakWhenAllMuted:{type:"boolean",description:"Allow speaking when all muted (for update_member_speaking)."}},required:["action"]}},{name:"grix_message_send",description:"Send a message to a session in the Grix/AIBot platform.",inputSchema:{type:"object",properties:{sessionId:{type:"string"},content:{type:"string"},msgType:{type:"number"},quotedMessageId:{type:"string"},threadId:{type:"string"}},required:["sessionId","content"]}},{name:"grix_message_unsend",description:"Recall/unsend a message in the Grix/AIBot platform.",inputSchema:{type:"object",properties:{sessionId:{type:"string"},msgId:{type:"string"}},required:["sessionId","msgId"]}},{name:"grix_admin",description:"Agent and category management in the Grix/AIBot platform: create agents, manage categories, rotate API keys.",inputSchema:{type:"object",properties:{action:{type:"string",enum:["create_agent","list_categories","create_category","update_category","assign_category","rotate_api_key"]},agentId:{type:"string"},agentName:{type:"string"},introduction:{type:"string"},isMain:{type:"boolean"},categoryId:{type:"string"},name:{type:"string"},parentId:{type:"string"},sortOrder:{type:"number"}},required:["action"]}}];function q(r,e){r.setRequestHandler(w,async()=>({tools:I})),r.setRequestHandler(S,async i=>{const{name:n,arguments:t}=i.params,s=t??{};try{switch(n){case"reply":return await A(s,e);case"complete":return await $(s,e);case"delete_message":return await T(s,e);case"status":return j(e);case"send":return await M(s,e);case"access_pair":case"access_deny":case"allow_sender":case"remove_sender":case"access_policy":return await R(n,s,e);case"grix_query":case"grix_group":case"grix_message_send":case"grix_message_unsend":case"grix_admin":return await O(n,s,e);default:return{content:[{type:"text",text:`Unknown tool: ${n}`}],isError:!0}}}catch(a){return f.error("claude-tools",`Tool ${n} error: ${a}`),{content:[{type:"text",text:`Error: ${a instanceof Error?a.message:String(a)}`}],isError:!0}}})}async function A(r,e){const i=e.getActiveEvent();if(!i)return{content:[{type:"text",text:"No active event to reply to"}],isError:!0};const n=String(r.text??""),t=String(r.chat_id??""),s=String(r.event_id??i.eventId),a=r.reply_to,d=r.files,_=r.final===!0;if(!t||!s)return{content:[{type:"text",text:"reply requires chat_id and event_id"}],isError:!0};if(!n.trim()&&(!d||d.length===0))return{content:[{type:"text",text:"reply requires at least one of text or files"}],isError:!0};const{text:g,quotedMessageId:v}=e.resolveQuotedMessageId(a,n),b=[];let m=0;const h=`reply_${s}_${Date.now()}`;try{if(g){const p=e.splitText(g);for(let o=0;o<p.length;o++){if(!e.isEventActive(s))return c("ignored: event no longer active");m++,e.bridge.sendStreamChunk(s,t,p[o],++i.chunkSeq,!1,h)}}if(d&&d.length>0)for(const p of d){if(!e.isEventActive(s))return c("ignored: event no longer active");m++;const o=await e.uploadFile(p,t),l=`${x()}_${m}`;e.bridge.sendMedia(s,t,o.access_url,o.file_name,v,l,o.extra),f.info("claude-tools",`File sent: ${o.file_name}`)}e.bridge.sendStreamChunk(s,t,"",++i.chunkSeq,!0,h)}catch(p){if(g&&b.length===0)try{const o=`fallback_${s}_${Date.now()}`,l=e.splitText(g);for(let u=0;u<l.length;u++)e.bridge.sendStreamChunk("",t,l[u],u+1,!1,o);return e.bridge.sendStreamChunk("",t,"",l.length+1,!0,o),e.markReplySent(s),_&&e.finalizeEvent(s,"responded"),c("sent via fallback")}catch{}if(!e.isEventActive(s))return c("ignored: event no longer active");throw e.bridge.sendEventResult(s,"failed",String(p),"send_msg_failed"),p}return e.markReplySent(s),_?e.finalizeEvent(s,"responded"):(i.responded=!0,e.clearActiveEvent("completed")),c("Reply sent")}async function $(r,e){const i=e.getActiveEvent(),n=String(r.event_id??""),t=r.status??"",s=r.code,a=r.msg;if(!n||!t)return{content:[{type:"text",text:"complete requires event_id and status"}],isError:!0};const d=["responded","canceled","failed"];return d.includes(t)?e.isEventActive(n)?(e.bridge.sendEventResult(n,t,a,s),e.clearActiveEvent(t),c("Event completed")):c("ignored: event no longer active"):{content:[{type:"text",text:`status must be one of: ${d.join(", ")}`}],isError:!0}}async function T(r,e){const i=String(r.chat_id??""),n=String(r.message_id??"");if(!i||!n)return{content:[{type:"text",text:"chat_id and message_id are required"}],isError:!0};try{return await e.bridge.agentInvoke("grix_message_unsend",{sessionId:i,msgId:n},y),c(`deleted (${n})`)}catch(t){return{content:[{type:"text",text:`Delete failed: ${t}`}],isError:!0}}}function j(r){const e=r.getStatusInfo();return{content:[{type:"text",text:JSON.stringify({alive:e.alive,active_event:e.activeEvent,pending_approvals:e.pendingPermissions,pending_questions:e.pendingElicitations})}]}}async function M(r,e){const i=String(r.chat_id??""),n=String(r.text??"");if(!i||!n)return{content:[{type:"text",text:"chat_id and text are required"}],isError:!0};try{const t=e.splitText(n),s=`send_${i}_${Date.now()}`;for(let a=0;a<t.length;a++)e.bridge.sendStreamChunk("",i,t[a],a+1,!1,s);return e.bridge.sendStreamChunk("",i,"",t.length+1,!0,s),c("sent")}catch(t){return{content:[{type:"text",text:`Send failed: ${t}`}],isError:!0}}}const C={access_pair:{verb:"pair_approve",payloadKey:"code"},access_deny:{verb:"pair_deny",payloadKey:"code"},allow_sender:{verb:"sender_allow",payloadKey:"sender_id"},remove_sender:{verb:"sender_remove",payloadKey:"sender_id"},access_policy:{verb:"policy_set",payloadKey:"policy"}};async function R(r,e,i){try{const n=C[r];if(!n)throw new Error(`Unknown access control tool: ${r}`);const t={};e.code!=null&&(t.code=e.code),e.sender_id!=null&&(t.sender_id=e.sender_id),e.policy!=null&&(t.policy=e.policy);const s=await i.bridge.agentInvoke("claude_access_control",{verb:n.verb,payload:t},y);return{content:[{type:"text",text:typeof s=="string"?s:JSON.stringify(s)}]}}catch(n){return{content:[{type:"text",text:`${r} failed: ${n}`}],isError:!0}}}async function O(r,e,i){try{const n=k(r,e);if(!E.has(n.action))throw new Error(`Action not allowed: ${n.action}`);const t=await i.bridge.agentInvoke(n.action,n.params,y);if(t&&Number(t.code??0)!==0)throw new Error(String(t.msg??"invoke failed"));return{content:[{type:"text",text:t?.data!=null?typeof t.data=="string"?t.data:JSON.stringify(t.data):JSON.stringify(t)}]}}catch(n){return{content:[{type:"text",text:`${r} failed: ${n}`}],isError:!0}}}function c(r){return{content:[{type:"text",text:r}]}}export{q as registerClaudeTools};
@@ -1 +0,0 @@
1
- import{log as l}from"../../core/log/index.js";class c{controlURL="";token="";isConfigured(){return!!(this.controlURL&&this.token)}configure(r,e){this.controlURL=r.replace(/\/+$/,""),this.token=e.trim(),l.info("claude-worker-client",`Configured with control URL: ${this.controlURL}`)}async post(r,e,s){if(!this.isConfigured())throw new Error("worker control not configured");const i=new AbortController,o=setTimeout(()=>i.abort(),s);try{const t=await fetch(`${this.controlURL}${r}`,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${this.token}`},body:JSON.stringify(e),signal:i.signal}),n=await t.text(),a=n.trim()?JSON.parse(n):{};if(!t.ok)throw new Error(a.error||`worker control failed ${t.status}`);return a}finally{clearTimeout(o)}}isRetryableError(r){const e=r instanceof Error?r.message:String(r);return/fetch failed|network|ECONNRESET|ETIMEDOUT|EAI_AGAIN|socket hang up|aborted/i.test(e)}async postWithRetry(r,e,s,i=1){let o;for(let t=0;t<=i;t++)try{return t>0&&l.info("claude-worker-client",`Retrying ${r} attempt=${t+1}`),await this.post(r,e,s)}catch(n){if(o=n,t>=i||!this.isRetryableError(n))break;await new Promise(a=>setTimeout(a,150))}throw o instanceof Error?o:new Error(String(o))}async deliverEvent(r){return this.postWithRetry("/v1/worker/deliver-event",{payload:r},1e4,1)}async deliverStop(r){return this.postWithRetry("/v1/worker/deliver-stop",{payload:r},1e4,1)}async deliverLocalAction(r){return this.postWithRetry("/v1/worker/deliver-local-action",{payload:r},1e4,1)}async ping(){try{return await this.postWithRetry("/v1/worker/ping",{},5e3,1),!0}catch{return!1}}}export{c as ClaudeWorkerClient};
@@ -1,2 +0,0 @@
1
- import{spawn as x,execSync as y}from"node:child_process";import{randomUUID as v}from"node:crypto";import{mkdir as S}from"node:fs/promises";import{readFileSync as C}from"node:fs";import{join as d}from"node:path";import{homedir as T,tmpdir as I}from"node:os";import{log as o}from"../../core/log/index.js";import{MCP_HTTP_CHANNEL_NAME as m}from"./protocol-contract.js";function P(t){let e=null,r=0,n=!1,i=!1;const a=v(),s=t.gatewayUrl??"http://127.0.0.1:19580/mcp";return{async start(){await F(t.command,s,t.env);const c=E(t.grix),u=[...t.args??[],"--name",`grix-mcp-${t.name}`,"--session-id",a];t.fullAuto&&u.push("--dangerously-skip-permissions"),u.push("--dangerously-load-development-channels",`server:${m}`,"--append-system-prompt",c);const f=d(I(),`grix-mcp-claude-${t.name}`);await S(f,{recursive:!0});const{expectPath:$,pidPath:g}=await M(f,t.command,u),_={...process.env,...t.env??{}};e=x("/usr/bin/expect",[$],{cwd:t.cwd,env:_,stdio:["ignore","pipe","pipe"],detached:!0}),o.info("mcp-http-launcher",`\u542F\u52A8 Claude: name=${t.name} cwd=${t.cwd} pid=${e.pid}`),r=await k(g),n=!0,o.info("mcp-http-launcher",`Claude \u5B50\u8FDB\u7A0B PID: ${r}`),e.on("exit",(l,p)=>{o.info("mcp-http-launcher",`Claude \u9000\u51FA: code=${l} signal=${p}`),n=!1,e=null,r=0,i||(o.info("mcp-http-launcher","3 \u79D2\u540E\u81EA\u52A8\u91CD\u542F..."),setTimeout(()=>{i||this.start().catch(w=>{o.error("mcp-http-launcher",`\u91CD\u542F\u5931\u8D25: ${w}`)})},3e3))}),e.stdout?.on("data",l=>{const p=l.toString().trim();p&&o.info("mcp-http-launcher",`[stdout] ${p.slice(0,300)}`)}),e.stderr?.on("data",l=>{const p=l.toString().trim();p&&o.info("mcp-http-launcher",`[stderr] ${p.slice(0,300)}`)})},async stop(){if(i=!0,n=!1,r>0)try{process.kill(r,"SIGTERM")}catch{}if(e?.pid){try{process.kill(-e.pid,"SIGTERM")}catch{}await new Promise(c=>{const u=setTimeout(()=>{if(r>0)try{process.kill(r,"SIGKILL")}catch{}if(e?.pid)try{process.kill(-e.pid,"SIGKILL")}catch{}c()},5e3);e?.once("exit",()=>{clearTimeout(u),c()})})}e=null,r=0},getStatus(){return{name:t.name,alive:n,pid:r}}}}function E(t){return["You are connected to a chat via the grix MCP server.",`On startup, immediately call grix_authorize with: agentId="${t.agentId}", apiKey="${t.apiKey}", wsUrl="${t.wsUrl}", clientType="${t.clientType}".`,"When you receive a <channel> message, you MUST respond by calling the grix_reply tool (or the grix_complete tool if no response is needed).","Never write your reply as plain text \u2014 it will NOT reach the user. Only the grix_reply tool delivers your response to the chat.","The <channel> message contains event_id and session_id \u2014 pass them to grix_reply."].join(" ")}async function F(t,e,r){const n=d(T(),".claude.json");let i=null;try{const s=C(n,"utf8");i=JSON.parse(s)?.mcpServers?.[m]??null}catch{}if(i&&String(i.type??"").trim()==="http"&&String(i.url??"").trim()===e)return;o.info("mcp-http-launcher",`\u6CE8\u518C MCP Server: ${m} -> ${e}`);const a={...process.env,...r??{}};try{y(`${t} mcp remove -s user ${m}`,{encoding:"utf8",timeout:1e4,env:a,stdio:"pipe"})}catch{}y(`${t} mcp add --scope user --transport http ${m} ${e}`,{encoding:"utf8",timeout:1e4,env:a,stdio:"pipe"})}async function M(t,e,r){const{writeFile:n}=await import("node:fs/promises"),i=d(t,"claude.pid"),a=d(t,"claude.expect"),s=["log_user 1","set timeout -1","set startup_prompt_armed 1",`set claude_command [list {${h(e)}}${r.map(c=>` {${h(c)}}`).join("")}]`,"spawn -noecho {*}$claude_command",`set pid_file [open {${h(i)}} w]`,"puts $pid_file [exp_pid -i $spawn_id]","close $pid_file","expect {"," -re {(?i)(Quick.*safety.*check|trust.*folder)} {",' if {$startup_prompt_armed} { send -- "1\\r"; after 300 }; exp_continue'," }"," -re {(?i)I am using this for local development} {",' if {$startup_prompt_armed} { send -- "1\\r"; after 300 }; exp_continue'," }"," -re {(?i)(Enter.*confirm|Press.*Enter|Hit.*Enter)} {",' if {$startup_prompt_armed} { send -- "\\r"; after 300 }; exp_continue'," }"," -re {Listening for channel} {"," set startup_prompt_armed 0"," after 1000",' send -- "Call grix_authorize now as instructed in your system prompt.\\r"'," }"," -re {bypass permissions} {"," set startup_prompt_armed 0"," after 1000",' send -- "Call grix_authorize now as instructed in your system prompt.\\r"'," }"," eof {}","}","expect eof",""];return await n(i,"","utf8"),await n(a,s.join(`
2
- `),"utf8"),{expectPath:a,pidPath:i}}function h(t){return t.replace(/[\\{}$\[\]"]/g,"\\$&")}async function k(t,e=1e4){const{readFile:r}=await import("node:fs/promises"),n=Math.ceil(e/100);for(let i=0;i<n;i++){try{const a=await r(t,"utf8"),s=parseInt(String(a).trim(),10);if(Number.isFinite(s)&&s>0)return s}catch{}await new Promise(a=>setTimeout(a,100))}return 0}export{P as createMcpHttpLauncher};
@@ -1 +0,0 @@
1
- class m{defaultTimeoutMs;onTimeout;timers=new Map;constructor(e){this.defaultTimeoutMs=e.defaultTimeoutMs??9e4,this.onTimeout=e.onTimeout}arm(e,t){this.cancel(e);const s=t?.timeoutMs??this.defaultTimeoutMs,i=Date.now()+s,o=setTimeout(()=>{this.timers.delete(e),this.onTimeout(e).catch(()=>{})},s);return this.timers.set(e,o),i}cancel(e){const t=this.timers.get(e);t&&(clearTimeout(t),this.timers.delete(e))}has(e){return this.timers.has(e)}close(){for(const e of this.timers.values())clearTimeout(e);this.timers.clear()}}export{m as ResultTimeoutManager};