@dhf-openclaw/grix 0.4.9 → 0.4.10
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 +128 -143
- package/dist/index.js +1279 -1
- package/package.json +4 -6
- package/skills/egg-install/SKILL.md +170 -0
- package/skills/egg-install/references/api-contract.md +38 -0
- package/skills/grix-agent-admin/SKILL.md +85 -0
- package/skills/grix-agent-admin/agents/openai.yaml +4 -0
- package/skills/grix-agent-admin/references/api-contract.md +87 -0
- package/skills/grix-agent-admin/scripts/grix_agent_bind.py +587 -0
- package/skills/grix-group-governance/SKILL.md +144 -0
- package/skills/grix-group-governance/agents/openai.yaml +4 -0
- package/skills/grix-group-governance/references/api-contract.md +73 -0
- package/skills/grix-query/SKILL.md +151 -0
- package/skills/grix-query/agents/openai.yaml +4 -0
package/dist/index.js
CHANGED
|
@@ -5056,6 +5056,1278 @@ var aibotPlugin = {
|
|
|
5056
5056
|
}
|
|
5057
5057
|
};
|
|
5058
5058
|
|
|
5059
|
+
// src/admin/agent-api-actions.ts
|
|
5060
|
+
var AGENT_NAME_RE = /^[a-z][a-z0-9-]{2,31}$/;
|
|
5061
|
+
function readRawParam(params, key) {
|
|
5062
|
+
if (Object.hasOwn(params, key)) {
|
|
5063
|
+
return params[key];
|
|
5064
|
+
}
|
|
5065
|
+
return void 0;
|
|
5066
|
+
}
|
|
5067
|
+
function isPlainObject(value) {
|
|
5068
|
+
return value != null && typeof value === "object" && !Array.isArray(value);
|
|
5069
|
+
}
|
|
5070
|
+
function readStringParam2(params, key) {
|
|
5071
|
+
const raw = readRawParam(params, key);
|
|
5072
|
+
if (typeof raw === "string") {
|
|
5073
|
+
return raw.trim();
|
|
5074
|
+
}
|
|
5075
|
+
if (typeof raw === "number" && Number.isFinite(raw)) {
|
|
5076
|
+
return String(raw);
|
|
5077
|
+
}
|
|
5078
|
+
return "";
|
|
5079
|
+
}
|
|
5080
|
+
function readRequiredStringParam(params, key) {
|
|
5081
|
+
const value = readStringParam2(params, key);
|
|
5082
|
+
if (!value) {
|
|
5083
|
+
throw new Error(`Grix action requires ${key}.`);
|
|
5084
|
+
}
|
|
5085
|
+
return value;
|
|
5086
|
+
}
|
|
5087
|
+
function readArrayParam(params, key) {
|
|
5088
|
+
const raw = readRawParam(params, key);
|
|
5089
|
+
if (raw == null) {
|
|
5090
|
+
return void 0;
|
|
5091
|
+
}
|
|
5092
|
+
if (!Array.isArray(raw)) {
|
|
5093
|
+
throw new Error(`Grix action requires ${key} as array.`);
|
|
5094
|
+
}
|
|
5095
|
+
return raw;
|
|
5096
|
+
}
|
|
5097
|
+
function readNumericIDArray(params, key, required) {
|
|
5098
|
+
const values = readArrayParam(params, key);
|
|
5099
|
+
if (!values || values.length == 0) {
|
|
5100
|
+
if (required) {
|
|
5101
|
+
throw new Error(`Grix action requires non-empty ${key}.`);
|
|
5102
|
+
}
|
|
5103
|
+
return [];
|
|
5104
|
+
}
|
|
5105
|
+
const normalized = [];
|
|
5106
|
+
for (const item of values) {
|
|
5107
|
+
const value = typeof item === "string" ? item.trim() : typeof item === "number" && Number.isFinite(item) ? String(Math.trunc(item)) : "";
|
|
5108
|
+
if (!/^\d+$/.test(value)) {
|
|
5109
|
+
throw new Error(`Grix action ${key} must contain numeric IDs.`);
|
|
5110
|
+
}
|
|
5111
|
+
normalized.push(value);
|
|
5112
|
+
}
|
|
5113
|
+
return normalized;
|
|
5114
|
+
}
|
|
5115
|
+
function readIntArray(params, key) {
|
|
5116
|
+
const values = readArrayParam(params, key);
|
|
5117
|
+
if (!values || values.length == 0) {
|
|
5118
|
+
return [];
|
|
5119
|
+
}
|
|
5120
|
+
const normalized = [];
|
|
5121
|
+
for (const item of values) {
|
|
5122
|
+
const num = typeof item === "number" ? item : Number(String(item ?? "").trim());
|
|
5123
|
+
if (!Number.isInteger(num)) {
|
|
5124
|
+
throw new Error(`Grix action ${key} must contain integers.`);
|
|
5125
|
+
}
|
|
5126
|
+
normalized.push(num);
|
|
5127
|
+
}
|
|
5128
|
+
return normalized;
|
|
5129
|
+
}
|
|
5130
|
+
function readOptionalInt(params, key) {
|
|
5131
|
+
const raw = readRawParam(params, key);
|
|
5132
|
+
if (raw == null) {
|
|
5133
|
+
return void 0;
|
|
5134
|
+
}
|
|
5135
|
+
const num = typeof raw === "number" ? raw : Number(String(raw).trim());
|
|
5136
|
+
if (!Number.isInteger(num)) {
|
|
5137
|
+
throw new Error(`Grix action ${key} must be an integer.`);
|
|
5138
|
+
}
|
|
5139
|
+
return num;
|
|
5140
|
+
}
|
|
5141
|
+
function readRequiredInt(params, key) {
|
|
5142
|
+
const value = readOptionalInt(params, key);
|
|
5143
|
+
if (value == null) {
|
|
5144
|
+
throw new Error(`Grix action requires ${key}.`);
|
|
5145
|
+
}
|
|
5146
|
+
return value;
|
|
5147
|
+
}
|
|
5148
|
+
function readOptionalBool(params, key) {
|
|
5149
|
+
const raw = readRawParam(params, key);
|
|
5150
|
+
if (raw == null) {
|
|
5151
|
+
return void 0;
|
|
5152
|
+
}
|
|
5153
|
+
if (typeof raw === "boolean") {
|
|
5154
|
+
return raw;
|
|
5155
|
+
}
|
|
5156
|
+
if (typeof raw === "number") {
|
|
5157
|
+
if (raw === 1) return true;
|
|
5158
|
+
if (raw === 0) return false;
|
|
5159
|
+
}
|
|
5160
|
+
if (typeof raw === "string") {
|
|
5161
|
+
const normalized = raw.trim().toLowerCase();
|
|
5162
|
+
if (normalized === "true" || normalized === "1") return true;
|
|
5163
|
+
if (normalized === "false" || normalized === "0") return false;
|
|
5164
|
+
}
|
|
5165
|
+
throw new Error(`Grix action ${key} must be a boolean.`);
|
|
5166
|
+
}
|
|
5167
|
+
function readRequiredBool(params, key) {
|
|
5168
|
+
const value = readOptionalBool(params, key);
|
|
5169
|
+
if (value == null) {
|
|
5170
|
+
throw new Error(`Grix action requires ${key}.`);
|
|
5171
|
+
}
|
|
5172
|
+
return value;
|
|
5173
|
+
}
|
|
5174
|
+
function normalizeStringList(raw, key, required) {
|
|
5175
|
+
if (!Array.isArray(raw)) {
|
|
5176
|
+
if (required) {
|
|
5177
|
+
throw new Error(`Grix action requires ${key}.`);
|
|
5178
|
+
}
|
|
5179
|
+
return [];
|
|
5180
|
+
}
|
|
5181
|
+
if (required && raw.length === 0) {
|
|
5182
|
+
throw new Error(`Grix action requires non-empty ${key}.`);
|
|
5183
|
+
}
|
|
5184
|
+
const normalized = [];
|
|
5185
|
+
for (const value of raw) {
|
|
5186
|
+
if (typeof value !== "string") {
|
|
5187
|
+
throw new Error(`Grix action ${key} must contain strings.`);
|
|
5188
|
+
}
|
|
5189
|
+
const trimmed = value.trim();
|
|
5190
|
+
if (!trimmed) {
|
|
5191
|
+
throw new Error(`Grix action ${key} must not contain empty strings.`);
|
|
5192
|
+
}
|
|
5193
|
+
normalized.push(trimmed);
|
|
5194
|
+
}
|
|
5195
|
+
return normalized;
|
|
5196
|
+
}
|
|
5197
|
+
function normalizeSchemaContribution(raw, key) {
|
|
5198
|
+
if (!isPlainObject(raw)) {
|
|
5199
|
+
throw new Error(`Grix action ${key} must be an object.`);
|
|
5200
|
+
}
|
|
5201
|
+
if (!isPlainObject(raw.properties)) {
|
|
5202
|
+
throw new Error(`Grix action ${key}.properties must be an object.`);
|
|
5203
|
+
}
|
|
5204
|
+
const contribution = {
|
|
5205
|
+
properties: raw.properties
|
|
5206
|
+
};
|
|
5207
|
+
if (Object.hasOwn(raw, "visibility")) {
|
|
5208
|
+
const visibility = readStringParam2(raw, "visibility");
|
|
5209
|
+
if (visibility !== "current-channel" && visibility !== "all-configured") {
|
|
5210
|
+
throw new Error(
|
|
5211
|
+
`Grix action ${key}.visibility must be current-channel or all-configured.`
|
|
5212
|
+
);
|
|
5213
|
+
}
|
|
5214
|
+
contribution.visibility = visibility;
|
|
5215
|
+
}
|
|
5216
|
+
return contribution;
|
|
5217
|
+
}
|
|
5218
|
+
function readRequiredDescribeMessageTool(params) {
|
|
5219
|
+
const raw = readRawParam(params, "describeMessageTool");
|
|
5220
|
+
if (!isPlainObject(raw)) {
|
|
5221
|
+
throw new Error("Grix action requires describeMessageTool as object.");
|
|
5222
|
+
}
|
|
5223
|
+
const actions = normalizeStringList(raw.actions, "describeMessageTool.actions", true);
|
|
5224
|
+
const payload = { actions };
|
|
5225
|
+
if (Object.hasOwn(raw, "capabilities")) {
|
|
5226
|
+
payload.capabilities = normalizeStringList(
|
|
5227
|
+
raw.capabilities,
|
|
5228
|
+
"describeMessageTool.capabilities",
|
|
5229
|
+
false
|
|
5230
|
+
);
|
|
5231
|
+
}
|
|
5232
|
+
if (Object.hasOwn(raw, "schema")) {
|
|
5233
|
+
const schema = raw.schema;
|
|
5234
|
+
if (Array.isArray(schema)) {
|
|
5235
|
+
if (schema.length === 0) {
|
|
5236
|
+
throw new Error("Grix action describeMessageTool.schema must not be empty.");
|
|
5237
|
+
}
|
|
5238
|
+
payload.schema = schema.map(
|
|
5239
|
+
(item, idx) => normalizeSchemaContribution(item, `describeMessageTool.schema[${idx}]`)
|
|
5240
|
+
);
|
|
5241
|
+
} else {
|
|
5242
|
+
payload.schema = normalizeSchemaContribution(schema, "describeMessageTool.schema");
|
|
5243
|
+
}
|
|
5244
|
+
}
|
|
5245
|
+
return payload;
|
|
5246
|
+
}
|
|
5247
|
+
function ensureMemberTypes(types) {
|
|
5248
|
+
for (const memberType of types) {
|
|
5249
|
+
if (memberType !== 1 && memberType !== 2) {
|
|
5250
|
+
throw new Error("Grix action member_types only supports 1 (human) or 2 (agent).");
|
|
5251
|
+
}
|
|
5252
|
+
}
|
|
5253
|
+
}
|
|
5254
|
+
function ensureMemberType(memberType) {
|
|
5255
|
+
if (memberType !== 1) {
|
|
5256
|
+
throw new Error("Grix action member_type only supports 1 for role update.");
|
|
5257
|
+
}
|
|
5258
|
+
}
|
|
5259
|
+
function ensureSpeakingMemberType(memberType) {
|
|
5260
|
+
if (memberType !== 1 && memberType !== 2) {
|
|
5261
|
+
throw new Error("Grix action member_type only supports 1 (human) or 2 (agent).");
|
|
5262
|
+
}
|
|
5263
|
+
}
|
|
5264
|
+
function buildGroupCreateRequest(params) {
|
|
5265
|
+
const name = readRequiredStringParam(params, "name");
|
|
5266
|
+
const memberIDs = readNumericIDArray(params, "memberIds", false);
|
|
5267
|
+
const memberTypes = readIntArray(params, "memberTypes");
|
|
5268
|
+
if (memberTypes.length > 0) {
|
|
5269
|
+
ensureMemberTypes(memberTypes);
|
|
5270
|
+
if (memberIDs.length == 0 || memberTypes.length !== memberIDs.length) {
|
|
5271
|
+
throw new Error("Grix action memberTypes length must match memberIds.");
|
|
5272
|
+
}
|
|
5273
|
+
}
|
|
5274
|
+
const body = { name };
|
|
5275
|
+
if (memberIDs.length > 0) {
|
|
5276
|
+
body.member_ids = memberIDs;
|
|
5277
|
+
}
|
|
5278
|
+
if (memberTypes.length > 0) {
|
|
5279
|
+
body.member_types = memberTypes;
|
|
5280
|
+
}
|
|
5281
|
+
return {
|
|
5282
|
+
actionName: "group_create",
|
|
5283
|
+
method: "POST",
|
|
5284
|
+
path: "/sessions/create_group",
|
|
5285
|
+
body
|
|
5286
|
+
};
|
|
5287
|
+
}
|
|
5288
|
+
function buildGroupMemberAddRequest(params) {
|
|
5289
|
+
const sessionID = readRequiredStringParam(params, "sessionId");
|
|
5290
|
+
const memberIDs = readNumericIDArray(params, "memberIds", true);
|
|
5291
|
+
const memberTypes = readIntArray(params, "memberTypes");
|
|
5292
|
+
if (memberTypes.length > 0) {
|
|
5293
|
+
ensureMemberTypes(memberTypes);
|
|
5294
|
+
if (memberTypes.length !== memberIDs.length) {
|
|
5295
|
+
throw new Error("Grix action memberTypes length must match memberIds.");
|
|
5296
|
+
}
|
|
5297
|
+
}
|
|
5298
|
+
const body = {
|
|
5299
|
+
session_id: sessionID,
|
|
5300
|
+
member_ids: memberIDs
|
|
5301
|
+
};
|
|
5302
|
+
if (memberTypes.length > 0) {
|
|
5303
|
+
body.member_types = memberTypes;
|
|
5304
|
+
}
|
|
5305
|
+
return {
|
|
5306
|
+
actionName: "group_member_add",
|
|
5307
|
+
method: "POST",
|
|
5308
|
+
path: "/sessions/members/add",
|
|
5309
|
+
body
|
|
5310
|
+
};
|
|
5311
|
+
}
|
|
5312
|
+
function buildGroupMemberRemoveRequest(params) {
|
|
5313
|
+
const sessionID = readRequiredStringParam(params, "sessionId");
|
|
5314
|
+
const memberIDs = readNumericIDArray(params, "memberIds", true);
|
|
5315
|
+
const memberTypes = readIntArray(params, "memberTypes");
|
|
5316
|
+
if (memberTypes.length > 0) {
|
|
5317
|
+
ensureMemberTypes(memberTypes);
|
|
5318
|
+
if (memberTypes.length !== memberIDs.length) {
|
|
5319
|
+
throw new Error("Grix action memberTypes length must match memberIds.");
|
|
5320
|
+
}
|
|
5321
|
+
}
|
|
5322
|
+
const body = {
|
|
5323
|
+
session_id: sessionID,
|
|
5324
|
+
member_ids: memberIDs
|
|
5325
|
+
};
|
|
5326
|
+
if (memberTypes.length > 0) {
|
|
5327
|
+
body.member_types = memberTypes;
|
|
5328
|
+
}
|
|
5329
|
+
return {
|
|
5330
|
+
actionName: "group_member_remove",
|
|
5331
|
+
method: "POST",
|
|
5332
|
+
path: "/sessions/members/remove",
|
|
5333
|
+
body
|
|
5334
|
+
};
|
|
5335
|
+
}
|
|
5336
|
+
function buildGroupMemberRoleUpdateRequest(params) {
|
|
5337
|
+
const sessionID = readRequiredStringParam(params, "sessionId");
|
|
5338
|
+
const memberID = readRequiredStringParam(params, "memberId");
|
|
5339
|
+
if (!/^\d+$/.test(memberID)) {
|
|
5340
|
+
throw new Error("Grix action memberId must be numeric.");
|
|
5341
|
+
}
|
|
5342
|
+
const role = readRequiredInt(params, "role");
|
|
5343
|
+
if (role !== 1 && role !== 2) {
|
|
5344
|
+
throw new Error("Grix action role only supports 1 or 2.");
|
|
5345
|
+
}
|
|
5346
|
+
const memberType = readOptionalInt(params, "memberType") ?? 1;
|
|
5347
|
+
ensureMemberType(memberType);
|
|
5348
|
+
return {
|
|
5349
|
+
actionName: "group_member_role_update",
|
|
5350
|
+
method: "POST",
|
|
5351
|
+
path: "/sessions/members/role",
|
|
5352
|
+
body: {
|
|
5353
|
+
session_id: sessionID,
|
|
5354
|
+
member_id: memberID,
|
|
5355
|
+
member_type: memberType,
|
|
5356
|
+
role
|
|
5357
|
+
}
|
|
5358
|
+
};
|
|
5359
|
+
}
|
|
5360
|
+
function buildGroupDissolveRequest(params) {
|
|
5361
|
+
const sessionID = readRequiredStringParam(params, "sessionId");
|
|
5362
|
+
return {
|
|
5363
|
+
actionName: "group_dissolve",
|
|
5364
|
+
method: "POST",
|
|
5365
|
+
path: "/sessions/dissolve",
|
|
5366
|
+
body: {
|
|
5367
|
+
session_id: sessionID
|
|
5368
|
+
}
|
|
5369
|
+
};
|
|
5370
|
+
}
|
|
5371
|
+
function buildGroupAllMembersMutedUpdateRequest(params) {
|
|
5372
|
+
const sessionID = readRequiredStringParam(params, "sessionId");
|
|
5373
|
+
const allMembersMuted = readRequiredBool(params, "allMembersMuted");
|
|
5374
|
+
return {
|
|
5375
|
+
actionName: "group_all_members_muted_update",
|
|
5376
|
+
method: "POST",
|
|
5377
|
+
path: "/sessions/speaking/all_muted",
|
|
5378
|
+
body: {
|
|
5379
|
+
session_id: sessionID,
|
|
5380
|
+
all_members_muted: allMembersMuted
|
|
5381
|
+
}
|
|
5382
|
+
};
|
|
5383
|
+
}
|
|
5384
|
+
function buildGroupMemberSpeakingUpdateRequest(params) {
|
|
5385
|
+
const sessionID = readRequiredStringParam(params, "sessionId");
|
|
5386
|
+
const memberID = readRequiredStringParam(params, "memberId");
|
|
5387
|
+
if (!/^\d+$/.test(memberID)) {
|
|
5388
|
+
throw new Error("Grix action memberId must be numeric.");
|
|
5389
|
+
}
|
|
5390
|
+
const memberType = readOptionalInt(params, "memberType") ?? 1;
|
|
5391
|
+
ensureSpeakingMemberType(memberType);
|
|
5392
|
+
const isSpeakMuted = readOptionalBool(params, "isSpeakMuted");
|
|
5393
|
+
const canSpeakWhenAllMuted = readOptionalBool(params, "canSpeakWhenAllMuted");
|
|
5394
|
+
if (isSpeakMuted == null && canSpeakWhenAllMuted == null) {
|
|
5395
|
+
throw new Error(
|
|
5396
|
+
"Grix action update_member_speaking requires isSpeakMuted or canSpeakWhenAllMuted."
|
|
5397
|
+
);
|
|
5398
|
+
}
|
|
5399
|
+
const body = {
|
|
5400
|
+
session_id: sessionID,
|
|
5401
|
+
member_id: memberID,
|
|
5402
|
+
member_type: memberType
|
|
5403
|
+
};
|
|
5404
|
+
if (isSpeakMuted != null) {
|
|
5405
|
+
body.is_speak_muted = isSpeakMuted;
|
|
5406
|
+
}
|
|
5407
|
+
if (canSpeakWhenAllMuted != null) {
|
|
5408
|
+
body.can_speak_when_all_muted = canSpeakWhenAllMuted;
|
|
5409
|
+
}
|
|
5410
|
+
return {
|
|
5411
|
+
actionName: "group_member_speaking_update",
|
|
5412
|
+
method: "POST",
|
|
5413
|
+
path: "/sessions/members/speaking",
|
|
5414
|
+
body
|
|
5415
|
+
};
|
|
5416
|
+
}
|
|
5417
|
+
function buildGroupDetailReadRequest(params) {
|
|
5418
|
+
const sessionID = readRequiredStringParam(params, "sessionId");
|
|
5419
|
+
return {
|
|
5420
|
+
actionName: "group_detail_read",
|
|
5421
|
+
method: "GET",
|
|
5422
|
+
path: "/sessions/group/detail",
|
|
5423
|
+
query: {
|
|
5424
|
+
session_id: sessionID
|
|
5425
|
+
}
|
|
5426
|
+
};
|
|
5427
|
+
}
|
|
5428
|
+
function buildContactSearchRequest(params) {
|
|
5429
|
+
const id = readRequiredStringParam(params, "id");
|
|
5430
|
+
const limit = readOptionalInt(params, "limit");
|
|
5431
|
+
const offset = readOptionalInt(params, "offset");
|
|
5432
|
+
const query = {
|
|
5433
|
+
id
|
|
5434
|
+
};
|
|
5435
|
+
if (limit != null) {
|
|
5436
|
+
query.limit = String(limit);
|
|
5437
|
+
}
|
|
5438
|
+
if (offset != null) {
|
|
5439
|
+
query.offset = String(offset);
|
|
5440
|
+
}
|
|
5441
|
+
return {
|
|
5442
|
+
actionName: "contact_search",
|
|
5443
|
+
method: "GET",
|
|
5444
|
+
path: "/contacts/search",
|
|
5445
|
+
query
|
|
5446
|
+
};
|
|
5447
|
+
}
|
|
5448
|
+
function buildSessionSearchRequest(params) {
|
|
5449
|
+
const id = readRequiredStringParam(params, "id");
|
|
5450
|
+
const limit = readOptionalInt(params, "limit");
|
|
5451
|
+
const offset = readOptionalInt(params, "offset");
|
|
5452
|
+
const query = {
|
|
5453
|
+
id
|
|
5454
|
+
};
|
|
5455
|
+
if (limit != null) {
|
|
5456
|
+
query.limit = String(limit);
|
|
5457
|
+
}
|
|
5458
|
+
if (offset != null) {
|
|
5459
|
+
query.offset = String(offset);
|
|
5460
|
+
}
|
|
5461
|
+
return {
|
|
5462
|
+
actionName: "session_search",
|
|
5463
|
+
method: "GET",
|
|
5464
|
+
path: "/sessions/search",
|
|
5465
|
+
query
|
|
5466
|
+
};
|
|
5467
|
+
}
|
|
5468
|
+
function buildMessageHistoryRequest(params) {
|
|
5469
|
+
const sessionID = readRequiredStringParam(params, "sessionId");
|
|
5470
|
+
const beforeID = readStringParam2(params, "beforeId");
|
|
5471
|
+
const limit = readOptionalInt(params, "limit");
|
|
5472
|
+
const query = {
|
|
5473
|
+
session_id: sessionID
|
|
5474
|
+
};
|
|
5475
|
+
if (beforeID) {
|
|
5476
|
+
query.before_id = beforeID;
|
|
5477
|
+
}
|
|
5478
|
+
if (limit != null) {
|
|
5479
|
+
query.limit = String(limit);
|
|
5480
|
+
}
|
|
5481
|
+
return {
|
|
5482
|
+
actionName: "message_history",
|
|
5483
|
+
method: "GET",
|
|
5484
|
+
path: "/messages/history",
|
|
5485
|
+
query
|
|
5486
|
+
};
|
|
5487
|
+
}
|
|
5488
|
+
function buildAgentAPICreateRequest(params) {
|
|
5489
|
+
const agentName = readRequiredStringParam(params, "agentName");
|
|
5490
|
+
if (!AGENT_NAME_RE.test(agentName)) {
|
|
5491
|
+
throw new Error("Grix action agentName must match ^[a-z][a-z0-9-]{2,31}$.");
|
|
5492
|
+
}
|
|
5493
|
+
const describeMessageTool = readRequiredDescribeMessageTool(params);
|
|
5494
|
+
const avatarURL = readStringParam2(params, "avatarUrl");
|
|
5495
|
+
const body = {
|
|
5496
|
+
agent_name: agentName,
|
|
5497
|
+
describe_message_tool: describeMessageTool
|
|
5498
|
+
};
|
|
5499
|
+
if (avatarURL) {
|
|
5500
|
+
body.avatar_url = avatarURL;
|
|
5501
|
+
}
|
|
5502
|
+
return {
|
|
5503
|
+
actionName: "agent_api_create",
|
|
5504
|
+
method: "POST",
|
|
5505
|
+
path: "/agents/create",
|
|
5506
|
+
body
|
|
5507
|
+
};
|
|
5508
|
+
}
|
|
5509
|
+
function buildAgentHTTPRequest(action, params) {
|
|
5510
|
+
switch (action) {
|
|
5511
|
+
case "contact_search":
|
|
5512
|
+
return buildContactSearchRequest(params);
|
|
5513
|
+
case "session_search":
|
|
5514
|
+
return buildSessionSearchRequest(params);
|
|
5515
|
+
case "message_history":
|
|
5516
|
+
return buildMessageHistoryRequest(params);
|
|
5517
|
+
case "group_create":
|
|
5518
|
+
return buildGroupCreateRequest(params);
|
|
5519
|
+
case "group_member_add":
|
|
5520
|
+
return buildGroupMemberAddRequest(params);
|
|
5521
|
+
case "group_member_remove":
|
|
5522
|
+
return buildGroupMemberRemoveRequest(params);
|
|
5523
|
+
case "group_member_role_update":
|
|
5524
|
+
return buildGroupMemberRoleUpdateRequest(params);
|
|
5525
|
+
case "group_all_members_muted_update":
|
|
5526
|
+
return buildGroupAllMembersMutedUpdateRequest(params);
|
|
5527
|
+
case "group_member_speaking_update":
|
|
5528
|
+
return buildGroupMemberSpeakingUpdateRequest(params);
|
|
5529
|
+
case "group_dissolve":
|
|
5530
|
+
return buildGroupDissolveRequest(params);
|
|
5531
|
+
case "group_detail_read":
|
|
5532
|
+
return buildGroupDetailReadRequest(params);
|
|
5533
|
+
case "agent_api_create":
|
|
5534
|
+
return buildAgentAPICreateRequest(params);
|
|
5535
|
+
default:
|
|
5536
|
+
throw new Error(`Grix action ${action} is not supported.`);
|
|
5537
|
+
}
|
|
5538
|
+
}
|
|
5539
|
+
|
|
5540
|
+
// src/admin/agent-api-http.ts
|
|
5541
|
+
var DEFAULT_HTTP_TIMEOUT_MS = 15e3;
|
|
5542
|
+
function trimTrailingSlash(value) {
|
|
5543
|
+
return value.replace(/\/+$/, "");
|
|
5544
|
+
}
|
|
5545
|
+
function resolveExplicitAgentAPIBase() {
|
|
5546
|
+
const base = String(
|
|
5547
|
+
process.env.GRIX_AGENT_API_BASE ?? process.env.AIBOT_AGENT_API_BASE ?? ""
|
|
5548
|
+
).trim();
|
|
5549
|
+
if (!base) {
|
|
5550
|
+
return "";
|
|
5551
|
+
}
|
|
5552
|
+
return trimTrailingSlash(base);
|
|
5553
|
+
}
|
|
5554
|
+
function deriveAgentAPIBaseFromWsUrl(wsUrl) {
|
|
5555
|
+
const normalizedWsUrl = String(wsUrl ?? "").trim();
|
|
5556
|
+
if (!normalizedWsUrl) {
|
|
5557
|
+
throw new Error("Grix account wsUrl is missing");
|
|
5558
|
+
}
|
|
5559
|
+
let parsed;
|
|
5560
|
+
try {
|
|
5561
|
+
parsed = new URL(normalizedWsUrl);
|
|
5562
|
+
} catch {
|
|
5563
|
+
throw new Error(`Grix wsUrl is invalid: ${normalizedWsUrl}`);
|
|
5564
|
+
}
|
|
5565
|
+
const protocol = parsed.protocol === "wss:" ? "https:" : parsed.protocol === "ws:" ? "http:" : "";
|
|
5566
|
+
if (!protocol) {
|
|
5567
|
+
throw new Error(`Grix wsUrl must start with ws:// or wss://: ${normalizedWsUrl}`);
|
|
5568
|
+
}
|
|
5569
|
+
const marker = "/v1/agent-api/ws";
|
|
5570
|
+
const markerIndex = parsed.pathname.indexOf(marker);
|
|
5571
|
+
const basePath = markerIndex >= 0 ? parsed.pathname.slice(0, markerIndex) : parsed.pathname;
|
|
5572
|
+
return trimTrailingSlash(`${protocol}//${parsed.host}${basePath}`) + "/v1/agent-api";
|
|
5573
|
+
}
|
|
5574
|
+
function deriveLocalAgentAPIBaseFromWsUrl(wsUrl) {
|
|
5575
|
+
const normalizedWsUrl = String(wsUrl ?? "").trim();
|
|
5576
|
+
if (!normalizedWsUrl) {
|
|
5577
|
+
return "";
|
|
5578
|
+
}
|
|
5579
|
+
let parsed;
|
|
5580
|
+
try {
|
|
5581
|
+
parsed = new URL(normalizedWsUrl);
|
|
5582
|
+
} catch {
|
|
5583
|
+
return "";
|
|
5584
|
+
}
|
|
5585
|
+
const host = String(parsed.hostname ?? "").trim().toLowerCase();
|
|
5586
|
+
const localHosts = /* @__PURE__ */ new Set(["127.0.0.1", "localhost", "::1"]);
|
|
5587
|
+
if (!localHosts.has(host)) {
|
|
5588
|
+
return "";
|
|
5589
|
+
}
|
|
5590
|
+
const wsPort = Number(parsed.port || (parsed.protocol === "wss:" ? 443 : 80));
|
|
5591
|
+
if (!Number.isFinite(wsPort) || wsPort <= 0) {
|
|
5592
|
+
return "";
|
|
5593
|
+
}
|
|
5594
|
+
const apiPort = wsPort % 10 === 9 ? wsPort - 9 : 27180;
|
|
5595
|
+
const protocol = parsed.protocol === "wss:" ? "https:" : "http:";
|
|
5596
|
+
return trimTrailingSlash(`${protocol}//${parsed.hostname}:${apiPort}`) + "/v1/agent-api";
|
|
5597
|
+
}
|
|
5598
|
+
function resolveAgentAPIBase(account) {
|
|
5599
|
+
const explicit = resolveExplicitAgentAPIBase();
|
|
5600
|
+
if (explicit) {
|
|
5601
|
+
return explicit;
|
|
5602
|
+
}
|
|
5603
|
+
const local = deriveLocalAgentAPIBaseFromWsUrl(account.wsUrl);
|
|
5604
|
+
if (local) {
|
|
5605
|
+
return local;
|
|
5606
|
+
}
|
|
5607
|
+
return deriveAgentAPIBaseFromWsUrl(account.wsUrl);
|
|
5608
|
+
}
|
|
5609
|
+
function buildRequestURL(base, path, query) {
|
|
5610
|
+
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
|
|
5611
|
+
const url = new URL(`${trimTrailingSlash(base)}${normalizedPath}`);
|
|
5612
|
+
if (query) {
|
|
5613
|
+
for (const [key, value] of Object.entries(query)) {
|
|
5614
|
+
const normalizedValue = String(value ?? "").trim();
|
|
5615
|
+
if (!normalizedValue) {
|
|
5616
|
+
continue;
|
|
5617
|
+
}
|
|
5618
|
+
url.searchParams.set(key, normalizedValue);
|
|
5619
|
+
}
|
|
5620
|
+
}
|
|
5621
|
+
return url.toString();
|
|
5622
|
+
}
|
|
5623
|
+
function normalizeStatusCode(raw) {
|
|
5624
|
+
const n = Number(raw);
|
|
5625
|
+
if (Number.isFinite(n)) {
|
|
5626
|
+
return Math.floor(n);
|
|
5627
|
+
}
|
|
5628
|
+
return 0;
|
|
5629
|
+
}
|
|
5630
|
+
function normalizeBizCode(raw) {
|
|
5631
|
+
const n = Number(raw);
|
|
5632
|
+
if (Number.isFinite(n)) {
|
|
5633
|
+
return Math.floor(n);
|
|
5634
|
+
}
|
|
5635
|
+
return -1;
|
|
5636
|
+
}
|
|
5637
|
+
function normalizeMessage(raw) {
|
|
5638
|
+
const message = String(raw ?? "").trim();
|
|
5639
|
+
if (!message) {
|
|
5640
|
+
return "unknown error";
|
|
5641
|
+
}
|
|
5642
|
+
return message;
|
|
5643
|
+
}
|
|
5644
|
+
function extractNetworkErrorMessage(error) {
|
|
5645
|
+
if (error instanceof Error) {
|
|
5646
|
+
return error.message || String(error);
|
|
5647
|
+
}
|
|
5648
|
+
return String(error);
|
|
5649
|
+
}
|
|
5650
|
+
async function callAgentAPI(params) {
|
|
5651
|
+
const base = resolveAgentAPIBase(params.account);
|
|
5652
|
+
const url = buildRequestURL(base, params.path, params.query);
|
|
5653
|
+
const timeoutMs = Number.isFinite(params.timeoutMs) ? Math.max(1e3, Math.floor(params.timeoutMs)) : DEFAULT_HTTP_TIMEOUT_MS;
|
|
5654
|
+
const controller = new AbortController();
|
|
5655
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
5656
|
+
let resp;
|
|
5657
|
+
try {
|
|
5658
|
+
resp = await fetch(url, {
|
|
5659
|
+
method: params.method,
|
|
5660
|
+
headers: {
|
|
5661
|
+
Authorization: `Bearer ${params.account.apiKey}`,
|
|
5662
|
+
...params.method === "POST" ? { "Content-Type": "application/json" } : {}
|
|
5663
|
+
},
|
|
5664
|
+
body: params.method === "POST" ? JSON.stringify(params.body ?? {}) : void 0,
|
|
5665
|
+
signal: controller.signal
|
|
5666
|
+
});
|
|
5667
|
+
} catch (error) {
|
|
5668
|
+
clearTimeout(timer);
|
|
5669
|
+
throw new Error(
|
|
5670
|
+
`Grix ${params.actionName} network error: ${extractNetworkErrorMessage(error)}`
|
|
5671
|
+
);
|
|
5672
|
+
}
|
|
5673
|
+
clearTimeout(timer);
|
|
5674
|
+
const status = normalizeStatusCode(resp.status);
|
|
5675
|
+
const rawBody = await resp.text();
|
|
5676
|
+
let envelope;
|
|
5677
|
+
try {
|
|
5678
|
+
envelope = JSON.parse(rawBody);
|
|
5679
|
+
} catch {
|
|
5680
|
+
throw new Error(
|
|
5681
|
+
`Grix ${params.actionName} invalid response: status=${status} body=${rawBody.slice(0, 256)}`
|
|
5682
|
+
);
|
|
5683
|
+
}
|
|
5684
|
+
const bizCode = normalizeBizCode(envelope.code);
|
|
5685
|
+
if (!resp.ok || bizCode !== 0) {
|
|
5686
|
+
const message = normalizeMessage(envelope.msg);
|
|
5687
|
+
throw new Error(
|
|
5688
|
+
`Grix ${params.actionName} failed: status=${status} code=${bizCode} msg=${message}`
|
|
5689
|
+
);
|
|
5690
|
+
}
|
|
5691
|
+
return envelope.data;
|
|
5692
|
+
}
|
|
5693
|
+
|
|
5694
|
+
// src/admin/accounts.ts
|
|
5695
|
+
var DEFAULT_ACCOUNT_ID2 = "default";
|
|
5696
|
+
function normalizeAccountId2(value) {
|
|
5697
|
+
const normalized = String(value ?? "").trim();
|
|
5698
|
+
return normalized || DEFAULT_ACCOUNT_ID2;
|
|
5699
|
+
}
|
|
5700
|
+
function normalizeOptionalAccountId2(value) {
|
|
5701
|
+
const normalized = String(value ?? "").trim();
|
|
5702
|
+
return normalized || void 0;
|
|
5703
|
+
}
|
|
5704
|
+
function rawGrixConfig(cfg) {
|
|
5705
|
+
return cfg.channels?.grix ?? {};
|
|
5706
|
+
}
|
|
5707
|
+
function listConfiguredAccountIds2(cfg) {
|
|
5708
|
+
const accounts = rawGrixConfig(cfg).accounts;
|
|
5709
|
+
if (!accounts || typeof accounts !== "object") {
|
|
5710
|
+
return [];
|
|
5711
|
+
}
|
|
5712
|
+
return Object.keys(accounts).filter(Boolean);
|
|
5713
|
+
}
|
|
5714
|
+
function normalizeNonEmpty2(value) {
|
|
5715
|
+
return String(value ?? "").trim();
|
|
5716
|
+
}
|
|
5717
|
+
function appendAgentIdToWsUrl2(rawWsUrl, agentId) {
|
|
5718
|
+
if (!rawWsUrl) {
|
|
5719
|
+
return "";
|
|
5720
|
+
}
|
|
5721
|
+
const direct = rawWsUrl.replaceAll("{agent_id}", encodeURIComponent(agentId));
|
|
5722
|
+
if (!agentId) {
|
|
5723
|
+
return direct;
|
|
5724
|
+
}
|
|
5725
|
+
try {
|
|
5726
|
+
const parsed = new URL(direct);
|
|
5727
|
+
if (!parsed.searchParams.get("agent_id")) {
|
|
5728
|
+
parsed.searchParams.set("agent_id", agentId);
|
|
5729
|
+
}
|
|
5730
|
+
return parsed.toString();
|
|
5731
|
+
} catch {
|
|
5732
|
+
if (direct.includes("agent_id=")) {
|
|
5733
|
+
return direct;
|
|
5734
|
+
}
|
|
5735
|
+
return direct.includes("?") ? `${direct}&agent_id=${encodeURIComponent(agentId)}` : `${direct}?agent_id=${encodeURIComponent(agentId)}`;
|
|
5736
|
+
}
|
|
5737
|
+
}
|
|
5738
|
+
function resolveWsUrl2(merged, agentId) {
|
|
5739
|
+
const envWs = normalizeNonEmpty2(process.env.GRIX_WS_URL);
|
|
5740
|
+
const cfgWs = normalizeNonEmpty2(merged.wsUrl);
|
|
5741
|
+
const ws = cfgWs || envWs;
|
|
5742
|
+
if (ws) {
|
|
5743
|
+
return appendAgentIdToWsUrl2(ws, agentId);
|
|
5744
|
+
}
|
|
5745
|
+
if (!agentId) {
|
|
5746
|
+
return "";
|
|
5747
|
+
}
|
|
5748
|
+
return `ws://127.0.0.1:27189/v1/agent-api/ws?agent_id=${encodeURIComponent(agentId)}`;
|
|
5749
|
+
}
|
|
5750
|
+
function resolveMergedAccountConfig(cfg, accountId) {
|
|
5751
|
+
const grixCfg = rawGrixConfig(cfg);
|
|
5752
|
+
const { accounts: _ignoredAccounts, defaultAccount: _ignoredDefault, ...base } = grixCfg;
|
|
5753
|
+
const account = grixCfg.accounts?.[accountId] ?? {};
|
|
5754
|
+
return {
|
|
5755
|
+
...base,
|
|
5756
|
+
...account
|
|
5757
|
+
};
|
|
5758
|
+
}
|
|
5759
|
+
function listGrixAccountIds(cfg) {
|
|
5760
|
+
const ids = listConfiguredAccountIds2(cfg);
|
|
5761
|
+
if (ids.length === 0) {
|
|
5762
|
+
return [DEFAULT_ACCOUNT_ID2];
|
|
5763
|
+
}
|
|
5764
|
+
return ids.toSorted((a, b) => a.localeCompare(b));
|
|
5765
|
+
}
|
|
5766
|
+
function resolveDefaultGrixAccountId(cfg) {
|
|
5767
|
+
const grixCfg = rawGrixConfig(cfg);
|
|
5768
|
+
const preferred = normalizeOptionalAccountId2(grixCfg.defaultAccount);
|
|
5769
|
+
if (preferred && listGrixAccountIds(cfg).some((accountId) => normalizeAccountId2(accountId) === preferred)) {
|
|
5770
|
+
return preferred;
|
|
5771
|
+
}
|
|
5772
|
+
const ids = listGrixAccountIds(cfg);
|
|
5773
|
+
if (ids.includes(DEFAULT_ACCOUNT_ID2)) {
|
|
5774
|
+
return DEFAULT_ACCOUNT_ID2;
|
|
5775
|
+
}
|
|
5776
|
+
return ids[0] ?? DEFAULT_ACCOUNT_ID2;
|
|
5777
|
+
}
|
|
5778
|
+
function resolveGrixAccount(params) {
|
|
5779
|
+
const accountId = params.accountId == null || String(params.accountId).trim() === "" ? resolveDefaultGrixAccountId(params.cfg) : normalizeAccountId2(params.accountId);
|
|
5780
|
+
const merged = resolveMergedAccountConfig(params.cfg, accountId);
|
|
5781
|
+
const baseEnabled = rawGrixConfig(params.cfg).enabled !== false;
|
|
5782
|
+
const accountEnabled = merged.enabled !== false;
|
|
5783
|
+
const enabled = baseEnabled && accountEnabled;
|
|
5784
|
+
const agentId = normalizeNonEmpty2(merged.agentId || process.env.GRIX_AGENT_ID);
|
|
5785
|
+
const apiKey = normalizeNonEmpty2(merged.apiKey || process.env.GRIX_API_KEY);
|
|
5786
|
+
const wsUrl = resolveWsUrl2(merged, agentId);
|
|
5787
|
+
const configured = Boolean(wsUrl && agentId && apiKey);
|
|
5788
|
+
return {
|
|
5789
|
+
accountId,
|
|
5790
|
+
name: normalizeNonEmpty2(merged.name) || void 0,
|
|
5791
|
+
enabled,
|
|
5792
|
+
configured,
|
|
5793
|
+
wsUrl,
|
|
5794
|
+
agentId,
|
|
5795
|
+
apiKey,
|
|
5796
|
+
config: merged
|
|
5797
|
+
};
|
|
5798
|
+
}
|
|
5799
|
+
function summarizeGrixAccounts(cfg) {
|
|
5800
|
+
return listGrixAccountIds(cfg).map((accountId) => {
|
|
5801
|
+
const account = resolveGrixAccount({ cfg, accountId });
|
|
5802
|
+
return {
|
|
5803
|
+
accountId: account.accountId,
|
|
5804
|
+
name: account.name ?? null,
|
|
5805
|
+
enabled: account.enabled,
|
|
5806
|
+
configured: account.configured,
|
|
5807
|
+
wsUrl: account.wsUrl || null,
|
|
5808
|
+
agentId: account.agentId || null
|
|
5809
|
+
};
|
|
5810
|
+
});
|
|
5811
|
+
}
|
|
5812
|
+
|
|
5813
|
+
// src/admin/agent-admin-service.ts
|
|
5814
|
+
function buildChannelBootstrapCommand(params) {
|
|
5815
|
+
return [
|
|
5816
|
+
"openclaw channels add",
|
|
5817
|
+
"--channel grix",
|
|
5818
|
+
`--name ${JSON.stringify(params.channelName)}`,
|
|
5819
|
+
`--http-url ${JSON.stringify(params.apiEndpoint)}`,
|
|
5820
|
+
`--user-id ${JSON.stringify(params.agentId)}`,
|
|
5821
|
+
`--token ${JSON.stringify(params.apiKeyPlaceholder)}`
|
|
5822
|
+
].join(" ");
|
|
5823
|
+
}
|
|
5824
|
+
function maskSecret(value) {
|
|
5825
|
+
const normalized = String(value ?? "").trim();
|
|
5826
|
+
if (!normalized) {
|
|
5827
|
+
return "";
|
|
5828
|
+
}
|
|
5829
|
+
if (normalized.length <= 8) {
|
|
5830
|
+
return "*".repeat(normalized.length);
|
|
5831
|
+
}
|
|
5832
|
+
return `${normalized.slice(0, 4)}...${normalized.slice(-4)}`;
|
|
5833
|
+
}
|
|
5834
|
+
function sanitizeCreatedAgentData(data) {
|
|
5835
|
+
const payload = { ...data };
|
|
5836
|
+
if ("api_key" in payload) {
|
|
5837
|
+
payload.api_key = "<redacted>";
|
|
5838
|
+
}
|
|
5839
|
+
if (!payload.api_key_hint && typeof data.api_key === "string") {
|
|
5840
|
+
payload.api_key_hint = maskSecret(String(data.api_key));
|
|
5841
|
+
}
|
|
5842
|
+
return payload;
|
|
5843
|
+
}
|
|
5844
|
+
async function createGrixApiAgent(params) {
|
|
5845
|
+
const account = resolveGrixAccount({
|
|
5846
|
+
cfg: params.cfg,
|
|
5847
|
+
accountId: params.toolParams.accountId
|
|
5848
|
+
});
|
|
5849
|
+
if (!account.enabled) {
|
|
5850
|
+
throw new Error(`Grix account "${account.accountId}" is disabled.`);
|
|
5851
|
+
}
|
|
5852
|
+
if (!account.configured) {
|
|
5853
|
+
throw new Error(`Grix account "${account.accountId}" is not configured.`);
|
|
5854
|
+
}
|
|
5855
|
+
const request = buildAgentHTTPRequest("agent_api_create", params.toolParams);
|
|
5856
|
+
const data = await callAgentAPI({
|
|
5857
|
+
account,
|
|
5858
|
+
actionName: request.actionName,
|
|
5859
|
+
method: request.method,
|
|
5860
|
+
path: request.path,
|
|
5861
|
+
query: request.query,
|
|
5862
|
+
body: request.body
|
|
5863
|
+
});
|
|
5864
|
+
const agentName = String(data.agent_name ?? params.toolParams.agentName ?? "").trim();
|
|
5865
|
+
const apiEndpoint = String(data.api_endpoint ?? "").trim();
|
|
5866
|
+
const agentId = String(data.id ?? "").trim();
|
|
5867
|
+
const apiKey = String(data.api_key ?? "").trim();
|
|
5868
|
+
const apiKeyHint = String(data.api_key_hint ?? "").trim() || maskSecret(apiKey);
|
|
5869
|
+
return {
|
|
5870
|
+
ok: true,
|
|
5871
|
+
accountId: account.accountId,
|
|
5872
|
+
action: "create_api_agent",
|
|
5873
|
+
createdAgent: {
|
|
5874
|
+
id: agentId,
|
|
5875
|
+
agent_name: agentName,
|
|
5876
|
+
provider_type: Number(data.provider_type ?? 0) || 0,
|
|
5877
|
+
api_endpoint: apiEndpoint,
|
|
5878
|
+
api_key: apiKey,
|
|
5879
|
+
api_key_hint: apiKeyHint
|
|
5880
|
+
},
|
|
5881
|
+
data: sanitizeCreatedAgentData(data),
|
|
5882
|
+
nextSteps: agentName && apiEndpoint && agentId && apiKey ? [
|
|
5883
|
+
"Install and enable the channel plugin if it is not installed yet: `openclaw plugins install @dhf-openclaw/grix && openclaw plugins enable grix`.",
|
|
5884
|
+
"Use the one-time `createdAgent.api_key` from this result as `<NEW_AGENT_API_KEY>` for the binding command, then stop sharing it in chat.",
|
|
5885
|
+
`Bind the new API agent to OpenClaw with: \`${buildChannelBootstrapCommand({
|
|
5886
|
+
channelName: `grix-${agentName}`,
|
|
5887
|
+
apiEndpoint,
|
|
5888
|
+
agentId,
|
|
5889
|
+
apiKeyPlaceholder: "<NEW_AGENT_API_KEY>"
|
|
5890
|
+
})}\``,
|
|
5891
|
+
"Restart the gateway after adding the channel: `openclaw gateway restart`."
|
|
5892
|
+
] : []
|
|
5893
|
+
};
|
|
5894
|
+
}
|
|
5895
|
+
function inspectGrixAdminConfig(cfg) {
|
|
5896
|
+
return {
|
|
5897
|
+
accounts: summarizeGrixAccounts(cfg),
|
|
5898
|
+
defaultAccountId: resolveGrixAccount({ cfg }).accountId
|
|
5899
|
+
};
|
|
5900
|
+
}
|
|
5901
|
+
|
|
5902
|
+
// src/admin/json-result.ts
|
|
5903
|
+
function jsonToolResult(payload) {
|
|
5904
|
+
return {
|
|
5905
|
+
content: [{ type: "text", text: JSON.stringify(payload, null, 2) }],
|
|
5906
|
+
details: payload
|
|
5907
|
+
};
|
|
5908
|
+
}
|
|
5909
|
+
|
|
5910
|
+
// src/admin/agent-admin-tool.ts
|
|
5911
|
+
var GrixAgentAdminToolSchema = {
|
|
5912
|
+
type: "object",
|
|
5913
|
+
additionalProperties: false,
|
|
5914
|
+
properties: {
|
|
5915
|
+
accountId: { type: "string", minLength: 1 },
|
|
5916
|
+
agentName: {
|
|
5917
|
+
type: "string",
|
|
5918
|
+
pattern: "^[a-z][a-z0-9-]{2,31}$",
|
|
5919
|
+
description: "Lowercase API agent name."
|
|
5920
|
+
},
|
|
5921
|
+
avatarUrl: { type: "string", minLength: 1 },
|
|
5922
|
+
describeMessageTool: {
|
|
5923
|
+
type: "object",
|
|
5924
|
+
additionalProperties: false,
|
|
5925
|
+
properties: {
|
|
5926
|
+
actions: {
|
|
5927
|
+
type: "array",
|
|
5928
|
+
minItems: 1,
|
|
5929
|
+
items: { type: "string", minLength: 1 }
|
|
5930
|
+
},
|
|
5931
|
+
capabilities: {
|
|
5932
|
+
type: "array",
|
|
5933
|
+
items: { type: "string", minLength: 1 }
|
|
5934
|
+
},
|
|
5935
|
+
schema: {
|
|
5936
|
+
oneOf: [
|
|
5937
|
+
{
|
|
5938
|
+
type: "object",
|
|
5939
|
+
additionalProperties: false,
|
|
5940
|
+
properties: {
|
|
5941
|
+
properties: { type: "object" },
|
|
5942
|
+
visibility: {
|
|
5943
|
+
type: "string",
|
|
5944
|
+
enum: ["current-channel", "all-configured"]
|
|
5945
|
+
}
|
|
5946
|
+
},
|
|
5947
|
+
required: ["properties"]
|
|
5948
|
+
},
|
|
5949
|
+
{
|
|
5950
|
+
type: "array",
|
|
5951
|
+
minItems: 1,
|
|
5952
|
+
items: {
|
|
5953
|
+
type: "object",
|
|
5954
|
+
additionalProperties: false,
|
|
5955
|
+
properties: {
|
|
5956
|
+
properties: { type: "object" },
|
|
5957
|
+
visibility: {
|
|
5958
|
+
type: "string",
|
|
5959
|
+
enum: ["current-channel", "all-configured"]
|
|
5960
|
+
}
|
|
5961
|
+
},
|
|
5962
|
+
required: ["properties"]
|
|
5963
|
+
}
|
|
5964
|
+
}
|
|
5965
|
+
]
|
|
5966
|
+
}
|
|
5967
|
+
},
|
|
5968
|
+
required: ["actions"]
|
|
5969
|
+
}
|
|
5970
|
+
},
|
|
5971
|
+
required: ["agentName", "describeMessageTool"]
|
|
5972
|
+
};
|
|
5973
|
+
function createGrixAgentAdminTool(api) {
|
|
5974
|
+
return {
|
|
5975
|
+
name: "grix_agent_admin",
|
|
5976
|
+
label: "Grix Agent Admin",
|
|
5977
|
+
description: "Create Grix API agents with typed parameters. This tool does not modify local OpenClaw channel config.",
|
|
5978
|
+
parameters: GrixAgentAdminToolSchema,
|
|
5979
|
+
async execute(_toolCallId, params) {
|
|
5980
|
+
try {
|
|
5981
|
+
return jsonToolResult(
|
|
5982
|
+
await createGrixApiAgent({
|
|
5983
|
+
cfg: api.config,
|
|
5984
|
+
toolParams: params
|
|
5985
|
+
})
|
|
5986
|
+
);
|
|
5987
|
+
} catch (err) {
|
|
5988
|
+
return jsonToolResult({
|
|
5989
|
+
error: err instanceof Error ? err.message : String(err)
|
|
5990
|
+
});
|
|
5991
|
+
}
|
|
5992
|
+
}
|
|
5993
|
+
};
|
|
5994
|
+
}
|
|
5995
|
+
|
|
5996
|
+
// src/admin/group-service.ts
|
|
5997
|
+
function mapGroupActionToRequestAction(action) {
|
|
5998
|
+
switch (action) {
|
|
5999
|
+
case "create":
|
|
6000
|
+
return "group_create";
|
|
6001
|
+
case "detail":
|
|
6002
|
+
return "group_detail_read";
|
|
6003
|
+
case "add_members":
|
|
6004
|
+
return "group_member_add";
|
|
6005
|
+
case "remove_members":
|
|
6006
|
+
return "group_member_remove";
|
|
6007
|
+
case "update_member_role":
|
|
6008
|
+
return "group_member_role_update";
|
|
6009
|
+
case "update_all_members_muted":
|
|
6010
|
+
return "group_all_members_muted_update";
|
|
6011
|
+
case "update_member_speaking":
|
|
6012
|
+
return "group_member_speaking_update";
|
|
6013
|
+
case "dissolve":
|
|
6014
|
+
return "group_dissolve";
|
|
6015
|
+
default:
|
|
6016
|
+
action;
|
|
6017
|
+
throw new Error(`Unsupported Grix group action: ${String(action)}`);
|
|
6018
|
+
}
|
|
6019
|
+
}
|
|
6020
|
+
async function runGrixGroupAction(params) {
|
|
6021
|
+
const account = resolveGrixAccount({
|
|
6022
|
+
cfg: params.cfg,
|
|
6023
|
+
accountId: params.toolParams.accountId
|
|
6024
|
+
});
|
|
6025
|
+
if (!account.enabled) {
|
|
6026
|
+
throw new Error(`Grix account "${account.accountId}" is disabled.`);
|
|
6027
|
+
}
|
|
6028
|
+
if (!account.configured) {
|
|
6029
|
+
throw new Error(`Grix account "${account.accountId}" is not configured.`);
|
|
6030
|
+
}
|
|
6031
|
+
const requestAction = mapGroupActionToRequestAction(params.toolParams.action);
|
|
6032
|
+
const request = buildAgentHTTPRequest(requestAction, params.toolParams);
|
|
6033
|
+
const data = await callAgentAPI({
|
|
6034
|
+
account,
|
|
6035
|
+
actionName: request.actionName,
|
|
6036
|
+
method: request.method,
|
|
6037
|
+
path: request.path,
|
|
6038
|
+
query: request.query,
|
|
6039
|
+
body: request.body
|
|
6040
|
+
});
|
|
6041
|
+
return {
|
|
6042
|
+
ok: true,
|
|
6043
|
+
accountId: account.accountId,
|
|
6044
|
+
action: params.toolParams.action,
|
|
6045
|
+
data
|
|
6046
|
+
};
|
|
6047
|
+
}
|
|
6048
|
+
|
|
6049
|
+
// src/admin/group-tool.ts
|
|
6050
|
+
var numericIdSchema = {
|
|
6051
|
+
type: "string",
|
|
6052
|
+
pattern: "^[0-9]+$"
|
|
6053
|
+
};
|
|
6054
|
+
var GrixGroupToolSchema = {
|
|
6055
|
+
oneOf: [
|
|
6056
|
+
{
|
|
6057
|
+
type: "object",
|
|
6058
|
+
additionalProperties: false,
|
|
6059
|
+
properties: {
|
|
6060
|
+
action: { const: "create" },
|
|
6061
|
+
accountId: { type: "string", minLength: 1 },
|
|
6062
|
+
name: { type: "string", minLength: 1 },
|
|
6063
|
+
memberIds: { type: "array", items: numericIdSchema },
|
|
6064
|
+
memberTypes: { type: "array", items: { type: "integer", enum: [1, 2] } }
|
|
6065
|
+
},
|
|
6066
|
+
required: ["action", "name"]
|
|
6067
|
+
},
|
|
6068
|
+
{
|
|
6069
|
+
type: "object",
|
|
6070
|
+
additionalProperties: false,
|
|
6071
|
+
properties: {
|
|
6072
|
+
action: { const: "detail" },
|
|
6073
|
+
accountId: { type: "string", minLength: 1 },
|
|
6074
|
+
sessionId: { type: "string", minLength: 1 }
|
|
6075
|
+
},
|
|
6076
|
+
required: ["action", "sessionId"]
|
|
6077
|
+
},
|
|
6078
|
+
{
|
|
6079
|
+
type: "object",
|
|
6080
|
+
additionalProperties: false,
|
|
6081
|
+
properties: {
|
|
6082
|
+
action: { const: "add_members" },
|
|
6083
|
+
accountId: { type: "string", minLength: 1 },
|
|
6084
|
+
sessionId: { type: "string", minLength: 1 },
|
|
6085
|
+
memberIds: { type: "array", items: numericIdSchema, minItems: 1 },
|
|
6086
|
+
memberTypes: { type: "array", items: { type: "integer", enum: [1, 2] } }
|
|
6087
|
+
},
|
|
6088
|
+
required: ["action", "sessionId", "memberIds"]
|
|
6089
|
+
},
|
|
6090
|
+
{
|
|
6091
|
+
type: "object",
|
|
6092
|
+
additionalProperties: false,
|
|
6093
|
+
properties: {
|
|
6094
|
+
action: { const: "remove_members" },
|
|
6095
|
+
accountId: { type: "string", minLength: 1 },
|
|
6096
|
+
sessionId: { type: "string", minLength: 1 },
|
|
6097
|
+
memberIds: { type: "array", items: numericIdSchema, minItems: 1 },
|
|
6098
|
+
memberTypes: { type: "array", items: { type: "integer", enum: [1, 2] } }
|
|
6099
|
+
},
|
|
6100
|
+
required: ["action", "sessionId", "memberIds"]
|
|
6101
|
+
},
|
|
6102
|
+
{
|
|
6103
|
+
type: "object",
|
|
6104
|
+
additionalProperties: false,
|
|
6105
|
+
properties: {
|
|
6106
|
+
action: { const: "update_member_role" },
|
|
6107
|
+
accountId: { type: "string", minLength: 1 },
|
|
6108
|
+
sessionId: { type: "string", minLength: 1 },
|
|
6109
|
+
memberId: numericIdSchema,
|
|
6110
|
+
memberType: { type: "integer", enum: [1] },
|
|
6111
|
+
role: { type: "integer", enum: [1, 2] }
|
|
6112
|
+
},
|
|
6113
|
+
required: ["action", "sessionId", "memberId", "role"]
|
|
6114
|
+
},
|
|
6115
|
+
{
|
|
6116
|
+
type: "object",
|
|
6117
|
+
additionalProperties: false,
|
|
6118
|
+
properties: {
|
|
6119
|
+
action: { const: "update_all_members_muted" },
|
|
6120
|
+
accountId: { type: "string", minLength: 1 },
|
|
6121
|
+
sessionId: { type: "string", minLength: 1 },
|
|
6122
|
+
allMembersMuted: { type: "boolean" }
|
|
6123
|
+
},
|
|
6124
|
+
required: ["action", "sessionId", "allMembersMuted"]
|
|
6125
|
+
},
|
|
6126
|
+
{
|
|
6127
|
+
type: "object",
|
|
6128
|
+
additionalProperties: false,
|
|
6129
|
+
properties: {
|
|
6130
|
+
action: { const: "update_member_speaking" },
|
|
6131
|
+
accountId: { type: "string", minLength: 1 },
|
|
6132
|
+
sessionId: { type: "string", minLength: 1 },
|
|
6133
|
+
memberId: numericIdSchema,
|
|
6134
|
+
memberType: { type: "integer", enum: [1, 2] },
|
|
6135
|
+
isSpeakMuted: { type: "boolean" },
|
|
6136
|
+
canSpeakWhenAllMuted: { type: "boolean" }
|
|
6137
|
+
},
|
|
6138
|
+
required: ["action", "sessionId", "memberId"],
|
|
6139
|
+
anyOf: [
|
|
6140
|
+
{ required: ["isSpeakMuted"] },
|
|
6141
|
+
{ required: ["canSpeakWhenAllMuted"] }
|
|
6142
|
+
]
|
|
6143
|
+
},
|
|
6144
|
+
{
|
|
6145
|
+
type: "object",
|
|
6146
|
+
additionalProperties: false,
|
|
6147
|
+
properties: {
|
|
6148
|
+
action: { const: "dissolve" },
|
|
6149
|
+
accountId: { type: "string", minLength: 1 },
|
|
6150
|
+
sessionId: { type: "string", minLength: 1 }
|
|
6151
|
+
},
|
|
6152
|
+
required: ["action", "sessionId"]
|
|
6153
|
+
}
|
|
6154
|
+
]
|
|
6155
|
+
};
|
|
6156
|
+
function createGrixGroupTool(api) {
|
|
6157
|
+
return {
|
|
6158
|
+
name: "grix_group",
|
|
6159
|
+
label: "Grix Group",
|
|
6160
|
+
description: "Manage Grix groups through typed admin operations. This tool only handles group lifecycle and membership changes.",
|
|
6161
|
+
parameters: GrixGroupToolSchema,
|
|
6162
|
+
async execute(_toolCallId, params) {
|
|
6163
|
+
try {
|
|
6164
|
+
return jsonToolResult(
|
|
6165
|
+
await runGrixGroupAction({
|
|
6166
|
+
cfg: api.config,
|
|
6167
|
+
toolParams: params
|
|
6168
|
+
})
|
|
6169
|
+
);
|
|
6170
|
+
} catch (err) {
|
|
6171
|
+
return jsonToolResult({
|
|
6172
|
+
error: err instanceof Error ? err.message : String(err)
|
|
6173
|
+
});
|
|
6174
|
+
}
|
|
6175
|
+
}
|
|
6176
|
+
};
|
|
6177
|
+
}
|
|
6178
|
+
|
|
6179
|
+
// src/admin/query-service.ts
|
|
6180
|
+
function mapQueryActionToRequestAction(action) {
|
|
6181
|
+
switch (action) {
|
|
6182
|
+
case "contact_search":
|
|
6183
|
+
return "contact_search";
|
|
6184
|
+
case "session_search":
|
|
6185
|
+
return "session_search";
|
|
6186
|
+
case "message_history":
|
|
6187
|
+
return "message_history";
|
|
6188
|
+
default:
|
|
6189
|
+
action;
|
|
6190
|
+
throw new Error(`Unsupported Grix query action: ${String(action)}`);
|
|
6191
|
+
}
|
|
6192
|
+
}
|
|
6193
|
+
async function runGrixQueryAction(params) {
|
|
6194
|
+
const account = resolveGrixAccount({
|
|
6195
|
+
cfg: params.cfg,
|
|
6196
|
+
accountId: params.toolParams.accountId
|
|
6197
|
+
});
|
|
6198
|
+
if (!account.enabled) {
|
|
6199
|
+
throw new Error(`Grix account "${account.accountId}" is disabled.`);
|
|
6200
|
+
}
|
|
6201
|
+
if (!account.configured) {
|
|
6202
|
+
throw new Error(`Grix account "${account.accountId}" is not configured.`);
|
|
6203
|
+
}
|
|
6204
|
+
const requestAction = mapQueryActionToRequestAction(params.toolParams.action);
|
|
6205
|
+
const request = buildAgentHTTPRequest(requestAction, params.toolParams);
|
|
6206
|
+
const data = await callAgentAPI({
|
|
6207
|
+
account,
|
|
6208
|
+
actionName: request.actionName,
|
|
6209
|
+
method: request.method,
|
|
6210
|
+
path: request.path,
|
|
6211
|
+
query: request.query,
|
|
6212
|
+
body: request.body
|
|
6213
|
+
});
|
|
6214
|
+
return {
|
|
6215
|
+
ok: true,
|
|
6216
|
+
accountId: account.accountId,
|
|
6217
|
+
action: params.toolParams.action,
|
|
6218
|
+
data
|
|
6219
|
+
};
|
|
6220
|
+
}
|
|
6221
|
+
|
|
6222
|
+
// src/admin/query-tool.ts
|
|
6223
|
+
var GrixQueryToolSchema = {
|
|
6224
|
+
oneOf: [
|
|
6225
|
+
{
|
|
6226
|
+
type: "object",
|
|
6227
|
+
additionalProperties: false,
|
|
6228
|
+
properties: {
|
|
6229
|
+
action: { const: "contact_search" },
|
|
6230
|
+
accountId: { type: "string", minLength: 1 },
|
|
6231
|
+
id: { type: "string", pattern: "^[0-9]+$" },
|
|
6232
|
+
limit: { type: "integer", minimum: 1 },
|
|
6233
|
+
offset: { type: "integer", minimum: 0 }
|
|
6234
|
+
},
|
|
6235
|
+
required: ["action", "id"]
|
|
6236
|
+
},
|
|
6237
|
+
{
|
|
6238
|
+
type: "object",
|
|
6239
|
+
additionalProperties: false,
|
|
6240
|
+
properties: {
|
|
6241
|
+
action: { const: "session_search" },
|
|
6242
|
+
accountId: { type: "string", minLength: 1 },
|
|
6243
|
+
id: { type: "string", minLength: 1 },
|
|
6244
|
+
limit: { type: "integer", minimum: 1 },
|
|
6245
|
+
offset: { type: "integer", minimum: 0 }
|
|
6246
|
+
},
|
|
6247
|
+
required: ["action", "id"]
|
|
6248
|
+
},
|
|
6249
|
+
{
|
|
6250
|
+
type: "object",
|
|
6251
|
+
additionalProperties: false,
|
|
6252
|
+
properties: {
|
|
6253
|
+
action: { const: "message_history" },
|
|
6254
|
+
accountId: { type: "string", minLength: 1 },
|
|
6255
|
+
sessionId: { type: "string", minLength: 1 },
|
|
6256
|
+
beforeId: { type: "string", pattern: "^[0-9]+$" },
|
|
6257
|
+
limit: { type: "integer", minimum: 1 }
|
|
6258
|
+
},
|
|
6259
|
+
required: ["action", "sessionId"]
|
|
6260
|
+
}
|
|
6261
|
+
]
|
|
6262
|
+
};
|
|
6263
|
+
function createGrixQueryTool(api) {
|
|
6264
|
+
return {
|
|
6265
|
+
name: "grix_query",
|
|
6266
|
+
label: "Grix Query",
|
|
6267
|
+
description: "Search Grix contacts and sessions, or read session message history through typed query operations.",
|
|
6268
|
+
parameters: GrixQueryToolSchema,
|
|
6269
|
+
async execute(_toolCallId, params) {
|
|
6270
|
+
try {
|
|
6271
|
+
return jsonToolResult(
|
|
6272
|
+
await runGrixQueryAction({
|
|
6273
|
+
cfg: api.config,
|
|
6274
|
+
toolParams: params
|
|
6275
|
+
})
|
|
6276
|
+
);
|
|
6277
|
+
} catch (err) {
|
|
6278
|
+
return jsonToolResult({
|
|
6279
|
+
error: err instanceof Error ? err.message : String(err)
|
|
6280
|
+
});
|
|
6281
|
+
}
|
|
6282
|
+
}
|
|
6283
|
+
};
|
|
6284
|
+
}
|
|
6285
|
+
|
|
6286
|
+
// src/admin/cli.ts
|
|
6287
|
+
function parseDescribeMessageToolJSON(value) {
|
|
6288
|
+
let parsed;
|
|
6289
|
+
try {
|
|
6290
|
+
parsed = JSON.parse(value);
|
|
6291
|
+
} catch {
|
|
6292
|
+
throw new Error("Invalid --describe-message-tool JSON.");
|
|
6293
|
+
}
|
|
6294
|
+
if (parsed == null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
6295
|
+
throw new Error("--describe-message-tool must be a JSON object.");
|
|
6296
|
+
}
|
|
6297
|
+
const raw = parsed;
|
|
6298
|
+
if (!Array.isArray(raw.actions) || raw.actions.length === 0) {
|
|
6299
|
+
throw new Error("--describe-message-tool.actions must be a non-empty array.");
|
|
6300
|
+
}
|
|
6301
|
+
for (const action of raw.actions) {
|
|
6302
|
+
if (typeof action !== "string" || !action.trim()) {
|
|
6303
|
+
throw new Error("--describe-message-tool.actions must contain non-empty strings.");
|
|
6304
|
+
}
|
|
6305
|
+
}
|
|
6306
|
+
return raw;
|
|
6307
|
+
}
|
|
6308
|
+
function registerGrixAdminCli(params) {
|
|
6309
|
+
const root = params.program.command("grix").description("Grix operator utilities").addHelpText(
|
|
6310
|
+
"after",
|
|
6311
|
+
"\nThis CLI is for operator workflows. Agent tools stay scoped to typed remote admin actions only.\n"
|
|
6312
|
+
);
|
|
6313
|
+
root.command("doctor").description("Show the Grix accounts visible from the current OpenClaw config").action(() => {
|
|
6314
|
+
console.log(JSON.stringify(inspectGrixAdminConfig(params.api.config), null, 2));
|
|
6315
|
+
});
|
|
6316
|
+
root.command("create-agent").description("Create a Grix API agent and print the exact next steps for channel binding").requiredOption("--agent-name <name>", "New API agent name").requiredOption(
|
|
6317
|
+
"--describe-message-tool <json>",
|
|
6318
|
+
"Message tool discovery JSON aligned with OpenClaw describeMessageTool",
|
|
6319
|
+
parseDescribeMessageToolJSON
|
|
6320
|
+
).option("--account-id <id>", "Configured Grix account id").option("--avatar-url <url>", "Optional avatar URL").action(
|
|
6321
|
+
async (options) => {
|
|
6322
|
+
const result = await createGrixApiAgent({
|
|
6323
|
+
cfg: params.api.config,
|
|
6324
|
+
toolParams: options
|
|
6325
|
+
});
|
|
6326
|
+
console.log(JSON.stringify(result, null, 2));
|
|
6327
|
+
}
|
|
6328
|
+
);
|
|
6329
|
+
}
|
|
6330
|
+
|
|
5059
6331
|
// index.ts
|
|
5060
6332
|
function emptyPluginConfigSchema() {
|
|
5061
6333
|
return {
|
|
@@ -5087,11 +6359,17 @@ function emptyPluginConfigSchema() {
|
|
|
5087
6359
|
var plugin = {
|
|
5088
6360
|
id: "grix",
|
|
5089
6361
|
name: "Grix OpenClaw",
|
|
5090
|
-
description: "
|
|
6362
|
+
description: "Unified Grix plugin for OpenClaw channel transport, typed admin tools, and operator CLI",
|
|
5091
6363
|
configSchema: emptyPluginConfigSchema(),
|
|
5092
6364
|
register(api) {
|
|
5093
6365
|
setAibotRuntime(api.runtime);
|
|
5094
6366
|
api.registerChannel({ plugin: aibotPlugin });
|
|
6367
|
+
api.registerTool(createGrixQueryTool(api), { optional: true });
|
|
6368
|
+
api.registerTool(createGrixGroupTool(api), { optional: true });
|
|
6369
|
+
api.registerTool(createGrixAgentAdminTool(api), { optional: true });
|
|
6370
|
+
api.registerCli(({ program }) => registerGrixAdminCli({ api, program }), {
|
|
6371
|
+
commands: ["grix"]
|
|
6372
|
+
});
|
|
5095
6373
|
}
|
|
5096
6374
|
};
|
|
5097
6375
|
var index_default = plugin;
|