windmill-cli 1.693.4 → 1.693.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/esm/main.js +1124 -768
  2. package/package.json +1 -1
package/esm/main.js CHANGED
@@ -5,43 +5,25 @@ var __getProtoOf = Object.getPrototypeOf;
5
5
  var __defProp = Object.defineProperty;
6
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- function __accessProp(key) {
9
- return this[key];
10
- }
11
- var __toESMCache_node;
12
- var __toESMCache_esm;
13
8
  var __toESM = (mod, isNodeMode, target) => {
14
- var canCache = mod != null && typeof mod === "object";
15
- if (canCache) {
16
- var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
17
- var cached = cache.get(mod);
18
- if (cached)
19
- return cached;
20
- }
21
9
  target = mod != null ? __create(__getProtoOf(mod)) : {};
22
10
  const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
23
11
  for (let key of __getOwnPropNames(mod))
24
12
  if (!__hasOwnProp.call(to, key))
25
13
  __defProp(to, key, {
26
- get: __accessProp.bind(mod, key),
14
+ get: () => mod[key],
27
15
  enumerable: true
28
16
  });
29
- if (canCache)
30
- cache.set(mod, to);
31
17
  return to;
32
18
  };
33
19
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
34
- var __returnValue = (v) => v;
35
- function __exportSetter(name, newValue) {
36
- this[name] = __returnValue.bind(null, newValue);
37
- }
38
20
  var __export = (target, all) => {
39
21
  for (var name in all)
40
22
  __defProp(target, name, {
41
23
  get: all[name],
42
24
  enumerable: true,
43
25
  configurable: true,
44
- set: __exportSetter.bind(all, name)
26
+ set: (newValue) => all[name] = () => newValue
45
27
  });
46
28
  };
47
29
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
@@ -13203,7 +13185,7 @@ function sameValueZero(a, b) {
13203
13185
  }
13204
13186
  function equal(a, b) {
13205
13187
  const seen = new Map;
13206
- return function compare3(a2, b2) {
13188
+ return function compare(a2, b2) {
13207
13189
  if (sameValueZero(a2, b2))
13208
13190
  return true;
13209
13191
  if (isPrimitive(a2) || isPrimitive(b2))
@@ -13228,7 +13210,7 @@ function equal(a, b) {
13228
13210
  throw new TypeError("Cannot compare WeakSet instances");
13229
13211
  }
13230
13212
  if (a2 instanceof WeakRef) {
13231
- return compare3(a2.deref(), b2.deref());
13213
+ return compare(a2.deref(), b2.deref());
13232
13214
  }
13233
13215
  if (seen.get(a2) === b2) {
13234
13216
  return true;
@@ -13250,7 +13232,7 @@ function equal(a, b) {
13250
13232
  return a2.symmetricDifference(b2).size === 0;
13251
13233
  }
13252
13234
  for (const key of aKeys) {
13253
- if (!b2.has(key) || !compare3(a2.get(key), b2.get(key))) {
13235
+ if (!b2.has(key) || !compare(a2.get(key), b2.get(key))) {
13254
13236
  return false;
13255
13237
  }
13256
13238
  }
@@ -13259,9 +13241,9 @@ function equal(a, b) {
13259
13241
  let unmatchedEntries = a2.size;
13260
13242
  for (const [aKey, aValue] of a2.entries()) {
13261
13243
  for (const [bKey, bValue] of b2.entries()) {
13262
- if (!compare3(aKey, bKey))
13244
+ if (!compare(aKey, bKey))
13263
13245
  continue;
13264
- if (aKey === aValue && bKey === bValue || compare3(aValue, bValue)) {
13246
+ if (aKey === aValue && bKey === bValue || compare(aValue, bValue)) {
13265
13247
  unmatchedEntries--;
13266
13248
  break;
13267
13249
  }
@@ -13281,7 +13263,7 @@ function equal(a, b) {
13281
13263
  keys = getKeysDeep(a2).union(getKeysDeep(b2));
13282
13264
  }
13283
13265
  for (const key of keys) {
13284
- if (!compare3(a2[key], b2[key])) {
13266
+ if (!compare(a2[key], b2[key])) {
13285
13267
  return false;
13286
13268
  }
13287
13269
  if (key in a2 && !(key in b2) || key in b2 && !(key in a2)) {
@@ -26462,6 +26444,9 @@ function getScriptBasePathFromModulePath(p) {
26462
26444
  return;
26463
26445
  return norm.slice(0, idx);
26464
26446
  }
26447
+ function scriptPathToRemotePath(p) {
26448
+ return (isModuleEntryPoint(p) ? getScriptBasePathFromModulePath(p) : p.substring(0, p.indexOf("."))).replaceAll(SEP2, "/");
26449
+ }
26465
26450
  function getDeleteSuffix(type, format6) {
26466
26451
  return getFolderSuffixes()[type] + "/" + METADATA_FILES[type][format6];
26467
26452
  }
@@ -30420,7 +30405,7 @@ var require_stream = __commonJS((exports, module) => {
30420
30405
  if (!duplex.push(data3))
30421
30406
  ws.pause();
30422
30407
  });
30423
- ws.once("error", function error2(err) {
30408
+ ws.once("error", function error(err) {
30424
30409
  if (duplex.destroyed)
30425
30410
  return;
30426
30411
  terminateOnDestroy = false;
@@ -30438,7 +30423,7 @@ var require_stream = __commonJS((exports, module) => {
30438
30423
  return;
30439
30424
  }
30440
30425
  let called = false;
30441
- ws.once("error", function error2(err2) {
30426
+ ws.once("error", function error(err2) {
30442
30427
  called = true;
30443
30428
  callback(err2);
30444
30429
  });
@@ -30452,7 +30437,7 @@ var require_stream = __commonJS((exports, module) => {
30452
30437
  };
30453
30438
  duplex._final = function(callback) {
30454
30439
  if (ws.readyState === ws.CONNECTING) {
30455
- ws.once("open", function open2() {
30440
+ ws.once("open", function open() {
30456
30441
  duplex._final(callback);
30457
30442
  });
30458
30443
  return;
@@ -30476,7 +30461,7 @@ var require_stream = __commonJS((exports, module) => {
30476
30461
  };
30477
30462
  duplex._write = function(chunk, encoding, callback) {
30478
30463
  if (ws.readyState === ws.CONNECTING) {
30479
- ws.once("open", function open2() {
30464
+ ws.once("open", function open() {
30480
30465
  duplex._write(chunk, encoding, callback);
30481
30466
  });
30482
30467
  return;
@@ -35527,7 +35512,7 @@ var require_BufferList = __commonJS((exports, module) => {
35527
35512
  this.tail = null;
35528
35513
  this.length = 0;
35529
35514
  }
35530
- BufferList.prototype.push = function push2(v) {
35515
+ BufferList.prototype.push = function push(v) {
35531
35516
  var entry = { data: v, next: null };
35532
35517
  if (this.length > 0)
35533
35518
  this.tail.next = entry;
@@ -35558,7 +35543,7 @@ var require_BufferList = __commonJS((exports, module) => {
35558
35543
  this.head = this.tail = null;
35559
35544
  this.length = 0;
35560
35545
  };
35561
- BufferList.prototype.join = function join9(s) {
35546
+ BufferList.prototype.join = function join(s) {
35562
35547
  if (this.length === 0)
35563
35548
  return "";
35564
35549
  var p = this.head;
@@ -37493,7 +37478,7 @@ var require_nodejsUtils = __commonJS((exports, module) => {
37493
37478
 
37494
37479
  // node_modules/set-immediate-shim/index.js
37495
37480
  var require_set_immediate_shim = __commonJS((exports, module) => {
37496
- module.exports = typeof setImmediate === "function" ? setImmediate : function setImmediate2() {
37481
+ module.exports = typeof setImmediate === "function" ? setImmediate : function setImmediate() {
37497
37482
  var args = [].slice.apply(arguments);
37498
37483
  args.splice(1, 0, 0);
37499
37484
  setTimeout.apply(null, args);
@@ -45848,7 +45833,7 @@ var require_headers = __commonJS((exports) => {
45848
45833
  }
45849
45834
  return result;
45850
45835
  };
45851
- exports.encode = function encode2(opts) {
45836
+ exports.encode = function encode(opts) {
45852
45837
  const buf = b4a.alloc(512);
45853
45838
  let name = opts.name;
45854
45839
  let prefix = "";
@@ -45889,7 +45874,7 @@ var require_headers = __commonJS((exports) => {
45889
45874
  b4a.write(buf, encodeOct(cksum(buf), 6), 148);
45890
45875
  return buf;
45891
45876
  };
45892
- exports.decode = function decode2(buf, filenameEncoding, allowUnknownFormat) {
45877
+ exports.decode = function decode(buf, filenameEncoding, allowUnknownFormat) {
45893
45878
  let typeflag = buf[156] === 0 ? 0 : buf[156] - ZERO_OFFSET;
45894
45879
  let name = decodeStr(buf, 0, 100, filenameEncoding);
45895
45880
  const mode = decodeOct(buf, 100, 8);
@@ -48800,7 +48785,7 @@ var require_subschema = __commonJS((exports) => {
48800
48785
 
48801
48786
  // node_modules/fast-deep-equal/index.js
48802
48787
  var require_fast_deep_equal = __commonJS((exports, module) => {
48803
- module.exports = function equal2(a, b) {
48788
+ module.exports = function equal(a, b) {
48804
48789
  if (a === b)
48805
48790
  return true;
48806
48791
  if (a && b && typeof a == "object" && typeof b == "object") {
@@ -48812,7 +48797,7 @@ var require_fast_deep_equal = __commonJS((exports, module) => {
48812
48797
  if (length != b.length)
48813
48798
  return false;
48814
48799
  for (i = length;i-- !== 0; )
48815
- if (!equal2(a[i], b[i]))
48800
+ if (!equal(a[i], b[i]))
48816
48801
  return false;
48817
48802
  return true;
48818
48803
  }
@@ -48831,7 +48816,7 @@ var require_fast_deep_equal = __commonJS((exports, module) => {
48831
48816
  return false;
48832
48817
  for (i = length;i-- !== 0; ) {
48833
48818
  var key = keys[i];
48834
- if (!equal2(a[key], b[key]))
48819
+ if (!equal(a[key], b[key]))
48835
48820
  return false;
48836
48821
  }
48837
48822
  return true;
@@ -61660,7 +61645,7 @@ async function handleFile(path8, workspace, alreadySynced, message, opts, rawWor
61660
61645
  }
61661
61646
  debug(`Processing local script ${path8}`);
61662
61647
  alreadySynced.push(path8);
61663
- const remotePath = moduleEntryPoint ? getScriptBasePathFromModulePath(path8).replaceAll(SEP4, "/") : path8.substring(0, path8.indexOf(".")).replaceAll(SEP4, "/");
61648
+ const remotePath = scriptPathToRemotePath(path8);
61664
61649
  const language = inferContentTypeFromFilePath(path8, opts?.defaultTs);
61665
61650
  const codebase = language == "bun" ? findCodebase(path8, codebases) : undefined;
61666
61651
  let bundleContent = undefined;
@@ -63593,8 +63578,11 @@ function getLanguageFromExtension(ext2, defaultTs = "bun") {
63593
63578
  }
63594
63579
  return;
63595
63580
  }
63596
- function sanitizeForFilesystem(summary) {
63597
- const name = summary.replaceAll(" ", "_").replace(/[/\\:*?"<>|\x00-\x1f\x7f]/g, "").replace(/_+/g, "_").replace(/^[._]+|[._]+$/g, "");
63581
+ function sanitizeForFilesystem(summary, options) {
63582
+ let name = summary.replaceAll(" ", "_").replace(/[/\\:*?"<>|\x00-\x1f\x7f]/g, "").replace(/_+/g, "_").replace(/^[._]+|[._]+$/g, "");
63583
+ if (!options?.preserveCase) {
63584
+ name = name.toLowerCase();
63585
+ }
63598
63586
  return WINDOWS_RESERVED.test(name.toLowerCase()) ? `_${name}` : name;
63599
63587
  }
63600
63588
  function newPathAssigner(defaultTs, options) {
@@ -63626,7 +63614,7 @@ function newRawAppPathAssigner(defaultTs) {
63626
63614
  const seen_names = new Set;
63627
63615
  function assignPath(summary, language) {
63628
63616
  let name;
63629
- name = summary ? sanitizeForFilesystem(summary) : "";
63617
+ name = summary ? sanitizeForFilesystem(summary, { preserveCase: true }) : "";
63630
63618
  let original_name = name;
63631
63619
  if (name == "") {
63632
63620
  original_name = "runnable";
@@ -63995,6 +63983,19 @@ var init_relative_imports = __esm(async () => {
63995
63983
  // src/commands/flow/flow_metadata.ts
63996
63984
  import * as path10 from "node:path";
63997
63985
  import { sep as SEP7 } from "node:path";
63986
+ async function isFlowDirectlyStale(folder, hashes, conf) {
63987
+ if (await checkifMetadataUptodate(folder, hashes[TOP_HASH], conf, TOP_HASH)) {
63988
+ return false;
63989
+ }
63990
+ const fileEntries = Object.entries(hashes).filter(([k]) => k !== TOP_HASH);
63991
+ if (fileEntries.length === 0)
63992
+ return true;
63993
+ for (const [k, h] of fileEntries) {
63994
+ if (!await checkifMetadataUptodate(folder, h, conf, k))
63995
+ return true;
63996
+ }
63997
+ return false;
63998
+ }
63998
63999
  async function generateFlowHash(rawWorkspaceDependencies, folder, defaultTs) {
63999
64000
  const elems = await FSFSElement(path10.join(process.cwd(), folder), [], true);
64000
64001
  const hashes = {};
@@ -64014,6 +64015,14 @@ async function generateFlowLockInternal(folder, dryRun, workspace, opts, justUpd
64014
64015
  if (folder.endsWith(SEP7)) {
64015
64016
  folder = folder.substring(0, folder.length - 1);
64016
64017
  }
64018
+ if (opts.rehashOnly) {
64019
+ const hashes = await generateFlowHash({}, folder, opts.defaultTs);
64020
+ await clearGlobalLock(folder);
64021
+ for (const [k, v] of Object.entries(hashes)) {
64022
+ await updateMetadataGlobalLock(folder, v, k);
64023
+ }
64024
+ return;
64025
+ }
64017
64026
  const remote_path = extractNameFromFolder(folder.replaceAll(SEP7, "/"), "flow");
64018
64027
  if (!justUpdateMetadataLock && !noStaleMessage) {
64019
64028
  info(`Generating lock for flow ${folder} at ${remote_path}`);
@@ -64043,7 +64052,7 @@ async function generateFlowLockInternal(folder, dryRun, workspace, opts, justUpd
64043
64052
  inlineScriptPaths.push(treePath);
64044
64053
  }
64045
64054
  const hashes = await generateFlowHash({}, folder, opts.defaultTs);
64046
- const isDirectlyStale = !await checkifMetadataUptodate(folder, hashes[TOP_HASH], conf, TOP_HASH);
64055
+ const isDirectlyStale = await isFlowDirectlyStale(folder, hashes, conf);
64047
64056
  await tree.addNode(folderNormalized, "", "bun", "", inlineScriptPaths, "flow", folderNormalized, folder, isDirectlyStale);
64048
64057
  return;
64049
64058
  }
@@ -64052,7 +64061,7 @@ async function generateFlowLockInternal(folder, dryRun, workspace, opts, justUpd
64052
64061
  const rawWorkspaceDependencies = await getRawWorkspaceDependencies(true);
64053
64062
  filteredDeps = await filterWorkspaceDependenciesForFlow(flowValue.value, rawWorkspaceDependencies, folder);
64054
64063
  const hashes = await generateFlowHash(filteredDeps, folder, opts.defaultTs);
64055
- const isDirectlyStale = !await checkifMetadataUptodate(folder, hashes[TOP_HASH], conf, TOP_HASH);
64064
+ const isDirectlyStale = await isFlowDirectlyStale(folder, hashes, conf);
64056
64065
  if (!isDirectlyStale) {
64057
64066
  if (!noStaleMessage) {
64058
64067
  info(colors.green(`Flow ${remote_path} metadata is up-to-date, skipping`));
@@ -64227,6 +64236,662 @@ var init_flow_metadata = __esm(async () => {
64227
64236
  import_yaml9 = __toESM(require_dist(), 1);
64228
64237
  });
64229
64238
 
64239
+ // src/utils/dependency_tree.ts
64240
+ async function uploadScripts(tree, workspace) {
64241
+ const scriptHashes = {};
64242
+ const workspaceDeps = [];
64243
+ for (const path11 of tree.allPaths()) {
64244
+ const content = tree.getContent(path11);
64245
+ const itemType = tree.getItemType(path11);
64246
+ if (itemType === "dependencies") {
64247
+ if (content === undefined)
64248
+ continue;
64249
+ const info2 = workspaceDependenciesPathToLanguageAndFilename(path11);
64250
+ if (info2) {
64251
+ const hash2 = await generateHash(content);
64252
+ workspaceDeps.push({ path: path11, language: info2.language, name: info2.name, hash: hash2 });
64253
+ }
64254
+ } else if (itemType === "script") {
64255
+ if (!content)
64256
+ continue;
64257
+ const hash2 = await generateHash(content);
64258
+ scriptHashes[path11] = hash2;
64259
+ }
64260
+ }
64261
+ if (Object.keys(scriptHashes).length === 0 && workspaceDeps.length === 0)
64262
+ return;
64263
+ const mismatched = await diffRawScriptsWithDeployed({
64264
+ workspace: workspace.workspaceId,
64265
+ requestBody: {
64266
+ scripts: scriptHashes,
64267
+ workspace_deps: workspaceDeps
64268
+ }
64269
+ });
64270
+ for (const path11 of mismatched) {
64271
+ const content = tree.getContent(path11);
64272
+ const itemType = tree.getItemType(path11);
64273
+ if (itemType === "dependencies") {
64274
+ if (content !== undefined) {
64275
+ tree.setContentHash(path11, "mismatched");
64276
+ }
64277
+ } else if (content) {
64278
+ const hash2 = await storeRawScriptTemp({
64279
+ workspace: workspace.workspaceId,
64280
+ requestBody: content
64281
+ });
64282
+ tree.setContentHash(path11, hash2);
64283
+ }
64284
+ }
64285
+ }
64286
+
64287
+ class DoubleLinkedDependencyTree {
64288
+ nodes = new Map;
64289
+ workspaceDeps = {};
64290
+ setWorkspaceDeps(deps) {
64291
+ this.workspaceDeps = deps;
64292
+ }
64293
+ async addNode(path11, content, language, metadata, imports, itemType, folder, originalPath, isDirectlyStale, isRawApp) {
64294
+ const hasWorkspaceDeps = itemType === "script" || itemType === "inline_script";
64295
+ const filteredDeps = hasWorkspaceDeps ? filterWorkspaceDependencies(this.workspaceDeps, content, language) : {};
64296
+ const stalenessHash = await generateScriptHash({}, content, metadata);
64297
+ if (!this.nodes.has(path11)) {
64298
+ this.nodes.set(path11, {
64299
+ content: "",
64300
+ stalenessHash: "",
64301
+ language: "deno",
64302
+ metadata: "",
64303
+ imports: new Set,
64304
+ importedBy: new Set,
64305
+ itemType: "script",
64306
+ folder: "",
64307
+ originalPath: "",
64308
+ isDirectlyStale: false
64309
+ });
64310
+ }
64311
+ const node = this.nodes.get(path11);
64312
+ node.content = content;
64313
+ node.stalenessHash = stalenessHash;
64314
+ node.language = language;
64315
+ node.metadata = metadata;
64316
+ node.itemType = itemType;
64317
+ node.folder = folder;
64318
+ node.originalPath = originalPath;
64319
+ node.isDirectlyStale = isDirectlyStale;
64320
+ node.isRawApp = isRawApp;
64321
+ const filteredDepsPaths = Object.keys(filteredDeps);
64322
+ for (const depsPath of filteredDepsPaths) {
64323
+ if (!this.nodes.has(depsPath)) {
64324
+ const depsInfo = workspaceDependenciesPathToLanguageAndFilename(depsPath);
64325
+ const contentHash = await generateHash(filteredDeps[depsPath] + depsPath);
64326
+ const isUpToDate = await checkifMetadataUptodate(depsPath, contentHash, undefined);
64327
+ this.nodes.set(depsPath, {
64328
+ content: filteredDeps[depsPath],
64329
+ stalenessHash: "",
64330
+ language: depsInfo?.language ?? "deno",
64331
+ metadata: "",
64332
+ imports: new Set,
64333
+ importedBy: new Set,
64334
+ itemType: "dependencies",
64335
+ folder: "",
64336
+ originalPath: depsPath,
64337
+ isDirectlyStale: !isUpToDate
64338
+ });
64339
+ }
64340
+ }
64341
+ const allImports = [...imports, ...filteredDepsPaths];
64342
+ for (const importPath of allImports) {
64343
+ node.imports.add(importPath);
64344
+ if (!this.nodes.has(importPath)) {
64345
+ this.nodes.set(importPath, {
64346
+ content: "",
64347
+ stalenessHash: "",
64348
+ language: "deno",
64349
+ metadata: "",
64350
+ imports: new Set,
64351
+ importedBy: new Set,
64352
+ itemType: "script",
64353
+ folder: "",
64354
+ originalPath: "",
64355
+ isDirectlyStale: false
64356
+ });
64357
+ }
64358
+ this.nodes.get(importPath).importedBy.add(path11);
64359
+ }
64360
+ }
64361
+ getContent(path11) {
64362
+ return this.nodes.get(path11)?.content;
64363
+ }
64364
+ getStalenessHash(path11) {
64365
+ return this.nodes.get(path11)?.stalenessHash;
64366
+ }
64367
+ getContentHash(path11) {
64368
+ return this.nodes.get(path11)?.contentHash;
64369
+ }
64370
+ setContentHash(path11, hash2) {
64371
+ const node = this.nodes.get(path11);
64372
+ if (node) {
64373
+ node.contentHash = hash2;
64374
+ }
64375
+ }
64376
+ getLanguage(path11) {
64377
+ return this.nodes.get(path11)?.language;
64378
+ }
64379
+ getMetadata(path11) {
64380
+ return this.nodes.get(path11)?.metadata;
64381
+ }
64382
+ getStaleReason(path11) {
64383
+ return this.nodes.get(path11)?.staleReason;
64384
+ }
64385
+ getItemType(path11) {
64386
+ return this.nodes.get(path11)?.itemType;
64387
+ }
64388
+ getFolder(path11) {
64389
+ return this.nodes.get(path11)?.folder;
64390
+ }
64391
+ getIsRawApp(path11) {
64392
+ return this.nodes.get(path11)?.isRawApp;
64393
+ }
64394
+ getIsDirectlyStale(path11) {
64395
+ return this.nodes.get(path11)?.isDirectlyStale ?? false;
64396
+ }
64397
+ getOriginalPath(path11) {
64398
+ return this.nodes.get(path11)?.originalPath;
64399
+ }
64400
+ getImports(path11) {
64401
+ return this.nodes.get(path11)?.imports;
64402
+ }
64403
+ isStale(path11) {
64404
+ return this.nodes.get(path11)?.staleReason !== undefined;
64405
+ }
64406
+ propagateStaleness() {
64407
+ const directlyStale = new Set;
64408
+ for (const [path11, node] of this.nodes.entries()) {
64409
+ if (node.isDirectlyStale) {
64410
+ directlyStale.add(path11);
64411
+ node.staleReason = "content changed";
64412
+ }
64413
+ }
64414
+ const allStale = new Set(directlyStale);
64415
+ const queue = [...directlyStale];
64416
+ const visited = new Set;
64417
+ while (queue.length > 0) {
64418
+ const scriptPath = queue.shift();
64419
+ if (visited.has(scriptPath))
64420
+ continue;
64421
+ visited.add(scriptPath);
64422
+ const node = this.nodes.get(scriptPath);
64423
+ if (!node)
64424
+ continue;
64425
+ for (const importer of node.importedBy) {
64426
+ if (!allStale.has(importer)) {
64427
+ allStale.add(importer);
64428
+ queue.push(importer);
64429
+ const importerNode = this.nodes.get(importer);
64430
+ if (importerNode)
64431
+ importerNode.staleReason = `depends on ${scriptPath}`;
64432
+ }
64433
+ }
64434
+ }
64435
+ }
64436
+ traverseTransitive(scriptPath, callback) {
64437
+ const queue = [scriptPath];
64438
+ const visited = new Set;
64439
+ while (queue.length > 0) {
64440
+ const current = queue.shift();
64441
+ if (visited.has(current))
64442
+ continue;
64443
+ visited.add(current);
64444
+ const node = this.nodes.get(current);
64445
+ if (!node)
64446
+ continue;
64447
+ for (const importPath of node.imports) {
64448
+ const importNode = this.nodes.get(importPath);
64449
+ if (importNode) {
64450
+ const stop = callback(importPath, importNode);
64451
+ if (!stop) {
64452
+ queue.push(importPath);
64453
+ }
64454
+ }
64455
+ }
64456
+ }
64457
+ }
64458
+ allPaths() {
64459
+ return this.nodes.keys();
64460
+ }
64461
+ *stalePaths() {
64462
+ for (const [path11, node] of this.nodes.entries()) {
64463
+ if (node.staleReason) {
64464
+ yield path11;
64465
+ }
64466
+ }
64467
+ }
64468
+ has(path11) {
64469
+ return this.nodes.has(path11);
64470
+ }
64471
+ getMismatchedWorkspaceDeps() {
64472
+ const result = {};
64473
+ for (const [path11, node] of this.nodes.entries()) {
64474
+ if (node.itemType === "dependencies" && node.contentHash && node.content !== undefined) {
64475
+ result[path11] = node.content;
64476
+ }
64477
+ }
64478
+ return result;
64479
+ }
64480
+ getTempScriptRefs(scriptPath) {
64481
+ const result = {};
64482
+ this.traverseTransitive(scriptPath, (_path, node) => {
64483
+ if (node.contentHash) {
64484
+ result[_path] = node.contentHash;
64485
+ }
64486
+ });
64487
+ return result;
64488
+ }
64489
+ async persistDepsHashes(depsPaths) {
64490
+ for (const path11 of depsPaths) {
64491
+ const node = this.nodes.get(path11);
64492
+ if (node?.itemType === "dependencies" && node.content !== undefined) {
64493
+ const hash2 = await generateHash(node.content + path11);
64494
+ await updateMetadataGlobalLock(path11, hash2);
64495
+ }
64496
+ }
64497
+ }
64498
+ get size() {
64499
+ return this.nodes.size;
64500
+ }
64501
+ }
64502
+ var init_dependency_tree = __esm(async () => {
64503
+ init_services_gen();
64504
+ await __promiseAll([
64505
+ init_metadata(),
64506
+ init_utils()
64507
+ ]);
64508
+ });
64509
+
64510
+ // src/commands/generate-metadata/generate-metadata.ts
64511
+ var exports_generate_metadata = {};
64512
+ __export(exports_generate_metadata, {
64513
+ rehashOnly: () => rehashOnly,
64514
+ default: () => generate_metadata_default
64515
+ });
64516
+ import { sep as SEP8 } from "node:path";
64517
+ async function walkLocalScripts(codebases, ignore) {
64518
+ const elems = await elementsToMap(await FSFSElement(process.cwd(), codebases, false), (p, isD) => !isD && !exts.some((ext2) => p.endsWith(ext2)) || ignore(p, isD) || isFolderResourcePathAnyFormat(p) || isScriptModulePath(p) && !isModuleEntryPoint(p), false, {});
64519
+ return Object.keys(elems);
64520
+ }
64521
+ async function walkLocalFlowFolders(ignore) {
64522
+ const elems = await elementsToMap(await FSFSElement(process.cwd(), [], true), (p, isD) => ignore(p, isD) || !isD && !p.endsWith(SEP8 + "flow.yaml") && !p.endsWith(SEP8 + "flow.json"), false, {});
64523
+ return Object.keys(elems).map((x) => x.substring(0, x.lastIndexOf(SEP8)));
64524
+ }
64525
+ async function walkLocalAppItems(ignore) {
64526
+ const elems = await elementsToMap(await FSFSElement(process.cwd(), [], true), (p, isD) => ignore(p, isD) || !isD && !p.endsWith(SEP8 + "raw_app.yaml") && !p.endsWith(SEP8 + "app.yaml"), false, {});
64527
+ return Object.keys(elems).map((p) => ({
64528
+ folder: p.substring(0, p.lastIndexOf(SEP8)),
64529
+ rawApp: p.endsWith(SEP8 + "raw_app.yaml")
64530
+ }));
64531
+ }
64532
+ function categorizeLocalFiles(paths) {
64533
+ const scripts = [];
64534
+ const flowFolderSet = new Set;
64535
+ const appPaths = [];
64536
+ for (const p of paths) {
64537
+ if (p.endsWith(SEP8 + "flow.yaml") || p.endsWith(SEP8 + "flow.json")) {
64538
+ flowFolderSet.add(p.substring(0, p.lastIndexOf(SEP8)));
64539
+ } else if (p.endsWith(SEP8 + "raw_app.yaml") || p.endsWith(SEP8 + "app.yaml")) {
64540
+ appPaths.push(p);
64541
+ } else if (exts.some((ext2) => p.endsWith(ext2)) && !isFolderResourcePathAnyFormat(p) && !(isScriptModulePath(p) && !isModuleEntryPoint(p))) {
64542
+ scripts.push(p);
64543
+ }
64544
+ }
64545
+ return { scripts, flowFolders: [...flowFolderSet], appPaths };
64546
+ }
64547
+ async function rehashOnly(opts, folder, rehashFilter) {
64548
+ const codebases = await listSyncCodebases(opts);
64549
+ const ignore = await ignoreF(opts);
64550
+ const counts = { scripts: 0, flows: 0, apps: 0 };
64551
+ const folderFilter = folder?.replaceAll("\\", "/").replace(/^\.\//, "").replace(/\/$/, "");
64552
+ const inFilter = (p) => {
64553
+ if (!folderFilter)
64554
+ return true;
64555
+ const n = p.replaceAll("\\", "/");
64556
+ return n === folderFilter || n.startsWith(folderFilter + "/");
64557
+ };
64558
+ const conf = rehashFilter?.missingOnly ? await readLockfile() : undefined;
64559
+ const isFlatKeyed = conf?.version === "v2";
64560
+ const hasEntry = (key, subpath) => {
64561
+ if (!conf?.locks)
64562
+ return false;
64563
+ if (isFlatKeyed) {
64564
+ const fullKey = subpath ? `${key}+${subpath}` : key;
64565
+ return conf.locks[fullKey] !== undefined || conf.locks["./" + fullKey] !== undefined;
64566
+ }
64567
+ for (const p of [key, "./" + key]) {
64568
+ const obj = conf.locks[p];
64569
+ if (obj === undefined)
64570
+ continue;
64571
+ if (!subpath)
64572
+ return true;
64573
+ if (typeof obj === "object" && obj?.[subpath] !== undefined)
64574
+ return true;
64575
+ }
64576
+ return false;
64577
+ };
64578
+ const skipIfExisting = (remotePath, subpath) => !!rehashFilter?.missingOnly && hasEntry(remotePath, subpath);
64579
+ let scriptPaths;
64580
+ let flowFolders;
64581
+ let appPaths;
64582
+ if (rehashFilter?.localFiles) {
64583
+ const cat = categorizeLocalFiles(rehashFilter.localFiles);
64584
+ scriptPaths = cat.scripts;
64585
+ flowFolders = cat.flowFolders;
64586
+ appPaths = cat.appPaths.map((p) => ({
64587
+ folder: p.substring(0, p.lastIndexOf(SEP8)),
64588
+ rawApp: p.endsWith(SEP8 + "raw_app.yaml")
64589
+ }));
64590
+ } else {
64591
+ scriptPaths = await walkLocalScripts(codebases, ignore);
64592
+ flowFolders = await walkLocalFlowFolders(ignore);
64593
+ appPaths = await walkLocalAppItems(ignore);
64594
+ }
64595
+ const stubWorkspace = {};
64596
+ const rehashOpts = { ...opts, rehashOnly: true };
64597
+ if (!rehashFilter?.skipScripts) {
64598
+ for (const e of scriptPaths) {
64599
+ const remotePath = scriptPathToRemotePath(e);
64600
+ if (!inFilter(remotePath))
64601
+ continue;
64602
+ if (rehashFilter?.missingOnly) {
64603
+ if (skipIfExisting(remotePath) || skipIfExisting(remotePath, "__script_hash"))
64604
+ continue;
64605
+ }
64606
+ try {
64607
+ await generateScriptMetadataInternal(e, stubWorkspace, rehashOpts, false, true, {}, codebases, false);
64608
+ counts.scripts++;
64609
+ } catch (err) {
64610
+ warn(`Skipping ${e}: ${err instanceof Error ? err.message : err}`);
64611
+ }
64612
+ }
64613
+ }
64614
+ if (!rehashFilter?.skipFlows) {
64615
+ for (const f of flowFolders) {
64616
+ if (!inFilter(f))
64617
+ continue;
64618
+ if (rehashFilter?.missingOnly) {
64619
+ const folderNormalized = f.replaceAll(SEP8, "/");
64620
+ if (skipIfExisting(folderNormalized, "__flow_hash"))
64621
+ continue;
64622
+ }
64623
+ try {
64624
+ await generateFlowLockInternal(f, false, stubWorkspace, rehashOpts, false, true);
64625
+ counts.flows++;
64626
+ } catch (err) {
64627
+ warn(`Skipping ${f}: ${err instanceof Error ? err.message : err}`);
64628
+ }
64629
+ }
64630
+ }
64631
+ if (!rehashFilter?.skipApps) {
64632
+ for (const { folder: appFolder, rawApp } of appPaths) {
64633
+ if (!inFilter(appFolder))
64634
+ continue;
64635
+ if (rehashFilter?.missingOnly) {
64636
+ const folderNormalized = appFolder.replaceAll(SEP8, "/");
64637
+ if (skipIfExisting(folderNormalized, "__app_hash"))
64638
+ continue;
64639
+ }
64640
+ try {
64641
+ await generateAppLocksInternal(appFolder, rawApp, false, stubWorkspace, rehashOpts, false, true);
64642
+ counts.apps++;
64643
+ } catch (err) {
64644
+ warn(`Skipping ${appFolder}: ${err instanceof Error ? err.message : err}`);
64645
+ }
64646
+ }
64647
+ }
64648
+ if (counts.scripts + counts.flows + counts.apps > 0 || !rehashFilter?.missingOnly) {
64649
+ info(`Rehashed ${colors.bold(String(counts.scripts))} script(s), ` + `${colors.bold(String(counts.flows))} flow(s), ` + `${colors.bold(String(counts.apps))} app(s) from disk.`);
64650
+ }
64651
+ return counts;
64652
+ }
64653
+ async function generateMetadata2(opts, folder) {
64654
+ if (folder === "") {
64655
+ folder = undefined;
64656
+ }
64657
+ const workspace = await resolveWorkspace(opts);
64658
+ await requireLogin(opts);
64659
+ opts = await mergeConfigWithConfigFile(opts);
64660
+ const rawWorkspaceDependencies = await getRawWorkspaceDependencies(false);
64661
+ const codebases = await listSyncCodebases(opts);
64662
+ const ignore = await ignoreF(opts);
64663
+ const skipScripts = opts.skipScripts ?? false;
64664
+ const skipFlows = opts.skipFlows ?? opts.schemaOnly ?? false;
64665
+ const skipApps = opts.skipApps ?? opts.schemaOnly ?? false;
64666
+ const checking = [];
64667
+ if (!skipScripts)
64668
+ checking.push("scripts");
64669
+ if (!skipFlows)
64670
+ checking.push("flows");
64671
+ if (!skipApps)
64672
+ checking.push("apps");
64673
+ if (checking.length === 0) {
64674
+ info(colors.yellow("Nothing to check (all types skipped)"));
64675
+ return;
64676
+ }
64677
+ info(`Checking ${checking.join(", ")}...`);
64678
+ const tree = new DoubleLinkedDependencyTree;
64679
+ tree.setWorkspaceDeps(rawWorkspaceDependencies);
64680
+ if (!skipScripts) {
64681
+ for (const e of await walkLocalScripts(codebases, ignore)) {
64682
+ await generateScriptMetadataInternal(e, workspace, opts, true, true, rawWorkspaceDependencies, codebases, false, tree);
64683
+ }
64684
+ }
64685
+ if (!skipFlows) {
64686
+ for (const flowFolder of await walkLocalFlowFolders(ignore)) {
64687
+ await generateFlowLockInternal(flowFolder, true, workspace, opts, false, true, tree);
64688
+ }
64689
+ }
64690
+ if (!skipApps) {
64691
+ for (const { folder: appFolder, rawApp } of await walkLocalAppItems(ignore)) {
64692
+ await generateAppLocksInternal(appFolder, rawApp, true, workspace, opts, false, true, tree);
64693
+ }
64694
+ }
64695
+ tree.propagateStaleness();
64696
+ try {
64697
+ await uploadScripts(tree, workspace);
64698
+ } catch (e) {
64699
+ 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.`));
64700
+ }
64701
+ const staleItems = [];
64702
+ const seenFolders = new Set;
64703
+ for (const p of tree.allPaths()) {
64704
+ const staleReason = tree.getStaleReason(p);
64705
+ if (!staleReason)
64706
+ continue;
64707
+ const itemType = tree.getItemType(p);
64708
+ const itemFolder = tree.getFolder(p);
64709
+ if (itemType === "dependencies") {
64710
+ staleItems.push({ type: itemType, path: p, folder: itemFolder, staleReason });
64711
+ } else if (itemType === "inline_script") {
64712
+ continue;
64713
+ } else if (itemType === "script") {
64714
+ const originalPath = tree.getOriginalPath(p);
64715
+ staleItems.push({ type: itemType, path: originalPath, folder: itemFolder, staleReason });
64716
+ } else if (!seenFolders.has(itemFolder)) {
64717
+ seenFolders.add(itemFolder);
64718
+ const originalPath = tree.getOriginalPath(p);
64719
+ staleItems.push({ type: itemType, path: originalPath, folder: itemFolder, isRawApp: tree.getIsRawApp(p), staleReason });
64720
+ }
64721
+ }
64722
+ let filteredItems = staleItems;
64723
+ if (folder) {
64724
+ folder = folder.replaceAll("\\", "/");
64725
+ if (folder.endsWith("/")) {
64726
+ folder = folder.substring(0, folder.length - 1);
64727
+ }
64728
+ const folderNoExt = folder.replace(/\.[^/.]+$/, "");
64729
+ const isInsideFolder = (item) => {
64730
+ const normalizedFolder = item.folder.replaceAll("\\", "/");
64731
+ const normalizedPath = item.path.replaceAll("\\", "/");
64732
+ return normalizedFolder === folder || normalizedFolder.startsWith(folder + "/") || normalizedPath === folder || normalizedPath === folderNoExt;
64733
+ };
64734
+ const isPathInFolder = (p) => p.startsWith(folder + "/") || p === folder || p === folderNoExt;
64735
+ const touchesFolder = (treePath) => {
64736
+ if (isPathInFolder(treePath))
64737
+ return true;
64738
+ let found = false;
64739
+ tree.traverseTransitive(treePath, (importPath) => {
64740
+ if (isPathInFolder(importPath)) {
64741
+ found = true;
64742
+ return true;
64743
+ }
64744
+ });
64745
+ return found;
64746
+ };
64747
+ const isRelevant = (item) => {
64748
+ if (isInsideFolder(item))
64749
+ return true;
64750
+ if (item.type === "dependencies")
64751
+ return true;
64752
+ const treePath = (item.type === "script" ? item.path.replace(/\.[^/.]+$/, "") : item.folder).replaceAll("\\", "/");
64753
+ return touchesFolder(treePath);
64754
+ };
64755
+ if (opts.strictFolderBoundaries) {
64756
+ filteredItems = staleItems.filter(isInsideFolder);
64757
+ const excludedStale = staleItems.filter((item) => !isInsideFolder(item) && isRelevant(item) && item.type !== "dependencies");
64758
+ for (const item of excludedStale) {
64759
+ const normalizedPath = item.path.replaceAll("\\", "/");
64760
+ 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.`));
64761
+ }
64762
+ } else {
64763
+ filteredItems = staleItems.filter(isRelevant);
64764
+ }
64765
+ }
64766
+ if (filteredItems.length === 0) {
64767
+ info(colors.green("All metadata up-to-date"));
64768
+ return;
64769
+ }
64770
+ const scripts = filteredItems.filter((i) => i.type === "script");
64771
+ const flows = filteredItems.filter((i) => i.type === "flow");
64772
+ const apps2 = filteredItems.filter((i) => i.type === "app");
64773
+ const deps = filteredItems.filter((i) => i.type === "dependencies");
64774
+ info("");
64775
+ info(`Found ${colors.bold(String(filteredItems.length))} item(s) with stale metadata:`);
64776
+ const printItems = (label, items) => {
64777
+ if (items.length === 0)
64778
+ return;
64779
+ info(` ${label} (${items.length}):`);
64780
+ for (const item of items) {
64781
+ const reason = item.staleReason ? colors.dim(colors.white(` — ${item.staleReason}`)) : "";
64782
+ info(` ~ ${item.path}` + reason);
64783
+ }
64784
+ };
64785
+ printItems("Workspace dependencies", deps);
64786
+ printItems("Scripts", scripts);
64787
+ printItems("Flows", flows);
64788
+ printItems("Apps", apps2);
64789
+ if (opts.dryRun) {
64790
+ return;
64791
+ }
64792
+ info("");
64793
+ const isInteractive = process.stdin.isTTY ?? false;
64794
+ if (!opts.yes && isInteractive && !await Confirm.prompt({
64795
+ message: "Update metadata?",
64796
+ default: true
64797
+ })) {
64798
+ return;
64799
+ }
64800
+ info("");
64801
+ const mismatchedWorkspaceDeps = tree.getMismatchedWorkspaceDeps();
64802
+ const total = filteredItems.length - deps.length;
64803
+ const maxWidth = `[${total}/${total}]`.length;
64804
+ let current = 0;
64805
+ const formatProgress = (n) => {
64806
+ return colors.dim(colors.white(`[${n}/${total}]`.padEnd(maxWidth, " ")));
64807
+ };
64808
+ const errors = [];
64809
+ for (const item of scripts) {
64810
+ current++;
64811
+ info(`${formatProgress(current)} script ${item.path}`);
64812
+ try {
64813
+ await generateScriptMetadataInternal(item.path, workspace, opts, false, true, mismatchedWorkspaceDeps, codebases, false, tree);
64814
+ } catch (e) {
64815
+ const msg = e instanceof Error ? e.message : String(e);
64816
+ errors.push({ path: item.path, error: msg });
64817
+ error(` Failed: ${msg}`);
64818
+ }
64819
+ }
64820
+ for (const item of flows) {
64821
+ current++;
64822
+ try {
64823
+ const result = await generateFlowLockInternal(item.folder.replaceAll("/", SEP8), false, workspace, opts, false, true, tree);
64824
+ const flowResult = result;
64825
+ const scriptsInfo = flowResult?.updatedScripts?.length ? colors.dim(colors.white(`: ${flowResult.updatedScripts.join(", ")}`)) : "";
64826
+ info(`${formatProgress(current)} flow ${item.path}${scriptsInfo}`);
64827
+ } catch (e) {
64828
+ const msg = e instanceof Error ? e.message : String(e);
64829
+ errors.push({ path: item.path, error: msg });
64830
+ info(`${formatProgress(current)} flow ${item.path}`);
64831
+ error(` Failed: ${msg}`);
64832
+ }
64833
+ }
64834
+ for (const item of apps2) {
64835
+ current++;
64836
+ try {
64837
+ const result = await generateAppLocksInternal(item.folder.replaceAll("/", SEP8), item.isRawApp, false, workspace, opts, false, true, tree);
64838
+ const appResult = result;
64839
+ const scriptsInfo = appResult?.updatedScripts?.length ? colors.dim(colors.white(`: ${appResult.updatedScripts.join(", ")}`)) : "";
64840
+ info(`${formatProgress(current)} app ${item.path}${scriptsInfo}`);
64841
+ } catch (e) {
64842
+ const msg = e instanceof Error ? e.message : String(e);
64843
+ errors.push({ path: item.path, error: msg });
64844
+ info(`${formatProgress(current)} app ${item.path}`);
64845
+ error(` Failed: ${msg}`);
64846
+ }
64847
+ }
64848
+ const allStaleDeps = staleItems.filter((i) => i.type === "dependencies");
64849
+ await tree.persistDepsHashes(allStaleDeps.map((d) => d.path));
64850
+ const succeeded = total - errors.length;
64851
+ info("");
64852
+ if (errors.length > 0) {
64853
+ info(`Done. Updated ${colors.bold(String(succeeded))}/${total} item(s). ${colors.red(String(errors.length) + " failed")}:`);
64854
+ for (const { path: path11, error: error2 } of errors) {
64855
+ error(` ${path11}: ${error2}`);
64856
+ }
64857
+ process.exitCode = 1;
64858
+ } else {
64859
+ info(`Done. Updated ${colors.bold(String(total))} item(s).`);
64860
+ }
64861
+ }
64862
+ async function rehashCommand(opts, folder) {
64863
+ if (folder === "")
64864
+ folder = undefined;
64865
+ opts = await mergeConfigWithConfigFile(opts);
64866
+ await rehashOnly(opts, folder, {
64867
+ skipScripts: opts.skipScripts,
64868
+ skipFlows: opts.skipFlows,
64869
+ skipApps: opts.skipApps
64870
+ });
64871
+ }
64872
+ var command7, generate_metadata_default;
64873
+ var init_generate_metadata = __esm(async () => {
64874
+ init_mod3();
64875
+ init_colors2();
64876
+ init_log();
64877
+ await __promiseAll([
64878
+ init_confirm(),
64879
+ init_conf(),
64880
+ init_context(),
64881
+ init_auth(),
64882
+ init_metadata(),
64883
+ init_flow_metadata(),
64884
+ init_app_metadata(),
64885
+ init_sync(),
64886
+ init_script(),
64887
+ init_resource_folders(),
64888
+ init_codebase(),
64889
+ init_dependency_tree()
64890
+ ]);
64891
+ command7 = 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).command("rehash", new Command().description("Trust on-disk content; rewrite wmill-lock.yaml hashes without backend " + "trips or yaml/lock rewrites. Useful for bootstrapping missing lockfile " + "entries or recovering from older-CLI hash drift.").arguments("[folder:string]").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(rehashCommand));
64892
+ generate_metadata_default = command7;
64893
+ });
64894
+
64230
64895
  // src/commands/sync/sync.ts
64231
64896
  var exports_sync = {};
64232
64897
  __export(exports_sync, {
@@ -64248,7 +64913,7 @@ __export(exports_sync, {
64248
64913
  });
64249
64914
  import { writeFile as writeFile7, readdir as readdir4, stat as stat7, rm, copyFile, mkdir as mkdir5 } from "node:fs/promises";
64250
64915
  import * as path11 from "node:path";
64251
- import { sep as SEP8 } from "node:path";
64916
+ import { sep as SEP9 } from "node:path";
64252
64917
  function resolveWsNameFromBranch(opts, branchName) {
64253
64918
  const match2 = findWorkspaceByGitBranch(opts.workspaces, branchName);
64254
64919
  return match2 ? match2[0] : branchName;
@@ -64578,11 +65243,11 @@ function extractInlineScriptsForApps(key, rec, pathAssigner, toId, removeSchema)
64578
65243
  const o = v;
64579
65244
  const name = toId(key ?? "", rec);
64580
65245
  const [basePathO, ext2] = pathAssigner.assignPath(name, o["language"]);
64581
- const basePath = basePathO.replaceAll(SEP8, "/");
65246
+ const basePath = basePathO.replaceAll(SEP9, "/");
64582
65247
  const r = [];
64583
65248
  if (o["content"]) {
64584
65249
  const content = o["content"];
64585
- o["content"] = "!inline " + basePath.replaceAll(SEP8, "/") + ext2;
65250
+ o["content"] = "!inline " + basePath.replaceAll(SEP9, "/") + ext2;
64586
65251
  r.push({
64587
65252
  path: basePath + ext2,
64588
65253
  content
@@ -64590,7 +65255,7 @@ function extractInlineScriptsForApps(key, rec, pathAssigner, toId, removeSchema)
64590
65255
  }
64591
65256
  if (o["lock"] && o["lock"] != "") {
64592
65257
  const lock = o["lock"];
64593
- o["lock"] = "!inline " + basePath.replaceAll(SEP8, "/") + "lock";
65258
+ o["lock"] = "!inline " + basePath.replaceAll(SEP9, "/") + "lock";
64594
65259
  r.push({
64595
65260
  path: basePath + "lock",
64596
65261
  content: lock
@@ -64624,7 +65289,7 @@ function parseFileResourceTypeMap(raw) {
64624
65289
  return { formatExtMap, filesetMap };
64625
65290
  }
64626
65291
  async function findFilesetResourceFile(changePath) {
64627
- const filesetIdx = changePath.indexOf(".fileset" + SEP8);
65292
+ const filesetIdx = changePath.indexOf(".fileset" + SEP9);
64628
65293
  if (filesetIdx === -1) {
64629
65294
  throw new Error(`Not a fileset resource path: ${changePath}`);
64630
65295
  }
@@ -64724,12 +65389,12 @@ function ZipFSElement(zip, useYaml, defaultTs, resourceTypeToFormatExtension, re
64724
65389
  try {
64725
65390
  const assigner = newPathAssigner(defaultTs, { skipInlineScriptSuffix: getNonDottedPaths() });
64726
65391
  const inlineMapping = extractCurrentMapping(flow.value.modules, {}, flow.value.failure_module, flow.value.preprocessor_module);
64727
- inlineScripts = extractInlineScripts(flow.value.modules, inlineMapping, SEP8, defaultTs, assigner, { skipInlineScriptSuffix: getNonDottedPaths() });
65392
+ inlineScripts = extractInlineScripts(flow.value.modules, inlineMapping, SEP9, defaultTs, assigner, { skipInlineScriptSuffix: getNonDottedPaths() });
64728
65393
  if (flow.value.failure_module) {
64729
- inlineScripts.push(...extractInlineScripts([flow.value.failure_module], inlineMapping, SEP8, defaultTs, assigner, { skipInlineScriptSuffix: getNonDottedPaths() }));
65394
+ inlineScripts.push(...extractInlineScripts([flow.value.failure_module], inlineMapping, SEP9, defaultTs, assigner, { skipInlineScriptSuffix: getNonDottedPaths() }));
64730
65395
  }
64731
65396
  if (flow.value.preprocessor_module) {
64732
- inlineScripts.push(...extractInlineScripts([flow.value.preprocessor_module], inlineMapping, SEP8, defaultTs, assigner, { skipInlineScriptSuffix: getNonDottedPaths() }));
65397
+ inlineScripts.push(...extractInlineScripts([flow.value.preprocessor_module], inlineMapping, SEP9, defaultTs, assigner, { skipInlineScriptSuffix: getNonDottedPaths() }));
64733
65398
  }
64734
65399
  } catch (error2) {
64735
65400
  error(`Failed to extract inline scripts for flow at path: ${p}`);
@@ -64939,10 +65604,10 @@ function ZipFSElement(zip, useYaml, defaultTs, resourceTypeToFormatExtension, re
64939
65604
  const hasModules = parsed["modules"] && Object.keys(parsed["modules"]).length > 0;
64940
65605
  if (parsed["lock"] && parsed["lock"] != "" && parsed["codebase"] == undefined) {
64941
65606
  if (hasModules) {
64942
- const scriptBase = removeSuffix(removeSuffix(p.replaceAll(SEP8, "/"), ".json"), ".script");
65607
+ const scriptBase = removeSuffix(removeSuffix(p.replaceAll(SEP9, "/"), ".json"), ".script");
64943
65608
  parsed["lock"] = "!inline " + scriptBase + getModuleFolderSuffix() + "/script.lock";
64944
65609
  } else {
64945
- parsed["lock"] = "!inline " + removeSuffix(p.replaceAll(SEP8, "/"), ".json") + ".lock";
65610
+ parsed["lock"] = "!inline " + removeSuffix(p.replaceAll(SEP9, "/"), ".json") + ".lock";
64946
65611
  }
64947
65612
  } else if (parsed["lock"] == "") {
64948
65613
  parsed["lock"] = "";
@@ -64972,9 +65637,9 @@ function ZipFSElement(zip, useYaml, defaultTs, resourceTypeToFormatExtension, re
64972
65637
  const formatExtension = resourceTypeToFormatExtension[resourceType];
64973
65638
  const isFileset = resourceTypeToIsFileset[resourceType] ?? false;
64974
65639
  if (isFileset) {
64975
- parsed["value"] = "!inline_fileset " + removeSuffix(p.replaceAll(SEP8, "/"), ".resource.json") + ".fileset";
65640
+ parsed["value"] = "!inline_fileset " + removeSuffix(p.replaceAll(SEP9, "/"), ".resource.json") + ".fileset";
64976
65641
  } else if (formatExtension) {
64977
- parsed["value"]["content"] = "!inline " + removeSuffix(p.replaceAll(SEP8, "/"), ".resource.json") + ".resource.file." + formatExtension;
65642
+ parsed["value"]["content"] = "!inline " + removeSuffix(p.replaceAll(SEP9, "/"), ".resource.json") + ".resource.file." + formatExtension;
64978
65643
  }
64979
65644
  return useYaml ? import_yaml11.stringify(parsed, yamlOptions) : JSON.stringify(parsed, null, 2);
64980
65645
  }
@@ -65141,7 +65806,7 @@ function ZipFSElement(zip, useYaml, defaultTs, resourceTypeToFormatExtension, re
65141
65806
  }
65142
65807
  };
65143
65808
  }
65144
- return _internal_folder("." + SEP8, zip);
65809
+ return _internal_folder("." + SEP9, zip);
65145
65810
  }
65146
65811
  async function* readDirRecursiveWithIgnore2(ignore, root) {
65147
65812
  const stack = [
@@ -65160,7 +65825,7 @@ async function* readDirRecursiveWithIgnore2(ignore, root) {
65160
65825
  yield e;
65161
65826
  for await (const e2 of e.c()) {
65162
65827
  if (e2.isDirectory) {
65163
- const dirName = e2.path.split(SEP8).pop();
65828
+ const dirName = e2.path.split(SEP9).pop();
65164
65829
  if (dirName == "node_modules" || dirName == ".claude" || dirName?.startsWith(".")) {
65165
65830
  continue;
65166
65831
  }
@@ -65183,7 +65848,7 @@ async function elementsToMap(els, ignore, json, skips, specificItems, branchOver
65183
65848
  for await (const entry of readDirRecursiveWithIgnore2(ignore, els)) {
65184
65849
  if (entry.isDirectory) {
65185
65850
  if (!isRemote) {
65186
- const dirName = entry.path.split(SEP8).pop() ?? "";
65851
+ const dirName = entry.path.split(SEP9).pop() ?? "";
65187
65852
  if (hasWrongFormatSuffix(dirName)) {
65188
65853
  wrongFormatPaths.push(entry.path);
65189
65854
  }
@@ -65228,7 +65893,7 @@ async function elementsToMap(els, ignore, json, skips, specificItems, branchOver
65228
65893
  }
65229
65894
  }
65230
65895
  if (isRawAppFile(path12)) {
65231
- const suffix = path12.split(getFolderSuffix("raw_app") + SEP8).pop();
65896
+ const suffix = path12.split(getFolderSuffix("raw_app") + SEP9).pop();
65232
65897
  if (suffix?.startsWith("dist/") || suffix == "wmill.d.ts" || suffix == "package-lock.json" || suffix == "DATATABLES.md") {
65233
65898
  continue;
65234
65899
  }
@@ -65499,7 +66164,8 @@ async function compareDynFSElement(els1, els2, ignore, json, skips, ignoreMetada
65499
66164
  }
65500
66165
  return a.path.localeCompare(b.path);
65501
66166
  });
65502
- return changes;
66167
+ const localMap = isEls1Remote ? m2 : m1;
66168
+ return { changes, localMap };
65503
66169
  }
65504
66170
  function getOrderFromPath(p) {
65505
66171
  const typ = getTypeStrFromPath(p);
@@ -65728,7 +66394,7 @@ async function pull(opts) {
65728
66394
  const zipFile = 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);
65729
66395
  const remote = ZipFSElement(zipFile, !opts.json, opts.defaultTs ?? "bun", resourceTypeToFormatExtension, resourceTypeToIsFileset, true, parseSyncBehavior(opts.syncBehavior) >= 1);
65730
66396
  const local = !opts.stateful ? await FSFSElement(process.cwd(), codebases, true) : await FSFSElement(path11.join(process.cwd(), ".wmill"), [], true);
65731
- const changes = await compareDynFSElement(remote, local, await ignoreF(opts), opts.json ?? false, opts, false, codebases, true, specificItems, wsNameForFiles, true);
66397
+ const { changes, localMap } = await compareDynFSElement(remote, local, await ignoreF(opts), opts.json ?? false, opts, false, codebases, true, specificItems, wsNameForFiles, true);
65732
66398
  info(`remote (${workspace.name}) -> local: ${changes.length} changes to apply`);
65733
66399
  if (opts.dryRun && opts.jsonOutput) {
65734
66400
  const result = {
@@ -65892,6 +66558,31 @@ Done! All ${changes.length} changes applied locally and wmill-lock.yaml updated.
65892
66558
  } else if (opts.jsonOutput) {
65893
66559
  console.log(JSON.stringify({ success: true, message: "No changes to apply", total: 0 }, null, 2));
65894
66560
  }
66561
+ if (!opts.jsonOutput && !opts.dryRun) {
66562
+ try {
66563
+ const { rehashOnly: rehashOnly2 } = await init_generate_metadata().then(() => exports_generate_metadata);
66564
+ const postPullPaths = new Set(Object.keys(localMap));
66565
+ for (const change of changes) {
66566
+ if (change.name === "added")
66567
+ postPullPaths.add(change.path);
66568
+ else if (change.name === "deleted")
66569
+ postPullPaths.delete(change.path);
66570
+ }
66571
+ const filled = await rehashOnly2(opts, undefined, {
66572
+ missingOnly: true,
66573
+ localFiles: postPullPaths
66574
+ });
66575
+ const total = filled.scripts + filled.flows + filled.apps;
66576
+ if (total > 0) {
66577
+ info(colors.gray(`Auto-filled ${total} missing lockfile entr${total === 1 ? "y" : "ies"} (${filled.scripts} script, ${filled.flows} flow, ${filled.apps} app) from disk.`));
66578
+ }
66579
+ } catch (e) {
66580
+ if (e instanceof UnknownLockVersionError || e instanceof MalformedLockfileError) {
66581
+ throw e;
66582
+ }
66583
+ warn(colors.yellow(`Could not auto-fill missing lockfile entries: ${e instanceof Error ? e.message : e}`));
66584
+ }
66585
+ }
65895
66586
  try {
65896
66587
  await pullSharedUi(workspace.workspaceId);
65897
66588
  } catch (e) {
@@ -66020,7 +66711,7 @@ Push aborted: ${lockIssues.length} script(s) missing locks.`));
66020
66711
  } catch {}
66021
66712
  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, parseSyncBehavior(opts.syncBehavior) >= 1);
66022
66713
  const local = await FSFSElement(path11.join(process.cwd(), ""), codebases, false);
66023
- const changes = await compareDynFSElement(local, remote, await ignoreF(opts), opts.json ?? false, opts, true, codebases, false, specificItems, wsNameForFiles, false);
66714
+ const { changes } = await compareDynFSElement(local, remote, await ignoreF(opts), opts.json ?? false, opts, true, codebases, false, specificItems, wsNameForFiles, false);
66024
66715
  const rawWorkspaceDependencies = await getRawWorkspaceDependencies(true);
66025
66716
  const tracker = await buildTracker(changes);
66026
66717
  const autoRegenerate = !!opts.autoMetadata;
@@ -66127,7 +66818,7 @@ Push aborted: ${lockIssues.length} script(s) missing locks.`));
66127
66818
  if (changes.length > 0) {
66128
66819
  const folderNames = new Set;
66129
66820
  for (const change of changes) {
66130
- const parts = change.path.split(SEP8);
66821
+ const parts = change.path.split(SEP9);
66131
66822
  if (parts.length >= 3 && parts[0] === "f" && change.name !== "deleted") {
66132
66823
  folderNames.add(parts[1]);
66133
66824
  }
@@ -66160,7 +66851,7 @@ Push aborted: ${lockIssues.length} script(s) missing locks.`));
66160
66851
  const userIsAdmin = user.is_admin;
66161
66852
  const msg = `${userIsAdmin ? "Warning: " : ""}Missing folder.meta.yaml for:
66162
66853
  ${folderList}
66163
- ` + `Run 'wmill folder add-missing' to create them locally, then push again.`;
66854
+ Run 'wmill folder add-missing' to create them locally, then push again.`;
66164
66855
  if (!userIsAdmin) {
66165
66856
  if (opts.jsonOutput) {
66166
66857
  console.log(JSON.stringify({ success: false, error: "missing_folders", missing_folders: missingFolders, message: msg }, null, 2));
@@ -66242,7 +66933,7 @@ ${folderList}
66242
66933
  };
66243
66934
  await preCheckPermissionedAs(changes, user.email, userIsAdminOrDeployer, opts.acceptOverridingPermissionedAsWithSelf ?? false, !!process.stdin.isTTY);
66244
66935
  } else if (folderDefaultAnnotations && folderDefaultAnnotations.size > 0) {
66245
- warn(colors.yellow(`This workspace has folder default_permissioned_as rules that affect ${folderDefaultAnnotations.size} item(s) being pushed, ` + `but syncBehavior is not set in wmill.yaml. Add 'syncBehavior: v1' to enable ownership preservation on update and on_behalf_of stripping on pull.`));
66936
+ warn(colors.yellow(`This workspace has folder default_permissioned_as rules that affect ${folderDefaultAnnotations.size} item(s) being pushed, but syncBehavior is not set in wmill.yaml. Add 'syncBehavior: v1' to enable ownership preservation on update and on_behalf_of stripping on pull.`));
66246
66937
  }
66247
66938
  if (!opts.yes && !await Confirm.prompt({
66248
66939
  message: `Do you want to apply these ${changes.length} changes to the remote?`,
@@ -66273,7 +66964,7 @@ ${folderList}
66273
66964
  parallelizationFactor = 1;
66274
66965
  }
66275
66966
  const allGrouped = Array.from(groupedChanges.entries());
66276
- const isFolderMetaGroup = ([basePath]) => basePath.endsWith(`${SEP8}folder`) || basePath === "folder";
66967
+ const isFolderMetaGroup = ([basePath]) => basePath.endsWith(`${SEP9}folder`) || basePath === "folder";
66277
66968
  const folderMetaGroups = allGrouped.filter(isFolderMetaGroup);
66278
66969
  const groupedChangesArray = allGrouped.filter((g) => !isFolderMetaGroup(g));
66279
66970
  info(`found changes for ${allGrouped.length} items with a total of ${allGrouped.reduce((acc, [_, changes2]) => acc + changes2.length, 0)} files to process`);
@@ -66289,7 +66980,7 @@ ${folderList}
66289
66980
  while (pool.size < effectiveParallelism() && queue.length > 0) {
66290
66981
  const [groupBasePath, initialChanges] = queue.shift();
66291
66982
  let changes2 = initialChanges;
66292
- const isFolderGroup = groupBasePath.endsWith(`${SEP8}folder`) || groupBasePath === "folder";
66983
+ const isFolderGroup = groupBasePath.endsWith(`${SEP9}folder`) || groupBasePath === "folder";
66293
66984
  const promise = (async () => {
66294
66985
  const alreadySynced = [];
66295
66986
  const deletedVarsResPaths = [];
@@ -66421,7 +67112,7 @@ ${folderList}
66421
67112
  info(`Deleting ${typ} ${change.path}`);
66422
67113
  }
66423
67114
  const workspaceId = workspace.workspaceId;
66424
- const target = change.path.replaceAll(SEP8, "/");
67115
+ const target = change.path.replaceAll(SEP9, "/");
66425
67116
  switch (typ) {
66426
67117
  case "script": {
66427
67118
  await archiveScriptByPath({
@@ -66433,7 +67124,7 @@ ${folderList}
66433
67124
  case "folder":
66434
67125
  await deleteFolder({
66435
67126
  workspace: workspaceId,
66436
- name: change.path.split(SEP8)[1]
67127
+ name: change.path.split(SEP9)[1]
66437
67128
  });
66438
67129
  break;
66439
67130
  case "resource": {
@@ -66732,14 +67423,14 @@ Done! All ${changes.length} changes pushed to the remote workspace ${workspace.w
66732
67423
  }
66733
67424
  }
66734
67425
  var import_yaml11, branchDeprecationWarned = false, yamlOptions, isNotWmillFile = (p, isDirectory2) => {
66735
- if (p.endsWith(SEP8)) {
67426
+ if (p.endsWith(SEP9)) {
66736
67427
  return false;
66737
67428
  }
66738
- if (p.startsWith("ui" + SEP8)) {
67429
+ if (p.startsWith("ui" + SEP9)) {
66739
67430
  return true;
66740
67431
  }
66741
67432
  if (isDirectory2) {
66742
- return !p.startsWith("u" + SEP8) && !p.startsWith("f" + SEP8) && !p.startsWith("g" + SEP8) && !p.startsWith("users" + SEP8) && !p.startsWith("groups" + SEP8) && !p.startsWith("dependencies" + SEP8);
67433
+ return !p.startsWith("u" + SEP9) && !p.startsWith("f" + SEP9) && !p.startsWith("g" + SEP9) && !p.startsWith("users" + SEP9) && !p.startsWith("groups" + SEP9) && !p.startsWith("dependencies" + SEP9);
66743
67434
  }
66744
67435
  if (isScriptModulePath(p)) {
66745
67436
  return false;
@@ -66747,16 +67438,16 @@ var import_yaml11, branchDeprecationWarned = false, yamlOptions, isNotWmillFile
66747
67438
  try {
66748
67439
  const typ = getTypeStrFromPath(p);
66749
67440
  if (typ == "resource-type" || typ == "settings" || typ == "encryption_key") {
66750
- return p.includes(SEP8);
67441
+ return p.includes(SEP9);
66751
67442
  } else {
66752
- return !p.startsWith("u" + SEP8) && !p.startsWith("f" + SEP8) && !p.startsWith("g" + SEP8) && !p.startsWith("users" + SEP8) && !p.startsWith("groups" + SEP8) && !p.startsWith("dependencies" + SEP8);
67443
+ return !p.startsWith("u" + SEP9) && !p.startsWith("f" + SEP9) && !p.startsWith("g" + SEP9) && !p.startsWith("users" + SEP9) && !p.startsWith("groups" + SEP9) && !p.startsWith("dependencies" + SEP9);
66753
67444
  }
66754
67445
  } catch {
66755
67446
  return true;
66756
67447
  }
66757
67448
  }, isWhitelisted = (p) => {
66758
- return p == "." + SEP8 || p == "" || p == "u" || p == "f" || p == "g" || p == "ui" || p == "users" || p == "groups" || p == "dependencies";
66759
- }, command7, sync_default;
67449
+ return p == "." + SEP9 || p == "" || p == "u" || p == "f" || p == "g" || p == "ui" || p == "users" || p == "groups" || p == "dependencies";
67450
+ }, command8, sync_default;
66760
67451
  var init_sync = __esm(async () => {
66761
67452
  init_colors2();
66762
67453
  init_mod3();
@@ -66798,8 +67489,8 @@ var init_sync = __esm(async () => {
66798
67489
  aliasDuplicateObjects: false,
66799
67490
  singleQuote: true
66800
67491
  };
66801
- command7 = new Command().description("sync local with a remote workspaces or the opposite (push or pull)").action(() => info("2 actions available, pull and push. Use -h to display help.")).command("pull").description("Pull any remote changes and apply them locally.").option("--yes", "Pull without needing confirmation").option("--dry-run", "Show changes that would be pulled without actually pushing").option("--plain-secrets", "Pull secrets as plain text").option("--json", "Use JSON instead of YAML").option("--skip-variables", "Skip syncing variables (including secrets)").option("--skip-secrets", "Skip syncing only secrets variables").option("--include-secrets", "Include secrets in sync (overrides skipSecrets in wmill.yaml)").option("--skip-resources", "Skip syncing resources").option("--skip-resource-types", "Skip syncing resource types").option("--skip-scripts", "Skip syncing scripts").option("--skip-flows", "Skip syncing flows").option("--skip-apps", "Skip syncing apps").option("--skip-folders", "Skip syncing folders").option("--skip-workspace-dependencies", "Skip syncing workspace dependencies").option("--include-schedules", "Include syncing schedules").option("--include-triggers", "Include syncing triggers").option("--include-users", "Include syncing users").option("--include-groups", "Include syncing groups").option("--include-settings", "Include syncing workspace settings").option("--include-key", "Include workspace encryption key").option("--skip-branch-validation", "Skip git branch validation and prompts").option("--json-output", "Output results in JSON format").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). Overrides wmill.yaml includes").option("-e --excludes <patterns:file[]>", "Comma separated patterns to specify which file to NOT take into account. Overrides wmill.yaml excludes").option("--extra-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). Useful to still take wmill.yaml into account and act as a second pattern to satisfy").option("--repository <repo:string>", "Specify repository path (e.g., u/user/repo) when multiple repositories exist").option("--promotion <branch:string>", "Use promotionOverrides from the specified branch instead of regular overrides").option("--branch, --env <branch:string>", "[Deprecated: use --workspace] Override the current git branch/environment").action(pull).command("push").description("Push any local changes and apply them remotely.").option("--yes", "Push without needing confirmation").option("--dry-run", "Show changes that would be pushed without actually pushing").option("--plain-secrets", "Push secrets as plain text").option("--json", "Use JSON instead of YAML").option("--skip-variables", "Skip syncing variables (including secrets)").option("--skip-secrets", "Skip syncing only secrets variables").option("--include-secrets", "Include secrets in sync (overrides skipSecrets in wmill.yaml)").option("--skip-resources", "Skip syncing resources").option("--skip-resource-types", "Skip syncing resource types").option("--skip-scripts", "Skip syncing scripts").option("--skip-flows", "Skip syncing flows").option("--skip-apps", "Skip syncing apps").option("--skip-folders", "Skip syncing folders").option("--skip-workspace-dependencies", "Skip syncing workspace dependencies").option("--include-schedules", "Include syncing schedules").option("--include-triggers", "Include syncing triggers").option("--include-users", "Include syncing users").option("--include-groups", "Include syncing groups").option("--include-settings", "Include syncing workspace settings").option("--include-key", "Include workspace encryption key").option("--skip-branch-validation", "Skip git branch validation and prompts").option("--json-output", "Output results in JSON format").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.").option("--extra-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). Useful to still take wmill.yaml into account and act as a second pattern to satisfy").option("--message <message:string>", "Include a message that will be added to all scripts/flows/apps updated during this push").option("--parallel <number>", "Number of changes to process in parallel").option("--repository <repo:string>", "Specify repository path (e.g., u/user/repo) when multiple repositories exist").option("--branch, --env <branch:string>", "[Deprecated: use --workspace] Override the current git branch/environment").option("--lint", "Run lint validation before pushing").option("--locks-required", "Fail if scripts or flow inline scripts that need locks have no locks").option("--auto-metadata", "Automatically regenerate stale metadata (locks and schemas) before pushing").option("--accept-overriding-permissioned-as-with-self", "Accept that items with a different permissioned_as will be updated with your own user").action(push4);
66802
- sync_default = command7;
67492
+ command8 = new Command().description("sync local with a remote workspaces or the opposite (push or pull)").action(() => info("2 actions available, pull and push. Use -h to display help.")).command("pull").description("Pull any remote changes and apply them locally.").option("--yes", "Pull without needing confirmation").option("--dry-run", "Show changes that would be pulled without actually pushing").option("--plain-secrets", "Pull secrets as plain text").option("--json", "Use JSON instead of YAML").option("--skip-variables", "Skip syncing variables (including secrets)").option("--skip-secrets", "Skip syncing only secrets variables").option("--include-secrets", "Include secrets in sync (overrides skipSecrets in wmill.yaml)").option("--skip-resources", "Skip syncing resources").option("--skip-resource-types", "Skip syncing resource types").option("--skip-scripts", "Skip syncing scripts").option("--skip-flows", "Skip syncing flows").option("--skip-apps", "Skip syncing apps").option("--skip-folders", "Skip syncing folders").option("--skip-workspace-dependencies", "Skip syncing workspace dependencies").option("--include-schedules", "Include syncing schedules").option("--include-triggers", "Include syncing triggers").option("--include-users", "Include syncing users").option("--include-groups", "Include syncing groups").option("--include-settings", "Include syncing workspace settings").option("--include-key", "Include workspace encryption key").option("--skip-branch-validation", "Skip git branch validation and prompts").option("--json-output", "Output results in JSON format").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). Overrides wmill.yaml includes").option("-e --excludes <patterns:file[]>", "Comma separated patterns to specify which file to NOT take into account. Overrides wmill.yaml excludes").option("--extra-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). Useful to still take wmill.yaml into account and act as a second pattern to satisfy").option("--repository <repo:string>", "Specify repository path (e.g., u/user/repo) when multiple repositories exist").option("--promotion <branch:string>", "Use promotionOverrides from the specified branch instead of regular overrides").option("--branch, --env <branch:string>", "[Deprecated: use --workspace] Override the current git branch/environment").action(pull).command("push").description("Push any local changes and apply them remotely.").option("--yes", "Push without needing confirmation").option("--dry-run", "Show changes that would be pushed without actually pushing").option("--plain-secrets", "Push secrets as plain text").option("--json", "Use JSON instead of YAML").option("--skip-variables", "Skip syncing variables (including secrets)").option("--skip-secrets", "Skip syncing only secrets variables").option("--include-secrets", "Include secrets in sync (overrides skipSecrets in wmill.yaml)").option("--skip-resources", "Skip syncing resources").option("--skip-resource-types", "Skip syncing resource types").option("--skip-scripts", "Skip syncing scripts").option("--skip-flows", "Skip syncing flows").option("--skip-apps", "Skip syncing apps").option("--skip-folders", "Skip syncing folders").option("--skip-workspace-dependencies", "Skip syncing workspace dependencies").option("--include-schedules", "Include syncing schedules").option("--include-triggers", "Include syncing triggers").option("--include-users", "Include syncing users").option("--include-groups", "Include syncing groups").option("--include-settings", "Include syncing workspace settings").option("--include-key", "Include workspace encryption key").option("--skip-branch-validation", "Skip git branch validation and prompts").option("--json-output", "Output results in JSON format").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.").option("--extra-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). Useful to still take wmill.yaml into account and act as a second pattern to satisfy").option("--message <message:string>", "Include a message that will be added to all scripts/flows/apps updated during this push").option("--parallel <number>", "Number of changes to process in parallel").option("--repository <repo:string>", "Specify repository path (e.g., u/user/repo) when multiple repositories exist").option("--branch, --env <branch:string>", "[Deprecated: use --workspace] Override the current git branch/environment").option("--lint", "Run lint validation before pushing").option("--locks-required", "Fail if scripts or flow inline scripts that need locks have no locks").option("--auto-metadata", "Automatically regenerate stale metadata (locks and schemas) before pushing").option("--accept-overriding-permissioned-as-with-self", "Accept that items with a different permissioned_as will be updated with your own user").action(push4);
67493
+ sync_default = command8;
66803
67494
  });
66804
67495
 
66805
67496
  // windmill-utils-internal/src/parse/parse-schema.ts
@@ -67004,7 +67695,7 @@ var init_parse_schema = __esm(() => {
67004
67695
  });
67005
67696
 
67006
67697
  // src/utils/metadata.ts
67007
- import { sep as SEP9 } from "node:path";
67698
+ import { sep as SEP10 } from "node:path";
67008
67699
  import { writeFile as writeFile8, stat as stat8, rm as rm2, readdir as readdir5 } from "node:fs/promises";
67009
67700
  import { readFileSync as readFileSync2, existsSync as existsSync6, readdirSync, statSync, writeFileSync as writeFileSync5 } from "node:fs";
67010
67701
  import * as path12 from "node:path";
@@ -67098,7 +67789,7 @@ async function blueColor() {
67098
67789
  }
67099
67790
  async function generateScriptMetadataInternal(scriptPath, workspace, opts, dryRun, noStaleMessage, rawWorkspaceDependencies, codebases, justUpdateMetadataLock, tree) {
67100
67791
  const isFolderLayout = isModuleEntryPoint(scriptPath);
67101
- const remotePath = isFolderLayout ? getScriptBasePathFromModulePath(scriptPath).replaceAll(SEP9, "/") : scriptPath.substring(0, scriptPath.indexOf(".")).replaceAll(SEP9, "/");
67792
+ const remotePath = scriptPathToRemotePath(scriptPath);
67102
67793
  const language = inferContentTypeFromFilePath(scriptPath, opts.defaultTs);
67103
67794
  const metadataWithType = await parseMetadataFile(remotePath, undefined);
67104
67795
  const scriptContent = await readTextFile(scriptPath);
@@ -67113,6 +67804,22 @@ async function generateScriptMetadataInternal(scriptPath, workspace, opts, dryRu
67113
67804
  moduleHashes = await computeModuleHashes(moduleFolderPath, opts.defaultTs, tree ? {} : rawWorkspaceDependencies, isFolderLayout);
67114
67805
  }
67115
67806
  const hasModuleHashes = Object.keys(moduleHashes).length > 0;
67807
+ if (opts.rehashOnly) {
67808
+ if (hasModuleHashes) {
67809
+ const sortedEntries = Object.entries(moduleHashes).sort(([a], [b]) => a.localeCompare(b));
67810
+ const metaHash = await generateHash(hash2 + JSON.stringify(sortedEntries));
67811
+ await clearGlobalLock(remotePath);
67812
+ await updateMetadataGlobalLock(remotePath, metaHash, SCRIPT_TOP_HASH);
67813
+ for (const [modulePath, moduleHash] of Object.entries(moduleHashes)) {
67814
+ await updateMetadataGlobalLock(remotePath, moduleHash, modulePath);
67815
+ }
67816
+ } else {
67817
+ await clearGlobalLock(remotePath);
67818
+ await updateMetadataGlobalLock(remotePath, hash2);
67819
+ }
67820
+ return;
67821
+ }
67822
+ const conf = await readLockfile();
67116
67823
  let checkHash = hash2;
67117
67824
  let checkSubpath;
67118
67825
  if (hasModuleHashes) {
@@ -67120,7 +67827,6 @@ async function generateScriptMetadataInternal(scriptPath, workspace, opts, dryRu
67120
67827
  checkHash = await generateHash(hash2 + JSON.stringify(sortedEntries));
67121
67828
  checkSubpath = SCRIPT_TOP_HASH;
67122
67829
  }
67123
- const conf = await readLockfile();
67124
67830
  const isDirectlyStale = !await checkifMetadataUptodate(remotePath, checkHash, conf, checkSubpath);
67125
67831
  if (tree) {
67126
67832
  if (dryRun) {
@@ -67183,9 +67889,9 @@ async function generateScriptMetadataInternal(scriptPath, workspace, opts, dryRu
67183
67889
  }
67184
67890
  } else {
67185
67891
  if (isFolderLayout) {
67186
- metadataParsedContent.lock = "!inline " + remotePath.replaceAll(SEP9, "/") + getModuleFolderSuffix() + "/script.lock";
67892
+ metadataParsedContent.lock = "!inline " + remotePath.replaceAll(SEP10, "/") + getModuleFolderSuffix() + "/script.lock";
67187
67893
  } else {
67188
- metadataParsedContent.lock = "!inline " + remotePath.replaceAll(SEP9, "/") + ".script.lock";
67894
+ metadataParsedContent.lock = "!inline " + remotePath.replaceAll(SEP10, "/") + ".script.lock";
67189
67895
  }
67190
67896
  }
67191
67897
  let metaPath;
@@ -67372,7 +68078,7 @@ async function updateScriptLock(workspace, scriptContent, language, remotePath,
67372
68078
  const lockPath = lockPathOverride ?? remotePath + ".script.lock";
67373
68079
  if (lock != "") {
67374
68080
  await writeFile8(lockPath, lock, "utf-8");
67375
- metadataContent.lock = "!inline " + lockPath.replaceAll(SEP9, "/");
68081
+ metadataContent.lock = "!inline " + lockPath.replaceAll(SEP10, "/");
67376
68082
  } else {
67377
68083
  try {
67378
68084
  if (await stat8(lockPath)) {
@@ -67686,22 +68392,29 @@ async function parseMetadataFile(scriptPath, generateMetadataIfMissing) {
67686
68392
  };
67687
68393
  }
67688
68394
  function normalizeLockPath(p) {
67689
- return p.replace(/\\/g, "/");
68395
+ let n = p.replace(/\\/g, "/");
68396
+ if (n.startsWith("./"))
68397
+ n = n.slice(2);
68398
+ return n;
67690
68399
  }
67691
68400
  async function readLockfile() {
68401
+ let parsed;
67692
68402
  try {
67693
- const read2 = await yamlParseFile(WMILL_LOCKFILE);
67694
- if (typeof read2 == "object" && read2 != null) {
67695
- return read2;
67696
- } else {
67697
- throw new Error("Invalid lockfile");
67698
- }
68403
+ parsed = await yamlParseFile(WMILL_LOCKFILE);
67699
68404
  } catch {
67700
- const lock = { locks: {}, version: "v2" };
68405
+ const lock = { locks: {}, version: CURRENT_LOCK_VERSION };
67701
68406
  await writeFile8(WMILL_LOCKFILE, import_yaml13.stringify(lock, yamlOptions), "utf-8");
67702
68407
  info(colors.green("wmill-lock.yaml created"));
67703
68408
  return lock;
67704
68409
  }
68410
+ if (typeof parsed != "object" || parsed == null) {
68411
+ throw new MalformedLockfileError("wmill-lock.yaml is malformed (expected an object). Refusing to operate to avoid corrupting the lockfile.");
68412
+ }
68413
+ const conf = parsed;
68414
+ if (conf.version != null && !KNOWN_LOCK_VERSIONS.includes(conf.version)) {
68415
+ throw new UnknownLockVersionError(`wmill-lock.yaml is at unknown version "${conf.version}". This was written by a newer wmill CLI; please upgrade with \`wmill upgrade\`. Refusing to operate to avoid corrupting the lockfile.`);
68416
+ }
68417
+ return conf;
67705
68418
  }
67706
68419
  function v2LockPath(path13, subpath) {
67707
68420
  const normalizedPath = normalizeLockPath(path13);
@@ -67718,15 +68431,18 @@ async function checkifMetadataUptodate(path13, hash2, conf, subpath) {
67718
68431
  if (!conf.locks) {
67719
68432
  return false;
67720
68433
  }
67721
- const isV2 = conf?.version == "v2";
67722
- if (isV2) {
67723
- const current = conf.locks?.[v2LockPath(path13, subpath)];
67724
- return current == hash2;
67725
- } else {
67726
- const obj = conf.locks?.[path13];
67727
- const current = subpath && typeof obj == "object" ? obj?.[subpath] : obj;
67728
- return current == hash2;
68434
+ const isFlatKeyed = conf?.version === "v2";
68435
+ if (isFlatKeyed) {
68436
+ const key = v2LockPath(path13, subpath);
68437
+ return conf.locks?.[key] === hash2 || conf.locks?.["./" + key] === hash2;
68438
+ }
68439
+ for (const p of [path13, "./" + path13]) {
68440
+ const obj = conf.locks?.[p];
68441
+ const v = subpath && typeof obj == "object" ? obj?.[subpath] : obj;
68442
+ if (v === hash2)
68443
+ return true;
67729
68444
  }
68445
+ return false;
67730
68446
  }
67731
68447
  async function generateScriptHash(rawWorkspaceDependencies, scriptContent, newMetadataContent) {
67732
68448
  return await generateHash(JSON.stringify(rawWorkspaceDependencies) + scriptContent + newMetadataContent);
@@ -67761,15 +68477,16 @@ async function clearGlobalLock(path13) {
67761
68477
  if (!conf?.locks) {
67762
68478
  conf.locks = {};
67763
68479
  }
67764
- const isV2 = conf?.version == "v2";
67765
- if (isV2) {
68480
+ const isFlatKeyed = conf?.version === "v2";
68481
+ if (isFlatKeyed) {
67766
68482
  const key = v2LockPath(path13);
68483
+ const legacyKey = "./" + key;
67767
68484
  if (conf.locks) {
67768
68485
  Object.keys(conf.locks).forEach((k) => {
67769
- if (conf.locks) {
67770
- if (k.startsWith(key)) {
67771
- delete conf.locks[k];
67772
- }
68486
+ if (!conf.locks)
68487
+ return;
68488
+ if (k === key || k.startsWith(key + "+") || k === legacyKey || k.startsWith(legacyKey + "+")) {
68489
+ delete conf.locks[k];
67773
68490
  }
67774
68491
  });
67775
68492
  }
@@ -67781,8 +68498,8 @@ async function updateMetadataGlobalLock(path13, hash2, subpath) {
67781
68498
  if (!conf?.locks) {
67782
68499
  conf.locks = {};
67783
68500
  }
67784
- const isV2 = conf?.version == "v2";
67785
- if (isV2) {
68501
+ const isFlatKeyed = conf?.version === "v2";
68502
+ if (isFlatKeyed) {
67786
68503
  conf.locks[v2LockPath(path13, subpath)] = hash2;
67787
68504
  } else {
67788
68505
  if (subpath) {
@@ -67798,7 +68515,7 @@ async function updateMetadataGlobalLock(path13, hash2, subpath) {
67798
68515
  }
67799
68516
  await writeFile8(WMILL_LOCKFILE, import_yaml13.stringify(conf, yamlOptions), "utf-8");
67800
68517
  }
67801
- var import_yaml13, _require, _parserCache, LockfileGenerationError, LANG_ANNOTATION_CONFIG, lockCache, WMILL_LOCKFILE = "wmill-lock.yaml", SCRIPT_TOP_HASH = "__script_hash";
68518
+ var import_yaml13, _require, _parserCache, LockfileGenerationError, UnknownLockVersionError, MalformedLockfileError, LANG_ANNOTATION_CONFIG, lockCache, WMILL_LOCKFILE = "wmill-lock.yaml", CURRENT_LOCK_VERSION = "v2", KNOWN_LOCK_VERSIONS, SCRIPT_TOP_HASH = "__script_hash";
67802
68519
  var init_metadata = __esm(async () => {
67803
68520
  init_colors2();
67804
68521
  init_log();
@@ -67824,6 +68541,18 @@ var init_metadata = __esm(async () => {
67824
68541
  this.name = "LockfileGenerationError";
67825
68542
  }
67826
68543
  };
68544
+ UnknownLockVersionError = class UnknownLockVersionError extends Error {
68545
+ constructor(message) {
68546
+ super(message);
68547
+ this.name = "UnknownLockVersionError";
68548
+ }
68549
+ };
68550
+ MalformedLockfileError = class MalformedLockfileError extends Error {
68551
+ constructor(message) {
68552
+ super(message);
68553
+ this.name = "MalformedLockfileError";
68554
+ }
68555
+ };
67827
68556
  LANG_ANNOTATION_CONFIG = {
67828
68557
  python3: { comment: "#", keyword: "requirements", validityRe: /^#\s?(\S+)\s*$/ },
67829
68558
  bun: { comment: "//", keyword: "package_json" },
@@ -67833,6 +68562,7 @@ var init_metadata = __esm(async () => {
67833
68562
  powershell: { comment: "#", keyword: "modules_json" }
67834
68563
  };
67835
68564
  lockCache = new Map;
68565
+ KNOWN_LOCK_VERSIONS = ["v1", "v2"];
67836
68566
  });
67837
68567
 
67838
68568
  // src/commands/app/raw_apps.ts
@@ -67843,7 +68573,7 @@ __export(exports_raw_apps, {
67843
68573
  loadRunnablesFromBackend: () => loadRunnablesFromBackend,
67844
68574
  generatingPolicy: () => generatingPolicy
67845
68575
  });
67846
- import { sep as SEP10 } from "node:path";
68576
+ import { sep as SEP11 } from "node:path";
67847
68577
  import path13 from "node:path";
67848
68578
  import { readdir as readdir6 } from "node:fs/promises";
67849
68579
  async function readSiblingLock(backendPath, runnableId, allFiles) {
@@ -67981,7 +68711,7 @@ async function collectAppFiles(localPath) {
67981
68711
  if (entry.name === APP_BACKEND_FOLDER || entry.name === "node_modules" || entry.name === "dist" || entry.name === ".claude" || entry.name === "sql_to_apply") {
67982
68712
  continue;
67983
68713
  }
67984
- await readDirRecursive2(fullPath + SEP10, relativePath + "/");
68714
+ await readDirRecursive2(fullPath + SEP11, relativePath + "/");
67985
68715
  } else if (entry.isFile()) {
67986
68716
  if (entry.name === "raw_app.yaml" || entry.name === "package-lock.json" || entry.name === "DATATABLES.md" || entry.name === "AGENTS.md" || entry.name === "wmill.d.ts") {
67987
68717
  continue;
@@ -67999,7 +68729,7 @@ async function pushRawApp(workspace, remotePath, localPath, message) {
67999
68729
  return;
68000
68730
  }
68001
68731
  alreadySynced.push(localPath);
68002
- remotePath = remotePath.replaceAll(SEP10, "/");
68732
+ remotePath = remotePath.replaceAll(SEP11, "/");
68003
68733
  let app = undefined;
68004
68734
  try {
68005
68735
  app = await getAppByPath({
@@ -68013,8 +68743,8 @@ async function pushRawApp(workspace, remotePath, localPath, message) {
68013
68743
  if (app) {
68014
68744
  app.policy = undefined;
68015
68745
  }
68016
- if (!localPath.endsWith(SEP10)) {
68017
- localPath += SEP10;
68746
+ if (!localPath.endsWith(SEP11)) {
68747
+ localPath += SEP11;
68018
68748
  }
68019
68749
  const appFilePath = localPath + "raw_app.yaml";
68020
68750
  const localApp = await yamlParseFile(appFilePath);
@@ -68030,7 +68760,7 @@ async function pushRawApp(workspace, remotePath, localPath, message) {
68030
68760
  } else {
68031
68761
  runnables = {};
68032
68762
  }
68033
- replaceInlineScripts2(runnables, backendPath + SEP10, true);
68763
+ replaceInlineScripts2(runnables, backendPath + SEP11, true);
68034
68764
  repopulateFields(runnables);
68035
68765
  const appForPolicy = { ...localApp, runnables };
68036
68766
  await generatingPolicy(appForPolicy, remotePath, localApp?.["public"] ?? false);
@@ -68142,7 +68872,20 @@ __export(exports_app_metadata, {
68142
68872
  });
68143
68873
  import path14 from "node:path";
68144
68874
  import { mkdir as mkdir6, readdir as readdir7 } from "node:fs/promises";
68145
- import { sep as SEP11 } from "node:path";
68875
+ import { sep as SEP12 } from "node:path";
68876
+ async function isAppDirectlyStale(appFolder, hashes, conf) {
68877
+ if (await checkifMetadataUptodate(appFolder, hashes[TOP_HASH2], conf, TOP_HASH2)) {
68878
+ return false;
68879
+ }
68880
+ const fileEntries = Object.entries(hashes).filter(([k]) => k !== TOP_HASH2);
68881
+ if (fileEntries.length === 0)
68882
+ return true;
68883
+ for (const [k, h] of fileEntries) {
68884
+ if (!await checkifMetadataUptodate(appFolder, h, conf, k))
68885
+ return true;
68886
+ }
68887
+ return false;
68888
+ }
68146
68889
  async function generateAppHash(rawReqs, folder, rawApp, defaultTs) {
68147
68890
  const runnablesFolder = rawApp ? path14.join(folder, APP_BACKEND_FOLDER) : folder;
68148
68891
  const hashes = {};
@@ -68156,7 +68899,7 @@ async function generateAppHash(rawReqs, folder, rawApp, defaultTs) {
68156
68899
  }
68157
68900
  }
68158
68901
  if (exts.some((e) => f.path.endsWith(e))) {
68159
- const relativePath = normalizeLockPath(f.path.replace(runnablesFolder + SEP11, ""));
68902
+ const relativePath = normalizeLockPath(f.path.replace(runnablesFolder + SEP12, ""));
68160
68903
  hashes[relativePath] = await generateHash(await f.getContentText() + JSON.stringify(rawReqs));
68161
68904
  }
68162
68905
  }
@@ -68172,23 +68915,31 @@ async function generateAppHash(rawReqs, folder, rawApp, defaultTs) {
68172
68915
  return { ...sortedHashes, [TOP_HASH2]: await generateHash(JSON.stringify(sortedHashes)) };
68173
68916
  }
68174
68917
  async function generateAppLocksInternal(appFolder, rawApp, dryRun, workspace, opts, justUpdateMetadataLock, noStaleMessage, tree) {
68175
- if (appFolder.endsWith(SEP11)) {
68918
+ if (appFolder.endsWith(SEP12)) {
68176
68919
  appFolder = appFolder.substring(0, appFolder.length - 1);
68177
68920
  }
68178
- const remote_path = appFolder.replaceAll(SEP11, "/");
68921
+ if (opts.rehashOnly) {
68922
+ const hashes = await generateAppHash({}, appFolder, rawApp, opts.defaultTs);
68923
+ await clearGlobalLock(appFolder);
68924
+ for (const [k, v] of Object.entries(hashes)) {
68925
+ await updateMetadataGlobalLock(appFolder, v, k);
68926
+ }
68927
+ return;
68928
+ }
68929
+ const remote_path = appFolder.replaceAll(SEP12, "/");
68179
68930
  if (!justUpdateMetadataLock && !noStaleMessage) {
68180
68931
  info(`Generating locks for app ${appFolder} at ${remote_path}`);
68181
68932
  }
68182
68933
  const appFilePath = path14.join(appFolder, rawApp ? "raw_app.yaml" : "app.yaml");
68183
68934
  const appFile = await yamlParseFile(appFilePath);
68184
68935
  const appValue = rawApp ? appFile.runnables : appFile.value;
68185
- const folderNormalized = appFolder.replaceAll(SEP11, "/");
68936
+ const folderNormalized = appFolder.replaceAll(SEP12, "/");
68186
68937
  let filteredDeps = {};
68187
68938
  const conf = await readLockfile();
68188
68939
  if (tree) {
68189
68940
  if (dryRun) {
68190
68941
  const hashes = await generateAppHash({}, appFolder, rawApp, opts.defaultTs);
68191
- const isDirectlyStale = !await checkifMetadataUptodate(appFolder, hashes[TOP_HASH2], conf, TOP_HASH2);
68942
+ const isDirectlyStale = await isAppDirectlyStale(appFolder, hashes, conf);
68192
68943
  let treeAppValue = structuredClone(appValue);
68193
68944
  if (rawApp) {
68194
68945
  const runnablesPath = path14.join(appFolder, APP_BACKEND_FOLDER);
@@ -68204,7 +68955,7 @@ async function generateAppLocksInternal(appFolder, rawApp, dryRun, workspace, op
68204
68955
  }
68205
68956
  let content = inlineScript.content;
68206
68957
  if (typeof content === "string" && content.startsWith("!inline ")) {
68207
- const filePath = appFolder + SEP11 + content.replace("!inline ", "");
68958
+ const filePath = appFolder + SEP12 + content.replace("!inline ", "");
68208
68959
  try {
68209
68960
  content = await readTextFile(filePath);
68210
68961
  } catch {
@@ -68226,7 +68977,7 @@ async function generateAppLocksInternal(appFolder, rawApp, dryRun, workspace, op
68226
68977
  const rawWorkspaceDependencies = await getRawWorkspaceDependencies(true);
68227
68978
  filteredDeps = await filterWorkspaceDependenciesForApp(appValue, rawWorkspaceDependencies, appFolder);
68228
68979
  const hashes = await generateAppHash(filteredDeps, appFolder, rawApp, opts.defaultTs);
68229
- const isDirectlyStale = !await checkifMetadataUptodate(appFolder, hashes[TOP_HASH2], conf, TOP_HASH2);
68980
+ const isDirectlyStale = await isAppDirectlyStale(appFolder, hashes, conf);
68230
68981
  if (!isDirectlyStale) {
68231
68982
  if (!noStaleMessage) {
68232
68983
  info(colors.green(`App ${remote_path} metadata is up-to-date, skipping`));
@@ -68263,11 +69014,11 @@ async function generateAppLocksInternal(appFolder, rawApp, dryRun, workspace, op
68263
69014
  if (Object.keys(runnables).length === 0 && rawAppFile.runnables) {
68264
69015
  runnables = rawAppFile.runnables;
68265
69016
  }
68266
- replaceInlineScripts2(runnables, runnablesPath + SEP11, false);
69017
+ replaceInlineScripts2(runnables, runnablesPath + SEP12, false);
68267
69018
  updatedScripts = await updateRawAppRunnables(workspace, runnables, remote_path, appFolder, filteredDeps, opts.defaultTs, noStaleMessage, tempScriptRefs);
68268
69019
  } else {
68269
69020
  const normalAppFile = appFile;
68270
- replaceInlineScripts2(normalAppFile.value, appFolder + SEP11, false);
69021
+ replaceInlineScripts2(normalAppFile.value, appFolder + SEP12, false);
68271
69022
  const result = await updateAppInlineScripts(workspace, normalAppFile.value, remote_path, appFolder, filteredDeps, opts.defaultTs, noStaleMessage, tempScriptRefs);
68272
69023
  normalAppFile.value = result.value;
68273
69024
  updatedScripts = result.updatedScripts;
@@ -68299,7 +69050,7 @@ async function filterWorkspaceDependenciesForApp(appValue, rawWorkspaceDependenc
68299
69050
  }
68300
69051
  return inlineScript;
68301
69052
  });
68302
- return await filterWorkspaceDependenciesForScripts(scripts, rawWorkspaceDependencies, folder, SEP11);
69053
+ return await filterWorkspaceDependenciesForScripts(scripts, rawWorkspaceDependencies, folder, SEP12);
68303
69054
  }
68304
69055
  async function traverseAndProcessInlineScripts(obj, processor, currentPath = []) {
68305
69056
  if (!obj || typeof obj !== "object") {
@@ -68354,7 +69105,7 @@ async function updateRawAppRunnables(workspace, runnables, remotePath, appFolder
68354
69105
  }
68355
69106
  if (language === "frontend") {
68356
69107
  const [basePathO, ext2] = pathAssigner.assignPath(runnableId, language);
68357
- const basePath = basePathO.replaceAll(SEP11, "/");
69108
+ const basePath = basePathO.replaceAll(SEP12, "/");
68358
69109
  const contentPath = path14.join(runnablesFolder, `${basePath}${ext2}`);
68359
69110
  writeIfChanged(contentPath, content);
68360
69111
  const simplifiedRunnable = { type: "inline" };
@@ -68372,7 +69123,7 @@ async function updateRawAppRunnables(workspace, runnables, remotePath, appFolder
68372
69123
  try {
68373
69124
  const lock = await generateInlineScriptLock(workspace, content, language, `${remotePath}/${runnableId}`, rawDeps, tempScriptRefs);
68374
69125
  const [basePathO, ext2] = pathAssigner.assignPath(runnableId, language);
68375
- const basePath = basePathO.replaceAll(SEP11, "/");
69126
+ const basePath = basePathO.replaceAll(SEP12, "/");
68376
69127
  const contentPath = path14.join(runnablesFolder, `${basePath}${ext2}`);
68377
69128
  const lockPath = path14.join(runnablesFolder, `${basePath}lock`);
68378
69129
  writeIfChanged(contentPath, content);
@@ -68421,7 +69172,7 @@ async function updateAppInlineScripts(workspace, appValue, remotePath, appFolder
68421
69172
  lock = await generateInlineScriptLock(workspace, content, language, scriptPath, rawDeps, tempScriptRefs);
68422
69173
  }
68423
69174
  const [basePathO, ext2] = pathAssigner.assignPath(scriptName, language);
68424
- const basePath = basePathO.replaceAll(SEP11, "/");
69175
+ const basePath = basePathO.replaceAll(SEP12, "/");
68425
69176
  const contentPath = path14.join(appFolder, `${basePath}${ext2}`);
68426
69177
  const lockPath = path14.join(appFolder, `${basePath}lock`);
68427
69178
  writeIfChanged(contentPath, content);
@@ -68550,7 +69301,7 @@ async function inferRunnableSchemaFromFile(appFolder, runnableFilePath, defaultT
68550
69301
  warn(colors.yellow(`Could not read file: ${fullFilePath}`));
68551
69302
  return;
68552
69303
  }
68553
- const remotePath = appFolder.replaceAll(SEP11, "/");
69304
+ const remotePath = appFolder.replaceAll(SEP12, "/");
68554
69305
  try {
68555
69306
  const schemaResult = await inferSchema(language, content, currentSchema, `${remotePath}/${runnableId}`);
68556
69307
  info(colors.green(` Inferred schema for ${runnableId}`));
@@ -68586,7 +69337,7 @@ async function inferAllInlineSchemas(appFolder, defaultTs = "bun") {
68586
69337
  return schemas;
68587
69338
  }
68588
69339
  function getAppFolders(elems, extension) {
68589
- return Object.keys(elems).filter((p) => p.endsWith(SEP11 + extension)).map((p) => p.substring(0, p.length - (SEP11 + extension).length));
69340
+ return Object.keys(elems).filter((p) => p.endsWith(SEP12 + extension)).map((p) => p.substring(0, p.length - (SEP12 + extension).length));
68590
69341
  }
68591
69342
  async function generateLocksCommand(opts, appPath) {
68592
69343
  const { generateAppLocksInternal: generateAppLocksInternal2 } = await init_app_metadata().then(() => exports_app_metadata);
@@ -68604,7 +69355,7 @@ async function generateLocksCommand(opts, appPath) {
68604
69355
  } else {
68605
69356
  const ignore = await ignoreF2(opts);
68606
69357
  const elems = await elementsToMap2(await FSFSElement2(process.cwd(), [], true), (p, isD) => {
68607
- return ignore(p, isD) || !isD && !p.endsWith(SEP11 + "raw_app.yaml") && !p.endsWith(SEP11 + "app.yaml");
69358
+ return ignore(p, isD) || !isD && !p.endsWith(SEP12 + "raw_app.yaml") && !p.endsWith(SEP12 + "app.yaml");
68608
69359
  }, false, {});
68609
69360
  const rawAppFolders = getAppFolders(elems, "raw_app.yaml");
68610
69361
  const appFolders = getAppFolders(elems, "app.yaml");
@@ -68846,7 +69597,7 @@ async function generateAgents(opts, appFolder) {
68846
69597
  await requireLogin(opts);
68847
69598
  await regenerateAgentDocs(workspace.workspaceId, targetDir);
68848
69599
  }
68849
- var command8, generate_agents_default;
69600
+ var command9, generate_agents_default;
68850
69601
  var init_generate_agents = __esm(async () => {
68851
69602
  init_mod3();
68852
69603
  init_colors2();
@@ -68859,12 +69610,12 @@ var init_generate_agents = __esm(async () => {
68859
69610
  init_sync(),
68860
69611
  init_resource_folders()
68861
69612
  ]);
68862
- command8 = new Command().description("regenerate AGENTS.md and DATATABLES.md from remote workspace").arguments("[app_folder:string]").action(generateAgents);
68863
- generate_agents_default = command8;
69613
+ command9 = new Command().description("regenerate AGENTS.md and DATATABLES.md from remote workspace").arguments("[app_folder:string]").action(generateAgents);
69614
+ generate_agents_default = command9;
68864
69615
  });
68865
69616
 
68866
69617
  // src/commands/app/dev.ts
68867
- import { sep as SEP12 } from "node:path";
69618
+ import { sep as SEP13 } from "node:path";
68868
69619
  import * as http2 from "node:http";
68869
69620
  import * as fs11 from "node:fs";
68870
69621
  import * as path16 from "node:path";
@@ -69555,7 +70306,7 @@ async function loadRunnables() {
69555
70306
  runnables = rawApp?.runnables ?? {};
69556
70307
  }
69557
70308
  convertRunnablesToApiFormat(runnables);
69558
- replaceInlineScripts2(runnables, backendPath + SEP12, true);
70309
+ replaceInlineScripts2(runnables, backendPath + SEP13, true);
69559
70310
  repopulateFields(runnables);
69560
70311
  return runnables;
69561
70312
  } catch (error2) {
@@ -70007,7 +70758,7 @@ var DEFAULT_PORT = 4000, DEFAULT_HOST = "localhost", createHTML = (jsPath, cssPa
70007
70758
  </script>
70008
70759
  </body>
70009
70760
  </html>
70010
- `, command9, dev_default, ITERATIONS_BEFORE_SLOW_REFRESH = 10, ITERATIONS_BEFORE_SUPER_SLOW_REFRESH = 100, QUEUE_LOG_INTERVAL_MS2 = 5000;
70761
+ `, command10, dev_default, ITERATIONS_BEFORE_SLOW_REFRESH = 10, ITERATIONS_BEFORE_SUPER_SLOW_REFRESH = 100, QUEUE_LOG_INTERVAL_MS2 = 5000;
70011
70762
  var init_dev = __esm(async () => {
70012
70763
  init_mod3();
70013
70764
  init_colors2();
@@ -70032,10 +70783,10 @@ var init_dev = __esm(async () => {
70032
70783
  init_generate_agents(),
70033
70784
  init_resource_folders()
70034
70785
  ]);
70035
- command9 = new Command().description("Start a development server for building apps with live reload and hot module replacement").arguments("[app_folder:string]").option("--port <port:number>", "Port to run the dev server on (will find next available port if occupied)").option("--host <host:string>", "Host to bind the dev server to", {
70786
+ command10 = new Command().description("Start a development server for building apps with live reload and hot module replacement").arguments("[app_folder:string]").option("--port <port:number>", "Port to run the dev server on (will find next available port if occupied)").option("--host <host:string>", "Host to bind the dev server to", {
70036
70787
  default: DEFAULT_HOST
70037
70788
  }).option("--entry <entry:string>", "Entry point file (default: index.ts for Svelte/Vue, index.tsx otherwise)").option("--no-open", "Don't automatically open the browser").action(dev);
70038
- dev_default = command9;
70789
+ dev_default = command10;
70039
70790
  });
70040
70791
 
70041
70792
  // src/commands/app/lint.ts
@@ -70164,7 +70915,7 @@ async function lint2(opts, appFolder) {
70164
70915
  ✅ All checks passed
70165
70916
  `));
70166
70917
  }
70167
- var command10, lint_default2;
70918
+ var command11, lint_default2;
70168
70919
  var init_lint2 = __esm(async () => {
70169
70920
  init_mod3();
70170
70921
  init_colors2();
@@ -70176,8 +70927,8 @@ var init_lint2 = __esm(async () => {
70176
70927
  init_raw_apps(),
70177
70928
  init_resource_folders()
70178
70929
  ]);
70179
- command10 = new Command().description("Lint a raw app folder to validate structure and buildability").arguments("[app_folder:string]").option("--fix", "Attempt to fix common issues (not implemented yet)").action(lint2);
70180
- lint_default2 = command10;
70930
+ command11 = new Command().description("Lint a raw app folder to validate structure and buildability").arguments("[app_folder:string]").option("--fix", "Attempt to fix common issues (not implemented yet)").action(lint2);
70931
+ lint_default2 = command11;
70181
70932
  });
70182
70933
 
70183
70934
  // src/commands/app/new.ts
@@ -70713,7 +71464,7 @@ createApp(App).mount('#root')`, indexCss = `body {
70713
71464
  .myclass {
70714
71465
  border: 1px solid gray;
70715
71466
  padding: 2px;
70716
- }`, templates, command11, new_default;
71467
+ }`, templates, command12, new_default;
70717
71468
  var init_new = __esm(async () => {
70718
71469
  init_mod3();
70719
71470
  init_colors2();
@@ -70798,12 +71549,12 @@ var init_new = __esm(async () => {
70798
71549
  }
70799
71550
  }
70800
71551
  };
70801
- command11 = new Command().description("create a new raw app from a template").option("--summary <summary:string>", "App summary (short description). Skips the prompt when provided. Triggers non-interactive mode.").option("--path <path:string>", "App path (e.g., f/folder/my_app or u/username/my_app). Skips the prompt when provided. Triggers non-interactive mode.").option("--framework <framework:string>", "Framework template: react19 | react18 | svelte5 | vue. Skips the prompt when provided. Triggers non-interactive mode.").option("--datatable <datatable:string>", "Datatable to wire up. Without this flag in non-interactive mode, no datatable is configured.").option("--schema <schema:string>", "Schema to use with --datatable. Created (CREATE SCHEMA IF NOT EXISTS) if it doesn't already exist.").option("--overwrite", "Overwrite the target directory if it already exists, without prompting.").option("--no-open-in-desktop", "Do not prompt to open the new app in Claude Desktop.").action(newApp);
70802
- new_default = command11;
71552
+ command12 = new Command().description("create a new raw app from a template").option("--summary <summary:string>", "App summary (short description). Skips the prompt when provided. Triggers non-interactive mode.").option("--path <path:string>", "App path (e.g., f/folder/my_app or u/username/my_app). Skips the prompt when provided. Triggers non-interactive mode.").option("--framework <framework:string>", "Framework template: react19 | react18 | svelte5 | vue. Skips the prompt when provided. Triggers non-interactive mode.").option("--datatable <datatable:string>", "Datatable to wire up. Without this flag in non-interactive mode, no datatable is configured.").option("--schema <schema:string>", "Schema to use with --datatable. Created (CREATE SCHEMA IF NOT EXISTS) if it doesn't already exist.").option("--overwrite", "Overwrite the target directory if it already exists, without prompting.").option("--no-open-in-desktop", "Do not prompt to open the new app in Claude Desktop.").action(newApp);
71553
+ new_default = command12;
70803
71554
  });
70804
71555
 
70805
71556
  // src/commands/app/app.ts
70806
- import { sep as SEP13 } from "node:path";
71557
+ import { sep as SEP14 } from "node:path";
70807
71558
  import { stat as stat10 } from "node:fs/promises";
70808
71559
  function respecializeFields(fields) {
70809
71560
  Object.entries(fields).forEach(([k, v]) => {
@@ -70877,7 +71628,7 @@ async function pushApp(workspace, remotePath, localPath, message, permissionedAs
70877
71628
  return;
70878
71629
  }
70879
71630
  alreadySynced2.push(localPath);
70880
- remotePath = remotePath.replaceAll(SEP13, "/");
71631
+ remotePath = remotePath.replaceAll(SEP14, "/");
70881
71632
  let app = undefined;
70882
71633
  try {
70883
71634
  app = await getAppByPath({
@@ -70897,8 +71648,8 @@ async function pushApp(workspace, remotePath, localPath, message, permissionedAs
70897
71648
  if (app) {
70898
71649
  app.policy = undefined;
70899
71650
  }
70900
- if (!localPath.endsWith(SEP13)) {
70901
- localPath += SEP13;
71651
+ if (!localPath.endsWith(SEP14)) {
71652
+ localPath += SEP14;
70902
71653
  }
70903
71654
  const path19 = localPath + "app.yaml";
70904
71655
  const localApp = await yamlParseFile(path19);
@@ -70999,12 +71750,12 @@ async function push5(opts, filePath, remotePath) {
70999
71750
  }
71000
71751
  const workspace = await resolveWorkspace(opts);
71001
71752
  await requireLogin(opts);
71002
- const normalizedPath = filePath.endsWith(SEP13) ? filePath.slice(0, -1) : filePath;
71753
+ const normalizedPath = filePath.endsWith(SEP14) ? filePath.slice(0, -1) : filePath;
71003
71754
  const isRawApp = normalizedPath.endsWith("__raw_app") || normalizedPath.endsWith(".raw_app");
71004
71755
  let hasRawAppYaml = false;
71005
71756
  if (!isRawApp) {
71006
71757
  try {
71007
- const rawAppPath = (filePath.endsWith(SEP13) ? filePath : filePath + SEP13) + "raw_app.yaml";
71758
+ const rawAppPath = (filePath.endsWith(SEP14) ? filePath : filePath + SEP14) + "raw_app.yaml";
71008
71759
  await stat10(rawAppPath);
71009
71760
  hasRawAppYaml = true;
71010
71761
  } catch {}
@@ -71018,7 +71769,7 @@ async function push5(opts, filePath, remotePath) {
71018
71769
  info(colors.bold.underline.green("App pushed"));
71019
71770
  }
71020
71771
  }
71021
- var alreadySynced2, command12, app_default;
71772
+ var alreadySynced2, command13, app_default;
71022
71773
  var init_app = __esm(async () => {
71023
71774
  init_mod3();
71024
71775
  init_mod6();
@@ -71039,7 +71790,7 @@ var init_app = __esm(async () => {
71039
71790
  init_generate_agents()
71040
71791
  ]);
71041
71792
  alreadySynced2 = [];
71042
- command12 = new Command().description("app related commands").option("--json", "Output as JSON (for piping to jq)").action(list6).command("list", "list all apps").option("--json", "Output as JSON (for piping to jq)").action(list6).command("get", "get an app's details").arguments("<path:string>").option("--json", "Output as JSON (for piping to jq)").action(get4).command("push", "push a local app ").arguments("<file_path:string> <remote_path:string>").action(push5).command("dev", dev_default).command("lint", lint_default2).command("new", new_default).command("generate-agents", generate_agents_default).command("generate-locks", 'DEPRECATED: re-generate app lockfiles. Use "wmill generate-metadata" instead.').arguments("[app_folder:string]").option("--yes", "Skip confirmation prompt").option("--dry-run", "Perform a dry run without making changes").option("--default-ts <runtime:string>", "Default TypeScript runtime (bun or deno)").action(async (opts, appFolder) => {
71793
+ command13 = new Command().description("app related commands").option("--json", "Output as JSON (for piping to jq)").action(list6).command("list", "list all apps").option("--json", "Output as JSON (for piping to jq)").action(list6).command("get", "get an app's details").arguments("<path:string>").option("--json", "Output as JSON (for piping to jq)").action(get4).command("push", "push a local app ").arguments("<file_path:string> <remote_path:string>").action(push5).command("dev", dev_default).command("lint", lint_default2).command("new", new_default).command("generate-agents", generate_agents_default).command("generate-locks", 'DEPRECATED: re-generate app lockfiles. Use "wmill generate-metadata" instead.').arguments("[app_folder:string]").option("--yes", "Skip confirmation prompt").option("--dry-run", "Perform a dry run without making changes").option("--default-ts <runtime:string>", "Default TypeScript runtime (bun or deno)").action(async (opts, appFolder) => {
71043
71794
  warn(colors.yellow('This command is deprecated. Use "wmill generate-metadata" instead.'));
71044
71795
  const { generateLocksCommand: generateLocksCommand2 } = await init_app_metadata().then(() => exports_app_metadata);
71045
71796
  await generateLocksCommand2(opts, appFolder);
@@ -71069,12 +71820,12 @@ var init_app = __esm(async () => {
71069
71820
  });
71070
71821
  info(colors.green(`Updated permissioned_as for app ${appPath} to ${email}`));
71071
71822
  });
71072
- app_default = command12;
71823
+ app_default = command13;
71073
71824
  });
71074
71825
 
71075
71826
  // src/commands/folder/folder.ts
71076
71827
  import { stat as stat11, readdir as readdir8, writeFile as writeFile11, mkdir as mkdir8 } from "node:fs/promises";
71077
- import { sep as SEP14 } from "node:path";
71828
+ import { sep as SEP15 } from "node:path";
71078
71829
  async function list7(opts) {
71079
71830
  if (opts.json)
71080
71831
  setSilent(true);
@@ -71094,8 +71845,8 @@ async function list7(opts) {
71094
71845
  }
71095
71846
  }
71096
71847
  async function newFolder(opts, name) {
71097
- const dirPath = `f${SEP14}${name}`;
71098
- const filePath = `${dirPath}${SEP14}folder.meta.yaml`;
71848
+ const dirPath = `f${SEP15}${name}`;
71849
+ const filePath = `${dirPath}${SEP15}folder.meta.yaml`;
71099
71850
  try {
71100
71851
  await stat11(filePath);
71101
71852
  throw new Error("File already exists: " + filePath);
@@ -71133,13 +71884,13 @@ async function get5(opts, name) {
71133
71884
  }
71134
71885
  }
71135
71886
  async function pushFolder(workspace, name, folder, localFolder) {
71136
- if (name.startsWith(SEP14)) {
71887
+ if (name.startsWith(SEP15)) {
71137
71888
  name = name.substring(1);
71138
71889
  }
71139
- if (name.startsWith("f" + SEP14)) {
71890
+ if (name.startsWith("f" + SEP15)) {
71140
71891
  name = name.substring(2);
71141
71892
  }
71142
- name = name.split(SEP14)[0];
71893
+ name = name.split(SEP15)[0];
71143
71894
  debug(`Processing local folder ${name}`);
71144
71895
  try {
71145
71896
  folder = await getFolder({ workspace, name });
@@ -71183,7 +71934,7 @@ async function pushFolder(workspace, name, folder, localFolder) {
71183
71934
  async function push6(opts, name) {
71184
71935
  const workspace = await resolveWorkspace(opts);
71185
71936
  await requireLogin(opts);
71186
- const metaPath = `f${SEP14}${name}${SEP14}folder.meta.yaml`;
71937
+ const metaPath = `f${SEP15}${name}${SEP15}folder.meta.yaml`;
71187
71938
  try {
71188
71939
  await stat11(metaPath);
71189
71940
  } catch {
@@ -71206,7 +71957,7 @@ async function addMissing(opts) {
71206
71957
  for (const entry of entries) {
71207
71958
  if (!entry.isDirectory())
71208
71959
  continue;
71209
- const metaPath = `${fDir}${SEP14}${entry.name}${SEP14}folder.meta.yaml`;
71960
+ const metaPath = `${fDir}${SEP15}${entry.name}${SEP15}folder.meta.yaml`;
71210
71961
  try {
71211
71962
  await stat11(metaPath);
71212
71963
  } catch {
@@ -71233,7 +71984,7 @@ async function addMissing(opts) {
71233
71984
  info(`
71234
71985
  Created ${missing.length} folder.meta.yaml file(s). You can now run 'wmill sync push' to push them.`);
71235
71986
  }
71236
- var import_yaml24, command13, folder_default;
71987
+ var import_yaml24, command14, folder_default;
71237
71988
  var init_folder = __esm(async () => {
71238
71989
  init_colors2();
71239
71990
  init_mod3();
@@ -71247,7 +71998,7 @@ var init_folder = __esm(async () => {
71247
71998
  init_types()
71248
71999
  ]);
71249
72000
  import_yaml24 = __toESM(require_dist(), 1);
71250
- command13 = new Command().description("folder related commands").option("--json", "Output as JSON (for piping to jq)").action(list7).command("list", "list all folders").option("--json", "Output as JSON (for piping to jq)").action(list7).command("get", "get a folder's details").arguments("<name:string>").option("--json", "Output as JSON (for piping to jq)").action(get5).command("new", "create a new folder locally").arguments("<name:string>").option("--summary <summary:string>", "folder summary").action(newFolder).command("push", "push a local folder to the remote by name. This overrides any remote versions.").arguments("<name:string>").action(push6).command("add-missing", "create default folder.meta.yaml for all subdirectories of f/ that are missing one").option("-y, --yes", "skip confirmation prompt").action(addMissing).command("show-rules", "Show default_permissioned_as rules for a folder. Use --test-path to see which rule matches a given item path.").arguments("<name:string>").option("--test-path <path:string>", "Test which rule matches this item path (e.g. f/prod/jobs/my_script)").option("--json", "Output as JSON").action(async (opts, folderName2) => {
72001
+ command14 = new Command().description("folder related commands").option("--json", "Output as JSON (for piping to jq)").action(list7).command("list", "list all folders").option("--json", "Output as JSON (for piping to jq)").action(list7).command("get", "get a folder's details").arguments("<name:string>").option("--json", "Output as JSON (for piping to jq)").action(get5).command("new", "create a new folder locally").arguments("<name:string>").option("--summary <summary:string>", "folder summary").action(newFolder).command("push", "push a local folder to the remote by name. This overrides any remote versions.").arguments("<name:string>").action(push6).command("add-missing", "create default folder.meta.yaml for all subdirectories of f/ that are missing one").option("-y, --yes", "skip confirmation prompt").action(addMissing).command("show-rules", "Show default_permissioned_as rules for a folder. Use --test-path to see which rule matches a given item path.").arguments("<name:string>").option("--test-path <path:string>", "Test which rule matches this item path (e.g. f/prod/jobs/my_script)").option("--json", "Output as JSON").action(async (opts, folderName2) => {
71251
72002
  const workspace = await resolveWorkspace(opts);
71252
72003
  await requireLogin(opts);
71253
72004
  const folder = await getFolder({
@@ -71295,13 +72046,13 @@ var init_folder = __esm(async () => {
71295
72046
  info(colors.yellow(`No rule matches path '${testPath}' (relative: '${relative7}')`));
71296
72047
  }
71297
72048
  });
71298
- folder_default = command13;
72049
+ folder_default = command14;
71299
72050
  });
71300
72051
 
71301
72052
  // src/commands/variable/variable.ts
71302
72053
  import { mkdir as mkdir9, stat as stat12, writeFile as writeFile12 } from "node:fs/promises";
71303
72054
  import { dirname as dirname14 } from "node:path";
71304
- import { sep as SEP15 } from "node:path";
72055
+ import { sep as SEP16 } from "node:path";
71305
72056
  async function list8(opts) {
71306
72057
  if (opts.json)
71307
72058
  setSilent(true);
@@ -71370,7 +72121,7 @@ async function pushVariable(workspace, remotePath, variable, localVariable, plai
71370
72121
  try {
71371
72122
  variable = await getVariable({
71372
72123
  workspace,
71373
- path: remotePath.replaceAll(SEP15, "/"),
72124
+ path: remotePath.replaceAll(SEP16, "/"),
71374
72125
  decryptSecret: plainSecrets,
71375
72126
  includeEncrypted: true
71376
72127
  });
@@ -71386,7 +72137,7 @@ async function pushVariable(workspace, remotePath, variable, localVariable, plai
71386
72137
  debug(`Variable ${remotePath} is not up-to-date, updating`);
71387
72138
  await updateVariable({
71388
72139
  workspace,
71389
- path: remotePath.replaceAll(SEP15, "/"),
72140
+ path: remotePath.replaceAll(SEP16, "/"),
71390
72141
  alreadyEncrypted: !plainSecrets,
71391
72142
  requestBody: {
71392
72143
  ...localVariable,
@@ -71399,7 +72150,7 @@ async function pushVariable(workspace, remotePath, variable, localVariable, plai
71399
72150
  workspace,
71400
72151
  alreadyEncrypted: !plainSecrets,
71401
72152
  requestBody: {
71402
- path: remotePath.replaceAll(SEP15, "/"),
72153
+ path: remotePath.replaceAll(SEP16, "/"),
71403
72154
  ...localVariable
71404
72155
  }
71405
72156
  });
@@ -71445,7 +72196,7 @@ async function add2(opts, value, remotePath) {
71445
72196
  }, true);
71446
72197
  info(colors.bold.underline.green(`Variable ${remotePath} pushed`));
71447
72198
  }
71448
- var import_yaml25, command14, variable_default;
72199
+ var import_yaml25, command15, variable_default;
71449
72200
  var init_variable = __esm(async () => {
71450
72201
  init_mod3();
71451
72202
  init_mod6();
@@ -71459,14 +72210,14 @@ var init_variable = __esm(async () => {
71459
72210
  init_confirm()
71460
72211
  ]);
71461
72212
  import_yaml25 = __toESM(require_dist(), 1);
71462
- command14 = new Command().description("variable related commands").option("--json", "Output as JSON (for piping to jq)").action(list8).command("list", "list all variables").option("--json", "Output as JSON (for piping to jq)").action(list8).command("get", "get a variable's details").arguments("<path:string>").option("--json", "Output as JSON (for piping to jq)").action(get6).command("new", "create a new variable locally").arguments("<path:string>").action(newVariable).command("push", "Push a local variable spec. This overrides any remote versions.").arguments("<file_path:string> <remote_path:string>").option("--plain-secrets", "Push secrets as plain text").action(push7).command("add", "Create a new variable on the remote. This will update the variable if it already exists.").arguments("<value:string> <remote_path:string>").option("--plain-secrets", "Push secrets as plain text").option("--public", "Legacy option, use --plain-secrets instead").action(add2);
71463
- variable_default = command14;
72213
+ command15 = new Command().description("variable related commands").option("--json", "Output as JSON (for piping to jq)").action(list8).command("list", "list all variables").option("--json", "Output as JSON (for piping to jq)").action(list8).command("get", "get a variable's details").arguments("<path:string>").option("--json", "Output as JSON (for piping to jq)").action(get6).command("new", "create a new variable locally").arguments("<path:string>").action(newVariable).command("push", "Push a local variable spec. This overrides any remote versions.").arguments("<file_path:string> <remote_path:string>").option("--plain-secrets", "Push secrets as plain text").action(push7).command("add", "Create a new variable on the remote. This will update the variable if it already exists.").arguments("<value:string> <remote_path:string>").option("--plain-secrets", "Push secrets as plain text").option("--public", "Legacy option, use --plain-secrets instead").action(add2);
72214
+ variable_default = command15;
71464
72215
  });
71465
72216
 
71466
72217
  // src/commands/schedule/schedule.ts
71467
72218
  import { mkdir as mkdir10, stat as stat13, writeFile as writeFile13 } from "node:fs/promises";
71468
72219
  import { dirname as dirname15 } from "node:path";
71469
- import { sep as SEP16 } from "node:path";
72220
+ import { sep as SEP17 } from "node:path";
71470
72221
  async function list9(opts) {
71471
72222
  if (opts.json)
71472
72223
  setSilent(true);
@@ -71530,7 +72281,7 @@ async function get7(opts, path19) {
71530
72281
  }
71531
72282
  }
71532
72283
  async function pushSchedule(workspace, path19, schedule, localSchedule, permissionedAsContext) {
71533
- path19 = removeType(path19, "schedule").replaceAll(SEP16, "/");
72284
+ path19 = removeType(path19, "schedule").replaceAll(SEP17, "/");
71534
72285
  debug(`Processing local schedule ${path19}`);
71535
72286
  try {
71536
72287
  schedule = await getSchedule({ workspace, path: path19 });
@@ -71632,7 +72383,7 @@ async function push8(opts, filePath, remotePath) {
71632
72383
  await pushSchedule(workspace.workspaceId, remotePath, undefined, parseFromFile(filePath));
71633
72384
  console.log(colors.bold.underline.green("Schedule pushed"));
71634
72385
  }
71635
- var import_yaml26, command15, schedule_default;
72386
+ var import_yaml26, command16, schedule_default;
71636
72387
  var init_schedule = __esm(async () => {
71637
72388
  init_mod3();
71638
72389
  init_mod6();
@@ -71647,7 +72398,7 @@ var init_schedule = __esm(async () => {
71647
72398
  init_types()
71648
72399
  ]);
71649
72400
  import_yaml26 = __toESM(require_dist(), 1);
71650
- command15 = new Command().description("schedule related commands").option("--json", "Output as JSON (for piping to jq)").action(list9).command("list", "list all schedules").option("--json", "Output as JSON (for piping to jq)").action(list9).command("get", "get a schedule's details").arguments("<path:string>").option("--json", "Output as JSON (for piping to jq)").action(get7).command("new", "create a new schedule locally").arguments("<path:string>").action(newSchedule).command("push", "push a local schedule spec. This overrides any remote versions.").arguments("<file_path:string> <remote_path:string>").action(push8).command("enable", "Enable a schedule").arguments("<path:string>").action(enable).command("disable", "Disable a schedule").arguments("<path:string>").action(disable).command("set-permissioned-as", "Set the email (run-as user) for a schedule (requires admin or wm_deployers group)").arguments("<path:string> <email:string>").action(async (opts, schedulePath, email) => {
72401
+ command16 = new Command().description("schedule related commands").option("--json", "Output as JSON (for piping to jq)").action(list9).command("list", "list all schedules").option("--json", "Output as JSON (for piping to jq)").action(list9).command("get", "get a schedule's details").arguments("<path:string>").option("--json", "Output as JSON (for piping to jq)").action(get7).command("new", "create a new schedule locally").arguments("<path:string>").action(newSchedule).command("push", "push a local schedule spec. This overrides any remote versions.").arguments("<file_path:string> <remote_path:string>").action(push8).command("enable", "Enable a schedule").arguments("<path:string>").action(enable).command("disable", "Disable a schedule").arguments("<path:string>").action(disable).command("set-permissioned-as", "Set the email (run-as user) for a schedule (requires admin or wm_deployers group)").arguments("<path:string> <email:string>").action(async (opts, schedulePath, email) => {
71651
72402
  const workspace = await resolveWorkspace(opts);
71652
72403
  await requireLogin(opts);
71653
72404
  const cache3 = new Map;
@@ -71669,7 +72420,7 @@ var init_schedule = __esm(async () => {
71669
72420
  });
71670
72421
  info(colors.green(`Updated permissioned_as for schedule ${schedulePath} to ${email} (username: ${username})`));
71671
72422
  });
71672
- schedule_default = command15;
72423
+ schedule_default = command16;
71673
72424
  });
71674
72425
 
71675
72426
  // src/commands/instance/slack.ts
@@ -72763,7 +73514,7 @@ async function whoami3(opts) {
72763
73514
  error(colors.red(`Failed to retrieve whoami information: ${error2.message}`));
72764
73515
  }
72765
73516
  }
72766
- var import_yaml29, command16, instance_default;
73517
+ var import_yaml29, command17, instance_default;
72767
73518
  var init_instance = __esm(async () => {
72768
73519
  init_colors2();
72769
73520
  init_mod3();
@@ -72788,7 +73539,7 @@ var init_instance = __esm(async () => {
72788
73539
  init_workspace()
72789
73540
  ]);
72790
73541
  import_yaml29 = __toESM(require_dist(), 1);
72791
- command16 = new Command().description("sync local with a remote instance or the opposite (push or pull)").action(async () => {
73542
+ command17 = new Command().description("sync local with a remote instance or the opposite (push or pull)").action(async () => {
72792
73543
  info("4 actions available, add, remove, switch, pull and push. Use -h to display help.");
72793
73544
  const activeInstance = await getActiveInstance({});
72794
73545
  new Table2().header(["name", "remote", "token"]).padding(2).border(true).body((await allInstances()).map((x) => [
@@ -72814,7 +73565,7 @@ var init_instance = __esm(async () => {
72814
73565
  await removeInstance(choice);
72815
73566
  info(colors.green.underline(`Removed instance ${choice}`));
72816
73567
  }).command("switch").complete("instance", async () => (await allInstances()).map((x) => x.name)).arguments("<instance:string:instance>").description("Switch the current instance").action(switchI).command("pull").description("Pull instance settings, users, configs, instance groups and overwrite local").option("--yes", "Pull without needing confirmation").option("--dry-run", "Perform a dry run without making changes").option("--skip-users", "Skip pulling users").option("--skip-settings", "Skip pulling settings").option("--skip-configs", "Skip pulling configs (worker groups)").option("--skip-groups", "Skip pulling instance groups").option("--include-workspaces", "Also pull workspaces").option("--folder-per-instance", "Create a folder per instance").option("--instance <instance:string>", "Name of the instance to pull from, override the active instance").option("--prefix <prefix:string>", "Prefix of the local workspaces to pull, used to create the folders when using --include-workspaces").option("--prefix-settings", "Store instance yamls inside prefixed folders when using --prefix and --folder-per-instance").action(instancePull).command("push").description("Push instance settings, users, configs, group and overwrite remote").option("--yes", "Push without needing confirmation").option("--dry-run", "Perform a dry run without making changes").option("--skip-users", "Skip pushing users").option("--skip-settings", "Skip pushing settings").option("--skip-configs", "Skip pushing configs (worker groups)").option("--skip-groups", "Skip pushing instance groups").option("--include-workspaces", "Also push workspaces").option("--folder-per-instance", "Create a folder per instance").option("--instance <instance:string>", "Name of the instance to push to, override the active instance").option("--prefix <prefix:string>", "Prefix of the local workspaces folders to push").option("--prefix-settings", "Store instance yamls inside prefixed folders when using --prefix and --folder-per-instance").action(instancePush).command("whoami").description("Display information about the currently logged-in user").action(whoami3).command("get-config").description("Dump the current instance config (global settings + worker configs) as YAML").option("-o, --output-file <file:string>", "Write YAML to a file instead of stdout").option("--show-secrets", "Include sensitive fields (license key, JWT secret) without prompting").option("--instance <instance:string>", "Name of the instance, override the active instance").action(getConfig2).command("connect-slack").description("Non-interactively connect Slack at the instance level using a pre-minted bot token (xoxb-...). Produces the same artifacts as the UI OAuth flow: global_settings 'slack' row + encrypted f/slack_bot/global_bot_token variable and resource in the admins workspace.").option("--bot-token <bot_token:string>", "Slack bot token (xoxb-...)", { required: true }).option("--team-id <team_id:string>", "Slack team id", { required: true }).option("--team-name <team_name:string>", "Slack team name", { required: true }).option("--instance <instance:string>", "Instance profile to connect against (defaults to the active instance)").action((opts) => connectSlackInstance2(opts));
72817
- instance_default = command16;
73568
+ instance_default = command17;
72818
73569
  });
72819
73570
 
72820
73571
  // src/commands/user/user.ts
@@ -73169,7 +73920,7 @@ async function pushInstanceGroups(opts, preview2 = false) {
73169
73920
  info(colors.green("Groups pushed to the instance"));
73170
73921
  }
73171
73922
  }
73172
- var import_yaml31, INSTANCE_USERS_PATH = "instance_users.yaml", instanceUsersPath, INSTANCE_GROUPS_PATH = "instance_groups.yaml", instanceGroupsPath, command17, user_default;
73923
+ var import_yaml31, INSTANCE_USERS_PATH = "instance_users.yaml", instanceUsersPath, INSTANCE_GROUPS_PATH = "instance_groups.yaml", instanceGroupsPath, command18, user_default;
73173
73924
  var init_user = __esm(async () => {
73174
73925
  init_colors2();
73175
73926
  init_mod3();
@@ -73185,12 +73936,12 @@ var init_user = __esm(async () => {
73185
73936
  import_yaml31 = __toESM(require_dist(), 1);
73186
73937
  instanceUsersPath = INSTANCE_USERS_PATH;
73187
73938
  instanceGroupsPath = INSTANCE_GROUPS_PATH;
73188
- command17 = new Command().description("user related commands").action(list10).command("add", "Create a user").arguments("<email:string> [password:string]").option("--superadmin", "Specify to make the new user superadmin.").option("--company <company:string>", "Specify to set the company of the new user.").option("--name <name:string>", "Specify to set the name of the new user.").action(add3).command("remove", "Delete a user").arguments("<email:string>").action(remove2).command("create-token", "Create a new API token for the authenticated user").option("--email <email:string>", "Specify credentials to use for authentication. This will not be stored. It will only be used to exchange for a token with the API server, which will not be stored either.", {
73939
+ command18 = new Command().description("user related commands").action(list10).command("add", "Create a user").arguments("<email:string> [password:string]").option("--superadmin", "Specify to make the new user superadmin.").option("--company <company:string>", "Specify to set the company of the new user.").option("--name <name:string>", "Specify to set the name of the new user.").action(add3).command("remove", "Delete a user").arguments("<email:string>").action(remove2).command("create-token", "Create a new API token for the authenticated user").option("--email <email:string>", "Specify credentials to use for authentication. This will not be stored. It will only be used to exchange for a token with the API server, which will not be stored either.", {
73189
73940
  depends: ["password"]
73190
73941
  }).option("--password <password:string>", "Specify credentials to use for authentication. This will not be stored. It will only be used to exchange for a token with the API server, which will not be stored either.", {
73191
73942
  depends: ["email"]
73192
73943
  }).action(createToken2);
73193
- user_default = command17;
73944
+ user_default = command18;
73194
73945
  });
73195
73946
 
73196
73947
  // src/commands/dependencies/dependencies.ts
@@ -73239,7 +73990,7 @@ async function pushWorkspaceDependencies(workspace, path20, _befObj, newDependen
73239
73990
  });
73240
73991
  info(colors.green(`Successfully pushed ${displayName} for ${language}`));
73241
73992
  }
73242
- var command18, dependencies_default;
73993
+ var command19, dependencies_default;
73243
73994
  var init_dependencies = __esm(async () => {
73244
73995
  init_colors2();
73245
73996
  init_mod3();
@@ -73251,14 +74002,14 @@ var init_dependencies = __esm(async () => {
73251
74002
  init_metadata(),
73252
74003
  init_utils()
73253
74004
  ]);
73254
- command18 = new Command().alias("deps").description("workspace dependencies related commands").command("push", "Push workspace dependencies from a local file").arguments("<file_path:string>").action(push9);
73255
- dependencies_default = command18;
74005
+ command19 = new Command().alias("deps").description("workspace dependencies related commands").command("push", "Push workspace dependencies from a local file").arguments("<file_path:string>").action(push9);
74006
+ dependencies_default = command19;
73256
74007
  });
73257
74008
 
73258
74009
  // src/commands/trigger/trigger.ts
73259
74010
  import { mkdir as mkdir12, stat as stat15, writeFile as writeFile17 } from "node:fs/promises";
73260
74011
  import { dirname as dirname16 } from "node:path";
73261
- import { sep as SEP17 } from "node:path";
74012
+ import { sep as SEP18 } from "node:path";
73262
74013
  async function getTrigger(triggerType, workspace, path20) {
73263
74014
  const triggerFunctions = {
73264
74015
  http: getHttpTrigger,
@@ -73309,7 +74060,7 @@ async function createTrigger(triggerType, workspace, path20, trigger) {
73309
74060
  await triggerFunction({ workspace, path: path20, requestBody: trigger });
73310
74061
  }
73311
74062
  async function pushTrigger(triggerType, workspace, path20, trigger, localTrigger, permissionedAsContext) {
73312
- path20 = removeType(path20, triggerType + "_trigger").replaceAll(SEP17, "/");
74063
+ path20 = removeType(path20, triggerType + "_trigger").replaceAll(SEP18, "/");
73313
74064
  debug(`Processing local ${triggerType} trigger ${path20}`);
73314
74065
  try {
73315
74066
  trigger = await getTrigger(triggerType, workspace, path20);
@@ -73615,7 +74366,7 @@ async function push10(opts, filePath, remotePath) {
73615
74366
  await pushTrigger(triggerKind, workspace.workspaceId, remotePath, undefined, parseFromFile(filePath));
73616
74367
  console.log(colors.bold.underline.green("Trigger pushed"));
73617
74368
  }
73618
- var import_yaml33, triggerTemplates, TRIGGER_SKIP_FIELDS, command19, trigger_default;
74369
+ var import_yaml33, triggerTemplates, TRIGGER_SKIP_FIELDS, command20, trigger_default;
73619
74370
  var init_trigger = __esm(async () => {
73620
74371
  init_services_gen();
73621
74372
  init_mod3();
@@ -73721,7 +74472,7 @@ var init_trigger = __esm(async () => {
73721
74472
  }
73722
74473
  };
73723
74474
  TRIGGER_SKIP_FIELDS = new Set(["workspace_id", "extra_perms", "edited_by", "edited_at"]);
73724
- command19 = new Command().description("trigger related commands").option("--json", "Output as JSON (for piping to jq)").action(list11).command("list", "list all triggers").option("--json", "Output as JSON (for piping to jq)").action(list11).command("get", "get a trigger's details").arguments("<path:string>").option("--json", "Output as JSON (for piping to jq)").option("--kind <kind:string>", "Trigger kind (http, websocket, kafka, nats, postgres, mqtt, sqs, gcp, azure, email). Recommended for faster lookup").action(get8).command("new", "create a new trigger locally").arguments("<path:string>").option("--kind <kind:string>", "Trigger kind (required: http, websocket, kafka, nats, postgres, mqtt, sqs, gcp, azure, email)").action(newTrigger).command("push", "push a local trigger spec. This overrides any remote versions.").arguments("<file_path:string> <remote_path:string>").action(push10).command("set-permissioned-as", "Set the email (run-as user) for a trigger (requires admin or wm_deployers group)").arguments("<path:string> <email:string>").option("--kind <kind:string>", "Trigger kind (required: http, websocket, kafka, nats, postgres, mqtt, sqs, gcp, azure, email)").action(async (opts, triggerPath, email) => {
74475
+ command20 = new Command().description("trigger related commands").option("--json", "Output as JSON (for piping to jq)").action(list11).command("list", "list all triggers").option("--json", "Output as JSON (for piping to jq)").action(list11).command("get", "get a trigger's details").arguments("<path:string>").option("--json", "Output as JSON (for piping to jq)").option("--kind <kind:string>", "Trigger kind (http, websocket, kafka, nats, postgres, mqtt, sqs, gcp, azure, email). Recommended for faster lookup").action(get8).command("new", "create a new trigger locally").arguments("<path:string>").option("--kind <kind:string>", "Trigger kind (required: http, websocket, kafka, nats, postgres, mqtt, sqs, gcp, azure, email)").action(newTrigger).command("push", "push a local trigger spec. This overrides any remote versions.").arguments("<file_path:string> <remote_path:string>").action(push10).command("set-permissioned-as", "Set the email (run-as user) for a trigger (requires admin or wm_deployers group)").arguments("<path:string> <email:string>").option("--kind <kind:string>", "Trigger kind (required: http, websocket, kafka, nats, postgres, mqtt, sqs, gcp, azure, email)").action(async (opts, triggerPath, email) => {
73725
74476
  const workspace = await resolveWorkspace(opts);
73726
74477
  await requireLogin(opts);
73727
74478
  if (!opts.kind) {
@@ -73744,12 +74495,12 @@ var init_trigger = __esm(async () => {
73744
74495
  });
73745
74496
  info(colors.green(`Updated permissioned_as for ${opts.kind} trigger ${triggerPath} to ${email} (username: ${username})`));
73746
74497
  });
73747
- trigger_default = command19;
74498
+ trigger_default = command20;
73748
74499
  });
73749
74500
 
73750
74501
  // src/types.ts
73751
74502
  import * as path20 from "node:path";
73752
- import { sep as SEP18 } from "node:path";
74503
+ import { sep as SEP19 } from "node:path";
73753
74504
  function isSuperset(subset, superset) {
73754
74505
  return Object.keys(subset).every((key) => {
73755
74506
  const eq = deepEqual(subset[key], superset[key]);
@@ -73893,7 +74644,7 @@ function getTypeStrFromPath(p) {
73893
74644
  if (isRawAppPath(p)) {
73894
74645
  return "raw_app";
73895
74646
  }
73896
- if (p.startsWith("dependencies" + SEP18)) {
74647
+ if (p.startsWith("dependencies" + SEP19)) {
73897
74648
  return "workspace_dependencies";
73898
74649
  }
73899
74650
  if (isFileResource(p) || isFilesetResource(p)) {
@@ -73923,7 +74674,7 @@ function getTypeStrFromPath(p) {
73923
74674
  }
73924
74675
  }
73925
74676
  function removeType(str, type) {
73926
- const normalizedStr = path20.normalize(str).replaceAll(SEP18, "/");
74677
+ const normalizedStr = path20.normalize(str).replaceAll(SEP19, "/");
73927
74678
  if (normalizedStr.endsWith("." + type + ".yaml") || normalizedStr.endsWith("." + type + ".json")) {
73928
74679
  return normalizedStr.slice(0, normalizedStr.length - type.length - 6);
73929
74680
  }
@@ -73933,7 +74684,7 @@ function removeType(str, type) {
73933
74684
  return normalizedStr;
73934
74685
  }
73935
74686
  function extractNativeTriggerInfo(p) {
73936
- const normalizedPath = path20.normalize(p).replaceAll(SEP18, "/");
74687
+ const normalizedPath = path20.normalize(p).replaceAll(SEP19, "/");
73937
74688
  const withoutExt = normalizedPath.replace(/\.(json|yaml)$/, "");
73938
74689
  const match2 = withoutExt.match(/^(.+)\.(flow|script)\.([^.]+)\.(\w+)_native_trigger$/);
73939
74690
  if (!match2) {
@@ -73947,8 +74698,8 @@ function extractNativeTriggerInfo(p) {
73947
74698
  };
73948
74699
  }
73949
74700
  function removePathPrefix(str, prefix) {
73950
- const normalizedStr = path20.normalize(str).replaceAll(SEP18, "/");
73951
- const normalizedPrefix = path20.normalize(prefix).replaceAll(SEP18, "/");
74701
+ const normalizedStr = path20.normalize(str).replaceAll(SEP19, "/");
74702
+ const normalizedPrefix = path20.normalize(prefix).replaceAll(SEP19, "/");
73952
74703
  if (normalizedStr === normalizedPrefix) {
73953
74704
  return "";
73954
74705
  }
@@ -74120,7 +74871,7 @@ var init_local_path_scripts = __esm(async () => {
74120
74871
  });
74121
74872
 
74122
74873
  // src/commands/flow/flow.ts
74123
- import { sep as SEP19 } from "node:path";
74874
+ import { sep as SEP20 } from "node:path";
74124
74875
  import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync7 } from "node:fs";
74125
74876
  function normalizeOptionalString(value) {
74126
74877
  return typeof value === "string" && value.trim() === "" ? undefined : value ?? undefined;
@@ -74181,7 +74932,7 @@ async function pushFlow(workspace, remotePath, localPath, message, permissionedA
74181
74932
  return;
74182
74933
  }
74183
74934
  alreadySynced3.push(localPath);
74184
- remotePath = remotePath.replaceAll(SEP19, "/");
74935
+ remotePath = remotePath.replaceAll(SEP20, "/");
74185
74936
  let flow = undefined;
74186
74937
  try {
74187
74938
  flow = await getFlowByPath({
@@ -74189,18 +74940,18 @@ async function pushFlow(workspace, remotePath, localPath, message, permissionedA
74189
74940
  path: remotePath
74190
74941
  });
74191
74942
  } catch {}
74192
- if (!localPath.endsWith(SEP19)) {
74193
- localPath += SEP19;
74943
+ if (!localPath.endsWith(SEP20)) {
74944
+ localPath += SEP20;
74194
74945
  }
74195
74946
  const localFlow = await yamlParseFile(localPath + "flow.yaml");
74196
74947
  const fileReader = async (path21) => await readTextFile(localPath + path21);
74197
74948
  const missingFiles = [];
74198
- await replaceInlineScripts(localFlow.value.modules, fileReader, exports_log, localPath, SEP19, undefined, missingFiles);
74949
+ await replaceInlineScripts(localFlow.value.modules, fileReader, exports_log, localPath, SEP20, undefined, missingFiles);
74199
74950
  if (localFlow.value.failure_module) {
74200
- await replaceInlineScripts([localFlow.value.failure_module], fileReader, exports_log, localPath, SEP19, undefined, missingFiles);
74951
+ await replaceInlineScripts([localFlow.value.failure_module], fileReader, exports_log, localPath, SEP20, undefined, missingFiles);
74201
74952
  }
74202
74953
  if (localFlow.value.preprocessor_module) {
74203
- await replaceInlineScripts([localFlow.value.preprocessor_module], fileReader, exports_log, localPath, SEP19, undefined, missingFiles);
74954
+ await replaceInlineScripts([localFlow.value.preprocessor_module], fileReader, exports_log, localPath, SEP20, undefined, missingFiles);
74204
74955
  }
74205
74956
  if (missingFiles.length > 0) {
74206
74957
  warn(colors.yellow(`Warning: missing inline script file(s): ${missingFiles.join(", ")}. ` + `The flow will be pushed with unresolved !inline references.`));
@@ -74223,9 +74974,9 @@ async function pushFlow(workspace, remotePath, localPath, message, permissionedA
74223
74974
  info(colors.bold.yellow(`Updating flow ${remotePath}...`));
74224
74975
  await updateFlow({
74225
74976
  workspace,
74226
- path: remotePath.replaceAll(SEP19, "/"),
74977
+ path: remotePath.replaceAll(SEP20, "/"),
74227
74978
  requestBody: {
74228
- path: remotePath.replaceAll(SEP19, "/"),
74979
+ path: remotePath.replaceAll(SEP20, "/"),
74229
74980
  deployment_message: message,
74230
74981
  ...localFlow,
74231
74982
  ...preserveFields
@@ -74237,7 +74988,7 @@ async function pushFlow(workspace, remotePath, localPath, message, permissionedA
74237
74988
  await createFlow({
74238
74989
  workspace,
74239
74990
  requestBody: {
74240
- path: remotePath.replaceAll(SEP19, "/"),
74991
+ path: remotePath.replaceAll(SEP20, "/"),
74241
74992
  deployment_message: message,
74242
74993
  ...localFlow,
74243
74994
  ...preserveFields
@@ -74486,25 +75237,25 @@ async function preview2(opts, flowPath) {
74486
75237
  const workspace = await resolveWorkspace(opts);
74487
75238
  await requireLogin(opts);
74488
75239
  const codebases = useLocalPathScripts ? listSyncCodebases(opts) : [];
74489
- const isFlowDir = flowPath.endsWith(".flow") || flowPath.endsWith(".flow" + SEP19) || flowPath.endsWith("__flow") || flowPath.endsWith("__flow" + SEP19);
75240
+ const isFlowDir = flowPath.endsWith(".flow") || flowPath.endsWith(".flow" + SEP20) || flowPath.endsWith("__flow") || flowPath.endsWith("__flow" + SEP20);
74490
75241
  if (!isFlowDir) {
74491
75242
  if (flowPath.endsWith("flow.yaml") || flowPath.endsWith("flow.json")) {
74492
- flowPath = flowPath.substring(0, flowPath.lastIndexOf(SEP19));
75243
+ flowPath = flowPath.substring(0, flowPath.lastIndexOf(SEP20));
74493
75244
  } else {
74494
75245
  throw new Error("Flow path must be a .flow/__flow directory or a flow.yaml file");
74495
75246
  }
74496
75247
  }
74497
- if (!flowPath.endsWith(SEP19)) {
74498
- flowPath += SEP19;
75248
+ if (!flowPath.endsWith(SEP20)) {
75249
+ flowPath += SEP20;
74499
75250
  }
74500
75251
  const localFlow = await yamlParseFile(flowPath + "flow.yaml");
74501
75252
  const fileReader = async (path21) => await readTextFile(flowPath + path21);
74502
- await replaceInlineScripts(localFlow.value.modules, fileReader, exports_log, flowPath, SEP19);
75253
+ await replaceInlineScripts(localFlow.value.modules, fileReader, exports_log, flowPath, SEP20);
74503
75254
  if (localFlow.value.failure_module) {
74504
- await replaceInlineScripts([localFlow.value.failure_module], fileReader, exports_log, flowPath, SEP19);
75255
+ await replaceInlineScripts([localFlow.value.failure_module], fileReader, exports_log, flowPath, SEP20);
74505
75256
  }
74506
75257
  if (localFlow.value.preprocessor_module) {
74507
- await replaceInlineScripts([localFlow.value.preprocessor_module], fileReader, exports_log, flowPath, SEP19);
75258
+ await replaceInlineScripts([localFlow.value.preprocessor_module], fileReader, exports_log, flowPath, SEP20);
74508
75259
  }
74509
75260
  if (useLocalPathScripts) {
74510
75261
  const scriptPaths = collectPathScriptPaths(localFlow.value);
@@ -74534,7 +75285,7 @@ async function preview2(opts, flowPath) {
74534
75285
  workspace: workspace.workspaceId,
74535
75286
  requestBody: {
74536
75287
  value: localFlow.value,
74537
- path: flowPath.substring(0, flowPath.indexOf(".flow")).replaceAll(SEP19, "/"),
75288
+ path: flowPath.substring(0, flowPath.indexOf(".flow")).replaceAll(SEP20, "/"),
74538
75289
  args: input
74539
75290
  }
74540
75291
  });
@@ -74566,8 +75317,8 @@ async function generateLocks(opts, folder) {
74566
75317
  } else {
74567
75318
  const ignore = await ignoreF(opts);
74568
75319
  const elems = Object.keys(await elementsToMap(await FSFSElement(process.cwd(), [], true), (p, isD) => {
74569
- return ignore(p, isD) || !isD && !p.endsWith(SEP19 + "flow.yaml") && !p.endsWith(SEP19 + "flow.json");
74570
- }, false, {})).map((x) => x.substring(0, x.lastIndexOf(SEP19)));
75320
+ return ignore(p, isD) || !isD && !p.endsWith(SEP20 + "flow.yaml") && !p.endsWith(SEP20 + "flow.json");
75321
+ }, false, {})).map((x) => x.substring(0, x.lastIndexOf(SEP20)));
74571
75322
  let hasAny = false;
74572
75323
  for (const folder2 of elems) {
74573
75324
  const candidate = await generateFlowLockInternal(folder2, true, workspace, opts);
@@ -74666,7 +75417,7 @@ async function showVersion(opts, flowPath, version) {
74666
75417
  console.log(JSON.stringify(flow.value, null, 2));
74667
75418
  }
74668
75419
  }
74669
- var import_yaml36, alreadySynced3, command20, flow_default;
75420
+ var import_yaml36, alreadySynced3, command21, flow_default;
74670
75421
  var init_flow = __esm(async () => {
74671
75422
  init_colors2();
74672
75423
  init_mod3();
@@ -74691,7 +75442,7 @@ var init_flow = __esm(async () => {
74691
75442
  ]);
74692
75443
  import_yaml36 = __toESM(require_dist(), 1);
74693
75444
  alreadySynced3 = [];
74694
- command20 = new Command().description("flow related commands").option("--show-archived", "Enable archived flows in output").option("--json", "Output as JSON (for piping to jq)").action(list12).command("list", "list all flows").option("--show-archived", "Enable archived flows in output").option("--json", "Output as JSON (for piping to jq)").action(list12).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>").option("--message <message:string>", "Deployment message").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", 'DEPRECATED: re-generate flow lock files. Use "wmill generate-metadata" instead.').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).command("history", "Show version history for a flow").arguments("<path:string>").option("--json", "Output as JSON (for piping to jq)").action(history2).command("show-version", "Show a specific version of a flow").arguments("<path:string> <version:string>").option("--json", "Output as JSON (for piping to jq)").action(showVersion).command("set-permissioned-as", "Set the on_behalf_of_email for a flow (requires admin or wm_deployers group)").arguments("<path:string> <email:string>").action(async (opts, flowPath, email) => {
75445
+ 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(list12).command("list", "list all flows").option("--show-archived", "Enable archived flows in output").option("--json", "Output as JSON (for piping to jq)").action(list12).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>").option("--message <message:string>", "Deployment message").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", 'DEPRECATED: re-generate flow lock files. Use "wmill generate-metadata" instead.').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).command("history", "Show version history for a flow").arguments("<path:string>").option("--json", "Output as JSON (for piping to jq)").action(history2).command("show-version", "Show a specific version of a flow").arguments("<path:string> <version:string>").option("--json", "Output as JSON (for piping to jq)").action(showVersion).command("set-permissioned-as", "Set the on_behalf_of_email for a flow (requires admin or wm_deployers group)").arguments("<path:string> <email:string>").action(async (opts, flowPath, email) => {
74695
75446
  const workspace = await resolveWorkspace(opts);
74696
75447
  await requireLogin(opts);
74697
75448
  const remote = await getFlowByPath({
@@ -74712,7 +75463,7 @@ var init_flow = __esm(async () => {
74712
75463
  });
74713
75464
  info(colors.green(`Updated permissioned_as for flow ${flowPath} to ${email}`));
74714
75465
  });
74715
- flow_default = command20;
75466
+ flow_default = command21;
74716
75467
  });
74717
75468
 
74718
75469
  // src/commands/gitsync-settings/converter.ts
@@ -75651,286 +76402,15 @@ __export(exports_gitsync_settings, {
75651
76402
  pullGitSyncSettings: () => pullGitSyncSettings,
75652
76403
  default: () => gitsync_settings_default
75653
76404
  });
75654
- var command22, gitsync_settings_default;
76405
+ var command23, gitsync_settings_default;
75655
76406
  var init_gitsync_settings = __esm(async () => {
75656
76407
  init_mod3();
75657
76408
  await __promiseAll([
75658
76409
  init_pull2(),
75659
76410
  init_push()
75660
76411
  ]);
75661
- command22 = new Command().description("Manage git-sync settings between local wmill.yaml and Windmill backend").command("pull").description("Pull git-sync settings from Windmill backend to local wmill.yaml").option("--repository <repo:string>", "Specify repository path (e.g., u/user/repo)").option("--default", "Write settings to top-level defaults instead of overrides").option("--replace", "Replace existing settings (non-interactive mode)").option("--override", "Add branch-specific override (non-interactive mode)").option("--diff", "Show differences without applying changes").option("--json-output", "Output in JSON format").option("--with-backend-settings <json:string>", "Use provided JSON settings instead of querying backend (for testing)").option("--yes", "Skip interactive prompts and use default behavior").option("--promotion <branch:string>", "Use promotionOverrides from the specified branch instead of regular overrides").action(pullGitSyncSettings).command("push").description("Push git-sync settings from local wmill.yaml to Windmill backend").option("--repository <repo:string>", "Specify repository path (e.g., u/user/repo)").option("--diff", "Show what would be pushed without applying changes").option("--json-output", "Output in JSON format").option("--with-backend-settings <json:string>", "Use provided JSON settings instead of querying backend (for testing)").option("--yes", "Skip interactive prompts and use default behavior").option("--promotion <branch:string>", "Use promotionOverrides from the specified branch instead of regular overrides").action(pushGitSyncSettings);
75662
- gitsync_settings_default = command22;
75663
- });
75664
-
75665
- // src/utils/dependency_tree.ts
75666
- async function uploadScripts(tree, workspace) {
75667
- const scriptHashes = {};
75668
- const workspaceDeps = [];
75669
- for (const path22 of tree.allPaths()) {
75670
- const content = tree.getContent(path22);
75671
- const itemType = tree.getItemType(path22);
75672
- if (itemType === "dependencies") {
75673
- if (content === undefined)
75674
- continue;
75675
- const info2 = workspaceDependenciesPathToLanguageAndFilename(path22);
75676
- if (info2) {
75677
- const hash2 = await generateHash(content);
75678
- workspaceDeps.push({ path: path22, language: info2.language, name: info2.name, hash: hash2 });
75679
- }
75680
- } else if (itemType === "script") {
75681
- if (!content)
75682
- continue;
75683
- const hash2 = await generateHash(content);
75684
- scriptHashes[path22] = hash2;
75685
- }
75686
- }
75687
- if (Object.keys(scriptHashes).length === 0 && workspaceDeps.length === 0)
75688
- return;
75689
- const mismatched = await diffRawScriptsWithDeployed({
75690
- workspace: workspace.workspaceId,
75691
- requestBody: {
75692
- scripts: scriptHashes,
75693
- workspace_deps: workspaceDeps
75694
- }
75695
- });
75696
- for (const path22 of mismatched) {
75697
- const content = tree.getContent(path22);
75698
- const itemType = tree.getItemType(path22);
75699
- if (itemType === "dependencies") {
75700
- if (content !== undefined) {
75701
- tree.setContentHash(path22, "mismatched");
75702
- }
75703
- } else if (content) {
75704
- const hash2 = await storeRawScriptTemp({
75705
- workspace: workspace.workspaceId,
75706
- requestBody: content
75707
- });
75708
- tree.setContentHash(path22, hash2);
75709
- }
75710
- }
75711
- }
75712
-
75713
- class DoubleLinkedDependencyTree {
75714
- nodes = new Map;
75715
- workspaceDeps = {};
75716
- setWorkspaceDeps(deps) {
75717
- this.workspaceDeps = deps;
75718
- }
75719
- async addNode(path22, content, language, metadata, imports, itemType, folder, originalPath, isDirectlyStale, isRawApp) {
75720
- const hasWorkspaceDeps = itemType === "script" || itemType === "inline_script";
75721
- const filteredDeps = hasWorkspaceDeps ? filterWorkspaceDependencies(this.workspaceDeps, content, language) : {};
75722
- const stalenessHash = await generateScriptHash({}, content, metadata);
75723
- if (!this.nodes.has(path22)) {
75724
- this.nodes.set(path22, {
75725
- content: "",
75726
- stalenessHash: "",
75727
- language: "deno",
75728
- metadata: "",
75729
- imports: new Set,
75730
- importedBy: new Set,
75731
- itemType: "script",
75732
- folder: "",
75733
- originalPath: "",
75734
- isDirectlyStale: false
75735
- });
75736
- }
75737
- const node = this.nodes.get(path22);
75738
- node.content = content;
75739
- node.stalenessHash = stalenessHash;
75740
- node.language = language;
75741
- node.metadata = metadata;
75742
- node.itemType = itemType;
75743
- node.folder = folder;
75744
- node.originalPath = originalPath;
75745
- node.isDirectlyStale = isDirectlyStale;
75746
- node.isRawApp = isRawApp;
75747
- const filteredDepsPaths = Object.keys(filteredDeps);
75748
- for (const depsPath of filteredDepsPaths) {
75749
- if (!this.nodes.has(depsPath)) {
75750
- const depsInfo = workspaceDependenciesPathToLanguageAndFilename(depsPath);
75751
- const contentHash = await generateHash(filteredDeps[depsPath] + depsPath);
75752
- const isUpToDate = await checkifMetadataUptodate(depsPath, contentHash, undefined);
75753
- this.nodes.set(depsPath, {
75754
- content: filteredDeps[depsPath],
75755
- stalenessHash: "",
75756
- language: depsInfo?.language ?? "deno",
75757
- metadata: "",
75758
- imports: new Set,
75759
- importedBy: new Set,
75760
- itemType: "dependencies",
75761
- folder: "",
75762
- originalPath: depsPath,
75763
- isDirectlyStale: !isUpToDate
75764
- });
75765
- }
75766
- }
75767
- const allImports = [...imports, ...filteredDepsPaths];
75768
- for (const importPath of allImports) {
75769
- node.imports.add(importPath);
75770
- if (!this.nodes.has(importPath)) {
75771
- this.nodes.set(importPath, {
75772
- content: "",
75773
- stalenessHash: "",
75774
- language: "deno",
75775
- metadata: "",
75776
- imports: new Set,
75777
- importedBy: new Set,
75778
- itemType: "script",
75779
- folder: "",
75780
- originalPath: "",
75781
- isDirectlyStale: false
75782
- });
75783
- }
75784
- this.nodes.get(importPath).importedBy.add(path22);
75785
- }
75786
- }
75787
- getContent(path22) {
75788
- return this.nodes.get(path22)?.content;
75789
- }
75790
- getStalenessHash(path22) {
75791
- return this.nodes.get(path22)?.stalenessHash;
75792
- }
75793
- getContentHash(path22) {
75794
- return this.nodes.get(path22)?.contentHash;
75795
- }
75796
- setContentHash(path22, hash2) {
75797
- const node = this.nodes.get(path22);
75798
- if (node) {
75799
- node.contentHash = hash2;
75800
- }
75801
- }
75802
- getLanguage(path22) {
75803
- return this.nodes.get(path22)?.language;
75804
- }
75805
- getMetadata(path22) {
75806
- return this.nodes.get(path22)?.metadata;
75807
- }
75808
- getStaleReason(path22) {
75809
- return this.nodes.get(path22)?.staleReason;
75810
- }
75811
- getItemType(path22) {
75812
- return this.nodes.get(path22)?.itemType;
75813
- }
75814
- getFolder(path22) {
75815
- return this.nodes.get(path22)?.folder;
75816
- }
75817
- getIsRawApp(path22) {
75818
- return this.nodes.get(path22)?.isRawApp;
75819
- }
75820
- getIsDirectlyStale(path22) {
75821
- return this.nodes.get(path22)?.isDirectlyStale ?? false;
75822
- }
75823
- getOriginalPath(path22) {
75824
- return this.nodes.get(path22)?.originalPath;
75825
- }
75826
- getImports(path22) {
75827
- return this.nodes.get(path22)?.imports;
75828
- }
75829
- isStale(path22) {
75830
- return this.nodes.get(path22)?.staleReason !== undefined;
75831
- }
75832
- propagateStaleness() {
75833
- const directlyStale = new Set;
75834
- for (const [path22, node] of this.nodes.entries()) {
75835
- if (node.isDirectlyStale) {
75836
- directlyStale.add(path22);
75837
- node.staleReason = "content changed";
75838
- }
75839
- }
75840
- const allStale = new Set(directlyStale);
75841
- const queue = [...directlyStale];
75842
- const visited = new Set;
75843
- while (queue.length > 0) {
75844
- const scriptPath = queue.shift();
75845
- if (visited.has(scriptPath))
75846
- continue;
75847
- visited.add(scriptPath);
75848
- const node = this.nodes.get(scriptPath);
75849
- if (!node)
75850
- continue;
75851
- for (const importer of node.importedBy) {
75852
- if (!allStale.has(importer)) {
75853
- allStale.add(importer);
75854
- queue.push(importer);
75855
- const importerNode = this.nodes.get(importer);
75856
- if (importerNode)
75857
- importerNode.staleReason = `depends on ${scriptPath}`;
75858
- }
75859
- }
75860
- }
75861
- }
75862
- traverseTransitive(scriptPath, callback) {
75863
- const queue = [scriptPath];
75864
- const visited = new Set;
75865
- while (queue.length > 0) {
75866
- const current = queue.shift();
75867
- if (visited.has(current))
75868
- continue;
75869
- visited.add(current);
75870
- const node = this.nodes.get(current);
75871
- if (!node)
75872
- continue;
75873
- for (const importPath of node.imports) {
75874
- const importNode = this.nodes.get(importPath);
75875
- if (importNode) {
75876
- const stop = callback(importPath, importNode);
75877
- if (!stop) {
75878
- queue.push(importPath);
75879
- }
75880
- }
75881
- }
75882
- }
75883
- }
75884
- allPaths() {
75885
- return this.nodes.keys();
75886
- }
75887
- *stalePaths() {
75888
- for (const [path22, node] of this.nodes.entries()) {
75889
- if (node.staleReason) {
75890
- yield path22;
75891
- }
75892
- }
75893
- }
75894
- has(path22) {
75895
- return this.nodes.has(path22);
75896
- }
75897
- getMismatchedWorkspaceDeps() {
75898
- const result2 = {};
75899
- for (const [path22, node] of this.nodes.entries()) {
75900
- if (node.itemType === "dependencies" && node.contentHash && node.content !== undefined) {
75901
- result2[path22] = node.content;
75902
- }
75903
- }
75904
- return result2;
75905
- }
75906
- getTempScriptRefs(scriptPath) {
75907
- const result2 = {};
75908
- this.traverseTransitive(scriptPath, (_path, node) => {
75909
- if (node.contentHash) {
75910
- result2[_path] = node.contentHash;
75911
- }
75912
- });
75913
- return result2;
75914
- }
75915
- async persistDepsHashes(depsPaths) {
75916
- for (const path22 of depsPaths) {
75917
- const node = this.nodes.get(path22);
75918
- if (node?.itemType === "dependencies" && node.content !== undefined) {
75919
- const hash2 = await generateHash(node.content + path22);
75920
- await updateMetadataGlobalLock(path22, hash2);
75921
- }
75922
- }
75923
- }
75924
- get size() {
75925
- return this.nodes.size;
75926
- }
75927
- }
75928
- var init_dependency_tree = __esm(async () => {
75929
- init_services_gen();
75930
- await __promiseAll([
75931
- init_metadata(),
75932
- init_utils()
75933
- ]);
76412
+ command23 = new Command().description("Manage git-sync settings between local wmill.yaml and Windmill backend").command("pull").description("Pull git-sync settings from Windmill backend to local wmill.yaml").option("--repository <repo:string>", "Specify repository path (e.g., u/user/repo)").option("--default", "Write settings to top-level defaults instead of overrides").option("--replace", "Replace existing settings (non-interactive mode)").option("--override", "Add branch-specific override (non-interactive mode)").option("--diff", "Show differences without applying changes").option("--json-output", "Output in JSON format").option("--with-backend-settings <json:string>", "Use provided JSON settings instead of querying backend (for testing)").option("--yes", "Skip interactive prompts and use default behavior").option("--promotion <branch:string>", "Use promotionOverrides from the specified branch instead of regular overrides").action(pullGitSyncSettings).command("push").description("Push git-sync settings from local wmill.yaml to Windmill backend").option("--repository <repo:string>", "Specify repository path (e.g., u/user/repo)").option("--diff", "Show what would be pushed without applying changes").option("--json-output", "Output in JSON format").option("--with-backend-settings <json:string>", "Use provided JSON settings instead of querying backend (for testing)").option("--yes", "Skip interactive prompts and use default behavior").option("--promotion <branch:string>", "Use promotionOverrides from the specified branch instead of regular overrides").action(pushGitSyncSettings);
76413
+ gitsync_settings_default = command23;
75934
76414
  });
75935
76415
 
75936
76416
  // src/main.ts
@@ -77207,8 +77687,8 @@ async function pull2(opts) {
77207
77687
  await pushResourceType(workspace.workspaceId, x.name + ".resource-type.json", undefined, x);
77208
77688
  }
77209
77689
  }
77210
- var command21 = new Command().name("hub").description("Hub related commands. EXPERIMENTAL. INTERNAL USE ONLY.").command("pull").description("pull any supported definitions. EXPERIMENTAL.").action(pull2);
77211
- var hub_default = command21;
77690
+ var command22 = new Command().name("hub").description("Hub related commands. EXPERIMENTAL. INTERNAL USE ONLY.").command("pull").description("pull any supported definitions. EXPERIMENTAL.").action(pull2);
77691
+ var hub_default = command22;
77212
77692
 
77213
77693
  // src/main.ts
77214
77694
  await __promiseAll([
@@ -77299,8 +77779,8 @@ async function pushWorkerGroups(opts) {
77299
77779
  await pushInstanceConfigs(opts, false);
77300
77780
  }
77301
77781
  }
77302
- var command23 = new Command().description("display worker groups, pull and push worker groups configs").action(displayWorkerGroups).command("pull").description("Pull worker groups (similar to `wmill instance pull --skip-users --skip-settings --skip-groups`)").option("--instance", "Name of the instance to push to, override the active instance").option("--base-url", "Base url to be passed to the instance settings instead of the local one").option("--yes", "Pull without needing confirmation").action(pullWorkerGroups).command("push").description("Push worker groups (similar to `wmill instance push --skip-users --skip-settings --skip-groups`)").option("--instance [instance]", "Name of the instance to push to, override the active instance").option("--base-url [baseUrl]", "If used with --token, will be used as the base url for the instance").option("--yes", "Push without needing confirmation").action(pushWorkerGroups);
77303
- var worker_groups_default = command23;
77782
+ var command24 = new Command().description("display worker groups, pull and push worker groups configs").action(displayWorkerGroups).command("pull").description("Pull worker groups (similar to `wmill instance pull --skip-users --skip-settings --skip-groups`)").option("--instance", "Name of the instance to push to, override the active instance").option("--base-url", "Base url to be passed to the instance settings instead of the local one").option("--yes", "Pull without needing confirmation").action(pullWorkerGroups).command("push").description("Push worker groups (similar to `wmill instance push --skip-users --skip-settings --skip-groups`)").option("--instance [instance]", "Name of the instance to push to, override the active instance").option("--base-url [baseUrl]", "If used with --token, will be used as the base url for the instance").option("--yes", "Push without needing confirmation").action(pushWorkerGroups);
77783
+ var worker_groups_default = command24;
77304
77784
 
77305
77785
  // src/main.ts
77306
77786
  await init_lint();
@@ -77329,7 +77809,7 @@ await __promiseAll([
77329
77809
  init_local_path_scripts()
77330
77810
  ]);
77331
77811
  var import_yaml40 = __toESM(require_dist(), 1);
77332
- import { sep as SEP20 } from "node:path";
77812
+ import { sep as SEP21 } from "node:path";
77333
77813
  import * as http3 from "node:http";
77334
77814
  import * as https from "node:https";
77335
77815
  import { access, readdir as readdir10, realpath, stat as stat17, unlink, writeFile as writeFile19 } from "node:fs/promises";
@@ -77582,7 +78062,7 @@ async function dev2(opts) {
77582
78062
  if (paths.length == 0) {
77583
78063
  return;
77584
78064
  }
77585
- const nativePath = (await realpath(paths[0])).replace(base + SEP20, "");
78065
+ const nativePath = (await realpath(paths[0])).replace(base + SEP21, "");
77586
78066
  const cpath = nativePath.replaceAll("\\", "/");
77587
78067
  const insideFlow = isInsideFlowFolder(cpath);
77588
78068
  if (insideFlow || !ignore(nativePath, false)) {
@@ -77599,7 +78079,7 @@ async function dev2(opts) {
77599
78079
  return;
77600
78080
  const wmFlowPath = stripFolderSuffix(localPath.replace(/\/$/, ""), FLOW_SUFFIXES);
77601
78081
  const localFlow = await yamlParseFile(localPath + "flow.yaml");
77602
- await replaceInlineScripts(localFlow.value.modules, async (path22) => await readTextFile(localPath + path22), exports_log, localPath, SEP20, undefined);
78082
+ await replaceInlineScripts(localFlow.value.modules, async (path22) => await readTextFile(localPath + path22), exports_log, localPath, SEP21, undefined);
77603
78083
  snapshotPathScripts(localFlow.value);
77604
78084
  const localScriptReader = createPreviewLocalScriptReader({
77605
78085
  exts,
@@ -77652,7 +78132,7 @@ async function dev2(opts) {
77652
78132
  if (!flowDir || !flowYaml)
77653
78133
  throw new Error("not a flow");
77654
78134
  const localFlow = await yamlParseFile(flowYaml);
77655
- await replaceInlineScripts(localFlow.value.modules, async (p) => await readTextFile(flowDir + p), exports_log, flowDir, SEP20, undefined);
78135
+ await replaceInlineScripts(localFlow.value.modules, async (p) => await readTextFile(flowDir + p), exports_log, flowDir, SEP21, undefined);
77656
78136
  snapshotPathScripts(localFlow.value);
77657
78137
  const localScriptReader = createPreviewLocalScriptReader({
77658
78138
  exts,
@@ -77999,8 +78479,8 @@ async function dev2(opts) {
77999
78479
  await Promise.all([startServer(), watchChanges()]);
78000
78480
  console.log("Stopped dev mode");
78001
78481
  }
78002
- var command24 = new Command().description("Watch local file changes and live-reload the dev page for preview. Does NOT deploy to the remote workspace — use wmill sync push for that.").option("--includes <pattern...:string>", "Filter paths given a glob pattern or path").option("--proxy-port <port:number>", "Port for a localhost reverse proxy to the remote Windmill server").option("--path <path:string>", "Watch a specific windmill path (e.g., u/admin/my_script or f/my_flow)").option("--no-open", "Do not open the browser automatically").action(dev2);
78003
- var dev_default2 = command24;
78482
+ var command25 = new Command().description("Watch local file changes and live-reload the dev page for preview. Does NOT deploy to the remote workspace — use wmill sync push for that.").option("--includes <pattern...:string>", "Filter paths given a glob pattern or path").option("--proxy-port <port:number>", "Port for a localhost reverse proxy to the remote Windmill server").option("--path <path:string>", "Watch a specific windmill path (e.g., u/admin/my_script or f/my_flow)").option("--no-open", "Do not open the browser automatically").action(dev2);
78483
+ var dev_default2 = command25;
78004
78484
 
78005
78485
  // src/main.ts
78006
78486
  init_gen();
@@ -78114,8 +78594,8 @@ Worker Group: ${group.groupName} (${group.workers.length} workers)`);
78114
78594
  info("Use 'wmill instance add' to add a new instance");
78115
78595
  }
78116
78596
  }
78117
- var command25 = new Command().description("List all workers grouped by worker groups").option("--instance [instance]", "Name of the instance to push to, override the active instance").option("--base-url [baseUrl]", "If used with --token, will be used as the base url for the instance").action(displayWorkers);
78118
- var workers_default = command25;
78597
+ var command26 = new Command().description("List all workers grouped by worker groups").option("--instance [instance]", "Name of the instance to push to, override the active instance").option("--base-url [baseUrl]", "If used with --token, will be used as the base url for the instance").action(displayWorkers);
78598
+ var workers_default = command26;
78119
78599
 
78120
78600
  // src/commands/queues/queues.ts
78121
78601
  init_mod3();
@@ -78223,8 +78703,8 @@ async function displayQueues(opts, workspace) {
78223
78703
  info("Use 'wmill instance add' to add a new instance");
78224
78704
  }
78225
78705
  }
78226
- var command26 = new Command().description("List all queues with their metrics").arguments("[workspace:string] the optional workspace to filter by (default to all workspaces)").option("--instance [instance]", "Name of the instance to push to, override the active instance").option("--base-url [baseUrl]", "If used with --token, will be used as the base url for the instance").action(displayQueues);
78227
- var queues_default = command26;
78706
+ var command27 = new Command().description("List all queues with their metrics").arguments("[workspace:string] the optional workspace to filter by (default to all workspaces)").option("--instance [instance]", "Name of the instance to push to, override the active instance").option("--base-url [baseUrl]", "If used with --token, will be used as the base url for the instance").action(displayQueues);
78707
+ var queues_default = command27;
78228
78708
 
78229
78709
  // src/main.ts
78230
78710
  await init_dependencies();
@@ -78276,7 +78756,7 @@ When a new app needs to be created, YOU run \`wmill app new\` yourself with \`--
78276
78756
 
78277
78757
  ## Triggers
78278
78758
 
78279
- You MUST use the \`triggers\` skill to configure HTTP routes, WebSocket, Kafka, NATS, SQS, MQTT, GCP, or Postgres CDC triggers.
78759
+ You MUST use the \`triggers\` skill to configure HTTP routes, WebSocket, Kafka, NATS, SQS, MQTT, GCP, Azure, Email, or Postgres CDC triggers.
78280
78760
 
78281
78761
  ## Schedules
78282
78762
 
@@ -83871,6 +84351,49 @@ Examples:
83871
84351
  - \`u/user/webhook.http_trigger.yaml\`
83872
84352
  - \`f/data/kafka_consumer.kafka_trigger.yaml\`
83873
84353
  - \`f/sync/postgres_cdc.postgres_trigger.yaml\`
84354
+ - \`f/inbound/orders.email_trigger.yaml\`
84355
+
84356
+ ## Email Triggers
84357
+
84358
+ An email trigger routes incoming emails to a script or flow. Each trigger reserves a local-part: emails sent to \`<local_part>@<windmill_email_domain>\` are delivered to the configured runnable. Set \`workspaced_local_part: true\` to namespace it per workspace (the actual recipient becomes \`<workspace_id>-<local_part>@…\`); on Windmill Cloud this is required.
84359
+
84360
+ Senders may append URL-style extras to the local-part with \`+\`: \`mytrigger+foo=bar+baz=qux@…\`. They flow through to the script as \`email_extra_args\` (see below).
84361
+
84362
+ ### Payload
84363
+
84364
+ The runnable receives:
84365
+
84366
+ - \`parsed_email\` — \`{ headers, text_body, html_body, attachments[] }\`. Each \`attachment\` has \`{ headers, body }\`.
84367
+ - \`raw_email\` — the raw RFC 822 message as a string, **or** an S3 object (\`{ s3: "windmill_emails/<job_id>/raw.eml" }\`) if the message exceeds 1 MiB.
84368
+ - \`email_extra_args\` (optional, only when sender appended \`+key=value\` extras) — a flat object of the parsed extras.
84369
+
84370
+ With a preprocessor, all of the above are nested under \`event\` along with \`event.kind = "email"\` and \`event.trigger_path\` (the trigger's path). Without a preprocessor, \`trigger_path\` is **not** exposed — add a preprocessor if you need it.
84371
+
84372
+ ### Attachments are S3 objects
84373
+
84374
+ Binary attachments are uploaded to the workspace S3 bucket and surface in \`parsed_email.attachments[i].body\` as:
84375
+
84376
+ \`\`\`json
84377
+ { "s3": "windmill_emails/<job_id>/attachments/<filename>" }
84378
+ \`\`\`
84379
+
84380
+ To read the bytes inside a script, use the wmill SDK:
84381
+
84382
+ \`\`\`ts
84383
+ // TypeScript
84384
+ import * as wmill from "windmill-client"
84385
+ const file = await wmill.loadS3File(parsed_email.attachments[0].body)
84386
+ \`\`\`
84387
+
84388
+ \`\`\`python
84389
+ # Python
84390
+ import wmill
84391
+ data = wmill.load_s3_file(parsed_email["attachments"][0]["body"])
84392
+ \`\`\`
84393
+
84394
+ If the workspace has no S3 resource configured (Workspace Settings → Object storage), \`body\` falls back to the string \`"configure s3 in the workspace settings to handle attachments"\`. The same applies to large \`raw_email\` bodies. Email attachment storage requires the server to be built with the \`parquet\` feature.
84395
+
84396
+ Text/HTML/inline parts are placed inline in \`body\` as strings.
83874
84397
 
83875
84398
  ## CLI Commands
83876
84399
 
@@ -84804,6 +85327,15 @@ Generate metadata (locks, schemas) for all scripts, flows, and apps
84804
85327
  - \`-i --includes <patterns:file[]>\` - Comma separated patterns to specify which files to include
84805
85328
  - \`-e --excludes <patterns:file[]>\` - Comma separated patterns to specify which files to exclude
84806
85329
 
85330
+ **Subcommands:**
85331
+
85332
+ - \`generate-metadata rehash [folder:string]\`
85333
+ - \`--skip-scripts\` - Skip processing scripts
85334
+ - \`--skip-flows\` - Skip processing flows
85335
+ - \`--skip-apps\` - Skip processing apps
85336
+ - \`-i --includes <patterns:file[]>\` - Comma separated patterns to specify which files to include
85337
+ - \`-e --excludes <patterns:file[]>\` - Comma separated patterns to specify which files to exclude
85338
+
84807
85339
  ### gitsync-settings
84808
85340
 
84809
85341
  Manage git-sync settings between local wmill.yaml and Windmill backend
@@ -85477,6 +86009,71 @@ required:
85477
86009
  - azure_mode
85478
86010
  - scope_resource_id
85479
86011
  - subscription_name
86012
+ `,
86013
+ email_trigger: `type: object
86014
+ properties:
86015
+ script_path:
86016
+ type: string
86017
+ description: Path to the script or flow to execute when triggered
86018
+ permissioned_as:
86019
+ type: string
86020
+ description: The user or group this trigger runs as (permissioned_as)
86021
+ is_flow:
86022
+ type: boolean
86023
+ description: True if script_path points to a flow, false if it points to a script
86024
+ labels:
86025
+ type: array
86026
+ items:
86027
+ type: string
86028
+ local_part:
86029
+ type: string
86030
+ workspaced_local_part:
86031
+ type: boolean
86032
+ error_handler_path:
86033
+ type: string
86034
+ error_handler_args:
86035
+ type: object
86036
+ description: The arguments to pass to the script or flow
86037
+ retry:
86038
+ type: object
86039
+ properties:
86040
+ constant:
86041
+ type: object
86042
+ description: Retry with constant delay between attempts
86043
+ properties:
86044
+ attempts:
86045
+ type: integer
86046
+ description: Number of retry attempts
86047
+ seconds:
86048
+ type: integer
86049
+ description: Seconds to wait between retries
86050
+ exponential:
86051
+ type: object
86052
+ description: Retry with exponential backoff (delay doubles each time)
86053
+ properties:
86054
+ attempts:
86055
+ type: integer
86056
+ description: Number of retry attempts
86057
+ multiplier:
86058
+ type: integer
86059
+ description: Multiplier for exponential backoff
86060
+ seconds:
86061
+ type: integer
86062
+ minimum: 1
86063
+ description: Initial delay in seconds
86064
+ random_factor:
86065
+ type: integer
86066
+ minimum: 0
86067
+ maximum: 100
86068
+ description: Random jitter percentage (0-100) to avoid thundering herd
86069
+ retry_if:
86070
+ $ref: '#/components/schemas/RetryIf'
86071
+ description: Retry configuration for failed module executions
86072
+ required:
86073
+ - script_path
86074
+ - permissioned_as
86075
+ - is_flow
86076
+ - local_part
85480
86077
  `,
85481
86078
  gcp_trigger: `type: object
85482
86079
  properties:
@@ -86404,7 +87001,8 @@ var SCHEMA_MAPPINGS = {
86404
87001
  { name: "MqttTrigger", schemaKey: "mqtt_trigger", filePattern: "*.mqtt_trigger.yaml" },
86405
87002
  { name: "SqsTrigger", schemaKey: "sqs_trigger", filePattern: "*.sqs_trigger.yaml" },
86406
87003
  { name: "GcpTrigger", schemaKey: "gcp_trigger", filePattern: "*.gcp_trigger.yaml" },
86407
- { name: "AzureTrigger", schemaKey: "azure_trigger", filePattern: "*.azure_trigger.yaml" }
87004
+ { name: "AzureTrigger", schemaKey: "azure_trigger", filePattern: "*.azure_trigger.yaml" },
87005
+ { name: "EmailTrigger", schemaKey: "email_trigger", filePattern: "*.email_trigger.yaml" }
86408
87006
  ],
86409
87007
  schedules: [
86410
87008
  { name: "Schedule", schemaKey: "schedule", filePattern: "*.schedule.yaml" }
@@ -87225,8 +87823,8 @@ async function initAction(opts) {
87225
87823
  info(colors.gray("Skipped resource type namespace generation (no workspace bound). Run 'wmill workspace bind' then 'wmill init' to generate it."));
87226
87824
  }
87227
87825
  }
87228
- var command27 = new Command().description("Bootstrap a windmill project with a wmill.yaml file").option("--use-default", "Use default settings without checking backend").option("--use-backend", "Use backend git-sync settings if available").option("--repository <repo:string>", "Specify repository path (e.g., u/user/repo) when using backend settings").option("--bind-profile", "Automatically bind active workspace profile to current Git branch").option("--no-bind-profile", "Skip workspace profile binding prompt").action(initAction);
87229
- var init_default = command27;
87826
+ var command28 = new Command().description("Bootstrap a windmill project with a wmill.yaml file").option("--use-default", "Use default settings without checking backend").option("--use-backend", "Use backend git-sync settings if available").option("--repository <repo:string>", "Specify repository path (e.g., u/user/repo) when using backend settings").option("--bind-profile", "Automatically bind active workspace profile to current Git branch").option("--no-bind-profile", "Skip workspace profile binding prompt").action(initAction);
87827
+ var init_default = command28;
87230
87828
 
87231
87829
  // src/commands/jobs/jobs.ts
87232
87830
  init_mod3();
@@ -87392,8 +87990,8 @@ Warning: Found ${workers.length} active worker(s) on the instance.`));
87392
87990
  }
87393
87991
  var pull3 = new Command().description("Pull completed and queued jobs from workspace").option("-c, --completed-output <file:string>", "Completed jobs output file (default: completed_jobs.json)").option("-q, --queued-output <file:string>", "Queued jobs output file (default: queued_jobs.json)").option("--skip-worker-check", "Skip checking for active workers before export").arguments("[workspace:string]").action(pullJobs);
87394
87992
  var push12 = new Command().description("Push completed and queued jobs to workspace").option("-c, --completed-file <file:string>", "Completed jobs input file (default: completed_jobs.json)").option("-q, --queued-file <file:string>", "Queued jobs input file (default: queued_jobs.json)").option("--skip-worker-check", "Skip checking for active workers before import").arguments("[workspace:string]").action(pushJobs);
87395
- var command28 = new Command().description("Manage jobs (import/export)").command("pull", pull3).command("push", push12);
87396
- var jobs_default = command28;
87993
+ var command29 = new Command().description("Manage jobs (import/export)").command("pull", pull3).command("push", push12);
87994
+ var jobs_default = command29;
87397
87995
 
87398
87996
  // src/commands/job/job.ts
87399
87997
  init_mod3();
@@ -87707,8 +88305,8 @@ async function cancel(opts, id) {
87707
88305
  info(colors.green(`Job ${id} canceled.`));
87708
88306
  }
87709
88307
  var listOptions = (cmd) => cmd.option("--json", "Output as JSON (for piping to jq)").option("--script-path <scriptPath:string>", "Filter by exact script/flow path").option("--created-by <createdBy:string>", "Filter by creator username").option("--running", "Show only running jobs").option("--failed", "Show only failed jobs").option("--success <success:boolean>", "Filter by success status (true/false)").option("--limit <limit:number>", "Number of jobs to return (default 30, max 100)").option("--job-kinds <jobKinds:string>", "Filter by job kinds (default: script,flow,singlestepflow)").option("--label <label:string>", "Filter by job label").option("--all", "Include sub-jobs (flow steps). By default only top-level jobs are shown").option("--parent <parent:string>", "Filter by parent job ID (show sub-jobs of a specific flow)").option("--is-flow-step", "Show only flow step jobs");
87710
- var command29 = listOptions(new Command().description("Manage jobs (list, inspect, cancel)")).action(list13).command("list", listOptions(new Command().description("List recent jobs"))).action(list13).command("get", "Get job details. For flows: shows step tree with sub-job IDs").arguments("<id:string>").option("--json", "Output as JSON (for piping to jq)").action(get10).command("result", "Get the result of a completed job (machine-friendly)").arguments("<id:string>").action(result).command("logs", "Get job logs. For flows: aggregates all step logs").arguments("<id:string>").action(logs).command("cancel", "Cancel a running or queued job").arguments("<id:string>").option("--reason <reason:string>", "Reason for cancellation").action(cancel);
87711
- var job_default = command29;
88308
+ var command30 = listOptions(new Command().description("Manage jobs (list, inspect, cancel)")).action(list13).command("list", listOptions(new Command().description("List recent jobs"))).action(list13).command("get", "Get job details. For flows: shows step tree with sub-job IDs").arguments("<id:string>").option("--json", "Output as JSON (for piping to jq)").action(get10).command("result", "Get the result of a completed job (machine-friendly)").arguments("<id:string>").action(result).command("logs", "Get job logs. For flows: aggregates all step logs").arguments("<id:string>").action(logs).command("cancel", "Cancel a running or queued job").arguments("<id:string>").option("--reason <reason:string>", "Reason for cancellation").action(cancel);
88309
+ var job_default = command30;
87712
88310
 
87713
88311
  // src/commands/group/group.ts
87714
88312
  init_mod3();
@@ -87807,8 +88405,8 @@ async function removeUser(opts, name, username) {
87807
88405
  });
87808
88406
  info(colors.green(`User '${username}' removed from group '${name}'.`));
87809
88407
  }
87810
- var command30 = new Command().description("Manage workspace groups").option("--json", "Output as JSON (for piping to jq)").action(list14).command("list", "List all groups in the workspace").option("--json", "Output as JSON (for piping to jq)").action(list14).command("get", "Get group details and members").arguments("<name:string>").option("--json", "Output as JSON (for piping to jq)").action(get11).command("create", "Create a new group").arguments("<name:string>").option("--summary <summary:string>", "Group summary/description").action(create).command("delete", "Delete a group").arguments("<name:string>").action(deleteGroup2).command("add-user", "Add a user to a group").arguments("<name:string> <username:string>").action(addUser2).command("remove-user", "Remove a user from a group").arguments("<name:string> <username:string>").action(removeUser);
87811
- var group_default = command30;
88408
+ var command31 = new Command().description("Manage workspace groups").option("--json", "Output as JSON (for piping to jq)").action(list14).command("list", "List all groups in the workspace").option("--json", "Output as JSON (for piping to jq)").action(list14).command("get", "Get group details and members").arguments("<name:string>").option("--json", "Output as JSON (for piping to jq)").action(get11).command("create", "Create a new group").arguments("<name:string>").option("--summary <summary:string>", "Group summary/description").action(create).command("delete", "Delete a group").arguments("<name:string>").action(deleteGroup2).command("add-user", "Add a user to a group").arguments("<name:string> <username:string>").action(addUser2).command("remove-user", "Remove a user from a group").arguments("<name:string> <username:string>").action(removeUser);
88409
+ var group_default = command31;
87812
88410
 
87813
88411
  // src/commands/audit/audit.ts
87814
88412
  init_mod3();
@@ -87885,8 +88483,8 @@ async function get12(opts, id) {
87885
88483
  }
87886
88484
  }
87887
88485
  var auditListOptions = (cmd) => cmd.option("--json", "Output as JSON (for piping to jq)").option("--username <username:string>", "Filter by username").option("--operation <operation:string>", "Filter by operation (exact or prefix)").option("--action-kind <actionKind:string>", "Filter by action kind (Create, Update, Delete, Execute)").option("--before <before:string>", "Filter events before this timestamp").option("--after <after:string>", "Filter events after this timestamp").option("--limit <limit:number>", "Number of entries to return (default 30, max 100)");
87888
- var command31 = auditListOptions(new Command().description("View audit logs (requires admin)")).action(list15).command("list", auditListOptions(new Command().description("List audit log entries"))).action(list15).command("get", "Get a specific audit log entry").arguments("<id:string>").option("--json", "Output as JSON (for piping to jq)").action(get12);
87889
- var audit_default = command31;
88486
+ var command32 = auditListOptions(new Command().description("View audit logs (requires admin)")).action(list15).command("list", auditListOptions(new Command().description("List audit log entries"))).action(list15).command("get", "Get a specific audit log entry").arguments("<id:string>").option("--json", "Output as JSON (for piping to jq)").action(get12);
88487
+ var audit_default = command32;
87890
88488
 
87891
88489
  // src/commands/token/token.ts
87892
88490
  init_mod3();
@@ -87940,253 +88538,11 @@ async function deleteToken2(opts, tokenPrefix) {
87940
88538
  await deleteToken({ tokenPrefix });
87941
88539
  info(colors.green(`Token with prefix '${tokenPrefix}' deleted.`));
87942
88540
  }
87943
- var command32 = new Command().description("Manage API tokens").option("--json", "Output as JSON (for piping to jq)").action(list16).command("list", "List API tokens").option("--json", "Output as JSON (for piping to jq)").action(list16).command("create", "Create a new API token").option("--label <label:string>", "Token label").option("--expiration <expiration:string>", "Token expiration (ISO 8601 timestamp)").action(create2).command("delete", "Delete a token by its prefix").arguments("<token_prefix:string>").action(deleteToken2);
87944
- var token_default = command32;
88541
+ var command33 = new Command().description("Manage API tokens").option("--json", "Output as JSON (for piping to jq)").action(list16).command("list", "List API tokens").option("--json", "Output as JSON (for piping to jq)").action(list16).command("create", "Create a new API token").option("--label <label:string>", "Token label").option("--expiration <expiration:string>", "Token expiration (ISO 8601 timestamp)").action(create2).command("delete", "Delete a token by its prefix").arguments("<token_prefix:string>").action(deleteToken2);
88542
+ var token_default = command33;
87945
88543
 
87946
- // src/commands/generate-metadata/generate-metadata.ts
87947
- init_mod3();
87948
- init_colors2();
87949
- init_log();
87950
- await __promiseAll([
87951
- init_confirm(),
87952
- init_conf(),
87953
- init_context(),
87954
- init_auth(),
87955
- init_metadata(),
87956
- init_flow_metadata(),
87957
- init_app_metadata(),
87958
- init_sync(),
87959
- init_script(),
87960
- init_resource_folders(),
87961
- init_codebase(),
87962
- init_dependency_tree()
87963
- ]);
87964
- import { sep as SEP21 } from "node:path";
87965
- async function generateMetadata2(opts, folder) {
87966
- if (folder === "") {
87967
- folder = undefined;
87968
- }
87969
- const workspace = await resolveWorkspace(opts);
87970
- await requireLogin(opts);
87971
- opts = await mergeConfigWithConfigFile(opts);
87972
- const rawWorkspaceDependencies = await getRawWorkspaceDependencies(false);
87973
- const codebases = await listSyncCodebases(opts);
87974
- const ignore = await ignoreF(opts);
87975
- const skipScripts = opts.skipScripts ?? false;
87976
- const skipFlows = opts.skipFlows ?? opts.schemaOnly ?? false;
87977
- const skipApps = opts.skipApps ?? opts.schemaOnly ?? false;
87978
- const checking = [];
87979
- if (!skipScripts)
87980
- checking.push("scripts");
87981
- if (!skipFlows)
87982
- checking.push("flows");
87983
- if (!skipApps)
87984
- checking.push("apps");
87985
- if (checking.length === 0) {
87986
- info(colors.yellow("Nothing to check (all types skipped)"));
87987
- return;
87988
- }
87989
- info(`Checking ${checking.join(", ")}...`);
87990
- const tree = new DoubleLinkedDependencyTree;
87991
- tree.setWorkspaceDeps(rawWorkspaceDependencies);
87992
- if (!skipScripts) {
87993
- const scriptElems = await elementsToMap(await FSFSElement(process.cwd(), codebases, false), (p, isD) => {
87994
- return !isD && !exts.some((ext2) => p.endsWith(ext2)) || ignore(p, isD) || isFolderResourcePathAnyFormat(p) || isScriptModulePath(p) && !isModuleEntryPoint(p);
87995
- }, false, {});
87996
- for (const e of Object.keys(scriptElems)) {
87997
- await generateScriptMetadataInternal(e, workspace, opts, true, true, rawWorkspaceDependencies, codebases, false, tree);
87998
- }
87999
- }
88000
- if (!skipFlows) {
88001
- const flowElems = Object.keys(await elementsToMap(await FSFSElement(process.cwd(), [], true), (p, isD) => {
88002
- return ignore(p, isD) || !isD && !p.endsWith(SEP21 + "flow.yaml") && !p.endsWith(SEP21 + "flow.json");
88003
- }, false, {})).map((x) => x.substring(0, x.lastIndexOf(SEP21)));
88004
- for (const flowFolder of flowElems) {
88005
- await generateFlowLockInternal(flowFolder, true, workspace, opts, false, true, tree);
88006
- }
88007
- }
88008
- if (!skipApps) {
88009
- const elems = await elementsToMap(await FSFSElement(process.cwd(), [], true), (p, isD) => {
88010
- return ignore(p, isD) || !isD && !p.endsWith(SEP21 + "raw_app.yaml") && !p.endsWith(SEP21 + "app.yaml");
88011
- }, false, {});
88012
- const rawAppFolders = getAppFolders(elems, "raw_app.yaml");
88013
- const appFolders = getAppFolders(elems, "app.yaml");
88014
- for (const appFolder of rawAppFolders) {
88015
- await generateAppLocksInternal(appFolder, true, true, workspace, opts, false, true, tree);
88016
- }
88017
- for (const appFolder of appFolders) {
88018
- await generateAppLocksInternal(appFolder, false, true, workspace, opts, false, true, tree);
88019
- }
88020
- }
88021
- tree.propagateStaleness();
88022
- try {
88023
- await uploadScripts(tree, workspace);
88024
- } catch (e) {
88025
- 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.`));
88026
- }
88027
- const staleItems = [];
88028
- const seenFolders = new Set;
88029
- for (const p of tree.allPaths()) {
88030
- const staleReason = tree.getStaleReason(p);
88031
- if (!staleReason)
88032
- continue;
88033
- const itemType = tree.getItemType(p);
88034
- const itemFolder = tree.getFolder(p);
88035
- if (itemType === "dependencies") {
88036
- staleItems.push({ type: itemType, path: p, folder: itemFolder, staleReason });
88037
- } else if (itemType === "inline_script") {
88038
- continue;
88039
- } else if (itemType === "script") {
88040
- const originalPath = tree.getOriginalPath(p);
88041
- staleItems.push({ type: itemType, path: originalPath, folder: itemFolder, staleReason });
88042
- } else if (!seenFolders.has(itemFolder)) {
88043
- seenFolders.add(itemFolder);
88044
- const originalPath = tree.getOriginalPath(p);
88045
- staleItems.push({ type: itemType, path: originalPath, folder: itemFolder, isRawApp: tree.getIsRawApp(p), staleReason });
88046
- }
88047
- }
88048
- let filteredItems = staleItems;
88049
- if (folder) {
88050
- folder = folder.replaceAll("\\", "/");
88051
- if (folder.endsWith("/")) {
88052
- folder = folder.substring(0, folder.length - 1);
88053
- }
88054
- const folderNoExt = folder.replace(/\.[^/.]+$/, "");
88055
- const isInsideFolder = (item) => {
88056
- const normalizedFolder = item.folder.replaceAll("\\", "/");
88057
- const normalizedPath = item.path.replaceAll("\\", "/");
88058
- return normalizedFolder === folder || normalizedFolder.startsWith(folder + "/") || normalizedPath === folder || normalizedPath === folderNoExt;
88059
- };
88060
- const isPathInFolder = (p) => p.startsWith(folder + "/") || p === folder || p === folderNoExt;
88061
- const touchesFolder = (treePath) => {
88062
- if (isPathInFolder(treePath))
88063
- return true;
88064
- let found = false;
88065
- tree.traverseTransitive(treePath, (importPath) => {
88066
- if (isPathInFolder(importPath)) {
88067
- found = true;
88068
- return true;
88069
- }
88070
- });
88071
- return found;
88072
- };
88073
- const isRelevant = (item) => {
88074
- if (isInsideFolder(item))
88075
- return true;
88076
- if (item.type === "dependencies")
88077
- return true;
88078
- const treePath = (item.type === "script" ? item.path.replace(/\.[^/.]+$/, "") : item.folder).replaceAll("\\", "/");
88079
- return touchesFolder(treePath);
88080
- };
88081
- if (opts.strictFolderBoundaries) {
88082
- filteredItems = staleItems.filter(isInsideFolder);
88083
- const excludedStale = staleItems.filter((item) => !isInsideFolder(item) && isRelevant(item) && item.type !== "dependencies");
88084
- for (const item of excludedStale) {
88085
- const normalizedPath = item.path.replaceAll("\\", "/");
88086
- 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.`));
88087
- }
88088
- } else {
88089
- filteredItems = staleItems.filter(isRelevant);
88090
- }
88091
- }
88092
- if (filteredItems.length === 0) {
88093
- info(colors.green("All metadata up-to-date"));
88094
- return;
88095
- }
88096
- const scripts = filteredItems.filter((i) => i.type === "script");
88097
- const flows = filteredItems.filter((i) => i.type === "flow");
88098
- const apps2 = filteredItems.filter((i) => i.type === "app");
88099
- const deps = filteredItems.filter((i) => i.type === "dependencies");
88100
- info("");
88101
- info(`Found ${colors.bold(String(filteredItems.length))} item(s) with stale metadata:`);
88102
- const printItems = (label, items) => {
88103
- if (items.length === 0)
88104
- return;
88105
- info(` ${label} (${items.length}):`);
88106
- for (const item of items) {
88107
- const reason = item.staleReason ? colors.dim(colors.white(` — ${item.staleReason}`)) : "";
88108
- info(` ~ ${item.path}` + reason);
88109
- }
88110
- };
88111
- printItems("Workspace dependencies", deps);
88112
- printItems("Scripts", scripts);
88113
- printItems("Flows", flows);
88114
- printItems("Apps", apps2);
88115
- if (opts.dryRun) {
88116
- return;
88117
- }
88118
- info("");
88119
- const isInteractive = process.stdin.isTTY ?? false;
88120
- if (!opts.yes && isInteractive && !await Confirm.prompt({
88121
- message: "Update metadata?",
88122
- default: true
88123
- })) {
88124
- return;
88125
- }
88126
- info("");
88127
- const mismatchedWorkspaceDeps = tree.getMismatchedWorkspaceDeps();
88128
- const total = filteredItems.length - deps.length;
88129
- const maxWidth = `[${total}/${total}]`.length;
88130
- let current = 0;
88131
- const formatProgress = (n) => {
88132
- return colors.dim(colors.white(`[${n}/${total}]`.padEnd(maxWidth, " ")));
88133
- };
88134
- const errors = [];
88135
- for (const item of scripts) {
88136
- current++;
88137
- info(`${formatProgress(current)} script ${item.path}`);
88138
- try {
88139
- await generateScriptMetadataInternal(item.path, workspace, opts, false, true, mismatchedWorkspaceDeps, codebases, false, tree);
88140
- } catch (e) {
88141
- const msg = e instanceof Error ? e.message : String(e);
88142
- errors.push({ path: item.path, error: msg });
88143
- error(` Failed: ${msg}`);
88144
- }
88145
- }
88146
- for (const item of flows) {
88147
- current++;
88148
- try {
88149
- const result2 = await generateFlowLockInternal(item.folder.replaceAll("/", SEP21), false, workspace, opts, false, true, tree);
88150
- const flowResult = result2;
88151
- const scriptsInfo = flowResult?.updatedScripts?.length ? colors.dim(colors.white(`: ${flowResult.updatedScripts.join(", ")}`)) : "";
88152
- info(`${formatProgress(current)} flow ${item.path}${scriptsInfo}`);
88153
- } catch (e) {
88154
- const msg = e instanceof Error ? e.message : String(e);
88155
- errors.push({ path: item.path, error: msg });
88156
- info(`${formatProgress(current)} flow ${item.path}`);
88157
- error(` Failed: ${msg}`);
88158
- }
88159
- }
88160
- for (const item of apps2) {
88161
- current++;
88162
- try {
88163
- const result2 = await generateAppLocksInternal(item.folder.replaceAll("/", SEP21), item.isRawApp, false, workspace, opts, false, true, tree);
88164
- const appResult = result2;
88165
- const scriptsInfo = appResult?.updatedScripts?.length ? colors.dim(colors.white(`: ${appResult.updatedScripts.join(", ")}`)) : "";
88166
- info(`${formatProgress(current)} app ${item.path}${scriptsInfo}`);
88167
- } catch (e) {
88168
- const msg = e instanceof Error ? e.message : String(e);
88169
- errors.push({ path: item.path, error: msg });
88170
- info(`${formatProgress(current)} app ${item.path}`);
88171
- error(` Failed: ${msg}`);
88172
- }
88173
- }
88174
- const allStaleDeps = staleItems.filter((i) => i.type === "dependencies");
88175
- await tree.persistDepsHashes(allStaleDeps.map((d) => d.path));
88176
- const succeeded = total - errors.length;
88177
- info("");
88178
- if (errors.length > 0) {
88179
- info(`Done. Updated ${colors.bold(String(succeeded))}/${total} item(s). ${colors.red(String(errors.length) + " failed")}:`);
88180
- for (const { path: path22, error: error2 } of errors) {
88181
- error(` ${path22}: ${error2}`);
88182
- }
88183
- process.exitCode = 1;
88184
- } else {
88185
- info(`Done. Updated ${colors.bold(String(total))} item(s).`);
88186
- }
88187
- }
88188
- var command33 = 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);
88189
- var generate_metadata_default = command33;
88544
+ // src/main.ts
88545
+ await init_generate_metadata();
88190
88546
 
88191
88547
  // src/commands/docs/docs.ts
88192
88548
  init_mod3();