grix-connector 2.1.3 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,12 +1,12 @@
1
- import{mkdir as P,readdir as b,readFile as k,rm as g,stat as E,writeFile as S}from"node:fs/promises";import p from"node:os";import d from"node:path";import{runCommand as u,spawnDetached as W,killProcessesByCommandLine as L,isWindowsElevated as D}from"./process-control.js";import{getServicePrefix as x,parseConfigDirFromPlistXML as j,parseConfigDirFromSystemdUnit as R,resolveLinuxUserUnitPath as M,resolveMacOSLaunchAgentPath as O}from"./service-paths.js";function h(t){return String(t??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function B(t){return`'${String(t??"").replace(/'/g,"'\\''")}'`}function T(t){return[t.nodePath,t.cliPath,...t.configDir?["--config-dir",t.configDir]:[]]}function U(t){const r=T(t);return`<?xml version="1.0" encoding="UTF-8"?>
1
+ import{existsSync as R,readFileSync as j}from"node:fs";import{mkdir as P,readdir as C,readFile as f,rm as w,stat as V,writeFile as h}from"node:fs/promises";import p from"node:os";import u from"node:path";import{isProcessRunning as v,runCommand as d,spawnDetached as W,isWindowsElevated as G,killProcessesByCommandLine as T}from"./process-control.js";import{getServicePrefix as A,parseConfigDirFromPlistXML as Q,parseConfigDirFromSystemdUnit as z,resolveBareDaemonMarkerPath as g,resolveLinuxUserUnitPath as H,resolveMacOSLaunchAgentPath as J}from"./service-paths.js";function y(t){return String(t??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function F(t){return`'${String(t??"").replace(/'/g,"'\\''")}'`}function M(t){return[t.nodePath,t.cliPath,...t.configDir?["--config-dir",t.configDir]:[]]}function X(t){const r=M(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>${h(t.serviceID)}</string>
6
+ <string>${y(t.serviceID)}</string>
7
7
  <key>ProgramArguments</key>
8
8
  <array>
9
- ${r.map(e=>` <string>${h(e)}</string>`).join(`
9
+ ${r.map(e=>` <string>${y(e)}</string>`).join(`
10
10
  `)}
11
11
  </array>
12
12
  <key>RunAtLoad</key>
@@ -14,26 +14,26 @@ ${r.map(e=>` <string>${h(e)}</string>`).join(`
14
14
  <key>KeepAlive</key>
15
15
  <true/>
16
16
  <key>WorkingDirectory</key>
17
- <string>${h(d.dirname(t.cliPath))}</string>
17
+ <string>${y(u.dirname(t.cliPath))}</string>
18
18
  ${t.environmentPath?` <key>EnvironmentVariables</key>
19
19
  <dict>
20
20
  <key>PATH</key>
21
- <string>${h(t.environmentPath)}</string>
21
+ <string>${y(t.environmentPath)}</string>
22
22
  </dict>
23
23
  `:""} <key>StandardOutPath</key>
24
- <string>${h(t.stdoutPath)}</string>
24
+ <string>${y(t.stdoutPath)}</string>
25
25
  <key>StandardErrorPath</key>
26
- <string>${h(t.stderrPath)}</string>
26
+ <string>${y(t.stderrPath)}</string>
27
27
  </dict>
28
28
  </plist>
29
- `}function V(t){const r=T(t).map(e=>B(e)).join(" ");return`[Unit]
29
+ `}function Z(t){const r=M(t).map(e=>F(e)).join(" ");return`[Unit]
30
30
  Description=grix-connector daemon (${t.serviceID})
31
31
  After=network.target
32
32
 
33
33
  [Service]
34
34
  Type=simple
35
35
  ExecStart=${r}
36
- WorkingDirectory=${d.dirname(t.cliPath)}
36
+ WorkingDirectory=${u.dirname(t.cliPath)}
37
37
  Restart=always
38
38
  RestartSec=2
39
39
  StandardOutput=append:${t.stdoutPath}
@@ -41,6 +41,13 @@ StandardError=append:${t.stderrPath}
41
41
 
42
42
  [Install]
43
43
  WantedBy=default.target
44
- `}function w(t,r){const e=t.replace(/[^a-zA-Z0-9._-]/g,"_");return d.join(r,".grix",`${e}-wrapper.vbs`)}function A(t,r){const e=t.replace(/[^a-zA-Z0-9._-]/g,"_"),a=d.join(r,"AppData","Roaming","Microsoft","Windows","Start Menu","Programs","Startup");return d.join(a,`${e}.lnk`)}function f(t){return String(t??"").replace(/"/g,'""')}function Q(t,r){const e=d.join(d.dirname(t),d.basename(r).replace(/\.lnk$/,"-create.vbs")),a=["Option Explicit","Dim shell, shortcut",'Set shell = CreateObject("WScript.Shell")',`Set shortcut = shell.CreateShortcut("${f(r)}")`,'shortcut.TargetPath = "wscript.exe"',`shortcut.Arguments = "//B //NoLogo \\"${f(t)}\\""`,"shortcut.WindowStyle = 7","shortcut.Save",""].join(`\r
45
- `);return{path:e,content:a}}function _(t){const r=[`"${f(t.nodePath)}"`,`"${f(t.cliPath)}"`,...t.configDir?["--config-dir",`"${f(t.configDir)}"`]:[]].join(" ");return["Option Explicit","Dim shell, command, delayMs, rapidCount",`command = "${f(r)}"`,'Set shell = CreateObject("WScript.Shell")',"delayMs = 5000","rapidCount = 0","Do"," shell.Run command, 0, True"," rapidCount = rapidCount + 1"," If rapidCount >= 10 Then"," WScript.Sleep 300000"," rapidCount = 0"," Else"," WScript.Sleep delayMs"," End If","Loop",""].join(`\r
46
- `)}function y(t){return`gui/${t??0}`}function v(t){const r=String(t?.stdout??"").trim();return String(t?.stderr??"").trim()||r||`exit=${Number(t?.exitCode??-1)}`}function F(t,r){if(Number(t?.exitCode??0)!==0)throw new Error(`${r}: ${v(t)}`)}function z(){return{platform:"darwin",kind:"launchd",async install({serviceID:t,nodePath:r,cliPath:e,configDir:a,stdoutPath:n,stderrPath:i,environmentPath:c="",homeDir:o=p.homedir()}){const s=O(t,o);return await P(d.dirname(s),{recursive:!0}),await S(s,U({serviceID:t,nodePath:r,cliPath:e,configDir:a,stdoutPath:n,stderrPath:i,environmentPath:c}),{encoding:"utf8",mode:384}),{definitionPath:s}},async start({serviceID:t,definitionPath:r,runCommand:e=u,uid:a=process.getuid?.()??0}){const n=y(a);let i=await e("launchctl",["bootstrap",n,r],{allowFailure:!0});Number(i?.exitCode??0)!==0&&(await e("launchctl",["bootout",`${n}/${t}`],{allowFailure:!0}),i=await e("launchctl",["bootstrap",n,r],{allowFailure:!0}),F(i,`launchctl bootstrap ${n}`));const c=await e("launchctl",["kickstart","-k",`${n}/${t}`],{allowFailure:!0});if(Number(c?.exitCode??0)!==0){const o=Number(i?.exitCode??0)===0?"":`, bootstrap=${v(i)}`;throw new Error(`launchctl start failed for ${n}/${t}: ${v(c)}${o}`)}},async stop({serviceID:t,runCommand:r=u,uid:e=process.getuid?.()??0}){const a=y(e);await r("launchctl",["bootout",`${a}/${t}`],{allowFailure:!0})},async restart({serviceID:t,definitionPath:r,runCommand:e=u,uid:a=process.getuid?.()??0}){const n=y(a);await e("launchctl",["bootout",`${n}/${t}`],{allowFailure:!0});for(let o=0;o<20&&(await e("launchctl",["print",`${n}/${t}`],{allowFailure:!0})).exitCode===0;o+=1)await new Promise(l=>setTimeout(l,250));try{await E(r)}catch{throw new Error(`launchd plist missing: ${r}`)}const i=await e("launchctl",["bootstrap",n,r],{allowFailure:!0});F(i,`launchctl bootstrap ${n}`);const c=await e("launchctl",["kickstart","-k",`${n}/${t}`],{allowFailure:!0});F(c,`launchctl kickstart ${n}/${t}`)},async uninstall({serviceID:t,definitionPath:r,runCommand:e=u,uid:a=process.getuid?.()??0}){const n=y(a);await e("launchctl",["bootout",`${n}/${t}`],{allowFailure:!0}),await g(r,{force:!0})},async discoverServices({homeDir:t=p.homedir()}={}){const r=d.join(t,"Library","LaunchAgents"),e=await b(r).catch(()=>[]),a=x("darwin"),n=[];for(const i of e){if(!i.startsWith(a)||!i.endsWith(".plist"))continue;const c=i.slice(0,-6),o=d.join(r,i);let s=null;try{const l=await k(o,"utf8");s=j(l)}catch{}n.push({serviceID:c,definitionPath:o,configDir:s})}return n},async isServiceLoaded({serviceID:t,runCommand:r=u,uid:e=process.getuid?.()??0}){const a=y(e),n=await r("launchctl",["print",`${a}/${t}`],{allowFailure:!0});return Number(n?.exitCode??1)===0}}}function H(){return{platform:"win32",kind:"task-scheduler",async install({serviceID:t,nodePath:r,cliPath:e,configDir:a,runCommand:n=u,homeDir:i=p.homedir()}){const c=w(t,i);await P(d.dirname(c),{recursive:!0}),await S(c,_({nodePath:r,cliPath:e,configDir:a}),"utf8");let o=!1;if(D())try{await n("schtasks",["/Create","/TN",t,"/SC","ONCE","/ST","00:00","/SD","2099/12/31","/RL","LIMITED","/F","/TR",`wscript.exe //B //NoLogo "${c}"`]),o=!0}catch{}const s=A(t,i),l=Q(c,s);return await S(l.path,l.content,"utf8"),await n("wscript.exe",[l.path,"//B","//NoLogo"]).catch(()=>{}),await g(l.path,{force:!0}),{definitionPath:o?`task:${t}`:`startup:${t}`}},async start({serviceID:t,definitionPath:r,runCommand:e=u,homeDir:a=p.homedir()}){const n=w(t,a);if(r.startsWith("task:"))try{await e("schtasks",["/Run","/TN",t]);return}catch{}W("wscript.exe",["//B","//NoLogo",n])},async stop({serviceID:t,runCommand:r=u,homeDir:e=p.homedir()}){await r("schtasks",["/End","/TN",t],{allowFailure:!0});const a=`${t}-wrapper.vbs`;await L(a,{platform:"win32"})},async restart({serviceID:t,definitionPath:r,runCommand:e=u,homeDir:a=p.homedir()}){await e("schtasks",["/End","/TN",t],{allowFailure:!0});const n=`${t}-wrapper.vbs`;await L(n,{platform:"win32"});const i=w(t,a);if(r?.startsWith("task:"))try{await e("schtasks",["/Run","/TN",t]);return}catch{}W("wscript.exe",["//B","//NoLogo",i])},async uninstall({serviceID:t,runCommand:r=u,homeDir:e=p.homedir()}){await r("schtasks",["/Delete","/TN",t,"/F"],{allowFailure:!0});const a=w(t,e);await g(a,{force:!0});const n=A(t,e);await g(n,{force:!0})},async discoverServices({homeDir:t=p.homedir(),runCommand:r=u}={}){const e=x("win32"),a=await r("schtasks",["/Query","/FO","CSV","/NH"],{allowFailure:!0}),n=[];if(Number(a?.exitCode??-1)===0){const o=String(a.stdout??"").split(/\r?\n/);for(const s of o){const l=s.match(/^"([^"]+)"/);if(!l)continue;const m=l[1];if(!m.startsWith(e))continue;let C=null;try{const $=w(m,t),N=(await k($,"utf8")).match(/--config-dir\s+""([^""]+)""/);N&&(C=N[1])}catch{}n.push({serviceID:m,definitionPath:`task:${m}`,configDir:C})}}const i=d.join(t,"AppData","Roaming","Microsoft","Windows","Start Menu","Programs","Startup"),c=await b(i).catch(()=>[]);for(const o of c){if(!o.startsWith(e)||!o.endsWith(".lnk"))continue;const s=o.slice(0,-4);if(n.some(m=>m.serviceID===s))continue;let l=null;try{const m=w(s,t),$=(await k(m,"utf8")).match(/--config-dir\s+""([^""]+)""/);$&&(l=$[1])}catch{}n.push({serviceID:s,definitionPath:`startup:${s}`,configDir:l})}return n},async isServiceLoaded({serviceID:t,runCommand:r=u}){const e=await r("schtasks",["/Query","/TN",t,"/NH"],{allowFailure:!0});return Number(e?.exitCode??1)===0}}}function Z(){return{platform:"linux",kind:"systemd-user",async install({serviceID:t,nodePath:r,cliPath:e,configDir:a,stdoutPath:n,stderrPath:i,homeDir:c=p.homedir(),runCommand:o=u}){const s=M(t,c);return await P(d.dirname(s),{recursive:!0}),await S(s,V({serviceID:t,nodePath:r,cliPath:e,configDir:a,stdoutPath:n,stderrPath:i}),{encoding:"utf8",mode:384}),await o("systemctl",["--user","daemon-reload"]),await o("systemctl",["--user","enable",`${t}.service`]),{definitionPath:s}},async start({serviceID:t,runCommand:r=u}){await r("systemctl",["--user","start",`${t}.service`])},async stop({serviceID:t,runCommand:r=u}){await r("systemctl",["--user","stop",`${t}.service`],{allowFailure:!0})},async restart({serviceID:t,runCommand:r=u}){await r("systemctl",["--user","restart",`${t}.service`])},async uninstall({serviceID:t,definitionPath:r,runCommand:e=u}){await e("systemctl",["--user","stop",`${t}.service`],{allowFailure:!0}),await e("systemctl",["--user","disable",`${t}.service`],{allowFailure:!0}),await g(r,{force:!0}),await e("systemctl",["--user","daemon-reload"])},async discoverServices({homeDir:t=p.homedir()}={}){const r=d.join(t,".config","systemd","user"),e=await b(r).catch(()=>[]),a=x("linux"),n=[];for(const i of e){if(!i.startsWith(a)||!i.endsWith(".service"))continue;const c=i.slice(0,-8),o=d.join(r,i);let s=null;try{const l=await k(o,"utf8");s=R(l)}catch{}n.push({serviceID:c,definitionPath:o,configDir:s})}return n},async isServiceLoaded({serviceID:t,runCommand:r=u}){const e=await r("systemctl",["--user","is-active",`${t}.service`],{allowFailure:!0});return String(e?.stdout??"").trim()==="active"}}}function I(t=process.platform){return t==="darwin"?z():t==="win32"?H():Z()}export{I as getPlatformServiceAdapter};
44
+ `}function k(t,r){const e=t.replace(/[^a-zA-Z0-9._-]/g,"_");return u.join(r,".grix",`${e}-wrapper.vbs`)}function B(t,r){const e=t.replace(/[^a-zA-Z0-9._-]/g,"_"),n=u.join(r,"AppData","Roaming","Microsoft","Windows","Start Menu","Programs","Startup");return u.join(n,`${e}.lnk`)}function $(t){return String(t??"").replace(/"/g,'""')}function q(t,r){const e=u.join(u.dirname(t),u.basename(r).replace(/\.lnk$/,"-create.vbs")),n=["Option Explicit","Dim shell, shortcut",'Set shell = CreateObject("WScript.Shell")',`Set shortcut = shell.CreateShortcut("${$(r)}")`,'shortcut.TargetPath = "wscript.exe"',`shortcut.Arguments = "//B //NoLogo \\"${$(t)}\\""`,"shortcut.WindowStyle = 7","shortcut.Save",""].join(`\r
45
+ `);return{path:e,content:n}}function K(t){const r=[`"${$(t.nodePath)}"`,`"${$(t.cliPath)}"`,...t.configDir?["--config-dir",`"${$(t.configDir)}"`]:[]].join(" ");return["Option Explicit","Dim shell, command, delayMs, rapidCount",`command = "${$(r)}"`,'Set shell = CreateObject("WScript.Shell")',"delayMs = 5000","rapidCount = 0","Do"," shell.Run command, 0, True"," rapidCount = rapidCount + 1"," If rapidCount >= 10 Then"," WScript.Sleep 300000"," rapidCount = 0"," Else"," WScript.Sleep delayMs"," End If","Loop",""].join(`\r
46
+ `)}function b(t){return`gui/${t??0}`}function E(t){const r=String(t?.stdout??"").trim();return String(t?.stderr??"").trim()||r||`exit=${Number(t?.exitCode??-1)}`}function L(t,r){if(Number(t?.exitCode??0)!==0)throw new Error(`${r}: ${E(t)}`)}function Y(){return{platform:"darwin",kind:"launchd",async install({serviceID:t,nodePath:r,cliPath:e,configDir:n,stdoutPath:a,stderrPath:i,environmentPath:s="",homeDir:o=p.homedir()}){const c=J(t,o);return await P(u.dirname(c),{recursive:!0}),await h(c,X({serviceID:t,nodePath:r,cliPath:e,configDir:n,stdoutPath:a,stderrPath:i,environmentPath:s}),{encoding:"utf8",mode:384}),{definitionPath:c}},async start({serviceID:t,definitionPath:r,runCommand:e=d,uid:n=process.getuid?.()??0}){const a=b(n);let i=await e("launchctl",["bootstrap",a,r],{allowFailure:!0});Number(i?.exitCode??0)!==0&&(await e("launchctl",["bootout",`${a}/${t}`],{allowFailure:!0}),i=await e("launchctl",["bootstrap",a,r],{allowFailure:!0}),L(i,`launchctl bootstrap ${a}`));const s=await e("launchctl",["kickstart","-k",`${a}/${t}`],{allowFailure:!0});if(Number(s?.exitCode??0)!==0){const o=Number(i?.exitCode??0)===0?"":`, bootstrap=${E(i)}`;throw new Error(`launchctl start failed for ${a}/${t}: ${E(s)}${o}`)}},async stop({serviceID:t,runCommand:r=d,uid:e=process.getuid?.()??0}){const n=b(e);await r("launchctl",["bootout",`${n}/${t}`],{allowFailure:!0})},async restart({serviceID:t,definitionPath:r,runCommand:e=d,uid:n=process.getuid?.()??0}){const a=b(n);await e("launchctl",["bootout",`${a}/${t}`],{allowFailure:!0});for(let o=0;o<20&&(await e("launchctl",["print",`${a}/${t}`],{allowFailure:!0})).exitCode===0;o+=1)await new Promise(l=>setTimeout(l,250));try{await V(r)}catch{throw new Error(`launchd plist missing: ${r}`)}const i=await e("launchctl",["bootstrap",a,r],{allowFailure:!0});L(i,`launchctl bootstrap ${a}`);const s=await e("launchctl",["kickstart","-k",`${a}/${t}`],{allowFailure:!0});L(s,`launchctl kickstart ${a}/${t}`)},async uninstall({serviceID:t,definitionPath:r,runCommand:e=d,uid:n=process.getuid?.()??0}){const a=b(n);await e("launchctl",["bootout",`${a}/${t}`],{allowFailure:!0}),await w(r,{force:!0})},async discoverServices({homeDir:t=p.homedir()}={}){const r=u.join(t,"Library","LaunchAgents"),e=await C(r).catch(()=>[]),n=A("darwin"),a=[];for(const i of e){if(!i.startsWith(n)||!i.endsWith(".plist"))continue;const s=i.slice(0,-6),o=u.join(r,i);let c=null;try{const l=await f(o,"utf8");c=Q(l)}catch{}a.push({serviceID:s,definitionPath:o,configDir:c})}return a},async isServiceLoaded({serviceID:t,runCommand:r=d,uid:e=process.getuid?.()??0}){const n=b(e),a=await r("launchctl",["print",`${n}/${t}`],{allowFailure:!0});return Number(a?.exitCode??1)===0}}}function I(){return{platform:"win32",kind:"task-scheduler",async install({serviceID:t,nodePath:r,cliPath:e,configDir:n,runCommand:a=d,homeDir:i=p.homedir()}){const s=k(t,i);await P(u.dirname(s),{recursive:!0}),await h(s,K({nodePath:r,cliPath:e,configDir:n}),"utf8");let o=!1;if(G())try{await a("schtasks",["/Create","/TN",t,"/SC","ONCE","/ST","00:00","/SD","2099/12/31","/RL","LIMITED","/F","/TR",`wscript.exe //B //NoLogo "${s}"`]),o=!0}catch{}const c=B(t,i),l=q(s,c);return await h(l.path,l.content,"utf8"),await a("wscript.exe",[l.path,"//B","//NoLogo"]).catch(()=>{}),await w(l.path,{force:!0}),{definitionPath:o?`task:${t}`:`startup:${t}`}},async start({serviceID:t,definitionPath:r,runCommand:e=d,homeDir:n=p.homedir()}){const a=k(t,n);if(r.startsWith("task:"))try{await e("schtasks",["/Run","/TN",t]);return}catch{}W("wscript.exe",["//B","//NoLogo",a])},async stop({serviceID:t,runCommand:r=d,homeDir:e=p.homedir()}){await r("schtasks",["/End","/TN",t],{allowFailure:!0});const n=`${t}-wrapper.vbs`;await T(n,{platform:"win32"})},async restart({serviceID:t,definitionPath:r,runCommand:e=d,homeDir:n=p.homedir()}){await e("schtasks",["/End","/TN",t],{allowFailure:!0});const a=`${t}-wrapper.vbs`;await T(a,{platform:"win32"});const i=k(t,n);if(r?.startsWith("task:"))try{await e("schtasks",["/Run","/TN",t]);return}catch{}W("wscript.exe",["//B","//NoLogo",i])},async uninstall({serviceID:t,runCommand:r=d,homeDir:e=p.homedir()}){await r("schtasks",["/Delete","/TN",t,"/F"],{allowFailure:!0});const n=k(t,e);await w(n,{force:!0});const a=B(t,e);await w(a,{force:!0})},async discoverServices({homeDir:t=p.homedir(),runCommand:r=d}={}){const e=A("win32"),n=await r("schtasks",["/Query","/FO","CSV","/NH"],{allowFailure:!0}),a=[];if(Number(n?.exitCode??-1)===0){const o=String(n.stdout??"").split(/\r?\n/);for(const c of o){const l=c.match(/^"([^"]+)"/);if(!l)continue;const m=l[1];if(!m.startsWith(e))continue;let _=null;try{const x=k(m,t),D=(await f(x,"utf8")).match(/--config-dir\s+""([^""]+)""/);D&&(_=D[1])}catch{}a.push({serviceID:m,definitionPath:`task:${m}`,configDir:_})}}const i=u.join(t,"AppData","Roaming","Microsoft","Windows","Start Menu","Programs","Startup"),s=await C(i).catch(()=>[]);for(const o of s){if(!o.startsWith(e)||!o.endsWith(".lnk"))continue;const c=o.slice(0,-4);if(a.some(m=>m.serviceID===c))continue;let l=null;try{const m=k(c,t),x=(await f(m,"utf8")).match(/--config-dir\s+""([^""]+)""/);x&&(l=x[1])}catch{}a.push({serviceID:c,definitionPath:`startup:${c}`,configDir:l})}return a},async isServiceLoaded({serviceID:t,runCommand:r=d}){const e=await r("schtasks",["/Query","/TN",t,"/NH"],{allowFailure:!0});return Number(e?.exitCode??1)===0}}}function tt(){return{platform:"linux",kind:"systemd-user",async install({serviceID:t,nodePath:r,cliPath:e,configDir:n,stdoutPath:a,stderrPath:i,homeDir:s=p.homedir(),runCommand:o=d}){const c=H(t,s);return await P(u.dirname(c),{recursive:!0}),await h(c,Z({serviceID:t,nodePath:r,cliPath:e,configDir:n,stdoutPath:a,stderrPath:i}),{encoding:"utf8",mode:384}),await o("systemctl",["--user","daemon-reload"]),await o("systemctl",["--user","enable",`${t}.service`]),{definitionPath:c}},async start({serviceID:t,runCommand:r=d}){await r("systemctl",["--user","start",`${t}.service`])},async stop({serviceID:t,runCommand:r=d}){await r("systemctl",["--user","stop",`${t}.service`],{allowFailure:!0})},async restart({serviceID:t,runCommand:r=d}){await r("systemctl",["--user","restart",`${t}.service`])},async uninstall({serviceID:t,definitionPath:r,runCommand:e=d}){await e("systemctl",["--user","stop",`${t}.service`],{allowFailure:!0}),await e("systemctl",["--user","disable",`${t}.service`],{allowFailure:!0}),await w(r,{force:!0}),await e("systemctl",["--user","daemon-reload"])},async discoverServices({homeDir:t=p.homedir()}={}){const r=u.join(t,".config","systemd","user"),e=await C(r).catch(()=>[]),n=A("linux"),a=[];for(const i of e){if(!i.startsWith(n)||!i.endsWith(".service"))continue;const s=i.slice(0,-8),o=u.join(r,i);let c=null;try{const l=await f(o,"utf8");c=z(l)}catch{}a.push({serviceID:s,definitionPath:o,configDir:c})}return a},async isServiceLoaded({serviceID:t,runCommand:r=d}){const e=await r("systemctl",["--user","is-active",`${t}.service`],{allowFailure:!0});return String(e?.stdout??"").trim()==="active"}}}function S(t){try{const r=j(t,"utf8"),e=JSON.parse(r);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 rt(t){return u.join(t,"daemon.lock.json")}function N(t){const r=rt(t);try{const e=j(r,"utf8"),n=JSON.parse(e);if(typeof n?.pid=="number")return n.pid}catch{}return 0}function O(t){return u.join(t,".profile")}function et(t){return t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function nt(t){const r=[F(t.nodePath),F(t.cliPath)];t.configDir&&r.push("--config-dir",F(t.configDir));const e=r.join(" ");return`${e} status >/dev/null 2>&1 || (nohup ${e} start >/dev/null 2>&1 &)`}function at(t,r){return`# >>> grix-connector autostart [${t}] >>>
47
+ ${r}
48
+ # <<< grix-connector autostart [${t}] <<<
49
+ `}function U(t){const r=et(t);return new RegExp(`(?:^|\\n)# >>> grix-connector autostart \\[${r}\\] >>>\\n[\\s\\S]*?\\n# <<< grix-connector autostart \\[${r}\\] <<<\\n?`,"m")}async function it(t,r,e){let n="";try{n=await f(t,"utf8")}catch{}const a=at(r,e),i=U(r);let s;if(i.test(n))s=n.replace(i,`
50
+ ${a}`).replace(/^\n+/,"");else{const o=n.length===0||n.endsWith(`
51
+ `)?"":`
52
+ `;s=`${n}${o}${a}`}s!==n&&(await P(u.dirname(t),{recursive:!0}),await h(t,s,{encoding:"utf8"}))}async function ot(t,r){let e="";try{e=await f(t,"utf8")}catch{return}const n=U(r);if(!n.test(e))return;const a=e.replace(n,"").replace(/^\n+/,"");await h(t,a,{encoding:"utf8"})}function st(){return{platform:"linux",kind:"bare-daemon",async install({serviceID:t,nodePath:r,cliPath:e,configDir:n,stdoutPath:a,stderrPath:i,homeDir:s=p.homedir()}){const o=g(t,s);await P(u.dirname(o),{recursive:!0});const c=u.resolve(u.dirname(u.dirname(a||i||o))),l={schema_version:1,service_id:t,node_path:r,cli_path:e,config_dir:n??"",root_dir:c,stdout_path:a,stderr_path:i,installed_at:Date.now()};await h(o,`${JSON.stringify(l,null,2)}
53
+ `,{encoding:"utf8",mode:384});const m=nt({nodePath:r,cliPath:e,configDir:n??""});return await it(O(s),t,m),{definitionPath:o}},async start({serviceID:t,homeDir:r=p.homedir()}){const e=g(t,r),n=S(e);if(!n)throw new Error(`bare-daemon marker missing: ${e}`);const a=[n.cli_path];n.config_dir&&a.push("--config-dir",n.config_dir),W(n.node_path,a)},async stop({serviceID:t,homeDir:r=p.homedir()}){const e=g(t,r),n=S(e);if(!n)return;const a=N(n.root_dir);if(a>0&&v(a))try{process.kill(a,"SIGTERM")}catch{}},async restart(t){await this.stop(t);const r=Date.now()+5e3,e=g(t.serviceID,t.homeDir??p.homedir()),n=S(e);for(;n&&Date.now()<r;){const a=N(n.root_dir);if(a<=0||!v(a))break;await new Promise(i=>setTimeout(i,100))}await this.start(t)},async uninstall({serviceID:t,homeDir:r=p.homedir()}){const e=g(t,r),n=S(e);if(n){const a=N(n.root_dir);if(a>0&&v(a))try{process.kill(a,"SIGTERM")}catch{}}await w(e,{force:!0}),await ot(O(r),t)},async discoverServices({homeDir:t=p.homedir()}={}){const r=u.join(t,".grix","service"),e=await C(r).catch(()=>[]),n=A("linux"),a=[];for(const i of e){if(!i.startsWith(n)||!i.endsWith(".bare.json"))continue;const s=i.slice(0,-10),o=u.join(r,i),c=S(o);a.push({serviceID:s,definitionPath:o,configDir:c?.config_dir||null})}return a},async isServiceLoaded({serviceID:t,homeDir:r=p.homedir()}){const e=g(t,r),n=S(e);if(!n)return!1;const a=N(n.root_dir);return a>0&&v(a)}}}function ct(t=process.getuid?.()??0){if(process.env.GRIX_FORCE_BARE_DAEMON==="1")return!1;const r=process.env.XDG_RUNTIME_DIR;return!!(r&&R(u.join(r,"bus"))||R(`/run/user/${t}/bus`))}function wt(t=process.platform,r={}){return t==="darwin"?Y():t==="win32"?I():r.systemdUserAvailable??ct()?tt():st()}export{wt as getPlatformServiceAdapter,ct as isSystemdUserBusAvailable,ot as removeProfileAutostart,it as upsertProfileAutostart};
@@ -1,4 +1,4 @@
1
- import{createHash as w}from"node:crypto";import P from"node:fs";import S from"node:os";import c from"node:path";import m from"node:process";import{mkdir as p}from"node:fs/promises";import{setTimeout as _}from"node:timers/promises";import{resolveRuntimePaths as u}from"../core/config/paths.js";import{inspectDaemonState as l}from"../runtime/service-state.js";import{isProcessRunning as y,killOrphanedDaemonProcesses as F,runCommand as n,terminateProcessTree as L,waitForProcessExit as v}from"./process-control.js";import{readDaemonLock as g}from"../runtime/daemon-lock.js";import{ServiceInstallStore as z}from"./service-install-store.js";import{getPlatformServiceAdapter as R}from"./platform-adapter.js";import{buildServiceID as D,resolveServiceInstallRecordPath as b,resolveServiceStderrPath as I,resolveServiceStdoutPath as $}from"./service-paths.js";class G{adapter;platform;homeDir;uid;nodePath;cliPath;constructor(t){this.platform=t.platform??m.platform,this.homeDir=t.homeDir??S.homedir(),this.uid=t.uid??m.getuid?.()??0,this.nodePath=t.nodePath??m.execPath,this.cliPath=t.cliPath,this.adapter=R(this.platform)}normalizeRootDir(t){return c.resolve(String(t??"").trim())}buildDescriptor(t,i,r=""){const e=this.normalizeRootDir(t);return{schema_version:1,platform:this.platform,service_id:D(e,this.platform),node_path:this.nodePath,cli_path:this.cliPath,cli_hash:this.computeFileHash(this.cliPath)??"",definition_path:r,config_dir:i??"",installed_at:0,updated_at:0}}createStore(t){return new z(b(this.normalizeRootDir(t)))}toAdapterPayload(t){return{serviceID:t.service_id,nodePath:t.node_path,cliPath:t.cli_path,configDir:t.config_dir||void 0,environmentPath:this.buildServiceEnvironmentPath()}}async resolveServiceLogPaths(t,i){if(this.platform==="darwin"){const r=c.join(this.homeDir,"Library","Logs","grix-connector");return await p(r,{recursive:!0}),{stdoutPath:c.join(r,`${i}.out.log`),stderrPath:c.join(r,`${i}.err.log`)}}return await p(c.join(t,"service"),{recursive:!0}),{stdoutPath:$(t),stderrPath:I(t)}}buildServiceEnvironmentPath(){const t=[c.dirname(this.nodePath),"/opt/homebrew/bin","/usr/local/bin","/usr/bin","/bin","/usr/sbin","/sbin"],i=String(m.env.PATH??"").split(c.delimiter).filter(s=>s&&c.isAbsolute(s)).filter(s=>!s.includes("/node_modules/.bin")).filter(s=>!s.includes("/node-gyp-bin")),r=[],e=new Set;for(const s of[...t,...i]){if(e.has(s))continue;if(e.add(s),[...r,s].join(c.delimiter).length>2048)break;r.push(s)}return r.join(c.delimiter)}toStartParams(t){return{serviceID:t.service_id,definitionPath:t.definition_path,uid:this.uid,homeDir:this.homeDir,runCommand:n}}toStopParams(t){return{serviceID:t.service_id,uid:this.uid,homeDir:this.homeDir,runCommand:n}}toRestartParams(t){return{serviceID:t.service_id,definitionPath:t.definition_path,uid:this.uid,homeDir:this.homeDir,runCommand:n}}toUninstallParams(t){return{serviceID:t.service_id,definitionPath:t.definition_path,uid:this.uid,homeDir:this.homeDir,runCommand:n}}async loadDescriptor(t){const i=this.createStore(t),r=await i.load();if(!r)throw new Error("\u540E\u53F0\u670D\u52A1\u8FD8\u6CA1\u6709\u5B89\u88C5\uFF0C\u8BF7\u5148\u6267\u884C service install\u3002");return{store:i,descriptor:r}}isDescriptorCurrent(t){if(t.node_path!==this.nodePath||t.cli_path!==this.cliPath||!t.cli_hash)return!1;const i=this.computeFileHash(t.cli_path);return i!==null&&i===t.cli_hash}computeFileHash(t){try{const i=P.readFileSync(t);return w("sha256").update(i).digest("hex")}catch{return null}}async refreshDescriptor(t,i){const r=this.normalizeRootDir(t);await p(c.join(r,"service"),{recursive:!0});const e={...i,platform:this.platform,service_id:i.service_id||D(r,this.platform),node_path:this.nodePath,cli_path:this.cliPath,cli_hash:this.computeFileHash(this.cliPath)??"",config_dir:i.config_dir},s=await this.resolveServiceLogPaths(r,e.service_id),a=await this.adapter.install({...this.toAdapterPayload(e),stdoutPath:s.stdoutPath,stderrPath:s.stderrPath,homeDir:this.homeDir,uid:this.uid,runCommand:n}),o={...e,installed_at:i.installed_at,definition_path:a?.definitionPath||e.definition_path,updated_at:Date.now()};return await this.createStore(r).save(o),o}async resolveActiveDescriptor(t){const{descriptor:i}=await this.loadDescriptor(t);return this.isDescriptorCurrent(i)?i:this.refreshDescriptor(t,i)}async discoverConflictingServices(t){const i=this.normalizeRootDir(t),r=D(i,this.platform);return(await this.adapter.discoverServices({homeDir:this.homeDir,runCommand:n})).filter(s=>s.serviceID!==r)}async cleanupConflictingServices(t){const i=await this.discoverConflictingServices(t);if(i.length>0){const e=i.map(s=>s.serviceID).join(", ");console.log(`\u53D1\u73B0 ${i.length} \u4E2A\u6B8B\u7559\u670D\u52A1\uFF0C\u6B63\u5728\u68C0\u67E5: ${e}`)}let r=0;for(const e of i){if(await this.adapter.isServiceLoaded({serviceID:e.serviceID,uid:this.uid,runCommand:n})){console.log(`\u8DF3\u8FC7\u6B63\u5728\u8FD0\u884C\u7684\u670D\u52A1: ${e.serviceID}`);continue}try{await this.adapter.stop({serviceID:e.serviceID,uid:this.uid,runCommand:n})}catch{}try{await this.adapter.uninstall({serviceID:e.serviceID,definitionPath:e.definitionPath,uid:this.uid,runCommand:n}),r++}catch{}}return r}async waitForDaemonStarted(t,i={}){const{oldPid:r=0,timeoutMs:e=2e4}=i,s=Date.now();for(;Date.now()-s<e;){const h=g(t.daemonLockFile);if(h&&h.pid>0&&y(h.pid)&&h.pid!==r)return;const f=l(t.daemonStatusFile);if(f.running&&f.pid>0&&f.pid!==r)return;await _(100)}const a=g(t.daemonLockFile),o=l(t.daemonStatusFile),d=[`daemon start timeout (${e}ms)`,`status=${JSON.stringify(o)}`,`lock=${a?JSON.stringify(a):"missing"}`,this.tailLogForError("stderr",i.stderrLogFile??t.stderrLogFile),this.tailLogForError("stdout",i.stdoutLogFile??t.stdoutLogFile)].filter(Boolean);throw new Error(d.join(`
2
- `))}tailLogForError(t,i,r=20){if(!P.existsSync(i))return`${t}=missing`;try{const e=P.readFileSync(i,"utf8").trim();if(!e)return`${t}=empty`;const s=e.split(/\r?\n/).slice(-r);return`${t}:
1
+ import{createHash as w}from"node:crypto";import D from"node:fs";import S from"node:os";import h from"node:path";import m from"node:process";import{mkdir as p}from"node:fs/promises";import{setTimeout as _}from"node:timers/promises";import{resolveRuntimePaths as u}from"../core/config/paths.js";import{inspectDaemonState as l}from"../runtime/service-state.js";import{isProcessRunning as y,killOrphanedDaemonProcesses as F,runCommand as n,terminateProcessTree as L,waitForProcessExit as v}from"./process-control.js";import{readDaemonLock as g}from"../runtime/daemon-lock.js";import{ServiceInstallStore as z}from"./service-install-store.js";import{getPlatformServiceAdapter as R}from"./platform-adapter.js";import{buildServiceID as P,resolveServiceInstallRecordPath as b,resolveServiceStderrPath as I,resolveServiceStdoutPath as $}from"./service-paths.js";class G{adapter;platform;homeDir;uid;nodePath;cliPath;constructor(t){this.platform=t.platform??m.platform,this.homeDir=t.homeDir??S.homedir(),this.uid=t.uid??m.getuid?.()??0,this.nodePath=t.nodePath??m.execPath,this.cliPath=t.cliPath,this.adapter=R(this.platform)}normalizeRootDir(t){return h.resolve(String(t??"").trim())}buildDescriptor(t,i,r=""){const e=this.normalizeRootDir(t);return{schema_version:1,platform:this.platform,service_id:P(e,this.platform),node_path:this.nodePath,cli_path:this.cliPath,cli_hash:this.computeFileHash(this.cliPath)??"",definition_path:r,config_dir:i??"",installed_at:0,updated_at:0}}createStore(t){return new z(b(this.normalizeRootDir(t)))}toAdapterPayload(t){return{serviceID:t.service_id,nodePath:t.node_path,cliPath:t.cli_path,configDir:t.config_dir||void 0,environmentPath:this.buildServiceEnvironmentPath()}}async resolveServiceLogPaths(t,i){if(this.platform==="darwin"){const r=h.join(this.homeDir,"Library","Logs","grix-connector");return await p(r,{recursive:!0}),{stdoutPath:h.join(r,`${i}.out.log`),stderrPath:h.join(r,`${i}.err.log`)}}return await p(h.join(t,"service"),{recursive:!0}),{stdoutPath:$(t),stderrPath:I(t)}}buildServiceEnvironmentPath(){const t=[h.dirname(this.nodePath),"/opt/homebrew/bin","/usr/local/bin","/usr/bin","/bin","/usr/sbin","/sbin"],i=String(m.env.PATH??"").split(h.delimiter).filter(s=>s&&h.isAbsolute(s)).filter(s=>!s.includes("/node_modules/.bin")).filter(s=>!s.includes("/node-gyp-bin")),r=[],e=new Set;for(const s of[...t,...i]){if(e.has(s))continue;if(e.add(s),[...r,s].join(h.delimiter).length>2048)break;r.push(s)}return r.join(h.delimiter)}toStartParams(t){return{serviceID:t.service_id,definitionPath:t.definition_path,uid:this.uid,homeDir:this.homeDir,runCommand:n}}toStopParams(t){return{serviceID:t.service_id,uid:this.uid,homeDir:this.homeDir,runCommand:n}}toRestartParams(t){return{serviceID:t.service_id,definitionPath:t.definition_path,uid:this.uid,homeDir:this.homeDir,runCommand:n}}toUninstallParams(t){return{serviceID:t.service_id,definitionPath:t.definition_path,uid:this.uid,homeDir:this.homeDir,runCommand:n}}async loadDescriptor(t){const i=this.createStore(t),r=await i.load();if(!r)throw new Error("\u540E\u53F0\u670D\u52A1\u8FD8\u6CA1\u6709\u5B89\u88C5\uFF0C\u8BF7\u5148\u6267\u884C service install\u3002");return{store:i,descriptor:r}}isDescriptorCurrent(t){if(t.node_path!==this.nodePath||t.cli_path!==this.cliPath||!t.cli_hash)return!1;const i=this.computeFileHash(t.cli_path);return i!==null&&i===t.cli_hash}computeFileHash(t){try{const i=D.readFileSync(t);return w("sha256").update(i).digest("hex")}catch{return null}}async refreshDescriptor(t,i){const r=this.normalizeRootDir(t);await p(h.join(r,"service"),{recursive:!0});const e={...i,platform:this.platform,service_id:i.service_id||P(r,this.platform),node_path:this.nodePath,cli_path:this.cliPath,cli_hash:this.computeFileHash(this.cliPath)??"",config_dir:i.config_dir},s=await this.resolveServiceLogPaths(r,e.service_id),a=await this.adapter.install({...this.toAdapterPayload(e),stdoutPath:s.stdoutPath,stderrPath:s.stderrPath,homeDir:this.homeDir,uid:this.uid,runCommand:n}),o={...e,installed_at:i.installed_at,definition_path:a?.definitionPath||e.definition_path,updated_at:Date.now()};return await this.createStore(r).save(o),o}async resolveActiveDescriptor(t){const{descriptor:i}=await this.loadDescriptor(t);return this.isDescriptorCurrent(i)?i:this.refreshDescriptor(t,i)}async discoverConflictingServices(t){const i=this.normalizeRootDir(t),r=P(i,this.platform);return(await this.adapter.discoverServices({homeDir:this.homeDir,runCommand:n})).filter(s=>s.serviceID!==r)}async cleanupConflictingServices(t){const i=await this.discoverConflictingServices(t);if(i.length>0){const e=i.map(s=>s.serviceID).join(", ");console.log(`\u53D1\u73B0 ${i.length} \u4E2A\u6B8B\u7559\u670D\u52A1\uFF0C\u6B63\u5728\u68C0\u67E5: ${e}`)}let r=0;for(const e of i){if(await this.adapter.isServiceLoaded({serviceID:e.serviceID,uid:this.uid,homeDir:this.homeDir,runCommand:n})){console.log(`\u8DF3\u8FC7\u6B63\u5728\u8FD0\u884C\u7684\u670D\u52A1: ${e.serviceID}`);continue}try{await this.adapter.stop({serviceID:e.serviceID,uid:this.uid,homeDir:this.homeDir,runCommand:n})}catch{}try{await this.adapter.uninstall({serviceID:e.serviceID,definitionPath:e.definitionPath,uid:this.uid,homeDir:this.homeDir,runCommand:n}),r++}catch{}}return r}async waitForDaemonStarted(t,i={}){const{oldPid:r=0,timeoutMs:e=2e4}=i,s=Date.now();for(;Date.now()-s<e;){const c=g(t.daemonLockFile);if(c&&c.pid>0&&y(c.pid)&&c.pid!==r)return;const f=l(t.daemonStatusFile);if(f.running&&f.pid>0&&f.pid!==r)return;await _(100)}const a=g(t.daemonLockFile),o=l(t.daemonStatusFile),d=[`daemon start timeout (${e}ms)`,`status=${JSON.stringify(o)}`,`lock=${a?JSON.stringify(a):"missing"}`,this.tailLogForError("stderr",i.stderrLogFile??t.stderrLogFile),this.tailLogForError("stdout",i.stdoutLogFile??t.stdoutLogFile)].filter(Boolean);throw new Error(d.join(`
2
+ `))}tailLogForError(t,i,r=20){if(!D.existsSync(i))return`${t}=missing`;try{const e=D.readFileSync(i,"utf8").trim();if(!e)return`${t}=empty`;const s=e.split(/\r?\n/).slice(-r);return`${t}:
3
3
  ${s.join(`
4
- `)}`}catch(e){return`${t}=unreadable: ${e instanceof Error?e.message:String(e)}`}}async install(t){const i=this.normalizeRootDir(t.rootDir);await this.cleanupConflictingServices(i),await p(c.join(i,"service"),{recursive:!0});const r=this.buildDescriptor(i,t.configDir),e=await this.resolveServiceLogPaths(i,r.service_id),s=await this.adapter.install({...this.toAdapterPayload(r),stdoutPath:e.stdoutPath,stderrPath:e.stderrPath,homeDir:this.homeDir,uid:this.uid,runCommand:n}),a={...r,definition_path:s?.definitionPath??"",installed_at:Date.now(),updated_at:Date.now()};await this.createStore(i).save(a),await this.adapter.start(this.toStartParams(a));const o=u(i);return await this.waitForDaemonStarted(o,{stdoutLogFile:e.stdoutPath,stderrLogFile:e.stderrPath}),this.status({rootDir:i})}async start(t){const i=this.normalizeRootDir(t.rootDir);await this.cleanupConflictingServices(i);const r=u(i),e=l(r.daemonStatusFile),a=await this.createStore(i).load();if(e.running&&a&&this.isDescriptorCurrent(a))return this.status({rootDir:i});const o=a?this.isDescriptorCurrent(a)&&e.running?a:await this.refreshDescriptor(i,a):await this.resolveActiveDescriptor(i),d=await this.resolveServiceLogPaths(i,o.service_id);try{e.running?await this.adapter.restart(this.toRestartParams(o)):await this.adapter.start(this.toStartParams(o))}catch(h){if(h instanceof Error&&h.message.includes("plist missing"))await this.adapter.install({...this.toAdapterPayload(o),stdoutPath:d.stdoutPath,stderrPath:d.stderrPath,homeDir:this.homeDir,uid:this.uid,runCommand:n}),await this.adapter.start(this.toStartParams(o));else throw h}return await this.waitForDaemonStarted(r,{oldPid:e.pid,stdoutLogFile:d.stdoutPath,stderrLogFile:d.stderrPath}),this.status({rootDir:i})}async stop(t){const{descriptor:i}=await this.loadDescriptor(t.rootDir),r=u(i.config_dir||this.normalizeRootDir(t.rootDir)),e=l(r.daemonStatusFile);await this.adapter.stop(this.toStopParams(i)),e.running&&e.pid&&(await v(e.pid,{timeoutMs:5e3})||(await L(e.pid,{platform:this.platform,runCommandImpl:n}),await v(e.pid,{timeoutMs:5e3})));const s=await F(i.cli_path);return s>0&&console.log(`\u6E05\u7406\u4E86 ${s} \u4E2A\u6B8B\u7559 daemon \u8FDB\u7A0B`),this.status({rootDir:t.rootDir})}async restart(t){const i=this.normalizeRootDir(t.rootDir);await this.cleanupConflictingServices(i);const r=await this.resolveActiveDescriptor(i),e=u(i),s=l(e.daemonStatusFile),a=await this.resolveServiceLogPaths(i,r.service_id);try{await this.adapter.restart(this.toRestartParams(r))}catch(o){if(o instanceof Error&&o.message.includes("plist missing"))await this.adapter.install({...this.toAdapterPayload(r),stdoutPath:a.stdoutPath,stderrPath:a.stderrPath,homeDir:this.homeDir,uid:this.uid,runCommand:n}),await this.adapter.start(this.toStartParams(r));else throw o}return await this.waitForDaemonStarted(e,{oldPid:s.pid,stdoutLogFile:a.stdoutPath,stderrLogFile:a.stderrPath}),this.status({rootDir:i})}async uninstall(t){const i=this.normalizeRootDir(t.rootDir),r=this.createStore(i),e=await r.load();return e&&(await this.adapter.uninstall(this.toUninstallParams(e)),await r.clear()),this.status({rootDir:i})}async status(t){const i=this.normalizeRootDir(t.rootDir),e=await this.createStore(i).load(),s=u(i),a=l(s.daemonStatusFile);return e?{installed:!0,install_state:this.isDescriptorCurrent(e)?"current":"stale",service_kind:this.adapter.kind,service_id:e.service_id,definition_path:e.definition_path,root_dir:i,daemon_state:a.running?"running":a.state,pid:a.pid,connection_state:a.connection_state,updated_at:a.updated_at}:{installed:!1,install_state:"missing",service_kind:this.adapter.kind,root_dir:i,daemon_state:a.running?"running":a.state,pid:a.pid}}}export{G as ServiceManager};
4
+ `)}`}catch(e){return`${t}=unreadable: ${e instanceof Error?e.message:String(e)}`}}async install(t){const i=this.normalizeRootDir(t.rootDir);await this.cleanupConflictingServices(i),await p(h.join(i,"service"),{recursive:!0});const r=this.buildDescriptor(i,t.configDir),e=await this.resolveServiceLogPaths(i,r.service_id),s=await this.adapter.install({...this.toAdapterPayload(r),stdoutPath:e.stdoutPath,stderrPath:e.stderrPath,homeDir:this.homeDir,uid:this.uid,runCommand:n}),a={...r,definition_path:s?.definitionPath??"",installed_at:Date.now(),updated_at:Date.now()};await this.createStore(i).save(a),await this.adapter.start(this.toStartParams(a));const o=u(i);return await this.waitForDaemonStarted(o,{stdoutLogFile:e.stdoutPath,stderrLogFile:e.stderrPath}),this.status({rootDir:i})}async start(t){const i=this.normalizeRootDir(t.rootDir);await this.cleanupConflictingServices(i);const r=u(i),e=l(r.daemonStatusFile),a=await this.createStore(i).load();if(e.running&&a&&this.isDescriptorCurrent(a))return this.status({rootDir:i});const o=a?this.isDescriptorCurrent(a)&&e.running?a:await this.refreshDescriptor(i,a):await this.resolveActiveDescriptor(i),d=await this.resolveServiceLogPaths(i,o.service_id);try{e.running?await this.adapter.restart(this.toRestartParams(o)):await this.adapter.start(this.toStartParams(o))}catch(c){if(c instanceof Error&&c.message.includes("plist missing"))await this.adapter.install({...this.toAdapterPayload(o),stdoutPath:d.stdoutPath,stderrPath:d.stderrPath,homeDir:this.homeDir,uid:this.uid,runCommand:n}),await this.adapter.start(this.toStartParams(o));else throw c}return await this.waitForDaemonStarted(r,{oldPid:e.pid,stdoutLogFile:d.stdoutPath,stderrLogFile:d.stderrPath}),this.status({rootDir:i})}async stop(t){const{descriptor:i}=await this.loadDescriptor(t.rootDir),r=u(i.config_dir||this.normalizeRootDir(t.rootDir)),e=l(r.daemonStatusFile);await this.adapter.stop(this.toStopParams(i)),e.running&&e.pid&&(await v(e.pid,{timeoutMs:5e3})||(await L(e.pid,{platform:this.platform,runCommandImpl:n}),await v(e.pid,{timeoutMs:5e3})));const s=await F(i.cli_path);return s>0&&console.log(`\u6E05\u7406\u4E86 ${s} \u4E2A\u6B8B\u7559 daemon \u8FDB\u7A0B`),this.status({rootDir:t.rootDir})}async restart(t){const i=this.normalizeRootDir(t.rootDir);await this.cleanupConflictingServices(i);const r=await this.resolveActiveDescriptor(i),e=u(i),s=l(e.daemonStatusFile),a=await this.resolveServiceLogPaths(i,r.service_id);try{await this.adapter.restart(this.toRestartParams(r))}catch(o){if(o instanceof Error&&o.message.includes("plist missing"))await this.adapter.install({...this.toAdapterPayload(r),stdoutPath:a.stdoutPath,stderrPath:a.stderrPath,homeDir:this.homeDir,uid:this.uid,runCommand:n}),await this.adapter.start(this.toStartParams(r));else throw o}return await this.waitForDaemonStarted(e,{oldPid:s.pid,stdoutLogFile:a.stdoutPath,stderrLogFile:a.stderrPath}),this.status({rootDir:i})}async uninstall(t){const i=this.normalizeRootDir(t.rootDir),r=this.createStore(i),e=await r.load();return e&&(await this.adapter.uninstall(this.toUninstallParams(e)),await r.clear()),this.status({rootDir:i})}async status(t){const i=this.normalizeRootDir(t.rootDir),e=await this.createStore(i).load(),s=u(i),a=l(s.daemonStatusFile);return e?{installed:!0,install_state:this.isDescriptorCurrent(e)?"current":"stale",service_kind:this.adapter.kind,service_id:e.service_id,definition_path:e.definition_path,root_dir:i,daemon_state:a.running?"running":a.state,pid:a.pid,connection_state:a.connection_state,updated_at:a.updated_at}:{installed:!1,install_state:"missing",service_kind:this.adapter.kind,root_dir:i,daemon_state:a.running?"running":a.state,pid:a.pid}}}export{G as ServiceManager};
@@ -1 +1 @@
1
- import{createHash as u}from"node:crypto";import o from"node:os";import t from"node:path";import{normalizeString as a}from"../core/util/normalize-string.js";const i="com.dhfpub.grix-connector.daemon.",c="grix-connector-daemon-",s="GrixConnectorDaemon-";function f(r){return u("sha1").update(a(r)||"default").digest("hex").slice(0,12)}function x(r,e=process.platform){const n=f(r);return e==="win32"?`${s}${n}`:e==="darwin"?`${i}${n}`:`${c}${n}`}function h(r=process.platform){return r==="win32"?s:r==="darwin"?i:c}function l(r){return r.replace(/&amp;/g,"&").replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&quot;/g,'"')}function v(r){const e=[...r.matchAll(/<string>([^<]+)<\/string>/g)].map(n=>l(n[1]));for(let n=0;n<e.length-1;n+=1)if(e[n]==="--config-dir")return e[n+1];return null}function S(r){const e=r.match(/--config-dir\s+(?:'([^']*)'|"([^"]*)"|(\S+))/);return e?e[1]??e[2]??e[3]??null:null}function I(r){return t.join(r,"daemon-service.json")}function P(r){return t.join(r,"service","daemon.out.log")}function E(r){return t.join(r,"service","daemon.err.log")}function R(r,e){return t.join(e??o.homedir(),"Library","LaunchAgents",`${r}.plist`)}function $(r,e){return t.join(e??o.homedir(),".config","systemd","user",`${r}.service`)}export{i as SERVICE_PREFIX_DARWIN,c as SERVICE_PREFIX_LINUX,s as SERVICE_PREFIX_WIN32,x as buildServiceID,h as getServicePrefix,v as parseConfigDirFromPlistXML,S as parseConfigDirFromSystemdUnit,$ as resolveLinuxUserUnitPath,R as resolveMacOSLaunchAgentPath,I as resolveServiceInstallRecordPath,E as resolveServiceStderrPath,P as resolveServiceStdoutPath};
1
+ import{createHash as u}from"node:crypto";import o from"node:os";import t from"node:path";import{normalizeString as a}from"../core/util/normalize-string.js";const i="com.dhfpub.grix-connector.daemon.",c="grix-connector-daemon-",s="GrixConnectorDaemon-";function f(r){return u("sha1").update(a(r)||"default").digest("hex").slice(0,12)}function x(r,e=process.platform){const n=f(r);return e==="win32"?`${s}${n}`:e==="darwin"?`${i}${n}`:`${c}${n}`}function h(r=process.platform){return r==="win32"?s:r==="darwin"?i:c}function l(r){return r.replace(/&amp;/g,"&").replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&quot;/g,'"')}function v(r){const e=[...r.matchAll(/<string>([^<]+)<\/string>/g)].map(n=>l(n[1]));for(let n=0;n<e.length-1;n+=1)if(e[n]==="--config-dir")return e[n+1];return null}function S(r){const e=r.match(/--config-dir\s+(?:'([^']*)'|"([^"]*)"|(\S+))/);return e?e[1]??e[2]??e[3]??null:null}function I(r){return t.join(r,"daemon-service.json")}function P(r){return t.join(r,"service","daemon.out.log")}function E(r){return t.join(r,"service","daemon.err.log")}function $(r,e){return t.join(e??o.homedir(),"Library","LaunchAgents",`${r}.plist`)}function j(r,e){return t.join(e??o.homedir(),".config","systemd","user",`${r}.service`)}function R(r,e){return t.join(e??o.homedir(),".grix","service",`${r}.bare.json`)}export{i as SERVICE_PREFIX_DARWIN,c as SERVICE_PREFIX_LINUX,s as SERVICE_PREFIX_WIN32,x as buildServiceID,h as getServicePrefix,v as parseConfigDirFromPlistXML,S as parseConfigDirFromSystemdUnit,R as resolveBareDaemonMarkerPath,j as resolveLinuxUserUnitPath,$ as resolveMacOSLaunchAgentPath,I as resolveServiceInstallRecordPath,E as resolveServiceStderrPath,P as resolveServiceStdoutPath};
@@ -9095,13 +9095,13 @@ var TOOLS = [
9095
9095
  },
9096
9096
  {
9097
9097
  name: "grix_dispatch_agent",
9098
- description: "Dispatch one of your owner's agents to do work in a given working directory. Provide the target agent numeric ID, the working directory, and a text description of the task. The backend opens (or reuses) a private session between the owner and that agent, binds the working directory when the agent type requires it (claude/codex/etc.), and sends the task into the session as the owner so the agent starts working.",
9098
+ description: `Dispatch one of your owner's agents to do work in a given working directory. Provide the target agent numeric ID, the working directory, and a text description of the task. The backend opens (or reuses) a private session between the owner and that agent, binds the working directory when the agent type requires it (claude/codex/etc.), and sends the task into the session AS THE OWNER so the agent starts working. Because the task is delivered as the owner, write it in the owner's first-person voice and tone \u2014 phrase it the way the owner would speak directly to the agent (e.g. "\u5E2E\u6211\u2026", "\u4F60\u53BB\u2026"), NOT as a third-person relay or as yourself narrating on the owner's behalf.`,
9099
9099
  inputSchema: {
9100
9100
  type: "object",
9101
9101
  properties: {
9102
9102
  agent_id: { type: "string", description: "Target agent's numeric ID, passed as a string." },
9103
9103
  cwd: { type: "string", description: "Absolute working directory where the agent should do the work." },
9104
- task: { type: "string", description: "Text description of the task to perform." }
9104
+ task: { type: "string", description: "Text description of the task to perform, written in the owner's first-person voice and tone \u2014 it is delivered into the session as the owner, so phrase it as the owner speaking directly to the agent, not as a third-person relay." }
9105
9105
  },
9106
9106
  required: ["agent_id", "cwd", "task"]
9107
9107
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grix-connector",
3
- "version": "2.1.3",
3
+ "version": "2.2.0",
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",