@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.
@@ -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,CAsJnE;AA8BD;;;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;AA+WD,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"}
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 { CONFIG, HELLO_WORLD_RUNNER, HELLO_WORLD_WORKFLOW, POSTGRES_CLIENT, POSTGRES_PROD_SQLITE_DEV_CLIENT, SQLITE_CLIENT, } from "./templates.js";
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
- validateInitManifest(packageJson);
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$ 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");
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 && worker !== "npx @openworkflow/cli worker start") {
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, CONFIG, "utf8");
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"] = "npx @openworkflow/cli worker start";
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 to a file if no existing line matches. Creates the file if it
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 returns true when an existing line
809
- * should be treated as already representing `line`
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 (content.split("\n").some((existing) => matchesExisting(existing))) {
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}`, (line) => {
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.
@@ -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
- export declare const CONFIG = "import { backend } from \"./openworkflow/client.js\";\nimport { defineConfig } from \"@openworkflow/cli\";\n\nexport default defineConfig({\n backend,\n dirs: \"./openworkflow\",\n ignorePatterns: [\"**/*.run.*\"],\n});\n";
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
@@ -1 +1 @@
1
- {"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../templates.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,aAAa,6OAKzB,CAAC;AAEF,eAAO,MAAM,eAAe,+QAO3B,CAAC;AAEF,eAAO,MAAM,+BAA+B,ibAS3C,CAAC;AAEF,eAAO,MAAM,MAAM,qOAQlB,CAAC;AAEF,eAAO,MAAM,oBAAoB,wvBA0BhC,CAAC;AAEF,eAAO,MAAM,kBAAkB,w8BA0B9B,CAAC"}
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
- export const CONFIG = `import { backend } from "./openworkflow/client.js";
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: "./openworkflow",
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
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openworkflow/cli",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {