@algosuite/vo-mcp 0.2.0-beta.20 → 0.2.0-beta.21
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/cli.js +180 -15
- package/dist/cli.js.map +4 -4
- package/dist/index.js +186 -34
- package/dist/index.js.map +4 -4
- package/dist/runner-cli.js +569 -11
- package/dist/runner-cli.js.map +4 -4
- package/dist/runner-supervisor.js +4 -1
- package/dist/runner-supervisor.js.map +2 -2
- package/dist/set-key-cli.js +8 -2
- package/dist/set-key-cli.js.map +2 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -293,6 +293,123 @@ var init_credential_store = __esm({
|
|
|
293
293
|
}
|
|
294
294
|
});
|
|
295
295
|
|
|
296
|
+
// src/tools/memory/safe-memory-file.ts
|
|
297
|
+
import { resolve, sep } from "node:path";
|
|
298
|
+
function isSafeMemoryFileName(fileName) {
|
|
299
|
+
return fileName.length <= 200 && fileName.trim() === fileName && !fileName.includes("/") && !fileName.includes("\\") && !fileName.includes(":") && SAFE_MEMORY_FILE_RE.test(fileName);
|
|
300
|
+
}
|
|
301
|
+
function resolveMemoryFilePath(memoryDir, fileName) {
|
|
302
|
+
if (!isSafeMemoryFileName(fileName)) {
|
|
303
|
+
throw new Error(`unsafe memory file_name: ${fileName.slice(0, 80)}`);
|
|
304
|
+
}
|
|
305
|
+
const root = resolve(memoryDir);
|
|
306
|
+
const filePath = resolve(root, fileName);
|
|
307
|
+
const rootPrefix = root.endsWith(sep) ? root : `${root}${sep}`;
|
|
308
|
+
if (filePath !== root && !filePath.startsWith(rootPrefix)) {
|
|
309
|
+
throw new Error(`memory file path escapes memory directory: ${fileName.slice(0, 80)}`);
|
|
310
|
+
}
|
|
311
|
+
return filePath;
|
|
312
|
+
}
|
|
313
|
+
var SAFE_MEMORY_FILE_RE;
|
|
314
|
+
var init_safe_memory_file = __esm({
|
|
315
|
+
"src/tools/memory/safe-memory-file.ts"() {
|
|
316
|
+
"use strict";
|
|
317
|
+
SAFE_MEMORY_FILE_RE = /^[A-Za-z0-9][A-Za-z0-9._ -]*\.md$/i;
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
// src/tools/memory/memory-knowledge-bridge.ts
|
|
322
|
+
var memory_knowledge_bridge_exports = {};
|
|
323
|
+
__export(memory_knowledge_bridge_exports, {
|
|
324
|
+
extractMemoryTitle: () => extractMemoryTitle,
|
|
325
|
+
upsertMemoryFilesAsKnowledge: () => upsertMemoryFilesAsKnowledge
|
|
326
|
+
});
|
|
327
|
+
import { existsSync as existsSync5, readdirSync as readdirSync4, readFileSync as readFileSync7 } from "node:fs";
|
|
328
|
+
function extractMemoryTitle(fileName, content) {
|
|
329
|
+
const frontmatter = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
330
|
+
if (frontmatter) {
|
|
331
|
+
const description23 = frontmatter[1].match(/^description:\s*(.+)$/m);
|
|
332
|
+
if (description23 && description23[1].trim()) return description23[1].trim().slice(0, 200);
|
|
333
|
+
}
|
|
334
|
+
const heading = content.match(/^#\s+(.+)$/m);
|
|
335
|
+
if (heading && heading[1].trim()) return heading[1].trim().slice(0, 200);
|
|
336
|
+
return fileName;
|
|
337
|
+
}
|
|
338
|
+
async function upsertMemoryFilesAsKnowledge(options) {
|
|
339
|
+
const { controlPlaneUrl, token, memoryDir, fetchFn } = options;
|
|
340
|
+
let files;
|
|
341
|
+
try {
|
|
342
|
+
if (!existsSync5(memoryDir)) {
|
|
343
|
+
return { attempted: 0, upserted: 0, failed: 0, failures: [] };
|
|
344
|
+
}
|
|
345
|
+
files = readdirSync4(memoryDir).filter(
|
|
346
|
+
(f) => f.endsWith(".md") && f.toUpperCase() !== "MEMORY.MD"
|
|
347
|
+
);
|
|
348
|
+
} catch (err) {
|
|
349
|
+
return {
|
|
350
|
+
attempted: 0,
|
|
351
|
+
upserted: 0,
|
|
352
|
+
failed: 1,
|
|
353
|
+
failures: [`memory dir scan: ${err instanceof Error ? err.message : String(err)}`]
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
let upserted = 0;
|
|
357
|
+
const failures = [];
|
|
358
|
+
for (const fileName of files) {
|
|
359
|
+
try {
|
|
360
|
+
const content = readFileSync7(resolveMemoryFilePath(memoryDir, fileName), "utf8");
|
|
361
|
+
if (content.length > CONTENT_HARD_LIMIT) {
|
|
362
|
+
failures.push(`${fileName}: ${content.length} chars exceeds the ${CONTENT_HARD_LIMIT} server limit \u2014 split the memory file`);
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
const title = extractMemoryTitle(fileName, content);
|
|
366
|
+
const base = {
|
|
367
|
+
knowledge_class: "memory",
|
|
368
|
+
source_path: `memory/${fileName}`,
|
|
369
|
+
title,
|
|
370
|
+
content
|
|
371
|
+
};
|
|
372
|
+
const post = (body) => fetchFn(`${controlPlaneUrl}/api/v1/knowledge/private`, {
|
|
373
|
+
method: "POST",
|
|
374
|
+
headers: {
|
|
375
|
+
authorization: `Bearer ${token}`,
|
|
376
|
+
"content-type": "application/json"
|
|
377
|
+
},
|
|
378
|
+
body: JSON.stringify(body)
|
|
379
|
+
});
|
|
380
|
+
let response = await post({
|
|
381
|
+
...base,
|
|
382
|
+
provenance: { written_by: "memory-bridge", source_kind: "operator_memory" }
|
|
383
|
+
});
|
|
384
|
+
if (response.status === 400) {
|
|
385
|
+
response = await post(base);
|
|
386
|
+
}
|
|
387
|
+
if (response.status >= 200 && response.status < 300) {
|
|
388
|
+
upserted += 1;
|
|
389
|
+
} else {
|
|
390
|
+
const text = await response.text();
|
|
391
|
+
failures.push(`${fileName}: HTTP ${response.status} ${text.slice(0, 80)}`);
|
|
392
|
+
}
|
|
393
|
+
} catch (err) {
|
|
394
|
+
failures.push(`${fileName}: ${err instanceof Error ? err.message : String(err)}`);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
return {
|
|
398
|
+
attempted: files.length,
|
|
399
|
+
upserted,
|
|
400
|
+
failed: failures.length,
|
|
401
|
+
failures: failures.slice(0, 5)
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
var CONTENT_HARD_LIMIT;
|
|
405
|
+
var init_memory_knowledge_bridge = __esm({
|
|
406
|
+
"src/tools/memory/memory-knowledge-bridge.ts"() {
|
|
407
|
+
"use strict";
|
|
408
|
+
init_safe_memory_file();
|
|
409
|
+
CONTENT_HARD_LIMIT = 5e5;
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
|
|
296
413
|
// src/server.ts
|
|
297
414
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
298
415
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
@@ -4943,28 +5060,8 @@ async function handleConciergeDispatch(deps, rawInput, _signal) {
|
|
|
4943
5060
|
// src/tools/memory/sync-config.ts
|
|
4944
5061
|
import { homedir as homedir5 } from "node:os";
|
|
4945
5062
|
import { join as join7 } from "node:path";
|
|
4946
|
-
import { existsSync as
|
|
4947
|
-
|
|
4948
|
-
// src/tools/memory/safe-memory-file.ts
|
|
4949
|
-
import { resolve, sep } from "node:path";
|
|
4950
|
-
var SAFE_MEMORY_FILE_RE = /^[A-Za-z0-9][A-Za-z0-9._ -]*\.md$/i;
|
|
4951
|
-
function isSafeMemoryFileName(fileName) {
|
|
4952
|
-
return fileName.length <= 200 && fileName.trim() === fileName && !fileName.includes("/") && !fileName.includes("\\") && !fileName.includes(":") && SAFE_MEMORY_FILE_RE.test(fileName);
|
|
4953
|
-
}
|
|
4954
|
-
function resolveMemoryFilePath(memoryDir, fileName) {
|
|
4955
|
-
if (!isSafeMemoryFileName(fileName)) {
|
|
4956
|
-
throw new Error(`unsafe memory file_name: ${fileName.slice(0, 80)}`);
|
|
4957
|
-
}
|
|
4958
|
-
const root = resolve(memoryDir);
|
|
4959
|
-
const filePath = resolve(root, fileName);
|
|
4960
|
-
const rootPrefix = root.endsWith(sep) ? root : `${root}${sep}`;
|
|
4961
|
-
if (filePath !== root && !filePath.startsWith(rootPrefix)) {
|
|
4962
|
-
throw new Error(`memory file path escapes memory directory: ${fileName.slice(0, 80)}`);
|
|
4963
|
-
}
|
|
4964
|
-
return filePath;
|
|
4965
|
-
}
|
|
4966
|
-
|
|
4967
|
-
// src/tools/memory/sync-config.ts
|
|
5063
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync8, writeFileSync as writeFileSync3, readdirSync as readdirSync5 } from "node:fs";
|
|
5064
|
+
init_safe_memory_file();
|
|
4968
5065
|
var TOOL_NAME22 = "vo_sync_config";
|
|
4969
5066
|
var inputSchema22 = {
|
|
4970
5067
|
type: "object",
|
|
@@ -4991,7 +5088,7 @@ function isToolInput22(v) {
|
|
|
4991
5088
|
return true;
|
|
4992
5089
|
}
|
|
4993
5090
|
function deriveProjectSlug(cwd) {
|
|
4994
|
-
return cwd.replace(
|
|
5091
|
+
return cwd.replace(/([^:\\/])[\\/]+$/, "$1").replace(/\\/g, "/").replace(/^([a-zA-Z]):/, (_m, drive) => `${drive.toUpperCase()}:`).replace(/[^a-zA-Z0-9]/g, "-");
|
|
4995
5092
|
}
|
|
4996
5093
|
function getMemoryDir(cwd) {
|
|
4997
5094
|
const slug = deriveProjectSlug(cwd);
|
|
@@ -5026,12 +5123,12 @@ async function pullMemory(controlPlaneUrl, token, memoryDir, fetchFn) {
|
|
|
5026
5123
|
return { pulled: data.entries.length, files };
|
|
5027
5124
|
}
|
|
5028
5125
|
async function pushMemory(controlPlaneUrl, token, memoryDir, sessionId, fetchFn) {
|
|
5029
|
-
if (!
|
|
5126
|
+
if (!existsSync6(memoryDir)) {
|
|
5030
5127
|
return { pushed: 0, created: 0, updated: 0 };
|
|
5031
5128
|
}
|
|
5032
|
-
const localFiles =
|
|
5129
|
+
const localFiles = readdirSync5(memoryDir).filter((f) => f.endsWith(".md")).map((f) => ({
|
|
5033
5130
|
file_name: f,
|
|
5034
|
-
content:
|
|
5131
|
+
content: readFileSync8(resolveMemoryFilePath(memoryDir, f), "utf8"),
|
|
5035
5132
|
entry_type: f === "MEMORY.md" ? "index" : "topic"
|
|
5036
5133
|
}));
|
|
5037
5134
|
if (localFiles.length === 0) {
|
|
@@ -5136,13 +5233,32 @@ async function runMemorySync(action, cwd, sessionId, fetchFn = globalThis.fetch)
|
|
|
5136
5233
|
return { synced: true, action: "pull", pulled: result2.pulled, files: result2.files, memory_dir: memoryDir };
|
|
5137
5234
|
}
|
|
5138
5235
|
const result = await pushMemory(baseUrl, token, memoryDir, sessionId, fetchFn);
|
|
5236
|
+
let bridge = { upserted: 0, failed: 0, failures: [] };
|
|
5237
|
+
try {
|
|
5238
|
+
const { upsertMemoryFilesAsKnowledge: upsertMemoryFilesAsKnowledge2 } = await Promise.resolve().then(() => (init_memory_knowledge_bridge(), memory_knowledge_bridge_exports));
|
|
5239
|
+
bridge = await upsertMemoryFilesAsKnowledge2({
|
|
5240
|
+
controlPlaneUrl: baseUrl,
|
|
5241
|
+
token,
|
|
5242
|
+
memoryDir,
|
|
5243
|
+
fetchFn
|
|
5244
|
+
});
|
|
5245
|
+
} catch (err) {
|
|
5246
|
+
bridge = {
|
|
5247
|
+
upserted: 0,
|
|
5248
|
+
failed: 1,
|
|
5249
|
+
failures: [`bridge unavailable: ${err instanceof Error ? err.message : String(err)}`]
|
|
5250
|
+
};
|
|
5251
|
+
}
|
|
5139
5252
|
return {
|
|
5140
5253
|
synced: true,
|
|
5141
5254
|
action: "push",
|
|
5142
5255
|
pushed: result.pushed,
|
|
5143
5256
|
created: result.created,
|
|
5144
5257
|
updated: result.updated,
|
|
5145
|
-
memory_dir: memoryDir
|
|
5258
|
+
memory_dir: memoryDir,
|
|
5259
|
+
knowledge_upserted: bridge.upserted,
|
|
5260
|
+
knowledge_failed: bridge.failed,
|
|
5261
|
+
...bridge.failed > 0 ? { knowledge_failures: bridge.failures } : {}
|
|
5146
5262
|
};
|
|
5147
5263
|
} catch (err) {
|
|
5148
5264
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -5164,6 +5280,7 @@ async function handleSyncConfig(deps, rawInput, _signal, fetchFn = globalThis.fe
|
|
|
5164
5280
|
// src/tools/memory/private-knowledge.ts
|
|
5165
5281
|
var UPSERT_TOOL_NAME = "vo_private_knowledge_upsert";
|
|
5166
5282
|
var CONTEXT_TOOL_NAME = "vo_private_knowledge_context";
|
|
5283
|
+
var INVALIDATE_TOOL_NAME = "vo_private_knowledge_invalidate";
|
|
5167
5284
|
var KNOWLEDGE_CLASSES = ["memory", "skill", "doctrine", "hook", "command"];
|
|
5168
5285
|
var PRECISION_CHAR_BUDGET = 12e3;
|
|
5169
5286
|
var upsertInputSchema = {
|
|
@@ -5187,8 +5304,18 @@ var contextInputSchema = {
|
|
|
5187
5304
|
required: ["query"],
|
|
5188
5305
|
additionalProperties: false
|
|
5189
5306
|
};
|
|
5307
|
+
var invalidateInputSchema = {
|
|
5308
|
+
type: "object",
|
|
5309
|
+
properties: {
|
|
5310
|
+
knowledge_class: { type: "string", enum: KNOWLEDGE_CLASSES },
|
|
5311
|
+
source_path: { type: "string", minLength: 1, maxLength: 400, description: "Stable private source identifier of the entry to invalidate \u2014 must match the source_path used at upsert." }
|
|
5312
|
+
},
|
|
5313
|
+
required: ["knowledge_class", "source_path"],
|
|
5314
|
+
additionalProperties: false
|
|
5315
|
+
};
|
|
5190
5316
|
var upsertDescription = "Uploads or refreshes the authenticated operator\u2019s private cloud knowledge. Works for Claude, Codex, Cursor, and cowork clients via the same vo-mcp login credential. Returns metadata only, not raw stored content. PRECISION DISCIPLINE: keep each entry tight and focused (~1-3 pages) with a descriptive retrieval-friendly title \u2014 retrieval surfaces whole entries, so small dense entries beat bulk dumps. Split large corpora into focused entries, then run a retrieval self-test via vo_private_knowledge_context before relying on the knowledge.";
|
|
5191
5317
|
var contextDescription = "Retrieves prompt-ready private knowledge context for the authenticated operator. Returns snippets/context only; no raw corpus download. Also the retrieval self-test surface: after upserting critical knowledge, query for it here and confirm the entry surfaces before trusting it in downstream work.";
|
|
5318
|
+
var invalidateDescription = `Soft-deletes one private-knowledge entry for the authenticated operator: closes the live entry\u2019s validity window (bi-temporal) so it stops surfacing in retrieval. Never destroys data \u2014 invalidated versions remain queryable server-side via include_invalidated. Identify the entry by the same { knowledge_class, source_path } used at upsert; a not_found response means no live entry matches. After invalidating, self-test via ${CONTEXT_TOOL_NAME} to confirm the entry no longer surfaces.`;
|
|
5192
5319
|
function isKnowledgeClass(value) {
|
|
5193
5320
|
return typeof value === "string" && KNOWLEDGE_CLASSES.includes(value);
|
|
5194
5321
|
}
|
|
@@ -5197,6 +5324,11 @@ function isUpsertInput(value) {
|
|
|
5197
5324
|
const input = value;
|
|
5198
5325
|
return isKnowledgeClass(input["knowledge_class"]) && typeof input["source_path"] === "string" && typeof input["title"] === "string" && typeof input["content"] === "string";
|
|
5199
5326
|
}
|
|
5327
|
+
function isInvalidateInput(value) {
|
|
5328
|
+
if (typeof value !== "object" || value === null) return false;
|
|
5329
|
+
const input = value;
|
|
5330
|
+
return isKnowledgeClass(input["knowledge_class"]) && typeof input["source_path"] === "string";
|
|
5331
|
+
}
|
|
5200
5332
|
function isContextInput(value) {
|
|
5201
5333
|
if (typeof value !== "object" || value === null) return false;
|
|
5202
5334
|
const input = value;
|
|
@@ -5230,7 +5362,12 @@ async function callPrivateKnowledge(path3, body, fetchFn) {
|
|
|
5230
5362
|
body: JSON.stringify(body)
|
|
5231
5363
|
});
|
|
5232
5364
|
const text = await response.text();
|
|
5233
|
-
|
|
5365
|
+
let parsed;
|
|
5366
|
+
try {
|
|
5367
|
+
parsed = text ? JSON.parse(text) : null;
|
|
5368
|
+
} catch {
|
|
5369
|
+
parsed = null;
|
|
5370
|
+
}
|
|
5234
5371
|
if (response.status < 200 || response.status >= 300) {
|
|
5235
5372
|
return { ok: false, status: response.status, response: parsed ?? text };
|
|
5236
5373
|
}
|
|
@@ -5251,6 +5388,13 @@ async function handlePrivateKnowledgeUpsert(_deps, rawInput, _signal, fetchFn =
|
|
|
5251
5388
|
}
|
|
5252
5389
|
return jsonContent(envelope);
|
|
5253
5390
|
}
|
|
5391
|
+
async function handlePrivateKnowledgeInvalidate(_deps, rawInput, _signal, fetchFn = globalThis.fetch) {
|
|
5392
|
+
if (!isInvalidateInput(rawInput)) {
|
|
5393
|
+
throw invalidParams(INVALIDATE_TOOL_NAME, "expected { knowledge_class, source_path }.");
|
|
5394
|
+
}
|
|
5395
|
+
const payload = await callPrivateKnowledge("/api/v1/knowledge/private/invalidate", rawInput, fetchFn);
|
|
5396
|
+
return jsonContent({ tool: INVALIDATE_TOOL_NAME, schema_version: 1, payload });
|
|
5397
|
+
}
|
|
5254
5398
|
async function handlePrivateKnowledgeContext(_deps, rawInput, _signal, fetchFn = globalThis.fetch) {
|
|
5255
5399
|
if (!isContextInput(rawInput)) {
|
|
5256
5400
|
throw invalidParams(CONTEXT_TOOL_NAME, "expected { query, optional limit, optional knowledge_class }.");
|
|
@@ -5402,11 +5546,11 @@ async function handleHqWhiteboardRead(_deps, rawInput, signal) {
|
|
|
5402
5546
|
}
|
|
5403
5547
|
|
|
5404
5548
|
// src/tools/skills/skill-corpus.ts
|
|
5405
|
-
import { existsSync as
|
|
5549
|
+
import { existsSync as existsSync7, statSync as statSync5 } from "node:fs";
|
|
5406
5550
|
import { dirname as dirname5, isAbsolute, join as join9, resolve as resolve2 } from "node:path";
|
|
5407
5551
|
|
|
5408
5552
|
// ../skill-registry/src/loader.ts
|
|
5409
|
-
import { readdirSync as
|
|
5553
|
+
import { readdirSync as readdirSync6, readFileSync as readFileSync9, statSync as statSync4 } from "node:fs";
|
|
5410
5554
|
import { join as join8 } from "node:path";
|
|
5411
5555
|
var InvalidSkillFrontmatterError = class extends Error {
|
|
5412
5556
|
constructor(skillFile, reason) {
|
|
@@ -5457,7 +5601,7 @@ ${FRONTMATTER_DELIMITER}
|
|
|
5457
5601
|
return { name, description: description23, body };
|
|
5458
5602
|
}
|
|
5459
5603
|
function loadSkillsFromDir(skillsDir) {
|
|
5460
|
-
const entries =
|
|
5604
|
+
const entries = readdirSync6(skillsDir);
|
|
5461
5605
|
const skills = [];
|
|
5462
5606
|
for (const entry of entries) {
|
|
5463
5607
|
const entryPath = join8(skillsDir, entry);
|
|
@@ -5471,7 +5615,7 @@ function loadSkillsFromDir(skillsDir) {
|
|
|
5471
5615
|
const skillFile = join8(entryPath, "SKILL.md");
|
|
5472
5616
|
let raw;
|
|
5473
5617
|
try {
|
|
5474
|
-
raw =
|
|
5618
|
+
raw = readFileSync9(skillFile, "utf8");
|
|
5475
5619
|
} catch {
|
|
5476
5620
|
continue;
|
|
5477
5621
|
}
|
|
@@ -5512,12 +5656,12 @@ function resolveSkillsDir(env = process.env, startDir = process.cwd()) {
|
|
|
5512
5656
|
const override = env.VO_SKILLS_DIR;
|
|
5513
5657
|
if (typeof override === "string" && override.length > 0) {
|
|
5514
5658
|
const abs = isAbsolute(override) ? override : resolve2(startDir, override);
|
|
5515
|
-
return
|
|
5659
|
+
return existsSync7(abs) && statSync5(abs).isDirectory() ? abs : null;
|
|
5516
5660
|
}
|
|
5517
5661
|
let dir = resolve2(startDir);
|
|
5518
5662
|
for (let i = 0; i < MAX_WALK_UP_LEVELS; i += 1) {
|
|
5519
5663
|
const candidate = join9(dir, ".claude", "skills");
|
|
5520
|
-
if (
|
|
5664
|
+
if (existsSync7(candidate) && statSync5(candidate).isDirectory()) return candidate;
|
|
5521
5665
|
const parent = dirname5(dir);
|
|
5522
5666
|
if (parent === dir) break;
|
|
5523
5667
|
dir = parent;
|
|
@@ -5785,6 +5929,14 @@ function buildToolRegistry() {
|
|
|
5785
5929
|
},
|
|
5786
5930
|
handler: handlePrivateKnowledgeContext
|
|
5787
5931
|
},
|
|
5932
|
+
[INVALIDATE_TOOL_NAME]: {
|
|
5933
|
+
definition: {
|
|
5934
|
+
name: INVALIDATE_TOOL_NAME,
|
|
5935
|
+
description: invalidateDescription,
|
|
5936
|
+
inputSchema: invalidateInputSchema
|
|
5937
|
+
},
|
|
5938
|
+
handler: handlePrivateKnowledgeInvalidate
|
|
5939
|
+
},
|
|
5788
5940
|
[POST_TOOL_NAME]: {
|
|
5789
5941
|
definition: {
|
|
5790
5942
|
name: POST_TOOL_NAME,
|