@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.
Files changed (96) hide show
  1. package/README.md +1 -42
  2. package/dist/build-info.js +2 -2
  3. package/dist/build-info.js.map +1 -1
  4. package/dist/cli.js +120 -16
  5. package/dist/cli.js.map +1 -1
  6. package/dist/commands/quota-daemon.d.ts +2 -0
  7. package/dist/commands/quota-daemon.js +89 -0
  8. package/dist/commands/quota-daemon.js.map +1 -0
  9. package/dist/commands/token-daemon.js +1 -1
  10. package/dist/commands/token-daemon.js.map +1 -1
  11. package/dist/docs/daemon-admin-ui.html +958 -0
  12. package/dist/index.js +54 -4
  13. package/dist/index.js.map +1 -1
  14. package/dist/manager/modules/quota/index.d.ts +34 -0
  15. package/dist/manager/modules/quota/index.js +291 -0
  16. package/dist/manager/modules/quota/index.js.map +1 -1
  17. package/dist/manager/modules/token/index.js +14 -3
  18. package/dist/manager/modules/token/index.js.map +1 -1
  19. package/dist/manager/quota/provider-quota-center.d.ts +48 -0
  20. package/dist/manager/quota/provider-quota-center.js +239 -0
  21. package/dist/manager/quota/provider-quota-center.js.map +1 -0
  22. package/dist/manager/quota/provider-quota-store.d.ts +17 -0
  23. package/dist/manager/quota/provider-quota-store.js +88 -0
  24. package/dist/manager/quota/provider-quota-store.js.map +1 -0
  25. package/dist/providers/auth/token-scanner/index.js +11 -3
  26. package/dist/providers/auth/token-scanner/index.js.map +1 -1
  27. package/dist/providers/core/runtime/http-request-executor.js +24 -7
  28. package/dist/providers/core/runtime/http-request-executor.js.map +1 -1
  29. package/dist/providers/core/runtime/http-transport-provider.js +11 -3
  30. package/dist/providers/core/runtime/http-transport-provider.js.map +1 -1
  31. package/dist/providers/core/runtime/responses-provider.js +9 -3
  32. package/dist/providers/core/runtime/responses-provider.js.map +1 -1
  33. package/dist/providers/core/utils/http-client.d.ts +1 -0
  34. package/dist/providers/core/utils/http-client.js +139 -4
  35. package/dist/providers/core/utils/http-client.js.map +1 -1
  36. package/dist/providers/core/utils/snapshot-writer.d.ts +12 -0
  37. package/dist/providers/core/utils/snapshot-writer.js +99 -18
  38. package/dist/providers/core/utils/snapshot-writer.js.map +1 -1
  39. package/dist/providers/mock/mock-provider-runtime.d.ts +3 -0
  40. package/dist/providers/mock/mock-provider-runtime.js +176 -4
  41. package/dist/providers/mock/mock-provider-runtime.js.map +1 -1
  42. package/dist/server/handlers/chat-handler.js +13 -1
  43. package/dist/server/handlers/chat-handler.js.map +1 -1
  44. package/dist/server/handlers/handler-utils.js +5 -0
  45. package/dist/server/handlers/handler-utils.js.map +1 -1
  46. package/dist/server/handlers/messages-handler.js +13 -1
  47. package/dist/server/handlers/messages-handler.js.map +1 -1
  48. package/dist/server/handlers/responses-handler.js +73 -1
  49. package/dist/server/handlers/responses-handler.js.map +1 -1
  50. package/dist/server/runtime/http-server/daemon-admin/credentials-handler.js +174 -2
  51. package/dist/server/runtime/http-server/daemon-admin/credentials-handler.js.map +1 -1
  52. package/dist/server/runtime/http-server/daemon-admin/providers-handler.js +519 -0
  53. package/dist/server/runtime/http-server/daemon-admin/providers-handler.js.map +1 -1
  54. package/dist/server/runtime/http-server/executor-response.js +6 -0
  55. package/dist/server/runtime/http-server/executor-response.js.map +1 -1
  56. package/dist/server/runtime/http-server/index.d.ts +5 -0
  57. package/dist/server/runtime/http-server/index.js +205 -4
  58. package/dist/server/runtime/http-server/index.js.map +1 -1
  59. package/dist/server/runtime/http-server/middleware.d.ts +2 -0
  60. package/dist/server/runtime/http-server/middleware.js +63 -0
  61. package/dist/server/runtime/http-server/middleware.js.map +1 -1
  62. package/dist/server/runtime/http-server/request-executor.d.ts +2 -0
  63. package/dist/server/runtime/http-server/request-executor.js +57 -10
  64. package/dist/server/runtime/http-server/request-executor.js.map +1 -1
  65. package/dist/server/runtime/http-server/routes.js +38 -1
  66. package/dist/server/runtime/http-server/routes.js.map +1 -1
  67. package/dist/server/runtime/http-server/stats-manager.d.ts +55 -0
  68. package/dist/server/runtime/http-server/stats-manager.js +462 -4
  69. package/dist/server/runtime/http-server/stats-manager.js.map +1 -1
  70. package/dist/server/runtime/http-server/types.d.ts +1 -0
  71. package/dist/token-daemon/token-daemon.d.ts +3 -1
  72. package/dist/token-daemon/token-daemon.js +130 -8
  73. package/dist/token-daemon/token-daemon.js.map +1 -1
  74. package/dist/token-daemon/token-utils.d.ts +1 -0
  75. package/dist/token-daemon/token-utils.js +9 -1
  76. package/dist/token-daemon/token-utils.js.map +1 -1
  77. package/dist/tools/semantic-replay.js +29 -0
  78. package/dist/tools/semantic-replay.js.map +1 -1
  79. package/dist/utils/snapshot-writer.d.ts +2 -0
  80. package/dist/utils/snapshot-writer.js +47 -4
  81. package/dist/utils/snapshot-writer.js.map +1 -1
  82. package/package.json +2 -3
  83. package/scripts/analyze-apply-patch-exec-failures.mjs +153 -0
  84. package/scripts/analyze-apply-patch-samples.mjs +242 -0
  85. package/scripts/analyze-codex-error-failures.mjs +24 -14
  86. package/scripts/classify-codex-samples.mjs +0 -35
  87. package/scripts/copy-modules-config.mjs +17 -1
  88. package/scripts/generate-snapshot-data.mjs +41 -11
  89. package/scripts/mock-provider/extract.mjs +254 -21
  90. package/scripts/mock-provider/run-regressions.mjs +97 -16
  91. package/scripts/quota-dryrun.mjs +124 -0
  92. package/scripts/tests/apply-patch-loop.mjs +5 -1
  93. package/scripts/tests/exec-command-loop.mjs +16 -19
  94. package/scripts/verify-apply-patch.mjs +335 -5
  95. package/scripts/verify-e2e-toolcall.mjs +49 -10
  96. 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 listJsonFiles(root) {
41
- const entries = await fs.readdir(root);
42
- return entries
43
- .filter((name) => name.toLowerCase().endsWith('.json'))
44
- .map((name) => path.join(root, name));
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
- for (const file of files) {
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
- for (const [requestId, groupFiles] of Object.entries(groups)) {
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
- groupFiles,
163
- responsesDir
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) {