@senguoyun/dsh-arkme 0.1.29 → 0.1.30
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/lib/client.js +3525 -477
- package/lib/client.js.map +1 -1
- package/lib/index.js +235 -7
- package/lib/persistent-extension.js +1 -1
- package/lib/{persistent-runtime-DLKV_LTp.js → persistent-runtime-D-NwFt9z.js} +867 -21
- package/lib/sdk.js +44 -0
- package/lib/types/arkme-service.d.ts +20 -1
- package/lib/types/arkme-service.d.ts.map +1 -1
- package/lib/types/client/ArkmeNoteDetails.d.ts +3 -1
- package/lib/types/client/ArkmeNoteDetails.d.ts.map +1 -1
- package/lib/types/client/ArkmePersistentShell.d.ts +3 -0
- package/lib/types/client/ArkmePersistentShell.d.ts.map +1 -1
- package/lib/types/client/ArkmeProductNavigation.d.ts +2 -1
- package/lib/types/client/ArkmeProductNavigation.d.ts.map +1 -1
- package/lib/types/client/ArkmeRichContent.d.ts +15 -1
- package/lib/types/client/ArkmeRichContent.d.ts.map +1 -1
- package/lib/types/client/ArkmeSidebar.d.ts +14 -0
- package/lib/types/client/ArkmeSidebar.d.ts.map +1 -1
- package/lib/types/client/ArkmeVirtualWorkspace.d.ts +9 -2
- package/lib/types/client/ArkmeVirtualWorkspace.d.ts.map +1 -1
- package/lib/types/client/ArkmeWebLoginOverlay.d.ts +6 -0
- package/lib/types/client/ArkmeWebLoginOverlay.d.ts.map +1 -0
- package/lib/types/client/DeepSeekHarnessSurface.d.ts +7 -4
- package/lib/types/client/DeepSeekHarnessSurface.d.ts.map +1 -1
- package/lib/types/client/api.d.ts +1 -1
- package/lib/types/client/api.d.ts.map +1 -1
- package/lib/types/client/arkme-auth-flow.d.ts +4 -0
- package/lib/types/client/arkme-auth-flow.d.ts.map +1 -1
- package/lib/types/client/index.d.ts +1 -0
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/ui-controller.d.ts +4 -0
- package/lib/types/client/ui-controller.d.ts.map +1 -1
- package/lib/types/extensions/manager.d.ts +4 -0
- package/lib/types/extensions/manager.d.ts.map +1 -1
- package/lib/types/extensions/persistent-bundle.d.ts +14 -0
- package/lib/types/extensions/persistent-bundle.d.ts.map +1 -1
- package/lib/types/extensions/persistent-client-bundle.d.ts +1 -1
- package/lib/types/extensions/persistent-client-bundle.d.ts.map +1 -1
- package/lib/types/extensions/persistent-runtime.d.ts.map +1 -1
- package/lib/types/extensions/profile-installer.d.ts.map +1 -1
- package/lib/types/harness-embed-contract.d.ts +2 -0
- package/lib/types/harness-embed-contract.d.ts.map +1 -0
- package/lib/types/harness-embed-route.d.ts +32 -0
- package/lib/types/harness-embed-route.d.ts.map +1 -0
- package/lib/types/host-api.d.ts.map +1 -1
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/sdk/index.d.ts +17 -2
- package/lib/types/sdk/index.d.ts.map +1 -1
- package/lib/types/services/chat-service.d.ts +37 -4
- package/lib/types/services/chat-service.d.ts.map +1 -1
- package/lib/types/tools/business/conversation/copy-link-extend.d.ts +2 -0
- package/lib/types/tools/business/conversation/copy-link-extend.d.ts.map +1 -0
- package/lib/types/tools/business/index.d.ts.map +1 -1
- package/lib/types/tools/ports/conversations.d.ts +4 -1
- package/lib/types/tools/ports/conversations.d.ts.map +1 -1
- package/lib/types/tools/prompts/business.d.ts.map +1 -1
- package/lib/types/types.d.ts +82 -1
- package/lib/types/types.d.ts.map +1 -1
- package/package.json +5 -5
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { chmodSync, createReadStream, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { createCipheriv, createDecipheriv, createHash, createHmac, createPublicKey, randomBytes, randomUUID, timingSafeEqual, verify } from "node:crypto";
|
|
3
|
-
import { dirname, isAbsolute, join, posix, relative, resolve, sep } from "node:path";
|
|
3
|
+
import { basename, dirname, isAbsolute, join, posix, relative, resolve, sep } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { open } from "node:fs/promises";
|
|
6
6
|
import { pinyin } from "pinyin-pro";
|
|
@@ -7077,6 +7077,18 @@ const ARKME_WORLD_PUBLISH_MAX_IMAGE_BYTES = 20 * 1024 * 1024;
|
|
|
7077
7077
|
//#region src/services/chat-service.ts
|
|
7078
7078
|
const OFFICIAL_AUTHOR_USER_ID = 11;
|
|
7079
7079
|
const OFFICIAL_AUTHOR_FALLBACK_DISPLAY_NAME = "即我作者";
|
|
7080
|
+
const MAX_MESSAGE_COPY_LINK_ITEMS = 100;
|
|
7081
|
+
const LINK_METADATA_MAX_BYTES = 128 * 1024;
|
|
7082
|
+
const LINK_METADATA_MAX_REDIRECTS = 3;
|
|
7083
|
+
const RECORD_UID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
7084
|
+
const HTML_ENTITY_MAP = {
|
|
7085
|
+
amp: "&",
|
|
7086
|
+
lt: "<",
|
|
7087
|
+
gt: ">",
|
|
7088
|
+
quot: "\"",
|
|
7089
|
+
apos: "'",
|
|
7090
|
+
nbsp: " "
|
|
7091
|
+
};
|
|
7080
7092
|
function numberValue$16(value) {
|
|
7081
7093
|
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
7082
7094
|
}
|
|
@@ -7101,6 +7113,88 @@ function favoriteStickerPersistenceItem(item) {
|
|
|
7101
7113
|
function listValue$16(value) {
|
|
7102
7114
|
return Array.isArray(value) ? value : [];
|
|
7103
7115
|
}
|
|
7116
|
+
function htmlTextValue(value) {
|
|
7117
|
+
return value.replace(/<script[\s\S]*?<\/script>/giu, " ").replace(/<style[\s\S]*?<\/style>/giu, " ").replace(/<[^>]+>/gu, " ").replace(/&(#x[0-9a-f]+|#[0-9]+|[a-z]+);/giu, (_match, entity) => {
|
|
7118
|
+
const normalized = entity.toLowerCase();
|
|
7119
|
+
if (normalized.startsWith("#x")) {
|
|
7120
|
+
const code = Number.parseInt(normalized.slice(2), 16);
|
|
7121
|
+
return Number.isFinite(code) ? String.fromCodePoint(code) : " ";
|
|
7122
|
+
}
|
|
7123
|
+
if (normalized.startsWith("#")) {
|
|
7124
|
+
const code = Number.parseInt(normalized.slice(1), 10);
|
|
7125
|
+
return Number.isFinite(code) ? String.fromCodePoint(code) : " ";
|
|
7126
|
+
}
|
|
7127
|
+
return HTML_ENTITY_MAP[normalized] ?? " ";
|
|
7128
|
+
}).replace(/\s+/gu, " ").trim();
|
|
7129
|
+
}
|
|
7130
|
+
function firstHtmlMetaContent(html, key) {
|
|
7131
|
+
const escaped = key.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
7132
|
+
const patterns = [new RegExp(`<meta\\b(?=[^>]*(?:property|name)=["']${escaped}["'])(?=[^>]*content=["']([^"']*)["'])[^>]*>`, "iu"), new RegExp(`<meta\\b(?=[^>]*content=["']([^"']*)["'])(?=[^>]*(?:property|name)=["']${escaped}["'])[^>]*>`, "iu")];
|
|
7133
|
+
for (const pattern of patterns) {
|
|
7134
|
+
const value = pattern.exec(html)?.[1];
|
|
7135
|
+
if (value !== void 0) return htmlTextValue(value);
|
|
7136
|
+
}
|
|
7137
|
+
return "";
|
|
7138
|
+
}
|
|
7139
|
+
function fallbackLinkTitle(url) {
|
|
7140
|
+
const host = url.hostname.replace(/^www\./iu, "");
|
|
7141
|
+
if (host === "github.com") {
|
|
7142
|
+
const parts = url.pathname.split("/").filter(Boolean);
|
|
7143
|
+
if (parts.length >= 4 && parts[2] === "pull") return `Pull Request #${parts[3]} · ${parts[0]}/${parts[1]}`;
|
|
7144
|
+
if (parts.length >= 2) return `${parts[0]}/${parts[1]}`;
|
|
7145
|
+
}
|
|
7146
|
+
if (host === "codeup.aliyun.com" || host.endsWith(".codeup.aliyun.com")) {
|
|
7147
|
+
const parts = url.pathname.split("/").filter(Boolean);
|
|
7148
|
+
const changeIndex = parts.indexOf("change");
|
|
7149
|
+
const repository = changeIndex >= 2 ? parts[changeIndex - 1] : parts.at(-1);
|
|
7150
|
+
const changeNo = changeIndex >= 0 ? parts[changeIndex + 1] : void 0;
|
|
7151
|
+
if (repository !== void 0 && changeNo !== void 0) return `${repository} · Change #${changeNo}`;
|
|
7152
|
+
}
|
|
7153
|
+
return "分享链接";
|
|
7154
|
+
}
|
|
7155
|
+
function ensureMetadataUrl(rawUrl) {
|
|
7156
|
+
let url;
|
|
7157
|
+
try {
|
|
7158
|
+
url = new URL(rawUrl.trim());
|
|
7159
|
+
} catch {
|
|
7160
|
+
throw new ArkmePluginError("link-metadata-url-invalid", "链接暂不可预览", false, 400);
|
|
7161
|
+
}
|
|
7162
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") throw new ArkmePluginError("link-metadata-url-invalid", "链接暂不可预览", false, 400);
|
|
7163
|
+
if (url.username !== "" || url.password !== "") throw new ArkmePluginError("link-metadata-url-forbidden", "链接暂不可预览", false, 403);
|
|
7164
|
+
const hostname = url.hostname.toLowerCase();
|
|
7165
|
+
if (hostname === "localhost" || hostname.endsWith(".local")) throw new ArkmePluginError("link-metadata-url-forbidden", "链接暂不可预览", false, 403);
|
|
7166
|
+
if (/^(?:127|10|0|169\.254|192\.168)\./u.test(hostname) || /^172\.(?:1[6-9]|2\d|3[0-1])\./u.test(hostname)) throw new ArkmePluginError("link-metadata-url-forbidden", "链接暂不可预览", false, 403);
|
|
7167
|
+
if (hostname === "::1" || hostname.startsWith("fc") || hostname.startsWith("fd") || hostname.startsWith("fe80")) throw new ArkmePluginError("link-metadata-url-forbidden", "链接暂不可预览", false, 403);
|
|
7168
|
+
return url;
|
|
7169
|
+
}
|
|
7170
|
+
async function readResponseTextPrefix(response) {
|
|
7171
|
+
if (response.body === null) return await response.text();
|
|
7172
|
+
const reader = response.body.getReader();
|
|
7173
|
+
const chunks = [];
|
|
7174
|
+
let bytes = 0;
|
|
7175
|
+
try {
|
|
7176
|
+
while (bytes < LINK_METADATA_MAX_BYTES) {
|
|
7177
|
+
const next = await reader.read();
|
|
7178
|
+
if (next.done) break;
|
|
7179
|
+
chunks.push(next.value);
|
|
7180
|
+
bytes += next.value.byteLength;
|
|
7181
|
+
}
|
|
7182
|
+
} finally {
|
|
7183
|
+
reader.releaseLock();
|
|
7184
|
+
}
|
|
7185
|
+
return new TextDecoder("utf-8", { fatal: false }).decode(Uint8Array.from(chunks.flatMap((chunk) => [...chunk])));
|
|
7186
|
+
}
|
|
7187
|
+
function linkMetadataFromHtml(url, html) {
|
|
7188
|
+
const title = firstHtmlMetaContent(html, "og:title") || firstHtmlMetaContent(html, "twitter:title") || htmlTextValue(/<title\b[^>]*>([\s\S]*?)<\/title>/iu.exec(html)?.[1] ?? "") || fallbackLinkTitle(url);
|
|
7189
|
+
const description = firstHtmlMetaContent(html, "og:description") || firstHtmlMetaContent(html, "description");
|
|
7190
|
+
const siteName = firstHtmlMetaContent(html, "og:site_name") || url.hostname.replace(/^www\./iu, "");
|
|
7191
|
+
return {
|
|
7192
|
+
url: url.toString(),
|
|
7193
|
+
title,
|
|
7194
|
+
...description === "" ? {} : { description },
|
|
7195
|
+
...siteName === "" ? {} : { siteName }
|
|
7196
|
+
};
|
|
7197
|
+
}
|
|
7104
7198
|
function chatMemberRole$1(value) {
|
|
7105
7199
|
if (value === "owner" || value === 1) return "owner";
|
|
7106
7200
|
if (value === "admin" || value === 2) return "admin";
|
|
@@ -7121,6 +7215,185 @@ function integerLikeValue$1(value) {
|
|
|
7121
7215
|
}
|
|
7122
7216
|
return 0;
|
|
7123
7217
|
}
|
|
7218
|
+
function epochMillisValue(value) {
|
|
7219
|
+
const timestamp = integerLikeValue$1(value);
|
|
7220
|
+
return timestamp > 0 && timestamp < 1e11 ? timestamp * 1e3 : timestamp;
|
|
7221
|
+
}
|
|
7222
|
+
function messageCopyLinkMediaItemFromData(value) {
|
|
7223
|
+
const data = objectValue$1(value);
|
|
7224
|
+
return {
|
|
7225
|
+
fileKind: integerLikeValue$1(data.file_kind),
|
|
7226
|
+
fileName: stringValue$1(data.file_name),
|
|
7227
|
+
size: Math.max(0, integerLikeValue$1(data.size))
|
|
7228
|
+
};
|
|
7229
|
+
}
|
|
7230
|
+
function messageCopyLinkStructuredContentFromData(value) {
|
|
7231
|
+
const data = objectValue$1(value);
|
|
7232
|
+
if (Object.keys(data).length === 0) return void 0;
|
|
7233
|
+
return {
|
|
7234
|
+
structuredKind: integerLikeValue$1(data.structured_kind),
|
|
7235
|
+
durationMillis: Math.max(0, integerLikeValue$1(data.duration_millis))
|
|
7236
|
+
};
|
|
7237
|
+
}
|
|
7238
|
+
function messageCopyLinkSnapshotItemFromData(value) {
|
|
7239
|
+
const data = objectValue$1(value);
|
|
7240
|
+
const structuredContent = messageCopyLinkStructuredContentFromData(data.structured_content);
|
|
7241
|
+
const recordUid = stringValue$1(data.record_uid ?? data.recordUid ?? data.uid).trim();
|
|
7242
|
+
const item = {
|
|
7243
|
+
...recordUid === "" ? {} : { recordUid },
|
|
7244
|
+
sourceKind: stringValue$1(data.source_kind),
|
|
7245
|
+
senderDisplayName: stringValue$1(data.sender_display_name),
|
|
7246
|
+
senderAvatarUrl: stringValue$1(data.sender_avatar_url),
|
|
7247
|
+
title: stringValue$1(data.title),
|
|
7248
|
+
textContent: stringValue$1(data.text_content),
|
|
7249
|
+
sendAtMillis: epochMillisValue(data.send_at),
|
|
7250
|
+
templateKind: integerLikeValue$1(data.template_kind),
|
|
7251
|
+
displayKind: integerLikeValue$1(data.display_kind),
|
|
7252
|
+
officialMark: integerLikeValue$1(data.official_mark),
|
|
7253
|
+
mediaItems: listValue$16(data.media_items).map(messageCopyLinkMediaItemFromData),
|
|
7254
|
+
...structuredContent === void 0 ? {} : { structuredContent }
|
|
7255
|
+
};
|
|
7256
|
+
if (item.sendAtMillis <= 0 || item.senderDisplayName.trim() === "" && item.sourceKind !== "agent_message") throw new ArkmePluginError("message-copy-link-unavailable", "链接暂不可用", false, 404);
|
|
7257
|
+
if (item.title.trim() === "" && item.textContent.trim() === "" && item.mediaItems.length === 0 && item.structuredContent === void 0) throw new ArkmePluginError("message-copy-link-unavailable", "链接暂不可用", false, 404);
|
|
7258
|
+
return item;
|
|
7259
|
+
}
|
|
7260
|
+
function messageCopyLinkPresentationNodesFromData(value, itemCount, depth = 1, seenItemIndexes = /* @__PURE__ */ new Set()) {
|
|
7261
|
+
const rawNodes = listValue$16(value);
|
|
7262
|
+
if (rawNodes.length === 0) return [];
|
|
7263
|
+
if (depth > 8) throw new ArkmePluginError("message-copy-link-unavailable", "链接暂不可用", false, 404);
|
|
7264
|
+
return rawNodes.map((rawNode) => {
|
|
7265
|
+
const data = objectValue$1(rawNode);
|
|
7266
|
+
const kind = stringValue$1(data.kind);
|
|
7267
|
+
if (kind === "item") {
|
|
7268
|
+
const itemIndex = integerLikeValue$1(data.item_index);
|
|
7269
|
+
if (itemIndex < 0 || itemIndex >= itemCount || seenItemIndexes.has(itemIndex)) throw new ArkmePluginError("message-copy-link-unavailable", "链接暂不可用", false, 404);
|
|
7270
|
+
seenItemIndexes.add(itemIndex);
|
|
7271
|
+
return {
|
|
7272
|
+
kind: "item",
|
|
7273
|
+
itemIndex
|
|
7274
|
+
};
|
|
7275
|
+
}
|
|
7276
|
+
if (kind === "forward_bundle") return {
|
|
7277
|
+
kind: "forward_bundle",
|
|
7278
|
+
title: stringValue$1(data.title),
|
|
7279
|
+
commentText: stringValue$1(data.comment_text),
|
|
7280
|
+
createdAtMillis: integerLikeValue$1(data.created_at),
|
|
7281
|
+
senderDisplayName: stringValue$1(data.sender_display_name),
|
|
7282
|
+
children: messageCopyLinkPresentationNodesFromData(data.children, itemCount, depth + 1, seenItemIndexes)
|
|
7283
|
+
};
|
|
7284
|
+
throw new ArkmePluginError("message-copy-link-unavailable", "链接暂不可用", false, 404);
|
|
7285
|
+
});
|
|
7286
|
+
}
|
|
7287
|
+
function messageCopyLinkSourceAnchorFromData(value) {
|
|
7288
|
+
const data = objectValue$1(value);
|
|
7289
|
+
return {
|
|
7290
|
+
relationUid: stringValue$1(data.rel_uid),
|
|
7291
|
+
recordUid: stringValue$1(data.record_uid),
|
|
7292
|
+
recordOwnerUserId: integerLikeValue$1(data.record_owner_user_id),
|
|
7293
|
+
sequence: integerLikeValue$1(data.seq)
|
|
7294
|
+
};
|
|
7295
|
+
}
|
|
7296
|
+
function firstTextValue(source, keys) {
|
|
7297
|
+
for (const key of keys) {
|
|
7298
|
+
const value = stringValue$1(source[key]).trim();
|
|
7299
|
+
if (value !== "") return value;
|
|
7300
|
+
}
|
|
7301
|
+
return "";
|
|
7302
|
+
}
|
|
7303
|
+
function messageCopyLinkExtensionItemFromData(value) {
|
|
7304
|
+
const data = objectValue$1(value);
|
|
7305
|
+
const core = objectValue$1(data.record_core ?? data.recordCore);
|
|
7306
|
+
const recordUid = firstTextValue(data, [
|
|
7307
|
+
"record_uid",
|
|
7308
|
+
"recordUid",
|
|
7309
|
+
"uid"
|
|
7310
|
+
]) || firstTextValue(core, [
|
|
7311
|
+
"record_uid",
|
|
7312
|
+
"recordUid",
|
|
7313
|
+
"uid"
|
|
7314
|
+
]);
|
|
7315
|
+
const senderDisplayName = firstTextValue(data, [
|
|
7316
|
+
"sender_display_name",
|
|
7317
|
+
"senderDisplayName",
|
|
7318
|
+
"nickname",
|
|
7319
|
+
"nick_name",
|
|
7320
|
+
"nickName",
|
|
7321
|
+
"owner_name",
|
|
7322
|
+
"ownerName"
|
|
7323
|
+
]) || firstTextValue(core, [
|
|
7324
|
+
"sender_display_name",
|
|
7325
|
+
"senderDisplayName",
|
|
7326
|
+
"nickname",
|
|
7327
|
+
"nick_name",
|
|
7328
|
+
"nickName",
|
|
7329
|
+
"owner_name",
|
|
7330
|
+
"ownerName"
|
|
7331
|
+
]) || "Arkme用户";
|
|
7332
|
+
const sendAtMillis = epochMillisValue(data.send_at ?? data.sendAt ?? data.created_at ?? data.createdAt ?? data.published_at ?? data.publishedAt ?? core.send_at ?? core.sendAt ?? core.created_at ?? core.createdAt ?? core.published_at ?? core.publishedAt);
|
|
7333
|
+
const title = stringValue$1(data.title ?? core.title);
|
|
7334
|
+
const textContent = stringValue$1(data.text_content ?? data.textContent ?? data.content ?? core.text_content ?? core.textContent ?? core.content);
|
|
7335
|
+
const mediaItems = listValue$16(data.media_items ?? data.mediaItems ?? core.media_items ?? core.mediaItems).map(messageCopyLinkMediaItemFromData);
|
|
7336
|
+
const structuredContent = messageCopyLinkStructuredContentFromData(data.structured_content ?? data.structuredContent ?? core.structured_content ?? core.structuredContent);
|
|
7337
|
+
if (recordUid === "" || sendAtMillis <= 0 || title.trim() === "" && textContent.trim() === "" && mediaItems.length === 0 && structuredContent === void 0) return;
|
|
7338
|
+
return {
|
|
7339
|
+
recordUid,
|
|
7340
|
+
level: Math.max(2, integerLikeValue$1(data.level) || 2),
|
|
7341
|
+
sourceKind: stringValue$1(data.source_kind ?? data.sourceKind) || "record_extension",
|
|
7342
|
+
senderDisplayName,
|
|
7343
|
+
senderAvatarUrl: firstTextValue(data, [
|
|
7344
|
+
"sender_avatar_url",
|
|
7345
|
+
"senderAvatarUrl",
|
|
7346
|
+
"avatar_ref",
|
|
7347
|
+
"avatarRef",
|
|
7348
|
+
"avatar_url",
|
|
7349
|
+
"avatarUrl",
|
|
7350
|
+
"avatar"
|
|
7351
|
+
]),
|
|
7352
|
+
title,
|
|
7353
|
+
textContent,
|
|
7354
|
+
sendAtMillis,
|
|
7355
|
+
templateKind: integerLikeValue$1(data.template_kind ?? data.templateKind ?? core.template_kind ?? core.templateKind) || 1,
|
|
7356
|
+
displayKind: integerLikeValue$1(data.display_kind ?? data.displayKind ?? core.display_kind ?? core.displayKind),
|
|
7357
|
+
officialMark: integerLikeValue$1(data.official_mark ?? data.officialMark ?? core.official_mark ?? core.officialMark),
|
|
7358
|
+
mediaItems,
|
|
7359
|
+
...structuredContent === void 0 ? {} : { structuredContent }
|
|
7360
|
+
};
|
|
7361
|
+
}
|
|
7362
|
+
function messageCopyLinkRecordContextFromData(data) {
|
|
7363
|
+
const context = objectValue$1(data.record_context ?? data.recordContext);
|
|
7364
|
+
const extensions = listValue$16(context.extensions ?? context.extend_records ?? context.extendRecords ?? context.list ?? data.extensions ?? data.extend_records ?? data.extendRecords ?? data.list).map(messageCopyLinkExtensionItemFromData).filter((item) => item !== void 0);
|
|
7365
|
+
const extensionCount = Math.max(integerLikeValue$1(context.extension_count ?? context.extensionCount ?? context.total ?? data.extension_count ?? data.extensionCount ?? data.total), extensions.length);
|
|
7366
|
+
if (extensionCount <= 0 && extensions.length === 0) return void 0;
|
|
7367
|
+
extensions.sort((left, right) => right.sendAtMillis - left.sendAtMillis);
|
|
7368
|
+
return {
|
|
7369
|
+
extensionCount,
|
|
7370
|
+
extensions
|
|
7371
|
+
};
|
|
7372
|
+
}
|
|
7373
|
+
function mergeMessageCopyLinkRecordContexts(left, right) {
|
|
7374
|
+
if (left === void 0) return right;
|
|
7375
|
+
if (right === void 0) return left;
|
|
7376
|
+
const byUid = /* @__PURE__ */ new Map();
|
|
7377
|
+
for (const item of [...left.extensions, ...right.extensions]) byUid.set(item.recordUid, item);
|
|
7378
|
+
const extensions = [...byUid.values()].sort((a, b) => b.sendAtMillis - a.sendAtMillis);
|
|
7379
|
+
return {
|
|
7380
|
+
extensionCount: Math.max(left.extensionCount, right.extensionCount, extensions.length),
|
|
7381
|
+
extensions
|
|
7382
|
+
};
|
|
7383
|
+
}
|
|
7384
|
+
function messageCopyLinkWorldTags(text) {
|
|
7385
|
+
return [...text.matchAll(/#(\S+)/gu)].map((match) => match[1]?.trim() ?? "").filter((tag) => tag !== "");
|
|
7386
|
+
}
|
|
7387
|
+
function messageCopyLinkRecordUidCandidates(items, anchors) {
|
|
7388
|
+
const candidates = [];
|
|
7389
|
+
const push = (value) => {
|
|
7390
|
+
const normalized = value?.trim() ?? "";
|
|
7391
|
+
if (normalized !== "" && !candidates.includes(normalized)) candidates.push(normalized);
|
|
7392
|
+
};
|
|
7393
|
+
for (const item of items) push(item.recordUid);
|
|
7394
|
+
for (const anchor of anchors) push(anchor.recordUid);
|
|
7395
|
+
return candidates;
|
|
7396
|
+
}
|
|
7124
7397
|
function optionalString$2(value) {
|
|
7125
7398
|
const text = stringValue$1(value).trim();
|
|
7126
7399
|
return text === "" ? void 0 : text;
|
|
@@ -7479,6 +7752,8 @@ var ChatService = class {
|
|
|
7479
7752
|
}
|
|
7480
7753
|
async openOfficialAuthorPrivateChat(options = {}) {
|
|
7481
7754
|
const session = await this.runtime.requireSession();
|
|
7755
|
+
const existing = await this.findPrivateChatByPeerUserId(OFFICIAL_AUTHOR_USER_ID, session, options.signal);
|
|
7756
|
+
if (existing !== void 0) return { source: existing };
|
|
7482
7757
|
const created = await this.runtime.authenticatedSubjectPost("/api/v1/private/create-chat-ref-asen", {
|
|
7483
7758
|
subject_uid: `dsh_official_author_${randomUUID().replace(/-/g, "")}`,
|
|
7484
7759
|
network: "dsh",
|
|
@@ -7500,6 +7775,25 @@ var ChatService = class {
|
|
|
7500
7775
|
...options.signal === void 0 ? {} : { signal: options.signal }
|
|
7501
7776
|
});
|
|
7502
7777
|
}
|
|
7778
|
+
/**
|
|
7779
|
+
* Contact-author is an entry point, not a second conversation type. Scan the
|
|
7780
|
+
* ordinary directory first so repeated clicks always reuse the existing chat.
|
|
7781
|
+
*/
|
|
7782
|
+
async findPrivateChatByPeerUserId(peerUserId, session, signal) {
|
|
7783
|
+
let cursor;
|
|
7784
|
+
for (let pageIndex = 0; pageIndex < 10; pageIndex += 1) {
|
|
7785
|
+
const page = await this.source.listSources("root", {
|
|
7786
|
+
limit: 50,
|
|
7787
|
+
refresh: true,
|
|
7788
|
+
...cursor === void 0 ? {} : { cursor },
|
|
7789
|
+
...signal === void 0 ? {} : { signal }
|
|
7790
|
+
});
|
|
7791
|
+
const existing = page.items.find((source) => source.kind === "private_chat" && source.peerUserId === peerUserId);
|
|
7792
|
+
if (existing !== void 0) return existing;
|
|
7793
|
+
if (!page.hasMore || page.nextCursor === void 0) return void 0;
|
|
7794
|
+
cursor = page.nextCursor;
|
|
7795
|
+
}
|
|
7796
|
+
}
|
|
7503
7797
|
async officialAuthorProfile(options = {}) {
|
|
7504
7798
|
const session = await this.runtime.requireSession();
|
|
7505
7799
|
const profile = (await this.profile.publicProfileSummariesByUserIds([OFFICIAL_AUTHOR_USER_ID], session, options.signal)).get(OFFICIAL_AUTHOR_USER_ID);
|
|
@@ -7562,9 +7856,10 @@ var ChatService = class {
|
|
|
7562
7856
|
sendAtMillis: options.cursor.sendAtMillis,
|
|
7563
7857
|
recordUid: options.cursor.itemUid
|
|
7564
7858
|
} : void 0);
|
|
7859
|
+
const signingKey = await this.runtime.stateStore.uniqueCode();
|
|
7565
7860
|
return {
|
|
7566
7861
|
source: await this.source.sourceItem(source),
|
|
7567
|
-
items: page.items.map((item) => this.record.recordTimelineItem(item)),
|
|
7862
|
+
items: page.items.map((item) => this.withRecordMessageActionRef(source, this.record.recordTimelineItem(item), session.userId, signingKey)),
|
|
7568
7863
|
hasMore: page.hasMore,
|
|
7569
7864
|
...page.nextCursor === void 0 ? {} : { nextCursor: {
|
|
7570
7865
|
sendAtMillis: page.nextCursor.sendAtMillis,
|
|
@@ -7800,6 +8095,329 @@ var ChatService = class {
|
|
|
7800
8095
|
readReceiptItemKey(itemUid, sequence) {
|
|
7801
8096
|
return `${itemUid}\u0000${String(sequence)}`;
|
|
7802
8097
|
}
|
|
8098
|
+
async copySourceMessageLink(sourceRef, actionRefs, options = {}) {
|
|
8099
|
+
const session = await this.runtime.requireSession();
|
|
8100
|
+
const source = await this.source.openSourceRef(sourceRef, session.userId);
|
|
8101
|
+
const references = this.forwardReferencesOrderedForCommand(await this.openMessageActionRefs(actionRefs, session.userId, source));
|
|
8102
|
+
if (references.length === 0 || references.length > 100) throw new ArkmePluginError("message-actions-selection-invalid", "请选择 1 至 100 条消息", false);
|
|
8103
|
+
this.validateForwardReferences(references);
|
|
8104
|
+
const sources = references.map((reference) => reference.sourceKind === "chat_relation" ? {
|
|
8105
|
+
kind: "chat_relation",
|
|
8106
|
+
chat_session_uid: reference.chatSessionUid,
|
|
8107
|
+
relation_uid: reference.relationUid
|
|
8108
|
+
} : {
|
|
8109
|
+
kind: "record",
|
|
8110
|
+
record_owner_user_id: reference.recordOwnerUserId,
|
|
8111
|
+
record_uid: reference.recordUid
|
|
8112
|
+
});
|
|
8113
|
+
const data = await this.runtime.authenticatedChatPost("/api/v1/chats/messages/copy-link/get-or-create", { sources }, session, options.signal);
|
|
8114
|
+
const sid = stringValue$1(data.sid).trim();
|
|
8115
|
+
const url = stringValue$1(data.url).trim();
|
|
8116
|
+
if (sid === "" || !/^https?:\/\//i.test(url)) throw new ArkmePluginError("message-copy-link-response-invalid", "复制链接失败,请稍后重试", true, 502);
|
|
8117
|
+
return {
|
|
8118
|
+
sid,
|
|
8119
|
+
url
|
|
8120
|
+
};
|
|
8121
|
+
}
|
|
8122
|
+
async resolveMessageCopyLink(sid, options = {}) {
|
|
8123
|
+
const normalizedSid = sid.trim();
|
|
8124
|
+
if (!/^[0-9A-Za-z]{16}$/.test(normalizedSid)) throw new ArkmePluginError("message-copy-link-unavailable", "链接暂不可用", false, 404);
|
|
8125
|
+
const session = await this.runtime.requireSession();
|
|
8126
|
+
const data = await this.runtime.authenticatedChatPost("/api/v1/chats/messages/copy-link/resolve", { sid: normalizedSid }, session, options.signal);
|
|
8127
|
+
const rawItems = listValue$16(data.items);
|
|
8128
|
+
if (rawItems.length === 0 && stringValue$1(data.audit_status) === "auditing") throw new ArkmePluginError("message-copy-link-auditing", "审核中", true, 409);
|
|
8129
|
+
const items = rawItems.map(messageCopyLinkSnapshotItemFromData);
|
|
8130
|
+
if (items.length === 0 || items.length > MAX_MESSAGE_COPY_LINK_ITEMS) throw new ArkmePluginError("message-copy-link-unavailable", "链接暂不可用", false, 404);
|
|
8131
|
+
const presentation = messageCopyLinkPresentationNodesFromData(data.presentation, items.length);
|
|
8132
|
+
const sourceContext = objectValue$1(data.source_context);
|
|
8133
|
+
const accessMode = stringValue$1(data.access_mode);
|
|
8134
|
+
if (accessMode !== "normal" && accessMode !== "link_read_only") throw new ArkmePluginError("message-copy-link-unavailable", "链接暂不可用", false, 404);
|
|
8135
|
+
const anchors = accessMode === "normal" ? listValue$16(sourceContext.anchors).map(messageCopyLinkSourceAnchorFromData).filter((anchor) => anchor.relationUid.trim() !== "" && anchor.recordUid.trim() !== "" && anchor.recordOwnerUserId > 0) : [];
|
|
8136
|
+
const recordContext = mergeMessageCopyLinkRecordContexts(messageCopyLinkRecordContextFromData(data), accessMode === "normal" ? await this.loadMessageCopyLinkPublicExtensions(messageCopyLinkRecordUidCandidates(items, anchors), session, options.signal) : void 0);
|
|
8137
|
+
const result = {
|
|
8138
|
+
sid: stringValue$1(data.sid),
|
|
8139
|
+
displayTitle: stringValue$1(data.display_title),
|
|
8140
|
+
generatedAtMillis: epochMillisValue(data.generated_at),
|
|
8141
|
+
accessMode,
|
|
8142
|
+
items,
|
|
8143
|
+
presentation,
|
|
8144
|
+
...accessMode === "normal" ? {
|
|
8145
|
+
sourceSessionUid: stringValue$1(sourceContext.chat_session_uid),
|
|
8146
|
+
sourceAnchors: anchors
|
|
8147
|
+
} : {},
|
|
8148
|
+
...recordContext === void 0 ? {} : { recordContext }
|
|
8149
|
+
};
|
|
8150
|
+
if (result.sid !== normalizedSid || accessMode === "normal" && (result.sourceSessionUid === void 0 || result.sourceSessionUid.trim() === "" || anchors.length === 0)) throw new ArkmePluginError("message-copy-link-unavailable", "链接暂不可用", false, 404);
|
|
8151
|
+
return result;
|
|
8152
|
+
}
|
|
8153
|
+
async loadMessageCopyLinkPublicExtensions(recordUids, session, signal) {
|
|
8154
|
+
const worldPost = this.runtime.authenticatedWorldPost;
|
|
8155
|
+
const candidates = recordUids.map((recordUid) => recordUid.trim()).filter((recordUid, index, array) => recordUid !== "" && array.indexOf(recordUid) === index).slice(0, 3);
|
|
8156
|
+
if (candidates.length === 0 || worldPost === void 0) return void 0;
|
|
8157
|
+
for (const normalizedRecordUid of candidates) {
|
|
8158
|
+
const extensions = [];
|
|
8159
|
+
let offset = 0;
|
|
8160
|
+
let total = 0;
|
|
8161
|
+
let hasMore;
|
|
8162
|
+
try {
|
|
8163
|
+
while (true) {
|
|
8164
|
+
const data = await worldPost.call(this.runtime, "/api/v1/public-record/extend-list", {
|
|
8165
|
+
record_uid: normalizedRecordUid,
|
|
8166
|
+
limit: 50,
|
|
8167
|
+
offset
|
|
8168
|
+
}, session, signal, {
|
|
8169
|
+
lane: "interactive-read",
|
|
8170
|
+
bypassCache: true
|
|
8171
|
+
});
|
|
8172
|
+
const rawItems = listValue$16(data.list ?? data.items);
|
|
8173
|
+
if (offset === 0) total = Math.max(0, integerLikeValue$1(data.total));
|
|
8174
|
+
const pageItems = rawItems.map((raw) => messageCopyLinkExtensionItemFromData(raw)).filter((item) => item !== void 0);
|
|
8175
|
+
extensions.push(...pageItems);
|
|
8176
|
+
if (rawItems.length === 0) break;
|
|
8177
|
+
offset += rawItems.length;
|
|
8178
|
+
hasMore = data.has_more === true || data.hasMore === true ? true : data.has_more === false || data.hasMore === false ? false : void 0;
|
|
8179
|
+
if (total > 0 && offset >= total) break;
|
|
8180
|
+
if (hasMore === false) break;
|
|
8181
|
+
if (hasMore === void 0 && total <= 0) break;
|
|
8182
|
+
}
|
|
8183
|
+
} catch {
|
|
8184
|
+
continue;
|
|
8185
|
+
}
|
|
8186
|
+
const extensionCount = Math.max(total, extensions.length);
|
|
8187
|
+
if (extensionCount <= 0 && extensions.length === 0) continue;
|
|
8188
|
+
extensions.sort((left, right) => right.sendAtMillis - left.sendAtMillis);
|
|
8189
|
+
return {
|
|
8190
|
+
extensionCount,
|
|
8191
|
+
extensions
|
|
8192
|
+
};
|
|
8193
|
+
}
|
|
8194
|
+
}
|
|
8195
|
+
async extendMessageCopyLink(sid, itemIndex, textContent, recordUid, options = {}) {
|
|
8196
|
+
const normalizedSid = sid.trim();
|
|
8197
|
+
const normalizedIndex = Math.trunc(itemIndex);
|
|
8198
|
+
const normalizedUid = recordUid.trim();
|
|
8199
|
+
const normalizedText = textContent.trim();
|
|
8200
|
+
if (!/^[0-9A-Za-z]{16}$/.test(normalizedSid) || normalizedIndex < 0 || normalizedIndex >= MAX_MESSAGE_COPY_LINK_ITEMS) throw new ArkmePluginError("message-copy-link-unavailable", "链接暂不可用", false, 404);
|
|
8201
|
+
if (!RECORD_UID_PATTERN.test(normalizedUid)) throw new ArkmePluginError("record-uid-invalid", "写入标识无效,请重试", false);
|
|
8202
|
+
if (normalizedText === "") throw new ArkmePluginError("record-text-empty", "请输入延展内容", false);
|
|
8203
|
+
if (normalizedText.length > this.runtime.config.maxTextLength) throw new ArkmePluginError("record-text-too-long", `内容不能超过 ${this.runtime.config.maxTextLength} 个字符`, false);
|
|
8204
|
+
const session = await this.runtime.requireSession();
|
|
8205
|
+
const detail = await this.resolveMessageCopyLink(normalizedSid, options);
|
|
8206
|
+
const item = detail.items[normalizedIndex];
|
|
8207
|
+
const anchor = detail.accessMode === "normal" ? detail.sourceAnchors?.[normalizedIndex] : void 0;
|
|
8208
|
+
const parentRecordUid = item === void 0 ? "" : messageCopyLinkRecordUidCandidates([item], anchor === void 0 ? [] : [anchor])[0] ?? "";
|
|
8209
|
+
if (item === void 0 || parentRecordUid === "") throw new ArkmePluginError("message-copy-link-extension-unavailable", "链接暂不可延展", false, 409);
|
|
8210
|
+
const profile = (await this.profile.refreshProfile()).profile;
|
|
8211
|
+
if (profile === null) throw new ArkmePluginError("profile-unavailable", "无法读取当前 Arkme 账号资料", true);
|
|
8212
|
+
if (profile.contact.phoneMasked === void 0) throw new ArkmePluginError("world-phone-binding-required", "请先在 Arkme 客户端绑定手机号,再延展快记", false);
|
|
8213
|
+
const sendAtMillis = Date.now();
|
|
8214
|
+
const recordResult = await this.record.createTextForConversation(normalizedUid, normalizedText);
|
|
8215
|
+
if (recordResult.localState !== "synced") throw new ArkmePluginError("message-copy-link-extension-record-pending", recordResult.error ?? "延展已保存到待重试队列,请稍后重试", true);
|
|
8216
|
+
const createdRecordUid = recordResult.recordUid.trim() || normalizedUid;
|
|
8217
|
+
const data = await this.runtime.authenticatedWorldPost("/api/v1/public-record/publish", {
|
|
8218
|
+
record_uid: createdRecordUid,
|
|
8219
|
+
content: normalizedText,
|
|
8220
|
+
text_content: normalizedText,
|
|
8221
|
+
tags: messageCopyLinkWorldTags(normalizedText),
|
|
8222
|
+
original_topic_id: 0,
|
|
8223
|
+
created_at: sendAtMillis,
|
|
8224
|
+
nick_name: profile.nickname || profile.displayName,
|
|
8225
|
+
avatar: profile.avatarRef,
|
|
8226
|
+
template_kind: 1,
|
|
8227
|
+
parent_record_uid: parentRecordUid
|
|
8228
|
+
}, session, options.signal);
|
|
8229
|
+
const publishedRecordUid = stringValue$1(data.record_uid).trim() || createdRecordUid;
|
|
8230
|
+
const senderDisplayName = profile.nickname.trim() || profile.displayName.trim() || "我";
|
|
8231
|
+
const senderAvatarUrl = profile.avatarRef.trim();
|
|
8232
|
+
this.source.invalidateSourceListCache(session.userId, "send_to_self");
|
|
8233
|
+
this.realtime.emitChatClientEvent({
|
|
8234
|
+
type: "projection-invalidated",
|
|
8235
|
+
revision: this.realtime.nextChatClientRevision(),
|
|
8236
|
+
projection: "record"
|
|
8237
|
+
});
|
|
8238
|
+
return {
|
|
8239
|
+
sid: detail.sid,
|
|
8240
|
+
recordUid: publishedRecordUid,
|
|
8241
|
+
parentRecordUid,
|
|
8242
|
+
status: numberValue$16(data.status ?? data.check_status ?? recordResult.status),
|
|
8243
|
+
localState: "synced",
|
|
8244
|
+
extension: {
|
|
8245
|
+
recordUid: publishedRecordUid,
|
|
8246
|
+
level: 2,
|
|
8247
|
+
sourceKind: "record_extension",
|
|
8248
|
+
senderDisplayName,
|
|
8249
|
+
...senderAvatarUrl === "" ? {} : { senderAvatarUrl },
|
|
8250
|
+
title: "",
|
|
8251
|
+
textContent: normalizedText,
|
|
8252
|
+
sendAtMillis,
|
|
8253
|
+
templateKind: 1,
|
|
8254
|
+
displayKind: 0,
|
|
8255
|
+
officialMark: 0,
|
|
8256
|
+
mediaItems: []
|
|
8257
|
+
}
|
|
8258
|
+
};
|
|
8259
|
+
}
|
|
8260
|
+
async resolveLinkMetadata(rawUrl, options = {}) {
|
|
8261
|
+
const url = ensureMetadataUrl(rawUrl);
|
|
8262
|
+
try {
|
|
8263
|
+
return await this.fetchLinkMetadata(url, 0, options.signal);
|
|
8264
|
+
} catch (error) {
|
|
8265
|
+
if (error instanceof ArkmePluginError && (error.code === "link-metadata-url-invalid" || error.code === "link-metadata-url-forbidden")) throw error;
|
|
8266
|
+
return {
|
|
8267
|
+
url: url.toString(),
|
|
8268
|
+
title: fallbackLinkTitle(url),
|
|
8269
|
+
siteName: url.hostname.replace(/^www\./iu, "")
|
|
8270
|
+
};
|
|
8271
|
+
}
|
|
8272
|
+
}
|
|
8273
|
+
async fetchLinkMetadata(url, redirectCount, signal) {
|
|
8274
|
+
const controller = new AbortController();
|
|
8275
|
+
const abort = () => {
|
|
8276
|
+
controller.abort(signal?.reason);
|
|
8277
|
+
};
|
|
8278
|
+
if (signal?.aborted === true) abort();
|
|
8279
|
+
else signal?.addEventListener("abort", abort, { once: true });
|
|
8280
|
+
const timeout = setTimeout(() => {
|
|
8281
|
+
controller.abort();
|
|
8282
|
+
}, Math.min(8e3, this.runtime.config.requestTimeoutMs));
|
|
8283
|
+
try {
|
|
8284
|
+
const response = await this.runtime.fetchImpl(url, {
|
|
8285
|
+
method: "GET",
|
|
8286
|
+
redirect: "manual",
|
|
8287
|
+
headers: {
|
|
8288
|
+
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
|
8289
|
+
"Accept-Language": "zh-CN",
|
|
8290
|
+
"User-Agent": "Mozilla/5.0 ArkmeDSHPlugin/0.1 LinkPreview"
|
|
8291
|
+
},
|
|
8292
|
+
signal: controller.signal
|
|
8293
|
+
});
|
|
8294
|
+
if (response.status >= 300 && response.status < 400) {
|
|
8295
|
+
if (redirectCount >= LINK_METADATA_MAX_REDIRECTS) return {
|
|
8296
|
+
url: url.toString(),
|
|
8297
|
+
title: fallbackLinkTitle(url),
|
|
8298
|
+
siteName: url.hostname.replace(/^www\./iu, "")
|
|
8299
|
+
};
|
|
8300
|
+
const location = response.headers.get("location");
|
|
8301
|
+
if (location === null || location.trim() === "") return {
|
|
8302
|
+
url: url.toString(),
|
|
8303
|
+
title: fallbackLinkTitle(url),
|
|
8304
|
+
siteName: url.hostname.replace(/^www\./iu, "")
|
|
8305
|
+
};
|
|
8306
|
+
return await this.fetchLinkMetadata(ensureMetadataUrl(new URL(location, url).toString()), redirectCount + 1, signal);
|
|
8307
|
+
}
|
|
8308
|
+
if (!response.ok) return {
|
|
8309
|
+
url: url.toString(),
|
|
8310
|
+
title: fallbackLinkTitle(url),
|
|
8311
|
+
siteName: url.hostname.replace(/^www\./iu, "")
|
|
8312
|
+
};
|
|
8313
|
+
const contentType = response.headers.get("content-type")?.toLowerCase() ?? "";
|
|
8314
|
+
if (contentType !== "" && !contentType.includes("html") && !contentType.includes("xml") && !contentType.includes("text/")) return {
|
|
8315
|
+
url: url.toString(),
|
|
8316
|
+
title: fallbackLinkTitle(url),
|
|
8317
|
+
siteName: url.hostname.replace(/^www\./iu, "")
|
|
8318
|
+
};
|
|
8319
|
+
return linkMetadataFromHtml(url, await readResponseTextPrefix(response));
|
|
8320
|
+
} finally {
|
|
8321
|
+
clearTimeout(timeout);
|
|
8322
|
+
signal?.removeEventListener("abort", abort);
|
|
8323
|
+
}
|
|
8324
|
+
}
|
|
8325
|
+
async forwardSourceMessages(sourceRef, actionRefs, options = {}) {
|
|
8326
|
+
const session = await this.runtime.requireSession();
|
|
8327
|
+
const source = await this.source.openSourceRef(sourceRef, session.userId);
|
|
8328
|
+
const targetSourceRef = options.targetSourceRef?.trim() || sourceRef;
|
|
8329
|
+
const targetSource = targetSourceRef === sourceRef ? source : await this.source.openSourceRef(targetSourceRef, session.userId);
|
|
8330
|
+
const commentText = options.commentText?.trim() ?? "";
|
|
8331
|
+
if (commentText.length > this.runtime.config.maxTextLength) throw new ArkmePluginError("source-text-invalid", "发送内容超过长度限制", false);
|
|
8332
|
+
if (targetSource.kind !== "private_chat" && targetSource.kind !== "group_chat" && targetSource.kind !== "send_to_self" && targetSource.kind !== "default_category" && targetSource.kind !== "topic") throw new ArkmePluginError("message-actions-source-invalid", "当前数据源暂不支持转发", false, 409);
|
|
8333
|
+
const references = this.forwardReferencesOrderedForCommand(await this.openMessageActionRefs(actionRefs, session.userId, source));
|
|
8334
|
+
if (references.length === 0 || references.length > 100) throw new ArkmePluginError("message-actions-selection-invalid", "请选择 1 至 100 条消息", false);
|
|
8335
|
+
this.validateForwardReferences(references);
|
|
8336
|
+
const recordUid = options.recordUid?.trim() || randomUUID();
|
|
8337
|
+
const title = references.length === 1 ? "转发快记" : `转发 ${String(references.length)} 条快记`;
|
|
8338
|
+
const contentPayload = {
|
|
8339
|
+
payload_kind: 1,
|
|
8340
|
+
schema_version: 1,
|
|
8341
|
+
text_state: 1,
|
|
8342
|
+
forward_records: {
|
|
8343
|
+
render_kind: "forward_records",
|
|
8344
|
+
schema_version: 1,
|
|
8345
|
+
source_type: "quick_records",
|
|
8346
|
+
title: "转发快记",
|
|
8347
|
+
source_record_uids: references.map((reference) => reference.recordUid).filter((recordUid) => recordUid.trim() !== ""),
|
|
8348
|
+
created_at: Date.now(),
|
|
8349
|
+
summary_lines: references.slice(0, 3).map((reference) => `${reference.senderName}: ${reference.textContent.trim() || reference.title.trim() || "非文本内容"}`),
|
|
8350
|
+
items: references.map((reference, index) => this.forwardPayloadItem(reference, index))
|
|
8351
|
+
}
|
|
8352
|
+
};
|
|
8353
|
+
const appendCommentWarning = async (sent) => {
|
|
8354
|
+
if (commentText === "") return sent;
|
|
8355
|
+
try {
|
|
8356
|
+
await this.sendSourceText(targetSourceRef, commentText, { ...options.signal === void 0 ? {} : { signal: options.signal } });
|
|
8357
|
+
return sent;
|
|
8358
|
+
} catch {
|
|
8359
|
+
return {
|
|
8360
|
+
...sent,
|
|
8361
|
+
warningText: "转发已完成,附言发送失败"
|
|
8362
|
+
};
|
|
8363
|
+
}
|
|
8364
|
+
};
|
|
8365
|
+
if (targetSource.kind === "private_chat" || targetSource.kind === "group_chat") {
|
|
8366
|
+
const sendAtMillis = Date.now();
|
|
8367
|
+
const clientRequestId = this.forwardClientRequestId(targetSource.ownerRef, references, sendAtMillis);
|
|
8368
|
+
const sourceRecordUids = references.some((reference) => reference.sourceKind === "chat_relation") ? [] : references.map((reference) => reference.recordUid).filter((recordUid) => recordUid.trim() !== "");
|
|
8369
|
+
const data = await this.runtime.authenticatedChatPost("/api/v1/chats/records/forward", {
|
|
8370
|
+
chat_session_uid: targetSource.ownerRef,
|
|
8371
|
+
client_request_id: clientRequestId,
|
|
8372
|
+
...sourceRecordUids.length === 0 ? {} : { source_record_uids: sourceRecordUids },
|
|
8373
|
+
source_items: references.map((reference) => this.forwardSourceItemBody(reference)),
|
|
8374
|
+
...commentText === "" ? {} : { comment_text: commentText },
|
|
8375
|
+
send_at: sendAtMillis
|
|
8376
|
+
}, session, options.signal);
|
|
8377
|
+
const sequence = numberValue$16(data.seq ?? data.sequence);
|
|
8378
|
+
this.realtime.scheduleChatSessionProjection(targetSource.ownerRef, sequence);
|
|
8379
|
+
return {
|
|
8380
|
+
sourceRef: targetSourceRef,
|
|
8381
|
+
itemUid: stringValue$1(data.record_uid).trim() || recordUid,
|
|
8382
|
+
status: numberValue$16(data.audit_status ?? data.status) || 1,
|
|
8383
|
+
...sequence > 0 ? { sequence } : {},
|
|
8384
|
+
localState: "synced"
|
|
8385
|
+
};
|
|
8386
|
+
}
|
|
8387
|
+
if (targetSource.kind === "topic") {
|
|
8388
|
+
const data = await this.runtime.authenticatedPost("/api/v1/topics/records/create", {
|
|
8389
|
+
topic_uid: targetSource.ownerRef,
|
|
8390
|
+
record_uid: recordUid,
|
|
8391
|
+
template_kind: 1,
|
|
8392
|
+
title: "",
|
|
8393
|
+
text_content: title,
|
|
8394
|
+
content_payload: contentPayload,
|
|
8395
|
+
send_at: Date.now()
|
|
8396
|
+
}, session, options.signal);
|
|
8397
|
+
this.source.invalidateSourceListCache(session.userId, "send_to_self");
|
|
8398
|
+
return await appendCommentWarning({
|
|
8399
|
+
sourceRef: targetSourceRef,
|
|
8400
|
+
itemUid: stringValue$1(data.record_uid).trim() || recordUid,
|
|
8401
|
+
status: numberValue$16(data.status),
|
|
8402
|
+
localState: "synced"
|
|
8403
|
+
});
|
|
8404
|
+
}
|
|
8405
|
+
const data = await this.runtime.authenticatedPost("/api/v1/records/create", {
|
|
8406
|
+
record_uid: recordUid,
|
|
8407
|
+
template_kind: 1,
|
|
8408
|
+
title: "",
|
|
8409
|
+
text_content: title,
|
|
8410
|
+
content_payload: contentPayload,
|
|
8411
|
+
send_at: Date.now()
|
|
8412
|
+
}, session, options.signal);
|
|
8413
|
+
this.source.invalidateSourceListCache(session.userId, "send_to_self");
|
|
8414
|
+
return await appendCommentWarning({
|
|
8415
|
+
sourceRef: targetSourceRef,
|
|
8416
|
+
itemUid: stringValue$1(data.record_uid).trim() || recordUid,
|
|
8417
|
+
status: numberValue$16(data.status),
|
|
8418
|
+
localState: "synced"
|
|
8419
|
+
});
|
|
8420
|
+
}
|
|
7803
8421
|
async sendSourceText(sourceRef, textContent, options = {}) {
|
|
7804
8422
|
const session = await this.runtime.requireSession();
|
|
7805
8423
|
const source = await this.source.openSourceRef(sourceRef, session.userId);
|
|
@@ -8053,9 +8671,6 @@ var ChatService = class {
|
|
|
8053
8671
|
localState: "synced"
|
|
8054
8672
|
};
|
|
8055
8673
|
}
|
|
8056
|
-
async sendGroupSourceTextWithAiPolish(sourceRef, chatSessionUid, originalText, recordUid, relationUid, session, options = {}) {
|
|
8057
|
-
return await this.aiPolish.sendGroupSourceTextWithAiPolish(sourceRef, chatSessionUid, originalText, recordUid, relationUid, session, options);
|
|
8058
|
-
}
|
|
8059
8674
|
async sendSourceRich(sourceRef, input, options = {}) {
|
|
8060
8675
|
if (this.runtime.config.richMediaSendEnabled === false) throw new ArkmePluginError("rich-content-disabled", "富内容发送已被插件配置关闭", false, 403);
|
|
8061
8676
|
const session = await this.runtime.requireSession();
|
|
@@ -8285,12 +8900,6 @@ var ChatService = class {
|
|
|
8285
8900
|
async fetchMedia(mediaRef, range, signal) {
|
|
8286
8901
|
return await this.media.fetchMedia(mediaRef, range, signal);
|
|
8287
8902
|
}
|
|
8288
|
-
currentUserAgentSourceFallback(userId, source) {
|
|
8289
|
-
return this.arko.currentUserAgentSourceFallback(userId, source);
|
|
8290
|
-
}
|
|
8291
|
-
async agentSourceDisplayName(session) {
|
|
8292
|
-
return await this.arko.agentSourceDisplayName(session);
|
|
8293
|
-
}
|
|
8294
8903
|
async sendDirectText(recipientArkmeId, textContent, options = {}) {
|
|
8295
8904
|
const session = await this.runtime.requireSession();
|
|
8296
8905
|
const recipient = recipientArkmeId.trim();
|
|
@@ -8383,6 +8992,7 @@ var ChatService = class {
|
|
|
8383
8992
|
const forwardRecords = await this.chatForwardRecordsPreview(item, session.userId, sendAtMillis);
|
|
8384
8993
|
const rawAgentSource = timelineAgentSource(relation, record, payload);
|
|
8385
8994
|
const agentSource = senderUserId === session.userId ? this.arko.currentUserAgentSourceFallback(session.userId, rawAgentSource) : rawAgentSource;
|
|
8995
|
+
const contentBlocks = this.media.richContentBlocks(item, session.userId);
|
|
8386
8996
|
const mentionsViewer = senderUserId !== session.userId && arkmeMentionMetadataMentionsViewer(record, payload, session.userId);
|
|
8387
8997
|
items.push({
|
|
8388
8998
|
itemUid: uid,
|
|
@@ -8401,7 +9011,7 @@ var ChatService = class {
|
|
|
8401
9011
|
...aiPolish === void 0 ? {} : { aiPolish },
|
|
8402
9012
|
...forwardRecords === void 0 ? {} : { forwardRecords },
|
|
8403
9013
|
displayKind: numberValue$16(payload.display_kind),
|
|
8404
|
-
contentBlocks
|
|
9014
|
+
contentBlocks
|
|
8405
9015
|
});
|
|
8406
9016
|
}
|
|
8407
9017
|
return items;
|
|
@@ -8571,11 +9181,31 @@ var ChatService = class {
|
|
|
8571
9181
|
const forwardRecords = await this.chatForwardRecordsPreview(item, session.userId, sendAtMillis);
|
|
8572
9182
|
const rawAgentSource = timelineAgentSource(relation, record, payload);
|
|
8573
9183
|
const agentSource = senderUserId === session.userId ? this.arko.currentUserAgentSourceFallback(session.userId, rawAgentSource) : rawAgentSource;
|
|
9184
|
+
const contentBlocks = this.media.richContentBlocks(item, session.userId);
|
|
9185
|
+
const senderName = stringValue$1(relation.display_name_snapshot).trim() || "Arkme用户";
|
|
8574
9186
|
const itemIndex = items.push({
|
|
8575
9187
|
itemUid: uid,
|
|
8576
9188
|
...source.kind !== "group_chat" || relationUid === "" || senderUserId === session.userId ? {} : { messageRef: this.sealMessageRef(session.userId, source.ownerRef, relationUid, signingKey) },
|
|
9189
|
+
...relationUid === "" ? {} : { messageActionRef: this.sealMessageActionRef(this.messageActionRefPayload({
|
|
9190
|
+
sourceKind: "chat_relation",
|
|
9191
|
+
userId: session.userId,
|
|
9192
|
+
sourceOwnerRef: source.ownerRef,
|
|
9193
|
+
chatSessionUid: source.ownerRef,
|
|
9194
|
+
relationUid,
|
|
9195
|
+
recordOwnerUserId: 0,
|
|
9196
|
+
recordUid: uid,
|
|
9197
|
+
senderUserId,
|
|
9198
|
+
senderName,
|
|
9199
|
+
title: stringValue$1(payload.title),
|
|
9200
|
+
textContent: stringValue$1(payload.text_content),
|
|
9201
|
+
sendAtMillis,
|
|
9202
|
+
sourceSequence: numberValue$16(relation.seq),
|
|
9203
|
+
templateKind: numberValue$16(payload.template_kind),
|
|
9204
|
+
displayKind: numberValue$16(payload.display_kind),
|
|
9205
|
+
contentBlocks
|
|
9206
|
+
}), signingKey) },
|
|
8577
9207
|
...senderUserId > 0 ? { memberRef: await this.sealChatMemberRef(session.userId, source.ownerRef, senderUserId) } : {},
|
|
8578
|
-
senderName
|
|
9208
|
+
senderName,
|
|
8579
9209
|
...agentSource === void 0 ? {} : { agentSource },
|
|
8580
9210
|
isMe: senderUserId === session.userId,
|
|
8581
9211
|
sendAtMillis,
|
|
@@ -8591,7 +9221,7 @@ var ChatService = class {
|
|
|
8591
9221
|
updateAtMillis: numberValue$16(payload.update_at ?? record.update_at),
|
|
8592
9222
|
recordDurationMillis: numberValue$16(payload.record_duration_millis),
|
|
8593
9223
|
editDurationMillis: numberValue$16(payload.edit_duration_millis),
|
|
8594
|
-
contentBlocks
|
|
9224
|
+
contentBlocks,
|
|
8595
9225
|
...forwardRecords === void 0 ? {} : { forwardRecords }
|
|
8596
9226
|
}) - 1;
|
|
8597
9227
|
if (senderUserId > 0) senderUserIdByIndex.set(itemIndex, senderUserId);
|
|
@@ -8636,6 +9266,176 @@ var ChatService = class {
|
|
|
8636
9266
|
if (raw.version !== 1 || result.viewerUserId !== expectedViewerUserId || result.chatSessionUid !== expectedChatSessionUid || !Number.isSafeInteger(result.targetUserId) || result.targetUserId <= 0) throw new ArkmePluginError("chat-member-ref-invalid", "聊天成员引用与当前账号或会话不匹配", false, 403);
|
|
8637
9267
|
return result;
|
|
8638
9268
|
}
|
|
9269
|
+
messageActionRefPayload(input) {
|
|
9270
|
+
const contentBlocks = input.contentBlocks ?? [];
|
|
9271
|
+
const fileNames = contentBlocks.filter((block) => block.kind === "file" && block.fileName.trim() !== "").slice(0, 5).map((block) => block.fileName.trim().slice(0, 256));
|
|
9272
|
+
return {
|
|
9273
|
+
version: 1,
|
|
9274
|
+
sourceKind: input.sourceKind,
|
|
9275
|
+
userId: input.userId,
|
|
9276
|
+
sourceOwnerRef: input.sourceOwnerRef,
|
|
9277
|
+
chatSessionUid: input.chatSessionUid,
|
|
9278
|
+
relationUid: input.relationUid,
|
|
9279
|
+
recordOwnerUserId: Math.trunc(input.recordOwnerUserId),
|
|
9280
|
+
recordUid: input.recordUid,
|
|
9281
|
+
senderUserId: Math.trunc(input.senderUserId),
|
|
9282
|
+
senderName: input.senderName.trim() || "Arkme用户",
|
|
9283
|
+
title: input.title.slice(0, 500),
|
|
9284
|
+
textContent: input.textContent.slice(0, this.runtime.config.maxTextLength),
|
|
9285
|
+
sendAtMillis: Math.trunc(input.sendAtMillis),
|
|
9286
|
+
sourceSequence: Math.max(0, Math.trunc(numberValue$16(input.sourceSequence))),
|
|
9287
|
+
templateKind: Math.trunc(input.templateKind),
|
|
9288
|
+
displayKind: Math.trunc(input.displayKind),
|
|
9289
|
+
imageCount: contentBlocks.filter((block) => block.kind === "image").length,
|
|
9290
|
+
voiceCount: contentBlocks.filter((block) => block.kind === "audio").length,
|
|
9291
|
+
fileCount: contentBlocks.filter((block) => block.kind === "file").length,
|
|
9292
|
+
fileNames
|
|
9293
|
+
};
|
|
9294
|
+
}
|
|
9295
|
+
withRecordMessageActionRef(source, item, userId, signingKey) {
|
|
9296
|
+
if (source.kind !== "send_to_self" && source.kind !== "default_category" && source.kind !== "topic" || item.itemUid.trim() === "" || !item.isMe) return item;
|
|
9297
|
+
return {
|
|
9298
|
+
...item,
|
|
9299
|
+
messageActionRef: this.sealMessageActionRef(this.messageActionRefPayload({
|
|
9300
|
+
sourceKind: "record",
|
|
9301
|
+
userId,
|
|
9302
|
+
sourceOwnerRef: source.ownerRef,
|
|
9303
|
+
chatSessionUid: "",
|
|
9304
|
+
relationUid: "",
|
|
9305
|
+
recordOwnerUserId: userId,
|
|
9306
|
+
recordUid: item.itemUid,
|
|
9307
|
+
senderUserId: userId,
|
|
9308
|
+
senderName: item.senderName,
|
|
9309
|
+
title: item.title,
|
|
9310
|
+
textContent: item.textContent,
|
|
9311
|
+
sendAtMillis: item.sendAtMillis,
|
|
9312
|
+
templateKind: item.templateKind ?? 1,
|
|
9313
|
+
displayKind: item.displayKind ?? 0,
|
|
9314
|
+
contentBlocks: item.contentBlocks
|
|
9315
|
+
}), signingKey)
|
|
9316
|
+
};
|
|
9317
|
+
}
|
|
9318
|
+
sealMessageActionRef(payload, signingKey) {
|
|
9319
|
+
const encoded = encodeOpaqueJson$5(payload);
|
|
9320
|
+
return `arkme-message-action-v1.${encoded}.${createHmac("sha256", signingKey).update(encoded).digest("base64url")}`;
|
|
9321
|
+
}
|
|
9322
|
+
async openMessageActionRefs(actionRefs, expectedUserId, source) {
|
|
9323
|
+
const normalizedRefs = actionRefs.map((value) => value.trim()).filter((value) => value !== "");
|
|
9324
|
+
const uniqueRefs = new Set(normalizedRefs);
|
|
9325
|
+
if (normalizedRefs.length !== uniqueRefs.size) throw new ArkmePluginError("message-actions-selection-invalid", "不能选择重复消息", false);
|
|
9326
|
+
const references = [];
|
|
9327
|
+
const relationIdentities = /* @__PURE__ */ new Set();
|
|
9328
|
+
for (const actionRef of normalizedRefs) {
|
|
9329
|
+
const reference = await this.openMessageActionRef(actionRef, expectedUserId, source);
|
|
9330
|
+
const relationIdentity = reference.sourceKind === "chat_relation" ? `chat_relation\u0000${reference.chatSessionUid}\u0000${reference.relationUid}` : `record\u0000${String(reference.recordOwnerUserId)}\u0000${reference.recordUid}`;
|
|
9331
|
+
if (!relationIdentities.add(relationIdentity)) throw new ArkmePluginError("message-actions-selection-invalid", "不能选择重复消息", false);
|
|
9332
|
+
references.push(reference);
|
|
9333
|
+
}
|
|
9334
|
+
return references;
|
|
9335
|
+
}
|
|
9336
|
+
async openMessageActionRef(actionRef, expectedUserId, source) {
|
|
9337
|
+
const parts = actionRef.trim().split(".");
|
|
9338
|
+
if (parts.length !== 3 || parts[0] !== "arkme-message-action-v1") throw new ArkmePluginError("message-action-ref-invalid", "消息操作引用无效,请刷新后重试", false);
|
|
9339
|
+
const payload = parts[1] ?? "";
|
|
9340
|
+
const supplied = Buffer.from(parts[2] ?? "", "base64url");
|
|
9341
|
+
const expected = createHmac("sha256", await this.runtime.stateStore.uniqueCode()).update(payload).digest();
|
|
9342
|
+
if (supplied.length !== expected.length || !timingSafeEqual(supplied, expected)) throw new ArkmePluginError("message-action-ref-invalid", "消息操作引用无效,请刷新后重试", false);
|
|
9343
|
+
let parsed;
|
|
9344
|
+
try {
|
|
9345
|
+
parsed = objectValue$1(decodeOpaqueJson$5(payload));
|
|
9346
|
+
} catch (error) {
|
|
9347
|
+
throw new ArkmePluginError("message-action-ref-invalid", "消息操作引用无效,请刷新后重试", false, 400, { cause: error });
|
|
9348
|
+
}
|
|
9349
|
+
const fileNames = listValue$16(parsed.fileNames).map((value) => stringValue$1(value).trim()).filter((value) => value !== "").slice(0, 5);
|
|
9350
|
+
const result = {
|
|
9351
|
+
version: 1,
|
|
9352
|
+
sourceKind: stringValue$1(parsed.sourceKind).trim() === "record" ? "record" : "chat_relation",
|
|
9353
|
+
userId: Math.trunc(numberValue$16(parsed.userId)),
|
|
9354
|
+
sourceOwnerRef: stringValue$1(parsed.sourceOwnerRef).trim(),
|
|
9355
|
+
chatSessionUid: stringValue$1(parsed.chatSessionUid).trim(),
|
|
9356
|
+
relationUid: stringValue$1(parsed.relationUid).trim(),
|
|
9357
|
+
recordOwnerUserId: Math.trunc(numberValue$16(parsed.recordOwnerUserId)),
|
|
9358
|
+
recordUid: stringValue$1(parsed.recordUid).trim(),
|
|
9359
|
+
senderUserId: Math.trunc(numberValue$16(parsed.senderUserId)),
|
|
9360
|
+
senderName: stringValue$1(parsed.senderName).trim() || "Arkme用户",
|
|
9361
|
+
title: stringValue$1(parsed.title),
|
|
9362
|
+
textContent: stringValue$1(parsed.textContent),
|
|
9363
|
+
sendAtMillis: Math.trunc(numberValue$16(parsed.sendAtMillis)),
|
|
9364
|
+
sourceSequence: Math.max(0, Math.trunc(numberValue$16(parsed.sourceSequence))),
|
|
9365
|
+
templateKind: Math.trunc(numberValue$16(parsed.templateKind)),
|
|
9366
|
+
displayKind: Math.trunc(numberValue$16(parsed.displayKind)),
|
|
9367
|
+
imageCount: Math.max(0, Math.trunc(numberValue$16(parsed.imageCount))),
|
|
9368
|
+
voiceCount: Math.max(0, Math.trunc(numberValue$16(parsed.voiceCount))),
|
|
9369
|
+
fileCount: Math.max(0, Math.trunc(numberValue$16(parsed.fileCount))),
|
|
9370
|
+
fileNames
|
|
9371
|
+
};
|
|
9372
|
+
const matchesChatSource = result.sourceKind === "chat_relation" && (source.kind === "private_chat" || source.kind === "group_chat") && result.sourceOwnerRef === source.ownerRef && result.chatSessionUid === source.ownerRef && result.relationUid !== "" && result.recordUid !== "";
|
|
9373
|
+
const matchesRecordSource = result.sourceKind === "record" && (source.kind === "send_to_self" || source.kind === "default_category" || source.kind === "topic") && result.sourceOwnerRef === source.ownerRef && result.chatSessionUid === "" && result.relationUid === "" && result.recordOwnerUserId === expectedUserId && result.recordUid !== "";
|
|
9374
|
+
if (parsed.version !== 1 || result.userId !== expectedUserId || !matchesChatSource && !matchesRecordSource) throw new ArkmePluginError("message-action-ref-invalid", "消息操作引用与当前账号或会话不匹配,请刷新后重试", false, 403);
|
|
9375
|
+
return result;
|
|
9376
|
+
}
|
|
9377
|
+
forwardPayloadItem(reference, itemOrder) {
|
|
9378
|
+
return {
|
|
9379
|
+
item_order: itemOrder,
|
|
9380
|
+
source_kind: reference.sourceKind,
|
|
9381
|
+
record_uid: reference.recordUid,
|
|
9382
|
+
source_type: reference.sourceKind === "chat_relation" ? "chat_record" : "record",
|
|
9383
|
+
...reference.recordOwnerUserId > 0 ? { owner_id: reference.recordOwnerUserId } : {},
|
|
9384
|
+
...reference.relationUid === "" ? {} : { source_rel_uid: reference.relationUid },
|
|
9385
|
+
...reference.chatSessionUid === "" ? {} : { source_chat_session_uid: reference.chatSessionUid },
|
|
9386
|
+
...reference.sourceSequence > 0 ? { source_seq: reference.sourceSequence } : {},
|
|
9387
|
+
source_sender_user_id: reference.senderUserId,
|
|
9388
|
+
source_display_name: reference.senderName,
|
|
9389
|
+
owner_name: reference.senderName,
|
|
9390
|
+
send_at: reference.sendAtMillis,
|
|
9391
|
+
title: reference.title,
|
|
9392
|
+
text: reference.textContent,
|
|
9393
|
+
text_preview: reference.textContent.trim().slice(0, 500),
|
|
9394
|
+
template_kind: reference.templateKind,
|
|
9395
|
+
display_kind: reference.displayKind,
|
|
9396
|
+
image_count: reference.imageCount,
|
|
9397
|
+
voice_count: reference.voiceCount,
|
|
9398
|
+
file_count: reference.fileCount,
|
|
9399
|
+
file_names: reference.fileNames
|
|
9400
|
+
};
|
|
9401
|
+
}
|
|
9402
|
+
forwardClientRequestId(targetChatSessionUid, references, sendAtMillis) {
|
|
9403
|
+
const sourceIdentity = references.map((reference) => reference.sourceKind === "chat_relation" ? `chat_record:${reference.chatSessionUid}:${reference.relationUid}` : reference.recordUid).join("|");
|
|
9404
|
+
const sourceHash = createHash("sha256").update(sourceIdentity).digest("hex").slice(0, 16);
|
|
9405
|
+
return `forward_${targetChatSessionUid}_${String(Math.trunc(sendAtMillis))}_${sourceHash}`;
|
|
9406
|
+
}
|
|
9407
|
+
forwardReferencesOrderedForCommand(references) {
|
|
9408
|
+
if (references.length < 2 || !references.every((reference) => reference.sourceKind === "chat_relation" && reference.sourceSequence > 0 && reference.chatSessionUid.trim() !== "")) return references;
|
|
9409
|
+
const sourceSessionUid = references[0]?.chatSessionUid.trim() ?? "";
|
|
9410
|
+
if (sourceSessionUid === "" || !references.every((reference) => reference.chatSessionUid.trim() === sourceSessionUid)) return references;
|
|
9411
|
+
const ordered = references.map((reference, index) => ({
|
|
9412
|
+
reference,
|
|
9413
|
+
index
|
|
9414
|
+
})).sort((left, right) => {
|
|
9415
|
+
const sequenceCompare = left.reference.sourceSequence - right.reference.sourceSequence;
|
|
9416
|
+
return sequenceCompare === 0 ? left.index - right.index : sequenceCompare;
|
|
9417
|
+
}).map((item) => item.reference);
|
|
9418
|
+
return ordered.every((reference, index) => reference === references[index]) ? references : ordered;
|
|
9419
|
+
}
|
|
9420
|
+
validateForwardReferences(references) {
|
|
9421
|
+
const sourceTypes = new Set(references.map((reference) => reference.sourceKind));
|
|
9422
|
+
if (sourceTypes.size > 1) throw new ArkmePluginError("message-actions-selection-invalid", "暂不支持混合转发不同类型的消息", false);
|
|
9423
|
+
if (!sourceTypes.has("chat_relation")) return;
|
|
9424
|
+
if (new Set(references.map((reference) => reference.chatSessionUid.trim()).filter((value) => value !== "")).size > 1) throw new ArkmePluginError("message-actions-selection-invalid", "暂不支持跨会话转发聊天消息", false);
|
|
9425
|
+
}
|
|
9426
|
+
forwardSourceItemBody(reference) {
|
|
9427
|
+
if (reference.sourceKind === "chat_relation") return {
|
|
9428
|
+
source_type: "chat_record",
|
|
9429
|
+
record_uid: reference.recordUid,
|
|
9430
|
+
source_chat_session_uid: reference.chatSessionUid,
|
|
9431
|
+
source_rel_uid: reference.relationUid,
|
|
9432
|
+
...reference.sourceSequence > 0 ? { source_seq: reference.sourceSequence } : {}
|
|
9433
|
+
};
|
|
9434
|
+
return {
|
|
9435
|
+
source_type: "record",
|
|
9436
|
+
record_uid: reference.recordUid
|
|
9437
|
+
};
|
|
9438
|
+
}
|
|
8639
9439
|
sealMessageRef(userId, chatSessionUid, relationUid, signingKey) {
|
|
8640
9440
|
const payload = encodeOpaqueJson$5({
|
|
8641
9441
|
version: 1,
|
|
@@ -17059,6 +17859,21 @@ var ArkmeService = class {
|
|
|
17059
17859
|
async reportMessage(messageRef, reportType, options = {}) {
|
|
17060
17860
|
return await this.chat.reportMessage(messageRef, reportType, options);
|
|
17061
17861
|
}
|
|
17862
|
+
async copySourceMessageLink(sourceRef, actionRefs, options = {}) {
|
|
17863
|
+
return await this.chat.copySourceMessageLink(sourceRef, actionRefs, options);
|
|
17864
|
+
}
|
|
17865
|
+
async resolveMessageCopyLink(sid, options = {}) {
|
|
17866
|
+
return await this.chat.resolveMessageCopyLink(sid, options);
|
|
17867
|
+
}
|
|
17868
|
+
async extendMessageCopyLink(sid, itemIndex, textContent, recordUid, options = {}) {
|
|
17869
|
+
return await this.chat.extendMessageCopyLink(sid, itemIndex, textContent, recordUid, options);
|
|
17870
|
+
}
|
|
17871
|
+
async resolveLinkMetadata(rawUrl, options = {}) {
|
|
17872
|
+
return await this.chat.resolveLinkMetadata(rawUrl, options);
|
|
17873
|
+
}
|
|
17874
|
+
async forwardSourceMessages(sourceRef, actionRefs, options = {}) {
|
|
17875
|
+
return await this.chat.forwardSourceMessages(sourceRef, actionRefs, options);
|
|
17876
|
+
}
|
|
17062
17877
|
async sendSourceText(sourceRef, textContent, options = {}) {
|
|
17063
17878
|
return await this.chat.sendSourceText(sourceRef, textContent, options);
|
|
17064
17879
|
}
|
|
@@ -18306,6 +19121,7 @@ function persistentClientFactory(requireModule, spec) {
|
|
|
18306
19121
|
return {
|
|
18307
19122
|
name: `arkme-extension-client:${spec.extensionId}`,
|
|
18308
19123
|
async apply(ctx) {
|
|
19124
|
+
if ((typeof document.location?.search === "string" ? document.location.search : "").split(/[?&]/).includes("arkme-harness-embed=1")) return;
|
|
18309
19125
|
const styles = new Styles();
|
|
18310
19126
|
const taggedConsole = {
|
|
18311
19127
|
...console,
|
|
@@ -18420,7 +19236,7 @@ function renderPersistentClientBundle(packageName, spec) {
|
|
|
18420
19236
|
const renderedSpec = {
|
|
18421
19237
|
...spec,
|
|
18422
19238
|
clientContentDigest: spec.clientContentDigest ?? arkmeClientContentDigest(spec.code),
|
|
18423
|
-
wrapperVersion:
|
|
19239
|
+
wrapperVersion: 4
|
|
18424
19240
|
};
|
|
18425
19241
|
return [
|
|
18426
19242
|
`window.__ModuleLoader__.load({ id: ${JSON.stringify(packageName)}, factory: (require) => {`,
|
|
@@ -18438,13 +19254,16 @@ function renderArkmeBundleClientBundle(packageName, spec) {
|
|
|
18438
19254
|
identityKey: "packageName"
|
|
18439
19255
|
});
|
|
18440
19256
|
}
|
|
18441
|
-
function
|
|
19257
|
+
function persistentExtensionPackageIdentity(extensionId) {
|
|
18442
19258
|
const suffix = createHash("sha256").update(extensionId).digest("hex").slice(0, 16);
|
|
18443
19259
|
return {
|
|
18444
19260
|
packageName: `@arkme-local/ext-${suffix}`,
|
|
18445
19261
|
entryId: `arkme-extension-${suffix}`
|
|
18446
19262
|
};
|
|
18447
19263
|
}
|
|
19264
|
+
function persistentExtensionBundleDirectory(profileDirectory, extensionId, version) {
|
|
19265
|
+
return join(profileDirectory, "arkme-extensions", createHash("sha256").update(extensionId).digest("hex").slice(0, 16), version);
|
|
19266
|
+
}
|
|
18448
19267
|
function writeSecure(path, content) {
|
|
18449
19268
|
writeFileSync(path, content, {
|
|
18450
19269
|
encoding: "utf8",
|
|
@@ -18541,9 +19360,22 @@ function readPersistentExtensionActivation(installationUrl) {
|
|
|
18541
19360
|
}
|
|
18542
19361
|
return state;
|
|
18543
19362
|
}
|
|
19363
|
+
function repairPersistentExtensionInstallation(input) {
|
|
19364
|
+
const installationPath = join(input.bundleDirectory, "installation.json");
|
|
19365
|
+
const installation = JSON.parse(readFileSync(installationPath, "utf8"));
|
|
19366
|
+
if (installation.format_version !== 1 || installation.extension_id !== input.extensionId || installation.version !== input.version || typeof installation.artifact_path !== "string" || installation.artifact_path.trim() === "" || installation.artifact_sha256 !== input.artifactSha256 || installation.manifest_sha256 !== input.manifestSha256 || typeof installation.signature !== "string" || installation.signature.trim() === "" || typeof installation.signing_key_id !== "string" || installation.signing_key_id.trim() === "" || typeof installation.trusted_public_key !== "string" || installation.trusted_public_key.trim() === "" || !Number.isSafeInteger(installation.published_at) || (installation.published_at ?? -1) < 0) throw new Error("Arkme persistent extension installation identity mismatch");
|
|
19367
|
+
const manifest = JSON.parse(readFileSync(join(input.bundleDirectory, "package.json"), "utf8"));
|
|
19368
|
+
if (manifest.name !== input.packageName || manifest.version !== input.version) throw new Error("Arkme persistent extension package identity mismatch");
|
|
19369
|
+
if (installation.artifact_path === input.artifactPath) return false;
|
|
19370
|
+
replaceSecure(installationPath, `${JSON.stringify({
|
|
19371
|
+
...installation,
|
|
19372
|
+
artifact_path: input.artifactPath
|
|
19373
|
+
}, void 0, 2)}\n`);
|
|
19374
|
+
return true;
|
|
19375
|
+
}
|
|
18544
19376
|
function materializePersistentExtensionBundle(input) {
|
|
18545
|
-
const { packageName, entryId } =
|
|
18546
|
-
const bundleDirectory =
|
|
19377
|
+
const { packageName, entryId } = persistentExtensionPackageIdentity(input.resolution.extension_id);
|
|
19378
|
+
const bundleDirectory = persistentExtensionBundleDirectory(input.profileDirectory, input.resolution.extension_id, input.resolution.version);
|
|
18547
19379
|
const installation = {
|
|
18548
19380
|
format_version: 1,
|
|
18549
19381
|
extension_id: input.resolution.extension_id,
|
|
@@ -18568,7 +19400,7 @@ function materializePersistentExtensionBundle(input) {
|
|
|
18568
19400
|
try {
|
|
18569
19401
|
const manifest = JSON.parse(readFileSync(join(bundleDirectory, "package.json"), "utf8"));
|
|
18570
19402
|
const clientBundle = input.clientCode === void 0 ? void 0 : readFileSync(join(bundleDirectory, "lib", "client.js"), "utf8");
|
|
18571
|
-
const clientReady = input.clientCode === void 0 || manifest.exports?.["./client"] === "./lib/client.js" && manifest.dsh?.client?.inject?.length === 0 && clientBundle?.includes("extensions.persistent.invoke") === true && clientBundle.includes("extensions.persistent.client-state") && clientBundle.includes(`\"wrapperVersion\":${String(
|
|
19403
|
+
const clientReady = input.clientCode === void 0 || manifest.exports?.["./client"] === "./lib/client.js" && manifest.dsh?.client?.inject?.length === 0 && clientBundle?.includes("extensions.persistent.invoke") === true && clientBundle.includes("extensions.persistent.client-state") && clientBundle.includes(`\"wrapperVersion\":${String(4)}`);
|
|
18572
19404
|
if (manifest.exports?.["."] === "./lib/index.js" && manifest.exports?.["./package.json"] === "./package.json" && clientReady) {
|
|
18573
19405
|
if (!existsSync(activationPath)) writeSecure(activationPath, activationText(input.resolution.extension_id, true));
|
|
18574
19406
|
return {
|
|
@@ -18654,7 +19486,21 @@ const VM_TIMEOUT_MS = 5e3;
|
|
|
18654
19486
|
function readInstallation(url) {
|
|
18655
19487
|
const value = JSON.parse(readFileSync(url, "utf8"));
|
|
18656
19488
|
if (value.format_version !== 1 || !/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(value.extension_id) || value.version.trim() === "" || value.artifact_path.trim() === "" || value.trusted_public_key.trim() === "") throw new Error("Arkme persistent extension installation metadata is invalid");
|
|
18657
|
-
return
|
|
19489
|
+
return {
|
|
19490
|
+
...value,
|
|
19491
|
+
artifact_path: resolveRuntimeArtifactPath(value, url)
|
|
19492
|
+
};
|
|
19493
|
+
}
|
|
19494
|
+
function resolveRuntimeArtifactPath(installation, installationUrl) {
|
|
19495
|
+
const dshHome = process.env.DSH_HOME?.trim();
|
|
19496
|
+
const filename = basename(installation.artifact_path);
|
|
19497
|
+
if (dshHome !== void 0 && dshHome !== "" && filename !== "" && filename !== "." && filename !== "..") try {
|
|
19498
|
+
const root = realpathSync(join(dshHome, "arkme-self", "extensions"));
|
|
19499
|
+
const candidate = realpathSync(join(root, installation.extension_id, installation.version, filename));
|
|
19500
|
+
const fromRoot = relative(root, candidate);
|
|
19501
|
+
if (fromRoot !== "" && fromRoot !== ".." && !fromRoot.startsWith(`..${sep}`) && !isAbsolute(fromRoot) && sha256Hex(readFileSync(candidate)) === installation.artifact_sha256) return candidate;
|
|
19502
|
+
} catch {}
|
|
19503
|
+
return isAbsolute(installation.artifact_path) ? installation.artifact_path : resolve(dirname(fileURLToPath(installationUrl)), installation.artifact_path);
|
|
18658
19504
|
}
|
|
18659
19505
|
function jsonValue(value) {
|
|
18660
19506
|
if (value === void 0) return null;
|
|
@@ -18922,4 +19768,4 @@ async function invokePersistentArkmeExtension(extensionId, method, args) {
|
|
|
18922
19768
|
return jsonValue(await handler(jsonValue(args)));
|
|
18923
19769
|
}
|
|
18924
19770
|
//#endregion
|
|
18925
|
-
export {
|
|
19771
|
+
export { isArkmeBotAvatarRef as $, inspectNativeBundleArtifactV3 as A, ARKME_EXTENSION_ICON_MAX_BYTES as B, ARKME_BUNDLE_MAX_BYTES as C, bundleSha256 as D, arkmeSandboxEntryId as E, assertExtensionArtifactSize as F, validateVoiceprintPcm16Wav as G, ARKME_EXTENSION_PREVIEW_MAX_BYTES as H, canonicalExtensionJson as I, ARKME_VOICEPRINT_ENROLLMENT_MAX_AUDIO_BYTES as J, ARKME_DEFAULT_SHARE_WEBSITE as K, sha256Hex as L, packLocalNativeBundleDirectoryV3 as M, readNativeBundleTarballV3 as N, canonicalBundleJson as O, renderArkmeSandboxHostEntry as P, ARKME_WORLD_PUBLISH_MAX_IMAGE_BYTES as Q, unpackArkmeExtension as R, ARKME_BUNDLE_ARTIFACT_KIND as S, ArkmeBundleArtifactError as T, ARKME_EXTENSION_RUNTIME_UNAVAILABLE_MESSAGE as U, ARKME_EXTENSION_MAX_BYTES as V, ArkmeService as W, ARKME_VOICEPRINT_ENROLLMENT_MIN_DURATION_MS as X, ARKME_VOICEPRINT_ENROLLMENT_MAX_DURATION_MS as Y, ARKME_WORLD_PUBLISH_MAX_IMAGES as Z, arkmeClientContentDigest as _, persistentArkmeExtensionActive as a, verifyExtensionResolutionSignature as b, persistentExtensionBundleDirectory as c, readPersistentExtensionActivation as d, ArkmePluginError as et, refreshPersistentClientWrapper as f, activeProfileExtensionOwnerConflicts as g, renderArkmeBundleClientBundle as h, invokePersistentArkmeExtension as i, packBundleFiles as j, inspectBundleArtifact as k, persistentExtensionPackageIdentity as l, writePersistentExtensionActivation as m, applyPersistentArkmeHostExtension as n, persistentArkmeExtensionRuntimeState as o, repairPersistentExtensionInstallation as p, ARKME_PROVIDER_CONTRACT_VERSION as q, deactivatePersistentArkmeExtension as r, materializePersistentExtensionBundle as s, activatePersistentArkmeExtension as t, ArkmeOutgoingCallError as tt, quarantinePersistentExtension as u, arkmeClientContentDigestFromSource as v, ARKME_NATIVE_BUNDLE_ARTIFACT_KIND as w, verifyNativeBundleResolutionSignature as x, verifyBundleResolutionSignature as y, ARKME_EXTENSION_FORMAT as z };
|