@output.ai/cli 0.0.9 → 0.2.4
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/README.md +48 -0
- package/bin/copyassets.sh +8 -0
- package/bin/run.js +4 -0
- package/dist/api/generated/api.d.ts +35 -13
- package/dist/api/http_client.d.ts +8 -2
- package/dist/api/http_client.js +14 -6
- package/dist/api/orval_post_process.d.ts +2 -1
- package/dist/api/orval_post_process.js +18 -5
- package/dist/assets/docker/docker-compose-dev.yml +130 -0
- package/dist/commands/agents/init.js +4 -2
- package/dist/commands/dev/eject.d.ts +11 -0
- package/dist/commands/dev/eject.js +58 -0
- package/dist/commands/dev/eject.spec.d.ts +1 -0
- package/dist/commands/dev/eject.spec.js +109 -0
- package/dist/commands/dev/index.d.ts +11 -0
- package/dist/commands/dev/index.js +54 -0
- package/dist/commands/dev/index.spec.d.ts +1 -0
- package/dist/commands/dev/index.spec.js +150 -0
- package/dist/commands/init.d.ts +10 -0
- package/dist/commands/init.js +30 -0
- package/dist/commands/init.spec.d.ts +1 -0
- package/dist/commands/init.spec.js +109 -0
- package/dist/commands/workflow/run.js +5 -0
- package/dist/services/claude_client.js +15 -2
- package/dist/services/claude_client.spec.js +5 -1
- package/dist/services/coding_agents.js +13 -4
- package/dist/services/coding_agents.spec.js +2 -1
- package/dist/services/docker.d.ts +12 -0
- package/dist/services/docker.js +79 -0
- package/dist/services/env_configurator.d.ts +11 -0
- package/dist/services/env_configurator.js +158 -0
- package/dist/services/env_configurator.spec.d.ts +1 -0
- package/dist/services/env_configurator.spec.js +123 -0
- package/dist/services/messages.d.ts +5 -0
- package/dist/services/messages.js +233 -0
- package/dist/services/project_scaffold.d.ts +6 -0
- package/dist/services/project_scaffold.js +140 -0
- package/dist/services/project_scaffold.spec.d.ts +1 -0
- package/dist/services/project_scaffold.spec.js +43 -0
- package/dist/services/template_processor.d.ts +1 -1
- package/dist/services/template_processor.js +26 -11
- package/dist/services/workflow_builder.js +2 -1
- package/dist/templates/project/.env.template +9 -0
- package/dist/templates/project/.gitignore.template +33 -0
- package/dist/templates/project/README.md.template +60 -0
- package/dist/templates/project/package.json.template +26 -0
- package/dist/templates/project/src/simple/prompts/answer_question@v1.prompt.template +13 -0
- package/dist/templates/project/src/simple/steps.ts.template +16 -0
- package/dist/templates/project/src/simple/workflow.ts.template +22 -0
- package/dist/templates/project/tsconfig.json.template +20 -0
- package/dist/types/errors.d.ts +32 -0
- package/dist/types/errors.js +48 -0
- package/dist/utils/env_loader.d.ts +6 -0
- package/dist/utils/env_loader.js +43 -0
- package/dist/utils/error_utils.d.ts +24 -0
- package/dist/utils/error_utils.js +87 -0
- package/dist/utils/file_system.d.ts +3 -0
- package/dist/utils/file_system.js +33 -0
- package/dist/utils/process.d.ts +4 -0
- package/dist/utils/process.js +48 -0
- package/dist/utils/sdk_versions.d.ts +7 -0
- package/dist/utils/sdk_versions.js +28 -0
- package/dist/utils/sdk_versions.spec.d.ts +1 -0
- package/dist/utils/sdk_versions.spec.js +19 -0
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -14,6 +14,54 @@ npx @output.ai/cli
|
|
|
14
14
|
|
|
15
15
|
## Usage
|
|
16
16
|
|
|
17
|
+
### Initialize a New Project
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# Create a new Output SDK project
|
|
21
|
+
output init my-project
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
#### What It Does
|
|
25
|
+
|
|
26
|
+
Creates a complete Output SDK project structure with:
|
|
27
|
+
- Example workflows demonstrating SDK features
|
|
28
|
+
- TypeScript project configuration
|
|
29
|
+
- Agent configuration files
|
|
30
|
+
|
|
31
|
+
### Start Development Services
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
# Start with automatic file watching (default)
|
|
35
|
+
output dev
|
|
36
|
+
|
|
37
|
+
# Start without file watching
|
|
38
|
+
output dev --no-watch
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
#### What It Does
|
|
42
|
+
|
|
43
|
+
The `dev` command orchestrates your entire development environment:
|
|
44
|
+
1. Starts Docker services (Temporal, Redis, PostgreSQL)
|
|
45
|
+
2. Launches the API server for workflow execution
|
|
46
|
+
3. Starts the worker to process workflows
|
|
47
|
+
4. Opens Temporal UI for workflow monitoring
|
|
48
|
+
5. Automatically restarts the worker when source files change
|
|
49
|
+
6. Provides a unified log view of all services
|
|
50
|
+
|
|
51
|
+
This is the primary command for local development - it handles all the complexity of running multiple services and keeps them synchronized.
|
|
52
|
+
|
|
53
|
+
#### Automatic File Watching
|
|
54
|
+
|
|
55
|
+
By default, the worker container automatically restarts when you modify files in:
|
|
56
|
+
- `src/` - Your workflow source files and implementations
|
|
57
|
+
- `package.json` - When dependencies change
|
|
58
|
+
|
|
59
|
+
This feature uses Docker Compose's native watch functionality (requires Docker Compose v2.22+).
|
|
60
|
+
|
|
61
|
+
#### Command Options
|
|
62
|
+
|
|
63
|
+
- `--no-watch` - Disable automatic container restart on file changes
|
|
64
|
+
|
|
17
65
|
### List Workflows
|
|
18
66
|
|
|
19
67
|
```bash
|
package/bin/run.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { execute } from '@oclif/core';
|
|
4
|
+
import { loadEnvironment } from '../dist/utils/env_loader.js';
|
|
5
|
+
|
|
6
|
+
// Load environment variables from .env files before executing CLI
|
|
7
|
+
loadEnvironment();
|
|
4
8
|
|
|
5
9
|
await execute( { dir: import.meta.url } );
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generated by orval v7.13.2 🍺
|
|
3
|
+
* Do not edit manually.
|
|
4
|
+
* Output.ai SDK API
|
|
5
|
+
* API for managing and executing Temporal workflows through Output SDK
|
|
6
|
+
* OpenAPI spec version: 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
import { type ApiRequestOptions } from '../http_client.js';
|
|
1
9
|
export type JSONSchemaProperties = {
|
|
2
10
|
[key: string]: JSONSchema;
|
|
3
11
|
};
|
|
@@ -29,20 +37,32 @@ export type PostWorkflowRunBody = {
|
|
|
29
37
|
workflowName: string;
|
|
30
38
|
/** The payload to send to the workflow */
|
|
31
39
|
input: unknown;
|
|
40
|
+
/** (Optional) The workflowId to use. Must be unique */
|
|
41
|
+
workflowId?: string;
|
|
32
42
|
/** The name of the task queue to send the workflow to */
|
|
33
43
|
taskQueue?: string;
|
|
34
44
|
};
|
|
45
|
+
/**
|
|
46
|
+
* An object with information about the trace generated by the execution
|
|
47
|
+
*/
|
|
48
|
+
export type PostWorkflowRun200Trace = {
|
|
49
|
+
[key: string]: unknown;
|
|
50
|
+
};
|
|
35
51
|
export type PostWorkflowRun200 = {
|
|
36
52
|
/** The workflow execution id */
|
|
37
53
|
workflowId?: string;
|
|
38
54
|
/** The output of the workflow */
|
|
39
55
|
output?: unknown;
|
|
56
|
+
/** An object with information about the trace generated by the execution */
|
|
57
|
+
trace?: PostWorkflowRun200Trace;
|
|
40
58
|
};
|
|
41
59
|
export type PostWorkflowStartBody = {
|
|
42
60
|
/** The name of the workflow to execute */
|
|
43
61
|
workflowName: string;
|
|
44
62
|
/** The payload to send to the workflow */
|
|
45
63
|
input: unknown;
|
|
64
|
+
/** (Optional) The workflowId to use. Must be unique */
|
|
65
|
+
workflowId?: string;
|
|
46
66
|
/** The name of the task queue to send the workflow to */
|
|
47
67
|
taskQueue?: string;
|
|
48
68
|
};
|
|
@@ -75,16 +95,18 @@ export type GetWorkflowIdStatus200 = {
|
|
|
75
95
|
completedAt?: number;
|
|
76
96
|
};
|
|
77
97
|
/**
|
|
78
|
-
*
|
|
98
|
+
* An object with information about the trace generated by the execution
|
|
79
99
|
*/
|
|
80
|
-
export type
|
|
100
|
+
export type GetWorkflowIdOutput200Trace = {
|
|
81
101
|
[key: string]: unknown;
|
|
82
102
|
};
|
|
83
103
|
export type GetWorkflowIdOutput200 = {
|
|
84
104
|
/** The workflow execution id */
|
|
85
105
|
workflowId?: string;
|
|
86
106
|
/** The output of workflow */
|
|
87
|
-
output?:
|
|
107
|
+
output?: unknown;
|
|
108
|
+
/** An object with information about the trace generated by the execution */
|
|
109
|
+
trace?: GetWorkflowIdOutput200Trace;
|
|
88
110
|
};
|
|
89
111
|
export type GetWorkflowCatalogId200 = {
|
|
90
112
|
/** Each workflow available in this catalog */
|
|
@@ -110,7 +132,7 @@ export type getHealthResponseSuccess = (getHealthResponse200) & {
|
|
|
110
132
|
};
|
|
111
133
|
export type getHealthResponse = (getHealthResponseSuccess);
|
|
112
134
|
export declare const getGetHealthUrl: () => string;
|
|
113
|
-
export declare const getHealth: (options?:
|
|
135
|
+
export declare const getHealth: (options?: ApiRequestOptions) => Promise<getHealthResponse>;
|
|
114
136
|
/**
|
|
115
137
|
* Executes a workflow and waits for it to complete before returning the result
|
|
116
138
|
* @summary Execute a workflow synchronously
|
|
@@ -124,7 +146,7 @@ export type postWorkflowRunResponseSuccess = (postWorkflowRunResponse200) & {
|
|
|
124
146
|
};
|
|
125
147
|
export type postWorkflowRunResponse = (postWorkflowRunResponseSuccess);
|
|
126
148
|
export declare const getPostWorkflowRunUrl: () => string;
|
|
127
|
-
export declare const postWorkflowRun: (postWorkflowRunBody: PostWorkflowRunBody, options?:
|
|
149
|
+
export declare const postWorkflowRun: (postWorkflowRunBody: PostWorkflowRunBody, options?: ApiRequestOptions) => Promise<postWorkflowRunResponse>;
|
|
128
150
|
/**
|
|
129
151
|
* @summary Start a workflow asynchronously
|
|
130
152
|
*/
|
|
@@ -137,7 +159,7 @@ export type postWorkflowStartResponseSuccess = (postWorkflowStartResponse200) &
|
|
|
137
159
|
};
|
|
138
160
|
export type postWorkflowStartResponse = (postWorkflowStartResponseSuccess);
|
|
139
161
|
export declare const getPostWorkflowStartUrl: () => string;
|
|
140
|
-
export declare const postWorkflowStart: (postWorkflowStartBody: PostWorkflowStartBody, options?:
|
|
162
|
+
export declare const postWorkflowStart: (postWorkflowStartBody: PostWorkflowStartBody, options?: ApiRequestOptions) => Promise<postWorkflowStartResponse>;
|
|
141
163
|
/**
|
|
142
164
|
* @summary Get workflow execution status
|
|
143
165
|
*/
|
|
@@ -157,7 +179,7 @@ export type getWorkflowIdStatusResponseError = (getWorkflowIdStatusResponse404)
|
|
|
157
179
|
};
|
|
158
180
|
export type getWorkflowIdStatusResponse = (getWorkflowIdStatusResponseSuccess | getWorkflowIdStatusResponseError);
|
|
159
181
|
export declare const getGetWorkflowIdStatusUrl: (id: string) => string;
|
|
160
|
-
export declare const getWorkflowIdStatus: (id: string, options?:
|
|
182
|
+
export declare const getWorkflowIdStatus: (id: string, options?: ApiRequestOptions) => Promise<getWorkflowIdStatusResponse>;
|
|
161
183
|
/**
|
|
162
184
|
* @summary Stop a workflow execution
|
|
163
185
|
*/
|
|
@@ -177,7 +199,7 @@ export type patchWorkflowIdStopResponseError = (patchWorkflowIdStopResponse404)
|
|
|
177
199
|
};
|
|
178
200
|
export type patchWorkflowIdStopResponse = (patchWorkflowIdStopResponseSuccess | patchWorkflowIdStopResponseError);
|
|
179
201
|
export declare const getPatchWorkflowIdStopUrl: (id: string) => string;
|
|
180
|
-
export declare const patchWorkflowIdStop: (id: string, options?:
|
|
202
|
+
export declare const patchWorkflowIdStop: (id: string, options?: ApiRequestOptions) => Promise<patchWorkflowIdStopResponse>;
|
|
181
203
|
/**
|
|
182
204
|
* @summary Return the output of a workflow
|
|
183
205
|
*/
|
|
@@ -197,7 +219,7 @@ export type getWorkflowIdOutputResponseError = (getWorkflowIdOutputResponse404)
|
|
|
197
219
|
};
|
|
198
220
|
export type getWorkflowIdOutputResponse = (getWorkflowIdOutputResponseSuccess | getWorkflowIdOutputResponseError);
|
|
199
221
|
export declare const getGetWorkflowIdOutputUrl: (id: string) => string;
|
|
200
|
-
export declare const getWorkflowIdOutput: (id: string, options?:
|
|
222
|
+
export declare const getWorkflowIdOutput: (id: string, options?: ApiRequestOptions) => Promise<getWorkflowIdOutputResponse>;
|
|
201
223
|
/**
|
|
202
224
|
* @summary Get a specific workflow catalog by ID
|
|
203
225
|
*/
|
|
@@ -210,7 +232,7 @@ export type getWorkflowCatalogIdResponseSuccess = (getWorkflowCatalogIdResponse2
|
|
|
210
232
|
};
|
|
211
233
|
export type getWorkflowCatalogIdResponse = (getWorkflowCatalogIdResponseSuccess);
|
|
212
234
|
export declare const getGetWorkflowCatalogIdUrl: (id: string) => string;
|
|
213
|
-
export declare const getWorkflowCatalogId: (id: string, options?:
|
|
235
|
+
export declare const getWorkflowCatalogId: (id: string, options?: ApiRequestOptions) => Promise<getWorkflowCatalogIdResponse>;
|
|
214
236
|
/**
|
|
215
237
|
* @summary Get the default workflow catalog
|
|
216
238
|
*/
|
|
@@ -223,7 +245,7 @@ export type getWorkflowCatalogResponseSuccess = (getWorkflowCatalogResponse200)
|
|
|
223
245
|
};
|
|
224
246
|
export type getWorkflowCatalogResponse = (getWorkflowCatalogResponseSuccess);
|
|
225
247
|
export declare const getGetWorkflowCatalogUrl: () => string;
|
|
226
|
-
export declare const getWorkflowCatalog: (options?:
|
|
248
|
+
export declare const getWorkflowCatalog: (options?: ApiRequestOptions) => Promise<getWorkflowCatalogResponse>;
|
|
227
249
|
/**
|
|
228
250
|
* @summary Send feedback to a payload
|
|
229
251
|
*/
|
|
@@ -236,7 +258,7 @@ export type postWorkflowIdFeedbackResponseSuccess = (postWorkflowIdFeedbackRespo
|
|
|
236
258
|
};
|
|
237
259
|
export type postWorkflowIdFeedbackResponse = (postWorkflowIdFeedbackResponseSuccess);
|
|
238
260
|
export declare const getPostWorkflowIdFeedbackUrl: (id: string) => string;
|
|
239
|
-
export declare const postWorkflowIdFeedback: (id: string, postWorkflowIdFeedbackBody: PostWorkflowIdFeedbackBody, options?:
|
|
261
|
+
export declare const postWorkflowIdFeedback: (id: string, postWorkflowIdFeedbackBody: PostWorkflowIdFeedbackBody, options?: ApiRequestOptions) => Promise<postWorkflowIdFeedbackResponse>;
|
|
240
262
|
/**
|
|
241
263
|
* @summary A dummy post endpoint for test only
|
|
242
264
|
*/
|
|
@@ -249,4 +271,4 @@ export type postHeartbeatResponseSuccess = (postHeartbeatResponse204) & {
|
|
|
249
271
|
};
|
|
250
272
|
export type postHeartbeatResponse = (postHeartbeatResponseSuccess);
|
|
251
273
|
export declare const getPostHeartbeatUrl: () => string;
|
|
252
|
-
export declare const postHeartbeat: (options?:
|
|
274
|
+
export declare const postHeartbeat: (options?: ApiRequestOptions) => Promise<postHeartbeatResponse>;
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Custom ky-based HTTP client for Orval-generated API
|
|
3
3
|
*/
|
|
4
|
-
|
|
4
|
+
import type { Options as KyOptions } from 'ky';
|
|
5
|
+
/**
|
|
6
|
+
* Custom API request options that extend RequestInit with additional config
|
|
7
|
+
*/
|
|
8
|
+
export type ApiRequestOptions = RequestInit & {
|
|
5
9
|
params?: Record<string, unknown>;
|
|
6
|
-
|
|
10
|
+
config?: KyOptions;
|
|
11
|
+
};
|
|
12
|
+
export declare const customFetchInstance: <T>(url: string, options: ApiRequestOptions) => Promise<T>;
|
package/dist/api/http_client.js
CHANGED
|
@@ -23,12 +23,20 @@ const api = ky.create({
|
|
|
23
23
|
}
|
|
24
24
|
});
|
|
25
25
|
const stripLeadingSlash = (url) => url.startsWith('/') ? url.slice(1) : url;
|
|
26
|
-
const buildKyOptions = (options) =>
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
26
|
+
const buildKyOptions = (options) => {
|
|
27
|
+
// Extract params, config, and body for special handling
|
|
28
|
+
const { params, config: customConfig, body, ...restOptions } = options;
|
|
29
|
+
return {
|
|
30
|
+
// Pass through standard RequestInit options
|
|
31
|
+
...restOptions,
|
|
32
|
+
// Convert params to searchParams for ky (if not already in config)
|
|
33
|
+
searchParams: customConfig?.searchParams || params,
|
|
34
|
+
// Only include body for non-GET requests
|
|
35
|
+
...(body && options.method !== 'GET' ? { body } : {}),
|
|
36
|
+
// Spread any ky-specific config options
|
|
37
|
+
...customConfig
|
|
38
|
+
};
|
|
39
|
+
};
|
|
32
40
|
const wrapResponse = (response, data) => ({
|
|
33
41
|
data,
|
|
34
42
|
status: response.status,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Orval post-generation hook to fix ES module imports by adding .js extensions
|
|
2
|
+
* Orval post-generation hook to fix ES module imports by adding .js extensions
|
|
3
|
+
* and update RequestInit types to use our custom ApiRequestOptions.
|
|
3
4
|
* This is necessary because the SDK uses "type": "module" in package.json,
|
|
4
5
|
* which requires all relative imports to have explicit .js extensions.
|
|
5
6
|
*/
|
|
@@ -1,15 +1,28 @@
|
|
|
1
1
|
import { readFileSync, writeFileSync } from 'fs';
|
|
2
2
|
import { execSync } from 'child_process';
|
|
3
3
|
/**
|
|
4
|
-
* Orval post-generation hook to fix ES module imports by adding .js extensions
|
|
4
|
+
* Orval post-generation hook to fix ES module imports by adding .js extensions
|
|
5
|
+
* and update RequestInit types to use our custom ApiRequestOptions.
|
|
5
6
|
* This is necessary because the SDK uses "type": "module" in package.json,
|
|
6
7
|
* which requires all relative imports to have explicit .js extensions.
|
|
7
8
|
*/
|
|
8
9
|
export async function fixEsmImports(outputPath) {
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
const originalContent = readFileSync(outputPath, 'utf8');
|
|
11
|
+
// Apply all transformations in sequence
|
|
12
|
+
const transformedContent = originalContent
|
|
13
|
+
// Fix ESM imports
|
|
14
|
+
.replace(/from '\.\.\/http_client'/g, 'from \'../http_client.js\'')
|
|
15
|
+
// Import ApiRequestOptions type from http_client
|
|
16
|
+
.replace(/import { customFetchInstance } from '\.\.\/http_client\.js';/, match => {
|
|
17
|
+
if (!originalContent.includes('ApiRequestOptions')) {
|
|
18
|
+
return 'import { customFetchInstance, type ApiRequestOptions } from \'../http_client.js\';';
|
|
19
|
+
}
|
|
20
|
+
return match;
|
|
21
|
+
})
|
|
22
|
+
// Replace RequestInit with ApiRequestOptions in function signatures
|
|
23
|
+
.replace(/options\?: RequestInit/g, 'options?: ApiRequestOptions');
|
|
24
|
+
writeFileSync(outputPath, transformedContent, 'utf8');
|
|
25
|
+
console.log('✅ Fixed ESM imports and updated types in Orval generated file');
|
|
13
26
|
}
|
|
14
27
|
/**
|
|
15
28
|
* Run ESLint fix on generated files to ensure they follow project standards
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
name: ${DOCKER_SERVICE_NAME:-output-sdk}
|
|
2
|
+
services:
|
|
3
|
+
redis:
|
|
4
|
+
image: redis:8-alpine
|
|
5
|
+
networks:
|
|
6
|
+
- main
|
|
7
|
+
ports:
|
|
8
|
+
- '6379:6379'
|
|
9
|
+
volumes:
|
|
10
|
+
- redis:/data
|
|
11
|
+
healthcheck:
|
|
12
|
+
test: ['CMD', 'redis-cli', 'ping']
|
|
13
|
+
interval: 2s
|
|
14
|
+
timeout: 2s
|
|
15
|
+
retries: 5
|
|
16
|
+
start_period: 3s
|
|
17
|
+
|
|
18
|
+
postgresql:
|
|
19
|
+
environment:
|
|
20
|
+
POSTGRES_PASSWORD: temporal
|
|
21
|
+
POSTGRES_USER: temporal
|
|
22
|
+
image: postgres:17.5
|
|
23
|
+
networks:
|
|
24
|
+
- main
|
|
25
|
+
expose:
|
|
26
|
+
- 5432
|
|
27
|
+
volumes:
|
|
28
|
+
- postgres:/var/lib/postgresql/data
|
|
29
|
+
healthcheck:
|
|
30
|
+
test: ['CMD-SHELL', 'pg_isready -U temporal -d temporal']
|
|
31
|
+
interval: 2s
|
|
32
|
+
timeout: 2s
|
|
33
|
+
retries: 5
|
|
34
|
+
start_period: 3s
|
|
35
|
+
|
|
36
|
+
temporal:
|
|
37
|
+
depends_on:
|
|
38
|
+
postgresql:
|
|
39
|
+
condition: service_healthy
|
|
40
|
+
redis:
|
|
41
|
+
condition: service_healthy
|
|
42
|
+
environment:
|
|
43
|
+
- DB=postgres12
|
|
44
|
+
- DB_PORT=5432
|
|
45
|
+
- POSTGRES_USER=temporal
|
|
46
|
+
- POSTGRES_PWD=temporal
|
|
47
|
+
- POSTGRES_SEEDS=postgresql
|
|
48
|
+
image: temporalio/auto-setup:latest
|
|
49
|
+
networks:
|
|
50
|
+
- main
|
|
51
|
+
ports:
|
|
52
|
+
- '7233:7233'
|
|
53
|
+
healthcheck:
|
|
54
|
+
test:
|
|
55
|
+
[
|
|
56
|
+
'CMD',
|
|
57
|
+
'sh',
|
|
58
|
+
'-c',
|
|
59
|
+
'tctl --address temporal:7233 cluster health 2>&1 | grep -q "temporal.api.workflowservice.v1.WorkflowService: SERVING"'
|
|
60
|
+
]
|
|
61
|
+
interval: 3s
|
|
62
|
+
timeout: 5s
|
|
63
|
+
retries: 20
|
|
64
|
+
start_period: 10s
|
|
65
|
+
|
|
66
|
+
temporal-ui:
|
|
67
|
+
depends_on:
|
|
68
|
+
- temporal
|
|
69
|
+
environment:
|
|
70
|
+
- TEMPORAL_ADDRESS=temporal:7233
|
|
71
|
+
- TEMPORAL_CORS_ORIGINS=http://localhost:3000
|
|
72
|
+
image: temporalio/ui:latest
|
|
73
|
+
networks:
|
|
74
|
+
- main
|
|
75
|
+
ports:
|
|
76
|
+
- '8080:8080'
|
|
77
|
+
|
|
78
|
+
api:
|
|
79
|
+
depends_on:
|
|
80
|
+
temporal:
|
|
81
|
+
condition: service_healthy
|
|
82
|
+
image: growthxteam/output-api:latest
|
|
83
|
+
networks:
|
|
84
|
+
- main
|
|
85
|
+
environment:
|
|
86
|
+
- PORT=3001
|
|
87
|
+
- CATALOG_ID=main
|
|
88
|
+
- TEMPORAL_ADDRESS=temporal:7233
|
|
89
|
+
- NODE_ENV=development
|
|
90
|
+
ports:
|
|
91
|
+
- '3001:3001'
|
|
92
|
+
|
|
93
|
+
worker:
|
|
94
|
+
depends_on:
|
|
95
|
+
temporal:
|
|
96
|
+
condition: service_healthy
|
|
97
|
+
image: node:24.3-slim
|
|
98
|
+
networks:
|
|
99
|
+
- main
|
|
100
|
+
env_file: './.env'
|
|
101
|
+
environment:
|
|
102
|
+
- CATALOG_ID=main
|
|
103
|
+
- LOG_HTTP_VERBOSE=true
|
|
104
|
+
- REDIS_URL=redis://redis:6379
|
|
105
|
+
- TEMPORAL_ADDRESS=temporal:7233
|
|
106
|
+
- TRACE_LOCAL_ON=true
|
|
107
|
+
command: npm run start-worker
|
|
108
|
+
working_dir: /app
|
|
109
|
+
volumes:
|
|
110
|
+
- ./:/app
|
|
111
|
+
develop:
|
|
112
|
+
watch:
|
|
113
|
+
- path: ./src
|
|
114
|
+
target: /app/src
|
|
115
|
+
action: restart
|
|
116
|
+
ignore:
|
|
117
|
+
- node_modules/
|
|
118
|
+
- '**/*.test.ts'
|
|
119
|
+
- '**/*.spec.ts'
|
|
120
|
+
- path: ./package.json
|
|
121
|
+
target: /app/package.json
|
|
122
|
+
action: restart
|
|
123
|
+
|
|
124
|
+
volumes:
|
|
125
|
+
postgres:
|
|
126
|
+
redis:
|
|
127
|
+
|
|
128
|
+
networks:
|
|
129
|
+
main:
|
|
130
|
+
driver: bridge
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Command, Flags } from '@oclif/core';
|
|
2
2
|
import { AGENT_CONFIG_DIR } from '#config.js';
|
|
3
3
|
import { initializeAgentConfig, AGENT_CONFIGS } from '#services/coding_agents.js';
|
|
4
|
+
import { getErrorMessage, getErrorCode } from '#utils/error_utils.js';
|
|
4
5
|
export default class Init extends Command {
|
|
5
6
|
static description = 'Initialize agent configuration files for AI assistant integration';
|
|
6
7
|
static examples = [
|
|
@@ -39,10 +40,11 @@ export default class Init extends Command {
|
|
|
39
40
|
this.log('Claude Code will automatically detect and use these configurations.');
|
|
40
41
|
}
|
|
41
42
|
catch (error) {
|
|
42
|
-
if (error
|
|
43
|
+
if (getErrorCode(error) === 'EACCES') {
|
|
43
44
|
this.error('Permission denied. Please check file permissions and try again.');
|
|
45
|
+
return;
|
|
44
46
|
}
|
|
45
|
-
this.error(`Failed to initialize agent configuration: ${error
|
|
47
|
+
this.error(`Failed to initialize agent configuration: ${getErrorMessage(error)}`);
|
|
46
48
|
}
|
|
47
49
|
}
|
|
48
50
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
export default class DevEject extends Command {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static args: {};
|
|
6
|
+
static flags: {
|
|
7
|
+
output: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
8
|
+
force: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
9
|
+
};
|
|
10
|
+
run(): Promise<void>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Command, Flags } from '@oclif/core';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { getDefaultDockerComposePath } from '#services/docker.js';
|
|
5
|
+
import { getErrorMessage } from '#utils/error_utils.js';
|
|
6
|
+
import { getEjectSuccessMessage } from '#services/messages.js';
|
|
7
|
+
export default class DevEject extends Command {
|
|
8
|
+
static description = 'Eject the Docker Compose configuration to your project root for customization';
|
|
9
|
+
static examples = [
|
|
10
|
+
'<%= config.bin %> <%= command.id %>',
|
|
11
|
+
'<%= config.bin %> <%= command.id %> --output ./custom-compose.yml'
|
|
12
|
+
];
|
|
13
|
+
static args = {};
|
|
14
|
+
static flags = {
|
|
15
|
+
output: Flags.string({
|
|
16
|
+
description: 'Output path for the docker-compose file',
|
|
17
|
+
required: false,
|
|
18
|
+
char: 'o',
|
|
19
|
+
default: 'docker-compose.yml'
|
|
20
|
+
}),
|
|
21
|
+
force: Flags.boolean({
|
|
22
|
+
description: 'Overwrite existing file without prompting',
|
|
23
|
+
required: false,
|
|
24
|
+
char: 'f',
|
|
25
|
+
default: false
|
|
26
|
+
})
|
|
27
|
+
};
|
|
28
|
+
async run() {
|
|
29
|
+
const { flags } = await this.parse(DevEject);
|
|
30
|
+
// Source docker-compose file from assets
|
|
31
|
+
const sourcePath = getDefaultDockerComposePath();
|
|
32
|
+
// Destination path (relative to current working directory)
|
|
33
|
+
const destPath = path.resolve(process.cwd(), flags.output);
|
|
34
|
+
try {
|
|
35
|
+
// Check if source file exists
|
|
36
|
+
await fs.access(sourcePath);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
this.error(`Docker Compose template not found at: ${sourcePath}`, { exit: 1 });
|
|
40
|
+
}
|
|
41
|
+
// Check if destination file already exists
|
|
42
|
+
const fileExists = await fs.access(destPath).then(() => true).catch(() => false);
|
|
43
|
+
if (fileExists && !flags.force) {
|
|
44
|
+
this.error(`File already exists at ${destPath}. Use --force to overwrite or specify a different output path with --output`, { exit: 1 });
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
// Read the source file
|
|
48
|
+
const dockerComposeContent = await fs.readFile(sourcePath, 'utf-8');
|
|
49
|
+
// Write to destination
|
|
50
|
+
await fs.writeFile(destPath, dockerComposeContent, 'utf-8');
|
|
51
|
+
// Display the styled success message
|
|
52
|
+
this.log(getEjectSuccessMessage(destPath, flags.output, this.config.bin));
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
this.error(`Failed to eject docker-compose configuration: ${getErrorMessage(error)}`, { exit: 1 });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
3
|
+
import fs from 'node:fs/promises';
|
|
4
|
+
import DevEject from './eject.js';
|
|
5
|
+
vi.mock('node:fs/promises');
|
|
6
|
+
describe('dev eject command', () => {
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
vi.clearAllMocks();
|
|
9
|
+
});
|
|
10
|
+
afterEach(() => {
|
|
11
|
+
vi.restoreAllMocks();
|
|
12
|
+
});
|
|
13
|
+
describe('command structure', () => {
|
|
14
|
+
it('should have correct description', () => {
|
|
15
|
+
expect(DevEject.description).toBeDefined();
|
|
16
|
+
expect(DevEject.description).toContain('Eject');
|
|
17
|
+
expect(DevEject.description).toContain('Docker Compose');
|
|
18
|
+
});
|
|
19
|
+
it('should have examples', () => {
|
|
20
|
+
expect(DevEject.examples).toBeDefined();
|
|
21
|
+
expect(Array.isArray(DevEject.examples)).toBe(true);
|
|
22
|
+
expect(DevEject.examples.length).toBeGreaterThan(0);
|
|
23
|
+
});
|
|
24
|
+
it('should have no required arguments', () => {
|
|
25
|
+
expect(DevEject.args).toBeDefined();
|
|
26
|
+
expect(Object.keys(DevEject.args)).toHaveLength(0);
|
|
27
|
+
});
|
|
28
|
+
it('should have output and force flags defined', () => {
|
|
29
|
+
expect(DevEject.flags).toBeDefined();
|
|
30
|
+
expect(DevEject.flags.output).toBeDefined();
|
|
31
|
+
expect(DevEject.flags.output.description).toContain('Output path');
|
|
32
|
+
expect(DevEject.flags.output.required).toBe(false);
|
|
33
|
+
expect(DevEject.flags.output.char).toBe('o');
|
|
34
|
+
expect(DevEject.flags.output.default).toBe('docker-compose.yml');
|
|
35
|
+
expect(DevEject.flags.force).toBeDefined();
|
|
36
|
+
expect(DevEject.flags.force.description).toContain('Overwrite');
|
|
37
|
+
expect(DevEject.flags.force.required).toBe(false);
|
|
38
|
+
expect(DevEject.flags.force.char).toBe('f');
|
|
39
|
+
expect(DevEject.flags.force.default).toBe(false);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
describe('command instantiation', () => {
|
|
43
|
+
it('should be instantiable', () => {
|
|
44
|
+
const cmd = new DevEject([], {});
|
|
45
|
+
expect(cmd).toBeInstanceOf(DevEject);
|
|
46
|
+
});
|
|
47
|
+
it('should have a run method', () => {
|
|
48
|
+
const cmd = new DevEject([], {});
|
|
49
|
+
expect(cmd.run).toBeDefined();
|
|
50
|
+
expect(typeof cmd.run).toBe('function');
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
describe('file ejection', () => {
|
|
54
|
+
it('should eject docker-compose file to default location', async () => {
|
|
55
|
+
const config = {
|
|
56
|
+
runHook: vi.fn().mockResolvedValue({ failures: [], successes: [] })
|
|
57
|
+
};
|
|
58
|
+
const cmd = new DevEject([], config);
|
|
59
|
+
cmd.log = vi.fn();
|
|
60
|
+
cmd.error = vi.fn();
|
|
61
|
+
const mockDockerComposeContent = 'name: output-sdk\nservices:\n redis:\n image: redis:8-alpine';
|
|
62
|
+
// Mock source file exists and can be read
|
|
63
|
+
vi.mocked(fs.access).mockImplementation(path => {
|
|
64
|
+
if (path.toString().includes('assets/docker/docker-compose-dev.yml')) {
|
|
65
|
+
return Promise.resolve();
|
|
66
|
+
}
|
|
67
|
+
// Destination file doesn't exist
|
|
68
|
+
return Promise.reject(new Error('File not found'));
|
|
69
|
+
});
|
|
70
|
+
vi.mocked(fs.readFile).mockResolvedValue(mockDockerComposeContent);
|
|
71
|
+
vi.mocked(fs.writeFile).mockResolvedValue();
|
|
72
|
+
await cmd.run();
|
|
73
|
+
expect(fs.readFile).toHaveBeenCalled();
|
|
74
|
+
expect(fs.writeFile).toHaveBeenCalledWith(expect.stringContaining('docker-compose.yml'), mockDockerComposeContent, 'utf-8');
|
|
75
|
+
expect(cmd.log).toHaveBeenCalledWith(expect.stringContaining('SUCCESS!'));
|
|
76
|
+
});
|
|
77
|
+
it('should error if destination file exists and force flag is not set', async () => {
|
|
78
|
+
const config = {
|
|
79
|
+
runHook: vi.fn().mockResolvedValue({ failures: [], successes: [] })
|
|
80
|
+
};
|
|
81
|
+
const cmd = new DevEject([], config);
|
|
82
|
+
cmd.log = vi.fn();
|
|
83
|
+
cmd.error = vi.fn().mockImplementation(msg => {
|
|
84
|
+
throw new Error(msg);
|
|
85
|
+
});
|
|
86
|
+
// Mock both source and destination files exist
|
|
87
|
+
vi.mocked(fs.access).mockResolvedValue();
|
|
88
|
+
await expect(cmd.run()).rejects.toThrow('File already exists');
|
|
89
|
+
expect(cmd.error).toHaveBeenCalledWith(expect.stringContaining('File already exists'), { exit: 1 });
|
|
90
|
+
});
|
|
91
|
+
it('should overwrite file if force flag is set', async () => {
|
|
92
|
+
const config = {
|
|
93
|
+
runHook: vi.fn().mockResolvedValue({ failures: [], successes: [] })
|
|
94
|
+
};
|
|
95
|
+
const cmd = new DevEject(['--force'], config);
|
|
96
|
+
cmd.log = vi.fn();
|
|
97
|
+
cmd.error = vi.fn();
|
|
98
|
+
const mockDockerComposeContent = 'name: output-sdk\nservices:\n redis:\n image: redis:8-alpine';
|
|
99
|
+
// Mock both source and destination files exist
|
|
100
|
+
vi.mocked(fs.access).mockResolvedValue();
|
|
101
|
+
vi.mocked(fs.readFile).mockResolvedValue(mockDockerComposeContent);
|
|
102
|
+
vi.mocked(fs.writeFile).mockResolvedValue();
|
|
103
|
+
await cmd.run();
|
|
104
|
+
expect(fs.readFile).toHaveBeenCalled();
|
|
105
|
+
expect(fs.writeFile).toHaveBeenCalledWith(expect.stringContaining('docker-compose.yml'), mockDockerComposeContent, 'utf-8');
|
|
106
|
+
expect(cmd.log).toHaveBeenCalledWith(expect.stringContaining('SUCCESS!'));
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
export default class Dev extends Command {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static args: {};
|
|
6
|
+
static flags: {
|
|
7
|
+
'compose-file': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
8
|
+
'no-watch': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
9
|
+
};
|
|
10
|
+
run(): Promise<void>;
|
|
11
|
+
}
|