@awiki/dsh-plugin 0.3.5 → 0.3.6
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 +29 -14
- package/README.zh.md +24 -13
- package/cordis.patch.yml +7 -4
- package/lib/client.js +569 -95
- package/lib/client.js.map +1 -1
- package/lib/index.js +402 -98
- package/lib/provider.js +1 -1
- package/lib/{sdk-adapter-CDLTgHJA.mjs → sdk-adapter--trNlr-T.mjs} +34 -1
- package/lib/typert.host.js +97 -42
- package/lib/typert.remote-client.d.ts +3 -1
- package/lib/typert.remote-client.d.ts.map +1 -1
- package/lib/typert.remote-client.js +97 -42
- package/lib/types/client/AwikiMail.d.ts +1 -1
- package/lib/types/client/AwikiMail.d.ts.map +1 -1
- package/lib/types/client/AwikiMail.js +130 -6
- package/lib/types/client/AwikiMail.js.map +1 -1
- package/lib/types/client/AwikiOverlay.d.ts.map +1 -1
- package/lib/types/client/AwikiOverlay.js +1 -1
- package/lib/types/client/AwikiOverlay.js.map +1 -1
- package/lib/types/client/controller.d.ts +7 -1
- package/lib/types/client/controller.d.ts.map +1 -1
- package/lib/types/client/controller.js +41 -15
- 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 +2 -0
- package/lib/types/client/index.js.map +1 -1
- package/lib/types/client/mail-attachment.d.ts +40 -0
- package/lib/types/client/mail-attachment.d.ts.map +1 -0
- package/lib/types/client/mail-attachment.js +186 -0
- package/lib/types/client/mail-attachment.js.map +1 -0
- package/lib/types/client/settings-locales.js +8 -8
- package/lib/types/client/settings-locales.js.map +1 -1
- package/lib/types/client/slots.d.ts +5 -1
- package/lib/types/client/slots.d.ts.map +1 -1
- package/lib/types/index.d.ts +18 -6
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +244 -68
- package/lib/types/index.js.map +1 -1
- package/lib/types/mail.d.ts +28 -2
- package/lib/types/mail.d.ts.map +1 -1
- package/lib/types/mail.js +78 -2
- package/lib/types/mail.js.map +1 -1
- package/lib/types/profile-state.d.ts +6 -0
- package/lib/types/profile-state.d.ts.map +1 -1
- package/lib/types/profile-state.js +10 -0
- package/lib/types/profile-state.js.map +1 -1
- package/lib/types/provider-api.d.ts +32 -4
- package/lib/types/provider-api.d.ts.map +1 -1
- package/lib/types/sdk-adapter.d.ts +4 -3
- package/lib/types/sdk-adapter.d.ts.map +1 -1
- package/lib/types/sdk-adapter.js +45 -1
- package/lib/types/sdk-adapter.js.map +1 -1
- package/lib/types/sent-mail-store.d.ts +7 -2
- package/lib/types/sent-mail-store.d.ts.map +1 -1
- package/lib/types/sent-mail-store.js +125 -23
- package/lib/types/sent-mail-store.js.map +1 -1
- package/lib/types/tools.d.ts +1 -1
- package/lib/types/tools.d.ts.map +1 -1
- package/lib/types/tools.js +20 -4
- package/lib/types/tools.js.map +1 -1
- package/lib/types/types.d.ts +39 -1
- package/lib/types/types.d.ts.map +1 -1
- package/lib/types/types.js.map +1 -1
- package/package.json +15 -17
package/lib/types/index.js
CHANGED
|
@@ -34,6 +34,7 @@ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn,
|
|
|
34
34
|
done = true;
|
|
35
35
|
};
|
|
36
36
|
import { Context } from '@deepseek-ai/cordis';
|
|
37
|
+
import { createHash } from 'node:crypto';
|
|
37
38
|
import { existsSync } from 'node:fs';
|
|
38
39
|
import { homedir } from 'node:os';
|
|
39
40
|
import { isAbsolute, join } from 'node:path';
|
|
@@ -44,7 +45,7 @@ import { AWIKI_CLEAR_LOCAL_DATA_CONFIRMATION, AWIKI_LOGOUT_CONFIRMATION } from "
|
|
|
44
45
|
import { AwikiExternalHttpAuthError, createAwikiExternalHttpAuth, externalHttpAuthError, mapProviderError as mapExternalHttpProviderError, } from "./external-http-auth.js";
|
|
45
46
|
import { downloadedAttachment } from "./sdk-adapter.js";
|
|
46
47
|
import { registerAwikiTools } from "./tools.js";
|
|
47
|
-
import { mailInboxRequest, mailMarkReadRequest, mailReadRequest, mailSendRequest, } from "./mail.js";
|
|
48
|
+
import { MAIL_ATTACHMENT_SERVICE_MAX_BYTES, MAIL_ATTACHMENT_SERVICE_MAX_COUNT, MAIL_ATTACHMENT_SERVICE_TOTAL_MAX_BYTES, mailAttachmentDownloadRequest, mailAttachmentContentType, mailAttachmentFileName, mailInboxRequest, mailMarkReadRequest, mailReadRequest, mailSendRequest, } from "./mail.js";
|
|
48
49
|
import { AwikiSettingsSchema, validateAwikiSettings, } from "./settings.js";
|
|
49
50
|
import { AWIKI_SETTINGS_NAMESPACE, DEFAULT_AWIKI_DOMAIN, normalizeAwikiDomain } from "./domain.js";
|
|
50
51
|
import { AWIKI_SETTINGS_RPC_CHANNEL } from "./settings-rpc-contract.js";
|
|
@@ -54,7 +55,7 @@ import { AwikiImageAttachmentCache, minimumImageAttachmentCacheMaxBytes } from "
|
|
|
54
55
|
import { AwikiSentMailStore, isLocalSentMailId } from "./sent-mail-store.js";
|
|
55
56
|
import { AwikiConversationPreferenceStore, normalizeConversationPreferenceMutation, } from "./conversation-preferences.js";
|
|
56
57
|
import { AwikiAgentListener, DshAwikiListenerAgentRuntime, } from "./listener.js";
|
|
57
|
-
import { resolveAwikiProfileName, resolveAwikiStateRoot } from "./profile-state.js";
|
|
58
|
+
import { resolveAwikiProfileName, resolveAwikiStateRoot, resolveAwikiTenantStateRoot, } from "./profile-state.js";
|
|
58
59
|
export { AWIKI_CLEAR_LOCAL_DATA_CONFIRMATION, AWIKI_LOGOUT_CONFIRMATION } from "./types.js";
|
|
59
60
|
export { AWIKI_EXTERNAL_HTTP_MAX_BODY_BYTES, AwikiExternalHttpAuthError, } from "./external-http-auth.js";
|
|
60
61
|
export { AWIKI_DOMAIN_FIELD, AWIKI_SETTINGS_NAMESPACE, AwikiSettingsSchema, DEFAULT_AWIKI_DOMAIN, normalizeAwikiDomain, validateAwikiSettings, } from "./settings.js";
|
|
@@ -75,16 +76,19 @@ export const DEFAULT_SUMMARY_MAX_INPUT_BYTES = 32 * 1024;
|
|
|
75
76
|
export const MAX_SUMMARY_MESSAGES = 50;
|
|
76
77
|
/** Loader schema for the Host deployment configuration. */
|
|
77
78
|
export const Config = z.object({
|
|
78
|
-
userServiceUrl: z.string()
|
|
79
|
+
userServiceUrl: z.string(),
|
|
79
80
|
userServiceDomain: z.string().default(DEFAULT_AWIKI_DOMAIN),
|
|
80
|
-
messageServiceUrl: z.string()
|
|
81
|
+
messageServiceUrl: z.string(),
|
|
81
82
|
mailServiceUrl: z.string(),
|
|
82
|
-
messageServicePublicUrl: z.string()
|
|
83
|
-
messageServiceDid: z.string()
|
|
83
|
+
messageServicePublicUrl: z.string(),
|
|
84
|
+
messageServiceDid: z.string(),
|
|
84
85
|
allowedAttachmentOrigins: z.array(z.string()).default([]),
|
|
85
86
|
allowInsecureLoopbackForTesting: z.boolean().default(false),
|
|
86
87
|
stateRoot: z.string(),
|
|
87
88
|
attachmentMaxBytes: z.number().default(DEFAULT_ATTACHMENT_MAX_BYTES),
|
|
89
|
+
mailAttachmentMaxCount: z.number().default(MAIL_ATTACHMENT_SERVICE_MAX_COUNT),
|
|
90
|
+
mailAttachmentMaxBytes: z.number().default(MAIL_ATTACHMENT_SERVICE_MAX_BYTES),
|
|
91
|
+
mailAttachmentTotalMaxBytes: z.number().default(MAIL_ATTACHMENT_SERVICE_TOTAL_MAX_BYTES),
|
|
88
92
|
imageAttachmentCacheMaxBytes: z.number().default(DEFAULT_IMAGE_ATTACHMENT_CACHE_MAX_BYTES),
|
|
89
93
|
pollIntervalMs: z.number().default(DEFAULT_POLL_INTERVAL_MS),
|
|
90
94
|
listenerEnabled: z.boolean().default(false),
|
|
@@ -250,6 +254,24 @@ function resolveConfig(ctx, config) {
|
|
|
250
254
|
if (!Number.isSafeInteger(attachmentMaxBytes) || attachmentMaxBytes < 1) {
|
|
251
255
|
throw new TypeError('awiki: attachmentMaxBytes must be a positive safe integer');
|
|
252
256
|
}
|
|
257
|
+
const mailAttachmentMaxCount = config.mailAttachmentMaxCount ?? MAIL_ATTACHMENT_SERVICE_MAX_COUNT;
|
|
258
|
+
if (!Number.isSafeInteger(mailAttachmentMaxCount)
|
|
259
|
+
|| mailAttachmentMaxCount < 0
|
|
260
|
+
|| mailAttachmentMaxCount > MAIL_ATTACHMENT_SERVICE_MAX_COUNT) {
|
|
261
|
+
throw new TypeError('awiki: mailAttachmentMaxCount must be an integer from 0 through 10');
|
|
262
|
+
}
|
|
263
|
+
const mailAttachmentMaxBytes = config.mailAttachmentMaxBytes ?? MAIL_ATTACHMENT_SERVICE_MAX_BYTES;
|
|
264
|
+
if (!Number.isSafeInteger(mailAttachmentMaxBytes)
|
|
265
|
+
|| mailAttachmentMaxBytes < 1
|
|
266
|
+
|| mailAttachmentMaxBytes > MAIL_ATTACHMENT_SERVICE_MAX_BYTES) {
|
|
267
|
+
throw new TypeError('awiki: mailAttachmentMaxBytes must be a positive safe integer no greater than 10 MiB');
|
|
268
|
+
}
|
|
269
|
+
const mailAttachmentTotalMaxBytes = config.mailAttachmentTotalMaxBytes ?? MAIL_ATTACHMENT_SERVICE_TOTAL_MAX_BYTES;
|
|
270
|
+
if (!Number.isSafeInteger(mailAttachmentTotalMaxBytes)
|
|
271
|
+
|| mailAttachmentTotalMaxBytes < mailAttachmentMaxBytes
|
|
272
|
+
|| mailAttachmentTotalMaxBytes > MAIL_ATTACHMENT_SERVICE_TOTAL_MAX_BYTES) {
|
|
273
|
+
throw new TypeError('awiki: mailAttachmentTotalMaxBytes must cover one attachment and not exceed 18 MiB');
|
|
274
|
+
}
|
|
253
275
|
const imageAttachmentCacheMaxBytes = config.imageAttachmentCacheMaxBytes ?? DEFAULT_IMAGE_ATTACHMENT_CACHE_MAX_BYTES;
|
|
254
276
|
if (!Number.isSafeInteger(imageAttachmentCacheMaxBytes)
|
|
255
277
|
|| imageAttachmentCacheMaxBytes < minimumImageAttachmentCacheMaxBytes(attachmentMaxBytes)) {
|
|
@@ -274,6 +296,7 @@ function resolveConfig(ctx, config) {
|
|
|
274
296
|
const mailServiceUrl = serviceUrl('mailServiceUrl', config.mailServiceUrl ?? userServiceUrl, allowInsecureLoopbackForTesting);
|
|
275
297
|
const messageServicePublicUrl = serviceUrl('messageServicePublicUrl', config.messageServicePublicUrl ?? DEFAULT_AWIKI_SERVICE_URL, allowInsecureLoopbackForTesting);
|
|
276
298
|
return {
|
|
299
|
+
tenantDomain: serviceDomain(config.userServiceDomain ?? DEFAULT_AWIKI_DOMAIN),
|
|
277
300
|
userServiceUrl,
|
|
278
301
|
userServiceDomain: serviceDomain(config.userServiceDomain ?? DEFAULT_AWIKI_DOMAIN),
|
|
279
302
|
messageServiceUrl,
|
|
@@ -284,6 +307,9 @@ function resolveConfig(ctx, config) {
|
|
|
284
307
|
allowInsecureLoopbackForTesting,
|
|
285
308
|
stateRoot,
|
|
286
309
|
attachmentMaxBytes,
|
|
310
|
+
mailAttachmentMaxCount,
|
|
311
|
+
mailAttachmentMaxBytes,
|
|
312
|
+
mailAttachmentTotalMaxBytes,
|
|
287
313
|
imageAttachmentCacheMaxBytes,
|
|
288
314
|
pollIntervalMs,
|
|
289
315
|
...profileName === undefined ? {} : { profileName, legacySharedStateDetected },
|
|
@@ -294,6 +320,42 @@ function resolveConfig(ctx, config) {
|
|
|
294
320
|
stateRoot,
|
|
295
321
|
},
|
|
296
322
|
summaryMaxInputBytes,
|
|
323
|
+
deriveUserServiceUrl: config.userServiceUrl === undefined,
|
|
324
|
+
deriveMessageServiceUrl: config.messageServiceUrl === undefined,
|
|
325
|
+
deriveMailServiceUrl: config.mailServiceUrl === undefined,
|
|
326
|
+
deriveMessageServicePublicUrl: config.messageServicePublicUrl === undefined,
|
|
327
|
+
deriveMessageServiceDid: config.messageServiceDid === undefined,
|
|
328
|
+
deriveAllowedAttachmentOrigins: config.allowedAttachmentOrigins === undefined
|
|
329
|
+
|| config.allowedAttachmentOrigins.length === 0,
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
/** Resolve every tenant-sensitive endpoint and state path from one selected domain. */
|
|
333
|
+
function activeTenantConfig(config, domain) {
|
|
334
|
+
const tenantOrigin = `https://${domain}`;
|
|
335
|
+
const userServiceUrl = config.deriveUserServiceUrl ? tenantOrigin : config.userServiceUrl;
|
|
336
|
+
const messageServiceUrl = config.deriveMessageServiceUrl ? tenantOrigin : config.messageServiceUrl;
|
|
337
|
+
const mailServiceUrl = config.deriveMailServiceUrl ? userServiceUrl : config.mailServiceUrl;
|
|
338
|
+
const messageServicePublicUrl = config.deriveMessageServicePublicUrl
|
|
339
|
+
? tenantOrigin
|
|
340
|
+
: config.messageServicePublicUrl;
|
|
341
|
+
const messageServiceDid = config.deriveMessageServiceDid
|
|
342
|
+
? `did:wba:${domain}`
|
|
343
|
+
: config.messageServiceDid;
|
|
344
|
+
const allowedAttachmentOrigins = config.deriveAllowedAttachmentOrigins
|
|
345
|
+
? attachmentOrigins(undefined, messageServicePublicUrl, config.allowInsecureLoopbackForTesting)
|
|
346
|
+
: config.allowedAttachmentOrigins;
|
|
347
|
+
return {
|
|
348
|
+
domain,
|
|
349
|
+
userServiceUrl,
|
|
350
|
+
userServiceDomain: domain,
|
|
351
|
+
messageServiceUrl,
|
|
352
|
+
mailServiceUrl,
|
|
353
|
+
messageServicePublicUrl,
|
|
354
|
+
messageServiceDid,
|
|
355
|
+
allowedAttachmentOrigins,
|
|
356
|
+
attachmentMaxBytes: config.attachmentMaxBytes,
|
|
357
|
+
allowInsecureLoopbackForTesting: config.allowInsecureLoopbackForTesting,
|
|
358
|
+
stateRoot: resolveAwikiTenantStateRoot(config.stateRoot, domain, config.userServiceDomain),
|
|
297
359
|
};
|
|
298
360
|
}
|
|
299
361
|
/** Return a public, fixed-message failure without retaining a thrown value. */
|
|
@@ -708,6 +770,7 @@ let AwikiService = (() => {
|
|
|
708
770
|
let _readMail_decorators;
|
|
709
771
|
let _markMailRead_decorators;
|
|
710
772
|
let _sendMail_decorators;
|
|
773
|
+
let _downloadMailAttachment_decorators;
|
|
711
774
|
let _clearLocalData_decorators;
|
|
712
775
|
return class AwikiService extends _classSuper {
|
|
713
776
|
static {
|
|
@@ -753,6 +816,7 @@ let AwikiService = (() => {
|
|
|
753
816
|
_readMail_decorators = [Remote];
|
|
754
817
|
_markMailRead_decorators = [Remote];
|
|
755
818
|
_sendMail_decorators = [Remote];
|
|
819
|
+
_downloadMailAttachment_decorators = [Remote];
|
|
756
820
|
_clearLocalData_decorators = [Remote];
|
|
757
821
|
__esDecorate(this, null, _getConfig_decorators, { kind: "method", name: "getConfig", static: false, private: false, access: { has: obj => "getConfig" in obj, get: obj => obj.getConfig }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
758
822
|
__esDecorate(this, null, _getIdentity_decorators, { kind: "method", name: "getIdentity", static: false, private: false, access: { has: obj => "getIdentity" in obj, get: obj => obj.getIdentity }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
@@ -795,16 +859,15 @@ let AwikiService = (() => {
|
|
|
795
859
|
__esDecorate(this, null, _readMail_decorators, { kind: "method", name: "readMail", static: false, private: false, access: { has: obj => "readMail" in obj, get: obj => obj.readMail }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
796
860
|
__esDecorate(this, null, _markMailRead_decorators, { kind: "method", name: "markMailRead", static: false, private: false, access: { has: obj => "markMailRead" in obj, get: obj => obj.markMailRead }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
797
861
|
__esDecorate(this, null, _sendMail_decorators, { kind: "method", name: "sendMail", static: false, private: false, access: { has: obj => "sendMail" in obj, get: obj => obj.sendMail }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
862
|
+
__esDecorate(this, null, _downloadMailAttachment_decorators, { kind: "method", name: "downloadMailAttachment", static: false, private: false, access: { has: obj => "downloadMailAttachment" in obj, get: obj => obj.downloadMailAttachment }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
798
863
|
__esDecorate(this, null, _clearLocalData_decorators, { kind: "method", name: "clearLocalData", static: false, private: false, access: { has: obj => "clearLocalData" in obj, get: obj => obj.clearLocalData }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
799
864
|
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
800
865
|
}
|
|
801
|
-
static inject = ['tools'];
|
|
866
|
+
static inject = ['tools', 'settings'];
|
|
802
867
|
static Config = Config;
|
|
803
868
|
resolved = __runInitializers(this, _instanceExtraInitializers);
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
sentMailStore;
|
|
807
|
-
conversationPreferenceStore;
|
|
869
|
+
tenantConfigCache;
|
|
870
|
+
tenantStateCache;
|
|
808
871
|
startupUserServiceDomain;
|
|
809
872
|
settingsProvider;
|
|
810
873
|
provider;
|
|
@@ -828,27 +891,21 @@ let AwikiService = (() => {
|
|
|
828
891
|
this.hostContext = ctx;
|
|
829
892
|
this.resolved = resolveConfig(ctx, config);
|
|
830
893
|
this.externalHttpAuth = createAwikiExternalHttpAuth(() => this.acquireExternalHttpAuthSession());
|
|
831
|
-
this.sessionStore = new AwikiSessionStore(this.resolved.stateRoot);
|
|
832
|
-
this.imageAttachmentCache = new AwikiImageAttachmentCache(this.resolved.stateRoot, this.resolved.attachmentMaxBytes, this.resolved.imageAttachmentCacheMaxBytes);
|
|
833
|
-
this.sentMailStore = new AwikiSentMailStore(this.resolved.stateRoot);
|
|
834
|
-
this.conversationPreferenceStore = new AwikiConversationPreferenceStore(this.resolved.stateRoot);
|
|
835
894
|
this.startupUserServiceDomain = this.resolved.userServiceDomain;
|
|
836
|
-
ctx.
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
validate: validateAwikiSettings,
|
|
842
|
-
});
|
|
843
|
-
this.settingsProvider = provider;
|
|
844
|
-
this.startupUserServiceDomain = settingsScope.get().domain;
|
|
845
|
-
settingsCtx.effect(() => () => {
|
|
846
|
-
if (this.settingsProvider === provider) {
|
|
847
|
-
this.settingsProvider = undefined;
|
|
848
|
-
this.startupUserServiceDomain = this.resolved.userServiceDomain;
|
|
849
|
-
}
|
|
850
|
-
}, 'awiki: release settings namespace');
|
|
895
|
+
const provider = ctx.settings;
|
|
896
|
+
const settingsScope = provider.register(settingsNamespace(AWIKI_SETTINGS_NAMESPACE), AwikiSettingsSchema, {
|
|
897
|
+
base: { domain: this.resolved.userServiceDomain },
|
|
898
|
+
applies: 'restart',
|
|
899
|
+
validate: validateAwikiSettings,
|
|
851
900
|
});
|
|
901
|
+
this.settingsProvider = provider;
|
|
902
|
+
this.startupUserServiceDomain = settingsScope.get().domain;
|
|
903
|
+
ctx.effect(() => () => {
|
|
904
|
+
if (this.settingsProvider === provider) {
|
|
905
|
+
this.settingsProvider = undefined;
|
|
906
|
+
this.startupUserServiceDomain = this.resolved.userServiceDomain;
|
|
907
|
+
}
|
|
908
|
+
}, 'awiki: release settings namespace');
|
|
852
909
|
ctx.inject(['connection'], (connectionCtx) => {
|
|
853
910
|
connectionCtx.connection.rpc.handle(AWIKI_SETTINGS_RPC_CHANNEL, createAwikiSettingsRpcHandler(() => this.settingsProvider), { authority: 'loopback' });
|
|
854
911
|
});
|
|
@@ -886,18 +943,7 @@ let AwikiService = (() => {
|
|
|
886
943
|
registerClientFactory(factory) {
|
|
887
944
|
if (this.provider !== undefined)
|
|
888
945
|
throw new Error('awiki: a client provider is already registered');
|
|
889
|
-
const client = factory(
|
|
890
|
-
userServiceUrl: this.resolved.userServiceUrl,
|
|
891
|
-
userServiceDomain: this.startupUserServiceDomain,
|
|
892
|
-
messageServiceUrl: this.resolved.messageServiceUrl,
|
|
893
|
-
mailServiceUrl: this.resolved.mailServiceUrl,
|
|
894
|
-
messageServicePublicUrl: this.resolved.messageServicePublicUrl,
|
|
895
|
-
messageServiceDid: this.resolved.messageServiceDid,
|
|
896
|
-
allowedAttachmentOrigins: this.resolved.allowedAttachmentOrigins,
|
|
897
|
-
attachmentMaxBytes: this.resolved.attachmentMaxBytes,
|
|
898
|
-
allowInsecureLoopbackForTesting: this.resolved.allowInsecureLoopbackForTesting,
|
|
899
|
-
stateRoot: this.resolved.stateRoot,
|
|
900
|
-
});
|
|
946
|
+
const client = factory(this.activeTenant());
|
|
901
947
|
const provider = {
|
|
902
948
|
client,
|
|
903
949
|
listenerRestartAttempt: 0,
|
|
@@ -949,8 +995,12 @@ let AwikiService = (() => {
|
|
|
949
995
|
return Promise.resolve({
|
|
950
996
|
ok: true,
|
|
951
997
|
value: {
|
|
998
|
+
tenantDomain: this.activeTenant().domain,
|
|
952
999
|
pollIntervalMs: this.resolved.pollIntervalMs,
|
|
953
1000
|
attachmentMaxBytes: this.resolved.attachmentMaxBytes,
|
|
1001
|
+
mailAttachmentMaxCount: this.resolved.mailAttachmentMaxCount,
|
|
1002
|
+
mailAttachmentMaxBytes: this.resolved.mailAttachmentMaxBytes,
|
|
1003
|
+
mailAttachmentTotalMaxBytes: this.resolved.mailAttachmentTotalMaxBytes,
|
|
954
1004
|
...this.resolved.profileName === undefined ? {} : {
|
|
955
1005
|
profileName: this.resolved.profileName,
|
|
956
1006
|
legacySharedStateDetected: this.resolved.legacySharedStateDetected,
|
|
@@ -958,6 +1008,42 @@ let AwikiService = (() => {
|
|
|
958
1008
|
},
|
|
959
1009
|
});
|
|
960
1010
|
}
|
|
1011
|
+
activeTenant() {
|
|
1012
|
+
const cached = this.tenantConfigCache;
|
|
1013
|
+
if (cached !== undefined)
|
|
1014
|
+
return cached;
|
|
1015
|
+
const value = Object.freeze(activeTenantConfig(this.resolved, this.startupUserServiceDomain));
|
|
1016
|
+
this.tenantConfigCache = value;
|
|
1017
|
+
return value;
|
|
1018
|
+
}
|
|
1019
|
+
activeTenantState() {
|
|
1020
|
+
const cached = this.tenantStateCache;
|
|
1021
|
+
if (cached !== undefined)
|
|
1022
|
+
return cached;
|
|
1023
|
+
const tenant = this.activeTenant();
|
|
1024
|
+
const value = Object.freeze({
|
|
1025
|
+
domain: tenant.domain,
|
|
1026
|
+
stateRoot: tenant.stateRoot,
|
|
1027
|
+
sessionStore: new AwikiSessionStore(tenant.stateRoot),
|
|
1028
|
+
imageAttachmentCache: new AwikiImageAttachmentCache(tenant.stateRoot, this.resolved.attachmentMaxBytes, this.resolved.imageAttachmentCacheMaxBytes),
|
|
1029
|
+
sentMailStore: new AwikiSentMailStore(tenant.stateRoot),
|
|
1030
|
+
conversationPreferenceStore: new AwikiConversationPreferenceStore(tenant.stateRoot),
|
|
1031
|
+
});
|
|
1032
|
+
this.tenantStateCache = value;
|
|
1033
|
+
return value;
|
|
1034
|
+
}
|
|
1035
|
+
get sessionStore() {
|
|
1036
|
+
return this.activeTenantState().sessionStore;
|
|
1037
|
+
}
|
|
1038
|
+
get imageAttachmentCache() {
|
|
1039
|
+
return this.activeTenantState().imageAttachmentCache;
|
|
1040
|
+
}
|
|
1041
|
+
get sentMailStore() {
|
|
1042
|
+
return this.activeTenantState().sentMailStore;
|
|
1043
|
+
}
|
|
1044
|
+
get conversationPreferenceStore() {
|
|
1045
|
+
return this.activeTenantState().conversationPreferenceStore;
|
|
1046
|
+
}
|
|
961
1047
|
/**
|
|
962
1048
|
* Read the deployment's identity status.
|
|
963
1049
|
* @returns The public deployment identity or `null`.
|
|
@@ -1042,7 +1128,7 @@ let AwikiService = (() => {
|
|
|
1042
1128
|
const target = identityAccessTarget(request, this.startupUserServiceDomain);
|
|
1043
1129
|
if (target === undefined)
|
|
1044
1130
|
return { ok: false, error: failure('invalid-request') };
|
|
1045
|
-
const endpoint = new URL(`/.well-known/handle/${encodeURIComponent(target.localPart)}`, this.
|
|
1131
|
+
const endpoint = new URL(`/.well-known/handle/${encodeURIComponent(target.localPart)}`, this.activeTenant().userServiceUrl);
|
|
1046
1132
|
const abort = new AbortController();
|
|
1047
1133
|
const timeout = setTimeout(() => { abort.abort(); }, 10_000);
|
|
1048
1134
|
let response;
|
|
@@ -1496,7 +1582,7 @@ let AwikiService = (() => {
|
|
|
1496
1582
|
}
|
|
1497
1583
|
/** Return the deployment identity's public mailbox state. */
|
|
1498
1584
|
getMailAccount() {
|
|
1499
|
-
return this.run(client => client.getMailAccount());
|
|
1585
|
+
return this.run(client => client.getMailAccount(), { bindMailOwner: true });
|
|
1500
1586
|
}
|
|
1501
1587
|
/** List one bounded mailbox page on explicit browser/tool demand. */
|
|
1502
1588
|
async listMailInbox(request) {
|
|
@@ -1507,11 +1593,11 @@ let AwikiService = (() => {
|
|
|
1507
1593
|
catch {
|
|
1508
1594
|
return { ok: false, error: failure('invalid-request') };
|
|
1509
1595
|
}
|
|
1510
|
-
return this.run(async (client) => {
|
|
1596
|
+
return this.run(async (client, _provider, ownerDid) => {
|
|
1511
1597
|
if (normalized.folder !== 'sent')
|
|
1512
1598
|
return client.listMailInbox(normalized);
|
|
1513
|
-
return this.sentMailStore.list(
|
|
1514
|
-
});
|
|
1599
|
+
return this.sentMailStore.list(ownerDid, normalized);
|
|
1600
|
+
}, { bindMailOwner: true });
|
|
1515
1601
|
}
|
|
1516
1602
|
/** Read one bounded plain-text mail message. */
|
|
1517
1603
|
async readMail(request) {
|
|
@@ -1522,45 +1608,116 @@ let AwikiService = (() => {
|
|
|
1522
1608
|
catch {
|
|
1523
1609
|
return { ok: false, error: failure('invalid-request') };
|
|
1524
1610
|
}
|
|
1525
|
-
return this.run(async (client) => {
|
|
1611
|
+
return this.run(async (client, _provider, ownerDid) => {
|
|
1526
1612
|
if (!isLocalSentMailId(normalized.messageId))
|
|
1527
1613
|
return client.readMail(normalized);
|
|
1528
|
-
const local = await this.sentMailStore.read(
|
|
1614
|
+
const local = await this.sentMailStore.read(ownerDid, normalized.messageId);
|
|
1529
1615
|
if (local === undefined) {
|
|
1530
1616
|
throw Object.assign(new Error('sent mail not found'), { name: 'AwikiSdkError', code: 'not-found' });
|
|
1531
1617
|
}
|
|
1532
1618
|
return local;
|
|
1533
|
-
});
|
|
1619
|
+
}, { bindMailOwner: true });
|
|
1534
1620
|
}
|
|
1535
1621
|
/** Mark explicitly selected mail messages read. Browser callers require an explicit click. */
|
|
1536
1622
|
markMailRead(request) {
|
|
1537
|
-
return this.runValidatedMail(() => mailMarkReadRequest(request), (client, normalized) => client.markMailRead(normalized));
|
|
1623
|
+
return this.runValidatedMail(() => mailMarkReadRequest(request), (client, normalized) => client.markMailRead(normalized), { bindMailOwner: true });
|
|
1538
1624
|
}
|
|
1539
|
-
/** Send one
|
|
1625
|
+
/** Send one mail once. Browser callers require an explicit confirmation. */
|
|
1540
1626
|
async sendMail(request) {
|
|
1541
1627
|
let normalized;
|
|
1542
1628
|
try {
|
|
1543
|
-
normalized = mailSendRequest(request
|
|
1629
|
+
normalized = mailSendRequest(request, {
|
|
1630
|
+
maxCount: this.resolved.mailAttachmentMaxCount,
|
|
1631
|
+
maxBytes: this.resolved.mailAttachmentMaxBytes,
|
|
1632
|
+
totalMaxBytes: this.resolved.mailAttachmentTotalMaxBytes,
|
|
1633
|
+
});
|
|
1544
1634
|
}
|
|
1545
1635
|
catch {
|
|
1546
1636
|
return { ok: false, error: failure('invalid-request') };
|
|
1547
1637
|
}
|
|
1548
|
-
return this.run(async (client) => {
|
|
1549
|
-
const result = await client.sendMail(
|
|
1638
|
+
return this.run(async (client, _provider, ownerDid, assertActive) => {
|
|
1639
|
+
const result = await client.sendMail({
|
|
1640
|
+
to: [...normalized.to],
|
|
1641
|
+
cc: [...normalized.cc],
|
|
1642
|
+
subject: normalized.subject,
|
|
1643
|
+
bodyText: normalized.bodyText,
|
|
1644
|
+
...normalized.attachments.length === 0 ? {} : {
|
|
1645
|
+
attachments: normalized.attachments.map(attachment => ({
|
|
1646
|
+
fileName: attachment.fileName,
|
|
1647
|
+
contentType: attachment.contentType,
|
|
1648
|
+
bytes: attachment.bytes,
|
|
1649
|
+
})),
|
|
1650
|
+
},
|
|
1651
|
+
});
|
|
1652
|
+
assertActive();
|
|
1550
1653
|
if (!result.accepted)
|
|
1551
1654
|
return result;
|
|
1655
|
+
const settled = normalized.attachments.length > 0
|
|
1656
|
+
&& result.messageId === undefined
|
|
1657
|
+
&& result.warnings.length < 100
|
|
1658
|
+
? { ...result, warnings: [...result.warnings, 'Sent attachment download is unavailable because the service returned no message id.'] }
|
|
1659
|
+
: result;
|
|
1552
1660
|
try {
|
|
1553
|
-
const ownerDid = await this.ownerDid(client);
|
|
1554
1661
|
const account = await client.getMailAccount().catch(() => undefined);
|
|
1555
|
-
|
|
1556
|
-
|
|
1662
|
+
assertActive();
|
|
1663
|
+
await this.sentMailStore.append(ownerDid, normalized, settled, account);
|
|
1664
|
+
assertActive();
|
|
1665
|
+
return settled;
|
|
1557
1666
|
}
|
|
1558
1667
|
catch {
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1668
|
+
assertActive();
|
|
1669
|
+
return settled.warnings.length >= 100
|
|
1670
|
+
? settled
|
|
1671
|
+
: { ...settled, warnings: [...settled.warnings, 'Sent history could not be saved locally.'] };
|
|
1562
1672
|
}
|
|
1563
|
-
});
|
|
1673
|
+
}, { bindMailOwner: true });
|
|
1674
|
+
}
|
|
1675
|
+
/** Download one mail attachment only after an explicit browser action. */
|
|
1676
|
+
async downloadMailAttachment(request) {
|
|
1677
|
+
let normalized;
|
|
1678
|
+
try {
|
|
1679
|
+
normalized = mailAttachmentDownloadRequest(request);
|
|
1680
|
+
}
|
|
1681
|
+
catch {
|
|
1682
|
+
return { ok: false, error: failure('invalid-request') };
|
|
1683
|
+
}
|
|
1684
|
+
return this.run(async (client, _provider, ownerDid, assertActive) => {
|
|
1685
|
+
let serviceMessageId = normalized.localMessageId;
|
|
1686
|
+
if (isLocalSentMailId(serviceMessageId)) {
|
|
1687
|
+
const resolved = await this.sentMailStore.resolveAttachment(ownerDid, serviceMessageId, normalized.attachmentIndex);
|
|
1688
|
+
if (resolved === undefined) {
|
|
1689
|
+
throw Object.assign(new Error('sent mail attachment is unavailable'), {
|
|
1690
|
+
name: 'AwikiSdkError', code: 'not-found',
|
|
1691
|
+
});
|
|
1692
|
+
}
|
|
1693
|
+
serviceMessageId = resolved;
|
|
1694
|
+
}
|
|
1695
|
+
const value = await client.downloadMailAttachment({
|
|
1696
|
+
messageId: serviceMessageId,
|
|
1697
|
+
attachmentIndex: normalized.attachmentIndex,
|
|
1698
|
+
});
|
|
1699
|
+
assertActive();
|
|
1700
|
+
if (!Number.isSafeInteger(value.sizeBytes) || value.sizeBytes < 0)
|
|
1701
|
+
throw new TypeError('invalid mail attachment size');
|
|
1702
|
+
if (value.sizeBytes > this.resolved.mailAttachmentMaxBytes) {
|
|
1703
|
+
throw Object.assign(new Error('mail attachment exceeds Host limit'), {
|
|
1704
|
+
name: 'AwikiSdkError', code: 'attachment-too-large',
|
|
1705
|
+
});
|
|
1706
|
+
}
|
|
1707
|
+
if (!(value.bytes instanceof Uint8Array) || value.bytes.byteLength !== value.sizeBytes) {
|
|
1708
|
+
throw new TypeError('invalid mail attachment bytes');
|
|
1709
|
+
}
|
|
1710
|
+
const fileName = mailAttachmentFileName(value.fileName);
|
|
1711
|
+
const contentType = mailAttachmentContentType(value.contentType);
|
|
1712
|
+
const bytes = value.bytes;
|
|
1713
|
+
return {
|
|
1714
|
+
fileName,
|
|
1715
|
+
contentType,
|
|
1716
|
+
sizeBytes: bytes.byteLength,
|
|
1717
|
+
sha256: createHash('sha256').update(bytes).digest('hex'),
|
|
1718
|
+
bytesBase64: Buffer.from(bytes).toString('base64'),
|
|
1719
|
+
};
|
|
1720
|
+
}, { bindMailOwner: true });
|
|
1564
1721
|
}
|
|
1565
1722
|
/**
|
|
1566
1723
|
* Permanently remove the exact SDK-owned local state after an explicit browser acknowledgement.
|
|
@@ -1657,24 +1814,40 @@ let AwikiService = (() => {
|
|
|
1657
1814
|
}
|
|
1658
1815
|
/** Resolve and cache the owner binding required by private Host-side projections. */
|
|
1659
1816
|
async ownerDid(client) {
|
|
1660
|
-
const identity =
|
|
1661
|
-
const ownerDid =
|
|
1817
|
+
const identity = await client.getIdentity();
|
|
1818
|
+
const ownerDid = identity?.did;
|
|
1662
1819
|
if (ownerDid === undefined) {
|
|
1663
1820
|
throw Object.assign(new Error('not registered'), { name: 'AwikiSdkError', code: 'not-registered' });
|
|
1664
1821
|
}
|
|
1822
|
+
if (this.activeIdentityDid !== undefined && this.activeIdentityDid !== ownerDid) {
|
|
1823
|
+
throw new ProviderUnavailableError();
|
|
1824
|
+
}
|
|
1665
1825
|
this.activeIdentityDid = ownerDid;
|
|
1666
1826
|
return ownerDid;
|
|
1667
1827
|
}
|
|
1668
1828
|
/** Invoke the current client and normalize every rejection to a public result. */
|
|
1669
1829
|
async run(operation, options = {}) {
|
|
1670
1830
|
try {
|
|
1831
|
+
const sessionRevision = this.sessionRevision;
|
|
1671
1832
|
if (options.allowSignedOut !== true && await this.isSignedOut()) {
|
|
1672
1833
|
return { ok: false, error: failure('signed-out') };
|
|
1673
1834
|
}
|
|
1674
1835
|
const provider = this.provider;
|
|
1675
1836
|
if (provider === undefined)
|
|
1676
1837
|
throw new ProviderUnavailableError();
|
|
1677
|
-
const
|
|
1838
|
+
const ownerDid = options.bindMailOwner === true
|
|
1839
|
+
? await this.ownerDid(provider.client)
|
|
1840
|
+
: undefined;
|
|
1841
|
+
const assertActive = () => {
|
|
1842
|
+
if (this.provider !== provider
|
|
1843
|
+
|| (options.bindMailOwner === true && (this.sessionRevision !== sessionRevision
|
|
1844
|
+
|| this.signedOut === true
|
|
1845
|
+
|| this.activeIdentityDid !== ownerDid)))
|
|
1846
|
+
throw new ProviderUnavailableError();
|
|
1847
|
+
};
|
|
1848
|
+
assertActive();
|
|
1849
|
+
const value = await operation(provider.client, provider, ownerDid, assertActive);
|
|
1850
|
+
assertActive();
|
|
1678
1851
|
if (options.skipAttachmentByteValidation !== true && containsUnexpectedBinary(value, new Set())) {
|
|
1679
1852
|
return { ok: false, error: failure('remote') };
|
|
1680
1853
|
}
|
|
@@ -1685,7 +1858,7 @@ let AwikiService = (() => {
|
|
|
1685
1858
|
}
|
|
1686
1859
|
}
|
|
1687
1860
|
/** Validate mail input before entering the provider and preserve fixed public failures. */
|
|
1688
|
-
runValidatedMail(validate, operation) {
|
|
1861
|
+
runValidatedMail(validate, operation, options = {}) {
|
|
1689
1862
|
let request;
|
|
1690
1863
|
try {
|
|
1691
1864
|
request = validate();
|
|
@@ -1693,7 +1866,7 @@ let AwikiService = (() => {
|
|
|
1693
1866
|
catch {
|
|
1694
1867
|
return Promise.resolve({ ok: false, error: failure('invalid-request') });
|
|
1695
1868
|
}
|
|
1696
|
-
return this.run(client => operation(client, request));
|
|
1869
|
+
return this.run(client => operation(client, request), options);
|
|
1697
1870
|
}
|
|
1698
1871
|
/** Read and cache the private Host-owned session marker. */
|
|
1699
1872
|
async isSignedOut() {
|
|
@@ -1774,7 +1947,10 @@ let AwikiService = (() => {
|
|
|
1774
1947
|
if (!this.listenerFenceMatches(provider, workspaceContext, generation))
|
|
1775
1948
|
return;
|
|
1776
1949
|
const agents = new DshAwikiListenerAgentRuntime(workspaceContext, this.resolved.listener.workspacePath);
|
|
1777
|
-
const listener = new AwikiAgentListener(source, agents,
|
|
1950
|
+
const listener = new AwikiAgentListener(source, agents, {
|
|
1951
|
+
...this.resolved.listener,
|
|
1952
|
+
stateRoot: this.activeTenant().stateRoot,
|
|
1953
|
+
}, logger);
|
|
1778
1954
|
provider.listener = listener;
|
|
1779
1955
|
try {
|
|
1780
1956
|
await listener.start();
|