windmill-cli 1.588.0 → 1.589.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/esm/gen/core/OpenAPI.js +1 -1
  2. package/esm/gen/services.gen.js +15 -8
  3. package/esm/src/commands/app/app_metadata.js +224 -33
  4. package/esm/src/commands/app/apps.js +19 -9
  5. package/esm/src/commands/app/dev.js +10 -8
  6. package/esm/src/commands/app/raw_apps.js +7 -59
  7. package/esm/src/commands/flow/flow_metadata.js +5 -2
  8. package/esm/src/commands/sync/global.js +2 -2
  9. package/esm/src/commands/sync/sync.js +29 -10
  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/services.gen.d.ts +7 -3
  14. package/types/gen/services.gen.d.ts.map +1 -1
  15. package/types/gen/types.gen.d.ts +1 -2
  16. package/types/gen/types.gen.d.ts.map +1 -1
  17. package/types/src/commands/app/app_metadata.d.ts +8 -1
  18. package/types/src/commands/app/app_metadata.d.ts.map +1 -1
  19. package/types/src/commands/app/apps.d.ts +1 -1
  20. package/types/src/commands/app/apps.d.ts.map +1 -1
  21. package/types/src/commands/app/dev.d.ts.map +1 -1
  22. package/types/src/commands/app/raw_apps.d.ts +0 -7
  23. package/types/src/commands/app/raw_apps.d.ts.map +1 -1
  24. package/types/src/commands/flow/flow_metadata.d.ts.map +1 -1
  25. package/types/src/commands/sync/global.d.ts +1 -1
  26. package/types/src/commands/sync/global.d.ts.map +1 -1
  27. package/types/src/commands/sync/sync.d.ts.map +1 -1
  28. package/types/src/main.d.ts +1 -1
  29. package/types/src/main.d.ts.map +1 -1
  30. package/types/src/utils/utils.d.ts +1 -0
  31. package/types/src/utils/utils.d.ts.map +1 -1
  32. package/types/windmill-utils-internal/src/gen/types.gen.d.ts +1 -2
  33. 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.588.0',
35
+ VERSION: '1.589.1',
36
36
  WITH_CREDENTIALS: true,
37
37
  interceptors: {
38
38
  request: new Interceptors(),
@@ -4924,18 +4924,16 @@ export const listFlowPathsFromWorkspaceRunnable = (data) => {
4924
4924
  * @param data The data for the request.
4925
4925
  * @param data.workspace
4926
4926
  * @param data.version
4927
- * @param data.path
4928
4927
  * @returns Flow flow details
4929
4928
  * @throws ApiError
4930
4929
  */
4931
4930
  export const getFlowVersion = (data) => {
4932
4931
  return __request(OpenAPI, {
4933
4932
  method: 'GET',
4934
- url: '/w/{workspace}/flows/get/v/{version}/p/{path}',
4933
+ url: '/w/{workspace}/flows/get/v/{version}',
4935
4934
  path: {
4936
4935
  workspace: data.workspace,
4937
- version: data.version,
4938
- path: data.path
4936
+ version: data.version
4939
4937
  }
4940
4938
  });
4941
4939
  };
@@ -4944,7 +4942,6 @@ export const getFlowVersion = (data) => {
4944
4942
  * @param data The data for the request.
4945
4943
  * @param data.workspace
4946
4944
  * @param data.version
4947
- * @param data.path
4948
4945
  * @param data.requestBody Flow deployment message
4949
4946
  * @returns string success
4950
4947
  * @throws ApiError
@@ -4952,11 +4949,10 @@ export const getFlowVersion = (data) => {
4952
4949
  export const updateFlowHistory = (data) => {
4953
4950
  return __request(OpenAPI, {
4954
4951
  method: 'POST',
4955
- url: '/w/{workspace}/flows/history_update/v/{version}/p/{path}',
4952
+ url: '/w/{workspace}/flows/history_update/v/{version}',
4956
4953
  path: {
4957
4954
  workspace: data.workspace,
4958
- version: data.version,
4959
- path: data.path
4955
+ version: data.version
4960
4956
  },
4961
4957
  body: data.requestBody,
4962
4958
  mediaType: 'application/json'
@@ -10308,6 +10304,17 @@ export const listBlacklistedAgentTokens = (data = {}) => {
10308
10304
  }
10309
10305
  });
10310
10306
  };
10307
+ /**
10308
+ * get minimum worker version across all workers
10309
+ * @returns string minimum worker version
10310
+ * @throws ApiError
10311
+ */
10312
+ export const getMinVersion = () => {
10313
+ return __request(OpenAPI, {
10314
+ method: 'GET',
10315
+ url: '/agent_workers/get_min_version'
10316
+ });
10317
+ };
10311
10318
  /**
10312
10319
  * get granular acls
10313
10320
  * @param data The data for the request.
@@ -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
+ }
@@ -6,7 +6,7 @@ import * as wmill from "../../../gen/services.gen.js";
6
6
  import { isSuperset } from "../../types.js";
7
7
  import { readInlinePathSync } from "../../utils/utils.js";
8
8
  import devCommand from "./dev.js";
9
- import { isVersionsGeq15851 } from "../sync/global.js";
9
+ import { isVersionsGeq1585 } from "../sync/global.js";
10
10
  const alreadySynced = [];
11
11
  function respecializeFields(fields) {
12
12
  Object.entries(fields).forEach(([k, v]) => {
@@ -33,20 +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 (isVersionsGeq15851()) {
44
- rec["type"] = "path";
43
+ if (addType) {
44
+ if (isVersionsGeq1585()) {
45
+ rec["type"] = "path";
46
+ }
47
+ else {
48
+ rec["type"] = "runnableByPath";
49
+ }
45
50
  }
46
51
  }
47
52
  else if (k == "inlineScript" && typeof v == "object") {
48
- if (isVersionsGeq15851()) {
49
- rec["type"] = "inline";
53
+ if (addType) {
54
+ if (isVersionsGeq1585()) {
55
+ rec["type"] = "inline";
56
+ }
57
+ else {
58
+ rec["type"] = "runnableByName";
59
+ }
50
60
  }
51
61
  const o = v;
52
62
  if (o["content"] && o["content"].startsWith("!inline")) {
@@ -59,7 +69,7 @@ export function replaceInlineScripts(rec, localPath) {
59
69
  }
60
70
  }
61
71
  else {
62
- replaceInlineScripts(v, localPath);
72
+ replaceInlineScripts(v, localPath, addType);
63
73
  }
64
74
  });
65
75
  }
@@ -97,7 +107,7 @@ export async function pushApp(workspace, remotePath, localPath, message) {
97
107
  }
98
108
  const path = localPath + "app.yaml";
99
109
  const localApp = (await yamlParseFile(path));
100
- replaceInlineScripts(localApp.value, localPath);
110
+ replaceInlineScripts(localApp.value, localPath, true);
101
111
  await generatingPolicy(localApp, remotePath, localApp?.["public"] ??
102
112
  localApp?.["policy"]?.["execution_mode"] == "anonymous");
103
113
  if (app) {
@@ -186,7 +196,7 @@ const command = new Command()
186
196
  .option("--dry-run", "Perform a dry run without making changes")
187
197
  .option("--default-ts <runtime:string>", "Default TypeScript runtime (bun or deno)")
188
198
  .action(async (opts, appFolder) => {
189
- const { generateLocksCommand } = await import("./raw_apps.js");
199
+ const { generateLocksCommand } = await import("./app_metadata.js");
190
200
  await generateLocksCommand(opts, appFolder);
191
201
  });
192
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"