@output.ai/cli 0.0.9 → 0.2.3
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 +31 -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
|
};
|
|
@@ -32,11 +40,19 @@ export type PostWorkflowRunBody = {
|
|
|
32
40
|
/** The name of the task queue to send the workflow to */
|
|
33
41
|
taskQueue?: string;
|
|
34
42
|
};
|
|
43
|
+
/**
|
|
44
|
+
* An object with information about the trace generated by the execution
|
|
45
|
+
*/
|
|
46
|
+
export type PostWorkflowRun200Trace = {
|
|
47
|
+
[key: string]: unknown;
|
|
48
|
+
};
|
|
35
49
|
export type PostWorkflowRun200 = {
|
|
36
50
|
/** The workflow execution id */
|
|
37
51
|
workflowId?: string;
|
|
38
52
|
/** The output of the workflow */
|
|
39
53
|
output?: unknown;
|
|
54
|
+
/** An object with information about the trace generated by the execution */
|
|
55
|
+
trace?: PostWorkflowRun200Trace;
|
|
40
56
|
};
|
|
41
57
|
export type PostWorkflowStartBody = {
|
|
42
58
|
/** The name of the workflow to execute */
|
|
@@ -75,16 +91,18 @@ export type GetWorkflowIdStatus200 = {
|
|
|
75
91
|
completedAt?: number;
|
|
76
92
|
};
|
|
77
93
|
/**
|
|
78
|
-
*
|
|
94
|
+
* An object with information about the trace generated by the execution
|
|
79
95
|
*/
|
|
80
|
-
export type
|
|
96
|
+
export type GetWorkflowIdOutput200Trace = {
|
|
81
97
|
[key: string]: unknown;
|
|
82
98
|
};
|
|
83
99
|
export type GetWorkflowIdOutput200 = {
|
|
84
100
|
/** The workflow execution id */
|
|
85
101
|
workflowId?: string;
|
|
86
102
|
/** The output of workflow */
|
|
87
|
-
output?:
|
|
103
|
+
output?: unknown;
|
|
104
|
+
/** An object with information about the trace generated by the execution */
|
|
105
|
+
trace?: GetWorkflowIdOutput200Trace;
|
|
88
106
|
};
|
|
89
107
|
export type GetWorkflowCatalogId200 = {
|
|
90
108
|
/** Each workflow available in this catalog */
|
|
@@ -110,7 +128,7 @@ export type getHealthResponseSuccess = (getHealthResponse200) & {
|
|
|
110
128
|
};
|
|
111
129
|
export type getHealthResponse = (getHealthResponseSuccess);
|
|
112
130
|
export declare const getGetHealthUrl: () => string;
|
|
113
|
-
export declare const getHealth: (options?:
|
|
131
|
+
export declare const getHealth: (options?: ApiRequestOptions) => Promise<getHealthResponse>;
|
|
114
132
|
/**
|
|
115
133
|
* Executes a workflow and waits for it to complete before returning the result
|
|
116
134
|
* @summary Execute a workflow synchronously
|
|
@@ -124,7 +142,7 @@ export type postWorkflowRunResponseSuccess = (postWorkflowRunResponse200) & {
|
|
|
124
142
|
};
|
|
125
143
|
export type postWorkflowRunResponse = (postWorkflowRunResponseSuccess);
|
|
126
144
|
export declare const getPostWorkflowRunUrl: () => string;
|
|
127
|
-
export declare const postWorkflowRun: (postWorkflowRunBody: PostWorkflowRunBody, options?:
|
|
145
|
+
export declare const postWorkflowRun: (postWorkflowRunBody: PostWorkflowRunBody, options?: ApiRequestOptions) => Promise<postWorkflowRunResponse>;
|
|
128
146
|
/**
|
|
129
147
|
* @summary Start a workflow asynchronously
|
|
130
148
|
*/
|
|
@@ -137,7 +155,7 @@ export type postWorkflowStartResponseSuccess = (postWorkflowStartResponse200) &
|
|
|
137
155
|
};
|
|
138
156
|
export type postWorkflowStartResponse = (postWorkflowStartResponseSuccess);
|
|
139
157
|
export declare const getPostWorkflowStartUrl: () => string;
|
|
140
|
-
export declare const postWorkflowStart: (postWorkflowStartBody: PostWorkflowStartBody, options?:
|
|
158
|
+
export declare const postWorkflowStart: (postWorkflowStartBody: PostWorkflowStartBody, options?: ApiRequestOptions) => Promise<postWorkflowStartResponse>;
|
|
141
159
|
/**
|
|
142
160
|
* @summary Get workflow execution status
|
|
143
161
|
*/
|
|
@@ -157,7 +175,7 @@ export type getWorkflowIdStatusResponseError = (getWorkflowIdStatusResponse404)
|
|
|
157
175
|
};
|
|
158
176
|
export type getWorkflowIdStatusResponse = (getWorkflowIdStatusResponseSuccess | getWorkflowIdStatusResponseError);
|
|
159
177
|
export declare const getGetWorkflowIdStatusUrl: (id: string) => string;
|
|
160
|
-
export declare const getWorkflowIdStatus: (id: string, options?:
|
|
178
|
+
export declare const getWorkflowIdStatus: (id: string, options?: ApiRequestOptions) => Promise<getWorkflowIdStatusResponse>;
|
|
161
179
|
/**
|
|
162
180
|
* @summary Stop a workflow execution
|
|
163
181
|
*/
|
|
@@ -177,7 +195,7 @@ export type patchWorkflowIdStopResponseError = (patchWorkflowIdStopResponse404)
|
|
|
177
195
|
};
|
|
178
196
|
export type patchWorkflowIdStopResponse = (patchWorkflowIdStopResponseSuccess | patchWorkflowIdStopResponseError);
|
|
179
197
|
export declare const getPatchWorkflowIdStopUrl: (id: string) => string;
|
|
180
|
-
export declare const patchWorkflowIdStop: (id: string, options?:
|
|
198
|
+
export declare const patchWorkflowIdStop: (id: string, options?: ApiRequestOptions) => Promise<patchWorkflowIdStopResponse>;
|
|
181
199
|
/**
|
|
182
200
|
* @summary Return the output of a workflow
|
|
183
201
|
*/
|
|
@@ -197,7 +215,7 @@ export type getWorkflowIdOutputResponseError = (getWorkflowIdOutputResponse404)
|
|
|
197
215
|
};
|
|
198
216
|
export type getWorkflowIdOutputResponse = (getWorkflowIdOutputResponseSuccess | getWorkflowIdOutputResponseError);
|
|
199
217
|
export declare const getGetWorkflowIdOutputUrl: (id: string) => string;
|
|
200
|
-
export declare const getWorkflowIdOutput: (id: string, options?:
|
|
218
|
+
export declare const getWorkflowIdOutput: (id: string, options?: ApiRequestOptions) => Promise<getWorkflowIdOutputResponse>;
|
|
201
219
|
/**
|
|
202
220
|
* @summary Get a specific workflow catalog by ID
|
|
203
221
|
*/
|
|
@@ -210,7 +228,7 @@ export type getWorkflowCatalogIdResponseSuccess = (getWorkflowCatalogIdResponse2
|
|
|
210
228
|
};
|
|
211
229
|
export type getWorkflowCatalogIdResponse = (getWorkflowCatalogIdResponseSuccess);
|
|
212
230
|
export declare const getGetWorkflowCatalogIdUrl: (id: string) => string;
|
|
213
|
-
export declare const getWorkflowCatalogId: (id: string, options?:
|
|
231
|
+
export declare const getWorkflowCatalogId: (id: string, options?: ApiRequestOptions) => Promise<getWorkflowCatalogIdResponse>;
|
|
214
232
|
/**
|
|
215
233
|
* @summary Get the default workflow catalog
|
|
216
234
|
*/
|
|
@@ -223,7 +241,7 @@ export type getWorkflowCatalogResponseSuccess = (getWorkflowCatalogResponse200)
|
|
|
223
241
|
};
|
|
224
242
|
export type getWorkflowCatalogResponse = (getWorkflowCatalogResponseSuccess);
|
|
225
243
|
export declare const getGetWorkflowCatalogUrl: () => string;
|
|
226
|
-
export declare const getWorkflowCatalog: (options?:
|
|
244
|
+
export declare const getWorkflowCatalog: (options?: ApiRequestOptions) => Promise<getWorkflowCatalogResponse>;
|
|
227
245
|
/**
|
|
228
246
|
* @summary Send feedback to a payload
|
|
229
247
|
*/
|
|
@@ -236,7 +254,7 @@ export type postWorkflowIdFeedbackResponseSuccess = (postWorkflowIdFeedbackRespo
|
|
|
236
254
|
};
|
|
237
255
|
export type postWorkflowIdFeedbackResponse = (postWorkflowIdFeedbackResponseSuccess);
|
|
238
256
|
export declare const getPostWorkflowIdFeedbackUrl: (id: string) => string;
|
|
239
|
-
export declare const postWorkflowIdFeedback: (id: string, postWorkflowIdFeedbackBody: PostWorkflowIdFeedbackBody, options?:
|
|
257
|
+
export declare const postWorkflowIdFeedback: (id: string, postWorkflowIdFeedbackBody: PostWorkflowIdFeedbackBody, options?: ApiRequestOptions) => Promise<postWorkflowIdFeedbackResponse>;
|
|
240
258
|
/**
|
|
241
259
|
* @summary A dummy post endpoint for test only
|
|
242
260
|
*/
|
|
@@ -249,4 +267,4 @@ export type postHeartbeatResponseSuccess = (postHeartbeatResponse204) & {
|
|
|
249
267
|
};
|
|
250
268
|
export type postHeartbeatResponse = (postHeartbeatResponseSuccess);
|
|
251
269
|
export declare const getPostHeartbeatUrl: () => string;
|
|
252
|
-
export declare const postHeartbeat: (options?:
|
|
270
|
+
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
|
+
}
|