@openworkflow/cli 0.3.1 → 0.4.1
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/cli.js +6 -2
- package/dist/commands.d.ts +18 -7
- package/dist/commands.d.ts.map +1 -1
- package/dist/commands.js +36 -20
- package/dist/config.d.ts +7 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +40 -13
- package/dist/skills.d.ts +61 -0
- package/dist/skills.d.ts.map +1 -0
- package/dist/skills.js +286 -0
- package/package.json +5 -6
package/dist/cli.js
CHANGED
|
@@ -3,22 +3,24 @@
|
|
|
3
3
|
import { dashboard, doctor, getVersion, init, workerStart, } from "./commands.js";
|
|
4
4
|
import { withErrorHandling } from "./errors.js";
|
|
5
5
|
import { Command } from "commander";
|
|
6
|
-
// openworkflow
|
|
6
|
+
// openworkflow
|
|
7
7
|
const program = new Command();
|
|
8
8
|
program
|
|
9
9
|
.name("openworkflow")
|
|
10
|
-
.alias("ow")
|
|
11
10
|
.description("OpenWorkflow CLI - learn more at https://openworkflow.dev")
|
|
11
|
+
.usage("<command> [options]")
|
|
12
12
|
.version(getVersion());
|
|
13
13
|
// init
|
|
14
14
|
program
|
|
15
15
|
.command("init")
|
|
16
16
|
.description("initialize OpenWorkflow")
|
|
17
|
+
.option("--config <path>", "path to OpenWorkflow config file")
|
|
17
18
|
.action(withErrorHandling(init));
|
|
18
19
|
// doctor
|
|
19
20
|
program
|
|
20
21
|
.command("doctor")
|
|
21
22
|
.description("check configuration and list available workflows")
|
|
23
|
+
.option("--config <path>", "path to OpenWorkflow config file")
|
|
22
24
|
.action(withErrorHandling(doctor));
|
|
23
25
|
// worker
|
|
24
26
|
const workerCmd = program.command("worker").description("manage workers");
|
|
@@ -27,10 +29,12 @@ workerCmd
|
|
|
27
29
|
.command("start")
|
|
28
30
|
.description("start a worker to process workflows")
|
|
29
31
|
.option("-c, --concurrency <number>", "number of concurrent workflows to process", Number.parseInt)
|
|
32
|
+
.option("--config <path>", "path to OpenWorkflow config file")
|
|
30
33
|
.action(withErrorHandling(workerStart));
|
|
31
34
|
// dashboard
|
|
32
35
|
program
|
|
33
36
|
.command("dashboard")
|
|
34
37
|
.description("start the dashboard to view workflow runs")
|
|
38
|
+
.option("--config <path>", "path to OpenWorkflow config file")
|
|
35
39
|
.action(withErrorHandling(dashboard));
|
|
36
40
|
await program.parseAsync(process.argv);
|
package/dist/commands.d.ts
CHANGED
|
@@ -1,23 +1,34 @@
|
|
|
1
1
|
import { WorkerConfig } from "./config.js";
|
|
2
|
+
interface CommandOptions {
|
|
3
|
+
config?: string;
|
|
4
|
+
}
|
|
2
5
|
/**
|
|
3
6
|
* openworkflow -V | --version
|
|
4
7
|
* @returns the version string, or "-" if it cannot be determined
|
|
5
8
|
*/
|
|
6
9
|
export declare function getVersion(): string;
|
|
7
|
-
/**
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
/**
|
|
11
|
+
* openworkflow init
|
|
12
|
+
* @param options - Command options
|
|
13
|
+
*/
|
|
14
|
+
export declare function init(options?: CommandOptions): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* openworkflow doctor
|
|
17
|
+
* @param options - Command options
|
|
18
|
+
*/
|
|
19
|
+
export declare function doctor(options?: CommandOptions): Promise<void>;
|
|
20
|
+
export type WorkerStartOptions = WorkerConfig & CommandOptions;
|
|
11
21
|
/**
|
|
12
22
|
* openworkflow worker start
|
|
13
|
-
* @param
|
|
23
|
+
* @param options - Worker config and command options
|
|
14
24
|
*/
|
|
15
|
-
export declare function workerStart(
|
|
25
|
+
export declare function workerStart(options?: WorkerStartOptions): Promise<void>;
|
|
16
26
|
/**
|
|
17
27
|
* openworkflow dashboard
|
|
18
28
|
* Starts the dashboard by delegating to `@openworkflow/dashboard` via npx.
|
|
29
|
+
* @param options - Command options
|
|
19
30
|
*/
|
|
20
|
-
export declare function dashboard(): Promise<void>;
|
|
31
|
+
export declare function dashboard(options?: CommandOptions): Promise<void>;
|
|
21
32
|
/**
|
|
22
33
|
* Discover workflow files from directories. Recursively scans directories for
|
|
23
34
|
* workflow files with supported extensions (.ts, .js, .mjs, .cjs).
|
package/dist/commands.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../commands.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,
|
|
1
|
+
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../commands.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAkC,MAAM,aAAa,CAAC;AAiC3E,UAAU,cAAc;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAoBnC;AAED;;;GAGG;AACH,wBAAsB,IAAI,CAAC,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoItE;AAED;;;GAGG;AACH,wBAAsB,MAAM,CAAC,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAmDxE;AAED,MAAM,MAAM,kBAAkB,GAAG,YAAY,GAAG,cAAc,CAAC;AAE/D;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,IAAI,CAAC,CAuEf;AAED;;;;GAIG;AACH,wBAAsB,SAAS,CAAC,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CA2D3E;AAwOD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,EAAE,MAAM,EACf,cAAc,GAAE,MAAM,EAAO,GAC5B,MAAM,EAAE,CA4CV;AA8VD,UAAU,oBAAoB;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC1C;AAsBD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,QAAQ,CAAC,oBAAoB,CAAC,GAAG,IAAI,GACjD,MAAM,CAMR;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,WAAW,EAAE,QAAQ,CAAC,oBAAoB,CAAC,GAAG,IAAI,GACjD,MAAM,CAKR;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,WAAW,EAAE,QAAQ,CAAC,oBAAoB,CAAC,GAAG,IAAI,GACjD,MAAM,CAKR;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,QAAQ,CAAC,oBAAoB,CAAC,GAAG,IAAI,GACjD,MAAM,CAKR"}
|
package/dist/commands.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { loadConfig } from "./config.js";
|
|
1
|
+
import { loadConfig, loadConfigFromPath } from "./config.js";
|
|
2
2
|
import { CLIError } from "./errors.js";
|
|
3
3
|
import { CONFIG, HELLO_WORLD_RUNNER, HELLO_WORLD_WORKFLOW, POSTGRES_CLIENT, POSTGRES_PROD_SQLITE_DEV_CLIENT, SQLITE_CLIENT, } from "./templates.js";
|
|
4
4
|
import * as p from "@clack/prompts";
|
|
@@ -36,10 +36,14 @@ export function getVersion() {
|
|
|
36
36
|
}
|
|
37
37
|
return "-";
|
|
38
38
|
}
|
|
39
|
-
/**
|
|
40
|
-
|
|
39
|
+
/**
|
|
40
|
+
* openworkflow init
|
|
41
|
+
* @param options - Command options
|
|
42
|
+
*/
|
|
43
|
+
export async function init(options = {}) {
|
|
44
|
+
const configPath = options.config;
|
|
41
45
|
p.intro("Initializing OpenWorkflow...");
|
|
42
|
-
const { configFile } = await loadConfigWithEnv();
|
|
46
|
+
const { configFile } = await loadConfigWithEnv(configPath);
|
|
43
47
|
let configFileToDelete = null;
|
|
44
48
|
if (configFile) {
|
|
45
49
|
const shouldOverride = await p.confirm({
|
|
@@ -89,7 +93,7 @@ export async function init() {
|
|
|
89
93
|
if (!packageJson) {
|
|
90
94
|
throw new CLIError("No package.json found.", "Please create a package.json file first by running `npm init` or `npm init -y`.");
|
|
91
95
|
}
|
|
92
|
-
const configFileName = getConfigFileName(packageJson);
|
|
96
|
+
const configFileName = configPath ?? getConfigFileName(packageJson);
|
|
93
97
|
const clientFileName = getClientFileName(packageJson);
|
|
94
98
|
const exampleWorkflowFileName = getExampleWorkflowFileName(packageJson);
|
|
95
99
|
const runFileName = getRunFileName(packageJson);
|
|
@@ -141,12 +145,16 @@ export async function init() {
|
|
|
141
145
|
p.note(`➡️ Start a worker:\n$ npx @openworkflow/cli worker start\n\n➡️ Run the example workflow:\n$ ${runCommand}\n\n➡️ View the dashboard:\n$ npx @openworkflow/cli dashboard`, "Next steps");
|
|
142
146
|
p.outro("✅ Setup complete!");
|
|
143
147
|
}
|
|
144
|
-
/**
|
|
145
|
-
|
|
148
|
+
/**
|
|
149
|
+
* openworkflow doctor
|
|
150
|
+
* @param options - Command options
|
|
151
|
+
*/
|
|
152
|
+
export async function doctor(options = {}) {
|
|
153
|
+
const configPath = options.config;
|
|
146
154
|
consola.start("Running OpenWorkflow doctor...");
|
|
147
|
-
const { config, configFile } = await loadConfigWithEnv();
|
|
155
|
+
const { config, configFile } = await loadConfigWithEnv(configPath);
|
|
148
156
|
if (!configFile) {
|
|
149
|
-
throw new CLIError("No config file found.", "Run `
|
|
157
|
+
throw new CLIError("No config file found.", "Run `npx @openworkflow/cli init` to create a config file.");
|
|
150
158
|
}
|
|
151
159
|
const backend = config.backend;
|
|
152
160
|
try {
|
|
@@ -181,13 +189,14 @@ export async function doctor() {
|
|
|
181
189
|
}
|
|
182
190
|
/**
|
|
183
191
|
* openworkflow worker start
|
|
184
|
-
* @param
|
|
192
|
+
* @param options - Worker config and command options
|
|
185
193
|
*/
|
|
186
|
-
export async function workerStart(
|
|
194
|
+
export async function workerStart(options = {}) {
|
|
195
|
+
const { config: configPath, ...workerConfig } = options;
|
|
187
196
|
consola.start("Starting worker...");
|
|
188
|
-
const { config, configFile } = await loadConfigWithEnv();
|
|
197
|
+
const { config, configFile } = await loadConfigWithEnv(configPath);
|
|
189
198
|
if (!configFile) {
|
|
190
|
-
throw new CLIError("No config file found.", "Run `
|
|
199
|
+
throw new CLIError("No config file found.", "Run `npx @openworkflow/cli init` to create a config file.");
|
|
191
200
|
}
|
|
192
201
|
const backend = config.backend;
|
|
193
202
|
const ow = new OpenWorkflow({ backend });
|
|
@@ -216,7 +225,7 @@ export async function workerStart(cliOptions) {
|
|
|
216
225
|
consola.info(`Found ${String(files.length)} workflow file(s)`);
|
|
217
226
|
consola.success(`Loaded ${String(workflows.length)} workflow(s): ${workflows.map((w) => w.spec.name).join(", ")}`);
|
|
218
227
|
assertNoDuplicateWorkflows(workflows);
|
|
219
|
-
const workerOptions = mergeDefinedOptions(config.worker,
|
|
228
|
+
const workerOptions = mergeDefinedOptions(config.worker, workerConfig);
|
|
220
229
|
if (workerOptions.concurrency !== undefined) {
|
|
221
230
|
assertPositiveInteger("concurrency", workerOptions.concurrency);
|
|
222
231
|
}
|
|
@@ -238,12 +247,14 @@ export async function workerStart(cliOptions) {
|
|
|
238
247
|
/**
|
|
239
248
|
* openworkflow dashboard
|
|
240
249
|
* Starts the dashboard by delegating to `@openworkflow/dashboard` via npx.
|
|
250
|
+
* @param options - Command options
|
|
241
251
|
*/
|
|
242
|
-
export async function dashboard() {
|
|
252
|
+
export async function dashboard(options = {}) {
|
|
253
|
+
const configPath = options.config;
|
|
243
254
|
consola.start("Starting dashboard...");
|
|
244
|
-
const { configFile } = await loadConfigWithEnv();
|
|
255
|
+
const { configFile } = await loadConfigWithEnv(configPath);
|
|
245
256
|
if (!configFile) {
|
|
246
|
-
throw new CLIError("No config file found.", "Run `
|
|
257
|
+
throw new CLIError("No config file found.", "Run `npx @openworkflow/cli init` to create a config file before starting the dashboard.");
|
|
247
258
|
}
|
|
248
259
|
consola.info(`Using config: ${configFile}`);
|
|
249
260
|
// eslint-disable-next-line sonarjs/no-os-command-from-path
|
|
@@ -596,7 +607,9 @@ function getDevDependenciesToInstall() {
|
|
|
596
607
|
function createConfigFile(configFileName) {
|
|
597
608
|
const spinner = p.spinner();
|
|
598
609
|
spinner.start("Writing config...");
|
|
599
|
-
const configDestPath = path.
|
|
610
|
+
const configDestPath = path.resolve(process.cwd(), configFileName);
|
|
611
|
+
// mkdir if the user specified a config file, and they want it in a dir
|
|
612
|
+
mkdirSync(path.dirname(configDestPath), { recursive: true });
|
|
600
613
|
writeFileSync(configDestPath, CONFIG, "utf8");
|
|
601
614
|
spinner.stop(`Config written to ${configDestPath}`);
|
|
602
615
|
}
|
|
@@ -747,12 +760,15 @@ function updateEnvForPostgres() {
|
|
|
747
760
|
}
|
|
748
761
|
/**
|
|
749
762
|
* Load CLI config after loading .env, and wrap errors for user-facing output.
|
|
763
|
+
* @param configPath - Optional explicit config file path
|
|
750
764
|
* @returns Loaded config and metadata.
|
|
751
765
|
*/
|
|
752
|
-
async function loadConfigWithEnv() {
|
|
766
|
+
async function loadConfigWithEnv(configPath) {
|
|
753
767
|
loadDotenv({ quiet: true });
|
|
754
768
|
try {
|
|
755
|
-
return
|
|
769
|
+
return configPath
|
|
770
|
+
? await loadConfigFromPath(configPath)
|
|
771
|
+
: await loadConfig();
|
|
756
772
|
}
|
|
757
773
|
catch (error) {
|
|
758
774
|
const message = error instanceof Error ? error.message : String(error);
|
package/dist/config.d.ts
CHANGED
|
@@ -30,6 +30,13 @@ interface LoadedConfig {
|
|
|
30
30
|
config: OpenWorkflowConfig;
|
|
31
31
|
configFile: string | undefined;
|
|
32
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Load OpenWorkflow config from an explicit path.
|
|
35
|
+
* @param configPath - Explicit config file path
|
|
36
|
+
* @param startDir - Optional base directory for resolving relative paths
|
|
37
|
+
* @returns The loaded configuration and metadata
|
|
38
|
+
*/
|
|
39
|
+
export declare function loadConfigFromPath(configPath: string, startDir?: string): Promise<LoadedConfig>;
|
|
33
40
|
/**
|
|
34
41
|
* Load the OpenWorkflow config at openworkflow.config.{ts,mts,cts,js,mjs,cjs}.
|
|
35
42
|
* Searches up the directory tree from the starting directory to find the
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../config.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAErD,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACzB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,MAAM,MAAM,YAAY,GAAG,IAAI,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;AAE9D;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,kBAAkB,GAAG,kBAAkB,CAE3E;AAED,UAAU,YAAY;IACpB,MAAM,EAAE,kBAAkB,CAAC;IAC3B,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC;AAMD;;;;;;;GAOG;AACH,wBAAsB,UAAU,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../config.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAErD,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACzB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,MAAM,MAAM,YAAY,GAAG,IAAI,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;AAE9D;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,kBAAkB,GAAG,kBAAkB,CAE3E;AAED,UAAU,YAAY;IACpB,MAAM,EAAE,kBAAkB,CAAC;IAC3B,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC;AAMD;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,MAAM,EAClB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,YAAY,CAAC,CAKvB;AAED;;;;;;;GAOG;AACH,wBAAsB,UAAU,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAyBzE"}
|
package/dist/config.js
CHANGED
|
@@ -13,6 +13,18 @@ export function defineConfig(config) {
|
|
|
13
13
|
const CONFIG_NAME = "openworkflow.config";
|
|
14
14
|
const CONFIG_EXTENSIONS = ["ts", "mts", "cts", "js", "mjs", "cjs"];
|
|
15
15
|
const jiti = createJiti(import.meta.url);
|
|
16
|
+
/**
|
|
17
|
+
* Load OpenWorkflow config from an explicit path.
|
|
18
|
+
* @param configPath - Explicit config file path
|
|
19
|
+
* @param startDir - Optional base directory for resolving relative paths
|
|
20
|
+
* @returns The loaded configuration and metadata
|
|
21
|
+
*/
|
|
22
|
+
export async function loadConfigFromPath(configPath, startDir) {
|
|
23
|
+
const filePath = path.resolve(startDir ?? process.cwd(), configPath);
|
|
24
|
+
return existsSync(filePath)
|
|
25
|
+
? importConfigFile(filePath)
|
|
26
|
+
: getEmptyLoadedConfig();
|
|
27
|
+
}
|
|
16
28
|
/**
|
|
17
29
|
* Load the OpenWorkflow config at openworkflow.config.{ts,mts,cts,js,mjs,cjs}.
|
|
18
30
|
* Searches up the directory tree from the starting directory to find the
|
|
@@ -30,19 +42,7 @@ export async function loadConfig(startDir) {
|
|
|
30
42
|
const fileName = `${CONFIG_NAME}.${ext}`;
|
|
31
43
|
const filePath = path.join(currentDir, fileName);
|
|
32
44
|
if (existsSync(filePath)) {
|
|
33
|
-
|
|
34
|
-
const fileUrl = pathToFileURL(filePath).href;
|
|
35
|
-
const config = await jiti.import(fileUrl, {
|
|
36
|
-
default: true,
|
|
37
|
-
});
|
|
38
|
-
return {
|
|
39
|
-
config,
|
|
40
|
-
configFile: filePath,
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
catch (error) {
|
|
44
|
-
throw new Error(`Failed to load config file ${filePath}: ${String(error)}`);
|
|
45
|
-
}
|
|
45
|
+
return await importConfigFile(filePath);
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
const parentDir = path.dirname(currentDir);
|
|
@@ -52,6 +52,33 @@ export async function loadConfig(startDir) {
|
|
|
52
52
|
}
|
|
53
53
|
currentDir = parentDir;
|
|
54
54
|
}
|
|
55
|
+
return getEmptyLoadedConfig();
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Import a config file and wrap load errors with a stable message.
|
|
59
|
+
* @param filePath - Absolute config file path.
|
|
60
|
+
* @returns Loaded config metadata.
|
|
61
|
+
*/
|
|
62
|
+
async function importConfigFile(filePath) {
|
|
63
|
+
try {
|
|
64
|
+
const fileUrl = pathToFileURL(filePath).href;
|
|
65
|
+
const config = await jiti.import(fileUrl, {
|
|
66
|
+
default: true,
|
|
67
|
+
});
|
|
68
|
+
return {
|
|
69
|
+
config,
|
|
70
|
+
configFile: filePath,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
throw new Error(`Failed to load config file ${filePath}: ${String(error)}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Return an empty config result when no config file is found.
|
|
79
|
+
* @returns Empty config metadata.
|
|
80
|
+
*/
|
|
81
|
+
function getEmptyLoadedConfig() {
|
|
55
82
|
return {
|
|
56
83
|
// not great, but meant to match the c12 api since that is what was used in
|
|
57
84
|
// the initial implementation of loadConfig
|
package/dist/skills.d.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
declare const SUPPORTED_SKILL_AGENTS: readonly ["codex", "claude"];
|
|
2
|
+
export type SkillAgent = (typeof SUPPORTED_SKILL_AGENTS)[number];
|
|
3
|
+
export interface SkillCatalogEntry {
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
}
|
|
7
|
+
export interface InstallOpenWorkflowSkillsOptions {
|
|
8
|
+
agent: SkillAgent;
|
|
9
|
+
force: boolean;
|
|
10
|
+
requestedSkillNames?: string[];
|
|
11
|
+
targetDirectory?: string;
|
|
12
|
+
workspaceDirectory: string;
|
|
13
|
+
}
|
|
14
|
+
export interface InstalledSkill {
|
|
15
|
+
name: string;
|
|
16
|
+
overwritten: boolean;
|
|
17
|
+
path: string;
|
|
18
|
+
}
|
|
19
|
+
export interface InstallOpenWorkflowSkillsResult {
|
|
20
|
+
installedSkills: InstalledSkill[];
|
|
21
|
+
targetDirectory: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* True when the provided value is a supported skill agent.
|
|
25
|
+
* @param value - Agent value from CLI options
|
|
26
|
+
* @returns Whether the value is supported
|
|
27
|
+
*/
|
|
28
|
+
export declare function isSkillAgent(value: string): value is SkillAgent;
|
|
29
|
+
/**
|
|
30
|
+
* Returns the default skill directory for an agent.
|
|
31
|
+
* @param agent - Skill agent
|
|
32
|
+
* @returns Relative target directory
|
|
33
|
+
*/
|
|
34
|
+
export declare function getDefaultSkillDirectory(agent: SkillAgent): string;
|
|
35
|
+
/**
|
|
36
|
+
* List available OpenWorkflow skills.
|
|
37
|
+
* @returns Skill catalog entries
|
|
38
|
+
*/
|
|
39
|
+
export declare function listOpenWorkflowSkills(): SkillCatalogEntry[];
|
|
40
|
+
/**
|
|
41
|
+
* Resolve requested skill names, defaulting to all.
|
|
42
|
+
* @param requestedSkillNames - Skill names from CLI options
|
|
43
|
+
* @returns Unique, validated skill names
|
|
44
|
+
* @throws {Error} When one or more skill names are unknown
|
|
45
|
+
*/
|
|
46
|
+
export declare function resolveSkillNames(requestedSkillNames?: string[]): string[];
|
|
47
|
+
/**
|
|
48
|
+
* Install OpenWorkflow skills to an agent skill directory.
|
|
49
|
+
* @param options - Install options
|
|
50
|
+
* @returns Install result metadata
|
|
51
|
+
* @throws {Error} When skill names are invalid or files already exist without force
|
|
52
|
+
*/
|
|
53
|
+
export declare function installOpenWorkflowSkills(options: InstallOpenWorkflowSkillsOptions): InstallOpenWorkflowSkillsResult;
|
|
54
|
+
/**
|
|
55
|
+
* Read a skill file from disk. Used by tests.
|
|
56
|
+
* @param skillPath - Path to the skill file
|
|
57
|
+
* @returns File content
|
|
58
|
+
*/
|
|
59
|
+
export declare function readSkillFile(skillPath: string): string;
|
|
60
|
+
export {};
|
|
61
|
+
//# sourceMappingURL=skills.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../skills.ts"],"names":[],"mappings":"AASA,QAAA,MAAM,sBAAsB,8BAA+B,CAAC;AAE5D,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,sBAAsB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEjE,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;CACrB;AAMD,MAAM,WAAW,gCAAgC;IAC/C,KAAK,EAAE,UAAU,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;IACf,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC/B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,OAAO,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,+BAA+B;IAC9C,eAAe,EAAE,cAAc,EAAE,CAAC;IAClC,eAAe,EAAE,MAAM,CAAC;CACzB;AAyMD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,UAAU,CAE/D;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAElE;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,iBAAiB,EAAE,CAK5D;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,mBAAmB,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAiB1E;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,gCAAgC,GACxC,+BAA+B,CA2CjC;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEvD"}
|
package/dist/skills.js
ADDED
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync, } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
const SUPPORTED_SKILL_AGENTS = ["codex", "claude"];
|
|
4
|
+
const DEFAULT_SKILL_DIRECTORIES = {
|
|
5
|
+
codex: path.join(".agents", "skills"),
|
|
6
|
+
claude: path.join(".claude", "skills"),
|
|
7
|
+
};
|
|
8
|
+
const OPENWORKFLOW_SKILLS = [
|
|
9
|
+
{
|
|
10
|
+
name: "openworkflow-quickstart",
|
|
11
|
+
description: "Scaffold, implement, and run durable workflows with the OpenWorkflow CLI.",
|
|
12
|
+
files: {
|
|
13
|
+
"SKILL.md": `---
|
|
14
|
+
name: openworkflow-quickstart
|
|
15
|
+
description: Use when a user asks to create, run, or update OpenWorkflow workflows in an app codebase.
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
# OpenWorkflow Quickstart
|
|
19
|
+
|
|
20
|
+
Use this skill when the user wants to build or update a workflow quickly.
|
|
21
|
+
|
|
22
|
+
## Standard flow
|
|
23
|
+
|
|
24
|
+
1. Confirm the project has OpenWorkflow set up. If not, run \`npx @openworkflow/cli init\`.
|
|
25
|
+
2. Create or update workflow code in \`openworkflow/\` with \`defineWorkflow\`.
|
|
26
|
+
3. Keep side effects inside \`step.run(...)\` calls, and keep step names stable.
|
|
27
|
+
4. Start a worker with \`npx @openworkflow/cli worker start\`.
|
|
28
|
+
5. Trigger the workflow with \`ow.runWorkflow(workflow.spec, input)\`.
|
|
29
|
+
6. Validate setup with \`npx @openworkflow/cli doctor\`.
|
|
30
|
+
|
|
31
|
+
## Guardrails
|
|
32
|
+
|
|
33
|
+
- Keep workflow replay deterministic outside of \`step.run\`.
|
|
34
|
+
- Use explicit, descriptive, stable step names.
|
|
35
|
+
- Use \`step.sleep\` for durable waits instead of process timers.
|
|
36
|
+
- Return JSON-serializable values from steps and workflow output.
|
|
37
|
+
|
|
38
|
+
## Useful commands
|
|
39
|
+
|
|
40
|
+
\`\`\`bash
|
|
41
|
+
npx @openworkflow/cli init
|
|
42
|
+
npx @openworkflow/cli worker start
|
|
43
|
+
npx @openworkflow/cli doctor
|
|
44
|
+
npx @openworkflow/cli dashboard
|
|
45
|
+
\`\`\`
|
|
46
|
+
`,
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: "openworkflow-patterns",
|
|
51
|
+
description: "Apply durable workflow patterns for retries, idempotency, and versioning.",
|
|
52
|
+
files: {
|
|
53
|
+
"SKILL.md": `---
|
|
54
|
+
name: openworkflow-patterns
|
|
55
|
+
description: Use when a user asks for production workflow design patterns in OpenWorkflow.
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
# OpenWorkflow Patterns
|
|
59
|
+
|
|
60
|
+
Use this skill for architecture and implementation guidance on durable workflows.
|
|
61
|
+
|
|
62
|
+
## Patterns to apply
|
|
63
|
+
|
|
64
|
+
1. Prefer many small \`step.run\` calls over one large step.
|
|
65
|
+
2. Keep external side effects in steps with stable names to preserve memoization.
|
|
66
|
+
3. Use idempotency keys for enqueue endpoints that may be retried.
|
|
67
|
+
4. Use workflow versioning when behavior changes in non-backward-compatible ways.
|
|
68
|
+
5. Use \`step.sleep\` for delays and backoff scheduling.
|
|
69
|
+
|
|
70
|
+
## Failure handling
|
|
71
|
+
|
|
72
|
+
- Let transient failures throw from step functions so retries can reschedule runs.
|
|
73
|
+
- Use explicit deadlines for workflows that should fail fast.
|
|
74
|
+
- Add compensation steps for side effects that need rollback behavior.
|
|
75
|
+
|
|
76
|
+
## Review checklist
|
|
77
|
+
|
|
78
|
+
- Is workflow code deterministic between checkpoints?
|
|
79
|
+
- Are step names stable across deployments?
|
|
80
|
+
- Are retry expectations documented for each external dependency?
|
|
81
|
+
- Are deadline and workflow cancel paths tested?
|
|
82
|
+
`,
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
name: "openworkflow-sql-inspector",
|
|
87
|
+
description: "Inspect workflow and step state directly in SQLite or PostgreSQL with read-only SQL.",
|
|
88
|
+
files: {
|
|
89
|
+
"SKILL.md": `---
|
|
90
|
+
name: openworkflow-sql-inspector
|
|
91
|
+
description: Use when a user asks why workflows are stuck, slow, failed, or repeatedly retried.
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
# OpenWorkflow SQL Inspector
|
|
95
|
+
|
|
96
|
+
Use this skill to answer workflow runtime questions from database state.
|
|
97
|
+
|
|
98
|
+
## Safety rules
|
|
99
|
+
|
|
100
|
+
1. Use read-only SQL only.
|
|
101
|
+
2. Do not run INSERT, UPDATE, DELETE, ALTER, DROP, TRUNCATE, or CREATE.
|
|
102
|
+
3. Always add \`LIMIT\` to exploratory queries (default 100).
|
|
103
|
+
4. Redact sensitive values before sharing query output.
|
|
104
|
+
|
|
105
|
+
## Database notes
|
|
106
|
+
|
|
107
|
+
- SQLite default path from CLI init: \`openworkflow/backend.db\`.
|
|
108
|
+
- PostgreSQL default schema: \`openworkflow\`.
|
|
109
|
+
- Core tables: \`workflow_runs\`, \`step_attempts\`.
|
|
110
|
+
|
|
111
|
+
Read [references/sql-queries.md](references/sql-queries.md) for practical query templates.
|
|
112
|
+
`,
|
|
113
|
+
"references/sql-queries.md": `# SQL Query Templates
|
|
114
|
+
|
|
115
|
+
## PostgreSQL
|
|
116
|
+
|
|
117
|
+
Use a read-only transaction for ad hoc inspection:
|
|
118
|
+
|
|
119
|
+
\`\`\`sql
|
|
120
|
+
BEGIN READ ONLY;
|
|
121
|
+
SELECT
|
|
122
|
+
namespace_id,
|
|
123
|
+
id,
|
|
124
|
+
workflow_name,
|
|
125
|
+
status,
|
|
126
|
+
attempts,
|
|
127
|
+
available_at,
|
|
128
|
+
deadline_at,
|
|
129
|
+
created_at,
|
|
130
|
+
updated_at
|
|
131
|
+
FROM openworkflow.workflow_runs
|
|
132
|
+
ORDER BY created_at DESC
|
|
133
|
+
LIMIT 100;
|
|
134
|
+
COMMIT;
|
|
135
|
+
\`\`\`
|
|
136
|
+
|
|
137
|
+
Recent step attempts for a workflow run:
|
|
138
|
+
|
|
139
|
+
\`\`\`sql
|
|
140
|
+
SELECT
|
|
141
|
+
namespace_id,
|
|
142
|
+
id,
|
|
143
|
+
workflow_run_id,
|
|
144
|
+
step_name,
|
|
145
|
+
kind,
|
|
146
|
+
status,
|
|
147
|
+
started_at,
|
|
148
|
+
finished_at,
|
|
149
|
+
created_at
|
|
150
|
+
FROM openworkflow.step_attempts
|
|
151
|
+
WHERE workflow_run_id = $1
|
|
152
|
+
ORDER BY created_at DESC
|
|
153
|
+
LIMIT 200;
|
|
154
|
+
\`\`\`
|
|
155
|
+
|
|
156
|
+
## SQLite
|
|
157
|
+
|
|
158
|
+
List recent workflow runs:
|
|
159
|
+
|
|
160
|
+
\`\`\`sql
|
|
161
|
+
SELECT
|
|
162
|
+
namespace_id,
|
|
163
|
+
id,
|
|
164
|
+
workflow_name,
|
|
165
|
+
status,
|
|
166
|
+
attempts,
|
|
167
|
+
available_at,
|
|
168
|
+
deadline_at,
|
|
169
|
+
created_at,
|
|
170
|
+
updated_at
|
|
171
|
+
FROM workflow_runs
|
|
172
|
+
ORDER BY created_at DESC
|
|
173
|
+
LIMIT 100;
|
|
174
|
+
\`\`\`
|
|
175
|
+
|
|
176
|
+
List step attempts for one run:
|
|
177
|
+
|
|
178
|
+
\`\`\`sql
|
|
179
|
+
SELECT
|
|
180
|
+
namespace_id,
|
|
181
|
+
id,
|
|
182
|
+
workflow_run_id,
|
|
183
|
+
step_name,
|
|
184
|
+
kind,
|
|
185
|
+
status,
|
|
186
|
+
started_at,
|
|
187
|
+
finished_at,
|
|
188
|
+
created_at
|
|
189
|
+
FROM step_attempts
|
|
190
|
+
WHERE workflow_run_id = ?
|
|
191
|
+
ORDER BY created_at DESC
|
|
192
|
+
LIMIT 200;
|
|
193
|
+
\`\`\`
|
|
194
|
+
`,
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
];
|
|
198
|
+
/**
|
|
199
|
+
* True when the provided value is a supported skill agent.
|
|
200
|
+
* @param value - Agent value from CLI options
|
|
201
|
+
* @returns Whether the value is supported
|
|
202
|
+
*/
|
|
203
|
+
export function isSkillAgent(value) {
|
|
204
|
+
return SUPPORTED_SKILL_AGENTS.includes(value);
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Returns the default skill directory for an agent.
|
|
208
|
+
* @param agent - Skill agent
|
|
209
|
+
* @returns Relative target directory
|
|
210
|
+
*/
|
|
211
|
+
export function getDefaultSkillDirectory(agent) {
|
|
212
|
+
return DEFAULT_SKILL_DIRECTORIES[agent];
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* List available OpenWorkflow skills.
|
|
216
|
+
* @returns Skill catalog entries
|
|
217
|
+
*/
|
|
218
|
+
export function listOpenWorkflowSkills() {
|
|
219
|
+
return OPENWORKFLOW_SKILLS.map((skill) => ({
|
|
220
|
+
name: skill.name,
|
|
221
|
+
description: skill.description,
|
|
222
|
+
}));
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Resolve requested skill names, defaulting to all.
|
|
226
|
+
* @param requestedSkillNames - Skill names from CLI options
|
|
227
|
+
* @returns Unique, validated skill names
|
|
228
|
+
* @throws {Error} When one or more skill names are unknown
|
|
229
|
+
*/
|
|
230
|
+
export function resolveSkillNames(requestedSkillNames) {
|
|
231
|
+
const requested = requestedSkillNames && requestedSkillNames.length > 0
|
|
232
|
+
? requestedSkillNames
|
|
233
|
+
: OPENWORKFLOW_SKILLS.map((skill) => skill.name);
|
|
234
|
+
const deduped = [...new Set(requested)];
|
|
235
|
+
const unknownSkills = deduped.filter((name) => OPENWORKFLOW_SKILLS.every((skill) => skill.name !== name));
|
|
236
|
+
if (unknownSkills.length > 0) {
|
|
237
|
+
throw new Error(`Unknown skill name${unknownSkills.length === 1 ? "" : "s"}: ${unknownSkills.join(", ")}`);
|
|
238
|
+
}
|
|
239
|
+
return deduped;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Install OpenWorkflow skills to an agent skill directory.
|
|
243
|
+
* @param options - Install options
|
|
244
|
+
* @returns Install result metadata
|
|
245
|
+
* @throws {Error} When skill names are invalid or files already exist without force
|
|
246
|
+
*/
|
|
247
|
+
export function installOpenWorkflowSkills(options) {
|
|
248
|
+
const requestedSkillNames = resolveSkillNames(options.requestedSkillNames);
|
|
249
|
+
const defaultDirectory = getDefaultSkillDirectory(options.agent);
|
|
250
|
+
const targetDirectory = path.resolve(options.workspaceDirectory, options.targetDirectory ?? defaultDirectory);
|
|
251
|
+
mkdirSync(targetDirectory, { recursive: true });
|
|
252
|
+
const installedSkills = [];
|
|
253
|
+
for (const skillName of requestedSkillNames) {
|
|
254
|
+
const skill = OPENWORKFLOW_SKILLS.find((item) => item.name === skillName);
|
|
255
|
+
if (!skill) {
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
const skillDirectory = path.join(targetDirectory, skill.name);
|
|
259
|
+
const hadExistingDirectory = existsSync(skillDirectory);
|
|
260
|
+
if (hadExistingDirectory && !options.force) {
|
|
261
|
+
throw new Error(`Skill "${skill.name}" already exists at ${skillDirectory}. Use --force to overwrite.`);
|
|
262
|
+
}
|
|
263
|
+
if (hadExistingDirectory) {
|
|
264
|
+
rmSync(skillDirectory, { force: true, recursive: true });
|
|
265
|
+
}
|
|
266
|
+
for (const [relativePath, content] of Object.entries(skill.files)) {
|
|
267
|
+
const destinationPath = path.join(skillDirectory, relativePath);
|
|
268
|
+
mkdirSync(path.dirname(destinationPath), { recursive: true });
|
|
269
|
+
writeFileSync(destinationPath, content, "utf8");
|
|
270
|
+
}
|
|
271
|
+
installedSkills.push({
|
|
272
|
+
name: skill.name,
|
|
273
|
+
overwritten: hadExistingDirectory,
|
|
274
|
+
path: skillDirectory,
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
return { installedSkills, targetDirectory };
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Read a skill file from disk. Used by tests.
|
|
281
|
+
* @param skillPath - Path to the skill file
|
|
282
|
+
* @returns File content
|
|
283
|
+
*/
|
|
284
|
+
export function readSkillFile(skillPath) {
|
|
285
|
+
return readFileSync(skillPath, "utf8");
|
|
286
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openworkflow/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -13,8 +13,7 @@
|
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
15
|
"bin": {
|
|
16
|
-
"openworkflow": "./dist/cli.js"
|
|
17
|
-
"ow": "./dist/cli.js"
|
|
16
|
+
"openworkflow": "./dist/cli.js"
|
|
18
17
|
},
|
|
19
18
|
"files": [
|
|
20
19
|
"dist",
|
|
@@ -30,15 +29,15 @@
|
|
|
30
29
|
"@clack/prompts": "^1.0.0",
|
|
31
30
|
"commander": "^14.0.3",
|
|
32
31
|
"consola": "^3.4.2",
|
|
33
|
-
"dotenv": "^17.2.
|
|
32
|
+
"dotenv": "^17.2.4",
|
|
34
33
|
"jiti": "^2.6.1",
|
|
35
|
-
"nypm": "^0.6.
|
|
34
|
+
"nypm": "^0.6.5"
|
|
36
35
|
},
|
|
37
36
|
"devDependencies": {
|
|
38
37
|
"openworkflow": "*",
|
|
39
38
|
"vitest": "^4.0.18"
|
|
40
39
|
},
|
|
41
40
|
"peerDependencies": {
|
|
42
|
-
"openworkflow": "^0.6.0"
|
|
41
|
+
"openworkflow": "^0.6.0 || ^0.7.0"
|
|
43
42
|
}
|
|
44
43
|
}
|