@alter-ai/cli 0.9.0 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +609 -98
- package/npm-shrinkwrap.json +848 -0
- package/package.json +10 -6
package/dist/cli.js
CHANGED
|
@@ -33,7 +33,7 @@ import { join } from "path";
|
|
|
33
33
|
// package.json
|
|
34
34
|
var package_default = {
|
|
35
35
|
name: "@alter-ai/cli",
|
|
36
|
-
version: "0.9.
|
|
36
|
+
version: "0.9.2",
|
|
37
37
|
description: "Command-line interface for the Alter Vault dev portal \u2014 scripted dashboard automation.",
|
|
38
38
|
type: "module",
|
|
39
39
|
bin: {
|
|
@@ -44,7 +44,8 @@ var package_default = {
|
|
|
44
44
|
"dist",
|
|
45
45
|
"README.md",
|
|
46
46
|
"LICENSE",
|
|
47
|
-
"THIRD_PARTY_NOTICES"
|
|
47
|
+
"THIRD_PARTY_NOTICES",
|
|
48
|
+
"npm-shrinkwrap.json"
|
|
48
49
|
],
|
|
49
50
|
scripts: {
|
|
50
51
|
build: "tsup",
|
|
@@ -69,14 +70,17 @@ var package_default = {
|
|
|
69
70
|
author: "Alter Labs, Inc.",
|
|
70
71
|
license: "MIT",
|
|
71
72
|
dependencies: {
|
|
72
|
-
commander: "
|
|
73
|
-
"posthog-node": "
|
|
73
|
+
commander: "12.1.0",
|
|
74
|
+
"posthog-node": "4.18.0"
|
|
74
75
|
},
|
|
75
76
|
optionalDependencies: {
|
|
76
|
-
keytar: "
|
|
77
|
+
keytar: "7.9.0"
|
|
78
|
+
},
|
|
79
|
+
overrides: {
|
|
80
|
+
esbuild: "0.28.1"
|
|
77
81
|
},
|
|
78
82
|
devDependencies: {
|
|
79
|
-
"@alter-ai/alter-sdk": "workspace:0.
|
|
83
|
+
"@alter-ai/alter-sdk": "workspace:0.25.0",
|
|
80
84
|
"@alter-vault/shared-types": "workspace:0.0.1",
|
|
81
85
|
"@alter-vault/shared-utils": "workspace:0.0.1",
|
|
82
86
|
"@eslint/js": "9.39.4",
|
|
@@ -4739,7 +4743,14 @@ var AmbiguousGrantError = class extends BackendError {
|
|
|
4739
4743
|
// scoped to the caller's own accessible set. Retry with the chosen
|
|
4740
4744
|
// `grantId`.
|
|
4741
4745
|
candidates;
|
|
4742
|
-
|
|
4746
|
+
// True when more grants matched than the error body can carry. It bounds
|
|
4747
|
+
// EVERY disambiguator on this error — `candidates`, `accountIdentifiers`,
|
|
4748
|
+
// `appUserIds` and `grantIds` all come from the same capped set — so a
|
|
4749
|
+
// grant, account or user missing from any of them is not proof it does not
|
|
4750
|
+
// exist. Narrow the request or call `listGrants()` for the authoritative
|
|
4751
|
+
// set.
|
|
4752
|
+
candidatesTruncated;
|
|
4753
|
+
constructor(message, providerId, accountIdentifiers, accountWasProvided, appUserIds, details, grantIds, candidates, candidatesTruncated) {
|
|
4743
4754
|
super(message, details);
|
|
4744
4755
|
this.name = "AmbiguousGrantError";
|
|
4745
4756
|
this.providerId = providerId;
|
|
@@ -4748,6 +4759,7 @@ var AmbiguousGrantError = class extends BackendError {
|
|
|
4748
4759
|
this.appUserIds = appUserIds ?? [];
|
|
4749
4760
|
this.grantIds = grantIds ?? [];
|
|
4750
4761
|
this.candidates = candidates ?? [];
|
|
4762
|
+
this.candidatesTruncated = candidatesTruncated ?? false;
|
|
4751
4763
|
}
|
|
4752
4764
|
};
|
|
4753
4765
|
var NoDelegatedGrantError = class extends BackendError {
|
|
@@ -5720,6 +5732,14 @@ var ConnectSession = class {
|
|
|
5720
5732
|
* Python SDK's scope_constraint_warnings coercion (parity contract).
|
|
5721
5733
|
*/
|
|
5722
5734
|
scopeConstraintWarnings;
|
|
5735
|
+
/**
|
|
5736
|
+
* True when the session names exactly one provider: the hosted UI shows no
|
|
5737
|
+
* picker, confirmation screen or Alter-branded page, and the browser widget
|
|
5738
|
+
* can open the provider window straight from the application's click when
|
|
5739
|
+
* the value is passed through as `open({ direct })`. False on a malformed
|
|
5740
|
+
* wire value (informational, not load-bearing).
|
|
5741
|
+
*/
|
|
5742
|
+
direct;
|
|
5723
5743
|
constructor(data) {
|
|
5724
5744
|
_assertString(data.session_token, "session_token", "ConnectSession");
|
|
5725
5745
|
_assertString(data.connect_url, "connect_url", "ConnectSession");
|
|
@@ -5741,10 +5761,12 @@ var ConnectSession = class {
|
|
|
5741
5761
|
(w) => typeof w === "string"
|
|
5742
5762
|
) : []
|
|
5743
5763
|
);
|
|
5764
|
+
this.direct = data.direct === true;
|
|
5744
5765
|
Object.freeze(this);
|
|
5745
5766
|
}
|
|
5746
5767
|
toJSON() {
|
|
5747
5768
|
return {
|
|
5769
|
+
direct: this.direct,
|
|
5748
5770
|
session_token: this.sessionToken,
|
|
5749
5771
|
connect_url: this.connectUrl,
|
|
5750
5772
|
expires_in: this.expiresIn,
|
|
@@ -6255,7 +6277,11 @@ var ConnectResult = class {
|
|
|
6255
6277
|
providerId;
|
|
6256
6278
|
accountIdentifier;
|
|
6257
6279
|
scopes;
|
|
6258
|
-
/**
|
|
6280
|
+
/**
|
|
6281
|
+
* "creation" when a new grant was minted; "reauth" when an existing
|
|
6282
|
+
* grant was returned with its id preserved (credential repair, or reuse
|
|
6283
|
+
* of an already-connected grant).
|
|
6284
|
+
*/
|
|
6259
6285
|
operation;
|
|
6260
6286
|
grantPolicy;
|
|
6261
6287
|
/**
|
|
@@ -8143,7 +8169,7 @@ async function _raiseForStatus(response) {
|
|
|
8143
8169
|
if (code === AgentConcurrentUpdateError.code && response.status === 409) {
|
|
8144
8170
|
throw new AgentConcurrentUpdateError({ message, details: body, hint });
|
|
8145
8171
|
}
|
|
8146
|
-
const ExcCtor = ERROR_CODE_STATUS_MAP[`${code}:${response.status}`] ?? ERROR_CODE_MAP[code];
|
|
8172
|
+
const ExcCtor = ERROR_CODE_STATUS_MAP[`${code}:${response.status}`] ?? (Object.hasOwn(ERROR_CODE_MAP, code) ? ERROR_CODE_MAP[code] : void 0);
|
|
8147
8173
|
if (ExcCtor !== void 0) {
|
|
8148
8174
|
throw new ExcCtor({ message, details: body, hint });
|
|
8149
8175
|
}
|
|
@@ -10172,7 +10198,7 @@ function _extractAdditionalCredentials(token) {
|
|
|
10172
10198
|
return _additionalCredsStore.get(token);
|
|
10173
10199
|
}
|
|
10174
10200
|
var _fetch;
|
|
10175
|
-
var SDK_VERSION = "0.
|
|
10201
|
+
var SDK_VERSION = "0.25.0";
|
|
10176
10202
|
var SDK_USER_AGENT = `alter-sdk-node/${SDK_VERSION}`;
|
|
10177
10203
|
function pyUnquote(s) {
|
|
10178
10204
|
if (!s.includes("%")) return s;
|
|
@@ -10243,8 +10269,12 @@ function canonicalSigningQuery(rawQuery) {
|
|
|
10243
10269
|
pairs.sort((p, q) => byCodePoint(p[0], q[0]) || byCodePoint(p[1], q[1]));
|
|
10244
10270
|
return pairs.map(([k, v]) => `${pyQuotePlus(k)}=${pyQuotePlus(v)}`).join("&");
|
|
10245
10271
|
}
|
|
10272
|
+
var DEFAULT_TIMEOUT_MS = 95e3;
|
|
10273
|
+
var _ApprovalDeadlineElapsed = class extends Error {
|
|
10274
|
+
};
|
|
10246
10275
|
var AUTH_POLL_SERVER_WAIT_MS = 25e3;
|
|
10247
10276
|
var AUTH_POLL_HTTP_BUFFER_MS = 15e3;
|
|
10277
|
+
var APPROVAL_POLL_SERVER_WAIT_MS = 25e3;
|
|
10248
10278
|
var PERMANENT_POLL_STATUSES = /* @__PURE__ */ new Set([400, 401, 403, 404, 422]);
|
|
10249
10279
|
var HTTP_FORBIDDEN = 403;
|
|
10250
10280
|
var HTTP_NO_CONTENT2 = 204;
|
|
@@ -10520,6 +10550,23 @@ function validateAndSerializeContext(context) {
|
|
|
10520
10550
|
return encoded;
|
|
10521
10551
|
}
|
|
10522
10552
|
var MAX_BODY_SIZE_BYTES = 1e4;
|
|
10553
|
+
var AUDIT_REFUSAL_BODY_MAX_CHARS = 65536;
|
|
10554
|
+
var REFUSAL_MESSAGE_CONTROL_CHARS = /[\u0000-\u001f\u007f-\u009f]/g;
|
|
10555
|
+
var REFUSAL_MESSAGE_MAX_CHARS = 300;
|
|
10556
|
+
function sanitizeRefusalMessage(message) {
|
|
10557
|
+
const cleaned = message.replace(REFUSAL_MESSAGE_CONTROL_CHARS, " ");
|
|
10558
|
+
return cleaned.length > REFUSAL_MESSAGE_MAX_CHARS ? `${cleaned.slice(0, REFUSAL_MESSAGE_MAX_CHARS)}\u2026` : cleaned;
|
|
10559
|
+
}
|
|
10560
|
+
function _redactUrlForLog(url2) {
|
|
10561
|
+
try {
|
|
10562
|
+
const parsed = new URL(url2);
|
|
10563
|
+
if (parsed.protocol && parsed.hostname) {
|
|
10564
|
+
return `${parsed.protocol}//${parsed.hostname}`;
|
|
10565
|
+
}
|
|
10566
|
+
} catch {
|
|
10567
|
+
}
|
|
10568
|
+
return "<unparseable-url>";
|
|
10569
|
+
}
|
|
10523
10570
|
var HTTP_CLIENT_ERROR_START = 400;
|
|
10524
10571
|
var MAX_ACTOR_STRING_LENGTH = 255;
|
|
10525
10572
|
var SAFE_HEADER_PATTERN = /^[\x20-\x7E]+$/;
|
|
@@ -10808,6 +10855,16 @@ var HttpClient = class {
|
|
|
10808
10855
|
new DOMException("The operation timed out.", "TimeoutError")
|
|
10809
10856
|
);
|
|
10810
10857
|
}, effectiveTimeoutMs);
|
|
10858
|
+
const callerSignal = options?.signal;
|
|
10859
|
+
let onCallerAbort;
|
|
10860
|
+
if (callerSignal !== void 0) {
|
|
10861
|
+
if (callerSignal.aborted) {
|
|
10862
|
+
controller.abort(callerSignal.reason);
|
|
10863
|
+
} else {
|
|
10864
|
+
onCallerAbort = () => controller.abort(callerSignal.reason);
|
|
10865
|
+
callerSignal.addEventListener("abort", onCallerAbort);
|
|
10866
|
+
}
|
|
10867
|
+
}
|
|
10811
10868
|
const init = {
|
|
10812
10869
|
method,
|
|
10813
10870
|
headers: mergedHeaders,
|
|
@@ -10846,6 +10903,9 @@ var HttpClient = class {
|
|
|
10846
10903
|
return response;
|
|
10847
10904
|
} finally {
|
|
10848
10905
|
clearTimeout(timeoutId);
|
|
10906
|
+
if (callerSignal !== void 0 && onCallerAbort !== void 0) {
|
|
10907
|
+
callerSignal.removeEventListener("abort", onCallerAbort);
|
|
10908
|
+
}
|
|
10849
10909
|
}
|
|
10850
10910
|
}
|
|
10851
10911
|
/**
|
|
@@ -10958,7 +11018,7 @@ var _VaultClient = class __VaultClient {
|
|
|
10958
11018
|
unresolvedBaseUrl,
|
|
10959
11019
|
options.logger ?? console
|
|
10960
11020
|
);
|
|
10961
|
-
const timeoutMs = options.timeout ??
|
|
11021
|
+
const timeoutMs = options.timeout ?? DEFAULT_TIMEOUT_MS;
|
|
10962
11022
|
this.#caller = options.caller;
|
|
10963
11023
|
const rawCallerType = options.callerType ?? "agent";
|
|
10964
11024
|
if (rawCallerType !== "agent" && rawCallerType !== "service") {
|
|
@@ -11640,7 +11700,11 @@ ${label}:${value}`;
|
|
|
11640
11700
|
appUserIds,
|
|
11641
11701
|
errorData,
|
|
11642
11702
|
grantIds,
|
|
11643
|
-
candidates
|
|
11703
|
+
candidates,
|
|
11704
|
+
// Absent on the wire means "complete" — only a real cap emits the
|
|
11705
|
+
// key, so a strict `=== true` keeps a stray falsy value from
|
|
11706
|
+
// reading as truncation.
|
|
11707
|
+
errorData.candidates_truncated === true
|
|
11644
11708
|
);
|
|
11645
11709
|
}
|
|
11646
11710
|
if (errorData.error === "sibling_label_conflict") {
|
|
@@ -12163,9 +12227,34 @@ ${label}:${value}`;
|
|
|
12163
12227
|
});
|
|
12164
12228
|
this.#cacheActorIdFromResponse(response);
|
|
12165
12229
|
if (!response.ok) {
|
|
12230
|
+
let detail = "";
|
|
12231
|
+
try {
|
|
12232
|
+
const raw = await response.text();
|
|
12233
|
+
if (raw.length <= AUDIT_REFUSAL_BODY_MAX_CHARS) {
|
|
12234
|
+
const body = JSON.parse(raw);
|
|
12235
|
+
let message = body.message;
|
|
12236
|
+
if (typeof message !== "string" || message === "") {
|
|
12237
|
+
const inner = body.detail;
|
|
12238
|
+
if (typeof inner === "string") {
|
|
12239
|
+
message = inner;
|
|
12240
|
+
} else if (inner !== null && typeof inner === "object") {
|
|
12241
|
+
message = inner.message;
|
|
12242
|
+
}
|
|
12243
|
+
}
|
|
12244
|
+
if (typeof message === "string" && message !== "") {
|
|
12245
|
+
detail = `: ${sanitizeRefusalMessage(message)}`;
|
|
12246
|
+
}
|
|
12247
|
+
}
|
|
12248
|
+
} catch {
|
|
12249
|
+
}
|
|
12166
12250
|
this.#logger.warn(
|
|
12167
|
-
`Audit log failed with status ${response.status} (non-fatal)`
|
|
12251
|
+
`Audit log failed with status ${response.status} (non-fatal)${detail} \u2014 this call's api-call audit row was not recorded (grant_id=${params.grantId}, method=${params.method}, url=${_redactUrlForLog(params.url)})`
|
|
12168
12252
|
);
|
|
12253
|
+
} else {
|
|
12254
|
+
try {
|
|
12255
|
+
await response.arrayBuffer();
|
|
12256
|
+
} catch {
|
|
12257
|
+
}
|
|
12169
12258
|
}
|
|
12170
12259
|
} catch (error51) {
|
|
12171
12260
|
this.#logger.warn(
|
|
@@ -12676,7 +12765,7 @@ ${label}:${value}`;
|
|
|
12676
12765
|
headers: hmacHeaders,
|
|
12677
12766
|
// Cap the best-effort background report at 2s (parity with the Python
|
|
12678
12767
|
// SDK's asyncio.wait_for). Without an override it would inherit the
|
|
12679
|
-
// instance timeout (default
|
|
12768
|
+
// instance timeout (default 95s), so a hung backend could keep the
|
|
12680
12769
|
// fire-and-forget task — and close()'s drain of it — alive far longer
|
|
12681
12770
|
// than the Python SDK does. Next provider 403 re-reports (self-healing).
|
|
12682
12771
|
timeoutMs: 2e3
|
|
@@ -13053,8 +13142,11 @@ ${label}:${value}`;
|
|
|
13053
13142
|
* @param options - Optional revocation options (reason for audit log).
|
|
13054
13143
|
* @returns RevokeGrantResult confirming the revocation.
|
|
13055
13144
|
* @throws {GrantNotFoundError} If the grant does not exist, is not
|
|
13056
|
-
* active,
|
|
13057
|
-
* the calling user
|
|
13145
|
+
* active, (when a user token is configured) does not belong to
|
|
13146
|
+
* the calling user, or — when this client holds an AGENT key — is
|
|
13147
|
+
* not a grant the agent is the principal of (its own delegation or
|
|
13148
|
+
* an agent-owned grant). An allowlist entry for the grant's provider
|
|
13149
|
+
* is never enough.
|
|
13058
13150
|
* @throws {NetworkError} If connection to backend fails.
|
|
13059
13151
|
* @throws {TimeoutError} If request to backend times out.
|
|
13060
13152
|
*/
|
|
@@ -13865,14 +13957,17 @@ ${label}:${value}`;
|
|
|
13865
13957
|
* @returns One `ConnectResult` per provider the user completed
|
|
13866
13958
|
* within the session (multi-provider Connect sessions yield
|
|
13867
13959
|
* multiple results).
|
|
13868
|
-
* @throws AlterValueError if `sessionToken` is blank,
|
|
13869
|
-
* `pollIntervalMs` is non-finite or not greater than zero
|
|
13960
|
+
* @throws AlterValueError if `sessionToken` is blank, `timeoutMs` /
|
|
13961
|
+
* `pollIntervalMs` is non-finite or not greater than zero, or
|
|
13962
|
+
* `onEvent` is provided but not a function.
|
|
13870
13963
|
* @throws ConnectTimeoutError if the local `timeoutMs` elapses, or a
|
|
13871
13964
|
* session observed as pending expires before Alter receives a
|
|
13872
13965
|
* completion callback. `details.reason` distinguishes
|
|
13873
13966
|
* `poll_deadline_elapsed`, `session_expired`, and `rate_limited` (the
|
|
13874
13967
|
* whole budget was spent backing off a throttled poll —
|
|
13875
|
-
* `details.retryAfter` carries the last hint the server gave).
|
|
13968
|
+
* `details.retryAfter` carries the last hint the server gave). When
|
|
13969
|
+
* denials were observed before the timeout,
|
|
13970
|
+
* `details.declined_providers` lists them on EVERY variant.
|
|
13876
13971
|
* @throws ConnectFlowError / ConnectDeniedError / ConnectConfigError
|
|
13877
13972
|
* for user denial, a first poll that finds an unavailable/expired
|
|
13878
13973
|
* session, unrecognized status, or a completed session whose every grant
|
|
@@ -13892,8 +13987,18 @@ ${label}:${value}`;
|
|
|
13892
13987
|
const timeoutMs = options?.timeoutMs ?? 3e5;
|
|
13893
13988
|
const pollIntervalMs = options?.pollIntervalMs ?? 2e3;
|
|
13894
13989
|
validatePollingInputs(sessionToken, timeoutMs, pollIntervalMs);
|
|
13990
|
+
if (options?.onEvent !== void 0 && typeof options.onEvent !== "function") {
|
|
13991
|
+
throw new AlterValueError("onEvent must be a function when provided");
|
|
13992
|
+
}
|
|
13895
13993
|
const deadline = performance.now() + timeoutMs;
|
|
13896
13994
|
let sawPending = false;
|
|
13995
|
+
const seenDeclinedProviders = /* @__PURE__ */ new Set();
|
|
13996
|
+
const withDeclined = (base) => {
|
|
13997
|
+
if (seenDeclinedProviders.size > 0) {
|
|
13998
|
+
base.declined_providers = [...seenDeclinedProviders].sort();
|
|
13999
|
+
}
|
|
14000
|
+
return base;
|
|
14001
|
+
};
|
|
13897
14002
|
while (true) {
|
|
13898
14003
|
this.#assertNotClosed();
|
|
13899
14004
|
let pollResult;
|
|
@@ -13907,11 +14012,11 @@ ${label}:${value}`;
|
|
|
13907
14012
|
if (remaining2 <= 0) {
|
|
13908
14013
|
throw new ConnectTimeoutError(
|
|
13909
14014
|
`OAuth flow did not complete within ${Math.round(timeoutMs / 1e3)} seconds: polling was rate-limited for the remainder of the budget. The authorization may still have succeeded \u2014 retry the poll with a fresh timeout, or raise the API key's per-minute rate limit if Connect flows run concurrently.`,
|
|
13910
|
-
{
|
|
14015
|
+
withDeclined({
|
|
13911
14016
|
timeoutMs,
|
|
13912
14017
|
reason: "rate_limited",
|
|
13913
14018
|
retryAfter: e.retryAfter
|
|
13914
|
-
}
|
|
14019
|
+
})
|
|
13915
14020
|
);
|
|
13916
14021
|
}
|
|
13917
14022
|
let backoffMs = pollIntervalMs;
|
|
@@ -14044,7 +14149,10 @@ ${label}:${value}`;
|
|
|
14044
14149
|
].includes(errorCode)) {
|
|
14045
14150
|
throw new ConnectConfigError(errorMessage, errorDetails);
|
|
14046
14151
|
}
|
|
14047
|
-
const identitySyncStatus =
|
|
14152
|
+
const identitySyncStatus = Object.hasOwn(
|
|
14153
|
+
IDENTITY_SYNC_POLL_STATUS,
|
|
14154
|
+
errorCode
|
|
14155
|
+
) ? IDENTITY_SYNC_POLL_STATUS[errorCode] : void 0;
|
|
14048
14156
|
if (identitySyncStatus !== void 0) {
|
|
14049
14157
|
throw new BackendError(
|
|
14050
14158
|
errorMessage,
|
|
@@ -14058,7 +14166,7 @@ ${label}:${value}`;
|
|
|
14058
14166
|
if (sawPending) {
|
|
14059
14167
|
throw new ConnectTimeoutError(
|
|
14060
14168
|
"Connect session expired before authorization returned to Alter. The provider may have shown an error without redirecting, the browser may have been closed, or the user may not have completed the flow.",
|
|
14061
|
-
{ timeoutMs, reason: "session_expired" }
|
|
14169
|
+
withDeclined({ timeoutMs, reason: "session_expired" })
|
|
14062
14170
|
);
|
|
14063
14171
|
}
|
|
14064
14172
|
throw new ConnectFlowError(
|
|
@@ -14071,6 +14179,46 @@ ${label}:${value}`;
|
|
|
14071
14179
|
{ status: pollStatus }
|
|
14072
14180
|
);
|
|
14073
14181
|
}
|
|
14182
|
+
const rawDeclined = pollResult.declined_providers;
|
|
14183
|
+
if (Array.isArray(rawDeclined)) {
|
|
14184
|
+
for (const entry of rawDeclined) {
|
|
14185
|
+
if (entry === null || typeof entry !== "object") continue;
|
|
14186
|
+
const declinedProvider = entry.provider_id;
|
|
14187
|
+
if (typeof declinedProvider !== "string" || declinedProvider === "") {
|
|
14188
|
+
continue;
|
|
14189
|
+
}
|
|
14190
|
+
const rawCode = entry.error_code;
|
|
14191
|
+
if (typeof rawCode !== "string" || rawCode === "") continue;
|
|
14192
|
+
if (seenDeclinedProviders.has(declinedProvider)) continue;
|
|
14193
|
+
seenDeclinedProviders.add(declinedProvider);
|
|
14194
|
+
if (!options?.onEvent) continue;
|
|
14195
|
+
const rawDeclinedAt = entry.declined_at;
|
|
14196
|
+
const logCallbackFailure = (error51) => {
|
|
14197
|
+
this.#logger.warn(
|
|
14198
|
+
"pollConnectSession onEvent callback threw (non-fatal)",
|
|
14199
|
+
{
|
|
14200
|
+
provider_id: declinedProvider,
|
|
14201
|
+
error_type: error51 instanceof Error ? error51.constructor.name : typeof error51
|
|
14202
|
+
}
|
|
14203
|
+
);
|
|
14204
|
+
};
|
|
14205
|
+
try {
|
|
14206
|
+
const result = options.onEvent({
|
|
14207
|
+
type: "provider_declined",
|
|
14208
|
+
provider_id: declinedProvider,
|
|
14209
|
+
error_code: rawCode,
|
|
14210
|
+
declined_at: typeof rawDeclinedAt === "string" ? rawDeclinedAt : null
|
|
14211
|
+
});
|
|
14212
|
+
if (result !== null && (typeof result === "object" || typeof result === "function") && typeof result.then === "function") {
|
|
14213
|
+
void Promise.resolve(result).catch(
|
|
14214
|
+
logCallbackFailure
|
|
14215
|
+
);
|
|
14216
|
+
}
|
|
14217
|
+
} catch (error51) {
|
|
14218
|
+
logCallbackFailure(error51);
|
|
14219
|
+
}
|
|
14220
|
+
}
|
|
14221
|
+
}
|
|
14074
14222
|
sawPending = true;
|
|
14075
14223
|
const remaining = deadline - performance.now();
|
|
14076
14224
|
if (remaining <= 0) break;
|
|
@@ -14080,7 +14228,7 @@ ${label}:${value}`;
|
|
|
14080
14228
|
}
|
|
14081
14229
|
throw new ConnectTimeoutError(
|
|
14082
14230
|
`OAuth flow did not complete within ${Math.round(timeoutMs / 1e3)} seconds. Alter did not receive a completion callback; the provider may have shown an error without redirecting, the browser may have been closed, or the user may not have finished authorizing.`,
|
|
14083
|
-
{ timeoutMs, reason: "poll_deadline_elapsed" }
|
|
14231
|
+
withDeclined({ timeoutMs, reason: "poll_deadline_elapsed" })
|
|
14084
14232
|
);
|
|
14085
14233
|
}
|
|
14086
14234
|
/**
|
|
@@ -14333,7 +14481,10 @@ ${label}:${value}`;
|
|
|
14333
14481
|
const errorMessage = typeof pollData.error_message === "string" && pollData.error_message ? pollData.error_message : "unknown error";
|
|
14334
14482
|
const message = `Authentication failed: ${errorMessage}`;
|
|
14335
14483
|
const syncCode = typeof pollData.error_code === "string" ? pollData.error_code : "";
|
|
14336
|
-
const identitySyncStatus =
|
|
14484
|
+
const identitySyncStatus = Object.hasOwn(
|
|
14485
|
+
IDENTITY_SYNC_POLL_STATUS,
|
|
14486
|
+
syncCode
|
|
14487
|
+
) ? IDENTITY_SYNC_POLL_STATUS[syncCode] : void 0;
|
|
14337
14488
|
if (identitySyncStatus !== void 0) {
|
|
14338
14489
|
const details = { error_code: syncCode };
|
|
14339
14490
|
const retryAfter = _coerceInt(pollData.retry_after_seconds);
|
|
@@ -14958,10 +15109,34 @@ ${label}:${value}`;
|
|
|
14958
15109
|
error: errCode
|
|
14959
15110
|
});
|
|
14960
15111
|
}
|
|
14961
|
-
/**
|
|
14962
|
-
|
|
15112
|
+
/**
|
|
15113
|
+
* Single-shot poll of an approval.
|
|
15114
|
+
*
|
|
15115
|
+
* `wait` asks the server to hold the request open until the approval's
|
|
15116
|
+
* status changes, up to that many seconds (0 = return immediately, the
|
|
15117
|
+
* default and the historical behaviour). It is a pure latency optimisation:
|
|
15118
|
+
* the response shape is identical either way, and a backend that predates
|
|
15119
|
+
* the parameter simply ignores it and answers immediately. The server caps
|
|
15120
|
+
* `wait` at 25 seconds (`APPROVAL_POLL_SERVER_WAIT_MS`, mirroring the
|
|
15121
|
+
* backend's `MAX_LONG_POLL_SECONDS`) and REJECTS larger values with a
|
|
15122
|
+
* validation error (422) rather than clamping them.
|
|
15123
|
+
*
|
|
15124
|
+
* `signal` (internal — not exposed on App/Agent) lets `awaitApproval` abort
|
|
15125
|
+
* an in-flight held request its local deadline has abandoned.
|
|
15126
|
+
*
|
|
15127
|
+
* @throws AlterValueError if `wait` is not a finite number.
|
|
15128
|
+
*/
|
|
15129
|
+
async getApprovalStatus(approvalId, options = {}) {
|
|
14963
15130
|
this.#assertNotClosed();
|
|
14964
|
-
|
|
15131
|
+
if (options !== null && options !== void 0 && typeof options !== "object") {
|
|
15132
|
+
throw new AlterValueError("options must be an object");
|
|
15133
|
+
}
|
|
15134
|
+
const waitOption = options?.wait ?? 0;
|
|
15135
|
+
if (typeof waitOption !== "number" || !Number.isFinite(waitOption)) {
|
|
15136
|
+
throw new AlterValueError("wait must be a finite number");
|
|
15137
|
+
}
|
|
15138
|
+
const wait = Math.max(0, Math.floor(waitOption));
|
|
15139
|
+
const sdkPath = wait > 0 ? `/sdk/approvals/${approvalId}?wait=${wait}` : `/sdk/approvals/${approvalId}`;
|
|
14965
15140
|
const hmacHeaders = this.#computeHmacHeaders("GET", sdkPath, "");
|
|
14966
15141
|
const traceparent = await ambientTraceparent();
|
|
14967
15142
|
if (traceparent !== void 0) {
|
|
@@ -14970,7 +15145,18 @@ ${label}:${value}`;
|
|
|
14970
15145
|
let response;
|
|
14971
15146
|
try {
|
|
14972
15147
|
response = await this.#alterClient.request("GET", sdkPath, {
|
|
14973
|
-
headers: hmacHeaders
|
|
15148
|
+
headers: hmacHeaders,
|
|
15149
|
+
// A held request needs a wider per-request timeout than the client
|
|
15150
|
+
// default, or the client aborts its own long-poll (see
|
|
15151
|
+
// AUTH_POLL_HTTP_BUFFER_MS). Only applied when actually parking.
|
|
15152
|
+
// Capped at the server's own ceiling: `wait` is deliberately NOT
|
|
15153
|
+
// clamped on the wire (the backend owns that contract and answers an
|
|
15154
|
+
// over-cap value with its documented validation error), but an
|
|
15155
|
+
// unbounded `wait * 1000` overflows to `Infinity` for a huge finite
|
|
15156
|
+
// value, and Node coerces such a timer to fire almost immediately —
|
|
15157
|
+
// aborting the request before that very error can come back.
|
|
15158
|
+
timeoutMs: wait > 0 ? Math.min(wait * 1e3, APPROVAL_POLL_SERVER_WAIT_MS) + AUTH_POLL_HTTP_BUFFER_MS : void 0,
|
|
15159
|
+
signal: options?.signal
|
|
14974
15160
|
});
|
|
14975
15161
|
} catch (error51) {
|
|
14976
15162
|
if (error51 instanceof Error && error51.name === "AbortError") {
|
|
@@ -15041,9 +15227,39 @@ ${label}:${value}`;
|
|
|
15041
15227
|
throw this.#buildApprovalTimeout(approvalId, timeoutMs, lastTransient);
|
|
15042
15228
|
}
|
|
15043
15229
|
let status;
|
|
15230
|
+
const raceController = new AbortController();
|
|
15231
|
+
let deadlineTimer;
|
|
15044
15232
|
try {
|
|
15045
|
-
|
|
15233
|
+
const serverWaitSeconds = Math.max(
|
|
15234
|
+
0,
|
|
15235
|
+
Math.min(
|
|
15236
|
+
Math.floor(APPROVAL_POLL_SERVER_WAIT_MS / 1e3),
|
|
15237
|
+
Math.floor((deadline - Date.now()) / 1e3)
|
|
15238
|
+
)
|
|
15239
|
+
);
|
|
15240
|
+
const remainingMs = Math.max(0, deadline - Date.now());
|
|
15241
|
+
status = await Promise.race([
|
|
15242
|
+
this.getApprovalStatus(approvalId, {
|
|
15243
|
+
wait: serverWaitSeconds,
|
|
15244
|
+
signal: raceController.signal
|
|
15245
|
+
}),
|
|
15246
|
+
new Promise((_resolve, reject) => {
|
|
15247
|
+
deadlineTimer = setTimeout(
|
|
15248
|
+
() => reject(new _ApprovalDeadlineElapsed()),
|
|
15249
|
+
remainingMs
|
|
15250
|
+
);
|
|
15251
|
+
deadlineTimer.unref?.();
|
|
15252
|
+
})
|
|
15253
|
+
]);
|
|
15046
15254
|
} catch (err2) {
|
|
15255
|
+
if (err2 instanceof _ApprovalDeadlineElapsed) {
|
|
15256
|
+
raceController.abort();
|
|
15257
|
+
throw this.#buildApprovalTimeout(
|
|
15258
|
+
approvalId,
|
|
15259
|
+
timeoutMs,
|
|
15260
|
+
lastTransient
|
|
15261
|
+
);
|
|
15262
|
+
}
|
|
15047
15263
|
if (!__VaultClient.#isTransientPollError(err2)) {
|
|
15048
15264
|
throw err2;
|
|
15049
15265
|
}
|
|
@@ -15065,6 +15281,8 @@ ${label}:${value}`;
|
|
|
15065
15281
|
);
|
|
15066
15282
|
await new Promise((res) => setTimeout(res, sleep2));
|
|
15067
15283
|
continue;
|
|
15284
|
+
} finally {
|
|
15285
|
+
if (deadlineTimer !== void 0) clearTimeout(deadlineTimer);
|
|
15068
15286
|
}
|
|
15069
15287
|
lastTransient = null;
|
|
15070
15288
|
if (status.status === "executed" && !status.hasResult) {
|
|
@@ -15880,8 +16098,8 @@ var Agent = class _Agent {
|
|
|
15880
16098
|
return this.#client.delegate(grantId, toAgentId, options);
|
|
15881
16099
|
}
|
|
15882
16100
|
// ── Approvals ──────────────────────────────────────────────────────────
|
|
15883
|
-
async getApprovalStatus(approvalId) {
|
|
15884
|
-
return this.#client.getApprovalStatus(approvalId);
|
|
16101
|
+
async getApprovalStatus(approvalId, options = {}) {
|
|
16102
|
+
return this.#client.getApprovalStatus(approvalId, options);
|
|
15885
16103
|
}
|
|
15886
16104
|
async awaitApproval(...args) {
|
|
15887
16105
|
return this.#client.awaitApproval(...args);
|
|
@@ -16301,8 +16519,8 @@ var App = class _App {
|
|
|
16301
16519
|
});
|
|
16302
16520
|
}
|
|
16303
16521
|
// ── Approvals ──────────────────────────────────────────────────────────
|
|
16304
|
-
async getApprovalStatus(approvalId) {
|
|
16305
|
-
return this.#client.getApprovalStatus(approvalId);
|
|
16522
|
+
async getApprovalStatus(approvalId, options = {}) {
|
|
16523
|
+
return this.#client.getApprovalStatus(approvalId, options);
|
|
16306
16524
|
}
|
|
16307
16525
|
async awaitApproval(...args) {
|
|
16308
16526
|
return this.#client.awaitApproval(...args);
|
|
@@ -16558,8 +16776,8 @@ async function _listGrantsUnified(client, body) {
|
|
|
16558
16776
|
var DEFAULT_BASE_URL = "https://backend.alterauth.com";
|
|
16559
16777
|
var PAT_API_PREFIX = "/api/v1/dev-portal";
|
|
16560
16778
|
var HTTP_ERROR_THRESHOLD = 400;
|
|
16561
|
-
var
|
|
16562
|
-
var CLI_VERSION = "0.9.
|
|
16779
|
+
var DEFAULT_TIMEOUT_MS2 = 3e4;
|
|
16780
|
+
var CLI_VERSION = "0.9.2";
|
|
16563
16781
|
var USER_AGENT = buildUserAgent();
|
|
16564
16782
|
function buildUserAgent() {
|
|
16565
16783
|
let osTag = "";
|
|
@@ -16804,7 +17022,7 @@ var DashboardClient = class {
|
|
|
16804
17022
|
);
|
|
16805
17023
|
}
|
|
16806
17024
|
}
|
|
16807
|
-
this.#timeoutMs = options.timeoutMs ??
|
|
17025
|
+
this.#timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
|
|
16808
17026
|
this.pats = new PATsNamespace(this);
|
|
16809
17027
|
this.apps = new AppsNamespace(this);
|
|
16810
17028
|
this.keys = new KeysNamespace2(this);
|
|
@@ -17544,6 +17762,13 @@ var AgentsNamespace2 = class {
|
|
|
17544
17762
|
return expectDict(body, "agents.undeprecate_key", 200);
|
|
17545
17763
|
}
|
|
17546
17764
|
};
|
|
17765
|
+
function isRedirectUriAliasSpec(value) {
|
|
17766
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
17767
|
+
return false;
|
|
17768
|
+
}
|
|
17769
|
+
const spec = value;
|
|
17770
|
+
return typeof spec.label === "string" && typeof spec.description === "string";
|
|
17771
|
+
}
|
|
17547
17772
|
function isSharedDevProviderEntry(value) {
|
|
17548
17773
|
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
17549
17774
|
return false;
|
|
@@ -17565,7 +17790,7 @@ function isProviderCatalogEntry(value) {
|
|
|
17565
17790
|
const v = value;
|
|
17566
17791
|
return typeof v.id === "string" && typeof v.name === "string" && typeof v.display_name === "string" && (v.category === null || typeof v.category === "string") && (v.supports_refresh === null || typeof v.supports_refresh === "boolean") && (v.supports_pkce === null || typeof v.supports_pkce === "boolean") && (v.available_environments === void 0 || Array.isArray(v.available_environments) && v.available_environments.every(
|
|
17567
17792
|
(environment) => environment === "production" || environment === "sandbox"
|
|
17568
|
-
)) && (v.scopes_configured_on_provider === void 0 || typeof v.scopes_configured_on_provider === "boolean") && v.available_scopes !== null && typeof v.available_scopes === "object" && !Array.isArray(v.available_scopes) && Object.values(v.available_scopes).every(
|
|
17793
|
+
)) && (v.scopes_configured_on_provider === void 0 || typeof v.scopes_configured_on_provider === "boolean") && (v.redirect_uri_alias === void 0 || v.redirect_uri_alias === null || isRedirectUriAliasSpec(v.redirect_uri_alias)) && v.available_scopes !== null && typeof v.available_scopes === "object" && !Array.isArray(v.available_scopes) && Object.values(v.available_scopes).every(
|
|
17569
17794
|
isProviderScopeCatalogEntry
|
|
17570
17795
|
) && (v.managed_credentials_available === void 0 || typeof v.managed_credentials_available === "boolean") && (v.managed_approved_scopes === void 0 || v.managed_approved_scopes === null || Array.isArray(v.managed_approved_scopes) && v.managed_approved_scopes.every(
|
|
17571
17796
|
(scope) => typeof scope === "string"
|
|
@@ -17660,7 +17885,8 @@ var ProvidersNamespace = class {
|
|
|
17660
17885
|
client_secret: options.client_secret,
|
|
17661
17886
|
environment: options.environment,
|
|
17662
17887
|
scopes: options.scopes,
|
|
17663
|
-
redirect_uris: options.redirect_uris
|
|
17888
|
+
redirect_uris: options.redirect_uris,
|
|
17889
|
+
redirect_uri_alias: options.redirect_uri_alias
|
|
17664
17890
|
});
|
|
17665
17891
|
const body = await this.#client._call(
|
|
17666
17892
|
"POST",
|
|
@@ -17697,6 +17923,7 @@ var ProvidersNamespace = class {
|
|
|
17697
17923
|
environment: options.environment,
|
|
17698
17924
|
scopes: options.scopes,
|
|
17699
17925
|
redirect_uris: options.redirect_uris,
|
|
17926
|
+
redirect_uri_alias: options.redirect_uri_alias,
|
|
17700
17927
|
status: options.status
|
|
17701
17928
|
});
|
|
17702
17929
|
const body = await this.#client._call(
|
|
@@ -19535,6 +19762,38 @@ function resolveAppId(flagValue, options = {}) {
|
|
|
19535
19762
|
return null;
|
|
19536
19763
|
}
|
|
19537
19764
|
|
|
19765
|
+
// ../shared-utils/src/dns-host.ts
|
|
19766
|
+
var LABEL_SEPARATOR = /[.。.。]/;
|
|
19767
|
+
var TRAILING_LABEL_SEPARATOR = /[.。.。]$/;
|
|
19768
|
+
var isInvalidLabel = (label) => label.length === 0 || label.includes("_") || label.startsWith("-") || label.endsWith("-") || label.length >= 4 && label[2] === "-" && label[3] === "-" && !label.startsWith("xn--");
|
|
19769
|
+
var IDNA2008_LABEL = /^(?:(?!\u0640|\u07FA|\u302E|\u302F|[\u3031-\u3035]|\u303B)[\p{L}\p{Mn}\p{Mc}\p{Nd}\-\u00DF\u03C2\u06FD\u06FE\u0F0B\u3007\u00B7\u0375\u05F3\u05F4\u30FB]|\u200C|\u200D)*$/u;
|
|
19770
|
+
var isOutsideIdna2008Repertoire = (label) => !IDNA2008_LABEL.test(label);
|
|
19771
|
+
var canonicalizeDnsHost = (host) => {
|
|
19772
|
+
let h = host;
|
|
19773
|
+
if (!/^[\x20-\x7e]*$/.test(h)) {
|
|
19774
|
+
if (/[/:@\s?#%\\]/.test(h)) return null;
|
|
19775
|
+
const uLabels = h.normalize("NFKC").replace(TRAILING_LABEL_SEPARATOR, "").split(LABEL_SEPARATOR);
|
|
19776
|
+
if (uLabels.some(isInvalidLabel)) return null;
|
|
19777
|
+
if (uLabels.some(isOutsideIdna2008Repertoire)) return null;
|
|
19778
|
+
let url2;
|
|
19779
|
+
try {
|
|
19780
|
+
url2 = new URL(`http://${h}`);
|
|
19781
|
+
} catch {
|
|
19782
|
+
return null;
|
|
19783
|
+
}
|
|
19784
|
+
if (url2.pathname !== "/" || url2.search !== "" || url2.hash !== "" || url2.port !== "" || url2.username !== "" || url2.password !== "") {
|
|
19785
|
+
return null;
|
|
19786
|
+
}
|
|
19787
|
+
h = url2.hostname;
|
|
19788
|
+
if (!/^[\x20-\x7e]*$/.test(h)) return null;
|
|
19789
|
+
h = h.replace(/\.$/, "");
|
|
19790
|
+
if (h.split(".").some(isInvalidLabel)) return null;
|
|
19791
|
+
}
|
|
19792
|
+
if (h.length > 253) return null;
|
|
19793
|
+
if (h.split(".").some((label) => label.length > 63)) return null;
|
|
19794
|
+
return h;
|
|
19795
|
+
};
|
|
19796
|
+
|
|
19538
19797
|
// src/commands/_helpers.ts
|
|
19539
19798
|
async function withClient(fn) {
|
|
19540
19799
|
const client = await createPortalClient();
|
|
@@ -20087,9 +20346,9 @@ function parseAllowedHostsOrExit(flag, entries) {
|
|
|
20087
20346
|
fail(`${JSON.stringify(entry)} must not contain control characters`);
|
|
20088
20347
|
}
|
|
20089
20348
|
}
|
|
20090
|
-
if (/[/:@\s]/.test(e)) {
|
|
20349
|
+
if (/[/:@\s?#%\\]/.test(e)) {
|
|
20091
20350
|
fail(
|
|
20092
|
-
`${JSON.stringify(entry)} must be a bare host \u2014 no scheme, port, path,
|
|
20351
|
+
`${JSON.stringify(entry)} must be a bare host \u2014 no scheme, port, path, "@", whitespace, or the URL delimiters ? # % \\`
|
|
20093
20352
|
);
|
|
20094
20353
|
}
|
|
20095
20354
|
let normalized;
|
|
@@ -20100,12 +20359,23 @@ function parseAllowedHostsOrExit(flag, entries) {
|
|
|
20100
20359
|
`${JSON.stringify(entry)} is a malformed wildcard \u2014 use "*.domain.tld"`
|
|
20101
20360
|
);
|
|
20102
20361
|
}
|
|
20103
|
-
|
|
20362
|
+
const canonicalRest = canonicalizeDnsHost(rest);
|
|
20363
|
+
if (canonicalRest === null) {
|
|
20364
|
+
fail(
|
|
20365
|
+
`${JSON.stringify(entry)} is not a valid DNS name (IDNA-encodable, labels up to 63 characters)`
|
|
20366
|
+
);
|
|
20367
|
+
}
|
|
20368
|
+
if (!canonicalRest.includes(".")) {
|
|
20104
20369
|
fail(
|
|
20105
20370
|
`${JSON.stringify(entry)}: wildcard must cover a domain, not a bare TLD (use "*.example.com", not "*.com")`
|
|
20106
20371
|
);
|
|
20107
20372
|
}
|
|
20108
|
-
|
|
20373
|
+
if (canonicalRest.length > 251) {
|
|
20374
|
+
fail(
|
|
20375
|
+
`${JSON.stringify(entry)} is too long for a wildcard \u2014 no host could ever match it`
|
|
20376
|
+
);
|
|
20377
|
+
}
|
|
20378
|
+
normalized = `*.${canonicalRest}`;
|
|
20109
20379
|
} else if (e.includes("*")) {
|
|
20110
20380
|
fail(
|
|
20111
20381
|
`${JSON.stringify(entry)}: "*" is only allowed as a leading subdomain wildcard ("*.domain.tld")`
|
|
@@ -20114,9 +20384,16 @@ function parseAllowedHostsOrExit(flag, entries) {
|
|
|
20114
20384
|
normalized = e.replace(/\.+$/, "");
|
|
20115
20385
|
if (!normalized) {
|
|
20116
20386
|
fail(
|
|
20117
|
-
`${JSON.stringify(entry)} must be a bare host \u2014 no scheme, port, path,
|
|
20387
|
+
`${JSON.stringify(entry)} must be a bare host \u2014 no scheme, port, path, "@", whitespace, or the URL delimiters ? # % \\`
|
|
20388
|
+
);
|
|
20389
|
+
}
|
|
20390
|
+
const canonical = canonicalizeDnsHost(normalized);
|
|
20391
|
+
if (canonical === null) {
|
|
20392
|
+
fail(
|
|
20393
|
+
`${JSON.stringify(entry)} is not a valid DNS name (IDNA-encodable, labels up to 63 characters)`
|
|
20118
20394
|
);
|
|
20119
20395
|
}
|
|
20396
|
+
normalized = canonical;
|
|
20120
20397
|
}
|
|
20121
20398
|
if (seen.has(normalized)) continue;
|
|
20122
20399
|
seen.add(normalized);
|
|
@@ -20372,6 +20649,20 @@ function surfaceCatalogWarnings(row) {
|
|
|
20372
20649
|
);
|
|
20373
20650
|
}
|
|
20374
20651
|
}
|
|
20652
|
+
function surfaceScopeWarnings(row) {
|
|
20653
|
+
if (typeof row !== "object" || row === null) return;
|
|
20654
|
+
const warnings = row.scope_warnings;
|
|
20655
|
+
if (!Array.isArray(warnings)) return;
|
|
20656
|
+
for (const warning of warnings) {
|
|
20657
|
+
if (typeof warning !== "string") continue;
|
|
20658
|
+
const sanitized = sanitizeStderrText(warning);
|
|
20659
|
+
if (sanitized.length === 0) continue;
|
|
20660
|
+
process.stderr.write(
|
|
20661
|
+
`alter: scope warning \u2014 ${sanitized} (save was allowed)
|
|
20662
|
+
`
|
|
20663
|
+
);
|
|
20664
|
+
}
|
|
20665
|
+
}
|
|
20375
20666
|
function writeNextPageHint(page, noun) {
|
|
20376
20667
|
if (typeof page !== "object" || page === null) return;
|
|
20377
20668
|
const { has_more: hasMore, offset, limit } = page;
|
|
@@ -20534,7 +20825,9 @@ function buildAgentsCommand() {
|
|
|
20534
20825
|
});
|
|
20535
20826
|
}
|
|
20536
20827
|
);
|
|
20537
|
-
agents.command("create").description(
|
|
20828
|
+
agents.command("create").description(
|
|
20829
|
+
"Create a managed agent (returns its first API key ONCE, in the api_key field)"
|
|
20830
|
+
).option(APP_FLAG, APP_TARGET_DESC).option(
|
|
20538
20831
|
"--name <name>",
|
|
20539
20832
|
"Agent name (stable identifier). Required unless --input is supplied."
|
|
20540
20833
|
).option("--display-name <name>", "Human-readable display name").option("--type <type>", "Type: agent|service (default: agent)", "agent").option(
|
|
@@ -20566,6 +20859,7 @@ function buildAgentsCommand() {
|
|
|
20566
20859
|
"alter: the api_key field above is shown ONCE and cannot be retrieved later.\n"
|
|
20567
20860
|
);
|
|
20568
20861
|
surfaceApproverWarnings(result);
|
|
20862
|
+
surfaceScopeWarnings(result);
|
|
20569
20863
|
});
|
|
20570
20864
|
return;
|
|
20571
20865
|
}
|
|
@@ -20621,6 +20915,7 @@ function buildAgentsCommand() {
|
|
|
20621
20915
|
"alter: the api_key field above is shown ONCE and cannot be retrieved later.\n"
|
|
20622
20916
|
);
|
|
20623
20917
|
surfaceApproverWarnings(result);
|
|
20918
|
+
surfaceScopeWarnings(result);
|
|
20624
20919
|
});
|
|
20625
20920
|
}
|
|
20626
20921
|
);
|
|
@@ -20840,6 +21135,7 @@ function buildAgentsCommand() {
|
|
|
20840
21135
|
}
|
|
20841
21136
|
);
|
|
20842
21137
|
emit2(format, row);
|
|
21138
|
+
surfaceScopeWarnings(row);
|
|
20843
21139
|
} catch (error51) {
|
|
20844
21140
|
if (error51 instanceof PortalBackendError && error51.statusCode === 409 && error51.code === "agent_concurrent_update") {
|
|
20845
21141
|
throw new PortalBackendError(
|
|
@@ -20936,7 +21232,9 @@ function buildAgentsCommand() {
|
|
|
20936
21232
|
`);
|
|
20937
21233
|
});
|
|
20938
21234
|
});
|
|
20939
|
-
agents.command("mint-key").description(
|
|
21235
|
+
agents.command("mint-key").description(
|
|
21236
|
+
"Mint a fresh API key for the agent (plaintext returned ONCE, in the api_key field)"
|
|
21237
|
+
).option(APP_FLAG, APP_TARGET_DESC).requiredOption(
|
|
20940
21238
|
"--agent <agent-id>",
|
|
20941
21239
|
"Agent ID",
|
|
20942
21240
|
parseUuidArgument("--agent")
|
|
@@ -35908,7 +36206,7 @@ var RULE_TYPE_LABELS = /* @__PURE__ */ new Map([
|
|
|
35908
36206
|
["json_match", "Request condition"],
|
|
35909
36207
|
["ip_allowlist", "IP allowlist"],
|
|
35910
36208
|
["time_window", "Time window"],
|
|
35911
|
-
["require_approval", "
|
|
36209
|
+
["require_approval", "Human in the loop (HITL)"],
|
|
35912
36210
|
["restriction", "Method and endpoint allowlist"],
|
|
35913
36211
|
["quota", "Request quota"],
|
|
35914
36212
|
["content_match", "Operation and parameter policy"]
|
|
@@ -36334,10 +36632,13 @@ function buildPolicyRulePresentation(rule) {
|
|
|
36334
36632
|
});
|
|
36335
36633
|
}
|
|
36336
36634
|
if (rule.display?.inherited) {
|
|
36337
|
-
displayItems.push({
|
|
36635
|
+
displayItems.push({
|
|
36636
|
+
label: "Relationship",
|
|
36637
|
+
value: "Inherited runtime policy"
|
|
36638
|
+
});
|
|
36338
36639
|
}
|
|
36339
36640
|
if (displayItems.length > 0) {
|
|
36340
|
-
groups.unshift({ label: "
|
|
36641
|
+
groups.unshift({ label: "Runtime policy scope", items: displayItems });
|
|
36341
36642
|
}
|
|
36342
36643
|
return {
|
|
36343
36644
|
typeLabel: policyRuleTypeLabel(rule.rule_type),
|
|
@@ -36350,7 +36651,10 @@ function formatPolicyRuleAsText(rule) {
|
|
|
36350
36651
|
const lines = [
|
|
36351
36652
|
...rule.name ? [`Name: ${rule.name}`] : [],
|
|
36352
36653
|
...rule.id ? [`Policy ID: ${rule.id}`] : [],
|
|
36353
|
-
|
|
36654
|
+
// The friendly label matches the dashboard; the wire identifier is what
|
|
36655
|
+
// `alter policy rules create --type <rule_type>` accepts, so the text
|
|
36656
|
+
// view prints both for operators who script from it.
|
|
36657
|
+
`Type: ${presentation.typeLabel} [${rule.rule_type}]`,
|
|
36354
36658
|
`Effect: ${presentation.effectLabel}`,
|
|
36355
36659
|
`Status: ${rule.enabled === false ? "Disabled" : "Enabled"}`,
|
|
36356
36660
|
presentation.summary,
|
|
@@ -36865,7 +37169,7 @@ function buildAppsCommand() {
|
|
|
36865
37169
|
"table"
|
|
36866
37170
|
).option(
|
|
36867
37171
|
"--no-include-stats",
|
|
36868
|
-
"Skip per-app statistics (grant / key / API-call counts)"
|
|
37172
|
+
"Skip per-app statistics (grant / connection / secret / provider / key / API-call counts)"
|
|
36869
37173
|
).option(
|
|
36870
37174
|
"--include-archived",
|
|
36871
37175
|
"Include archived (soft-deleted) apps. Default is active-only \u2014 archive's whole point is to hide clutter from the default view, so opt in only when you want the full set (e.g. before unarchiving something)."
|
|
@@ -38012,7 +38316,25 @@ import { hostname as hostname3, platform as platform2 } from "os";
|
|
|
38012
38316
|
import { spawn } from "child_process";
|
|
38013
38317
|
var MIN_EPHEMERAL_PORT = 49152;
|
|
38014
38318
|
var MAX_EPHEMERAL_PORT = 65535;
|
|
38015
|
-
var LISTENER_TIMEOUT_MS =
|
|
38319
|
+
var LISTENER_TIMEOUT_MS = 6e5;
|
|
38320
|
+
function describeDeadline(ms) {
|
|
38321
|
+
if (ms >= 6e4 && ms % 6e4 === 0) {
|
|
38322
|
+
const minutes = ms / 6e4;
|
|
38323
|
+
return `${minutes} minute${minutes === 1 ? "" : "s"}`;
|
|
38324
|
+
}
|
|
38325
|
+
if (ms >= 1e3) {
|
|
38326
|
+
const seconds = Math.round(ms / 1e3);
|
|
38327
|
+
return `${seconds} second${seconds === 1 ? "" : "s"}`;
|
|
38328
|
+
}
|
|
38329
|
+
return `${ms} ms`;
|
|
38330
|
+
}
|
|
38331
|
+
var SELF_INSPECTION_SCOPE = "dashboard_pats:read";
|
|
38332
|
+
function withSelfInspectionScope(scopes) {
|
|
38333
|
+
if (scopes.includes(SELF_INSPECTION_SCOPE)) {
|
|
38334
|
+
return { scopes: [...scopes], added: false };
|
|
38335
|
+
}
|
|
38336
|
+
return { scopes: [...scopes, SELF_INSPECTION_SCOPE], added: true };
|
|
38337
|
+
}
|
|
38016
38338
|
var CALLBACK_STATE_PATTERN = /^[0-9a-f]{64}$/;
|
|
38017
38339
|
var DEFAULT_SCOPES = [
|
|
38018
38340
|
"dashboard_apps:read",
|
|
@@ -38090,7 +38412,9 @@ function pickEphemeralPort() {
|
|
|
38090
38412
|
}
|
|
38091
38413
|
async function runBrowserDance(options) {
|
|
38092
38414
|
const state = randomBytes2(32).toString("hex");
|
|
38093
|
-
const scopes
|
|
38415
|
+
const { scopes, added: addedSelfInspectionScope } = withSelfInspectionScope(
|
|
38416
|
+
options.scopes ?? DEFAULT_SCOPES
|
|
38417
|
+
);
|
|
38094
38418
|
const dashboardUrl = options.dashboardUrl ?? deriveDashboardUrl(options.baseUrl);
|
|
38095
38419
|
const openBrowser = options.openBrowser ?? defaultOpenBrowser;
|
|
38096
38420
|
const timeoutMs = options.timeoutMs ?? LISTENER_TIMEOUT_MS;
|
|
@@ -38145,7 +38469,7 @@ async function runBrowserDance(options) {
|
|
|
38145
38469
|
cleanupListener();
|
|
38146
38470
|
reject(
|
|
38147
38471
|
new Error(
|
|
38148
|
-
|
|
38472
|
+
`browser-dance timed out \u2014 operator did not complete the consent flow within ${describeDeadline(timeoutMs)}`
|
|
38149
38473
|
)
|
|
38150
38474
|
);
|
|
38151
38475
|
}, timeoutMs);
|
|
@@ -38303,9 +38627,15 @@ async function runBrowserDance(options) {
|
|
|
38303
38627
|
bindQuery = `&bind=cidr&cidr=${encodeURIComponent(options.cidrValue)}`;
|
|
38304
38628
|
}
|
|
38305
38629
|
const url2 = `${dashboardUrl}/cli-auth?port=${port}&state=${state}&scopes=${scopeQuery}&hostname=${encodeURIComponent(hostname3())}${bindQuery}`;
|
|
38630
|
+
if (addedSelfInspectionScope) {
|
|
38631
|
+
process.stderr.write(
|
|
38632
|
+
`alter: added ${SELF_INSPECTION_SCOPE} to the requested scopes \u2014 the CLI verifies the sign-in (and \`alter auth status\`) with it.
|
|
38633
|
+
`
|
|
38634
|
+
);
|
|
38635
|
+
}
|
|
38306
38636
|
process.stdout.write(
|
|
38307
38637
|
`alter: opening browser at ${dashboardUrl}/cli-auth
|
|
38308
|
-
(waiting up to
|
|
38638
|
+
(waiting up to ${describeDeadline(timeoutMs)} for you to approve)\u2026
|
|
38309
38639
|
`
|
|
38310
38640
|
);
|
|
38311
38641
|
openBrowser(url2);
|
|
@@ -38951,7 +39281,7 @@ function buildAuthCommand() {
|
|
|
38951
39281
|
"Override the dashboard URL used by the browser-dance flow. Defaults to the backend URL with the leftmost `backend.` subdomain swapped for `portal.`. Use this when the dashboard isn't at the regex-derivable host (e.g. staging / self-hosted setups). Must use https://."
|
|
38952
39282
|
).option(
|
|
38953
39283
|
"--scopes <list>",
|
|
38954
|
-
"Comma-separated scope list to request on the browser-dance consent page (e.g. 'dashboard_apps:read,dashboard_keys:write'). Defaults to the full read+write set across the CLI surface \u2014 enough for every non-destructive day-to-day command. Opt in to admin / delete tiers explicitly: 'dashboard_keys:admin' for runtime-key admin ops, 'dashboard_apps:admin' to archive/unarchive an app, and 'dashboard_apps:delete' for cascade-delete an app (note the Destructive-Action Policy keeps :delete as its own verb, separate from :admin \u2014 :admin does not grant the cascade). Each entry is validated client-side against the dashboard scope catalog \u2014 typos fail fast. Mutually exclusive with --token / --token-file / --token-stdin \u2014 pre-minted PATs have their scope set baked at mint time on the dashboard's Settings page; combining the flags is a usage error."
|
|
39284
|
+
"Comma-separated scope list to request on the browser-dance consent page (e.g. 'dashboard_apps:read,dashboard_keys:write'). Defaults to the full read+write set across the CLI surface \u2014 enough for every non-destructive day-to-day command. Opt in to admin / delete tiers explicitly: 'dashboard_keys:admin' for runtime-key admin ops, 'dashboard_apps:admin' to archive/unarchive an app, and 'dashboard_apps:delete' for cascade-delete an app (note the Destructive-Action Policy keeps :delete as its own verb, separate from :admin \u2014 :admin does not grant the cascade). The list REPLACES the default set, except that dashboard_pats:read is always added: the CLI verifies every sign-in (and `alter auth status`) with it, so a PAT without it cannot complete the login. Each entry is validated client-side against the dashboard scope catalog \u2014 typos fail fast. Mutually exclusive with --token / --token-file / --token-stdin \u2014 pre-minted PATs have their scope set baked at mint time on the dashboard's Settings page; combining the flags is a usage error."
|
|
38955
39285
|
).option(
|
|
38956
39286
|
"--no-bind-ip",
|
|
38957
39287
|
"Explicit opt-out of IP binding. This is now the DEFAULT for the browser-dance flow, so the flag is a no-op alias retained for scripts that pass it explicitly. The resulting PAT works from any source IP. Browser-dance flow only."
|
|
@@ -40696,7 +41026,9 @@ function buildKeysCommand() {
|
|
|
40696
41026
|
emit2(format, row);
|
|
40697
41027
|
});
|
|
40698
41028
|
});
|
|
40699
|
-
keys.command("mint").description(
|
|
41029
|
+
keys.command("mint").description(
|
|
41030
|
+
"Mint a new scoped key (plaintext returned ONCE, in the plain_key field \u2014 api_key is the key's metadata; capture it with --output json --fields plain_key or jq -r .plain_key)"
|
|
41031
|
+
).option(APP_FLAG, APP_TARGET_DESC).option(
|
|
40700
41032
|
"--name <name>",
|
|
40701
41033
|
"Display name. Required unless --input is supplied."
|
|
40702
41034
|
).option(
|
|
@@ -40758,7 +41090,7 @@ function buildKeysCommand() {
|
|
|
40758
41090
|
}
|
|
40759
41091
|
emit2(format, outcome2.result);
|
|
40760
41092
|
process.stderr.write(
|
|
40761
|
-
"alter: the
|
|
41093
|
+
"alter: the plain_key field above is the secret (api_key is metadata); it is shown ONCE and cannot be retrieved later.\n"
|
|
40762
41094
|
);
|
|
40763
41095
|
return;
|
|
40764
41096
|
}
|
|
@@ -40851,7 +41183,7 @@ function buildKeysCommand() {
|
|
|
40851
41183
|
}
|
|
40852
41184
|
emit2(format, outcome.result);
|
|
40853
41185
|
process.stderr.write(
|
|
40854
|
-
"alter: the
|
|
41186
|
+
"alter: the plain_key field above is the secret (api_key is metadata); it is shown ONCE and cannot be retrieved later.\n"
|
|
40855
41187
|
);
|
|
40856
41188
|
}
|
|
40857
41189
|
);
|
|
@@ -40868,7 +41200,7 @@ function buildKeysCommand() {
|
|
|
40868
41200
|
});
|
|
40869
41201
|
});
|
|
40870
41202
|
keys.command("rotate").description(
|
|
40871
|
-
"Rotate a key (new plaintext returned ONCE; old key enters grace until revoke). The app-key successor receives the organization's current finite default TTL. Requires dashboard_keys:write scope."
|
|
41203
|
+
"Rotate a key (new plaintext returned ONCE, in the new_plain_key field; old key enters grace until revoke). The app-key successor receives the organization's current finite default TTL. Requires dashboard_keys:write scope."
|
|
40872
41204
|
).option(APP_FLAG, APP_TARGET_DESC).requiredOption("--key <key-id>", "Key ID", parseUuidArgument("--key")).option(
|
|
40873
41205
|
"--scopes <list>",
|
|
40874
41206
|
"Optional new scope set (defaults to the key's current scopes)"
|
|
@@ -40893,7 +41225,7 @@ function buildKeysCommand() {
|
|
|
40893
41225
|
});
|
|
40894
41226
|
emit2(format, result);
|
|
40895
41227
|
process.stderr.write(
|
|
40896
|
-
"alter: the new
|
|
41228
|
+
"alter: the new_plain_key field above is the new secret (new_api_key is metadata); it is shown ONCE and cannot be retrieved later.\n"
|
|
40897
41229
|
);
|
|
40898
41230
|
});
|
|
40899
41231
|
}
|
|
@@ -40902,7 +41234,7 @@ function buildKeysCommand() {
|
|
|
40902
41234
|
"Revoke a key immediately (cascades to derived keys). Requires dashboard_keys:admin scope."
|
|
40903
41235
|
).option(APP_FLAG, APP_TARGET_DESC).requiredOption("--key <key-id>", "Key ID", parseUuidArgument("--key")).option(
|
|
40904
41236
|
"--force",
|
|
40905
|
-
"Override the last-active-key guard (revoking an agent
|
|
41237
|
+
"Override the last-active-key guard (revoking the last active key on an app or an agent leaves whatever authenticates with it broken until a fresh key is minted)"
|
|
40906
41238
|
).option("--yes", "Skip the interactive y/N prompt").action(
|
|
40907
41239
|
async (options) => {
|
|
40908
41240
|
const resolvedAppId = await resolveAppOrExit(options.app);
|
|
@@ -41066,6 +41398,7 @@ async function runStatus() {
|
|
|
41066
41398
|
// src/commands/managed-secrets.ts
|
|
41067
41399
|
import { readFileSync as readFileSync9 } from "fs";
|
|
41068
41400
|
import { Command as Command16, Option } from "commander";
|
|
41401
|
+
var MAX_SEARCH_LENGTH = 255;
|
|
41069
41402
|
var PRINCIPAL_TYPES = ["user", "group", "system", "agent"];
|
|
41070
41403
|
var CREDENTIAL_TYPES = [
|
|
41071
41404
|
"bearer_token",
|
|
@@ -41470,6 +41803,13 @@ function surfaceManagedSecretResponse(row) {
|
|
|
41470
41803
|
}
|
|
41471
41804
|
surfaceApproverWarnings(row);
|
|
41472
41805
|
}
|
|
41806
|
+
function derivedAllowlistMessage(e) {
|
|
41807
|
+
if (!(e instanceof PortalBackendError) || e.statusCode !== 409 || e.code !== "managed_secret_allowed_hosts_derived") {
|
|
41808
|
+
return null;
|
|
41809
|
+
}
|
|
41810
|
+
const detail = e.body?.detail;
|
|
41811
|
+
return typeof detail?.message === "string" ? sanitizeStderrText(detail.message) : "This secret's allowed hosts are derived from its provider binding and cannot be edited directly.";
|
|
41812
|
+
}
|
|
41473
41813
|
function groupPrincipalGateMessage(e) {
|
|
41474
41814
|
if (!(e instanceof PortalBackendError) || e.statusCode !== 422 || e.code !== "group_principal_unsupported_idp") {
|
|
41475
41815
|
return null;
|
|
@@ -41806,7 +42146,11 @@ var TEMPLATE_COLUMNS = [
|
|
|
41806
42146
|
get: (t) => t.form_schema?.fields.map(
|
|
41807
42147
|
(field) => `${credentialFieldFlag(field)}${field.required ? "" : " (optional)"}`
|
|
41808
42148
|
).join(" ") || "\u2014"
|
|
41809
|
-
}
|
|
42149
|
+
},
|
|
42150
|
+
// The field a bound template derives its allowlist from — the operator
|
|
42151
|
+
// needs it to know which --credential-field sets (and later retargets,
|
|
42152
|
+
// via rotate) the secret's allowed hosts.
|
|
42153
|
+
{ label: "BINDING", get: (t) => t.allowed_hosts_from_field ?? "\u2014" }
|
|
41810
42154
|
];
|
|
41811
42155
|
var SECRET_COLUMNS = [
|
|
41812
42156
|
{ label: "ID", get: (s) => s.id, maxWidth: 36 },
|
|
@@ -41818,10 +42162,20 @@ var SECRET_COLUMNS = [
|
|
|
41818
42162
|
{ label: "CREATED", get: (s) => s.created_at },
|
|
41819
42163
|
{ label: "UPDATED", get: (s) => s.updated_at ?? "\u2014" }
|
|
41820
42164
|
];
|
|
42165
|
+
var DEPTH_COLUMN = {
|
|
42166
|
+
label: "DEPTH",
|
|
42167
|
+
get: (g) => g.depth == null ? "?" : String(g.depth)
|
|
42168
|
+
};
|
|
42169
|
+
var PARENT_COLUMN = {
|
|
42170
|
+
label: "PARENT",
|
|
42171
|
+
get: (g) => g.parent_grant_id === void 0 ? "?" : g.parent_grant_id ?? "\u2014",
|
|
42172
|
+
maxWidth: 36
|
|
42173
|
+
};
|
|
41821
42174
|
var GRANT_COLUMNS2 = [
|
|
41822
42175
|
{ label: "GRANT_ID", get: (g) => g.grant_id, maxWidth: 36 },
|
|
41823
42176
|
{ label: "PRINCIPAL", get: (g) => g.principal_type },
|
|
41824
42177
|
{ label: "LABEL", get: (g) => g.label ?? "\u2014", maxWidth: 24 },
|
|
42178
|
+
DEPTH_COLUMN,
|
|
41825
42179
|
{ label: "STATUS", get: (g) => g.status },
|
|
41826
42180
|
{ label: "CREATED", get: (g) => g.created_at },
|
|
41827
42181
|
{
|
|
@@ -41833,6 +42187,9 @@ var GRANT_COLUMNS2 = [
|
|
|
41833
42187
|
get: (g) => g.grant_expires_at ?? "\u2014"
|
|
41834
42188
|
}
|
|
41835
42189
|
];
|
|
42190
|
+
var AGENT_GRANT_COLUMNS = GRANT_COLUMNS2.flatMap(
|
|
42191
|
+
(col) => col === DEPTH_COLUMN ? [col, PARENT_COLUMN] : [col]
|
|
42192
|
+
);
|
|
41836
42193
|
var USER_COLUMNS = [
|
|
41837
42194
|
{ label: "ID", get: (u) => u.id, maxWidth: 36 },
|
|
41838
42195
|
{ label: "EMAIL", get: (u) => u.email ?? "\u2014" },
|
|
@@ -41909,7 +42266,9 @@ function buildGrantsSubcommand() {
|
|
|
41909
42266
|
label: isOptionalString,
|
|
41910
42267
|
created_at: isString,
|
|
41911
42268
|
expires_at: isOptionalString,
|
|
41912
|
-
grant_expires_at: isAbsentOrOptionalString
|
|
42269
|
+
grant_expires_at: isAbsentOrOptionalString,
|
|
42270
|
+
parent_grant_id: isAbsentOrOptionalString,
|
|
42271
|
+
depth: isAbsentOrOptionalNumber
|
|
41913
42272
|
},
|
|
41914
42273
|
"managed-secrets.grants.list"
|
|
41915
42274
|
);
|
|
@@ -41957,11 +42316,14 @@ function buildGrantsSubcommand() {
|
|
|
41957
42316
|
label: isOptionalString,
|
|
41958
42317
|
created_at: isString,
|
|
41959
42318
|
expires_at: isOptionalString,
|
|
41960
|
-
grant_expires_at: isAbsentOrOptionalString
|
|
42319
|
+
grant_expires_at: isAbsentOrOptionalString,
|
|
42320
|
+
// Rendered through ``AGENT_GRANT_COLUMNS`` (adds PARENT).
|
|
42321
|
+
parent_grant_id: isAbsentOrOptionalString,
|
|
42322
|
+
depth: isAbsentOrOptionalNumber
|
|
41961
42323
|
},
|
|
41962
42324
|
"managed-secrets.grants.list-for-agent"
|
|
41963
42325
|
);
|
|
41964
|
-
emit2(format, rows,
|
|
42326
|
+
emit2(format, rows, AGENT_GRANT_COLUMNS);
|
|
41965
42327
|
});
|
|
41966
42328
|
}
|
|
41967
42329
|
);
|
|
@@ -42275,7 +42637,7 @@ function buildGroupsSubcommand() {
|
|
|
42275
42637
|
);
|
|
42276
42638
|
groups.command("list").description("List app-user-groups (Group-tab picker source)").option(APP_FLAG, APP_TARGET_DESC).option("--idp <uuid>", "Filter by identity provider id").option(
|
|
42277
42639
|
"--search <substring>",
|
|
42278
|
-
"Substring search on group name / external_group_id"
|
|
42640
|
+
"Substring search on group name / external_group_id (max 255 chars)"
|
|
42279
42641
|
).option(
|
|
42280
42642
|
"--limit <n>",
|
|
42281
42643
|
"Page size (default: 50)",
|
|
@@ -42292,6 +42654,12 @@ function buildGroupsSubcommand() {
|
|
|
42292
42654
|
async (options) => {
|
|
42293
42655
|
const format = coerceOutputFormat(options.output);
|
|
42294
42656
|
if (options.idp !== void 0) validateUuidOrExit("--idp", options.idp);
|
|
42657
|
+
if (options.search !== void 0)
|
|
42658
|
+
validateMaxLengthOrExit(
|
|
42659
|
+
"--search",
|
|
42660
|
+
options.search,
|
|
42661
|
+
MAX_SEARCH_LENGTH
|
|
42662
|
+
);
|
|
42295
42663
|
const resolvedAppId = await resolveAppOrExit(options.app);
|
|
42296
42664
|
await withClient(async (client) => {
|
|
42297
42665
|
const response = await client.managedSecrets.listGroups(
|
|
@@ -42404,7 +42772,8 @@ function buildManagedSecretsCommand() {
|
|
|
42404
42772
|
credential_type: isString,
|
|
42405
42773
|
category: isOptionalString,
|
|
42406
42774
|
popular: (v) => typeof v === "boolean",
|
|
42407
|
-
form_schema: isTemplateFormSchema
|
|
42775
|
+
form_schema: isTemplateFormSchema,
|
|
42776
|
+
allowed_hosts_from_field: isAbsentOrOptionalString
|
|
42408
42777
|
},
|
|
42409
42778
|
"managed-secrets.templates"
|
|
42410
42779
|
);
|
|
@@ -42788,11 +43157,25 @@ function buildManagedSecretsCommand() {
|
|
|
42788
43157
|
}
|
|
42789
43158
|
const hostList = options.clear ? null : parseAllowedHostsOrExit("--host", options.host);
|
|
42790
43159
|
await withClient(async (client) => {
|
|
42791
|
-
|
|
42792
|
-
|
|
42793
|
-
|
|
42794
|
-
|
|
42795
|
-
|
|
43160
|
+
let row;
|
|
43161
|
+
try {
|
|
43162
|
+
row = await client.managedSecrets.setAllowedHosts(
|
|
43163
|
+
appId,
|
|
43164
|
+
secretId,
|
|
43165
|
+
hostList
|
|
43166
|
+
);
|
|
43167
|
+
} catch (e) {
|
|
43168
|
+
const derived = derivedAllowlistMessage(e);
|
|
43169
|
+
if (derived !== null) {
|
|
43170
|
+
process.stderr.write(`alter: ${derived}
|
|
43171
|
+
`);
|
|
43172
|
+
process.stderr.write(
|
|
43173
|
+
"alter: rotate the credential (`alter managed-secrets rotate`) to change its binding\n"
|
|
43174
|
+
);
|
|
43175
|
+
process.exit(EXIT_CONFLICT);
|
|
43176
|
+
}
|
|
43177
|
+
throw e;
|
|
43178
|
+
}
|
|
42796
43179
|
emit2(format, row);
|
|
42797
43180
|
if (options.clear) {
|
|
42798
43181
|
process.stderr.write(
|
|
@@ -43078,7 +43461,7 @@ function buildManagedSecretsCommand() {
|
|
|
43078
43461
|
);
|
|
43079
43462
|
root.command("users").description("List app-users (User-tab picker source for grants create)").option(APP_FLAG, APP_TARGET_DESC).option("--idp <uuid>", "Filter by identity provider id").option(
|
|
43080
43463
|
"--search <substring>",
|
|
43081
|
-
"Substring search on email / display_name / external_subject_id"
|
|
43464
|
+
"Substring search on email / display_name / external_subject_id (max 255 chars)"
|
|
43082
43465
|
).option(
|
|
43083
43466
|
"--limit <n>",
|
|
43084
43467
|
"Page size (default: 50)",
|
|
@@ -43095,6 +43478,12 @@ function buildManagedSecretsCommand() {
|
|
|
43095
43478
|
async (options) => {
|
|
43096
43479
|
const format = coerceOutputFormat(options.output);
|
|
43097
43480
|
if (options.idp !== void 0) validateUuidOrExit("--idp", options.idp);
|
|
43481
|
+
if (options.search !== void 0)
|
|
43482
|
+
validateMaxLengthOrExit(
|
|
43483
|
+
"--search",
|
|
43484
|
+
options.search,
|
|
43485
|
+
MAX_SEARCH_LENGTH
|
|
43486
|
+
);
|
|
43098
43487
|
const resolvedAppId = await resolveAppOrExit(options.app);
|
|
43099
43488
|
await withClient(async (client) => {
|
|
43100
43489
|
const response = await client.managedSecrets.listUsers(
|
|
@@ -43418,7 +43807,9 @@ function buildIdentityProvidersCommand() {
|
|
|
43418
43807
|
const webhook = idp.command("webhook").description(
|
|
43419
43808
|
"Manage an identity provider's webhook integration (requires dashboard_identity_providers:webhooks)"
|
|
43420
43809
|
);
|
|
43421
|
-
webhook.command("enable").description(
|
|
43810
|
+
webhook.command("enable").description(
|
|
43811
|
+
"Enable webhook integration (returns the signing secret ONCE, in the webhook_secret field)"
|
|
43812
|
+
).option(APP_FLAG, APP_TARGET_DESC).requiredOption(
|
|
43422
43813
|
"--provider <provider-id>",
|
|
43423
43814
|
"Identity provider ID",
|
|
43424
43815
|
parseUuidArgument("--provider")
|
|
@@ -43450,7 +43841,7 @@ function buildIdentityProvidersCommand() {
|
|
|
43450
43841
|
emit2(format, row);
|
|
43451
43842
|
if (typeof row["webhook_secret"] === "string") {
|
|
43452
43843
|
process.stderr.write(
|
|
43453
|
-
"alter: the webhook_secret above is shown ONCE and cannot be retrieved later.\n"
|
|
43844
|
+
"alter: the webhook_secret field above is shown ONCE and cannot be retrieved later.\n"
|
|
43454
43845
|
);
|
|
43455
43846
|
}
|
|
43456
43847
|
return null;
|
|
@@ -43570,7 +43961,7 @@ alter: re-run with --force --confirm <issuer-host> to revoke those grants and di
|
|
|
43570
43961
|
}
|
|
43571
43962
|
);
|
|
43572
43963
|
webhook.command("rotate").description(
|
|
43573
|
-
"Rotate the webhook signing secret (invalidates the previous; returns the new one ONCE)"
|
|
43964
|
+
"Rotate the webhook signing secret (invalidates the previous; returns the new one ONCE, in the webhook_secret field)"
|
|
43574
43965
|
).option(APP_FLAG, APP_TARGET_DESC).requiredOption(
|
|
43575
43966
|
"--provider <provider-id>",
|
|
43576
43967
|
"Identity provider ID",
|
|
@@ -43618,7 +44009,7 @@ alter: re-run with --force --confirm <issuer-host> to revoke those grants and di
|
|
|
43618
44009
|
emit2(format, row);
|
|
43619
44010
|
if (typeof row["webhook_secret"] === "string") {
|
|
43620
44011
|
process.stderr.write(
|
|
43621
|
-
"alter: the webhook_secret above is shown ONCE and cannot be retrieved later.\n"
|
|
44012
|
+
"alter: the webhook_secret field above is shown ONCE and cannot be retrieved later.\n"
|
|
43622
44013
|
);
|
|
43623
44014
|
}
|
|
43624
44015
|
return null;
|
|
@@ -45019,8 +45410,12 @@ function buildPolicyCommand() {
|
|
|
45019
45410
|
import { Command as Command20 } from "commander";
|
|
45020
45411
|
var CREDENTIAL_SOURCES = ["custom", "shared_dev"];
|
|
45021
45412
|
var PROVIDER_ENVIRONMENTS = ["production", "sandbox"];
|
|
45022
|
-
async function providerCatalogError(client, providerId, environment, credentialSource, scopes,
|
|
45023
|
-
|
|
45413
|
+
async function providerCatalogError(client, providerId, environment, credentialSource, scopes, options = {}) {
|
|
45414
|
+
const create = options.create === true;
|
|
45415
|
+
const redirectUriAlias = options.redirectUriAlias;
|
|
45416
|
+
const readStoredProvider = options.readStoredProvider;
|
|
45417
|
+
const switchesToCustom = !create && credentialSource === "custom" && readStoredProvider !== void 0;
|
|
45418
|
+
if (environment === void 0 && scopes === void 0 && redirectUriAlias === void 0 && !create && !switchesToCustom) {
|
|
45024
45419
|
return null;
|
|
45025
45420
|
}
|
|
45026
45421
|
if (!providerId) {
|
|
@@ -45042,6 +45437,23 @@ async function providerCatalogError(client, providerId, environment, credentialS
|
|
|
45042
45437
|
if (environment !== void 0 && credentialSource === "shared_dev" && environment !== "production") {
|
|
45043
45438
|
return "alter: --credential-source shared_dev only supports --environment production; use custom sandbox credentials\n";
|
|
45044
45439
|
}
|
|
45440
|
+
const aliasSpec = provider.redirect_uri_alias ?? null;
|
|
45441
|
+
const aliasError = redirectUriAliasError(
|
|
45442
|
+
providerId,
|
|
45443
|
+
aliasSpec,
|
|
45444
|
+
credentialSource,
|
|
45445
|
+
redirectUriAlias,
|
|
45446
|
+
create
|
|
45447
|
+
);
|
|
45448
|
+
if (aliasError !== null) return aliasError;
|
|
45449
|
+
if (switchesToCustom && readStoredProvider !== void 0 && aliasSpec !== null && redirectUriAlias === void 0) {
|
|
45450
|
+
const storedAliasError = await redirectUriAliasStoredStateError(
|
|
45451
|
+
readStoredProvider,
|
|
45452
|
+
providerId,
|
|
45453
|
+
aliasSpec
|
|
45454
|
+
);
|
|
45455
|
+
if (storedAliasError !== null) return storedAliasError;
|
|
45456
|
+
}
|
|
45045
45457
|
const availableScopes = provider.available_scopes ?? {};
|
|
45046
45458
|
if (Object.keys(availableScopes).length === 0) {
|
|
45047
45459
|
if (scopes !== void 0 && scopes.length > 0) {
|
|
@@ -45051,7 +45463,7 @@ async function providerCatalogError(client, providerId, environment, credentialS
|
|
|
45051
45463
|
}
|
|
45052
45464
|
return null;
|
|
45053
45465
|
}
|
|
45054
|
-
if (
|
|
45466
|
+
if (create && scopes === void 0) {
|
|
45055
45467
|
return `alter: provider '${providerId}' requires at least one scope (pass --scopes, or "scopes" in --input)
|
|
45056
45468
|
`;
|
|
45057
45469
|
}
|
|
@@ -45082,6 +45494,41 @@ async function providerCatalogError(client, providerId, environment, credentialS
|
|
|
45082
45494
|
}
|
|
45083
45495
|
return null;
|
|
45084
45496
|
}
|
|
45497
|
+
function redirectUriAliasError(providerId, spec, credentialSource, redirectUriAlias, create) {
|
|
45498
|
+
if (spec === null) {
|
|
45499
|
+
return redirectUriAlias === void 0 ? null : `alter: --redirect-uri-alias is not used by ${providerId}
|
|
45500
|
+
`;
|
|
45501
|
+
}
|
|
45502
|
+
if (redirectUriAlias !== void 0 && credentialSource === "shared_dev") {
|
|
45503
|
+
return `alter: --redirect-uri-alias is only accepted with --credential-source custom (Alter-managed credentials carry their own ${spec.label})
|
|
45504
|
+
`;
|
|
45505
|
+
}
|
|
45506
|
+
const custom3 = credentialSource === void 0 || credentialSource === "custom";
|
|
45507
|
+
if (create && custom3 && redirectUriAlias === void 0) {
|
|
45508
|
+
return redirectUriAliasRequiredError(providerId, spec);
|
|
45509
|
+
}
|
|
45510
|
+
return null;
|
|
45511
|
+
}
|
|
45512
|
+
function redirectUriAliasRequiredError(providerId, spec) {
|
|
45513
|
+
return `alter: ${providerId} requires --redirect-uri-alias (${spec.label}): ${spec.description} (pass --redirect-uri-alias, or "redirect_uri_alias" in --input)
|
|
45514
|
+
`;
|
|
45515
|
+
}
|
|
45516
|
+
async function redirectUriAliasStoredStateError(readStoredProvider, providerId, spec) {
|
|
45517
|
+
let current;
|
|
45518
|
+
try {
|
|
45519
|
+
const row = await readStoredProvider();
|
|
45520
|
+
if (row === null || typeof row !== "object") return null;
|
|
45521
|
+
current = row;
|
|
45522
|
+
} catch {
|
|
45523
|
+
return null;
|
|
45524
|
+
}
|
|
45525
|
+
const storedSource = typeof current.credential_source === "string" ? current.credential_source : "custom";
|
|
45526
|
+
const storedAlias = typeof current.redirect_uri_alias === "string" ? current.redirect_uri_alias.trim() : "";
|
|
45527
|
+
if (storedSource === "shared_dev" || storedAlias.length === 0) {
|
|
45528
|
+
return redirectUriAliasRequiredError(providerId, spec);
|
|
45529
|
+
}
|
|
45530
|
+
return null;
|
|
45531
|
+
}
|
|
45085
45532
|
function storedProviderReader(client, appId, providerId) {
|
|
45086
45533
|
let pending2 = null;
|
|
45087
45534
|
return () => pending2 ??= client.providers.get(appId, providerId);
|
|
@@ -45167,6 +45614,16 @@ var PROVIDER_COLUMNS = [
|
|
|
45167
45614
|
// between test money and real money.
|
|
45168
45615
|
{ label: "ENV", get: (p) => p.environment ?? "production" },
|
|
45169
45616
|
{ label: "SCOPES", get: (p) => p.scopes.join(",") || "\u2014", maxWidth: 40 },
|
|
45617
|
+
// The provider-issued redirect name (eBay's RuName). Its provider-side
|
|
45618
|
+
// label is on ``list-catalog``'s REDIRECT ALIAS column. Wide enough for a
|
|
45619
|
+
// real RuName (``<user>-<app>-<title>-<token>``, ~40–60 chars) to print
|
|
45620
|
+
// untruncated — an operator copies this column back into a provider
|
|
45621
|
+
// console, so a clipped value is worse than none.
|
|
45622
|
+
{
|
|
45623
|
+
label: "REDIRECT ALIAS",
|
|
45624
|
+
get: (p) => p.redirect_uri_alias ?? "\u2014",
|
|
45625
|
+
maxWidth: 60
|
|
45626
|
+
},
|
|
45170
45627
|
{ label: "GRANTS", get: (p) => String(p.grants_count) },
|
|
45171
45628
|
{ label: "STATUS", get: (p) => p.status },
|
|
45172
45629
|
{
|
|
@@ -45250,6 +45707,22 @@ function collectRedirectUris(value, previous = []) {
|
|
|
45250
45707
|
const next = value.split(",").map((s) => s.trim()).filter(Boolean);
|
|
45251
45708
|
return [...previous, ...next];
|
|
45252
45709
|
}
|
|
45710
|
+
function validateRedirectUriAliasOrExit(raw, source = "--redirect-uri-alias") {
|
|
45711
|
+
if (raw === void 0) return void 0;
|
|
45712
|
+
const value = raw.trim();
|
|
45713
|
+
if (value.length === 0) {
|
|
45714
|
+
process.stderr.write(`alter: ${source} requires a non-empty value
|
|
45715
|
+
`);
|
|
45716
|
+
process.exit(EXIT_USAGE);
|
|
45717
|
+
}
|
|
45718
|
+
if (value.length > 255) {
|
|
45719
|
+
process.stderr.write(`alter: ${source} must be at most 255 characters
|
|
45720
|
+
`);
|
|
45721
|
+
process.exit(EXIT_USAGE);
|
|
45722
|
+
}
|
|
45723
|
+
return value;
|
|
45724
|
+
}
|
|
45725
|
+
var INPUT_ALIAS_SOURCE = '"redirect_uri_alias" in --input';
|
|
45253
45726
|
function surfaceProviderResponse(row) {
|
|
45254
45727
|
if (typeof row !== "object" || row === null) return;
|
|
45255
45728
|
const r = row;
|
|
@@ -45319,6 +45792,7 @@ function buildProvidersCommand() {
|
|
|
45319
45792
|
// "production" — defeating the tolerance this line exists for.
|
|
45320
45793
|
environment: isAbsentOrOptionalString,
|
|
45321
45794
|
scopes: (v) => Array.isArray(v),
|
|
45795
|
+
redirect_uri_alias: isAbsentOrOptionalString,
|
|
45322
45796
|
status: isString,
|
|
45323
45797
|
grants_count: (v) => typeof v === "number"
|
|
45324
45798
|
},
|
|
@@ -45367,6 +45841,13 @@ function buildProvidersCommand() {
|
|
|
45367
45841
|
get: (p) => p.managed_approved_scopes === void 0 ? "?" : p.managed_approved_scopes === null ? "\u2014" : p.managed_approved_scopes.join(",") || "(none)",
|
|
45368
45842
|
maxWidth: 36
|
|
45369
45843
|
},
|
|
45844
|
+
// Providers that take a provider-issued NAME as redirect_uri
|
|
45845
|
+
// instead of the callback URL show its label (eBay: RuName) —
|
|
45846
|
+
// ``create`` then requires ``--redirect-uri-alias``.
|
|
45847
|
+
{
|
|
45848
|
+
label: "REDIRECT ALIAS",
|
|
45849
|
+
get: (p) => p.redirect_uri_alias?.label ?? "\u2014"
|
|
45850
|
+
},
|
|
45370
45851
|
{ label: "STATUS", get: (p) => p.status }
|
|
45371
45852
|
]);
|
|
45372
45853
|
});
|
|
@@ -45395,9 +45876,12 @@ function buildProvidersCommand() {
|
|
|
45395
45876
|
"--redirect-uri <uri>",
|
|
45396
45877
|
"Add a redirect URI. Repeat the flag (``--redirect-uri A --redirect-uri B``) or pass a comma-separated list (``--redirect-uri A,B``); both forms accumulate.",
|
|
45397
45878
|
collectRedirectUris
|
|
45879
|
+
).option(
|
|
45880
|
+
"--redirect-uri-alias <name>",
|
|
45881
|
+
"The provider-issued redirect name sent as redirect_uri in place of the callback URL \u2014 required for providers that use one (eBay's RuName); see `alter providers list-catalog`."
|
|
45398
45882
|
).option(
|
|
45399
45883
|
"--input <path>",
|
|
45400
|
-
"JSON wire body from file (@/path/to/body.json) or stdin (-). Mirrors ``gh api --input``. Replaces the per-field flags (``--provider``, ``--credential-source``, ``--client-id``, ``--client-secret``, ``--environment``, ``--scopes``, ``--redirect-uri``); the body is sent to ``POST /apps/<app>/providers`` verbatim. If both --input and per-field flags are supplied, --input wins and the per-field flags are ignored (with a warning)."
|
|
45884
|
+
"JSON wire body from file (@/path/to/body.json) or stdin (-). Mirrors ``gh api --input``. Replaces the per-field flags (``--provider``, ``--credential-source``, ``--client-id``, ``--client-secret``, ``--environment``, ``--scopes``, ``--redirect-uri``, ``--redirect-uri-alias``); the body is sent to ``POST /apps/<app>/providers`` verbatim. If both --input and per-field flags are supplied, --input wins and the per-field flags are ignored (with a warning)."
|
|
45401
45885
|
).option(
|
|
45402
45886
|
"--output <format>",
|
|
45403
45887
|
"Output format: json|jsonl|table (default: json)",
|
|
@@ -45415,7 +45899,8 @@ function buildProvidersCommand() {
|
|
|
45415
45899
|
"clientSecret",
|
|
45416
45900
|
"environment",
|
|
45417
45901
|
"scopes",
|
|
45418
|
-
"redirectUri"
|
|
45902
|
+
"redirectUri",
|
|
45903
|
+
"redirectUriAlias"
|
|
45419
45904
|
].filter((k) => {
|
|
45420
45905
|
if (k === "credentialSource")
|
|
45421
45906
|
return options.credentialSource !== "custom";
|
|
@@ -45424,6 +45909,10 @@ function buildProvidersCommand() {
|
|
|
45424
45909
|
return options[k] !== void 0;
|
|
45425
45910
|
});
|
|
45426
45911
|
warnInputOverridesPerFieldFlags(perField.map(optionKeyToFlag));
|
|
45912
|
+
const bodyRedirectUriAlias = validateRedirectUriAliasOrExit(
|
|
45913
|
+
inputBodyString(body, "redirect_uri_alias"),
|
|
45914
|
+
INPUT_ALIAS_SOURCE
|
|
45915
|
+
);
|
|
45427
45916
|
const inputPreflightError = await withClient(
|
|
45428
45917
|
async (client) => {
|
|
45429
45918
|
const bodyEnvironment = inputBodyString(body, "environment");
|
|
@@ -45433,7 +45922,7 @@ function buildProvidersCommand() {
|
|
|
45433
45922
|
bodyEnvironment,
|
|
45434
45923
|
inputBodyString(body, "credential_source"),
|
|
45435
45924
|
inputBodyStringArray(body, "scopes"),
|
|
45436
|
-
true
|
|
45925
|
+
{ create: true, redirectUriAlias: bodyRedirectUriAlias }
|
|
45437
45926
|
);
|
|
45438
45927
|
if (catalogError !== null) return catalogError;
|
|
45439
45928
|
const availabilityError = await sharedDevAvailabilityError(
|
|
@@ -45502,6 +45991,9 @@ function buildProvidersCommand() {
|
|
|
45502
45991
|
validateUrlOrExit("--redirect-uri", uri);
|
|
45503
45992
|
}
|
|
45504
45993
|
const redirects = options.redirectUri && options.redirectUri.length > 0 ? options.redirectUri : void 0;
|
|
45994
|
+
const redirectUriAlias = validateRedirectUriAliasOrExit(
|
|
45995
|
+
options.redirectUriAlias
|
|
45996
|
+
);
|
|
45505
45997
|
const environmentPreflightError = await withClient(
|
|
45506
45998
|
async (client) => {
|
|
45507
45999
|
const catalogError = await providerCatalogError(
|
|
@@ -45510,7 +46002,7 @@ function buildProvidersCommand() {
|
|
|
45510
46002
|
environment,
|
|
45511
46003
|
credentialSource,
|
|
45512
46004
|
scopes,
|
|
45513
|
-
true
|
|
46005
|
+
{ create: true, redirectUriAlias }
|
|
45514
46006
|
);
|
|
45515
46007
|
if (catalogError !== null) return catalogError;
|
|
45516
46008
|
const availabilityError = await sharedDevAvailabilityError(
|
|
@@ -45529,7 +46021,8 @@ function buildProvidersCommand() {
|
|
|
45529
46021
|
// Scopeless provider with --scopes omitted: the dashboard's
|
|
45530
46022
|
// exact wire body for these providers is ``scopes: []``.
|
|
45531
46023
|
scopes: scopes ?? [],
|
|
45532
|
-
redirect_uris: redirects
|
|
46024
|
+
redirect_uris: redirects,
|
|
46025
|
+
redirect_uri_alias: redirectUriAlias
|
|
45533
46026
|
});
|
|
45534
46027
|
surfaceProviderResponse(row);
|
|
45535
46028
|
emit2(format, row);
|
|
@@ -45563,6 +46056,7 @@ function buildProvidersCommand() {
|
|
|
45563
46056
|
credential_source: isOptionalString,
|
|
45564
46057
|
environment: isAbsentOrOptionalString,
|
|
45565
46058
|
scopes: (v) => Array.isArray(v),
|
|
46059
|
+
redirect_uri_alias: isAbsentOrOptionalString,
|
|
45566
46060
|
status: isString,
|
|
45567
46061
|
grants_count: (v) => typeof v === "number"
|
|
45568
46062
|
},
|
|
@@ -45590,6 +46084,9 @@ function buildProvidersCommand() {
|
|
|
45590
46084
|
"--redirect-uri <uri>",
|
|
45591
46085
|
"Replace redirect URIs. Repeat the flag or pass a comma-separated list; both forms accumulate.",
|
|
45592
46086
|
collectRedirectUris
|
|
46087
|
+
).option(
|
|
46088
|
+
"--redirect-uri-alias <name>",
|
|
46089
|
+
"Replace the provider-issued redirect name sent as redirect_uri in place of the callback URL (eBay's RuName). Accepted only for providers that use one; see `alter providers list-catalog`."
|
|
45593
46090
|
).option(
|
|
45594
46091
|
"--status <status>",
|
|
45595
46092
|
"New status: active | disabled (rejected at the CLI if not exactly one of the two values)"
|
|
@@ -45619,24 +46116,33 @@ function buildProvidersCommand() {
|
|
|
45619
46116
|
"environment",
|
|
45620
46117
|
"scopes",
|
|
45621
46118
|
"redirectUri",
|
|
46119
|
+
"redirectUriAlias",
|
|
45622
46120
|
"status"
|
|
45623
46121
|
].filter((k) => options[k] !== void 0);
|
|
45624
46122
|
warnInputOverridesPerFieldFlags(perField.map(optionKeyToFlag));
|
|
46123
|
+
const bodyRedirectUriAlias = validateRedirectUriAliasOrExit(
|
|
46124
|
+
inputBodyString(body, "redirect_uri_alias"),
|
|
46125
|
+
INPUT_ALIAS_SOURCE
|
|
46126
|
+
);
|
|
45625
46127
|
const inputPreflightError = await withClient(
|
|
45626
46128
|
async (client) => {
|
|
46129
|
+
const readStoredProvider = storedProviderReader(
|
|
46130
|
+
client,
|
|
46131
|
+
appId,
|
|
46132
|
+
options.provider
|
|
46133
|
+
);
|
|
45627
46134
|
const catalogError = await providerCatalogError(
|
|
45628
46135
|
client,
|
|
45629
46136
|
options.provider,
|
|
45630
46137
|
inputBodyString(body, "environment"),
|
|
45631
46138
|
inputBodyString(body, "credential_source"),
|
|
45632
|
-
inputBodyStringArray(body, "scopes")
|
|
46139
|
+
inputBodyStringArray(body, "scopes"),
|
|
46140
|
+
{
|
|
46141
|
+
redirectUriAlias: bodyRedirectUriAlias,
|
|
46142
|
+
readStoredProvider
|
|
46143
|
+
}
|
|
45633
46144
|
);
|
|
45634
46145
|
if (catalogError !== null) return catalogError;
|
|
45635
|
-
const readStoredProvider = storedProviderReader(
|
|
45636
|
-
client,
|
|
45637
|
-
appId,
|
|
45638
|
-
options.provider
|
|
45639
|
-
);
|
|
45640
46146
|
const storedStateError = await sharedDevStoredStateError(
|
|
45641
46147
|
readStoredProvider,
|
|
45642
46148
|
options.provider,
|
|
@@ -45698,21 +46204,25 @@ function buildProvidersCommand() {
|
|
|
45698
46204
|
validateUrlOrExit("--redirect-uri", uri);
|
|
45699
46205
|
}
|
|
45700
46206
|
const redirects = options.redirectUri && options.redirectUri.length > 0 ? options.redirectUri : void 0;
|
|
46207
|
+
const redirectUriAlias = validateRedirectUriAliasOrExit(
|
|
46208
|
+
options.redirectUriAlias
|
|
46209
|
+
);
|
|
45701
46210
|
const updatePreflightError = await withClient(
|
|
45702
46211
|
async (client) => {
|
|
46212
|
+
const readStoredProvider = storedProviderReader(
|
|
46213
|
+
client,
|
|
46214
|
+
appId,
|
|
46215
|
+
options.provider
|
|
46216
|
+
);
|
|
45703
46217
|
const catalogError = await providerCatalogError(
|
|
45704
46218
|
client,
|
|
45705
46219
|
options.provider,
|
|
45706
46220
|
environment,
|
|
45707
46221
|
credentialSource,
|
|
45708
|
-
scopes
|
|
46222
|
+
scopes,
|
|
46223
|
+
{ redirectUriAlias, readStoredProvider }
|
|
45709
46224
|
);
|
|
45710
46225
|
if (catalogError !== null) return catalogError;
|
|
45711
|
-
const readStoredProvider = storedProviderReader(
|
|
45712
|
-
client,
|
|
45713
|
-
appId,
|
|
45714
|
-
options.provider
|
|
45715
|
-
);
|
|
45716
46226
|
const storedStateError = await sharedDevStoredStateError(
|
|
45717
46227
|
readStoredProvider,
|
|
45718
46228
|
options.provider,
|
|
@@ -45736,6 +46246,7 @@ function buildProvidersCommand() {
|
|
|
45736
46246
|
...options.skipPreflight ? { skip_preflight: true } : {},
|
|
45737
46247
|
scopes,
|
|
45738
46248
|
redirect_uris: redirects,
|
|
46249
|
+
redirect_uri_alias: redirectUriAlias,
|
|
45739
46250
|
status: providerStatus
|
|
45740
46251
|
});
|
|
45741
46252
|
surfaceProviderResponse(row);
|