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.
- package/esm/gen/core/OpenAPI.js +1 -1
- package/esm/gen/services.gen.js +71 -2
- package/esm/src/commands/app/dev.js +13 -1
- package/esm/src/commands/sync/sync.js +9 -8
- package/esm/src/main.js +1 -1
- package/esm/src/utils/metadata.js +111 -26
- package/esm/windmill-utils-internal/src/inline-scripts/extractor.js +34 -12
- package/esm/windmill-utils-internal/src/inline-scripts/replacer.js +49 -57
- package/package.json +1 -1
- package/types/gen/services.gen.d.ts +35 -3
- package/types/gen/services.gen.d.ts.map +1 -1
- package/types/gen/types.gen.d.ts +136 -20
- package/types/gen/types.gen.d.ts.map +1 -1
- package/types/src/commands/app/dev.d.ts.map +1 -1
- package/types/src/commands/sync/sync.d.ts +1 -1
- package/types/src/commands/sync/sync.d.ts.map +1 -1
- package/types/src/main.d.ts +1 -1
- package/types/src/utils/metadata.d.ts +9 -0
- package/types/src/utils/metadata.d.ts.map +1 -1
- package/types/windmill-utils-internal/src/gen/types.gen.d.ts +136 -20
- package/types/windmill-utils-internal/src/gen/types.gen.d.ts.map +1 -1
- package/types/windmill-utils-internal/src/inline-scripts/extractor.d.ts.map +1 -1
- package/types/windmill-utils-internal/src/inline-scripts/replacer.d.ts.map +1 -1
package/esm/gen/core/OpenAPI.js
CHANGED
package/esm/gen/services.gen.js
CHANGED
|
@@ -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
|
-
* @
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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 (
|
|
148
|
-
|
|
149
|
-
|
|
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
|
-
|
|
186
|
-
|
|
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
|
-
|
|
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"
|
|
21
|
-
|
|
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
|
}
|