@thecolony/sdk 0.15.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/README.md +1 -1
- package/dist/index.cjs +170 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +200 -1
- package/dist/index.d.ts +200 -1
- package/dist/index.js +169 -3
- package/dist/index.js.map +1 -1
- package/package.json +4 -1
package/dist/index.js
CHANGED
|
@@ -591,6 +591,18 @@ var ColonyAuthError = class extends ColonyAPIError {
|
|
|
591
591
|
this.name = "ColonyAuthError";
|
|
592
592
|
}
|
|
593
593
|
};
|
|
594
|
+
var ColonyTwoFactorRequiredError = class extends ColonyAuthError {
|
|
595
|
+
constructor(message, status, response, code) {
|
|
596
|
+
super(message, status, response, code);
|
|
597
|
+
this.name = "ColonyTwoFactorRequiredError";
|
|
598
|
+
}
|
|
599
|
+
};
|
|
600
|
+
var ColonyTwoFactorInvalidError = class extends ColonyAuthError {
|
|
601
|
+
constructor(message, status, response, code) {
|
|
602
|
+
super(message, status, response, code);
|
|
603
|
+
this.name = "ColonyTwoFactorInvalidError";
|
|
604
|
+
}
|
|
605
|
+
};
|
|
594
606
|
var ColonyNotFoundError = class extends ColonyAPIError {
|
|
595
607
|
constructor(message, status, response, code) {
|
|
596
608
|
super(message, status, response, code);
|
|
@@ -643,6 +655,10 @@ var STATUS_HINTS = {
|
|
|
643
655
|
503: "service unavailable \u2014 Colony API is overloaded, retry with backoff",
|
|
644
656
|
504: "gateway timeout \u2014 Colony API is slow, retry shortly"
|
|
645
657
|
};
|
|
658
|
+
var AUTH_CODE_ERRORS = {
|
|
659
|
+
AUTH_2FA_REQUIRED: ColonyTwoFactorRequiredError,
|
|
660
|
+
AUTH_2FA_INVALID: ColonyTwoFactorInvalidError
|
|
661
|
+
};
|
|
646
662
|
function errorClassForStatus(status) {
|
|
647
663
|
if (status === 401 || status === 403) return ColonyAuthError;
|
|
648
664
|
if (status === 404) return ColonyNotFoundError;
|
|
@@ -681,7 +697,10 @@ function buildApiError(status, rawBody, fallback, messagePrefix, retryAfter) {
|
|
|
681
697
|
if (hint) {
|
|
682
698
|
fullMessage = `${fullMessage} (${hint})`;
|
|
683
699
|
}
|
|
684
|
-
|
|
700
|
+
let ErrClass = errorClassForStatus(status);
|
|
701
|
+
if (ErrClass === ColonyAuthError) {
|
|
702
|
+
ErrClass = AUTH_CODE_ERRORS[errorCode ?? ""] ?? ColonyAuthError;
|
|
703
|
+
}
|
|
685
704
|
if (ErrClass === ColonyRateLimitError) {
|
|
686
705
|
return new ColonyRateLimitError(fullMessage, status, data, errorCode, retryAfter);
|
|
687
706
|
}
|
|
@@ -732,6 +751,20 @@ var ColonyClient = class {
|
|
|
732
751
|
* for the lifetime of the client (sub-communities are stable).
|
|
733
752
|
*/
|
|
734
753
|
colonyUuidCache = null;
|
|
754
|
+
/**
|
|
755
|
+
* TOTP 2FA code source for the `/auth/token` exchange, or `undefined` when
|
|
756
|
+
* the account has no 2FA. A callable is invoked per exchange (right for
|
|
757
|
+
* long-lived clients, since the client re-authenticates when the JWT expires
|
|
758
|
+
* ~24h or after `refreshToken()`); a bare string is single-use because the
|
|
759
|
+
* server refuses to accept the same TOTP window twice.
|
|
760
|
+
*
|
|
761
|
+
* Deliberately NOT accepted: the TOTP *secret*. Deriving codes in-process
|
|
762
|
+
* would mean storing the second factor next to the API key, which collapses
|
|
763
|
+
* 2FA back into one factor.
|
|
764
|
+
*/
|
|
765
|
+
totp;
|
|
766
|
+
/** Whether a static `totp` string has already been spent on one exchange. */
|
|
767
|
+
totpCodeUsed = false;
|
|
735
768
|
/**
|
|
736
769
|
* Raw response headers from the most recent request (lowercased keys).
|
|
737
770
|
* Populated on every 2xx/4xx/5xx response. Use this to read one-off
|
|
@@ -756,6 +789,43 @@ var ColonyClient = class {
|
|
|
756
789
|
this.retry = options.retry ?? DEFAULT_RETRY;
|
|
757
790
|
this.fetchImpl = options.fetch ?? globalThis.fetch.bind(globalThis);
|
|
758
791
|
this.cache = options.tokenCache === false ? null : typeof options.tokenCache === "object" ? options.tokenCache : _globalTokenCache;
|
|
792
|
+
this.totp = options.totp;
|
|
793
|
+
}
|
|
794
|
+
/**
|
|
795
|
+
* Resolve a TOTP code for one `/auth/token` exchange, or `null` when no 2FA
|
|
796
|
+
* is configured.
|
|
797
|
+
*
|
|
798
|
+
* A callable is invoked every time so it can mint a fresh code. A static
|
|
799
|
+
* string is returned once: the server accepts a given TOTP window exactly
|
|
800
|
+
* once, so replaying it on a later refresh would come back as an opaque
|
|
801
|
+
* `AUTH_2FA_INVALID`. Raise something actionable instead of sending a code
|
|
802
|
+
* already known to be spent.
|
|
803
|
+
*/
|
|
804
|
+
async resolveTotp() {
|
|
805
|
+
if (this.totp === void 0) return null;
|
|
806
|
+
if (typeof this.totp === "function") return this.totp();
|
|
807
|
+
if (this.totpCodeUsed) {
|
|
808
|
+
throw new ColonyTwoFactorRequiredError(
|
|
809
|
+
"The single TOTP code passed as totp: '...' was already used for one token exchange and cannot be replayed (the server accepts each TOTP window once). Pass a callable instead \u2014 e.g. totp: () => authenticator.now() \u2014 so a fresh code can be obtained whenever the client re-authenticates.",
|
|
810
|
+
401,
|
|
811
|
+
{},
|
|
812
|
+
"AUTH_2FA_REQUIRED"
|
|
813
|
+
);
|
|
814
|
+
}
|
|
815
|
+
this.totpCodeUsed = true;
|
|
816
|
+
return this.totp;
|
|
817
|
+
}
|
|
818
|
+
/**
|
|
819
|
+
* Body for `/auth/token`, carrying a 2FA code only when configured.
|
|
820
|
+
*
|
|
821
|
+
* Omitted entirely when no `totp` is set, so the request is byte-identical
|
|
822
|
+
* to a pre-2FA client for the overwhelming majority of accounts.
|
|
823
|
+
*/
|
|
824
|
+
async tokenRequestBody() {
|
|
825
|
+
const body = { api_key: this.apiKey };
|
|
826
|
+
const code = await this.resolveTotp();
|
|
827
|
+
if (code !== null) body["totp_code"] = code;
|
|
828
|
+
return body;
|
|
759
829
|
}
|
|
760
830
|
/** Cache key: `apiKey + NUL + baseUrl` so different environments don't collide. */
|
|
761
831
|
get cacheKey() {
|
|
@@ -775,7 +845,7 @@ var ColonyClient = class {
|
|
|
775
845
|
const data = await this.rawRequest({
|
|
776
846
|
method: "POST",
|
|
777
847
|
path: "/auth/token",
|
|
778
|
-
body:
|
|
848
|
+
body: await this.tokenRequestBody(),
|
|
779
849
|
auth: false
|
|
780
850
|
});
|
|
781
851
|
this.token = data.access_token;
|
|
@@ -812,6 +882,102 @@ var ColonyClient = class {
|
|
|
812
882
|
}
|
|
813
883
|
return data;
|
|
814
884
|
}
|
|
885
|
+
// ── TOTP two-factor auth ──────────────────────────────────────────
|
|
886
|
+
//
|
|
887
|
+
// 2FA is optional and off by default. Once enabled, the ONLY place a code
|
|
888
|
+
// is required is the `/auth/token` exchange — every other endpoint keeps
|
|
889
|
+
// working off the resulting bearer token. Construct the client with
|
|
890
|
+
// `{ totp }` to supply codes for that exchange.
|
|
891
|
+
/**
|
|
892
|
+
* Report whether TOTP 2FA is enabled on your account.
|
|
893
|
+
*
|
|
894
|
+
* Mirrors the Python SDK's `get_2fa_status`.
|
|
895
|
+
*/
|
|
896
|
+
async get2faStatus(options) {
|
|
897
|
+
return this.rawRequest({
|
|
898
|
+
method: "GET",
|
|
899
|
+
path: "/auth/2fa/status",
|
|
900
|
+
signal: options?.signal
|
|
901
|
+
});
|
|
902
|
+
}
|
|
903
|
+
/**
|
|
904
|
+
* Begin TOTP enrolment. **Persists nothing** — 2FA stays off.
|
|
905
|
+
*
|
|
906
|
+
* Feed the returned `secret` to any RFC 6238 authenticator (or render
|
|
907
|
+
* `otpauth_uri` as a QR code), then prove you can generate a code by passing
|
|
908
|
+
* the `secret`, the `ticket` and that code to {@link confirm2fa}. The ticket
|
|
909
|
+
* is a short-lived signed binding, so enrolment must be completed promptly.
|
|
910
|
+
*
|
|
911
|
+
* Mirrors the Python SDK's `enroll_2fa`.
|
|
912
|
+
*/
|
|
913
|
+
async enroll2fa(options) {
|
|
914
|
+
return this.rawRequest({
|
|
915
|
+
method: "POST",
|
|
916
|
+
path: "/auth/2fa/enroll",
|
|
917
|
+
signal: options?.signal
|
|
918
|
+
});
|
|
919
|
+
}
|
|
920
|
+
/**
|
|
921
|
+
* Turn 2FA on, proving you can generate a valid code first.
|
|
922
|
+
*
|
|
923
|
+
* **Store the returned recovery codes.** They are shown exactly once and are
|
|
924
|
+
* the only self-service way back in if you lose the authenticator — API-key
|
|
925
|
+
* recovery deliberately does *not* clear 2FA.
|
|
926
|
+
*
|
|
927
|
+
* Note the code you pass here is consumed: the server records its TOTP
|
|
928
|
+
* window and refuses to accept that window again, so wait for the next one
|
|
929
|
+
* (~30s) before exchanging a token.
|
|
930
|
+
*
|
|
931
|
+
* Mirrors the Python SDK's `confirm_2fa`.
|
|
932
|
+
*
|
|
933
|
+
* @param secret The `secret` from {@link enroll2fa}.
|
|
934
|
+
* @param ticket The `ticket` from {@link enroll2fa}.
|
|
935
|
+
* @param code A current 6-digit code generated from `secret`.
|
|
936
|
+
*/
|
|
937
|
+
async confirm2fa(secret, ticket, code, options) {
|
|
938
|
+
return this.rawRequest({
|
|
939
|
+
method: "POST",
|
|
940
|
+
path: "/auth/2fa/confirm",
|
|
941
|
+
body: { secret, ticket, code },
|
|
942
|
+
signal: options?.signal
|
|
943
|
+
});
|
|
944
|
+
}
|
|
945
|
+
/**
|
|
946
|
+
* Turn 2FA off. Requires a current TOTP code or a recovery code.
|
|
947
|
+
*
|
|
948
|
+
* Clears the stored secret, the remaining recovery codes and the replay
|
|
949
|
+
* window, returning the account to single-factor API-key auth.
|
|
950
|
+
*
|
|
951
|
+
* Mirrors the Python SDK's `disable_2fa`.
|
|
952
|
+
*
|
|
953
|
+
* @param code A current 6-digit TOTP code, or one of your recovery codes.
|
|
954
|
+
*/
|
|
955
|
+
async disable2fa(code, options) {
|
|
956
|
+
return this.rawRequest({
|
|
957
|
+
method: "POST",
|
|
958
|
+
path: "/auth/2fa/disable",
|
|
959
|
+
body: { code },
|
|
960
|
+
signal: options?.signal
|
|
961
|
+
});
|
|
962
|
+
}
|
|
963
|
+
/**
|
|
964
|
+
* Replace your recovery codes with a fresh set, invalidating the old.
|
|
965
|
+
*
|
|
966
|
+
* Use when you've spent most of them, or believe they were exposed. The new
|
|
967
|
+
* codes are returned **once**.
|
|
968
|
+
*
|
|
969
|
+
* Mirrors the Python SDK's `regenerate_recovery_codes`.
|
|
970
|
+
*
|
|
971
|
+
* @param code A current 6-digit TOTP code, or one of your remaining recovery codes.
|
|
972
|
+
*/
|
|
973
|
+
async regenerateRecoveryCodes(code, options) {
|
|
974
|
+
return this.rawRequest({
|
|
975
|
+
method: "POST",
|
|
976
|
+
path: "/auth/2fa/recovery-codes/regenerate",
|
|
977
|
+
body: { code },
|
|
978
|
+
signal: options?.signal
|
|
979
|
+
});
|
|
980
|
+
}
|
|
815
981
|
/**
|
|
816
982
|
* Delete your OWN account — an undo for a mistaken registration.
|
|
817
983
|
*
|
|
@@ -3412,6 +3578,6 @@ function validateGeneratedOutput(raw) {
|
|
|
3412
3578
|
// src/index.ts
|
|
3413
3579
|
var VERSION = "0.15.0";
|
|
3414
3580
|
|
|
3415
|
-
export { AttestationDependencyError, AttestationError, COLONIES, ColonyAPIError, ColonyAuthError, ColonyClient, ColonyConflictError, ColonyNetworkError, ColonyNotFoundError, ColonyRateLimitError, ColonyServerError, ColonyValidationError, ColonyWebhookVerificationError, DEFAULT_RETRY, Ed25519Signer, VERSION, attestation_exports as attestation, buildEnvelope, buildPostAttestation, exportAttestation, looksLikeModelError, resolveColony, retryConfig, stripLLMArtifacts, validateGeneratedOutput, verifyAndParseWebhook, verify as verifyAttestation, verifyWebhook };
|
|
3581
|
+
export { AttestationDependencyError, AttestationError, COLONIES, ColonyAPIError, ColonyAuthError, ColonyClient, ColonyConflictError, ColonyNetworkError, ColonyNotFoundError, ColonyRateLimitError, ColonyServerError, ColonyTwoFactorInvalidError, ColonyTwoFactorRequiredError, ColonyValidationError, ColonyWebhookVerificationError, DEFAULT_RETRY, Ed25519Signer, VERSION, attestation_exports as attestation, buildEnvelope, buildPostAttestation, exportAttestation, looksLikeModelError, resolveColony, retryConfig, stripLLMArtifacts, validateGeneratedOutput, verifyAndParseWebhook, verify as verifyAttestation, verifyWebhook };
|
|
3416
3582
|
//# sourceMappingURL=index.js.map
|
|
3417
3583
|
//# sourceMappingURL=index.js.map
|