@jsonstudio/llms 0.6.802 → 0.6.938
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/bridge/routecodex-adapter.d.ts +74 -0
- package/dist/config-unified/enhanced-path-resolver.d.ts +5 -0
- package/dist/config-unified/unified-config.d.ts +26 -0
- package/dist/conversion/codec-registry.d.ts +10 -0
- package/dist/conversion/codecs/gemini-openai-codec.d.ts +16 -0
- package/dist/conversion/codecs/openai-openai-codec.d.ts +12 -0
- package/dist/conversion/codecs/responses-openai-codec.d.ts +12 -0
- package/dist/conversion/compat/profiles/chat-gemini.json +12 -0
- package/dist/conversion/config/config-manager.d.ts +212 -0
- package/dist/conversion/hub/config/types.d.ts +26 -0
- package/dist/conversion/hub/core/detour-registry.d.ts +9 -0
- package/dist/conversion/hub/core/hub-context.d.ts +21 -0
- package/dist/conversion/hub/core/index.d.ts +3 -0
- package/dist/conversion/hub/core/stage-driver.d.ts +30 -0
- package/dist/conversion/hub/format-adapters/anthropic-format-adapter.d.ts +16 -0
- package/dist/conversion/hub/format-adapters/chat-format-adapter.d.ts +17 -0
- package/dist/conversion/hub/format-adapters/gemini-format-adapter.d.ts +16 -0
- package/dist/conversion/hub/format-adapters/index.d.ts +21 -0
- package/dist/conversion/hub/hub-feature.d.ts +1 -0
- package/dist/conversion/hub/node-support.d.ts +19 -0
- package/dist/conversion/hub/pipeline/compat/compat-pipeline-executor.js +11 -0
- package/dist/conversion/hub/pipeline/compat/compat-types.d.ts +3 -0
- package/dist/conversion/hub/pipeline/hub-pipeline.d.ts +7 -0
- package/dist/conversion/hub/pipeline/hub-pipeline.js +71 -14
- package/dist/conversion/hub/pipeline/stages/req_process/req_process_stage1_tool_governance/index.js +4 -0
- package/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage1_client_remap/index.js +23 -1
- package/dist/conversion/hub/pipelines/inbound.d.ts +22 -0
- package/dist/conversion/hub/pipelines/outbound.d.ts +22 -0
- package/dist/conversion/hub/policy/policy-engine.d.ts +46 -0
- package/dist/conversion/hub/policy/policy-engine.js +176 -0
- package/dist/conversion/hub/policy/protocol-spec.d.ts +50 -0
- package/dist/conversion/hub/policy/protocol-spec.js +105 -0
- package/dist/conversion/hub/process/chat-process.d.ts +32 -0
- package/dist/conversion/hub/registry.d.ts +28 -0
- package/dist/conversion/hub/response/chat-response-utils.d.ts +6 -0
- package/dist/conversion/hub/response/provider-response.js +31 -0
- package/dist/conversion/hub/semantic-mappers/gemini-mapper.d.ts +7 -0
- package/dist/conversion/hub/semantic-mappers/gemini-mapper.js +87 -1
- package/dist/conversion/hub/semantic-mappers/index.d.ts +4 -0
- package/dist/conversion/hub/semantic-mappers/responses-mapper.d.ts +21 -0
- package/dist/conversion/hub/standardized-bridge.d.ts +12 -0
- package/dist/conversion/hub/types/chat-schema.d.ts +112 -0
- package/dist/conversion/hub/types/errors.d.ts +5 -0
- package/dist/conversion/hub/types/format-envelope.d.ts +7 -0
- package/dist/conversion/hub/types/index.d.ts +6 -0
- package/dist/conversion/hub/types/json.d.ts +9 -0
- package/dist/conversion/hub/types/node.d.ts +31 -0
- package/dist/conversion/responses/responses-openai-bridge.js +263 -10
- package/dist/conversion/schema-validator.d.ts +7 -0
- package/dist/conversion/shared/args-mapping.d.ts +18 -0
- package/dist/conversion/shared/chat-request-filters.d.ts +9 -0
- package/dist/conversion/shared/errors.d.ts +1 -1
- package/dist/conversion/shared/gemini-tool-utils.js +61 -0
- package/dist/conversion/shared/jsonish.d.ts +3 -0
- package/dist/conversion/shared/mcp-injection.d.ts +2 -0
- package/dist/conversion/shared/media.d.ts +1 -0
- package/dist/conversion/shared/openai-message-normalize.d.ts +1 -0
- package/dist/conversion/shared/payload-budget.d.ts +13 -0
- package/dist/conversion/shared/reasoning-mapping.d.ts +5 -0
- package/dist/conversion/shared/responses-request-adapter.d.ts +1 -28
- package/dist/conversion/shared/responses-request-adapter.js +1 -430
- package/dist/conversion/shared/snapshot-hooks.js +58 -3
- package/dist/conversion/shared/tool-governor.js +8 -2
- package/dist/conversion/shared/tool-harvester.d.ts +31 -0
- package/dist/conversion/shared/tool-mapping.js +10 -29
- package/dist/conversion/types.d.ts +33 -0
- package/dist/filters/builtin/add-fields-filter.d.ts +8 -0
- package/dist/filters/builtin/blacklist-filter.d.ts +8 -0
- package/dist/filters/builtin/whitelist-filter.d.ts +8 -0
- package/dist/filters/engine.d.ts +16 -0
- package/dist/filters/special/request-tool-choice-policy.d.ts +11 -0
- package/dist/filters/special/response-finish-invariants.d.ts +11 -0
- package/dist/filters/special/response-openai-to-responses-bridge.d.ts +13 -0
- package/dist/filters/special/response-tool-arguments-blacklist.d.ts +12 -0
- package/dist/filters/special/response-tool-arguments-schema-converge.d.ts +13 -0
- package/dist/filters/special/response-tool-arguments-stringify.d.ts +9 -0
- package/dist/filters/special/response-tool-arguments-whitelist.d.ts +11 -0
- package/dist/filters/special/tool-filter-hooks.d.ts +19 -0
- package/dist/filters/special/tool-post-constraints.d.ts +31 -0
- package/dist/filters/types.d.ts +68 -0
- package/dist/filters/utils/fieldmap-loader.d.ts +2 -0
- package/dist/filters/utils/snapshot-writer.d.ts +10 -0
- package/dist/guidance/index.d.ts +3 -0
- package/dist/guidance/index.js +78 -83
- package/dist/http/sse-response.d.ts +22 -0
- package/dist/router/virtual-router/bootstrap.d.ts +6 -0
- package/dist/router/virtual-router/bootstrap.js +49 -5
- package/dist/router/virtual-router/classifier.d.ts +10 -0
- package/dist/router/virtual-router/engine-selection.js +98 -11
- package/dist/router/virtual-router/engine.js +177 -31
- package/dist/router/virtual-router/error-center.d.ts +10 -0
- package/dist/router/virtual-router/features.d.ts +3 -0
- package/dist/router/virtual-router/routing-instructions.d.ts +23 -1
- package/dist/router/virtual-router/routing-instructions.js +120 -30
- package/dist/router/virtual-router/types.d.ts +11 -0
- package/dist/servertool/engine.js +189 -16
- package/dist/servertool/handlers/apply-patch-guard.js +269 -0
- package/dist/servertool/handlers/exec-command-guard.js +558 -0
- package/dist/servertool/handlers/followup-message-trimmer.d.ts +16 -0
- package/dist/servertool/handlers/followup-message-trimmer.js +198 -0
- package/dist/servertool/handlers/followup-request-builder.d.ts +17 -0
- package/dist/servertool/handlers/followup-request-builder.js +122 -0
- package/dist/servertool/handlers/gemini-empty-reply-continue.js +252 -51
- package/dist/servertool/handlers/iflow-model-error-retry.js +12 -22
- package/dist/servertool/handlers/stop-message-auto.js +237 -75
- package/dist/servertool/handlers/vision.js +15 -27
- package/dist/servertool/handlers/web-search.js +17 -43
- package/dist/servertool/server-side-tools.d.ts +3 -0
- package/dist/servertool/server-side-tools.js +3 -0
- package/dist/sse/json-to-sse/anthropic-json-to-sse-converter.d.ts +2 -1
- package/dist/sse/json-to-sse/chat-json-to-sse-converter.d.ts +80 -0
- package/dist/sse/json-to-sse/event-generators/chat.d.ts +55 -0
- package/dist/sse/json-to-sse/event-generators/responses.d.ts +99 -0
- package/dist/sse/json-to-sse/gemini-json-to-sse-converter.d.ts +2 -1
- package/dist/sse/json-to-sse/responses-json-to-sse-converter.d.ts +80 -0
- package/dist/sse/json-to-sse/sequencers/anthropic-sequencer.d.ts +1 -1
- package/dist/sse/json-to-sse/sequencers/chat-sequencer.d.ts +2 -2
- package/dist/sse/json-to-sse/sequencers/gemini-sequencer.d.ts +1 -1
- package/dist/sse/json-to-sse/sequencers/responses-sequencer.d.ts +40 -0
- package/dist/sse/shared/chat-serializer.d.ts +4 -0
- package/dist/sse/shared/constants.d.ts +272 -0
- package/dist/sse/shared/serializers/anthropic-event-serializer.d.ts +1 -1
- package/dist/sse/shared/serializers/base-serializer.d.ts +158 -0
- package/dist/sse/shared/serializers/chat-event-serializer.d.ts +82 -0
- package/dist/sse/shared/serializers/gemini-event-serializer.d.ts +1 -1
- package/dist/sse/shared/serializers/index.d.ts +2 -1
- package/dist/sse/shared/serializers/responses-event-serializer.d.ts +123 -0
- package/dist/sse/shared/serializers/types.d.ts +51 -0
- package/dist/sse/shared/utils.d.ts +254 -0
- package/dist/sse/shared/writer.d.ts +2 -2
- package/dist/sse/sse-to-json/anthropic-sse-to-json-converter.d.ts +1 -1
- package/dist/sse/sse-to-json/builders/anthropic-response-builder.d.ts +1 -1
- package/dist/sse/sse-to-json/builders/response-builder.d.ts +1 -1
- package/dist/sse/sse-to-json/chat-sse-to-json-converter.d.ts +2 -1
- package/dist/sse/sse-to-json/gemini-sse-to-json-converter.d.ts +2 -1
- package/dist/sse/sse-to-json/parsers/sse-parser.d.ts +73 -0
- package/dist/sse/sse-to-json/responses-sse-to-json-converter.d.ts +1 -1
- package/dist/sse/types/chat-types.d.ts +1 -1
- package/dist/sse/types/responses-types.d.ts +1 -1
- package/dist/tools/apply-patch/execution-capturer.d.ts +13 -0
- package/dist/tools/apply-patch/execution-capturer.js +158 -0
- package/dist/tools/apply-patch/regression-capturer.d.ts +1 -0
- package/dist/tools/apply-patch/regression-capturer.js +5 -4
- package/dist/tools/apply-patch/structured.js +109 -13
- package/dist/tools/apply-patch/validator.js +112 -18
- package/dist/tools/tool-registry.d.ts +8 -0
- package/dist/tools/tool-registry.js +2 -1
- package/package.json +4 -4
- package/dist/conversion/compat/actions/apply-patch-format-fixer.js +0 -233
- package/dist/conversion/config/compat-profiles.json +0 -38
- package/dist/conversion/hub/pipeline/context-limit.d.ts +0 -13
- package/dist/conversion/hub/pipeline/context-limit.js +0 -55
- package/dist/conversion/hub/response/server-side-tools.d.ts +0 -26
- package/dist/conversion/hub/response/server-side-tools.js +0 -383
- package/dist/conversion/shared/bridge-conversation-store.d.ts +0 -41
- package/dist/conversion/shared/bridge-conversation-store.js +0 -279
- package/dist/conversion/shared/bridge-request-adapter.d.ts +0 -28
- package/dist/conversion/shared/bridge-request-adapter.js +0 -430
- package/dist/conversion/shared/responses-id-utils.js +0 -42
- package/dist/conversion/shared/responses-instructions.js +0 -113
- package/dist/conversion/shared/responses-message-utils.d.ts +0 -15
- package/dist/conversion/shared/responses-message-utils.js +0 -206
- package/dist/conversion/shared/responses-metadata.js +0 -1
- package/dist/conversion/shared/responses-output-utils.d.ts +0 -7
- package/dist/conversion/shared/responses-output-utils.js +0 -108
- package/dist/conversion/shared/responses-types.d.ts +0 -33
- package/dist/conversion/shared/tool-normalizers.d.ts +0 -4
- package/dist/conversion/shared/tool-normalizers.js +0 -84
- package/dist/filters/special/request-streaming-to-nonstreaming.d.ts +0 -13
- package/dist/filters/special/request-streaming-to-nonstreaming.js +0 -39
- package/dist/filters/special/response-apply-patch-toon-decode.d.ts +0 -23
- package/dist/filters/special/response-apply-patch-toon-decode.js +0 -460
- package/dist/filters/special/response-tool-arguments-toon-decode.d.ts +0 -10
- package/dist/filters/special/response-tool-arguments-toon-decode.js +0 -154
- package/dist/servertool/flow-types.d.ts +0 -40
- package/dist/servertool/flow-types.js +0 -1
- package/dist/servertool/orchestration-types.d.ts +0 -33
- package/dist/servertool/orchestration-types.js +0 -1
- package/dist/servertool/vision-tool.d.ts +0 -2
- package/dist/servertool/vision-tool.js +0 -185
- package/dist/tools/patch-args-normalizer.d.ts +0 -15
- package/dist/tools/patch-args-normalizer.js +0 -472
- package/dist/utils/toon.d.ts +0 -4
- package/dist/utils/toon.js +0 -75
- /package/dist/{conversion/compat/actions/apply-patch-format-fixer.d.ts → servertool/handlers/apply-patch-guard.d.ts} +0 -0
- /package/dist/{conversion/shared/responses-types.js → servertool/handlers/exec-command-guard.d.ts} +0 -0
|
@@ -1,460 +0,0 @@
|
|
|
1
|
-
import { buildStructuredPatch, isStructuredApplyPatchPayload, StructuredApplyPatchError } from '../../tools/apply-patch-structured.js';
|
|
2
|
-
function envEnabled() {
|
|
3
|
-
const v = String(process?.env?.RCC_TOON_ENABLE ||
|
|
4
|
-
process?.env?.ROUTECODEX_TOON_ENABLE ||
|
|
5
|
-
'').toLowerCase();
|
|
6
|
-
if (!v)
|
|
7
|
-
return true;
|
|
8
|
-
return !(v === '0' || v === 'false' || v === 'off');
|
|
9
|
-
}
|
|
10
|
-
function isObject(v) {
|
|
11
|
-
return !!v && typeof v === 'object' && !Array.isArray(v);
|
|
12
|
-
}
|
|
13
|
-
function readString(value) {
|
|
14
|
-
if (typeof value === 'string') {
|
|
15
|
-
const trimmed = value.trim();
|
|
16
|
-
return trimmed ? trimmed : undefined;
|
|
17
|
-
}
|
|
18
|
-
return undefined;
|
|
19
|
-
}
|
|
20
|
-
function stripCodeFences(text) {
|
|
21
|
-
const trimmed = text.trim();
|
|
22
|
-
const fenceRe = /```(?:diff|patch|apply_patch|toon|text|json)?\s*([\s\S]*?)```/gi;
|
|
23
|
-
const candidates = [];
|
|
24
|
-
let match = null;
|
|
25
|
-
while ((match = fenceRe.exec(trimmed))) {
|
|
26
|
-
if (match[1]) {
|
|
27
|
-
candidates.push(match[1].trim());
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
if (!candidates.length) {
|
|
31
|
-
return text;
|
|
32
|
-
}
|
|
33
|
-
for (const candidate of candidates) {
|
|
34
|
-
if (candidate.includes('*** Begin Patch') ||
|
|
35
|
-
candidate.includes('*** Update File:') ||
|
|
36
|
-
candidate.includes('diff --git')) {
|
|
37
|
-
return candidate;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
return candidates[0] ?? text;
|
|
41
|
-
}
|
|
42
|
-
function looksLikePatch(text) {
|
|
43
|
-
if (!text)
|
|
44
|
-
return false;
|
|
45
|
-
const t = text.trim();
|
|
46
|
-
if (!t)
|
|
47
|
-
return false;
|
|
48
|
-
return (t.includes('*** Begin Patch') ||
|
|
49
|
-
t.includes('*** Update File:') ||
|
|
50
|
-
t.includes('*** Add File:') ||
|
|
51
|
-
t.includes('*** Delete File:') ||
|
|
52
|
-
t.includes('diff --git') ||
|
|
53
|
-
/^(?:@@|\+\+\+\s|---\s)/m.test(t));
|
|
54
|
-
}
|
|
55
|
-
function convertGitDiffToApplyPatch(text) {
|
|
56
|
-
const lines = text.replace(/\r\n/g, '\n').split('\n');
|
|
57
|
-
const files = [];
|
|
58
|
-
let current = null;
|
|
59
|
-
let sawDiff = false;
|
|
60
|
-
const flush = () => {
|
|
61
|
-
if (!current)
|
|
62
|
-
return;
|
|
63
|
-
if (current.path && current.kind === 'delete') {
|
|
64
|
-
files.push(current);
|
|
65
|
-
current = null;
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
if (!current.path || (current.kind === 'update' && current.lines.length === 0)) {
|
|
69
|
-
current = null;
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
files.push(current);
|
|
73
|
-
current = null;
|
|
74
|
-
};
|
|
75
|
-
for (const raw of lines) {
|
|
76
|
-
const line = raw;
|
|
77
|
-
const diffMatch = line.match(/^diff --git a\/(.+?) b\/(.+)$/);
|
|
78
|
-
if (diffMatch) {
|
|
79
|
-
sawDiff = true;
|
|
80
|
-
flush();
|
|
81
|
-
const path = diffMatch[2] || diffMatch[1];
|
|
82
|
-
current = { path, kind: 'update', lines: [] };
|
|
83
|
-
continue;
|
|
84
|
-
}
|
|
85
|
-
if (!current) {
|
|
86
|
-
continue;
|
|
87
|
-
}
|
|
88
|
-
if (line.startsWith('new file mode')) {
|
|
89
|
-
current.kind = 'add';
|
|
90
|
-
continue;
|
|
91
|
-
}
|
|
92
|
-
if (line.startsWith('deleted file mode')) {
|
|
93
|
-
current.kind = 'delete';
|
|
94
|
-
continue;
|
|
95
|
-
}
|
|
96
|
-
if (line.startsWith('index ') || line.startsWith('old mode') || line.startsWith('new mode')) {
|
|
97
|
-
continue;
|
|
98
|
-
}
|
|
99
|
-
if (line.startsWith('--- ')) {
|
|
100
|
-
if (line.includes('/dev/null')) {
|
|
101
|
-
current.kind = 'add';
|
|
102
|
-
}
|
|
103
|
-
continue;
|
|
104
|
-
}
|
|
105
|
-
if (line.startsWith('+++ ')) {
|
|
106
|
-
if (line.includes('/dev/null')) {
|
|
107
|
-
current.kind = 'delete';
|
|
108
|
-
}
|
|
109
|
-
else {
|
|
110
|
-
const plusMatch = line.match(/^\+\+\+\s+(?:b\/)?(.+)$/);
|
|
111
|
-
if (plusMatch && plusMatch[1]) {
|
|
112
|
-
current.path = plusMatch[1];
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
continue;
|
|
116
|
-
}
|
|
117
|
-
if (current.kind === 'delete') {
|
|
118
|
-
continue;
|
|
119
|
-
}
|
|
120
|
-
if (current.kind === 'add') {
|
|
121
|
-
if (line.startsWith('+') && !line.startsWith('+++')) {
|
|
122
|
-
current.lines.push(line);
|
|
123
|
-
}
|
|
124
|
-
else if (line.startsWith('\\')) {
|
|
125
|
-
current.lines.push(`+${line}`);
|
|
126
|
-
}
|
|
127
|
-
continue;
|
|
128
|
-
}
|
|
129
|
-
if (line.startsWith('@@') ||
|
|
130
|
-
line.startsWith('+') ||
|
|
131
|
-
line.startsWith('-') ||
|
|
132
|
-
line.startsWith(' ') ||
|
|
133
|
-
line.startsWith('\\')) {
|
|
134
|
-
current.lines.push(line.startsWith('\\') ? ` ${line}` : line);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
flush();
|
|
138
|
-
if (!sawDiff || files.length === 0) {
|
|
139
|
-
return null;
|
|
140
|
-
}
|
|
141
|
-
const output = ['*** Begin Patch'];
|
|
142
|
-
for (const file of files) {
|
|
143
|
-
if (!file.path)
|
|
144
|
-
continue;
|
|
145
|
-
if (file.kind === 'add') {
|
|
146
|
-
output.push(`*** Add File: ${file.path}`);
|
|
147
|
-
for (const line of file.lines) {
|
|
148
|
-
if (line.startsWith('+')) {
|
|
149
|
-
output.push(line);
|
|
150
|
-
}
|
|
151
|
-
else {
|
|
152
|
-
output.push(`+${line}`);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
continue;
|
|
156
|
-
}
|
|
157
|
-
if (file.kind === 'delete') {
|
|
158
|
-
output.push(`*** Delete File: ${file.path}`);
|
|
159
|
-
continue;
|
|
160
|
-
}
|
|
161
|
-
output.push(`*** Update File: ${file.path}`);
|
|
162
|
-
for (const line of file.lines) {
|
|
163
|
-
if (line.startsWith('@@') || line.startsWith('+') || line.startsWith('-') || line.startsWith(' ')) {
|
|
164
|
-
output.push(line);
|
|
165
|
-
}
|
|
166
|
-
else {
|
|
167
|
-
output.push(` ${line}`);
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
output.push('*** End Patch');
|
|
172
|
-
return output.join('\n');
|
|
173
|
-
}
|
|
174
|
-
function normalizeApplyPatchText(raw) {
|
|
175
|
-
if (!raw)
|
|
176
|
-
return raw;
|
|
177
|
-
let text = raw.replace(/\r\n/g, '\n');
|
|
178
|
-
text = stripCodeFences(text);
|
|
179
|
-
text = text.trim();
|
|
180
|
-
if (!text) {
|
|
181
|
-
return raw;
|
|
182
|
-
}
|
|
183
|
-
if (!text.includes('*** Begin Patch') && text.includes('diff --git')) {
|
|
184
|
-
const converted = convertGitDiffToApplyPatch(text);
|
|
185
|
-
if (converted) {
|
|
186
|
-
text = converted;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
if (text.includes('*** Create File:')) {
|
|
190
|
-
text = text.replace(/\*\*\* Create File:/g, '*** Add File:');
|
|
191
|
-
}
|
|
192
|
-
const hasBegin = text.includes('*** Begin Patch');
|
|
193
|
-
const hasEnd = text.includes('*** End Patch');
|
|
194
|
-
if (hasBegin && !hasEnd) {
|
|
195
|
-
text = `${text}\n*** End Patch`;
|
|
196
|
-
}
|
|
197
|
-
if (!hasBegin && /^\*\*\* (Add|Update|Delete) File:/m.test(text)) {
|
|
198
|
-
text = `*** Begin Patch\n${text}\n*** End Patch`;
|
|
199
|
-
}
|
|
200
|
-
if (!text.includes('*** Begin Patch')) {
|
|
201
|
-
return text;
|
|
202
|
-
}
|
|
203
|
-
const lines = text.split('\n');
|
|
204
|
-
const output = [];
|
|
205
|
-
let inUpdateSection = false;
|
|
206
|
-
let afterUpdateHeader = false;
|
|
207
|
-
for (const line of lines) {
|
|
208
|
-
if (line.startsWith('*** Begin Patch')) {
|
|
209
|
-
output.push(line);
|
|
210
|
-
inUpdateSection = false;
|
|
211
|
-
afterUpdateHeader = false;
|
|
212
|
-
continue;
|
|
213
|
-
}
|
|
214
|
-
if (line.startsWith('*** End Patch')) {
|
|
215
|
-
output.push(line);
|
|
216
|
-
inUpdateSection = false;
|
|
217
|
-
afterUpdateHeader = false;
|
|
218
|
-
continue;
|
|
219
|
-
}
|
|
220
|
-
if (line.startsWith('*** Update File:')) {
|
|
221
|
-
output.push(line);
|
|
222
|
-
inUpdateSection = true;
|
|
223
|
-
afterUpdateHeader = true;
|
|
224
|
-
continue;
|
|
225
|
-
}
|
|
226
|
-
if (line.startsWith('*** Add File:') || line.startsWith('*** Delete File:')) {
|
|
227
|
-
output.push(line);
|
|
228
|
-
inUpdateSection = false;
|
|
229
|
-
afterUpdateHeader = false;
|
|
230
|
-
continue;
|
|
231
|
-
}
|
|
232
|
-
if (inUpdateSection) {
|
|
233
|
-
if (afterUpdateHeader && line.trim() === '') {
|
|
234
|
-
continue;
|
|
235
|
-
}
|
|
236
|
-
afterUpdateHeader = false;
|
|
237
|
-
if (line.startsWith('@@') || line.startsWith('+') || line.startsWith('-') || line.startsWith(' ')) {
|
|
238
|
-
output.push(line);
|
|
239
|
-
}
|
|
240
|
-
else {
|
|
241
|
-
output.push(` ${line}`);
|
|
242
|
-
}
|
|
243
|
-
continue;
|
|
244
|
-
}
|
|
245
|
-
output.push(line);
|
|
246
|
-
}
|
|
247
|
-
return output.join('\n');
|
|
248
|
-
}
|
|
249
|
-
function collectFunctionCallAdapters(payload) {
|
|
250
|
-
const adapters = [];
|
|
251
|
-
const anyPayload = payload;
|
|
252
|
-
const choices = Array.isArray(anyPayload.choices)
|
|
253
|
-
? (anyPayload.choices ?? [])
|
|
254
|
-
: [];
|
|
255
|
-
for (const choice of choices) {
|
|
256
|
-
const message = choice && typeof choice === 'object' ? choice.message : undefined;
|
|
257
|
-
const toolCalls = message && Array.isArray(message.tool_calls) ? message.tool_calls : [];
|
|
258
|
-
for (const tc of toolCalls) {
|
|
259
|
-
const fnObj = tc && typeof tc === 'object' ? tc.function : undefined;
|
|
260
|
-
if (fnObj && typeof fnObj === 'object') {
|
|
261
|
-
adapters.push({
|
|
262
|
-
name: typeof fnObj.name === 'string' ? String(fnObj.name) : undefined,
|
|
263
|
-
getArguments: () => fnObj.arguments,
|
|
264
|
-
setArguments: (value) => {
|
|
265
|
-
fnObj.arguments = value;
|
|
266
|
-
}
|
|
267
|
-
});
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
const output = Array.isArray(anyPayload.output) ? anyPayload.output : [];
|
|
272
|
-
for (const item of output) {
|
|
273
|
-
if (!item || typeof item !== 'object')
|
|
274
|
-
continue;
|
|
275
|
-
const type = String(item.type || '').toLowerCase();
|
|
276
|
-
if (type === 'function_call') {
|
|
277
|
-
adapters.push({
|
|
278
|
-
name: typeof item.name === 'string' ? String(item.name) : undefined,
|
|
279
|
-
getArguments: () => item.arguments,
|
|
280
|
-
setArguments: (value) => {
|
|
281
|
-
item.arguments = value;
|
|
282
|
-
}
|
|
283
|
-
});
|
|
284
|
-
continue;
|
|
285
|
-
}
|
|
286
|
-
if (type === 'message' && Array.isArray(item.tool_calls)) {
|
|
287
|
-
for (const tc of item.tool_calls) {
|
|
288
|
-
const fnObj = tc && typeof tc === 'object' ? tc.function : undefined;
|
|
289
|
-
if (!fnObj || typeof fnObj !== 'object')
|
|
290
|
-
continue;
|
|
291
|
-
adapters.push({
|
|
292
|
-
name: typeof fnObj.name === 'string' ? String(fnObj.name) : undefined,
|
|
293
|
-
getArguments: () => fnObj.arguments,
|
|
294
|
-
setArguments: (value) => {
|
|
295
|
-
fnObj.arguments = value;
|
|
296
|
-
}
|
|
297
|
-
});
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
return adapters;
|
|
302
|
-
}
|
|
303
|
-
function buildSingleChangePayload(record) {
|
|
304
|
-
const kindRaw = readString(record.kind);
|
|
305
|
-
if (!kindRaw)
|
|
306
|
-
return undefined;
|
|
307
|
-
const change = {
|
|
308
|
-
kind: kindRaw.toLowerCase(),
|
|
309
|
-
lines: record.lines ?? record.text ?? record.body,
|
|
310
|
-
target: readString(record.target),
|
|
311
|
-
anchor: readString(record.anchor)
|
|
312
|
-
};
|
|
313
|
-
if (typeof record.use_anchor_indent === 'boolean') {
|
|
314
|
-
change.use_anchor_indent = record.use_anchor_indent;
|
|
315
|
-
}
|
|
316
|
-
const changeFile = readString(record.file);
|
|
317
|
-
if (changeFile) {
|
|
318
|
-
change.file = changeFile;
|
|
319
|
-
}
|
|
320
|
-
return {
|
|
321
|
-
file: changeFile,
|
|
322
|
-
changes: [change]
|
|
323
|
-
};
|
|
324
|
-
}
|
|
325
|
-
function coerceStructuredPayload(record) {
|
|
326
|
-
if (isStructuredApplyPatchPayload(record)) {
|
|
327
|
-
return record;
|
|
328
|
-
}
|
|
329
|
-
if (Array.isArray(record.changes) && record.changes.length === 0) {
|
|
330
|
-
return undefined;
|
|
331
|
-
}
|
|
332
|
-
const single = buildSingleChangePayload(record);
|
|
333
|
-
if (single) {
|
|
334
|
-
return single;
|
|
335
|
-
}
|
|
336
|
-
return undefined;
|
|
337
|
-
}
|
|
338
|
-
function maybeNormalizeArguments(argIn) {
|
|
339
|
-
let parsed;
|
|
340
|
-
let patchText;
|
|
341
|
-
if (typeof argIn === 'string') {
|
|
342
|
-
const trimmed = argIn.trim();
|
|
343
|
-
if (!trimmed)
|
|
344
|
-
return undefined;
|
|
345
|
-
try {
|
|
346
|
-
parsed = JSON.parse(trimmed);
|
|
347
|
-
}
|
|
348
|
-
catch {
|
|
349
|
-
if (looksLikePatch(trimmed)) {
|
|
350
|
-
patchText = trimmed;
|
|
351
|
-
}
|
|
352
|
-
else {
|
|
353
|
-
const stripped = stripCodeFences(trimmed);
|
|
354
|
-
if (looksLikePatch(stripped)) {
|
|
355
|
-
patchText = stripped;
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
else if (isObject(argIn)) {
|
|
361
|
-
parsed = argIn;
|
|
362
|
-
}
|
|
363
|
-
else {
|
|
364
|
-
return undefined;
|
|
365
|
-
}
|
|
366
|
-
const record = parsed ?? {};
|
|
367
|
-
if (!patchText) {
|
|
368
|
-
const toon = readString(record.toon);
|
|
369
|
-
if (looksLikePatch(toon)) {
|
|
370
|
-
patchText = toon;
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
if (!patchText) {
|
|
374
|
-
const payload = coerceStructuredPayload(record);
|
|
375
|
-
if (payload) {
|
|
376
|
-
try {
|
|
377
|
-
patchText = buildStructuredPatch(payload);
|
|
378
|
-
}
|
|
379
|
-
catch (error) {
|
|
380
|
-
if (!(error instanceof StructuredApplyPatchError)) {
|
|
381
|
-
/* ignore */
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
if (!patchText) {
|
|
387
|
-
const patchField = readString(record.patch);
|
|
388
|
-
if (looksLikePatch(patchField)) {
|
|
389
|
-
patchText = patchField;
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
if (!patchText) {
|
|
393
|
-
const inputField = readString(record.input);
|
|
394
|
-
if (looksLikePatch(inputField)) {
|
|
395
|
-
patchText = inputField;
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
if (!patchText) {
|
|
399
|
-
return undefined;
|
|
400
|
-
}
|
|
401
|
-
const normalizedText = normalizeApplyPatchText(patchText);
|
|
402
|
-
if (!looksLikePatch(normalizedText)) {
|
|
403
|
-
return undefined;
|
|
404
|
-
}
|
|
405
|
-
return JSON.stringify({ input: normalizedText, patch: normalizedText });
|
|
406
|
-
}
|
|
407
|
-
/**
|
|
408
|
-
* Response-side apply_patch arguments 规范化(TOON + 结构化 JSON → 统一 diff 文本)。
|
|
409
|
-
*
|
|
410
|
-
* 目标:
|
|
411
|
-
* - 对上游模型:仍然可以使用结构化 JSON(changes 数组)或 toon 字段输出补丁;
|
|
412
|
-
* - 对下游客户端(Codex CLI 等):始终看到 { input, patch } 形式的统一 diff 文本,
|
|
413
|
-
* 与本地 apply_patch 工具的旧语义完全一致(对客户端透明)。
|
|
414
|
-
*
|
|
415
|
-
* 支持两种入参形态(arguments 反序列化后):
|
|
416
|
-
* 1) { toon: "<*** Begin Patch ... *** End Patch>" }
|
|
417
|
-
* 2) StructuredApplyPatchPayload(含 changes 数组等字段)
|
|
418
|
-
*
|
|
419
|
-
* 输出:
|
|
420
|
-
* - arguments 统一替换为 JSON 字符串:{ input: patchText, patch: patchText }。
|
|
421
|
-
*
|
|
422
|
-
* Stage: response_pre(在 arguments stringify 之前运行)。
|
|
423
|
-
*/
|
|
424
|
-
export class ResponseApplyPatchToonDecodeFilter {
|
|
425
|
-
name = 'response_apply_patch_toon_decode';
|
|
426
|
-
stage = 'response_pre';
|
|
427
|
-
apply(input) {
|
|
428
|
-
if (!envEnabled())
|
|
429
|
-
return { ok: true, data: input };
|
|
430
|
-
try {
|
|
431
|
-
const out = JSON.parse(JSON.stringify(input || {}));
|
|
432
|
-
const adapters = collectFunctionCallAdapters(out);
|
|
433
|
-
for (const adapter of adapters) {
|
|
434
|
-
try {
|
|
435
|
-
const name = adapter.name ? adapter.name.trim().toLowerCase() : '';
|
|
436
|
-
if (name !== 'apply_patch') {
|
|
437
|
-
continue;
|
|
438
|
-
}
|
|
439
|
-
const normalizedArgs = maybeNormalizeArguments(adapter.getArguments());
|
|
440
|
-
if (!normalizedArgs) {
|
|
441
|
-
continue;
|
|
442
|
-
}
|
|
443
|
-
try {
|
|
444
|
-
adapter.setArguments(normalizedArgs);
|
|
445
|
-
}
|
|
446
|
-
catch {
|
|
447
|
-
// ignore single adapter failures
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
catch {
|
|
451
|
-
// 单个 tool_call best-effort
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
return { ok: true, data: out };
|
|
455
|
-
}
|
|
456
|
-
catch {
|
|
457
|
-
return { ok: true, data: input };
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { Filter, FilterContext, FilterResult, JsonObject } from '../types.js';
|
|
2
|
-
/**
|
|
3
|
-
* Decode arguments.toon to standard JSON ({command, workdir?}) and map tool name 'shell_toon' → 'shell'.
|
|
4
|
-
* Stage: response_pre (before arguments stringify and invariants).
|
|
5
|
-
*/
|
|
6
|
-
export declare class ResponseToolArgumentsToonDecodeFilter implements Filter<JsonObject> {
|
|
7
|
-
readonly name = "response_tool_arguments_toon_decode";
|
|
8
|
-
readonly stage: FilterContext['stage'];
|
|
9
|
-
apply(input: JsonObject, ctx: FilterContext): FilterResult<JsonObject>;
|
|
10
|
-
}
|
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
import { isShellToolName, normalizeToolName } from '../../tools/tool-description-utils.js';
|
|
2
|
-
import { repairFindMeta } from '../../conversion/shared/tooling.js';
|
|
3
|
-
import { decodeToonToKeyValue, coerceToonValue } from '../../utils/toon.js';
|
|
4
|
-
function envEnabled() {
|
|
5
|
-
// Default ON. Allow disabling via env RCC_TOON_ENABLE/ROUTECODEX_TOON_ENABLE = 0|false|off
|
|
6
|
-
const v = String(process?.env?.RCC_TOON_ENABLE || process?.env?.ROUTECODEX_TOON_ENABLE || '').toLowerCase();
|
|
7
|
-
if (!v)
|
|
8
|
-
return true;
|
|
9
|
-
return !(v === '0' || v === 'false' || v === 'off');
|
|
10
|
-
}
|
|
11
|
-
function isObject(v) { return !!v && typeof v === 'object' && !Array.isArray(v); }
|
|
12
|
-
/**
|
|
13
|
-
* Decode arguments.toon to standard JSON ({command, workdir?}) and map tool name 'shell_toon' → 'shell'.
|
|
14
|
-
* Stage: response_pre (before arguments stringify and invariants).
|
|
15
|
-
*/
|
|
16
|
-
export class ResponseToolArgumentsToonDecodeFilter {
|
|
17
|
-
name = 'response_tool_arguments_toon_decode';
|
|
18
|
-
stage = 'response_pre';
|
|
19
|
-
apply(input, ctx) {
|
|
20
|
-
if (!envEnabled())
|
|
21
|
-
return { ok: true, data: input };
|
|
22
|
-
try {
|
|
23
|
-
const out = JSON.parse(JSON.stringify(input || {}));
|
|
24
|
-
const warnings = [];
|
|
25
|
-
const choices = Array.isArray(out.choices) ? out.choices : [];
|
|
26
|
-
for (const ch of choices) {
|
|
27
|
-
const msg = ch && ch.message ? ch.message : undefined;
|
|
28
|
-
const tcs = msg && Array.isArray(msg.tool_calls) ? msg.tool_calls : [];
|
|
29
|
-
for (const tc of tcs) {
|
|
30
|
-
try {
|
|
31
|
-
const fn = tc && tc.function ? tc.function : undefined;
|
|
32
|
-
if (!fn || typeof fn !== 'object')
|
|
33
|
-
continue;
|
|
34
|
-
const rawName = fn.name;
|
|
35
|
-
const toolName = typeof rawName === 'string' ? rawName : '';
|
|
36
|
-
const normalizedName = normalizeToolName(toolName);
|
|
37
|
-
const isShellLike = isShellToolName(toolName);
|
|
38
|
-
const argIn = fn.arguments;
|
|
39
|
-
let parsed = undefined;
|
|
40
|
-
if (typeof argIn === 'string') {
|
|
41
|
-
try {
|
|
42
|
-
parsed = JSON.parse(argIn);
|
|
43
|
-
}
|
|
44
|
-
catch {
|
|
45
|
-
parsed = undefined;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
else if (isObject(argIn)) {
|
|
49
|
-
parsed = argIn;
|
|
50
|
-
}
|
|
51
|
-
if (!isObject(parsed))
|
|
52
|
-
continue;
|
|
53
|
-
const toon = parsed.toon;
|
|
54
|
-
if (typeof toon !== 'string' || !toon.trim())
|
|
55
|
-
continue;
|
|
56
|
-
const kv = decodeToonToKeyValue(toon);
|
|
57
|
-
if (!kv) {
|
|
58
|
-
const preview = toon.split(/\r?\n/).slice(0, 5).join('\n');
|
|
59
|
-
const warnMsg = `response_tool_arguments_toon_decode: failed to decode TOON arguments for tool "${fn.name ?? 'unknown'}"`;
|
|
60
|
-
warnings.push(warnMsg);
|
|
61
|
-
if (ctx?.debug?.emit) {
|
|
62
|
-
ctx.debug.emit('tool_toon_decode_error', {
|
|
63
|
-
requestId: ctx.requestId,
|
|
64
|
-
model: ctx.model,
|
|
65
|
-
endpoint: ctx.endpoint,
|
|
66
|
-
stage: ctx.stage,
|
|
67
|
-
provider: ctx.provider,
|
|
68
|
-
toolName: fn.name ?? 'unknown',
|
|
69
|
-
message: warnMsg,
|
|
70
|
-
toonPreview: preview
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
continue; // keep original if decode fails
|
|
74
|
-
}
|
|
75
|
-
if (isShellLike) {
|
|
76
|
-
const commandRaw = (typeof kv['command'] === 'string' && kv['command'].trim()
|
|
77
|
-
? kv['command']
|
|
78
|
-
: typeof kv['cmd'] === 'string' && kv['cmd'].trim()
|
|
79
|
-
? kv['cmd']
|
|
80
|
-
: undefined) ?? '';
|
|
81
|
-
const workdirRaw = (typeof kv['workdir'] === 'string' && kv['workdir'].trim()
|
|
82
|
-
? kv['workdir']
|
|
83
|
-
: typeof kv['cwd'] === 'string' && kv['cwd'].trim()
|
|
84
|
-
? kv['cwd']
|
|
85
|
-
: undefined) ?? '';
|
|
86
|
-
const timeoutRaw = typeof kv['timeout_ms'] === 'string' ? kv['timeout_ms'] : undefined;
|
|
87
|
-
const escalatedRaw = typeof kv['with_escalated_permissions'] === 'string'
|
|
88
|
-
? kv['with_escalated_permissions']
|
|
89
|
-
: undefined;
|
|
90
|
-
const justificationRaw = typeof kv['justification'] === 'string' ? kv['justification'] : undefined;
|
|
91
|
-
const command = repairFindMeta(commandRaw.trim());
|
|
92
|
-
if (command) {
|
|
93
|
-
const merged = {
|
|
94
|
-
cmd: command,
|
|
95
|
-
command
|
|
96
|
-
};
|
|
97
|
-
const workdir = workdirRaw.trim();
|
|
98
|
-
if (workdir) {
|
|
99
|
-
merged.workdir = workdir;
|
|
100
|
-
}
|
|
101
|
-
if (timeoutRaw) {
|
|
102
|
-
const timeoutNum = Number(timeoutRaw);
|
|
103
|
-
if (Number.isFinite(timeoutNum)) {
|
|
104
|
-
merged.timeout_ms = timeoutNum;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
if (escalatedRaw) {
|
|
108
|
-
const escLower = escalatedRaw.trim().toLowerCase();
|
|
109
|
-
if (escLower === 'true' || escLower === 'yes' || escLower === '1') {
|
|
110
|
-
merged.with_escalated_permissions = true;
|
|
111
|
-
}
|
|
112
|
-
else if (escLower === 'false' || escLower === 'no' || escLower === '0') {
|
|
113
|
-
merged.with_escalated_permissions = false;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
if (justificationRaw && justificationRaw.trim()) {
|
|
117
|
-
merged.justification = justificationRaw;
|
|
118
|
-
}
|
|
119
|
-
try {
|
|
120
|
-
fn.arguments = JSON.stringify(merged);
|
|
121
|
-
}
|
|
122
|
-
catch {
|
|
123
|
-
/* keep original */
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
if (typeof fn.name === 'string' && fn.name === 'shell_toon') {
|
|
127
|
-
fn.name = 'shell';
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
else {
|
|
131
|
-
// 通用 TOON → JSON 解码:除 shell / apply_patch 以外的工具,将 key: value 对映射为普通 JSON 字段。
|
|
132
|
-
const merged = {};
|
|
133
|
-
for (const [key, value] of Object.entries(kv)) {
|
|
134
|
-
merged[key] = coerceToonValue(value);
|
|
135
|
-
}
|
|
136
|
-
try {
|
|
137
|
-
fn.arguments = JSON.stringify(merged);
|
|
138
|
-
}
|
|
139
|
-
catch {
|
|
140
|
-
// keep original on stringify failure
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
catch { /* one tool_call decode failure → ignore */ }
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
out.choices = choices;
|
|
148
|
-
return warnings.length ? { ok: true, data: out, warnings } : { ok: true, data: out };
|
|
149
|
-
}
|
|
150
|
-
catch {
|
|
151
|
-
return { ok: true, data: input };
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import type { JsonObject } from '../conversion/hub/types/json.js';
|
|
2
|
-
import type { ServerToolOrchestrationOptions, ServerToolOrchestrationResult } from './orchestration-types.js';
|
|
3
|
-
export interface ServerToolFlowContext {
|
|
4
|
-
options: ServerToolOrchestrationOptions;
|
|
5
|
-
baseChatResponse: JsonObject;
|
|
6
|
-
capturedChatRequest?: JsonObject;
|
|
7
|
-
routeId?: string;
|
|
8
|
-
cache: Record<string, unknown>;
|
|
9
|
-
}
|
|
10
|
-
export interface ServerToolHopResult {
|
|
11
|
-
body?: JsonObject;
|
|
12
|
-
__sse_responses?: unknown;
|
|
13
|
-
format?: string;
|
|
14
|
-
}
|
|
15
|
-
export interface ServerToolReenterCallOptions {
|
|
16
|
-
requestIdSuffix: string;
|
|
17
|
-
body: JsonObject;
|
|
18
|
-
entryEndpoint?: string;
|
|
19
|
-
metadata?: JsonObject;
|
|
20
|
-
}
|
|
21
|
-
export interface ServerToolProviderCallOptions {
|
|
22
|
-
requestIdSuffix: string;
|
|
23
|
-
providerKey: string;
|
|
24
|
-
providerProtocol: string;
|
|
25
|
-
entryEndpoint: string;
|
|
26
|
-
payload: JsonObject;
|
|
27
|
-
modelId?: string;
|
|
28
|
-
routeHint?: string;
|
|
29
|
-
}
|
|
30
|
-
export interface ServerToolFlowHelpers {
|
|
31
|
-
makeRequestId: (suffix: string) => string;
|
|
32
|
-
callReenterHop: (options: ServerToolReenterCallOptions) => Promise<ServerToolHopResult | null>;
|
|
33
|
-
callProviderHop: (options: ServerToolProviderCallOptions) => Promise<ServerToolHopResult | null>;
|
|
34
|
-
getRouteHintForFollowup: (exclude?: string) => string | undefined;
|
|
35
|
-
}
|
|
36
|
-
export interface ServerToolFlow {
|
|
37
|
-
id: string;
|
|
38
|
-
shouldRun: (context: ServerToolFlowContext) => Promise<boolean> | boolean;
|
|
39
|
-
run: (context: ServerToolFlowContext, helpers: ServerToolFlowHelpers) => Promise<ServerToolOrchestrationResult | null>;
|
|
40
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|