grix-connector 4.5.0 → 4.5.1
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/claude/claude-bridge-server.js +1 -1
- package/dist/adapter/claude/claude-tools.js +1 -1
- package/dist/adapter/claude/claude-worker-client.js +1 -1
- package/dist/adapter/claude/mcp-http-launcher.js +2 -2
- package/dist/adapter/claude/result-timeout.js +1 -1
- package/dist/assets/dsh-bridge/{grix-dsh-bridge-4.5.0.tgz → grix-dsh-bridge-4.5.1.tgz} +0 -0
- package/dist/assets/dsh-bridge/manifest.json +4 -4
- package/dist/core/access/allowlist-store.js +1 -1
- package/dist/core/file-ops/list-files.js +1 -1
- package/dist/core/installer/npm-registry.js +1 -1
- package/dist/core/upgrade/npm-upgrader.js +2 -2
- package/dist/core/upgrade/upgrade-checker.js +1 -1
- package/dist/core/upgrade/version-store.js +2 -2
- package/dist/grix.js +7 -7
- package/dist/launcher.mjs +98 -35
- package/dist/log.js +2 -2
- package/dist/manager.js +1 -1
- package/dist/mcp/stream-http/config.js +1 -1
- package/dist/mcp/stream-http/connection-binding.js +1 -1
- package/dist/mcp/stream-http/security.js +1 -1
- package/dist/mcp/stream-http/tool-executor.js +1 -1
- package/dist/mcp/stream-http/tool-registry.js +1 -1
- package/dist/mcp/stream-http/tool-schemas.js +1 -1
- package/package.json +1 -1
- package/scripts/launcher.mjs +98 -35
|
@@ -1 +1 @@
|
|
|
1
|
-
const o={required:["action"],properties:{action:{type:"string",enum:["contact_search","session_search","message_history","message_search"]},id:{type:"string"},keyword:{type:"string",maxLength:200},limit:{type:"integer",minimum:1,maximum:100},offset:{type:"integer",minimum:0},sessionId:{type:"string"},beforeId:{type:"string"}}},a={required:["action"],properties:{action:{type:"string",enum:["create","detail","leave","add_members","remove_members","update_member_role","update_all_members_muted","update_member_speaking","dissolve"]},sessionId:{type:"string"},name:{type:"string",maxLength:128},memberIds:{type:"array",items:{type:"string"},maxItems:100},memberTypes:{type:"array",items:{type:"integer",enum:[1,2]}},memberId:{type:"string"},role:{type:"integer",enum:[1,2]},memberType:{type:"integer"},allMembersMuted:{type:"boolean"},isSpeakMuted:{type:"boolean"},canSpeakWhenAllMuted:{type:"boolean"}}},p={required:["sessionId","content"],properties:{sessionId:{type:"string"},content:{type:"string",maxLength:1e4},msgType:{type:"integer"},quotedMessageId:{type:"string"},threadId:{type:"string"}}},m={required:["sessionId","msgId"],properties:{sessionId:{type:"string"},msgId:{type:"string"}}},g={required:["action"],properties:{action:{type:"string",enum:["create_agent","list_categories","create_category","update_category","assign_category","rotate_api_key"]},agentName:{type:"string"},introduction:{type:"string"},isMain:{type:"boolean"},agentId:{type:"string"},categoryId:{type:"string"},name:{type:"string"},parentId:{type:"string"},sortOrder:{type:"integer"}}},y={required:["session_id","text"],properties:{event_id:{type:"string"},session_id:{type:"string"},text:{type:"string",maxLength:5e4},quoted_message_id:{type:"string"},is_final:{type:"boolean"}}},d={required:["event_id","status"],properties:{event_id:{type:"string"},status:{type:"string",enum:["responded","canceled","failed"]},msg:{type:"string",maxLength:500}}},c={required:["event_id"],properties:{event_id:{type:"string"},session_id:{type:"string"}}},l={required:["session_id","active"],properties:{session_id:{type:"string"},active:{type:"boolean"},event_id:{type:"string"}}},_={required:["action"],properties:{action:{type:"string",enum:["pair_approve","pair_deny","allow_sender","remove_sender","set_policy"]},code:{type:"string"},sender_id:{type:"string"},policy:{type:"string",enum:["allowlist","open","disabled"]}}},f={required:[],properties:{}},C={grix_query:o,grix_group:a,grix_message_send:p,grix_message_unsend:m,grix_admin:g,grix_reply:y,grix_complete:d,grix_event_ack:c,grix_composing:l,grix_access_control:_,grix_status:f};function B(
|
|
1
|
+
const o={required:["action"],properties:{action:{type:"string",enum:["contact_search","session_search","message_history","message_search"]},id:{type:"string"},keyword:{type:"string",maxLength:200},limit:{type:"integer",minimum:1,maximum:100},offset:{type:"integer",minimum:0},sessionId:{type:"string"},beforeId:{type:"string"}}},a={required:["action"],properties:{action:{type:"string",enum:["create","detail","leave","add_members","remove_members","update_member_role","update_all_members_muted","update_member_speaking","dissolve"]},sessionId:{type:"string"},name:{type:"string",maxLength:128},memberIds:{type:"array",items:{type:"string"},maxItems:100},memberTypes:{type:"array",items:{type:"integer",enum:[1,2]}},memberId:{type:"string"},role:{type:"integer",enum:[1,2]},memberType:{type:"integer"},allMembersMuted:{type:"boolean"},isSpeakMuted:{type:"boolean"},canSpeakWhenAllMuted:{type:"boolean"}}},p={required:["sessionId","content"],properties:{sessionId:{type:"string"},content:{type:"string",maxLength:1e4},msgType:{type:"integer"},quotedMessageId:{type:"string"},threadId:{type:"string"}}},m={required:["sessionId","msgId"],properties:{sessionId:{type:"string"},msgId:{type:"string"}}},g={required:["action"],properties:{action:{type:"string",enum:["create_agent","list_categories","create_category","update_category","assign_category","rotate_api_key"]},agentName:{type:"string"},introduction:{type:"string"},isMain:{type:"boolean"},agentId:{type:"string"},categoryId:{type:"string"},name:{type:"string"},parentId:{type:"string"},sortOrder:{type:"integer"}}},y={required:["session_id","text"],properties:{event_id:{type:"string"},session_id:{type:"string"},text:{type:"string",maxLength:5e4},quoted_message_id:{type:"string"},is_final:{type:"boolean"}}},d={required:["event_id","status"],properties:{event_id:{type:"string"},status:{type:"string",enum:["responded","canceled","failed"]},msg:{type:"string",maxLength:500}}},c={required:["event_id"],properties:{event_id:{type:"string"},session_id:{type:"string"}}},l={required:["session_id","active"],properties:{session_id:{type:"string"},active:{type:"boolean"},event_id:{type:"string"}}},_={required:["action"],properties:{action:{type:"string",enum:["pair_approve","pair_deny","allow_sender","remove_sender","set_policy"]},code:{type:"string"},sender_id:{type:"string"},policy:{type:"string",enum:["allowlist","open","disabled"]}}},f={required:[],properties:{}},C={grix_query:o,grix_group:a,grix_message_send:p,grix_message_unsend:m,grix_admin:g,grix_reply:y,grix_complete:d,grix_event_ack:c,grix_composing:l,grix_access_control:_,grix_status:f};function B(r,t){const e=C[r];if(!e)return{valid:!1,error:`\u672A\u77E5\u5DE5\u5177: ${r}`};for(const i of e.required)if(t[i]===void 0||t[i]===null)return{valid:!1,error:`\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570: ${i}`};for(const[i,u]of Object.entries(t)){if(u==null)continue;const n=e.properties[i];if(!n)continue;const s=$(i,u,n);if(s)return{valid:!1,error:s}}return{valid:!0}}function $(r,t,e){switch(e.type){case"string":if(typeof t!="string")return`\u53C2\u6570 ${r} \u7C7B\u578B\u9519\u8BEF: \u671F\u671B string\uFF0C\u5B9E\u9645 ${typeof t}`;if(e.maxLength!==void 0&&t.length>e.maxLength)return`\u53C2\u6570 ${r} \u8D85\u8FC7\u6700\u5927\u957F\u5EA6 ${e.maxLength}\uFF0C\u5B9E\u9645 ${t.length}`;if(e.enum&&!e.enum.includes(t))return`\u53C2\u6570 ${r} \u503C "${t}" \u4E0D\u5728\u5141\u8BB8\u8303\u56F4 [${e.enum.join(", ")}]`;break;case"integer":if(typeof t!="number"||!Number.isInteger(t))return`\u53C2\u6570 ${r} \u7C7B\u578B\u9519\u8BEF: \u671F\u671B integer\uFF0C\u5B9E\u9645 ${typeof t=="number"?"\u6D6E\u70B9\u6570":typeof t}`;if(e.minimum!==void 0&&t<e.minimum)return`\u53C2\u6570 ${r} \u503C ${t} \u5C0F\u4E8E\u6700\u5C0F\u503C ${e.minimum}`;if(e.maximum!==void 0&&t>e.maximum)return`\u53C2\u6570 ${r} \u503C ${t} \u5927\u4E8E\u6700\u5927\u503C ${e.maximum}`;if(e.enum&&!e.enum.includes(t))return`\u53C2\u6570 ${r} \u503C ${t} \u4E0D\u5728\u5141\u8BB8\u8303\u56F4 [${e.enum.join(", ")}]`;break;case"boolean":if(typeof t!="boolean")return`\u53C2\u6570 ${r} \u7C7B\u578B\u9519\u8BEF: \u671F\u671B boolean\uFF0C\u5B9E\u9645 ${typeof t}`;break;case"array":if(!Array.isArray(t))return`\u53C2\u6570 ${r} \u7C7B\u578B\u9519\u8BEF: \u671F\u671B array\uFF0C\u5B9E\u9645 ${typeof t}`;if(e.maxItems!==void 0&&t.length>e.maxItems)return`\u53C2\u6570 ${r} \u8D85\u8FC7\u6700\u5927\u5143\u7D20\u6570 ${e.maxItems}\uFF0C\u5B9E\u9645 ${t.length}`;if(e.items)for(let i=0;i<t.length;i++){const u=t[i];if(e.items.type==="string"&&typeof u!="string")return`\u53C2\u6570 ${r}[${i}] \u7C7B\u578B\u9519\u8BEF: \u671F\u671B string\uFF0C\u5B9E\u9645 ${typeof u}`;if(e.items.type==="integer"){if(typeof u!="number"||!Number.isInteger(u))return`\u53C2\u6570 ${r}[${i}] \u7C7B\u578B\u9519\u8BEF: \u671F\u671B integer\uFF0C\u5B9E\u9645 ${typeof u}`;if(e.items.enum&&!e.items.enum.includes(u))return`\u53C2\u6570 ${r}[${i}] \u503C ${u} \u4E0D\u5728\u5141\u8BB8\u8303\u56F4 [${e.items.enum.join(", ")}]`}}break}}export{B as validateToolArgs};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "grix-connector",
|
|
3
|
-
"version": "4.5.
|
|
3
|
+
"version": "4.5.1",
|
|
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",
|
package/scripts/launcher.mjs
CHANGED
|
@@ -7,13 +7,16 @@
|
|
|
7
7
|
// - 退出码 75(RESTART_EXIT_CODE):升级切换 / 重载请求,立即重新读指针拉起;
|
|
8
8
|
// - 退出码 0 或被信号终止:daemon 是被要求停下的,launcher 随之退出,保活与否交给
|
|
9
9
|
// 上层 supervisor(launchd KeepAlive / Windows wrapper),语义与 launcher 出现前一致;
|
|
10
|
-
// 否则 `grix-connector stop` 的兜底强杀会被 launcher
|
|
10
|
+
// 否则 `grix-connector stop` 的兜底强杀会被 launcher 复活。不是 launcher 转发的信号
|
|
11
|
+
// (OOM/SIGKILL 等)仍按崩溃记账,让 supervisor 重拉后的 launcher 能接着数;daemon 进入
|
|
12
|
+
// 优雅关停时会写 `<store>/managed-stop`,关停卡住后被兜底强杀的不算崩溃。
|
|
11
13
|
// - 其他非零退出:按退避重启;若该版本从未写过 `<store>/health/<ver>.ok` 且 5 分钟内快崩
|
|
12
14
|
// 3 次,则把 `current` 切回 `previous`,写 `<store>/rollback.json` 供旧版本上报。
|
|
15
|
+
// 崩溃时间戳落在 `<store>/crashes.json`,launcher 自身被重拉也不会把计数归零。
|
|
13
16
|
// 收到 SIGTERM/SIGINT/SIGHUP 时转发给子进程并随其退出(服务停止语义)。
|
|
14
17
|
//
|
|
15
18
|
// 本文件由 daemon 在启动时按 LAUNCHER_TEMPLATE_VERSION 比对并原子重写,改动行为时必须 +1。
|
|
16
|
-
export const LAUNCHER_TEMPLATE_VERSION =
|
|
19
|
+
export const LAUNCHER_TEMPLATE_VERSION = 2;
|
|
17
20
|
|
|
18
21
|
import { spawn } from 'node:child_process';
|
|
19
22
|
import { existsSync, mkdirSync, readFileSync, realpathSync, renameSync, rmSync, writeFileSync, appendFileSync } from 'node:fs';
|
|
@@ -48,6 +51,79 @@ function writePointer(store, name, value) {
|
|
|
48
51
|
renameSync(tmp, file);
|
|
49
52
|
}
|
|
50
53
|
|
|
54
|
+
function crashFile(store) {
|
|
55
|
+
return join(store, 'crashes.json');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** daemon 自己在优雅关停开始时写下的标记:随后的信号致死是管理性终止,不记崩溃。 */
|
|
59
|
+
function consumeManagedStop(store) {
|
|
60
|
+
const file = join(store, 'managed-stop');
|
|
61
|
+
const present = existsSync(file);
|
|
62
|
+
rmSync(file, { force: true });
|
|
63
|
+
return present;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** 读取该版本在快崩窗口内的崩溃时间戳;文件属于别的版本或损坏时视为空。 */
|
|
67
|
+
function loadCrashes(store, version) {
|
|
68
|
+
try {
|
|
69
|
+
const parsed = JSON.parse(readFileSync(crashFile(store), 'utf8'));
|
|
70
|
+
if (parsed?.version !== version || !Array.isArray(parsed.at)) return [];
|
|
71
|
+
const now = Date.now();
|
|
72
|
+
return parsed.at.filter((at) => typeof at === 'number' && now - at < RAPID_CRASH_WINDOW_MS);
|
|
73
|
+
} catch {
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function saveCrashes(store, version, crashes) {
|
|
79
|
+
try {
|
|
80
|
+
if (crashes.length === 0) {
|
|
81
|
+
rmSync(crashFile(store), { force: true });
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const tmp = `${crashFile(store)}.tmp-${process.pid}`;
|
|
85
|
+
writeFileSync(tmp, JSON.stringify({ version, at: crashes }), 'utf8');
|
|
86
|
+
renameSync(tmp, crashFile(store));
|
|
87
|
+
} catch { /* best effort */ }
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* 记一次未达健康的崩溃;达到阈值且有可用的 previous 时切回并写 rollback.json。
|
|
92
|
+
* 返回 true 表示已回滚(调用方应立即按新指针重拉)。
|
|
93
|
+
*/
|
|
94
|
+
function noteCrashAndMaybeRollback(store, version, code, startedAt) {
|
|
95
|
+
if (existsSync(join(store, 'health', `${version}.ok`))) {
|
|
96
|
+
saveCrashes(store, version, []);
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
const now = Date.now();
|
|
100
|
+
const crashes = loadCrashes(store, version);
|
|
101
|
+
crashes.push(now);
|
|
102
|
+
saveCrashes(store, version, crashes);
|
|
103
|
+
if (crashes.length < RAPID_CRASH_LIMIT) return false;
|
|
104
|
+
const previous = readPointer(store, 'previous');
|
|
105
|
+
if (!previous || previous === version || !existsSync(entryFor(store, previous))) {
|
|
106
|
+
log(store, `${version} keeps crashing and there is no previous version to roll back to`);
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
log(store, `${version} crashed ${crashes.length}x without becoming healthy; rolling back to ${previous}`);
|
|
110
|
+
const record = {
|
|
111
|
+
from_version: previous,
|
|
112
|
+
to_version: version,
|
|
113
|
+
exit_code: code,
|
|
114
|
+
crash_count: crashes.length,
|
|
115
|
+
uptime_ms: now - startedAt,
|
|
116
|
+
rolled_back_at: new Date().toISOString(),
|
|
117
|
+
};
|
|
118
|
+
const rollbackFile = join(store, 'rollback.json');
|
|
119
|
+
writeFileSync(`${rollbackFile}.tmp-${process.pid}`, JSON.stringify(record), 'utf8');
|
|
120
|
+
renameSync(`${rollbackFile}.tmp-${process.pid}`, rollbackFile);
|
|
121
|
+
writePointer(store, 'current', previous);
|
|
122
|
+
rmSync(join(store, 'previous'), { force: true });
|
|
123
|
+
saveCrashes(store, version, []);
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
|
|
51
127
|
function entryFor(store, version) {
|
|
52
128
|
return join(store, 'versions', version, 'dist', 'grix.js');
|
|
53
129
|
}
|
|
@@ -107,7 +183,6 @@ async function main() {
|
|
|
107
183
|
const store = resolveStoreDir();
|
|
108
184
|
const args = process.argv.slice(2);
|
|
109
185
|
mkdirSync(join(store, 'health'), { recursive: true });
|
|
110
|
-
let crashes = [];
|
|
111
186
|
let backoffMs = 1000;
|
|
112
187
|
log(store, `started node=${process.version} store=${store} template=${LAUNCHER_TEMPLATE_VERSION}`);
|
|
113
188
|
|
|
@@ -125,47 +200,35 @@ async function main() {
|
|
|
125
200
|
}
|
|
126
201
|
}
|
|
127
202
|
|
|
203
|
+
// launcher 自己被 supervisor 重拉时,上一轮落盘的崩溃可能已经够数(例如被 OOM 杀了三次)。
|
|
204
|
+
if (loadCrashes(store, version).length >= RAPID_CRASH_LIMIT
|
|
205
|
+
&& noteCrashAndMaybeRollback(store, version, null, Date.now())) {
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
|
|
128
209
|
const startedAt = Date.now();
|
|
210
|
+
consumeManagedStop(store);
|
|
129
211
|
log(store, `starting ${version}`);
|
|
130
212
|
const { code, signal, stopping } = await runChild(store, version, args);
|
|
131
213
|
log(store, `daemon ${version} exited code=${code} signal=${signal ?? '-'}`);
|
|
132
|
-
|
|
214
|
+
const managedStop = consumeManagedStop(store);
|
|
215
|
+
if (stopping || code === 0) process.exit(typeof code === 'number' ? code : 0);
|
|
216
|
+
if (signal) {
|
|
217
|
+
// 不是我们转发的信号:除非 daemon 已进入优雅关停(管理性终止的兜底强杀),
|
|
218
|
+
// 否则记一次崩溃;随后仍交还 supervisor,重拉后的 launcher 接着数。
|
|
219
|
+
if (managedStop) log(store, `${version} was terminated during a managed stop; not counting as a crash`);
|
|
220
|
+
else noteCrashAndMaybeRollback(store, version, code, startedAt);
|
|
221
|
+
process.exit(code);
|
|
222
|
+
}
|
|
133
223
|
if (code === RESTART_EXIT_CODE) {
|
|
134
|
-
|
|
224
|
+
saveCrashes(store, version, []);
|
|
135
225
|
backoffMs = 1000;
|
|
136
226
|
continue;
|
|
137
227
|
}
|
|
138
228
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
crashes = crashes.filter((at) => now - at < RAPID_CRASH_WINDOW_MS);
|
|
143
|
-
crashes.push(now);
|
|
144
|
-
if (crashes.length >= RAPID_CRASH_LIMIT) {
|
|
145
|
-
const previous = readPointer(store, 'previous');
|
|
146
|
-
if (previous && previous !== version && existsSync(entryFor(store, previous))) {
|
|
147
|
-
log(store, `${version} crashed ${crashes.length}x without becoming healthy; rolling back to ${previous}`);
|
|
148
|
-
const record = {
|
|
149
|
-
from_version: previous,
|
|
150
|
-
to_version: version,
|
|
151
|
-
exit_code: code,
|
|
152
|
-
crash_count: crashes.length,
|
|
153
|
-
uptime_ms: now - startedAt,
|
|
154
|
-
rolled_back_at: new Date().toISOString(),
|
|
155
|
-
};
|
|
156
|
-
const rollbackFile = join(store, 'rollback.json');
|
|
157
|
-
writeFileSync(`${rollbackFile}.tmp-${process.pid}`, JSON.stringify(record), 'utf8');
|
|
158
|
-
renameSync(`${rollbackFile}.tmp-${process.pid}`, rollbackFile);
|
|
159
|
-
writePointer(store, 'current', previous);
|
|
160
|
-
rmSync(join(store, 'previous'), { force: true });
|
|
161
|
-
crashes = [];
|
|
162
|
-
backoffMs = 1000;
|
|
163
|
-
continue;
|
|
164
|
-
}
|
|
165
|
-
log(store, `${version} keeps crashing and there is no previous version to roll back to`);
|
|
166
|
-
}
|
|
167
|
-
} else {
|
|
168
|
-
crashes = [];
|
|
229
|
+
if (noteCrashAndMaybeRollback(store, version, code, startedAt)) {
|
|
230
|
+
backoffMs = 1000;
|
|
231
|
+
continue;
|
|
169
232
|
}
|
|
170
233
|
|
|
171
234
|
await sleep(backoffMs);
|