@outputai/cli 0.2.1-next.af8a069.0 → 0.2.1-next.b87b58f.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 (71) hide show
  1. package/bin/run.js +4 -2
  2. package/dist/api/generated/api.d.ts +160 -7
  3. package/dist/api/generated/api.js +33 -1
  4. package/dist/api/http_client.js +24 -19
  5. package/dist/assets/docker/docker-compose-dev.yml +3 -7
  6. package/dist/commands/dev/index.js +12 -1
  7. package/dist/commands/fix.js +1 -1
  8. package/dist/commands/fix.spec.js +2 -2
  9. package/dist/commands/init.d.ts +1 -0
  10. package/dist/commands/init.js +5 -1
  11. package/dist/commands/init.spec.js +10 -5
  12. package/dist/commands/update.js +1 -1
  13. package/dist/commands/update.spec.js +2 -2
  14. package/dist/commands/workflow/plan.js +5 -1
  15. package/dist/commands/workflow/plan.spec.js +3 -2
  16. package/dist/commands/workflow/run.d.ts +1 -1
  17. package/dist/commands/workflow/run.js +8 -5
  18. package/dist/commands/workflow/run.spec.js +3 -3
  19. package/dist/commands/workflow/runs/list.d.ts +1 -0
  20. package/dist/commands/workflow/runs/list.js +7 -0
  21. package/dist/commands/workflow/start.d.ts +1 -1
  22. package/dist/commands/workflow/start.js +8 -5
  23. package/dist/commands/workflow/start.spec.js +1 -1
  24. package/dist/config.d.ts +11 -38
  25. package/dist/config.js +34 -42
  26. package/dist/config.spec.d.ts +1 -0
  27. package/dist/config.spec.js +129 -0
  28. package/dist/generated/framework_version.json +1 -1
  29. package/dist/hooks/init.d.ts +4 -0
  30. package/dist/hooks/init.js +17 -1
  31. package/dist/hooks/init.spec.js +79 -5
  32. package/dist/services/coding_agents.js +5 -1
  33. package/dist/services/coding_agents.spec.js +19 -6
  34. package/dist/services/credentials_configurator.js +1 -1
  35. package/dist/services/docker.js +5 -2
  36. package/dist/services/docker.spec.js +74 -3
  37. package/dist/services/env_configurator.js +1 -1
  38. package/dist/services/env_configurator.spec.js +12 -12
  39. package/dist/services/messages.js +2 -1
  40. package/dist/services/project_scaffold.d.ts +1 -1
  41. package/dist/services/project_scaffold.js +17 -2
  42. package/dist/services/project_scaffold.spec.js +6 -6
  43. package/dist/services/workflow_builder.js +5 -1
  44. package/dist/services/workflow_builder.spec.js +3 -2
  45. package/dist/services/workflow_runs.d.ts +1 -0
  46. package/dist/services/workflow_runs.js +3 -0
  47. package/dist/templates/project/.env.example.template +17 -0
  48. package/dist/utils/credentials_loader.d.ts +1 -0
  49. package/dist/utils/credentials_loader.js +18 -0
  50. package/dist/utils/credentials_loader.spec.d.ts +1 -0
  51. package/dist/utils/credentials_loader.spec.js +84 -0
  52. package/dist/utils/env_loader.js +1 -2
  53. package/dist/utils/error_handler.js +10 -8
  54. package/dist/utils/interactive.d.ts +2 -0
  55. package/dist/utils/interactive.js +5 -0
  56. package/dist/utils/interactive.spec.d.ts +1 -0
  57. package/dist/utils/interactive.spec.js +40 -0
  58. package/dist/utils/prompt.d.ts +17 -0
  59. package/dist/utils/prompt.js +20 -0
  60. package/dist/utils/prompt.spec.d.ts +1 -0
  61. package/dist/utils/prompt.spec.js +70 -0
  62. package/dist/utils/proxy.d.ts +9 -0
  63. package/dist/utils/proxy.js +24 -0
  64. package/dist/utils/proxy.spec.d.ts +1 -0
  65. package/dist/utils/proxy.spec.js +48 -0
  66. package/dist/utils/validation.d.ts +13 -0
  67. package/dist/utils/validation.js +31 -0
  68. package/dist/utils/validation.spec.js +47 -1
  69. package/dist/views/dev.js +3 -3
  70. package/dist/views/workflow/list.js +10 -8
  71. package/package.json +10 -9
package/bin/run.js CHANGED
@@ -2,10 +2,12 @@
2
2
 
3
3
  import { execute } from '@oclif/core';
4
4
  import { loadEnvironment } from '../dist/utils/env_loader.js';
5
- import { resolveCredentialRefs } from '@outputai/credentials';
5
+ import { bootstrapProxy } from '../dist/utils/proxy.js';
6
+ import { loadCredentialRefs } from '../dist/utils/credentials_loader.js';
6
7
 
7
8
  // Load environment variables from .env files before executing CLI
8
9
  loadEnvironment();
9
- resolveCredentialRefs();
10
+ bootstrapProxy();
11
+ loadCredentialRefs();
10
12
 
11
13
  await execute( { dir: import.meta.url } );
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Generated by orval v8.8.0 🍺
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 */
@@ -257,9 +259,9 @@ export interface ResetWorkflowResponse {
257
259
  runId?: string;
258
260
  }
259
261
  /**
260
- * Invalid request body or query (validation failed)
262
+ * Invalid request body, query, or pagination token
261
263
  */
262
- export type BadRequestResponse = ValidationErrorResponse;
264
+ export type BadRequestResponse = ValidationErrorResponse | ErrorResponse;
263
265
  /**
264
266
  * Workflow execution, workflow type, or catalog not found
265
267
  */
@@ -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 name of the task queue to send the workflow to */
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 name of the task queue to send the workflow to */
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 = {
@@ -344,6 +356,86 @@ export type PostWorkflowIdRunsRidTerminateBody = {
344
356
  export type PostWorkflowIdTerminateBody = {
345
357
  reason?: string;
346
358
  };
359
+ export type GetWorkflowIdHistoryParams = {
360
+ /**
361
+ * Specific run ID. Required when using pageToken.
362
+ */
363
+ runId?: string;
364
+ /**
365
+ * Number of events per page
366
+ * @minimum 1
367
+ * @maximum 50
368
+ */
369
+ pageSize?: number;
370
+ /**
371
+ * Base64 pagination token from previous response
372
+ */
373
+ pageToken?: string;
374
+ /**
375
+ * Include decoded input/output payloads in events
376
+ */
377
+ includePayloads?: boolean;
378
+ };
379
+ /**
380
+ * Workflow metadata (null on subsequent pages)
381
+ * @nullable
382
+ */
383
+ export type GetWorkflowIdHistory200Workflow = {
384
+ [key: string]: unknown;
385
+ } | null;
386
+ export type GetWorkflowIdHistory200EventsItem = {
387
+ [key: string]: unknown;
388
+ };
389
+ export type GetWorkflowIdHistory200 = {
390
+ /**
391
+ * Workflow metadata (null on subsequent pages)
392
+ * @nullable
393
+ */
394
+ workflow?: GetWorkflowIdHistory200Workflow;
395
+ events?: GetWorkflowIdHistory200EventsItem[];
396
+ /** Resolved run ID. Echo this value as the runId query parameter when fetching subsequent pages. */
397
+ runId?: string;
398
+ /** @nullable */
399
+ nextPageToken?: string | null;
400
+ };
401
+ export type GetWorkflowIdRunsRidHistoryParams = {
402
+ /**
403
+ * Number of events per page
404
+ * @minimum 1
405
+ * @maximum 50
406
+ */
407
+ pageSize?: number;
408
+ /**
409
+ * Base64 pagination token from previous response
410
+ */
411
+ pageToken?: string;
412
+ /**
413
+ * Include decoded input/output payloads in events
414
+ */
415
+ includePayloads?: boolean;
416
+ };
417
+ /**
418
+ * Workflow metadata (null on subsequent pages)
419
+ * @nullable
420
+ */
421
+ export type GetWorkflowIdRunsRidHistory200Workflow = {
422
+ [key: string]: unknown;
423
+ } | null;
424
+ export type GetWorkflowIdRunsRidHistory200EventsItem = {
425
+ [key: string]: unknown;
426
+ };
427
+ export type GetWorkflowIdRunsRidHistory200 = {
428
+ /**
429
+ * Workflow metadata (null on subsequent pages)
430
+ * @nullable
431
+ */
432
+ workflow?: GetWorkflowIdRunsRidHistory200Workflow;
433
+ events?: GetWorkflowIdRunsRidHistory200EventsItem[];
434
+ /** The pinned run ID */
435
+ runId?: string;
436
+ /** @nullable */
437
+ nextPageToken?: string | null;
438
+ };
347
439
  export type GetWorkflowCatalogId200 = {
348
440
  /** Each workflow available in this catalog */
349
441
  workflows?: Workflow[];
@@ -357,6 +449,10 @@ export type GetWorkflowRunsParams = {
357
449
  * Filter by workflow type/name
358
450
  */
359
451
  workflowType?: string;
452
+ /**
453
+ * Filter by catalog ID (scopes runs to a single worker's catalog/session)
454
+ */
455
+ catalog?: string;
360
456
  /**
361
457
  * Maximum number of runs to return
362
458
  * @minimum 1
@@ -629,7 +725,7 @@ export type postWorkflowIdRunsRidTerminateResponseError = (postWorkflowIdRunsRid
629
725
  };
630
726
  export type postWorkflowIdRunsRidTerminateResponse = (postWorkflowIdRunsRidTerminateResponseSuccess | postWorkflowIdRunsRidTerminateResponseError);
631
727
  export declare const getPostWorkflowIdRunsRidTerminateUrl: (id: string, rid: string) => string;
632
- export declare const postWorkflowIdRunsRidTerminate: (id: string, rid: string, postWorkflowIdRunsRidTerminateBody: PostWorkflowIdRunsRidTerminateBody, options?: ApiRequestOptions) => Promise<postWorkflowIdRunsRidTerminateResponse>;
728
+ export declare const postWorkflowIdRunsRidTerminate: (id: string, rid: string, postWorkflowIdRunsRidTerminateBody?: PostWorkflowIdRunsRidTerminateBody, options?: ApiRequestOptions) => Promise<postWorkflowIdRunsRidTerminateResponse>;
633
729
  /**
634
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.
635
731
  * @deprecated
@@ -663,7 +759,7 @@ export type postWorkflowIdTerminateResponseError = (postWorkflowIdTerminateRespo
663
759
  };
664
760
  export type postWorkflowIdTerminateResponse = (postWorkflowIdTerminateResponseSuccess | postWorkflowIdTerminateResponseError);
665
761
  export declare const getPostWorkflowIdTerminateUrl: (id: string) => string;
666
- export declare const postWorkflowIdTerminate: (id: string, postWorkflowIdTerminateBody: PostWorkflowIdTerminateBody, options?: ApiRequestOptions) => Promise<postWorkflowIdTerminateResponse>;
762
+ export declare const postWorkflowIdTerminate: (id: string, postWorkflowIdTerminateBody?: PostWorkflowIdTerminateBody, options?: ApiRequestOptions) => Promise<postWorkflowIdTerminateResponse>;
667
763
  /**
668
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.
669
765
  * @summary Reset a specific workflow run to re-run from after a completed step
@@ -854,6 +950,63 @@ export type getWorkflowIdRunsRidTraceLogResponseError = (getWorkflowIdRunsRidTra
854
950
  export type getWorkflowIdRunsRidTraceLogResponse = (getWorkflowIdRunsRidTraceLogResponseSuccess | getWorkflowIdRunsRidTraceLogResponseError);
855
951
  export declare const getGetWorkflowIdRunsRidTraceLogUrl: (id: string, rid: string) => string;
856
952
  export declare const getWorkflowIdRunsRidTraceLog: (id: string, rid: string, options?: ApiRequestOptions) => Promise<getWorkflowIdRunsRidTraceLogResponse>;
953
+ /**
954
+ * Returns decoded Temporal history events with optional payload inclusion. First page includes workflow metadata; subsequent pages return events only.
955
+ * @summary Get paginated workflow execution history
956
+ */
957
+ export type getWorkflowIdHistoryResponse200 = {
958
+ data: GetWorkflowIdHistory200;
959
+ status: 200;
960
+ };
961
+ export type getWorkflowIdHistoryResponse400 = {
962
+ data: BadRequestResponse;
963
+ status: 400;
964
+ };
965
+ export type getWorkflowIdHistoryResponse404 = {
966
+ data: NotFoundResponse;
967
+ status: 404;
968
+ };
969
+ export type getWorkflowIdHistoryResponse500 = {
970
+ data: InternalServerErrorResponse;
971
+ status: 500;
972
+ };
973
+ export type getWorkflowIdHistoryResponseSuccess = (getWorkflowIdHistoryResponse200) & {
974
+ headers: Headers;
975
+ };
976
+ export type getWorkflowIdHistoryResponseError = (getWorkflowIdHistoryResponse400 | getWorkflowIdHistoryResponse404 | getWorkflowIdHistoryResponse500) & {
977
+ headers: Headers;
978
+ };
979
+ export type getWorkflowIdHistoryResponse = (getWorkflowIdHistoryResponseSuccess | getWorkflowIdHistoryResponseError);
980
+ export declare const getGetWorkflowIdHistoryUrl: (id: string, params?: GetWorkflowIdHistoryParams) => string;
981
+ export declare const getWorkflowIdHistory: (id: string, params?: GetWorkflowIdHistoryParams, options?: ApiRequestOptions) => Promise<getWorkflowIdHistoryResponse>;
982
+ /**
983
+ * @summary Get paginated workflow execution history for a specific run
984
+ */
985
+ export type getWorkflowIdRunsRidHistoryResponse200 = {
986
+ data: GetWorkflowIdRunsRidHistory200;
987
+ status: 200;
988
+ };
989
+ export type getWorkflowIdRunsRidHistoryResponse400 = {
990
+ data: BadRequestResponse;
991
+ status: 400;
992
+ };
993
+ export type getWorkflowIdRunsRidHistoryResponse404 = {
994
+ data: NotFoundResponse;
995
+ status: 404;
996
+ };
997
+ export type getWorkflowIdRunsRidHistoryResponse500 = {
998
+ data: InternalServerErrorResponse;
999
+ status: 500;
1000
+ };
1001
+ export type getWorkflowIdRunsRidHistoryResponseSuccess = (getWorkflowIdRunsRidHistoryResponse200) & {
1002
+ headers: Headers;
1003
+ };
1004
+ export type getWorkflowIdRunsRidHistoryResponseError = (getWorkflowIdRunsRidHistoryResponse400 | getWorkflowIdRunsRidHistoryResponse404 | getWorkflowIdRunsRidHistoryResponse500) & {
1005
+ headers: Headers;
1006
+ };
1007
+ export type getWorkflowIdRunsRidHistoryResponse = (getWorkflowIdRunsRidHistoryResponseSuccess | getWorkflowIdRunsRidHistoryResponseError);
1008
+ export declare const getGetWorkflowIdRunsRidHistoryUrl: (id: string, rid: string, params?: GetWorkflowIdRunsRidHistoryParams) => string;
1009
+ export declare const getWorkflowIdRunsRidHistory: (id: string, rid: string, params?: GetWorkflowIdRunsRidHistoryParams, options?: ApiRequestOptions) => Promise<getWorkflowIdRunsRidHistoryResponse>;
857
1010
  /**
858
1011
  * @summary Get a specific workflow catalog by ID
859
1012
  */
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Generated by orval v8.8.0 🍺
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
@@ -194,6 +194,38 @@ export const getWorkflowIdRunsRidTraceLog = async (id, rid, options) => {
194
194
  method: 'GET'
195
195
  });
196
196
  };
197
+ export const getGetWorkflowIdHistoryUrl = (id, params) => {
198
+ const normalizedParams = new URLSearchParams();
199
+ Object.entries(params || {}).forEach(([key, value]) => {
200
+ if (value !== undefined) {
201
+ normalizedParams.append(key, value === null ? 'null' : value.toString());
202
+ }
203
+ });
204
+ const stringifiedParams = normalizedParams.toString();
205
+ return stringifiedParams.length > 0 ? `/workflow/${id}/history?${stringifiedParams}` : `/workflow/${id}/history`;
206
+ };
207
+ export const getWorkflowIdHistory = async (id, params, options) => {
208
+ return customFetchInstance(getGetWorkflowIdHistoryUrl(id, params), {
209
+ ...options,
210
+ method: 'GET'
211
+ });
212
+ };
213
+ export const getGetWorkflowIdRunsRidHistoryUrl = (id, rid, params) => {
214
+ const normalizedParams = new URLSearchParams();
215
+ Object.entries(params || {}).forEach(([key, value]) => {
216
+ if (value !== undefined) {
217
+ normalizedParams.append(key, value === null ? 'null' : value.toString());
218
+ }
219
+ });
220
+ const stringifiedParams = normalizedParams.toString();
221
+ return stringifiedParams.length > 0 ? `/workflow/${id}/runs/${rid}/history?${stringifiedParams}` : `/workflow/${id}/runs/${rid}/history`;
222
+ };
223
+ export const getWorkflowIdRunsRidHistory = async (id, rid, params, options) => {
224
+ return customFetchInstance(getGetWorkflowIdRunsRidHistoryUrl(id, rid, params), {
225
+ ...options,
226
+ method: 'GET'
227
+ });
228
+ };
197
229
  export const getGetWorkflowCatalogIdUrl = (id) => {
198
230
  return `/workflow/catalog/${id}`;
199
231
  };
@@ -14,26 +14,31 @@ export class HttpError extends Error {
14
14
  this.name = 'HttpError';
15
15
  }
16
16
  }
17
- const api = ky.create({
18
- prefixUrl: config.apiUrl,
19
- timeout: config.requestTimeout,
20
- retry: {
21
- limit: 2,
22
- methods: ['get', 'put', 'head', 'delete', 'options', 'trace'],
23
- statusCodes: [408, 413, 429, 502, 503, 504]
24
- },
25
- throwHttpErrors: false,
26
- hooks: {
27
- beforeRequest: [
28
- request => {
29
- // Add auth token if available
30
- if (config.apiToken) {
31
- request.headers.set('Authorization', `Basic ${config.apiToken}`);
32
- }
17
+ const apiState = {};
18
+ function getApi() {
19
+ if (!apiState.api) {
20
+ apiState.api = ky.create({
21
+ prefixUrl: config.apiUrl,
22
+ timeout: config.requestTimeout,
23
+ retry: {
24
+ limit: 2,
25
+ methods: ['get', 'put', 'head', 'delete', 'options', 'trace'],
26
+ statusCodes: [408, 413, 429, 502, 503, 504]
27
+ },
28
+ throwHttpErrors: false,
29
+ hooks: {
30
+ beforeRequest: [
31
+ request => {
32
+ if (config.apiToken) {
33
+ request.headers.set('Authorization', `Basic ${config.apiToken}`);
34
+ }
35
+ }
36
+ ]
33
37
  }
34
- ]
38
+ });
35
39
  }
36
- });
40
+ return apiState.api;
41
+ }
37
42
  const stripLeadingSlash = (url) => url.startsWith('/') ? url.slice(1) : url;
38
43
  const buildKyOptions = (options) => {
39
44
  // Extract params, config, and body for special handling
@@ -55,7 +60,7 @@ const wrapResponse = (response, data) => ({
55
60
  headers: response.headers
56
61
  });
57
62
  export const customFetchInstance = async (url, options) => {
58
- const response = await api(stripLeadingSlash(url), buildKyOptions(options));
63
+ const response = await getApi()(stripLeadingSlash(url), buildKyOptions(options));
59
64
  const data = await response.json().catch(() => undefined);
60
65
  // Throw for non-2xx responses so catch handlers can process errors
61
66
  if (!response.ok) {
@@ -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.af8a069.0}
80
+ image: outputai/api:${OUTPUT_API_VERSION:-0.2.1-next.b87b58f.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:
@@ -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 = 'Start Output development services (auto-restarts worker on file changes)';
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
  }
@@ -1,5 +1,5 @@
1
1
  import { Command } from '@oclif/core';
2
- import { confirm } from '@inquirer/prompts';
2
+ import { confirm } from '#utils/prompt.js';
3
3
  import { applyFix, planFix } from '#services/fix_package.js';
4
4
  import { getErrorMessage } from '#utils/error_utils.js';
5
5
  const Ansi = {
@@ -2,12 +2,12 @@
2
2
  import { describe, it, expect, beforeEach, vi } from 'vitest';
3
3
  import Fix from './fix.js';
4
4
  import * as fixService from '#services/fix_package.js';
5
- import { confirm } from '@inquirer/prompts';
5
+ import { confirm } from '#utils/prompt.js';
6
6
  vi.mock('#services/fix_package.js', () => ({
7
7
  planFix: vi.fn(),
8
8
  applyFix: vi.fn()
9
9
  }));
10
- vi.mock('@inquirer/prompts', () => ({
10
+ vi.mock('#utils/prompt.js', () => ({
11
11
  confirm: vi.fn()
12
12
  }));
13
13
  const basePlan = () => ({
@@ -7,6 +7,7 @@ export default class Init extends Command {
7
7
  };
8
8
  static flags: {
9
9
  'skip-env': import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ 'skip-git': import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
11
  };
11
12
  run(): Promise<void>;
12
13
  }
@@ -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
  });
@@ -1,5 +1,5 @@
1
1
  import { Command, Flags } from '@oclif/core';
2
- import { confirm } from '@inquirer/prompts';
2
+ import { confirm } from '#utils/prompt.js';
3
3
  import { fetchLatestVersion, getGlobalInstalledVersion, getLocalInstalledVersion, updateGlobal, updateLocal, isOutdated } from '#services/npm_update_service.js';
4
4
  import { ensureClaudePlugin } from '#services/coding_agents.js';
5
5
  import { getErrorMessage } from '#utils/error_utils.js';
@@ -3,7 +3,7 @@ import { describe, it, expect, beforeEach, vi } from 'vitest';
3
3
  import Update from './update.js';
4
4
  import { fetchLatestVersion, getGlobalInstalledVersion, getLocalInstalledVersion, updateGlobal, updateLocal, isOutdated } from '#services/npm_update_service.js';
5
5
  import { ensureClaudePlugin } from '#services/coding_agents.js';
6
- import { confirm } from '@inquirer/prompts';
6
+ import { confirm } from '#utils/prompt.js';
7
7
  vi.mock('#services/npm_update_service.js', () => ({
8
8
  fetchLatestVersion: vi.fn(),
9
9
  getGlobalInstalledVersion: vi.fn(),
@@ -15,7 +15,7 @@ vi.mock('#services/npm_update_service.js', () => ({
15
15
  vi.mock('#services/coding_agents.js', () => ({
16
16
  ensureClaudePlugin: vi.fn()
17
17
  }));
18
- vi.mock('@inquirer/prompts', () => ({
18
+ vi.mock('#utils/prompt.js', () => ({
19
19
  confirm: vi.fn()
20
20
  }));
21
21
  describe('update command', () => {
@@ -1,5 +1,6 @@
1
1
  import { Command, Flags, ux } from '@oclif/core';
2
- import { input } from '@inquirer/prompts';
2
+ import { input } from '#utils/prompt.js';
3
+ import { isInteractive } from '#utils/interactive.js';
3
4
  import { generatePlanName, updateAgentTemplates, writePlanFile } from '#services/workflow_planner.js';
4
5
  import { ensureOutputAISystem } from '#services/coding_agents.js';
5
6
  import { invokePlanWorkflow, PLAN_COMMAND_OPTIONS, replyToClaude } from '#services/claude_client.js';
@@ -45,6 +46,9 @@ export default class WorkflowPlan extends Command {
45
46
  this.log('=========');
46
47
  this.log(originalPlanContent);
47
48
  this.log('=========');
49
+ if (!isInteractive()) {
50
+ return originalPlanContent;
51
+ }
48
52
  const modifications = await input({
49
53
  message: ux.colorize('gray', `Reply or type ${acceptKey} to accept the plan as is: `),
50
54
  validate: (value) => value.length >= 10 || value === acceptKey
@@ -3,11 +3,12 @@ import WorkflowPlan from './plan.js';
3
3
  import { generatePlanName, writePlanFile, updateAgentTemplates } from '#services/workflow_planner.js';
4
4
  import { ensureOutputAISystem } from '#services/coding_agents.js';
5
5
  import { invokePlanWorkflow, replyToClaude, ClaudeInvocationError } from '#services/claude_client.js';
6
- import { input } from '@inquirer/prompts';
6
+ import { input } from '#utils/prompt.js';
7
7
  vi.mock('#services/workflow_planner.js');
8
8
  vi.mock('#services/coding_agents.js');
9
9
  vi.mock('#services/claude_client.js');
10
- vi.mock('@inquirer/prompts');
10
+ vi.mock('#utils/prompt.js');
11
+ vi.mock('#utils/interactive.js', () => ({ isInteractive: () => true }));
11
12
  describe('WorkflowPlan Command', () => {
12
13
  const createCommand = () => {
13
14
  const cmd = new WorkflowPlan([], {});
@@ -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
- 'task-queue': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
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"}\' --task-queue my-queue'
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
- 'task-queue': Flags.string({
57
- char: 'q',
58
- description: 'Task queue name for workflow execution (defaults to OUTPUT_CATALOG_ID)',
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
- taskQueue: flags['task-queue']
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('task-queue');
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, 'task-queue': undefined, format: 'text' }
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' }, taskQueue: undefined }, expect.objectContaining({ config: { timeout: 600000 } }));
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
  });