@outputai/cli 0.2.1-next.f1502fb.0 → 0.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.
Files changed (45) hide show
  1. package/bin/run.js +2 -2
  2. package/dist/api/generated/api.d.ts +21 -5
  3. package/dist/api/generated/api.js +1 -1
  4. package/dist/api/http_client.js +24 -19
  5. package/dist/assets/docker/docker-compose-dev.yml +5 -9
  6. package/dist/commands/dev/index.js +12 -1
  7. package/dist/commands/init.d.ts +1 -0
  8. package/dist/commands/init.js +5 -1
  9. package/dist/commands/init.spec.js +10 -5
  10. package/dist/commands/workflow/run.d.ts +1 -1
  11. package/dist/commands/workflow/run.js +8 -5
  12. package/dist/commands/workflow/run.spec.js +3 -3
  13. package/dist/commands/workflow/runs/list.d.ts +1 -0
  14. package/dist/commands/workflow/runs/list.js +7 -0
  15. package/dist/commands/workflow/start.d.ts +1 -1
  16. package/dist/commands/workflow/start.js +8 -5
  17. package/dist/commands/workflow/start.spec.js +1 -1
  18. package/dist/config.d.ts +11 -38
  19. package/dist/config.js +34 -42
  20. package/dist/config.spec.d.ts +1 -0
  21. package/dist/config.spec.js +129 -0
  22. package/dist/generated/framework_version.json +1 -1
  23. package/dist/hooks/init.d.ts +4 -0
  24. package/dist/hooks/init.js +14 -2
  25. package/dist/hooks/init.spec.js +79 -5
  26. package/dist/services/docker.js +5 -2
  27. package/dist/services/docker.spec.js +74 -3
  28. package/dist/services/messages.js +2 -1
  29. package/dist/services/project_scaffold.d.ts +1 -1
  30. package/dist/services/project_scaffold.js +16 -1
  31. package/dist/services/workflow_runs.d.ts +1 -0
  32. package/dist/services/workflow_runs.js +3 -0
  33. package/dist/templates/project/.env.example.template +17 -0
  34. package/dist/utils/credentials_loader.d.ts +1 -0
  35. package/dist/utils/credentials_loader.js +18 -0
  36. package/dist/utils/credentials_loader.spec.d.ts +1 -0
  37. package/dist/utils/credentials_loader.spec.js +84 -0
  38. package/dist/utils/env_loader.js +1 -2
  39. package/dist/utils/error_handler.js +10 -8
  40. package/dist/utils/validation.d.ts +13 -0
  41. package/dist/utils/validation.js +31 -0
  42. package/dist/utils/validation.spec.js +47 -1
  43. package/dist/views/dev.js +3 -3
  44. package/dist/views/workflow/list.js +10 -8
  45. package/package.json +10 -10
@@ -1,11 +1,13 @@
1
1
  import { config } from '#config.js';
2
- const DEFAULT_MESSAGES = {
3
- ECONNREFUSED: `Connection refused to ${config.apiUrl}. Is the API server running?`,
4
- 401: 'Authentication failed. Check your OUTPUT_API_AUTH_TOKEN.',
5
- 404: 'Resource not found.',
6
- 500: 'Server error.',
7
- UNKNOWN: 'An unknown error occurred.'
8
- };
2
+ function getDefaultMessages() {
3
+ return {
4
+ ECONNREFUSED: `Connection refused to ${config.apiUrl}. Is the API server running?`,
5
+ 401: 'Authentication failed. Check your OUTPUT_API_AUTH_TOKEN.',
6
+ 404: 'Resource not found.',
7
+ 500: 'Server error.',
8
+ UNKNOWN: 'An unknown error occurred.'
9
+ };
10
+ }
9
11
  /**
10
12
  * Extract error type and message from API response data
11
13
  */
@@ -49,7 +51,7 @@ function getDetailedErrorMessage(error) {
49
51
  }
50
52
  export function handleApiError(error, errorFn, overrides = {}) {
51
53
  const apiError = error;
52
- const errorMessages = { ...DEFAULT_MESSAGES, ...overrides };
54
+ const errorMessages = { ...getDefaultMessages(), ...overrides };
53
55
  if (apiError.code === 'ECONNREFUSED' || apiError.cause?.code === 'ECONNREFUSED') {
54
56
  errorFn(errorMessages.ECONNREFUSED, { exit: 1 });
55
57
  }
@@ -11,3 +11,16 @@ export declare function validateWorkflowName(name: string): void;
11
11
  * Validate that a directory path is safe to create
12
12
  */
13
13
  export declare function validateOutputDirectory(outputDir: string): void;
14
+ export declare class InvalidPortError extends Error {
15
+ constructor(envVarName: string, raw: string, reason: string);
16
+ }
17
+ /**
18
+ * Parse a port number from an env var. Empty string and undefined fall back to
19
+ * the default silently (matching Compose's `${VAR:-default}` semantics).
20
+ * Throws InvalidPortError on anything else: non-numeric, signed, decimal,
21
+ * trailing junk (e.g. "3001abc"), or out of range 1-65535. Throwing (vs
22
+ * warn-and-fallback) prevents CLI/Docker disagreement: Compose reads the same
23
+ * env var via `${VAR:-default}` and uses its own parser, so a CLI fallback
24
+ * would silently desync from the bound port.
25
+ */
26
+ export declare function parsePort(raw: string | undefined, defaultPort: number, envVarName: string): number;
@@ -23,3 +23,34 @@ export function validateOutputDirectory(outputDir) {
23
23
  throw new InvalidOutputDirectoryError(outputDir, 'Output directory cannot be empty');
24
24
  }
25
25
  }
26
+ const MIN_PORT = 1;
27
+ const MAX_PORT = 65535;
28
+ export class InvalidPortError extends Error {
29
+ constructor(envVarName, raw, reason) {
30
+ super(`${envVarName}=${raw} is invalid (${reason}). ` +
31
+ `Set a port in ${MIN_PORT}-${MAX_PORT} in your .env file, or unset the variable to use the default.`);
32
+ this.name = 'InvalidPortError';
33
+ }
34
+ }
35
+ /**
36
+ * Parse a port number from an env var. Empty string and undefined fall back to
37
+ * the default silently (matching Compose's `${VAR:-default}` semantics).
38
+ * Throws InvalidPortError on anything else: non-numeric, signed, decimal,
39
+ * trailing junk (e.g. "3001abc"), or out of range 1-65535. Throwing (vs
40
+ * warn-and-fallback) prevents CLI/Docker disagreement: Compose reads the same
41
+ * env var via `${VAR:-default}` and uses its own parser, so a CLI fallback
42
+ * would silently desync from the bound port.
43
+ */
44
+ export function parsePort(raw, defaultPort, envVarName) {
45
+ if (raw === undefined || raw === '') {
46
+ return defaultPort;
47
+ }
48
+ if (!/^\d+$/.test(raw)) {
49
+ throw new InvalidPortError(envVarName, raw, 'not a positive integer');
50
+ }
51
+ const n = parseInt(raw, 10);
52
+ if (n < MIN_PORT || n > MAX_PORT) {
53
+ throw new InvalidPortError(envVarName, raw, `out of range ${MIN_PORT}-${MAX_PORT}`);
54
+ }
55
+ return n;
56
+ }
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, it } from 'vitest';
2
- import { isValidWorkflowName } from './validation.js';
2
+ import { isValidWorkflowName, parsePort, InvalidPortError } from './validation.js';
3
3
  describe('isValidWorkflowName', () => {
4
4
  describe('valid workflow names', () => {
5
5
  it('should accept single letter', () => {
@@ -138,3 +138,49 @@ describe('isValidWorkflowName', () => {
138
138
  });
139
139
  });
140
140
  });
141
+ describe('parsePort', () => {
142
+ it('returns the parsed value for a valid port', () => {
143
+ expect(parsePort('3001', 3001, 'P')).toBe(3001);
144
+ expect(parsePort('7234', 7233, 'P')).toBe(7234);
145
+ });
146
+ it('falls back to default for undefined silently', () => {
147
+ expect(parsePort(undefined, 3001, 'P')).toBe(3001);
148
+ });
149
+ it('falls back to default for empty string silently (matches Compose ${VAR:-default})', () => {
150
+ expect(parsePort('', 3001, 'P')).toBe(3001);
151
+ });
152
+ it('throws InvalidPortError for non-numeric input', () => {
153
+ expect(() => parsePort('abc', 3001, 'OUTPUT_API_HOST_PORT')).toThrow(InvalidPortError);
154
+ expect(() => parsePort('abc', 3001, 'OUTPUT_API_HOST_PORT'))
155
+ .toThrow(/OUTPUT_API_HOST_PORT=abc/);
156
+ });
157
+ it('throws for trailing junk (parseInt would silently truncate)', () => {
158
+ expect(() => parsePort('3001abc', 3001, 'P')).toThrow(InvalidPortError);
159
+ });
160
+ it('throws for negative input', () => {
161
+ expect(() => parsePort('-1', 3001, 'P')).toThrow(InvalidPortError);
162
+ });
163
+ it('throws for port 0 (CLI rejects but Compose would treat as ephemeral - prevents desync)', () => {
164
+ expect(() => parsePort('0', 3001, 'P')).toThrow(InvalidPortError);
165
+ });
166
+ it('throws for port above 65535', () => {
167
+ expect(() => parsePort('65536', 3001, 'P')).toThrow(InvalidPortError);
168
+ expect(() => parsePort('99999', 3001, 'P')).toThrow(InvalidPortError);
169
+ });
170
+ it('accepts boundary ports 1 and 65535', () => {
171
+ expect(parsePort('1', 3001, 'P')).toBe(1);
172
+ expect(parsePort('65535', 3001, 'P')).toBe(65535);
173
+ });
174
+ it('error message includes env var name and remediation hint', () => {
175
+ try {
176
+ parsePort('abc', 3001, 'OUTPUT_API_HOST_PORT');
177
+ expect.fail('expected throw');
178
+ }
179
+ catch (err) {
180
+ expect(err).toBeInstanceOf(InvalidPortError);
181
+ expect(err.message).toContain('OUTPUT_API_HOST_PORT=abc');
182
+ expect(err.message).toContain('1-65535');
183
+ expect(err.message).toContain('.env file');
184
+ }
185
+ });
186
+ });
package/dist/views/dev.js CHANGED
@@ -138,10 +138,10 @@ const DevSuccessMessage = ({ services }) => {
138
138
  const divider = '─'.repeat(80);
139
139
  const sortedNames = services.map(s => s.name).sort().join('|');
140
140
  const logsCommand = `docker compose -p ${config.dockerServiceName} logs -f <${sortedNames}>`;
141
- return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsx(Box, { marginTop: 1, marginBottom: 1, children: _jsx(Text, { dimColor: true, children: divider }) }), _jsxs(Box, { children: [_jsx(Text, { color: "green", bold: true, children: '✅ SUCCESS! ' }), _jsx(Text, { bold: true, children: "Development services are running" })] }), _jsx(Box, { marginTop: 1, marginBottom: 1, children: _jsx(Text, { dimColor: true, children: divider }) }), _jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, children: "\uD83D\uDC33 SERVICES" }) }), _jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [_jsxs(Box, { children: [_jsx(Text, { color: "white", children: 'Temporal: ' }), _jsx(Text, { color: "yellow", children: "localhost:7233" })] }), _jsxs(Box, { children: [_jsx(Text, { color: "white", children: 'Temporal UI: ' }), _jsx(Text, { color: "cyan", children: "http://localhost:8080" })] }), _jsxs(Box, { children: [_jsx(Text, { color: "white", children: 'API Server: ' }), _jsx(Text, { color: "yellow", children: "localhost:3001" })] }), _jsxs(Box, { children: [_jsx(Text, { color: "white", children: 'Redis: ' }), _jsx(Text, { color: "yellow", children: "localhost:6379" })] })] }), _jsx(Box, { marginTop: 1, marginBottom: 1, children: _jsx(Text, { dimColor: true, children: divider }) }), _jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, children: "\uD83D\uDE80 RUN A WORKFLOW" }) }), _jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [_jsx(Text, { color: "white", children: "In a new terminal, execute:" }), _jsx(Box, { marginLeft: 2, children: _jsx(Text, { color: "cyan", children: "npx output workflow run blog_evaluator paulgraham_hwh" }) })] }), _jsx(Box, { marginTop: 1, marginBottom: 1, children: _jsx(Text, { dimColor: true, children: divider }) }), _jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, children: "\u26A1 USEFUL COMMANDS" }) }), _jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [_jsxs(Box, { children: [_jsx(Text, { color: "white", children: 'Open Temporal UI: ' }), _jsx(Text, { color: "cyan", children: "open http://localhost:8080" })] }), _jsxs(Box, { children: [_jsx(Text, { color: "white", children: 'View logs: ' }), _jsx(Text, { color: "cyan", children: logsCommand })] }), _jsxs(Box, { children: [_jsx(Text, { color: "white", children: 'Stop services: ' }), _jsx(Text, { color: "cyan", children: "Press Ctrl+C" })] })] }), _jsx(Box, { marginTop: 1, marginBottom: 1, children: _jsx(Text, { dimColor: true, children: divider }) }), _jsx(Text, { dimColor: true, children: "\uD83D\uDCA1 Tip: The Temporal UI lets you monitor workflow executions in real-time" })] }));
141
+ return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsx(Box, { marginTop: 1, marginBottom: 1, children: _jsx(Text, { dimColor: true, children: divider }) }), _jsxs(Box, { children: [_jsx(Text, { color: "green", bold: true, children: '✅ SUCCESS! ' }), _jsx(Text, { bold: true, children: "Development services are running" })] }), _jsx(Box, { marginTop: 1, marginBottom: 1, children: _jsx(Text, { dimColor: true, children: divider }) }), _jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, children: "\uD83D\uDC33 SERVICES" }) }), _jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [_jsxs(Box, { children: [_jsx(Text, { color: "white", children: 'Temporal UI: ' }), _jsx(Text, { color: "cyan", children: config.temporalUiUrl })] }), _jsxs(Box, { children: [_jsx(Text, { color: "white", children: 'API Server: ' }), _jsxs(Text, { color: "yellow", children: ["localhost:", config.ports.api] })] })] }), _jsx(Box, { marginTop: 1, marginBottom: 1, children: _jsx(Text, { dimColor: true, children: divider }) }), _jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, children: "\uD83D\uDE80 RUN A WORKFLOW" }) }), _jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [_jsx(Text, { color: "white", children: "In a new terminal, execute:" }), _jsx(Box, { marginLeft: 2, children: _jsx(Text, { color: "cyan", children: "npx output workflow run blog_evaluator paulgraham_hwh" }) })] }), _jsx(Box, { marginTop: 1, marginBottom: 1, children: _jsx(Text, { dimColor: true, children: divider }) }), _jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, children: "\u26A1 USEFUL COMMANDS" }) }), _jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [_jsxs(Box, { children: [_jsx(Text, { color: "white", children: 'Open Temporal UI: ' }), _jsxs(Text, { color: "cyan", children: ["open ", config.temporalUiUrl] })] }), _jsxs(Box, { children: [_jsx(Text, { color: "white", children: 'View logs: ' }), _jsx(Text, { color: "cyan", children: logsCommand })] }), _jsxs(Box, { children: [_jsx(Text, { color: "white", children: 'Stop services: ' }), _jsx(Text, { color: "cyan", children: "Press Ctrl+C" })] })] }), _jsx(Box, { marginTop: 1, marginBottom: 1, children: _jsx(Text, { dimColor: true, children: divider }) }), _jsx(Text, { dimColor: true, children: "\uD83D\uDCA1 Tip: The Temporal UI lets you monitor workflow executions in real-time" })] }));
142
142
  };
143
143
  const WaitingView = ({ services }) => (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { color: "yellow", children: _jsx(Spinner, { type: "dots" }) }), _jsx(Text, { children: " Waiting for services to become healthy..." })] }), services.length > 0 && (_jsx(Box, { flexDirection: "column", marginTop: 1, children: services.map(s => _jsx(ServiceRow, { service: s }, s.name)) }))] }));
144
- const RunningView = ({ services, workflowSummary }) => (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "\uD83D\uDCCA Service Status" }), _jsx(Box, { flexDirection: "column", marginTop: 1, children: services.map(s => _jsx(ServiceRow, { service: s }, s.name)) }), _jsx(FailureWarning, { services: services }), workflowSummary && _jsx(WorkflowSummarySection, { summary: workflowSummary }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: "cyan", children: '🌐 Temporal UI: ' }), _jsx(Text, { bold: true, children: "http://localhost:8080" })] }), _jsx(CommandFooter, { hints: [
144
+ const RunningView = ({ services, workflowSummary }) => (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "\uD83D\uDCCA Service Status" }), _jsx(Box, { flexDirection: "column", marginTop: 1, children: services.map(s => _jsx(ServiceRow, { service: s }, s.name)) }), _jsx(FailureWarning, { services: services }), workflowSummary && _jsx(WorkflowSummarySection, { summary: workflowSummary }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: "cyan", children: '🌐 Temporal UI: ' }), _jsx(Text, { bold: true, children: config.temporalUiUrl })] }), _jsx(CommandFooter, { hints: [
145
145
  { key: 'o', label: 'open ui' },
146
146
  { key: 'w', label: 'view workflow runs' },
147
147
  { key: 'ctrl+c', label: 'stop' }
@@ -173,7 +173,7 @@ export const DevApp = ({ dockerComposePath, onCleanup }) => {
173
173
  useStatusRefresh(dockerComposePath, phase === 'running', setServices);
174
174
  useWorkflowPolling(phase === 'running' || phase === 'failed', setWorkflowRuns);
175
175
  useMainViewInput(activeView === 'main' && phase !== 'waiting', {
176
- onOpenTemporal: () => openUrl('http://localhost:8080'),
176
+ onOpenTemporal: () => openUrl(config.temporalUiUrl),
177
177
  onOpenWorkflows: () => setActiveView('workflows')
178
178
  });
179
179
  useCtrlC(onCleanup);
@@ -2,12 +2,12 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useState, useEffect, useRef, useMemo } from 'react';
3
3
  import { Box, Text, useInput } from 'ink';
4
4
  import Spinner from 'ink-spinner';
5
- import { getWorkflowIdResult } from '#api/generated/api.js';
5
+ import { getWorkflowIdRunsRidResult } from '#api/generated/api.js';
6
6
  import { StatusIcon, statusColor } from '#components/status_icon.js';
7
7
  import { elapsedMs, formatDurationCompact } from '#utils/date_formatter.js';
8
8
  import { CommandFooter } from '#components/command_footer.js';
9
9
  import { openUrl } from '#utils/open_url.js';
10
- const TEMPORAL_UI_BASE = 'http://localhost:8080';
10
+ import { config } from '#config.js';
11
11
  const VISIBLE_ROWS = 15;
12
12
  const STATUS_ORDER = {
13
13
  running: 0,
@@ -59,17 +59,19 @@ export const WorkflowListView = ({ runs, onBack }) => {
59
59
  const clampedIndex = Math.min(selectedIndex, Math.max(0, sortedRuns.length - 1));
60
60
  const selectedRun = sortedRuns[clampedIndex];
61
61
  const selectedWorkflowId = selectedRun?.workflowId;
62
+ const selectedRunId = selectedRun?.runId;
62
63
  useEffect(() => {
63
64
  if (clampedIndex !== selectedIndex) {
64
65
  setSelectedIndex(clampedIndex);
65
66
  }
66
67
  }, [clampedIndex, selectedIndex]);
67
68
  useEffect(() => {
68
- if (!selectedWorkflowId) {
69
+ if (!selectedWorkflowId || !selectedRunId) {
69
70
  setDetail(null);
70
71
  return;
71
72
  }
72
- const cached = cacheRef.current.get(selectedWorkflowId);
73
+ const cacheKey = `${selectedWorkflowId}:${selectedRunId}`;
74
+ const cached = cacheRef.current.get(cacheKey);
73
75
  if (cached) {
74
76
  setDetail(cached);
75
77
  setDetailLoading(false);
@@ -77,13 +79,13 @@ export const WorkflowListView = ({ runs, onBack }) => {
77
79
  }
78
80
  const currentFetchId = ++fetchIdRef.current;
79
81
  setDetailLoading(true);
80
- getWorkflowIdResult(selectedWorkflowId)
82
+ getWorkflowIdRunsRidResult(selectedWorkflowId, selectedRunId)
81
83
  .then(response => {
82
84
  if (fetchIdRef.current !== currentFetchId) {
83
85
  return;
84
86
  }
85
87
  const data = response.data;
86
- cacheRef.current.set(selectedWorkflowId, data);
88
+ cacheRef.current.set(cacheKey, data);
87
89
  setDetail(data);
88
90
  setDetailLoading(false);
89
91
  })
@@ -94,7 +96,7 @@ export const WorkflowListView = ({ runs, onBack }) => {
94
96
  setDetail(null);
95
97
  setDetailLoading(false);
96
98
  });
97
- }, [selectedWorkflowId]);
99
+ }, [selectedWorkflowId, selectedRunId]);
98
100
  useInput((input, key) => {
99
101
  if (key.upArrow) {
100
102
  setSelectedIndex(i => Math.max(0, i - 1));
@@ -106,7 +108,7 @@ export const WorkflowListView = ({ runs, onBack }) => {
106
108
  onBack();
107
109
  }
108
110
  else if (input === 'o' && selectedWorkflowId) {
109
- openUrl(`${TEMPORAL_UI_BASE}/namespaces/default/workflows/${selectedWorkflowId}`);
111
+ openUrl(`${config.temporalUiUrl}/namespaces/default/workflows/${selectedWorkflowId}`);
110
112
  }
111
113
  });
112
114
  const windowStart = useMemo(() => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@outputai/cli",
3
- "version": "0.2.1-next.f1502fb.0",
3
+ "version": "0.3.0",
4
4
  "description": "CLI for Output.ai workflow generation",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -15,11 +15,11 @@
15
15
  },
16
16
  "dependencies": {
17
17
  "@anthropic-ai/claude-agent-sdk": "0.2.92",
18
- "@aws-sdk/client-s3": "3.1031.0",
18
+ "@aws-sdk/client-s3": "3.1038.0",
19
19
  "@hackylabs/deep-redact": "3.0.5",
20
- "@inquirer/prompts": "8.4.1",
21
- "@oclif/core": "4.10.5",
22
- "@oclif/plugin-help": "6.2.44",
20
+ "@inquirer/prompts": "8.4.2",
21
+ "@oclif/core": "4.10.6",
22
+ "@oclif/plugin-help": "6.2.45",
23
23
  "change-case": "5.4.4",
24
24
  "cli-progress": "3.12.0",
25
25
  "cli-table3": "0.6.5",
@@ -34,11 +34,11 @@
34
34
  "ky": "1.14.3",
35
35
  "react": "19.2.5",
36
36
  "semver": "7.7.4",
37
- "undici": "8.0.2",
37
+ "undici": "8.1.0",
38
38
  "yaml": "^2.8.3",
39
- "@outputai/credentials": "0.2.1-next.f1502fb.0",
40
- "@outputai/evals": "0.2.1-next.f1502fb.0",
41
- "@outputai/llm": "0.2.1-next.f1502fb.0"
39
+ "@outputai/credentials": "0.3.0",
40
+ "@outputai/llm": "0.3.0",
41
+ "@outputai/evals": "0.3.0"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@types/cli-progress": "3.11.6",
@@ -46,7 +46,7 @@
46
46
  "@types/js-yaml": "4.0.9",
47
47
  "@types/react": "19.2.14",
48
48
  "@types/semver": "7.7.1",
49
- "orval": "8.8.0"
49
+ "orval": "8.9.0"
50
50
  },
51
51
  "license": "Apache-2.0",
52
52
  "publishConfig": {