@cef-ai/cli 2.9.1 → 3.0.0
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 +1 -1
- package/dist/commands/eval.d.ts +14 -18
- package/dist/commands/eval.d.ts.map +1 -1
- package/dist/commands/eval.js +78 -99
- package/dist/commands/eval.js.map +1 -1
- package/dist/commands/eval.test.js +124 -43
- package/dist/commands/eval.test.js.map +1 -1
- package/dist/lib/eval-auth.d.ts +1 -124
- package/dist/lib/eval-auth.d.ts.map +1 -1
- package/dist/lib/eval-auth.js +8 -343
- package/dist/lib/eval-auth.js.map +1 -1
- package/dist/lib/eval-vault.d.ts +35 -0
- package/dist/lib/eval-vault.d.ts.map +1 -0
- package/dist/lib/eval-vault.js +54 -0
- package/dist/lib/eval-vault.js.map +1 -0
- package/dist/lib/eval-workflow.d.ts +11 -0
- package/dist/lib/eval-workflow.d.ts.map +1 -1
- package/dist/lib/eval-workflow.js +19 -1
- package/dist/lib/eval-workflow.js.map +1 -1
- package/package.json +5 -7
- package/dist/lib/eval-auth.test.d.ts +0 -2
- package/dist/lib/eval-auth.test.d.ts.map +0 -1
- package/dist/lib/eval-auth.test.js +0 -192
- package/dist/lib/eval-auth.test.js.map +0 -1
- package/dist/lib/eval-store.d.ts +0 -62
- package/dist/lib/eval-store.d.ts.map +0 -1
- package/dist/lib/eval-store.js +0 -334
- package/dist/lib/eval-store.js.map +0 -1
- package/dist/lib/eval-store.test.d.ts +0 -2
- package/dist/lib/eval-store.test.d.ts.map +0 -1
- package/dist/lib/eval-store.test.js +0 -155
- package/dist/lib/eval-store.test.js.map +0 -1
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@ invoke it via `pnpm exec cef <subcommand>`.
|
|
|
25
25
|
| `cef inspect <agentDir>` | shipped | Read a built `manifest.json`, run the bundle in `node:vm`, and report the handler names exposed on `__handlers` plus any routing targets the manifest references but the bundle doesn't expose. |
|
|
26
26
|
| `cef publish` | shipped | POST a built agent's card to the marketplace API (`--as-pubkey`, `--access-token`/`$CEF_ACCESS_TOKEN` — the same CLI access token `cef deploy` uses). |
|
|
27
27
|
| `cef push` | shipped | Upload an agent to DDC. `--kind internal` (the default) uploads a built agent's `bundle.js` (+ widget dirs) and stamps the manifest's bundle ref — directly into a bucket you control (`--bucket`), or through vault-api into an organization vault's scope (`--vault <id> --vault-scope <scope>`). `--kind external` registers an agent that runs **outside** the platform, from its [A2A](https://agent2agent.info) Agent Card: fetches the card, derives a manifest (`kind: "external"`, `a2aCard: <url>`, no bundle) and writes it to the same bucket registry. External flags: `--card <url>`, `--alias`, `--agent-version`, `--scope`, `--idle-timeout`. |
|
|
28
|
-
| `cef eval <sub>` | shipped | Workflow evaluations, one workflow at a time (`--workflow`, default: the one `cef.config.ts` or `dist/` declares). `datasets`, `create <dataset>`, `pull <dataset>` / `push <dataset>` (repo `datasets/<dataset>/` ↔ the
|
|
28
|
+
| `cef eval <sub>` | shipped | Workflow evaluations, one workflow at a time (`--workflow`, default: the one `cef.config.ts` or `dist/` declares). `datasets`, `create <dataset>`, `pull <dataset>` / `push <dataset>` (repo `datasets/<dataset>/` ↔ the vault; `push --cut`, `--force`, `--prune`, `--restore`; `pull --force`), `run <dataset>` (`--workflow-version`, `--dataset-version`, `--repeats`, `--baseline`, `--resume`), `experiments [dataset]`, `compare <a> <b>`. Evaluations live in the vault the workflow runs in (`--vault`/`$CEF_VAULT_ID`, default the wallet's own), in its scope (`--scope`, default its `requiredScopes`); needs a wallet (`--keystore`/`--password` or `--secret-phrase`) or `--access-token`/`$CEF_ACCESS_TOKEN`, and the vault's grant on that scope. `run` also needs `--as-pubkey`/`$CEF_AS_PUBKEY`. See [`@cef-ai/eval`](../eval/README.md). |
|
|
29
29
|
| `cef test` | stub | Placeholder — use `vitest` directly with `@cef-ai/testing` until the integrated runner lands. |
|
|
30
30
|
| `cef dev` | stub | Placeholder — pending the packaged platform image. |
|
|
31
31
|
|
package/dist/commands/eval.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Command } from "commander";
|
|
2
|
-
import { type Case, type DatasetSummary, type EvalStore, type Experiment, type ExperimentComparison, type ExperimentProgress, type Header, type Limits, type
|
|
2
|
+
import { type Case, type DatasetSummary, type EvalStore, type Experiment, type ExperimentComparison, type ExperimentProgress, type Header, type Limits, type ResultCompatibility, type ResultField, type WorkflowTarget } from "@cef-ai/eval";
|
|
3
3
|
import { type LocalWorkflowOptions } from "../lib/eval-workflow.js";
|
|
4
4
|
export declare const VERSIONS_FILE = ".versions.json";
|
|
5
5
|
/** Where datasets live in a workflow's folder. */
|
|
@@ -25,7 +25,7 @@ export interface PulledState {
|
|
|
25
25
|
}
|
|
26
26
|
/**
|
|
27
27
|
* Read the repo dir: header and every case file, each checked. A header written
|
|
28
|
-
* by hand need not carry `createdAt` (the
|
|
28
|
+
* by hand need not carry `createdAt` (the vault's wins on push) nor
|
|
29
29
|
* `workflowId` (the folder's workflow is meant); a `baselineId` in it is
|
|
30
30
|
* ignored — the baseline is chosen on the platform.
|
|
31
31
|
*/
|
|
@@ -44,7 +44,7 @@ export declare function evalCreate(opts: {
|
|
|
44
44
|
export interface PullResult {
|
|
45
45
|
dir: string;
|
|
46
46
|
cases: number;
|
|
47
|
-
/** Case files removed: pulled before, no longer live in the
|
|
47
|
+
/** Case files removed: pulled before, no longer live in the vault. */
|
|
48
48
|
removed: string[];
|
|
49
49
|
/** Case files the last pull never wrote (new, unpushed cases) — left untouched. */
|
|
50
50
|
localOnly: string[];
|
|
@@ -54,14 +54,14 @@ export interface PullResult {
|
|
|
54
54
|
* Bucket → repo dir.
|
|
55
55
|
*
|
|
56
56
|
* A case file is removed only when the LAST PULL wrote it (its id is in
|
|
57
|
-
* `.versions.json`) and it is no longer live in the
|
|
57
|
+
* `.versions.json`) and it is no longer live in the vault — deleted or
|
|
58
58
|
* archived there since. A file the last pull did not write is a case someone
|
|
59
59
|
* is adding in the repo and has not pushed yet; it is reported, never touched.
|
|
60
60
|
*
|
|
61
61
|
* UNPUSHED EDITS ARE NOT OVERWRITTEN. Before writing anything, every case file
|
|
62
62
|
* pull would rewrite or remove is compared with what the last pull/push left
|
|
63
63
|
* there (the hash in `.versions.json`; for an older file without hashes, the
|
|
64
|
-
* pulled revision's content) and with what the
|
|
64
|
+
* pulled revision's content) and with what the vault now holds. A file that
|
|
65
65
|
* matches neither was edited locally and not pushed: the pull is refused
|
|
66
66
|
* unless `force`.
|
|
67
67
|
*/
|
|
@@ -81,11 +81,11 @@ export interface PushResult {
|
|
|
81
81
|
rev: number;
|
|
82
82
|
}>;
|
|
83
83
|
unchanged: string[];
|
|
84
|
-
/** Deleted or archived in the
|
|
84
|
+
/** Deleted or archived in the vault, put back by `restore` before the repo's version was written. */
|
|
85
85
|
restored: string[];
|
|
86
|
-
/** Pulled, then removed from the repo dir; still live in the
|
|
86
|
+
/** Pulled, then removed from the repo dir; still live in the vault. Deleted with `prune`. */
|
|
87
87
|
missingLocally: string[];
|
|
88
|
-
/** Live in the
|
|
88
|
+
/** Live in the vault but never pulled (added there since). Kept; `prune` never touches them. */
|
|
89
89
|
notPulled: string[];
|
|
90
90
|
deleted: string[];
|
|
91
91
|
cut?: {
|
|
@@ -94,19 +94,19 @@ export interface PushResult {
|
|
|
94
94
|
};
|
|
95
95
|
}
|
|
96
96
|
/**
|
|
97
|
-
* Repo dir →
|
|
97
|
+
* Repo dir → vault.
|
|
98
98
|
*
|
|
99
|
-
* EVERY CHECK BEFORE ANY WRITE, so a refused push leaves the
|
|
99
|
+
* EVERY CHECK BEFORE ANY WRITE, so a refused push leaves the vault exactly as
|
|
100
100
|
* it was — header included:
|
|
101
|
-
* - a repo case that is deleted or archived in the
|
|
101
|
+
* - a repo case that is deleted or archived in the vault is refused unless
|
|
102
102
|
* `restore` (an explicit undelete/unarchive, then the repo's version is
|
|
103
103
|
* written as a new revision). `force` does NOT imply it: overwriting an
|
|
104
104
|
* edit and resurrecting a case someone removed are different decisions.
|
|
105
|
-
* - a case edited in the
|
|
105
|
+
* - a case edited in the vault since the pull, whose content differs from
|
|
106
106
|
* the repo's, is a conflict unless `force`.
|
|
107
107
|
* - `prune` deletes only cases the last pull wrote and the repo has since
|
|
108
|
-
* dropped; a case edited in the
|
|
109
|
-
* unless `force`. Cases added in the
|
|
108
|
+
* dropped; a case edited in the vault since that pull is a conflict
|
|
109
|
+
* unless `force`. Cases added in the vault after the pull are never
|
|
110
110
|
* pruned — the repo never knew them.
|
|
111
111
|
*/
|
|
112
112
|
export declare function evalPush(opts: {
|
|
@@ -173,9 +173,6 @@ export declare function evalCompare(opts: {
|
|
|
173
173
|
base: string;
|
|
174
174
|
candidate: string;
|
|
175
175
|
}): Promise<ExperimentComparison>;
|
|
176
|
-
export declare function evalMigrate(opts: {
|
|
177
|
-
store: EvalStore;
|
|
178
|
-
}): Promise<MigrationReport>;
|
|
179
176
|
/**
|
|
180
177
|
* The Result a workflow version declares, read off its graph:
|
|
181
178
|
* - the live deployment row's `params.graph` when the version is the live
|
|
@@ -208,7 +205,6 @@ export declare function formatDatasetDiff(c: Pick<ExperimentComparison, "dataset
|
|
|
208
205
|
export declare function formatResultCheck(r: ResultCheck): string[];
|
|
209
206
|
export declare function formatCompatibility(r: ResultCompatibility, version: string): string[];
|
|
210
207
|
export declare function formatComparison(c: ExperimentComparison): string;
|
|
211
|
-
export declare function formatMigration(r: MigrationReport): string;
|
|
212
208
|
export declare function addEvalCommands(program: Command, opts: {
|
|
213
209
|
onError: (e: unknown) => never;
|
|
214
210
|
fetchImpl?: typeof fetch;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"eval.d.ts","sourceRoot":"","sources":["../../src/commands/eval.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"eval.d.ts","sourceRoot":"","sources":["../../src/commands/eval.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/eval.js
CHANGED
|
@@ -3,12 +3,11 @@
|
|
|
3
3
|
*
|
|
4
4
|
* cef eval datasets list the workflow's datasets
|
|
5
5
|
* cef eval create <dataset> create one
|
|
6
|
-
* cef eval pull <dataset> [--dir datasets] [--force]
|
|
7
|
-
* cef eval push <dataset> [--dir datasets] [--cut note] repo dir →
|
|
6
|
+
* cef eval pull <dataset> [--dir datasets] [--force] vault → repo dir (refuses to overwrite unpushed edits)
|
|
7
|
+
* cef eval push <dataset> [--dir datasets] [--cut note] repo dir → vault (new revisions for changed cases)
|
|
8
8
|
* cef eval run <dataset> [--dataset-version N] [--workflow-version v] [--repeats 3] [--concurrency 4] [--baseline exp]
|
|
9
9
|
* cef eval experiments [dataset] experiment history
|
|
10
10
|
* cef eval compare <a> <b> how experiment b differs from a
|
|
11
|
-
* cef eval migrate copy v1 storage (evals/datasets/…) into the v2 layout
|
|
12
11
|
*
|
|
13
12
|
* Every command is about one workflow: `--workflow <id>`, by default the one
|
|
14
13
|
* `cef.config.ts` (or the build output) in the current directory declares.
|
|
@@ -21,28 +20,27 @@
|
|
|
21
20
|
*
|
|
22
21
|
* Experiments are not in the repo; their results stay on the platform.
|
|
23
22
|
*
|
|
24
|
-
* The
|
|
23
|
+
* The vault keeps every revision; the repo holds the latest, so a git diff of a
|
|
25
24
|
* case reads as the edit it is. `push` writes a new revision only for a case
|
|
26
|
-
* whose content changed, and refuses one that changed in the
|
|
25
|
+
* whose content changed, and refuses one that changed in the vault since it was
|
|
27
26
|
* pulled (someone edited it in ROC) unless `--force`, and one that is deleted or
|
|
28
27
|
* archived there unless `--restore`. `pull` and `push --prune` only ever remove
|
|
29
28
|
* what the last pull wrote (`.versions.json`), never a case the other side has
|
|
30
29
|
* not seen. `pull` refuses to overwrite or remove a case file edited since the
|
|
31
30
|
* last pull/push (its content matches neither what was pulled nor what the
|
|
32
|
-
*
|
|
31
|
+
* vault now holds) unless `--force`.
|
|
33
32
|
*
|
|
34
33
|
* The functions below take an `EvalStore` (and a `WorkflowTarget` for `run`) so
|
|
35
34
|
* they run unchanged against a memory store in tests.
|
|
36
35
|
*/
|
|
37
36
|
import { mkdir, readdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
38
37
|
import path from "node:path";
|
|
39
|
-
import { canonicalJson, checkResultCompatibility, compareExperiments, createDataset, cutVersion, deleteCase, findExperiment, getCaseRevision, getHeader, getVersion, listArchivedCaseIds, listCaseRevisions, listDatasets, listDeletedCaseIds, listExperiments, listRunRecords, listVersions, listWorkflows, loadWorkingSet,
|
|
40
|
-
import {
|
|
41
|
-
import {
|
|
42
|
-
import {
|
|
43
|
-
import { gatewayStore } from "../lib/eval-store.js";
|
|
38
|
+
import { canonicalJson, checkResultCompatibility, compareExperiments, createDataset, cutVersion, deleteCase, findExperiment, getCaseRevision, getHeader, getVersion, listArchivedCaseIds, listCaseRevisions, listDatasets, listDeletedCaseIds, listExperiments, listRunRecords, listVersions, listWorkflows, loadWorkingSet, parseCase, parseHeader, resultFieldsFromGraph, runExperiment, saveCase, sha256Hex, unarchiveCase, undeleteCase, updateHeader, } from "@cef-ai/eval";
|
|
39
|
+
import { Browse, Events } from "@cef-ai/vault-sdk/internal";
|
|
40
|
+
import { loadWallet } from "../lib/eval-auth.js";
|
|
41
|
+
import { openVault, vaultEvalStore } from "../lib/eval-vault.js";
|
|
44
42
|
import { fetchOrchestrator, getDeploymentSet, nodeWorkflowTarget, workflowAgentId, } from "../lib/eval-target.js";
|
|
45
|
-
import { localGraph, resolveWorkflowId } from "../lib/eval-workflow.js";
|
|
43
|
+
import { DEFAULT_WORKFLOW_SCOPE, localGraph, localWorkflowScope, resolveWorkflowId, } from "../lib/eval-workflow.js";
|
|
46
44
|
import { envConfig } from "../lib/env.js";
|
|
47
45
|
/* ------------------------------------------------------------------------ */
|
|
48
46
|
/* Repo dir */
|
|
@@ -76,7 +74,7 @@ async function writeState(store, workflowId, dir, dataset, cases, hashes) {
|
|
|
76
74
|
}
|
|
77
75
|
/**
|
|
78
76
|
* Read the repo dir: header and every case file, each checked. A header written
|
|
79
|
-
* by hand need not carry `createdAt` (the
|
|
77
|
+
* by hand need not carry `createdAt` (the vault's wins on push) nor
|
|
80
78
|
* `workflowId` (the folder's workflow is meant); a `baselineId` in it is
|
|
81
79
|
* ignored — the baseline is chosen on the platform.
|
|
82
80
|
*/
|
|
@@ -153,14 +151,14 @@ async function caseFileHash(file) {
|
|
|
153
151
|
* Bucket → repo dir.
|
|
154
152
|
*
|
|
155
153
|
* A case file is removed only when the LAST PULL wrote it (its id is in
|
|
156
|
-
* `.versions.json`) and it is no longer live in the
|
|
154
|
+
* `.versions.json`) and it is no longer live in the vault — deleted or
|
|
157
155
|
* archived there since. A file the last pull did not write is a case someone
|
|
158
156
|
* is adding in the repo and has not pushed yet; it is reported, never touched.
|
|
159
157
|
*
|
|
160
158
|
* UNPUSHED EDITS ARE NOT OVERWRITTEN. Before writing anything, every case file
|
|
161
159
|
* pull would rewrite or remove is compared with what the last pull/push left
|
|
162
160
|
* there (the hash in `.versions.json`; for an older file without hashes, the
|
|
163
|
-
* pulled revision's content) and with what the
|
|
161
|
+
* pulled revision's content) and with what the vault now holds. A file that
|
|
164
162
|
* matches neither was edited locally and not pushed: the pull is refused
|
|
165
163
|
* unless `force`.
|
|
166
164
|
*/
|
|
@@ -211,13 +209,13 @@ export async function evalPull(opts) {
|
|
|
211
209
|
continue;
|
|
212
210
|
if (local === (await lastSynced(id)))
|
|
213
211
|
continue;
|
|
214
|
-
edited.push(c ? id : `${id} (removed in the
|
|
212
|
+
edited.push(c ? id : `${id} (removed in the vault)`);
|
|
215
213
|
}
|
|
216
214
|
}
|
|
217
215
|
if (edited.length) {
|
|
218
|
-
throw new Error(`[cef eval] these case files were edited since the last pull/push and differ from the
|
|
216
|
+
throw new Error(`[cef eval] these case files were edited since the last pull/push and differ from the vault:\n ${edited.join("\n ")}\n`
|
|
219
217
|
+ "Nothing was written. Push them first (cef eval push), move the edits aside, "
|
|
220
|
-
+ "or pull with --force to overwrite them with the
|
|
218
|
+
+ "or pull with --force to overwrite them with the vault's version.");
|
|
221
219
|
}
|
|
222
220
|
// The baseline stays on the platform: in git it would be stale the moment someone picks another in ROC.
|
|
223
221
|
await writeFile(path.join(root, "header.json"), canonicalJson(withoutBaseline(header)));
|
|
@@ -242,19 +240,19 @@ async function latestRevision(store, workflowId, dataset, id) {
|
|
|
242
240
|
return c ? { case: c, rev } : undefined;
|
|
243
241
|
}
|
|
244
242
|
/**
|
|
245
|
-
* Repo dir →
|
|
243
|
+
* Repo dir → vault.
|
|
246
244
|
*
|
|
247
|
-
* EVERY CHECK BEFORE ANY WRITE, so a refused push leaves the
|
|
245
|
+
* EVERY CHECK BEFORE ANY WRITE, so a refused push leaves the vault exactly as
|
|
248
246
|
* it was — header included:
|
|
249
|
-
* - a repo case that is deleted or archived in the
|
|
247
|
+
* - a repo case that is deleted or archived in the vault is refused unless
|
|
250
248
|
* `restore` (an explicit undelete/unarchive, then the repo's version is
|
|
251
249
|
* written as a new revision). `force` does NOT imply it: overwriting an
|
|
252
250
|
* edit and resurrecting a case someone removed are different decisions.
|
|
253
|
-
* - a case edited in the
|
|
251
|
+
* - a case edited in the vault since the pull, whose content differs from
|
|
254
252
|
* the repo's, is a conflict unless `force`.
|
|
255
253
|
* - `prune` deletes only cases the last pull wrote and the repo has since
|
|
256
|
-
* dropped; a case edited in the
|
|
257
|
-
* unless `force`. Cases added in the
|
|
254
|
+
* dropped; a case edited in the vault since that pull is a conflict
|
|
255
|
+
* unless `force`. Cases added in the vault after the pull are never
|
|
258
256
|
* pruned — the repo never knew them.
|
|
259
257
|
*/
|
|
260
258
|
export async function evalPush(opts) {
|
|
@@ -275,15 +273,15 @@ export async function evalPush(opts) {
|
|
|
275
273
|
const deleted = new Set(remoteHeader ? await listDeletedCaseIds(opts.store, wf, opts.dataset) : []);
|
|
276
274
|
const archived = new Set(remoteHeader ? await listArchivedCaseIds(opts.store, wf, opts.dataset) : []);
|
|
277
275
|
const pulled = local.state?.dataset === opts.dataset ? local.state.cases : {};
|
|
278
|
-
// 1. Repo cases the
|
|
276
|
+
// 1. Repo cases the vault's working set hides.
|
|
279
277
|
const hidden = local.cases.filter((c) => deleted.has(c.id) || archived.has(c.id));
|
|
280
278
|
if (hidden.length && !opts.restore) {
|
|
281
|
-
throw new Error(`[cef eval] these cases are ${hidden.length === 1 ? "" : "each "}deleted or archived in the
|
|
279
|
+
throw new Error(`[cef eval] these cases are ${hidden.length === 1 ? "" : "each "}deleted or archived in the vault, and still in the repo dir:\n `
|
|
282
280
|
+ hidden.map((c) => `${c.id} (${deleted.has(c.id) ? "deleted" : "archived"})`).join("\n ")
|
|
283
281
|
+ "\nNothing was written. Pull to drop them from the repo dir, give a case a new id to add it as a new case, "
|
|
284
282
|
+ "or push with --restore to undelete/unarchive them and write the repo's version.");
|
|
285
283
|
}
|
|
286
|
-
// 2. Edits in the
|
|
284
|
+
// 2. Edits in the vault since the pull.
|
|
287
285
|
const conflicts = [];
|
|
288
286
|
for (const c of local.cases) {
|
|
289
287
|
if (opts.force)
|
|
@@ -294,7 +292,7 @@ export async function evalPush(opts) {
|
|
|
294
292
|
const base = has(pulled, c.id) ? pulled[c.id] : undefined;
|
|
295
293
|
const editedRemotely = base === undefined || r.rev !== base;
|
|
296
294
|
if (editedRemotely && canonicalJson(r.case) !== canonicalJson(c))
|
|
297
|
-
conflicts.push(`${c.id} (
|
|
295
|
+
conflicts.push(`${c.id} (vault r${r.rev}, pulled ${base === undefined ? "never" : `r${base}`})`);
|
|
298
296
|
}
|
|
299
297
|
// 3. What prune would delete.
|
|
300
298
|
const localIds = new Set(local.cases.map((c) => c.id));
|
|
@@ -310,7 +308,7 @@ export async function evalPush(opts) {
|
|
|
310
308
|
}
|
|
311
309
|
missingLocally.push(id);
|
|
312
310
|
if (opts.prune && !opts.force && r.rev !== pulled[id]) {
|
|
313
|
-
pruneConflicts.push(`${id} (removed from the repo, but
|
|
311
|
+
pruneConflicts.push(`${id} (removed from the repo, but vault r${r.rev} was edited since the pull of r${pulled[id]})`);
|
|
314
312
|
}
|
|
315
313
|
}
|
|
316
314
|
if (conflicts.length || pruneConflicts.length) {
|
|
@@ -318,13 +316,13 @@ export async function evalPush(opts) {
|
|
|
318
316
|
"[cef eval] Nothing was written.",
|
|
319
317
|
...(conflicts.length
|
|
320
318
|
? [
|
|
321
|
-
`These cases changed in the
|
|
319
|
+
`These cases changed in the vault since they were pulled, and differ from the repo:\n ${conflicts.join("\n ")}`,
|
|
322
320
|
"Pull first and redo the edit, or push with --force to write the repo's version over them as a new revision.",
|
|
323
321
|
]
|
|
324
322
|
: []),
|
|
325
323
|
...(pruneConflicts.length
|
|
326
324
|
? [
|
|
327
|
-
`--prune would delete these cases, edited in the
|
|
325
|
+
`--prune would delete these cases, edited in the vault since they were pulled:\n ${pruneConflicts.join("\n ")}`,
|
|
328
326
|
"Pull to get the edited case back into the repo dir, or push with --prune --force to delete it, edit and all "
|
|
329
327
|
+ "(--restore brings a deleted case back).",
|
|
330
328
|
]
|
|
@@ -438,9 +436,6 @@ export async function evalCompare(opts) {
|
|
|
438
436
|
};
|
|
439
437
|
return compareExperiments(await load(opts.base), await load(opts.candidate));
|
|
440
438
|
}
|
|
441
|
-
export async function evalMigrate(opts) {
|
|
442
|
-
return migrateLegacyLayout(opts.store);
|
|
443
|
-
}
|
|
444
439
|
/**
|
|
445
440
|
* The Result a workflow version declares, read off its graph:
|
|
446
441
|
* - the live deployment row's `params.graph` when the version is the live
|
|
@@ -568,17 +563,6 @@ export function formatComparison(c) {
|
|
|
568
563
|
: "Every run unchanged.";
|
|
569
564
|
return [head, ...(diff ? [diff] : []), counts, "", metrics, "", runs].join("\n");
|
|
570
565
|
}
|
|
571
|
-
export function formatMigration(r) {
|
|
572
|
-
if (r.alreadyMigrated)
|
|
573
|
-
return "Already migrated (evals/MIGRATED.json is there); nothing to do.";
|
|
574
|
-
return [
|
|
575
|
-
`✓ ${r.datasets.length ? `Copied ${plural(r.datasets.length, "dataset")}` : "No legacy datasets"}: `
|
|
576
|
-
+ `${r.copied} keys written, ${r.existing} already in place`,
|
|
577
|
-
...r.datasets.map((d) => ` ${d.dataset} → workflows/${d.workflowId}/ (${d.keys} keys, ${plural(d.experiments, "experiment")})`),
|
|
578
|
-
...r.skipped.map((x) => ` skipped ${x.dataset}: ${x.reason}`),
|
|
579
|
-
...(r.skipped.length ? ["Not marked migrated: fix the skipped datasets and run it again."] : []),
|
|
580
|
-
].join("\n");
|
|
581
|
-
}
|
|
582
566
|
const int = (v, flag) => {
|
|
583
567
|
if (v === undefined)
|
|
584
568
|
return undefined;
|
|
@@ -599,14 +583,13 @@ const number = (v, flag) => {
|
|
|
599
583
|
function withStoreOptions(cmd) {
|
|
600
584
|
return cmd
|
|
601
585
|
.option("--env <env>", "Environment: dev | stage | prod (default dev; overrides $CEF_ENV)")
|
|
602
|
-
.option("--
|
|
603
|
-
.option("--keystore <file>", "Owner wallet keystore JSON, ed25519 ($CEF_KEYSTORE)")
|
|
586
|
+
.option("--keystore <file>", "Wallet keystore JSON, ed25519 ($CEF_KEYSTORE)")
|
|
604
587
|
.option("--password <pw>", "Keystore password ($CEF_KEYSTORE_PASSWORD)")
|
|
605
|
-
.option("--secret-phrase <phrase>", "
|
|
606
|
-
.option("--
|
|
607
|
-
.option("--
|
|
608
|
-
.option("--
|
|
609
|
-
.option("--
|
|
588
|
+
.option("--secret-phrase <phrase>", "Wallet secret phrase, ed25519 ($CEF_SECRET_PHRASE)")
|
|
589
|
+
.option("--access-token <token>", "CLI access token, for the vault without a wallet and for the orchestrator ($CEF_ACCESS_TOKEN)")
|
|
590
|
+
.option("--vault <id>", "Vault the workflow runs in ($CEF_VAULT_ID; default: the wallet's own)")
|
|
591
|
+
.option("--scope <scope>", "Vault scope the workflow runs in (default: its requiredScopes, else \"default\")")
|
|
592
|
+
.option("--vault-api <url>", "vault-api base URL (default from --env)")
|
|
610
593
|
.option("--json", "Print JSON instead of a table");
|
|
611
594
|
}
|
|
612
595
|
const WORKFLOW_HELP = "Workflow id (agent alias); default: the workflow cef.config.ts (or dist/) in this directory declares";
|
|
@@ -616,53 +599,58 @@ function withWorkflow(cmd) {
|
|
|
616
599
|
function withDir(cmd) {
|
|
617
600
|
return cmd.option("--dir <dir>", "Directory holding the workflow's datasets", DEFAULT_DATASETS_DIR);
|
|
618
601
|
}
|
|
619
|
-
async function openStore(o, wallet) {
|
|
620
|
-
return gatewayStore(await resolveGatewayAccess({ ...o, ...(o.gateway ? { endpoint: o.gateway } : {}) }, wallet));
|
|
621
|
-
}
|
|
622
602
|
/** The Node target: vault-api for events, the orchestrator for jobs and deployments. */
|
|
623
|
-
|
|
603
|
+
function openTarget(o, vault, scope, workflowId, fetchImpl) {
|
|
624
604
|
const env = envConfig(o.env);
|
|
625
605
|
const asPubKey = o.asPubkey ?? process.env.CEF_AS_PUBKEY;
|
|
626
606
|
if (!asPubKey)
|
|
627
607
|
throw new Error("[cef eval] --as-pubkey (or $CEF_AS_PUBKEY) is required");
|
|
628
608
|
const accessToken = o.accessToken ?? process.env.CEF_ACCESS_TOKEN;
|
|
629
|
-
const
|
|
630
|
-
|
|
631
|
-
: accessToken
|
|
632
|
-
? new DelegationAuthProvider(accessToken)
|
|
633
|
-
: undefined;
|
|
634
|
-
if (!auth)
|
|
635
|
-
throw new Error("[cef eval] run needs a wallet (--keystore/--secret-phrase) or --access-token to reach the vault");
|
|
636
|
-
const http = new VaultHttpClient({ url: o.vaultApi ?? env.vault, auth, ...(fetchImpl ? { fetch: fetchImpl } : {}) });
|
|
637
|
-
const vaultId = o.vault ?? process.env.CEF_VAULT_ID ?? (await new Vaults(http).current()).vaultId;
|
|
638
|
-
const events = new Events(http);
|
|
639
|
-
const browse = new Browse(http);
|
|
609
|
+
const events = new Events(vault.http);
|
|
610
|
+
const browse = new Browse(vault.http);
|
|
640
611
|
const orchestrator = fetchOrchestrator({
|
|
641
612
|
endpoint: o.orchestrator ?? process.env.CEF_ENDPOINT ?? env.orchestrator,
|
|
642
613
|
...(accessToken ? { accessToken } : {}),
|
|
643
614
|
...(fetchImpl ? { fetchImpl } : {}),
|
|
644
615
|
});
|
|
645
616
|
const target = nodeWorkflowTarget({
|
|
646
|
-
vaultId,
|
|
617
|
+
vaultId: vault.vaultId,
|
|
647
618
|
asPubKey,
|
|
648
619
|
workflowId,
|
|
649
620
|
orchestrator,
|
|
650
621
|
vault: {
|
|
651
622
|
async publish(ev) {
|
|
652
|
-
const res = await events.publish(vaultId,
|
|
623
|
+
const res = await events.publish(vault.vaultId, scope, ev);
|
|
653
624
|
const rejected = res.rejected ?? [];
|
|
654
625
|
if (rejected.length)
|
|
655
626
|
throw new Error(`the vault rejected ${ev.type}: ${rejected[0].reason}`);
|
|
656
627
|
},
|
|
657
|
-
events: (context, cursor) => browse.listStreamEvents(vaultId,
|
|
628
|
+
events: (context, cursor) => browse.listStreamEvents(vault.vaultId, scope, context, { limit: 200, ...(cursor ? { cursor } : {}) }),
|
|
658
629
|
},
|
|
659
630
|
});
|
|
660
|
-
return { target, orchestrator, agentId: workflowAgentId(asPubKey, workflowId)
|
|
631
|
+
return { target, orchestrator, agentId: workflowAgentId(asPubKey, workflowId) };
|
|
661
632
|
}
|
|
662
633
|
const print = (json, data, text) => console.log(json ? JSON.stringify(data, null, 2) : text());
|
|
663
634
|
export function addEvalCommands(program, opts) {
|
|
664
635
|
const local = () => ({ cwd: opts.cwd?.() ?? process.cwd() });
|
|
665
636
|
const workflowOf = (o) => resolveWorkflowId(o.workflow, local());
|
|
637
|
+
/**
|
|
638
|
+
* `--scope`, else the scope the workflow declares, else `default` — said on
|
|
639
|
+
* stderr, because a workflow that runs in another scope reads as an empty
|
|
640
|
+
* one there, with nothing else hinting why.
|
|
641
|
+
*/
|
|
642
|
+
const scopeOf = async (o, workflowId) => {
|
|
643
|
+
const given = o.scope?.trim();
|
|
644
|
+
if (given)
|
|
645
|
+
return given;
|
|
646
|
+
const declared = workflowId ? await localWorkflowScope(workflowId, local()) : undefined;
|
|
647
|
+
if (declared)
|
|
648
|
+
return declared;
|
|
649
|
+
console.error(`${workflowId ? `No scope declared for workflow ${workflowId} in this project` : "No workflow found in this project"}; `
|
|
650
|
+
+ `reading vault scope "${DEFAULT_WORKFLOW_SCOPE}". Pass --scope <name> if the workflow runs in another one.`);
|
|
651
|
+
return DEFAULT_WORKFLOW_SCOPE;
|
|
652
|
+
};
|
|
653
|
+
const openStore = async (o, workflowId) => vaultEvalStore(await openVault(o, undefined, opts.fetchImpl), await scopeOf(o, workflowId));
|
|
666
654
|
const ev = program
|
|
667
655
|
.command("eval")
|
|
668
656
|
.description("Workflow evaluations: datasets, experiments, comparisons");
|
|
@@ -670,7 +658,7 @@ export function addEvalCommands(program, opts) {
|
|
|
670
658
|
.action(async (o) => {
|
|
671
659
|
try {
|
|
672
660
|
const workflowId = await workflowOf(o).catch(() => undefined);
|
|
673
|
-
const store = await openStore(o);
|
|
661
|
+
const store = await openStore(o, workflowId);
|
|
674
662
|
const list = [];
|
|
675
663
|
for (const wf of workflowId ? [workflowId] : await listWorkflows(store))
|
|
676
664
|
list.push(...await listDatasets(store, wf));
|
|
@@ -700,7 +688,7 @@ export function addEvalCommands(program, opts) {
|
|
|
700
688
|
set("maxCost", number(o.maxCost, "--max-cost"));
|
|
701
689
|
const workflowId = await workflowOf(o);
|
|
702
690
|
const header = await evalCreate({
|
|
703
|
-
store: await openStore(o), dataset, workflowId,
|
|
691
|
+
store: await openStore(o, workflowId), dataset, workflowId,
|
|
704
692
|
...(o.description ? { description: o.description } : {}),
|
|
705
693
|
...(Object.keys(limits).length ? { limits } : {}),
|
|
706
694
|
});
|
|
@@ -715,7 +703,7 @@ export function addEvalCommands(program, opts) {
|
|
|
715
703
|
.action(async (dataset, o) => {
|
|
716
704
|
try {
|
|
717
705
|
const workflowId = await workflowOf(o);
|
|
718
|
-
const r = await evalPull({ store: await openStore(o), workflowId, dataset, dir: o.dir, force: !!o.force });
|
|
706
|
+
const r = await evalPull({ store: await openStore(o, workflowId), workflowId, dataset, dir: o.dir, force: !!o.force });
|
|
719
707
|
print(o.json, r, () => [
|
|
720
708
|
`✓ Pulled ${r.cases} case(s) into ${r.dir}`,
|
|
721
709
|
...(r.removed.length ? [` removed (no longer live): ${r.removed.join(", ")}`] : []),
|
|
@@ -727,16 +715,16 @@ export function addEvalCommands(program, opts) {
|
|
|
727
715
|
opts.onError(e);
|
|
728
716
|
}
|
|
729
717
|
});
|
|
730
|
-
withDir(withWorkflow(ev.command("push <dataset>").description("Write the repo dir's cases to the
|
|
718
|
+
withDir(withWorkflow(ev.command("push <dataset>").description("Write the repo dir's cases to the vault as new revisions")))
|
|
731
719
|
.option("--cut [note]", "Cut a new version after pushing (runs cut one automatically when the cases changed)")
|
|
732
|
-
.option("--force", "Write over cases that changed in the
|
|
720
|
+
.option("--force", "Write over cases that changed in the vault since the last pull")
|
|
733
721
|
.option("--prune", "Delete cases the last pull wrote and the repo dir has since removed")
|
|
734
|
-
.option("--restore", "Undelete/unarchive cases that are deleted or archived in the
|
|
722
|
+
.option("--restore", "Undelete/unarchive cases that are deleted or archived in the vault but present in the repo dir")
|
|
735
723
|
.action(async (dataset, o) => {
|
|
736
724
|
try {
|
|
737
725
|
const workflowId = await workflowOf(o);
|
|
738
726
|
const r = await evalPush({
|
|
739
|
-
store: await openStore(o), workflowId, dataset, dir: o.dir,
|
|
727
|
+
store: await openStore(o, workflowId), workflowId, dataset, dir: o.dir,
|
|
740
728
|
...(o.cut !== undefined ? { cut: o.cut } : {}), force: !!o.force, prune: !!o.prune, restore: !!o.restore,
|
|
741
729
|
});
|
|
742
730
|
print(o.json, r, () => [
|
|
@@ -745,8 +733,8 @@ export function addEvalCommands(program, opts) {
|
|
|
745
733
|
...r.updated.map((u) => ` ${u.id} → r${u.rev}`),
|
|
746
734
|
...(r.added.length ? [` added: ${r.added.join(", ")}`] : []),
|
|
747
735
|
...(r.restored.length ? [` restored (were deleted/archived): ${r.restored.join(", ")}`] : []),
|
|
748
|
-
...(r.missingLocally.length ? [` removed from the repo, still in the
|
|
749
|
-
...(r.notPulled.length ? [` added in the
|
|
736
|
+
...(r.missingLocally.length ? [` removed from the repo, still in the vault (kept; --prune deletes): ${r.missingLocally.join(", ")}`] : []),
|
|
737
|
+
...(r.notPulled.length ? [` added in the vault since the last pull (kept; pull to get them): ${r.notPulled.join(", ")}`] : []),
|
|
750
738
|
...(r.deleted.length ? [` deleted: ${r.deleted.join(", ")}`] : []),
|
|
751
739
|
r.cut ? `✓ Cut v${r.cut.version} (${r.cut.cases} cases)` : null,
|
|
752
740
|
].filter((l) => l !== null).join("\n"));
|
|
@@ -766,11 +754,8 @@ export function addEvalCommands(program, opts) {
|
|
|
766
754
|
.option("--baseline <exp>", "Experiment to compare against when done (default: the dataset's baseline)")
|
|
767
755
|
.option("--resume <exp>", "Resume an experiment that did not finish")
|
|
768
756
|
.option("--note <text>", "Note recorded on the experiment")
|
|
769
|
-
.option("--
|
|
770
|
-
.option("--scope <scope>", "Vault scope to publish into", "default")
|
|
757
|
+
.option("--as-pubkey <hex>", "Agent service pubkey the workflow belongs to ($CEF_AS_PUBKEY)")
|
|
771
758
|
.option("--orchestrator <url>", "Orchestrator base URL ($CEF_ENDPOINT; default from --env)")
|
|
772
|
-
.option("--vault-api <url>", "vault-api base URL (default from --env)")
|
|
773
|
-
.option("--access-token <token>", "CLI access token for the orchestrator ($CEF_ACCESS_TOKEN)")
|
|
774
759
|
.action(async (dataset, o) => {
|
|
775
760
|
try {
|
|
776
761
|
// Numbers first, so a typo fails before any credential is minted or run started.
|
|
@@ -782,11 +767,14 @@ export function addEvalCommands(program, opts) {
|
|
|
782
767
|
};
|
|
783
768
|
const workflowId = await workflowOf(o);
|
|
784
769
|
const wallet = await loadWallet(o);
|
|
785
|
-
const
|
|
770
|
+
const vault = await openVault(o, wallet, opts.fetchImpl);
|
|
771
|
+
// One scope for both: the experiment's records and the runs it starts.
|
|
772
|
+
const scope = await scopeOf(o, workflowId);
|
|
773
|
+
const store = vaultEvalStore(vault, scope);
|
|
786
774
|
const header = await getHeader(store, workflowId, dataset);
|
|
787
775
|
if (!header)
|
|
788
776
|
throw new Error(`[cef eval] workflow "${workflowId}" has no dataset "${dataset}"`);
|
|
789
|
-
const { target, orchestrator, agentId } =
|
|
777
|
+
const { target, orchestrator, agentId } = openTarget(o, vault, scope, workflowId, opts.fetchImpl);
|
|
790
778
|
const liveRow = (await getDeploymentSet(orchestrator, agentId))?.deployments
|
|
791
779
|
.find((d) => d.targeting === "" && d.enabled !== false);
|
|
792
780
|
const live = liveRow?.version ?? null;
|
|
@@ -827,7 +815,7 @@ export function addEvalCommands(program, opts) {
|
|
|
827
815
|
.action(async (dataset, o) => {
|
|
828
816
|
try {
|
|
829
817
|
const workflowId = await workflowOf(o);
|
|
830
|
-
const store = await openStore(o);
|
|
818
|
+
const store = await openStore(o, workflowId);
|
|
831
819
|
const list = await listExperiments(store, workflowId, dataset ? { dataset } : {});
|
|
832
820
|
const baselineId = dataset ? (await getHeader(store, workflowId, dataset))?.baselineId : undefined;
|
|
833
821
|
print(o.json, list, () => formatExperiments(list, baselineId));
|
|
@@ -836,25 +824,16 @@ export function addEvalCommands(program, opts) {
|
|
|
836
824
|
opts.onError(e);
|
|
837
825
|
}
|
|
838
826
|
});
|
|
839
|
-
|
|
827
|
+
withWorkflow(ev.command("compare <base> <candidate>").description("How one experiment differs from another"))
|
|
840
828
|
.action(async (base, candidate, o) => {
|
|
841
829
|
try {
|
|
842
|
-
const
|
|
830
|
+
const workflowId = await workflowOf(o).catch(() => undefined);
|
|
831
|
+
const c = await evalCompare({ store: await openStore(o, workflowId), base, candidate });
|
|
843
832
|
print(o.json, c, () => formatComparison(c));
|
|
844
833
|
}
|
|
845
834
|
catch (e) {
|
|
846
835
|
opts.onError(e);
|
|
847
836
|
}
|
|
848
837
|
});
|
|
849
|
-
withStoreOptions(ev.command("migrate").description("Copy v1 storage (evals/datasets/…) into the per-workflow layout"))
|
|
850
|
-
.action(async (o) => {
|
|
851
|
-
try {
|
|
852
|
-
const r = await evalMigrate({ store: await openStore(o) });
|
|
853
|
-
print(o.json, r, () => formatMigration(r));
|
|
854
|
-
}
|
|
855
|
-
catch (e) {
|
|
856
|
-
opts.onError(e);
|
|
857
|
-
}
|
|
858
|
-
});
|
|
859
838
|
}
|
|
860
839
|
//# sourceMappingURL=eval.js.map
|