@agentunion/fastaun-browser 0.4.11 → 0.4.13
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 +47 -0
- package/_packed_docs/CHANGELOG.md +47 -0
- package/_packed_docs/sdk/05-E2EE/345/212/240/345/257/206/351/200/232/344/277/241.md +4 -2
- package/_packed_docs/sdk/09-group-rpc-manual.md +80 -37
- package/_packed_docs/sdk/09-message-rpc-manual.md +49 -26
- package/_packed_docs/sdk/09-payload-reference.md +1 -1
- package/_packed_docs/sdk/09-storage-rpc-manual.md +12 -9
- package/dist/auth.d.ts +2 -0
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +98 -9
- package/dist/auth.js.map +1 -1
- package/dist/bundle.js +439 -33
- package/dist/client/delivery.d.ts +9 -0
- package/dist/client/delivery.d.ts.map +1 -1
- package/dist/client/delivery.js +209 -6
- package/dist/client/delivery.js.map +1 -1
- package/dist/client/rpc-pipeline.d.ts.map +1 -1
- package/dist/client/rpc-pipeline.js +10 -4
- package/dist/client/rpc-pipeline.js.map +1 -1
- package/dist/client/v2-e2ee.d.ts.map +1 -1
- package/dist/client/v2-e2ee.js +67 -8
- package/dist/client/v2-e2ee.js.map +1 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +49 -3
- package/dist/client.js.map +1 -1
- package/dist/service-proxy.d.ts.map +1 -1
- package/dist/service-proxy.js +17 -2
- package/dist/service-proxy.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/bundle.js
CHANGED
|
@@ -454,7 +454,7 @@ var init_indexeddb_store = __esm({
|
|
|
454
454
|
});
|
|
455
455
|
|
|
456
456
|
// src/version.ts
|
|
457
|
-
var VERSION = "0.4.
|
|
457
|
+
var VERSION = "0.4.13";
|
|
458
458
|
|
|
459
459
|
// src/types.ts
|
|
460
460
|
var ConnectionState = /* @__PURE__ */ ((ConnectionState2) => {
|
|
@@ -1838,6 +1838,31 @@ var _noopLog4 = { error: () => {
|
|
|
1838
1838
|
}, debug: () => {
|
|
1839
1839
|
} };
|
|
1840
1840
|
var AUN_SDK_LANG = "javascript";
|
|
1841
|
+
var SENSITIVE_RPC_LOG_KEYS = /* @__PURE__ */ new Set([
|
|
1842
|
+
"access_token",
|
|
1843
|
+
"refresh_token",
|
|
1844
|
+
"kite_token",
|
|
1845
|
+
"token"
|
|
1846
|
+
]);
|
|
1847
|
+
function redactRpcLogPayload(value, key = "", depth = 0) {
|
|
1848
|
+
const keyLower = key.toLowerCase();
|
|
1849
|
+
if (SENSITIVE_RPC_LOG_KEYS.has(keyLower) || keyLower.endsWith("_token")) {
|
|
1850
|
+
const text = String(value ?? "");
|
|
1851
|
+
return text ? `<redacted len=${text.length}>` : "";
|
|
1852
|
+
}
|
|
1853
|
+
if (depth >= 6) return "<max-depth>";
|
|
1854
|
+
if (Array.isArray(value)) {
|
|
1855
|
+
return value.map((item) => redactRpcLogPayload(item, "", depth + 1));
|
|
1856
|
+
}
|
|
1857
|
+
if (value && typeof value === "object") {
|
|
1858
|
+
const out = {};
|
|
1859
|
+
for (const [childKey, childValue] of Object.entries(value)) {
|
|
1860
|
+
out[childKey] = redactRpcLogPayload(childValue, childKey, depth + 1);
|
|
1861
|
+
}
|
|
1862
|
+
return out;
|
|
1863
|
+
}
|
|
1864
|
+
return value;
|
|
1865
|
+
}
|
|
1841
1866
|
function splitPemBundle(bundle) {
|
|
1842
1867
|
const marker = "-----END CERTIFICATE-----";
|
|
1843
1868
|
const certs = [];
|
|
@@ -2401,6 +2426,9 @@ var _AuthFlow = class _AuthFlow {
|
|
|
2401
2426
|
}
|
|
2402
2427
|
} catch (e) {
|
|
2403
2428
|
if (!(e instanceof AuthError)) throw e;
|
|
2429
|
+
if (this._refreshFailureRequiresRelogin(e)) {
|
|
2430
|
+
await this._clearCachedTokens(identity, e.message);
|
|
2431
|
+
}
|
|
2404
2432
|
}
|
|
2405
2433
|
}
|
|
2406
2434
|
const login = await this.authenticate(gatewayUrl, identity.aid);
|
|
@@ -2430,14 +2458,20 @@ var _AuthFlow = class _AuthFlow {
|
|
|
2430
2458
|
this._log.debug(`refreshCachedTokens enter: aid=${identity.aid} gateway=${gatewayUrl}`);
|
|
2431
2459
|
try {
|
|
2432
2460
|
const refreshToken = String(identity.refresh_token ?? "");
|
|
2433
|
-
if (!refreshToken)
|
|
2434
|
-
|
|
2461
|
+
if (!refreshToken) {
|
|
2462
|
+
await this._clearCachedTokens(identity, "missing refresh_token");
|
|
2463
|
+
throw new AuthError("missing refresh_token");
|
|
2464
|
+
}
|
|
2465
|
+
const refreshed = await this._refreshAccessToken(gatewayUrl, refreshToken, identity);
|
|
2435
2466
|
this._rememberTokens(identity, refreshed);
|
|
2436
2467
|
await this._validateNewCert(identity, gatewayUrl);
|
|
2437
2468
|
await this._persistIdentity(identity);
|
|
2438
2469
|
this._log.debug(`refreshCachedTokens exit: elapsed=${Date.now() - tStart}ms aid=${identity.aid}`);
|
|
2439
2470
|
return identity;
|
|
2440
2471
|
} catch (err) {
|
|
2472
|
+
if (this._refreshFailureRequiresRelogin(err)) {
|
|
2473
|
+
await this._clearCachedTokens(identity, err instanceof Error ? err.message : String(err));
|
|
2474
|
+
}
|
|
2441
2475
|
this._log.debug(`refreshCachedTokens exit (error): elapsed=${Date.now() - tStart}ms err=${err instanceof Error ? err.message : String(err)}`);
|
|
2442
2476
|
throw err;
|
|
2443
2477
|
}
|
|
@@ -2518,12 +2552,12 @@ var _AuthFlow = class _AuthFlow {
|
|
|
2518
2552
|
method,
|
|
2519
2553
|
params
|
|
2520
2554
|
});
|
|
2521
|
-
this._log.debug(`short RPC request full: ${requestPayload}`);
|
|
2555
|
+
this._log.debug(`short RPC request full: ${JSON.stringify(redactRpcLogPayload(JSON.parse(requestPayload)))}`);
|
|
2522
2556
|
ws.send(requestPayload);
|
|
2523
2557
|
return;
|
|
2524
2558
|
}
|
|
2525
2559
|
globalThis.clearTimeout(timeout);
|
|
2526
|
-
this._log.debug(`short RPC response full: method=${method} ${JSON.stringify(msg)}`);
|
|
2560
|
+
this._log.debug(`short RPC response full: method=${method} ${JSON.stringify(redactRpcLogPayload(msg))}`);
|
|
2527
2561
|
try {
|
|
2528
2562
|
ws.close();
|
|
2529
2563
|
} catch {
|
|
@@ -2538,7 +2572,7 @@ var _AuthFlow = class _AuthFlow {
|
|
|
2538
2572
|
return;
|
|
2539
2573
|
}
|
|
2540
2574
|
if (result.success === false) {
|
|
2541
|
-
reject(new AuthError(String(result.error ?? `${method} failed`)));
|
|
2575
|
+
reject(new AuthError(String(result.error ?? `${method} failed`), { data: result }));
|
|
2542
2576
|
return;
|
|
2543
2577
|
}
|
|
2544
2578
|
resolve(result);
|
|
@@ -2674,12 +2708,28 @@ var _AuthFlow = class _AuthFlow {
|
|
|
2674
2708
|
return phase2;
|
|
2675
2709
|
}
|
|
2676
2710
|
/** 刷新 access token */
|
|
2677
|
-
async _refreshAccessToken(gatewayUrl, refreshToken) {
|
|
2678
|
-
const
|
|
2679
|
-
refresh_token: refreshToken
|
|
2680
|
-
|
|
2711
|
+
async _refreshAccessToken(gatewayUrl, refreshToken, identity) {
|
|
2712
|
+
const params = {
|
|
2713
|
+
refresh_token: refreshToken,
|
|
2714
|
+
sdk_lang: AUN_SDK_LANG,
|
|
2715
|
+
sdk_version: VERSION
|
|
2716
|
+
};
|
|
2717
|
+
if (identity) {
|
|
2718
|
+
const aid = String(identity.aid ?? "").trim();
|
|
2719
|
+
const deviceId = String(identity.device_id ?? "").trim();
|
|
2720
|
+
const slotId = String(identity.slot_id ?? "").trim();
|
|
2721
|
+
const accessToken = String(identity.access_token ?? "").trim();
|
|
2722
|
+
if (aid) params.aid = aid;
|
|
2723
|
+
if (deviceId) params.device_id = deviceId;
|
|
2724
|
+
if (slotId) params.slot_id = slotId;
|
|
2725
|
+
if (accessToken) params.access_token = accessToken;
|
|
2726
|
+
if (typeof identity.access_token_expires_at === "number") {
|
|
2727
|
+
params.access_token_expires_at = identity.access_token_expires_at;
|
|
2728
|
+
}
|
|
2729
|
+
}
|
|
2730
|
+
const result = await this._shortRpc(gatewayUrl, "auth.refresh_token", params);
|
|
2681
2731
|
if (!result.success) {
|
|
2682
|
-
throw new AuthError(String(result.error ?? "refresh failed"));
|
|
2732
|
+
throw new AuthError(String(result.error ?? "refresh failed"), { data: result });
|
|
2683
2733
|
}
|
|
2684
2734
|
return result;
|
|
2685
2735
|
}
|
|
@@ -3012,6 +3062,35 @@ var _AuthFlow = class _AuthFlow {
|
|
|
3012
3062
|
const activeCert = authResult.active_cert;
|
|
3013
3063
|
if (typeof activeCert === "string" && activeCert) identity._pending_active_cert = activeCert;
|
|
3014
3064
|
}
|
|
3065
|
+
_refreshFailureRequiresRelogin(err) {
|
|
3066
|
+
if (!(err instanceof AuthError)) return false;
|
|
3067
|
+
const data = err.data;
|
|
3068
|
+
if (isJsonObject(data)) {
|
|
3069
|
+
if (data.relogin_required === true) return true;
|
|
3070
|
+
const error = String(data.error ?? "").trim().toLowerCase();
|
|
3071
|
+
return ["missing refresh_token", "invalid_or_expired_refresh_token", "refresh not supported"].includes(error);
|
|
3072
|
+
}
|
|
3073
|
+
const message = err.message.trim().toLowerCase();
|
|
3074
|
+
return ["missing refresh_token", "invalid_or_expired_refresh_token", "refresh not supported"].includes(message);
|
|
3075
|
+
}
|
|
3076
|
+
async _clearCachedTokens(identity, reason = "") {
|
|
3077
|
+
const aid = String(identity.aid ?? "");
|
|
3078
|
+
const hadToken = Boolean(
|
|
3079
|
+
identity.access_token || identity.refresh_token || identity.kite_token || identity.token || identity.access_token_expires_at
|
|
3080
|
+
);
|
|
3081
|
+
if (!hadToken) return;
|
|
3082
|
+
identity.access_token = "";
|
|
3083
|
+
identity.refresh_token = "";
|
|
3084
|
+
identity.kite_token = "";
|
|
3085
|
+
identity.token = "";
|
|
3086
|
+
identity.access_token_expires_at = 0;
|
|
3087
|
+
try {
|
|
3088
|
+
await this._persistIdentity(identity);
|
|
3089
|
+
this._log.warn(`cleared cached tokens after refresh failure: aid=${aid} reason=${reason}`);
|
|
3090
|
+
} catch (persistErr) {
|
|
3091
|
+
this._log.warn(`failed to persist token cleanup: aid=${aid} err=${persistErr instanceof Error ? persistErr.message : String(persistErr)}`);
|
|
3092
|
+
}
|
|
3093
|
+
}
|
|
3015
3094
|
/** 验证服务端返回的 new_cert,通过后正式接受 */
|
|
3016
3095
|
async _validateNewCert(identity, gatewayUrl = "") {
|
|
3017
3096
|
const newCertPem = identity._pending_new_cert;
|
|
@@ -3710,6 +3789,52 @@ var ClientRuntime = class {
|
|
|
3710
3789
|
var PUSHED_SEQS_LIMIT = 5e4;
|
|
3711
3790
|
var PENDING_ORDERED_LIMIT = 5e4;
|
|
3712
3791
|
var GROUP_RECALL_SEEN_LIMIT = 1e4;
|
|
3792
|
+
var APP_MESSAGE_ENVELOPE_KEYS = [
|
|
3793
|
+
"module_id",
|
|
3794
|
+
"message_type",
|
|
3795
|
+
"type",
|
|
3796
|
+
"kind",
|
|
3797
|
+
"version",
|
|
3798
|
+
"from",
|
|
3799
|
+
"from_aid",
|
|
3800
|
+
"sender_aid",
|
|
3801
|
+
"to",
|
|
3802
|
+
"to_aid",
|
|
3803
|
+
"group_id",
|
|
3804
|
+
"timestamp",
|
|
3805
|
+
"created_at",
|
|
3806
|
+
"encrypted",
|
|
3807
|
+
"context",
|
|
3808
|
+
"protected_headers",
|
|
3809
|
+
"headers",
|
|
3810
|
+
"payload_type"
|
|
3811
|
+
];
|
|
3812
|
+
var APP_SEND_ENVELOPE_METHODS = /* @__PURE__ */ new Set([
|
|
3813
|
+
"message.send",
|
|
3814
|
+
"group.send",
|
|
3815
|
+
"message.thought.put",
|
|
3816
|
+
"group.thought.put"
|
|
3817
|
+
]);
|
|
3818
|
+
var APP_GROUP_EVENT_ENVELOPE_KEYS = [
|
|
3819
|
+
"module_id",
|
|
3820
|
+
"event_id",
|
|
3821
|
+
"event_seq",
|
|
3822
|
+
"seq",
|
|
3823
|
+
"event_type",
|
|
3824
|
+
"action",
|
|
3825
|
+
"group_id",
|
|
3826
|
+
"actor_aid",
|
|
3827
|
+
"sender_aid",
|
|
3828
|
+
"member_aid",
|
|
3829
|
+
"target_aid",
|
|
3830
|
+
"operator_aid",
|
|
3831
|
+
"created_at",
|
|
3832
|
+
"timestamp",
|
|
3833
|
+
"t_server",
|
|
3834
|
+
"status",
|
|
3835
|
+
"device_id",
|
|
3836
|
+
"slot_id"
|
|
3837
|
+
];
|
|
3713
3838
|
function formatDeliveryError(error) {
|
|
3714
3839
|
return error instanceof Error ? error : String(error);
|
|
3715
3840
|
}
|
|
@@ -3792,8 +3917,131 @@ var MessageDeliveryEngine = class {
|
|
|
3792
3917
|
return result;
|
|
3793
3918
|
}
|
|
3794
3919
|
normalizePublishedMessagePayload(event, payload) {
|
|
3795
|
-
if (
|
|
3796
|
-
|
|
3920
|
+
if (this.isInstanceScopedMessageEvent(event)) {
|
|
3921
|
+
return this.attachAppMessageEnvelope(this.stripInternalSenderDeviceFields(this.attachCurrentInstanceContext(payload)));
|
|
3922
|
+
}
|
|
3923
|
+
if (this.isGroupScopedEvent(event)) {
|
|
3924
|
+
return this.attachAppGroupEventEnvelope(this.attachCurrentInstanceContext(payload));
|
|
3925
|
+
}
|
|
3926
|
+
return payload;
|
|
3927
|
+
}
|
|
3928
|
+
envelopeMetadata(value) {
|
|
3929
|
+
let source = value;
|
|
3930
|
+
if (source && typeof source === "object") {
|
|
3931
|
+
const maybeHeaders = source;
|
|
3932
|
+
if (typeof maybeHeaders.toObject === "function") source = maybeHeaders.toObject();
|
|
3933
|
+
}
|
|
3934
|
+
if (!isJsonObject(source)) return void 0;
|
|
3935
|
+
const out = {};
|
|
3936
|
+
for (const [key, item] of Object.entries(source)) {
|
|
3937
|
+
if (key === "_auth") continue;
|
|
3938
|
+
out[key] = item;
|
|
3939
|
+
}
|
|
3940
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
3941
|
+
}
|
|
3942
|
+
appMessageEnvelope(payload) {
|
|
3943
|
+
if (!isJsonObject(payload)) return {};
|
|
3944
|
+
const message = payload;
|
|
3945
|
+
const body = isJsonObject(message.payload) ? message.payload : {};
|
|
3946
|
+
const envelope = {};
|
|
3947
|
+
const firstValue = (...values) => {
|
|
3948
|
+
for (const value of values) {
|
|
3949
|
+
if (value === void 0 || value === null) continue;
|
|
3950
|
+
if (typeof value === "string" && !value.trim()) continue;
|
|
3951
|
+
return value;
|
|
3952
|
+
}
|
|
3953
|
+
return void 0;
|
|
3954
|
+
};
|
|
3955
|
+
const setIfPresent = (key, value) => {
|
|
3956
|
+
if (value === void 0 || value === null) return;
|
|
3957
|
+
if (typeof value === "string" && !value.trim()) return;
|
|
3958
|
+
envelope[key] = value;
|
|
3959
|
+
};
|
|
3960
|
+
setIfPresent("from", firstValue(message.from, message.from_aid, message.sender_aid));
|
|
3961
|
+
setIfPresent("to", firstValue(message.to, message.to_aid));
|
|
3962
|
+
setIfPresent("group_id", message.group_id);
|
|
3963
|
+
setIfPresent("type", firstValue(body.type, message.type, message.message_type, message.payload_type));
|
|
3964
|
+
setIfPresent("kind", firstValue(body.kind, message.kind));
|
|
3965
|
+
setIfPresent("version", firstValue(body.version, message.version));
|
|
3966
|
+
setIfPresent("timestamp", firstValue(message.timestamp, message.created_at, message.t_server));
|
|
3967
|
+
if ("encrypted" in message) envelope.encrypted = Boolean(message.encrypted);
|
|
3968
|
+
const context = this.envelopeMetadata(message.context);
|
|
3969
|
+
if (context) envelope.context = context;
|
|
3970
|
+
const protectedHeaders = this.envelopeMetadata(message.protected_headers) ?? this.envelopeMetadata(message.headers);
|
|
3971
|
+
if (protectedHeaders) envelope.protected_headers = protectedHeaders;
|
|
3972
|
+
setIfPresent("payload_type", firstValue(message.payload_type, protectedHeaders?.payload_type));
|
|
3973
|
+
return envelope;
|
|
3974
|
+
}
|
|
3975
|
+
isGroupScopedEvent(event) {
|
|
3976
|
+
return event === "group.changed";
|
|
3977
|
+
}
|
|
3978
|
+
appGroupEventEnvelope(payload) {
|
|
3979
|
+
if (!isJsonObject(payload)) return {};
|
|
3980
|
+
const groupEvent = payload;
|
|
3981
|
+
const envelope = {};
|
|
3982
|
+
for (const key of APP_GROUP_EVENT_ENVELOPE_KEYS) {
|
|
3983
|
+
if (Object.prototype.hasOwnProperty.call(groupEvent, key)) envelope[key] = groupEvent[key];
|
|
3984
|
+
}
|
|
3985
|
+
return envelope;
|
|
3986
|
+
}
|
|
3987
|
+
attachAppMessageEnvelope(payload) {
|
|
3988
|
+
if (!isJsonObject(payload)) return payload;
|
|
3989
|
+
const result = { ...payload };
|
|
3990
|
+
result.envelope = this.appMessageEnvelope(result);
|
|
3991
|
+
return result;
|
|
3992
|
+
}
|
|
3993
|
+
sendResultEnvelope(method, params, result, encrypted) {
|
|
3994
|
+
if (!APP_SEND_ENVELOPE_METHODS.has(method)) return {};
|
|
3995
|
+
const body = isJsonObject(params.payload) ? params.payload : {};
|
|
3996
|
+
const resultObj = isJsonObject(result) ? result : {};
|
|
3997
|
+
const envelope = {};
|
|
3998
|
+
const firstValue = (...values) => {
|
|
3999
|
+
for (const value of values) {
|
|
4000
|
+
if (value === void 0 || value === null) continue;
|
|
4001
|
+
if (typeof value === "string" && !value.trim()) continue;
|
|
4002
|
+
return value;
|
|
4003
|
+
}
|
|
4004
|
+
return void 0;
|
|
4005
|
+
};
|
|
4006
|
+
const setIfPresent = (key, value) => {
|
|
4007
|
+
if (value === void 0 || value === null) return;
|
|
4008
|
+
if (typeof value === "string" && !value.trim()) return;
|
|
4009
|
+
envelope[key] = value;
|
|
4010
|
+
};
|
|
4011
|
+
setIfPresent("from", this.runtime.client._aid);
|
|
4012
|
+
if (method.startsWith("message.")) {
|
|
4013
|
+
setIfPresent("to", params.to);
|
|
4014
|
+
} else {
|
|
4015
|
+
setIfPresent("group_id", params.group_id);
|
|
4016
|
+
}
|
|
4017
|
+
setIfPresent("type", firstValue(body.type, params.type, params.message_type, params.payload_type));
|
|
4018
|
+
setIfPresent("kind", firstValue(body.kind, params.kind));
|
|
4019
|
+
setIfPresent("version", firstValue(body.version, params.version));
|
|
4020
|
+
setIfPresent("timestamp", firstValue(params.timestamp, resultObj.timestamp, resultObj.created_at, resultObj.t_server, Date.now()));
|
|
4021
|
+
envelope.encrypted = Boolean(encrypted);
|
|
4022
|
+
const context = this.envelopeMetadata(params.context);
|
|
4023
|
+
if (context) envelope.context = context;
|
|
4024
|
+
const protectedHeaders = this.envelopeMetadata(params.protected_headers) ?? this.envelopeMetadata(params.headers);
|
|
4025
|
+
if (protectedHeaders) envelope.protected_headers = protectedHeaders;
|
|
4026
|
+
setIfPresent("payload_type", firstValue(params.payload_type, protectedHeaders?.payload_type, body.type));
|
|
4027
|
+
return envelope;
|
|
4028
|
+
}
|
|
4029
|
+
attachSendResultEnvelope(method, params, result, encrypted) {
|
|
4030
|
+
if (!APP_SEND_ENVELOPE_METHODS.has(method) || !isJsonObject(result)) return result;
|
|
4031
|
+
const out = { ...result };
|
|
4032
|
+
out.envelope = this.sendResultEnvelope(method, params, out, encrypted);
|
|
4033
|
+
if ("payload" in params) {
|
|
4034
|
+
out.payload = params.payload;
|
|
4035
|
+
} else if ("content" in params) {
|
|
4036
|
+
out.payload = params.content;
|
|
4037
|
+
}
|
|
4038
|
+
return out;
|
|
4039
|
+
}
|
|
4040
|
+
attachAppGroupEventEnvelope(payload) {
|
|
4041
|
+
if (!isJsonObject(payload)) return payload;
|
|
4042
|
+
const result = { ...payload };
|
|
4043
|
+
result.envelope = this.appGroupEventEnvelope(result);
|
|
4044
|
+
return result;
|
|
3797
4045
|
}
|
|
3798
4046
|
stripInternalSenderDeviceFields(payload) {
|
|
3799
4047
|
if (!isJsonObject(payload)) return payload;
|
|
@@ -3813,6 +4061,11 @@ var MessageDeliveryEngine = class {
|
|
|
3813
4061
|
const payloadType = String(payload.type ?? payload.kind ?? "").trim();
|
|
3814
4062
|
if (msgType !== "message.recalled" && payloadType !== "message.recalled") return null;
|
|
3815
4063
|
const event = { ...payload };
|
|
4064
|
+
for (const key of APP_MESSAGE_ENVELOPE_KEYS) {
|
|
4065
|
+
if (Object.prototype.hasOwnProperty.call(msg, key) && !(key in event)) {
|
|
4066
|
+
event[key] = msg[key];
|
|
4067
|
+
}
|
|
4068
|
+
}
|
|
3816
4069
|
const rawIds = event.message_ids;
|
|
3817
4070
|
let messageIds = Array.isArray(rawIds) ? rawIds.map((item) => String(item ?? "").trim()).filter(Boolean) : [];
|
|
3818
4071
|
if (messageIds.length === 0) {
|
|
@@ -3831,7 +4084,10 @@ var MessageDeliveryEngine = class {
|
|
|
3831
4084
|
if (!("to" in event)) event.to = msg.to ?? msg.to_aid ?? "";
|
|
3832
4085
|
if (!("timestamp" in event)) event.timestamp = msg.timestamp ?? msg.t_server ?? event.recalled_at ?? 0;
|
|
3833
4086
|
if ("seq" in msg && !("seq" in event)) event.seq = msg.seq;
|
|
3834
|
-
if ("message_id" in msg
|
|
4087
|
+
if ("message_id" in msg) {
|
|
4088
|
+
event.message_id = msg.message_id;
|
|
4089
|
+
if (!("tombstone_message_id" in event)) event.tombstone_message_id = msg.message_id;
|
|
4090
|
+
}
|
|
3835
4091
|
if ("device_id" in msg && !("device_id" in event)) event.device_id = msg.device_id;
|
|
3836
4092
|
if ("slot_id" in msg && !("slot_id" in event)) event.slot_id = msg.slot_id;
|
|
3837
4093
|
return event;
|
|
@@ -3850,6 +4106,11 @@ var MessageDeliveryEngine = class {
|
|
|
3850
4106
|
const payloadType = String(payload.type ?? payload.kind ?? "").trim();
|
|
3851
4107
|
if (msgType !== "group.message_recalled" && payloadType !== "group.message_recalled") return null;
|
|
3852
4108
|
const event = { ...payload };
|
|
4109
|
+
for (const key of APP_MESSAGE_ENVELOPE_KEYS) {
|
|
4110
|
+
if (Object.prototype.hasOwnProperty.call(msg, key) && !(key in event)) {
|
|
4111
|
+
event[key] = msg[key];
|
|
4112
|
+
}
|
|
4113
|
+
}
|
|
3853
4114
|
const rawIds = event.message_ids;
|
|
3854
4115
|
let messageIds = Array.isArray(rawIds) ? rawIds.map((item) => String(item ?? "").trim()).filter(Boolean) : [];
|
|
3855
4116
|
if (messageIds.length === 0) {
|
|
@@ -3867,7 +4128,10 @@ var MessageDeliveryEngine = class {
|
|
|
3867
4128
|
if (!("group_id" in event)) event.group_id = msg.group_id ?? "";
|
|
3868
4129
|
if (!("timestamp" in event)) event.timestamp = msg.timestamp ?? msg.t_server ?? event.recalled_at ?? 0;
|
|
3869
4130
|
if ("seq" in msg) event.seq = msg.seq;
|
|
3870
|
-
if ("message_id" in msg
|
|
4131
|
+
if ("message_id" in msg) {
|
|
4132
|
+
event.message_id = msg.message_id;
|
|
4133
|
+
if (!("tombstone_message_id" in event)) event.tombstone_message_id = msg.message_id;
|
|
4134
|
+
}
|
|
3871
4135
|
return event;
|
|
3872
4136
|
}
|
|
3873
4137
|
groupRecallDedupKey(groupId, payload) {
|
|
@@ -4357,6 +4621,14 @@ var MessageDeliveryEngine = class {
|
|
|
4357
4621
|
return;
|
|
4358
4622
|
}
|
|
4359
4623
|
const ns = `group_event:${groupId}`;
|
|
4624
|
+
if (this.isSelfJoinGroupChanged(data)) {
|
|
4625
|
+
const contig = client._seqTracker.getContiguousSeq(ns);
|
|
4626
|
+
const maxSeen = client._seqTracker.getMaxSeenSeq(ns);
|
|
4627
|
+
if (contig === 0 && maxSeen === 0 && eventSeq > 1) {
|
|
4628
|
+
client._clientLog.debug(`group.changed self-join baseline: group=${groupId}, event_seq=${eventSeq}, baseline=${eventSeq - 1}`);
|
|
4629
|
+
client._seqTracker.forceContiguousSeq(ns, eventSeq - 1);
|
|
4630
|
+
}
|
|
4631
|
+
}
|
|
4360
4632
|
const contigBefore = client._seqTracker.getContiguousSeq(ns);
|
|
4361
4633
|
if (eventSeq <= contigBefore || client._pushedSeqs.get(ns)?.has(eventSeq)) {
|
|
4362
4634
|
client._clientLog.debug(`group.changed skipped duplicate/stale: group=${groupId}, event_seq=${eventSeq}, contiguous=${contigBefore}`);
|
|
@@ -4382,6 +4654,16 @@ var MessageDeliveryEngine = class {
|
|
|
4382
4654
|
client._safeAsync(this.fillGroupEventGap(groupId));
|
|
4383
4655
|
}
|
|
4384
4656
|
}
|
|
4657
|
+
isSelfJoinGroupChanged(data) {
|
|
4658
|
+
const action = String(data.action ?? "").trim();
|
|
4659
|
+
if (!["member_added", "joined", "join_approved", "invite_code_used"].includes(action)) return false;
|
|
4660
|
+
const selfAid = String(this.runtime.client._aid ?? "").trim();
|
|
4661
|
+
if (!selfAid) return false;
|
|
4662
|
+
const joinedAid = String(data.joined_aid ?? data.member_aid ?? data.aid ?? "").trim();
|
|
4663
|
+
if (joinedAid === selfAid) return true;
|
|
4664
|
+
const actorAid = String(data.actor_aid ?? "").trim();
|
|
4665
|
+
return !joinedAid && ["joined", "invite_code_used"].includes(action) && actorAid === selfAid;
|
|
4666
|
+
}
|
|
4385
4667
|
enqueueOnlineUnreadHint(data) {
|
|
4386
4668
|
const client = this.runtime.client;
|
|
4387
4669
|
const groupId = String(data.group_id ?? "").trim();
|
|
@@ -5336,6 +5618,8 @@ var RpcPipeline = class {
|
|
|
5336
5618
|
async callImpl(method, params) {
|
|
5337
5619
|
const client = this.runtime.client;
|
|
5338
5620
|
const p = this.preflight(method, params).params;
|
|
5621
|
+
const skipSendResultEnvelope = Boolean(p._skip_send_result_envelope);
|
|
5622
|
+
delete p._skip_send_result_envelope;
|
|
5339
5623
|
if (method === "message.send") {
|
|
5340
5624
|
const encrypt = p.encrypt !== void 0 ? p.encrypt : true;
|
|
5341
5625
|
delete p.encrypt;
|
|
@@ -5399,12 +5683,12 @@ var RpcPipeline = class {
|
|
|
5399
5683
|
const pullGateKey = this.pullGateKeyForCall(method, p);
|
|
5400
5684
|
if (pullGateKey) {
|
|
5401
5685
|
return await this.runPullSerialized(pullGateKey, async () => {
|
|
5402
|
-
return await this.callImplInner(method, p);
|
|
5686
|
+
return await this.callImplInner(method, p, skipSendResultEnvelope);
|
|
5403
5687
|
});
|
|
5404
5688
|
}
|
|
5405
|
-
return await this.callImplInner(method, p);
|
|
5689
|
+
return await this.callImplInner(method, p, skipSendResultEnvelope);
|
|
5406
5690
|
}
|
|
5407
|
-
async callImplInner(method, p) {
|
|
5691
|
+
async callImplInner(method, p, skipSendResultEnvelope = false) {
|
|
5408
5692
|
const client = this.runtime.client;
|
|
5409
5693
|
if (method === "message.pull") {
|
|
5410
5694
|
await client._ensureV2SessionReady("message.pull");
|
|
@@ -5452,6 +5736,14 @@ var RpcPipeline = class {
|
|
|
5452
5736
|
const callTimeout = NON_IDEMPOTENT_METHODS.has(method) ? NON_IDEMPOTENT_TIMEOUT : void 0;
|
|
5453
5737
|
let result = callTimeout ? await client._transport.call(method, p, callTimeout) : await client._transport.call(method, p);
|
|
5454
5738
|
result = await this.postprocessResult(method, p, result);
|
|
5739
|
+
if (!skipSendResultEnvelope) {
|
|
5740
|
+
result = client._delivery.attachSendResultEnvelope(
|
|
5741
|
+
method,
|
|
5742
|
+
p,
|
|
5743
|
+
result,
|
|
5744
|
+
Boolean(p.encrypted)
|
|
5745
|
+
);
|
|
5746
|
+
}
|
|
5455
5747
|
return result;
|
|
5456
5748
|
}
|
|
5457
5749
|
preflight(method, params) {
|
|
@@ -5482,7 +5774,8 @@ var RpcPipeline = class {
|
|
|
5482
5774
|
if (!client._instanceProtectedHeaders || !PROTECTED_HEADERS_METHODS.has(method)) {
|
|
5483
5775
|
return;
|
|
5484
5776
|
}
|
|
5485
|
-
const
|
|
5777
|
+
const existingValue = params.protected_headers ?? params.headers;
|
|
5778
|
+
const existing = isJsonObject(existingValue) ? existingValue : {};
|
|
5486
5779
|
params.protected_headers = { ...client._instanceProtectedHeaders, ...existing };
|
|
5487
5780
|
}
|
|
5488
5781
|
normalizeOutboundMessagePayload(params, method = "") {
|
|
@@ -10513,17 +10806,34 @@ var V2E2EECoordinator = class {
|
|
|
10513
10806
|
return client.call("message.send", {
|
|
10514
10807
|
to: toAid,
|
|
10515
10808
|
payload: envelope,
|
|
10516
|
-
encrypt: false
|
|
10809
|
+
encrypt: false,
|
|
10810
|
+
_skip_send_result_envelope: true
|
|
10517
10811
|
});
|
|
10518
10812
|
};
|
|
10519
10813
|
try {
|
|
10520
|
-
|
|
10814
|
+
const result = await attempt(true);
|
|
10815
|
+
return client._delivery.attachSendResultEnvelope("message.send", {
|
|
10816
|
+
to: toAid,
|
|
10817
|
+
payload,
|
|
10818
|
+
message_id: opts?.messageId,
|
|
10819
|
+
timestamp: opts?.timestamp,
|
|
10820
|
+
protected_headers: opts?.protectedHeaders,
|
|
10821
|
+
context: opts?.context
|
|
10822
|
+
}, result, true);
|
|
10521
10823
|
} catch (exc) {
|
|
10522
10824
|
const excCode = exc?.code;
|
|
10523
10825
|
if (V2_RETRYABLE_CODES.has(excCode)) {
|
|
10524
10826
|
client._clientLog.debug(`V2 P2P speculative send rejected (code=${excCode}), refreshing bootstrap`);
|
|
10525
10827
|
this.deleteBootstrapCacheEntry(toAid);
|
|
10526
|
-
|
|
10828
|
+
const result = await attempt(false);
|
|
10829
|
+
return client._delivery.attachSendResultEnvelope("message.send", {
|
|
10830
|
+
to: toAid,
|
|
10831
|
+
payload,
|
|
10832
|
+
message_id: opts?.messageId,
|
|
10833
|
+
timestamp: opts?.timestamp,
|
|
10834
|
+
protected_headers: opts?.protectedHeaders,
|
|
10835
|
+
context: opts?.context
|
|
10836
|
+
}, result, true);
|
|
10527
10837
|
}
|
|
10528
10838
|
throw exc;
|
|
10529
10839
|
}
|
|
@@ -10690,7 +11000,14 @@ var V2E2EECoordinator = class {
|
|
|
10690
11000
|
client._saveSeqTrackerState();
|
|
10691
11001
|
}
|
|
10692
11002
|
}
|
|
10693
|
-
return
|
|
11003
|
+
return client._delivery.attachSendResultEnvelope("group.send", {
|
|
11004
|
+
group_id: gid,
|
|
11005
|
+
payload,
|
|
11006
|
+
message_id: opts?.messageId,
|
|
11007
|
+
timestamp: opts?.timestamp,
|
|
11008
|
+
protected_headers: opts?.protectedHeaders,
|
|
11009
|
+
context: opts?.context
|
|
11010
|
+
}, result, true);
|
|
10694
11011
|
} catch (exc) {
|
|
10695
11012
|
const excCode = exc?.code;
|
|
10696
11013
|
if (V2_RETRYABLE_CODES.has(excCode)) {
|
|
@@ -10706,7 +11023,14 @@ var V2E2EECoordinator = class {
|
|
|
10706
11023
|
client._saveSeqTrackerState();
|
|
10707
11024
|
}
|
|
10708
11025
|
}
|
|
10709
|
-
return
|
|
11026
|
+
return client._delivery.attachSendResultEnvelope("group.send", {
|
|
11027
|
+
group_id: gid,
|
|
11028
|
+
payload,
|
|
11029
|
+
message_id: opts?.messageId,
|
|
11030
|
+
timestamp: opts?.timestamp,
|
|
11031
|
+
protected_headers: opts?.protectedHeaders,
|
|
11032
|
+
context: opts?.context
|
|
11033
|
+
}, result, true);
|
|
10710
11034
|
}
|
|
10711
11035
|
throw exc;
|
|
10712
11036
|
}
|
|
@@ -10887,13 +11211,27 @@ var V2E2EECoordinator = class {
|
|
|
10887
11211
|
return result;
|
|
10888
11212
|
};
|
|
10889
11213
|
try {
|
|
10890
|
-
|
|
11214
|
+
const result = await attempt(true);
|
|
11215
|
+
return client._delivery.attachSendResultEnvelope("message.thought.put", {
|
|
11216
|
+
...params,
|
|
11217
|
+
to: toAid,
|
|
11218
|
+
payload,
|
|
11219
|
+
thought_id: thoughtId,
|
|
11220
|
+
timestamp
|
|
11221
|
+
}, result, true);
|
|
10891
11222
|
} catch (exc) {
|
|
10892
11223
|
const excCode = Number(exc?.code);
|
|
10893
11224
|
if (V2_RETRYABLE_CODES.has(excCode)) {
|
|
10894
11225
|
client._clientLog.debug(`V2 P2P thought put speculative rejected (code=${String(excCode)}), refreshing bootstrap`);
|
|
10895
11226
|
this.deleteBootstrapCacheEntry(toAid);
|
|
10896
|
-
|
|
11227
|
+
const result = await attempt(false);
|
|
11228
|
+
return client._delivery.attachSendResultEnvelope("message.thought.put", {
|
|
11229
|
+
...params,
|
|
11230
|
+
to: toAid,
|
|
11231
|
+
payload,
|
|
11232
|
+
thought_id: thoughtId,
|
|
11233
|
+
timestamp
|
|
11234
|
+
}, result, true);
|
|
10897
11235
|
}
|
|
10898
11236
|
throw exc;
|
|
10899
11237
|
}
|
|
@@ -10943,13 +11281,27 @@ var V2E2EECoordinator = class {
|
|
|
10943
11281
|
return result;
|
|
10944
11282
|
};
|
|
10945
11283
|
try {
|
|
10946
|
-
|
|
11284
|
+
const result = await attempt(true);
|
|
11285
|
+
return client._delivery.attachSendResultEnvelope("group.thought.put", {
|
|
11286
|
+
...params,
|
|
11287
|
+
group_id: groupId,
|
|
11288
|
+
payload,
|
|
11289
|
+
thought_id: thoughtId,
|
|
11290
|
+
timestamp
|
|
11291
|
+
}, result, true);
|
|
10947
11292
|
} catch (exc) {
|
|
10948
11293
|
const excCode = Number(exc?.code);
|
|
10949
11294
|
if (V2_RETRYABLE_CODES.has(excCode)) {
|
|
10950
11295
|
client._clientLog.debug(`V2 group thought put speculative rejected (code=${String(excCode)}), refreshing bootstrap`);
|
|
10951
11296
|
this.deleteBootstrapCacheEntry(`group:${groupId}`);
|
|
10952
|
-
|
|
11297
|
+
const result = await attempt(false);
|
|
11298
|
+
return client._delivery.attachSendResultEnvelope("group.thought.put", {
|
|
11299
|
+
...params,
|
|
11300
|
+
group_id: groupId,
|
|
11301
|
+
payload,
|
|
11302
|
+
thought_id: thoughtId,
|
|
11303
|
+
timestamp
|
|
11304
|
+
}, result, true);
|
|
10953
11305
|
}
|
|
10954
11306
|
throw exc;
|
|
10955
11307
|
}
|
|
@@ -15126,6 +15478,20 @@ function _v2ConcatBytes(...parts) {
|
|
|
15126
15478
|
function formatCaughtError2(error) {
|
|
15127
15479
|
return error instanceof Error ? error : String(error);
|
|
15128
15480
|
}
|
|
15481
|
+
var RELOGIN_REFRESH_ERRORS = /* @__PURE__ */ new Set([
|
|
15482
|
+
"missing refresh_token",
|
|
15483
|
+
"invalid_or_expired_refresh_token",
|
|
15484
|
+
"refresh not supported"
|
|
15485
|
+
]);
|
|
15486
|
+
function authErrorRequiresRelogin(error) {
|
|
15487
|
+
const data = error.data;
|
|
15488
|
+
if (isJsonObject(data)) {
|
|
15489
|
+
if (data.relogin_required === true) return true;
|
|
15490
|
+
const code = String(data.error ?? "").trim().toLowerCase();
|
|
15491
|
+
if (RELOGIN_REFRESH_ERRORS.has(code)) return true;
|
|
15492
|
+
}
|
|
15493
|
+
return RELOGIN_REFRESH_ERRORS.has(error.message.trim().toLowerCase());
|
|
15494
|
+
}
|
|
15129
15495
|
function v2E2eeMeta2(envelope) {
|
|
15130
15496
|
const suite = String(envelope.suite ?? "");
|
|
15131
15497
|
const modeSuite = String(envelope.suite ?? "unknown");
|
|
@@ -16540,16 +16906,28 @@ var _AUNClient = class _AUNClient {
|
|
|
16540
16906
|
this._tokenRefreshFailures = 0;
|
|
16541
16907
|
} catch (exc) {
|
|
16542
16908
|
if (exc instanceof AuthError) {
|
|
16909
|
+
if (authErrorRequiresRelogin(exc)) {
|
|
16910
|
+
this._clientLog.warn(`token refresh requires relogin, stopping refresh loop and triggering reconnect: ${exc.message}`);
|
|
16911
|
+
await this._dispatcher.publish("token.refresh_exhausted", {
|
|
16912
|
+
aid: this._identity?.aid ?? null,
|
|
16913
|
+
consecutive_failures: 1,
|
|
16914
|
+
last_error: String(exc),
|
|
16915
|
+
relogin_required: true
|
|
16916
|
+
});
|
|
16917
|
+
this._tokenRefreshFailures = 0;
|
|
16918
|
+
await this._handleTransportDisconnect(new Error("token refresh relogin required, triggering reconnect"));
|
|
16919
|
+
return;
|
|
16920
|
+
}
|
|
16543
16921
|
this._tokenRefreshFailures++;
|
|
16544
16922
|
if (this._tokenRefreshFailures >= 3) {
|
|
16545
|
-
this._clientLog.warn(`token
|
|
16546
|
-
this._dispatcher.publish("token.refresh_exhausted", {
|
|
16923
|
+
this._clientLog.warn(`token refresh failed ${this._tokenRefreshFailures} consecutive times, stopping refresh loop and triggering reconnect`);
|
|
16924
|
+
await this._dispatcher.publish("token.refresh_exhausted", {
|
|
16547
16925
|
aid: this._identity?.aid ?? null,
|
|
16548
16926
|
consecutive_failures: this._tokenRefreshFailures,
|
|
16549
16927
|
last_error: String(exc)
|
|
16550
16928
|
});
|
|
16551
16929
|
this._tokenRefreshFailures = 0;
|
|
16552
|
-
this._handleTransportDisconnect(new Error("token refresh exhausted, triggering reconnect"));
|
|
16930
|
+
await this._handleTransportDisconnect(new Error("token refresh exhausted, triggering reconnect"));
|
|
16553
16931
|
return;
|
|
16554
16932
|
}
|
|
16555
16933
|
this._clientLog.warn(`token refresh failed (${this._tokenRefreshFailures}/3), next retry: ${String(exc)}`);
|
|
@@ -16684,6 +17062,21 @@ var _AUNClient = class _AUNClient {
|
|
|
16684
17062
|
if (!this._sessionParams) {
|
|
16685
17063
|
throw new StateError("missing connect params for reconnect");
|
|
16686
17064
|
}
|
|
17065
|
+
{
|
|
17066
|
+
const identity = this._identity;
|
|
17067
|
+
if (identity) {
|
|
17068
|
+
const cachedToken = String(identity.access_token ?? "");
|
|
17069
|
+
const expiresAt = this._auth.getAccessTokenExpiry(identity);
|
|
17070
|
+
if (cachedToken && (expiresAt === null || expiresAt > Date.now() / 1e3 + 30)) {
|
|
17071
|
+
this._sessionParams.access_token = cachedToken;
|
|
17072
|
+
} else {
|
|
17073
|
+
this._clientLog.debug(`reconnect: cached token expired or missing for aid=${this._aid ?? ""}, clearing to trigger re-login`);
|
|
17074
|
+
this._sessionParams.access_token = "";
|
|
17075
|
+
}
|
|
17076
|
+
} else {
|
|
17077
|
+
this._sessionParams.access_token = "";
|
|
17078
|
+
}
|
|
17079
|
+
}
|
|
16687
17080
|
await this._connectOnce(this._sessionParams, true);
|
|
16688
17081
|
this._lastError = null;
|
|
16689
17082
|
this._lastErrorCode = null;
|
|
@@ -19249,6 +19642,8 @@ var ServiceProxyClient = class {
|
|
|
19249
19642
|
const stats = { connection_mode: mode, connections: 0, registered: 0, handled_requests: 0, wakeup_count: 0 };
|
|
19250
19643
|
try {
|
|
19251
19644
|
if (mode === "persistent") {
|
|
19645
|
+
const maxReconnectDelay = 6e4;
|
|
19646
|
+
let _delay = Math.max(0, opts.reconnectDelaySeconds ?? 1) * 1e3;
|
|
19252
19647
|
while (this._running) {
|
|
19253
19648
|
try {
|
|
19254
19649
|
await this._autoRegisterServicesWithGateway();
|
|
@@ -19261,10 +19656,21 @@ var ServiceProxyClient = class {
|
|
|
19261
19656
|
stats.connections = Number(stats.connections) + 1;
|
|
19262
19657
|
stats.registered = Number(result.registered ?? stats.registered);
|
|
19263
19658
|
stats.handled_requests = Number(stats.handled_requests) + Number(result.handled_requests ?? 0);
|
|
19659
|
+
_delay = Math.max(0, opts.reconnectDelaySeconds ?? 1) * 1e3;
|
|
19264
19660
|
} catch (exc) {
|
|
19265
19661
|
if (!this._running) break;
|
|
19266
|
-
|
|
19267
|
-
|
|
19662
|
+
if (exc instanceof AuthError) {
|
|
19663
|
+
this._logWarn(`persistent tunnel auth error, re-authenticating: ${formatError(exc)}`);
|
|
19664
|
+
try {
|
|
19665
|
+
await this._authenticateForAccessToken();
|
|
19666
|
+
} catch (reAuthExc) {
|
|
19667
|
+
this._logWarn(`re-authentication failed: ${formatError(reAuthExc)}`);
|
|
19668
|
+
}
|
|
19669
|
+
} else {
|
|
19670
|
+
this._logWarn(`persistent tunnel reconnect scheduled after error: ${formatError(exc)}`);
|
|
19671
|
+
}
|
|
19672
|
+
await sleep(_delay);
|
|
19673
|
+
_delay = Math.min(_delay * 2, maxReconnectDelay);
|
|
19268
19674
|
} finally {
|
|
19269
19675
|
this._activeTunnel?.close();
|
|
19270
19676
|
this._activeTunnel = null;
|