@mafdet/cli 0.4.10 → 0.4.11
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/CHANGELOG.md +36 -6
- package/README.md +3 -3
- package/README.zh-CN.md +1 -1
- package/dist/agent/approvals.d.ts.map +1 -1
- package/dist/agent/approvals.js +8 -1
- package/dist/agent/loop.d.ts.map +1 -1
- package/dist/agent/loop.js +3 -6
- package/dist/agent/session.d.ts +2 -0
- package/dist/agent/session.d.ts.map +1 -1
- package/dist/agent/session.js +19 -4
- package/dist/api/account.d.ts.map +1 -1
- package/dist/api/account.js +46 -3
- package/dist/api/models.d.ts.map +1 -1
- package/dist/api/models.js +30 -4
- package/dist/api/sse-parser.d.ts.map +1 -1
- package/dist/api/sse-parser.js +9 -1
- package/dist/api/validate.d.ts +4 -0
- package/dist/api/validate.d.ts.map +1 -0
- package/dist/api/validate.js +15 -0
- package/dist/commands/account.d.ts.map +1 -1
- package/dist/commands/account.js +6 -5
- package/dist/commands/agent.d.ts.map +1 -1
- package/dist/commands/agent.js +3 -2
- package/dist/commands/chat.d.ts.map +1 -1
- package/dist/commands/chat.js +30 -5
- package/dist/commands/models.js +4 -4
- package/dist/output/money.d.ts +2 -0
- package/dist/output/money.d.ts.map +1 -1
- package/dist/output/money.js +4 -0
- package/dist/tui/adapters/models.d.ts.map +1 -1
- package/dist/tui/adapters/models.js +2 -1
- package/dist/tui/tool-card.js +3 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,17 +6,47 @@ The version is the contract for the JSON output shapes and the exit codes.
|
|
|
6
6
|
Being 0.x, semver still permits a break in a minor bump; the intent is not to
|
|
7
7
|
use that latitude, and the promise becomes binding at 1.0.0.
|
|
8
8
|
|
|
9
|
+
## 0.4.11 - 2026-08-12
|
|
10
|
+
|
|
11
|
+
A seventh audit round, fixed at the root rather than sink by sink - which is
|
|
12
|
+
what the first round asked for and what six rounds of patching individual
|
|
13
|
+
strings failed to do.
|
|
14
|
+
|
|
15
|
+
**One boundary for human-readable output.** Interactive agent failures,
|
|
16
|
+
model-controlled tool names and results, the command-line approval preview,
|
|
17
|
+
TUI tool cards, account tables and model retirement text all go through it
|
|
18
|
+
now. Previously each was a separate string that someone had to remember. The
|
|
19
|
+
approval preview mattered most: a control sequence there can redraw the diff
|
|
20
|
+
between what you are shown and what you are agreeing to.
|
|
21
|
+
|
|
22
|
+
**One definition of a price.** `isMicroUsd` is shared by the agent's cost
|
|
23
|
+
ceiling, chat estimates, `models show` and the TUI picker. A catalogue serving
|
|
24
|
+
`abc` used to crash `models show` and the model picker outright, and in chat it
|
|
25
|
+
threw _after_ the request had been paid for, losing the answer and the saved
|
|
26
|
+
session with it. Invalid prices now read as unknown, and `"0"` is still a
|
|
27
|
+
price.
|
|
28
|
+
|
|
29
|
+
**Remote JSON is validated where it arrives.** Catalogue, wallet, usage and
|
|
30
|
+
subscription responses are checked at the HTTP boundary instead of being
|
|
31
|
+
trusted because TypeScript describes them. Malformed token counts are ignored
|
|
32
|
+
rather than entering totals or BigInt arithmetic.
|
|
33
|
+
|
|
34
|
+
Machine output is unchanged throughout: `--json`, `--plain` and redirected
|
|
35
|
+
streams still carry exactly what the server sent.
|
|
36
|
+
|
|
9
37
|
## 0.4.10 - 2026-08-12
|
|
10
38
|
|
|
11
39
|
A sixth audit round, and the finding that matters most is about how the last
|
|
12
40
|
one was tested.
|
|
13
41
|
|
|
14
|
-
**
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
42
|
+
**Four previously missed error paths now share one function.** 0.4.9 said every
|
|
43
|
+
human-readable error used it, which was still too broad: the interactive Agent
|
|
44
|
+
and other human-output paths were found separately and are fixed under
|
|
45
|
+
Unreleased. The four 0.4.10 paths were: `embed` and `speech` handled a surface
|
|
46
|
+
mismatch themselves, `mafdet agent` printed the upstream's message directly,
|
|
47
|
+
and the request id - which comes from a response header - was concatenated
|
|
48
|
+
outside the sanitiser inside the very function meant to be the boundary. All
|
|
49
|
+
four carried remote text to a terminal.
|
|
20
50
|
|
|
21
51
|
The test that was supposed to cover this called the sanitiser directly, so it
|
|
22
52
|
passed while every caller bypassed it. That is the same mistake as the one in
|
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
The official Mafdet AI command line: model calls, account queries, and a
|
|
4
4
|
permissioned local agent.
|
|
5
5
|
|
|
6
|
-
> **0.4.
|
|
6
|
+
> **0.4.11.** 中文版见 [README.zh-CN.md](./README.zh-CN.md). The CLI never
|
|
7
7
|
> advertises a command it cannot serve, so `mafdet help` always lists exactly
|
|
8
8
|
> what works.
|
|
9
9
|
>
|
|
@@ -23,8 +23,8 @@ Or build it from this repository:
|
|
|
23
23
|
|
|
24
24
|
```bash
|
|
25
25
|
pnpm --filter @mafdet/cli build
|
|
26
|
-
cd packages/cli && npm pack # -> mafdet-cli-0.4.
|
|
27
|
-
npm install -g ./mafdet-cli-0.4.
|
|
26
|
+
cd packages/cli && npm pack # -> mafdet-cli-0.4.11.tgz
|
|
27
|
+
npm install -g ./mafdet-cli-0.4.11.tgz
|
|
28
28
|
```
|
|
29
29
|
|
|
30
30
|
## The interactive session
|
package/README.zh-CN.md
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"approvals.d.ts","sourceRoot":"","sources":["../../src/agent/approvals.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"approvals.d.ts","sourceRoot":"","sources":["../../src/agent/approvals.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,eAAe;IAC9B,yDAAyD;IACzD,OAAO,EAAE,MAAM,CAAC;IAChB,8DAA8D;IAC9D,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,QAAQ;IACvB,OAAO,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACrD;AAED;;;;GAIG;AACH,eAAO,MAAM,OAAO,EAAE,QAIrB,CAAC;AAEF,uEAAuE;AACvE,eAAO,MAAM,UAAU,EAAE,QAIxB,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,EAAE;IACtC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC;CAChC,GAAG,QAAQ,CAwBX;AAED,MAAM,MAAM,YAAY,GAAG,WAAW,GAAG,SAAS,GAAG,WAAW,CAAC;AAEjE;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,YAAY,EAClB,EAAE,EAAE;IAAE,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,EACtC,aAAa,GAAE,OAAsC,GACpD,QAAQ,CAIV"}
|
package/dist/agent/approvals.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createInterface } from "node:readline/promises";
|
|
2
|
+
import { safeForTerminal } from "../output/terminal-safe.js";
|
|
2
3
|
/**
|
|
3
4
|
* `read-only`, and every non-interactive run that has not been given explicit
|
|
4
5
|
* permission. Plan §8.1: a CI job must not acquire write access by accident,
|
|
@@ -24,7 +25,13 @@ export const approveAll = {
|
|
|
24
25
|
export function interactiveApprover(io) {
|
|
25
26
|
return {
|
|
26
27
|
async approve(request) {
|
|
27
|
-
|
|
28
|
+
// Always sanitised, unlike every other stderr write, and the exception
|
|
29
|
+
// is deliberate: this prompt only exists when a person is about to
|
|
30
|
+
// answer it, and the answer decides whether a file is written. A control
|
|
31
|
+
// sequence here could clear the screen or redraw the diff between what
|
|
32
|
+
// is shown and what is asked. There is no redirected-output case to
|
|
33
|
+
// preserve bytes for - a redirected run cannot answer the prompt.
|
|
34
|
+
io.stderr(safeForTerminal(`\n${request.summary}\n${request.preview}\n`, true));
|
|
28
35
|
const rl = createInterface({
|
|
29
36
|
input: io.input ?? process.stdin,
|
|
30
37
|
output: io.output ?? process.stderr,
|
package/dist/agent/loop.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loop.d.ts","sourceRoot":"","sources":["../../src/agent/loop.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"loop.d.ts","sourceRoot":"","sources":["../../src/agent/loop.ts"],"names":[],"mappings":"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"}
|
package/dist/agent/loop.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ApiError } from "../api/errors.js";
|
|
2
2
|
import { compactUntilItFits, requestSize, shouldCompact, } from "./context-budget.js";
|
|
3
|
+
import { isMicroUsd } from "../output/money.js";
|
|
3
4
|
import { toolToJsonSchema, } from "./tools/types.js";
|
|
4
5
|
/** Plan §7.4. */
|
|
5
6
|
export const DEFAULT_LIMITS = {
|
|
@@ -345,10 +346,6 @@ export function backoffMs(retryAfterSeconds, attempt, random = Math.random) {
|
|
|
345
346
|
function isTokenCount(value) {
|
|
346
347
|
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
347
348
|
}
|
|
348
|
-
/** Integer micro-USD, non-negative. Anything else is not a price. */
|
|
349
|
-
function isPrice(value) {
|
|
350
|
-
return typeof value === "string" && /^\d+$/.test(value);
|
|
351
|
-
}
|
|
352
349
|
/**
|
|
353
350
|
* W-1 · Can this model be priced at all, before anything is sent?
|
|
354
351
|
*
|
|
@@ -362,8 +359,8 @@ function isPrice(value) {
|
|
|
362
359
|
* direction - a free model would have been refused.
|
|
363
360
|
*/
|
|
364
361
|
export function hasValidPrices(model) {
|
|
365
|
-
return (
|
|
366
|
-
|
|
362
|
+
return (isMicroUsd(model?.inputPricePer1mMicroUsd) &&
|
|
363
|
+
isMicroUsd(model?.outputPricePer1mMicroUsd));
|
|
367
364
|
}
|
|
368
365
|
function canPrice(model, usage) {
|
|
369
366
|
if (usage === undefined)
|
package/dist/agent/session.d.ts
CHANGED
|
@@ -44,6 +44,8 @@ export interface SessionOptions {
|
|
|
44
44
|
onGoal?: (goal: string) => void;
|
|
45
45
|
/** R-4 · False when stdout is a pipe or a file, which get exact bytes. */
|
|
46
46
|
isTty?: boolean;
|
|
47
|
+
/** Whether stderr is a terminal; independent from stdout. */
|
|
48
|
+
isErrTty?: boolean;
|
|
47
49
|
/**
|
|
48
50
|
* A conversation to continue, rebuilt from a saved session (CLI-024).
|
|
49
51
|
* Appended to in place, exactly as the fresh array would be.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/agent/session.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAIrD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/agent/session.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAIrD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAK/C,OAAO,EAAY,KAAK,UAAU,EAAE,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;AACxE,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAapE,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,UAAU,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,oEAAoE;IACpE,OAAO,EAAE,SAAS,YAAY,EAAE,CAAC;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,WAAW,EAAE,WAAW,CAAC;IACzB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAC7C,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IACtC;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,0EAA0E;IAC1E,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,OAAO,CAAC,EAAE,WAAW,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,uEAAuE;IACvE,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;GAOG;AACH,wBAAsB,UAAU,CAC9B,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,cAAc,CAAC,CAqHzB;AAsHD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,MAAM,CA2B7D"}
|
package/dist/agent/session.js
CHANGED
|
@@ -2,7 +2,7 @@ import { createInterface } from "node:readline/promises";
|
|
|
2
2
|
import { isAgentEligible } from "../api/models.js";
|
|
3
3
|
import { formatUsdWithUnpriced } from "../output/money.js";
|
|
4
4
|
import { renderRecord } from "../output/format.js";
|
|
5
|
-
import { safeForTerminal } from "../output/terminal-safe.js";
|
|
5
|
+
import { safeForTerminal, terminalSafeWriter, } from "../output/terminal-safe.js";
|
|
6
6
|
import { runAgent } from "./loop.js";
|
|
7
7
|
const HELP = `Session commands:
|
|
8
8
|
/model [id] Show or switch the model
|
|
@@ -23,6 +23,18 @@ Anything else is sent to the model as the next goal.
|
|
|
23
23
|
* rather than the last question.
|
|
24
24
|
*/
|
|
25
25
|
export async function runSession(options) {
|
|
26
|
+
// Interactive session stderr is a human surface. Wrap it once so new slash
|
|
27
|
+
// commands and progress messages cannot accidentally bypass the boundary.
|
|
28
|
+
// Preserve a dynamic signal getter: the TUI swaps its AbortController after
|
|
29
|
+
// cancelling one request so the session can return to the prompt.
|
|
30
|
+
const signalDescriptor = Object.getOwnPropertyDescriptor(options, "signal");
|
|
31
|
+
options = {
|
|
32
|
+
...options,
|
|
33
|
+
stderr: terminalSafeWriter(options.stderr, options.isErrTty ?? true),
|
|
34
|
+
};
|
|
35
|
+
if (signalDescriptor?.get !== undefined) {
|
|
36
|
+
Object.defineProperty(options, "signal", signalDescriptor);
|
|
37
|
+
}
|
|
26
38
|
const history = options.history ?? [];
|
|
27
39
|
const summary = {
|
|
28
40
|
goals: 0,
|
|
@@ -105,7 +117,7 @@ export async function runSession(options) {
|
|
|
105
117
|
options.stdout(`${safeForTerminal(report.finalText, options.isTty ?? true)}\n`);
|
|
106
118
|
}
|
|
107
119
|
if (report.stopReason !== "completed") {
|
|
108
|
-
options
|
|
120
|
+
humanError(options, `[${report.stopReason}${report.error ? `: ${report.error}` : ""}]\n`);
|
|
109
121
|
}
|
|
110
122
|
if (report.stopReason === "cancelled" && options.signal?.aborted)
|
|
111
123
|
break;
|
|
@@ -296,12 +308,15 @@ function lineSource(input) {
|
|
|
296
308
|
function reportEvent(event, options) {
|
|
297
309
|
if (event.type === "tool") {
|
|
298
310
|
const first = event.detail.split("\n")[0].slice(0, 100);
|
|
299
|
-
options
|
|
311
|
+
humanError(options, ` ${event.ok ? "-" : "!"} ${event.name}: ${first}\n`);
|
|
300
312
|
}
|
|
301
313
|
else if (event.type === "declined") {
|
|
302
|
-
options
|
|
314
|
+
humanError(options, ` declined: ${event.summary}\n`);
|
|
303
315
|
}
|
|
304
316
|
else if (event.type === "rate_limited") {
|
|
305
317
|
options.stderr(` rate limited, waiting ${event.waitMs}ms (attempt ${event.attempt})\n`);
|
|
306
318
|
}
|
|
307
319
|
}
|
|
320
|
+
function humanError(options, text) {
|
|
321
|
+
options.stderr(safeForTerminal(text, options.isErrTty ?? true));
|
|
322
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"account.d.ts","sourceRoot":"","sources":["../../src/api/account.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"account.d.ts","sourceRoot":"","sources":["../../src/api/account.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AA6C5C;;;GAGG;AAEH,MAAM,WAAW,MAAM;IACrB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,UAAU,CAAC,EAAE;QACX,mBAAmB,EAAE,MAAM,CAAC;QAC5B,cAAc,EAAE,MAAM,CAAC;QACvB,mBAAmB,EAAE,MAAM,CAAC;QAC5B,aAAa,EAAE,MAAM,CAAC;QACtB,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;CACH;AAyBD,wBAAsB,WAAW,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAKrE;AAED,wBAAsB,UAAU,CAC9B,MAAM,EAAE,UAAU,EAClB,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,YAAY,CAAC,CAMvB;AAED,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,UAAU,GACjB,OAAO,CAAC,YAAY,CAAC,CAKvB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,yBAAyB,EAAE,MAAM,CAAC;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB;AAOD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,MAAM,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAC7C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB;;;;;;OAMG;IACH,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B;AAED,gFAAgF;AAChF,eAAO,MAAM,qBAAqB,KAAK,CAAC;AAExC;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,UAAU,EAClB,OAAO,GAAE;IACP,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,WAAW,CAAC;CACjB,GACL,OAAO,CAAC,iBAAiB,CAAC,CAgC5B"}
|
package/dist/api/account.js
CHANGED
|
@@ -1,4 +1,47 @@
|
|
|
1
1
|
import { ApiError } from "./errors.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { parseApiData } from "./validate.js";
|
|
4
|
+
const integralMoney = z.string().regex(/^-?\d+$/);
|
|
5
|
+
const count = z.number().safe().int().nonnegative();
|
|
6
|
+
const walletSchema = z
|
|
7
|
+
.object({
|
|
8
|
+
balance_micro_usd: integralMoney,
|
|
9
|
+
bonus_micro_usd: integralMoney,
|
|
10
|
+
frozen_micro_usd: integralMoney,
|
|
11
|
+
available_micro_usd: integralMoney,
|
|
12
|
+
currency_anchor: z.string(),
|
|
13
|
+
})
|
|
14
|
+
.passthrough();
|
|
15
|
+
const usageSummarySchema = z
|
|
16
|
+
.object({
|
|
17
|
+
days: count,
|
|
18
|
+
from: z.string(),
|
|
19
|
+
to: z.string(),
|
|
20
|
+
count,
|
|
21
|
+
input_tokens: count,
|
|
22
|
+
output_tokens: count,
|
|
23
|
+
total_tokens: count,
|
|
24
|
+
charge_micro_usd: integralMoney,
|
|
25
|
+
})
|
|
26
|
+
.passthrough();
|
|
27
|
+
const subscriptionSchema = z
|
|
28
|
+
.object({
|
|
29
|
+
status: z.string(),
|
|
30
|
+
plan_slug: z.string().optional(),
|
|
31
|
+
plan_name: z.string().optional(),
|
|
32
|
+
current_period_end: z.string().optional(),
|
|
33
|
+
playground: z
|
|
34
|
+
.object({
|
|
35
|
+
allowance_micro_usd: integralMoney,
|
|
36
|
+
used_micro_usd: integralMoney,
|
|
37
|
+
remaining_micro_usd: integralMoney,
|
|
38
|
+
usage_percent: z.number().finite(),
|
|
39
|
+
state: z.string(),
|
|
40
|
+
})
|
|
41
|
+
.passthrough()
|
|
42
|
+
.optional(),
|
|
43
|
+
})
|
|
44
|
+
.passthrough();
|
|
2
45
|
/**
|
|
3
46
|
* The account plane answers 401 with its own error shape when the key lacks
|
|
4
47
|
* `account:read`. That is a scope problem, not a bad key, and saying so saves
|
|
@@ -22,20 +65,20 @@ export async function fetchWallet(client) {
|
|
|
22
65
|
const { data } = await client
|
|
23
66
|
.getJson("/keyinfo/wallet")
|
|
24
67
|
.catch(explainScope);
|
|
25
|
-
return data;
|
|
68
|
+
return parseApiData(walletSchema, data, "wallet API");
|
|
26
69
|
}
|
|
27
70
|
export async function fetchUsage(client, days) {
|
|
28
71
|
const query = days === undefined ? "" : `?days=${days}`;
|
|
29
72
|
const { data } = await client
|
|
30
73
|
.getJson(`/keyinfo/usage-summary${query}`)
|
|
31
74
|
.catch(explainScope);
|
|
32
|
-
return data;
|
|
75
|
+
return parseApiData(usageSummarySchema, data, "usage API");
|
|
33
76
|
}
|
|
34
77
|
export async function fetchSubscription(client) {
|
|
35
78
|
const { data } = await client
|
|
36
79
|
.getJson("/keyinfo/subscription")
|
|
37
80
|
.catch(explainScope);
|
|
38
|
-
return data;
|
|
81
|
+
return parseApiData(subscriptionSchema, data, "subscription API");
|
|
39
82
|
}
|
|
40
83
|
/** Hard stop on paging, so a busy key cannot turn one command into hundreds. */
|
|
41
84
|
export const MAX_USAGE_EVENT_PAGES = 20;
|
package/dist/api/models.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../src/api/models.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../src/api/models.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AA6B5C,iFAAiF;AACjF,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,0CAA0C;AAC1C,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,oBAAoB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACtC;AAED,MAAM,WAAW,aAAc,SAAQ,YAAY;IACjD,aAAa,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,SAAS;IACxB,mDAAmD;IACnD,SAAS,EAAE,aAAa,EAAE,CAAC;IAC3B,sFAAsF;IACtF,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,gDAAgD;IAChD,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,YAAY,GAAG,SAAS,GAAG,OAAO,CAUxE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAC7B,SAAS,EAAE,SAAS,cAAc,EAAE,EACpC,OAAO,EAAE,SAAS,YAAY,EAAE,GAC/B,SAAS,CA4BX;AAED,8CAA8C;AAC9C,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,UAAU,GACjB,OAAO,CAAC,cAAc,EAAE,CAAC,CAK3B;AAED,2EAA2E;AAC3E,wBAAsB,YAAY,CAChC,MAAM,EAAE,UAAU,GACjB,OAAO,CAAC,YAAY,EAAE,CAAC,CAKzB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAGnD;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,SAAS,YAAY,EAAE,EAChC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACf,YAAY,GAAG,SAAS,CAY1B;AAED,qBAAa,oBAAqB,SAAQ,KAAK;IAE3C,QAAQ,CAAC,OAAO,EAAE,MAAM;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM;gBAFhB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM;CAO5B"}
|
package/dist/api/models.js
CHANGED
|
@@ -1,3 +1,29 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { parseApiData } from "./validate.js";
|
|
3
|
+
const dataPlaneModelsSchema = z
|
|
4
|
+
.object({
|
|
5
|
+
data: z.array(z.object({ id: z.string() }).passthrough()).optional(),
|
|
6
|
+
})
|
|
7
|
+
.passthrough();
|
|
8
|
+
const catalogModelSchema = z
|
|
9
|
+
.object({
|
|
10
|
+
modelId: z.string(),
|
|
11
|
+
displayName: z.string().optional(),
|
|
12
|
+
provider: z.string().optional(),
|
|
13
|
+
capabilities: z.array(z.string()).optional(),
|
|
14
|
+
apiSurface: z.string().optional(),
|
|
15
|
+
contextLength: z.number().safe().int().nonnegative().optional(),
|
|
16
|
+
inputPricePer1mMicroUsd: z.string().optional(),
|
|
17
|
+
outputPricePer1mMicroUsd: z.string().optional(),
|
|
18
|
+
accessTier: z.string().optional(),
|
|
19
|
+
deprecated: z.boolean().optional(),
|
|
20
|
+
retires_at: z.string().nullable().optional(),
|
|
21
|
+
replacement_model_id: z.string().nullable().optional(),
|
|
22
|
+
})
|
|
23
|
+
.passthrough();
|
|
24
|
+
const catalogSchema = z
|
|
25
|
+
.object({ data: z.array(catalogModelSchema).optional() })
|
|
26
|
+
.passthrough();
|
|
1
27
|
/**
|
|
2
28
|
* Agent admission. Every condition is something the catalogue states outright;
|
|
3
29
|
* nothing is inferred from the model id, the provider or the display name —
|
|
@@ -59,13 +85,13 @@ export function intersectModels(dataPlane, catalog) {
|
|
|
59
85
|
}
|
|
60
86
|
/** GET /v1/models — the key's access list. */
|
|
61
87
|
export async function fetchAccessibleModels(client) {
|
|
62
|
-
const
|
|
63
|
-
return data.data ?? [];
|
|
88
|
+
const response = await client.getJson("/models");
|
|
89
|
+
return (parseApiData(dataPlaneModelsSchema, response.data, "model API").data ?? []);
|
|
64
90
|
}
|
|
65
91
|
/** GET /models on the business API — public catalogue, no key required. */
|
|
66
92
|
export async function fetchCatalog(client) {
|
|
67
|
-
const
|
|
68
|
-
return data.data ?? [];
|
|
93
|
+
const response = await client.getJson("/models");
|
|
94
|
+
return (parseApiData(catalogSchema, response.data, "model catalogue").data ?? []);
|
|
69
95
|
}
|
|
70
96
|
/**
|
|
71
97
|
* U-3 · Is this a usable model identifier?
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sse-parser.d.ts","sourceRoot":"","sources":["../../src/api/sse-parser.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,6DAA6D;IAC7D,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,oCAAoC;IACpC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CAClC;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,gBAAgB,EAAE,CAAC;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,IAAI,EAAE,OAAO,CAAC;CACf;AAWD,+EAA+E;AAC/E,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAE7C,IAAI,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,EAAE;IAMjC,kDAAkD;IAClD,GAAG,IAAI,MAAM,EAAE;IASf,OAAO,CAAC,KAAK;CAYd;AAED,8DAA8D;AAC9D,qBAAa,mBAAmB;IAOlB,OAAO,CAAC,QAAQ,CAAC,MAAM;IANnC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAuC;IAC7D,OAAO,CAAC,IAAI,CAAM;IAClB,OAAO,CAAC,YAAY,CAAC,CAAS;IAC9B,OAAO,CAAC,KAAK,CAAC,CAAc;IAC5B,OAAO,CAAC,IAAI,CAAS;gBAEQ,MAAM,GAAE,YAAiB;IAEtD,4DAA4D;IAC5D,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAgD9B,MAAM,IAAI,YAAY;CASvB;
|
|
1
|
+
{"version":3,"file":"sse-parser.d.ts","sourceRoot":"","sources":["../../src/api/sse-parser.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,6DAA6D;IAC7D,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,oCAAoC;IACpC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CAClC;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,gBAAgB,EAAE,CAAC;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,IAAI,EAAE,OAAO,CAAC;CACf;AAWD,+EAA+E;AAC/E,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAE7C,IAAI,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,EAAE;IAMjC,kDAAkD;IAClD,GAAG,IAAI,MAAM,EAAE;IASf,OAAO,CAAC,KAAK;CAYd;AAED,8DAA8D;AAC9D,qBAAa,mBAAmB;IAOlB,OAAO,CAAC,QAAQ,CAAC,MAAM;IANnC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAuC;IAC7D,OAAO,CAAC,IAAI,CAAM;IAClB,OAAO,CAAC,YAAY,CAAC,CAAS;IAC9B,OAAO,CAAC,KAAK,CAAC,CAAc;IAC5B,OAAO,CAAC,IAAI,CAAS;gBAEQ,MAAM,GAAE,YAAiB;IAEtD,4DAA4D;IAC5D,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAgD9B,MAAM,IAAI,YAAY;CASvB;AAaD,kEAAkE;AAClE,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,oEAAoE;IACpE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC;CAC7B;AAED,eAAO,MAAM,sBAAsB,SAAU,CAAC;AAC9C,eAAO,MAAM,wBAAwB,QAAmB,CAAC;AACzD,0EAA0E;AAC1E,eAAO,MAAM,uBAAuB,QAAc,CAAC;AAEnD;;;;;;;;;;;GAWG;AACH,wBAAsB,cAAc,CAClC,QAAQ,EAAE,QAAQ,EAClB,MAAM,GAAE,YAAiB,EACzB,MAAM,CAAC,EAAE,WAAW,EACpB,MAAM,GAAE,YAAiB,GACxB,OAAO,CAAC,YAAY,CAAC,CAqGvB"}
|
package/dist/api/sse-parser.js
CHANGED
|
@@ -61,7 +61,7 @@ export class ChatStreamAssembler {
|
|
|
61
61
|
}
|
|
62
62
|
// Read usage wherever it appears — it rides a frame that still has a
|
|
63
63
|
// (empty-delta) choice on this gateway.
|
|
64
|
-
if (chunk.usage)
|
|
64
|
+
if (isStreamUsage(chunk.usage))
|
|
65
65
|
this.usage = chunk.usage;
|
|
66
66
|
const choice = chunk.choices?.[0];
|
|
67
67
|
if (!choice)
|
|
@@ -100,6 +100,14 @@ export class ChatStreamAssembler {
|
|
|
100
100
|
};
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
|
+
function isStreamUsage(value) {
|
|
104
|
+
if (value === null || typeof value !== "object")
|
|
105
|
+
return false;
|
|
106
|
+
const usage = value;
|
|
107
|
+
return ["prompt_tokens", "completion_tokens", "total_tokens"].every((key) => typeof usage[key] === "number" &&
|
|
108
|
+
Number.isSafeInteger(usage[key]) &&
|
|
109
|
+
usage[key] >= 0);
|
|
110
|
+
}
|
|
103
111
|
export const DEFAULT_STREAM_IDLE_MS = 120_000;
|
|
104
112
|
export const DEFAULT_STREAM_MAX_BYTES = 32 * 1024 * 1024;
|
|
105
113
|
/** Matches the agent's wall-clock ceiling (§7.4), for the same reason. */
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/api/validate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAK7B,kFAAkF;AAClF,wBAAgB,YAAY,CAAC,CAAC,EAC5B,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EACpB,KAAK,EAAE,OAAO,EACd,KAAK,EAAE,MAAM,GACZ,CAAC,CAUH"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ExitCode } from "../output/exit-codes.js";
|
|
2
|
+
import { ApiError } from "./errors.js";
|
|
3
|
+
/** Validate untrusted JSON at the HTTP boundary and expose a stable CLI error. */
|
|
4
|
+
export function parseApiData(schema, value, label) {
|
|
5
|
+
const parsed = schema.safeParse(value);
|
|
6
|
+
if (parsed.success)
|
|
7
|
+
return parsed.data;
|
|
8
|
+
const issue = parsed.error.issues[0];
|
|
9
|
+
const path = issue?.path.length ? ` at ${issue.path.join(".")}` : "";
|
|
10
|
+
throw new ApiError({
|
|
11
|
+
kind: "server",
|
|
12
|
+
exitCode: ExitCode.Unavailable,
|
|
13
|
+
message: `${label} returned an invalid response${path}`,
|
|
14
|
+
});
|
|
15
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"account.d.ts","sourceRoot":"","sources":["../../src/commands/account.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"account.d.ts","sourceRoot":"","sources":["../../src/commands/account.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAQnD,OAAO,EAIL,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAUtB,wBAAsB,cAAc,CAClC,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,EAAE,EAAE,SAAS,GACZ,OAAO,CAAC,QAAQ,CAAC,CA8GnB"}
|
package/dist/commands/account.js
CHANGED
|
@@ -2,6 +2,7 @@ import { fetchSubscription, fetchUsage, fetchWallet } from "../api/account.js";
|
|
|
2
2
|
import { ExitCode } from "../output/exit-codes.js";
|
|
3
3
|
import { parseFormatFlags, renderJson, renderRecord, } from "../output/format.js";
|
|
4
4
|
import { formatUsd } from "../output/money.js";
|
|
5
|
+
import { safeForTerminal } from "../output/terminal-safe.js";
|
|
5
6
|
import { buildRuntime, reportError, requireApiKey, } from "./runtime.js";
|
|
6
7
|
const USAGE = `Usage:
|
|
7
8
|
mafdet account balance [--json]
|
|
@@ -30,12 +31,12 @@ export async function accountCommand(argv, io) {
|
|
|
30
31
|
io.stdout(renderJson(wallet));
|
|
31
32
|
return ExitCode.Ok;
|
|
32
33
|
}
|
|
33
|
-
io.stdout(renderRecord([
|
|
34
|
+
io.stdout(safeForTerminal(renderRecord([
|
|
34
35
|
["balance", formatUsd(wallet.balance_micro_usd)],
|
|
35
36
|
["bonus", formatUsd(wallet.bonus_micro_usd)],
|
|
36
37
|
["frozen", formatUsd(wallet.frozen_micro_usd)],
|
|
37
38
|
["available", formatUsd(wallet.available_micro_usd)],
|
|
38
|
-
]));
|
|
39
|
+
]), io.isTty ?? process.stdout.isTTY === true));
|
|
39
40
|
return ExitCode.Ok;
|
|
40
41
|
}
|
|
41
42
|
if (subcommand === "usage") {
|
|
@@ -57,7 +58,7 @@ export async function accountCommand(argv, io) {
|
|
|
57
58
|
io.stdout(renderJson(usage));
|
|
58
59
|
return ExitCode.Ok;
|
|
59
60
|
}
|
|
60
|
-
io.stdout(renderRecord([
|
|
61
|
+
io.stdout(safeForTerminal(renderRecord([
|
|
61
62
|
["window", `${usage.days} day(s), ${usage.from} -> ${usage.to}`],
|
|
62
63
|
// Throttled attempts land in the ledger with a zero charge
|
|
63
64
|
// (CLI-001), so the count and the money answer different questions.
|
|
@@ -68,7 +69,7 @@ export async function accountCommand(argv, io) {
|
|
|
68
69
|
["input tokens", String(usage.input_tokens)],
|
|
69
70
|
["output tokens", String(usage.output_tokens)],
|
|
70
71
|
["charged", formatUsd(usage.charge_micro_usd)],
|
|
71
|
-
]));
|
|
72
|
+
]), io.isTty ?? process.stdout.isTTY === true));
|
|
72
73
|
return ExitCode.Ok;
|
|
73
74
|
}
|
|
74
75
|
if (args.length > 0)
|
|
@@ -90,7 +91,7 @@ export async function accountCommand(argv, io) {
|
|
|
90
91
|
`${formatUsd(pg.used_micro_usd)} (${pg.usage_percent}%)`,
|
|
91
92
|
], ["playground left", formatUsd(pg.remaining_micro_usd)]);
|
|
92
93
|
}
|
|
93
|
-
io.stdout(renderRecord(rows));
|
|
94
|
+
io.stdout(safeForTerminal(renderRecord(rows), io.isTty ?? process.stdout.isTTY === true));
|
|
94
95
|
return ExitCode.Ok;
|
|
95
96
|
}
|
|
96
97
|
catch (error) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/commands/agent.ts"],"names":[],"mappings":"AAQA,OAAO,EAAe,KAAK,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAiBvE,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAGnD,OAAO,EAKL,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAmBtB,eAAO,MAAM,gBAAgB,0XAK+C,CAAC;AAE7E,eAAO,MAAM,YAAY,mgBAOmB,CAAC;AAE7C,MAAM,WAAW,OAAQ,SAAQ,SAAS;IACxC,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,qDAAqD;IACrD,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,EAAE,OAAO,uBAAuB,EAAE,QAAQ,CAAC;IACpD;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;CAC9C;AAED,wBAAsB,YAAY,CAChC,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,EAAE,EAAE,OAAO,GACV,OAAO,CAAC,QAAQ,CAAC,
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/commands/agent.ts"],"names":[],"mappings":"AAQA,OAAO,EAAe,KAAK,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAiBvE,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAGnD,OAAO,EAKL,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAmBtB,eAAO,MAAM,gBAAgB,0XAK+C,CAAC;AAE7E,eAAO,MAAM,YAAY,mgBAOmB,CAAC;AAE7C,MAAM,WAAW,OAAQ,SAAQ,SAAS;IACxC,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,qDAAqD;IACrD,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,EAAE,OAAO,uBAAuB,EAAE,QAAQ,CAAC;IACpD;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;CAC9C;AAED,wBAAsB,YAAY,CAChC,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,EAAE,EAAE,OAAO,GACV,OAAO,CAAC,QAAQ,CAAC,CA2InB;AAoLD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,YAAY,EAAE,kBAAkB,EAAE,OAAO,0JAI3E"}
|
package/dist/commands/agent.js
CHANGED
|
@@ -82,7 +82,7 @@ export async function agentCommand(argv, io) {
|
|
|
82
82
|
if (parsed.allowWrite && !interactive) {
|
|
83
83
|
io.stderr("note: --allow-write needs an interactive terminal to confirm changes; running read-only\n");
|
|
84
84
|
}
|
|
85
|
-
io
|
|
85
|
+
writeHumanError(io, `workspace: ${workspace.root}\nmodel: ${model} (${mode})\n`);
|
|
86
86
|
if (parsed.goal.length === 0) {
|
|
87
87
|
if (!interactive) {
|
|
88
88
|
io.stderr(`mafdet agent: no goal\n${USAGE}`);
|
|
@@ -119,6 +119,7 @@ export async function agentCommand(argv, io) {
|
|
|
119
119
|
stderr: io.stderr,
|
|
120
120
|
signal: io.signal,
|
|
121
121
|
isTty: isTty(io),
|
|
122
|
+
isErrTty: io.isErrTty ?? process.stderr.isTTY === true,
|
|
122
123
|
...(io.readLine === undefined ? {} : { readLine: io.readLine }),
|
|
123
124
|
});
|
|
124
125
|
}
|
|
@@ -345,7 +346,7 @@ function reportEvent(event, io) {
|
|
|
345
346
|
io.stderr(`\n[turn ${event.turn}]\n`);
|
|
346
347
|
else if (event.type === "tool") {
|
|
347
348
|
const first = event.detail.split("\n")[0].slice(0, 100);
|
|
348
|
-
io
|
|
349
|
+
writeHumanError(io, ` ${event.ok ? "-" : "!"} ${event.name}: ${first}\n`);
|
|
349
350
|
}
|
|
350
351
|
else if (event.type === "rate_limited") {
|
|
351
352
|
io.stderr(` rate limited, waiting ${event.waitMs}ms (attempt ${event.attempt})\n`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../src/commands/chat.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgB,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACnE,OAAO,EAAkB,KAAK,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAWxE,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAEnD,OAAO,EAIL,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAatB,MAAM,WAAW,MAAO,SAAQ,SAAS;IACvC,qDAAqD;IACrD,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IAClC,gDAAgD;IAChD,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAcD,wBAAsB,WAAW,CAC/B,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,EAAE,EAAE,MAAM,GACT,OAAO,CAAC,QAAQ,CAAC,CA8BnB;
|
|
1
|
+
{"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../src/commands/chat.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgB,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACnE,OAAO,EAAkB,KAAK,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAWxE,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAEnD,OAAO,EAIL,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAatB,MAAM,WAAW,MAAO,SAAQ,SAAS;IACvC,qDAAqD;IACrD,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IAClC,gDAAgD;IAChD,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAcD,wBAAsB,WAAW,CAC/B,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,EAAE,EAAE,MAAM,GACT,OAAO,CAAC,QAAQ,CAAC,CA8BnB;AAgTD,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,YAAY,GAAG,SAAS,EAC/B,KAAK,EAAE,WAAW,GACjB,MAAM,GAAG,SAAS,CAYpB"}
|
package/dist/commands/chat.js
CHANGED
|
@@ -9,7 +9,7 @@ import { upsertIndexEntry } from "../sessions/index-file.js";
|
|
|
9
9
|
import { decideRecording } from "../sessions/policy.js";
|
|
10
10
|
import { SessionWriter } from "../sessions/store.js";
|
|
11
11
|
import { ExitCode } from "../output/exit-codes.js";
|
|
12
|
-
import { formatUsd } from "../output/money.js";
|
|
12
|
+
import { formatUsd, isMicroUsd } from "../output/money.js";
|
|
13
13
|
import { buildRuntime, reportError, requireApiKey, } from "./runtime.js";
|
|
14
14
|
const USAGE = `Usage:
|
|
15
15
|
mafdet chat [prompt] [--model <id>] [--system <file>] [--input <file>]
|
|
@@ -276,11 +276,13 @@ async function streamChat(runtime, body, args, io) {
|
|
|
276
276
|
}
|
|
277
277
|
async function completeChat(runtime, body, args, io) {
|
|
278
278
|
const { data, requestId } = await runtime.dataPlane.postJson("/chat/completions", body, { signal: io.signal });
|
|
279
|
-
const
|
|
279
|
+
const rawAnswer = data.choices?.[0]?.message?.content;
|
|
280
|
+
const answer = typeof rawAnswer === "string" ? rawAnswer : "";
|
|
281
|
+
const usage = validUsage(data.usage);
|
|
280
282
|
io.stdout(`${safeForTerminal(answer, isTty(io))}\n`);
|
|
281
283
|
const model = await priceModel(runtime, args.model, !args.quiet || args.save);
|
|
282
|
-
summarise(args,
|
|
283
|
-
saveChat(args, io, answer,
|
|
284
|
+
summarise(args, usage, io, model);
|
|
285
|
+
saveChat(args, io, answer, usage, requestId, model);
|
|
284
286
|
return ExitCode.Ok;
|
|
285
287
|
}
|
|
286
288
|
/**
|
|
@@ -300,9 +302,32 @@ function summarise(args, usage, io, model, options = {}) {
|
|
|
300
302
|
io.stderr(`${prefix}${usage.prompt_tokens} in / ${usage.completion_tokens} out tokens${cost}\n`);
|
|
301
303
|
}
|
|
302
304
|
export function estimateChargeMicroUsd(model, usage) {
|
|
303
|
-
if (!model?.inputPricePer1mMicroUsd ||
|
|
305
|
+
if (!isMicroUsd(model?.inputPricePer1mMicroUsd) ||
|
|
306
|
+
!isMicroUsd(model.outputPricePer1mMicroUsd))
|
|
304
307
|
return undefined;
|
|
305
308
|
const ceilDiv = (tokens, pricePer1m) => (BigInt(tokens) * BigInt(pricePer1m) + 999999n) / 1000000n;
|
|
306
309
|
return (ceilDiv(usage.prompt_tokens, model.inputPricePer1mMicroUsd) +
|
|
307
310
|
ceilDiv(usage.completion_tokens, model.outputPricePer1mMicroUsd));
|
|
308
311
|
}
|
|
312
|
+
function validUsage(value) {
|
|
313
|
+
if (value === null || typeof value !== "object")
|
|
314
|
+
return undefined;
|
|
315
|
+
const usage = value;
|
|
316
|
+
const count = (field) => {
|
|
317
|
+
const candidate = usage[field];
|
|
318
|
+
return typeof candidate === "number" &&
|
|
319
|
+
Number.isSafeInteger(candidate) &&
|
|
320
|
+
candidate >= 0
|
|
321
|
+
? candidate
|
|
322
|
+
: undefined;
|
|
323
|
+
};
|
|
324
|
+
const prompt = count("prompt_tokens");
|
|
325
|
+
const completion = count("completion_tokens");
|
|
326
|
+
if (prompt === undefined || completion === undefined)
|
|
327
|
+
return undefined;
|
|
328
|
+
return {
|
|
329
|
+
prompt_tokens: prompt,
|
|
330
|
+
completion_tokens: completion,
|
|
331
|
+
total_tokens: count("total_tokens") ?? prompt + completion,
|
|
332
|
+
};
|
|
333
|
+
}
|
package/dist/commands/models.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { fetchAccessibleModels, fetchCatalog, intersectModels, } from "../api/models.js";
|
|
2
2
|
import { ExitCode } from "../output/exit-codes.js";
|
|
3
3
|
import { parseFormatFlags, renderJson, renderPlain, renderRecord, renderTable, } from "../output/format.js";
|
|
4
|
-
import { formatUsd } from "../output/money.js";
|
|
4
|
+
import { formatUsd, isMicroUsd } from "../output/money.js";
|
|
5
5
|
import { safeForTerminal } from "../output/terminal-safe.js";
|
|
6
6
|
import { buildRuntime, reportError, requireApiKey, } from "./runtime.js";
|
|
7
7
|
const USAGE = `Usage:
|
|
@@ -151,20 +151,20 @@ function showModel(sets, args, format, io) {
|
|
|
151
151
|
["agent eligible", model.agentEligible ? "yes" : "no"],
|
|
152
152
|
[
|
|
153
153
|
"input / 1M",
|
|
154
|
-
model.inputPricePer1mMicroUsd
|
|
154
|
+
isMicroUsd(model.inputPricePer1mMicroUsd)
|
|
155
155
|
? formatUsd(model.inputPricePer1mMicroUsd)
|
|
156
156
|
: "-",
|
|
157
157
|
],
|
|
158
158
|
[
|
|
159
159
|
"output / 1M",
|
|
160
|
-
model.outputPricePer1mMicroUsd
|
|
160
|
+
isMicroUsd(model.outputPricePer1mMicroUsd)
|
|
161
161
|
? formatUsd(model.outputPricePer1mMicroUsd)
|
|
162
162
|
: "-",
|
|
163
163
|
],
|
|
164
164
|
[
|
|
165
165
|
"deprecated",
|
|
166
166
|
model.deprecated
|
|
167
|
-
? `yes${model.retires_at ? ` (retires ${model.retires_at})` : ""}`
|
|
167
|
+
? clean(`yes${model.retires_at ? ` (retires ${model.retires_at})` : ""}`)
|
|
168
168
|
: "no",
|
|
169
169
|
],
|
|
170
170
|
]));
|
package/dist/output/money.d.ts
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
* reason: a float round-trip can turn a balance into a number that does not
|
|
6
6
|
* match the invoice. Everything here is BigInt in, string out.
|
|
7
7
|
*/
|
|
8
|
+
/** A non-negative integral micro-USD value as spoken by Mafdet APIs. */
|
|
9
|
+
export declare function isMicroUsd(value: unknown): value is string;
|
|
8
10
|
export declare function parseMicroUsd(value: string | number | bigint): bigint;
|
|
9
11
|
/**
|
|
10
12
|
* Render micro-USD as dollars. `minimumFractionDigits` keeps small amounts
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"money.d.ts","sourceRoot":"","sources":["../../src/output/money.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAOrE;AAED;;;GAGG;AACH,wBAAgB,SAAS,CACvB,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,EAClC,OAAO,GAAE;IAAE,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAAO,GAC3C,MAAM,CAeR;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,GAAG,MAAM,EACzB,gBAAgB,EAAE,MAAM,GACvB,MAAM,CAIR"}
|
|
1
|
+
{"version":3,"file":"money.d.ts","sourceRoot":"","sources":["../../src/output/money.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,wEAAwE;AACxE,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAE1D;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAOrE;AAED;;;GAGG;AACH,wBAAgB,SAAS,CACvB,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,EAClC,OAAO,GAAE;IAAE,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAAO,GAC3C,MAAM,CAeR;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,GAAG,MAAM,EACzB,gBAAgB,EAAE,MAAM,GACvB,MAAM,CAIR"}
|
package/dist/output/money.js
CHANGED
|
@@ -6,6 +6,10 @@
|
|
|
6
6
|
* match the invoice. Everything here is BigInt in, string out.
|
|
7
7
|
*/
|
|
8
8
|
const MICROS_PER_USD = 1000000n;
|
|
9
|
+
/** A non-negative integral micro-USD value as spoken by Mafdet APIs. */
|
|
10
|
+
export function isMicroUsd(value) {
|
|
11
|
+
return typeof value === "string" && /^\d+$/.test(value);
|
|
12
|
+
}
|
|
9
13
|
export function parseMicroUsd(value) {
|
|
10
14
|
if (typeof value === "bigint")
|
|
11
15
|
return value;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../../src/tui/adapters/models.ts"],"names":[],"mappings":"AAiBA,OAAO,EAIL,KAAK,SAAS,EACd,KAAK,aAAa,EACnB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAgB,KAAK,SAAS,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../../src/tui/adapters/models.ts"],"names":[],"mappings":"AAiBA,OAAO,EAIL,KAAK,SAAS,EACd,KAAK,aAAa,EACnB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAgB,KAAK,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAMzE;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,QAAQ,CAAC;AAEzC,MAAM,WAAW,cAAe,SAAQ,SAAS;IAC/C,uEAAuE;IACvE,QAAQ,CAAC,IAAI,EAAE,SAAS,aAAa,EAAE,CAAC;IACxC,wDAAwD;IACxD,QAAQ,CAAC,UAAU,EAAE,SAAS,aAAa,EAAE,CAAC;CAC/C;AAED,wBAAsB,aAAa,CAAC,OAAO,EAAE;IAC3C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,EAAE,SAAS,CAAC;CACxB,GAAG,OAAO,CAAC,cAAc,CAAC,CAoB1B;AAED,8EAA8E;AAC9E,wBAAgB,aAAa,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,CAoB1D;AASD;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,SAAS,aAAa,EAAE,EAChC,KAAK,EAAE,MAAM,GACZ,SAAS,aAAa,EAAE,CAgB1B;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,qEAAqE;IACrE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE;IACxC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IAChC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B,GAAG,UAAU,CA8Cb"}
|
|
@@ -13,6 +13,7 @@ import { buildRuntime } from "../../commands/runtime.js";
|
|
|
13
13
|
import { parseUserConfig } from "../../config/config.js";
|
|
14
14
|
import { userConfigPath } from "../../config/paths.js";
|
|
15
15
|
import { formatUsd } from "../../output/money.js";
|
|
16
|
+
import { isMicroUsd } from "../../output/money.js";
|
|
16
17
|
/**
|
|
17
18
|
* The catalogue does not publish a per-route RPM - checked against the live
|
|
18
19
|
* `/models` response, which has 31 models and no rate field at all. Plan §19.5
|
|
@@ -62,7 +63,7 @@ export function describeModel(model) {
|
|
|
62
63
|
function priceSummary(model) {
|
|
63
64
|
const input = model.inputPricePer1mMicroUsd;
|
|
64
65
|
const output = model.outputPricePer1mMicroUsd;
|
|
65
|
-
if (input
|
|
66
|
+
if (!isMicroUsd(input) || !isMicroUsd(output))
|
|
66
67
|
return undefined;
|
|
67
68
|
return `${formatUsd(input)}/${formatUsd(output)} per 1M`;
|
|
68
69
|
}
|
package/dist/tui/tool-card.js
CHANGED
|
@@ -122,11 +122,11 @@ function firstLine(text) {
|
|
|
122
122
|
export function renderCard(card, capabilities) {
|
|
123
123
|
const { dim, bold, accent } = palette(capabilities);
|
|
124
124
|
const glyph = GLYPHS[card.state];
|
|
125
|
-
const head = ` ${glyph} ${bold(card.name)} ${dim(card.arguments)}`;
|
|
125
|
+
const head = ` ${glyph} ${bold(stripAnsi(card.name))} ${dim(card.arguments)}`;
|
|
126
126
|
if (card.detail === undefined || card.detail.length === 0)
|
|
127
127
|
return `${head}\n`;
|
|
128
128
|
const detail = card.state === "failed" || card.state === "refused"
|
|
129
|
-
? accent(card.detail)
|
|
130
|
-
: dim(card.detail);
|
|
129
|
+
? accent(firstLine(card.detail))
|
|
130
|
+
: dim(firstLine(card.detail));
|
|
131
131
|
return `${head}\n ${detail}\n`;
|
|
132
132
|
}
|