@askalf/dario 3.30.6 → 3.30.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.d.ts +17 -1
- package/dist/cli.js +88 -1
- package/dist/proxy.d.ts +44 -2
- package/dist/proxy.js +67 -13
- package/package.json +2 -2
package/dist/cli.d.ts
CHANGED
|
@@ -9,4 +9,20 @@
|
|
|
9
9
|
* dario refresh — Force token refresh
|
|
10
10
|
* dario logout — Remove saved credentials
|
|
11
11
|
*/
|
|
12
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Parse a boolean env var. Accepts "1", "true", "yes", "on" (case-insensitive)
|
|
14
|
+
* as truthy; everything else (including unset) is undefined/false. Exported
|
|
15
|
+
* for tests. Used by dario#77 DARIO_STRICT_TEMPLATE / DARIO_NO_LIVE_CAPTURE
|
|
16
|
+
* and any future boolean env mirror.
|
|
17
|
+
*/
|
|
18
|
+
export declare function parseBooleanEnv(value: string | undefined): boolean | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* Parse --preserve-orchestration-tags (bare or =value) + env mirror.
|
|
21
|
+
* Exported for tests.
|
|
22
|
+
* dario#78.
|
|
23
|
+
*
|
|
24
|
+
* undefined — flag not passed + env unset → strip all (default)
|
|
25
|
+
* Set(['*']) — flag bare OR value "*" → preserve all
|
|
26
|
+
* Set(['thinking','env']) — value "thinking,env" → preserve listed
|
|
27
|
+
*/
|
|
28
|
+
export declare function resolvePreserveOrchestrationTags(args: string[], env: string | undefined): Set<string> | undefined;
|
package/dist/cli.js
CHANGED
|
@@ -229,6 +229,27 @@ async function proxy() {
|
|
|
229
229
|
const sessionRotateJitterMs = parsePositiveIntFlag('--session-rotate-jitter=');
|
|
230
230
|
const sessionMaxAgeMs = parsePositiveIntFlag('--session-max-age=');
|
|
231
231
|
const sessionPerClient = args.includes('--session-per-client') || undefined;
|
|
232
|
+
// --preserve-orchestration-tags (bare OR =tag1,tag2,...) — opt-out for
|
|
233
|
+
// workflows that legitimately need <system-reminder>, <thinking>, etc.
|
|
234
|
+
// preserved on the wire. Default behaviour unchanged (strip all).
|
|
235
|
+
// dario#78 (Gemini review push-back). Env mirror:
|
|
236
|
+
// DARIO_PRESERVE_ORCHESTRATION_TAGS=* (preserve all)
|
|
237
|
+
// DARIO_PRESERVE_ORCHESTRATION_TAGS=thinking,env (preserve listed)
|
|
238
|
+
const preserveOrchestrationTags = resolvePreserveOrchestrationTags(args, process.env['DARIO_PRESERVE_ORCHESTRATION_TAGS']);
|
|
239
|
+
// --no-live-capture / --strict-template — template fail-closed knobs.
|
|
240
|
+
// Convergent push-back from Grok + GPT in reviews/: drift resilience
|
|
241
|
+
// should be opt-in-verifiable, not silently best-effort. dario#77.
|
|
242
|
+
// --no-live-capture → skip the background CC capture entirely, use
|
|
243
|
+
// the bundled snapshot; for air-gapped / CI.
|
|
244
|
+
// --strict-template → refuse to start if the loaded template is
|
|
245
|
+
// bundled (no live capture) or drifted from
|
|
246
|
+
// the installed CC; same shape as --strict-tls.
|
|
247
|
+
const noLiveCapture = args.includes('--no-live-capture')
|
|
248
|
+
|| parseBooleanEnv(process.env['DARIO_NO_LIVE_CAPTURE'])
|
|
249
|
+
|| undefined;
|
|
250
|
+
const strictTemplate = args.includes('--strict-template')
|
|
251
|
+
|| parseBooleanEnv(process.env['DARIO_STRICT_TEMPLATE'])
|
|
252
|
+
|| undefined;
|
|
232
253
|
// Non-loopback bind without DARIO_API_KEY turns dario into an open
|
|
233
254
|
// OAuth-subscription relay for anyone on the reachable network. Refuse
|
|
234
255
|
// to start rather than rely on the operator to read the startup banner.
|
|
@@ -248,7 +269,49 @@ async function proxy() {
|
|
|
248
269
|
console.error(`[dario] Override (not recommended): pass --unsafe-no-auth if you have out-of-band network controls and accept the risk.`);
|
|
249
270
|
process.exit(1);
|
|
250
271
|
}
|
|
251
|
-
await startProxy({ port, host, verbose, verboseBodies, model, passthrough, preserveTools, hybridTools, noAutoDetect, strictTls, pacingMinMs, pacingJitterMs, drainOnClose, sessionIdleRotateMs, sessionRotateJitterMs, sessionMaxAgeMs, sessionPerClient });
|
|
272
|
+
await startProxy({ port, host, verbose, verboseBodies, model, passthrough, preserveTools, hybridTools, noAutoDetect, strictTls, pacingMinMs, pacingJitterMs, drainOnClose, sessionIdleRotateMs, sessionRotateJitterMs, sessionMaxAgeMs, sessionPerClient, preserveOrchestrationTags, noLiveCapture, strictTemplate });
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Parse a boolean env var. Accepts "1", "true", "yes", "on" (case-insensitive)
|
|
276
|
+
* as truthy; everything else (including unset) is undefined/false. Exported
|
|
277
|
+
* for tests. Used by dario#77 DARIO_STRICT_TEMPLATE / DARIO_NO_LIVE_CAPTURE
|
|
278
|
+
* and any future boolean env mirror.
|
|
279
|
+
*/
|
|
280
|
+
export function parseBooleanEnv(value) {
|
|
281
|
+
if (value === undefined)
|
|
282
|
+
return undefined;
|
|
283
|
+
const normalized = value.trim().toLowerCase();
|
|
284
|
+
if (normalized === '1' || normalized === 'true' || normalized === 'yes' || normalized === 'on')
|
|
285
|
+
return true;
|
|
286
|
+
return undefined;
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Parse --preserve-orchestration-tags (bare or =value) + env mirror.
|
|
290
|
+
* Exported for tests.
|
|
291
|
+
* dario#78.
|
|
292
|
+
*
|
|
293
|
+
* undefined — flag not passed + env unset → strip all (default)
|
|
294
|
+
* Set(['*']) — flag bare OR value "*" → preserve all
|
|
295
|
+
* Set(['thinking','env']) — value "thinking,env" → preserve listed
|
|
296
|
+
*/
|
|
297
|
+
export function resolvePreserveOrchestrationTags(args, env) {
|
|
298
|
+
// Explicit --preserve-orchestration-tags=value wins over everything.
|
|
299
|
+
const withValue = args.find(a => a.startsWith('--preserve-orchestration-tags='));
|
|
300
|
+
if (withValue)
|
|
301
|
+
return parsePreserveTagsValue(withValue.split('=').slice(1).join('='));
|
|
302
|
+
// Bare flag = preserve all.
|
|
303
|
+
if (args.includes('--preserve-orchestration-tags'))
|
|
304
|
+
return new Set(['*']);
|
|
305
|
+
// Env mirror — explicit flag always wins, checked last.
|
|
306
|
+
if (env !== undefined)
|
|
307
|
+
return parsePreserveTagsValue(env);
|
|
308
|
+
return undefined;
|
|
309
|
+
}
|
|
310
|
+
function parsePreserveTagsValue(value) {
|
|
311
|
+
const trimmed = value.trim();
|
|
312
|
+
if (trimmed === '' || trimmed === '*')
|
|
313
|
+
return new Set(['*']);
|
|
314
|
+
return new Set(trimmed.split(',').map(s => s.trim()).filter(Boolean));
|
|
252
315
|
}
|
|
253
316
|
function parsePositiveIntFlag(prefix) {
|
|
254
317
|
const found = args.find(a => a.startsWith(prefix));
|
|
@@ -561,6 +624,30 @@ async function help() {
|
|
|
561
624
|
header) its own rotated session id.
|
|
562
625
|
Default: off (single session across all
|
|
563
626
|
clients, v3.27 behaviour). (v3.28)
|
|
627
|
+
--preserve-orchestration-tags[=TAG,TAG]
|
|
628
|
+
Opt specific orchestration wrapper tags
|
|
629
|
+
(<system-reminder>, <env>, <thinking>,
|
|
630
|
+
etc.) out of the scrub. Bare flag =
|
|
631
|
+
preserve all. Value form = preserve only
|
|
632
|
+
those listed; everything else is still
|
|
633
|
+
stripped. Default: strip every tag in
|
|
634
|
+
ORCHESTRATION_TAG_NAMES. Env mirror:
|
|
635
|
+
DARIO_PRESERVE_ORCHESTRATION_TAGS=*
|
|
636
|
+
or =tag1,tag2. (v3.30.7, dario#78)
|
|
637
|
+
--no-live-capture Skip the background live-fingerprint
|
|
638
|
+
refresh entirely. dario uses the bundled
|
|
639
|
+
snapshot and will NOT spawn the installed
|
|
640
|
+
Claude Code binary. For air-gapped /
|
|
641
|
+
reproducible-build / CI-harness runs.
|
|
642
|
+
Env: DARIO_NO_LIVE_CAPTURE=1.
|
|
643
|
+
(v3.30.8, dario#77)
|
|
644
|
+
--strict-template Refuse to start if the loaded template
|
|
645
|
+
is the bundled snapshot (no live capture
|
|
646
|
+
ever succeeded) or drifts from the
|
|
647
|
+
installed CC version. Same philosophy
|
|
648
|
+
as --strict-tls: make the unsafe state
|
|
649
|
+
require intent. Env: DARIO_STRICT_TEMPLATE=1.
|
|
650
|
+
(v3.30.8, dario#77)
|
|
564
651
|
--port=PORT Port to listen on (default: 3456)
|
|
565
652
|
--host=ADDRESS Address to bind to (default: 127.0.0.1)
|
|
566
653
|
Use 0.0.0.0 for LAN; see README for DARIO_API_KEY
|
package/dist/proxy.d.ts
CHANGED
|
@@ -3,8 +3,29 @@ export declare function parseProviderPrefix(model: string): {
|
|
|
3
3
|
provider: 'openai' | 'claude';
|
|
4
4
|
model: string;
|
|
5
5
|
} | null;
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
export declare const ORCHESTRATION_TAG_NAMES: string[];
|
|
7
|
+
/**
|
|
8
|
+
* Build the regex list that actually strips orchestration tags.
|
|
9
|
+
*
|
|
10
|
+
* `preserveTags` selects which tags to KEEP in the outbound body.
|
|
11
|
+
* undefined → strip every tag in ORCHESTRATION_TAG_NAMES (default)
|
|
12
|
+
* Set(['*']) → preserve all tags (strip none)
|
|
13
|
+
* Set(['thinking']) → strip everything except `<thinking>...</thinking>`
|
|
14
|
+
*
|
|
15
|
+
* Each tag produces two patterns — the wrapper form (`<tag>...</tag>`) and
|
|
16
|
+
* the self-closing form (`<tag ... />`) — so callers that emit either shape
|
|
17
|
+
* get the same treatment.
|
|
18
|
+
*
|
|
19
|
+
* dario#78 (Gemini review push-back).
|
|
20
|
+
*/
|
|
21
|
+
export declare function buildOrchestrationPatterns(preserveTags?: Set<string>): RegExp[];
|
|
22
|
+
/**
|
|
23
|
+
* Strip orchestration tags from all messages in a request body.
|
|
24
|
+
*
|
|
25
|
+
* Pass `preserveTags` (a Set of tag names, or `Set(['*'])` for all) to
|
|
26
|
+
* opt any tag out of the scrub. dario#78.
|
|
27
|
+
*/
|
|
28
|
+
export declare function sanitizeMessages(body: Record<string, unknown>, preserveTags?: Set<string>): void;
|
|
8
29
|
interface ProxyOptions {
|
|
9
30
|
port?: number;
|
|
10
31
|
host?: string;
|
|
@@ -23,6 +44,27 @@ interface ProxyOptions {
|
|
|
23
44
|
sessionRotateJitterMs?: number;
|
|
24
45
|
sessionMaxAgeMs?: number;
|
|
25
46
|
sessionPerClient?: boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Opt specific orchestration tags out of the scrub. Undefined = strip all
|
|
49
|
+
* (default, v3.30 and earlier behaviour). Set(['*']) = preserve all.
|
|
50
|
+
* Set(['thinking','env']) = strip everything except those two. dario#78.
|
|
51
|
+
*/
|
|
52
|
+
preserveOrchestrationTags?: Set<string>;
|
|
53
|
+
/**
|
|
54
|
+
* Skip the background live-fingerprint refresh entirely. Use the bundled
|
|
55
|
+
* snapshot even when a live capture would have been possible. For
|
|
56
|
+
* air-gapped / reproducible-build / CI-harness operators who want no
|
|
57
|
+
* subprocess capture of the installed CC binary. dario#77.
|
|
58
|
+
*/
|
|
59
|
+
noLiveCapture?: boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Fail-closed mode for the template. If the loaded template is the
|
|
62
|
+
* bundled snapshot (live capture has never been run or failed), or if
|
|
63
|
+
* it's a live cache that drifts from the installed CC, refuse to start
|
|
64
|
+
* rather than silently serve the stale shape. Same philosophy as
|
|
65
|
+
* --strict-tls. dario#77.
|
|
66
|
+
*/
|
|
67
|
+
strictTemplate?: boolean;
|
|
26
68
|
}
|
|
27
69
|
export declare function sanitizeError(err: unknown): string;
|
|
28
70
|
/**
|
package/dist/proxy.js
CHANGED
|
@@ -190,38 +190,66 @@ function filterBillableBetas(betas) {
|
|
|
190
190
|
}
|
|
191
191
|
// Orchestration tags injected by agents (Aider, Cursor, OpenCode, etc.)
|
|
192
192
|
// that confuse Claude when passed through. Strip before forwarding.
|
|
193
|
-
const ORCHESTRATION_TAG_NAMES = [
|
|
193
|
+
export const ORCHESTRATION_TAG_NAMES = [
|
|
194
194
|
'system-reminder', 'env', 'system_information', 'current_working_directory',
|
|
195
195
|
'operating_system', 'default_shell', 'home_directory', 'task_metadata',
|
|
196
196
|
'directories', 'thinking',
|
|
197
197
|
'agent_persona', 'agent_context', 'tool_context', 'persona', 'tool_call',
|
|
198
198
|
];
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
199
|
+
/**
|
|
200
|
+
* Build the regex list that actually strips orchestration tags.
|
|
201
|
+
*
|
|
202
|
+
* `preserveTags` selects which tags to KEEP in the outbound body.
|
|
203
|
+
* undefined → strip every tag in ORCHESTRATION_TAG_NAMES (default)
|
|
204
|
+
* Set(['*']) → preserve all tags (strip none)
|
|
205
|
+
* Set(['thinking']) → strip everything except `<thinking>...</thinking>`
|
|
206
|
+
*
|
|
207
|
+
* Each tag produces two patterns — the wrapper form (`<tag>...</tag>`) and
|
|
208
|
+
* the self-closing form (`<tag ... />`) — so callers that emit either shape
|
|
209
|
+
* get the same treatment.
|
|
210
|
+
*
|
|
211
|
+
* dario#78 (Gemini review push-back).
|
|
212
|
+
*/
|
|
213
|
+
export function buildOrchestrationPatterns(preserveTags) {
|
|
214
|
+
if (preserveTags?.has('*'))
|
|
215
|
+
return [];
|
|
216
|
+
const effective = preserveTags
|
|
217
|
+
? ORCHESTRATION_TAG_NAMES.filter(tag => !preserveTags.has(tag))
|
|
218
|
+
: ORCHESTRATION_TAG_NAMES;
|
|
219
|
+
return effective.flatMap(tag => [
|
|
220
|
+
new RegExp(`<${tag}\\b[^>]*>[\\s\\S]*?<\\/${tag}>`, 'gi'),
|
|
221
|
+
new RegExp(`<${tag}\\b[^>]*\\/>`, 'gi'),
|
|
222
|
+
]);
|
|
223
|
+
}
|
|
224
|
+
const ORCHESTRATION_PATTERNS_DEFAULT = buildOrchestrationPatterns();
|
|
203
225
|
/** Strip orchestration wrapper tags from message content. */
|
|
204
|
-
function sanitizeContent(text) {
|
|
226
|
+
function sanitizeContent(text, patterns) {
|
|
205
227
|
let result = text;
|
|
206
|
-
for (const pattern of
|
|
228
|
+
for (const pattern of patterns) {
|
|
207
229
|
pattern.lastIndex = 0;
|
|
208
230
|
result = result.replace(pattern, '');
|
|
209
231
|
}
|
|
210
232
|
return result.replace(/\n{3,}/g, '\n\n').trim();
|
|
211
233
|
}
|
|
212
|
-
/**
|
|
213
|
-
|
|
234
|
+
/**
|
|
235
|
+
* Strip orchestration tags from all messages in a request body.
|
|
236
|
+
*
|
|
237
|
+
* Pass `preserveTags` (a Set of tag names, or `Set(['*'])` for all) to
|
|
238
|
+
* opt any tag out of the scrub. dario#78.
|
|
239
|
+
*/
|
|
240
|
+
export function sanitizeMessages(body, preserveTags) {
|
|
214
241
|
const messages = body.messages;
|
|
215
242
|
if (!messages)
|
|
216
243
|
return;
|
|
244
|
+
const patterns = preserveTags === undefined ? ORCHESTRATION_PATTERNS_DEFAULT : buildOrchestrationPatterns(preserveTags);
|
|
217
245
|
for (const msg of messages) {
|
|
218
246
|
if (typeof msg.content === 'string') {
|
|
219
|
-
msg.content = sanitizeContent(msg.content);
|
|
247
|
+
msg.content = sanitizeContent(msg.content, patterns);
|
|
220
248
|
}
|
|
221
249
|
else if (Array.isArray(msg.content)) {
|
|
222
250
|
for (const block of msg.content) {
|
|
223
251
|
if (typeof block === 'object' && block && 'text' in block && typeof block.text === 'string') {
|
|
224
|
-
block.text = sanitizeContent(block.text);
|
|
252
|
+
block.text = sanitizeContent(block.text, patterns);
|
|
225
253
|
}
|
|
226
254
|
}
|
|
227
255
|
// Drop text blocks that became empty after orchestration-tag scrubbing.
|
|
@@ -876,7 +904,7 @@ export async function startProxy(opts = {}) {
|
|
|
876
904
|
try {
|
|
877
905
|
const parsed = JSON.parse(body.toString());
|
|
878
906
|
// Strip orchestration tags from messages (Aider, Cursor, etc.)
|
|
879
|
-
sanitizeMessages(parsed);
|
|
907
|
+
sanitizeMessages(parsed, opts.preserveOrchestrationTags);
|
|
880
908
|
const result = isOpenAI ? openaiToAnthropic(parsed, modelOverride) : (modelOverride ? { ...parsed, model: modelOverride } : parsed);
|
|
881
909
|
const r = result;
|
|
882
910
|
requestModel = (r.model || '').toLowerCase();
|
|
@@ -1612,6 +1640,23 @@ export async function startProxy(opts = {}) {
|
|
|
1612
1640
|
if (drift.drifted) {
|
|
1613
1641
|
console.log(`[dario] ⚠ template drift: ${drift.message}`);
|
|
1614
1642
|
}
|
|
1643
|
+
// Strict-template fail-closed mode. Template must be from a live capture
|
|
1644
|
+
// (not the bundled snapshot) and must not have drifted from the installed
|
|
1645
|
+
// CC. Operator opts in via --strict-template / DARIO_STRICT_TEMPLATE=1.
|
|
1646
|
+
// Same philosophy as --strict-tls: make the unsafe state require intent.
|
|
1647
|
+
// dario#77.
|
|
1648
|
+
if (opts.strictTemplate) {
|
|
1649
|
+
if (CC_TEMPLATE._source === 'bundled') {
|
|
1650
|
+
console.error(`[dario] Refusing to start proxy in --strict-template mode: template source is 'bundled' (no live capture available).`);
|
|
1651
|
+
console.error(`[dario] Fix: run \`claude --print hello\` once so dario can capture the live template, then retry. Or drop --strict-template if the bundled fingerprint is acceptable for this run.`);
|
|
1652
|
+
process.exit(1);
|
|
1653
|
+
}
|
|
1654
|
+
if (drift.drifted) {
|
|
1655
|
+
console.error(`[dario] Refusing to start proxy in --strict-template mode: template drift detected (${drift.message}).`);
|
|
1656
|
+
console.error(`[dario] Fix: rm ~/.dario/cc-template.live.json and retry (the next capture will be against your current CC), or drop --strict-template if the drift is acceptable.`);
|
|
1657
|
+
process.exit(1);
|
|
1658
|
+
}
|
|
1659
|
+
}
|
|
1615
1660
|
// Compat check: is the installed CC inside the range this dario
|
|
1616
1661
|
// release has been tested against? Only log when non-OK so the happy
|
|
1617
1662
|
// path stays quiet. `unknown` (no CC on PATH) is also quiet — bundled
|
|
@@ -1633,7 +1678,16 @@ export async function startProxy(opts = {}) {
|
|
|
1633
1678
|
// user's own CC binary request shape and updates ~/.dario/cc-template.live.json
|
|
1634
1679
|
// for the next startup. No-op if CC isn't installed or the cache is fresh.
|
|
1635
1680
|
// Never blocks proxy startup; never throws.
|
|
1636
|
-
|
|
1681
|
+
//
|
|
1682
|
+
// Skipped entirely under --no-live-capture / DARIO_NO_LIVE_CAPTURE=1 —
|
|
1683
|
+
// the operator has opted into a bundled-only shape (air-gapped runs,
|
|
1684
|
+
// reproducible-build CI, deliberate pinning). dario#77.
|
|
1685
|
+
if (!opts.noLiveCapture) {
|
|
1686
|
+
void import('./live-fingerprint.js').then(({ refreshLiveFingerprintAsync }) => refreshLiveFingerprintAsync({ silent: false, force: drift.drifted }).catch(() => { }));
|
|
1687
|
+
}
|
|
1688
|
+
else {
|
|
1689
|
+
console.log('[dario] --no-live-capture: background live fingerprint refresh skipped; using bundled template.');
|
|
1690
|
+
}
|
|
1637
1691
|
server.listen(port, host, () => {
|
|
1638
1692
|
const modeLine = passthrough
|
|
1639
1693
|
? 'Mode: passthrough (OAuth swap only, no injection)'
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@askalf/dario",
|
|
3
|
-
"version": "3.30.
|
|
3
|
+
"version": "3.30.8",
|
|
4
4
|
"description": "A local LLM router. One endpoint, every provider — Claude subscriptions, OpenAI, OpenRouter, Groq, local LiteLLM, any OpenAI-compat endpoint — your tools don't need to change.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
],
|
|
22
22
|
"scripts": {
|
|
23
23
|
"build": "tsc && cp src/cc-template-data.json dist/ && node -e \"require('fs').mkdirSync('dist/shim',{recursive:true})\" && cp src/shim/runtime.cjs dist/shim/",
|
|
24
|
-
"test": "node test/issue-29-tool-translation.mjs && node test/hybrid-tools.mjs && node test/tool-schema-contract.mjs && node test/scrub-paths.mjs && node test/provider-prefix.mjs && node test/analytics-recording.mjs && node test/analytics-billing-bucket.mjs && node test/failover-429.mjs && node test/pool-sticky.mjs && node test/live-fingerprint.mjs && node test/shim-runtime.mjs && node test/shim-e2e.mjs && node test/proxy-header-order.mjs && node test/proxy-body-order.mjs && node test/runtime-fingerprint.mjs && node test/pacing.mjs && node test/stream-drain.mjs && node test/subagent.mjs && node test/mcp-protocol.mjs && node test/mcp-tools.mjs && node test/mcp-e2e.mjs && node test/session-rotation.mjs && node test/drift-detection.mjs && node test/cc-authorize-probe-classifier.mjs && node test/compat-range.mjs && node test/doctor-formatter.mjs && node test/atomic-write.mjs && node test/account-refresh-singleflight.mjs && node test/streaming-edge-cases.mjs && node test/client-detection.mjs && node test/manual-oauth-flow.mjs && node test/scrub-template.mjs && node test/sanitize-messages.mjs && node test/platform-tools.mjs",
|
|
24
|
+
"test": "node test/issue-29-tool-translation.mjs && node test/hybrid-tools.mjs && node test/tool-schema-contract.mjs && node test/scrub-paths.mjs && node test/provider-prefix.mjs && node test/analytics-recording.mjs && node test/analytics-billing-bucket.mjs && node test/failover-429.mjs && node test/pool-sticky.mjs && node test/live-fingerprint.mjs && node test/shim-runtime.mjs && node test/shim-e2e.mjs && node test/proxy-header-order.mjs && node test/proxy-body-order.mjs && node test/runtime-fingerprint.mjs && node test/pacing.mjs && node test/stream-drain.mjs && node test/subagent.mjs && node test/mcp-protocol.mjs && node test/mcp-tools.mjs && node test/mcp-e2e.mjs && node test/session-rotation.mjs && node test/drift-detection.mjs && node test/cc-authorize-probe-classifier.mjs && node test/compat-range.mjs && node test/doctor-formatter.mjs && node test/atomic-write.mjs && node test/account-refresh-singleflight.mjs && node test/streaming-edge-cases.mjs && node test/client-detection.mjs && node test/manual-oauth-flow.mjs && node test/scrub-template.mjs && node test/sanitize-messages.mjs && node test/platform-tools.mjs && node test/strict-template-flags.mjs",
|
|
25
25
|
"audit": "npm audit --production --audit-level=high",
|
|
26
26
|
"prepublishOnly": "npm run build",
|
|
27
27
|
"start": "node dist/cli.js",
|