@postman-cse/onboarding-insights 1.0.4 → 2.1.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/README.md +33 -86
- package/action.yml +4 -4
- package/dist/action.cjs +483 -82
- package/dist/cli.cjs +584 -183
- package/dist/index.cjs +487 -82
- package/package.json +8 -7
package/dist/cli.cjs
CHANGED
|
@@ -12167,7 +12167,7 @@ var require_response = __commonJS({
|
|
|
12167
12167
|
var assert = require("node:assert");
|
|
12168
12168
|
var { types } = require("node:util");
|
|
12169
12169
|
var textEncoder = new TextEncoder("utf-8");
|
|
12170
|
-
var
|
|
12170
|
+
var Response2 = class _Response {
|
|
12171
12171
|
// Creates network error Response.
|
|
12172
12172
|
static error() {
|
|
12173
12173
|
const responseObject = fromInnerResponse(makeNetworkError(), "immutable");
|
|
@@ -12310,8 +12310,8 @@ var require_response = __commonJS({
|
|
|
12310
12310
|
return `Response ${nodeUtil.formatWithOptions(options, properties)}`;
|
|
12311
12311
|
}
|
|
12312
12312
|
};
|
|
12313
|
-
mixinBody(
|
|
12314
|
-
Object.defineProperties(
|
|
12313
|
+
mixinBody(Response2);
|
|
12314
|
+
Object.defineProperties(Response2.prototype, {
|
|
12315
12315
|
type: kEnumerableProperty,
|
|
12316
12316
|
url: kEnumerableProperty,
|
|
12317
12317
|
status: kEnumerableProperty,
|
|
@@ -12327,7 +12327,7 @@ var require_response = __commonJS({
|
|
|
12327
12327
|
configurable: true
|
|
12328
12328
|
}
|
|
12329
12329
|
});
|
|
12330
|
-
Object.defineProperties(
|
|
12330
|
+
Object.defineProperties(Response2, {
|
|
12331
12331
|
json: kEnumerableProperty,
|
|
12332
12332
|
redirect: kEnumerableProperty,
|
|
12333
12333
|
error: kEnumerableProperty
|
|
@@ -12460,7 +12460,7 @@ var require_response = __commonJS({
|
|
|
12460
12460
|
}
|
|
12461
12461
|
}
|
|
12462
12462
|
function fromInnerResponse(innerResponse, guard) {
|
|
12463
|
-
const response = new
|
|
12463
|
+
const response = new Response2(kConstruct);
|
|
12464
12464
|
response[kState] = innerResponse;
|
|
12465
12465
|
response[kHeaders] = new Headers3(kConstruct);
|
|
12466
12466
|
setHeadersList(response[kHeaders], innerResponse.headersList);
|
|
@@ -12528,7 +12528,7 @@ var require_response = __commonJS({
|
|
|
12528
12528
|
makeResponse,
|
|
12529
12529
|
makeAppropriateNetworkError,
|
|
12530
12530
|
filterResponse,
|
|
12531
|
-
Response,
|
|
12531
|
+
Response: Response2,
|
|
12532
12532
|
cloneResponse,
|
|
12533
12533
|
fromInnerResponse
|
|
12534
12534
|
};
|
|
@@ -15200,7 +15200,7 @@ var require_cache = __commonJS({
|
|
|
15200
15200
|
var { urlEquals, getFieldValues } = require_util5();
|
|
15201
15201
|
var { kEnumerableProperty, isDisturbed } = require_util();
|
|
15202
15202
|
var { webidl } = require_webidl();
|
|
15203
|
-
var { Response, cloneResponse, fromInnerResponse } = require_response();
|
|
15203
|
+
var { Response: Response2, cloneResponse, fromInnerResponse } = require_response();
|
|
15204
15204
|
var { Request, fromInnerRequest } = require_request2();
|
|
15205
15205
|
var { kState } = require_symbols2();
|
|
15206
15206
|
var { fetching } = require_fetch();
|
|
@@ -15727,7 +15727,7 @@ var require_cache = __commonJS({
|
|
|
15727
15727
|
converter: webidl.converters.DOMString
|
|
15728
15728
|
}
|
|
15729
15729
|
]);
|
|
15730
|
-
webidl.converters.Response = webidl.interfaceConverter(
|
|
15730
|
+
webidl.converters.Response = webidl.interfaceConverter(Response2);
|
|
15731
15731
|
webidl.converters["sequence<RequestInfo>"] = webidl.sequenceConverter(
|
|
15732
15732
|
webidl.converters.RequestInfo
|
|
15733
15733
|
);
|
|
@@ -18685,9 +18685,259 @@ __export(cli_exports, {
|
|
|
18685
18685
|
toDotenv: () => toDotenv
|
|
18686
18686
|
});
|
|
18687
18687
|
module.exports = __toCommonJS(cli_exports);
|
|
18688
|
-
var
|
|
18688
|
+
var import_node_fs2 = require("node:fs");
|
|
18689
18689
|
var import_promises = require("node:fs/promises");
|
|
18690
|
-
var
|
|
18690
|
+
var import_node_path2 = __toESM(require("node:path"), 1);
|
|
18691
|
+
|
|
18692
|
+
// src/lib/retry.ts
|
|
18693
|
+
function sleep(delayMs) {
|
|
18694
|
+
return new Promise((resolve2) => {
|
|
18695
|
+
setTimeout(resolve2, delayMs);
|
|
18696
|
+
});
|
|
18697
|
+
}
|
|
18698
|
+
function normalizeRetryOptions(options) {
|
|
18699
|
+
return {
|
|
18700
|
+
maxAttempts: Math.max(1, options.maxAttempts ?? 3),
|
|
18701
|
+
delayMs: Math.max(0, options.delayMs ?? 2e3),
|
|
18702
|
+
backoffMultiplier: Math.max(1, options.backoffMultiplier ?? 1),
|
|
18703
|
+
maxDelayMs: options.maxDelayMs === void 0 ? Number.POSITIVE_INFINITY : Math.max(0, options.maxDelayMs),
|
|
18704
|
+
onRetry: options.onRetry ?? (async () => void 0),
|
|
18705
|
+
shouldRetry: options.shouldRetry ?? (() => true),
|
|
18706
|
+
sleep: options.sleep ?? sleep
|
|
18707
|
+
};
|
|
18708
|
+
}
|
|
18709
|
+
async function retry(operation, options = {}) {
|
|
18710
|
+
const normalized = normalizeRetryOptions(options);
|
|
18711
|
+
let nextDelayMs = normalized.delayMs;
|
|
18712
|
+
for (let attempt = 1; attempt <= normalized.maxAttempts; attempt += 1) {
|
|
18713
|
+
try {
|
|
18714
|
+
return await operation();
|
|
18715
|
+
} catch (error2) {
|
|
18716
|
+
const shouldRetry = attempt < normalized.maxAttempts && normalized.shouldRetry(error2, {
|
|
18717
|
+
attempt,
|
|
18718
|
+
maxAttempts: normalized.maxAttempts
|
|
18719
|
+
});
|
|
18720
|
+
if (!shouldRetry) {
|
|
18721
|
+
throw error2;
|
|
18722
|
+
}
|
|
18723
|
+
await normalized.onRetry({
|
|
18724
|
+
attempt,
|
|
18725
|
+
maxAttempts: normalized.maxAttempts,
|
|
18726
|
+
delayMs: nextDelayMs,
|
|
18727
|
+
error: error2
|
|
18728
|
+
});
|
|
18729
|
+
await normalized.sleep(nextDelayMs);
|
|
18730
|
+
nextDelayMs = Math.min(
|
|
18731
|
+
normalized.maxDelayMs,
|
|
18732
|
+
Math.round(nextDelayMs * normalized.backoffMultiplier)
|
|
18733
|
+
);
|
|
18734
|
+
}
|
|
18735
|
+
}
|
|
18736
|
+
throw new Error("Retry exhausted without returning or throwing");
|
|
18737
|
+
}
|
|
18738
|
+
|
|
18739
|
+
// src/lib/postman/base-urls.ts
|
|
18740
|
+
var POSTMAN_ENDPOINT_PROFILES = {
|
|
18741
|
+
prod: {
|
|
18742
|
+
apiBaseUrl: "https://api.getpostman.com",
|
|
18743
|
+
bifrostBaseUrl: "https://bifrost-premium-https-v4.gw.postman.com",
|
|
18744
|
+
iapubBaseUrl: "https://iapub.postman.co",
|
|
18745
|
+
observabilityBaseUrl: "https://api.observability.postman.com",
|
|
18746
|
+
observabilityEnv: "production"
|
|
18747
|
+
},
|
|
18748
|
+
beta: {
|
|
18749
|
+
apiBaseUrl: "https://api.getpostman-beta.com",
|
|
18750
|
+
bifrostBaseUrl: "https://bifrost-https-v4.gw.postman-beta.com",
|
|
18751
|
+
iapubBaseUrl: "https://iapub.postman.co",
|
|
18752
|
+
observabilityBaseUrl: "https://api.observability.postman-beta.com",
|
|
18753
|
+
observabilityEnv: "beta"
|
|
18754
|
+
}
|
|
18755
|
+
};
|
|
18756
|
+
function parsePostmanRegion(value) {
|
|
18757
|
+
const normalized = String(value || "us").trim().toLowerCase();
|
|
18758
|
+
if (normalized === "us" || normalized === "eu") {
|
|
18759
|
+
return normalized;
|
|
18760
|
+
}
|
|
18761
|
+
throw new Error(`Unsupported postman-region "${value}". Supported values: us, eu`);
|
|
18762
|
+
}
|
|
18763
|
+
function parsePostmanStack(value) {
|
|
18764
|
+
const normalized = String(value || "prod").trim().toLowerCase();
|
|
18765
|
+
if (normalized === "prod" || normalized === "beta") {
|
|
18766
|
+
return normalized;
|
|
18767
|
+
}
|
|
18768
|
+
throw new Error(`Unsupported postman-stack "${value}". Supported values: prod, beta`);
|
|
18769
|
+
}
|
|
18770
|
+
function resolvePostmanEndpointProfile(stack, region = "us") {
|
|
18771
|
+
if (stack === "beta" && region !== "us") {
|
|
18772
|
+
throw new Error("postman-region=eu is only supported with postman-stack=prod");
|
|
18773
|
+
}
|
|
18774
|
+
const profile = POSTMAN_ENDPOINT_PROFILES[stack];
|
|
18775
|
+
if (region === "eu") {
|
|
18776
|
+
return {
|
|
18777
|
+
...profile,
|
|
18778
|
+
apiBaseUrl: "https://api.eu.postman.com"
|
|
18779
|
+
};
|
|
18780
|
+
}
|
|
18781
|
+
return profile;
|
|
18782
|
+
}
|
|
18783
|
+
|
|
18784
|
+
// src/lib/postman/token-provider.ts
|
|
18785
|
+
var MintError = class extends Error {
|
|
18786
|
+
permanent;
|
|
18787
|
+
constructor(message, permanent) {
|
|
18788
|
+
super(message);
|
|
18789
|
+
this.name = "MintError";
|
|
18790
|
+
this.permanent = permanent;
|
|
18791
|
+
}
|
|
18792
|
+
};
|
|
18793
|
+
function extractAccessToken(payload) {
|
|
18794
|
+
if (!payload || typeof payload !== "object") return void 0;
|
|
18795
|
+
const record = payload;
|
|
18796
|
+
const direct = record.access_token;
|
|
18797
|
+
if (typeof direct === "string" && direct.trim()) return direct.trim();
|
|
18798
|
+
const session = record.session;
|
|
18799
|
+
if (session && typeof session === "object") {
|
|
18800
|
+
const token = session.token;
|
|
18801
|
+
if (typeof token === "string" && token.trim()) return token.trim();
|
|
18802
|
+
}
|
|
18803
|
+
return void 0;
|
|
18804
|
+
}
|
|
18805
|
+
var AccessTokenProvider = class {
|
|
18806
|
+
token;
|
|
18807
|
+
apiKey;
|
|
18808
|
+
apiBaseUrl;
|
|
18809
|
+
fetchImpl;
|
|
18810
|
+
maxAttempts;
|
|
18811
|
+
onToken;
|
|
18812
|
+
sleep;
|
|
18813
|
+
inflight;
|
|
18814
|
+
constructor(options) {
|
|
18815
|
+
this.token = String(options.accessToken || "").trim();
|
|
18816
|
+
this.apiKey = String(options.apiKey || "").trim();
|
|
18817
|
+
this.apiBaseUrl = String(
|
|
18818
|
+
options.apiBaseUrl || POSTMAN_ENDPOINT_PROFILES.prod.apiBaseUrl
|
|
18819
|
+
).replace(/\/+$/, "");
|
|
18820
|
+
this.fetchImpl = options.fetchImpl ?? fetch;
|
|
18821
|
+
this.maxAttempts = Math.max(1, options.maxAttempts ?? 2);
|
|
18822
|
+
this.onToken = options.onToken;
|
|
18823
|
+
this.sleep = options.sleep;
|
|
18824
|
+
}
|
|
18825
|
+
current() {
|
|
18826
|
+
return this.token;
|
|
18827
|
+
}
|
|
18828
|
+
/** True when a PMAK is present, so an expired token can be re-minted. */
|
|
18829
|
+
canRefresh() {
|
|
18830
|
+
return Boolean(this.apiKey);
|
|
18831
|
+
}
|
|
18832
|
+
refresh() {
|
|
18833
|
+
this.inflight ??= this.mintWithRetry().finally(() => {
|
|
18834
|
+
this.inflight = void 0;
|
|
18835
|
+
});
|
|
18836
|
+
return this.inflight;
|
|
18837
|
+
}
|
|
18838
|
+
async mintWithRetry() {
|
|
18839
|
+
if (!this.apiKey) {
|
|
18840
|
+
throw new Error(
|
|
18841
|
+
"postman: the access token expired and cannot be refreshed because no postman-api-key is present. Service-account access tokens expire after about 1 to 1.5 hours. Re-mint a fresh token (postman-resolve-service-token-action) and re-run."
|
|
18842
|
+
);
|
|
18843
|
+
}
|
|
18844
|
+
const token = await retry(() => this.mintOnce(), {
|
|
18845
|
+
maxAttempts: this.maxAttempts,
|
|
18846
|
+
delayMs: 1e3,
|
|
18847
|
+
backoffMultiplier: 2,
|
|
18848
|
+
...this.sleep ? { sleep: this.sleep } : {},
|
|
18849
|
+
shouldRetry: (error2) => !(error2 instanceof MintError && error2.permanent)
|
|
18850
|
+
});
|
|
18851
|
+
this.token = token;
|
|
18852
|
+
this.onToken?.(token);
|
|
18853
|
+
return token;
|
|
18854
|
+
}
|
|
18855
|
+
async mintOnce() {
|
|
18856
|
+
const response = await this.fetchImpl(`${this.apiBaseUrl}/service-account-tokens`, {
|
|
18857
|
+
method: "POST",
|
|
18858
|
+
headers: {
|
|
18859
|
+
"Content-Type": "application/json",
|
|
18860
|
+
"x-api-key": this.apiKey
|
|
18861
|
+
},
|
|
18862
|
+
body: JSON.stringify({ apiKey: this.apiKey })
|
|
18863
|
+
});
|
|
18864
|
+
const body = await response.text().catch(() => "");
|
|
18865
|
+
if (!response.ok) {
|
|
18866
|
+
const status = response.status;
|
|
18867
|
+
if (status === 401 || status === 403) {
|
|
18868
|
+
throw new MintError(
|
|
18869
|
+
`postman: re-mint failed because the postman-api-key was rejected (PMAK rejected, HTTP ${status}); confirm it is a valid, enabled service-account PMAK for the intended team.`,
|
|
18870
|
+
true
|
|
18871
|
+
);
|
|
18872
|
+
}
|
|
18873
|
+
if (status === 400 && body.toLowerCase().includes("service accounts not enabled")) {
|
|
18874
|
+
throw new MintError(
|
|
18875
|
+
"postman: re-mint failed because service accounts are not enabled for this team; enable them in Team Settings or use a team where they are.",
|
|
18876
|
+
true
|
|
18877
|
+
);
|
|
18878
|
+
}
|
|
18879
|
+
throw new MintError(`postman: re-mint failed (service-account-tokens HTTP ${status}).`, false);
|
|
18880
|
+
}
|
|
18881
|
+
let parsed;
|
|
18882
|
+
try {
|
|
18883
|
+
parsed = JSON.parse(body);
|
|
18884
|
+
} catch {
|
|
18885
|
+
parsed = void 0;
|
|
18886
|
+
}
|
|
18887
|
+
const token = extractAccessToken(parsed);
|
|
18888
|
+
if (!token) {
|
|
18889
|
+
throw new MintError("postman: re-mint succeeded but no access token was returned.", false);
|
|
18890
|
+
}
|
|
18891
|
+
return token;
|
|
18892
|
+
}
|
|
18893
|
+
};
|
|
18894
|
+
async function describeMintFailure(mintError, apiKey, apiBaseUrl, fetchImpl) {
|
|
18895
|
+
const raw = mintError instanceof Error ? mintError.message : String(mintError);
|
|
18896
|
+
const rejected = /HTTP 40[13]|PMAK rejected/.test(raw);
|
|
18897
|
+
if (!rejected) {
|
|
18898
|
+
return raw;
|
|
18899
|
+
}
|
|
18900
|
+
try {
|
|
18901
|
+
const me = await fetchImpl(`${apiBaseUrl}/me`, { headers: { "x-api-key": apiKey } });
|
|
18902
|
+
if (me.ok) {
|
|
18903
|
+
const body = await me.json().catch(() => void 0);
|
|
18904
|
+
const user = body?.user;
|
|
18905
|
+
const looksPersonal = Boolean(user && (user.username || user.email));
|
|
18906
|
+
if (looksPersonal) {
|
|
18907
|
+
return "Personal API key detected, cannot mint a service-account access token. POST /service-account-tokens only accepts a SERVICE-ACCOUNT API key; this postman-api-key belongs to a user account" + (user?.teamId ? ` (team ${user.teamId})` : "") + ". Create a service account in Team Settings and use its PMAK, or mint the token elsewhere and pass postman-access-token.";
|
|
18908
|
+
}
|
|
18909
|
+
return "The postman-api-key authenticates (GET /me OK) but was rejected by POST /service-account-tokens" + (user?.teamId ? ` (team ${user.teamId})` : "") + ". The service account likely lacks permission to mint access tokens, or service accounts are restricted for this team. Check the service account role in Team Settings, or pass a pre-minted postman-access-token.";
|
|
18910
|
+
}
|
|
18911
|
+
return "The postman-api-key is invalid, disabled, or expired (rejected by both POST /service-account-tokens and GET /me). Generate a fresh service-account PMAK in Team Settings and update the secret.";
|
|
18912
|
+
} catch {
|
|
18913
|
+
return raw;
|
|
18914
|
+
}
|
|
18915
|
+
}
|
|
18916
|
+
async function mintAccessTokenIfNeeded(inputs, log, setSecret2, fetchImpl = fetch) {
|
|
18917
|
+
if (inputs.postmanAccessToken || !inputs.postmanApiKey) {
|
|
18918
|
+
return;
|
|
18919
|
+
}
|
|
18920
|
+
const apiBaseUrl = String(
|
|
18921
|
+
inputs.postmanApiBase || POSTMAN_ENDPOINT_PROFILES.prod.apiBaseUrl
|
|
18922
|
+
).replace(/\/+$/, "");
|
|
18923
|
+
const provider = new AccessTokenProvider({
|
|
18924
|
+
apiKey: inputs.postmanApiKey,
|
|
18925
|
+
apiBaseUrl,
|
|
18926
|
+
fetchImpl,
|
|
18927
|
+
onToken: (token) => setSecret2?.(token)
|
|
18928
|
+
});
|
|
18929
|
+
try {
|
|
18930
|
+
inputs.postmanAccessToken = await provider.refresh();
|
|
18931
|
+
log.info(
|
|
18932
|
+
"postman: no postman-access-token configured - minted a short-lived service-account access token from the postman-api-key."
|
|
18933
|
+
);
|
|
18934
|
+
} catch (error2) {
|
|
18935
|
+
const diagnosis = await describeMintFailure(error2, inputs.postmanApiKey, apiBaseUrl, fetchImpl);
|
|
18936
|
+
log.warning(
|
|
18937
|
+
"postman: could not mint an access token from the postman-api-key. " + diagnosis + " Continuing without an access token - access-token-only functionality will be unavailable unless postman-access-token is provided."
|
|
18938
|
+
);
|
|
18939
|
+
}
|
|
18940
|
+
}
|
|
18691
18941
|
|
|
18692
18942
|
// node_modules/@actions/core/lib/core.js
|
|
18693
18943
|
var core_exports = {};
|
|
@@ -20964,12 +21214,62 @@ function getIDToken(aud) {
|
|
|
20964
21214
|
|
|
20965
21215
|
// src/lib/credential-identity.ts
|
|
20966
21216
|
var sessionPath = "/api/sessions/current";
|
|
21217
|
+
var SESSION_MAX_ATTEMPTS = 3;
|
|
21218
|
+
var SESSION_RETRY_BASE_DELAY_MS = 500;
|
|
21219
|
+
var SESSION_RETRY_MAX_DELAY_MS = 8e3;
|
|
20967
21220
|
var pmakMemo = /* @__PURE__ */ new Map();
|
|
20968
21221
|
var sessionMemo = /* @__PURE__ */ new Map();
|
|
20969
21222
|
var memoizedSessionIdentity;
|
|
21223
|
+
var memoizedSessionFailure;
|
|
21224
|
+
function defaultSessionSleep(ms) {
|
|
21225
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
21226
|
+
}
|
|
21227
|
+
function defaultRandom() {
|
|
21228
|
+
return Math.random();
|
|
21229
|
+
}
|
|
21230
|
+
function parseRetryAfterMs(value) {
|
|
21231
|
+
const trimmed = value?.trim();
|
|
21232
|
+
if (!trimmed) {
|
|
21233
|
+
return void 0;
|
|
21234
|
+
}
|
|
21235
|
+
if (/^\d+$/.test(trimmed)) {
|
|
21236
|
+
return Number(trimmed) * 1e3;
|
|
21237
|
+
}
|
|
21238
|
+
const dateMs = Date.parse(trimmed);
|
|
21239
|
+
if (!Number.isNaN(dateMs)) {
|
|
21240
|
+
return Math.max(0, dateMs - Date.now());
|
|
21241
|
+
}
|
|
21242
|
+
return void 0;
|
|
21243
|
+
}
|
|
21244
|
+
function parseRateLimitResetMs(value) {
|
|
21245
|
+
const trimmed = value?.trim();
|
|
21246
|
+
if (!trimmed || !/^\d+$/.test(trimmed)) {
|
|
21247
|
+
return void 0;
|
|
21248
|
+
}
|
|
21249
|
+
const seconds = Number(trimmed);
|
|
21250
|
+
const nowSeconds = Date.now() / 1e3;
|
|
21251
|
+
if (seconds > nowSeconds) {
|
|
21252
|
+
return Math.max(0, (seconds - nowSeconds) * 1e3);
|
|
21253
|
+
}
|
|
21254
|
+
return seconds * 1e3;
|
|
21255
|
+
}
|
|
21256
|
+
function computeSessionRetryDelayMs(response, attempt, random) {
|
|
21257
|
+
const headers = response?.headers;
|
|
21258
|
+
const signal = parseRetryAfterMs(headers?.get("retry-after") ?? null) ?? parseRateLimitResetMs(
|
|
21259
|
+
headers?.get("ratelimit-reset") ?? headers?.get("x-ratelimit-reset") ?? null
|
|
21260
|
+
);
|
|
21261
|
+
if (signal !== void 0) {
|
|
21262
|
+
return Math.min(Math.max(0, signal), SESSION_RETRY_MAX_DELAY_MS);
|
|
21263
|
+
}
|
|
21264
|
+
const ceiling = Math.min(SESSION_RETRY_MAX_DELAY_MS, SESSION_RETRY_BASE_DELAY_MS * 2 ** (attempt - 1));
|
|
21265
|
+
return Math.round(random() * ceiling);
|
|
21266
|
+
}
|
|
20970
21267
|
function getMemoizedSessionIdentity() {
|
|
20971
21268
|
return memoizedSessionIdentity;
|
|
20972
21269
|
}
|
|
21270
|
+
function getSessionResolutionFailure() {
|
|
21271
|
+
return memoizedSessionFailure;
|
|
21272
|
+
}
|
|
20973
21273
|
function asRecord(value) {
|
|
20974
21274
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
20975
21275
|
return void 0;
|
|
@@ -21038,46 +21338,90 @@ async function resolveSessionIdentity(opts) {
|
|
|
21038
21338
|
const memoKey = `${baseUrl}::${accessToken}`;
|
|
21039
21339
|
let pending = sessionMemo.get(memoKey);
|
|
21040
21340
|
if (!pending) {
|
|
21041
|
-
pending = probeSessionIdentity(
|
|
21341
|
+
pending = probeSessionIdentity(
|
|
21342
|
+
baseUrl,
|
|
21343
|
+
accessToken,
|
|
21344
|
+
opts.fetchImpl ?? fetch,
|
|
21345
|
+
Math.max(1, opts.maxAttempts ?? SESSION_MAX_ATTEMPTS),
|
|
21346
|
+
opts.sleepImpl ?? defaultSessionSleep,
|
|
21347
|
+
opts.randomImpl ?? defaultRandom
|
|
21348
|
+
);
|
|
21042
21349
|
sessionMemo.set(memoKey, pending);
|
|
21043
21350
|
}
|
|
21044
21351
|
return pending;
|
|
21045
21352
|
}
|
|
21046
|
-
async function
|
|
21353
|
+
async function parseSessionResponse(response) {
|
|
21354
|
+
let payload;
|
|
21047
21355
|
try {
|
|
21048
|
-
|
|
21049
|
-
method: "GET",
|
|
21050
|
-
headers: { "x-access-token": accessToken }
|
|
21051
|
-
});
|
|
21052
|
-
if (!response.ok) {
|
|
21053
|
-
return void 0;
|
|
21054
|
-
}
|
|
21055
|
-
const payload = asRecord(await response.json());
|
|
21056
|
-
if (!payload) {
|
|
21057
|
-
return void 0;
|
|
21058
|
-
}
|
|
21059
|
-
const root = asRecord(payload.session) ?? payload;
|
|
21060
|
-
const identity = asRecord(root.identity);
|
|
21061
|
-
const data = asRecord(root.data);
|
|
21062
|
-
const user = asRecord(data?.user);
|
|
21063
|
-
const roleEntries = Array.isArray(user?.roles) ? user.roles.map((entry) => coerceText(entry) ?? coerceId(entry)).filter((entry) => Boolean(entry)) : [];
|
|
21064
|
-
const singleRole = coerceText(user?.role);
|
|
21065
|
-
const roles = roleEntries.length > 0 ? roleEntries : singleRole ? [singleRole] : void 0;
|
|
21066
|
-
const resolved = {
|
|
21067
|
-
source: "iapub/sessions",
|
|
21068
|
-
userId: coerceId(identity?.user) ?? coerceId(user?.id),
|
|
21069
|
-
fullName: coerceText(user?.fullName) ?? coerceText(user?.name) ?? coerceText(user?.username),
|
|
21070
|
-
teamId: coerceId(identity?.team),
|
|
21071
|
-
teamName: coerceText(user?.teamName),
|
|
21072
|
-
teamDomain: coerceText(identity?.domain),
|
|
21073
|
-
...roles ? { roles } : {},
|
|
21074
|
-
consumerType: coerceText(root.consumerType) ?? coerceText(data?.consumerType) ?? coerceText(user?.consumerType)
|
|
21075
|
-
};
|
|
21076
|
-
memoizedSessionIdentity = resolved;
|
|
21077
|
-
return resolved;
|
|
21356
|
+
payload = asRecord(await response.json());
|
|
21078
21357
|
} catch {
|
|
21079
21358
|
return void 0;
|
|
21080
21359
|
}
|
|
21360
|
+
if (!payload) {
|
|
21361
|
+
return void 0;
|
|
21362
|
+
}
|
|
21363
|
+
const root = asRecord(payload.session) ?? payload;
|
|
21364
|
+
const identity = asRecord(root.identity);
|
|
21365
|
+
const data = asRecord(root.data);
|
|
21366
|
+
const user = asRecord(data?.user);
|
|
21367
|
+
const roleEntries = Array.isArray(user?.roles) ? user.roles.map((entry) => coerceText(entry) ?? coerceId(entry)).filter((entry) => Boolean(entry)) : [];
|
|
21368
|
+
const singleRole = coerceText(user?.role);
|
|
21369
|
+
const roles = roleEntries.length > 0 ? roleEntries : singleRole ? [singleRole] : void 0;
|
|
21370
|
+
return {
|
|
21371
|
+
source: "iapub/sessions",
|
|
21372
|
+
userId: coerceId(identity?.user) ?? coerceId(user?.id),
|
|
21373
|
+
fullName: coerceText(user?.fullName) ?? coerceText(user?.name) ?? coerceText(user?.username),
|
|
21374
|
+
teamId: coerceId(identity?.team),
|
|
21375
|
+
teamName: coerceText(user?.teamName),
|
|
21376
|
+
teamDomain: coerceText(identity?.domain),
|
|
21377
|
+
...roles ? { roles } : {},
|
|
21378
|
+
consumerType: coerceText(root.consumerType) ?? coerceText(data?.consumerType) ?? coerceText(user?.consumerType)
|
|
21379
|
+
};
|
|
21380
|
+
}
|
|
21381
|
+
async function probeSessionIdentity(baseUrl, accessToken, fetchImpl, maxAttempts, sleepImpl, random) {
|
|
21382
|
+
let failure = "unavailable";
|
|
21383
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
21384
|
+
let response;
|
|
21385
|
+
try {
|
|
21386
|
+
response = await fetchImpl(`${baseUrl}${sessionPath}`, {
|
|
21387
|
+
method: "GET",
|
|
21388
|
+
headers: { "x-access-token": accessToken }
|
|
21389
|
+
});
|
|
21390
|
+
} catch {
|
|
21391
|
+
failure = "unavailable";
|
|
21392
|
+
if (attempt < maxAttempts) {
|
|
21393
|
+
await sleepImpl(computeSessionRetryDelayMs(void 0, attempt, random));
|
|
21394
|
+
continue;
|
|
21395
|
+
}
|
|
21396
|
+
break;
|
|
21397
|
+
}
|
|
21398
|
+
if (response.ok) {
|
|
21399
|
+
const resolved = await parseSessionResponse(response);
|
|
21400
|
+
if (resolved) {
|
|
21401
|
+
memoizedSessionIdentity = resolved;
|
|
21402
|
+
memoizedSessionFailure = void 0;
|
|
21403
|
+
return resolved;
|
|
21404
|
+
}
|
|
21405
|
+
failure = "unavailable";
|
|
21406
|
+
break;
|
|
21407
|
+
}
|
|
21408
|
+
if (response.status === 401 || response.status === 403) {
|
|
21409
|
+
failure = "auth";
|
|
21410
|
+
break;
|
|
21411
|
+
}
|
|
21412
|
+
if (response.status === 429 || response.status >= 500) {
|
|
21413
|
+
failure = "unavailable";
|
|
21414
|
+
if (attempt < maxAttempts) {
|
|
21415
|
+
await sleepImpl(computeSessionRetryDelayMs(response, attempt, random));
|
|
21416
|
+
continue;
|
|
21417
|
+
}
|
|
21418
|
+
break;
|
|
21419
|
+
}
|
|
21420
|
+
failure = "unavailable";
|
|
21421
|
+
break;
|
|
21422
|
+
}
|
|
21423
|
+
memoizedSessionFailure = failure;
|
|
21424
|
+
return void 0;
|
|
21081
21425
|
}
|
|
21082
21426
|
function describeTeam(id) {
|
|
21083
21427
|
const label = id?.teamName ?? id?.teamDomain;
|
|
@@ -21170,7 +21514,9 @@ async function runCredentialPreflight(args) {
|
|
|
21170
21514
|
session = await resolveSessionIdentity({
|
|
21171
21515
|
iapubBaseUrl: args.iapubBaseUrl,
|
|
21172
21516
|
accessToken,
|
|
21173
|
-
fetchImpl: args.fetchImpl
|
|
21517
|
+
fetchImpl: args.fetchImpl,
|
|
21518
|
+
...args.sleepImpl ? { sleepImpl: args.sleepImpl } : {},
|
|
21519
|
+
...args.randomImpl ? { randomImpl: args.randomImpl } : {}
|
|
21174
21520
|
});
|
|
21175
21521
|
} catch (error2) {
|
|
21176
21522
|
args.log.warning(
|
|
@@ -21190,11 +21536,20 @@ async function runCredentialPreflight(args) {
|
|
|
21190
21536
|
);
|
|
21191
21537
|
}
|
|
21192
21538
|
} else {
|
|
21539
|
+
const failure = getSessionResolutionFailure();
|
|
21540
|
+
const detail = failure === "auth" ? "the access token was rejected by iapub (401/403), so it is invalid or expired. Re-mint it with postman-resolve-service-token-action (or POST https://api.getpostman.com/service-account-tokens) and re-run." : "iapub was unreachable after retries (network or 5xx). This is usually transient; re-run the job.";
|
|
21541
|
+
const base = "postman: credential preflight could not resolve the access-token session identity from iapub: " + detail;
|
|
21542
|
+
if (args.mode === "enforce") {
|
|
21543
|
+
throw new Error(
|
|
21544
|
+
mask(
|
|
21545
|
+
`${base} (credential-preflight: enforce requires a resolvable session identity; use credential-preflight: warn to continue with reactive error guidance only.)`
|
|
21546
|
+
)
|
|
21547
|
+
);
|
|
21548
|
+
}
|
|
21193
21549
|
args.log.warning(
|
|
21194
|
-
mask(
|
|
21195
|
-
"postman: credential preflight could not resolve the access-token session identity from iapub; continuing with reactive error guidance only"
|
|
21196
|
-
)
|
|
21550
|
+
mask(`${base} Continuing with reactive error guidance only (credential-preflight: warn).`)
|
|
21197
21551
|
);
|
|
21552
|
+
return;
|
|
21198
21553
|
}
|
|
21199
21554
|
const result = crossCheckIdentities({
|
|
21200
21555
|
pmak,
|
|
@@ -21404,98 +21759,6 @@ var HttpError = class _HttpError extends Error {
|
|
|
21404
21759
|
}
|
|
21405
21760
|
};
|
|
21406
21761
|
|
|
21407
|
-
// src/lib/retry.ts
|
|
21408
|
-
function sleep(delayMs) {
|
|
21409
|
-
return new Promise((resolve2) => {
|
|
21410
|
-
setTimeout(resolve2, delayMs);
|
|
21411
|
-
});
|
|
21412
|
-
}
|
|
21413
|
-
function normalizeRetryOptions(options) {
|
|
21414
|
-
return {
|
|
21415
|
-
maxAttempts: Math.max(1, options.maxAttempts ?? 3),
|
|
21416
|
-
delayMs: Math.max(0, options.delayMs ?? 2e3),
|
|
21417
|
-
backoffMultiplier: Math.max(1, options.backoffMultiplier ?? 1),
|
|
21418
|
-
maxDelayMs: options.maxDelayMs === void 0 ? Number.POSITIVE_INFINITY : Math.max(0, options.maxDelayMs),
|
|
21419
|
-
onRetry: options.onRetry ?? (async () => void 0),
|
|
21420
|
-
shouldRetry: options.shouldRetry ?? (() => true),
|
|
21421
|
-
sleep: options.sleep ?? sleep
|
|
21422
|
-
};
|
|
21423
|
-
}
|
|
21424
|
-
async function retry(operation, options = {}) {
|
|
21425
|
-
const normalized = normalizeRetryOptions(options);
|
|
21426
|
-
let nextDelayMs = normalized.delayMs;
|
|
21427
|
-
for (let attempt = 1; attempt <= normalized.maxAttempts; attempt += 1) {
|
|
21428
|
-
try {
|
|
21429
|
-
return await operation();
|
|
21430
|
-
} catch (error2) {
|
|
21431
|
-
const shouldRetry = attempt < normalized.maxAttempts && normalized.shouldRetry(error2, {
|
|
21432
|
-
attempt,
|
|
21433
|
-
maxAttempts: normalized.maxAttempts
|
|
21434
|
-
});
|
|
21435
|
-
if (!shouldRetry) {
|
|
21436
|
-
throw error2;
|
|
21437
|
-
}
|
|
21438
|
-
await normalized.onRetry({
|
|
21439
|
-
attempt,
|
|
21440
|
-
maxAttempts: normalized.maxAttempts,
|
|
21441
|
-
delayMs: nextDelayMs,
|
|
21442
|
-
error: error2
|
|
21443
|
-
});
|
|
21444
|
-
await normalized.sleep(nextDelayMs);
|
|
21445
|
-
nextDelayMs = Math.min(
|
|
21446
|
-
normalized.maxDelayMs,
|
|
21447
|
-
Math.round(nextDelayMs * normalized.backoffMultiplier)
|
|
21448
|
-
);
|
|
21449
|
-
}
|
|
21450
|
-
}
|
|
21451
|
-
throw new Error("Retry exhausted without returning or throwing");
|
|
21452
|
-
}
|
|
21453
|
-
|
|
21454
|
-
// src/lib/postman/base-urls.ts
|
|
21455
|
-
var POSTMAN_ENDPOINT_PROFILES = {
|
|
21456
|
-
prod: {
|
|
21457
|
-
apiBaseUrl: "https://api.getpostman.com",
|
|
21458
|
-
bifrostBaseUrl: "https://bifrost-premium-https-v4.gw.postman.com",
|
|
21459
|
-
iapubBaseUrl: "https://iapub.postman.co",
|
|
21460
|
-
observabilityBaseUrl: "https://api.observability.postman.com",
|
|
21461
|
-
observabilityEnv: "production"
|
|
21462
|
-
},
|
|
21463
|
-
beta: {
|
|
21464
|
-
apiBaseUrl: "https://api.getpostman-beta.com",
|
|
21465
|
-
bifrostBaseUrl: "https://bifrost-https-v4.gw.postman-beta.com",
|
|
21466
|
-
iapubBaseUrl: "https://iapub.postman.co",
|
|
21467
|
-
observabilityBaseUrl: "https://api.observability.postman-beta.com",
|
|
21468
|
-
observabilityEnv: "beta"
|
|
21469
|
-
}
|
|
21470
|
-
};
|
|
21471
|
-
function parsePostmanRegion(value) {
|
|
21472
|
-
const normalized = String(value || "us").trim().toLowerCase();
|
|
21473
|
-
if (normalized === "us" || normalized === "eu") {
|
|
21474
|
-
return normalized;
|
|
21475
|
-
}
|
|
21476
|
-
throw new Error(`Unsupported postman-region "${value}". Supported values: us, eu`);
|
|
21477
|
-
}
|
|
21478
|
-
function parsePostmanStack(value) {
|
|
21479
|
-
const normalized = String(value || "prod").trim().toLowerCase();
|
|
21480
|
-
if (normalized === "prod" || normalized === "beta") {
|
|
21481
|
-
return normalized;
|
|
21482
|
-
}
|
|
21483
|
-
throw new Error(`Unsupported postman-stack "${value}". Supported values: prod, beta`);
|
|
21484
|
-
}
|
|
21485
|
-
function resolvePostmanEndpointProfile(stack, region = "us") {
|
|
21486
|
-
if (stack === "beta" && region !== "us") {
|
|
21487
|
-
throw new Error("postman-region=eu is only supported with postman-stack=prod");
|
|
21488
|
-
}
|
|
21489
|
-
const profile = POSTMAN_ENDPOINT_PROFILES[stack];
|
|
21490
|
-
if (region === "eu") {
|
|
21491
|
-
return {
|
|
21492
|
-
...profile,
|
|
21493
|
-
apiBaseUrl: "https://api.eu.postman.com"
|
|
21494
|
-
};
|
|
21495
|
-
}
|
|
21496
|
-
return profile;
|
|
21497
|
-
}
|
|
21498
|
-
|
|
21499
21762
|
// src/lib/bifrost-client.ts
|
|
21500
21763
|
var DEFAULT_BIFROST_BASE_URL = POSTMAN_ENDPOINT_PROFILES.prod.bifrostBaseUrl;
|
|
21501
21764
|
var BIFROST_PROXY_PATH = "/ws/proxy";
|
|
@@ -21503,8 +21766,11 @@ var DEFAULT_OBSERVABILITY_BASE_URL = POSTMAN_ENDPOINT_PROFILES.prod.observabilit
|
|
|
21503
21766
|
var DEFAULT_OBSERVABILITY_ENV = POSTMAN_ENDPOINT_PROFILES.prod.observabilityEnv;
|
|
21504
21767
|
var MAX_DISCOVERED_SERVICE_PAGES = 100;
|
|
21505
21768
|
var MAX_PROVIDER_SERVICE_PAGES = 100;
|
|
21769
|
+
function isExpiredAuthError(status, body) {
|
|
21770
|
+
return status === 401 || body.includes("UNAUTHENTICATED") || body.includes("authenticationError");
|
|
21771
|
+
}
|
|
21506
21772
|
var BifrostCatalogClient = class {
|
|
21507
|
-
|
|
21773
|
+
tokenProvider;
|
|
21508
21774
|
teamId;
|
|
21509
21775
|
apiKey;
|
|
21510
21776
|
fetchFn;
|
|
@@ -21513,11 +21779,14 @@ var BifrostCatalogClient = class {
|
|
|
21513
21779
|
observabilityBaseUrl;
|
|
21514
21780
|
observabilityEnv;
|
|
21515
21781
|
constructor(options) {
|
|
21516
|
-
this.
|
|
21782
|
+
this.tokenProvider = options.tokenProvider ?? new AccessTokenProvider({
|
|
21783
|
+
accessToken: options.accessToken,
|
|
21784
|
+
apiKey: options.apiKey
|
|
21785
|
+
});
|
|
21517
21786
|
this.teamId = options.teamId;
|
|
21518
21787
|
this.apiKey = options.apiKey;
|
|
21519
21788
|
this.fetchFn = options.fetchFn ?? globalThis.fetch;
|
|
21520
|
-
this.secretValues = [
|
|
21789
|
+
this.secretValues = [this.tokenProvider.current(), options.apiKey].filter(Boolean);
|
|
21521
21790
|
const base = (options.bifrostBaseUrl || DEFAULT_BIFROST_BASE_URL).replace(/\/+$/, "");
|
|
21522
21791
|
this.bifrostProxyUrl = `${base}${BIFROST_PROXY_PATH}`;
|
|
21523
21792
|
this.observabilityBaseUrl = (options.observabilityBaseUrl || DEFAULT_OBSERVABILITY_BASE_URL).replace(/\/+$/, "");
|
|
@@ -21529,6 +21798,11 @@ var BifrostCatalogClient = class {
|
|
|
21529
21798
|
this.secretValues.push(apiKey);
|
|
21530
21799
|
}
|
|
21531
21800
|
}
|
|
21801
|
+
registerAccessToken(token) {
|
|
21802
|
+
if (token && !this.secretValues.includes(token)) {
|
|
21803
|
+
this.secretValues.push(token);
|
|
21804
|
+
}
|
|
21805
|
+
}
|
|
21532
21806
|
/**
|
|
21533
21807
|
* Build Bifrost proxy headers.
|
|
21534
21808
|
* x-entity-team-id is ONLY included when teamId is present (org-mode tokens).
|
|
@@ -21536,7 +21810,7 @@ var BifrostCatalogClient = class {
|
|
|
21536
21810
|
*/
|
|
21537
21811
|
headers() {
|
|
21538
21812
|
const h = {
|
|
21539
|
-
"x-access-token": this.
|
|
21813
|
+
"x-access-token": this.tokenProvider.current(),
|
|
21540
21814
|
"Content-Type": "application/json"
|
|
21541
21815
|
};
|
|
21542
21816
|
if (this.teamId) {
|
|
@@ -21552,7 +21826,7 @@ var BifrostCatalogClient = class {
|
|
|
21552
21826
|
const session = getMemoizedSessionIdentity();
|
|
21553
21827
|
return {
|
|
21554
21828
|
operation,
|
|
21555
|
-
hasAccessToken: Boolean(this.
|
|
21829
|
+
hasAccessToken: Boolean(this.tokenProvider.current()),
|
|
21556
21830
|
sessionTeamId: session?.teamId,
|
|
21557
21831
|
sessionRoles: session?.roles,
|
|
21558
21832
|
sessionConsumerType: session?.consumerType,
|
|
@@ -21561,7 +21835,7 @@ var BifrostCatalogClient = class {
|
|
|
21561
21835
|
};
|
|
21562
21836
|
}
|
|
21563
21837
|
async proxyRequest(method, path7, body = {}, operation = "api-catalog request") {
|
|
21564
|
-
const
|
|
21838
|
+
const send2 = async () => this.fetchFn(this.bifrostProxyUrl, {
|
|
21565
21839
|
method: "POST",
|
|
21566
21840
|
headers: this.headers(),
|
|
21567
21841
|
body: JSON.stringify({
|
|
@@ -21571,6 +21845,39 @@ var BifrostCatalogClient = class {
|
|
|
21571
21845
|
body
|
|
21572
21846
|
})
|
|
21573
21847
|
});
|
|
21848
|
+
let response = await send2();
|
|
21849
|
+
if (!response.ok) {
|
|
21850
|
+
const bodyText = await response.text().catch(() => "");
|
|
21851
|
+
if (isExpiredAuthError(response.status, bodyText) && this.tokenProvider.canRefresh()) {
|
|
21852
|
+
try {
|
|
21853
|
+
const refreshed = await this.tokenProvider.refresh();
|
|
21854
|
+
this.registerAccessToken(refreshed);
|
|
21855
|
+
response = await send2();
|
|
21856
|
+
} catch {
|
|
21857
|
+
const httpErr = await HttpError.fromResponse(
|
|
21858
|
+
new Response(bodyText, { status: response.status, headers: response.headers }),
|
|
21859
|
+
{
|
|
21860
|
+
method: "POST",
|
|
21861
|
+
url: `bifrost:api-catalog:${method} ${path7}`,
|
|
21862
|
+
secretValues: this.secretValues
|
|
21863
|
+
}
|
|
21864
|
+
);
|
|
21865
|
+
const advised = adviseFromHttpError(httpErr, this.adviceContext(operation));
|
|
21866
|
+
throw advised ?? httpErr;
|
|
21867
|
+
}
|
|
21868
|
+
} else {
|
|
21869
|
+
const httpErr = await HttpError.fromResponse(
|
|
21870
|
+
new Response(bodyText, { status: response.status, headers: response.headers }),
|
|
21871
|
+
{
|
|
21872
|
+
method: "POST",
|
|
21873
|
+
url: `bifrost:api-catalog:${method} ${path7}`,
|
|
21874
|
+
secretValues: this.secretValues
|
|
21875
|
+
}
|
|
21876
|
+
);
|
|
21877
|
+
const advised = adviseFromHttpError(httpErr, this.adviceContext(operation));
|
|
21878
|
+
throw advised ?? httpErr;
|
|
21879
|
+
}
|
|
21880
|
+
}
|
|
21574
21881
|
if (!response.ok) {
|
|
21575
21882
|
const httpErr = await HttpError.fromResponse(response, {
|
|
21576
21883
|
method: "POST",
|
|
@@ -21593,7 +21900,7 @@ var BifrostCatalogClient = class {
|
|
|
21593
21900
|
return data;
|
|
21594
21901
|
}
|
|
21595
21902
|
async akitaProxyRequest(method, path7, body = {}, operation = "Insights request") {
|
|
21596
|
-
const
|
|
21903
|
+
const send2 = async () => this.fetchFn(this.bifrostProxyUrl, {
|
|
21597
21904
|
method: "POST",
|
|
21598
21905
|
headers: this.headers(),
|
|
21599
21906
|
body: JSON.stringify({
|
|
@@ -21603,8 +21910,30 @@ var BifrostCatalogClient = class {
|
|
|
21603
21910
|
body
|
|
21604
21911
|
})
|
|
21605
21912
|
});
|
|
21913
|
+
let response = await send2();
|
|
21606
21914
|
if (!response.ok) {
|
|
21607
21915
|
const text = await response.text().catch(() => "");
|
|
21916
|
+
if (isExpiredAuthError(response.status, text) && this.tokenProvider.canRefresh()) {
|
|
21917
|
+
try {
|
|
21918
|
+
const refreshed = await this.tokenProvider.refresh();
|
|
21919
|
+
this.registerAccessToken(refreshed);
|
|
21920
|
+
response = await send2();
|
|
21921
|
+
if (response.ok) {
|
|
21922
|
+
const data2 = await response.json();
|
|
21923
|
+
return { ok: true, status: response.status, data: data2, errorText: "" };
|
|
21924
|
+
}
|
|
21925
|
+
const retryText = await response.text().catch(() => "");
|
|
21926
|
+
const advised2 = adviseFromBifrostBody(response.status, retryText, this.adviceContext(operation));
|
|
21927
|
+
const errorText2 = advised2 ? retryText ? `${retryText}
|
|
21928
|
+
${advised2.message}` : advised2.message : retryText;
|
|
21929
|
+
return { ok: false, status: response.status, data: null, errorText: errorText2 };
|
|
21930
|
+
} catch {
|
|
21931
|
+
const advised2 = adviseFromBifrostBody(response.status, text, this.adviceContext(operation));
|
|
21932
|
+
const errorText2 = advised2 ? text ? `${text}
|
|
21933
|
+
${advised2.message}` : advised2.message : text;
|
|
21934
|
+
return { ok: false, status: response.status, data: null, errorText: errorText2 };
|
|
21935
|
+
}
|
|
21936
|
+
}
|
|
21608
21937
|
const advised = adviseFromBifrostBody(response.status, text, this.adviceContext(operation));
|
|
21609
21938
|
const errorText = advised ? text ? `${text}
|
|
21610
21939
|
${advised.message}` : advised.message : text;
|
|
@@ -21703,12 +22032,19 @@ ${advised.message}` : advised.message : text;
|
|
|
21703
22032
|
}
|
|
21704
22033
|
page++;
|
|
21705
22034
|
}
|
|
21706
|
-
|
|
21707
|
-
|
|
21708
|
-
|
|
21709
|
-
|
|
21710
|
-
|
|
21711
|
-
|
|
22035
|
+
if (clusterName) {
|
|
22036
|
+
const fullName = `${clusterName}/${projectName}`;
|
|
22037
|
+
const exactMatch = allServices.find((s) => s.name === fullName);
|
|
22038
|
+
return exactMatch?.id || null;
|
|
22039
|
+
}
|
|
22040
|
+
const finalSegmentMatch = allServices.find(
|
|
22041
|
+
(s) => getFinalServiceSegment(s.name) === projectName
|
|
22042
|
+
);
|
|
22043
|
+
if (finalSegmentMatch) return finalSegmentMatch.id;
|
|
22044
|
+
const bracketedMatch = allServices.find(
|
|
22045
|
+
(s) => getFinalServiceSegment(s.name).includes(`[${projectName}]`)
|
|
22046
|
+
);
|
|
22047
|
+
return bracketedMatch?.id || null;
|
|
21712
22048
|
}
|
|
21713
22049
|
async acknowledgeOnboarding(providerServiceId, workspaceId, systemEnvironmentId) {
|
|
21714
22050
|
const result = await this.akitaProxyRequest(
|
|
@@ -21738,6 +22074,14 @@ ${advised.message}` : advised.message : text;
|
|
|
21738
22074
|
throw new Error(`Workspace acknowledge failed: ${result.status} ${result.errorText}`);
|
|
21739
22075
|
}
|
|
21740
22076
|
}
|
|
22077
|
+
// PMAK-only by proven exception. A live probe against the observability
|
|
22078
|
+
// application-binding endpoint (POST /v2/agent/api-catalog/workspaces/:id/
|
|
22079
|
+
// applications) showed x-access-token is rejected identically to the x-api-key
|
|
22080
|
+
// control: both return 401 {"message":"Postman User not found"} for a
|
|
22081
|
+
// service-account credential, because the observability service has no
|
|
22082
|
+
// "Postman User" for a service account. The access token offers no improvement
|
|
22083
|
+
// over the API key here, so this route is not migrated to access-token-primary
|
|
22084
|
+
// (the suite-wide migration explicitly leaves probe-failed routes on PMAK).
|
|
21741
22085
|
async createApplication(workspaceId, systemEnv) {
|
|
21742
22086
|
const response = await this.fetchFn(
|
|
21743
22087
|
`${this.observabilityBaseUrl}/v2/agent/api-catalog/workspaces/${workspaceId}/applications`,
|
|
@@ -21805,7 +22149,17 @@ function findDiscoveredService(services, projectName, clusterName) {
|
|
|
21805
22149
|
const fullName = `${clusterName}/${projectName}`;
|
|
21806
22150
|
return services.find((s) => s.name === fullName);
|
|
21807
22151
|
}
|
|
21808
|
-
|
|
22152
|
+
const finalSegmentMatch = services.find(
|
|
22153
|
+
(service) => getFinalServiceSegment(service.name) === projectName
|
|
22154
|
+
);
|
|
22155
|
+
if (finalSegmentMatch) return finalSegmentMatch;
|
|
22156
|
+
return services.find(
|
|
22157
|
+
(service) => getFinalServiceSegment(service.name).includes(`[${projectName}]`)
|
|
22158
|
+
);
|
|
22159
|
+
}
|
|
22160
|
+
function getFinalServiceSegment(serviceName) {
|
|
22161
|
+
const lastSlash = serviceName.lastIndexOf("/");
|
|
22162
|
+
return lastSlash === -1 ? serviceName : serviceName.slice(lastSlash + 1);
|
|
21809
22163
|
}
|
|
21810
22164
|
|
|
21811
22165
|
// node_modules/@postman-cse/automation-telemetry-core/dist/ci-context.js
|
|
@@ -22062,7 +22416,7 @@ function resolveActionVersion(explicit) {
|
|
|
22062
22416
|
if (explicit) {
|
|
22063
22417
|
return explicit;
|
|
22064
22418
|
}
|
|
22065
|
-
return "
|
|
22419
|
+
return typeof __ACTION_VERSION__ !== "undefined" && __ACTION_VERSION__ ? __ACTION_VERSION__ : "unknown";
|
|
22066
22420
|
}
|
|
22067
22421
|
function telemetryDisabled(env) {
|
|
22068
22422
|
const flag = String(env.POSTMAN_ACTIONS_TELEMETRY ?? "").trim().toLowerCase();
|
|
@@ -22184,6 +22538,18 @@ function createTelemetryContext(options) {
|
|
|
22184
22538
|
};
|
|
22185
22539
|
}
|
|
22186
22540
|
|
|
22541
|
+
// src/action-version.ts
|
|
22542
|
+
var import_node_fs = require("node:fs");
|
|
22543
|
+
var import_node_path = require("node:path");
|
|
22544
|
+
function resolveActionVersion2() {
|
|
22545
|
+
try {
|
|
22546
|
+
const raw = (0, import_node_fs.readFileSync)((0, import_node_path.join)(__dirname, "..", "package.json"), "utf8");
|
|
22547
|
+
return JSON.parse(raw).version ?? "unknown";
|
|
22548
|
+
} catch {
|
|
22549
|
+
return "unknown";
|
|
22550
|
+
}
|
|
22551
|
+
}
|
|
22552
|
+
|
|
22187
22553
|
// src/index.ts
|
|
22188
22554
|
var POLL_TIMEOUT_MIN = 10;
|
|
22189
22555
|
var POLL_TIMEOUT_MAX = 600;
|
|
@@ -22249,8 +22615,12 @@ function resolveInputs(env = process.env) {
|
|
|
22249
22615
|
const projectName = get("project-name");
|
|
22250
22616
|
if (!projectName) throw new Error("project-name is required");
|
|
22251
22617
|
const postmanAccessToken = get("postman-access-token");
|
|
22252
|
-
if (!postmanAccessToken) throw new Error("postman-access-token is required");
|
|
22253
22618
|
const postmanApiKey = get("postman-api-key");
|
|
22619
|
+
if (!postmanAccessToken && !postmanApiKey) {
|
|
22620
|
+
throw new Error(
|
|
22621
|
+
"postman-access-token is required (or provide a service-account postman-api-key so the action can mint one)."
|
|
22622
|
+
);
|
|
22623
|
+
}
|
|
22254
22624
|
const postmanTeamId = get("postman-team-id") || env.POSTMAN_TEAM_ID?.trim() || "";
|
|
22255
22625
|
const workspaceId = get("workspace-id") || env.POSTMAN_WORKSPACE_ID?.trim() || "";
|
|
22256
22626
|
if (!workspaceId) {
|
|
@@ -22441,19 +22811,39 @@ async function resolveApiKeyAndTeamId(inputs, client, reporter = core_exports) {
|
|
|
22441
22811
|
}
|
|
22442
22812
|
return { apiKey, teamId: resolvedTeamId, pmakIdentity };
|
|
22443
22813
|
}
|
|
22444
|
-
async function runCredentialPreflightForInputs(inputs, pmak, reporter, fetchImpl) {
|
|
22814
|
+
async function runCredentialPreflightForInputs(inputs, pmak, reporter, fetchImpl, liveAccessToken) {
|
|
22815
|
+
const accessToken = liveAccessToken ?? inputs.postmanAccessToken;
|
|
22445
22816
|
await runCredentialPreflight({
|
|
22446
22817
|
apiBaseUrl: inputs.postmanApiBase || DEFAULT_POSTMAN_API_BASE,
|
|
22447
22818
|
iapubBaseUrl: inputs.postmanIapubBase || DEFAULT_POSTMAN_IAPUB_BASE,
|
|
22448
22819
|
pmak,
|
|
22449
|
-
postmanAccessToken:
|
|
22820
|
+
postmanAccessToken: accessToken,
|
|
22450
22821
|
explicitTeamId: inputs.postmanTeamId || void 0,
|
|
22451
22822
|
mode: inputs.credentialPreflight,
|
|
22452
|
-
mask: createSecretMasker([inputs.postmanApiKey,
|
|
22823
|
+
mask: createSecretMasker([inputs.postmanApiKey, accessToken]),
|
|
22453
22824
|
log: reporter,
|
|
22454
22825
|
fetchImpl
|
|
22455
22826
|
});
|
|
22456
22827
|
}
|
|
22828
|
+
function createInsightsTokenProvider(inputs, reporter, apiKey = inputs.postmanApiKey) {
|
|
22829
|
+
return new AccessTokenProvider({
|
|
22830
|
+
accessToken: inputs.postmanAccessToken,
|
|
22831
|
+
apiKey,
|
|
22832
|
+
apiBaseUrl: inputs.postmanApiBase || DEFAULT_POSTMAN_API_BASE,
|
|
22833
|
+
onToken: (token) => reporter.setSecret(token)
|
|
22834
|
+
});
|
|
22835
|
+
}
|
|
22836
|
+
function createInsightsBifrostClient(inputs, tokenProvider, teamId, apiKey) {
|
|
22837
|
+
return new BifrostCatalogClient({
|
|
22838
|
+
tokenProvider,
|
|
22839
|
+
accessToken: tokenProvider.current(),
|
|
22840
|
+
teamId,
|
|
22841
|
+
apiKey,
|
|
22842
|
+
bifrostBaseUrl: inputs.postmanBifrostBase,
|
|
22843
|
+
observabilityBaseUrl: inputs.postmanObservabilityBase,
|
|
22844
|
+
observabilityEnv: inputs.postmanObservabilityEnv
|
|
22845
|
+
});
|
|
22846
|
+
}
|
|
22457
22847
|
|
|
22458
22848
|
// src/cli.ts
|
|
22459
22849
|
var ConsoleReporter = class {
|
|
@@ -22534,13 +22924,13 @@ async function writeOptionalFile(filePath, content) {
|
|
|
22534
22924
|
if (!filePath) {
|
|
22535
22925
|
return;
|
|
22536
22926
|
}
|
|
22537
|
-
const workspaceRoot =
|
|
22538
|
-
const resolved =
|
|
22539
|
-
const relative2 =
|
|
22540
|
-
if (relative2.startsWith("..") ||
|
|
22927
|
+
const workspaceRoot = import_node_path2.default.resolve(process.cwd());
|
|
22928
|
+
const resolved = import_node_path2.default.resolve(workspaceRoot, filePath);
|
|
22929
|
+
const relative2 = import_node_path2.default.relative(workspaceRoot, resolved);
|
|
22930
|
+
if (relative2.startsWith("..") || import_node_path2.default.isAbsolute(relative2)) {
|
|
22541
22931
|
throw new Error(`Output path must stay within workspace: ${filePath}`);
|
|
22542
22932
|
}
|
|
22543
|
-
await (0, import_promises.mkdir)(
|
|
22933
|
+
await (0, import_promises.mkdir)(import_node_path2.default.dirname(resolved), { recursive: true });
|
|
22544
22934
|
await (0, import_promises.writeFile)(resolved, content, "utf8");
|
|
22545
22935
|
}
|
|
22546
22936
|
function toOutputs(result) {
|
|
@@ -22558,42 +22948,53 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
|
|
|
22558
22948
|
const config = parseCliArgs(argv, env);
|
|
22559
22949
|
const inputs = resolveInputs(config.inputEnv);
|
|
22560
22950
|
const reporter = new ConsoleReporter();
|
|
22561
|
-
|
|
22951
|
+
const mintHolder = {
|
|
22952
|
+
postmanAccessToken: inputs.postmanAccessToken,
|
|
22953
|
+
postmanApiKey: inputs.postmanApiKey,
|
|
22954
|
+
postmanApiBase: inputs.postmanApiBase
|
|
22955
|
+
};
|
|
22956
|
+
await mintAccessTokenIfNeeded(mintHolder, reporter, (secret) => reporter.setSecret(secret));
|
|
22957
|
+
inputs.postmanAccessToken = mintHolder.postmanAccessToken;
|
|
22958
|
+
if (inputs.postmanAccessToken) reporter.setSecret(inputs.postmanAccessToken);
|
|
22562
22959
|
if (inputs.postmanApiKey) {
|
|
22563
22960
|
reporter.setSecret(inputs.postmanApiKey);
|
|
22564
22961
|
}
|
|
22565
22962
|
if (inputs.githubToken) {
|
|
22566
22963
|
reporter.setSecret(inputs.githubToken);
|
|
22567
22964
|
}
|
|
22568
|
-
const
|
|
22569
|
-
|
|
22570
|
-
|
|
22571
|
-
|
|
22572
|
-
|
|
22573
|
-
|
|
22574
|
-
|
|
22575
|
-
});
|
|
22965
|
+
const tokenProvider = createInsightsTokenProvider(inputs, reporter);
|
|
22966
|
+
const preliminaryClient = createInsightsBifrostClient(
|
|
22967
|
+
inputs,
|
|
22968
|
+
tokenProvider,
|
|
22969
|
+
inputs.postmanTeamId,
|
|
22970
|
+
inputs.postmanApiKey
|
|
22971
|
+
);
|
|
22576
22972
|
const { apiKey, teamId, pmakIdentity } = await resolveApiKeyAndTeamId(
|
|
22577
22973
|
inputs,
|
|
22578
22974
|
preliminaryClient,
|
|
22579
22975
|
reporter
|
|
22580
22976
|
);
|
|
22581
|
-
const
|
|
22977
|
+
const activeTokenProvider = apiKey !== inputs.postmanApiKey ? new AccessTokenProvider({
|
|
22978
|
+
accessToken: tokenProvider.current(),
|
|
22979
|
+
apiKey,
|
|
22980
|
+
apiBaseUrl: inputs.postmanApiBase || DEFAULT_POSTMAN_API_BASE,
|
|
22981
|
+
onToken: (token) => reporter.setSecret(token)
|
|
22982
|
+
}) : tokenProvider;
|
|
22983
|
+
const telemetry = createTelemetryContext({ action: "postman-insights-onboarding-action", actionVersion: resolveActionVersion2(), logger: reporter });
|
|
22582
22984
|
telemetry.setTeamId(inputs.postmanTeamId || pmakIdentity?.teamId);
|
|
22583
22985
|
if (apiKey) {
|
|
22584
22986
|
reporter.setSecret(apiKey);
|
|
22585
22987
|
}
|
|
22586
22988
|
let result;
|
|
22587
22989
|
try {
|
|
22588
|
-
await runCredentialPreflightForInputs(
|
|
22589
|
-
|
|
22590
|
-
|
|
22591
|
-
|
|
22592
|
-
|
|
22593
|
-
|
|
22594
|
-
|
|
22595
|
-
|
|
22596
|
-
});
|
|
22990
|
+
await runCredentialPreflightForInputs(
|
|
22991
|
+
inputs,
|
|
22992
|
+
pmakIdentity,
|
|
22993
|
+
reporter,
|
|
22994
|
+
void 0,
|
|
22995
|
+
activeTokenProvider.current()
|
|
22996
|
+
);
|
|
22997
|
+
const client = createInsightsBifrostClient(inputs, activeTokenProvider, teamId, apiKey);
|
|
22597
22998
|
result = await (runtime.executeOnboarding ?? runOnboarding)(
|
|
22598
22999
|
inputs,
|
|
22599
23000
|
client,
|
|
@@ -22624,9 +23025,9 @@ function isEntrypoint(currentPath, entrypointPath) {
|
|
|
22624
23025
|
return false;
|
|
22625
23026
|
}
|
|
22626
23027
|
try {
|
|
22627
|
-
return (0,
|
|
23028
|
+
return (0, import_node_fs2.realpathSync)(currentPath) === (0, import_node_fs2.realpathSync)(entrypointPath);
|
|
22628
23029
|
} catch {
|
|
22629
|
-
return
|
|
23030
|
+
return import_node_path2.default.resolve(currentPath) === import_node_path2.default.resolve(entrypointPath);
|
|
22630
23031
|
}
|
|
22631
23032
|
}
|
|
22632
23033
|
if (isEntrypoint(currentModulePath, entrypoint)) {
|