@stalfh233/omc-cli 0.6.22 → 0.6.23
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 +36 -17
- package/dist/graph.js +32 -14
- package/dist/mcp-graph.js +9 -10
- package/package.json +3 -3
package/dist/cli.js
CHANGED
|
@@ -23,7 +23,7 @@ import { formatDoctorHumanSummary, unsupportedCapabilities } from "./version-fam
|
|
|
23
23
|
import { beginWriteFace, configureOutput, endWriteFace, humanSummary, maybeWarnMergedStreams, stripOutputFlags, writeEnvelope } from "./cli-output.js";
|
|
24
24
|
import { beginUsage, completeUsage, currentUsageArgsDigest, ensureProjectIgnore, priorIdenticalSuccess, recordUsageOutput, usageReport } from "./usage-log.js";
|
|
25
25
|
import { artifactFetchHint, artifactRoot, boundResult, emitInvalid, emitResult, emitThrown, emitTransportResult, outputBudget, readArtifact, withTransport, } from "./command-transport.js";
|
|
26
|
-
import { CLI_DEFAULT_OUTPUT_BYTES, assertSafeOutputPath,
|
|
26
|
+
import { CLI_DEFAULT_OUTPUT_BYTES, assertSafeOutputPath, pruneArtifacts } from "./artifacts.js";
|
|
27
27
|
import { artifactPathMissHint, nullProjectionHint, partialPathMissHint, projectJsonPath, projectJsonPathFanout, projectResultExtract } from "./path-projection.js";
|
|
28
28
|
import { basename, join, resolve } from "node:path";
|
|
29
29
|
import { buildToolRegistry } from "./tools.js";
|
|
@@ -1671,12 +1671,16 @@ async function cmdGraphPull(environment, concurrency, transport, path, applicati
|
|
|
1671
1671
|
}
|
|
1672
1672
|
}
|
|
1673
1673
|
function cmdGraphReferences(kind, code, transport, requestedEnvironment) {
|
|
1674
|
-
if (!["service", "model", "field"].includes(kind))
|
|
1675
|
-
return emitInvalid(transport, { message: "kind 必须是 service / model / field
|
|
1674
|
+
if (kind !== undefined && !["service", "model", "field"].includes(kind))
|
|
1675
|
+
return emitInvalid(transport, { message: "kind 必须是 service / model / field" });
|
|
1676
1676
|
try {
|
|
1677
|
-
const
|
|
1677
|
+
const kinds = kind === undefined ? ["service", "model", "field"] : [kind];
|
|
1678
|
+
const results = kinds.map((one) => queryGraphReferences(one, code));
|
|
1679
|
+
const result = results[0];
|
|
1678
1680
|
if (requestedEnvironment !== undefined && requestedEnvironment !== result.index.environmentName)
|
|
1679
1681
|
return emitResult({ status: "refused", blocker: "graph-environment-mismatch", message: `索引已绑定环境 ${result.index.environmentName},不是 ${requestedEnvironment};请先 omc graph pull --env ${requestedEnvironment}`, index: result.index }, transport);
|
|
1682
|
+
if (kind === undefined)
|
|
1683
|
+
return emitResult({ status: "ok", code, index: result.index, references: Object.fromEntries(results.map((item) => [item.kind, { matches: item.matches, ...(item.coverage !== undefined ? { coverage: item.coverage } : {}), ...(item.note !== undefined ? { note: item.note } : {}) }])) }, transport);
|
|
1680
1684
|
return emitResult({
|
|
1681
1685
|
status: "ok",
|
|
1682
1686
|
kind: result.kind,
|
|
@@ -1707,24 +1711,28 @@ function cmdGraphReferencesBatch(kind, codeList, transport) {
|
|
|
1707
1711
|
return emitThrown(error, transport);
|
|
1708
1712
|
}
|
|
1709
1713
|
}
|
|
1710
|
-
function cmdGraphEdges(fromSchema, toSchema, requestedEnvironment, transport) {
|
|
1714
|
+
function cmdGraphEdges(fromSchema, toSchema, kind, offset, limit, requestedEnvironment, transport) {
|
|
1711
1715
|
try {
|
|
1712
|
-
|
|
1716
|
+
if (kind !== undefined && !["service", "model", "field"].includes(kind))
|
|
1717
|
+
return emitInvalid(transport, { message: "kind 必须是 service / model / field" });
|
|
1718
|
+
const pageOffset = offset ?? 0;
|
|
1719
|
+
const pageLimit = limit ?? 50;
|
|
1720
|
+
if (!Number.isSafeInteger(pageOffset) || pageOffset < 0 || !Number.isSafeInteger(pageLimit) || pageLimit < 1)
|
|
1721
|
+
return emitInvalid(transport, { message: "offset 必须是非负整数,limit 必须是正整数" });
|
|
1722
|
+
const result = queryGraphEdges(fromSchema, toSchema, kind, pageOffset, pageLimit);
|
|
1713
1723
|
if (requestedEnvironment !== undefined && requestedEnvironment !== result.index.environmentName)
|
|
1714
1724
|
return emitResult({ status: "refused", blocker: "graph-environment-mismatch", message: `索引已绑定环境 ${result.index.environmentName},不是 ${requestedEnvironment};请先 omc graph pull --env ${requestedEnvironment}`, index: result.index }, transport);
|
|
1715
|
-
if (result.total > 200) {
|
|
1716
|
-
const artifact = createArtifact(artifactRoot(), JSON.stringify(result.edges, null, 2));
|
|
1717
|
-
return emitResult({ status: "ok", total: result.total, index: result.index, artifactId: artifact.artifactId, sha256: artifact.sha256, edges: { count: result.edges.length, truncated: true } }, transport);
|
|
1718
|
-
}
|
|
1719
1725
|
return emitResult({ status: "ok", ...result }, transport);
|
|
1720
1726
|
}
|
|
1721
1727
|
catch (error) {
|
|
1722
1728
|
return emitThrown(error, transport);
|
|
1723
1729
|
}
|
|
1724
1730
|
}
|
|
1725
|
-
function cmdGraphStatus(transport) {
|
|
1731
|
+
function cmdGraphStatus(requestedEnvironment, transport) {
|
|
1726
1732
|
try {
|
|
1727
1733
|
const status = readGraphStatus();
|
|
1734
|
+
if (requestedEnvironment !== undefined && requestedEnvironment !== status.index.environmentName)
|
|
1735
|
+
return emitResult({ status: "refused", blocker: "graph-environment-mismatch", message: `索引已绑定环境 ${status.index.environmentName},不是 ${requestedEnvironment}`, index: status.index }, transport);
|
|
1728
1736
|
// r31a-F16:这里是**第四个**白名单式重建(前三次:compact() 投影、references
|
|
1729
1737
|
// 覆盖声明、mcp-graph 的 explore)。白名单每漏一个键就等于没修,且不会被任何
|
|
1730
1738
|
// 单测发现——所以改成展开,新增字段自动透传。
|
|
@@ -2595,14 +2603,25 @@ async function main() {
|
|
|
2595
2603
|
const codes = options.applicationCodes ? String(options.applicationCodes).split(",").map((code) => code.trim()).filter(Boolean) : undefined;
|
|
2596
2604
|
commandExitCode = await withTransport(options, request, (transport) => cmdGraphPull(options.environment ?? options.env, parsed, transport, options.path, codes, options.allApplications === true));
|
|
2597
2605
|
});
|
|
2598
|
-
graphCommand.command("references [kind] [code]").option("--batch <codes>").option("--env <environment>", "本子命令不接触环境;该参数仅用于提示索引绑定环境").option("--max-output-bytes <bytes>").addHelpText("after", "\n离线查询(零 wire
|
|
2599
|
-
|
|
2606
|
+
graphCommand.command("references [kind] [code]").option("--batch <codes>").option("--env <environment>", "本子命令不接触环境;该参数仅用于提示索引绑定环境").option("--max-output-bytes <bytes>").addHelpText("after", "\n离线查询(零 wire):省略 kind 时=service|model|field 三类全查;支持 --batch code1,code2(例如 --batch svc_a,svc_b)。\n").action(async (kind, code, options) => {
|
|
2607
|
+
// r42b 接线(2026-11-08,r42a-F8):commander 把首个位置参数绑给 [kind],
|
|
2608
|
+
// 「references <code>」这种最自然写法会把 code 当 kind 拒掉,而
|
|
2609
|
+
// cmdGraphReferences 的三类全查分支只认 kind===undefined——help 承诺的
|
|
2610
|
+
// 「省略 kind」在 CLI 面不可达。这里做智能重绑:首参不是合法 kind 且
|
|
2611
|
+
// 第二参缺席时,视首参为 code。
|
|
2612
|
+
let boundKind = kind;
|
|
2613
|
+
let boundCode = code;
|
|
2614
|
+
if (kind !== undefined && !["service", "model", "field"].includes(kind) && code === undefined) {
|
|
2615
|
+
boundKind = undefined;
|
|
2616
|
+
boundCode = kind;
|
|
2617
|
+
}
|
|
2618
|
+
commandExitCode = await withTransport(options, request, (transport) => options.batch ? cmdGraphReferencesBatch(boundKind ?? "model", options.batch, transport) : cmdGraphReferences(boundKind, boundCode ?? "", transport, options.environment ?? options.env));
|
|
2600
2619
|
});
|
|
2601
2620
|
graphCommand.command("references-batch <kind> <code>").option("--max-output-bytes <bytes>").action(async (kind, code, options) => {
|
|
2602
2621
|
commandExitCode = await withTransport(options, request, (transport) => cmdGraphReferencesBatch(kind, code, transport));
|
|
2603
2622
|
});
|
|
2604
|
-
graphCommand.command("edges").option("--env <environment>", "本子命令不接触环境;该参数仅用于提示索引绑定环境").option("--from-schema <code>").option("--to-schema <code>").option("--max-output-bytes <bytes>").addHelpText("after", "\n离线全图边清单(零 wire
|
|
2605
|
-
commandExitCode = await withTransport(options, request, (transport) => cmdGraphEdges(options.fromSchema, options.toSchema, options.environment ?? options.env, transport));
|
|
2623
|
+
graphCommand.command("edges").option("--env <environment>", "本子命令不接触环境;该参数仅用于提示索引绑定环境").option("--from-schema <code>").option("--to-schema <code>").option("--kind <kind>").option("--offset <n>").option("--limit <n>").option("--max-output-bytes <bytes>").addHelpText("after", "\n离线全图边清单(零 wire);支持 --kind model|field|service 与 --offset/--limit 分页。\n").action(async (options) => {
|
|
2624
|
+
commandExitCode = await withTransport(options, request, (transport) => cmdGraphEdges(options.fromSchema, options.toSchema, options.kind, options.offset === undefined ? undefined : Number(options.offset), options.limit === undefined ? undefined : Number(options.limit), options.environment ?? options.env, transport));
|
|
2606
2625
|
});
|
|
2607
2626
|
graphCommand.command("impact <kind> <code>").option("--max-output-bytes <bytes>").addHelpText("after", "\n影响面(离线):跳1=引用该编码的规则(同 references),跳2=这些规则还读写哪些 service/model/field——改前看爆炸半径。截断上限 50 规则×每类 20 码。\n").action(async (kind, code, options) => {
|
|
2608
2627
|
commandExitCode = await withTransport(options, request, (transport) => cmdGraphImpact(kind, code, transport));
|
|
@@ -2610,8 +2629,8 @@ async function main() {
|
|
|
2610
2629
|
graphCommand.command("search <term...>").option("--max-output-bytes <bytes>").addHelpText("after", "\n检索规则的 code+name(FTS5,末词前缀匹配;**不含规则体/配置内容**)。找到\"关于 X 的规则\"拿 code,再走 references/impact。中文必须是全词或词首通配(如 报名* / 增加预算*)——子串不命中:FTS5 把「增加预算」当一个 token,搜「预算」为 0。\n").action(async (term, options) => {
|
|
2611
2630
|
commandExitCode = await withTransport(options, request, (transport) => cmdGraphSearch(term.join(" "), transport));
|
|
2612
2631
|
});
|
|
2613
|
-
graphCommand.command("status").option("--max-output-bytes <bytes>").action(async (options) => {
|
|
2614
|
-
commandExitCode = await withTransport(options, request, (transport) => cmdGraphStatus(transport));
|
|
2632
|
+
graphCommand.command("status").option("--env <environment>").option("--environment <environment>").option("--max-output-bytes <bytes>").action(async (options) => {
|
|
2633
|
+
commandExitCode = await withTransport(options, request, (transport) => cmdGraphStatus(options.environment ?? options.env, transport));
|
|
2615
2634
|
});
|
|
2616
2635
|
// MCP stdio server (host registration: cpdocs installer's omc-graph entry;
|
|
2617
2636
|
// top-level verb like `codegraph serve --mcp`, not a graph subcommand).
|
package/dist/graph.js
CHANGED
|
@@ -46,7 +46,7 @@ function nodeSqlite() {
|
|
|
46
46
|
* staleness is handled by reporting pulledAt + identity, refreshed by an
|
|
47
47
|
* explicit re-pull (no incremental watermarks, no write-back hooks).
|
|
48
48
|
*/
|
|
49
|
-
export const GRAPH_SCHEMA_VERSION = "
|
|
49
|
+
export const GRAPH_SCHEMA_VERSION = "7";
|
|
50
50
|
const STALE_LOCK_MS = 15 * 60 * 1000;
|
|
51
51
|
export const DEFAULT_PULL_CONCURRENCY = 16;
|
|
52
52
|
export const MAX_PULL_CONCURRENCY = 32;
|
|
@@ -202,7 +202,13 @@ export async function pullRuleGraph(session, concurrency = DEFAULT_PULL_CONCURRE
|
|
|
202
202
|
const services = [];
|
|
203
203
|
await Promise.all(schemaCodes.map(async (schemaCode) => {
|
|
204
204
|
try {
|
|
205
|
-
|
|
205
|
+
let rows = wireRows((await wireCall(session, { path: ENDPOINTS.fieldList, query: { schemaCode, isPublish: "true" } })).data);
|
|
206
|
+
// Several applications expose fields only on the draft face. An empty
|
|
207
|
+
// published response is not evidence that the model has no fields.
|
|
208
|
+
if (rows.length === 0)
|
|
209
|
+
rows = wireRows((await wireCall(session, { path: ENDPOINTS.fieldList, query: { schemaCode, isPublish: "false" } })).data);
|
|
210
|
+
if (rows.length === 0)
|
|
211
|
+
gaps.push({ kind: "field-list", ref: schemaCode, note: "published and draft faces are empty" });
|
|
206
212
|
for (const row of rows) {
|
|
207
213
|
const code = String(row.code ?? row.propertyCode ?? "");
|
|
208
214
|
if (code)
|
|
@@ -219,7 +225,7 @@ export async function pullRuleGraph(session, concurrency = DEFAULT_PULL_CONCURRE
|
|
|
219
225
|
for (const row of rows) {
|
|
220
226
|
const code = String(row.code ?? "");
|
|
221
227
|
if (code)
|
|
222
|
-
services.push({ code, name: String(row.name ?? code) });
|
|
228
|
+
services.push({ code, name: String(row.name ?? code), applicationCode });
|
|
223
229
|
}
|
|
224
230
|
}
|
|
225
231
|
catch (error) {
|
|
@@ -504,7 +510,7 @@ export async function publishGraphPull(session, concurrency = DEFAULT_PULL_CONCU
|
|
|
504
510
|
const first = parts[0];
|
|
505
511
|
const data = parts.length === 1 ? first : {
|
|
506
512
|
...first,
|
|
507
|
-
meta: { ...first.meta, applicationCode: "*", coverage: { applicationCodes: parts.flatMap((part) => part.meta.coverage.applicationCodes), tenantApplications: parts.flatMap((part) => part.meta.coverage.applicationCodes) } },
|
|
513
|
+
meta: { ...first.meta, applicationCode: "*", applicationResolution: { code: "*", source: "wildcard" }, coverage: { applicationCodes: parts.flatMap((part) => part.meta.coverage.applicationCodes), tenantApplications: parts.flatMap((part) => part.meta.coverage.applicationCodes) } },
|
|
508
514
|
rules: parts.flatMap((part) => part.rules),
|
|
509
515
|
edges: parts.flatMap((part) => part.edges),
|
|
510
516
|
models: parts.flatMap((part) => part.models),
|
|
@@ -552,7 +558,7 @@ export async function publishGraphPull(session, concurrency = DEFAULT_PULL_CONCU
|
|
|
552
558
|
CREATE TABLE gaps (kind TEXT NOT NULL, ref TEXT NOT NULL, note TEXT);
|
|
553
559
|
CREATE TABLE models (schema_code TEXT PRIMARY KEY, name TEXT NOT NULL, application_code TEXT);
|
|
554
560
|
CREATE TABLE fields (schema_code TEXT NOT NULL, code TEXT NOT NULL, name TEXT NOT NULL, application_code TEXT);
|
|
555
|
-
|
|
561
|
+
CREATE TABLE services (code TEXT PRIMARY KEY, name TEXT NOT NULL, application_code TEXT);
|
|
556
562
|
`);
|
|
557
563
|
const insertMeta = db.prepare("INSERT INTO meta (key, value) VALUES (?, ?)");
|
|
558
564
|
// `applicationResolution` is an object; String(value) would persist
|
|
@@ -575,9 +581,9 @@ export async function publishGraphPull(session, concurrency = DEFAULT_PULL_CONCU
|
|
|
575
581
|
const insertField = db.prepare("INSERT INTO fields (schema_code, code, name, application_code) VALUES (?, ?, ?, ?)");
|
|
576
582
|
for (const row of data.fields)
|
|
577
583
|
insertField.run(row.schemaCode, row.code, row.name, row.applicationCode ?? null);
|
|
578
|
-
const insertService = db.prepare("INSERT OR
|
|
584
|
+
const insertService = db.prepare("INSERT OR REPLACE INTO services (code, name, application_code) VALUES (?, ?, ?)");
|
|
579
585
|
for (const row of data.services)
|
|
580
|
-
insertService.run(row.code, row.name);
|
|
586
|
+
insertService.run(row.code, row.name, row.applicationCode ?? null);
|
|
581
587
|
// FTS5 mirrors rules (code+name) for `graph search`; one rebuild after
|
|
582
588
|
// the bulk insert is cheaper than per-row triggers on a wholesale rebuild.
|
|
583
589
|
db.exec(`
|
|
@@ -613,7 +619,7 @@ function openGraphIndex() {
|
|
|
613
619
|
for (const row of db.prepare("SELECT key, value FROM meta").all())
|
|
614
620
|
meta[row.key] = row.value;
|
|
615
621
|
if (meta.schemaVersion !== GRAPH_SCHEMA_VERSION)
|
|
616
|
-
throw new GraphIndexAbsentError(`索引 schema 版本不匹配(${meta.schemaVersion ?? "unknown"} ≠ ${GRAPH_SCHEMA_VERSION}
|
|
622
|
+
throw new GraphIndexAbsentError(`索引 schema 版本不匹配(${meta.schemaVersion ?? "unknown"} ≠ ${GRAPH_SCHEMA_VERSION});当前版本需要 schema ${GRAPH_SCHEMA_VERSION},旧版索引已作废,请重跑 omc graph pull(schema 升版需重拉)`);
|
|
617
623
|
const ruleCount = db.prepare("SELECT COUNT(*) AS n FROM rules").get().n;
|
|
618
624
|
const modelCount = db.prepare("SELECT COUNT(DISTINCT schema_code) AS n FROM rules").get().n;
|
|
619
625
|
const entityCounts = {
|
|
@@ -649,7 +655,13 @@ function openGraphIndex() {
|
|
|
649
655
|
tenantApplications = parsedCoverage.tenantApplications.filter((code) => typeof code === "string");
|
|
650
656
|
}
|
|
651
657
|
catch { /* legacy index */ }
|
|
652
|
-
const
|
|
658
|
+
const fieldRows = db.prepare("SELECT application_code AS applicationCode, COUNT(*) AS n FROM fields GROUP BY application_code").all();
|
|
659
|
+
const countsByApplication = {};
|
|
660
|
+
for (const row of fieldRows)
|
|
661
|
+
if (row.applicationCode)
|
|
662
|
+
countsByApplication[row.applicationCode] = Number(row.n);
|
|
663
|
+
const fieldApplications = Object.keys(countsByApplication);
|
|
664
|
+
const coverage = { applicationCodes, ...(tenantApplications ? { tenantApplications } : {}), fields: { coveredApplications: fieldApplications, notCovered: applicationCodes.filter((code) => !fieldApplications.includes(code)), countsByApplication } };
|
|
653
665
|
return {
|
|
654
666
|
db,
|
|
655
667
|
summary: {
|
|
@@ -721,7 +733,7 @@ export function queryGraphReferences(kind, code) {
|
|
|
721
733
|
const { db, summary } = openGraphIndex();
|
|
722
734
|
try {
|
|
723
735
|
const rows = db
|
|
724
|
-
.prepare("SELECT schema_code, rule_code, node_code, node_type, via FROM edges WHERE kind = ? AND code = ? ORDER BY schema_code, rule_code, node_code, via")
|
|
736
|
+
.prepare("SELECT schema_code, rule_code, node_code, node_type, via, application_code FROM edges WHERE kind = ? AND code = ? ORDER BY schema_code, rule_code, node_code, via")
|
|
725
737
|
.all(kind, code);
|
|
726
738
|
const matches = rows.map((row) => ({
|
|
727
739
|
schemaCode: row.schema_code,
|
|
@@ -731,6 +743,7 @@ export function queryGraphReferences(kind, code) {
|
|
|
731
743
|
via: row.via,
|
|
732
744
|
referrer: { schemaCode: row.schema_code, ruleCode: row.rule_code },
|
|
733
745
|
isSelf: row.schema_code === code,
|
|
746
|
+
...(row.application_code ? { applicationCode: row.application_code } : {}),
|
|
734
747
|
}));
|
|
735
748
|
const gapped = summary.gaps.count > 0;
|
|
736
749
|
// r35a-F4(真机 2026-11-06):探针拿 matches:[] 差点把一个**被表单引用**的
|
|
@@ -772,7 +785,7 @@ export function queryGraphReferencesBatch(kind, codes) {
|
|
|
772
785
|
db.close();
|
|
773
786
|
}
|
|
774
787
|
}
|
|
775
|
-
export function queryGraphEdges(fromSchema, toSchema) {
|
|
788
|
+
export function queryGraphEdges(fromSchema, toSchema, kind, offset = 0, limit = 50) {
|
|
776
789
|
const { db, summary } = openGraphIndex();
|
|
777
790
|
try {
|
|
778
791
|
const conditions = [];
|
|
@@ -785,10 +798,15 @@ export function queryGraphEdges(fromSchema, toSchema) {
|
|
|
785
798
|
conditions.push("code = ?");
|
|
786
799
|
params.push(toSchema);
|
|
787
800
|
}
|
|
801
|
+
if (kind) {
|
|
802
|
+
conditions.push("kind = ?");
|
|
803
|
+
params.push(kind);
|
|
804
|
+
}
|
|
788
805
|
const where = conditions.length ? ` WHERE ${conditions.join(" AND ")}` : "";
|
|
789
806
|
const total = Number(db.prepare(`SELECT COUNT(*) AS n FROM edges${where}`).get(...params).n);
|
|
790
|
-
const rows = db.prepare(`SELECT kind, code, schema_code, rule_code, node_code, node_type, via, application_code FROM edges${where} ORDER BY schema_code, code, rule_code, node_code, via
|
|
791
|
-
|
|
807
|
+
const rows = db.prepare(`SELECT kind, code, schema_code, rule_code, node_code, node_type, via, application_code FROM edges${where} ORDER BY schema_code, code, rule_code, node_code, via LIMIT ? OFFSET ?`).all(...params, limit, offset);
|
|
808
|
+
const edges = rows.map((row) => ({ kind: row.kind, code: row.code, schemaCode: row.schema_code, ruleCode: row.rule_code, nodeCode: row.node_code ?? "", nodeType: row.node_type ?? "", via: row.via, ...(row.application_code ? { applicationCode: row.application_code } : {}) }));
|
|
809
|
+
return { edges, index: summary, total, truncated: offset + edges.length < total, ...(offset + edges.length < total ? { nextOffset: offset + edges.length } : {}) };
|
|
792
810
|
}
|
|
793
811
|
finally {
|
|
794
812
|
db.close();
|
|
@@ -804,7 +822,7 @@ export function readGraphStatus() {
|
|
|
804
822
|
dbBytes: statSync(dbPath).size,
|
|
805
823
|
dbPath,
|
|
806
824
|
dbId: sha256(dbPath).slice(0, 12),
|
|
807
|
-
mcpHint: `MCP 工具按注册时的 --path
|
|
825
|
+
mcpHint: `MCP 工具按注册时的 --path 读库(当前索引根:${dirname(dirname(dbPath))});要让 MCP 看到本次拉取,请执行 omc graph pull --path ${dirname(dirname(dbPath))} --env <env>。`,
|
|
808
826
|
ageMinutes: summary.ageMinutes,
|
|
809
827
|
staleAfterMinutes: summary.staleAfterMinutes,
|
|
810
828
|
...(summary.staleWarning ? { warning: summary.staleWarning } : {}),
|
package/dist/mcp-graph.js
CHANGED
|
@@ -130,7 +130,7 @@ function entityInventory() {
|
|
|
130
130
|
return [
|
|
131
131
|
...db.prepare("SELECT schema_code, name, application_code FROM models").all().map((r) => ({ kind: "model", schemaCode: r.schema_code, code: r.schema_code, name: r.name ?? "", applicationCode: r.application_code ?? undefined })),
|
|
132
132
|
...db.prepare("SELECT schema_code, code, name, application_code FROM fields").all().map((r) => ({ kind: "field", schemaCode: r.schema_code, code: r.code, name: r.name ?? "", applicationCode: r.application_code ?? undefined })),
|
|
133
|
-
...db.prepare("SELECT code, name FROM services").all().map((r) => ({ kind: "service", code: r.code, name: r.name ?? "" })),
|
|
133
|
+
...db.prepare("SELECT code, name, application_code FROM services").all().map((r) => ({ kind: "service", code: r.code, name: r.name ?? "", applicationCode: r.application_code ?? undefined })),
|
|
134
134
|
];
|
|
135
135
|
}
|
|
136
136
|
finally {
|
|
@@ -199,10 +199,10 @@ export async function executeGraphTool(name, rawArgs) {
|
|
|
199
199
|
const loweredQuery = primaryQuery.toLocaleLowerCase();
|
|
200
200
|
const allCandidates = entityInventory().filter((r) => (r.code.toLocaleLowerCase().includes(loweredQuery) || r.name.toLocaleLowerCase().includes(loweredQuery)) &&
|
|
201
201
|
(!schemaCode || (r.kind !== "service" && r.schemaCode === schemaCode)) &&
|
|
202
|
-
(!applicationCode || r.
|
|
202
|
+
(!applicationCode || r.applicationCode === applicationCode));
|
|
203
203
|
const candidatesForQuery = (term) => entityInventory().filter((r) => (r.code.toLocaleLowerCase().includes(term.toLocaleLowerCase()) || r.name.toLocaleLowerCase().includes(term.toLocaleLowerCase())) &&
|
|
204
204
|
(!schemaCode || (r.kind !== "service" && r.schemaCode === schemaCode)) &&
|
|
205
|
-
(!applicationCode || r.
|
|
205
|
+
(!applicationCode || r.applicationCode === applicationCode));
|
|
206
206
|
const perQueryModels = queries.map((term) => new Set(candidatesForQuery(term).filter((r) => r.kind === "model").map((r) => r.schemaCode)));
|
|
207
207
|
const perQueryFieldModels = queries.map((term) => new Set(candidatesForQuery(term).filter((r) => r.kind === "field").map((r) => r.schemaCode)));
|
|
208
208
|
const combine = (sets) => {
|
|
@@ -226,7 +226,7 @@ export async function executeGraphTool(name, rawArgs) {
|
|
|
226
226
|
for (const one of kinds) {
|
|
227
227
|
const subject = subjects.find((s) => s.kind === one);
|
|
228
228
|
const ref = queryGraphReferences(one, subject?.code ?? query);
|
|
229
|
-
const scopedMatches = subject && one === "field" && schemaCode ?
|
|
229
|
+
const scopedMatches = ref.matches.filter((row) => !applicationCode || row.applicationCode === applicationCode).filter((row) => subject && one === "field" && schemaCode ? row.schemaCode === schemaCode : true);
|
|
230
230
|
const capped = cap(scopedMatches, offset, limit);
|
|
231
231
|
// r35a-F4c(真机 2026-11-06):**MCP 面是第三个消费者**——CLI 的 references
|
|
232
232
|
// 与 impact 都带 coverage 了,这里若不显式透传,MCP 用户看到的仍是
|
|
@@ -237,7 +237,6 @@ export async function executeGraphTool(name, rawArgs) {
|
|
|
237
237
|
offset: capped.offset,
|
|
238
238
|
limit: capped.limit,
|
|
239
239
|
...(capped.truncated ? { truncated: true, nextOffset: capped.nextOffset, note: `共 ${capped.total} 条,已给 ${capped.rows.length} 条,offset=${capped.offset} 取下一段(调用时传 offset=${capped.nextOffset}、limit=${capped.limit})` } : {}),
|
|
240
|
-
...(ref.coverage ? { coverage: ref.coverage } : {}),
|
|
241
240
|
...(ref.gapWarning ? { gapWarning: ref.gapWarning } : {}),
|
|
242
241
|
};
|
|
243
242
|
if (scopedMatches.length > 0 || kind !== undefined || subject !== undefined) {
|
|
@@ -257,7 +256,6 @@ export async function executeGraphTool(name, rawArgs) {
|
|
|
257
256
|
return m;
|
|
258
257
|
}, new Map()).values()],
|
|
259
258
|
} : {}),
|
|
260
|
-
...(imp.coverage ? { coverage: imp.coverage } : {}),
|
|
261
259
|
};
|
|
262
260
|
}
|
|
263
261
|
}
|
|
@@ -274,10 +272,10 @@ export async function executeGraphTool(name, rawArgs) {
|
|
|
274
272
|
query,
|
|
275
273
|
...(queries.length > 1 ? { queries, match: args.match === "all" ? "all" : "any" } : {}),
|
|
276
274
|
hitModels: [...hitModels],
|
|
277
|
-
|
|
275
|
+
...(() => { const page = cap([...hitFieldModels].flatMap((schema) => entityInventory().filter((r) => r.kind === "field" && r.schemaCode === schema && (!applicationCode || r.applicationCode === applicationCode)).map((r) => ({ schemaCode: schema, code: r.code, name: r.name }))), offset, limit); return { hitFields: page.rows, hitFieldsCount: page.total, ...(page.truncated ? { hitFieldsTruncated: true, hitFieldsNextOffset: page.nextOffset } : {}) }; })(),
|
|
278
276
|
found: resolvedEntity !== undefined,
|
|
279
|
-
...(resolvedEntity ? { resolved: { kind: resolvedEntity.kind, schemaCode: "schemaCode" in resolvedEntity ? resolvedEntity.schemaCode : "", code: resolvedEntity.code, matchedOn: { on: resolvedEntity.name.includes(query) ? "name" : "code", fragment: query } } } : {}),
|
|
280
|
-
...(resolvedEntity ? { otherCandidates: allCandidates.filter((r) => r.code !== resolvedEntity.code).slice(0, 5).map((r) => ({ schemaCode: r.schemaCode ?? r.code, name: r.name })) } : {}),
|
|
277
|
+
...(resolvedEntity ? { resolved: { kind: resolvedEntity.kind, schemaCode: "schemaCode" in resolvedEntity ? resolvedEntity.schemaCode : "", code: resolvedEntity.code, matchedOn: { on: resolvedEntity.name.includes(query) ? "name" : "code", fragment: query }, ...(resolvedEntity.applicationCode ? { applicationCode: resolvedEntity.applicationCode } : {}) } } : {}),
|
|
278
|
+
...(resolvedEntity ? { otherCandidates: allCandidates.filter((r) => r.code !== resolvedEntity.code).slice(0, 5).map((r) => ({ schemaCode: r.schemaCode ?? r.code, name: r.name, ...(r.applicationCode ? { applicationCode: r.applicationCode } : {}) })) } : {}),
|
|
281
279
|
...(ambiguous ? { ambiguous: true, candidates: exactSame.map((r) => ({ schemaCode: r.schemaCode, code: r.code, name: r.name })) } : {}),
|
|
282
280
|
...(kind !== undefined ? { kind } : {}),
|
|
283
281
|
index,
|
|
@@ -291,7 +289,8 @@ export async function executeGraphTool(name, rawArgs) {
|
|
|
291
289
|
...(search.note ? { searchNote: `${searchNote}\n${search.note}` } : {}),
|
|
292
290
|
// 旧句「索引不含规则条件叶子与流程绑定」漏了表单与流程权限(r35a-F4 实证
|
|
293
291
|
// 探针据此差点误删被表单引用的字段)。改为指向结构化 coverage,不再靠散文。
|
|
294
|
-
|
|
292
|
+
coverage: { ...index.coverage, scanned: ["businessrule", "datarule", "listview"], notScanned: ["form", "workflow.permissions", "workflow.bindings", "onlinejs", "rule.conditions"] },
|
|
293
|
+
note: `零命中只代表 references[].coverage.scanned 面没有命中;字段面覆盖应用:${index.coverage.fields?.coveredApplications.join("、") || "(未知)"};notScanned 面(尤其 form 与 workflow.permissions)未扫描,不可据此判定可安全删除或无影响`,
|
|
295
294
|
});
|
|
296
295
|
}
|
|
297
296
|
return toolError(`未知工具:${name}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stalfh233/omc-cli",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.23",
|
|
4
4
|
"description": "CloudPivot 8.6 configuration capability CLI — governed, fail-closed, coverage-manifest bounded.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
57
|
"commander": "14.0.3",
|
|
58
|
-
"omc-core": "0.6.
|
|
58
|
+
"omc-core": "0.6.23",
|
|
59
59
|
"proper-lockfile": "4.1.2",
|
|
60
60
|
"write-file-atomic": "6.0.0",
|
|
61
61
|
"zod": "^3.23.8",
|
|
@@ -73,6 +73,6 @@
|
|
|
73
73
|
"directory": "packages/omc-cli"
|
|
74
74
|
},
|
|
75
75
|
"optionalDependencies": {
|
|
76
|
-
"@stalfh233/omc-fake-platform": "0.6.
|
|
76
|
+
"@stalfh233/omc-fake-platform": "0.6.23"
|
|
77
77
|
}
|
|
78
78
|
}
|