@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/index.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")) {
@@ -28671,7 +28684,6 @@ var index_exports = {};
28671
28684
  __export(index_exports, {
28672
28685
  createBootstrapDependencies: () => createBootstrapDependencies,
28673
28686
  createPlannedOutputs: () => createPlannedOutputs,
28674
- extractRepositorySlug: () => extractRepositorySlug,
28675
28687
  getInput: () => getInput,
28676
28688
  lintSpecViaCli: () => lintSpecViaCli,
28677
28689
  normalizeSpecDocument: () => normalizeSpecDocument,
@@ -28684,6 +28696,7 @@ module.exports = __toCommonJS(index_exports);
28684
28696
  var core = __toESM(require_core(), 1);
28685
28697
  var exec = __toESM(require_exec(), 1);
28686
28698
  var io = __toESM(require_io(), 1);
28699
+ var import_node_fs = require("node:fs");
28687
28700
  var import_yaml = __toESM(require_dist(), 1);
28688
28701
 
28689
28702
  // src/contracts.ts
@@ -28711,6 +28724,28 @@ var openAlphaActionContract = {
28711
28724
  description: "Existing contract collection ID.",
28712
28725
  required: false
28713
28726
  },
28727
+ "sync-examples": {
28728
+ description: "Whether linked spec/collection relations should enable example syncing.",
28729
+ required: false,
28730
+ default: "true",
28731
+ allowedValues: ["true", "false"]
28732
+ },
28733
+ "collection-sync-mode": {
28734
+ description: "Collection lifecycle policy: reuse existing collections, refresh them from the latest spec, or version them by release label.",
28735
+ required: false,
28736
+ default: "refresh",
28737
+ allowedValues: ["reuse", "refresh", "version"]
28738
+ },
28739
+ "spec-sync-mode": {
28740
+ description: "Spec lifecycle policy: update the canonical spec or create/reuse a versioned spec for the resolved release label.",
28741
+ required: false,
28742
+ default: "update",
28743
+ allowedValues: ["update", "version"]
28744
+ },
28745
+ "release-label": {
28746
+ description: "Optional release label. When omitted for versioned sync, the action derives one from GitHub ref metadata.",
28747
+ required: false
28748
+ },
28714
28749
  "project-name": {
28715
28750
  description: "Service project name.",
28716
28751
  required: true
@@ -28731,20 +28766,14 @@ var openAlphaActionContract = {
28731
28766
  description: "Comma-separated workspace admin user ids.",
28732
28767
  required: false
28733
28768
  },
28769
+ "workspace-team-id": {
28770
+ description: "Numeric sub-team ID for org-mode workspace creation.",
28771
+ required: false
28772
+ },
28734
28773
  "spec-url": {
28735
28774
  description: "HTTPS URL to the OpenAPI document.",
28736
28775
  required: true
28737
28776
  },
28738
- "environments-json": {
28739
- description: "JSON array of environment slugs to preserve in bootstrap outputs.",
28740
- required: false,
28741
- default: '["prod"]'
28742
- },
28743
- "system-env-map-json": {
28744
- description: "JSON map of environment slug to system environment id.",
28745
- required: false,
28746
- default: "{}"
28747
- },
28748
28777
  "governance-mapping-json": {
28749
28778
  description: "JSON map of business domain to governance group name.",
28750
28779
  required: false,
@@ -28758,19 +28787,6 @@ var openAlphaActionContract = {
28758
28787
  description: "Postman access token used for governance and workspace mutations.",
28759
28788
  required: false
28760
28789
  },
28761
- "github-token": {
28762
- description: "GitHub token for repository variable persistence.",
28763
- required: false
28764
- },
28765
- "gh-fallback-token": {
28766
- description: "Fallback token for repository variable APIs.",
28767
- required: false
28768
- },
28769
- "github-auth-mode": {
28770
- description: "GitHub auth mode for repository variable APIs.",
28771
- required: false,
28772
- default: "github_token_first"
28773
- },
28774
28790
  "integration-backend": {
28775
28791
  description: "Integration backend for downstream workspace connectivity.",
28776
28792
  required: false,
@@ -28816,8 +28832,8 @@ var openAlphaActionContract = {
28816
28832
  "OpenAPI operation summary normalization before upload (missing or oversized summaries)",
28817
28833
  "spec linting by UID",
28818
28834
  "baseline, smoke, and contract collection generation",
28835
+ "collection refresh and versioning policies",
28819
28836
  "collection tagging",
28820
- "GitHub repository variable persistence for downstream sync steps",
28821
28837
  "workspace, spec, and collection outputs"
28822
28838
  ],
28823
28839
  removedBehavior: [
@@ -28908,189 +28924,6 @@ function sanitizeHeaders(headers, secretValues) {
28908
28924
  return sanitized;
28909
28925
  }
28910
28926
 
28911
- // src/lib/github/github-api-client.ts
28912
- function buildErrorMessage(method, path, response, body, masker) {
28913
- const status = `${response.status}${response.statusText ? ` ${response.statusText}` : ""}`;
28914
- const sanitizedBody = masker(body || "");
28915
- return sanitizedBody ? masker(`${method} ${path} failed with ${status} - ${sanitizedBody}`) : masker(`${method} ${path} failed with ${status} - [REDACTED]`);
28916
- }
28917
- var GitHubApiClient = class {
28918
- apiBase;
28919
- authMode;
28920
- appToken;
28921
- fallbackToken;
28922
- fetchImpl;
28923
- owner;
28924
- repo;
28925
- repository;
28926
- secretMasker;
28927
- token;
28928
- constructor(options) {
28929
- this.apiBase = String(options.apiBase || "https://api.github.com").replace(
28930
- /\/+$/,
28931
- ""
28932
- );
28933
- this.appToken = String(options.appToken || "").trim();
28934
- this.authMode = options.authMode || "github_token_first";
28935
- this.fallbackToken = String(options.fallbackToken || "").trim();
28936
- this.fetchImpl = options.fetch ?? fetch;
28937
- this.repository = options.repository;
28938
- const [owner, repo] = options.repository.split("/");
28939
- this.owner = owner;
28940
- this.repo = repo;
28941
- this.token = String(options.token || "").trim();
28942
- this.secretMasker = options.secretMasker ?? createSecretMasker([this.token, this.fallbackToken, this.appToken]);
28943
- }
28944
- getTokenOrder() {
28945
- const ordered = [];
28946
- if (this.authMode === "app_token") {
28947
- if (this.appToken) ordered.push(this.appToken);
28948
- if (this.token && this.token !== this.appToken) ordered.push(this.token);
28949
- if (this.fallbackToken && this.fallbackToken !== this.appToken && this.fallbackToken !== this.token) {
28950
- ordered.push(this.fallbackToken);
28951
- }
28952
- return ordered;
28953
- }
28954
- if (this.authMode === "fallback_pat_first") {
28955
- if (this.fallbackToken) ordered.push(this.fallbackToken);
28956
- if (this.token && this.token !== this.fallbackToken) ordered.push(this.token);
28957
- return ordered;
28958
- }
28959
- if (this.token) ordered.push(this.token);
28960
- if (this.fallbackToken && this.fallbackToken !== this.token) {
28961
- ordered.push(this.fallbackToken);
28962
- }
28963
- return ordered;
28964
- }
28965
- isVariablesEndpoint(path) {
28966
- return path.startsWith(`/repos/${this.owner}/${this.repo}/actions/variables`);
28967
- }
28968
- canUseFallback(path) {
28969
- return this.isVariablesEndpoint(path) || path.includes(`/repos/${this.owner}/${this.repo}/contents`) || path.includes("/dispatches");
28970
- }
28971
- rateLimitDelayMs(response, attempt) {
28972
- const retryAfter = Number(response.headers.get("retry-after") || "");
28973
- if (Number.isFinite(retryAfter) && retryAfter > 0) {
28974
- return Math.min(retryAfter * 1e3, 12e4);
28975
- }
28976
- const resetAtSeconds = Number(response.headers.get("x-ratelimit-reset") || "");
28977
- if (Number.isFinite(resetAtSeconds) && resetAtSeconds > 0) {
28978
- const delta = resetAtSeconds * 1e3 - Date.now();
28979
- if (delta > 0) {
28980
- return Math.min(delta + 250, 12e4);
28981
- }
28982
- }
28983
- const base = Math.min(5e3 * Math.pow(2, attempt), 12e4);
28984
- const jitter = Math.floor(Math.random() * 250);
28985
- return Math.min(base + jitter, 12e4);
28986
- }
28987
- async requestWithToken(path, init, token) {
28988
- const MAX_RETRIES = 5;
28989
- const normalizedToken = String(token || "").trim();
28990
- if (!normalizedToken) {
28991
- throw new Error(`Missing GitHub auth token for request ${path}`);
28992
- }
28993
- for (let attempt = 0; ; attempt++) {
28994
- const response = await this.fetchImpl(`${this.apiBase}${path}`, {
28995
- ...init,
28996
- headers: {
28997
- Accept: "application/vnd.github+json",
28998
- Authorization: `Bearer ${normalizedToken}`,
28999
- "Content-Type": "application/json",
29000
- ...init.headers || {}
29001
- }
29002
- });
29003
- if (attempt < MAX_RETRIES && (response.status === 403 || response.status === 429)) {
29004
- const body = await response.clone().text().catch(() => "");
29005
- if (isRateLimitedResponse(response, body)) {
29006
- const delay = this.rateLimitDelayMs(response, attempt);
29007
- console.log(
29008
- `GitHub API rate limited, retrying in ${Math.ceil(delay / 1e3)}s (attempt ${attempt + 1}/${MAX_RETRIES})...`
29009
- );
29010
- await new Promise((r) => setTimeout(r, delay));
29011
- continue;
29012
- }
29013
- }
29014
- return response;
29015
- }
29016
- }
29017
- async request(path, init = {}) {
29018
- const orderedTokens = this.getTokenOrder();
29019
- if (orderedTokens.length === 0) {
29020
- throw new Error("No GitHub auth token configured");
29021
- }
29022
- const first = await this.requestWithToken(path, init, orderedTokens[0]);
29023
- if (orderedTokens.length < 2 || !this.canUseFallback(path)) {
29024
- return first;
29025
- }
29026
- const isVariableGet404 = first.status === 404 && (!init.method || init.method === "GET") && this.isVariablesEndpoint(path);
29027
- if (first.status !== 403 && !isVariableGet404) {
29028
- return first;
29029
- }
29030
- return this.requestWithToken(path, init, orderedTokens[1]);
29031
- }
29032
- async setRepositoryVariable(name, value) {
29033
- if (!value) {
29034
- throw new Error(`Repo variable ${name} is empty`);
29035
- }
29036
- const path = `/repos/${this.repository}/actions/variables`;
29037
- const body = JSON.stringify({ name, value: String(value) });
29038
- const createResponse = await this.request(path, {
29039
- method: "POST",
29040
- body
29041
- });
29042
- if (createResponse.ok || createResponse.status === 201) {
29043
- return;
29044
- }
29045
- if (createResponse.status === 409 || createResponse.status === 422) {
29046
- const updatePath = `/repos/${this.repository}/actions/variables/${name}`;
29047
- const updateResponse = await this.request(updatePath, {
29048
- method: "PATCH",
29049
- body
29050
- });
29051
- if (updateResponse.ok) {
29052
- return;
29053
- }
29054
- const text2 = await updateResponse.text().catch(() => "");
29055
- throw new Error(
29056
- buildErrorMessage("PATCH", updatePath, updateResponse, text2, this.secretMasker)
29057
- );
29058
- }
29059
- const text = await createResponse.text().catch(() => "");
29060
- throw new Error(
29061
- buildErrorMessage("POST", path, createResponse, text, this.secretMasker)
29062
- );
29063
- }
29064
- async getRepositoryVariable(name) {
29065
- const path = `/repos/${this.repository}/actions/variables/${name}`;
29066
- const response = await this.request(path, {
29067
- method: "GET"
29068
- });
29069
- if (response.status === 404) {
29070
- return "";
29071
- }
29072
- if (!response.ok) {
29073
- const text = await response.text().catch(() => "");
29074
- throw new Error(
29075
- buildErrorMessage("GET", path, response, text, this.secretMasker)
29076
- );
29077
- }
29078
- const data = await response.json();
29079
- return String(data.value || "");
29080
- }
29081
- };
29082
- function isRateLimitedResponse(response, body) {
29083
- if (response.status !== 403 && response.status !== 429) return false;
29084
- const remaining = response.headers.get("x-ratelimit-remaining");
29085
- const retryAfter = response.headers.get("retry-after");
29086
- if (remaining === "0") return true;
29087
- if (retryAfter) return true;
29088
- const message = body.toLowerCase();
29089
- if (message.includes("secondary rate limit")) return true;
29090
- if (message.includes("api rate limit exceeded")) return true;
29091
- return response.status === 429;
29092
- }
29093
-
29094
28927
  // src/lib/http-error.ts
29095
28928
  function truncate(value, limit) {
29096
28929
  if (value.length <= limit) {
@@ -29273,6 +29106,16 @@ var PostmanAssetsClient = class {
29273
29106
  }
29274
29107
  return void 0;
29275
29108
  }
29109
+ async getTeams() {
29110
+ const data = await this.request("/teams");
29111
+ const teams = data?.data ?? [];
29112
+ return Array.isArray(teams) ? teams.filter((t) => t?.id && t?.name).map((t) => ({
29113
+ id: Number(t.id),
29114
+ name: String(t.name),
29115
+ handle: String(t.handle || ""),
29116
+ ...t.organizationId != null ? { organizationId: Number(t.organizationId) } : {}
29117
+ })) : [];
29118
+ }
29276
29119
  async request(path, init = {}) {
29277
29120
  const url = path.startsWith("http") ? path : `${this.baseUrl}${path}`;
29278
29121
  const response = await this.fetchImpl(url, {
@@ -29301,13 +29144,14 @@ var PostmanAssetsClient = class {
29301
29144
  return null;
29302
29145
  }
29303
29146
  }
29304
- async createWorkspace(name, about) {
29147
+ async createWorkspace(name, about, targetTeamId) {
29305
29148
  return retry(async () => {
29306
29149
  const payload = {
29307
29150
  workspace: {
29308
29151
  about,
29309
29152
  name,
29310
- type: "team"
29153
+ type: "team",
29154
+ ...targetTeamId != null && !Number.isNaN(targetTeamId) ? { teamId: targetTeamId } : {}
29311
29155
  }
29312
29156
  };
29313
29157
  let created;
@@ -29319,7 +29163,7 @@ var PostmanAssetsClient = class {
29319
29163
  } catch (err) {
29320
29164
  if (err instanceof Error && err.message.includes("Only personal workspaces")) {
29321
29165
  throw new Error(
29322
- "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."
29166
+ "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."
29323
29167
  );
29324
29168
  }
29325
29169
  throw err;
@@ -29338,7 +29182,11 @@ var PostmanAssetsClient = class {
29338
29182
  return {
29339
29183
  id: workspaceId
29340
29184
  };
29341
- }, { maxAttempts: 3, delayMs: 2e3 });
29185
+ }, {
29186
+ maxAttempts: 3,
29187
+ delayMs: 2e3,
29188
+ shouldRetry: (err) => !(err instanceof Error && err.message.includes("workspace-team-id"))
29189
+ });
29342
29190
  }
29343
29191
  async listWorkspaces() {
29344
29192
  const data = await this.request("/workspaces");
@@ -29813,6 +29661,26 @@ var BifrostInternalIntegrationAdapter = class {
29813
29661
  options.workerBaseUrl || "https://catalog-admin.postman-account2009.workers.dev"
29814
29662
  ).replace(/\/+$/, "");
29815
29663
  }
29664
+ async proxyRequest(service, method, requestPath, body) {
29665
+ const url = "https://bifrost-premium-https-v4.gw.postman.com/ws/proxy";
29666
+ const headers = {
29667
+ "Content-Type": "application/json",
29668
+ "x-access-token": this.accessToken
29669
+ };
29670
+ if (this.teamId) {
29671
+ headers["x-entity-team-id"] = this.teamId;
29672
+ }
29673
+ return this.fetchImpl(url, {
29674
+ method: "POST",
29675
+ headers,
29676
+ body: JSON.stringify({
29677
+ service,
29678
+ method,
29679
+ path: requestPath,
29680
+ ...body !== void 0 ? { body } : {}
29681
+ })
29682
+ });
29683
+ }
29816
29684
  async assignWorkspaceToGovernanceGroup(workspaceId, domain, mappingJson) {
29817
29685
  let mapping;
29818
29686
  try {
@@ -29906,14 +29774,6 @@ var BifrostInternalIntegrationAdapter = class {
29906
29774
  }
29907
29775
  }
29908
29776
  async connectWorkspaceToRepository(workspaceId, repoUrl) {
29909
- const url = "https://bifrost-premium-https-v4.gw.postman.com/ws/proxy";
29910
- const headers = {
29911
- "Content-Type": "application/json",
29912
- "x-access-token": this.accessToken
29913
- };
29914
- if (this.teamId) {
29915
- headers["x-entity-team-id"] = this.teamId;
29916
- }
29917
29777
  const payload = {
29918
29778
  service: "workspaces",
29919
29779
  method: "POST",
@@ -29924,11 +29784,12 @@ var BifrostInternalIntegrationAdapter = class {
29924
29784
  versionControl: true
29925
29785
  }
29926
29786
  };
29927
- const response = await this.fetchImpl(url, {
29928
- method: "POST",
29929
- headers,
29930
- body: JSON.stringify(payload)
29931
- });
29787
+ const response = await this.proxyRequest(
29788
+ payload.service,
29789
+ payload.method,
29790
+ payload.path,
29791
+ payload.body
29792
+ );
29932
29793
  if (response.ok) return;
29933
29794
  if (response.status === 400) {
29934
29795
  const body = await response.text();
@@ -29945,29 +29806,68 @@ var BifrostInternalIntegrationAdapter = class {
29945
29806
  }
29946
29807
  throw await HttpError.fromResponse(response, {
29947
29808
  method: "POST",
29948
- requestHeaders: headers,
29809
+ requestHeaders: {
29810
+ "Content-Type": "application/json",
29811
+ "x-access-token": this.accessToken,
29812
+ ...this.teamId ? { "x-entity-team-id": this.teamId } : {}
29813
+ },
29949
29814
  secretValues: [this.accessToken],
29950
- url
29815
+ url: "https://bifrost-premium-https-v4.gw.postman.com/ws/proxy"
29951
29816
  });
29952
29817
  }
29953
- async getWorkspaceGitRepoUrl(workspaceId) {
29954
- const url = "https://bifrost-premium-https-v4.gw.postman.com/ws/proxy";
29955
- const headers = {
29956
- "Content-Type": "application/json",
29957
- "x-access-token": this.accessToken
29958
- };
29959
- if (this.teamId) {
29960
- headers["x-entity-team-id"] = this.teamId;
29818
+ async linkCollectionsToSpecification(specificationId, collections) {
29819
+ if (collections.length === 0) {
29820
+ return;
29961
29821
  }
29962
- const response = await this.fetchImpl(url, {
29822
+ const response = await this.proxyRequest(
29823
+ "specification",
29824
+ "put",
29825
+ `/specifications/${specificationId}/collections`,
29826
+ collections.map((collection) => ({
29827
+ collectionId: collection.collectionId,
29828
+ ...collection.syncOptions ? { syncOptions: collection.syncOptions } : {}
29829
+ }))
29830
+ );
29831
+ if (response.ok) {
29832
+ return;
29833
+ }
29834
+ throw await HttpError.fromResponse(response, {
29963
29835
  method: "POST",
29964
- headers,
29965
- body: JSON.stringify({
29966
- service: "workspaces",
29967
- method: "GET",
29968
- path: `/workspaces/${workspaceId}/filesystem`
29969
- })
29836
+ requestHeaders: {
29837
+ "Content-Type": "application/json",
29838
+ "x-access-token": this.accessToken,
29839
+ ...this.teamId ? { "x-entity-team-id": this.teamId } : {}
29840
+ },
29841
+ secretValues: [this.accessToken],
29842
+ url: "https://bifrost-premium-https-v4.gw.postman.com/ws/proxy"
29970
29843
  });
29844
+ }
29845
+ async syncCollection(specificationId, collectionId) {
29846
+ const response = await this.proxyRequest(
29847
+ "specification",
29848
+ "post",
29849
+ `/specifications/${specificationId}/collections/${collectionId}/sync`
29850
+ );
29851
+ if (response.ok) {
29852
+ return;
29853
+ }
29854
+ throw await HttpError.fromResponse(response, {
29855
+ method: "POST",
29856
+ requestHeaders: {
29857
+ "Content-Type": "application/json",
29858
+ "x-access-token": this.accessToken,
29859
+ ...this.teamId ? { "x-entity-team-id": this.teamId } : {}
29860
+ },
29861
+ secretValues: [this.accessToken],
29862
+ url: "https://bifrost-premium-https-v4.gw.postman.com/ws/proxy"
29863
+ });
29864
+ }
29865
+ async getWorkspaceGitRepoUrl(workspaceId) {
29866
+ const response = await this.proxyRequest(
29867
+ "workspaces",
29868
+ "GET",
29869
+ `/workspaces/${workspaceId}/filesystem`
29870
+ );
29971
29871
  if (response.status === 404) return null;
29972
29872
  if (!response.ok) return null;
29973
29873
  const body = await response.text();
@@ -30150,31 +30050,24 @@ function requireInput(actionCore, name) {
30150
30050
  function optionalInput(actionCore, name) {
30151
30051
  return normalizeInputValue(actionCore.getInput(name));
30152
30052
  }
30153
- function parseJsonValue(raw, fallback, inputName) {
30154
- try {
30155
- return JSON.parse(raw || JSON.stringify(fallback)) ?? fallback;
30156
- } catch (error) {
30157
- throw new Error(
30158
- `Invalid JSON for ${inputName}: ${error instanceof Error ? error.message : String(error)}`
30159
- );
30160
- }
30053
+ function parseBooleanInput(value, defaultValue) {
30054
+ const normalized = String(value || "").trim().toLowerCase();
30055
+ if (!normalized) return defaultValue;
30056
+ if (["true", "1", "yes", "on"].includes(normalized)) return true;
30057
+ if (["false", "0", "no", "off"].includes(normalized)) return false;
30058
+ return defaultValue;
30161
30059
  }
30162
- function asStringArray(value, inputName) {
30163
- if (!Array.isArray(value)) {
30164
- throw new Error(`${inputName} must be a JSON array`);
30060
+ function parseCollectionSyncMode(value) {
30061
+ if (value === "reuse" || value === "version") {
30062
+ return value;
30165
30063
  }
30166
- return value.map((entry) => String(entry));
30064
+ return "refresh";
30167
30065
  }
30168
- function asStringMap(value, inputName) {
30169
- if (!value || typeof value !== "object" || Array.isArray(value)) {
30170
- throw new Error(`${inputName} must be a JSON object`);
30171
- }
30172
- return Object.fromEntries(
30173
- Object.entries(value).map(([key, entry]) => [
30174
- key,
30175
- String(entry)
30176
- ])
30177
- );
30066
+ function parseSpecSyncMode(value) {
30067
+ if (value === "version") {
30068
+ return value;
30069
+ }
30070
+ return "update";
30178
30071
  }
30179
30072
  function resolveInputs(env = process.env) {
30180
30073
  const repoContext = detectRepoContext(
@@ -30202,28 +30095,32 @@ function resolveInputs(env = process.env) {
30202
30095
  }
30203
30096
  }
30204
30097
  return {
30205
- projectName: getInput("project-name", env) ?? "",
30098
+ projectName: getInput("project-name", env) ?? env.GITHUB_REPOSITORY?.split("/").pop() ?? env.CI_PROJECT_NAME ?? "",
30206
30099
  workspaceId: getInput("workspace-id", env),
30207
30100
  specId: getInput("spec-id", env),
30208
30101
  baselineCollectionId: getInput("baseline-collection-id", env),
30209
30102
  smokeCollectionId: getInput("smoke-collection-id", env),
30210
30103
  contractCollectionId: getInput("contract-collection-id", env),
30104
+ syncExamples: parseBooleanInput(getInput("sync-examples", env), true),
30105
+ collectionSyncMode: parseCollectionSyncMode(getInput("collection-sync-mode", env)),
30106
+ specSyncMode: parseSpecSyncMode(getInput("spec-sync-mode", env)),
30107
+ releaseLabel: getInput("release-label", env),
30211
30108
  domain: getInput("domain", env),
30212
30109
  domainCode: getInput("domain-code", env),
30213
30110
  requesterEmail: getInput("requester-email", env),
30214
30111
  workspaceAdminUserIds: getInput("workspace-admin-user-ids", env) || env.WORKSPACE_ADMIN_USER_IDS || "",
30112
+ workspaceTeamId: getInput("workspace-team-id", env) || env.POSTMAN_WORKSPACE_TEAM_ID,
30215
30113
  teamId: getInput("team-id", env) || env.POSTMAN_TEAM_ID || "",
30216
30114
  repoUrl: repoContext.repoUrl || "",
30217
30115
  specUrl,
30218
- environmentsJson: getInput("environments-json", env) ?? openAlphaActionContract.inputs["environments-json"].default ?? '["prod"]',
30219
- systemEnvMapJson: getInput("system-env-map-json", env) ?? openAlphaActionContract.inputs["system-env-map-json"].default ?? "{}",
30220
30116
  governanceMappingJson: getInput("governance-mapping-json", env) ?? openAlphaActionContract.inputs["governance-mapping-json"].default ?? "{}",
30221
30117
  postmanApiKey: getInput("postman-api-key", env) ?? "",
30222
30118
  postmanAccessToken: getInput("postman-access-token", env),
30223
- githubToken: getInput("github-token", env),
30224
- ghFallbackToken: getInput("gh-fallback-token", env),
30225
- githubAuthMode: getInput("github-auth-mode", env) ?? openAlphaActionContract.inputs["github-auth-mode"].default ?? "github_token_first",
30226
- integrationBackend
30119
+ integrationBackend,
30120
+ githubRefName: env.GITHUB_REF_NAME,
30121
+ githubHeadRef: env.GITHUB_HEAD_REF,
30122
+ githubRef: env.GITHUB_REF,
30123
+ githubSha: env.GITHUB_SHA
30227
30124
  };
30228
30125
  }
30229
30126
  function createPlannedOutputs(inputs) {
@@ -30254,12 +30151,8 @@ function readActionInputs(actionCore) {
30254
30151
  const specUrl = requireInput(actionCore, "spec-url");
30255
30152
  const postmanApiKey = requireInput(actionCore, "postman-api-key");
30256
30153
  const postmanAccessToken = optionalInput(actionCore, "postman-access-token");
30257
- const githubToken = optionalInput(actionCore, "github-token");
30258
- const ghFallbackToken = optionalInput(actionCore, "gh-fallback-token");
30259
30154
  actionCore.setSecret(postmanApiKey);
30260
30155
  if (postmanAccessToken) actionCore.setSecret(postmanAccessToken);
30261
- if (githubToken) actionCore.setSecret(githubToken);
30262
- if (ghFallbackToken) actionCore.setSecret(ghFallbackToken);
30263
30156
  const inputs = resolveInputs({
30264
30157
  ...process.env,
30265
30158
  INPUT_PROJECT_NAME: projectName,
@@ -30268,6 +30161,10 @@ function readActionInputs(actionCore) {
30268
30161
  INPUT_BASELINE_COLLECTION_ID: optionalInput(actionCore, "baseline-collection-id"),
30269
30162
  INPUT_SMOKE_COLLECTION_ID: optionalInput(actionCore, "smoke-collection-id"),
30270
30163
  INPUT_CONTRACT_COLLECTION_ID: optionalInput(actionCore, "contract-collection-id"),
30164
+ INPUT_SYNC_EXAMPLES: optionalInput(actionCore, "sync-examples") ?? openAlphaActionContract.inputs["sync-examples"].default,
30165
+ INPUT_COLLECTION_SYNC_MODE: optionalInput(actionCore, "collection-sync-mode") ?? openAlphaActionContract.inputs["collection-sync-mode"].default,
30166
+ INPUT_SPEC_SYNC_MODE: optionalInput(actionCore, "spec-sync-mode") ?? openAlphaActionContract.inputs["spec-sync-mode"].default,
30167
+ INPUT_RELEASE_LABEL: optionalInput(actionCore, "release-label"),
30271
30168
  INPUT_DOMAIN: optionalInput(actionCore, "domain"),
30272
30169
  INPUT_DOMAIN_CODE: optionalInput(actionCore, "domain-code"),
30273
30170
  INPUT_REQUESTER_EMAIL: optionalInput(actionCore, "requester-email"),
@@ -30275,17 +30172,13 @@ function readActionInputs(actionCore) {
30275
30172
  actionCore,
30276
30173
  "workspace-admin-user-ids"
30277
30174
  ),
30175
+ INPUT_WORKSPACE_TEAM_ID: optionalInput(actionCore, "workspace-team-id") || process.env.POSTMAN_WORKSPACE_TEAM_ID,
30278
30176
  INPUT_TEAM_ID: optionalInput(actionCore, "postman-team-id") || process.env.POSTMAN_TEAM_ID,
30279
30177
  INPUT_REPO_URL: optionalInput(actionCore, "repo-url"),
30280
30178
  INPUT_SPEC_URL: specUrl,
30281
- INPUT_ENVIRONMENTS_JSON: optionalInput(actionCore, "environments-json") ?? openAlphaActionContract.inputs["environments-json"].default,
30282
- INPUT_SYSTEM_ENV_MAP_JSON: optionalInput(actionCore, "system-env-map-json") ?? openAlphaActionContract.inputs["system-env-map-json"].default,
30283
30179
  INPUT_GOVERNANCE_MAPPING_JSON: optionalInput(actionCore, "governance-mapping-json") ?? openAlphaActionContract.inputs["governance-mapping-json"].default,
30284
30180
  INPUT_POSTMAN_API_KEY: postmanApiKey,
30285
30181
  INPUT_POSTMAN_ACCESS_TOKEN: postmanAccessToken,
30286
- INPUT_GITHUB_TOKEN: githubToken,
30287
- INPUT_GH_FALLBACK_TOKEN: ghFallbackToken,
30288
- INPUT_GITHUB_AUTH_MODE: optionalInput(actionCore, "github-auth-mode") ?? openAlphaActionContract.inputs["github-auth-mode"].default,
30289
30182
  INPUT_INTEGRATION_BACKEND: optionalInput(actionCore, "integration-backend") ?? openAlphaActionContract.inputs["integration-backend"].default
30290
30183
  });
30291
30184
  return inputs;
@@ -30362,6 +30255,45 @@ async function fetchSpecDocument(specUrl, specFetcher) {
30362
30255
  }
30363
30256
  );
30364
30257
  }
30258
+ function normalizeReleaseLabel(value) {
30259
+ const trimmed = String(value || "").trim();
30260
+ if (!trimmed) {
30261
+ return void 0;
30262
+ }
30263
+ return trimmed.replace(/^refs\/heads\//, "").replace(/^refs\/tags\//, "").replace(/^refs\/pull\//, "pull-").replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || void 0;
30264
+ }
30265
+ function deriveReleaseLabel(inputs) {
30266
+ if (inputs.releaseLabel) {
30267
+ return normalizeReleaseLabel(inputs.releaseLabel);
30268
+ }
30269
+ return normalizeReleaseLabel(inputs.githubRefName) ?? normalizeReleaseLabel(inputs.githubHeadRef) ?? normalizeReleaseLabel(inputs.githubRef);
30270
+ }
30271
+ function createAssetProjectName(inputs, releaseLabel) {
30272
+ if (!releaseLabel) {
30273
+ return inputs.projectName;
30274
+ }
30275
+ return `${inputs.projectName} ${releaseLabel}`;
30276
+ }
30277
+ function readResourcesState() {
30278
+ try {
30279
+ return (0, import_yaml.parse)((0, import_node_fs.readFileSync)(".postman/resources.yaml", "utf8"));
30280
+ } catch {
30281
+ return null;
30282
+ }
30283
+ }
30284
+ function getFirstCloudResourceId(map) {
30285
+ if (!map) {
30286
+ return void 0;
30287
+ }
30288
+ return Object.values(map)[0];
30289
+ }
30290
+ function findCloudResourceId(map, matcher) {
30291
+ if (!map) {
30292
+ return void 0;
30293
+ }
30294
+ const match = Object.entries(map).find(([filePath]) => matcher(filePath));
30295
+ return match?.[1];
30296
+ }
30365
30297
  var SPEC_SUMMARY_MAX_LEN = 200;
30366
30298
  var SPEC_HTTP_METHODS = /* @__PURE__ */ new Set([
30367
30299
  "get",
@@ -30450,122 +30382,28 @@ function validateSpecStructure(content) {
30450
30382
  throw new Error('Spec is missing "openapi" or "swagger" version field');
30451
30383
  }
30452
30384
  }
30453
- function varName(projectName, baseName) {
30454
- const slug = projectName.toUpperCase().replace(/[^A-Z0-9]/g, "_");
30455
- return `POSTMAN_${slug}_${baseName}`;
30456
- }
30457
- async function readVariable(github, projectName, baseName) {
30458
- if (!github) {
30459
- return void 0;
30460
- }
30461
- const namespaced = await github.getRepositoryVariable(varName(projectName, baseName)).catch(() => void 0);
30462
- if (namespaced) {
30463
- return namespaced;
30464
- }
30465
- const legacy = await github.getRepositoryVariable(`POSTMAN_${baseName}`).catch(() => void 0);
30466
- return legacy || void 0;
30467
- }
30468
- async function persistVariable(github, name, value, actionCore) {
30469
- if (!github || !value) {
30470
- return;
30471
- }
30472
- try {
30473
- await github.setRepositoryVariable(name, value);
30474
- } catch (err) {
30475
- actionCore.warning(
30476
- `Failed to persist ${name}: ${err instanceof Error ? err.message : String(err)}`
30477
- );
30478
- }
30479
- }
30480
- async function writeVariable(github, projectName, baseName, value, actionCore) {
30481
- if (!github || !value) {
30482
- return;
30483
- }
30484
- await persistVariable(github, varName(projectName, baseName), value, actionCore);
30485
- await persistVariable(github, `POSTMAN_${baseName}`, value, actionCore);
30486
- }
30487
- async function persistBootstrapRepositoryVariables(github, projectName, outputs, systemEnvMap, environments, lintSummary, actionCore) {
30488
- await persistVariable(
30489
- github,
30490
- "LINT_WARNINGS",
30491
- String(lintSummary.lintWarnings),
30492
- actionCore
30493
- );
30494
- await persistVariable(
30495
- github,
30496
- "LINT_ERRORS",
30497
- String(lintSummary.lintErrors),
30498
- actionCore
30499
- );
30500
- await writeVariable(
30501
- github,
30502
- projectName,
30503
- "WORKSPACE_ID",
30504
- outputs["workspace-id"],
30505
- actionCore
30506
- );
30507
- await writeVariable(github, projectName, "SPEC_UID", outputs["spec-id"], actionCore);
30508
- await writeVariable(
30509
- github,
30510
- projectName,
30511
- "BASELINE_COLLECTION_UID",
30512
- outputs["baseline-collection-id"],
30513
- actionCore
30514
- );
30515
- await writeVariable(
30516
- github,
30517
- projectName,
30518
- "SMOKE_COLLECTION_UID",
30519
- outputs["smoke-collection-id"],
30520
- actionCore
30521
- );
30522
- await writeVariable(
30523
- github,
30524
- projectName,
30525
- "CONTRACT_COLLECTION_UID",
30526
- outputs["contract-collection-id"],
30527
- actionCore
30528
- );
30529
- for (const envName of environments) {
30530
- const systemEnvId = systemEnvMap[envName];
30531
- if (!systemEnvId) {
30532
- continue;
30533
- }
30534
- await writeVariable(
30535
- github,
30536
- projectName,
30537
- `SYSTEM_ENV_${envName.toUpperCase()}`,
30538
- systemEnvId,
30539
- actionCore
30540
- );
30541
- }
30542
- }
30543
30385
  async function runBootstrap(inputs, dependencies) {
30544
30386
  const outputs = createPlannedOutputs(inputs);
30545
- const environments = asStringArray(
30546
- parseJsonValue(inputs.environmentsJson, ["prod"], "environments-json"),
30547
- "environments-json"
30548
- );
30549
- const systemEnvMap = asStringMap(
30550
- parseJsonValue(inputs.systemEnvMapJson, {}, "system-env-map-json"),
30551
- "system-env-map-json"
30552
- );
30387
+ const requiresReleaseLabel = inputs.collectionSyncMode === "version" || inputs.specSyncMode === "version";
30388
+ const releaseLabel = requiresReleaseLabel ? deriveReleaseLabel(inputs) : void 0;
30389
+ if (requiresReleaseLabel && !releaseLabel) {
30390
+ throw new Error(
30391
+ "Versioned spec or collection sync requires a release-label or derivable GitHub ref metadata"
30392
+ );
30393
+ }
30553
30394
  const workspaceName = createWorkspaceName(inputs);
30554
30395
  const aboutText = `Auto-provisioned by Postman CS open-alpha for ${inputs.projectName}`;
30555
30396
  await runGroup(dependencies.core, "Install Postman CLI", async () => {
30556
30397
  await ensurePostmanCli(dependencies, inputs.postmanApiKey);
30557
30398
  });
30558
- const explicitWorkspaceId = inputs.workspaceId;
30559
- let repoWorkspaceId;
30560
- let workspaceId = explicitWorkspaceId;
30561
- if (!workspaceId && dependencies.github) {
30562
- repoWorkspaceId = await readVariable(
30563
- dependencies.github,
30564
- inputs.projectName,
30565
- "WORKSPACE_ID"
30566
- );
30567
- workspaceId = repoWorkspaceId;
30399
+ const resourcesState = readResourcesState();
30400
+ let explicitWorkspaceId = inputs.workspaceId;
30401
+ if (!explicitWorkspaceId && resourcesState?.workspace?.id) {
30402
+ explicitWorkspaceId = resourcesState.workspace.id;
30403
+ dependencies.core.info("Resolved workspace-id from .postman/resources.yaml");
30568
30404
  }
30405
+ const repoWorkspaceId = explicitWorkspaceId;
30406
+ let workspaceId = explicitWorkspaceId;
30569
30407
  let teamId = inputs.teamId || "";
30570
30408
  if (!teamId) {
30571
30409
  teamId = await dependencies.postman.getAutoDerivedTeamId() || "";
@@ -30599,24 +30437,65 @@ async function runBootstrap(inputs, dependencies) {
30599
30437
  } else if (workspaceId) {
30600
30438
  dependencies.core.info(`Using existing workspace: ${workspaceId}`);
30601
30439
  }
30440
+ let workspaceTeamId;
30441
+ if (inputs.workspaceTeamId) {
30442
+ workspaceTeamId = parseInt(inputs.workspaceTeamId, 10);
30443
+ if (Number.isNaN(workspaceTeamId)) {
30444
+ throw new Error(`workspace-team-id must be a numeric sub-team ID, got: ${inputs.workspaceTeamId}`);
30445
+ }
30446
+ }
30447
+ if (!workspaceId && !workspaceTeamId) {
30448
+ try {
30449
+ const teams = await dependencies.postman.getTeams();
30450
+ if (teams.length > 1 && teams.every((t) => t.organizationId == null)) {
30451
+ dependencies.core.warning(
30452
+ "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."
30453
+ );
30454
+ }
30455
+ const orgIds = new Set(teams.filter((t) => t.organizationId != null).map((t) => t.organizationId));
30456
+ const meTeamId = parseInt(teamId, 10);
30457
+ const isOrgMode = teams.length > 1 && orgIds.size === 1 && orgIds.has(meTeamId);
30458
+ if (isOrgMode) {
30459
+ const teamList = teams.map((t) => ` ${t.id} ${t.name}`).join("\n");
30460
+ throw new Error(
30461
+ `Org-mode account detected. Workspace creation requires a specific sub-team ID.
30462
+
30463
+ Available sub-teams:
30464
+ ${teamList}
30465
+
30466
+ To fix this, set the workspace-team-id input in your workflow:
30467
+ workspace-team-id: '<id>'
30468
+
30469
+ Or for reuse across runs, create a repository variable and reference it:
30470
+ workspace-team-id: \${{ vars.POSTMAN_WORKSPACE_TEAM_ID }}
30471
+
30472
+ For CLI usage, pass --workspace-team-id <id> or export POSTMAN_WORKSPACE_TEAM_ID=<id>.`
30473
+ );
30474
+ } else if (teams.length > 1) {
30475
+ dependencies.core.warning(
30476
+ `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.`
30477
+ );
30478
+ }
30479
+ } catch (err) {
30480
+ if (err instanceof Error && err.message.includes("Org-mode account detected")) {
30481
+ throw err;
30482
+ }
30483
+ dependencies.core.warning(
30484
+ `Could not check for org-mode sub-teams: ${err instanceof Error ? err.message : String(err)}`
30485
+ );
30486
+ }
30487
+ }
30602
30488
  if (!workspaceId) {
30603
30489
  const workspace = await runGroup(
30604
30490
  dependencies.core,
30605
30491
  "Create Postman Workspace",
30606
- async () => dependencies.postman.createWorkspace(workspaceName, aboutText)
30492
+ async () => dependencies.postman.createWorkspace(workspaceName, aboutText, workspaceTeamId)
30607
30493
  );
30608
30494
  workspaceId = workspace.id;
30609
30495
  }
30610
30496
  outputs["workspace-id"] = workspaceId || "";
30611
30497
  outputs["workspace-url"] = `https://go.postman.co/workspace/${workspaceId}`;
30612
30498
  outputs["workspace-name"] = workspaceName;
30613
- await writeVariable(
30614
- dependencies.github,
30615
- inputs.projectName,
30616
- "WORKSPACE_ID",
30617
- outputs["workspace-id"],
30618
- dependencies.core
30619
- );
30620
30499
  if (inputs.domain && dependencies.internalIntegration) {
30621
30500
  await runGroup(
30622
30501
  dependencies.core,
@@ -30671,33 +30550,43 @@ async function runBootstrap(inputs, dependencies) {
30671
30550
  );
30672
30551
  }
30673
30552
  let specId = inputs.specId;
30674
- if (!specId && dependencies.github) {
30675
- specId = await readVariable(dependencies.github, inputs.projectName, "SPEC_UID");
30553
+ if (!specId) {
30554
+ specId = getFirstCloudResourceId(resourcesState?.cloudResources?.specs);
30555
+ if (specId) {
30556
+ dependencies.core.info("Resolved spec-id from .postman/resources.yaml");
30557
+ }
30676
30558
  }
30677
- let baselineCollectionId = inputs.baselineCollectionId;
30678
- let smokeCollectionId = inputs.smokeCollectionId;
30679
- let contractCollectionId = inputs.contractCollectionId;
30680
- if (dependencies.github) {
30559
+ let baselineCollectionId = inputs.collectionSyncMode === "refresh" ? void 0 : inputs.baselineCollectionId;
30560
+ let smokeCollectionId = inputs.collectionSyncMode === "refresh" ? void 0 : inputs.smokeCollectionId;
30561
+ let contractCollectionId = inputs.collectionSyncMode === "refresh" ? void 0 : inputs.contractCollectionId;
30562
+ if (inputs.collectionSyncMode !== "refresh") {
30563
+ const cloudCollections = resourcesState?.cloudResources?.collections;
30681
30564
  if (!baselineCollectionId) {
30682
- baselineCollectionId = await readVariable(
30683
- dependencies.github,
30684
- inputs.projectName,
30685
- "BASELINE_COLLECTION_UID"
30565
+ baselineCollectionId = findCloudResourceId(
30566
+ cloudCollections,
30567
+ (filePath) => filePath.includes("[Baseline]")
30686
30568
  );
30569
+ if (baselineCollectionId) {
30570
+ dependencies.core.info("Resolved baseline-collection-id from .postman/resources.yaml");
30571
+ }
30687
30572
  }
30688
30573
  if (!smokeCollectionId) {
30689
- smokeCollectionId = await readVariable(
30690
- dependencies.github,
30691
- inputs.projectName,
30692
- "SMOKE_COLLECTION_UID"
30574
+ smokeCollectionId = findCloudResourceId(
30575
+ cloudCollections,
30576
+ (filePath) => filePath.includes("[Smoke]")
30693
30577
  );
30578
+ if (smokeCollectionId) {
30579
+ dependencies.core.info("Resolved smoke-collection-id from .postman/resources.yaml");
30580
+ }
30694
30581
  }
30695
30582
  if (!contractCollectionId) {
30696
- contractCollectionId = await readVariable(
30697
- dependencies.github,
30698
- inputs.projectName,
30699
- "CONTRACT_COLLECTION_UID"
30583
+ contractCollectionId = findCloudResourceId(
30584
+ cloudCollections,
30585
+ (filePath) => filePath.includes("[Contract]")
30700
30586
  );
30587
+ if (contractCollectionId) {
30588
+ dependencies.core.info("Resolved contract-collection-id from .postman/resources.yaml");
30589
+ }
30701
30590
  }
30702
30591
  }
30703
30592
  if (specId) {
@@ -30721,7 +30610,10 @@ async function runBootstrap(inputs, dependencies) {
30721
30610
  } else {
30722
30611
  specId = await dependencies.postman.uploadSpec(
30723
30612
  workspaceId || "",
30724
- inputs.projectName,
30613
+ createAssetProjectName(
30614
+ inputs,
30615
+ inputs.specSyncMode === "version" ? releaseLabel : void 0
30616
+ ),
30725
30617
  document
30726
30618
  );
30727
30619
  }
@@ -30730,13 +30622,6 @@ async function runBootstrap(inputs, dependencies) {
30730
30622
  }
30731
30623
  );
30732
30624
  void specContent;
30733
- await writeVariable(
30734
- dependencies.github,
30735
- inputs.projectName,
30736
- "SPEC_UID",
30737
- outputs["spec-id"],
30738
- dependencies.core
30739
- );
30740
30625
  const lintSummary = await runGroup(
30741
30626
  dependencies.core,
30742
30627
  "Lint Spec via Postman CLI",
@@ -30774,13 +30659,15 @@ async function runBootstrap(inputs, dependencies) {
30774
30659
  dependencies.core,
30775
30660
  "Generate Collections from Spec",
30776
30661
  async () => {
30777
- outputs["baseline-collection-id"] = baselineCollectionId || "";
30778
- outputs["smoke-collection-id"] = smokeCollectionId || "";
30779
- outputs["contract-collection-id"] = contractCollectionId || "";
30662
+ const shouldReuseCollections = inputs.collectionSyncMode !== "refresh";
30663
+ const assetProjectName = inputs.collectionSyncMode === "version" ? createAssetProjectName(inputs, releaseLabel) : inputs.projectName;
30664
+ outputs["baseline-collection-id"] = shouldReuseCollections ? baselineCollectionId || "" : "";
30665
+ outputs["smoke-collection-id"] = shouldReuseCollections ? smokeCollectionId || "" : "";
30666
+ outputs["contract-collection-id"] = shouldReuseCollections ? contractCollectionId || "" : "";
30780
30667
  if (!outputs["baseline-collection-id"]) {
30781
30668
  outputs["baseline-collection-id"] = await dependencies.postman.generateCollection(
30782
30669
  outputs["spec-id"],
30783
- inputs.projectName,
30670
+ assetProjectName,
30784
30671
  "[Baseline]"
30785
30672
  );
30786
30673
  } else {
@@ -30788,17 +30675,10 @@ async function runBootstrap(inputs, dependencies) {
30788
30675
  `Using existing baseline collection: ${outputs["baseline-collection-id"]}`
30789
30676
  );
30790
30677
  }
30791
- await writeVariable(
30792
- dependencies.github,
30793
- inputs.projectName,
30794
- "BASELINE_COLLECTION_UID",
30795
- outputs["baseline-collection-id"],
30796
- dependencies.core
30797
- );
30798
30678
  if (!outputs["smoke-collection-id"]) {
30799
30679
  outputs["smoke-collection-id"] = await dependencies.postman.generateCollection(
30800
30680
  outputs["spec-id"],
30801
- inputs.projectName,
30681
+ assetProjectName,
30802
30682
  "[Smoke]"
30803
30683
  );
30804
30684
  } else {
@@ -30806,17 +30686,10 @@ async function runBootstrap(inputs, dependencies) {
30806
30686
  `Using existing smoke collection: ${outputs["smoke-collection-id"]}`
30807
30687
  );
30808
30688
  }
30809
- await writeVariable(
30810
- dependencies.github,
30811
- inputs.projectName,
30812
- "SMOKE_COLLECTION_UID",
30813
- outputs["smoke-collection-id"],
30814
- dependencies.core
30815
- );
30816
30689
  if (!outputs["contract-collection-id"]) {
30817
30690
  outputs["contract-collection-id"] = await dependencies.postman.generateCollection(
30818
30691
  outputs["spec-id"],
30819
- inputs.projectName,
30692
+ assetProjectName,
30820
30693
  "[Contract]"
30821
30694
  );
30822
30695
  } else {
@@ -30824,13 +30697,6 @@ async function runBootstrap(inputs, dependencies) {
30824
30697
  `Using existing contract collection: ${outputs["contract-collection-id"]}`
30825
30698
  );
30826
30699
  }
30827
- await writeVariable(
30828
- dependencies.github,
30829
- inputs.projectName,
30830
- "CONTRACT_COLLECTION_UID",
30831
- outputs["contract-collection-id"],
30832
- dependencies.core
30833
- );
30834
30700
  }
30835
30701
  );
30836
30702
  outputs["collections-json"] = JSON.stringify({
@@ -30868,26 +30734,47 @@ async function runBootstrap(inputs, dependencies) {
30868
30734
  ]);
30869
30735
  }
30870
30736
  );
30871
- if (dependencies.github) {
30872
- const github = dependencies.github;
30873
- await runGroup(
30874
- dependencies.core,
30875
- "Store Postman UIDs as Repo Variables",
30876
- async () => {
30877
- await persistBootstrapRepositoryVariables(
30878
- github,
30879
- inputs.projectName,
30880
- outputs,
30881
- systemEnvMap,
30882
- environments,
30883
- {
30884
- lintErrors: lintSummary.errors,
30885
- lintWarnings: lintSummary.warnings
30886
- },
30887
- dependencies.core
30888
- );
30889
- }
30890
- );
30737
+ const linkedCollectionIds = [
30738
+ outputs["baseline-collection-id"],
30739
+ outputs["smoke-collection-id"],
30740
+ outputs["contract-collection-id"]
30741
+ ].filter(Boolean);
30742
+ if (linkedCollectionIds.length > 0) {
30743
+ if (dependencies.internalIntegration) {
30744
+ await runGroup(
30745
+ dependencies.core,
30746
+ "Link Collections to Specification",
30747
+ async () => {
30748
+ await dependencies.internalIntegration?.linkCollectionsToSpecification(
30749
+ outputs["spec-id"],
30750
+ linkedCollectionIds.map((collectionId) => ({
30751
+ collectionId,
30752
+ syncOptions: {
30753
+ syncExamples: inputs.syncExamples
30754
+ }
30755
+ }))
30756
+ );
30757
+ }
30758
+ );
30759
+ await runGroup(
30760
+ dependencies.core,
30761
+ "Sync Linked Collections",
30762
+ async () => {
30763
+ await Promise.all(
30764
+ linkedCollectionIds.map(
30765
+ (collectionId) => dependencies.internalIntegration.syncCollection(
30766
+ outputs["spec-id"],
30767
+ collectionId
30768
+ )
30769
+ )
30770
+ );
30771
+ }
30772
+ );
30773
+ } else {
30774
+ dependencies.core.warning(
30775
+ "Skipping cloud spec-to-collection linking and sync because postman-access-token is not configured"
30776
+ );
30777
+ }
30891
30778
  }
30892
30779
  for (const [name, value] of Object.entries(outputs)) {
30893
30780
  dependencies.core.setOutput(name, value);
@@ -30902,9 +30789,6 @@ async function runAction(actionCore = core, actionExec = exec, actionIo = io) {
30902
30789
  io: actionIo,
30903
30790
  specFetcher: fetch
30904
30791
  });
30905
- if (!dependencies.github) {
30906
- actionCore.info("GitHub repository variable persistence disabled for this run");
30907
- }
30908
30792
  if (inputs.domain && !dependencies.internalIntegration) {
30909
30793
  actionCore.warning(
30910
30794
  "Skipping governance assignment because postman-access-token is not configured"
@@ -30915,22 +30799,12 @@ async function runAction(actionCore = core, actionExec = exec, actionIo = io) {
30915
30799
  function createBootstrapDependencies(inputs, factories) {
30916
30800
  const secretMasker = createSecretMasker([
30917
30801
  inputs.postmanApiKey,
30918
- inputs.postmanAccessToken,
30919
- inputs.githubToken,
30920
- inputs.ghFallbackToken
30802
+ inputs.postmanAccessToken
30921
30803
  ]);
30922
30804
  const postman = new PostmanAssetsClient({
30923
30805
  apiKey: inputs.postmanApiKey,
30924
30806
  secretMasker
30925
30807
  });
30926
- const repository = extractRepositorySlug(inputs.repoUrl);
30927
- const github = inputs.githubToken && inputs.repoUrl && repository ? new GitHubApiClient({
30928
- authMode: inputs.githubAuthMode,
30929
- fallbackToken: inputs.ghFallbackToken,
30930
- repository,
30931
- secretMasker,
30932
- token: inputs.githubToken
30933
- }) : void 0;
30934
30808
  const internalIntegration = inputs.postmanAccessToken ? createInternalIntegrationAdapter({
30935
30809
  accessToken: inputs.postmanAccessToken,
30936
30810
  backend: inputs.integrationBackend,
@@ -30940,30 +30814,12 @@ function createBootstrapDependencies(inputs, factories) {
30940
30814
  return {
30941
30815
  core: factories.core,
30942
30816
  exec: factories.exec,
30943
- github,
30944
30817
  io: factories.io,
30945
30818
  internalIntegration,
30946
30819
  postman,
30947
30820
  specFetcher: factories.specFetcher ?? fetch
30948
30821
  };
30949
30822
  }
30950
- function extractRepositorySlug(repoUrl) {
30951
- const normalized = normalizeInputValue(repoUrl);
30952
- if (!normalized) {
30953
- return void 0;
30954
- }
30955
- try {
30956
- const parsed = new URL(normalized);
30957
- const pathname = parsed.pathname.replace(/^\/+|\/+$/g, "").replace(/\.git$/, "");
30958
- const segments = pathname.split("/").filter(Boolean);
30959
- if (segments.length >= 2) {
30960
- return `${segments[0]}/${segments[1]}`;
30961
- }
30962
- return void 0;
30963
- } catch {
30964
- return void 0;
30965
- }
30966
- }
30967
30823
  var currentModulePath = typeof __filename === "string" ? __filename : "";
30968
30824
  var entrypoint = process.argv[1];
30969
30825
  if (entrypoint && currentModulePath === entrypoint) {
@@ -30979,7 +30835,6 @@ if (entrypoint && currentModulePath === entrypoint) {
30979
30835
  0 && (module.exports = {
30980
30836
  createBootstrapDependencies,
30981
30837
  createPlannedOutputs,
30982
- extractRepositorySlug,
30983
30838
  getInput,
30984
30839
  lintSpecViaCli,
30985
30840
  normalizeSpecDocument,