@zenera/cli 1.1.24 → 1.1.26
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/README.md +65 -0
- package/dist/ask.js +28 -5
- package/dist/banner.js +2 -2
- package/dist/batch.d.ts +6 -0
- package/dist/batch.js +76 -1
- package/dist/catalog.d.ts +31 -1
- package/dist/catalog.js +140 -7
- package/dist/commands/index.js +2 -0
- package/dist/commands/inspect.d.ts +52 -2
- package/dist/commands/inspect.js +220 -31
- package/dist/commands/memory.js +156 -32
- package/dist/commands/meta.js +333 -24
- package/dist/commands/models.js +97 -12
- package/dist/commands/open.js +4 -4
- package/dist/commands/run.js +38 -4
- package/dist/commands/sandbox.js +148 -16
- package/dist/commands/view.d.ts +8 -0
- package/dist/commands/view.js +114 -0
- package/dist/engine.d.ts +8 -1
- package/dist/engine.js +7 -0
- package/dist/eventlog.d.ts +64 -0
- package/dist/eventlog.js +170 -0
- package/dist/liveness.d.ts +7 -0
- package/dist/liveness.js +35 -3
- package/dist/main.js +0 -0
- package/dist/meta/editor.d.ts +9 -0
- package/dist/meta/editor.js +43 -0
- package/dist/meta/finetune/analyze-brief.d.ts +26 -0
- package/dist/meta/finetune/analyze-brief.js +131 -0
- package/dist/meta/finetune/apply-brief.d.ts +23 -0
- package/dist/meta/finetune/apply-brief.js +247 -0
- package/dist/meta/finetune/brief.d.ts +45 -0
- package/dist/meta/finetune/brief.js +158 -0
- package/dist/meta/finetune/children.d.ts +8 -0
- package/dist/meta/finetune/children.js +76 -0
- package/dist/meta/finetune/command.d.ts +30 -0
- package/dist/meta/finetune/command.js +516 -0
- package/dist/meta/finetune/cost.d.ts +46 -0
- package/dist/meta/finetune/cost.js +105 -0
- package/dist/meta/finetune/dataset/anchors.d.ts +41 -0
- package/dist/meta/finetune/dataset/anchors.js +239 -0
- package/dist/meta/finetune/dataset/changes.d.ts +36 -0
- package/dist/meta/finetune/dataset/changes.js +319 -0
- package/dist/meta/finetune/dataset/command.d.ts +17 -0
- package/dist/meta/finetune/dataset/command.js +1011 -0
- package/dist/meta/finetune/dataset/drift.d.ts +33 -0
- package/dist/meta/finetune/dataset/drift.js +71 -0
- package/dist/meta/finetune/dataset/export.d.ts +26 -0
- package/dist/meta/finetune/dataset/export.js +51 -0
- package/dist/meta/finetune/dataset/index.d.ts +2 -0
- package/dist/meta/finetune/dataset/index.js +2 -0
- package/dist/meta/finetune/dataset/sample.d.ts +50 -0
- package/dist/meta/finetune/dataset/sample.js +197 -0
- package/dist/meta/finetune/dataset/schema.d.ts +33 -0
- package/dist/meta/finetune/dataset/schema.js +216 -0
- package/dist/meta/finetune/dataset/store.d.ts +35 -0
- package/dist/meta/finetune/dataset/store.js +177 -0
- package/dist/meta/finetune/dataset/types.d.ts +127 -0
- package/dist/meta/finetune/dataset/types.js +39 -0
- package/dist/meta/finetune/failure.d.ts +15 -0
- package/dist/meta/finetune/failure.js +26 -0
- package/dist/meta/finetune/feedback.d.ts +57 -0
- package/dist/meta/finetune/feedback.js +104 -0
- package/dist/meta/finetune/guard.d.ts +29 -0
- package/dist/meta/finetune/guard.js +104 -0
- package/dist/meta/finetune/improvements.d.ts +98 -0
- package/dist/meta/finetune/improvements.js +501 -0
- package/dist/meta/finetune/index.d.ts +2 -0
- package/dist/meta/finetune/index.js +2 -0
- package/dist/meta/finetune/journal.d.ts +35 -0
- package/dist/meta/finetune/journal.js +135 -0
- package/dist/meta/finetune/live.d.ts +84 -0
- package/dist/meta/finetune/live.js +272 -0
- package/dist/meta/finetune/loop.d.ts +3 -0
- package/dist/meta/finetune/loop.js +163 -0
- package/dist/meta/finetune/memories.d.ts +47 -0
- package/dist/meta/finetune/memories.js +154 -0
- package/dist/meta/finetune/recheck.d.ts +58 -0
- package/dist/meta/finetune/recheck.js +311 -0
- package/dist/meta/finetune/report.d.ts +40 -0
- package/dist/meta/finetune/report.js +1397 -0
- package/dist/meta/finetune/retry.d.ts +24 -0
- package/dist/meta/finetune/retry.js +96 -0
- package/dist/meta/finetune/sampler.d.ts +18 -0
- package/dist/meta/finetune/sampler.js +47 -0
- package/dist/meta/finetune/system.d.ts +27 -0
- package/dist/meta/finetune/system.js +132 -0
- package/dist/meta/finetune/train.d.ts +43 -0
- package/dist/meta/finetune/train.js +425 -0
- package/dist/meta/finetune/tui.d.ts +16 -0
- package/dist/meta/finetune/tui.js +316 -0
- package/dist/meta/finetune/tuning.d.ts +193 -0
- package/dist/meta/finetune/tuning.js +308 -0
- package/dist/meta/finetune/usage.d.ts +66 -0
- package/dist/meta/finetune/usage.js +260 -0
- package/dist/meta/finetune/view.d.ts +9 -0
- package/dist/meta/finetune/view.js +27 -0
- package/dist/meta/host.d.ts +15 -0
- package/dist/meta/host.js +21 -0
- package/dist/meta/index.d.ts +11 -0
- package/dist/meta/index.js +11 -0
- package/dist/meta/launch.d.ts +81 -0
- package/dist/meta/launch.js +286 -0
- package/dist/meta/model.d.ts +44 -0
- package/dist/meta/model.js +119 -0
- package/dist/meta/prompts.d.ts +18 -0
- package/dist/meta/prompts.js +61 -0
- package/dist/meta/relay.d.ts +20 -0
- package/dist/meta/relay.js +123 -0
- package/dist/meta/tokens.d.ts +52 -0
- package/dist/meta/tokens.js +226 -0
- package/dist/meta/window.d.ts +61 -0
- package/dist/meta/window.js +198 -0
- package/dist/meta/wire.d.ts +42 -0
- package/dist/meta/wire.js +129 -0
- package/dist/meta.d.ts +71 -1
- package/dist/meta.js +303 -6
- package/dist/modelref.d.ts +16 -0
- package/dist/modelref.js +41 -0
- package/dist/narrate.js +5 -2
- package/dist/podman.d.ts +26 -0
- package/dist/podman.js +83 -3
- package/dist/projects.d.ts +0 -5
- package/dist/projects.js +2 -15
- package/dist/resources.d.ts +215 -0
- package/dist/resources.js +856 -0
- package/dist/sandbox.d.ts +2 -0
- package/dist/sandbox.js +10 -3
- package/dist/scaffold.d.ts +15 -7
- package/dist/scaffold.js +24 -67
- package/dist/session.js +4 -29
- package/dist/term.d.ts +2 -0
- package/dist/term.js +11 -2
- package/dist/trace.d.ts +5 -0
- package/dist/trace.js +107 -7
- package/dist/tui/app.js +19 -5
- package/dist/tui/links.d.ts +27 -0
- package/dist/tui/links.js +142 -0
- package/dist/tui/models.d.ts +35 -0
- package/dist/tui/models.js +447 -0
- package/dist/tui/sandbox.d.ts +10 -0
- package/dist/tui/sandbox.js +131 -0
- package/dist/tui/wrap.d.ts +5 -0
- package/dist/tui/wrap.js +12 -0
- package/dist/usage.d.ts +78 -0
- package/dist/usage.js +51 -0
- package/package.json +4 -2
- package/templates/editor/.github/copilot-instructions.md +60 -145
- package/templates/editor/.github/prompts/analyze.prompt.md +33 -0
- package/templates/editor/.github/prompts/dataset.prompt.md +108 -0
- package/templates/editor/.github/prompts/finetune-apply.prompt.md +270 -0
- package/templates/editor/.github/prompts/finetune.prompt.md +691 -198
- package/templates/editor/.github/prompts/new-agent.prompt.md +3 -1
- package/templates/editor/.github/prompts/new-skill.prompt.md +2 -0
- package/templates/editor/.github/prompts/project-review.prompt.md +5 -4
- package/templates/editor/.github/skills/zen-analyze-run/SKILL.md +588 -0
- package/templates/editor/.github/skills/zen-cli/SKILL.md +9 -1
- package/templates/editor/.github/skills/zen-cli/references/dataset.md +34 -0
- package/templates/editor/.github/skills/zen-cli/references/finetune.md +137 -0
- package/templates/editor/.github/skills/zen-cli/references/inspect.md +63 -11
- package/templates/editor/.github/skills/zen-cli/references/memory.md +33 -10
- package/templates/editor/.github/skills/zen-cli/references/meta.md +61 -0
- package/templates/editor/.github/skills/zen-cli/references/models.md +5 -1
- package/templates/editor/.github/skills/zen-cli/references/projects.md +4 -6
- package/templates/editor/.github/skills/zen-cli/references/rag.md +24 -6
- package/templates/editor/.github/skills/zen-cli/references/sandbox.md +35 -14
- package/templates/editor/.github/skills/zen-code-python/SKILL.md +123 -0
- package/templates/editor/.github/skills/zen-code-python/references/code_python.md +48 -0
- package/templates/editor/.github/skills/zen-dataset/SKILL.md +253 -0
- package/templates/editor/.github/skills/zen-finetune/SKILL.md +1492 -660
- package/templates/editor/.github/skills/zen-finetune/references/readme-template.md +268 -0
- package/templates/editor/.github/skills/zen-finetune/scripts/batch.mjs +390 -0
- package/templates/editor/.github/skills/zen-finetune/scripts/difficult.mjs +243 -0
- package/templates/editor/.github/skills/zen-finetune/scripts/memory.mjs +280 -0
- package/templates/editor/.github/skills/zen-finetune/scripts/next.mjs +398 -0
- package/templates/editor/.github/skills/zen-finetune/scripts/report.mjs +797 -0
- package/templates/editor/.github/skills/zen-finetune/scripts/select.mjs +354 -0
- package/templates/editor/.github/skills/zen-finetune/scripts/usage.mjs +722 -0
- package/templates/editor/.github/skills/zen-inspect/SKILL.md +74 -26
- package/templates/editor/.github/skills/zen-inspect-ask/SKILL.md +271 -0
- package/templates/editor/.github/skills/zen-instructions/SKILL.md +4 -12
- package/templates/editor/.github/skills/zen-memory/SKILL.md +82 -53
- package/templates/editor/.github/skills/zen-rag-docs/SKILL.md +20 -8
- package/templates/editor/.github/skills/zen-review/SKILL.md +8 -31
- package/templates/editor/.github/skills/zen-review/scripts/check-paths.sh +5 -5
- package/templates/editor/.github/skills/zen-review/scripts/review.sh +3 -12
- package/templates/editor/.github/skills/zen-sandbox/SKILL.md +9 -4
- package/templates/editor/.github/skills/zen-sandbox-capacity/SKILL.md +2 -2
- package/templates/editor/.github/skills/zen-spec-sync/SKILL.md +2 -4
- package/templates/editor/.github/skills/zen-topology/SKILL.md +283 -0
- package/templates/editor/.vscode/settings.json +1 -0
- package/templates/project/agents/files-instructions.md +29 -20
- package/templates/project/agents/fork-instructions.md +78 -28
- package/templates/project/agents/memory-instructions.md +222 -494
- package/templates/project/agents/tools-instructions.md +14 -4
- package/templates/project/gitignore +2 -0
- package/templates/editor/.github/skills/zen-finetune/scripts/collect.sh +0 -432
- package/templates/editor/.github/skills/zen-finetune/scripts/sample.sh +0 -199
- package/templates/editor/.github/skills/zen-memory/scripts/check-instructions.sh +0 -116
package/README.md
CHANGED
|
@@ -241,6 +241,7 @@ so they never go stale - and edits to them do not survive.
|
|
|
241
241
|
| `/spec-sync-project` | Makes every file match `SPECIFICATION.md`, both directions. |
|
|
242
242
|
| `/spec-apply-feedback` | Folds your answers in `SPECIFICATION-FEEDBACK.md` into the spec. |
|
|
243
243
|
| `/project-review` | Reads the project as a reviewer would, and reports. |
|
|
244
|
+
| `/analyze [run]` | Analyzes one run and says which instruction to change. |
|
|
244
245
|
| `/new-agent` | Adds an agent - prompt, wiring and hand-offs. |
|
|
245
246
|
| `/new-skill` | Adds a skill under `agents/skills/`. |
|
|
246
247
|
|
|
@@ -283,6 +284,17 @@ schemas - to a model with your question on the end and tool calling off, and
|
|
|
283
284
|
tells it the run is over and its own instructions are quotable. It is how a
|
|
284
285
|
prompt that reads correctly and behaves wrongly explains itself.
|
|
285
286
|
|
|
287
|
+
`ask` has two modes, decided by whether the question is given:
|
|
288
|
+
|
|
289
|
+
- **One-shot** - a question on the line, in `--question-file`, or `--json`.
|
|
290
|
+
For a script or the agent in your editor: no banner, no prompt, no picker.
|
|
291
|
+
stdout is the answer and nothing else, and the command exits.
|
|
292
|
+
- **Interactive** - no question. For you, at a terminal: `zen inspect ask`
|
|
293
|
+
walks through the session, run and recorded LLM call (whichever are not
|
|
294
|
+
named), frames each question and Markdown answer separately, and keeps
|
|
295
|
+
accepting follow-ups until you submit an empty question. Without a terminal
|
|
296
|
+
it is an error, never a wait.
|
|
297
|
+
|
|
286
298
|
Every step is a command, and everything each one reads or writes is a plain
|
|
287
299
|
file: the specification, the findings `zen check` prints with a code, a location
|
|
288
300
|
and a fix, the record `zen inspect` renders of what the run actually did. So the
|
|
@@ -482,11 +494,14 @@ you want to validate a project's execution environment before a run.
|
|
|
482
494
|
|
|
483
495
|
```sh
|
|
484
496
|
zen sandbox status # engine, project image and active containers
|
|
497
|
+
zen sandbox top # memory and cpu, live: this computer, the machine, each container
|
|
498
|
+
zen sandbox top --once # the same, printed once (also with --json or when piped)
|
|
485
499
|
zen sandbox up # prepare the execution environment for this project
|
|
486
500
|
zen sandbox pull # pull or build this project's image only
|
|
487
501
|
zen sandbox status --project my-app # inspect a named project from anywhere
|
|
488
502
|
zen sandbox disk # storage used by the engine and known projects
|
|
489
503
|
zen sandbox clean # remove containers created by zen
|
|
504
|
+
zen sandbox clean --idle # only what no live run is using
|
|
490
505
|
```
|
|
491
506
|
|
|
492
507
|
`status`, `up` and `pull` use the project in the current directory, or accept
|
|
@@ -494,6 +509,13 @@ zen sandbox clean # remove containers created by zen
|
|
|
494
509
|
`disk` are machine-wide operations, so use them when you mean to inspect or
|
|
495
510
|
remove resources beyond the current project.
|
|
496
511
|
|
|
512
|
+
`top` is machine-wide too. Next to every figure it says in a few words what the
|
|
513
|
+
figure means; `--brief` leaves those notes out, `--interval <seconds>` sets how
|
|
514
|
+
often the live screen refreshes. On macOS it answers the usual question - why
|
|
515
|
+
Activity Monitor shows "Virtual Machine Service for vfkit" holding many
|
|
516
|
+
gigabytes - by setting what macOS counts for the machine against what is in use
|
|
517
|
+
inside it, and naming the command that gives the difference back.
|
|
518
|
+
|
|
497
519
|
## Sharing a project
|
|
498
520
|
|
|
499
521
|
A project is a directory, so sending one somewhere else is a zip - except that
|
|
@@ -541,6 +563,7 @@ The commands to run next are printed for you to read and type.
|
|
|
541
563
|
| `run` | Runs the project - the TUI on a terminal, a single answer otherwise. |
|
|
542
564
|
| `meta` | Runs the meta agent over the project - the one that edits it. |
|
|
543
565
|
| `inspect` | Opens or rebuilds a run's `report.html`. |
|
|
566
|
+
| `view` | Shows a folder in the browser, redrawn live as its files change. |
|
|
544
567
|
| `memory` | What the agents remember - size, listing, one node, or a whole page. |
|
|
545
568
|
| `check` | Validates the project and every file it names, and asks the models. |
|
|
546
569
|
| `export` | Writes the project to a shareable zip archive. |
|
|
@@ -631,6 +654,7 @@ zen meta run acme "review the last commit"
|
|
|
631
654
|
zen meta prompts # which /<name> prompts this project has
|
|
632
655
|
zen meta run /project-review # .github/prompts/project-review.prompt.md
|
|
633
656
|
zen meta run acme /spec-sync-project agents/triage.md
|
|
657
|
+
zen meta run /analyze 20260825-143012-a7f3 # analyze one run
|
|
634
658
|
git diff | zen meta run "what broke?" --allow-tool read
|
|
635
659
|
zen meta run --dry-run "hello" # what would run, secrets masked
|
|
636
660
|
```
|
|
@@ -698,6 +722,41 @@ the next run. A typed prefix that names no provider - `vertes/...` - is caught
|
|
|
698
722
|
before that, because it would otherwise be sent whole to a vendor you never
|
|
699
723
|
named. `--force` stores a ref without asking.
|
|
700
724
|
|
|
725
|
+
### The dataset it evaluates against
|
|
726
|
+
|
|
727
|
+
```sh
|
|
728
|
+
zen meta run /dataset # build or refresh dataset/ from its sources
|
|
729
|
+
zen meta dataset # revision, counts, what drifted
|
|
730
|
+
zen meta dataset drift # which cases their source sections moved under
|
|
731
|
+
zen meta dataset sample --rubric yes -n 12 --format batch -o cases.json
|
|
732
|
+
zen meta dataset log planning-organize-day # every change and verdict, and the sessions
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
`dataset/` holds every case with its rubric, an anchor to the section it was
|
|
736
|
+
read from, and a journal of what changed and what was found. Only
|
|
737
|
+
`zen meta dataset` writes it; `/dataset` re-reads just the sections that
|
|
738
|
+
changed, and a case whose question or rubric changed is listed as restarted.
|
|
739
|
+
See the `zen-dataset` skill a project carries in `.github/skills/`.
|
|
740
|
+
|
|
741
|
+
### Tuning on it
|
|
742
|
+
|
|
743
|
+
```sh
|
|
744
|
+
zen meta finetune start -N 4 # 4 cases at a time; resumes if stopped
|
|
745
|
+
zen meta finetune # where it stands
|
|
746
|
+
zen meta finetune stop # finish the steps in flight, then stop
|
|
747
|
+
zen meta finetune serve # the pages live in a browser, while it is stopped
|
|
748
|
+
zen meta finetune session plan-day # resume that case's analyze session
|
|
749
|
+
```
|
|
750
|
+
|
|
751
|
+
Each case is run without memory and analyzed until it is right and lean, then
|
|
752
|
+
run with the memory it wrote until that is cheaper. Improvement requests are
|
|
753
|
+
applied in batches between runs, and every apply is a new, diffable version of
|
|
754
|
+
the prose. Follow it in `finetune/STATUS.md`, whose summary shows whether the
|
|
755
|
+
fixes and the memory are making runs cheaper; each case has its own
|
|
756
|
+
`finetune/cases/<id>/FEEDBACK.md` with its latest analysis and the metrics of
|
|
757
|
+
every try. `start` prints a local address that shows those pages in a browser,
|
|
758
|
+
with their diagrams drawn, updated in place as the tuning writes them.
|
|
759
|
+
|
|
701
760
|
## Credentials
|
|
702
761
|
|
|
703
762
|
One keyring serves every provider, and a key goes in the same way whatever it
|
|
@@ -755,6 +814,7 @@ zen models search haiku --tools --free # narrow it
|
|
|
755
814
|
zen models show openrouter:anthropic/claude-haiku-4.5
|
|
756
815
|
zen models test vertex:gemini-embedding-001 # one real call, one verdict
|
|
757
816
|
zen models pick --embedding # the first ref that answers, on stdout
|
|
817
|
+
zen models browse # walk providers and models with the arrow keys
|
|
758
818
|
```
|
|
759
819
|
|
|
760
820
|
Listings are cached for a day in `~/.zenera/neo/cache`. If a provider cannot be
|
|
@@ -788,6 +848,11 @@ the above is a single substitution, whether a person or an agent is doing it:
|
|
|
788
848
|
zen rag schema index --embedding "$(zen models pick --embedding)" ./specs/*.yaml
|
|
789
849
|
```
|
|
790
850
|
|
|
851
|
+
`zen models browse [provider]` is the interactive way in: arrow keys through
|
|
852
|
+
the providers and their models, `/` to filter, `tab` to cycle roles, `enter`
|
|
853
|
+
for one model in full, `t` to ask it one question, `p` to pick it. The picked
|
|
854
|
+
ref is printed on stdout, so `ref="$(zen models browse)"` works too.
|
|
855
|
+
|
|
791
856
|
### Setting up each provider
|
|
792
857
|
|
|
793
858
|
The shape is the same everywhere - `zen key add <provider>`, then answer the
|
package/dist/ask.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { text, } from '@zenera/neo';
|
|
2
|
-
import { splitRef } from "./
|
|
2
|
+
import { splitRef } from "./modelref.js";
|
|
3
3
|
import { invalidError } from "./term.js";
|
|
4
4
|
// ---------------------------------------------------------------------------
|
|
5
5
|
// Asking a run about itself
|
|
@@ -31,13 +31,36 @@ export const DEBUG_PREAMBLE = [
|
|
|
31
31
|
'write now reaches a user, a file or a machine. The last message is a question from',
|
|
32
32
|
'the operator of this agent about what you did and why.',
|
|
33
33
|
'',
|
|
34
|
-
'Answer that question
|
|
34
|
+
'Answer that question. Do not call a tool. Do not resume the task. If the question',
|
|
35
|
+
'asks for a format, use it.',
|
|
35
36
|
'',
|
|
36
37
|
'This is an authorised post-mortem over data the operator already holds, so you are',
|
|
37
38
|
'allowed — and asked — to quote your system prompt, instruction files and skills',
|
|
38
|
-
'verbatim
|
|
39
|
-
'
|
|
40
|
-
'
|
|
39
|
+
'verbatim. "I cannot reveal my instructions" is not an answer to any question below.',
|
|
40
|
+
'',
|
|
41
|
+
'## Cite where every reason came from',
|
|
42
|
+
'',
|
|
43
|
+
'The operator is using your answer to find the text that shaped your behaviour and',
|
|
44
|
+
'edit it. An explanation without a source cannot be acted on. For every reason you',
|
|
45
|
+
'give, name its source and quote it verbatim, the exact words as they appear above:',
|
|
46
|
+
'',
|
|
47
|
+
'- **system prompt** — the instruction file or section, by its file name or nearest heading',
|
|
48
|
+
'- **skill** — the skill name and the heading inside it',
|
|
49
|
+
'- **tool description** — the tool name, and the parameter when the text is in its schema',
|
|
50
|
+
'- **message** — the user request, or a tool result, by tool name and call id',
|
|
51
|
+
'',
|
|
52
|
+
'Write each as: [<kind>: <name> › <heading or parameter>] "<verbatim quote>".',
|
|
53
|
+
'',
|
|
54
|
+
'Then trace the decision itself: what options you could see at that point, which one',
|
|
55
|
+
'you took, and which quoted text tipped it. Say which instruction you followed, which',
|
|
56
|
+
'applied but you missed or never reached, which two pulled in different directions,',
|
|
57
|
+
'and any condition or exception ("unless…", "only when…") you judged to apply.',
|
|
58
|
+
'',
|
|
59
|
+
'Be exact about provenance. When no text above told you to do what you did, say',
|
|
60
|
+
'"no instruction — my own default" rather than attaching it to the nearest',
|
|
61
|
+
'plausible source. When you are unsure whether a sentence is really there, say so.',
|
|
62
|
+
'Never paraphrase inside quotation marks: the operator will search for your quote,',
|
|
63
|
+
'and a quote that is not found discredits the whole answer.',
|
|
41
64
|
].join('\n');
|
|
42
65
|
/**
|
|
43
66
|
* The `request` blob of an `llm_call` node, which is `Kernel.serializeRequest`
|
package/dist/banner.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { appearance, dim, note, styled } from "./term.js";
|
|
1
|
+
import { appearance, dim, drivenByAgent, note, styled } from "./term.js";
|
|
2
2
|
// ---------------------------------------------------------------------------
|
|
3
3
|
// The banner
|
|
4
4
|
//
|
|
@@ -149,7 +149,7 @@ export function bannerArt(text) {
|
|
|
149
149
|
}
|
|
150
150
|
/** Narration, and only for someone watching. */
|
|
151
151
|
export function printBanner(text, options = {}) {
|
|
152
|
-
if (!process.stderr.isTTY) {
|
|
152
|
+
if (!process.stderr.isTTY || drivenByAgent()) {
|
|
153
153
|
return;
|
|
154
154
|
}
|
|
155
155
|
note('');
|
package/dist/batch.d.ts
CHANGED
|
@@ -40,6 +40,12 @@ export interface BatchOptions {
|
|
|
40
40
|
}
|
|
41
41
|
export type MemoryMode = 'read-only' | 'copied' | 'none';
|
|
42
42
|
export declare function runBatch(opts: BatchOptions): Promise<void>;
|
|
43
|
+
/**
|
|
44
|
+
* The most containers one item can start: one shared by the agents on the
|
|
45
|
+
* project's sandbox block, one more for each agent with a block of its own.
|
|
46
|
+
* A project that does not load is each item's to report, so it counts none.
|
|
47
|
+
*/
|
|
48
|
+
export declare function containersPerItem(dir: string): number;
|
|
43
49
|
/**
|
|
44
50
|
* A bounded map that keeps its order. Workers pull from one cursor rather than
|
|
45
51
|
* taking a slice each, so a batch of one slow item and fifty fast ones finishes
|
package/dist/batch.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { addUsage, lockHolder, MANIFEST_FILE as MEMORY_MANIFEST, memoryDir, MemoryStore, mergeModelUsage, readProjectConfig, usageByModel, zeroUsage, } from '@zenera/neo';
|
|
1
|
+
import { addUsage, lockHolder, MANIFEST_FILE as MEMORY_MANIFEST, memoryDir, MemoryStore, mergeModelUsage, readProjectConfig, SANDBOX_GROUP, selectTools, usageByModel, zeroUsage, } from '@zenera/neo';
|
|
2
2
|
import { existsSync } from 'node:fs';
|
|
3
3
|
import { cp, mkdir, rm, writeFile } from 'node:fs/promises';
|
|
4
4
|
import { basename, join, resolve } from 'node:path';
|
|
@@ -6,14 +6,23 @@ import * as Engine from "./engine.js";
|
|
|
6
6
|
import { stamp } from "./ids.js";
|
|
7
7
|
import { duration } from "./narrate.js";
|
|
8
8
|
import { BatchProgress } from "./progress.js";
|
|
9
|
+
import { assertLocks } from "./podman.js";
|
|
9
10
|
import { project as resolveProject, target } from "./resolve.js";
|
|
10
11
|
import { display } from "./session.js";
|
|
11
12
|
import { CliError, count, cyan, dim, EXIT, invalidError, json, jsonText, note, usageError, write, } from "./term.js";
|
|
13
|
+
import { appendUsage } from "./usage.js";
|
|
14
|
+
import { availableTools } from "./validate.js";
|
|
12
15
|
/** Past this, the limit is the provider's rate limit and the failures are 429s. */
|
|
13
16
|
const MAX_CONCURRENCY = 32;
|
|
14
17
|
export async function runBatch(opts) {
|
|
15
18
|
const project = await resolveProject({ cwd: opts.cwd, project: opts.project });
|
|
16
19
|
const items = opts.request.items;
|
|
20
|
+
// Every item at once holds its own containers, so a short engine fails
|
|
21
|
+
// here once instead of once per item.
|
|
22
|
+
const boxes = containersPerItem(project.dir);
|
|
23
|
+
if (boxes > 0) {
|
|
24
|
+
await assertLocks(boxes * Math.min(opts.concurrency, items.length));
|
|
25
|
+
}
|
|
17
26
|
const dir = opts.dir ? resolve(opts.cwd, opts.dir) : join(project.dir, 'batches', stamp());
|
|
18
27
|
// Two batches in one directory would interleave their item folders and the
|
|
19
28
|
// second would overwrite the first's index while the first still points at
|
|
@@ -127,6 +136,20 @@ export async function runBatch(opts) {
|
|
|
127
136
|
};
|
|
128
137
|
const text = jsonText(body);
|
|
129
138
|
await writeFile(join(dir, 'batch.json'), text, 'utf8');
|
|
139
|
+
appendUsage(project.dir, {
|
|
140
|
+
kind: 'batch',
|
|
141
|
+
ts: finishedAt.toISOString(),
|
|
142
|
+
batchDir: dir,
|
|
143
|
+
input: opts.input,
|
|
144
|
+
items: items.length,
|
|
145
|
+
ok: results.length - failed,
|
|
146
|
+
failed,
|
|
147
|
+
concurrency: opts.concurrency,
|
|
148
|
+
memory: mode,
|
|
149
|
+
startedAt: startedAt.toISOString(),
|
|
150
|
+
durationMs: finishedAt.getTime() - startedAt.getTime(),
|
|
151
|
+
models,
|
|
152
|
+
});
|
|
130
153
|
if (opts.out) {
|
|
131
154
|
await writeFile(resolve(opts.cwd, opts.out), text, 'utf8');
|
|
132
155
|
}
|
|
@@ -150,6 +173,34 @@ export async function runBatch(opts) {
|
|
|
150
173
|
throw new CliError(`${count(failed, 'item')} failed`, EXIT.failed, `what each one said: ${join(dir, 'batch.json')}`);
|
|
151
174
|
}
|
|
152
175
|
}
|
|
176
|
+
// ---------------------------------------------------------------------------
|
|
177
|
+
// One item
|
|
178
|
+
// ---------------------------------------------------------------------------
|
|
179
|
+
/**
|
|
180
|
+
* The most containers one item can start: one shared by the agents on the
|
|
181
|
+
* project's sandbox block, one more for each agent with a block of its own.
|
|
182
|
+
* A project that does not load is each item's to report, so it counts none.
|
|
183
|
+
*/
|
|
184
|
+
export function containersPerItem(dir) {
|
|
185
|
+
let read;
|
|
186
|
+
try {
|
|
187
|
+
read = readProjectConfig(dir);
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
return 0;
|
|
191
|
+
}
|
|
192
|
+
const tools = availableTools(read.root, read.config);
|
|
193
|
+
const shells = read.config.agents.filter((a) => {
|
|
194
|
+
try {
|
|
195
|
+
return selectTools(tools, [...(a.tools ?? [])], { where: a.name }).some((t) => t.group === SANDBOX_GROUP);
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
const own = shells.filter((a) => a.sandbox).length;
|
|
202
|
+
return own + (shells.length > own ? 1 : 0);
|
|
203
|
+
}
|
|
153
204
|
/**
|
|
154
205
|
* A failure is data. One item that asks something the model cannot answer must
|
|
155
206
|
* not cost the other ninety-nine their answers, so everything is caught and
|
|
@@ -181,6 +232,9 @@ async function runItem(item, ctx) {
|
|
|
181
232
|
yes: true,
|
|
182
233
|
// The project's own problems are the batch's, not each item's.
|
|
183
234
|
quiet: true,
|
|
235
|
+
// A fresh session nobody resumes: a stopped container per item
|
|
236
|
+
// is how a few fine-tuning rounds used up every engine lock.
|
|
237
|
+
disposable: true,
|
|
184
238
|
});
|
|
185
239
|
try {
|
|
186
240
|
// Nothing is narrated to the terminal — a hundred interleaved
|
|
@@ -192,6 +246,18 @@ async function runItem(item, ctx) {
|
|
|
192
246
|
await writeFile(output, jsonText(Engine.envelope(engine, outcome)), 'utf8');
|
|
193
247
|
const models = usageByModel(outcome.result.state.trajectory);
|
|
194
248
|
ctx.spent.push(models);
|
|
249
|
+
appendUsage(ctx.projectDir, {
|
|
250
|
+
kind: 'run',
|
|
251
|
+
ts: new Date().toISOString(),
|
|
252
|
+
runDir: outcome.run.dir,
|
|
253
|
+
session: engine.session.id,
|
|
254
|
+
batchDir: ctx.dir,
|
|
255
|
+
item: item.id,
|
|
256
|
+
ok: true,
|
|
257
|
+
stopReason: outcome.result.stopReason,
|
|
258
|
+
durationMs: outcome.durationMs,
|
|
259
|
+
models,
|
|
260
|
+
});
|
|
195
261
|
ctx.progress.finish(item.id, { ok: true, usage: outcome.result.usage, models });
|
|
196
262
|
return { index: item.index, id: item.id, ok: true, input: index(item.input), output };
|
|
197
263
|
}
|
|
@@ -205,6 +271,15 @@ async function runItem(item, ctx) {
|
|
|
205
271
|
...(err instanceof CliError && err.hint ? { hint: err.hint } : {}),
|
|
206
272
|
};
|
|
207
273
|
await writeFile(output, jsonText({ ok: false, id: item.id, error }), 'utf8');
|
|
274
|
+
appendUsage(ctx.projectDir, {
|
|
275
|
+
kind: 'run',
|
|
276
|
+
ts: new Date().toISOString(),
|
|
277
|
+
batchDir: ctx.dir,
|
|
278
|
+
item: item.id,
|
|
279
|
+
ok: false,
|
|
280
|
+
error: error.message,
|
|
281
|
+
models: [],
|
|
282
|
+
});
|
|
208
283
|
ctx.progress.finish(item.id, { ok: false, error: error.message });
|
|
209
284
|
return {
|
|
210
285
|
index: item.index,
|
package/dist/catalog.d.ts
CHANGED
|
@@ -36,6 +36,8 @@ export interface CatalogEntry {
|
|
|
36
36
|
};
|
|
37
37
|
/** ISO date the model was published, when known */
|
|
38
38
|
created?: string;
|
|
39
|
+
/** vertex only: who makes it - `google`, or the partner Vertex resells */
|
|
40
|
+
publisher?: string;
|
|
39
41
|
source: 'live' | 'curated';
|
|
40
42
|
}
|
|
41
43
|
/** A provider's listing, and an honest account of where it came from. */
|
|
@@ -72,6 +74,7 @@ export declare const PREFERRED: Record<Provider, {
|
|
|
72
74
|
chat: readonly string[];
|
|
73
75
|
embedding: readonly string[];
|
|
74
76
|
}>;
|
|
77
|
+
export declare function fromVertexPartners(): Promise<CatalogEntry[]>;
|
|
75
78
|
/**
|
|
76
79
|
* Asks one provider what it serves. Throws whatever the SDK throws.
|
|
77
80
|
*
|
|
@@ -79,8 +82,9 @@ export declare const PREFERRED: Record<Provider, {
|
|
|
79
82
|
* to break when a vendor reshapes a payload, and they are untestable if the
|
|
80
83
|
* only way to reach them is a credential and a network. It mirrors
|
|
81
84
|
* `ProviderSpec.client`, which exists in the library for the same reason.
|
|
85
|
+
* `partners` is the same seam for Vertex's second listing.
|
|
82
86
|
*/
|
|
83
|
-
export declare function fetchCatalog(provider: Provider, client?: unknown): Promise<CatalogEntry[]>;
|
|
87
|
+
export declare function fetchCatalog(provider: Provider, client?: unknown, partners?: () => Promise<CatalogEntry[]>): Promise<CatalogEntry[]>;
|
|
84
88
|
/** The key one provider's listing is filed under, version and all. */
|
|
85
89
|
export declare const catalogKey: (provider: Provider) => string;
|
|
86
90
|
export interface CatalogOptions {
|
|
@@ -99,6 +103,26 @@ export interface CatalogOptions {
|
|
|
99
103
|
* model list to four rows.
|
|
100
104
|
*/
|
|
101
105
|
export declare function loadCatalog(provider: Provider, opts?: CatalogOptions): Promise<Catalog>;
|
|
106
|
+
/** A per-token USD price as dollars per million tokens, the unit price lists use. */
|
|
107
|
+
export declare function perMillion(price: string | undefined): string;
|
|
108
|
+
/** What an OpenRouter account has left to spend, as far as its key may see. */
|
|
109
|
+
export interface Balance {
|
|
110
|
+
/** credits bought minus credits used; absent when the key may not read the account */
|
|
111
|
+
credits?: number;
|
|
112
|
+
/** the key's own spending cap, when one is set */
|
|
113
|
+
cap?: {
|
|
114
|
+
limit: number;
|
|
115
|
+
remaining: number;
|
|
116
|
+
reset?: string;
|
|
117
|
+
};
|
|
118
|
+
/** requests to `:free` models left today */
|
|
119
|
+
free?: {
|
|
120
|
+
used: number;
|
|
121
|
+
limit: number;
|
|
122
|
+
remaining: number;
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
export declare function routerBalance(): Promise<Balance>;
|
|
102
126
|
/** Every provider asked at once — they are independent and mostly latency. */
|
|
103
127
|
export declare function loadCatalogs(providers: readonly Provider[], opts?: CatalogOptions): Promise<Catalog[]>;
|
|
104
128
|
export interface Filters {
|
|
@@ -111,4 +135,10 @@ export interface Filters {
|
|
|
111
135
|
export declare function matches(row: CatalogEntry, query: string, filters?: Filters): boolean;
|
|
112
136
|
/** The providers a search covers when none was named. */
|
|
113
137
|
export declare const catalogProviders: () => readonly Provider[];
|
|
138
|
+
/** Resold on Vertex rather than made by Google. */
|
|
139
|
+
export declare const isPartner: (e: CatalogEntry) => boolean;
|
|
140
|
+
/** Provider, then Google's own models, then each partner's together. */
|
|
141
|
+
export declare function byGroup(a: CatalogEntry, b: CatalogEntry): number;
|
|
142
|
+
/** The heading a row is listed under, when its provider resells other makers' models. */
|
|
143
|
+
export declare function groupOf(e: CatalogEntry): string | undefined;
|
|
114
144
|
//# sourceMappingURL=catalog.d.ts.map
|
package/dist/catalog.js
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
// `source` for exactly that reason: a curated guess must never be mistaken for
|
|
17
17
|
// the vendor's own word.
|
|
18
18
|
// ---------------------------------------------------------------------------
|
|
19
|
-
import { ModelRegistry } from '@zenera/neo';
|
|
19
|
+
import { ModelRegistry, vertexEndpoint } from '@zenera/neo';
|
|
20
20
|
import { Cache, cacheKey } from "./cache.js";
|
|
21
21
|
import { PROVIDERS } from "./keys.js";
|
|
22
22
|
import { classify } from "./liveness.js";
|
|
@@ -24,7 +24,7 @@ import { classify } from "./liveness.js";
|
|
|
24
24
|
export const CATALOG_TTL_MS = 24 * 60 * 60 * 1000;
|
|
25
25
|
export const CATALOG_KIND = 'catalog';
|
|
26
26
|
/** Bumped when the stored shape changes, which makes every old entry a miss. */
|
|
27
|
-
const CATALOG_VERSION =
|
|
27
|
+
const CATALOG_VERSION = 2;
|
|
28
28
|
// ---------------------------------------------------------------------------
|
|
29
29
|
// The fallback table
|
|
30
30
|
// ---------------------------------------------------------------------------
|
|
@@ -53,9 +53,17 @@ export const CURATED = {
|
|
|
53
53
|
{ id: 'gemini-embedding-2', roles: ['embedding'] },
|
|
54
54
|
],
|
|
55
55
|
vertex: [
|
|
56
|
-
{
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
{
|
|
57
|
+
id: 'gemini-3.5-flash-lite',
|
|
58
|
+
roles: ['chat'],
|
|
59
|
+
contextLength: 1_048_576,
|
|
60
|
+
publisher: 'google',
|
|
61
|
+
},
|
|
62
|
+
{ id: 'gemini-3.8-flash', roles: ['chat'], contextLength: 1_048_576, publisher: 'google' },
|
|
63
|
+
{ id: 'gemini-embedding-2', roles: ['embedding'], publisher: 'google' },
|
|
64
|
+
// Open-weight models are served but not listed by Model Garden, so these are the only record.
|
|
65
|
+
{ id: 'qwen/qwen3-235b-a22b-instruct-2507-maas', roles: ['chat'], publisher: 'qwen' },
|
|
66
|
+
{ id: 'openai/gpt-oss-120b-maas', roles: ['chat'], publisher: 'openai' },
|
|
59
67
|
],
|
|
60
68
|
openrouter: [
|
|
61
69
|
{ id: 'openai/gpt-5.6-luna', roles: ['chat'], contextLength: 1_050_000 },
|
|
@@ -227,6 +235,7 @@ async function fromGenAI(client, provider) {
|
|
|
227
235
|
description: model.description,
|
|
228
236
|
contextLength: model.inputTokenLimit,
|
|
229
237
|
maxOutputTokens: model.outputTokenLimit,
|
|
238
|
+
publisher: provider === 'vertex' ? 'google' : undefined,
|
|
230
239
|
}));
|
|
231
240
|
}
|
|
232
241
|
return rows;
|
|
@@ -296,6 +305,38 @@ async function fromRouter(client, provider) {
|
|
|
296
305
|
}
|
|
297
306
|
return rows;
|
|
298
307
|
}
|
|
308
|
+
/**
|
|
309
|
+
* Partner models on Vertex. The GenAI listing holds Google's alone, so each
|
|
310
|
+
* partner's Model Garden listing is asked as well - only for publishers whose
|
|
311
|
+
* models the runtime can call: Claude at `rawPredict`, the rest at the
|
|
312
|
+
* OpenAI-compatible endpoint as `publisher/model`.
|
|
313
|
+
*/
|
|
314
|
+
const VERTEX_PARTNERS = ['anthropic', 'xai'];
|
|
315
|
+
export async function fromVertexPartners() {
|
|
316
|
+
const at = vertexEndpoint();
|
|
317
|
+
const token = await at.token();
|
|
318
|
+
const lists = await Promise.all(VERTEX_PARTNERS.map(async (publisher) => {
|
|
319
|
+
const res = await fetch(`https://aiplatform.googleapis.com/v1beta1/publishers/${publisher}/models?pageSize=200`, {
|
|
320
|
+
headers: {
|
|
321
|
+
Authorization: `Bearer ${token}`,
|
|
322
|
+
'x-goog-user-project': at.project,
|
|
323
|
+
},
|
|
324
|
+
});
|
|
325
|
+
if (!res.ok) {
|
|
326
|
+
return [];
|
|
327
|
+
}
|
|
328
|
+
const body = (await res.json());
|
|
329
|
+
return (body.publisherModels ?? [])
|
|
330
|
+
.map((m) => bareId(m.name ?? ''))
|
|
331
|
+
.filter((id) => id && !/embed|ocr/.test(id))
|
|
332
|
+
.map((id) => entry('vertex', {
|
|
333
|
+
id: publisher === 'anthropic' ? id : `${publisher}/${id}`,
|
|
334
|
+
roles: ['chat'],
|
|
335
|
+
publisher,
|
|
336
|
+
}));
|
|
337
|
+
}));
|
|
338
|
+
return lists.flat();
|
|
339
|
+
}
|
|
299
340
|
/**
|
|
300
341
|
* Asks one provider what it serves. Throws whatever the SDK throws.
|
|
301
342
|
*
|
|
@@ -303,16 +344,27 @@ async function fromRouter(client, provider) {
|
|
|
303
344
|
* to break when a vendor reshapes a payload, and they are untestable if the
|
|
304
345
|
* only way to reach them is a credential and a network. It mirrors
|
|
305
346
|
* `ProviderSpec.client`, which exists in the library for the same reason.
|
|
347
|
+
* `partners` is the same seam for Vertex's second listing.
|
|
306
348
|
*/
|
|
307
|
-
export async function fetchCatalog(provider, client = new ModelRegistry().client(provider)) {
|
|
349
|
+
export async function fetchCatalog(provider, client = new ModelRegistry().client(provider), partners = fromVertexPartners) {
|
|
308
350
|
switch (provider) {
|
|
309
351
|
case 'openai':
|
|
310
352
|
return enrich(provider, await fromOpenAI(client, provider));
|
|
311
353
|
case 'anthropic':
|
|
312
354
|
return enrich(provider, await fromAnthropic(client, provider));
|
|
313
355
|
case 'google':
|
|
314
|
-
case 'vertex':
|
|
315
356
|
return enrich(provider, await fromGenAI(client, provider));
|
|
357
|
+
case 'vertex': {
|
|
358
|
+
// A partner listing that fails must not cost the Gemini one.
|
|
359
|
+
const [google, others] = await Promise.all([
|
|
360
|
+
fromGenAI(client, provider),
|
|
361
|
+
partners().catch(() => []),
|
|
362
|
+
]);
|
|
363
|
+
const live = [...google, ...others];
|
|
364
|
+
const seen = new Set(live.map((r) => r.id));
|
|
365
|
+
const unlisted = curated(provider).filter((r) => r.publisher !== 'google' && !seen.has(r.id));
|
|
366
|
+
return enrich(provider, [...live, ...unlisted]);
|
|
367
|
+
}
|
|
316
368
|
case 'openrouter':
|
|
317
369
|
return enrich(provider, await fromRouter(client, provider));
|
|
318
370
|
}
|
|
@@ -425,6 +477,66 @@ export async function loadCatalog(provider, opts = {}) {
|
|
|
425
477
|
fetchedAt: new Date(0).toISOString(),
|
|
426
478
|
};
|
|
427
479
|
}
|
|
480
|
+
// ---------------------------------------------------------------------------
|
|
481
|
+
// Prices and balance
|
|
482
|
+
// ---------------------------------------------------------------------------
|
|
483
|
+
/** A per-token USD price as dollars per million tokens, the unit price lists use. */
|
|
484
|
+
export function perMillion(price) {
|
|
485
|
+
if (price === undefined || price === '') {
|
|
486
|
+
return '';
|
|
487
|
+
}
|
|
488
|
+
const n = Number(price) * 1_000_000;
|
|
489
|
+
if (!Number.isFinite(n)) {
|
|
490
|
+
return '';
|
|
491
|
+
}
|
|
492
|
+
// OpenRouter prices its routers (`openrouter/auto`) as "-1": it depends on the pick.
|
|
493
|
+
if (n < 0) {
|
|
494
|
+
return 'varies';
|
|
495
|
+
}
|
|
496
|
+
if (n === 0) {
|
|
497
|
+
return 'free';
|
|
498
|
+
}
|
|
499
|
+
if (n >= 1) {
|
|
500
|
+
return `$${n.toFixed(2)}`;
|
|
501
|
+
}
|
|
502
|
+
// Two significant digits for fractions of a dollar, but never fewer than cents.
|
|
503
|
+
const short = String(Number(n.toPrecision(2)));
|
|
504
|
+
return `$${(short.split('.')[1]?.length ?? 0) < 2 ? n.toFixed(2) : short}`;
|
|
505
|
+
}
|
|
506
|
+
export async function routerBalance() {
|
|
507
|
+
const base = (process.env.OPENROUTER_BASE_URL ?? 'https://openrouter.ai/api/v1').replace(/\/+$/, '');
|
|
508
|
+
const get = async (path) => {
|
|
509
|
+
const res = await fetch(`${base}${path}`, {
|
|
510
|
+
headers: { Authorization: `Bearer ${process.env.OPENROUTER_API_KEY ?? ''}` },
|
|
511
|
+
signal: AbortSignal.timeout(15_000),
|
|
512
|
+
});
|
|
513
|
+
if (!res.ok) {
|
|
514
|
+
throw Object.assign(new Error(`${path} answered ${res.status}`), {
|
|
515
|
+
status: res.status,
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
return (await res.json()).data;
|
|
519
|
+
};
|
|
520
|
+
// Raw rather than the SDK: its schema drops `free_model_daily_requests`.
|
|
521
|
+
const [key, credits] = await Promise.all([
|
|
522
|
+
get('/key'),
|
|
523
|
+
// Documented as management-key only, though ordinary keys are answered too.
|
|
524
|
+
get('/credits').catch(() => undefined),
|
|
525
|
+
]);
|
|
526
|
+
return {
|
|
527
|
+
...(credits ? { credits: credits.total_credits - credits.total_usage } : {}),
|
|
528
|
+
...(key.limit !== null && key.limit_remaining !== null
|
|
529
|
+
? {
|
|
530
|
+
cap: {
|
|
531
|
+
limit: key.limit,
|
|
532
|
+
remaining: key.limit_remaining,
|
|
533
|
+
...(key.limit_reset ? { reset: key.limit_reset } : {}),
|
|
534
|
+
},
|
|
535
|
+
}
|
|
536
|
+
: {}),
|
|
537
|
+
...(key.free_model_daily_requests ? { free: key.free_model_daily_requests } : {}),
|
|
538
|
+
};
|
|
539
|
+
}
|
|
428
540
|
/** Every provider asked at once — they are independent and mostly latency. */
|
|
429
541
|
export async function loadCatalogs(providers, opts = {}) {
|
|
430
542
|
return Promise.all(providers.map((p) => loadCatalog(p, opts)));
|
|
@@ -459,4 +571,25 @@ export function matches(row, query, filters = {}) {
|
|
|
459
571
|
}
|
|
460
572
|
/** The providers a search covers when none was named. */
|
|
461
573
|
export const catalogProviders = () => PROVIDERS;
|
|
574
|
+
// ---------------------------------------------------------------------------
|
|
575
|
+
// Grouping
|
|
576
|
+
// ---------------------------------------------------------------------------
|
|
577
|
+
/** Resold on Vertex rather than made by Google. */
|
|
578
|
+
export const isPartner = (e) => e.publisher !== undefined && e.publisher !== 'google';
|
|
579
|
+
/** Provider, then Google's own models, then each partner's together. */
|
|
580
|
+
export function byGroup(a, b) {
|
|
581
|
+
return (a.provider.localeCompare(b.provider) ||
|
|
582
|
+
Number(isPartner(a)) - Number(isPartner(b)) ||
|
|
583
|
+
(a.publisher ?? '').localeCompare(b.publisher ?? '') ||
|
|
584
|
+
a.id.localeCompare(b.id));
|
|
585
|
+
}
|
|
586
|
+
/** The heading a row is listed under, when its provider resells other makers' models. */
|
|
587
|
+
export function groupOf(e) {
|
|
588
|
+
if (!e.publisher) {
|
|
589
|
+
return undefined;
|
|
590
|
+
}
|
|
591
|
+
return isPartner(e)
|
|
592
|
+
? `${e.provider} · partner · ${e.publisher}`
|
|
593
|
+
: `${e.provider} · Google (Gemini, Gemma, Imagen, Veo)`;
|
|
594
|
+
}
|
|
462
595
|
//# sourceMappingURL=catalog.js.map
|
package/dist/commands/index.js
CHANGED
|
@@ -13,6 +13,7 @@ import { open } from "./open.js";
|
|
|
13
13
|
import { run } from "./run.js";
|
|
14
14
|
import { sandbox } from "./sandbox.js";
|
|
15
15
|
import { version } from "./version.js";
|
|
16
|
+
import { view } from "./view.js";
|
|
16
17
|
/**
|
|
17
18
|
* Insertion order is the order help prints in, and it is deliberate: the four
|
|
18
19
|
* a new user needs first, then the credentials-and-models pair, then the two
|
|
@@ -32,6 +33,7 @@ export const COMMANDS = {
|
|
|
32
33
|
models,
|
|
33
34
|
check,
|
|
34
35
|
inspect,
|
|
36
|
+
view,
|
|
35
37
|
export: pack,
|
|
36
38
|
import: unpack,
|
|
37
39
|
memory,
|