@lazyneoaz/metachat 4.0.6 → 4.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +608 -78
- package/dist/index.d.mts +298 -1
- package/dist/index.d.ts +298 -1
- package/dist/index.mjs +601 -79
- package/package.json +2 -2
package/dist/index.d.mts
CHANGED
|
@@ -99,6 +99,19 @@ interface LoginOptions$1 {
|
|
|
99
99
|
warmUp?: boolean;
|
|
100
100
|
/** Alternate foreground/background on a human rhythm to avoid detection. */
|
|
101
101
|
liveness?: boolean;
|
|
102
|
+
/**
|
|
103
|
+
* Auto-recovery supervisor. `true` (default) keeps a dead session alive by
|
|
104
|
+
* refreshing cookies and, when credentials are available, re-logging in.
|
|
105
|
+
* Pass an options object to tune it, or `false` to disable it.
|
|
106
|
+
*/
|
|
107
|
+
recovery?: boolean | {
|
|
108
|
+
intervalMs?: number;
|
|
109
|
+
failureThreshold?: number;
|
|
110
|
+
maxRecoveries?: number;
|
|
111
|
+
noRelogin?: boolean;
|
|
112
|
+
};
|
|
113
|
+
/** Write a refreshed appState here on successful recovery (best-effort). */
|
|
114
|
+
appStatePath?: string;
|
|
102
115
|
logging?: boolean;
|
|
103
116
|
pageID?: string;
|
|
104
117
|
[key: string]: any;
|
|
@@ -132,6 +145,8 @@ interface ApiContext$1 {
|
|
|
132
145
|
fb_dtsg?: string;
|
|
133
146
|
jazoest?: string;
|
|
134
147
|
lsd?: string;
|
|
148
|
+
/** App-scoped abuse id (`x-asbd-id`) scraped from the current document. */
|
|
149
|
+
asbdId?: string;
|
|
135
150
|
ttstamp?: string;
|
|
136
151
|
threadTypeCache?: Record<string, boolean>;
|
|
137
152
|
lastMessageTime?: number;
|
|
@@ -720,6 +735,16 @@ declare function refreshSessionCookies(ctx: any, defaultFuncs: any, options?: {
|
|
|
720
735
|
}>;
|
|
721
736
|
/** Detect an auth/document-token failure in a thrown or returned payload. */
|
|
722
737
|
declare function isAuthFailure(value: any): boolean;
|
|
738
|
+
/**
|
|
739
|
+
* Detect a logged-out document.
|
|
740
|
+
*
|
|
741
|
+
* When Facebook invalidates a session it does not always answer with an auth
|
|
742
|
+
* error code: it serves the login page, or (when the browser/device is still
|
|
743
|
+
* trusted) the one-tap "Continue as <name>" interstitial. Both look like a
|
|
744
|
+
* normal `200 text/html`, so a session can silently become useless. Recognising
|
|
745
|
+
* the document lets the caller re-authenticate instead of limping along.
|
|
746
|
+
*/
|
|
747
|
+
declare function isLoggedOutDocument(html: any): boolean;
|
|
723
748
|
/**
|
|
724
749
|
* Wrap a network operation with one repair-and-retry pass.
|
|
725
750
|
*
|
|
@@ -935,9 +960,281 @@ declare function canActProactively(ctx: any): boolean;
|
|
|
935
960
|
/** Wait until the session is foregrounded and the breaker is closed. */
|
|
936
961
|
declare function awaitProactiveSlot(ctx: any, maxWaitMs?: number): Promise<boolean>;
|
|
937
962
|
|
|
963
|
+
/**
|
|
964
|
+
* Long-run auto-recovery ("set and forget").
|
|
965
|
+
*
|
|
966
|
+
* The pieces that keep a session alive already exist and are individually
|
|
967
|
+
* correct: cookie refresh, Comet-token invalidation, the account-safety
|
|
968
|
+
* breaker, MQTT reconnect backoff, human pacing. What was missing was the
|
|
969
|
+
* *supervisor* that ties them together — something that notices when the
|
|
970
|
+
* session has quietly died and does the one useful thing that remains.
|
|
971
|
+
*
|
|
972
|
+
* This module adds that supervisor and is deliberately conservative:
|
|
973
|
+
*
|
|
974
|
+
* - It samples health on a slow, jittered cadence (never a hot loop).
|
|
975
|
+
* - It first tries the cheap fix: refresh cookies + tokens.
|
|
976
|
+
* - Only if the session is still unauthenticated *and* credentials were
|
|
977
|
+
* supplied does it attempt a real credential re-login.
|
|
978
|
+
* - Every recovery attempt is gated by the safety breaker and a per-session
|
|
979
|
+
* attempt budget with exponential backoff, so a genuinely dead account
|
|
980
|
+
* cannot turn into a login storm that gets it checkpointed.
|
|
981
|
+
* - It reports every state change through a callback, so a bot can log or
|
|
982
|
+
* alert instead of failing silently.
|
|
983
|
+
*
|
|
984
|
+
* Everything here is pure logic over an injected "runtime" — the network work
|
|
985
|
+
* is delegated to callbacks the caller supplies. That keeps the module
|
|
986
|
+
* unit-testable without any network, and means a bug here can only ever
|
|
987
|
+
* *skip* a recovery, never corrupt a live request.
|
|
988
|
+
*/
|
|
989
|
+
|
|
990
|
+
type RecoveryPhase = "healthy" | "refreshing" | "recovering" | "relogin" | "cooling" | "stopped";
|
|
991
|
+
interface RecoveryState {
|
|
992
|
+
phase: RecoveryPhase;
|
|
993
|
+
/** Total health samples taken. */
|
|
994
|
+
checks: number;
|
|
995
|
+
/** Consecutive unhealthy samples. */
|
|
996
|
+
consecutiveFailures: number;
|
|
997
|
+
/** Successful auto-heals (cookie refresh or repair) that restored auth. */
|
|
998
|
+
healCount: number;
|
|
999
|
+
/** Total recovery attempts made (successful or not), for the budget. */
|
|
1000
|
+
attempts: number;
|
|
1001
|
+
/** Successful full re-logins. */
|
|
1002
|
+
reloginCount: number;
|
|
1003
|
+
/** Recoveries abandoned because the budget/breaker said stop. */
|
|
1004
|
+
givenUp: number;
|
|
1005
|
+
/** ms timestamp of the last successful health confirmation. */
|
|
1006
|
+
lastHealthyAt: number;
|
|
1007
|
+
/** ms until the next scheduled check. */
|
|
1008
|
+
nextCheckMs: number;
|
|
1009
|
+
/** Whether a credential re-login is possible for this session. */
|
|
1010
|
+
canRelogin: boolean;
|
|
1011
|
+
/** Last human-readable detail. */
|
|
1012
|
+
lastDetail?: string;
|
|
1013
|
+
}
|
|
1014
|
+
/**
|
|
1015
|
+
* The dependency surface the supervisor needs. All network work is behind
|
|
1016
|
+
* these callbacks so the supervisor itself stays pure and testable.
|
|
1017
|
+
*/
|
|
1018
|
+
interface RecoveryRuntime {
|
|
1019
|
+
ctx: any;
|
|
1020
|
+
/** True when the jar still holds a usable session. Cheap, synchronous. */
|
|
1021
|
+
isAuthenticated: () => boolean;
|
|
1022
|
+
/**
|
|
1023
|
+
* Active server-side liveness check. Distinct from {@link isAuthenticated}:
|
|
1024
|
+
* the jar can still look authenticated locally while Facebook has already
|
|
1025
|
+
* invalidated the session server-side (the "Continue as <name>" page). When
|
|
1026
|
+
* provided, the supervisor trusts this over the cheap local check.
|
|
1027
|
+
*/
|
|
1028
|
+
probeLiveness?: () => Promise<boolean>;
|
|
1029
|
+
/** Refresh cookies/tokens in place; resolves to whether auth survived. */
|
|
1030
|
+
refresh: () => Promise<boolean>;
|
|
1031
|
+
/**
|
|
1032
|
+
* Full credential re-login. Must resolve to a fresh appState (array) on
|
|
1033
|
+
* success, or null/throw on failure. Optional: when absent, no re-login is
|
|
1034
|
+
* attempted and the supervisor only reports the dead session.
|
|
1035
|
+
*/
|
|
1036
|
+
relogin?: () => Promise<any[] | null>;
|
|
1037
|
+
/**
|
|
1038
|
+
* Whether a credential re-login is actually possible for this session (e.g.
|
|
1039
|
+
* email+password were supplied). Defaults to whether `relogin` is set. This
|
|
1040
|
+
* exists because `relogin` is always provided by the login plumbing, but it
|
|
1041
|
+
* is only *useful* when the caller had credentials.
|
|
1042
|
+
*/
|
|
1043
|
+
canRelogin?: boolean;
|
|
1044
|
+
/** Persist a refreshed appState (e.g. write the account file). Optional. */
|
|
1045
|
+
persistAppState?: (appState: any[]) => void;
|
|
1046
|
+
/** Restart the MQTT listener after a recovery. Optional. */
|
|
1047
|
+
restartMqtt?: () => void;
|
|
1048
|
+
/** Observe state transitions (logging/alerting). Optional. */
|
|
1049
|
+
onEvent?: (event: RecoveryEvent) => void;
|
|
1050
|
+
}
|
|
1051
|
+
interface RecoveryEvent {
|
|
1052
|
+
type: "check" | "unhealthy" | "healed" | "relogin" | "gave-up" | "cooling" | "stopped";
|
|
1053
|
+
detail: string;
|
|
1054
|
+
state: RecoveryState;
|
|
1055
|
+
}
|
|
1056
|
+
interface RecoveryOptions {
|
|
1057
|
+
/** Base interval between health samples. Jittered ±30% each tick. */
|
|
1058
|
+
intervalMs?: number;
|
|
1059
|
+
/** Consecutive failed checks before the supervisor intervenes. */
|
|
1060
|
+
failureThreshold?: number;
|
|
1061
|
+
/** Max automatic recoveries (heal+relogin) before the supervisor gives up. */
|
|
1062
|
+
maxRecoveries?: number;
|
|
1063
|
+
/** Disable credential re-login even when credentials exist. */
|
|
1064
|
+
noRelogin?: boolean;
|
|
1065
|
+
}
|
|
1066
|
+
/**
|
|
1067
|
+
* The supervisor. Create with {@link startRecovery} rather than directly.
|
|
1068
|
+
*/
|
|
1069
|
+
declare class RecoverySupervisor {
|
|
1070
|
+
private runtime;
|
|
1071
|
+
private opts;
|
|
1072
|
+
private timer;
|
|
1073
|
+
private stopped;
|
|
1074
|
+
private running;
|
|
1075
|
+
private state;
|
|
1076
|
+
private backoffMs;
|
|
1077
|
+
constructor(runtime: RecoveryRuntime, options?: RecoveryOptions);
|
|
1078
|
+
/** A point-in-time copy of the supervisor state. */
|
|
1079
|
+
snapshot(): RecoveryState;
|
|
1080
|
+
isRunning(): boolean;
|
|
1081
|
+
/** Start the supervisor. Idempotent. */
|
|
1082
|
+
start(): void;
|
|
1083
|
+
/** Stop the supervisor and clear any pending timer. */
|
|
1084
|
+
stop(): void;
|
|
1085
|
+
/**
|
|
1086
|
+
* Run one health check immediately (used by tests and by `checkNow()`).
|
|
1087
|
+
* Returns the resulting state. Never throws.
|
|
1088
|
+
*/
|
|
1089
|
+
checkNow(): Promise<RecoveryState>;
|
|
1090
|
+
private nextCheckAt;
|
|
1091
|
+
private startTimer;
|
|
1092
|
+
private runOnce;
|
|
1093
|
+
private markHealthy;
|
|
1094
|
+
private afterRecovery;
|
|
1095
|
+
private emit;
|
|
1096
|
+
}
|
|
1097
|
+
/**
|
|
1098
|
+
* Wire a supervisor to a live session.
|
|
1099
|
+
*
|
|
1100
|
+
* @param ctx session context (mutated with `__recovery`)
|
|
1101
|
+
* @param runtime the injected dependency surface
|
|
1102
|
+
* @param options tuning knobs
|
|
1103
|
+
*/
|
|
1104
|
+
declare function startRecovery(ctx: any, runtime: RecoveryRuntime, options?: RecoveryOptions): RecoverySupervisor;
|
|
1105
|
+
|
|
1106
|
+
/**
|
|
1107
|
+
* MQTT connection health.
|
|
1108
|
+
*
|
|
1109
|
+
* A Messenger realtime socket fails in two ways:
|
|
1110
|
+
*
|
|
1111
|
+
* 1. **Loudly** — TCP drop, broker close, auth error. mqtt.js raises `close`
|
|
1112
|
+
* and the listener reconnects. Easy.
|
|
1113
|
+
* 2. **Silently** — the socket stays "connected" but Facebook has stopped
|
|
1114
|
+
* routing events to it (a half-open connection, a broker-side idle reap, a
|
|
1115
|
+
* revoked session). No error is emitted, `client.connected` stays `true`,
|
|
1116
|
+
* and from the bot's point of view it is online while receiving nothing.
|
|
1117
|
+
*
|
|
1118
|
+
* Case 2 is why bots are traditionally torn down and restarted on a timer:
|
|
1119
|
+
* there was no way to tell a healthy quiet socket from a dead one. This module
|
|
1120
|
+
* provides that signal, so the socket can stay up indefinitely and only ever be
|
|
1121
|
+
* recycled when it is *actually* broken.
|
|
1122
|
+
*
|
|
1123
|
+
* The detector is deliberately conservative — real accounts genuinely go quiet
|
|
1124
|
+
* for long stretches, so it only acts when the socket has been silent for a
|
|
1125
|
+
* long window AND an active ping fails to elicit any traffic.
|
|
1126
|
+
*/
|
|
1127
|
+
/** No traffic and no pong for this long => the socket is suspect. */
|
|
1128
|
+
declare const SILENT_WINDOW_MS: number;
|
|
1129
|
+
/** How long to wait for traffic after an active ping before declaring death. */
|
|
1130
|
+
declare const PING_GRACE_MS: number;
|
|
1131
|
+
interface RealtimeHealthState {
|
|
1132
|
+
/** Whether the transport currently reports itself connected. */
|
|
1133
|
+
connected: boolean;
|
|
1134
|
+
/** ms since the last inbound frame (or since connect when none seen yet). */
|
|
1135
|
+
sinceLastFrameMs: number;
|
|
1136
|
+
/** Total inbound frames observed. */
|
|
1137
|
+
frames: number;
|
|
1138
|
+
/** Completed health probes. */
|
|
1139
|
+
probes: number;
|
|
1140
|
+
/** Times the health check forced a reconnect. */
|
|
1141
|
+
forcedReconnects: number;
|
|
1142
|
+
/** The last determined verdict. */
|
|
1143
|
+
verdict: "healthy" | "quiet" | "suspect" | "dead" | "disconnected";
|
|
1144
|
+
}
|
|
1145
|
+
/**
|
|
1146
|
+
* Tracks liveness of one realtime connection. Instances are cheap and are
|
|
1147
|
+
* recreated per connection generation by the listener.
|
|
1148
|
+
*/
|
|
1149
|
+
declare class RealtimeHealth {
|
|
1150
|
+
private now;
|
|
1151
|
+
private frames;
|
|
1152
|
+
private lastFrameAt;
|
|
1153
|
+
private connectedAt;
|
|
1154
|
+
private probes;
|
|
1155
|
+
private forcedReconnects;
|
|
1156
|
+
private lastPingAt;
|
|
1157
|
+
private startedAt;
|
|
1158
|
+
constructor(now?: () => number);
|
|
1159
|
+
/** Record any inbound frame (message, pong, anything from the broker). */
|
|
1160
|
+
noteFrame(): void;
|
|
1161
|
+
/** Record a successful connect. */
|
|
1162
|
+
noteConnected(): void;
|
|
1163
|
+
/** Record that the transport reports itself disconnected. */
|
|
1164
|
+
noteDisconnected(): void;
|
|
1165
|
+
/** Record an active ping being sent. */
|
|
1166
|
+
notePing(): void;
|
|
1167
|
+
sinceLastFrame(): number;
|
|
1168
|
+
/**
|
|
1169
|
+
* Decide what to do about the current socket.
|
|
1170
|
+
*
|
|
1171
|
+
* - `disconnected` — the transport already knows it is down; reconnect.
|
|
1172
|
+
* - `dead` — connected on paper, silent past the window, and a ping raised no
|
|
1173
|
+
* traffic within the grace period; reconnect.
|
|
1174
|
+
* - `suspect` — silent past the window but a ping was only just sent; wait.
|
|
1175
|
+
* - `quiet` — silent but inside the window; normal.
|
|
1176
|
+
* - `healthy` — recent traffic.
|
|
1177
|
+
*/
|
|
1178
|
+
verdict(connected: boolean): RealtimeHealthState["verdict"];
|
|
1179
|
+
/** A snapshot for diagnostics. */
|
|
1180
|
+
state(connected: boolean): RealtimeHealthState;
|
|
1181
|
+
noteProbe(): void;
|
|
1182
|
+
noteForcedReconnect(): void;
|
|
1183
|
+
}
|
|
1184
|
+
interface RealtimeSupervisorOptions {
|
|
1185
|
+
/** How often to evaluate the socket. Jittered. Default 3 min. */
|
|
1186
|
+
intervalMs?: number;
|
|
1187
|
+
/** Ask the transport to reconnect (fresh socket) — must be idempotent. */
|
|
1188
|
+
reconnect: (reason: string) => void;
|
|
1189
|
+
/** Send an MQTT ping. Return false if it could not be sent. */
|
|
1190
|
+
ping?: () => boolean;
|
|
1191
|
+
/** Whether the transport currently reports connected. */
|
|
1192
|
+
isConnected: () => boolean;
|
|
1193
|
+
/** Observe verdict changes (logging). */
|
|
1194
|
+
onVerdict?: (state: RealtimeHealthState) => void;
|
|
1195
|
+
}
|
|
1196
|
+
/**
|
|
1197
|
+
* A lightweight supervisor that keeps ONE realtime socket honest: it pings a
|
|
1198
|
+
* long-quiet socket and recycles only a genuinely dead one. It never restarts a
|
|
1199
|
+
* socket that is simply idle, which is what lets the connection live for days.
|
|
1200
|
+
*/
|
|
1201
|
+
declare function startRealtimeSupervisor(options: RealtimeSupervisorOptions): {
|
|
1202
|
+
health: RealtimeHealth;
|
|
1203
|
+
stop: () => void;
|
|
1204
|
+
checkNow: () => RealtimeHealthState;
|
|
1205
|
+
};
|
|
1206
|
+
|
|
938
1207
|
/** Extract the `CurrentUserInitialData` object from a profile HTML document. */
|
|
939
1208
|
declare function extractCurrentUser(html: any): Record<string, any> | null;
|
|
940
1209
|
|
|
1210
|
+
/**
|
|
1211
|
+
* Reusable cookie acquisition.
|
|
1212
|
+
*
|
|
1213
|
+
* `loginHelper` needs this to build the initial jar, and the recovery
|
|
1214
|
+
* supervisor needs the exact same logic to *re-login* a dead session from
|
|
1215
|
+
* stored credentials. Keeping a single implementation means a fix to cookie
|
|
1216
|
+
* handling (domain normalization, Set-Cookie parsing, appState formats) can
|
|
1217
|
+
* never apply to login but not to recovery.
|
|
1218
|
+
*
|
|
1219
|
+
* The function is deliberately narrow: it mutates a cookie jar and reports
|
|
1220
|
+
* what it loaded. It performs no session validation and starts nothing.
|
|
1221
|
+
*/
|
|
1222
|
+
|
|
1223
|
+
interface AcquireResult {
|
|
1224
|
+
/** How cookies were obtained. */
|
|
1225
|
+
source: "appState" | "credentials" | "appStateString";
|
|
1226
|
+
/** Number of cookies written into the jar. */
|
|
1227
|
+
loaded: number;
|
|
1228
|
+
}
|
|
1229
|
+
/**
|
|
1230
|
+
* Acquire session cookies into `jar`.
|
|
1231
|
+
*
|
|
1232
|
+
* Accepts an appState array, a `;`-joined cookie string, or email+password
|
|
1233
|
+
* credentials. Throws only when *no* usable source was provided — an individual
|
|
1234
|
+
* bad cookie never fails the whole load.
|
|
1235
|
+
*/
|
|
1236
|
+
declare function acquireCookies(jar: any, credentials: LoginCredentials$1, globalOptions: LoginOptions$1): Promise<AcquireResult>;
|
|
1237
|
+
|
|
941
1238
|
type ApiContext = ApiContext$1;
|
|
942
1239
|
type ApiInternal = Api;
|
|
943
1240
|
type DefaultFuncs = DefaultFuncs$1;
|
|
@@ -948,4 +1245,4 @@ declare const _default: typeof login$1 & {
|
|
|
948
1245
|
default: typeof login$1;
|
|
949
1246
|
};
|
|
950
1247
|
|
|
951
|
-
export { type API, type AddedStickerPackInfo, type AiTheme, type AnyAttachment, type ApiContext, type ApiInternal, type Attachment, type AudioAttachment, type BrowserProfile, type Callback, type CommentMessage, type CommentResult, type Coordinates, type DefaultFuncs, type EmojiEvent, type Event, type FileAttachment, type GroupNameEvent, type ListenEvent, type LivenessHandle, type LivenessState, type LoginCredentials, type LoginOptions, type Mention, type Message, type MessageID, type MessageObject, type MessageReply, type MusicTag, type MusicTrack, type NicknameEvent, type PhotoAttachment, type Reaction, type SearchMusicOptions, type SearchMusicResult, type ShareAttachment, type ShareResult, type StickerAttachment, type StickerInfo, type StickerPackInfo, type ThreadID, type ThreadInfo, type ThreadThemeEvent, type TypingIndicator, type UnsendMessageEvent, type UserID, type UserInfo, type VideoAttachment, type WarmUpResult, applySetCookie, awaitProactiveSlot, canActProactively, collectSessionCookies, _default as default, defaultBrowserProfile, ensureCometTokens, extractCookies, extractCookiesFromBody, extractCookiesFromJar, extractCookiesFromSetCookie, extractCurrentUser, getFingerprintParams, getHeaders, getWebSocketHeaders, hasAuthenticatedSession, humanDelay, invalidateCometTokens, isAuthFailure, isBreakerOpen, isStaleTokenError, login$1 as login, missingSessionCookies, normalizeBrowserProfile, noteAction, noteAuthFailure, noteCheckpoint, noteRateLimit, paceRequest, publicTypes, randomUserAgent, refreshSessionCookies, repairSession, reserveRequest, resetBreaker, resolveProfile, retryDelay, safetySnapshot, sleep, startLiveness, tripBreaker, warmUpSession, withCometTokens, withSessionRetry };
|
|
1248
|
+
export { type API, type AddedStickerPackInfo, type AiTheme, type AnyAttachment, type ApiContext, type ApiInternal, type Attachment, type AudioAttachment, type BrowserProfile, type Callback, type CommentMessage, type CommentResult, type Coordinates, type DefaultFuncs, type EmojiEvent, type Event, type FileAttachment, type GroupNameEvent, type ListenEvent, type LivenessHandle, type LivenessState, type LoginCredentials, type LoginOptions, type Mention, type Message, type MessageID, type MessageObject, type MessageReply, type MusicTag, type MusicTrack, type NicknameEvent, PING_GRACE_MS, type PhotoAttachment, type Reaction, RealtimeHealth, type RealtimeHealthState, type RecoveryEvent, type RecoveryOptions, type RecoveryRuntime, type RecoveryState, RecoverySupervisor, SILENT_WINDOW_MS, type SearchMusicOptions, type SearchMusicResult, type ShareAttachment, type ShareResult, type StickerAttachment, type StickerInfo, type StickerPackInfo, type ThreadID, type ThreadInfo, type ThreadThemeEvent, type TypingIndicator, type UnsendMessageEvent, type UserID, type UserInfo, type VideoAttachment, type WarmUpResult, acquireCookies, applySetCookie, awaitProactiveSlot, canActProactively, collectSessionCookies, _default as default, defaultBrowserProfile, ensureCometTokens, extractCookies, extractCookiesFromBody, extractCookiesFromJar, extractCookiesFromSetCookie, extractCurrentUser, getFingerprintParams, getHeaders, getWebSocketHeaders, hasAuthenticatedSession, humanDelay, invalidateCometTokens, isAuthFailure, isBreakerOpen, isLoggedOutDocument, isStaleTokenError, login$1 as login, missingSessionCookies, normalizeBrowserProfile, noteAction, noteAuthFailure, noteCheckpoint, noteRateLimit, paceRequest, publicTypes, randomUserAgent, refreshSessionCookies, repairSession, reserveRequest, resetBreaker, resolveProfile, retryDelay, safetySnapshot, sleep, startLiveness, startRealtimeSupervisor, startRecovery, tripBreaker, warmUpSession, withCometTokens, withSessionRetry };
|
package/dist/index.d.ts
CHANGED
|
@@ -99,6 +99,19 @@ interface LoginOptions$1 {
|
|
|
99
99
|
warmUp?: boolean;
|
|
100
100
|
/** Alternate foreground/background on a human rhythm to avoid detection. */
|
|
101
101
|
liveness?: boolean;
|
|
102
|
+
/**
|
|
103
|
+
* Auto-recovery supervisor. `true` (default) keeps a dead session alive by
|
|
104
|
+
* refreshing cookies and, when credentials are available, re-logging in.
|
|
105
|
+
* Pass an options object to tune it, or `false` to disable it.
|
|
106
|
+
*/
|
|
107
|
+
recovery?: boolean | {
|
|
108
|
+
intervalMs?: number;
|
|
109
|
+
failureThreshold?: number;
|
|
110
|
+
maxRecoveries?: number;
|
|
111
|
+
noRelogin?: boolean;
|
|
112
|
+
};
|
|
113
|
+
/** Write a refreshed appState here on successful recovery (best-effort). */
|
|
114
|
+
appStatePath?: string;
|
|
102
115
|
logging?: boolean;
|
|
103
116
|
pageID?: string;
|
|
104
117
|
[key: string]: any;
|
|
@@ -132,6 +145,8 @@ interface ApiContext$1 {
|
|
|
132
145
|
fb_dtsg?: string;
|
|
133
146
|
jazoest?: string;
|
|
134
147
|
lsd?: string;
|
|
148
|
+
/** App-scoped abuse id (`x-asbd-id`) scraped from the current document. */
|
|
149
|
+
asbdId?: string;
|
|
135
150
|
ttstamp?: string;
|
|
136
151
|
threadTypeCache?: Record<string, boolean>;
|
|
137
152
|
lastMessageTime?: number;
|
|
@@ -720,6 +735,16 @@ declare function refreshSessionCookies(ctx: any, defaultFuncs: any, options?: {
|
|
|
720
735
|
}>;
|
|
721
736
|
/** Detect an auth/document-token failure in a thrown or returned payload. */
|
|
722
737
|
declare function isAuthFailure(value: any): boolean;
|
|
738
|
+
/**
|
|
739
|
+
* Detect a logged-out document.
|
|
740
|
+
*
|
|
741
|
+
* When Facebook invalidates a session it does not always answer with an auth
|
|
742
|
+
* error code: it serves the login page, or (when the browser/device is still
|
|
743
|
+
* trusted) the one-tap "Continue as <name>" interstitial. Both look like a
|
|
744
|
+
* normal `200 text/html`, so a session can silently become useless. Recognising
|
|
745
|
+
* the document lets the caller re-authenticate instead of limping along.
|
|
746
|
+
*/
|
|
747
|
+
declare function isLoggedOutDocument(html: any): boolean;
|
|
723
748
|
/**
|
|
724
749
|
* Wrap a network operation with one repair-and-retry pass.
|
|
725
750
|
*
|
|
@@ -935,9 +960,281 @@ declare function canActProactively(ctx: any): boolean;
|
|
|
935
960
|
/** Wait until the session is foregrounded and the breaker is closed. */
|
|
936
961
|
declare function awaitProactiveSlot(ctx: any, maxWaitMs?: number): Promise<boolean>;
|
|
937
962
|
|
|
963
|
+
/**
|
|
964
|
+
* Long-run auto-recovery ("set and forget").
|
|
965
|
+
*
|
|
966
|
+
* The pieces that keep a session alive already exist and are individually
|
|
967
|
+
* correct: cookie refresh, Comet-token invalidation, the account-safety
|
|
968
|
+
* breaker, MQTT reconnect backoff, human pacing. What was missing was the
|
|
969
|
+
* *supervisor* that ties them together — something that notices when the
|
|
970
|
+
* session has quietly died and does the one useful thing that remains.
|
|
971
|
+
*
|
|
972
|
+
* This module adds that supervisor and is deliberately conservative:
|
|
973
|
+
*
|
|
974
|
+
* - It samples health on a slow, jittered cadence (never a hot loop).
|
|
975
|
+
* - It first tries the cheap fix: refresh cookies + tokens.
|
|
976
|
+
* - Only if the session is still unauthenticated *and* credentials were
|
|
977
|
+
* supplied does it attempt a real credential re-login.
|
|
978
|
+
* - Every recovery attempt is gated by the safety breaker and a per-session
|
|
979
|
+
* attempt budget with exponential backoff, so a genuinely dead account
|
|
980
|
+
* cannot turn into a login storm that gets it checkpointed.
|
|
981
|
+
* - It reports every state change through a callback, so a bot can log or
|
|
982
|
+
* alert instead of failing silently.
|
|
983
|
+
*
|
|
984
|
+
* Everything here is pure logic over an injected "runtime" — the network work
|
|
985
|
+
* is delegated to callbacks the caller supplies. That keeps the module
|
|
986
|
+
* unit-testable without any network, and means a bug here can only ever
|
|
987
|
+
* *skip* a recovery, never corrupt a live request.
|
|
988
|
+
*/
|
|
989
|
+
|
|
990
|
+
type RecoveryPhase = "healthy" | "refreshing" | "recovering" | "relogin" | "cooling" | "stopped";
|
|
991
|
+
interface RecoveryState {
|
|
992
|
+
phase: RecoveryPhase;
|
|
993
|
+
/** Total health samples taken. */
|
|
994
|
+
checks: number;
|
|
995
|
+
/** Consecutive unhealthy samples. */
|
|
996
|
+
consecutiveFailures: number;
|
|
997
|
+
/** Successful auto-heals (cookie refresh or repair) that restored auth. */
|
|
998
|
+
healCount: number;
|
|
999
|
+
/** Total recovery attempts made (successful or not), for the budget. */
|
|
1000
|
+
attempts: number;
|
|
1001
|
+
/** Successful full re-logins. */
|
|
1002
|
+
reloginCount: number;
|
|
1003
|
+
/** Recoveries abandoned because the budget/breaker said stop. */
|
|
1004
|
+
givenUp: number;
|
|
1005
|
+
/** ms timestamp of the last successful health confirmation. */
|
|
1006
|
+
lastHealthyAt: number;
|
|
1007
|
+
/** ms until the next scheduled check. */
|
|
1008
|
+
nextCheckMs: number;
|
|
1009
|
+
/** Whether a credential re-login is possible for this session. */
|
|
1010
|
+
canRelogin: boolean;
|
|
1011
|
+
/** Last human-readable detail. */
|
|
1012
|
+
lastDetail?: string;
|
|
1013
|
+
}
|
|
1014
|
+
/**
|
|
1015
|
+
* The dependency surface the supervisor needs. All network work is behind
|
|
1016
|
+
* these callbacks so the supervisor itself stays pure and testable.
|
|
1017
|
+
*/
|
|
1018
|
+
interface RecoveryRuntime {
|
|
1019
|
+
ctx: any;
|
|
1020
|
+
/** True when the jar still holds a usable session. Cheap, synchronous. */
|
|
1021
|
+
isAuthenticated: () => boolean;
|
|
1022
|
+
/**
|
|
1023
|
+
* Active server-side liveness check. Distinct from {@link isAuthenticated}:
|
|
1024
|
+
* the jar can still look authenticated locally while Facebook has already
|
|
1025
|
+
* invalidated the session server-side (the "Continue as <name>" page). When
|
|
1026
|
+
* provided, the supervisor trusts this over the cheap local check.
|
|
1027
|
+
*/
|
|
1028
|
+
probeLiveness?: () => Promise<boolean>;
|
|
1029
|
+
/** Refresh cookies/tokens in place; resolves to whether auth survived. */
|
|
1030
|
+
refresh: () => Promise<boolean>;
|
|
1031
|
+
/**
|
|
1032
|
+
* Full credential re-login. Must resolve to a fresh appState (array) on
|
|
1033
|
+
* success, or null/throw on failure. Optional: when absent, no re-login is
|
|
1034
|
+
* attempted and the supervisor only reports the dead session.
|
|
1035
|
+
*/
|
|
1036
|
+
relogin?: () => Promise<any[] | null>;
|
|
1037
|
+
/**
|
|
1038
|
+
* Whether a credential re-login is actually possible for this session (e.g.
|
|
1039
|
+
* email+password were supplied). Defaults to whether `relogin` is set. This
|
|
1040
|
+
* exists because `relogin` is always provided by the login plumbing, but it
|
|
1041
|
+
* is only *useful* when the caller had credentials.
|
|
1042
|
+
*/
|
|
1043
|
+
canRelogin?: boolean;
|
|
1044
|
+
/** Persist a refreshed appState (e.g. write the account file). Optional. */
|
|
1045
|
+
persistAppState?: (appState: any[]) => void;
|
|
1046
|
+
/** Restart the MQTT listener after a recovery. Optional. */
|
|
1047
|
+
restartMqtt?: () => void;
|
|
1048
|
+
/** Observe state transitions (logging/alerting). Optional. */
|
|
1049
|
+
onEvent?: (event: RecoveryEvent) => void;
|
|
1050
|
+
}
|
|
1051
|
+
interface RecoveryEvent {
|
|
1052
|
+
type: "check" | "unhealthy" | "healed" | "relogin" | "gave-up" | "cooling" | "stopped";
|
|
1053
|
+
detail: string;
|
|
1054
|
+
state: RecoveryState;
|
|
1055
|
+
}
|
|
1056
|
+
interface RecoveryOptions {
|
|
1057
|
+
/** Base interval between health samples. Jittered ±30% each tick. */
|
|
1058
|
+
intervalMs?: number;
|
|
1059
|
+
/** Consecutive failed checks before the supervisor intervenes. */
|
|
1060
|
+
failureThreshold?: number;
|
|
1061
|
+
/** Max automatic recoveries (heal+relogin) before the supervisor gives up. */
|
|
1062
|
+
maxRecoveries?: number;
|
|
1063
|
+
/** Disable credential re-login even when credentials exist. */
|
|
1064
|
+
noRelogin?: boolean;
|
|
1065
|
+
}
|
|
1066
|
+
/**
|
|
1067
|
+
* The supervisor. Create with {@link startRecovery} rather than directly.
|
|
1068
|
+
*/
|
|
1069
|
+
declare class RecoverySupervisor {
|
|
1070
|
+
private runtime;
|
|
1071
|
+
private opts;
|
|
1072
|
+
private timer;
|
|
1073
|
+
private stopped;
|
|
1074
|
+
private running;
|
|
1075
|
+
private state;
|
|
1076
|
+
private backoffMs;
|
|
1077
|
+
constructor(runtime: RecoveryRuntime, options?: RecoveryOptions);
|
|
1078
|
+
/** A point-in-time copy of the supervisor state. */
|
|
1079
|
+
snapshot(): RecoveryState;
|
|
1080
|
+
isRunning(): boolean;
|
|
1081
|
+
/** Start the supervisor. Idempotent. */
|
|
1082
|
+
start(): void;
|
|
1083
|
+
/** Stop the supervisor and clear any pending timer. */
|
|
1084
|
+
stop(): void;
|
|
1085
|
+
/**
|
|
1086
|
+
* Run one health check immediately (used by tests and by `checkNow()`).
|
|
1087
|
+
* Returns the resulting state. Never throws.
|
|
1088
|
+
*/
|
|
1089
|
+
checkNow(): Promise<RecoveryState>;
|
|
1090
|
+
private nextCheckAt;
|
|
1091
|
+
private startTimer;
|
|
1092
|
+
private runOnce;
|
|
1093
|
+
private markHealthy;
|
|
1094
|
+
private afterRecovery;
|
|
1095
|
+
private emit;
|
|
1096
|
+
}
|
|
1097
|
+
/**
|
|
1098
|
+
* Wire a supervisor to a live session.
|
|
1099
|
+
*
|
|
1100
|
+
* @param ctx session context (mutated with `__recovery`)
|
|
1101
|
+
* @param runtime the injected dependency surface
|
|
1102
|
+
* @param options tuning knobs
|
|
1103
|
+
*/
|
|
1104
|
+
declare function startRecovery(ctx: any, runtime: RecoveryRuntime, options?: RecoveryOptions): RecoverySupervisor;
|
|
1105
|
+
|
|
1106
|
+
/**
|
|
1107
|
+
* MQTT connection health.
|
|
1108
|
+
*
|
|
1109
|
+
* A Messenger realtime socket fails in two ways:
|
|
1110
|
+
*
|
|
1111
|
+
* 1. **Loudly** — TCP drop, broker close, auth error. mqtt.js raises `close`
|
|
1112
|
+
* and the listener reconnects. Easy.
|
|
1113
|
+
* 2. **Silently** — the socket stays "connected" but Facebook has stopped
|
|
1114
|
+
* routing events to it (a half-open connection, a broker-side idle reap, a
|
|
1115
|
+
* revoked session). No error is emitted, `client.connected` stays `true`,
|
|
1116
|
+
* and from the bot's point of view it is online while receiving nothing.
|
|
1117
|
+
*
|
|
1118
|
+
* Case 2 is why bots are traditionally torn down and restarted on a timer:
|
|
1119
|
+
* there was no way to tell a healthy quiet socket from a dead one. This module
|
|
1120
|
+
* provides that signal, so the socket can stay up indefinitely and only ever be
|
|
1121
|
+
* recycled when it is *actually* broken.
|
|
1122
|
+
*
|
|
1123
|
+
* The detector is deliberately conservative — real accounts genuinely go quiet
|
|
1124
|
+
* for long stretches, so it only acts when the socket has been silent for a
|
|
1125
|
+
* long window AND an active ping fails to elicit any traffic.
|
|
1126
|
+
*/
|
|
1127
|
+
/** No traffic and no pong for this long => the socket is suspect. */
|
|
1128
|
+
declare const SILENT_WINDOW_MS: number;
|
|
1129
|
+
/** How long to wait for traffic after an active ping before declaring death. */
|
|
1130
|
+
declare const PING_GRACE_MS: number;
|
|
1131
|
+
interface RealtimeHealthState {
|
|
1132
|
+
/** Whether the transport currently reports itself connected. */
|
|
1133
|
+
connected: boolean;
|
|
1134
|
+
/** ms since the last inbound frame (or since connect when none seen yet). */
|
|
1135
|
+
sinceLastFrameMs: number;
|
|
1136
|
+
/** Total inbound frames observed. */
|
|
1137
|
+
frames: number;
|
|
1138
|
+
/** Completed health probes. */
|
|
1139
|
+
probes: number;
|
|
1140
|
+
/** Times the health check forced a reconnect. */
|
|
1141
|
+
forcedReconnects: number;
|
|
1142
|
+
/** The last determined verdict. */
|
|
1143
|
+
verdict: "healthy" | "quiet" | "suspect" | "dead" | "disconnected";
|
|
1144
|
+
}
|
|
1145
|
+
/**
|
|
1146
|
+
* Tracks liveness of one realtime connection. Instances are cheap and are
|
|
1147
|
+
* recreated per connection generation by the listener.
|
|
1148
|
+
*/
|
|
1149
|
+
declare class RealtimeHealth {
|
|
1150
|
+
private now;
|
|
1151
|
+
private frames;
|
|
1152
|
+
private lastFrameAt;
|
|
1153
|
+
private connectedAt;
|
|
1154
|
+
private probes;
|
|
1155
|
+
private forcedReconnects;
|
|
1156
|
+
private lastPingAt;
|
|
1157
|
+
private startedAt;
|
|
1158
|
+
constructor(now?: () => number);
|
|
1159
|
+
/** Record any inbound frame (message, pong, anything from the broker). */
|
|
1160
|
+
noteFrame(): void;
|
|
1161
|
+
/** Record a successful connect. */
|
|
1162
|
+
noteConnected(): void;
|
|
1163
|
+
/** Record that the transport reports itself disconnected. */
|
|
1164
|
+
noteDisconnected(): void;
|
|
1165
|
+
/** Record an active ping being sent. */
|
|
1166
|
+
notePing(): void;
|
|
1167
|
+
sinceLastFrame(): number;
|
|
1168
|
+
/**
|
|
1169
|
+
* Decide what to do about the current socket.
|
|
1170
|
+
*
|
|
1171
|
+
* - `disconnected` — the transport already knows it is down; reconnect.
|
|
1172
|
+
* - `dead` — connected on paper, silent past the window, and a ping raised no
|
|
1173
|
+
* traffic within the grace period; reconnect.
|
|
1174
|
+
* - `suspect` — silent past the window but a ping was only just sent; wait.
|
|
1175
|
+
* - `quiet` — silent but inside the window; normal.
|
|
1176
|
+
* - `healthy` — recent traffic.
|
|
1177
|
+
*/
|
|
1178
|
+
verdict(connected: boolean): RealtimeHealthState["verdict"];
|
|
1179
|
+
/** A snapshot for diagnostics. */
|
|
1180
|
+
state(connected: boolean): RealtimeHealthState;
|
|
1181
|
+
noteProbe(): void;
|
|
1182
|
+
noteForcedReconnect(): void;
|
|
1183
|
+
}
|
|
1184
|
+
interface RealtimeSupervisorOptions {
|
|
1185
|
+
/** How often to evaluate the socket. Jittered. Default 3 min. */
|
|
1186
|
+
intervalMs?: number;
|
|
1187
|
+
/** Ask the transport to reconnect (fresh socket) — must be idempotent. */
|
|
1188
|
+
reconnect: (reason: string) => void;
|
|
1189
|
+
/** Send an MQTT ping. Return false if it could not be sent. */
|
|
1190
|
+
ping?: () => boolean;
|
|
1191
|
+
/** Whether the transport currently reports connected. */
|
|
1192
|
+
isConnected: () => boolean;
|
|
1193
|
+
/** Observe verdict changes (logging). */
|
|
1194
|
+
onVerdict?: (state: RealtimeHealthState) => void;
|
|
1195
|
+
}
|
|
1196
|
+
/**
|
|
1197
|
+
* A lightweight supervisor that keeps ONE realtime socket honest: it pings a
|
|
1198
|
+
* long-quiet socket and recycles only a genuinely dead one. It never restarts a
|
|
1199
|
+
* socket that is simply idle, which is what lets the connection live for days.
|
|
1200
|
+
*/
|
|
1201
|
+
declare function startRealtimeSupervisor(options: RealtimeSupervisorOptions): {
|
|
1202
|
+
health: RealtimeHealth;
|
|
1203
|
+
stop: () => void;
|
|
1204
|
+
checkNow: () => RealtimeHealthState;
|
|
1205
|
+
};
|
|
1206
|
+
|
|
938
1207
|
/** Extract the `CurrentUserInitialData` object from a profile HTML document. */
|
|
939
1208
|
declare function extractCurrentUser(html: any): Record<string, any> | null;
|
|
940
1209
|
|
|
1210
|
+
/**
|
|
1211
|
+
* Reusable cookie acquisition.
|
|
1212
|
+
*
|
|
1213
|
+
* `loginHelper` needs this to build the initial jar, and the recovery
|
|
1214
|
+
* supervisor needs the exact same logic to *re-login* a dead session from
|
|
1215
|
+
* stored credentials. Keeping a single implementation means a fix to cookie
|
|
1216
|
+
* handling (domain normalization, Set-Cookie parsing, appState formats) can
|
|
1217
|
+
* never apply to login but not to recovery.
|
|
1218
|
+
*
|
|
1219
|
+
* The function is deliberately narrow: it mutates a cookie jar and reports
|
|
1220
|
+
* what it loaded. It performs no session validation and starts nothing.
|
|
1221
|
+
*/
|
|
1222
|
+
|
|
1223
|
+
interface AcquireResult {
|
|
1224
|
+
/** How cookies were obtained. */
|
|
1225
|
+
source: "appState" | "credentials" | "appStateString";
|
|
1226
|
+
/** Number of cookies written into the jar. */
|
|
1227
|
+
loaded: number;
|
|
1228
|
+
}
|
|
1229
|
+
/**
|
|
1230
|
+
* Acquire session cookies into `jar`.
|
|
1231
|
+
*
|
|
1232
|
+
* Accepts an appState array, a `;`-joined cookie string, or email+password
|
|
1233
|
+
* credentials. Throws only when *no* usable source was provided — an individual
|
|
1234
|
+
* bad cookie never fails the whole load.
|
|
1235
|
+
*/
|
|
1236
|
+
declare function acquireCookies(jar: any, credentials: LoginCredentials$1, globalOptions: LoginOptions$1): Promise<AcquireResult>;
|
|
1237
|
+
|
|
941
1238
|
type ApiContext = ApiContext$1;
|
|
942
1239
|
type ApiInternal = Api;
|
|
943
1240
|
type DefaultFuncs = DefaultFuncs$1;
|
|
@@ -948,4 +1245,4 @@ declare const _default: typeof login$1 & {
|
|
|
948
1245
|
default: typeof login$1;
|
|
949
1246
|
};
|
|
950
1247
|
|
|
951
|
-
export { type API, type AddedStickerPackInfo, type AiTheme, type AnyAttachment, type ApiContext, type ApiInternal, type Attachment, type AudioAttachment, type BrowserProfile, type Callback, type CommentMessage, type CommentResult, type Coordinates, type DefaultFuncs, type EmojiEvent, type Event, type FileAttachment, type GroupNameEvent, type ListenEvent, type LivenessHandle, type LivenessState, type LoginCredentials, type LoginOptions, type Mention, type Message, type MessageID, type MessageObject, type MessageReply, type MusicTag, type MusicTrack, type NicknameEvent, type PhotoAttachment, type Reaction, type SearchMusicOptions, type SearchMusicResult, type ShareAttachment, type ShareResult, type StickerAttachment, type StickerInfo, type StickerPackInfo, type ThreadID, type ThreadInfo, type ThreadThemeEvent, type TypingIndicator, type UnsendMessageEvent, type UserID, type UserInfo, type VideoAttachment, type WarmUpResult, applySetCookie, awaitProactiveSlot, canActProactively, collectSessionCookies, _default as default, defaultBrowserProfile, ensureCometTokens, extractCookies, extractCookiesFromBody, extractCookiesFromJar, extractCookiesFromSetCookie, extractCurrentUser, getFingerprintParams, getHeaders, getWebSocketHeaders, hasAuthenticatedSession, humanDelay, invalidateCometTokens, isAuthFailure, isBreakerOpen, isStaleTokenError, login$1 as login, missingSessionCookies, normalizeBrowserProfile, noteAction, noteAuthFailure, noteCheckpoint, noteRateLimit, paceRequest, publicTypes, randomUserAgent, refreshSessionCookies, repairSession, reserveRequest, resetBreaker, resolveProfile, retryDelay, safetySnapshot, sleep, startLiveness, tripBreaker, warmUpSession, withCometTokens, withSessionRetry };
|
|
1248
|
+
export { type API, type AddedStickerPackInfo, type AiTheme, type AnyAttachment, type ApiContext, type ApiInternal, type Attachment, type AudioAttachment, type BrowserProfile, type Callback, type CommentMessage, type CommentResult, type Coordinates, type DefaultFuncs, type EmojiEvent, type Event, type FileAttachment, type GroupNameEvent, type ListenEvent, type LivenessHandle, type LivenessState, type LoginCredentials, type LoginOptions, type Mention, type Message, type MessageID, type MessageObject, type MessageReply, type MusicTag, type MusicTrack, type NicknameEvent, PING_GRACE_MS, type PhotoAttachment, type Reaction, RealtimeHealth, type RealtimeHealthState, type RecoveryEvent, type RecoveryOptions, type RecoveryRuntime, type RecoveryState, RecoverySupervisor, SILENT_WINDOW_MS, type SearchMusicOptions, type SearchMusicResult, type ShareAttachment, type ShareResult, type StickerAttachment, type StickerInfo, type StickerPackInfo, type ThreadID, type ThreadInfo, type ThreadThemeEvent, type TypingIndicator, type UnsendMessageEvent, type UserID, type UserInfo, type VideoAttachment, type WarmUpResult, acquireCookies, applySetCookie, awaitProactiveSlot, canActProactively, collectSessionCookies, _default as default, defaultBrowserProfile, ensureCometTokens, extractCookies, extractCookiesFromBody, extractCookiesFromJar, extractCookiesFromSetCookie, extractCurrentUser, getFingerprintParams, getHeaders, getWebSocketHeaders, hasAuthenticatedSession, humanDelay, invalidateCometTokens, isAuthFailure, isBreakerOpen, isLoggedOutDocument, isStaleTokenError, login$1 as login, missingSessionCookies, normalizeBrowserProfile, noteAction, noteAuthFailure, noteCheckpoint, noteRateLimit, paceRequest, publicTypes, randomUserAgent, refreshSessionCookies, repairSession, reserveRequest, resetBreaker, resolveProfile, retryDelay, safetySnapshot, sleep, startLiveness, startRealtimeSupervisor, startRecovery, tripBreaker, warmUpSession, withCometTokens, withSessionRetry };
|