@valerypopoff/rivet2-cli 2.1.9 → 2.3.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 CHANGED
@@ -10,11 +10,14 @@ npx @valerypopoff/rivet2-cli list my-project.rivet-project
10
10
  npx @valerypopoff/rivet2-cli inspect my-project.rivet-project
11
11
  npx @valerypopoff/rivet2-cli doctor my-project.rivet-project
12
12
  npx @valerypopoff/rivet2-cli run my-project.rivet-project
13
+ npx @valerypopoff/rivet2-cli evaluations run --project my-project.rivet-project --suite-file evaluation.json
13
14
  npx @valerypopoff/rivet2-cli serve my-project.rivet-project --port 8080
14
15
  npx @valerypopoff/rivet2-cli serve-app my-project.rivet-project "My web app"
15
16
  npx @valerypopoff/rivet2-cli completion
16
17
  ```
17
18
 
19
+ The Evaluations command consumes a **suite + dataset** bundle exported from Rivet's application-local Evaluations workspace. The project supplies its target and evaluator graphs; the bundle does not contain graph definitions, baselines, run history, or recordings. Use `--benchmark` for measurement without quality checks, `--json` for a complete `EvaluationRun`, or `--junit` for CI.
20
+
18
21
  The CLI package exposes the `rivet` binary when installed globally:
19
22
 
20
23
  ```bash
package/bin/cli.js CHANGED
@@ -7,7 +7,9 @@ import { doctor, makeDoctorCommand } from './commands/doctor.js';
7
7
  import { makeCommand as makeRunCommand, run } from './commands/run.js';
8
8
  import { makeCommand as makeServeCommand, serve } from './commands/serve.js';
9
9
  import { makeCommand as makeServeAppCommand, serveApp } from './commands/serveApp.js';
10
+ import { EvaluationCliError, makeEvaluationCommand, runEvaluation } from './commands/evaluations.js';
10
11
  await yargs(hideBin(process.argv))
12
+ .command('evaluations run', 'Run a named Evaluation suite.', (y) => makeEvaluationCommand(y), (args) => runCli(() => runEvaluation(args)))
11
13
  .command('list <projectFile>', 'List graphs, web apps, library nodes, and plugins in a project file.', (y) => makeListCommand(y), (args) => runCli(() => list(args)))
12
14
  .command('inspect <projectFile>', 'Print a machine-readable project summary.', (y) => makeInspectCommand(y), (args) => runCli(() => inspect(args.projectFile)))
13
15
  .command('doctor <projectFile>', 'Check a project file for common CLI/runtime problems.', (y) => makeDoctorCommand(y), (args) => runCli(() => doctor(args)))
@@ -23,7 +25,7 @@ async function runCli(action) {
23
25
  }
24
26
  catch (err) {
25
27
  console.error(chalk.red(formatCliError(err)));
26
- process.exit(1);
28
+ process.exit(err instanceof EvaluationCliError ? err.exitCode : 1);
27
29
  }
28
30
  }
29
31
  function formatCliError(err) {
@@ -0,0 +1,350 @@
1
+ import { createProcessor, loadProjectAndAttachedDataFromFile, } from '@valerypopoff/rivet2-node';
2
+ import { deserializeEvaluationProjectData, deserializeEvaluationSuiteBundleJson, EvaluationGraphExecutionError, runEvaluationSuite, validateEvaluationDataset, } from '@valerypopoff/rivet2-evaluations';
3
+ import { readFile } from 'node:fs/promises';
4
+ import { resolve } from 'node:path';
5
+ import { addDatasetOptions, addProviderOptions, createDatasetProvider, getProjectFile, resolveDatasetFilePath, withCliProcessorOptions, } from '../cliRuntime.js';
6
+ export class EvaluationCliError extends Error {
7
+ exitCode;
8
+ constructor(message, exitCode) {
9
+ super(message);
10
+ this.exitCode = exitCode;
11
+ }
12
+ }
13
+ export function makeEvaluationCommand(y) {
14
+ return addDatasetOptions(addProviderOptions(y))
15
+ .option('project', {
16
+ demandOption: true,
17
+ describe: 'The currently runnable Rivet project containing the target and evaluator graphs',
18
+ type: 'string',
19
+ })
20
+ .option('suite', {
21
+ describe: 'Evaluation suite ID or name in a legacy project attachment; omit when using --suite-file',
22
+ type: 'string',
23
+ })
24
+ .option('suite-file', {
25
+ describe: 'An Export suite + dataset JSON bundle from the Evaluations workspace',
26
+ type: 'string',
27
+ })
28
+ .option('trials', { describe: 'Override the suite trial count', type: 'number' })
29
+ .option('concurrency', { describe: 'Override the suite worker-pool concurrency (1–32)', type: 'number' })
30
+ .option('baseline', { describe: 'Use this saved baseline ID instead of the suite default', type: 'string' })
31
+ .option('benchmark', {
32
+ describe: 'Measure execution without requiring or applying output-quality checks',
33
+ type: 'boolean',
34
+ default: false,
35
+ })
36
+ .option('json', { describe: 'Write the complete evaluation run as JSON', type: 'boolean', default: false })
37
+ .option('junit', { describe: 'Write a JUnit XML report', type: 'boolean', default: false });
38
+ }
39
+ function findSuite(data, input) {
40
+ return data.suites.find((suite) => suite.id === input || suite.name === input);
41
+ }
42
+ async function loadEvaluationSuiteBundle(path) {
43
+ let source;
44
+ const resolvedPath = resolve(process.cwd(), path);
45
+ try {
46
+ source = await readFile(resolvedPath, 'utf8');
47
+ }
48
+ catch (error) {
49
+ throw new EvaluationCliError(`Could not read evaluation suite bundle "${resolvedPath}": ${error instanceof Error ? error.message : String(error)}`, 3);
50
+ }
51
+ try {
52
+ const bundle = deserializeEvaluationSuiteBundleJson(source, {
53
+ suiteId: 'cli-imported-suite',
54
+ datasetId: 'cli-imported-dataset',
55
+ });
56
+ return { data: { version: 1, suites: [bundle.suite], baselines: [] }, dataset: bundle.dataset };
57
+ }
58
+ catch (error) {
59
+ throw new EvaluationCliError(`Could not import evaluation suite bundle "${resolvedPath}": ${error instanceof Error ? error.message : String(error)}`, 3);
60
+ }
61
+ }
62
+ async function loadEvaluationDataset(projectPath, projectId, datasetId, datasetFile) {
63
+ const path = resolveDatasetFilePath(projectPath, datasetFile);
64
+ let content;
65
+ try {
66
+ content = await readFile(path, 'utf8');
67
+ }
68
+ catch (error) {
69
+ throw new EvaluationCliError(`Could not read evaluation dataset file "${path}": ${error instanceof Error ? error.message : String(error)}`, 3);
70
+ }
71
+ let parsed;
72
+ try {
73
+ parsed = JSON.parse(content);
74
+ }
75
+ catch {
76
+ throw new EvaluationCliError(`Evaluation dataset file "${path}" is not valid JSON.`, 3);
77
+ }
78
+ const dataset = Array.isArray(parsed.evaluationDatasets)
79
+ ? parsed.evaluationDatasets.find((candidate) => candidate.id === datasetId && candidate.projectId === projectId)
80
+ : undefined;
81
+ if (!dataset)
82
+ throw new EvaluationCliError(`Evaluation dataset "${datasetId}" was not found in "${path}".`, 3);
83
+ return validateEvaluationDataset(dataset);
84
+ }
85
+ function graphInputsToOutputs(project, graphId, inputs) {
86
+ const graph = project.graphs[graphId];
87
+ if (!graph)
88
+ throw new Error(`Evaluation graph "${graphId}" does not exist.`);
89
+ const graphInputs = new Map(graph.nodes
90
+ .filter((node) => node.type === 'graphInput')
91
+ .map((node) => [node.data.id, node]));
92
+ return Object.fromEntries(Object.entries(inputs).map(([id, value]) => {
93
+ const input = graphInputs.get(id);
94
+ if (!input)
95
+ throw new Error(`Evaluation supplied unknown graph input "${id}".`);
96
+ return [id, { type: input.data.dataType, value }];
97
+ }));
98
+ }
99
+ export function formatEvaluationRunAsJUnit(run) {
100
+ const escape = (value) => value.replace(/[<>&"']/gu, (character) => ({ '<': '&lt;', '>': '&gt;', '&': '&amp;', '"': '&quot;', "'": '&apos;' })[character]);
101
+ const cases = run.trials.map((trial) => {
102
+ const observationDetails = trial.observations
103
+ .filter((observation) => observation.status !== 'passed')
104
+ .map((observation) => observation.message ?? observation.name)
105
+ .join('; ');
106
+ const details = trial.error ?? (observationDetails || trial.qualityReason.message);
107
+ const body = trial.executionStatus === 'error'
108
+ ? `<error message="${escape(details || 'Evaluation execution error')}" />`
109
+ : trial.executionStatus === 'canceled'
110
+ ? `<skipped message="Evaluation execution was canceled." />`
111
+ : trial.qualityStatus === 'unable-to-evaluate'
112
+ ? `<error message="${escape(details || 'Evaluation infrastructure error')}" />`
113
+ : trial.qualityStatus === 'failed'
114
+ ? `<failure message="${escape(details || 'Evaluation failed')}" />`
115
+ : trial.qualityStatus === 'not-evaluated'
116
+ ? `<skipped message="${escape(trial.qualityReason.message)}" />`
117
+ : '';
118
+ return {
119
+ xml: `<testcase name="${escape(trial.caseName)} [trial ${trial.trialIndex + 1}]" time="${(trial.totalMetrics.durationMs / 1000).toFixed(3)}">${body}</testcase>`,
120
+ outcome: trial.executionStatus === 'error' ||
121
+ (trial.executionStatus === 'completed' && trial.qualityStatus === 'unable-to-evaluate')
122
+ ? 'error'
123
+ : trial.executionStatus === 'canceled' || trial.qualityStatus === 'not-evaluated'
124
+ ? 'skipped'
125
+ : trial.qualityStatus === 'failed'
126
+ ? 'failure'
127
+ : 'passed',
128
+ };
129
+ });
130
+ const representedRunQuality = cases.some((item) => run.qualityStatus === 'failed'
131
+ ? item.outcome === 'failure'
132
+ : run.qualityStatus === 'unable-to-evaluate'
133
+ ? item.outcome === 'error'
134
+ : false);
135
+ const representedRunExecution = cases.some((item) => run.executionStatus === 'error'
136
+ ? item.outcome === 'error'
137
+ : run.executionStatus === 'canceled'
138
+ ? item.outcome === 'skipped'
139
+ : false);
140
+ if (run.executionStatus !== 'completed' && !representedRunExecution) {
141
+ const details = run.qualityReason.message;
142
+ cases.push(run.executionStatus === 'canceled'
143
+ ? {
144
+ xml: `<testcase name="Evaluation execution"><skipped message="${escape(details)}" /></testcase>`,
145
+ outcome: 'skipped',
146
+ }
147
+ : {
148
+ xml: `<testcase name="Evaluation execution"><error message="${escape(details)}" /></testcase>`,
149
+ outcome: 'error',
150
+ });
151
+ }
152
+ else if (run.executionStatus === 'completed' &&
153
+ !representedRunQuality &&
154
+ !cases.some((item) => item.outcome === 'error')) {
155
+ const thresholdDetails = run.thresholdResults
156
+ .filter((result) => result.status !== 'passed')
157
+ .map((result) => result.message)
158
+ .join('; ');
159
+ const details = thresholdDetails || run.qualityReason.message;
160
+ if (run.qualityStatus === 'failed') {
161
+ cases.push({
162
+ xml: `<testcase name="Evaluation aggregate requirements"><failure message="${escape(details)}" /></testcase>`,
163
+ outcome: 'failure',
164
+ });
165
+ }
166
+ else if (run.qualityStatus === 'unable-to-evaluate' ||
167
+ (run.purpose === 'evaluation' && run.qualityStatus === 'not-evaluated')) {
168
+ cases.push({
169
+ xml: `<testcase name="Evaluation aggregate requirements"><error message="${escape(details)}" /></testcase>`,
170
+ outcome: 'error',
171
+ });
172
+ }
173
+ }
174
+ const failures = cases.filter((item) => item.outcome === 'failure').length;
175
+ const errors = cases.filter((item) => item.outcome === 'error').length;
176
+ const skipped = cases.filter((item) => item.outcome === 'skipped').length;
177
+ return `<?xml version="1.0" encoding="UTF-8"?><testsuite name="${escape(run.suiteName)}" tests="${cases.length}" failures="${failures}" errors="${errors}" skipped="${skipped}">${cases.map((item) => item.xml).join('')}</testsuite>`;
178
+ }
179
+ export function evaluationRunFailure(run) {
180
+ if (run.executionStatus !== 'completed') {
181
+ return new EvaluationCliError(`Evaluation execution ${run.executionStatus}: ${run.qualityReason.message}`, 3);
182
+ }
183
+ const erroredTrials = run.aggregate?.erroredTrialCount ?? run.trials.filter((trial) => trial.executionStatus === 'error').length;
184
+ const canceledTrials = run.aggregate?.canceledTrialCount ?? run.trials.filter((trial) => trial.executionStatus === 'canceled').length;
185
+ if (erroredTrials > 0 || canceledTrials > 0) {
186
+ return new EvaluationCliError(`Evaluation execution completed with ${erroredTrials} errored and ${canceledTrials} canceled trials.`, 3);
187
+ }
188
+ if (run.qualityStatus === 'failed') {
189
+ return new EvaluationCliError('Evaluation thresholds or required checks failed.', 2);
190
+ }
191
+ if (run.qualityStatus === 'unable-to-evaluate') {
192
+ return new EvaluationCliError(run.qualityReason.message, 3);
193
+ }
194
+ if (run.purpose === 'evaluation' && run.qualityStatus === 'not-evaluated') {
195
+ return new EvaluationCliError(run.qualityReason.message, 3);
196
+ }
197
+ return undefined;
198
+ }
199
+ export async function runEvaluation(args) {
200
+ if (!args.project || (!args.suite && !args.suiteFile)) {
201
+ throw new EvaluationCliError('Provide --project and either --suite-file or --suite for a legacy project attachment.', 3);
202
+ }
203
+ const projectPath = await getProjectFile(args.project);
204
+ const [project, attachedData] = await loadProjectAndAttachedDataFromFile(projectPath);
205
+ const importedBundle = args.suiteFile === undefined ? undefined : await loadEvaluationSuiteBundle(args.suiteFile);
206
+ const rawData = attachedData.evaluations;
207
+ if (!importedBundle && !rawData) {
208
+ throw new EvaluationCliError('This project has no legacy Evaluation definitions. Export the suite + dataset from Rivet and pass it with --suite-file.', 3);
209
+ }
210
+ const evaluationData = importedBundle?.data ?? deserializeEvaluationProjectData(rawData);
211
+ const suite = importedBundle?.data.suites[0] ?? findSuite(evaluationData, args.suite);
212
+ if (!suite)
213
+ throw new EvaluationCliError(`Evaluation suite "${args.suite}" was not found.`, 3);
214
+ if (args.baseline !== undefined && importedBundle) {
215
+ throw new EvaluationCliError('Exported suite bundles do not include baselines; omit --baseline or use a legacy project attachment.', 3);
216
+ }
217
+ const dataset = importedBundle?.dataset ?? (await loadEvaluationDataset(projectPath, project.metadata.id, suite.datasetId, args.datasetFile));
218
+ const datasetProvider = await createDatasetProvider(projectPath, args);
219
+ const effectiveData = args.trials === undefined && args.concurrency === undefined
220
+ ? evaluationData
221
+ : {
222
+ ...evaluationData,
223
+ suites: evaluationData.suites.map((candidate) => candidate.id === suite.id
224
+ ? {
225
+ ...candidate,
226
+ configuration: {
227
+ ...candidate.configuration,
228
+ ...(args.trials === undefined ? {} : { trialCount: args.trials }),
229
+ ...(args.concurrency === undefined ? {} : { concurrency: args.concurrency }),
230
+ },
231
+ }
232
+ : candidate),
233
+ };
234
+ const actualSuite = effectiveData.suites.find((candidate) => candidate.id === suite.id);
235
+ const selectedBaseline = args.baseline === undefined
236
+ ? undefined
237
+ : effectiveData.baselines.find((candidate) => candidate.id === args.baseline && candidate.suiteId === actualSuite.id);
238
+ if (args.baseline !== undefined && !selectedBaseline) {
239
+ throw new EvaluationCliError(`Baseline "${args.baseline}" was not found for evaluation suite "${actualSuite.name}".`, 3);
240
+ }
241
+ const run = await runEvaluationSuite({
242
+ project,
243
+ evaluationData: effectiveData,
244
+ dataset,
245
+ suiteId: actualSuite.id,
246
+ purpose: args.benchmark ? 'execution-benchmark' : 'evaluation',
247
+ ...(selectedBaseline === undefined ? {} : { baseline: selectedBaseline }),
248
+ executionMode: 'node-cli',
249
+ runGraph: async ({ graphId, inputs, signal, metadata }) => {
250
+ const startedAt = Date.now();
251
+ const metrics = {
252
+ durationMs: 0,
253
+ modelCallCount: 0,
254
+ toolCallCount: 0,
255
+ toolFailureCount: 0,
256
+ };
257
+ const providerAttempts = [];
258
+ const processorInfo = createProcessor(project, withCliProcessorOptions({ datasetProvider, projectPath }, args, {
259
+ graph: graphId,
260
+ inputs: graphInputsToOutputs(project, graphId, inputs),
261
+ abortSignal: signal,
262
+ evaluation: metadata,
263
+ }));
264
+ processorInfo.processor.on('llmCallFinished', (event) => {
265
+ metrics.modelCallCount = (metrics.modelCallCount ?? 0) + 1;
266
+ metrics.inputTokens = (metrics.inputTokens ?? 0) + (event.normalizedUsage?.promptTokens ?? 0);
267
+ metrics.outputTokens = (metrics.outputTokens ?? 0) + (event.normalizedUsage?.completionTokens ?? 0);
268
+ metrics.cachedInputTokens = (metrics.cachedInputTokens ?? 0) + (event.normalizedUsage?.cachedTokens ?? 0);
269
+ metrics.reasoningTokens = (metrics.reasoningTokens ?? 0) + (event.normalizedUsage?.reasoningTokens ?? 0);
270
+ if (event.pricing.status === 'known')
271
+ metrics.costUsd = (metrics.costUsd ?? 0) + (event.pricing.costUsd ?? 0);
272
+ else
273
+ metrics.hasUnknownCost = true;
274
+ providerAttempts.push({
275
+ kind: 'provider-call',
276
+ provider: event.provider,
277
+ model: event.model,
278
+ customProviderApi: event.customProviderApi ?? null,
279
+ outcome: event.outcome,
280
+ profileIndex: event.profileIndex ?? null,
281
+ attemptIndex: event.attemptIndex,
282
+ roundIndex: event.roundIndex ?? null,
283
+ durationMs: event.durationMs ?? null,
284
+ });
285
+ });
286
+ processorInfo.processor.on('llmProfileAttempt', (event) => {
287
+ providerAttempts.push({
288
+ kind: 'profile-decision',
289
+ provider: event.provider,
290
+ model: event.model,
291
+ customProviderApi: event.customProviderApi ?? null,
292
+ stage: event.stage,
293
+ outcome: event.outcome,
294
+ profileIndex: event.profileIndex ?? null,
295
+ attemptIndex: event.attemptIndex ?? null,
296
+ roundIndex: event.roundIndex,
297
+ status: event.status ?? null,
298
+ healthState: event.healthState ?? null,
299
+ healthDisposition: event.healthDisposition ?? null,
300
+ timeoutKind: event.timeoutKind ?? null,
301
+ });
302
+ });
303
+ processorInfo.processor.on('toolCallFinished', (event) => {
304
+ metrics.toolCallCount = (metrics.toolCallCount ?? 0) + 1;
305
+ if (event.outcome !== 'success')
306
+ metrics.toolFailureCount = (metrics.toolFailureCount ?? 0) + 1;
307
+ });
308
+ try {
309
+ const outputs = await processorInfo.run();
310
+ metrics.durationMs = Date.now() - startedAt;
311
+ return {
312
+ outputs: Object.fromEntries(Object.entries(outputs).map(([key, value]) => [key, value.value])),
313
+ metrics,
314
+ ...(providerAttempts.length === 0 ? {} : { providerAttempts }),
315
+ };
316
+ }
317
+ catch (error) {
318
+ metrics.durationMs = Math.max(metrics.durationMs, Date.now() - startedAt);
319
+ throw new EvaluationGraphExecutionError(error instanceof Error ? error.message : String(error), {
320
+ metrics,
321
+ ...(providerAttempts.length === 0 ? {} : { providerAttempts }),
322
+ });
323
+ }
324
+ finally {
325
+ processorInfo.dispose();
326
+ }
327
+ },
328
+ });
329
+ if (args.junit)
330
+ process.stdout.write(`${formatEvaluationRunAsJUnit(run)}\n`);
331
+ else if (args.json)
332
+ process.stdout.write(`${JSON.stringify(run, null, 2)}\n`);
333
+ else {
334
+ const measured = run.purpose === 'execution-benchmark'
335
+ ? `${run.aggregate?.trialCount ?? 0} trials measured`
336
+ : run.evaluationMode === 'scoring'
337
+ ? `${formatScore(run.aggregate?.meanScore)}; ${run.aggregate?.scoredTrialCount ?? 0}/${run.aggregate?.trialCount ?? 0} trials scored`
338
+ : `${run.aggregate?.passedTrialCount ?? 0}/${run.aggregate?.evaluatedTrialCount ?? 0} evaluated trials passed`;
339
+ process.stdout.write(`${run.suiteName}: ${run.qualityStatus} (${run.executionStatus}; ${measured})\n`);
340
+ }
341
+ const failure = evaluationRunFailure(run);
342
+ if (failure)
343
+ throw failure;
344
+ }
345
+ function formatScore(score) {
346
+ if (score === undefined)
347
+ return 'score unavailable';
348
+ const outOfHundred = score * 100;
349
+ return `${outOfHundred.toFixed(outOfHundred === Math.round(outOfHundred) ? 0 : 1)}/100`;
350
+ }
@@ -0,0 +1,41 @@
1
+ import { runEvaluationSuite, type EvaluationRun } from '@valerypopoff/rivet2-evaluations';
2
+ import type * as yargs from 'yargs';
3
+ import { type DatasetCliOptions, type ProviderCliOptions } from '../cliRuntime.js';
4
+ export declare class EvaluationCliError extends Error {
5
+ readonly exitCode: number;
6
+ constructor(message: string, exitCode: number);
7
+ }
8
+ type EvaluationRunArgs = {
9
+ project?: string;
10
+ suite?: string;
11
+ suiteFile?: string;
12
+ trials?: number;
13
+ concurrency?: number;
14
+ baseline?: string;
15
+ json?: boolean;
16
+ junit?: boolean;
17
+ benchmark?: boolean;
18
+ } & DatasetCliOptions & ProviderCliOptions;
19
+ export declare function makeEvaluationCommand<T>(y: yargs.Argv<T>): yargs.Argv<T & {
20
+ project: string;
21
+ } & {
22
+ suite: string | undefined;
23
+ } & {
24
+ "suite-file": string | undefined;
25
+ } & {
26
+ trials: number | undefined;
27
+ } & {
28
+ concurrency: number | undefined;
29
+ } & {
30
+ baseline: string | undefined;
31
+ } & {
32
+ benchmark: boolean;
33
+ } & {
34
+ json: boolean;
35
+ } & {
36
+ junit: boolean;
37
+ }>;
38
+ export declare function formatEvaluationRunAsJUnit(run: Awaited<ReturnType<typeof runEvaluationSuite>>): string;
39
+ export declare function evaluationRunFailure(run: Pick<EvaluationRun, 'purpose' | 'executionStatus' | 'qualityStatus' | 'qualityReason' | 'aggregate' | 'trials'>): EvaluationCliError | undefined;
40
+ export declare function runEvaluation(args: EvaluationRunArgs): Promise<void>;
41
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@valerypopoff/rivet2-cli",
3
- "version": "2.1.9",
3
+ "version": "2.3.0",
4
4
  "license": "MIT",
5
5
  "types": "dist/types/cli.d.ts",
6
6
  "type": "module",
@@ -23,7 +23,8 @@
23
23
  },
24
24
  "dependencies": {
25
25
  "@hono/node-server": "^1.19.14",
26
- "@valerypopoff/rivet2-node": "^2.1.9",
26
+ "@valerypopoff/rivet2-evaluations": "^2.3.0",
27
+ "@valerypopoff/rivet2-node": "^2.3.0",
27
28
  "chalk": "^5.4.1",
28
29
  "didyoumean2": "^7.0.4",
29
30
  "dotenv": "^16.4.7",