@outputai/cli 0.2.1-next.af8a069.0 → 0.2.1-next.eadab44.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 (48) hide show
  1. package/bin/run.js +2 -0
  2. package/dist/api/generated/api.d.ts +141 -2
  3. package/dist/api/generated/api.js +32 -0
  4. package/dist/api/http_client.js +24 -19
  5. package/dist/assets/docker/docker-compose-dev.yml +1 -1
  6. package/dist/commands/fix.js +1 -1
  7. package/dist/commands/fix.spec.js +2 -2
  8. package/dist/commands/init.d.ts +1 -0
  9. package/dist/commands/init.js +5 -1
  10. package/dist/commands/init.spec.js +10 -5
  11. package/dist/commands/update.js +1 -1
  12. package/dist/commands/update.spec.js +2 -2
  13. package/dist/commands/workflow/plan.js +5 -1
  14. package/dist/commands/workflow/plan.spec.js +3 -2
  15. package/dist/config.d.ts +6 -38
  16. package/dist/config.js +22 -42
  17. package/dist/config.spec.d.ts +1 -0
  18. package/dist/config.spec.js +75 -0
  19. package/dist/generated/framework_version.json +1 -1
  20. package/dist/hooks/init.d.ts +4 -0
  21. package/dist/hooks/init.js +17 -1
  22. package/dist/hooks/init.spec.js +79 -5
  23. package/dist/services/coding_agents.js +5 -1
  24. package/dist/services/coding_agents.spec.js +19 -6
  25. package/dist/services/credentials_configurator.js +1 -1
  26. package/dist/services/env_configurator.js +1 -1
  27. package/dist/services/env_configurator.spec.js +12 -12
  28. package/dist/services/project_scaffold.d.ts +1 -1
  29. package/dist/services/project_scaffold.js +17 -2
  30. package/dist/services/project_scaffold.spec.js +6 -6
  31. package/dist/services/workflow_builder.js +5 -1
  32. package/dist/services/workflow_builder.spec.js +3 -2
  33. package/dist/utils/env_loader.js +1 -2
  34. package/dist/utils/error_handler.js +10 -8
  35. package/dist/utils/interactive.d.ts +2 -0
  36. package/dist/utils/interactive.js +5 -0
  37. package/dist/utils/interactive.spec.d.ts +1 -0
  38. package/dist/utils/interactive.spec.js +40 -0
  39. package/dist/utils/prompt.d.ts +17 -0
  40. package/dist/utils/prompt.js +20 -0
  41. package/dist/utils/prompt.spec.d.ts +1 -0
  42. package/dist/utils/prompt.spec.js +70 -0
  43. package/dist/utils/proxy.d.ts +9 -0
  44. package/dist/utils/proxy.js +24 -0
  45. package/dist/utils/proxy.spec.d.ts +1 -0
  46. package/dist/utils/proxy.spec.js +48 -0
  47. package/dist/views/workflow/list.js +8 -6
  48. package/package.json +5 -4
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 { bootstrapProxy } from '../dist/utils/proxy.js';
5
6
  import { resolveCredentialRefs } from '@outputai/credentials';
6
7
 
7
8
  // Load environment variables from .env files before executing CLI
8
9
  loadEnvironment();
10
+ bootstrapProxy();
9
11
  resolveCredentialRefs();
10
12
 
11
13
  await execute( { dir: import.meta.url } );
@@ -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
  */
@@ -344,6 +346,86 @@ export type PostWorkflowIdRunsRidTerminateBody = {
344
346
  export type PostWorkflowIdTerminateBody = {
345
347
  reason?: string;
346
348
  };
349
+ export type GetWorkflowIdHistoryParams = {
350
+ /**
351
+ * Specific run ID. Required when using pageToken.
352
+ */
353
+ runId?: string;
354
+ /**
355
+ * Number of events per page
356
+ * @minimum 1
357
+ * @maximum 50
358
+ */
359
+ pageSize?: number;
360
+ /**
361
+ * Base64 pagination token from previous response
362
+ */
363
+ pageToken?: string;
364
+ /**
365
+ * Include decoded input/output payloads in events
366
+ */
367
+ includePayloads?: boolean;
368
+ };
369
+ /**
370
+ * Workflow metadata (null on subsequent pages)
371
+ * @nullable
372
+ */
373
+ export type GetWorkflowIdHistory200Workflow = {
374
+ [key: string]: unknown;
375
+ } | null;
376
+ export type GetWorkflowIdHistory200EventsItem = {
377
+ [key: string]: unknown;
378
+ };
379
+ export type GetWorkflowIdHistory200 = {
380
+ /**
381
+ * Workflow metadata (null on subsequent pages)
382
+ * @nullable
383
+ */
384
+ workflow?: GetWorkflowIdHistory200Workflow;
385
+ events?: GetWorkflowIdHistory200EventsItem[];
386
+ /** Resolved run ID. Echo this value as the runId query parameter when fetching subsequent pages. */
387
+ runId?: string;
388
+ /** @nullable */
389
+ nextPageToken?: string | null;
390
+ };
391
+ export type GetWorkflowIdRunsRidHistoryParams = {
392
+ /**
393
+ * Number of events per page
394
+ * @minimum 1
395
+ * @maximum 50
396
+ */
397
+ pageSize?: number;
398
+ /**
399
+ * Base64 pagination token from previous response
400
+ */
401
+ pageToken?: string;
402
+ /**
403
+ * Include decoded input/output payloads in events
404
+ */
405
+ includePayloads?: boolean;
406
+ };
407
+ /**
408
+ * Workflow metadata (null on subsequent pages)
409
+ * @nullable
410
+ */
411
+ export type GetWorkflowIdRunsRidHistory200Workflow = {
412
+ [key: string]: unknown;
413
+ } | null;
414
+ export type GetWorkflowIdRunsRidHistory200EventsItem = {
415
+ [key: string]: unknown;
416
+ };
417
+ export type GetWorkflowIdRunsRidHistory200 = {
418
+ /**
419
+ * Workflow metadata (null on subsequent pages)
420
+ * @nullable
421
+ */
422
+ workflow?: GetWorkflowIdRunsRidHistory200Workflow;
423
+ events?: GetWorkflowIdRunsRidHistory200EventsItem[];
424
+ /** The pinned run ID */
425
+ runId?: string;
426
+ /** @nullable */
427
+ nextPageToken?: string | null;
428
+ };
347
429
  export type GetWorkflowCatalogId200 = {
348
430
  /** Each workflow available in this catalog */
349
431
  workflows?: Workflow[];
@@ -854,6 +936,63 @@ export type getWorkflowIdRunsRidTraceLogResponseError = (getWorkflowIdRunsRidTra
854
936
  export type getWorkflowIdRunsRidTraceLogResponse = (getWorkflowIdRunsRidTraceLogResponseSuccess | getWorkflowIdRunsRidTraceLogResponseError);
855
937
  export declare const getGetWorkflowIdRunsRidTraceLogUrl: (id: string, rid: string) => string;
856
938
  export declare const getWorkflowIdRunsRidTraceLog: (id: string, rid: string, options?: ApiRequestOptions) => Promise<getWorkflowIdRunsRidTraceLogResponse>;
939
+ /**
940
+ * Returns decoded Temporal history events with optional payload inclusion. First page includes workflow metadata; subsequent pages return events only.
941
+ * @summary Get paginated workflow execution history
942
+ */
943
+ export type getWorkflowIdHistoryResponse200 = {
944
+ data: GetWorkflowIdHistory200;
945
+ status: 200;
946
+ };
947
+ export type getWorkflowIdHistoryResponse400 = {
948
+ data: BadRequestResponse;
949
+ status: 400;
950
+ };
951
+ export type getWorkflowIdHistoryResponse404 = {
952
+ data: NotFoundResponse;
953
+ status: 404;
954
+ };
955
+ export type getWorkflowIdHistoryResponse500 = {
956
+ data: InternalServerErrorResponse;
957
+ status: 500;
958
+ };
959
+ export type getWorkflowIdHistoryResponseSuccess = (getWorkflowIdHistoryResponse200) & {
960
+ headers: Headers;
961
+ };
962
+ export type getWorkflowIdHistoryResponseError = (getWorkflowIdHistoryResponse400 | getWorkflowIdHistoryResponse404 | getWorkflowIdHistoryResponse500) & {
963
+ headers: Headers;
964
+ };
965
+ export type getWorkflowIdHistoryResponse = (getWorkflowIdHistoryResponseSuccess | getWorkflowIdHistoryResponseError);
966
+ export declare const getGetWorkflowIdHistoryUrl: (id: string, params?: GetWorkflowIdHistoryParams) => string;
967
+ export declare const getWorkflowIdHistory: (id: string, params?: GetWorkflowIdHistoryParams, options?: ApiRequestOptions) => Promise<getWorkflowIdHistoryResponse>;
968
+ /**
969
+ * @summary Get paginated workflow execution history for a specific run
970
+ */
971
+ export type getWorkflowIdRunsRidHistoryResponse200 = {
972
+ data: GetWorkflowIdRunsRidHistory200;
973
+ status: 200;
974
+ };
975
+ export type getWorkflowIdRunsRidHistoryResponse400 = {
976
+ data: BadRequestResponse;
977
+ status: 400;
978
+ };
979
+ export type getWorkflowIdRunsRidHistoryResponse404 = {
980
+ data: NotFoundResponse;
981
+ status: 404;
982
+ };
983
+ export type getWorkflowIdRunsRidHistoryResponse500 = {
984
+ data: InternalServerErrorResponse;
985
+ status: 500;
986
+ };
987
+ export type getWorkflowIdRunsRidHistoryResponseSuccess = (getWorkflowIdRunsRidHistoryResponse200) & {
988
+ headers: Headers;
989
+ };
990
+ export type getWorkflowIdRunsRidHistoryResponseError = (getWorkflowIdRunsRidHistoryResponse400 | getWorkflowIdRunsRidHistoryResponse404 | getWorkflowIdRunsRidHistoryResponse500) & {
991
+ headers: Headers;
992
+ };
993
+ export type getWorkflowIdRunsRidHistoryResponse = (getWorkflowIdRunsRidHistoryResponseSuccess | getWorkflowIdRunsRidHistoryResponseError);
994
+ export declare const getGetWorkflowIdRunsRidHistoryUrl: (id: string, rid: string, params?: GetWorkflowIdRunsRidHistoryParams) => string;
995
+ export declare const getWorkflowIdRunsRidHistory: (id: string, rid: string, params?: GetWorkflowIdRunsRidHistoryParams, options?: ApiRequestOptions) => Promise<getWorkflowIdRunsRidHistoryResponse>;
857
996
  /**
858
997
  * @summary Get a specific workflow catalog by ID
859
998
  */
@@ -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) {
@@ -81,7 +81,7 @@ services:
81
81
  condition: service_healthy
82
82
  worker:
83
83
  condition: service_healthy
84
- image: outputai/api:${OUTPUT_API_VERSION:-0.2.1-next.af8a069.0}
84
+ image: outputai/api:${OUTPUT_API_VERSION:-0.2.1-next.eadab44.0}
85
85
  init: true
86
86
  networks:
87
87
  - main
@@ -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([], {});
package/dist/config.d.ts CHANGED
@@ -1,44 +1,12 @@
1
- /**
2
- * CLI configuration
3
- */
4
1
  export declare const config: {
5
- /**
6
- * Base URL for the Output.ai API server
7
- * Can be overridden with OUTPUT_API_URL environment variable
8
- */
9
- apiUrl: string;
10
- /**
11
- * API authentication token
12
- * Set via OUTPUT_API_AUTH_TOKEN environment variable
13
- */
14
- apiToken: string | undefined;
15
- /**
16
- * Default timeout for API requests (in milliseconds)
17
- */
2
+ readonly apiUrl: string;
3
+ readonly apiToken: string | undefined;
18
4
  requestTimeout: number;
19
- /**
20
- * Docker Compose project name
21
- * Can be overridden with DOCKER_SERVICE_NAME environment variable
22
- */
23
- dockerServiceName: string;
24
- /**
25
- * Set the debug mode
26
- */
27
- debugMode: boolean;
28
- /**
29
- * Where the env vars are stored, defaults to `.env`
30
- */
31
- envFile: string;
32
- /**
33
- * Agent configuration directory name
34
- */
5
+ readonly dockerServiceName: string;
6
+ readonly debugMode: boolean;
7
+ readonly envFile: string;
35
8
  agentConfigDir: string;
36
- /**
37
- * S3 configuration for remote trace storage
38
- * Set via OUTPUT_TRACE_REMOTE_S3_BUCKET, OUTPUT_AWS_REGION,
39
- * OUTPUT_AWS_ACCESS_KEY_ID, and OUTPUT_AWS_SECRET_ACCESS_KEY environment variables
40
- */
41
- s3: {
9
+ readonly s3: {
42
10
  bucket: string | undefined;
43
11
  region: string | undefined;
44
12
  accessKeyId: string | undefined;
package/dist/config.js CHANGED
@@ -1,47 +1,27 @@
1
- /**
2
- * CLI configuration
3
- */
4
1
  export const config = {
5
- /**
6
- * Base URL for the Output.ai API server
7
- * Can be overridden with OUTPUT_API_URL environment variable
8
- */
9
- apiUrl: process.env.OUTPUT_API_URL || 'http://localhost:3001',
10
- /**
11
- * API authentication token
12
- * Set via OUTPUT_API_AUTH_TOKEN environment variable
13
- */
14
- apiToken: process.env.OUTPUT_API_AUTH_TOKEN,
15
- /**
16
- * Default timeout for API requests (in milliseconds)
17
- */
2
+ get apiUrl() {
3
+ return process.env.OUTPUT_API_URL || 'http://localhost:3001';
4
+ },
5
+ get apiToken() {
6
+ return process.env.OUTPUT_API_AUTH_TOKEN;
7
+ },
18
8
  requestTimeout: 30000,
19
- /**
20
- * Docker Compose project name
21
- * Can be overridden with DOCKER_SERVICE_NAME environment variable
22
- */
23
- dockerServiceName: process.env.DOCKER_SERVICE_NAME || 'output-sdk',
24
- /**
25
- * Set the debug mode
26
- */
27
- debugMode: process.env.OUTPUT_DEBUG === 'true',
28
- /**
29
- * Where the env vars are stored, defaults to `.env`
30
- */
31
- envFile: process.env.OUTPUT_CLI_ENV || '.env',
32
- /**
33
- * Agent configuration directory name
34
- */
9
+ get dockerServiceName() {
10
+ return process.env.DOCKER_SERVICE_NAME || 'output-sdk';
11
+ },
12
+ get debugMode() {
13
+ return process.env.OUTPUT_DEBUG === 'true';
14
+ },
15
+ get envFile() {
16
+ return process.env.OUTPUT_CLI_ENV || '.env';
17
+ },
35
18
  agentConfigDir: '.outputai',
36
- /**
37
- * S3 configuration for remote trace storage
38
- * Set via OUTPUT_TRACE_REMOTE_S3_BUCKET, OUTPUT_AWS_REGION,
39
- * OUTPUT_AWS_ACCESS_KEY_ID, and OUTPUT_AWS_SECRET_ACCESS_KEY environment variables
40
- */
41
- s3: {
42
- bucket: process.env.OUTPUT_TRACE_REMOTE_S3_BUCKET,
43
- region: process.env.OUTPUT_AWS_REGION,
44
- accessKeyId: process.env.OUTPUT_AWS_ACCESS_KEY_ID,
45
- secretAccessKey: process.env.OUTPUT_AWS_SECRET_ACCESS_KEY
19
+ get s3() {
20
+ return {
21
+ bucket: process.env.OUTPUT_TRACE_REMOTE_S3_BUCKET,
22
+ region: process.env.OUTPUT_AWS_REGION,
23
+ accessKeyId: process.env.OUTPUT_AWS_ACCESS_KEY_ID,
24
+ secretAccessKey: process.env.OUTPUT_AWS_SECRET_ACCESS_KEY
25
+ };
46
26
  }
47
27
  };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,75 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import { config } from '#config.js';
3
+ describe('config', () => {
4
+ const envVars = [
5
+ 'OUTPUT_API_URL',
6
+ 'OUTPUT_API_AUTH_TOKEN',
7
+ 'DOCKER_SERVICE_NAME',
8
+ 'OUTPUT_DEBUG',
9
+ 'OUTPUT_CLI_ENV',
10
+ 'OUTPUT_TRACE_REMOTE_S3_BUCKET',
11
+ 'OUTPUT_AWS_REGION',
12
+ 'OUTPUT_AWS_ACCESS_KEY_ID',
13
+ 'OUTPUT_AWS_SECRET_ACCESS_KEY'
14
+ ];
15
+ const saved = {};
16
+ beforeEach(() => {
17
+ for (const key of envVars) {
18
+ saved[key] = process.env[key];
19
+ }
20
+ });
21
+ afterEach(() => {
22
+ for (const key of envVars) {
23
+ if (saved[key] === undefined) {
24
+ delete process.env[key];
25
+ }
26
+ else {
27
+ process.env[key] = saved[key];
28
+ }
29
+ }
30
+ });
31
+ it('reads env vars lazily, not at module evaluation time', () => {
32
+ process.env.OUTPUT_API_URL = 'https://lazy-test.example.com';
33
+ expect(config.apiUrl).toBe('https://lazy-test.example.com');
34
+ process.env.OUTPUT_API_URL = 'https://changed.example.com';
35
+ expect(config.apiUrl).toBe('https://changed.example.com');
36
+ });
37
+ it('falls back to defaults when env vars are unset', () => {
38
+ delete process.env.OUTPUT_API_URL;
39
+ delete process.env.DOCKER_SERVICE_NAME;
40
+ delete process.env.OUTPUT_DEBUG;
41
+ delete process.env.OUTPUT_CLI_ENV;
42
+ expect(config.apiUrl).toBe('http://localhost:3001');
43
+ expect(config.dockerServiceName).toBe('output-sdk');
44
+ expect(config.debugMode).toBe(false);
45
+ expect(config.envFile).toBe('.env');
46
+ });
47
+ it('reads apiToken from env', () => {
48
+ process.env.OUTPUT_API_AUTH_TOKEN = 'test-token-123';
49
+ expect(config.apiToken).toBe('test-token-123');
50
+ delete process.env.OUTPUT_API_AUTH_TOKEN;
51
+ expect(config.apiToken).toBeUndefined();
52
+ });
53
+ it('reads debugMode as boolean', () => {
54
+ process.env.OUTPUT_DEBUG = 'true';
55
+ expect(config.debugMode).toBe(true);
56
+ process.env.OUTPUT_DEBUG = 'false';
57
+ expect(config.debugMode).toBe(false);
58
+ });
59
+ it('reads s3 config lazily', () => {
60
+ process.env.OUTPUT_TRACE_REMOTE_S3_BUCKET = 'my-bucket';
61
+ process.env.OUTPUT_AWS_REGION = 'us-west-2';
62
+ process.env.OUTPUT_AWS_ACCESS_KEY_ID = 'AKIA123';
63
+ process.env.OUTPUT_AWS_SECRET_ACCESS_KEY = 'secret123';
64
+ expect(config.s3).toEqual({
65
+ bucket: 'my-bucket',
66
+ region: 'us-west-2',
67
+ accessKeyId: 'AKIA123',
68
+ secretAccessKey: 'secret123'
69
+ });
70
+ });
71
+ it('has static properties that are not env-derived', () => {
72
+ expect(config.requestTimeout).toBe(30000);
73
+ expect(config.agentConfigDir).toBe('.outputai');
74
+ });
75
+ });
@@ -1,3 +1,3 @@
1
1
  {
2
- "framework": "0.2.1-next.af8a069.0"
2
+ "framework": "0.2.1-next.eadab44.0"
3
3
  }
@@ -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;