@zenera/cli 1.1.31 → 1.1.32
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 +13 -0
- package/dist/commands/meta.js +14 -13
- package/dist/keys.d.ts +6 -1
- package/dist/keys.js +6 -1
- package/dist/meta/model.d.ts +11 -2
- package/dist/meta/model.js +12 -3
- package/dist/meta/wire.d.ts +7 -0
- package/dist/meta/wire.js +26 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -14,6 +14,9 @@
|
|
|
14
14
|
[](https://www.npmjs.com/package/@zenera/cli)
|
|
15
15
|
[](https://github.com/andreyryabov/ZeneraNeo/blob/main/LICENSE)
|
|
16
16
|
[](https://nodejs.org)
|
|
17
|
+
[](https://github.com/andreyryabov/ZeneraNeo/actions/workflows/ci.yml)
|
|
18
|
+
|
|
19
|
+
[Install](#install) · [Quickstart](#quickstart) · [Commands](#commands) · [Documentation](#documentation) · [Family](#the-rest-of-the-family) · [License](#license)
|
|
17
20
|
|
|
18
21
|
</div>
|
|
19
22
|
|
|
@@ -1125,6 +1128,16 @@ terminal:
|
|
|
1125
1128
|
- [docs/gateway.md](https://github.com/andreyryabov/ZeneraNeo/blob/main/docs/gateway.md)
|
|
1126
1129
|
- every model through a Bifrost gateway, with one virtual key.
|
|
1127
1130
|
|
|
1131
|
+
## The rest of the family
|
|
1132
|
+
|
|
1133
|
+
| Package | What it is |
|
|
1134
|
+
| ----------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
|
|
1135
|
+
| **`@zenera/cli`** | `zen` - agent projects on the command line |
|
|
1136
|
+
| [`@zenera/neo`](https://github.com/andreyryabov/ZeneraNeo/blob/main/packages/neo/README.md) | the runtime - agents, models, tools, skills, memory |
|
|
1137
|
+
| [`@zenera/faker`](https://github.com/andreyryabov/ZeneraNeo/blob/main/packages/faker/README.md) | `zen faker` - a mock API from an openapi/swagger document |
|
|
1138
|
+
| [`@zenera/rag`](https://github.com/andreyryabov/ZeneraNeo/blob/main/packages/rag/README.md) | `zen rag` - indexing and search for documents and API schemas |
|
|
1139
|
+
| [`@zenera/view`](https://github.com/andreyryabov/ZeneraNeo/blob/main/packages/view/README.md) | `zen view` - a live local viewer for a folder |
|
|
1140
|
+
|
|
1128
1141
|
## License
|
|
1129
1142
|
|
|
1130
1143
|
Early days and moving fast - issues, questions and pull requests are welcome.
|
package/dist/commands/meta.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readProjectConfig } from '@zenera/neo';
|
|
1
|
+
import { gatewayOf, readProjectConfig } from '@zenera/neo';
|
|
2
2
|
import { spawn } from 'node:child_process';
|
|
3
3
|
import { randomUUID } from 'node:crypto';
|
|
4
4
|
import { existsSync, readFileSync } from 'node:fs';
|
|
@@ -11,7 +11,7 @@ import { copilotEventLog } from "../eventlog.js";
|
|
|
11
11
|
import { ensureHome } from "../home.js";
|
|
12
12
|
import { assertOwner, isProvider, KeyStore, PROVIDERS } from "../keys.js";
|
|
13
13
|
import { probeChat, probeModel } from "../liveness.js";
|
|
14
|
-
import { answerBox, chooseModel, DATASET_DETAILS, DATASET_SUMMARY, DATASET_USAGE, datasetProjectFlag, DEFAULT_RESUMES, defaultRef, editorFiles, FINETUNE_DETAILS, FINETUNE_SUMMARY, FINETUNE_USAGE, finetuneProjectFlag, lastSession, launch, listPrompts, loadPrompt, locate, masked, misspelledProvider, MODEL_ENV, openLog, ORDER, PROMPT_DIR, providersWarning, readMeta, recordSession, RESUME_PROMPT, resumeDelayMs, runDataset, runFinetune, sameModel, sessionModel, sessionTotals, SOURCE_LABELS, startVertexRelay, tailSpans, Tally, terminalSink, transient, wire, writeMeta, } from "../meta/index.js";
|
|
14
|
+
import { answerBox, chooseModel, DATASET_DETAILS, DATASET_SUMMARY, DATASET_USAGE, datasetProjectFlag, DEFAULT_RESUMES, defaultRef, editorFiles, FINETUNE_DETAILS, FINETUNE_SUMMARY, FINETUNE_USAGE, finetuneProjectFlag, lastSession, launch, listPrompts, loadPrompt, locate, masked, misspelledProvider, MODEL_ENV, openLog, ORDER, PROMPT_DIR, providersWarning, readMeta, recordSession, RESUME_PROMPT, resumeDelayMs, runDataset, runFinetune, sameModel, sessionModel, sessionTotals, SOURCE_LABELS, startVertexRelay, tailSpans, Tally, terminalSink, transient, wire, wireGateway, writeMeta, } from "../meta/index.js";
|
|
15
15
|
import { splitRef } from "../modelref.js";
|
|
16
16
|
import { duration } from "../narrate.js";
|
|
17
17
|
import * as Projects from "../projects.js";
|
|
@@ -554,6 +554,8 @@ async function go(ctx, values, project, prompt, promptName) {
|
|
|
554
554
|
ensureHome();
|
|
555
555
|
const store = await KeyStore.open();
|
|
556
556
|
store.materialize();
|
|
557
|
+
// After materialize: a keyring in bifrost mode is what sets the url.
|
|
558
|
+
const gateway = gatewayOf();
|
|
557
559
|
const only = values.provider ? asProvider(values.provider) : undefined;
|
|
558
560
|
const chosen = chooseModel({
|
|
559
561
|
flag: values.model,
|
|
@@ -561,7 +563,7 @@ async function go(ctx, values, project, prompt, promptName) {
|
|
|
561
563
|
env: process.env[MODEL_ENV],
|
|
562
564
|
stored: (await readMeta()).model,
|
|
563
565
|
project: projectModel(project.dir),
|
|
564
|
-
fallback: defaultRef(store, only),
|
|
566
|
+
fallback: defaultRef(store, only, gateway !== undefined),
|
|
565
567
|
});
|
|
566
568
|
if (!chosen) {
|
|
567
569
|
if (only) {
|
|
@@ -577,11 +579,11 @@ async function go(ctx, values, project, prompt, promptName) {
|
|
|
577
579
|
if (meant && !only) {
|
|
578
580
|
warn(`${chosen.ref} names no provider — did you mean ${meant}/${id}?`);
|
|
579
581
|
}
|
|
580
|
-
const entry = store.active(owner);
|
|
581
|
-
if (!entry) {
|
|
582
|
+
const entry = gateway ? undefined : store.active(owner);
|
|
583
|
+
if (!gateway && !entry) {
|
|
582
584
|
throw credentialError(`no ${owner} key`, `add one: zen key add ${owner}`);
|
|
583
585
|
}
|
|
584
|
-
const wiring = wire(store, entry, id);
|
|
586
|
+
const wiring = gateway ? wireGateway(gateway, owner, id) : wire(store, entry, id);
|
|
585
587
|
const binary = locate();
|
|
586
588
|
// Copilot cannot carry a session to another model: the resume fails on the first call.
|
|
587
589
|
const ran = values.resume ? sessionModel(values.resume) : undefined;
|
|
@@ -999,10 +1001,10 @@ async function vet(ctx, ref) {
|
|
|
999
1001
|
const { provider, id } = splitRef(ref);
|
|
1000
1002
|
const owner = provider ?? 'openai';
|
|
1001
1003
|
const store = await KeyStore.open();
|
|
1002
|
-
|
|
1004
|
+
store.materialize();
|
|
1005
|
+
if (!gatewayOf() && !store.active(owner)) {
|
|
1003
1006
|
throw credentialError(`no ${owner} key to try ${id} with`, `add one: zen key add ${owner}`);
|
|
1004
1007
|
}
|
|
1005
|
-
store.materialize();
|
|
1006
1008
|
const bar = ctx.json ? undefined : progress();
|
|
1007
1009
|
bar?.update(dim(`asking ${owner} about ${id} …`));
|
|
1008
1010
|
const probe = await probeModel({
|
|
@@ -1058,13 +1060,12 @@ async function pickModel(ctx) {
|
|
|
1058
1060
|
throw usageError('--pick needs a terminal', 'name the model instead');
|
|
1059
1061
|
}
|
|
1060
1062
|
const store = await KeyStore.open();
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
if (known.length === 0) {
|
|
1063
|
+
store.materialize();
|
|
1064
|
+
const known = store.entries.map((e) => e.provider).filter(isProvider);
|
|
1065
|
+
if (known.length === 0 && !gatewayOf()) {
|
|
1065
1066
|
throw credentialError('no keys to choose from', 'add one: zen key add openai');
|
|
1066
1067
|
}
|
|
1067
|
-
const held = [...new Set(known)];
|
|
1068
|
+
const held = gatewayOf() ? [] : [...new Set(known)];
|
|
1068
1069
|
const { browseModels } = await import("./models.js");
|
|
1069
1070
|
const chosen = await browseModels({
|
|
1070
1071
|
initial: held.length === 1 ? held[0] : undefined,
|
package/dist/keys.d.ts
CHANGED
|
@@ -202,7 +202,12 @@ export declare class KeyStore {
|
|
|
202
202
|
* since its url alone is what switches the library over.
|
|
203
203
|
*/
|
|
204
204
|
environment(only?: KeyOwner[]): Record<string, string>;
|
|
205
|
-
/**
|
|
205
|
+
/**
|
|
206
|
+
* Copies the keyring's active credentials into `process.env`, the only
|
|
207
|
+
* place the library looks. Must run before anything that reads them —
|
|
208
|
+
* in `bifrost` mode this is what sets `BIFROST_BASE_URL`, so `gatewayOf()`
|
|
209
|
+
* returns undefined until it has. Returns what it set.
|
|
210
|
+
*/
|
|
206
211
|
materialize(only?: KeyOwner[]): Record<string, string>;
|
|
207
212
|
}
|
|
208
213
|
/**
|
package/dist/keys.js
CHANGED
|
@@ -482,7 +482,12 @@ export class KeyStore {
|
|
|
482
482
|
}
|
|
483
483
|
return env;
|
|
484
484
|
}
|
|
485
|
-
/**
|
|
485
|
+
/**
|
|
486
|
+
* Copies the keyring's active credentials into `process.env`, the only
|
|
487
|
+
* place the library looks. Must run before anything that reads them —
|
|
488
|
+
* in `bifrost` mode this is what sets `BIFROST_BASE_URL`, so `gatewayOf()`
|
|
489
|
+
* returns undefined until it has. Returns what it set.
|
|
490
|
+
*/
|
|
486
491
|
materialize(only) {
|
|
487
492
|
const env = this.environment(only);
|
|
488
493
|
Object.assign(process.env, env);
|
package/dist/meta/model.d.ts
CHANGED
|
@@ -21,12 +21,21 @@ export interface ModelSources {
|
|
|
21
21
|
}
|
|
22
22
|
/** Where the answer is looked for, best first. */
|
|
23
23
|
export declare const ORDER: readonly ModelSource[];
|
|
24
|
+
/**
|
|
25
|
+
* Decides which model `zen meta` runs copilot on, when it can be named in six
|
|
26
|
+
* places at once. The source comes back too, so a surprising pick can be traced
|
|
27
|
+
* ("from agents.yaml model:") instead of guessed at.
|
|
28
|
+
*/
|
|
24
29
|
export declare function chooseModel(sources: ModelSources): ModelChoice | undefined;
|
|
25
30
|
export declare const SOURCE_LABELS: Record<ModelSource, string>;
|
|
26
31
|
/** First of each row is the default; the rest are the next best. */
|
|
27
32
|
export declare const RECOMMENDED: Partial<Record<Provider, readonly string[]>>;
|
|
28
|
-
/**
|
|
29
|
-
|
|
33
|
+
/**
|
|
34
|
+
* The model `zen meta` falls back to when nothing names one, so a machine with
|
|
35
|
+
* just a key (or just a gateway, which reaches every provider) still runs.
|
|
36
|
+
* `only` is `--provider`: the recommendation for that provider, if it is held.
|
|
37
|
+
*/
|
|
38
|
+
export declare function defaultRef(store: KeyStore, only?: Provider, gateway?: boolean): string | undefined;
|
|
30
39
|
export interface MetaFile {
|
|
31
40
|
version: 1;
|
|
32
41
|
model?: string;
|
package/dist/meta/model.js
CHANGED
|
@@ -13,6 +13,11 @@ export const ORDER = [
|
|
|
13
13
|
'project',
|
|
14
14
|
'default',
|
|
15
15
|
];
|
|
16
|
+
/**
|
|
17
|
+
* Decides which model `zen meta` runs copilot on, when it can be named in six
|
|
18
|
+
* places at once. The source comes back too, so a surprising pick can be traced
|
|
19
|
+
* ("from agents.yaml model:") instead of guessed at.
|
|
20
|
+
*/
|
|
16
21
|
export function chooseModel(sources) {
|
|
17
22
|
const at = {
|
|
18
23
|
flag: sources.flag,
|
|
@@ -57,11 +62,15 @@ export const RECOMMENDED = {
|
|
|
57
62
|
};
|
|
58
63
|
/** Which provider is tried first when several are held. */
|
|
59
64
|
const PREFERENCE = ['anthropic', 'openai', 'vertex', 'google', 'openrouter'];
|
|
60
|
-
/**
|
|
61
|
-
|
|
65
|
+
/**
|
|
66
|
+
* The model `zen meta` falls back to when nothing names one, so a machine with
|
|
67
|
+
* just a key (or just a gateway, which reaches every provider) still runs.
|
|
68
|
+
* `only` is `--provider`: the recommendation for that provider, if it is held.
|
|
69
|
+
*/
|
|
70
|
+
export function defaultRef(store, only, gateway = false) {
|
|
62
71
|
for (const provider of only ? [only] : PREFERENCE) {
|
|
63
72
|
const id = RECOMMENDED[provider]?.[0];
|
|
64
|
-
if (id && store.active(provider)) {
|
|
73
|
+
if (id && (gateway || store.active(provider))) {
|
|
65
74
|
return `${provider}/${id}`;
|
|
66
75
|
}
|
|
67
76
|
}
|
package/dist/meta/wire.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type Gateway } from '@zenera/neo';
|
|
1
2
|
import { type KeyEntry, type KeyStore, type Provider } from './host.ts';
|
|
2
3
|
/**
|
|
3
4
|
* Copilot offers its tools as OpenAI *custom* tools, which the completions API
|
|
@@ -35,6 +36,12 @@ export declare const RELAY_URL = "http://127.0.0.1:<relay>";
|
|
|
35
36
|
* through a GitHub account we are deliberately not using.
|
|
36
37
|
*/
|
|
37
38
|
export declare function wire(store: KeyStore, entry: KeyEntry, id: string): Wiring;
|
|
39
|
+
/**
|
|
40
|
+
* Through a gateway: one virtual key, and Bifrost's OpenAI or Anthropic drop-in,
|
|
41
|
+
* which routes on an `<upstream>/<id>` wire model. Claude speaks Anthropic
|
|
42
|
+
* wherever it is hosted; everything else goes through the OpenAI one.
|
|
43
|
+
*/
|
|
44
|
+
export declare function wireGateway(at: Gateway, provider: Provider, id: string): Wiring;
|
|
38
45
|
/** Values a `--dry-run` may print. A key is four characters and an apology. */
|
|
39
46
|
export declare function masked(env: Record<string, string>, secret: readonly string[]): string[];
|
|
40
47
|
/** Non-fatal, said once, because a competing file is a silent override. */
|
package/dist/meta/wire.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { GATEWAY_UPSTREAMS } from '@zenera/neo';
|
|
1
2
|
import { randomBytes } from 'node:crypto';
|
|
2
3
|
import { existsSync } from 'node:fs';
|
|
3
4
|
import { credentialError, envOf, invokedAs, yellow, } from "./host.js";
|
|
@@ -112,6 +113,31 @@ export function wire(store, entry, id) {
|
|
|
112
113
|
}
|
|
113
114
|
return { provider, model: env.COPILOT_PROVIDER_WIRE_MODEL ?? id, env, secret, warnings };
|
|
114
115
|
}
|
|
116
|
+
/**
|
|
117
|
+
* Through a gateway: one virtual key, and Bifrost's OpenAI or Anthropic drop-in,
|
|
118
|
+
* which routes on an `<upstream>/<id>` wire model. Claude speaks Anthropic
|
|
119
|
+
* wherever it is hosted; everything else goes through the OpenAI one.
|
|
120
|
+
*/
|
|
121
|
+
export function wireGateway(at, provider, id) {
|
|
122
|
+
const claude = provider === 'anthropic' || (provider === 'vertex' && vertexClaude(id));
|
|
123
|
+
const model = provider === 'vertex' ? id.replace(/^anthropic\//, '') : id;
|
|
124
|
+
const env = {
|
|
125
|
+
COPILOT_PROVIDER_TYPE: claude ? 'anthropic' : 'openai',
|
|
126
|
+
COPILOT_PROVIDER_BASE_URL: `${at.baseURL}${claude ? '/anthropic' : '/openai/v1'}`,
|
|
127
|
+
COPILOT_PROVIDER_MODEL_ID: model.replace(/^google\//, ''),
|
|
128
|
+
COPILOT_PROVIDER_WIRE_MODEL: `${GATEWAY_UPSTREAMS[provider]}/${model}`,
|
|
129
|
+
};
|
|
130
|
+
const api = wireApi(provider, model);
|
|
131
|
+
if (api) {
|
|
132
|
+
env.COPILOT_PROVIDER_WIRE_API = api;
|
|
133
|
+
}
|
|
134
|
+
const secret = [];
|
|
135
|
+
if (at.apiKey) {
|
|
136
|
+
env.COPILOT_PROVIDER_API_KEY = at.apiKey;
|
|
137
|
+
secret.push('COPILOT_PROVIDER_API_KEY');
|
|
138
|
+
}
|
|
139
|
+
return { provider, model: env.COPILOT_PROVIDER_WIRE_MODEL, env, secret, warnings: [] };
|
|
140
|
+
}
|
|
115
141
|
/** Values a `--dry-run` may print. A key is four characters and an apology. */
|
|
116
142
|
export function masked(env, secret) {
|
|
117
143
|
return Object.entries(env)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zenera/cli",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.32",
|
|
4
4
|
"description": "Command-line front end for @zenera/neo: agentic projects you can run, share and commit.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agents",
|
|
@@ -54,8 +54,8 @@
|
|
|
54
54
|
"@google/genai": "^2.30.0",
|
|
55
55
|
"@inkjs/ui": "^2.0.0",
|
|
56
56
|
"@openrouter/sdk": "^1.4.25",
|
|
57
|
-
"@zenera/neo": "^1.1.
|
|
58
|
-
"@zenera/view": "^1.1.
|
|
57
|
+
"@zenera/neo": "^1.1.32",
|
|
58
|
+
"@zenera/view": "^1.1.32",
|
|
59
59
|
"google-auth-library": "^10.0.0",
|
|
60
60
|
"ink": "^7.1.1",
|
|
61
61
|
"openai": "^6.0.0",
|