windmill-cli 1.693.4 → 1.694.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/esm/main.js +1149 -738
  2. package/package.json +1 -1
package/esm/main.js CHANGED
@@ -16710,7 +16710,7 @@ var init_OpenAPI = __esm(() => {
16710
16710
  PASSWORD: undefined,
16711
16711
  TOKEN: getEnv3("WM_TOKEN"),
16712
16712
  USERNAME: undefined,
16713
- VERSION: "1.693.4",
16713
+ VERSION: "1.694.0",
16714
16714
  WITH_CREDENTIALS: true,
16715
16715
  interceptors: {
16716
16716
  request: new Interceptors,
@@ -25100,6 +25100,24 @@ async function requireLogin(opts) {
25100
25100
  throw new Error(`Network error: Could not connect to Windmill server at ${workspace.remote}`);
25101
25101
  }
25102
25102
  if (opts.token || opts.baseUrl) {
25103
+ const isApiError = error2 && typeof error2 === "object" && "name" in error2 && error2.name === "ApiError";
25104
+ if (isApiError) {
25105
+ const status = error2.status;
25106
+ const body = error2.body;
25107
+ let bodyStr = typeof body === "object" && body !== null ? JSON.stringify(body) : String(body ?? "").trim();
25108
+ bodyStr = bodyStr.replace(/\s*[@(]\w+\.rs:\d+[:\d]*\)?/g, "");
25109
+ bodyStr = bodyStr.replace(/^(Permission denied|Not authorized): /, "");
25110
+ if (status === 403) {
25111
+ info(colors.red(`Permission denied: the token is valid but lacks the required scope.${bodyStr ? `
25112
+ ${bodyStr}` : ""}`));
25113
+ } else if (status === 401) {
25114
+ info(colors.red(`Could not authenticate with the provided credentials. Please check your --token and --base-url and try again.${bodyStr ? `
25115
+ ${bodyStr}` : ""}`));
25116
+ } else {
25117
+ info(colors.red(`Request failed (${status ?? "unknown"}): ${bodyStr}`));
25118
+ }
25119
+ return process.exit(1);
25120
+ }
25103
25121
  info(colors.red("Could not authenticate with the provided credentials. Please check your --token and --base-url and try again."));
25104
25122
  return process.exit(1);
25105
25123
  }
@@ -26462,6 +26480,9 @@ function getScriptBasePathFromModulePath(p) {
26462
26480
  return;
26463
26481
  return norm.slice(0, idx);
26464
26482
  }
26483
+ function scriptPathToRemotePath(p) {
26484
+ return (isModuleEntryPoint(p) ? getScriptBasePathFromModulePath(p) : p.substring(0, p.indexOf("."))).replaceAll(SEP2, "/");
26485
+ }
26465
26486
  function getDeleteSuffix(type, format6) {
26466
26487
  return getFolderSuffixes()[type] + "/" + METADATA_FILES[type][format6];
26467
26488
  }
@@ -61660,7 +61681,7 @@ async function handleFile(path8, workspace, alreadySynced, message, opts, rawWor
61660
61681
  }
61661
61682
  debug(`Processing local script ${path8}`);
61662
61683
  alreadySynced.push(path8);
61663
- const remotePath = moduleEntryPoint ? getScriptBasePathFromModulePath(path8).replaceAll(SEP4, "/") : path8.substring(0, path8.indexOf(".")).replaceAll(SEP4, "/");
61684
+ const remotePath = scriptPathToRemotePath(path8);
61664
61685
  const language = inferContentTypeFromFilePath(path8, opts?.defaultTs);
61665
61686
  const codebase = language == "bun" ? findCodebase(path8, codebases) : undefined;
61666
61687
  let bundleContent = undefined;
@@ -63593,8 +63614,11 @@ function getLanguageFromExtension(ext2, defaultTs = "bun") {
63593
63614
  }
63594
63615
  return;
63595
63616
  }
63596
- function sanitizeForFilesystem(summary) {
63597
- const name = summary.replaceAll(" ", "_").replace(/[/\\:*?"<>|\x00-\x1f\x7f]/g, "").replace(/_+/g, "_").replace(/^[._]+|[._]+$/g, "");
63617
+ function sanitizeForFilesystem(summary, options) {
63618
+ let name = summary.replaceAll(" ", "_").replace(/[/\\:*?"<>|\x00-\x1f\x7f]/g, "").replace(/_+/g, "_").replace(/^[._]+|[._]+$/g, "");
63619
+ if (!options?.preserveCase) {
63620
+ name = name.toLowerCase();
63621
+ }
63598
63622
  return WINDOWS_RESERVED.test(name.toLowerCase()) ? `_${name}` : name;
63599
63623
  }
63600
63624
  function newPathAssigner(defaultTs, options) {
@@ -63626,7 +63650,7 @@ function newRawAppPathAssigner(defaultTs) {
63626
63650
  const seen_names = new Set;
63627
63651
  function assignPath(summary, language) {
63628
63652
  let name;
63629
- name = summary ? sanitizeForFilesystem(summary) : "";
63653
+ name = summary ? sanitizeForFilesystem(summary, { preserveCase: true }) : "";
63630
63654
  let original_name = name;
63631
63655
  if (name == "") {
63632
63656
  original_name = "runnable";
@@ -63995,6 +64019,19 @@ var init_relative_imports = __esm(async () => {
63995
64019
  // src/commands/flow/flow_metadata.ts
63996
64020
  import * as path10 from "node:path";
63997
64021
  import { sep as SEP7 } from "node:path";
64022
+ async function isFlowDirectlyStale(folder, hashes, conf) {
64023
+ if (await checkifMetadataUptodate(folder, hashes[TOP_HASH], conf, TOP_HASH)) {
64024
+ return false;
64025
+ }
64026
+ const fileEntries = Object.entries(hashes).filter(([k]) => k !== TOP_HASH);
64027
+ if (fileEntries.length === 0)
64028
+ return true;
64029
+ for (const [k, h] of fileEntries) {
64030
+ if (!await checkifMetadataUptodate(folder, h, conf, k))
64031
+ return true;
64032
+ }
64033
+ return false;
64034
+ }
63998
64035
  async function generateFlowHash(rawWorkspaceDependencies, folder, defaultTs) {
63999
64036
  const elems = await FSFSElement(path10.join(process.cwd(), folder), [], true);
64000
64037
  const hashes = {};
@@ -64014,6 +64051,14 @@ async function generateFlowLockInternal(folder, dryRun, workspace, opts, justUpd
64014
64051
  if (folder.endsWith(SEP7)) {
64015
64052
  folder = folder.substring(0, folder.length - 1);
64016
64053
  }
64054
+ if (opts.rehashOnly) {
64055
+ const hashes = await generateFlowHash({}, folder, opts.defaultTs);
64056
+ await clearGlobalLock(folder);
64057
+ for (const [k, v] of Object.entries(hashes)) {
64058
+ await updateMetadataGlobalLock(folder, v, k);
64059
+ }
64060
+ return;
64061
+ }
64017
64062
  const remote_path = extractNameFromFolder(folder.replaceAll(SEP7, "/"), "flow");
64018
64063
  if (!justUpdateMetadataLock && !noStaleMessage) {
64019
64064
  info(`Generating lock for flow ${folder} at ${remote_path}`);
@@ -64043,7 +64088,7 @@ async function generateFlowLockInternal(folder, dryRun, workspace, opts, justUpd
64043
64088
  inlineScriptPaths.push(treePath);
64044
64089
  }
64045
64090
  const hashes = await generateFlowHash({}, folder, opts.defaultTs);
64046
- const isDirectlyStale = !await checkifMetadataUptodate(folder, hashes[TOP_HASH], conf, TOP_HASH);
64091
+ const isDirectlyStale = await isFlowDirectlyStale(folder, hashes, conf);
64047
64092
  await tree.addNode(folderNormalized, "", "bun", "", inlineScriptPaths, "flow", folderNormalized, folder, isDirectlyStale);
64048
64093
  return;
64049
64094
  }
@@ -64052,7 +64097,7 @@ async function generateFlowLockInternal(folder, dryRun, workspace, opts, justUpd
64052
64097
  const rawWorkspaceDependencies = await getRawWorkspaceDependencies(true);
64053
64098
  filteredDeps = await filterWorkspaceDependenciesForFlow(flowValue.value, rawWorkspaceDependencies, folder);
64054
64099
  const hashes = await generateFlowHash(filteredDeps, folder, opts.defaultTs);
64055
- const isDirectlyStale = !await checkifMetadataUptodate(folder, hashes[TOP_HASH], conf, TOP_HASH);
64100
+ const isDirectlyStale = await isFlowDirectlyStale(folder, hashes, conf);
64056
64101
  if (!isDirectlyStale) {
64057
64102
  if (!noStaleMessage) {
64058
64103
  info(colors.green(`Flow ${remote_path} metadata is up-to-date, skipping`));
@@ -64227,6 +64272,662 @@ var init_flow_metadata = __esm(async () => {
64227
64272
  import_yaml9 = __toESM(require_dist(), 1);
64228
64273
  });
64229
64274
 
64275
+ // src/utils/dependency_tree.ts
64276
+ async function uploadScripts(tree, workspace) {
64277
+ const scriptHashes = {};
64278
+ const workspaceDeps = [];
64279
+ for (const path11 of tree.allPaths()) {
64280
+ const content = tree.getContent(path11);
64281
+ const itemType = tree.getItemType(path11);
64282
+ if (itemType === "dependencies") {
64283
+ if (content === undefined)
64284
+ continue;
64285
+ const info2 = workspaceDependenciesPathToLanguageAndFilename(path11);
64286
+ if (info2) {
64287
+ const hash2 = await generateHash(content);
64288
+ workspaceDeps.push({ path: path11, language: info2.language, name: info2.name, hash: hash2 });
64289
+ }
64290
+ } else if (itemType === "script") {
64291
+ if (!content)
64292
+ continue;
64293
+ const hash2 = await generateHash(content);
64294
+ scriptHashes[path11] = hash2;
64295
+ }
64296
+ }
64297
+ if (Object.keys(scriptHashes).length === 0 && workspaceDeps.length === 0)
64298
+ return;
64299
+ const mismatched = await diffRawScriptsWithDeployed({
64300
+ workspace: workspace.workspaceId,
64301
+ requestBody: {
64302
+ scripts: scriptHashes,
64303
+ workspace_deps: workspaceDeps
64304
+ }
64305
+ });
64306
+ for (const path11 of mismatched) {
64307
+ const content = tree.getContent(path11);
64308
+ const itemType = tree.getItemType(path11);
64309
+ if (itemType === "dependencies") {
64310
+ if (content !== undefined) {
64311
+ tree.setContentHash(path11, "mismatched");
64312
+ }
64313
+ } else if (content) {
64314
+ const hash2 = await storeRawScriptTemp({
64315
+ workspace: workspace.workspaceId,
64316
+ requestBody: content
64317
+ });
64318
+ tree.setContentHash(path11, hash2);
64319
+ }
64320
+ }
64321
+ }
64322
+
64323
+ class DoubleLinkedDependencyTree {
64324
+ nodes = new Map;
64325
+ workspaceDeps = {};
64326
+ setWorkspaceDeps(deps) {
64327
+ this.workspaceDeps = deps;
64328
+ }
64329
+ async addNode(path11, content, language, metadata, imports, itemType, folder, originalPath, isDirectlyStale, isRawApp) {
64330
+ const hasWorkspaceDeps = itemType === "script" || itemType === "inline_script";
64331
+ const filteredDeps = hasWorkspaceDeps ? filterWorkspaceDependencies(this.workspaceDeps, content, language) : {};
64332
+ const stalenessHash = await generateScriptHash({}, content, metadata);
64333
+ if (!this.nodes.has(path11)) {
64334
+ this.nodes.set(path11, {
64335
+ content: "",
64336
+ stalenessHash: "",
64337
+ language: "deno",
64338
+ metadata: "",
64339
+ imports: new Set,
64340
+ importedBy: new Set,
64341
+ itemType: "script",
64342
+ folder: "",
64343
+ originalPath: "",
64344
+ isDirectlyStale: false
64345
+ });
64346
+ }
64347
+ const node = this.nodes.get(path11);
64348
+ node.content = content;
64349
+ node.stalenessHash = stalenessHash;
64350
+ node.language = language;
64351
+ node.metadata = metadata;
64352
+ node.itemType = itemType;
64353
+ node.folder = folder;
64354
+ node.originalPath = originalPath;
64355
+ node.isDirectlyStale = isDirectlyStale;
64356
+ node.isRawApp = isRawApp;
64357
+ const filteredDepsPaths = Object.keys(filteredDeps);
64358
+ for (const depsPath of filteredDepsPaths) {
64359
+ if (!this.nodes.has(depsPath)) {
64360
+ const depsInfo = workspaceDependenciesPathToLanguageAndFilename(depsPath);
64361
+ const contentHash = await generateHash(filteredDeps[depsPath] + depsPath);
64362
+ const isUpToDate = await checkifMetadataUptodate(depsPath, contentHash, undefined);
64363
+ this.nodes.set(depsPath, {
64364
+ content: filteredDeps[depsPath],
64365
+ stalenessHash: "",
64366
+ language: depsInfo?.language ?? "deno",
64367
+ metadata: "",
64368
+ imports: new Set,
64369
+ importedBy: new Set,
64370
+ itemType: "dependencies",
64371
+ folder: "",
64372
+ originalPath: depsPath,
64373
+ isDirectlyStale: !isUpToDate
64374
+ });
64375
+ }
64376
+ }
64377
+ const allImports = [...imports, ...filteredDepsPaths];
64378
+ for (const importPath of allImports) {
64379
+ node.imports.add(importPath);
64380
+ if (!this.nodes.has(importPath)) {
64381
+ this.nodes.set(importPath, {
64382
+ content: "",
64383
+ stalenessHash: "",
64384
+ language: "deno",
64385
+ metadata: "",
64386
+ imports: new Set,
64387
+ importedBy: new Set,
64388
+ itemType: "script",
64389
+ folder: "",
64390
+ originalPath: "",
64391
+ isDirectlyStale: false
64392
+ });
64393
+ }
64394
+ this.nodes.get(importPath).importedBy.add(path11);
64395
+ }
64396
+ }
64397
+ getContent(path11) {
64398
+ return this.nodes.get(path11)?.content;
64399
+ }
64400
+ getStalenessHash(path11) {
64401
+ return this.nodes.get(path11)?.stalenessHash;
64402
+ }
64403
+ getContentHash(path11) {
64404
+ return this.nodes.get(path11)?.contentHash;
64405
+ }
64406
+ setContentHash(path11, hash2) {
64407
+ const node = this.nodes.get(path11);
64408
+ if (node) {
64409
+ node.contentHash = hash2;
64410
+ }
64411
+ }
64412
+ getLanguage(path11) {
64413
+ return this.nodes.get(path11)?.language;
64414
+ }
64415
+ getMetadata(path11) {
64416
+ return this.nodes.get(path11)?.metadata;
64417
+ }
64418
+ getStaleReason(path11) {
64419
+ return this.nodes.get(path11)?.staleReason;
64420
+ }
64421
+ getItemType(path11) {
64422
+ return this.nodes.get(path11)?.itemType;
64423
+ }
64424
+ getFolder(path11) {
64425
+ return this.nodes.get(path11)?.folder;
64426
+ }
64427
+ getIsRawApp(path11) {
64428
+ return this.nodes.get(path11)?.isRawApp;
64429
+ }
64430
+ getIsDirectlyStale(path11) {
64431
+ return this.nodes.get(path11)?.isDirectlyStale ?? false;
64432
+ }
64433
+ getOriginalPath(path11) {
64434
+ return this.nodes.get(path11)?.originalPath;
64435
+ }
64436
+ getImports(path11) {
64437
+ return this.nodes.get(path11)?.imports;
64438
+ }
64439
+ isStale(path11) {
64440
+ return this.nodes.get(path11)?.staleReason !== undefined;
64441
+ }
64442
+ propagateStaleness() {
64443
+ const directlyStale = new Set;
64444
+ for (const [path11, node] of this.nodes.entries()) {
64445
+ if (node.isDirectlyStale) {
64446
+ directlyStale.add(path11);
64447
+ node.staleReason = "content changed";
64448
+ }
64449
+ }
64450
+ const allStale = new Set(directlyStale);
64451
+ const queue = [...directlyStale];
64452
+ const visited = new Set;
64453
+ while (queue.length > 0) {
64454
+ const scriptPath = queue.shift();
64455
+ if (visited.has(scriptPath))
64456
+ continue;
64457
+ visited.add(scriptPath);
64458
+ const node = this.nodes.get(scriptPath);
64459
+ if (!node)
64460
+ continue;
64461
+ for (const importer of node.importedBy) {
64462
+ if (!allStale.has(importer)) {
64463
+ allStale.add(importer);
64464
+ queue.push(importer);
64465
+ const importerNode = this.nodes.get(importer);
64466
+ if (importerNode)
64467
+ importerNode.staleReason = `depends on ${scriptPath}`;
64468
+ }
64469
+ }
64470
+ }
64471
+ }
64472
+ traverseTransitive(scriptPath, callback) {
64473
+ const queue = [scriptPath];
64474
+ const visited = new Set;
64475
+ while (queue.length > 0) {
64476
+ const current = queue.shift();
64477
+ if (visited.has(current))
64478
+ continue;
64479
+ visited.add(current);
64480
+ const node = this.nodes.get(current);
64481
+ if (!node)
64482
+ continue;
64483
+ for (const importPath of node.imports) {
64484
+ const importNode = this.nodes.get(importPath);
64485
+ if (importNode) {
64486
+ const stop = callback(importPath, importNode);
64487
+ if (!stop) {
64488
+ queue.push(importPath);
64489
+ }
64490
+ }
64491
+ }
64492
+ }
64493
+ }
64494
+ allPaths() {
64495
+ return this.nodes.keys();
64496
+ }
64497
+ *stalePaths() {
64498
+ for (const [path11, node] of this.nodes.entries()) {
64499
+ if (node.staleReason) {
64500
+ yield path11;
64501
+ }
64502
+ }
64503
+ }
64504
+ has(path11) {
64505
+ return this.nodes.has(path11);
64506
+ }
64507
+ getMismatchedWorkspaceDeps() {
64508
+ const result = {};
64509
+ for (const [path11, node] of this.nodes.entries()) {
64510
+ if (node.itemType === "dependencies" && node.contentHash && node.content !== undefined) {
64511
+ result[path11] = node.content;
64512
+ }
64513
+ }
64514
+ return result;
64515
+ }
64516
+ getTempScriptRefs(scriptPath) {
64517
+ const result = {};
64518
+ this.traverseTransitive(scriptPath, (_path, node) => {
64519
+ if (node.contentHash) {
64520
+ result[_path] = node.contentHash;
64521
+ }
64522
+ });
64523
+ return result;
64524
+ }
64525
+ async persistDepsHashes(depsPaths) {
64526
+ for (const path11 of depsPaths) {
64527
+ const node = this.nodes.get(path11);
64528
+ if (node?.itemType === "dependencies" && node.content !== undefined) {
64529
+ const hash2 = await generateHash(node.content + path11);
64530
+ await updateMetadataGlobalLock(path11, hash2);
64531
+ }
64532
+ }
64533
+ }
64534
+ get size() {
64535
+ return this.nodes.size;
64536
+ }
64537
+ }
64538
+ var init_dependency_tree = __esm(async () => {
64539
+ init_services_gen();
64540
+ await __promiseAll([
64541
+ init_metadata(),
64542
+ init_utils()
64543
+ ]);
64544
+ });
64545
+
64546
+ // src/commands/generate-metadata/generate-metadata.ts
64547
+ var exports_generate_metadata = {};
64548
+ __export(exports_generate_metadata, {
64549
+ rehashOnly: () => rehashOnly,
64550
+ default: () => generate_metadata_default
64551
+ });
64552
+ import { sep as SEP8 } from "node:path";
64553
+ async function walkLocalScripts(codebases, ignore) {
64554
+ 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, {});
64555
+ return Object.keys(elems);
64556
+ }
64557
+ async function walkLocalFlowFolders(ignore) {
64558
+ 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, {});
64559
+ return Object.keys(elems).map((x) => x.substring(0, x.lastIndexOf(SEP8)));
64560
+ }
64561
+ async function walkLocalAppItems(ignore) {
64562
+ 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, {});
64563
+ return Object.keys(elems).map((p) => ({
64564
+ folder: p.substring(0, p.lastIndexOf(SEP8)),
64565
+ rawApp: p.endsWith(SEP8 + "raw_app.yaml")
64566
+ }));
64567
+ }
64568
+ function categorizeLocalFiles(paths) {
64569
+ const scripts = [];
64570
+ const flowFolderSet = new Set;
64571
+ const appPaths = [];
64572
+ for (const p of paths) {
64573
+ if (p.endsWith(SEP8 + "flow.yaml") || p.endsWith(SEP8 + "flow.json")) {
64574
+ flowFolderSet.add(p.substring(0, p.lastIndexOf(SEP8)));
64575
+ } else if (p.endsWith(SEP8 + "raw_app.yaml") || p.endsWith(SEP8 + "app.yaml")) {
64576
+ appPaths.push(p);
64577
+ } else if (exts.some((ext2) => p.endsWith(ext2)) && !isFolderResourcePathAnyFormat(p) && !(isScriptModulePath(p) && !isModuleEntryPoint(p))) {
64578
+ scripts.push(p);
64579
+ }
64580
+ }
64581
+ return { scripts, flowFolders: [...flowFolderSet], appPaths };
64582
+ }
64583
+ async function rehashOnly(opts, folder, rehashFilter) {
64584
+ const codebases = await listSyncCodebases(opts);
64585
+ const ignore = await ignoreF(opts);
64586
+ const counts = { scripts: 0, flows: 0, apps: 0 };
64587
+ const folderFilter = folder?.replaceAll("\\", "/").replace(/^\.\//, "").replace(/\/$/, "");
64588
+ const inFilter = (p) => {
64589
+ if (!folderFilter)
64590
+ return true;
64591
+ const n = p.replaceAll("\\", "/");
64592
+ return n === folderFilter || n.startsWith(folderFilter + "/");
64593
+ };
64594
+ const conf = rehashFilter?.missingOnly ? await readLockfile() : undefined;
64595
+ const isFlatKeyed = conf?.version === "v2";
64596
+ const hasEntry = (key, subpath) => {
64597
+ if (!conf?.locks)
64598
+ return false;
64599
+ if (isFlatKeyed) {
64600
+ const fullKey = subpath ? `${key}+${subpath}` : key;
64601
+ return conf.locks[fullKey] !== undefined || conf.locks["./" + fullKey] !== undefined;
64602
+ }
64603
+ for (const p of [key, "./" + key]) {
64604
+ const obj = conf.locks[p];
64605
+ if (obj === undefined)
64606
+ continue;
64607
+ if (!subpath)
64608
+ return true;
64609
+ if (typeof obj === "object" && obj?.[subpath] !== undefined)
64610
+ return true;
64611
+ }
64612
+ return false;
64613
+ };
64614
+ const skipIfExisting = (remotePath, subpath) => !!rehashFilter?.missingOnly && hasEntry(remotePath, subpath);
64615
+ let scriptPaths;
64616
+ let flowFolders;
64617
+ let appPaths;
64618
+ if (rehashFilter?.localFiles) {
64619
+ const cat = categorizeLocalFiles(rehashFilter.localFiles);
64620
+ scriptPaths = cat.scripts;
64621
+ flowFolders = cat.flowFolders;
64622
+ appPaths = cat.appPaths.map((p) => ({
64623
+ folder: p.substring(0, p.lastIndexOf(SEP8)),
64624
+ rawApp: p.endsWith(SEP8 + "raw_app.yaml")
64625
+ }));
64626
+ } else {
64627
+ scriptPaths = await walkLocalScripts(codebases, ignore);
64628
+ flowFolders = await walkLocalFlowFolders(ignore);
64629
+ appPaths = await walkLocalAppItems(ignore);
64630
+ }
64631
+ const stubWorkspace = {};
64632
+ const rehashOpts = { ...opts, rehashOnly: true };
64633
+ if (!rehashFilter?.skipScripts) {
64634
+ for (const e of scriptPaths) {
64635
+ const remotePath = scriptPathToRemotePath(e);
64636
+ if (!inFilter(remotePath))
64637
+ continue;
64638
+ if (rehashFilter?.missingOnly) {
64639
+ if (skipIfExisting(remotePath) || skipIfExisting(remotePath, "__script_hash"))
64640
+ continue;
64641
+ }
64642
+ try {
64643
+ await generateScriptMetadataInternal(e, stubWorkspace, rehashOpts, false, true, {}, codebases, false);
64644
+ counts.scripts++;
64645
+ } catch (err) {
64646
+ warn(`Skipping ${e}: ${err instanceof Error ? err.message : err}`);
64647
+ }
64648
+ }
64649
+ }
64650
+ if (!rehashFilter?.skipFlows) {
64651
+ for (const f of flowFolders) {
64652
+ if (!inFilter(f))
64653
+ continue;
64654
+ if (rehashFilter?.missingOnly) {
64655
+ const folderNormalized = f.replaceAll(SEP8, "/");
64656
+ if (skipIfExisting(folderNormalized, "__flow_hash"))
64657
+ continue;
64658
+ }
64659
+ try {
64660
+ await generateFlowLockInternal(f, false, stubWorkspace, rehashOpts, false, true);
64661
+ counts.flows++;
64662
+ } catch (err) {
64663
+ warn(`Skipping ${f}: ${err instanceof Error ? err.message : err}`);
64664
+ }
64665
+ }
64666
+ }
64667
+ if (!rehashFilter?.skipApps) {
64668
+ for (const { folder: appFolder, rawApp } of appPaths) {
64669
+ if (!inFilter(appFolder))
64670
+ continue;
64671
+ if (rehashFilter?.missingOnly) {
64672
+ const folderNormalized = appFolder.replaceAll(SEP8, "/");
64673
+ if (skipIfExisting(folderNormalized, "__app_hash"))
64674
+ continue;
64675
+ }
64676
+ try {
64677
+ await generateAppLocksInternal(appFolder, rawApp, false, stubWorkspace, rehashOpts, false, true);
64678
+ counts.apps++;
64679
+ } catch (err) {
64680
+ warn(`Skipping ${appFolder}: ${err instanceof Error ? err.message : err}`);
64681
+ }
64682
+ }
64683
+ }
64684
+ if (counts.scripts + counts.flows + counts.apps > 0 || !rehashFilter?.missingOnly) {
64685
+ 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.`);
64686
+ }
64687
+ return counts;
64688
+ }
64689
+ async function generateMetadata2(opts, folder) {
64690
+ if (folder === "") {
64691
+ folder = undefined;
64692
+ }
64693
+ const workspace = await resolveWorkspace(opts);
64694
+ await requireLogin(opts);
64695
+ opts = await mergeConfigWithConfigFile(opts);
64696
+ const rawWorkspaceDependencies = await getRawWorkspaceDependencies(false);
64697
+ const codebases = await listSyncCodebases(opts);
64698
+ const ignore = await ignoreF(opts);
64699
+ const skipScripts = opts.skipScripts ?? false;
64700
+ const skipFlows = opts.skipFlows ?? opts.schemaOnly ?? false;
64701
+ const skipApps = opts.skipApps ?? opts.schemaOnly ?? false;
64702
+ const checking = [];
64703
+ if (!skipScripts)
64704
+ checking.push("scripts");
64705
+ if (!skipFlows)
64706
+ checking.push("flows");
64707
+ if (!skipApps)
64708
+ checking.push("apps");
64709
+ if (checking.length === 0) {
64710
+ info(colors.yellow("Nothing to check (all types skipped)"));
64711
+ return;
64712
+ }
64713
+ info(`Checking ${checking.join(", ")}...`);
64714
+ const tree = new DoubleLinkedDependencyTree;
64715
+ tree.setWorkspaceDeps(rawWorkspaceDependencies);
64716
+ if (!skipScripts) {
64717
+ for (const e of await walkLocalScripts(codebases, ignore)) {
64718
+ await generateScriptMetadataInternal(e, workspace, opts, true, true, rawWorkspaceDependencies, codebases, false, tree);
64719
+ }
64720
+ }
64721
+ if (!skipFlows) {
64722
+ for (const flowFolder of await walkLocalFlowFolders(ignore)) {
64723
+ await generateFlowLockInternal(flowFolder, true, workspace, opts, false, true, tree);
64724
+ }
64725
+ }
64726
+ if (!skipApps) {
64727
+ for (const { folder: appFolder, rawApp } of await walkLocalAppItems(ignore)) {
64728
+ await generateAppLocksInternal(appFolder, rawApp, true, workspace, opts, false, true, tree);
64729
+ }
64730
+ }
64731
+ tree.propagateStaleness();
64732
+ try {
64733
+ await uploadScripts(tree, workspace);
64734
+ } catch (e) {
64735
+ 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.`));
64736
+ }
64737
+ const staleItems = [];
64738
+ const seenFolders = new Set;
64739
+ for (const p of tree.allPaths()) {
64740
+ const staleReason = tree.getStaleReason(p);
64741
+ if (!staleReason)
64742
+ continue;
64743
+ const itemType = tree.getItemType(p);
64744
+ const itemFolder = tree.getFolder(p);
64745
+ if (itemType === "dependencies") {
64746
+ staleItems.push({ type: itemType, path: p, folder: itemFolder, staleReason });
64747
+ } else if (itemType === "inline_script") {
64748
+ continue;
64749
+ } else if (itemType === "script") {
64750
+ const originalPath = tree.getOriginalPath(p);
64751
+ staleItems.push({ type: itemType, path: originalPath, folder: itemFolder, staleReason });
64752
+ } else if (!seenFolders.has(itemFolder)) {
64753
+ seenFolders.add(itemFolder);
64754
+ const originalPath = tree.getOriginalPath(p);
64755
+ staleItems.push({ type: itemType, path: originalPath, folder: itemFolder, isRawApp: tree.getIsRawApp(p), staleReason });
64756
+ }
64757
+ }
64758
+ let filteredItems = staleItems;
64759
+ if (folder) {
64760
+ folder = folder.replaceAll("\\", "/");
64761
+ if (folder.endsWith("/")) {
64762
+ folder = folder.substring(0, folder.length - 1);
64763
+ }
64764
+ const folderNoExt = folder.replace(/\.[^/.]+$/, "");
64765
+ const isInsideFolder = (item) => {
64766
+ const normalizedFolder = item.folder.replaceAll("\\", "/");
64767
+ const normalizedPath = item.path.replaceAll("\\", "/");
64768
+ return normalizedFolder === folder || normalizedFolder.startsWith(folder + "/") || normalizedPath === folder || normalizedPath === folderNoExt;
64769
+ };
64770
+ const isPathInFolder = (p) => p.startsWith(folder + "/") || p === folder || p === folderNoExt;
64771
+ const touchesFolder = (treePath) => {
64772
+ if (isPathInFolder(treePath))
64773
+ return true;
64774
+ let found = false;
64775
+ tree.traverseTransitive(treePath, (importPath) => {
64776
+ if (isPathInFolder(importPath)) {
64777
+ found = true;
64778
+ return true;
64779
+ }
64780
+ });
64781
+ return found;
64782
+ };
64783
+ const isRelevant = (item) => {
64784
+ if (isInsideFolder(item))
64785
+ return true;
64786
+ if (item.type === "dependencies")
64787
+ return true;
64788
+ const treePath = (item.type === "script" ? item.path.replace(/\.[^/.]+$/, "") : item.folder).replaceAll("\\", "/");
64789
+ return touchesFolder(treePath);
64790
+ };
64791
+ if (opts.strictFolderBoundaries) {
64792
+ filteredItems = staleItems.filter(isInsideFolder);
64793
+ const excludedStale = staleItems.filter((item) => !isInsideFolder(item) && isRelevant(item) && item.type !== "dependencies");
64794
+ for (const item of excludedStale) {
64795
+ const normalizedPath = item.path.replaceAll("\\", "/");
64796
+ 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.`));
64797
+ }
64798
+ } else {
64799
+ filteredItems = staleItems.filter(isRelevant);
64800
+ }
64801
+ }
64802
+ if (filteredItems.length === 0) {
64803
+ info(colors.green("All metadata up-to-date"));
64804
+ return;
64805
+ }
64806
+ const scripts = filteredItems.filter((i) => i.type === "script");
64807
+ const flows = filteredItems.filter((i) => i.type === "flow");
64808
+ const apps2 = filteredItems.filter((i) => i.type === "app");
64809
+ const deps = filteredItems.filter((i) => i.type === "dependencies");
64810
+ info("");
64811
+ info(`Found ${colors.bold(String(filteredItems.length))} item(s) with stale metadata:`);
64812
+ const printItems = (label, items) => {
64813
+ if (items.length === 0)
64814
+ return;
64815
+ info(` ${label} (${items.length}):`);
64816
+ for (const item of items) {
64817
+ const reason = item.staleReason ? colors.dim(colors.white(` — ${item.staleReason}`)) : "";
64818
+ info(` ~ ${item.path}` + reason);
64819
+ }
64820
+ };
64821
+ printItems("Workspace dependencies", deps);
64822
+ printItems("Scripts", scripts);
64823
+ printItems("Flows", flows);
64824
+ printItems("Apps", apps2);
64825
+ if (opts.dryRun) {
64826
+ return;
64827
+ }
64828
+ info("");
64829
+ const isInteractive = process.stdin.isTTY ?? false;
64830
+ if (!opts.yes && isInteractive && !await Confirm.prompt({
64831
+ message: "Update metadata?",
64832
+ default: true
64833
+ })) {
64834
+ return;
64835
+ }
64836
+ info("");
64837
+ const mismatchedWorkspaceDeps = tree.getMismatchedWorkspaceDeps();
64838
+ const total = filteredItems.length - deps.length;
64839
+ const maxWidth = `[${total}/${total}]`.length;
64840
+ let current = 0;
64841
+ const formatProgress = (n) => {
64842
+ return colors.dim(colors.white(`[${n}/${total}]`.padEnd(maxWidth, " ")));
64843
+ };
64844
+ const errors = [];
64845
+ for (const item of scripts) {
64846
+ current++;
64847
+ info(`${formatProgress(current)} script ${item.path}`);
64848
+ try {
64849
+ await generateScriptMetadataInternal(item.path, workspace, opts, false, true, mismatchedWorkspaceDeps, codebases, false, tree);
64850
+ } catch (e) {
64851
+ const msg = e instanceof Error ? e.message : String(e);
64852
+ errors.push({ path: item.path, error: msg });
64853
+ error(` Failed: ${msg}`);
64854
+ }
64855
+ }
64856
+ for (const item of flows) {
64857
+ current++;
64858
+ try {
64859
+ const result = await generateFlowLockInternal(item.folder.replaceAll("/", SEP8), false, workspace, opts, false, true, tree);
64860
+ const flowResult = result;
64861
+ const scriptsInfo = flowResult?.updatedScripts?.length ? colors.dim(colors.white(`: ${flowResult.updatedScripts.join(", ")}`)) : "";
64862
+ info(`${formatProgress(current)} flow ${item.path}${scriptsInfo}`);
64863
+ } catch (e) {
64864
+ const msg = e instanceof Error ? e.message : String(e);
64865
+ errors.push({ path: item.path, error: msg });
64866
+ info(`${formatProgress(current)} flow ${item.path}`);
64867
+ error(` Failed: ${msg}`);
64868
+ }
64869
+ }
64870
+ for (const item of apps2) {
64871
+ current++;
64872
+ try {
64873
+ const result = await generateAppLocksInternal(item.folder.replaceAll("/", SEP8), item.isRawApp, false, workspace, opts, false, true, tree);
64874
+ const appResult = result;
64875
+ const scriptsInfo = appResult?.updatedScripts?.length ? colors.dim(colors.white(`: ${appResult.updatedScripts.join(", ")}`)) : "";
64876
+ info(`${formatProgress(current)} app ${item.path}${scriptsInfo}`);
64877
+ } catch (e) {
64878
+ const msg = e instanceof Error ? e.message : String(e);
64879
+ errors.push({ path: item.path, error: msg });
64880
+ info(`${formatProgress(current)} app ${item.path}`);
64881
+ error(` Failed: ${msg}`);
64882
+ }
64883
+ }
64884
+ const allStaleDeps = staleItems.filter((i) => i.type === "dependencies");
64885
+ await tree.persistDepsHashes(allStaleDeps.map((d) => d.path));
64886
+ const succeeded = total - errors.length;
64887
+ info("");
64888
+ if (errors.length > 0) {
64889
+ info(`Done. Updated ${colors.bold(String(succeeded))}/${total} item(s). ${colors.red(String(errors.length) + " failed")}:`);
64890
+ for (const { path: path11, error: error2 } of errors) {
64891
+ error(` ${path11}: ${error2}`);
64892
+ }
64893
+ process.exitCode = 1;
64894
+ } else {
64895
+ info(`Done. Updated ${colors.bold(String(total))} item(s).`);
64896
+ }
64897
+ }
64898
+ async function rehashCommand(opts, folder) {
64899
+ if (folder === "")
64900
+ folder = undefined;
64901
+ opts = await mergeConfigWithConfigFile(opts);
64902
+ await rehashOnly(opts, folder, {
64903
+ skipScripts: opts.skipScripts,
64904
+ skipFlows: opts.skipFlows,
64905
+ skipApps: opts.skipApps
64906
+ });
64907
+ }
64908
+ var command7, generate_metadata_default;
64909
+ var init_generate_metadata = __esm(async () => {
64910
+ init_mod3();
64911
+ init_colors2();
64912
+ init_log();
64913
+ await __promiseAll([
64914
+ init_confirm(),
64915
+ init_conf(),
64916
+ init_context(),
64917
+ init_auth(),
64918
+ init_metadata(),
64919
+ init_flow_metadata(),
64920
+ init_app_metadata(),
64921
+ init_sync(),
64922
+ init_script(),
64923
+ init_resource_folders(),
64924
+ init_codebase(),
64925
+ init_dependency_tree()
64926
+ ]);
64927
+ 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));
64928
+ generate_metadata_default = command7;
64929
+ });
64930
+
64230
64931
  // src/commands/sync/sync.ts
64231
64932
  var exports_sync = {};
64232
64933
  __export(exports_sync, {
@@ -64248,7 +64949,7 @@ __export(exports_sync, {
64248
64949
  });
64249
64950
  import { writeFile as writeFile7, readdir as readdir4, stat as stat7, rm, copyFile, mkdir as mkdir5 } from "node:fs/promises";
64250
64951
  import * as path11 from "node:path";
64251
- import { sep as SEP8 } from "node:path";
64952
+ import { sep as SEP9 } from "node:path";
64252
64953
  function resolveWsNameFromBranch(opts, branchName) {
64253
64954
  const match2 = findWorkspaceByGitBranch(opts.workspaces, branchName);
64254
64955
  return match2 ? match2[0] : branchName;
@@ -64578,11 +65279,11 @@ function extractInlineScriptsForApps(key, rec, pathAssigner, toId, removeSchema)
64578
65279
  const o = v;
64579
65280
  const name = toId(key ?? "", rec);
64580
65281
  const [basePathO, ext2] = pathAssigner.assignPath(name, o["language"]);
64581
- const basePath = basePathO.replaceAll(SEP8, "/");
65282
+ const basePath = basePathO.replaceAll(SEP9, "/");
64582
65283
  const r = [];
64583
65284
  if (o["content"]) {
64584
65285
  const content = o["content"];
64585
- o["content"] = "!inline " + basePath.replaceAll(SEP8, "/") + ext2;
65286
+ o["content"] = "!inline " + basePath.replaceAll(SEP9, "/") + ext2;
64586
65287
  r.push({
64587
65288
  path: basePath + ext2,
64588
65289
  content
@@ -64590,7 +65291,7 @@ function extractInlineScriptsForApps(key, rec, pathAssigner, toId, removeSchema)
64590
65291
  }
64591
65292
  if (o["lock"] && o["lock"] != "") {
64592
65293
  const lock = o["lock"];
64593
- o["lock"] = "!inline " + basePath.replaceAll(SEP8, "/") + "lock";
65294
+ o["lock"] = "!inline " + basePath.replaceAll(SEP9, "/") + "lock";
64594
65295
  r.push({
64595
65296
  path: basePath + "lock",
64596
65297
  content: lock
@@ -64624,7 +65325,7 @@ function parseFileResourceTypeMap(raw) {
64624
65325
  return { formatExtMap, filesetMap };
64625
65326
  }
64626
65327
  async function findFilesetResourceFile(changePath) {
64627
- const filesetIdx = changePath.indexOf(".fileset" + SEP8);
65328
+ const filesetIdx = changePath.indexOf(".fileset" + SEP9);
64628
65329
  if (filesetIdx === -1) {
64629
65330
  throw new Error(`Not a fileset resource path: ${changePath}`);
64630
65331
  }
@@ -64724,12 +65425,12 @@ function ZipFSElement(zip, useYaml, defaultTs, resourceTypeToFormatExtension, re
64724
65425
  try {
64725
65426
  const assigner = newPathAssigner(defaultTs, { skipInlineScriptSuffix: getNonDottedPaths() });
64726
65427
  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() });
65428
+ inlineScripts = extractInlineScripts(flow.value.modules, inlineMapping, SEP9, defaultTs, assigner, { skipInlineScriptSuffix: getNonDottedPaths() });
64728
65429
  if (flow.value.failure_module) {
64729
- inlineScripts.push(...extractInlineScripts([flow.value.failure_module], inlineMapping, SEP8, defaultTs, assigner, { skipInlineScriptSuffix: getNonDottedPaths() }));
65430
+ inlineScripts.push(...extractInlineScripts([flow.value.failure_module], inlineMapping, SEP9, defaultTs, assigner, { skipInlineScriptSuffix: getNonDottedPaths() }));
64730
65431
  }
64731
65432
  if (flow.value.preprocessor_module) {
64732
- inlineScripts.push(...extractInlineScripts([flow.value.preprocessor_module], inlineMapping, SEP8, defaultTs, assigner, { skipInlineScriptSuffix: getNonDottedPaths() }));
65433
+ inlineScripts.push(...extractInlineScripts([flow.value.preprocessor_module], inlineMapping, SEP9, defaultTs, assigner, { skipInlineScriptSuffix: getNonDottedPaths() }));
64733
65434
  }
64734
65435
  } catch (error2) {
64735
65436
  error(`Failed to extract inline scripts for flow at path: ${p}`);
@@ -64939,10 +65640,10 @@ function ZipFSElement(zip, useYaml, defaultTs, resourceTypeToFormatExtension, re
64939
65640
  const hasModules = parsed["modules"] && Object.keys(parsed["modules"]).length > 0;
64940
65641
  if (parsed["lock"] && parsed["lock"] != "" && parsed["codebase"] == undefined) {
64941
65642
  if (hasModules) {
64942
- const scriptBase = removeSuffix(removeSuffix(p.replaceAll(SEP8, "/"), ".json"), ".script");
65643
+ const scriptBase = removeSuffix(removeSuffix(p.replaceAll(SEP9, "/"), ".json"), ".script");
64943
65644
  parsed["lock"] = "!inline " + scriptBase + getModuleFolderSuffix() + "/script.lock";
64944
65645
  } else {
64945
- parsed["lock"] = "!inline " + removeSuffix(p.replaceAll(SEP8, "/"), ".json") + ".lock";
65646
+ parsed["lock"] = "!inline " + removeSuffix(p.replaceAll(SEP9, "/"), ".json") + ".lock";
64946
65647
  }
64947
65648
  } else if (parsed["lock"] == "") {
64948
65649
  parsed["lock"] = "";
@@ -64972,9 +65673,9 @@ function ZipFSElement(zip, useYaml, defaultTs, resourceTypeToFormatExtension, re
64972
65673
  const formatExtension = resourceTypeToFormatExtension[resourceType];
64973
65674
  const isFileset = resourceTypeToIsFileset[resourceType] ?? false;
64974
65675
  if (isFileset) {
64975
- parsed["value"] = "!inline_fileset " + removeSuffix(p.replaceAll(SEP8, "/"), ".resource.json") + ".fileset";
65676
+ parsed["value"] = "!inline_fileset " + removeSuffix(p.replaceAll(SEP9, "/"), ".resource.json") + ".fileset";
64976
65677
  } else if (formatExtension) {
64977
- parsed["value"]["content"] = "!inline " + removeSuffix(p.replaceAll(SEP8, "/"), ".resource.json") + ".resource.file." + formatExtension;
65678
+ parsed["value"]["content"] = "!inline " + removeSuffix(p.replaceAll(SEP9, "/"), ".resource.json") + ".resource.file." + formatExtension;
64978
65679
  }
64979
65680
  return useYaml ? import_yaml11.stringify(parsed, yamlOptions) : JSON.stringify(parsed, null, 2);
64980
65681
  }
@@ -65141,7 +65842,7 @@ function ZipFSElement(zip, useYaml, defaultTs, resourceTypeToFormatExtension, re
65141
65842
  }
65142
65843
  };
65143
65844
  }
65144
- return _internal_folder("." + SEP8, zip);
65845
+ return _internal_folder("." + SEP9, zip);
65145
65846
  }
65146
65847
  async function* readDirRecursiveWithIgnore2(ignore, root) {
65147
65848
  const stack = [
@@ -65160,7 +65861,7 @@ async function* readDirRecursiveWithIgnore2(ignore, root) {
65160
65861
  yield e;
65161
65862
  for await (const e2 of e.c()) {
65162
65863
  if (e2.isDirectory) {
65163
- const dirName = e2.path.split(SEP8).pop();
65864
+ const dirName = e2.path.split(SEP9).pop();
65164
65865
  if (dirName == "node_modules" || dirName == ".claude" || dirName?.startsWith(".")) {
65165
65866
  continue;
65166
65867
  }
@@ -65183,7 +65884,7 @@ async function elementsToMap(els, ignore, json, skips, specificItems, branchOver
65183
65884
  for await (const entry of readDirRecursiveWithIgnore2(ignore, els)) {
65184
65885
  if (entry.isDirectory) {
65185
65886
  if (!isRemote) {
65186
- const dirName = entry.path.split(SEP8).pop() ?? "";
65887
+ const dirName = entry.path.split(SEP9).pop() ?? "";
65187
65888
  if (hasWrongFormatSuffix(dirName)) {
65188
65889
  wrongFormatPaths.push(entry.path);
65189
65890
  }
@@ -65228,7 +65929,7 @@ async function elementsToMap(els, ignore, json, skips, specificItems, branchOver
65228
65929
  }
65229
65930
  }
65230
65931
  if (isRawAppFile(path12)) {
65231
- const suffix = path12.split(getFolderSuffix("raw_app") + SEP8).pop();
65932
+ const suffix = path12.split(getFolderSuffix("raw_app") + SEP9).pop();
65232
65933
  if (suffix?.startsWith("dist/") || suffix == "wmill.d.ts" || suffix == "package-lock.json" || suffix == "DATATABLES.md") {
65233
65934
  continue;
65234
65935
  }
@@ -65499,7 +66200,8 @@ async function compareDynFSElement(els1, els2, ignore, json, skips, ignoreMetada
65499
66200
  }
65500
66201
  return a.path.localeCompare(b.path);
65501
66202
  });
65502
- return changes;
66203
+ const localMap = isEls1Remote ? m2 : m1;
66204
+ return { changes, localMap };
65503
66205
  }
65504
66206
  function getOrderFromPath(p) {
65505
66207
  const typ = getTypeStrFromPath(p);
@@ -65728,7 +66430,7 @@ async function pull(opts) {
65728
66430
  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
66431
  const remote = ZipFSElement(zipFile, !opts.json, opts.defaultTs ?? "bun", resourceTypeToFormatExtension, resourceTypeToIsFileset, true, parseSyncBehavior(opts.syncBehavior) >= 1);
65730
66432
  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);
66433
+ const { changes, localMap } = await compareDynFSElement(remote, local, await ignoreF(opts), opts.json ?? false, opts, false, codebases, true, specificItems, wsNameForFiles, true);
65732
66434
  info(`remote (${workspace.name}) -> local: ${changes.length} changes to apply`);
65733
66435
  if (opts.dryRun && opts.jsonOutput) {
65734
66436
  const result = {
@@ -65892,6 +66594,31 @@ Done! All ${changes.length} changes applied locally and wmill-lock.yaml updated.
65892
66594
  } else if (opts.jsonOutput) {
65893
66595
  console.log(JSON.stringify({ success: true, message: "No changes to apply", total: 0 }, null, 2));
65894
66596
  }
66597
+ if (!opts.jsonOutput && !opts.dryRun) {
66598
+ try {
66599
+ const { rehashOnly: rehashOnly2 } = await init_generate_metadata().then(() => exports_generate_metadata);
66600
+ const postPullPaths = new Set(Object.keys(localMap));
66601
+ for (const change of changes) {
66602
+ if (change.name === "added")
66603
+ postPullPaths.add(change.path);
66604
+ else if (change.name === "deleted")
66605
+ postPullPaths.delete(change.path);
66606
+ }
66607
+ const filled = await rehashOnly2(opts, undefined, {
66608
+ missingOnly: true,
66609
+ localFiles: postPullPaths
66610
+ });
66611
+ const total = filled.scripts + filled.flows + filled.apps;
66612
+ if (total > 0) {
66613
+ info(colors.gray(`Auto-filled ${total} missing lockfile entr${total === 1 ? "y" : "ies"} (${filled.scripts} script, ${filled.flows} flow, ${filled.apps} app) from disk.`));
66614
+ }
66615
+ } catch (e) {
66616
+ if (e instanceof UnknownLockVersionError || e instanceof MalformedLockfileError) {
66617
+ throw e;
66618
+ }
66619
+ warn(colors.yellow(`Could not auto-fill missing lockfile entries: ${e instanceof Error ? e.message : e}`));
66620
+ }
66621
+ }
65895
66622
  try {
65896
66623
  await pullSharedUi(workspace.workspaceId);
65897
66624
  } catch (e) {
@@ -66020,7 +66747,7 @@ Push aborted: ${lockIssues.length} script(s) missing locks.`));
66020
66747
  } catch {}
66021
66748
  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
66749
  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);
66750
+ const { changes } = await compareDynFSElement(local, remote, await ignoreF(opts), opts.json ?? false, opts, true, codebases, false, specificItems, wsNameForFiles, false);
66024
66751
  const rawWorkspaceDependencies = await getRawWorkspaceDependencies(true);
66025
66752
  const tracker = await buildTracker(changes);
66026
66753
  const autoRegenerate = !!opts.autoMetadata;
@@ -66127,7 +66854,7 @@ Push aborted: ${lockIssues.length} script(s) missing locks.`));
66127
66854
  if (changes.length > 0) {
66128
66855
  const folderNames = new Set;
66129
66856
  for (const change of changes) {
66130
- const parts = change.path.split(SEP8);
66857
+ const parts = change.path.split(SEP9);
66131
66858
  if (parts.length >= 3 && parts[0] === "f" && change.name !== "deleted") {
66132
66859
  folderNames.add(parts[1]);
66133
66860
  }
@@ -66160,7 +66887,7 @@ Push aborted: ${lockIssues.length} script(s) missing locks.`));
66160
66887
  const userIsAdmin = user.is_admin;
66161
66888
  const msg = `${userIsAdmin ? "Warning: " : ""}Missing folder.meta.yaml for:
66162
66889
  ${folderList}
66163
- ` + `Run 'wmill folder add-missing' to create them locally, then push again.`;
66890
+ Run 'wmill folder add-missing' to create them locally, then push again.`;
66164
66891
  if (!userIsAdmin) {
66165
66892
  if (opts.jsonOutput) {
66166
66893
  console.log(JSON.stringify({ success: false, error: "missing_folders", missing_folders: missingFolders, message: msg }, null, 2));
@@ -66242,7 +66969,7 @@ ${folderList}
66242
66969
  };
66243
66970
  await preCheckPermissionedAs(changes, user.email, userIsAdminOrDeployer, opts.acceptOverridingPermissionedAsWithSelf ?? false, !!process.stdin.isTTY);
66244
66971
  } 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.`));
66972
+ 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
66973
  }
66247
66974
  if (!opts.yes && !await Confirm.prompt({
66248
66975
  message: `Do you want to apply these ${changes.length} changes to the remote?`,
@@ -66273,7 +67000,7 @@ ${folderList}
66273
67000
  parallelizationFactor = 1;
66274
67001
  }
66275
67002
  const allGrouped = Array.from(groupedChanges.entries());
66276
- const isFolderMetaGroup = ([basePath]) => basePath.endsWith(`${SEP8}folder`) || basePath === "folder";
67003
+ const isFolderMetaGroup = ([basePath]) => basePath.endsWith(`${SEP9}folder`) || basePath === "folder";
66277
67004
  const folderMetaGroups = allGrouped.filter(isFolderMetaGroup);
66278
67005
  const groupedChangesArray = allGrouped.filter((g) => !isFolderMetaGroup(g));
66279
67006
  info(`found changes for ${allGrouped.length} items with a total of ${allGrouped.reduce((acc, [_, changes2]) => acc + changes2.length, 0)} files to process`);
@@ -66289,7 +67016,7 @@ ${folderList}
66289
67016
  while (pool.size < effectiveParallelism() && queue.length > 0) {
66290
67017
  const [groupBasePath, initialChanges] = queue.shift();
66291
67018
  let changes2 = initialChanges;
66292
- const isFolderGroup = groupBasePath.endsWith(`${SEP8}folder`) || groupBasePath === "folder";
67019
+ const isFolderGroup = groupBasePath.endsWith(`${SEP9}folder`) || groupBasePath === "folder";
66293
67020
  const promise = (async () => {
66294
67021
  const alreadySynced = [];
66295
67022
  const deletedVarsResPaths = [];
@@ -66421,7 +67148,7 @@ ${folderList}
66421
67148
  info(`Deleting ${typ} ${change.path}`);
66422
67149
  }
66423
67150
  const workspaceId = workspace.workspaceId;
66424
- const target = change.path.replaceAll(SEP8, "/");
67151
+ const target = change.path.replaceAll(SEP9, "/");
66425
67152
  switch (typ) {
66426
67153
  case "script": {
66427
67154
  await archiveScriptByPath({
@@ -66433,7 +67160,7 @@ ${folderList}
66433
67160
  case "folder":
66434
67161
  await deleteFolder({
66435
67162
  workspace: workspaceId,
66436
- name: change.path.split(SEP8)[1]
67163
+ name: change.path.split(SEP9)[1]
66437
67164
  });
66438
67165
  break;
66439
67166
  case "resource": {
@@ -66732,14 +67459,14 @@ Done! All ${changes.length} changes pushed to the remote workspace ${workspace.w
66732
67459
  }
66733
67460
  }
66734
67461
  var import_yaml11, branchDeprecationWarned = false, yamlOptions, isNotWmillFile = (p, isDirectory2) => {
66735
- if (p.endsWith(SEP8)) {
67462
+ if (p.endsWith(SEP9)) {
66736
67463
  return false;
66737
67464
  }
66738
- if (p.startsWith("ui" + SEP8)) {
67465
+ if (p.startsWith("ui" + SEP9)) {
66739
67466
  return true;
66740
67467
  }
66741
67468
  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);
67469
+ 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
67470
  }
66744
67471
  if (isScriptModulePath(p)) {
66745
67472
  return false;
@@ -66747,16 +67474,16 @@ var import_yaml11, branchDeprecationWarned = false, yamlOptions, isNotWmillFile
66747
67474
  try {
66748
67475
  const typ = getTypeStrFromPath(p);
66749
67476
  if (typ == "resource-type" || typ == "settings" || typ == "encryption_key") {
66750
- return p.includes(SEP8);
67477
+ return p.includes(SEP9);
66751
67478
  } 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);
67479
+ 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
67480
  }
66754
67481
  } catch {
66755
67482
  return true;
66756
67483
  }
66757
67484
  }, 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;
67485
+ return p == "." + SEP9 || p == "" || p == "u" || p == "f" || p == "g" || p == "ui" || p == "users" || p == "groups" || p == "dependencies";
67486
+ }, command8, sync_default;
66760
67487
  var init_sync = __esm(async () => {
66761
67488
  init_colors2();
66762
67489
  init_mod3();
@@ -66798,8 +67525,8 @@ var init_sync = __esm(async () => {
66798
67525
  aliasDuplicateObjects: false,
66799
67526
  singleQuote: true
66800
67527
  };
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;
67528
+ 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);
67529
+ sync_default = command8;
66803
67530
  });
66804
67531
 
66805
67532
  // windmill-utils-internal/src/parse/parse-schema.ts
@@ -67004,7 +67731,7 @@ var init_parse_schema = __esm(() => {
67004
67731
  });
67005
67732
 
67006
67733
  // src/utils/metadata.ts
67007
- import { sep as SEP9 } from "node:path";
67734
+ import { sep as SEP10 } from "node:path";
67008
67735
  import { writeFile as writeFile8, stat as stat8, rm as rm2, readdir as readdir5 } from "node:fs/promises";
67009
67736
  import { readFileSync as readFileSync2, existsSync as existsSync6, readdirSync, statSync, writeFileSync as writeFileSync5 } from "node:fs";
67010
67737
  import * as path12 from "node:path";
@@ -67098,7 +67825,7 @@ async function blueColor() {
67098
67825
  }
67099
67826
  async function generateScriptMetadataInternal(scriptPath, workspace, opts, dryRun, noStaleMessage, rawWorkspaceDependencies, codebases, justUpdateMetadataLock, tree) {
67100
67827
  const isFolderLayout = isModuleEntryPoint(scriptPath);
67101
- const remotePath = isFolderLayout ? getScriptBasePathFromModulePath(scriptPath).replaceAll(SEP9, "/") : scriptPath.substring(0, scriptPath.indexOf(".")).replaceAll(SEP9, "/");
67828
+ const remotePath = scriptPathToRemotePath(scriptPath);
67102
67829
  const language = inferContentTypeFromFilePath(scriptPath, opts.defaultTs);
67103
67830
  const metadataWithType = await parseMetadataFile(remotePath, undefined);
67104
67831
  const scriptContent = await readTextFile(scriptPath);
@@ -67113,6 +67840,22 @@ async function generateScriptMetadataInternal(scriptPath, workspace, opts, dryRu
67113
67840
  moduleHashes = await computeModuleHashes(moduleFolderPath, opts.defaultTs, tree ? {} : rawWorkspaceDependencies, isFolderLayout);
67114
67841
  }
67115
67842
  const hasModuleHashes = Object.keys(moduleHashes).length > 0;
67843
+ if (opts.rehashOnly) {
67844
+ if (hasModuleHashes) {
67845
+ const sortedEntries = Object.entries(moduleHashes).sort(([a], [b]) => a.localeCompare(b));
67846
+ const metaHash = await generateHash(hash2 + JSON.stringify(sortedEntries));
67847
+ await clearGlobalLock(remotePath);
67848
+ await updateMetadataGlobalLock(remotePath, metaHash, SCRIPT_TOP_HASH);
67849
+ for (const [modulePath, moduleHash] of Object.entries(moduleHashes)) {
67850
+ await updateMetadataGlobalLock(remotePath, moduleHash, modulePath);
67851
+ }
67852
+ } else {
67853
+ await clearGlobalLock(remotePath);
67854
+ await updateMetadataGlobalLock(remotePath, hash2);
67855
+ }
67856
+ return;
67857
+ }
67858
+ const conf = await readLockfile();
67116
67859
  let checkHash = hash2;
67117
67860
  let checkSubpath;
67118
67861
  if (hasModuleHashes) {
@@ -67120,7 +67863,6 @@ async function generateScriptMetadataInternal(scriptPath, workspace, opts, dryRu
67120
67863
  checkHash = await generateHash(hash2 + JSON.stringify(sortedEntries));
67121
67864
  checkSubpath = SCRIPT_TOP_HASH;
67122
67865
  }
67123
- const conf = await readLockfile();
67124
67866
  const isDirectlyStale = !await checkifMetadataUptodate(remotePath, checkHash, conf, checkSubpath);
67125
67867
  if (tree) {
67126
67868
  if (dryRun) {
@@ -67183,9 +67925,9 @@ async function generateScriptMetadataInternal(scriptPath, workspace, opts, dryRu
67183
67925
  }
67184
67926
  } else {
67185
67927
  if (isFolderLayout) {
67186
- metadataParsedContent.lock = "!inline " + remotePath.replaceAll(SEP9, "/") + getModuleFolderSuffix() + "/script.lock";
67928
+ metadataParsedContent.lock = "!inline " + remotePath.replaceAll(SEP10, "/") + getModuleFolderSuffix() + "/script.lock";
67187
67929
  } else {
67188
- metadataParsedContent.lock = "!inline " + remotePath.replaceAll(SEP9, "/") + ".script.lock";
67930
+ metadataParsedContent.lock = "!inline " + remotePath.replaceAll(SEP10, "/") + ".script.lock";
67189
67931
  }
67190
67932
  }
67191
67933
  let metaPath;
@@ -67372,7 +68114,7 @@ async function updateScriptLock(workspace, scriptContent, language, remotePath,
67372
68114
  const lockPath = lockPathOverride ?? remotePath + ".script.lock";
67373
68115
  if (lock != "") {
67374
68116
  await writeFile8(lockPath, lock, "utf-8");
67375
- metadataContent.lock = "!inline " + lockPath.replaceAll(SEP9, "/");
68117
+ metadataContent.lock = "!inline " + lockPath.replaceAll(SEP10, "/");
67376
68118
  } else {
67377
68119
  try {
67378
68120
  if (await stat8(lockPath)) {
@@ -67686,22 +68428,29 @@ async function parseMetadataFile(scriptPath, generateMetadataIfMissing) {
67686
68428
  };
67687
68429
  }
67688
68430
  function normalizeLockPath(p) {
67689
- return p.replace(/\\/g, "/");
68431
+ let n = p.replace(/\\/g, "/");
68432
+ if (n.startsWith("./"))
68433
+ n = n.slice(2);
68434
+ return n;
67690
68435
  }
67691
68436
  async function readLockfile() {
68437
+ let parsed;
67692
68438
  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
- }
68439
+ parsed = await yamlParseFile(WMILL_LOCKFILE);
67699
68440
  } catch {
67700
- const lock = { locks: {}, version: "v2" };
68441
+ const lock = { locks: {}, version: CURRENT_LOCK_VERSION };
67701
68442
  await writeFile8(WMILL_LOCKFILE, import_yaml13.stringify(lock, yamlOptions), "utf-8");
67702
68443
  info(colors.green("wmill-lock.yaml created"));
67703
68444
  return lock;
67704
68445
  }
68446
+ if (typeof parsed != "object" || parsed == null) {
68447
+ throw new MalformedLockfileError("wmill-lock.yaml is malformed (expected an object). Refusing to operate to avoid corrupting the lockfile.");
68448
+ }
68449
+ const conf = parsed;
68450
+ if (conf.version != null && !KNOWN_LOCK_VERSIONS.includes(conf.version)) {
68451
+ 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.`);
68452
+ }
68453
+ return conf;
67705
68454
  }
67706
68455
  function v2LockPath(path13, subpath) {
67707
68456
  const normalizedPath = normalizeLockPath(path13);
@@ -67718,15 +68467,18 @@ async function checkifMetadataUptodate(path13, hash2, conf, subpath) {
67718
68467
  if (!conf.locks) {
67719
68468
  return false;
67720
68469
  }
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;
68470
+ const isFlatKeyed = conf?.version === "v2";
68471
+ if (isFlatKeyed) {
68472
+ const key = v2LockPath(path13, subpath);
68473
+ return conf.locks?.[key] === hash2 || conf.locks?.["./" + key] === hash2;
68474
+ }
68475
+ for (const p of [path13, "./" + path13]) {
68476
+ const obj = conf.locks?.[p];
68477
+ const v = subpath && typeof obj == "object" ? obj?.[subpath] : obj;
68478
+ if (v === hash2)
68479
+ return true;
67729
68480
  }
68481
+ return false;
67730
68482
  }
67731
68483
  async function generateScriptHash(rawWorkspaceDependencies, scriptContent, newMetadataContent) {
67732
68484
  return await generateHash(JSON.stringify(rawWorkspaceDependencies) + scriptContent + newMetadataContent);
@@ -67761,15 +68513,16 @@ async function clearGlobalLock(path13) {
67761
68513
  if (!conf?.locks) {
67762
68514
  conf.locks = {};
67763
68515
  }
67764
- const isV2 = conf?.version == "v2";
67765
- if (isV2) {
68516
+ const isFlatKeyed = conf?.version === "v2";
68517
+ if (isFlatKeyed) {
67766
68518
  const key = v2LockPath(path13);
68519
+ const legacyKey = "./" + key;
67767
68520
  if (conf.locks) {
67768
68521
  Object.keys(conf.locks).forEach((k) => {
67769
- if (conf.locks) {
67770
- if (k.startsWith(key)) {
67771
- delete conf.locks[k];
67772
- }
68522
+ if (!conf.locks)
68523
+ return;
68524
+ if (k === key || k.startsWith(key + "+") || k === legacyKey || k.startsWith(legacyKey + "+")) {
68525
+ delete conf.locks[k];
67773
68526
  }
67774
68527
  });
67775
68528
  }
@@ -67781,8 +68534,8 @@ async function updateMetadataGlobalLock(path13, hash2, subpath) {
67781
68534
  if (!conf?.locks) {
67782
68535
  conf.locks = {};
67783
68536
  }
67784
- const isV2 = conf?.version == "v2";
67785
- if (isV2) {
68537
+ const isFlatKeyed = conf?.version === "v2";
68538
+ if (isFlatKeyed) {
67786
68539
  conf.locks[v2LockPath(path13, subpath)] = hash2;
67787
68540
  } else {
67788
68541
  if (subpath) {
@@ -67798,7 +68551,7 @@ async function updateMetadataGlobalLock(path13, hash2, subpath) {
67798
68551
  }
67799
68552
  await writeFile8(WMILL_LOCKFILE, import_yaml13.stringify(conf, yamlOptions), "utf-8");
67800
68553
  }
67801
- var import_yaml13, _require, _parserCache, LockfileGenerationError, LANG_ANNOTATION_CONFIG, lockCache, WMILL_LOCKFILE = "wmill-lock.yaml", SCRIPT_TOP_HASH = "__script_hash";
68554
+ 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
68555
  var init_metadata = __esm(async () => {
67803
68556
  init_colors2();
67804
68557
  init_log();
@@ -67824,6 +68577,18 @@ var init_metadata = __esm(async () => {
67824
68577
  this.name = "LockfileGenerationError";
67825
68578
  }
67826
68579
  };
68580
+ UnknownLockVersionError = class UnknownLockVersionError extends Error {
68581
+ constructor(message) {
68582
+ super(message);
68583
+ this.name = "UnknownLockVersionError";
68584
+ }
68585
+ };
68586
+ MalformedLockfileError = class MalformedLockfileError extends Error {
68587
+ constructor(message) {
68588
+ super(message);
68589
+ this.name = "MalformedLockfileError";
68590
+ }
68591
+ };
67827
68592
  LANG_ANNOTATION_CONFIG = {
67828
68593
  python3: { comment: "#", keyword: "requirements", validityRe: /^#\s?(\S+)\s*$/ },
67829
68594
  bun: { comment: "//", keyword: "package_json" },
@@ -67833,6 +68598,7 @@ var init_metadata = __esm(async () => {
67833
68598
  powershell: { comment: "#", keyword: "modules_json" }
67834
68599
  };
67835
68600
  lockCache = new Map;
68601
+ KNOWN_LOCK_VERSIONS = ["v1", "v2"];
67836
68602
  });
67837
68603
 
67838
68604
  // src/commands/app/raw_apps.ts
@@ -67843,7 +68609,7 @@ __export(exports_raw_apps, {
67843
68609
  loadRunnablesFromBackend: () => loadRunnablesFromBackend,
67844
68610
  generatingPolicy: () => generatingPolicy
67845
68611
  });
67846
- import { sep as SEP10 } from "node:path";
68612
+ import { sep as SEP11 } from "node:path";
67847
68613
  import path13 from "node:path";
67848
68614
  import { readdir as readdir6 } from "node:fs/promises";
67849
68615
  async function readSiblingLock(backendPath, runnableId, allFiles) {
@@ -67981,7 +68747,7 @@ async function collectAppFiles(localPath) {
67981
68747
  if (entry.name === APP_BACKEND_FOLDER || entry.name === "node_modules" || entry.name === "dist" || entry.name === ".claude" || entry.name === "sql_to_apply") {
67982
68748
  continue;
67983
68749
  }
67984
- await readDirRecursive2(fullPath + SEP10, relativePath + "/");
68750
+ await readDirRecursive2(fullPath + SEP11, relativePath + "/");
67985
68751
  } else if (entry.isFile()) {
67986
68752
  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
68753
  continue;
@@ -67999,7 +68765,7 @@ async function pushRawApp(workspace, remotePath, localPath, message) {
67999
68765
  return;
68000
68766
  }
68001
68767
  alreadySynced.push(localPath);
68002
- remotePath = remotePath.replaceAll(SEP10, "/");
68768
+ remotePath = remotePath.replaceAll(SEP11, "/");
68003
68769
  let app = undefined;
68004
68770
  try {
68005
68771
  app = await getAppByPath({
@@ -68013,8 +68779,8 @@ async function pushRawApp(workspace, remotePath, localPath, message) {
68013
68779
  if (app) {
68014
68780
  app.policy = undefined;
68015
68781
  }
68016
- if (!localPath.endsWith(SEP10)) {
68017
- localPath += SEP10;
68782
+ if (!localPath.endsWith(SEP11)) {
68783
+ localPath += SEP11;
68018
68784
  }
68019
68785
  const appFilePath = localPath + "raw_app.yaml";
68020
68786
  const localApp = await yamlParseFile(appFilePath);
@@ -68030,7 +68796,7 @@ async function pushRawApp(workspace, remotePath, localPath, message) {
68030
68796
  } else {
68031
68797
  runnables = {};
68032
68798
  }
68033
- replaceInlineScripts2(runnables, backendPath + SEP10, true);
68799
+ replaceInlineScripts2(runnables, backendPath + SEP11, true);
68034
68800
  repopulateFields(runnables);
68035
68801
  const appForPolicy = { ...localApp, runnables };
68036
68802
  await generatingPolicy(appForPolicy, remotePath, localApp?.["public"] ?? false);
@@ -68142,7 +68908,20 @@ __export(exports_app_metadata, {
68142
68908
  });
68143
68909
  import path14 from "node:path";
68144
68910
  import { mkdir as mkdir6, readdir as readdir7 } from "node:fs/promises";
68145
- import { sep as SEP11 } from "node:path";
68911
+ import { sep as SEP12 } from "node:path";
68912
+ async function isAppDirectlyStale(appFolder, hashes, conf) {
68913
+ if (await checkifMetadataUptodate(appFolder, hashes[TOP_HASH2], conf, TOP_HASH2)) {
68914
+ return false;
68915
+ }
68916
+ const fileEntries = Object.entries(hashes).filter(([k]) => k !== TOP_HASH2);
68917
+ if (fileEntries.length === 0)
68918
+ return true;
68919
+ for (const [k, h] of fileEntries) {
68920
+ if (!await checkifMetadataUptodate(appFolder, h, conf, k))
68921
+ return true;
68922
+ }
68923
+ return false;
68924
+ }
68146
68925
  async function generateAppHash(rawReqs, folder, rawApp, defaultTs) {
68147
68926
  const runnablesFolder = rawApp ? path14.join(folder, APP_BACKEND_FOLDER) : folder;
68148
68927
  const hashes = {};
@@ -68156,7 +68935,7 @@ async function generateAppHash(rawReqs, folder, rawApp, defaultTs) {
68156
68935
  }
68157
68936
  }
68158
68937
  if (exts.some((e) => f.path.endsWith(e))) {
68159
- const relativePath = normalizeLockPath(f.path.replace(runnablesFolder + SEP11, ""));
68938
+ const relativePath = normalizeLockPath(f.path.replace(runnablesFolder + SEP12, ""));
68160
68939
  hashes[relativePath] = await generateHash(await f.getContentText() + JSON.stringify(rawReqs));
68161
68940
  }
68162
68941
  }
@@ -68172,23 +68951,31 @@ async function generateAppHash(rawReqs, folder, rawApp, defaultTs) {
68172
68951
  return { ...sortedHashes, [TOP_HASH2]: await generateHash(JSON.stringify(sortedHashes)) };
68173
68952
  }
68174
68953
  async function generateAppLocksInternal(appFolder, rawApp, dryRun, workspace, opts, justUpdateMetadataLock, noStaleMessage, tree) {
68175
- if (appFolder.endsWith(SEP11)) {
68954
+ if (appFolder.endsWith(SEP12)) {
68176
68955
  appFolder = appFolder.substring(0, appFolder.length - 1);
68177
68956
  }
68178
- const remote_path = appFolder.replaceAll(SEP11, "/");
68957
+ if (opts.rehashOnly) {
68958
+ const hashes = await generateAppHash({}, appFolder, rawApp, opts.defaultTs);
68959
+ await clearGlobalLock(appFolder);
68960
+ for (const [k, v] of Object.entries(hashes)) {
68961
+ await updateMetadataGlobalLock(appFolder, v, k);
68962
+ }
68963
+ return;
68964
+ }
68965
+ const remote_path = appFolder.replaceAll(SEP12, "/");
68179
68966
  if (!justUpdateMetadataLock && !noStaleMessage) {
68180
68967
  info(`Generating locks for app ${appFolder} at ${remote_path}`);
68181
68968
  }
68182
68969
  const appFilePath = path14.join(appFolder, rawApp ? "raw_app.yaml" : "app.yaml");
68183
68970
  const appFile = await yamlParseFile(appFilePath);
68184
68971
  const appValue = rawApp ? appFile.runnables : appFile.value;
68185
- const folderNormalized = appFolder.replaceAll(SEP11, "/");
68972
+ const folderNormalized = appFolder.replaceAll(SEP12, "/");
68186
68973
  let filteredDeps = {};
68187
68974
  const conf = await readLockfile();
68188
68975
  if (tree) {
68189
68976
  if (dryRun) {
68190
68977
  const hashes = await generateAppHash({}, appFolder, rawApp, opts.defaultTs);
68191
- const isDirectlyStale = !await checkifMetadataUptodate(appFolder, hashes[TOP_HASH2], conf, TOP_HASH2);
68978
+ const isDirectlyStale = await isAppDirectlyStale(appFolder, hashes, conf);
68192
68979
  let treeAppValue = structuredClone(appValue);
68193
68980
  if (rawApp) {
68194
68981
  const runnablesPath = path14.join(appFolder, APP_BACKEND_FOLDER);
@@ -68204,7 +68991,7 @@ async function generateAppLocksInternal(appFolder, rawApp, dryRun, workspace, op
68204
68991
  }
68205
68992
  let content = inlineScript.content;
68206
68993
  if (typeof content === "string" && content.startsWith("!inline ")) {
68207
- const filePath = appFolder + SEP11 + content.replace("!inline ", "");
68994
+ const filePath = appFolder + SEP12 + content.replace("!inline ", "");
68208
68995
  try {
68209
68996
  content = await readTextFile(filePath);
68210
68997
  } catch {
@@ -68226,7 +69013,7 @@ async function generateAppLocksInternal(appFolder, rawApp, dryRun, workspace, op
68226
69013
  const rawWorkspaceDependencies = await getRawWorkspaceDependencies(true);
68227
69014
  filteredDeps = await filterWorkspaceDependenciesForApp(appValue, rawWorkspaceDependencies, appFolder);
68228
69015
  const hashes = await generateAppHash(filteredDeps, appFolder, rawApp, opts.defaultTs);
68229
- const isDirectlyStale = !await checkifMetadataUptodate(appFolder, hashes[TOP_HASH2], conf, TOP_HASH2);
69016
+ const isDirectlyStale = await isAppDirectlyStale(appFolder, hashes, conf);
68230
69017
  if (!isDirectlyStale) {
68231
69018
  if (!noStaleMessage) {
68232
69019
  info(colors.green(`App ${remote_path} metadata is up-to-date, skipping`));
@@ -68263,11 +69050,11 @@ async function generateAppLocksInternal(appFolder, rawApp, dryRun, workspace, op
68263
69050
  if (Object.keys(runnables).length === 0 && rawAppFile.runnables) {
68264
69051
  runnables = rawAppFile.runnables;
68265
69052
  }
68266
- replaceInlineScripts2(runnables, runnablesPath + SEP11, false);
69053
+ replaceInlineScripts2(runnables, runnablesPath + SEP12, false);
68267
69054
  updatedScripts = await updateRawAppRunnables(workspace, runnables, remote_path, appFolder, filteredDeps, opts.defaultTs, noStaleMessage, tempScriptRefs);
68268
69055
  } else {
68269
69056
  const normalAppFile = appFile;
68270
- replaceInlineScripts2(normalAppFile.value, appFolder + SEP11, false);
69057
+ replaceInlineScripts2(normalAppFile.value, appFolder + SEP12, false);
68271
69058
  const result = await updateAppInlineScripts(workspace, normalAppFile.value, remote_path, appFolder, filteredDeps, opts.defaultTs, noStaleMessage, tempScriptRefs);
68272
69059
  normalAppFile.value = result.value;
68273
69060
  updatedScripts = result.updatedScripts;
@@ -68299,7 +69086,7 @@ async function filterWorkspaceDependenciesForApp(appValue, rawWorkspaceDependenc
68299
69086
  }
68300
69087
  return inlineScript;
68301
69088
  });
68302
- return await filterWorkspaceDependenciesForScripts(scripts, rawWorkspaceDependencies, folder, SEP11);
69089
+ return await filterWorkspaceDependenciesForScripts(scripts, rawWorkspaceDependencies, folder, SEP12);
68303
69090
  }
68304
69091
  async function traverseAndProcessInlineScripts(obj, processor, currentPath = []) {
68305
69092
  if (!obj || typeof obj !== "object") {
@@ -68354,7 +69141,7 @@ async function updateRawAppRunnables(workspace, runnables, remotePath, appFolder
68354
69141
  }
68355
69142
  if (language === "frontend") {
68356
69143
  const [basePathO, ext2] = pathAssigner.assignPath(runnableId, language);
68357
- const basePath = basePathO.replaceAll(SEP11, "/");
69144
+ const basePath = basePathO.replaceAll(SEP12, "/");
68358
69145
  const contentPath = path14.join(runnablesFolder, `${basePath}${ext2}`);
68359
69146
  writeIfChanged(contentPath, content);
68360
69147
  const simplifiedRunnable = { type: "inline" };
@@ -68372,7 +69159,7 @@ async function updateRawAppRunnables(workspace, runnables, remotePath, appFolder
68372
69159
  try {
68373
69160
  const lock = await generateInlineScriptLock(workspace, content, language, `${remotePath}/${runnableId}`, rawDeps, tempScriptRefs);
68374
69161
  const [basePathO, ext2] = pathAssigner.assignPath(runnableId, language);
68375
- const basePath = basePathO.replaceAll(SEP11, "/");
69162
+ const basePath = basePathO.replaceAll(SEP12, "/");
68376
69163
  const contentPath = path14.join(runnablesFolder, `${basePath}${ext2}`);
68377
69164
  const lockPath = path14.join(runnablesFolder, `${basePath}lock`);
68378
69165
  writeIfChanged(contentPath, content);
@@ -68421,7 +69208,7 @@ async function updateAppInlineScripts(workspace, appValue, remotePath, appFolder
68421
69208
  lock = await generateInlineScriptLock(workspace, content, language, scriptPath, rawDeps, tempScriptRefs);
68422
69209
  }
68423
69210
  const [basePathO, ext2] = pathAssigner.assignPath(scriptName, language);
68424
- const basePath = basePathO.replaceAll(SEP11, "/");
69211
+ const basePath = basePathO.replaceAll(SEP12, "/");
68425
69212
  const contentPath = path14.join(appFolder, `${basePath}${ext2}`);
68426
69213
  const lockPath = path14.join(appFolder, `${basePath}lock`);
68427
69214
  writeIfChanged(contentPath, content);
@@ -68550,7 +69337,7 @@ async function inferRunnableSchemaFromFile(appFolder, runnableFilePath, defaultT
68550
69337
  warn(colors.yellow(`Could not read file: ${fullFilePath}`));
68551
69338
  return;
68552
69339
  }
68553
- const remotePath = appFolder.replaceAll(SEP11, "/");
69340
+ const remotePath = appFolder.replaceAll(SEP12, "/");
68554
69341
  try {
68555
69342
  const schemaResult = await inferSchema(language, content, currentSchema, `${remotePath}/${runnableId}`);
68556
69343
  info(colors.green(` Inferred schema for ${runnableId}`));
@@ -68586,7 +69373,7 @@ async function inferAllInlineSchemas(appFolder, defaultTs = "bun") {
68586
69373
  return schemas;
68587
69374
  }
68588
69375
  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));
69376
+ return Object.keys(elems).filter((p) => p.endsWith(SEP12 + extension)).map((p) => p.substring(0, p.length - (SEP12 + extension).length));
68590
69377
  }
68591
69378
  async function generateLocksCommand(opts, appPath) {
68592
69379
  const { generateAppLocksInternal: generateAppLocksInternal2 } = await init_app_metadata().then(() => exports_app_metadata);
@@ -68604,7 +69391,7 @@ async function generateLocksCommand(opts, appPath) {
68604
69391
  } else {
68605
69392
  const ignore = await ignoreF2(opts);
68606
69393
  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");
69394
+ return ignore(p, isD) || !isD && !p.endsWith(SEP12 + "raw_app.yaml") && !p.endsWith(SEP12 + "app.yaml");
68608
69395
  }, false, {});
68609
69396
  const rawAppFolders = getAppFolders(elems, "raw_app.yaml");
68610
69397
  const appFolders = getAppFolders(elems, "app.yaml");
@@ -68846,7 +69633,7 @@ async function generateAgents(opts, appFolder) {
68846
69633
  await requireLogin(opts);
68847
69634
  await regenerateAgentDocs(workspace.workspaceId, targetDir);
68848
69635
  }
68849
- var command8, generate_agents_default;
69636
+ var command9, generate_agents_default;
68850
69637
  var init_generate_agents = __esm(async () => {
68851
69638
  init_mod3();
68852
69639
  init_colors2();
@@ -68859,12 +69646,12 @@ var init_generate_agents = __esm(async () => {
68859
69646
  init_sync(),
68860
69647
  init_resource_folders()
68861
69648
  ]);
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;
69649
+ command9 = new Command().description("regenerate AGENTS.md and DATATABLES.md from remote workspace").arguments("[app_folder:string]").action(generateAgents);
69650
+ generate_agents_default = command9;
68864
69651
  });
68865
69652
 
68866
69653
  // src/commands/app/dev.ts
68867
- import { sep as SEP12 } from "node:path";
69654
+ import { sep as SEP13 } from "node:path";
68868
69655
  import * as http2 from "node:http";
68869
69656
  import * as fs11 from "node:fs";
68870
69657
  import * as path16 from "node:path";
@@ -69555,7 +70342,7 @@ async function loadRunnables() {
69555
70342
  runnables = rawApp?.runnables ?? {};
69556
70343
  }
69557
70344
  convertRunnablesToApiFormat(runnables);
69558
- replaceInlineScripts2(runnables, backendPath + SEP12, true);
70345
+ replaceInlineScripts2(runnables, backendPath + SEP13, true);
69559
70346
  repopulateFields(runnables);
69560
70347
  return runnables;
69561
70348
  } catch (error2) {
@@ -70007,7 +70794,7 @@ var DEFAULT_PORT = 4000, DEFAULT_HOST = "localhost", createHTML = (jsPath, cssPa
70007
70794
  </script>
70008
70795
  </body>
70009
70796
  </html>
70010
- `, command9, dev_default, ITERATIONS_BEFORE_SLOW_REFRESH = 10, ITERATIONS_BEFORE_SUPER_SLOW_REFRESH = 100, QUEUE_LOG_INTERVAL_MS2 = 5000;
70797
+ `, command10, dev_default, ITERATIONS_BEFORE_SLOW_REFRESH = 10, ITERATIONS_BEFORE_SUPER_SLOW_REFRESH = 100, QUEUE_LOG_INTERVAL_MS2 = 5000;
70011
70798
  var init_dev = __esm(async () => {
70012
70799
  init_mod3();
70013
70800
  init_colors2();
@@ -70032,10 +70819,10 @@ var init_dev = __esm(async () => {
70032
70819
  init_generate_agents(),
70033
70820
  init_resource_folders()
70034
70821
  ]);
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", {
70822
+ 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
70823
  default: DEFAULT_HOST
70037
70824
  }).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;
70825
+ dev_default = command10;
70039
70826
  });
70040
70827
 
70041
70828
  // src/commands/app/lint.ts
@@ -70164,7 +70951,7 @@ async function lint2(opts, appFolder) {
70164
70951
  ✅ All checks passed
70165
70952
  `));
70166
70953
  }
70167
- var command10, lint_default2;
70954
+ var command11, lint_default2;
70168
70955
  var init_lint2 = __esm(async () => {
70169
70956
  init_mod3();
70170
70957
  init_colors2();
@@ -70176,8 +70963,8 @@ var init_lint2 = __esm(async () => {
70176
70963
  init_raw_apps(),
70177
70964
  init_resource_folders()
70178
70965
  ]);
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;
70966
+ 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);
70967
+ lint_default2 = command11;
70181
70968
  });
70182
70969
 
70183
70970
  // src/commands/app/new.ts
@@ -70713,7 +71500,7 @@ createApp(App).mount('#root')`, indexCss = `body {
70713
71500
  .myclass {
70714
71501
  border: 1px solid gray;
70715
71502
  padding: 2px;
70716
- }`, templates, command11, new_default;
71503
+ }`, templates, command12, new_default;
70717
71504
  var init_new = __esm(async () => {
70718
71505
  init_mod3();
70719
71506
  init_colors2();
@@ -70798,12 +71585,12 @@ var init_new = __esm(async () => {
70798
71585
  }
70799
71586
  }
70800
71587
  };
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;
71588
+ 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);
71589
+ new_default = command12;
70803
71590
  });
70804
71591
 
70805
71592
  // src/commands/app/app.ts
70806
- import { sep as SEP13 } from "node:path";
71593
+ import { sep as SEP14 } from "node:path";
70807
71594
  import { stat as stat10 } from "node:fs/promises";
70808
71595
  function respecializeFields(fields) {
70809
71596
  Object.entries(fields).forEach(([k, v]) => {
@@ -70877,7 +71664,7 @@ async function pushApp(workspace, remotePath, localPath, message, permissionedAs
70877
71664
  return;
70878
71665
  }
70879
71666
  alreadySynced2.push(localPath);
70880
- remotePath = remotePath.replaceAll(SEP13, "/");
71667
+ remotePath = remotePath.replaceAll(SEP14, "/");
70881
71668
  let app = undefined;
70882
71669
  try {
70883
71670
  app = await getAppByPath({
@@ -70897,8 +71684,8 @@ async function pushApp(workspace, remotePath, localPath, message, permissionedAs
70897
71684
  if (app) {
70898
71685
  app.policy = undefined;
70899
71686
  }
70900
- if (!localPath.endsWith(SEP13)) {
70901
- localPath += SEP13;
71687
+ if (!localPath.endsWith(SEP14)) {
71688
+ localPath += SEP14;
70902
71689
  }
70903
71690
  const path19 = localPath + "app.yaml";
70904
71691
  const localApp = await yamlParseFile(path19);
@@ -70999,12 +71786,12 @@ async function push5(opts, filePath, remotePath) {
70999
71786
  }
71000
71787
  const workspace = await resolveWorkspace(opts);
71001
71788
  await requireLogin(opts);
71002
- const normalizedPath = filePath.endsWith(SEP13) ? filePath.slice(0, -1) : filePath;
71789
+ const normalizedPath = filePath.endsWith(SEP14) ? filePath.slice(0, -1) : filePath;
71003
71790
  const isRawApp = normalizedPath.endsWith("__raw_app") || normalizedPath.endsWith(".raw_app");
71004
71791
  let hasRawAppYaml = false;
71005
71792
  if (!isRawApp) {
71006
71793
  try {
71007
- const rawAppPath = (filePath.endsWith(SEP13) ? filePath : filePath + SEP13) + "raw_app.yaml";
71794
+ const rawAppPath = (filePath.endsWith(SEP14) ? filePath : filePath + SEP14) + "raw_app.yaml";
71008
71795
  await stat10(rawAppPath);
71009
71796
  hasRawAppYaml = true;
71010
71797
  } catch {}
@@ -71018,7 +71805,7 @@ async function push5(opts, filePath, remotePath) {
71018
71805
  info(colors.bold.underline.green("App pushed"));
71019
71806
  }
71020
71807
  }
71021
- var alreadySynced2, command12, app_default;
71808
+ var alreadySynced2, command13, app_default;
71022
71809
  var init_app = __esm(async () => {
71023
71810
  init_mod3();
71024
71811
  init_mod6();
@@ -71039,7 +71826,7 @@ var init_app = __esm(async () => {
71039
71826
  init_generate_agents()
71040
71827
  ]);
71041
71828
  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) => {
71829
+ 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
71830
  warn(colors.yellow('This command is deprecated. Use "wmill generate-metadata" instead.'));
71044
71831
  const { generateLocksCommand: generateLocksCommand2 } = await init_app_metadata().then(() => exports_app_metadata);
71045
71832
  await generateLocksCommand2(opts, appFolder);
@@ -71069,12 +71856,12 @@ var init_app = __esm(async () => {
71069
71856
  });
71070
71857
  info(colors.green(`Updated permissioned_as for app ${appPath} to ${email}`));
71071
71858
  });
71072
- app_default = command12;
71859
+ app_default = command13;
71073
71860
  });
71074
71861
 
71075
71862
  // src/commands/folder/folder.ts
71076
71863
  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";
71864
+ import { sep as SEP15 } from "node:path";
71078
71865
  async function list7(opts) {
71079
71866
  if (opts.json)
71080
71867
  setSilent(true);
@@ -71094,8 +71881,8 @@ async function list7(opts) {
71094
71881
  }
71095
71882
  }
71096
71883
  async function newFolder(opts, name) {
71097
- const dirPath = `f${SEP14}${name}`;
71098
- const filePath = `${dirPath}${SEP14}folder.meta.yaml`;
71884
+ const dirPath = `f${SEP15}${name}`;
71885
+ const filePath = `${dirPath}${SEP15}folder.meta.yaml`;
71099
71886
  try {
71100
71887
  await stat11(filePath);
71101
71888
  throw new Error("File already exists: " + filePath);
@@ -71133,13 +71920,13 @@ async function get5(opts, name) {
71133
71920
  }
71134
71921
  }
71135
71922
  async function pushFolder(workspace, name, folder, localFolder) {
71136
- if (name.startsWith(SEP14)) {
71923
+ if (name.startsWith(SEP15)) {
71137
71924
  name = name.substring(1);
71138
71925
  }
71139
- if (name.startsWith("f" + SEP14)) {
71926
+ if (name.startsWith("f" + SEP15)) {
71140
71927
  name = name.substring(2);
71141
71928
  }
71142
- name = name.split(SEP14)[0];
71929
+ name = name.split(SEP15)[0];
71143
71930
  debug(`Processing local folder ${name}`);
71144
71931
  try {
71145
71932
  folder = await getFolder({ workspace, name });
@@ -71183,7 +71970,7 @@ async function pushFolder(workspace, name, folder, localFolder) {
71183
71970
  async function push6(opts, name) {
71184
71971
  const workspace = await resolveWorkspace(opts);
71185
71972
  await requireLogin(opts);
71186
- const metaPath = `f${SEP14}${name}${SEP14}folder.meta.yaml`;
71973
+ const metaPath = `f${SEP15}${name}${SEP15}folder.meta.yaml`;
71187
71974
  try {
71188
71975
  await stat11(metaPath);
71189
71976
  } catch {
@@ -71206,7 +71993,7 @@ async function addMissing(opts) {
71206
71993
  for (const entry of entries) {
71207
71994
  if (!entry.isDirectory())
71208
71995
  continue;
71209
- const metaPath = `${fDir}${SEP14}${entry.name}${SEP14}folder.meta.yaml`;
71996
+ const metaPath = `${fDir}${SEP15}${entry.name}${SEP15}folder.meta.yaml`;
71210
71997
  try {
71211
71998
  await stat11(metaPath);
71212
71999
  } catch {
@@ -71233,7 +72020,7 @@ async function addMissing(opts) {
71233
72020
  info(`
71234
72021
  Created ${missing.length} folder.meta.yaml file(s). You can now run 'wmill sync push' to push them.`);
71235
72022
  }
71236
- var import_yaml24, command13, folder_default;
72023
+ var import_yaml24, command14, folder_default;
71237
72024
  var init_folder = __esm(async () => {
71238
72025
  init_colors2();
71239
72026
  init_mod3();
@@ -71247,7 +72034,7 @@ var init_folder = __esm(async () => {
71247
72034
  init_types()
71248
72035
  ]);
71249
72036
  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) => {
72037
+ 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
72038
  const workspace = await resolveWorkspace(opts);
71252
72039
  await requireLogin(opts);
71253
72040
  const folder = await getFolder({
@@ -71295,13 +72082,13 @@ var init_folder = __esm(async () => {
71295
72082
  info(colors.yellow(`No rule matches path '${testPath}' (relative: '${relative7}')`));
71296
72083
  }
71297
72084
  });
71298
- folder_default = command13;
72085
+ folder_default = command14;
71299
72086
  });
71300
72087
 
71301
72088
  // src/commands/variable/variable.ts
71302
72089
  import { mkdir as mkdir9, stat as stat12, writeFile as writeFile12 } from "node:fs/promises";
71303
72090
  import { dirname as dirname14 } from "node:path";
71304
- import { sep as SEP15 } from "node:path";
72091
+ import { sep as SEP16 } from "node:path";
71305
72092
  async function list8(opts) {
71306
72093
  if (opts.json)
71307
72094
  setSilent(true);
@@ -71370,7 +72157,7 @@ async function pushVariable(workspace, remotePath, variable, localVariable, plai
71370
72157
  try {
71371
72158
  variable = await getVariable({
71372
72159
  workspace,
71373
- path: remotePath.replaceAll(SEP15, "/"),
72160
+ path: remotePath.replaceAll(SEP16, "/"),
71374
72161
  decryptSecret: plainSecrets,
71375
72162
  includeEncrypted: true
71376
72163
  });
@@ -71386,7 +72173,7 @@ async function pushVariable(workspace, remotePath, variable, localVariable, plai
71386
72173
  debug(`Variable ${remotePath} is not up-to-date, updating`);
71387
72174
  await updateVariable({
71388
72175
  workspace,
71389
- path: remotePath.replaceAll(SEP15, "/"),
72176
+ path: remotePath.replaceAll(SEP16, "/"),
71390
72177
  alreadyEncrypted: !plainSecrets,
71391
72178
  requestBody: {
71392
72179
  ...localVariable,
@@ -71399,7 +72186,7 @@ async function pushVariable(workspace, remotePath, variable, localVariable, plai
71399
72186
  workspace,
71400
72187
  alreadyEncrypted: !plainSecrets,
71401
72188
  requestBody: {
71402
- path: remotePath.replaceAll(SEP15, "/"),
72189
+ path: remotePath.replaceAll(SEP16, "/"),
71403
72190
  ...localVariable
71404
72191
  }
71405
72192
  });
@@ -71445,7 +72232,7 @@ async function add2(opts, value, remotePath) {
71445
72232
  }, true);
71446
72233
  info(colors.bold.underline.green(`Variable ${remotePath} pushed`));
71447
72234
  }
71448
- var import_yaml25, command14, variable_default;
72235
+ var import_yaml25, command15, variable_default;
71449
72236
  var init_variable = __esm(async () => {
71450
72237
  init_mod3();
71451
72238
  init_mod6();
@@ -71459,14 +72246,14 @@ var init_variable = __esm(async () => {
71459
72246
  init_confirm()
71460
72247
  ]);
71461
72248
  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;
72249
+ 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);
72250
+ variable_default = command15;
71464
72251
  });
71465
72252
 
71466
72253
  // src/commands/schedule/schedule.ts
71467
72254
  import { mkdir as mkdir10, stat as stat13, writeFile as writeFile13 } from "node:fs/promises";
71468
72255
  import { dirname as dirname15 } from "node:path";
71469
- import { sep as SEP16 } from "node:path";
72256
+ import { sep as SEP17 } from "node:path";
71470
72257
  async function list9(opts) {
71471
72258
  if (opts.json)
71472
72259
  setSilent(true);
@@ -71530,7 +72317,7 @@ async function get7(opts, path19) {
71530
72317
  }
71531
72318
  }
71532
72319
  async function pushSchedule(workspace, path19, schedule, localSchedule, permissionedAsContext) {
71533
- path19 = removeType(path19, "schedule").replaceAll(SEP16, "/");
72320
+ path19 = removeType(path19, "schedule").replaceAll(SEP17, "/");
71534
72321
  debug(`Processing local schedule ${path19}`);
71535
72322
  try {
71536
72323
  schedule = await getSchedule({ workspace, path: path19 });
@@ -71565,7 +72352,7 @@ async function pushSchedule(workspace, path19, schedule, localSchedule, permissi
71565
72352
  ...preserveFields
71566
72353
  }
71567
72354
  });
71568
- if (localSchedule.enabled != schedule.enabled) {
72355
+ if (localSchedule.enabled !== undefined && localSchedule.enabled !== schedule.enabled) {
71569
72356
  info(colors.bold.yellow(`Schedule ${path19} is ${localSchedule.enabled ? "enabled" : "disabled"} locally but not on remote, updating remote`));
71570
72357
  await setScheduleEnabled({
71571
72358
  workspace,
@@ -71600,13 +72387,31 @@ async function enable(opts, path19) {
71600
72387
  opts = await mergeConfigWithConfigFile(opts);
71601
72388
  const workspace = await resolveWorkspace(opts);
71602
72389
  await requireLogin(opts);
71603
- await setScheduleEnabled({
71604
- workspace: workspace.workspaceId,
71605
- path: path19,
71606
- requestBody: { enabled: true }
71607
- });
72390
+ try {
72391
+ await setScheduleEnabled({
72392
+ workspace: workspace.workspaceId,
72393
+ path: path19,
72394
+ requestBody: { enabled: true, force: opts.force }
72395
+ });
72396
+ } catch (e) {
72397
+ const conflict = parseForkConflict(e);
72398
+ if (conflict) {
72399
+ error(`Cannot enable schedule '${path19}': the parent workspace '${conflict.parentWorkspaceId}' has the same path configured. ` + `Both crons would fire on every tick and the script would run twice per scheduled time.
72400
+ ` + `Re-run with --force to enable anyway.`);
72401
+ process.exit(1);
72402
+ }
72403
+ throw e;
72404
+ }
71608
72405
  info(colors.green(`Schedule ${path19} enabled.`));
71609
72406
  }
72407
+ function parseForkConflict(e) {
72408
+ const body = e?.body;
72409
+ const raw = typeof body === "string" ? body : e?.message ?? "";
72410
+ const m = String(raw).match(/fork-conflict:([^:]+):(.+)/);
72411
+ if (!m)
72412
+ return;
72413
+ return { kind: m[1], parentWorkspaceId: m[2].trim() };
72414
+ }
71610
72415
  async function disable(opts, path19) {
71611
72416
  opts = await mergeConfigWithConfigFile(opts);
71612
72417
  const workspace = await resolveWorkspace(opts);
@@ -71632,7 +72437,7 @@ async function push8(opts, filePath, remotePath) {
71632
72437
  await pushSchedule(workspace.workspaceId, remotePath, undefined, parseFromFile(filePath));
71633
72438
  console.log(colors.bold.underline.green("Schedule pushed"));
71634
72439
  }
71635
- var import_yaml26, command15, schedule_default;
72440
+ var import_yaml26, command16, schedule_default;
71636
72441
  var init_schedule = __esm(async () => {
71637
72442
  init_mod3();
71638
72443
  init_mod6();
@@ -71647,7 +72452,7 @@ var init_schedule = __esm(async () => {
71647
72452
  init_types()
71648
72453
  ]);
71649
72454
  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) => {
72455
+ 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").option("--force", "Bypass the fork-conflict warning when the parent workspace has the same schedule (acknowledges that both crons will fire)").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
72456
  const workspace = await resolveWorkspace(opts);
71652
72457
  await requireLogin(opts);
71653
72458
  const cache3 = new Map;
@@ -71669,7 +72474,7 @@ var init_schedule = __esm(async () => {
71669
72474
  });
71670
72475
  info(colors.green(`Updated permissioned_as for schedule ${schedulePath} to ${email} (username: ${username})`));
71671
72476
  });
71672
- schedule_default = command15;
72477
+ schedule_default = command16;
71673
72478
  });
71674
72479
 
71675
72480
  // src/commands/instance/slack.ts
@@ -72763,7 +73568,7 @@ async function whoami3(opts) {
72763
73568
  error(colors.red(`Failed to retrieve whoami information: ${error2.message}`));
72764
73569
  }
72765
73570
  }
72766
- var import_yaml29, command16, instance_default;
73571
+ var import_yaml29, command17, instance_default;
72767
73572
  var init_instance = __esm(async () => {
72768
73573
  init_colors2();
72769
73574
  init_mod3();
@@ -72788,7 +73593,7 @@ var init_instance = __esm(async () => {
72788
73593
  init_workspace()
72789
73594
  ]);
72790
73595
  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 () => {
73596
+ command17 = new Command().description("sync local with a remote instance or the opposite (push or pull)").action(async () => {
72792
73597
  info("4 actions available, add, remove, switch, pull and push. Use -h to display help.");
72793
73598
  const activeInstance = await getActiveInstance({});
72794
73599
  new Table2().header(["name", "remote", "token"]).padding(2).border(true).body((await allInstances()).map((x) => [
@@ -72814,7 +73619,7 @@ var init_instance = __esm(async () => {
72814
73619
  await removeInstance(choice);
72815
73620
  info(colors.green.underline(`Removed instance ${choice}`));
72816
73621
  }).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;
73622
+ instance_default = command17;
72818
73623
  });
72819
73624
 
72820
73625
  // src/commands/user/user.ts
@@ -73169,7 +73974,7 @@ async function pushInstanceGroups(opts, preview2 = false) {
73169
73974
  info(colors.green("Groups pushed to the instance"));
73170
73975
  }
73171
73976
  }
73172
- var import_yaml31, INSTANCE_USERS_PATH = "instance_users.yaml", instanceUsersPath, INSTANCE_GROUPS_PATH = "instance_groups.yaml", instanceGroupsPath, command17, user_default;
73977
+ var import_yaml31, INSTANCE_USERS_PATH = "instance_users.yaml", instanceUsersPath, INSTANCE_GROUPS_PATH = "instance_groups.yaml", instanceGroupsPath, command18, user_default;
73173
73978
  var init_user = __esm(async () => {
73174
73979
  init_colors2();
73175
73980
  init_mod3();
@@ -73185,12 +73990,12 @@ var init_user = __esm(async () => {
73185
73990
  import_yaml31 = __toESM(require_dist(), 1);
73186
73991
  instanceUsersPath = INSTANCE_USERS_PATH;
73187
73992
  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.", {
73993
+ 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
73994
  depends: ["password"]
73190
73995
  }).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
73996
  depends: ["email"]
73192
73997
  }).action(createToken2);
73193
- user_default = command17;
73998
+ user_default = command18;
73194
73999
  });
73195
74000
 
73196
74001
  // src/commands/dependencies/dependencies.ts
@@ -73239,7 +74044,7 @@ async function pushWorkspaceDependencies(workspace, path20, _befObj, newDependen
73239
74044
  });
73240
74045
  info(colors.green(`Successfully pushed ${displayName} for ${language}`));
73241
74046
  }
73242
- var command18, dependencies_default;
74047
+ var command19, dependencies_default;
73243
74048
  var init_dependencies = __esm(async () => {
73244
74049
  init_colors2();
73245
74050
  init_mod3();
@@ -73251,14 +74056,14 @@ var init_dependencies = __esm(async () => {
73251
74056
  init_metadata(),
73252
74057
  init_utils()
73253
74058
  ]);
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;
74059
+ 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);
74060
+ dependencies_default = command19;
73256
74061
  });
73257
74062
 
73258
74063
  // src/commands/trigger/trigger.ts
73259
74064
  import { mkdir as mkdir12, stat as stat15, writeFile as writeFile17 } from "node:fs/promises";
73260
74065
  import { dirname as dirname16 } from "node:path";
73261
- import { sep as SEP17 } from "node:path";
74066
+ import { sep as SEP18 } from "node:path";
73262
74067
  async function getTrigger(triggerType, workspace, path20) {
73263
74068
  const triggerFunctions = {
73264
74069
  http: getHttpTrigger,
@@ -73309,7 +74114,7 @@ async function createTrigger(triggerType, workspace, path20, trigger) {
73309
74114
  await triggerFunction({ workspace, path: path20, requestBody: trigger });
73310
74115
  }
73311
74116
  async function pushTrigger(triggerType, workspace, path20, trigger, localTrigger, permissionedAsContext) {
73312
- path20 = removeType(path20, triggerType + "_trigger").replaceAll(SEP17, "/");
74117
+ path20 = removeType(path20, triggerType + "_trigger").replaceAll(SEP18, "/");
73313
74118
  debug(`Processing local ${triggerType} trigger ${path20}`);
73314
74119
  try {
73315
74120
  trigger = await getTrigger(triggerType, workspace, path20);
@@ -73615,7 +74420,7 @@ async function push10(opts, filePath, remotePath) {
73615
74420
  await pushTrigger(triggerKind, workspace.workspaceId, remotePath, undefined, parseFromFile(filePath));
73616
74421
  console.log(colors.bold.underline.green("Trigger pushed"));
73617
74422
  }
73618
- var import_yaml33, triggerTemplates, TRIGGER_SKIP_FIELDS, command19, trigger_default;
74423
+ var import_yaml33, triggerTemplates, TRIGGER_SKIP_FIELDS, command20, trigger_default;
73619
74424
  var init_trigger = __esm(async () => {
73620
74425
  init_services_gen();
73621
74426
  init_mod3();
@@ -73721,7 +74526,7 @@ var init_trigger = __esm(async () => {
73721
74526
  }
73722
74527
  };
73723
74528
  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) => {
74529
+ 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
74530
  const workspace = await resolveWorkspace(opts);
73726
74531
  await requireLogin(opts);
73727
74532
  if (!opts.kind) {
@@ -73744,12 +74549,12 @@ var init_trigger = __esm(async () => {
73744
74549
  });
73745
74550
  info(colors.green(`Updated permissioned_as for ${opts.kind} trigger ${triggerPath} to ${email} (username: ${username})`));
73746
74551
  });
73747
- trigger_default = command19;
74552
+ trigger_default = command20;
73748
74553
  });
73749
74554
 
73750
74555
  // src/types.ts
73751
74556
  import * as path20 from "node:path";
73752
- import { sep as SEP18 } from "node:path";
74557
+ import { sep as SEP19 } from "node:path";
73753
74558
  function isSuperset(subset, superset) {
73754
74559
  return Object.keys(subset).every((key) => {
73755
74560
  const eq = deepEqual(subset[key], superset[key]);
@@ -73893,7 +74698,7 @@ function getTypeStrFromPath(p) {
73893
74698
  if (isRawAppPath(p)) {
73894
74699
  return "raw_app";
73895
74700
  }
73896
- if (p.startsWith("dependencies" + SEP18)) {
74701
+ if (p.startsWith("dependencies" + SEP19)) {
73897
74702
  return "workspace_dependencies";
73898
74703
  }
73899
74704
  if (isFileResource(p) || isFilesetResource(p)) {
@@ -73923,7 +74728,7 @@ function getTypeStrFromPath(p) {
73923
74728
  }
73924
74729
  }
73925
74730
  function removeType(str, type) {
73926
- const normalizedStr = path20.normalize(str).replaceAll(SEP18, "/");
74731
+ const normalizedStr = path20.normalize(str).replaceAll(SEP19, "/");
73927
74732
  if (normalizedStr.endsWith("." + type + ".yaml") || normalizedStr.endsWith("." + type + ".json")) {
73928
74733
  return normalizedStr.slice(0, normalizedStr.length - type.length - 6);
73929
74734
  }
@@ -73933,7 +74738,7 @@ function removeType(str, type) {
73933
74738
  return normalizedStr;
73934
74739
  }
73935
74740
  function extractNativeTriggerInfo(p) {
73936
- const normalizedPath = path20.normalize(p).replaceAll(SEP18, "/");
74741
+ const normalizedPath = path20.normalize(p).replaceAll(SEP19, "/");
73937
74742
  const withoutExt = normalizedPath.replace(/\.(json|yaml)$/, "");
73938
74743
  const match2 = withoutExt.match(/^(.+)\.(flow|script)\.([^.]+)\.(\w+)_native_trigger$/);
73939
74744
  if (!match2) {
@@ -73947,8 +74752,8 @@ function extractNativeTriggerInfo(p) {
73947
74752
  };
73948
74753
  }
73949
74754
  function removePathPrefix(str, prefix) {
73950
- const normalizedStr = path20.normalize(str).replaceAll(SEP18, "/");
73951
- const normalizedPrefix = path20.normalize(prefix).replaceAll(SEP18, "/");
74755
+ const normalizedStr = path20.normalize(str).replaceAll(SEP19, "/");
74756
+ const normalizedPrefix = path20.normalize(prefix).replaceAll(SEP19, "/");
73952
74757
  if (normalizedStr === normalizedPrefix) {
73953
74758
  return "";
73954
74759
  }
@@ -74120,7 +74925,7 @@ var init_local_path_scripts = __esm(async () => {
74120
74925
  });
74121
74926
 
74122
74927
  // src/commands/flow/flow.ts
74123
- import { sep as SEP19 } from "node:path";
74928
+ import { sep as SEP20 } from "node:path";
74124
74929
  import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync7 } from "node:fs";
74125
74930
  function normalizeOptionalString(value) {
74126
74931
  return typeof value === "string" && value.trim() === "" ? undefined : value ?? undefined;
@@ -74181,7 +74986,7 @@ async function pushFlow(workspace, remotePath, localPath, message, permissionedA
74181
74986
  return;
74182
74987
  }
74183
74988
  alreadySynced3.push(localPath);
74184
- remotePath = remotePath.replaceAll(SEP19, "/");
74989
+ remotePath = remotePath.replaceAll(SEP20, "/");
74185
74990
  let flow = undefined;
74186
74991
  try {
74187
74992
  flow = await getFlowByPath({
@@ -74189,18 +74994,18 @@ async function pushFlow(workspace, remotePath, localPath, message, permissionedA
74189
74994
  path: remotePath
74190
74995
  });
74191
74996
  } catch {}
74192
- if (!localPath.endsWith(SEP19)) {
74193
- localPath += SEP19;
74997
+ if (!localPath.endsWith(SEP20)) {
74998
+ localPath += SEP20;
74194
74999
  }
74195
75000
  const localFlow = await yamlParseFile(localPath + "flow.yaml");
74196
75001
  const fileReader = async (path21) => await readTextFile(localPath + path21);
74197
75002
  const missingFiles = [];
74198
- await replaceInlineScripts(localFlow.value.modules, fileReader, exports_log, localPath, SEP19, undefined, missingFiles);
75003
+ await replaceInlineScripts(localFlow.value.modules, fileReader, exports_log, localPath, SEP20, undefined, missingFiles);
74199
75004
  if (localFlow.value.failure_module) {
74200
- await replaceInlineScripts([localFlow.value.failure_module], fileReader, exports_log, localPath, SEP19, undefined, missingFiles);
75005
+ await replaceInlineScripts([localFlow.value.failure_module], fileReader, exports_log, localPath, SEP20, undefined, missingFiles);
74201
75006
  }
74202
75007
  if (localFlow.value.preprocessor_module) {
74203
- await replaceInlineScripts([localFlow.value.preprocessor_module], fileReader, exports_log, localPath, SEP19, undefined, missingFiles);
75008
+ await replaceInlineScripts([localFlow.value.preprocessor_module], fileReader, exports_log, localPath, SEP20, undefined, missingFiles);
74204
75009
  }
74205
75010
  if (missingFiles.length > 0) {
74206
75011
  warn(colors.yellow(`Warning: missing inline script file(s): ${missingFiles.join(", ")}. ` + `The flow will be pushed with unresolved !inline references.`));
@@ -74223,9 +75028,9 @@ async function pushFlow(workspace, remotePath, localPath, message, permissionedA
74223
75028
  info(colors.bold.yellow(`Updating flow ${remotePath}...`));
74224
75029
  await updateFlow({
74225
75030
  workspace,
74226
- path: remotePath.replaceAll(SEP19, "/"),
75031
+ path: remotePath.replaceAll(SEP20, "/"),
74227
75032
  requestBody: {
74228
- path: remotePath.replaceAll(SEP19, "/"),
75033
+ path: remotePath.replaceAll(SEP20, "/"),
74229
75034
  deployment_message: message,
74230
75035
  ...localFlow,
74231
75036
  ...preserveFields
@@ -74237,7 +75042,7 @@ async function pushFlow(workspace, remotePath, localPath, message, permissionedA
74237
75042
  await createFlow({
74238
75043
  workspace,
74239
75044
  requestBody: {
74240
- path: remotePath.replaceAll(SEP19, "/"),
75045
+ path: remotePath.replaceAll(SEP20, "/"),
74241
75046
  deployment_message: message,
74242
75047
  ...localFlow,
74243
75048
  ...preserveFields
@@ -74486,25 +75291,25 @@ async function preview2(opts, flowPath) {
74486
75291
  const workspace = await resolveWorkspace(opts);
74487
75292
  await requireLogin(opts);
74488
75293
  const codebases = useLocalPathScripts ? listSyncCodebases(opts) : [];
74489
- const isFlowDir = flowPath.endsWith(".flow") || flowPath.endsWith(".flow" + SEP19) || flowPath.endsWith("__flow") || flowPath.endsWith("__flow" + SEP19);
75294
+ const isFlowDir = flowPath.endsWith(".flow") || flowPath.endsWith(".flow" + SEP20) || flowPath.endsWith("__flow") || flowPath.endsWith("__flow" + SEP20);
74490
75295
  if (!isFlowDir) {
74491
75296
  if (flowPath.endsWith("flow.yaml") || flowPath.endsWith("flow.json")) {
74492
- flowPath = flowPath.substring(0, flowPath.lastIndexOf(SEP19));
75297
+ flowPath = flowPath.substring(0, flowPath.lastIndexOf(SEP20));
74493
75298
  } else {
74494
75299
  throw new Error("Flow path must be a .flow/__flow directory or a flow.yaml file");
74495
75300
  }
74496
75301
  }
74497
- if (!flowPath.endsWith(SEP19)) {
74498
- flowPath += SEP19;
75302
+ if (!flowPath.endsWith(SEP20)) {
75303
+ flowPath += SEP20;
74499
75304
  }
74500
75305
  const localFlow = await yamlParseFile(flowPath + "flow.yaml");
74501
75306
  const fileReader = async (path21) => await readTextFile(flowPath + path21);
74502
- await replaceInlineScripts(localFlow.value.modules, fileReader, exports_log, flowPath, SEP19);
75307
+ await replaceInlineScripts(localFlow.value.modules, fileReader, exports_log, flowPath, SEP20);
74503
75308
  if (localFlow.value.failure_module) {
74504
- await replaceInlineScripts([localFlow.value.failure_module], fileReader, exports_log, flowPath, SEP19);
75309
+ await replaceInlineScripts([localFlow.value.failure_module], fileReader, exports_log, flowPath, SEP20);
74505
75310
  }
74506
75311
  if (localFlow.value.preprocessor_module) {
74507
- await replaceInlineScripts([localFlow.value.preprocessor_module], fileReader, exports_log, flowPath, SEP19);
75312
+ await replaceInlineScripts([localFlow.value.preprocessor_module], fileReader, exports_log, flowPath, SEP20);
74508
75313
  }
74509
75314
  if (useLocalPathScripts) {
74510
75315
  const scriptPaths = collectPathScriptPaths(localFlow.value);
@@ -74534,7 +75339,7 @@ async function preview2(opts, flowPath) {
74534
75339
  workspace: workspace.workspaceId,
74535
75340
  requestBody: {
74536
75341
  value: localFlow.value,
74537
- path: flowPath.substring(0, flowPath.indexOf(".flow")).replaceAll(SEP19, "/"),
75342
+ path: flowPath.substring(0, flowPath.indexOf(".flow")).replaceAll(SEP20, "/"),
74538
75343
  args: input
74539
75344
  }
74540
75345
  });
@@ -74566,8 +75371,8 @@ async function generateLocks(opts, folder) {
74566
75371
  } else {
74567
75372
  const ignore = await ignoreF(opts);
74568
75373
  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)));
75374
+ return ignore(p, isD) || !isD && !p.endsWith(SEP20 + "flow.yaml") && !p.endsWith(SEP20 + "flow.json");
75375
+ }, false, {})).map((x) => x.substring(0, x.lastIndexOf(SEP20)));
74571
75376
  let hasAny = false;
74572
75377
  for (const folder2 of elems) {
74573
75378
  const candidate = await generateFlowLockInternal(folder2, true, workspace, opts);
@@ -74666,7 +75471,7 @@ async function showVersion(opts, flowPath, version) {
74666
75471
  console.log(JSON.stringify(flow.value, null, 2));
74667
75472
  }
74668
75473
  }
74669
- var import_yaml36, alreadySynced3, command20, flow_default;
75474
+ var import_yaml36, alreadySynced3, command21, flow_default;
74670
75475
  var init_flow = __esm(async () => {
74671
75476
  init_colors2();
74672
75477
  init_mod3();
@@ -74691,7 +75496,7 @@ var init_flow = __esm(async () => {
74691
75496
  ]);
74692
75497
  import_yaml36 = __toESM(require_dist(), 1);
74693
75498
  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) => {
75499
+ 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
75500
  const workspace = await resolveWorkspace(opts);
74696
75501
  await requireLogin(opts);
74697
75502
  const remote = await getFlowByPath({
@@ -74712,7 +75517,7 @@ var init_flow = __esm(async () => {
74712
75517
  });
74713
75518
  info(colors.green(`Updated permissioned_as for flow ${flowPath} to ${email}`));
74714
75519
  });
74715
- flow_default = command20;
75520
+ flow_default = command21;
74716
75521
  });
74717
75522
 
74718
75523
  // src/commands/gitsync-settings/converter.ts
@@ -75651,286 +76456,15 @@ __export(exports_gitsync_settings, {
75651
76456
  pullGitSyncSettings: () => pullGitSyncSettings,
75652
76457
  default: () => gitsync_settings_default
75653
76458
  });
75654
- var command22, gitsync_settings_default;
76459
+ var command23, gitsync_settings_default;
75655
76460
  var init_gitsync_settings = __esm(async () => {
75656
76461
  init_mod3();
75657
76462
  await __promiseAll([
75658
76463
  init_pull2(),
75659
76464
  init_push()
75660
76465
  ]);
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
- ]);
76466
+ 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);
76467
+ gitsync_settings_default = command23;
75934
76468
  });
75935
76469
 
75936
76470
  // src/main.ts
@@ -77207,8 +77741,8 @@ async function pull2(opts) {
77207
77741
  await pushResourceType(workspace.workspaceId, x.name + ".resource-type.json", undefined, x);
77208
77742
  }
77209
77743
  }
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;
77744
+ var command22 = new Command().name("hub").description("Hub related commands. EXPERIMENTAL. INTERNAL USE ONLY.").command("pull").description("pull any supported definitions. EXPERIMENTAL.").action(pull2);
77745
+ var hub_default = command22;
77212
77746
 
77213
77747
  // src/main.ts
77214
77748
  await __promiseAll([
@@ -77299,8 +77833,8 @@ async function pushWorkerGroups(opts) {
77299
77833
  await pushInstanceConfigs(opts, false);
77300
77834
  }
77301
77835
  }
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;
77836
+ 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);
77837
+ var worker_groups_default = command24;
77304
77838
 
77305
77839
  // src/main.ts
77306
77840
  await init_lint();
@@ -77329,7 +77863,7 @@ await __promiseAll([
77329
77863
  init_local_path_scripts()
77330
77864
  ]);
77331
77865
  var import_yaml40 = __toESM(require_dist(), 1);
77332
- import { sep as SEP20 } from "node:path";
77866
+ import { sep as SEP21 } from "node:path";
77333
77867
  import * as http3 from "node:http";
77334
77868
  import * as https from "node:https";
77335
77869
  import { access, readdir as readdir10, realpath, stat as stat17, unlink, writeFile as writeFile19 } from "node:fs/promises";
@@ -77582,7 +78116,7 @@ async function dev2(opts) {
77582
78116
  if (paths.length == 0) {
77583
78117
  return;
77584
78118
  }
77585
- const nativePath = (await realpath(paths[0])).replace(base + SEP20, "");
78119
+ const nativePath = (await realpath(paths[0])).replace(base + SEP21, "");
77586
78120
  const cpath = nativePath.replaceAll("\\", "/");
77587
78121
  const insideFlow = isInsideFlowFolder(cpath);
77588
78122
  if (insideFlow || !ignore(nativePath, false)) {
@@ -77599,7 +78133,7 @@ async function dev2(opts) {
77599
78133
  return;
77600
78134
  const wmFlowPath = stripFolderSuffix(localPath.replace(/\/$/, ""), FLOW_SUFFIXES);
77601
78135
  const localFlow = await yamlParseFile(localPath + "flow.yaml");
77602
- await replaceInlineScripts(localFlow.value.modules, async (path22) => await readTextFile(localPath + path22), exports_log, localPath, SEP20, undefined);
78136
+ await replaceInlineScripts(localFlow.value.modules, async (path22) => await readTextFile(localPath + path22), exports_log, localPath, SEP21, undefined);
77603
78137
  snapshotPathScripts(localFlow.value);
77604
78138
  const localScriptReader = createPreviewLocalScriptReader({
77605
78139
  exts,
@@ -77652,7 +78186,7 @@ async function dev2(opts) {
77652
78186
  if (!flowDir || !flowYaml)
77653
78187
  throw new Error("not a flow");
77654
78188
  const localFlow = await yamlParseFile(flowYaml);
77655
- await replaceInlineScripts(localFlow.value.modules, async (p) => await readTextFile(flowDir + p), exports_log, flowDir, SEP20, undefined);
78189
+ await replaceInlineScripts(localFlow.value.modules, async (p) => await readTextFile(flowDir + p), exports_log, flowDir, SEP21, undefined);
77656
78190
  snapshotPathScripts(localFlow.value);
77657
78191
  const localScriptReader = createPreviewLocalScriptReader({
77658
78192
  exts,
@@ -77999,8 +78533,8 @@ async function dev2(opts) {
77999
78533
  await Promise.all([startServer(), watchChanges()]);
78000
78534
  console.log("Stopped dev mode");
78001
78535
  }
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;
78536
+ 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);
78537
+ var dev_default2 = command25;
78004
78538
 
78005
78539
  // src/main.ts
78006
78540
  init_gen();
@@ -78114,8 +78648,8 @@ Worker Group: ${group.groupName} (${group.workers.length} workers)`);
78114
78648
  info("Use 'wmill instance add' to add a new instance");
78115
78649
  }
78116
78650
  }
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;
78651
+ 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);
78652
+ var workers_default = command26;
78119
78653
 
78120
78654
  // src/commands/queues/queues.ts
78121
78655
  init_mod3();
@@ -78223,8 +78757,8 @@ async function displayQueues(opts, workspace) {
78223
78757
  info("Use 'wmill instance add' to add a new instance");
78224
78758
  }
78225
78759
  }
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;
78760
+ 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);
78761
+ var queues_default = command27;
78228
78762
 
78229
78763
  // src/main.ts
78230
78764
  await init_dependencies();
@@ -78276,7 +78810,7 @@ When a new app needs to be created, YOU run \`wmill app new\` yourself with \`--
78276
78810
 
78277
78811
  ## Triggers
78278
78812
 
78279
- You MUST use the \`triggers\` skill to configure HTTP routes, WebSocket, Kafka, NATS, SQS, MQTT, GCP, or Postgres CDC triggers.
78813
+ You MUST use the \`triggers\` skill to configure HTTP routes, WebSocket, Kafka, NATS, SQS, MQTT, GCP, Azure, Email, or Postgres CDC triggers.
78280
78814
 
78281
78815
  ## Schedules
78282
78816
 
@@ -83871,6 +84405,49 @@ Examples:
83871
84405
  - \`u/user/webhook.http_trigger.yaml\`
83872
84406
  - \`f/data/kafka_consumer.kafka_trigger.yaml\`
83873
84407
  - \`f/sync/postgres_cdc.postgres_trigger.yaml\`
84408
+ - \`f/inbound/orders.email_trigger.yaml\`
84409
+
84410
+ ## Email Triggers
84411
+
84412
+ 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.
84413
+
84414
+ 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).
84415
+
84416
+ ### Payload
84417
+
84418
+ The runnable receives:
84419
+
84420
+ - \`parsed_email\` — \`{ headers, text_body, html_body, attachments[] }\`. Each \`attachment\` has \`{ headers, body }\`.
84421
+ - \`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.
84422
+ - \`email_extra_args\` (optional, only when sender appended \`+key=value\` extras) — a flat object of the parsed extras.
84423
+
84424
+ 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.
84425
+
84426
+ ### Attachments are S3 objects
84427
+
84428
+ Binary attachments are uploaded to the workspace S3 bucket and surface in \`parsed_email.attachments[i].body\` as:
84429
+
84430
+ \`\`\`json
84431
+ { "s3": "windmill_emails/<job_id>/attachments/<filename>" }
84432
+ \`\`\`
84433
+
84434
+ To read the bytes inside a script, use the wmill SDK:
84435
+
84436
+ \`\`\`ts
84437
+ // TypeScript
84438
+ import * as wmill from "windmill-client"
84439
+ const file = await wmill.loadS3File(parsed_email.attachments[0].body)
84440
+ \`\`\`
84441
+
84442
+ \`\`\`python
84443
+ # Python
84444
+ import wmill
84445
+ data = wmill.load_s3_file(parsed_email["attachments"][0]["body"])
84446
+ \`\`\`
84447
+
84448
+ 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.
84449
+
84450
+ Text/HTML/inline parts are placed inline in \`body\` as strings.
83874
84451
 
83875
84452
  ## CLI Commands
83876
84453
 
@@ -84804,6 +85381,15 @@ Generate metadata (locks, schemas) for all scripts, flows, and apps
84804
85381
  - \`-i --includes <patterns:file[]>\` - Comma separated patterns to specify which files to include
84805
85382
  - \`-e --excludes <patterns:file[]>\` - Comma separated patterns to specify which files to exclude
84806
85383
 
85384
+ **Subcommands:**
85385
+
85386
+ - \`generate-metadata rehash [folder:string]\`
85387
+ - \`--skip-scripts\` - Skip processing scripts
85388
+ - \`--skip-flows\` - Skip processing flows
85389
+ - \`--skip-apps\` - Skip processing apps
85390
+ - \`-i --includes <patterns:file[]>\` - Comma separated patterns to specify which files to include
85391
+ - \`-e --excludes <patterns:file[]>\` - Comma separated patterns to specify which files to exclude
85392
+
84807
85393
  ### gitsync-settings
84808
85394
 
84809
85395
  Manage git-sync settings between local wmill.yaml and Windmill backend
@@ -85012,6 +85598,7 @@ schedule related commands
85012
85598
  - \`schedule new <path:string>\` - create a new schedule locally
85013
85599
  - \`schedule push <file_path:string> <remote_path:string>\` - push a local schedule spec. This overrides any remote versions.
85014
85600
  - \`schedule enable <path:string>\` - Enable a schedule
85601
+ - \`--force\` - Bypass the fork-conflict warning when the parent workspace has the same schedule (acknowledges that both crons will fire)
85015
85602
  - \`schedule disable <path:string>\` - Disable a schedule
85016
85603
  - \`schedule set-permissioned-as <path:string> <email:string>\` - Set the email (run-as user) for a schedule (requires admin or wm_deployers group)
85017
85604
 
@@ -85477,6 +86064,71 @@ required:
85477
86064
  - azure_mode
85478
86065
  - scope_resource_id
85479
86066
  - subscription_name
86067
+ `,
86068
+ email_trigger: `type: object
86069
+ properties:
86070
+ script_path:
86071
+ type: string
86072
+ description: Path to the script or flow to execute when triggered
86073
+ permissioned_as:
86074
+ type: string
86075
+ description: The user or group this trigger runs as (permissioned_as)
86076
+ is_flow:
86077
+ type: boolean
86078
+ description: True if script_path points to a flow, false if it points to a script
86079
+ labels:
86080
+ type: array
86081
+ items:
86082
+ type: string
86083
+ local_part:
86084
+ type: string
86085
+ workspaced_local_part:
86086
+ type: boolean
86087
+ error_handler_path:
86088
+ type: string
86089
+ error_handler_args:
86090
+ type: object
86091
+ description: The arguments to pass to the script or flow
86092
+ retry:
86093
+ type: object
86094
+ properties:
86095
+ constant:
86096
+ type: object
86097
+ description: Retry with constant delay between attempts
86098
+ properties:
86099
+ attempts:
86100
+ type: integer
86101
+ description: Number of retry attempts
86102
+ seconds:
86103
+ type: integer
86104
+ description: Seconds to wait between retries
86105
+ exponential:
86106
+ type: object
86107
+ description: Retry with exponential backoff (delay doubles each time)
86108
+ properties:
86109
+ attempts:
86110
+ type: integer
86111
+ description: Number of retry attempts
86112
+ multiplier:
86113
+ type: integer
86114
+ description: Multiplier for exponential backoff
86115
+ seconds:
86116
+ type: integer
86117
+ minimum: 1
86118
+ description: Initial delay in seconds
86119
+ random_factor:
86120
+ type: integer
86121
+ minimum: 0
86122
+ maximum: 100
86123
+ description: Random jitter percentage (0-100) to avoid thundering herd
86124
+ retry_if:
86125
+ $ref: '#/components/schemas/RetryIf'
86126
+ description: Retry configuration for failed module executions
86127
+ required:
86128
+ - script_path
86129
+ - permissioned_as
86130
+ - is_flow
86131
+ - local_part
85480
86132
  `,
85481
86133
  gcp_trigger: `type: object
85482
86134
  properties:
@@ -86404,7 +87056,8 @@ var SCHEMA_MAPPINGS = {
86404
87056
  { name: "MqttTrigger", schemaKey: "mqtt_trigger", filePattern: "*.mqtt_trigger.yaml" },
86405
87057
  { name: "SqsTrigger", schemaKey: "sqs_trigger", filePattern: "*.sqs_trigger.yaml" },
86406
87058
  { name: "GcpTrigger", schemaKey: "gcp_trigger", filePattern: "*.gcp_trigger.yaml" },
86407
- { name: "AzureTrigger", schemaKey: "azure_trigger", filePattern: "*.azure_trigger.yaml" }
87059
+ { name: "AzureTrigger", schemaKey: "azure_trigger", filePattern: "*.azure_trigger.yaml" },
87060
+ { name: "EmailTrigger", schemaKey: "email_trigger", filePattern: "*.email_trigger.yaml" }
86408
87061
  ],
86409
87062
  schedules: [
86410
87063
  { name: "Schedule", schemaKey: "schedule", filePattern: "*.schedule.yaml" }
@@ -87225,8 +87878,8 @@ async function initAction(opts) {
87225
87878
  info(colors.gray("Skipped resource type namespace generation (no workspace bound). Run 'wmill workspace bind' then 'wmill init' to generate it."));
87226
87879
  }
87227
87880
  }
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;
87881
+ 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);
87882
+ var init_default = command28;
87230
87883
 
87231
87884
  // src/commands/jobs/jobs.ts
87232
87885
  init_mod3();
@@ -87392,8 +88045,8 @@ Warning: Found ${workers.length} active worker(s) on the instance.`));
87392
88045
  }
87393
88046
  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
88047
  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;
88048
+ var command29 = new Command().description("Manage jobs (import/export)").command("pull", pull3).command("push", push12);
88049
+ var jobs_default = command29;
87397
88050
 
87398
88051
  // src/commands/job/job.ts
87399
88052
  init_mod3();
@@ -87707,8 +88360,8 @@ async function cancel(opts, id) {
87707
88360
  info(colors.green(`Job ${id} canceled.`));
87708
88361
  }
87709
88362
  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;
88363
+ 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);
88364
+ var job_default = command30;
87712
88365
 
87713
88366
  // src/commands/group/group.ts
87714
88367
  init_mod3();
@@ -87807,8 +88460,8 @@ async function removeUser(opts, name, username) {
87807
88460
  });
87808
88461
  info(colors.green(`User '${username}' removed from group '${name}'.`));
87809
88462
  }
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;
88463
+ 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);
88464
+ var group_default = command31;
87812
88465
 
87813
88466
  // src/commands/audit/audit.ts
87814
88467
  init_mod3();
@@ -87885,8 +88538,8 @@ async function get12(opts, id) {
87885
88538
  }
87886
88539
  }
87887
88540
  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;
88541
+ 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);
88542
+ var audit_default = command32;
87890
88543
 
87891
88544
  // src/commands/token/token.ts
87892
88545
  init_mod3();
@@ -87940,253 +88593,11 @@ async function deleteToken2(opts, tokenPrefix) {
87940
88593
  await deleteToken({ tokenPrefix });
87941
88594
  info(colors.green(`Token with prefix '${tokenPrefix}' deleted.`));
87942
88595
  }
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;
88596
+ 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);
88597
+ var token_default = command33;
87945
88598
 
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;
88599
+ // src/main.ts
88600
+ await init_generate_metadata();
88190
88601
 
88191
88602
  // src/commands/docs/docs.ts
88192
88603
  init_mod3();
@@ -88334,7 +88745,7 @@ var config_default = command35;
88334
88745
 
88335
88746
  // src/main.ts
88336
88747
  await init_context();
88337
- var VERSION = "1.693.4";
88748
+ var VERSION = "1.694.0";
88338
88749
  async function checkVersionSafe(cmd) {
88339
88750
  const mainCommand = cmd.getMainCommand();
88340
88751
  const upgradeCommand = mainCommand.getCommand("upgrade");