grix-connector 3.33.2 → 3.34.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.
- package/dist/adapter/acp/acp-adapter.js +6 -6
- package/dist/adapter/agy/agy-adapter.js +9 -9
- package/dist/adapter/agy/model-list.js +2 -2
- package/dist/adapter/agy/quota.js +5 -5
- package/dist/adapter/claude/claude-adapter.js +21 -21
- package/dist/adapter/claude/claude-bridge-server.js +1 -0
- package/dist/adapter/claude/claude-tools.js +1 -0
- package/dist/adapter/claude/claude-worker-client.js +1 -0
- package/dist/adapter/claude/mcp-http-launcher.js +2 -0
- package/dist/adapter/claude/model-list.js +1 -1
- package/dist/adapter/claude/result-timeout.js +1 -0
- package/dist/adapter/codewhale/codewhale-adapter.js +2 -2
- package/dist/adapter/codewhale/model-list.js +2 -2
- package/dist/adapter/codex/codex-bridge.js +9 -9
- package/dist/adapter/cursor/cursor-adapter.js +7 -7
- package/dist/adapter/cursor/fetch-models.js +2 -2
- package/dist/adapter/cursor/resolve-command.js +1 -1
- package/dist/adapter/deepseek/deepseek-adapter.js +6 -0
- package/dist/adapter/deepseek/index.js +1 -0
- package/dist/adapter/deepseek-harness/jsonrpc-adapter.js +2 -2
- package/dist/adapter/opencode/opencode-adapter.js +3 -3
- package/dist/adapter/openhuman/openhuman-adapter.js +3 -3
- package/dist/adapter/qwen/index.js +1 -0
- package/dist/adapter/qwen/qwen-adapter.js +4 -0
- package/dist/aibot/client.js +1 -0
- package/dist/aibot/index.js +1 -0
- package/dist/aibot/types.js +0 -0
- package/dist/assets/dsh-bridge/{grix-dsh-bridge-3.33.2.tgz → grix-dsh-bridge-3.34.0.tgz} +0 -0
- package/dist/assets/dsh-bridge/manifest.json +5 -5
- package/dist/audit/adapters/agy/agy-audit-adapter.js +1 -0
- package/dist/audit/adapters/openclaw/openclaw-audit-adapter.js +1 -1
- package/dist/audit/adapters/pi/pi-transcript-source.js +1 -1
- package/dist/audit/core/audit-state.js +1 -1
- package/dist/audit/core/transcript-fingerprint.js +1 -0
- package/dist/audit/producer/ipc-audit-producer.js +1 -1
- package/dist/audit/worker/audit-recorder-worker-entry.js +1 -1
- package/dist/bridge/adapter-pool.js +1 -1
- package/dist/bridge/bridge.js +9 -9
- package/dist/bridge/cli-install-offer.js +1 -0
- package/dist/bridge/deferred-events.js +1 -1
- package/dist/bridge/lifecycle-barrier.js +1 -0
- package/dist/bridge/pending-event-coordinator.js +1 -0
- package/dist/bridge/respawn-manager.js +1 -1
- package/dist/core/access/allowlist-gate.js +1 -0
- package/dist/core/access/allowlist-store.js +1 -0
- package/dist/core/access/index.js +1 -0
- package/dist/core/aibot/client.js +2 -2
- package/dist/core/file-ops/handler.js +1 -0
- package/dist/core/file-ops/list-files.js +1 -0
- package/dist/core/file-ops/types.js +0 -0
- package/dist/core/files/file-serve.js +1 -1
- package/dist/core/installer/installer.js +6 -6
- package/dist/core/installer/npm-registry.js +3 -3
- package/dist/core/installer/prereq-installer.js +1 -1
- package/dist/core/observability/sentry.js +1 -1
- package/dist/core/persistence/pending-event-store.js +1 -0
- package/dist/core/protocol/interaction-parser.js +1 -1
- package/dist/core/proxy/hermes-profile-relay.js +1 -1
- package/dist/core/proxy/hermes-profile-relay.py +34 -15
- package/dist/core/proxy/relay-state-sync.js +1 -1
- package/dist/core/runtime/spawn.js +2 -3
- package/dist/core/upgrade/npm-upgrader.js +2 -2
- package/dist/core/upgrade/upgrade-checker.js +1 -1
- package/dist/core/util/cli-probe.js +2 -2
- package/dist/core/util/git-project-root.js +1 -1
- package/dist/default-skills/grix-agent-dispatch/SKILL.md +19 -9
- package/dist/default-skills/grix-egg/SKILL.md +2 -2
- package/dist/default-skills/message-send/SKILL.md +1 -1
- package/dist/default-skills/message-unsend/SKILL.md +1 -1
- package/dist/grix.js +0 -0
- package/dist/log.js +3 -0
- package/dist/main.js +31 -0
- package/dist/manager-share-config.js +1 -1
- package/dist/manager.js +2 -2
- package/dist/mcp/stream-http/config.js +1 -0
- package/dist/mcp/stream-http/connection-binding.js +1 -0
- package/dist/mcp/stream-http/event-tool-executor.js +1 -0
- package/dist/mcp/stream-http/gateway.js +1 -0
- package/dist/mcp/stream-http/index.js +1 -0
- package/dist/mcp/stream-http/security.js +1 -0
- package/dist/mcp/stream-http/session-manager.js +1 -0
- package/dist/mcp/stream-http/tool-executor.js +1 -0
- package/dist/mcp/stream-http/tool-registry.js +1 -0
- package/dist/mcp/stream-http/tool-schemas.js +1 -0
- package/dist/service/process-control.js +1 -1
- package/dist/session/index.js +1 -0
- package/dist/session/manager.js +1 -0
- package/dist/transport/index.js +1 -0
- package/dist/transport/json-rpc.js +3 -0
- package/openclaw-plugin/audit-recorder-worker.js +531 -76
- package/openclaw-plugin/index.js +89 -14
- package/package.json +1 -1
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import{spawn as f}from"node:child_process";import{log as d}from"../log/logger.js";import{checkPrerequisite as b}from"./preflight.js";const u=64*1024,h={node:{id:"node",label:"Node.js",macos:"NONINTERACTIVE=1 CI=1 brew install node 2>&1 && brew link node --overwrite 2>/dev/null; command -v node >/dev/null 2>&1 || (npm install -g n 2>&1 && n lts 2>&1)",linux:'curl -fsSL https://fnm.vercel.app/install | bash -s -- --install-latest-lts --skip-shell 2>&1 && eval "$(fnm env)" && fnm install --lts 2>&1 && fnm use --lts 2>&1',windows:"winget install Schniz.fnm --accept-source-agreements --accept-package-agreements",mode:"shell",timeoutMs:18e4},npm:{id:"npm",label:"npm",macos:null,linux:null,windows:null,mode:"exec",timeoutMs:6e4},curl:{id:"curl",label:"curl",macos:null,linux:'sudo apt-get install -y -qq curl 2>/dev/null || sudo dnf install -y curl 2>/dev/null || sudo pacman -S --noconfirm curl 2>/dev/null || echo "curl install skipped: no supported package manager"',windows:null,mode:"shell",timeoutMs:6e4},gh:{id:"gh",label:"GitHub CLI",macos:"NONINTERACTIVE=1 brew install gh 2>/dev/null",linux:`(command -v apt-get >/dev/null 2>&1 && (sudo mkdir -p /etc/apt/keyrings 2>/dev/null; wget -qO- https://cli.github.com/packages/githubcli-archive-keyring.gpg 2>/dev/null | sudo tee /etc/apt/keyrings/githubcli-archive-keyring.gpg >/dev/null 2>&1; echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list >/dev/null 2>&1; sudo apt-get update -qq 2>/dev/null; sudo apt-get install -y -qq gh 2>/dev/null)) || (command -v dnf >/dev/null 2>&1 && sudo dnf install -y 'dnf-command(config-manager)' 2>/dev/null && sudo dnf config-manager --add-repo https://cli.github.com/packages/rpm/gh-cli.repo 2>/dev/null && sudo dnf install -y gh 2>/dev/null) || echo "gh install skipped: no supported package manager"`,windows:"winget install GitHub.cli --accept-source-agreements --accept-package-agreements",mode:"shell",timeoutMs:18e4},brew:{id:"brew",label:"Homebrew",macos:'NONINTERACTIVE=1 CI=1 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"',linux:'NONINTERACTIVE=1 CI=1 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"',windows:null,mode:"shell",timeoutMs:3e5}};async function w(e,t){const i=h[e.id];if(!i)return{ok:!1,output:`No install spec for ${e.id}`};const n=i[t];if(!n){const l=g(e.id,t);return{ok:!1,output:`${e.label} cannot be auto-installed on ${t}.${l?` Please install manually: ${l}`:""}`}}d.info("installer",`Installing prerequisite ${e.label}: ${n}`);try{const l=await k(n,i.timeoutMs);return d.info("installer",`Prerequisite ${e.label} installed successfully`),{ok:!0,output:l}}catch(l){const a=l instanceof Error?l.message:String(l);d.error("installer",`Failed to install prerequisite ${e.label}: ${a}`);const c=g(e.id,t),o=e.id==="brew"&&(a.includes("code 1")||a.includes("sudo"))?`
|
|
2
2
|
Note: Homebrew installation may require administrator (sudo) password. Try running: NONINTERACTIVE=1 CI=1 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" in your terminal.`:"";return{ok:!1,output:`${a}${o}${c?`
|
|
3
|
-
Manual install: ${c}`:""}`}}}async function T(e,t){const i=[];for(const n of e){const l=await w(n,t);if(i.push({prereq:n,ok:l.ok,output:l.output}),!l.ok)return{allOk:!1,results:i};if(!(await b(n.id,t)).met)return i.push({prereq:{...n,met:!1},ok:!1,output:`${n.label} install command succeeded but verification failed. This usually means you need to open a new terminal or run: source ~/.bashrc (or ~/.zshrc)`}),{allOk:!1,results:i}}return{allOk:!0,results:i}}function E(e,t){return h[e]?.[t]??null}function g(e,t){switch(e){case"node":return t==="windows"?"Install Node.js LTS from https://nodejs.org or run: winget install OpenJS.NodeJS.LTS":"Install Node.js LTS from https://nodejs.org or run: brew install node (macOS) / follow instructions at https://fnm.vercel.app (Linux)";case"npm":return"npm comes bundled with Node.js. Install Node.js first.";case"curl":return t==="windows"?"curl is built into Windows 10+. For older versions, install from https://curl.se/windows/":t==="macos"?"curl is pre-installed on macOS. If missing, install Xcode Command Line Tools: xcode-select --install":"Install curl: sudo apt install curl (Debian/Ubuntu) or sudo dnf install curl (Fedora/RHEL)";case"gh":return"Install GitHub CLI from https://cli.github.com or follow instructions at https://github.com/cli/cli#installation";case"brew":return'Install Homebrew from https://brew.sh or run: /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"';default:return null}}function k(e,t){return new Promise((i,n)=>{const l=process.platform==="win32",o=f(l?"cmd.exe":"sh",l?["/d","/s","/c",e]:["-c",e],{timeout:t,stdio:["ignore","pipe","pipe"],env:{...process.env,NONINTERACTIVE:"1",DEBIAN_FRONTEND:"noninteractive"}});let s="",m=!1;const p=setTimeout(()=>{m=!0,o.kill("SIGTERM"),setTimeout(()=>{try{o.kill("SIGKILL")}catch{}},5e3).unref()},t);o.stdout?.on("data",r=>{s+=r.toString("utf-8"),s.length>u&&(s=s.slice(-u))}),o.stderr?.on("data",r=>{s+=r.toString("utf-8"),s.length>u&&(s=s.slice(-u))}),o.on("error",r=>{clearTimeout(p),n(r)}),o.on("close",r=>{if(clearTimeout(p),s.length>u&&(s=s.slice(-u)),m){n(new Error(`Timed out after ${t/1e3}s`));return}if(r!==0){n(new Error(`Process exited with code ${r}: ${s.slice(-1024)}`));return}i(s.trim())})})}export{E as getPrereqInstallCommand,g as getPrereqManualInstructions,T as installMissingPrerequisites,w as installPrerequisite};
|
|
3
|
+
Manual install: ${c}`:""}`}}}async function T(e,t){const i=[];for(const n of e){const l=await w(n,t);if(i.push({prereq:n,ok:l.ok,output:l.output}),!l.ok)return{allOk:!1,results:i};if(!(await b(n.id,t)).met)return i.push({prereq:{...n,met:!1},ok:!1,output:`${n.label} install command succeeded but verification failed. This usually means you need to open a new terminal or run: source ~/.bashrc (or ~/.zshrc)`}),{allOk:!1,results:i}}return{allOk:!0,results:i}}function E(e,t){return h[e]?.[t]??null}function g(e,t){switch(e){case"node":return t==="windows"?"Install Node.js LTS from https://nodejs.org or run: winget install OpenJS.NodeJS.LTS":"Install Node.js LTS from https://nodejs.org or run: brew install node (macOS) / follow instructions at https://fnm.vercel.app (Linux)";case"npm":return"npm comes bundled with Node.js. Install Node.js first.";case"curl":return t==="windows"?"curl is built into Windows 10+. For older versions, install from https://curl.se/windows/":t==="macos"?"curl is pre-installed on macOS. If missing, install Xcode Command Line Tools: xcode-select --install":"Install curl: sudo apt install curl (Debian/Ubuntu) or sudo dnf install curl (Fedora/RHEL)";case"gh":return"Install GitHub CLI from https://cli.github.com or follow instructions at https://github.com/cli/cli#installation";case"brew":return'Install Homebrew from https://brew.sh or run: /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"';default:return null}}function k(e,t){return new Promise((i,n)=>{const l=process.platform==="win32",o=f(l?"cmd.exe":"sh",l?["/d","/s","/c",e]:["-c",e],{timeout:t,stdio:["ignore","pipe","pipe"],windowsHide:!0,env:{...process.env,NONINTERACTIVE:"1",DEBIAN_FRONTEND:"noninteractive"}});let s="",m=!1;const p=setTimeout(()=>{m=!0,o.kill("SIGTERM"),setTimeout(()=>{try{o.kill("SIGKILL")}catch{}},5e3).unref()},t);o.stdout?.on("data",r=>{s+=r.toString("utf-8"),s.length>u&&(s=s.slice(-u))}),o.stderr?.on("data",r=>{s+=r.toString("utf-8"),s.length>u&&(s=s.slice(-u))}),o.on("error",r=>{clearTimeout(p),n(r)}),o.on("close",r=>{if(clearTimeout(p),s.length>u&&(s=s.slice(-u)),m){n(new Error(`Timed out after ${t/1e3}s`));return}if(r!==0){n(new Error(`Process exited with code ${r}: ${s.slice(-1024)}`));return}i(s.trim())})})}export{E as getPrereqInstallCommand,g as getPrereqManualInstructions,T as installMissingPrerequisites,w as installPrerequisite};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{log as i}from"../log/logger.js";import{resolveClientVersion as g}from"../util/client-version.js";let s=null,a=!1;const
|
|
1
|
+
import{log as i}from"../log/logger.js";import{resolveClientVersion as g}from"../util/client-version.js";let s=null,a=!1;const E="https://e8e202d7625372b1314b3ff4e85a7ff9@o119262.ingest.us.sentry.io/4511410543329280";function l(){if(!process.env.GRIX_SENTRY_DISABLE)return process.env.SENTRY_DSN||process.env.GRIX_SENTRY_DSN||E}async function I(){const e=l();if(e){try{s=await import("@sentry/node")}catch(n){i.warn("sentry",`Sentry \u6A21\u5757\u52A0\u8F7D\u5931\u8D25\uFF08\u5E73\u53F0\u4E0D\u517C\u5BB9\uFF09\uFF0C\u9519\u8BEF\u4E0A\u62A5\u5DF2\u7981\u7528: ${n instanceof Error?n.message:String(n)}`);return}try{s.init({dsn:e,release:`grix-connector@${g()}`,environment:process.env.SENTRY_ENVIRONMENT||process.env.NODE_ENV||"production",sendDefaultPii:!1,initialScope:{tags:{component:"grix-connector"}},defaultIntegrations:!1,tracesSampleRate:0,beforeSend(n){const r=u(n);return r&&c(r)?null:n}}),a=!0}catch(n){i.error("sentry",`Sentry \u521D\u59CB\u5316\u5931\u8D25: ${n instanceof Error?n.message:String(n)}`)}}}function u(e){if(typeof e.message=="string"&&e.message)return e.message;const n=e.exception?.values;if(!n?.length)return"";const r=n[0],o=typeof r?.value=="string"?r.value:"",t=typeof r?.type=="string"?r.type:"";return o||t}function d(){return a}const S=new Set(["INSTALL_FAILED","INSTALL_TIMEOUT","FALLBACK_EXHAUSTED","PREREQ_INSTALL_FAILED","VERIFICATION_FAILED","ENVIRONMENT_UNSUPPORTED","INTERNAL"]);function y(e){if(e.ok)return!1;const n=e.error?.code;return!!n&&S.has(n)}function R(e){if(!a||!s||!y(e))return;const n=e.error?.code??"INTERNAL",r=e.error?.message??"unknown install failure",o=e.environment;try{s.withScope(t=>{t.setLevel("error"),t.setTags({agent_type:e.agentType,error_code:n,phase:e.phase}),t.setContext("install",{agentType:e.agentType,code:n,phase:e.phase,durationMs:e.durationMs,os:o?.platform,osVersion:o?.osVersion,arch:o?.arch,nodeVersion:o?.nodeVersion,npmVersion:o?.npmVersion,isDocker:o?.isDocker,isCI:o?.isCI,outputTail:(e.output??"").slice(-2e3)}),t.setFingerprint(["agent-install-failure",e.agentType,n]),s.captureException(new Error(`Agent install failed [${e.agentType}/${n}]: ${r}`))}),i.info("sentry",`\u5DF2\u4E0A\u62A5\u5B89\u88C5\u5931\u8D25: ${e.agentType}/${n}`)}catch(t){i.error("sentry",`\u4E0A\u62A5\u5B89\u88C5\u5931\u8D25\u65F6\u51FA\u9519: ${t instanceof Error?t.message:String(t)}`)}}const N=[/No config files found/,/spawn wmic ENOENT/i];function c(e){const n=e instanceof Error?e.message:String(e);return N.some(r=>r.test(n))}const p=new Set;function _(e,n){if(!a||!s||c(e))return;const r=e instanceof Error?e.message:String(e),o=`${n}:${r}`;if(!p.has(o)){p.add(o);try{s.withScope(t=>{t.setLevel("fatal"),t.setTag("crash_type",n);const f=e instanceof Error?e:new Error(String(e));s.captureException(f)}),i.info("sentry",`\u5DF2\u4E0A\u62A5\u5D29\u6E83: ${n}`)}catch(t){i.error("sentry",`\u4E0A\u62A5\u5D29\u6E83\u65F6\u51FA\u9519: ${t instanceof Error?t.message:String(t)}`)}}}async function h(e=2e3){if(!(!a||!s))try{await s.close(e)}catch{}}export{h as closeSentry,u as extractEventMessage,I as initSentry,c as isNonReportable,d as isSentryEnabled,_ as reportFatal,R as reportInstallFailure,y as shouldReportInstallFailure};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{readJSONFile as s,writeJSONFileAtomic as a}from"../util/json-file.js";import{log as c}from"../log/index.js";import{unlink as d}from"node:fs/promises";class y{filePath;writeQueue=Promise.resolve();constructor(e){this.filePath=e}async update(e){let n=[];const r=this.writeQueue.catch(()=>{}).then(async()=>{const o=this.loadNow();n=e(o),await this.saveNow(n)});return this.writeQueue=r,await r,n}async saveNow(e){if(e.length===0){await this.remove();return}await a(this.filePath,e),c.info("pending-event-store",`Saved ${e.length} pending event(s)`)}async load(){return this.loadNow()}loadNow(){const e=s(this.filePath);return Array.isArray(e)?e.flatMap(f):[]}async remove(){try{await d(this.filePath)}catch{}}}function f(t){if(!t||typeof t!="object")return[];const e=t;return e.kind==="queued"&&i(e.event)?[{kind:"queued",event:e.event}]:e.kind==="deferred"&&h(e.channel)&&typeof e.sessionId=="string"&&i(e.event)?[{kind:"deferred",channel:e.channel,sessionId:e.sessionId,event:e.event}]:i(t)?[{kind:"queued",event:t}]:[]}function h(t){return t==="claude"||t==="codex"||t==="cursor"||t==="acp"||t==="pi"||t==="openhuman"||t==="codewhale"||t==="opencode"||t==="agy"||t==="deepseek-harness"}function i(t){if(!t||typeof t!="object")return!1;const e=t;return typeof e.event_id=="string"&&typeof e.session_id=="string"&&typeof e.content=="string"}export{y as PendingEventStore};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const
|
|
1
|
+
const h="grix://card/",I="grix://open/",R="_submit",U=/\(\s*(grix:\/\/card\/[\s\S]+?)\s*\)/gi,A=/grix:\/\/card\/[^\s)\]]+/gi,x=/^grix:\/\/open\/\S+$/i;function b(e){const n=[],t=new Set;for(const r of E(e)){const s=v(r);!s||t.has(s.normalizedUri)||(t.add(s.normalizedUri),n.push(s))}return{cardSubmissions:n}}function N(e,n){return g(e.params.get(n))}function O(e){const n=_(f(e).trim());if(!n.startsWith(h))return null;let t;try{t=new URL(n)}catch{return null}if(t.hostname!=="card"||t.pathname.replace(/^\/+/,"")!=="agent_question_reply")return null;const r=g(t.searchParams.get("d"));if(!r)return null;let s;try{const i=JSON.parse(r);if(!i||typeof i!="object"||Array.isArray(i))return null;s=i}catch{return null}const a=d(s.request_id);if(!a)return null;const o=d(s.action);if(o)return o!=="accept"&&o!=="cancel"?null:{request_id:a,action:o};const c=C(s.response),u=d(c?.type);if(u==="single"){const i=d(c?.value);return i?{request_id:a,response:{type:"single",value:i}}:null}if(u==="map"){const i=Array.isArray(c?.entries)?c.entries:[],l=[];for(const m of i){const p=C(m),y=d(p?.key),S=d(p?.value);if(!y||!S)return null;l.push({key:y,value:S})}return l.length>0?{request_id:a,response:{type:"map",entries:l}}:null}return null}function W(e){const n=f(e).trim(),t=/^\[\[exec-approval-resolution\|([\s\S]+)\]\]$/.exec(n);if(!t)return null;const r=new Map;for(const i of t[1].split("|")){const l=i.indexOf("=");if(l<=0)continue;const m=i.slice(0,l).trim(),p=g(i.slice(l+1));m&&p&&r.set(m,p)}const s=r.get("decision");if(s!=="allow-once"&&s!=="allow-always"&&s!=="deny")return null;const a=r.get("approval_id"),o=r.get("approval_command_id"),c=r.get("tool_call_id"),u=r.get("exec_context_id");return!a&&!o&&!c&&!u?null:{action_type:s==="deny"?"exec_reject":"exec_approve",params:{...a?{approval_id:a}:{},...o?{approval_command_id:o}:{},...c?{tool_call_id:c}:{},...u?{exec_context_id:u}:{},decision:s}}}function z(e){return x.test(f(e).trim())}function E(e){const n=f(e),t=[];for(const a of n.matchAll(U)){const o=_(a[1]);o&&t.push(o)}for(const a of n.matchAll(A)){const o=_(a[0]);o&&t.push(o)}const r=n.trim();x.test(r)&&t.push(r);const s=_(r);return s.startsWith(h)&&t.push(s),P(t)}function v(e){if(e.startsWith(I))return w(e);if(!e.startsWith(h))return null;let n;try{n=new URL(e)}catch{return null}if(n.hostname!=="card")return null;const t=n.pathname.replace(/^\/+/,"").trim();if(!t.endsWith(R))return null;const r=t.slice(0,-R.length).trim();return!r||r==="agent_open_session"?null:{cardId:r,rawUri:e,normalizedUri:n.toString(),params:n.searchParams}}function w(e){let n;try{n=new URL(e)}catch{return null}if(n.hostname!=="open")return null;const t=n.pathname.split("/").map(r=>r.trim()).filter(Boolean);return t.length!==1||t[0]!=="session"?null:{cardId:"agent_open_session",rawUri:e,normalizedUri:n.toString(),params:n.searchParams}}function f(e){return e.replace(/&/gi,"&")}function _(e){return e.replace(/\s+/g,"").trim()}function P(e){const n=[...new Set(e)];return n.filter(t=>!n.some(r=>r!==t&&r.startsWith(t)))}function g(e){if(!e)return;let n=e.trim();for(let t=0;t<3;t++)try{const r=decodeURIComponent(n);if(r===n)break;n=r}catch{break}return n.trim()||void 0}function C(e){return!e||typeof e!="object"||Array.isArray(e)?null:e}function d(e){return String(e??"").trim()||void 0}export{z as isOpenSessionDirectiveMessage,O as parseAgentQuestionReplyMessage,W as parseExecApprovalResolutionMessage,b as parseInteractionMessage,N as readCardSubmissionParam};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{execFile as h}from"node:child_process";import{existsSync as a}from"node:fs";import{readFile as
|
|
1
|
+
import{execFile as h}from"node:child_process";import{existsSync as a}from"node:fs";import{readFile as w}from"node:fs/promises";import{homedir as f}from"node:os";import{delimiter as g,dirname as S,join as l}from"node:path";import{fileURLToPath as I}from"node:url";const R=3e4,v="hermes-profile-relay.py";class i extends Error{code;constructor(o,n){super(o),this.code=n,this.name="HermesProfileRelayError"}}function E(){return l(S(I(import.meta.url)),v)}function P(){const e=(process.env.PATH??"").split(g).filter(Boolean),o=process.platform==="win32"?["hermes.exe","hermes.cmd","hermes"]:["hermes"];for(const n of e)for(const r of o){const t=l(n,r);if(a(t))return t}}async function A(){const e=process.env.GRIX_HERMES_PYTHON?.trim();if(e){if(!a(e))throw new i(`GRIX_HERMES_PYTHON points to a missing interpreter: ${e}`,"HERMES_NOT_FOUND");return e}const o=P();if(o)try{const r=(await w(o,"utf8")).slice(0,512),t=/^#!\s*(\S+)/.exec(r)?.[1];if(t&&a(t))return t}catch{}const n=l(f(),".hermes","hermes-agent",".venv","bin","python3");if(a(n))return n;throw new i("cannot locate hermes' python interpreter (set GRIX_HERMES_PYTHON to override)","HERMES_NOT_FOUND")}async function d(e){const o=await A(),n=E();if(!a(n))throw new i(`relay script is missing: ${n}`,"SCRIPT_MISSING");const r=await new Promise((m,_)=>{h(o,[n],{timeout:R,cwd:f(),maxBuffer:8388608,windowsHide:!0},(u,s,y)=>{if(s&&s.trim())return m(s);if(u)return _(new i(`hermes relay script failed: ${String(y||u.message).slice(0,400)}`,"SCRIPT_FAILED"));m(s)}).stdin?.end(JSON.stringify(e))});let t;try{t=JSON.parse(r.trim())}catch{throw new i(`hermes relay script returned non-JSON output: ${r.slice(0,200)}`,"SCRIPT_FAILED")}if(!t.ok)throw new i(String(t.error_msg??"hermes relay script reported failure"),String(t.error_code??"SCRIPT_FAILED"));return t}function c(e){return typeof e=="boolean"?e:void 0}function p(e){return{profile:String(e.profile??""),home:String(e.home??""),gatewayRunning:e.gateway_running===!0,restartRequired:e.restart_required===!0,providerKept:c(e.provider_kept),restoredModel:c(e.restored_model),userSwitchedAway:c(e.user_switched_away),modelCleared:c(e.model_cleared),warning:typeof e.warning=="string"?e.warning:void 0,snapshot:typeof e.snapshot=="string"?e.snapshot:void 0}}async function G(){const e=await d({command:"discover"}),o=Array.isArray(e.profiles)?e.profiles:[],n=Array.isArray(e.unreadable)?e.unreadable:[];return{profiles:o.map(r=>N(r)),unreadable:n.map(r=>{const t=r;return{name:String(t.name??""),home:String(t.home??""),error:String(t.error??"")}})}}function N(e){return{name:String(e.name??""),home:String(e.home??""),agentId:String(e.agent_id??""),gatewayRunning:e.gateway_running===!0,relayActive:e.relay_active===!0,modelProvider:typeof e.model_provider=="string"?e.model_provider:void 0,modelDefault:typeof e.model_default=="string"?e.model_default:void 0}}async function L(){const e=await d({command:"discover"});return(Array.isArray(e.profiles)?e.profiles:[]).map(n=>{const r=n;return{name:String(r.name??""),home:String(r.home??""),agentId:String(r.agent_id??""),gatewayRunning:r.gateway_running===!0,relayActive:r.relay_active===!0,modelProvider:typeof r.model_provider=="string"?r.model_provider:void 0,modelDefault:typeof r.model_default=="string"?r.model_default:void 0}})}async function U(e){const o=e.agentId?.trim(),n=e.baseUrl?.trim(),r=e.virtualKey?.trim(),t=e.model?.trim();if(!o)throw new i("agent_id is required","MISSING_AGENT_ID");if(!r)throw new i("virtual_key is required","MISSING_VIRTUAL_KEY");if(!n)throw new i("openai_base_url is required","MISSING_BASE_URL");try{new URL(n)}catch{throw new i("openai_base_url must be a valid URL","INVALID_BASE_URL")}if(!t)throw new i("model is required for hermes","MISSING_MODEL");return p(await d({command:"apply",agent_id:o,base_url:n,api_key:r,model:t}))}async function q(e){const o=e?.trim();if(!o)throw new i("agent_id is required","MISSING_AGENT_ID");return p(await d({command:"clear",agent_id:o}))}export{i as HermesProfileRelayError,U as applyHermesProfileRelay,q as clearHermesProfileRelay,L as discoverHermesProfiles,G as discoverHermesProfilesRaw,A as resolveHermesPython};
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
与连接器托管的 agent 不同:独立 Hermes 是用户自己跑的 gateway 进程,连接器既不 spawn 它、
|
|
5
5
|
也不在 agents.json 里认识它,因此没有"spawn 时注入 env"这一步——只能直接改它 profile 里的
|
|
6
6
|
config.yaml。目标 profile **不靠传路径、靠 agent_id 认**:每个接了 Grix 的 profile 都在
|
|
7
|
-
``channels.grix.wsUrl
|
|
7
|
+
``channels.grix.wsUrl``(旧版)或 ``platforms.grix.extra.wsUrl``(新版)里带着自己的
|
|
8
|
+
``agent_id``,用它跟服务端下发的 agent_id 对上号。
|
|
8
9
|
|
|
9
10
|
**为什么必须用 Hermes 自己的解释器跑**(而不是在连接器里用 Node 解析 YAML):
|
|
10
11
|
配置的读写全交给宿主的 ``hermes_cli.config``,它负责原子写(temp+fsync+replace)、保留原
|
|
@@ -81,21 +82,39 @@ def _redact(text: str, req: Dict[str, Any]) -> str:
|
|
|
81
82
|
|
|
82
83
|
|
|
83
84
|
def _agent_id_of(cfg: Dict[str, Any]) -> str:
|
|
84
|
-
"""
|
|
85
|
+
"""从旧/新版 Grix WebSocket 配置中取 profile 的唯一 agent_id。"""
|
|
86
|
+
urls: List[str] = []
|
|
87
|
+
|
|
85
88
|
channels = cfg.get("channels")
|
|
86
|
-
if
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
89
|
+
if isinstance(channels, dict):
|
|
90
|
+
grix = channels.get(GRIX_PROVIDER_ID)
|
|
91
|
+
if isinstance(grix, dict):
|
|
92
|
+
ws_url = grix.get("wsUrl")
|
|
93
|
+
if isinstance(ws_url, str) and ws_url.strip():
|
|
94
|
+
urls.append(ws_url)
|
|
95
|
+
|
|
96
|
+
platforms = cfg.get("platforms")
|
|
97
|
+
if isinstance(platforms, dict):
|
|
98
|
+
grix = platforms.get(GRIX_PROVIDER_ID)
|
|
99
|
+
if isinstance(grix, dict):
|
|
100
|
+
extra = grix.get("extra")
|
|
101
|
+
if isinstance(extra, dict):
|
|
102
|
+
ws_url = extra.get("wsUrl")
|
|
103
|
+
if isinstance(ws_url, str) and ws_url.strip():
|
|
104
|
+
urls.append(ws_url)
|
|
105
|
+
|
|
106
|
+
agent_ids = set()
|
|
107
|
+
for ws_url in urls:
|
|
108
|
+
try:
|
|
109
|
+
values = parse_qs(urlparse(ws_url).query).get("agent_id") or []
|
|
110
|
+
except Exception:
|
|
111
|
+
continue
|
|
112
|
+
for value in values:
|
|
113
|
+
agent_id = str(value).strip()
|
|
114
|
+
if agent_id:
|
|
115
|
+
agent_ids.add(agent_id)
|
|
116
|
+
|
|
117
|
+
return next(iter(agent_ids)) if len(agent_ids) == 1 else ""
|
|
99
118
|
|
|
100
119
|
|
|
101
120
|
def _config_path(home: str) -> str:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{fetchRelayCredentialWithRetry as l,RelayFetchError as c}from"./relay-credential-fetch.js";const
|
|
1
|
+
import{fetchRelayCredentialWithRetry as l,RelayFetchError as c}from"./relay-credential-fetch.js";const d=15e3;function a(n){return n instanceof Error?n.message:String(n)}function o(n){if(n instanceof c)return n.bizCode?.trim()||n.code;const e=n?.code;return typeof e=="string"&&e.trim()?e.trim():"INTERNAL"}class u{ports;lastRevision=null;applyingFromServer=0;chain=Promise.resolve();constructor(e){this.ports=e}get knownRevision(){return this.lastRevision}syncOnConnect(){return this.enqueue(()=>this.doSync())}applyFromLocalAction(e){return this.enqueue(()=>this.doApply(e))}reportLocalChange(){if(this.applyingFromServer>0)return;const e=this.lastRevision;e!=null&&this.safeReport({applied:this.currentApplied(),revision:e})}enqueue(e){const t=this.chain.catch(()=>{}).then(e);return this.chain=t.then(()=>{},()=>{}),t}async doSync(){const e=this.ports.getLocalState();let t;try{t=await this.ports.sendSyncRequest({local_enabled:e.enabled,...e.model?{local_model:e.model}:{}},d)}catch(i){this.ports.log.warn(`relay_state_sync_request not answered: ${a(i)}`);return}if(t.cmd==="error"){const i=t.payload??{};this.ports.log.warn(`relay_state_sync_request rejected by server: code=${i.code??"?"} msg=${i.msg??""}`);return}const r=t.payload??{};if(r.status!=="ok"){this.ports.log.warn(`relay_state_sync_result failed: ${r.error_code??""} ${r.error_msg??""}`.trim());return}const s=Number(r.revision);if(!Number.isFinite(s)){this.ports.log.warn("relay_state_sync_result missing/invalid revision; ignored");return}this.lastRevision=s;try{r.enabled?await this.ensureEnabled(r.model?.trim()||void 0,r.credential):await this.ensureDisabled()}catch(i){this.ports.log.warn(`relay state apply after sync failed: ${a(i)}`),this.safeReport({applied:this.currentApplied(),revision:s,error_code:o(i)});return}this.safeReport({applied:this.currentApplied(),revision:s})}async doApply(e){const t=e.revision;this.lastRevision=t;const r=e.model?.trim()||void 0;try{return e.enabled?await this.ensureEnabled(r,void 0):await this.ensureDisabled(),{ok:!0,revision:t}}catch(s){return this.ports.log.warn(`apply_relay_state failed: ${a(s)}`),{ok:!1,error_code:o(s),error_msg:a(s),revision:t}}}async ensureEnabled(e,t){const r=t?.api_key?.trim();if(r){await this.guarded(()=>this.ports.applyEnable({apiKey:r,anthropicBaseUrl:t?.anthropic_base_url?.trim()||void 0,openaiBaseUrl:t?.openai_base_url?.trim()||void 0,model:e,...t?.direct_relay!==void 0?{directRelay:t.direct_relay}:{}},e));return}const s=this.ports.getLocalState();if(s.enabled&&(!e||!s.model||s.model===e))return;const i=await l(this.ports.sendCredentialRequest,e?{model:e}:{});await this.guarded(()=>this.ports.applyEnable(i,e??i.model))}async ensureDisabled(){this.ports.getLocalState().enabled&&await this.guarded(()=>this.ports.applyDisable())}async guarded(e){this.applyingFromServer++;try{await e()}finally{this.applyingFromServer--}}currentApplied(){try{return this.ports.getLocalState().enabled}catch{return!1}}safeReport(e){try{this.ports.sendReport(e)}catch(t){this.ports.log.warn(`relay_state_report not sent: ${a(t)}`)}}}export{u as RelayStateSyncer};
|
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
import{accessSync as
|
|
2
|
-
`).
|
|
3
|
-
`).forEach(o=>{const s=parseInt(o.trim(),10);!isNaN(s)&&s>0&&r.push(s)})}),e.on("close",()=>n(r)),e.on("error",()=>n([]))})}async function g(t,n=[]){return(await C(t)).some(r=>!n.includes(r))}function v(t,n="SIGTERM"){if(t.pid){if(process.platform==="win32"){try{w(`taskkill /pid ${t.pid} /T /F`,{stdio:"pipe",timeout:5e3})}catch{t.kill(n)}return}try{process.kill(-t.pid,n)}catch{t.kill(n)}}}export{g as hasChildProcesses,v as killProcessGroup,C as listGroupPids,b as resolveCommandPath,k as spawnCommand};
|
|
1
|
+
import{accessSync as l,constants as w}from"node:fs";import{spawn as p,execSync as h}from"node:child_process";import{isAbsolute as P,join as m}from"node:path";const S=process.platform==="win32"?";":":";function C(t){try{return l(t,w.X_OK),!0}catch{return!1}}function H(t,e){const n=String(t??"").trim();if(!n||P(n))return n;const i=process.platform==="win32",r=String(e??process.env.PATH??""),s=[],o=new Set;for(const c of r.split(S)){const d=c.trim();if(!d)continue;if(i){const u=m(d,n+".cmd");o.has(u)||(o.add(u),s.push(u))}const a=m(d,n);o.has(a)||(o.add(a),s.push(a))}for(const c of s)if(C(c))return c;return console.warn(`[spawn:resolve] command="${n}" not found in PATH. candidates tried: [${s.join(", ")}] PATH=${r.substring(0,500)}${r.length>500?"...(truncated)":""}`),n}function N(t,e,n={}){const{cwd:i,env:r,...s}=n,o={stdio:s.stdio??["pipe","pipe","pipe"],env:r??{...process.env},cwd:i,windowsHide:!0,...s};if(process.platform==="win32"&&/\.(cmd|bat)$/i.test(t)){const d=process.env.ComSpec||"cmd.exe",a=`""${t}" ${e.join(" ")}"`;return{process:p(d,["/d","/s","/c",a],{...o,windowsVerbatimArguments:!0}),isCmdSpawn:!0}}return{process:p(t,e,{...o,detached:o.detached??process.platform!=="win32"}),isCmdSpawn:!1}}async function $(t){return!t||t<=0?[]:process.platform==="win32"?y(t):new Promise(e=>{const n=p("pgrep",["-g",String(t)],{stdio:"pipe",timeout:3e3}),i=[];n.stdout?.on("data",r=>{r.toString().split(`
|
|
2
|
+
`).forEach(s=>{const o=parseInt(s.trim(),10);!isNaN(o)&&o>0&&i.push(o)})}),n.on("close",()=>e(i)),n.on("error",()=>e([]))})}function y(t){return new Promise(e=>{const n=s=>{e(s.split(/\r?\n/).map(o=>parseInt(o.trim(),10)).filter(o=>!isNaN(o)&&o>0))},i=p("wmic",["process","where",`(ParentProcessId=${t})`,"get","ProcessId"],{stdio:"pipe",windowsHide:!0});let r="";i.stdout?.on("data",s=>{r+=s.toString()}),i.on("close",s=>{if(s===0){n(r);return}f(t).then(e)}),i.on("error",()=>{f(t).then(e)})})}function f(t){return new Promise(e=>{const n=`${process.env.SystemRoot||"C:\\Windows"}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`,i=p(n,["-NoProfile","-NonInteractive","-WindowStyle","Hidden","-Command",`(Get-CimInstance Win32_Process -Filter 'ParentProcessId=${t}').ProcessId`],{stdio:"pipe",windowsHide:!0});let r="";i.stdout?.on("data",s=>{r+=s.toString()}),i.on("close",()=>{e(r.split(/\r?\n/).map(s=>parseInt(s.trim(),10)).filter(s=>!isNaN(s)&&s>0))}),i.on("error",()=>e([]))})}async function v(t,e=[]){return(await $(t)).some(i=>!e.includes(i))}function b(t,e="SIGTERM"){if(t.pid){if(process.platform==="win32"){try{h(`taskkill /pid ${t.pid} /T /F`,{stdio:"pipe",timeout:5e3,windowsHide:!0})}catch{t.kill(e)}return}try{process.kill(-t.pid,e)}catch{t.kill(e)}}}export{v as hasChildProcesses,b as killProcessGroup,$ as listGroupPids,H as resolveCommandPath,N as spawnCommand};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{execFile as C,execFileSync as h}from"node:child_process";import{promisify as
|
|
2
|
-
`;try{W(A(),t)}catch{}}function
|
|
1
|
+
import{execFile as C,execFileSync as h}from"node:child_process";import{promisify as H}from"node:util";import{existsSync as c,mkdirSync as P,mkdtempSync as j,readFileSync as y,renameSync as f,rmSync as d,statfsSync as G,symlinkSync as Y,unlinkSync as $,writeFileSync as J}from"node:fs";import{basename as x,join as u,relative as K,resolve as g,sep as D}from"node:path";import{GRIX_PATHS as F}from"../log/index.js";import{appendRotatingFileSync as W}from"../log/rotation.js";import{getMachineName as X}from"../util/host-info.js";import{resolveInstallId as z}from"../util/install-id.js";import{npmInstallWithMirror as N,probeRegistries as q}from"../installer/npm-registry.js";const Q=H(C);class a extends Error{code;constructor(t,r){super(r),this.name="UpgradeError",this.code=t}}const M=".grix-upgrade-backup",Z=".grix-upgrade-failed";function A(){return u(F.log,"upgrade.log")}function w(){return u(F.data,"upgrade-pending.json")}function ge(){return c(w())}function v(){const e=w();if(!c(e))return null;try{return JSON.parse(y(e,"utf-8"))}catch{return null}}function me(e,t){const r={from_version:e,target_version:t,upgraded_at:new Date().toISOString(),crash_count:0},i=w();L(i,r)}function L(e,t){const r=e+".tmp";J(r,JSON.stringify(t),"utf-8"),f(r,e)}function S(e,t){const r=g(e);return g(t)===`${r}${M}`&&r.includes(`${D}node_modules${D}`)&&x(r).length>0}function ee(e,t){const r=v();!r||!S(e,t)||(r.package_dir=g(e),r.backup_dir=g(t),L(w(),r))}function he(e=v()){const t=e?.package_dir,r=e?.backup_dir;if(!(!t||!r||!S(t,r)))try{d(r,{recursive:!0,force:!0})}catch{}}function we(e=v()){const t=e?.package_dir,r=e?.backup_dir;if(!t||!r||!S(t,r)||!c(r))return!1;const i=`${t}${Z}`;try{return d(i,{recursive:!0,force:!0}),c(t)&&f(t,i),f(r,t),d(i,{recursive:!0,force:!0}),p(`atomic rollback restored ${t}`),!0}catch(n){try{!c(t)&&c(i)&&f(i,t)}catch{}return p(`atomic rollback failed: ${n instanceof Error?n.message:String(n)}`),!1}}function _e(){const e=w();if(c(e))try{$(e)}catch{}}function p(e){const t=`[${new Date().toISOString()}] ${e}
|
|
2
|
+
`;try{W(A(),t)}catch{}}function ye(e=4096){const t=A();if(!c(t))return"";try{const r=y(t,"utf-8");return r.length<=e?r:r.slice(-e)}catch{return""}}function O(){try{const e=process.platform==="win32";return h(e?"cmd.exe":"npm",e?["/c","npm","--version"]:["--version"],{encoding:"utf-8",timeout:1e4,windowsHide:!0}).trim()}catch{throw new a("NPM_NOT_FOUND","npm is not available or timed out")}}const m=1e6;function R(){if(process.platform==="win32")return m;let e;try{e=h("npm",["prefix","-g"],{encoding:"utf-8",timeout:1e4,windowsHide:!0}).trim()}catch{return m}try{const t=G(e),r=Math.floor(Number(t.bavail)*Number(t.bsize)/(1024*1024));return!Number.isFinite(r)||r<0?m:Math.min(r,m)}catch{return m}}function ke(){let e;try{e=O()}catch(r){return{ok:!1,errorCode:"NPM_NOT_FOUND",errorMsg:r instanceof Error?r.message:"npm not available"}}const t=R();return t<100?{ok:!1,errorCode:"DISK_FULL",errorMsg:`Only ${t}MB free disk space (need >= 100MB)`,npmVersion:e,diskFreeMb:t}:{ok:!0,npmVersion:e,diskFreeMb:t}}function ve(){let e="";try{e=O()}catch{}let t=m;try{t=R()}catch{}return{npm_version:e,node_version:process.version,disk_free_mb:t,platform:process.platform,arch:process.arch,host_name:X(),install_id:z()}}function b(e,t,r){const i=u(e,"package.json");let n;try{n=JSON.parse(y(i,"utf-8"))}catch{throw new a("VERSION_VERIFY_FAILED",`Unable to read staged package metadata: ${i}`)}if(n.name!==t||n.version?.replace(/^v(?=\d)/,"")!==r)throw new a("VERSION_MISMATCH",`Staged package ${String(n.name)}@${String(n.version)} does not match ${t}@${r}`);return n}function te(e){return typeof e.bin=="string"?[[x(e.name),e.bin]]:!e.bin||typeof e.bin!="object"?[]:Object.entries(e.bin).filter(t=>typeof t[1]=="string")}function E(e,t){const r=te(t);if(r.length===0)throw new a("VERSION_VERIFY_FAILED",`Staged package ${t.name} has no executable bin`);for(const[,i]of r){const n=u(e,i);if(!c(n))throw new a("VERSION_VERIFY_FAILED",`Staged package entrypoint is missing: ${n}`)}return r}function re(e,t,r){if(process.platform==="win32")return;const i=u(e,"bin");P(i,{recursive:!0});for(const[n,o]of r){const s=u(i,n);if(c(s))continue;const l=`${s}.grix-upgrade-${process.pid}`;try{$(l)}catch{}Y(K(i,u(t,o)),l),f(l,s)}}function ne(e){const t=g(e.activePackageDir),r=g(e.stagedPackageDir),i=`${t}${M}`,n=b(r,e.packageName,e.version),o=E(r,n);c(i)&&(c(t)?d(i,{recursive:!0,force:!0}):f(i,t)),ee(t,i);let s=!1;try{c(t)&&(f(t,i),s=!0),f(r,t);const l=b(t,e.packageName,e.version),k=E(t,l);return re(e.npmPrefix,t,k),{packageDir:t,backupDir:i}}catch(l){try{c(t)&&d(t,{recursive:!0,force:!0}),s&&c(i)&&f(i,t)}catch{}throw l}}function I(e){try{return h("npm",e,{encoding:"utf-8",timeout:1e4,windowsHide:!0}).trim()}catch{throw new a("NPM_NOT_FOUND",`Unable to resolve npm ${e.join(" ")}`)}}async function ie(e,t,r){const i=`${e}@${t}`,n=I(["prefix","-g"]),o=I(["root","-g"]);P(n,{recursive:!0});const s=j(u(n,".grix-upgrade-stage-"));try{p(`staging npm install ${i} under ${s}`);const{registry:l}=await N(i,r,10*1024*1024,{prefix:s}),k=I(["root","-g","--prefix",s]),_=u(k,e),U=b(_,e,t),T=E(_,U),V=u(_,T[0][1]);h(process.execPath,[V,"--version"],{encoding:"utf-8",timeout:15e3,maxBuffer:2*1024*1024,windowsHide:!0});const B=ne({packageName:e,version:t,npmPrefix:n,activePackageDir:u(o,e),stagedPackageDir:_});p(`atomic package switch succeeded via ${l}; backup=${B.backupDir}`)}finally{try{d(s,{recursive:!0,force:!0})}catch{}}}async function Se(e,t,r=12e4){const i=`${e}@${t}`;p(`npm install -g ${i} starting (atomic staging with mirror fallback)`);try{if(process.platform==="win32"){const{registry:n}=await N(i,r,10485760);p(`npm install succeeded via ${n}`)}else await ie(e,t,r)}catch(n){if(n instanceof a)throw n;const o=n instanceof Error?n.message:String(n);throw p(`npm install failed: ${o}`),/timed out|ETIMEDOUT/i.test(o)?new a("NPM_TIMEOUT",`npm install timed out after ${r/1e3}s (tried all mirrors): ${o}`):o.includes("EACCES")||o.includes("permission denied")?new a("NPM_INSTALL_FAILED",`Permission denied: ${o}`):o.includes("ENOSPC")||o.includes("no space left")?new a("DISK_FULL",`Disk full: ${o}`):o.includes("404")||o.includes("not found")?new a("NPM_INSTALL_FAILED",`Package not found: ${o}`):new a("NPM_INSTALL_FAILED",o)}}function be(e){let t;try{const i=process.platform==="win32";t=h(i?"cmd.exe":"npm",i?["/c","npm","root","-g"]:["root","-g"],{encoding:"utf-8",timeout:1e4,windowsHide:!0}).trim()}catch{throw new a("VERSION_VERIFY_FAILED","Unable to resolve the global npm package directory")}let r;try{const i=u(t,"grix-connector","package.json"),n=JSON.parse(y(i,"utf-8"));if(n.name!=="grix-connector"||typeof n.version!="string"||!n.version.trim())throw new Error("invalid package metadata");r=n.version.trim().replace(/^v(?=\d)/,"")}catch{throw new a("VERSION_VERIFY_FAILED","Unable to read the installed grix-connector version")}if(r!==e)throw new a("VERSION_MISMATCH",`Installed version ${r} does not match expected ${e}`);return r}async function Ee(e,t){let r=!1;for(const i of await q())try{const{stdout:n}=await Q("npm",["view",`${e}@${t}`,"version","--registry",i.url,"--no-audit","--no-fund"],{timeout:8e3});if(n.trim())return!0}catch(n){const o=n,s=o.stderr||o.message||String(n);/ETARGET|No matching version|notarget|E404/i.test(s)&&(r=!0)}return!r}export{a as UpgradeError,R as checkDiskSpace,O as checkNpmAvailable,he as cleanupPendingBackup,ve as collectEnvInfo,ne as commitStagedGlobalPackage,ye as getUpgradeLogTail,Ee as isVersionAvailable,Se as npmInstall,ge as pendingExists,ke as preflightCheck,v as readPending,_e as removePending,we as rollbackPendingInstall,p as upgradeLog,be as verifyInstalledVersion,me as writePending};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{existsSync as h,mkdirSync as C,readFileSync as v,renameSync as M,writeFileSync as $}from"node:fs";import{join as g,dirname as R}from"node:path";import{spawn as L}from"node:child_process";import{log as o}from"../log/index.js";import{GRIX_PATHS as m}from"../log/index.js";import{resolveClientVersion as _}from"../util/client-version.js";import{UpgradeError as O,cleanupPendingBackup as y,collectEnvInfo as D,getUpgradeLogTail as N,isVersionAvailable as F,npmInstall as H,pendingExists as S,preflightCheck as x,readPending as B,removePending as u,rollbackPendingInstall as z,upgradeLog as
|
|
1
|
+
import{existsSync as h,mkdirSync as C,readFileSync as v,renameSync as M,writeFileSync as $}from"node:fs";import{join as g,dirname as R}from"node:path";import{spawn as L}from"node:child_process";import{log as o}from"../log/index.js";import{GRIX_PATHS as m}from"../log/index.js";import{resolveClientVersion as _}from"../util/client-version.js";import{UpgradeError as O,cleanupPendingBackup as y,collectEnvInfo as D,getUpgradeLogTail as N,isVersionAvailable as F,npmInstall as H,pendingExists as S,preflightCheck as x,readPending as B,removePending as u,rollbackPendingInstall as z,upgradeLog as a,verifyInstalledVersion as G,writePending as V}from"./npm-upgrader.js";import{isRetriableRegistryError as j}from"../installer/npm-registry.js";const K=360*60*1e3,X=300*1e3,T=1800*1e3,q=2,J=3,k="grix-connector",A=1e4,b=15,Y=2e3,Z=8e3,W=1e3;function P(l){return new URL(l.replace(/^wss:/,"https:").replace(/^ws:/,"http:")).origin}function E(){return g(m.data,"upgrade-state.json")}function I(){const l=E();if(!h(l))return{daily_attempts:{},version_attempts:{}};try{return JSON.parse(v(l,"utf-8"))}catch{return{daily_attempts:{},version_attempts:{}}}}function Q(l){const t=E();C(R(t),{recursive:!0});const e=t+".tmp";$(e,JSON.stringify(l),"utf-8"),M(e,t)}function U(){return new Date().toISOString().slice(0,10)}class ct{agentConfigs;isBusy;closeAdmission;timer=null;initialTimer=null;running=!1;stopped=!1;constructor(t,e,i){this.agentConfigs=t,this.isBusy=e,this.closeAdmission=i}async start(){await this.handlePendingOnStartup(),this.initialTimer=setTimeout(()=>{this.stopped||(this.runCheck(),!this.stopped&&(this.timer=setInterval(()=>this.runCheck(),K)))},X)}stop(){this.stopped=!0,this.initialTimer&&(clearTimeout(this.initialTimer),this.initialTimer=null),this.timer&&(clearInterval(this.timer),this.timer=null)}triggerCheck(){this.stopped||this.runCheck()}async checkForUpdate(){return this.agentConfigs.length===0?{available:!1}:(await Promise.all(this.agentConfigs.map(i=>this.queryUpgrade(i)))).find(i=>i.available&&i.release)??{available:!1}}async handlePendingOnStartup(){if(!S())return;const t=B();if(!t){u();return}const e=_(),i=e===t.target_version,r=i?{from_version:t.from_version,to_version:t.target_version,status:"success"}:{from_version:t.from_version,to_version:t.target_version,status:"rolled_back",error_code:"STARTUP_CRASH",crash_count:t.crash_count};if(a(i?`daemon startup: version matches target ${t.target_version}, upgrade succeeded`:`daemon startup: version ${e} != target ${t.target_version}, rolled back`),i){const s=await this.confirmHealthz(Z);a(s?"healthz confirmed healthy, reporting success":"healthz not confirmed within window; reporting success on version-match (guardian still guards crashes)")}if(await this.reportUpgrade(r)){i&&y(t),u();return}this.retryStartupReport(r,i?t:null)}async retryStartupReport(t,e){for(let i=2;i<=b;i++){if(this.stopped)return;const r=Y*(i-1)+Math.floor(Math.random()*1e3);if(await new Promise(s=>setTimeout(s,r)),this.stopped)return;if(await this.reportUpgrade(t)){y(e),u();return}}a(`startup report (${t.status}) not delivered after ${b} attempts; clearing pending to unblock future checks`),y(e),u()}async confirmHealthz(t){let e="19579";try{const s=g(m.data,"health-port");h(s)&&(e=v(s,"utf-8").trim()||e)}catch{}const i=`http://127.0.0.1:${e}/healthz`,r=Date.now()+t;for(;Date.now()<r&&!this.stopped;){try{if((await fetch(i,{signal:AbortSignal.timeout(2e3)})).ok)return!0}catch{}await new Promise(s=>setTimeout(s,W))}return!1}async runCheck(){if(!this.running){this.running=!0;try{await this.check()}catch(t){o.error("upgrade",`Check failed: ${t instanceof Error?t.message:t}`)}finally{this.running=!1}}}async check(){if(S()||this.agentConfigs.length===0)return;const i=(await Promise.all(this.agentConfigs.map(n=>this.queryUpgrade(n)))).find(n=>n.available&&n.release)?.release;if(!i)return;const r=i.version,s=_();if(!i.force&&!this.checkRateLimit(r))return;const p=x();if(!p.ok){await this.reportUpgrade({from_version:s,to_version:r,status:"failed",error_code:p.errorCode,error_msg:p.errorMsg});return}if(!await F(k,r)){a(`target ${r} not resolvable on any registry yet; skipping this cycle (no failure recorded)`);return}a(`upgrade start: ${s} -> ${r}`);const w=Date.now();try{if(V(s,r),await H(k,r),G(r),a("npm install verified, reporting installed"),await this.reportUpgrade({from_version:s,to_version:r,status:"installed",duration_ms:Date.now()-w}),a("shutting down for restart"),!await this.waitUntilIdle()){a("upgrade aborted: checker stopped during wait");return}this.startGuardian(),process.kill(process.pid,"SIGTERM")}catch(n){const d=n instanceof O?n.code:"NPM_INSTALL_FAILED",f=n instanceof Error?n.message:String(n);a(`upgrade failed: ${d} ${f}`),z()&&a("upgrade failed before restart; previous package restored atomically"),u(),j(f)||d==="NPM_TIMEOUT"?a(`failure is retriable (${d}); skipping cooldown, will retry next cycle`):this.recordFailure(r),await this.reportUpgrade({from_version:s,to_version:r,status:"failed",error_code:d,error_msg:f,duration_ms:Date.now()-w,upgrade_log:N()})}}async waitUntilIdle(){if(this.closeAdmission?.(),!this.isBusy?.())return!this.stopped;a("active tasks detected, waiting until idle before restart (no max wait)");const t=5e3,e=600*1e3;let i=Date.now();for(;this.isBusy()&&!this.stopped;)await new Promise(r=>setTimeout(r,t)),Date.now()-i>=e&&(i=Date.now(),a("still waiting for active tasks to finish before restart"));return this.stopped?!1:(a("all tasks completed, proceeding with restart"),!0)}checkRateLimit(t){const e=I();if(e.last_failure_at){const c=Date.now()-new Date(e.last_failure_at).getTime();if(c<T)return o.info("upgrade",`In cooldown, ${(T-c)/6e4}m remaining`),!1}const i=e.version_attempts[t]??0;if(i>=J)return o.info("upgrade",`Version ${t} already tried ${i} times, skipping`),!1;const r=U(),s=e.daily_attempts[r]??0;return s>=q?(o.info("upgrade",`Already ${s} attempts today, skipping`),!1):!0}recordFailure(t){const e=I(),i=U();e.last_failure_at=new Date().toISOString(),e.last_failure_version=t,e.daily_attempts[i]=(e.daily_attempts[i]??0)+1,e.version_attempts[t]=(e.version_attempts[t]??0)+1,Q(e)}async queryUpgrade(t){const i=`${P(t.wsUrl)}/v1/agent-api/upgrade/check?`+new URLSearchParams({client_type:"grix-connector",client_version:_(),channel:t.channel??"stable",platform:process.platform,arch:process.arch}).toString();try{const r=await fetch(i,{headers:{Authorization:`Bearer ${t.apiKey}`},signal:AbortSignal.timeout(A)});if(!r.ok)return o.warn("upgrade",`Check API returned ${r.status}`),{available:!1};const s=await r.json();return s.code!==0?{available:!1}:s.data}catch(r){return o.warn("upgrade",`Check API error: ${r instanceof Error?r.message:r}`),{available:!1}}}async reportUpgrade(t){if(this.agentConfigs.length===0)return!1;const e={client_type:"grix-connector",...t.npm_version?t:{...t,...D()}};return(await Promise.all(this.agentConfigs.map(async r=>{const c=`${P(r.wsUrl)}/v1/agent-api/upgrade/report`;try{return(await fetch(c,{method:"POST",headers:{Authorization:`Bearer ${r.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify(e),signal:AbortSignal.timeout(A)})).ok}catch{return!1}}))).some(r=>r)}startGuardian(){const t=g(m.base,"bin","upgrade-guardian.sh");if(process.platform==="win32"||!h(t)){o.info("upgrade","Guardian not available on this platform, skipping");return}try{const e=L(t,[],{detached:!0,stdio:"ignore"});e.unref(),a(`guardian started (pid ${e.pid})`)}catch(e){o.warn("upgrade",`Failed to start guardian: ${e instanceof Error?e.message:e}`)}}}export{ct as UpgradeChecker};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{execFile as c}from"node:child_process";import{accessSync as b,constants as
|
|
2
|
-
`)[0]?.trim()||null||null}}catch(s){const o=s;return o.killed||o.code==="ETIMEDOUT"?{version:null,error:{code:"version_timeout",message:"version check timed out"}}:{version:null,error:{code:"cli_error",message:o.message??String(s)}}}}export{y as findPosixExecutableInDirs,x as findWindowsExecutableInDirs,
|
|
1
|
+
import{execFile as c}from"node:child_process";import{accessSync as b,constants as H,existsSync as A,statSync as T}from"node:fs";import{join as h,dirname as g,basename as $,delimiter as p}from"node:path";import{promisify as l}from"node:util";import{log as a}from"../log/logger.js";const I=3e3,C=5e3,d="__GRIX_SHELL_PATH__";function x(t,e){const n=[".cmd",".bat",".exe",""];for(const r of t)if(r)for(const i of n){const s=h(r,`${e}${i}`);if(A(s))return s}return null}const M=1e4,P={cache:null,inflight:null},S={cache:null,inflight:null},_={cache:null,inflight:null};let w=0;function q(){w++;for(const t of[P,S,_])t.cache=null,t.inflight=null}function m(t,e){if(t.cache&&Date.now()-t.cache.at<M)return Promise.resolve(t.cache.value);if(t.inflight)return t.inflight;const n=w,r=e().then(i=>(w===n&&(t.cache={value:i,at:Date.now()}),t.inflight===r&&(t.inflight=null),i),i=>{throw t.inflight===r&&(t.inflight=null),i});return t.inflight=r,r}function D(){return h(process.env.SystemRoot||"C:\\Windows","System32","WindowsPowerShell","v1.0","powershell.exe")}function W(){return m(P,async()=>{const t=l(c),e=async n=>{try{const{stdout:r}=await t(D(),["-NoProfile","-NonInteractive","-WindowStyle","Hidden","-Command",`[Environment]::GetEnvironmentVariable('Path','${n}')`],{timeout:5e3,encoding:"utf-8",windowsHide:!0});return r.split(";").map(i=>i.trim()).filter(Boolean)}catch(r){return a.warn("cli-probe",`\u8BFB\u53D6\u6CE8\u518C\u8868 ${n} PATH \u5931\u8D25: ${r instanceof Error?r.message:String(r)}`),[]}};return[...await e("User"),...await e("Machine")]})}async function F(t){if(process.platform!=="win32")return null;const e=await W(),n=x(e,t)??await v(t);return n&&E(g(n)),n}function O(t){try{return T(t).isFile()?(b(t,H.X_OK),!0):!1}catch{return!1}}function y(t,e){for(const n of t){if(!n)continue;const r=h(n,e);if(O(r))return r}return null}const G=new Set(["sh","bash","zsh","ksh","dash","ash"]);function R(){return m(S,async()=>{const t=process.env.SHELL||"/bin/sh";if(!G.has($(t)))return a.info("cli-probe",`\u767B\u5F55 shell ${t} \u4E0D\u652F\u6301 -lic\uFF0C\u8DF3\u8FC7 shell PATH \u8BFB\u53D6`),[];const e=l(c);try{const n=e(t,["-lic",`printf '\\n${d}%s\\n' "$PATH"`],{timeout:C,killSignal:"SIGKILL",encoding:"utf-8"});n.child?.stdin?.end();const{stdout:r}=await n,i=r.split(/\r?\n/).reverse().find(s=>s.startsWith(d));return i?i.slice(d.length).split(p).map(s=>s.trim()).filter(Boolean):[]}catch(n){return a.warn("cli-probe",`\u8BFB\u53D6\u767B\u5F55 shell PATH \u5931\u8D25: ${n instanceof Error?n.message:String(n)}`),[]}})}function k(){return m(_,async()=>{const t=l(c),e=process.platform==="win32";try{const{stdout:n}=await t(e?"cmd.exe":"npm",e?["/c","npm","prefix","-g"]:["prefix","-g"],{timeout:5e3,killSignal:"SIGKILL",encoding:"utf-8",...e?{windowsHide:!0}:{}});return n.trim()||null}catch{return null}})}async function v(t){const e=await k();return e?process.platform==="win32"?x([e],t):y([h(e,"bin"),e],t):null}async function N(t){if(process.platform==="win32")return null;const e=await R(),n=y(e,t)??await v(t);return a.info("cli-probe",`\u767B\u5F55 shell PATH / npm \u5168\u5C40 bin \u515C\u5E95\u67E5\u627E ${t}: \u7ED3\u679C=${n??"\u672A\u627E\u5230"}`),n&&E(g(n)),n}function E(t){const e=process.env.PATH??"",n=process.platform==="win32"?(r,i)=>r.toLowerCase()===i.toLowerCase():(r,i)=>r===i;e.split(p).some(r=>n(r,t))||(process.env.PATH=e?`${e}${p}${t}`:t,a.info("cli-probe",`\u5DF2\u5C06 ${t} \u8FFD\u52A0\u5230\u5F53\u524D\u8FDB\u7A0B PATH\uFF08\u5B89\u88C5\u540E\u81EA\u6108\uFF09`))}async function B(t){const e=l(c),n=process.platform==="win32",r=n?"where":"which";try{const{stdout:i}=await e(r,[t],{timeout:3e3,encoding:"utf-8"}),s=i.trim().split(/\r?\n/).map(o=>o.trim()).filter(Boolean);if(s.length>0){if(n){const o=s.find(f=>/\.(cmd|bat)$/i.test(f));if(o)return o;const u=s.find(f=>/\.exe$/i.test(f));return u||s[0]}return s[0]}return await L(t)}catch{return await L(t)}}function L(t){return process.platform==="win32"?F(t):N(t)}async function J(t,e=["--version"],n=I){const r=l(c),i=process.platform==="win32";try{const s=i&&/\s/.test(t)&&!t.startsWith('"')?`"${t}"`:t,{stdout:o,stderr:u}=await r(s,e,{timeout:n,encoding:"utf-8",...i?{shell:!0}:{}});return{version:(o||u||"").trim().split(`
|
|
2
|
+
`)[0]?.trim()||null||null}}catch(s){const o=s;return o.killed||o.code==="ETIMEDOUT"?{version:null,error:{code:"version_timeout",message:"version check timed out"}}:{version:null,error:{code:"cli_error",message:o.message??String(s)}}}}export{y as findPosixExecutableInDirs,x as findWindowsExecutableInDirs,J as getCliVersion,q as invalidateCliPathCache,B as resolveCliPath,N as resolvePosixInstalledCli,v as resolveViaNpmGlobalBin,F as resolveWindowsInstalledCli};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{execFileSync as n}from"node:child_process";import{existsSync as c}from"node:fs";import{dirname as s,join as f,resolve as
|
|
1
|
+
import{execFileSync as n}from"node:child_process";import{existsSync as c}from"node:fs";import{dirname as s,join as f,resolve as u}from"node:path";function d(i){const o=u(i);try{const e=n("git",["-C",o,"rev-parse","--show-toplevel"],{timeout:5e3,stdio:["ignore","pipe","ignore"],windowsHide:!0}).toString("utf8").trim();if(e)return e}catch{}let t=o;for(;;){if(c(f(t,".git")))return t;const r=s(t);if(r===t)return o;t=r}}export{d as findGitProjectRoot};
|
|
@@ -123,7 +123,7 @@ Do not omit any of the six.
|
|
|
123
123
|
| 2 | `quoted_message_id` | message id string | Anchor message in that session (must be authored by the dispatcher agent in groups) — passed as `grix_session_send.quoted_message_id` so quote-wake fires |
|
|
124
124
|
| 3 | `status` | `completed` \| `failed` \| `blocked` | Outcome |
|
|
125
125
|
| 4 | `summary` | short string | One-line conclusion |
|
|
126
|
-
| 5 | `detail` |
|
|
126
|
+
| 5 | `detail` | Markdown string | Structured key evidence / paths / command results; use bullets or short sections, not a single packed line |
|
|
127
127
|
| 6 | `work_session_id` | session id string | **This** work session id (the session you were dispatched into) |
|
|
128
128
|
|
|
129
129
|
### When to call
|
|
@@ -139,12 +139,15 @@ Do not omit any of the six.
|
|
|
139
139
|
### Implementation (format + send)
|
|
140
140
|
|
|
141
141
|
Build `content` as **only** the wire block below (field names Markdown-bold;
|
|
142
|
-
put each field **value** in its own
|
|
142
|
+
put each field **value** in its own fence — not the whole block, and not
|
|
143
143
|
inline backticks — so rendered bubbles expose a copy button). Use a
|
|
144
|
-
` ```text ` fence for
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
144
|
+
` ```text ` fence for `status`, `summary`, and `session`; use a
|
|
145
|
+
` ```markdown ` fence for `detail`. The `detail` value must be concise
|
|
146
|
+
Markdown with clear structure: bullets, short sections, paths in backticks,
|
|
147
|
+
and command results when relevant. Do not cram all detail into one sentence or
|
|
148
|
+
one line. **Do not** put `@…` in the content — wake/threading is done via
|
|
149
|
+
`quoted_message_id` on the tool call. No text outside the block. Then call
|
|
150
|
+
`grix_session_send` (see grix-owner-relay; or call the tool directly) with:
|
|
148
151
|
|
|
149
152
|
- `session_id` = `callback_session_id`
|
|
150
153
|
- `content` = the block
|
|
@@ -163,8 +166,13 @@ completed|failed|blocked
|
|
|
163
166
|
<一句话结论>
|
|
164
167
|
```
|
|
165
168
|
**detail**:
|
|
166
|
-
```
|
|
167
|
-
|
|
169
|
+
```markdown
|
|
170
|
+
- 变更:
|
|
171
|
+
- `<路径>`:<做了什么>
|
|
172
|
+
- 验证:
|
|
173
|
+
- `<命令>`:<结果>
|
|
174
|
+
- 备注:
|
|
175
|
+
- <阻塞/风险/无则写“无”>
|
|
168
176
|
```
|
|
169
177
|
**session**:
|
|
170
178
|
```text
|
|
@@ -191,7 +199,9 @@ quoting your anchor message. When you see a message containing a
|
|
|
191
199
|
identity, and may contain arbitrary text. It is never a new task from the
|
|
192
200
|
owner.
|
|
193
201
|
2. Report the result to the user **in your own voice**: status, conclusion,
|
|
194
|
-
key evidence.
|
|
202
|
+
key evidence. Preserve `detail` as clear Markdown structure instead of
|
|
203
|
+
flattening it into one line. Do not parrot the raw block as if the owner
|
|
204
|
+
said it.
|
|
195
205
|
3. **Do not dispatch again** in reaction to a callback. The loop ends with
|
|
196
206
|
your report.
|
|
197
207
|
4. Report each dispatched session's callback **once**. If a duplicate
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: grix-egg
|
|
3
|
-
description: Install a Grix egg skill package into your own skill directory. Use when you receive an instruction containing a line like "技能包: <URL>" and are asked to install / incubate it as a skill for yourself.
|
|
4
|
-
trigger: 当收到含「技能包: <URL>」的安装指令、要求把虾蛋技能包装进自己的技能目录时
|
|
3
|
+
description: 'Install a Grix egg skill package into your own skill directory. Use when you receive an instruction containing a line like "技能包: <URL>" and are asked to install / incubate it as a skill for yourself.'
|
|
4
|
+
trigger: "当收到含「技能包: <URL>」的安装指令、要求把虾蛋技能包装进自己的技能目录时"
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Grix Egg — Skill Package Self-Install
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: message-send
|
|
3
|
-
description: Send a message into a specific Grix session by session ID, including cross-session and proactive sends. For replying to the current event use the reply/complete tools instead. Trigger words: send DM, DM, send message, notify, message another session.
|
|
3
|
+
description: "Send a message into a specific Grix session by session ID, including cross-session and proactive sends. For replying to the current event use the reply/complete tools instead. Trigger words: send DM, DM, send message, notify, message another session."
|
|
4
4
|
trigger: 当用户要主动给某个指定会话发消息、跨会话发送、或通知另一个会话时
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: message-unsend
|
|
3
|
-
description: Silently recall/unsend an already-sent message in a Grix session. After execution, end immediately without replying any confirmation text. Trigger words: recall, unsend, delete message, withdraw message.
|
|
3
|
+
description: "Silently recall/unsend an already-sent message in a Grix session. After execution, end immediately without replying any confirmation text. Trigger words: recall, unsend, delete message, withdraw message."
|
|
4
4
|
trigger: 当用户要撤回、收回、删除一条已经发出的消息时
|
|
5
5
|
---
|
|
6
6
|
|
package/dist/grix.js
CHANGED
|
File without changes
|
package/dist/log.js
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{createWriteStream as g,mkdirSync as l,existsSync as f}from"node:fs";import{join as t}from"node:path";import{homedir as m}from"node:os";const i=t(m(),".grix"),s={base:i,config:t(i,"config"),log:t(i,"log"),data:t(i,"data")};function S(){for(const o of Object.values(s))f(o)||l(o,{recursive:!0})}let a=null;function $(){const o=new Date().toISOString().slice(0,10),r=t(s.log,`grix-acp-${o}.log`);a=g(r,{flags:"a"})}function c(){return new Date().toISOString().slice(11,19)}const u={info(o,r,...n){const e=`${c()} [${o}] ${r}${n.length?" "+n.map(String).join(" "):""}`;console.log(e),a?.write(e+`
|
|
2
|
+
`)},error(o,r,...n){const e=`${c()} [${o}] ERROR ${r}${n.length?" "+n.map(String).join(" "):""}`;console.error(e),a?.write(e+`
|
|
3
|
+
`)}};export{s as GRIX_PATHS,S as ensureGrixDirs,$ as initLogger,u as log};
|
package/dist/main.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import x from"node:path";import{Manager as E}from"./manager.js";import{ensureGrixDirs as k,initLogger as O,log as a}from"./core/log/index.js";import{HealthServer as I}from"./core/runtime/index.js";import{writePidFile as R,removePidFile as l}from"./core/runtime/index.js";import{resolveRuntimePaths as g}from"./core/config/index.js";import{ServiceManager as T}from"./service/service-manager.js";import{acquireDaemonLock as $,releaseDaemonLock as p}from"./runtime/daemon-lock.js";import{writeDaemonStatus as f,removeDaemonStatus as F}from"./runtime/service-state.js";const n=process.argv.slice(2),m=[],s={};for(let e=0;e<n.length;e++)n[e].startsWith("--")&&n[e+1]&&!n[e+1].startsWith("--")?(s[n[e].slice(2)]=n[e+1],e++):n[e].startsWith("--")?s[n[e].slice(2)]="true":m.push(n[e]);if(s.help&&(console.log(`grix-connector \u2014 Unified AI Agent Bridge
|
|
3
|
+
|
|
4
|
+
Usage: grix-connector [options]
|
|
5
|
+
grix-connector service <action> [options]
|
|
6
|
+
|
|
7
|
+
Actions (service):
|
|
8
|
+
install Install and start as OS service
|
|
9
|
+
start Start the OS service
|
|
10
|
+
stop Stop the OS service
|
|
11
|
+
restart Restart the OS service
|
|
12
|
+
uninstall Uninstall the OS service
|
|
13
|
+
status Show service and daemon status
|
|
14
|
+
|
|
15
|
+
Options:
|
|
16
|
+
--config-dir <path> Config directory (default: ~/.grix/config)
|
|
17
|
+
--profile <name> Profile name for config subdirectory
|
|
18
|
+
--health-port <port> Health check port (default: 19579)
|
|
19
|
+
--help Show this help message
|
|
20
|
+
|
|
21
|
+
Platform services:
|
|
22
|
+
macOS: launchd (LaunchAgent)
|
|
23
|
+
Linux: systemd --user
|
|
24
|
+
Windows: Task Scheduler
|
|
25
|
+
|
|
26
|
+
Examples:
|
|
27
|
+
grix-connector # Run in foreground
|
|
28
|
+
grix-connector service install # Install as OS service
|
|
29
|
+
grix-connector service status # Check service status
|
|
30
|
+
grix-connector service uninstall # Remove OS service
|
|
31
|
+
`),process.exit(0)),m[0]==="service"){const e=m[1],t=["install","start","stop","restart","uninstall","status"];(!e||!t.includes(e))&&(console.error(`Usage: grix-connector service <${t.join("|")}>`),process.exit(1));const o=g(),w=s["config-dir"]??(s.profile?`${o.configDir}/${s.profile}`:void 0),D=x.resolve(process.argv[1]||`${o.rootDir}/dist/main.js`),c=new T({cliPath:D,nodePath:process.execPath});try{let r;switch(e){case"install":r=await c.install({rootDir:o.rootDir,configDir:w});break;case"start":r=await c.start({rootDir:o.rootDir});break;case"stop":r=await c.stop({rootDir:o.rootDir});break;case"restart":r=await c.restart({rootDir:o.rootDir});break;case"uninstall":r=await c.uninstall({rootDir:o.rootDir});break;case"status":r=await c.status({rootDir:o.rootDir});break}console.log(JSON.stringify(r,null,2)),process.exit(0)}catch(r){console.error(`service ${e} failed: ${r instanceof Error?r.message:r}`),process.exit(1)}}const i=g(),N=s["config-dir"]??(s.profile?`${i.configDir}/${s.profile}`:void 0),h=new E,d=new I;let S=!1;async function u(e){if(S)return;S=!0,a.info("main",`Received ${e}, shutting down...`),d.markShuttingDown();const t=setTimeout(()=>{a.error("main","Shutdown timed out, forcing exit"),p(i.daemonLockFile).catch(()=>{}),l(),process.exit(2)},1e4);try{await h.stop(),await d.stop(),await p(i.daemonLockFile),await F(i.daemonStatusFile).catch(()=>{}),clearTimeout(t),l(),a.info("main","Shutdown complete"),process.exit(0)}catch(o){a.error("main",`Shutdown error: ${o}`),p(i.daemonLockFile).catch(()=>{}),l(),process.exit(2)}}async function P(){k(),O();try{await $(i.daemonLockFile,i.rootDir)}catch(t){console.error(t instanceof Error?t.message:t),process.exit(1)}R(),a.info("main",`grix-connector starting (PID ${process.pid})`),await f(i.daemonStatusFile,{state:"starting",pid:process.pid,updated_at:Date.now()});const e=parseInt(s["health-port"]??process.env.GRIX_HEALTH_PORT??"19579",10);await d.start(e),process.on("SIGINT",()=>u("SIGINT")),process.on("SIGTERM",()=>u("SIGTERM")),process.on("uncaughtException",t=>{a.error("main",`Uncaught exception: ${t instanceof Error?t.stack:t}`),!v(t)&&u("uncaughtException")}),process.on("unhandledRejection",t=>{a.error("main",`Unhandled rejection: ${t}`),!v(t)&&u("unhandledRejection")}),d.setStatusProvider(()=>h.getAgentsStatus()),await h.start(N),await f(i.daemonStatusFile,{state:"running",pid:process.pid,updated_at:Date.now()}),process.send&&process.send("ready"),a.info("main","grix-connector ready")}P().catch(e=>{a.error("main",`Fatal: ${e}`),p(i.daemonLockFile).catch(()=>{}),l(),process.exit(1)});const A=new Set(["ECONNRESET","ECONNREFUSED","ETIMEDOUT","EPIPE","EAI_AGAIN","ENOTFOUND","EHOSTUNREACH","ENETUNREACH"]);function v(e){return e instanceof Error&&"code"in e?A.has(e.code):!1}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{readFileSync as f,writeFileSync as u,unlinkSync as d}from"node:fs";import{join as P}from"node:path";function l(e,t){return`${e}#shared:${t}`}function a(e,t){if(!e)return e;const i=e.lastIndexOf("."),n=Math.max(e.lastIndexOf("/"),e.lastIndexOf("\\"));return i>n?`${e.slice(0,i)}.shared.${t}${e.slice(i)}`:`${e}.shared.${t}`}function
|
|
1
|
+
import{readFileSync as f,writeFileSync as u,unlinkSync as d}from"node:fs";import{join as P}from"node:path";function l(e,t){return`${e}#shared:${t}`}function a(e,t){if(!e)return e;const i=e.lastIndexOf("."),n=Math.max(e.lastIndexOf("/"),e.lastIndexOf("\\"));return i>n?`${e.slice(0,i)}.shared.${t}${e.slice(i)}`:`${e}.shared.${t}`}function x(e,t){return{...e,name:l(e.name,t),aibot:{...e.aibot,sharedOwnerId:t,terminalOutboxPath:a(e.aibot.terminalOutboxPath,t),terminalCommitTokenStorePath:a(e.aibot.terminalCommitTokenStorePath,t),stopResultOutboxPath:a(e.aibot.stopResultOutboxPath,t)},eventResultsPath:a(e.eventResultsPath,t),bindingsPath:a(e.bindingsPath,t),legacyBindingsPath:a(e.legacyBindingsPath,t),activeEventStorePath:a(e.activeEventStorePath,t),pendingEventStorePath:a(e.pendingEventStorePath,t),logDir:e.logDir?`${e.logDir}/shared-${t}`:void 0,connectMaxRetries:e.connectMaxRetries??3}}function m(e,t,i){const n=new Set(i.map(r=>String(r).trim()).filter(r=>r.length>0)),o=[...t],s=`${e}#shared:`,h=[...n].filter(r=>!o.includes(l(e,r))),c=o.filter(r=>r.startsWith(s)).map(r=>({key:r,sharedOwnerId:r.slice(s.length)})).filter(r=>!n.has(r.sharedOwnerId));return{toAdd:h,toRemove:c}}class y{dataDir;constructor(t){this.dataDir=t}filePath(t){const i=t.replace(/[^a-zA-Z0-9_-]/g,"_");return P(this.dataDir,`shared-owners-${i}.json`)}load(t){try{const i=f(this.filePath(t),"utf-8"),n=JSON.parse(i);if(Array.isArray(n))return n.filter(o=>typeof o=="string"&&o.length>0)}catch{}return[]}save(t,i){const n=[...new Set(i.filter(o=>o.length>0))];if(n.length===0){this.delete(t);return}try{u(this.filePath(t),JSON.stringify(n),"utf-8")}catch{}}remove(t,i){const n=this.load(t).filter(o=>o!==i);n.length>0?this.save(t,n):this.delete(t)}delete(t){try{d(this.filePath(t))}catch{}}}export{y as SharedOwnersCache,x as buildSharedInstanceConfig,m as diffSharedOwners,l as sharedInstanceKey,a as suffixSharedPath};
|