@alter-ai/cli 0.9.0 → 0.9.1
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 +362 -54
- 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.1",
|
|
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.24.
|
|
83
|
+
"@alter-ai/alter-sdk": "workspace:0.24.2",
|
|
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",
|
|
@@ -6255,7 +6259,11 @@ var ConnectResult = class {
|
|
|
6255
6259
|
providerId;
|
|
6256
6260
|
accountIdentifier;
|
|
6257
6261
|
scopes;
|
|
6258
|
-
/**
|
|
6262
|
+
/**
|
|
6263
|
+
* "creation" when a new grant was minted; "reauth" when an existing
|
|
6264
|
+
* grant was returned with its id preserved (credential repair, or reuse
|
|
6265
|
+
* of an already-connected grant).
|
|
6266
|
+
*/
|
|
6259
6267
|
operation;
|
|
6260
6268
|
grantPolicy;
|
|
6261
6269
|
/**
|
|
@@ -8143,7 +8151,7 @@ async function _raiseForStatus(response) {
|
|
|
8143
8151
|
if (code === AgentConcurrentUpdateError.code && response.status === 409) {
|
|
8144
8152
|
throw new AgentConcurrentUpdateError({ message, details: body, hint });
|
|
8145
8153
|
}
|
|
8146
|
-
const ExcCtor = ERROR_CODE_STATUS_MAP[`${code}:${response.status}`] ?? ERROR_CODE_MAP[code];
|
|
8154
|
+
const ExcCtor = ERROR_CODE_STATUS_MAP[`${code}:${response.status}`] ?? (Object.hasOwn(ERROR_CODE_MAP, code) ? ERROR_CODE_MAP[code] : void 0);
|
|
8147
8155
|
if (ExcCtor !== void 0) {
|
|
8148
8156
|
throw new ExcCtor({ message, details: body, hint });
|
|
8149
8157
|
}
|
|
@@ -10172,7 +10180,7 @@ function _extractAdditionalCredentials(token) {
|
|
|
10172
10180
|
return _additionalCredsStore.get(token);
|
|
10173
10181
|
}
|
|
10174
10182
|
var _fetch;
|
|
10175
|
-
var SDK_VERSION = "0.24.
|
|
10183
|
+
var SDK_VERSION = "0.24.2";
|
|
10176
10184
|
var SDK_USER_AGENT = `alter-sdk-node/${SDK_VERSION}`;
|
|
10177
10185
|
function pyUnquote(s) {
|
|
10178
10186
|
if (!s.includes("%")) return s;
|
|
@@ -10243,8 +10251,12 @@ function canonicalSigningQuery(rawQuery) {
|
|
|
10243
10251
|
pairs.sort((p, q) => byCodePoint(p[0], q[0]) || byCodePoint(p[1], q[1]));
|
|
10244
10252
|
return pairs.map(([k, v]) => `${pyQuotePlus(k)}=${pyQuotePlus(v)}`).join("&");
|
|
10245
10253
|
}
|
|
10254
|
+
var DEFAULT_TIMEOUT_MS = 95e3;
|
|
10255
|
+
var _ApprovalDeadlineElapsed = class extends Error {
|
|
10256
|
+
};
|
|
10246
10257
|
var AUTH_POLL_SERVER_WAIT_MS = 25e3;
|
|
10247
10258
|
var AUTH_POLL_HTTP_BUFFER_MS = 15e3;
|
|
10259
|
+
var APPROVAL_POLL_SERVER_WAIT_MS = 25e3;
|
|
10248
10260
|
var PERMANENT_POLL_STATUSES = /* @__PURE__ */ new Set([400, 401, 403, 404, 422]);
|
|
10249
10261
|
var HTTP_FORBIDDEN = 403;
|
|
10250
10262
|
var HTTP_NO_CONTENT2 = 204;
|
|
@@ -10520,6 +10532,23 @@ function validateAndSerializeContext(context) {
|
|
|
10520
10532
|
return encoded;
|
|
10521
10533
|
}
|
|
10522
10534
|
var MAX_BODY_SIZE_BYTES = 1e4;
|
|
10535
|
+
var AUDIT_REFUSAL_BODY_MAX_CHARS = 65536;
|
|
10536
|
+
var REFUSAL_MESSAGE_CONTROL_CHARS = /[\u0000-\u001f\u007f-\u009f]/g;
|
|
10537
|
+
var REFUSAL_MESSAGE_MAX_CHARS = 300;
|
|
10538
|
+
function sanitizeRefusalMessage(message) {
|
|
10539
|
+
const cleaned = message.replace(REFUSAL_MESSAGE_CONTROL_CHARS, " ");
|
|
10540
|
+
return cleaned.length > REFUSAL_MESSAGE_MAX_CHARS ? `${cleaned.slice(0, REFUSAL_MESSAGE_MAX_CHARS)}\u2026` : cleaned;
|
|
10541
|
+
}
|
|
10542
|
+
function _redactUrlForLog(url2) {
|
|
10543
|
+
try {
|
|
10544
|
+
const parsed = new URL(url2);
|
|
10545
|
+
if (parsed.protocol && parsed.hostname) {
|
|
10546
|
+
return `${parsed.protocol}//${parsed.hostname}`;
|
|
10547
|
+
}
|
|
10548
|
+
} catch {
|
|
10549
|
+
}
|
|
10550
|
+
return "<unparseable-url>";
|
|
10551
|
+
}
|
|
10523
10552
|
var HTTP_CLIENT_ERROR_START = 400;
|
|
10524
10553
|
var MAX_ACTOR_STRING_LENGTH = 255;
|
|
10525
10554
|
var SAFE_HEADER_PATTERN = /^[\x20-\x7E]+$/;
|
|
@@ -10808,6 +10837,16 @@ var HttpClient = class {
|
|
|
10808
10837
|
new DOMException("The operation timed out.", "TimeoutError")
|
|
10809
10838
|
);
|
|
10810
10839
|
}, effectiveTimeoutMs);
|
|
10840
|
+
const callerSignal = options?.signal;
|
|
10841
|
+
let onCallerAbort;
|
|
10842
|
+
if (callerSignal !== void 0) {
|
|
10843
|
+
if (callerSignal.aborted) {
|
|
10844
|
+
controller.abort(callerSignal.reason);
|
|
10845
|
+
} else {
|
|
10846
|
+
onCallerAbort = () => controller.abort(callerSignal.reason);
|
|
10847
|
+
callerSignal.addEventListener("abort", onCallerAbort);
|
|
10848
|
+
}
|
|
10849
|
+
}
|
|
10811
10850
|
const init = {
|
|
10812
10851
|
method,
|
|
10813
10852
|
headers: mergedHeaders,
|
|
@@ -10846,6 +10885,9 @@ var HttpClient = class {
|
|
|
10846
10885
|
return response;
|
|
10847
10886
|
} finally {
|
|
10848
10887
|
clearTimeout(timeoutId);
|
|
10888
|
+
if (callerSignal !== void 0 && onCallerAbort !== void 0) {
|
|
10889
|
+
callerSignal.removeEventListener("abort", onCallerAbort);
|
|
10890
|
+
}
|
|
10849
10891
|
}
|
|
10850
10892
|
}
|
|
10851
10893
|
/**
|
|
@@ -10958,7 +11000,7 @@ var _VaultClient = class __VaultClient {
|
|
|
10958
11000
|
unresolvedBaseUrl,
|
|
10959
11001
|
options.logger ?? console
|
|
10960
11002
|
);
|
|
10961
|
-
const timeoutMs = options.timeout ??
|
|
11003
|
+
const timeoutMs = options.timeout ?? DEFAULT_TIMEOUT_MS;
|
|
10962
11004
|
this.#caller = options.caller;
|
|
10963
11005
|
const rawCallerType = options.callerType ?? "agent";
|
|
10964
11006
|
if (rawCallerType !== "agent" && rawCallerType !== "service") {
|
|
@@ -12163,9 +12205,34 @@ ${label}:${value}`;
|
|
|
12163
12205
|
});
|
|
12164
12206
|
this.#cacheActorIdFromResponse(response);
|
|
12165
12207
|
if (!response.ok) {
|
|
12208
|
+
let detail = "";
|
|
12209
|
+
try {
|
|
12210
|
+
const raw = await response.text();
|
|
12211
|
+
if (raw.length <= AUDIT_REFUSAL_BODY_MAX_CHARS) {
|
|
12212
|
+
const body = JSON.parse(raw);
|
|
12213
|
+
let message = body.message;
|
|
12214
|
+
if (typeof message !== "string" || message === "") {
|
|
12215
|
+
const inner = body.detail;
|
|
12216
|
+
if (typeof inner === "string") {
|
|
12217
|
+
message = inner;
|
|
12218
|
+
} else if (inner !== null && typeof inner === "object") {
|
|
12219
|
+
message = inner.message;
|
|
12220
|
+
}
|
|
12221
|
+
}
|
|
12222
|
+
if (typeof message === "string" && message !== "") {
|
|
12223
|
+
detail = `: ${sanitizeRefusalMessage(message)}`;
|
|
12224
|
+
}
|
|
12225
|
+
}
|
|
12226
|
+
} catch {
|
|
12227
|
+
}
|
|
12166
12228
|
this.#logger.warn(
|
|
12167
|
-
`Audit log failed with status ${response.status} (non-fatal)`
|
|
12229
|
+
`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
12230
|
);
|
|
12231
|
+
} else {
|
|
12232
|
+
try {
|
|
12233
|
+
await response.arrayBuffer();
|
|
12234
|
+
} catch {
|
|
12235
|
+
}
|
|
12169
12236
|
}
|
|
12170
12237
|
} catch (error51) {
|
|
12171
12238
|
this.#logger.warn(
|
|
@@ -12676,7 +12743,7 @@ ${label}:${value}`;
|
|
|
12676
12743
|
headers: hmacHeaders,
|
|
12677
12744
|
// Cap the best-effort background report at 2s (parity with the Python
|
|
12678
12745
|
// SDK's asyncio.wait_for). Without an override it would inherit the
|
|
12679
|
-
// instance timeout (default
|
|
12746
|
+
// instance timeout (default 95s), so a hung backend could keep the
|
|
12680
12747
|
// fire-and-forget task — and close()'s drain of it — alive far longer
|
|
12681
12748
|
// than the Python SDK does. Next provider 403 re-reports (self-healing).
|
|
12682
12749
|
timeoutMs: 2e3
|
|
@@ -13053,8 +13120,11 @@ ${label}:${value}`;
|
|
|
13053
13120
|
* @param options - Optional revocation options (reason for audit log).
|
|
13054
13121
|
* @returns RevokeGrantResult confirming the revocation.
|
|
13055
13122
|
* @throws {GrantNotFoundError} If the grant does not exist, is not
|
|
13056
|
-
* active,
|
|
13057
|
-
* the calling user
|
|
13123
|
+
* active, (when a user token is configured) does not belong to
|
|
13124
|
+
* the calling user, or — when this client holds an AGENT key — is
|
|
13125
|
+
* not a grant the agent is the principal of (its own delegation or
|
|
13126
|
+
* an agent-owned grant). An allowlist entry for the grant's provider
|
|
13127
|
+
* is never enough.
|
|
13058
13128
|
* @throws {NetworkError} If connection to backend fails.
|
|
13059
13129
|
* @throws {TimeoutError} If request to backend times out.
|
|
13060
13130
|
*/
|
|
@@ -13865,14 +13935,17 @@ ${label}:${value}`;
|
|
|
13865
13935
|
* @returns One `ConnectResult` per provider the user completed
|
|
13866
13936
|
* within the session (multi-provider Connect sessions yield
|
|
13867
13937
|
* multiple results).
|
|
13868
|
-
* @throws AlterValueError if `sessionToken` is blank,
|
|
13869
|
-
* `pollIntervalMs` is non-finite or not greater than zero
|
|
13938
|
+
* @throws AlterValueError if `sessionToken` is blank, `timeoutMs` /
|
|
13939
|
+
* `pollIntervalMs` is non-finite or not greater than zero, or
|
|
13940
|
+
* `onEvent` is provided but not a function.
|
|
13870
13941
|
* @throws ConnectTimeoutError if the local `timeoutMs` elapses, or a
|
|
13871
13942
|
* session observed as pending expires before Alter receives a
|
|
13872
13943
|
* completion callback. `details.reason` distinguishes
|
|
13873
13944
|
* `poll_deadline_elapsed`, `session_expired`, and `rate_limited` (the
|
|
13874
13945
|
* whole budget was spent backing off a throttled poll —
|
|
13875
|
-
* `details.retryAfter` carries the last hint the server gave).
|
|
13946
|
+
* `details.retryAfter` carries the last hint the server gave). When
|
|
13947
|
+
* denials were observed before the timeout,
|
|
13948
|
+
* `details.declined_providers` lists them on EVERY variant.
|
|
13876
13949
|
* @throws ConnectFlowError / ConnectDeniedError / ConnectConfigError
|
|
13877
13950
|
* for user denial, a first poll that finds an unavailable/expired
|
|
13878
13951
|
* session, unrecognized status, or a completed session whose every grant
|
|
@@ -13892,8 +13965,18 @@ ${label}:${value}`;
|
|
|
13892
13965
|
const timeoutMs = options?.timeoutMs ?? 3e5;
|
|
13893
13966
|
const pollIntervalMs = options?.pollIntervalMs ?? 2e3;
|
|
13894
13967
|
validatePollingInputs(sessionToken, timeoutMs, pollIntervalMs);
|
|
13968
|
+
if (options?.onEvent !== void 0 && typeof options.onEvent !== "function") {
|
|
13969
|
+
throw new AlterValueError("onEvent must be a function when provided");
|
|
13970
|
+
}
|
|
13895
13971
|
const deadline = performance.now() + timeoutMs;
|
|
13896
13972
|
let sawPending = false;
|
|
13973
|
+
const seenDeclinedProviders = /* @__PURE__ */ new Set();
|
|
13974
|
+
const withDeclined = (base) => {
|
|
13975
|
+
if (seenDeclinedProviders.size > 0) {
|
|
13976
|
+
base.declined_providers = [...seenDeclinedProviders].sort();
|
|
13977
|
+
}
|
|
13978
|
+
return base;
|
|
13979
|
+
};
|
|
13897
13980
|
while (true) {
|
|
13898
13981
|
this.#assertNotClosed();
|
|
13899
13982
|
let pollResult;
|
|
@@ -13907,11 +13990,11 @@ ${label}:${value}`;
|
|
|
13907
13990
|
if (remaining2 <= 0) {
|
|
13908
13991
|
throw new ConnectTimeoutError(
|
|
13909
13992
|
`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
|
-
{
|
|
13993
|
+
withDeclined({
|
|
13911
13994
|
timeoutMs,
|
|
13912
13995
|
reason: "rate_limited",
|
|
13913
13996
|
retryAfter: e.retryAfter
|
|
13914
|
-
}
|
|
13997
|
+
})
|
|
13915
13998
|
);
|
|
13916
13999
|
}
|
|
13917
14000
|
let backoffMs = pollIntervalMs;
|
|
@@ -14044,7 +14127,10 @@ ${label}:${value}`;
|
|
|
14044
14127
|
].includes(errorCode)) {
|
|
14045
14128
|
throw new ConnectConfigError(errorMessage, errorDetails);
|
|
14046
14129
|
}
|
|
14047
|
-
const identitySyncStatus =
|
|
14130
|
+
const identitySyncStatus = Object.hasOwn(
|
|
14131
|
+
IDENTITY_SYNC_POLL_STATUS,
|
|
14132
|
+
errorCode
|
|
14133
|
+
) ? IDENTITY_SYNC_POLL_STATUS[errorCode] : void 0;
|
|
14048
14134
|
if (identitySyncStatus !== void 0) {
|
|
14049
14135
|
throw new BackendError(
|
|
14050
14136
|
errorMessage,
|
|
@@ -14058,7 +14144,7 @@ ${label}:${value}`;
|
|
|
14058
14144
|
if (sawPending) {
|
|
14059
14145
|
throw new ConnectTimeoutError(
|
|
14060
14146
|
"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" }
|
|
14147
|
+
withDeclined({ timeoutMs, reason: "session_expired" })
|
|
14062
14148
|
);
|
|
14063
14149
|
}
|
|
14064
14150
|
throw new ConnectFlowError(
|
|
@@ -14071,6 +14157,46 @@ ${label}:${value}`;
|
|
|
14071
14157
|
{ status: pollStatus }
|
|
14072
14158
|
);
|
|
14073
14159
|
}
|
|
14160
|
+
const rawDeclined = pollResult.declined_providers;
|
|
14161
|
+
if (Array.isArray(rawDeclined)) {
|
|
14162
|
+
for (const entry of rawDeclined) {
|
|
14163
|
+
if (entry === null || typeof entry !== "object") continue;
|
|
14164
|
+
const declinedProvider = entry.provider_id;
|
|
14165
|
+
if (typeof declinedProvider !== "string" || declinedProvider === "") {
|
|
14166
|
+
continue;
|
|
14167
|
+
}
|
|
14168
|
+
const rawCode = entry.error_code;
|
|
14169
|
+
if (typeof rawCode !== "string" || rawCode === "") continue;
|
|
14170
|
+
if (seenDeclinedProviders.has(declinedProvider)) continue;
|
|
14171
|
+
seenDeclinedProviders.add(declinedProvider);
|
|
14172
|
+
if (!options?.onEvent) continue;
|
|
14173
|
+
const rawDeclinedAt = entry.declined_at;
|
|
14174
|
+
const logCallbackFailure = (error51) => {
|
|
14175
|
+
this.#logger.warn(
|
|
14176
|
+
"pollConnectSession onEvent callback threw (non-fatal)",
|
|
14177
|
+
{
|
|
14178
|
+
provider_id: declinedProvider,
|
|
14179
|
+
error_type: error51 instanceof Error ? error51.constructor.name : typeof error51
|
|
14180
|
+
}
|
|
14181
|
+
);
|
|
14182
|
+
};
|
|
14183
|
+
try {
|
|
14184
|
+
const result = options.onEvent({
|
|
14185
|
+
type: "provider_declined",
|
|
14186
|
+
provider_id: declinedProvider,
|
|
14187
|
+
error_code: rawCode,
|
|
14188
|
+
declined_at: typeof rawDeclinedAt === "string" ? rawDeclinedAt : null
|
|
14189
|
+
});
|
|
14190
|
+
if (result !== null && (typeof result === "object" || typeof result === "function") && typeof result.then === "function") {
|
|
14191
|
+
void Promise.resolve(result).catch(
|
|
14192
|
+
logCallbackFailure
|
|
14193
|
+
);
|
|
14194
|
+
}
|
|
14195
|
+
} catch (error51) {
|
|
14196
|
+
logCallbackFailure(error51);
|
|
14197
|
+
}
|
|
14198
|
+
}
|
|
14199
|
+
}
|
|
14074
14200
|
sawPending = true;
|
|
14075
14201
|
const remaining = deadline - performance.now();
|
|
14076
14202
|
if (remaining <= 0) break;
|
|
@@ -14080,7 +14206,7 @@ ${label}:${value}`;
|
|
|
14080
14206
|
}
|
|
14081
14207
|
throw new ConnectTimeoutError(
|
|
14082
14208
|
`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" }
|
|
14209
|
+
withDeclined({ timeoutMs, reason: "poll_deadline_elapsed" })
|
|
14084
14210
|
);
|
|
14085
14211
|
}
|
|
14086
14212
|
/**
|
|
@@ -14333,7 +14459,10 @@ ${label}:${value}`;
|
|
|
14333
14459
|
const errorMessage = typeof pollData.error_message === "string" && pollData.error_message ? pollData.error_message : "unknown error";
|
|
14334
14460
|
const message = `Authentication failed: ${errorMessage}`;
|
|
14335
14461
|
const syncCode = typeof pollData.error_code === "string" ? pollData.error_code : "";
|
|
14336
|
-
const identitySyncStatus =
|
|
14462
|
+
const identitySyncStatus = Object.hasOwn(
|
|
14463
|
+
IDENTITY_SYNC_POLL_STATUS,
|
|
14464
|
+
syncCode
|
|
14465
|
+
) ? IDENTITY_SYNC_POLL_STATUS[syncCode] : void 0;
|
|
14337
14466
|
if (identitySyncStatus !== void 0) {
|
|
14338
14467
|
const details = { error_code: syncCode };
|
|
14339
14468
|
const retryAfter = _coerceInt(pollData.retry_after_seconds);
|
|
@@ -14958,10 +15087,34 @@ ${label}:${value}`;
|
|
|
14958
15087
|
error: errCode
|
|
14959
15088
|
});
|
|
14960
15089
|
}
|
|
14961
|
-
/**
|
|
14962
|
-
|
|
15090
|
+
/**
|
|
15091
|
+
* Single-shot poll of an approval.
|
|
15092
|
+
*
|
|
15093
|
+
* `wait` asks the server to hold the request open until the approval's
|
|
15094
|
+
* status changes, up to that many seconds (0 = return immediately, the
|
|
15095
|
+
* default and the historical behaviour). It is a pure latency optimisation:
|
|
15096
|
+
* the response shape is identical either way, and a backend that predates
|
|
15097
|
+
* the parameter simply ignores it and answers immediately. The server caps
|
|
15098
|
+
* `wait` at 25 seconds (`APPROVAL_POLL_SERVER_WAIT_MS`, mirroring the
|
|
15099
|
+
* backend's `MAX_LONG_POLL_SECONDS`) and REJECTS larger values with a
|
|
15100
|
+
* validation error (422) rather than clamping them.
|
|
15101
|
+
*
|
|
15102
|
+
* `signal` (internal — not exposed on App/Agent) lets `awaitApproval` abort
|
|
15103
|
+
* an in-flight held request its local deadline has abandoned.
|
|
15104
|
+
*
|
|
15105
|
+
* @throws AlterValueError if `wait` is not a finite number.
|
|
15106
|
+
*/
|
|
15107
|
+
async getApprovalStatus(approvalId, options = {}) {
|
|
14963
15108
|
this.#assertNotClosed();
|
|
14964
|
-
|
|
15109
|
+
if (options !== null && options !== void 0 && typeof options !== "object") {
|
|
15110
|
+
throw new AlterValueError("options must be an object");
|
|
15111
|
+
}
|
|
15112
|
+
const waitOption = options?.wait ?? 0;
|
|
15113
|
+
if (typeof waitOption !== "number" || !Number.isFinite(waitOption)) {
|
|
15114
|
+
throw new AlterValueError("wait must be a finite number");
|
|
15115
|
+
}
|
|
15116
|
+
const wait = Math.max(0, Math.floor(waitOption));
|
|
15117
|
+
const sdkPath = wait > 0 ? `/sdk/approvals/${approvalId}?wait=${wait}` : `/sdk/approvals/${approvalId}`;
|
|
14965
15118
|
const hmacHeaders = this.#computeHmacHeaders("GET", sdkPath, "");
|
|
14966
15119
|
const traceparent = await ambientTraceparent();
|
|
14967
15120
|
if (traceparent !== void 0) {
|
|
@@ -14970,7 +15123,18 @@ ${label}:${value}`;
|
|
|
14970
15123
|
let response;
|
|
14971
15124
|
try {
|
|
14972
15125
|
response = await this.#alterClient.request("GET", sdkPath, {
|
|
14973
|
-
headers: hmacHeaders
|
|
15126
|
+
headers: hmacHeaders,
|
|
15127
|
+
// A held request needs a wider per-request timeout than the client
|
|
15128
|
+
// default, or the client aborts its own long-poll (see
|
|
15129
|
+
// AUTH_POLL_HTTP_BUFFER_MS). Only applied when actually parking.
|
|
15130
|
+
// Capped at the server's own ceiling: `wait` is deliberately NOT
|
|
15131
|
+
// clamped on the wire (the backend owns that contract and answers an
|
|
15132
|
+
// over-cap value with its documented validation error), but an
|
|
15133
|
+
// unbounded `wait * 1000` overflows to `Infinity` for a huge finite
|
|
15134
|
+
// value, and Node coerces such a timer to fire almost immediately —
|
|
15135
|
+
// aborting the request before that very error can come back.
|
|
15136
|
+
timeoutMs: wait > 0 ? Math.min(wait * 1e3, APPROVAL_POLL_SERVER_WAIT_MS) + AUTH_POLL_HTTP_BUFFER_MS : void 0,
|
|
15137
|
+
signal: options?.signal
|
|
14974
15138
|
});
|
|
14975
15139
|
} catch (error51) {
|
|
14976
15140
|
if (error51 instanceof Error && error51.name === "AbortError") {
|
|
@@ -15041,9 +15205,39 @@ ${label}:${value}`;
|
|
|
15041
15205
|
throw this.#buildApprovalTimeout(approvalId, timeoutMs, lastTransient);
|
|
15042
15206
|
}
|
|
15043
15207
|
let status;
|
|
15208
|
+
const raceController = new AbortController();
|
|
15209
|
+
let deadlineTimer;
|
|
15044
15210
|
try {
|
|
15045
|
-
|
|
15211
|
+
const serverWaitSeconds = Math.max(
|
|
15212
|
+
0,
|
|
15213
|
+
Math.min(
|
|
15214
|
+
Math.floor(APPROVAL_POLL_SERVER_WAIT_MS / 1e3),
|
|
15215
|
+
Math.floor((deadline - Date.now()) / 1e3)
|
|
15216
|
+
)
|
|
15217
|
+
);
|
|
15218
|
+
const remainingMs = Math.max(0, deadline - Date.now());
|
|
15219
|
+
status = await Promise.race([
|
|
15220
|
+
this.getApprovalStatus(approvalId, {
|
|
15221
|
+
wait: serverWaitSeconds,
|
|
15222
|
+
signal: raceController.signal
|
|
15223
|
+
}),
|
|
15224
|
+
new Promise((_resolve, reject) => {
|
|
15225
|
+
deadlineTimer = setTimeout(
|
|
15226
|
+
() => reject(new _ApprovalDeadlineElapsed()),
|
|
15227
|
+
remainingMs
|
|
15228
|
+
);
|
|
15229
|
+
deadlineTimer.unref?.();
|
|
15230
|
+
})
|
|
15231
|
+
]);
|
|
15046
15232
|
} catch (err2) {
|
|
15233
|
+
if (err2 instanceof _ApprovalDeadlineElapsed) {
|
|
15234
|
+
raceController.abort();
|
|
15235
|
+
throw this.#buildApprovalTimeout(
|
|
15236
|
+
approvalId,
|
|
15237
|
+
timeoutMs,
|
|
15238
|
+
lastTransient
|
|
15239
|
+
);
|
|
15240
|
+
}
|
|
15047
15241
|
if (!__VaultClient.#isTransientPollError(err2)) {
|
|
15048
15242
|
throw err2;
|
|
15049
15243
|
}
|
|
@@ -15065,6 +15259,8 @@ ${label}:${value}`;
|
|
|
15065
15259
|
);
|
|
15066
15260
|
await new Promise((res) => setTimeout(res, sleep2));
|
|
15067
15261
|
continue;
|
|
15262
|
+
} finally {
|
|
15263
|
+
if (deadlineTimer !== void 0) clearTimeout(deadlineTimer);
|
|
15068
15264
|
}
|
|
15069
15265
|
lastTransient = null;
|
|
15070
15266
|
if (status.status === "executed" && !status.hasResult) {
|
|
@@ -15880,8 +16076,8 @@ var Agent = class _Agent {
|
|
|
15880
16076
|
return this.#client.delegate(grantId, toAgentId, options);
|
|
15881
16077
|
}
|
|
15882
16078
|
// ── Approvals ──────────────────────────────────────────────────────────
|
|
15883
|
-
async getApprovalStatus(approvalId) {
|
|
15884
|
-
return this.#client.getApprovalStatus(approvalId);
|
|
16079
|
+
async getApprovalStatus(approvalId, options = {}) {
|
|
16080
|
+
return this.#client.getApprovalStatus(approvalId, options);
|
|
15885
16081
|
}
|
|
15886
16082
|
async awaitApproval(...args) {
|
|
15887
16083
|
return this.#client.awaitApproval(...args);
|
|
@@ -16301,8 +16497,8 @@ var App = class _App {
|
|
|
16301
16497
|
});
|
|
16302
16498
|
}
|
|
16303
16499
|
// ── Approvals ──────────────────────────────────────────────────────────
|
|
16304
|
-
async getApprovalStatus(approvalId) {
|
|
16305
|
-
return this.#client.getApprovalStatus(approvalId);
|
|
16500
|
+
async getApprovalStatus(approvalId, options = {}) {
|
|
16501
|
+
return this.#client.getApprovalStatus(approvalId, options);
|
|
16306
16502
|
}
|
|
16307
16503
|
async awaitApproval(...args) {
|
|
16308
16504
|
return this.#client.awaitApproval(...args);
|
|
@@ -16558,8 +16754,8 @@ async function _listGrantsUnified(client, body) {
|
|
|
16558
16754
|
var DEFAULT_BASE_URL = "https://backend.alterauth.com";
|
|
16559
16755
|
var PAT_API_PREFIX = "/api/v1/dev-portal";
|
|
16560
16756
|
var HTTP_ERROR_THRESHOLD = 400;
|
|
16561
|
-
var
|
|
16562
|
-
var CLI_VERSION = "0.9.
|
|
16757
|
+
var DEFAULT_TIMEOUT_MS2 = 3e4;
|
|
16758
|
+
var CLI_VERSION = "0.9.1";
|
|
16563
16759
|
var USER_AGENT = buildUserAgent();
|
|
16564
16760
|
function buildUserAgent() {
|
|
16565
16761
|
let osTag = "";
|
|
@@ -16804,7 +17000,7 @@ var DashboardClient = class {
|
|
|
16804
17000
|
);
|
|
16805
17001
|
}
|
|
16806
17002
|
}
|
|
16807
|
-
this.#timeoutMs = options.timeoutMs ??
|
|
17003
|
+
this.#timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
|
|
16808
17004
|
this.pats = new PATsNamespace(this);
|
|
16809
17005
|
this.apps = new AppsNamespace(this);
|
|
16810
17006
|
this.keys = new KeysNamespace2(this);
|
|
@@ -19535,6 +19731,38 @@ function resolveAppId(flagValue, options = {}) {
|
|
|
19535
19731
|
return null;
|
|
19536
19732
|
}
|
|
19537
19733
|
|
|
19734
|
+
// ../shared-utils/src/dns-host.ts
|
|
19735
|
+
var LABEL_SEPARATOR = /[.。.。]/;
|
|
19736
|
+
var TRAILING_LABEL_SEPARATOR = /[.。.。]$/;
|
|
19737
|
+
var isInvalidLabel = (label) => label.length === 0 || label.includes("_") || label.startsWith("-") || label.endsWith("-") || label.length >= 4 && label[2] === "-" && label[3] === "-" && !label.startsWith("xn--");
|
|
19738
|
+
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;
|
|
19739
|
+
var isOutsideIdna2008Repertoire = (label) => !IDNA2008_LABEL.test(label);
|
|
19740
|
+
var canonicalizeDnsHost = (host) => {
|
|
19741
|
+
let h = host;
|
|
19742
|
+
if (!/^[\x20-\x7e]*$/.test(h)) {
|
|
19743
|
+
if (/[/:@\s?#%\\]/.test(h)) return null;
|
|
19744
|
+
const uLabels = h.normalize("NFKC").replace(TRAILING_LABEL_SEPARATOR, "").split(LABEL_SEPARATOR);
|
|
19745
|
+
if (uLabels.some(isInvalidLabel)) return null;
|
|
19746
|
+
if (uLabels.some(isOutsideIdna2008Repertoire)) return null;
|
|
19747
|
+
let url2;
|
|
19748
|
+
try {
|
|
19749
|
+
url2 = new URL(`http://${h}`);
|
|
19750
|
+
} catch {
|
|
19751
|
+
return null;
|
|
19752
|
+
}
|
|
19753
|
+
if (url2.pathname !== "/" || url2.search !== "" || url2.hash !== "" || url2.port !== "" || url2.username !== "" || url2.password !== "") {
|
|
19754
|
+
return null;
|
|
19755
|
+
}
|
|
19756
|
+
h = url2.hostname;
|
|
19757
|
+
if (!/^[\x20-\x7e]*$/.test(h)) return null;
|
|
19758
|
+
h = h.replace(/\.$/, "");
|
|
19759
|
+
if (h.split(".").some(isInvalidLabel)) return null;
|
|
19760
|
+
}
|
|
19761
|
+
if (h.length > 253) return null;
|
|
19762
|
+
if (h.split(".").some((label) => label.length > 63)) return null;
|
|
19763
|
+
return h;
|
|
19764
|
+
};
|
|
19765
|
+
|
|
19538
19766
|
// src/commands/_helpers.ts
|
|
19539
19767
|
async function withClient(fn) {
|
|
19540
19768
|
const client = await createPortalClient();
|
|
@@ -20087,9 +20315,9 @@ function parseAllowedHostsOrExit(flag, entries) {
|
|
|
20087
20315
|
fail(`${JSON.stringify(entry)} must not contain control characters`);
|
|
20088
20316
|
}
|
|
20089
20317
|
}
|
|
20090
|
-
if (/[/:@\s]/.test(e)) {
|
|
20318
|
+
if (/[/:@\s?#%\\]/.test(e)) {
|
|
20091
20319
|
fail(
|
|
20092
|
-
`${JSON.stringify(entry)} must be a bare host \u2014 no scheme, port, path,
|
|
20320
|
+
`${JSON.stringify(entry)} must be a bare host \u2014 no scheme, port, path, "@", whitespace, or the URL delimiters ? # % \\`
|
|
20093
20321
|
);
|
|
20094
20322
|
}
|
|
20095
20323
|
let normalized;
|
|
@@ -20100,12 +20328,23 @@ function parseAllowedHostsOrExit(flag, entries) {
|
|
|
20100
20328
|
`${JSON.stringify(entry)} is a malformed wildcard \u2014 use "*.domain.tld"`
|
|
20101
20329
|
);
|
|
20102
20330
|
}
|
|
20103
|
-
|
|
20331
|
+
const canonicalRest = canonicalizeDnsHost(rest);
|
|
20332
|
+
if (canonicalRest === null) {
|
|
20333
|
+
fail(
|
|
20334
|
+
`${JSON.stringify(entry)} is not a valid DNS name (IDNA-encodable, labels up to 63 characters)`
|
|
20335
|
+
);
|
|
20336
|
+
}
|
|
20337
|
+
if (!canonicalRest.includes(".")) {
|
|
20104
20338
|
fail(
|
|
20105
20339
|
`${JSON.stringify(entry)}: wildcard must cover a domain, not a bare TLD (use "*.example.com", not "*.com")`
|
|
20106
20340
|
);
|
|
20107
20341
|
}
|
|
20108
|
-
|
|
20342
|
+
if (canonicalRest.length > 251) {
|
|
20343
|
+
fail(
|
|
20344
|
+
`${JSON.stringify(entry)} is too long for a wildcard \u2014 no host could ever match it`
|
|
20345
|
+
);
|
|
20346
|
+
}
|
|
20347
|
+
normalized = `*.${canonicalRest}`;
|
|
20109
20348
|
} else if (e.includes("*")) {
|
|
20110
20349
|
fail(
|
|
20111
20350
|
`${JSON.stringify(entry)}: "*" is only allowed as a leading subdomain wildcard ("*.domain.tld")`
|
|
@@ -20114,9 +20353,16 @@ function parseAllowedHostsOrExit(flag, entries) {
|
|
|
20114
20353
|
normalized = e.replace(/\.+$/, "");
|
|
20115
20354
|
if (!normalized) {
|
|
20116
20355
|
fail(
|
|
20117
|
-
`${JSON.stringify(entry)} must be a bare host \u2014 no scheme, port, path,
|
|
20356
|
+
`${JSON.stringify(entry)} must be a bare host \u2014 no scheme, port, path, "@", whitespace, or the URL delimiters ? # % \\`
|
|
20357
|
+
);
|
|
20358
|
+
}
|
|
20359
|
+
const canonical = canonicalizeDnsHost(normalized);
|
|
20360
|
+
if (canonical === null) {
|
|
20361
|
+
fail(
|
|
20362
|
+
`${JSON.stringify(entry)} is not a valid DNS name (IDNA-encodable, labels up to 63 characters)`
|
|
20118
20363
|
);
|
|
20119
20364
|
}
|
|
20365
|
+
normalized = canonical;
|
|
20120
20366
|
}
|
|
20121
20367
|
if (seen.has(normalized)) continue;
|
|
20122
20368
|
seen.add(normalized);
|
|
@@ -20372,6 +20618,20 @@ function surfaceCatalogWarnings(row) {
|
|
|
20372
20618
|
);
|
|
20373
20619
|
}
|
|
20374
20620
|
}
|
|
20621
|
+
function surfaceScopeWarnings(row) {
|
|
20622
|
+
if (typeof row !== "object" || row === null) return;
|
|
20623
|
+
const warnings = row.scope_warnings;
|
|
20624
|
+
if (!Array.isArray(warnings)) return;
|
|
20625
|
+
for (const warning of warnings) {
|
|
20626
|
+
if (typeof warning !== "string") continue;
|
|
20627
|
+
const sanitized = sanitizeStderrText(warning);
|
|
20628
|
+
if (sanitized.length === 0) continue;
|
|
20629
|
+
process.stderr.write(
|
|
20630
|
+
`alter: scope warning \u2014 ${sanitized} (save was allowed)
|
|
20631
|
+
`
|
|
20632
|
+
);
|
|
20633
|
+
}
|
|
20634
|
+
}
|
|
20375
20635
|
function writeNextPageHint(page, noun) {
|
|
20376
20636
|
if (typeof page !== "object" || page === null) return;
|
|
20377
20637
|
const { has_more: hasMore, offset, limit } = page;
|
|
@@ -20566,6 +20826,7 @@ function buildAgentsCommand() {
|
|
|
20566
20826
|
"alter: the api_key field above is shown ONCE and cannot be retrieved later.\n"
|
|
20567
20827
|
);
|
|
20568
20828
|
surfaceApproverWarnings(result);
|
|
20829
|
+
surfaceScopeWarnings(result);
|
|
20569
20830
|
});
|
|
20570
20831
|
return;
|
|
20571
20832
|
}
|
|
@@ -20621,6 +20882,7 @@ function buildAgentsCommand() {
|
|
|
20621
20882
|
"alter: the api_key field above is shown ONCE and cannot be retrieved later.\n"
|
|
20622
20883
|
);
|
|
20623
20884
|
surfaceApproverWarnings(result);
|
|
20885
|
+
surfaceScopeWarnings(result);
|
|
20624
20886
|
});
|
|
20625
20887
|
}
|
|
20626
20888
|
);
|
|
@@ -20840,6 +21102,7 @@ function buildAgentsCommand() {
|
|
|
20840
21102
|
}
|
|
20841
21103
|
);
|
|
20842
21104
|
emit2(format, row);
|
|
21105
|
+
surfaceScopeWarnings(row);
|
|
20843
21106
|
} catch (error51) {
|
|
20844
21107
|
if (error51 instanceof PortalBackendError && error51.statusCode === 409 && error51.code === "agent_concurrent_update") {
|
|
20845
21108
|
throw new PortalBackendError(
|
|
@@ -35908,7 +36171,7 @@ var RULE_TYPE_LABELS = /* @__PURE__ */ new Map([
|
|
|
35908
36171
|
["json_match", "Request condition"],
|
|
35909
36172
|
["ip_allowlist", "IP allowlist"],
|
|
35910
36173
|
["time_window", "Time window"],
|
|
35911
|
-
["require_approval", "
|
|
36174
|
+
["require_approval", "Human in the loop (HITL)"],
|
|
35912
36175
|
["restriction", "Method and endpoint allowlist"],
|
|
35913
36176
|
["quota", "Request quota"],
|
|
35914
36177
|
["content_match", "Operation and parameter policy"]
|
|
@@ -36334,10 +36597,13 @@ function buildPolicyRulePresentation(rule) {
|
|
|
36334
36597
|
});
|
|
36335
36598
|
}
|
|
36336
36599
|
if (rule.display?.inherited) {
|
|
36337
|
-
displayItems.push({
|
|
36600
|
+
displayItems.push({
|
|
36601
|
+
label: "Relationship",
|
|
36602
|
+
value: "Inherited runtime policy"
|
|
36603
|
+
});
|
|
36338
36604
|
}
|
|
36339
36605
|
if (displayItems.length > 0) {
|
|
36340
|
-
groups.unshift({ label: "
|
|
36606
|
+
groups.unshift({ label: "Runtime policy scope", items: displayItems });
|
|
36341
36607
|
}
|
|
36342
36608
|
return {
|
|
36343
36609
|
typeLabel: policyRuleTypeLabel(rule.rule_type),
|
|
@@ -36350,7 +36616,10 @@ function formatPolicyRuleAsText(rule) {
|
|
|
36350
36616
|
const lines = [
|
|
36351
36617
|
...rule.name ? [`Name: ${rule.name}`] : [],
|
|
36352
36618
|
...rule.id ? [`Policy ID: ${rule.id}`] : [],
|
|
36353
|
-
|
|
36619
|
+
// The friendly label matches the dashboard; the wire identifier is what
|
|
36620
|
+
// `alter policy rules create --type <rule_type>` accepts, so the text
|
|
36621
|
+
// view prints both for operators who script from it.
|
|
36622
|
+
`Type: ${presentation.typeLabel} [${rule.rule_type}]`,
|
|
36354
36623
|
`Effect: ${presentation.effectLabel}`,
|
|
36355
36624
|
`Status: ${rule.enabled === false ? "Disabled" : "Enabled"}`,
|
|
36356
36625
|
presentation.summary,
|
|
@@ -36865,7 +37134,7 @@ function buildAppsCommand() {
|
|
|
36865
37134
|
"table"
|
|
36866
37135
|
).option(
|
|
36867
37136
|
"--no-include-stats",
|
|
36868
|
-
"Skip per-app statistics (grant / key / API-call counts)"
|
|
37137
|
+
"Skip per-app statistics (grant / connection / secret / provider / key / API-call counts)"
|
|
36869
37138
|
).option(
|
|
36870
37139
|
"--include-archived",
|
|
36871
37140
|
"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)."
|
|
@@ -41066,6 +41335,7 @@ async function runStatus() {
|
|
|
41066
41335
|
// src/commands/managed-secrets.ts
|
|
41067
41336
|
import { readFileSync as readFileSync9 } from "fs";
|
|
41068
41337
|
import { Command as Command16, Option } from "commander";
|
|
41338
|
+
var MAX_SEARCH_LENGTH = 255;
|
|
41069
41339
|
var PRINCIPAL_TYPES = ["user", "group", "system", "agent"];
|
|
41070
41340
|
var CREDENTIAL_TYPES = [
|
|
41071
41341
|
"bearer_token",
|
|
@@ -41470,6 +41740,13 @@ function surfaceManagedSecretResponse(row) {
|
|
|
41470
41740
|
}
|
|
41471
41741
|
surfaceApproverWarnings(row);
|
|
41472
41742
|
}
|
|
41743
|
+
function derivedAllowlistMessage(e) {
|
|
41744
|
+
if (!(e instanceof PortalBackendError) || e.statusCode !== 409 || e.code !== "managed_secret_allowed_hosts_derived") {
|
|
41745
|
+
return null;
|
|
41746
|
+
}
|
|
41747
|
+
const detail = e.body?.detail;
|
|
41748
|
+
return typeof detail?.message === "string" ? sanitizeStderrText(detail.message) : "This secret's allowed hosts are derived from its provider binding and cannot be edited directly.";
|
|
41749
|
+
}
|
|
41473
41750
|
function groupPrincipalGateMessage(e) {
|
|
41474
41751
|
if (!(e instanceof PortalBackendError) || e.statusCode !== 422 || e.code !== "group_principal_unsupported_idp") {
|
|
41475
41752
|
return null;
|
|
@@ -41806,7 +42083,11 @@ var TEMPLATE_COLUMNS = [
|
|
|
41806
42083
|
get: (t) => t.form_schema?.fields.map(
|
|
41807
42084
|
(field) => `${credentialFieldFlag(field)}${field.required ? "" : " (optional)"}`
|
|
41808
42085
|
).join(" ") || "\u2014"
|
|
41809
|
-
}
|
|
42086
|
+
},
|
|
42087
|
+
// The field a bound template derives its allowlist from — the operator
|
|
42088
|
+
// needs it to know which --credential-field sets (and later retargets,
|
|
42089
|
+
// via rotate) the secret's allowed hosts.
|
|
42090
|
+
{ label: "BINDING", get: (t) => t.allowed_hosts_from_field ?? "\u2014" }
|
|
41810
42091
|
];
|
|
41811
42092
|
var SECRET_COLUMNS = [
|
|
41812
42093
|
{ label: "ID", get: (s) => s.id, maxWidth: 36 },
|
|
@@ -42275,7 +42556,7 @@ function buildGroupsSubcommand() {
|
|
|
42275
42556
|
);
|
|
42276
42557
|
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
42558
|
"--search <substring>",
|
|
42278
|
-
"Substring search on group name / external_group_id"
|
|
42559
|
+
"Substring search on group name / external_group_id (max 255 chars)"
|
|
42279
42560
|
).option(
|
|
42280
42561
|
"--limit <n>",
|
|
42281
42562
|
"Page size (default: 50)",
|
|
@@ -42292,6 +42573,12 @@ function buildGroupsSubcommand() {
|
|
|
42292
42573
|
async (options) => {
|
|
42293
42574
|
const format = coerceOutputFormat(options.output);
|
|
42294
42575
|
if (options.idp !== void 0) validateUuidOrExit("--idp", options.idp);
|
|
42576
|
+
if (options.search !== void 0)
|
|
42577
|
+
validateMaxLengthOrExit(
|
|
42578
|
+
"--search",
|
|
42579
|
+
options.search,
|
|
42580
|
+
MAX_SEARCH_LENGTH
|
|
42581
|
+
);
|
|
42295
42582
|
const resolvedAppId = await resolveAppOrExit(options.app);
|
|
42296
42583
|
await withClient(async (client) => {
|
|
42297
42584
|
const response = await client.managedSecrets.listGroups(
|
|
@@ -42404,7 +42691,8 @@ function buildManagedSecretsCommand() {
|
|
|
42404
42691
|
credential_type: isString,
|
|
42405
42692
|
category: isOptionalString,
|
|
42406
42693
|
popular: (v) => typeof v === "boolean",
|
|
42407
|
-
form_schema: isTemplateFormSchema
|
|
42694
|
+
form_schema: isTemplateFormSchema,
|
|
42695
|
+
allowed_hosts_from_field: isAbsentOrOptionalString
|
|
42408
42696
|
},
|
|
42409
42697
|
"managed-secrets.templates"
|
|
42410
42698
|
);
|
|
@@ -42788,11 +43076,25 @@ function buildManagedSecretsCommand() {
|
|
|
42788
43076
|
}
|
|
42789
43077
|
const hostList = options.clear ? null : parseAllowedHostsOrExit("--host", options.host);
|
|
42790
43078
|
await withClient(async (client) => {
|
|
42791
|
-
|
|
42792
|
-
|
|
42793
|
-
|
|
42794
|
-
|
|
42795
|
-
|
|
43079
|
+
let row;
|
|
43080
|
+
try {
|
|
43081
|
+
row = await client.managedSecrets.setAllowedHosts(
|
|
43082
|
+
appId,
|
|
43083
|
+
secretId,
|
|
43084
|
+
hostList
|
|
43085
|
+
);
|
|
43086
|
+
} catch (e) {
|
|
43087
|
+
const derived = derivedAllowlistMessage(e);
|
|
43088
|
+
if (derived !== null) {
|
|
43089
|
+
process.stderr.write(`alter: ${derived}
|
|
43090
|
+
`);
|
|
43091
|
+
process.stderr.write(
|
|
43092
|
+
"alter: rotate the credential (`alter managed-secrets rotate`) to change its binding\n"
|
|
43093
|
+
);
|
|
43094
|
+
process.exit(EXIT_CONFLICT);
|
|
43095
|
+
}
|
|
43096
|
+
throw e;
|
|
43097
|
+
}
|
|
42796
43098
|
emit2(format, row);
|
|
42797
43099
|
if (options.clear) {
|
|
42798
43100
|
process.stderr.write(
|
|
@@ -43078,7 +43380,7 @@ function buildManagedSecretsCommand() {
|
|
|
43078
43380
|
);
|
|
43079
43381
|
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
43382
|
"--search <substring>",
|
|
43081
|
-
"Substring search on email / display_name / external_subject_id"
|
|
43383
|
+
"Substring search on email / display_name / external_subject_id (max 255 chars)"
|
|
43082
43384
|
).option(
|
|
43083
43385
|
"--limit <n>",
|
|
43084
43386
|
"Page size (default: 50)",
|
|
@@ -43095,6 +43397,12 @@ function buildManagedSecretsCommand() {
|
|
|
43095
43397
|
async (options) => {
|
|
43096
43398
|
const format = coerceOutputFormat(options.output);
|
|
43097
43399
|
if (options.idp !== void 0) validateUuidOrExit("--idp", options.idp);
|
|
43400
|
+
if (options.search !== void 0)
|
|
43401
|
+
validateMaxLengthOrExit(
|
|
43402
|
+
"--search",
|
|
43403
|
+
options.search,
|
|
43404
|
+
MAX_SEARCH_LENGTH
|
|
43405
|
+
);
|
|
43098
43406
|
const resolvedAppId = await resolveAppOrExit(options.app);
|
|
43099
43407
|
await withClient(async (client) => {
|
|
43100
43408
|
const response = await client.managedSecrets.listUsers(
|