@hfunlabs/hypurr-connect 0.1.23 → 0.1.25
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 +2 -3
- package/dist/index.d.ts +0 -2
- package/dist/index.js +273 -24
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/HypurrConnectProvider.tsx +61 -20
- package/src/UserProfileModal.tsx +205 -4
- package/src/agent.ts +35 -4
- package/src/types.ts +0 -2
package/package.json
CHANGED
|
@@ -55,13 +55,14 @@ import type {
|
|
|
55
55
|
/** @internal context value — extends the public type with fields used only by library internals */
|
|
56
56
|
interface InternalConnectState extends HypurrConnectState {
|
|
57
57
|
loginTelegram: () => void;
|
|
58
|
+
/** Connected EOA owner address (EOA auth only); null otherwise. */
|
|
59
|
+
eoaAddress: `0x${string}` | null;
|
|
58
60
|
}
|
|
59
61
|
|
|
60
62
|
const TELEGRAM_STORAGE_KEY = "hypurr-connect-tg-jwt";
|
|
61
63
|
const LEGACY_TELEGRAM_STORAGE_KEY = "hypurr-connect-tg-user";
|
|
62
64
|
const TELEGRAM_AUTH_STATE_KEY = "hypurr-connect-auth-state";
|
|
63
|
-
const TELEGRAM_AUTH_CODE_VERIFIER_PREFIX =
|
|
64
|
-
"hypurr-connect-auth-code-verifier:";
|
|
65
|
+
const TELEGRAM_AUTH_CODE_VERIFIER_PREFIX = "hypurr-connect-auth-code-verifier:";
|
|
65
66
|
const TELEGRAM_AUTH_RETURN_TO_PREFIX = "hypurr-connect-auth-return-to:";
|
|
66
67
|
const TELEGRAM_AUTH_MESSAGE = "hypurr-connect:telegram-auth";
|
|
67
68
|
const DEFAULT_AUTH_HUB_URL = "https://auth.hypurr.fun/login";
|
|
@@ -350,12 +351,10 @@ function clearTelegramAuthSession(state: string): void {
|
|
|
350
351
|
sessionStorage.removeItem(returnToStorageKey(state));
|
|
351
352
|
}
|
|
352
353
|
|
|
353
|
-
function takeTelegramAuthSession(state: string):
|
|
354
|
-
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
}
|
|
358
|
-
| null {
|
|
354
|
+
function takeTelegramAuthSession(state: string): {
|
|
355
|
+
codeVerifier: string | null;
|
|
356
|
+
returnTo: string | null;
|
|
357
|
+
} | null {
|
|
359
358
|
const expectedState = sessionStorage.getItem(TELEGRAM_AUTH_STATE_KEY);
|
|
360
359
|
sessionStorage.removeItem(TELEGRAM_AUTH_STATE_KEY);
|
|
361
360
|
if (!expectedState || state !== expectedState) {
|
|
@@ -375,19 +374,63 @@ function takeTelegramAuthSession(state: string):
|
|
|
375
374
|
return { codeVerifier, returnTo };
|
|
376
375
|
}
|
|
377
376
|
|
|
378
|
-
function
|
|
379
|
-
const configuredTokenUrl = tokenUrl?.trim();
|
|
380
|
-
if (configuredTokenUrl) return configuredTokenUrl;
|
|
381
|
-
|
|
377
|
+
function fallbackAuthTokenUrl(authHubUrl?: string): string {
|
|
382
378
|
const url = new URL(authHubUrl || DEFAULT_AUTH_HUB_URL);
|
|
383
|
-
|
|
384
|
-
const basePath = pathWithoutTrailingSlash.replace(/\/[^/]*$/, "");
|
|
385
|
-
url.pathname = `${basePath}/token`;
|
|
379
|
+
url.pathname = "/oauth/token";
|
|
386
380
|
url.search = "";
|
|
387
381
|
url.hash = "";
|
|
388
382
|
return url.toString();
|
|
389
383
|
}
|
|
390
384
|
|
|
385
|
+
function authMetadataUrls(authHubUrl?: string): string[] {
|
|
386
|
+
const authUrl = new URL(authHubUrl || DEFAULT_AUTH_HUB_URL);
|
|
387
|
+
const urls = [
|
|
388
|
+
new URL("/.well-known/oauth-authorization-server", authUrl).toString(),
|
|
389
|
+
new URL("/.well-known/openid-configuration", authUrl).toString(),
|
|
390
|
+
];
|
|
391
|
+
|
|
392
|
+
const authPath = authUrl.pathname.replace(/\/+$/, "");
|
|
393
|
+
const basePath = authPath.replace(/\/[^/]*$/, "");
|
|
394
|
+
if (basePath) {
|
|
395
|
+
urls.push(
|
|
396
|
+
new URL(
|
|
397
|
+
`/.well-known/oauth-authorization-server${basePath}`,
|
|
398
|
+
authUrl,
|
|
399
|
+
).toString(),
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
return Array.from(new Set(urls));
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
async function tokenUrlFromMetadata(
|
|
407
|
+
authHubUrl?: string,
|
|
408
|
+
): Promise<string | undefined> {
|
|
409
|
+
for (const metadataUrl of authMetadataUrls(authHubUrl)) {
|
|
410
|
+
try {
|
|
411
|
+
const response = await fetch(metadataUrl, {
|
|
412
|
+
headers: { accept: "application/json" },
|
|
413
|
+
});
|
|
414
|
+
if (!response.ok) continue;
|
|
415
|
+
const metadata = (await response.json()) as { token_endpoint?: unknown };
|
|
416
|
+
const tokenEndpoint = metadata.token_endpoint;
|
|
417
|
+
if (typeof tokenEndpoint === "string" && tokenEndpoint.trim()) {
|
|
418
|
+
return tokenEndpoint.trim();
|
|
419
|
+
}
|
|
420
|
+
} catch {
|
|
421
|
+
// Metadata discovery is best effort; fall back to the conventional route.
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
return undefined;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
async function resolveAuthTokenUrl(authHubUrl?: string): Promise<string> {
|
|
429
|
+
return (
|
|
430
|
+
(await tokenUrlFromMetadata(authHubUrl)) || fallbackAuthTokenUrl(authHubUrl)
|
|
431
|
+
);
|
|
432
|
+
}
|
|
433
|
+
|
|
391
434
|
function getTokenFromExchangeResponse(data: unknown): string | null {
|
|
392
435
|
if (typeof data === "string") {
|
|
393
436
|
const token = data.trim();
|
|
@@ -412,14 +455,12 @@ async function exchangeTelegramAuthCode({
|
|
|
412
455
|
code,
|
|
413
456
|
codeVerifier,
|
|
414
457
|
returnTo,
|
|
415
|
-
tokenUrl,
|
|
416
458
|
}: {
|
|
417
459
|
authHubUrl?: string;
|
|
418
460
|
clientId: string;
|
|
419
461
|
code: string;
|
|
420
462
|
codeVerifier: string;
|
|
421
463
|
returnTo: string;
|
|
422
|
-
tokenUrl?: string;
|
|
423
464
|
}): Promise<string> {
|
|
424
465
|
const body = new URLSearchParams({
|
|
425
466
|
client_id: clientId,
|
|
@@ -429,7 +470,7 @@ async function exchangeTelegramAuthCode({
|
|
|
429
470
|
return_to: returnTo,
|
|
430
471
|
});
|
|
431
472
|
|
|
432
|
-
const response = await fetch(resolveAuthTokenUrl(authHubUrl
|
|
473
|
+
const response = await fetch(await resolveAuthTokenUrl(authHubUrl), {
|
|
433
474
|
method: "POST",
|
|
434
475
|
headers: {
|
|
435
476
|
accept: "application/json",
|
|
@@ -599,7 +640,6 @@ export function HypurrConnectProvider({
|
|
|
599
640
|
code: callback.code,
|
|
600
641
|
codeVerifier: authSession.codeVerifier,
|
|
601
642
|
returnTo: authSession.returnTo || currentReturnTo(),
|
|
602
|
-
tokenUrl: config.telegram?.tokenUrl,
|
|
603
643
|
})
|
|
604
644
|
.then(acceptTelegramToken)
|
|
605
645
|
.catch((err) =>
|
|
@@ -620,7 +660,6 @@ export function HypurrConnectProvider({
|
|
|
620
660
|
acceptTelegramToken,
|
|
621
661
|
config.clientId,
|
|
622
662
|
config.telegram?.authHubUrl,
|
|
623
|
-
config.telegram?.tokenUrl,
|
|
624
663
|
],
|
|
625
664
|
);
|
|
626
665
|
|
|
@@ -1858,6 +1897,7 @@ export function HypurrConnectProvider({
|
|
|
1858
1897
|
agent,
|
|
1859
1898
|
agentReady,
|
|
1860
1899
|
clearAgent: handleClearAgent,
|
|
1900
|
+
eoaAddress,
|
|
1861
1901
|
|
|
1862
1902
|
authDataMap,
|
|
1863
1903
|
authToken: tgAuthToken,
|
|
@@ -1902,6 +1942,7 @@ export function HypurrConnectProvider({
|
|
|
1902
1942
|
agent,
|
|
1903
1943
|
agentReady,
|
|
1904
1944
|
handleClearAgent,
|
|
1945
|
+
eoaAddress,
|
|
1905
1946
|
authDataMap,
|
|
1906
1947
|
tgAuthToken,
|
|
1907
1948
|
telegramRpcOptions,
|
package/src/UserProfileModal.tsx
CHANGED
|
@@ -2,6 +2,8 @@ import { AnimatePresence, motion } from "framer-motion";
|
|
|
2
2
|
import type { HyperliquidWallet } from "hypurr-grpc/ts/hypurr/wallet";
|
|
3
3
|
import {
|
|
4
4
|
useCallback,
|
|
5
|
+
useEffect,
|
|
6
|
+
useMemo,
|
|
5
7
|
useState,
|
|
6
8
|
type CSSProperties,
|
|
7
9
|
type ReactNode,
|
|
@@ -13,7 +15,12 @@ import {
|
|
|
13
15
|
import { DeleteWalletModal } from "./DeleteWalletModal";
|
|
14
16
|
import { useHypurrConnectInternal } from "./HypurrConnectProvider";
|
|
15
17
|
import { RenameWalletModal } from "./RenameWalletModal";
|
|
16
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
clearAgent as clearStoredAgent,
|
|
20
|
+
loadAllAgents,
|
|
21
|
+
type LocalApprovedAgent,
|
|
22
|
+
} from "./agent";
|
|
23
|
+
import { formatAgentExpiry, getAgentExpiryTitle } from "./agentWallet";
|
|
17
24
|
import {
|
|
18
25
|
Bot,
|
|
19
26
|
Copy,
|
|
@@ -314,6 +321,9 @@ export function UserProfileModal({
|
|
|
314
321
|
deleteWallet,
|
|
315
322
|
renameWallet,
|
|
316
323
|
authMethod,
|
|
324
|
+
agent,
|
|
325
|
+
eoaAddress,
|
|
326
|
+
clearAgent: clearContextAgent,
|
|
317
327
|
} = useHypurrConnectInternal();
|
|
318
328
|
|
|
319
329
|
const [settingsTab, setSettingsTab] = useState<SettingsTab>("ui");
|
|
@@ -321,6 +331,41 @@ export function UserProfileModal({
|
|
|
321
331
|
useState<HyperliquidWallet | null>(null);
|
|
322
332
|
const [walletToRename, setWalletToRename] =
|
|
323
333
|
useState<HyperliquidWallet | null>(null);
|
|
334
|
+
const [storedAgents, setStoredAgents] = useState<LocalApprovedAgent[]>([]);
|
|
335
|
+
|
|
336
|
+
// Local approved agents live in localStorage; refresh whenever the modal opens.
|
|
337
|
+
useEffect(() => {
|
|
338
|
+
if (isOpen) setStoredAgents(loadAllAgents());
|
|
339
|
+
}, [isOpen]);
|
|
340
|
+
|
|
341
|
+
// Merge the localStorage scan with the reactive context agent (the currently
|
|
342
|
+
// connected EOA's approved agent), deduped by owner address.
|
|
343
|
+
const localAgents = useMemo(() => {
|
|
344
|
+
const byOwner = new Map<string, LocalApprovedAgent>();
|
|
345
|
+
for (const a of storedAgents) byOwner.set(a.masterAddress.toLowerCase(), a);
|
|
346
|
+
if (agent && eoaAddress && !byOwner.has(eoaAddress.toLowerCase())) {
|
|
347
|
+
byOwner.set(eoaAddress.toLowerCase(), {
|
|
348
|
+
...agent,
|
|
349
|
+
masterAddress: eoaAddress,
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
return [...byOwner.values()].sort((a, b) => b.approvedAt - a.approvedAt);
|
|
353
|
+
}, [storedAgents, agent, eoaAddress]);
|
|
354
|
+
|
|
355
|
+
const handleRemoveLocalAgent = useCallback(
|
|
356
|
+
(masterAddress: string) => {
|
|
357
|
+
if (
|
|
358
|
+
eoaAddress &&
|
|
359
|
+
masterAddress.toLowerCase() === eoaAddress.toLowerCase()
|
|
360
|
+
) {
|
|
361
|
+
clearContextAgent();
|
|
362
|
+
} else {
|
|
363
|
+
clearStoredAgent(masterAddress);
|
|
364
|
+
}
|
|
365
|
+
setStoredAgents(loadAllAgents());
|
|
366
|
+
},
|
|
367
|
+
[eoaAddress, clearContextAgent],
|
|
368
|
+
);
|
|
324
369
|
|
|
325
370
|
const profilePic = user?.photoUrl || "";
|
|
326
371
|
const displayName = user?.displayName || "";
|
|
@@ -347,10 +392,12 @@ export function UserProfileModal({
|
|
|
347
392
|
);
|
|
348
393
|
|
|
349
394
|
const handleCopyAddress = useCallback(() => {
|
|
350
|
-
|
|
351
|
-
|
|
395
|
+
// Copy the full address, not the truncated display name.
|
|
396
|
+
const address = user?.address || eoaAddress || displayName;
|
|
397
|
+
if (!address) return;
|
|
398
|
+
navigator.clipboard.writeText(address);
|
|
352
399
|
onNotify?.({ type: "success", message: "Address copied" });
|
|
353
|
-
}, [displayName, onNotify]);
|
|
400
|
+
}, [user?.address, eoaAddress, displayName, onNotify]);
|
|
354
401
|
|
|
355
402
|
return (
|
|
356
403
|
<div className="hypurr-connect" style={{ display: "contents" }}>
|
|
@@ -798,6 +845,48 @@ export function UserProfileModal({
|
|
|
798
845
|
{wallets.length} wallet
|
|
799
846
|
{wallets.length !== 1 ? "s" : ""} linked
|
|
800
847
|
</p>
|
|
848
|
+
|
|
849
|
+
{localAgents.length > 0 && (
|
|
850
|
+
<div style={{ marginTop: 8 }}>
|
|
851
|
+
<p
|
|
852
|
+
style={{
|
|
853
|
+
margin: "0 0 8px",
|
|
854
|
+
color: profileColors.muted,
|
|
855
|
+
...upperLabelStyle,
|
|
856
|
+
}}
|
|
857
|
+
>
|
|
858
|
+
Agent wallets
|
|
859
|
+
</p>
|
|
860
|
+
<div
|
|
861
|
+
style={{
|
|
862
|
+
display: "flex",
|
|
863
|
+
flexDirection: "column",
|
|
864
|
+
gap: 6,
|
|
865
|
+
}}
|
|
866
|
+
>
|
|
867
|
+
{localAgents.map((agent) => (
|
|
868
|
+
<LocalAgentRow
|
|
869
|
+
key={agent.masterAddress}
|
|
870
|
+
agent={agent}
|
|
871
|
+
onRemove={() =>
|
|
872
|
+
handleRemoveLocalAgent(agent.masterAddress)
|
|
873
|
+
}
|
|
874
|
+
/>
|
|
875
|
+
))}
|
|
876
|
+
</div>
|
|
877
|
+
<p
|
|
878
|
+
style={{
|
|
879
|
+
margin: "8px 0 0",
|
|
880
|
+
fontSize: 12.5,
|
|
881
|
+
lineHeight: "1rem",
|
|
882
|
+
color: profileColors.subdued,
|
|
883
|
+
textAlign: "center",
|
|
884
|
+
}}
|
|
885
|
+
>
|
|
886
|
+
Approved on this device
|
|
887
|
+
</p>
|
|
888
|
+
</div>
|
|
889
|
+
)}
|
|
801
890
|
</div>
|
|
802
891
|
)}
|
|
803
892
|
</div>
|
|
@@ -1039,6 +1128,118 @@ function WalletRow({
|
|
|
1039
1128
|
);
|
|
1040
1129
|
}
|
|
1041
1130
|
|
|
1131
|
+
function shortAddress(address: string): string {
|
|
1132
|
+
return `${address.slice(0, 6)}...${address.slice(-4)}`;
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
// Older builds stored validUntil scaled ×1000 (µs). Coerce those back to ms.
|
|
1136
|
+
function normalizeExpiryMs(validUntil: number): number {
|
|
1137
|
+
return validUntil > 1e14 ? Math.round(validUntil / 1000) : validUntil;
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
function LocalAgentRow({
|
|
1141
|
+
agent,
|
|
1142
|
+
onRemove,
|
|
1143
|
+
}: {
|
|
1144
|
+
agent: LocalApprovedAgent;
|
|
1145
|
+
onRemove: () => void;
|
|
1146
|
+
}) {
|
|
1147
|
+
const [hovered, setHovered] = useState(false);
|
|
1148
|
+
const expiresAtMs = normalizeExpiryMs(agent.validUntil);
|
|
1149
|
+
const isExpired = expiresAtMs <= Date.now();
|
|
1150
|
+
const agentTypeColor = "rgb(var(--blue-500))";
|
|
1151
|
+
return (
|
|
1152
|
+
<div
|
|
1153
|
+
style={{
|
|
1154
|
+
...walletRowStyle,
|
|
1155
|
+
background: hovered
|
|
1156
|
+
? profileColors.surfaceBtnHover
|
|
1157
|
+
: profileColors.surfaceBtn,
|
|
1158
|
+
borderColor: hovered
|
|
1159
|
+
? profileColors.surfaceBdHover
|
|
1160
|
+
: profileColors.surfaceBd,
|
|
1161
|
+
}}
|
|
1162
|
+
onMouseEnter={() => setHovered(true)}
|
|
1163
|
+
onMouseLeave={() => setHovered(false)}
|
|
1164
|
+
>
|
|
1165
|
+
<div
|
|
1166
|
+
style={{
|
|
1167
|
+
width: 32,
|
|
1168
|
+
height: 32,
|
|
1169
|
+
borderRadius: 6,
|
|
1170
|
+
background: "rgb(var(--blue-500) / 0.16)",
|
|
1171
|
+
border: "1px solid rgb(var(--blue-500) / 0.34)",
|
|
1172
|
+
display: "flex",
|
|
1173
|
+
alignItems: "center",
|
|
1174
|
+
justifyContent: "center",
|
|
1175
|
+
flexShrink: 0,
|
|
1176
|
+
color: agentTypeColor,
|
|
1177
|
+
}}
|
|
1178
|
+
>
|
|
1179
|
+
<Bot size={15} />
|
|
1180
|
+
</div>
|
|
1181
|
+
<div style={{ flex: 1, minWidth: 0 }}>
|
|
1182
|
+
<p
|
|
1183
|
+
style={{
|
|
1184
|
+
margin: 0,
|
|
1185
|
+
fontSize: 12.5,
|
|
1186
|
+
lineHeight: "1rem",
|
|
1187
|
+
fontWeight: 500,
|
|
1188
|
+
color: profileColors.text,
|
|
1189
|
+
fontFamily: fontFamily.mono,
|
|
1190
|
+
overflow: "hidden",
|
|
1191
|
+
textOverflow: "ellipsis",
|
|
1192
|
+
whiteSpace: "nowrap",
|
|
1193
|
+
}}
|
|
1194
|
+
>
|
|
1195
|
+
{shortAddress(agent.address)}
|
|
1196
|
+
</p>
|
|
1197
|
+
<p
|
|
1198
|
+
style={{
|
|
1199
|
+
margin: "2px 0 0",
|
|
1200
|
+
fontSize: 12.5,
|
|
1201
|
+
lineHeight: "1rem",
|
|
1202
|
+
color: isExpired ? EXPIRED_AGENT_COLOR : profileColors.muted,
|
|
1203
|
+
}}
|
|
1204
|
+
>
|
|
1205
|
+
{isExpired ? "Expired" : "Valid until"}{" "}
|
|
1206
|
+
{formatAgentExpiry(expiresAtMs)}
|
|
1207
|
+
</p>
|
|
1208
|
+
<p
|
|
1209
|
+
style={{
|
|
1210
|
+
margin: "2px 0 0",
|
|
1211
|
+
fontSize: 12.5,
|
|
1212
|
+
lineHeight: "1rem",
|
|
1213
|
+
color: profileColors.subdued,
|
|
1214
|
+
fontFamily: fontFamily.mono,
|
|
1215
|
+
}}
|
|
1216
|
+
>
|
|
1217
|
+
Owner {shortAddress(agent.masterAddress)}
|
|
1218
|
+
</p>
|
|
1219
|
+
</div>
|
|
1220
|
+
<div
|
|
1221
|
+
style={{
|
|
1222
|
+
display: "flex",
|
|
1223
|
+
alignItems: "center",
|
|
1224
|
+
flexShrink: 0,
|
|
1225
|
+
opacity: hovered ? 1 : 0,
|
|
1226
|
+
transition: "opacity 120ms",
|
|
1227
|
+
}}
|
|
1228
|
+
>
|
|
1229
|
+
<IconBtn
|
|
1230
|
+
color="#ef4444"
|
|
1231
|
+
hoverBackgroundColor="rgba(239,68,68,0.1)"
|
|
1232
|
+
title="Remove local agent"
|
|
1233
|
+
ariaLabel={`Remove local agent for ${shortAddress(agent.masterAddress)}`}
|
|
1234
|
+
onClick={onRemove}
|
|
1235
|
+
>
|
|
1236
|
+
<Trash2 size={13} />
|
|
1237
|
+
</IconBtn>
|
|
1238
|
+
</div>
|
|
1239
|
+
</div>
|
|
1240
|
+
);
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1042
1243
|
function IconBtn({
|
|
1043
1244
|
children,
|
|
1044
1245
|
color,
|
package/src/agent.ts
CHANGED
|
@@ -26,6 +26,39 @@ export function clearAgent(masterAddress: string): void {
|
|
|
26
26
|
localStorage.removeItem(storageKey(masterAddress));
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
/** A locally-stored agent alongside the master address it was approved for. */
|
|
30
|
+
export interface LocalApprovedAgent extends StoredAgent {
|
|
31
|
+
masterAddress: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Enumerate every agent approved and stored locally on this device by scanning
|
|
36
|
+
* localStorage for the agent storage prefix. Newest approvals first.
|
|
37
|
+
*/
|
|
38
|
+
export function loadAllAgents(): LocalApprovedAgent[] {
|
|
39
|
+
const agents: LocalApprovedAgent[] = [];
|
|
40
|
+
try {
|
|
41
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
42
|
+
const key = localStorage.key(i);
|
|
43
|
+
if (!key || !key.startsWith(`${AGENT_STORAGE_PREFIX}:`)) continue;
|
|
44
|
+
const raw = localStorage.getItem(key);
|
|
45
|
+
if (!raw) continue;
|
|
46
|
+
try {
|
|
47
|
+
const agent = JSON.parse(raw) as StoredAgent;
|
|
48
|
+
agents.push({
|
|
49
|
+
...agent,
|
|
50
|
+
masterAddress: key.slice(key.indexOf(":") + 1),
|
|
51
|
+
});
|
|
52
|
+
} catch {
|
|
53
|
+
// Skip malformed entries.
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
} catch {
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
59
|
+
return agents.sort((a, b) => b.approvedAt - a.approvedAt);
|
|
60
|
+
}
|
|
61
|
+
|
|
29
62
|
/**
|
|
30
63
|
* Generate a random 32-byte private key and derive its address using the
|
|
31
64
|
* local PrivateKeySigner compatibility wrapper.
|
|
@@ -73,10 +106,8 @@ async function fetchExtraAgents(
|
|
|
73
106
|
const agents: unknown = await res.json();
|
|
74
107
|
if (!Array.isArray(agents)) return [];
|
|
75
108
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
validUntil: agent.validUntil * 1000,
|
|
79
|
-
}));
|
|
109
|
+
// The extraAgents API already returns validUntil in epoch ms.
|
|
110
|
+
return agents as ExtraAgent[];
|
|
80
111
|
}
|
|
81
112
|
|
|
82
113
|
/**
|
package/src/types.ts
CHANGED
|
@@ -33,8 +33,6 @@ export interface HypurrConnectConfig {
|
|
|
33
33
|
telegram?: {
|
|
34
34
|
/** Auth hub login URL. Defaults to https://auth.hypurr.fun/login. */
|
|
35
35
|
authHubUrl?: string;
|
|
36
|
-
/** Auth hub token exchange URL. Defaults to the auth hub login URL with `/login` replaced by `/token`. */
|
|
37
|
-
tokenUrl?: string;
|
|
38
36
|
/** Optional callback URL. Defaults to the current page without auth query params. */
|
|
39
37
|
returnTo?: string | (() => string);
|
|
40
38
|
/** Requested hub scopes. Defaults to the scopes required by this SDK. */
|