windmill-cli 1.589.0 → 1.589.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.
Files changed (31) hide show
  1. package/esm/gen/core/OpenAPI.js +1 -1
  2. package/esm/src/commands/app/app_metadata.js +224 -33
  3. package/esm/src/commands/app/apps.js +18 -14
  4. package/esm/src/commands/app/dev.js +10 -8
  5. package/esm/src/commands/app/raw_apps.js +7 -59
  6. package/esm/src/commands/flow/flow_metadata.js +5 -2
  7. package/esm/src/commands/init/init.js +5 -7
  8. package/esm/src/commands/sync/sync.js +29 -10
  9. package/esm/src/commands/trigger/trigger.js +3 -7
  10. package/esm/src/main.js +2 -2
  11. package/esm/src/utils/utils.js +9 -0
  12. package/package.json +1 -1
  13. package/types/gen/types.gen.d.ts +2 -0
  14. package/types/gen/types.gen.d.ts.map +1 -1
  15. package/types/src/commands/app/app_metadata.d.ts +8 -1
  16. package/types/src/commands/app/app_metadata.d.ts.map +1 -1
  17. package/types/src/commands/app/apps.d.ts +1 -1
  18. package/types/src/commands/app/apps.d.ts.map +1 -1
  19. package/types/src/commands/app/dev.d.ts.map +1 -1
  20. package/types/src/commands/app/raw_apps.d.ts +0 -7
  21. package/types/src/commands/app/raw_apps.d.ts.map +1 -1
  22. package/types/src/commands/flow/flow_metadata.d.ts.map +1 -1
  23. package/types/src/commands/init/init.d.ts.map +1 -1
  24. package/types/src/commands/sync/sync.d.ts.map +1 -1
  25. package/types/src/commands/trigger/trigger.d.ts.map +1 -1
  26. package/types/src/main.d.ts +1 -1
  27. package/types/src/main.d.ts.map +1 -1
  28. package/types/src/utils/utils.d.ts +1 -0
  29. package/types/src/utils/utils.d.ts.map +1 -1
  30. package/types/windmill-utils-internal/src/gen/types.gen.d.ts +2 -0
  31. package/types/windmill-utils-internal/src/gen/types.gen.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.589.0',
35
+ VERSION: '1.589.2',
36
36
  WITH_CREDENTIALS: true,
37
37
  interceptors: {
38
38
  request: new Interceptors(),
@@ -3,35 +3,35 @@ import * as dntShim from "../../../_dnt.shims.js";
3
3
  import path from "node:path";
4
4
  import { SEP, colors, log, yamlParseFile, yamlStringify, } from "../../../deps.js";
5
5
  import { checkifMetadataUptodate, blueColor, clearGlobalLock, updateMetadataGlobalLock, inferSchema, getRawWorkspaceDependencies, } from "../../utils/metadata.js";
6
- import { workspaceDependenciesLanguages } from "../../utils/script_common.js";
7
- import { inferContentTypeFromFilePath, } from "../../utils/script_common.js";
6
+ import { workspaceDependenciesLanguages, } from "../../utils/script_common.js";
8
7
  import { generateHash, getHeaders, writeIfChanged } from "../../utils/utils.js";
9
8
  import { exts } from "../script/script.js";
10
9
  import { FSFSElement, yamlOptions } from "../sync/sync.js";
11
10
  import { replaceInlineScripts } from "./apps.js";
12
11
  import { newPathAssigner, } from "../../../windmill-utils-internal/src/path-utils/path-assigner.js";
12
+ import { mergeConfigWithConfigFile } from "../../core/conf.js";
13
+ import { resolveWorkspace } from "../../core/context.js";
14
+ import { requireLogin } from "../../core/auth.js";
13
15
  const TOP_HASH = "__app_hash";
16
+ export const APP_BACKEND_FOLDER = "backend";
14
17
  /**
15
18
  * Generates a hash for all inline scripts in an app directory
16
19
  */
17
- async function generateAppHash(rawReqs, folder, defaultTs) {
18
- const runnablesFolder = path.join(folder, "runnables");
20
+ async function generateAppHash(rawReqs, folder, rawApp, defaultTs) {
21
+ const runnablesFolder = rawApp
22
+ ? path.join(folder, APP_BACKEND_FOLDER)
23
+ : folder;
19
24
  const hashes = {};
20
25
  try {
21
26
  const elems = await FSFSElement(runnablesFolder, [], true);
22
27
  for await (const f of elems.getChildren()) {
28
+ if (!rawApp && !f.path.includes(".inline_script.")) {
29
+ continue;
30
+ }
23
31
  if (exts.some((e) => f.path.endsWith(e))) {
24
- let reqs;
25
- if (rawReqs) {
26
- // Get language name from path
27
- const lang = inferContentTypeFromFilePath(f.path, defaultTs);
28
- // Get lock for that language
29
- [, reqs] =
30
- Object.entries(rawReqs).find(([lang2, _]) => lang == lang2) ?? [];
31
- }
32
32
  // Embed lock into hash
33
33
  const relativePath = f.path.replace(runnablesFolder + SEP, "");
34
- hashes[relativePath] = await generateHash((await f.getContentText()) + (reqs ?? ""));
34
+ hashes[relativePath] = await generateHash((await f.getContentText()) + JSON.stringify(rawReqs));
35
35
  }
36
36
  }
37
37
  }
@@ -46,7 +46,7 @@ async function generateAppHash(rawReqs, folder, defaultTs) {
46
46
  /**
47
47
  * Updates locks for inline scripts in an app
48
48
  */
49
- export async function generateAppLocksInternal(appFolder, dryRun, workspace, opts, justUpdateMetadataLock, noStaleMessage) {
49
+ export async function generateAppLocksInternal(appFolder, rawApp, dryRun, workspace, opts, justUpdateMetadataLock, noStaleMessage) {
50
50
  if (appFolder.endsWith(SEP)) {
51
51
  appFolder = appFolder.substring(0, appFolder.length - 1);
52
52
  }
@@ -55,7 +55,7 @@ export async function generateAppLocksInternal(appFolder, dryRun, workspace, opt
55
55
  log.info(`Generating locks for app ${appFolder} at ${remote_path}`);
56
56
  }
57
57
  const rawWorkspaceDependencies = await getRawWorkspaceDependencies();
58
- let hashes = await generateAppHash(rawWorkspaceDependencies, appFolder, opts.defaultTs);
58
+ let hashes = await generateAppHash(rawWorkspaceDependencies, appFolder, rawApp, opts.defaultTs);
59
59
  const conf = await import("../../utils/metadata.js").then((m) => m.readLockfile());
60
60
  if (await checkifMetadataUptodate(appFolder, hashes[TOP_HASH], conf, TOP_HASH)) {
61
61
  if (!noStaleMessage) {
@@ -72,7 +72,7 @@ export async function generateAppLocksInternal(appFolder, dryRun, workspace, opt
72
72
  .join("/")}) for ${appFolder}, using them`));
73
73
  }
74
74
  // Read the app file
75
- const appFilePath = path.join(appFolder, "raw_app.yaml");
75
+ const appFilePath = path.join(appFolder, rawApp ? "raw_app.yaml" : "app.yaml");
76
76
  const appFile = (await yamlParseFile(appFilePath));
77
77
  if (!justUpdateMetadataLock) {
78
78
  const changedScripts = [];
@@ -87,11 +87,21 @@ export async function generateAppLocksInternal(appFolder, dryRun, workspace, opt
87
87
  }
88
88
  if (changedScripts.length > 0) {
89
89
  log.info(`Recomputing locks of ${changedScripts.join(", ")} in ${appFolder}`);
90
- const runnablesPath = path.join(appFolder, "runnables") + SEP;
91
- // Replace inline scripts for changed runnables
92
- await replaceInlineScripts(appFile.runnables, runnablesPath);
93
- // Update the app runnables with new locks
94
- appFile.runnables = await updateAppRunnables(workspace, appFile.runnables, remote_path, appFolder, rawWorkspaceDependencies, opts.defaultTs);
90
+ if (rawApp) {
91
+ const runnablesPath = path.join(appFolder, APP_BACKEND_FOLDER) + SEP;
92
+ const rawAppFile = appFile;
93
+ // Replace inline scripts for changed runnables
94
+ replaceInlineScripts(rawAppFile.runnables, runnablesPath, false);
95
+ // Update the app runnables with new locks
96
+ rawAppFile.runnables = await updateRawAppRunnables(workspace, rawAppFile.runnables, remote_path, appFolder, rawWorkspaceDependencies, opts.defaultTs);
97
+ }
98
+ else {
99
+ const normalAppFile = appFile;
100
+ // Replace inline scripts for normal apps
101
+ replaceInlineScripts(normalAppFile.value, appFolder + SEP, false);
102
+ // Update the app value with new locks
103
+ normalAppFile.value = await updateAppInlineScripts(workspace, normalAppFile.value, remote_path, appFolder, rawWorkspaceDependencies, opts.defaultTs);
104
+ }
95
105
  // Write the updated app file
96
106
  writeIfChanged(appFilePath, yamlStringify(appFile, yamlOptions));
97
107
  }
@@ -100,7 +110,7 @@ export async function generateAppLocksInternal(appFolder, dryRun, workspace, opt
100
110
  }
101
111
  }
102
112
  // Regenerate hashes after updates
103
- hashes = await generateAppHash(rawWorkspaceDependencies, appFolder, opts.defaultTs);
113
+ hashes = await generateAppHash(rawWorkspaceDependencies, appFolder, rawApp, opts.defaultTs);
104
114
  await clearGlobalLock(appFolder);
105
115
  for (const [scriptPath, hash] of Object.entries(hashes)) {
106
116
  await updateMetadataGlobalLock(appFolder, hash, scriptPath);
@@ -108,12 +118,45 @@ export async function generateAppLocksInternal(appFolder, dryRun, workspace, opt
108
118
  log.info(colors.green(`App ${remote_path} lockfiles updated`));
109
119
  }
110
120
  /**
111
- * Updates locks for all runnables in an app, generating locks inline script by inline script
121
+ * Traverses an app structure (either app.value for normal apps or app.runnables for raw apps)
122
+ * and processes all inline scripts found, returning the updated structure
123
+ */
124
+ async function traverseAndProcessInlineScripts(obj, processor, currentPath = []) {
125
+ if (!obj || typeof obj !== "object") {
126
+ return obj;
127
+ }
128
+ if (Array.isArray(obj)) {
129
+ return await Promise.all(obj.map((item, index) => traverseAndProcessInlineScripts(item, processor, [
130
+ ...currentPath,
131
+ `[${index}]`,
132
+ ])));
133
+ }
134
+ const result = {};
135
+ for (const [key, value] of Object.entries(obj)) {
136
+ if (key === "inlineScript" && typeof value === "object") {
137
+ // Found an inline script - process it
138
+ result[key] = await processor(value, {
139
+ path: currentPath,
140
+ parentKey: key,
141
+ parentObject: obj,
142
+ });
143
+ }
144
+ else {
145
+ // Recursively process nested objects
146
+ result[key] = await traverseAndProcessInlineScripts(value, processor, [
147
+ ...currentPath,
148
+ key,
149
+ ]);
150
+ }
151
+ }
152
+ return result;
153
+ }
154
+ /**
155
+ * Updates locks for all runnables in a raw app, generating locks inline script by inline script
112
156
  * Also writes content and locks back to the runnables folder
113
157
  */
114
- async function updateAppRunnables(workspace, runnables, remotePath, appFolder, rawDeps, defaultTs = "bun") {
115
- const updatedRunnables = { ...runnables };
116
- const runnablesFolder = path.join(appFolder, "runnables");
158
+ async function updateRawAppRunnables(workspace, runnables, remotePath, appFolder, rawDeps, defaultTs = "bun") {
159
+ const runnablesFolder = path.join(appFolder, APP_BACKEND_FOLDER);
117
160
  // Ensure runnables folder exists
118
161
  try {
119
162
  await dntShim.Deno.mkdir(runnablesFolder, { recursive: true });
@@ -122,31 +165,36 @@ async function updateAppRunnables(workspace, runnables, remotePath, appFolder, r
122
165
  // Folder may already exist
123
166
  }
124
167
  const pathAssigner = newPathAssigner(defaultTs);
168
+ // Process each runnable
169
+ const updatedRunnables = {};
125
170
  for (const [runnableId, runnable] of Object.entries(runnables)) {
126
171
  // Only process inline scripts (runnableByName with inlineScript)
127
172
  if (runnable?.type !== "runnableByName" || !runnable?.inlineScript) {
173
+ updatedRunnables[runnableId] = runnable;
128
174
  continue;
129
175
  }
130
176
  const inlineScript = runnable.inlineScript;
131
177
  const language = inlineScript.language;
132
178
  const content = inlineScript.content;
133
179
  if (!content || !language) {
180
+ updatedRunnables[runnableId] = runnable;
134
181
  continue;
135
182
  }
136
183
  // Skip if content is still an !inline reference (should have been replaced by replaceInlineScripts)
137
184
  if (typeof content === "string" && content.startsWith("!inline ")) {
138
185
  log.warn(colors.yellow(`Runnable ${runnableId} content is still an !inline reference, skipping`));
186
+ updatedRunnables[runnableId] = runnable;
139
187
  continue;
140
188
  }
141
189
  // Skip frontend scripts - they don't need locks
142
190
  if (language === "frontend") {
191
+ updatedRunnables[runnableId] = runnable;
143
192
  continue;
144
193
  }
145
- // Find raw deps for this language if available
146
- const langRawDeps = rawDeps?.[language];
147
- log.info(colors.gray(`Generating lock for runnable ${runnableId} (${language})${langRawDeps ? " with raw deps" : ""}`));
194
+ log.info(colors.gray(`Generating lock for runnable ${runnableId} (${language})
195
+ }`));
148
196
  try {
149
- const lock = await generateInlineScriptLock(workspace, content, language, `${remotePath}/${runnableId}`, langRawDeps);
197
+ const lock = await generateInlineScriptLock(workspace, content, language, `${remotePath}/${runnableId}`, rawDeps);
150
198
  // Determine file extension for this language
151
199
  const [basePathO, ext] = pathAssigner.assignPath(runnable.name, language);
152
200
  const basePath = basePathO.replaceAll(SEP, "/");
@@ -174,14 +222,72 @@ async function updateAppRunnables(workspace, runnables, remotePath, appFolder, r
174
222
  catch (error) {
175
223
  log.error(colors.red(`Failed to generate lock for runnable ${runnableId}: ${error.message}`));
176
224
  // Continue with other runnables even if one fails
225
+ updatedRunnables[runnableId] = runnable;
177
226
  }
178
227
  }
179
228
  return updatedRunnables;
180
229
  }
230
+ /**
231
+ * Updates locks for all inline scripts in a normal app, similar to updateRawAppRunnables
232
+ * but for the app.value structure instead of app.runnables
233
+ */
234
+ async function updateAppInlineScripts(workspace, appValue, remotePath, appFolder, rawDeps, defaultTs = "bun") {
235
+ const pathAssigner = newPathAssigner(defaultTs);
236
+ const processor = async (inlineScript, context) => {
237
+ const language = inlineScript.language;
238
+ const content = inlineScript.content;
239
+ if (!content || !language) {
240
+ return inlineScript;
241
+ }
242
+ // Skip if content is still an !inline reference (should have been replaced by replaceInlineScripts)
243
+ if (typeof content === "string" && content.startsWith("!inline ")) {
244
+ log.warn(colors.yellow(`Inline script at ${context.path.join(".")} is still an !inline reference, skipping`));
245
+ return inlineScript;
246
+ }
247
+ // Skip frontend scripts - they don't need locks
248
+ if (language === "frontend") {
249
+ return inlineScript;
250
+ }
251
+ // Get the name from the parent object (following extractInlineScriptsForApps pattern)
252
+ // For normal apps, the name is stored in the component's "name" property
253
+ const scriptName = context.parentObject?.["name"] || "unnamed";
254
+ const scriptPath = `${remotePath}/${context.path.join("/")}`;
255
+ log.info(colors.gray(`Generating lock for inline script "${scriptName}" at ${context.path.join(".")} (${language})`));
256
+ try {
257
+ const lock = await generateInlineScriptLock(workspace, content, language, scriptPath, rawDeps);
258
+ // Determine file extension for this language (following extractInlineScriptsForApps pattern)
259
+ const [basePathO, ext] = pathAssigner.assignPath(scriptName, language);
260
+ const basePath = basePathO.replaceAll(SEP, "/");
261
+ const contentPath = path.join(appFolder, `${basePath}${ext}`);
262
+ const lockPath = path.join(appFolder, `${basePath}lock`);
263
+ // Write content to file
264
+ writeIfChanged(contentPath, content);
265
+ // Write lock to file if it exists
266
+ if (lock && lock !== "") {
267
+ writeIfChanged(lockPath, lock);
268
+ }
269
+ // Update the inline script with !inline references
270
+ const inlineContentRef = `!inline ${basePath}${ext}`;
271
+ const inlineLockRef = lock && lock !== "" ? `!inline ${basePath}lock` : "";
272
+ log.info(colors.gray(` Written ${basePath}${ext}${lock ? ` and ${basePath}lock` : ""}`));
273
+ return {
274
+ ...inlineScript,
275
+ content: inlineContentRef,
276
+ lock: inlineLockRef,
277
+ };
278
+ }
279
+ catch (error) {
280
+ log.error(colors.red(`Failed to generate lock for inline script at ${context.path.join(".")}: ${error.message}`));
281
+ // Return original on error
282
+ return inlineScript;
283
+ }
284
+ };
285
+ return await traverseAndProcessInlineScripts(appValue, processor);
286
+ }
181
287
  /**
182
288
  * Generates a lock for a single inline script using the dependencies endpoint
183
289
  */
184
- async function generateInlineScriptLock(workspace, content, language, scriptPath, rawDeps) {
290
+ async function generateInlineScriptLock(workspace, content, language, scriptPath, rawWorkspaceDependencies) {
185
291
  const extraHeaders = getHeaders();
186
292
  const rawResponse = await fetch(`${workspace.remote}api/w/${workspace.workspaceId}/jobs/run/dependencies`, {
187
293
  method: "POST",
@@ -198,7 +304,10 @@ async function generateInlineScriptLock(workspace, content, language, scriptPath
198
304
  script_path: scriptPath,
199
305
  },
200
306
  ],
201
- raw_deps: rawDeps,
307
+ raw_workspace_dependencies: rawWorkspaceDependencies &&
308
+ Object.keys(rawWorkspaceDependencies).length > 0
309
+ ? rawWorkspaceDependencies
310
+ : null,
202
311
  entrypoint: scriptPath,
203
312
  }),
204
313
  });
@@ -260,7 +369,7 @@ export async function inferRunnableSchemaFromFile(appFolder, runnableFilePath) {
260
369
  const inlineScript = runnable.inlineScript;
261
370
  const language = inlineScript.language;
262
371
  // Read the actual content from the file
263
- const fullFilePath = path.join(appFolder, "runnables", runnableFilePath);
372
+ const fullFilePath = path.join(appFolder, APP_BACKEND_FOLDER, runnableFilePath);
264
373
  let content;
265
374
  try {
266
375
  content = await dntShim.Deno.readTextFile(fullFilePath);
@@ -285,3 +394,85 @@ export async function inferRunnableSchemaFromFile(appFolder, runnableFilePath) {
285
394
  return undefined;
286
395
  }
287
396
  }
397
+ function getAppFolders(elems, extension) {
398
+ return Object.keys(elems)
399
+ .filter((p) => p.endsWith(SEP + extension))
400
+ .map((p) => p.substring(0, p.length - (SEP + extension).length));
401
+ }
402
+ export async function generateLocksCommand(opts, appPath) {
403
+ const { generateAppLocksInternal } = await import("./app_metadata.js");
404
+ const { elementsToMap, FSFSElement } = await import("../sync/sync.js");
405
+ const { ignoreF } = await import("../sync/sync.js");
406
+ const { Confirm } = await import("../../../deps.js");
407
+ if (appPath == "") {
408
+ appPath = undefined;
409
+ }
410
+ const workspace = await resolveWorkspace(opts);
411
+ await requireLogin(opts);
412
+ opts = await mergeConfigWithConfigFile(opts);
413
+ if (appPath) {
414
+ //TODO: Generate metadata for a specific raw app but handle normal apps to
415
+ throw new Error("Not implemented");
416
+ // Generate metadata for a specific app
417
+ // await generateAppLocksInternal(
418
+ // appPath,
419
+ // true,
420
+ // false,
421
+ // workspace,
422
+ // opts,
423
+ // false,
424
+ // false
425
+ // );
426
+ }
427
+ else {
428
+ // Generate metadata for all apps
429
+ const ignore = await ignoreF(opts);
430
+ const elems = await elementsToMap(await FSFSElement(dntShim.Deno.cwd(), [], true), (p, isD) => {
431
+ return (ignore(p, isD) ||
432
+ (!isD &&
433
+ !p.endsWith(SEP + "raw_app.yaml") &&
434
+ !p.endsWith(SEP + "app.yaml")));
435
+ }, false, {});
436
+ const rawAppFolders = getAppFolders(elems, "raw_app.yaml");
437
+ const appFolders = getAppFolders(elems, "app.yaml");
438
+ let hasAny = false;
439
+ log.info(`Checking metadata for all apps (${appFolders.length}) and raw apps (${rawAppFolders.length})`);
440
+ for (const appFolder of rawAppFolders) {
441
+ const candidate = await generateAppLocksInternal(appFolder, true, true, workspace, opts, false, true);
442
+ if (candidate) {
443
+ hasAny = true;
444
+ log.info(colors.green(`+ ${candidate}`));
445
+ }
446
+ }
447
+ for (const appFolder of appFolders) {
448
+ const candidate = await generateAppLocksInternal(appFolder, false, true, workspace, opts, false, true);
449
+ if (candidate) {
450
+ hasAny = true;
451
+ log.info(colors.green(`+ ${candidate}`));
452
+ }
453
+ }
454
+ if (hasAny) {
455
+ if (opts.dryRun) {
456
+ log.info(colors.gray(`Dry run complete.`));
457
+ return;
458
+ }
459
+ if (!opts.yes &&
460
+ !(await Confirm.prompt({
461
+ message: "Update the metadata of the above apps?",
462
+ default: true,
463
+ }))) {
464
+ return;
465
+ }
466
+ }
467
+ else {
468
+ log.info(colors.green.bold("No metadata to update"));
469
+ return;
470
+ }
471
+ for (const appFolder of rawAppFolders) {
472
+ await generateAppLocksInternal(appFolder, true, false, workspace, opts, false, true);
473
+ }
474
+ for (const appFolder of appFolders) {
475
+ await generateAppLocksInternal(appFolder, false, false, workspace, opts, false, true);
476
+ }
477
+ }
478
+ }
@@ -33,26 +33,30 @@ export function repopulateFields(runnables) {
33
33
  }
34
34
  });
35
35
  }
36
- export function replaceInlineScripts(rec, localPath) {
36
+ export function replaceInlineScripts(rec, localPath, addType) {
37
37
  if (!rec) {
38
38
  return;
39
39
  }
40
40
  if (typeof rec == "object") {
41
41
  return Object.entries(rec).flatMap(([k, v]) => {
42
42
  if (k == "runType") {
43
- if (isVersionsGeq1585()) {
44
- rec["type"] = "path";
45
- }
46
- else {
47
- rec["type"] = "runnableByPath";
43
+ if (addType) {
44
+ if (isVersionsGeq1585()) {
45
+ rec["type"] = "path";
46
+ }
47
+ else {
48
+ rec["type"] = "runnableByPath";
49
+ }
48
50
  }
49
51
  }
50
52
  else if (k == "inlineScript" && typeof v == "object") {
51
- if (isVersionsGeq1585()) {
52
- rec["type"] = "inline";
53
- }
54
- else {
55
- rec["type"] = "runnableByName";
53
+ if (addType) {
54
+ if (isVersionsGeq1585()) {
55
+ rec["type"] = "inline";
56
+ }
57
+ else {
58
+ rec["type"] = "runnableByName";
59
+ }
56
60
  }
57
61
  const o = v;
58
62
  if (o["content"] && o["content"].startsWith("!inline")) {
@@ -65,7 +69,7 @@ export function replaceInlineScripts(rec, localPath) {
65
69
  }
66
70
  }
67
71
  else {
68
- replaceInlineScripts(v, localPath);
72
+ replaceInlineScripts(v, localPath, addType);
69
73
  }
70
74
  });
71
75
  }
@@ -103,7 +107,7 @@ export async function pushApp(workspace, remotePath, localPath, message) {
103
107
  }
104
108
  const path = localPath + "app.yaml";
105
109
  const localApp = (await yamlParseFile(path));
106
- replaceInlineScripts(localApp.value, localPath);
110
+ replaceInlineScripts(localApp.value, localPath, true);
107
111
  await generatingPolicy(localApp, remotePath, localApp?.["public"] ??
108
112
  localApp?.["policy"]?.["execution_mode"] == "anonymous");
109
113
  if (app) {
@@ -192,7 +196,7 @@ const command = new Command()
192
196
  .option("--dry-run", "Perform a dry run without making changes")
193
197
  .option("--default-ts <runtime:string>", "Default TypeScript runtime (bun or deno)")
194
198
  .action(async (opts, appFolder) => {
195
- const { generateLocksCommand } = await import("./raw_apps.js");
199
+ const { generateLocksCommand } = await import("./app_metadata.js");
196
200
  await generateLocksCommand(opts, appFolder);
197
201
  });
198
202
  export default command;
@@ -1,20 +1,20 @@
1
1
  // deno-lint-ignore-file no-explicit-any
2
2
  import * as dntShim from "../../../_dnt.shims.js";
3
- import { Command, colors, log, getPort, open, windmillUtils, yamlParseFile, } from "../../../deps.js";
3
+ import { Command, colors, log, getPort, open, windmillUtils, yamlParseFile, SEP, } from "../../../deps.js";
4
4
  import * as http from "node:http";
5
5
  import * as fs from "node:fs";
6
6
  import * as path from "node:path";
7
7
  import process from "node:process";
8
8
  import { writeFileSync } from "node:fs";
9
9
  import { WebSocketServer } from "ws";
10
- import { getDevBuildOptions, ensureNodeModules, createFrameworkPlugins, detectFrameworks } from "./bundle.js";
10
+ import { getDevBuildOptions, ensureNodeModules, createFrameworkPlugins, detectFrameworks, } from "./bundle.js";
11
11
  import { wmillTsDev as wmillTs } from "./wmillTsDev.js";
12
12
  import * as wmill from "../../../gen/services.gen.js";
13
13
  import { resolveWorkspace } from "../../core/context.js";
14
14
  import { requireLogin } from "../../core/auth.js";
15
15
  import { GLOBAL_CONFIG_OPT } from "../../core/conf.js";
16
16
  import { replaceInlineScripts } from "./apps.js";
17
- import { inferRunnableSchemaFromFile } from "./app_metadata.js";
17
+ import { APP_BACKEND_FOLDER, inferRunnableSchemaFromFile, } from "./app_metadata.js";
18
18
  const DEFAULT_PORT = 4000;
19
19
  const DEFAULT_HOST = "localhost";
20
20
  // HTML template with live reload
@@ -96,7 +96,7 @@ async function dev(opts) {
96
96
  const shouldOpen = opts.open ?? true;
97
97
  // Detect frameworks to determine default entry point
98
98
  const frameworks = detectFrameworks(process.cwd());
99
- const defaultEntry = (frameworks.svelte || frameworks.vue) ? "index.ts" : "index.tsx";
99
+ const defaultEntry = frameworks.svelte || frameworks.vue ? "index.ts" : "index.tsx";
100
100
  const entryPoint = opts.entry ?? defaultEntry;
101
101
  // Verify entry point exists
102
102
  if (!fs.existsSync(entryPoint)) {
@@ -177,7 +177,7 @@ async function dev(opts) {
177
177
  // Initial build
178
178
  await ctx.rebuild();
179
179
  // Watch runnables folder for changes
180
- const runnablesPath = path.join(process.cwd(), "runnables");
180
+ const runnablesPath = path.join(process.cwd(), APP_BACKEND_FOLDER);
181
181
  let runnablesWatcher;
182
182
  if (fs.existsSync(runnablesPath)) {
183
183
  log.info(colors.blue(`👁️ Watching runnables folder at: ${runnablesPath}\n`));
@@ -498,7 +498,7 @@ async function loadRunnables() {
498
498
  try {
499
499
  const localPath = process.cwd();
500
500
  const rawApp = (await yamlParseFile(path.join(localPath, "raw_app.yaml")));
501
- replaceInlineScripts(rawApp.runnables, path.join(localPath, "runnables/"));
501
+ replaceInlineScripts(rawApp.runnables, path.join(localPath, APP_BACKEND_FOLDER) + SEP, true);
502
502
  return rawApp?.runnables ?? {};
503
503
  }
504
504
  catch (error) {
@@ -525,7 +525,8 @@ async function executeRunnable(runnable, workspace, appPath, runnableId, args) {
525
525
  }
526
526
  }
527
527
  }
528
- if ((runnable.type === "inline" || runnable.type === "runnableByName") && runnable.inlineScript) {
528
+ if ((runnable.type === "inline" || runnable.type === "runnableByName") &&
529
+ runnable.inlineScript) {
529
530
  const inlineScript = runnable.inlineScript;
530
531
  if (inlineScript.id !== undefined) {
531
532
  requestBody.id = inlineScript.id;
@@ -538,7 +539,8 @@ async function executeRunnable(runnable, workspace, appPath, runnableId, args) {
538
539
  cache_ttl: inlineScript.cache_ttl,
539
540
  };
540
541
  }
541
- else if ((runnable.type === "path" || runnable.type === "runnableByPath") && runnable.path) {
542
+ else if ((runnable.type === "path" || runnable.type === "runnableByPath") &&
543
+ runnable.path) {
542
544
  const runType = runnable.runType ?? "script";
543
545
  requestBody.path =
544
546
  runType !== "hubscript"
@@ -7,7 +7,7 @@ import * as wmill from "../../../gen/services.gen.js";
7
7
  import { isSuperset } from "../../types.js";
8
8
  import { replaceInlineScripts, repopulateFields } from "./apps.js";
9
9
  import { createBundle, detectFrameworks } from "./bundle.js";
10
- import { mergeConfigWithConfigFile } from "../../core/conf.js";
10
+ import { APP_BACKEND_FOLDER } from "./app_metadata.js";
11
11
  const alreadySynced = [];
12
12
  async function collectAppFiles(localPath) {
13
13
  const files = {};
@@ -17,7 +17,10 @@ async function collectAppFiles(localPath) {
17
17
  const relativePath = basePath + entry.name;
18
18
  if (entry.isDirectory) {
19
19
  // Skip the runnables and node_modules subfolders
20
- if (entry.name === "runnables" || entry.name === "node_modules" || entry.name === "dist" || entry.name === ".claude") {
20
+ if (entry.name === APP_BACKEND_FOLDER ||
21
+ entry.name === "node_modules" ||
22
+ entry.name === "dist" ||
23
+ entry.name === ".claude") {
21
24
  continue;
22
25
  }
23
26
  await readDirRecursive(fullPath + SEP, relativePath + SEP);
@@ -66,7 +69,7 @@ export async function pushRawApp(workspace, remotePath, localPath, message) {
66
69
  }
67
70
  const path = localPath + "raw_app.yaml";
68
71
  const localApp = (await yamlParseFile(path));
69
- replaceInlineScripts(localApp.runnables, localPath + SEP + "runnables/");
72
+ replaceInlineScripts(localApp.runnables, localPath + SEP + APP_BACKEND_FOLDER + SEP, true);
70
73
  repopulateFields(localApp.runnables);
71
74
  await generatingPolicy(localApp, remotePath, localApp?.["public"] ?? false);
72
75
  const files = await collectAppFiles(localPath);
@@ -74,7 +77,7 @@ export async function pushRawApp(workspace, remotePath, localPath, message) {
74
77
  log.info(colors.yellow.bold(`Creating raw app ${remotePath} bundle...`));
75
78
  // Detect frameworks to determine entry point
76
79
  const frameworks = detectFrameworks(localPath);
77
- const entryFile = (frameworks.svelte || frameworks.vue) ? "index.ts" : "index.tsx";
80
+ const entryFile = frameworks.svelte || frameworks.vue ? "index.ts" : "index.tsx";
78
81
  const entryPoint = localPath + entryFile;
79
82
  return await createBundle({
80
83
  entryPoint: entryPoint,
@@ -146,61 +149,6 @@ export async function generatingPolicy(app, path, publicApp) {
146
149
  throw e;
147
150
  }
148
151
  }
149
- export async function generateLocksCommand(opts, appPath) {
150
- const { generateAppLocksInternal } = await import("./app_metadata.js");
151
- const { elementsToMap, FSFSElement } = await import("../sync/sync.js");
152
- const { ignoreF } = await import("../sync/sync.js");
153
- const { Confirm } = await import("../../../deps.js");
154
- if (appPath == "") {
155
- appPath = undefined;
156
- }
157
- const workspace = await resolveWorkspace(opts);
158
- await requireLogin(opts);
159
- opts = await mergeConfigWithConfigFile(opts);
160
- if (appPath) {
161
- // Generate metadata for a specific app
162
- await generateAppLocksInternal(appPath, false, workspace, opts, false, false);
163
- }
164
- else {
165
- // Generate metadata for all apps
166
- const ignore = await ignoreF(opts);
167
- const elems = await elementsToMap(await FSFSElement(dntShim.Deno.cwd(), [], true), (p, isD) => {
168
- return ignore(p, isD) || (!isD && !p.endsWith(SEP + "raw_app.yaml"));
169
- }, false, {});
170
- const appFolders = Object.keys(elems)
171
- .filter((p) => p.endsWith(SEP + "raw_app.yaml"))
172
- .map((p) => p.substring(0, p.length - (SEP + "raw_app.yaml").length));
173
- let hasAny = false;
174
- log.info("Checking metadata for all apps:");
175
- for (const appFolder of appFolders) {
176
- const candidate = await generateAppLocksInternal(appFolder, true, workspace, opts, false, true);
177
- if (candidate) {
178
- hasAny = true;
179
- log.info(colors.green(`+ ${candidate}`));
180
- }
181
- }
182
- if (hasAny) {
183
- if (opts.dryRun) {
184
- log.info(colors.gray(`Dry run complete.`));
185
- return;
186
- }
187
- if (!opts.yes &&
188
- !(await Confirm.prompt({
189
- message: "Update the metadata of the above apps?",
190
- default: true,
191
- }))) {
192
- return;
193
- }
194
- }
195
- else {
196
- log.info(colors.green.bold("No metadata to update"));
197
- return;
198
- }
199
- for (const appFolder of appFolders) {
200
- await generateAppLocksInternal(appFolder, false, workspace, opts, false, true);
201
- }
202
- }
203
- }
204
152
  async function pushRawAppCommand(opts, filePath, remotePath) {
205
153
  if (!validatePath(remotePath)) {
206
154
  return;
@@ -3,7 +3,7 @@ import { SEP, colors, log, path, yamlParseFile, yamlStringify, } from "../../../
3
3
  import { readLockfile, checkifMetadataUptodate, blueColor, clearGlobalLock, updateMetadataGlobalLock, LockfileGenerationError, getRawWorkspaceDependencies, } from "../../utils/metadata.js";
4
4
  import { extractInlineScripts as extractInlineScriptsForFlows } from "../../../windmill-utils-internal/src/inline-scripts/extractor.js";
5
5
  import { generateHash, getHeaders, writeIfChanged } from "../../utils/utils.js";
6
- import { exts, } from "../script/script.js";
6
+ 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";
@@ -60,7 +60,10 @@ export async function generateFlowLockInternal(folder, dryRun, workspace, opts,
60
60
  }
61
61
  }
62
62
  log.info(`Recomputing locks of ${changedScripts.join(", ")} in ${folder}`);
63
- await replaceInlineScripts(flowValue.value.modules, async (path) => await dntShim.Deno.readTextFile(folder + SEP + path), log, folder + SEP, SEP, changedScripts);
63
+ await replaceInlineScripts(flowValue.value.modules, async (path) => await dntShim.Deno.readTextFile(folder + SEP + path), log, folder + SEP, SEP, changedScripts
64
+ // (path: string, newPath: string) => Deno.renameSync(path, newPath),
65
+ // (path: string) => Deno.removeSync(path)
66
+ );
64
67
  //removeChangedLocks
65
68
  flowValue.value = await updateFlow(workspace, flowValue.value, remote_path, rawWorkspaceDependencies);
66
69
  const inlineScripts = extractInlineScriptsForFlows(flowValue.value.modules, {}, SEP, opts.defaultTs);