@outputai/cli 0.2.1-next.bd54540.0 → 0.2.1-next.ef50432.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/bin/run.js +2 -2
- package/dist/api/generated/api.d.ts +21 -5
- package/dist/api/generated/api.js +1 -1
- package/dist/assets/docker/docker-compose-dev.yml +5 -9
- package/dist/commands/dev/index.js +12 -1
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.js +5 -1
- package/dist/commands/init.spec.js +10 -5
- package/dist/commands/workflow/run.d.ts +1 -1
- package/dist/commands/workflow/run.js +8 -5
- package/dist/commands/workflow/run.spec.js +3 -3
- package/dist/commands/workflow/runs/list.d.ts +1 -0
- package/dist/commands/workflow/runs/list.js +7 -0
- package/dist/commands/workflow/start.d.ts +1 -1
- package/dist/commands/workflow/start.js +8 -5
- package/dist/commands/workflow/start.spec.js +1 -1
- package/dist/config.d.ts +5 -0
- package/dist/config.js +13 -1
- package/dist/config.spec.js +54 -0
- package/dist/generated/framework_version.json +1 -1
- package/dist/hooks/init.d.ts +4 -0
- package/dist/hooks/init.js +14 -2
- package/dist/hooks/init.spec.js +79 -5
- package/dist/services/docker.js +5 -2
- package/dist/services/docker.spec.js +74 -3
- package/dist/services/messages.js +2 -1
- package/dist/services/project_scaffold.d.ts +1 -1
- package/dist/services/project_scaffold.js +16 -1
- package/dist/services/workflow_runs.d.ts +1 -0
- package/dist/services/workflow_runs.js +3 -0
- package/dist/templates/project/.env.example.template +17 -0
- package/dist/utils/credentials_loader.d.ts +1 -0
- package/dist/utils/credentials_loader.js +18 -0
- package/dist/utils/credentials_loader.spec.d.ts +1 -0
- package/dist/utils/credentials_loader.spec.js +84 -0
- package/dist/utils/validation.d.ts +13 -0
- package/dist/utils/validation.js +31 -0
- package/dist/utils/validation.spec.js +47 -1
- package/dist/views/dev.js +3 -3
- package/dist/views/workflow/list.js +10 -8
- package/package.json +10 -10
package/bin/run.js
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
import { execute } from '@oclif/core';
|
|
4
4
|
import { loadEnvironment } from '../dist/utils/env_loader.js';
|
|
5
5
|
import { bootstrapProxy } from '../dist/utils/proxy.js';
|
|
6
|
-
import {
|
|
6
|
+
import { loadCredentialRefs } from '../dist/utils/credentials_loader.js';
|
|
7
7
|
|
|
8
8
|
// Load environment variables from .env files before executing CLI
|
|
9
9
|
loadEnvironment();
|
|
10
10
|
bootstrapProxy();
|
|
11
|
-
|
|
11
|
+
loadCredentialRefs();
|
|
12
12
|
|
|
13
13
|
await execute( { dir: import.meta.url } );
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Generated by orval v8.
|
|
2
|
+
* Generated by orval v8.9.0 🍺
|
|
3
3
|
* Do not edit manually.
|
|
4
4
|
* Output.ai API
|
|
5
5
|
* API for managing and executing Output.ai workflows
|
|
@@ -162,6 +162,8 @@ export declare const WorkflowRunInfoStatus: {
|
|
|
162
162
|
export interface WorkflowRunInfo {
|
|
163
163
|
/** Unique identifier for this run */
|
|
164
164
|
workflowId?: string;
|
|
165
|
+
/** The specific run id for this execution */
|
|
166
|
+
runId?: string;
|
|
165
167
|
/** Name of the workflow definition */
|
|
166
168
|
workflowType?: string;
|
|
167
169
|
/** Current run status */
|
|
@@ -291,7 +293,12 @@ export type PostWorkflowRunBody = {
|
|
|
291
293
|
input: unknown;
|
|
292
294
|
/** (Optional) The workflowId to use. Must be unique */
|
|
293
295
|
workflowId?: string;
|
|
294
|
-
/** The
|
|
296
|
+
/** The catalog (Temporal task queue) to route the execution to. Falls back to the default catalog. */
|
|
297
|
+
catalog?: string;
|
|
298
|
+
/**
|
|
299
|
+
* Deprecated alias for `catalog`. If both are sent, `catalog` wins.
|
|
300
|
+
* @deprecated
|
|
301
|
+
*/
|
|
295
302
|
taskQueue?: string;
|
|
296
303
|
/** (Optional) The max time to wait for the execution, defaults to 30s */
|
|
297
304
|
timeout?: number;
|
|
@@ -325,7 +332,12 @@ export type PostWorkflowStartBody = {
|
|
|
325
332
|
input: unknown;
|
|
326
333
|
/** (Optional) The workflowId to use. Must be unique */
|
|
327
334
|
workflowId?: string;
|
|
328
|
-
/** The
|
|
335
|
+
/** The catalog (Temporal task queue) to route the execution to. Falls back to the default catalog. */
|
|
336
|
+
catalog?: string;
|
|
337
|
+
/**
|
|
338
|
+
* Deprecated alias for `catalog`. If both are sent, `catalog` wins.
|
|
339
|
+
* @deprecated
|
|
340
|
+
*/
|
|
329
341
|
taskQueue?: string;
|
|
330
342
|
};
|
|
331
343
|
export type PostWorkflowStart200 = {
|
|
@@ -437,6 +449,10 @@ export type GetWorkflowRunsParams = {
|
|
|
437
449
|
* Filter by workflow type/name
|
|
438
450
|
*/
|
|
439
451
|
workflowType?: string;
|
|
452
|
+
/**
|
|
453
|
+
* Filter by catalog ID (scopes runs to a single worker's catalog/session)
|
|
454
|
+
*/
|
|
455
|
+
catalog?: string;
|
|
440
456
|
/**
|
|
441
457
|
* Maximum number of runs to return
|
|
442
458
|
* @minimum 1
|
|
@@ -709,7 +725,7 @@ export type postWorkflowIdRunsRidTerminateResponseError = (postWorkflowIdRunsRid
|
|
|
709
725
|
};
|
|
710
726
|
export type postWorkflowIdRunsRidTerminateResponse = (postWorkflowIdRunsRidTerminateResponseSuccess | postWorkflowIdRunsRidTerminateResponseError);
|
|
711
727
|
export declare const getPostWorkflowIdRunsRidTerminateUrl: (id: string, rid: string) => string;
|
|
712
|
-
export declare const postWorkflowIdRunsRidTerminate: (id: string, rid: string, postWorkflowIdRunsRidTerminateBody
|
|
728
|
+
export declare const postWorkflowIdRunsRidTerminate: (id: string, rid: string, postWorkflowIdRunsRidTerminateBody?: PostWorkflowIdRunsRidTerminateBody, options?: ApiRequestOptions) => Promise<postWorkflowIdRunsRidTerminateResponse>;
|
|
713
729
|
/**
|
|
714
730
|
* Force terminates the latest run. Deprecated; use `POST /workflow/{id}/runs/{rid}/terminate` to target a specific run. Scheduled for removal after 2026-07-16.
|
|
715
731
|
* @deprecated
|
|
@@ -743,7 +759,7 @@ export type postWorkflowIdTerminateResponseError = (postWorkflowIdTerminateRespo
|
|
|
743
759
|
};
|
|
744
760
|
export type postWorkflowIdTerminateResponse = (postWorkflowIdTerminateResponseSuccess | postWorkflowIdTerminateResponseError);
|
|
745
761
|
export declare const getPostWorkflowIdTerminateUrl: (id: string) => string;
|
|
746
|
-
export declare const postWorkflowIdTerminate: (id: string, postWorkflowIdTerminateBody
|
|
762
|
+
export declare const postWorkflowIdTerminate: (id: string, postWorkflowIdTerminateBody?: PostWorkflowIdTerminateBody, options?: ApiRequestOptions) => Promise<postWorkflowIdTerminateResponse>;
|
|
747
763
|
/**
|
|
748
764
|
* Resets a pinned workflow run to the point after a completed step, creating a new run that replays from that point. The current execution is terminated.
|
|
749
765
|
* @summary Reset a specific workflow run to re-run from after a completed step
|
|
@@ -4,8 +4,6 @@ services:
|
|
|
4
4
|
image: redis:8-alpine
|
|
5
5
|
networks:
|
|
6
6
|
- main
|
|
7
|
-
ports:
|
|
8
|
-
- '6379:6379'
|
|
9
7
|
volumes:
|
|
10
8
|
- redis:/data
|
|
11
9
|
healthcheck:
|
|
@@ -48,8 +46,6 @@ services:
|
|
|
48
46
|
image: temporalio/auto-setup:latest
|
|
49
47
|
networks:
|
|
50
48
|
- main
|
|
51
|
-
ports:
|
|
52
|
-
- '7233:7233'
|
|
53
49
|
healthcheck:
|
|
54
50
|
test:
|
|
55
51
|
[
|
|
@@ -73,7 +69,7 @@ services:
|
|
|
73
69
|
networks:
|
|
74
70
|
- main
|
|
75
71
|
ports:
|
|
76
|
-
- '8080:8080'
|
|
72
|
+
- '${OUTPUT_TEMPORAL_UI_HOST_PORT:-8080}:8080'
|
|
77
73
|
|
|
78
74
|
api:
|
|
79
75
|
depends_on:
|
|
@@ -81,7 +77,7 @@ services:
|
|
|
81
77
|
condition: service_healthy
|
|
82
78
|
worker:
|
|
83
79
|
condition: service_healthy
|
|
84
|
-
image: outputai/api:${OUTPUT_API_VERSION:-0.2.1-next.
|
|
80
|
+
image: outputai/api:${OUTPUT_API_VERSION:-0.2.1-next.ef50432.0}
|
|
85
81
|
init: true
|
|
86
82
|
networks:
|
|
87
83
|
- main
|
|
@@ -97,7 +93,7 @@ services:
|
|
|
97
93
|
- OUTPUT_AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY:-}
|
|
98
94
|
- TEMPORAL_ADDRESS=temporal:7233
|
|
99
95
|
ports:
|
|
100
|
-
- '3001:3001'
|
|
96
|
+
- '${OUTPUT_API_HOST_PORT:-3001}:3001'
|
|
101
97
|
|
|
102
98
|
worker:
|
|
103
99
|
depends_on:
|
|
@@ -126,11 +122,11 @@ services:
|
|
|
126
122
|
- OUTPUT_TRACE_HTTP_VERBOSE=${OUTPUT_TRACE_HTTP_VERBOSE:-true}
|
|
127
123
|
- TEMPORAL_ADDRESS=temporal:7233
|
|
128
124
|
- NODE_OPTIONS=${NODE_OPTIONS:---max-old-space-size=4096}
|
|
129
|
-
command: sh -c "corepack enable && npm run output:worker:watch"
|
|
125
|
+
command: sh -c "corepack enable && npm run output:worker:install && npm run output:worker:watch"
|
|
130
126
|
working_dir: /app/${OUTPUT_WORKFLOWS_DIR:-.}
|
|
131
127
|
volumes:
|
|
132
128
|
- ./:/app
|
|
133
|
-
- worker_node_modules:/app
|
|
129
|
+
- worker_node_modules:/app/node_modules
|
|
134
130
|
|
|
135
131
|
volumes:
|
|
136
132
|
postgres:
|
|
@@ -7,8 +7,16 @@ import { validateDockerEnvironment, startDockerCompose, startDockerComposeDetach
|
|
|
7
7
|
import { getErrorMessage } from '#utils/error_utils.js';
|
|
8
8
|
import { ensureClaudePlugin } from '#services/coding_agents.js';
|
|
9
9
|
import { DevApp } from '#views/dev.js';
|
|
10
|
+
import { config } from '#config.js';
|
|
10
11
|
export default class Dev extends Command {
|
|
11
|
-
static description =
|
|
12
|
+
static description = [
|
|
13
|
+
'Start Output development services (auto-restarts worker on file changes)',
|
|
14
|
+
'',
|
|
15
|
+
'To run a second dev stack concurrently, override host ports in .env:',
|
|
16
|
+
'',
|
|
17
|
+
' OUTPUT_API_HOST_PORT=3002',
|
|
18
|
+
' OUTPUT_TEMPORAL_UI_HOST_PORT=8081'
|
|
19
|
+
].join('\n');
|
|
12
20
|
static examples = [
|
|
13
21
|
'<%= config.bin %> <%= command.id %>',
|
|
14
22
|
'<%= config.bin %> <%= command.id %> --compose-file ./custom-docker-compose.yml',
|
|
@@ -38,6 +46,8 @@ export default class Dev extends Command {
|
|
|
38
46
|
// Ensure Claude plugin is configured (fire-and-forget, silent)
|
|
39
47
|
ensureClaudePlugin(process.cwd(), { silent: true }).catch(() => { });
|
|
40
48
|
validateDockerEnvironment();
|
|
49
|
+
// Eagerly resolve ports so InvalidPortError surfaces before Ink mounts.
|
|
50
|
+
void config.ports;
|
|
41
51
|
const dockerComposePath = flags['compose-file'] ?
|
|
42
52
|
path.resolve(process.cwd(), flags['compose-file']) :
|
|
43
53
|
getDefaultDockerComposePath();
|
|
@@ -48,6 +58,7 @@ export default class Dev extends Command {
|
|
|
48
58
|
throw new DockerComposeConfigNotFoundError(dockerComposePath);
|
|
49
59
|
}
|
|
50
60
|
this.log('\n🚀 Starting Output development services...\n');
|
|
61
|
+
this.log(`Docker project name: ${config.dockerServiceName}\n`);
|
|
51
62
|
if (flags['compose-file']) {
|
|
52
63
|
this.log(`Using custom docker-compose file: ${flags['compose-file']}\n`);
|
|
53
64
|
}
|
package/dist/commands/init.d.ts
CHANGED
package/dist/commands/init.js
CHANGED
|
@@ -18,12 +18,16 @@ export default class Init extends Command {
|
|
|
18
18
|
'skip-env': Flags.boolean({
|
|
19
19
|
description: 'Skip interactive environment variable configuration',
|
|
20
20
|
default: false
|
|
21
|
+
}),
|
|
22
|
+
'skip-git': Flags.boolean({
|
|
23
|
+
description: 'Skip git repository initialization',
|
|
24
|
+
default: false
|
|
21
25
|
})
|
|
22
26
|
};
|
|
23
27
|
async run() {
|
|
24
28
|
try {
|
|
25
29
|
const { args, flags } = await this.parse(Init);
|
|
26
|
-
await runInit(flags['skip-env'], args.folderName);
|
|
30
|
+
await runInit(flags['skip-env'], flags['skip-git'], args.folderName);
|
|
27
31
|
}
|
|
28
32
|
catch (error) {
|
|
29
33
|
if (error instanceof UserCancelledError) {
|
|
@@ -37,7 +37,7 @@ describe('init command', () => {
|
|
|
37
37
|
Object.defineProperty(cmd, 'parse', {
|
|
38
38
|
value: vi.fn().mockResolvedValue({
|
|
39
39
|
args: { folderName: undefined, ...parsedArgs },
|
|
40
|
-
flags: { 'skip-env': false, ...flags }
|
|
40
|
+
flags: { 'skip-env': false, 'skip-git': false, ...flags }
|
|
41
41
|
}),
|
|
42
42
|
configurable: true
|
|
43
43
|
});
|
|
@@ -52,10 +52,10 @@ describe('init command', () => {
|
|
|
52
52
|
expect(cmd.run).toBeDefined();
|
|
53
53
|
expect(typeof cmd.run).toBe('function');
|
|
54
54
|
});
|
|
55
|
-
it('should call runInit with skip-env flag and folder name', async () => {
|
|
55
|
+
it('should call runInit with skip-env flag, skip-git flag, and folder name', async () => {
|
|
56
56
|
const cmd = createTestCommand();
|
|
57
57
|
await cmd.run();
|
|
58
|
-
expect(projectScaffold.runInit).toHaveBeenCalledWith(false, undefined);
|
|
58
|
+
expect(projectScaffold.runInit).toHaveBeenCalledWith(false, false, undefined);
|
|
59
59
|
});
|
|
60
60
|
it('should handle UserCancelledError', async () => {
|
|
61
61
|
const error = new UserCancelledError();
|
|
@@ -89,12 +89,17 @@ describe('init command', () => {
|
|
|
89
89
|
it('should pass skip-env flag to runInit when --skip-env is provided', async () => {
|
|
90
90
|
const cmd = createTestCommand([], { 'skip-env': true });
|
|
91
91
|
await cmd.run();
|
|
92
|
-
expect(projectScaffold.runInit).toHaveBeenCalledWith(true, undefined);
|
|
92
|
+
expect(projectScaffold.runInit).toHaveBeenCalledWith(true, false, undefined);
|
|
93
|
+
});
|
|
94
|
+
it('should pass skip-git flag to runInit when --skip-git is provided', async () => {
|
|
95
|
+
const cmd = createTestCommand([], { 'skip-git': true });
|
|
96
|
+
await cmd.run();
|
|
97
|
+
expect(projectScaffold.runInit).toHaveBeenCalledWith(false, true, undefined);
|
|
93
98
|
});
|
|
94
99
|
it('should pass folder name to runInit when provided', async () => {
|
|
95
100
|
const cmd = createTestCommand([], {}, { folderName: 'my-project' });
|
|
96
101
|
await cmd.run();
|
|
97
|
-
expect(projectScaffold.runInit).toHaveBeenCalledWith(false, 'my-project');
|
|
102
|
+
expect(projectScaffold.runInit).toHaveBeenCalledWith(false, false, 'my-project');
|
|
98
103
|
});
|
|
99
104
|
});
|
|
100
105
|
});
|
|
@@ -8,7 +8,7 @@ export default class WorkflowRun extends Command {
|
|
|
8
8
|
};
|
|
9
9
|
static flags: {
|
|
10
10
|
input: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
-
|
|
11
|
+
catalog: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
12
|
format: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
13
|
};
|
|
14
14
|
run(): Promise<void>;
|
|
@@ -35,7 +35,7 @@ export default class WorkflowRun extends Command {
|
|
|
35
35
|
'<%= config.bin %> <%= command.id %> simple my_scenario --format json',
|
|
36
36
|
'<%= config.bin %> <%= command.id %> simple --input \'{"values":[1,2,3]}\'',
|
|
37
37
|
'<%= config.bin %> <%= command.id %> simple --input input.json',
|
|
38
|
-
'<%= config.bin %> <%= command.id %> simple --input \'{"key":"value"}\' --
|
|
38
|
+
'<%= config.bin %> <%= command.id %> simple --input \'{"key":"value"}\' --catalog my-catalog'
|
|
39
39
|
];
|
|
40
40
|
static args = {
|
|
41
41
|
workflowName: Args.string({
|
|
@@ -53,9 +53,12 @@ export default class WorkflowRun extends Command {
|
|
|
53
53
|
description: 'Workflow input as JSON string or file path (overrides scenario)',
|
|
54
54
|
required: false
|
|
55
55
|
}),
|
|
56
|
-
|
|
57
|
-
char: '
|
|
58
|
-
|
|
56
|
+
catalog: Flags.string({
|
|
57
|
+
char: 'c',
|
|
58
|
+
aliases: ['task-queue'],
|
|
59
|
+
charAliases: ['q'],
|
|
60
|
+
deprecateAliases: true,
|
|
61
|
+
description: 'Catalog name for workflow execution (defaults to OUTPUT_CATALOG_ID)',
|
|
59
62
|
env: 'OUTPUT_CATALOG_ID'
|
|
60
63
|
}),
|
|
61
64
|
format: Flags.string({
|
|
@@ -73,7 +76,7 @@ export default class WorkflowRun extends Command {
|
|
|
73
76
|
body: {
|
|
74
77
|
workflowName: args.workflowName,
|
|
75
78
|
input,
|
|
76
|
-
|
|
79
|
+
catalog: flags.catalog
|
|
77
80
|
},
|
|
78
81
|
options: { config: { timeout: 600000 } },
|
|
79
82
|
log: msg => this.log(msg)
|
|
@@ -27,7 +27,7 @@ describe('workflow run command', () => {
|
|
|
27
27
|
expect(WorkflowRun.args).toHaveProperty('workflowName');
|
|
28
28
|
expect(WorkflowRun.flags).toHaveProperty('input');
|
|
29
29
|
expect(WorkflowRun.flags).toHaveProperty('format');
|
|
30
|
-
expect(WorkflowRun.flags).toHaveProperty('
|
|
30
|
+
expect(WorkflowRun.flags).toHaveProperty('catalog');
|
|
31
31
|
});
|
|
32
32
|
it('should have correct flag configuration', async () => {
|
|
33
33
|
const WorkflowRun = (await import('./run.js')).default;
|
|
@@ -53,7 +53,7 @@ describe('workflow run command', () => {
|
|
|
53
53
|
});
|
|
54
54
|
cmd.parse = vi.fn().mockResolvedValue({
|
|
55
55
|
args: { workflowName: 'my_workflow', scenario: undefined },
|
|
56
|
-
flags: { input: undefined,
|
|
56
|
+
flags: { input: undefined, catalog: undefined, format: 'text' }
|
|
57
57
|
});
|
|
58
58
|
return { cmd, postWorkflowRun: vi.mocked(postWorkflowRun), resolveInput: vi.mocked(resolveInput) };
|
|
59
59
|
};
|
|
@@ -67,7 +67,7 @@ describe('workflow run command', () => {
|
|
|
67
67
|
});
|
|
68
68
|
await cmd.run();
|
|
69
69
|
expect(postWorkflowRun).toHaveBeenCalledTimes(1);
|
|
70
|
-
expect(postWorkflowRun).toHaveBeenCalledWith({ workflowName: 'my_workflow', input: { key: 'value' },
|
|
70
|
+
expect(postWorkflowRun).toHaveBeenCalledWith({ workflowName: 'my_workflow', input: { key: 'value' }, catalog: undefined }, expect.objectContaining({ config: { timeout: 600000 } }));
|
|
71
71
|
expect(cmd.log).toHaveBeenCalledWith('Executing workflow: my_workflow...');
|
|
72
72
|
expect(cmd.log).toHaveBeenCalledWith(expect.stringMatching(/\n/));
|
|
73
73
|
});
|
|
@@ -6,6 +6,7 @@ export default class WorkflowRunsList extends Command {
|
|
|
6
6
|
workflowName: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
7
7
|
};
|
|
8
8
|
static flags: {
|
|
9
|
+
catalog: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
10
|
limit: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
11
|
format: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
12
|
};
|
|
@@ -55,6 +55,7 @@ export default class WorkflowRunsList extends Command {
|
|
|
55
55
|
'<%= config.bin %> <%= command.id %>',
|
|
56
56
|
'<%= config.bin %> <%= command.id %> simple',
|
|
57
57
|
'<%= config.bin %> <%= command.id %> simple --limit 10',
|
|
58
|
+
'<%= config.bin %> <%= command.id %> --catalog my-catalog',
|
|
58
59
|
'<%= config.bin %> <%= command.id %> --format json',
|
|
59
60
|
'<%= config.bin %> <%= command.id %> --format table'
|
|
60
61
|
];
|
|
@@ -65,6 +66,11 @@ export default class WorkflowRunsList extends Command {
|
|
|
65
66
|
})
|
|
66
67
|
};
|
|
67
68
|
static flags = {
|
|
69
|
+
catalog: Flags.string({
|
|
70
|
+
char: 'c',
|
|
71
|
+
description: 'Filter runs by catalog (defaults to OUTPUT_CATALOG_ID)',
|
|
72
|
+
env: 'OUTPUT_CATALOG_ID'
|
|
73
|
+
}),
|
|
68
74
|
limit: Flags.integer({
|
|
69
75
|
char: 'l',
|
|
70
76
|
description: 'Maximum number of runs to return',
|
|
@@ -81,6 +87,7 @@ export default class WorkflowRunsList extends Command {
|
|
|
81
87
|
const { args, flags } = await this.parse(WorkflowRunsList);
|
|
82
88
|
const { runs, count } = await fetchWorkflowRuns({
|
|
83
89
|
workflowType: args.workflowName,
|
|
90
|
+
catalog: flags.catalog,
|
|
84
91
|
limit: flags.limit
|
|
85
92
|
});
|
|
86
93
|
if (runs.length === 0) {
|
|
@@ -8,7 +8,7 @@ export default class WorkflowStart extends Command {
|
|
|
8
8
|
};
|
|
9
9
|
static flags: {
|
|
10
10
|
input: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
-
|
|
11
|
+
catalog: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
12
|
};
|
|
13
13
|
run(): Promise<void>;
|
|
14
14
|
catch(error: Error): Promise<void>;
|
|
@@ -8,7 +8,7 @@ export default class WorkflowStart extends Command {
|
|
|
8
8
|
'<%= config.bin %> <%= command.id %> simple basic_input',
|
|
9
9
|
'<%= config.bin %> <%= command.id %> simple --input \'{"values":[1,2,3]}\'',
|
|
10
10
|
'<%= config.bin %> <%= command.id %> simple --input input.json',
|
|
11
|
-
'<%= config.bin %> <%= command.id %> simple --input \'{"key":"value"}\' --
|
|
11
|
+
'<%= config.bin %> <%= command.id %> simple --input \'{"key":"value"}\' --catalog my-catalog'
|
|
12
12
|
];
|
|
13
13
|
static args = {
|
|
14
14
|
workflowName: Args.string({
|
|
@@ -26,9 +26,12 @@ export default class WorkflowStart extends Command {
|
|
|
26
26
|
description: 'Workflow input as JSON string or file path (overrides scenario)',
|
|
27
27
|
required: false
|
|
28
28
|
}),
|
|
29
|
-
|
|
30
|
-
char: '
|
|
31
|
-
|
|
29
|
+
catalog: Flags.string({
|
|
30
|
+
char: 'c',
|
|
31
|
+
aliases: ['task-queue'],
|
|
32
|
+
charAliases: ['q'],
|
|
33
|
+
deprecateAliases: true,
|
|
34
|
+
description: 'Catalog name for workflow execution (defaults to OUTPUT_CATALOG_ID)',
|
|
32
35
|
env: 'OUTPUT_CATALOG_ID'
|
|
33
36
|
})
|
|
34
37
|
};
|
|
@@ -39,7 +42,7 @@ export default class WorkflowStart extends Command {
|
|
|
39
42
|
const response = await postWorkflowStart({
|
|
40
43
|
workflowName: args.workflowName,
|
|
41
44
|
input,
|
|
42
|
-
|
|
45
|
+
catalog: flags.catalog
|
|
43
46
|
});
|
|
44
47
|
if (!response || !response.data) {
|
|
45
48
|
this.error('API returned invalid response', { exit: 1 });
|
|
@@ -14,7 +14,7 @@ describe('workflow start command', () => {
|
|
|
14
14
|
expect(WorkflowStart.description).toContain('Start a workflow');
|
|
15
15
|
expect(WorkflowStart.args).toHaveProperty('workflowName');
|
|
16
16
|
expect(WorkflowStart.flags).toHaveProperty('input');
|
|
17
|
-
expect(WorkflowStart.flags).toHaveProperty('
|
|
17
|
+
expect(WorkflowStart.flags).toHaveProperty('catalog');
|
|
18
18
|
});
|
|
19
19
|
it('should have correct flag configuration', async () => {
|
|
20
20
|
const WorkflowStart = (await import('./start.js')).default;
|
package/dist/config.d.ts
CHANGED
package/dist/config.js
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
|
+
import { parsePort } from '#utils/validation.js';
|
|
2
|
+
const DEFAULT_API_PORT = 3001;
|
|
3
|
+
const DEFAULT_TEMPORAL_UI_PORT = 8080;
|
|
1
4
|
export const config = {
|
|
2
5
|
get apiUrl() {
|
|
3
|
-
return process.env.OUTPUT_API_URL ||
|
|
6
|
+
return process.env.OUTPUT_API_URL || `http://localhost:${this.ports.api}`;
|
|
7
|
+
},
|
|
8
|
+
get ports() {
|
|
9
|
+
return {
|
|
10
|
+
temporalUi: parsePort(process.env.OUTPUT_TEMPORAL_UI_HOST_PORT, DEFAULT_TEMPORAL_UI_PORT, 'OUTPUT_TEMPORAL_UI_HOST_PORT'),
|
|
11
|
+
api: parsePort(process.env.OUTPUT_API_HOST_PORT, DEFAULT_API_PORT, 'OUTPUT_API_HOST_PORT')
|
|
12
|
+
};
|
|
13
|
+
},
|
|
14
|
+
get temporalUiUrl() {
|
|
15
|
+
return `http://localhost:${this.ports.temporalUi}`;
|
|
4
16
|
},
|
|
5
17
|
get apiToken() {
|
|
6
18
|
return process.env.OUTPUT_API_AUTH_TOKEN;
|
package/dist/config.spec.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
2
|
import { config } from '#config.js';
|
|
3
|
+
import { InvalidPortError } from '#utils/validation.js';
|
|
3
4
|
describe('config', () => {
|
|
4
5
|
const envVars = [
|
|
5
6
|
'OUTPUT_API_URL',
|
|
7
|
+
'OUTPUT_API_HOST_PORT',
|
|
8
|
+
'OUTPUT_TEMPORAL_UI_HOST_PORT',
|
|
6
9
|
'OUTPUT_API_AUTH_TOKEN',
|
|
7
10
|
'DOCKER_SERVICE_NAME',
|
|
8
11
|
'OUTPUT_DEBUG',
|
|
@@ -36,14 +39,65 @@ describe('config', () => {
|
|
|
36
39
|
});
|
|
37
40
|
it('falls back to defaults when env vars are unset', () => {
|
|
38
41
|
delete process.env.OUTPUT_API_URL;
|
|
42
|
+
delete process.env.OUTPUT_API_HOST_PORT;
|
|
43
|
+
delete process.env.OUTPUT_TEMPORAL_UI_HOST_PORT;
|
|
39
44
|
delete process.env.DOCKER_SERVICE_NAME;
|
|
40
45
|
delete process.env.OUTPUT_DEBUG;
|
|
41
46
|
delete process.env.OUTPUT_CLI_ENV;
|
|
42
47
|
expect(config.apiUrl).toBe('http://localhost:3001');
|
|
48
|
+
expect(config.ports).toEqual({ temporalUi: 8080, api: 3001 });
|
|
49
|
+
expect(config.temporalUiUrl).toBe('http://localhost:8080');
|
|
43
50
|
expect(config.dockerServiceName).toBe('output-sdk');
|
|
44
51
|
expect(config.debugMode).toBe(false);
|
|
45
52
|
expect(config.envFile).toBe('.env');
|
|
46
53
|
});
|
|
54
|
+
it('derives apiUrl from OUTPUT_API_HOST_PORT when OUTPUT_API_URL is unset', () => {
|
|
55
|
+
delete process.env.OUTPUT_API_URL;
|
|
56
|
+
process.env.OUTPUT_API_HOST_PORT = '3002';
|
|
57
|
+
expect(config.apiUrl).toBe('http://localhost:3002');
|
|
58
|
+
});
|
|
59
|
+
it('OUTPUT_API_URL takes precedence over OUTPUT_API_HOST_PORT', () => {
|
|
60
|
+
process.env.OUTPUT_API_URL = 'https://api.example.com';
|
|
61
|
+
process.env.OUTPUT_API_HOST_PORT = '3002';
|
|
62
|
+
expect(config.apiUrl).toBe('https://api.example.com');
|
|
63
|
+
});
|
|
64
|
+
it('empty-string OUTPUT_API_URL falls through to OUTPUT_API_HOST_PORT (|| not ??)', () => {
|
|
65
|
+
process.env.OUTPUT_API_URL = '';
|
|
66
|
+
process.env.OUTPUT_API_HOST_PORT = '3002';
|
|
67
|
+
expect(config.apiUrl).toBe('http://localhost:3002');
|
|
68
|
+
});
|
|
69
|
+
it('reads port overrides from env vars', () => {
|
|
70
|
+
process.env.OUTPUT_TEMPORAL_UI_HOST_PORT = '8081';
|
|
71
|
+
process.env.OUTPUT_API_HOST_PORT = '3002';
|
|
72
|
+
expect(config.ports).toEqual({ temporalUi: 8081, api: 3002 });
|
|
73
|
+
expect(config.temporalUiUrl).toBe('http://localhost:8081');
|
|
74
|
+
});
|
|
75
|
+
it('treats empty-string port env vars as unset (matches Compose semantics)', () => {
|
|
76
|
+
delete process.env.OUTPUT_API_URL;
|
|
77
|
+
process.env.OUTPUT_API_HOST_PORT = '';
|
|
78
|
+
process.env.OUTPUT_TEMPORAL_UI_HOST_PORT = '';
|
|
79
|
+
expect(config.apiUrl).toBe('http://localhost:3001');
|
|
80
|
+
expect(config.ports).toEqual({ temporalUi: 8080, api: 3001 });
|
|
81
|
+
expect(config.temporalUiUrl).toBe('http://localhost:8080');
|
|
82
|
+
});
|
|
83
|
+
it('throws InvalidPortError on non-numeric port values', () => {
|
|
84
|
+
delete process.env.OUTPUT_API_URL;
|
|
85
|
+
process.env.OUTPUT_API_HOST_PORT = 'abc';
|
|
86
|
+
expect(() => config.ports).toThrow(InvalidPortError);
|
|
87
|
+
expect(() => config.apiUrl).toThrow(InvalidPortError);
|
|
88
|
+
});
|
|
89
|
+
it('throws InvalidPortError on out-of-range port values', () => {
|
|
90
|
+
process.env.OUTPUT_API_HOST_PORT = '99999';
|
|
91
|
+
expect(() => config.ports).toThrow(InvalidPortError);
|
|
92
|
+
});
|
|
93
|
+
it('throws InvalidPortError on trailing-junk port values', () => {
|
|
94
|
+
process.env.OUTPUT_TEMPORAL_UI_HOST_PORT = '8080abc';
|
|
95
|
+
expect(() => config.ports).toThrow(InvalidPortError);
|
|
96
|
+
});
|
|
97
|
+
it('throws InvalidPortError on port 0 (Compose treats 0 as ephemeral - prevents CLI/Docker desync)', () => {
|
|
98
|
+
process.env.OUTPUT_API_HOST_PORT = '0';
|
|
99
|
+
expect(() => config.ports).toThrow(InvalidPortError);
|
|
100
|
+
});
|
|
47
101
|
it('reads apiToken from env', () => {
|
|
48
102
|
process.env.OUTPUT_API_AUTH_TOKEN = 'test-token-123';
|
|
49
103
|
expect(config.apiToken).toBe('test-token-123');
|
package/dist/hooks/init.d.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
import { Hook } from '@oclif/core';
|
|
2
|
+
export declare const INTERACTIVE_FLAGS: string[];
|
|
3
|
+
export declare const GLOBAL_FLAGS: Set<string>;
|
|
4
|
+
export declare const hasInteractiveFlag: (argv: string[]) => boolean;
|
|
5
|
+
export declare const stripGlobalFlags: (argv: string[]) => void;
|
|
2
6
|
declare const hook: Hook<'init'>;
|
|
3
7
|
export default hook;
|
package/dist/hooks/init.js
CHANGED
|
@@ -1,8 +1,20 @@
|
|
|
1
1
|
import { ux } from '@oclif/core';
|
|
2
2
|
import { checkForUpdate } from '#services/version_check.js';
|
|
3
3
|
import { setNonInteractive } from '#utils/interactive.js';
|
|
4
|
-
const
|
|
5
|
-
|
|
4
|
+
export const INTERACTIVE_FLAGS = ['--yes', '--non-interactive'];
|
|
5
|
+
export const GLOBAL_FLAGS = new Set(INTERACTIVE_FLAGS);
|
|
6
|
+
export const hasInteractiveFlag = (argv) => argv.some(arg => INTERACTIVE_FLAGS.includes(arg));
|
|
7
|
+
export const stripGlobalFlags = (argv) => {
|
|
8
|
+
const kept = argv.filter(arg => !GLOBAL_FLAGS.has(arg));
|
|
9
|
+
if (kept.length !== argv.length) {
|
|
10
|
+
argv.splice(0, argv.length, ...kept);
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
const hook = async function (opts) {
|
|
14
|
+
const interactive = hasInteractiveFlag(opts.argv) || hasInteractiveFlag(process.argv);
|
|
15
|
+
stripGlobalFlags(opts.argv);
|
|
16
|
+
stripGlobalFlags(process.argv);
|
|
17
|
+
if (interactive) {
|
|
6
18
|
setNonInteractive(true);
|
|
7
19
|
}
|
|
8
20
|
try {
|