windmill-cli 1.662.0 → 1.663.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/main.js +668 -175
- package/package.json +3 -2
package/esm/main.js
CHANGED
|
@@ -11785,7 +11785,7 @@ var init_OpenAPI = __esm(() => {
|
|
|
11785
11785
|
PASSWORD: undefined,
|
|
11786
11786
|
TOKEN: getEnv2("WM_TOKEN"),
|
|
11787
11787
|
USERNAME: undefined,
|
|
11788
|
-
VERSION: "1.
|
|
11788
|
+
VERSION: "1.663.0",
|
|
11789
11789
|
WITH_CREDENTIALS: true,
|
|
11790
11790
|
interceptors: {
|
|
11791
11791
|
request: new Interceptors,
|
|
@@ -12114,6 +12114,7 @@ __export(exports_services_gen, {
|
|
|
12114
12114
|
testCriticalChannels: () => testCriticalChannels,
|
|
12115
12115
|
syncNativeTriggers: () => syncNativeTriggers,
|
|
12116
12116
|
submitOnboardingData: () => submitOnboardingData,
|
|
12117
|
+
storeRawScriptTemp: () => storeRawScriptTemp,
|
|
12117
12118
|
startMcpOauthPopup: () => startMcpOauthPopup,
|
|
12118
12119
|
star: () => star,
|
|
12119
12120
|
signS3Objects: () => signS3Objects,
|
|
@@ -12563,6 +12564,7 @@ __export(exports_services_gen, {
|
|
|
12563
12564
|
disconnectTeams: () => disconnectTeams,
|
|
12564
12565
|
disconnectSlack: () => disconnectSlack,
|
|
12565
12566
|
disconnectAccount: () => disconnectAccount,
|
|
12567
|
+
diffRawScriptsWithDeployed: () => diffRawScriptsWithDeployed,
|
|
12566
12568
|
deleteWorkspaceSlackOauthConfig: () => deleteWorkspaceSlackOauthConfig,
|
|
12567
12569
|
deleteWorkspaceDependencies: () => deleteWorkspaceDependencies,
|
|
12568
12570
|
deleteWorkspace: () => deleteWorkspace,
|
|
@@ -15125,6 +15127,26 @@ var backendVersion = () => {
|
|
|
15125
15127
|
hash: data2.hash
|
|
15126
15128
|
}
|
|
15127
15129
|
});
|
|
15130
|
+
}, storeRawScriptTemp = (data2) => {
|
|
15131
|
+
return request(OpenAPI, {
|
|
15132
|
+
method: "POST",
|
|
15133
|
+
url: "/w/{workspace}/scripts/raw_temp/store",
|
|
15134
|
+
path: {
|
|
15135
|
+
workspace: data2.workspace
|
|
15136
|
+
},
|
|
15137
|
+
body: data2.requestBody,
|
|
15138
|
+
mediaType: "application/json"
|
|
15139
|
+
});
|
|
15140
|
+
}, diffRawScriptsWithDeployed = (data2) => {
|
|
15141
|
+
return request(OpenAPI, {
|
|
15142
|
+
method: "POST",
|
|
15143
|
+
url: "/w/{workspace}/scripts/raw_temp/diff",
|
|
15144
|
+
path: {
|
|
15145
|
+
workspace: data2.workspace
|
|
15146
|
+
},
|
|
15147
|
+
body: data2.requestBody,
|
|
15148
|
+
mediaType: "application/json"
|
|
15149
|
+
});
|
|
15128
15150
|
}, listSelectedJobGroups = (data2) => {
|
|
15129
15151
|
return request(OpenAPI, {
|
|
15130
15152
|
method: "POST",
|
|
@@ -24269,6 +24291,15 @@ function isAppPath(p) {
|
|
|
24269
24291
|
function isRawAppPath(p) {
|
|
24270
24292
|
return normalizeSep(p).includes(getFolderSuffixes().raw_app + "/");
|
|
24271
24293
|
}
|
|
24294
|
+
function isFolderResourcePathAnyFormat(p) {
|
|
24295
|
+
const n = normalizeSep(p);
|
|
24296
|
+
for (const suffixes of [DOTTED_SUFFIXES, NON_DOTTED_SUFFIXES]) {
|
|
24297
|
+
if (n.includes(suffixes.flow + "/") || n.includes(suffixes.app + "/") || n.includes(suffixes.raw_app + "/")) {
|
|
24298
|
+
return true;
|
|
24299
|
+
}
|
|
24300
|
+
}
|
|
24301
|
+
return false;
|
|
24302
|
+
}
|
|
24272
24303
|
function isRawAppBackendPath(filePath) {
|
|
24273
24304
|
const suffixes = getFolderSuffixes();
|
|
24274
24305
|
const normalizedPath = filePath.replaceAll(SEP2, "/");
|
|
@@ -58891,7 +58922,7 @@ async function push(opts, filePath) {
|
|
|
58891
58922
|
}
|
|
58892
58923
|
await requireLogin(opts);
|
|
58893
58924
|
const codebases = await listSyncCodebases(opts);
|
|
58894
|
-
await handleFile(filePath, workspace, [], undefined, opts, await getRawWorkspaceDependencies(), codebases);
|
|
58925
|
+
await handleFile(filePath, workspace, [], undefined, opts, await getRawWorkspaceDependencies(true), codebases);
|
|
58895
58926
|
info(colors.bold.underline.green(`Script ${filePath} pushed`));
|
|
58896
58927
|
}
|
|
58897
58928
|
async function findResourceFile(path6) {
|
|
@@ -59489,7 +59520,7 @@ async function generateMetadata(opts, scriptPath) {
|
|
|
59489
59520
|
await requireLogin(opts);
|
|
59490
59521
|
opts = await mergeConfigWithConfigFile(opts);
|
|
59491
59522
|
const codebases = await listSyncCodebases(opts);
|
|
59492
|
-
const rawWorkspaceDependencies = await getRawWorkspaceDependencies();
|
|
59523
|
+
const rawWorkspaceDependencies = await getRawWorkspaceDependencies(true);
|
|
59493
59524
|
if (scriptPath) {
|
|
59494
59525
|
await generateScriptMetadataInternal(scriptPath, workspace, opts, false, false, rawWorkspaceDependencies, codebases, false);
|
|
59495
59526
|
} else {
|
|
@@ -60772,6 +60803,33 @@ function collectPathScriptPaths(flowValue) {
|
|
|
60772
60803
|
return [...paths];
|
|
60773
60804
|
}
|
|
60774
60805
|
|
|
60806
|
+
// src/utils/relative_imports.ts
|
|
60807
|
+
async function extractRelativeImports(code2, scriptPath, language) {
|
|
60808
|
+
try {
|
|
60809
|
+
switch (language) {
|
|
60810
|
+
case "bun":
|
|
60811
|
+
case "nativets":
|
|
60812
|
+
case "deno": {
|
|
60813
|
+
const { parse_ts_relative_imports } = await loadParser("windmill-parser-wasm-ts");
|
|
60814
|
+
return parse_ts_relative_imports(code2, scriptPath);
|
|
60815
|
+
}
|
|
60816
|
+
case "python3": {
|
|
60817
|
+
const { parse_py_relative_imports } = await loadParser("windmill-parser-wasm-py-imports");
|
|
60818
|
+
return parse_py_relative_imports(code2, scriptPath);
|
|
60819
|
+
}
|
|
60820
|
+
default:
|
|
60821
|
+
return [];
|
|
60822
|
+
}
|
|
60823
|
+
} catch (e) {
|
|
60824
|
+
warn(`Failed to parse relative imports for ${scriptPath}: ${e}. Dependency tracking for relative imports will be disabled.`);
|
|
60825
|
+
return [];
|
|
60826
|
+
}
|
|
60827
|
+
}
|
|
60828
|
+
var init_relative_imports = __esm(async () => {
|
|
60829
|
+
init_log();
|
|
60830
|
+
await init_metadata();
|
|
60831
|
+
});
|
|
60832
|
+
|
|
60775
60833
|
// src/commands/flow/flow_metadata.ts
|
|
60776
60834
|
import * as path7 from "node:path";
|
|
60777
60835
|
import { sep as SEP7 } from "node:path";
|
|
@@ -60787,7 +60845,7 @@ async function generateFlowHash(rawWorkspaceDependencies, folder, defaultTs) {
|
|
|
60787
60845
|
}
|
|
60788
60846
|
return { ...hashes, [TOP_HASH]: await generateHash(JSON.stringify(hashes)) };
|
|
60789
60847
|
}
|
|
60790
|
-
async function generateFlowLockInternal(folder, dryRun, workspace, opts, justUpdateMetadataLock, noStaleMessage) {
|
|
60848
|
+
async function generateFlowLockInternal(folder, dryRun, workspace, opts, justUpdateMetadataLock, noStaleMessage, legacyBehaviour, tree) {
|
|
60791
60849
|
if (folder.endsWith(SEP7)) {
|
|
60792
60850
|
folder = folder.substring(0, folder.length - 1);
|
|
60793
60851
|
}
|
|
@@ -60795,24 +60853,65 @@ async function generateFlowLockInternal(folder, dryRun, workspace, opts, justUpd
|
|
|
60795
60853
|
if (!justUpdateMetadataLock && !noStaleMessage) {
|
|
60796
60854
|
info(`Generating lock for flow ${folder} at ${remote_path}`);
|
|
60797
60855
|
}
|
|
60798
|
-
const rawWorkspaceDependencies = await getRawWorkspaceDependencies();
|
|
60799
60856
|
const flowValue = await yamlParseFile(folder + SEP7 + "flow.yaml");
|
|
60800
|
-
const
|
|
60801
|
-
|
|
60857
|
+
const folderNormalized = folder.replaceAll(SEP7, "/");
|
|
60858
|
+
const inlineScriptsForTree = extractInlineScripts(structuredClone(flowValue.value.modules), {}, SEP7, opts.defaultTs).filter((s) => !s.is_lock);
|
|
60859
|
+
let filteredDeps = {};
|
|
60802
60860
|
const conf = await readLockfile();
|
|
60803
|
-
if (
|
|
60804
|
-
if (
|
|
60805
|
-
|
|
60861
|
+
if (!legacyBehaviour && tree) {
|
|
60862
|
+
if (dryRun) {
|
|
60863
|
+
const inlineScriptPaths = [];
|
|
60864
|
+
for (const script of inlineScriptsForTree) {
|
|
60865
|
+
let content = script.content;
|
|
60866
|
+
if (content.startsWith("!inline ")) {
|
|
60867
|
+
const filePath = folder + SEP7 + content.replace("!inline ", "");
|
|
60868
|
+
try {
|
|
60869
|
+
content = await readFile7(filePath, "utf-8");
|
|
60870
|
+
} catch {
|
|
60871
|
+
continue;
|
|
60872
|
+
}
|
|
60873
|
+
}
|
|
60874
|
+
const treePath = folderNormalized + "/" + path7.basename(script.path, path7.extname(script.path));
|
|
60875
|
+
const language = script.language;
|
|
60876
|
+
const imports = await extractRelativeImports(content, treePath, language);
|
|
60877
|
+
await tree.addNode(treePath, content, language, "", imports, "inline_script", folderNormalized, folder, false);
|
|
60878
|
+
inlineScriptPaths.push(treePath);
|
|
60879
|
+
}
|
|
60880
|
+
const hashes = await generateFlowHash({}, folder, opts.defaultTs);
|
|
60881
|
+
const isDirectlyStale = !await checkifMetadataUptodate(folder, hashes[TOP_HASH], conf, TOP_HASH);
|
|
60882
|
+
await tree.addNode(folderNormalized, "", "bun", "", inlineScriptPaths, "flow", folderNormalized, folder, isDirectlyStale);
|
|
60883
|
+
return;
|
|
60884
|
+
}
|
|
60885
|
+
filteredDeps = await filterWorkspaceDependenciesForFlow(flowValue.value, tree.getMismatchedWorkspaceDeps(), folder);
|
|
60886
|
+
} else {
|
|
60887
|
+
const rawWorkspaceDependencies = await getRawWorkspaceDependencies(true);
|
|
60888
|
+
filteredDeps = await filterWorkspaceDependenciesForFlow(flowValue.value, rawWorkspaceDependencies, folder);
|
|
60889
|
+
const hashes = await generateFlowHash(filteredDeps, folder, opts.defaultTs);
|
|
60890
|
+
const isDirectlyStale = !await checkifMetadataUptodate(folder, hashes[TOP_HASH], conf, TOP_HASH);
|
|
60891
|
+
if (!isDirectlyStale) {
|
|
60892
|
+
if (!noStaleMessage) {
|
|
60893
|
+
info(colors.green(`Flow ${remote_path} metadata is up-to-date, skipping`));
|
|
60894
|
+
}
|
|
60895
|
+
return;
|
|
60896
|
+
} else if (dryRun) {
|
|
60897
|
+
return remote_path;
|
|
60806
60898
|
}
|
|
60807
|
-
return;
|
|
60808
|
-
} else if (dryRun) {
|
|
60809
|
-
return remote_path;
|
|
60810
60899
|
}
|
|
60811
60900
|
if (Object.keys(filteredDeps).length > 0 && !noStaleMessage) {
|
|
60812
60901
|
info((await blueColor())(`Found workspace dependencies (${workspaceDependenciesLanguages.map((l) => l.filename).join("/")}) for ${folder}, using them`));
|
|
60813
60902
|
}
|
|
60814
60903
|
let changedScripts = [];
|
|
60904
|
+
const fileToTreePath = new Map;
|
|
60905
|
+
for (const script of inlineScriptsForTree) {
|
|
60906
|
+
const c = script.content;
|
|
60907
|
+
if (c.startsWith("!inline ")) {
|
|
60908
|
+
const fileName = c.replace("!inline ", "");
|
|
60909
|
+
const treePath = folderNormalized + "/" + path7.basename(script.path, path7.extname(script.path));
|
|
60910
|
+
fileToTreePath.set(fileName, treePath);
|
|
60911
|
+
}
|
|
60912
|
+
}
|
|
60815
60913
|
if (!justUpdateMetadataLock) {
|
|
60914
|
+
const hashes = await generateFlowHash(filteredDeps, folder, opts.defaultTs);
|
|
60816
60915
|
for (const [path8, hash2] of Object.entries(hashes)) {
|
|
60817
60916
|
if (path8 == TOP_HASH) {
|
|
60818
60917
|
continue;
|
|
@@ -60825,14 +60924,21 @@ async function generateFlowLockInternal(folder, dryRun, workspace, opts, justUpd
|
|
|
60825
60924
|
info(`Recomputing locks of ${changedScripts.join(", ")} in ${folder}`);
|
|
60826
60925
|
}
|
|
60827
60926
|
const fileReader = async (path8) => await readFile7(folder + SEP7 + path8, "utf-8");
|
|
60828
|
-
|
|
60927
|
+
const locksToRemove = tree && !legacyBehaviour ? Object.keys(hashes).filter((k) => {
|
|
60928
|
+
if (k === TOP_HASH)
|
|
60929
|
+
return false;
|
|
60930
|
+
const treePath = fileToTreePath.get(k) ?? folderNormalized + "/" + path7.basename(k, path7.extname(k));
|
|
60931
|
+
return tree.isStale(treePath);
|
|
60932
|
+
}) : changedScripts;
|
|
60933
|
+
await replaceInlineScripts(flowValue.value.modules, fileReader, exports_log, folder + SEP7, SEP7, locksToRemove);
|
|
60829
60934
|
if (flowValue.value.failure_module) {
|
|
60830
|
-
await replaceInlineScripts([flowValue.value.failure_module], fileReader, exports_log, folder + SEP7, SEP7,
|
|
60935
|
+
await replaceInlineScripts([flowValue.value.failure_module], fileReader, exports_log, folder + SEP7, SEP7, locksToRemove);
|
|
60831
60936
|
}
|
|
60832
60937
|
if (flowValue.value.preprocessor_module) {
|
|
60833
|
-
await replaceInlineScripts([flowValue.value.preprocessor_module], fileReader, exports_log, folder + SEP7, SEP7,
|
|
60938
|
+
await replaceInlineScripts([flowValue.value.preprocessor_module], fileReader, exports_log, folder + SEP7, SEP7, locksToRemove);
|
|
60834
60939
|
}
|
|
60835
|
-
|
|
60940
|
+
const tempScriptRefs = tree?.getTempScriptRefs(folderNormalized);
|
|
60941
|
+
flowValue.value = await updateFlow2(workspace, flowValue.value, remote_path, filteredDeps, tempScriptRefs);
|
|
60836
60942
|
const lockAssigner = newPathAssigner(opts.defaultTs ?? "bun", {
|
|
60837
60943
|
skipInlineScriptSuffix: getNonDottedPaths()
|
|
60838
60944
|
});
|
|
@@ -60848,15 +60954,22 @@ async function generateFlowLockInternal(folder, dryRun, workspace, opts, justUpd
|
|
|
60848
60954
|
});
|
|
60849
60955
|
writeIfChanged(process.cwd() + SEP7 + folder + SEP7 + "flow.yaml", import_yaml8.stringify(flowValue));
|
|
60850
60956
|
}
|
|
60851
|
-
|
|
60957
|
+
const depsForHash = tree && !legacyBehaviour ? {} : filteredDeps;
|
|
60958
|
+
const finalHashes = await generateFlowHash(depsForHash, folder, opts.defaultTs);
|
|
60852
60959
|
await clearGlobalLock(folder);
|
|
60853
|
-
for (const [path8, hash2] of Object.entries(
|
|
60960
|
+
for (const [path8, hash2] of Object.entries(finalHashes)) {
|
|
60854
60961
|
await updateMetadataGlobalLock(folder, hash2, path8);
|
|
60855
60962
|
}
|
|
60856
60963
|
if (!noStaleMessage) {
|
|
60857
60964
|
info(colors.green(`Flow ${remote_path} lockfiles updated`));
|
|
60858
60965
|
}
|
|
60859
|
-
const
|
|
60966
|
+
const relocked = tree && !legacyBehaviour ? Object.keys(finalHashes).filter((k) => {
|
|
60967
|
+
if (k === TOP_HASH)
|
|
60968
|
+
return false;
|
|
60969
|
+
const treePath = fileToTreePath.get(k) ?? folderNormalized + "/" + path7.basename(k, path7.extname(k));
|
|
60970
|
+
return tree.isStale(treePath);
|
|
60971
|
+
}) : changedScripts;
|
|
60972
|
+
const updatedScripts = relocked.map((p) => {
|
|
60860
60973
|
const parts = p.split(SEP7);
|
|
60861
60974
|
return parts[parts.length - 1].replace(/\.[^.]+$/, "");
|
|
60862
60975
|
});
|
|
@@ -60875,7 +60988,7 @@ async function filterWorkspaceDependenciesForFlow(flowValue, rawWorkspaceDepende
|
|
|
60875
60988
|
const scripts = inlineScripts.filter((s) => !s.is_lock).map((s) => ({ content: s.content, language: s.language }));
|
|
60876
60989
|
return await filterWorkspaceDependenciesForScripts(scripts, rawWorkspaceDependencies, folder, SEP7);
|
|
60877
60990
|
}
|
|
60878
|
-
async function updateFlow2(workspace, flow_value, remotePath, rawWorkspaceDependencies) {
|
|
60991
|
+
async function updateFlow2(workspace, flow_value, remotePath, rawWorkspaceDependencies, tempScriptRefs) {
|
|
60879
60992
|
let rawResponse;
|
|
60880
60993
|
if (Object.keys(rawWorkspaceDependencies).length > 0) {
|
|
60881
60994
|
info(colors.blue("Using raw workspace dependencies for flow dependencies"));
|
|
@@ -60891,7 +61004,8 @@ async function updateFlow2(workspace, flow_value, remotePath, rawWorkspaceDepend
|
|
|
60891
61004
|
flow_value,
|
|
60892
61005
|
path: remotePath,
|
|
60893
61006
|
use_local_lockfiles: true,
|
|
60894
|
-
raw_workspace_dependencies: rawWorkspaceDependencies
|
|
61007
|
+
raw_workspace_dependencies: rawWorkspaceDependencies,
|
|
61008
|
+
...tempScriptRefs && Object.keys(tempScriptRefs).length > 0 ? { temp_script_refs: tempScriptRefs } : {}
|
|
60895
61009
|
})
|
|
60896
61010
|
});
|
|
60897
61011
|
} else {
|
|
@@ -60905,7 +61019,8 @@ async function updateFlow2(workspace, flow_value, remotePath, rawWorkspaceDepend
|
|
|
60905
61019
|
},
|
|
60906
61020
|
body: JSON.stringify({
|
|
60907
61021
|
flow_value,
|
|
60908
|
-
path: remotePath
|
|
61022
|
+
path: remotePath,
|
|
61023
|
+
...tempScriptRefs && Object.keys(tempScriptRefs).length > 0 ? { temp_script_refs: tempScriptRefs } : {}
|
|
60909
61024
|
})
|
|
60910
61025
|
});
|
|
60911
61026
|
}
|
|
@@ -60942,7 +61057,8 @@ var init_flow_metadata = __esm(async () => {
|
|
|
60942
61057
|
init_metadata(),
|
|
60943
61058
|
init_utils(),
|
|
60944
61059
|
init_script(),
|
|
60945
|
-
init_sync()
|
|
61060
|
+
init_sync(),
|
|
61061
|
+
init_relative_imports()
|
|
60946
61062
|
]);
|
|
60947
61063
|
import_yaml8 = __toESM(require_dist(), 1);
|
|
60948
61064
|
});
|
|
@@ -62412,7 +62528,7 @@ Both local and remote have been modified.`));
|
|
|
62412
62528
|
info("All local changes pulled, now updating wmill-lock.yaml");
|
|
62413
62529
|
await readLockfile();
|
|
62414
62530
|
const tracker = await buildTracker(changes);
|
|
62415
|
-
const rawWorkspaceDependencies = await getRawWorkspaceDependencies();
|
|
62531
|
+
const rawWorkspaceDependencies = await getRawWorkspaceDependencies(true);
|
|
62416
62532
|
for (const change of tracker.scripts) {
|
|
62417
62533
|
await generateScriptMetadataInternal(change, workspace, opts, false, true, rawWorkspaceDependencies, codebases, true);
|
|
62418
62534
|
}
|
|
@@ -62557,7 +62673,7 @@ Push aborted: ${lockIssues.length} script(s) missing locks.`));
|
|
|
62557
62673
|
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, resourceTypeToIsFileset, false);
|
|
62558
62674
|
const local = await FSFSElement(path8.join(process.cwd(), ""), codebases, false);
|
|
62559
62675
|
const changes = await compareDynFSElement(local, remote, await ignoreF(opts), opts.json ?? false, opts, true, codebases, false, specificItems, opts.branch, false);
|
|
62560
|
-
const rawWorkspaceDependencies = await getRawWorkspaceDependencies();
|
|
62676
|
+
const rawWorkspaceDependencies = await getRawWorkspaceDependencies(true);
|
|
62561
62677
|
const tracker = await buildTracker(changes);
|
|
62562
62678
|
const staleScripts = [];
|
|
62563
62679
|
const staleFlows = [];
|
|
@@ -63351,30 +63467,6 @@ var init_parse_schema = __esm(() => {
|
|
|
63351
63467
|
});
|
|
63352
63468
|
|
|
63353
63469
|
// src/utils/metadata.ts
|
|
63354
|
-
var exports_metadata = {};
|
|
63355
|
-
__export(exports_metadata, {
|
|
63356
|
-
workspaceDependenciesPathToLanguageAndFilename: () => workspaceDependenciesPathToLanguageAndFilename,
|
|
63357
|
-
updateScriptSchema: () => updateScriptSchema,
|
|
63358
|
-
updateMetadataGlobalLock: () => updateMetadataGlobalLock,
|
|
63359
|
-
replaceLock: () => replaceLock,
|
|
63360
|
-
readLockfile: () => readLockfile,
|
|
63361
|
-
parseMetadataFileIfExists: () => parseMetadataFileIfExists,
|
|
63362
|
-
parseMetadataFile: () => parseMetadataFile,
|
|
63363
|
-
normalizeLockPath: () => normalizeLockPath,
|
|
63364
|
-
inferSchema: () => inferSchema,
|
|
63365
|
-
getRawWorkspaceDependencies: () => getRawWorkspaceDependencies,
|
|
63366
|
-
generateScriptMetadataInternal: () => generateScriptMetadataInternal,
|
|
63367
|
-
generateScriptHash: () => generateScriptHash,
|
|
63368
|
-
filterWorkspaceDependenciesForScripts: () => filterWorkspaceDependenciesForScripts,
|
|
63369
|
-
filterWorkspaceDependencies: () => filterWorkspaceDependencies,
|
|
63370
|
-
extractWorkspaceDepsAnnotation: () => extractWorkspaceDepsAnnotation,
|
|
63371
|
-
computeLockCacheKey: () => computeLockCacheKey,
|
|
63372
|
-
clearLockCache: () => clearLockCache,
|
|
63373
|
-
clearGlobalLock: () => clearGlobalLock,
|
|
63374
|
-
checkifMetadataUptodate: () => checkifMetadataUptodate,
|
|
63375
|
-
blueColor: () => blueColor,
|
|
63376
|
-
LockfileGenerationError: () => LockfileGenerationError
|
|
63377
|
-
});
|
|
63378
63470
|
import { sep as SEP9 } from "node:path";
|
|
63379
63471
|
import { readFile as readFile9, writeFile as writeFile7, stat as stat7, rm as rm2, readdir as readdir5 } from "node:fs/promises";
|
|
63380
63472
|
import { readFileSync as readFileSync3, existsSync as existsSync4, readdirSync, statSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
@@ -63393,7 +63485,7 @@ function loadParser(pkgName) {
|
|
|
63393
63485
|
}
|
|
63394
63486
|
return p;
|
|
63395
63487
|
}
|
|
63396
|
-
async function getRawWorkspaceDependencies() {
|
|
63488
|
+
async function getRawWorkspaceDependencies(legacyBehaviour) {
|
|
63397
63489
|
const rawWorkspaceDeps = {};
|
|
63398
63490
|
try {
|
|
63399
63491
|
const entries = await readdir5("dependencies", { withFileTypes: true });
|
|
@@ -63404,9 +63496,13 @@ async function getRawWorkspaceDependencies() {
|
|
|
63404
63496
|
const content = await readFile9(filePath, "utf-8");
|
|
63405
63497
|
for (const lang of workspaceDependenciesLanguages) {
|
|
63406
63498
|
if (entry.name.endsWith(lang.filename)) {
|
|
63407
|
-
|
|
63408
|
-
|
|
63409
|
-
|
|
63499
|
+
if (legacyBehaviour) {
|
|
63500
|
+
const contentHash = await generateHash(content + filePath);
|
|
63501
|
+
const isUpToDate = await checkifMetadataUptodate(filePath, contentHash, undefined);
|
|
63502
|
+
if (!isUpToDate) {
|
|
63503
|
+
rawWorkspaceDeps[filePath] = content;
|
|
63504
|
+
}
|
|
63505
|
+
} else {
|
|
63410
63506
|
rawWorkspaceDeps[filePath] = content;
|
|
63411
63507
|
}
|
|
63412
63508
|
break;
|
|
@@ -63463,7 +63559,7 @@ async function blueColor() {
|
|
|
63463
63559
|
const isWin2 = await getIsWin();
|
|
63464
63560
|
return isWin2 ? colors.black : colors.blue;
|
|
63465
63561
|
}
|
|
63466
|
-
async function generateScriptMetadataInternal(scriptPath, workspace, opts, dryRun, noStaleMessage, rawWorkspaceDependencies, codebases, justUpdateMetadataLock) {
|
|
63562
|
+
async function generateScriptMetadataInternal(scriptPath, workspace, opts, dryRun, noStaleMessage, rawWorkspaceDependencies, codebases, justUpdateMetadataLock, legacyBehaviour, tree) {
|
|
63467
63563
|
const isFolderLayout = isModuleEntryPoint(scriptPath);
|
|
63468
63564
|
const remotePath = isFolderLayout ? getScriptBasePathFromModulePath(scriptPath).replaceAll(SEP9, "/") : scriptPath.substring(0, scriptPath.indexOf(".")).replaceAll(SEP9, "/");
|
|
63469
63565
|
const language = inferContentTypeFromFilePath(scriptPath, opts.defaultTs);
|
|
@@ -63473,10 +63569,11 @@ async function generateScriptMetadataInternal(scriptPath, workspace, opts, dryRu
|
|
|
63473
63569
|
const filteredRawWorkspaceDependencies = filterWorkspaceDependencies(rawWorkspaceDependencies, scriptContent, language);
|
|
63474
63570
|
const moduleFolderPath = isFolderLayout ? path9.dirname(scriptPath) : scriptPath.substring(0, scriptPath.indexOf(".")) + getModuleFolderSuffix();
|
|
63475
63571
|
const hasModules = existsSync4(moduleFolderPath) && statSync(moduleFolderPath).isDirectory();
|
|
63476
|
-
|
|
63572
|
+
const depsForHash = !legacyBehaviour && tree ? {} : filteredRawWorkspaceDependencies;
|
|
63573
|
+
let hash2 = await generateScriptHash(depsForHash, scriptContent, metadataContent);
|
|
63477
63574
|
let moduleHashes = {};
|
|
63478
63575
|
if (hasModules) {
|
|
63479
|
-
moduleHashes = await computeModuleHashes(moduleFolderPath, opts.defaultTs, rawWorkspaceDependencies, isFolderLayout);
|
|
63576
|
+
moduleHashes = await computeModuleHashes(moduleFolderPath, opts.defaultTs, !legacyBehaviour && tree ? {} : rawWorkspaceDependencies, isFolderLayout);
|
|
63480
63577
|
}
|
|
63481
63578
|
const hasModuleHashes = Object.keys(moduleHashes).length > 0;
|
|
63482
63579
|
let checkHash = hash2;
|
|
@@ -63487,25 +63584,34 @@ async function generateScriptMetadataInternal(scriptPath, workspace, opts, dryRu
|
|
|
63487
63584
|
checkSubpath = SCRIPT_TOP_HASH;
|
|
63488
63585
|
}
|
|
63489
63586
|
const conf = await readLockfile();
|
|
63490
|
-
|
|
63491
|
-
|
|
63492
|
-
|
|
63587
|
+
const isDirectlyStale = !await checkifMetadataUptodate(remotePath, checkHash, conf, checkSubpath);
|
|
63588
|
+
if (!legacyBehaviour && tree) {
|
|
63589
|
+
if (dryRun) {
|
|
63590
|
+
const imports = await extractRelativeImports(scriptContent, remotePath, language);
|
|
63591
|
+
await tree.addNode(remotePath, scriptContent, language, metadataContent, imports, "script", remotePath, scriptPath, isDirectlyStale);
|
|
63592
|
+
return;
|
|
63493
63593
|
}
|
|
63494
|
-
|
|
63495
|
-
|
|
63496
|
-
|
|
63497
|
-
|
|
63498
|
-
const changed = [];
|
|
63499
|
-
for (const [modulePath, moduleHash] of Object.entries(moduleHashes)) {
|
|
63500
|
-
if (!await checkifMetadataUptodate(remotePath, moduleHash, conf, modulePath)) {
|
|
63501
|
-
changed.push(modulePath);
|
|
63502
|
-
}
|
|
63594
|
+
} else {
|
|
63595
|
+
if (await checkifMetadataUptodate(remotePath, checkHash, conf, checkSubpath)) {
|
|
63596
|
+
if (!noStaleMessage) {
|
|
63597
|
+
info(colors.green(`Script ${remotePath} metadata is up-to-date, skipping`));
|
|
63503
63598
|
}
|
|
63504
|
-
|
|
63505
|
-
|
|
63599
|
+
return;
|
|
63600
|
+
} else if (dryRun) {
|
|
63601
|
+
let detail = `${remotePath} (${language})`;
|
|
63602
|
+
if (hasModuleHashes) {
|
|
63603
|
+
const changed = [];
|
|
63604
|
+
for (const [modulePath, moduleHash] of Object.entries(moduleHashes)) {
|
|
63605
|
+
if (!await checkifMetadataUptodate(remotePath, moduleHash, conf, modulePath)) {
|
|
63606
|
+
changed.push(modulePath);
|
|
63607
|
+
}
|
|
63608
|
+
}
|
|
63609
|
+
if (changed.length > 0) {
|
|
63610
|
+
detail += ` [changed modules: ${changed.join(", ")}]`;
|
|
63611
|
+
}
|
|
63506
63612
|
}
|
|
63613
|
+
return detail;
|
|
63507
63614
|
}
|
|
63508
|
-
return detail;
|
|
63509
63615
|
}
|
|
63510
63616
|
if (!justUpdateMetadataLock && !noStaleMessage) {
|
|
63511
63617
|
info(colors.gray(`Generating metadata for ${scriptPath}`));
|
|
@@ -63517,8 +63623,9 @@ async function generateScriptMetadataInternal(scriptPath, workspace, opts, dryRu
|
|
|
63517
63623
|
if (!opts.schemaOnly && !justUpdateMetadataLock) {
|
|
63518
63624
|
const hasCodebase = findCodebase(scriptPath, codebases) != null;
|
|
63519
63625
|
if (!hasCodebase) {
|
|
63626
|
+
const tempScriptRefs = tree?.getTempScriptRefs(remotePath);
|
|
63520
63627
|
const lockPathOverride = isFolderLayout ? path9.dirname(scriptPath) + "/script.lock" : undefined;
|
|
63521
|
-
await updateScriptLock(workspace, scriptContent, language, remotePath, metadataParsedContent, filteredRawWorkspaceDependencies, lockPathOverride);
|
|
63628
|
+
await updateScriptLock(workspace, scriptContent, language, remotePath, metadataParsedContent, filteredRawWorkspaceDependencies, tempScriptRefs, lockPathOverride);
|
|
63522
63629
|
} else {
|
|
63523
63630
|
metadataParsedContent.lock = "";
|
|
63524
63631
|
}
|
|
@@ -63564,7 +63671,7 @@ async function generateScriptMetadataInternal(scriptPath, workspace, opts, dryRu
|
|
|
63564
63671
|
}
|
|
63565
63672
|
}
|
|
63566
63673
|
const metadataContentUsedForHash = newMetadataContent;
|
|
63567
|
-
hash2 = await generateScriptHash(
|
|
63674
|
+
hash2 = await generateScriptHash(depsForHash, scriptContent, metadataContentUsedForHash);
|
|
63568
63675
|
if (hasModuleHashes) {
|
|
63569
63676
|
const sortedEntries = Object.entries(moduleHashes).sort(([a], [b]) => a.localeCompare(b));
|
|
63570
63677
|
const metaHash = await generateHash(hash2 + JSON.stringify(sortedEntries));
|
|
@@ -63650,21 +63757,20 @@ function extractWorkspaceDepsAnnotation(scriptContent, language) {
|
|
|
63650
63757
|
const inline = inlineStr.trim().length > 0 ? inlineStr : null;
|
|
63651
63758
|
return { mode, external, inline };
|
|
63652
63759
|
}
|
|
63653
|
-
async function computeLockCacheKey(scriptContent, language, rawWorkspaceDependencies) {
|
|
63760
|
+
async function computeLockCacheKey(scriptContent, language, rawWorkspaceDependencies, tempScriptRefs) {
|
|
63654
63761
|
const annotation = extractWorkspaceDepsAnnotation(scriptContent, language);
|
|
63655
63762
|
const annotationStr = annotation ? `${annotation.mode}|${annotation.external.join(",")}|${annotation.inline ?? ""}` : "none";
|
|
63656
63763
|
const sortedDepsKeys = Object.keys(rawWorkspaceDependencies).sort();
|
|
63657
63764
|
const depsStr = sortedDepsKeys.map((k) => `${k}=${rawWorkspaceDependencies[k]}`).join(";");
|
|
63658
|
-
|
|
63765
|
+
const tempRefsStr = tempScriptRefs ? Object.keys(tempScriptRefs).sort().map((k) => `${k}=${tempScriptRefs[k]}`).join(";") : "";
|
|
63766
|
+
return await generateHash(`${language}|${annotationStr}|${depsStr}|${tempRefsStr}`);
|
|
63659
63767
|
}
|
|
63660
|
-
function
|
|
63661
|
-
lockCache.clear();
|
|
63662
|
-
}
|
|
63663
|
-
async function fetchScriptLock(workspace, scriptContent, language, remotePath, rawWorkspaceDependencies) {
|
|
63768
|
+
async function fetchScriptLock(workspace, scriptContent, language, remotePath, rawWorkspaceDependencies, tempScriptRefs) {
|
|
63664
63769
|
const hasRawDeps = Object.keys(rawWorkspaceDependencies).length > 0;
|
|
63665
|
-
const
|
|
63770
|
+
const hasTempRefs = tempScriptRefs && Object.keys(tempScriptRefs).length > 0;
|
|
63771
|
+
const cacheKey = hasRawDeps || hasTempRefs ? await computeLockCacheKey(scriptContent, language, rawWorkspaceDependencies, tempScriptRefs) : undefined;
|
|
63666
63772
|
if (cacheKey && lockCache.has(cacheKey)) {
|
|
63667
|
-
|
|
63773
|
+
debug(`Using cached lockfile for ${remotePath}`);
|
|
63668
63774
|
return lockCache.get(cacheKey);
|
|
63669
63775
|
}
|
|
63670
63776
|
const extraHeaders = getHeaders2();
|
|
@@ -63684,7 +63790,8 @@ async function fetchScriptLock(workspace, scriptContent, language, remotePath, r
|
|
|
63684
63790
|
}
|
|
63685
63791
|
],
|
|
63686
63792
|
raw_workspace_dependencies: Object.keys(rawWorkspaceDependencies).length > 0 ? rawWorkspaceDependencies : null,
|
|
63687
|
-
entrypoint: remotePath
|
|
63793
|
+
entrypoint: remotePath,
|
|
63794
|
+
temp_script_refs: tempScriptRefs && Object.keys(tempScriptRefs).length > 0 ? tempScriptRefs : null
|
|
63688
63795
|
})
|
|
63689
63796
|
});
|
|
63690
63797
|
let responseText = "reading response failed";
|
|
@@ -63709,15 +63816,15 @@ async function fetchScriptLock(workspace, scriptContent, language, remotePath, r
|
|
|
63709
63816
|
throw new LockfileGenerationError(`Failed to generate lockfile:${rawResponse.statusText}, ${responseText}, ${e}`);
|
|
63710
63817
|
}
|
|
63711
63818
|
}
|
|
63712
|
-
async function updateScriptLock(workspace, scriptContent, language, remotePath, metadataContent, rawWorkspaceDependencies, lockPathOverride) {
|
|
63819
|
+
async function updateScriptLock(workspace, scriptContent, language, remotePath, metadataContent, rawWorkspaceDependencies, tempScriptRefs, lockPathOverride) {
|
|
63713
63820
|
if (!(workspaceDependenciesLanguages.some((l) => l.language == language) && language !== "powershell" || language == "deno" || language == "rust" || language == "ansible")) {
|
|
63714
63821
|
return;
|
|
63715
63822
|
}
|
|
63716
63823
|
if (Object.keys(rawWorkspaceDependencies).length > 0) {
|
|
63717
63824
|
const dependencyPaths = Object.keys(rawWorkspaceDependencies).join(", ");
|
|
63718
|
-
|
|
63825
|
+
debug(`Generating script lock for ${remotePath} with raw workspace dependencies: ${dependencyPaths}`);
|
|
63719
63826
|
}
|
|
63720
|
-
const lock = await fetchScriptLock(workspace, scriptContent, language, remotePath, rawWorkspaceDependencies);
|
|
63827
|
+
const lock = await fetchScriptLock(workspace, scriptContent, language, remotePath, rawWorkspaceDependencies, tempScriptRefs);
|
|
63721
63828
|
const lockPath = lockPathOverride ?? remotePath + ".script.lock";
|
|
63722
63829
|
if (lock != "") {
|
|
63723
63830
|
await writeFile7(lockPath, lock, "utf-8");
|
|
@@ -63756,7 +63863,7 @@ async function updateModuleLocks(workspace, dirPath, relPrefix, scriptRemotePath
|
|
|
63756
63863
|
}
|
|
63757
63864
|
const moduleContent = readFileSync3(fullPath, "utf-8");
|
|
63758
63865
|
const moduleRemotePath = scriptRemotePath + "/" + relPath;
|
|
63759
|
-
|
|
63866
|
+
debug(`Generating lock for module ${relPath}`);
|
|
63760
63867
|
try {
|
|
63761
63868
|
const lock = await fetchScriptLock(workspace, moduleContent, modLanguage, moduleRemotePath, rawWorkspaceDependencies);
|
|
63762
63869
|
const baseName = entry.name.replace(/\.[^.]+$/, "");
|
|
@@ -64157,7 +64264,8 @@ var init_metadata = __esm(async () => {
|
|
|
64157
64264
|
await __promiseAll([
|
|
64158
64265
|
init_sync(),
|
|
64159
64266
|
init_utils(),
|
|
64160
|
-
init_utils()
|
|
64267
|
+
init_utils(),
|
|
64268
|
+
init_relative_imports()
|
|
64161
64269
|
]);
|
|
64162
64270
|
import_yaml12 = __toESM(require_dist(), 1);
|
|
64163
64271
|
_require = createRequire2(import.meta.url);
|
|
@@ -64495,7 +64603,7 @@ async function generateAppHash(rawReqs, folder, rawApp, defaultTs) {
|
|
|
64495
64603
|
}
|
|
64496
64604
|
return { ...hashes, [TOP_HASH2]: await generateHash(JSON.stringify(hashes)) };
|
|
64497
64605
|
}
|
|
64498
|
-
async function generateAppLocksInternal(appFolder, rawApp, dryRun, workspace, opts, justUpdateMetadataLock, noStaleMessage) {
|
|
64606
|
+
async function generateAppLocksInternal(appFolder, rawApp, dryRun, workspace, opts, justUpdateMetadataLock, noStaleMessage, legacyBehaviour, tree) {
|
|
64499
64607
|
if (appFolder.endsWith(SEP11)) {
|
|
64500
64608
|
appFolder = appFolder.substring(0, appFolder.length - 1);
|
|
64501
64609
|
}
|
|
@@ -64503,26 +64611,69 @@ async function generateAppLocksInternal(appFolder, rawApp, dryRun, workspace, op
|
|
|
64503
64611
|
if (!justUpdateMetadataLock && !noStaleMessage) {
|
|
64504
64612
|
info(`Generating locks for app ${appFolder} at ${remote_path}`);
|
|
64505
64613
|
}
|
|
64506
|
-
const rawWorkspaceDependencies = await getRawWorkspaceDependencies();
|
|
64507
64614
|
const appFilePath = path11.join(appFolder, rawApp ? "raw_app.yaml" : "app.yaml");
|
|
64508
64615
|
const appFile = await yamlParseFile(appFilePath);
|
|
64509
64616
|
const appValue = rawApp ? appFile.runnables : appFile.value;
|
|
64510
|
-
const
|
|
64511
|
-
let
|
|
64512
|
-
const conf = await
|
|
64513
|
-
if (
|
|
64514
|
-
if (
|
|
64515
|
-
|
|
64617
|
+
const folderNormalized = appFolder.replaceAll(SEP11, "/");
|
|
64618
|
+
let filteredDeps = {};
|
|
64619
|
+
const conf = await readLockfile();
|
|
64620
|
+
if (!legacyBehaviour && tree) {
|
|
64621
|
+
if (dryRun) {
|
|
64622
|
+
const hashes = await generateAppHash({}, appFolder, rawApp, opts.defaultTs);
|
|
64623
|
+
const isDirectlyStale = !await checkifMetadataUptodate(appFolder, hashes[TOP_HASH2], conf, TOP_HASH2);
|
|
64624
|
+
let treeAppValue = structuredClone(appValue);
|
|
64625
|
+
if (rawApp) {
|
|
64626
|
+
const runnablesPath = path11.join(appFolder, APP_BACKEND_FOLDER);
|
|
64627
|
+
const runnablesFromFiles = await loadRunnablesFromBackend(runnablesPath);
|
|
64628
|
+
if (Object.keys(runnablesFromFiles).length > 0) {
|
|
64629
|
+
treeAppValue = runnablesFromFiles;
|
|
64630
|
+
}
|
|
64631
|
+
}
|
|
64632
|
+
const inlineScriptPaths = [];
|
|
64633
|
+
await traverseAndProcessInlineScripts(treeAppValue, async (inlineScript, context) => {
|
|
64634
|
+
if (!inlineScript.content || !inlineScript.language) {
|
|
64635
|
+
return inlineScript;
|
|
64636
|
+
}
|
|
64637
|
+
let content = inlineScript.content;
|
|
64638
|
+
if (typeof content === "string" && content.startsWith("!inline ")) {
|
|
64639
|
+
const filePath = appFolder + SEP11 + content.replace("!inline ", "");
|
|
64640
|
+
try {
|
|
64641
|
+
content = await readFile11(filePath, "utf-8");
|
|
64642
|
+
} catch {
|
|
64643
|
+
return inlineScript;
|
|
64644
|
+
}
|
|
64645
|
+
}
|
|
64646
|
+
const treePath = folderNormalized + "/" + context.path.join("/");
|
|
64647
|
+
const language = inlineScript.language;
|
|
64648
|
+
const imports = await extractRelativeImports(content, treePath, language);
|
|
64649
|
+
await tree.addNode(treePath, content, language, "", imports, "inline_script", folderNormalized, appFolder, false);
|
|
64650
|
+
inlineScriptPaths.push(treePath);
|
|
64651
|
+
return inlineScript;
|
|
64652
|
+
});
|
|
64653
|
+
await tree.addNode(folderNormalized, "", "bun", "", inlineScriptPaths, "app", folderNormalized, appFolder, isDirectlyStale, rawApp);
|
|
64654
|
+
return;
|
|
64655
|
+
}
|
|
64656
|
+
filteredDeps = await filterWorkspaceDependenciesForApp(appValue, tree.getMismatchedWorkspaceDeps(), appFolder);
|
|
64657
|
+
} else {
|
|
64658
|
+
const rawWorkspaceDependencies = await getRawWorkspaceDependencies(true);
|
|
64659
|
+
filteredDeps = await filterWorkspaceDependenciesForApp(appValue, rawWorkspaceDependencies, appFolder);
|
|
64660
|
+
const hashes = await generateAppHash(filteredDeps, appFolder, rawApp, opts.defaultTs);
|
|
64661
|
+
const isDirectlyStale = !await checkifMetadataUptodate(appFolder, hashes[TOP_HASH2], conf, TOP_HASH2);
|
|
64662
|
+
if (!isDirectlyStale) {
|
|
64663
|
+
if (!noStaleMessage) {
|
|
64664
|
+
info(colors.green(`App ${remote_path} metadata is up-to-date, skipping`));
|
|
64665
|
+
}
|
|
64666
|
+
return;
|
|
64667
|
+
} else if (dryRun) {
|
|
64668
|
+
return remote_path;
|
|
64516
64669
|
}
|
|
64517
|
-
return;
|
|
64518
|
-
} else if (dryRun) {
|
|
64519
|
-
return remote_path;
|
|
64520
64670
|
}
|
|
64521
64671
|
if (Object.keys(filteredDeps).length > 0 && !noStaleMessage) {
|
|
64522
64672
|
info((await blueColor())(`Found workspace dependencies (${workspaceDependenciesLanguages.map((l) => l.filename).join("/")}) for ${appFolder}, using them`));
|
|
64523
64673
|
}
|
|
64524
64674
|
let updatedScripts = [];
|
|
64525
64675
|
if (!justUpdateMetadataLock) {
|
|
64676
|
+
const hashes = await generateAppHash(filteredDeps, appFolder, rawApp, opts.defaultTs);
|
|
64526
64677
|
const changedScripts = [];
|
|
64527
64678
|
for (const [scriptPath, hash2] of Object.entries(hashes)) {
|
|
64528
64679
|
if (scriptPath == TOP_HASH2) {
|
|
@@ -64532,7 +64683,8 @@ async function generateAppLocksInternal(appFolder, rawApp, dryRun, workspace, op
|
|
|
64532
64683
|
changedScripts.push(scriptPath);
|
|
64533
64684
|
}
|
|
64534
64685
|
}
|
|
64535
|
-
|
|
64686
|
+
const tempScriptRefs = tree?.getTempScriptRefs(folderNormalized);
|
|
64687
|
+
if (changedScripts.length > 0 || tree && !legacyBehaviour) {
|
|
64536
64688
|
if (!noStaleMessage) {
|
|
64537
64689
|
info(`Recomputing locks of ${changedScripts.join(", ")} in ${appFolder}`);
|
|
64538
64690
|
}
|
|
@@ -64544,11 +64696,11 @@ async function generateAppLocksInternal(appFolder, rawApp, dryRun, workspace, op
|
|
|
64544
64696
|
runnables = rawAppFile.runnables;
|
|
64545
64697
|
}
|
|
64546
64698
|
replaceInlineScripts2(runnables, runnablesPath + SEP11, false);
|
|
64547
|
-
updatedScripts = await updateRawAppRunnables(workspace, runnables, remote_path, appFolder, filteredDeps, opts.defaultTs, noStaleMessage);
|
|
64699
|
+
updatedScripts = await updateRawAppRunnables(workspace, runnables, remote_path, appFolder, filteredDeps, opts.defaultTs, noStaleMessage, tempScriptRefs);
|
|
64548
64700
|
} else {
|
|
64549
64701
|
const normalAppFile = appFile;
|
|
64550
64702
|
replaceInlineScripts2(normalAppFile.value, appFolder + SEP11, false);
|
|
64551
|
-
const result = await updateAppInlineScripts(workspace, normalAppFile.value, remote_path, appFolder, filteredDeps, opts.defaultTs, noStaleMessage);
|
|
64703
|
+
const result = await updateAppInlineScripts(workspace, normalAppFile.value, remote_path, appFolder, filteredDeps, opts.defaultTs, noStaleMessage, tempScriptRefs);
|
|
64552
64704
|
normalAppFile.value = result.value;
|
|
64553
64705
|
updatedScripts = result.updatedScripts;
|
|
64554
64706
|
writeIfChanged(appFilePath, import_yaml17.stringify(appFile, yamlOptions));
|
|
@@ -64557,9 +64709,10 @@ async function generateAppLocksInternal(appFolder, rawApp, dryRun, workspace, op
|
|
|
64557
64709
|
info(colors.gray(`No scripts changed in ${appFolder}`));
|
|
64558
64710
|
}
|
|
64559
64711
|
}
|
|
64560
|
-
|
|
64712
|
+
const depsForHash = tree && !legacyBehaviour ? {} : filteredDeps;
|
|
64713
|
+
const finalHashes = await generateAppHash(depsForHash, appFolder, rawApp, opts.defaultTs);
|
|
64561
64714
|
await clearGlobalLock(appFolder);
|
|
64562
|
-
for (const [scriptPath, hash2] of Object.entries(
|
|
64715
|
+
for (const [scriptPath, hash2] of Object.entries(finalHashes)) {
|
|
64563
64716
|
await updateMetadataGlobalLock(appFolder, hash2, scriptPath);
|
|
64564
64717
|
}
|
|
64565
64718
|
if (!noStaleMessage) {
|
|
@@ -64607,7 +64760,7 @@ async function traverseAndProcessInlineScripts(obj, processor, currentPath = [])
|
|
|
64607
64760
|
}
|
|
64608
64761
|
return result;
|
|
64609
64762
|
}
|
|
64610
|
-
async function updateRawAppRunnables(workspace, runnables, remotePath, appFolder, rawDeps, defaultTs = "bun", noStaleMessage) {
|
|
64763
|
+
async function updateRawAppRunnables(workspace, runnables, remotePath, appFolder, rawDeps, defaultTs = "bun", noStaleMessage, tempScriptRefs) {
|
|
64611
64764
|
const updatedRunnables = [];
|
|
64612
64765
|
const runnablesFolder = path11.join(appFolder, APP_BACKEND_FOLDER);
|
|
64613
64766
|
try {
|
|
@@ -64649,7 +64802,7 @@ async function updateRawAppRunnables(workspace, runnables, remotePath, appFolder
|
|
|
64649
64802
|
info(colors.gray(`Generating lock for runnable ${runnableId} (${language})`));
|
|
64650
64803
|
}
|
|
64651
64804
|
try {
|
|
64652
|
-
const lock = await generateInlineScriptLock(workspace, content, language, `${remotePath}/${runnableId}`, rawDeps);
|
|
64805
|
+
const lock = await generateInlineScriptLock(workspace, content, language, `${remotePath}/${runnableId}`, rawDeps, tempScriptRefs);
|
|
64653
64806
|
const [basePathO, ext2] = pathAssigner.assignPath(runnable.name ?? runnableId, language);
|
|
64654
64807
|
const basePath = basePathO.replaceAll(SEP11, "/");
|
|
64655
64808
|
const contentPath = path11.join(runnablesFolder, `${basePath}${ext2}`);
|
|
@@ -64676,7 +64829,7 @@ async function updateRawAppRunnables(workspace, runnables, remotePath, appFolder
|
|
|
64676
64829
|
}
|
|
64677
64830
|
return updatedRunnables;
|
|
64678
64831
|
}
|
|
64679
|
-
async function updateAppInlineScripts(workspace, appValue, remotePath, appFolder, rawDeps, defaultTs = "bun", noStaleMessage) {
|
|
64832
|
+
async function updateAppInlineScripts(workspace, appValue, remotePath, appFolder, rawDeps, defaultTs = "bun", noStaleMessage, tempScriptRefs) {
|
|
64680
64833
|
const pathAssigner = newPathAssigner(defaultTs, { skipInlineScriptSuffix: getNonDottedPaths() });
|
|
64681
64834
|
const updatedScripts = [];
|
|
64682
64835
|
const processor = async (inlineScript, context) => {
|
|
@@ -64697,7 +64850,7 @@ async function updateAppInlineScripts(workspace, appValue, remotePath, appFolder
|
|
|
64697
64850
|
if (!noStaleMessage) {
|
|
64698
64851
|
info(colors.gray(`Generating lock for inline script "${scriptName}" at ${context.path.join(".")} (${language})`));
|
|
64699
64852
|
}
|
|
64700
|
-
lock = await generateInlineScriptLock(workspace, content, language, scriptPath, rawDeps);
|
|
64853
|
+
lock = await generateInlineScriptLock(workspace, content, language, scriptPath, rawDeps, tempScriptRefs);
|
|
64701
64854
|
}
|
|
64702
64855
|
const [basePathO, ext2] = pathAssigner.assignPath(scriptName, language);
|
|
64703
64856
|
const basePath = basePathO.replaceAll(SEP11, "/");
|
|
@@ -64728,7 +64881,7 @@ async function updateAppInlineScripts(workspace, appValue, remotePath, appFolder
|
|
|
64728
64881
|
const updatedValue = await traverseAndProcessInlineScripts(appValue, processor);
|
|
64729
64882
|
return { value: updatedValue, updatedScripts };
|
|
64730
64883
|
}
|
|
64731
|
-
async function generateInlineScriptLock(workspace, content, language, scriptPath, rawWorkspaceDependencies) {
|
|
64884
|
+
async function generateInlineScriptLock(workspace, content, language, scriptPath, rawWorkspaceDependencies, tempScriptRefs) {
|
|
64732
64885
|
const filteredDeps = rawWorkspaceDependencies ? filterWorkspaceDependencies(rawWorkspaceDependencies, content, language) : undefined;
|
|
64733
64886
|
const extraHeaders = getHeaders2();
|
|
64734
64887
|
const rawResponse = await fetch(`${workspace.remote}api/w/${workspace.workspaceId}/jobs/run/dependencies`, {
|
|
@@ -64747,7 +64900,8 @@ async function generateInlineScriptLock(workspace, content, language, scriptPath
|
|
|
64747
64900
|
}
|
|
64748
64901
|
],
|
|
64749
64902
|
raw_workspace_dependencies: filteredDeps && Object.keys(filteredDeps).length > 0 ? filteredDeps : null,
|
|
64750
|
-
entrypoint: scriptPath
|
|
64903
|
+
entrypoint: scriptPath,
|
|
64904
|
+
...tempScriptRefs && Object.keys(tempScriptRefs).length > 0 ? { temp_script_refs: tempScriptRefs } : {}
|
|
64751
64905
|
})
|
|
64752
64906
|
});
|
|
64753
64907
|
if (!rawResponse.ok) {
|
|
@@ -64905,7 +65059,8 @@ var init_app_metadata = __esm(async () => {
|
|
|
64905
65059
|
init_app(),
|
|
64906
65060
|
init_conf(),
|
|
64907
65061
|
init_context(),
|
|
64908
|
-
init_auth()
|
|
65062
|
+
init_auth(),
|
|
65063
|
+
init_relative_imports()
|
|
64909
65064
|
]);
|
|
64910
65065
|
import_yaml17 = __toESM(require_dist(), 1);
|
|
64911
65066
|
});
|
|
@@ -69393,7 +69548,8 @@ async function pushNativeTrigger(workspace, filePath, _remoteTrigger, localTrigg
|
|
|
69393
69548
|
script_path: result.script_path,
|
|
69394
69549
|
is_flow: result.is_flow,
|
|
69395
69550
|
service_config: result.service_config,
|
|
69396
|
-
error: result.error
|
|
69551
|
+
error: result.error,
|
|
69552
|
+
summary: result.summary
|
|
69397
69553
|
};
|
|
69398
69554
|
debug(`Native trigger ${serviceName}/${externalId} exists on remote`);
|
|
69399
69555
|
} catch {
|
|
@@ -69402,18 +69558,21 @@ async function pushNativeTrigger(workspace, filePath, _remoteTrigger, localTrigg
|
|
|
69402
69558
|
const triggerData = {
|
|
69403
69559
|
script_path: localTrigger.script_path,
|
|
69404
69560
|
is_flow: localTrigger.is_flow,
|
|
69405
|
-
service_config: localTrigger.service_config
|
|
69561
|
+
service_config: localTrigger.service_config,
|
|
69562
|
+
summary: localTrigger.summary
|
|
69406
69563
|
};
|
|
69407
69564
|
if (remoteTrigger) {
|
|
69408
69565
|
const localCompare = {
|
|
69409
69566
|
script_path: localTrigger.script_path,
|
|
69410
69567
|
is_flow: localTrigger.is_flow,
|
|
69411
|
-
service_config: localTrigger.service_config
|
|
69568
|
+
service_config: localTrigger.service_config,
|
|
69569
|
+
summary: localTrigger.summary
|
|
69412
69570
|
};
|
|
69413
69571
|
const remoteCompare = {
|
|
69414
69572
|
script_path: remoteTrigger.script_path,
|
|
69415
69573
|
is_flow: remoteTrigger.is_flow,
|
|
69416
|
-
service_config: remoteTrigger.service_config
|
|
69574
|
+
service_config: remoteTrigger.service_config,
|
|
69575
|
+
summary: remoteTrigger.summary
|
|
69417
69576
|
};
|
|
69418
69577
|
if (isSuperset(localCompare, remoteCompare)) {
|
|
69419
69578
|
debug(`Native trigger ${serviceName}/${externalId} is up to date`);
|
|
@@ -70353,6 +70512,10 @@ async function generateLocks(opts, folder) {
|
|
|
70353
70512
|
}
|
|
70354
70513
|
}
|
|
70355
70514
|
if (hasAny) {
|
|
70515
|
+
if (opts.dryRun) {
|
|
70516
|
+
info(colors.gray("Dry run complete."));
|
|
70517
|
+
return;
|
|
70518
|
+
}
|
|
70356
70519
|
if (!opts.yes && !await Confirm.prompt({
|
|
70357
70520
|
message: "Update the locks of the inline scripts of the above flows?",
|
|
70358
70521
|
default: true
|
|
@@ -70408,7 +70571,7 @@ var init_flow = __esm(async () => {
|
|
|
70408
70571
|
]);
|
|
70409
70572
|
import_yaml36 = __toESM(require_dist(), 1);
|
|
70410
70573
|
alreadySynced3 = [];
|
|
70411
|
-
command21 = new Command().description("flow related commands").option("--show-archived", "Enable archived flows in output").option("--json", "Output as JSON (for piping to jq)").action(list11).command("list", "list all flows").option("--show-archived", "Enable archived flows in output").option("--json", "Output as JSON (for piping to jq)").action(list11).command("get", "get a flow's details").arguments("<path:string>").option("--json", "Output as JSON (for piping to jq)").action(get9).command("push", "push a local flow spec. This overrides any remote versions.").arguments("<file_path:string> <remote_path:string>").action(push11).command("run", "run a flow by path.").arguments("<path:string>").option("-d --data <data:string>", "Inputs specified as a JSON string or a file using @<filename> or stdin using @-.").option("-s --silent", "Do not ouput anything other then the final output. Useful for scripting.").action(run3).command("preview", "preview a local flow without deploying it. Runs the flow definition from local files and uses local PathScripts by default.").arguments("<flow_path:string>").option("-d --data <data:string>", "Inputs specified as a JSON string or a file using @<filename> or stdin using @-.").option("-s --silent", "Do not output anything other then the final output. Useful for scripting.").option("--remote", "Use deployed workspace scripts for PathScript steps instead of local files.").action(preview2).command("generate-locks", "re-generate the lock files of all inline scripts of all updated flows").arguments("[flow:file]").option("--yes", "Skip confirmation prompt").option("-i --includes <patterns:file[]>", "Comma separated patterns to specify which file to take into account (among files that are compatible with windmill). Patterns can include * (any string until '/') and ** (any string)").option("-e --excludes <patterns:file[]>", "Comma separated patterns to specify which file to NOT take into account.").action(generateLocks).command("new", "create a new empty flow").arguments("<flow_path:string>").option("--summary <summary:string>", "flow summary").option("--description <description:string>", "flow description").action(bootstrap2).command("bootstrap", "create a new empty flow (alias for new)").arguments("<flow_path:string>").option("--summary <summary:string>", "flow summary").option("--description <description:string>", "flow description").action(bootstrap2);
|
|
70574
|
+
command21 = new Command().description("flow related commands").option("--show-archived", "Enable archived flows in output").option("--json", "Output as JSON (for piping to jq)").action(list11).command("list", "list all flows").option("--show-archived", "Enable archived flows in output").option("--json", "Output as JSON (for piping to jq)").action(list11).command("get", "get a flow's details").arguments("<path:string>").option("--json", "Output as JSON (for piping to jq)").action(get9).command("push", "push a local flow spec. This overrides any remote versions.").arguments("<file_path:string> <remote_path:string>").action(push11).command("run", "run a flow by path.").arguments("<path:string>").option("-d --data <data:string>", "Inputs specified as a JSON string or a file using @<filename> or stdin using @-.").option("-s --silent", "Do not ouput anything other then the final output. Useful for scripting.").action(run3).command("preview", "preview a local flow without deploying it. Runs the flow definition from local files and uses local PathScripts by default.").arguments("<flow_path:string>").option("-d --data <data:string>", "Inputs specified as a JSON string or a file using @<filename> or stdin using @-.").option("-s --silent", "Do not output anything other then the final output. Useful for scripting.").option("--remote", "Use deployed workspace scripts for PathScript steps instead of local files.").action(preview2).command("generate-locks", "re-generate the lock files of all inline scripts of all updated flows").arguments("[flow:file]").option("--yes", "Skip confirmation prompt").option("--dry-run", "Perform a dry run without making changes").option("-i --includes <patterns:file[]>", "Comma separated patterns to specify which file to take into account (among files that are compatible with windmill). Patterns can include * (any string until '/') and ** (any string)").option("-e --excludes <patterns:file[]>", "Comma separated patterns to specify which file to NOT take into account.").action(generateLocks).command("new", "create a new empty flow").arguments("<flow_path:string>").option("--summary <summary:string>", "flow summary").option("--description <description:string>", "flow description").action(bootstrap2).command("bootstrap", "create a new empty flow (alias for new)").arguments("<flow_path:string>").option("--summary <summary:string>", "flow summary").option("--description <description:string>", "flow description").action(bootstrap2);
|
|
70412
70575
|
flow_default = command21;
|
|
70413
70576
|
});
|
|
70414
70577
|
|
|
@@ -71359,6 +71522,277 @@ var init_gitsync_settings = __esm(async () => {
|
|
|
71359
71522
|
gitsync_settings_default = command23;
|
|
71360
71523
|
});
|
|
71361
71524
|
|
|
71525
|
+
// src/utils/dependency_tree.ts
|
|
71526
|
+
async function uploadScripts(tree, workspace) {
|
|
71527
|
+
const scriptHashes = {};
|
|
71528
|
+
const workspaceDeps = [];
|
|
71529
|
+
for (const path19 of tree.allPaths()) {
|
|
71530
|
+
const content = tree.getContent(path19);
|
|
71531
|
+
const itemType = tree.getItemType(path19);
|
|
71532
|
+
if (itemType === "dependencies") {
|
|
71533
|
+
if (content === undefined)
|
|
71534
|
+
continue;
|
|
71535
|
+
const info2 = workspaceDependenciesPathToLanguageAndFilename(path19);
|
|
71536
|
+
if (info2) {
|
|
71537
|
+
const hash2 = await generateHash(content);
|
|
71538
|
+
workspaceDeps.push({ path: path19, language: info2.language, name: info2.name, hash: hash2 });
|
|
71539
|
+
}
|
|
71540
|
+
} else if (itemType === "script") {
|
|
71541
|
+
if (!content)
|
|
71542
|
+
continue;
|
|
71543
|
+
const hash2 = await generateHash(content);
|
|
71544
|
+
scriptHashes[path19] = hash2;
|
|
71545
|
+
}
|
|
71546
|
+
}
|
|
71547
|
+
if (Object.keys(scriptHashes).length === 0 && workspaceDeps.length === 0)
|
|
71548
|
+
return;
|
|
71549
|
+
const mismatched = await diffRawScriptsWithDeployed({
|
|
71550
|
+
workspace: workspace.workspaceId,
|
|
71551
|
+
requestBody: {
|
|
71552
|
+
scripts: scriptHashes,
|
|
71553
|
+
workspace_deps: workspaceDeps
|
|
71554
|
+
}
|
|
71555
|
+
});
|
|
71556
|
+
for (const path19 of mismatched) {
|
|
71557
|
+
const content = tree.getContent(path19);
|
|
71558
|
+
const itemType = tree.getItemType(path19);
|
|
71559
|
+
if (itemType === "dependencies") {
|
|
71560
|
+
if (content !== undefined) {
|
|
71561
|
+
tree.setContentHash(path19, "mismatched");
|
|
71562
|
+
}
|
|
71563
|
+
} else if (content) {
|
|
71564
|
+
const hash2 = await storeRawScriptTemp({
|
|
71565
|
+
workspace: workspace.workspaceId,
|
|
71566
|
+
requestBody: content
|
|
71567
|
+
});
|
|
71568
|
+
tree.setContentHash(path19, hash2);
|
|
71569
|
+
}
|
|
71570
|
+
}
|
|
71571
|
+
}
|
|
71572
|
+
|
|
71573
|
+
class DoubleLinkedDependencyTree {
|
|
71574
|
+
nodes = new Map;
|
|
71575
|
+
workspaceDeps = {};
|
|
71576
|
+
setWorkspaceDeps(deps) {
|
|
71577
|
+
this.workspaceDeps = deps;
|
|
71578
|
+
}
|
|
71579
|
+
async addNode(path19, content, language, metadata, imports, itemType, folder, originalPath, isDirectlyStale, isRawApp) {
|
|
71580
|
+
const hasWorkspaceDeps = itemType === "script" || itemType === "inline_script";
|
|
71581
|
+
const filteredDeps = hasWorkspaceDeps ? filterWorkspaceDependencies(this.workspaceDeps, content, language) : {};
|
|
71582
|
+
const stalenessHash = await generateScriptHash({}, content, metadata);
|
|
71583
|
+
if (!this.nodes.has(path19)) {
|
|
71584
|
+
this.nodes.set(path19, {
|
|
71585
|
+
content: "",
|
|
71586
|
+
stalenessHash: "",
|
|
71587
|
+
language: "deno",
|
|
71588
|
+
metadata: "",
|
|
71589
|
+
imports: new Set,
|
|
71590
|
+
importedBy: new Set,
|
|
71591
|
+
itemType: "script",
|
|
71592
|
+
folder: "",
|
|
71593
|
+
originalPath: "",
|
|
71594
|
+
isDirectlyStale: false
|
|
71595
|
+
});
|
|
71596
|
+
}
|
|
71597
|
+
const node = this.nodes.get(path19);
|
|
71598
|
+
node.content = content;
|
|
71599
|
+
node.stalenessHash = stalenessHash;
|
|
71600
|
+
node.language = language;
|
|
71601
|
+
node.metadata = metadata;
|
|
71602
|
+
node.itemType = itemType;
|
|
71603
|
+
node.folder = folder;
|
|
71604
|
+
node.originalPath = originalPath;
|
|
71605
|
+
node.isDirectlyStale = isDirectlyStale;
|
|
71606
|
+
node.isRawApp = isRawApp;
|
|
71607
|
+
const filteredDepsPaths = Object.keys(filteredDeps);
|
|
71608
|
+
for (const depsPath of filteredDepsPaths) {
|
|
71609
|
+
if (!this.nodes.has(depsPath)) {
|
|
71610
|
+
const depsInfo = workspaceDependenciesPathToLanguageAndFilename(depsPath);
|
|
71611
|
+
const contentHash = await generateHash(filteredDeps[depsPath] + depsPath);
|
|
71612
|
+
const isUpToDate = await checkifMetadataUptodate(depsPath, contentHash, undefined);
|
|
71613
|
+
this.nodes.set(depsPath, {
|
|
71614
|
+
content: filteredDeps[depsPath],
|
|
71615
|
+
stalenessHash: "",
|
|
71616
|
+
language: depsInfo?.language ?? "deno",
|
|
71617
|
+
metadata: "",
|
|
71618
|
+
imports: new Set,
|
|
71619
|
+
importedBy: new Set,
|
|
71620
|
+
itemType: "dependencies",
|
|
71621
|
+
folder: "",
|
|
71622
|
+
originalPath: depsPath,
|
|
71623
|
+
isDirectlyStale: !isUpToDate
|
|
71624
|
+
});
|
|
71625
|
+
}
|
|
71626
|
+
}
|
|
71627
|
+
const allImports = [...imports, ...filteredDepsPaths];
|
|
71628
|
+
for (const importPath of allImports) {
|
|
71629
|
+
node.imports.add(importPath);
|
|
71630
|
+
if (!this.nodes.has(importPath)) {
|
|
71631
|
+
this.nodes.set(importPath, {
|
|
71632
|
+
content: "",
|
|
71633
|
+
stalenessHash: "",
|
|
71634
|
+
language: "deno",
|
|
71635
|
+
metadata: "",
|
|
71636
|
+
imports: new Set,
|
|
71637
|
+
importedBy: new Set,
|
|
71638
|
+
itemType: "script",
|
|
71639
|
+
folder: "",
|
|
71640
|
+
originalPath: "",
|
|
71641
|
+
isDirectlyStale: false
|
|
71642
|
+
});
|
|
71643
|
+
}
|
|
71644
|
+
this.nodes.get(importPath).importedBy.add(path19);
|
|
71645
|
+
}
|
|
71646
|
+
}
|
|
71647
|
+
getContent(path19) {
|
|
71648
|
+
return this.nodes.get(path19)?.content;
|
|
71649
|
+
}
|
|
71650
|
+
getStalenessHash(path19) {
|
|
71651
|
+
return this.nodes.get(path19)?.stalenessHash;
|
|
71652
|
+
}
|
|
71653
|
+
getContentHash(path19) {
|
|
71654
|
+
return this.nodes.get(path19)?.contentHash;
|
|
71655
|
+
}
|
|
71656
|
+
setContentHash(path19, hash2) {
|
|
71657
|
+
const node = this.nodes.get(path19);
|
|
71658
|
+
if (node) {
|
|
71659
|
+
node.contentHash = hash2;
|
|
71660
|
+
}
|
|
71661
|
+
}
|
|
71662
|
+
getLanguage(path19) {
|
|
71663
|
+
return this.nodes.get(path19)?.language;
|
|
71664
|
+
}
|
|
71665
|
+
getMetadata(path19) {
|
|
71666
|
+
return this.nodes.get(path19)?.metadata;
|
|
71667
|
+
}
|
|
71668
|
+
getStaleReason(path19) {
|
|
71669
|
+
return this.nodes.get(path19)?.staleReason;
|
|
71670
|
+
}
|
|
71671
|
+
getItemType(path19) {
|
|
71672
|
+
return this.nodes.get(path19)?.itemType;
|
|
71673
|
+
}
|
|
71674
|
+
getFolder(path19) {
|
|
71675
|
+
return this.nodes.get(path19)?.folder;
|
|
71676
|
+
}
|
|
71677
|
+
getIsRawApp(path19) {
|
|
71678
|
+
return this.nodes.get(path19)?.isRawApp;
|
|
71679
|
+
}
|
|
71680
|
+
getIsDirectlyStale(path19) {
|
|
71681
|
+
return this.nodes.get(path19)?.isDirectlyStale ?? false;
|
|
71682
|
+
}
|
|
71683
|
+
getOriginalPath(path19) {
|
|
71684
|
+
return this.nodes.get(path19)?.originalPath;
|
|
71685
|
+
}
|
|
71686
|
+
getImports(path19) {
|
|
71687
|
+
return this.nodes.get(path19)?.imports;
|
|
71688
|
+
}
|
|
71689
|
+
isStale(path19) {
|
|
71690
|
+
return this.nodes.get(path19)?.staleReason !== undefined;
|
|
71691
|
+
}
|
|
71692
|
+
propagateStaleness() {
|
|
71693
|
+
const directlyStale = new Set;
|
|
71694
|
+
for (const [path19, node] of this.nodes.entries()) {
|
|
71695
|
+
if (node.isDirectlyStale) {
|
|
71696
|
+
directlyStale.add(path19);
|
|
71697
|
+
node.staleReason = "content changed";
|
|
71698
|
+
}
|
|
71699
|
+
}
|
|
71700
|
+
const allStale = new Set(directlyStale);
|
|
71701
|
+
const queue = [...directlyStale];
|
|
71702
|
+
const visited = new Set;
|
|
71703
|
+
while (queue.length > 0) {
|
|
71704
|
+
const scriptPath = queue.shift();
|
|
71705
|
+
if (visited.has(scriptPath))
|
|
71706
|
+
continue;
|
|
71707
|
+
visited.add(scriptPath);
|
|
71708
|
+
const node = this.nodes.get(scriptPath);
|
|
71709
|
+
if (!node)
|
|
71710
|
+
continue;
|
|
71711
|
+
for (const importer of node.importedBy) {
|
|
71712
|
+
if (!allStale.has(importer)) {
|
|
71713
|
+
allStale.add(importer);
|
|
71714
|
+
queue.push(importer);
|
|
71715
|
+
const importerNode = this.nodes.get(importer);
|
|
71716
|
+
if (importerNode)
|
|
71717
|
+
importerNode.staleReason = `depends on ${scriptPath}`;
|
|
71718
|
+
}
|
|
71719
|
+
}
|
|
71720
|
+
}
|
|
71721
|
+
}
|
|
71722
|
+
traverseTransitive(scriptPath, callback) {
|
|
71723
|
+
const queue = [scriptPath];
|
|
71724
|
+
const visited = new Set;
|
|
71725
|
+
while (queue.length > 0) {
|
|
71726
|
+
const current = queue.shift();
|
|
71727
|
+
if (visited.has(current))
|
|
71728
|
+
continue;
|
|
71729
|
+
visited.add(current);
|
|
71730
|
+
const node = this.nodes.get(current);
|
|
71731
|
+
if (!node)
|
|
71732
|
+
continue;
|
|
71733
|
+
for (const importPath of node.imports) {
|
|
71734
|
+
const importNode = this.nodes.get(importPath);
|
|
71735
|
+
if (importNode) {
|
|
71736
|
+
const stop = callback(importPath, importNode);
|
|
71737
|
+
if (!stop) {
|
|
71738
|
+
queue.push(importPath);
|
|
71739
|
+
}
|
|
71740
|
+
}
|
|
71741
|
+
}
|
|
71742
|
+
}
|
|
71743
|
+
}
|
|
71744
|
+
allPaths() {
|
|
71745
|
+
return this.nodes.keys();
|
|
71746
|
+
}
|
|
71747
|
+
*stalePaths() {
|
|
71748
|
+
for (const [path19, node] of this.nodes.entries()) {
|
|
71749
|
+
if (node.staleReason) {
|
|
71750
|
+
yield path19;
|
|
71751
|
+
}
|
|
71752
|
+
}
|
|
71753
|
+
}
|
|
71754
|
+
has(path19) {
|
|
71755
|
+
return this.nodes.has(path19);
|
|
71756
|
+
}
|
|
71757
|
+
getMismatchedWorkspaceDeps() {
|
|
71758
|
+
const result = {};
|
|
71759
|
+
for (const [path19, node] of this.nodes.entries()) {
|
|
71760
|
+
if (node.itemType === "dependencies" && node.contentHash && node.content !== undefined) {
|
|
71761
|
+
result[path19] = node.content;
|
|
71762
|
+
}
|
|
71763
|
+
}
|
|
71764
|
+
return result;
|
|
71765
|
+
}
|
|
71766
|
+
getTempScriptRefs(scriptPath) {
|
|
71767
|
+
const result = {};
|
|
71768
|
+
this.traverseTransitive(scriptPath, (_path, node) => {
|
|
71769
|
+
if (node.contentHash) {
|
|
71770
|
+
result[_path] = node.contentHash;
|
|
71771
|
+
}
|
|
71772
|
+
});
|
|
71773
|
+
return result;
|
|
71774
|
+
}
|
|
71775
|
+
async persistDepsHashes(depsPaths) {
|
|
71776
|
+
for (const path19 of depsPaths) {
|
|
71777
|
+
const node = this.nodes.get(path19);
|
|
71778
|
+
if (node?.itemType === "dependencies" && node.content !== undefined) {
|
|
71779
|
+
const hash2 = await generateHash(node.content + path19);
|
|
71780
|
+
await updateMetadataGlobalLock(path19, hash2);
|
|
71781
|
+
}
|
|
71782
|
+
}
|
|
71783
|
+
}
|
|
71784
|
+
get size() {
|
|
71785
|
+
return this.nodes.size;
|
|
71786
|
+
}
|
|
71787
|
+
}
|
|
71788
|
+
var init_dependency_tree = __esm(async () => {
|
|
71789
|
+
init_services_gen();
|
|
71790
|
+
await __promiseAll([
|
|
71791
|
+
init_metadata(),
|
|
71792
|
+
init_utils()
|
|
71793
|
+
]);
|
|
71794
|
+
});
|
|
71795
|
+
|
|
71362
71796
|
// src/main.ts
|
|
71363
71797
|
init_mod3();
|
|
71364
71798
|
|
|
@@ -77400,7 +77834,7 @@ Reference a specific resource using \`$res:\` prefix:
|
|
|
77400
77834
|
|
|
77401
77835
|
## OpenFlow Schema
|
|
77402
77836
|
|
|
77403
|
-
{"OpenFlow":{"type":"object","description":"Top-level flow definition containing metadata, configuration, and the flow structure","properties":{"summary":{"type":"string","description":"Short description of what this flow does"},"description":{"type":"string","description":"Detailed documentation for this flow"},"value":{"$ref":"#/components/schemas/FlowValue"},"schema":{"type":"object","description":"JSON Schema for flow inputs. Use this to define input parameters, their types, defaults, and validation. For resource inputs, set type to 'object' and format to 'resource-<type>' (e.g., 'resource-stripe')"},"on_behalf_of_email":{"type":"string","description":"The flow will be run with the permissions of the user with this email."}},"required":["summary","value"]},"FlowValue":{"type":"object","description":"The flow structure containing modules and optional preprocessor/failure handlers","properties":{"modules":{"type":"array","description":"Array of steps that execute in sequence. Each step can be a script, subflow, loop, or branch","items":{"$ref":"#/components/schemas/FlowModule"}},"failure_module":{"description":"Special module that executes when the flow fails. Receives error object with message, name, stack, and step_id. Must have id 'failure'. Only supports script/rawscript types","$ref":"#/components/schemas/FlowModule"},"preprocessor_module":{"description":"Special module that runs before the first step on external triggers. Must have id 'preprocessor'. Only supports script/rawscript types. Cannot reference other step results","$ref":"#/components/schemas/FlowModule"},"same_worker":{"type":"boolean","description":"If true, all steps run on the same worker for better performance"},"concurrent_limit":{"type":"number","description":"Maximum number of concurrent executions of this flow"},"concurrency_key":{"type":"string","description":"Expression to group concurrent executions (e.g., by user ID)"},"concurrency_time_window_s":{"type":"number","description":"Time window in seconds for concurrent_limit"},"debounce_delay_s":{"type":"number","description":"Delay in seconds to debounce flow executions"},"debounce_key":{"type":"string","description":"Expression to group debounced executions"},"debounce_args_to_accumulate":{"type":"array","description":"Arguments to accumulate across debounced executions","items":{"type":"string"}},"max_total_debouncing_time":{"type":"number","description":"Maximum total time in seconds that a job can be debounced"},"max_total_debounces_amount":{"type":"number","description":"Maximum number of times a job can be debounced"},"skip_expr":{"type":"string","description":"JavaScript expression to conditionally skip the entire flow"},"cache_ttl":{"type":"number","description":"Cache duration in seconds for flow results"},"cache_ignore_s3_path":{"type":"boolean"},"flow_env":{"type":"object","description":"Environment variables available to all steps. Values can be strings, JSON values, or special references: '$var:path' (workspace variable) or '$res:path' (resource).","additionalProperties":{}},"priority":{"type":"number","description":"Execution priority (higher numbers run first)"},"early_return":{"type":"string","description":"JavaScript expression to return early from the flow"},"chat_input_enabled":{"type":"boolean","description":"Whether this flow accepts chat-style input"},"notes":{"type":"array","description":"Sticky notes attached to the flow","items":{"$ref":"#/components/schemas/FlowNote"}}},"required":["modules"]},"Retry":{"type":"object","description":"Retry configuration for failed module executions","properties":{"constant":{"type":"object","description":"Retry with constant delay between attempts","properties":{"attempts":{"type":"integer","description":"Number of retry attempts"},"seconds":{"type":"integer","description":"Seconds to wait between retries"}}},"exponential":{"type":"object","description":"Retry with exponential backoff (delay doubles each time)","properties":{"attempts":{"type":"integer","description":"Number of retry attempts"},"multiplier":{"type":"integer","description":"Multiplier for exponential backoff"},"seconds":{"type":"integer","minimum":1,"description":"Initial delay in seconds"},"random_factor":{"type":"integer","minimum":0,"maximum":100,"description":"Random jitter percentage (0-100) to avoid thundering herd"}}},"retry_if":{"$ref":"#/components/schemas/RetryIf"}}},"FlowNote":{"type":"object","description":"A sticky note attached to a flow for documentation and annotation","properties":{"id":{"type":"string","description":"Unique identifier for the note"},"text":{"type":"string","description":"Content of the note"},"position":{"type":"object","description":"Position of the note in the flow editor","properties":{"x":{"type":"number","description":"X coordinate"},"y":{"type":"number","description":"Y coordinate"}},"required":["x","y"]},"size":{"type":"object","description":"Size of the note in the flow editor","properties":{"width":{"type":"number","description":"Width in pixels"},"height":{"type":"number","description":"Height in pixels"}},"required":["width","height"]},"color":{"type":"string","description":"Color of the note (e.g., \\"yellow\\", \\"#ffff00\\")"},"type":{"type":"string","enum":["free","group"],"description":"Type of note - 'free' for standalone notes, 'group' for notes that group other nodes"},"locked":{"type":"boolean","default":false,"description":"Whether the note is locked and cannot be edited or moved"},"contained_node_ids":{"type":"array","items":{"type":"string"},"description":"For group notes, the IDs of nodes contained within this group"}},"required":["id","text","color","type"]},"RetryIf":{"type":"object","description":"Conditional retry based on error or result","properties":{"expr":{"type":"string","description":"JavaScript expression that returns true to retry. Has access to 'result' and 'error' variables"}},"required":["expr"]},"StopAfterIf":{"type":"object","description":"Early termination condition for a module","properties":{"skip_if_stopped":{"type":"boolean","description":"If true, following steps are skipped when this condition triggers"},"expr":{"type":"string","description":"JavaScript expression evaluated after the module runs. Can use 'result' (step's result) or 'flow_input'. Return true to stop"},"error_message":{"type":"string","description":"Custom error message shown when stopping"}},"required":["expr"]},"FlowModule":{"type":"object","description":"A single step in a flow. Can be a script, subflow, loop, or branch","properties":{"id":{"type":"string","description":"Unique identifier for this step. Used to reference results via 'results.step_id'. Must be a valid identifier (alphanumeric, underscore, hyphen)"},"value":{"$ref":"#/components/schemas/FlowModuleValue"},"stop_after_if":{"description":"Early termination condition evaluated after this step completes","$ref":"#/components/schemas/StopAfterIf"},"stop_after_all_iters_if":{"description":"For loops only - early termination condition evaluated after all iterations complete","$ref":"#/components/schemas/StopAfterIf"},"skip_if":{"type":"object","description":"Conditionally skip this step based on previous results or flow inputs","properties":{"expr":{"type":"string","description":"JavaScript expression that returns true to skip. Can use 'flow_input' or 'results.<step_id>'"}},"required":["expr"]},"sleep":{"description":"Delay before executing this step (in seconds or as expression)","$ref":"#/components/schemas/InputTransform"},"cache_ttl":{"type":"number","description":"Cache duration in seconds for this step's results"},"cache_ignore_s3_path":{"type":"boolean"},"timeout":{"description":"Maximum execution time in seconds (static value or expression)","$ref":"#/components/schemas/InputTransform"},"delete_after_use":{"type":"boolean","description":"If true, this step's result is deleted after use to save memory"},"summary":{"type":"string","description":"Short description of what this step does"},"mock":{"type":"object","description":"Mock configuration for testing without executing the actual step","properties":{"enabled":{"type":"boolean","description":"If true, return mock value instead of executing"},"return_value":{"description":"Value to return when mocked"}}},"suspend":{"type":"object","description":"Configuration for approval/resume steps that wait for user input","properties":{"required_events":{"type":"integer","description":"Number of approvals required before continuing"},"timeout":{"type":"integer","description":"Timeout in seconds before auto-continuing or canceling"},"resume_form":{"type":"object","description":"Form schema for collecting input when resuming","properties":{"schema":{"type":"object","description":"JSON Schema for the resume form"}}},"user_auth_required":{"type":"boolean","description":"If true, only authenticated users can approve"},"user_groups_required":{"description":"Expression or list of groups that can approve","$ref":"#/components/schemas/InputTransform"},"self_approval_disabled":{"type":"boolean","description":"If true, the user who started the flow cannot approve"},"hide_cancel":{"type":"boolean","description":"If true, hide the cancel button on the approval form"},"continue_on_disapprove_timeout":{"type":"boolean","description":"If true, continue flow on timeout instead of canceling"}}},"priority":{"type":"number","description":"Execution priority for this step (higher numbers run first)"},"continue_on_error":{"type":"boolean","description":"If true, flow continues even if this step fails"},"retry":{"description":"Retry configuration if this step fails","$ref":"#/components/schemas/Retry"}},"required":["value","id"]},"InputTransform":{"description":"Maps input parameters for a step. Can be a static value or a JavaScript expression that references previous results or flow inputs","oneOf":[{"$ref":"#/components/schemas/StaticTransform"},{"$ref":"#/components/schemas/JavascriptTransform"},{"$ref":"#/components/schemas/AiTransform"}],"discriminator":{"propertyName":"type","mapping":{"static":"#/components/schemas/StaticTransform","javascript":"#/components/schemas/JavascriptTransform","ai":"#/components/schemas/AiTransform"}}},"StaticTransform":{"type":"object","description":"Static value passed directly to the step. Use for hardcoded values or resource references like '$res:path/to/resource'","properties":{"value":{"description":"The static value. For resources, use format '$res:path/to/resource'"},"type":{"type":"string","enum":["static"]}},"required":["type"]},"JavascriptTransform":{"type":"object","description":"JavaScript expression evaluated at runtime. Can reference previous step results via 'results.step_id' or flow inputs via 'flow_input.property'. Inside loops, use 'flow_input.iter.value' for the current iteration value","properties":{"expr":{"type":"string","description":"JavaScript expression returning the value. Available variables - results (object with all previous step results), flow_input (flow inputs), flow_input.iter (in loops)"},"type":{"type":"string","enum":["javascript"]}},"required":["expr","type"]},"AiTransform":{"type":"object","description":"Value resolved by the AI runtime for this input. The AI engine decides how to satisfy the parameter.","properties":{"type":{"type":"string","enum":["ai"]}},"required":["type"]},"AIProviderKind":{"type":"string","description":"Supported AI provider types","enum":["openai","azure_openai","anthropic","mistral","deepseek","googleai","groq","openrouter","togetherai","customai","aws_bedrock"]},"ProviderConfig":{"type":"object","description":"Complete AI provider configuration with resource reference and model selection","properties":{"kind":{"$ref":"#/components/schemas/AIProviderKind"},"resource":{"type":"string","description":"Resource reference in format '$res:{resource_path}' pointing to provider credentials"},"model":{"type":"string","description":"Model identifier (e.g., 'gpt-4', 'claude-3-opus-20240229', 'gemini-pro')"}},"required":["kind","resource","model"]},"StaticProviderTransform":{"type":"object","description":"Static provider configuration passed directly to the AI agent","properties":{"value":{"$ref":"#/components/schemas/ProviderConfig"},"type":{"type":"string","enum":["static"]}},"required":["type","value"]},"ProviderTransform":{"description":"Provider configuration - can be static (ProviderConfig), JavaScript expression, or AI-determined","oneOf":[{"$ref":"#/components/schemas/StaticProviderTransform"},{"$ref":"#/components/schemas/JavascriptTransform"},{"$ref":"#/components/schemas/AiTransform"}],"discriminator":{"propertyName":"type","mapping":{"static":"#/components/schemas/StaticProviderTransform","javascript":"#/components/schemas/JavascriptTransform","ai":"#/components/schemas/AiTransform"}}},"MemoryOff":{"type":"object","description":"No conversation memory/context","properties":{"kind":{"type":"string","enum":["off"]}},"required":["kind"]},"MemoryAuto":{"type":"object","description":"Automatic context management","properties":{"kind":{"type":"string","enum":["auto"]},"context_length":{"type":"integer","description":"Maximum number of messages to retain in context"},"memory_id":{"type":"string","description":"Identifier for persistent memory across agent invocations"}},"required":["kind"]},"MemoryMessage":{"type":"object","description":"A single message in conversation history","properties":{"role":{"type":"string","enum":["user","assistant","system"]},"content":{"type":"string"}},"required":["role","content"]},"MemoryManual":{"type":"object","description":"Explicit message history","properties":{"kind":{"type":"string","enum":["manual"]},"messages":{"type":"array","items":{"$ref":"#/components/schemas/MemoryMessage"}}},"required":["kind","messages"]},"MemoryConfig":{"description":"Conversation memory configuration","oneOf":[{"$ref":"#/components/schemas/MemoryOff"},{"$ref":"#/components/schemas/MemoryAuto"},{"$ref":"#/components/schemas/MemoryManual"}],"discriminator":{"propertyName":"kind","mapping":{"off":"#/components/schemas/MemoryOff","auto":"#/components/schemas/MemoryAuto","manual":"#/components/schemas/MemoryManual"}}},"StaticMemoryTransform":{"type":"object","description":"Static memory configuration passed directly to the AI agent","properties":{"value":{"$ref":"#/components/schemas/MemoryConfig"},"type":{"type":"string","enum":["static"]}},"required":["type","value"]},"MemoryTransform":{"description":"Memory configuration - can be static (MemoryConfig), JavaScript expression, or AI-determined","oneOf":[{"$ref":"#/components/schemas/StaticMemoryTransform"},{"$ref":"#/components/schemas/JavascriptTransform"},{"$ref":"#/components/schemas/AiTransform"}],"discriminator":{"propertyName":"type","mapping":{"static":"#/components/schemas/StaticMemoryTransform","javascript":"#/components/schemas/JavascriptTransform","ai":"#/components/schemas/AiTransform"}}},"FlowModuleValue":{"description":"The actual implementation of a flow step. Can be a script (inline or referenced), subflow, loop, branch, or special module type","oneOf":[{"$ref":"#/components/schemas/RawScript"},{"$ref":"#/components/schemas/PathScript"},{"$ref":"#/components/schemas/PathFlow"},{"$ref":"#/components/schemas/ForloopFlow"},{"$ref":"#/components/schemas/WhileloopFlow"},{"$ref":"#/components/schemas/BranchOne"},{"$ref":"#/components/schemas/BranchAll"},{"$ref":"#/components/schemas/Identity"},{"$ref":"#/components/schemas/AiAgent"}],"discriminator":{"propertyName":"type","mapping":{"rawscript":"#/components/schemas/RawScript","script":"#/components/schemas/PathScript","flow":"#/components/schemas/PathFlow","forloopflow":"#/components/schemas/ForloopFlow","whileloopflow":"#/components/schemas/WhileloopFlow","branchone":"#/components/schemas/BranchOne","branchall":"#/components/schemas/BranchAll","identity":"#/components/schemas/Identity","aiagent":"#/components/schemas/AiAgent"}}},"RawScript":{"type":"object","description":"Inline script with code defined directly in the flow. Use 'bun' as default language if unspecified. The script receives arguments from input_transforms","properties":{"input_transforms":{"type":"object","description":"Map of parameter names to their values (static or JavaScript expressions). These become the script's input arguments","additionalProperties":{"$ref":"#/components/schemas/InputTransform"}},"content":{"type":"string","description":"The script source code. Should export a 'main' function"},"language":{"type":"string","description":"Programming language for this script","enum":["deno","bun","python3","go","bash","powershell","postgresql","mysql","bigquery","snowflake","mssql","oracledb","graphql","nativets","php","rust","ansible","csharp","nu","java","ruby","duckdb"]},"path":{"type":"string","description":"Optional path for saving this script"},"lock":{"type":"string","description":"Lock file content for dependencies"},"type":{"type":"string","enum":["rawscript"]},"tag":{"type":"string","description":"Worker group tag for execution routing"},"concurrent_limit":{"type":"number","description":"Maximum concurrent executions of this script"},"concurrency_time_window_s":{"type":"number","description":"Time window for concurrent_limit"},"custom_concurrency_key":{"type":"string","description":"Custom key for grouping concurrent executions"},"is_trigger":{"type":"boolean","description":"If true, this script is a trigger that can start the flow"},"assets":{"type":"array","description":"External resources this script accesses (S3 objects, resources, etc.)","items":{"type":"object","required":["path","kind"],"properties":{"path":{"type":"string","description":"Path to the asset"},"kind":{"type":"string","description":"Type of asset","enum":["s3object","resource","ducklake","datatable","volume"]},"access_type":{"type":"string","nullable":true,"description":"Access level for this asset","enum":["r","w","rw"]},"alt_access_type":{"type":"string","nullable":true,"description":"Alternative access level","enum":["r","w","rw"]}}}}},"required":["type","content","language","input_transforms"]},"PathScript":{"type":"object","description":"Reference to an existing script by path. Use this when calling a previously saved script instead of writing inline code","properties":{"input_transforms":{"type":"object","description":"Map of parameter names to their values (static or JavaScript expressions). These become the script's input arguments","additionalProperties":{"$ref":"#/components/schemas/InputTransform"}},"path":{"type":"string","description":"Path to the script in the workspace (e.g., 'f/scripts/send_email')"},"hash":{"type":"string","description":"Optional specific version hash of the script to use"},"type":{"type":"string","enum":["script"]},"tag_override":{"type":"string","description":"Override the script's default worker group tag"},"is_trigger":{"type":"boolean","description":"If true, this script is a trigger that can start the flow"}},"required":["type","path","input_transforms"]},"PathFlow":{"type":"object","description":"Reference to an existing flow by path. Use this to call another flow as a subflow","properties":{"input_transforms":{"type":"object","description":"Map of parameter names to their values (static or JavaScript expressions). These become the subflow's input arguments","additionalProperties":{"$ref":"#/components/schemas/InputTransform"}},"path":{"type":"string","description":"Path to the flow in the workspace (e.g., 'f/flows/process_user')"},"type":{"type":"string","enum":["flow"]}},"required":["type","path","input_transforms"]},"ForloopFlow":{"type":"object","description":"Executes nested modules in a loop over an iterator. Inside the loop, use 'flow_input.iter.value' to access the current iteration value, and 'flow_input.iter.index' for the index. Supports parallel execution for better performance on I/O-bound operations","properties":{"modules":{"type":"array","description":"Steps to execute for each iteration. These can reference the iteration value via 'flow_input.iter.value'","items":{"$ref":"#/components/schemas/FlowModule"}},"iterator":{"description":"JavaScript expression that returns an array to iterate over. Can reference 'results.step_id' or 'flow_input'","$ref":"#/components/schemas/InputTransform"},"skip_failures":{"type":"boolean","description":"If true, iteration failures don't stop the loop. Failed iterations return null"},"type":{"type":"string","enum":["forloopflow"]},"parallel":{"type":"boolean","description":"If true, iterations run concurrently (faster for I/O-bound operations). Use with parallelism to control concurrency"},"parallelism":{"description":"Maximum number of concurrent iterations when parallel=true. Limits resource usage. Can be static number or expression","$ref":"#/components/schemas/InputTransform"},"squash":{"type":"boolean"}},"required":["modules","iterator","skip_failures","type"]},"WhileloopFlow":{"type":"object","description":"Executes nested modules repeatedly while a condition is true. The loop checks the condition after each iteration. Use stop_after_if on modules to control loop termination","properties":{"modules":{"type":"array","description":"Steps to execute in each iteration. Use stop_after_if to control when the loop ends","items":{"$ref":"#/components/schemas/FlowModule"}},"skip_failures":{"type":"boolean","description":"If true, iteration failures don't stop the loop. Failed iterations return null"},"type":{"type":"string","enum":["whileloopflow"]},"parallel":{"type":"boolean","description":"If true, iterations run concurrently (use with caution in while loops)"},"parallelism":{"description":"Maximum number of concurrent iterations when parallel=true","$ref":"#/components/schemas/InputTransform"},"squash":{"type":"boolean"}},"required":["modules","skip_failures","type"]},"BranchOne":{"type":"object","description":"Conditional branching where only the first matching branch executes. Branches are evaluated in order, and the first one with a true expression runs. If no branches match, the default branch executes","properties":{"branches":{"type":"array","description":"Array of branches to evaluate in order. The first branch with expr evaluating to true executes","items":{"type":"object","properties":{"summary":{"type":"string","description":"Short description of this branch condition"},"expr":{"type":"string","description":"JavaScript expression that returns boolean. Can use 'results.step_id' or 'flow_input'. First true expr wins"},"modules":{"type":"array","description":"Steps to execute if this branch's expr is true","items":{"$ref":"#/components/schemas/FlowModule"}}},"required":["modules","expr"]}},"default":{"type":"array","description":"Steps to execute if no branch expressions match","items":{"$ref":"#/components/schemas/FlowModule"}},"type":{"type":"string","enum":["branchone"]}},"required":["branches","default","type"]},"BranchAll":{"type":"object","description":"Parallel branching where all branches execute simultaneously. Unlike BranchOne, all branches run regardless of conditions. Useful for executing independent tasks concurrently","properties":{"branches":{"type":"array","description":"Array of branches that all execute (either in parallel or sequentially)","items":{"type":"object","properties":{"summary":{"type":"string","description":"Short description of this branch's purpose"},"skip_failure":{"type":"boolean","description":"If true, failure in this branch doesn't fail the entire flow"},"modules":{"type":"array","description":"Steps to execute in this branch","items":{"$ref":"#/components/schemas/FlowModule"}}},"required":["modules"]}},"type":{"type":"string","enum":["branchall"]},"parallel":{"type":"boolean","description":"If true, all branches execute concurrently. If false, they execute sequentially"}},"required":["branches","type"]},"AgentTool":{"type":"object","description":"A tool available to an AI agent. Can be a flow module or an external MCP (Model Context Protocol) tool","properties":{"id":{"type":"string","description":"Unique identifier for this tool. Cannot contain spaces - use underscores instead (e.g., 'get_user_data' not 'get user data')"},"summary":{"type":"string","description":"Short description of what this tool does (shown to the AI)"},"value":{"$ref":"#/components/schemas/ToolValue"}},"required":["id","value"]},"ToolValue":{"description":"The implementation of a tool. Can be a flow module (script/flow) or an MCP tool reference","oneOf":[{"$ref":"#/components/schemas/FlowModuleTool"},{"$ref":"#/components/schemas/McpToolValue"},{"$ref":"#/components/schemas/WebsearchToolValue"}],"discriminator":{"propertyName":"tool_type","mapping":{"flowmodule":"#/components/schemas/FlowModuleTool","mcp":"#/components/schemas/McpToolValue","websearch":"#/components/schemas/WebsearchToolValue"}}},"FlowModuleTool":{"description":"A tool implemented as a flow module (script, flow, etc.). The AI can call this like any other flow module","allOf":[{"type":"object","properties":{"tool_type":{"type":"string","enum":["flowmodule"]}},"required":["tool_type"]},{"$ref":"#/components/schemas/FlowModuleValue"}]},"WebsearchToolValue":{"type":"object","description":"A tool implemented as a websearch tool. The AI can call this like any other websearch tool","properties":{"tool_type":{"type":"string","enum":["websearch"]}},"required":["tool_type"]},"McpToolValue":{"type":"object","description":"Reference to an external MCP (Model Context Protocol) tool. The AI can call tools from MCP servers","properties":{"tool_type":{"type":"string","enum":["mcp"]},"resource_path":{"type":"string","description":"Path to the MCP resource/server configuration"},"include_tools":{"type":"array","description":"Whitelist of specific tools to include from this MCP server","items":{"type":"string"}},"exclude_tools":{"type":"array","description":"Blacklist of tools to exclude from this MCP server","items":{"type":"string"}}},"required":["tool_type","resource_path"]},"AiAgent":{"type":"object","description":"AI agent step that can use tools to accomplish tasks. The agent receives inputs and can call any of its configured tools to complete the task","properties":{"input_transforms":{"type":"object","description":"Input parameters for the AI agent mapped to their values","properties":{"provider":{"$ref":"#/components/schemas/ProviderTransform"},"output_type":{"allOf":[{"$ref":"#/components/schemas/InputTransform"}],"description":"Output format type.\\nValid values: 'text' (default) - plain text response, 'image' - image generation\\n"},"user_message":{"allOf":[{"$ref":"#/components/schemas/InputTransform"}],"description":"The user's prompt/message to the AI agent. Supports variable interpolation with flow.input syntax."},"system_prompt":{"allOf":[{"$ref":"#/components/schemas/InputTransform"}],"description":"System instructions that guide the AI's behavior, persona, and response style. Optional."},"streaming":{"allOf":[{"$ref":"#/components/schemas/InputTransform"}],"description":"Boolean. If true, stream the AI response incrementally.\\nStreaming events include: token_delta, tool_call, tool_call_arguments, tool_execution, tool_result\\n"},"memory":{"$ref":"#/components/schemas/MemoryTransform"},"output_schema":{"allOf":[{"$ref":"#/components/schemas/InputTransform"}],"description":"JSON Schema object defining structured output format. Used when you need the AI to return data in a specific shape.\\nSupports standard JSON Schema properties: type, properties, required, items, enum, pattern, minLength, maxLength, minimum, maximum, etc.\\nExample: { type: 'object', properties: { name: { type: 'string' }, age: { type: 'integer' } }, required: ['name'] }\\n"},"user_images":{"allOf":[{"$ref":"#/components/schemas/InputTransform"}],"description":"Array of image references for vision-capable models.\\nFormat: Array<{ bucket: string, key: string }> - S3 object references\\nExample: [{ bucket: 'my-bucket', key: 'images/photo.jpg' }]\\n"},"max_completion_tokens":{"allOf":[{"$ref":"#/components/schemas/InputTransform"}],"description":"Integer. Maximum number of tokens the AI will generate in its response.\\nRange: 1 to 4,294,967,295. Typical values: 256-4096 for most use cases.\\n"},"temperature":{"allOf":[{"$ref":"#/components/schemas/InputTransform"}],"description":"Float. Controls randomness/creativity of responses.\\nRange: 0.0 to 2.0 (provider-dependent)\\n- 0.0 = deterministic, focused responses\\n- 0.7 = balanced (common default)\\n- 1.0+ = more creative/random\\n"}},"required":["provider","user_message","output_type"]},"tools":{"type":"array","description":"Array of tools the agent can use. The agent decides which tools to call based on the task","items":{"$ref":"#/components/schemas/AgentTool"}},"type":{"type":"string","enum":["aiagent"]},"parallel":{"type":"boolean","description":"If true, the agent can execute multiple tool calls in parallel"}},"required":["tools","type","input_transforms"]},"Identity":{"type":"object","description":"Pass-through module that returns its input unchanged. Useful for flow structure or as a placeholder","properties":{"type":{"type":"string","enum":["identity"]},"flow":{"type":"boolean","description":"If true, marks this as a flow identity (special handling)"}},"required":["type"]},"FlowStatus":{"type":"object","properties":{"step":{"type":"integer"},"modules":{"type":"array","items":{"$ref":"#/components/schemas/FlowStatusModule"}},"user_states":{"additionalProperties":true},"preprocessor_module":{"allOf":[{"$ref":"#/components/schemas/FlowStatusModule"}]},"failure_module":{"allOf":[{"$ref":"#/components/schemas/FlowStatusModule"},{"type":"object","properties":{"parent_module":{"type":"string"}}}]},"retry":{"type":"object","properties":{"fail_count":{"type":"integer"},"failed_jobs":{"type":"array","items":{"type":"string","format":"uuid"}}}}},"required":["step","modules","failure_module"]},"FlowStatusModule":{"type":"object","properties":{"type":{"type":"string","enum":["WaitingForPriorSteps","WaitingForEvents","WaitingForExecutor","InProgress","Success","Failure"]},"id":{"type":"string"},"job":{"type":"string","format":"uuid"},"count":{"type":"integer"},"progress":{"type":"integer"},"iterator":{"type":"object","properties":{"index":{"type":"integer"},"itered":{"type":"array","items":{}},"itered_len":{"type":"integer"},"args":{}}},"flow_jobs":{"type":"array","items":{"type":"string"}},"flow_jobs_success":{"type":"array","items":{"type":"boolean"}},"flow_jobs_duration":{"type":"object","properties":{"started_at":{"type":"array","items":{"type":"string"}},"duration_ms":{"type":"array","items":{"type":"integer"}}}},"branch_chosen":{"type":"object","properties":{"type":{"type":"string","enum":["branch","default"]},"branch":{"type":"integer"}},"required":["type"]},"branchall":{"type":"object","properties":{"branch":{"type":"integer"},"len":{"type":"integer"}},"required":["branch","len"]},"approvers":{"type":"array","items":{"type":"object","properties":{"resume_id":{"type":"integer"},"approver":{"type":"string"}},"required":["resume_id","approver"]}},"failed_retries":{"type":"array","items":{"type":"string","format":"uuid"}},"skipped":{"type":"boolean"},"agent_actions":{"type":"array","items":{"type":"object","oneOf":[{"type":"object","properties":{"job_id":{"type":"string","format":"uuid"},"function_name":{"type":"string"},"type":{"type":"string","enum":["tool_call"]},"module_id":{"type":"string"}},"required":["job_id","function_name","type","module_id"]},{"type":"object","properties":{"call_id":{"type":"string","format":"uuid"},"function_name":{"type":"string"},"resource_path":{"type":"string"},"type":{"type":"string","enum":["mcp_tool_call"]},"arguments":{"type":"object"}},"required":["call_id","function_name","resource_path","type"]},{"type":"object","properties":{"type":{"type":"string","enum":["web_search"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["message"]}},"required":["content","type"]}]}},"agent_actions_success":{"type":"array","items":{"type":"boolean"}}},"required":["type"]}}`,
|
|
77837
|
+
{"OpenFlow":{"type":"object","description":"Top-level flow definition containing metadata, configuration, and the flow structure","properties":{"summary":{"type":"string","description":"Short description of what this flow does"},"description":{"type":"string","description":"Detailed documentation for this flow"},"value":{"$ref":"#/components/schemas/FlowValue"},"schema":{"type":"object","description":"JSON Schema for flow inputs. Use this to define input parameters, their types, defaults, and validation. For resource inputs, set type to 'object' and format to 'resource-<type>' (e.g., 'resource-stripe')"},"on_behalf_of_email":{"type":"string","description":"The flow will be run with the permissions of the user with this email."}},"required":["summary","value"]},"FlowValue":{"type":"object","description":"The flow structure containing modules and optional preprocessor/failure handlers","properties":{"modules":{"type":"array","description":"Array of steps that execute in sequence. Each step can be a script, subflow, loop, or branch","items":{"$ref":"#/components/schemas/FlowModule"}},"failure_module":{"description":"Special module that executes when the flow fails. Receives error object with message, name, stack, and step_id. Must have id 'failure'. Only supports script/rawscript types","$ref":"#/components/schemas/FlowModule"},"preprocessor_module":{"description":"Special module that runs before the first step on external triggers. Must have id 'preprocessor'. Only supports script/rawscript types. Cannot reference other step results","$ref":"#/components/schemas/FlowModule"},"same_worker":{"type":"boolean","description":"If true, all steps run on the same worker for better performance"},"concurrent_limit":{"type":"number","description":"Maximum number of concurrent executions of this flow"},"concurrency_key":{"type":"string","description":"Expression to group concurrent executions (e.g., by user ID)"},"concurrency_time_window_s":{"type":"number","description":"Time window in seconds for concurrent_limit"},"debounce_delay_s":{"type":"integer","description":"Delay in seconds to debounce flow executions"},"debounce_key":{"type":"string","description":"Expression to group debounced executions"},"debounce_args_to_accumulate":{"type":"array","description":"Arguments to accumulate across debounced executions","items":{"type":"string"}},"max_total_debouncing_time":{"type":"integer","description":"Maximum total time in seconds that a job can be debounced"},"max_total_debounces_amount":{"type":"integer","description":"Maximum number of times a job can be debounced"},"skip_expr":{"type":"string","description":"JavaScript expression to conditionally skip the entire flow"},"cache_ttl":{"type":"number","description":"Cache duration in seconds for flow results"},"cache_ignore_s3_path":{"type":"boolean"},"flow_env":{"type":"object","description":"Environment variables available to all steps. Values can be strings, JSON values, or special references: '$var:path' (workspace variable) or '$res:path' (resource).","additionalProperties":{}},"priority":{"type":"number","description":"Execution priority (higher numbers run first)"},"early_return":{"type":"string","description":"JavaScript expression to return early from the flow"},"chat_input_enabled":{"type":"boolean","description":"Whether this flow accepts chat-style input"},"notes":{"type":"array","description":"Sticky notes attached to the flow","items":{"$ref":"#/components/schemas/FlowNote"}}},"required":["modules"]},"Retry":{"type":"object","description":"Retry configuration for failed module executions","properties":{"constant":{"type":"object","description":"Retry with constant delay between attempts","properties":{"attempts":{"type":"integer","description":"Number of retry attempts"},"seconds":{"type":"integer","description":"Seconds to wait between retries"}}},"exponential":{"type":"object","description":"Retry with exponential backoff (delay doubles each time)","properties":{"attempts":{"type":"integer","description":"Number of retry attempts"},"multiplier":{"type":"integer","description":"Multiplier for exponential backoff"},"seconds":{"type":"integer","minimum":1,"description":"Initial delay in seconds"},"random_factor":{"type":"integer","minimum":0,"maximum":100,"description":"Random jitter percentage (0-100) to avoid thundering herd"}}},"retry_if":{"$ref":"#/components/schemas/RetryIf"}}},"FlowNote":{"type":"object","description":"A sticky note attached to a flow for documentation and annotation","properties":{"id":{"type":"string","description":"Unique identifier for the note"},"text":{"type":"string","description":"Content of the note"},"position":{"type":"object","description":"Position of the note in the flow editor","properties":{"x":{"type":"number","description":"X coordinate"},"y":{"type":"number","description":"Y coordinate"}},"required":["x","y"]},"size":{"type":"object","description":"Size of the note in the flow editor","properties":{"width":{"type":"number","description":"Width in pixels"},"height":{"type":"number","description":"Height in pixels"}},"required":["width","height"]},"color":{"type":"string","description":"Color of the note (e.g., \\"yellow\\", \\"#ffff00\\")"},"type":{"type":"string","enum":["free","group"],"description":"Type of note - 'free' for standalone notes, 'group' for notes that group other nodes"},"locked":{"type":"boolean","default":false,"description":"Whether the note is locked and cannot be edited or moved"},"contained_node_ids":{"type":"array","items":{"type":"string"},"description":"For group notes, the IDs of nodes contained within this group"}},"required":["id","text","color","type"]},"RetryIf":{"type":"object","description":"Conditional retry based on error or result","properties":{"expr":{"type":"string","description":"JavaScript expression that returns true to retry. Has access to 'result' and 'error' variables"}},"required":["expr"]},"StopAfterIf":{"type":"object","description":"Early termination condition for a module","properties":{"skip_if_stopped":{"type":"boolean","description":"If true, following steps are skipped when this condition triggers"},"expr":{"type":"string","description":"JavaScript expression evaluated after the module runs. Can use 'result' (step's result) or 'flow_input'. Return true to stop"},"error_message":{"type":"string","nullable":true,"description":"Custom error message when stopping with an error. Mutually exclusive with skip_if_stopped. If set to a non-empty string, the flow stops with this error. If empty string, a default error message is used. If null or omitted, no error is raised."}},"required":["expr"]},"FlowModule":{"type":"object","description":"A single step in a flow. Can be a script, subflow, loop, or branch","properties":{"id":{"type":"string","description":"Unique identifier for this step. Used to reference results via 'results.step_id'. Must be a valid identifier (alphanumeric, underscore, hyphen)"},"value":{"$ref":"#/components/schemas/FlowModuleValue"},"stop_after_if":{"description":"Early termination condition evaluated after this step completes","$ref":"#/components/schemas/StopAfterIf"},"stop_after_all_iters_if":{"description":"For loops only - early termination condition evaluated after all iterations complete","$ref":"#/components/schemas/StopAfterIf"},"skip_if":{"type":"object","description":"Conditionally skip this step based on previous results or flow inputs","properties":{"expr":{"type":"string","description":"JavaScript expression that returns true to skip. Can use 'flow_input' or 'results.<step_id>'"}},"required":["expr"]},"sleep":{"description":"Delay before executing this step (in seconds or as expression)","$ref":"#/components/schemas/InputTransform"},"cache_ttl":{"type":"number","description":"Cache duration in seconds for this step's results"},"cache_ignore_s3_path":{"type":"boolean"},"timeout":{"description":"Maximum execution time in seconds (static value or expression)","$ref":"#/components/schemas/InputTransform"},"delete_after_use":{"type":"boolean","description":"If true, this step's result is deleted after use to save memory"},"summary":{"type":"string","description":"Short description of what this step does"},"mock":{"type":"object","description":"Mock configuration for testing without executing the actual step","properties":{"enabled":{"type":"boolean","description":"If true, return mock value instead of executing"},"return_value":{"description":"Value to return when mocked"}}},"suspend":{"type":"object","description":"Configuration for approval/resume steps that wait for user input","properties":{"required_events":{"type":"integer","description":"Number of approvals required before continuing"},"timeout":{"type":"integer","description":"Timeout in seconds before auto-continuing or canceling"},"resume_form":{"type":"object","description":"Form schema for collecting input when resuming","properties":{"schema":{"type":"object","description":"JSON Schema for the resume form"}}},"user_auth_required":{"type":"boolean","description":"If true, only authenticated users can approve"},"user_groups_required":{"description":"Expression or list of groups that can approve","$ref":"#/components/schemas/InputTransform"},"self_approval_disabled":{"type":"boolean","description":"If true, the user who started the flow cannot approve"},"hide_cancel":{"type":"boolean","description":"If true, hide the cancel button on the approval form"},"continue_on_disapprove_timeout":{"type":"boolean","description":"If true, continue flow on timeout instead of canceling"}}},"priority":{"type":"number","description":"Execution priority for this step (higher numbers run first)"},"continue_on_error":{"type":"boolean","description":"If true, flow continues even if this step fails"},"retry":{"description":"Retry configuration if this step fails","$ref":"#/components/schemas/Retry"},"debouncing":{"description":"Debounce configuration for this step (EE only)","type":"object","properties":{"debounce_delay_s":{"type":"integer","description":"Delay in seconds to debounce this step's executions across flow runs"},"debounce_key":{"type":"string","description":"Expression to group debounced executions. Supports $workspace and $args[name]. Default: $workspace/flow/<flow_path>-<step_id>"},"debounce_args_to_accumulate":{"type":"array","description":"Array-type arguments to accumulate across debounced executions","items":{"type":"string"}},"max_total_debouncing_time":{"type":"integer","description":"Maximum total time in seconds before forced execution"},"max_total_debounces_amount":{"type":"integer","description":"Maximum number of debounces before forced execution"}}}},"required":["value","id"]},"InputTransform":{"description":"Maps input parameters for a step. Can be a static value or a JavaScript expression that references previous results or flow inputs","oneOf":[{"$ref":"#/components/schemas/StaticTransform"},{"$ref":"#/components/schemas/JavascriptTransform"},{"$ref":"#/components/schemas/AiTransform"}],"discriminator":{"propertyName":"type","mapping":{"static":"#/components/schemas/StaticTransform","javascript":"#/components/schemas/JavascriptTransform","ai":"#/components/schemas/AiTransform"}}},"StaticTransform":{"type":"object","description":"Static value passed directly to the step. Use for hardcoded values or resource references like '$res:path/to/resource'","properties":{"value":{"description":"The static value. For resources, use format '$res:path/to/resource'"},"type":{"type":"string","enum":["static"]}},"required":["type"]},"JavascriptTransform":{"type":"object","description":"JavaScript expression evaluated at runtime. Can reference previous step results via 'results.step_id' or flow inputs via 'flow_input.property'. Inside loops, use 'flow_input.iter.value' for the current iteration value","properties":{"expr":{"type":"string","description":"JavaScript expression returning the value. Available variables - results (object with all previous step results), flow_input (flow inputs), flow_input.iter (in loops)"},"type":{"type":"string","enum":["javascript"]}},"required":["expr","type"]},"AiTransform":{"type":"object","description":"Value resolved by the AI runtime for this input. The AI engine decides how to satisfy the parameter.","properties":{"type":{"type":"string","enum":["ai"]}},"required":["type"]},"AIProviderKind":{"type":"string","description":"Supported AI provider types","enum":["openai","azure_openai","anthropic","mistral","deepseek","googleai","groq","openrouter","togetherai","customai","aws_bedrock"]},"ProviderConfig":{"type":"object","description":"Complete AI provider configuration with resource reference and model selection","properties":{"kind":{"$ref":"#/components/schemas/AIProviderKind"},"resource":{"type":"string","description":"Resource reference in format '$res:{resource_path}' pointing to provider credentials"},"model":{"type":"string","description":"Model identifier (e.g., 'gpt-4', 'claude-3-opus-20240229', 'gemini-pro')"}},"required":["kind","resource","model"]},"StaticProviderTransform":{"type":"object","description":"Static provider configuration passed directly to the AI agent","properties":{"value":{"$ref":"#/components/schemas/ProviderConfig"},"type":{"type":"string","enum":["static"]}},"required":["type","value"]},"ProviderTransform":{"description":"Provider configuration - can be static (ProviderConfig), JavaScript expression, or AI-determined","oneOf":[{"$ref":"#/components/schemas/StaticProviderTransform"},{"$ref":"#/components/schemas/JavascriptTransform"},{"$ref":"#/components/schemas/AiTransform"}],"discriminator":{"propertyName":"type","mapping":{"static":"#/components/schemas/StaticProviderTransform","javascript":"#/components/schemas/JavascriptTransform","ai":"#/components/schemas/AiTransform"}}},"MemoryOff":{"type":"object","description":"No conversation memory/context","properties":{"kind":{"type":"string","enum":["off"]}},"required":["kind"]},"MemoryAuto":{"type":"object","description":"Automatic context management","properties":{"kind":{"type":"string","enum":["auto"]},"context_length":{"type":"integer","description":"Maximum number of messages to retain in context"},"memory_id":{"type":"string","description":"Identifier for persistent memory across agent invocations"}},"required":["kind"]},"MemoryMessage":{"type":"object","description":"A single message in conversation history","properties":{"role":{"type":"string","enum":["user","assistant","system"]},"content":{"type":"string"}},"required":["role","content"]},"MemoryManual":{"type":"object","description":"Explicit message history","properties":{"kind":{"type":"string","enum":["manual"]},"messages":{"type":"array","items":{"$ref":"#/components/schemas/MemoryMessage"}}},"required":["kind","messages"]},"MemoryConfig":{"description":"Conversation memory configuration","oneOf":[{"$ref":"#/components/schemas/MemoryOff"},{"$ref":"#/components/schemas/MemoryAuto"},{"$ref":"#/components/schemas/MemoryManual"}],"discriminator":{"propertyName":"kind","mapping":{"off":"#/components/schemas/MemoryOff","auto":"#/components/schemas/MemoryAuto","manual":"#/components/schemas/MemoryManual"}}},"StaticMemoryTransform":{"type":"object","description":"Static memory configuration passed directly to the AI agent","properties":{"value":{"$ref":"#/components/schemas/MemoryConfig"},"type":{"type":"string","enum":["static"]}},"required":["type","value"]},"MemoryTransform":{"description":"Memory configuration - can be static (MemoryConfig), JavaScript expression, or AI-determined","oneOf":[{"$ref":"#/components/schemas/StaticMemoryTransform"},{"$ref":"#/components/schemas/JavascriptTransform"},{"$ref":"#/components/schemas/AiTransform"}],"discriminator":{"propertyName":"type","mapping":{"static":"#/components/schemas/StaticMemoryTransform","javascript":"#/components/schemas/JavascriptTransform","ai":"#/components/schemas/AiTransform"}}},"FlowModuleValue":{"description":"The actual implementation of a flow step. Can be a script (inline or referenced), subflow, loop, branch, or special module type","oneOf":[{"$ref":"#/components/schemas/RawScript"},{"$ref":"#/components/schemas/PathScript"},{"$ref":"#/components/schemas/PathFlow"},{"$ref":"#/components/schemas/ForloopFlow"},{"$ref":"#/components/schemas/WhileloopFlow"},{"$ref":"#/components/schemas/BranchOne"},{"$ref":"#/components/schemas/BranchAll"},{"$ref":"#/components/schemas/Identity"},{"$ref":"#/components/schemas/AiAgent"}],"discriminator":{"propertyName":"type","mapping":{"rawscript":"#/components/schemas/RawScript","script":"#/components/schemas/PathScript","flow":"#/components/schemas/PathFlow","forloopflow":"#/components/schemas/ForloopFlow","whileloopflow":"#/components/schemas/WhileloopFlow","branchone":"#/components/schemas/BranchOne","branchall":"#/components/schemas/BranchAll","identity":"#/components/schemas/Identity","aiagent":"#/components/schemas/AiAgent"}}},"RawScript":{"type":"object","description":"Inline script with code defined directly in the flow. Use 'bun' as default language if unspecified. The script receives arguments from input_transforms","properties":{"input_transforms":{"type":"object","description":"Map of parameter names to their values (static or JavaScript expressions). These become the script's input arguments","additionalProperties":{"$ref":"#/components/schemas/InputTransform"}},"content":{"type":"string","description":"The script source code. Should export a 'main' function"},"language":{"type":"string","description":"Programming language for this script","enum":["deno","bun","python3","go","bash","powershell","postgresql","mysql","bigquery","snowflake","mssql","oracledb","graphql","nativets","php","rust","ansible","csharp","nu","java","ruby","duckdb"]},"path":{"type":"string","description":"Optional path for saving this script"},"lock":{"type":"string","description":"Lock file content for dependencies"},"type":{"type":"string","enum":["rawscript"]},"tag":{"type":"string","description":"Worker group tag for execution routing"},"concurrent_limit":{"type":"number","description":"Maximum concurrent executions of this script"},"concurrency_time_window_s":{"type":"number","description":"Time window for concurrent_limit"},"custom_concurrency_key":{"type":"string","description":"Custom key for grouping concurrent executions"},"is_trigger":{"type":"boolean","description":"If true, this script is a trigger that can start the flow"},"assets":{"type":"array","description":"External resources this script accesses (S3 objects, resources, etc.)","items":{"type":"object","required":["path","kind"],"properties":{"path":{"type":"string","description":"Path to the asset"},"kind":{"type":"string","description":"Type of asset","enum":["s3object","resource","ducklake","datatable","volume"]},"access_type":{"type":"string","nullable":true,"description":"Access level for this asset","enum":["r","w","rw"]},"alt_access_type":{"type":"string","nullable":true,"description":"Alternative access level","enum":["r","w","rw"]}}}}},"required":["type","content","language","input_transforms"]},"PathScript":{"type":"object","description":"Reference to an existing script by path. Use this when calling a previously saved script instead of writing inline code","properties":{"input_transforms":{"type":"object","description":"Map of parameter names to their values (static or JavaScript expressions). These become the script's input arguments","additionalProperties":{"$ref":"#/components/schemas/InputTransform"}},"path":{"type":"string","description":"Path to the script in the workspace (e.g., 'f/scripts/send_email')"},"hash":{"type":"string","description":"Optional specific version hash of the script to use"},"type":{"type":"string","enum":["script"]},"tag_override":{"type":"string","description":"Override the script's default worker group tag"},"is_trigger":{"type":"boolean","description":"If true, this script is a trigger that can start the flow"}},"required":["type","path","input_transforms"]},"PathFlow":{"type":"object","description":"Reference to an existing flow by path. Use this to call another flow as a subflow","properties":{"input_transforms":{"type":"object","description":"Map of parameter names to their values (static or JavaScript expressions). These become the subflow's input arguments","additionalProperties":{"$ref":"#/components/schemas/InputTransform"}},"path":{"type":"string","description":"Path to the flow in the workspace (e.g., 'f/flows/process_user')"},"type":{"type":"string","enum":["flow"]}},"required":["type","path","input_transforms"]},"ForloopFlow":{"type":"object","description":"Executes nested modules in a loop over an iterator. Inside the loop, use 'flow_input.iter.value' to access the current iteration value, and 'flow_input.iter.index' for the index. Supports parallel execution for better performance on I/O-bound operations","properties":{"modules":{"type":"array","description":"Steps to execute for each iteration. These can reference the iteration value via 'flow_input.iter.value'","items":{"$ref":"#/components/schemas/FlowModule"}},"iterator":{"description":"JavaScript expression that returns an array to iterate over. Can reference 'results.step_id' or 'flow_input'","$ref":"#/components/schemas/InputTransform"},"skip_failures":{"type":"boolean","description":"If true, iteration failures don't stop the loop. Failed iterations return null"},"type":{"type":"string","enum":["forloopflow"]},"parallel":{"type":"boolean","description":"If true, iterations run concurrently (faster for I/O-bound operations). Use with parallelism to control concurrency"},"parallelism":{"description":"Maximum number of concurrent iterations when parallel=true. Limits resource usage. Can be static number or expression","$ref":"#/components/schemas/InputTransform"},"squash":{"type":"boolean"}},"required":["modules","iterator","skip_failures","type"]},"WhileloopFlow":{"type":"object","description":"Executes nested modules repeatedly while a condition is true. The loop checks the condition after each iteration. Use stop_after_if on modules to control loop termination","properties":{"modules":{"type":"array","description":"Steps to execute in each iteration. Use stop_after_if to control when the loop ends","items":{"$ref":"#/components/schemas/FlowModule"}},"skip_failures":{"type":"boolean","description":"If true, iteration failures don't stop the loop. Failed iterations return null"},"type":{"type":"string","enum":["whileloopflow"]},"parallel":{"type":"boolean","description":"If true, iterations run concurrently (use with caution in while loops)"},"parallelism":{"description":"Maximum number of concurrent iterations when parallel=true","$ref":"#/components/schemas/InputTransform"},"squash":{"type":"boolean"}},"required":["modules","skip_failures","type"]},"BranchOne":{"type":"object","description":"Conditional branching where only the first matching branch executes. Branches are evaluated in order, and the first one with a true expression runs. If no branches match, the default branch executes","properties":{"branches":{"type":"array","description":"Array of branches to evaluate in order. The first branch with expr evaluating to true executes","items":{"type":"object","properties":{"summary":{"type":"string","description":"Short description of this branch condition"},"expr":{"type":"string","description":"JavaScript expression that returns boolean. Can use 'results.step_id' or 'flow_input'. First true expr wins"},"modules":{"type":"array","description":"Steps to execute if this branch's expr is true","items":{"$ref":"#/components/schemas/FlowModule"}}},"required":["modules","expr"]}},"default":{"type":"array","description":"Steps to execute if no branch expressions match","items":{"$ref":"#/components/schemas/FlowModule"}},"type":{"type":"string","enum":["branchone"]}},"required":["branches","default","type"]},"BranchAll":{"type":"object","description":"Parallel branching where all branches execute simultaneously. Unlike BranchOne, all branches run regardless of conditions. Useful for executing independent tasks concurrently","properties":{"branches":{"type":"array","description":"Array of branches that all execute (either in parallel or sequentially)","items":{"type":"object","properties":{"summary":{"type":"string","description":"Short description of this branch's purpose"},"skip_failure":{"type":"boolean","description":"If true, failure in this branch doesn't fail the entire flow"},"modules":{"type":"array","description":"Steps to execute in this branch","items":{"$ref":"#/components/schemas/FlowModule"}}},"required":["modules"]}},"type":{"type":"string","enum":["branchall"]},"parallel":{"type":"boolean","description":"If true, all branches execute concurrently. If false, they execute sequentially"}},"required":["branches","type"]},"AgentTool":{"type":"object","description":"A tool available to an AI agent. Can be a flow module or an external MCP (Model Context Protocol) tool","properties":{"id":{"type":"string","description":"Unique identifier for this tool. Cannot contain spaces - use underscores instead (e.g., 'get_user_data' not 'get user data')"},"summary":{"type":"string","description":"Short description of what this tool does (shown to the AI)"},"value":{"$ref":"#/components/schemas/ToolValue"}},"required":["id","value"]},"ToolValue":{"description":"The implementation of a tool. Can be a flow module (script/flow) or an MCP tool reference","oneOf":[{"$ref":"#/components/schemas/FlowModuleTool"},{"$ref":"#/components/schemas/McpToolValue"},{"$ref":"#/components/schemas/WebsearchToolValue"}],"discriminator":{"propertyName":"tool_type","mapping":{"flowmodule":"#/components/schemas/FlowModuleTool","mcp":"#/components/schemas/McpToolValue","websearch":"#/components/schemas/WebsearchToolValue"}}},"FlowModuleTool":{"description":"A tool implemented as a flow module (script, flow, etc.). The AI can call this like any other flow module","allOf":[{"type":"object","properties":{"tool_type":{"type":"string","enum":["flowmodule"]}},"required":["tool_type"]},{"$ref":"#/components/schemas/FlowModuleValue"}]},"WebsearchToolValue":{"type":"object","description":"A tool implemented as a websearch tool. The AI can call this like any other websearch tool","properties":{"tool_type":{"type":"string","enum":["websearch"]}},"required":["tool_type"]},"McpToolValue":{"type":"object","description":"Reference to an external MCP (Model Context Protocol) tool. The AI can call tools from MCP servers","properties":{"tool_type":{"type":"string","enum":["mcp"]},"resource_path":{"type":"string","description":"Path to the MCP resource/server configuration"},"include_tools":{"type":"array","description":"Whitelist of specific tools to include from this MCP server","items":{"type":"string"}},"exclude_tools":{"type":"array","description":"Blacklist of tools to exclude from this MCP server","items":{"type":"string"}}},"required":["tool_type","resource_path"]},"AiAgent":{"type":"object","description":"AI agent step that can use tools to accomplish tasks. The agent receives inputs and can call any of its configured tools to complete the task","properties":{"input_transforms":{"type":"object","description":"Input parameters for the AI agent mapped to their values","properties":{"provider":{"$ref":"#/components/schemas/ProviderTransform"},"output_type":{"allOf":[{"$ref":"#/components/schemas/InputTransform"}],"description":"Output format type.\\nValid values: 'text' (default) - plain text response, 'image' - image generation\\n"},"user_message":{"allOf":[{"$ref":"#/components/schemas/InputTransform"}],"description":"The user's prompt/message to the AI agent. Supports variable interpolation with flow.input syntax."},"system_prompt":{"allOf":[{"$ref":"#/components/schemas/InputTransform"}],"description":"System instructions that guide the AI's behavior, persona, and response style. Optional."},"streaming":{"allOf":[{"$ref":"#/components/schemas/InputTransform"}],"description":"Boolean. If true, stream the AI response incrementally.\\nStreaming events include: token_delta, tool_call, tool_call_arguments, tool_execution, tool_result\\n"},"memory":{"$ref":"#/components/schemas/MemoryTransform"},"output_schema":{"allOf":[{"$ref":"#/components/schemas/InputTransform"}],"description":"JSON Schema object defining structured output format. Used when you need the AI to return data in a specific shape.\\nSupports standard JSON Schema properties: type, properties, required, items, enum, pattern, minLength, maxLength, minimum, maximum, etc.\\nExample: { type: 'object', properties: { name: { type: 'string' }, age: { type: 'integer' } }, required: ['name'] }\\n"},"user_images":{"allOf":[{"$ref":"#/components/schemas/InputTransform"}],"description":"Array of image references for vision-capable models.\\nFormat: Array<{ bucket: string, key: string }> - S3 object references\\nExample: [{ bucket: 'my-bucket', key: 'images/photo.jpg' }]\\n"},"max_completion_tokens":{"allOf":[{"$ref":"#/components/schemas/InputTransform"}],"description":"Integer. Maximum number of tokens the AI will generate in its response.\\nRange: 1 to 4,294,967,295. Typical values: 256-4096 for most use cases.\\n"},"temperature":{"allOf":[{"$ref":"#/components/schemas/InputTransform"}],"description":"Float. Controls randomness/creativity of responses.\\nRange: 0.0 to 2.0 (provider-dependent)\\n- 0.0 = deterministic, focused responses\\n- 0.7 = balanced (common default)\\n- 1.0+ = more creative/random\\n"}},"required":["provider","user_message","output_type"]},"tools":{"type":"array","description":"Array of tools the agent can use. The agent decides which tools to call based on the task","items":{"$ref":"#/components/schemas/AgentTool"}},"type":{"type":"string","enum":["aiagent"]},"parallel":{"type":"boolean","description":"If true, the agent can execute multiple tool calls in parallel"}},"required":["tools","type","input_transforms"]},"Identity":{"type":"object","description":"Pass-through module that returns its input unchanged. Useful for flow structure or as a placeholder","properties":{"type":{"type":"string","enum":["identity"]},"flow":{"type":"boolean","description":"If true, marks this as a flow identity (special handling)"}},"required":["type"]},"FlowStatus":{"type":"object","properties":{"step":{"type":"integer"},"modules":{"type":"array","items":{"$ref":"#/components/schemas/FlowStatusModule"}},"user_states":{"additionalProperties":true},"preprocessor_module":{"allOf":[{"$ref":"#/components/schemas/FlowStatusModule"}]},"failure_module":{"allOf":[{"$ref":"#/components/schemas/FlowStatusModule"},{"type":"object","properties":{"parent_module":{"type":"string"}}}]},"retry":{"type":"object","properties":{"fail_count":{"type":"integer"},"failed_jobs":{"type":"array","items":{"type":"string","format":"uuid"}}}}},"required":["step","modules","failure_module"]},"FlowStatusModule":{"type":"object","properties":{"type":{"type":"string","enum":["WaitingForPriorSteps","WaitingForEvents","WaitingForExecutor","InProgress","Success","Failure"]},"id":{"type":"string"},"job":{"type":"string","format":"uuid"},"count":{"type":"integer"},"progress":{"type":"integer"},"iterator":{"type":"object","properties":{"index":{"type":"integer"},"itered":{"type":"array","items":{}},"itered_len":{"type":"integer"},"args":{}}},"flow_jobs":{"type":"array","items":{"type":"string"}},"flow_jobs_success":{"type":"array","items":{"type":"boolean"}},"flow_jobs_duration":{"type":"object","properties":{"started_at":{"type":"array","items":{"type":"string"}},"duration_ms":{"type":"array","items":{"type":"integer"}}}},"branch_chosen":{"type":"object","properties":{"type":{"type":"string","enum":["branch","default"]},"branch":{"type":"integer"}},"required":["type"]},"branchall":{"type":"object","properties":{"branch":{"type":"integer"},"len":{"type":"integer"}},"required":["branch","len"]},"approvers":{"type":"array","items":{"type":"object","properties":{"resume_id":{"type":"integer"},"approver":{"type":"string"}},"required":["resume_id","approver"]}},"failed_retries":{"type":"array","items":{"type":"string","format":"uuid"}},"skipped":{"type":"boolean"},"agent_actions":{"type":"array","items":{"type":"object","oneOf":[{"type":"object","properties":{"job_id":{"type":"string","format":"uuid"},"function_name":{"type":"string"},"type":{"type":"string","enum":["tool_call"]},"module_id":{"type":"string"}},"required":["job_id","function_name","type","module_id"]},{"type":"object","properties":{"call_id":{"type":"string","format":"uuid"},"function_name":{"type":"string"},"resource_path":{"type":"string"},"type":{"type":"string","enum":["mcp_tool_call"]},"arguments":{"type":"object"}},"required":["call_id","function_name","resource_path","type"]},{"type":"object","properties":{"type":{"type":"string","enum":["web_search"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["message"]}},"required":["content","type"]}]}},"agent_actions_success":{"type":"array","items":{"type":"boolean"}}},"required":["type"]}}`,
|
|
77404
77838
|
"raw-app": `---
|
|
77405
77839
|
name: raw-app
|
|
77406
77840
|
description: MUST use when creating raw apps.
|
|
@@ -77648,9 +78082,18 @@ Tell the user they can run these commands (do NOT run them yourself):
|
|
|
77648
78082
|
| \`wmill app dev\` | Start dev server with live reload |
|
|
77649
78083
|
| \`wmill app generate-agents\` | Refresh AGENTS.md and DATATABLES.md |
|
|
77650
78084
|
| \`wmill app generate-locks\` | Generate lock files for backend runnables |
|
|
77651
|
-
| \`wmill sync push\` | Deploy app to Windmill |
|
|
78085
|
+
| \`wmill sync push --extra-includes "f/<folder>/<app>.raw_app/**" --yes\` | Deploy this specific raw app to Windmill (never do a blanket \`wmill sync push\`) |
|
|
77652
78086
|
| \`wmill sync pull\` | Pull latest from Windmill |
|
|
77653
78087
|
|
|
78088
|
+
## Svelte 5 Event Handling
|
|
78089
|
+
|
|
78090
|
+
When building Svelte 5 raw apps, be aware of event delegation:
|
|
78091
|
+
|
|
78092
|
+
- The Svelte runtime version in \`node_modules/svelte\` **must match** the compiler version used by \`wmill sync push\`. If you get \`$.delegated is undefined\` errors at runtime, run \`npm install svelte@latest\` in the raw app folder and re-push.
|
|
78093
|
+
- \`onclick\` on \`<div>\`, \`<span>\`, and other non-interactive elements uses Svelte's event delegation system. If the runtime doesn't support it, you'll get errors.
|
|
78094
|
+
- \`onclick\` on \`<button>\` elements is native and generally works fine.
|
|
78095
|
+
- For modal overlays or click-outside patterns, prefer using \`<button>\` elements styled as overlays, or ensure the Svelte runtime is up to date.
|
|
78096
|
+
|
|
77654
78097
|
## Best Practices
|
|
77655
78098
|
|
|
77656
78099
|
1. **Check DATATABLES.md** for existing tables before creating new ones
|
|
@@ -79826,7 +80269,8 @@ await __promiseAll([
|
|
|
79826
80269
|
init_app_metadata(),
|
|
79827
80270
|
init_sync(),
|
|
79828
80271
|
init_script(),
|
|
79829
|
-
init_codebase()
|
|
80272
|
+
init_codebase(),
|
|
80273
|
+
init_dependency_tree()
|
|
79830
80274
|
]);
|
|
79831
80275
|
import { sep as SEP21 } from "node:path";
|
|
79832
80276
|
async function generateMetadata2(opts, folder) {
|
|
@@ -79836,10 +80280,9 @@ async function generateMetadata2(opts, folder) {
|
|
|
79836
80280
|
const workspace = await resolveWorkspace(opts);
|
|
79837
80281
|
await requireLogin(opts);
|
|
79838
80282
|
opts = await mergeConfigWithConfigFile(opts);
|
|
79839
|
-
const rawWorkspaceDependencies = await getRawWorkspaceDependencies();
|
|
80283
|
+
const rawWorkspaceDependencies = await getRawWorkspaceDependencies(false);
|
|
79840
80284
|
const codebases = await listSyncCodebases(opts);
|
|
79841
80285
|
const ignore = await ignoreF(opts);
|
|
79842
|
-
const staleItems = [];
|
|
79843
80286
|
const skipScripts = opts.skipScripts ?? false;
|
|
79844
80287
|
const skipFlows = opts.skipFlows ?? opts.schemaOnly ?? false;
|
|
79845
80288
|
const skipApps = opts.skipApps ?? opts.schemaOnly ?? false;
|
|
@@ -79854,27 +80297,23 @@ async function generateMetadata2(opts, folder) {
|
|
|
79854
80297
|
info(colors.yellow("Nothing to check (all types skipped)"));
|
|
79855
80298
|
return;
|
|
79856
80299
|
}
|
|
79857
|
-
info(
|
|
80300
|
+
info(`Checking ${checking.join(", ")}...`);
|
|
80301
|
+
const tree = new DoubleLinkedDependencyTree;
|
|
80302
|
+
tree.setWorkspaceDeps(rawWorkspaceDependencies);
|
|
79858
80303
|
if (!skipScripts) {
|
|
79859
80304
|
const scriptElems = await elementsToMap(await FSFSElement(process.cwd(), codebases, false), (p, isD) => {
|
|
79860
|
-
return !isD && !exts.some((ext2) => p.endsWith(ext2)) || ignore(p, isD) ||
|
|
80305
|
+
return !isD && !exts.some((ext2) => p.endsWith(ext2)) || ignore(p, isD) || isFolderResourcePathAnyFormat(p) || isScriptModulePath(p) && !isModuleEntryPoint(p);
|
|
79861
80306
|
}, false, {});
|
|
79862
80307
|
for (const e of Object.keys(scriptElems)) {
|
|
79863
|
-
|
|
79864
|
-
if (candidate) {
|
|
79865
|
-
staleItems.push({ type: "script", path: candidate, folder: e });
|
|
79866
|
-
}
|
|
80308
|
+
await generateScriptMetadataInternal(e, workspace, opts, true, true, rawWorkspaceDependencies, codebases, false, false, tree);
|
|
79867
80309
|
}
|
|
79868
80310
|
}
|
|
79869
80311
|
if (!skipFlows) {
|
|
79870
80312
|
const flowElems = Object.keys(await elementsToMap(await FSFSElement(process.cwd(), [], true), (p, isD) => {
|
|
79871
80313
|
return ignore(p, isD) || !isD && !p.endsWith(SEP21 + "flow.yaml") && !p.endsWith(SEP21 + "flow.json");
|
|
79872
80314
|
}, false, {})).map((x) => x.substring(0, x.lastIndexOf(SEP21)));
|
|
79873
|
-
for (const
|
|
79874
|
-
|
|
79875
|
-
if (candidate) {
|
|
79876
|
-
staleItems.push({ type: "flow", path: candidate, folder: folder2 });
|
|
79877
|
-
}
|
|
80315
|
+
for (const flowFolder of flowElems) {
|
|
80316
|
+
await generateFlowLockInternal(flowFolder, true, workspace, opts, false, true, false, tree);
|
|
79878
80317
|
}
|
|
79879
80318
|
}
|
|
79880
80319
|
if (!skipApps) {
|
|
@@ -79884,16 +80323,37 @@ async function generateMetadata2(opts, folder) {
|
|
|
79884
80323
|
const rawAppFolders = getAppFolders(elems, "raw_app.yaml");
|
|
79885
80324
|
const appFolders = getAppFolders(elems, "app.yaml");
|
|
79886
80325
|
for (const appFolder of rawAppFolders) {
|
|
79887
|
-
|
|
79888
|
-
if (candidate) {
|
|
79889
|
-
staleItems.push({ type: "app", path: candidate, folder: appFolder, isRawApp: true });
|
|
79890
|
-
}
|
|
80326
|
+
await generateAppLocksInternal(appFolder, true, true, workspace, opts, false, true, false, tree);
|
|
79891
80327
|
}
|
|
79892
80328
|
for (const appFolder of appFolders) {
|
|
79893
|
-
|
|
79894
|
-
|
|
79895
|
-
|
|
79896
|
-
|
|
80329
|
+
await generateAppLocksInternal(appFolder, false, true, workspace, opts, false, true, false, tree);
|
|
80330
|
+
}
|
|
80331
|
+
}
|
|
80332
|
+
tree.propagateStaleness();
|
|
80333
|
+
try {
|
|
80334
|
+
await uploadScripts(tree, workspace);
|
|
80335
|
+
} catch (e) {
|
|
80336
|
+
warn(colors.yellow(`Failed to upload scripts to temp storage (backend may be too old): ${e}. ` + `Locks will be generated using deployed script versions only — locally modified ` + `relative imports may not be reflected.`));
|
|
80337
|
+
}
|
|
80338
|
+
const staleItems = [];
|
|
80339
|
+
const seenFolders = new Set;
|
|
80340
|
+
for (const p of tree.allPaths()) {
|
|
80341
|
+
const staleReason = tree.getStaleReason(p);
|
|
80342
|
+
if (!staleReason)
|
|
80343
|
+
continue;
|
|
80344
|
+
const itemType = tree.getItemType(p);
|
|
80345
|
+
const itemFolder = tree.getFolder(p);
|
|
80346
|
+
if (itemType === "dependencies") {
|
|
80347
|
+
staleItems.push({ type: itemType, path: p, folder: itemFolder, staleReason });
|
|
80348
|
+
} else if (itemType === "inline_script") {
|
|
80349
|
+
continue;
|
|
80350
|
+
} else if (itemType === "script") {
|
|
80351
|
+
const originalPath = tree.getOriginalPath(p);
|
|
80352
|
+
staleItems.push({ type: itemType, path: originalPath, folder: itemFolder, staleReason });
|
|
80353
|
+
} else if (!seenFolders.has(itemFolder)) {
|
|
80354
|
+
seenFolders.add(itemFolder);
|
|
80355
|
+
const originalPath = tree.getOriginalPath(p);
|
|
80356
|
+
staleItems.push({ type: itemType, path: originalPath, folder: itemFolder, isRawApp: tree.getIsRawApp(p), staleReason });
|
|
79897
80357
|
}
|
|
79898
80358
|
}
|
|
79899
80359
|
let filteredItems = staleItems;
|
|
@@ -79902,10 +80362,43 @@ async function generateMetadata2(opts, folder) {
|
|
|
79902
80362
|
if (folder.endsWith("/")) {
|
|
79903
80363
|
folder = folder.substring(0, folder.length - 1);
|
|
79904
80364
|
}
|
|
79905
|
-
|
|
80365
|
+
const folderNoExt = folder.replace(/\.[^/.]+$/, "");
|
|
80366
|
+
const isInsideFolder = (item) => {
|
|
79906
80367
|
const normalizedFolder = item.folder.replaceAll("\\", "/");
|
|
79907
|
-
|
|
79908
|
-
|
|
80368
|
+
const normalizedPath = item.path.replaceAll("\\", "/");
|
|
80369
|
+
return normalizedFolder === folder || normalizedFolder.startsWith(folder + "/") || normalizedPath === folder || normalizedPath === folderNoExt;
|
|
80370
|
+
};
|
|
80371
|
+
const isPathInFolder = (p) => p.startsWith(folder + "/") || p === folder || p === folderNoExt;
|
|
80372
|
+
const touchesFolder = (treePath) => {
|
|
80373
|
+
if (isPathInFolder(treePath))
|
|
80374
|
+
return true;
|
|
80375
|
+
let found = false;
|
|
80376
|
+
tree.traverseTransitive(treePath, (importPath) => {
|
|
80377
|
+
if (isPathInFolder(importPath)) {
|
|
80378
|
+
found = true;
|
|
80379
|
+
return true;
|
|
80380
|
+
}
|
|
80381
|
+
});
|
|
80382
|
+
return found;
|
|
80383
|
+
};
|
|
80384
|
+
const isRelevant = (item) => {
|
|
80385
|
+
if (isInsideFolder(item))
|
|
80386
|
+
return true;
|
|
80387
|
+
if (item.type === "dependencies")
|
|
80388
|
+
return true;
|
|
80389
|
+
const treePath = (item.type === "script" ? item.path.replace(/\.[^/.]+$/, "") : item.folder).replaceAll("\\", "/");
|
|
80390
|
+
return touchesFolder(treePath);
|
|
80391
|
+
};
|
|
80392
|
+
if (opts.strictFolderBoundaries) {
|
|
80393
|
+
filteredItems = staleItems.filter(isInsideFolder);
|
|
80394
|
+
const excludedStale = staleItems.filter((item) => !isInsideFolder(item) && isRelevant(item) && item.type !== "dependencies");
|
|
80395
|
+
for (const item of excludedStale) {
|
|
80396
|
+
const normalizedPath = item.path.replaceAll("\\", "/");
|
|
80397
|
+
warn(colors.yellow(`Warning: ${normalizedPath} depends on something inside "${folder}" but is outside it — skipped due to --strict-folder-boundaries. Next generate-metadata will not detect it as stale.`));
|
|
80398
|
+
}
|
|
80399
|
+
} else {
|
|
80400
|
+
filteredItems = staleItems.filter(isRelevant);
|
|
80401
|
+
}
|
|
79909
80402
|
}
|
|
79910
80403
|
if (filteredItems.length === 0) {
|
|
79911
80404
|
info(colors.green("All metadata up-to-date"));
|
|
@@ -79914,26 +80407,22 @@ async function generateMetadata2(opts, folder) {
|
|
|
79914
80407
|
const scripts = filteredItems.filter((i) => i.type === "script");
|
|
79915
80408
|
const flows = filteredItems.filter((i) => i.type === "flow");
|
|
79916
80409
|
const apps2 = filteredItems.filter((i) => i.type === "app");
|
|
80410
|
+
const deps = filteredItems.filter((i) => i.type === "dependencies");
|
|
79917
80411
|
info("");
|
|
79918
|
-
info(`Found ${filteredItems.length} item(s) with stale metadata:`);
|
|
79919
|
-
|
|
79920
|
-
|
|
79921
|
-
|
|
79922
|
-
|
|
79923
|
-
|
|
79924
|
-
|
|
79925
|
-
|
|
79926
|
-
info(colors.gray(` Flows (${flows.length}):`));
|
|
79927
|
-
for (const item of flows) {
|
|
79928
|
-
info(colors.yellow(` ${item.path}`));
|
|
79929
|
-
}
|
|
79930
|
-
}
|
|
79931
|
-
if (apps2.length > 0) {
|
|
79932
|
-
info(colors.gray(` Apps (${apps2.length}):`));
|
|
79933
|
-
for (const item of apps2) {
|
|
79934
|
-
info(colors.yellow(` ${item.path}`));
|
|
80412
|
+
info(`Found ${colors.bold(String(filteredItems.length))} item(s) with stale metadata:`);
|
|
80413
|
+
const printItems = (label, items) => {
|
|
80414
|
+
if (items.length === 0)
|
|
80415
|
+
return;
|
|
80416
|
+
info(` ${label} (${items.length}):`);
|
|
80417
|
+
for (const item of items) {
|
|
80418
|
+
const reason = item.staleReason ? colors.dim(colors.white(` — ${item.staleReason}`)) : "";
|
|
80419
|
+
info(` ~ ${item.path}` + reason);
|
|
79935
80420
|
}
|
|
79936
|
-
}
|
|
80421
|
+
};
|
|
80422
|
+
printItems("Workspace dependencies", deps);
|
|
80423
|
+
printItems("Scripts", scripts);
|
|
80424
|
+
printItems("Flows", flows);
|
|
80425
|
+
printItems("Apps", apps2);
|
|
79937
80426
|
if (opts.dryRun) {
|
|
79938
80427
|
return;
|
|
79939
80428
|
}
|
|
@@ -79945,34 +80434,38 @@ async function generateMetadata2(opts, folder) {
|
|
|
79945
80434
|
return;
|
|
79946
80435
|
}
|
|
79947
80436
|
info("");
|
|
79948
|
-
const
|
|
80437
|
+
const mismatchedWorkspaceDeps = tree.getMismatchedWorkspaceDeps();
|
|
80438
|
+
const total = filteredItems.length - deps.length;
|
|
79949
80439
|
const maxWidth = `[${total}/${total}]`.length;
|
|
79950
80440
|
let current = 0;
|
|
79951
80441
|
const formatProgress = (n) => {
|
|
79952
|
-
|
|
79953
|
-
return colors.gray(bracket.padEnd(maxWidth, " "));
|
|
80442
|
+
return colors.dim(colors.white(`[${n}/${total}]`.padEnd(maxWidth, " ")));
|
|
79954
80443
|
};
|
|
79955
80444
|
for (const item of scripts) {
|
|
79956
80445
|
current++;
|
|
79957
|
-
info(`${formatProgress(current)} script ${
|
|
79958
|
-
await generateScriptMetadataInternal(item.
|
|
80446
|
+
info(`${formatProgress(current)} script ${item.path}`);
|
|
80447
|
+
await generateScriptMetadataInternal(item.path, workspace, opts, false, true, mismatchedWorkspaceDeps, codebases, false, false, tree);
|
|
79959
80448
|
}
|
|
79960
80449
|
for (const item of flows) {
|
|
79961
80450
|
current++;
|
|
79962
|
-
const result = await generateFlowLockInternal(item.folder, false, workspace, opts, false, true);
|
|
79963
|
-
const
|
|
79964
|
-
|
|
80451
|
+
const result = await generateFlowLockInternal(item.folder.replaceAll("/", SEP21), false, workspace, opts, false, true, false, tree);
|
|
80452
|
+
const flowResult = result;
|
|
80453
|
+
const scriptsInfo = flowResult?.updatedScripts?.length ? colors.dim(colors.white(`: ${flowResult.updatedScripts.join(", ")}`)) : "";
|
|
80454
|
+
info(`${formatProgress(current)} flow ${item.path}${scriptsInfo}`);
|
|
79965
80455
|
}
|
|
79966
80456
|
for (const item of apps2) {
|
|
79967
80457
|
current++;
|
|
79968
|
-
const result = await generateAppLocksInternal(item.folder, item.isRawApp, false, workspace, opts, false, true);
|
|
79969
|
-
const
|
|
79970
|
-
|
|
80458
|
+
const result = await generateAppLocksInternal(item.folder.replaceAll("/", SEP21), item.isRawApp, false, workspace, opts, false, true, false, tree);
|
|
80459
|
+
const appResult = result;
|
|
80460
|
+
const scriptsInfo = appResult?.updatedScripts?.length ? colors.dim(colors.white(`: ${appResult.updatedScripts.join(", ")}`)) : "";
|
|
80461
|
+
info(`${formatProgress(current)} app ${item.path}${scriptsInfo}`);
|
|
79971
80462
|
}
|
|
80463
|
+
const allStaleDeps = staleItems.filter((i) => i.type === "dependencies");
|
|
80464
|
+
await tree.persistDepsHashes(allStaleDeps.map((d) => d.path));
|
|
79972
80465
|
info("");
|
|
79973
|
-
info(
|
|
80466
|
+
info(`Done. Updated ${colors.bold(String(total))} item(s).`);
|
|
79974
80467
|
}
|
|
79975
|
-
var command29 = new Command().description("Generate metadata (locks, schemas) for all scripts, flows, and apps").arguments("[folder:string]").option("--yes", "Skip confirmation prompt").option("--dry-run", "Show what would be updated without making changes").option("--lock-only", "Re-generate only the lock files").option("--schema-only", "Re-generate only script schemas (skips flows and apps)").option("--skip-scripts", "Skip processing scripts").option("--skip-flows", "Skip processing flows").option("--skip-apps", "Skip processing apps").option("-i --includes <patterns:file[]>", "Comma separated patterns to specify which files to include").option("-e --excludes <patterns:file[]>", "Comma separated patterns to specify which files to exclude").action(generateMetadata2);
|
|
80468
|
+
var command29 = new Command().description("Generate metadata (locks, schemas) for all scripts, flows, and apps").arguments("[folder:string]").option("--yes", "Skip confirmation prompt").option("--dry-run", "Show what would be updated without making changes").option("--lock-only", "Re-generate only the lock files").option("--schema-only", "Re-generate only script schemas (skips flows and apps)").option("--skip-scripts", "Skip processing scripts").option("--skip-flows", "Skip processing flows").option("--skip-apps", "Skip processing apps").option("--strict-folder-boundaries", "Only update items inside the specified folder (requires folder argument)").option("-i --includes <patterns:file[]>", "Comma separated patterns to specify which files to include").option("-e --excludes <patterns:file[]>", "Comma separated patterns to specify which files to exclude").action(generateMetadata2);
|
|
79976
80469
|
var generate_metadata_default = command29;
|
|
79977
80470
|
|
|
79978
80471
|
// src/commands/docs/docs.ts
|
|
@@ -80050,7 +80543,7 @@ var docs_default = command30;
|
|
|
80050
80543
|
|
|
80051
80544
|
// src/main.ts
|
|
80052
80545
|
await init_context();
|
|
80053
|
-
var VERSION = "1.
|
|
80546
|
+
var VERSION = "1.663.0";
|
|
80054
80547
|
var command31 = new Command().name("wmill").action(() => info(`Welcome to Windmill CLI ${VERSION}. Use -h for help.`)).description("Windmill CLI").globalOption("--workspace <workspace:string>", "Specify the target workspace. This overrides the default workspace.").globalOption("--debug --verbose", "Show debug/verbose logs").globalOption("--show-diffs", "Show diff informations when syncing (may show sensitive informations)").globalOption("--token <token:string>", "Specify an API token. This will override any stored token.").globalOption("--base-url <baseUrl:string>", "Specify the base URL of the API. If used, --token and --workspace are required and no local remote/workspace already set will be used.").globalOption("--config-dir <configDir:string>", "Specify a custom config directory. Overrides WMILL_CONFIG_DIR environment variable and default ~/.config location.").env("HEADERS <headers:string>", `Specify headers to use for all requests. e.g: "HEADERS='h1: v1, h2: v2'"`).version(VERSION).versionOption(false).command("init", init_default).command("app", app_default).command("flow", flow_default).command("script", script_default).command("workspace", workspace_default).command("resource", resource_default).command("resource-type", resource_type_default).command("user", user_default).command("variable", variable_default).command("hub", hub_default).command("folder", folder_default).command("schedule", schedule_default).command("trigger", trigger_default).command("dev", dev_default2).command("sync", sync_default).command("lint", lint_default).command("gitsync-settings", gitsync_settings_default).command("instance", instance_default).command("worker-groups", worker_groups_default).command("workers", workers_default).command("queues", queues_default).command("dependencies", dependencies_default).command("jobs", jobs_default).command("generate-metadata", generate_metadata_default).command("docs", docs_default).command("version --version", "Show version information").action(async (opts) => {
|
|
80055
80548
|
console.log("CLI version: " + VERSION);
|
|
80056
80549
|
try {
|