@stalfh233/omc-cli 0.4.67 → 0.4.68
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-output.js +32 -2
- package/dist/lanes/workflow.js +8 -0
- package/dist/usage-log.js +75 -1
- package/package.json +3 -3
package/dist/cli-output.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { writeFileSync } from "node:fs";
|
|
2
2
|
import { asciiStdout, serializeEnvelope } from "./cli-contract.js";
|
|
3
|
-
import { recordUsageOutput } from "./usage-log.js";
|
|
3
|
+
import { currentUsageCommand, priorRepeatedFailures, recordUsageOutput } from "./usage-log.js";
|
|
4
4
|
let options = { quiet: false, verbose: false };
|
|
5
5
|
/** Parse `--quiet`, `--verbose` and `--out <file>` from the raw argv once, before dispatch. */
|
|
6
6
|
export function configureOutput(argv) {
|
|
@@ -85,8 +85,38 @@ function emitAsciiBannerOnce() {
|
|
|
85
85
|
// 横幅走 stderr(不进管道、不破坏单信封契约),在痛点时刻给出两个解法。
|
|
86
86
|
process.stderr.write("[omc] 当前控制台代码页非 UTF-8,中文已 ASCII 转义防乱码。要原样中文:本次调用加 --utf8,或 chcp 65001 后重跑,或设 OMC_ASCII_STDOUT=0。\n");
|
|
87
87
|
}
|
|
88
|
+
/**
|
|
89
|
+
* Attach a repeat warning to a refusal that repeats the previous one. Refusal
|
|
90
|
+
* envelopes already say what is wrong; a third identical failure means the
|
|
91
|
+
* caller is tuning arguments inside a dead end (r6-onboarding 2026-10-21: 16
|
|
92
|
+
* repeat runs, 27 avoidable calls), so say so where it will be read.
|
|
93
|
+
*/
|
|
94
|
+
function withRepeatWarning(value) {
|
|
95
|
+
if (!value || typeof value !== "object")
|
|
96
|
+
return value;
|
|
97
|
+
const envelope = value;
|
|
98
|
+
const status = String(envelope.status ?? "");
|
|
99
|
+
if (status !== "refused" && status !== "error" && status !== "uncertain")
|
|
100
|
+
return value;
|
|
101
|
+
const result = envelope.result && typeof envelope.result === "object" ? envelope.result : undefined;
|
|
102
|
+
const blocker = typeof result?.blocker === "string" ? result.blocker : typeof envelope.blocker === "string" ? envelope.blocker : undefined;
|
|
103
|
+
const prior = priorRepeatedFailures(currentUsageCommand(), blocker);
|
|
104
|
+
if (prior.count < 2)
|
|
105
|
+
return value;
|
|
106
|
+
const repeatWarning = {
|
|
107
|
+
sameFailureCount: prior.count + 1,
|
|
108
|
+
blocker: blocker ?? null,
|
|
109
|
+
...(prior.requestIds.length > 0 ? { previousRequestIds: prior.requestIds } : {}),
|
|
110
|
+
hint: status === "uncertain"
|
|
111
|
+
? `同一调用已连续返回 uncertain ${prior.count + 1} 次。uncertain 表示结果未确定:先按 requestId 回读真实状态再决定,不要盲目重发写请求。`
|
|
112
|
+
: `同一调用已连续失败 ${prior.count + 1} 次(blocker ${blocker ?? "unknown"})。不要再微调参数重试:换形状或换工具——先 describe/配方确认契约,或按 blocker 指向的替代路径处理。`,
|
|
113
|
+
};
|
|
114
|
+
if (result)
|
|
115
|
+
return { ...envelope, result: { ...result, repeatWarning } };
|
|
116
|
+
return { ...envelope, repeatWarning };
|
|
117
|
+
}
|
|
88
118
|
export function writeEnvelope(value) {
|
|
89
|
-
const outputValue = compactForOutput(value);
|
|
119
|
+
const outputValue = compactForOutput(withRepeatWarning(value));
|
|
90
120
|
const text = serializeEnvelope(outputValue);
|
|
91
121
|
if (asciiStdout())
|
|
92
122
|
emitAsciiBannerOnce();
|
package/dist/lanes/workflow.js
CHANGED
|
@@ -2226,10 +2226,18 @@ export function registerWorkflowLane(tools) {
|
|
|
2226
2226
|
schemaCode: linked?.schemaCode ?? wireString(item.schemaCode),
|
|
2227
2227
|
};
|
|
2228
2228
|
});
|
|
2229
|
+
// r6-onboarding 2026-10-21: 89 of 117 calls used this tool with only a
|
|
2230
|
+
// schemaCode — mostly to ask "where did the flow go", which the todo query
|
|
2231
|
+
// cannot answer (it lists pending items only). Say what this call is for
|
|
2232
|
+
// at the moment it is used, and name the two substitutes.
|
|
2233
|
+
const purposeHint = args.workflowInstanceId === undefined && args.workflowCode === undefined
|
|
2234
|
+
? "本调用用于发现待办并取 workItemId 做审批;只想看流程走到哪,用 workflow.instanceActivity;提交/审批后优先复用 data.submit 的 readback.nextSubmit,不要重复查询。"
|
|
2235
|
+
: undefined;
|
|
2229
2236
|
return {
|
|
2230
2237
|
total: page.total,
|
|
2231
2238
|
totalPages: page.totalPages,
|
|
2232
2239
|
count: items.length,
|
|
2240
|
+
...(purposeHint ? { purposeHint } : {}),
|
|
2233
2241
|
...(page.unfilteredTotal !== undefined ? { unfilteredTotal: page.unfilteredTotal } : {}),
|
|
2234
2242
|
items,
|
|
2235
2243
|
};
|
package/dist/usage-log.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { appendFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
1
|
+
import { appendFileSync, existsSync, mkdirSync, openSync, readFileSync, readSync, closeSync, statSync } from "node:fs";
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
3
|
import { dirname, resolve } from "node:path";
|
|
4
4
|
let pending;
|
|
@@ -9,6 +9,47 @@ function logPath() {
|
|
|
9
9
|
const projectEnv = resolve(process.cwd(), ".omc-env");
|
|
10
10
|
return existsSync(projectEnv) ? resolve(process.cwd(), ".omc", "usage.jsonl") : undefined;
|
|
11
11
|
}
|
|
12
|
+
/** The command string of the in-flight invocation (for diagnostics). */
|
|
13
|
+
export function currentUsageCommand() {
|
|
14
|
+
return pending?.command;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* How many consecutive PRIOR invocations failed the same way. A refusal tells
|
|
18
|
+
* the caller what is wrong; three identical refusals in a row mean the caller is
|
|
19
|
+
* tuning arguments inside a dead end instead of changing approach (r6-onboarding
|
|
20
|
+
* 2026-10-21: 16 such runs, 43 calls, 27 of them avoidable).
|
|
21
|
+
*/
|
|
22
|
+
export function priorRepeatedFailures(command, blocker, limit = 40) {
|
|
23
|
+
if (!command)
|
|
24
|
+
return { count: 0, requestIds: [] };
|
|
25
|
+
const path = logPath();
|
|
26
|
+
if (!path)
|
|
27
|
+
return { count: 0, requestIds: [] };
|
|
28
|
+
// Read only the tail: this runs on every refusal, and the log grows with the
|
|
29
|
+
// session (r6: 1528 records). Parsing the last ~64 KB is enough for `limit`.
|
|
30
|
+
const tail = readUsageTail(path, 65536).slice(-limit);
|
|
31
|
+
const matches = (record) => {
|
|
32
|
+
if (String(record.command ?? "") !== command)
|
|
33
|
+
return false;
|
|
34
|
+
const status = String(record.status ?? "");
|
|
35
|
+
if (status === "ok" || status === "dry-run" || status === "no-envelope")
|
|
36
|
+
return false;
|
|
37
|
+
if (blocker === undefined)
|
|
38
|
+
return true;
|
|
39
|
+
return String(record.blocker ?? "") === blocker;
|
|
40
|
+
};
|
|
41
|
+
let count = 0;
|
|
42
|
+
const requestIds = [];
|
|
43
|
+
for (let index = tail.length - 1; index >= 0; index -= 1) {
|
|
44
|
+
if (!matches(tail[index]))
|
|
45
|
+
break;
|
|
46
|
+
count += 1;
|
|
47
|
+
const requestId = tail[index].requestId;
|
|
48
|
+
if (typeof requestId === "string" && requestIds.length < 3)
|
|
49
|
+
requestIds.push(requestId);
|
|
50
|
+
}
|
|
51
|
+
return { count, requestIds };
|
|
52
|
+
}
|
|
12
53
|
function commandOf(argv) {
|
|
13
54
|
const values = [];
|
|
14
55
|
for (let index = 2; index < argv.length && values.length < 2; index += 1) {
|
|
@@ -91,6 +132,12 @@ export function recordUsageOutput(value, stdoutBytes, fileBytes = 0) {
|
|
|
91
132
|
pending.status = String(envelope.status ?? "unknown");
|
|
92
133
|
if (typeof error?.code === "string")
|
|
93
134
|
pending.errorCode = error.code;
|
|
135
|
+
// The blocker is the actionable half of a refusal (policy-refused alone says
|
|
136
|
+
// nothing); record it so repeat detection and accounting can group by cause.
|
|
137
|
+
const resultEnvelope = envelope.result && typeof envelope.result === "object" ? envelope.result : undefined;
|
|
138
|
+
const blocker = typeof resultEnvelope?.blocker === "string" ? resultEnvelope.blocker : typeof envelope.blocker === "string" ? envelope.blocker : undefined;
|
|
139
|
+
if (blocker)
|
|
140
|
+
pending.blocker = blocker;
|
|
94
141
|
// 0.4.16:requestId 落盘(信封瘦身把它从 stdout 移除后,排障关联的唯一落点)
|
|
95
142
|
if (typeof envelope.requestId === "string")
|
|
96
143
|
pending.requestId = envelope.requestId;
|
|
@@ -118,10 +165,37 @@ export function completeUsage(exitCode) {
|
|
|
118
165
|
status: current.status,
|
|
119
166
|
exitCode,
|
|
120
167
|
...(current.errorCode ? { errorCode: current.errorCode } : {}),
|
|
168
|
+
...(current.blocker ? { blocker: current.blocker } : {}),
|
|
121
169
|
...(current.requestId ? { requestId: current.requestId } : {}),
|
|
122
170
|
};
|
|
123
171
|
appendFileSync(path, `${JSON.stringify(record)}\n`, "utf8");
|
|
124
172
|
}
|
|
173
|
+
function readUsageTail(path, maxBytes) {
|
|
174
|
+
if (!existsSync(path))
|
|
175
|
+
return [];
|
|
176
|
+
const size = statSync(path).size;
|
|
177
|
+
const start = Math.max(0, size - maxBytes);
|
|
178
|
+
const fd = openSync(path, "r");
|
|
179
|
+
try {
|
|
180
|
+
const buffer = Buffer.alloc(size - start);
|
|
181
|
+
readSync(fd, buffer, 0, buffer.length, start);
|
|
182
|
+
const text = buffer.toString("utf8");
|
|
183
|
+
// Drop a leading partial line when the tail started mid-record.
|
|
184
|
+
const body = start > 0 ? text.slice(text.indexOf("\n") + 1) : text;
|
|
185
|
+
return body.split(/\r?\n/).filter(Boolean).flatMap((line) => {
|
|
186
|
+
try {
|
|
187
|
+
const value = JSON.parse(line);
|
|
188
|
+
return value && typeof value === "object" ? [value] : [];
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
return [];
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
finally {
|
|
196
|
+
closeSync(fd);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
125
199
|
function readUsage(path) {
|
|
126
200
|
if (!existsSync(path))
|
|
127
201
|
return [];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stalfh233/omc-cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.68",
|
|
4
4
|
"description": "CloudPivot 8.6 configuration capability CLI — governed, fail-closed, coverage-manifest bounded.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -50,9 +50,9 @@
|
|
|
50
50
|
"access": "public"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@stalfh233/omc-fake-platform": "0.4.
|
|
53
|
+
"@stalfh233/omc-fake-platform": "0.4.68",
|
|
54
54
|
"commander": "14.0.3",
|
|
55
|
-
"omc-core": "0.4.
|
|
55
|
+
"omc-core": "0.4.68",
|
|
56
56
|
"proper-lockfile": "4.1.2",
|
|
57
57
|
"write-file-atomic": "6.0.0",
|
|
58
58
|
"zod": "^3.23.8",
|