windmill-cli 1.696.2 → 1.697.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 +459 -75
  2. package/package.json +1 -1
package/esm/main.js CHANGED
@@ -16752,7 +16752,7 @@ var init_OpenAPI = __esm(() => {
16752
16752
  PASSWORD: undefined,
16753
16753
  TOKEN: getEnv3("WM_TOKEN"),
16754
16754
  USERNAME: undefined,
16755
- VERSION: "1.696.2",
16755
+ VERSION: "1.697.0",
16756
16756
  WITH_CREDENTIALS: true,
16757
16757
  interceptors: {
16758
16758
  request: new Interceptors,
@@ -17216,6 +17216,8 @@ __export(exports_services_gen, {
17216
17216
  loadFilePreview: () => loadFilePreview,
17217
17217
  loadFileMetadata: () => loadFileMetadata,
17218
17218
  loadCsvPreview: () => loadCsvPreview,
17219
+ listWsSpecificVersions: () => listWsSpecificVersions,
17220
+ listWsSpecific: () => listWsSpecific,
17219
17221
  listWorkspacesAsSuperAdmin: () => listWorkspacesAsSuperAdmin,
17220
17222
  listWorkspaces: () => listWorkspaces,
17221
17223
  listWorkspaceInvites: () => listWorkspaceInvites,
@@ -19667,6 +19669,26 @@ var backendVersion = () => {
19667
19669
  body: data3.requestBody,
19668
19670
  mediaType: "application/json"
19669
19671
  });
19672
+ }, listWsSpecific = (data3) => {
19673
+ return request(OpenAPI, {
19674
+ method: "GET",
19675
+ url: "/w/{workspace}/workspaces/list_ws_specific",
19676
+ path: {
19677
+ workspace: data3.workspace
19678
+ }
19679
+ });
19680
+ }, listWsSpecificVersions = (data3) => {
19681
+ return request(OpenAPI, {
19682
+ method: "GET",
19683
+ url: "/w/{workspace}/workspaces/list_ws_specific_versions",
19684
+ path: {
19685
+ workspace: data3.workspace
19686
+ },
19687
+ query: {
19688
+ kind: data3.kind,
19689
+ path: data3.path
19690
+ }
19691
+ });
19670
19692
  }, setPublicAppRateLimit = (data3) => {
19671
19693
  return request(OpenAPI, {
19672
19694
  method: "POST",
@@ -25373,7 +25395,7 @@ Found ${datatables.length} datatable(s):`);
25373
25395
  workspace: workspace.workspaceId,
25374
25396
  requestBody: {
25375
25397
  id: trueWorkspaceId,
25376
- name: opts.createWorkspaceName ?? trueWorkspaceId,
25398
+ name: opts.createWorkspaceName ?? workspaceName ?? trueWorkspaceId,
25377
25399
  color: forkColor
25378
25400
  }
25379
25401
  });
@@ -25389,7 +25411,7 @@ Found ${datatables.length} datatable(s):`);
25389
25411
  workspace: workspace.workspaceId,
25390
25412
  requestBody: {
25391
25413
  id: trueWorkspaceId,
25392
- name: opts.createWorkspaceName ?? trueWorkspaceId,
25414
+ name: opts.createWorkspaceName ?? workspaceName ?? trueWorkspaceId,
25393
25415
  color: forkColor,
25394
25416
  forked_datatables: forkedDatatables
25395
25417
  }
@@ -25475,9 +25497,21 @@ var init_fork = __esm(async () => {
25475
25497
  });
25476
25498
 
25477
25499
  // windmill-utils-internal/src/deploy.ts
25500
+ function isTriggerKind(kind) {
25501
+ return TRIGGER_KINDS.includes(kind);
25502
+ }
25503
+ function isTriggerOrScheduleKind(kind) {
25504
+ return kind === "schedule" || isTriggerKind(kind);
25505
+ }
25478
25506
  function folderName(path2) {
25479
25507
  return path2.replace(/^f\//, "");
25480
25508
  }
25509
+ function stripOperationalStateOnUpdate(payload, alreadyExists) {
25510
+ if (!alreadyExists)
25511
+ return payload;
25512
+ const { mode: _mode, enabled: _enabled, ...rest } = payload;
25513
+ return rest;
25514
+ }
25481
25515
  function getSubModules(flowModule) {
25482
25516
  const type = flowModule?.value?.type;
25483
25517
  if (type === "forloopflow" || type === "whileloopflow") {
@@ -25531,6 +25565,10 @@ async function checkItemExists(provider, kind, path2, workspace) {
25531
25565
  return provider.existsResourceType({ workspace, path: path2 });
25532
25566
  } else if (kind === "folder") {
25533
25567
  return provider.existsFolder({ workspace, name: folderName(path2) });
25568
+ } else if (kind === "schedule") {
25569
+ return provider.existsSchedule({ workspace, path: path2 });
25570
+ } else if (isTriggerKind(kind)) {
25571
+ return provider.existsTriggerByKind(kind, { workspace, path: path2 });
25534
25572
  }
25535
25573
  throw new Error(`Unknown kind: ${kind}`);
25536
25574
  }
@@ -25767,6 +25805,48 @@ async function deployItem(provider, kind, path2, workspaceFrom, workspaceTo, onB
25767
25805
  }
25768
25806
  });
25769
25807
  }
25808
+ } else if (kind === "schedule") {
25809
+ const schedule = await provider.getSchedule({
25810
+ workspace: workspaceFrom,
25811
+ path: path2
25812
+ });
25813
+ const baseBody = stripOperationalStateOnUpdate(schedule, alreadyExists);
25814
+ const requestBody = {
25815
+ ...baseBody,
25816
+ permissioned_as: onBehalfOf,
25817
+ preserve_permissioned_as: preserveOnBehalfOf
25818
+ };
25819
+ if (alreadyExists) {
25820
+ await provider.updateSchedule({
25821
+ workspace: workspaceTo,
25822
+ path: path2,
25823
+ requestBody
25824
+ });
25825
+ } else {
25826
+ await provider.createSchedule({
25827
+ workspace: workspaceTo,
25828
+ requestBody
25829
+ });
25830
+ }
25831
+ } else if (isTriggerKind(kind)) {
25832
+ const triggerBody = await provider.getTriggerForDeploy(kind, {
25833
+ workspace: workspaceFrom,
25834
+ path: path2,
25835
+ onBehalfOf
25836
+ });
25837
+ const requestBody = stripOperationalStateOnUpdate(triggerBody, alreadyExists);
25838
+ if (alreadyExists) {
25839
+ await provider.updateTriggerByKind(kind, {
25840
+ workspace: workspaceTo,
25841
+ path: path2,
25842
+ requestBody
25843
+ });
25844
+ } else {
25845
+ await provider.createTriggerByKind(kind, {
25846
+ workspace: workspaceTo,
25847
+ requestBody
25848
+ });
25849
+ }
25770
25850
  } else {
25771
25851
  throw new Error(`Unknown kind: ${kind}`);
25772
25852
  }
@@ -25795,6 +25875,10 @@ async function deleteItemInWorkspace(provider, kind, path2, workspace) {
25795
25875
  await provider.deleteResourceType({ workspace, path: path2 });
25796
25876
  } else if (kind === "folder") {
25797
25877
  await provider.deleteFolder({ workspace, name: folderName(path2) });
25878
+ } else if (kind === "schedule") {
25879
+ await provider.deleteSchedule({ workspace, path: path2 });
25880
+ } else if (isTriggerKind(kind)) {
25881
+ await provider.deleteTriggerByKind(kind, { workspace, path: path2 });
25798
25882
  } else {
25799
25883
  throw new Error(`Deletion not supported for kind: ${kind}`);
25800
25884
  }
@@ -25814,10 +25898,30 @@ async function getOnBehalfOf(provider, kind, path2, workspace) {
25814
25898
  } else if (kind === "app" || kind === "raw_app") {
25815
25899
  const app = await provider.getAppByPath({ workspace, path: path2 });
25816
25900
  return app.policy?.on_behalf_of_email;
25901
+ } else if (kind === "schedule") {
25902
+ const schedule = await provider.getSchedule({ workspace, path: path2 });
25903
+ return schedule.permissioned_as;
25904
+ } else if (isTriggerKind(kind)) {
25905
+ return await provider.getTriggerPermissionedAs(kind, { workspace, path: path2 });
25817
25906
  }
25818
25907
  } catch {}
25819
25908
  return;
25820
25909
  }
25910
+ var TRIGGER_KINDS;
25911
+ var init_deploy = __esm(() => {
25912
+ TRIGGER_KINDS = [
25913
+ "http_trigger",
25914
+ "websocket_trigger",
25915
+ "kafka_trigger",
25916
+ "nats_trigger",
25917
+ "postgres_trigger",
25918
+ "mqtt_trigger",
25919
+ "sqs_trigger",
25920
+ "gcp_trigger",
25921
+ "azure_trigger",
25922
+ "email_trigger"
25923
+ ];
25924
+ });
25821
25925
 
25822
25926
  // node_modules/@cliffy/prompt/checkbox.js
25823
25927
  var exports_checkbox = {};
@@ -26041,6 +26145,111 @@ var init_checkbox = __esm(async () => {
26041
26145
  });
26042
26146
 
26043
26147
  // src/commands/workspace/merge.ts
26148
+ function triggerService(kind) {
26149
+ switch (kind) {
26150
+ case "http_trigger":
26151
+ return {
26152
+ exists: existsHttpTrigger,
26153
+ get: getHttpTrigger,
26154
+ create: createHttpTrigger,
26155
+ update: updateHttpTrigger,
26156
+ delete: deleteHttpTrigger
26157
+ };
26158
+ case "websocket_trigger":
26159
+ return {
26160
+ exists: existsWebsocketTrigger,
26161
+ get: getWebsocketTrigger,
26162
+ create: createWebsocketTrigger,
26163
+ update: updateWebsocketTrigger,
26164
+ delete: deleteWebsocketTrigger
26165
+ };
26166
+ case "kafka_trigger":
26167
+ return {
26168
+ exists: existsKafkaTrigger,
26169
+ get: getKafkaTrigger,
26170
+ create: createKafkaTrigger,
26171
+ update: updateKafkaTrigger,
26172
+ delete: deleteKafkaTrigger
26173
+ };
26174
+ case "nats_trigger":
26175
+ return {
26176
+ exists: existsNatsTrigger,
26177
+ get: getNatsTrigger,
26178
+ create: createNatsTrigger,
26179
+ update: updateNatsTrigger,
26180
+ delete: deleteNatsTrigger
26181
+ };
26182
+ case "postgres_trigger":
26183
+ return {
26184
+ exists: existsPostgresTrigger,
26185
+ get: getPostgresTrigger,
26186
+ create: createPostgresTrigger,
26187
+ update: updatePostgresTrigger,
26188
+ delete: deletePostgresTrigger
26189
+ };
26190
+ case "mqtt_trigger":
26191
+ return {
26192
+ exists: existsMqttTrigger,
26193
+ get: getMqttTrigger,
26194
+ create: createMqttTrigger,
26195
+ update: updateMqttTrigger,
26196
+ delete: deleteMqttTrigger
26197
+ };
26198
+ case "sqs_trigger":
26199
+ return {
26200
+ exists: existsSqsTrigger,
26201
+ get: getSqsTrigger,
26202
+ create: createSqsTrigger,
26203
+ update: updateSqsTrigger,
26204
+ delete: deleteSqsTrigger
26205
+ };
26206
+ case "gcp_trigger":
26207
+ return {
26208
+ exists: existsGcpTrigger,
26209
+ get: getGcpTrigger,
26210
+ create: createGcpTrigger,
26211
+ update: updateGcpTrigger,
26212
+ delete: deleteGcpTrigger
26213
+ };
26214
+ case "azure_trigger":
26215
+ return {
26216
+ exists: existsAzureTrigger,
26217
+ get: getAzureTrigger,
26218
+ create: createAzureTrigger,
26219
+ update: updateAzureTrigger,
26220
+ delete: deleteAzureTrigger
26221
+ };
26222
+ case "email_trigger":
26223
+ return {
26224
+ exists: existsEmailTrigger,
26225
+ get: getEmailTrigger,
26226
+ create: createEmailTrigger,
26227
+ update: updateEmailTrigger,
26228
+ delete: deleteEmailTrigger
26229
+ };
26230
+ }
26231
+ }
26232
+ function preparePayload(kind, trigger, onBehalfOf) {
26233
+ const preserve = onBehalfOf !== undefined;
26234
+ const base = {
26235
+ ...trigger,
26236
+ permissioned_as: onBehalfOf,
26237
+ preserve_permissioned_as: preserve
26238
+ };
26239
+ if (kind === "gcp_trigger") {
26240
+ base.subscription_id = "";
26241
+ base.subscription_mode = "create_update";
26242
+ if (base.delivery_config) {
26243
+ base.delivery_config = { ...base.delivery_config, audience: "" };
26244
+ }
26245
+ if (base.delivery_type === "push") {
26246
+ base.base_endpoint = OpenAPI.BASE.replace(/\/api\/?$/, "");
26247
+ } else {
26248
+ base.base_endpoint = undefined;
26249
+ }
26250
+ }
26251
+ return base;
26252
+ }
26044
26253
  async function mergeWorkspaces(opts) {
26045
26254
  const workspace = await tryResolveBranchWorkspace(opts);
26046
26255
  if (!workspace) {
@@ -26091,6 +26300,10 @@ async function mergeWorkspaces(opts) {
26091
26300
  summaryRows.push(["Resource Types", String(summary.resource_types_changed)]);
26092
26301
  if (summary.folders_changed > 0)
26093
26302
  summaryRows.push(["Folders", String(summary.folders_changed)]);
26303
+ if (summary.schedules_changed > 0)
26304
+ summaryRows.push(["Schedules", String(summary.schedules_changed)]);
26305
+ if (summary.triggers_changed > 0)
26306
+ summaryRows.push(["Triggers", String(summary.triggers_changed)]);
26094
26307
  summaryRows.push(["Total", String(summary.total_diffs)]);
26095
26308
  if (summary.conflicts > 0)
26096
26309
  summaryRows.push([
@@ -26156,11 +26369,9 @@ Direction: ${colors.bold(direction === "to-parent" ? `Fork → Parent (${parentW
26156
26369
  if (opts.all) {
26157
26370
  selectedDiffs = selectableDiffs;
26158
26371
  } else if (opts.skipConflicts) {
26159
- selectedDiffs = selectableDiffs.filter((d) => !(d.ahead > 0 && d.behind > 0));
26372
+ selectedDiffs = selectableDiffs.filter((d) => !(d.ahead > 0 && d.behind > 0) && (!!opts.include || !isTriggerOrScheduleKind(d.kind)));
26160
26373
  } else if (opts.yes && !opts.include && !opts.exclude) {
26161
- if (direction === "to-fork") {
26162
- selectedDiffs = selectableDiffs.filter((d) => !(d.ahead > 0 && d.behind > 0));
26163
- }
26374
+ selectedDiffs = selectableDiffs.filter((d) => !isTriggerOrScheduleKind(d.kind) && (direction !== "to-fork" || !(d.ahead > 0 && d.behind > 0)));
26164
26375
  } else if (!opts.yes) {
26165
26376
  const { Checkbox: Checkbox2 } = await init_checkbox().then(() => exports_checkbox);
26166
26377
  const defaultForToFork = direction === "to-fork";
@@ -26168,11 +26379,12 @@ Direction: ${colors.bold(direction === "to-parent" ? `Fork → Parent (${parentW
26168
26379
  message: `Select items to deploy (${selectableDiffs.length} available):`,
26169
26380
  options: selectableDiffs.map((d) => {
26170
26381
  const isConflict = d.ahead > 0 && d.behind > 0;
26382
+ const isTriggerOrSchedule = isTriggerOrScheduleKind(d.kind);
26171
26383
  const label = `${d.kind}:${d.path}${isConflict ? colors.red(" [CONFLICT]") : ""}`;
26172
26384
  return {
26173
26385
  name: label,
26174
26386
  value: `${d.kind}:${d.path}`,
26175
- checked: defaultForToFork ? !isConflict : true
26387
+ checked: !isTriggerOrSchedule && (defaultForToFork ? !isConflict : true)
26176
26388
  };
26177
26389
  })
26178
26390
  });
@@ -26261,6 +26473,8 @@ var init_merge = __esm(async () => {
26261
26473
  init_log();
26262
26474
  init_client();
26263
26475
  init_services_gen();
26476
+ init_OpenAPI();
26477
+ init_deploy();
26264
26478
  await init_context();
26265
26479
  provider = {
26266
26480
  existsFlowByPath,
@@ -26300,7 +26514,28 @@ var init_merge = __esm(async () => {
26300
26514
  getFolder,
26301
26515
  createFolder,
26302
26516
  updateFolder,
26303
- deleteFolder
26517
+ deleteFolder,
26518
+ existsTriggerByKind: (kind, p) => triggerService(kind).exists(p),
26519
+ getTriggerForDeploy: async (kind, p) => {
26520
+ const trigger = await triggerService(kind).get({
26521
+ workspace: p.workspace,
26522
+ path: p.path
26523
+ });
26524
+ return preparePayload(kind, trigger, p.onBehalfOf);
26525
+ },
26526
+ createTriggerByKind: (kind, p) => triggerService(kind).create(p),
26527
+ updateTriggerByKind: (kind, p) => triggerService(kind).update(p),
26528
+ deleteTriggerByKind: (kind, p) => triggerService(kind).delete(p),
26529
+ getTriggerValue: (kind, p) => triggerService(kind).get(p),
26530
+ getTriggerPermissionedAs: async (kind, p) => {
26531
+ const trigger = await triggerService(kind).get(p);
26532
+ return trigger?.permissioned_as;
26533
+ },
26534
+ existsSchedule,
26535
+ getSchedule,
26536
+ createSchedule,
26537
+ updateSchedule,
26538
+ deleteSchedule
26304
26539
  };
26305
26540
  });
26306
26541
 
@@ -61342,33 +61577,34 @@ async function resolveWsNameForGitBranch(branchName) {
61342
61577
  const match2 = findWorkspaceByGitBranch(config.workspaces, branchName);
61343
61578
  return match2 ? match2[0] : branchName;
61344
61579
  }
61345
- function getWorkspaceSpecificTypes() {
61346
- return {
61347
- variable: ".variable.yaml",
61348
- resource: ".resource.yaml",
61349
- schedule: ".schedule.yaml",
61350
- ...Object.fromEntries(TRIGGER_TYPES.map((t) => [`${t}_trigger`, `.${t}_trigger.yaml`]))
61351
- };
61580
+ function getWorkspaceSpecificTypeKinds() {
61581
+ return [
61582
+ "variable",
61583
+ "resource",
61584
+ "schedule",
61585
+ ...TRIGGER_TYPES.map((t) => `${t}_trigger`)
61586
+ ];
61352
61587
  }
61353
61588
  function isTriggerFile(path7) {
61354
- return TRIGGER_TYPES.some((type) => path7.endsWith(`.${type}_trigger.yaml`));
61589
+ return TRIGGER_TYPES.some((type) => path7.endsWith(`.${type}_trigger.yaml`) || path7.endsWith(`.${type}_trigger.json`));
61355
61590
  }
61356
61591
  function isScheduleFile(path7) {
61357
- return path7.endsWith(".schedule.yaml");
61592
+ return path7.endsWith(".schedule.yaml") || path7.endsWith(".schedule.json");
61358
61593
  }
61359
61594
  function getFileTypeSuffix(path7) {
61360
- for (const [_, suffix] of Object.entries(getWorkspaceSpecificTypes())) {
61361
- if (path7.endsWith(suffix)) {
61362
- return suffix;
61363
- }
61595
+ for (const kind of getWorkspaceSpecificTypeKinds()) {
61596
+ if (path7.endsWith(`.${kind}.yaml`))
61597
+ return `.${kind}.yaml`;
61598
+ if (path7.endsWith(`.${kind}.json`))
61599
+ return `.${kind}.json`;
61364
61600
  }
61365
- const resourceFileMatch = path7.match(/(\\.resource\\.file\\..+)$/);
61601
+ const resourceFileMatch = path7.match(/(\.resource\.file\..+)$/);
61366
61602
  if (resourceFileMatch) {
61367
61603
  return resourceFileMatch[1];
61368
61604
  }
61369
61605
  return null;
61370
61606
  }
61371
- function buildYamlTypePattern() {
61607
+ function buildItemTypePattern() {
61372
61608
  const basicTypes = ["variable", "resource", "schedule"];
61373
61609
  const triggerTypes = TRIGGER_TYPES.map((t) => `${t}_trigger`);
61374
61610
  return `((${basicTypes.join("|")})|(${triggerTypes.join("|")}))`;
@@ -61436,10 +61672,14 @@ function getSpecificItemsForCurrentBranch(config, workspaceNameOverride) {
61436
61672
  function matchesPatterns(path7, patterns) {
61437
61673
  return patterns.some((pattern) => minimatch(path7, pattern));
61438
61674
  }
61675
+ function normalizeJsonToYaml(path7) {
61676
+ return path7.endsWith(".json") ? path7.slice(0, -".json".length) + ".yaml" : path7;
61677
+ }
61439
61678
  function isItemTypeConfigured(path7, specificItems) {
61440
61679
  if (!specificItems) {
61441
61680
  return false;
61442
61681
  }
61682
+ path7 = normalizeJsonToYaml(path7);
61443
61683
  if (path7.endsWith(".variable.yaml")) {
61444
61684
  return specificItems.variables !== undefined;
61445
61685
  }
@@ -61467,6 +61707,7 @@ function isSpecificItem(path7, specificItems) {
61467
61707
  if (!specificItems) {
61468
61708
  return false;
61469
61709
  }
61710
+ path7 = normalizeJsonToYaml(path7);
61470
61711
  if (path7.endsWith(".variable.yaml")) {
61471
61712
  return specificItems.variables ? matchesPatterns(path7, specificItems.variables) : false;
61472
61713
  }
@@ -61510,12 +61751,13 @@ function toWorkspaceSpecificPath(basePath, workspaceName) {
61510
61751
  if (sanitizedName !== workspaceName) {
61511
61752
  console.warn(`Warning: Workspace name "${workspaceName}" contains filesystem-unsafe characters (/ \\ : * ? " < > | .) and was sanitized to "${sanitizedName}". This may cause collisions with other similarly named branches.`);
61512
61753
  }
61513
- if (basePath.endsWith("/folder.meta.yaml")) {
61514
- const pathWithoutMeta = basePath.substring(0, basePath.length - "/folder.meta.yaml".length);
61515
- return `${pathWithoutMeta}/folder.${sanitizedName}.meta.yaml`;
61754
+ const folderMetaMatch = basePath.match(/^(.*)\/folder\.meta\.(yaml|json)$/);
61755
+ if (folderMetaMatch) {
61756
+ return `${folderMetaMatch[1]}/folder.${sanitizedName}.meta.${folderMetaMatch[2]}`;
61516
61757
  }
61517
- if (basePath === "settings.yaml") {
61518
- return `settings.${sanitizedName}.yaml`;
61758
+ const settingsMatch = basePath.match(/^settings\.(yaml|json)$/);
61759
+ if (settingsMatch) {
61760
+ return `settings.${sanitizedName}.${settingsMatch[1]}`;
61519
61761
  }
61520
61762
  const resourceFileMatch = basePath.match(/^(.+?)(\.resource\.file\..+)$/);
61521
61763
  let extension;
@@ -61536,13 +61778,15 @@ function toWorkspaceSpecificPath(basePath, workspaceName) {
61536
61778
  function fromWorkspaceSpecificPath(workspaceSpecificPath, workspaceName) {
61537
61779
  const sanitizedName = workspaceName.replace(/[\/\\:*?"<>|.]/g, "_");
61538
61780
  const escapedName = sanitizedName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
61539
- const folderPattern = new RegExp(`/folder\\.${escapedName}\\.meta\\.yaml$`);
61540
- if (folderPattern.test(workspaceSpecificPath)) {
61541
- return workspaceSpecificPath.replace(folderPattern, "/folder.meta.yaml");
61781
+ const folderPattern = new RegExp(`/folder\\.${escapedName}\\.meta\\.(yaml|json)$`);
61782
+ const folderMatch = workspaceSpecificPath.match(folderPattern);
61783
+ if (folderMatch) {
61784
+ return workspaceSpecificPath.replace(folderPattern, `/folder.meta.${folderMatch[1]}`);
61542
61785
  }
61543
- const settingsPattern = new RegExp(`^settings\\.${escapedName}\\.yaml$`);
61544
- if (settingsPattern.test(workspaceSpecificPath)) {
61545
- return "settings.yaml";
61786
+ const settingsPattern = new RegExp(`^settings\\.${escapedName}\\.(yaml|json)$`);
61787
+ const settingsMatch = workspaceSpecificPath.match(settingsPattern);
61788
+ if (settingsMatch) {
61789
+ return `settings.${settingsMatch[1]}`;
61546
61790
  }
61547
61791
  const resourceFilePattern = new RegExp(`\\.${escapedName}(\\.resource\\.file\\..+)$`);
61548
61792
  const resourceFileMatch = workspaceSpecificPath.match(resourceFilePattern);
@@ -61551,12 +61795,12 @@ function fromWorkspaceSpecificPath(workspaceSpecificPath, workspaceName) {
61551
61795
  const pathWithoutBranchAndExtension2 = workspaceSpecificPath.substring(0, workspaceSpecificPath.length - `.${sanitizedName}${extension2}`.length);
61552
61796
  return `${pathWithoutBranchAndExtension2}${extension2}`;
61553
61797
  }
61554
- const yamlPattern = new RegExp(`\\.${escapedName}(\\.${buildYamlTypePattern()}\\.yaml)$`);
61555
- const yamlMatch = workspaceSpecificPath.match(yamlPattern);
61556
- if (!yamlMatch) {
61798
+ const typedPattern = new RegExp(`\\.${escapedName}(\\.${buildItemTypePattern()}\\.(yaml|json))$`);
61799
+ const typedMatch = workspaceSpecificPath.match(typedPattern);
61800
+ if (!typedMatch) {
61557
61801
  return workspaceSpecificPath;
61558
61802
  }
61559
- const extension = yamlMatch[1];
61803
+ const extension = typedMatch[1];
61560
61804
  const pathWithoutBranchAndExtension = workspaceSpecificPath.substring(0, workspaceSpecificPath.length - `.${sanitizedName}${extension}`.length);
61561
61805
  return `${pathWithoutBranchAndExtension}${extension}`;
61562
61806
  }
@@ -61592,14 +61836,14 @@ function isCurrentWorkspaceFile(path7, workspaceNameOverride) {
61592
61836
  const escapedName = sanitizedName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
61593
61837
  let pattern = workspacePatternCache.get(currentWorkspace);
61594
61838
  if (!pattern) {
61595
- pattern = new RegExp(`\\.${escapedName}\\.${buildYamlTypePattern()}\\.yaml$|` + `\\.${escapedName}\\.resource\\.file\\..+$|` + `/folder\\.${escapedName}\\.meta\\.yaml$|` + `^settings\\.${escapedName}\\.yaml$`);
61839
+ pattern = new RegExp(`\\.${escapedName}\\.${buildItemTypePattern()}\\.(yaml|json)$|` + `\\.${escapedName}\\.resource\\.file\\..+$|` + `/folder\\.${escapedName}\\.meta\\.(yaml|json)$|` + `^settings\\.${escapedName}\\.(yaml|json)$`);
61596
61840
  workspacePatternCache.set(currentWorkspace, pattern);
61597
61841
  }
61598
61842
  return pattern.test(path7);
61599
61843
  }
61600
61844
  function isWorkspaceSpecificFile(path7) {
61601
- const yamlTypePattern = buildYamlTypePattern();
61602
- return new RegExp(`\\.[^.]+\\.${yamlTypePattern}\\.yaml$|` + `\\.[^.]+\\.resource\\.file\\..+$|` + `/folder\\.[^.]+\\.meta\\.yaml$|` + `^settings\\.[^.]+\\.yaml$`).test(path7);
61845
+ const typePattern = buildItemTypePattern();
61846
+ return new RegExp(`\\.[^.]+\\.${typePattern}\\.(yaml|json)$|` + `\\.[^.]+\\.resource\\.file\\..+$|` + `/folder\\.[^.]+\\.meta\\.(yaml|json)$|` + `^settings\\.[^.]+\\.(yaml|json)$`).test(path7);
61603
61847
  }
61604
61848
  var workspacePatternCache;
61605
61849
  var init_specific_items = __esm(async () => {
@@ -63815,7 +64059,7 @@ async function readFilesetDirectory(dirPath) {
63815
64059
  await walk(dirPath, "");
63816
64060
  return result;
63817
64061
  }
63818
- async function pushResource(workspace, remotePath, resource, localResource, originalLocalPath) {
64062
+ async function pushResource(workspace, remotePath, resource, localResource, originalLocalPath, wsSpecific) {
63819
64063
  remotePath = removeType(remotePath, "resource");
63820
64064
  try {
63821
64065
  resource = await getResource({
@@ -63852,7 +64096,7 @@ async function pushResource(workspace, remotePath, resource, localResource, orig
63852
64096
  await updateResource({
63853
64097
  workspace,
63854
64098
  path: remotePath.replaceAll(SEP6, "/"),
63855
- requestBody: { ...localResource }
64099
+ requestBody: { ...localResource, ...wsSpecific !== undefined ? { ws_specific: wsSpecific } : {} }
63856
64100
  });
63857
64101
  } else {
63858
64102
  await resolveInlineContent();
@@ -63864,7 +64108,8 @@ async function pushResource(workspace, remotePath, resource, localResource, orig
63864
64108
  workspace,
63865
64109
  requestBody: {
63866
64110
  path: remotePath.replaceAll(SEP6, "/"),
63867
- ...localResource
64111
+ ...localResource,
64112
+ ...wsSpecific !== undefined ? { ws_specific: wsSpecific } : {}
63868
64113
  }
63869
64114
  });
63870
64115
  }
@@ -65037,6 +65282,7 @@ var init_generate_metadata = __esm(async () => {
65037
65282
  // src/commands/sync/sync.ts
65038
65283
  var exports_sync = {};
65039
65284
  __export(exports_sync, {
65285
+ yamlSortedEntries: () => yamlSortedEntries,
65040
65286
  yamlOptions: () => yamlOptions,
65041
65287
  resolveWsNameForConfigFromFlags: () => resolveWsNameForConfigFromFlags,
65042
65288
  readDirRecursiveWithIgnore: () => readDirRecursiveWithIgnore2,
@@ -65051,11 +65297,74 @@ __export(exports_sync, {
65051
65297
  extractFieldsForRawApps: () => extractFieldsForRawApps,
65052
65298
  elementsToMap: () => elementsToMap,
65053
65299
  default: () => sync_default,
65300
+ computeWsSpecificFlagOnlyPushes: () => computeWsSpecificFlagOnlyPushes,
65054
65301
  FSFSElement: () => FSFSElement
65055
65302
  });
65056
65303
  import { writeFile as writeFile7, readdir as readdir4, stat as stat7, rm, copyFile, mkdir as mkdir5 } from "node:fs/promises";
65057
65304
  import * as path11 from "node:path";
65058
65305
  import { sep as SEP9 } from "node:path";
65306
+ function configKeyForItemKind(kind) {
65307
+ switch (kind) {
65308
+ case "resource":
65309
+ return "resources";
65310
+ case "variable":
65311
+ return "variables";
65312
+ }
65313
+ return null;
65314
+ }
65315
+ async function mergeWsSpecificFromServer(workspaceId, specificItems) {
65316
+ let wsSpecificItems;
65317
+ try {
65318
+ wsSpecificItems = await listWsSpecific({ workspace: workspaceId });
65319
+ } catch (err) {
65320
+ const isApiError = err && typeof err === "object" && "name" in err && err.name === "ApiError";
65321
+ const status = isApiError ? err.status : undefined;
65322
+ if (status === 404) {
65323
+ debug("listWsSpecific endpoint not available on server, skipping");
65324
+ } else {
65325
+ const msg = err instanceof Error ? err.message : String(err);
65326
+ warn(`Could not fetch ws_specific items from server (${status ?? "no status"}): ${msg}. ` + `Sync will proceed without server-side ws_specific items.`);
65327
+ }
65328
+ return { merged: specificItems, serverItems: null };
65329
+ }
65330
+ if (wsSpecificItems.length === 0) {
65331
+ return { merged: specificItems, serverItems: wsSpecificItems };
65332
+ }
65333
+ const merged = specificItems ? { ...specificItems } : {};
65334
+ for (const item of wsSpecificItems) {
65335
+ const configKey = configKeyForItemKind(item.item_kind);
65336
+ if (!configKey)
65337
+ continue;
65338
+ if (!merged[configKey]) {
65339
+ merged[configKey] = [];
65340
+ }
65341
+ merged[configKey].push(`${item.path}.${item.item_kind}.yaml`);
65342
+ }
65343
+ return { merged, serverItems: wsSpecificItems };
65344
+ }
65345
+ function computeWsSpecificFlagOnlyPushes(localMap, localSpecificItems, serverItems) {
65346
+ if (!localSpecificItems || serverItems === null)
65347
+ return [];
65348
+ const serverSet = new Set(serverItems.map((i) => `${i.item_kind}:${i.path}`));
65349
+ const out = [];
65350
+ for (const filePath of Object.keys(localMap)) {
65351
+ let kind;
65352
+ try {
65353
+ kind = getTypeStrFromPath(filePath);
65354
+ } catch {
65355
+ continue;
65356
+ }
65357
+ if (configKeyForItemKind(kind) === null)
65358
+ continue;
65359
+ if (!isSpecificItem(filePath, localSpecificItems))
65360
+ continue;
65361
+ const serverPath = removeType(filePath, kind);
65362
+ if (serverSet.has(`${kind}:${serverPath}`))
65363
+ continue;
65364
+ out.push({ kind, serverPath, filePath });
65365
+ }
65366
+ return out;
65367
+ }
65059
65368
  function resolveWsNameFromBranch(opts, branchName) {
65060
65369
  const match2 = findWorkspaceByGitBranch(opts.workspaces, branchName);
65061
65370
  return match2 ? match2[0] : branchName;
@@ -65256,6 +65565,14 @@ function prioritizeName(name) {
65256
65565
  return "azz";
65257
65566
  return name;
65258
65567
  }
65568
+ function yamlSortedEntries(rec) {
65569
+ const entries = Object.entries(rec);
65570
+ if (Array.isArray(rec)) {
65571
+ return entries;
65572
+ }
65573
+ entries.sort(([a], [b]) => prioritizeName(a).localeCompare(prioritizeName(b)));
65574
+ return entries;
65575
+ }
65259
65576
  function extractFields(fields) {
65260
65577
  Object.entries(fields).forEach(([k, v]) => {
65261
65578
  if (typeof v == "object") {
@@ -65379,7 +65696,7 @@ function extractInlineScriptsForApps(key, rec, pathAssigner, toId, removeSchema)
65379
65696
  return [];
65380
65697
  }
65381
65698
  if (typeof rec == "object") {
65382
- return Object.entries(rec).flatMap(([k, v]) => {
65699
+ return yamlSortedEntries(rec).flatMap(([k, v]) => {
65383
65700
  if (k == "inlineScript" && v != null && typeof v == "object") {
65384
65701
  rec["type"] = undefined;
65385
65702
  const o = v;
@@ -65446,7 +65763,7 @@ async function findFilesetResourceFile(changePath) {
65446
65763
  }
65447
65764
  throw new Error(`No resource metadata file found for fileset resource: ${changePath}`);
65448
65765
  }
65449
- async function pushFilesetParentResource(childPath, workspaceId, alreadySynced, cachedWsName) {
65766
+ async function pushFilesetParentResource(childPath, workspaceId, alreadySynced, cachedWsName, specificItems) {
65450
65767
  let resourceFilePath;
65451
65768
  try {
65452
65769
  resourceFilePath = await findFilesetResourceFile(childPath);
@@ -65459,10 +65776,14 @@ async function pushFilesetParentResource(childPath, workspaceId, alreadySynced,
65459
65776
  alreadySynced.push(resourceFilePath);
65460
65777
  const newObj = parseFromPath(resourceFilePath, await readTextFile(resourceFilePath));
65461
65778
  let serverPath = resourceFilePath;
65779
+ let wsSpecific = false;
65462
65780
  if (cachedWsName && isWorkspaceSpecificFile(resourceFilePath)) {
65463
65781
  serverPath = fromWorkspaceSpecificPath(resourceFilePath, cachedWsName);
65782
+ wsSpecific = true;
65783
+ } else if (specificItems && isSpecificItem(childPath, specificItems)) {
65784
+ wsSpecific = true;
65464
65785
  }
65465
- await pushResource(workspaceId, serverPath, undefined, newObj, resourceFilePath);
65786
+ await pushResource(workspaceId, serverPath, undefined, newObj, resourceFilePath, wsSpecific ? true : undefined);
65466
65787
  return { status: "pushed", resourceFilePath };
65467
65788
  }
65468
65789
  function ZipFSElement(zip, useYaml, defaultTs, resourceTypeToFormatExtension, resourceTypeToIsFileset, ignoreCodebaseChanges, stripOnBehalfOf) {
@@ -66518,8 +66839,11 @@ async function pull(opts) {
66518
66839
  wsNameForConfig = inferWsNameFromProfile(opts, workspace);
66519
66840
  }
66520
66841
  const effectiveOpts = await resolveEffectiveSyncOptions(workspace, opts, opts.promotion, wsNameForConfig);
66521
- const specificItems = getSpecificItemsForCurrentBranch(opts, wsNameForConfig);
66522
- const wsNameForFiles = wsNameForConfig ? resolveWsNameForFiles(opts, wsNameForConfig) : undefined;
66842
+ let specificItems = getSpecificItemsForCurrentBranch(opts, wsNameForConfig);
66843
+ let wsNameForFiles = wsNameForConfig ? resolveWsNameForFiles(opts, wsNameForConfig) : workspace.workspaceId;
66844
+ const localSpecificItems = specificItems;
66845
+ const wsSpecificMerge = await mergeWsSpecificFromServer(workspace.workspaceId, specificItems);
66846
+ specificItems = wsSpecificMerge.merged;
66523
66847
  opts = mergeCliWithEffectiveOptions(originalCliOpts, effectiveOpts);
66524
66848
  const codebases = await listSyncCodebases(opts);
66525
66849
  info(colors.gray("Computing the files to update locally to match remote (taking wmill.yaml into account)"));
@@ -66538,6 +66862,17 @@ async function pull(opts) {
66538
66862
  const local = !opts.stateful ? await FSFSElement(process.cwd(), codebases, true) : await FSFSElement(path11.join(process.cwd(), ".wmill"), [], true);
66539
66863
  const { changes, localMap } = await compareDynFSElement(remote, local, await ignoreF(opts), opts.json ?? false, opts, false, codebases, true, specificItems, wsNameForFiles, true);
66540
66864
  info(`remote (${workspace.name}) -> local: ${changes.length} changes to apply`);
66865
+ if (wsSpecificMerge.serverItems && wsSpecificMerge.serverItems.length > 0) {
66866
+ const changedPaths = new Set(changes.map((c) => c.path));
66867
+ for (const item of wsSpecificMerge.serverItems) {
66868
+ const filePath = `${item.path}.${item.item_kind}.yaml`;
66869
+ if (!changedPaths.has(filePath))
66870
+ continue;
66871
+ if (!isSpecificItem(filePath, localSpecificItems)) {
66872
+ warn(`${item.item_kind} ${item.path} is workspace-specific on the remote ` + `but not flagged in wmill.yaml's specificItems — consider adding it.`);
66873
+ }
66874
+ }
66875
+ }
66541
66876
  if (opts.dryRun && opts.jsonOutput) {
66542
66877
  const result = {
66543
66878
  success: true,
@@ -66761,6 +67096,8 @@ function prettyChanges(changes, specificItems, branchOverride, folderDefaultAnno
66761
67096
  showDiff(change.before, change.after);
66762
67097
  }
66763
67098
  }
67099
+ } else if (change.name === "ws_specific_flag") {
67100
+ info(colors.cyan(`~ ${change.kind} ${displayPath} ` + (change.wsSpecific ? "(mark as workspace-specific)" : "(unmark as workspace-specific)")));
66764
67101
  }
66765
67102
  }
66766
67103
  }
@@ -66800,8 +67137,12 @@ async function push4(opts) {
66800
67137
  wsNameForConfig = inferWsNameFromProfile(opts, workspace);
66801
67138
  }
66802
67139
  const effectiveOpts = await resolveEffectiveSyncOptions(workspace, opts, opts.promotion, wsNameForConfig);
66803
- const specificItems = getSpecificItemsForCurrentBranch(opts, wsNameForConfig);
66804
- const wsNameForFiles = wsNameForConfig ? resolveWsNameForFiles(opts, wsNameForConfig) : undefined;
67140
+ let specificItems = getSpecificItemsForCurrentBranch(opts, wsNameForConfig);
67141
+ let wsNameForFiles = wsNameForConfig ? resolveWsNameForFiles(opts, wsNameForConfig) : workspace.workspaceId;
67142
+ const localSpecificItems = specificItems;
67143
+ const wsSpecificMerge = await mergeWsSpecificFromServer(workspace.workspaceId, specificItems);
67144
+ specificItems = wsSpecificMerge.merged;
67145
+ const serverWsSpecificItems = wsSpecificMerge.serverItems;
66805
67146
  opts = mergeCliWithEffectiveOptions(originalCliOpts, effectiveOpts);
66806
67147
  if (opts.lint) {
66807
67148
  info("Running lint validation before push...");
@@ -66853,7 +67194,18 @@ Push aborted: ${lockIssues.length} script(s) missing locks.`));
66853
67194
  } catch {}
66854
67195
  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);
66855
67196
  const local = await FSFSElement(path11.join(process.cwd(), ""), codebases, false);
66856
- const { changes } = await compareDynFSElement(local, remote, await ignoreF(opts), opts.json ?? false, opts, true, codebases, false, specificItems, wsNameForFiles, false);
67197
+ const { changes, localMap } = await compareDynFSElement(local, remote, await ignoreF(opts), opts.json ?? false, opts, true, codebases, false, specificItems, wsNameForFiles, false);
67198
+ const wsSpecificFlagOnly = computeWsSpecificFlagOnlyPushes(localMap, localSpecificItems, serverWsSpecificItems);
67199
+ for (const item of wsSpecificFlagOnly) {
67200
+ if (changes.some((c) => c.path === item.filePath))
67201
+ continue;
67202
+ changes.push({
67203
+ name: "ws_specific_flag",
67204
+ path: item.filePath,
67205
+ kind: item.kind,
67206
+ wsSpecific: true
67207
+ });
67208
+ }
66857
67209
  const rawWorkspaceDependencies = await getRawWorkspaceDependencies(true);
66858
67210
  const tracker = await buildTracker(changes);
66859
67211
  const autoRegenerate = !!opts.autoMetadata;
@@ -67108,7 +67460,7 @@ Run 'wmill folder add-missing' to create them locally, then push again.`;
67108
67460
  userIsAdminOrDeployer,
67109
67461
  userEmail: user.email
67110
67462
  };
67111
- await preCheckPermissionedAs(changes, user.email, userIsAdminOrDeployer, opts.acceptOverridingPermissionedAsWithSelf ?? false, !!process.stdin.isTTY);
67463
+ await preCheckPermissionedAs(changes.filter((c) => c.name !== "ws_specific_flag"), user.email, userIsAdminOrDeployer, opts.acceptOverridingPermissionedAsWithSelf ?? false, !!process.stdin.isTTY);
67112
67464
  } else if (folderDefaultAnnotations && folderDefaultAnnotations.size > 0) {
67113
67465
  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.`));
67114
67466
  }
@@ -67209,10 +67561,14 @@ Run 'wmill folder add-missing' to create them locally, then push again.`;
67209
67561
  const newObj2 = parseFromPath(resourceFilePath, await readTextFile(resourceFilePath));
67210
67562
  let serverPath = resourceFilePath;
67211
67563
  const currentBranch = cachedWsNameForPush;
67564
+ let isFileResWsSpecific = false;
67212
67565
  if (currentBranch && isWorkspaceSpecificFile(resourceFilePath)) {
67213
67566
  serverPath = fromWorkspaceSpecificPath(resourceFilePath, currentBranch);
67567
+ isFileResWsSpecific = true;
67568
+ } else if (specificItems && isSpecificItem(change.path, specificItems)) {
67569
+ isFileResWsSpecific = true;
67214
67570
  }
67215
- await pushResource(workspace.workspaceId, serverPath, undefined, newObj2, resourceFilePath);
67571
+ await pushResource(workspace.workspaceId, serverPath, undefined, newObj2, resourceFilePath, isFileResWsSpecific ? true : undefined);
67216
67572
  if (stateTarget) {
67217
67573
  await writeFile7(stateTarget, change.after, "utf-8");
67218
67574
  }
@@ -67220,7 +67576,7 @@ Run 'wmill folder add-missing' to create them locally, then push again.`;
67220
67576
  }
67221
67577
  }
67222
67578
  if (isFilesetResource(change.path)) {
67223
- const result = await pushFilesetParentResource(change.path, workspace.workspaceId, alreadySynced, cachedWsNameForPush);
67579
+ const result = await pushFilesetParentResource(change.path, workspace.workspaceId, alreadySynced, cachedWsNameForPush, specificItems);
67224
67580
  if (result.status === "parent-missing") {
67225
67581
  throw new Error(`No resource metadata file found for fileset resource: ${change.path}`);
67226
67582
  }
@@ -67234,16 +67590,17 @@ Run 'wmill folder add-missing' to create them locally, then push again.`;
67234
67590
  const oldObj = parseFromPath(change.path, change.before);
67235
67591
  const newObj = parseFromPath(change.path, change.after);
67236
67592
  let originalWorkspaceSpecificPath;
67237
- if (specificItems && isSpecificItem(change.path, specificItems)) {
67593
+ const isWsSpecific = specificItems && isSpecificItem(change.path, specificItems);
67594
+ if (isWsSpecific) {
67238
67595
  originalWorkspaceSpecificPath = getWorkspaceSpecificPath(change.path, specificItems, wsNameForFiles);
67239
67596
  }
67240
- await pushObj(workspace.workspaceId, change.path, oldObj, newObj, opts.plainSecrets ?? false, alreadySynced, opts.message, originalWorkspaceSpecificPath, permissionedAsContext);
67597
+ await pushObj(workspace.workspaceId, change.path, oldObj, newObj, opts.plainSecrets ?? false, alreadySynced, opts.message, originalWorkspaceSpecificPath, permissionedAsContext, isWsSpecific ? true : undefined);
67241
67598
  if (stateTarget) {
67242
67599
  await writeFile7(stateTarget, change.after, "utf-8");
67243
67600
  }
67244
67601
  } else if (change.name === "added") {
67245
67602
  if (isFilesetResource(change.path)) {
67246
- await pushFilesetParentResource(change.path, workspace.workspaceId, alreadySynced, cachedWsNameForPush);
67603
+ await pushFilesetParentResource(change.path, workspace.workspaceId, alreadySynced, cachedWsNameForPush, specificItems);
67247
67604
  continue;
67248
67605
  }
67249
67606
  if (change.path.endsWith(".script.json") || change.path.endsWith(".script.yaml") || change.path.endsWith(".lock") || isFileResource(change.path)) {
@@ -67260,13 +67617,14 @@ Run 'wmill folder add-missing' to create them locally, then push again.`;
67260
67617
  }
67261
67618
  const obj = parseFromPath(change.path, change.content);
67262
67619
  let localFilePath = change.path;
67263
- if (specificItems && isSpecificItem(change.path, specificItems)) {
67620
+ const isAddedWsSpecific = specificItems && isSpecificItem(change.path, specificItems);
67621
+ if (isAddedWsSpecific) {
67264
67622
  const workspaceSpecificPath = getWorkspaceSpecificPath(change.path, specificItems, wsNameForFiles);
67265
67623
  if (workspaceSpecificPath) {
67266
67624
  localFilePath = workspaceSpecificPath;
67267
67625
  }
67268
67626
  }
67269
- await pushObj(workspace.workspaceId, change.path, undefined, obj, opts.plainSecrets ?? false, [], opts.message, localFilePath, permissionedAsContext);
67627
+ await pushObj(workspace.workspaceId, change.path, undefined, obj, opts.plainSecrets ?? false, [], opts.message, localFilePath, permissionedAsContext, isAddedWsSpecific ? true : undefined);
67270
67628
  if (stateTarget) {
67271
67629
  await writeFile7(stateTarget, change.content, "utf-8");
67272
67630
  }
@@ -67279,7 +67637,7 @@ Run 'wmill folder add-missing' to create them locally, then push again.`;
67279
67637
  continue;
67280
67638
  }
67281
67639
  if (isFilesetResource(change.path)) {
67282
- await pushFilesetParentResource(change.path, workspace.workspaceId, alreadySynced, cachedWsNameForPush);
67640
+ await pushFilesetParentResource(change.path, workspace.workspaceId, alreadySynced, cachedWsNameForPush, specificItems);
67283
67641
  continue;
67284
67642
  }
67285
67643
  const typ = getTypeStrFromPath(change.path);
@@ -67548,6 +67906,23 @@ Run 'wmill folder add-missing' to create them locally, then push again.`;
67548
67906
  await rm(stateTarget);
67549
67907
  } catch {}
67550
67908
  }
67909
+ } else if (change.name === "ws_specific_flag") {
67910
+ const target = change.path.replaceAll(SEP9, "/");
67911
+ if (change.kind === "resource") {
67912
+ await updateResource({
67913
+ workspace: workspace.workspaceId,
67914
+ path: removeType(target, "resource"),
67915
+ requestBody: { ws_specific: change.wsSpecific }
67916
+ });
67917
+ } else if (change.kind === "variable") {
67918
+ await updateVariable({
67919
+ workspace: workspace.workspaceId,
67920
+ path: removeType(target, "variable"),
67921
+ requestBody: { ws_specific: change.wsSpecific }
67922
+ });
67923
+ } else {
67924
+ warn(`ws_specific_flag change for unsupported kind '${change.kind}' at ${change.path} — skipping`);
67925
+ }
67551
67926
  }
67552
67927
  }
67553
67928
  })();
@@ -69243,7 +69618,7 @@ async function traverseAndProcessInlineScripts(obj, processor, currentPath = [])
69243
69618
  ])));
69244
69619
  }
69245
69620
  const result = {};
69246
- for (const [key, value] of Object.entries(obj)) {
69621
+ for (const [key, value] of yamlSortedEntries(obj)) {
69247
69622
  if (key === "inlineScript" && typeof value === "object") {
69248
69623
  result[key] = await processor(value, {
69249
69624
  path: currentPath,
@@ -70684,13 +71059,20 @@ var DEFAULT_PORT = 4000, DEFAULT_HOST = "localhost", createHTML = (jsPath, cssPa
70684
71059
  <meta charset="UTF-8">
70685
71060
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
70686
71061
  <title>Windmill App Dev Preview</title>
70687
- <link rel="stylesheet" href="${cssPath}">
70688
71062
  <style>
70689
- * {
70690
- margin: 0;
70691
- padding: 0;
70692
- box-sizing: border-box;
71063
+ /* Declared before the user stylesheet so Tailwind's layers (theme, base,
71064
+ components, utilities) are appended after wmill-shell and win the
71065
+ cascade. Unlayered styles would override Tailwind utilities. */
71066
+ @layer wmill-shell {
71067
+ * {
71068
+ margin: 0;
71069
+ padding: 0;
71070
+ box-sizing: border-box;
71071
+ }
70693
71072
  }
71073
+ </style>
71074
+ <link rel="stylesheet" href="${cssPath}">
71075
+ <style>
70694
71076
  body {
70695
71077
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
70696
71078
  'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
@@ -72297,7 +72679,7 @@ async function get6(opts, path19) {
72297
72679
  console.log(colors.bold("Account:") + " " + (v.account ?? "-"));
72298
72680
  }
72299
72681
  }
72300
- async function pushVariable(workspace, remotePath, variable, localVariable, plainSecrets) {
72682
+ async function pushVariable(workspace, remotePath, variable, localVariable, plainSecrets, wsSpecific) {
72301
72683
  remotePath = removeType(remotePath, "variable");
72302
72684
  debug(`Processing local variable ${remotePath}`);
72303
72685
  try {
@@ -72323,7 +72705,8 @@ async function pushVariable(workspace, remotePath, variable, localVariable, plai
72323
72705
  alreadyEncrypted: !plainSecrets,
72324
72706
  requestBody: {
72325
72707
  ...localVariable,
72326
- is_secret: localVariable.is_secret && !variable.is_secret ? true : undefined
72708
+ is_secret: localVariable.is_secret && !variable.is_secret ? true : undefined,
72709
+ ...wsSpecific !== undefined ? { ws_specific: wsSpecific } : {}
72327
72710
  }
72328
72711
  });
72329
72712
  } else {
@@ -72333,7 +72716,8 @@ async function pushVariable(workspace, remotePath, variable, localVariable, plai
72333
72716
  alreadyEncrypted: !plainSecrets,
72334
72717
  requestBody: {
72335
72718
  path: remotePath.replaceAll(SEP16, "/"),
72336
- ...localVariable
72719
+ ...localVariable,
72720
+ ...wsSpecific !== undefined ? { ws_specific: wsSpecific } : {}
72337
72721
  }
72338
72722
  });
72339
72723
  }
@@ -74750,7 +75134,7 @@ function showConflict(path21, local, remote) {
74750
75134
  info(`
74751
75135
  `);
74752
75136
  }
74753
- async function pushObj(workspace, p, befObj, newObj, plainSecrets, alreadySynced3, message, originalLocalPath, permissionedAsContext) {
75137
+ async function pushObj(workspace, p, befObj, newObj, plainSecrets, alreadySynced3, message, originalLocalPath, permissionedAsContext, wsSpecific) {
74754
75138
  const typeEnding = getTypeStrFromPath(p);
74755
75139
  if (typeEnding === "app") {
74756
75140
  const appName = extractResourceName(p, "app");
@@ -74767,7 +75151,7 @@ async function pushObj(workspace, p, befObj, newObj, plainSecrets, alreadySynced
74767
75151
  } else if (typeEnding === "folder") {
74768
75152
  await pushFolder(workspace, p, befObj, newObj);
74769
75153
  } else if (typeEnding === "variable") {
74770
- await pushVariable(workspace, p, befObj, newObj, plainSecrets);
75154
+ await pushVariable(workspace, p, befObj, newObj, plainSecrets, wsSpecific);
74771
75155
  } else if (typeEnding === "flow") {
74772
75156
  const flowName = extractResourceName(p, "flow");
74773
75157
  if (!flowName) {
@@ -74777,7 +75161,7 @@ async function pushObj(workspace, p, befObj, newObj, plainSecrets, alreadySynced
74777
75161
  } else if (typeEnding === "resource") {
74778
75162
  if (!alreadySynced3.includes(p)) {
74779
75163
  alreadySynced3.push(p);
74780
- await pushResource(workspace, p, befObj, newObj, originalLocalPath || p);
75164
+ await pushResource(workspace, p, befObj, newObj, originalLocalPath || p, wsSpecific);
74781
75165
  }
74782
75166
  } else if (typeEnding === "resource-type") {
74783
75167
  await pushResourceType(workspace, p, befObj, newObj);
@@ -89087,7 +89471,7 @@ var config_default = command35;
89087
89471
 
89088
89472
  // src/main.ts
89089
89473
  await init_context();
89090
- var VERSION = "1.696.2";
89474
+ var VERSION = "1.697.0";
89091
89475
  async function checkVersionSafe(cmd) {
89092
89476
  const mainCommand = cmd.getMainCommand();
89093
89477
  const upgradeCommand = mainCommand.getCommand("upgrade");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "windmill-cli",
3
- "version": "1.696.2",
3
+ "version": "1.697.0",
4
4
  "description": "CLI for Windmill",
5
5
  "license": "Apache 2.0",
6
6
  "type": "module",