@kutrion/cli 1.2.12 → 1.2.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/dist/index.js +460 -228
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -24054,6 +24054,49 @@ function auditResultLabel(raw) {
|
|
|
24054
24054
|
return humanizeAction(raw);
|
|
24055
24055
|
}
|
|
24056
24056
|
|
|
24057
|
+
// ../packages/shared/src/refresh-refusal-copy.ts
|
|
24058
|
+
init_define_KT_HUB_FILES();
|
|
24059
|
+
var REFRESH_REFUSAL_COPY = {
|
|
24060
|
+
refresh_hub_signed_out: { title: "This session was signed out", why: "You signed out in Kutrion Hub." },
|
|
24061
|
+
refresh_hub_revoked: { title: "This session was signed out", why: "This app was signed out because its Kutrion Hub was removed." },
|
|
24062
|
+
refresh_credentials_changed: {
|
|
24063
|
+
title: "Your password changed",
|
|
24064
|
+
why: "Your password changed, so Kutrion signed this {app} out.",
|
|
24065
|
+
action: "Sign in again with your new password."
|
|
24066
|
+
},
|
|
24067
|
+
device_revoked: {
|
|
24068
|
+
title: "This device was signed out",
|
|
24069
|
+
why: "This {app}'s access was withdrawn from your Kutrion account.",
|
|
24070
|
+
action: "Sign in again to re-register it."
|
|
24071
|
+
},
|
|
24072
|
+
account_inactive: {
|
|
24073
|
+
title: "This account has ended",
|
|
24074
|
+
why: "Your Kutrion account is no longer active, so this {app} was signed out.",
|
|
24075
|
+
action: "Your administrator can restore it. Signing in again won't work until they do."
|
|
24076
|
+
},
|
|
24077
|
+
user_inactive: {
|
|
24078
|
+
title: "This account has ended",
|
|
24079
|
+
why: "Your Kutrion account is no longer active, so this {app} was signed out.",
|
|
24080
|
+
action: "Your administrator can restore it. Signing in again won't work until they do."
|
|
24081
|
+
},
|
|
24082
|
+
account_expired: {
|
|
24083
|
+
title: "This account has expired",
|
|
24084
|
+
why: "Your Kutrion account has expired, so this {app} was signed out.",
|
|
24085
|
+
action: "Your administrator can restore it. Signing in again won't work until they do."
|
|
24086
|
+
},
|
|
24087
|
+
org_mismatch: {
|
|
24088
|
+
title: "This session was signed out",
|
|
24089
|
+
why: "You're no longer a member of the organization this {app} was signed in to.",
|
|
24090
|
+
action: "Sign in again to continue with your current organization."
|
|
24091
|
+
}
|
|
24092
|
+
};
|
|
24093
|
+
function refreshRefusalCopy(code, noun = "app") {
|
|
24094
|
+
if (typeof code !== "string" || !Object.prototype.hasOwnProperty.call(REFRESH_REFUSAL_COPY, code)) return null;
|
|
24095
|
+
const c = REFRESH_REFUSAL_COPY[code];
|
|
24096
|
+
const fill = (s) => s.replace(/\{app\}/g, noun);
|
|
24097
|
+
return { title: c.title, why: fill(c.why), ...c.action ? { action: fill(c.action) } : {} };
|
|
24098
|
+
}
|
|
24099
|
+
|
|
24057
24100
|
// ../packages/shared/src/client.ts
|
|
24058
24101
|
init_define_KT_HUB_FILES();
|
|
24059
24102
|
|
|
@@ -27621,6 +27664,9 @@ var ApiError = class extends Error {
|
|
|
27621
27664
|
this.headers = headers;
|
|
27622
27665
|
this.name = "ApiError";
|
|
27623
27666
|
}
|
|
27667
|
+
// #3445: the `error` code /auth/refresh gave when it ended the session behind this 401, set only by
|
|
27668
|
+
// the SDK's own refresh-then-replay path. `body` and `message` stay the original request's.
|
|
27669
|
+
refreshCode;
|
|
27624
27670
|
// #2747: the error class slug — the server's `error` code, or the code the SDK minted itself — or
|
|
27625
27671
|
// null when the reply carried none (the message is then a status phrase, not a code).
|
|
27626
27672
|
get slug() {
|
|
@@ -28053,15 +28099,22 @@ var KutrionClient = class {
|
|
|
28053
28099
|
});
|
|
28054
28100
|
const { body: signed, signerThrew } = await this.signedRefreshBody(rt);
|
|
28055
28101
|
let res = await post(signed);
|
|
28056
|
-
|
|
28057
|
-
|
|
28058
|
-
|
|
28102
|
+
let refusalBody;
|
|
28103
|
+
if (res.status === 401 && signed.signature) {
|
|
28104
|
+
refusalBody = await readBodyWithin(res, REFRESH_BODY_MS);
|
|
28105
|
+
if (isRefreshSignatureRefusal(refusalBody)) {
|
|
28106
|
+
res = await post(this.refreshBody(rt));
|
|
28107
|
+
refusalBody = void 0;
|
|
28108
|
+
if (res.ok) this.refreshSigningRefused = true;
|
|
28109
|
+
}
|
|
28059
28110
|
}
|
|
28060
28111
|
if (res.status === 401) {
|
|
28061
|
-
if (
|
|
28112
|
+
if (refusalBody === void 0) refusalBody = await readBodyWithin(res, REFRESH_BODY_MS);
|
|
28113
|
+
if (signerThrew && !signed.signature && isRefreshSignatureRefusal(refusalBody)) {
|
|
28062
28114
|
return { outcome: "indeterminate", status: 401 };
|
|
28063
28115
|
}
|
|
28064
|
-
|
|
28116
|
+
const code = refusalBody?.error;
|
|
28117
|
+
return typeof code === "string" && code ? { outcome: "rejected", status: 401, code } : { outcome: "rejected", status: 401 };
|
|
28065
28118
|
}
|
|
28066
28119
|
if (!res.ok) {
|
|
28067
28120
|
const body = res.status === 403 ? await readBodyWithin(res, REFRESH_BODY_MS) : void 0;
|
|
@@ -28147,6 +28200,7 @@ var KutrionClient = class {
|
|
|
28147
28200
|
data = JSON.parse(await decryptEnvelope(this.sessionKey, data.enc));
|
|
28148
28201
|
}
|
|
28149
28202
|
if (!res.ok) {
|
|
28203
|
+
let refreshCode;
|
|
28150
28204
|
if (res.status === 401 && auth) {
|
|
28151
28205
|
if (_retry) {
|
|
28152
28206
|
this.onUnauthorized?.();
|
|
@@ -28156,6 +28210,9 @@ var KutrionClient = class {
|
|
|
28156
28210
|
case "refreshed":
|
|
28157
28211
|
return this.req(method, path, body, auth, true);
|
|
28158
28212
|
case "rejected":
|
|
28213
|
+
refreshCode = r.code;
|
|
28214
|
+
this.onUnauthorized?.(r.code);
|
|
28215
|
+
break;
|
|
28159
28216
|
case "no_token":
|
|
28160
28217
|
this.onUnauthorized?.();
|
|
28161
28218
|
break;
|
|
@@ -28170,7 +28227,9 @@ var KutrionClient = class {
|
|
|
28170
28227
|
}
|
|
28171
28228
|
}
|
|
28172
28229
|
}
|
|
28173
|
-
|
|
28230
|
+
const err = new ApiError(res.status, apiErrorMessage(res.status, data), data, res.headers);
|
|
28231
|
+
if (refreshCode) err.refreshCode = refreshCode;
|
|
28232
|
+
throw err;
|
|
28174
28233
|
}
|
|
28175
28234
|
return data;
|
|
28176
28235
|
}
|
|
@@ -28420,7 +28479,7 @@ var KutrionClient = class {
|
|
|
28420
28479
|
// #2578: clears a factor's pending-approval mark. A step-up-pending session is refused with 403
|
|
28421
28480
|
// step_up_required; `approved: false` means the factor was already approved.
|
|
28422
28481
|
approveFactor(kind, id) {
|
|
28423
|
-
return this.req("POST", `/auth/factors/${kind}/${encodeURIComponent(id)}/approve`);
|
|
28482
|
+
return this.req("POST", `/auth/factors/${encodeURIComponent(kind)}/${encodeURIComponent(id)}/approve`);
|
|
28424
28483
|
}
|
|
28425
28484
|
mfaGenerateBackupCodes() {
|
|
28426
28485
|
return this.req("POST", "/auth/mfa/backup-codes/generate");
|
|
@@ -28447,7 +28506,7 @@ var KutrionClient = class {
|
|
|
28447
28506
|
// (the iOS AutoFill extension) so it rotates independently of the app and never trips the app's
|
|
28448
28507
|
// reuse-detection. The extension persists its own rotations; the app must not share its token.
|
|
28449
28508
|
provisionCompanionSession(deviceId) {
|
|
28450
|
-
return this.req("POST", `/auth/devices/${deviceId}/companion`, {});
|
|
28509
|
+
return this.req("POST", `/auth/devices/${encodeURIComponent(deviceId)}/companion`, {});
|
|
28451
28510
|
}
|
|
28452
28511
|
// #768: `drain_limits` is what each device's `drain.state` may and may not be said to prove. The
|
|
28453
28512
|
// server ships the sentence so three clients cannot each invent their own; optional here only
|
|
@@ -28456,7 +28515,7 @@ var KutrionClient = class {
|
|
|
28456
28515
|
return this.req("GET", "/auth/devices");
|
|
28457
28516
|
}
|
|
28458
28517
|
revokeDevice(id) {
|
|
28459
|
-
return this.req("DELETE", `/auth/devices/${id}`, void 0);
|
|
28518
|
+
return this.req("DELETE", `/auth/devices/${encodeURIComponent(id)}`, void 0);
|
|
28460
28519
|
}
|
|
28461
28520
|
// Rotate tokens explicitly (req() also does this transparently on 401).
|
|
28462
28521
|
async refreshTokens(refreshToken) {
|
|
@@ -28493,7 +28552,7 @@ var KutrionClient = class {
|
|
|
28493
28552
|
// nowhere else afterwards, so a caller that drops it must either re-issue (same key while it lives)
|
|
28494
28553
|
// or rotate — and rotating deliberately kills whatever the old key was protecting.
|
|
28495
28554
|
issueCacheKey(deviceId, opts2) {
|
|
28496
|
-
return this.req("POST", `/auth/devices/${deviceId}/cache-key`, { rotate: opts2?.rotate === true });
|
|
28555
|
+
return this.req("POST", `/auth/devices/${encodeURIComponent(deviceId)}/cache-key`, { rotate: opts2?.rotate === true });
|
|
28497
28556
|
}
|
|
28498
28557
|
// #712: drain this device's local reveal journal. The batch is MAC'd under the cache KEK; a broken
|
|
28499
28558
|
// chain answers 409 with the head the server holds, never a silent success.
|
|
@@ -28508,10 +28567,10 @@ var KutrionClient = class {
|
|
|
28508
28567
|
return this.req("POST", "/audit/journal-discarded", input);
|
|
28509
28568
|
}
|
|
28510
28569
|
cacheKeyStatus(deviceId) {
|
|
28511
|
-
return this.req("GET", `/auth/devices/${deviceId}/cache-key`);
|
|
28570
|
+
return this.req("GET", `/auth/devices/${encodeURIComponent(deviceId)}/cache-key`);
|
|
28512
28571
|
}
|
|
28513
28572
|
revokeCacheKey(deviceId) {
|
|
28514
|
-
return this.req("DELETE", `/auth/devices/${deviceId}/cache-key`);
|
|
28573
|
+
return this.req("DELETE", `/auth/devices/${encodeURIComponent(deviceId)}/cache-key`);
|
|
28515
28574
|
}
|
|
28516
28575
|
listCacheKeys() {
|
|
28517
28576
|
return this.req("GET", "/auth/cache-keys");
|
|
@@ -28532,21 +28591,24 @@ var KutrionClient = class {
|
|
|
28532
28591
|
return this.req("GET", "/auth/webauthn");
|
|
28533
28592
|
}
|
|
28534
28593
|
deletePasskey(id) {
|
|
28535
|
-
return this.req("DELETE", `/auth/webauthn/${id}`);
|
|
28594
|
+
return this.req("DELETE", `/auth/webauthn/${encodeURIComponent(id)}`);
|
|
28595
|
+
}
|
|
28596
|
+
renamePasskey(id, label) {
|
|
28597
|
+
return this.req("PATCH", `/auth/webauthn/${encodeURIComponent(id)}`, { label });
|
|
28536
28598
|
}
|
|
28537
28599
|
// VT-120: linked sign-in methods (SSO/social identities) + set a first password for social-only accounts.
|
|
28538
28600
|
listIdentities() {
|
|
28539
28601
|
return this.req("GET", "/auth/identities");
|
|
28540
28602
|
}
|
|
28541
28603
|
unlinkIdentity(id) {
|
|
28542
|
-
return this.req("DELETE", `/auth/identities/${id}`);
|
|
28604
|
+
return this.req("DELETE", `/auth/identities/${encodeURIComponent(id)}`);
|
|
28543
28605
|
}
|
|
28544
28606
|
// #2678: begin linking a platform provider. The browser then navigates to `url`; the callback hands
|
|
28545
28607
|
// back a handle that completeIdentityLink attaches from this same session. #2847: a native app passes
|
|
28546
28608
|
// its registered client and the challenge for a verifier it keeps, then completes with that verifier.
|
|
28547
28609
|
startIdentityLink(ref, binding, currentPassword) {
|
|
28548
28610
|
const how = typeof binding === "string" ? { cb: binding } : { native: true, native_client: binding.nativeClient, native_challenge: binding.challenge };
|
|
28549
|
-
return this.req("POST", `/auth/sso/${ref}/link`, { ...how, ...currentPassword ? { current_password: currentPassword } : {} });
|
|
28611
|
+
return this.req("POST", `/auth/sso/${encodeURIComponent(ref)}/link`, { ...how, ...currentPassword ? { current_password: currentPassword } : {} });
|
|
28550
28612
|
}
|
|
28551
28613
|
completeIdentityLink(handle, verifier) {
|
|
28552
28614
|
return this.req("POST", "/auth/sso/link/complete", { handle, ...verifier ? { verifier } : {} });
|
|
@@ -28587,15 +28649,15 @@ var KutrionClient = class {
|
|
|
28587
28649
|
// Works for both OIDC and SAML providers — /start dispatches on the provider's protocol. `ref`
|
|
28588
28650
|
// is the provider id (preferred, unambiguous across orgs) or a platform provider's slug.
|
|
28589
28651
|
ssoStartUrl(ref) {
|
|
28590
|
-
return `${this.baseUrl}/auth/sso/${ref}/start`;
|
|
28652
|
+
return `${this.baseUrl}/auth/sso/${encodeURIComponent(ref)}/start`;
|
|
28591
28653
|
}
|
|
28592
28654
|
// SP metadata URL to hand a SAML IdP (entityID + ACS endpoint). Keyed by provider id.
|
|
28593
28655
|
samlMetadataUrl(ref) {
|
|
28594
|
-
return `${this.baseUrl}/auth/sso/${ref}/saml/metadata`;
|
|
28656
|
+
return `${this.baseUrl}/auth/sso/${encodeURIComponent(ref)}/saml/metadata`;
|
|
28595
28657
|
}
|
|
28596
28658
|
// OIDC redirect URI to register at the IdP, keyed by provider id (per-org safe).
|
|
28597
28659
|
ssoCallbackUrl(ref) {
|
|
28598
|
-
return `${this.baseUrl}/auth/sso/${ref}/callback`;
|
|
28660
|
+
return `${this.baseUrl}/auth/sso/${encodeURIComponent(ref)}/callback`;
|
|
28599
28661
|
}
|
|
28600
28662
|
// --- SSO provider admin (MT-3, org admin/owner only; secrets are write-only) ---
|
|
28601
28663
|
listSsoProvidersAdmin() {
|
|
@@ -28686,10 +28748,10 @@ var KutrionClient = class {
|
|
|
28686
28748
|
return this.req("POST", "/workspaces", input);
|
|
28687
28749
|
}
|
|
28688
28750
|
updateWorkspace(id, input) {
|
|
28689
|
-
return this.req("PUT", `/workspaces/${id}`, input);
|
|
28751
|
+
return this.req("PUT", `/workspaces/${encodeURIComponent(id)}`, input);
|
|
28690
28752
|
}
|
|
28691
28753
|
deleteWorkspace(id) {
|
|
28692
|
-
return this.req("DELETE", `/workspaces/${id}`);
|
|
28754
|
+
return this.req("DELETE", `/workspaces/${encodeURIComponent(id)}`);
|
|
28693
28755
|
}
|
|
28694
28756
|
// --- relay (#458 vault-synced API-client documents; #464 team grants) ---
|
|
28695
28757
|
listRelayDocuments() {
|
|
@@ -28705,7 +28767,7 @@ var KutrionClient = class {
|
|
|
28705
28767
|
return this.req("POST", `/relay/documents/${encodeURIComponent(clientId)}/grants`, { team_id, role });
|
|
28706
28768
|
}
|
|
28707
28769
|
relayUnshare(clientId, teamId) {
|
|
28708
|
-
return this.req("DELETE", `/relay/documents/${encodeURIComponent(clientId)}/grants/${teamId}`);
|
|
28770
|
+
return this.req("DELETE", `/relay/documents/${encodeURIComponent(clientId)}/grants/${encodeURIComponent(teamId)}`);
|
|
28709
28771
|
}
|
|
28710
28772
|
// --- vault ---
|
|
28711
28773
|
listVaults() {
|
|
@@ -28715,13 +28777,13 @@ var KutrionClient = class {
|
|
|
28715
28777
|
return this.req("POST", "/vault/create", { name, team_id });
|
|
28716
28778
|
}
|
|
28717
28779
|
renameVault(vaultId, name) {
|
|
28718
|
-
return this.req("PATCH", `/vault/${vaultId}`, { name });
|
|
28780
|
+
return this.req("PATCH", `/vault/${encodeURIComponent(vaultId)}`, { name });
|
|
28719
28781
|
}
|
|
28720
28782
|
deleteVault(vaultId) {
|
|
28721
|
-
return this.req("DELETE", `/vault/${vaultId}`);
|
|
28783
|
+
return this.req("DELETE", `/vault/${encodeURIComponent(vaultId)}`);
|
|
28722
28784
|
}
|
|
28723
28785
|
listCredentials(vaultId) {
|
|
28724
|
-
return this.req("GET", `/vault/${vaultId}/credentials`);
|
|
28786
|
+
return this.req("GET", `/vault/${encodeURIComponent(vaultId)}/credentials`);
|
|
28725
28787
|
}
|
|
28726
28788
|
// #1122: listVaults + every listCredentials in one round trip, with the same permissions gating.
|
|
28727
28789
|
// Metadata only — revealing a secret is still the audited per-credential call.
|
|
@@ -28747,19 +28809,19 @@ var KutrionClient = class {
|
|
|
28747
28809
|
return this.req("GET", `/vault/host-matches?host=${encodeURIComponent(host2)}`);
|
|
28748
28810
|
}
|
|
28749
28811
|
getCredential(vaultId, cid) {
|
|
28750
|
-
return this.req("GET", `/vault/${vaultId}/credentials/${cid}`);
|
|
28812
|
+
return this.req("GET", `/vault/${encodeURIComponent(vaultId)}/credentials/${encodeURIComponent(cid)}`);
|
|
28751
28813
|
}
|
|
28752
28814
|
// #717: `mutation_id` and `base_updated_at` are what a replayed offline write adds — the first
|
|
28753
28815
|
// makes a retry a no-op, the second lets the server tell an edit from an overwrite.
|
|
28754
28816
|
storeCredential(vaultId, input) {
|
|
28755
|
-
return this.req("POST", `/vault/${vaultId}/credentials`, input);
|
|
28817
|
+
return this.req("POST", `/vault/${encodeURIComponent(vaultId)}/credentials`, input);
|
|
28756
28818
|
}
|
|
28757
28819
|
updateCredential(vaultId, cid, input) {
|
|
28758
|
-
return this.req("PATCH", `/vault/${vaultId}/credentials/${cid}`, input);
|
|
28820
|
+
return this.req("PATCH", `/vault/${encodeURIComponent(vaultId)}/credentials/${encodeURIComponent(cid)}`, input);
|
|
28759
28821
|
}
|
|
28760
28822
|
deleteCredential(vaultId, cid, opts2) {
|
|
28761
28823
|
const q = opts2?.mutation_id ? `?mutation_id=${encodeURIComponent(opts2.mutation_id)}` : "";
|
|
28762
|
-
return this.req("DELETE", `/vault/${vaultId}/credentials/${cid}
|
|
28824
|
+
return this.req("DELETE", `/vault/${encodeURIComponent(vaultId)}/credentials/${encodeURIComponent(cid)}` + q);
|
|
28763
28825
|
}
|
|
28764
28826
|
// #2622: snippets. A brokered shell runs one over its socket (snippetFrame); useSnippet is for a
|
|
28765
28827
|
// terminal the server does not broker, and records the use the same way.
|
|
@@ -28787,23 +28849,23 @@ var KutrionClient = class {
|
|
|
28787
28849
|
return this.req("GET", `/vault/conflicts${opts2?.open === false ? "?open=false" : ""}`);
|
|
28788
28850
|
}
|
|
28789
28851
|
acknowledgeConflict(id) {
|
|
28790
|
-
return this.req("POST", `/vault/conflicts/${id}/ack`);
|
|
28852
|
+
return this.req("POST", `/vault/conflicts/${encodeURIComponent(id)}/ack`);
|
|
28791
28853
|
}
|
|
28792
28854
|
conflictPrevious(id) {
|
|
28793
|
-
return this.req("GET", `/vault/conflicts/${id}/previous`);
|
|
28855
|
+
return this.req("GET", `/vault/conflicts/${encodeURIComponent(id)}/previous`);
|
|
28794
28856
|
}
|
|
28795
28857
|
// VT-487: group RBAC on a vault (collection) + item folders.
|
|
28796
28858
|
listVaultGrants(vaultId) {
|
|
28797
|
-
return this.req("GET", `/vault/${vaultId}/grants`);
|
|
28859
|
+
return this.req("GET", `/vault/${encodeURIComponent(vaultId)}/grants`);
|
|
28798
28860
|
}
|
|
28799
28861
|
grantVaultToTeam(vaultId, input) {
|
|
28800
|
-
return this.req("POST", `/vault/${vaultId}/grants`, input);
|
|
28862
|
+
return this.req("POST", `/vault/${encodeURIComponent(vaultId)}/grants`, input);
|
|
28801
28863
|
}
|
|
28802
28864
|
revokeVaultGrant(vaultId, grantId) {
|
|
28803
|
-
return this.req("DELETE", `/vault/${vaultId}/grants/${grantId}`);
|
|
28865
|
+
return this.req("DELETE", `/vault/${encodeURIComponent(vaultId)}/grants/${encodeURIComponent(grantId)}`);
|
|
28804
28866
|
}
|
|
28805
28867
|
listVaultFolders(vaultId) {
|
|
28806
|
-
return this.req("GET", `/vault/${vaultId}/folders`);
|
|
28868
|
+
return this.req("GET", `/vault/${encodeURIComponent(vaultId)}/folders`);
|
|
28807
28869
|
}
|
|
28808
28870
|
// #1169: the credential's destination allow-list — where its secret may be presented. The routes
|
|
28809
28871
|
// live in their own module mounted on /vault (server/src/index.ts:310), so the paths hang off the
|
|
@@ -28820,20 +28882,20 @@ var KutrionClient = class {
|
|
|
28820
28882
|
// No write response carries the three list-only fields; re-list rather than deriving them.
|
|
28821
28883
|
// #2634: who revealed or used one credential. Vault owners and admins only; never carries the value.
|
|
28822
28884
|
credentialProvenance(vaultId, cid) {
|
|
28823
|
-
return this.req("GET", `/vault/${vaultId}/credentials/${cid}/provenance`);
|
|
28885
|
+
return this.req("GET", `/vault/${encodeURIComponent(vaultId)}/credentials/${encodeURIComponent(cid)}/provenance`);
|
|
28824
28886
|
}
|
|
28825
28887
|
// #3074: password history. The list carries no values; revealing one is a gated read like getCredential.
|
|
28826
28888
|
listCredentialHistory(vaultId, cid) {
|
|
28827
|
-
return this.req("GET", `/vault/${vaultId}/credentials/${cid}/history`);
|
|
28889
|
+
return this.req("GET", `/vault/${encodeURIComponent(vaultId)}/credentials/${encodeURIComponent(cid)}/history`);
|
|
28828
28890
|
}
|
|
28829
28891
|
revealCredentialHistory(vaultId, cid, hid) {
|
|
28830
|
-
return this.req("GET", `/vault/${vaultId}/credentials/${cid}/history/${hid}`);
|
|
28892
|
+
return this.req("GET", `/vault/${encodeURIComponent(vaultId)}/credentials/${encodeURIComponent(cid)}/history/${encodeURIComponent(hid)}`);
|
|
28831
28893
|
}
|
|
28832
28894
|
restoreCredentialHistory(vaultId, cid, hid) {
|
|
28833
|
-
return this.req("POST", `/vault/${vaultId}/credentials/${cid}/history/${hid}/restore`);
|
|
28895
|
+
return this.req("POST", `/vault/${encodeURIComponent(vaultId)}/credentials/${encodeURIComponent(cid)}/history/${encodeURIComponent(hid)}/restore`);
|
|
28834
28896
|
}
|
|
28835
28897
|
listCredentialDestinations(vaultId, cid) {
|
|
28836
|
-
return this.req("GET", `/vault/${vaultId}/credentials/${cid}/destinations`);
|
|
28898
|
+
return this.req("GET", `/vault/${encodeURIComponent(vaultId)}/credentials/${encodeURIComponent(cid)}/destinations`);
|
|
28837
28899
|
}
|
|
28838
28900
|
// `port` omitted means this protocol's default, resolved and stored server-side — NOT a wildcard, and
|
|
28839
28901
|
// not something to fill in here: a client-supplied default would come back as a chosen port.
|
|
@@ -28849,7 +28911,7 @@ var KutrionClient = class {
|
|
|
28849
28911
|
// names it. Sent on any other protocol it answers 400 `domain_not_supported`; unexpressible values
|
|
28850
28912
|
// answer 400 `invalid_domain`.
|
|
28851
28913
|
addCredentialDestination(vaultId, cid, input) {
|
|
28852
|
-
return this.req("POST", `/vault/${vaultId}/credentials/${cid}/destinations`, input);
|
|
28914
|
+
return this.req("POST", `/vault/${encodeURIComponent(vaultId)}/credentials/${encodeURIComponent(cid)}/destinations`, input);
|
|
28853
28915
|
}
|
|
28854
28916
|
// Gated on the secret for the same reason adding is, but NOT because removal widens — #1281 reversed
|
|
28855
28917
|
// that. Emptying a protocol while rows survive elsewhere leaves the credential claimed, so that
|
|
@@ -28861,7 +28923,7 @@ var KutrionClient = class {
|
|
|
28861
28923
|
removeCredentialDestination(vaultId, cid, destinationId, secret) {
|
|
28862
28924
|
return this.req(
|
|
28863
28925
|
"DELETE",
|
|
28864
|
-
`/vault/${vaultId}/credentials/${cid}/destinations/${destinationId}`,
|
|
28926
|
+
`/vault/${encodeURIComponent(vaultId)}/credentials/${encodeURIComponent(cid)}/destinations/${encodeURIComponent(destinationId)}`,
|
|
28865
28927
|
{ secret }
|
|
28866
28928
|
);
|
|
28867
28929
|
}
|
|
@@ -28901,20 +28963,20 @@ var KutrionClient = class {
|
|
|
28901
28963
|
return this.req("POST", "/vault/breach-alerts/scan");
|
|
28902
28964
|
}
|
|
28903
28965
|
updateBreachAlert(id, status) {
|
|
28904
|
-
return this.req("PATCH", `/vault/breach-alerts/${id}`, { status });
|
|
28966
|
+
return this.req("PATCH", `/vault/breach-alerts/${encodeURIComponent(id)}`, { status });
|
|
28905
28967
|
}
|
|
28906
28968
|
setBreachMonitor(enabled) {
|
|
28907
28969
|
return this.req("PUT", "/vault/breach-alerts/settings", { enabled });
|
|
28908
28970
|
}
|
|
28909
28971
|
importEnv(vaultId, name, dotenv) {
|
|
28910
|
-
return this.req("POST", `/vault/${vaultId}/env-import`, { name, dotenv });
|
|
28972
|
+
return this.req("POST", `/vault/${encodeURIComponent(vaultId)}/env-import`, { name, dotenv });
|
|
28911
28973
|
}
|
|
28912
28974
|
readEnv(vaultId, cid) {
|
|
28913
|
-
return this.req("GET", `/vault/${vaultId}/credentials/${cid}/env`);
|
|
28975
|
+
return this.req("GET", `/vault/${encodeURIComponent(vaultId)}/credentials/${encodeURIComponent(cid)}/env`);
|
|
28914
28976
|
}
|
|
28915
28977
|
// #96: live TOTP code for a TOTP credential (secret stays on the broker).
|
|
28916
28978
|
getTotpCode(vaultId, cid) {
|
|
28917
|
-
return this.req("GET", `/vault/${vaultId}/credentials/${cid}/totp`);
|
|
28979
|
+
return this.req("GET", `/vault/${encodeURIComponent(vaultId)}/credentials/${encodeURIComponent(cid)}/totp`);
|
|
28918
28980
|
}
|
|
28919
28981
|
// --- passkey provider (VT-405g): Kutrion as a roaming WebAuthn authenticator for OTHER sites
|
|
28920
28982
|
// (distinct from listPasskeys/deletePasskey above, which are passkeys for logging into Kutrion). ---
|
|
@@ -28935,7 +28997,7 @@ var KutrionClient = class {
|
|
|
28935
28997
|
return this.req("POST", "/share", { credential_id: credentialId, ...opts2 });
|
|
28936
28998
|
}
|
|
28937
28999
|
redeemShare(token) {
|
|
28938
|
-
return this.req("GET", `/share/${token}`, void 0, false);
|
|
29000
|
+
return this.req("GET", `/share/${encodeURIComponent(token)}`, void 0, false);
|
|
28939
29001
|
}
|
|
28940
29002
|
// #869: with a credential id, every link issued for that credential (needs read on it); without
|
|
28941
29003
|
// one, only the links the caller issued.
|
|
@@ -28943,14 +29005,14 @@ var KutrionClient = class {
|
|
|
28943
29005
|
return this.req("GET", `/share${credentialId ? `?credential_id=${encodeURIComponent(credentialId)}` : ""}`);
|
|
28944
29006
|
}
|
|
28945
29007
|
revokeShare(id) {
|
|
28946
|
-
return this.req("DELETE", `/share/${id}`);
|
|
29008
|
+
return this.req("DELETE", `/share/${encodeURIComponent(id)}`);
|
|
28947
29009
|
}
|
|
28948
29010
|
// --- import / export ---
|
|
28949
29011
|
exportVault(vaultId) {
|
|
28950
|
-
return this.req("GET", `/vault/${vaultId}/export`);
|
|
29012
|
+
return this.req("GET", `/vault/${encodeURIComponent(vaultId)}/export`);
|
|
28951
29013
|
}
|
|
28952
29014
|
importVault(vaultId, items) {
|
|
28953
|
-
return this.req("POST", `/vault/${vaultId}/import`, { items });
|
|
29015
|
+
return this.req("POST", `/vault/${encodeURIComponent(vaultId)}/import`, { items });
|
|
28954
29016
|
}
|
|
28955
29017
|
importProviders() {
|
|
28956
29018
|
return this.req("GET", "/vault/import/providers");
|
|
@@ -28988,7 +29050,7 @@ var KutrionClient = class {
|
|
|
28988
29050
|
* a move's `enabled:false` after the caller's fields precisely so one request can never both repoint a
|
|
28989
29051
|
* partner and turn it back on, and a client that batched them would be asking to be refused. */
|
|
28990
29052
|
setAs2PartnerEnabled(id, enabled) {
|
|
28991
|
-
return this.req("PATCH", `/as2/partners/${id}`, { enabled });
|
|
29053
|
+
return this.req("PATCH", `/as2/partners/${encodeURIComponent(id)}`, { enabled });
|
|
28992
29054
|
}
|
|
28993
29055
|
listAs2Messages() {
|
|
28994
29056
|
return this.req("GET", "/as2/messages");
|
|
@@ -29001,33 +29063,33 @@ var KutrionClient = class {
|
|
|
29001
29063
|
return this.req("POST", "/teams", { name });
|
|
29002
29064
|
}
|
|
29003
29065
|
listMembers(teamId) {
|
|
29004
|
-
return this.req("GET", `/teams/${teamId}/members`);
|
|
29066
|
+
return this.req("GET", `/teams/${encodeURIComponent(teamId)}/members`);
|
|
29005
29067
|
}
|
|
29006
29068
|
addMember(teamId, email, role) {
|
|
29007
|
-
return this.req("POST", `/teams/${teamId}/members`, { email, role });
|
|
29069
|
+
return this.req("POST", `/teams/${encodeURIComponent(teamId)}/members`, { email, role });
|
|
29008
29070
|
}
|
|
29009
29071
|
removeMember(teamId, userId) {
|
|
29010
|
-
return this.req("DELETE", `/teams/${teamId}/members/${userId}`);
|
|
29072
|
+
return this.req("DELETE", `/teams/${encodeURIComponent(teamId)}/members/${encodeURIComponent(userId)}`);
|
|
29011
29073
|
}
|
|
29012
29074
|
// #2775: org owner or admin only. 409 team_has_vaults or scim_managed.
|
|
29013
29075
|
deleteTeam(teamId) {
|
|
29014
|
-
return this.req("DELETE", `/teams/${teamId}`);
|
|
29076
|
+
return this.req("DELETE", `/teams/${encodeURIComponent(teamId)}`);
|
|
29015
29077
|
}
|
|
29016
29078
|
// --- team invitations (VT-102) ---
|
|
29017
29079
|
inviteMember(teamId, email, role) {
|
|
29018
|
-
return this.req("POST", `/teams/${teamId}/invitations`, { email, role });
|
|
29080
|
+
return this.req("POST", `/teams/${encodeURIComponent(teamId)}/invitations`, { email, role });
|
|
29019
29081
|
}
|
|
29020
29082
|
listInvitations(teamId) {
|
|
29021
|
-
return this.req("GET", `/teams/${teamId}/invitations`);
|
|
29083
|
+
return this.req("GET", `/teams/${encodeURIComponent(teamId)}/invitations`);
|
|
29022
29084
|
}
|
|
29023
29085
|
revokeInvitation(teamId, invId) {
|
|
29024
|
-
return this.req("DELETE", `/teams/${teamId}/invitations/${invId}`);
|
|
29086
|
+
return this.req("DELETE", `/teams/${encodeURIComponent(teamId)}/invitations/${encodeURIComponent(invId)}`);
|
|
29025
29087
|
}
|
|
29026
29088
|
invitePreview(token) {
|
|
29027
|
-
return this.req("GET", `/teams/invitations/${token}`, void 0, false);
|
|
29089
|
+
return this.req("GET", `/teams/invitations/${encodeURIComponent(token)}`, void 0, false);
|
|
29028
29090
|
}
|
|
29029
29091
|
acceptInvitation(token) {
|
|
29030
|
-
return this.req("POST", `/teams/invitations/${token}/accept`);
|
|
29092
|
+
return this.req("POST", `/teams/invitations/${encodeURIComponent(token)}/accept`);
|
|
29031
29093
|
}
|
|
29032
29094
|
// --- org onboarding (MT-4 #115): admin/owner-gated unless noted ---
|
|
29033
29095
|
getOrg() {
|
|
@@ -29088,10 +29150,10 @@ var KutrionClient = class {
|
|
|
29088
29150
|
return this.req("POST", "/emergency/contacts", input);
|
|
29089
29151
|
}
|
|
29090
29152
|
removeEmergencyContact(id) {
|
|
29091
|
-
return this.req("DELETE", `/emergency/contacts/${id}`);
|
|
29153
|
+
return this.req("DELETE", `/emergency/contacts/${encodeURIComponent(id)}`);
|
|
29092
29154
|
}
|
|
29093
29155
|
denyEmergencyRequest(id) {
|
|
29094
|
-
return this.req("POST", `/emergency/requests/${id}/deny`);
|
|
29156
|
+
return this.req("POST", `/emergency/requests/${encodeURIComponent(id)}/deny`);
|
|
29095
29157
|
}
|
|
29096
29158
|
listEmergencyGrants() {
|
|
29097
29159
|
return this.req("GET", "/emergency/grants");
|
|
@@ -29100,10 +29162,10 @@ var KutrionClient = class {
|
|
|
29100
29162
|
return this.req("POST", "/emergency/grants/accept", { token });
|
|
29101
29163
|
}
|
|
29102
29164
|
requestEmergencyAccess(grantId) {
|
|
29103
|
-
return this.req("POST", `/emergency/grants/${grantId}/request`);
|
|
29165
|
+
return this.req("POST", `/emergency/grants/${encodeURIComponent(grantId)}/request`);
|
|
29104
29166
|
}
|
|
29105
29167
|
completeEmergencyAccess(requestId) {
|
|
29106
|
-
return this.req("POST", `/emergency/requests/${requestId}/complete`);
|
|
29168
|
+
return this.req("POST", `/emergency/requests/${encodeURIComponent(requestId)}/complete`);
|
|
29107
29169
|
}
|
|
29108
29170
|
// VT-443: the user-facing announcement feed (filtered server-side by the org's tier/status).
|
|
29109
29171
|
listAnnouncements() {
|
|
@@ -29193,7 +29255,7 @@ var KutrionClient = class {
|
|
|
29193
29255
|
// #1959: org-admin surface grants for one member of the caller's own org. Distinct from
|
|
29194
29256
|
// selfGrantSurface, which only ever acts on the caller.
|
|
29195
29257
|
listUserSurfaceGrants(userId) {
|
|
29196
|
-
return this.req("GET", `/surfaces/users/${userId}`);
|
|
29258
|
+
return this.req("GET", `/surfaces/users/${encodeURIComponent(userId)}`);
|
|
29197
29259
|
}
|
|
29198
29260
|
grantUserSurface(userId, surface, opts2) {
|
|
29199
29261
|
return this.req("POST", "/surfaces/grants", {
|
|
@@ -29294,15 +29356,15 @@ var KutrionClient = class {
|
|
|
29294
29356
|
return this.req("GET", "/org/members");
|
|
29295
29357
|
}
|
|
29296
29358
|
setOrgMemberRole(id, org_role) {
|
|
29297
|
-
return this.req("PATCH", `/org/members/${id}`, { org_role });
|
|
29359
|
+
return this.req("PATCH", `/org/members/${encodeURIComponent(id)}`, { org_role });
|
|
29298
29360
|
}
|
|
29299
29361
|
removeOrgMember(id) {
|
|
29300
|
-
return this.req("DELETE", `/org/members/${id}`);
|
|
29362
|
+
return this.req("DELETE", `/org/members/${encodeURIComponent(id)}`);
|
|
29301
29363
|
}
|
|
29302
29364
|
// VT-483: admin-mediated recovery — email a locked-out member a password-reset link/code. The admin
|
|
29303
29365
|
// never sets or learns the new password.
|
|
29304
29366
|
resetOrgMemberPassword(id) {
|
|
29305
|
-
return this.req("POST", `/org/members/${id}/password-reset`);
|
|
29367
|
+
return this.req("POST", `/org/members/${encodeURIComponent(id)}/password-reset`);
|
|
29306
29368
|
}
|
|
29307
29369
|
// #1433: admin-mediated ACCOUNT RECOVERY, for a member who cannot sign in AT ALL. Distinct from the
|
|
29308
29370
|
// password reset above, which refuses exactly the accounts that most need it: an SSO- or
|
|
@@ -29314,7 +29376,7 @@ var KutrionClient = class {
|
|
|
29314
29376
|
// step_up_required), refuses a suspended member (409 member_inactive), and refuses a non-owner admin
|
|
29315
29377
|
// who targets an owner (403 owner_recovery_requires_owner).
|
|
29316
29378
|
recoverOrgMember(id) {
|
|
29317
|
-
return this.req("POST", `/org/members/${id}/recover`);
|
|
29379
|
+
return this.req("POST", `/org/members/${encodeURIComponent(id)}/recover`);
|
|
29318
29380
|
}
|
|
29319
29381
|
inviteToOrg(email, org_role) {
|
|
29320
29382
|
return this.req("POST", "/org/invitations", { email, org_role });
|
|
@@ -29323,13 +29385,13 @@ var KutrionClient = class {
|
|
|
29323
29385
|
return this.req("GET", "/org/invitations");
|
|
29324
29386
|
}
|
|
29325
29387
|
revokeOrgInvitation(id) {
|
|
29326
|
-
return this.req("DELETE", `/org/invitations/${id}`);
|
|
29388
|
+
return this.req("DELETE", `/org/invitations/${encodeURIComponent(id)}`);
|
|
29327
29389
|
}
|
|
29328
29390
|
orgInvitePreview(token) {
|
|
29329
|
-
return this.req("GET", `/org/invitations/${token}`, void 0, false);
|
|
29391
|
+
return this.req("GET", `/org/invitations/${encodeURIComponent(token)}`, void 0, false);
|
|
29330
29392
|
}
|
|
29331
29393
|
async acceptOrgInvitation(token) {
|
|
29332
|
-
const r = await this.req("POST", `/org/invitations/${token}/accept`);
|
|
29394
|
+
const r = await this.req("POST", `/org/invitations/${encodeURIComponent(token)}/accept`);
|
|
29333
29395
|
this.onToken?.(r.token);
|
|
29334
29396
|
return r;
|
|
29335
29397
|
}
|
|
@@ -29342,18 +29404,18 @@ var KutrionClient = class {
|
|
|
29342
29404
|
return this.req("POST", "/msp/clients", { client_email, role, ...expires_days ? { expires_days } : {} });
|
|
29343
29405
|
}
|
|
29344
29406
|
revokeMspClient(id) {
|
|
29345
|
-
return this.req("POST", `/msp/clients/${id}/revoke`);
|
|
29407
|
+
return this.req("POST", `/msp/clients/${encodeURIComponent(id)}/revoke`);
|
|
29346
29408
|
}
|
|
29347
29409
|
// Mint a read-only "view as client" scoped session (#415c). The token is used as the active bearer;
|
|
29348
29410
|
// the existing impersonation guard makes it read-only. Revoking the relationship kills it instantly.
|
|
29349
29411
|
viewAsMspClient(id) {
|
|
29350
|
-
return this.req("POST", `/msp/clients/${id}/view-as`);
|
|
29412
|
+
return this.req("POST", `/msp/clients/${encodeURIComponent(id)}/view-as`);
|
|
29351
29413
|
}
|
|
29352
29414
|
// #432: enter a WRITE-capable "act as client" session at the clamped effective role. Requires a fresh
|
|
29353
29415
|
// step-up; writes are bounded by the deny-by-default server allowlist. Returns 403 step_up_required if
|
|
29354
29416
|
// the operator has not recently verified a passkey/MFA, 409 read_only_relationship for a VIEWER ceiling.
|
|
29355
29417
|
actAsMspClient(id, role = "EDITOR") {
|
|
29356
|
-
return this.req("POST", `/msp/clients/${id}/act-as`, { role });
|
|
29418
|
+
return this.req("POST", `/msp/clients/${encodeURIComponent(id)}/act-as`, { role });
|
|
29357
29419
|
}
|
|
29358
29420
|
// #415d: the MSP's own access ledger across managed clients (sessions opened, last access).
|
|
29359
29421
|
mspClientsActivity() {
|
|
@@ -29377,13 +29439,13 @@ var KutrionClient = class {
|
|
|
29377
29439
|
return this.req("GET", "/msp/providers");
|
|
29378
29440
|
}
|
|
29379
29441
|
acceptMspProvider(id) {
|
|
29380
|
-
return this.req("POST", `/msp/providers/${id}/accept`);
|
|
29442
|
+
return this.req("POST", `/msp/providers/${encodeURIComponent(id)}/accept`);
|
|
29381
29443
|
}
|
|
29382
29444
|
declineMspProvider(id) {
|
|
29383
|
-
return this.req("POST", `/msp/providers/${id}/decline`);
|
|
29445
|
+
return this.req("POST", `/msp/providers/${encodeURIComponent(id)}/decline`);
|
|
29384
29446
|
}
|
|
29385
29447
|
revokeMspProvider(id) {
|
|
29386
|
-
return this.req("POST", `/msp/providers/${id}/revoke`);
|
|
29448
|
+
return this.req("POST", `/msp/providers/${encodeURIComponent(id)}/revoke`);
|
|
29387
29449
|
}
|
|
29388
29450
|
// #434: what my providers did in my org (read-only-session opens etc.), for client-side transparency.
|
|
29389
29451
|
mspProviderActivity() {
|
|
@@ -29417,13 +29479,13 @@ var KutrionClient = class {
|
|
|
29417
29479
|
setMemberActive(memberId, active, reason) {
|
|
29418
29480
|
return this.req(
|
|
29419
29481
|
"PATCH",
|
|
29420
|
-
`/org/members/${memberId}/active`,
|
|
29482
|
+
`/org/members/${encodeURIComponent(memberId)}/active`,
|
|
29421
29483
|
{ active, ...reason ? { reason } : {} }
|
|
29422
29484
|
);
|
|
29423
29485
|
}
|
|
29424
29486
|
// Offboarding: kill every offline copy a member holds, from the admin side.
|
|
29425
29487
|
revokeMemberCacheKeys(memberId) {
|
|
29426
|
-
return this.req("DELETE", `/org/members/${memberId}/cache-keys`);
|
|
29488
|
+
return this.req("DELETE", `/org/members/${encodeURIComponent(memberId)}/cache-keys`);
|
|
29427
29489
|
}
|
|
29428
29490
|
// #815: the other half of that revoke — which devices came back and were told to wipe, and which
|
|
29429
29491
|
// have not been seen since. Org-admin only.
|
|
@@ -29439,7 +29501,7 @@ var KutrionClient = class {
|
|
|
29439
29501
|
return this.req("POST", "/org/equivalent-domains", { domains, note: note ?? null });
|
|
29440
29502
|
}
|
|
29441
29503
|
removeEquivalentDomains(id) {
|
|
29442
|
-
return this.req("DELETE", `/org/equivalent-domains/${id}`);
|
|
29504
|
+
return this.req("DELETE", `/org/equivalent-domains/${encodeURIComponent(id)}`);
|
|
29443
29505
|
}
|
|
29444
29506
|
// #904: the caller's OWN remembered host bindings. Per user and per (credential, host) — never per
|
|
29445
29507
|
// org, which would make phishing one member a lateral-movement primitive. Creation is screened
|
|
@@ -29449,10 +29511,10 @@ var KutrionClient = class {
|
|
|
29449
29511
|
return this.req("GET", "/vault/site-bindings");
|
|
29450
29512
|
}
|
|
29451
29513
|
createSiteBinding(vaultId, cid, targetUrl, sourceUrl) {
|
|
29452
|
-
return this.req("POST", `/vault/${vaultId}/credentials/${cid}/site-bindings`, { target_url: targetUrl, source_url: sourceUrl });
|
|
29514
|
+
return this.req("POST", `/vault/${encodeURIComponent(vaultId)}/credentials/${encodeURIComponent(cid)}/site-bindings`, { target_url: targetUrl, source_url: sourceUrl });
|
|
29453
29515
|
}
|
|
29454
29516
|
revokeSiteBinding(id) {
|
|
29455
|
-
return this.req("DELETE", `/vault/site-bindings/${id}`);
|
|
29517
|
+
return this.req("DELETE", `/vault/site-bindings/${encodeURIComponent(id)}`);
|
|
29456
29518
|
}
|
|
29457
29519
|
listOrgDomains() {
|
|
29458
29520
|
return this.req("GET", "/org/domains");
|
|
@@ -29461,10 +29523,10 @@ var KutrionClient = class {
|
|
|
29461
29523
|
return this.req("POST", "/org/domains", { domain });
|
|
29462
29524
|
}
|
|
29463
29525
|
verifyOrgDomain(id) {
|
|
29464
|
-
return this.req("POST", `/org/domains/${id}/verify`);
|
|
29526
|
+
return this.req("POST", `/org/domains/${encodeURIComponent(id)}/verify`);
|
|
29465
29527
|
}
|
|
29466
29528
|
removeOrgDomain(id) {
|
|
29467
|
-
return this.req("DELETE", `/org/domains/${id}`);
|
|
29529
|
+
return this.req("DELETE", `/org/domains/${encodeURIComponent(id)}`);
|
|
29468
29530
|
}
|
|
29469
29531
|
// VT-485/486: enterprise API tokens for SCIM provisioning and SIEM event export. The secret is
|
|
29470
29532
|
// returned only on create; thereafter only the prefix is shown.
|
|
@@ -29475,7 +29537,7 @@ var KutrionClient = class {
|
|
|
29475
29537
|
return this.req("POST", "/api-tokens", input);
|
|
29476
29538
|
}
|
|
29477
29539
|
revokeApiToken(id) {
|
|
29478
|
-
return this.req("DELETE", `/api-tokens/${id}`);
|
|
29540
|
+
return this.req("DELETE", `/api-tokens/${encodeURIComponent(id)}`);
|
|
29479
29541
|
}
|
|
29480
29542
|
// #662 agent access plane: non-human identities (#664), their activity (#665) and the kill
|
|
29481
29543
|
// switch (#675). The credential is returned only on create; thereafter only the prefix is shown.
|
|
@@ -29490,23 +29552,23 @@ var KutrionClient = class {
|
|
|
29490
29552
|
return this.req("POST", "/agents", input);
|
|
29491
29553
|
}
|
|
29492
29554
|
renewAgent(id, ttl_minutes) {
|
|
29493
|
-
return this.req("POST", `/agents/${id}/renew`, { ttl_minutes });
|
|
29555
|
+
return this.req("POST", `/agents/${encodeURIComponent(id)}/renew`, { ttl_minutes });
|
|
29494
29556
|
}
|
|
29495
29557
|
revokeAgent(id) {
|
|
29496
|
-
return this.req("DELETE", `/agents/${id}`);
|
|
29558
|
+
return this.req("DELETE", `/agents/${encodeURIComponent(id)}`);
|
|
29497
29559
|
}
|
|
29498
29560
|
// #2700: PUT replaces the whole resource scope; an omitted or empty list means unrestricted.
|
|
29499
29561
|
agentScopeOptions() {
|
|
29500
29562
|
return this.req("GET", "/agents/scope-options");
|
|
29501
29563
|
}
|
|
29502
29564
|
setAgentResourceScope(id, scope) {
|
|
29503
|
-
return this.req("PUT", `/agents/${id}/resource-scope`, scope);
|
|
29565
|
+
return this.req("PUT", `/agents/${encodeURIComponent(id)}/resource-scope`, scope);
|
|
29504
29566
|
}
|
|
29505
29567
|
listAgentOidcTrusts() {
|
|
29506
29568
|
return this.req("GET", "/agents/oidc/trusts");
|
|
29507
29569
|
}
|
|
29508
29570
|
setAgentOidcTrustResourceScope(id, scope, opts2 = {}) {
|
|
29509
|
-
return this.req("PUT", `/agents/oidc/trusts/${id}/resource-scope`, { ...scope, ...opts2 });
|
|
29571
|
+
return this.req("PUT", `/agents/oidc/trusts/${encodeURIComponent(id)}/resource-scope`, { ...scope, ...opts2 });
|
|
29510
29572
|
}
|
|
29511
29573
|
revokeAgents(input) {
|
|
29512
29574
|
return this.req(
|
|
@@ -29523,7 +29585,7 @@ var KutrionClient = class {
|
|
|
29523
29585
|
return this.req("GET", qs ? `/agents/sessions?${qs}` : "/agents/sessions");
|
|
29524
29586
|
}
|
|
29525
29587
|
getAgentSession(id) {
|
|
29526
|
-
return this.req("GET", `/agents/sessions/${id}`);
|
|
29588
|
+
return this.req("GET", `/agents/sessions/${encodeURIComponent(id)}`);
|
|
29527
29589
|
}
|
|
29528
29590
|
listAgentActivity(opts2 = {}) {
|
|
29529
29591
|
const q = new URLSearchParams();
|
|
@@ -29533,7 +29595,7 @@ var KutrionClient = class {
|
|
|
29533
29595
|
}
|
|
29534
29596
|
/** Relative path for the auditor export; the caller downloads it with its own auth. */
|
|
29535
29597
|
agentSessionExportPath(id) {
|
|
29536
|
-
return `/agents/sessions/${id}/export`;
|
|
29598
|
+
return `/agents/sessions/${encodeURIComponent(id)}/export`;
|
|
29537
29599
|
}
|
|
29538
29600
|
// #669: the human half of just-in-time agent approval. These routes are deliberately unreachable
|
|
29539
29601
|
// from an MCP client — the agent opens a request and settles nothing, a person in an authenticated
|
|
@@ -29544,13 +29606,13 @@ var KutrionClient = class {
|
|
|
29544
29606
|
return this.req("GET", "/agent-approvals");
|
|
29545
29607
|
}
|
|
29546
29608
|
getAgentApproval(id) {
|
|
29547
|
-
return this.req("GET", `/agent-approvals/${id}`);
|
|
29609
|
+
return this.req("GET", `/agent-approvals/${encodeURIComponent(id)}`);
|
|
29548
29610
|
}
|
|
29549
29611
|
approveAgentApproval(id, note) {
|
|
29550
|
-
return this.req("POST", `/agent-approvals/${id}/approve`, note ? { note } : {});
|
|
29612
|
+
return this.req("POST", `/agent-approvals/${encodeURIComponent(id)}/approve`, note ? { note } : {});
|
|
29551
29613
|
}
|
|
29552
29614
|
denyAgentApproval(id, note) {
|
|
29553
|
-
return this.req("POST", `/agent-approvals/${id}/deny`, note ? { note } : {});
|
|
29615
|
+
return this.req("POST", `/agent-approvals/${encodeURIComponent(id)}/deny`, note ? { note } : {});
|
|
29554
29616
|
}
|
|
29555
29617
|
// #670 (epic #662): the outbound MCP gateway's control plane. Curation is ORG ADMIN; only the
|
|
29556
29618
|
// report-what-I-run call is open to any member, and that asymmetry is deliberate — the machines
|
|
@@ -29573,14 +29635,14 @@ var KutrionClient = class {
|
|
|
29573
29635
|
/** Aim a credential at an approved entry. Authorised by WRITE on the credential's own vault, not by
|
|
29574
29636
|
* org admin — binding is an act of delegation, so 403 here is about the vault and not the console. */
|
|
29575
29637
|
bindMcpCredential(entryId, credential_id) {
|
|
29576
|
-
return this.req("POST", `/mcp/gateway/servers/${entryId}/credential`, { credential_id });
|
|
29638
|
+
return this.req("POST", `/mcp/gateway/servers/${encodeURIComponent(entryId)}/credential`, { credential_id });
|
|
29577
29639
|
}
|
|
29578
29640
|
unbindMcpCredential(entryId) {
|
|
29579
|
-
return this.req("DELETE", `/mcp/gateway/servers/${entryId}/credential`);
|
|
29641
|
+
return this.req("DELETE", `/mcp/gateway/servers/${encodeURIComponent(entryId)}/credential`);
|
|
29580
29642
|
}
|
|
29581
29643
|
/** Stops traffic on the next call, with nothing cached in between. */
|
|
29582
29644
|
revokeMcpServer(entryId) {
|
|
29583
|
-
return this.req("DELETE", `/mcp/gateway/servers/${entryId}`);
|
|
29645
|
+
return this.req("DELETE", `/mcp/gateway/servers/${encodeURIComponent(entryId)}`);
|
|
29584
29646
|
}
|
|
29585
29647
|
listMcpObserved() {
|
|
29586
29648
|
return this.req("GET", "/mcp/gateway/observed");
|
|
@@ -29613,10 +29675,10 @@ var KutrionClient = class {
|
|
|
29613
29675
|
// which leaves it as it is — a connection could otherwise never be made direct again.
|
|
29614
29676
|
// #2621: `vault_id` shares a personal host into that vault, one way; its credential must live there.
|
|
29615
29677
|
updateConnection(connId, input) {
|
|
29616
|
-
return this.req("PATCH", `/ssh/connections/${connId}`, input);
|
|
29678
|
+
return this.req("PATCH", `/ssh/connections/${encodeURIComponent(connId)}`, input);
|
|
29617
29679
|
}
|
|
29618
29680
|
deleteConnection(connId) {
|
|
29619
|
-
return this.req("DELETE", `/ssh/connections/${connId}`);
|
|
29681
|
+
return this.req("DELETE", `/ssh/connections/${encodeURIComponent(connId)}`);
|
|
29620
29682
|
}
|
|
29621
29683
|
// #414: self-serve secure-session demo. status drives the empty-state affordance; provision is
|
|
29622
29684
|
// idempotent (reattaches to an existing demo connection); reset removes the demo artifacts.
|
|
@@ -29632,7 +29694,7 @@ var KutrionClient = class {
|
|
|
29632
29694
|
// Pre-flight risk guardrail (#141): a high-risk command 403s with { error: "step_up_required" }
|
|
29633
29695
|
// until the session completes WebAuthn step-up; retry after stepping up.
|
|
29634
29696
|
exec(connId, command) {
|
|
29635
|
-
return this.req("POST", `/ssh/connections/${connId}/exec`, { command });
|
|
29697
|
+
return this.req("POST", `/ssh/connections/${encodeURIComponent(connId)}/exec`, { command });
|
|
29636
29698
|
}
|
|
29637
29699
|
validateCommand(command) {
|
|
29638
29700
|
return this.req("POST", "/ssh/validate", { command });
|
|
@@ -29715,7 +29777,7 @@ var KutrionClient = class {
|
|
|
29715
29777
|
}
|
|
29716
29778
|
/** 403 step_up_required until the session has a fresh passkey/MFA check; retry after stepping up. */
|
|
29717
29779
|
clearSshHostKey(id) {
|
|
29718
|
-
return this.req("DELETE", `/ssh/host-keys/${id}`);
|
|
29780
|
+
return this.req("DELETE", `/ssh/host-keys/${encodeURIComponent(id)}`);
|
|
29719
29781
|
}
|
|
29720
29782
|
/**
|
|
29721
29783
|
* #2743: pins the key a host_key_unconfirmed refusal showed this caller. For a first-use refusal the server accepts only the
|
|
@@ -29733,7 +29795,7 @@ var KutrionClient = class {
|
|
|
29733
29795
|
}
|
|
29734
29796
|
/** 404 when the session already ended, or is not in the caller's org. */
|
|
29735
29797
|
endLiveSession(kind, id) {
|
|
29736
|
-
return this.req("DELETE", `/org/live-sessions/${kind}/${encodeURIComponent(id)}`);
|
|
29798
|
+
return this.req("DELETE", `/org/live-sessions/${encodeURIComponent(kind)}/${encodeURIComponent(id)}`);
|
|
29737
29799
|
}
|
|
29738
29800
|
// --- SSH certificate authority (VT-484): mint short-lived user certs from a per-org internal CA ---
|
|
29739
29801
|
getSshCa() {
|
|
@@ -29828,29 +29890,29 @@ var KutrionClient = class {
|
|
|
29828
29890
|
// It does NOT mean "the whole directory": the routes default to limit=1000 and report `hasMore`.
|
|
29829
29891
|
// --- SFTP file transfer (VT-112) ---
|
|
29830
29892
|
sftpList(connId, path = ".", page) {
|
|
29831
|
-
return this.req("GET", `/ssh/connections/${connId}/sftp/list?path=${encodeURIComponent(path)}${listPageQuery(page)}`);
|
|
29893
|
+
return this.req("GET", `/ssh/connections/${encodeURIComponent(connId)}/sftp/list?path=${encodeURIComponent(path)}${listPageQuery(page)}`);
|
|
29832
29894
|
}
|
|
29833
29895
|
sftpMkdir(connId, path) {
|
|
29834
|
-
return this.req("POST", `/ssh/connections/${connId}/sftp/mkdir`, { path });
|
|
29896
|
+
return this.req("POST", `/ssh/connections/${encodeURIComponent(connId)}/sftp/mkdir`, { path });
|
|
29835
29897
|
}
|
|
29836
29898
|
sftpRename(connId, from, to) {
|
|
29837
|
-
return this.req("POST", `/ssh/connections/${connId}/sftp/rename`, { from, to });
|
|
29899
|
+
return this.req("POST", `/ssh/connections/${encodeURIComponent(connId)}/sftp/rename`, { from, to });
|
|
29838
29900
|
}
|
|
29839
29901
|
sftpDelete(connId, path, isDir) {
|
|
29840
|
-
return this.req("POST", `/ssh/connections/${connId}/sftp/delete`, { path, is_dir: isDir });
|
|
29902
|
+
return this.req("POST", `/ssh/connections/${encodeURIComponent(connId)}/sftp/delete`, { path, is_dir: isDir });
|
|
29841
29903
|
}
|
|
29842
29904
|
// #587: SFTP checksums + duplicate detection (mirror the file-connection routes).
|
|
29843
29905
|
sftpChecksum(connId, path, algos = ["sha256"]) {
|
|
29844
|
-
return this.req("GET", `/ssh/connections/${connId}/sftp/checksum?path=${encodeURIComponent(path)}&algos=${algos.join(",")}`);
|
|
29906
|
+
return this.req("GET", `/ssh/connections/${encodeURIComponent(connId)}/sftp/checksum?path=${encodeURIComponent(path)}&algos=${algos.join(",")}`);
|
|
29845
29907
|
}
|
|
29846
29908
|
sftpDuplicates(connId, path = ".", opts2) {
|
|
29847
|
-
return this.req("POST", `/ssh/connections/${connId}/sftp/duplicates`, { path, maxFiles: opts2?.maxFiles, maxBytesPerFile: opts2?.maxBytesPerFile });
|
|
29909
|
+
return this.req("POST", `/ssh/connections/${encodeURIComponent(connId)}/sftp/duplicates`, { path, maxFiles: opts2?.maxFiles, maxBytesPerFile: opts2?.maxBytesPerFile });
|
|
29848
29910
|
}
|
|
29849
29911
|
// Download a remote file as a Blob (carries the auth token). With { redact: true } the server
|
|
29850
29912
|
// routes the bytes through the #142 redaction gate (text only; binary → 415) before sending (#320c).
|
|
29851
29913
|
async sftpDownload(connId, path, opts2) {
|
|
29852
29914
|
const tok = this.getToken();
|
|
29853
|
-
const res = await this.f(`${this.baseUrl}/ssh/connections/${connId}/sftp/download?path=${encodeURIComponent(path)}${opts2?.redact ? "&redact=1" : ""}`, {
|
|
29915
|
+
const res = await this.f(`${this.baseUrl}/ssh/connections/${encodeURIComponent(connId)}/sftp/download?path=${encodeURIComponent(path)}${opts2?.redact ? "&redact=1" : ""}`, {
|
|
29854
29916
|
headers: this.rawHeaders(tok)
|
|
29855
29917
|
});
|
|
29856
29918
|
if (!res.ok) {
|
|
@@ -29906,12 +29968,12 @@ var KutrionClient = class {
|
|
|
29906
29968
|
// Stream a remote file's bytes instead of buffering them into a Blob. `opts.start` resumes from an
|
|
29907
29969
|
// offset via Range (#555); read `resumedAt` off the result rather than assuming the server obeyed.
|
|
29908
29970
|
sftpDownloadStream(connId, path, opts2) {
|
|
29909
|
-
return this.downloadStream(`${this.baseUrl}/ssh/connections/${connId}/sftp/download?path=${encodeURIComponent(path)}${opts2?.redact ? "&redact=1" : ""}`, opts2);
|
|
29971
|
+
return this.downloadStream(`${this.baseUrl}/ssh/connections/${encodeURIComponent(connId)}/sftp/download?path=${encodeURIComponent(path)}${opts2?.redact ? "&redact=1" : ""}`, opts2);
|
|
29910
29972
|
}
|
|
29911
29973
|
// Upload from a byte source without materialising it. `streamed` on the result reports whether this
|
|
29912
29974
|
// runtime actually streamed the request body or had to collect it first.
|
|
29913
29975
|
sftpUploadStream(connId, path, body, opts2) {
|
|
29914
|
-
return this.uploadStream(`${this.baseUrl}/ssh/connections/${connId}/sftp/upload?path=${encodeURIComponent(path)}`, body, opts2);
|
|
29976
|
+
return this.uploadStream(`${this.baseUrl}/ssh/connections/${encodeURIComponent(connId)}/sftp/upload?path=${encodeURIComponent(path)}`, body, opts2);
|
|
29915
29977
|
}
|
|
29916
29978
|
// #2556: throws UploadTooLargeToInspectError when this org inspects uploads and `size` is over what the
|
|
29917
29979
|
// inspection can hold, so the file is refused before any of it is sent. A policy that cannot be read
|
|
@@ -29926,7 +29988,7 @@ var KutrionClient = class {
|
|
|
29926
29988
|
throw new UploadTooLargeToInspectError(size, insp.max_bytes, insp.enabled ? "too_large_for_scan" : "too_large_for_policy");
|
|
29927
29989
|
}
|
|
29928
29990
|
sftpUploadResumable(connId, path, source, opts2) {
|
|
29929
|
-
return this.uploadResumable(`/ssh/connections/${connId}/sftp/upload`, path, source, opts2);
|
|
29991
|
+
return this.uploadResumable(`/ssh/connections/${encodeURIComponent(connId)}/sftp/upload`, path, source, opts2);
|
|
29930
29992
|
}
|
|
29931
29993
|
// #2556: one request per UPLOAD_CHUNK_BYTES over the #1566 resume protocol, so a file of any size
|
|
29932
29994
|
// crosses an edge that caps a request body, and a dropped chunk is re-sent from the offset the
|
|
@@ -30055,26 +30117,26 @@ var KutrionClient = class {
|
|
|
30055
30117
|
return this.req("POST", "/files/connections", input);
|
|
30056
30118
|
}
|
|
30057
30119
|
updateFileConnection(id, input) {
|
|
30058
|
-
return this.req("PATCH", `/files/connections/${id}`, input);
|
|
30120
|
+
return this.req("PATCH", `/files/connections/${encodeURIComponent(id)}`, input);
|
|
30059
30121
|
}
|
|
30060
30122
|
deleteFileConnection(id) {
|
|
30061
|
-
return this.req("DELETE", `/files/connections/${id}`);
|
|
30123
|
+
return this.req("DELETE", `/files/connections/${encodeURIComponent(id)}`);
|
|
30062
30124
|
}
|
|
30063
30125
|
fileList(connId, path = "/", page) {
|
|
30064
|
-
return this.req("GET", `/files/connections/${connId}/list?path=${encodeURIComponent(path)}${listPageQuery(page)}`);
|
|
30126
|
+
return this.req("GET", `/files/connections/${encodeURIComponent(connId)}/list?path=${encodeURIComponent(path)}${listPageQuery(page)}`);
|
|
30065
30127
|
}
|
|
30066
30128
|
// #587: checksums + duplicate detection.
|
|
30067
30129
|
fileChecksum(connId, path, algos = ["sha256"]) {
|
|
30068
|
-
return this.req("GET", `/files/connections/${connId}/checksum?path=${encodeURIComponent(path)}&algos=${algos.join(",")}`);
|
|
30130
|
+
return this.req("GET", `/files/connections/${encodeURIComponent(connId)}/checksum?path=${encodeURIComponent(path)}&algos=${algos.join(",")}`);
|
|
30069
30131
|
}
|
|
30070
30132
|
fileDuplicates(connId, path = "/", opts2) {
|
|
30071
|
-
return this.req("POST", `/files/connections/${connId}/duplicates`, { path, maxFiles: opts2?.maxFiles, maxBytesPerFile: opts2?.maxBytesPerFile });
|
|
30133
|
+
return this.req("POST", `/files/connections/${encodeURIComponent(connId)}/duplicates`, { path, maxFiles: opts2?.maxFiles, maxBytesPerFile: opts2?.maxBytesPerFile });
|
|
30072
30134
|
}
|
|
30073
30135
|
// #586: recursive CONTENT search under `path`. Bytes are read on the server and only path/line/
|
|
30074
30136
|
// snippet come back. `glob` filters which files are opened by name; it does not match names into
|
|
30075
30137
|
// the result. Check `truncated` before presenting the matches as the whole answer.
|
|
30076
30138
|
fileSearch(connId, path, query, opts2) {
|
|
30077
|
-
return this.req("POST", `/files/connections/${connId}/search`, {
|
|
30139
|
+
return this.req("POST", `/files/connections/${encodeURIComponent(connId)}/search`, {
|
|
30078
30140
|
path,
|
|
30079
30141
|
query,
|
|
30080
30142
|
glob: opts2?.glob,
|
|
@@ -30084,39 +30146,39 @@ var KutrionClient = class {
|
|
|
30084
30146
|
}
|
|
30085
30147
|
// #588: time-limited presigned direct-download URL (S3 only).
|
|
30086
30148
|
filePresign(connId, path, expiresSec = 3600) {
|
|
30087
|
-
return this.req("GET", `/files/connections/${connId}/presign?path=${encodeURIComponent(path)}&expires=${expiresSec}`);
|
|
30149
|
+
return this.req("GET", `/files/connections/${encodeURIComponent(connId)}/presign?path=${encodeURIComponent(path)}&expires=${expiresSec}`);
|
|
30088
30150
|
}
|
|
30089
30151
|
// #574: OAuth cloud connectors (Dropbox/Drive/OneDrive/Box). Tokens stay server-side.
|
|
30090
30152
|
cloudProviders() {
|
|
30091
30153
|
return this.req("GET", "/cloud/providers");
|
|
30092
30154
|
}
|
|
30093
30155
|
cloudAuthorize(provider, name) {
|
|
30094
|
-
return this.req("POST", `/cloud/${provider}/authorize`, { name });
|
|
30156
|
+
return this.req("POST", `/cloud/${encodeURIComponent(provider)}/authorize`, { name });
|
|
30095
30157
|
}
|
|
30096
30158
|
listCloudConnections() {
|
|
30097
30159
|
return this.req("GET", "/cloud/connections");
|
|
30098
30160
|
}
|
|
30099
30161
|
deleteCloudConnection(id) {
|
|
30100
|
-
return this.req("DELETE", `/cloud/connections/${id}`);
|
|
30162
|
+
return this.req("DELETE", `/cloud/connections/${encodeURIComponent(id)}`);
|
|
30101
30163
|
}
|
|
30102
30164
|
cloudList(connId, path = "/", page) {
|
|
30103
|
-
return this.req("GET", `/cloud/connections/${connId}/list?path=${encodeURIComponent(path)}${listPageQuery(page)}`);
|
|
30165
|
+
return this.req("GET", `/cloud/connections/${encodeURIComponent(connId)}/list?path=${encodeURIComponent(path)}${listPageQuery(page)}`);
|
|
30104
30166
|
}
|
|
30105
30167
|
// #608: list + pin the connection's browse root (Shared Drive / SharePoint site / Box folder).
|
|
30106
30168
|
cloudRoots(connId) {
|
|
30107
|
-
return this.req("GET", `/cloud/connections/${connId}/roots`);
|
|
30169
|
+
return this.req("GET", `/cloud/connections/${encodeURIComponent(connId)}/roots`);
|
|
30108
30170
|
}
|
|
30109
30171
|
setCloudRoot(connId, root) {
|
|
30110
|
-
return this.req("PUT", `/cloud/connections/${connId}/root`, { root });
|
|
30172
|
+
return this.req("PUT", `/cloud/connections/${encodeURIComponent(connId)}/root`, { root });
|
|
30111
30173
|
}
|
|
30112
30174
|
cloudMkdir(connId, path) {
|
|
30113
|
-
return this.req("POST", `/cloud/connections/${connId}/mkdir`, { path });
|
|
30175
|
+
return this.req("POST", `/cloud/connections/${encodeURIComponent(connId)}/mkdir`, { path });
|
|
30114
30176
|
}
|
|
30115
30177
|
cloudRename(connId, from, to) {
|
|
30116
|
-
return this.req("POST", `/cloud/connections/${connId}/rename`, { from, to });
|
|
30178
|
+
return this.req("POST", `/cloud/connections/${encodeURIComponent(connId)}/rename`, { from, to });
|
|
30117
30179
|
}
|
|
30118
30180
|
cloudDelete(connId, path, isDir) {
|
|
30119
|
-
return this.req("POST", `/cloud/connections/${connId}/delete`, { path, is_dir: isDir });
|
|
30181
|
+
return this.req("POST", `/cloud/connections/${encodeURIComponent(connId)}/delete`, { path, is_dir: isDir });
|
|
30120
30182
|
}
|
|
30121
30183
|
// #1627: `redact` is the same opt-in the /files and /ssh download helpers take, and the cloud route
|
|
30122
30184
|
// has honoured it since #1562 (server/src/modules/cloud.ts:461). It only ever ADDS masking — the
|
|
@@ -30124,7 +30186,7 @@ var KutrionClient = class {
|
|
|
30124
30186
|
// a client whose users cannot ask for less, not one that is safer.
|
|
30125
30187
|
async cloudDownload(connId, path, opts2) {
|
|
30126
30188
|
const tok = this.getToken();
|
|
30127
|
-
const res = await this.f(`${this.baseUrl}/cloud/connections/${connId}/download?path=${encodeURIComponent(path)}${opts2?.redact ? "&redact=1" : ""}`, { headers: this.rawHeaders(tok) });
|
|
30189
|
+
const res = await this.f(`${this.baseUrl}/cloud/connections/${encodeURIComponent(connId)}/download?path=${encodeURIComponent(path)}${opts2?.redact ? "&redact=1" : ""}`, { headers: this.rawHeaders(tok) });
|
|
30128
30190
|
if (!res.ok) {
|
|
30129
30191
|
const errBody = await res.json().catch(() => void 0);
|
|
30130
30192
|
throw new ApiError(res.status, apiErrorMessage(res.status, errBody), errBody);
|
|
@@ -30134,7 +30196,7 @@ var KutrionClient = class {
|
|
|
30134
30196
|
// #2768: one request, so `signal` cancels it but there is no per-piece progress to report.
|
|
30135
30197
|
async cloudUpload(connId, path, data, opts2) {
|
|
30136
30198
|
const tok = this.getToken();
|
|
30137
|
-
const res = await this.fUpload(`${this.baseUrl}/cloud/connections/${connId}/upload?path=${encodeURIComponent(path)}${opts2?.ifAbsent ? "&if_absent=1" : ""}`, {
|
|
30199
|
+
const res = await this.fUpload(`${this.baseUrl}/cloud/connections/${encodeURIComponent(connId)}/upload?path=${encodeURIComponent(path)}${opts2?.ifAbsent ? "&if_absent=1" : ""}`, {
|
|
30138
30200
|
method: "POST",
|
|
30139
30201
|
headers: this.rawHeaders(tok, { "Content-Type": "application/octet-stream" }),
|
|
30140
30202
|
body: data,
|
|
@@ -30155,27 +30217,27 @@ var KutrionClient = class {
|
|
|
30155
30217
|
// (cloud.ts:453) — downloadStream only ever sends the open-ended form, so that refusal is
|
|
30156
30218
|
// unreachable from here.
|
|
30157
30219
|
cloudDownloadStream(connId, path, opts2) {
|
|
30158
|
-
return this.downloadStream(`${this.baseUrl}/cloud/connections/${connId}/download?path=${encodeURIComponent(path)}${opts2?.redact ? "&redact=1" : ""}`, opts2);
|
|
30220
|
+
return this.downloadStream(`${this.baseUrl}/cloud/connections/${encodeURIComponent(connId)}/download?path=${encodeURIComponent(path)}${opts2?.redact ? "&redact=1" : ""}`, opts2);
|
|
30159
30221
|
}
|
|
30160
30222
|
// POST /cloud/connections/:id/upload (cloud.ts:537) answers `{ ok }` plus the staging keys and NOT
|
|
30161
30223
|
// `size` — it returns `uploadProgress()` alone, where the file and sftp routes add `path`/`size`
|
|
30162
30224
|
// (files.ts:1428, ssh.ts:1070). Typed to what this route actually sends: claiming `size: number`
|
|
30163
30225
|
// here would hand every caller a number that is undefined at runtime.
|
|
30164
30226
|
cloudUploadStream(connId, path, body, opts2) {
|
|
30165
|
-
return this.uploadStream(`${this.baseUrl}/cloud/connections/${connId}/upload?path=${encodeURIComponent(path)}`, body, opts2);
|
|
30227
|
+
return this.uploadStream(`${this.baseUrl}/cloud/connections/${encodeURIComponent(connId)}/upload?path=${encodeURIComponent(path)}`, body, opts2);
|
|
30166
30228
|
}
|
|
30167
30229
|
fileMkdir(connId, path) {
|
|
30168
|
-
return this.req("POST", `/files/connections/${connId}/mkdir`, { path });
|
|
30230
|
+
return this.req("POST", `/files/connections/${encodeURIComponent(connId)}/mkdir`, { path });
|
|
30169
30231
|
}
|
|
30170
30232
|
fileRename(connId, from, to) {
|
|
30171
|
-
return this.req("POST", `/files/connections/${connId}/rename`, { from, to });
|
|
30233
|
+
return this.req("POST", `/files/connections/${encodeURIComponent(connId)}/rename`, { from, to });
|
|
30172
30234
|
}
|
|
30173
30235
|
fileDelete(connId, path, isDir) {
|
|
30174
|
-
return this.req("POST", `/files/connections/${connId}/delete`, { path, is_dir: isDir });
|
|
30236
|
+
return this.req("POST", `/files/connections/${encodeURIComponent(connId)}/delete`, { path, is_dir: isDir });
|
|
30175
30237
|
}
|
|
30176
30238
|
async fileDownload(connId, path, opts2) {
|
|
30177
30239
|
const tok = this.getToken();
|
|
30178
|
-
const res = await this.f(`${this.baseUrl}/files/connections/${connId}/download?path=${encodeURIComponent(path)}${opts2?.redact ? "&redact=1" : ""}`, {
|
|
30240
|
+
const res = await this.f(`${this.baseUrl}/files/connections/${encodeURIComponent(connId)}/download?path=${encodeURIComponent(path)}${opts2?.redact ? "&redact=1" : ""}`, {
|
|
30179
30241
|
headers: this.rawHeaders(tok)
|
|
30180
30242
|
});
|
|
30181
30243
|
if (!res.ok) {
|
|
@@ -30186,13 +30248,13 @@ var KutrionClient = class {
|
|
|
30186
30248
|
}
|
|
30187
30249
|
// #1542: streaming + resumable counterparts of fileDownload/fileUpload. See downloadStream above.
|
|
30188
30250
|
fileDownloadStream(connId, path, opts2) {
|
|
30189
|
-
return this.downloadStream(`${this.baseUrl}/files/connections/${connId}/download?path=${encodeURIComponent(path)}${opts2?.redact ? "&redact=1" : ""}`, opts2);
|
|
30251
|
+
return this.downloadStream(`${this.baseUrl}/files/connections/${encodeURIComponent(connId)}/download?path=${encodeURIComponent(path)}${opts2?.redact ? "&redact=1" : ""}`, opts2);
|
|
30190
30252
|
}
|
|
30191
30253
|
fileUploadStream(connId, path, body, opts2) {
|
|
30192
|
-
return this.uploadStream(`${this.baseUrl}/files/connections/${connId}/upload?path=${encodeURIComponent(path)}`, body, opts2);
|
|
30254
|
+
return this.uploadStream(`${this.baseUrl}/files/connections/${encodeURIComponent(connId)}/upload?path=${encodeURIComponent(path)}`, body, opts2);
|
|
30193
30255
|
}
|
|
30194
30256
|
fileUploadResumable(connId, path, source, opts2) {
|
|
30195
|
-
return this.uploadResumable(`/files/connections/${connId}/upload`, path, source, opts2);
|
|
30257
|
+
return this.uploadResumable(`/files/connections/${encodeURIComponent(connId)}/upload`, path, source, opts2);
|
|
30196
30258
|
}
|
|
30197
30259
|
// #325: download a selection of files/folders as one zip, built server-side (works for every protocol).
|
|
30198
30260
|
async fileArchive(input) {
|
|
@@ -30221,7 +30283,7 @@ var KutrionClient = class {
|
|
|
30221
30283
|
return this.req("GET", "/files/shares");
|
|
30222
30284
|
}
|
|
30223
30285
|
revokeFileShare(id) {
|
|
30224
|
-
return this.req("DELETE", `/files/shares/${id}`);
|
|
30286
|
+
return this.req("DELETE", `/files/shares/${encodeURIComponent(id)}`);
|
|
30225
30287
|
}
|
|
30226
30288
|
// #1636: the anonymous side of a Vault Drop — the recipient is outside the org and has no account.
|
|
30227
30289
|
//
|
|
@@ -30263,12 +30325,12 @@ var KutrionClient = class {
|
|
|
30263
30325
|
return this.req("GET", "/files/jobs");
|
|
30264
30326
|
}
|
|
30265
30327
|
getTransferJob(id) {
|
|
30266
|
-
return this.req("GET", `/files/jobs/${id}`);
|
|
30328
|
+
return this.req("GET", `/files/jobs/${encodeURIComponent(id)}`);
|
|
30267
30329
|
}
|
|
30268
30330
|
// #583: reprioritise or cancel a job that is still QUEUED. A running or finished job answers 409
|
|
30269
30331
|
// not_queued — the runner has already leased it and there is nothing left to reorder.
|
|
30270
30332
|
patchTransferJob(id, input) {
|
|
30271
|
-
return this.req("PATCH", `/files/jobs/${id}`, input);
|
|
30333
|
+
return this.req("PATCH", `/files/jobs/${encodeURIComponent(id)}`, input);
|
|
30272
30334
|
}
|
|
30273
30335
|
// #563: recurring transfer schedules (interval or daily UTC time).
|
|
30274
30336
|
createSchedule(input) {
|
|
@@ -30278,10 +30340,10 @@ var KutrionClient = class {
|
|
|
30278
30340
|
return this.req("GET", "/files/schedules");
|
|
30279
30341
|
}
|
|
30280
30342
|
setScheduleEnabled(id, enabled) {
|
|
30281
|
-
return this.req("PATCH", `/files/schedules/${id}`, { enabled });
|
|
30343
|
+
return this.req("PATCH", `/files/schedules/${encodeURIComponent(id)}`, { enabled });
|
|
30282
30344
|
}
|
|
30283
30345
|
deleteSchedule(id) {
|
|
30284
|
-
return this.req("DELETE", `/files/schedules/${id}`);
|
|
30346
|
+
return this.req("DELETE", `/files/schedules/${encodeURIComponent(id)}`);
|
|
30285
30347
|
}
|
|
30286
30348
|
// #614: folder watchers — poll a source directory and fire a transfer job on new/changed, size-stable
|
|
30287
30349
|
// files matching the filters. The transfer runs as a durable job.
|
|
@@ -30292,10 +30354,10 @@ var KutrionClient = class {
|
|
|
30292
30354
|
return this.req("GET", "/files/folder-watchers");
|
|
30293
30355
|
}
|
|
30294
30356
|
setFolderWatcherEnabled(id, enabled) {
|
|
30295
|
-
return this.req("PATCH", `/files/folder-watchers/${id}`, { enabled });
|
|
30357
|
+
return this.req("PATCH", `/files/folder-watchers/${encodeURIComponent(id)}`, { enabled });
|
|
30296
30358
|
}
|
|
30297
30359
|
deleteFolderWatcher(id) {
|
|
30298
|
-
return this.req("DELETE", `/files/folder-watchers/${id}`);
|
|
30360
|
+
return this.req("DELETE", `/files/folder-watchers/${encodeURIComponent(id)}`);
|
|
30299
30361
|
}
|
|
30300
30362
|
// #627: workflow templates. A saved edit is a NEW VERSION, not a PATCH — 0150's trigger refuses an
|
|
30301
30363
|
// UPDATE, because a run names the version it used and reproducibility depends on a stored revision
|
|
@@ -30304,19 +30366,19 @@ var KutrionClient = class {
|
|
|
30304
30366
|
return this.req("GET", "/files/workflow-templates");
|
|
30305
30367
|
}
|
|
30306
30368
|
getWorkflowTemplate(id) {
|
|
30307
|
-
return this.req("GET", `/files/workflow-templates/${id}`);
|
|
30369
|
+
return this.req("GET", `/files/workflow-templates/${encodeURIComponent(id)}`);
|
|
30308
30370
|
}
|
|
30309
30371
|
createWorkflowTemplate(input) {
|
|
30310
30372
|
return this.req("POST", "/files/workflow-templates", input);
|
|
30311
30373
|
}
|
|
30312
30374
|
addWorkflowTemplateVersion(id, input) {
|
|
30313
|
-
return this.req("POST", `/files/workflow-templates/${id}/versions`, input);
|
|
30375
|
+
return this.req("POST", `/files/workflow-templates/${encodeURIComponent(id)}/versions`, input);
|
|
30314
30376
|
}
|
|
30315
30377
|
deleteWorkflowTemplate(id) {
|
|
30316
|
-
return this.req("DELETE", `/files/workflow-templates/${id}`);
|
|
30378
|
+
return this.req("DELETE", `/files/workflow-templates/${encodeURIComponent(id)}`);
|
|
30317
30379
|
}
|
|
30318
30380
|
instantiateWorkflowTemplate(id, input) {
|
|
30319
|
-
return this.req("POST", `/files/workflow-templates/${id}/instantiate`, input);
|
|
30381
|
+
return this.req("POST", `/files/workflow-templates/${encodeURIComponent(id)}/instantiate`, input);
|
|
30320
30382
|
}
|
|
30321
30383
|
// #559: directory sync / mirror. dry_run returns the plan without writing; omit it to apply.
|
|
30322
30384
|
// #585: `two_way` and `mirror` are mutually exclusive and the server refuses the pair outright
|
|
@@ -30366,13 +30428,13 @@ var KutrionClient = class {
|
|
|
30366
30428
|
}
|
|
30367
30429
|
// #557: SFTP POSIX metadata (chmod / set mtime / create symlink).
|
|
30368
30430
|
sftpChmod(connId, path, mode) {
|
|
30369
|
-
return this.req("POST", `/ssh/connections/${connId}/sftp/chmod`, { path, mode });
|
|
30431
|
+
return this.req("POST", `/ssh/connections/${encodeURIComponent(connId)}/sftp/chmod`, { path, mode });
|
|
30370
30432
|
}
|
|
30371
30433
|
sftpChtime(connId, path, mtime) {
|
|
30372
|
-
return this.req("POST", `/ssh/connections/${connId}/sftp/chtime`, { path, mtime });
|
|
30434
|
+
return this.req("POST", `/ssh/connections/${encodeURIComponent(connId)}/sftp/chtime`, { path, mtime });
|
|
30373
30435
|
}
|
|
30374
30436
|
sftpSymlink(connId, target, link) {
|
|
30375
|
-
return this.req("POST", `/ssh/connections/${connId}/sftp/symlink`, { target, link });
|
|
30437
|
+
return this.req("POST", `/ssh/connections/${encodeURIComponent(connId)}/sftp/symlink`, { target, link });
|
|
30376
30438
|
}
|
|
30377
30439
|
// #2556: resumable, as sftpUpload.
|
|
30378
30440
|
async fileUpload(connId, path, data, opts2) {
|
|
@@ -30391,7 +30453,7 @@ var KutrionClient = class {
|
|
|
30391
30453
|
return this.req("GET", "/ssh/recordings");
|
|
30392
30454
|
}
|
|
30393
30455
|
getRecording(id) {
|
|
30394
|
-
return this.req("GET", `/ssh/recordings/${id}`);
|
|
30456
|
+
return this.req("GET", `/ssh/recordings/${encodeURIComponent(id)}`);
|
|
30395
30457
|
}
|
|
30396
30458
|
/** #2631: what was stored for one of the caller's recorded shell sessions, once it has ended. */
|
|
30397
30459
|
shellSessionReceipt(sessionId) {
|
|
@@ -30406,11 +30468,11 @@ var KutrionClient = class {
|
|
|
30406
30468
|
return this.req("DELETE", `/ssh/shell-sessions/${encodeURIComponent(sessionId)}`);
|
|
30407
30469
|
}
|
|
30408
30470
|
deleteRecording(id) {
|
|
30409
|
-
return this.req("DELETE", `/ssh/recordings/${id}`);
|
|
30471
|
+
return this.req("DELETE", `/ssh/recordings/${encodeURIComponent(id)}`);
|
|
30410
30472
|
}
|
|
30411
30473
|
// VT-143: generate (or fetch cached) an AI session summary for a recording. regenerate forces recompute.
|
|
30412
30474
|
summarizeRecording(id, regenerate = false) {
|
|
30413
|
-
return this.req("POST", `/ssh/recordings/${id}/summary${regenerate ? "?regenerate=1" : ""}`);
|
|
30475
|
+
return this.req("POST", `/ssh/recordings/${encodeURIComponent(id)}/summary${regenerate ? "?regenerate=1" : ""}`);
|
|
30414
30476
|
}
|
|
30415
30477
|
// --- #2628: org recording review and retention, org owners and admins only ---
|
|
30416
30478
|
getRecordingPolicy() {
|
|
@@ -30477,10 +30539,10 @@ var KutrionClient = class {
|
|
|
30477
30539
|
return this.req("GET", `/exposure${s ? `?${s}` : ""}`);
|
|
30478
30540
|
}
|
|
30479
30541
|
resolveExposure(id, resolution) {
|
|
30480
|
-
return this.req("POST", `/exposure/${id}/resolve`, { resolution });
|
|
30542
|
+
return this.req("POST", `/exposure/${encodeURIComponent(id)}/resolve`, { resolution });
|
|
30481
30543
|
}
|
|
30482
30544
|
suppressExposure(id, patterns = []) {
|
|
30483
|
-
return this.req("POST", `/exposure/${id}/suppress`, { patterns });
|
|
30545
|
+
return this.req("POST", `/exposure/${encodeURIComponent(id)}/suppress`, { patterns });
|
|
30484
30546
|
}
|
|
30485
30547
|
// --- integrations hub: outbound alert + SIEM connectors (VT-310 / VT-485) ---
|
|
30486
30548
|
integrationCatalog() {
|
|
@@ -30493,16 +30555,16 @@ var KutrionClient = class {
|
|
|
30493
30555
|
return this.req("POST", "/integrations", input);
|
|
30494
30556
|
}
|
|
30495
30557
|
updateIntegration(id, input) {
|
|
30496
|
-
return this.req("PATCH", `/integrations/${id}`, input);
|
|
30558
|
+
return this.req("PATCH", `/integrations/${encodeURIComponent(id)}`, input);
|
|
30497
30559
|
}
|
|
30498
30560
|
deleteIntegration(id) {
|
|
30499
|
-
return this.req("DELETE", `/integrations/${id}`);
|
|
30561
|
+
return this.req("DELETE", `/integrations/${encodeURIComponent(id)}`);
|
|
30500
30562
|
}
|
|
30501
30563
|
testIntegration(id) {
|
|
30502
|
-
return this.req("POST", `/integrations/${id}/test`);
|
|
30564
|
+
return this.req("POST", `/integrations/${encodeURIComponent(id)}/test`);
|
|
30503
30565
|
}
|
|
30504
30566
|
listIntegrationDeliveries(id, limit = 50) {
|
|
30505
|
-
return this.req("GET", `/integrations/${id}/deliveries?limit=${limit}`);
|
|
30567
|
+
return this.req("GET", `/integrations/${encodeURIComponent(id)}/deliveries?limit=${limit}`);
|
|
30506
30568
|
}
|
|
30507
30569
|
// --- HashiCorp Vault secret sync (VT-306): ENTERPRISE-gated (secret_sync). Auth secret is write-only. ---
|
|
30508
30570
|
listHcvConnections() {
|
|
@@ -30512,28 +30574,28 @@ var KutrionClient = class {
|
|
|
30512
30574
|
return this.req("POST", "/hashicorp/connections", input);
|
|
30513
30575
|
}
|
|
30514
30576
|
updateHcvConnection(id, input) {
|
|
30515
|
-
return this.req("PATCH", `/hashicorp/connections/${id}`, input);
|
|
30577
|
+
return this.req("PATCH", `/hashicorp/connections/${encodeURIComponent(id)}`, input);
|
|
30516
30578
|
}
|
|
30517
30579
|
deleteHcvConnection(id) {
|
|
30518
|
-
return this.req("DELETE", `/hashicorp/connections/${id}`);
|
|
30580
|
+
return this.req("DELETE", `/hashicorp/connections/${encodeURIComponent(id)}`);
|
|
30519
30581
|
}
|
|
30520
30582
|
testHcvConnection(id) {
|
|
30521
|
-
return this.req("POST", `/hashicorp/connections/${id}/test`);
|
|
30583
|
+
return this.req("POST", `/hashicorp/connections/${encodeURIComponent(id)}/test`);
|
|
30522
30584
|
}
|
|
30523
30585
|
listHcvMappings(connId) {
|
|
30524
|
-
return this.req("GET", `/hashicorp/connections/${connId}/mappings`);
|
|
30586
|
+
return this.req("GET", `/hashicorp/connections/${encodeURIComponent(connId)}/mappings`);
|
|
30525
30587
|
}
|
|
30526
30588
|
createHcvMapping(connId, input) {
|
|
30527
|
-
return this.req("POST", `/hashicorp/connections/${connId}/mappings`, input);
|
|
30589
|
+
return this.req("POST", `/hashicorp/connections/${encodeURIComponent(connId)}/mappings`, input);
|
|
30528
30590
|
}
|
|
30529
30591
|
deleteHcvMapping(mappingId) {
|
|
30530
|
-
return this.req("DELETE", `/hashicorp/mappings/${mappingId}`);
|
|
30592
|
+
return this.req("DELETE", `/hashicorp/mappings/${encodeURIComponent(mappingId)}`);
|
|
30531
30593
|
}
|
|
30532
30594
|
syncHcvMapping(mappingId, resolve2) {
|
|
30533
|
-
return this.req("POST", `/hashicorp/mappings/${mappingId}/sync${resolve2 ? `?resolve=${resolve2}` : ""}`);
|
|
30595
|
+
return this.req("POST", `/hashicorp/mappings/${encodeURIComponent(mappingId)}/sync${resolve2 ? `?resolve=${resolve2}` : ""}`);
|
|
30534
30596
|
}
|
|
30535
30597
|
syncHcvConnection(connId, resolve2) {
|
|
30536
|
-
return this.req("POST", `/hashicorp/connections/${connId}/sync${resolve2 ? `?resolve=${resolve2}` : ""}`);
|
|
30598
|
+
return this.req("POST", `/hashicorp/connections/${encodeURIComponent(connId)}/sync${resolve2 ? `?resolve=${resolve2}` : ""}`);
|
|
30537
30599
|
}
|
|
30538
30600
|
// --- rdp broker (VT-490) ---
|
|
30539
30601
|
listRdpConnections() {
|
|
@@ -30543,28 +30605,28 @@ var KutrionClient = class {
|
|
|
30543
30605
|
return this.req("POST", "/rdp/connections", input);
|
|
30544
30606
|
}
|
|
30545
30607
|
updateRdpConnection(id, input) {
|
|
30546
|
-
return this.req("PATCH", `/rdp/connections/${id}`, input);
|
|
30608
|
+
return this.req("PATCH", `/rdp/connections/${encodeURIComponent(id)}`, input);
|
|
30547
30609
|
}
|
|
30548
30610
|
deleteRdpConnection(id) {
|
|
30549
|
-
return this.req("DELETE", `/rdp/connections/${id}`);
|
|
30611
|
+
return this.req("DELETE", `/rdp/connections/${encodeURIComponent(id)}`);
|
|
30550
30612
|
}
|
|
30551
30613
|
testRdpConnection(id) {
|
|
30552
|
-
return this.req("POST", `/rdp/connections/${id}/test`);
|
|
30614
|
+
return this.req("POST", `/rdp/connections/${encodeURIComponent(id)}/test`);
|
|
30553
30615
|
}
|
|
30554
30616
|
startRdpSession(id) {
|
|
30555
|
-
return this.req("POST", `/rdp/connections/${id}/session`);
|
|
30617
|
+
return this.req("POST", `/rdp/connections/${encodeURIComponent(id)}/session`);
|
|
30556
30618
|
}
|
|
30557
30619
|
listRdpSessions() {
|
|
30558
30620
|
return this.req("GET", "/rdp/sessions");
|
|
30559
30621
|
}
|
|
30560
30622
|
endRdpSession(sessionId) {
|
|
30561
|
-
return this.req("DELETE", `/rdp/sessions/${sessionId}`);
|
|
30623
|
+
return this.req("DELETE", `/rdp/sessions/${encodeURIComponent(sessionId)}`);
|
|
30562
30624
|
}
|
|
30563
30625
|
// Stream a brokered session's recording (broker proxies it from the gateway). Binary, so it bypasses
|
|
30564
30626
|
// the JSON req() helper — same shape as sftpDownload/fileDownload.
|
|
30565
30627
|
async getRdpRecording(sessionId) {
|
|
30566
30628
|
const tok = this.getToken();
|
|
30567
|
-
const res = await this.f(`${this.baseUrl}/rdp/sessions/${sessionId}/recording`, {
|
|
30629
|
+
const res = await this.f(`${this.baseUrl}/rdp/sessions/${encodeURIComponent(sessionId)}/recording`, {
|
|
30568
30630
|
headers: this.rawHeaders(tok)
|
|
30569
30631
|
});
|
|
30570
30632
|
if (!res.ok) throw new ApiError(res.status, apiErrorMessage(res.status));
|
|
@@ -30607,7 +30669,7 @@ var KutrionClient = class {
|
|
|
30607
30669
|
}
|
|
30608
30670
|
// Files AI (#325): build a filename regex from natural language, summarize a listing, or propose a
|
|
30609
30671
|
// rename plan. All run LAN-first; only file NAMES/metadata are sent (never file content), through the
|
|
30610
|
-
// redaction gate. The returned regex is server-validated to compile and
|
|
30672
|
+
// redaction gate. The returned regex is server-validated to compile and to lack the most obvious ReDoS shapes only (isSafeRegex is a heuristic: (a|a)+ passes); clients must run it under a time budget.
|
|
30611
30673
|
aiFilePattern(input) {
|
|
30612
30674
|
return this.req("POST", "/ai/files/pattern", input);
|
|
30613
30675
|
}
|
|
@@ -30639,16 +30701,16 @@ var KutrionClient = class {
|
|
|
30639
30701
|
return this.req("GET", "/jit/requests");
|
|
30640
30702
|
}
|
|
30641
30703
|
getJitRequest(id) {
|
|
30642
|
-
return this.req("GET", `/jit/requests/${id}`);
|
|
30704
|
+
return this.req("GET", `/jit/requests/${encodeURIComponent(id)}`);
|
|
30643
30705
|
}
|
|
30644
30706
|
approveJitRequest(id, note) {
|
|
30645
|
-
return this.req("POST", `/jit/requests/${id}/approve`, { note });
|
|
30707
|
+
return this.req("POST", `/jit/requests/${encodeURIComponent(id)}/approve`, { note });
|
|
30646
30708
|
}
|
|
30647
30709
|
denyJitRequest(id, note) {
|
|
30648
|
-
return this.req("POST", `/jit/requests/${id}/deny`, { note });
|
|
30710
|
+
return this.req("POST", `/jit/requests/${encodeURIComponent(id)}/deny`, { note });
|
|
30649
30711
|
}
|
|
30650
30712
|
revokeJitRequest(id) {
|
|
30651
|
-
return this.req("POST", `/jit/requests/${id}/revoke`);
|
|
30713
|
+
return this.req("POST", `/jit/requests/${encodeURIComponent(id)}/revoke`);
|
|
30652
30714
|
}
|
|
30653
30715
|
// --- rotation copilot (VT-145) ---
|
|
30654
30716
|
rotationServices() {
|
|
@@ -30672,13 +30734,13 @@ var KutrionClient = class {
|
|
|
30672
30734
|
return this.req("POST", "/fleet/plan", input);
|
|
30673
30735
|
}
|
|
30674
30736
|
executeFleetRun(id) {
|
|
30675
|
-
return this.req("POST", `/fleet/runs/${id}/execute`, { confirm: true });
|
|
30737
|
+
return this.req("POST", `/fleet/runs/${encodeURIComponent(id)}/execute`, { confirm: true });
|
|
30676
30738
|
}
|
|
30677
30739
|
fleetRuns() {
|
|
30678
30740
|
return this.req("GET", "/fleet/runs");
|
|
30679
30741
|
}
|
|
30680
30742
|
fleetRun(id) {
|
|
30681
|
-
return this.req("GET", `/fleet/runs/${id}`);
|
|
30743
|
+
return this.req("GET", `/fleet/runs/${encodeURIComponent(id)}`);
|
|
30682
30744
|
}
|
|
30683
30745
|
// --- anomaly baseline (VT-204) ---
|
|
30684
30746
|
refreshBaseline() {
|
|
@@ -31222,6 +31284,9 @@ var SSH_REFUSAL_CLASSIFICATION = {
|
|
|
31222
31284
|
// Something answered on that port and it was not an SSH server; what is listening has to change
|
|
31223
31285
|
// first, even though the user is who can change it.
|
|
31224
31286
|
protocol_error: { family: "failed", retry: "pointless" },
|
|
31287
|
+
// #3419: the login was accepted and the host refused a PTY or a shell. The account is set up that
|
|
31288
|
+
// way on the host, so every attempt earns the same answer.
|
|
31289
|
+
shell_refused: { family: "failed", retry: "pointless" },
|
|
31225
31290
|
// The rest are genuine retry candidates: a transport fact can be different a second later.
|
|
31226
31291
|
connection_refused: { family: "failed", retry: "possible" },
|
|
31227
31292
|
host_not_found: { family: "failed", retry: "possible" },
|
|
@@ -32853,14 +32918,14 @@ var KNOWN_FLAGS = /* @__PURE__ */ new Set([
|
|
|
32853
32918
|
function unknownFlags(flags) {
|
|
32854
32919
|
return Object.keys(flags).filter((k) => !KNOWN_FLAGS.has(k));
|
|
32855
32920
|
}
|
|
32856
|
-
function parseArgs(
|
|
32921
|
+
function parseArgs(argv2) {
|
|
32857
32922
|
const flags = {};
|
|
32858
32923
|
let at = 0;
|
|
32859
|
-
while (at <
|
|
32860
|
-
const key =
|
|
32924
|
+
while (at < argv2.length && argv2[at].startsWith("--")) {
|
|
32925
|
+
const key = argv2[at].slice(2);
|
|
32861
32926
|
const kind = GLOBAL_FLAGS[key];
|
|
32862
32927
|
if (!kind) break;
|
|
32863
|
-
const next =
|
|
32928
|
+
const next = argv2[at + 1];
|
|
32864
32929
|
if (kind === "value" && next !== void 0 && !next.startsWith("--")) {
|
|
32865
32930
|
flags[key] = next;
|
|
32866
32931
|
at += 2;
|
|
@@ -32869,8 +32934,8 @@ function parseArgs(argv) {
|
|
|
32869
32934
|
at += 1;
|
|
32870
32935
|
}
|
|
32871
32936
|
}
|
|
32872
|
-
const cmd =
|
|
32873
|
-
const rest =
|
|
32937
|
+
const cmd = argv2[at];
|
|
32938
|
+
const rest = argv2.slice(at + 1);
|
|
32874
32939
|
const positionals = [];
|
|
32875
32940
|
for (let i = 0; i < rest.length; i++) {
|
|
32876
32941
|
const a = rest[i];
|
|
@@ -34029,15 +34094,18 @@ async function borrowAndEnrol(deps) {
|
|
|
34029
34094
|
if (enrolment === "device_clients_disabled" || enrolment === "satellite_update_required" || enrolment === "hub_not_bound") return { ok: false, reason: enrolment };
|
|
34030
34095
|
return enrolment ? { ok: true, enrolment, hubPublicKey: borrowed.hubPublicKey } : { ok: false, reason: "enrol_refused" };
|
|
34031
34096
|
}
|
|
34032
|
-
function createBorrowSession(store, client, resolveDeps) {
|
|
34097
|
+
function createBorrowSession(store, client, resolveDeps, otherSigninHeld = () => false) {
|
|
34033
34098
|
let pending = null;
|
|
34034
34099
|
let held = null;
|
|
34035
34100
|
let pin = null;
|
|
34036
34101
|
let running = false;
|
|
34102
|
+
let autoTried = false;
|
|
34037
34103
|
return {
|
|
34038
|
-
begin() {
|
|
34039
|
-
if (
|
|
34040
|
-
if (
|
|
34104
|
+
begin(auto = false) {
|
|
34105
|
+
if (auto && autoTried) return { ok: false, reason: "auto_done" };
|
|
34106
|
+
if (auto) autoTried = true;
|
|
34107
|
+
if (running) return { ok: false, reason: "busy" };
|
|
34108
|
+
if (held || otherSigninHeld()) return { ok: false, reason: "signin_in_progress" };
|
|
34041
34109
|
const p = prepareBorrow(store, client);
|
|
34042
34110
|
pending = p.ok ? p : null;
|
|
34043
34111
|
return p.ok ? { ok: true, fingerprint: p.fingerprint } : { ok: false, reason: p.reason };
|
|
@@ -34064,6 +34132,9 @@ function createBorrowSession(store, client, resolveDeps) {
|
|
|
34064
34132
|
pin = deps.pin;
|
|
34065
34133
|
return { ok: true, deviceId: out.deviceId, firstContact: out.held !== null };
|
|
34066
34134
|
},
|
|
34135
|
+
suppressAuto() {
|
|
34136
|
+
autoTried = true;
|
|
34137
|
+
},
|
|
34067
34138
|
cancel() {
|
|
34068
34139
|
pending = null;
|
|
34069
34140
|
},
|
|
@@ -34241,14 +34312,29 @@ function joinedAccount(user, lastAccount) {
|
|
|
34241
34312
|
accountChanged: lastAccount !== null && lastAccount !== user.id
|
|
34242
34313
|
};
|
|
34243
34314
|
}
|
|
34315
|
+
var HELD_TTL_MS = 3e5;
|
|
34244
34316
|
function createBorrowConfirm(deps) {
|
|
34317
|
+
const ttl = deps.ttlMs ?? HELD_TTL_MS;
|
|
34318
|
+
const now = deps.now ?? Date.now;
|
|
34245
34319
|
let account = null;
|
|
34246
34320
|
let heldId = null;
|
|
34247
34321
|
let heldName = null;
|
|
34322
|
+
let heldAt = 0;
|
|
34323
|
+
let timer = null;
|
|
34248
34324
|
const remember = (user) => {
|
|
34249
34325
|
if (deps.lastAccount.get() !== user.id) deps.lastAccount.set(user.id);
|
|
34250
34326
|
};
|
|
34327
|
+
const expired = () => account !== null && now() - heldAt >= ttl;
|
|
34328
|
+
const reap = () => {
|
|
34329
|
+
if (!expired()) return false;
|
|
34330
|
+
void reject().then(() => deps.onExpired?.());
|
|
34331
|
+
return true;
|
|
34332
|
+
};
|
|
34251
34333
|
const reject = async () => {
|
|
34334
|
+
if (timer) {
|
|
34335
|
+
clearTimeout(timer);
|
|
34336
|
+
timer = null;
|
|
34337
|
+
}
|
|
34252
34338
|
account = null;
|
|
34253
34339
|
heldId = null;
|
|
34254
34340
|
heldName = null;
|
|
@@ -34262,19 +34348,28 @@ function createBorrowConfirm(deps) {
|
|
|
34262
34348
|
account = user;
|
|
34263
34349
|
heldId = deviceId;
|
|
34264
34350
|
heldName = deviceName ?? null;
|
|
34351
|
+
heldAt = now();
|
|
34352
|
+
if (timer) clearTimeout(timer);
|
|
34353
|
+
timer = setTimeout(reap, ttl);
|
|
34354
|
+
timer.unref?.();
|
|
34265
34355
|
},
|
|
34266
|
-
pending: () => account ? { ...joinedAccount(account, deps.lastAccount.get()), deviceName: heldName } : null,
|
|
34356
|
+
pending: () => account && !reap() ? { ...joinedAccount(account, deps.lastAccount.get()), deviceName: heldName, deviceId: heldId } : null,
|
|
34267
34357
|
seen(user) {
|
|
34268
34358
|
if (!account && user) remember(user);
|
|
34269
34359
|
},
|
|
34270
|
-
async confirm() {
|
|
34360
|
+
async confirm(deviceId) {
|
|
34361
|
+
if (reap()) return false;
|
|
34271
34362
|
const user = account;
|
|
34272
34363
|
const id = heldId;
|
|
34273
|
-
if (!user || !id) return false;
|
|
34364
|
+
if (!user || !id || id !== deviceId) return false;
|
|
34274
34365
|
if (!deps.borrow.commit(id)) {
|
|
34275
34366
|
await reject();
|
|
34276
34367
|
return false;
|
|
34277
34368
|
}
|
|
34369
|
+
if (timer) {
|
|
34370
|
+
clearTimeout(timer);
|
|
34371
|
+
timer = null;
|
|
34372
|
+
}
|
|
34278
34373
|
account = null;
|
|
34279
34374
|
heldId = null;
|
|
34280
34375
|
remember(user);
|
|
@@ -34476,6 +34571,10 @@ function cliHubStores(base, kc, keychainAvailable) {
|
|
|
34476
34571
|
};
|
|
34477
34572
|
return { store, pin, latch, lastAccount, failedWrite: () => failed2 ? { deviceId, bearer } : null };
|
|
34478
34573
|
}
|
|
34574
|
+
var lastAccountId = (base, kc) => read(kc, slot.lastAccount(base));
|
|
34575
|
+
function recordLastAccountId(base, kc, user) {
|
|
34576
|
+
if (user && typeof user.id === "string" && user.id) write(kc, slot.lastAccount(base), user.id);
|
|
34577
|
+
}
|
|
34479
34578
|
function borrowedDeviceKey(base, kc) {
|
|
34480
34579
|
return readDevice(kc, base).key;
|
|
34481
34580
|
}
|
|
@@ -34576,6 +34675,7 @@ async function hubLogin(deps) {
|
|
|
34576
34675
|
if (!deps.keychainAvailable()) return { kind: "fallback", note: null, reason: "no_keychain" };
|
|
34577
34676
|
if (locationFault(deps.location)) return { kind: "fallback", note: null, reason: "hub_not_running" };
|
|
34578
34677
|
const { store, pin, latch, lastAccount, failedWrite } = cliHubStores(deps.base, deps.kc, deps.keychainAvailable);
|
|
34678
|
+
if (deps.pairedOnly && !pin.hubPublicKey) return { kind: "fallback", note: null, reason: "not_paired" };
|
|
34579
34679
|
const revoke = (deviceId, bearer) => endServerSession(deps.call, deviceId, bearer);
|
|
34580
34680
|
const session = createBorrowSession(store, "cli", () => ({
|
|
34581
34681
|
location: deps.location,
|
|
@@ -34627,11 +34727,36 @@ async function hubLogin(deps) {
|
|
|
34627
34727
|
const verdict = await presentJoined(confirm, deps.out, deps.ask);
|
|
34628
34728
|
if (verdict === "not_kept") return { kind: "fallback", note: HUB_COPY.notKept, reason: "not_kept" };
|
|
34629
34729
|
if (verdict === "not_you") return { kind: "fallback", note: HUB_COPY.notYou, reason: "not_you" };
|
|
34630
|
-
if (!await confirm.confirm()) return { kind: "fallback", note: HUB_COPY.notKept, reason: "not_kept" };
|
|
34730
|
+
if (!await confirm.confirm(done.deviceId)) return { kind: "fallback", note: HUB_COPY.notKept, reason: "not_kept" };
|
|
34631
34731
|
const lost = await unkept();
|
|
34632
34732
|
if (lost) return lost;
|
|
34633
34733
|
return { kind: "signed_in", user, firstContact: true };
|
|
34634
34734
|
}
|
|
34735
|
+
var canPromptYes = (stdin, stderr) => Boolean(stdin.isTTY && stderr.isTTY);
|
|
34736
|
+
var reborrowFromHub = (deps) => hubLogin({ ...deps, pairedOnly: true });
|
|
34737
|
+
var loginTriesHub = (flags, accountRoot) => flags.email === void 0 && flags["password-stdin"] !== true && accountRoot !== null;
|
|
34738
|
+
var HubAccountSwitchedError = class extends Error {
|
|
34739
|
+
constructor(email) {
|
|
34740
|
+
super(`Signed in through Kutrion Hub as ${email === null ? "an account it could not name" : printable(email)}, which is not the account this command started on. Run it again to use that account.`);
|
|
34741
|
+
this.name = "HubAccountSwitchedError";
|
|
34742
|
+
}
|
|
34743
|
+
};
|
|
34744
|
+
async function retryAfterHubReborrow(run2, deps) {
|
|
34745
|
+
try {
|
|
34746
|
+
return await run2();
|
|
34747
|
+
} catch (e) {
|
|
34748
|
+
if (!deps.refusedSession(e) || deps.wrote()) throw e;
|
|
34749
|
+
const before = deps.lastAccount();
|
|
34750
|
+
const hub = await deps.reborrow();
|
|
34751
|
+
if (hub.kind !== "signed_in") {
|
|
34752
|
+
if (hub.note) deps.out(hub.note);
|
|
34753
|
+
throw e;
|
|
34754
|
+
}
|
|
34755
|
+
if (!before || !hub.user || hub.user.id !== before) throw new HubAccountSwitchedError(hub.user ? hub.user.email : null);
|
|
34756
|
+
deps.out(`Signed in again with the Kutrion app as ${printable(hub.user.email)}; running the command again.`);
|
|
34757
|
+
}
|
|
34758
|
+
return run2();
|
|
34759
|
+
}
|
|
34635
34760
|
|
|
34636
34761
|
// src/hub/hub-seed.ts
|
|
34637
34762
|
init_define_KT_HUB_FILES();
|
|
@@ -35517,6 +35642,7 @@ async function confirmChainedHostKey(deps, connId, k, own) {
|
|
|
35517
35642
|
}
|
|
35518
35643
|
|
|
35519
35644
|
// src/refusal.ts
|
|
35645
|
+
var refreshWhy = (code, noun = "session") => refreshRefusalCopy(code, noun)?.why ?? code;
|
|
35520
35646
|
var REFUSALS = {
|
|
35521
35647
|
// #1538 authoring time. Safe to key here even though `jump_cycle` and `jump_too_deep` are also
|
|
35522
35648
|
// WebSocket codes: this table is read only by refusalLines() on the HTTP path, and wsRefusalLines()
|
|
@@ -35539,7 +35665,7 @@ var REFUSALS = {
|
|
|
35539
35665
|
action: "Run `kt conns` and pass an id from the first column to --jump."
|
|
35540
35666
|
},
|
|
35541
35667
|
// auth-guard.ts:109 (social-session gate) and :167 (adaptive), plus rotation.ts:159, jit.ts:226,
|
|
35542
|
-
// fleet.ts:190, agents.ts:146, modules/ssh.ts:
|
|
35668
|
+
// fleet.ts:190, agents.ts:146, modules/ssh.ts:730 and :1228. The remedy is mechanical and worth
|
|
35543
35669
|
// stating exactly, because it is not obvious that it can be satisfied anywhere but here: step-up
|
|
35544
35670
|
// freshness is a per-USER Redis proof with a ~5 minute TTL (server/src/lib/step-up.ts:16), not a
|
|
35545
35671
|
// claim baked into this CLI's token. Stepping up in the web app on the same account therefore
|
|
@@ -35568,6 +35694,49 @@ var REFUSALS = {
|
|
|
35568
35694
|
why: "The token stored for this server is no longer accepted, so every command will be refused until this machine signs in again.",
|
|
35569
35695
|
action: "Run `kt login` to sign in again."
|
|
35570
35696
|
},
|
|
35697
|
+
// #3444/#3445: POST /auth/refresh terminal verdicts (server/src/modules/devices.ts). Each ends the
|
|
35698
|
+
// stored session, like device_revoked, so the only way forward is a fresh `kt login`. The Hub pair
|
|
35699
|
+
// reuses the extension's sentences word for word.
|
|
35700
|
+
refresh_credentials_changed: {
|
|
35701
|
+
title: "Your password changed",
|
|
35702
|
+
why: refreshWhy("refresh_credentials_changed"),
|
|
35703
|
+
action: "Run `kt login` with your new password."
|
|
35704
|
+
},
|
|
35705
|
+
refresh_hub_signed_out: {
|
|
35706
|
+
title: "This session was signed out",
|
|
35707
|
+
why: refreshWhy("refresh_hub_signed_out"),
|
|
35708
|
+
action: "Run `kt login` to sign in again."
|
|
35709
|
+
},
|
|
35710
|
+
refresh_hub_revoked: {
|
|
35711
|
+
title: "This session was signed out",
|
|
35712
|
+
why: refreshWhy("refresh_hub_revoked"),
|
|
35713
|
+
action: "Run `kt login` to sign in again."
|
|
35714
|
+
},
|
|
35715
|
+
device_revoked: {
|
|
35716
|
+
title: "This device was signed out",
|
|
35717
|
+
why: refreshWhy("device_revoked", "device"),
|
|
35718
|
+
action: "Run `kt login` to sign in again and register it anew."
|
|
35719
|
+
},
|
|
35720
|
+
account_inactive: {
|
|
35721
|
+
title: "This account has ended",
|
|
35722
|
+
why: refreshWhy("account_inactive"),
|
|
35723
|
+
action: "Your administrator can restore the account. Signing in again will not work until they do."
|
|
35724
|
+
},
|
|
35725
|
+
account_expired: {
|
|
35726
|
+
title: "This account has expired",
|
|
35727
|
+
why: refreshWhy("account_expired"),
|
|
35728
|
+
action: "Your administrator can extend the account. Signing in again will not work until they do."
|
|
35729
|
+
},
|
|
35730
|
+
user_inactive: {
|
|
35731
|
+
title: "This account has ended",
|
|
35732
|
+
why: refreshWhy("user_inactive"),
|
|
35733
|
+
action: "Your administrator can restore the account. Signing in again will not work until they do."
|
|
35734
|
+
},
|
|
35735
|
+
org_mismatch: {
|
|
35736
|
+
title: "This session was signed out",
|
|
35737
|
+
why: refreshWhy("org_mismatch"),
|
|
35738
|
+
action: "Run `kt login` to sign in again with your current organization."
|
|
35739
|
+
},
|
|
35571
35740
|
// auth-guard.ts:164. The distinguishing fact is that this one CANNOT be stepped up out of — it is
|
|
35572
35741
|
// sent precisely when the account holds no second factor to step up with.
|
|
35573
35742
|
access_blocked: {
|
|
@@ -35739,7 +35908,7 @@ var REFUSALS = {
|
|
|
35739
35908
|
why: "Kutrion does not display, copy, export or share this credential's value. It is still used to authenticate connections and integrations.",
|
|
35740
35909
|
action: "Use the credential rather than reading it \u2014 `kt ssh <connId>` or `kt exec <connId> <command>` broker a session with it without the value passing through this terminal."
|
|
35741
35910
|
},
|
|
35742
|
-
// modules/ssh.ts:
|
|
35911
|
+
// modules/ssh.ts:473, modules/rdp.ts:151.
|
|
35743
35912
|
inject_only_requires_write: {
|
|
35744
35913
|
title: "Write access required for a No-reveal credential",
|
|
35745
35914
|
why: "Pointing a connection at a No-reveal credential needs write access to the vault holding it, because the person doing it decides where the credential gets presented.",
|
|
@@ -35936,11 +36105,15 @@ var STATUS_ACTION = {
|
|
|
35936
36105
|
422: "The server understood the request but refused its values. Check the values passed against `kt --help`.",
|
|
35937
36106
|
429: "Too many requests in a short period. Wait before retrying rather than looping."
|
|
35938
36107
|
};
|
|
36108
|
+
var HUB_SIGN_IN_ACTION = {
|
|
36109
|
+
status: "This machine's session was not accepted. Run `kt login`, it signs in through Kutrion Hub.",
|
|
36110
|
+
signed_out: "Run `kt login`, it signs in through Kutrion Hub."
|
|
36111
|
+
};
|
|
35939
36112
|
var SERVER_REFUSALS = /* @__PURE__ */ new Set(["payment_unconfirmed"]);
|
|
35940
36113
|
var sentenceKey = (t) => t.toLowerCase().replace(/[^a-z0-9]+/g, "");
|
|
35941
36114
|
var sameSentence = (a, b) => b !== void 0 && sentenceKey(a) === sentenceKey(b);
|
|
35942
36115
|
var SIGNIN_CODE_REFUSALS = /* @__PURE__ */ new Set(["email_code_required", "signin_code_cap"]);
|
|
35943
|
-
function refusalLines(e, indent = " ") {
|
|
36116
|
+
function refusalLines(e, indent = " ", opts2 = {}) {
|
|
35944
36117
|
if (!(e instanceof ApiError)) return null;
|
|
35945
36118
|
if (e.status < 400 || e.status >= 500 && !SERVER_REFUSALS.has(refusalCode(e))) return null;
|
|
35946
36119
|
const dest = parseDestinationRefusal(e.body);
|
|
@@ -35963,11 +36136,12 @@ function refusalLines(e, indent = " ") {
|
|
|
35963
36136
|
`${indent}kt conn set <id> --jump none`
|
|
35964
36137
|
];
|
|
35965
36138
|
}
|
|
35966
|
-
const
|
|
36139
|
+
const fromRefresh = e.refreshCode !== void 0 && Object.hasOwn(REFUSALS, e.refreshCode);
|
|
36140
|
+
const code = fromRefresh ? e.refreshCode : refusalCode(e);
|
|
35967
36141
|
const platformFlag = platformFlagRefusalText(code);
|
|
35968
36142
|
if (platformFlag) return [`Turned off on this deployment (${code}, HTTP ${e.status})`, "", platformFlag];
|
|
35969
36143
|
const copy = Object.hasOwn(REFUSALS, code) ? REFUSALS[code] : void 0;
|
|
35970
|
-
const server = SIGNIN_CODE_REFUSALS.has(code) ? null : serverText(e.body);
|
|
36144
|
+
const server = fromRefresh || SIGNIN_CODE_REFUSALS.has(code) ? null : serverText(e.body);
|
|
35971
36145
|
const context = contextLines(e.body, indent);
|
|
35972
36146
|
const head = copy ? `${copy.title} (${code}, HTTP ${e.status})` : `Refused by the server (${code}, HTTP ${e.status})`;
|
|
35973
36147
|
const lines = [head, ""];
|
|
@@ -35976,7 +36150,9 @@ function refusalLines(e, indent = " ") {
|
|
|
35976
36150
|
if (!copy && !server) lines.push("The server sent no explanation with this refusal.");
|
|
35977
36151
|
if (context.length) lines.push("", ...context);
|
|
35978
36152
|
lines.push("");
|
|
35979
|
-
|
|
36153
|
+
const hubAction = !opts2.hubSignIn || e.status !== 401 ? null : code === "session_signed_out" ? HUB_SIGN_IN_ACTION.signed_out : copy ? null : HUB_SIGN_IN_ACTION.status;
|
|
36154
|
+
if (hubAction) lines.push(hubAction);
|
|
36155
|
+
else if (copy?.action) lines.push(copy.action);
|
|
35980
36156
|
else if (copy) lines.push("Nothing at this terminal changes this answer.");
|
|
35981
36157
|
else lines.push(STATUS_ACTION[e.status] ?? `Quote the code and status above when reporting this; they are the whole of what the server sent.`);
|
|
35982
36158
|
return lines;
|
|
@@ -36233,6 +36409,18 @@ function refuseBodyRedirects(inner = fetch) {
|
|
|
36233
36409
|
}
|
|
36234
36410
|
};
|
|
36235
36411
|
}
|
|
36412
|
+
function trackWrites(inner) {
|
|
36413
|
+
let wrote = false;
|
|
36414
|
+
return {
|
|
36415
|
+
fetch: async (input, init) => {
|
|
36416
|
+
const res = await inner(input, init);
|
|
36417
|
+
const method = (init?.method ?? (input instanceof Request ? input.method : "GET")).toUpperCase();
|
|
36418
|
+
if (res.ok && method !== "GET") wrote = true;
|
|
36419
|
+
return res;
|
|
36420
|
+
},
|
|
36421
|
+
wrote: () => wrote
|
|
36422
|
+
};
|
|
36423
|
+
}
|
|
36236
36424
|
|
|
36237
36425
|
// src/mcp-install.ts
|
|
36238
36426
|
var appData = () => process.env.APPDATA ?? (0, import_path4.join)((0, import_os4.homedir)(), "AppData", "Roaming");
|
|
@@ -37415,11 +37603,11 @@ function parsePurposes(v) {
|
|
|
37415
37603
|
}
|
|
37416
37604
|
return out.length ? out : { error: `--purposes is required: one or more of ${ENROLL_PURPOSES.join(", ")}` };
|
|
37417
37605
|
}
|
|
37418
|
-
function parseConfirms(
|
|
37606
|
+
function parseConfirms(argv2) {
|
|
37419
37607
|
const out = /* @__PURE__ */ new Map();
|
|
37420
|
-
for (let i = 0; i <
|
|
37421
|
-
if (
|
|
37422
|
-
const v =
|
|
37608
|
+
for (let i = 0; i < argv2.length; i++) {
|
|
37609
|
+
if (argv2[i] !== "--confirm") continue;
|
|
37610
|
+
const v = argv2[i + 1];
|
|
37423
37611
|
if (v === void 0 || v.startsWith("--")) return { error: "--confirm needs <connId|host>=<fingerprint>" };
|
|
37424
37612
|
i++;
|
|
37425
37613
|
for (const pair of v.split(",").map((s) => s.trim()).filter(Boolean)) {
|
|
@@ -38155,10 +38343,11 @@ var ROLES = ["OWNER", "ADMIN", "EDITOR", "VIEWER"];
|
|
|
38155
38343
|
var TOKEN_SCOPES = ["scim", "events"];
|
|
38156
38344
|
var ORG_ROLES = ["owner", "admin", "member"];
|
|
38157
38345
|
var ROTATION_SERVICES = ["linux_password", "postgres_password", "mysql_password"];
|
|
38158
|
-
var VERSION = true ? "1.2.
|
|
38346
|
+
var VERSION = true ? "1.2.14" : "dev";
|
|
38159
38347
|
var HELP = `kt \u2014 Kutrion CLI
|
|
38160
38348
|
kt login [--email <e>] [--password-stdin] [--code <2fa>] prompts for the password, or reads it from stdin;
|
|
38161
|
-
|
|
38349
|
+
with no --email or --password-stdin, first asks the Kutrion desktop app
|
|
38350
|
+
(without a terminal, only one this CLI has already paired with),
|
|
38162
38351
|
then falls back to your email and password;
|
|
38163
38352
|
at the emailed-code prompt, a blank entry or r sends a new code
|
|
38164
38353
|
kt login --browser not available; exits with an error
|
|
@@ -38472,6 +38661,34 @@ function runForward(base, token, connId, spec, ctx = {}) {
|
|
|
38472
38661
|
console.log(`forwarding 127.0.0.1:${localPort} -> ${dstHost}:${dstPort} via ${connId} (Ctrl-C to stop)`);
|
|
38473
38662
|
});
|
|
38474
38663
|
}
|
|
38664
|
+
function hubCallFor(base) {
|
|
38665
|
+
return async (method, path, body, bearer) => {
|
|
38666
|
+
try {
|
|
38667
|
+
const res = await refuseBodyRedirects()(base + path, {
|
|
38668
|
+
method,
|
|
38669
|
+
headers: { Authorization: `Bearer ${bearer}`, Accept: "application/json", ...body === void 0 ? {} : { "Content-Type": "application/json" } },
|
|
38670
|
+
body: body === void 0 ? void 0 : JSON.stringify(body),
|
|
38671
|
+
signal: AbortSignal.timeout(15e3)
|
|
38672
|
+
});
|
|
38673
|
+
return { ok: res.ok, status: res.status, data: res.ok ? await res.json().catch(() => null) : null };
|
|
38674
|
+
} catch {
|
|
38675
|
+
return { ok: false, status: null, data: null };
|
|
38676
|
+
}
|
|
38677
|
+
};
|
|
38678
|
+
}
|
|
38679
|
+
var OWN_SIGN_IN = /* @__PURE__ */ new Set(["login", "logout", "mcp", "ci-login", "run", "enroll", "share-redeem", "help", "--help", "-h"]);
|
|
38680
|
+
var SESSION_REFUSED = /* @__PURE__ */ new Set(["unauthorized", "session_signed_out"]);
|
|
38681
|
+
var sessionWrites = trackWrites(refuseBodyRedirects());
|
|
38682
|
+
function refusedSavedSession(e, argv2) {
|
|
38683
|
+
if (!(e instanceof ApiError) || e.status !== 401 || !SESSION_REFUSED.has(refusalCode(e)) || OWN_SIGN_IN.has(argv2.cmd)) return false;
|
|
38684
|
+
if (Object.entries(argv2.flags).some(([k, v]) => k.endsWith("-stdin") && v === true)) return false;
|
|
38685
|
+
const base = resolveBase(argv2.flags.base);
|
|
38686
|
+
try {
|
|
38687
|
+
return Boolean(base && getToken(base));
|
|
38688
|
+
} catch {
|
|
38689
|
+
return false;
|
|
38690
|
+
}
|
|
38691
|
+
}
|
|
38475
38692
|
async function main() {
|
|
38476
38693
|
const { cmd, positionals, flags } = parseArgs(process.argv.slice(2));
|
|
38477
38694
|
const asJson = flags.json === true;
|
|
@@ -38527,7 +38744,7 @@ run \`kt --help\` for the flags each command takes`);
|
|
|
38527
38744
|
// blank User-Agent it never set. Relaxing only, and never required — an older CLI that omits it
|
|
38528
38745
|
// is treated exactly as it is today.
|
|
38529
38746
|
client: "cli",
|
|
38530
|
-
fetchImpl:
|
|
38747
|
+
fetchImpl: sessionWrites.fetch,
|
|
38531
38748
|
// #2984: only a hub borrow leaves a refresh token, in the OS keychain beside the access token.
|
|
38532
38749
|
getRefreshToken: () => {
|
|
38533
38750
|
try {
|
|
@@ -38562,19 +38779,7 @@ run \`kt --help\` for the flags each command takes`);
|
|
|
38562
38779
|
}
|
|
38563
38780
|
});
|
|
38564
38781
|
const kc = { get: getToken, set: setToken, clear: clearToken };
|
|
38565
|
-
const hubCall =
|
|
38566
|
-
try {
|
|
38567
|
-
const res = await refuseBodyRedirects()(cfg.base + path, {
|
|
38568
|
-
method,
|
|
38569
|
-
headers: { Authorization: `Bearer ${bearer}`, Accept: "application/json", ...body === void 0 ? {} : { "Content-Type": "application/json" } },
|
|
38570
|
-
body: body === void 0 ? void 0 : JSON.stringify(body),
|
|
38571
|
-
signal: AbortSignal.timeout(15e3)
|
|
38572
|
-
});
|
|
38573
|
-
return { ok: res.ok, status: res.status, data: res.ok ? await res.json().catch(() => null) : null };
|
|
38574
|
-
} catch {
|
|
38575
|
-
return { ok: false, status: null, data: null };
|
|
38576
|
-
}
|
|
38577
|
-
};
|
|
38782
|
+
const hubCall = hubCallFor(cfg.base);
|
|
38578
38783
|
const connectionContext = async (connId) => {
|
|
38579
38784
|
try {
|
|
38580
38785
|
const rows = (await client.listConnections()).connections;
|
|
@@ -38684,7 +38889,8 @@ run \`kt --help\` for the flags each command takes`);
|
|
|
38684
38889
|
console.error("kt login --browser is not available: browser sign-in is switched off. Sign in with the Kutrion app (`kt login`), or use `kt login --email <email> --password-stdin`.");
|
|
38685
38890
|
process.exit(EXIT_ERROR);
|
|
38686
38891
|
}
|
|
38687
|
-
|
|
38892
|
+
const tty = canPromptYes(process.stdin, process.stderr);
|
|
38893
|
+
if (accountRoot && loginTriesHub(flags, accountRoot)) {
|
|
38688
38894
|
const hub = await hubLogin({
|
|
38689
38895
|
base: cfg.base,
|
|
38690
38896
|
location: hubLocation(accountRoot),
|
|
@@ -38692,7 +38898,8 @@ run \`kt --help\` for the flags each command takes`);
|
|
|
38692
38898
|
keychainAvailable: osKeychainAvailable,
|
|
38693
38899
|
call: hubCall,
|
|
38694
38900
|
out: (l) => console.error(l),
|
|
38695
|
-
ask: (q) => promptLine(q)
|
|
38901
|
+
ask: (q) => promptLine(q),
|
|
38902
|
+
pairedOnly: !tty
|
|
38696
38903
|
});
|
|
38697
38904
|
if (hub.kind === "store_failed") die(hub.note);
|
|
38698
38905
|
if (hub.kind === "signed_in") {
|
|
@@ -38702,7 +38909,7 @@ run \`kt --help\` for the flags each command takes`);
|
|
|
38702
38909
|
break;
|
|
38703
38910
|
}
|
|
38704
38911
|
if (hub.note) console.error(hub.note);
|
|
38705
|
-
if (hubAbsent(hub.reason)) console.error("Installing the Kutrion app... Run `kt login` again once it is open, or sign in with your email and password below.");
|
|
38912
|
+
if (tty && hubAbsent(hub.reason)) console.error("Installing the Kutrion app... Run `kt login` again once it is open, or sign in with your email and password below.");
|
|
38706
38913
|
}
|
|
38707
38914
|
if (flags.email === void 0 && !fromStdin) {
|
|
38708
38915
|
const hint = otherAppsHint(accountRoot);
|
|
@@ -38762,6 +38969,7 @@ run \`kt --help\` for the flags each command takes`);
|
|
|
38762
38969
|
})()
|
|
38763
38970
|
}).catch(() => ({ kind: "skipped", note: null }));
|
|
38764
38971
|
recordSignIn(accountRoot, signedIn);
|
|
38972
|
+
recordLastAccountId(cfg.base, kc, signedIn);
|
|
38765
38973
|
console.log(`logged in (token stored in ${backendName()})`);
|
|
38766
38974
|
if (rememberBase(cfg.base)) console.log(`server saved: ${cfg.base} (later commands need no --base)`);
|
|
38767
38975
|
if (registered.kind === "skipped" && registered.note) console.error(registered.note);
|
|
@@ -40287,12 +40495,36 @@ run \`kt --help\` for the list of commands`);
|
|
|
40287
40495
|
}
|
|
40288
40496
|
}
|
|
40289
40497
|
installFetchProxy();
|
|
40290
|
-
|
|
40498
|
+
var argv = parseArgs(process.argv.slice(2));
|
|
40499
|
+
var hubSlots = { get: getToken, set: setToken, clear: clearToken };
|
|
40500
|
+
var hubSignInAvailable = (e) => e instanceof ApiError && e.status === 401 && appDataDir() !== null && osKeychainAvailable();
|
|
40501
|
+
retryAfterHubReborrow(main, {
|
|
40502
|
+
refusedSession: (e) => refusedSavedSession(e, argv),
|
|
40503
|
+
wrote: sessionWrites.wrote,
|
|
40504
|
+
lastAccount: () => lastAccountId(resolveBase(argv.flags.base), hubSlots),
|
|
40505
|
+
reborrow: async () => {
|
|
40506
|
+
const base = resolveBase(argv.flags.base);
|
|
40507
|
+
const root = appDataDir();
|
|
40508
|
+
if (!root) return { kind: "fallback", note: null, reason: "hub_not_running" };
|
|
40509
|
+
const hub = await reborrowFromHub({
|
|
40510
|
+
base,
|
|
40511
|
+
location: hubLocation(root),
|
|
40512
|
+
kc: hubSlots,
|
|
40513
|
+
keychainAvailable: osKeychainAvailable,
|
|
40514
|
+
call: hubCallFor(base),
|
|
40515
|
+
out: (l) => console.error(l),
|
|
40516
|
+
ask: (q) => promptLine(q)
|
|
40517
|
+
});
|
|
40518
|
+
if (hub.kind === "signed_in") recordSignIn(root, hub.user);
|
|
40519
|
+
return hub.kind === "fallback" && hubAbsent(hub.reason) ? { ...hub, note: null } : hub;
|
|
40520
|
+
},
|
|
40521
|
+
out: (l) => console.error(l)
|
|
40522
|
+
}).catch((e) => {
|
|
40291
40523
|
const api3 = e instanceof ApiError ? e : null;
|
|
40292
40524
|
const code = api3 ? exitForStatus(api3.status) : EXIT_ERROR;
|
|
40293
40525
|
const message = redactProxyUrls(e instanceof Error ? e.message : String(e));
|
|
40294
|
-
const lines = (storeFailure(e) ?? refusalLines(e) ?? [`error: ${message}`]).map(redactProxyUrls);
|
|
40295
|
-
if (
|
|
40526
|
+
const lines = (e instanceof HubAccountSwitchedError ? [e.message] : storeFailure(e) ?? refusalLines(e, " ", { hubSignIn: hubSignInAvailable(e) }) ?? [`error: ${message}`]).map(redactProxyUrls);
|
|
40527
|
+
if (argv.flags.json === true) {
|
|
40296
40528
|
console.log(JSON.stringify({
|
|
40297
40529
|
error: api3 ? refusalCode(api3) : message,
|
|
40298
40530
|
...api3 ? { status: api3.status } : {},
|