@outputai/cli 0.2.1-next.7fd86e7.0 → 0.2.1-next.bc8ccee.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/generated/api.d.ts +141 -2
- package/dist/api/generated/api.js +32 -0
- package/dist/api/http_client.js +24 -19
- package/dist/assets/docker/docker-compose-dev.yml +1 -1
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.js +5 -1
- package/dist/commands/init.spec.js +10 -5
- package/dist/config.d.ts +6 -38
- package/dist/config.js +22 -42
- package/dist/config.spec.d.ts +1 -0
- package/dist/config.spec.js +75 -0
- package/dist/generated/framework_version.json +1 -1
- package/dist/hooks/init.d.ts +4 -0
- package/dist/hooks/init.js +14 -2
- package/dist/hooks/init.spec.js +79 -5
- package/dist/services/project_scaffold.d.ts +1 -1
- package/dist/services/project_scaffold.js +16 -1
- package/dist/utils/env_loader.js +1 -2
- package/dist/utils/error_handler.js +10 -8
- package/dist/views/workflow/list.js +8 -6
- package/package.json +4 -4
|
@@ -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
|
|
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
|
};
|
package/dist/api/http_client.js
CHANGED
|
@@ -14,26 +14,31 @@ export class HttpError extends Error {
|
|
|
14
14
|
this.name = 'HttpError';
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
request
|
|
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
|
|
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) {
|
package/dist/commands/init.d.ts
CHANGED
package/dist/commands/init.js
CHANGED
|
@@ -18,12 +18,16 @@ export default class Init extends Command {
|
|
|
18
18
|
'skip-env': Flags.boolean({
|
|
19
19
|
description: 'Skip interactive environment variable configuration',
|
|
20
20
|
default: false
|
|
21
|
+
}),
|
|
22
|
+
'skip-git': Flags.boolean({
|
|
23
|
+
description: 'Skip git repository initialization',
|
|
24
|
+
default: false
|
|
21
25
|
})
|
|
22
26
|
};
|
|
23
27
|
async run() {
|
|
24
28
|
try {
|
|
25
29
|
const { args, flags } = await this.parse(Init);
|
|
26
|
-
await runInit(flags['skip-env'], args.folderName);
|
|
30
|
+
await runInit(flags['skip-env'], flags['skip-git'], args.folderName);
|
|
27
31
|
}
|
|
28
32
|
catch (error) {
|
|
29
33
|
if (error instanceof UserCancelledError) {
|
|
@@ -37,7 +37,7 @@ describe('init command', () => {
|
|
|
37
37
|
Object.defineProperty(cmd, 'parse', {
|
|
38
38
|
value: vi.fn().mockResolvedValue({
|
|
39
39
|
args: { folderName: undefined, ...parsedArgs },
|
|
40
|
-
flags: { 'skip-env': false, ...flags }
|
|
40
|
+
flags: { 'skip-env': false, 'skip-git': false, ...flags }
|
|
41
41
|
}),
|
|
42
42
|
configurable: true
|
|
43
43
|
});
|
|
@@ -52,10 +52,10 @@ describe('init command', () => {
|
|
|
52
52
|
expect(cmd.run).toBeDefined();
|
|
53
53
|
expect(typeof cmd.run).toBe('function');
|
|
54
54
|
});
|
|
55
|
-
it('should call runInit with skip-env flag and folder name', async () => {
|
|
55
|
+
it('should call runInit with skip-env flag, skip-git flag, and folder name', async () => {
|
|
56
56
|
const cmd = createTestCommand();
|
|
57
57
|
await cmd.run();
|
|
58
|
-
expect(projectScaffold.runInit).toHaveBeenCalledWith(false, undefined);
|
|
58
|
+
expect(projectScaffold.runInit).toHaveBeenCalledWith(false, false, undefined);
|
|
59
59
|
});
|
|
60
60
|
it('should handle UserCancelledError', async () => {
|
|
61
61
|
const error = new UserCancelledError();
|
|
@@ -89,12 +89,17 @@ describe('init command', () => {
|
|
|
89
89
|
it('should pass skip-env flag to runInit when --skip-env is provided', async () => {
|
|
90
90
|
const cmd = createTestCommand([], { 'skip-env': true });
|
|
91
91
|
await cmd.run();
|
|
92
|
-
expect(projectScaffold.runInit).toHaveBeenCalledWith(true, undefined);
|
|
92
|
+
expect(projectScaffold.runInit).toHaveBeenCalledWith(true, false, undefined);
|
|
93
|
+
});
|
|
94
|
+
it('should pass skip-git flag to runInit when --skip-git is provided', async () => {
|
|
95
|
+
const cmd = createTestCommand([], { 'skip-git': true });
|
|
96
|
+
await cmd.run();
|
|
97
|
+
expect(projectScaffold.runInit).toHaveBeenCalledWith(false, true, undefined);
|
|
93
98
|
});
|
|
94
99
|
it('should pass folder name to runInit when provided', async () => {
|
|
95
100
|
const cmd = createTestCommand([], {}, { folderName: 'my-project' });
|
|
96
101
|
await cmd.run();
|
|
97
|
-
expect(projectScaffold.runInit).toHaveBeenCalledWith(false, 'my-project');
|
|
102
|
+
expect(projectScaffold.runInit).toHaveBeenCalledWith(false, false, 'my-project');
|
|
98
103
|
});
|
|
99
104
|
});
|
|
100
105
|
});
|
package/dist/config.d.ts
CHANGED
|
@@ -1,44 +1,12 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CLI configuration
|
|
3
|
-
*/
|
|
4
1
|
export declare const config: {
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
21
|
-
|
|
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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
+
});
|
package/dist/hooks/init.d.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
import { Hook } from '@oclif/core';
|
|
2
|
+
export declare const INTERACTIVE_FLAGS: string[];
|
|
3
|
+
export declare const GLOBAL_FLAGS: Set<string>;
|
|
4
|
+
export declare const hasInteractiveFlag: (argv: string[]) => boolean;
|
|
5
|
+
export declare const stripGlobalFlags: (argv: string[]) => void;
|
|
2
6
|
declare const hook: Hook<'init'>;
|
|
3
7
|
export default hook;
|
package/dist/hooks/init.js
CHANGED
|
@@ -1,8 +1,20 @@
|
|
|
1
1
|
import { ux } from '@oclif/core';
|
|
2
2
|
import { checkForUpdate } from '#services/version_check.js';
|
|
3
3
|
import { setNonInteractive } from '#utils/interactive.js';
|
|
4
|
-
const
|
|
5
|
-
|
|
4
|
+
export const INTERACTIVE_FLAGS = ['--yes', '--non-interactive'];
|
|
5
|
+
export const GLOBAL_FLAGS = new Set(INTERACTIVE_FLAGS);
|
|
6
|
+
export const hasInteractiveFlag = (argv) => argv.some(arg => INTERACTIVE_FLAGS.includes(arg));
|
|
7
|
+
export const stripGlobalFlags = (argv) => {
|
|
8
|
+
const kept = argv.filter(arg => !GLOBAL_FLAGS.has(arg));
|
|
9
|
+
if (kept.length !== argv.length) {
|
|
10
|
+
argv.splice(0, argv.length, ...kept);
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
const hook = async function (opts) {
|
|
14
|
+
const interactive = hasInteractiveFlag(opts.argv) || hasInteractiveFlag(process.argv);
|
|
15
|
+
stripGlobalFlags(opts.argv);
|
|
16
|
+
stripGlobalFlags(process.argv);
|
|
17
|
+
if (interactive) {
|
|
6
18
|
setNonInteractive(true);
|
|
7
19
|
}
|
|
8
20
|
try {
|
package/dist/hooks/init.spec.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
2
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
3
3
|
import { checkForUpdate } from '#services/version_check.js';
|
|
4
|
+
import { setNonInteractive } from '#utils/interactive.js';
|
|
4
5
|
vi.mock('#services/version_check.js', () => ({
|
|
5
6
|
checkForUpdate: vi.fn()
|
|
6
7
|
}));
|
|
8
|
+
vi.mock('#utils/interactive.js', () => ({
|
|
9
|
+
setNonInteractive: vi.fn()
|
|
10
|
+
}));
|
|
7
11
|
vi.mock('@oclif/core', () => ({
|
|
8
12
|
ux: {
|
|
9
13
|
stdout: vi.fn(),
|
|
@@ -11,7 +15,7 @@ vi.mock('@oclif/core', () => ({
|
|
|
11
15
|
}
|
|
12
16
|
}));
|
|
13
17
|
import { ux } from '@oclif/core';
|
|
14
|
-
import hook from './init.js';
|
|
18
|
+
import hook, { hasInteractiveFlag, stripGlobalFlags } from './init.js';
|
|
15
19
|
describe('init hook', () => {
|
|
16
20
|
beforeEach(() => {
|
|
17
21
|
vi.clearAllMocks();
|
|
@@ -26,7 +30,7 @@ describe('init hook', () => {
|
|
|
26
30
|
latestVersion: '1.0.0'
|
|
27
31
|
});
|
|
28
32
|
const ctx = createHookContext();
|
|
29
|
-
await hook.call(ctx, {});
|
|
33
|
+
await hook.call(ctx, { argv: [], id: undefined });
|
|
30
34
|
expect(checkForUpdate).toHaveBeenCalledWith('0.8.4', '/tmp/test-cache');
|
|
31
35
|
expect(ux.stdout).toHaveBeenCalled();
|
|
32
36
|
const output = vi.mocked(ux.stdout).mock.calls.map(c => c[0]).join('\n');
|
|
@@ -42,13 +46,83 @@ describe('init hook', () => {
|
|
|
42
46
|
latestVersion: '0.8.4'
|
|
43
47
|
});
|
|
44
48
|
const ctx = createHookContext();
|
|
45
|
-
await hook.call(ctx, {});
|
|
49
|
+
await hook.call(ctx, { argv: [], id: undefined });
|
|
46
50
|
expect(ux.stdout).not.toHaveBeenCalled();
|
|
47
51
|
});
|
|
48
52
|
it('should silently handle errors', async () => {
|
|
49
53
|
vi.mocked(checkForUpdate).mockRejectedValue(new Error('network failure'));
|
|
50
54
|
const ctx = createHookContext();
|
|
51
|
-
await hook.call(ctx, {});
|
|
55
|
+
await hook.call(ctx, { argv: [], id: undefined });
|
|
52
56
|
expect(ux.stdout).not.toHaveBeenCalled();
|
|
53
57
|
});
|
|
58
|
+
describe('global interactive flags', () => {
|
|
59
|
+
const originalArgv = process.argv;
|
|
60
|
+
beforeEach(() => {
|
|
61
|
+
vi.mocked(checkForUpdate).mockResolvedValue({
|
|
62
|
+
updateAvailable: false,
|
|
63
|
+
currentVersion: '0.8.4',
|
|
64
|
+
latestVersion: '0.8.4'
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
afterEach(() => {
|
|
68
|
+
process.argv = originalArgv;
|
|
69
|
+
});
|
|
70
|
+
it('should mutate opts.argv in place to strip --yes', async () => {
|
|
71
|
+
process.argv = ['node', 'run.js', 'init', '--yes', 'my-project'];
|
|
72
|
+
const optsArgv = ['--yes', 'my-project'];
|
|
73
|
+
const argvRef = optsArgv;
|
|
74
|
+
const ctx = createHookContext();
|
|
75
|
+
await hook.call(ctx, { argv: optsArgv, id: 'init' });
|
|
76
|
+
expect(setNonInteractive).toHaveBeenCalledWith(true);
|
|
77
|
+
expect(optsArgv).toBe(argvRef);
|
|
78
|
+
expect(optsArgv).toEqual(['my-project']);
|
|
79
|
+
expect(process.argv).toEqual(['node', 'run.js', 'init', 'my-project']);
|
|
80
|
+
});
|
|
81
|
+
it('should mutate opts.argv in place to strip --non-interactive', async () => {
|
|
82
|
+
process.argv = ['node', 'run.js', 'init', '--non-interactive'];
|
|
83
|
+
const optsArgv = ['--non-interactive'];
|
|
84
|
+
const ctx = createHookContext();
|
|
85
|
+
await hook.call(ctx, { argv: optsArgv, id: 'init' });
|
|
86
|
+
expect(setNonInteractive).toHaveBeenCalledWith(true);
|
|
87
|
+
expect(optsArgv).toEqual([]);
|
|
88
|
+
expect(process.argv).toEqual(['node', 'run.js', 'init']);
|
|
89
|
+
});
|
|
90
|
+
it('should leave argv untouched when no global flag is present', async () => {
|
|
91
|
+
process.argv = ['node', 'run.js', 'init', '--skip-env'];
|
|
92
|
+
const optsArgv = ['--skip-env'];
|
|
93
|
+
const ctx = createHookContext();
|
|
94
|
+
await hook.call(ctx, { argv: optsArgv, id: 'init' });
|
|
95
|
+
expect(setNonInteractive).not.toHaveBeenCalled();
|
|
96
|
+
expect(optsArgv).toEqual(['--skip-env']);
|
|
97
|
+
expect(process.argv).toEqual(['node', 'run.js', 'init', '--skip-env']);
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
describe('hasInteractiveFlag', () => {
|
|
101
|
+
it('returns true when --yes is present', () => {
|
|
102
|
+
expect(hasInteractiveFlag(['init', '--yes', 'foo'])).toBe(true);
|
|
103
|
+
});
|
|
104
|
+
it('returns true when --non-interactive is present', () => {
|
|
105
|
+
expect(hasInteractiveFlag(['--non-interactive'])).toBe(true);
|
|
106
|
+
});
|
|
107
|
+
it('returns false for unrelated flags', () => {
|
|
108
|
+
expect(hasInteractiveFlag(['init', '--skip-env', '--skip-git'])).toBe(false);
|
|
109
|
+
});
|
|
110
|
+
it('returns false for an empty argv', () => {
|
|
111
|
+
expect(hasInteractiveFlag([])).toBe(false);
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
describe('stripGlobalFlags', () => {
|
|
115
|
+
it('mutates argv in place to remove global flags', () => {
|
|
116
|
+
const argv = ['init', '--yes', 'foo', '--non-interactive'];
|
|
117
|
+
const ref = argv;
|
|
118
|
+
stripGlobalFlags(argv);
|
|
119
|
+
expect(argv).toBe(ref);
|
|
120
|
+
expect(argv).toEqual(['init', 'foo']);
|
|
121
|
+
});
|
|
122
|
+
it('leaves argv untouched when no global flag is present', () => {
|
|
123
|
+
const argv = ['init', '--skip-env'];
|
|
124
|
+
stripGlobalFlags(argv);
|
|
125
|
+
expect(argv).toEqual(['init', '--skip-env']);
|
|
126
|
+
});
|
|
127
|
+
});
|
|
54
128
|
});
|
|
@@ -27,5 +27,5 @@ export declare function createSigintHandler(projectPath: string, folderCreated:
|
|
|
27
27
|
* @param skipEnv - Whether to skip environment configuration prompts
|
|
28
28
|
* @param folderName - Optional folder name to skip folder name prompt
|
|
29
29
|
*/
|
|
30
|
-
export declare function runInit(skipEnv?: boolean, folderName?: string): Promise<void>;
|
|
30
|
+
export declare function runInit(skipEnv?: boolean, skipGit?: boolean, folderName?: string): Promise<void>;
|
|
31
31
|
export {};
|
|
@@ -119,6 +119,18 @@ async function executeNpmInstall(projectPath) {
|
|
|
119
119
|
async function initializeAgents(projectPath) {
|
|
120
120
|
await initializeAgentConfig({ projectRoot: projectPath, force: false });
|
|
121
121
|
}
|
|
122
|
+
async function maybeInitializeGit(projectPath) {
|
|
123
|
+
const shouldInit = await confirm({
|
|
124
|
+
message: 'Initialize a git repository?',
|
|
125
|
+
default: true
|
|
126
|
+
});
|
|
127
|
+
if (!shouldInit) {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
return executeCommandWithMessages(async () => {
|
|
131
|
+
await executeCommand('git', ['init'], projectPath);
|
|
132
|
+
}, 'Initializing git repository...', 'Git repository initialized');
|
|
133
|
+
}
|
|
122
134
|
/**
|
|
123
135
|
* Format error message for init errors
|
|
124
136
|
* Single responsibility: only format error messages, no cleanup logic
|
|
@@ -172,7 +184,7 @@ function handleRunInitError(error, projectPath, projectFolderCreated) {
|
|
|
172
184
|
* @param skipEnv - Whether to skip environment configuration prompts
|
|
173
185
|
* @param folderName - Optional folder name to skip folder name prompt
|
|
174
186
|
*/
|
|
175
|
-
export async function runInit(skipEnv = false, folderName) {
|
|
187
|
+
export async function runInit(skipEnv = false, skipGit = false, folderName) {
|
|
176
188
|
// Track state for SIGINT cleanup using an object to avoid let
|
|
177
189
|
const state = {
|
|
178
190
|
projectFolderCreated: false,
|
|
@@ -210,6 +222,9 @@ export async function runInit(skipEnv = false, folderName) {
|
|
|
210
222
|
await fs.copyFile(path.join(config.projectPath, '.env.example'), path.join(config.projectPath, '.env'));
|
|
211
223
|
await executeCommandWithMessages(() => initializeAgents(config.projectPath), 'Initializing agent system...', 'Agent system initialized');
|
|
212
224
|
const installSuccess = await executeCommandWithMessages(() => executeNpmInstall(config.projectPath), 'Installing dependencies...', 'Dependencies installed');
|
|
225
|
+
if (!skipGit) {
|
|
226
|
+
await maybeInitializeGit(config.projectPath);
|
|
227
|
+
}
|
|
213
228
|
const nextSteps = getProjectSuccessMessage(config.folderName, installSuccess, credentialsConfigured);
|
|
214
229
|
ux.stdout('Project created successfully!');
|
|
215
230
|
ux.stdout(nextSteps);
|
package/dist/utils/env_loader.js
CHANGED
|
@@ -7,11 +7,10 @@ import { existsSync } from 'node:fs';
|
|
|
7
7
|
import { resolve } from 'node:path';
|
|
8
8
|
import * as dotenv from 'dotenv';
|
|
9
9
|
import debugFactory from 'debug';
|
|
10
|
-
import { config } from '#config.js';
|
|
11
10
|
const debug = debugFactory('output-cli:env-loader');
|
|
12
11
|
export function loadEnvironment() {
|
|
13
12
|
const cwd = process.cwd();
|
|
14
|
-
const envFile =
|
|
13
|
+
const envFile = process.env.OUTPUT_CLI_ENV || '.env';
|
|
15
14
|
const envPath = resolve(cwd, envFile);
|
|
16
15
|
if (!existsSync(envPath)) {
|
|
17
16
|
debug(`Warning: Env file not found: ${envPath}`);
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { config } from '#config.js';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
2
|
+
function getDefaultMessages() {
|
|
3
|
+
return {
|
|
4
|
+
ECONNREFUSED: `Connection refused to ${config.apiUrl}. Is the API server running?`,
|
|
5
|
+
401: 'Authentication failed. Check your OUTPUT_API_AUTH_TOKEN.',
|
|
6
|
+
404: 'Resource not found.',
|
|
7
|
+
500: 'Server error.',
|
|
8
|
+
UNKNOWN: 'An unknown error occurred.'
|
|
9
|
+
};
|
|
10
|
+
}
|
|
9
11
|
/**
|
|
10
12
|
* Extract error type and message from API response data
|
|
11
13
|
*/
|
|
@@ -49,7 +51,7 @@ function getDetailedErrorMessage(error) {
|
|
|
49
51
|
}
|
|
50
52
|
export function handleApiError(error, errorFn, overrides = {}) {
|
|
51
53
|
const apiError = error;
|
|
52
|
-
const errorMessages = { ...
|
|
54
|
+
const errorMessages = { ...getDefaultMessages(), ...overrides };
|
|
53
55
|
if (apiError.code === 'ECONNREFUSED' || apiError.cause?.code === 'ECONNREFUSED') {
|
|
54
56
|
errorFn(errorMessages.ECONNREFUSED, { exit: 1 });
|
|
55
57
|
}
|
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { useState, useEffect, useRef, useMemo } from 'react';
|
|
3
3
|
import { Box, Text, useInput } from 'ink';
|
|
4
4
|
import Spinner from 'ink-spinner';
|
|
5
|
-
import {
|
|
5
|
+
import { getWorkflowIdRunsRidResult } from '#api/generated/api.js';
|
|
6
6
|
import { StatusIcon, statusColor } from '#components/status_icon.js';
|
|
7
7
|
import { elapsedMs, formatDurationCompact } from '#utils/date_formatter.js';
|
|
8
8
|
import { CommandFooter } from '#components/command_footer.js';
|
|
@@ -59,17 +59,19 @@ export const WorkflowListView = ({ runs, onBack }) => {
|
|
|
59
59
|
const clampedIndex = Math.min(selectedIndex, Math.max(0, sortedRuns.length - 1));
|
|
60
60
|
const selectedRun = sortedRuns[clampedIndex];
|
|
61
61
|
const selectedWorkflowId = selectedRun?.workflowId;
|
|
62
|
+
const selectedRunId = selectedRun?.runId;
|
|
62
63
|
useEffect(() => {
|
|
63
64
|
if (clampedIndex !== selectedIndex) {
|
|
64
65
|
setSelectedIndex(clampedIndex);
|
|
65
66
|
}
|
|
66
67
|
}, [clampedIndex, selectedIndex]);
|
|
67
68
|
useEffect(() => {
|
|
68
|
-
if (!selectedWorkflowId) {
|
|
69
|
+
if (!selectedWorkflowId || !selectedRunId) {
|
|
69
70
|
setDetail(null);
|
|
70
71
|
return;
|
|
71
72
|
}
|
|
72
|
-
const
|
|
73
|
+
const cacheKey = `${selectedWorkflowId}:${selectedRunId}`;
|
|
74
|
+
const cached = cacheRef.current.get(cacheKey);
|
|
73
75
|
if (cached) {
|
|
74
76
|
setDetail(cached);
|
|
75
77
|
setDetailLoading(false);
|
|
@@ -77,13 +79,13 @@ export const WorkflowListView = ({ runs, onBack }) => {
|
|
|
77
79
|
}
|
|
78
80
|
const currentFetchId = ++fetchIdRef.current;
|
|
79
81
|
setDetailLoading(true);
|
|
80
|
-
|
|
82
|
+
getWorkflowIdRunsRidResult(selectedWorkflowId, selectedRunId)
|
|
81
83
|
.then(response => {
|
|
82
84
|
if (fetchIdRef.current !== currentFetchId) {
|
|
83
85
|
return;
|
|
84
86
|
}
|
|
85
87
|
const data = response.data;
|
|
86
|
-
cacheRef.current.set(
|
|
88
|
+
cacheRef.current.set(cacheKey, data);
|
|
87
89
|
setDetail(data);
|
|
88
90
|
setDetailLoading(false);
|
|
89
91
|
})
|
|
@@ -94,7 +96,7 @@ export const WorkflowListView = ({ runs, onBack }) => {
|
|
|
94
96
|
setDetail(null);
|
|
95
97
|
setDetailLoading(false);
|
|
96
98
|
});
|
|
97
|
-
}, [selectedWorkflowId]);
|
|
99
|
+
}, [selectedWorkflowId, selectedRunId]);
|
|
98
100
|
useInput((input, key) => {
|
|
99
101
|
if (key.upArrow) {
|
|
100
102
|
setSelectedIndex(i => Math.max(0, i - 1));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@outputai/cli",
|
|
3
|
-
"version": "0.2.1-next.
|
|
3
|
+
"version": "0.2.1-next.bc8ccee.0",
|
|
4
4
|
"description": "CLI for Output.ai workflow generation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -36,9 +36,9 @@
|
|
|
36
36
|
"semver": "7.7.4",
|
|
37
37
|
"undici": "8.0.2",
|
|
38
38
|
"yaml": "^2.8.3",
|
|
39
|
-
"@outputai/
|
|
40
|
-
"@outputai/
|
|
41
|
-
"@outputai/llm": "0.2.1-next.
|
|
39
|
+
"@outputai/credentials": "0.2.1-next.bc8ccee.0",
|
|
40
|
+
"@outputai/evals": "0.2.1-next.bc8ccee.0",
|
|
41
|
+
"@outputai/llm": "0.2.1-next.bc8ccee.0"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@types/cli-progress": "3.11.6",
|