@trazum/cli 1.50.1 → 1.50.3
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/gateway-server.d.ts +91 -0
- package/dist/gateway-server.d.ts.map +1 -0
- package/dist/gateway-server.js +290 -0
- package/dist/gateway-server.js.map +1 -0
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +59 -1
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/es.d.ts.map +1 -1
- package/dist/i18n/es.js +62 -1
- package/dist/i18n/es.js.map +1 -1
- package/dist/i18n/types.d.ts +36 -0
- package/dist/i18n/types.d.ts.map +1 -1
- package/dist/index.js +242 -9
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/gateway-server.ts +350 -0
- package/src/i18n/en.ts +74 -1
- package/src/i18n/es.ts +77 -1
- package/src/i18n/types.ts +38 -0
- package/src/index.ts +289 -7
package/dist/index.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
2
3
|
import { open, readdir, readFile, stat, writeFile } from 'node:fs/promises';
|
|
3
|
-
import { join, resolve as resolvePath } from 'node:path';
|
|
4
|
+
import { dirname, join, resolve as resolvePath } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
4
6
|
import { gunzipSync } from 'node:zlib';
|
|
5
|
-
import { applyRewrites, BASELINE_FILENAME, BASELINE_VERSION, breaches, cacheableMinimum, analyzeCachePrefix, billLevers, bucketedCacheEconomics, bucketedProfile, buildHistory, buildPlan, connectorFor, CONNECTORS, normalizeAnthropicUsage, normalizeOpenAIUsage, bucketsFromRecords, evaluateWatch, firedKey, pruneRecords, recordsFromBuckets, storeInventory, storedReportFrom, verifyPlan, cacheEconomics, cacheHitRate, contextPressure, comparePrompts, compareToBaseline, computeSavings, countTokensAnthropic, DEFAULT_USAGE, budgetPositions, conform, detectFromSource, matchLocale, parsePlanDocument, waiverDay, waiverHistory, proposeInit, MIN_RATE_DAYS, parseConfig, coverageDrift, driversBetween, explainGateFailure, assignSources, fleetRollup, labelCoverage, measuredUsage, gateMargin, GATE_MARGIN_TIGHT, estimateTokens, evaluate, extractPrompts, findExamples, formatBaseline, formatSignedUsd, formatUsd, getMessages, getModel, hasMarker, LOCALES, MAX_BASELINE_BYTES, moneyIsComparable, mostSpecificMatch, nearestName, optimize, parseBaseline, PHRASE_LANGUAGES, plannedCalls, profilePrompt, profileToCsv, profileUsage, promptId, providerFromEnv, pruneExamples, refineWithLlm, rejectionText, reorderForCache, repriceProfile, reviewAgeDays, reviewExamples, RULES, sharedPrefixes, sharesOf, SOURCE_EXTENSIONS, suggestRewrites, toOtlpMetrics, toPromptfoo, TTL_1H_MS, UNLABELLED, withExactTokenCounts, } from '@trazum/core';
|
|
7
|
+
import { applyRewrites, BASELINE_FILENAME, BASELINE_VERSION, breaches, cacheableMinimum, analyzeCachePrefix, billLevers, bucketedCacheEconomics, bucketedProfile, buildHistory, buildPlan, connectorFor, CONNECTORS, normalizeAnthropicUsage, normalizeOpenAIUsage, bucketsFromRecords, evaluateWatch, firedKey, pruneRecords, recordsFromBuckets, storeInventory, storedReportFrom, verifyPlan, cacheEconomics, cacheHitRate, contextPressure, comparePrompts, compareToBaseline, computeSavings, countTokensAnthropic, DEFAULT_USAGE, budgetPositions, conform, FAILURE_POLICIES, detectFromSource, matchLocale, parsePlanDocument, waiverDay, waiverHistory, proposeInit, MIN_RATE_DAYS, parseConfig, coverageDrift, driversBetween, explainGateFailure, assignSources, fleetRollup, labelCoverage, measuredUsage, gateMargin, GATE_MARGIN_TIGHT, estimateTokens, evaluate, extractPrompts, findExamples, formatBaseline, formatSignedUsd, formatUsd, getMessages, getModel, hasMarker, LOCALES, MAX_BASELINE_BYTES, moneyIsComparable, mostSpecificMatch, nearestName, optimize, parseBaseline, PHRASE_LANGUAGES, plannedCalls, profilePrompt, profileToCsv, profileUsage, promptId, providerFromEnv, pruneExamples, refineWithLlm, rejectionText, reorderForCache, repriceProfile, reviewAgeDays, reviewExamples, RULES, sharedPrefixes, sharesOf, SOURCE_EXTENSIONS, suggestRewrites, toOtlpMetrics, toPromptfoo, TTL_1H_MS, UNLABELLED, withExactTokenCounts, } from '@trazum/core';
|
|
6
8
|
import { cacheDir, cacheStats, cachingProvider, clearCache } from './suggest-cache.js';
|
|
7
9
|
import { dayOf, formatGap, median, spanDays } from './time.js';
|
|
8
10
|
// Everything that reads the filesystem, on its own entry point so the web
|
|
@@ -13,6 +15,7 @@ import { fetchProviderUsage, findCredential } from './connect.js';
|
|
|
13
15
|
import { STORE_DIR, appendRecords, readStore, rewriteStore } from './store-fs.js';
|
|
14
16
|
import { WAIVER_LOG, appendWaiverUse, readWaiverLog } from './waiver-log.js';
|
|
15
17
|
import { DEFAULT_PORT, buildServer, listen } from './serve.js';
|
|
18
|
+
import { DEFAULT_GATEWAY_PORT, UPSTREAMS, buildGateway, listenGateway, } from './gateway-server.js';
|
|
16
19
|
import { WATCH_STATE_VERSION, checkWebhook, postWebhook, readWatchState, writeWatchState, } from './watch-run.js';
|
|
17
20
|
import { LOCALE_ENV_VARS, detectLocale, getCliMessages } from './i18n/index.js';
|
|
18
21
|
import { MAX_SUMMARY_CHARS, fitWithin, renderBlameMarkdown, renderCheckMarkdown, renderDiffMarkdown, renderRankMarkdown, renderProfileMarkdown, } from './markdown.js';
|
|
@@ -31,6 +34,7 @@ const c = {
|
|
|
31
34
|
const VALUE_FLAGS = new Set([
|
|
32
35
|
'against',
|
|
33
36
|
'contract',
|
|
37
|
+
'on-cannot-tell',
|
|
34
38
|
'from-log',
|
|
35
39
|
'min-usd',
|
|
36
40
|
'payload',
|
|
@@ -287,7 +291,7 @@ function disabledRules(args, config) {
|
|
|
287
291
|
* a threshold is set — `--max-growh 5` would have been ignored and the build
|
|
288
292
|
* gone green. Silence is the wrong answer for a typo.
|
|
289
293
|
*/
|
|
290
|
-
const GLOBAL_FLAGS = ['help', 'h', 'locale', 'json', 'config', 'pricing', 'pricing-live'];
|
|
294
|
+
const GLOBAL_FLAGS = ['help', 'h', 'version', 'v', 'locale', 'json', 'config', 'pricing', 'pricing-live'];
|
|
291
295
|
const COMMAND_FLAGS = {
|
|
292
296
|
optimize: [
|
|
293
297
|
'level', 'model', 'calls', 'output-tokens', 'cache-hit-rate', 'batch',
|
|
@@ -313,6 +317,8 @@ const COMMAND_FLAGS = {
|
|
|
313
317
|
rank: ['level', 'model', 'calls', 'output-tokens', 'batch', 'disable', 'prompt', 'markdown-out'],
|
|
314
318
|
init: ['dry-run', 'yes', 'json', 'pricing', 'pricing-live'],
|
|
315
319
|
conform: ['contract', 'json'],
|
|
320
|
+
feedback: [],
|
|
321
|
+
gateway: ['on-cannot-tell', 'port', 'socket', 'pricing', 'pricing-live'],
|
|
316
322
|
where: [],
|
|
317
323
|
rules: [],
|
|
318
324
|
blame: ['limit', 'model', 'calls', 'output-tokens', 'batch', 'prompt', 'markdown-out'],
|
|
@@ -1039,6 +1045,43 @@ const INIT_LOG_CANDIDATES = [
|
|
|
1039
1045
|
* answers for a file somebody named while this answers for a repository
|
|
1040
1046
|
* nobody has vouched for.
|
|
1041
1047
|
*/
|
|
1048
|
+
/**
|
|
1049
|
+
* Where feedback goes. Compiled in, never configurable.
|
|
1050
|
+
*
|
|
1051
|
+
* A flag or a config key naming this host would let a fork — or anything that
|
|
1052
|
+
* had rewritten a config on disk — point somebody's bug report, and the
|
|
1053
|
+
* prefilled body with it, at a machine they did not choose. It is one string
|
|
1054
|
+
* and it stays one string.
|
|
1055
|
+
*/
|
|
1056
|
+
/**
|
|
1057
|
+
* Which Trazum this is.
|
|
1058
|
+
*
|
|
1059
|
+
* Read from the manifest beside the built entry point rather than baked in by
|
|
1060
|
+
* a generator, so it cannot drift from what npm installed — the one number a
|
|
1061
|
+
* bug report is useless without is the one that must not be a copy.
|
|
1062
|
+
*
|
|
1063
|
+
* `readFileSync` at module load, deliberately: every other read in this file
|
|
1064
|
+
* is async and inside a command, but a version has to be available to
|
|
1065
|
+
* `--version` before any command is chosen, and one small synchronous read at
|
|
1066
|
+
* startup is cheaper than making the whole entry point await.
|
|
1067
|
+
*
|
|
1068
|
+
* A failure falls back to `unknown` rather than throwing. A tool that will not
|
|
1069
|
+
* start because it cannot find its own manifest is worse than one that admits
|
|
1070
|
+
* it does not know — and `unknown` in a bug report is itself a useful fact
|
|
1071
|
+
* about how somebody installed it.
|
|
1072
|
+
*/
|
|
1073
|
+
const VERSION = (() => {
|
|
1074
|
+
try {
|
|
1075
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
1076
|
+
const manifest = JSON.parse(readFileSync(join(here, '..', 'package.json'), 'utf8'));
|
|
1077
|
+
const found = manifest.version;
|
|
1078
|
+
return typeof found === 'string' ? found : 'unknown';
|
|
1079
|
+
}
|
|
1080
|
+
catch {
|
|
1081
|
+
return 'unknown';
|
|
1082
|
+
}
|
|
1083
|
+
})();
|
|
1084
|
+
const FEEDBACK_REPO = 'https://github.com/Davmunrey/Trazum';
|
|
1042
1085
|
/** Problems listed before the rest are counted. A wall of them helps nobody. */
|
|
1043
1086
|
const MAX_CONFORM_PROBLEMS = 20;
|
|
1044
1087
|
/** The contracts `--contract` accepts, so a typo is refused with the list. */
|
|
@@ -1467,6 +1510,161 @@ async function commandConform(args, t) {
|
|
|
1467
1510
|
}
|
|
1468
1511
|
console.log();
|
|
1469
1512
|
}
|
|
1513
|
+
/**
|
|
1514
|
+
* `trazum feedback` — where to say it, and what to say.
|
|
1515
|
+
*
|
|
1516
|
+
* **This command sends nothing.** Trazum has no telemetry: the CLI makes no
|
|
1517
|
+
* network call it was not explicitly asked to make, and there is no ping, no
|
|
1518
|
+
* install hook and no anonymous counter anywhere in it. That is not an
|
|
1519
|
+
* omission somebody has been meaning to fix — a tool whose entire argument is
|
|
1520
|
+
* that it reads your bill without uploading it cannot also be quietly
|
|
1521
|
+
* reporting on you, and the security suite fails the build if this command
|
|
1522
|
+
* ever reaches the network.
|
|
1523
|
+
*
|
|
1524
|
+
* So the loop is closed the only honest way: the person decides to send
|
|
1525
|
+
* something, and this makes that as cheap as possible. It prints the four
|
|
1526
|
+
* places worth writing to, and a **prefilled link** carrying the facts a
|
|
1527
|
+
* maintainer always has to ask for — version, runtime, platform — printed in
|
|
1528
|
+
* full first, so nothing travels that the sender has not read.
|
|
1529
|
+
*
|
|
1530
|
+
* Nothing about *their work* is in it. Not the config, not a prompt, not a
|
|
1531
|
+
* label, not a figure. Those are the things a bug report needs and the things
|
|
1532
|
+
* only the reporter can decide to share, and a command that helpfully attached
|
|
1533
|
+
* them would be the leak this product exists not to be.
|
|
1534
|
+
*/
|
|
1535
|
+
function commandFeedback(t) {
|
|
1536
|
+
const version = VERSION;
|
|
1537
|
+
/**
|
|
1538
|
+
* Facts about the machine, and nothing about the person.
|
|
1539
|
+
*
|
|
1540
|
+
* `process.platform` and the Node version are what every "cannot reproduce"
|
|
1541
|
+
* thread eventually asks for. The locale is here because Trazum ships two
|
|
1542
|
+
* languages and a report reading wrong in one of them is a real bug class.
|
|
1543
|
+
*/
|
|
1544
|
+
const environment = [
|
|
1545
|
+
`Trazum ${version}`,
|
|
1546
|
+
`Node ${process.version}`,
|
|
1547
|
+
`${process.platform} ${process.arch}`,
|
|
1548
|
+
`locale ${t.locale}`,
|
|
1549
|
+
];
|
|
1550
|
+
const body = [
|
|
1551
|
+
'<!-- What happened, and what you expected instead. -->',
|
|
1552
|
+
'',
|
|
1553
|
+
'',
|
|
1554
|
+
'---',
|
|
1555
|
+
...environment.map((line) => `- ${line}`),
|
|
1556
|
+
].join('\n');
|
|
1557
|
+
const url = `${FEEDBACK_REPO}/issues/new?body=${encodeURIComponent(body)}`;
|
|
1558
|
+
console.log();
|
|
1559
|
+
console.log(c.bold(t.feedback.heading()));
|
|
1560
|
+
console.log(` ${c.dim(wrap(t.feedback.sendsNothing(), 74, ' '))}`);
|
|
1561
|
+
console.log();
|
|
1562
|
+
console.log(c.bold(t.feedback.whereHeading()));
|
|
1563
|
+
console.log(` ${t.feedback.wrongOptimisation()}`);
|
|
1564
|
+
console.log(` ${c.dim(`${FEEDBACK_REPO}/issues/new?template=wrong_optimisation.yml`)}`);
|
|
1565
|
+
console.log(` ${t.feedback.bug()}`);
|
|
1566
|
+
console.log(` ${c.dim(`${FEEDBACK_REPO}/issues/new?template=bug_report.yml`)}`);
|
|
1567
|
+
console.log(` ${t.feedback.question()}`);
|
|
1568
|
+
console.log(` ${c.dim(`${FEEDBACK_REPO}/discussions`)}`);
|
|
1569
|
+
console.log(` ${t.feedback.security()}`);
|
|
1570
|
+
console.log(` ${c.dim(`${FEEDBACK_REPO}/security/advisories/new`)}`);
|
|
1571
|
+
console.log();
|
|
1572
|
+
console.log(c.bold(t.feedback.environmentHeading()));
|
|
1573
|
+
for (const line of environment)
|
|
1574
|
+
console.log(` ${line}`);
|
|
1575
|
+
console.log(` ${c.dim(wrap(t.feedback.environmentOnly(), 74, ' '))}`);
|
|
1576
|
+
console.log();
|
|
1577
|
+
console.log(c.bold(t.feedback.linkHeading()));
|
|
1578
|
+
console.log(` ${url}`);
|
|
1579
|
+
console.log();
|
|
1580
|
+
}
|
|
1581
|
+
/**
|
|
1582
|
+
* `trazum gateway <provider>` — in the path, and refusing rather than advising.
|
|
1583
|
+
*
|
|
1584
|
+
* The last thing this product could not do. `serve` answers a question an
|
|
1585
|
+
* implementation may ignore; a connector reports the runaway after it ran.
|
|
1586
|
+
* Standing between the caller and the provider fixes both — usage is measured
|
|
1587
|
+
* from the provider's own response as it comes back, and a refusal is a
|
|
1588
|
+
* refusal.
|
|
1589
|
+
*
|
|
1590
|
+
* **The failure policy is required.** `--on-cannot-tell fail-open` keeps the
|
|
1591
|
+
* product working and lets the bill run; `fail-closed` stops the bill and takes
|
|
1592
|
+
* the product down with it. Both are defensible and there is deliberately no
|
|
1593
|
+
* default: a proxy that picks silently has made the most consequential decision
|
|
1594
|
+
* in somebody's architecture on their behalf, at install time, without saying
|
|
1595
|
+
* so.
|
|
1596
|
+
*
|
|
1597
|
+
* **Substitution is off unless it is written down.** `spend.substitute` in the
|
|
1598
|
+
* config, with the operator's own reason, and every substituted call is marked
|
|
1599
|
+
* so no later report treats it as the call the caller made.
|
|
1600
|
+
*/
|
|
1601
|
+
async function commandGateway(args, config, configDir, pricing, t) {
|
|
1602
|
+
const provider = args.positional[0];
|
|
1603
|
+
if (provider === undefined || UPSTREAMS[provider] === undefined) {
|
|
1604
|
+
throw new Error(t.gateway.badProvider(provider ?? '', Object.keys(UPSTREAMS).join(', ')));
|
|
1605
|
+
}
|
|
1606
|
+
/**
|
|
1607
|
+
* No default, and the error says why rather than just what.
|
|
1608
|
+
*
|
|
1609
|
+
* The one flag in this product that refuses to guess on the reader's behalf,
|
|
1610
|
+
* because the two answers differ in which failure they accept and nobody but
|
|
1611
|
+
* the operator knows which their product can survive.
|
|
1612
|
+
*/
|
|
1613
|
+
const policyFlag = stringFlag(args, 'on-cannot-tell');
|
|
1614
|
+
if (policyFlag === undefined || !FAILURE_POLICIES.includes(policyFlag)) {
|
|
1615
|
+
throw new Error(t.gateway.needsPolicy(FAILURE_POLICIES.join(', ')));
|
|
1616
|
+
}
|
|
1617
|
+
const { resolved } = await readStore(configDir);
|
|
1618
|
+
const budget = budgetPositions(resolved.records, config.spend, { catalogue: pricing });
|
|
1619
|
+
const position = budget.positions[0] ?? null;
|
|
1620
|
+
/**
|
|
1621
|
+
* Read once at start, like `serve`'s.
|
|
1622
|
+
*
|
|
1623
|
+
* A file read in the request path would put Trazum's own latency between a
|
|
1624
|
+
* caller and their provider on every call, which is a cost this product
|
|
1625
|
+
* would otherwise be reporting on somebody else. The staleness is real, so a
|
|
1626
|
+
* refusal carries `asOfMs` and says what it rested on.
|
|
1627
|
+
*/
|
|
1628
|
+
const standing = position === null || position.coverage === 'none'
|
|
1629
|
+
? null
|
|
1630
|
+
: {
|
|
1631
|
+
limitUsd: position.limitUsd,
|
|
1632
|
+
consumedUsd: position.consumedUsd,
|
|
1633
|
+
provenance: 'measured',
|
|
1634
|
+
asOfMs: Date.now(),
|
|
1635
|
+
};
|
|
1636
|
+
const measured = { calls: 0, usd: 0 };
|
|
1637
|
+
const server = buildGateway({
|
|
1638
|
+
provider,
|
|
1639
|
+
catalogue: pricing,
|
|
1640
|
+
policy: {
|
|
1641
|
+
onCannotTell: policyFlag,
|
|
1642
|
+
...(config.spend?.substitute === undefined ? {} : { substitute: config.spend.substitute }),
|
|
1643
|
+
},
|
|
1644
|
+
standing: () => standing,
|
|
1645
|
+
record: (call) => {
|
|
1646
|
+
measured.calls += 1;
|
|
1647
|
+
console.error(c.dim(t.gateway.measured(call.model, call.label, call.inputTokens, call.outputTokens, call.substituted)));
|
|
1648
|
+
},
|
|
1649
|
+
note: (line) => {
|
|
1650
|
+
console.error(c.yellow(` ${line}`));
|
|
1651
|
+
},
|
|
1652
|
+
});
|
|
1653
|
+
const socket = stringFlag(args, 'socket');
|
|
1654
|
+
const portRaw = stringFlag(args, 'port');
|
|
1655
|
+
const port = portRaw === undefined ? DEFAULT_GATEWAY_PORT : Number(portRaw);
|
|
1656
|
+
if (socket === undefined && (!Number.isInteger(port) || port < 0 || port > 65_535)) {
|
|
1657
|
+
throw new Error(t.serve.badPort(String(portRaw)));
|
|
1658
|
+
}
|
|
1659
|
+
const where = await listenGateway(server, socket !== undefined ? { socket } : { port });
|
|
1660
|
+
console.log(c.bold(t.gateway.listening(where, provider)));
|
|
1661
|
+
console.log(` ${c.dim(wrap(t.gateway.pointYourSdk(where), 74, ' '))}`);
|
|
1662
|
+
console.log(` ${c.dim(wrap(t.gateway.credential(), 74, ' '))}`);
|
|
1663
|
+
console.log(` ${c.dim(wrap(t.gateway.neverSubstitutes(), 74, ' '))}`);
|
|
1664
|
+
console.log(` ${c.dim(wrap(standing === null ? t.gateway.noStanding() : t.gateway.standing(formatUsd(standing.consumedUsd), formatUsd(standing.limitUsd)), 74, ' '))}`);
|
|
1665
|
+
console.log(` ${c.dim(wrap(t.gateway.policy(policyFlag), 74, ' '))}`);
|
|
1666
|
+
console.log();
|
|
1667
|
+
}
|
|
1470
1668
|
function commandModels(t, pricing) {
|
|
1471
1669
|
const n = (value) => value.toLocaleString(t.numberLocale);
|
|
1472
1670
|
const col = t.models.columns;
|
|
@@ -2653,7 +2851,7 @@ async function commandConnect(args, pricing, t) {
|
|
|
2653
2851
|
* same action planned twice — and no series, however long, becomes a
|
|
2654
2852
|
* forecast.
|
|
2655
2853
|
*/
|
|
2656
|
-
async function commandHistory(args, config, pricing, t) {
|
|
2854
|
+
async function commandHistory(args, config, configDir, pricing, t) {
|
|
2657
2855
|
/**
|
|
2658
2856
|
* `--store` builds the series from measured spend already on disk.
|
|
2659
2857
|
*
|
|
@@ -2765,7 +2963,7 @@ async function commandHistory(args, config, pricing, t) {
|
|
|
2765
2963
|
* the waiver record belongs to the repository whose gates fired, and the
|
|
2766
2964
|
* stored reports may have come from anywhere.
|
|
2767
2965
|
*/
|
|
2768
|
-
const waivers = await readWaiverLog(
|
|
2966
|
+
const waivers = await readWaiverLog(configDir);
|
|
2769
2967
|
const waiverReport = waiverHistory(waivers.uses, config.waive ?? []);
|
|
2770
2968
|
const stamped = { ...history, unrecognizedFiles: unrecognized, waivers: waiverReport };
|
|
2771
2969
|
const n = (value) => value.toLocaleString(t.numberLocale);
|
|
@@ -3117,7 +3315,7 @@ async function commandPlan(args, pricing, t) {
|
|
|
3117
3315
|
if (outPath !== undefined)
|
|
3118
3316
|
console.log(c.dim(wrap(t.plan.wrote(outPath), 74, '')));
|
|
3119
3317
|
}
|
|
3120
|
-
async function commandProfile(args, config, pricing, t) {
|
|
3318
|
+
async function commandProfile(args, config, configDir, pricing, t) {
|
|
3121
3319
|
const path = args.positional[0];
|
|
3122
3320
|
if (path === undefined) {
|
|
3123
3321
|
console.log();
|
|
@@ -3876,7 +4074,7 @@ async function commandProfile(args, config, pricing, t) {
|
|
|
3876
4074
|
if (waiverUses.length === 0)
|
|
3877
4075
|
return;
|
|
3878
4076
|
for (const use of waiverUses) {
|
|
3879
|
-
const failed = await appendWaiverUse(
|
|
4077
|
+
const failed = await appendWaiverUse(configDir, use);
|
|
3880
4078
|
if (failed !== null) {
|
|
3881
4079
|
console.error(c.dim(t.profile.waiveNotRecorded(WAIVER_LOG, failed)));
|
|
3882
4080
|
return;
|
|
@@ -6265,6 +6463,19 @@ async function main() {
|
|
|
6265
6463
|
console.log(t.cache.cleared(removed, before.bytes, dir));
|
|
6266
6464
|
return;
|
|
6267
6465
|
}
|
|
6466
|
+
/**
|
|
6467
|
+
* Before the help branch, and before the config loads.
|
|
6468
|
+
*
|
|
6469
|
+
* `trazum --version` on its own is how somebody answers "which one is
|
|
6470
|
+
* installed", and it has to work when the config is broken — that is
|
|
6471
|
+
* precisely the moment they are being asked. Placed above `!args.command`
|
|
6472
|
+
* for the same reason `--clear-suggestion-cache` is: with nothing else on
|
|
6473
|
+
* the line, the help branch would have swallowed it.
|
|
6474
|
+
*/
|
|
6475
|
+
if (boolFlag(args, 'version') || boolFlag(args, 'v')) {
|
|
6476
|
+
console.log(VERSION);
|
|
6477
|
+
return;
|
|
6478
|
+
}
|
|
6268
6479
|
if (boolFlag(args, 'help') || boolFlag(args, 'h') || !args.command) {
|
|
6269
6480
|
console.log(t.help({
|
|
6270
6481
|
model: DEFAULT_USAGE.model,
|
|
@@ -6310,6 +6521,22 @@ async function main() {
|
|
|
6310
6521
|
};
|
|
6311
6522
|
}
|
|
6312
6523
|
const { config } = loaded;
|
|
6524
|
+
/**
|
|
6525
|
+
* Where the waiver record lives: **beside the config that declared it**.
|
|
6526
|
+
*
|
|
6527
|
+
* It used to be the process's working directory, which is a different place
|
|
6528
|
+
* whenever somebody runs `trazum profile ../logs/x.jsonl --config ../repo/
|
|
6529
|
+
* trazum.config.json` — and that is not hypothetical. This repository's own
|
|
6530
|
+
* test suite did exactly that from `packages/cli`, so sixty records of a
|
|
6531
|
+
* fixture's decisions accumulated in a package directory and one of them was
|
|
6532
|
+
* committed to `main`, where it sat for two releases.
|
|
6533
|
+
*
|
|
6534
|
+
* A waiver is a decision a *repository* made. The record of using it belongs
|
|
6535
|
+
* with the file that made it, not with wherever the terminal happened to be.
|
|
6536
|
+
* No config means no waivers, so there is nothing to write and `.` is never
|
|
6537
|
+
* reached.
|
|
6538
|
+
*/
|
|
6539
|
+
const configDir = loaded.path === null ? '.' : dirname(loaded.path);
|
|
6313
6540
|
const pricing = await pricingFor(args, loaded, t);
|
|
6314
6541
|
// The config only gets to choose the locale when nothing more explicit did.
|
|
6315
6542
|
if (config.locale && !stringFlag(args, 'locale')) {
|
|
@@ -6327,7 +6554,7 @@ async function main() {
|
|
|
6327
6554
|
await commandBaseline(args, config, pricing, t, locale);
|
|
6328
6555
|
break;
|
|
6329
6556
|
case 'profile':
|
|
6330
|
-
await commandProfile(args, config, pricing, t);
|
|
6557
|
+
await commandProfile(args, config, configDir, pricing, t);
|
|
6331
6558
|
break;
|
|
6332
6559
|
case 'plan':
|
|
6333
6560
|
await commandPlan(args, pricing, t);
|
|
@@ -6336,7 +6563,7 @@ async function main() {
|
|
|
6336
6563
|
await commandVerify(args, pricing, t);
|
|
6337
6564
|
break;
|
|
6338
6565
|
case 'history':
|
|
6339
|
-
await commandHistory(args, config, pricing, t);
|
|
6566
|
+
await commandHistory(args, config, configDir, pricing, t);
|
|
6340
6567
|
break;
|
|
6341
6568
|
case 'connect':
|
|
6342
6569
|
await commandConnect(args, pricing, t);
|
|
@@ -6365,6 +6592,12 @@ async function main() {
|
|
|
6365
6592
|
case 'models':
|
|
6366
6593
|
commandModels(t, pricing);
|
|
6367
6594
|
break;
|
|
6595
|
+
case 'gateway':
|
|
6596
|
+
await commandGateway(args, config, configDir, pricing, t);
|
|
6597
|
+
break;
|
|
6598
|
+
case 'feedback':
|
|
6599
|
+
commandFeedback(t);
|
|
6600
|
+
break;
|
|
6368
6601
|
case 'conform':
|
|
6369
6602
|
await commandConform(args, t);
|
|
6370
6603
|
break;
|