@awiki/dsh-plugin 0.2.5 → 0.3.0-rc.1

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.
Files changed (110) hide show
  1. package/README.md +63 -23
  2. package/README.zh.md +55 -20
  3. package/THIRD_PARTY_NOTICES.md +1 -1
  4. package/cordis.patch.yml +0 -8
  5. package/lib/client.js +5452 -1849
  6. package/lib/client.js.map +1 -1
  7. package/lib/index.js +950 -38
  8. package/lib/model-proxy-contract.js +8 -1
  9. package/lib/provider.js +3 -1
  10. package/lib/{sdk-adapter-DKgkyApB.mjs → sdk-adapter-DLhLrLI6.mjs} +219 -2
  11. package/lib/typert.host.js +796 -49
  12. package/lib/typert.remote-client.d.ts +33 -1
  13. package/lib/typert.remote-client.d.ts.map +1 -1
  14. package/lib/typert.remote-client.js +796 -49
  15. package/lib/types/client/AwikiGroupDetails.d.ts +16 -0
  16. package/lib/types/client/AwikiGroupDetails.d.ts.map +1 -0
  17. package/lib/types/client/AwikiGroupDetails.js +108 -0
  18. package/lib/types/client/AwikiGroupDetails.js.map +1 -0
  19. package/lib/types/client/AwikiIdentityAccess.d.ts +21 -0
  20. package/lib/types/client/AwikiIdentityAccess.d.ts.map +1 -0
  21. package/lib/types/client/AwikiIdentityAccess.js +148 -0
  22. package/lib/types/client/AwikiIdentityAccess.js.map +1 -0
  23. package/lib/types/client/AwikiIdentityPage.d.ts +11 -0
  24. package/lib/types/client/AwikiIdentityPage.d.ts.map +1 -0
  25. package/lib/types/client/AwikiIdentityPage.js +8 -0
  26. package/lib/types/client/AwikiIdentityPage.js.map +1 -0
  27. package/lib/types/client/AwikiMail.d.ts +2 -1
  28. package/lib/types/client/AwikiMail.d.ts.map +1 -1
  29. package/lib/types/client/AwikiMail.js +153 -33
  30. package/lib/types/client/AwikiMail.js.map +1 -1
  31. package/lib/types/client/AwikiOnboarding.d.ts.map +1 -1
  32. package/lib/types/client/AwikiOnboarding.js +26 -9
  33. package/lib/types/client/AwikiOnboarding.js.map +1 -1
  34. package/lib/types/client/AwikiOverlay.d.ts +0 -5
  35. package/lib/types/client/AwikiOverlay.d.ts.map +1 -1
  36. package/lib/types/client/AwikiOverlay.js +136 -124
  37. package/lib/types/client/AwikiOverlay.js.map +1 -1
  38. package/lib/types/client/AwikiProfileCard.d.ts +9 -0
  39. package/lib/types/client/AwikiProfileCard.d.ts.map +1 -0
  40. package/lib/types/client/AwikiProfileCard.js +90 -0
  41. package/lib/types/client/AwikiProfileCard.js.map +1 -0
  42. package/lib/types/client/AwikiRecoveryForm.d.ts +24 -0
  43. package/lib/types/client/AwikiRecoveryForm.d.ts.map +1 -0
  44. package/lib/types/client/AwikiRecoveryForm.js +148 -0
  45. package/lib/types/client/AwikiRecoveryForm.js.map +1 -0
  46. package/lib/types/client/AwikiSettingsSection.d.ts +0 -2
  47. package/lib/types/client/AwikiSettingsSection.d.ts.map +1 -1
  48. package/lib/types/client/AwikiSettingsSection.js +13 -15
  49. package/lib/types/client/AwikiSettingsSection.js.map +1 -1
  50. package/lib/types/client/MentionText.d.ts +7 -0
  51. package/lib/types/client/MentionText.d.ts.map +1 -0
  52. package/lib/types/client/MentionText.js +10 -0
  53. package/lib/types/client/MentionText.js.map +1 -0
  54. package/lib/types/client/controller.d.ts +94 -2
  55. package/lib/types/client/controller.d.ts.map +1 -1
  56. package/lib/types/client/controller.js +466 -13
  57. package/lib/types/client/controller.js.map +1 -1
  58. package/lib/types/client/index.d.ts.map +1 -1
  59. package/lib/types/client/index.js +21 -4
  60. package/lib/types/client/index.js.map +1 -1
  61. package/lib/types/client/mail-list-cache.d.ts +15 -0
  62. package/lib/types/client/mail-list-cache.d.ts.map +1 -0
  63. package/lib/types/client/mail-list-cache.js +212 -0
  64. package/lib/types/client/mail-list-cache.js.map +1 -0
  65. package/lib/types/client/mentions.d.ts +51 -0
  66. package/lib/types/client/mentions.d.ts.map +1 -0
  67. package/lib/types/client/mentions.js +165 -0
  68. package/lib/types/client/mentions.js.map +1 -0
  69. package/lib/types/client/model-proxy-controller.d.ts +4 -0
  70. package/lib/types/client/model-proxy-controller.d.ts.map +1 -1
  71. package/lib/types/client/model-proxy-controller.js +68 -3
  72. package/lib/types/client/model-proxy-controller.js.map +1 -1
  73. package/lib/types/client/settings-locales.js +8 -8
  74. package/lib/types/client/settings-locales.js.map +1 -1
  75. package/lib/types/client/slots.d.ts +21 -2
  76. package/lib/types/client/slots.d.ts.map +1 -1
  77. package/lib/types/index.d.ts +40 -6
  78. package/lib/types/index.d.ts.map +1 -1
  79. package/lib/types/index.js +523 -23
  80. package/lib/types/index.js.map +1 -1
  81. package/lib/types/model-proxy-contract.d.ts +6 -0
  82. package/lib/types/model-proxy-contract.d.ts.map +1 -1
  83. package/lib/types/model-proxy-contract.js +6 -0
  84. package/lib/types/model-proxy-contract.js.map +1 -1
  85. package/lib/types/provider-api.d.ts +29 -1
  86. package/lib/types/provider-api.d.ts.map +1 -1
  87. package/lib/types/provider.d.ts.map +1 -1
  88. package/lib/types/provider.js +2 -0
  89. package/lib/types/provider.js.map +1 -1
  90. package/lib/types/sdk-adapter.d.ts +18 -1
  91. package/lib/types/sdk-adapter.d.ts.map +1 -1
  92. package/lib/types/sdk-adapter.js +244 -2
  93. package/lib/types/sdk-adapter.js.map +1 -1
  94. package/lib/types/sent-mail-store.d.ts +22 -0
  95. package/lib/types/sent-mail-store.d.ts.map +1 -0
  96. package/lib/types/sent-mail-store.js +246 -0
  97. package/lib/types/sent-mail-store.js.map +1 -0
  98. package/lib/types/types.d.ts +152 -2
  99. package/lib/types/types.d.ts.map +1 -1
  100. package/lib/types/types.js.map +1 -1
  101. package/package.json +15 -26
  102. package/lib/model-proxy.js +0 -383
  103. package/lib/types/client/model-account-settings-availability.d.ts +0 -4
  104. package/lib/types/client/model-account-settings-availability.d.ts.map +0 -1
  105. package/lib/types/client/model-account-settings-availability.js +0 -4
  106. package/lib/types/client/model-account-settings-availability.js.map +0 -1
  107. package/lib/types/model-proxy.d.ts +0 -14
  108. package/lib/types/model-proxy.d.ts.map +0 -1
  109. package/lib/types/model-proxy.js +0 -371
  110. package/lib/types/model-proxy.js.map +0 -1
package/lib/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { n as downloadedAttachment } from "./sdk-adapter-DKgkyApB.mjs";
1
+ import { n as downloadedAttachment } from "./sdk-adapter-DLhLrLI6.mjs";
2
2
  import "@deepseek-ai/cordis";
3
3
  import { homedir } from "node:os";
4
4
  import { isAbsolute, join } from "node:path";
@@ -740,7 +740,7 @@ var AwikiSessionStore = class {
740
740
  if (await readFile(this.markerPath, "utf8") !== SIGNED_OUT_MARKER) throw new TypeError("awiki: local session marker is invalid");
741
741
  return true;
742
742
  } catch (error) {
743
- if (isMissing$2(error)) return false;
743
+ if (isMissing$3(error)) return false;
744
744
  throw error;
745
745
  }
746
746
  }
@@ -758,7 +758,7 @@ var AwikiSessionStore = class {
758
758
  mode: 384
759
759
  });
760
760
  } catch (error) {
761
- if (!isFileExists$1(error) || !await this.isSignedOut()) throw error;
761
+ if (!isFileExists$2(error) || !await this.isSignedOut()) throw error;
762
762
  }
763
763
  await chmod(this.markerPath, 384);
764
764
  }
@@ -767,7 +767,7 @@ var AwikiSessionStore = class {
767
767
  try {
768
768
  await unlink(this.markerPath);
769
769
  } catch (error) {
770
- if (!isMissing$2(error)) throw error;
770
+ if (!isMissing$3(error)) throw error;
771
771
  }
772
772
  }
773
773
  async hasPrivateHostDirectory() {
@@ -776,15 +776,15 @@ var AwikiSessionStore = class {
776
776
  if (!directory.isDirectory() || directory.isSymbolicLink()) throw new TypeError("awiki: local session directory is invalid");
777
777
  return true;
778
778
  } catch (error) {
779
- if (isMissing$2(error)) return false;
779
+ if (isMissing$3(error)) return false;
780
780
  throw error;
781
781
  }
782
782
  }
783
783
  };
784
- function isMissing$2(error) {
784
+ function isMissing$3(error) {
785
785
  return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
786
786
  }
787
- function isFileExists$1(error) {
787
+ function isFileExists$2(error) {
788
788
  return typeof error === "object" && error !== null && "code" in error && error.code === "EEXIST";
789
789
  }
790
790
  //#endregion
@@ -877,7 +877,7 @@ var AwikiImageAttachmentCache = class {
877
877
  force: true
878
878
  });
879
879
  } catch (error) {
880
- if (!isMissing$1(error)) throw error;
880
+ if (!isMissing$2(error)) throw error;
881
881
  }
882
882
  }
883
883
  decode(input, ownerDid, request) {
@@ -903,7 +903,7 @@ var AwikiImageAttachmentCache = class {
903
903
  if (!await this.hasPrivateDirectory(this.hostDirectory)) throw new TypeError("awiki: local Host directory is invalid");
904
904
  await chmod(this.hostDirectory, 448);
905
905
  await mkdir(this.directory, { mode: 448 }).catch((error) => {
906
- if (!isFileExists(error)) throw error;
906
+ if (!isFileExists$1(error)) throw error;
907
907
  });
908
908
  if (!await this.hasPrivateDirectory(this.directory)) throw new TypeError("awiki: local image cache directory is invalid");
909
909
  await chmod(this.directory, 448);
@@ -941,18 +941,234 @@ var AwikiImageAttachmentCache = class {
941
941
  if (!metadata.isDirectory() || metadata.isSymbolicLink()) throw new TypeError("awiki: local image cache path is invalid");
942
942
  return true;
943
943
  } catch (error) {
944
- if (isMissing$1(error)) return false;
944
+ if (isMissing$2(error)) return false;
945
945
  throw error;
946
946
  }
947
947
  }
948
948
  };
949
- function isMissing$1(error) {
949
+ function isMissing$2(error) {
950
950
  return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
951
951
  }
952
- function isFileExists(error) {
952
+ function isFileExists$1(error) {
953
953
  return typeof error === "object" && error !== null && "code" in error && error.code === "EEXIST";
954
954
  }
955
955
  //#endregion
956
+ //#region lib/types/sent-mail-store.js
957
+ /** Private, owner-bound persistence for mail successfully sent by this installation. */
958
+ const STORE_VERSION = 1;
959
+ const STORE_DIRECTORY = "sent-mail-v1";
960
+ const MAX_RECORDS = 200;
961
+ const MAX_STORE_BYTES = 16777216;
962
+ const PREVIEW_CHARACTERS = 160;
963
+ const LOCAL_SENT_MAIL_ID_PREFIX = "awiki-sent-v1:";
964
+ function invalidState() {
965
+ throw new TypeError("awiki: local sent-mail history is invalid");
966
+ }
967
+ function isMissing$1(error) {
968
+ return typeof error === "object" && error !== null && error.code === "ENOENT";
969
+ }
970
+ function isFileExists(error) {
971
+ return typeof error === "object" && error !== null && error.code === "EEXIST";
972
+ }
973
+ function validToken(value, maxCharacters) {
974
+ return typeof value === "string" && value.length > 0 && value.trim() === value && Array.from(value).length <= maxCharacters && !/[\u0000-\u001f\u007f-\u009f]/u.test(value);
975
+ }
976
+ function validAddress(value) {
977
+ return typeof value === "string" && Array.from(value).length >= 3 && Array.from(value).length <= 320 && value.includes("@") && !/[\s\u0000-\u001f\u007f-\u009f]/u.test(value);
978
+ }
979
+ function decodeRecord(input) {
980
+ if (typeof input !== "object" || input === null || Array.isArray(input)) invalidState();
981
+ const value = input;
982
+ if (typeof value.id !== "string" || !/^awiki-sent-v1:[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u.test(value.id) || value.serviceMessageId !== void 0 && !validToken(value.serviceMessageId, 2048) || value.from !== void 0 && !validAddress(value.from) || typeof value.sentAt !== "string" || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/u.test(value.sentAt) || !Number.isFinite(Date.parse(value.sentAt))) invalidState();
983
+ let request;
984
+ try {
985
+ request = mailSendRequest({
986
+ to: value.to,
987
+ cc: value.cc,
988
+ subject: value.subject,
989
+ bodyText: value.bodyText
990
+ });
991
+ } catch {
992
+ invalidState();
993
+ }
994
+ return {
995
+ id: value.id,
996
+ ...value.serviceMessageId === void 0 ? {} : { serviceMessageId: value.serviceMessageId },
997
+ ...value.from === void 0 ? {} : { from: value.from },
998
+ to: [...request.to],
999
+ cc: [...request.cc ?? []],
1000
+ subject: request.subject,
1001
+ bodyText: request.bodyText,
1002
+ sentAt: value.sentAt
1003
+ };
1004
+ }
1005
+ function message(record) {
1006
+ const characters = Array.from(record.bodyText);
1007
+ const preview = characters.slice(0, PREVIEW_CHARACTERS).join("");
1008
+ return {
1009
+ summary: {
1010
+ id: record.id,
1011
+ folder: "sent",
1012
+ from: record.from === void 0 ? [] : [record.from],
1013
+ to: [...record.to],
1014
+ cc: [...record.cc],
1015
+ subject: record.subject,
1016
+ subjectTruncated: false,
1017
+ preview,
1018
+ previewTruncated: characters.length > PREVIEW_CHARACTERS,
1019
+ sentAt: record.sentAt,
1020
+ unread: false,
1021
+ hasAttachments: false,
1022
+ attachmentCount: 0
1023
+ },
1024
+ bodyText: record.bodyText,
1025
+ bodyTruncated: false,
1026
+ hasHtmlBody: false,
1027
+ attachments: []
1028
+ };
1029
+ }
1030
+ function isLocalSentMailId(messageId) {
1031
+ return String(messageId).startsWith(LOCAL_SENT_MAIL_ID_PREFIX);
1032
+ }
1033
+ /** Atomic, bounded history of sends accepted by the Mail Service. */
1034
+ var AwikiSentMailStore = class {
1035
+ hostDirectory;
1036
+ directory;
1037
+ mutation = Promise.resolve();
1038
+ constructor(stateRoot) {
1039
+ this.hostDirectory = join(stateRoot, ".host");
1040
+ this.directory = join(this.hostDirectory, STORE_DIRECTORY);
1041
+ }
1042
+ async list(ownerDid, request) {
1043
+ const records = request.unreadOnly === true ? [] : await this.load(ownerDid);
1044
+ const offset = request.offset ?? 0;
1045
+ const limit = request.limit ?? 20;
1046
+ const page = records.slice(offset, offset + limit);
1047
+ const nextOffset = offset + page.length;
1048
+ const hasMore = nextOffset < records.length;
1049
+ return {
1050
+ items: page.map((record) => message(record).summary),
1051
+ ...hasMore ? { nextOffset } : {},
1052
+ hasMore
1053
+ };
1054
+ }
1055
+ async read(ownerDid, messageId) {
1056
+ if (!isLocalSentMailId(messageId)) return void 0;
1057
+ const record = (await this.load(ownerDid)).find((candidate) => candidate.id === String(messageId));
1058
+ return record === void 0 ? void 0 : message(record);
1059
+ }
1060
+ append(ownerDid, request, result, account) {
1061
+ const normalized = mailSendRequest(request);
1062
+ const record = {
1063
+ id: `${LOCAL_SENT_MAIL_ID_PREFIX}${randomUUID()}`,
1064
+ ...result.messageId === void 0 ? {} : { serviceMessageId: String(result.messageId) },
1065
+ ...account?.mailboxAddress === void 0 ? {} : { from: account.mailboxAddress },
1066
+ to: [...normalized.to],
1067
+ cc: [...normalized.cc ?? []],
1068
+ subject: normalized.subject,
1069
+ bodyText: normalized.bodyText,
1070
+ sentAt: (/* @__PURE__ */ new Date()).toISOString()
1071
+ };
1072
+ const append = async () => {
1073
+ const records = await this.load(ownerDid);
1074
+ await this.write(ownerDid, [record, ...records].slice(0, MAX_RECORDS));
1075
+ };
1076
+ const pending = this.mutation.then(append, append);
1077
+ this.mutation = pending.catch(() => void 0);
1078
+ return pending;
1079
+ }
1080
+ async clear() {
1081
+ await this.mutation;
1082
+ try {
1083
+ if (!await this.hasDirectory(this.hostDirectory)) return;
1084
+ const metadata = await lstat(this.directory);
1085
+ if (metadata.isSymbolicLink() || !metadata.isDirectory()) {
1086
+ await unlink(this.directory);
1087
+ return;
1088
+ }
1089
+ await rm(this.directory, {
1090
+ recursive: true,
1091
+ force: true
1092
+ });
1093
+ } catch (error) {
1094
+ if (!isMissing$1(error)) throw error;
1095
+ }
1096
+ }
1097
+ async load(ownerDid) {
1098
+ if (!await this.hasDirectory(this.hostDirectory) || !await this.hasDirectory(this.directory)) return [];
1099
+ const path = this.path(ownerDid);
1100
+ let text;
1101
+ try {
1102
+ const metadata = await lstat(path);
1103
+ if (!metadata.isFile() || metadata.isSymbolicLink() || metadata.size > MAX_STORE_BYTES) invalidState();
1104
+ text = await readFile(path, "utf8");
1105
+ } catch (error) {
1106
+ if (isMissing$1(error)) return [];
1107
+ throw error;
1108
+ }
1109
+ let parsed;
1110
+ try {
1111
+ parsed = JSON.parse(text);
1112
+ } catch {
1113
+ invalidState();
1114
+ }
1115
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) invalidState();
1116
+ const value = parsed;
1117
+ if (value.version !== STORE_VERSION || value.ownerDid !== String(ownerDid) || !Array.isArray(value.records) || value.records.length > MAX_RECORDS) invalidState();
1118
+ return value.records.map(decodeRecord);
1119
+ }
1120
+ async write(ownerDid, records) {
1121
+ await this.ensureDirectory();
1122
+ const path = this.path(ownerDid);
1123
+ const temporary = join(this.directory, `.${this.key(ownerDid)}.${randomUUID()}.tmp`);
1124
+ const text = JSON.stringify({
1125
+ version: STORE_VERSION,
1126
+ ownerDid: String(ownerDid),
1127
+ records
1128
+ });
1129
+ if (Buffer.byteLength(text, "utf8") > MAX_STORE_BYTES) invalidState();
1130
+ try {
1131
+ await writeFile(temporary, text, {
1132
+ flag: "wx",
1133
+ mode: 384
1134
+ });
1135
+ await rename(temporary, path);
1136
+ await chmod(path, 384);
1137
+ } finally {
1138
+ await unlink(temporary).catch(() => void 0);
1139
+ }
1140
+ }
1141
+ async ensureDirectory() {
1142
+ await mkdir(this.hostDirectory, {
1143
+ recursive: true,
1144
+ mode: 448
1145
+ });
1146
+ if (!await this.hasDirectory(this.hostDirectory)) invalidState();
1147
+ await chmod(this.hostDirectory, 448);
1148
+ await mkdir(this.directory, { mode: 448 }).catch((error) => {
1149
+ if (!isFileExists(error)) throw error;
1150
+ });
1151
+ if (!await this.hasDirectory(this.directory)) invalidState();
1152
+ await chmod(this.directory, 448);
1153
+ }
1154
+ async hasDirectory(path) {
1155
+ try {
1156
+ const metadata = await lstat(path);
1157
+ if (!metadata.isDirectory() || metadata.isSymbolicLink()) invalidState();
1158
+ return true;
1159
+ } catch (error) {
1160
+ if (isMissing$1(error)) return false;
1161
+ throw error;
1162
+ }
1163
+ }
1164
+ path(ownerDid) {
1165
+ return join(this.directory, `${this.key(ownerDid)}.json`);
1166
+ }
1167
+ key(ownerDid) {
1168
+ return createHash("sha256").update(String(ownerDid)).digest("hex");
1169
+ }
1170
+ };
1171
+ //#endregion
956
1172
  //#region lib/types/listener-state.js
957
1173
  const STATE_VERSION = 1;
958
1174
  const MAX_STATE_BYTES = 1048576;
@@ -1685,6 +1901,8 @@ const FAILURE_CODES = /* @__PURE__ */ new Set([
1685
1901
  "forbidden",
1686
1902
  "conflict",
1687
1903
  "rate-limited",
1904
+ "group-membership-required",
1905
+ "group-identity-stale",
1688
1906
  "attachment-too-large",
1689
1907
  "summary-unavailable",
1690
1908
  "summary-timeout",
@@ -1711,6 +1929,8 @@ const FAILURE_MESSAGES = {
1711
1929
  "forbidden": "The AWiki operation is not permitted.",
1712
1930
  "conflict": "The AWiki operation conflicts with current state.",
1713
1931
  "rate-limited": "The AWiki service rate-limited the request.",
1932
+ "group-membership-required": "The active AWiki identity is not a member of this group.",
1933
+ "group-identity-stale": "The AWiki group identity binding is still recovering.",
1714
1934
  "attachment-too-large": "The attachment exceeds this deployment's size limit.",
1715
1935
  "summary-unavailable": "AI summary is unavailable. Check the current default model configuration.",
1716
1936
  "summary-timeout": "AI summary timed out. Try again.",
@@ -1840,11 +2060,16 @@ function normalizeFailure(error) {
1840
2060
  const MAX_GROUP_NAME_CHARACTERS = 100;
1841
2061
  const MAX_GROUP_INITIAL_MEMBERS = 50;
1842
2062
  const MAX_GROUP_MEMBER_CHARACTERS = 512;
2063
+ const MAX_PROFILE_DISPLAY_NAME_CHARACTERS = 50;
2064
+ const MAX_PROFILE_BIO_CHARACTERS = 100;
2065
+ const MAX_PROFILE_TAGS = 5;
2066
+ const MAX_PROFILE_TAG_CHARACTERS = 30;
2067
+ const MAX_MESSAGE_CHARACTERS = 2e4;
1843
2068
  function normalizeCreateGroupRequest(request) {
1844
2069
  if (typeof request?.name !== "string" || !Array.isArray(request.members)) return void 0;
1845
2070
  const name = request.name.trim();
1846
2071
  if (name.length === 0 || Array.from(name).length > MAX_GROUP_NAME_CHARACTERS) return void 0;
1847
- if (request.members.length === 0 || request.members.length > MAX_GROUP_INITIAL_MEMBERS) return void 0;
2072
+ if (request.members.length > MAX_GROUP_INITIAL_MEMBERS) return void 0;
1848
2073
  const members = [];
1849
2074
  const seen = /* @__PURE__ */ new Set();
1850
2075
  for (const raw of request.members) {
@@ -1855,11 +2080,159 @@ function normalizeCreateGroupRequest(request) {
1855
2080
  seen.add(member);
1856
2081
  members.push(member);
1857
2082
  }
1858
- return members.length === 0 ? void 0 : {
2083
+ return {
1859
2084
  name,
1860
2085
  members
1861
2086
  };
1862
2087
  }
2088
+ function normalizeMember(value) {
2089
+ if (typeof value !== "string") return void 0;
2090
+ const member = value.trim().replace(/^@+/u, "");
2091
+ return member.length > 0 && Array.from(member).length <= MAX_GROUP_MEMBER_CHARACTERS ? member : void 0;
2092
+ }
2093
+ function normalizeGroupDid(value) {
2094
+ if (typeof value !== "string" || !value.startsWith("did:") || value.length > 2048) return void 0;
2095
+ return value;
2096
+ }
2097
+ /** Rebuild the browser-safe recovery DTO instead of trusting provider object shape. */
2098
+ function publicGroupRebindRecoverySummary(value) {
2099
+ if (typeof value !== "object" || value === null) return void 0;
2100
+ const candidate = value;
2101
+ if ([
2102
+ candidate.processed,
2103
+ candidate.completed,
2104
+ candidate.pending,
2105
+ candidate.blocked
2106
+ ].some((count) => !Number.isSafeInteger(count) || count < 0 || count > 4294967295)) return;
2107
+ return {
2108
+ processed: candidate.processed,
2109
+ completed: candidate.completed,
2110
+ pending: candidate.pending,
2111
+ blocked: candidate.blocked
2112
+ };
2113
+ }
2114
+ function normalizeProfileRequest(request) {
2115
+ if (typeof request?.displayName !== "string" || typeof request.bio !== "string" || !Array.isArray(request.tags)) return void 0;
2116
+ const displayName = request.displayName.trim();
2117
+ const bio = request.bio.trim();
2118
+ if (displayName.length === 0 || Array.from(displayName).length > MAX_PROFILE_DISPLAY_NAME_CHARACTERS || Array.from(bio).length > MAX_PROFILE_BIO_CHARACTERS || request.tags.length > MAX_PROFILE_TAGS) return void 0;
2119
+ const tags = [];
2120
+ const seen = /* @__PURE__ */ new Set();
2121
+ for (const raw of request.tags) {
2122
+ if (typeof raw !== "string") return void 0;
2123
+ const tag = raw.trim();
2124
+ const key = tag.toLocaleLowerCase();
2125
+ if (tag.length === 0 || Array.from(tag).length > MAX_PROFILE_TAG_CHARACTERS || seen.has(key)) return void 0;
2126
+ seen.add(key);
2127
+ tags.push(tag);
2128
+ }
2129
+ return {
2130
+ displayName,
2131
+ bio,
2132
+ tags
2133
+ };
2134
+ }
2135
+ function normalizeRecoveryOperation(request) {
2136
+ if (typeof request?.operationId !== "string") return void 0;
2137
+ const operationId = request.operationId.trim();
2138
+ return operationId.length > 0 && operationId.length <= 512 ? { operationId } : void 0;
2139
+ }
2140
+ function normalizeRecoveryOtpRequest(request) {
2141
+ if (typeof request?.fullHandle !== "string" || typeof request.phone !== "string") return void 0;
2142
+ const fullHandle = request.fullHandle.trim().replace(/^@+/u, "");
2143
+ const phone = request.phone.trim();
2144
+ if (fullHandle.length === 0 || fullHandle.length > 512 || phone.length < 5 || phone.length > 32) return void 0;
2145
+ return {
2146
+ fullHandle,
2147
+ phone
2148
+ };
2149
+ }
2150
+ function normalizeRecoveryPrepareRequest(request) {
2151
+ const operation = normalizeRecoveryOperation(request);
2152
+ if (operation === void 0 || typeof request.phone !== "string" || typeof request.otp !== "string") return void 0;
2153
+ const phone = request.phone.trim();
2154
+ const otp = request.otp.trim();
2155
+ if (phone.length < 5 || phone.length > 32 || !/^\d{4,12}$/u.test(otp)) return void 0;
2156
+ return {
2157
+ ...operation,
2158
+ phone,
2159
+ otp
2160
+ };
2161
+ }
2162
+ const IDENTITY_ACCESS_RESPONSE_MAX_BYTES = 65536;
2163
+ /** Read one untrusted discovery response without buffering beyond the fixed Host limit. */
2164
+ async function readBoundedResponseText(response, maxBytes) {
2165
+ const declaredLength = response.headers.get("content-length");
2166
+ if (declaredLength !== null && /^\d+$/u.test(declaredLength) && Number(declaredLength) > maxBytes) return void 0;
2167
+ if (response.body === null) return "";
2168
+ const reader = response.body.getReader();
2169
+ const chunks = [];
2170
+ let length = 0;
2171
+ try {
2172
+ while (true) {
2173
+ const result = await reader.read();
2174
+ if (result.done) break;
2175
+ length += result.value.byteLength;
2176
+ if (length > maxBytes) {
2177
+ await reader.cancel();
2178
+ return;
2179
+ }
2180
+ chunks.push(Uint8Array.from(result.value));
2181
+ }
2182
+ } catch {
2183
+ return;
2184
+ } finally {
2185
+ reader.releaseLock();
2186
+ }
2187
+ const bytes = new Uint8Array(length);
2188
+ let offset = 0;
2189
+ for (const chunk of chunks) {
2190
+ bytes.set(chunk, offset);
2191
+ offset += chunk.byteLength;
2192
+ }
2193
+ return Buffer.from(bytes).toString("utf8");
2194
+ }
2195
+ /** Resolve one configured-domain Handle without widening registration authority. */
2196
+ function identityAccessTarget(request, configuredDomain) {
2197
+ if (typeof request?.handle !== "string") return void 0;
2198
+ const raw = request.handle.trim();
2199
+ if (raw.length === 0 || raw.length > 255 || /[\u0000-\u001f\u007f]/u.test(raw)) return void 0;
2200
+ const lowered = raw.replace(/[A-Z]/gu, (character) => character.toLowerCase());
2201
+ const handle = lowered.startsWith("wba://") ? lowered.slice(6) : lowered;
2202
+ const dot = handle.indexOf(".");
2203
+ const localPart = (dot < 0 ? handle : handle.slice(0, dot)).trim();
2204
+ const domain = (dot < 0 ? configuredDomain : handle.slice(dot + 1)).trim().replace(/\.$/u, "");
2205
+ if (localPart.length === 0 || domain !== configuredDomain) return void 0;
2206
+ return {
2207
+ localPart,
2208
+ fullHandle: `${localPart}.${configuredDomain}`
2209
+ };
2210
+ }
2211
+ function normalizeSendTextRequest(request) {
2212
+ if (typeof request?.text !== "string" || typeof request.idempotencyKey !== "string" || typeof request.target !== "object" || request.target === null) return void 0;
2213
+ const text = request.text;
2214
+ const codePoints = Array.from(text);
2215
+ if (text.trim().length === 0 || codePoints.length > MAX_MESSAGE_CHARACTERS || request.idempotencyKey.length === 0 || request.idempotencyKey.length > 512) return void 0;
2216
+ if (request.target.kind === "direct") {
2217
+ if (typeof request.target.peer !== "string" || request.mentions !== void 0) return void 0;
2218
+ } else if (request.target.kind === "group") {
2219
+ if (typeof request.target.group !== "string") return void 0;
2220
+ } else return void 0;
2221
+ if (request.mentions === void 0) return request;
2222
+ if (!Array.isArray(request.mentions) || request.mentions.length === 0 || request.mentions.length > 100) return void 0;
2223
+ const ids = /* @__PURE__ */ new Set();
2224
+ let previousEnd = 0;
2225
+ const mentions = [...request.mentions].sort((left, right) => left.start - right.start || left.end - right.end);
2226
+ for (const mention of mentions) {
2227
+ if (typeof mention !== "object" || mention === null || typeof mention.id !== "string" || mention.id.trim() === "" || ids.has(mention.id) || !Number.isSafeInteger(mention.start) || !Number.isSafeInteger(mention.end) || mention.start < previousEnd || mention.start < 0 || mention.end <= mention.start || mention.end > codePoints.length || typeof mention.did !== "string" || !mention.did.startsWith("did:") || mention.displayName !== void 0 && typeof mention.displayName !== "string") return void 0;
2228
+ ids.add(mention.id);
2229
+ previousEnd = mention.end;
2230
+ }
2231
+ return {
2232
+ ...request,
2233
+ mentions
2234
+ };
2235
+ }
1863
2236
  /** Normalize summary-provider failures without returning prompts, model output, routes, or causes. */
1864
2237
  function normalizeSummaryFailure(error) {
1865
2238
  if (error instanceof SummaryProviderUnavailableError) return failure("summary-unavailable");
@@ -2001,11 +2374,27 @@ let AwikiService = (() => {
2001
2374
  let _getSession_decorators;
2002
2375
  let _logout_decorators;
2003
2376
  let _login_decorators;
2377
+ let _inspectIdentityAccess_decorators;
2004
2378
  let _sendRegistrationOtp_decorators;
2005
2379
  let _registerIdentity_decorators;
2006
2380
  let _updateDisplayName_decorators;
2381
+ let _getProfile_decorators;
2382
+ let _updateProfile_decorators;
2383
+ let _sendRecoveryOtp_decorators;
2384
+ let _prepareRecovery_decorators;
2385
+ let _activateRecovery_decorators;
2386
+ let _getRecoveryStatus_decorators;
2387
+ let _resumeRecovery_decorators;
2388
+ let _discardRecovery_decorators;
2007
2389
  let _resolvePeer_decorators;
2008
2390
  let _createGroup_decorators;
2391
+ let _getGroup_decorators;
2392
+ let _joinGroup_decorators;
2393
+ let _leaveGroup_decorators;
2394
+ let _listGroupMembers_decorators;
2395
+ let _addGroupMember_decorators;
2396
+ let _removeGroupMember_decorators;
2397
+ let _resumeGroupRebindRecovery_decorators;
2009
2398
  let _listConversations_decorators;
2010
2399
  let _getHistory_decorators;
2011
2400
  let _getLocalHistory_decorators;
@@ -2028,11 +2417,27 @@ let AwikiService = (() => {
2028
2417
  _getSession_decorators = [Remote];
2029
2418
  _logout_decorators = [Remote];
2030
2419
  _login_decorators = [Remote];
2420
+ _inspectIdentityAccess_decorators = [Remote];
2031
2421
  _sendRegistrationOtp_decorators = [Remote];
2032
2422
  _registerIdentity_decorators = [Remote];
2033
2423
  _updateDisplayName_decorators = [Remote];
2424
+ _getProfile_decorators = [Remote];
2425
+ _updateProfile_decorators = [Remote];
2426
+ _sendRecoveryOtp_decorators = [Remote];
2427
+ _prepareRecovery_decorators = [Remote];
2428
+ _activateRecovery_decorators = [Remote];
2429
+ _getRecoveryStatus_decorators = [Remote];
2430
+ _resumeRecovery_decorators = [Remote];
2431
+ _discardRecovery_decorators = [Remote];
2034
2432
  _resolvePeer_decorators = [Remote];
2035
2433
  _createGroup_decorators = [Remote];
2434
+ _getGroup_decorators = [Remote];
2435
+ _joinGroup_decorators = [Remote];
2436
+ _leaveGroup_decorators = [Remote];
2437
+ _listGroupMembers_decorators = [Remote];
2438
+ _addGroupMember_decorators = [Remote];
2439
+ _removeGroupMember_decorators = [Remote];
2440
+ _resumeGroupRebindRecovery_decorators = [Remote];
2036
2441
  _listConversations_decorators = [Remote];
2037
2442
  _getHistory_decorators = [Remote];
2038
2443
  _getLocalHistory_decorators = [Remote];
@@ -2102,6 +2507,17 @@ let AwikiService = (() => {
2102
2507
  },
2103
2508
  metadata: _metadata
2104
2509
  }, null, _instanceExtraInitializers);
2510
+ __esDecorate(this, null, _inspectIdentityAccess_decorators, {
2511
+ kind: "method",
2512
+ name: "inspectIdentityAccess",
2513
+ static: false,
2514
+ private: false,
2515
+ access: {
2516
+ has: (obj) => "inspectIdentityAccess" in obj,
2517
+ get: (obj) => obj.inspectIdentityAccess
2518
+ },
2519
+ metadata: _metadata
2520
+ }, null, _instanceExtraInitializers);
2105
2521
  __esDecorate(this, null, _sendRegistrationOtp_decorators, {
2106
2522
  kind: "method",
2107
2523
  name: "sendRegistrationOtp",
@@ -2135,6 +2551,94 @@ let AwikiService = (() => {
2135
2551
  },
2136
2552
  metadata: _metadata
2137
2553
  }, null, _instanceExtraInitializers);
2554
+ __esDecorate(this, null, _getProfile_decorators, {
2555
+ kind: "method",
2556
+ name: "getProfile",
2557
+ static: false,
2558
+ private: false,
2559
+ access: {
2560
+ has: (obj) => "getProfile" in obj,
2561
+ get: (obj) => obj.getProfile
2562
+ },
2563
+ metadata: _metadata
2564
+ }, null, _instanceExtraInitializers);
2565
+ __esDecorate(this, null, _updateProfile_decorators, {
2566
+ kind: "method",
2567
+ name: "updateProfile",
2568
+ static: false,
2569
+ private: false,
2570
+ access: {
2571
+ has: (obj) => "updateProfile" in obj,
2572
+ get: (obj) => obj.updateProfile
2573
+ },
2574
+ metadata: _metadata
2575
+ }, null, _instanceExtraInitializers);
2576
+ __esDecorate(this, null, _sendRecoveryOtp_decorators, {
2577
+ kind: "method",
2578
+ name: "sendRecoveryOtp",
2579
+ static: false,
2580
+ private: false,
2581
+ access: {
2582
+ has: (obj) => "sendRecoveryOtp" in obj,
2583
+ get: (obj) => obj.sendRecoveryOtp
2584
+ },
2585
+ metadata: _metadata
2586
+ }, null, _instanceExtraInitializers);
2587
+ __esDecorate(this, null, _prepareRecovery_decorators, {
2588
+ kind: "method",
2589
+ name: "prepareRecovery",
2590
+ static: false,
2591
+ private: false,
2592
+ access: {
2593
+ has: (obj) => "prepareRecovery" in obj,
2594
+ get: (obj) => obj.prepareRecovery
2595
+ },
2596
+ metadata: _metadata
2597
+ }, null, _instanceExtraInitializers);
2598
+ __esDecorate(this, null, _activateRecovery_decorators, {
2599
+ kind: "method",
2600
+ name: "activateRecovery",
2601
+ static: false,
2602
+ private: false,
2603
+ access: {
2604
+ has: (obj) => "activateRecovery" in obj,
2605
+ get: (obj) => obj.activateRecovery
2606
+ },
2607
+ metadata: _metadata
2608
+ }, null, _instanceExtraInitializers);
2609
+ __esDecorate(this, null, _getRecoveryStatus_decorators, {
2610
+ kind: "method",
2611
+ name: "getRecoveryStatus",
2612
+ static: false,
2613
+ private: false,
2614
+ access: {
2615
+ has: (obj) => "getRecoveryStatus" in obj,
2616
+ get: (obj) => obj.getRecoveryStatus
2617
+ },
2618
+ metadata: _metadata
2619
+ }, null, _instanceExtraInitializers);
2620
+ __esDecorate(this, null, _resumeRecovery_decorators, {
2621
+ kind: "method",
2622
+ name: "resumeRecovery",
2623
+ static: false,
2624
+ private: false,
2625
+ access: {
2626
+ has: (obj) => "resumeRecovery" in obj,
2627
+ get: (obj) => obj.resumeRecovery
2628
+ },
2629
+ metadata: _metadata
2630
+ }, null, _instanceExtraInitializers);
2631
+ __esDecorate(this, null, _discardRecovery_decorators, {
2632
+ kind: "method",
2633
+ name: "discardRecovery",
2634
+ static: false,
2635
+ private: false,
2636
+ access: {
2637
+ has: (obj) => "discardRecovery" in obj,
2638
+ get: (obj) => obj.discardRecovery
2639
+ },
2640
+ metadata: _metadata
2641
+ }, null, _instanceExtraInitializers);
2138
2642
  __esDecorate(this, null, _resolvePeer_decorators, {
2139
2643
  kind: "method",
2140
2644
  name: "resolvePeer",
@@ -2157,6 +2661,83 @@ let AwikiService = (() => {
2157
2661
  },
2158
2662
  metadata: _metadata
2159
2663
  }, null, _instanceExtraInitializers);
2664
+ __esDecorate(this, null, _getGroup_decorators, {
2665
+ kind: "method",
2666
+ name: "getGroup",
2667
+ static: false,
2668
+ private: false,
2669
+ access: {
2670
+ has: (obj) => "getGroup" in obj,
2671
+ get: (obj) => obj.getGroup
2672
+ },
2673
+ metadata: _metadata
2674
+ }, null, _instanceExtraInitializers);
2675
+ __esDecorate(this, null, _joinGroup_decorators, {
2676
+ kind: "method",
2677
+ name: "joinGroup",
2678
+ static: false,
2679
+ private: false,
2680
+ access: {
2681
+ has: (obj) => "joinGroup" in obj,
2682
+ get: (obj) => obj.joinGroup
2683
+ },
2684
+ metadata: _metadata
2685
+ }, null, _instanceExtraInitializers);
2686
+ __esDecorate(this, null, _leaveGroup_decorators, {
2687
+ kind: "method",
2688
+ name: "leaveGroup",
2689
+ static: false,
2690
+ private: false,
2691
+ access: {
2692
+ has: (obj) => "leaveGroup" in obj,
2693
+ get: (obj) => obj.leaveGroup
2694
+ },
2695
+ metadata: _metadata
2696
+ }, null, _instanceExtraInitializers);
2697
+ __esDecorate(this, null, _listGroupMembers_decorators, {
2698
+ kind: "method",
2699
+ name: "listGroupMembers",
2700
+ static: false,
2701
+ private: false,
2702
+ access: {
2703
+ has: (obj) => "listGroupMembers" in obj,
2704
+ get: (obj) => obj.listGroupMembers
2705
+ },
2706
+ metadata: _metadata
2707
+ }, null, _instanceExtraInitializers);
2708
+ __esDecorate(this, null, _addGroupMember_decorators, {
2709
+ kind: "method",
2710
+ name: "addGroupMember",
2711
+ static: false,
2712
+ private: false,
2713
+ access: {
2714
+ has: (obj) => "addGroupMember" in obj,
2715
+ get: (obj) => obj.addGroupMember
2716
+ },
2717
+ metadata: _metadata
2718
+ }, null, _instanceExtraInitializers);
2719
+ __esDecorate(this, null, _removeGroupMember_decorators, {
2720
+ kind: "method",
2721
+ name: "removeGroupMember",
2722
+ static: false,
2723
+ private: false,
2724
+ access: {
2725
+ has: (obj) => "removeGroupMember" in obj,
2726
+ get: (obj) => obj.removeGroupMember
2727
+ },
2728
+ metadata: _metadata
2729
+ }, null, _instanceExtraInitializers);
2730
+ __esDecorate(this, null, _resumeGroupRebindRecovery_decorators, {
2731
+ kind: "method",
2732
+ name: "resumeGroupRebindRecovery",
2733
+ static: false,
2734
+ private: false,
2735
+ access: {
2736
+ has: (obj) => "resumeGroupRebindRecovery" in obj,
2737
+ get: (obj) => obj.resumeGroupRebindRecovery
2738
+ },
2739
+ metadata: _metadata
2740
+ }, null, _instanceExtraInitializers);
2160
2741
  __esDecorate(this, null, _listConversations_decorators, {
2161
2742
  kind: "method",
2162
2743
  name: "listConversations",
@@ -2323,6 +2904,7 @@ let AwikiService = (() => {
2323
2904
  resolved = __runInitializers(this, _instanceExtraInitializers);
2324
2905
  sessionStore;
2325
2906
  imageAttachmentCache;
2907
+ sentMailStore;
2326
2908
  startupUserServiceDomain;
2327
2909
  settingsProvider;
2328
2910
  provider;
@@ -2347,6 +2929,7 @@ let AwikiService = (() => {
2347
2929
  this.externalHttpAuth = createAwikiExternalHttpAuth(() => this.acquireExternalHttpAuthSession());
2348
2930
  this.sessionStore = new AwikiSessionStore(this.resolved.stateRoot);
2349
2931
  this.imageAttachmentCache = new AwikiImageAttachmentCache(this.resolved.stateRoot, this.resolved.attachmentMaxBytes, this.resolved.imageAttachmentCacheMaxBytes);
2932
+ this.sentMailStore = new AwikiSentMailStore(this.resolved.stateRoot);
2350
2933
  this.startupUserServiceDomain = this.resolved.userServiceDomain;
2351
2934
  ctx.inject(["settings"], (settingsCtx) => {
2352
2935
  const provider = settingsCtx.settings;
@@ -2544,11 +3127,79 @@ let AwikiService = (() => {
2544
3127
  }
2545
3128
  });
2546
3129
  }
2547
- /**
2548
- * Send one Legacy registration verification code.
2549
- * @param request - Handle and phone used for the registration challenge.
2550
- * @returns Public retry timing or a closed failure.
2551
- */
3130
+ /** Classify one configured-domain Handle before selecting the registration or recovery OTP purpose. */
3131
+ async inspectIdentityAccess(request) {
3132
+ const target = identityAccessTarget(request, this.startupUserServiceDomain);
3133
+ if (target === void 0) return {
3134
+ ok: false,
3135
+ error: failure("invalid-request")
3136
+ };
3137
+ const endpoint = new URL(`/.well-known/handle/${encodeURIComponent(target.localPart)}`, this.resolved.userServiceUrl);
3138
+ const abort = new AbortController();
3139
+ const timeout = setTimeout(() => {
3140
+ abort.abort();
3141
+ }, 1e4);
3142
+ let response;
3143
+ try {
3144
+ response = await fetch(endpoint, {
3145
+ method: "GET",
3146
+ headers: {
3147
+ accept: "application/json",
3148
+ "cache-control": "no-store"
3149
+ },
3150
+ cache: "no-store",
3151
+ redirect: "error",
3152
+ signal: abort.signal
3153
+ });
3154
+ } catch {
3155
+ return {
3156
+ ok: false,
3157
+ error: failure("network")
3158
+ };
3159
+ } finally {
3160
+ clearTimeout(timeout);
3161
+ }
3162
+ if (response.status === 404) return {
3163
+ ok: true,
3164
+ value: {
3165
+ status: "available",
3166
+ fullHandle: target.fullHandle
3167
+ }
3168
+ };
3169
+ if (!response.ok) return {
3170
+ ok: false,
3171
+ error: failure("remote")
3172
+ };
3173
+ try {
3174
+ const text = await readBoundedResponseText(response, IDENTITY_ACCESS_RESPONSE_MAX_BYTES);
3175
+ if (text === void 0) return {
3176
+ ok: false,
3177
+ error: failure("remote")
3178
+ };
3179
+ const value = JSON.parse(text);
3180
+ if (typeof value !== "object" || value === null) return {
3181
+ ok: false,
3182
+ error: failure("remote")
3183
+ };
3184
+ const binding = value;
3185
+ if (binding.handle !== target.fullHandle || typeof binding.did !== "string" || !binding.did.startsWith("did:") || typeof binding.status !== "string" || binding.status.length === 0) return {
3186
+ ok: false,
3187
+ error: failure("remote")
3188
+ };
3189
+ return {
3190
+ ok: true,
3191
+ value: {
3192
+ status: "existing",
3193
+ fullHandle: target.fullHandle
3194
+ }
3195
+ };
3196
+ } catch {
3197
+ return {
3198
+ ok: false,
3199
+ error: failure("remote")
3200
+ };
3201
+ }
3202
+ }
2552
3203
  sendRegistrationOtp(request) {
2553
3204
  return this.run((client) => client.sendRegistrationOtp(request));
2554
3205
  }
@@ -2559,18 +3210,7 @@ let AwikiService = (() => {
2559
3210
  */
2560
3211
  async registerIdentity(request) {
2561
3212
  const result = await this.run((client) => client.registerIdentity(request));
2562
- if (result.ok) {
2563
- this.activeIdentityDid = result.value.did;
2564
- this.publishSession({
2565
- status: "active",
2566
- identity: result.value
2567
- });
2568
- const provider = this.provider;
2569
- if (provider !== void 0) {
2570
- await provider.listenerStartup;
2571
- await this.startListener(provider);
2572
- }
2573
- }
3213
+ if (result.ok) await this.activateRegisteredIdentity(result.value);
2574
3214
  return result;
2575
3215
  }
2576
3216
  /**
@@ -2583,6 +3223,92 @@ let AwikiService = (() => {
2583
3223
  if (result.ok) this.activeIdentityDid = result.value.did;
2584
3224
  return result;
2585
3225
  }
3226
+ /** Return the public editable profile for the active identity. */
3227
+ getProfile() {
3228
+ return this.run((client) => client.getProfile());
3229
+ }
3230
+ /** Update Display Name, bio, and tags after applying product limits in the Host. */
3231
+ updateProfile(request) {
3232
+ const normalized = normalizeProfileRequest(request);
3233
+ if (normalized === void 0) return Promise.resolve({
3234
+ ok: false,
3235
+ error: failure("invalid-request")
3236
+ });
3237
+ return this.run((client) => client.updateProfile(normalized));
3238
+ }
3239
+ /** Start durable phone recovery for one existing full Handle. */
3240
+ sendRecoveryOtp(request) {
3241
+ const normalized = normalizeRecoveryOtpRequest(request);
3242
+ if (normalized === void 0) return Promise.resolve({
3243
+ ok: false,
3244
+ error: failure("invalid-request")
3245
+ });
3246
+ return this.run((client) => client.sendRecoveryOtp(normalized), { allowSignedOut: true });
3247
+ }
3248
+ /** Verify a recovery OTP and freeze its exact intent before the remote commit. */
3249
+ prepareRecovery(request) {
3250
+ const normalized = normalizeRecoveryPrepareRequest(request);
3251
+ if (normalized === void 0) return Promise.resolve({
3252
+ ok: false,
3253
+ error: failure("invalid-request")
3254
+ });
3255
+ return this.run((client) => client.prepareRecovery(normalized), { allowSignedOut: true });
3256
+ }
3257
+ /** Attempt one prepared recovery commit; uncertain outcomes remain durable in Core. */
3258
+ async activateRecovery(request) {
3259
+ const normalized = normalizeRecoveryOperation(request);
3260
+ if (normalized === void 0) return {
3261
+ ok: false,
3262
+ error: failure("invalid-request")
3263
+ };
3264
+ const result = await this.run((client) => client.activateRecovery(normalized), { allowSignedOut: true });
3265
+ if (result.ok && !await this.applyRecoveredSession(result.value)) return {
3266
+ ok: false,
3267
+ error: failure("remote")
3268
+ };
3269
+ return result;
3270
+ }
3271
+ /** Read durable recovery state before deciding whether a retry is valid. */
3272
+ async getRecoveryStatus(request) {
3273
+ const normalized = normalizeRecoveryOperation(request);
3274
+ if (normalized === void 0) return {
3275
+ ok: false,
3276
+ error: failure("invalid-request")
3277
+ };
3278
+ const result = await this.run((client) => client.getRecoveryStatus(normalized), { allowSignedOut: true });
3279
+ if (result.ok && !await this.applyRecoveredSession(result.value)) return {
3280
+ ok: false,
3281
+ error: failure("remote")
3282
+ };
3283
+ return result;
3284
+ }
3285
+ /** Resume only the exact Core-owned operation selected by the browser. */
3286
+ async resumeRecovery(request) {
3287
+ const normalized = normalizeRecoveryOperation(request);
3288
+ if (normalized === void 0) return {
3289
+ ok: false,
3290
+ error: failure("invalid-request")
3291
+ };
3292
+ const result = await this.run((client) => client.resumeRecovery(normalized), { allowSignedOut: true });
3293
+ if (result.ok && !await this.applyRecoveredSession(result.value)) return {
3294
+ ok: false,
3295
+ error: failure("remote")
3296
+ };
3297
+ return result;
3298
+ }
3299
+ /** Discard only a pre-attempt operation; Core rejects post-attempt deletion. */
3300
+ async discardRecovery(request) {
3301
+ const normalized = normalizeRecoveryOperation(request);
3302
+ if (normalized === void 0) return {
3303
+ ok: false,
3304
+ error: failure("invalid-request")
3305
+ };
3306
+ const result = await this.run((client) => client.discardRecovery(normalized), { allowSignedOut: true });
3307
+ return result.ok ? {
3308
+ ok: true,
3309
+ value: { completed: true }
3310
+ } : result;
3311
+ }
2586
3312
  /**
2587
3313
  * Resolve one Handle or DID before the browser opens a direct chat.
2588
3314
  * @param request - typed Handle or DID.
@@ -2623,6 +3349,83 @@ let AwikiService = (() => {
2623
3349
  }
2624
3350
  };
2625
3351
  }
3352
+ /** Return one authoritative group snapshot for permission-aware UI. */
3353
+ getGroup(request) {
3354
+ const groupDid = normalizeGroupDid(request?.groupDid);
3355
+ if (groupDid === void 0) return Promise.resolve({
3356
+ ok: false,
3357
+ error: failure("invalid-request")
3358
+ });
3359
+ return this.run((client) => client.getGroup(groupDid));
3360
+ }
3361
+ /** Join one open group and return its authoritative membership state. */
3362
+ joinGroup(request) {
3363
+ const groupDid = normalizeGroupDid(request?.groupDid);
3364
+ if (groupDid === void 0) return Promise.resolve({
3365
+ ok: false,
3366
+ error: failure("invalid-request")
3367
+ });
3368
+ return this.run((client) => client.joinGroup(groupDid));
3369
+ }
3370
+ /** Leave one group. Core rejects owner leave and unsupported security profiles. */
3371
+ async leaveGroup(request) {
3372
+ const groupDid = normalizeGroupDid(request?.groupDid);
3373
+ if (groupDid === void 0) return Promise.resolve({
3374
+ ok: false,
3375
+ error: failure("invalid-request")
3376
+ });
3377
+ const result = await this.run((client) => client.leaveGroup(groupDid));
3378
+ return result.ok ? {
3379
+ ok: true,
3380
+ value: { completed: true }
3381
+ } : result;
3382
+ }
3383
+ /** Read one authoritative versioned member page. */
3384
+ listGroupMembers(request) {
3385
+ const groupDid = normalizeGroupDid(request?.groupDid);
3386
+ if (groupDid === void 0 || request.cursor !== void 0 && (typeof request.cursor !== "string" || request.cursor.length > 4096) || request.limit !== void 0 && (!Number.isSafeInteger(request.limit) || request.limit < 1 || request.limit > 100)) return Promise.resolve({
3387
+ ok: false,
3388
+ error: failure("invalid-request")
3389
+ });
3390
+ return this.run((client) => client.listGroupMembers({
3391
+ groupDid,
3392
+ ...request.cursor === void 0 ? {} : { cursor: request.cursor },
3393
+ ...request.limit === void 0 ? {} : { limit: request.limit }
3394
+ }));
3395
+ }
3396
+ /** Invite one ordinary member after group creation. */
3397
+ addGroupMember(request) {
3398
+ const groupDid = normalizeGroupDid(request?.groupDid);
3399
+ const member = normalizeMember(request?.member);
3400
+ if (groupDid === void 0 || member === void 0 || request.role !== void 0 && request.role !== "member") return Promise.resolve({
3401
+ ok: false,
3402
+ error: failure("invalid-request")
3403
+ });
3404
+ return this.run((client) => client.addGroupMember(groupDid, member));
3405
+ }
3406
+ /** Remove one member. The authoritative Core role check remains decisive. */
3407
+ removeGroupMember(request) {
3408
+ const groupDid = normalizeGroupDid(request?.groupDid);
3409
+ const member = normalizeMember(request?.member);
3410
+ if (groupDid === void 0 || member === void 0) return Promise.resolve({
3411
+ ok: false,
3412
+ error: failure("invalid-request")
3413
+ });
3414
+ return this.run((client) => client.removeGroupMember(groupDid, member));
3415
+ }
3416
+ /** Resume durable group membership convergence after account sync restores old groups. */
3417
+ async resumeGroupRebindRecovery() {
3418
+ const result = await this.run((client) => client.resumeGroupRebindRecovery());
3419
+ if (!result.ok) return result;
3420
+ const summary = publicGroupRebindRecoverySummary(result.value);
3421
+ return summary === void 0 ? {
3422
+ ok: false,
3423
+ error: failure("remote")
3424
+ } : {
3425
+ ok: true,
3426
+ value: summary
3427
+ };
3428
+ }
2626
3429
  /**
2627
3430
  * List direct and existing group conversations.
2628
3431
  * @param request - Optional opaque cursor and page limit.
@@ -2762,7 +3565,12 @@ let AwikiService = (() => {
2762
3565
  * @returns The accepted public message or a closed failure.
2763
3566
  */
2764
3567
  sendText(request) {
2765
- return this.run((client) => client.sendText(request));
3568
+ const normalized = normalizeSendTextRequest(request);
3569
+ if (normalized === void 0) return Promise.resolve({
3570
+ ok: false,
3571
+ error: failure("invalid-request")
3572
+ });
3573
+ return this.run((client) => client.sendText(normalized));
2766
3574
  }
2767
3575
  /**
2768
3576
  * Upload and send one attachment after Host validation.
@@ -2854,20 +3662,72 @@ let AwikiService = (() => {
2854
3662
  return this.run((client) => client.getMailAccount());
2855
3663
  }
2856
3664
  /** List one bounded mailbox page on explicit browser/tool demand. */
2857
- listMailInbox(request) {
2858
- return this.runValidatedMail(() => mailInboxRequest(request ?? {}), (client, normalized) => client.listMailInbox(normalized));
3665
+ async listMailInbox(request) {
3666
+ let normalized;
3667
+ try {
3668
+ normalized = mailInboxRequest(request ?? {});
3669
+ } catch {
3670
+ return {
3671
+ ok: false,
3672
+ error: failure("invalid-request")
3673
+ };
3674
+ }
3675
+ return this.run(async (client) => {
3676
+ if (normalized.folder !== "sent") return client.listMailInbox(normalized);
3677
+ return this.sentMailStore.list(await this.ownerDid(client), normalized);
3678
+ });
2859
3679
  }
2860
3680
  /** Read one bounded plain-text mail message. */
2861
- readMail(request) {
2862
- return this.runValidatedMail(() => mailReadRequest(request), (client, normalized) => client.readMail(normalized));
3681
+ async readMail(request) {
3682
+ let normalized;
3683
+ try {
3684
+ normalized = mailReadRequest(request);
3685
+ } catch {
3686
+ return {
3687
+ ok: false,
3688
+ error: failure("invalid-request")
3689
+ };
3690
+ }
3691
+ return this.run(async (client) => {
3692
+ if (!isLocalSentMailId(normalized.messageId)) return client.readMail(normalized);
3693
+ const local = await this.sentMailStore.read(await this.ownerDid(client), normalized.messageId);
3694
+ if (local === void 0) throw Object.assign(/* @__PURE__ */ new Error("sent mail not found"), {
3695
+ name: "AwikiSdkError",
3696
+ code: "not-found"
3697
+ });
3698
+ return local;
3699
+ });
2863
3700
  }
2864
3701
  /** Mark explicitly selected mail messages read. Browser callers require an explicit click. */
2865
3702
  markMailRead(request) {
2866
3703
  return this.runValidatedMail(() => mailMarkReadRequest(request), (client, normalized) => client.markMailRead(normalized));
2867
3704
  }
2868
3705
  /** Send one plain-text mail once. Browser callers require an explicit confirmation. */
2869
- sendMail(request) {
2870
- return this.runValidatedMail(() => mailSendRequest(request), (client, normalized) => client.sendMail(normalized));
3706
+ async sendMail(request) {
3707
+ let normalized;
3708
+ try {
3709
+ normalized = mailSendRequest(request);
3710
+ } catch {
3711
+ return {
3712
+ ok: false,
3713
+ error: failure("invalid-request")
3714
+ };
3715
+ }
3716
+ return this.run(async (client) => {
3717
+ const result = await client.sendMail(normalized);
3718
+ if (!result.accepted) return result;
3719
+ try {
3720
+ const ownerDid = await this.ownerDid(client);
3721
+ const account = await client.getMailAccount().catch(() => void 0);
3722
+ await this.sentMailStore.append(ownerDid, normalized, result, account);
3723
+ return result;
3724
+ } catch {
3725
+ return result.warnings.length >= 100 ? result : {
3726
+ ...result,
3727
+ warnings: [...result.warnings, "Sent history could not be saved locally."]
3728
+ };
3729
+ }
3730
+ });
2871
3731
  }
2872
3732
  /**
2873
3733
  * Permanently remove the exact SDK-owned local state after an explicit browser acknowledgement.
@@ -2890,6 +3750,7 @@ let AwikiService = (() => {
2890
3750
  }
2891
3751
  try {
2892
3752
  await this.imageAttachmentCache.clear();
3753
+ await this.sentMailStore.clear();
2893
3754
  await this.sessionStore.signIn();
2894
3755
  this.signedOut = false;
2895
3756
  this.activeIdentityDid = void 0;
@@ -2905,6 +3766,46 @@ let AwikiService = (() => {
2905
3766
  }
2906
3767
  });
2907
3768
  }
3769
+ /** Re-enter only after Core confirms that the exact recovered identity is applied locally. */
3770
+ async applyRecoveredSession(progress) {
3771
+ if (progress.phase !== "applied") return true;
3772
+ return this.mutateSession(async () => {
3773
+ const provider = this.provider;
3774
+ if (provider === void 0) return false;
3775
+ try {
3776
+ const identity = await provider.client.getIdentity();
3777
+ if (identity === null || identity.did !== progress.currentDid) return false;
3778
+ if (this.signedOut === false && this.activeIdentityDid === identity.did) return true;
3779
+ await this.sessionStore.signIn();
3780
+ this.signedOut = false;
3781
+ this.activeIdentityDid = identity.did;
3782
+ this.invalidateSummaries();
3783
+ const session = {
3784
+ status: "active",
3785
+ identity
3786
+ };
3787
+ this.publishSession(session);
3788
+ await provider.listenerStartup;
3789
+ await this.startListener(provider);
3790
+ return true;
3791
+ } catch {
3792
+ return false;
3793
+ }
3794
+ });
3795
+ }
3796
+ /** Publish one newly registered identity and start its listener through the existing session path. */
3797
+ async activateRegisteredIdentity(identity) {
3798
+ this.activeIdentityDid = identity.did;
3799
+ this.publishSession({
3800
+ status: "active",
3801
+ identity
3802
+ });
3803
+ const provider = this.provider;
3804
+ if (provider !== void 0) {
3805
+ await provider.listenerStartup;
3806
+ await this.startListener(provider);
3807
+ }
3808
+ }
2908
3809
  /** Invalidate cached session work and cancel every model request still owned by the old session. */
2909
3810
  invalidateSummaries() {
2910
3811
  this.sessionRevision += 1;
@@ -2915,6 +3816,17 @@ let AwikiService = (() => {
2915
3816
  publishSession(session) {
2916
3817
  this.hostContext.emit("awiki/session", session);
2917
3818
  }
3819
+ /** Resolve and cache the owner binding required by private Host-side projections. */
3820
+ async ownerDid(client) {
3821
+ const identity = this.activeIdentityDid === void 0 ? await client.getIdentity() : void 0;
3822
+ const ownerDid = this.activeIdentityDid ?? identity?.did;
3823
+ if (ownerDid === void 0) throw Object.assign(/* @__PURE__ */ new Error("not registered"), {
3824
+ name: "AwikiSdkError",
3825
+ code: "not-registered"
3826
+ });
3827
+ this.activeIdentityDid = ownerDid;
3828
+ return ownerDid;
3829
+ }
2918
3830
  /** Invoke the current client and normalize every rejection to a public result. */
2919
3831
  async run(operation, options = {}) {
2920
3832
  try {