@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/src/index.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
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
7
|
|
|
6
8
|
import {
|
|
@@ -37,6 +39,7 @@ import {
|
|
|
37
39
|
DEFAULT_USAGE,
|
|
38
40
|
budgetPositions,
|
|
39
41
|
conform,
|
|
42
|
+
FAILURE_POLICIES,
|
|
40
43
|
detectFromSource,
|
|
41
44
|
matchLocale,
|
|
42
45
|
parsePlanDocument,
|
|
@@ -131,6 +134,8 @@ import type {
|
|
|
131
134
|
import type {
|
|
132
135
|
BudgetReport,
|
|
133
136
|
ContractName,
|
|
137
|
+
FailurePolicy,
|
|
138
|
+
GatewayStanding,
|
|
134
139
|
UsageProfileReport,
|
|
135
140
|
WaiverUse,
|
|
136
141
|
InitDecline,
|
|
@@ -177,6 +182,12 @@ import { fetchProviderUsage, findCredential } from './connect.js';
|
|
|
177
182
|
import { STORE_DIR, appendRecords, readStore, rewriteStore } from './store-fs.js';
|
|
178
183
|
import { WAIVER_LOG, appendWaiverUse, readWaiverLog } from './waiver-log.js';
|
|
179
184
|
import { DEFAULT_PORT, buildServer, listen } from './serve.js';
|
|
185
|
+
import {
|
|
186
|
+
DEFAULT_GATEWAY_PORT,
|
|
187
|
+
UPSTREAMS,
|
|
188
|
+
buildGateway,
|
|
189
|
+
listenGateway,
|
|
190
|
+
} from './gateway-server.js';
|
|
180
191
|
import {
|
|
181
192
|
WATCH_STATE_VERSION,
|
|
182
193
|
checkWebhook,
|
|
@@ -231,6 +242,7 @@ interface Args {
|
|
|
231
242
|
const VALUE_FLAGS = new Set([
|
|
232
243
|
'against',
|
|
233
244
|
'contract',
|
|
245
|
+
'on-cannot-tell',
|
|
234
246
|
'from-log',
|
|
235
247
|
'min-usd',
|
|
236
248
|
'payload',
|
|
@@ -528,7 +540,7 @@ function disabledRules(args: Args, config: TrazumConfig): RuleId[] | undefined {
|
|
|
528
540
|
* a threshold is set — `--max-growh 5` would have been ignored and the build
|
|
529
541
|
* gone green. Silence is the wrong answer for a typo.
|
|
530
542
|
*/
|
|
531
|
-
const GLOBAL_FLAGS = ['help', 'h', 'locale', 'json', 'config', 'pricing', 'pricing-live'];
|
|
543
|
+
const GLOBAL_FLAGS = ['help', 'h', 'version', 'v', 'locale', 'json', 'config', 'pricing', 'pricing-live'];
|
|
532
544
|
const COMMAND_FLAGS: Record<string, string[]> = {
|
|
533
545
|
optimize: [
|
|
534
546
|
'level', 'model', 'calls', 'output-tokens', 'cache-hit-rate', 'batch',
|
|
@@ -554,6 +566,8 @@ const COMMAND_FLAGS: Record<string, string[]> = {
|
|
|
554
566
|
rank: ['level', 'model', 'calls', 'output-tokens', 'batch', 'disable', 'prompt', 'markdown-out'],
|
|
555
567
|
init: ['dry-run', 'yes', 'json', 'pricing', 'pricing-live'],
|
|
556
568
|
conform: ['contract', 'json'],
|
|
569
|
+
feedback: [],
|
|
570
|
+
gateway: ['on-cannot-tell', 'port', 'socket', 'pricing', 'pricing-live'],
|
|
557
571
|
where: [],
|
|
558
572
|
rules: [],
|
|
559
573
|
blame: ['limit', 'model', 'calls', 'output-tokens', 'batch', 'prompt', 'markdown-out'],
|
|
@@ -1476,6 +1490,44 @@ const INIT_LOG_CANDIDATES = [
|
|
|
1476
1490
|
* nobody has vouched for.
|
|
1477
1491
|
*/
|
|
1478
1492
|
|
|
1493
|
+
/**
|
|
1494
|
+
* Where feedback goes. Compiled in, never configurable.
|
|
1495
|
+
*
|
|
1496
|
+
* A flag or a config key naming this host would let a fork — or anything that
|
|
1497
|
+
* had rewritten a config on disk — point somebody's bug report, and the
|
|
1498
|
+
* prefilled body with it, at a machine they did not choose. It is one string
|
|
1499
|
+
* and it stays one string.
|
|
1500
|
+
*/
|
|
1501
|
+
/**
|
|
1502
|
+
* Which Trazum this is.
|
|
1503
|
+
*
|
|
1504
|
+
* Read from the manifest beside the built entry point rather than baked in by
|
|
1505
|
+
* a generator, so it cannot drift from what npm installed — the one number a
|
|
1506
|
+
* bug report is useless without is the one that must not be a copy.
|
|
1507
|
+
*
|
|
1508
|
+
* `readFileSync` at module load, deliberately: every other read in this file
|
|
1509
|
+
* is async and inside a command, but a version has to be available to
|
|
1510
|
+
* `--version` before any command is chosen, and one small synchronous read at
|
|
1511
|
+
* startup is cheaper than making the whole entry point await.
|
|
1512
|
+
*
|
|
1513
|
+
* A failure falls back to `unknown` rather than throwing. A tool that will not
|
|
1514
|
+
* start because it cannot find its own manifest is worse than one that admits
|
|
1515
|
+
* it does not know — and `unknown` in a bug report is itself a useful fact
|
|
1516
|
+
* about how somebody installed it.
|
|
1517
|
+
*/
|
|
1518
|
+
const VERSION: string = (() => {
|
|
1519
|
+
try {
|
|
1520
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
1521
|
+
const manifest: unknown = JSON.parse(readFileSync(join(here, '..', 'package.json'), 'utf8'));
|
|
1522
|
+
const found = (manifest as { version?: unknown }).version;
|
|
1523
|
+
return typeof found === 'string' ? found : 'unknown';
|
|
1524
|
+
} catch {
|
|
1525
|
+
return 'unknown';
|
|
1526
|
+
}
|
|
1527
|
+
})();
|
|
1528
|
+
|
|
1529
|
+
const FEEDBACK_REPO = 'https://github.com/Davmunrey/Trazum';
|
|
1530
|
+
|
|
1479
1531
|
/** Problems listed before the rest are counted. A wall of them helps nobody. */
|
|
1480
1532
|
const MAX_CONFORM_PROBLEMS = 20;
|
|
1481
1533
|
|
|
@@ -1949,6 +2001,193 @@ async function commandConform(args: Args, t: CliMessages): Promise<void> {
|
|
|
1949
2001
|
console.log();
|
|
1950
2002
|
}
|
|
1951
2003
|
|
|
2004
|
+
/**
|
|
2005
|
+
* `trazum feedback` — where to say it, and what to say.
|
|
2006
|
+
*
|
|
2007
|
+
* **This command sends nothing.** Trazum has no telemetry: the CLI makes no
|
|
2008
|
+
* network call it was not explicitly asked to make, and there is no ping, no
|
|
2009
|
+
* install hook and no anonymous counter anywhere in it. That is not an
|
|
2010
|
+
* omission somebody has been meaning to fix — a tool whose entire argument is
|
|
2011
|
+
* that it reads your bill without uploading it cannot also be quietly
|
|
2012
|
+
* reporting on you, and the security suite fails the build if this command
|
|
2013
|
+
* ever reaches the network.
|
|
2014
|
+
*
|
|
2015
|
+
* So the loop is closed the only honest way: the person decides to send
|
|
2016
|
+
* something, and this makes that as cheap as possible. It prints the four
|
|
2017
|
+
* places worth writing to, and a **prefilled link** carrying the facts a
|
|
2018
|
+
* maintainer always has to ask for — version, runtime, platform — printed in
|
|
2019
|
+
* full first, so nothing travels that the sender has not read.
|
|
2020
|
+
*
|
|
2021
|
+
* Nothing about *their work* is in it. Not the config, not a prompt, not a
|
|
2022
|
+
* label, not a figure. Those are the things a bug report needs and the things
|
|
2023
|
+
* only the reporter can decide to share, and a command that helpfully attached
|
|
2024
|
+
* them would be the leak this product exists not to be.
|
|
2025
|
+
*/
|
|
2026
|
+
function commandFeedback(t: CliMessages): void {
|
|
2027
|
+
const version = VERSION;
|
|
2028
|
+
/**
|
|
2029
|
+
* Facts about the machine, and nothing about the person.
|
|
2030
|
+
*
|
|
2031
|
+
* `process.platform` and the Node version are what every "cannot reproduce"
|
|
2032
|
+
* thread eventually asks for. The locale is here because Trazum ships two
|
|
2033
|
+
* languages and a report reading wrong in one of them is a real bug class.
|
|
2034
|
+
*/
|
|
2035
|
+
const environment = [
|
|
2036
|
+
`Trazum ${version}`,
|
|
2037
|
+
`Node ${process.version}`,
|
|
2038
|
+
`${process.platform} ${process.arch}`,
|
|
2039
|
+
`locale ${t.locale}`,
|
|
2040
|
+
];
|
|
2041
|
+
|
|
2042
|
+
const body = [
|
|
2043
|
+
'<!-- What happened, and what you expected instead. -->',
|
|
2044
|
+
'',
|
|
2045
|
+
'',
|
|
2046
|
+
'---',
|
|
2047
|
+
...environment.map((line) => `- ${line}`),
|
|
2048
|
+
].join('\n');
|
|
2049
|
+
const url =
|
|
2050
|
+
`${FEEDBACK_REPO}/issues/new?body=${encodeURIComponent(body)}`;
|
|
2051
|
+
|
|
2052
|
+
console.log();
|
|
2053
|
+
console.log(c.bold(t.feedback.heading()));
|
|
2054
|
+
console.log(` ${c.dim(wrap(t.feedback.sendsNothing(), 74, ' '))}`);
|
|
2055
|
+
console.log();
|
|
2056
|
+
|
|
2057
|
+
console.log(c.bold(t.feedback.whereHeading()));
|
|
2058
|
+
console.log(` ${t.feedback.wrongOptimisation()}`);
|
|
2059
|
+
console.log(` ${c.dim(`${FEEDBACK_REPO}/issues/new?template=wrong_optimisation.yml`)}`);
|
|
2060
|
+
console.log(` ${t.feedback.bug()}`);
|
|
2061
|
+
console.log(` ${c.dim(`${FEEDBACK_REPO}/issues/new?template=bug_report.yml`)}`);
|
|
2062
|
+
console.log(` ${t.feedback.question()}`);
|
|
2063
|
+
console.log(` ${c.dim(`${FEEDBACK_REPO}/discussions`)}`);
|
|
2064
|
+
console.log(` ${t.feedback.security()}`);
|
|
2065
|
+
console.log(` ${c.dim(`${FEEDBACK_REPO}/security/advisories/new`)}`);
|
|
2066
|
+
console.log();
|
|
2067
|
+
|
|
2068
|
+
console.log(c.bold(t.feedback.environmentHeading()));
|
|
2069
|
+
for (const line of environment) console.log(` ${line}`);
|
|
2070
|
+
console.log(` ${c.dim(wrap(t.feedback.environmentOnly(), 74, ' '))}`);
|
|
2071
|
+
console.log();
|
|
2072
|
+
|
|
2073
|
+
console.log(c.bold(t.feedback.linkHeading()));
|
|
2074
|
+
console.log(` ${url}`);
|
|
2075
|
+
console.log();
|
|
2076
|
+
}
|
|
2077
|
+
|
|
2078
|
+
/**
|
|
2079
|
+
* `trazum gateway <provider>` — in the path, and refusing rather than advising.
|
|
2080
|
+
*
|
|
2081
|
+
* The last thing this product could not do. `serve` answers a question an
|
|
2082
|
+
* implementation may ignore; a connector reports the runaway after it ran.
|
|
2083
|
+
* Standing between the caller and the provider fixes both — usage is measured
|
|
2084
|
+
* from the provider's own response as it comes back, and a refusal is a
|
|
2085
|
+
* refusal.
|
|
2086
|
+
*
|
|
2087
|
+
* **The failure policy is required.** `--on-cannot-tell fail-open` keeps the
|
|
2088
|
+
* product working and lets the bill run; `fail-closed` stops the bill and takes
|
|
2089
|
+
* the product down with it. Both are defensible and there is deliberately no
|
|
2090
|
+
* default: a proxy that picks silently has made the most consequential decision
|
|
2091
|
+
* in somebody's architecture on their behalf, at install time, without saying
|
|
2092
|
+
* so.
|
|
2093
|
+
*
|
|
2094
|
+
* **Substitution is off unless it is written down.** `spend.substitute` in the
|
|
2095
|
+
* config, with the operator's own reason, and every substituted call is marked
|
|
2096
|
+
* so no later report treats it as the call the caller made.
|
|
2097
|
+
*/
|
|
2098
|
+
async function commandGateway(
|
|
2099
|
+
args: Args,
|
|
2100
|
+
config: TrazumConfig,
|
|
2101
|
+
configDir: string,
|
|
2102
|
+
pricing: PricingCatalogue,
|
|
2103
|
+
t: CliMessages,
|
|
2104
|
+
): Promise<void> {
|
|
2105
|
+
const provider = args.positional[0];
|
|
2106
|
+
if (provider === undefined || UPSTREAMS[provider] === undefined) {
|
|
2107
|
+
throw new Error(t.gateway.badProvider(provider ?? '', Object.keys(UPSTREAMS).join(', ')));
|
|
2108
|
+
}
|
|
2109
|
+
|
|
2110
|
+
/**
|
|
2111
|
+
* No default, and the error says why rather than just what.
|
|
2112
|
+
*
|
|
2113
|
+
* The one flag in this product that refuses to guess on the reader's behalf,
|
|
2114
|
+
* because the two answers differ in which failure they accept and nobody but
|
|
2115
|
+
* the operator knows which their product can survive.
|
|
2116
|
+
*/
|
|
2117
|
+
const policyFlag = stringFlag(args, 'on-cannot-tell');
|
|
2118
|
+
if (policyFlag === undefined || !FAILURE_POLICIES.includes(policyFlag as FailurePolicy)) {
|
|
2119
|
+
throw new Error(t.gateway.needsPolicy(FAILURE_POLICIES.join(', ')));
|
|
2120
|
+
}
|
|
2121
|
+
|
|
2122
|
+
const { resolved } = await readStore(configDir);
|
|
2123
|
+
const budget = budgetPositions(resolved.records, config.spend, { catalogue: pricing });
|
|
2124
|
+
const position = budget.positions[0] ?? null;
|
|
2125
|
+
|
|
2126
|
+
/**
|
|
2127
|
+
* Read once at start, like `serve`'s.
|
|
2128
|
+
*
|
|
2129
|
+
* A file read in the request path would put Trazum's own latency between a
|
|
2130
|
+
* caller and their provider on every call, which is a cost this product
|
|
2131
|
+
* would otherwise be reporting on somebody else. The staleness is real, so a
|
|
2132
|
+
* refusal carries `asOfMs` and says what it rested on.
|
|
2133
|
+
*/
|
|
2134
|
+
const standing: GatewayStanding | null =
|
|
2135
|
+
position === null || position.coverage === 'none'
|
|
2136
|
+
? null
|
|
2137
|
+
: {
|
|
2138
|
+
limitUsd: position.limitUsd,
|
|
2139
|
+
consumedUsd: position.consumedUsd,
|
|
2140
|
+
provenance: 'measured',
|
|
2141
|
+
asOfMs: Date.now(),
|
|
2142
|
+
};
|
|
2143
|
+
|
|
2144
|
+
const measured: { calls: number; usd: number } = { calls: 0, usd: 0 };
|
|
2145
|
+
const server = buildGateway({
|
|
2146
|
+
provider,
|
|
2147
|
+
catalogue: pricing,
|
|
2148
|
+
policy: {
|
|
2149
|
+
onCannotTell: policyFlag as FailurePolicy,
|
|
2150
|
+
...(config.spend?.substitute === undefined ? {} : { substitute: config.spend.substitute }),
|
|
2151
|
+
},
|
|
2152
|
+
standing: () => standing,
|
|
2153
|
+
record: (call) => {
|
|
2154
|
+
measured.calls += 1;
|
|
2155
|
+
console.error(
|
|
2156
|
+
c.dim(
|
|
2157
|
+
t.gateway.measured(
|
|
2158
|
+
call.model,
|
|
2159
|
+
call.label,
|
|
2160
|
+
call.inputTokens,
|
|
2161
|
+
call.outputTokens,
|
|
2162
|
+
call.substituted,
|
|
2163
|
+
),
|
|
2164
|
+
),
|
|
2165
|
+
);
|
|
2166
|
+
},
|
|
2167
|
+
note: (line) => {
|
|
2168
|
+
console.error(c.yellow(` ${line}`));
|
|
2169
|
+
},
|
|
2170
|
+
});
|
|
2171
|
+
|
|
2172
|
+
const socket = stringFlag(args, 'socket');
|
|
2173
|
+
const portRaw = stringFlag(args, 'port');
|
|
2174
|
+
const port = portRaw === undefined ? DEFAULT_GATEWAY_PORT : Number(portRaw);
|
|
2175
|
+
if (socket === undefined && (!Number.isInteger(port) || port < 0 || port > 65_535)) {
|
|
2176
|
+
throw new Error(t.serve.badPort(String(portRaw)));
|
|
2177
|
+
}
|
|
2178
|
+
|
|
2179
|
+
const where = await listenGateway(server, socket !== undefined ? { socket } : { port });
|
|
2180
|
+
console.log(c.bold(t.gateway.listening(where, provider)));
|
|
2181
|
+
console.log(` ${c.dim(wrap(t.gateway.pointYourSdk(where), 74, ' '))}`);
|
|
2182
|
+
console.log(` ${c.dim(wrap(t.gateway.credential(), 74, ' '))}`);
|
|
2183
|
+
console.log(` ${c.dim(wrap(t.gateway.neverSubstitutes(), 74, ' '))}`);
|
|
2184
|
+
console.log(
|
|
2185
|
+
` ${c.dim(wrap(standing === null ? t.gateway.noStanding() : t.gateway.standing(formatUsd(standing.consumedUsd), formatUsd(standing.limitUsd)), 74, ' '))}`,
|
|
2186
|
+
);
|
|
2187
|
+
console.log(` ${c.dim(wrap(t.gateway.policy(policyFlag), 74, ' '))}`);
|
|
2188
|
+
console.log();
|
|
2189
|
+
}
|
|
2190
|
+
|
|
1952
2191
|
function commandModels(t: CliMessages, pricing: PricingCatalogue): void {
|
|
1953
2192
|
const n = (value: number): string => value.toLocaleString(t.numberLocale);
|
|
1954
2193
|
const col = t.models.columns;
|
|
@@ -3525,6 +3764,7 @@ async function commandConnect(
|
|
|
3525
3764
|
async function commandHistory(
|
|
3526
3765
|
args: Args,
|
|
3527
3766
|
config: TrazumConfig,
|
|
3767
|
+
configDir: string,
|
|
3528
3768
|
pricing: PricingCatalogue,
|
|
3529
3769
|
t: CliMessages,
|
|
3530
3770
|
): Promise<void> {
|
|
@@ -3644,7 +3884,7 @@ async function commandHistory(
|
|
|
3644
3884
|
* the waiver record belongs to the repository whose gates fired, and the
|
|
3645
3885
|
* stored reports may have come from anywhere.
|
|
3646
3886
|
*/
|
|
3647
|
-
const waivers = await readWaiverLog(
|
|
3887
|
+
const waivers = await readWaiverLog(configDir);
|
|
3648
3888
|
const waiverReport = waiverHistory(waivers.uses, config.waive ?? []);
|
|
3649
3889
|
|
|
3650
3890
|
const stamped = { ...history, unrecognizedFiles: unrecognized, waivers: waiverReport };
|
|
@@ -4046,7 +4286,13 @@ async function commandPlan(
|
|
|
4046
4286
|
if (outPath !== undefined) console.log(c.dim(wrap(t.plan.wrote(outPath), 74, '')));
|
|
4047
4287
|
}
|
|
4048
4288
|
|
|
4049
|
-
async function commandProfile(
|
|
4289
|
+
async function commandProfile(
|
|
4290
|
+
args: Args,
|
|
4291
|
+
config: TrazumConfig,
|
|
4292
|
+
configDir: string,
|
|
4293
|
+
pricing: PricingCatalogue,
|
|
4294
|
+
t: CliMessages,
|
|
4295
|
+
): Promise<void> {
|
|
4050
4296
|
const path = args.positional[0];
|
|
4051
4297
|
if (path === undefined) {
|
|
4052
4298
|
console.log();
|
|
@@ -4870,7 +5116,7 @@ async function commandProfile(args: Args, config: TrazumConfig, pricing: Pricing
|
|
|
4870
5116
|
const recordWaiverUses = async (): Promise<void> => {
|
|
4871
5117
|
if (waiverUses.length === 0) return;
|
|
4872
5118
|
for (const use of waiverUses) {
|
|
4873
|
-
const failed = await appendWaiverUse(
|
|
5119
|
+
const failed = await appendWaiverUse(configDir, use);
|
|
4874
5120
|
if (failed !== null) {
|
|
4875
5121
|
console.error(c.dim(t.profile.waiveNotRecorded(WAIVER_LOG, failed)));
|
|
4876
5122
|
return;
|
|
@@ -7835,6 +8081,20 @@ async function main(): Promise<void> {
|
|
|
7835
8081
|
return;
|
|
7836
8082
|
}
|
|
7837
8083
|
|
|
8084
|
+
/**
|
|
8085
|
+
* Before the help branch, and before the config loads.
|
|
8086
|
+
*
|
|
8087
|
+
* `trazum --version` on its own is how somebody answers "which one is
|
|
8088
|
+
* installed", and it has to work when the config is broken — that is
|
|
8089
|
+
* precisely the moment they are being asked. Placed above `!args.command`
|
|
8090
|
+
* for the same reason `--clear-suggestion-cache` is: with nothing else on
|
|
8091
|
+
* the line, the help branch would have swallowed it.
|
|
8092
|
+
*/
|
|
8093
|
+
if (boolFlag(args, 'version') || boolFlag(args, 'v')) {
|
|
8094
|
+
console.log(VERSION);
|
|
8095
|
+
return;
|
|
8096
|
+
}
|
|
8097
|
+
|
|
7838
8098
|
if (boolFlag(args, 'help') || boolFlag(args, 'h') || !args.command) {
|
|
7839
8099
|
console.log(
|
|
7840
8100
|
t.help(
|
|
@@ -7885,6 +8145,22 @@ async function main(): Promise<void> {
|
|
|
7885
8145
|
};
|
|
7886
8146
|
}
|
|
7887
8147
|
const { config } = loaded;
|
|
8148
|
+
/**
|
|
8149
|
+
* Where the waiver record lives: **beside the config that declared it**.
|
|
8150
|
+
*
|
|
8151
|
+
* It used to be the process's working directory, which is a different place
|
|
8152
|
+
* whenever somebody runs `trazum profile ../logs/x.jsonl --config ../repo/
|
|
8153
|
+
* trazum.config.json` — and that is not hypothetical. This repository's own
|
|
8154
|
+
* test suite did exactly that from `packages/cli`, so sixty records of a
|
|
8155
|
+
* fixture's decisions accumulated in a package directory and one of them was
|
|
8156
|
+
* committed to `main`, where it sat for two releases.
|
|
8157
|
+
*
|
|
8158
|
+
* A waiver is a decision a *repository* made. The record of using it belongs
|
|
8159
|
+
* with the file that made it, not with wherever the terminal happened to be.
|
|
8160
|
+
* No config means no waivers, so there is nothing to write and `.` is never
|
|
8161
|
+
* reached.
|
|
8162
|
+
*/
|
|
8163
|
+
const configDir = loaded.path === null ? '.' : dirname(loaded.path);
|
|
7888
8164
|
const pricing = await pricingFor(args, loaded, t);
|
|
7889
8165
|
|
|
7890
8166
|
// The config only gets to choose the locale when nothing more explicit did.
|
|
@@ -7904,7 +8180,7 @@ async function main(): Promise<void> {
|
|
|
7904
8180
|
await commandBaseline(args, config, pricing, t, locale);
|
|
7905
8181
|
break;
|
|
7906
8182
|
case 'profile':
|
|
7907
|
-
await commandProfile(args, config, pricing, t);
|
|
8183
|
+
await commandProfile(args, config, configDir, pricing, t);
|
|
7908
8184
|
break;
|
|
7909
8185
|
case 'plan':
|
|
7910
8186
|
await commandPlan(args, pricing, t);
|
|
@@ -7913,7 +8189,7 @@ async function main(): Promise<void> {
|
|
|
7913
8189
|
await commandVerify(args, pricing, t);
|
|
7914
8190
|
break;
|
|
7915
8191
|
case 'history':
|
|
7916
|
-
await commandHistory(args, config, pricing, t);
|
|
8192
|
+
await commandHistory(args, config, configDir, pricing, t);
|
|
7917
8193
|
break;
|
|
7918
8194
|
case 'connect':
|
|
7919
8195
|
await commandConnect(args, pricing, t);
|
|
@@ -7942,6 +8218,12 @@ async function main(): Promise<void> {
|
|
|
7942
8218
|
case 'models':
|
|
7943
8219
|
commandModels(t, pricing);
|
|
7944
8220
|
break;
|
|
8221
|
+
case 'gateway':
|
|
8222
|
+
await commandGateway(args, config, configDir, pricing, t);
|
|
8223
|
+
break;
|
|
8224
|
+
case 'feedback':
|
|
8225
|
+
commandFeedback(t);
|
|
8226
|
+
break;
|
|
7945
8227
|
case 'conform':
|
|
7946
8228
|
await commandConform(args, t);
|
|
7947
8229
|
break;
|