grix-connector 3.3.2 → 3.3.5
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/README.md +67 -1
- package/dist/core/access/allowlist-gate.js +1 -1
- package/dist/core/access/allowlist-store.js +1 -1
- package/dist/core/access/index.js +1 -1
- package/dist/core/protocol/interaction-parser.js +1 -1
- package/dist/core/runtime/health.js +1 -1
- package/dist/core/util/cli-args.js +1 -0
- package/dist/grix.js +8 -6
- package/dist/mcp/stream-http/security.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -142,6 +142,47 @@ grix-connector start
|
|
|
142
142
|
|
|
143
143
|
The daemon connects to Grix via WebSocket and starts routing chat messages to your agents.
|
|
144
144
|
|
|
145
|
+
> The daemon refuses to start when the config directory holds no valid agent config, so on a first-time setup the config file must be written **before** `start`.
|
|
146
|
+
|
|
147
|
+
### Adding an agent to an existing setup
|
|
148
|
+
|
|
149
|
+
If `~/.grix/config/agents.json` already exists and other agents are running, **do not overwrite the file** — merge the new entry into it.
|
|
150
|
+
|
|
151
|
+
**1. Merge the entry.** Read the file as JSON and look through the `agents` array for an entry whose `agent_id` matches the one you are adding:
|
|
152
|
+
|
|
153
|
+
- found → replace that entry
|
|
154
|
+
- not found → append the new entry
|
|
155
|
+
- every other entry must be left exactly as it was
|
|
156
|
+
|
|
157
|
+
Edit it with a script (`node`, `python3`), not by hand — a truncated or re-indented file silently drops the agents you did not intend to touch. If the file does not exist yet, create it with a single-element `agents` array.
|
|
158
|
+
|
|
159
|
+
**2. Apply the change.** Check whether the daemon is already running:
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
grix-connector status
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
- not running (or it reports `daemon is not running`) → `grix-connector start`
|
|
166
|
+
- already running → `grix-connector reload`
|
|
167
|
+
|
|
168
|
+
`reload` hot-loads the new agent and leaves every already-running agent untouched — their live sessions are not interrupted. Do not use `restart` to add an agent: it reconnects everything and drops all in-flight conversations.
|
|
169
|
+
|
|
170
|
+
**3. Verify.** `grix-connector status` reports the daemon only — it does not list agents. To confirm the new agent is connected, query the local admin API (give it a few seconds after `start`/`reload`):
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
curl -s http://127.0.0.1:19580/api/agents
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
The entry for your agent should report `"alive": true`. `19580` is the default admin port; if it was overridden (`--admin-port` or `GRIX_ADMIN_PORT`), the port actually in use is written to `~/.grix/data/admin-port`.
|
|
177
|
+
|
|
178
|
+
If it never connects, read the newest log under `~/.grix/log/`. In practice it is almost always one of three things:
|
|
179
|
+
|
|
180
|
+
- the CLI for that `client_type` is not on `PATH`
|
|
181
|
+
- the CLI is installed but not logged in
|
|
182
|
+
- the `api_key` was truncated when it was copied
|
|
183
|
+
|
|
184
|
+
**Credentials.** `api_key` is a one-time secret: write it into `~/.grix/config/agents.json` and nowhere else. Do not echo it into logs or commit it to a repository.
|
|
185
|
+
|
|
145
186
|
### Commands
|
|
146
187
|
|
|
147
188
|
```bash
|
|
@@ -224,11 +265,34 @@ Or manually add to your OpenClaw project:
|
|
|
224
265
|
npm install grix-connector
|
|
225
266
|
```
|
|
226
267
|
|
|
268
|
+
### Connecting an agent (plugin mode)
|
|
269
|
+
|
|
270
|
+
Plugin mode does **not** use `~/.grix/config/agents.json`. The credentials live in OpenClaw's own config (`openclaw.json`) under `channels.grix`, and the field names are camelCase — `wsUrl` / `agentId` / `apiKey` — not the snake_case names used by the daemon.
|
|
271
|
+
|
|
272
|
+
Write them with `openclaw config set`. **Do not hand-edit `openclaw.json`.**
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
openclaw config set channels.grix.accounts.<account-id> \
|
|
276
|
+
'{"name":"my-agent","enabled":true,"wsUrl":"wss://grix.dhf.pub/v1/agent-api/ws","agentId":"your-agent-id","apiKey":"your-grix-api-key"}' \
|
|
277
|
+
--strict-json
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
`<account-id>` is a name you choose (lowercase letters, digits, hyphens). Use the `ws.grix.im` domain instead for the global region. If `channels.grix.enabled` was explicitly turned off before, also run `openclaw config set channels.grix.enabled true --strict-json`.
|
|
281
|
+
|
|
282
|
+
Then check it:
|
|
283
|
+
|
|
284
|
+
```bash
|
|
285
|
+
openclaw config validate
|
|
286
|
+
openclaw grix doctor # the account should report configured: true, enabled: true
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
Finally send a message to the agent from Grix and confirm the reply really comes from it. Only if the config validates but messages do not route should you run `openclaw gateway restart` — once — and then re-send the message.
|
|
290
|
+
|
|
227
291
|
### Plugin Features
|
|
228
292
|
|
|
229
293
|
- **Channel**: Grix chat transport — routes messages between OpenClaw and your Grix deployment
|
|
230
294
|
- **Tools**: `grix_query`, `grix_group`, `grix_admin`, `grix_egg`, `grix_register`, `grix_update`, `grix_message_send`, `grix_message_unsend`, `openclaw_memory_setup`
|
|
231
|
-
- **CLI**: `openclaw grix` —
|
|
295
|
+
- **CLI**: `openclaw grix doctor` — local diagnostics (the only subcommand; configuration goes through `openclaw config set`)
|
|
232
296
|
- **Skills**: 9 bundled skills for admin, group, query, registration, update, messaging, memory setup, and egg orchestration
|
|
233
297
|
|
|
234
298
|
### Requirements
|
|
@@ -236,6 +300,8 @@ npm install grix-connector
|
|
|
236
300
|
- OpenClaw >= 2026.4.8
|
|
237
301
|
- A Grix account with agent ID and API key
|
|
238
302
|
|
|
303
|
+
Plugin mode and the standalone daemon can coexist on one machine — they use separate configs and separate processes. Do not, however, bring the **same** `agent_id` online in both at once; give each mode its own agent.
|
|
304
|
+
|
|
239
305
|
## License
|
|
240
306
|
|
|
241
307
|
MIT
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{readAllowlist as
|
|
1
|
+
import{readAllowlist as r,writeAllowlist as s}from"./allowlist-store.js";import{log as a}from"../log/index.js";class h{filePath;writeQueue=Promise.resolve();constructor(e){this.filePath=e}async checkAccess(e){if(!e?.trim())return!1;const t=r(this.filePath);return t.includes(e)?!0:t.length===0?this.autoAdd(e):!1}async addOwner(e){e?.trim()&&(this.writeQueue=this.writeQueue.then(async()=>{const t=r(this.filePath);t.includes(e)||(t.push(e),await s(this.filePath,t))}).catch(t=>{a.error("allowlist-gate",`addOwner failed for ${e}: ${t instanceof Error?t.message:t}`)}),await this.writeQueue)}async removeOwner(e){e?.trim()&&(this.writeQueue=this.writeQueue.then(async()=>{const t=r(this.filePath),i=t.filter(l=>l!==e);i.length!==t.length&&await s(this.filePath,i)}).catch(t=>{a.error("allowlist-gate",`removeOwner failed for ${e}: ${t instanceof Error?t.message:t}`)}),await this.writeQueue)}async listOwners(){return r(this.filePath)}autoAdd(e){return new Promise(t=>{this.writeQueue=this.writeQueue.then(async()=>{const i=r(this.filePath);if(i.includes(e)){t(!0);return}if(i.length>0){t(!1);return}await s(this.filePath,[e]),t(!0)}).catch(i=>{a.error("allowlist-gate",`autoAdd failed for ${e}: ${i instanceof Error?i.message:i}`),t(!1)})})}}export{h as AllowlistGate};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{readJSONFile as
|
|
1
|
+
import{readJSONFile as o,writeJSONFileAtomic as i}from"../util/json-file.js";function n(t){const r=o(t);return r&&typeof r=="object"&&"owners"in r&&Array.isArray(r.owners)?r.owners.filter(e=>typeof e=="string"&&e.trim().length>0):[]}async function s(t,r){await i(t,{owners:r})}export{n as readAllowlist,s as writeAllowlist};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{AllowlistGate as
|
|
1
|
+
import{AllowlistGate as l}from"./allowlist-gate.js";import{readAllowlist as t,writeAllowlist as s}from"./allowlist-store.js";export{l as AllowlistGate,t as readAllowlist,s as writeAllowlist};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const
|
|
1
|
+
const c="grix://card/",l="grix://open/",u="_submit",d=/\(\s*(grix:\/\/card\/[\s\S]+?)\s*\)/gi,m=/grix:\/\/card\/[^\s)\]]+/gi,f=/^grix:\/\/open\/\S+$/i;function g(t){const n=[],r=new Set;for(const e of p(t)){const s=h(e);!s||r.has(s.normalizedUri)||(r.add(s.normalizedUri),n.push(s))}return{cardSubmissions:n}}function C(t,n){return U(t.params.get(n))}function p(t){const n=S(t),r=[];for(const o of n.matchAll(d)){const i=a(o[1]);i&&r.push(i)}for(const o of n.matchAll(m)){const i=a(o[0]);i&&r.push(i)}const e=n.trim();f.test(e)&&r.push(e);const s=a(e);return s.startsWith(c)&&r.push(s),R(r)}function h(t){if(t.startsWith(l))return _(t);if(!t.startsWith(c))return null;let n;try{n=new URL(t)}catch{return null}if(n.hostname!=="card")return null;const r=n.pathname.replace(/^\/+/,"").trim();if(!r.endsWith(u))return null;const e=r.slice(0,-u.length).trim();return!e||e==="agent_open_session"?null:{cardId:e,rawUri:t,normalizedUri:n.toString(),params:n.searchParams}}function _(t){let n;try{n=new URL(t)}catch{return null}if(n.hostname!=="open")return null;const r=n.pathname.split("/").map(e=>e.trim()).filter(Boolean);return r.length!==1||r[0]!=="session"?null:{cardId:"agent_open_session",rawUri:t,normalizedUri:n.toString(),params:n.searchParams}}function S(t){return t.replace(/&/gi,"&")}function a(t){return t.replace(/\s+/g,"").trim()}function R(t){const n=[...new Set(t)];return n.filter(r=>!n.some(e=>e!==r&&e.startsWith(r)))}function U(t){if(!t)return;let n=t.trim();for(let r=0;r<3;r++)try{const e=decodeURIComponent(n);if(e===n)break;n=e}catch{break}return n.trim()||void 0}export{g as parseInteractionMessage,C as readCardSubmissionParam};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createServer as
|
|
1
|
+
import{createServer as o}from"node:http";import{log as a}from"../log/logger.js";import{resolveClientVersion as l}from"../util/client-version.js";class d{server=null;startTime=Date.now();alive=!0;statusProvider=null;version=l();setStatusProvider(e){this.statusProvider=e}async start(e){return new Promise((s,i)=>{this.server=o((n,t)=>{if(n.url==="/healthz")if(this.alive){const r=this.statusProvider?.();t.writeHead(200,{"Content-Type":"application/json"}),t.end(JSON.stringify({status:"ok",version:this.version,uptime:Math.floor((Date.now()-this.startTime)/1e3),pid:process.pid,...r?{agents:r}:{}}))}else t.writeHead(503,{"Content-Type":"application/json"}),t.end(JSON.stringify({status:"shutting_down"}));else t.writeHead(404),t.end()}),this.server.listen(e,"127.0.0.1",()=>{a.info("health",`Listening on 127.0.0.1:${e}`),s()}),this.server.on("error",i)})}markShuttingDown(){this.alive=!1}async stop(){if(this.server)return new Promise(e=>{this.server.close(()=>e())})}}export{d as HealthServer};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const r=["help","version","config-dir","profile","health-port","admin-port"],c=new Map([["-h","--help"],["-v","--version"]]);function p(e){const i=[],n=Object.create(null);for(let t=0;t<e.length;t++){const s=c.get(e[t])??e[t],o=e[t+1];s.startsWith("--")?o&&!o.startsWith("-")?(n[s.slice(2)]=o,t++):n[s.slice(2)]="true":i.push(s)}const l=new Set(r);return{command:i[0],flags:n,unknownFlags:Object.keys(n).filter(t=>!l.has(t))}}export{r as KNOWN_FLAGS,p as parseCliArgs};
|
package/dist/grix.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
2
|
+
import h from"node:path";import{writeFileSync as A}from"node:fs";import{Manager as _}from"./manager.js";import{ensureGrixDirs as N,initLogger as G,log as n,installProcessLogRotation as M,setConsoleOutput as j}from"./core/log/index.js";import{HealthServer as B,bindPortOrFail as $}from"./core/runtime/index.js";import{writePidFile as V,removePidFile as w,readDaemonPid as W}from"./core/runtime/index.js";import{resolveRuntimePaths as E}from"./core/config/index.js";import{ServiceManager as X}from"./service/service-manager.js";import{killProcessesByCommandLine as J,isWindowsElevated as K}from"./service/process-control.js";import{acquireDaemonLock as q,isLockHolderSameProcess as z,readDaemonLock as Q,releaseDaemonLock as S}from"./runtime/daemon-lock.js";import{writeDaemonStatus as k,removeDaemonStatus as Y}from"./runtime/service-state.js";import{AdminServer as Z,generateToken as ee,writeTokenFile as te}from"./core/admin/index.js";import{initSentry as oe,closeSentry as y,reportFatal as v}from"./core/observability/sentry.js";import{initProxyManager as re,getProxyManager as p}from"./core/proxy/index.js";import{resolveClientVersion as ae}from"./core/util/client-version.js";import{parseCliArgs as ne}from"./core/util/cli-args.js";const{command:u,flags:d,unknownFlags:P}=ne(process.argv.slice(2));if(P.length>0&&(console.error(`Unknown option${P.length>1?"s":""}: ${P.map(t=>`--${t}`).join(", ")}
|
|
3
|
+
Run \`grix-connector --help\` to see the supported commands and options.`),process.exit(1)),d.version&&(console.log(ae()),process.exit(0)),d.help&&(console.log(`grix-connector \u2014 Unified AI Agent Bridge
|
|
3
4
|
|
|
4
5
|
Usage: grix-connector <command> [options]
|
|
5
6
|
|
|
@@ -17,7 +18,8 @@ Options:
|
|
|
17
18
|
--profile <name> Profile name for config subdirectory
|
|
18
19
|
--health-port <port> Health check port (default: 19579)
|
|
19
20
|
--admin-port <port> Admin API port (default: 19580)
|
|
20
|
-
--
|
|
21
|
+
--version, -v Print the installed version and exit
|
|
22
|
+
--help, -h Show this help message
|
|
21
23
|
|
|
22
24
|
Platform services:
|
|
23
25
|
macOS: launchd (LaunchAgent)
|
|
@@ -32,8 +34,8 @@ Examples:
|
|
|
32
34
|
grix-connector start # Start as system service
|
|
33
35
|
grix-connector status # Check service status
|
|
34
36
|
grix-connector restart # Restart the service
|
|
35
|
-
`),process.exit(0))
|
|
37
|
+
`),process.exit(0)),u==="reload"){const t=W();t||(console.error("reload failed: daemon is not running (no pid file)"),process.exit(1));try{process.kill(t,0)}catch{console.error(`reload failed: daemon process ${t} is not running (stale pid file)`),process.exit(1)}{const c=Q(E().daemonLockFile);c&&c.pid===t&&!z(c)&&(console.error(`reload failed: pid ${t} has been reused by another process (stale pid file)`),process.exit(1))}try{process.kill(t,"SIGHUP"),console.log(JSON.stringify({ok:!0,signaled:t},null,2)),process.exit(0)}catch(c){console.error(`reload failed: ${c instanceof Error?c.message:c}`),process.exit(1)}}const T=["start","stop","restart","status"];if(u&&T.includes(u)){process.platform==="win32"&&["start","stop","restart"].includes(u)&&!K()&&console.warn(`Warning: Not running as administrator. Task Scheduler registration is skipped;
|
|
36
38
|
using Startup folder auto-start instead. For full Task Scheduler integration,
|
|
37
|
-
right-click the terminal and select "Run as administrator".`);const t=
|
|
38
|
-
Valid commands: ${
|
|
39
|
-
`),n.error("main",t.message.replace(/\n/g," \u2014 ")),await k(a.daemonStatusFile,{state:"failed",pid:process.pid,updated_at:Date.now(),reason:`port_bind_${t.kind}:${t.label}:${t.port}`}).catch(()=>{}),await
|
|
39
|
+
right-click the terminal and select "Run as administrator".`);const t=E(),c=d["config-dir"]??(d.profile?h.join(t.configDir,d.profile):void 0),g=h.resolve(process.argv[1]||`${t.rootDir}/dist/grix.js`),r=new X({cliPath:g,nodePath:process.execPath});try{let s;switch(u){case"start":(await r.status({rootDir:t.rootDir})).installed?s=await r.start({rootDir:t.rootDir}):s=await r.install({rootDir:t.rootDir,configDir:c});break;case"stop":s=await r.stop({rootDir:t.rootDir});break;case"restart":(await r.status({rootDir:t.rootDir})).installed?s=await r.restart({rootDir:t.rootDir}):s=await r.install({rootDir:t.rootDir,configDir:c});break;case"status":s=await r.status({rootDir:t.rootDir});break}console.log(JSON.stringify(s,null,2)),process.exit(0)}catch(s){console.error(`${u} failed: ${s instanceof Error?s.message:s}`),process.exit(1)}}else u&&(console.error(`Unknown command: ${u}
|
|
40
|
+
Valid commands: ${T.join(", ")}`),process.exit(1));const a=E(),ie=d["config-dir"]??(d.profile?`${a.configDir}/${d.profile}`:void 0),i=new _,x=new B,O=ee(),f=new Z(O);let R=!1;async function C(t){process.stderr.write(t.message+`
|
|
41
|
+
`),n.error("main",t.message.replace(/\n/g," \u2014 ")),await k(a.daemonStatusFile,{state:"failed",pid:process.pid,updated_at:Date.now(),reason:`port_bind_${t.kind}:${t.label}:${t.port}`}).catch(()=>{}),await y(),S(a.daemonLockFile).catch(()=>{}),w(),process.exit(1)}async function D(t){if(R)return;R=!0,n.info("main",`Received ${t}, shutting down...`),x.markShuttingDown();const c=setTimeout(()=>{n.error("main","Shutdown timed out, forcing exit"),S(a.daemonLockFile).catch(()=>{}),w(),process.exit(2)},1e4);try{await i.stop(),await p()?.stop().catch(()=>{}),await f.stop(),await x.stop(),await y(),await S(a.daemonLockFile),await Y(a.daemonStatusFile).catch(()=>{}),clearTimeout(c),w(),n.info("main","Shutdown complete"),process.exit(0)}catch(g){n.error("main",`Shutdown error: ${g}`),S(a.daemonLockFile).catch(()=>{}),w(),process.exit(2)}}async function se(){N(),G(),await oe(),M(a.stdoutLogFile,a.stderrLogFile),j(!1),process.platform==="win32"&&await J("GrixConnectorDaemon",{platform:"win32"});try{await q(a.daemonLockFile,a.rootDir)}catch(e){console.error(e instanceof Error?e.message:e),process.exit(1)}V(),n.info("main",`grix-connector starting (PID ${process.pid})`),await k(a.daemonStatusFile,{state:"starting",pid:process.pid,updated_at:Date.now()});const t=parseInt(d["health-port"]??process.env.GRIX_HEALTH_PORT??"19579",10);{const e=await $({label:"health",port:t,envVar:"GRIX_HEALTH_PORT",cliFlag:"health-port",start:o=>x.start(o)});e&&await C(e)}const c=h.join(a.dataDir,"health-port");A(c,String(t),"utf-8"),process.on("SIGINT",()=>D("SIGINT")),process.on("SIGTERM",()=>D("SIGTERM")),process.on("SIGHUP",()=>{R||(n.info("main","Received SIGHUP, reloading config..."),i.reload().then(e=>n.info("main",`reload done: ${JSON.stringify(e)}`)).catch(e=>n.error("main",`reload failed: ${e instanceof Error?e.message:e}`)))});let g="",r=0,s;process.on("uncaughtException",e=>{const o=e instanceof Error?e.stack??e.message:String(e);o===g?(r++,(r<=3||r%100===0)&&n.error("main",`Uncaught exception (x${r}): ${o}`)):(r>3&&n.error("main",`Previous exception repeated ${r} times total`),g=o,r=1,n.error("main",`Uncaught exception: ${e instanceof Error?e.stack:e}`),s||(s=setTimeout(()=>{r>3&&n.error("main",`Previous exception repeated ${r} times total`),g="",r=0,s=void 0},1e4).unref())),!L(e)&&(v(e,"uncaughtException"),D("uncaughtException"))}),process.on("unhandledRejection",e=>{n.error("main",`Unhandled rejection: ${e}`),!L(e)&&(v(e,"unhandledRejection"),D("unhandledRejection"))}),await re(a.dataDir).clearRuntimeState(),n.info("main",'mitm proxy disabled by default (enable online: PUT /api/proxy/enabled {"enabled":true}; not persisted, resets to off on restart)'),x.setStatusProvider(()=>i.getAgentsStatus()),await i.start(ie);const I=parseInt(d["admin-port"]??process.env.GRIX_ADMIN_PORT??"19580",10);if(f.setAgentHandler({list:()=>i.getAgentsStatus(),add:e=>i.addAgent(e),remove:e=>i.removeAgent(e),restart:e=>i.restartAgent(e),reload:()=>i.reload()}),f.setUpgradeHandler({check:()=>i.checkUpgrade(),trigger:()=>i.triggerUpgrade()}),f.setProbeHandler({probeAll:e=>i.probeAll(e),probeOne:(e,o)=>i.probeOne(e,o)}),f.setInstallHandler({listInstallable:()=>i.listInstallable(),installAgent:e=>i.installAgent(e),getInstallProgress:e=>i.getInstallProgress(e)}),p()){const e=()=>{const o=p();return{enabled:o.isInjectionEnabled(),runtime:o.getRuntimeInfo(),config:o.getConfigSnapshot()}};f.setProxyHandler({status:()=>e(),enable:async()=>(await p().enable(),e()),disable:async()=>(await p().disable(),e()),setRoute:async(o,l)=>{const m=l,F=p();return F.setRoute({routeKey:o,targetBaseUrl:m.targetBaseUrl,...m.headers?{headers:m.headers}:{},...m.model?{model:m.model}:{},...m.modelMap?{modelMap:m.modelMap}:{},...m.passthrough?{passthrough:!0}:{}}),await F.persistConfig(),e()},deleteRoute:async o=>{const l=p();l.deleteRoute(o),await l.persistConfig()},setDefaultRoute:async o=>{const l=p();return l.setDefaultRouteKey(o),await l.persistConfig(),e()},setInterceptHosts:async o=>{const l=p();return l.setInterceptHosts(o),await l.persistConfig(),e()}})}{const e=await $({label:"admin",port:I,envVar:"GRIX_ADMIN_PORT",cliFlag:"admin-port",start:o=>f.start(o)});e&&await C(e)}const H=h.join(a.dataDir,"admin-token"),U=h.join(a.dataDir,"admin-port");te(H,O),A(U,String(I),"utf-8"),await k(a.daemonStatusFile,{state:"running",pid:process.pid,updated_at:Date.now()}),process.send&&process.send("ready"),n.info("main","grix-connector ready")}se().catch(async t=>{n.error("main",`Fatal: ${t}`),v(t,"startup"),await y(),S(a.daemonLockFile).catch(()=>{}),w(),process.exit(1)});const ce=new Set(["ECONNRESET","ECONNREFUSED","ETIMEDOUT","EPIPE","EAI_AGAIN","ENOTFOUND","EHOSTUNREACH","ENETUNREACH","EIO"]);function L(t){return t instanceof Error&&"code"in t?ce.has(t.code):!1}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
function a(
|
|
1
|
+
function a(o){const e=new Set([`http://127.0.0.1:${o.serverPort}`,`http://localhost:${o.serverPort}`,...o.allowedOrigins]),t=new Set([`127.0.0.1:${o.serverPort}`,`localhost:${o.serverPort}`,...o.allowedHosts]);return{validateRequest(s){const r=i(s,e);if(!r.ok)return r;const n=l(s,t);return n.ok?{ok:!0}:n}}}function i(o,e){const t=o.headers.origin;return t?e.has(t)?{ok:!0}:{ok:!1,statusCode:403,message:`Origin not allowed: ${t}`}:{ok:!0}}function l(o,e){const t=o.headers.host;return t?e.has(t)?{ok:!0}:{ok:!1,statusCode:403,message:`Host not allowed: ${t}`}:{ok:!1,statusCode:403,message:"Missing Host header"}}export{a as createSecurityPolicy};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "grix-connector",
|
|
3
|
-
"version": "3.3.
|
|
3
|
+
"version": "3.3.5",
|
|
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",
|