@stalfh233/omc-cli 0.5.0 → 0.5.2
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 +0 -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 +9 -1
- 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 +75 -7
- 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 +29 -29
- 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 +0 -0
- 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 +14 -29
- 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 +2 -2
- package/dist/model-record.js +40 -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 +0 -0
- 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 +5 -11
- 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 +4 -4
- 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 -200
- 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
|
|
File without changes
|
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,7 +15,7 @@ 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
20
|
import { beginUsage, completeUsage, currentUsageArgsDigest, ensureProjectIgnore, priorIdenticalSuccess, recordUsageOutput, usageReport } from "./usage-log.js";
|
|
21
21
|
import { artifactFetchHint, artifactRoot, boundResult, emitInvalid, emitResult, emitThrown, emitTransportResult, outputBudget, readArtifact, withTransport, } from "./command-transport.js";
|
|
@@ -174,6 +174,11 @@ async function cmdDoctor(store, environment, serviceCode) {
|
|
|
174
174
|
const session = new CloudPivotSession(resolved.name, resolved.profile, credential);
|
|
175
175
|
await session.login();
|
|
176
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}`);
|
|
177
182
|
const version = session.detectedVersion();
|
|
178
183
|
if (!version) {
|
|
179
184
|
steps.push(`版本 WARN 无版本证据;写入将保守拒绝(fail closed)`);
|
|
@@ -1456,6 +1461,9 @@ function joinSplitFlagValues(raw, flags) {
|
|
|
1456
1461
|
}
|
|
1457
1462
|
async function main() {
|
|
1458
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();
|
|
1459
1467
|
beginUsage(argv);
|
|
1460
1468
|
const command = new Command();
|
|
1461
1469
|
const ownPackageVersion = () => {
|
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
|
package/dist/discovery.js
CHANGED
|
File without changes
|
package/dist/endpoints.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/dist/fake/app.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { NOT_HANDLED } from "./state.js";
|
|
2
|
+
/** app-management lane: application package CRUD. Twin of src/lanes/app.ts. */
|
|
3
|
+
export const appRoute = (ctx, input) => {
|
|
4
|
+
const q = input.query ?? {};
|
|
5
|
+
const body = (input.body ?? {});
|
|
6
|
+
switch (input.path) {
|
|
7
|
+
case "/api/api/app/apppackage/list":
|
|
8
|
+
return ctx.options.applicationList ?? [...ctx.state.apps.values()].map((app) => ({ appCode: app.code, appName: app.name, id: app.id, groupId: app.groupId, published: app.published !== false }));
|
|
9
|
+
case "/api/api/app/apppackage/list_all_group":
|
|
10
|
+
return [...ctx.state.appGroups.values()].map((group) => ({
|
|
11
|
+
id: group.id,
|
|
12
|
+
code: group.code,
|
|
13
|
+
name: group.name,
|
|
14
|
+
children: [...ctx.state.apps.values()].filter((app) => app.groupId === group.id).map((app) => ({ id: app.id, code: app.code, name: app.name, groupId: app.groupId })),
|
|
15
|
+
}));
|
|
16
|
+
case "/api/api/app/apppackage/create": {
|
|
17
|
+
const code = String(body.code);
|
|
18
|
+
ctx.state.apps.set(code, { id: `app-${ctx.state.apps.size + 1}`, code, name: String(body.name ?? code), groupId: String(body.groupId ?? [...ctx.state.appGroups.keys()][0] ?? "group-1") });
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
case "/api/api/app/apppackage/get": {
|
|
22
|
+
const app = [...ctx.state.apps.values()].find((row) => row.code === q.appCode);
|
|
23
|
+
return app ? { id: app.id, code: app.code, name: app.name, name_i18n: JSON.stringify({ "zh-CN": app.name }), groupId: app.groupId, enabled: true } : null;
|
|
24
|
+
}
|
|
25
|
+
case "/api/api/app/apppackage/update": {
|
|
26
|
+
// AppIntegrationFacadeImpl.updateAppPackage: id-required whole-record
|
|
27
|
+
// update; explicit groupId honored; blank groupId would be forced to
|
|
28
|
+
// the "default" group; enabled !== true wipes workflow favorites.
|
|
29
|
+
const target = [...ctx.state.apps.values()].find((row) => row.id === body.id);
|
|
30
|
+
if (target) {
|
|
31
|
+
if (typeof body.groupId === "string" && body.groupId)
|
|
32
|
+
target.groupId = body.groupId;
|
|
33
|
+
if (typeof body.name === "string" && body.name)
|
|
34
|
+
target.name = body.name;
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
case "/api/api/app/apppackage/delete":
|
|
39
|
+
ctx.state.apps.delete(String(q.appCode));
|
|
40
|
+
return null;
|
|
41
|
+
default:
|
|
42
|
+
return NOT_HANDLED;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
//# sourceMappingURL=app.js.map
|