@jsonstudio/rcc 0.89.1121 → 0.89.1189
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/build-info.js +2 -2
- package/dist/cli/commands/clean.d.ts +16 -0
- package/dist/cli/commands/clean.js +58 -0
- package/dist/cli/commands/clean.js.map +1 -0
- package/dist/cli/commands/code.d.ts +55 -0
- package/dist/cli/commands/code.js +376 -0
- package/dist/cli/commands/code.js.map +1 -0
- package/dist/cli/commands/config.d.ts +31 -0
- package/dist/cli/commands/config.js +168 -0
- package/dist/cli/commands/config.js.map +1 -0
- package/dist/cli/commands/env.d.ts +20 -0
- package/dist/cli/commands/env.js +73 -0
- package/dist/cli/commands/env.js.map +1 -0
- package/dist/cli/commands/examples.d.ts +5 -0
- package/dist/cli/commands/examples.js +66 -0
- package/dist/cli/commands/examples.js.map +1 -0
- package/dist/cli/commands/port.d.ts +24 -0
- package/dist/cli/commands/port.js +85 -0
- package/dist/cli/commands/port.js.map +1 -0
- package/dist/cli/commands/restart.d.ts +50 -0
- package/dist/cli/commands/restart.js +176 -0
- package/dist/cli/commands/restart.js.map +1 -0
- package/dist/cli/commands/start.d.ts +68 -0
- package/dist/cli/commands/start.js +295 -0
- package/dist/cli/commands/start.js.map +1 -0
- package/dist/cli/commands/status.d.ts +16 -0
- package/dist/cli/commands/status.js +104 -0
- package/dist/cli/commands/status.js.map +1 -0
- package/dist/cli/commands/stop.d.ts +35 -0
- package/dist/cli/commands/stop.js +95 -0
- package/dist/cli/commands/stop.js.map +1 -0
- package/dist/cli/logger.d.ts +8 -0
- package/dist/cli/logger.js +9 -0
- package/dist/cli/logger.js.map +1 -0
- package/dist/cli/main.d.ts +6 -0
- package/dist/cli/main.js +16 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/program.d.ts +8 -0
- package/dist/cli/program.js +16 -0
- package/dist/cli/program.js.map +1 -0
- package/dist/cli/register/basic-commands.d.ts +30 -0
- package/dist/cli/register/basic-commands.js +11 -0
- package/dist/cli/register/basic-commands.js.map +1 -0
- package/dist/cli/register/code-command.d.ts +3 -0
- package/dist/cli/register/code-command.js +5 -0
- package/dist/cli/register/code-command.js.map +1 -0
- package/dist/cli/register/restart-command.d.ts +3 -0
- package/dist/cli/register/restart-command.js +5 -0
- package/dist/cli/register/restart-command.js.map +1 -0
- package/dist/cli/register/start-command.d.ts +3 -0
- package/dist/cli/register/start-command.js +5 -0
- package/dist/cli/register/start-command.js.map +1 -0
- package/dist/cli/register/status-config-commands.d.ts +16 -0
- package/dist/cli/register/status-config-commands.js +7 -0
- package/dist/cli/register/status-config-commands.js.map +1 -0
- package/dist/cli/register/stop-command.d.ts +3 -0
- package/dist/cli/register/stop-command.js +5 -0
- package/dist/cli/register/stop-command.js.map +1 -0
- package/dist/cli/runtime.d.ts +5 -0
- package/dist/cli/runtime.js +11 -0
- package/dist/cli/runtime.js.map +1 -0
- package/dist/cli/server/port-utils.d.ts +52 -0
- package/dist/cli/server/port-utils.js +193 -0
- package/dist/cli/server/port-utils.js.map +1 -0
- package/dist/cli/spinner.d.ts +10 -0
- package/dist/cli/spinner.js +59 -0
- package/dist/cli/spinner.js.map +1 -0
- package/dist/cli/utils/normalize.d.ts +2 -0
- package/dist/cli/utils/normalize.js +22 -0
- package/dist/cli/utils/normalize.js.map +1 -0
- package/dist/cli/utils/safe-read-json.d.ts +1 -0
- package/dist/cli/utils/safe-read-json.js +11 -0
- package/dist/cli/utils/safe-read-json.js.map +1 -0
- package/dist/cli.js +149 -1738
- package/dist/cli.js.map +1 -1
- package/dist/client/anthropic/anthropic-protocol-client.js +4 -3
- package/dist/client/anthropic/anthropic-protocol-client.js.map +1 -1
- package/dist/client/gemini/gemini-protocol-client.js +5 -0
- package/dist/client/gemini/gemini-protocol-client.js.map +1 -1
- package/dist/client/gemini-cli/gemini-cli-protocol-client.d.ts +1 -1
- package/dist/client/gemini-cli/gemini-cli-protocol-client.js +10 -3
- package/dist/client/gemini-cli/gemini-cli-protocol-client.js.map +1 -1
- package/dist/commands/provider-update.js +355 -5
- package/dist/commands/provider-update.js.map +1 -1
- package/dist/commands/quota-daemon.js +2 -2
- package/dist/commands/quota-daemon.js.map +1 -1
- package/dist/config/provider-v2-loader.js +4 -2
- package/dist/config/provider-v2-loader.js.map +1 -1
- package/dist/docs/daemon-admin-ui.html +583 -87
- package/dist/index.js +32 -1
- package/dist/index.js.map +1 -1
- package/dist/manager/modules/quota/index.d.ts +19 -1
- package/dist/manager/modules/quota/index.js +130 -5
- package/dist/manager/modules/quota/index.js.map +1 -1
- package/dist/manager/modules/routing/index.js.map +1 -1
- package/dist/manager/storage/file-store.js +1 -1
- package/dist/manager/storage/file-store.js.map +1 -1
- package/dist/manager/types.d.ts +5 -0
- package/dist/providers/auth/oauth-lifecycle.js +2 -2
- package/dist/providers/auth/oauth-lifecycle.js.map +1 -1
- package/dist/providers/core/api/provider-config.d.ts +2 -0
- package/dist/providers/core/api/provider-types.d.ts +2 -0
- package/dist/providers/core/config/service-profiles.js +1 -1
- package/dist/providers/core/config/service-profiles.js.map +1 -1
- package/dist/providers/core/runtime/base-provider.js +21 -27
- package/dist/providers/core/runtime/base-provider.js.map +1 -1
- package/dist/providers/core/runtime/gemini-cli-http-provider.d.ts +1 -0
- package/dist/providers/core/runtime/gemini-cli-http-provider.js +37 -6
- package/dist/providers/core/runtime/gemini-cli-http-provider.js.map +1 -1
- package/dist/providers/core/runtime/http-request-executor.js +23 -29
- package/dist/providers/core/runtime/http-request-executor.js.map +1 -1
- package/dist/providers/core/runtime/http-transport-provider.js +46 -38
- package/dist/providers/core/runtime/http-transport-provider.js.map +1 -1
- package/dist/providers/core/utils/http-client.d.ts +9 -0
- package/dist/providers/core/utils/http-client.js +9 -11
- package/dist/providers/core/utils/http-client.js.map +1 -1
- package/dist/providers/core/utils/provider-error-reporter.js +2 -6
- package/dist/providers/core/utils/provider-error-reporter.js.map +1 -1
- package/dist/providers/mock/mock-provider-runtime.js +19 -5
- package/dist/providers/mock/mock-provider-runtime.js.map +1 -1
- package/dist/server/handlers/handler-utils.d.ts +1 -1
- package/dist/server/handlers/handler-utils.js +4 -4
- package/dist/server/handlers/handler-utils.js.map +1 -1
- package/dist/server/handlers/responses-handler.js +2 -1
- package/dist/server/handlers/responses-handler.js.map +1 -1
- package/dist/server/handlers/sse-dispatcher.js +1 -4
- package/dist/server/handlers/sse-dispatcher.js.map +1 -1
- package/dist/server/runtime/http-server/colored-logger.d.ts +1 -1
- package/dist/server/runtime/http-server/colored-logger.js +22 -10
- package/dist/server/runtime/http-server/colored-logger.js.map +1 -1
- package/dist/server/runtime/http-server/daemon-admin/credentials-handler.js +12 -6
- package/dist/server/runtime/http-server/daemon-admin/credentials-handler.js.map +1 -1
- package/dist/server/runtime/http-server/daemon-admin/providers-handler.js +116 -98
- package/dist/server/runtime/http-server/daemon-admin/providers-handler.js.map +1 -1
- package/dist/server/runtime/http-server/daemon-admin/quota-handler.js +108 -15
- package/dist/server/runtime/http-server/daemon-admin/quota-handler.js.map +1 -1
- package/dist/server/runtime/http-server/daemon-admin/restart-handler.js +2 -1
- package/dist/server/runtime/http-server/daemon-admin/restart-handler.js.map +1 -1
- package/dist/server/runtime/http-server/daemon-admin/stats-handler.d.ts +3 -0
- package/dist/server/runtime/http-server/daemon-admin/stats-handler.js +56 -0
- package/dist/server/runtime/http-server/daemon-admin/stats-handler.js.map +1 -0
- package/dist/server/runtime/http-server/daemon-admin/status-handler.js +8 -4
- package/dist/server/runtime/http-server/daemon-admin/status-handler.js.map +1 -1
- package/dist/server/runtime/http-server/daemon-admin-routes.d.ts +9 -0
- package/dist/server/runtime/http-server/daemon-admin-routes.js +3 -0
- package/dist/server/runtime/http-server/daemon-admin-routes.js.map +1 -1
- package/dist/server/runtime/http-server/executor-provider.js +74 -0
- package/dist/server/runtime/http-server/executor-provider.js.map +1 -1
- package/dist/server/runtime/http-server/index.d.ts +2 -0
- package/dist/server/runtime/http-server/index.js +107 -17
- package/dist/server/runtime/http-server/index.js.map +1 -1
- package/dist/server/runtime/http-server/request-executor.js +18 -11
- package/dist/server/runtime/http-server/request-executor.js.map +1 -1
- package/dist/server/runtime/http-server/routes.d.ts +5 -0
- package/dist/server/runtime/http-server/routes.js +17 -4
- package/dist/server/runtime/http-server/routes.js.map +1 -1
- package/dist/server/runtime/http-server/stats-manager.d.ts +7 -0
- package/dist/server/runtime/http-server/stats-manager.js +31 -6
- package/dist/server/runtime/http-server/stats-manager.js.map +1 -1
- package/dist/server/runtime/http-server/types.d.ts +5 -0
- package/dist/server/utils/http-error-mapper.js +70 -9
- package/dist/server/utils/http-error-mapper.js.map +1 -1
- package/dist/server/utils/request-id-manager.js +9 -5
- package/dist/server/utils/request-id-manager.js.map +1 -1
- package/dist/server/utils/sse-request-parser.js +2 -1
- package/dist/server/utils/sse-request-parser.js.map +1 -1
- package/dist/server/utils/utf8-chunk-buffer.d.ts +15 -30
- package/dist/server/utils/utf8-chunk-buffer.js +78 -88
- package/dist/server/utils/utf8-chunk-buffer.js.map +1 -1
- package/dist/server/utils/warmup-storm-tracker.js +1 -1
- package/dist/server/utils/warmup-storm-tracker.js.map +1 -1
- package/dist/tools/provider-update/fetch-models.js +8 -5
- package/dist/tools/provider-update/fetch-models.js.map +1 -1
- package/dist/tools/provider-update/probe-context.d.ts +24 -0
- package/dist/tools/provider-update/probe-context.js +199 -0
- package/dist/tools/provider-update/probe-context.js.map +1 -0
- package/dist/tools/provider-update/types.d.ts +1 -0
- package/package.json +10 -4
- package/scripts/anthropic-compare-modes.mjs +40 -3
- package/scripts/antigravity-smoke.mjs +180 -0
- package/scripts/backfill-apply-patch-exec-errorsamples.mjs +225 -0
- package/scripts/compare-codex-rccx.mjs +59 -1
- package/scripts/compare-responses-request.mjs +50 -4
- package/scripts/lib/errorsamples.mjs +23 -0
- package/scripts/mock-provider/run-regressions.mjs +12 -2
- package/scripts/policy-violations-report.mjs +257 -0
- package/scripts/publish-rcc.mjs +16 -2
- package/scripts/scan-apply-patch-samples.mjs +148 -7
- package/scripts/tests/unified-hub-responses-enforce-safe.mjs +37 -0
- package/scripts/tests/unified-hub-shadow-regression.mjs +55 -0
- package/scripts/unified-hub-shadow-compare.mjs +359 -0
- package/scripts/verify-e2e-gemini-followup-sample.mjs +269 -0
- package/scripts/virtual-router-shadow-v2-real.mjs +71 -1
- package/scripts/virtual-router-shadow-v2.mjs +41 -0
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Report Unified Hub policy violations/enforcement rewrites captured under:
|
|
4
|
+
* ~/.routecodex/codex-samples/__policy_violations__/
|
|
5
|
+
*
|
|
6
|
+
* This is intended for day-to-day monitoring when policy is enabled by default.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import fs from 'node:fs/promises';
|
|
10
|
+
import os from 'node:os';
|
|
11
|
+
import path from 'node:path';
|
|
12
|
+
|
|
13
|
+
function usage() {
|
|
14
|
+
console.log(`Usage:
|
|
15
|
+
node scripts/policy-violations-report.mjs [options]
|
|
16
|
+
|
|
17
|
+
Options:
|
|
18
|
+
--root <dir> default: ~/.routecodex/codex-samples/__policy_violations__
|
|
19
|
+
--since-hours <n> only include files modified in last N hours
|
|
20
|
+
--limit <n> limit printed rows per section (default: 30)
|
|
21
|
+
--fail exit 1 if any records found
|
|
22
|
+
--help show help
|
|
23
|
+
`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function parseArgs(argv) {
|
|
27
|
+
const out = {
|
|
28
|
+
root: path.join(os.homedir(), '.routecodex', 'errorsamples', 'policy'),
|
|
29
|
+
sinceHours: undefined,
|
|
30
|
+
limit: 30,
|
|
31
|
+
fail: false
|
|
32
|
+
};
|
|
33
|
+
for (let i = 2; i < argv.length; i += 1) {
|
|
34
|
+
const a = argv[i];
|
|
35
|
+
if (a === '--root' && i + 1 < argv.length) out.root = argv[++i];
|
|
36
|
+
else if (a === '--since-hours' && i + 1 < argv.length) out.sinceHours = Number(argv[++i]);
|
|
37
|
+
else if (a === '--limit' && i + 1 < argv.length) out.limit = Number(argv[++i]);
|
|
38
|
+
else if (a === '--fail') out.fail = true;
|
|
39
|
+
else if (a === '--help' || a === '-h') out.help = true;
|
|
40
|
+
else {
|
|
41
|
+
console.error(`Unknown arg: ${a}`);
|
|
42
|
+
out.help = true;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return out;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function fileExists(p) {
|
|
49
|
+
try {
|
|
50
|
+
await fs.access(p);
|
|
51
|
+
return true;
|
|
52
|
+
} catch {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function walk(dir) {
|
|
58
|
+
const out = [];
|
|
59
|
+
const stack = [dir];
|
|
60
|
+
while (stack.length) {
|
|
61
|
+
const current = stack.pop();
|
|
62
|
+
let entries = [];
|
|
63
|
+
try {
|
|
64
|
+
entries = await fs.readdir(current, { withFileTypes: true });
|
|
65
|
+
} catch {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
for (const ent of entries) {
|
|
69
|
+
const p = path.join(current, ent.name);
|
|
70
|
+
if (ent.isDirectory()) stack.push(p);
|
|
71
|
+
else if (ent.isFile() && ent.name.endsWith('.json')) out.push(p);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return out;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function inc(map, key, by = 1) {
|
|
78
|
+
map.set(key, (map.get(key) || 0) + by);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function topN(map, n) {
|
|
82
|
+
return [...map.entries()].sort((a, b) => b[1] - a[1]).slice(0, n);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async function readJson(p) {
|
|
86
|
+
try {
|
|
87
|
+
const raw = await fs.readFile(p, 'utf8');
|
|
88
|
+
return JSON.parse(raw);
|
|
89
|
+
} catch {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function classifyRecord(obj) {
|
|
95
|
+
if (!obj || typeof obj !== 'object') return { kind: 'unknown' };
|
|
96
|
+
const o = obj;
|
|
97
|
+
if (Array.isArray(o.violations) || (o.summary && typeof o.summary === 'object')) return { kind: 'observe' };
|
|
98
|
+
if (Array.isArray(o.removedTopLevelKeys) || Array.isArray(o.flattenedWrappers)) return { kind: 'enforce' };
|
|
99
|
+
return { kind: 'unknown' };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function safeString(value) {
|
|
103
|
+
return typeof value === 'string' && value.trim() ? value.trim() : '';
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function inferStageFromFilename(filePath) {
|
|
107
|
+
const base = path.basename(filePath, '.json');
|
|
108
|
+
return base.replace(/_[0-9]+$/, '');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function fmtRow(cols, widths) {
|
|
112
|
+
return cols
|
|
113
|
+
.map((c, i) => {
|
|
114
|
+
const w = widths[i] || 20;
|
|
115
|
+
const s = String(c ?? '');
|
|
116
|
+
return s.length > w ? `${s.slice(0, Math.max(0, w - 1))}…` : s.padEnd(w, ' ');
|
|
117
|
+
})
|
|
118
|
+
.join(' ');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async function main() {
|
|
122
|
+
const args = parseArgs(process.argv);
|
|
123
|
+
if (args.help) {
|
|
124
|
+
usage();
|
|
125
|
+
process.exit(0);
|
|
126
|
+
}
|
|
127
|
+
let root = path.resolve(args.root);
|
|
128
|
+
if (!(await fileExists(root))) {
|
|
129
|
+
const fallback = path.join(os.homedir(), '.routecodex', 'codex-samples', '__policy_violations__');
|
|
130
|
+
if (await fileExists(fallback)) {
|
|
131
|
+
root = fallback;
|
|
132
|
+
} else {
|
|
133
|
+
console.log(`[policy-report] no folder: ${root}`);
|
|
134
|
+
process.exit(0);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const sinceMs =
|
|
139
|
+
typeof args.sinceHours === 'number' && Number.isFinite(args.sinceHours) && args.sinceHours > 0
|
|
140
|
+
? Date.now() - args.sinceHours * 60 * 60 * 1000
|
|
141
|
+
: null;
|
|
142
|
+
|
|
143
|
+
const files = await walk(root);
|
|
144
|
+
const rows = [];
|
|
145
|
+
for (const file of files) {
|
|
146
|
+
let st;
|
|
147
|
+
try {
|
|
148
|
+
st = await fs.stat(file);
|
|
149
|
+
} catch {
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
if (sinceMs !== null && st.mtimeMs < sinceMs) continue;
|
|
153
|
+
const obj = await readJson(file);
|
|
154
|
+
if (!obj) continue;
|
|
155
|
+
const rel = path.relative(root, file);
|
|
156
|
+
const parts = rel.split(path.sep);
|
|
157
|
+
const endpointFolder = parts[0] || '';
|
|
158
|
+
const providerKey = parts[1] || '';
|
|
159
|
+
const requestId = parts[2] || '';
|
|
160
|
+
rows.push({
|
|
161
|
+
file,
|
|
162
|
+
rel,
|
|
163
|
+
endpointFolder,
|
|
164
|
+
providerKey,
|
|
165
|
+
requestId,
|
|
166
|
+
stage: safeString(obj?.stage) || safeString(obj?.meta?.stage) || inferStageFromFilename(file),
|
|
167
|
+
protocol: safeString(obj?.providerProtocol) || safeString(obj?.protocol),
|
|
168
|
+
kind: classifyRecord(obj).kind,
|
|
169
|
+
obj
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
console.log(`[policy-report] root=${root}`);
|
|
174
|
+
if (sinceMs !== null) {
|
|
175
|
+
console.log(`[policy-report] sinceHours=${args.sinceHours}`);
|
|
176
|
+
}
|
|
177
|
+
console.log(`[policy-report] records=${rows.length}`);
|
|
178
|
+
if (!rows.length) {
|
|
179
|
+
process.exit(0);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const byStage = new Map();
|
|
183
|
+
const byProtocol = new Map();
|
|
184
|
+
const violationPathCounts = new Map();
|
|
185
|
+
const wrapperCounts = new Map();
|
|
186
|
+
const removedKeyCounts = new Map();
|
|
187
|
+
|
|
188
|
+
for (const r of rows) {
|
|
189
|
+
inc(byStage, r.stage);
|
|
190
|
+
if (r.protocol) inc(byProtocol, r.protocol);
|
|
191
|
+
|
|
192
|
+
if (r.kind === 'observe' && Array.isArray(r.obj?.violations)) {
|
|
193
|
+
for (const v of r.obj.violations) {
|
|
194
|
+
const p = safeString(v?.path) || '(unknown)';
|
|
195
|
+
inc(violationPathCounts, p);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
if (r.kind === 'enforce') {
|
|
199
|
+
const flattened = Array.isArray(r.obj?.flattenedWrappers) ? r.obj.flattenedWrappers : [];
|
|
200
|
+
for (const w of flattened) inc(wrapperCounts, String(w));
|
|
201
|
+
const removed = Array.isArray(r.obj?.removedTopLevelKeys) ? r.obj.removedTopLevelKeys : [];
|
|
202
|
+
for (const k of removed) inc(removedKeyCounts, String(k));
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const limit = Number.isFinite(args.limit) && args.limit > 0 ? args.limit : 30;
|
|
207
|
+
|
|
208
|
+
console.log('\n[policy-report] top stages:');
|
|
209
|
+
for (const [k, v] of topN(byStage, limit)) {
|
|
210
|
+
console.log(`- ${k}: ${v}`);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
console.log('\n[policy-report] top protocols:');
|
|
214
|
+
for (const [k, v] of topN(byProtocol, limit)) {
|
|
215
|
+
console.log(`- ${k}: ${v}`);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (violationPathCounts.size) {
|
|
219
|
+
console.log('\n[policy-report] top violation paths:');
|
|
220
|
+
for (const [k, v] of topN(violationPathCounts, limit)) {
|
|
221
|
+
console.log(`- ${k}: ${v}`);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (wrapperCounts.size) {
|
|
226
|
+
console.log('\n[policy-report] top flattened wrappers (enforce):');
|
|
227
|
+
for (const [k, v] of topN(wrapperCounts, limit)) {
|
|
228
|
+
console.log(`- ${k}: ${v}`);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (removedKeyCounts.size) {
|
|
233
|
+
console.log('\n[policy-report] top removed keys (enforce):');
|
|
234
|
+
for (const [k, v] of topN(removedKeyCounts, limit)) {
|
|
235
|
+
console.log(`- ${k}: ${v}`);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
console.log('\n[policy-report] newest records:');
|
|
240
|
+
const newest = rows
|
|
241
|
+
.slice()
|
|
242
|
+
.sort((a, b) => (a.file < b.file ? 1 : -1))
|
|
243
|
+
.slice(0, Math.min(limit, rows.length));
|
|
244
|
+
console.log(fmtRow(['endpoint', 'providerKey', 'stage', 'protocol', 'requestId'], [16, 28, 40, 18, 24]));
|
|
245
|
+
for (const r of newest) {
|
|
246
|
+
console.log(fmtRow([r.endpointFolder, r.providerKey, r.stage, r.protocol || '-', r.requestId], [16, 28, 40, 18, 24]));
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (args.fail) {
|
|
250
|
+
process.exit(1);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
main().catch((err) => {
|
|
255
|
+
console.error('[policy-report] failed:', err);
|
|
256
|
+
process.exit(2);
|
|
257
|
+
});
|
package/scripts/publish-rcc.mjs
CHANGED
|
@@ -8,6 +8,7 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
|
8
8
|
const PROJECT_ROOT = path.resolve(__dirname, '..');
|
|
9
9
|
const PACK_SCRIPT = path.join(PROJECT_ROOT, 'scripts', 'pack-mode.mjs');
|
|
10
10
|
const pkgPath = path.join(PROJECT_ROOT, 'package.json');
|
|
11
|
+
const llmsPath = path.join(PROJECT_ROOT, 'node_modules', '@jsonstudio', 'llms');
|
|
11
12
|
|
|
12
13
|
function run(command, args, options = {}) {
|
|
13
14
|
const res = spawnSync(command, args, { stdio: 'inherit', ...options });
|
|
@@ -17,6 +18,14 @@ function run(command, args, options = {}) {
|
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
try {
|
|
21
|
+
const hadDevLink = (() => {
|
|
22
|
+
try {
|
|
23
|
+
return fs.lstatSync(llmsPath).isSymbolicLink();
|
|
24
|
+
} catch {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
})();
|
|
28
|
+
|
|
20
29
|
// 1) 使用 release 模式构建 dist(依赖 npm 上的 @jsonstudio/llms)
|
|
21
30
|
run('npm', ['run', 'build:min'], {
|
|
22
31
|
cwd: PROJECT_ROOT,
|
|
@@ -39,8 +48,13 @@ try {
|
|
|
39
48
|
// 3) 发布 npm 包
|
|
40
49
|
run('npm', ['publish', tarballName], { cwd: PROJECT_ROOT });
|
|
41
50
|
|
|
42
|
-
// 4)
|
|
43
|
-
|
|
51
|
+
// 4) 发布结束后恢复 dev 模式(routecodex 约定始终为 dev CLI;rcc 发布时才切 release)。
|
|
52
|
+
if (hadDevLink) {
|
|
53
|
+
run('npm', ['run', 'llmswitch:ensure'], {
|
|
54
|
+
cwd: PROJECT_ROOT,
|
|
55
|
+
env: { ...process.env, BUILD_MODE: 'dev' }
|
|
56
|
+
});
|
|
57
|
+
}
|
|
44
58
|
} catch (err) {
|
|
45
59
|
console.error('[publish-rcc] failed:', err.message);
|
|
46
60
|
process.exit(1);
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
import fs from 'node:fs/promises';
|
|
18
18
|
import path from 'node:path';
|
|
19
19
|
import os from 'node:os';
|
|
20
|
+
import crypto from 'node:crypto';
|
|
20
21
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
21
22
|
|
|
22
23
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -27,8 +28,13 @@ const coreLoaderUrl = pathToFileURL(coreLoaderPath).href;
|
|
|
27
28
|
|
|
28
29
|
const HOME = os.homedir();
|
|
29
30
|
const USER_CODEX_ROOT = path.join(HOME, '.routecodex', 'codex-samples');
|
|
31
|
+
const USER_CODEX_ROOT_ALT = path.join(HOME, '.routecodex', 'codex samples');
|
|
30
32
|
const USER_CODEX_PENDING = path.join(USER_CODEX_ROOT, 'openai-chat', '__pending__');
|
|
31
33
|
const REPO_GOLDENS_ROOT = path.join(repoRoot, 'samples', 'ci-goldens');
|
|
34
|
+
const ERROR_SAMPLES_ROOT = path.join(HOME, '.routecodex', 'errorsamples', 'apply_patch');
|
|
35
|
+
|
|
36
|
+
const DEFAULT_CAPTURE_TOTAL_LIMIT = 5000;
|
|
37
|
+
const DEFAULT_CAPTURE_PER_REASON_LIMIT = 250;
|
|
32
38
|
|
|
33
39
|
async function fileExists(p) {
|
|
34
40
|
try {
|
|
@@ -111,6 +117,74 @@ function extractApplyPatchArgs(doc, { allowRegressionOriginalArgs } = { allowReg
|
|
|
111
117
|
return out;
|
|
112
118
|
}
|
|
113
119
|
|
|
120
|
+
function isContextMismatchReason(reason) {
|
|
121
|
+
// We only capture/repair shape issues; context mismatches are not actionable here.
|
|
122
|
+
// Keep this conservative: if a new reason is introduced, we still capture it unless
|
|
123
|
+
// it clearly indicates a context mismatch.
|
|
124
|
+
if (!reason) return false;
|
|
125
|
+
const r = String(reason);
|
|
126
|
+
return (
|
|
127
|
+
r.includes('context') ||
|
|
128
|
+
r.includes('expected') ||
|
|
129
|
+
r.includes('hunk') ||
|
|
130
|
+
r.includes('no_match') ||
|
|
131
|
+
r.includes('not_found')
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function stableHash(input) {
|
|
136
|
+
return crypto.createHash('sha256').update(input).digest('hex').slice(0, 16);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
async function captureFailure({
|
|
140
|
+
destRoot,
|
|
141
|
+
label,
|
|
142
|
+
sourceFile,
|
|
143
|
+
reason,
|
|
144
|
+
originalArgs,
|
|
145
|
+
validationResult,
|
|
146
|
+
captureState,
|
|
147
|
+
}) {
|
|
148
|
+
if (isContextMismatchReason(reason)) return;
|
|
149
|
+
if (captureState?.reasonAllowList && !captureState.reasonAllowList.has(reason)) return;
|
|
150
|
+
if (!destRoot) return;
|
|
151
|
+
|
|
152
|
+
const totalLimit = captureState.totalLimit ?? DEFAULT_CAPTURE_TOTAL_LIMIT;
|
|
153
|
+
const perReasonLimit = captureState.perReasonLimit ?? DEFAULT_CAPTURE_PER_REASON_LIMIT;
|
|
154
|
+
if (captureState.totalCaptured >= totalLimit) return;
|
|
155
|
+
|
|
156
|
+
const currentReasonCount = captureState.byReason.get(reason) ?? 0;
|
|
157
|
+
if (currentReasonCount >= perReasonLimit) return;
|
|
158
|
+
|
|
159
|
+
const key = `${label}\n${reason}\n${sourceFile}\n${originalArgs}`;
|
|
160
|
+
const filename = `sample_${stableHash(key)}.json`;
|
|
161
|
+
const folder = path.join(destRoot, reason);
|
|
162
|
+
const outPath = path.join(folder, filename);
|
|
163
|
+
|
|
164
|
+
try {
|
|
165
|
+
await fs.access(outPath);
|
|
166
|
+
return;
|
|
167
|
+
} catch {
|
|
168
|
+
// continue
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
await fs.mkdir(folder, { recursive: true });
|
|
172
|
+
const payload = {
|
|
173
|
+
tool: 'apply_patch',
|
|
174
|
+
label,
|
|
175
|
+
reason,
|
|
176
|
+
capturedAt: new Date().toISOString(),
|
|
177
|
+
sourceFile,
|
|
178
|
+
argsSha256: crypto.createHash('sha256').update(originalArgs).digest('hex'),
|
|
179
|
+
originalArgs,
|
|
180
|
+
validationResult,
|
|
181
|
+
};
|
|
182
|
+
await fs.writeFile(outPath, JSON.stringify(payload, null, 2), 'utf-8');
|
|
183
|
+
|
|
184
|
+
captureState.totalCaptured += 1;
|
|
185
|
+
captureState.byReason.set(reason, currentReasonCount + 1);
|
|
186
|
+
}
|
|
187
|
+
|
|
114
188
|
async function loadValidator() {
|
|
115
189
|
if (!(await fileExists(coreLoaderPath))) {
|
|
116
190
|
throw new Error(`core-loader missing at ${coreLoaderPath} (run npm run build:dev first)`);
|
|
@@ -123,7 +197,7 @@ async function loadValidator() {
|
|
|
123
197
|
return { validateToolCall };
|
|
124
198
|
}
|
|
125
199
|
|
|
126
|
-
async function scanRoot(label, rootDir, validateToolCall) {
|
|
200
|
+
async function scanRoot(label, rootDir, validateToolCall, capture) {
|
|
127
201
|
if (!(await fileExists(rootDir))) {
|
|
128
202
|
return { label, rootDir, files: 0, calls: 0, ok: 0, byReason: new Map(), examples: [] };
|
|
129
203
|
}
|
|
@@ -168,6 +242,17 @@ async function scanRoot(label, rootDir, validateToolCall) {
|
|
|
168
242
|
const cur = byReason.get(reason) || { count: 0 };
|
|
169
243
|
cur.count += 1;
|
|
170
244
|
byReason.set(reason, cur);
|
|
245
|
+
if (capture?.enabled) {
|
|
246
|
+
await captureFailure({
|
|
247
|
+
destRoot: capture.destRoot,
|
|
248
|
+
label,
|
|
249
|
+
sourceFile: filePath,
|
|
250
|
+
reason,
|
|
251
|
+
originalArgs: args,
|
|
252
|
+
validationResult: res,
|
|
253
|
+
captureState: capture.state,
|
|
254
|
+
});
|
|
255
|
+
}
|
|
171
256
|
if (examples.length < 12) {
|
|
172
257
|
examples.push({ file: filePath, reason });
|
|
173
258
|
}
|
|
@@ -200,17 +285,73 @@ function printReport(report) {
|
|
|
200
285
|
async function main() {
|
|
201
286
|
const { validateToolCall } = await loadValidator();
|
|
202
287
|
|
|
203
|
-
const
|
|
288
|
+
const captureEnabled = process.argv.slice(2).includes('--capture') || process.env.ROUTECODEX_SCAN_CAPTURE === '1';
|
|
289
|
+
const captureRepo = process.argv.slice(2).includes('--capture-repo') || process.env.ROUTECODEX_SCAN_CAPTURE_REPO === '1';
|
|
290
|
+
const captureRoot = process.env.ROUTECODEX_ERRORSAMPLES_DIR || ERROR_SAMPLES_ROOT;
|
|
291
|
+
const totalLimit = Number.parseInt(process.env.ROUTECODEX_CAPTURE_TOTAL_LIMIT || '', 10);
|
|
292
|
+
const perReasonLimit = Number.parseInt(process.env.ROUTECODEX_CAPTURE_PER_REASON_LIMIT || '', 10);
|
|
293
|
+
const reasonsArg = process.argv
|
|
294
|
+
.slice(2)
|
|
295
|
+
.find((arg) => arg.startsWith('--capture-reasons='))
|
|
296
|
+
?.split('=')[1];
|
|
297
|
+
const reasonsEnv = process.env.ROUTECODEX_CAPTURE_REASONS;
|
|
298
|
+
const reasonAllowListRaw = (reasonsArg || reasonsEnv || '').trim();
|
|
299
|
+
const reasonAllowList = reasonAllowListRaw
|
|
300
|
+
? new Set(
|
|
301
|
+
reasonAllowListRaw
|
|
302
|
+
.split(',')
|
|
303
|
+
.map((s) => s.trim())
|
|
304
|
+
.filter(Boolean)
|
|
305
|
+
)
|
|
306
|
+
: null;
|
|
307
|
+
const captureState = {
|
|
308
|
+
totalCaptured: 0,
|
|
309
|
+
totalLimit: Number.isFinite(totalLimit) ? totalLimit : DEFAULT_CAPTURE_TOTAL_LIMIT,
|
|
310
|
+
perReasonLimit: Number.isFinite(perReasonLimit) ? perReasonLimit : DEFAULT_CAPTURE_PER_REASON_LIMIT,
|
|
311
|
+
byReason: new Map(),
|
|
312
|
+
reasonAllowList,
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
const repo = await scanRoot('repo samples/ci-goldens', REPO_GOLDENS_ROOT, validateToolCall, {
|
|
316
|
+
enabled: captureEnabled && captureRepo,
|
|
317
|
+
destRoot: captureRoot,
|
|
318
|
+
state: captureState,
|
|
319
|
+
});
|
|
204
320
|
printReport(repo);
|
|
205
321
|
|
|
206
322
|
const userAll = process.argv.slice(2).includes('--user-all');
|
|
207
|
-
const
|
|
208
|
-
|
|
209
|
-
|
|
323
|
+
const userRoots = [];
|
|
324
|
+
if (userAll) {
|
|
325
|
+
if (await fileExists(USER_CODEX_ROOT)) userRoots.push({ label: 'user ~/.routecodex/codex-samples (all)', root: USER_CODEX_ROOT });
|
|
326
|
+
if (await fileExists(USER_CODEX_ROOT_ALT))
|
|
327
|
+
userRoots.push({ label: 'user ~/.routecodex/codex samples (all)', root: USER_CODEX_ROOT_ALT });
|
|
328
|
+
} else {
|
|
329
|
+
const pending = (await fileExists(USER_CODEX_PENDING)) ? USER_CODEX_PENDING : null;
|
|
330
|
+
if (pending) userRoots.push({ label: 'user ~/.routecodex/codex-samples/openai-chat/__pending__', root: pending });
|
|
331
|
+
else if (await fileExists(USER_CODEX_ROOT)) userRoots.push({ label: 'user ~/.routecodex/codex-samples', root: USER_CODEX_ROOT });
|
|
332
|
+
else if (await fileExists(USER_CODEX_ROOT_ALT)) userRoots.push({ label: 'user ~/.routecodex/codex samples', root: USER_CODEX_ROOT_ALT });
|
|
333
|
+
else userRoots.push({ label: 'user ~/.routecodex/codex-samples', root: USER_CODEX_ROOT });
|
|
334
|
+
}
|
|
210
335
|
|
|
211
|
-
const
|
|
212
|
-
const
|
|
336
|
+
const userReports = [];
|
|
337
|
+
for (const entry of userRoots) {
|
|
338
|
+
const report = await scanRoot(entry.label, entry.root, validateToolCall, {
|
|
339
|
+
enabled: captureEnabled,
|
|
340
|
+
destRoot: captureRoot,
|
|
341
|
+
state: captureState,
|
|
342
|
+
});
|
|
343
|
+
userReports.push(report);
|
|
344
|
+
printReport(report);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const totalCalls = repo.calls + userReports.reduce((sum, r) => sum + r.calls, 0);
|
|
348
|
+
const totalOk = repo.ok + userReports.reduce((sum, r) => sum + r.ok, 0);
|
|
213
349
|
console.log(`\n[scan] TOTAL apply_patch_calls=${totalCalls} ok=${totalOk} fail=${totalCalls - totalOk}`);
|
|
350
|
+
if (captureEnabled) {
|
|
351
|
+
console.log(
|
|
352
|
+
`[scan] captured_failures=${captureState.totalCaptured} dest=${captureRoot} (per_reason<=${captureState.perReasonLimit}, total<=${captureState.totalLimit})`
|
|
353
|
+
);
|
|
354
|
+
}
|
|
214
355
|
|
|
215
356
|
process.exitCode = totalCalls - totalOk > 0 ? 1 : 0;
|
|
216
357
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { spawnSync } from 'node:child_process';
|
|
4
|
+
|
|
5
|
+
function runCase(args) {
|
|
6
|
+
const nodeArgs = ['scripts/unified-hub-shadow-compare.mjs', ...args];
|
|
7
|
+
const result = spawnSync(process.execPath, nodeArgs, {
|
|
8
|
+
cwd: path.resolve(path.dirname(new URL(import.meta.url).pathname), '..', '..'),
|
|
9
|
+
stdio: 'inherit'
|
|
10
|
+
});
|
|
11
|
+
if (result.status !== 0) {
|
|
12
|
+
throw new Error(`shadow compare failed: ${nodeArgs.join(' ')}`);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function main() {
|
|
17
|
+
const repoRoot = path.resolve(path.dirname(new URL(import.meta.url).pathname), '..', '..');
|
|
18
|
+
const fixturesDir = path.join(repoRoot, 'tests', 'fixtures', 'unified-hub');
|
|
19
|
+
|
|
20
|
+
runCase([
|
|
21
|
+
'--request',
|
|
22
|
+
path.join(fixturesDir, 'responses.clean.json'),
|
|
23
|
+
'--entry-endpoint',
|
|
24
|
+
'/v1/responses',
|
|
25
|
+
'--route-hint',
|
|
26
|
+
'responses',
|
|
27
|
+
'--baseline-mode',
|
|
28
|
+
'off',
|
|
29
|
+
'--candidate-mode',
|
|
30
|
+
'enforce'
|
|
31
|
+
]);
|
|
32
|
+
|
|
33
|
+
console.log('[unified-hub-responses-enforce-safe] OK');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
main();
|
|
37
|
+
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { spawnSync } from 'node:child_process';
|
|
5
|
+
|
|
6
|
+
function runCase(args) {
|
|
7
|
+
const nodeArgs = ['scripts/unified-hub-shadow-compare.mjs', ...args];
|
|
8
|
+
const result = spawnSync(process.execPath, nodeArgs, {
|
|
9
|
+
cwd: path.resolve(path.dirname(new URL(import.meta.url).pathname), '..', '..'),
|
|
10
|
+
stdio: 'inherit'
|
|
11
|
+
});
|
|
12
|
+
if (result.status !== 0) {
|
|
13
|
+
throw new Error(`shadow compare failed: ${nodeArgs.join(' ')}`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function main() {
|
|
18
|
+
const repoRoot = path.resolve(path.dirname(new URL(import.meta.url).pathname), '..', '..');
|
|
19
|
+
const fixturesDir = path.join(repoRoot, 'tests', 'fixtures', 'unified-hub');
|
|
20
|
+
if (!fs.existsSync(fixturesDir)) {
|
|
21
|
+
throw new Error(`fixtures dir missing: ${fixturesDir}`);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
runCase([
|
|
25
|
+
'--request',
|
|
26
|
+
path.join(fixturesDir, 'chat.json'),
|
|
27
|
+
'--entry-endpoint',
|
|
28
|
+
'/v1/chat/completions',
|
|
29
|
+
'--route-hint',
|
|
30
|
+
'openai'
|
|
31
|
+
]);
|
|
32
|
+
|
|
33
|
+
runCase([
|
|
34
|
+
'--request',
|
|
35
|
+
path.join(fixturesDir, 'responses.json'),
|
|
36
|
+
'--entry-endpoint',
|
|
37
|
+
'/v1/responses',
|
|
38
|
+
'--route-hint',
|
|
39
|
+
'responses'
|
|
40
|
+
]);
|
|
41
|
+
|
|
42
|
+
runCase([
|
|
43
|
+
'--request',
|
|
44
|
+
path.join(fixturesDir, 'anthropic.json'),
|
|
45
|
+
'--entry-endpoint',
|
|
46
|
+
'/v1/messages',
|
|
47
|
+
'--route-hint',
|
|
48
|
+
'anthropic'
|
|
49
|
+
]);
|
|
50
|
+
|
|
51
|
+
console.log('[unified-hub-shadow-regression] OK');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
main();
|
|
55
|
+
|