@jsonstudio/rcc 0.89.935 → 0.89.1083
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/README.md +1 -42
- package/dist/build-info.js +2 -2
- package/dist/build-info.js.map +1 -1
- package/dist/cli.js +120 -16
- package/dist/cli.js.map +1 -1
- package/dist/commands/quota-daemon.d.ts +2 -0
- package/dist/commands/quota-daemon.js +89 -0
- package/dist/commands/quota-daemon.js.map +1 -0
- package/dist/commands/token-daemon.js +1 -1
- package/dist/commands/token-daemon.js.map +1 -1
- package/dist/docs/daemon-admin-ui.html +958 -0
- package/dist/index.js +54 -4
- package/dist/index.js.map +1 -1
- package/dist/manager/modules/quota/index.d.ts +34 -0
- package/dist/manager/modules/quota/index.js +291 -0
- package/dist/manager/modules/quota/index.js.map +1 -1
- package/dist/manager/modules/token/index.js +14 -3
- package/dist/manager/modules/token/index.js.map +1 -1
- package/dist/manager/quota/provider-quota-center.d.ts +48 -0
- package/dist/manager/quota/provider-quota-center.js +239 -0
- package/dist/manager/quota/provider-quota-center.js.map +1 -0
- package/dist/manager/quota/provider-quota-store.d.ts +17 -0
- package/dist/manager/quota/provider-quota-store.js +88 -0
- package/dist/manager/quota/provider-quota-store.js.map +1 -0
- package/dist/providers/auth/token-scanner/index.js +11 -3
- package/dist/providers/auth/token-scanner/index.js.map +1 -1
- package/dist/providers/core/runtime/http-request-executor.js +24 -7
- package/dist/providers/core/runtime/http-request-executor.js.map +1 -1
- package/dist/providers/core/runtime/http-transport-provider.js +11 -3
- package/dist/providers/core/runtime/http-transport-provider.js.map +1 -1
- package/dist/providers/core/runtime/responses-provider.js +9 -3
- package/dist/providers/core/runtime/responses-provider.js.map +1 -1
- package/dist/providers/core/utils/http-client.d.ts +1 -0
- package/dist/providers/core/utils/http-client.js +139 -4
- package/dist/providers/core/utils/http-client.js.map +1 -1
- package/dist/providers/core/utils/snapshot-writer.d.ts +12 -0
- package/dist/providers/core/utils/snapshot-writer.js +99 -18
- package/dist/providers/core/utils/snapshot-writer.js.map +1 -1
- package/dist/providers/mock/mock-provider-runtime.d.ts +3 -0
- package/dist/providers/mock/mock-provider-runtime.js +176 -4
- package/dist/providers/mock/mock-provider-runtime.js.map +1 -1
- package/dist/server/handlers/chat-handler.js +13 -1
- package/dist/server/handlers/chat-handler.js.map +1 -1
- package/dist/server/handlers/handler-utils.js +5 -0
- package/dist/server/handlers/handler-utils.js.map +1 -1
- package/dist/server/handlers/messages-handler.js +13 -1
- package/dist/server/handlers/messages-handler.js.map +1 -1
- package/dist/server/handlers/responses-handler.js +73 -1
- package/dist/server/handlers/responses-handler.js.map +1 -1
- package/dist/server/runtime/http-server/daemon-admin/credentials-handler.js +174 -2
- 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 +519 -0
- package/dist/server/runtime/http-server/daemon-admin/providers-handler.js.map +1 -1
- package/dist/server/runtime/http-server/executor-response.js +6 -0
- package/dist/server/runtime/http-server/executor-response.js.map +1 -1
- package/dist/server/runtime/http-server/index.d.ts +5 -0
- package/dist/server/runtime/http-server/index.js +205 -4
- package/dist/server/runtime/http-server/index.js.map +1 -1
- package/dist/server/runtime/http-server/middleware.d.ts +2 -0
- package/dist/server/runtime/http-server/middleware.js +63 -0
- package/dist/server/runtime/http-server/middleware.js.map +1 -1
- package/dist/server/runtime/http-server/request-executor.d.ts +2 -0
- package/dist/server/runtime/http-server/request-executor.js +57 -10
- package/dist/server/runtime/http-server/request-executor.js.map +1 -1
- package/dist/server/runtime/http-server/routes.js +38 -1
- package/dist/server/runtime/http-server/routes.js.map +1 -1
- package/dist/server/runtime/http-server/stats-manager.d.ts +55 -0
- package/dist/server/runtime/http-server/stats-manager.js +462 -4
- package/dist/server/runtime/http-server/stats-manager.js.map +1 -1
- package/dist/server/runtime/http-server/types.d.ts +1 -0
- package/dist/token-daemon/token-daemon.d.ts +3 -1
- package/dist/token-daemon/token-daemon.js +130 -8
- package/dist/token-daemon/token-daemon.js.map +1 -1
- package/dist/token-daemon/token-utils.d.ts +1 -0
- package/dist/token-daemon/token-utils.js +9 -1
- package/dist/token-daemon/token-utils.js.map +1 -1
- package/dist/tools/semantic-replay.js +29 -0
- package/dist/tools/semantic-replay.js.map +1 -1
- package/dist/utils/snapshot-writer.d.ts +2 -0
- package/dist/utils/snapshot-writer.js +47 -4
- package/dist/utils/snapshot-writer.js.map +1 -1
- package/package.json +2 -3
- package/scripts/analyze-apply-patch-exec-failures.mjs +153 -0
- package/scripts/analyze-apply-patch-samples.mjs +242 -0
- package/scripts/analyze-codex-error-failures.mjs +24 -14
- package/scripts/classify-codex-samples.mjs +0 -35
- package/scripts/copy-modules-config.mjs +17 -1
- package/scripts/generate-snapshot-data.mjs +41 -11
- package/scripts/mock-provider/extract.mjs +254 -21
- package/scripts/mock-provider/run-regressions.mjs +97 -16
- package/scripts/quota-dryrun.mjs +124 -0
- package/scripts/tests/apply-patch-loop.mjs +5 -1
- package/scripts/tests/exec-command-loop.mjs +16 -19
- package/scripts/verify-apply-patch.mjs +335 -5
- package/scripts/verify-e2e-toolcall.mjs +49 -10
- package/scripts/toon-suite.mjs +0 -141
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from 'node:fs/promises';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import os from 'node:os';
|
|
6
|
+
|
|
7
|
+
const HOME = os.homedir();
|
|
8
|
+
const RESPONSES_DIR = path.join(HOME, '.routecodex', 'codex-samples', 'openai-responses');
|
|
9
|
+
|
|
10
|
+
async function fileExists(p) {
|
|
11
|
+
try {
|
|
12
|
+
await fs.access(p);
|
|
13
|
+
return true;
|
|
14
|
+
} catch {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async function* walkJsonFiles(root) {
|
|
20
|
+
const stack = [root];
|
|
21
|
+
while (stack.length) {
|
|
22
|
+
const current = stack.pop();
|
|
23
|
+
let entries;
|
|
24
|
+
try {
|
|
25
|
+
entries = await fs.readdir(current, { withFileTypes: true });
|
|
26
|
+
} catch {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
for (const entry of entries) {
|
|
30
|
+
const full = path.join(current, entry.name);
|
|
31
|
+
if (entry.isDirectory()) {
|
|
32
|
+
stack.push(full);
|
|
33
|
+
} else if (entry.isFile() && entry.name.toLowerCase().endsWith('.json')) {
|
|
34
|
+
yield full;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function extractApplyPatchResults(doc) {
|
|
41
|
+
const results = [];
|
|
42
|
+
function visit(node) {
|
|
43
|
+
if (!node) return;
|
|
44
|
+
if (Array.isArray(node)) {
|
|
45
|
+
for (const item of node) visit(item);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (typeof node !== 'object') return;
|
|
49
|
+
const any = node;
|
|
50
|
+
|
|
51
|
+
if (any.name === 'apply_patch' && any.response && typeof any.response === 'object') {
|
|
52
|
+
const resp = any.response;
|
|
53
|
+
let text = '';
|
|
54
|
+
if (typeof resp.result === 'string') text = resp.result;
|
|
55
|
+
else if (typeof resp.output === 'string') text = resp.output;
|
|
56
|
+
results.push(text);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (any.type === 'tool_result' && any.name === 'apply_patch') {
|
|
60
|
+
let text = '';
|
|
61
|
+
if (typeof any.result === 'string') text = any.result;
|
|
62
|
+
else if (typeof any.output === 'string') text = any.output;
|
|
63
|
+
else if (Array.isArray(any.output)) {
|
|
64
|
+
text = any.output
|
|
65
|
+
.map((entry) => {
|
|
66
|
+
if (typeof entry === 'string') return entry;
|
|
67
|
+
if (entry && typeof entry === 'object') {
|
|
68
|
+
if (typeof entry.text === 'string') return entry.text;
|
|
69
|
+
if (typeof entry.content === 'string') return entry.content;
|
|
70
|
+
}
|
|
71
|
+
return '';
|
|
72
|
+
})
|
|
73
|
+
.filter(Boolean)
|
|
74
|
+
.join('\n');
|
|
75
|
+
}
|
|
76
|
+
results.push(text);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
for (const value of Object.values(any)) visit(value);
|
|
80
|
+
}
|
|
81
|
+
visit(doc);
|
|
82
|
+
return results;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function isExecutionFailure(text) {
|
|
86
|
+
if (!text || typeof text !== 'string') return false;
|
|
87
|
+
const lower = text.toLowerCase();
|
|
88
|
+
if (text.includes('apply_patch verification failed')) return false;
|
|
89
|
+
if (!lower.includes('failed') && !lower.includes('error') && !lower.includes('exception')) return false;
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function normalizeKey(text) {
|
|
94
|
+
if (!text) return '<empty>';
|
|
95
|
+
const firstLine = text.split('\n')[0].trim();
|
|
96
|
+
return firstLine.length > 200 ? firstLine.slice(0, 200) : firstLine;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async function main() {
|
|
100
|
+
if (!(await fileExists(RESPONSES_DIR))) {
|
|
101
|
+
console.error('[analyze-apply-patch-exec-failures] no openai-responses dir at', RESPONSES_DIR);
|
|
102
|
+
process.exit(0);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const byKey = new Map();
|
|
106
|
+
let total = 0;
|
|
107
|
+
|
|
108
|
+
for await (const filePath of walkJsonFiles(RESPONSES_DIR)) {
|
|
109
|
+
let doc;
|
|
110
|
+
try {
|
|
111
|
+
const raw = await fs.readFile(filePath, 'utf-8');
|
|
112
|
+
doc = JSON.parse(raw);
|
|
113
|
+
} catch {
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
const results = extractApplyPatchResults(doc);
|
|
117
|
+
if (!results.length) continue;
|
|
118
|
+
for (const text of results) {
|
|
119
|
+
if (!isExecutionFailure(text)) continue;
|
|
120
|
+
total += 1;
|
|
121
|
+
const key = normalizeKey(text);
|
|
122
|
+
const current = byKey.get(key) || { count: 0, examples: [] };
|
|
123
|
+
current.count += 1;
|
|
124
|
+
if (current.examples.length < 3) {
|
|
125
|
+
current.examples.push(path.basename(filePath));
|
|
126
|
+
}
|
|
127
|
+
byKey.set(key, current);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
console.log(`[analyze-apply-patch-exec-failures] total execution-like failures: ${total}`);
|
|
132
|
+
|
|
133
|
+
const sorted = Array.from(byKey.entries()).sort((a, b) => b[1].count - a[1].count);
|
|
134
|
+
const top = sorted.slice(0, 30);
|
|
135
|
+
|
|
136
|
+
for (const [key, value] of top) {
|
|
137
|
+
console.log('\n--- pattern ---');
|
|
138
|
+
console.log(`count=${value.count}`);
|
|
139
|
+
console.log(`firstLine=${JSON.stringify(key)}`);
|
|
140
|
+
if (value.examples.length) {
|
|
141
|
+
console.log('examples:');
|
|
142
|
+
for (const ex of value.examples) {
|
|
143
|
+
console.log(` - ${ex}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
main().catch((err) => {
|
|
150
|
+
console.error('[analyze-apply-patch-exec-failures] failed:', err);
|
|
151
|
+
process.exit(1);
|
|
152
|
+
});
|
|
153
|
+
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Scan ~/.routecodex/codex-samples for apply_patch usage and
|
|
5
|
+
* count successful vs failed invocations.
|
|
6
|
+
*
|
|
7
|
+
* We look for apply_patch tool *results* in any JSON snapshot:
|
|
8
|
+
* - Gemini-style functionResponse.response.result
|
|
9
|
+
* - OpenAI-style tool_result with name === "apply_patch"
|
|
10
|
+
*
|
|
11
|
+
* Classification:
|
|
12
|
+
* - validationFailure: message contains "apply_patch verification failed"
|
|
13
|
+
* - executionFailure: message contains generic "failed"/"error"/"exception"
|
|
14
|
+
* but not the verification pattern
|
|
15
|
+
* - success: there is a result string and it doesn't look like a failure
|
|
16
|
+
* - unknown: no textual result payload
|
|
17
|
+
*
|
|
18
|
+
* Note: counts are per snapshot occurrence, not de-duplicated by requestId.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import fs from 'node:fs/promises';
|
|
22
|
+
import path from 'node:path';
|
|
23
|
+
import os from 'node:os';
|
|
24
|
+
|
|
25
|
+
const HOME = os.homedir();
|
|
26
|
+
const ROOT = path.join(HOME, '.routecodex', 'codex-samples');
|
|
27
|
+
const ENTRIES = ['openai-responses', 'openai-chat', 'anthropic-messages'];
|
|
28
|
+
|
|
29
|
+
async function fileExists(p) {
|
|
30
|
+
try {
|
|
31
|
+
await fs.access(p);
|
|
32
|
+
return true;
|
|
33
|
+
} catch {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function* walkJsonFiles(root) {
|
|
39
|
+
const stack = [root];
|
|
40
|
+
while (stack.length) {
|
|
41
|
+
const current = stack.pop();
|
|
42
|
+
let entries;
|
|
43
|
+
try {
|
|
44
|
+
entries = await fs.readdir(current, { withFileTypes: true });
|
|
45
|
+
} catch {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
for (const entry of entries) {
|
|
49
|
+
const full = path.join(current, entry.name);
|
|
50
|
+
if (entry.isDirectory()) {
|
|
51
|
+
stack.push(full);
|
|
52
|
+
} else if (entry.isFile() && entry.name.toLowerCase().endsWith('.json')) {
|
|
53
|
+
yield full;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function extractApplyPatchResults(doc) {
|
|
60
|
+
const results = [];
|
|
61
|
+
|
|
62
|
+
function visit(node) {
|
|
63
|
+
if (!node) return;
|
|
64
|
+
if (Array.isArray(node)) {
|
|
65
|
+
for (const item of node) visit(item);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (typeof node !== 'object') {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const any = node;
|
|
72
|
+
|
|
73
|
+
// Gemini-style: functionResponse { name: "apply_patch", response: { result: "..." } }
|
|
74
|
+
if (any.name === 'apply_patch' && any.response && typeof any.response === 'object') {
|
|
75
|
+
const resp = any.response;
|
|
76
|
+
let text = '';
|
|
77
|
+
if (typeof resp.result === 'string') {
|
|
78
|
+
text = resp.result;
|
|
79
|
+
} else if (typeof resp.output === 'string') {
|
|
80
|
+
text = resp.output;
|
|
81
|
+
}
|
|
82
|
+
results.push({ kind: 'result', text });
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// OpenAI-style tool_result: { type: "tool_result", name: "apply_patch", result/output: ... }
|
|
86
|
+
if (any.type === 'tool_result' && any.name === 'apply_patch') {
|
|
87
|
+
let text = '';
|
|
88
|
+
if (typeof any.result === 'string') {
|
|
89
|
+
text = any.result;
|
|
90
|
+
} else if (typeof any.output === 'string') {
|
|
91
|
+
text = any.output;
|
|
92
|
+
} else if (Array.isArray(any.output)) {
|
|
93
|
+
text = any.output
|
|
94
|
+
.map((entry) => {
|
|
95
|
+
if (typeof entry === 'string') return entry;
|
|
96
|
+
if (entry && typeof entry === 'object') {
|
|
97
|
+
if (typeof entry.text === 'string') return entry.text;
|
|
98
|
+
if (typeof entry.content === 'string') return entry.content;
|
|
99
|
+
}
|
|
100
|
+
return '';
|
|
101
|
+
})
|
|
102
|
+
.filter(Boolean)
|
|
103
|
+
.join('\n');
|
|
104
|
+
}
|
|
105
|
+
results.push({ kind: 'result', text });
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Function-call style apply_patch invocation
|
|
109
|
+
if (
|
|
110
|
+
(any.type === 'function_call' || any.type === 'functionCall') &&
|
|
111
|
+
any.name === 'apply_patch'
|
|
112
|
+
) {
|
|
113
|
+
results.push({ kind: 'call', text: '' });
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Provider tool_calls: { function: { name: "apply_patch", ... } }
|
|
117
|
+
if (any.function && typeof any.function === 'object' && any.function.name === 'apply_patch') {
|
|
118
|
+
results.push({ kind: 'call', text: '' });
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
for (const value of Object.values(any)) {
|
|
122
|
+
visit(value);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
visit(doc);
|
|
127
|
+
return results;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function classifyResultText(text) {
|
|
131
|
+
if (!text || typeof text !== 'string') {
|
|
132
|
+
return 'unknown';
|
|
133
|
+
}
|
|
134
|
+
const lower = text.toLowerCase();
|
|
135
|
+
if (text.includes('apply_patch verification failed')) {
|
|
136
|
+
return 'validationFailure';
|
|
137
|
+
}
|
|
138
|
+
if (lower.includes('verification failed')) {
|
|
139
|
+
return 'validationFailure';
|
|
140
|
+
}
|
|
141
|
+
if (lower.includes('failed') || lower.includes('error') || lower.includes('exception')) {
|
|
142
|
+
return 'executionFailure';
|
|
143
|
+
}
|
|
144
|
+
return 'success';
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async function main() {
|
|
148
|
+
if (!(await fileExists(ROOT))) {
|
|
149
|
+
console.error('[analyze-apply-patch-samples] codex-samples not found at', ROOT);
|
|
150
|
+
process.exit(0);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const totals = {
|
|
154
|
+
filesScanned: 0,
|
|
155
|
+
calls: 0,
|
|
156
|
+
results: 0,
|
|
157
|
+
success: 0,
|
|
158
|
+
validationFailure: 0,
|
|
159
|
+
executionFailure: 0,
|
|
160
|
+
unknown: 0
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
const byEntry = {};
|
|
164
|
+
|
|
165
|
+
for (const entry of ENTRIES) {
|
|
166
|
+
const entryDir = path.join(ROOT, entry);
|
|
167
|
+
if (!(await fileExists(entryDir))) {
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
const stats = {
|
|
171
|
+
filesScanned: 0,
|
|
172
|
+
calls: 0,
|
|
173
|
+
results: 0,
|
|
174
|
+
success: 0,
|
|
175
|
+
validationFailure: 0,
|
|
176
|
+
executionFailure: 0,
|
|
177
|
+
unknown: 0
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
for await (const filePath of walkJsonFiles(entryDir)) {
|
|
181
|
+
stats.filesScanned += 1;
|
|
182
|
+
totals.filesScanned += 1;
|
|
183
|
+
let doc;
|
|
184
|
+
try {
|
|
185
|
+
const raw = await fs.readFile(filePath, 'utf-8');
|
|
186
|
+
doc = JSON.parse(raw);
|
|
187
|
+
} catch {
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
const results = extractApplyPatchResults(doc);
|
|
191
|
+
if (!results.length) continue;
|
|
192
|
+
|
|
193
|
+
for (const item of results) {
|
|
194
|
+
if (item.kind === 'call') {
|
|
195
|
+
stats.calls += 1;
|
|
196
|
+
totals.calls += 1;
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
if (item.kind === 'result') {
|
|
200
|
+
stats.results += 1;
|
|
201
|
+
totals.results += 1;
|
|
202
|
+
const bucket = classifyResultText(item.text);
|
|
203
|
+
if (bucket === 'success') {
|
|
204
|
+
stats.success += 1;
|
|
205
|
+
totals.success += 1;
|
|
206
|
+
} else if (bucket === 'validationFailure') {
|
|
207
|
+
stats.validationFailure += 1;
|
|
208
|
+
totals.validationFailure += 1;
|
|
209
|
+
} else if (bucket === 'executionFailure') {
|
|
210
|
+
stats.executionFailure += 1;
|
|
211
|
+
totals.executionFailure += 1;
|
|
212
|
+
} else {
|
|
213
|
+
stats.unknown += 1;
|
|
214
|
+
totals.unknown += 1;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
byEntry[entry] = stats;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
console.log('[analyze-apply-patch-samples] summary (per snapshot, not de-duplicated by reqId):');
|
|
224
|
+
console.log(
|
|
225
|
+
`TOTAL files=${totals.filesScanned}, calls=${totals.calls}, results=${totals.results},` +
|
|
226
|
+
` success=${totals.success}, validationFailure=${totals.validationFailure},` +
|
|
227
|
+
` executionFailure=${totals.executionFailure}, unknown=${totals.unknown}`
|
|
228
|
+
);
|
|
229
|
+
for (const [entry, s] of Object.entries(byEntry)) {
|
|
230
|
+
console.log(
|
|
231
|
+
`${entry}: files=${s.filesScanned}, calls=${s.calls}, results=${s.results},` +
|
|
232
|
+
` success=${s.success}, validationFailure=${s.validationFailure},` +
|
|
233
|
+
` executionFailure=${s.executionFailure}, unknown=${s.unknown}`
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
main().catch((error) => {
|
|
239
|
+
console.error('[analyze-apply-patch-samples] failed:', error);
|
|
240
|
+
process.exit(1);
|
|
241
|
+
});
|
|
242
|
+
|
|
@@ -37,11 +37,25 @@ async function fileExists(p) {
|
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
async function
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
async function* walkJsonFiles(root) {
|
|
41
|
+
const stack = [root];
|
|
42
|
+
while (stack.length) {
|
|
43
|
+
const current = stack.pop();
|
|
44
|
+
let entries;
|
|
45
|
+
try {
|
|
46
|
+
entries = await fs.readdir(current, { withFileTypes: true });
|
|
47
|
+
} catch {
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
for (const entry of entries) {
|
|
51
|
+
const full = path.join(current, entry.name);
|
|
52
|
+
if (entry.isDirectory()) {
|
|
53
|
+
stack.push(full);
|
|
54
|
+
} else if (entry.isFile() && entry.name.toLowerCase().endsWith('.json')) {
|
|
55
|
+
yield full;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
45
59
|
}
|
|
46
60
|
|
|
47
61
|
async function analyzeFile(filePath) {
|
|
@@ -61,20 +75,13 @@ async function main() {
|
|
|
61
75
|
process.exit(0);
|
|
62
76
|
}
|
|
63
77
|
|
|
64
|
-
const files = await listJsonFiles(RESPONSES_DIR);
|
|
65
|
-
if (!files.length) {
|
|
66
|
-
console.log('[analyze-codex-error-failures] no JSON files under', RESPONSES_DIR);
|
|
67
|
-
process.exit(0);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
console.log(`[analyze-codex-error-failures] scanning ${files.length} file(s) under ${RESPONSES_DIR}`);
|
|
71
|
-
|
|
72
78
|
const summary = new Map();
|
|
73
79
|
for (const p of PATTERNS) {
|
|
74
80
|
summary.set(p, { count: 0, files: [] });
|
|
75
81
|
}
|
|
76
82
|
|
|
77
|
-
|
|
83
|
+
let scanned = 0;
|
|
84
|
+
for await (const file of walkJsonFiles(RESPONSES_DIR)) {
|
|
78
85
|
let res;
|
|
79
86
|
try {
|
|
80
87
|
res = await analyzeFile(file);
|
|
@@ -90,8 +97,11 @@ async function main() {
|
|
|
90
97
|
entry.files.push(path.basename(file));
|
|
91
98
|
}
|
|
92
99
|
}
|
|
100
|
+
scanned += 1;
|
|
93
101
|
}
|
|
94
102
|
|
|
103
|
+
console.log(`[analyze-codex-error-failures] scanned ${scanned} file(s) under ${RESPONSES_DIR}`);
|
|
104
|
+
|
|
95
105
|
for (const key of PATTERNS) {
|
|
96
106
|
const { count, files } = summary.get(key);
|
|
97
107
|
console.log(`\n=== Pattern: "${key}" ===`);
|
|
@@ -30,7 +30,6 @@ const PROVIDER_KEYS = {
|
|
|
30
30
|
const TOOL_TYPES = {
|
|
31
31
|
'apply_patch': 'apply_patch',
|
|
32
32
|
'shell': 'shell_command',
|
|
33
|
-
'toon': 'toon_tool',
|
|
34
33
|
'submit_tool_outputs': 'tool_loop',
|
|
35
34
|
'list_files': 'file_operation',
|
|
36
35
|
'write_file': 'file_operation',
|
|
@@ -46,7 +45,6 @@ class SampleClassifier {
|
|
|
46
45
|
byProvider: {},
|
|
47
46
|
byToolType: {},
|
|
48
47
|
withToolCalls: 0,
|
|
49
|
-
withToon: 0,
|
|
50
48
|
errors: 0
|
|
51
49
|
};
|
|
52
50
|
}
|
|
@@ -68,9 +66,6 @@ class SampleClassifier {
|
|
|
68
66
|
identifyToolType(toolCall) {
|
|
69
67
|
const funcName = toolCall.function?.name || '';
|
|
70
68
|
|
|
71
|
-
// 检查 TOON 格式
|
|
72
|
-
if (this.isToonTool(toolCall)) return 'toon_tool';
|
|
73
|
-
|
|
74
69
|
// 检查已知工具名称
|
|
75
70
|
for (const [pattern, type] of Object.entries(TOOL_TYPES)) {
|
|
76
71
|
if (funcName.toLowerCase().includes(pattern)) return type;
|
|
@@ -84,19 +79,6 @@ class SampleClassifier {
|
|
|
84
79
|
return 'unknown_tool';
|
|
85
80
|
}
|
|
86
81
|
|
|
87
|
-
// 检查是否为 TOON 工具
|
|
88
|
-
isToonTool(toolCall) {
|
|
89
|
-
const args = toolCall.function?.arguments;
|
|
90
|
-
if (!args) return false;
|
|
91
|
-
|
|
92
|
-
try {
|
|
93
|
-
const parsed = JSON.parse(args);
|
|
94
|
-
return parsed && typeof parsed.toon === 'string';
|
|
95
|
-
} catch {
|
|
96
|
-
return false;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
82
|
// 分析单个样本
|
|
101
83
|
async analyzeSample(filePath) {
|
|
102
84
|
try {
|
|
@@ -107,7 +89,6 @@ class SampleClassifier {
|
|
|
107
89
|
const sampleId = basename(dirname(filePath)) + '/' + basename(filePath, '.json');
|
|
108
90
|
|
|
109
91
|
let toolCalls = [];
|
|
110
|
-
let hasToon = false;
|
|
111
92
|
|
|
112
93
|
// 提取 tool_calls
|
|
113
94
|
if (data.tool_calls) {
|
|
@@ -128,8 +109,6 @@ class SampleClassifier {
|
|
|
128
109
|
const toolType = this.identifyToolType(toolCall);
|
|
129
110
|
toolTypes.push(toolType);
|
|
130
111
|
|
|
131
|
-
if (toolType === 'toon_tool') hasToon = true;
|
|
132
|
-
|
|
133
112
|
// 更新统计
|
|
134
113
|
this.stats.byToolType[toolType] = (this.stats.byToolType[toolType] || 0) + 1;
|
|
135
114
|
}
|
|
@@ -140,7 +119,6 @@ class SampleClassifier {
|
|
|
140
119
|
filePath,
|
|
141
120
|
hasToolCalls: toolCalls.length > 0,
|
|
142
121
|
toolTypes,
|
|
143
|
-
hasToon,
|
|
144
122
|
toolCallCount: toolCalls.length
|
|
145
123
|
};
|
|
146
124
|
|
|
@@ -150,7 +128,6 @@ class SampleClassifier {
|
|
|
150
128
|
this.stats.total++;
|
|
151
129
|
this.stats.byProvider[providerKey] = (this.stats.byProvider[providerKey] || 0) + 1;
|
|
152
130
|
if (toolCalls.length > 0) this.stats.withToolCalls++;
|
|
153
|
-
if (hasToon) this.stats.withToon++;
|
|
154
131
|
|
|
155
132
|
} catch (error) {
|
|
156
133
|
console.error(`Error analyzing ${filePath}:`, error.message);
|
|
@@ -196,7 +173,6 @@ class SampleClassifier {
|
|
|
196
173
|
|
|
197
174
|
console.log(`总样本数: ${this.stats.total}`);
|
|
198
175
|
console.log(`包含工具调用: ${this.stats.withToolCalls}`);
|
|
199
|
-
console.log(`包含 TOON: ${this.stats.withToon}`);
|
|
200
176
|
console.log(`错误数: ${this.stats.errors}`);
|
|
201
177
|
|
|
202
178
|
console.log('\n按 Provider 分布:');
|
|
@@ -224,21 +200,10 @@ class SampleClassifier {
|
|
|
224
200
|
console.log('==================');
|
|
225
201
|
|
|
226
202
|
const hasApplyPatch = this.stats.byToolType['apply_patch'] > 0;
|
|
227
|
-
const hasToon = this.stats.byToolType['toon_tool'] > 0;
|
|
228
203
|
const hasShell = this.stats.byToolType['shell_command'] > 0;
|
|
229
204
|
|
|
230
205
|
if (!hasApplyPatch) console.log(' - 缺少 apply_patch 样本');
|
|
231
|
-
if (!hasToon) console.log(' - 缺少 TOON 工具样本');
|
|
232
206
|
if (!hasShell) console.log(' - 缺少 shell command 样本');
|
|
233
|
-
|
|
234
|
-
// TOON 样本详情
|
|
235
|
-
if (this.stats.withToon > 0) {
|
|
236
|
-
console.log('\n🔧 TOON 工具样本:');
|
|
237
|
-
const toonSamples = this.samples.filter(s => s.hasToon);
|
|
238
|
-
for (const sample of toonSamples) {
|
|
239
|
-
console.log(` - ${sample.provider}: ${sample.id}`);
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
207
|
}
|
|
243
208
|
}
|
|
244
209
|
|
|
@@ -6,6 +6,8 @@ async function copyModulesConfig() {
|
|
|
6
6
|
const root = process.cwd();
|
|
7
7
|
const srcModulesConfig = path.join(root, 'config', 'modules.json');
|
|
8
8
|
const distModulesConfig = path.join(root, 'dist', 'config', 'modules.json');
|
|
9
|
+
const srcDaemonAdminUi = path.join(root, 'docs', 'daemon-admin-ui.html');
|
|
10
|
+
const distDaemonAdminUi = path.join(root, 'dist', 'docs', 'daemon-admin-ui.html');
|
|
9
11
|
|
|
10
12
|
try {
|
|
11
13
|
// 确保源文件存在
|
|
@@ -18,6 +20,20 @@ async function copyModulesConfig() {
|
|
|
18
20
|
await fs.copyFile(srcModulesConfig, distModulesConfig);
|
|
19
21
|
|
|
20
22
|
console.log('[copy-modules-config] copied modules.json to dist/config/modules.json');
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
await fs.access(srcDaemonAdminUi);
|
|
26
|
+
await fs.mkdir(path.dirname(distDaemonAdminUi), { recursive: true });
|
|
27
|
+
await fs.copyFile(srcDaemonAdminUi, distDaemonAdminUi);
|
|
28
|
+
console.log('[copy-modules-config] copied daemon-admin-ui.html to dist/docs/daemon-admin-ui.html');
|
|
29
|
+
} catch (error) {
|
|
30
|
+
if (error.code === 'ENOENT') {
|
|
31
|
+
console.warn('[copy-modules-config] docs/daemon-admin-ui.html not found, skipping');
|
|
32
|
+
} else {
|
|
33
|
+
console.error('[copy-modules-config] failed to copy daemon admin ui:', error.message);
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
21
37
|
} catch (error) {
|
|
22
38
|
if (error.code === 'ENOENT') {
|
|
23
39
|
console.warn('[copy-modules-config] source modules.json not found, skipping');
|
|
@@ -28,4 +44,4 @@ async function copyModulesConfig() {
|
|
|
28
44
|
}
|
|
29
45
|
}
|
|
30
46
|
|
|
31
|
-
copyModulesConfig();
|
|
47
|
+
copyModulesConfig();
|
|
@@ -148,21 +148,26 @@ class SnapshotDataGenerator {
|
|
|
148
148
|
|
|
149
149
|
try {
|
|
150
150
|
await fs.mkdir(outputDir, { recursive: true });
|
|
151
|
-
const files = await fs.readdir(responsesDir);
|
|
152
|
-
|
|
153
|
-
const groups = this.groupFilesByRequestId(files);
|
|
154
151
|
let count = 0;
|
|
155
|
-
|
|
156
|
-
|
|
152
|
+
|
|
153
|
+
const entries = await fs.readdir(responsesDir, { withFileTypes: true });
|
|
154
|
+
|
|
155
|
+
// New layout: openai-responses/<requestId>/*.json
|
|
156
|
+
for (const entry of entries) {
|
|
157
157
|
if (count >= MAX_SAMPLES) break;
|
|
158
|
-
|
|
158
|
+
if (!entry.isDirectory()) continue;
|
|
159
|
+
const requestId = entry.name;
|
|
160
|
+
if (!requestId.startsWith('req_') && !requestId.includes('responses')) continue;
|
|
161
|
+
|
|
162
|
+
const subdirPath = path.join(responsesDir, requestId);
|
|
163
|
+
const files = (await fs.readdir(subdirPath)).filter((f) => f.endsWith('.json'));
|
|
159
164
|
const snapshot = await this.buildSnapshotFromFiles(
|
|
160
|
-
requestId,
|
|
161
|
-
'openai-responses',
|
|
162
|
-
|
|
163
|
-
|
|
165
|
+
requestId,
|
|
166
|
+
'openai-responses',
|
|
167
|
+
files,
|
|
168
|
+
subdirPath
|
|
164
169
|
);
|
|
165
|
-
|
|
170
|
+
|
|
166
171
|
if (snapshot) {
|
|
167
172
|
const outputPath = path.join(outputDir, `${requestId}.json`);
|
|
168
173
|
await fs.writeFile(outputPath, JSON.stringify(snapshot, null, 2));
|
|
@@ -171,6 +176,31 @@ class SnapshotDataGenerator {
|
|
|
171
176
|
this.samplesGenerated++;
|
|
172
177
|
}
|
|
173
178
|
}
|
|
179
|
+
|
|
180
|
+
// Legacy layout: openai-responses/*.json
|
|
181
|
+
if (count < MAX_SAMPLES) {
|
|
182
|
+
const files = entries.filter((e) => e.isFile()).map((e) => e.name);
|
|
183
|
+
const groups = this.groupFilesByRequestId(files);
|
|
184
|
+
|
|
185
|
+
for (const [requestId, groupFiles] of Object.entries(groups)) {
|
|
186
|
+
if (count >= MAX_SAMPLES) break;
|
|
187
|
+
|
|
188
|
+
const snapshot = await this.buildSnapshotFromFiles(
|
|
189
|
+
requestId,
|
|
190
|
+
'openai-responses',
|
|
191
|
+
groupFiles,
|
|
192
|
+
responsesDir
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
if (snapshot) {
|
|
196
|
+
const outputPath = path.join(outputDir, `${requestId}.json`);
|
|
197
|
+
await fs.writeFile(outputPath, JSON.stringify(snapshot, null, 2));
|
|
198
|
+
console.log(` ✅ 生成快照: ${requestId}`);
|
|
199
|
+
count++;
|
|
200
|
+
this.samplesGenerated++;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
174
204
|
|
|
175
205
|
console.log(` 📊 Responses: ${count} 个快照`);
|
|
176
206
|
} catch (error) {
|