@awiki/dsh-plugin 0.3.17 → 0.3.19
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 +4 -0
- package/README.zh.md +11 -0
- package/THIRD_PARTY_NOTICES.md +6 -0
- package/lib/client.js +4012 -671
- package/lib/client.js.map +1 -1
- package/lib/index.js +603 -61
- package/lib/{sdk-adapter-DGcTiEZz.mjs → package-version.generated-nZcAVnm8.mjs} +103 -10
- package/lib/provider.js +5 -3
- package/lib/typert.host.js +553 -133
- package/lib/typert.remote-client.d.ts +11 -3
- package/lib/typert.remote-client.d.ts.map +1 -1
- package/lib/typert.remote-client.js +553 -133
- package/lib/types/backend-capabilities.d.ts +12 -0
- package/lib/types/backend-capabilities.d.ts.map +1 -0
- package/lib/types/backend-capabilities.js +48 -0
- package/lib/types/backend-capabilities.js.map +1 -0
- package/lib/types/client/AwikiAvatar.d.ts +40 -0
- package/lib/types/client/AwikiAvatar.d.ts.map +1 -0
- package/lib/types/client/AwikiAvatar.js +201 -0
- package/lib/types/client/AwikiAvatar.js.map +1 -0
- package/lib/types/client/AwikiAvatarEditor.d.ts +11 -0
- package/lib/types/client/AwikiAvatarEditor.d.ts.map +1 -0
- package/lib/types/client/AwikiAvatarEditor.js +113 -0
- package/lib/types/client/AwikiAvatarEditor.js.map +1 -0
- package/lib/types/client/AwikiAvatarPreview.d.ts +8 -0
- package/lib/types/client/AwikiAvatarPreview.d.ts.map +1 -0
- package/lib/types/client/AwikiAvatarPreview.js +68 -0
- package/lib/types/client/AwikiAvatarPreview.js.map +1 -0
- package/lib/types/client/AwikiGroupDetails.js +2 -2
- package/lib/types/client/AwikiGroupDetails.js.map +1 -1
- package/lib/types/client/AwikiIdentityAccess.d.ts +1 -0
- package/lib/types/client/AwikiIdentityAccess.d.ts.map +1 -1
- package/lib/types/client/AwikiIdentityAccess.js +21 -4
- package/lib/types/client/AwikiIdentityAccess.js.map +1 -1
- package/lib/types/client/AwikiOverlay.d.ts.map +1 -1
- package/lib/types/client/AwikiOverlay.js +97 -29
- package/lib/types/client/AwikiOverlay.js.map +1 -1
- package/lib/types/client/AwikiProfileCard.d.ts +1 -1
- package/lib/types/client/AwikiProfileCard.d.ts.map +1 -1
- package/lib/types/client/AwikiProfileCard.js +6 -2
- package/lib/types/client/AwikiProfileCard.js.map +1 -1
- package/lib/types/client/AwikiSettingsSection.d.ts +1 -0
- package/lib/types/client/AwikiSettingsSection.d.ts.map +1 -1
- package/lib/types/client/AwikiSettingsSection.js +5 -4
- package/lib/types/client/AwikiSettingsSection.js.map +1 -1
- package/lib/types/client/avatar-cache.d.ts +24 -0
- package/lib/types/client/avatar-cache.d.ts.map +1 -0
- package/lib/types/client/avatar-cache.js +336 -0
- package/lib/types/client/avatar-cache.js.map +1 -0
- package/lib/types/client/avatar-dialog.d.ts +3 -0
- package/lib/types/client/avatar-dialog.d.ts.map +1 -0
- package/lib/types/client/avatar-dialog.js +18 -0
- package/lib/types/client/avatar-dialog.js.map +1 -0
- package/lib/types/client/avatar-image.d.ts +26 -0
- package/lib/types/client/avatar-image.d.ts.map +1 -0
- package/lib/types/client/avatar-image.js +193 -0
- package/lib/types/client/avatar-image.js.map +1 -0
- package/lib/types/client/controller.d.ts +37 -4
- package/lib/types/client/controller.d.ts.map +1 -1
- package/lib/types/client/controller.js +306 -59
- package/lib/types/client/controller.js.map +1 -1
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/index.js +11 -2
- package/lib/types/client/index.js.map +1 -1
- package/lib/types/client/settings-locales.d.ts +1 -1
- package/lib/types/client/settings-locales.d.ts.map +1 -1
- package/lib/types/client/settings-locales.js +26 -2
- package/lib/types/client/settings-locales.js.map +1 -1
- package/lib/types/client/slots.d.ts +12 -3
- package/lib/types/client/slots.d.ts.map +1 -1
- package/lib/types/conversation-preferences.d.ts.map +1 -1
- package/lib/types/conversation-preferences.js +27 -0
- package/lib/types/conversation-preferences.js.map +1 -1
- package/lib/types/index.d.ts +26 -3
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +321 -33
- package/lib/types/index.js.map +1 -1
- package/lib/types/listener.d.ts.map +1 -1
- package/lib/types/listener.js +9 -8
- package/lib/types/listener.js.map +1 -1
- package/lib/types/package-version.generated.d.ts +2 -2
- package/lib/types/package-version.generated.js +2 -2
- package/lib/types/provider-api.d.ts +28 -3
- package/lib/types/provider-api.d.ts.map +1 -1
- package/lib/types/provider.d.ts.map +1 -1
- package/lib/types/provider.js +5 -2
- package/lib/types/provider.js.map +1 -1
- package/lib/types/sdk-adapter.d.ts +10 -3
- package/lib/types/sdk-adapter.d.ts.map +1 -1
- package/lib/types/sdk-adapter.js +78 -7
- package/lib/types/sdk-adapter.js.map +1 -1
- package/lib/types/tenant-capability-cache.d.ts +15 -0
- package/lib/types/tenant-capability-cache.d.ts.map +1 -0
- package/lib/types/tenant-capability-cache.js +45 -0
- package/lib/types/tenant-capability-cache.js.map +1 -0
- package/lib/types/tenant-registry.d.ts +2 -0
- package/lib/types/tenant-registry.d.ts.map +1 -1
- package/lib/types/tenant-registry.js +83 -16
- package/lib/types/tenant-registry.js.map +1 -1
- package/lib/types/types.d.ts +70 -7
- package/lib/types/types.d.ts.map +1 -1
- package/lib/types/types.js.map +1 -1
- package/lib/types/update-policy.d.ts +2 -2
- package/lib/types/update-policy.d.ts.map +1 -1
- package/lib/types/update-policy.js +10 -1
- package/lib/types/update-policy.js.map +1 -1
- package/package.json +33 -34
package/lib/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
import "@deepseek-ai/cordis";
|
|
1
|
+
import { a as downloadedAttachment, c as successfulMailRecoveryObservability, l as isMessageTarget, n as DSH_AWIKI_PACKAGE_VERSION, o as failedMailRecoveryObservability, r as AwikiSdkError, s as fencedMailRecoveryObservability, t as DSH_AWIKI_MODEL_PROXY_PACKAGE_VERSION } from "./package-version.generated-nZcAVnm8.mjs";
|
|
3
2
|
import { createHash, randomUUID, timingSafeEqual } from "node:crypto";
|
|
4
|
-
import {
|
|
3
|
+
import { copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
5
4
|
import { basename, dirname, isAbsolute, join, resolve } from "node:path";
|
|
5
|
+
import "@deepseek-ai/cordis";
|
|
6
|
+
import { homedir } from "node:os";
|
|
6
7
|
import z from "@deepseek-ai/schemastery";
|
|
7
8
|
import { Remote, TypertRemoteService } from "@deepseek-ai/dsh-typert-protocol";
|
|
8
9
|
import { SettingsConflictError } from "@deepseek-ai/dsh-settings";
|
|
@@ -12,8 +13,122 @@ import { SessionId, SessionLogOffset } from "@deepseek-ai/dsh-session";
|
|
|
12
13
|
import { installModelSelection } from "@deepseek-ai/dsh-agent";
|
|
13
14
|
import { createUserMessage } from "@deepseek-ai/dsh-llm";
|
|
14
15
|
import { fileURLToPath } from "node:url";
|
|
15
|
-
import { copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
16
16
|
import { satisfies, validRange } from "semver";
|
|
17
|
+
//#region lib/types/tenant-capability-cache.js
|
|
18
|
+
/** Host-only metadata. Core owns feature truth; this contains no auth material. */
|
|
19
|
+
var TenantCapabilityCache = class {
|
|
20
|
+
key;
|
|
21
|
+
directory;
|
|
22
|
+
path;
|
|
23
|
+
constructor(stateRoot, endpoint) {
|
|
24
|
+
this.key = createHash("sha256").update(JSON.stringify([
|
|
25
|
+
endpoint,
|
|
26
|
+
stateRoot,
|
|
27
|
+
1
|
|
28
|
+
])).digest("hex");
|
|
29
|
+
this.directory = join(stateRoot, "cache", "tenant-capabilities-v1");
|
|
30
|
+
this.path = join(this.directory, `${this.key}.json`);
|
|
31
|
+
}
|
|
32
|
+
read(validate) {
|
|
33
|
+
try {
|
|
34
|
+
const bytes = readFileSync(this.path);
|
|
35
|
+
if (bytes.length > 16384) return void 0;
|
|
36
|
+
const value = JSON.parse(bytes.toString("utf8"));
|
|
37
|
+
if (value.version !== 1 || value.key !== this.key || typeof value.metadata?.handleRecoveryPhoneEnabled !== "boolean") return void 0;
|
|
38
|
+
return validate(value.metadata);
|
|
39
|
+
} catch {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
write(metadata) {
|
|
44
|
+
const temporary = `${this.path}.${randomUUID()}.tmp`;
|
|
45
|
+
try {
|
|
46
|
+
mkdirSync(this.directory, {
|
|
47
|
+
recursive: true,
|
|
48
|
+
mode: 448
|
|
49
|
+
});
|
|
50
|
+
writeFileSync(temporary, JSON.stringify({
|
|
51
|
+
version: 1,
|
|
52
|
+
key: this.key,
|
|
53
|
+
metadata
|
|
54
|
+
}), {
|
|
55
|
+
mode: 384,
|
|
56
|
+
flag: "wx"
|
|
57
|
+
});
|
|
58
|
+
renameSync(temporary, this.path);
|
|
59
|
+
return true;
|
|
60
|
+
} catch {
|
|
61
|
+
try {
|
|
62
|
+
unlinkSync(temporary);
|
|
63
|
+
} catch {}
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
//#endregion
|
|
69
|
+
//#region lib/types/backend-capabilities.js
|
|
70
|
+
/** Keep the client ceiling and only project a confirmed positive byte limit. */
|
|
71
|
+
function backendAttachmentMaxBytes(snapshot, clientLimit) {
|
|
72
|
+
if (snapshot?.status !== "confirmed" && snapshot?.status !== "stale") return clientLimit;
|
|
73
|
+
const capability = snapshot.capabilities.attachments;
|
|
74
|
+
if (capability?.support !== "supported") return clientLimit;
|
|
75
|
+
const raw = capability.limits?.max_object_bytes;
|
|
76
|
+
const value = typeof raw === "number" ? raw : typeof raw === "string" && /^\d+$/.test(raw) ? Number(raw) : NaN;
|
|
77
|
+
return Number.isSafeInteger(value) && value > 0 ? Math.min(clientLimit, value) : clientLimit;
|
|
78
|
+
}
|
|
79
|
+
const PUBLIC = [
|
|
80
|
+
"registration.handle_only",
|
|
81
|
+
"identity.multi_device",
|
|
82
|
+
"identity.device_revoke",
|
|
83
|
+
"identity.capability_upgrade",
|
|
84
|
+
"identity.root_transfer",
|
|
85
|
+
"agents.managed_agents",
|
|
86
|
+
"agents.daemon",
|
|
87
|
+
"services.mail",
|
|
88
|
+
"services.model_proxy",
|
|
89
|
+
"services.guest_gateway",
|
|
90
|
+
"account.state_sync",
|
|
91
|
+
"content.pages",
|
|
92
|
+
"content.sites",
|
|
93
|
+
"content.hosting",
|
|
94
|
+
"client_updates.policy"
|
|
95
|
+
];
|
|
96
|
+
const MESSAGE = [
|
|
97
|
+
"direct",
|
|
98
|
+
"groups",
|
|
99
|
+
"attachments",
|
|
100
|
+
"direct_e2ee",
|
|
101
|
+
"group_e2ee",
|
|
102
|
+
"encrypted_attachments",
|
|
103
|
+
"group_open_join",
|
|
104
|
+
"group_admin_add"
|
|
105
|
+
];
|
|
106
|
+
/** Project one source independently so a failed source cannot erase its peer. */
|
|
107
|
+
function projectBackendSnapshot(snapshot, authenticated) {
|
|
108
|
+
const result = {};
|
|
109
|
+
for (const key of authenticated ? MESSAGE : PUBLIC) {
|
|
110
|
+
const support = snapshot?.capabilities[key]?.support;
|
|
111
|
+
result[key] = (snapshot?.status === "confirmed" || snapshot?.status === "stale") && (support === "supported" || support === "unsupported") ? support : "unknown";
|
|
112
|
+
}
|
|
113
|
+
return result;
|
|
114
|
+
}
|
|
115
|
+
/** Copy only allowlisted support states. Browser never sees endpoints or SDK errors. */
|
|
116
|
+
async function projectBackendFeatures(client, authenticated) {
|
|
117
|
+
const sources = await Promise.allSettled([client.backendCapabilities?.(false, false), ...authenticated ? [client.backendCapabilities?.(true, false)] : []]);
|
|
118
|
+
const result = {};
|
|
119
|
+
for (const [index, source] of sources.entries()) if (source.status === "fulfilled") Object.assign(result, projectBackendSnapshot(source.value, index === 1));
|
|
120
|
+
else throw source.reason;
|
|
121
|
+
return result;
|
|
122
|
+
}
|
|
123
|
+
async function requireBackendFeature(client, key) {
|
|
124
|
+
const snapshot = await client.backendCapabilities?.(!key.includes("."), false);
|
|
125
|
+
const support = snapshot?.status === "confirmed" || snapshot?.status === "stale" ? snapshot.capabilities[key]?.support : "unknown";
|
|
126
|
+
if (support !== "supported") throw Object.assign(/* @__PURE__ */ new Error("Server feature unavailable"), {
|
|
127
|
+
name: "AwikiSdkError",
|
|
128
|
+
code: support === "unsupported" ? "backend-not-supported" : "backend-unconfirmed"
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
//#endregion
|
|
17
132
|
//#region lib/types/builtin-tenant-config.js
|
|
18
133
|
/** Build-injected two-slot tenant catalog with a source-tree default for tests. */
|
|
19
134
|
function record$2(value) {
|
|
@@ -2141,17 +2256,27 @@ function preferences(value, expectedOwnerDid) {
|
|
|
2141
2256
|
if (seen.has(hidden.conversation.id)) invalidState();
|
|
2142
2257
|
seen.add(hidden.conversation.id);
|
|
2143
2258
|
}
|
|
2259
|
+
const encryptedConversationIds = value.encryptedConversationIds === void 0 ? [] : value.encryptedConversationIds;
|
|
2260
|
+
if (!Array.isArray(encryptedConversationIds) || encryptedConversationIds.length > MAX_HIDDEN_CONVERSATIONS) invalidState();
|
|
2144
2261
|
return {
|
|
2262
|
+
encryptedConversationIds: encryptedConversationIds.map((id) => {
|
|
2263
|
+
const canonical = boundedString$1(id, MAX_IDENTIFIER_CHARACTERS);
|
|
2264
|
+
if (!canonical.startsWith("dm:")) invalidState();
|
|
2265
|
+
return canonical;
|
|
2266
|
+
}),
|
|
2145
2267
|
version: STORE_VERSION,
|
|
2146
2268
|
ownerDid: String(expectedOwnerDid),
|
|
2147
2269
|
hiddenConversations
|
|
2148
2270
|
};
|
|
2149
2271
|
}
|
|
2150
2272
|
function publicPreferences(value) {
|
|
2151
|
-
return {
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2273
|
+
return {
|
|
2274
|
+
encryptedConversationIds: [...value.encryptedConversationIds ?? []],
|
|
2275
|
+
hiddenConversations: value.hiddenConversations.map((hidden) => ({
|
|
2276
|
+
conversation: { ...hidden.conversation },
|
|
2277
|
+
hiddenAt: hidden.hiddenAt
|
|
2278
|
+
}))
|
|
2279
|
+
};
|
|
2155
2280
|
}
|
|
2156
2281
|
function emptyPreferences(ownerDid) {
|
|
2157
2282
|
return {
|
|
@@ -2164,6 +2289,15 @@ function emptyPreferences(ownerDid) {
|
|
|
2164
2289
|
function normalizeConversationPreferenceMutation(value) {
|
|
2165
2290
|
try {
|
|
2166
2291
|
if (!isRecord$3(value)) return void 0;
|
|
2292
|
+
if (value.action === "security" && typeof value.encrypted === "boolean") {
|
|
2293
|
+
const conversationId = boundedString$1(value.conversationId, MAX_IDENTIFIER_CHARACTERS);
|
|
2294
|
+
if (!conversationId.startsWith("dm:")) return void 0;
|
|
2295
|
+
return {
|
|
2296
|
+
action: "security",
|
|
2297
|
+
conversationId,
|
|
2298
|
+
encrypted: value.encrypted
|
|
2299
|
+
};
|
|
2300
|
+
}
|
|
2167
2301
|
if (value.action === "hide") return {
|
|
2168
2302
|
action: "hide",
|
|
2169
2303
|
conversation: conversation(value.conversation)
|
|
@@ -2196,6 +2330,17 @@ var AwikiConversationPreferenceStore = class {
|
|
|
2196
2330
|
const current = await this.load(ownerDid);
|
|
2197
2331
|
let next;
|
|
2198
2332
|
switch (normalized.action) {
|
|
2333
|
+
case "security": {
|
|
2334
|
+
const ids = new Set(current.encryptedConversationIds ?? []);
|
|
2335
|
+
if (normalized.encrypted) ids.add(normalized.conversationId);
|
|
2336
|
+
else ids.delete(normalized.conversationId);
|
|
2337
|
+
if (ids.size > MAX_HIDDEN_CONVERSATIONS) invalidState();
|
|
2338
|
+
next = {
|
|
2339
|
+
...current,
|
|
2340
|
+
encryptedConversationIds: [...ids]
|
|
2341
|
+
};
|
|
2342
|
+
break;
|
|
2343
|
+
}
|
|
2199
2344
|
case "hide": {
|
|
2200
2345
|
const snapshot = normalized.conversation;
|
|
2201
2346
|
const hidden = current.hiddenConversations.filter((item) => item.conversation.id !== snapshot.id);
|
|
@@ -2765,29 +2910,29 @@ var AwikiAgentListener = class {
|
|
|
2765
2910
|
await this.stateMutation;
|
|
2766
2911
|
const route = this.state.conversations[conversation.id];
|
|
2767
2912
|
if (route?.lastProcessedMessageId === message.id) return;
|
|
2768
|
-
if (message.content.kind !== "text") {
|
|
2913
|
+
if (message.content.kind !== "text" || message.encrypted === true && message.decryptionState !== "decrypted") {
|
|
2769
2914
|
await this.commit(conversation, message.id, route?.sessionId);
|
|
2770
2915
|
return;
|
|
2771
2916
|
}
|
|
2772
2917
|
const parsed = command(message.content.text);
|
|
2773
2918
|
if (parsed?.name === "/help") {
|
|
2774
|
-
await this.reply(conversation, message
|
|
2919
|
+
await this.reply(conversation, message, HELP_TEXT);
|
|
2775
2920
|
await this.commit(conversation, message.id, route?.sessionId);
|
|
2776
2921
|
return;
|
|
2777
2922
|
}
|
|
2778
2923
|
if (parsed?.name === "/status") {
|
|
2779
|
-
await this.reply(conversation, message
|
|
2924
|
+
await this.reply(conversation, message, route?.sessionId === void 0 ? NO_SESSION_TEXT : `当前 DSH 会话:${route.sessionId}`);
|
|
2780
2925
|
await this.commit(conversation, message.id, route?.sessionId);
|
|
2781
2926
|
return;
|
|
2782
2927
|
}
|
|
2783
2928
|
if (parsed?.name === "/new" && parsed.argument.length === 0) {
|
|
2784
2929
|
await this.agents.reset(route?.sessionId);
|
|
2785
|
-
await this.reply(conversation, message
|
|
2930
|
+
await this.reply(conversation, message, RESET_TEXT);
|
|
2786
2931
|
await this.commit(conversation, message.id, void 0);
|
|
2787
2932
|
return;
|
|
2788
2933
|
}
|
|
2789
2934
|
if (parsed !== void 0 && parsed.name !== "/new") {
|
|
2790
|
-
await this.reply(conversation, message
|
|
2935
|
+
await this.reply(conversation, message, UNKNOWN_COMMAND_TEXT);
|
|
2791
2936
|
await this.commit(conversation, message.id, route?.sessionId);
|
|
2792
2937
|
return;
|
|
2793
2938
|
}
|
|
@@ -2803,10 +2948,10 @@ var AwikiAgentListener = class {
|
|
|
2803
2948
|
} catch {
|
|
2804
2949
|
output = AGENT_FAILURE_TEXT;
|
|
2805
2950
|
}
|
|
2806
|
-
await this.reply(conversation, message
|
|
2951
|
+
await this.reply(conversation, message, output);
|
|
2807
2952
|
await this.commit(conversation, message.id, sessionId);
|
|
2808
2953
|
}
|
|
2809
|
-
async reply(conversation,
|
|
2954
|
+
async reply(conversation, message, text) {
|
|
2810
2955
|
const parts = chunks(text);
|
|
2811
2956
|
for (const [index, part] of parts.entries()) await this.awiki.sendText({
|
|
2812
2957
|
target: {
|
|
@@ -2814,7 +2959,8 @@ var AwikiAgentListener = class {
|
|
|
2814
2959
|
conversationId: conversation.id
|
|
2815
2960
|
},
|
|
2816
2961
|
text: part,
|
|
2817
|
-
|
|
2962
|
+
security: message.encrypted === true ? "e2ee-required" : "plain",
|
|
2963
|
+
idempotencyKey: replyKey(message.id, index)
|
|
2818
2964
|
});
|
|
2819
2965
|
}
|
|
2820
2966
|
async commit(conversation, messageId, sessionId) {
|
|
@@ -3137,7 +3283,7 @@ function endpointsForOrigin(origin, domain) {
|
|
|
3137
3283
|
function officialProfile(entry) {
|
|
3138
3284
|
return {
|
|
3139
3285
|
tenantId: entry.tenantId,
|
|
3140
|
-
storageScopeId:
|
|
3286
|
+
storageScopeId: randomUUID(),
|
|
3141
3287
|
kind: "built_in",
|
|
3142
3288
|
displayName: entry.displayName,
|
|
3143
3289
|
backendBaseUrl: entry.backendOrigin,
|
|
@@ -3177,10 +3323,13 @@ function decodeTenant(value) {
|
|
|
3177
3323
|
const backendBaseUrl = safeString(value.backendBaseUrl);
|
|
3178
3324
|
const didHost = safeString(value.didHost);
|
|
3179
3325
|
const endpoints = decodeEndpoints(value.endpoints);
|
|
3326
|
+
const preferenceTenantIds = value.preferenceTenantIds;
|
|
3327
|
+
if (preferenceTenantIds !== void 0 && (!Array.isArray(preferenceTenantIds) || preferenceTenantIds.some((id) => typeof id !== "string" || id.trim() === "") || new Set(preferenceTenantIds).size !== preferenceTenantIds.length)) return void 0;
|
|
3180
3328
|
if (tenantId === void 0 || storageScopeId === void 0 || displayName === void 0 || backendBaseUrl === void 0 || didHost === void 0 || endpoints === void 0 || value.kind !== "built_in" && value.kind !== "custom" || value.lifecycle !== "active" && value.lifecycle !== "inactive" && value.lifecycle !== "archived" || value.storageLayout !== "scope-v1" && value.storageLayout !== "legacy-base" && value.storageLayout !== "domain-v1") return;
|
|
3181
3329
|
return {
|
|
3182
3330
|
tenantId,
|
|
3183
3331
|
storageScopeId,
|
|
3332
|
+
...preferenceTenantIds === void 0 ? {} : { preferenceTenantIds },
|
|
3184
3333
|
kind: value.kind,
|
|
3185
3334
|
displayName,
|
|
3186
3335
|
backendBaseUrl,
|
|
@@ -3190,12 +3339,12 @@ function decodeTenant(value) {
|
|
|
3190
3339
|
endpoints
|
|
3191
3340
|
};
|
|
3192
3341
|
}
|
|
3193
|
-
function decodeDocument(value) {
|
|
3342
|
+
function decodeDocument(value, allowDuplicateScopes = false) {
|
|
3194
3343
|
if (!isRecord$1(value) || value.schemaVersion !== 1 || !Number.isSafeInteger(value.officialCatalogVersion) || value.officialCatalogVersion < 1 || value.officialCatalogVersion > 2 || !Number.isSafeInteger(value.generation) || value.generation < 0 || typeof value.activeTenantId !== "string" || !Array.isArray(value.tenants)) return void 0;
|
|
3195
3344
|
const tenants = value.tenants.map(decodeTenant);
|
|
3196
3345
|
if (tenants.some((tenant) => tenant === void 0)) return void 0;
|
|
3197
3346
|
const complete = tenants;
|
|
3198
|
-
if (new Set(complete.map((tenant) => tenant.tenantId)).size !== complete.length || new Set(complete.map((tenant) => tenant.storageScopeId)).size !== complete.length || !complete.some((tenant) => tenant.tenantId === value.activeTenantId && tenant.lifecycle === "active") || complete.filter((tenant) => tenant.lifecycle === "active").length !== 1) return void 0;
|
|
3347
|
+
if (new Set(complete.map((tenant) => tenant.tenantId)).size !== complete.length || !allowDuplicateScopes && new Set(complete.map((tenant) => tenant.storageScopeId)).size !== complete.length || complete.some((tenant) => !/^[a-zA-Z0-9_-]+$/u.test(tenant.storageScopeId)) || !complete.some((tenant) => tenant.tenantId === value.activeTenantId && tenant.lifecycle === "active") || complete.filter((tenant) => tenant.lifecycle === "active").length !== 1) return void 0;
|
|
3199
3348
|
return {
|
|
3200
3349
|
schemaVersion: 1,
|
|
3201
3350
|
officialCatalogVersion: 2,
|
|
@@ -3213,7 +3362,7 @@ function directoryHasData(path) {
|
|
|
3213
3362
|
}
|
|
3214
3363
|
function sameOfficialEndpoint(tenant, entry) {
|
|
3215
3364
|
const origin = entry.backendOrigin;
|
|
3216
|
-
return tenant.didHost === entry.didHost
|
|
3365
|
+
return tenant.didHost === entry.didHost && tenant.backendBaseUrl.replace(/\/$/u, "") === origin && tenant.endpoints.userServiceUrl.replace(/\/$/u, "") === origin;
|
|
3217
3366
|
}
|
|
3218
3367
|
function preservedTenantId(tenants, tenant) {
|
|
3219
3368
|
const base = `custom-${tenant.storageScopeId}`;
|
|
@@ -3244,6 +3393,7 @@ function reconcileOfficials(document) {
|
|
|
3244
3393
|
tenants[exactIndex] = {
|
|
3245
3394
|
...officialProfile(entry),
|
|
3246
3395
|
storageScopeId: current.storageScopeId,
|
|
3396
|
+
...current.preferenceTenantIds === void 0 ? {} : { preferenceTenantIds: current.preferenceTenantIds },
|
|
3247
3397
|
storageLayout: current.storageLayout,
|
|
3248
3398
|
lifecycle: current.lifecycle
|
|
3249
3399
|
};
|
|
@@ -3253,7 +3403,8 @@ function reconcileOfficials(document) {
|
|
|
3253
3403
|
tenants[exactIndex] = {
|
|
3254
3404
|
...current,
|
|
3255
3405
|
tenantId: preservedId,
|
|
3256
|
-
kind: "custom"
|
|
3406
|
+
kind: "custom",
|
|
3407
|
+
preferenceTenantIds: [.../* @__PURE__ */ new Set([current.tenantId, ...current.preferenceTenantIds ?? []])]
|
|
3257
3408
|
};
|
|
3258
3409
|
if (activeTenantId === current.tenantId) activeTenantId = preservedId;
|
|
3259
3410
|
exactIndex = -1;
|
|
@@ -3265,6 +3416,7 @@ function reconcileOfficials(document) {
|
|
|
3265
3416
|
tenants[legacyIndex] = {
|
|
3266
3417
|
...officialProfile(entry),
|
|
3267
3418
|
storageScopeId: current.storageScopeId,
|
|
3419
|
+
preferenceTenantIds: [.../* @__PURE__ */ new Set([current.tenantId, ...current.preferenceTenantIds ?? []])],
|
|
3268
3420
|
storageLayout: current.storageLayout,
|
|
3269
3421
|
lifecycle: current.lifecycle
|
|
3270
3422
|
};
|
|
@@ -3282,6 +3434,7 @@ function reconcileOfficials(document) {
|
|
|
3282
3434
|
tenants[endpointIndex] = {
|
|
3283
3435
|
...officialProfile(entry),
|
|
3284
3436
|
storageScopeId: current.storageScopeId,
|
|
3437
|
+
preferenceTenantIds: [.../* @__PURE__ */ new Set([current.tenantId, ...current.preferenceTenantIds ?? []])],
|
|
3285
3438
|
storageLayout: current.storageLayout,
|
|
3286
3439
|
lifecycle: current.lifecycle
|
|
3287
3440
|
};
|
|
@@ -3298,6 +3451,50 @@ function reconcileOfficials(document) {
|
|
|
3298
3451
|
function cloneDocument(document) {
|
|
3299
3452
|
return structuredClone(document);
|
|
3300
3453
|
}
|
|
3454
|
+
function tenantStateRoot(base, tenant) {
|
|
3455
|
+
if (tenant.storageLayout === "legacy-base") return resolve(base);
|
|
3456
|
+
if (tenant.storageLayout === "domain-v1") return resolve(dirname(base), "tenants", normalizeAwikiDomain(tenant.didHost), basename(base));
|
|
3457
|
+
return resolve(dirname(base), "tenant-scopes", tenant.storageScopeId, basename(base));
|
|
3458
|
+
}
|
|
3459
|
+
function validateDocument(base, document) {
|
|
3460
|
+
if (decodeDocument(document) === void 0 || new Set(document.tenants.map((tenant) => tenantStateRoot(base, tenant))).size !== document.tenants.length) throw new Error("registry schema or storage ownership invariants are invalid");
|
|
3461
|
+
}
|
|
3462
|
+
/** Repair only the known catalog-slot collision with a unique pre-migration owner. */
|
|
3463
|
+
function repairCatalogCollision(base, parsed, backupPath) {
|
|
3464
|
+
const document = decodeDocument(parsed, true);
|
|
3465
|
+
if (document === void 0 || !existsSync(backupPath)) return void 0;
|
|
3466
|
+
const backup = decodeDocument(JSON.parse(readFileSync(backupPath, "utf8")));
|
|
3467
|
+
if (backup === void 0) return void 0;
|
|
3468
|
+
let repaired = false;
|
|
3469
|
+
const tenants = [...document.tenants];
|
|
3470
|
+
for (const entry of OFFICIALS) {
|
|
3471
|
+
const group = tenants.filter((tenant) => tenant.storageScopeId === entry.storageScopeId);
|
|
3472
|
+
if (group.length !== 2) continue;
|
|
3473
|
+
const current = group.find((tenant) => tenant.tenantId === entry.tenantId && sameOfficialEndpoint(tenant, entry));
|
|
3474
|
+
const preserved = group.find((tenant) => tenant.kind === "custom" && tenant.tenantId === `custom-${entry.storageScopeId}`);
|
|
3475
|
+
if (current === void 0 || preserved === void 0 || current.storageLayout !== "scope-v1" || preserved.storageLayout !== "scope-v1") continue;
|
|
3476
|
+
const owners = backup.tenants.filter((tenant) => tenant.storageScopeId === entry.storageScopeId);
|
|
3477
|
+
const owner = owners[0];
|
|
3478
|
+
if (owners.length !== 1 || owner === void 0 || owner.storageLayout !== preserved.storageLayout || owner.didHost !== preserved.didHost || owner.backendBaseUrl !== preserved.backendBaseUrl || JSON.stringify(owner.endpoints) !== JSON.stringify(preserved.endpoints)) continue;
|
|
3479
|
+
const identityPath = join(tenantStateRoot(base, preserved), "identities", "registry.json");
|
|
3480
|
+
if (existsSync(identityPath)) {
|
|
3481
|
+
const identities = JSON.parse(readFileSync(identityPath, "utf8"));
|
|
3482
|
+
if (!isRecord$1(identities) || !isRecord$1(identities.credentials) || Object.values(identities.credentials).some((value) => !isRecord$1(value) || typeof value.did !== "string" || !value.did.startsWith(`did:wba:${preserved.didHost}:`))) continue;
|
|
3483
|
+
}
|
|
3484
|
+
tenants[tenants.indexOf(current)] = {
|
|
3485
|
+
...current,
|
|
3486
|
+
storageScopeId: randomUUID()
|
|
3487
|
+
};
|
|
3488
|
+
repaired = true;
|
|
3489
|
+
}
|
|
3490
|
+
if (!repaired) return void 0;
|
|
3491
|
+
const result = {
|
|
3492
|
+
...document,
|
|
3493
|
+
tenants
|
|
3494
|
+
};
|
|
3495
|
+
validateDocument(base, result);
|
|
3496
|
+
return result;
|
|
3497
|
+
}
|
|
3301
3498
|
/** Durable registry. All mutations are synchronous, backup-aware, and atomic. */
|
|
3302
3499
|
var AwikiTenantRegistry = class AwikiTenantRegistry {
|
|
3303
3500
|
baseStateRoot;
|
|
@@ -3317,10 +3514,16 @@ var AwikiTenantRegistry = class AwikiTenantRegistry {
|
|
|
3317
3514
|
static open(baseStateRoot, seed) {
|
|
3318
3515
|
const filePath = registryFilePath(baseStateRoot);
|
|
3319
3516
|
if (existsSync(filePath)) try {
|
|
3320
|
-
const
|
|
3517
|
+
const parsed = JSON.parse(readFileSync(filePath, "utf8"));
|
|
3518
|
+
const decoded = decodeDocument(parsed) ?? repairCatalogCollision(baseStateRoot, parsed, `${filePath}.pre-v1.bak`);
|
|
3321
3519
|
if (decoded === void 0) throw new Error("registry schema or invariants are invalid");
|
|
3322
3520
|
const registry = new AwikiTenantRegistry(baseStateRoot, reconcileOfficials(decoded), true);
|
|
3323
|
-
|
|
3521
|
+
validateDocument(baseStateRoot, registry.document);
|
|
3522
|
+
if (JSON.stringify(parsed) !== JSON.stringify(registry.document)) {
|
|
3523
|
+
const repairBackup = `${filePath}.pre-scope-repair.bak`;
|
|
3524
|
+
if (!existsSync(repairBackup)) copyFileSync(filePath, repairBackup);
|
|
3525
|
+
registry.persist(true);
|
|
3526
|
+
}
|
|
3324
3527
|
return registry;
|
|
3325
3528
|
} catch (error) {
|
|
3326
3529
|
const diagnostic = `AWiki tenant registry was not migrated: ${error instanceof Error ? error.message : "unknown error"}\n`;
|
|
@@ -3329,7 +3532,7 @@ var AwikiTenantRegistry = class AwikiTenantRegistry {
|
|
|
3329
3532
|
encoding: "utf8",
|
|
3330
3533
|
mode: 384
|
|
3331
3534
|
});
|
|
3332
|
-
|
|
3535
|
+
throw new Error("awiki: tenant registry needs repair; no environment was opened", { cause: error });
|
|
3333
3536
|
}
|
|
3334
3537
|
const hasLegacyData = directoryHasData(baseStateRoot);
|
|
3335
3538
|
const document = seed.configured || hasLegacyData ? legacyDocument(seed, hasLegacyData && !seed.configured) : freshDocument();
|
|
@@ -3354,9 +3557,7 @@ var AwikiTenantRegistry = class AwikiTenantRegistry {
|
|
|
3354
3557
|
return tenant === void 0 ? void 0 : structuredClone(tenant);
|
|
3355
3558
|
}
|
|
3356
3559
|
stateRoot(tenant) {
|
|
3357
|
-
|
|
3358
|
-
if (tenant.storageLayout === "domain-v1") return join(dirname(this.baseStateRoot), "tenants", tenant.didHost, basename(this.baseStateRoot));
|
|
3359
|
-
return join(dirname(this.baseStateRoot), "tenant-scopes", tenant.storageScopeId, basename(this.baseStateRoot));
|
|
3560
|
+
return tenantStateRoot(this.baseStateRoot, tenant);
|
|
3360
3561
|
}
|
|
3361
3562
|
createCustom(displayName, rawDomain) {
|
|
3362
3563
|
const domain = normalizeAwikiDomain(rawDomain);
|
|
@@ -3426,6 +3627,7 @@ var AwikiTenantRegistry = class AwikiTenantRegistry {
|
|
|
3426
3627
|
return cloneDocument(this.document);
|
|
3427
3628
|
}
|
|
3428
3629
|
persist(backup) {
|
|
3630
|
+
validateDocument(this.baseStateRoot, this.document);
|
|
3429
3631
|
if (!this.persistent) throw new Error("awiki: tenant registry is read-only until its migration diagnostic is resolved");
|
|
3430
3632
|
mkdirSync(dirname(this.filePath), { recursive: true });
|
|
3431
3633
|
if (backup && existsSync(this.filePath) && !existsSync(this.backupPath)) copyFileSync(this.filePath, this.backupPath);
|
|
@@ -3569,11 +3771,6 @@ function checkInstallation(requirements, installed) {
|
|
|
3569
3771
|
return {};
|
|
3570
3772
|
}
|
|
3571
3773
|
//#endregion
|
|
3572
|
-
//#region lib/types/package-version.generated.js
|
|
3573
|
-
/** Generated from package.json by scripts/sync-package-versions.mjs. */
|
|
3574
|
-
const DSH_AWIKI_PACKAGE_VERSION = "0.3.17";
|
|
3575
|
-
const DSH_AWIKI_MODEL_PROXY_PACKAGE_VERSION = "0.1.11";
|
|
3576
|
-
//#endregion
|
|
3577
3774
|
//#region lib/types/update-policy.js
|
|
3578
3775
|
/** Tenant-scoped DSH AWiki plugin update policy and verified cache. */
|
|
3579
3776
|
const DSH_AWIKI_VERSION = DSH_AWIKI_PACKAGE_VERSION;
|
|
@@ -3636,6 +3833,7 @@ async function checkAwikiUpdatePolicy(options) {
|
|
|
3636
3833
|
signal.throwIfAborted();
|
|
3637
3834
|
const endpoint = new URL("/user-service/v1/server-info", origin);
|
|
3638
3835
|
endpoint.searchParams.set("client_platform", "dsh");
|
|
3836
|
+
endpoint.searchParams.set("capabilities_version", "1");
|
|
3639
3837
|
const response = await bounded((options.fetcher ?? fetch)(endpoint, {
|
|
3640
3838
|
method: "GET",
|
|
3641
3839
|
headers: {
|
|
@@ -3664,7 +3862,19 @@ async function checkAwikiUpdatePolicy(options) {
|
|
|
3664
3862
|
const bytes = await bounded(readPolicyBody(response));
|
|
3665
3863
|
signal.throwIfAborted();
|
|
3666
3864
|
if (bytes.byteLength > MAX_POLICY_BYTES) throw new Error("policy response exceeds 1 MiB");
|
|
3667
|
-
const
|
|
3865
|
+
const document = JSON.parse(Buffer.from(bytes).toString("utf8"));
|
|
3866
|
+
if (isRecord(document) && document.schema_version === 1 && document.capabilities_version === 1 && isRecord(document.client_updates) && isRecord(document.client_updates.policy) && document.client_updates.policy.protocol_version === 1 && document.client_updates.policy.enabled === false) return {
|
|
3867
|
+
...cached === void 0 ? {
|
|
3868
|
+
...base,
|
|
3869
|
+
usedCache: false,
|
|
3870
|
+
restricted: false,
|
|
3871
|
+
modelProxyRestricted: false
|
|
3872
|
+
} : statusFromPolicy(base, cached.policy, cached.checkedAt, false, true, options.installedRuntime),
|
|
3873
|
+
offline: false,
|
|
3874
|
+
policyUnavailable: true,
|
|
3875
|
+
checkState: "unavailable"
|
|
3876
|
+
};
|
|
3877
|
+
const policy = decodeServerInfoPolicy(document, origin, cached?.policy.policy_revision);
|
|
3668
3878
|
if (policy === void 0) {
|
|
3669
3879
|
rmSync(cachePath, { force: true });
|
|
3670
3880
|
return {
|
|
@@ -3994,6 +4204,8 @@ const Config = z.object({
|
|
|
3994
4204
|
summaryMaxInputBytes: z.number().default(DEFAULT_SUMMARY_MAX_INPUT_BYTES)
|
|
3995
4205
|
});
|
|
3996
4206
|
const FAILURE_CODES = /* @__PURE__ */ new Set([
|
|
4207
|
+
"backend-not-supported",
|
|
4208
|
+
"backend-unconfirmed",
|
|
3997
4209
|
"client-version-unsupported",
|
|
3998
4210
|
"not-registered",
|
|
3999
4211
|
"signed-out",
|
|
@@ -4011,6 +4223,7 @@ const FAILURE_CODES = /* @__PURE__ */ new Set([
|
|
|
4011
4223
|
"state-in-use",
|
|
4012
4224
|
"rate-limited",
|
|
4013
4225
|
"group-membership-required",
|
|
4226
|
+
"group-member-encryption-not-ready",
|
|
4014
4227
|
"group-identity-stale",
|
|
4015
4228
|
"attachment-too-large",
|
|
4016
4229
|
"summary-unavailable",
|
|
@@ -4033,6 +4246,8 @@ const RESUMABLE_JOIN_PHASES = /* @__PURE__ */ new Set([
|
|
|
4033
4246
|
"approval_prepared"
|
|
4034
4247
|
]);
|
|
4035
4248
|
const FAILURE_MESSAGES = {
|
|
4249
|
+
"backend-not-supported": "This server does not support this feature.",
|
|
4250
|
+
"backend-unconfirmed": "Server features are not confirmed yet. Please retry.",
|
|
4036
4251
|
"client-version-unsupported": "This AWiki plugin version is not supported by the active tenant. Update the plugin or switch tenants.",
|
|
4037
4252
|
"not-registered": "No AWiki identity is registered for this deployment.",
|
|
4038
4253
|
"signed-out": "This installation is signed out of AWiki.",
|
|
@@ -4049,6 +4264,7 @@ const FAILURE_MESSAGES = {
|
|
|
4049
4264
|
"conflict": "The AWiki operation conflicts with current state.",
|
|
4050
4265
|
"state-in-use": "AWiki data is already open in another client.",
|
|
4051
4266
|
"rate-limited": "The AWiki service rate-limited the request.",
|
|
4267
|
+
"group-member-encryption-not-ready": "The member has not prepared encrypted messaging.",
|
|
4052
4268
|
"group-membership-required": "The active AWiki identity is not a member of this group.",
|
|
4053
4269
|
"group-identity-stale": "The AWiki group identity binding is still recovering.",
|
|
4054
4270
|
"attachment-too-large": "The attachment exceeds this deployment's size limit.",
|
|
@@ -4114,7 +4330,7 @@ function publishedServiceBaseUrl(services, name, allowInsecureLoopbackForTesting
|
|
|
4114
4330
|
const candidate = services[name];
|
|
4115
4331
|
if (typeof candidate !== "object" || candidate === null || Array.isArray(candidate)) return void 0;
|
|
4116
4332
|
const value = candidate;
|
|
4117
|
-
if (value.enabled !== true || typeof value.base_url !== "string") return void 0;
|
|
4333
|
+
if (value.enabled !== true || value.protocol_version !== 1 || typeof value.base_url !== "string") return void 0;
|
|
4118
4334
|
try {
|
|
4119
4335
|
const baseUrl = serviceUrl(`server-info.services.${name}.base_url`, value.base_url, allowInsecureLoopbackForTesting);
|
|
4120
4336
|
if (new URL(baseUrl).search !== "") return void 0;
|
|
@@ -4258,6 +4474,8 @@ const MAX_PROFILE_TAGS = 5;
|
|
|
4258
4474
|
const MAX_PROFILE_TAG_CHARACTERS = 30;
|
|
4259
4475
|
const MAX_MESSAGE_CHARACTERS = 2e4;
|
|
4260
4476
|
function normalizeCreateGroupRequest(request) {
|
|
4477
|
+
if (request?.operationId !== void 0 && (typeof request.operationId !== "string" || !/^[a-zA-Z0-9_-]{1,100}$/.test(request.operationId))) return void 0;
|
|
4478
|
+
if (request?.encrypted !== void 0 && typeof request.encrypted !== "boolean") return void 0;
|
|
4261
4479
|
if (typeof request?.name !== "string" || !Array.isArray(request.members)) return void 0;
|
|
4262
4480
|
const name = request.name.trim();
|
|
4263
4481
|
if (name.length === 0 || Array.from(name).length > MAX_GROUP_NAME_CHARACTERS) return void 0;
|
|
@@ -4274,7 +4492,9 @@ function normalizeCreateGroupRequest(request) {
|
|
|
4274
4492
|
}
|
|
4275
4493
|
return {
|
|
4276
4494
|
name,
|
|
4277
|
-
members
|
|
4495
|
+
members,
|
|
4496
|
+
...request.operationId === void 0 ? {} : { operationId: request.operationId },
|
|
4497
|
+
...request.encrypted === void 0 ? {} : { encrypted: request.encrypted }
|
|
4278
4498
|
};
|
|
4279
4499
|
}
|
|
4280
4500
|
function normalizeMember(value) {
|
|
@@ -4286,6 +4506,22 @@ function normalizeGroupDid(value) {
|
|
|
4286
4506
|
if (typeof value !== "string" || !value.startsWith("did:") || value.length > 2048) return void 0;
|
|
4287
4507
|
return value;
|
|
4288
4508
|
}
|
|
4509
|
+
function validAvatarRequest(request, image) {
|
|
4510
|
+
if (request === null || typeof request !== "object" || Array.isArray(request)) return false;
|
|
4511
|
+
const value = request;
|
|
4512
|
+
const keys = image ? [
|
|
4513
|
+
"requestId",
|
|
4514
|
+
"expectedProfileVersion",
|
|
4515
|
+
"imageBase64"
|
|
4516
|
+
] : ["requestId", "expectedProfileVersion"];
|
|
4517
|
+
if (Object.keys(value).length !== keys.length || Object.keys(value).some((key) => !keys.includes(key))) return false;
|
|
4518
|
+
if (typeof value.requestId !== "string" || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/u.test(value.requestId)) return false;
|
|
4519
|
+
if (typeof value.expectedProfileVersion !== "string" || !/^(0|[1-9][0-9]{0,18})$/u.test(value.expectedProfileVersion) || BigInt(value.expectedProfileVersion) > 9223372036854775807n) return false;
|
|
4520
|
+
if (!image) return true;
|
|
4521
|
+
if (typeof value.imageBase64 !== "string" || value.imageBase64.length === 0 || value.imageBase64.length > 699052) return false;
|
|
4522
|
+
const bytes = Buffer.from(value.imageBase64, "base64");
|
|
4523
|
+
return bytes.length <= 524288 && bytes.length >= 4 && bytes[0] === 255 && bytes[1] === 216 && bytes[bytes.length - 2] === 255 && bytes[bytes.length - 1] === 217 && bytes.toString("base64") === value.imageBase64;
|
|
4524
|
+
}
|
|
4289
4525
|
function normalizeProfileRequest(request) {
|
|
4290
4526
|
if (typeof request?.displayName !== "string" || typeof request.bio !== "string" || !Array.isArray(request.tags)) return void 0;
|
|
4291
4527
|
const displayName = request.displayName.trim();
|
|
@@ -4385,6 +4621,7 @@ function identityAccessTarget(request, configuredDomain) {
|
|
|
4385
4621
|
};
|
|
4386
4622
|
}
|
|
4387
4623
|
function normalizeSendTextRequest(request) {
|
|
4624
|
+
if (request?.security !== void 0 && request.security !== "plain" && request.security !== "e2ee-required") return void 0;
|
|
4388
4625
|
if (typeof request?.text !== "string" || typeof request.idempotencyKey !== "string" || !isMessageTarget(request.target)) return void 0;
|
|
4389
4626
|
const text = request.text;
|
|
4390
4627
|
const codePoints = Array.from(text);
|
|
@@ -4544,6 +4781,7 @@ let AwikiService = (() => {
|
|
|
4544
4781
|
let _classSuper = TypertRemoteService;
|
|
4545
4782
|
let _instanceExtraInitializers = [];
|
|
4546
4783
|
let _getConfig_decorators;
|
|
4784
|
+
let _refreshBackendCapabilities_decorators;
|
|
4547
4785
|
let _getIntegration_decorators;
|
|
4548
4786
|
let _createIntegration_decorators;
|
|
4549
4787
|
let _updateIntegration_decorators;
|
|
@@ -4574,6 +4812,8 @@ let AwikiService = (() => {
|
|
|
4574
4812
|
let _prepareRootTransfer_decorators;
|
|
4575
4813
|
let _confirmRootTransfer_decorators;
|
|
4576
4814
|
let _updateDisplayName_decorators;
|
|
4815
|
+
let _setAvatar_decorators;
|
|
4816
|
+
let _clearAvatar_decorators;
|
|
4577
4817
|
let _getProfile_decorators;
|
|
4578
4818
|
let _updateProfile_decorators;
|
|
4579
4819
|
let _sendRecoveryOtp_decorators;
|
|
@@ -4586,6 +4826,7 @@ let AwikiService = (() => {
|
|
|
4586
4826
|
let _resolvePeer_decorators;
|
|
4587
4827
|
let _createGroup_decorators;
|
|
4588
4828
|
let _getGroup_decorators;
|
|
4829
|
+
let _repairGroupSecurity_decorators;
|
|
4589
4830
|
let _joinGroup_decorators;
|
|
4590
4831
|
let _leaveGroup_decorators;
|
|
4591
4832
|
let _getDisplayProfiles_decorators;
|
|
@@ -4613,6 +4854,7 @@ let AwikiService = (() => {
|
|
|
4613
4854
|
static {
|
|
4614
4855
|
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
4615
4856
|
_getConfig_decorators = [Remote];
|
|
4857
|
+
_refreshBackendCapabilities_decorators = [Remote];
|
|
4616
4858
|
_getIntegration_decorators = [Remote];
|
|
4617
4859
|
_createIntegration_decorators = [Remote];
|
|
4618
4860
|
_updateIntegration_decorators = [Remote];
|
|
@@ -4643,6 +4885,8 @@ let AwikiService = (() => {
|
|
|
4643
4885
|
_prepareRootTransfer_decorators = [Remote];
|
|
4644
4886
|
_confirmRootTransfer_decorators = [Remote];
|
|
4645
4887
|
_updateDisplayName_decorators = [Remote];
|
|
4888
|
+
_setAvatar_decorators = [Remote];
|
|
4889
|
+
_clearAvatar_decorators = [Remote];
|
|
4646
4890
|
_getProfile_decorators = [Remote];
|
|
4647
4891
|
_updateProfile_decorators = [Remote];
|
|
4648
4892
|
_sendRecoveryOtp_decorators = [Remote];
|
|
@@ -4655,6 +4899,7 @@ let AwikiService = (() => {
|
|
|
4655
4899
|
_resolvePeer_decorators = [Remote];
|
|
4656
4900
|
_createGroup_decorators = [Remote];
|
|
4657
4901
|
_getGroup_decorators = [Remote];
|
|
4902
|
+
_repairGroupSecurity_decorators = [Remote];
|
|
4658
4903
|
_joinGroup_decorators = [Remote];
|
|
4659
4904
|
_leaveGroup_decorators = [Remote];
|
|
4660
4905
|
_getDisplayProfiles_decorators = [Remote];
|
|
@@ -4689,6 +4934,17 @@ let AwikiService = (() => {
|
|
|
4689
4934
|
},
|
|
4690
4935
|
metadata: _metadata
|
|
4691
4936
|
}, null, _instanceExtraInitializers);
|
|
4937
|
+
__esDecorate(this, null, _refreshBackendCapabilities_decorators, {
|
|
4938
|
+
kind: "method",
|
|
4939
|
+
name: "refreshBackendCapabilities",
|
|
4940
|
+
static: false,
|
|
4941
|
+
private: false,
|
|
4942
|
+
access: {
|
|
4943
|
+
has: (obj) => "refreshBackendCapabilities" in obj,
|
|
4944
|
+
get: (obj) => obj.refreshBackendCapabilities
|
|
4945
|
+
},
|
|
4946
|
+
metadata: _metadata
|
|
4947
|
+
}, null, _instanceExtraInitializers);
|
|
4692
4948
|
__esDecorate(this, null, _getIntegration_decorators, {
|
|
4693
4949
|
kind: "method",
|
|
4694
4950
|
name: "getIntegration",
|
|
@@ -5019,6 +5275,28 @@ let AwikiService = (() => {
|
|
|
5019
5275
|
},
|
|
5020
5276
|
metadata: _metadata
|
|
5021
5277
|
}, null, _instanceExtraInitializers);
|
|
5278
|
+
__esDecorate(this, null, _setAvatar_decorators, {
|
|
5279
|
+
kind: "method",
|
|
5280
|
+
name: "setAvatar",
|
|
5281
|
+
static: false,
|
|
5282
|
+
private: false,
|
|
5283
|
+
access: {
|
|
5284
|
+
has: (obj) => "setAvatar" in obj,
|
|
5285
|
+
get: (obj) => obj.setAvatar
|
|
5286
|
+
},
|
|
5287
|
+
metadata: _metadata
|
|
5288
|
+
}, null, _instanceExtraInitializers);
|
|
5289
|
+
__esDecorate(this, null, _clearAvatar_decorators, {
|
|
5290
|
+
kind: "method",
|
|
5291
|
+
name: "clearAvatar",
|
|
5292
|
+
static: false,
|
|
5293
|
+
private: false,
|
|
5294
|
+
access: {
|
|
5295
|
+
has: (obj) => "clearAvatar" in obj,
|
|
5296
|
+
get: (obj) => obj.clearAvatar
|
|
5297
|
+
},
|
|
5298
|
+
metadata: _metadata
|
|
5299
|
+
}, null, _instanceExtraInitializers);
|
|
5022
5300
|
__esDecorate(this, null, _getProfile_decorators, {
|
|
5023
5301
|
kind: "method",
|
|
5024
5302
|
name: "getProfile",
|
|
@@ -5151,6 +5429,17 @@ let AwikiService = (() => {
|
|
|
5151
5429
|
},
|
|
5152
5430
|
metadata: _metadata
|
|
5153
5431
|
}, null, _instanceExtraInitializers);
|
|
5432
|
+
__esDecorate(this, null, _repairGroupSecurity_decorators, {
|
|
5433
|
+
kind: "method",
|
|
5434
|
+
name: "repairGroupSecurity",
|
|
5435
|
+
static: false,
|
|
5436
|
+
private: false,
|
|
5437
|
+
access: {
|
|
5438
|
+
has: (obj) => "repairGroupSecurity" in obj,
|
|
5439
|
+
get: (obj) => obj.repairGroupSecurity
|
|
5440
|
+
},
|
|
5441
|
+
metadata: _metadata
|
|
5442
|
+
}, null, _instanceExtraInitializers);
|
|
5154
5443
|
__esDecorate(this, null, _joinGroup_decorators, {
|
|
5155
5444
|
kind: "method",
|
|
5156
5445
|
name: "joinGroup",
|
|
@@ -5428,6 +5717,15 @@ let AwikiService = (() => {
|
|
|
5428
5717
|
tenantSwitching = false;
|
|
5429
5718
|
tenantParticipants = /* @__PURE__ */ new Set();
|
|
5430
5719
|
activeCapabilities;
|
|
5720
|
+
backendFeatures = {};
|
|
5721
|
+
backendDiagnostics = {};
|
|
5722
|
+
backendFeaturesTask;
|
|
5723
|
+
backendFeaturesRevision = -1;
|
|
5724
|
+
backendSubscriptions = /* @__PURE__ */ new Map();
|
|
5725
|
+
backendSubscriptionPending = /* @__PURE__ */ new Set();
|
|
5726
|
+
backendAttachmentLimit;
|
|
5727
|
+
capabilityDiscoveryTask;
|
|
5728
|
+
capabilityDiscoveryAt = 0;
|
|
5431
5729
|
updatePolicyStatus;
|
|
5432
5730
|
updatePolicyRequest;
|
|
5433
5731
|
updatePolicyTask;
|
|
@@ -5524,6 +5822,7 @@ let AwikiService = (() => {
|
|
|
5524
5822
|
registerAwikiTools(ctx, this);
|
|
5525
5823
|
ctx.effect(() => async () => {
|
|
5526
5824
|
this.updatePolicyRequest?.abort();
|
|
5825
|
+
this.stopBackendSubscriptions();
|
|
5527
5826
|
const provider = this.provider;
|
|
5528
5827
|
if (provider !== void 0) await this.disposeProvider(provider);
|
|
5529
5828
|
}, "awiki: dispose current client provider");
|
|
@@ -5607,12 +5906,49 @@ let AwikiService = (() => {
|
|
|
5607
5906
|
this.conversationPreferenceStore = new AwikiConversationPreferenceStore(options.stateRoot);
|
|
5608
5907
|
this.mailListClient = new AwikiMailListClient(options.mailServiceUrl, this.externalHttpAuth);
|
|
5609
5908
|
this.integrationClient = void 0;
|
|
5909
|
+
this.stopBackendSubscriptions();
|
|
5910
|
+
this.backendFeatures = {};
|
|
5911
|
+
this.backendDiagnostics = {};
|
|
5912
|
+
this.backendFeaturesRevision = -1;
|
|
5913
|
+
this.backendFeaturesTask = void 0;
|
|
5914
|
+
this.backendAttachmentLimit = void 0;
|
|
5915
|
+
this.capabilityDiscoveryTask = void 0;
|
|
5916
|
+
this.capabilityDiscoveryAt = 0;
|
|
5610
5917
|
this.activeCapabilities = {
|
|
5611
5918
|
tenantId: tenant.tenantId,
|
|
5612
5919
|
generation: this.runtimeGeneration,
|
|
5613
5920
|
online: false,
|
|
5614
5921
|
handleRecoveryPhoneEnabled: false
|
|
5615
5922
|
};
|
|
5923
|
+
const metadata = new TenantCapabilityCache(options.stateRoot, options.userServiceUrl).read((value) => {
|
|
5924
|
+
const services = {
|
|
5925
|
+
model_proxy: {
|
|
5926
|
+
enabled: true,
|
|
5927
|
+
protocol_version: 1,
|
|
5928
|
+
base_url: value.modelProxyBaseUrl
|
|
5929
|
+
},
|
|
5930
|
+
guest_gateway: {
|
|
5931
|
+
enabled: true,
|
|
5932
|
+
protocol_version: 1,
|
|
5933
|
+
base_url: value.guestGatewayBaseUrl
|
|
5934
|
+
}
|
|
5935
|
+
};
|
|
5936
|
+
const modelProxyBaseUrl = publishedServiceBaseUrl(services, "model_proxy", this.resolved.allowInsecureLoopbackForTesting);
|
|
5937
|
+
const guestGatewayBaseUrl = publishedServiceBaseUrl(services, "guest_gateway", false);
|
|
5938
|
+
return {
|
|
5939
|
+
handleRecoveryPhoneEnabled: value.handleRecoveryPhoneEnabled,
|
|
5940
|
+
...modelProxyBaseUrl === void 0 ? {} : { modelProxyBaseUrl },
|
|
5941
|
+
...guestGatewayBaseUrl === void 0 ? {} : { guestGatewayBaseUrl }
|
|
5942
|
+
};
|
|
5943
|
+
});
|
|
5944
|
+
if (metadata !== void 0) {
|
|
5945
|
+
this.activeCapabilities = {
|
|
5946
|
+
...this.activeCapabilities,
|
|
5947
|
+
...metadata,
|
|
5948
|
+
online: true
|
|
5949
|
+
};
|
|
5950
|
+
this.integrationClient = metadata.guestGatewayBaseUrl === void 0 ? void 0 : new AwikiIntegrationClient(metadata.guestGatewayBaseUrl, this.externalHttpAuth);
|
|
5951
|
+
}
|
|
5616
5952
|
this.signedOut = void 0;
|
|
5617
5953
|
this.activeIdentityDid = void 0;
|
|
5618
5954
|
this.pendingDeviceJoin = void 0;
|
|
@@ -5773,6 +6109,11 @@ let AwikiService = (() => {
|
|
|
5773
6109
|
let committed = false;
|
|
5774
6110
|
try {
|
|
5775
6111
|
await this.sessionMutation;
|
|
6112
|
+
await this.sessionStore.signOut();
|
|
6113
|
+
this.signedOut = true;
|
|
6114
|
+
this.sessionRevision += 1;
|
|
6115
|
+
this.activeIdentityDid = void 0;
|
|
6116
|
+
await new AwikiSessionStore(registry.stateRoot(target)).signOut();
|
|
5776
6117
|
this.invalidateSummaries();
|
|
5777
6118
|
for (const participant of this.tenantParticipants) {
|
|
5778
6119
|
await participant.prepareSwitch(context);
|
|
@@ -5781,8 +6122,11 @@ let AwikiService = (() => {
|
|
|
5781
6122
|
await this.disposeProvider(previousProvider);
|
|
5782
6123
|
const targetProvider = this.openTenantProvider(target, factory, false);
|
|
5783
6124
|
targetOpened = true;
|
|
5784
|
-
await targetProvider.client.getIdentity()
|
|
5785
|
-
|
|
6125
|
+
if (await targetProvider.client.getIdentity() === null) {
|
|
6126
|
+
await this.sessionStore.signIn();
|
|
6127
|
+
this.signedOut = false;
|
|
6128
|
+
}
|
|
6129
|
+
Promise.all([this.refreshTenantCapabilities(), this.refreshUpdatePolicy()]).catch(() => void 0);
|
|
5786
6130
|
registry.commitActive(target.tenantId);
|
|
5787
6131
|
committed = true;
|
|
5788
6132
|
for (const participant of prepared) await participant.commitSwitch?.(context);
|
|
@@ -5860,16 +6204,22 @@ let AwikiService = (() => {
|
|
|
5860
6204
|
*/
|
|
5861
6205
|
async getConfig() {
|
|
5862
6206
|
this.ensureTenantOptions();
|
|
5863
|
-
|
|
5864
|
-
if (this.tenantSwitching || capabilities.generation !== this.runtimeGeneration || capabilities.tenantId !== this.activeTenant?.tenantId) return {
|
|
6207
|
+
if (this.runtimeGeneration !== this.runtimeGeneration) return {
|
|
5865
6208
|
ok: false,
|
|
5866
6209
|
error: failure("conflict")
|
|
5867
6210
|
};
|
|
6211
|
+
const capabilities = this.activeCapabilities;
|
|
6212
|
+
this.refreshBackendProjection();
|
|
6213
|
+
this.refreshTenantCapabilities(false).catch(() => void 0);
|
|
6214
|
+
const serverFeatures = this.backendFeatures;
|
|
6215
|
+
const attachmentMaxBytes = this.backendAttachmentLimit ?? this.resolved.attachmentMaxBytes;
|
|
5868
6216
|
return {
|
|
5869
6217
|
ok: true,
|
|
5870
6218
|
value: {
|
|
6219
|
+
serverFeatures,
|
|
6220
|
+
serverFeatureDiagnostics: this.backendDiagnostics,
|
|
5871
6221
|
pollIntervalMs: this.resolved.pollIntervalMs,
|
|
5872
|
-
attachmentMaxBytes
|
|
6222
|
+
attachmentMaxBytes,
|
|
5873
6223
|
mailAttachmentMaxCount: this.resolved.mailAttachmentMaxCount,
|
|
5874
6224
|
mailAttachmentMaxBytes: Math.min(this.resolved.mailAttachmentMaxBytes, MAIL_HTTP_UPLOAD_MAX_BYTES),
|
|
5875
6225
|
mailAttachmentDownloadMaxBytes: this.resolved.mailAttachmentMaxBytes,
|
|
@@ -5877,6 +6227,7 @@ let AwikiService = (() => {
|
|
|
5877
6227
|
handleRecoveryPhoneEnabled: capabilities.handleRecoveryPhoneEnabled,
|
|
5878
6228
|
...capabilities.guestGatewayBaseUrl === void 0 ? {} : { integrationGuideUrl: new URL("/guest/guide/integration", capabilities.guestGatewayBaseUrl).toString() },
|
|
5879
6229
|
tenantId: capabilities.tenantId,
|
|
6230
|
+
tenantStorageScopeId: this.activeTenant.storageScopeId,
|
|
5880
6231
|
tenantGeneration: capabilities.generation,
|
|
5881
6232
|
tenantOnline: capabilities.online,
|
|
5882
6233
|
services: {
|
|
@@ -5886,6 +6237,17 @@ let AwikiService = (() => {
|
|
|
5886
6237
|
}
|
|
5887
6238
|
};
|
|
5888
6239
|
}
|
|
6240
|
+
/** Explicit discovery retry; ordinary config reads retain their zero-argument contract. */
|
|
6241
|
+
async refreshBackendCapabilities() {
|
|
6242
|
+
this.ensureTenantOptions();
|
|
6243
|
+
const generation = this.runtimeGeneration;
|
|
6244
|
+
await Promise.allSettled([this.refreshTenantCapabilities(true), this.refreshBackendProjection(true)]);
|
|
6245
|
+
if (generation !== this.runtimeGeneration) return {
|
|
6246
|
+
ok: false,
|
|
6247
|
+
error: failure("conflict")
|
|
6248
|
+
};
|
|
6249
|
+
return this.getConfig();
|
|
6250
|
+
}
|
|
5889
6251
|
/** Trusted same-process capability binding for optional tenant participants. */
|
|
5890
6252
|
getTenantCapabilities() {
|
|
5891
6253
|
const capabilities = this.activeCapabilities;
|
|
@@ -5893,16 +6255,117 @@ let AwikiService = (() => {
|
|
|
5893
6255
|
return { ...capabilities };
|
|
5894
6256
|
}
|
|
5895
6257
|
/** Refresh the active tenant's optional service advertisement without affecting Core availability. */
|
|
5896
|
-
refreshTenantCapabilities() {
|
|
6258
|
+
refreshTenantCapabilities(force = true) {
|
|
5897
6259
|
this.ensureTenantOptions();
|
|
5898
|
-
return this.
|
|
6260
|
+
if (this.capabilityDiscoveryTask !== void 0) return this.capabilityDiscoveryTask;
|
|
6261
|
+
if (!force && Date.now() - this.capabilityDiscoveryAt < 3e5 && this.activeCapabilities !== void 0) return Promise.resolve(this.activeCapabilities);
|
|
6262
|
+
const generation = this.runtimeGeneration;
|
|
6263
|
+
const task = this.discoverTenantCapabilities(1e4).finally(() => {
|
|
6264
|
+
if (this.runtimeGeneration === generation && this.capabilityDiscoveryTask === task) {
|
|
6265
|
+
this.capabilityDiscoveryTask = void 0;
|
|
6266
|
+
this.capabilityDiscoveryAt = Date.now();
|
|
6267
|
+
}
|
|
6268
|
+
});
|
|
6269
|
+
this.capabilityDiscoveryTask = task;
|
|
6270
|
+
return task;
|
|
6271
|
+
}
|
|
6272
|
+
refreshBackendProjection(force = false) {
|
|
6273
|
+
const provider = this.provider;
|
|
6274
|
+
if (provider === void 0) return Promise.resolve();
|
|
6275
|
+
if (this.backendFeaturesTask !== void 0) return this.backendFeaturesTask;
|
|
6276
|
+
const revision = this.sessionRevision;
|
|
6277
|
+
const generation = this.runtimeGeneration;
|
|
6278
|
+
if (this.backendFeaturesRevision !== revision) {
|
|
6279
|
+
this.backendFeatures = Object.fromEntries(Object.entries(this.backendFeatures).filter(([key]) => key.includes(".")));
|
|
6280
|
+
this.backendAttachmentLimit = void 0;
|
|
6281
|
+
this.backendDiagnostics = this.backendDiagnostics.public === void 0 ? {} : { public: this.backendDiagnostics.public };
|
|
6282
|
+
this.backendFeaturesRevision = revision;
|
|
6283
|
+
}
|
|
6284
|
+
const current = () => provider === this.provider && revision === this.sessionRevision && generation === this.runtimeGeneration;
|
|
6285
|
+
const task = (async () => {
|
|
6286
|
+
const authenticated = !await this.isSignedOut() && await provider.client.getIdentity() !== null;
|
|
6287
|
+
await Promise.all([false, ...authenticated ? [true] : []].map(async (local) => {
|
|
6288
|
+
try {
|
|
6289
|
+
this.observeBackendProjection(provider, local, current);
|
|
6290
|
+
const snapshot = await provider.client.backendCapabilities?.(local, force);
|
|
6291
|
+
if (!current()) return;
|
|
6292
|
+
if (snapshot === void 0) return;
|
|
6293
|
+
if (this.backendSubscriptions.has(local)) return;
|
|
6294
|
+
this.recordBackendDiagnostics(local, snapshot);
|
|
6295
|
+
if (snapshot.status === "unavailable") return;
|
|
6296
|
+
this.backendFeatures = {
|
|
6297
|
+
...this.backendFeatures,
|
|
6298
|
+
...projectBackendSnapshot(snapshot, local)
|
|
6299
|
+
};
|
|
6300
|
+
if (local) this.backendAttachmentLimit = backendAttachmentMaxBytes(snapshot, this.resolved.attachmentMaxBytes);
|
|
6301
|
+
if (this.backendFeatures["agents.managed_agents"] === "unsupported") await this.detachAgentConsumer(provider);
|
|
6302
|
+
else if (this.backendFeatures["agents.managed_agents"] === "supported") this.ensureAgentConsumer(provider);
|
|
6303
|
+
} catch (error) {
|
|
6304
|
+
if (current()) this.ctx.logger("awiki-capabilities").debug("AWiki capability refresh failed: %s", error instanceof AwikiSdkError ? error.code : "unavailable");
|
|
6305
|
+
}
|
|
6306
|
+
}));
|
|
6307
|
+
})().finally(() => {
|
|
6308
|
+
if (this.backendFeaturesTask === task) this.backendFeaturesTask = void 0;
|
|
6309
|
+
});
|
|
6310
|
+
this.backendFeaturesTask = task;
|
|
6311
|
+
task.catch(() => void 0);
|
|
6312
|
+
return task;
|
|
6313
|
+
}
|
|
6314
|
+
recordBackendDiagnostics(local, snapshot) {
|
|
6315
|
+
this.backendDiagnostics = {
|
|
6316
|
+
...this.backendDiagnostics,
|
|
6317
|
+
[local ? "message" : "public"]: {
|
|
6318
|
+
...typeof snapshot.fetched_at === "number" ? { fetchedAt: snapshot.fetched_at } : {},
|
|
6319
|
+
...snapshot.refresh_error === void 0 ? {} : { refreshError: snapshot.refresh_error }
|
|
6320
|
+
}
|
|
6321
|
+
};
|
|
6322
|
+
}
|
|
6323
|
+
stopBackendSubscriptions() {
|
|
6324
|
+
for (const subscription of this.backendSubscriptions.values()) subscription.stop();
|
|
6325
|
+
this.backendSubscriptions.clear();
|
|
6326
|
+
this.backendSubscriptionPending.clear();
|
|
6327
|
+
}
|
|
6328
|
+
observeBackendProjection(provider, local, current) {
|
|
6329
|
+
const subscribe = provider.client.subscribeBackendCapabilities;
|
|
6330
|
+
if (subscribe === void 0 || this.backendSubscriptions.has(local) || this.backendSubscriptionPending.has(local)) return;
|
|
6331
|
+
this.backendSubscriptionPending.add(local);
|
|
6332
|
+
(async () => {
|
|
6333
|
+
const subscription = await subscribe.call(provider.client, local);
|
|
6334
|
+
if (!current()) {
|
|
6335
|
+
subscription.stop();
|
|
6336
|
+
return;
|
|
6337
|
+
}
|
|
6338
|
+
this.backendSubscriptionPending.delete(local);
|
|
6339
|
+
this.backendSubscriptions.set(local, subscription);
|
|
6340
|
+
try {
|
|
6341
|
+
while (current()) {
|
|
6342
|
+
const state = await subscription.nextUpdate();
|
|
6343
|
+
if (state === null || !current()) break;
|
|
6344
|
+
this.recordBackendDiagnostics(local, state.snapshot);
|
|
6345
|
+
if (state.snapshot.status === "unavailable") continue;
|
|
6346
|
+
this.backendFeatures = {
|
|
6347
|
+
...this.backendFeatures,
|
|
6348
|
+
...projectBackendSnapshot(state.snapshot, local)
|
|
6349
|
+
};
|
|
6350
|
+
if (local) this.backendAttachmentLimit = backendAttachmentMaxBytes(state.snapshot, this.resolved.attachmentMaxBytes);
|
|
6351
|
+
if (this.backendFeatures["agents.managed_agents"] === "unsupported") await this.detachAgentConsumer(provider);
|
|
6352
|
+
else if (this.backendFeatures["agents.managed_agents"] === "supported") this.ensureAgentConsumer(provider);
|
|
6353
|
+
}
|
|
6354
|
+
} finally {
|
|
6355
|
+
subscription.stop();
|
|
6356
|
+
if (this.backendSubscriptions.get(local) === subscription) this.backendSubscriptions.delete(local);
|
|
6357
|
+
}
|
|
6358
|
+
})().catch(() => {
|
|
6359
|
+
if (current()) this.backendSubscriptionPending.delete(local);
|
|
6360
|
+
});
|
|
5899
6361
|
}
|
|
5900
6362
|
async discoverTenantCapabilities(timeoutMs) {
|
|
5901
6363
|
const options = this.activeClientOptions;
|
|
5902
6364
|
const tenant = this.activeTenant;
|
|
5903
6365
|
if (options === void 0 || tenant === void 0) throw new ProviderUnavailableError();
|
|
5904
6366
|
const generation = this.runtimeGeneration;
|
|
5905
|
-
const
|
|
6367
|
+
const previous = this.activeCapabilities;
|
|
6368
|
+
const fallback = previous?.tenantId === tenant.tenantId && previous.generation === generation ? { ...previous } : {
|
|
5906
6369
|
tenantId: tenant.tenantId,
|
|
5907
6370
|
generation,
|
|
5908
6371
|
online: false,
|
|
@@ -5922,6 +6385,7 @@ let AwikiService = (() => {
|
|
|
5922
6385
|
try {
|
|
5923
6386
|
const endpoint = new URL("/user-service/v1/server-info", options.userServiceUrl);
|
|
5924
6387
|
endpoint.searchParams.set("client_platform", "dsh");
|
|
6388
|
+
endpoint.searchParams.set("capabilities_version", "1");
|
|
5925
6389
|
const response = await fetch(endpoint, {
|
|
5926
6390
|
method: "GET",
|
|
5927
6391
|
headers: {
|
|
@@ -5936,19 +6400,37 @@ let AwikiService = (() => {
|
|
|
5936
6400
|
const text = await readBoundedResponseText(response, SERVER_INFO_RESPONSE_MAX_BYTES);
|
|
5937
6401
|
const value = text === void 0 ? void 0 : JSON.parse(text);
|
|
5938
6402
|
if (typeof value !== "object" || value === null || value.schema_version !== 1) return publish(fallback);
|
|
6403
|
+
const versioned = value.capabilities_version === 1;
|
|
6404
|
+
if (!versioned) {
|
|
6405
|
+
if ("capabilities_version" in value) return publish({
|
|
6406
|
+
tenantId: tenant.tenantId,
|
|
6407
|
+
generation,
|
|
6408
|
+
online: true,
|
|
6409
|
+
handleRecoveryPhoneEnabled: false
|
|
6410
|
+
});
|
|
6411
|
+
return publish(fallback);
|
|
6412
|
+
}
|
|
5939
6413
|
const methods = value.identity?.handle_recovery?.methods;
|
|
5940
|
-
const handleRecoveryPhoneEnabled = Array.isArray(methods) && methods.some((method) => typeof method === "object" && method !== null && method.id === "phone" && method.enabled === true && method.verification?.required === true && method.verification?.type === "sms_otp");
|
|
5941
|
-
const services = value.services;
|
|
6414
|
+
const handleRecoveryPhoneEnabled = versioned && Array.isArray(methods) && methods.some((method) => typeof method === "object" && method !== null && method.id === "phone" && method.enabled === true && method.verification?.required === true && method.verification?.type === "sms_otp");
|
|
6415
|
+
const services = versioned ? value.services : void 0;
|
|
5942
6416
|
const modelProxyBaseUrl = publishedServiceBaseUrl(services, "model_proxy", this.resolved.allowInsecureLoopbackForTesting);
|
|
5943
6417
|
const guestGatewayBaseUrl = publishedServiceBaseUrl(services, "guest_gateway", false);
|
|
5944
|
-
|
|
6418
|
+
const capabilities = {
|
|
5945
6419
|
tenantId: tenant.tenantId,
|
|
5946
6420
|
generation,
|
|
5947
6421
|
online: true,
|
|
5948
6422
|
handleRecoveryPhoneEnabled,
|
|
5949
6423
|
...modelProxyBaseUrl === void 0 ? {} : { modelProxyBaseUrl },
|
|
5950
6424
|
...guestGatewayBaseUrl === void 0 ? {} : { guestGatewayBaseUrl }
|
|
5951
|
-
}
|
|
6425
|
+
};
|
|
6426
|
+
if (this.runtimeGeneration === generation && this.activeTenant?.tenantId === tenant.tenantId) {
|
|
6427
|
+
if (!new TenantCapabilityCache(options.stateRoot, options.userServiceUrl).write({
|
|
6428
|
+
handleRecoveryPhoneEnabled,
|
|
6429
|
+
...modelProxyBaseUrl === void 0 ? {} : { modelProxyBaseUrl },
|
|
6430
|
+
...guestGatewayBaseUrl === void 0 ? {} : { guestGatewayBaseUrl }
|
|
6431
|
+
})) this.ctx.logger("awiki-capabilities").debug("AWiki capability metadata persistence failed");
|
|
6432
|
+
}
|
|
6433
|
+
return publish(capabilities);
|
|
5952
6434
|
} catch {
|
|
5953
6435
|
return publish(fallback);
|
|
5954
6436
|
} finally {
|
|
@@ -6031,12 +6513,6 @@ let AwikiService = (() => {
|
|
|
6031
6513
|
ok: true,
|
|
6032
6514
|
value: { status: "signed-out" }
|
|
6033
6515
|
};
|
|
6034
|
-
const identity = await this.run((client) => client.getIdentity(), { allowSignedOut: true });
|
|
6035
|
-
if (!identity.ok) return identity;
|
|
6036
|
-
if (identity.value === null) return {
|
|
6037
|
-
ok: false,
|
|
6038
|
-
error: failure("not-registered")
|
|
6039
|
-
};
|
|
6040
6516
|
try {
|
|
6041
6517
|
await this.sessionStore.signOut();
|
|
6042
6518
|
this.signedOut = true;
|
|
@@ -6045,7 +6521,10 @@ let AwikiService = (() => {
|
|
|
6045
6521
|
const session = { status: "signed-out" };
|
|
6046
6522
|
this.publishSession(session);
|
|
6047
6523
|
const provider = this.provider;
|
|
6048
|
-
if (provider !== void 0)
|
|
6524
|
+
if (provider !== void 0) {
|
|
6525
|
+
await this.stopProviderRuntime(provider);
|
|
6526
|
+
await this.updateBackendDiscoveryLifecycle(provider, false);
|
|
6527
|
+
}
|
|
6049
6528
|
return {
|
|
6050
6529
|
ok: true,
|
|
6051
6530
|
value: session
|
|
@@ -6078,7 +6557,10 @@ let AwikiService = (() => {
|
|
|
6078
6557
|
};
|
|
6079
6558
|
this.publishSession(session);
|
|
6080
6559
|
const provider = this.provider;
|
|
6081
|
-
if (provider !== void 0)
|
|
6560
|
+
if (provider !== void 0) {
|
|
6561
|
+
this.ensureProviderRuntime(provider);
|
|
6562
|
+
await this.updateBackendDiscoveryLifecycle(provider, true);
|
|
6563
|
+
}
|
|
6082
6564
|
return {
|
|
6083
6565
|
ok: true,
|
|
6084
6566
|
value: session
|
|
@@ -6312,6 +6794,8 @@ let AwikiService = (() => {
|
|
|
6312
6794
|
const recoveries = await client.listPendingRecoveries();
|
|
6313
6795
|
const pendingRegistrations = await client.pendingIdentityRegistrations();
|
|
6314
6796
|
const creationMethods = await client.identityCreationMethods().catch(() => []);
|
|
6797
|
+
const backend = await client.backendCapabilities?.(false);
|
|
6798
|
+
const registrationHandleOnly = backend?.capabilities["registration.handle_only"]?.support === "supported" && (backend.status === "confirmed" || backend.status === "stale");
|
|
6315
6799
|
const identity = await client.getIdentity();
|
|
6316
6800
|
const capabilities = identity === null ? void 0 : await client.identityMethodCapabilities(identity.did);
|
|
6317
6801
|
const domain = this.activeClientOptions?.userServiceDomain;
|
|
@@ -6326,6 +6810,7 @@ let AwikiService = (() => {
|
|
|
6326
6810
|
},
|
|
6327
6811
|
joining,
|
|
6328
6812
|
creationMethods,
|
|
6813
|
+
registrationHandleOnly,
|
|
6329
6814
|
pendingRegistrations,
|
|
6330
6815
|
...capabilities === void 0 ? {} : { methodCapabilities: capabilities },
|
|
6331
6816
|
recoveries: recoveries.filter((value) => domain !== void 0 && value.fullHandle.slice(value.fullHandle.indexOf(".") + 1) === domain)
|
|
@@ -6598,6 +7083,22 @@ let AwikiService = (() => {
|
|
|
6598
7083
|
if (result.ok) this.activeIdentityDid = result.value.did;
|
|
6599
7084
|
return result;
|
|
6600
7085
|
}
|
|
7086
|
+
/** Replace only the current authenticated human account's avatar. */
|
|
7087
|
+
setAvatar(request) {
|
|
7088
|
+
if (!validAvatarRequest(request, true)) return Promise.resolve({
|
|
7089
|
+
ok: false,
|
|
7090
|
+
error: failure("invalid-request")
|
|
7091
|
+
});
|
|
7092
|
+
return this.run((client) => client.setAvatar(request));
|
|
7093
|
+
}
|
|
7094
|
+
/** Restore the current account's default character avatar. */
|
|
7095
|
+
clearAvatar(request) {
|
|
7096
|
+
if (!validAvatarRequest(request, false)) return Promise.resolve({
|
|
7097
|
+
ok: false,
|
|
7098
|
+
error: failure("invalid-request")
|
|
7099
|
+
});
|
|
7100
|
+
return this.run((client) => client.clearAvatar(request));
|
|
7101
|
+
}
|
|
6601
7102
|
/** Return the public editable profile for the active identity. */
|
|
6602
7103
|
getProfile() {
|
|
6603
7104
|
return this.run((client) => client.getProfile());
|
|
@@ -6711,7 +7212,12 @@ let AwikiService = (() => {
|
|
|
6711
7212
|
ok: false,
|
|
6712
7213
|
error: failure("invalid-request")
|
|
6713
7214
|
};
|
|
6714
|
-
const created = await this.run((client) =>
|
|
7215
|
+
const created = await this.run(async (client) => {
|
|
7216
|
+
await requireBackendFeature(client, "groups");
|
|
7217
|
+
if (normalized.encrypted) await requireBackendFeature(client, "group_e2ee");
|
|
7218
|
+
if (normalized.members.length > 0) await requireBackendFeature(client, "group_admin_add");
|
|
7219
|
+
return client.createGroup(normalized.name, normalized.encrypted, normalized.operationId);
|
|
7220
|
+
});
|
|
6715
7221
|
if (!created.ok) return created;
|
|
6716
7222
|
const addedMembers = [];
|
|
6717
7223
|
const failedMembers = [];
|
|
@@ -6741,6 +7247,18 @@ let AwikiService = (() => {
|
|
|
6741
7247
|
});
|
|
6742
7248
|
return this.run((client) => client.getGroup(groupDid));
|
|
6743
7249
|
}
|
|
7250
|
+
/** Resume owner-held group encryption maintenance through Core. */
|
|
7251
|
+
repairGroupSecurity(request) {
|
|
7252
|
+
const groupDid = normalizeGroupDid(request?.groupDid);
|
|
7253
|
+
if (groupDid === void 0) return Promise.resolve({
|
|
7254
|
+
ok: false,
|
|
7255
|
+
error: failure("invalid-request")
|
|
7256
|
+
});
|
|
7257
|
+
return this.run(async (client) => {
|
|
7258
|
+
await requireBackendFeature(client, "group_e2ee");
|
|
7259
|
+
return client.repairGroupSecurity(groupDid);
|
|
7260
|
+
});
|
|
7261
|
+
}
|
|
6744
7262
|
/** Join one open group and return its authoritative membership state. */
|
|
6745
7263
|
joinGroup(request) {
|
|
6746
7264
|
const groupDid = normalizeGroupDid(request?.groupDid);
|
|
@@ -6748,7 +7266,10 @@ let AwikiService = (() => {
|
|
|
6748
7266
|
ok: false,
|
|
6749
7267
|
error: failure("invalid-request")
|
|
6750
7268
|
});
|
|
6751
|
-
return this.run((client) =>
|
|
7269
|
+
return this.run(async (client) => {
|
|
7270
|
+
await requireBackendFeature(client, "group_open_join");
|
|
7271
|
+
return client.joinGroup(groupDid);
|
|
7272
|
+
});
|
|
6752
7273
|
}
|
|
6753
7274
|
/** Leave one group. Core rejects owner leave and unsupported security profiles. */
|
|
6754
7275
|
async leaveGroup(request) {
|
|
@@ -6764,12 +7285,12 @@ let AwikiService = (() => {
|
|
|
6764
7285
|
} : result;
|
|
6765
7286
|
}
|
|
6766
7287
|
/** Read one authoritative versioned member page. */
|
|
6767
|
-
getDisplayProfiles(peers) {
|
|
6768
|
-
if (!Array.isArray(peers) || peers.length > 100 || peers.some((peer) => typeof peer !== "string" || !peer.startsWith("did:"))) return Promise.resolve({
|
|
7288
|
+
getDisplayProfiles(peers, refresh) {
|
|
7289
|
+
if (refresh !== void 0 && typeof refresh !== "boolean" || !Array.isArray(peers) || peers.length > 100 || peers.some((peer) => typeof peer !== "string" || !peer.startsWith("did:"))) return Promise.resolve({
|
|
6769
7290
|
ok: false,
|
|
6770
7291
|
error: failure("invalid-request")
|
|
6771
7292
|
});
|
|
6772
|
-
return this.run((client) => client.getDisplayProfiles(peers));
|
|
7293
|
+
return this.run((client) => client.getDisplayProfiles(peers, refresh));
|
|
6773
7294
|
}
|
|
6774
7295
|
listGroupMembers(request) {
|
|
6775
7296
|
const groupDid = normalizeGroupDid(request?.groupDid);
|
|
@@ -6968,6 +7489,10 @@ let AwikiService = (() => {
|
|
|
6968
7489
|
* @returns The accepted attachment message or a closed failure.
|
|
6969
7490
|
*/
|
|
6970
7491
|
async sendAttachment(request) {
|
|
7492
|
+
if (request?.security !== void 0 && request.security !== "plain" && request.security !== "e2ee-required") return {
|
|
7493
|
+
ok: false,
|
|
7494
|
+
error: failure("invalid-request")
|
|
7495
|
+
};
|
|
6971
7496
|
if (!isMessageTarget(request?.target)) return {
|
|
6972
7497
|
ok: false,
|
|
6973
7498
|
error: failure("invalid-request")
|
|
@@ -6976,6 +7501,7 @@ let AwikiService = (() => {
|
|
|
6976
7501
|
if (!decoded.ok) return decoded;
|
|
6977
7502
|
return this.run(async (client) => {
|
|
6978
7503
|
const message = await client.sendAttachment({
|
|
7504
|
+
...request.security === void 0 ? {} : { security: request.security },
|
|
6979
7505
|
target: request.target,
|
|
6980
7506
|
attachment: {
|
|
6981
7507
|
fileName: request.fileName,
|
|
@@ -7325,6 +7851,7 @@ let AwikiService = (() => {
|
|
|
7325
7851
|
return client.getLocalDeviceJoinVerificationProgress(notice.joinSessionId);
|
|
7326
7852
|
}
|
|
7327
7853
|
async requireDeviceManager(client) {
|
|
7854
|
+
await requireBackendFeature(client, "identity.multi_device");
|
|
7328
7855
|
const current = await client.getCurrentDeviceSummary();
|
|
7329
7856
|
if (!current.canManage || current.role !== "admin" || current.readiness !== "admin_ready") throw Object.assign(/* @__PURE__ */ new Error("device manager required"), {
|
|
7330
7857
|
name: "AwikiSdkError",
|
|
@@ -7332,6 +7859,7 @@ let AwikiService = (() => {
|
|
|
7332
7859
|
});
|
|
7333
7860
|
}
|
|
7334
7861
|
async requireRootTransferRecipient(client, deviceId) {
|
|
7862
|
+
await requireBackendFeature(client, "identity.root_transfer");
|
|
7335
7863
|
if (!client.trustedUserPresenceSupported) throw Object.assign(/* @__PURE__ */ new Error("trusted user presence unsupported"), {
|
|
7336
7864
|
name: "AwikiSdkError",
|
|
7337
7865
|
code: "forbidden"
|
|
@@ -7458,6 +7986,7 @@ let AwikiService = (() => {
|
|
|
7458
7986
|
}
|
|
7459
7987
|
/** Invalidate cached session work and cancel every model request still owned by the old session. */
|
|
7460
7988
|
invalidateSummaries() {
|
|
7989
|
+
this.stopBackendSubscriptions();
|
|
7461
7990
|
this.sessionRevision += 1;
|
|
7462
7991
|
this.requestRefs.clear();
|
|
7463
7992
|
this.requestSessions.clear();
|
|
@@ -7541,7 +8070,10 @@ let AwikiService = (() => {
|
|
|
7541
8070
|
error: failure("invalid-request")
|
|
7542
8071
|
});
|
|
7543
8072
|
}
|
|
7544
|
-
return this.run((client) =>
|
|
8073
|
+
return this.run(async (client) => {
|
|
8074
|
+
await requireBackendFeature(client, "services.mail");
|
|
8075
|
+
return operation(client, request);
|
|
8076
|
+
}, options);
|
|
7545
8077
|
}
|
|
7546
8078
|
/** Read and cache the private Host-owned session marker. */
|
|
7547
8079
|
async isSignedOut() {
|
|
@@ -7595,6 +8127,14 @@ let AwikiService = (() => {
|
|
|
7595
8127
|
this.sessionMutation = pending.then(() => void 0, () => void 0);
|
|
7596
8128
|
return pending;
|
|
7597
8129
|
}
|
|
8130
|
+
/** Discovery is an optional projection, not authority for a committed session transition. */
|
|
8131
|
+
async updateBackendDiscoveryLifecycle(provider, active) {
|
|
8132
|
+
try {
|
|
8133
|
+
await provider.client.setBackendDiscoveryActive?.(active);
|
|
8134
|
+
} catch {
|
|
8135
|
+
this.ctx.logger("awiki-backend").warn("AWiki backend discovery lifecycle unavailable");
|
|
8136
|
+
}
|
|
8137
|
+
}
|
|
7598
8138
|
/** Start identity realtime and the optional Agent consumer without blocking identity success. */
|
|
7599
8139
|
ensureProviderRuntime(provider) {
|
|
7600
8140
|
if (this.tenantSwitching) return;
|
|
@@ -7604,6 +8144,7 @@ let AwikiService = (() => {
|
|
|
7604
8144
|
this.replaceProviderRuntime(provider, identityDid);
|
|
7605
8145
|
return;
|
|
7606
8146
|
}
|
|
8147
|
+
this.refreshBackendProjection();
|
|
7607
8148
|
this.ensureRealtimeSupervisor(provider);
|
|
7608
8149
|
this.ensureAgentConsumer(provider);
|
|
7609
8150
|
}
|
|
@@ -7680,6 +8221,7 @@ let AwikiService = (() => {
|
|
|
7680
8221
|
const logger = this.ctx.logger("awiki-listener");
|
|
7681
8222
|
const observed = (async () => {
|
|
7682
8223
|
await this.detachAgentConsumer(provider);
|
|
8224
|
+
if ((await projectBackendFeatures(provider.client, false))["agents.managed_agents"] !== "supported") return;
|
|
7683
8225
|
if (!this.agentConsumerFenceMatches(provider, workspaceContext, identityDid, generation)) return;
|
|
7684
8226
|
const agents = new DshAwikiListenerAgentRuntime(workspaceContext, this.resolved.listener.workspacePath);
|
|
7685
8227
|
const consumer = new AwikiAgentListener(source, agents, {
|