@openworkflow/cli 0.5.1 → 0.5.2
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/commands.d.ts.map +1 -1
- package/dist/commands.js +30 -20
- package/dist/templates.d.ts +6 -1
- package/dist/templates.d.ts.map +1 -1
- package/dist/templates.js +10 -2
- package/package.json +1 -1
package/dist/commands.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../commands.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EAIb,MAAM,aAAa,CAAC;AAyCrB,KAAK,aAAa,GAAG,QAAQ,GAAG,UAAU,GAAG,MAAM,CAAC;AAEpD,UAAU,cAAc;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,WAAY,SAAQ,cAAc;IAC1C,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,UAAU,gBAAiB,SAAQ,cAAc;IAC/C,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAoBnC;AAED;;;;GAIG;AAEH,wBAAsB,IAAI,CAAC,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../commands.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EAIb,MAAM,aAAa,CAAC;AAyCrB,KAAK,aAAa,GAAG,QAAQ,GAAG,UAAU,GAAG,MAAM,CAAC;AAEpD,UAAU,cAAc;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,WAAY,SAAQ,cAAc;IAC1C,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,UAAU,gBAAiB,SAAQ,cAAc;IAC/C,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAoBnC;AAED;;;;GAIG;AAEH,wBAAsB,IAAI,CAAC,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CA0JnE;AAkCD;;;GAGG;AACH,wBAAsB,MAAM,CAAC,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CA2DxE;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,CA8Ef;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG;IACvD,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,YAAY,EAAE;QACZ,KAAK,EAAE,SAAS,CAAC;QACjB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;KACzB,CAAC;CACH,CAYA;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAavE;AAED;;;;GAIG;AACH,wBAAsB,SAAS,CAAC,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CA6D7E;AA4PD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,EAAE,MAAM,EACf,cAAc,GAAE,MAAM,EAAO,GAC5B,MAAM,EAAE,CA8CV;AAuXD,UAAU,oBAAoB;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC1C;AAoCD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,QAAQ,CAAC,oBAAoB,CAAC,GAAG,IAAI,GACjD,MAAM,CAER;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,WAAW,EAAE,QAAQ,CAAC,oBAAoB,CAAC,GAAG,IAAI,GACjD,MAAM,CAER;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,WAAW,EAAE,QAAQ,CAAC,oBAAoB,CAAC,GAAG,IAAI,GACjD,MAAM,CAER;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,QAAQ,CAAC,oBAAoB,CAAC,GAAG,IAAI,GACjD,MAAM,CAER"}
|
package/dist/commands.js
CHANGED
|
@@ -2,10 +2,10 @@ import { findConfigFile, loadConfigFromPath, } from "./config.js";
|
|
|
2
2
|
import { CLIError, exit } from "./errors.js";
|
|
3
3
|
import { createModuleLoader } from "./module-loader.js";
|
|
4
4
|
import { trackCommand } from "./telemetry.js";
|
|
5
|
-
import {
|
|
5
|
+
import { getConfigTemplate, HELLO_WORLD_RUNNER, HELLO_WORLD_WORKFLOW, POSTGRES_CLIENT, POSTGRES_PROD_SQLITE_DEV_CLIENT, SQLITE_CLIENT, } from "./templates.js";
|
|
6
6
|
import * as p from "@clack/prompts";
|
|
7
7
|
import { consola } from "consola";
|
|
8
|
-
import { config as loadDotenv } from "dotenv";
|
|
8
|
+
import { config as loadDotenv, parse as parseDotenv } from "dotenv";
|
|
9
9
|
import { spawn } from "node:child_process";
|
|
10
10
|
import { existsSync, mkdirSync, readdirSync, readFileSync, unlinkSync, writeFileSync, } from "node:fs";
|
|
11
11
|
import path from "node:path";
|
|
@@ -101,7 +101,11 @@ export async function init(options = {}) {
|
|
|
101
101
|
if (!packageJson) {
|
|
102
102
|
throw new CLIError("No package.json found.", "Please create a package.json file first by running `npm init` or `npm init -y`.");
|
|
103
103
|
}
|
|
104
|
-
|
|
104
|
+
const configArg = options.config
|
|
105
|
+
? ` --config '${options.config.replaceAll("'", String.raw `'\''`)}'`
|
|
106
|
+
: "";
|
|
107
|
+
const workerCommand = `npx @openworkflow/cli worker start${configArg}`;
|
|
108
|
+
validateInitManifest(packageJson, workerCommand);
|
|
105
109
|
const configFileName = options.config ?? getConfigFileName(packageJson);
|
|
106
110
|
const clientFileName = getClientFileName(packageJson);
|
|
107
111
|
const exampleWorkflowFileName = getExampleWorkflowFileName(packageJson);
|
|
@@ -154,16 +158,16 @@ export async function init(options = {}) {
|
|
|
154
158
|
if (backendChoice === "postgres" || backendChoice === "both") {
|
|
155
159
|
updateEnvForPostgres();
|
|
156
160
|
}
|
|
157
|
-
addWorkerScriptToPackageJson();
|
|
161
|
+
addWorkerScriptToPackageJson(workerCommand);
|
|
158
162
|
// write config file last, so canceling earlier doesn't leave a config file
|
|
159
163
|
// which would prevent re-running init
|
|
160
164
|
createConfigFile(configFileName);
|
|
161
165
|
// wrap up
|
|
162
|
-
p.note(`➡️ Start a worker:\n$
|
|
166
|
+
p.note(`➡️ Start a worker:\n$ ${workerCommand}\n\n➡️ Run the example workflow:\n$ ${runCommand}\n\n➡️ View the dashboard:\n$ npx @openworkflow/cli dashboard${configArg}`, "Next steps");
|
|
163
167
|
p.outro("✅ Setup complete!");
|
|
164
168
|
}
|
|
165
169
|
// Validate the manifest fields that init reads or updates.
|
|
166
|
-
function validateInitManifest(manifest) {
|
|
170
|
+
function validateInitManifest(manifest, workerCommand) {
|
|
167
171
|
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) {
|
|
168
172
|
throw new CLIError("Invalid package.json: expected an object.");
|
|
169
173
|
}
|
|
@@ -180,7 +184,9 @@ function validateInitManifest(manifest) {
|
|
|
180
184
|
}
|
|
181
185
|
const { scripts } = manifest;
|
|
182
186
|
const worker = scripts?.["worker"];
|
|
183
|
-
if (worker !== undefined &&
|
|
187
|
+
if (worker !== undefined &&
|
|
188
|
+
worker !== "npx @openworkflow/cli worker start" &&
|
|
189
|
+
worker !== workerCommand) {
|
|
184
190
|
throw new CLIError("Setup would overwrite package.json scripts.worker.");
|
|
185
191
|
}
|
|
186
192
|
}
|
|
@@ -710,9 +716,15 @@ function createConfigFile(configFileName) {
|
|
|
710
716
|
const spinner = p.spinner();
|
|
711
717
|
spinner.start("Writing config...");
|
|
712
718
|
const configDestPath = path.resolve(process.cwd(), configFileName);
|
|
719
|
+
const relativeClientPath = path
|
|
720
|
+
.relative(path.dirname(configDestPath), path.join(process.cwd(), "openworkflow/client.js"))
|
|
721
|
+
.replaceAll(path.sep, "/");
|
|
722
|
+
const clientImport = relativeClientPath.startsWith("../")
|
|
723
|
+
? relativeClientPath
|
|
724
|
+
: `./${relativeClientPath}`;
|
|
713
725
|
// mkdir if the user specified a config file, and they want it in a dir
|
|
714
726
|
mkdirSync(path.dirname(configDestPath), { recursive: true });
|
|
715
|
-
writeFileSync(configDestPath,
|
|
727
|
+
writeFileSync(configDestPath, getConfigTemplate(clientImport), "utf8");
|
|
716
728
|
spinner.stop(`Config written to ${configDestPath}`);
|
|
717
729
|
}
|
|
718
730
|
/**
|
|
@@ -780,8 +792,9 @@ function updateGitignoreForSqlite() {
|
|
|
780
792
|
}
|
|
781
793
|
/**
|
|
782
794
|
* Add worker script to package.json.
|
|
795
|
+
* @param workerCommand - Worker command including any custom config path.
|
|
783
796
|
*/
|
|
784
|
-
function addWorkerScriptToPackageJson() {
|
|
797
|
+
function addWorkerScriptToPackageJson(workerCommand) {
|
|
785
798
|
const packageJsonPath = path.join(process.cwd(), "package.json");
|
|
786
799
|
if (!existsSync(packageJsonPath)) {
|
|
787
800
|
return;
|
|
@@ -791,7 +804,7 @@ function addWorkerScriptToPackageJson() {
|
|
|
791
804
|
try {
|
|
792
805
|
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
793
806
|
packageJson.scripts ??= {};
|
|
794
|
-
packageJson.scripts["worker"] =
|
|
807
|
+
packageJson.scripts["worker"] = workerCommand;
|
|
795
808
|
writeFileSync(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`, "utf8");
|
|
796
809
|
spinner.stop('Added "worker" script to package.json');
|
|
797
810
|
}
|
|
@@ -801,17 +814,17 @@ function addWorkerScriptToPackageJson() {
|
|
|
801
814
|
}
|
|
802
815
|
}
|
|
803
816
|
/**
|
|
804
|
-
* Append a line
|
|
805
|
-
* doesn't exist.
|
|
817
|
+
* Append a line if the file does not already contain the desired entry.
|
|
818
|
+
* Creates the file if it doesn't exist.
|
|
806
819
|
* @param filePath - Path to the file
|
|
807
820
|
* @param line - Line to append (without a trailing newline)
|
|
808
|
-
* @param matchesExisting - Predicate that
|
|
809
|
-
*
|
|
821
|
+
* @param matchesExisting - Predicate that checks whether the file contents
|
|
822
|
+
* already contain the desired entry
|
|
810
823
|
* @returns Whether the line was appended
|
|
811
824
|
*/
|
|
812
825
|
function appendLineIfMissing(filePath, line, matchesExisting) {
|
|
813
826
|
const content = existsSync(filePath) ? readFileSync(filePath, "utf8") : "";
|
|
814
|
-
if (
|
|
827
|
+
if (matchesExisting(content)) {
|
|
815
828
|
return false;
|
|
816
829
|
}
|
|
817
830
|
const separator = content === "" || content.endsWith("\n") ? "" : "\n";
|
|
@@ -826,7 +839,7 @@ function appendLineIfMissing(filePath, line, matchesExisting) {
|
|
|
826
839
|
* @returns Whether the entry was appended
|
|
827
840
|
*/
|
|
828
841
|
function ensureGitignoreEntry(gitignorePath, entry) {
|
|
829
|
-
return appendLineIfMissing(gitignorePath, entry, (line) => line.trim() === entry);
|
|
842
|
+
return appendLineIfMissing(gitignorePath, entry, (content) => content.split("\n").some((line) => line.trim() === entry));
|
|
830
843
|
}
|
|
831
844
|
/**
|
|
832
845
|
* Add OPENWORKFLOW_POSTGRES_URL to .env file.
|
|
@@ -951,10 +964,7 @@ function hasDependency(packageJson, name) {
|
|
|
951
964
|
* @returns Whether the entry was appended
|
|
952
965
|
*/
|
|
953
966
|
function ensureEnvEntry(envPath, key, value) {
|
|
954
|
-
return appendLineIfMissing(envPath, `${key}=${value}`, (
|
|
955
|
-
const trimmed = line.trim();
|
|
956
|
-
return trimmed.startsWith(`${key}=`) || trimmed.startsWith(`${key} =`);
|
|
957
|
-
});
|
|
967
|
+
return appendLineIfMissing(envPath, `${key}=${value}`, (content) => Object.hasOwn(parseDotenv(content), key));
|
|
958
968
|
}
|
|
959
969
|
/**
|
|
960
970
|
* Validate a numeric option is a positive integer.
|
package/dist/templates.d.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
export declare const SQLITE_CLIENT = "import { BackendSqlite } from \"openworkflow/sqlite\";\nimport { OpenWorkflow } from \"openworkflow\";\n\nexport const backend = BackendSqlite.connect(\"openworkflow/backend.db\");\nexport const ow = new OpenWorkflow({ backend });\n";
|
|
2
2
|
export declare const POSTGRES_CLIENT = "import { BackendPostgres } from \"openworkflow/postgres\";\nimport { OpenWorkflow } from \"openworkflow\";\n\nexport const backend = await BackendPostgres.connect(\n process.env[\"OPENWORKFLOW_POSTGRES_URL\"],\n);\nexport const ow = new OpenWorkflow({ backend });\n";
|
|
3
3
|
export declare const POSTGRES_PROD_SQLITE_DEV_CLIENT = "import { BackendPostgres } from \"openworkflow/postgres\";\nimport { BackendSqlite } from \"openworkflow/sqlite\";\nimport { OpenWorkflow } from \"openworkflow\";\n\nexport const backend =\n process.env[\"NODE_ENV\"] === \"production\"\n ? await BackendPostgres.connect(process.env[\"OPENWORKFLOW_POSTGRES_URL\"])\n : BackendSqlite.connect(\"openworkflow/backend.db\");\nexport const ow = new OpenWorkflow({ backend });\n";
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Render config paths relative to the config file's directory.
|
|
6
|
+
* @param clientImport - Relative import path to the scaffolded client.
|
|
7
|
+
* @returns The config template string.
|
|
8
|
+
*/
|
|
9
|
+
export declare function getConfigTemplate(clientImport: string): string;
|
|
5
10
|
export declare const HELLO_WORLD_WORKFLOW = "import { defineWorkflow } from \"openworkflow\";\n\n/**\n * Example workflow that greets the world.\n *\n * This workflow is auto-discovered by the CLI worker.\n * To trigger it, use ow.runWorkflow() from your app:\n * ```ts\n * import { ow } from \"./openworkflow/client.js\";\n * import { helloWorld } from \"./openworkflow/hello-world.js\";\n * const handle = await ow.runWorkflow(helloWorld.spec, {});\n * const result = await handle.result();\n * ```\n */\nexport const helloWorld = defineWorkflow(\n { name: \"hello-world\" },\n async ({ step }) => {\n const greeting = await step.run({ name: \"greet\" }, () => {\n return \"Hello, World!\";\n });\n\n await step.sleep(\"wait-a-bit\", \"1s\");\n\n return { greeting };\n },\n);\n";
|
|
6
11
|
export declare const HELLO_WORLD_RUNNER = "import { backend, ow } from \"./client.js\";\nimport { helloWorld } from \"./hello-world.js\";\n\n// Run this file:\n// JS: `node openworkflow/hello-world.run.js`\n// TS: `npx tsx openworkflow/hello-world.run.ts`\n//\n// This file is a temporary dev helper generated by\n// `npx @openworkflow/cli init` so you can quickly run the example workflow.\n//\n// Copy the code anywhere in your app (e.g., an API route or a script) and\n// modify it to run your workflows.\n//\n// You can delete this file once you're done with it.\n\nconsole.log(\"Running hello-world workflow...\");\nconst handle = await ow.runWorkflow(helloWorld.spec, {});\n\nconsole.log(\"Waiting for result...\");\nconst result = await handle.result(); // for very short workflows only\n\nconsole.log(`Workflow result: ${JSON.stringify(result, null, 2)}`);\n\n// Stop backend. Only needed if this is a standalone script that you want to\n// exit after running the workflow.\nawait backend.stop();\n";
|
|
7
12
|
//# sourceMappingURL=templates.d.ts.map
|
package/dist/templates.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../templates.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../templates.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,aAAa,6OAKzB,CAAC;AAEF,eAAO,MAAM,eAAe,+QAO3B,CAAC;AAEF,eAAO,MAAM,+BAA+B,ibAS3C,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAU9D;AAED,eAAO,MAAM,oBAAoB,wvBA0BhC,CAAC;AAEF,eAAO,MAAM,kBAAkB,w8BA0B9B,CAAC"}
|
package/dist/templates.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import path from "node:path";
|
|
1
2
|
export const SQLITE_CLIENT = `import { BackendSqlite } from "openworkflow/sqlite";
|
|
2
3
|
import { OpenWorkflow } from "openworkflow";
|
|
3
4
|
|
|
@@ -22,15 +23,22 @@ export const backend =
|
|
|
22
23
|
: BackendSqlite.connect("openworkflow/backend.db");
|
|
23
24
|
export const ow = new OpenWorkflow({ backend });
|
|
24
25
|
`;
|
|
25
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Render config paths relative to the config file's directory.
|
|
28
|
+
* @param clientImport - Relative import path to the scaffolded client.
|
|
29
|
+
* @returns The config template string.
|
|
30
|
+
*/
|
|
31
|
+
export function getConfigTemplate(clientImport) {
|
|
32
|
+
return `import { backend } from ${JSON.stringify(clientImport)};
|
|
26
33
|
import { defineConfig } from "@openworkflow/cli";
|
|
27
34
|
|
|
28
35
|
export default defineConfig({
|
|
29
36
|
backend,
|
|
30
|
-
dirs:
|
|
37
|
+
dirs: ${JSON.stringify(path.posix.dirname(clientImport))},
|
|
31
38
|
ignorePatterns: ["**/*.run.*"],
|
|
32
39
|
});
|
|
33
40
|
`;
|
|
41
|
+
}
|
|
34
42
|
export const HELLO_WORLD_WORKFLOW = `import { defineWorkflow } from "openworkflow";
|
|
35
43
|
|
|
36
44
|
/**
|