@stalfh233/omc-cli 0.4.102 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +359 -359
- package/coverage/m1-coverage-manifest-v1.json +1414 -1414
- package/dist/approval-flow.js +0 -0
- package/dist/approval-token.js +102 -0
- package/dist/args.js +0 -0
- package/dist/artifacts.js +0 -0
- package/dist/batch-call.js +0 -0
- package/dist/bench/call-face-eval.js +0 -0
- package/dist/bench/context-attribution.js +0 -0
- package/dist/bench/discovery-cost-eval.js +2 -0
- package/dist/bench/driver.js +0 -0
- package/dist/bench/evals.js +0 -0
- package/dist/bench/fake-http-server.js +65 -0
- package/dist/bench/instrument.js +0 -0
- package/dist/bench/intent-face-eval.js +40 -40
- package/dist/bench/run.js +0 -0
- package/dist/bench/scenario.js +0 -0
- package/dist/bench/types.js +0 -0
- package/dist/bi-wire.js +0 -0
- package/dist/bizservice-config.js +0 -0
- package/dist/call.js +0 -0
- package/dist/capability-absences.js +0 -0
- package/dist/capability-overview.js +0 -0
- package/dist/capability-shape.js +0 -0
- package/dist/cli-contract.js +38 -1
- package/dist/cli-output.js +8 -2
- package/dist/cli.js +147 -5
- package/dist/closest-match.js +0 -0
- package/dist/code-rules.js +0 -0
- package/dist/command-transport.js +0 -0
- package/dist/compound-writes.js +0 -0
- package/dist/compute.js +0 -0
- package/dist/config-store.js +0 -0
- package/dist/context.js +0 -0
- package/dist/contract-consistency.js +0 -0
- package/dist/contract-resources.js +0 -0
- package/dist/coverage-consistency.js +0 -0
- package/dist/coverage-registry.js +0 -0
- package/dist/coverage.js +0 -0
- package/dist/data-list-filter.js +0 -0
- package/dist/describe-cache.js +0 -0
- package/dist/discovery.js +0 -0
- package/dist/endpoints.js +0 -0
- package/dist/environment-policy.js +0 -0
- package/dist/execution-metadata.js +0 -0
- package/dist/fake/app.js +45 -0
- package/dist/fake/b2-registration.js +594 -0
- package/dist/fake/businessrule.js +242 -0
- package/dist/fake/datarule.js +72 -0
- package/dist/fake/dictionary.js +108 -0
- package/dist/fake/environment.js +30 -0
- package/dist/fake/field.js +167 -0
- package/dist/fake/form.js +101 -0
- package/dist/fake/index.js +121 -0
- package/dist/fake/list-view.js +289 -0
- package/dist/fake/model.js +136 -0
- package/dist/fake/online-js.js +18 -0
- package/dist/fake/report.js +253 -0
- package/dist/fake/routes.js +47 -0
- package/dist/fake/rule-lifecycle.js +37 -0
- package/dist/fake/runtime-data.js +367 -0
- package/dist/fake/state.js +67 -0
- package/dist/fake/workflow.js +364 -0
- package/dist/field-change.js +86 -2
- package/dist/field-families.js +59 -9
- package/dist/form-default.js +0 -0
- package/dist/form-layout.js +0 -0
- package/dist/form-support.js +0 -0
- package/dist/formula-lint.js +0 -0
- package/dist/goal-routes.js +16 -16
- package/dist/governed-execution.js +0 -0
- package/dist/graph.js +734 -0
- package/dist/human-summary.js +0 -0
- package/dist/identity.js +0 -0
- package/dist/intent/baseline.js +0 -0
- package/dist/intent/capabilities/bizservice.js +0 -0
- package/dist/intent/capabilities/businessrule.js +0 -0
- package/dist/intent/capabilities/datarule.js +0 -0
- package/dist/intent/capabilities/field.js +0 -0
- package/dist/intent/capabilities/form.js +0 -0
- package/dist/intent/capabilities/index.js +0 -0
- package/dist/intent/capabilities/listview.js +0 -0
- package/dist/intent/capabilities/model.js +0 -0
- package/dist/intent/capabilities/onlinejs.js +0 -0
- package/dist/intent/capabilities/report.js +0 -0
- package/dist/intent/capabilities/workflow.js +0 -0
- package/dist/intent/capability.js +0 -0
- package/dist/intent/cli.js +0 -0
- package/dist/intent/compare.js +0 -0
- package/dist/intent/compiler.js +0 -0
- package/dist/intent/dsl.js +0 -0
- package/dist/intent/plan-apply.js +0 -0
- package/dist/intent/plan-file.js +0 -0
- package/dist/intent/readback.js +0 -0
- package/dist/intent/schema.js +0 -0
- package/dist/intent/validation.js +0 -0
- package/dist/intent/yaml.js +0 -0
- package/dist/journal.js +0 -0
- package/dist/json-column.js +0 -0
- package/dist/lanes/app-contract.js +0 -0
- package/dist/lanes/app-coverage.js +0 -0
- package/dist/lanes/app.js +0 -0
- package/dist/lanes/apply-changes.js +0 -0
- package/dist/lanes/b2-registration-contract.js +0 -0
- package/dist/lanes/b2-registration-coverage.js +0 -0
- package/dist/lanes/b2-registration.js +0 -0
- package/dist/lanes/businessrule-contract.js +0 -0
- package/dist/lanes/businessrule-coverage.js +0 -0
- package/dist/lanes/businessrule.js +7 -1
- package/dist/lanes/contract-support.js +0 -0
- package/dist/lanes/coverage-declaration.js +0 -0
- package/dist/lanes/custom-page.js +0 -0
- package/dist/lanes/datarule-contract.js +0 -0
- package/dist/lanes/datarule-coverage.js +0 -0
- package/dist/lanes/datarule-protocol.js +0 -0
- package/dist/lanes/datarule.js +0 -0
- package/dist/lanes/dictionary-contract.js +0 -0
- package/dist/lanes/dictionary-coverage.js +0 -0
- package/dist/lanes/dictionary.js +0 -0
- package/dist/lanes/environment-contract.js +0 -0
- package/dist/lanes/environment-coverage.js +0 -0
- package/dist/lanes/environment.js +0 -0
- package/dist/lanes/field-contract.js +0 -0
- package/dist/lanes/field-coverage.js +0 -0
- package/dist/lanes/field.js +6 -1
- package/dist/lanes/folder-contract.js +0 -0
- package/dist/lanes/folder-coverage.js +0 -0
- package/dist/lanes/folder.js +0 -0
- package/dist/lanes/form-contract.js +0 -0
- package/dist/lanes/form-coverage.js +0 -0
- package/dist/lanes/form.js +0 -0
- package/dist/lanes/lane-ids.js +0 -0
- package/dist/lanes/list-view-contract.js +0 -0
- package/dist/lanes/list-view-coverage.js +0 -0
- package/dist/lanes/list-view-shapes.js +0 -0
- package/dist/lanes/list-view.js +0 -0
- package/dist/lanes/meta-contract.js +0 -0
- package/dist/lanes/meta.js +6 -0
- package/dist/lanes/model-contract.js +0 -0
- package/dist/lanes/model-coverage.js +0 -0
- package/dist/lanes/model.js +4 -2
- package/dist/lanes/online-js-contract.js +0 -0
- package/dist/lanes/online-js-coverage.js +0 -0
- package/dist/lanes/online-js.js +0 -0
- package/dist/lanes/org-tree.js +0 -0
- package/dist/lanes/organization.js +0 -0
- package/dist/lanes/report-contract.js +0 -0
- package/dist/lanes/report-coverage.js +0 -0
- package/dist/lanes/report.js +0 -0
- package/dist/lanes/rule-graph.js +0 -0
- package/dist/lanes/rule-lifecycle-contract.js +0 -0
- package/dist/lanes/rule-lifecycle-coverage.js +0 -0
- package/dist/lanes/rule-lifecycle.js +0 -0
- package/dist/lanes/runtime-data-contract.js +0 -0
- package/dist/lanes/runtime-data-coverage.js +0 -0
- package/dist/lanes/runtime-data.js +0 -0
- package/dist/lanes/workflow-contract.js +0 -0
- package/dist/lanes/workflow-coverage.js +0 -0
- package/dist/lanes/workflow.js +0 -0
- package/dist/model-purge.js +0 -0
- package/dist/model-scaffold.js +0 -0
- package/dist/omc-core-lazy.js +0 -0
- package/dist/online-js-layout.js +0 -0
- package/dist/online-js-source.js +28 -28
- package/dist/package-tool.js +0 -0
- package/dist/package.js +0 -0
- package/dist/path-projection.js +0 -0
- package/dist/plan.js +0 -0
- package/dist/read.js +0 -0
- package/dist/recipes/b2-registration.js +0 -0
- package/dist/recipes/businessrule.js +0 -0
- package/dist/recipes/datarule.js +0 -0
- package/dist/recipes/dictionary.js +0 -0
- package/dist/recipes/field.js +55 -1
- package/dist/recipes/form.js +0 -0
- package/dist/recipes/list-view.js +0 -0
- package/dist/recipes/model.js +0 -0
- package/dist/recipes/online-js.js +0 -0
- package/dist/recipes/runtime-data.js +0 -0
- package/dist/recipes/shared.js +0 -0
- package/dist/recipes/workflow.js +0 -0
- package/dist/redact.js +0 -0
- package/dist/refusal-guidance.js +0 -0
- package/dist/rollback-spec.js +0 -0
- package/dist/rollback.js +0 -0
- package/dist/rule-support.js +0 -0
- package/dist/rule-when.js +0 -0
- package/dist/runtime-support.js +0 -0
- package/dist/script.js +0 -0
- package/dist/server.js +92 -0
- package/dist/service-doctor.js +0 -0
- package/dist/session-manager.js +0 -0
- package/dist/session.js +12 -0
- package/dist/skills.js +12 -2
- package/dist/sqlite-warning-filter.js +0 -0
- package/dist/support.js +0 -0
- package/dist/token-cache.js +0 -0
- package/dist/tool-types.js +0 -0
- package/dist/tools.js +0 -0
- package/dist/usage-log.js +0 -0
- package/dist/wire.js +0 -0
- package/dist/workflow-support.js +0 -0
- package/dist/write-lease.js +0 -0
- package/dist/write-lock.js +0 -0
- package/dist/write.js +0 -0
- package/dist/zip.js +0 -0
- package/docs/images/omc-nixi/README.md +41 -41
- package/docs/omc-introduction.md +851 -851
- package/docs/tool-surface-map.md +42 -42
- package/package.json +7 -5
- package/skills/omc-acceptance-criteria.md +50 -50
- package/skills/omc-business-configuration.md +334 -334
- package/skills/omc-capabilities.md +214 -214
- package/skills/omc-capability-scouting.md +55 -55
- package/skills/omc-config-draft-review.md +175 -175
- package/skills/omc-five-piece-flow.md +35 -35
- package/skills/omc-glossary.md +86 -86
- package/skills/omc-refusals.md +127 -127
- package/skills/omc-requirement-analysis.md +161 -161
- package/skills/omc-requirement-vocabulary.md +61 -61
- package/skills/omc-start-here.md +106 -106
- package/skills/omc-tool-selection.md +119 -119
- package/skills/omc-write-hazards.md +200 -199
- package/dist/lanes/organization-coverage.js +0 -27
package/dist/approval-flow.js
CHANGED
|
File without changes
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { createHash, createHmac, randomUUID, timingSafeEqual } from "node:crypto";
|
|
2
|
+
export class ApprovalTokenRejectedError extends Error {
|
|
3
|
+
reason;
|
|
4
|
+
constructor(reason) {
|
|
5
|
+
super(`approval token rejected: ${reason}`);
|
|
6
|
+
this.reason = reason;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
function canonical(value) {
|
|
10
|
+
return JSON.stringify(sort(value));
|
|
11
|
+
}
|
|
12
|
+
function sort(value) {
|
|
13
|
+
if (Array.isArray(value))
|
|
14
|
+
return value.map(sort);
|
|
15
|
+
if (value && typeof value === "object")
|
|
16
|
+
return Object.fromEntries(Object.entries(value)
|
|
17
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
18
|
+
.map(([k, v]) => [k, sort(v)]));
|
|
19
|
+
return value;
|
|
20
|
+
}
|
|
21
|
+
export function packageFingerprint(plan) {
|
|
22
|
+
return createHash("sha256")
|
|
23
|
+
.update(canonical({
|
|
24
|
+
environment: plan.environment,
|
|
25
|
+
environmentFingerprint: plan.environmentFingerprint,
|
|
26
|
+
actions: plan.actions.map((a) => ({ tool: a.tool, args: sort(a.args) })),
|
|
27
|
+
}))
|
|
28
|
+
.digest("hex");
|
|
29
|
+
}
|
|
30
|
+
export class ApprovalTokenService {
|
|
31
|
+
key;
|
|
32
|
+
ttlMs;
|
|
33
|
+
now;
|
|
34
|
+
used = new Set();
|
|
35
|
+
constructor(options) {
|
|
36
|
+
const key = Buffer.from(options.key, "base64");
|
|
37
|
+
if (key.length !== 32)
|
|
38
|
+
throw new Error("approval token key must be exactly 32 bytes (base64).");
|
|
39
|
+
this.key = key;
|
|
40
|
+
this.ttlMs = options.ttlMs ?? 10 * 60 * 1000;
|
|
41
|
+
this.now = options.now ?? Date.now;
|
|
42
|
+
}
|
|
43
|
+
requestApproval(plan) {
|
|
44
|
+
const approvalId = randomUUID();
|
|
45
|
+
const fingerprint = packageFingerprint(plan);
|
|
46
|
+
const issuedAt = this.now();
|
|
47
|
+
const expiresAt = issuedAt + this.ttlMs;
|
|
48
|
+
const payload = canonical({
|
|
49
|
+
v: 1,
|
|
50
|
+
approvalId,
|
|
51
|
+
packageFingerprint: fingerprint,
|
|
52
|
+
issuedAt,
|
|
53
|
+
expiresAt,
|
|
54
|
+
});
|
|
55
|
+
const sig = createHmac("sha256", this.key).update(payload).digest("base64url");
|
|
56
|
+
const token = Buffer.from(payload, "utf8").toString("base64url") + "." + sig;
|
|
57
|
+
return {
|
|
58
|
+
token,
|
|
59
|
+
approvalId,
|
|
60
|
+
packageFingerprint: fingerprint,
|
|
61
|
+
expiresAt: new Date(expiresAt).toISOString(),
|
|
62
|
+
ttlSeconds: Math.round(this.ttlMs / 1000),
|
|
63
|
+
singleUse: true,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
verify(plan, token) {
|
|
67
|
+
const parts = token.split(".");
|
|
68
|
+
if (parts.length !== 2 || !parts[0] || !parts[1])
|
|
69
|
+
throw new ApprovalTokenRejectedError("token-malformed");
|
|
70
|
+
let payload;
|
|
71
|
+
try {
|
|
72
|
+
payload = Buffer.from(parts[0], "base64url").toString("utf8");
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
throw new ApprovalTokenRejectedError("token-malformed");
|
|
76
|
+
}
|
|
77
|
+
let decoded;
|
|
78
|
+
try {
|
|
79
|
+
decoded = JSON.parse(payload);
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
throw new ApprovalTokenRejectedError("token-malformed");
|
|
83
|
+
}
|
|
84
|
+
const expectedSig = createHmac("sha256", this.key).update(payload).digest("base64url");
|
|
85
|
+
if (!timingSafeEqualB64(parts[1], expectedSig))
|
|
86
|
+
throw new ApprovalTokenRejectedError("token-signature-invalid");
|
|
87
|
+
if (this.used.has(String(decoded.approvalId)))
|
|
88
|
+
throw new ApprovalTokenRejectedError("token-already-used");
|
|
89
|
+
if (Number(decoded.expiresAt) <= this.now())
|
|
90
|
+
throw new ApprovalTokenRejectedError("token-expired");
|
|
91
|
+
if (decoded.packageFingerprint !== packageFingerprint(plan))
|
|
92
|
+
throw new ApprovalTokenRejectedError("package-fingerprint-mismatch");
|
|
93
|
+
this.used.add(String(decoded.approvalId));
|
|
94
|
+
return { approvalId: String(decoded.approvalId) };
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function timingSafeEqualB64(a, b) {
|
|
98
|
+
const ba = Buffer.from(a);
|
|
99
|
+
const bb = Buffer.from(b);
|
|
100
|
+
return ba.length === bb.length && timingSafeEqual(ba, bb);
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=approval-token.js.map
|
package/dist/args.js
CHANGED
|
File without changes
|
package/dist/artifacts.js
CHANGED
|
File without changes
|
package/dist/batch-call.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -128,6 +128,8 @@ async function runScenario(scenario) {
|
|
|
128
128
|
};
|
|
129
129
|
}
|
|
130
130
|
export async function runDiscoveryCostBaseline() {
|
|
131
|
+
// r29(2026-10-29):describe 重复提示读项目 ledger,组合测试顺序会改变它——测量面禁用保证字节确定性。
|
|
132
|
+
process.env.OMC_DESC_REPEAT_HINT = "0";
|
|
131
133
|
const scenarios = [];
|
|
132
134
|
for (const scenario of SCENARIOS)
|
|
133
135
|
scenarios.push(await runScenario(scenario));
|
package/dist/bench/driver.js
CHANGED
|
File without changes
|
package/dist/bench/evals.js
CHANGED
|
File without changes
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { createServer } from "node:http";
|
|
2
|
+
import { FakeCloudPivot } from "../fake/index.js";
|
|
3
|
+
export async function startFakeHttpServer() {
|
|
4
|
+
const server = createServer();
|
|
5
|
+
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
|
|
6
|
+
const address = server.address();
|
|
7
|
+
if (address === null || typeof address === "string") {
|
|
8
|
+
server.closeAllConnections?.();
|
|
9
|
+
await new Promise((resolve) => server.close(() => resolve()));
|
|
10
|
+
throw new Error("fake http server has no listen address");
|
|
11
|
+
}
|
|
12
|
+
const url = `http://127.0.0.1:${address.port}`;
|
|
13
|
+
const fake = new FakeCloudPivot({ version: "8.6.26", baseUrl: url });
|
|
14
|
+
server.on("request", (request, response) => {
|
|
15
|
+
const chunks = [];
|
|
16
|
+
request.on("data", (chunk) => chunks.push(chunk));
|
|
17
|
+
request.on("end", () => {
|
|
18
|
+
const target = new URL(request.url ?? "/", url);
|
|
19
|
+
const query = {};
|
|
20
|
+
for (const [key, value] of target.searchParams)
|
|
21
|
+
query[key] = value;
|
|
22
|
+
const raw = Buffer.concat(chunks);
|
|
23
|
+
const contentType = request.headers["content-type"] ?? "";
|
|
24
|
+
const body = raw.length === 0 ? undefined : contentType.includes("json") ? JSON.parse(raw.toString("utf8")) : raw.toString("utf8");
|
|
25
|
+
const headers = {};
|
|
26
|
+
for (const [key, value] of Object.entries(request.headers)) {
|
|
27
|
+
if (typeof value === "string") {
|
|
28
|
+
const capitalized = key.replace(/\b\w/g, (c) => c.toUpperCase()).replace(/-(\w)/g, (_m, c) => `-${c.toUpperCase()}`);
|
|
29
|
+
headers[capitalized] = value;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
fake
|
|
33
|
+
.request({
|
|
34
|
+
path: target.pathname,
|
|
35
|
+
method: request.method === "POST" ? "POST" : "GET",
|
|
36
|
+
...(Object.keys(query).length > 0 ? { query } : {}),
|
|
37
|
+
...(body === undefined ? {} : { body }),
|
|
38
|
+
headers,
|
|
39
|
+
})
|
|
40
|
+
.then((result) => {
|
|
41
|
+
const payload = Buffer.isBuffer(result.body) ? result.body : JSON.stringify(result.body);
|
|
42
|
+
response.writeHead(result.status, Buffer.isBuffer(result.body) ? undefined : { "Content-Type": "application/json" });
|
|
43
|
+
response.end(payload);
|
|
44
|
+
})
|
|
45
|
+
.catch(() => {
|
|
46
|
+
response.writeHead(500, { "Content-Type": "application/json" });
|
|
47
|
+
response.end(JSON.stringify({ errcode: 500, errmsg: "fake transport failure", data: null }));
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
return {
|
|
52
|
+
fake,
|
|
53
|
+
url,
|
|
54
|
+
close: () => new Promise((resolve, reject) => {
|
|
55
|
+
server.closeAllConnections?.();
|
|
56
|
+
server.close((error) => (error ? reject(error) : resolve()));
|
|
57
|
+
}),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
export function canonicalBusinessCalls(fake) {
|
|
61
|
+
return JSON.stringify(fake.state.callLog
|
|
62
|
+
.filter((entry) => !entry.path.startsWith("/user/") && !entry.path.startsWith("/api/public/") && !entry.path.startsWith("/api/api/organization/"))
|
|
63
|
+
.map((entry) => ({ path: entry.path, method: entry.method, body: entry.body ?? null, query: entry.query && Object.keys(entry.query).length > 0 ? entry.query : null })));
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=fake-http-server.js.map
|
package/dist/bench/instrument.js
CHANGED
|
File without changes
|
|
@@ -122,24 +122,24 @@ function freshHome(url) {
|
|
|
122
122
|
writeFileSync(join(home, "credentials"), JSON.stringify({ eval: { username: "tester", password: "eval-face-password" } }));
|
|
123
123
|
return home;
|
|
124
124
|
}
|
|
125
|
-
const LADDER_INTENT = `intentVersion: 1
|
|
126
|
-
models:
|
|
127
|
-
- code: eval_m1
|
|
128
|
-
name: 评测建模
|
|
129
|
-
fields:
|
|
130
|
-
- code: f_title
|
|
131
|
-
name: 标题
|
|
132
|
-
type: SHORT_TEXT
|
|
133
|
-
- code: f_amount
|
|
134
|
-
name: 金额
|
|
135
|
-
type: NUMERICAL
|
|
136
|
-
options:
|
|
137
|
-
scale: 3
|
|
138
|
-
- code: f_when
|
|
139
|
-
name: 日期
|
|
140
|
-
type: DATE
|
|
141
|
-
form:
|
|
142
|
-
publish: true
|
|
125
|
+
const LADDER_INTENT = `intentVersion: 1
|
|
126
|
+
models:
|
|
127
|
+
- code: eval_m1
|
|
128
|
+
name: 评测建模
|
|
129
|
+
fields:
|
|
130
|
+
- code: f_title
|
|
131
|
+
name: 标题
|
|
132
|
+
type: SHORT_TEXT
|
|
133
|
+
- code: f_amount
|
|
134
|
+
name: 金额
|
|
135
|
+
type: NUMERICAL
|
|
136
|
+
options:
|
|
137
|
+
scale: 3
|
|
138
|
+
- code: f_when
|
|
139
|
+
name: 日期
|
|
140
|
+
type: DATE
|
|
141
|
+
form:
|
|
142
|
+
publish: true
|
|
143
143
|
`;
|
|
144
144
|
export const INTENT_FACE_TASKS = [
|
|
145
145
|
{
|
|
@@ -163,12 +163,12 @@ export const INTENT_FACE_TASKS = [
|
|
|
163
163
|
id: "rule-backfill-ladder",
|
|
164
164
|
description: "two models via intent + DYNAMIC backfill rule + enable + trigger row + verified target readback (mixed face: intent + escape hatch)",
|
|
165
165
|
async run(agent, server) {
|
|
166
|
-
const modelsIntent = agent.writeIntent("models.yaml", `intentVersion: 1
|
|
167
|
-
models:
|
|
168
|
-
- code: eval_rs
|
|
169
|
-
name: 回填源
|
|
170
|
-
- code: eval_rt
|
|
171
|
-
name: 回填目标
|
|
166
|
+
const modelsIntent = agent.writeIntent("models.yaml", `intentVersion: 1
|
|
167
|
+
models:
|
|
168
|
+
- code: eval_rs
|
|
169
|
+
name: 回填源
|
|
170
|
+
- code: eval_rt
|
|
171
|
+
name: 回填目标
|
|
172
172
|
`);
|
|
173
173
|
await agent.intentValidate(modelsIntent);
|
|
174
174
|
const { plan, planFile } = await agent.intentPlan(modelsIntent);
|
|
@@ -209,27 +209,27 @@ models:
|
|
|
209
209
|
id: "input-error-recovery",
|
|
210
210
|
description: "bad intent -> validate exit 10 with source location -> fix -> plan/apply -> idempotent re-plan/re-apply with zero duplicate wires",
|
|
211
211
|
async run(agent, server) {
|
|
212
|
-
const bad = agent.writeIntent("intent.yaml", `intentVersion: 1
|
|
213
|
-
models:
|
|
214
|
-
- code: eval_idem
|
|
215
|
-
name: 幂等模型
|
|
216
|
-
fields:
|
|
217
|
-
- code: f_bad
|
|
218
|
-
name: 坏类型
|
|
219
|
-
type: HOLOGRAM
|
|
212
|
+
const bad = agent.writeIntent("intent.yaml", `intentVersion: 1
|
|
213
|
+
models:
|
|
214
|
+
- code: eval_idem
|
|
215
|
+
name: 幂等模型
|
|
216
|
+
fields:
|
|
217
|
+
- code: f_bad
|
|
218
|
+
name: 坏类型
|
|
219
|
+
type: HOLOGRAM
|
|
220
220
|
`);
|
|
221
221
|
const rejected = await agent.run(["intent", "validate", bad]);
|
|
222
222
|
ok(rejected.code === 10 && rejected.envelope.error?.code === "invalid-input", `expected invalid-input exit 10, got ${String(rejected.code)}`);
|
|
223
223
|
const first = rejected.envelope.result.errors[0];
|
|
224
224
|
ok(first.code === "intent-field-type-unknown" && typeof first.line === "number", `located error: ${JSON.stringify(first).slice(0, 200)}`);
|
|
225
|
-
const fixed = agent.writeIntent("intent.yaml", `intentVersion: 1
|
|
226
|
-
models:
|
|
227
|
-
- code: eval_idem
|
|
228
|
-
name: 幂等模型
|
|
229
|
-
fields:
|
|
230
|
-
- code: f_bad
|
|
231
|
-
name: 改正的字段
|
|
232
|
-
type: SHORT_TEXT
|
|
225
|
+
const fixed = agent.writeIntent("intent.yaml", `intentVersion: 1
|
|
226
|
+
models:
|
|
227
|
+
- code: eval_idem
|
|
228
|
+
name: 幂等模型
|
|
229
|
+
fields:
|
|
230
|
+
- code: f_bad
|
|
231
|
+
name: 改正的字段
|
|
232
|
+
type: SHORT_TEXT
|
|
233
233
|
`);
|
|
234
234
|
const validated = await agent.intentValidate(fixed);
|
|
235
235
|
ok(validated.valid === true, `fixed validate: ${JSON.stringify(validated).slice(0, 200)}`);
|
package/dist/bench/run.js
CHANGED
|
File without changes
|
package/dist/bench/scenario.js
CHANGED
|
File without changes
|
package/dist/bench/types.js
CHANGED
|
File without changes
|
package/dist/bi-wire.js
CHANGED
|
File without changes
|
|
File without changes
|
package/dist/call.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/dist/capability-shape.js
CHANGED
|
File without changes
|
package/dist/cli-contract.js
CHANGED
|
@@ -39,15 +39,52 @@ export function asciiStdout() {
|
|
|
39
39
|
return true;
|
|
40
40
|
if (override === "0")
|
|
41
41
|
return false;
|
|
42
|
+
// Do not spawn chcp on POSIX: it costs a process per invocation and can only
|
|
43
|
+
// ever answer "not a Windows code page here".
|
|
44
|
+
if (process.platform !== "win32")
|
|
45
|
+
return false;
|
|
42
46
|
return asciiStdoutFor(process.platform, process.stdout.isTTY === true, activeCodePage());
|
|
43
47
|
}
|
|
48
|
+
/** Escape every non-ASCII code unit to `\uXXXX` (idempotent, JSON-safe). */
|
|
49
|
+
export function escapeNonAscii(text) {
|
|
50
|
+
return text.replace(/[\u007f-\uffff]/g, (character) => `\\u${character.charCodeAt(0).toString(16).padStart(4, "0")}`);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Apply the SAME code-page policy to stderr diagnostics.
|
|
54
|
+
*
|
|
55
|
+
* stdout was made ASCII-safe years ago, but every human/diagnostic line goes
|
|
56
|
+
* through `console.error`/`process.stderr.write` as raw UTF-8. Under a legacy
|
|
57
|
+
* Windows pipe (`omc … 2>&1 | tee log`, `npm test`, an agent capturing output)
|
|
58
|
+
* that is cp936 vs UTF-8 — the user sees `����` instead of the message. Escaping
|
|
59
|
+
* the same way as the envelope keeps diagnostics decodable, and a real console
|
|
60
|
+
* (TTY) is untouched because Node writes UTF-16 through WriteConsoleW there.
|
|
61
|
+
*
|
|
62
|
+
* Gated on `asciiStdout()` so `OMC_ASCII_STDOUT=0` (what the test suite sets)
|
|
63
|
+
* still yields raw UTF-8 for assertions that match Chinese text.
|
|
64
|
+
*/
|
|
65
|
+
let stderrHardened = false;
|
|
66
|
+
export function installCodePageSafeStderr() {
|
|
67
|
+
if (stderrHardened)
|
|
68
|
+
return;
|
|
69
|
+
stderrHardened = true;
|
|
70
|
+
if (!asciiStdout())
|
|
71
|
+
return;
|
|
72
|
+
const original = process.stderr.write.bind(process.stderr);
|
|
73
|
+
process.stderr.write = ((chunk, encodingOrCb, maybeCb) => {
|
|
74
|
+
if (typeof chunk !== "string")
|
|
75
|
+
return original(chunk, encodingOrCb, maybeCb);
|
|
76
|
+
return original(escapeNonAscii(chunk), encodingOrCb, maybeCb);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
44
79
|
let cachedCodePage;
|
|
45
80
|
function activeCodePage() {
|
|
46
81
|
if (cachedCodePage !== undefined)
|
|
47
82
|
return cachedCodePage;
|
|
48
83
|
cachedCodePage = null;
|
|
49
84
|
try {
|
|
50
|
-
|
|
85
|
+
// windowsHide: a GUI host (editor task, shortcut) must not flash a console
|
|
86
|
+
// window for a probe; timeout: never hang a one-shot CLI on chcp.
|
|
87
|
+
const chcp = spawnSync("chcp.com", [], { encoding: "utf8", windowsHide: true, timeout: 2000 });
|
|
51
88
|
cachedCodePage = /([0-9]{3,5})\s*$/.exec((chcp.stdout || "").trim().split("\n").pop() ?? "")?.[1] ?? null;
|
|
52
89
|
}
|
|
53
90
|
catch {
|
package/dist/cli-output.js
CHANGED
|
@@ -82,8 +82,14 @@ function emitAsciiBannerOnce() {
|
|
|
82
82
|
return;
|
|
83
83
|
asciiBannerEmitted = true;
|
|
84
84
|
// r73 win32 实测:管道 + 非 UTF-8 代码页时输出被 ASCII 转义(\uXXXX),用户视为乱码。
|
|
85
|
-
// 横幅走 stderr
|
|
86
|
-
|
|
85
|
+
// 横幅走 stderr(不进管道、不破坏单信封契约),在痛点时刻给出解法。
|
|
86
|
+
// 只有「自动判定」成 ascii 时才提示:调用方显式设了 OMC_ASCII_STDOUT/--utf8
|
|
87
|
+
// 说明已知情,再提示就是每次调用多两行噪音。
|
|
88
|
+
// 注意:ascii 模式下 stderr 本身也会被转义,所以横幅必须是纯 ASCII。
|
|
89
|
+
if (process.env.OMC_ASCII_STDOUT !== undefined)
|
|
90
|
+
return;
|
|
91
|
+
process.stderr.write("[omc] non-UTF-8 console code page: non-ASCII is \\u-escaped to survive the pipe. "
|
|
92
|
+
+ "Raw text: add --utf8, or `chcp 65001`, or set OMC_ASCII_STDOUT=0.\n");
|
|
87
93
|
}
|
|
88
94
|
/**
|
|
89
95
|
* Attach a repeat warning to a refusal that repeats the previous one. Refusal
|
package/dist/cli.js
CHANGED
|
@@ -15,10 +15,10 @@ import { BATCH_MAX_ENTRIES, runBatchCall } from "./batch-call.js";
|
|
|
15
15
|
import { cmdModelScaffold } from "./model-scaffold.js";
|
|
16
16
|
import { cmdModelPurge } from "./model-purge.js";
|
|
17
17
|
import { environmentWritable, requiresProductionConfirmation } from "./environment-policy.js";
|
|
18
|
-
import { CLI_EXIT_CODES, classifyResult, envelope, errorEnvelope, refusalText, requestId } from "./cli-contract.js";
|
|
18
|
+
import { CLI_EXIT_CODES, classifyResult, envelope, errorEnvelope, installCodePageSafeStderr, refusalText, requestId } from "./cli-contract.js";
|
|
19
19
|
import { configureOutput, humanSummary, stripOutputFlags, writeEnvelope } from "./cli-output.js";
|
|
20
|
-
import { beginUsage, completeUsage, ensureProjectIgnore, recordUsageOutput, usageReport } from "./usage-log.js";
|
|
21
|
-
import { artifactFetchHint, artifactRoot, boundResult, emitInvalid, emitResult, emitTransportResult, outputBudget, readArtifact, withTransport, } from "./command-transport.js";
|
|
20
|
+
import { beginUsage, completeUsage, currentUsageArgsDigest, ensureProjectIgnore, priorIdenticalSuccess, recordUsageOutput, usageReport } from "./usage-log.js";
|
|
21
|
+
import { artifactFetchHint, artifactRoot, boundResult, emitInvalid, emitResult, emitThrown, emitTransportResult, outputBudget, readArtifact, withTransport, } from "./command-transport.js";
|
|
22
22
|
import { CLI_DEFAULT_OUTPUT_BYTES, assertSafeOutputPath, pruneArtifacts } from "./artifacts.js";
|
|
23
23
|
import { projectJsonPath, projectJsonPathFanout, projectResultExtract } from "./path-projection.js";
|
|
24
24
|
import { basename, join, resolve } from "node:path";
|
|
@@ -44,8 +44,11 @@ import { planIntent, readbackIntent, applyIntent, intentWarnings } from "./inten
|
|
|
44
44
|
import { listSkills, readSkill, readSkillOutline, readSkillSection, searchSkills } from "./skills.js";
|
|
45
45
|
import { parseToolInput, ToolInputError } from "./tool-types.js";
|
|
46
46
|
import { WireError, wireCall } from "./wire.js";
|
|
47
|
+
import { DEFAULT_PULL_CONCURRENCY, GraphIndexAbsentError, GraphPullLockHeldError, MAX_PULL_CONCURRENCY, impactGraph, publishGraphPull, queryGraphReferences, readGraphStatus, searchGraphRules, } from "./graph.js";
|
|
47
48
|
import { ENDPOINTS } from "./endpoints.js";
|
|
49
|
+
import { makeToolContext } from "./context.js";
|
|
48
50
|
import { cmdRollbackList, cmdRollbackPlan, cmdRollbackApply } from "./rollback.js";
|
|
51
|
+
import { SessionManager } from "./session-manager.js";
|
|
49
52
|
async function ask(question) {
|
|
50
53
|
const rl = createInterface({ input: stdin, output: process.stderr });
|
|
51
54
|
try {
|
|
@@ -171,6 +174,11 @@ async function cmdDoctor(store, environment, serviceCode) {
|
|
|
171
174
|
const session = new CloudPivotSession(resolved.name, resolved.profile, credential);
|
|
172
175
|
await session.login();
|
|
173
176
|
steps.push(`登录 ok 企业=${session.enterpriseId()} 管理员=${session.administrator()}`);
|
|
177
|
+
// A deployment whose system/config `client` blob is absent/unreadable still
|
|
178
|
+
// logs in (client_secret is optional on 8.x). Say so as a WARN: it used to
|
|
179
|
+
// abort the login and report env-unreachable on a perfectly usable box.
|
|
180
|
+
for (const note of session.loginNotes())
|
|
181
|
+
steps.push(`OAuth 配置 WARN ${note}`);
|
|
174
182
|
const version = session.detectedVersion();
|
|
175
183
|
if (!version) {
|
|
176
184
|
steps.push(`版本 WARN 无版本证据;写入将保守拒绝(fail closed)`);
|
|
@@ -879,8 +887,20 @@ function cmdDiscovery(kind, transport, value, options = {}) {
|
|
|
879
887
|
// second identical read returns an artifact reference instead of the body.
|
|
880
888
|
result = full ? (repeatFullDescribe(value ?? "", full) ?? full) : full;
|
|
881
889
|
}
|
|
882
|
-
else
|
|
890
|
+
else {
|
|
883
891
|
result = describeTool(registry, value ?? "");
|
|
892
|
+
// r28b 账本(2026-10-28):describe rule.save×11 / form.publish×9——firstCall 教学后
|
|
893
|
+
// 仍反复全量 describe。同参数近期已 ok 时附降载提示,引导 --param/--example 精准取。
|
|
894
|
+
// 无项目 ledger(golden/repo 测试/一次性运行)时 priorIdenticalSuccess 返回
|
|
895
|
+
// undefined,输出保持 byte-stable。
|
|
896
|
+
if (result) {
|
|
897
|
+
const digest = currentUsageArgsDigest();
|
|
898
|
+
const prior = digest === undefined ? undefined : priorIdenticalSuccess(`describe ${String(value ?? "")}`, digest);
|
|
899
|
+
if (prior !== undefined && prior.count >= 1)
|
|
900
|
+
result.describeRepeatHint =
|
|
901
|
+
`同参数 describe 近期已执行过 ${prior.count + 1} 次(含本次)。在找单个参数:omc describe ${String(value ?? "")} --param <参数名>(几百字节);找可抄的完整例子:--example <配方id>;都不必重读整面`;
|
|
902
|
+
}
|
|
903
|
+
}
|
|
884
904
|
if (!result) {
|
|
885
905
|
// Name the closest candidates so a near-miss tool name does not cost a
|
|
886
906
|
// guess-and-retry call (round-4 dogfood: `form.create` / `data.create` /
|
|
@@ -1029,6 +1049,106 @@ async function cmdIntentReadback(file, environment, transport) {
|
|
|
1029
1049
|
return read.code;
|
|
1030
1050
|
return emitResult(await readbackIntent(read.intent, environment), transport);
|
|
1031
1051
|
}
|
|
1052
|
+
/**
|
|
1053
|
+
* ADR-0008 — `omc graph` family: the local derived rule-graph index. A CLI
|
|
1054
|
+
* management face like `intent`/`rollback`: NOT a call tool, not in the tool
|
|
1055
|
+
* registry or `commands --json`. Results are candidate pointers only — plan /
|
|
1056
|
+
* apply / readback keep reading the live baseline.
|
|
1057
|
+
*/
|
|
1058
|
+
async function cmdGraphPull(environment, concurrency, transport) {
|
|
1059
|
+
const workers = concurrency ?? DEFAULT_PULL_CONCURRENCY;
|
|
1060
|
+
if (!Number.isSafeInteger(workers) || workers < 1 || workers > MAX_PULL_CONCURRENCY)
|
|
1061
|
+
return emitInvalid(transport, { message: `--concurrency 必须是 1–${MAX_PULL_CONCURRENCY} 的整数(平台负载有界;默认 ${DEFAULT_PULL_CONCURRENCY})` });
|
|
1062
|
+
const context = makeToolContext({ sessions: new SessionManager(new ConfigStore(defaultPaths())) });
|
|
1063
|
+
try {
|
|
1064
|
+
const session = await context.sessions.session(environment);
|
|
1065
|
+
const data = await publishGraphPull(session, workers);
|
|
1066
|
+
const edgesByKind = {};
|
|
1067
|
+
for (const edge of data.edges)
|
|
1068
|
+
edgesByKind[edge.kind] = (edgesByKind[edge.kind] ?? 0) + 1;
|
|
1069
|
+
const gapsByKind = {};
|
|
1070
|
+
for (const gap of data.gaps)
|
|
1071
|
+
gapsByKind[gap.kind] = (gapsByKind[gap.kind] ?? 0) + 1;
|
|
1072
|
+
return emitResult({
|
|
1073
|
+
status: "ok",
|
|
1074
|
+
environment: data.meta.environmentName,
|
|
1075
|
+
applicationCode: data.meta.applicationCode,
|
|
1076
|
+
identityFingerprint: data.meta.identityFingerprint,
|
|
1077
|
+
pulledAtIso: data.meta.pulledAtIso,
|
|
1078
|
+
scope: data.meta.scope,
|
|
1079
|
+
freshness: data.gaps.length === 0 ? "complete" : "partial",
|
|
1080
|
+
scanned: { models: data.scanned.models, rules: data.scanned.rules, datarules: data.scanned.datarules, views: data.scanned.views, ...(data.scanned.parseFailures > 0 ? { parseFailures: data.scanned.parseFailures } : {}) },
|
|
1081
|
+
edges: edgesByKind,
|
|
1082
|
+
...(data.gaps.length > 0 ? { gaps: { count: data.gaps.length, byKind: gapsByKind } } : {}),
|
|
1083
|
+
dbBytes: data.dbBytes,
|
|
1084
|
+
timings: data.timings,
|
|
1085
|
+
nextCommand: "omc graph references <kind> <code>",
|
|
1086
|
+
}, transport);
|
|
1087
|
+
}
|
|
1088
|
+
catch (error) {
|
|
1089
|
+
if (error instanceof GraphPullLockHeldError)
|
|
1090
|
+
return emitResult({ status: "refused", blocker: "graph-pull-lock-held", message: error.message, nextCommand: "omc graph status" }, transport);
|
|
1091
|
+
if (error instanceof WireError)
|
|
1092
|
+
return emitResult({ status: "uncertain", reason: "wire-transport", message: `graph pull 中断(${error.message});索引未发布,重跑 omc graph pull --env <env>` }, transport);
|
|
1093
|
+
return emitThrown(error, transport);
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
function cmdGraphReferences(kind, code, transport) {
|
|
1097
|
+
if (!["service", "model", "field"].includes(kind))
|
|
1098
|
+
return emitInvalid(transport, { message: "kind 必须是 service / model / field(同 rule.references 第一版边界)" });
|
|
1099
|
+
try {
|
|
1100
|
+
const result = queryGraphReferences(kind, code);
|
|
1101
|
+
return emitResult({
|
|
1102
|
+
status: "ok",
|
|
1103
|
+
kind: result.kind,
|
|
1104
|
+
code: result.code,
|
|
1105
|
+
matches: result.matches,
|
|
1106
|
+
index: result.index,
|
|
1107
|
+
...(result.note !== undefined ? { note: result.note } : {}),
|
|
1108
|
+
...(result.gapWarning !== undefined ? { gapWarning: result.gapWarning } : {}),
|
|
1109
|
+
}, transport);
|
|
1110
|
+
}
|
|
1111
|
+
catch (error) {
|
|
1112
|
+
if (error instanceof GraphIndexAbsentError)
|
|
1113
|
+
return emitResult({ status: "refused", blocker: "graph-index-absent", message: error.message, nextCommand: "omc graph pull --env <env>" }, transport);
|
|
1114
|
+
return emitThrown(error, transport);
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
function cmdGraphStatus(transport) {
|
|
1118
|
+
try {
|
|
1119
|
+
const status = readGraphStatus();
|
|
1120
|
+
return emitResult({ status: "ok", index: status.index, dbBytes: status.dbBytes, gaps: status.gaps }, transport);
|
|
1121
|
+
}
|
|
1122
|
+
catch (error) {
|
|
1123
|
+
if (error instanceof GraphIndexAbsentError)
|
|
1124
|
+
return emitResult({ status: "refused", blocker: "graph-index-absent", message: error.message, nextCommand: "omc graph pull --env <env>" }, transport);
|
|
1125
|
+
return emitThrown(error, transport);
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
function cmdGraphImpact(kind, code, transport) {
|
|
1129
|
+
if (!["service", "model", "field"].includes(kind))
|
|
1130
|
+
return emitInvalid(transport, { message: "kind 必须是 service / model / field(同 rule.references 第一版边界)" });
|
|
1131
|
+
try {
|
|
1132
|
+
const result = impactGraph(kind, code);
|
|
1133
|
+
return emitResult({ status: "ok", ...result }, transport);
|
|
1134
|
+
}
|
|
1135
|
+
catch (error) {
|
|
1136
|
+
if (error instanceof GraphIndexAbsentError)
|
|
1137
|
+
return emitResult({ status: "refused", blocker: "graph-index-absent", message: error.message, nextCommand: "omc graph pull --env <env>" }, transport);
|
|
1138
|
+
return emitThrown(error, transport);
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
function cmdGraphSearch(term, transport) {
|
|
1142
|
+
try {
|
|
1143
|
+
const result = searchGraphRules(term);
|
|
1144
|
+
return emitResult({ status: "ok", ...result }, transport);
|
|
1145
|
+
}
|
|
1146
|
+
catch (error) {
|
|
1147
|
+
if (error instanceof GraphIndexAbsentError)
|
|
1148
|
+
return emitResult({ status: "refused", blocker: "graph-index-absent", message: error.message, nextCommand: "omc graph pull --env <env>" }, transport);
|
|
1149
|
+
return emitThrown(error, transport);
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1032
1152
|
/** Ticket 119 — `omc intent apply <plan-file>`: plan integrity + baseline drift gates, then serial execution through the EXISTING apply_changes machinery. */
|
|
1033
1153
|
async function cmdIntentApply(planPath, options, transport) {
|
|
1034
1154
|
return applyIntent(planPath, options, {
|
|
@@ -1341,6 +1461,9 @@ function joinSplitFlagValues(raw, flags) {
|
|
|
1341
1461
|
}
|
|
1342
1462
|
async function main() {
|
|
1343
1463
|
let commandExitCode = 0;
|
|
1464
|
+
// stderr is the diagnostics stream: apply the same Windows code-page policy
|
|
1465
|
+
// as stdout before ANY message is written (banner, doctor, refusals).
|
|
1466
|
+
installCodePageSafeStderr();
|
|
1344
1467
|
beginUsage(argv);
|
|
1345
1468
|
const command = new Command();
|
|
1346
1469
|
const ownPackageVersion = () => {
|
|
@@ -1714,6 +1837,25 @@ async function main() {
|
|
|
1714
1837
|
intentCommand.command("readback <file>").option("--env <environment>").option("--max-output-bytes <bytes>").action(async (file, options) => {
|
|
1715
1838
|
commandExitCode = await withTransport(options, request, (transport) => cmdIntentReadback(file, options.env, transport));
|
|
1716
1839
|
});
|
|
1840
|
+
// ADR-0008: local derived rule-graph index — CLI management face, not a call
|
|
1841
|
+
// tool (so it stays out of `commands --json`/coverage by construction).
|
|
1842
|
+
const graphCommand = command.command("graph").description("本地派生规则图索引(ADR-0008):pull 全量重建(有界并发)/ references 离线引用 / impact 影响面 / search 规则全文 / status 完整性与新鲜度。候选定位器——不进 baseline/plan/写门禁");
|
|
1843
|
+
graphCommand.command("pull").option("--env <environment>").option("--environment <environment>").option("--concurrency <n>", "并发 wire 读数(1–32,默认 16):拉取已流水线化,墙钟 ≈ max(模型面,规则面)/并发 × 延迟;过高可能触发平台限流形成缺口").option("--max-output-bytes <bytes>").addHelpText("after", "\n只读远端(model.list → 每模型 rule.list+datarule.list → 每规则 rule.get,统一任务池流水线并发)+ 本地原子重建;不写环境、不占环境写锁。新鲜度随结果回报(complete|partial),查询走 graph references / impact / search。\n").action(async (options) => {
|
|
1844
|
+
const parsed = options.concurrency === undefined ? undefined : Number.parseInt(String(options.concurrency), 10);
|
|
1845
|
+
commandExitCode = await withTransport(options, request, (transport) => cmdGraphPull(options.environment ?? options.env, parsed, transport));
|
|
1846
|
+
});
|
|
1847
|
+
graphCommand.command("references <kind> <code>").option("--max-output-bytes <bytes>").addHelpText("after", "\n离线查询(零 wire):kind = service|model|field,语义同 rule.references 第一版边界;结果带索引新鲜度与扫描缺口(有缺口时零命中不构成「无引用」证明)。\n").action(async (kind, code, options) => {
|
|
1848
|
+
commandExitCode = await withTransport(options, request, (transport) => cmdGraphReferences(kind, code, transport));
|
|
1849
|
+
});
|
|
1850
|
+
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) => {
|
|
1851
|
+
commandExitCode = await withTransport(options, request, (transport) => cmdGraphImpact(kind, code, transport));
|
|
1852
|
+
});
|
|
1853
|
+
graphCommand.command("search <term...>").option("--max-output-bytes <bytes>").addHelpText("after", "\n规则全文检索(code+name,FTS5,末词前缀匹配):找到\"关于 X 的规则\"拿 code,再走 references/impact。中文需给全词或词首(如 报名*)。\n").action(async (term, options) => {
|
|
1854
|
+
commandExitCode = await withTransport(options, request, (transport) => cmdGraphSearch(term.join(" "), transport));
|
|
1855
|
+
});
|
|
1856
|
+
graphCommand.command("status").option("--max-output-bytes <bytes>").action(async (options) => {
|
|
1857
|
+
commandExitCode = await withTransport(options, request, (transport) => cmdGraphStatus(transport));
|
|
1858
|
+
});
|
|
1717
1859
|
// `--jq` expressions the shell split arrive as several tokens; re-join them.
|
|
1718
1860
|
await command.parseAsync(joinSplitFlagValues(stripOutputFlags(argv), ["--jq"]));
|
|
1719
1861
|
return commandExitCode;
|
|
@@ -1742,7 +1884,7 @@ main().then((code) => {
|
|
|
1742
1884
|
// envelope (or, for `env --json`, their own JSON) and were printing a second
|
|
1743
1885
|
// empty envelope after it (live 2026-10-23: `omc rollback apply` answered two
|
|
1744
1886
|
// lines, violating the one-envelope stdout contract).
|
|
1745
|
-
const ownsStdout = ["call", "script", "artifact", "commands", "describe", "contract", "capabilities", "capability", "next", "usage", "intent", "help", "form-default", "compute", "rule-when", "approval-flow", "model-scaffold", "model-purge", "rollback", "env", "--version", "-V"];
|
|
1887
|
+
const ownsStdout = ["call", "script", "artifact", "commands", "describe", "contract", "capabilities", "capability", "next", "usage", "intent", "help", "form-default", "compute", "rule-when", "approval-flow", "model-scaffold", "model-purge", "rollback", "env", "graph", "--version", "-V"];
|
|
1746
1888
|
if (ownsStdout.includes(argv[2] ?? "")) {
|
|
1747
1889
|
completeUsage(code);
|
|
1748
1890
|
finish(code);
|
package/dist/closest-match.js
CHANGED
|
File without changes
|
package/dist/code-rules.js
CHANGED
|
File without changes
|
|
File without changes
|
package/dist/compound-writes.js
CHANGED
|
File without changes
|
package/dist/compute.js
CHANGED
|
File without changes
|
package/dist/config-store.js
CHANGED
|
File without changes
|
package/dist/context.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/dist/coverage.js
CHANGED
|
File without changes
|
package/dist/data-list-filter.js
CHANGED
|
File without changes
|
package/dist/describe-cache.js
CHANGED
|
File without changes
|