@outputai/cli 0.10.1-next.09ed166.0 → 0.10.1-next.2caa4a1.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/dist/api/http_client.js +2 -2
- package/dist/assets/docker/docker-compose-dev.yml +1 -1
- package/dist/commands/dev/down.d.ts +10 -0
- package/dist/commands/dev/down.js +34 -0
- package/dist/commands/dev/down.spec.d.ts +1 -0
- package/dist/commands/dev/down.spec.js +71 -0
- package/dist/commands/dev/index.d.ts +4 -0
- package/dist/commands/dev/index.js +200 -53
- package/dist/commands/dev/index.spec.js +390 -42
- package/dist/commands/workflow/run.js +8 -1
- package/dist/commands/workflow/run.spec.js +12 -2
- package/dist/commands/workflow/start.d.ts +3 -1
- package/dist/commands/workflow/start.js +12 -2
- package/dist/commands/workflow/start.spec.js +30 -5
- package/dist/generated/framework_version.json +1 -1
- package/dist/services/docker.d.ts +28 -1
- package/dist/services/docker.js +106 -12
- package/dist/services/docker.spec.js +144 -14
- package/dist/templates/agent_instructions/CLAUDE.md.template +1 -1
- package/dist/templates/project/src/clients/jina.ts.template +4 -4
- package/dist/utils/port_collision.d.ts +22 -7
- package/dist/utils/port_collision.js +39 -14
- package/dist/utils/port_collision.spec.js +40 -1
- package/dist/utils/resolve_input.d.ts +9 -1
- package/dist/utils/resolve_input.js +8 -2
- package/dist/utils/resolve_input.spec.d.ts +1 -0
- package/dist/utils/resolve_input.spec.js +75 -0
- package/dist/views/dev/chrome/footer.d.ts +2 -0
- package/dist/views/dev/chrome/footer.js +4 -4
- package/dist/views/dev/dev_app.d.ts +1 -0
- package/dist/views/dev/dev_app.js +13 -4
- package/dist/views/dev/hooks/use_run_detail.js +3 -1
- package/dist/views/dev/hooks/use_step_graph.js +3 -1
- package/dist/views/dev/utils/bounded_cache.d.ts +14 -0
- package/dist/views/dev/utils/bounded_cache.js +42 -0
- package/dist/views/dev/utils/bounded_cache.spec.d.ts +1 -0
- package/dist/views/dev/utils/bounded_cache.spec.js +53 -0
- package/oclif.manifest.json +48 -4
- package/package.json +7 -8
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { ux } from '@oclif/core';
|
|
2
2
|
import { parseInputFlag } from '#utils/input_parser.js';
|
|
3
3
|
import { resolveScenarioPath, getScenarioNotFoundMessage } from '#utils/scenario_resolver.js';
|
|
4
|
-
export async function resolveInput(
|
|
4
|
+
export async function resolveInput(options) {
|
|
5
|
+
const { workflowName, scenario, inputFlag, commandName, catalog, json } = options;
|
|
5
6
|
if (inputFlag && scenario) {
|
|
6
7
|
return ux.error('Cannot use both scenario argument and --input flag. Choose one.', { exit: 1 });
|
|
7
8
|
}
|
|
@@ -13,7 +14,12 @@ export async function resolveInput(workflowName, scenario, inputFlag, commandNam
|
|
|
13
14
|
if (!resolution.found) {
|
|
14
15
|
return ux.error(getScenarioNotFoundMessage(workflowName, scenario, resolution.searchedPaths), { exit: 1 });
|
|
15
16
|
}
|
|
16
|
-
|
|
17
|
+
// Advisory notice goes to stderr so stdout stays clean for piping, and is
|
|
18
|
+
// skipped entirely under --json where even stderr is noise to a script
|
|
19
|
+
// consuming the structured output (same rule as the init hook's banner).
|
|
20
|
+
if (!json) {
|
|
21
|
+
ux.stderr(`Using scenario: ${resolution.path}`);
|
|
22
|
+
}
|
|
17
23
|
return parseInputFlag(resolution.path);
|
|
18
24
|
}
|
|
19
25
|
return ux.error('Input required. Provide either:\n' +
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
3
|
+
vi.mock('@oclif/core', () => ({
|
|
4
|
+
ux: {
|
|
5
|
+
stdout: vi.fn(),
|
|
6
|
+
stderr: vi.fn(),
|
|
7
|
+
error: vi.fn(() => {
|
|
8
|
+
throw new Error('ux.error called');
|
|
9
|
+
})
|
|
10
|
+
}
|
|
11
|
+
}));
|
|
12
|
+
vi.mock('#utils/input_parser.js', () => ({
|
|
13
|
+
parseInputFlag: vi.fn()
|
|
14
|
+
}));
|
|
15
|
+
vi.mock('#utils/scenario_resolver.js', () => ({
|
|
16
|
+
resolveScenarioPath: vi.fn(),
|
|
17
|
+
getScenarioNotFoundMessage: vi.fn()
|
|
18
|
+
}));
|
|
19
|
+
describe('resolveInput', () => {
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
vi.clearAllMocks();
|
|
22
|
+
});
|
|
23
|
+
it('emits the scenario notice on stderr so --json stdout stays clean', async () => {
|
|
24
|
+
const { ux } = await import('@oclif/core');
|
|
25
|
+
const { parseInputFlag } = await import('#utils/input_parser.js');
|
|
26
|
+
const { resolveScenarioPath } = await import('#utils/scenario_resolver.js');
|
|
27
|
+
const { resolveInput } = await import('./resolve_input.js');
|
|
28
|
+
vi.mocked(resolveScenarioPath).mockResolvedValue({
|
|
29
|
+
found: true,
|
|
30
|
+
path: '/scenarios/happy_path.json'
|
|
31
|
+
});
|
|
32
|
+
vi.mocked(parseInputFlag).mockReturnValue({ key: 'value' });
|
|
33
|
+
const result = await resolveInput({
|
|
34
|
+
workflowName: 'web_search',
|
|
35
|
+
scenario: 'happy_path',
|
|
36
|
+
commandName: 'start'
|
|
37
|
+
});
|
|
38
|
+
expect(result).toEqual({ key: 'value' });
|
|
39
|
+
expect(ux.stderr).toHaveBeenCalledWith('Using scenario: /scenarios/happy_path.json');
|
|
40
|
+
expect(ux.stdout).not.toHaveBeenCalled();
|
|
41
|
+
});
|
|
42
|
+
it('suppresses the scenario notice entirely under --json', async () => {
|
|
43
|
+
const { ux } = await import('@oclif/core');
|
|
44
|
+
const { parseInputFlag } = await import('#utils/input_parser.js');
|
|
45
|
+
const { resolveScenarioPath } = await import('#utils/scenario_resolver.js');
|
|
46
|
+
const { resolveInput } = await import('./resolve_input.js');
|
|
47
|
+
vi.mocked(resolveScenarioPath).mockResolvedValue({
|
|
48
|
+
found: true,
|
|
49
|
+
path: '/scenarios/happy_path.json'
|
|
50
|
+
});
|
|
51
|
+
vi.mocked(parseInputFlag).mockReturnValue({ key: 'value' });
|
|
52
|
+
const result = await resolveInput({
|
|
53
|
+
workflowName: 'web_search',
|
|
54
|
+
scenario: 'happy_path',
|
|
55
|
+
commandName: 'start',
|
|
56
|
+
json: true
|
|
57
|
+
});
|
|
58
|
+
expect(result).toEqual({ key: 'value' });
|
|
59
|
+
expect(ux.stderr).not.toHaveBeenCalled();
|
|
60
|
+
expect(ux.stdout).not.toHaveBeenCalled();
|
|
61
|
+
});
|
|
62
|
+
it('does not emit the scenario notice when input comes from --input', async () => {
|
|
63
|
+
const { ux } = await import('@oclif/core');
|
|
64
|
+
const { parseInputFlag } = await import('#utils/input_parser.js');
|
|
65
|
+
const { resolveInput } = await import('./resolve_input.js');
|
|
66
|
+
vi.mocked(parseInputFlag).mockReturnValue({ key: 'value' });
|
|
67
|
+
await resolveInput({
|
|
68
|
+
workflowName: 'web_search',
|
|
69
|
+
inputFlag: '{"key":"value"}',
|
|
70
|
+
commandName: 'start'
|
|
71
|
+
});
|
|
72
|
+
expect(ux.stderr).not.toHaveBeenCalled();
|
|
73
|
+
expect(ux.stdout).not.toHaveBeenCalled();
|
|
74
|
+
});
|
|
75
|
+
});
|
|
@@ -5,16 +5,16 @@ import packageJson from '../../../../package.json' with { type: 'json' };
|
|
|
5
5
|
const GLOBAL_HINT_ROWS = 1;
|
|
6
6
|
const LOCAL_HINT_ROWS = 1;
|
|
7
7
|
export const getHeight = () => GLOBAL_HINT_ROWS + LOCAL_HINT_ROWS;
|
|
8
|
-
const
|
|
8
|
+
const globalHints = (attached) => [
|
|
9
9
|
{ key: 'tab', label: 'next tab' },
|
|
10
10
|
{ key: 'shift-tab', label: 'prev tab' },
|
|
11
11
|
{ key: '1-4', label: 'tabs' },
|
|
12
12
|
{ key: '/', label: 'search' },
|
|
13
13
|
{ key: '?', label: 'help' },
|
|
14
|
-
{ key: 'ctrl+c', label: 'quit' }
|
|
14
|
+
{ key: 'ctrl+c', label: attached ? 'detach (keeps running)' : 'quit' }
|
|
15
15
|
];
|
|
16
16
|
const VERSION = packageJson.version;
|
|
17
17
|
const HintRow = ({ hints }) => (_jsx(Box, { flexDirection: "row", children: hints.length === 0 ? (_jsx(Text, { children: " " })) : hints.map((hint, i) => (_jsxs(React.Fragment, { children: [i > 0 && _jsx(Text, { dimColor: true, children: ' ' }), _jsx(Text, { bold: true, children: hint.key }), _jsx(Text, { dimColor: true, children: ` ${hint.label}` })] }, hint.key))) }));
|
|
18
|
-
export const Footer = ({ hints = [], itemCount, itemLabel }) => {
|
|
19
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { flexDirection: "row", justifyContent: "space-between", children: [_jsx(HintRow, { hints:
|
|
18
|
+
export const Footer = ({ hints = [], itemCount, itemLabel, attached = false }) => {
|
|
19
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { flexDirection: "row", justifyContent: "space-between", children: [_jsx(HintRow, { hints: globalHints(attached) }), typeof itemCount === 'number' && itemLabel && (_jsx(Box, { children: _jsxs(Text, { dimColor: true, children: [itemCount, " ", itemLabel] }) }))] }), _jsxs(Box, { flexDirection: "row", justifyContent: "space-between", children: [_jsx(HintRow, { hints: hints }), _jsxs(Text, { color: "blackBright", children: ["v", VERSION] })] })] }));
|
|
20
20
|
};
|
|
@@ -150,7 +150,7 @@ const overlayFor = (opts) => {
|
|
|
150
150
|
}
|
|
151
151
|
return null;
|
|
152
152
|
};
|
|
153
|
-
const Shell = ({ dockerComposePath, onCleanup }) => {
|
|
153
|
+
const Shell = ({ dockerComposePath, onCleanup, attached }) => {
|
|
154
154
|
const { exit } = useApp();
|
|
155
155
|
const ui = useUiState();
|
|
156
156
|
const [phase, setPhase] = useState('waiting');
|
|
@@ -160,7 +160,16 @@ const Shell = ({ dockerComposePath, onCleanup }) => {
|
|
|
160
160
|
onServices: setServices,
|
|
161
161
|
onAllHealthy: () => setPhase('running'),
|
|
162
162
|
onFailure: () => setPhase('failed'),
|
|
163
|
-
|
|
163
|
+
// An attach/reconcile session monitors a stack it doesn't own, so a slow
|
|
164
|
+
// health check must not be fatal — drop into the dashboard and keep
|
|
165
|
+
// polling status. Only an owned fresh start treats the timeout as an error.
|
|
166
|
+
onTimeout: () => {
|
|
167
|
+
if (attached) {
|
|
168
|
+
setPhase('running');
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
exit(new Error('Timeout waiting for services to become healthy'));
|
|
172
|
+
}
|
|
164
173
|
});
|
|
165
174
|
useStatusRefresh(dockerComposePath, phase !== 'waiting', setServices);
|
|
166
175
|
useWorkflowRunsPolling(phase !== 'waiting', setRuns);
|
|
@@ -231,6 +240,6 @@ const Shell = ({ dockerComposePath, onCleanup }) => {
|
|
|
231
240
|
if (overlay) {
|
|
232
241
|
return (_jsx(Box, { flexDirection: "column", height: rows, paddingX: 1, children: overlay }));
|
|
233
242
|
}
|
|
234
|
-
return (_jsxs(Box, { flexDirection: "column", height: rows, paddingX: 1, children: [_jsx(Header, { counters: counters }), _jsx(TabBar, { active: ui.tab, borderColor: RULE_PURPLE }), _jsx(SearchBar, { active: ui.search.open }), _jsxs(Box, { flexDirection: "column", flexGrow: 1, height: contentRows, overflow: "hidden", children: [ui.tab === 'workflows' && _jsx(WorkflowsPanel, { workflows: workflows, runs: runs }), ui.tab === 'runs' && _jsx(RunsPanel, { runs: runs, height: contentRows }), ui.tab === 'services' && (_jsx(ServicesPanel, { height: contentRows, phase: phase, services: services, dockerComposePath: dockerComposePath })), ui.tab === 'help' && _jsx(HelpPanel, {})] }), _jsx(Toasts, {}), _jsx(Footer, { hints: footer.hints, itemCount: footer.itemCount, itemLabel: footer.itemLabel })] }));
|
|
243
|
+
return (_jsxs(Box, { flexDirection: "column", height: rows, paddingX: 1, children: [_jsx(Header, { counters: counters }), _jsx(TabBar, { active: ui.tab, borderColor: RULE_PURPLE }), _jsx(SearchBar, { active: ui.search.open }), _jsxs(Box, { flexDirection: "column", flexGrow: 1, height: contentRows, overflow: "hidden", children: [ui.tab === 'workflows' && _jsx(WorkflowsPanel, { workflows: workflows, runs: runs }), ui.tab === 'runs' && _jsx(RunsPanel, { runs: runs, height: contentRows }), ui.tab === 'services' && (_jsx(ServicesPanel, { height: contentRows, phase: phase, services: services, dockerComposePath: dockerComposePath })), ui.tab === 'help' && _jsx(HelpPanel, {})] }), _jsx(Toasts, {}), _jsx(Footer, { hints: footer.hints, itemCount: footer.itemCount, itemLabel: footer.itemLabel, attached: attached })] }));
|
|
235
244
|
};
|
|
236
|
-
export const DevApp = ({ dockerComposePath, onCleanup }) => (_jsx(UiStateProvider, { children: _jsx(Shell, { dockerComposePath: dockerComposePath, onCleanup: onCleanup }) }));
|
|
245
|
+
export const DevApp = ({ dockerComposePath, onCleanup, attached = false }) => (_jsx(UiStateProvider, { children: _jsx(Shell, { dockerComposePath: dockerComposePath, onCleanup: onCleanup, attached: attached }) }));
|
|
@@ -3,13 +3,15 @@ import { readFile } from 'node:fs/promises';
|
|
|
3
3
|
import { getWorkflowIdResult, getWorkflowIdRunsRidResult, getWorkflowIdTraceLog, getWorkflowIdRunsRidTraceLog } from '#api/generated/api.js';
|
|
4
4
|
import { normalizeWorkflowStatus } from '#utils/normalize_workflow_status.js';
|
|
5
5
|
import { TERMINAL_STATUSES } from '#utils/format_workflow_result.js';
|
|
6
|
+
import { createBoundedCache } from '#views/dev/utils/bounded_cache.js';
|
|
6
7
|
const EMPTY_DETAIL = {
|
|
7
8
|
result: null,
|
|
8
9
|
trace: null,
|
|
9
10
|
steps: [],
|
|
10
11
|
loading: false
|
|
11
12
|
};
|
|
12
|
-
const
|
|
13
|
+
const RUN_DETAIL_CACHE_MAX = 50;
|
|
14
|
+
const runDetailCache = createBoundedCache(RUN_DETAIL_CACHE_MAX);
|
|
13
15
|
const stepNameOf = (node) => {
|
|
14
16
|
if (node.name) {
|
|
15
17
|
return node.name;
|
|
@@ -3,6 +3,7 @@ import { fetchWorkflowHistory } from '#services/workflow_history.js';
|
|
|
3
3
|
import buildSpanLabels from '#utils/span_labels.js';
|
|
4
4
|
import { isTerminalRunStatus } from '#views/dev/hooks/use_run_detail.js';
|
|
5
5
|
import { usePoll, POLL_INTERVAL_MS } from '#views/dev/hooks/use_poll.js';
|
|
6
|
+
import { createBoundedCache } from '#views/dev/utils/bounded_cache.js';
|
|
6
7
|
const EMPTY_GRAPH = {
|
|
7
8
|
spans: [],
|
|
8
9
|
totalDurationMs: 0,
|
|
@@ -11,7 +12,8 @@ const EMPTY_GRAPH = {
|
|
|
11
12
|
loading: false,
|
|
12
13
|
error: null
|
|
13
14
|
};
|
|
14
|
-
const
|
|
15
|
+
const STEP_GRAPH_CACHE_MAX = 50;
|
|
16
|
+
const stepGraphCache = createBoundedCache(STEP_GRAPH_CACHE_MAX);
|
|
15
17
|
/**
|
|
16
18
|
* Fetches a run's correlated step spans for the dev TUI's waterfall overlay,
|
|
17
19
|
* reusing the same `fetchWorkflowHistory` path as the `workflow history` CLI
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface BoundedCache<K, V extends {}> {
|
|
2
|
+
get(key: K): V | undefined;
|
|
3
|
+
set(key: K, value: V): void;
|
|
4
|
+
has(key: K): boolean;
|
|
5
|
+
clear(): void;
|
|
6
|
+
size(): number;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* A small LRU cache backed by an insertion-ordered `Map`, capped at `maxSize`
|
|
10
|
+
* entries. Reading a key refreshes its recency; once the cap is exceeded the
|
|
11
|
+
* least-recently-used entries are evicted. Drop-in compatible with the
|
|
12
|
+
* `Map.get` / `Map.set` calls it replaces.
|
|
13
|
+
*/
|
|
14
|
+
export declare const createBoundedCache: <K, V extends {}>(maxSize: number) => BoundedCache<K, V>;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A small LRU cache backed by an insertion-ordered `Map`, capped at `maxSize`
|
|
3
|
+
* entries. Reading a key refreshes its recency; once the cap is exceeded the
|
|
4
|
+
* least-recently-used entries are evicted. Drop-in compatible with the
|
|
5
|
+
* `Map.get` / `Map.set` calls it replaces.
|
|
6
|
+
*/
|
|
7
|
+
export const createBoundedCache = (maxSize) => {
|
|
8
|
+
if (maxSize < 1) {
|
|
9
|
+
throw new Error('createBoundedCache: maxSize must be >= 1');
|
|
10
|
+
}
|
|
11
|
+
const entries = new Map();
|
|
12
|
+
return {
|
|
13
|
+
get(key) {
|
|
14
|
+
const value = entries.get(key);
|
|
15
|
+
if (value === undefined) {
|
|
16
|
+
return value;
|
|
17
|
+
}
|
|
18
|
+
entries.delete(key);
|
|
19
|
+
entries.set(key, value);
|
|
20
|
+
return value;
|
|
21
|
+
},
|
|
22
|
+
set(key, value) {
|
|
23
|
+
entries.delete(key);
|
|
24
|
+
entries.set(key, value);
|
|
25
|
+
if (entries.size > maxSize) {
|
|
26
|
+
const oldest = entries.keys().next();
|
|
27
|
+
if (!oldest.done) {
|
|
28
|
+
entries.delete(oldest.value);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
has(key) {
|
|
33
|
+
return entries.has(key);
|
|
34
|
+
},
|
|
35
|
+
clear() {
|
|
36
|
+
entries.clear();
|
|
37
|
+
},
|
|
38
|
+
size() {
|
|
39
|
+
return entries.size;
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { createBoundedCache } from './bounded_cache.js';
|
|
3
|
+
describe('createBoundedCache', () => {
|
|
4
|
+
it('round-trips set and get', () => {
|
|
5
|
+
const cache = createBoundedCache(3);
|
|
6
|
+
cache.set('a', 1);
|
|
7
|
+
expect(cache.get('a')).toBe(1);
|
|
8
|
+
expect(cache.get('missing')).toBeUndefined();
|
|
9
|
+
});
|
|
10
|
+
it('evicts the oldest entry once maxSize is exceeded', () => {
|
|
11
|
+
const cache = createBoundedCache(2);
|
|
12
|
+
cache.set('a', 1);
|
|
13
|
+
cache.set('b', 2);
|
|
14
|
+
cache.set('c', 3);
|
|
15
|
+
expect(cache.has('a')).toBe(false);
|
|
16
|
+
expect(cache.get('b')).toBe(2);
|
|
17
|
+
expect(cache.get('c')).toBe(3);
|
|
18
|
+
});
|
|
19
|
+
it('refreshes recency on get so the touched entry survives eviction', () => {
|
|
20
|
+
const cache = createBoundedCache(2);
|
|
21
|
+
cache.set('a', 1);
|
|
22
|
+
cache.set('b', 2);
|
|
23
|
+
// Touch 'a' so 'b' becomes the least-recently-used entry.
|
|
24
|
+
expect(cache.get('a')).toBe(1);
|
|
25
|
+
cache.set('c', 3);
|
|
26
|
+
expect(cache.has('a')).toBe(true);
|
|
27
|
+
expect(cache.has('b')).toBe(false);
|
|
28
|
+
expect(cache.has('c')).toBe(true);
|
|
29
|
+
});
|
|
30
|
+
it('updates an existing key in place without growing', () => {
|
|
31
|
+
const cache = createBoundedCache(2);
|
|
32
|
+
cache.set('a', 1);
|
|
33
|
+
cache.set('a', 2);
|
|
34
|
+
expect(cache.get('a')).toBe(2);
|
|
35
|
+
expect(cache.size()).toBe(1);
|
|
36
|
+
});
|
|
37
|
+
it('supports has and clear', () => {
|
|
38
|
+
const cache = createBoundedCache(2);
|
|
39
|
+
cache.set('a', 1);
|
|
40
|
+
expect(cache.has('a')).toBe(true);
|
|
41
|
+
cache.clear();
|
|
42
|
+
expect(cache.has('a')).toBe(false);
|
|
43
|
+
expect(cache.size()).toBe(0);
|
|
44
|
+
});
|
|
45
|
+
it('never exceeds maxSize', () => {
|
|
46
|
+
const cache = createBoundedCache(3);
|
|
47
|
+
Array.from({ length: 100 }, (_, i) => i).forEach(i => {
|
|
48
|
+
cache.set(`key-${i}`, i);
|
|
49
|
+
expect(cache.size()).toBeLessThanOrEqual(3);
|
|
50
|
+
});
|
|
51
|
+
expect(cache.size()).toBe(3);
|
|
52
|
+
});
|
|
53
|
+
});
|
package/oclif.manifest.json
CHANGED
|
@@ -416,6 +416,41 @@
|
|
|
416
416
|
"show.js"
|
|
417
417
|
]
|
|
418
418
|
},
|
|
419
|
+
"dev:down": {
|
|
420
|
+
"aliases": [],
|
|
421
|
+
"args": {},
|
|
422
|
+
"description": "Stop Output development services started by `output dev`\n\nUseful after `output dev -d`, or when an attached `output dev` session\nleft the services running on quit.",
|
|
423
|
+
"examples": [
|
|
424
|
+
"<%= config.bin %> <%= command.id %>",
|
|
425
|
+
"<%= config.bin %> <%= command.id %> --compose-file ./custom-docker-compose.yml"
|
|
426
|
+
],
|
|
427
|
+
"flags": {
|
|
428
|
+
"compose-file": {
|
|
429
|
+
"char": "f",
|
|
430
|
+
"description": "Path to a custom docker-compose file",
|
|
431
|
+
"name": "compose-file",
|
|
432
|
+
"required": false,
|
|
433
|
+
"hasDynamicHelp": false,
|
|
434
|
+
"multiple": false,
|
|
435
|
+
"type": "option"
|
|
436
|
+
}
|
|
437
|
+
},
|
|
438
|
+
"hasDynamicHelp": false,
|
|
439
|
+
"hiddenAliases": [],
|
|
440
|
+
"id": "dev:down",
|
|
441
|
+
"pluginAlias": "@outputai/cli",
|
|
442
|
+
"pluginName": "@outputai/cli",
|
|
443
|
+
"pluginType": "core",
|
|
444
|
+
"strict": true,
|
|
445
|
+
"enableJsonFlag": false,
|
|
446
|
+
"isESM": true,
|
|
447
|
+
"relativePath": [
|
|
448
|
+
"dist",
|
|
449
|
+
"commands",
|
|
450
|
+
"dev",
|
|
451
|
+
"down.js"
|
|
452
|
+
]
|
|
453
|
+
},
|
|
419
454
|
"dev:eject": {
|
|
420
455
|
"aliases": [],
|
|
421
456
|
"args": {},
|
|
@@ -463,9 +498,10 @@
|
|
|
463
498
|
"dev": {
|
|
464
499
|
"aliases": [],
|
|
465
500
|
"args": {},
|
|
466
|
-
"description": "Start Output development services (auto-restarts worker on file changes)\n\nTo run a second dev stack concurrently,
|
|
501
|
+
"description": "Start Output development services (auto-restarts worker on file changes)\n\nIf services are already running (e.g. after `output dev -d`), this attaches\nto monitor them rather than treating our own containers as a port collision.\nQuitting an attached session leaves the services running — stop them with\n`output dev down`.\n\nTo run a second dev stack concurrently, give it its own compose project and\nhost ports in .env — without DOCKER_SERVICE_NAME both checkouts share one\nstack, and the second will attach to the first instead of starting:\n\n DOCKER_SERVICE_NAME=output-sdk-two\n OUTPUT_API_HOST_PORT=3002\n OUTPUT_TEMPORAL_UI_HOST_PORT=8081\n OUTPUT_TEMPORAL_HOST_PORT=7234",
|
|
467
502
|
"examples": [
|
|
468
503
|
"<%= config.bin %> <%= command.id %>",
|
|
504
|
+
"<%= config.bin %> <%= command.id %> --detached",
|
|
469
505
|
"<%= config.bin %> <%= command.id %> --compose-file ./custom-docker-compose.yml",
|
|
470
506
|
"<%= config.bin %> <%= command.id %> --image-pull-policy missing"
|
|
471
507
|
],
|
|
@@ -1200,9 +1236,17 @@
|
|
|
1200
1236
|
"<%= config.bin %> <%= command.id %> simple basic_input",
|
|
1201
1237
|
"<%= config.bin %> <%= command.id %> simple --input '{\"values\":[1,2,3]}'",
|
|
1202
1238
|
"<%= config.bin %> <%= command.id %> simple --input input.json",
|
|
1203
|
-
"<%= config.bin %> <%= command.id %> simple --input '{\"key\":\"value\"}' --catalog my-catalog"
|
|
1239
|
+
"<%= config.bin %> <%= command.id %> simple --input '{\"key\":\"value\"}' --catalog my-catalog",
|
|
1240
|
+
"<%= config.bin %> <%= command.id %> simple --json"
|
|
1204
1241
|
],
|
|
1205
1242
|
"flags": {
|
|
1243
|
+
"json": {
|
|
1244
|
+
"description": "Format output as json.",
|
|
1245
|
+
"helpGroup": "GLOBAL",
|
|
1246
|
+
"name": "json",
|
|
1247
|
+
"allowNo": false,
|
|
1248
|
+
"type": "boolean"
|
|
1249
|
+
},
|
|
1206
1250
|
"input": {
|
|
1207
1251
|
"char": "i",
|
|
1208
1252
|
"description": "Workflow input as JSON string or file path (overrides scenario)",
|
|
@@ -1236,7 +1280,7 @@
|
|
|
1236
1280
|
"pluginName": "@outputai/cli",
|
|
1237
1281
|
"pluginType": "core",
|
|
1238
1282
|
"strict": true,
|
|
1239
|
-
"enableJsonFlag":
|
|
1283
|
+
"enableJsonFlag": true,
|
|
1240
1284
|
"isESM": true,
|
|
1241
1285
|
"relativePath": [
|
|
1242
1286
|
"dist",
|
|
@@ -1671,5 +1715,5 @@
|
|
|
1671
1715
|
]
|
|
1672
1716
|
}
|
|
1673
1717
|
},
|
|
1674
|
-
"version": "0.10.1-next.
|
|
1718
|
+
"version": "0.10.1-next.2caa4a1.0"
|
|
1675
1719
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@outputai/cli",
|
|
3
|
-
"version": "0.10.1-next.
|
|
3
|
+
"version": "0.10.1-next.2caa4a1.0",
|
|
4
4
|
"description": "CLI for Output.ai workflow generation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -31,16 +31,15 @@
|
|
|
31
31
|
"handlebars": "4.7.9",
|
|
32
32
|
"ink": "7.0.1",
|
|
33
33
|
"ink-spinner": "5.0.0",
|
|
34
|
-
"js-yaml": "4.
|
|
34
|
+
"js-yaml": "4.3.0",
|
|
35
35
|
"json-schema-library": "11.4.0",
|
|
36
|
-
"ky": "
|
|
36
|
+
"ky": "2.0.2",
|
|
37
37
|
"react": "19.2.5",
|
|
38
38
|
"semver": "7.7.4",
|
|
39
|
-
"undici": "8.
|
|
40
|
-
"
|
|
41
|
-
"@outputai/
|
|
42
|
-
"@outputai/llm": "0.10.1-next.
|
|
43
|
-
"@outputai/evals": "0.10.1-next.09ed166.0"
|
|
39
|
+
"undici": "8.9.0",
|
|
40
|
+
"@outputai/credentials": "0.10.1-next.2caa4a1.0",
|
|
41
|
+
"@outputai/evals": "0.10.1-next.2caa4a1.0",
|
|
42
|
+
"@outputai/llm": "0.10.1-next.2caa4a1.0"
|
|
44
43
|
},
|
|
45
44
|
"devDependencies": {
|
|
46
45
|
"@types/cli-progress": "3.11.6",
|