@outputai/cli 0.1.0 → 0.1.1-dev.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 +45 -0
- package/dist/api/generated/api.js +11 -0
- package/dist/commands/credentials/edit.js +6 -2
- package/dist/commands/workflow/reset.d.ts +14 -0
- package/dist/commands/workflow/reset.js +51 -0
- package/dist/services/copy_assets.spec.js +4 -0
- package/dist/templates/project/config/costs.yml.template +29 -0
- package/package.json +4 -4
|
@@ -277,6 +277,18 @@ export type PostWorkflowIdTerminate200 = {
|
|
|
277
277
|
terminated?: boolean;
|
|
278
278
|
workflowId?: string;
|
|
279
279
|
};
|
|
280
|
+
export type PostWorkflowIdResetBody = {
|
|
281
|
+
/** The name of the step to reset after */
|
|
282
|
+
stepName: string;
|
|
283
|
+
/** Optional reason for the reset */
|
|
284
|
+
reason?: string;
|
|
285
|
+
};
|
|
286
|
+
export type PostWorkflowIdReset200 = {
|
|
287
|
+
/** The original workflow ID */
|
|
288
|
+
workflowId?: string;
|
|
289
|
+
/** The run ID of the new execution created by the reset */
|
|
290
|
+
runId?: string;
|
|
291
|
+
};
|
|
280
292
|
/**
|
|
281
293
|
* The workflow execution status
|
|
282
294
|
*/
|
|
@@ -518,6 +530,39 @@ export type postWorkflowIdTerminateResponseError = (postWorkflowIdTerminateRespo
|
|
|
518
530
|
export type postWorkflowIdTerminateResponse = (postWorkflowIdTerminateResponseSuccess | postWorkflowIdTerminateResponseError);
|
|
519
531
|
export declare const getPostWorkflowIdTerminateUrl: (id: string) => string;
|
|
520
532
|
export declare const postWorkflowIdTerminate: (id: string, postWorkflowIdTerminateBody: PostWorkflowIdTerminateBody, options?: ApiRequestOptions) => Promise<postWorkflowIdTerminateResponse>;
|
|
533
|
+
/**
|
|
534
|
+
* Resets a workflow execution to the point after a completed step, creating a new run that replays from that point. The current execution is terminated.
|
|
535
|
+
* @summary Reset a workflow to re-run from after a specific step
|
|
536
|
+
*/
|
|
537
|
+
export type postWorkflowIdResetResponse200 = {
|
|
538
|
+
data: PostWorkflowIdReset200;
|
|
539
|
+
status: 200;
|
|
540
|
+
};
|
|
541
|
+
export type postWorkflowIdResetResponse400 = {
|
|
542
|
+
data: BadRequestResponse;
|
|
543
|
+
status: 400;
|
|
544
|
+
};
|
|
545
|
+
export type postWorkflowIdResetResponse404 = {
|
|
546
|
+
data: NotFoundResponse;
|
|
547
|
+
status: 404;
|
|
548
|
+
};
|
|
549
|
+
export type postWorkflowIdResetResponse409 = {
|
|
550
|
+
data: ErrorResponse;
|
|
551
|
+
status: 409;
|
|
552
|
+
};
|
|
553
|
+
export type postWorkflowIdResetResponse500 = {
|
|
554
|
+
data: InternalServerErrorResponse;
|
|
555
|
+
status: 500;
|
|
556
|
+
};
|
|
557
|
+
export type postWorkflowIdResetResponseSuccess = (postWorkflowIdResetResponse200) & {
|
|
558
|
+
headers: Headers;
|
|
559
|
+
};
|
|
560
|
+
export type postWorkflowIdResetResponseError = (postWorkflowIdResetResponse400 | postWorkflowIdResetResponse404 | postWorkflowIdResetResponse409 | postWorkflowIdResetResponse500) & {
|
|
561
|
+
headers: Headers;
|
|
562
|
+
};
|
|
563
|
+
export type postWorkflowIdResetResponse = (postWorkflowIdResetResponseSuccess | postWorkflowIdResetResponseError);
|
|
564
|
+
export declare const getPostWorkflowIdResetUrl: (id: string) => string;
|
|
565
|
+
export declare const postWorkflowIdReset: (id: string, postWorkflowIdResetBody: PostWorkflowIdResetBody, options?: ApiRequestOptions) => Promise<postWorkflowIdResetResponse>;
|
|
521
566
|
/**
|
|
522
567
|
* @summary Return the result of a workflow
|
|
523
568
|
*/
|
|
@@ -107,6 +107,17 @@ export const postWorkflowIdTerminate = async (id, postWorkflowIdTerminateBody, o
|
|
|
107
107
|
body: JSON.stringify(postWorkflowIdTerminateBody)
|
|
108
108
|
});
|
|
109
109
|
};
|
|
110
|
+
export const getPostWorkflowIdResetUrl = (id) => {
|
|
111
|
+
return `/workflow/${id}/reset`;
|
|
112
|
+
};
|
|
113
|
+
export const postWorkflowIdReset = async (id, postWorkflowIdResetBody, options) => {
|
|
114
|
+
return customFetchInstance(getPostWorkflowIdResetUrl(id), {
|
|
115
|
+
...options,
|
|
116
|
+
method: 'POST',
|
|
117
|
+
headers: { 'Content-Type': 'application/json', ...options?.headers },
|
|
118
|
+
body: JSON.stringify(postWorkflowIdResetBody)
|
|
119
|
+
});
|
|
120
|
+
};
|
|
110
121
|
export const getGetWorkflowIdResultUrl = (id) => {
|
|
111
122
|
return `/workflow/${id}/result`;
|
|
112
123
|
};
|
|
@@ -32,12 +32,16 @@ export default class CredentialsEdit extends Command {
|
|
|
32
32
|
if (!credentialsExist(environment, workflow)) {
|
|
33
33
|
this.error(`No credentials file found at ${resolveCredentialsPath(environment, workflow)}. Run "output credentials init" first.`);
|
|
34
34
|
}
|
|
35
|
-
const
|
|
35
|
+
const editorEnv = process.env.EDITOR || process.env.VISUAL || 'vi';
|
|
36
|
+
const [editorCmd, ...editorArgs] = editorEnv.split(/\s+/);
|
|
36
37
|
const plaintext = decryptCredentials(environment, workflow);
|
|
37
38
|
const tmpFile = path.join(os.tmpdir(), `output-credentials-${Date.now()}.yml`);
|
|
38
39
|
try {
|
|
39
40
|
fs.writeFileSync(tmpFile, plaintext, { mode: 0o600 });
|
|
40
|
-
const result = spawnSync(
|
|
41
|
+
const result = spawnSync(editorCmd, [...editorArgs, tmpFile], { stdio: 'inherit' });
|
|
42
|
+
if (result.error) {
|
|
43
|
+
this.error(`Failed to launch editor: ${result.error.message}`);
|
|
44
|
+
}
|
|
41
45
|
if (result.status !== 0) {
|
|
42
46
|
this.error(`Editor exited with non-zero status: ${result.status}`);
|
|
43
47
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
export default class WorkflowReset extends Command {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static args: {
|
|
6
|
+
workflowId: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
7
|
+
};
|
|
8
|
+
static flags: {
|
|
9
|
+
step: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
reason: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
};
|
|
12
|
+
run(): Promise<void>;
|
|
13
|
+
catch(error: Error): Promise<void>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Args, Command, Flags } from '@oclif/core';
|
|
2
|
+
import { postWorkflowIdReset } from '#api/generated/api.js';
|
|
3
|
+
import { handleApiError } from '#utils/error_handler.js';
|
|
4
|
+
export default class WorkflowReset extends Command {
|
|
5
|
+
static description = 'Reset a workflow to re-run from after a specific step';
|
|
6
|
+
static examples = [
|
|
7
|
+
'<%= config.bin %> <%= command.id %> wf-12345 --step generateBlogPost',
|
|
8
|
+
'<%= config.bin %> <%= command.id %> wf-12345 --step consolidateCompetitors --reason "Retry with updated prompt"'
|
|
9
|
+
];
|
|
10
|
+
static args = {
|
|
11
|
+
workflowId: Args.string({
|
|
12
|
+
description: 'The workflow ID to reset',
|
|
13
|
+
required: true
|
|
14
|
+
})
|
|
15
|
+
};
|
|
16
|
+
static flags = {
|
|
17
|
+
step: Flags.string({
|
|
18
|
+
char: 's',
|
|
19
|
+
description: 'The step name to reset after',
|
|
20
|
+
required: true
|
|
21
|
+
}),
|
|
22
|
+
reason: Flags.string({
|
|
23
|
+
char: 'r',
|
|
24
|
+
description: 'Reason for the reset'
|
|
25
|
+
})
|
|
26
|
+
};
|
|
27
|
+
async run() {
|
|
28
|
+
const { args, flags } = await this.parse(WorkflowReset);
|
|
29
|
+
this.log(`Resetting workflow: ${args.workflowId} to after step: ${flags.step}...`);
|
|
30
|
+
const response = await postWorkflowIdReset(args.workflowId, { stepName: flags.step, reason: flags.reason });
|
|
31
|
+
if (!response || !response.data) {
|
|
32
|
+
this.error('API returned invalid response', { exit: 1 });
|
|
33
|
+
}
|
|
34
|
+
const data = response.data;
|
|
35
|
+
const output = [
|
|
36
|
+
'Workflow reset successfully',
|
|
37
|
+
'',
|
|
38
|
+
`Workflow ID: ${args.workflowId}`,
|
|
39
|
+
`New Run ID: ${data.runId}`,
|
|
40
|
+
`Reset after step: ${flags.step}`,
|
|
41
|
+
flags.reason ? `Reason: ${flags.reason}` : ''
|
|
42
|
+
].filter(Boolean).join('\n');
|
|
43
|
+
this.log(`\n${output}`);
|
|
44
|
+
}
|
|
45
|
+
async catch(error) {
|
|
46
|
+
return handleApiError(error, (...args) => this.error(...args), {
|
|
47
|
+
404: 'Workflow or step not found. Check the workflow ID and step name.',
|
|
48
|
+
409: 'Step has not completed yet. Cannot reset to an incomplete step.'
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -15,4 +15,8 @@ describe('copy-assets build output', () => {
|
|
|
15
15
|
expect(fs.existsSync(filePath), `Missing dotfile template in dist: ${dotfile}`).toBe(true);
|
|
16
16
|
}
|
|
17
17
|
});
|
|
18
|
+
it('should include config templates in dist/templates/project/', () => {
|
|
19
|
+
const configFile = path.join(distTemplatesDir, 'config', 'costs.yml.template');
|
|
20
|
+
expect(fs.existsSync(configFile), 'Missing config/costs.yml.template in dist').toBe(true);
|
|
21
|
+
});
|
|
18
22
|
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Token & API Pricing Configuration
|
|
2
|
+
# Prices are per million tokens, in USD
|
|
3
|
+
# Entries here are merged with built-in defaults (project values take precedence)
|
|
4
|
+
# Model names support prefix matching: 'claude-sonnet-4' matches 'claude-sonnet-4-20250514'
|
|
5
|
+
# models:
|
|
6
|
+
# # =============================================================================
|
|
7
|
+
# # Anthropic
|
|
8
|
+
# # =============================================================================
|
|
9
|
+
# claude-haiku-4-5:
|
|
10
|
+
# provider: anthropic
|
|
11
|
+
# input: 1.00
|
|
12
|
+
# output: 5.00
|
|
13
|
+
# cached_input: 0.10
|
|
14
|
+
|
|
15
|
+
# claude-sonnet-4:
|
|
16
|
+
# provider: anthropic
|
|
17
|
+
# input: 3.00
|
|
18
|
+
# output: 15.00
|
|
19
|
+
# cached_input: 0.30
|
|
20
|
+
|
|
21
|
+
# services:
|
|
22
|
+
# # =============================================================================
|
|
23
|
+
# # Jina Reader API - Token-based
|
|
24
|
+
# # =============================================================================
|
|
25
|
+
# jina:
|
|
26
|
+
# type: token
|
|
27
|
+
# url_pattern: "r.jina.ai"
|
|
28
|
+
# usage_path: "body.data.usage.tokens"
|
|
29
|
+
# per_million: 0.045
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@outputai/cli",
|
|
3
|
-
"version": "0.1.0",
|
|
3
|
+
"version": "0.1.1-dev.0",
|
|
4
4
|
"description": "CLI for Output.ai workflow generation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -33,9 +33,9 @@
|
|
|
33
33
|
"log-update": "7.2.0",
|
|
34
34
|
"semver": "7.7.4",
|
|
35
35
|
"yaml": "^2.7.1",
|
|
36
|
-
"@outputai/
|
|
37
|
-
"@outputai/
|
|
38
|
-
"@outputai/
|
|
36
|
+
"@outputai/evals": "0.1.1-dev.0",
|
|
37
|
+
"@outputai/llm": "0.1.1-dev.0",
|
|
38
|
+
"@outputai/credentials": "0.1.1-dev.0"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@types/cli-progress": "3.11.6",
|