windmill-cli 1.696.1 → 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 +542 -84
  2. package/package.json +1 -1
package/esm/main.js CHANGED
@@ -16440,6 +16440,48 @@ var init_login = __esm(async () => {
16440
16440
  ]);
16441
16441
  });
16442
16442
 
16443
+ // src/utils/http_guards.ts
16444
+ async function detectAuthGatewayChallenge(response, url) {
16445
+ const contentType = (response.headers.get("content-type") ?? "").toLowerCase();
16446
+ const cfMitigated = response.headers.get("cf-mitigated") ?? undefined;
16447
+ const looksHtml = contentType.includes("text/html");
16448
+ if (!looksHtml && cfMitigated !== "challenge")
16449
+ return;
16450
+ let snippet = "";
16451
+ try {
16452
+ snippet = (await response.clone().text()).slice(0, 256);
16453
+ } catch {}
16454
+ const isChallenge = cfMitigated === "challenge" || ACCESS_TITLE.test(snippet) || looksHtml && HTML_DOCTYPE.test(snippet);
16455
+ if (!isChallenge)
16456
+ return;
16457
+ throw new AuthGatewayChallengeError(url || response.url || "(unknown)", response.headers.get("cf-ray") ?? undefined, cfMitigated, response.status, snippet);
16458
+ }
16459
+ var ACCESS_TITLE, HTML_DOCTYPE, AuthGatewayChallengeError;
16460
+ var init_http_guards = __esm(() => {
16461
+ ACCESS_TITLE = /<title>\s*Sign in[^<]*Cloudflare Access\s*<\/title>/i;
16462
+ HTML_DOCTYPE = /^\s*<(!doctype|html)/i;
16463
+ AuthGatewayChallengeError = class AuthGatewayChallengeError extends Error {
16464
+ url;
16465
+ cfRay;
16466
+ cfMitigated;
16467
+ status;
16468
+ bodySnippet;
16469
+ name = "AuthGatewayChallengeError";
16470
+ constructor(url, cfRay, cfMitigated, status, bodySnippet) {
16471
+ const cfPart = [
16472
+ cfRay ? `cf-ray=${cfRay}` : null,
16473
+ cfMitigated ? `cf-mitigated=${cfMitigated}` : null
16474
+ ].filter(Boolean).join(", ");
16475
+ super(`Got an HTML response from ${url} (status ${status}${cfPart ? `, ${cfPart}` : ""}). ` + `The request was intercepted by an upstream auth gateway (likely Cloudflare Access) ` + `before reaching Windmill. Verify the runner is on the right network or pass service-token headers via the HEADERS env var ` + `(e.g. HEADERS="CF-Access-Client-Id: <id>, CF-Access-Client-Secret: <secret>"). ` + `Body starts with: ${JSON.stringify(bodySnippet.slice(0, 120))}`);
16476
+ this.url = url;
16477
+ this.cfRay = cfRay;
16478
+ this.cfMitigated = cfMitigated;
16479
+ this.status = status;
16480
+ this.bodySnippet = bodySnippet;
16481
+ }
16482
+ };
16483
+ });
16484
+
16443
16485
  // windmill-utils-internal/src/config/config.ts
16444
16486
  import { stat as stat2, mkdir } from "node:fs/promises";
16445
16487
  function getEnv2(key) {
@@ -16710,7 +16752,7 @@ var init_OpenAPI = __esm(() => {
16710
16752
  PASSWORD: undefined,
16711
16753
  TOKEN: getEnv3("WM_TOKEN"),
16712
16754
  USERNAME: undefined,
16713
- VERSION: "1.696.1",
16755
+ VERSION: "1.697.0",
16714
16756
  WITH_CREDENTIALS: true,
16715
16757
  interceptors: {
16716
16758
  request: new Interceptors,
@@ -17174,6 +17216,8 @@ __export(exports_services_gen, {
17174
17216
  loadFilePreview: () => loadFilePreview,
17175
17217
  loadFileMetadata: () => loadFileMetadata,
17176
17218
  loadCsvPreview: () => loadCsvPreview,
17219
+ listWsSpecificVersions: () => listWsSpecificVersions,
17220
+ listWsSpecific: () => listWsSpecific,
17177
17221
  listWorkspacesAsSuperAdmin: () => listWorkspacesAsSuperAdmin,
17178
17222
  listWorkspaces: () => listWorkspaces,
17179
17223
  listWorkspaceInvites: () => listWorkspaceInvites,
@@ -19625,6 +19669,26 @@ var backendVersion = () => {
19625
19669
  body: data3.requestBody,
19626
19670
  mediaType: "application/json"
19627
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
+ });
19628
19692
  }, setPublicAppRateLimit = (data3) => {
19629
19693
  return request(OpenAPI, {
19630
19694
  method: "POST",
@@ -25331,7 +25395,7 @@ Found ${datatables.length} datatable(s):`);
25331
25395
  workspace: workspace.workspaceId,
25332
25396
  requestBody: {
25333
25397
  id: trueWorkspaceId,
25334
- name: opts.createWorkspaceName ?? trueWorkspaceId,
25398
+ name: opts.createWorkspaceName ?? workspaceName ?? trueWorkspaceId,
25335
25399
  color: forkColor
25336
25400
  }
25337
25401
  });
@@ -25347,7 +25411,7 @@ Found ${datatables.length} datatable(s):`);
25347
25411
  workspace: workspace.workspaceId,
25348
25412
  requestBody: {
25349
25413
  id: trueWorkspaceId,
25350
- name: opts.createWorkspaceName ?? trueWorkspaceId,
25414
+ name: opts.createWorkspaceName ?? workspaceName ?? trueWorkspaceId,
25351
25415
  color: forkColor,
25352
25416
  forked_datatables: forkedDatatables
25353
25417
  }
@@ -25433,9 +25497,21 @@ var init_fork = __esm(async () => {
25433
25497
  });
25434
25498
 
25435
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
+ }
25436
25506
  function folderName(path2) {
25437
25507
  return path2.replace(/^f\//, "");
25438
25508
  }
25509
+ function stripOperationalStateOnUpdate(payload, alreadyExists) {
25510
+ if (!alreadyExists)
25511
+ return payload;
25512
+ const { mode: _mode, enabled: _enabled, ...rest } = payload;
25513
+ return rest;
25514
+ }
25439
25515
  function getSubModules(flowModule) {
25440
25516
  const type = flowModule?.value?.type;
25441
25517
  if (type === "forloopflow" || type === "whileloopflow") {
@@ -25489,6 +25565,10 @@ async function checkItemExists(provider, kind, path2, workspace) {
25489
25565
  return provider.existsResourceType({ workspace, path: path2 });
25490
25566
  } else if (kind === "folder") {
25491
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 });
25492
25572
  }
25493
25573
  throw new Error(`Unknown kind: ${kind}`);
25494
25574
  }
@@ -25725,6 +25805,48 @@ async function deployItem(provider, kind, path2, workspaceFrom, workspaceTo, onB
25725
25805
  }
25726
25806
  });
25727
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
+ }
25728
25850
  } else {
25729
25851
  throw new Error(`Unknown kind: ${kind}`);
25730
25852
  }
@@ -25753,6 +25875,10 @@ async function deleteItemInWorkspace(provider, kind, path2, workspace) {
25753
25875
  await provider.deleteResourceType({ workspace, path: path2 });
25754
25876
  } else if (kind === "folder") {
25755
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 });
25756
25882
  } else {
25757
25883
  throw new Error(`Deletion not supported for kind: ${kind}`);
25758
25884
  }
@@ -25772,10 +25898,30 @@ async function getOnBehalfOf(provider, kind, path2, workspace) {
25772
25898
  } else if (kind === "app" || kind === "raw_app") {
25773
25899
  const app = await provider.getAppByPath({ workspace, path: path2 });
25774
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 });
25775
25906
  }
25776
25907
  } catch {}
25777
25908
  return;
25778
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
+ });
25779
25925
 
25780
25926
  // node_modules/@cliffy/prompt/checkbox.js
25781
25927
  var exports_checkbox = {};
@@ -25999,6 +26145,111 @@ var init_checkbox = __esm(async () => {
25999
26145
  });
26000
26146
 
26001
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
+ }
26002
26253
  async function mergeWorkspaces(opts) {
26003
26254
  const workspace = await tryResolveBranchWorkspace(opts);
26004
26255
  if (!workspace) {
@@ -26049,6 +26300,10 @@ async function mergeWorkspaces(opts) {
26049
26300
  summaryRows.push(["Resource Types", String(summary.resource_types_changed)]);
26050
26301
  if (summary.folders_changed > 0)
26051
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)]);
26052
26307
  summaryRows.push(["Total", String(summary.total_diffs)]);
26053
26308
  if (summary.conflicts > 0)
26054
26309
  summaryRows.push([
@@ -26114,11 +26369,9 @@ Direction: ${colors.bold(direction === "to-parent" ? `Fork → Parent (${parentW
26114
26369
  if (opts.all) {
26115
26370
  selectedDiffs = selectableDiffs;
26116
26371
  } else if (opts.skipConflicts) {
26117
- 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)));
26118
26373
  } else if (opts.yes && !opts.include && !opts.exclude) {
26119
- if (direction === "to-fork") {
26120
- selectedDiffs = selectableDiffs.filter((d) => !(d.ahead > 0 && d.behind > 0));
26121
- }
26374
+ selectedDiffs = selectableDiffs.filter((d) => !isTriggerOrScheduleKind(d.kind) && (direction !== "to-fork" || !(d.ahead > 0 && d.behind > 0)));
26122
26375
  } else if (!opts.yes) {
26123
26376
  const { Checkbox: Checkbox2 } = await init_checkbox().then(() => exports_checkbox);
26124
26377
  const defaultForToFork = direction === "to-fork";
@@ -26126,11 +26379,12 @@ Direction: ${colors.bold(direction === "to-parent" ? `Fork → Parent (${parentW
26126
26379
  message: `Select items to deploy (${selectableDiffs.length} available):`,
26127
26380
  options: selectableDiffs.map((d) => {
26128
26381
  const isConflict = d.ahead > 0 && d.behind > 0;
26382
+ const isTriggerOrSchedule = isTriggerOrScheduleKind(d.kind);
26129
26383
  const label = `${d.kind}:${d.path}${isConflict ? colors.red(" [CONFLICT]") : ""}`;
26130
26384
  return {
26131
26385
  name: label,
26132
26386
  value: `${d.kind}:${d.path}`,
26133
- checked: defaultForToFork ? !isConflict : true
26387
+ checked: !isTriggerOrSchedule && (defaultForToFork ? !isConflict : true)
26134
26388
  };
26135
26389
  })
26136
26390
  });
@@ -26219,6 +26473,8 @@ var init_merge = __esm(async () => {
26219
26473
  init_log();
26220
26474
  init_client();
26221
26475
  init_services_gen();
26476
+ init_OpenAPI();
26477
+ init_deploy();
26222
26478
  await init_context();
26223
26479
  provider = {
26224
26480
  existsFlowByPath,
@@ -26258,7 +26514,28 @@ var init_merge = __esm(async () => {
26258
26514
  getFolder,
26259
26515
  createFolder,
26260
26516
  updateFolder,
26261
- 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
26262
26539
  };
26263
26540
  });
26264
26541
 
@@ -28116,7 +28393,12 @@ async function fetchVersion(baseUrl2) {
28116
28393
  requestHeaders.set(key, value);
28117
28394
  }
28118
28395
  }
28119
- const response = await fetch(new URL(new URL(baseUrl2).origin + "/api/version"), { headers: requestHeaders, method: "GET" });
28396
+ const versionUrl = new URL(new URL(baseUrl2).origin + "/api/version");
28397
+ const response = await fetch(versionUrl, {
28398
+ headers: requestHeaders,
28399
+ method: "GET"
28400
+ });
28401
+ await detectAuthGatewayChallenge(response, versionUrl.toString());
28120
28402
  if (!response.ok) {
28121
28403
  await response.text();
28122
28404
  throw new Error(`Failed to fetch version: ${response.status} ${response.statusText}`);
@@ -28149,6 +28431,7 @@ var init_context = __esm(async () => {
28149
28431
  init_colors2();
28150
28432
  init_log();
28151
28433
  init_mod6();
28434
+ init_http_guards();
28152
28435
  init_git();
28153
28436
  init_levenshtein_distance();
28154
28437
  await __promiseAll([
@@ -33636,23 +33919,23 @@ async function logQueueStatus(workspace, jobId, label = "Job ") {
33636
33919
  try {
33637
33920
  const job = await getJob({ workspace, id: jobId });
33638
33921
  if (!job)
33639
- return;
33922
+ return false;
33640
33923
  if (job.running === true) {
33641
33924
  info(colors.gray(`${label}${jobId}: running, waiting for completion...`));
33642
- return;
33925
+ return true;
33643
33926
  }
33644
33927
  if (typeof job.running !== "boolean") {
33645
- return;
33928
+ return false;
33646
33929
  }
33647
33930
  const scheduledFor = job.scheduled_for;
33648
33931
  if (!scheduledFor) {
33649
33932
  info(colors.gray(`${label}${jobId}: queued, waiting for executor...`));
33650
- return;
33933
+ return true;
33651
33934
  }
33652
33935
  const scheduledForMs = new Date(scheduledFor).getTime();
33653
33936
  if (!Number.isFinite(scheduledForMs)) {
33654
33937
  info(colors.gray(`${label}${jobId}: queued, waiting for executor...`));
33655
- return;
33938
+ return true;
33656
33939
  }
33657
33940
  try {
33658
33941
  const pos = await getQueuePosition({
@@ -33665,10 +33948,14 @@ async function logQueueStatus(workspace, jobId, label = "Job ") {
33665
33948
  } else {
33666
33949
  info(colors.gray(`${label}${jobId}: queued, waiting for executor...`));
33667
33950
  }
33951
+ return true;
33668
33952
  } catch {
33669
33953
  info(colors.gray(`${label}${jobId}: queued, waiting for executor...`));
33954
+ return true;
33670
33955
  }
33671
- } catch {}
33956
+ } catch {
33957
+ return false;
33958
+ }
33672
33959
  }
33673
33960
  async function pollJobWithQueueLogging(workspace, jobId, options) {
33674
33961
  const fastPollIntervalMs = options?.fastPollIntervalMs ?? DEFAULT_FAST_POLL_INTERVAL_MS;
@@ -33677,6 +33964,7 @@ async function pollJobWithQueueLogging(workspace, jobId, options) {
33677
33964
  const label = options?.label ? `[${options.label}] ` : "Job ";
33678
33965
  const startedAt = Date.now();
33679
33966
  let lastQueueLogAt = Date.now();
33967
+ let lastHeartbeatAt = Date.now();
33680
33968
  let consecutiveErrors = 0;
33681
33969
  while (true) {
33682
33970
  try {
@@ -33692,19 +33980,26 @@ async function pollJobWithQueueLogging(workspace, jobId, options) {
33692
33980
  } catch (err) {
33693
33981
  consecutiveErrors++;
33694
33982
  warn(colors.yellow(`${label}${jobId}: error checking job status (${consecutiveErrors}/${MAX_CONSECUTIVE_POLL_ERRORS}): ${err?.message ?? err}`));
33983
+ lastHeartbeatAt = Date.now();
33695
33984
  if (consecutiveErrors >= MAX_CONSECUTIVE_POLL_ERRORS) {
33696
33985
  throw new Error(`Giving up polling job ${jobId} after ${MAX_CONSECUTIVE_POLL_ERRORS} consecutive errors. Last error: ${err?.message ?? err}`);
33697
33986
  }
33698
33987
  }
33699
33988
  if (Date.now() - lastQueueLogAt >= QUEUE_LOG_INTERVAL_MS) {
33700
33989
  lastQueueLogAt = Date.now();
33701
- await logQueueStatus(workspace, jobId, label);
33990
+ const logged = await logQueueStatus(workspace, jobId, label);
33991
+ if (logged)
33992
+ lastHeartbeatAt = Date.now();
33993
+ }
33994
+ if (Date.now() - lastHeartbeatAt >= HEARTBEAT_INTERVAL_MS) {
33995
+ lastHeartbeatAt = Date.now();
33996
+ info(colors.gray(`${label}${jobId}: still polling, queue status unavailable...`));
33702
33997
  }
33703
33998
  const delayMs = Date.now() - startedAt < fastPollDurationMs ? fastPollIntervalMs : slowPollIntervalMs;
33704
33999
  await new Promise((resolve6) => setTimeout(resolve6, delayMs));
33705
34000
  }
33706
34001
  }
33707
- var DEFAULT_FAST_POLL_INTERVAL_MS = 100, DEFAULT_FAST_POLL_DURATION_MS = 2000, DEFAULT_SLOW_POLL_INTERVAL_MS = 2000, QUEUE_LOG_INTERVAL_MS = 5000, MAX_CONSECUTIVE_POLL_ERRORS = 10;
34002
+ var DEFAULT_FAST_POLL_INTERVAL_MS = 100, DEFAULT_FAST_POLL_DURATION_MS = 2000, DEFAULT_SLOW_POLL_INTERVAL_MS = 2000, QUEUE_LOG_INTERVAL_MS = 5000, HEARTBEAT_INTERVAL_MS = 60000, MAX_CONSECUTIVE_POLL_ERRORS = 10;
33708
34003
  var init_job_polling = __esm(() => {
33709
34004
  init_services_gen();
33710
34005
  init_log();
@@ -46847,6 +47142,7 @@ async function downloadZip(workspace, plainSecrets, skipVariables, skipResources
46847
47142
  const baseUrl2 = workspace.remote + "api/w/" + workspace.workspaceId + "/workspaces/tarball?";
46848
47143
  const zipUrl = baseUrl2 + "archive_type=zip" + baseParams;
46849
47144
  const zipResponse = await fetch(zipUrl, { headers: requestHeaders, method: "GET" });
47145
+ await detectAuthGatewayChallenge(zipResponse, zipUrl);
46850
47146
  if (zipResponse.ok) {
46851
47147
  debug("Downloaded zip archive successfully");
46852
47148
  const blob = await zipResponse.blob();
@@ -46857,6 +47153,7 @@ async function downloadZip(workspace, plainSecrets, skipVariables, skipResources
46857
47153
  debug("Zip archive not supported by backend, falling back to tar");
46858
47154
  const tarUrl = baseUrl2 + "archive_type=tar" + baseParams;
46859
47155
  const tarResponse = await fetch(tarUrl, { headers: requestHeaders, method: "GET" });
47156
+ await detectAuthGatewayChallenge(tarResponse, tarUrl);
46860
47157
  if (tarResponse.ok) {
46861
47158
  debug("Downloaded tar archive successfully");
46862
47159
  return await parseTarResponse(tarResponse);
@@ -46885,6 +47182,7 @@ var init_pull = __esm(async () => {
46885
47182
  init_mod3();
46886
47183
  init_log();
46887
47184
  init_tar_stream();
47185
+ init_http_guards();
46888
47186
  await init_utils();
46889
47187
  import_jszip = __toESM(require_lib3(), 1);
46890
47188
  command3 = new Command().description("Pull all definitions in the current workspace from the API and write them to disk.").arguments("<dir:string>").action(stub);
@@ -61279,33 +61577,34 @@ async function resolveWsNameForGitBranch(branchName) {
61279
61577
  const match2 = findWorkspaceByGitBranch(config.workspaces, branchName);
61280
61578
  return match2 ? match2[0] : branchName;
61281
61579
  }
61282
- function getWorkspaceSpecificTypes() {
61283
- return {
61284
- variable: ".variable.yaml",
61285
- resource: ".resource.yaml",
61286
- schedule: ".schedule.yaml",
61287
- ...Object.fromEntries(TRIGGER_TYPES.map((t) => [`${t}_trigger`, `.${t}_trigger.yaml`]))
61288
- };
61580
+ function getWorkspaceSpecificTypeKinds() {
61581
+ return [
61582
+ "variable",
61583
+ "resource",
61584
+ "schedule",
61585
+ ...TRIGGER_TYPES.map((t) => `${t}_trigger`)
61586
+ ];
61289
61587
  }
61290
61588
  function isTriggerFile(path7) {
61291
- 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`));
61292
61590
  }
61293
61591
  function isScheduleFile(path7) {
61294
- return path7.endsWith(".schedule.yaml");
61592
+ return path7.endsWith(".schedule.yaml") || path7.endsWith(".schedule.json");
61295
61593
  }
61296
61594
  function getFileTypeSuffix(path7) {
61297
- for (const [_, suffix] of Object.entries(getWorkspaceSpecificTypes())) {
61298
- if (path7.endsWith(suffix)) {
61299
- return suffix;
61300
- }
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`;
61301
61600
  }
61302
- const resourceFileMatch = path7.match(/(\\.resource\\.file\\..+)$/);
61601
+ const resourceFileMatch = path7.match(/(\.resource\.file\..+)$/);
61303
61602
  if (resourceFileMatch) {
61304
61603
  return resourceFileMatch[1];
61305
61604
  }
61306
61605
  return null;
61307
61606
  }
61308
- function buildYamlTypePattern() {
61607
+ function buildItemTypePattern() {
61309
61608
  const basicTypes = ["variable", "resource", "schedule"];
61310
61609
  const triggerTypes = TRIGGER_TYPES.map((t) => `${t}_trigger`);
61311
61610
  return `((${basicTypes.join("|")})|(${triggerTypes.join("|")}))`;
@@ -61373,10 +61672,14 @@ function getSpecificItemsForCurrentBranch(config, workspaceNameOverride) {
61373
61672
  function matchesPatterns(path7, patterns) {
61374
61673
  return patterns.some((pattern) => minimatch(path7, pattern));
61375
61674
  }
61675
+ function normalizeJsonToYaml(path7) {
61676
+ return path7.endsWith(".json") ? path7.slice(0, -".json".length) + ".yaml" : path7;
61677
+ }
61376
61678
  function isItemTypeConfigured(path7, specificItems) {
61377
61679
  if (!specificItems) {
61378
61680
  return false;
61379
61681
  }
61682
+ path7 = normalizeJsonToYaml(path7);
61380
61683
  if (path7.endsWith(".variable.yaml")) {
61381
61684
  return specificItems.variables !== undefined;
61382
61685
  }
@@ -61404,6 +61707,7 @@ function isSpecificItem(path7, specificItems) {
61404
61707
  if (!specificItems) {
61405
61708
  return false;
61406
61709
  }
61710
+ path7 = normalizeJsonToYaml(path7);
61407
61711
  if (path7.endsWith(".variable.yaml")) {
61408
61712
  return specificItems.variables ? matchesPatterns(path7, specificItems.variables) : false;
61409
61713
  }
@@ -61447,12 +61751,13 @@ function toWorkspaceSpecificPath(basePath, workspaceName) {
61447
61751
  if (sanitizedName !== workspaceName) {
61448
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.`);
61449
61753
  }
61450
- if (basePath.endsWith("/folder.meta.yaml")) {
61451
- const pathWithoutMeta = basePath.substring(0, basePath.length - "/folder.meta.yaml".length);
61452
- 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]}`;
61453
61757
  }
61454
- if (basePath === "settings.yaml") {
61455
- return `settings.${sanitizedName}.yaml`;
61758
+ const settingsMatch = basePath.match(/^settings\.(yaml|json)$/);
61759
+ if (settingsMatch) {
61760
+ return `settings.${sanitizedName}.${settingsMatch[1]}`;
61456
61761
  }
61457
61762
  const resourceFileMatch = basePath.match(/^(.+?)(\.resource\.file\..+)$/);
61458
61763
  let extension;
@@ -61473,13 +61778,15 @@ function toWorkspaceSpecificPath(basePath, workspaceName) {
61473
61778
  function fromWorkspaceSpecificPath(workspaceSpecificPath, workspaceName) {
61474
61779
  const sanitizedName = workspaceName.replace(/[\/\\:*?"<>|.]/g, "_");
61475
61780
  const escapedName = sanitizedName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
61476
- const folderPattern = new RegExp(`/folder\\.${escapedName}\\.meta\\.yaml$`);
61477
- if (folderPattern.test(workspaceSpecificPath)) {
61478
- 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]}`);
61479
61785
  }
61480
- const settingsPattern = new RegExp(`^settings\\.${escapedName}\\.yaml$`);
61481
- if (settingsPattern.test(workspaceSpecificPath)) {
61482
- 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]}`;
61483
61790
  }
61484
61791
  const resourceFilePattern = new RegExp(`\\.${escapedName}(\\.resource\\.file\\..+)$`);
61485
61792
  const resourceFileMatch = workspaceSpecificPath.match(resourceFilePattern);
@@ -61488,12 +61795,12 @@ function fromWorkspaceSpecificPath(workspaceSpecificPath, workspaceName) {
61488
61795
  const pathWithoutBranchAndExtension2 = workspaceSpecificPath.substring(0, workspaceSpecificPath.length - `.${sanitizedName}${extension2}`.length);
61489
61796
  return `${pathWithoutBranchAndExtension2}${extension2}`;
61490
61797
  }
61491
- const yamlPattern = new RegExp(`\\.${escapedName}(\\.${buildYamlTypePattern()}\\.yaml)$`);
61492
- const yamlMatch = workspaceSpecificPath.match(yamlPattern);
61493
- if (!yamlMatch) {
61798
+ const typedPattern = new RegExp(`\\.${escapedName}(\\.${buildItemTypePattern()}\\.(yaml|json))$`);
61799
+ const typedMatch = workspaceSpecificPath.match(typedPattern);
61800
+ if (!typedMatch) {
61494
61801
  return workspaceSpecificPath;
61495
61802
  }
61496
- const extension = yamlMatch[1];
61803
+ const extension = typedMatch[1];
61497
61804
  const pathWithoutBranchAndExtension = workspaceSpecificPath.substring(0, workspaceSpecificPath.length - `.${sanitizedName}${extension}`.length);
61498
61805
  return `${pathWithoutBranchAndExtension}${extension}`;
61499
61806
  }
@@ -61529,14 +61836,14 @@ function isCurrentWorkspaceFile(path7, workspaceNameOverride) {
61529
61836
  const escapedName = sanitizedName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
61530
61837
  let pattern = workspacePatternCache.get(currentWorkspace);
61531
61838
  if (!pattern) {
61532
- 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)$`);
61533
61840
  workspacePatternCache.set(currentWorkspace, pattern);
61534
61841
  }
61535
61842
  return pattern.test(path7);
61536
61843
  }
61537
61844
  function isWorkspaceSpecificFile(path7) {
61538
- const yamlTypePattern = buildYamlTypePattern();
61539
- 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);
61540
61847
  }
61541
61848
  var workspacePatternCache;
61542
61849
  var init_specific_items = __esm(async () => {
@@ -63752,7 +64059,7 @@ async function readFilesetDirectory(dirPath) {
63752
64059
  await walk(dirPath, "");
63753
64060
  return result;
63754
64061
  }
63755
- async function pushResource(workspace, remotePath, resource, localResource, originalLocalPath) {
64062
+ async function pushResource(workspace, remotePath, resource, localResource, originalLocalPath, wsSpecific) {
63756
64063
  remotePath = removeType(remotePath, "resource");
63757
64064
  try {
63758
64065
  resource = await getResource({
@@ -63789,7 +64096,7 @@ async function pushResource(workspace, remotePath, resource, localResource, orig
63789
64096
  await updateResource({
63790
64097
  workspace,
63791
64098
  path: remotePath.replaceAll(SEP6, "/"),
63792
- requestBody: { ...localResource }
64099
+ requestBody: { ...localResource, ...wsSpecific !== undefined ? { ws_specific: wsSpecific } : {} }
63793
64100
  });
63794
64101
  } else {
63795
64102
  await resolveInlineContent();
@@ -63801,7 +64108,8 @@ async function pushResource(workspace, remotePath, resource, localResource, orig
63801
64108
  workspace,
63802
64109
  requestBody: {
63803
64110
  path: remotePath.replaceAll(SEP6, "/"),
63804
- ...localResource
64111
+ ...localResource,
64112
+ ...wsSpecific !== undefined ? { ws_specific: wsSpecific } : {}
63805
64113
  }
63806
64114
  });
63807
64115
  }
@@ -64542,6 +64850,7 @@ async function updateFlow2(workspace, flow_value, remotePath, rawWorkspaceDepend
64542
64850
  },
64543
64851
  body: JSON.stringify(body)
64544
64852
  });
64853
+ await detectAuthGatewayChallenge(queueResponse, `${workspace.remote}api/w/${workspace.workspaceId}/jobs/run/flow_dependencies_async`);
64545
64854
  if (!queueResponse.ok) {
64546
64855
  let bodyText = "";
64547
64856
  try {
@@ -64569,6 +64878,7 @@ var init_flow_metadata = __esm(async () => {
64569
64878
  init_log();
64570
64879
  init_extractor();
64571
64880
  init_path_assigner();
64881
+ init_http_guards();
64572
64882
  init_script_common();
64573
64883
  init_job_polling();
64574
64884
  await __promiseAll([
@@ -64972,6 +65282,7 @@ var init_generate_metadata = __esm(async () => {
64972
65282
  // src/commands/sync/sync.ts
64973
65283
  var exports_sync = {};
64974
65284
  __export(exports_sync, {
65285
+ yamlSortedEntries: () => yamlSortedEntries,
64975
65286
  yamlOptions: () => yamlOptions,
64976
65287
  resolveWsNameForConfigFromFlags: () => resolveWsNameForConfigFromFlags,
64977
65288
  readDirRecursiveWithIgnore: () => readDirRecursiveWithIgnore2,
@@ -64986,11 +65297,74 @@ __export(exports_sync, {
64986
65297
  extractFieldsForRawApps: () => extractFieldsForRawApps,
64987
65298
  elementsToMap: () => elementsToMap,
64988
65299
  default: () => sync_default,
65300
+ computeWsSpecificFlagOnlyPushes: () => computeWsSpecificFlagOnlyPushes,
64989
65301
  FSFSElement: () => FSFSElement
64990
65302
  });
64991
65303
  import { writeFile as writeFile7, readdir as readdir4, stat as stat7, rm, copyFile, mkdir as mkdir5 } from "node:fs/promises";
64992
65304
  import * as path11 from "node:path";
64993
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
+ }
64994
65368
  function resolveWsNameFromBranch(opts, branchName) {
64995
65369
  const match2 = findWorkspaceByGitBranch(opts.workspaces, branchName);
64996
65370
  return match2 ? match2[0] : branchName;
@@ -65191,6 +65565,14 @@ function prioritizeName(name) {
65191
65565
  return "azz";
65192
65566
  return name;
65193
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
+ }
65194
65576
  function extractFields(fields) {
65195
65577
  Object.entries(fields).forEach(([k, v]) => {
65196
65578
  if (typeof v == "object") {
@@ -65314,7 +65696,7 @@ function extractInlineScriptsForApps(key, rec, pathAssigner, toId, removeSchema)
65314
65696
  return [];
65315
65697
  }
65316
65698
  if (typeof rec == "object") {
65317
- return Object.entries(rec).flatMap(([k, v]) => {
65699
+ return yamlSortedEntries(rec).flatMap(([k, v]) => {
65318
65700
  if (k == "inlineScript" && v != null && typeof v == "object") {
65319
65701
  rec["type"] = undefined;
65320
65702
  const o = v;
@@ -65381,7 +65763,7 @@ async function findFilesetResourceFile(changePath) {
65381
65763
  }
65382
65764
  throw new Error(`No resource metadata file found for fileset resource: ${changePath}`);
65383
65765
  }
65384
- async function pushFilesetParentResource(childPath, workspaceId, alreadySynced, cachedWsName) {
65766
+ async function pushFilesetParentResource(childPath, workspaceId, alreadySynced, cachedWsName, specificItems) {
65385
65767
  let resourceFilePath;
65386
65768
  try {
65387
65769
  resourceFilePath = await findFilesetResourceFile(childPath);
@@ -65394,10 +65776,14 @@ async function pushFilesetParentResource(childPath, workspaceId, alreadySynced,
65394
65776
  alreadySynced.push(resourceFilePath);
65395
65777
  const newObj = parseFromPath(resourceFilePath, await readTextFile(resourceFilePath));
65396
65778
  let serverPath = resourceFilePath;
65779
+ let wsSpecific = false;
65397
65780
  if (cachedWsName && isWorkspaceSpecificFile(resourceFilePath)) {
65398
65781
  serverPath = fromWorkspaceSpecificPath(resourceFilePath, cachedWsName);
65782
+ wsSpecific = true;
65783
+ } else if (specificItems && isSpecificItem(childPath, specificItems)) {
65784
+ wsSpecific = true;
65399
65785
  }
65400
- await pushResource(workspaceId, serverPath, undefined, newObj, resourceFilePath);
65786
+ await pushResource(workspaceId, serverPath, undefined, newObj, resourceFilePath, wsSpecific ? true : undefined);
65401
65787
  return { status: "pushed", resourceFilePath };
65402
65788
  }
65403
65789
  function ZipFSElement(zip, useYaml, defaultTs, resourceTypeToFormatExtension, resourceTypeToIsFileset, ignoreCodebaseChanges, stripOnBehalfOf) {
@@ -66453,8 +66839,11 @@ async function pull(opts) {
66453
66839
  wsNameForConfig = inferWsNameFromProfile(opts, workspace);
66454
66840
  }
66455
66841
  const effectiveOpts = await resolveEffectiveSyncOptions(workspace, opts, opts.promotion, wsNameForConfig);
66456
- const specificItems = getSpecificItemsForCurrentBranch(opts, wsNameForConfig);
66457
- 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;
66458
66847
  opts = mergeCliWithEffectiveOptions(originalCliOpts, effectiveOpts);
66459
66848
  const codebases = await listSyncCodebases(opts);
66460
66849
  info(colors.gray("Computing the files to update locally to match remote (taking wmill.yaml into account)"));
@@ -66473,6 +66862,17 @@ async function pull(opts) {
66473
66862
  const local = !opts.stateful ? await FSFSElement(process.cwd(), codebases, true) : await FSFSElement(path11.join(process.cwd(), ".wmill"), [], true);
66474
66863
  const { changes, localMap } = await compareDynFSElement(remote, local, await ignoreF(opts), opts.json ?? false, opts, false, codebases, true, specificItems, wsNameForFiles, true);
66475
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
+ }
66476
66876
  if (opts.dryRun && opts.jsonOutput) {
66477
66877
  const result = {
66478
66878
  success: true,
@@ -66696,6 +67096,8 @@ function prettyChanges(changes, specificItems, branchOverride, folderDefaultAnno
66696
67096
  showDiff(change.before, change.after);
66697
67097
  }
66698
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)")));
66699
67101
  }
66700
67102
  }
66701
67103
  }
@@ -66735,8 +67137,12 @@ async function push4(opts) {
66735
67137
  wsNameForConfig = inferWsNameFromProfile(opts, workspace);
66736
67138
  }
66737
67139
  const effectiveOpts = await resolveEffectiveSyncOptions(workspace, opts, opts.promotion, wsNameForConfig);
66738
- const specificItems = getSpecificItemsForCurrentBranch(opts, wsNameForConfig);
66739
- 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;
66740
67146
  opts = mergeCliWithEffectiveOptions(originalCliOpts, effectiveOpts);
66741
67147
  if (opts.lint) {
66742
67148
  info("Running lint validation before push...");
@@ -66788,7 +67194,18 @@ Push aborted: ${lockIssues.length} script(s) missing locks.`));
66788
67194
  } catch {}
66789
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);
66790
67196
  const local = await FSFSElement(path11.join(process.cwd(), ""), codebases, false);
66791
- 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
+ }
66792
67209
  const rawWorkspaceDependencies = await getRawWorkspaceDependencies(true);
66793
67210
  const tracker = await buildTracker(changes);
66794
67211
  const autoRegenerate = !!opts.autoMetadata;
@@ -67043,7 +67460,7 @@ Run 'wmill folder add-missing' to create them locally, then push again.`;
67043
67460
  userIsAdminOrDeployer,
67044
67461
  userEmail: user.email
67045
67462
  };
67046
- 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);
67047
67464
  } else if (folderDefaultAnnotations && folderDefaultAnnotations.size > 0) {
67048
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.`));
67049
67466
  }
@@ -67144,10 +67561,14 @@ Run 'wmill folder add-missing' to create them locally, then push again.`;
67144
67561
  const newObj2 = parseFromPath(resourceFilePath, await readTextFile(resourceFilePath));
67145
67562
  let serverPath = resourceFilePath;
67146
67563
  const currentBranch = cachedWsNameForPush;
67564
+ let isFileResWsSpecific = false;
67147
67565
  if (currentBranch && isWorkspaceSpecificFile(resourceFilePath)) {
67148
67566
  serverPath = fromWorkspaceSpecificPath(resourceFilePath, currentBranch);
67567
+ isFileResWsSpecific = true;
67568
+ } else if (specificItems && isSpecificItem(change.path, specificItems)) {
67569
+ isFileResWsSpecific = true;
67149
67570
  }
67150
- await pushResource(workspace.workspaceId, serverPath, undefined, newObj2, resourceFilePath);
67571
+ await pushResource(workspace.workspaceId, serverPath, undefined, newObj2, resourceFilePath, isFileResWsSpecific ? true : undefined);
67151
67572
  if (stateTarget) {
67152
67573
  await writeFile7(stateTarget, change.after, "utf-8");
67153
67574
  }
@@ -67155,7 +67576,7 @@ Run 'wmill folder add-missing' to create them locally, then push again.`;
67155
67576
  }
67156
67577
  }
67157
67578
  if (isFilesetResource(change.path)) {
67158
- const result = await pushFilesetParentResource(change.path, workspace.workspaceId, alreadySynced, cachedWsNameForPush);
67579
+ const result = await pushFilesetParentResource(change.path, workspace.workspaceId, alreadySynced, cachedWsNameForPush, specificItems);
67159
67580
  if (result.status === "parent-missing") {
67160
67581
  throw new Error(`No resource metadata file found for fileset resource: ${change.path}`);
67161
67582
  }
@@ -67169,16 +67590,17 @@ Run 'wmill folder add-missing' to create them locally, then push again.`;
67169
67590
  const oldObj = parseFromPath(change.path, change.before);
67170
67591
  const newObj = parseFromPath(change.path, change.after);
67171
67592
  let originalWorkspaceSpecificPath;
67172
- if (specificItems && isSpecificItem(change.path, specificItems)) {
67593
+ const isWsSpecific = specificItems && isSpecificItem(change.path, specificItems);
67594
+ if (isWsSpecific) {
67173
67595
  originalWorkspaceSpecificPath = getWorkspaceSpecificPath(change.path, specificItems, wsNameForFiles);
67174
67596
  }
67175
- 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);
67176
67598
  if (stateTarget) {
67177
67599
  await writeFile7(stateTarget, change.after, "utf-8");
67178
67600
  }
67179
67601
  } else if (change.name === "added") {
67180
67602
  if (isFilesetResource(change.path)) {
67181
- await pushFilesetParentResource(change.path, workspace.workspaceId, alreadySynced, cachedWsNameForPush);
67603
+ await pushFilesetParentResource(change.path, workspace.workspaceId, alreadySynced, cachedWsNameForPush, specificItems);
67182
67604
  continue;
67183
67605
  }
67184
67606
  if (change.path.endsWith(".script.json") || change.path.endsWith(".script.yaml") || change.path.endsWith(".lock") || isFileResource(change.path)) {
@@ -67195,13 +67617,14 @@ Run 'wmill folder add-missing' to create them locally, then push again.`;
67195
67617
  }
67196
67618
  const obj = parseFromPath(change.path, change.content);
67197
67619
  let localFilePath = change.path;
67198
- if (specificItems && isSpecificItem(change.path, specificItems)) {
67620
+ const isAddedWsSpecific = specificItems && isSpecificItem(change.path, specificItems);
67621
+ if (isAddedWsSpecific) {
67199
67622
  const workspaceSpecificPath = getWorkspaceSpecificPath(change.path, specificItems, wsNameForFiles);
67200
67623
  if (workspaceSpecificPath) {
67201
67624
  localFilePath = workspaceSpecificPath;
67202
67625
  }
67203
67626
  }
67204
- 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);
67205
67628
  if (stateTarget) {
67206
67629
  await writeFile7(stateTarget, change.content, "utf-8");
67207
67630
  }
@@ -67214,7 +67637,7 @@ Run 'wmill folder add-missing' to create them locally, then push again.`;
67214
67637
  continue;
67215
67638
  }
67216
67639
  if (isFilesetResource(change.path)) {
67217
- await pushFilesetParentResource(change.path, workspace.workspaceId, alreadySynced, cachedWsNameForPush);
67640
+ await pushFilesetParentResource(change.path, workspace.workspaceId, alreadySynced, cachedWsNameForPush, specificItems);
67218
67641
  continue;
67219
67642
  }
67220
67643
  const typ = getTypeStrFromPath(change.path);
@@ -67483,6 +67906,23 @@ Run 'wmill folder add-missing' to create them locally, then push again.`;
67483
67906
  await rm(stateTarget);
67484
67907
  } catch {}
67485
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
+ }
67486
67926
  }
67487
67927
  }
67488
67928
  })();
@@ -68151,6 +68591,7 @@ async function fetchScriptLock(workspace, scriptContent, language, remotePath, r
68151
68591
  temp_script_refs: tempScriptRefs && Object.keys(tempScriptRefs).length > 0 ? tempScriptRefs : null
68152
68592
  })
68153
68593
  });
68594
+ await detectAuthGatewayChallenge(queueResponse, `${workspace.remote}api/w/${workspace.workspaceId}/jobs/run/dependencies_async`);
68154
68595
  if (!queueResponse.ok) {
68155
68596
  let bodyText = "";
68156
68597
  try {
@@ -68635,6 +69076,7 @@ var init_metadata = __esm(async () => {
68635
69076
  init_script_bootstrap();
68636
69077
  init_script_common();
68637
69078
  init_script_common();
69079
+ init_http_guards();
68638
69080
  init_parse_schema();
68639
69081
  init_job_polling();
68640
69082
  await __promiseAll([
@@ -69176,7 +69618,7 @@ async function traverseAndProcessInlineScripts(obj, processor, currentPath = [])
69176
69618
  ])));
69177
69619
  }
69178
69620
  const result = {};
69179
- for (const [key, value] of Object.entries(obj)) {
69621
+ for (const [key, value] of yamlSortedEntries(obj)) {
69180
69622
  if (key === "inlineScript" && typeof value === "object") {
69181
69623
  result[key] = await processor(value, {
69182
69624
  path: currentPath,
@@ -69336,6 +69778,7 @@ async function generateInlineScriptLock(workspace, content, language, scriptPath
69336
69778
  ...tempScriptRefs && Object.keys(tempScriptRefs).length > 0 ? { temp_script_refs: tempScriptRefs } : {}
69337
69779
  })
69338
69780
  });
69781
+ await detectAuthGatewayChallenge(queueResponse, `${workspace.remote}api/w/${workspace.workspaceId}/jobs/run/dependencies_async`);
69339
69782
  if (!queueResponse.ok) {
69340
69783
  const text = await queueResponse.text();
69341
69784
  throw new Error(`Dependency generation failed: ${queueResponse.status} ${queueResponse.statusText}
@@ -69516,6 +69959,7 @@ var init_app_metadata = __esm(async () => {
69516
69959
  init_colors2();
69517
69960
  init_log();
69518
69961
  init_script_common();
69962
+ init_http_guards();
69519
69963
  init_path_assigner();
69520
69964
  init_job_polling();
69521
69965
  await __promiseAll([
@@ -70615,13 +71059,20 @@ var DEFAULT_PORT = 4000, DEFAULT_HOST = "localhost", createHTML = (jsPath, cssPa
70615
71059
  <meta charset="UTF-8">
70616
71060
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
70617
71061
  <title>Windmill App Dev Preview</title>
70618
- <link rel="stylesheet" href="${cssPath}">
70619
71062
  <style>
70620
- * {
70621
- margin: 0;
70622
- padding: 0;
70623
- 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
+ }
70624
71072
  }
71073
+ </style>
71074
+ <link rel="stylesheet" href="${cssPath}">
71075
+ <style>
70625
71076
  body {
70626
71077
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
70627
71078
  'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
@@ -72228,7 +72679,7 @@ async function get6(opts, path19) {
72228
72679
  console.log(colors.bold("Account:") + " " + (v.account ?? "-"));
72229
72680
  }
72230
72681
  }
72231
- async function pushVariable(workspace, remotePath, variable, localVariable, plainSecrets) {
72682
+ async function pushVariable(workspace, remotePath, variable, localVariable, plainSecrets, wsSpecific) {
72232
72683
  remotePath = removeType(remotePath, "variable");
72233
72684
  debug(`Processing local variable ${remotePath}`);
72234
72685
  try {
@@ -72254,7 +72705,8 @@ async function pushVariable(workspace, remotePath, variable, localVariable, plai
72254
72705
  alreadyEncrypted: !plainSecrets,
72255
72706
  requestBody: {
72256
72707
  ...localVariable,
72257
- 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 } : {}
72258
72710
  }
72259
72711
  });
72260
72712
  } else {
@@ -72264,7 +72716,8 @@ async function pushVariable(workspace, remotePath, variable, localVariable, plai
72264
72716
  alreadyEncrypted: !plainSecrets,
72265
72717
  requestBody: {
72266
72718
  path: remotePath.replaceAll(SEP16, "/"),
72267
- ...localVariable
72719
+ ...localVariable,
72720
+ ...wsSpecific !== undefined ? { ws_specific: wsSpecific } : {}
72268
72721
  }
72269
72722
  });
72270
72723
  }
@@ -74681,7 +75134,7 @@ function showConflict(path21, local, remote) {
74681
75134
  info(`
74682
75135
  `);
74683
75136
  }
74684
- 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) {
74685
75138
  const typeEnding = getTypeStrFromPath(p);
74686
75139
  if (typeEnding === "app") {
74687
75140
  const appName = extractResourceName(p, "app");
@@ -74698,7 +75151,7 @@ async function pushObj(workspace, p, befObj, newObj, plainSecrets, alreadySynced
74698
75151
  } else if (typeEnding === "folder") {
74699
75152
  await pushFolder(workspace, p, befObj, newObj);
74700
75153
  } else if (typeEnding === "variable") {
74701
- await pushVariable(workspace, p, befObj, newObj, plainSecrets);
75154
+ await pushVariable(workspace, p, befObj, newObj, plainSecrets, wsSpecific);
74702
75155
  } else if (typeEnding === "flow") {
74703
75156
  const flowName = extractResourceName(p, "flow");
74704
75157
  if (!flowName) {
@@ -74708,7 +75161,7 @@ async function pushObj(workspace, p, befObj, newObj, plainSecrets, alreadySynced
74708
75161
  } else if (typeEnding === "resource") {
74709
75162
  if (!alreadySynced3.includes(p)) {
74710
75163
  alreadySynced3.push(p);
74711
- await pushResource(workspace, p, befObj, newObj, originalLocalPath || p);
75164
+ await pushResource(workspace, p, befObj, newObj, originalLocalPath || p, wsSpecific);
74712
75165
  }
74713
75166
  } else if (typeEnding === "resource-type") {
74714
75167
  await pushResourceType(workspace, p, befObj, newObj);
@@ -78615,6 +79068,7 @@ var dev_default2 = command25;
78615
79068
 
78616
79069
  // src/main.ts
78617
79070
  init_gen();
79071
+ init_http_guards();
78618
79072
  await __promiseAll([
78619
79073
  init_utils(),
78620
79074
  init_conf()
@@ -89017,7 +89471,7 @@ var config_default = command35;
89017
89471
 
89018
89472
  // src/main.ts
89019
89473
  await init_context();
89020
- var VERSION = "1.696.1";
89474
+ var VERSION = "1.697.0";
89021
89475
  async function checkVersionSafe(cmd) {
89022
89476
  const mainCommand = cmd.getMainCommand();
89023
89477
  const upgradeCommand = mainCommand.getCommand("upgrade");
@@ -89099,6 +89553,10 @@ async function main2() {
89099
89553
  if (extraHeaders) {
89100
89554
  OpenAPI.HEADERS = extraHeaders;
89101
89555
  }
89556
+ OpenAPI.interceptors.response.use(async (response) => {
89557
+ await detectAuthGatewayChallenge(response);
89558
+ return response;
89559
+ });
89102
89560
  await command36.parse(args);
89103
89561
  } catch (e) {
89104
89562
  if (e && typeof e === "object" && "name" in e && e.name === "ApiError") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "windmill-cli",
3
- "version": "1.696.1",
3
+ "version": "1.697.0",
4
4
  "description": "CLI for Windmill",
5
5
  "license": "Apache 2.0",
6
6
  "type": "module",