windmill-cli 1.600.1 → 1.601.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.
Files changed (46) hide show
  1. package/esm/gen/core/OpenAPI.js +1 -1
  2. package/esm/src/commands/app/app_metadata.js +9 -3
  3. package/esm/src/commands/app/dev.js +5 -4
  4. package/esm/src/commands/app/lint.js +3 -2
  5. package/esm/src/commands/app/new.js +2 -1
  6. package/esm/src/commands/dev/dev.js +5 -3
  7. package/esm/src/commands/flow/flow_metadata.js +2 -3
  8. package/esm/src/commands/init/init.js +1 -0
  9. package/esm/src/commands/script/script.js +20 -7
  10. package/esm/src/commands/sync/sync.js +22 -24
  11. package/esm/src/commands/workspace/fork.js +1 -1
  12. package/esm/src/core/conf.js +4 -0
  13. package/esm/src/core/constants.js +5 -0
  14. package/esm/src/core/context.js +1 -1
  15. package/esm/src/main.js +3 -2
  16. package/esm/src/types.js +10 -9
  17. package/esm/src/utils/git.js +1 -1
  18. package/esm/src/utils/resource_folders.js +329 -0
  19. package/esm/src/utils/utils.js +2 -1
  20. package/esm/windmill-utils-internal/src/inline-scripts/extractor.js +3 -2
  21. package/esm/windmill-utils-internal/src/path-utils/path-assigner.js +11 -3
  22. package/package.json +1 -1
  23. package/types/src/commands/app/app_metadata.d.ts.map +1 -1
  24. package/types/src/commands/app/dev.d.ts.map +1 -1
  25. package/types/src/commands/app/lint.d.ts.map +1 -1
  26. package/types/src/commands/app/new.d.ts.map +1 -1
  27. package/types/src/commands/dev/dev.d.ts.map +1 -1
  28. package/types/src/commands/flow/flow_metadata.d.ts.map +1 -1
  29. package/types/src/commands/init/init.d.ts.map +1 -1
  30. package/types/src/commands/script/script.d.ts +10 -0
  31. package/types/src/commands/script/script.d.ts.map +1 -1
  32. package/types/src/commands/sync/sync.d.ts.map +1 -1
  33. package/types/src/core/conf.d.ts +2 -1
  34. package/types/src/core/conf.d.ts.map +1 -1
  35. package/types/src/core/constants.d.ts +6 -0
  36. package/types/src/core/constants.d.ts.map +1 -0
  37. package/types/src/main.d.ts +2 -2
  38. package/types/src/main.d.ts.map +1 -1
  39. package/types/src/types.d.ts.map +1 -1
  40. package/types/src/utils/resource_folders.d.ts +160 -0
  41. package/types/src/utils/resource_folders.d.ts.map +1 -0
  42. package/types/src/utils/utils.d.ts.map +1 -1
  43. package/types/windmill-utils-internal/src/inline-scripts/extractor.d.ts +9 -1
  44. package/types/windmill-utils-internal/src/inline-scripts/extractor.d.ts.map +1 -1
  45. package/types/windmill-utils-internal/src/path-utils/path-assigner.d.ts +9 -1
  46. package/types/windmill-utils-internal/src/path-utils/path-assigner.d.ts.map +1 -1
@@ -32,7 +32,7 @@ export const OpenAPI = {
32
32
  PASSWORD: undefined,
33
33
  TOKEN: getEnv("WM_TOKEN"),
34
34
  USERNAME: undefined,
35
- VERSION: '1.600.1',
35
+ VERSION: '1.601.0',
36
36
  WITH_CREDENTIALS: true,
37
37
  interceptors: {
38
38
  request: new Interceptors(),
@@ -13,6 +13,7 @@ import { newPathAssigner, newRawAppPathAssigner, } from "../../../windmill-utils
13
13
  import { mergeConfigWithConfigFile } from "../../core/conf.js";
14
14
  import { resolveWorkspace } from "../../core/context.js";
15
15
  import { requireLogin } from "../../core/auth.js";
16
+ import { getNonDottedPaths } from "../../utils/resource_folders.js";
16
17
  const TOP_HASH = "__app_hash";
17
18
  export const APP_BACKEND_FOLDER = "backend";
18
19
  /**
@@ -26,8 +27,13 @@ async function generateAppHash(rawReqs, folder, rawApp, defaultTs) {
26
27
  try {
27
28
  const elems = await FSFSElement(runnablesFolder, [], true);
28
29
  for await (const f of elems.getChildren()) {
29
- if (!rawApp && !f.path.includes(".inline_script.")) {
30
- continue;
30
+ // For normal apps, skip non-script files (metadata files like app.yaml)
31
+ // For raw apps, all files in backend/ are scripts
32
+ if (!rawApp) {
33
+ const isMetadataFile = f.path.endsWith("app.yaml") || f.path.endsWith("app.json");
34
+ if (isMetadataFile) {
35
+ continue;
36
+ }
31
37
  }
32
38
  if (exts.some((e) => f.path.endsWith(e))) {
33
39
  // Embed lock into hash
@@ -252,7 +258,7 @@ async function updateRawAppRunnables(workspace, runnables, remotePath, appFolder
252
258
  * but for the app.value structure instead of app.runnables
253
259
  */
254
260
  async function updateAppInlineScripts(workspace, appValue, remotePath, appFolder, rawDeps, defaultTs = "bun") {
255
- const pathAssigner = newPathAssigner(defaultTs);
261
+ const pathAssigner = newPathAssigner(defaultTs, { skipInlineScriptSuffix: getNonDottedPaths() });
256
262
  const processor = async (inlineScript, context) => {
257
263
  const language = inlineScript.language;
258
264
  const content = inlineScript.content;
@@ -17,6 +17,7 @@ import { replaceInlineScripts } from "./app.js";
17
17
  import { APP_BACKEND_FOLDER, inferRunnableSchemaFromFile, } from "./app_metadata.js";
18
18
  import { loadRunnablesFromBackend } from "./raw_apps.js";
19
19
  import { regenerateAgentDocs } from "./generate_agents.js";
20
+ import { hasFolderSuffix, getFolderSuffix } from "../../utils/resource_folders.js";
20
21
  const DEFAULT_PORT = 4000;
21
22
  const DEFAULT_HOST = "localhost";
22
23
  // HTML template with live reload and SQL migration modal
@@ -277,17 +278,17 @@ async function dev(opts) {
277
278
  // Validate that we're in a .raw_app folder
278
279
  const cwd = process.cwd();
279
280
  const currentDirName = path.basename(cwd);
280
- if (!currentDirName.endsWith(".raw_app")) {
281
- log.error(colors.red(`Error: The dev command must be run inside a .raw_app folder.\n` +
281
+ if (!hasFolderSuffix(currentDirName, "raw_app")) {
282
+ log.error(colors.red(`Error: The dev command must be run inside a ${getFolderSuffix("raw_app")} folder.\n` +
282
283
  `Current directory: ${currentDirName}\n` +
283
- `Please navigate to a folder ending with '.raw_app' before running this command.`));
284
+ `Please navigate to a folder ending with '${getFolderSuffix("raw_app")}' before running this command.`));
284
285
  dntShim.Deno.exit(1);
285
286
  }
286
287
  // Check for raw_app.yaml
287
288
  const rawAppPath = path.join(cwd, "raw_app.yaml");
288
289
  if (!fs.existsSync(rawAppPath)) {
289
290
  log.error(colors.red(`Error: raw_app.yaml not found in current directory.\n` +
290
- `The dev command must be run in a .raw_app folder containing a raw_app.yaml file.`));
291
+ `The dev command must be run in a ${getFolderSuffix("raw_app")} folder containing a raw_app.yaml file.`));
291
292
  dntShim.Deno.exit(1);
292
293
  }
293
294
  // Resolve workspace and authenticate
@@ -7,6 +7,7 @@ import { Command, colors, log, yamlParseFile } from "../../../deps.js";
7
7
  import { createBundle } from "./bundle.js";
8
8
  import { APP_BACKEND_FOLDER } from "./app_metadata.js";
9
9
  import { loadRunnablesFromBackend } from "./raw_apps.js";
10
+ import { hasFolderSuffix, getFolderSuffix } from "../../utils/resource_folders.js";
10
11
  /**
11
12
  * Validates the structure of raw_app.yaml
12
13
  */
@@ -81,8 +82,8 @@ async function lintRawApp(appDir, opts) {
81
82
  const warnings = [];
82
83
  // Check if we're in a .raw_app folder
83
84
  const currentDirName = path.basename(appDir);
84
- if (!currentDirName.endsWith(".raw_app")) {
85
- errors.push(`Not a raw app folder: '${currentDirName}' does not end with '.raw_app'`);
85
+ if (!hasFolderSuffix(currentDirName, "raw_app")) {
86
+ errors.push(`Not a raw app folder: '${currentDirName}' does not end with '${getFolderSuffix("raw_app")}'`);
86
87
  return { valid: false, errors, warnings };
87
88
  }
88
89
  // Check if raw_app.yaml exists
@@ -5,6 +5,7 @@ import { resolveWorkspace } from "../../core/context.js";
5
5
  import { requireLogin } from "../../core/auth.js";
6
6
  import * as wmill from "../../../gen/services.gen.js";
7
7
  import path from "node:path";
8
+ import { buildFolderPath } from "../../utils/resource_folders.js";
8
9
  // Framework templates - adapted from frontend/src/routes/(root)/(logged)/apps_raw/add/templates.ts
9
10
  const reactIndex = `
10
11
  import React from 'react'
@@ -393,7 +394,7 @@ CREATE SCHEMA IF NOT EXISTS ${schemaName};
393
394
  log.info(colors.gray("You can configure datatables in Workspace Settings > Windmill Data Tables"));
394
395
  }
395
396
  // Create the directory structure - preserve full path (e.g., f/foobar/x/y becomes f/foobar/x/y.raw_app)
396
- const folderName = `${appPath}.raw_app`;
397
+ const folderName = buildFolderPath(appPath, "raw_app");
397
398
  const appDir = path.join(dntShim.Deno.cwd(), folderName);
398
399
  // Check if directory already exists
399
400
  try {
@@ -9,6 +9,7 @@ import { exts, removeExtensionToPath } from "../script/script.js";
9
9
  import { inferContentTypeFromFilePath } from "../../utils/script_common.js";
10
10
  import { replaceInlineScripts } from "../../../windmill-utils-internal/src/inline-scripts/replacer.js";
11
11
  import { parseMetadataFile } from "../../utils/metadata.js";
12
+ import { getFolderSuffixWithSep, getMetadataFileName, extractFolderPath, } from "../../utils/resource_folders.js";
12
13
  const PORT = 3001;
13
14
  async function dev(opts) {
14
15
  const workspace = await resolveWorkspace(opts);
@@ -35,9 +36,10 @@ async function dev(opts) {
35
36
  }, 100);
36
37
  }
37
38
  }
38
- const DOT_FLOW_SEP = ".flow" + SEP;
39
+ const flowFolderSuffix = getFolderSuffixWithSep("flow");
40
+ const flowMetadataFile = getMetadataFileName("flow", "yaml");
39
41
  async function loadPaths(pathsToLoad) {
40
- const paths = pathsToLoad.filter((path) => exts.some((ext) => path.endsWith(ext) || path.endsWith(DOT_FLOW_SEP + "flow.yaml")));
42
+ const paths = pathsToLoad.filter((path) => exts.some((ext) => path.endsWith(ext) || path.endsWith(flowFolderSuffix + flowMetadataFile)));
41
43
  if (paths.length == 0) {
42
44
  return;
43
45
  }
@@ -46,7 +48,7 @@ async function dev(opts) {
46
48
  const typ = getTypeStrFromPath(cpath);
47
49
  log.info("Detected change in " + cpath + " (" + typ + ")");
48
50
  if (typ == "flow") {
49
- const localPath = cpath.split(DOT_FLOW_SEP)[0] + DOT_FLOW_SEP;
51
+ const localPath = extractFolderPath(cpath, "flow");
50
52
  const localFlow = (await yamlParseFile(localPath + "flow.yaml"));
51
53
  await replaceInlineScripts(localFlow.value.modules, async (path) => await dntShim.Deno.readTextFile(localPath + path), log, localPath, SEP, undefined);
52
54
  currentLastEdit = {
@@ -7,6 +7,7 @@ import { exts } from "../script/script.js";
7
7
  import { FSFSElement } from "../sync/sync.js";
8
8
  import { replaceInlineScripts } from "../../../windmill-utils-internal/src/inline-scripts/replacer.js";
9
9
  import { workspaceDependenciesLanguages } from "../../utils/script_common.js";
10
+ import { extractNameFromFolder } from "../../utils/resource_folders.js";
10
11
  const TOP_HASH = "__flow_hash";
11
12
  async function generateFlowHash(rawWorkspaceDependencies, folder, defaultTs) {
12
13
  const elems = await FSFSElement(path.join(dntShim.Deno.cwd(), folder), [], true);
@@ -23,9 +24,7 @@ export async function generateFlowLockInternal(folder, dryRun, workspace, opts,
23
24
  if (folder.endsWith(SEP)) {
24
25
  folder = folder.substring(0, folder.length - 1);
25
26
  }
26
- const remote_path = folder
27
- .replaceAll(SEP, "/")
28
- .substring(0, folder.length - ".flow".length);
27
+ const remote_path = extractNameFromFolder(folder.replaceAll(SEP, "/"), "flow");
29
28
  if (!justUpdateMetadataLock && !noStaleMessage) {
30
29
  log.info(`Generating lock for flow ${folder} at ${remote_path}`);
31
30
  }
@@ -34,6 +34,7 @@ async function initAction(opts) {
34
34
  else {
35
35
  initialConfig.gitBranches = {};
36
36
  }
37
+ initialConfig.nonDottedPaths = true;
37
38
  await dntShim.Deno.writeTextFile("wmill.yaml", yamlStringify(initialConfig));
38
39
  log.info(colors.green("wmill.yaml created with default settings"));
39
40
  // Create lock file
@@ -17,15 +17,27 @@ import { mergeConfigWithConfigFile, readConfigFile, } from "../../core/conf.js";
17
17
  import { listSyncCodebases } from "../../utils/codebase.js";
18
18
  import fs from "node:fs";
19
19
  import { execSync } from "node:child_process";
20
+ import { isRawAppBackendPath as isRawAppBackendPathInternal, isAppInlineScriptPath as isAppInlineScriptPathInternal, isFlowInlineScriptPath as isFlowInlineScriptPathInternal, isFlowPath, isAppPath, } from "../../utils/resource_folders.js";
20
21
  /**
21
22
  * Checks if a path is inside a raw app backend folder.
22
23
  * Matches patterns like: .../myApp.raw_app/backend/...
23
24
  */
24
25
  export function isRawAppBackendPath(filePath) {
25
- // Normalize path separators for consistent matching
26
- const normalizedPath = filePath.replaceAll(SEP, "/");
27
- // Check if path contains pattern: *.raw_app/backend/
28
- return /\.raw_app\/backend\//.test(normalizedPath);
26
+ return isRawAppBackendPathInternal(filePath);
27
+ }
28
+ /**
29
+ * Checks if a path is inside a normal app folder (inline script).
30
+ * Matches patterns like: .../myApp.app/... or .../myApp__app/...
31
+ */
32
+ export function isAppInlineScriptPath(filePath) {
33
+ return isAppInlineScriptPathInternal(filePath);
34
+ }
35
+ /**
36
+ * Checks if a path is inside a flow folder (inline script).
37
+ * Matches patterns like: .../myFlow.flow/... or .../myFlow__flow/...
38
+ */
39
+ export function isFlowInlineScriptPath(filePath) {
40
+ return isFlowInlineScriptPathInternal(filePath);
29
41
  }
30
42
  async function push(opts, filePath) {
31
43
  opts = await mergeConfigWithConfigFile(opts);
@@ -89,7 +101,8 @@ export async function handleScriptMetadata(path, workspace, alreadySynced, messa
89
101
  }
90
102
  }
91
103
  export async function handleFile(path, workspace, alreadySynced, message, opts, rawWorkspaceDependencies, codebases) {
92
- if (!path.includes(".inline_script.") &&
104
+ if (!isAppInlineScriptPath(path) &&
105
+ !isFlowInlineScriptPath(path) &&
93
106
  !isRawAppBackendPath(path) &&
94
107
  exts.some((exts) => path.endsWith(exts))) {
95
108
  if (alreadySynced.includes(path)) {
@@ -736,8 +749,8 @@ async function generateMetadata(opts, scriptPath) {
736
749
  const elems = await elementsToMap(await FSFSElement(dntShim.Deno.cwd(), codebases, false), (p, isD) => {
737
750
  return ((!isD && !exts.some((ext) => p.endsWith(ext))) ||
738
751
  ignore(p, isD) ||
739
- p.includes(".flow" + SEP) ||
740
- p.includes(".app" + SEP));
752
+ isFlowPath(p) ||
753
+ isAppPath(p));
741
754
  }, false, {});
742
755
  let hasAny = false;
743
756
  log.info("Generating metadata for all stale scripts:");
@@ -20,6 +20,7 @@ import { extractInlineScripts as extractInlineScriptsForFlows } from "../../../w
20
20
  import { generateFlowLockInternal } from "../flow/flow_metadata.js";
21
21
  import { isExecutionModeAnonymous } from "../app/app.js";
22
22
  import { APP_BACKEND_FOLDER, generateAppLocksInternal, } from "../app/app_metadata.js";
23
+ import { isFlowPath, isAppPath, isRawAppPath, extractFolderPath, isFlowMetadataFile, isAppMetadataFile, isRawAppMetadataFile, isRawAppFolderMetadataFile, getDeleteSuffix, transformJsonPathToDir, getFolderSuffix, getNonDottedPaths, } from "../../utils/resource_folders.js";
23
24
  // Merge CLI options with effective settings, preserving CLI flags as overrides
24
25
  function mergeCliWithEffectiveOptions(cliOpts, effectiveOpts) {
25
26
  // overlay CLI options on top (undefined cliOpts won't override effectiveOpts)
@@ -567,11 +568,11 @@ export function extractInlineScriptsForApps(key, rec, pathAssigner, toId, remove
567
568
  }
568
569
  function ZipFSElement(zip, useYaml, defaultTs, resourceTypeToFormatExtension, ignoreCodebaseChanges) {
569
570
  async function _internal_file(p, f) {
570
- const kind = p.endsWith(".flow.json")
571
+ const kind = isFlowMetadataFile(p)
571
572
  ? "flow"
572
- : p.endsWith(".app.json")
573
+ : isAppMetadataFile(p)
573
574
  ? "app"
574
- : p.endsWith(".raw_app.json")
575
+ : isRawAppMetadataFile(p)
575
576
  ? "raw_app"
576
577
  : p.endsWith(".script.json")
577
578
  ? "script"
@@ -583,13 +584,13 @@ function ZipFSElement(zip, useYaml, defaultTs, resourceTypeToFormatExtension, ig
583
584
  const isJson = p.endsWith(".json");
584
585
  function transformPath() {
585
586
  if (kind == "flow") {
586
- return p.replace("flow.json", "flow");
587
+ return transformJsonPathToDir(p, "flow");
587
588
  }
588
589
  else if (kind == "app") {
589
- return p.replace("app.json", "app");
590
+ return transformJsonPathToDir(p, "app");
590
591
  }
591
592
  else if (kind == "raw_app") {
592
- return p.replace("raw_app.json", "raw_app");
593
+ return transformJsonPathToDir(p, "raw_app");
593
594
  }
594
595
  else if (kind == "dependencies") {
595
596
  return p;
@@ -615,7 +616,8 @@ function ZipFSElement(zip, useYaml, defaultTs, resourceTypeToFormatExtension, ig
615
616
  }
616
617
  let inlineScripts;
617
618
  try {
618
- inlineScripts = extractInlineScriptsForFlows(flow.value.modules, {}, SEP, defaultTs);
619
+ inlineScripts = extractInlineScriptsForFlows(flow.value.modules, {}, SEP, defaultTs, undefined, // pathAssigner - let it create one
620
+ { skipInlineScriptSuffix: getNonDottedPaths() });
619
621
  }
620
622
  catch (error) {
621
623
  log.error(`Failed to extract inline scripts for flow at path: ${p}`);
@@ -653,7 +655,7 @@ function ZipFSElement(zip, useYaml, defaultTs, resourceTypeToFormatExtension, ig
653
655
  }
654
656
  let inlineScripts;
655
657
  try {
656
- inlineScripts = extractInlineScriptsForApps(undefined, app?.["value"], newPathAssigner(defaultTs), (_, val) => val["name"], false);
658
+ inlineScripts = extractInlineScriptsForApps(undefined, app?.["value"], newPathAssigner(defaultTs, { skipInlineScriptSuffix: getNonDottedPaths() }), (_, val) => val["name"], false);
657
659
  }
658
660
  catch (error) {
659
661
  log.error(`Failed to extract inline scripts for app at path: ${p}`);
@@ -1053,7 +1055,7 @@ export async function elementsToMap(els, ignore, json, skips, specificItems) {
1053
1055
  }
1054
1056
  }
1055
1057
  if (isRawAppFile(path)) {
1056
- const suffix = path.split(".raw_app" + SEP).pop();
1058
+ const suffix = path.split(getFolderSuffix("raw_app") + SEP).pop();
1057
1059
  if (suffix?.startsWith("dist/") ||
1058
1060
  suffix == "wmill.d.ts" ||
1059
1061
  suffix == "package-lock.json" ||
@@ -1498,27 +1500,24 @@ export async function ignoreF(wmillconf) {
1498
1500
  (!isDirectory && whitelist != undefined && !whitelist.approve(p))));
1499
1501
  };
1500
1502
  }
1501
- const FLOW_EXT = ".flow" + SEP;
1502
- const APP_EXT = ".app" + SEP;
1503
- const RAW_APP_EXT = ".raw_app" + SEP;
1504
1503
  // deno-lint-ignore no-inner-declarations
1505
1504
  async function addToChangedIfNotExists(p, tracker) {
1506
1505
  const isScript = exts.some((e) => p.endsWith(e));
1507
1506
  if (isScript) {
1508
- if (p.includes(FLOW_EXT)) {
1509
- const folder = p.substring(0, p.indexOf(FLOW_EXT)) + FLOW_EXT;
1507
+ if (isFlowPath(p)) {
1508
+ const folder = extractFolderPath(p, "flow");
1510
1509
  if (!tracker.flows.includes(folder)) {
1511
1510
  tracker.flows.push(folder);
1512
1511
  }
1513
1512
  }
1514
- else if (p.includes(APP_EXT)) {
1515
- const folder = p.substring(0, p.indexOf(APP_EXT)) + APP_EXT;
1513
+ else if (isAppPath(p)) {
1514
+ const folder = extractFolderPath(p, "app");
1516
1515
  if (!tracker.apps.includes(folder)) {
1517
1516
  tracker.apps.push(folder);
1518
1517
  }
1519
1518
  }
1520
- else if (p.includes(RAW_APP_EXT)) {
1521
- const folder = p.substring(0, p.indexOf(RAW_APP_EXT)) + RAW_APP_EXT;
1519
+ else if (isRawAppPath(p)) {
1520
+ const folder = extractFolderPath(p, "raw_app");
1522
1521
  if (!tracker.rawApps.includes(folder)) {
1523
1522
  tracker.rawApps.push(folder);
1524
1523
  }
@@ -2040,7 +2039,7 @@ export async function push(opts) {
2040
2039
  const isRawApp = isRawAppFile(changes[0].path);
2041
2040
  if (isRawApp) {
2042
2041
  const deleteRawApp = changes.find((change) => change.name === "deleted" &&
2043
- change.path.endsWith(".raw_app/raw_app.yaml"));
2042
+ isRawAppFolderMetadataFile(change.path));
2044
2043
  if (deleteRawApp) {
2045
2044
  changes = [deleteRawApp];
2046
2045
  }
@@ -2178,21 +2177,20 @@ export async function push(opts) {
2178
2177
  case "flow":
2179
2178
  await wmill.deleteFlowByPath({
2180
2179
  workspace: workspaceId,
2181
- path: removeSuffix(target, ".flow/flow.json"),
2180
+ path: removeSuffix(target, getDeleteSuffix("flow", "json")),
2182
2181
  });
2183
2182
  break;
2184
2183
  case "app":
2185
2184
  await wmill.deleteApp({
2186
2185
  workspace: workspaceId,
2187
- path: removeSuffix(target, ".app/app.json"),
2186
+ path: removeSuffix(target, getDeleteSuffix("app", "json")),
2188
2187
  });
2189
2188
  break;
2190
2189
  case "raw_app":
2191
- if (target.endsWith(".raw_app/raw_app.yaml") ||
2192
- target.endsWith(".raw_app/raw_app.json")) {
2190
+ if (isRawAppFolderMetadataFile(target)) {
2193
2191
  await wmill.deleteApp({
2194
2192
  workspace: workspaceId,
2195
- path: removeSuffix(target, ".raw_app/raw_app.json"),
2193
+ path: removeSuffix(target, getDeleteSuffix("raw_app", "json")),
2196
2194
  });
2197
2195
  }
2198
2196
  break;
@@ -2,7 +2,7 @@ import { colors, Input, log, setClient } from "../../../deps.js";
2
2
  import { allWorkspaces, list, removeWorkspace } from "./workspace.js";
3
3
  import * as wmill from "../../../gen/services.gen.js";
4
4
  import { getCurrentGitBranch, getOriginalBranchForWorkspaceForks, isGitRepository } from "../../utils/git.js";
5
- import { WM_FORK_PREFIX } from "../../main.js";
5
+ import { WM_FORK_PREFIX } from "../../core/constants.js";
6
6
  import { tryResolveBranchWorkspace } from "../../core/context.js";
7
7
  // NOTE: This import will work after regenerating the API client
8
8
  // Run ./gen_wm_client.sh to regenerate after backend changes
@@ -4,6 +4,7 @@ import { getCurrentGitBranch, getOriginalBranchForWorkspaceForks, isGitRepositor
4
4
  import { join, dirname, resolve, relative } from "node:path";
5
5
  import { existsSync } from "node:fs";
6
6
  import { execSync } from "node:child_process";
7
+ import { setNonDottedPaths } from "../utils/resource_folders.js";
7
8
  export let showDiffs = false;
8
9
  export function setShowDiffs(value) {
9
10
  showDiffs = value;
@@ -143,6 +144,8 @@ export async function readConfigFile() {
143
144
  if (conf?.defaultTs == undefined) {
144
145
  log.warn("No defaultTs defined in your wmill.yaml. Using 'bun' as default.");
145
146
  }
147
+ // Initialize global nonDottedPaths setting from config
148
+ setNonDottedPaths(conf?.nonDottedPaths ?? false);
146
149
  return typeof conf == "object" ? conf : {};
147
150
  }
148
151
  catch (e) {
@@ -191,6 +194,7 @@ export const DEFAULT_SYNC_OPTIONS = {
191
194
  includeSettings: false,
192
195
  includeKey: false,
193
196
  skipWorkspaceDependencies: false,
197
+ nonDottedPaths: false,
194
198
  };
195
199
  export async function mergeConfigWithConfigFile(opts) {
196
200
  const configFile = await readConfigFile();
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Shared constants used across the CLI.
3
+ * This file should have minimal imports to avoid circular dependencies.
4
+ */
5
+ export const WM_FORK_PREFIX = "wm-fork";
@@ -7,7 +7,7 @@ import { getActiveWorkspace, getWorkspaceByName, allWorkspaces, addWorkspace, }
7
7
  import { getLastUsedProfile, setLastUsedProfile } from "./branch-profiles.js";
8
8
  import { readConfigFile } from "./conf.js";
9
9
  import { getCurrentGitBranch, getOriginalBranchForWorkspaceForks, getWorkspaceIdForWorkspaceForkFromBranchName, isGitRepository, } from "../utils/git.js";
10
- import { WM_FORK_PREFIX } from "../main.js";
10
+ import { WM_FORK_PREFIX } from "./constants.js";
11
11
  // Helper function to select from multiple matching profiles
12
12
  async function selectFromMultipleProfiles(profiles, baseUrl, workspaceId, context, configDir) {
13
13
  if (profiles.length === 1) {
package/esm/src/main.js CHANGED
@@ -40,8 +40,9 @@ export { flow, app, script, workspace, resource, resourceType, user, variable, h
40
40
  // console.error(JSON.stringify(event.error, null, 4));
41
41
  // }
42
42
  // });
43
- export const VERSION = "1.600.1";
44
- export const WM_FORK_PREFIX = "wm-fork";
43
+ export const VERSION = "1.601.0";
44
+ // Re-exported from constants.ts to maintain backwards compatibility
45
+ export { WM_FORK_PREFIX } from "./core/constants.js";
45
46
  const command = new Command()
46
47
  .name("wmill")
47
48
  .action(() => log.info(`Welcome to Windmill CLI ${VERSION}. Use -h for help.`))
package/esm/src/types.js CHANGED
@@ -17,6 +17,7 @@ import { pushWorkspaceDependencies } from "./commands/dependencies/dependencies.
17
17
  import { pushWorkspaceSettings, pushWorkspaceKey } from "./core/settings.js";
18
18
  import { pushTrigger } from "./commands/trigger/trigger.js";
19
19
  import { pushRawApp } from "./commands/app/raw_apps.js";
20
+ import { isFlowPath, isAppPath, isRawAppPath, extractResourceName, buildFolderPath, } from "./utils/resource_folders.js";
20
21
  export const TRIGGER_TYPES = [
21
22
  "http",
22
23
  "websocket",
@@ -93,12 +94,12 @@ export function showConflict(path, local, remote) {
93
94
  export async function pushObj(workspace, p, befObj, newObj, plainSecrets, alreadySynced, message, originalLocalPath) {
94
95
  const typeEnding = getTypeStrFromPath(p);
95
96
  if (typeEnding === "app") {
96
- const appName = p.split(".app" + SEP)[0];
97
- await pushApp(workspace, appName, appName + ".app", message);
97
+ const appName = extractResourceName(p, "app");
98
+ await pushApp(workspace, appName, buildFolderPath(appName, "app"), message);
98
99
  }
99
100
  else if (typeEnding === "raw_app") {
100
- const rawAppName = p.split(".raw_app" + SEP)[0];
101
- await pushRawApp(workspace, rawAppName, rawAppName + ".raw_app", message);
101
+ const rawAppName = extractResourceName(p, "raw_app");
102
+ await pushRawApp(workspace, rawAppName, buildFolderPath(rawAppName, "raw_app"), message);
102
103
  }
103
104
  else if (typeEnding === "folder") {
104
105
  await pushFolder(workspace, p, befObj, newObj);
@@ -107,8 +108,8 @@ export async function pushObj(workspace, p, befObj, newObj, plainSecrets, alread
107
108
  await pushVariable(workspace, p, befObj, newObj, plainSecrets);
108
109
  }
109
110
  else if (typeEnding === "flow") {
110
- const flowName = p.split(".flow" + SEP)[0];
111
- await pushFlow(workspace, flowName, flowName + ".flow", message);
111
+ const flowName = extractResourceName(p, "flow");
112
+ await pushFlow(workspace, flowName, buildFolderPath(flowName, "flow"), message);
112
113
  }
113
114
  else if (typeEnding === "resource") {
114
115
  if (!alreadySynced.includes(p)) {
@@ -189,13 +190,13 @@ export function parseFromFile(p) {
189
190
  }
190
191
  }
191
192
  export function getTypeStrFromPath(p) {
192
- if (p.includes(".flow" + SEP)) {
193
+ if (isFlowPath(p)) {
193
194
  return "flow";
194
195
  }
195
- if (p.includes(".app" + SEP)) {
196
+ if (isAppPath(p)) {
196
197
  return "app";
197
198
  }
198
- if (p.includes(".raw_app" + SEP)) {
199
+ if (isRawAppPath(p)) {
199
200
  return "raw_app";
200
201
  }
201
202
  if (p.startsWith("dependencies" + SEP)) {
@@ -1,6 +1,6 @@
1
1
  import { log } from "../../deps.js";
2
2
  import { execSync } from "node:child_process";
3
- import { WM_FORK_PREFIX } from "../main.js";
3
+ import { WM_FORK_PREFIX } from "../core/constants.js";
4
4
  export function getCurrentGitBranch() {
5
5
  try {
6
6
  const result = execSync("git rev-parse --abbrev-ref HEAD", {