windmill-cli 1.623.1 → 1.625.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.
@@ -32,7 +32,7 @@ export const OpenAPI = {
32
32
  PASSWORD: undefined,
33
33
  TOKEN: getEnv("WM_TOKEN"),
34
34
  USERNAME: undefined,
35
- VERSION: '1.623.1',
35
+ VERSION: '1.625.0',
36
36
  WITH_CREDENTIALS: true,
37
37
  interceptors: {
38
38
  request: new Interceptors(),
@@ -2777,6 +2777,7 @@ export const listContextualVariables = (data) => {
2777
2777
  * get secondary storage names
2778
2778
  * @param data The data for the request.
2779
2779
  * @param data.workspace
2780
+ * @param data.includeDefault If true, include "_default_" in the list if primary workspace storage is set
2780
2781
  * @returns string status
2781
2782
  * @throws ApiError
2782
2783
  */
@@ -2786,6 +2787,9 @@ export const getSecondaryStorageNames = (data) => {
2786
2787
  url: '/w/{workspace}/workspaces/get_secondary_storage_names',
2787
2788
  path: {
2788
2789
  workspace: data.workspace
2790
+ },
2791
+ query: {
2792
+ include_default: data.includeDefault
2789
2793
  }
2790
2794
  });
2791
2795
  };
@@ -2866,6 +2870,25 @@ export const workspaceMuteCriticalAlertsUi = (data) => {
2866
2870
  mediaType: 'application/json'
2867
2871
  });
2868
2872
  };
2873
+ /**
2874
+ * Set public app rate limit for this workspace
2875
+ * @param data The data for the request.
2876
+ * @param data.workspace
2877
+ * @param data.requestBody Public app rate limit configuration
2878
+ * @returns string Successfully updated public app rate limit settings.
2879
+ * @throws ApiError
2880
+ */
2881
+ export const setPublicAppRateLimit = (data) => {
2882
+ return __request(OpenAPI, {
2883
+ method: 'POST',
2884
+ url: '/w/{workspace}/workspaces/public_app_rate_limit',
2885
+ path: {
2886
+ workspace: data.workspace
2887
+ },
2888
+ body: data.requestBody,
2889
+ mediaType: 'application/json'
2890
+ });
2891
+ };
2869
2892
  /**
2870
2893
  * login with oauth authorization flow
2871
2894
  * @param data The data for the request.
@@ -3700,6 +3723,22 @@ export const getHubAppById = (data) => {
3700
3723
  }
3701
3724
  });
3702
3725
  };
3726
+ /**
3727
+ * get hub raw app by id
3728
+ * @param data The data for the request.
3729
+ * @param data.id
3730
+ * @returns unknown raw app
3731
+ * @throws ApiError
3732
+ */
3733
+ export const getHubRawAppById = (data) => {
3734
+ return __request(OpenAPI, {
3735
+ method: 'GET',
3736
+ url: '/apps/hub/get_raw/{id}',
3737
+ path: {
3738
+ id: data.id
3739
+ }
3740
+ });
3741
+ };
3703
3742
  /**
3704
3743
  * get public app by custom path
3705
3744
  * @param data The data for the request.
@@ -12395,10 +12434,16 @@ export const clearIndex = (data) => {
12395
12434
  });
12396
12435
  };
12397
12436
  /**
12398
- * List all assets in the workspace
12437
+ * List all assets in the workspace with cursor pagination
12399
12438
  * @param data The data for the request.
12400
12439
  * @param data.workspace
12401
- * @returns unknown all assets in the workspace
12440
+ * @param data.perPage Number of items per page (max 1000, default 50)
12441
+ * @param data.cursorCreatedAt Cursor timestamp for pagination (created_at of last item from previous page)
12442
+ * @param data.cursorId Cursor ID for pagination (id of last item from previous page)
12443
+ * @param data.assetPath Filter by asset path (case-insensitive partial match)
12444
+ * @param data.usagePath Filter by usage path (case-insensitive partial match)
12445
+ * @param data.assetKinds Filter by asset kinds (multiple values allowed)
12446
+ * @returns unknown paginated assets in the workspace
12402
12447
  * @throws ApiError
12403
12448
  */
12404
12449
  export const listAssets = (data) => {
@@ -12407,6 +12452,14 @@ export const listAssets = (data) => {
12407
12452
  url: '/w/{workspace}/assets/list',
12408
12453
  path: {
12409
12454
  workspace: data.workspace
12455
+ },
12456
+ query: {
12457
+ per_page: data.perPage,
12458
+ cursor_created_at: data.cursorCreatedAt,
12459
+ cursor_id: data.cursorId,
12460
+ asset_path: data.assetPath,
12461
+ usage_path: data.usagePath,
12462
+ asset_kinds: data.assetKinds
12410
12463
  }
12411
12464
  });
12412
12465
  };
@@ -12429,6 +12482,22 @@ export const listAssetsByUsage = (data) => {
12429
12482
  mediaType: 'application/json'
12430
12483
  });
12431
12484
  };
12485
+ /**
12486
+ * List all favorite assets for the authenticated user
12487
+ * @param data The data for the request.
12488
+ * @param data.workspace
12489
+ * @returns unknown list of favorite assets
12490
+ * @throws ApiError
12491
+ */
12492
+ export const listFavoriteAssets = (data) => {
12493
+ return __request(OpenAPI, {
12494
+ method: 'GET',
12495
+ url: '/w/{workspace}/assets/list_favorites',
12496
+ path: {
12497
+ workspace: data.workspace
12498
+ }
12499
+ });
12500
+ };
12432
12501
  /**
12433
12502
  * list available MCP tools
12434
12503
  * @param data The data for the request.
@@ -190,6 +190,7 @@ const createHTML = (jsPath, cssPath) => `
190
190
  // SQL Migration Modal handling
191
191
  let pendingSqlMigration = null;
192
192
  let sqlWebSocket = null;
193
+ let closeModalTimeout = null;
193
194
 
194
195
  function initSqlWebSocket() {
195
196
  const wsProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
@@ -211,12 +212,19 @@ const createHTML = (jsPath, cssPath) => `
211
212
  }
212
213
 
213
214
  function showSqlModal(data) {
215
+ // Cancel any pending close from a previous migration's success
216
+ if (closeModalTimeout) {
217
+ clearTimeout(closeModalTimeout);
218
+ closeModalTimeout = null;
219
+ }
214
220
  pendingSqlMigration = data;
215
221
  document.getElementById('sql-file-name').textContent = data.fileName;
216
222
  document.getElementById('sql-datatable').textContent = data.datatable || 'Not configured';
217
223
  document.getElementById('sql-content').textContent = data.sql;
218
224
  document.getElementById('sql-result').innerHTML = '';
225
+ document.getElementById('sql-result').className = 'sql-result';
219
226
  document.getElementById('apply-sql-btn').disabled = !data.datatable;
227
+ document.getElementById('apply-sql-btn').textContent = 'Apply SQL';
220
228
  document.getElementById('sql-modal-overlay').classList.add('visible');
221
229
  }
222
230
 
@@ -257,7 +265,11 @@ const createHTML = (jsPath, cssPath) => `
257
265
  resultDiv.className = 'sql-result success';
258
266
  resultDiv.innerHTML = '<strong>Success!</strong> SQL applied and file deleted.';
259
267
  // Auto-close after success (don't send skip since server already handled it)
260
- setTimeout(() => closeSqlModal(false), 1500);
268
+ // Save timeout ID so it can be cancelled if a new migration arrives
269
+ closeModalTimeout = setTimeout(() => {
270
+ closeModalTimeout = null;
271
+ closeSqlModal(false);
272
+ }, 1500);
261
273
  }
262
274
  }
263
275
 
@@ -1044,7 +1044,7 @@ export async function* readDirRecursiveWithIgnore(ignore, root) {
1044
1044
  }
1045
1045
  }
1046
1046
  }
1047
- export async function elementsToMap(els, ignore, json, skips, specificItems, branchOverride) {
1047
+ export async function elementsToMap(els, ignore, json, skips, specificItems, branchOverride, isRemote) {
1048
1048
  const map = {};
1049
1049
  const processedBasePaths = new Set();
1050
1050
  // Cache git branch at the start to avoid repeated execSync calls per file
@@ -1209,7 +1209,8 @@ export async function elementsToMap(els, ignore, json, skips, specificItems, bra
1209
1209
  continue;
1210
1210
  }
1211
1211
  // Skip base file if it's configured as branch-specific (expect branch version)
1212
- if (isSpecificItem(path, specificItems)) {
1212
+ // Only for LOCAL files - remote workspace only has base paths
1213
+ if (!isRemote && isSpecificItem(path, specificItems)) {
1213
1214
  continue;
1214
1215
  }
1215
1216
  map[path] = content;
@@ -1218,13 +1219,13 @@ export async function elementsToMap(els, ignore, json, skips, specificItems, bra
1218
1219
  }
1219
1220
  return map;
1220
1221
  }
1221
- async function compareDynFSElement(els1, els2, ignore, json, skips, ignoreMetadataDeletion, codebases, ignoreCodebaseChanges, specificItems, branchOverride) {
1222
+ async function compareDynFSElement(els1, els2, ignore, json, skips, ignoreMetadataDeletion, codebases, ignoreCodebaseChanges, specificItems, branchOverride, isEls1Remote) {
1222
1223
  const [m1, m2] = els2
1223
1224
  ? await Promise.all([
1224
- elementsToMap(els1, ignore, json, skips, specificItems, branchOverride),
1225
- elementsToMap(els2, ignore, json, skips, specificItems, branchOverride),
1225
+ elementsToMap(els1, ignore, json, skips, specificItems, branchOverride, isEls1Remote),
1226
+ elementsToMap(els2, ignore, json, skips, specificItems, branchOverride, !isEls1Remote),
1226
1227
  ])
1227
- : [await elementsToMap(els1, ignore, json, skips, specificItems, branchOverride), {}];
1228
+ : [await elementsToMap(els1, ignore, json, skips, specificItems, branchOverride, isEls1Remote), {}];
1228
1229
  const changes = [];
1229
1230
  function parseYaml(k, v) {
1230
1231
  if (k.endsWith(".script.yaml")) {
@@ -1633,7 +1634,7 @@ export async function pull(opts) {
1633
1634
  const local = !opts.stateful
1634
1635
  ? await FSFSElement(dntShim.Deno.cwd(), codebases, true)
1635
1636
  : await FSFSElement(path.join(dntShim.Deno.cwd(), ".wmill"), [], true);
1636
- const changes = await compareDynFSElement(remote, local, await ignoreF(opts), opts.json ?? false, opts, false, codebases, true, specificItems, opts.branch);
1637
+ const changes = await compareDynFSElement(remote, local, await ignoreF(opts), opts.json ?? false, opts, false, codebases, true, specificItems, opts.branch, true);
1637
1638
  log.info(`remote (${workspace.name}) -> local: ${changes.length} changes to apply`);
1638
1639
  // Handle JSON output for dry-run
1639
1640
  if (opts.dryRun && opts.jsonOutput) {
@@ -1943,7 +1944,7 @@ export async function push(opts) {
1943
1944
  }
1944
1945
  const remote = ZipFSElement((await downloadZip(workspace, opts.plainSecrets, opts.skipVariables, opts.skipResources, opts.skipResourceTypes, opts.skipSecrets, opts.includeSchedules, opts.includeTriggers, opts.includeUsers, opts.includeGroups, opts.includeSettings, opts.includeKey, opts.skipWorkspaceDependencies, opts.defaultTs)), !opts.json, opts.defaultTs ?? "bun", resourceTypeToFormatExtension, false);
1945
1946
  const local = await FSFSElement(path.join(dntShim.Deno.cwd(), ""), codebases, false);
1946
- const changes = await compareDynFSElement(local, remote, await ignoreF(opts), opts.json ?? false, opts, true, codebases, false, specificItems, opts.branch);
1947
+ const changes = await compareDynFSElement(local, remote, await ignoreF(opts), opts.json ?? false, opts, true, codebases, false, specificItems, opts.branch, false);
1947
1948
  const rawWorkspaceDependencies = await getRawWorkspaceDependencies();
1948
1949
  const tracker = await buildTracker(changes);
1949
1950
  const staleScripts = [];
package/esm/src/main.js CHANGED
@@ -46,7 +46,7 @@ export { flow, app, script, workspace, resource, resourceType, user, variable, h
46
46
  // console.error(JSON.stringify(event.error, null, 4));
47
47
  // }
48
48
  // });
49
- export const VERSION = "1.623.1";
49
+ export const VERSION = "1.625.0";
50
50
  // Re-exported from constants.ts to maintain backwards compatibility
51
51
  export { WM_FORK_PREFIX } from "./core/constants.js";
52
52
  const command = new Command()
@@ -137,19 +137,87 @@ export async function updateScriptSchema(scriptContent, language, metadataConten
137
137
  delete metadataContent.no_main_func;
138
138
  }
139
139
  }
140
- async function updateScriptLock(workspace, scriptContent, language, remotePath, metadataContent, rawWorkspaceDependencies) {
141
- if (!(workspaceDependenciesLanguages.some((l) => l.language == language) ||
142
- language == "deno" ||
143
- language == "rust" ||
144
- language == "ansible")) {
145
- return;
140
+ const LANG_ANNOTATION_CONFIG = {
141
+ python3: { comment: "#", keyword: "requirements", validityRe: /^#\s?(\S+)\s*$/ },
142
+ bun: { comment: "//", keyword: "package_json" },
143
+ nativets: { comment: "//", keyword: "package_json" },
144
+ go: { comment: "//", keyword: "go_mod" },
145
+ php: { comment: "//", keyword: "composer_json" },
146
+ };
147
+ export function extractWorkspaceDepsAnnotation(scriptContent, language) {
148
+ const config = LANG_ANNOTATION_CONFIG[language];
149
+ if (!config)
150
+ return null;
151
+ const { comment, keyword, validityRe } = config;
152
+ const extraMarker = `extra_${keyword}:`;
153
+ const manualMarker = `${keyword}:`;
154
+ const lines = scriptContent.split("\n");
155
+ // Find first annotation line (mirrors Rust find_position)
156
+ let pos = -1;
157
+ for (let i = 0; i < lines.length; i++) {
158
+ const l = lines[i];
159
+ if (l.startsWith(comment) && (l.includes(extraMarker) || l.includes(manualMarker))) {
160
+ pos = i;
161
+ break;
162
+ }
146
163
  }
147
- if (Object.keys(rawWorkspaceDependencies).length > 0) {
148
- const dependencyPaths = Object.keys(rawWorkspaceDependencies).join(', ');
149
- log.info(`Generating script lock for ${remotePath} with raw workspace dependencies: ${dependencyPaths}`);
164
+ if (pos === -1)
165
+ return null;
166
+ const annotationLine = lines[pos];
167
+ const mode = annotationLine.includes(extraMarker) ? "extra" : "manual";
168
+ // Parse external references from the annotation line
169
+ const marker = mode === "extra" ? extraMarker : manualMarker;
170
+ const unparsed = annotationLine.replaceAll(marker, "").replaceAll(comment, "");
171
+ const external = unparsed
172
+ .split(",")
173
+ .map((s) => s.trim())
174
+ .filter((s) => s.length > 0);
175
+ // Parse inline deps from subsequent lines
176
+ const inlineParts = [];
177
+ for (let i = pos + 1; i < lines.length; i++) {
178
+ const l = lines[i];
179
+ if (validityRe) {
180
+ const match = validityRe.exec(l);
181
+ if (match && match[1]) {
182
+ inlineParts.push(match[1]);
183
+ }
184
+ else {
185
+ break;
186
+ }
187
+ }
188
+ else {
189
+ if (!l.startsWith(comment)) {
190
+ break;
191
+ }
192
+ inlineParts.push(l.substring(comment.length));
193
+ }
194
+ }
195
+ const inlineStr = inlineParts.join("\n");
196
+ const inline = inlineStr.trim().length > 0 ? inlineStr : null;
197
+ return { mode, external, inline };
198
+ }
199
+ export async function computeLockCacheKey(scriptContent, language, rawWorkspaceDependencies) {
200
+ const annotation = extractWorkspaceDepsAnnotation(scriptContent, language);
201
+ const annotationStr = annotation
202
+ ? `${annotation.mode}|${annotation.external.join(",")}|${annotation.inline ?? ""}`
203
+ : "none";
204
+ const sortedDepsKeys = Object.keys(rawWorkspaceDependencies).sort();
205
+ const depsStr = sortedDepsKeys.map((k) => `${k}=${rawWorkspaceDependencies[k]}`).join(";");
206
+ return await generateHash(`${language}|${annotationStr}|${depsStr}`);
207
+ }
208
+ const lockCache = new Map();
209
+ export function clearLockCache() {
210
+ lockCache.clear();
211
+ }
212
+ async function fetchScriptLock(workspace, scriptContent, language, remotePath, rawWorkspaceDependencies) {
213
+ const hasRawDeps = Object.keys(rawWorkspaceDependencies).length > 0;
214
+ const cacheKey = hasRawDeps
215
+ ? await computeLockCacheKey(scriptContent, language, rawWorkspaceDependencies)
216
+ : undefined;
217
+ if (cacheKey && lockCache.has(cacheKey)) {
218
+ log.info(`Using cached lockfile for ${remotePath}`);
219
+ return lockCache.get(cacheKey);
150
220
  }
151
- // generate the script lock running a dependency job in Windmill and update it inplace
152
- // TODO: update this once the client is released
153
221
  const extraHeaders = getHeaders();
154
222
  const rawResponse = await fetch(`${workspace.remote}api/w/${workspace.workspaceId}/jobs/run/dependencies`, {
155
223
  method: "POST",
@@ -182,22 +250,10 @@ async function updateScriptLock(workspace, scriptContent, language, remotePath,
182
250
  }
183
251
  throw new LockfileGenerationError(`Failed to generate lockfile: ${JSON.stringify(response, null, 2)}`);
184
252
  }
185
- const lockPath = remotePath + ".script.lock";
186
- if (lock != "") {
187
- await dntShim.Deno.writeTextFile(lockPath, lock);
188
- metadataContent.lock = "!inline " + lockPath.replaceAll(SEP, "/");
189
- }
190
- else {
191
- try {
192
- if (await dntShim.Deno.stat(lockPath)) {
193
- await dntShim.Deno.remove(lockPath);
194
- }
195
- }
196
- catch (e) {
197
- log.info(colors.yellow(`Error removing lock file ${lockPath}: ${e}`));
198
- }
199
- metadataContent.lock = "";
253
+ if (cacheKey) {
254
+ lockCache.set(cacheKey, lock);
200
255
  }
256
+ return lock;
201
257
  }
202
258
  catch (e) {
203
259
  if (e instanceof LockfileGenerationError) {
@@ -206,6 +262,35 @@ async function updateScriptLock(workspace, scriptContent, language, remotePath,
206
262
  throw new LockfileGenerationError(`Failed to generate lockfile:${rawResponse.statusText}, ${responseText}, ${e}`);
207
263
  }
208
264
  }
265
+ async function updateScriptLock(workspace, scriptContent, language, remotePath, metadataContent, rawWorkspaceDependencies) {
266
+ if (!(workspaceDependenciesLanguages.some((l) => l.language == language) ||
267
+ language == "deno" ||
268
+ language == "rust" ||
269
+ language == "ansible")) {
270
+ return;
271
+ }
272
+ if (Object.keys(rawWorkspaceDependencies).length > 0) {
273
+ const dependencyPaths = Object.keys(rawWorkspaceDependencies).join(', ');
274
+ log.info(`Generating script lock for ${remotePath} with raw workspace dependencies: ${dependencyPaths}`);
275
+ }
276
+ const lock = await fetchScriptLock(workspace, scriptContent, language, remotePath, rawWorkspaceDependencies);
277
+ const lockPath = remotePath + ".script.lock";
278
+ if (lock != "") {
279
+ await dntShim.Deno.writeTextFile(lockPath, lock);
280
+ metadataContent.lock = "!inline " + lockPath.replaceAll(SEP, "/");
281
+ }
282
+ else {
283
+ try {
284
+ if (await dntShim.Deno.stat(lockPath)) {
285
+ await dntShim.Deno.remove(lockPath);
286
+ }
287
+ }
288
+ catch (e) {
289
+ log.info(colors.yellow(`Error removing lock file ${lockPath}: ${e}`));
290
+ }
291
+ metadataContent.lock = "";
292
+ }
293
+ }
209
294
  ////////////////////////////////////////////////////////////////////////////////////////////
210
295
  // below functions copied from Windmill's FE inferArgs function. TODO: refactor //
211
296
  ////////////////////////////////////////////////////////////////////////////////////////////
@@ -1,4 +1,18 @@
1
1
  import { newPathAssigner } from "../path-utils/path-assigner.js";
2
+ function extractRawscriptInline(id, summary, rawscript, mapping, separator, assigner) {
3
+ const [basePath, ext] = assigner.assignPath(summary ?? id, rawscript.language);
4
+ const path = mapping[id] ?? basePath + ext;
5
+ const content = rawscript.content;
6
+ const r = [{ path: path, content: content }];
7
+ rawscript.content = "!inline " + path.replaceAll(separator, "/");
8
+ const lock = rawscript.lock;
9
+ if (lock && lock != "") {
10
+ const lockPath = basePath + "lock";
11
+ rawscript.lock = "!inline " + lockPath.replaceAll(separator, "/");
12
+ r.push({ path: lockPath, content: lock });
13
+ }
14
+ return r;
15
+ }
2
16
  /**
3
17
  * Extracts inline scripts from flow modules, converting them to separate files
4
18
  * and replacing the original content with file references.
@@ -16,18 +30,7 @@ export function extractInlineScripts(modules, mapping = {}, separator = "/", def
16
30
  const assigner = pathAssigner ?? newPathAssigner(defaultTs ?? "bun", { skipInlineScriptSuffix: options?.skipInlineScriptSuffix });
17
31
  return modules.flatMap((m) => {
18
32
  if (m.value.type == "rawscript") {
19
- const [basePath, ext] = assigner.assignPath(m.summary, m.value.language);
20
- const path = mapping[m.id] ?? basePath + ext;
21
- const content = m.value.content;
22
- const r = [{ path: path, content: content }];
23
- m.value.content = "!inline " + path.replaceAll(separator, "/");
24
- const lock = m.value.lock;
25
- if (lock && lock != "") {
26
- const lockPath = basePath + "lock";
27
- m.value.lock = "!inline " + lockPath.replaceAll(separator, "/");
28
- r.push({ path: lockPath, content: lock });
29
- }
30
- return r;
33
+ return extractRawscriptInline(m.id, m.summary, m.value, mapping, separator, assigner);
31
34
  }
32
35
  else if (m.value.type == "forloopflow") {
33
36
  return extractInlineScripts(m.value.modules, mapping, separator, defaultTs, assigner);
@@ -44,6 +47,16 @@ export function extractInlineScripts(modules, mapping = {}, separator = "/", def
44
47
  ...extractInlineScripts(m.value.default, mapping, separator, defaultTs, assigner),
45
48
  ];
46
49
  }
50
+ else if (m.value.type == "aiagent") {
51
+ return (m.value.tools ?? []).flatMap((tool) => {
52
+ const toolValue = tool.value;
53
+ // Only process flowmodule tools with rawscript type
54
+ if (!toolValue || toolValue.tool_type !== 'flowmodule' || toolValue.type !== 'rawscript') {
55
+ return [];
56
+ }
57
+ return extractRawscriptInline(tool.id, tool.summary, toolValue, mapping, separator, assigner);
58
+ });
59
+ }
47
60
  else {
48
61
  return [];
49
62
  }
@@ -81,6 +94,15 @@ export function extractCurrentMapping(modules, mapping = {}) {
81
94
  m.value.branches.forEach((b) => extractCurrentMapping(b.modules, mapping));
82
95
  extractCurrentMapping(m.value.default, mapping);
83
96
  }
97
+ else if (m.value.type === "aiagent") {
98
+ (m.value.tools ?? []).forEach((tool) => {
99
+ const toolValue = tool.value;
100
+ if (!toolValue || toolValue.tool_type !== 'flowmodule' || toolValue.type !== 'rawscript' || !toolValue.content || !toolValue.content.startsWith("!inline")) {
101
+ return;
102
+ }
103
+ mapping[tool.id] = toolValue.content.trim().split(" ")[1];
104
+ });
105
+ }
84
106
  });
85
107
  return mapping;
86
108
  }
@@ -1,3 +1,39 @@
1
+ async function replaceRawscriptInline(id, rawscript, fileReader, logger, separator, removeLocks) {
2
+ if (!rawscript.content || !rawscript.content.startsWith("!inline")) {
3
+ return;
4
+ }
5
+ const path = rawscript.content.split(" ")[1];
6
+ const pathSuffix = path.split(".").slice(1).join(".");
7
+ const newPath = id + "." + pathSuffix;
8
+ try {
9
+ rawscript.content = await fileReader(path);
10
+ }
11
+ catch {
12
+ logger.error(`Script file ${path} not found`);
13
+ try {
14
+ rawscript.content = await fileReader(newPath);
15
+ }
16
+ catch {
17
+ logger.error(`Script file ${newPath} not found`);
18
+ }
19
+ }
20
+ const lock = rawscript.lock;
21
+ if (removeLocks && removeLocks.includes(path)) {
22
+ rawscript.lock = undefined;
23
+ }
24
+ else if (lock &&
25
+ typeof lock === "string" &&
26
+ lock.trimStart().startsWith("!inline ")) {
27
+ const lockPath = lock.split(" ")[1];
28
+ try {
29
+ rawscript.lock = await fileReader(lockPath.replaceAll("/", separator));
30
+ }
31
+ catch {
32
+ logger.error(`Lock file ${lockPath} not found, treating as empty`);
33
+ rawscript.lock = "";
34
+ }
35
+ }
36
+ }
1
37
  /**
2
38
  * Replaces inline script references with actual file content from the filesystem.
3
39
  * This function recursively processes all flow modules and their nested structures.
@@ -17,63 +53,8 @@ export async function replaceInlineScripts(modules, fileReader, logger = {
17
53
  if (!module.value) {
18
54
  throw new Error(`Module value is undefined for module ${module.id}`);
19
55
  }
20
- if (module.value.type === "rawscript" && module.value.content && module.value.content.startsWith("!inline")) {
21
- const path = module.value.content.split(" ")[1];
22
- // const pathPrefix = path.split(".")[0];
23
- const pathSuffix = path.split(".").slice(1).join(".");
24
- // new path is the module id with the same suffix
25
- const newPath = module.id + "." + pathSuffix;
26
- try {
27
- module.value.content = await fileReader(path);
28
- }
29
- catch {
30
- logger.error(`Script file ${path} not found`);
31
- // try new path
32
- try {
33
- module.value.content = await fileReader(newPath);
34
- }
35
- catch {
36
- logger.error(`Script file ${newPath} not found`);
37
- }
38
- }
39
- // rename the file if the prefix is different from the module id (fix old naming)
40
- // if (pathPrefix != module.id && renamer) {
41
- // logger.info(`Renaming ${path} to ${module.id}.${pathSuffix}`);
42
- // try {
43
- // renamer(localPath + path, localPath + module.id + "." + pathSuffix);
44
- // } catch {
45
- // logger.info(`Failed to rename ${path} to ${module.id}.${pathSuffix}`);
46
- // }
47
- // }
48
- const lock = module.value.lock;
49
- if (removeLocks && removeLocks.includes(path)) {
50
- module.value.lock = undefined;
51
- // delete the file if the prefix is different from the module id (fix old naming)
52
- // if (lock && lock != "") {
53
- // const path = lock.split(" ")[1];
54
- // const pathPrefix = path.split(".")[0];
55
- // if (pathPrefix != module.id && deleter) {
56
- // logger.info(`Deleting ${path}`);
57
- // try {
58
- // deleter(localPath + path);
59
- // } catch {
60
- // logger.error(`Failed to delete ${path}`);
61
- // }
62
- // }
63
- // }
64
- }
65
- else if (lock &&
66
- typeof lock == "string" &&
67
- lock.trimStart().startsWith("!inline ")) {
68
- const path = lock.split(" ")[1];
69
- try {
70
- module.value.lock = await fileReader(path.replaceAll("/", separator));
71
- }
72
- catch {
73
- logger.error(`Lock file ${path} not found, treating as empty`);
74
- module.value.lock = "";
75
- }
76
- }
56
+ if (module.value.type === "rawscript") {
57
+ await replaceRawscriptInline(module.id, module.value, fileReader, logger, separator, removeLocks);
77
58
  }
78
59
  else if (module.value.type === "forloopflow" || module.value.type === "whileloopflow") {
79
60
  await replaceInlineScripts(module.value.modules, fileReader, logger, localPath, separator, removeLocks);
@@ -89,5 +70,16 @@ export async function replaceInlineScripts(modules, fileReader, logger = {
89
70
  }));
90
71
  await replaceInlineScripts(module.value.default, fileReader, logger, localPath, separator, removeLocks);
91
72
  }
73
+ else if (module.value.type === "aiagent") {
74
+ await Promise.all((module.value.tools ?? []).map(async (tool) => {
75
+ const toolValue = tool.value;
76
+ if (!toolValue ||
77
+ toolValue.tool_type !== "flowmodule" ||
78
+ toolValue.type !== "rawscript") {
79
+ return;
80
+ }
81
+ await replaceRawscriptInline(tool.id, toolValue, fileReader, logger, separator, removeLocks);
82
+ }));
83
+ }
92
84
  }));
93
85
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "windmill-cli",
3
- "version": "1.623.1",
3
+ "version": "1.625.0",
4
4
  "description": "CLI for Windmill",
5
5
  "repository": {
6
6
  "type": "git",