@postman-cse/onboarding-bootstrap 0.9.1 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.cjs CHANGED
@@ -22777,6 +22777,7 @@ var require_stringify = __commonJS({
22777
22777
  nullStr: "null",
22778
22778
  simpleKeys: false,
22779
22779
  singleQuote: null,
22780
+ trailingComma: false,
22780
22781
  trueStr: "true",
22781
22782
  verifyAliasOrder: true
22782
22783
  }, doc.schema.toStringOptions, options);
@@ -23294,12 +23295,19 @@ ${indent}${line}` : "\n";
23294
23295
  if (comment)
23295
23296
  reqNewline = true;
23296
23297
  let str = stringify2.stringify(item, itemCtx, () => comment = null);
23297
- if (i < items.length - 1)
23298
+ reqNewline || (reqNewline = lines.length > linesAtValue || str.includes("\n"));
23299
+ if (i < items.length - 1) {
23298
23300
  str += ",";
23301
+ } else if (ctx.options.trailingComma) {
23302
+ if (ctx.options.lineWidth > 0) {
23303
+ reqNewline || (reqNewline = lines.reduce((sum, line) => sum + line.length + 2, 2) + (str.length + 2) > ctx.options.lineWidth);
23304
+ }
23305
+ if (reqNewline) {
23306
+ str += ",";
23307
+ }
23308
+ }
23299
23309
  if (comment)
23300
23310
  str += stringifyComment.lineComment(str, itemIndent, commentString(comment));
23301
- if (!reqNewline && (lines.length > linesAtValue || str.includes("\n")))
23302
- reqNewline = true;
23303
23311
  lines.push(str);
23304
23312
  linesAtValue = lines.length;
23305
23313
  }
@@ -26313,17 +26321,22 @@ var require_compose_node = __commonJS({
26313
26321
  case "block-map":
26314
26322
  case "block-seq":
26315
26323
  case "flow-collection":
26316
- node = composeCollection.composeCollection(CN, ctx, token, props, onError);
26317
- if (anchor)
26318
- node.anchor = anchor.source.substring(1);
26324
+ try {
26325
+ node = composeCollection.composeCollection(CN, ctx, token, props, onError);
26326
+ if (anchor)
26327
+ node.anchor = anchor.source.substring(1);
26328
+ } catch (error) {
26329
+ const message = error instanceof Error ? error.message : String(error);
26330
+ onError(token, "RESOURCE_EXHAUSTION", message);
26331
+ }
26319
26332
  break;
26320
26333
  default: {
26321
26334
  const message = token.type === "error" ? token.message : `Unsupported token (type: ${token.type})`;
26322
26335
  onError(token, "UNEXPECTED_TOKEN", message);
26323
- node = composeEmptyNode(ctx, token.offset, void 0, null, props, onError);
26324
26336
  isSrcToken = false;
26325
26337
  }
26326
26338
  }
26339
+ node ?? (node = composeEmptyNode(ctx, token.offset, void 0, null, props, onError));
26327
26340
  if (anchor && node.anchor === "")
26328
26341
  onError(anchor, "BAD_ALIAS", "Anchor cannot be an empty string");
26329
26342
  if (atKey && ctx.options.stringKeys && (!identity.isScalar(node) || typeof node.value !== "string" || node.tag && node.tag !== "tag:yaml.org,2002:str")) {
@@ -28687,6 +28700,7 @@ var io2 = __toESM(require_io(), 1);
28687
28700
  var core = __toESM(require_core(), 1);
28688
28701
  var exec = __toESM(require_exec(), 1);
28689
28702
  var io = __toESM(require_io(), 1);
28703
+ var import_node_fs = require("node:fs");
28690
28704
  var import_yaml = __toESM(require_dist(), 1);
28691
28705
 
28692
28706
  // src/contracts.ts
@@ -28714,6 +28728,28 @@ var openAlphaActionContract = {
28714
28728
  description: "Existing contract collection ID.",
28715
28729
  required: false
28716
28730
  },
28731
+ "sync-examples": {
28732
+ description: "Whether linked spec/collection relations should enable example syncing.",
28733
+ required: false,
28734
+ default: "true",
28735
+ allowedValues: ["true", "false"]
28736
+ },
28737
+ "collection-sync-mode": {
28738
+ description: "Collection lifecycle policy: reuse existing collections, refresh them from the latest spec, or version them by release label.",
28739
+ required: false,
28740
+ default: "refresh",
28741
+ allowedValues: ["reuse", "refresh", "version"]
28742
+ },
28743
+ "spec-sync-mode": {
28744
+ description: "Spec lifecycle policy: update the canonical spec or create/reuse a versioned spec for the resolved release label.",
28745
+ required: false,
28746
+ default: "update",
28747
+ allowedValues: ["update", "version"]
28748
+ },
28749
+ "release-label": {
28750
+ description: "Optional release label. When omitted for versioned sync, the action derives one from GitHub ref metadata.",
28751
+ required: false
28752
+ },
28717
28753
  "project-name": {
28718
28754
  description: "Service project name.",
28719
28755
  required: true
@@ -28734,20 +28770,14 @@ var openAlphaActionContract = {
28734
28770
  description: "Comma-separated workspace admin user ids.",
28735
28771
  required: false
28736
28772
  },
28773
+ "workspace-team-id": {
28774
+ description: "Numeric sub-team ID for org-mode workspace creation.",
28775
+ required: false
28776
+ },
28737
28777
  "spec-url": {
28738
28778
  description: "HTTPS URL to the OpenAPI document.",
28739
28779
  required: true
28740
28780
  },
28741
- "environments-json": {
28742
- description: "JSON array of environment slugs to preserve in bootstrap outputs.",
28743
- required: false,
28744
- default: '["prod"]'
28745
- },
28746
- "system-env-map-json": {
28747
- description: "JSON map of environment slug to system environment id.",
28748
- required: false,
28749
- default: "{}"
28750
- },
28751
28781
  "governance-mapping-json": {
28752
28782
  description: "JSON map of business domain to governance group name.",
28753
28783
  required: false,
@@ -28761,19 +28791,6 @@ var openAlphaActionContract = {
28761
28791
  description: "Postman access token used for governance and workspace mutations.",
28762
28792
  required: false
28763
28793
  },
28764
- "github-token": {
28765
- description: "GitHub token for repository variable persistence.",
28766
- required: false
28767
- },
28768
- "gh-fallback-token": {
28769
- description: "Fallback token for repository variable APIs.",
28770
- required: false
28771
- },
28772
- "github-auth-mode": {
28773
- description: "GitHub auth mode for repository variable APIs.",
28774
- required: false,
28775
- default: "github_token_first"
28776
- },
28777
28794
  "integration-backend": {
28778
28795
  description: "Integration backend for downstream workspace connectivity.",
28779
28796
  required: false,
@@ -28819,8 +28836,8 @@ var openAlphaActionContract = {
28819
28836
  "OpenAPI operation summary normalization before upload (missing or oversized summaries)",
28820
28837
  "spec linting by UID",
28821
28838
  "baseline, smoke, and contract collection generation",
28839
+ "collection refresh and versioning policies",
28822
28840
  "collection tagging",
28823
- "GitHub repository variable persistence for downstream sync steps",
28824
28841
  "workspace, spec, and collection outputs"
28825
28842
  ],
28826
28843
  removedBehavior: [
@@ -28911,189 +28928,6 @@ function sanitizeHeaders(headers, secretValues) {
28911
28928
  return sanitized;
28912
28929
  }
28913
28930
 
28914
- // src/lib/github/github-api-client.ts
28915
- function buildErrorMessage(method, path2, response, body, masker) {
28916
- const status = `${response.status}${response.statusText ? ` ${response.statusText}` : ""}`;
28917
- const sanitizedBody = masker(body || "");
28918
- return sanitizedBody ? masker(`${method} ${path2} failed with ${status} - ${sanitizedBody}`) : masker(`${method} ${path2} failed with ${status} - [REDACTED]`);
28919
- }
28920
- var GitHubApiClient = class {
28921
- apiBase;
28922
- authMode;
28923
- appToken;
28924
- fallbackToken;
28925
- fetchImpl;
28926
- owner;
28927
- repo;
28928
- repository;
28929
- secretMasker;
28930
- token;
28931
- constructor(options) {
28932
- this.apiBase = String(options.apiBase || "https://api.github.com").replace(
28933
- /\/+$/,
28934
- ""
28935
- );
28936
- this.appToken = String(options.appToken || "").trim();
28937
- this.authMode = options.authMode || "github_token_first";
28938
- this.fallbackToken = String(options.fallbackToken || "").trim();
28939
- this.fetchImpl = options.fetch ?? fetch;
28940
- this.repository = options.repository;
28941
- const [owner, repo] = options.repository.split("/");
28942
- this.owner = owner;
28943
- this.repo = repo;
28944
- this.token = String(options.token || "").trim();
28945
- this.secretMasker = options.secretMasker ?? createSecretMasker([this.token, this.fallbackToken, this.appToken]);
28946
- }
28947
- getTokenOrder() {
28948
- const ordered = [];
28949
- if (this.authMode === "app_token") {
28950
- if (this.appToken) ordered.push(this.appToken);
28951
- if (this.token && this.token !== this.appToken) ordered.push(this.token);
28952
- if (this.fallbackToken && this.fallbackToken !== this.appToken && this.fallbackToken !== this.token) {
28953
- ordered.push(this.fallbackToken);
28954
- }
28955
- return ordered;
28956
- }
28957
- if (this.authMode === "fallback_pat_first") {
28958
- if (this.fallbackToken) ordered.push(this.fallbackToken);
28959
- if (this.token && this.token !== this.fallbackToken) ordered.push(this.token);
28960
- return ordered;
28961
- }
28962
- if (this.token) ordered.push(this.token);
28963
- if (this.fallbackToken && this.fallbackToken !== this.token) {
28964
- ordered.push(this.fallbackToken);
28965
- }
28966
- return ordered;
28967
- }
28968
- isVariablesEndpoint(path2) {
28969
- return path2.startsWith(`/repos/${this.owner}/${this.repo}/actions/variables`);
28970
- }
28971
- canUseFallback(path2) {
28972
- return this.isVariablesEndpoint(path2) || path2.includes(`/repos/${this.owner}/${this.repo}/contents`) || path2.includes("/dispatches");
28973
- }
28974
- rateLimitDelayMs(response, attempt) {
28975
- const retryAfter = Number(response.headers.get("retry-after") || "");
28976
- if (Number.isFinite(retryAfter) && retryAfter > 0) {
28977
- return Math.min(retryAfter * 1e3, 12e4);
28978
- }
28979
- const resetAtSeconds = Number(response.headers.get("x-ratelimit-reset") || "");
28980
- if (Number.isFinite(resetAtSeconds) && resetAtSeconds > 0) {
28981
- const delta = resetAtSeconds * 1e3 - Date.now();
28982
- if (delta > 0) {
28983
- return Math.min(delta + 250, 12e4);
28984
- }
28985
- }
28986
- const base = Math.min(5e3 * Math.pow(2, attempt), 12e4);
28987
- const jitter = Math.floor(Math.random() * 250);
28988
- return Math.min(base + jitter, 12e4);
28989
- }
28990
- async requestWithToken(path2, init, token) {
28991
- const MAX_RETRIES = 5;
28992
- const normalizedToken = String(token || "").trim();
28993
- if (!normalizedToken) {
28994
- throw new Error(`Missing GitHub auth token for request ${path2}`);
28995
- }
28996
- for (let attempt = 0; ; attempt++) {
28997
- const response = await this.fetchImpl(`${this.apiBase}${path2}`, {
28998
- ...init,
28999
- headers: {
29000
- Accept: "application/vnd.github+json",
29001
- Authorization: `Bearer ${normalizedToken}`,
29002
- "Content-Type": "application/json",
29003
- ...init.headers || {}
29004
- }
29005
- });
29006
- if (attempt < MAX_RETRIES && (response.status === 403 || response.status === 429)) {
29007
- const body = await response.clone().text().catch(() => "");
29008
- if (isRateLimitedResponse(response, body)) {
29009
- const delay = this.rateLimitDelayMs(response, attempt);
29010
- console.log(
29011
- `GitHub API rate limited, retrying in ${Math.ceil(delay / 1e3)}s (attempt ${attempt + 1}/${MAX_RETRIES})...`
29012
- );
29013
- await new Promise((r) => setTimeout(r, delay));
29014
- continue;
29015
- }
29016
- }
29017
- return response;
29018
- }
29019
- }
29020
- async request(path2, init = {}) {
29021
- const orderedTokens = this.getTokenOrder();
29022
- if (orderedTokens.length === 0) {
29023
- throw new Error("No GitHub auth token configured");
29024
- }
29025
- const first = await this.requestWithToken(path2, init, orderedTokens[0]);
29026
- if (orderedTokens.length < 2 || !this.canUseFallback(path2)) {
29027
- return first;
29028
- }
29029
- const isVariableGet404 = first.status === 404 && (!init.method || init.method === "GET") && this.isVariablesEndpoint(path2);
29030
- if (first.status !== 403 && !isVariableGet404) {
29031
- return first;
29032
- }
29033
- return this.requestWithToken(path2, init, orderedTokens[1]);
29034
- }
29035
- async setRepositoryVariable(name, value) {
29036
- if (!value) {
29037
- throw new Error(`Repo variable ${name} is empty`);
29038
- }
29039
- const path2 = `/repos/${this.repository}/actions/variables`;
29040
- const body = JSON.stringify({ name, value: String(value) });
29041
- const createResponse = await this.request(path2, {
29042
- method: "POST",
29043
- body
29044
- });
29045
- if (createResponse.ok || createResponse.status === 201) {
29046
- return;
29047
- }
29048
- if (createResponse.status === 409 || createResponse.status === 422) {
29049
- const updatePath = `/repos/${this.repository}/actions/variables/${name}`;
29050
- const updateResponse = await this.request(updatePath, {
29051
- method: "PATCH",
29052
- body
29053
- });
29054
- if (updateResponse.ok) {
29055
- return;
29056
- }
29057
- const text2 = await updateResponse.text().catch(() => "");
29058
- throw new Error(
29059
- buildErrorMessage("PATCH", updatePath, updateResponse, text2, this.secretMasker)
29060
- );
29061
- }
29062
- const text = await createResponse.text().catch(() => "");
29063
- throw new Error(
29064
- buildErrorMessage("POST", path2, createResponse, text, this.secretMasker)
29065
- );
29066
- }
29067
- async getRepositoryVariable(name) {
29068
- const path2 = `/repos/${this.repository}/actions/variables/${name}`;
29069
- const response = await this.request(path2, {
29070
- method: "GET"
29071
- });
29072
- if (response.status === 404) {
29073
- return "";
29074
- }
29075
- if (!response.ok) {
29076
- const text = await response.text().catch(() => "");
29077
- throw new Error(
29078
- buildErrorMessage("GET", path2, response, text, this.secretMasker)
29079
- );
29080
- }
29081
- const data = await response.json();
29082
- return String(data.value || "");
29083
- }
29084
- };
29085
- function isRateLimitedResponse(response, body) {
29086
- if (response.status !== 403 && response.status !== 429) return false;
29087
- const remaining = response.headers.get("x-ratelimit-remaining");
29088
- const retryAfter = response.headers.get("retry-after");
29089
- if (remaining === "0") return true;
29090
- if (retryAfter) return true;
29091
- const message = body.toLowerCase();
29092
- if (message.includes("secondary rate limit")) return true;
29093
- if (message.includes("api rate limit exceeded")) return true;
29094
- return response.status === 429;
29095
- }
29096
-
29097
28931
  // src/lib/http-error.ts
29098
28932
  function truncate(value, limit) {
29099
28933
  if (value.length <= limit) {
@@ -29276,6 +29110,16 @@ var PostmanAssetsClient = class {
29276
29110
  }
29277
29111
  return void 0;
29278
29112
  }
29113
+ async getTeams() {
29114
+ const data = await this.request("/teams");
29115
+ const teams = data?.data ?? [];
29116
+ return Array.isArray(teams) ? teams.filter((t) => t?.id && t?.name).map((t) => ({
29117
+ id: Number(t.id),
29118
+ name: String(t.name),
29119
+ handle: String(t.handle || ""),
29120
+ ...t.organizationId != null ? { organizationId: Number(t.organizationId) } : {}
29121
+ })) : [];
29122
+ }
29279
29123
  async request(path2, init = {}) {
29280
29124
  const url = path2.startsWith("http") ? path2 : `${this.baseUrl}${path2}`;
29281
29125
  const response = await this.fetchImpl(url, {
@@ -29304,13 +29148,14 @@ var PostmanAssetsClient = class {
29304
29148
  return null;
29305
29149
  }
29306
29150
  }
29307
- async createWorkspace(name, about) {
29151
+ async createWorkspace(name, about, targetTeamId) {
29308
29152
  return retry(async () => {
29309
29153
  const payload = {
29310
29154
  workspace: {
29311
29155
  about,
29312
29156
  name,
29313
- type: "team"
29157
+ type: "team",
29158
+ ...targetTeamId != null && !Number.isNaN(targetTeamId) ? { teamId: targetTeamId } : {}
29314
29159
  }
29315
29160
  };
29316
29161
  let created;
@@ -29322,7 +29167,7 @@ var PostmanAssetsClient = class {
29322
29167
  } catch (err) {
29323
29168
  if (err instanceof Error && err.message.includes("Only personal workspaces")) {
29324
29169
  throw new Error(
29325
- "Workspace creation failed: Your team may have Org Mode enabled. Org-level workspaces require a different API request schema which is currently unsupported in this alpha version."
29170
+ "Workspace creation failed: This may be an Org-mode account that requires a workspace-team-id input. The Postman API does not allow creating team workspaces at the organization level. Use the workspace-team-id input to specify which sub-team should own this workspace."
29326
29171
  );
29327
29172
  }
29328
29173
  throw err;
@@ -29341,7 +29186,11 @@ var PostmanAssetsClient = class {
29341
29186
  return {
29342
29187
  id: workspaceId
29343
29188
  };
29344
- }, { maxAttempts: 3, delayMs: 2e3 });
29189
+ }, {
29190
+ maxAttempts: 3,
29191
+ delayMs: 2e3,
29192
+ shouldRetry: (err) => !(err instanceof Error && err.message.includes("workspace-team-id"))
29193
+ });
29345
29194
  }
29346
29195
  async listWorkspaces() {
29347
29196
  const data = await this.request("/workspaces");
@@ -29816,6 +29665,26 @@ var BifrostInternalIntegrationAdapter = class {
29816
29665
  options.workerBaseUrl || "https://catalog-admin.postman-account2009.workers.dev"
29817
29666
  ).replace(/\/+$/, "");
29818
29667
  }
29668
+ async proxyRequest(service, method, requestPath, body) {
29669
+ const url = "https://bifrost-premium-https-v4.gw.postman.com/ws/proxy";
29670
+ const headers = {
29671
+ "Content-Type": "application/json",
29672
+ "x-access-token": this.accessToken
29673
+ };
29674
+ if (this.teamId) {
29675
+ headers["x-entity-team-id"] = this.teamId;
29676
+ }
29677
+ return this.fetchImpl(url, {
29678
+ method: "POST",
29679
+ headers,
29680
+ body: JSON.stringify({
29681
+ service,
29682
+ method,
29683
+ path: requestPath,
29684
+ ...body !== void 0 ? { body } : {}
29685
+ })
29686
+ });
29687
+ }
29819
29688
  async assignWorkspaceToGovernanceGroup(workspaceId, domain, mappingJson) {
29820
29689
  let mapping;
29821
29690
  try {
@@ -29909,14 +29778,6 @@ var BifrostInternalIntegrationAdapter = class {
29909
29778
  }
29910
29779
  }
29911
29780
  async connectWorkspaceToRepository(workspaceId, repoUrl) {
29912
- const url = "https://bifrost-premium-https-v4.gw.postman.com/ws/proxy";
29913
- const headers = {
29914
- "Content-Type": "application/json",
29915
- "x-access-token": this.accessToken
29916
- };
29917
- if (this.teamId) {
29918
- headers["x-entity-team-id"] = this.teamId;
29919
- }
29920
29781
  const payload = {
29921
29782
  service: "workspaces",
29922
29783
  method: "POST",
@@ -29927,11 +29788,12 @@ var BifrostInternalIntegrationAdapter = class {
29927
29788
  versionControl: true
29928
29789
  }
29929
29790
  };
29930
- const response = await this.fetchImpl(url, {
29931
- method: "POST",
29932
- headers,
29933
- body: JSON.stringify(payload)
29934
- });
29791
+ const response = await this.proxyRequest(
29792
+ payload.service,
29793
+ payload.method,
29794
+ payload.path,
29795
+ payload.body
29796
+ );
29935
29797
  if (response.ok) return;
29936
29798
  if (response.status === 400) {
29937
29799
  const body = await response.text();
@@ -29948,29 +29810,68 @@ var BifrostInternalIntegrationAdapter = class {
29948
29810
  }
29949
29811
  throw await HttpError.fromResponse(response, {
29950
29812
  method: "POST",
29951
- requestHeaders: headers,
29813
+ requestHeaders: {
29814
+ "Content-Type": "application/json",
29815
+ "x-access-token": this.accessToken,
29816
+ ...this.teamId ? { "x-entity-team-id": this.teamId } : {}
29817
+ },
29952
29818
  secretValues: [this.accessToken],
29953
- url
29819
+ url: "https://bifrost-premium-https-v4.gw.postman.com/ws/proxy"
29954
29820
  });
29955
29821
  }
29956
- async getWorkspaceGitRepoUrl(workspaceId) {
29957
- const url = "https://bifrost-premium-https-v4.gw.postman.com/ws/proxy";
29958
- const headers = {
29959
- "Content-Type": "application/json",
29960
- "x-access-token": this.accessToken
29961
- };
29962
- if (this.teamId) {
29963
- headers["x-entity-team-id"] = this.teamId;
29822
+ async linkCollectionsToSpecification(specificationId, collections) {
29823
+ if (collections.length === 0) {
29824
+ return;
29964
29825
  }
29965
- const response = await this.fetchImpl(url, {
29826
+ const response = await this.proxyRequest(
29827
+ "specification",
29828
+ "put",
29829
+ `/specifications/${specificationId}/collections`,
29830
+ collections.map((collection) => ({
29831
+ collectionId: collection.collectionId,
29832
+ ...collection.syncOptions ? { syncOptions: collection.syncOptions } : {}
29833
+ }))
29834
+ );
29835
+ if (response.ok) {
29836
+ return;
29837
+ }
29838
+ throw await HttpError.fromResponse(response, {
29966
29839
  method: "POST",
29967
- headers,
29968
- body: JSON.stringify({
29969
- service: "workspaces",
29970
- method: "GET",
29971
- path: `/workspaces/${workspaceId}/filesystem`
29972
- })
29840
+ requestHeaders: {
29841
+ "Content-Type": "application/json",
29842
+ "x-access-token": this.accessToken,
29843
+ ...this.teamId ? { "x-entity-team-id": this.teamId } : {}
29844
+ },
29845
+ secretValues: [this.accessToken],
29846
+ url: "https://bifrost-premium-https-v4.gw.postman.com/ws/proxy"
29847
+ });
29848
+ }
29849
+ async syncCollection(specificationId, collectionId) {
29850
+ const response = await this.proxyRequest(
29851
+ "specification",
29852
+ "post",
29853
+ `/specifications/${specificationId}/collections/${collectionId}/sync`
29854
+ );
29855
+ if (response.ok) {
29856
+ return;
29857
+ }
29858
+ throw await HttpError.fromResponse(response, {
29859
+ method: "POST",
29860
+ requestHeaders: {
29861
+ "Content-Type": "application/json",
29862
+ "x-access-token": this.accessToken,
29863
+ ...this.teamId ? { "x-entity-team-id": this.teamId } : {}
29864
+ },
29865
+ secretValues: [this.accessToken],
29866
+ url: "https://bifrost-premium-https-v4.gw.postman.com/ws/proxy"
29973
29867
  });
29868
+ }
29869
+ async getWorkspaceGitRepoUrl(workspaceId) {
29870
+ const response = await this.proxyRequest(
29871
+ "workspaces",
29872
+ "GET",
29873
+ `/workspaces/${workspaceId}/filesystem`
29874
+ );
29974
29875
  if (response.status === 404) return null;
29975
29876
  if (!response.ok) return null;
29976
29877
  const body = await response.text();
@@ -30153,31 +30054,24 @@ function requireInput(actionCore, name) {
30153
30054
  function optionalInput(actionCore, name) {
30154
30055
  return normalizeInputValue(actionCore.getInput(name));
30155
30056
  }
30156
- function parseJsonValue(raw, fallback, inputName) {
30157
- try {
30158
- return JSON.parse(raw || JSON.stringify(fallback)) ?? fallback;
30159
- } catch (error) {
30160
- throw new Error(
30161
- `Invalid JSON for ${inputName}: ${error instanceof Error ? error.message : String(error)}`
30162
- );
30163
- }
30057
+ function parseBooleanInput(value, defaultValue) {
30058
+ const normalized = String(value || "").trim().toLowerCase();
30059
+ if (!normalized) return defaultValue;
30060
+ if (["true", "1", "yes", "on"].includes(normalized)) return true;
30061
+ if (["false", "0", "no", "off"].includes(normalized)) return false;
30062
+ return defaultValue;
30164
30063
  }
30165
- function asStringArray(value, inputName) {
30166
- if (!Array.isArray(value)) {
30167
- throw new Error(`${inputName} must be a JSON array`);
30064
+ function parseCollectionSyncMode(value) {
30065
+ if (value === "reuse" || value === "version") {
30066
+ return value;
30168
30067
  }
30169
- return value.map((entry) => String(entry));
30068
+ return "refresh";
30170
30069
  }
30171
- function asStringMap(value, inputName) {
30172
- if (!value || typeof value !== "object" || Array.isArray(value)) {
30173
- throw new Error(`${inputName} must be a JSON object`);
30174
- }
30175
- return Object.fromEntries(
30176
- Object.entries(value).map(([key, entry]) => [
30177
- key,
30178
- String(entry)
30179
- ])
30180
- );
30070
+ function parseSpecSyncMode(value) {
30071
+ if (value === "version") {
30072
+ return value;
30073
+ }
30074
+ return "update";
30181
30075
  }
30182
30076
  function resolveInputs(env = process.env) {
30183
30077
  const repoContext = detectRepoContext(
@@ -30205,28 +30099,32 @@ function resolveInputs(env = process.env) {
30205
30099
  }
30206
30100
  }
30207
30101
  return {
30208
- projectName: getInput("project-name", env) ?? "",
30102
+ projectName: getInput("project-name", env) ?? env.GITHUB_REPOSITORY?.split("/").pop() ?? env.CI_PROJECT_NAME ?? "",
30209
30103
  workspaceId: getInput("workspace-id", env),
30210
30104
  specId: getInput("spec-id", env),
30211
30105
  baselineCollectionId: getInput("baseline-collection-id", env),
30212
30106
  smokeCollectionId: getInput("smoke-collection-id", env),
30213
30107
  contractCollectionId: getInput("contract-collection-id", env),
30108
+ syncExamples: parseBooleanInput(getInput("sync-examples", env), true),
30109
+ collectionSyncMode: parseCollectionSyncMode(getInput("collection-sync-mode", env)),
30110
+ specSyncMode: parseSpecSyncMode(getInput("spec-sync-mode", env)),
30111
+ releaseLabel: getInput("release-label", env),
30214
30112
  domain: getInput("domain", env),
30215
30113
  domainCode: getInput("domain-code", env),
30216
30114
  requesterEmail: getInput("requester-email", env),
30217
30115
  workspaceAdminUserIds: getInput("workspace-admin-user-ids", env) || env.WORKSPACE_ADMIN_USER_IDS || "",
30116
+ workspaceTeamId: getInput("workspace-team-id", env) || env.POSTMAN_WORKSPACE_TEAM_ID,
30218
30117
  teamId: getInput("team-id", env) || env.POSTMAN_TEAM_ID || "",
30219
30118
  repoUrl: repoContext.repoUrl || "",
30220
30119
  specUrl,
30221
- environmentsJson: getInput("environments-json", env) ?? openAlphaActionContract.inputs["environments-json"].default ?? '["prod"]',
30222
- systemEnvMapJson: getInput("system-env-map-json", env) ?? openAlphaActionContract.inputs["system-env-map-json"].default ?? "{}",
30223
30120
  governanceMappingJson: getInput("governance-mapping-json", env) ?? openAlphaActionContract.inputs["governance-mapping-json"].default ?? "{}",
30224
30121
  postmanApiKey: getInput("postman-api-key", env) ?? "",
30225
30122
  postmanAccessToken: getInput("postman-access-token", env),
30226
- githubToken: getInput("github-token", env),
30227
- ghFallbackToken: getInput("gh-fallback-token", env),
30228
- githubAuthMode: getInput("github-auth-mode", env) ?? openAlphaActionContract.inputs["github-auth-mode"].default ?? "github_token_first",
30229
- integrationBackend
30123
+ integrationBackend,
30124
+ githubRefName: env.GITHUB_REF_NAME,
30125
+ githubHeadRef: env.GITHUB_HEAD_REF,
30126
+ githubRef: env.GITHUB_REF,
30127
+ githubSha: env.GITHUB_SHA
30230
30128
  };
30231
30129
  }
30232
30130
  function createPlannedOutputs(inputs) {
@@ -30257,12 +30155,8 @@ function readActionInputs(actionCore) {
30257
30155
  const specUrl = requireInput(actionCore, "spec-url");
30258
30156
  const postmanApiKey = requireInput(actionCore, "postman-api-key");
30259
30157
  const postmanAccessToken = optionalInput(actionCore, "postman-access-token");
30260
- const githubToken = optionalInput(actionCore, "github-token");
30261
- const ghFallbackToken = optionalInput(actionCore, "gh-fallback-token");
30262
30158
  actionCore.setSecret(postmanApiKey);
30263
30159
  if (postmanAccessToken) actionCore.setSecret(postmanAccessToken);
30264
- if (githubToken) actionCore.setSecret(githubToken);
30265
- if (ghFallbackToken) actionCore.setSecret(ghFallbackToken);
30266
30160
  const inputs = resolveInputs({
30267
30161
  ...process.env,
30268
30162
  INPUT_PROJECT_NAME: projectName,
@@ -30271,6 +30165,10 @@ function readActionInputs(actionCore) {
30271
30165
  INPUT_BASELINE_COLLECTION_ID: optionalInput(actionCore, "baseline-collection-id"),
30272
30166
  INPUT_SMOKE_COLLECTION_ID: optionalInput(actionCore, "smoke-collection-id"),
30273
30167
  INPUT_CONTRACT_COLLECTION_ID: optionalInput(actionCore, "contract-collection-id"),
30168
+ INPUT_SYNC_EXAMPLES: optionalInput(actionCore, "sync-examples") ?? openAlphaActionContract.inputs["sync-examples"].default,
30169
+ INPUT_COLLECTION_SYNC_MODE: optionalInput(actionCore, "collection-sync-mode") ?? openAlphaActionContract.inputs["collection-sync-mode"].default,
30170
+ INPUT_SPEC_SYNC_MODE: optionalInput(actionCore, "spec-sync-mode") ?? openAlphaActionContract.inputs["spec-sync-mode"].default,
30171
+ INPUT_RELEASE_LABEL: optionalInput(actionCore, "release-label"),
30274
30172
  INPUT_DOMAIN: optionalInput(actionCore, "domain"),
30275
30173
  INPUT_DOMAIN_CODE: optionalInput(actionCore, "domain-code"),
30276
30174
  INPUT_REQUESTER_EMAIL: optionalInput(actionCore, "requester-email"),
@@ -30278,17 +30176,13 @@ function readActionInputs(actionCore) {
30278
30176
  actionCore,
30279
30177
  "workspace-admin-user-ids"
30280
30178
  ),
30179
+ INPUT_WORKSPACE_TEAM_ID: optionalInput(actionCore, "workspace-team-id") || process.env.POSTMAN_WORKSPACE_TEAM_ID,
30281
30180
  INPUT_TEAM_ID: optionalInput(actionCore, "postman-team-id") || process.env.POSTMAN_TEAM_ID,
30282
30181
  INPUT_REPO_URL: optionalInput(actionCore, "repo-url"),
30283
30182
  INPUT_SPEC_URL: specUrl,
30284
- INPUT_ENVIRONMENTS_JSON: optionalInput(actionCore, "environments-json") ?? openAlphaActionContract.inputs["environments-json"].default,
30285
- INPUT_SYSTEM_ENV_MAP_JSON: optionalInput(actionCore, "system-env-map-json") ?? openAlphaActionContract.inputs["system-env-map-json"].default,
30286
30183
  INPUT_GOVERNANCE_MAPPING_JSON: optionalInput(actionCore, "governance-mapping-json") ?? openAlphaActionContract.inputs["governance-mapping-json"].default,
30287
30184
  INPUT_POSTMAN_API_KEY: postmanApiKey,
30288
30185
  INPUT_POSTMAN_ACCESS_TOKEN: postmanAccessToken,
30289
- INPUT_GITHUB_TOKEN: githubToken,
30290
- INPUT_GH_FALLBACK_TOKEN: ghFallbackToken,
30291
- INPUT_GITHUB_AUTH_MODE: optionalInput(actionCore, "github-auth-mode") ?? openAlphaActionContract.inputs["github-auth-mode"].default,
30292
30186
  INPUT_INTEGRATION_BACKEND: optionalInput(actionCore, "integration-backend") ?? openAlphaActionContract.inputs["integration-backend"].default
30293
30187
  });
30294
30188
  return inputs;
@@ -30365,6 +30259,45 @@ async function fetchSpecDocument(specUrl, specFetcher) {
30365
30259
  }
30366
30260
  );
30367
30261
  }
30262
+ function normalizeReleaseLabel(value) {
30263
+ const trimmed = String(value || "").trim();
30264
+ if (!trimmed) {
30265
+ return void 0;
30266
+ }
30267
+ return trimmed.replace(/^refs\/heads\//, "").replace(/^refs\/tags\//, "").replace(/^refs\/pull\//, "pull-").replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || void 0;
30268
+ }
30269
+ function deriveReleaseLabel(inputs) {
30270
+ if (inputs.releaseLabel) {
30271
+ return normalizeReleaseLabel(inputs.releaseLabel);
30272
+ }
30273
+ return normalizeReleaseLabel(inputs.githubRefName) ?? normalizeReleaseLabel(inputs.githubHeadRef) ?? normalizeReleaseLabel(inputs.githubRef);
30274
+ }
30275
+ function createAssetProjectName(inputs, releaseLabel) {
30276
+ if (!releaseLabel) {
30277
+ return inputs.projectName;
30278
+ }
30279
+ return `${inputs.projectName} ${releaseLabel}`;
30280
+ }
30281
+ function readResourcesState() {
30282
+ try {
30283
+ return (0, import_yaml.parse)((0, import_node_fs.readFileSync)(".postman/resources.yaml", "utf8"));
30284
+ } catch {
30285
+ return null;
30286
+ }
30287
+ }
30288
+ function getFirstCloudResourceId(map) {
30289
+ if (!map) {
30290
+ return void 0;
30291
+ }
30292
+ return Object.values(map)[0];
30293
+ }
30294
+ function findCloudResourceId(map, matcher) {
30295
+ if (!map) {
30296
+ return void 0;
30297
+ }
30298
+ const match = Object.entries(map).find(([filePath]) => matcher(filePath));
30299
+ return match?.[1];
30300
+ }
30368
30301
  var SPEC_SUMMARY_MAX_LEN = 200;
30369
30302
  var SPEC_HTTP_METHODS = /* @__PURE__ */ new Set([
30370
30303
  "get",
@@ -30453,122 +30386,28 @@ function validateSpecStructure(content) {
30453
30386
  throw new Error('Spec is missing "openapi" or "swagger" version field');
30454
30387
  }
30455
30388
  }
30456
- function varName(projectName, baseName) {
30457
- const slug = projectName.toUpperCase().replace(/[^A-Z0-9]/g, "_");
30458
- return `POSTMAN_${slug}_${baseName}`;
30459
- }
30460
- async function readVariable(github, projectName, baseName) {
30461
- if (!github) {
30462
- return void 0;
30463
- }
30464
- const namespaced = await github.getRepositoryVariable(varName(projectName, baseName)).catch(() => void 0);
30465
- if (namespaced) {
30466
- return namespaced;
30467
- }
30468
- const legacy = await github.getRepositoryVariable(`POSTMAN_${baseName}`).catch(() => void 0);
30469
- return legacy || void 0;
30470
- }
30471
- async function persistVariable(github, name, value, actionCore) {
30472
- if (!github || !value) {
30473
- return;
30474
- }
30475
- try {
30476
- await github.setRepositoryVariable(name, value);
30477
- } catch (err) {
30478
- actionCore.warning(
30479
- `Failed to persist ${name}: ${err instanceof Error ? err.message : String(err)}`
30480
- );
30481
- }
30482
- }
30483
- async function writeVariable(github, projectName, baseName, value, actionCore) {
30484
- if (!github || !value) {
30485
- return;
30486
- }
30487
- await persistVariable(github, varName(projectName, baseName), value, actionCore);
30488
- await persistVariable(github, `POSTMAN_${baseName}`, value, actionCore);
30489
- }
30490
- async function persistBootstrapRepositoryVariables(github, projectName, outputs, systemEnvMap, environments, lintSummary, actionCore) {
30491
- await persistVariable(
30492
- github,
30493
- "LINT_WARNINGS",
30494
- String(lintSummary.lintWarnings),
30495
- actionCore
30496
- );
30497
- await persistVariable(
30498
- github,
30499
- "LINT_ERRORS",
30500
- String(lintSummary.lintErrors),
30501
- actionCore
30502
- );
30503
- await writeVariable(
30504
- github,
30505
- projectName,
30506
- "WORKSPACE_ID",
30507
- outputs["workspace-id"],
30508
- actionCore
30509
- );
30510
- await writeVariable(github, projectName, "SPEC_UID", outputs["spec-id"], actionCore);
30511
- await writeVariable(
30512
- github,
30513
- projectName,
30514
- "BASELINE_COLLECTION_UID",
30515
- outputs["baseline-collection-id"],
30516
- actionCore
30517
- );
30518
- await writeVariable(
30519
- github,
30520
- projectName,
30521
- "SMOKE_COLLECTION_UID",
30522
- outputs["smoke-collection-id"],
30523
- actionCore
30524
- );
30525
- await writeVariable(
30526
- github,
30527
- projectName,
30528
- "CONTRACT_COLLECTION_UID",
30529
- outputs["contract-collection-id"],
30530
- actionCore
30531
- );
30532
- for (const envName of environments) {
30533
- const systemEnvId = systemEnvMap[envName];
30534
- if (!systemEnvId) {
30535
- continue;
30536
- }
30537
- await writeVariable(
30538
- github,
30539
- projectName,
30540
- `SYSTEM_ENV_${envName.toUpperCase()}`,
30541
- systemEnvId,
30542
- actionCore
30543
- );
30544
- }
30545
- }
30546
30389
  async function runBootstrap(inputs, dependencies) {
30547
30390
  const outputs = createPlannedOutputs(inputs);
30548
- const environments = asStringArray(
30549
- parseJsonValue(inputs.environmentsJson, ["prod"], "environments-json"),
30550
- "environments-json"
30551
- );
30552
- const systemEnvMap = asStringMap(
30553
- parseJsonValue(inputs.systemEnvMapJson, {}, "system-env-map-json"),
30554
- "system-env-map-json"
30555
- );
30391
+ const requiresReleaseLabel = inputs.collectionSyncMode === "version" || inputs.specSyncMode === "version";
30392
+ const releaseLabel = requiresReleaseLabel ? deriveReleaseLabel(inputs) : void 0;
30393
+ if (requiresReleaseLabel && !releaseLabel) {
30394
+ throw new Error(
30395
+ "Versioned spec or collection sync requires a release-label or derivable GitHub ref metadata"
30396
+ );
30397
+ }
30556
30398
  const workspaceName = createWorkspaceName(inputs);
30557
30399
  const aboutText = `Auto-provisioned by Postman CS open-alpha for ${inputs.projectName}`;
30558
30400
  await runGroup(dependencies.core, "Install Postman CLI", async () => {
30559
30401
  await ensurePostmanCli(dependencies, inputs.postmanApiKey);
30560
30402
  });
30561
- const explicitWorkspaceId = inputs.workspaceId;
30562
- let repoWorkspaceId;
30563
- let workspaceId = explicitWorkspaceId;
30564
- if (!workspaceId && dependencies.github) {
30565
- repoWorkspaceId = await readVariable(
30566
- dependencies.github,
30567
- inputs.projectName,
30568
- "WORKSPACE_ID"
30569
- );
30570
- workspaceId = repoWorkspaceId;
30403
+ const resourcesState = readResourcesState();
30404
+ let explicitWorkspaceId = inputs.workspaceId;
30405
+ if (!explicitWorkspaceId && resourcesState?.workspace?.id) {
30406
+ explicitWorkspaceId = resourcesState.workspace.id;
30407
+ dependencies.core.info("Resolved workspace-id from .postman/resources.yaml");
30571
30408
  }
30409
+ const repoWorkspaceId = explicitWorkspaceId;
30410
+ let workspaceId = explicitWorkspaceId;
30572
30411
  let teamId = inputs.teamId || "";
30573
30412
  if (!teamId) {
30574
30413
  teamId = await dependencies.postman.getAutoDerivedTeamId() || "";
@@ -30602,24 +30441,65 @@ async function runBootstrap(inputs, dependencies) {
30602
30441
  } else if (workspaceId) {
30603
30442
  dependencies.core.info(`Using existing workspace: ${workspaceId}`);
30604
30443
  }
30444
+ let workspaceTeamId;
30445
+ if (inputs.workspaceTeamId) {
30446
+ workspaceTeamId = parseInt(inputs.workspaceTeamId, 10);
30447
+ if (Number.isNaN(workspaceTeamId)) {
30448
+ throw new Error(`workspace-team-id must be a numeric sub-team ID, got: ${inputs.workspaceTeamId}`);
30449
+ }
30450
+ }
30451
+ if (!workspaceId && !workspaceTeamId) {
30452
+ try {
30453
+ const teams = await dependencies.postman.getTeams();
30454
+ if (teams.length > 1 && teams.every((t) => t.organizationId == null)) {
30455
+ dependencies.core.warning(
30456
+ "GET /teams returned multiple teams but none include organizationId. Org-mode detection may be degraded due to an upstream API change. If workspace creation fails, set workspace-team-id explicitly."
30457
+ );
30458
+ }
30459
+ const orgIds = new Set(teams.filter((t) => t.organizationId != null).map((t) => t.organizationId));
30460
+ const meTeamId = parseInt(teamId, 10);
30461
+ const isOrgMode = teams.length > 1 && orgIds.size === 1 && orgIds.has(meTeamId);
30462
+ if (isOrgMode) {
30463
+ const teamList = teams.map((t) => ` ${t.id} ${t.name}`).join("\n");
30464
+ throw new Error(
30465
+ `Org-mode account detected. Workspace creation requires a specific sub-team ID.
30466
+
30467
+ Available sub-teams:
30468
+ ${teamList}
30469
+
30470
+ To fix this, set the workspace-team-id input in your workflow:
30471
+ workspace-team-id: '<id>'
30472
+
30473
+ Or for reuse across runs, create a repository variable and reference it:
30474
+ workspace-team-id: \${{ vars.POSTMAN_WORKSPACE_TEAM_ID }}
30475
+
30476
+ For CLI usage, pass --workspace-team-id <id> or export POSTMAN_WORKSPACE_TEAM_ID=<id>.`
30477
+ );
30478
+ } else if (teams.length > 1) {
30479
+ dependencies.core.warning(
30480
+ `API key has access to ${teams.length} teams but org-mode could not be confirmed. Proceeding without teamId. If workspace creation fails, set workspace-team-id explicitly.`
30481
+ );
30482
+ }
30483
+ } catch (err) {
30484
+ if (err instanceof Error && err.message.includes("Org-mode account detected")) {
30485
+ throw err;
30486
+ }
30487
+ dependencies.core.warning(
30488
+ `Could not check for org-mode sub-teams: ${err instanceof Error ? err.message : String(err)}`
30489
+ );
30490
+ }
30491
+ }
30605
30492
  if (!workspaceId) {
30606
30493
  const workspace = await runGroup(
30607
30494
  dependencies.core,
30608
30495
  "Create Postman Workspace",
30609
- async () => dependencies.postman.createWorkspace(workspaceName, aboutText)
30496
+ async () => dependencies.postman.createWorkspace(workspaceName, aboutText, workspaceTeamId)
30610
30497
  );
30611
30498
  workspaceId = workspace.id;
30612
30499
  }
30613
30500
  outputs["workspace-id"] = workspaceId || "";
30614
30501
  outputs["workspace-url"] = `https://go.postman.co/workspace/${workspaceId}`;
30615
30502
  outputs["workspace-name"] = workspaceName;
30616
- await writeVariable(
30617
- dependencies.github,
30618
- inputs.projectName,
30619
- "WORKSPACE_ID",
30620
- outputs["workspace-id"],
30621
- dependencies.core
30622
- );
30623
30503
  if (inputs.domain && dependencies.internalIntegration) {
30624
30504
  await runGroup(
30625
30505
  dependencies.core,
@@ -30674,33 +30554,43 @@ async function runBootstrap(inputs, dependencies) {
30674
30554
  );
30675
30555
  }
30676
30556
  let specId = inputs.specId;
30677
- if (!specId && dependencies.github) {
30678
- specId = await readVariable(dependencies.github, inputs.projectName, "SPEC_UID");
30557
+ if (!specId) {
30558
+ specId = getFirstCloudResourceId(resourcesState?.cloudResources?.specs);
30559
+ if (specId) {
30560
+ dependencies.core.info("Resolved spec-id from .postman/resources.yaml");
30561
+ }
30679
30562
  }
30680
- let baselineCollectionId = inputs.baselineCollectionId;
30681
- let smokeCollectionId = inputs.smokeCollectionId;
30682
- let contractCollectionId = inputs.contractCollectionId;
30683
- if (dependencies.github) {
30563
+ let baselineCollectionId = inputs.collectionSyncMode === "refresh" ? void 0 : inputs.baselineCollectionId;
30564
+ let smokeCollectionId = inputs.collectionSyncMode === "refresh" ? void 0 : inputs.smokeCollectionId;
30565
+ let contractCollectionId = inputs.collectionSyncMode === "refresh" ? void 0 : inputs.contractCollectionId;
30566
+ if (inputs.collectionSyncMode !== "refresh") {
30567
+ const cloudCollections = resourcesState?.cloudResources?.collections;
30684
30568
  if (!baselineCollectionId) {
30685
- baselineCollectionId = await readVariable(
30686
- dependencies.github,
30687
- inputs.projectName,
30688
- "BASELINE_COLLECTION_UID"
30569
+ baselineCollectionId = findCloudResourceId(
30570
+ cloudCollections,
30571
+ (filePath) => filePath.includes("[Baseline]")
30689
30572
  );
30573
+ if (baselineCollectionId) {
30574
+ dependencies.core.info("Resolved baseline-collection-id from .postman/resources.yaml");
30575
+ }
30690
30576
  }
30691
30577
  if (!smokeCollectionId) {
30692
- smokeCollectionId = await readVariable(
30693
- dependencies.github,
30694
- inputs.projectName,
30695
- "SMOKE_COLLECTION_UID"
30578
+ smokeCollectionId = findCloudResourceId(
30579
+ cloudCollections,
30580
+ (filePath) => filePath.includes("[Smoke]")
30696
30581
  );
30582
+ if (smokeCollectionId) {
30583
+ dependencies.core.info("Resolved smoke-collection-id from .postman/resources.yaml");
30584
+ }
30697
30585
  }
30698
30586
  if (!contractCollectionId) {
30699
- contractCollectionId = await readVariable(
30700
- dependencies.github,
30701
- inputs.projectName,
30702
- "CONTRACT_COLLECTION_UID"
30587
+ contractCollectionId = findCloudResourceId(
30588
+ cloudCollections,
30589
+ (filePath) => filePath.includes("[Contract]")
30703
30590
  );
30591
+ if (contractCollectionId) {
30592
+ dependencies.core.info("Resolved contract-collection-id from .postman/resources.yaml");
30593
+ }
30704
30594
  }
30705
30595
  }
30706
30596
  if (specId) {
@@ -30724,7 +30614,10 @@ async function runBootstrap(inputs, dependencies) {
30724
30614
  } else {
30725
30615
  specId = await dependencies.postman.uploadSpec(
30726
30616
  workspaceId || "",
30727
- inputs.projectName,
30617
+ createAssetProjectName(
30618
+ inputs,
30619
+ inputs.specSyncMode === "version" ? releaseLabel : void 0
30620
+ ),
30728
30621
  document
30729
30622
  );
30730
30623
  }
@@ -30733,13 +30626,6 @@ async function runBootstrap(inputs, dependencies) {
30733
30626
  }
30734
30627
  );
30735
30628
  void specContent;
30736
- await writeVariable(
30737
- dependencies.github,
30738
- inputs.projectName,
30739
- "SPEC_UID",
30740
- outputs["spec-id"],
30741
- dependencies.core
30742
- );
30743
30629
  const lintSummary = await runGroup(
30744
30630
  dependencies.core,
30745
30631
  "Lint Spec via Postman CLI",
@@ -30777,13 +30663,15 @@ async function runBootstrap(inputs, dependencies) {
30777
30663
  dependencies.core,
30778
30664
  "Generate Collections from Spec",
30779
30665
  async () => {
30780
- outputs["baseline-collection-id"] = baselineCollectionId || "";
30781
- outputs["smoke-collection-id"] = smokeCollectionId || "";
30782
- outputs["contract-collection-id"] = contractCollectionId || "";
30666
+ const shouldReuseCollections = inputs.collectionSyncMode !== "refresh";
30667
+ const assetProjectName = inputs.collectionSyncMode === "version" ? createAssetProjectName(inputs, releaseLabel) : inputs.projectName;
30668
+ outputs["baseline-collection-id"] = shouldReuseCollections ? baselineCollectionId || "" : "";
30669
+ outputs["smoke-collection-id"] = shouldReuseCollections ? smokeCollectionId || "" : "";
30670
+ outputs["contract-collection-id"] = shouldReuseCollections ? contractCollectionId || "" : "";
30783
30671
  if (!outputs["baseline-collection-id"]) {
30784
30672
  outputs["baseline-collection-id"] = await dependencies.postman.generateCollection(
30785
30673
  outputs["spec-id"],
30786
- inputs.projectName,
30674
+ assetProjectName,
30787
30675
  "[Baseline]"
30788
30676
  );
30789
30677
  } else {
@@ -30791,17 +30679,10 @@ async function runBootstrap(inputs, dependencies) {
30791
30679
  `Using existing baseline collection: ${outputs["baseline-collection-id"]}`
30792
30680
  );
30793
30681
  }
30794
- await writeVariable(
30795
- dependencies.github,
30796
- inputs.projectName,
30797
- "BASELINE_COLLECTION_UID",
30798
- outputs["baseline-collection-id"],
30799
- dependencies.core
30800
- );
30801
30682
  if (!outputs["smoke-collection-id"]) {
30802
30683
  outputs["smoke-collection-id"] = await dependencies.postman.generateCollection(
30803
30684
  outputs["spec-id"],
30804
- inputs.projectName,
30685
+ assetProjectName,
30805
30686
  "[Smoke]"
30806
30687
  );
30807
30688
  } else {
@@ -30809,17 +30690,10 @@ async function runBootstrap(inputs, dependencies) {
30809
30690
  `Using existing smoke collection: ${outputs["smoke-collection-id"]}`
30810
30691
  );
30811
30692
  }
30812
- await writeVariable(
30813
- dependencies.github,
30814
- inputs.projectName,
30815
- "SMOKE_COLLECTION_UID",
30816
- outputs["smoke-collection-id"],
30817
- dependencies.core
30818
- );
30819
30693
  if (!outputs["contract-collection-id"]) {
30820
30694
  outputs["contract-collection-id"] = await dependencies.postman.generateCollection(
30821
30695
  outputs["spec-id"],
30822
- inputs.projectName,
30696
+ assetProjectName,
30823
30697
  "[Contract]"
30824
30698
  );
30825
30699
  } else {
@@ -30827,13 +30701,6 @@ async function runBootstrap(inputs, dependencies) {
30827
30701
  `Using existing contract collection: ${outputs["contract-collection-id"]}`
30828
30702
  );
30829
30703
  }
30830
- await writeVariable(
30831
- dependencies.github,
30832
- inputs.projectName,
30833
- "CONTRACT_COLLECTION_UID",
30834
- outputs["contract-collection-id"],
30835
- dependencies.core
30836
- );
30837
30704
  }
30838
30705
  );
30839
30706
  outputs["collections-json"] = JSON.stringify({
@@ -30871,26 +30738,47 @@ async function runBootstrap(inputs, dependencies) {
30871
30738
  ]);
30872
30739
  }
30873
30740
  );
30874
- if (dependencies.github) {
30875
- const github = dependencies.github;
30876
- await runGroup(
30877
- dependencies.core,
30878
- "Store Postman UIDs as Repo Variables",
30879
- async () => {
30880
- await persistBootstrapRepositoryVariables(
30881
- github,
30882
- inputs.projectName,
30883
- outputs,
30884
- systemEnvMap,
30885
- environments,
30886
- {
30887
- lintErrors: lintSummary.errors,
30888
- lintWarnings: lintSummary.warnings
30889
- },
30890
- dependencies.core
30891
- );
30892
- }
30893
- );
30741
+ const linkedCollectionIds = [
30742
+ outputs["baseline-collection-id"],
30743
+ outputs["smoke-collection-id"],
30744
+ outputs["contract-collection-id"]
30745
+ ].filter(Boolean);
30746
+ if (linkedCollectionIds.length > 0) {
30747
+ if (dependencies.internalIntegration) {
30748
+ await runGroup(
30749
+ dependencies.core,
30750
+ "Link Collections to Specification",
30751
+ async () => {
30752
+ await dependencies.internalIntegration?.linkCollectionsToSpecification(
30753
+ outputs["spec-id"],
30754
+ linkedCollectionIds.map((collectionId) => ({
30755
+ collectionId,
30756
+ syncOptions: {
30757
+ syncExamples: inputs.syncExamples
30758
+ }
30759
+ }))
30760
+ );
30761
+ }
30762
+ );
30763
+ await runGroup(
30764
+ dependencies.core,
30765
+ "Sync Linked Collections",
30766
+ async () => {
30767
+ await Promise.all(
30768
+ linkedCollectionIds.map(
30769
+ (collectionId) => dependencies.internalIntegration.syncCollection(
30770
+ outputs["spec-id"],
30771
+ collectionId
30772
+ )
30773
+ )
30774
+ );
30775
+ }
30776
+ );
30777
+ } else {
30778
+ dependencies.core.warning(
30779
+ "Skipping cloud spec-to-collection linking and sync because postman-access-token is not configured"
30780
+ );
30781
+ }
30894
30782
  }
30895
30783
  for (const [name, value] of Object.entries(outputs)) {
30896
30784
  dependencies.core.setOutput(name, value);
@@ -30905,9 +30793,6 @@ async function runAction(actionCore = core, actionExec = exec, actionIo = io) {
30905
30793
  io: actionIo,
30906
30794
  specFetcher: fetch
30907
30795
  });
30908
- if (!dependencies.github) {
30909
- actionCore.info("GitHub repository variable persistence disabled for this run");
30910
- }
30911
30796
  if (inputs.domain && !dependencies.internalIntegration) {
30912
30797
  actionCore.warning(
30913
30798
  "Skipping governance assignment because postman-access-token is not configured"
@@ -30918,22 +30803,12 @@ async function runAction(actionCore = core, actionExec = exec, actionIo = io) {
30918
30803
  function createBootstrapDependencies(inputs, factories) {
30919
30804
  const secretMasker = createSecretMasker([
30920
30805
  inputs.postmanApiKey,
30921
- inputs.postmanAccessToken,
30922
- inputs.githubToken,
30923
- inputs.ghFallbackToken
30806
+ inputs.postmanAccessToken
30924
30807
  ]);
30925
30808
  const postman = new PostmanAssetsClient({
30926
30809
  apiKey: inputs.postmanApiKey,
30927
30810
  secretMasker
30928
30811
  });
30929
- const repository = extractRepositorySlug(inputs.repoUrl);
30930
- const github = inputs.githubToken && inputs.repoUrl && repository ? new GitHubApiClient({
30931
- authMode: inputs.githubAuthMode,
30932
- fallbackToken: inputs.ghFallbackToken,
30933
- repository,
30934
- secretMasker,
30935
- token: inputs.githubToken
30936
- }) : void 0;
30937
30812
  const internalIntegration = inputs.postmanAccessToken ? createInternalIntegrationAdapter({
30938
30813
  accessToken: inputs.postmanAccessToken,
30939
30814
  backend: inputs.integrationBackend,
@@ -30943,30 +30818,12 @@ function createBootstrapDependencies(inputs, factories) {
30943
30818
  return {
30944
30819
  core: factories.core,
30945
30820
  exec: factories.exec,
30946
- github,
30947
30821
  io: factories.io,
30948
30822
  internalIntegration,
30949
30823
  postman,
30950
30824
  specFetcher: factories.specFetcher ?? fetch
30951
30825
  };
30952
30826
  }
30953
- function extractRepositorySlug(repoUrl) {
30954
- const normalized = normalizeInputValue(repoUrl);
30955
- if (!normalized) {
30956
- return void 0;
30957
- }
30958
- try {
30959
- const parsed = new URL(normalized);
30960
- const pathname = parsed.pathname.replace(/^\/+|\/+$/g, "").replace(/\.git$/, "");
30961
- const segments = pathname.split("/").filter(Boolean);
30962
- if (segments.length >= 2) {
30963
- return `${segments[0]}/${segments[1]}`;
30964
- }
30965
- return void 0;
30966
- } catch {
30967
- return void 0;
30968
- }
30969
- }
30970
30827
  var currentModulePath = typeof __filename === "string" ? __filename : "";
30971
30828
  var entrypoint = process.argv[1];
30972
30829
  if (entrypoint && currentModulePath === entrypoint) {
@@ -31089,9 +30946,7 @@ function createCliExec(secretMasker) {
31089
30946
  function createCliDependencies(inputs) {
31090
30947
  const secretMasker = createSecretMasker([
31091
30948
  inputs.postmanApiKey,
31092
- inputs.postmanAccessToken,
31093
- inputs.githubToken,
31094
- inputs.ghFallbackToken
30949
+ inputs.postmanAccessToken
31095
30950
  ]);
31096
30951
  const cliExec = createCliExec(secretMasker);
31097
30952
  return createBootstrapDependencies(inputs, {
@@ -31112,18 +30967,18 @@ function parseCliArgs(argv, env = process.env) {
31112
30967
  "baseline-collection-id",
31113
30968
  "smoke-collection-id",
31114
30969
  "contract-collection-id",
30970
+ "sync-examples",
30971
+ "collection-sync-mode",
30972
+ "spec-sync-mode",
30973
+ "release-label",
31115
30974
  "domain",
31116
30975
  "domain-code",
31117
30976
  "requester-email",
31118
30977
  "workspace-admin-user-ids",
31119
- "environments-json",
31120
- "system-env-map-json",
31121
30978
  "governance-mapping-json",
31122
30979
  "integration-backend",
31123
- "github-auth-mode",
31124
- "github-token",
31125
- "gh-fallback-token",
31126
30980
  "team-id",
30981
+ "workspace-team-id",
31127
30982
  "repo-url"
31128
30983
  ];
31129
30984
  const inputEnv = { ...env };
@@ -31163,9 +31018,6 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
31163
31018
  const config = parseCliArgs(argv, env);
31164
31019
  const inputs = resolveInputs(config.inputEnv);
31165
31020
  const dependencies = createCliDependencies(inputs);
31166
- if (!dependencies.github) {
31167
- dependencies.core.info("GitHub repository variable persistence disabled for this run");
31168
- }
31169
31021
  if (inputs.domain && !dependencies.internalIntegration) {
31170
31022
  dependencies.core.warning(
31171
31023
  "Skipping governance assignment because postman-access-token is not configured"