@senguoyun/dsh-arkme 0.1.12 → 0.1.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/cordis.patch.yml +4 -1
- package/docs/consumer-plugin-contract.md +15 -4
- package/docs/extension-market-controls.md +47 -8
- package/docs/gui-production-integration.md +11 -10
- package/lib/client.js +44991 -25812
- package/lib/client.js.map +1 -1
- package/lib/extension-profile-restart-helper.js +19 -3
- package/lib/index.js +1475 -217
- package/lib/{install-store-D6P-dH2i.js → install-store-Sn3Ccxfu.js} +32 -5
- package/lib/persistent-extension.js +1 -1
- package/lib/{persistent-runtime-kUpXXUZI.js → persistent-runtime-BAArZI-P.js} +1187 -173
- package/lib/plugin-updater-helper-CiPlNqBc.js +346 -0
- package/lib/plugin-updater-helper.js +2 -250
- package/lib/sdk.js +150 -2
- package/lib/types/arkme-service.d.ts +34 -1
- package/lib/types/arkme-service.d.ts.map +1 -1
- package/lib/types/bot-avatar-ref.d.ts +3 -0
- package/lib/types/bot-avatar-ref.d.ts.map +1 -0
- package/lib/types/client/ArkmeAppUpdateDialog.d.ts +5 -0
- package/lib/types/client/ArkmeAppUpdateDialog.d.ts.map +1 -0
- package/lib/types/client/ArkmeBotCreateDialog.d.ts +6 -0
- package/lib/types/client/ArkmeBotCreateDialog.d.ts.map +1 -0
- package/lib/types/client/ArkmeCalendarSurface.d.ts +4 -1
- package/lib/types/client/ArkmeCalendarSurface.d.ts.map +1 -1
- package/lib/types/client/ArkmeContactAddSurface.d.ts +9 -0
- package/lib/types/client/ArkmeContactAddSurface.d.ts.map +1 -0
- package/lib/types/client/ArkmeConversationSurface.d.ts +8 -18
- package/lib/types/client/ArkmeConversationSurface.d.ts.map +1 -1
- package/lib/types/client/ArkmeExtensionReviews.d.ts +10 -0
- package/lib/types/client/ArkmeExtensionReviews.d.ts.map +1 -1
- package/lib/types/client/ArkmeFooterDropdown.d.ts.map +1 -1
- package/lib/types/client/ArkmeLogin.d.ts.map +1 -1
- package/lib/types/client/ArkmeMarketplace.d.ts +164 -0
- package/lib/types/client/ArkmeMarketplace.d.ts.map +1 -0
- package/lib/types/client/ArkmePersistentShell.d.ts +20 -0
- package/lib/types/client/ArkmePersistentShell.d.ts.map +1 -0
- package/lib/types/client/ArkmeProductNavigation.d.ts +4 -3
- package/lib/types/client/ArkmeProductNavigation.d.ts.map +1 -1
- package/lib/types/client/ArkmeQuickAdd.d.ts +12 -0
- package/lib/types/client/ArkmeQuickAdd.d.ts.map +1 -0
- package/lib/types/client/ArkmeSettingsRow.d.ts +1 -1
- package/lib/types/client/ArkmeSettingsRow.d.ts.map +1 -1
- package/lib/types/client/ArkmeSettingsSurface.d.ts +24 -1
- package/lib/types/client/ArkmeSettingsSurface.d.ts.map +1 -1
- package/lib/types/client/ArkmeSidebar.d.ts +7 -1
- package/lib/types/client/ArkmeSidebar.d.ts.map +1 -1
- package/lib/types/client/ArkmeStartupAuthGate.d.ts.map +1 -1
- package/lib/types/client/ArkmeVirtualWorkspace.d.ts +12 -1
- package/lib/types/client/ArkmeVirtualWorkspace.d.ts.map +1 -1
- package/lib/types/client/ArkmeWorldSurface.d.ts +89 -0
- package/lib/types/client/ArkmeWorldSurface.d.ts.map +1 -0
- package/lib/types/client/DeepSeekHarnessSurface.d.ts +6 -0
- package/lib/types/client/DeepSeekHarnessSurface.d.ts.map +1 -0
- package/lib/types/client/api.d.ts +1 -1
- package/lib/types/client/api.d.ts.map +1 -1
- package/lib/types/client/app-update-store.d.ts +35 -0
- package/lib/types/client/app-update-store.d.ts.map +1 -0
- package/lib/types/client/arkme-emoji.d.ts +3 -0
- package/lib/types/client/arkme-emoji.d.ts.map +1 -0
- package/lib/types/client/extension-market-model.d.ts +1 -0
- package/lib/types/client/extension-market-model.d.ts.map +1 -1
- package/lib/types/client/index.d.ts +11 -3
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/redesign/ArkmeRootFrame.d.ts +19 -0
- package/lib/types/client/redesign/ArkmeRootFrame.d.ts.map +1 -0
- package/lib/types/client/redesign/ArkmeTaskSurface.d.ts +15 -0
- package/lib/types/client/redesign/ArkmeTaskSurface.d.ts.map +1 -0
- package/lib/types/client/redesign/ArkmeWorkspaceDialog.d.ts +9 -0
- package/lib/types/client/redesign/ArkmeWorkspaceDialog.d.ts.map +1 -0
- package/lib/types/client/redesign/layout-controller.d.ts +17 -0
- package/lib/types/client/redesign/layout-controller.d.ts.map +1 -0
- package/lib/types/client/redesign/styles.d.ts +3 -0
- package/lib/types/client/redesign/styles.d.ts.map +1 -0
- package/lib/types/client/redesign/task-session.d.ts +14 -0
- package/lib/types/client/redesign/task-session.d.ts.map +1 -0
- package/lib/types/client/redesign/theme-presenter.d.ts +10 -0
- package/lib/types/client/redesign/theme-presenter.d.ts.map +1 -0
- package/lib/types/client/slots-contract.d.ts +2 -2
- package/lib/types/client/slots-contract.d.ts.map +1 -1
- package/lib/types/client/ui-controller.d.ts +21 -8
- package/lib/types/client/ui-controller.d.ts.map +1 -1
- package/lib/types/client/world-voiceprint-expectation-copy.d.ts +7 -0
- package/lib/types/client/world-voiceprint-expectation-copy.d.ts.map +1 -0
- package/lib/types/extensions/bundle-artifact.d.ts +22 -0
- package/lib/types/extensions/bundle-artifact.d.ts.map +1 -1
- package/lib/types/extensions/install-store.d.ts +1 -0
- package/lib/types/extensions/install-store.d.ts.map +1 -1
- package/lib/types/extensions/manager.d.ts +38 -2
- package/lib/types/extensions/manager.d.ts.map +1 -1
- package/lib/types/extensions/owned-inventory.d.ts +29 -3
- package/lib/types/extensions/owned-inventory.d.ts.map +1 -1
- package/lib/types/extensions/owned-refs.d.ts +2 -1
- package/lib/types/extensions/owned-refs.d.ts.map +1 -1
- package/lib/types/extensions/owned-store.d.ts +8 -0
- package/lib/types/extensions/owned-store.d.ts.map +1 -1
- package/lib/types/extensions/owned-types.d.ts +18 -5
- package/lib/types/extensions/owned-types.d.ts.map +1 -1
- package/lib/types/extensions/profile-owned-inventory.d.ts +1 -0
- package/lib/types/extensions/profile-owned-inventory.d.ts.map +1 -1
- package/lib/types/extensions/profile-restart-helper.d.ts.map +1 -1
- package/lib/types/extensions/publish-client.d.ts +47 -2
- package/lib/types/extensions/publish-client.d.ts.map +1 -1
- package/lib/types/extensions/publisher-role.d.ts +9 -0
- package/lib/types/extensions/publisher-role.d.ts.map +1 -0
- package/lib/types/extensions/signature.d.ts +31 -1
- package/lib/types/extensions/signature.d.ts.map +1 -1
- package/lib/types/extensions/types.d.ts +82 -5
- package/lib/types/extensions/types.d.ts.map +1 -1
- package/lib/types/host-api.d.ts.map +1 -1
- package/lib/types/index.d.ts +10 -4
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/plugin-update-artifact.d.ts +46 -0
- package/lib/types/plugin-update-artifact.d.ts.map +1 -0
- package/lib/types/plugin-update.d.ts +20 -11
- package/lib/types/plugin-update.d.ts.map +1 -1
- package/lib/types/plugin-updater-helper.d.ts +11 -0
- package/lib/types/plugin-updater-helper.d.ts.map +1 -1
- package/lib/types/sdk/index.d.ts +77 -7
- package/lib/types/sdk/index.d.ts.map +1 -1
- package/lib/types/services/bot-service.d.ts +5 -1
- package/lib/types/services/bot-service.d.ts.map +1 -1
- package/lib/types/services/contact-service.d.ts +25 -0
- package/lib/types/services/contact-service.d.ts.map +1 -0
- package/lib/types/services/group-service.d.ts +4 -1
- package/lib/types/services/group-service.d.ts.map +1 -1
- package/lib/types/services/record-service.d.ts +2 -1
- package/lib/types/services/record-service.d.ts.map +1 -1
- package/lib/types/services/service.d.ts +1 -0
- package/lib/types/services/service.d.ts.map +1 -1
- package/lib/types/services/world-service.d.ts +53 -6
- package/lib/types/services/world-service.d.ts.map +1 -1
- package/lib/types/tools/business/bots/create.d.ts.map +1 -1
- package/lib/types/tools/business/contacts/index.d.ts +4 -0
- package/lib/types/tools/business/contacts/index.d.ts.map +1 -0
- package/lib/types/tools/business/groups/create.d.ts +2 -0
- package/lib/types/tools/business/groups/create.d.ts.map +1 -0
- package/lib/types/tools/business/groups/index.d.ts +1 -0
- package/lib/types/tools/business/groups/index.d.ts.map +1 -1
- package/lib/types/tools/business/index.d.ts.map +1 -1
- package/lib/types/tools/business/world/index.d.ts +4 -0
- package/lib/types/tools/business/world/index.d.ts.map +1 -1
- package/lib/types/tools/business/world/mine.d.ts +2 -0
- package/lib/types/tools/business/world/mine.d.ts.map +1 -0
- package/lib/types/tools/business/world/user.d.ts +2 -0
- package/lib/types/tools/business/world/user.d.ts.map +1 -0
- package/lib/types/tools/business/world/voiceprint-invite.d.ts +2 -0
- package/lib/types/tools/business/world/voiceprint-invite.d.ts.map +1 -0
- package/lib/types/tools/business/world/voiceprint-social-context.d.ts +2 -0
- package/lib/types/tools/business/world/voiceprint-social-context.d.ts.map +1 -0
- package/lib/types/tools/extensions/index.d.ts.map +1 -1
- package/lib/types/tools/extensions/publish-conversation.d.ts +8 -1
- package/lib/types/tools/extensions/publish-conversation.d.ts.map +1 -1
- package/lib/types/tools/ports/contacts.d.ts +12 -0
- package/lib/types/tools/ports/contacts.d.ts.map +1 -0
- package/lib/types/tools/ports/groups.d.ts +4 -1
- package/lib/types/tools/ports/groups.d.ts.map +1 -1
- package/lib/types/tools/ports/index.d.ts +3 -2
- package/lib/types/tools/ports/index.d.ts.map +1 -1
- package/lib/types/tools/ports/world.d.ts +21 -1
- package/lib/types/tools/ports/world.d.ts.map +1 -1
- package/lib/types/tools/prompts/business.d.ts.map +1 -1
- package/lib/types/tools/registry/registrar.d.ts.map +1 -1
- package/lib/types/types.d.ts +71 -3
- package/lib/types/types.d.ts.map +1 -1
- package/package.json +8 -4
- package/lib/plugin-update-install-state-CIsOCjWK.js +0 -67
- package/lib/types/client/ArkmeExtensionCenter.d.ts +0 -74
- package/lib/types/client/ArkmeExtensionCenter.d.ts.map +0 -1
|
@@ -4,10 +4,10 @@ import { dirname, isAbsolute, join, posix, relative, resolve, sep } from "node:p
|
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { open } from "node:fs/promises";
|
|
6
6
|
import OSS from "ali-oss";
|
|
7
|
+
import { gunzipSync, gzipSync } from "node:zlib";
|
|
7
8
|
import { createContext, runInContext } from "node:vm";
|
|
8
9
|
import { Context } from "@deepseek-ai/cordis";
|
|
9
10
|
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
10
|
-
import { gunzipSync, gzipSync } from "node:zlib";
|
|
11
11
|
import { parseDocument } from "yaml";
|
|
12
12
|
//#region src/outgoing-call-contract.ts
|
|
13
13
|
var ArkmeOutgoingCallError = class extends Error {
|
|
@@ -1072,7 +1072,7 @@ var ServiceRuntime = class {
|
|
|
1072
1072
|
}
|
|
1073
1073
|
async extensionPost(path, body, signal, options = {}) {
|
|
1074
1074
|
const baseUrl = this.config.extensionPublishBaseUrl?.trim() ?? "";
|
|
1075
|
-
if (baseUrl === "") throw new ArkmePluginError("extension-service-disabled", "
|
|
1075
|
+
if (baseUrl === "") throw new ArkmePluginError("extension-service-disabled", "市集服务尚未配置", false, 503);
|
|
1076
1076
|
let session = await this.requireSession();
|
|
1077
1077
|
const requestOptions = () => ({
|
|
1078
1078
|
...options,
|
|
@@ -1091,10 +1091,10 @@ var ServiceRuntime = class {
|
|
|
1091
1091
|
};
|
|
1092
1092
|
//#endregion
|
|
1093
1093
|
//#region src/services/ai-video-service.ts
|
|
1094
|
-
function numberValue$
|
|
1094
|
+
function numberValue$22(value) {
|
|
1095
1095
|
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
1096
1096
|
}
|
|
1097
|
-
function booleanValue$
|
|
1097
|
+
function booleanValue$13(value) {
|
|
1098
1098
|
return value === true;
|
|
1099
1099
|
}
|
|
1100
1100
|
function listValue$21(value) {
|
|
@@ -1109,12 +1109,12 @@ var AiVideoService = class {
|
|
|
1109
1109
|
const session = await this.runtime.requireSession();
|
|
1110
1110
|
const data = await this.runtime.authenticatedIntelligentPost("/api/v1/ai-comic-video/preflight", this.aiVideoSelectionBody(sessionId, segments), session, signal);
|
|
1111
1111
|
return {
|
|
1112
|
-
allowed: booleanValue$
|
|
1112
|
+
allowed: booleanValue$13(data.allowed),
|
|
1113
1113
|
message: stringValue$1(data.message).trim() || "AI 视频内容检查已完成",
|
|
1114
|
-
selectedDurationMillis: Math.max(0, numberValue$
|
|
1115
|
-
minimumDurationMillis: Math.max(0, numberValue$
|
|
1116
|
-
selectedSegmentCount: Math.max(0, numberValue$
|
|
1117
|
-
retryable: booleanValue$
|
|
1114
|
+
selectedDurationMillis: Math.max(0, numberValue$22(data.selected_duration_millis)),
|
|
1115
|
+
minimumDurationMillis: Math.max(0, numberValue$22(data.minimum_duration_millis)),
|
|
1116
|
+
selectedSegmentCount: Math.max(0, numberValue$22(data.selected_segment_count)),
|
|
1117
|
+
retryable: booleanValue$13(data.retryable),
|
|
1118
1118
|
...stringValue$1(data.reason_code).trim() === "" ? {} : { reasonCode: stringValue$1(data.reason_code).trim() },
|
|
1119
1119
|
...stringValue$1(data.proof).trim() === "" ? {} : { proof: stringValue$1(data.proof).trim() }
|
|
1120
1120
|
};
|
|
@@ -1150,7 +1150,7 @@ var AiVideoService = class {
|
|
|
1150
1150
|
}, session, options.signal);
|
|
1151
1151
|
return {
|
|
1152
1152
|
items: listValue$21(data.items).map((raw) => this.aiVideoListItem(raw)).filter((item) => item !== void 0),
|
|
1153
|
-
hasMore: booleanValue$
|
|
1153
|
+
hasMore: booleanValue$13(data.has_more),
|
|
1154
1154
|
...stringValue$1(data.next_cursor).trim() === "" ? {} : { nextCursor: stringValue$1(data.next_cursor).trim() }
|
|
1155
1155
|
};
|
|
1156
1156
|
}
|
|
@@ -1161,13 +1161,13 @@ var AiVideoService = class {
|
|
|
1161
1161
|
texts
|
|
1162
1162
|
}, session, signal);
|
|
1163
1163
|
return {
|
|
1164
|
-
allowed: booleanValue$
|
|
1164
|
+
allowed: booleanValue$13(data.allowed),
|
|
1165
1165
|
message: stringValue$1(data.message).trim() || "AI 视频内容检查已完成",
|
|
1166
|
-
selectedDurationMillis: Math.max(0, numberValue$
|
|
1167
|
-
minimumDurationMillis: Math.max(0, numberValue$
|
|
1168
|
-
selectedSegmentCount: Math.max(0, numberValue$
|
|
1169
|
-
selectedTextCount: Math.max(0, numberValue$
|
|
1170
|
-
retryable: booleanValue$
|
|
1166
|
+
selectedDurationMillis: Math.max(0, numberValue$22(data.selected_duration_millis)),
|
|
1167
|
+
minimumDurationMillis: Math.max(0, numberValue$22(data.minimum_duration_millis)),
|
|
1168
|
+
selectedSegmentCount: Math.max(0, numberValue$22(data.selected_segment_count)),
|
|
1169
|
+
selectedTextCount: Math.max(0, numberValue$22(data.selected_text_count)),
|
|
1170
|
+
retryable: booleanValue$13(data.retryable),
|
|
1171
1171
|
...stringValue$1(data.reason_code).trim() === "" ? {} : { reasonCode: stringValue$1(data.reason_code).trim() },
|
|
1172
1172
|
...stringValue$1(data.proof).trim() === "" ? {} : { proof: stringValue$1(data.proof).trim() }
|
|
1173
1173
|
};
|
|
@@ -1212,13 +1212,13 @@ var AiVideoService = class {
|
|
|
1212
1212
|
jobId,
|
|
1213
1213
|
status,
|
|
1214
1214
|
stage: stringValue$1(data.stage).trim() || status,
|
|
1215
|
-
progress: Math.min(100, Math.max(0, Math.trunc(numberValue$
|
|
1215
|
+
progress: Math.min(100, Math.max(0, Math.trunc(numberValue$22(data.progress)))),
|
|
1216
1216
|
selectedSegmentCount: segmentCount,
|
|
1217
1217
|
...textCount === 0 ? {} : { selectedTextCount: textCount },
|
|
1218
|
-
retryable: booleanValue$
|
|
1218
|
+
retryable: booleanValue$13(data.retryable),
|
|
1219
1219
|
...stringValue$1(data.video_asset_uid).trim() === "" ? {} : { videoAssetUid: stringValue$1(data.video_asset_uid).trim() },
|
|
1220
1220
|
...stringValue$1(data.cover_asset_uid).trim() === "" ? {} : { coverAssetUid: stringValue$1(data.cover_asset_uid).trim() },
|
|
1221
|
-
...numberValue$
|
|
1221
|
+
...numberValue$22(data.video_duration_millis) <= 0 ? {} : { videoDurationMillis: numberValue$22(data.video_duration_millis) },
|
|
1222
1222
|
...stringValue$1(data.error_code).trim() === "" ? {} : { errorCode: stringValue$1(data.error_code).trim() },
|
|
1223
1223
|
...stringValue$1(data.error_message).trim() === "" ? {} : { errorMessage: stringValue$1(data.error_message).trim() },
|
|
1224
1224
|
...stringValue$1(data.failure_stage).trim() === "" ? {} : { failureStage: stringValue$1(data.failure_stage).trim() }
|
|
@@ -1241,17 +1241,17 @@ var AiVideoService = class {
|
|
|
1241
1241
|
sessionId: stringValue$1(item.session_id).trim(),
|
|
1242
1242
|
status,
|
|
1243
1243
|
stage: stringValue$1(item.stage).trim() || status,
|
|
1244
|
-
progress: Math.min(100, Math.max(0, Math.trunc(numberValue$
|
|
1244
|
+
progress: Math.min(100, Math.max(0, Math.trunc(numberValue$22(item.progress)))),
|
|
1245
1245
|
title: stringValue$1(source.title).trim() || "长录音 AI 视频",
|
|
1246
|
-
sourceStartedAtMillis: numberValue$
|
|
1247
|
-
selectedDurationMillis: Math.max(0, numberValue$
|
|
1248
|
-
selectedSegmentCount: Math.max(0, Math.trunc(numberValue$
|
|
1249
|
-
retryable: booleanValue$
|
|
1250
|
-
createdAtMillis: numberValue$
|
|
1251
|
-
updatedAtMillis: numberValue$
|
|
1246
|
+
sourceStartedAtMillis: numberValue$22(source.started_at),
|
|
1247
|
+
selectedDurationMillis: Math.max(0, numberValue$22(source.selected_duration_millis)),
|
|
1248
|
+
selectedSegmentCount: Math.max(0, Math.trunc(numberValue$22(item.selected_segment_count))),
|
|
1249
|
+
retryable: booleanValue$13(item.retryable),
|
|
1250
|
+
createdAtMillis: numberValue$22(item.created_at),
|
|
1251
|
+
updatedAtMillis: numberValue$22(item.updated_at),
|
|
1252
1252
|
...stringValue$1(item.cover_asset_uid).trim() === "" ? {} : { coverAssetUid: stringValue$1(item.cover_asset_uid).trim() },
|
|
1253
1253
|
...stringValue$1(item.video_asset_uid).trim() === "" ? {} : { videoAssetUid: stringValue$1(item.video_asset_uid).trim() },
|
|
1254
|
-
...numberValue$
|
|
1254
|
+
...numberValue$22(item.video_duration_millis) <= 0 ? {} : { videoDurationMillis: numberValue$22(item.video_duration_millis) },
|
|
1255
1255
|
...stringValue$1(item.error_code).trim() === "" ? {} : { errorCode: stringValue$1(item.error_code).trim() },
|
|
1256
1256
|
...stringValue$1(item.error_message).trim() === "" ? {} : { errorMessage: clippedText(item.error_message, 500) }
|
|
1257
1257
|
};
|
|
@@ -1277,10 +1277,10 @@ const ARKO_RUN_STATUSES = /* @__PURE__ */ new Set([
|
|
|
1277
1277
|
"expired"
|
|
1278
1278
|
]);
|
|
1279
1279
|
const ARKO_PROFILE_DISPLAY_NAME_CACHE_TTL_MS = 10 * 6e4;
|
|
1280
|
-
function numberValue$
|
|
1280
|
+
function numberValue$21(value) {
|
|
1281
1281
|
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
1282
1282
|
}
|
|
1283
|
-
function booleanValue$
|
|
1283
|
+
function booleanValue$12(value) {
|
|
1284
1284
|
return value === true;
|
|
1285
1285
|
}
|
|
1286
1286
|
function listValue$20(value) {
|
|
@@ -1312,7 +1312,7 @@ function joinUrlWithQuery(baseUrl, path, query) {
|
|
|
1312
1312
|
}
|
|
1313
1313
|
function arkoProfileFromData(data) {
|
|
1314
1314
|
const displayName = stringValue$1(data.display_name).trim();
|
|
1315
|
-
const version = numberValue$
|
|
1315
|
+
const version = numberValue$21(data.version);
|
|
1316
1316
|
if (displayName === "" || !Number.isSafeInteger(version) || version < 0) throw new ArkmePluginError("arko-profile-contract-invalid", "Arko 资料响应不完整", true, 502);
|
|
1317
1317
|
return {
|
|
1318
1318
|
displayName,
|
|
@@ -1336,8 +1336,8 @@ function arkoModelOptionFromData(value) {
|
|
|
1336
1336
|
displayName,
|
|
1337
1337
|
provider,
|
|
1338
1338
|
description,
|
|
1339
|
-
recommended: booleanValue$
|
|
1340
|
-
selected: booleanValue$
|
|
1339
|
+
recommended: booleanValue$12(data.recommended),
|
|
1340
|
+
selected: booleanValue$12(data.selected)
|
|
1341
1341
|
};
|
|
1342
1342
|
}
|
|
1343
1343
|
function arkoModelCatalogFromData(data) {
|
|
@@ -1365,12 +1365,12 @@ function parsedJsonRecord(value) {
|
|
|
1365
1365
|
}
|
|
1366
1366
|
function arkoHistoryItemFromData(value) {
|
|
1367
1367
|
const data = objectValue$1(value);
|
|
1368
|
-
const messageId = numberValue$
|
|
1369
|
-
const sessionId = numberValue$
|
|
1370
|
-
const roleCode = numberValue$
|
|
1368
|
+
const messageId = numberValue$21(data.id ?? data.msg_id ?? data.msgId);
|
|
1369
|
+
const sessionId = numberValue$21(data.session_id ?? data.sessionId);
|
|
1370
|
+
const roleCode = numberValue$21(data.role ?? data.sender_role ?? data.senderRole);
|
|
1371
1371
|
if (!Number.isSafeInteger(messageId) || messageId <= 0 || !Number.isSafeInteger(sessionId) || sessionId <= 0 || roleCode !== 2 && roleCode !== 3) return void 0;
|
|
1372
1372
|
const extra = parsedJsonRecord(data.extra ?? data.metadata ?? data.meta);
|
|
1373
|
-
const createdAt = numberValue$
|
|
1373
|
+
const createdAt = numberValue$21(data.created_at ?? data.create_at ?? data.createdAt ?? data.createAt);
|
|
1374
1374
|
const runUid = optionalString$3(extra.agent_run_uid);
|
|
1375
1375
|
const runStatus = optionalString$3(extra.agent_run_status);
|
|
1376
1376
|
const retryable = optionalBooleanValue$1(extra.agent_run_retryable);
|
|
@@ -1383,7 +1383,7 @@ function arkoHistoryItemFromData(value) {
|
|
|
1383
1383
|
text: stringValue$1(data.content ?? data.text_content ?? data.textContent),
|
|
1384
1384
|
reasoning: stringValue$1(data.reason_content ?? data.reason_text ?? data.reasonText),
|
|
1385
1385
|
createdAtMillis: createdAt > 0 && createdAt < 1e11 ? createdAt * 1e3 : createdAt,
|
|
1386
|
-
status: numberValue$
|
|
1386
|
+
status: numberValue$21(data.status ?? data.msg_status),
|
|
1387
1387
|
...runUid === void 0 ? {} : { runUid },
|
|
1388
1388
|
...runStatus === void 0 ? {} : { runStatus },
|
|
1389
1389
|
...retryable === void 0 ? {} : { retryable },
|
|
@@ -1402,7 +1402,7 @@ function arkoRunProjectionFromData(data) {
|
|
|
1402
1402
|
return {
|
|
1403
1403
|
runUid,
|
|
1404
1404
|
status,
|
|
1405
|
-
retryable: booleanValue$
|
|
1405
|
+
retryable: booleanValue$12(data.agent_run_retryable ?? data.retryable),
|
|
1406
1406
|
...errorCode === "" ? {} : { errorCode },
|
|
1407
1407
|
...retryOfRunUid === "" ? {} : { retryOfRunUid },
|
|
1408
1408
|
...clientAction === void 0 ? {} : { clientAction }
|
|
@@ -1438,7 +1438,7 @@ var ArkoService = class {
|
|
|
1438
1438
|
async arkoEnsureSession(signal) {
|
|
1439
1439
|
const session = await this.runtime.requireSession();
|
|
1440
1440
|
const latest = await this.runtime.authenticatedIntelligentPost("/api/v1/qa/latest-session", { type: ARKO_AGENT_DIRECT_SESSION_TYPE }, session, signal);
|
|
1441
|
-
const latestSessionId = numberValue$
|
|
1441
|
+
const latestSessionId = numberValue$21(latest.id);
|
|
1442
1442
|
if (Number.isSafeInteger(latestSessionId) && latestSessionId > 0) return {
|
|
1443
1443
|
sessionId: latestSessionId,
|
|
1444
1444
|
created: false,
|
|
@@ -1448,7 +1448,7 @@ var ArkoService = class {
|
|
|
1448
1448
|
}
|
|
1449
1449
|
async arkoCreateSession(signal) {
|
|
1450
1450
|
const session = await this.runtime.requireSession();
|
|
1451
|
-
const createdSessionId = numberValue$
|
|
1451
|
+
const createdSessionId = numberValue$21((await this.runtime.authenticatedIntelligentPost("/api/v1/qa/new-session", {
|
|
1452
1452
|
name: ARKO_DEFAULT_SESSION_NAME,
|
|
1453
1453
|
type: ARKO_AGENT_DIRECT_SESSION_TYPE
|
|
1454
1454
|
}, session, signal)).session_id);
|
|
@@ -1516,9 +1516,9 @@ var ArkoService = class {
|
|
|
1516
1516
|
};
|
|
1517
1517
|
if (modelRouteKey !== "") body.model_route_key = modelRouteKey;
|
|
1518
1518
|
const accepted = await this.runtime.authenticatedIntelligentPost("/api/v1/qa/new-msg-v2", body, session, options.signal);
|
|
1519
|
-
const userMsgId = numberValue$
|
|
1520
|
-
const assistantMsgId = numberValue$
|
|
1521
|
-
const acceptedSessionId = numberValue$
|
|
1519
|
+
const userMsgId = numberValue$21(accepted.user_msg_id);
|
|
1520
|
+
const assistantMsgId = numberValue$21(accepted.assistant_msg_id);
|
|
1521
|
+
const acceptedSessionId = numberValue$21(accepted.session_id) || sessionId;
|
|
1522
1522
|
if (!Number.isSafeInteger(acceptedSessionId) || acceptedSessionId <= 0 || !Number.isSafeInteger(userMsgId) || userMsgId <= 0 || !Number.isSafeInteger(assistantMsgId) || assistantMsgId <= 0) throw new ArkmePluginError("arko-ask-contract-invalid", "Arko 消息响应不完整", true, 502);
|
|
1523
1523
|
const runUid = stringValue$1(accepted.run_uid).trim();
|
|
1524
1524
|
if (runUid === "") throw new ArkmePluginError("arko-ask-contract-invalid", "Arko 消息响应缺少运行标识", true, 502);
|
|
@@ -1554,8 +1554,8 @@ var ArkoService = class {
|
|
|
1554
1554
|
}, session, signal);
|
|
1555
1555
|
const status = stringValue$1(data.status).trim();
|
|
1556
1556
|
const normalizedRunUid = stringValue$1(data.run_uid).trim() || runUid.trim();
|
|
1557
|
-
const sequence = numberValue$
|
|
1558
|
-
const surfaceAssistantMsgId = numberValue$
|
|
1557
|
+
const sequence = numberValue$21(data.sequence);
|
|
1558
|
+
const surfaceAssistantMsgId = numberValue$21(data.surface_assistant_msg_id);
|
|
1559
1559
|
const clientAction = optionalRecord(data.client_action);
|
|
1560
1560
|
if (normalizedRunUid !== runUid.trim() || !ARKO_RUN_STATUSES.has(status) || !Number.isSafeInteger(sequence) || sequence < 0 || !Number.isSafeInteger(surfaceAssistantMsgId) || surfaceAssistantMsgId <= 0) throw new ArkmePluginError("arko-run-status-contract-invalid", "Arko 运行状态响应不完整", true, 502);
|
|
1561
1561
|
return {
|
|
@@ -1564,7 +1564,7 @@ var ArkoService = class {
|
|
|
1564
1564
|
status,
|
|
1565
1565
|
sequence,
|
|
1566
1566
|
surfaceAssistantMsgId,
|
|
1567
|
-
retryable: booleanValue$
|
|
1567
|
+
retryable: booleanValue$12(data.retryable),
|
|
1568
1568
|
...stringValue$1(data.error_code).trim() === "" ? {} : { errorCode: stringValue$1(data.error_code).trim() },
|
|
1569
1569
|
...stringValue$1(data.retry_of_run_uid).trim() === "" ? {} : { retryOfRunUid: stringValue$1(data.retry_of_run_uid).trim() },
|
|
1570
1570
|
...clientAction === void 0 ? {} : { clientAction }
|
|
@@ -1739,12 +1739,12 @@ var ArkoService = class {
|
|
|
1739
1739
|
if (tailRun !== void 0) {
|
|
1740
1740
|
run = tailRun;
|
|
1741
1741
|
status = tailRun.status;
|
|
1742
|
-
} else if (booleanValue$
|
|
1742
|
+
} else if (booleanValue$12(tail.error) || stringValue$1(tail.status).trim() === "error") status = "failed";
|
|
1743
1743
|
else status = ARKO_COMPLETED_STATUS;
|
|
1744
1744
|
createdRecordUids = arkoCreatedRecordUids(tail.created_record_uids);
|
|
1745
1745
|
const nextProfile = optionalRecord(tail.agent_profile);
|
|
1746
1746
|
if (nextProfile !== void 0) profile = arkoProfileFromData(nextProfile);
|
|
1747
|
-
if (booleanValue$
|
|
1747
|
+
if (booleanValue$12(tail.error) || stringValue$1(tail.status).trim() === "error") errorMessage = stringValue$1(tail.error_message).trim() || "Arko 执行失败";
|
|
1748
1748
|
}
|
|
1749
1749
|
const type = stringValue$1(frame.type ?? frame.event).trim();
|
|
1750
1750
|
const payload = optionalRecord(frame.payload) ?? frame;
|
|
@@ -1793,10 +1793,10 @@ const ARKME_ARRANGEMENT_REF_TTL_MILLIS = 900 * 1e3;
|
|
|
1793
1793
|
const MAX_ARKME_ARRANGEMENT_REFS = 4096;
|
|
1794
1794
|
const ARKME_ARRANGEMENT_REMINDER_REF_TTL_MILLIS = 900 * 1e3;
|
|
1795
1795
|
const MAX_ARKME_ARRANGEMENT_REMINDER_REFS = 4096;
|
|
1796
|
-
function numberValue$
|
|
1796
|
+
function numberValue$20(value) {
|
|
1797
1797
|
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
1798
1798
|
}
|
|
1799
|
-
function booleanValue$
|
|
1799
|
+
function booleanValue$11(value) {
|
|
1800
1800
|
return value === true;
|
|
1801
1801
|
}
|
|
1802
1802
|
function listValue$19(value) {
|
|
@@ -1809,7 +1809,7 @@ function arrangementStatusCode(status) {
|
|
|
1809
1809
|
return -1;
|
|
1810
1810
|
}
|
|
1811
1811
|
function arrangementStatus(value) {
|
|
1812
|
-
const code = Math.trunc(numberValue$
|
|
1812
|
+
const code = Math.trunc(numberValue$20(value));
|
|
1813
1813
|
if (code === 1) return "identified";
|
|
1814
1814
|
if (code === 2) return "following";
|
|
1815
1815
|
if (code === 3) return "completed";
|
|
@@ -1860,7 +1860,7 @@ var ArrangementService = class {
|
|
|
1860
1860
|
}, session, options.signal, { lane: "interactive-read" });
|
|
1861
1861
|
const rawItems = listValue$19(data.list);
|
|
1862
1862
|
const items = await Promise.all(rawItems.map(async (raw) => await this.arrangementItem(raw, session.userId)));
|
|
1863
|
-
const total = Math.max(0, Math.trunc(numberValue$
|
|
1863
|
+
const total = Math.max(0, Math.trunc(numberValue$20(data.total)));
|
|
1864
1864
|
const nextOffset = offset + rawItems.length;
|
|
1865
1865
|
const hasMore = rawItems.length > 0 && nextOffset < total;
|
|
1866
1866
|
return {
|
|
@@ -1886,7 +1886,7 @@ var ArrangementService = class {
|
|
|
1886
1886
|
}, session, options.signal, { lane: "interactive-read" });
|
|
1887
1887
|
const rawItems = listValue$19(data.list);
|
|
1888
1888
|
const items = await Promise.all(rawItems.map(async (raw) => await this.arrangementReminderEvent(raw, session.userId)));
|
|
1889
|
-
const total = Math.max(0, Math.trunc(numberValue$
|
|
1889
|
+
const total = Math.max(0, Math.trunc(numberValue$20(data.total)));
|
|
1890
1890
|
const nextOffset = offset + rawItems.length;
|
|
1891
1891
|
const hasMore = rawItems.length > 0 && nextOffset < total;
|
|
1892
1892
|
return {
|
|
@@ -1909,7 +1909,7 @@ var ArrangementService = class {
|
|
|
1909
1909
|
project(data.next_reminder)
|
|
1910
1910
|
]);
|
|
1911
1911
|
return {
|
|
1912
|
-
unreadCount: Math.max(0, Math.trunc(numberValue$
|
|
1912
|
+
unreadCount: Math.max(0, Math.trunc(numberValue$20(data.unread_count))),
|
|
1913
1913
|
...latestUnread === void 0 ? {} : { latestUnread },
|
|
1914
1914
|
...latestEvent === void 0 ? {} : { latestEvent },
|
|
1915
1915
|
...nextReminder === void 0 ? {} : { nextReminder }
|
|
@@ -2013,7 +2013,7 @@ var ArrangementService = class {
|
|
|
2013
2013
|
}, session, signal);
|
|
2014
2014
|
return {
|
|
2015
2015
|
outcome: "confirmed",
|
|
2016
|
-
updatedCount: Math.max(0, Math.trunc(numberValue$
|
|
2016
|
+
updatedCount: Math.max(0, Math.trunc(numberValue$20(data.updated_count)))
|
|
2017
2017
|
};
|
|
2018
2018
|
} catch (error) {
|
|
2019
2019
|
if (!isAmbiguousArrangementWriteError(error)) throw error;
|
|
@@ -2031,7 +2031,7 @@ var ArrangementService = class {
|
|
|
2031
2031
|
}, session, signal);
|
|
2032
2032
|
return {
|
|
2033
2033
|
outcome: "confirmed",
|
|
2034
|
-
updatedCount: Math.max(0, Math.trunc(numberValue$
|
|
2034
|
+
updatedCount: Math.max(0, Math.trunc(numberValue$20(data.updated_count)))
|
|
2035
2035
|
};
|
|
2036
2036
|
} catch (error) {
|
|
2037
2037
|
if (!isAmbiguousArrangementWriteError(error)) throw error;
|
|
@@ -2046,7 +2046,7 @@ var ArrangementService = class {
|
|
|
2046
2046
|
const data = await this.runtime.authenticatedIntelligentPost("/api/v1/arrangements/reminders/clear", {}, session, signal);
|
|
2047
2047
|
return {
|
|
2048
2048
|
outcome: "confirmed",
|
|
2049
|
-
updatedCount: Math.max(0, Math.trunc(numberValue$
|
|
2049
|
+
updatedCount: Math.max(0, Math.trunc(numberValue$20(data.updated_count)))
|
|
2050
2050
|
};
|
|
2051
2051
|
} catch (error) {
|
|
2052
2052
|
if (!isAmbiguousArrangementWriteError(error)) throw error;
|
|
@@ -2110,17 +2110,17 @@ var ArrangementService = class {
|
|
|
2110
2110
|
const item = objectValue$1(raw);
|
|
2111
2111
|
const arrangementUid = stringValue$1(item.uid).trim();
|
|
2112
2112
|
if (arrangementUid === "") throw new ArkmePluginError("arrangement-contract-invalid", "安排响应缺少业务身份", true, 502);
|
|
2113
|
-
const dueAtMillis = Math.trunc(numberValue$
|
|
2114
|
-
const remindAtMillis = Math.trunc(numberValue$
|
|
2113
|
+
const dueAtMillis = Math.trunc(numberValue$20(item.due_at));
|
|
2114
|
+
const remindAtMillis = Math.trunc(numberValue$20(item.remind_at));
|
|
2115
2115
|
return {
|
|
2116
2116
|
arrangementRef: await this.arrangementRef(viewerUserId, arrangementUid),
|
|
2117
2117
|
title: stringValue$1(item.title),
|
|
2118
2118
|
description: stringValue$1(item.description),
|
|
2119
2119
|
status: arrangementStatus(item.status),
|
|
2120
|
-
reminderEnabled: booleanValue$
|
|
2120
|
+
reminderEnabled: booleanValue$11(item.reminder_enabled),
|
|
2121
2121
|
reminderState: stringValue$1(item.reminder_state),
|
|
2122
|
-
createdAtMillis: Math.trunc(numberValue$
|
|
2123
|
-
updatedAtMillis: Math.trunc(numberValue$
|
|
2122
|
+
createdAtMillis: Math.trunc(numberValue$20(item.create_at)),
|
|
2123
|
+
updatedAtMillis: Math.trunc(numberValue$20(item.update_at)),
|
|
2124
2124
|
...dueAtMillis > 0 ? { dueAtMillis } : {},
|
|
2125
2125
|
...remindAtMillis > 0 ? { remindAtMillis } : {}
|
|
2126
2126
|
};
|
|
@@ -2141,20 +2141,20 @@ var ArrangementService = class {
|
|
|
2141
2141
|
const eventUid = stringValue$1(item.uid).trim();
|
|
2142
2142
|
const arrangementUid = stringValue$1(item.arrangement_uid).trim();
|
|
2143
2143
|
if (eventUid === "" || arrangementUid === "") throw new ArkmePluginError("arrangement-reminder-contract-invalid", "安排提醒响应缺少有效身份", true, 502);
|
|
2144
|
-
const dueAtMillis = Math.trunc(numberValue$
|
|
2145
|
-
const remindAtMillis = Math.trunc(numberValue$
|
|
2146
|
-
const readAtMillis = Math.trunc(numberValue$
|
|
2144
|
+
const dueAtMillis = Math.trunc(numberValue$20(item.due_at));
|
|
2145
|
+
const remindAtMillis = Math.trunc(numberValue$20(item.remind_at));
|
|
2146
|
+
const readAtMillis = Math.trunc(numberValue$20(item.read_at));
|
|
2147
2147
|
return {
|
|
2148
2148
|
eventRef: await this.arrangementReminderRef(viewerUserId, eventUid),
|
|
2149
2149
|
arrangementRef: await this.arrangementRef(viewerUserId, arrangementUid),
|
|
2150
2150
|
title: stringValue$1(item.title),
|
|
2151
2151
|
description: stringValue$1(item.description),
|
|
2152
2152
|
eventKind: stringValue$1(item.event_kind),
|
|
2153
|
-
eventAtMillis: Math.trunc(numberValue$
|
|
2153
|
+
eventAtMillis: Math.trunc(numberValue$20(item.event_at)),
|
|
2154
2154
|
read: readAtMillis > 0,
|
|
2155
2155
|
reminderState: stringValue$1(item.reminder_state),
|
|
2156
|
-
createdAtMillis: Math.trunc(numberValue$
|
|
2157
|
-
updatedAtMillis: Math.trunc(numberValue$
|
|
2156
|
+
createdAtMillis: Math.trunc(numberValue$20(item.create_at)),
|
|
2157
|
+
updatedAtMillis: Math.trunc(numberValue$20(item.update_at)),
|
|
2158
2158
|
...dueAtMillis > 0 ? { dueAtMillis } : {},
|
|
2159
2159
|
...remindAtMillis > 0 ? { remindAtMillis } : {},
|
|
2160
2160
|
...readAtMillis > 0 ? { readAtMillis } : {}
|
|
@@ -2211,7 +2211,7 @@ var ArrangementService = class {
|
|
|
2211
2211
|
const ARKME_PHONE_BIND_SUCCESS = 1;
|
|
2212
2212
|
const ARKME_PHONE_BIND_REPEAT = 2;
|
|
2213
2213
|
const ARKME_PHONE_BIND_CODE_ERR = 3;
|
|
2214
|
-
function numberValue$
|
|
2214
|
+
function numberValue$19(value) {
|
|
2215
2215
|
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
2216
2216
|
}
|
|
2217
2217
|
var AuthService = class {
|
|
@@ -2276,7 +2276,7 @@ var AuthService = class {
|
|
|
2276
2276
|
const data = await this.runtime.post(this.runtime.config.authBaseUrl, "/api/public/v1/auth/wechat-login-qrcode", {}, void 0, [200]);
|
|
2277
2277
|
const sceneStr = stringValue$1(data.scene_str).trim();
|
|
2278
2278
|
const qrContent = stringValue$1(data.url).trim();
|
|
2279
|
-
const expireSeconds = Math.max(30, numberValue$
|
|
2279
|
+
const expireSeconds = Math.max(30, numberValue$19(data.expire_seconds) || 300);
|
|
2280
2280
|
if (qrContent === "" && sceneStr !== "") throw new ArkmePluginError("wechat-qr-unavailable", "测试环境当前未返回可用微信二维码,请使用手机号登录", true, 503);
|
|
2281
2281
|
if (sceneStr === "" || qrContent === "") throw new ArkmePluginError("login-contract-invalid", "Arkme 登录二维码响应不完整", true, 502);
|
|
2282
2282
|
const attemptId = crypto.randomUUID();
|
|
@@ -2312,7 +2312,7 @@ var AuthService = class {
|
|
|
2312
2312
|
ref: 0,
|
|
2313
2313
|
keep_cancel: true
|
|
2314
2314
|
}, void 0, [200]);
|
|
2315
|
-
const userId = numberValue$
|
|
2315
|
+
const userId = numberValue$19(data.user_id);
|
|
2316
2316
|
if (userId <= 0) return {
|
|
2317
2317
|
status: "pending",
|
|
2318
2318
|
environment: this.runtime.config.environment,
|
|
@@ -2356,7 +2356,7 @@ var AuthService = class {
|
|
|
2356
2356
|
const normalizedCaptcha = this.normalizedCaptcha(captcha);
|
|
2357
2357
|
const session = await this.runtime.sessionStore.read() ?? await this.runtime.readPendingBindingSession();
|
|
2358
2358
|
if (session !== void 0) {
|
|
2359
|
-
if (numberValue$
|
|
2359
|
+
if (numberValue$19((await this.runtime.post(this.runtime.config.authBaseUrl, "/api/v1/auth/bind-phone-send-code", {
|
|
2360
2360
|
phone: normalizedPhone,
|
|
2361
2361
|
pre: "86",
|
|
2362
2362
|
is_test: this.runtime.config.environment === "test",
|
|
@@ -2378,7 +2378,7 @@ var AuthService = class {
|
|
|
2378
2378
|
if (!/^[0-9]{6}$/.test(normalizedCode)) throw new ArkmePluginError("phone-code-invalid", "请输入有效的短信验证码", false);
|
|
2379
2379
|
const session = await this.runtime.sessionStore.read() ?? await this.runtime.readPendingBindingSession();
|
|
2380
2380
|
if (session !== void 0) {
|
|
2381
|
-
const result = numberValue$
|
|
2381
|
+
const result = numberValue$19((await this.runtime.post(this.runtime.config.authBaseUrl, "/api/v1/auth/verify-bind-phone", {
|
|
2382
2382
|
phone: normalizedPhone,
|
|
2383
2383
|
pre: "86",
|
|
2384
2384
|
code: normalizedCode,
|
|
@@ -2399,7 +2399,7 @@ var AuthService = class {
|
|
|
2399
2399
|
keep_cancel: true
|
|
2400
2400
|
}, void 0, [200]);
|
|
2401
2401
|
if (data.ok === false) throw new ArkmePluginError("phone-code-rejected", "手机号或验证码错误", false, 401);
|
|
2402
|
-
const userId = numberValue$
|
|
2402
|
+
const userId = numberValue$19(data.user_id);
|
|
2403
2403
|
const accessToken = stringValue$1(data.access_token);
|
|
2404
2404
|
const refreshToken = stringValue$1(data.refresh_token);
|
|
2405
2405
|
if (userId <= 0 || accessToken === "" || refreshToken === "") throw new ArkmePluginError("login-contract-invalid", "Arkme手机号登录响应不完整", false, 502);
|
|
@@ -2454,6 +2454,13 @@ var AuthService = class {
|
|
|
2454
2454
|
}
|
|
2455
2455
|
};
|
|
2456
2456
|
//#endregion
|
|
2457
|
+
//#region src/bot-avatar-ref.ts
|
|
2458
|
+
const ARKME_BOT_AVATAR_REF_PATTERN = /^file_asset:\/\/[A-Za-z0-9_-]{8,128}$/;
|
|
2459
|
+
/** Bot avatars cross process boundaries only as account-scoped file asset references. */
|
|
2460
|
+
function isArkmeBotAvatarRef(value) {
|
|
2461
|
+
return ARKME_BOT_AVATAR_REF_PATTERN.test(value.trim());
|
|
2462
|
+
}
|
|
2463
|
+
//#endregion
|
|
2457
2464
|
//#region src/secret-value.ts
|
|
2458
2465
|
var SecretValue = class {
|
|
2459
2466
|
#value;
|
|
@@ -2466,10 +2473,10 @@ var SecretValue = class {
|
|
|
2466
2473
|
};
|
|
2467
2474
|
//#endregion
|
|
2468
2475
|
//#region src/services/bot-service.ts
|
|
2469
|
-
function numberValue$
|
|
2476
|
+
function numberValue$18(value) {
|
|
2470
2477
|
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
2471
2478
|
}
|
|
2472
|
-
function booleanValue$
|
|
2479
|
+
function booleanValue$10(value) {
|
|
2473
2480
|
return value === true;
|
|
2474
2481
|
}
|
|
2475
2482
|
function listValue$18(value) {
|
|
@@ -2528,6 +2535,7 @@ var BotService = class {
|
|
|
2528
2535
|
const avatar = input.avatar?.trim() ?? "";
|
|
2529
2536
|
if (name === "") throw new ArkmePluginError("bot-name-invalid", "Bot 名称不能为空", false);
|
|
2530
2537
|
if (provider !== "openclaw" && provider !== "webhook") throw new ArkmePluginError("bot-provider-unsupported", "Bot Provider 不受支持", false, 400);
|
|
2538
|
+
if (avatar !== "" && !isArkmeBotAvatarRef(avatar)) throw new ArkmePluginError("bot-avatar-invalid", "Bot 头像引用无效", false, 400);
|
|
2531
2539
|
const session = await this.runtime.requireSession();
|
|
2532
2540
|
let data;
|
|
2533
2541
|
try {
|
|
@@ -2552,6 +2560,10 @@ var BotService = class {
|
|
|
2552
2560
|
throw new ArkmePluginError("bot-create-outcome-unknown", "Bot 可能已创建,但响应不完整;请刷新 Bot 列表确认", false, 409, { cause: error });
|
|
2553
2561
|
}
|
|
2554
2562
|
}
|
|
2563
|
+
/** Browser-safe result: the one-time credential never leaves the Host owner. */
|
|
2564
|
+
async createBotSummary(input, options = {}) {
|
|
2565
|
+
return (await this.createBot(input, options)).bot;
|
|
2566
|
+
}
|
|
2555
2567
|
async revealBotSecret(botRef, options = {}) {
|
|
2556
2568
|
const session = await this.runtime.requireSession();
|
|
2557
2569
|
const reference = await this.openBotRef(botRef, session.userId);
|
|
@@ -2606,13 +2618,13 @@ var BotService = class {
|
|
|
2606
2618
|
const { directChatAvailable: _directChatAvailable, ...summary } = await this.botSummaryFromData(raw, session.userId);
|
|
2607
2619
|
items.push({
|
|
2608
2620
|
...summary,
|
|
2609
|
-
installed: booleanValue$
|
|
2621
|
+
installed: booleanValue$10(raw.installed)
|
|
2610
2622
|
});
|
|
2611
2623
|
}
|
|
2612
2624
|
return {
|
|
2613
2625
|
groupSourceRef,
|
|
2614
2626
|
displayName: stringValue$1(data.subject_title).trim() || group.displayName,
|
|
2615
|
-
canAddBots: booleanValue$
|
|
2627
|
+
canAddBots: booleanValue$10(data.can_current_user_add_bots),
|
|
2616
2628
|
items
|
|
2617
2629
|
};
|
|
2618
2630
|
}
|
|
@@ -2687,7 +2699,7 @@ var BotService = class {
|
|
|
2687
2699
|
}
|
|
2688
2700
|
const reference = {
|
|
2689
2701
|
version: 1,
|
|
2690
|
-
userId: numberValue$
|
|
2702
|
+
userId: numberValue$18(raw.userId),
|
|
2691
2703
|
botId: stringValue$1(raw.botId).trim()
|
|
2692
2704
|
};
|
|
2693
2705
|
if (raw.version !== 1 || reference.userId !== expectedUserId || reference.botId === "") throw new ArkmePluginError("bot-ref-invalid", "Bot 引用与当前账号不匹配", false, 403);
|
|
@@ -2698,10 +2710,10 @@ var BotService = class {
|
|
|
2698
2710
|
//#region src/services/calendar-service.ts
|
|
2699
2711
|
const MAX_CALENDAR_RANGE_DAYS = 62;
|
|
2700
2712
|
const MAX_DAY_RECORD_LIMIT = 50;
|
|
2701
|
-
function numberValue$
|
|
2713
|
+
function numberValue$17(value) {
|
|
2702
2714
|
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
2703
2715
|
}
|
|
2704
|
-
function booleanValue$
|
|
2716
|
+
function booleanValue$9(value) {
|
|
2705
2717
|
if (typeof value === "boolean") return value;
|
|
2706
2718
|
if (typeof value === "number") return value !== 0;
|
|
2707
2719
|
}
|
|
@@ -2738,7 +2750,7 @@ function boundedLimit(value) {
|
|
|
2738
2750
|
return limit;
|
|
2739
2751
|
}
|
|
2740
2752
|
function contentAccessState(value) {
|
|
2741
|
-
const code = Math.trunc(numberValue$
|
|
2753
|
+
const code = Math.trunc(numberValue$17(value));
|
|
2742
2754
|
if (code === 1) return "available";
|
|
2743
2755
|
if (code === 2) return "protected";
|
|
2744
2756
|
return "unknown";
|
|
@@ -2748,7 +2760,7 @@ function sourceKind(raw) {
|
|
|
2748
2760
|
const chat = objectValue$1(raw.chat_core);
|
|
2749
2761
|
if (stringValue$1(topic.topic_uid).trim() !== "") return "topic";
|
|
2750
2762
|
if (stringValue$1(chat.chat_session_uid).trim() !== "") return "chat";
|
|
2751
|
-
if (booleanValue$
|
|
2763
|
+
if (booleanValue$9(raw.is_uncategorized) === true) return "self";
|
|
2752
2764
|
return "unknown";
|
|
2753
2765
|
}
|
|
2754
2766
|
var CalendarService = class {
|
|
@@ -2807,7 +2819,7 @@ var CalendarService = class {
|
|
|
2807
2819
|
cacheMs: 1e4,
|
|
2808
2820
|
failureCooldownMs: 2e3
|
|
2809
2821
|
});
|
|
2810
|
-
const nextSendAt = Math.trunc(numberValue$
|
|
2822
|
+
const nextSendAt = Math.trunc(numberValue$17(data.next_cursor_send_at));
|
|
2811
2823
|
const nextUid = stringValue$1(data.next_cursor_record_uid).trim();
|
|
2812
2824
|
return {
|
|
2813
2825
|
scope: "self",
|
|
@@ -2826,20 +2838,20 @@ var CalendarService = class {
|
|
|
2826
2838
|
const item = objectValue$1(raw);
|
|
2827
2839
|
const bucketDate = stringValue$1(item.bucket_date).trim();
|
|
2828
2840
|
if (bucketDate === "") return void 0;
|
|
2829
|
-
const count = Math.max(0, Math.trunc(numberValue$
|
|
2841
|
+
const count = Math.max(0, Math.trunc(numberValue$17(item.count)));
|
|
2830
2842
|
return {
|
|
2831
2843
|
bucketDate,
|
|
2832
2844
|
count,
|
|
2833
|
-
protectedCount: Math.max(0, Math.trunc(numberValue$
|
|
2845
|
+
protectedCount: Math.max(0, Math.trunc(numberValue$17(item.protected_count))),
|
|
2834
2846
|
hasRecords: count > 0,
|
|
2835
|
-
...numberValue$
|
|
2847
|
+
...numberValue$17(item.first_send_at) > 0 ? { firstSendAtMillis: Math.trunc(numberValue$17(item.first_send_at)) } : {}
|
|
2836
2848
|
};
|
|
2837
2849
|
}
|
|
2838
2850
|
dayRecord(raw) {
|
|
2839
2851
|
const item = objectValue$1(raw);
|
|
2840
2852
|
const core = objectValue$1(item.record_core);
|
|
2841
2853
|
const recordUid = stringValue$1(item.record_uid ?? core.record_uid).trim();
|
|
2842
|
-
const sendAtMillis = Math.trunc(numberValue$
|
|
2854
|
+
const sendAtMillis = Math.trunc(numberValue$17(item.send_at ?? core.send_at));
|
|
2843
2855
|
if (recordUid === "" || sendAtMillis <= 0) return void 0;
|
|
2844
2856
|
const accessState = contentAccessState(core.content_access_state);
|
|
2845
2857
|
const available = accessState === "available";
|
|
@@ -2847,9 +2859,9 @@ var CalendarService = class {
|
|
|
2847
2859
|
const textContent = available ? clippedText(core.text_content, 4e3) : "";
|
|
2848
2860
|
const preview = title || clippedText(textContent, 160) || (available ? "无文字内容" : "受保护内容");
|
|
2849
2861
|
const topic = objectValue$1(item.topic_core);
|
|
2850
|
-
const isUncategorized = booleanValue$
|
|
2851
|
-
const hasManualEdit = booleanValue$
|
|
2852
|
-
const hasPolish = booleanValue$
|
|
2862
|
+
const isUncategorized = booleanValue$9(item.is_uncategorized);
|
|
2863
|
+
const hasManualEdit = booleanValue$9(core.has_manual_edit);
|
|
2864
|
+
const hasPolish = booleanValue$9(core.has_polish);
|
|
2853
2865
|
return {
|
|
2854
2866
|
recordUid,
|
|
2855
2867
|
sendAtMillis,
|
|
@@ -2859,9 +2871,9 @@ var CalendarService = class {
|
|
|
2859
2871
|
preview,
|
|
2860
2872
|
...stringValue$1(topic.title).trim() === "" ? {} : { topicTitle: stringValue$1(topic.title).trim() },
|
|
2861
2873
|
sourceKind: sourceKind(item),
|
|
2862
|
-
creationSource: Math.trunc(numberValue$
|
|
2863
|
-
templateKind: Math.trunc(numberValue$
|
|
2864
|
-
displayKind: Math.trunc(numberValue$
|
|
2874
|
+
creationSource: Math.trunc(numberValue$17(core.creation_source)),
|
|
2875
|
+
templateKind: Math.trunc(numberValue$17(core.template_kind)),
|
|
2876
|
+
displayKind: Math.trunc(numberValue$17(core.display_kind)),
|
|
2865
2877
|
protected: accessState === "protected",
|
|
2866
2878
|
...isUncategorized === void 0 ? {} : { isUncategorized },
|
|
2867
2879
|
...hasManualEdit === void 0 ? {} : { hasManualEdit },
|
|
@@ -3556,10 +3568,10 @@ var ChatRealtimeService = class {
|
|
|
3556
3568
|
};
|
|
3557
3569
|
//#endregion
|
|
3558
3570
|
//#region src/services/chat-service.ts
|
|
3559
|
-
function numberValue$
|
|
3571
|
+
function numberValue$16(value) {
|
|
3560
3572
|
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
3561
3573
|
}
|
|
3562
|
-
function booleanValue$
|
|
3574
|
+
function booleanValue$8(value) {
|
|
3563
3575
|
return value === true;
|
|
3564
3576
|
}
|
|
3565
3577
|
function listValue$15(value) {
|
|
@@ -3658,15 +3670,15 @@ var ChatService = class {
|
|
|
3658
3670
|
const uid = stringValue$1(chatSession.chat_session_uid).trim();
|
|
3659
3671
|
if (uid === "") throw new ArkmePluginError("private-chat-contract-invalid", "私聊会话响应不完整", true, 502);
|
|
3660
3672
|
const unread = objectValue$1(data.unread_snapshot);
|
|
3661
|
-
const latestSequence = numberValue$
|
|
3673
|
+
const latestSequence = numberValue$16(unread.session_last_seq ?? chatSession.last_seq);
|
|
3662
3674
|
const source = {
|
|
3663
3675
|
sourceRef: await this.source.sealSourceRef(session.userId, "private_chat", uid, displayName),
|
|
3664
3676
|
sourceKey: await this.source.chatDirectorySourceKey(session.userId, uid),
|
|
3665
3677
|
kind: "private_chat",
|
|
3666
3678
|
displayName,
|
|
3667
3679
|
...profile?.avatarUrl === void 0 ? {} : { avatarRef: await this.profile.sealProfileImageRef(session.userId, peerUserId) },
|
|
3668
|
-
activeAtMillis: numberValue$
|
|
3669
|
-
unreadCount: numberValue$
|
|
3680
|
+
activeAtMillis: numberValue$16(chatSession.last_active_at) || Date.now(),
|
|
3681
|
+
unreadCount: numberValue$16(unread.unread_count),
|
|
3670
3682
|
...latestSequence > 0 ? { latestSequence } : {}
|
|
3671
3683
|
};
|
|
3672
3684
|
this.source.setChatSourceByKey(`${String(session.userId)}:${uid}`, source);
|
|
@@ -3693,7 +3705,7 @@ var ChatService = class {
|
|
|
3693
3705
|
mediaUnavailable: media.unavailableRecordUids.has(recordUid)
|
|
3694
3706
|
});
|
|
3695
3707
|
}).filter((item) => item.itemUid !== "");
|
|
3696
|
-
const nextSendAt = numberValue$
|
|
3708
|
+
const nextSendAt = numberValue$16(data.next_cursor_send_at);
|
|
3697
3709
|
const nextUid = stringValue$1(data.next_cursor_record_uid).trim();
|
|
3698
3710
|
return {
|
|
3699
3711
|
source: await this.source.sourceItem(source),
|
|
@@ -3737,7 +3749,7 @@ var ChatService = class {
|
|
|
3737
3749
|
mediaUnavailable: media.unavailableRecordUids.has(recordUid)
|
|
3738
3750
|
});
|
|
3739
3751
|
});
|
|
3740
|
-
const nextSendAt = numberValue$
|
|
3752
|
+
const nextSendAt = numberValue$16(data.next_cursor_send_at);
|
|
3741
3753
|
const nextUid = stringValue$1(data.next_cursor_record_uid).trim();
|
|
3742
3754
|
return {
|
|
3743
3755
|
source: await this.source.sourceItem(source),
|
|
@@ -3767,14 +3779,14 @@ var ChatService = class {
|
|
|
3767
3779
|
const relation = objectValue$1(item.relation);
|
|
3768
3780
|
const record = objectValue$1(item.record);
|
|
3769
3781
|
const payload = objectValue$1(record.payload);
|
|
3770
|
-
const recordStatus = numberValue$
|
|
3782
|
+
const recordStatus = numberValue$16(record.status);
|
|
3771
3783
|
if (recordStatus !== 1) continue;
|
|
3772
3784
|
const uid = stringValue$1(relation.record_uid ?? payload.record_uid).trim();
|
|
3773
3785
|
if (uid === "") continue;
|
|
3774
3786
|
const relationUid = stringValue$1(relation.rel_uid).trim();
|
|
3775
|
-
const senderUserId = numberValue$
|
|
3787
|
+
const senderUserId = numberValue$16(relation.sender_user_id);
|
|
3776
3788
|
const aiPolish = this.aiPolish.timelineAiPolish(record, payload);
|
|
3777
|
-
const sendAtMillis = numberValue$
|
|
3789
|
+
const sendAtMillis = numberValue$16(relation.attach_at ?? payload.send_at);
|
|
3778
3790
|
const forwardRecords = await this.chatForwardRecordsPreview(item, session.userId, sendAtMillis);
|
|
3779
3791
|
const rawAgentSource = timelineAgentSource(relation, record, payload);
|
|
3780
3792
|
const agentSource = senderUserId === session.userId ? this.arko.currentUserAgentSourceFallback(session.userId, rawAgentSource) : rawAgentSource;
|
|
@@ -3788,16 +3800,16 @@ var ChatService = class {
|
|
|
3788
3800
|
title: stringValue$1(payload.title),
|
|
3789
3801
|
textContent: stringValue$1(payload.text_content),
|
|
3790
3802
|
status: recordStatus,
|
|
3791
|
-
sequence: numberValue$
|
|
3792
|
-
...numberValue$
|
|
3803
|
+
sequence: numberValue$16(relation.seq),
|
|
3804
|
+
...numberValue$16(record.version ?? payload.version) > 0 ? { recordVersion: numberValue$16(record.version ?? payload.version) } : {},
|
|
3793
3805
|
...aiPolish === void 0 ? {} : { aiPolish },
|
|
3794
3806
|
...forwardRecords === void 0 ? {} : { forwardRecords },
|
|
3795
|
-
templateKind: numberValue$
|
|
3796
|
-
displayKind: numberValue$
|
|
3797
|
-
version: numberValue$
|
|
3798
|
-
updateAtMillis: numberValue$
|
|
3799
|
-
recordDurationMillis: numberValue$
|
|
3800
|
-
editDurationMillis: numberValue$
|
|
3807
|
+
templateKind: numberValue$16(payload.template_kind),
|
|
3808
|
+
displayKind: numberValue$16(payload.display_kind),
|
|
3809
|
+
version: numberValue$16(payload.version ?? record.version),
|
|
3810
|
+
updateAtMillis: numberValue$16(payload.update_at ?? record.update_at),
|
|
3811
|
+
recordDurationMillis: numberValue$16(payload.record_duration_millis),
|
|
3812
|
+
editDurationMillis: numberValue$16(payload.edit_duration_millis),
|
|
3801
3813
|
contentBlocks: this.media.richContentBlocks(item, session.userId)
|
|
3802
3814
|
}) - 1;
|
|
3803
3815
|
if (Number.isSafeInteger(senderUserId) && senderUserId > 0) senderUserIdByIndex.set(itemIndex, senderUserId);
|
|
@@ -3809,7 +3821,7 @@ var ChatService = class {
|
|
|
3809
3821
|
items[index].avatarRef = await this.profile.sealProfileImageRef(session.userId, senderUserId);
|
|
3810
3822
|
}
|
|
3811
3823
|
} catch {}
|
|
3812
|
-
const beforeSequence = numberValue$
|
|
3824
|
+
const beforeSequence = numberValue$16(data.next_before_seq);
|
|
3813
3825
|
let aiPolishSettings;
|
|
3814
3826
|
let aiPolishNotices;
|
|
3815
3827
|
if (aiPolishDecorations !== void 0) {
|
|
@@ -3848,7 +3860,7 @@ var ChatService = class {
|
|
|
3848
3860
|
return {
|
|
3849
3861
|
messageRef,
|
|
3850
3862
|
reportUid,
|
|
3851
|
-
status: numberValue$
|
|
3863
|
+
status: numberValue$16(report.status)
|
|
3852
3864
|
};
|
|
3853
3865
|
}
|
|
3854
3866
|
async sendSourceText(sourceRef, textContent, options = {}) {
|
|
@@ -3879,7 +3891,7 @@ var ChatService = class {
|
|
|
3879
3891
|
return {
|
|
3880
3892
|
sourceRef,
|
|
3881
3893
|
itemUid: stringValue$1(result.record_uid).trim() || recordUid,
|
|
3882
|
-
status: numberValue$
|
|
3894
|
+
status: numberValue$16(result.status),
|
|
3883
3895
|
localState: "synced"
|
|
3884
3896
|
};
|
|
3885
3897
|
}
|
|
@@ -3912,7 +3924,7 @@ var ChatService = class {
|
|
|
3912
3924
|
const raw = objectValue$1(value);
|
|
3913
3925
|
const botId = stringValue$1(raw.bot_id).trim();
|
|
3914
3926
|
if (!requestedById.has(botId)) continue;
|
|
3915
|
-
if (stringValue$1(raw.provider).trim() !== "openclaw" || !booleanValue$
|
|
3927
|
+
if (stringValue$1(raw.provider).trim() !== "openclaw" || !booleanValue$8(raw.installed)) throw new ArkmePluginError("bot-mention-not-installed", "所选 Bot 未安装到该群聊", false, 409);
|
|
3916
3928
|
const name = stringValue$1(raw.name).trim();
|
|
3917
3929
|
if (name === "") throw new ArkmePluginError("bot-contract-invalid", "Bot 响应不完整", true, 502);
|
|
3918
3930
|
const display = `@${name}`;
|
|
@@ -3964,8 +3976,8 @@ var ChatService = class {
|
|
|
3964
3976
|
return {
|
|
3965
3977
|
sourceRef,
|
|
3966
3978
|
itemUid: stringValue$1(result.record_uid).trim() || recordUid,
|
|
3967
|
-
status: numberValue$
|
|
3968
|
-
sequence: numberValue$
|
|
3979
|
+
status: numberValue$16(result.audit_status),
|
|
3980
|
+
sequence: numberValue$16(result.seq),
|
|
3969
3981
|
localState: "synced"
|
|
3970
3982
|
};
|
|
3971
3983
|
}
|
|
@@ -4021,7 +4033,7 @@ var ChatService = class {
|
|
|
4021
4033
|
return {
|
|
4022
4034
|
sourceRef,
|
|
4023
4035
|
itemUid: stringValue$1(result.record_uid).trim() || recordUid,
|
|
4024
|
-
status: numberValue$
|
|
4036
|
+
status: numberValue$16(result.status),
|
|
4025
4037
|
localState: "synced"
|
|
4026
4038
|
};
|
|
4027
4039
|
}
|
|
@@ -4033,7 +4045,7 @@ var ChatService = class {
|
|
|
4033
4045
|
return {
|
|
4034
4046
|
sourceRef,
|
|
4035
4047
|
itemUid: stringValue$1(result.record_uid).trim() || recordUid,
|
|
4036
|
-
status: numberValue$
|
|
4048
|
+
status: numberValue$16(result.status),
|
|
4037
4049
|
localState: "synced"
|
|
4038
4050
|
};
|
|
4039
4051
|
}
|
|
@@ -4045,8 +4057,8 @@ var ChatService = class {
|
|
|
4045
4057
|
return {
|
|
4046
4058
|
sourceRef,
|
|
4047
4059
|
itemUid: stringValue$1(result.record_uid).trim() || recordUid,
|
|
4048
|
-
status: numberValue$
|
|
4049
|
-
sequence: numberValue$
|
|
4060
|
+
status: numberValue$16(result.audit_status ?? result.status),
|
|
4061
|
+
sequence: numberValue$16(result.seq),
|
|
4050
4062
|
localState: "synced"
|
|
4051
4063
|
};
|
|
4052
4064
|
}
|
|
@@ -4096,7 +4108,7 @@ var ChatService = class {
|
|
|
4096
4108
|
send_at: sendAtMillis
|
|
4097
4109
|
}, session, options.signal);
|
|
4098
4110
|
const chatSessionUid = stringValue$1(result.chat_session_uid).trim();
|
|
4099
|
-
const sequence = numberValue$
|
|
4111
|
+
const sequence = numberValue$16(result.seq);
|
|
4100
4112
|
const targetKind = stringValue$1(result.target_kind).trim();
|
|
4101
4113
|
if (chatSessionUid === "" || !Number.isSafeInteger(sequence) || sequence <= 0 || targetKind !== "direct") throw new ArkmePluginError("direct-send-response-invalid", "Chat Agent 发送返回了无效响应", true, 502);
|
|
4102
4114
|
const responseRecordUid = stringValue$1(result.record_uid).trim() || recordUid;
|
|
@@ -4124,10 +4136,10 @@ var ChatService = class {
|
|
|
4124
4136
|
reason: "arkme_dsh_open_chat"
|
|
4125
4137
|
}, session);
|
|
4126
4138
|
const responseSessionUid = stringValue$1(data.chat_session_uid).trim();
|
|
4127
|
-
const effectiveReadSequence = numberValue$
|
|
4128
|
-
const readAt = numberValue$
|
|
4129
|
-
const sessionLastSequence = numberValue$
|
|
4130
|
-
const unreadCount = numberValue$
|
|
4139
|
+
const effectiveReadSequence = numberValue$16(data.effective_read_seq);
|
|
4140
|
+
const readAt = numberValue$16(data.read_at);
|
|
4141
|
+
const sessionLastSequence = numberValue$16(data.session_last_seq);
|
|
4142
|
+
const unreadCount = numberValue$16(data.unread_count);
|
|
4131
4143
|
if (responseSessionUid !== source.ownerRef || !Number.isSafeInteger(effectiveReadSequence) || effectiveReadSequence < readSequence || readAt <= 0 || sessionLastSequence < effectiveReadSequence || !Number.isSafeInteger(unreadCount) || unreadCount < 0) throw new ArkmePluginError("source-read-ack-invalid", "聊天已读响应不完整", true, 502);
|
|
4132
4144
|
const cacheKey = `${String(session.userId)}:${source.ownerRef}`;
|
|
4133
4145
|
const cached = this.source.cachedChatSourceByKey(cacheKey);
|
|
@@ -4162,9 +4174,9 @@ var ChatService = class {
|
|
|
4162
4174
|
const payload = objectValue$1(record.payload);
|
|
4163
4175
|
const uid = stringValue$1(relation.record_uid ?? payload.record_uid).trim();
|
|
4164
4176
|
if (uid === "") continue;
|
|
4165
|
-
const senderUserId = numberValue$
|
|
4177
|
+
const senderUserId = numberValue$16(relation.sender_user_id);
|
|
4166
4178
|
const aiPolish = this.aiPolish.timelineAiPolish(record, payload);
|
|
4167
|
-
const sendAtMillis = numberValue$
|
|
4179
|
+
const sendAtMillis = numberValue$16(relation.attach_at ?? payload.send_at);
|
|
4168
4180
|
const forwardRecords = await this.chatForwardRecordsPreview(item, session.userId, sendAtMillis);
|
|
4169
4181
|
const rawAgentSource = timelineAgentSource(relation, record, payload);
|
|
4170
4182
|
const agentSource = senderUserId === session.userId ? this.arko.currentUserAgentSourceFallback(session.userId, rawAgentSource) : rawAgentSource;
|
|
@@ -4177,12 +4189,12 @@ var ChatService = class {
|
|
|
4177
4189
|
sendAtMillis,
|
|
4178
4190
|
title: stringValue$1(payload.title),
|
|
4179
4191
|
textContent: stringValue$1(payload.text_content),
|
|
4180
|
-
status: numberValue$
|
|
4181
|
-
sequence: numberValue$
|
|
4182
|
-
...numberValue$
|
|
4192
|
+
status: numberValue$16(record.status),
|
|
4193
|
+
sequence: numberValue$16(relation.seq),
|
|
4194
|
+
...numberValue$16(record.version ?? payload.version) > 0 ? { recordVersion: numberValue$16(record.version ?? payload.version) } : {},
|
|
4183
4195
|
...aiPolish === void 0 ? {} : { aiPolish },
|
|
4184
4196
|
...forwardRecords === void 0 ? {} : { forwardRecords },
|
|
4185
|
-
displayKind: numberValue$
|
|
4197
|
+
displayKind: numberValue$16(payload.display_kind),
|
|
4186
4198
|
contentBlocks: this.media.richContentBlocks(item, session.userId)
|
|
4187
4199
|
});
|
|
4188
4200
|
}
|
|
@@ -4198,7 +4210,7 @@ var ChatService = class {
|
|
|
4198
4210
|
const sorted = values.map((value, index) => ({
|
|
4199
4211
|
value,
|
|
4200
4212
|
index,
|
|
4201
|
-
order: numberValue$
|
|
4213
|
+
order: numberValue$16(objectValue$1(value).item_order)
|
|
4202
4214
|
})).sort((left, right) => (left.order || left.index) - (right.order || right.index));
|
|
4203
4215
|
for (const entry of sorted) {
|
|
4204
4216
|
if (projectedItems.length >= 100) return;
|
|
@@ -4208,19 +4220,19 @@ var ChatService = class {
|
|
|
4208
4220
|
await appendItems(listValue$15(nestedForward.items), depth + 1);
|
|
4209
4221
|
continue;
|
|
4210
4222
|
}
|
|
4211
|
-
const senderUserId = numberValue$
|
|
4223
|
+
const senderUserId = numberValue$16(item.source_sender_user_id ?? item.sourceSenderUserId ?? item.owner_id ?? item.ownerId);
|
|
4212
4224
|
const senderName = stringValue$1(item.owner_name ?? item.ownerName ?? item.source_display_name ?? item.sourceDisplayName).trim() || "Arkme用户";
|
|
4213
4225
|
const textContent = stringValue$1(item.text ?? item.text_preview ?? item.textPreview).trim();
|
|
4214
4226
|
const title = stringValue$1(item.title).trim();
|
|
4215
|
-
const imageCount = Math.max(0, Math.trunc(numberValue$
|
|
4216
|
-
const voiceCount = Math.max(0, Math.trunc(numberValue$
|
|
4217
|
-
const fileCount = Math.max(0, Math.trunc(numberValue$
|
|
4227
|
+
const imageCount = Math.max(0, Math.trunc(numberValue$16(item.image_count ?? item.imageCount)));
|
|
4228
|
+
const voiceCount = Math.max(0, Math.trunc(numberValue$16(item.voice_count ?? item.voiceCount)));
|
|
4229
|
+
const fileCount = Math.max(0, Math.trunc(numberValue$16(item.file_count ?? item.fileCount)));
|
|
4218
4230
|
const fileName = listValue$15(item.file_names ?? item.fileNames).map((value) => stringValue$1(value).trim()).find((value) => value !== "");
|
|
4219
4231
|
const contentLabel = imageCount > 0 ? imageCount > 1 ? `[${String(imageCount)}张图片]` : "[图片]" : voiceCount > 0 ? "[语音]" : fileCount > 0 ? fileName === void 0 ? "[文件]" : `[文件] ${fileName}` : stringValue$1(item.availability).trim() !== "" ? "原快记暂不可查看" : void 0;
|
|
4220
4232
|
projectedItems.push({
|
|
4221
4233
|
senderName,
|
|
4222
4234
|
...senderUserId > 0 ? { avatarRef: await this.profile.sealProfileImageRef(viewerUserId, senderUserId) } : {},
|
|
4223
|
-
sendAtMillis: numberValue$
|
|
4235
|
+
sendAtMillis: numberValue$16(item.send_at ?? item.sendAt),
|
|
4224
4236
|
title,
|
|
4225
4237
|
textContent,
|
|
4226
4238
|
...contentLabel === void 0 ? {} : { contentLabel }
|
|
@@ -4229,7 +4241,7 @@ var ChatService = class {
|
|
|
4229
4241
|
};
|
|
4230
4242
|
await appendItems(listValue$15(payload.items), 0);
|
|
4231
4243
|
const summaryLines = listValue$15(payload.summary_lines ?? payload.summaryLines).map((value) => stringValue$1(value).trim()).filter((value) => value !== "");
|
|
4232
|
-
const createdAtMillis = numberValue$
|
|
4244
|
+
const createdAtMillis = numberValue$16(payload.created_at ?? payload.createdAt) || fallbackCreatedAtMillis;
|
|
4233
4245
|
return {
|
|
4234
4246
|
title: stringValue$1(payload.title).trim() || "转发快记",
|
|
4235
4247
|
createdAtMillis,
|
|
@@ -4261,7 +4273,7 @@ var ChatService = class {
|
|
|
4261
4273
|
}
|
|
4262
4274
|
const result = {
|
|
4263
4275
|
version: 1,
|
|
4264
|
-
userId: numberValue$
|
|
4276
|
+
userId: numberValue$16(parsed.userId),
|
|
4265
4277
|
chatSessionUid: stringValue$1(parsed.chatSessionUid).trim(),
|
|
4266
4278
|
relationUid: stringValue$1(parsed.relationUid).trim()
|
|
4267
4279
|
};
|
|
@@ -4270,6 +4282,167 @@ var ChatService = class {
|
|
|
4270
4282
|
}
|
|
4271
4283
|
};
|
|
4272
4284
|
//#endregion
|
|
4285
|
+
//#region src/services/contact-service.ts
|
|
4286
|
+
const CONTACT_REF_TTL_MS = 10 * 6e4;
|
|
4287
|
+
const MAX_CONTACT_REFS = 512;
|
|
4288
|
+
const CONTACT_REF_PATTERN = /^arkme-contact-v1\.[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
4289
|
+
function numberValue$15(value) {
|
|
4290
|
+
const parsed = typeof value === "number" ? value : typeof value === "string" ? Number(value) : NaN;
|
|
4291
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
4292
|
+
}
|
|
4293
|
+
function booleanValue$7(value) {
|
|
4294
|
+
return value === true;
|
|
4295
|
+
}
|
|
4296
|
+
function normalizeIdentifier(value) {
|
|
4297
|
+
const trimmed = value.trim().replace(/^@/, "");
|
|
4298
|
+
if (trimmed === "" || trimmed.length > 64) throw new ArkmePluginError("contact-identifier-invalid", "请输入手机号或即我号", false);
|
|
4299
|
+
const compactPhone = trimmed.replace(/[\s()-]/g, "").replace(/^\+86/, "");
|
|
4300
|
+
if (/^\d+$/.test(compactPhone)) {
|
|
4301
|
+
if (!/^1[3-9]\d{9}$/.test(compactPhone)) throw new ArkmePluginError("contact-phone-invalid", "请输入有效的中国大陆手机号", false);
|
|
4302
|
+
return {
|
|
4303
|
+
kind: "phone",
|
|
4304
|
+
identifierType: 1,
|
|
4305
|
+
identifier: compactPhone,
|
|
4306
|
+
phonePre: "86"
|
|
4307
|
+
};
|
|
4308
|
+
}
|
|
4309
|
+
if (!/^[A-Za-z][A-Za-z0-9_-]{1,63}$/.test(trimmed)) throw new ArkmePluginError("contact-arkme-id-invalid", "即我号格式无效", false);
|
|
4310
|
+
return {
|
|
4311
|
+
kind: "arkme_id",
|
|
4312
|
+
identifierType: 2,
|
|
4313
|
+
identifier: trimmed,
|
|
4314
|
+
phonePre: "86"
|
|
4315
|
+
};
|
|
4316
|
+
}
|
|
4317
|
+
var ContactService = class {
|
|
4318
|
+
runtime;
|
|
4319
|
+
source;
|
|
4320
|
+
profile;
|
|
4321
|
+
realtime;
|
|
4322
|
+
candidates = /* @__PURE__ */ new Map();
|
|
4323
|
+
constructor(runtime, source, profile, realtime) {
|
|
4324
|
+
this.runtime = runtime;
|
|
4325
|
+
this.source = source;
|
|
4326
|
+
this.profile = profile;
|
|
4327
|
+
this.realtime = realtime;
|
|
4328
|
+
}
|
|
4329
|
+
dispose() {
|
|
4330
|
+
this.candidates.clear();
|
|
4331
|
+
}
|
|
4332
|
+
async search(identifier, options = {}) {
|
|
4333
|
+
const session = await this.runtime.requireSession();
|
|
4334
|
+
const normalized = normalizeIdentifier(identifier);
|
|
4335
|
+
const candidate = await this.lookup(normalized, session, options.signal);
|
|
4336
|
+
this.prune();
|
|
4337
|
+
this.candidates.set(candidate.result.contactRef, candidate);
|
|
4338
|
+
this.prune();
|
|
4339
|
+
return candidate.result;
|
|
4340
|
+
}
|
|
4341
|
+
async add(contactRef, options = {}) {
|
|
4342
|
+
const session = await this.runtime.requireSession();
|
|
4343
|
+
this.prune();
|
|
4344
|
+
const normalizedContactRef = contactRef.trim();
|
|
4345
|
+
if (!CONTACT_REF_PATTERN.test(normalizedContactRef)) throw new ArkmePluginError("contact-ref-invalid", "联系人搜索引用无效,请重新搜索", false);
|
|
4346
|
+
const cached = this.candidates.get(normalizedContactRef);
|
|
4347
|
+
if (cached === void 0 || cached.userId !== session.userId || cached.expiresAtMillis <= Date.now()) throw new ArkmePluginError("contact-ref-expired", "联系人搜索结果已过期,请重新搜索", false, 410);
|
|
4348
|
+
const candidate = await this.lookup(cached.normalized, session, options.signal, normalizedContactRef);
|
|
4349
|
+
if (candidate.targetUserId !== cached.targetUserId || candidate.result.registered !== cached.result.registered) {
|
|
4350
|
+
this.candidates.delete(normalizedContactRef);
|
|
4351
|
+
throw new ArkmePluginError("contact-candidate-changed", "联系人身份已发生变化,请重新搜索后确认", false, 409);
|
|
4352
|
+
}
|
|
4353
|
+
if (candidate.result.isSelf) throw new ArkmePluginError("contact-self-invalid", "不能添加自己为联系人", false);
|
|
4354
|
+
if (!candidate.result.canAdd) throw new ArkmePluginError("contact-add-unavailable", "该账号当前无法添加", false, 409);
|
|
4355
|
+
const remark = options.remark?.trim() ?? "";
|
|
4356
|
+
if (Array.from(remark).length > 100) throw new ArkmePluginError("contact-remark-invalid", "备注不能超过 100 个字符", false);
|
|
4357
|
+
const requestUid = options.requestUid?.trim() || randomUUID();
|
|
4358
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(requestUid)) throw new ArkmePluginError("contact-request-uid-invalid", "联系人添加请求标识无效", false);
|
|
4359
|
+
const chatSessionUid = `chat_session_${requestUid}`;
|
|
4360
|
+
const displayName = candidate.result.displayName;
|
|
4361
|
+
const common = {
|
|
4362
|
+
chat_session_uid: chatSessionUid,
|
|
4363
|
+
create_at: Date.now(),
|
|
4364
|
+
...remark === "" ? {} : { remark }
|
|
4365
|
+
};
|
|
4366
|
+
const registered = candidate.result.registered && candidate.targetUserId !== void 0;
|
|
4367
|
+
const bundle = registered ? await this.runtime.authenticatedChatPost("/api/v1/chats/contacts/add-and-open-private", {
|
|
4368
|
+
...common,
|
|
4369
|
+
target_user_id: candidate.targetUserId,
|
|
4370
|
+
target_display_name_snapshot: displayName
|
|
4371
|
+
}, session, options.signal, { key: `contact:add:${requestUid}` }) : await this.runtime.authenticatedChatPost("/api/v1/chats/contacts/add-and-open-pending-phone", {
|
|
4372
|
+
...common,
|
|
4373
|
+
phone_pre: candidate.normalized.phonePre,
|
|
4374
|
+
phone: candidate.phone ?? candidate.normalized.identifier,
|
|
4375
|
+
display_name_snapshot: displayName
|
|
4376
|
+
}, session, options.signal, { key: `contact:add:${requestUid}` });
|
|
4377
|
+
let source = await this.source.chatSourceFromBundle(bundle, session, void 0, []);
|
|
4378
|
+
if (registered && candidate.targetUserId !== void 0) source = {
|
|
4379
|
+
...source,
|
|
4380
|
+
avatarRef: await this.profile.sealProfileImageRef(session.userId, candidate.targetUserId)
|
|
4381
|
+
};
|
|
4382
|
+
const returnedUid = stringValue$1(objectValue$1(bundle.session).chat_session_uid).trim();
|
|
4383
|
+
if (returnedUid === "") throw new ArkmePluginError("contact-add-contract-invalid", "联系人添加响应不完整", true, 502);
|
|
4384
|
+
this.source.setChatSource(session.userId, returnedUid, source);
|
|
4385
|
+
this.source.invalidateSourceListCache(session.userId, "root");
|
|
4386
|
+
this.realtime.emitChatClientEvent({
|
|
4387
|
+
type: "sessions-delta",
|
|
4388
|
+
revision: this.realtime.nextChatClientRevision(),
|
|
4389
|
+
updates: [{
|
|
4390
|
+
sourceKey: source.sourceKey ?? await this.source.chatDirectorySourceKey(session.userId, returnedUid),
|
|
4391
|
+
source,
|
|
4392
|
+
timelineItems: []
|
|
4393
|
+
}]
|
|
4394
|
+
});
|
|
4395
|
+
this.candidates.delete(normalizedContactRef);
|
|
4396
|
+
return {
|
|
4397
|
+
state: registered ? "ready" : "pending",
|
|
4398
|
+
source
|
|
4399
|
+
};
|
|
4400
|
+
}
|
|
4401
|
+
async lookup(normalized, session, signal, contactRef = `arkme-contact-v1.${randomUUID()}`) {
|
|
4402
|
+
const data = await this.runtime.authenticatedAuthPost("/api/v1/auth/search-contact-account", {
|
|
4403
|
+
identifier_type: normalized.identifierType,
|
|
4404
|
+
identifier: normalized.identifier,
|
|
4405
|
+
phone_pre: normalized.phonePre
|
|
4406
|
+
}, session, signal, {
|
|
4407
|
+
key: `contact:search:${normalized.identifierType}:${normalized.identifier}`,
|
|
4408
|
+
bypassCache: true
|
|
4409
|
+
});
|
|
4410
|
+
const targetUserId = Math.trunc(numberValue$15(data.user_id));
|
|
4411
|
+
const registered = booleanValue$7(data.is_registered) && targetUserId > 0;
|
|
4412
|
+
const isSelf = booleanValue$7(data.is_self);
|
|
4413
|
+
const canAdd = booleanValue$7(data.can_add) && !isSelf && (normalized.kind === "phone" || registered);
|
|
4414
|
+
const displayName = stringValue$1(data.nick_name).trim() || stringValue$1(data.jotmo_id).trim() || (normalized.kind === "phone" ? `+${normalized.phonePre} ${normalized.identifier}` : normalized.identifier);
|
|
4415
|
+
const result = {
|
|
4416
|
+
contactRef,
|
|
4417
|
+
identifierKind: normalized.kind,
|
|
4418
|
+
displayName,
|
|
4419
|
+
...stringValue$1(data.jotmo_id).trim() === "" ? {} : { arkmeId: stringValue$1(data.jotmo_id).trim() },
|
|
4420
|
+
...registered ? { avatarRef: await this.profile.sealProfileImageRef(session.userId, targetUserId) } : {},
|
|
4421
|
+
registered,
|
|
4422
|
+
inviteBySms: booleanValue$7(data.invite_by_sms),
|
|
4423
|
+
canAdd,
|
|
4424
|
+
isSelf
|
|
4425
|
+
};
|
|
4426
|
+
return {
|
|
4427
|
+
userId: session.userId,
|
|
4428
|
+
expiresAtMillis: Date.now() + CONTACT_REF_TTL_MS,
|
|
4429
|
+
normalized,
|
|
4430
|
+
result,
|
|
4431
|
+
...targetUserId > 0 ? { targetUserId } : {},
|
|
4432
|
+
...normalized.kind === "phone" ? { phone: stringValue$1(data.phone).trim() || normalized.identifier } : {}
|
|
4433
|
+
};
|
|
4434
|
+
}
|
|
4435
|
+
prune() {
|
|
4436
|
+
const now = Date.now();
|
|
4437
|
+
for (const [ref, candidate] of this.candidates) if (candidate.expiresAtMillis <= now) this.candidates.delete(ref);
|
|
4438
|
+
while (this.candidates.size >= MAX_CONTACT_REFS) {
|
|
4439
|
+
const oldest = this.candidates.keys().next().value;
|
|
4440
|
+
if (oldest === void 0) break;
|
|
4441
|
+
this.candidates.delete(oldest);
|
|
4442
|
+
}
|
|
4443
|
+
}
|
|
4444
|
+
};
|
|
4445
|
+
//#endregion
|
|
4273
4446
|
//#region src/services/community-service.ts
|
|
4274
4447
|
function numberValue$14(value) {
|
|
4275
4448
|
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
@@ -4485,7 +4658,7 @@ var ExtensionReviewService = class {
|
|
|
4485
4658
|
422
|
|
4486
4659
|
].includes(error.httpStatus)) {
|
|
4487
4660
|
await this.runtime.stateStore.removeExtensionReviewOperation(userId, operation.clientMutationId);
|
|
4488
|
-
throw new ArkmePluginError("extension-review-registry-rejected",
|
|
4661
|
+
throw new ArkmePluginError("extension-review-registry-rejected", `评论已写入首页,但市集未接受:${error.message}`, false, error.httpStatus, { cause: error });
|
|
4489
4662
|
}
|
|
4490
4663
|
await this.runtime.stateStore.markExtensionReviewOperation(userId, operation.clientMutationId, "failed", safeFailureMessage$5(error));
|
|
4491
4664
|
throw new ArkmePluginError("extension-review-registry-pending", "评论已写入首页,但同步到扩展详情失败;请保留当前内容并重试", true, 503, { cause: error });
|
|
@@ -5024,6 +5197,7 @@ var GroupAiPolishService = class {
|
|
|
5024
5197
|
};
|
|
5025
5198
|
//#endregion
|
|
5026
5199
|
//#region src/services/group-service.ts
|
|
5200
|
+
const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
5027
5201
|
function numberValue$12(value) {
|
|
5028
5202
|
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
5029
5203
|
}
|
|
@@ -5356,6 +5530,27 @@ var GroupService = class {
|
|
|
5356
5530
|
if (raw.version !== 1 || result.userId !== expectedUserId || result.groupChatSessionUid !== expectedGroupChatSessionUid || !Number.isSafeInteger(result.targetUserId) || result.targetUserId <= 0 || result.targetUserId === expectedUserId || result.displayName === "") throw new ArkmePluginError("group-candidate-ref-invalid", "群成员候选引用与当前账号不匹配", false, 403);
|
|
5357
5531
|
return result;
|
|
5358
5532
|
}
|
|
5533
|
+
async createGroup(title, clientMutationId, options = {}) {
|
|
5534
|
+
const normalizedTitle = title.trim();
|
|
5535
|
+
if (normalizedTitle === "" || Array.from(normalizedTitle).length > 80) throw new ArkmePluginError("group-title-invalid", "群聊名称需为 1-80 个字符", false);
|
|
5536
|
+
if (!UUID.test(clientMutationId)) throw new ArkmePluginError("client-mutation-id-invalid", "群聊操作标识无效", false);
|
|
5537
|
+
const session = await this.runtime.requireSession();
|
|
5538
|
+
let data;
|
|
5539
|
+
try {
|
|
5540
|
+
data = await this.runtime.authenticatedChatPost("/api/v1/chats/create-group", {
|
|
5541
|
+
chat_session_uid: clientMutationId,
|
|
5542
|
+
title: normalizedTitle,
|
|
5543
|
+
member_user_ids: [],
|
|
5544
|
+
create_at: Date.now()
|
|
5545
|
+
}, session, options.signal);
|
|
5546
|
+
} catch (error) {
|
|
5547
|
+
if (error instanceof ArkmePluginError && ["arkme-network-error", "arkme-timeout"].includes(error.code)) throw new ArkmePluginError("group-create-outcome-unknown", "群聊创建结果未知,请刷新会话列表确认;不会自动重试", false, 409, { cause: error });
|
|
5548
|
+
throw error;
|
|
5549
|
+
}
|
|
5550
|
+
const source = await this.source.chatSourceFromBundle(data, session, void 0, []);
|
|
5551
|
+
this.source.setChatSourceByKey(`${String(session.userId)}:${clientMutationId}`, source);
|
|
5552
|
+
return source;
|
|
5553
|
+
}
|
|
5359
5554
|
async listGroupMembers(sourceRef, options = {}) {
|
|
5360
5555
|
const session = await this.runtime.requireSession();
|
|
5361
5556
|
const source = await this.source.openSourceRef(sourceRef, session.userId);
|
|
@@ -7202,6 +7397,43 @@ var RecordService = class {
|
|
|
7202
7397
|
};
|
|
7203
7398
|
}
|
|
7204
7399
|
}
|
|
7400
|
+
async createFileAssetsForConversation(recordUid, textContent, assets) {
|
|
7401
|
+
const session = await this.runtime.requireSession();
|
|
7402
|
+
const normalizedUid = recordUid.trim();
|
|
7403
|
+
const normalizedText = textContent.trim();
|
|
7404
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(normalizedUid)) throw new ArkmePluginError("record-uid-invalid", "写入标识无效,请重试", false);
|
|
7405
|
+
if (assets.length === 0 || assets.length > 20 || normalizedText.length > this.runtime.config.maxTextLength) throw new ArkmePluginError("record-file-assets-invalid", "附件内容为空、过长或数量超限", false);
|
|
7406
|
+
for (const asset of assets) if (!/^[A-Za-z0-9._:-]{8,256}$/.test(asset.fileAssetUid) || asset.fileName.trim() === "" || asset.fileName.length > 255 || !Number.isSafeInteger(asset.size) || asset.size <= 0 || ![
|
|
7407
|
+
1,
|
|
7408
|
+
2,
|
|
7409
|
+
3,
|
|
7410
|
+
4
|
|
7411
|
+
].includes(asset.fileKind)) throw new ArkmePluginError("record-file-asset-invalid", "附件资产参数无效", false);
|
|
7412
|
+
const data = await this.runtime.authenticatedPost("/api/v1/records/create", {
|
|
7413
|
+
record_uid: normalizedUid,
|
|
7414
|
+
template_kind: 2,
|
|
7415
|
+
display_kind: 0,
|
|
7416
|
+
title: "",
|
|
7417
|
+
text_content: normalizedText,
|
|
7418
|
+
content_payload: {
|
|
7419
|
+
payload_kind: 2,
|
|
7420
|
+
schema_version: 1,
|
|
7421
|
+
text_state: normalizedText === "" ? 3 : 1,
|
|
7422
|
+
media_refs: assets.map((asset, index) => ({
|
|
7423
|
+
file_asset_uid: asset.fileAssetUid,
|
|
7424
|
+
content_file_role: 1,
|
|
7425
|
+
render_role: 1,
|
|
7426
|
+
sort_order: index,
|
|
7427
|
+
file_name: asset.fileName
|
|
7428
|
+
}))
|
|
7429
|
+
},
|
|
7430
|
+
send_at: Date.now()
|
|
7431
|
+
}, session);
|
|
7432
|
+
return {
|
|
7433
|
+
recordUid: stringValue$1(data.record_uid).trim() || normalizedUid,
|
|
7434
|
+
status: numberValue$7(data.status)
|
|
7435
|
+
};
|
|
7436
|
+
}
|
|
7205
7437
|
async pendingWrites() {
|
|
7206
7438
|
const session = await this.runtime.requireSession();
|
|
7207
7439
|
return await this.runtime.stateStore.listPending(session.userId);
|
|
@@ -9178,14 +9410,63 @@ var WechatService = class {
|
|
|
9178
9410
|
}
|
|
9179
9411
|
};
|
|
9180
9412
|
//#endregion
|
|
9413
|
+
//#region src/types.ts
|
|
9414
|
+
const ARKME_PROVIDER_CONTRACT_VERSION = 1;
|
|
9415
|
+
const ARKME_DEFAULT_SHARE_WEBSITE = "https://app.arkme.ai";
|
|
9416
|
+
const ARKME_WORLD_PUBLISH_MAX_IMAGES = 9;
|
|
9417
|
+
const ARKME_WORLD_PUBLISH_MAX_IMAGE_BYTES = 20 * 1024 * 1024;
|
|
9418
|
+
//#endregion
|
|
9181
9419
|
//#region src/services/world-service.ts
|
|
9182
9420
|
const ARKME_WORLD_IMAGE_REF_TTL_MILLIS = 900 * 1e3;
|
|
9183
9421
|
const MAX_ARKME_WORLD_IMAGE_REFS = 2048;
|
|
9184
9422
|
const ARKME_WORLD_RECORD_REF_TTL_MILLIS = 900 * 1e3;
|
|
9185
9423
|
const MAX_ARKME_WORLD_RECORD_REFS = 4096;
|
|
9424
|
+
const ARKME_VOICEPRINT_PLAY_SCOPE = 2;
|
|
9425
|
+
const ARKME_VOICEPRINT_INVITE_TOKEN_MAX_LENGTH = 2048;
|
|
9426
|
+
const ARKME_VOICEPRINT_SOCIAL_CACHE_MILLIS = 300 * 1e3;
|
|
9427
|
+
const ARKME_VOICEPRINT_SOCIAL_STALE_MILLIS = 1440 * 60 * 1e3;
|
|
9428
|
+
const ARKME_VOICEPRINT_SOCIAL_SOURCE_TIMEOUT_MILLIS = 4e3;
|
|
9429
|
+
const WORLD_VOICEPRINT_SOCIAL_RELATIONS = {
|
|
9430
|
+
reciprocal_expectation: {
|
|
9431
|
+
type: "reciprocal_expectation",
|
|
9432
|
+
displayLine: "TA也曾期待过你的声音",
|
|
9433
|
+
reasonCode: "relationship_reciprocal_expectation",
|
|
9434
|
+
reasonLabel: "因为TA也曾期待过我的声音"
|
|
9435
|
+
},
|
|
9436
|
+
call: {
|
|
9437
|
+
type: "call",
|
|
9438
|
+
displayLine: "你们曾经通过话",
|
|
9439
|
+
reasonCode: "relationship_call",
|
|
9440
|
+
reasonLabel: "因为我们曾经通过话"
|
|
9441
|
+
},
|
|
9442
|
+
world_interaction: {
|
|
9443
|
+
type: "world_interaction",
|
|
9444
|
+
displayLine: "你们曾在世界回应过彼此",
|
|
9445
|
+
reasonCode: "relationship_world",
|
|
9446
|
+
reasonLabel: "因为我们在世界里回应过彼此"
|
|
9447
|
+
},
|
|
9448
|
+
group_interaction: {
|
|
9449
|
+
type: "group_interaction",
|
|
9450
|
+
displayLine: "你们曾在同一个群里互动过",
|
|
9451
|
+
reasonCode: "relationship_group",
|
|
9452
|
+
reasonLabel: "因为我们在群里有过互动"
|
|
9453
|
+
},
|
|
9454
|
+
private_chat: {
|
|
9455
|
+
type: "private_chat",
|
|
9456
|
+
displayLine: "你们曾经聊过",
|
|
9457
|
+
reasonCode: "relationship_chat",
|
|
9458
|
+
reasonLabel: "因为我们以前聊过"
|
|
9459
|
+
}
|
|
9460
|
+
};
|
|
9186
9461
|
function numberValue(value) {
|
|
9187
9462
|
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
9188
9463
|
}
|
|
9464
|
+
function integerValue(value) {
|
|
9465
|
+
if (typeof value === "number" && Number.isFinite(value)) return Math.trunc(value);
|
|
9466
|
+
if (typeof value !== "string" || !/^-?\d+$/.test(value.trim())) return 0;
|
|
9467
|
+
const parsed = Number(value.trim());
|
|
9468
|
+
return Number.isSafeInteger(parsed) ? parsed : 0;
|
|
9469
|
+
}
|
|
9189
9470
|
function listValue(value) {
|
|
9190
9471
|
return Array.isArray(value) ? value : [];
|
|
9191
9472
|
}
|
|
@@ -9194,12 +9475,53 @@ function safeFailureMessage(error) {
|
|
|
9194
9475
|
if (error instanceof Error && error.message.trim() !== "") return error.message;
|
|
9195
9476
|
return "未知错误";
|
|
9196
9477
|
}
|
|
9478
|
+
function optionalTrimmedText(value, limit) {
|
|
9479
|
+
const normalized = value?.replace(/\s+/g, " ").trim();
|
|
9480
|
+
if (normalized === void 0 || normalized === "") return void 0;
|
|
9481
|
+
return normalized.length <= limit ? normalized : `${normalized.slice(0, limit).trimEnd()}...`;
|
|
9482
|
+
}
|
|
9483
|
+
function buildVoiceprintInviteShareUrl(environment, token) {
|
|
9484
|
+
const url = new URL("/app/voiceprint/invite", environment === "prod" ? "https://jiwo.cc" : "https://jotmo-app.senguo.me");
|
|
9485
|
+
url.hash = `t=${encodeURIComponent(token)}`;
|
|
9486
|
+
return url.toString();
|
|
9487
|
+
}
|
|
9488
|
+
function validVoiceprintInviteToken(token) {
|
|
9489
|
+
if (token === "" || token.length > ARKME_VOICEPRINT_INVITE_TOKEN_MAX_LENGTH) return false;
|
|
9490
|
+
let byteLength = 0;
|
|
9491
|
+
for (const char of token) {
|
|
9492
|
+
const codePoint = char.codePointAt(0) ?? 0;
|
|
9493
|
+
if (codePoint <= 31 || codePoint === 127) return false;
|
|
9494
|
+
byteLength += codePoint <= 127 ? 1 : codePoint <= 2047 ? 2 : codePoint <= 65535 ? 3 : 4;
|
|
9495
|
+
if (byteLength > ARKME_VOICEPRINT_INVITE_TOKEN_MAX_LENGTH) return false;
|
|
9496
|
+
}
|
|
9497
|
+
return true;
|
|
9498
|
+
}
|
|
9197
9499
|
function worldVisibility(checkStatus) {
|
|
9198
9500
|
if (checkStatus === 1) return "pending_review";
|
|
9199
9501
|
if (checkStatus === 4) return "rejected";
|
|
9200
9502
|
if (checkStatus === 0 || checkStatus === 2 || checkStatus === 3) return "visible";
|
|
9201
9503
|
return "unknown";
|
|
9202
9504
|
}
|
|
9505
|
+
function worldPublicationResult(checkStatus) {
|
|
9506
|
+
const visibility = worldVisibility(checkStatus);
|
|
9507
|
+
if (visibility === "rejected") return {
|
|
9508
|
+
recordSaved: true,
|
|
9509
|
+
recordState: "synced",
|
|
9510
|
+
worldPublished: false,
|
|
9511
|
+
visibility,
|
|
9512
|
+
checkStatus,
|
|
9513
|
+
retryable: false,
|
|
9514
|
+
error: "内容未通过审核,请调整后重试"
|
|
9515
|
+
};
|
|
9516
|
+
return {
|
|
9517
|
+
recordSaved: true,
|
|
9518
|
+
recordState: "synced",
|
|
9519
|
+
worldPublished: true,
|
|
9520
|
+
visibility,
|
|
9521
|
+
checkStatus,
|
|
9522
|
+
retryable: false
|
|
9523
|
+
};
|
|
9524
|
+
}
|
|
9203
9525
|
function worldTags(text) {
|
|
9204
9526
|
return [...text.matchAll(/#(\S+)/gu)].map((match) => match[1] ?? "").filter((tag) => tag !== "");
|
|
9205
9527
|
}
|
|
@@ -9207,6 +9529,10 @@ function stableWorldInteractionRecordUid(userId, targetRecordUid, clientMutation
|
|
|
9207
9529
|
const hex = createHash("sha256").update(`dsh-arkme:world-interaction:${String(userId)}:${targetRecordUid}:${clientMutationId}`).digest("hex");
|
|
9208
9530
|
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-5${hex.slice(13, 16)}-a${hex.slice(17, 20)}-${hex.slice(20, 32)}`;
|
|
9209
9531
|
}
|
|
9532
|
+
function stableWorldPublishRecordUid(userId, clientMutationId) {
|
|
9533
|
+
const hex = createHash("sha256").update(`dsh-arkme:world-publish:${String(userId)}:${clientMutationId}`).digest("hex");
|
|
9534
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-5${hex.slice(13, 16)}-a${hex.slice(17, 20)}-${hex.slice(20, 32)}`;
|
|
9535
|
+
}
|
|
9210
9536
|
function allowedSignedImageHost(environment, hostname) {
|
|
9211
9537
|
return (environment === "prod" ? ["jotmo-userfiles.oss-cn-hangzhou.aliyuncs.com", "userfiles.jotmo.cc"] : ["jotmo-userfiles-test.oss-cn-hangzhou.aliyuncs.com", "jotmo-userfiles.senguo.me"]).includes(hostname.toLowerCase());
|
|
9212
9538
|
}
|
|
@@ -9246,6 +9572,8 @@ var WorldService = class {
|
|
|
9246
9572
|
record;
|
|
9247
9573
|
worldImageRefs = /* @__PURE__ */ new Map();
|
|
9248
9574
|
worldRecordRefs = /* @__PURE__ */ new Map();
|
|
9575
|
+
voiceprintSocialCache = /* @__PURE__ */ new Map();
|
|
9576
|
+
voiceprintSocialInFlight = /* @__PURE__ */ new Map();
|
|
9249
9577
|
constructor(runtime, profile, media, record) {
|
|
9250
9578
|
this.runtime = runtime;
|
|
9251
9579
|
this.profile = profile;
|
|
@@ -9255,6 +9583,8 @@ var WorldService = class {
|
|
|
9255
9583
|
dispose() {
|
|
9256
9584
|
this.worldImageRefs.clear();
|
|
9257
9585
|
this.worldRecordRefs.clear();
|
|
9586
|
+
this.voiceprintSocialCache.clear();
|
|
9587
|
+
this.voiceprintSocialInFlight.clear();
|
|
9258
9588
|
}
|
|
9259
9589
|
async listWorldRecords(options = {}) {
|
|
9260
9590
|
const limit = Math.min(20, Math.max(1, Math.trunc(options.limit ?? 10)));
|
|
@@ -9296,6 +9626,58 @@ var WorldService = class {
|
|
|
9296
9626
|
...hasMore ? { nextOffset } : {}
|
|
9297
9627
|
};
|
|
9298
9628
|
}
|
|
9629
|
+
async listMyWorldFeed(options = {}) {
|
|
9630
|
+
const session = await this.runtime.requireSession();
|
|
9631
|
+
const limit = Math.min(20, Math.max(1, Math.trunc(options.limit ?? 20)));
|
|
9632
|
+
const offset = Math.max(0, Math.trunc(options.offset ?? 0));
|
|
9633
|
+
const data = await this.runtime.authenticatedWorldPost("/api/v1/public-record/my-list", {
|
|
9634
|
+
limit,
|
|
9635
|
+
offset
|
|
9636
|
+
}, session, options.signal);
|
|
9637
|
+
const rawItems = listValue(data.list);
|
|
9638
|
+
const resolvedAvatars = await this.resolveWorldAvatarUrls(rawItems, session, options.signal);
|
|
9639
|
+
const items = (await Promise.all(rawItems.map((raw) => this.worldFeedItem(raw, session.userId, resolvedAvatars)))).filter((item) => item !== void 0);
|
|
9640
|
+
const total = Math.max(0, Math.trunc(numberValue(data.total)));
|
|
9641
|
+
const nextOffset = offset + rawItems.length;
|
|
9642
|
+
const hasMore = rawItems.length > 0 && nextOffset < total;
|
|
9643
|
+
return {
|
|
9644
|
+
items,
|
|
9645
|
+
total,
|
|
9646
|
+
hasMore,
|
|
9647
|
+
...hasMore ? { nextOffset } : {}
|
|
9648
|
+
};
|
|
9649
|
+
}
|
|
9650
|
+
async listUserWorldFeed(userId, options = {}) {
|
|
9651
|
+
if (!Number.isSafeInteger(userId) || userId <= 0) throw new ArkmePluginError("world-user-id-invalid", "世界用户 ID 无效,请刷新后重试", false, 400);
|
|
9652
|
+
const session = await this.runtime.requireSession();
|
|
9653
|
+
const limit = Math.min(20, Math.max(1, Math.trunc(options.limit ?? 20)));
|
|
9654
|
+
let offset = Math.max(0, Math.trunc(options.offset ?? 0));
|
|
9655
|
+
let total = 0;
|
|
9656
|
+
let items = [];
|
|
9657
|
+
let hasMore = false;
|
|
9658
|
+
let attempts = 0;
|
|
9659
|
+
do {
|
|
9660
|
+
const data = await this.runtime.post(this.runtime.config.worldBaseUrl, "/api/public/v1/public-record/user-list", {
|
|
9661
|
+
user_id: userId,
|
|
9662
|
+
limit,
|
|
9663
|
+
offset
|
|
9664
|
+
}, void 0, [200], options.signal);
|
|
9665
|
+
const rawItems = listValue(data.list);
|
|
9666
|
+
total = Math.max(0, Math.trunc(numberValue(data.total)));
|
|
9667
|
+
const rootItems = rawItems.filter((raw) => stringValue$1(objectValue$1(raw).parent_record_uid).trim() === "");
|
|
9668
|
+
const resolvedAvatars = await this.resolveWorldAvatarUrls(rootItems, session, options.signal);
|
|
9669
|
+
items = (await Promise.all(rootItems.map((raw) => this.worldFeedItem(raw, session.userId, resolvedAvatars)))).filter((item) => item !== void 0);
|
|
9670
|
+
offset += rawItems.length;
|
|
9671
|
+
hasMore = rawItems.length > 0 && offset < total;
|
|
9672
|
+
attempts += 1;
|
|
9673
|
+
} while (items.length === 0 && hasMore && attempts < 4);
|
|
9674
|
+
return {
|
|
9675
|
+
items,
|
|
9676
|
+
total,
|
|
9677
|
+
hasMore,
|
|
9678
|
+
...hasMore ? { nextOffset: offset } : {}
|
|
9679
|
+
};
|
|
9680
|
+
}
|
|
9299
9681
|
async worldVoiceprintPlaybackAvailability(recordRefs, signal) {
|
|
9300
9682
|
const session = await this.runtime.requireSession();
|
|
9301
9683
|
const normalizedRefs = [...new Set(recordRefs.map((value) => value.trim()).filter((value) => value !== ""))].slice(0, 20);
|
|
@@ -9357,6 +9739,236 @@ var WorldService = class {
|
|
|
9357
9739
|
chunkEndRune
|
|
9358
9740
|
};
|
|
9359
9741
|
}
|
|
9742
|
+
async worldVoiceprintSocialContext(recordRef, options = {}) {
|
|
9743
|
+
const session = await this.runtime.requireSession();
|
|
9744
|
+
const entry = this.openWorldRecordRef(recordRef, session.userId);
|
|
9745
|
+
const authorUserId = entry.ownerUserId ?? 0;
|
|
9746
|
+
if (!Number.isSafeInteger(authorUserId) || authorUserId <= 0 || authorUserId === session.userId) return { relations: [] };
|
|
9747
|
+
const cacheKey = `${String(session.userId)}:${String(authorUserId)}`;
|
|
9748
|
+
const cached = this.voiceprintSocialCache.get(cacheKey);
|
|
9749
|
+
const age = cached === void 0 ? Number.POSITIVE_INFINITY : Date.now() - cached.savedAtMillis;
|
|
9750
|
+
if (options.forceRefresh !== true && cached !== void 0 && cached.context.relations.length > 0 && age < ARKME_VOICEPRINT_SOCIAL_CACHE_MILLIS) return cached.context;
|
|
9751
|
+
if (options.forceRefresh !== true && cached !== void 0 && cached.context.relations.length > 0 && age < ARKME_VOICEPRINT_SOCIAL_STALE_MILLIS) {
|
|
9752
|
+
this.refreshWorldVoiceprintSocialContext({
|
|
9753
|
+
cacheKey,
|
|
9754
|
+
flightKey: `${cacheKey}:${entry.recordUid}`,
|
|
9755
|
+
viewerUserId: session.userId,
|
|
9756
|
+
authorUserId,
|
|
9757
|
+
recordUid: entry.recordUid,
|
|
9758
|
+
fallback: cached,
|
|
9759
|
+
...options.signal === void 0 ? {} : { signal: options.signal }
|
|
9760
|
+
});
|
|
9761
|
+
return cached.context;
|
|
9762
|
+
}
|
|
9763
|
+
return await this.refreshWorldVoiceprintSocialContext({
|
|
9764
|
+
cacheKey,
|
|
9765
|
+
flightKey: `${cacheKey}:${entry.recordUid}`,
|
|
9766
|
+
viewerUserId: session.userId,
|
|
9767
|
+
authorUserId,
|
|
9768
|
+
recordUid: entry.recordUid,
|
|
9769
|
+
...cached === void 0 ? {} : { fallback: cached },
|
|
9770
|
+
...options.signal === void 0 ? {} : { signal: options.signal }
|
|
9771
|
+
});
|
|
9772
|
+
}
|
|
9773
|
+
async createWorldVoiceprintInviteIntent(recordRef, signal) {
|
|
9774
|
+
const session = await this.runtime.requireSession();
|
|
9775
|
+
const entry = this.openWorldRecordRef(recordRef, session.userId);
|
|
9776
|
+
const peerUserId = entry.ownerUserId ?? 0;
|
|
9777
|
+
if (!Number.isSafeInteger(peerUserId) || peerUserId <= 0) throw new ArkmePluginError("world-voiceprint-invite-target-missing", "无法确认这条世界动态的作者,请刷新后重试", false);
|
|
9778
|
+
if (peerUserId === session.userId) throw new ArkmePluginError("world-voiceprint-invite-self-invalid", "这是你自己的动态,不需要提醒自己开启声纹", false, 409);
|
|
9779
|
+
const data = await this.runtime.authenticatedAudioPost("/api/v1/audio/voiceprint/invites/create", { scope: ARKME_VOICEPRINT_PLAY_SCOPE }, session, signal, {
|
|
9780
|
+
lane: "write",
|
|
9781
|
+
bypassCache: true
|
|
9782
|
+
});
|
|
9783
|
+
const token = stringValue$1(data.invite_token).trim();
|
|
9784
|
+
const expiresAtMillis = Math.trunc(numberValue(data.expires_at));
|
|
9785
|
+
const scope = Math.trunc(numberValue(data.scope));
|
|
9786
|
+
if (!validVoiceprintInviteToken(token) || expiresAtMillis <= 0 || scope !== ARKME_VOICEPRINT_PLAY_SCOPE) throw new ArkmePluginError("world-voiceprint-invite-contract-invalid", "声纹邀请响应不完整,请稍后重试", true, 502);
|
|
9787
|
+
return {
|
|
9788
|
+
peerUserId,
|
|
9789
|
+
peerDisplayName: optionalTrimmedText(entry.authorName, 64) ?? "这位用户",
|
|
9790
|
+
inviteUrl: buildVoiceprintInviteShareUrl(this.runtime.config.environment, token),
|
|
9791
|
+
expiresAtMillis,
|
|
9792
|
+
...entry.textPreview === void 0 ? {} : { textPreview: entry.textPreview }
|
|
9793
|
+
};
|
|
9794
|
+
}
|
|
9795
|
+
refreshWorldVoiceprintSocialContext(input) {
|
|
9796
|
+
const existing = this.voiceprintSocialInFlight.get(input.flightKey);
|
|
9797
|
+
if (existing !== void 0) return existing;
|
|
9798
|
+
const refresh = this.loadWorldVoiceprintSocialContext(input).then((result) => {
|
|
9799
|
+
const context = this.mergeWorldVoiceprintSocialContexts(input.fallback?.context, result.context);
|
|
9800
|
+
if (context.relations.length > 0 || result.allSourcesSucceeded) this.voiceprintSocialCache.set(input.cacheKey, {
|
|
9801
|
+
context,
|
|
9802
|
+
savedAtMillis: Date.now()
|
|
9803
|
+
});
|
|
9804
|
+
return context;
|
|
9805
|
+
}).catch(() => input.fallback?.context.relations.length ? input.fallback.context : { relations: [] }).finally(() => {
|
|
9806
|
+
this.voiceprintSocialInFlight.delete(input.flightKey);
|
|
9807
|
+
});
|
|
9808
|
+
this.voiceprintSocialInFlight.set(input.flightKey, refresh);
|
|
9809
|
+
return refresh;
|
|
9810
|
+
}
|
|
9811
|
+
async loadWorldVoiceprintSocialContext(input) {
|
|
9812
|
+
const session = await this.runtime.requireSession();
|
|
9813
|
+
const directChat = this.loadWorldVoiceprintDirectChat(input.authorUserId, session, input.signal);
|
|
9814
|
+
const results = await Promise.all([
|
|
9815
|
+
this.guardWorldVoiceprintSocialSource(async () => await this.loadWorldVoiceprintReciprocalExpectation(input.authorUserId, session, input.signal)),
|
|
9816
|
+
this.guardWorldVoiceprintSocialSource(async () => await this.loadWorldVoiceprintInterwovenRelations(input.authorUserId, directChat, session, input.signal)),
|
|
9817
|
+
this.guardWorldVoiceprintSocialSource(async () => await this.loadWorldVoiceprintPrivateChatRelation(input.authorUserId, directChat)),
|
|
9818
|
+
this.guardWorldVoiceprintSocialSource(async () => await this.loadWorldVoiceprintCommentRelation(input.viewerUserId, input.recordUid, session, input.signal))
|
|
9819
|
+
]);
|
|
9820
|
+
return {
|
|
9821
|
+
context: { relations: this.dedupeWorldVoiceprintRelations(results.flatMap((result) => result.relations)) },
|
|
9822
|
+
allSourcesSucceeded: results.every((result) => result.succeeded)
|
|
9823
|
+
};
|
|
9824
|
+
}
|
|
9825
|
+
async guardWorldVoiceprintSocialSource(loader) {
|
|
9826
|
+
let timeout;
|
|
9827
|
+
try {
|
|
9828
|
+
return await Promise.race([loader(), new Promise((resolve) => {
|
|
9829
|
+
timeout = setTimeout(() => {
|
|
9830
|
+
resolve({
|
|
9831
|
+
succeeded: false,
|
|
9832
|
+
relations: []
|
|
9833
|
+
});
|
|
9834
|
+
}, ARKME_VOICEPRINT_SOCIAL_SOURCE_TIMEOUT_MILLIS);
|
|
9835
|
+
})]);
|
|
9836
|
+
} catch {
|
|
9837
|
+
return {
|
|
9838
|
+
succeeded: false,
|
|
9839
|
+
relations: []
|
|
9840
|
+
};
|
|
9841
|
+
} finally {
|
|
9842
|
+
if (timeout !== void 0) clearTimeout(timeout);
|
|
9843
|
+
}
|
|
9844
|
+
}
|
|
9845
|
+
async loadWorldVoiceprintReciprocalExpectation(authorUserId, session, signal) {
|
|
9846
|
+
return {
|
|
9847
|
+
succeeded: true,
|
|
9848
|
+
relations: listValue(objectValue$1((await this.runtime.authenticatedAudioPost("/api/v1/audio/voiceprint/world-enrollment-interactions", {}, session, signal, {
|
|
9849
|
+
lane: "interactive-read",
|
|
9850
|
+
key: "world-enrollment-interactions",
|
|
9851
|
+
cacheMs: 5e3
|
|
9852
|
+
})).received).requesters).some((raw) => {
|
|
9853
|
+
const requester = objectValue$1(raw);
|
|
9854
|
+
return integerValue(requester.user_id ?? requester.userId) === authorUserId;
|
|
9855
|
+
}) ? [WORLD_VOICEPRINT_SOCIAL_RELATIONS.reciprocal_expectation] : []
|
|
9856
|
+
};
|
|
9857
|
+
}
|
|
9858
|
+
async loadWorldVoiceprintDirectChat(authorUserId, session, signal) {
|
|
9859
|
+
let pageCursor;
|
|
9860
|
+
const visited = /* @__PURE__ */ new Set();
|
|
9861
|
+
for (;;) {
|
|
9862
|
+
const data = await this.runtime.authenticatedChatPost("/api/v1/chats/list", {
|
|
9863
|
+
limit: 100,
|
|
9864
|
+
session_kind: 1,
|
|
9865
|
+
...pageCursor === void 0 ? {} : { page_cursor: pageCursor }
|
|
9866
|
+
}, session, signal, {
|
|
9867
|
+
lane: "interactive-read",
|
|
9868
|
+
bypassCache: true
|
|
9869
|
+
});
|
|
9870
|
+
for (const raw of listValue(data.items)) {
|
|
9871
|
+
const bundle = objectValue$1(raw);
|
|
9872
|
+
const chatSession = objectValue$1(bundle.session);
|
|
9873
|
+
const counterpart = objectValue$1(bundle.private_counterpart);
|
|
9874
|
+
const sessionKind = integerValue(chatSession.session_kind);
|
|
9875
|
+
if (sessionKind !== 1 && sessionKind !== 3 || integerValue(counterpart.user_id ?? counterpart.userId) !== authorUserId) continue;
|
|
9876
|
+
const latestPreview = objectValue$1(bundle.latest_preview);
|
|
9877
|
+
const latestRecord = objectValue$1(latestPreview.record);
|
|
9878
|
+
const latestRelation = objectValue$1(latestPreview.relation);
|
|
9879
|
+
const unread = objectValue$1(bundle.unread_snapshot);
|
|
9880
|
+
const sessionUid = stringValue$1(chatSession.chat_session_uid).trim();
|
|
9881
|
+
if (sessionUid === "") continue;
|
|
9882
|
+
const sharedTopicId = [
|
|
9883
|
+
chatSession,
|
|
9884
|
+
objectValue$1(chatSession.extra),
|
|
9885
|
+
objectValue$1(chatSession.policy_snapshot)
|
|
9886
|
+
].map((source) => integerValue(source.subject_id ?? source.subjectId ?? source.shared_topic_id ?? source.sharedTopicId)).find((value) => value > 0) ?? 0;
|
|
9887
|
+
const hasPrivateMessageEvidence = Object.keys(latestRecord).length > 0 || Object.keys(objectValue$1(latestRecord.payload)).length > 0 || stringValue$1(latestRecord.record_uid).trim() !== "" || stringValue$1(latestRelation.record_uid).trim() !== "" || integerValue(unread.session_last_seq ?? chatSession.last_seq) > 0;
|
|
9888
|
+
return {
|
|
9889
|
+
sessionUid,
|
|
9890
|
+
sharedTopicId: Math.max(0, sharedTopicId),
|
|
9891
|
+
hasPrivateMessageEvidence
|
|
9892
|
+
};
|
|
9893
|
+
}
|
|
9894
|
+
if (data.has_more !== true) return void 0;
|
|
9895
|
+
const nextCursor = objectValue$1(data.next_page_cursor);
|
|
9896
|
+
if (Object.keys(nextCursor).length === 0) throw new Error("voiceprint direct-chat pagination cursor missing");
|
|
9897
|
+
const identity = JSON.stringify(nextCursor);
|
|
9898
|
+
if (visited.has(identity)) throw new Error("voiceprint direct-chat pagination cursor repeated");
|
|
9899
|
+
visited.add(identity);
|
|
9900
|
+
pageCursor = nextCursor;
|
|
9901
|
+
}
|
|
9902
|
+
}
|
|
9903
|
+
async loadWorldVoiceprintInterwovenRelations(authorUserId, directChatFuture, session, signal) {
|
|
9904
|
+
const directChat = await directChatFuture;
|
|
9905
|
+
if (directChat === void 0 || authorUserId <= 0) return {
|
|
9906
|
+
succeeded: true,
|
|
9907
|
+
relations: []
|
|
9908
|
+
};
|
|
9909
|
+
const data = directChat.sharedTopicId > 0 ? await this.runtime.authenticatedWorldPost("/api/v1/interwoven-moments/summary", { subject_id: directChat.sharedTopicId }, session, signal, {
|
|
9910
|
+
lane: "interactive-read",
|
|
9911
|
+
bypassCache: true
|
|
9912
|
+
}) : await this.runtime.authenticatedChatPost("/api/v1/chats/interwoven/summary", {
|
|
9913
|
+
subject_id: 0,
|
|
9914
|
+
chat_session_uid: directChat.sessionUid
|
|
9915
|
+
}, session, signal, {
|
|
9916
|
+
lane: "interactive-read",
|
|
9917
|
+
bypassCache: true
|
|
9918
|
+
});
|
|
9919
|
+
const stats = listValue(objectValue$1(data.recent_card).total_stats);
|
|
9920
|
+
const countFor = (momentType) => {
|
|
9921
|
+
return integerValue(objectValue$1(stats.find((value) => integerValue(objectValue$1(value).moment_type) === momentType)).count);
|
|
9922
|
+
};
|
|
9923
|
+
const relations = [];
|
|
9924
|
+
const callSummary = data.call_summary;
|
|
9925
|
+
if ((callSummary !== null && typeof callSummary === "object" && !Array.isArray(callSummary) ? integerValue(objectValue$1(callSummary).total_call_count) : countFor(3)) > 0) relations.push(WORLD_VOICEPRINT_SOCIAL_RELATIONS.call);
|
|
9926
|
+
if (countFor(2) > 0) relations.push(WORLD_VOICEPRINT_SOCIAL_RELATIONS.world_interaction);
|
|
9927
|
+
const groupFromStatus = listValue(data.source_status).some((raw) => {
|
|
9928
|
+
const status = objectValue$1(raw);
|
|
9929
|
+
return integerValue(status.moment_type) === 1 && integerValue(status.item_count) > 0;
|
|
9930
|
+
});
|
|
9931
|
+
if (countFor(1) > 0 || groupFromStatus) relations.push(WORLD_VOICEPRINT_SOCIAL_RELATIONS.group_interaction);
|
|
9932
|
+
return {
|
|
9933
|
+
succeeded: true,
|
|
9934
|
+
relations
|
|
9935
|
+
};
|
|
9936
|
+
}
|
|
9937
|
+
async loadWorldVoiceprintPrivateChatRelation(authorUserId, directChatFuture) {
|
|
9938
|
+
const directChat = await directChatFuture;
|
|
9939
|
+
return {
|
|
9940
|
+
succeeded: true,
|
|
9941
|
+
relations: directChat !== void 0 && authorUserId > 0 && directChat.hasPrivateMessageEvidence ? [WORLD_VOICEPRINT_SOCIAL_RELATIONS.private_chat] : []
|
|
9942
|
+
};
|
|
9943
|
+
}
|
|
9944
|
+
async loadWorldVoiceprintCommentRelation(viewerUserId, recordUid, session, signal) {
|
|
9945
|
+
if (recordUid.trim() === "") return {
|
|
9946
|
+
succeeded: true,
|
|
9947
|
+
relations: []
|
|
9948
|
+
};
|
|
9949
|
+
return {
|
|
9950
|
+
succeeded: true,
|
|
9951
|
+
relations: listValue((await this.runtime.authenticatedWorldPost("/api/v1/public-record/extend-list", {
|
|
9952
|
+
record_uid: recordUid,
|
|
9953
|
+
limit: 100,
|
|
9954
|
+
offset: 0
|
|
9955
|
+
}, session, signal, {
|
|
9956
|
+
lane: "interactive-read",
|
|
9957
|
+
bypassCache: true
|
|
9958
|
+
})).list).some((raw) => {
|
|
9959
|
+
const item = objectValue$1(raw);
|
|
9960
|
+
return integerValue(item.user_id ?? item.userId) === viewerUserId;
|
|
9961
|
+
}) ? [WORLD_VOICEPRINT_SOCIAL_RELATIONS.world_interaction] : []
|
|
9962
|
+
};
|
|
9963
|
+
}
|
|
9964
|
+
dedupeWorldVoiceprintRelations(relations) {
|
|
9965
|
+
const seen = /* @__PURE__ */ new Set();
|
|
9966
|
+
return relations.filter((relation) => !seen.has(relation.type) && seen.add(relation.type));
|
|
9967
|
+
}
|
|
9968
|
+
mergeWorldVoiceprintSocialContexts(previous, current) {
|
|
9969
|
+
if (previous === void 0 || previous.relations.length === 0) return current;
|
|
9970
|
+
return { relations: this.dedupeWorldVoiceprintRelations([...previous.relations, ...current.relations]) };
|
|
9971
|
+
}
|
|
9360
9972
|
async listWorldInteractions(recordRef, options = {}) {
|
|
9361
9973
|
const session = await this.runtime.requireSession();
|
|
9362
9974
|
const root = this.openWorldRecordRef(recordRef, session.userId);
|
|
@@ -9502,18 +10114,88 @@ var WorldService = class {
|
|
|
9502
10114
|
avatar: profile.avatarRef,
|
|
9503
10115
|
template_kind: 1
|
|
9504
10116
|
}, void 0, signal);
|
|
9505
|
-
|
|
9506
|
-
|
|
10117
|
+
return worldPublicationResult(Math.trunc(numberValue(published.check_status)));
|
|
10118
|
+
} catch (error) {
|
|
10119
|
+
try {
|
|
10120
|
+
if (await this.worldRecordIsPublic(normalizedUid, signal)) return {
|
|
10121
|
+
recordSaved: true,
|
|
10122
|
+
recordState: "synced",
|
|
10123
|
+
worldPublished: true,
|
|
10124
|
+
visibility: "unknown",
|
|
10125
|
+
checkStatus: 0,
|
|
10126
|
+
retryable: false
|
|
10127
|
+
};
|
|
10128
|
+
} catch {}
|
|
10129
|
+
return this.worldPublishFailure(true, error, "synced");
|
|
10130
|
+
}
|
|
10131
|
+
}
|
|
10132
|
+
async publishWorldText(input) {
|
|
10133
|
+
const session = await this.runtime.requireSession();
|
|
10134
|
+
const clientMutationId = input.clientMutationId.trim();
|
|
10135
|
+
if (!/^[A-Za-z0-9_-]{16,128}$/.test(clientMutationId)) throw new ArkmePluginError("world-publish-mutation-invalid", "发布请求标识无效,请重试", false);
|
|
10136
|
+
return await this.publishWorldTextForConversation(stableWorldPublishRecordUid(session.userId, clientMutationId), input.textContent);
|
|
10137
|
+
}
|
|
10138
|
+
async publishWorldFileAssets(input) {
|
|
10139
|
+
const session = await this.runtime.requireSession();
|
|
10140
|
+
const clientMutationId = input.clientMutationId.trim();
|
|
10141
|
+
const textContent = input.textContent.trim();
|
|
10142
|
+
if (!/^[A-Za-z0-9_-]{16,128}$/.test(clientMutationId)) throw new ArkmePluginError("world-publish-mutation-invalid", "发布请求标识无效,请重试", false);
|
|
10143
|
+
if (textContent === "" || textContent.length > this.runtime.config.maxTextLength) throw new ArkmePluginError("world-text-invalid", "世界内容为空或过长", false);
|
|
10144
|
+
if (input.fileAssets.length === 0 || input.fileAssets.length > 9 || input.fileAssets.some((asset) => !/^[A-Za-z0-9._:-]{8,256}$/.test(asset.fileAssetUid) || asset.fileName.trim() === "" || !asset.mimeType.toLowerCase().startsWith("image/") || !Number.isSafeInteger(asset.size) || asset.size <= 0 || asset.size > 20971520 || asset.fileKind !== 1)) throw new ArkmePluginError("world-publish-assets-invalid", "世界图片参数无效", false);
|
|
10145
|
+
let profile;
|
|
10146
|
+
try {
|
|
10147
|
+
const snapshot = await this.profile.refreshProfile();
|
|
10148
|
+
if (snapshot.profile === null) throw new ArkmePluginError("profile-unavailable", "无法读取当前 Arkme 账号资料", true);
|
|
10149
|
+
profile = snapshot.profile;
|
|
10150
|
+
} catch (error) {
|
|
10151
|
+
return this.worldPublishFailure(false, error);
|
|
10152
|
+
}
|
|
10153
|
+
if (profile.contact.phoneMasked === void 0) return {
|
|
10154
|
+
recordSaved: false,
|
|
10155
|
+
recordState: "not_saved",
|
|
10156
|
+
worldPublished: false,
|
|
10157
|
+
visibility: "not_published",
|
|
10158
|
+
checkStatus: 0,
|
|
10159
|
+
retryable: false,
|
|
10160
|
+
error: "请先在 Arkme 客户端绑定手机号,再发到世界"
|
|
10161
|
+
};
|
|
10162
|
+
const recordUid = stableWorldPublishRecordUid(session.userId, clientMutationId);
|
|
10163
|
+
try {
|
|
10164
|
+
if (await this.worldRecordIsPublic(recordUid)) return {
|
|
9507
10165
|
recordSaved: true,
|
|
9508
10166
|
recordState: "synced",
|
|
9509
10167
|
worldPublished: true,
|
|
9510
|
-
visibility:
|
|
9511
|
-
checkStatus,
|
|
10168
|
+
visibility: "unknown",
|
|
10169
|
+
checkStatus: 0,
|
|
9512
10170
|
retryable: false
|
|
9513
10171
|
};
|
|
10172
|
+
} catch {}
|
|
10173
|
+
try {
|
|
10174
|
+
await this.record.createFileAssetsForConversation(recordUid, textContent, input.fileAssets);
|
|
10175
|
+
} catch (error) {
|
|
10176
|
+
return this.worldPublishFailure(false, error);
|
|
10177
|
+
}
|
|
10178
|
+
try {
|
|
10179
|
+
const published = await this.runtime.authenticatedWorldPost("/api/v1/public-record/publish-with-file-assets", {
|
|
10180
|
+
record_uid: recordUid,
|
|
10181
|
+
content: textContent,
|
|
10182
|
+
text_content: textContent,
|
|
10183
|
+
file_assets: input.fileAssets.map((asset, index) => ({
|
|
10184
|
+
file_asset_uid: asset.fileAssetUid,
|
|
10185
|
+
media_type: "image",
|
|
10186
|
+
file_kind: 1,
|
|
10187
|
+
sort_order: index
|
|
10188
|
+
})),
|
|
10189
|
+
tags: worldTags(textContent),
|
|
10190
|
+
original_topic_id: 0,
|
|
10191
|
+
created_at: Date.now(),
|
|
10192
|
+
nick_name: profile.nickname || profile.displayName,
|
|
10193
|
+
avatar: profile.avatarRef
|
|
10194
|
+
}, session);
|
|
10195
|
+
return worldPublicationResult(Math.trunc(numberValue(published.check_status)));
|
|
9514
10196
|
} catch (error) {
|
|
9515
10197
|
try {
|
|
9516
|
-
if (await this.worldRecordIsPublic(
|
|
10198
|
+
if (await this.worldRecordIsPublic(recordUid)) return {
|
|
9517
10199
|
recordSaved: true,
|
|
9518
10200
|
recordState: "synced",
|
|
9519
10201
|
worldPublished: true,
|
|
@@ -9567,7 +10249,14 @@ var WorldService = class {
|
|
|
9567
10249
|
}
|
|
9568
10250
|
const avatarRef = this.isTrustedWorldImageUrl(avatarUrl) ? await this.sealWorldImageRef(viewerUserId, avatarUrl) : void 0;
|
|
9569
10251
|
return {
|
|
9570
|
-
recordRef: await this.worldRecordRef(viewerUserId, recordUid,
|
|
10252
|
+
recordRef: await this.worldRecordRef(viewerUserId, recordUid, {
|
|
10253
|
+
ownerUserId,
|
|
10254
|
+
authorName: stringValue$1(item.nick_name ?? item.nickname).trim() || "Arkme用户",
|
|
10255
|
+
...(() => {
|
|
10256
|
+
const textPreview = optionalTrimmedText(headline || textContent, 80);
|
|
10257
|
+
return textPreview === void 0 ? {} : { textPreview };
|
|
10258
|
+
})()
|
|
10259
|
+
}),
|
|
9571
10260
|
authorName: stringValue$1(item.nick_name ?? item.nickname).trim() || "Arkme用户",
|
|
9572
10261
|
...avatarRef === void 0 ? {} : { avatarRef },
|
|
9573
10262
|
...avatarRef === void 0 && avatarFallback !== void 0 ? { avatarFallback } : {},
|
|
@@ -9637,7 +10326,14 @@ var WorldService = class {
|
|
|
9637
10326
|
const avatarFallback = worldPhoneDefaultAvatar(rawAvatar);
|
|
9638
10327
|
const avatarRef = this.isTrustedWorldImageUrl(avatarUrl) ? await this.sealWorldImageRef(viewerUserId, avatarUrl) : void 0;
|
|
9639
10328
|
return {
|
|
9640
|
-
interactionRef: await this.worldRecordRef(viewerUserId, recordUid
|
|
10329
|
+
interactionRef: await this.worldRecordRef(viewerUserId, recordUid, {
|
|
10330
|
+
ownerUserId,
|
|
10331
|
+
authorName: stringValue$1(item.nick_name ?? item.nickname).trim() || "Arkme用户",
|
|
10332
|
+
...(() => {
|
|
10333
|
+
const textPreview = optionalTrimmedText(textContent, 80);
|
|
10334
|
+
return textPreview === void 0 ? {} : { textPreview };
|
|
10335
|
+
})()
|
|
10336
|
+
}),
|
|
9641
10337
|
parentRef: await this.worldRecordRef(viewerUserId, parentRecordUid),
|
|
9642
10338
|
authorName: stringValue$1(item.nick_name ?? item.nickname).trim() || "Arkme用户",
|
|
9643
10339
|
...avatarRef === void 0 ? {} : { avatarRef },
|
|
@@ -9650,16 +10346,20 @@ var WorldService = class {
|
|
|
9650
10346
|
voiceCount
|
|
9651
10347
|
};
|
|
9652
10348
|
}
|
|
9653
|
-
async worldRecordRef(viewerUserId, recordUid,
|
|
10349
|
+
async worldRecordRef(viewerUserId, recordUid, metadata = {}) {
|
|
9654
10350
|
const recordRef = `arkme-world-record-v1.${createHmac("sha256", await this.runtime.stateStore.uniqueCode()).update(`world-record-v1:${String(viewerUserId)}:${recordUid}`).digest("base64url")}`;
|
|
9655
10351
|
const now = Date.now();
|
|
9656
10352
|
this.pruneWorldRecordRefs(now);
|
|
9657
10353
|
const previous = this.worldRecordRefs.get(recordRef);
|
|
9658
|
-
const resolvedOwnerUserId = ownerUserId ?? previous?.ownerUserId;
|
|
10354
|
+
const resolvedOwnerUserId = metadata.ownerUserId ?? previous?.ownerUserId;
|
|
10355
|
+
const authorName = optionalTrimmedText(metadata.authorName, 64) ?? previous?.authorName;
|
|
10356
|
+
const textPreview = optionalTrimmedText(metadata.textPreview, 80) ?? previous?.textPreview;
|
|
9659
10357
|
this.worldRecordRefs.set(recordRef, {
|
|
9660
10358
|
viewerUserId,
|
|
9661
10359
|
recordUid,
|
|
9662
10360
|
...resolvedOwnerUserId === void 0 || resolvedOwnerUserId <= 0 ? {} : { ownerUserId: resolvedOwnerUserId },
|
|
10361
|
+
...authorName === void 0 ? {} : { authorName },
|
|
10362
|
+
...textPreview === void 0 ? {} : { textPreview },
|
|
9663
10363
|
expiresAtMillis: now + ARKME_WORLD_RECORD_REF_TTL_MILLIS
|
|
9664
10364
|
});
|
|
9665
10365
|
return recordRef;
|
|
@@ -9739,10 +10439,16 @@ var WorldService = class {
|
|
|
9739
10439
|
}
|
|
9740
10440
|
};
|
|
9741
10441
|
//#endregion
|
|
9742
|
-
//#region src/types.ts
|
|
9743
|
-
const ARKME_PROVIDER_CONTRACT_VERSION = 1;
|
|
9744
|
-
//#endregion
|
|
9745
10442
|
//#region src/arkme-service.ts
|
|
10443
|
+
function worldVoiceprintInviteMessage(input) {
|
|
10444
|
+
return `${input.textPreview === void 0 ? "我想邀请你开启声纹。" : `我看到你在世界里发的「${input.textPreview}」,想邀请你开启声纹。`}\n以后看到你的世界动态时,可以直接听到你的声音。\n${input.inviteUrl}`;
|
|
10445
|
+
}
|
|
10446
|
+
function voiceprintInviteRateLimitMessage(error) {
|
|
10447
|
+
if (!(error instanceof ArkmePluginError)) return void 0;
|
|
10448
|
+
if (error.upstreamStatus !== 429 && error.httpStatus !== 429 && !/\bHTTP\s*429\b/.test(error.message)) return void 0;
|
|
10449
|
+
const seconds = error.retryAfterMillis === void 0 ? 0 : Math.ceil(error.retryAfterMillis / 1e3);
|
|
10450
|
+
return seconds > 0 ? `提醒发送太频繁了,请 ${String(seconds)} 秒后再试。` : "提醒发送太频繁了,稍后再试。";
|
|
10451
|
+
}
|
|
9746
10452
|
var ArkmeService = class {
|
|
9747
10453
|
config;
|
|
9748
10454
|
sessionStore;
|
|
@@ -9773,6 +10479,7 @@ var ArkmeService = class {
|
|
|
9773
10479
|
interwoven;
|
|
9774
10480
|
aiPolish;
|
|
9775
10481
|
chat;
|
|
10482
|
+
contact;
|
|
9776
10483
|
constructor(config, sessionStore, stateStore, fetchImpl = fetch, pendingSessionStore, outgoingCallBroker = new ArkmeOutgoingCallBroker()) {
|
|
9777
10484
|
this.config = config;
|
|
9778
10485
|
this.sessionStore = sessionStore;
|
|
@@ -9809,6 +10516,7 @@ var ArkmeService = class {
|
|
|
9809
10516
|
this.aiPolish = new GroupAiPolishService(this.runtime, this.source, { sendChatSourceTextRaw: async (...args) => await this.chat.sendChatSourceTextRaw(...args) });
|
|
9810
10517
|
this.realtime = new ChatRealtimeService(this.runtime, this.source, { chatTimelineItems: async (data, session) => await this.chat.chatTimelineItems(data, session) });
|
|
9811
10518
|
this.chat = new ChatService(this.runtime, this.source, this.profile, this.media, this.record, this.bot, this.arko, this.aiPolish, this.realtime);
|
|
10519
|
+
this.contact = new ContactService(this.runtime, this.source, this.profile, this.realtime);
|
|
9812
10520
|
this.auth = new AuthService(this.runtime, this.profile, {
|
|
9813
10521
|
reconnectChatRealtime: () => {
|
|
9814
10522
|
this.realtime.reconnect();
|
|
@@ -9826,6 +10534,7 @@ var ArkmeService = class {
|
|
|
9826
10534
|
this.interwoven.dispose();
|
|
9827
10535
|
this.world.dispose();
|
|
9828
10536
|
this.arrangement.dispose();
|
|
10537
|
+
this.contact.dispose();
|
|
9829
10538
|
}
|
|
9830
10539
|
startChatRealtime() {
|
|
9831
10540
|
return this.realtime.startChatRealtime();
|
|
@@ -9860,6 +10569,9 @@ var ArkmeService = class {
|
|
|
9860
10569
|
async createBot(input, options = {}) {
|
|
9861
10570
|
return await this.bot.createBot(input, options);
|
|
9862
10571
|
}
|
|
10572
|
+
async createBotSummary(input, options = {}) {
|
|
10573
|
+
return await this.bot.createBotSummary(input, options);
|
|
10574
|
+
}
|
|
9863
10575
|
async revealBotSecret(botRef, options = {}) {
|
|
9864
10576
|
return await this.bot.revealBotSecret(botRef, options);
|
|
9865
10577
|
}
|
|
@@ -9886,7 +10598,8 @@ var ArkmeService = class {
|
|
|
9886
10598
|
captchaId: this.config.geetestCaptchaId,
|
|
9887
10599
|
environment: this.config.environment,
|
|
9888
10600
|
testLoginEnabled: this.config.environment === "test",
|
|
9889
|
-
callAssetBasePath: `${this.config.routePath}/call
|
|
10601
|
+
callAssetBasePath: `${this.config.routePath}/call`,
|
|
10602
|
+
shareWebsite: this.config.shareWebsite ?? "https://app.arkme.ai"
|
|
9890
10603
|
};
|
|
9891
10604
|
}
|
|
9892
10605
|
providerCapabilities() {
|
|
@@ -9918,6 +10631,8 @@ var ArkmeService = class {
|
|
|
9918
10631
|
groupMemberAdd: true,
|
|
9919
10632
|
userCard: true,
|
|
9920
10633
|
openPrivateChat: true,
|
|
10634
|
+
contactAdd: true,
|
|
10635
|
+
conversationQuickAdd: true,
|
|
9921
10636
|
groupSettings: true,
|
|
9922
10637
|
extensionManagement: true,
|
|
9923
10638
|
extensionMetadataEdit: true,
|
|
@@ -9925,7 +10640,10 @@ var ArkmeService = class {
|
|
|
9925
10640
|
extensionPreviews: true,
|
|
9926
10641
|
worldFeed: true,
|
|
9927
10642
|
worldInteractions: true,
|
|
10643
|
+
worldPublish: true,
|
|
9928
10644
|
worldVoiceprintPlayback: true,
|
|
10645
|
+
worldVoiceprintInvite: true,
|
|
10646
|
+
worldVoiceprintSocialContext: true,
|
|
9929
10647
|
arrangements: true,
|
|
9930
10648
|
myExtensions: true,
|
|
9931
10649
|
extensionPublish: true,
|
|
@@ -9974,6 +10692,7 @@ var ArkmeService = class {
|
|
|
9974
10692
|
return await this.outgoingCall.releaseOutgoingCall(callRequestId);
|
|
9975
10693
|
}
|
|
9976
10694
|
dispose() {
|
|
10695
|
+
this.contact.dispose();
|
|
9977
10696
|
this.realtime.dispose();
|
|
9978
10697
|
this.arko.dispose();
|
|
9979
10698
|
this.auth.dispose();
|
|
@@ -9994,6 +10713,12 @@ var ArkmeService = class {
|
|
|
9994
10713
|
async cachedProfile() {
|
|
9995
10714
|
return await this.profile.cachedProfile();
|
|
9996
10715
|
}
|
|
10716
|
+
async searchContact(identifier, options = {}) {
|
|
10717
|
+
return await this.contact.search(identifier, options);
|
|
10718
|
+
}
|
|
10719
|
+
async addContact(contactRef, options = {}) {
|
|
10720
|
+
return await this.contact.add(contactRef, options);
|
|
10721
|
+
}
|
|
9997
10722
|
async extensionAuthors(userIds, signal) {
|
|
9998
10723
|
return await this.extensionReview.extensionAuthors(userIds, signal);
|
|
9999
10724
|
}
|
|
@@ -10141,6 +10866,9 @@ var ArkmeService = class {
|
|
|
10141
10866
|
async addGroupMembers(sourceRef, candidateRefs, signal) {
|
|
10142
10867
|
return await this.group.addGroupMembers(sourceRef, candidateRefs, signal);
|
|
10143
10868
|
}
|
|
10869
|
+
async createGroup(title, clientMutationId, options = {}) {
|
|
10870
|
+
return await this.group.createGroup(title, clientMutationId, options);
|
|
10871
|
+
}
|
|
10144
10872
|
async groupSettings(sourceRef, signal) {
|
|
10145
10873
|
return await this.group.groupSettings(sourceRef, signal);
|
|
10146
10874
|
}
|
|
@@ -10353,12 +11081,47 @@ var ArkmeService = class {
|
|
|
10353
11081
|
async listWorldFeed(options = {}) {
|
|
10354
11082
|
return await this.world.listWorldFeed(options);
|
|
10355
11083
|
}
|
|
11084
|
+
/** Build the signed-in account's World projection without exposing owner IDs. */
|
|
11085
|
+
async listMyWorldFeed(options = {}) {
|
|
11086
|
+
return await this.world.listMyWorldFeed(options);
|
|
11087
|
+
}
|
|
11088
|
+
async listUserWorldFeed(userId, options = {}) {
|
|
11089
|
+
return await this.world.listUserWorldFeed(userId, options);
|
|
11090
|
+
}
|
|
10356
11091
|
async worldVoiceprintPlaybackAvailability(recordRefs, signal) {
|
|
10357
11092
|
return await this.world.worldVoiceprintPlaybackAvailability(recordRefs, signal);
|
|
10358
11093
|
}
|
|
10359
11094
|
async generateWorldVoiceprintPlayback(input) {
|
|
10360
11095
|
return await this.world.generateWorldVoiceprintPlayback(input);
|
|
10361
11096
|
}
|
|
11097
|
+
async worldVoiceprintSocialContext(recordRef, options = {}) {
|
|
11098
|
+
return await this.world.worldVoiceprintSocialContext(recordRef, options);
|
|
11099
|
+
}
|
|
11100
|
+
async inviteWorldVoiceprint(recordRef, signal) {
|
|
11101
|
+
try {
|
|
11102
|
+
const intent = await this.world.createWorldVoiceprintInviteIntent(recordRef, signal);
|
|
11103
|
+
const privateChat = await this.chat.openPrivateChatFromUser(intent.peerUserId, {
|
|
11104
|
+
displayName: intent.peerDisplayName,
|
|
11105
|
+
...signal === void 0 ? {} : { signal }
|
|
11106
|
+
});
|
|
11107
|
+
const sent = await this.chat.sendSourceText(privateChat.source.sourceRef, worldVoiceprintInviteMessage(intent), signal === void 0 ? {} : { signal });
|
|
11108
|
+
if (sent.localState !== "synced") throw new ArkmePluginError("world-voiceprint-invite-send-pending", sent.error ?? "声纹邀请已进入发送队列,请稍后在私聊中确认", true);
|
|
11109
|
+
return {
|
|
11110
|
+
sent: true,
|
|
11111
|
+
peerDisplayName: intent.peerDisplayName,
|
|
11112
|
+
messageItemUid: sent.itemUid,
|
|
11113
|
+
expiresAtMillis: intent.expiresAtMillis
|
|
11114
|
+
};
|
|
11115
|
+
} catch (error) {
|
|
11116
|
+
const message = voiceprintInviteRateLimitMessage(error);
|
|
11117
|
+
if (message !== void 0) throw new ArkmePluginError("world-voiceprint-invite-rate-limited", message, true, 429, {
|
|
11118
|
+
cause: error,
|
|
11119
|
+
upstreamStatus: 429,
|
|
11120
|
+
...!(error instanceof ArkmePluginError) || error.retryAfterMillis === void 0 ? {} : { retryAfterMillis: error.retryAfterMillis }
|
|
11121
|
+
});
|
|
11122
|
+
throw error;
|
|
11123
|
+
}
|
|
11124
|
+
}
|
|
10362
11125
|
/** Read the authenticated comment/reply tree behind one account-bound World reference. */
|
|
10363
11126
|
async listWorldInteractions(recordRef, options = {}) {
|
|
10364
11127
|
return await this.world.listWorldInteractions(recordRef, options);
|
|
@@ -10374,6 +11137,12 @@ var ArkmeService = class {
|
|
|
10374
11137
|
async publishWorldTextForConversation(recordUid, textContent, signal) {
|
|
10375
11138
|
return await this.world.publishWorldTextForConversation(recordUid, textContent, signal);
|
|
10376
11139
|
}
|
|
11140
|
+
async publishWorldText(input) {
|
|
11141
|
+
return await this.world.publishWorldText(input);
|
|
11142
|
+
}
|
|
11143
|
+
async publishWorldFileAssets(input) {
|
|
11144
|
+
return await this.world.publishWorldFileAssets(input);
|
|
11145
|
+
}
|
|
10377
11146
|
async createText(recordUid, textContent) {
|
|
10378
11147
|
return await this.record.createText(recordUid, textContent);
|
|
10379
11148
|
}
|
|
@@ -10527,6 +11296,7 @@ function canonicalExtensionSignatureMessage(input) {
|
|
|
10527
11296
|
}), "utf8");
|
|
10528
11297
|
}
|
|
10529
11298
|
const ARKME_BUNDLE_ARTIFACT_KIND = "dsh-bundle-tgz";
|
|
11299
|
+
const ARKME_NATIVE_BUNDLE_ARTIFACT_KIND = "dsh-native-package-tgz";
|
|
10530
11300
|
const ARKME_BUNDLE_MAX_BYTES = 100 * 1024 * 1024;
|
|
10531
11301
|
const TAR_BLOCK_BYTES = 512;
|
|
10532
11302
|
const MAX_ARCHIVE_FILES = 1024;
|
|
@@ -10573,9 +11343,22 @@ function writeOctal(header, offset, length, value) {
|
|
|
10573
11343
|
header.write(octal.slice(-(length - 1)), offset, length - 1, "ascii");
|
|
10574
11344
|
header[offset + length - 1] = 0;
|
|
10575
11345
|
}
|
|
11346
|
+
function splitTarPath(path) {
|
|
11347
|
+
const direct = Buffer.from(path, "utf8");
|
|
11348
|
+
if (direct.byteLength <= 100) return { name: direct };
|
|
11349
|
+
const separators = [...path.matchAll(/\//g)].map((match) => match.index).reverse();
|
|
11350
|
+
for (const separator of separators) {
|
|
11351
|
+
const prefix = Buffer.from(path.slice(0, separator), "utf8");
|
|
11352
|
+
const name = Buffer.from(path.slice(separator + 1), "utf8");
|
|
11353
|
+
if (prefix.byteLength <= 155 && name.byteLength <= 100) return {
|
|
11354
|
+
name,
|
|
11355
|
+
prefix
|
|
11356
|
+
};
|
|
11357
|
+
}
|
|
11358
|
+
throw new ArkmeBundleArtifactError("bundle-path-too-long", `Bundle 路径过长:${path}`);
|
|
11359
|
+
}
|
|
10576
11360
|
function tarHeader(path, size) {
|
|
10577
|
-
const name =
|
|
10578
|
-
if (name.byteLength > 100) throw new ArkmeBundleArtifactError("bundle-path-too-long", `Bundle 路径过长:${path}`);
|
|
11361
|
+
const { name, prefix } = splitTarPath(path);
|
|
10579
11362
|
const header = Buffer.alloc(TAR_BLOCK_BYTES);
|
|
10580
11363
|
name.copy(header, 0);
|
|
10581
11364
|
writeOctal(header, 100, 8, 420);
|
|
@@ -10587,6 +11370,7 @@ function tarHeader(path, size) {
|
|
|
10587
11370
|
header[156] = "0".charCodeAt(0);
|
|
10588
11371
|
header.write("ustar\0", 257, 6, "ascii");
|
|
10589
11372
|
header.write("00", 263, 2, "ascii");
|
|
11373
|
+
prefix?.copy(header, 345);
|
|
10590
11374
|
writeOctal(header, 148, 8, [...header].reduce((sum, byte) => sum + byte, 0));
|
|
10591
11375
|
return header;
|
|
10592
11376
|
}
|
|
@@ -10621,7 +11405,9 @@ function readTar(tar) {
|
|
|
10621
11405
|
while (offset + TAR_BLOCK_BYTES <= tar.byteLength) {
|
|
10622
11406
|
const header = tar.subarray(offset, offset + TAR_BLOCK_BYTES);
|
|
10623
11407
|
if (header.every((byte) => byte === 0)) break;
|
|
10624
|
-
const
|
|
11408
|
+
const name = header.subarray(0, 100).toString("utf8").replace(/\0.*$/, "");
|
|
11409
|
+
const prefix = header.subarray(345, 500).toString("utf8").replace(/\0.*$/, "");
|
|
11410
|
+
const path = prefix === "" ? name : `${prefix}/${name}`;
|
|
10625
11411
|
validateArchivePath(path);
|
|
10626
11412
|
if (files.has(path)) throw new ArkmeBundleArtifactError("bundle-duplicate-path", `Bundle 包含重复路径:${path}`);
|
|
10627
11413
|
const type = header[156];
|
|
@@ -10706,6 +11492,105 @@ function validatePatch(raw, packageName, executionModel) {
|
|
|
10706
11492
|
if (ids.size === 0) throw new ArkmeBundleArtifactError("bundle-patch-empty", "Bundle patch 至少需要一条 row");
|
|
10707
11493
|
return [...ids].sort();
|
|
10708
11494
|
}
|
|
11495
|
+
const NATIVE_LIFECYCLE_SCRIPTS = /* @__PURE__ */ new Set([
|
|
11496
|
+
"preinstall",
|
|
11497
|
+
"install",
|
|
11498
|
+
"postinstall",
|
|
11499
|
+
"prepare",
|
|
11500
|
+
"prepublish",
|
|
11501
|
+
"prepublishOnly",
|
|
11502
|
+
"prepack",
|
|
11503
|
+
"postpack"
|
|
11504
|
+
]);
|
|
11505
|
+
function nonEmptyManifestValue(value) {
|
|
11506
|
+
if (value === void 0 || value === null) return false;
|
|
11507
|
+
if (typeof value === "string") return value.trim() !== "";
|
|
11508
|
+
if (Array.isArray(value)) return value.length > 0;
|
|
11509
|
+
if (typeof value === "object") return Object.keys(value).length > 0;
|
|
11510
|
+
if (typeof value === "boolean") return value;
|
|
11511
|
+
return true;
|
|
11512
|
+
}
|
|
11513
|
+
function inspectNativePatch(raw, packageName) {
|
|
11514
|
+
const document = parseDocument(raw.toString("utf8"), { uniqueKeys: true });
|
|
11515
|
+
if (document.errors.length > 0) throw new ArkmeBundleArtifactError("bundle-patch-invalid", "Bundle patch YAML 格式无效");
|
|
11516
|
+
const value = document.toJS();
|
|
11517
|
+
if (!Array.isArray(value)) throw new ArkmeBundleArtifactError("bundle-patch-invalid", "Bundle patch 必须是操作数组");
|
|
11518
|
+
const patchIds = /* @__PURE__ */ new Set();
|
|
11519
|
+
const capabilities = /* @__PURE__ */ new Set();
|
|
11520
|
+
for (const operation of value) {
|
|
11521
|
+
if (operation === null || typeof operation !== "object" || Array.isArray(operation)) {
|
|
11522
|
+
capabilities.add("profile_patch_override");
|
|
11523
|
+
continue;
|
|
11524
|
+
}
|
|
11525
|
+
const record = operation;
|
|
11526
|
+
if (!(Object.keys(record).length === 1 && Array.isArray(record.insert))) {
|
|
11527
|
+
capabilities.add("profile_patch_override");
|
|
11528
|
+
if (typeof record.id === "string" && record.id.trim() !== "") patchIds.add(record.id.trim());
|
|
11529
|
+
continue;
|
|
11530
|
+
}
|
|
11531
|
+
for (const row of record.insert) {
|
|
11532
|
+
if (row === null || typeof row !== "object" || Array.isArray(row)) continue;
|
|
11533
|
+
const item = row;
|
|
11534
|
+
if (typeof item.id === "string" && item.id.trim() !== "") patchIds.add(item.id.trim());
|
|
11535
|
+
if (typeof item.name === "string" && item.name !== packageName && !item.name.startsWith(`${packageName}/`)) capabilities.add("external_package_reference");
|
|
11536
|
+
}
|
|
11537
|
+
}
|
|
11538
|
+
return {
|
|
11539
|
+
patchIds: [...patchIds].sort(),
|
|
11540
|
+
capabilities: [...capabilities].sort()
|
|
11541
|
+
};
|
|
11542
|
+
}
|
|
11543
|
+
function inspectNativeManifest(manifest, files) {
|
|
11544
|
+
if (typeof manifest.name !== "string" || manifest.name.length > 214 || !PACKAGE_NAME.test(manifest.name) || manifest.name.startsWith("@deepseek-ai/") || manifest.name === "@senguoyun/dsh-arkme" || manifest.name.startsWith("@arkme-local/")) throw new ArkmeBundleArtifactError("bundle-package-name-invalid", "Bundle package name 无效或属于保留集合");
|
|
11545
|
+
if (typeof manifest.version !== "string" || !SEMVER.test(manifest.version)) throw new ArkmeBundleArtifactError("bundle-version-invalid", "Bundle version 必须是严格 SemVer");
|
|
11546
|
+
if (manifest.dsh?.arkme !== void 0 && manifest.dsh.arkme !== null) throw new ArkmeBundleArtifactError("bundle-execution-model-invalid", "原生 DSH V3 Bundle 不允许声明 Arkme sandbox marker");
|
|
11547
|
+
const patch = manifest.dsh?.bundle?.patch;
|
|
11548
|
+
if (typeof patch !== "string" || patch.trim() === "" || patch.includes("\\") || patch.startsWith("/")) throw new ArkmeBundleArtifactError("bundle-patch-missing", "Bundle 必须声明包内 dsh.bundle.patch");
|
|
11549
|
+
const patchPath = `package/${posix.normalize(patch.replace(/^\.\//, ""))}`;
|
|
11550
|
+
if (!files.has(patchPath) || patchPath.includes("/../")) throw new ArkmeBundleArtifactError("bundle-patch-missing", "Bundle patch 不在包内");
|
|
11551
|
+
const capabilities = /* @__PURE__ */ new Set();
|
|
11552
|
+
if (nonEmptyManifestValue(manifest.dependencies)) capabilities.add("runtime_dependencies");
|
|
11553
|
+
if (nonEmptyManifestValue(manifest.optionalDependencies)) capabilities.add("optional_dependencies");
|
|
11554
|
+
if (nonEmptyManifestValue(manifest.bundledDependencies) || nonEmptyManifestValue(manifest.bundleDependencies)) capabilities.add("bundled_dependencies");
|
|
11555
|
+
for (const name of Object.keys(manifest.peerDependencies ?? {})) if (!name.startsWith("@deepseek-ai/") && name !== "@senguoyun/dsh-arkme" && name !== "react" && name !== "react-dom") {
|
|
11556
|
+
capabilities.add("peer_dependencies");
|
|
11557
|
+
break;
|
|
11558
|
+
}
|
|
11559
|
+
if (Object.entries(manifest.scripts ?? {}).some(([name, value]) => NATIVE_LIFECYCLE_SCRIPTS.has(name) && nonEmptyManifestValue(value))) capabilities.add("lifecycle_scripts");
|
|
11560
|
+
if (nonEmptyManifestValue(manifest.bin)) capabilities.add("bin");
|
|
11561
|
+
if ([...files.keys()].some((name) => name.toLowerCase().endsWith(".node"))) capabilities.add("native_addon");
|
|
11562
|
+
return {
|
|
11563
|
+
patchPath,
|
|
11564
|
+
capabilities: [...capabilities].sort()
|
|
11565
|
+
};
|
|
11566
|
+
}
|
|
11567
|
+
function inspectNativeBundleArtifactV3(bytes) {
|
|
11568
|
+
assertArchiveSize(bytes.byteLength, "Bundle");
|
|
11569
|
+
let tar;
|
|
11570
|
+
try {
|
|
11571
|
+
tar = gunzipSync(bytes, { maxOutputLength: 105907200 });
|
|
11572
|
+
} catch {
|
|
11573
|
+
throw new ArkmeBundleArtifactError("bundle-gzip-invalid", "Bundle 不是有效 gzip");
|
|
11574
|
+
}
|
|
11575
|
+
const files = readTar(tar);
|
|
11576
|
+
const packageJSON = files.get("package/package.json");
|
|
11577
|
+
if (packageJSON === void 0) throw new ArkmeBundleArtifactError("bundle-package-json-missing", "Bundle 缺少 package.json");
|
|
11578
|
+
const manifest = readManifest(packageJSON);
|
|
11579
|
+
const inspected = inspectNativeManifest(manifest, files);
|
|
11580
|
+
const patch = inspectNativePatch(files.get(inspected.patchPath), manifest.name);
|
|
11581
|
+
const nativeCapabilities = [.../* @__PURE__ */ new Set([...inspected.capabilities, ...patch.capabilities])].sort();
|
|
11582
|
+
return {
|
|
11583
|
+
bytes: Buffer.from(bytes),
|
|
11584
|
+
bundleSha256: bundleSha256(bytes),
|
|
11585
|
+
packageJsonSha256: bundleSha256(packageJSON),
|
|
11586
|
+
packageName: manifest.name,
|
|
11587
|
+
version: manifest.version,
|
|
11588
|
+
executionModel: "dsh-native",
|
|
11589
|
+
nativeCapabilities,
|
|
11590
|
+
files,
|
|
11591
|
+
patchIds: patch.patchIds
|
|
11592
|
+
};
|
|
11593
|
+
}
|
|
10709
11594
|
function inspectBundleArtifact(bytes) {
|
|
10710
11595
|
assertArchiveSize(bytes.byteLength, "Bundle");
|
|
10711
11596
|
let tar;
|
|
@@ -10736,29 +11621,114 @@ function pathInside(root, target) {
|
|
|
10736
11621
|
const fromRoot = relative(root, target);
|
|
10737
11622
|
return fromRoot === "" || !fromRoot.startsWith(`..${sep}`) && fromRoot !== ".." && !isAbsolute(fromRoot);
|
|
10738
11623
|
}
|
|
10739
|
-
function
|
|
11624
|
+
function globPattern(pattern) {
|
|
11625
|
+
let expression = "^";
|
|
11626
|
+
for (let index = 0; index < pattern.length; index += 1) {
|
|
11627
|
+
const character = pattern[index];
|
|
11628
|
+
if (character === "*") if (pattern[index + 1] === "*") {
|
|
11629
|
+
index += 1;
|
|
11630
|
+
if (pattern[index + 1] === "/") {
|
|
11631
|
+
index += 1;
|
|
11632
|
+
expression += "(?:.*/)?";
|
|
11633
|
+
} else expression += ".*";
|
|
11634
|
+
} else expression += "[^/]*";
|
|
11635
|
+
else if (character === "?") expression += "[^/]";
|
|
11636
|
+
else expression += character.replace(/[|\\{}()[\]^$+?.]/g, "\\$&");
|
|
11637
|
+
}
|
|
11638
|
+
return new RegExp(`${expression}$`);
|
|
11639
|
+
}
|
|
11640
|
+
function collectPackageFiles(root, manifest, nativeV3 = false) {
|
|
10740
11641
|
const canonicalRoot = realpathSync(root);
|
|
10741
11642
|
const selected = /* @__PURE__ */ new Map();
|
|
10742
|
-
const
|
|
11643
|
+
const ignoredNativeRootEntries = /* @__PURE__ */ new Set([
|
|
11644
|
+
".git",
|
|
11645
|
+
".hg",
|
|
11646
|
+
".svn",
|
|
11647
|
+
"node_modules"
|
|
11648
|
+
]);
|
|
11649
|
+
const ignoredNativeFiles = /* @__PURE__ */ new Set([
|
|
11650
|
+
".npmrc",
|
|
11651
|
+
".yarnrc",
|
|
11652
|
+
".yarnrc.yml",
|
|
11653
|
+
".pnpmfile.cjs"
|
|
11654
|
+
]);
|
|
11655
|
+
const add = (absolute, allowIgnore = false) => {
|
|
10743
11656
|
const stat = lstatSync(absolute);
|
|
10744
|
-
|
|
11657
|
+
const rel = relative(canonicalRoot, absolute).split(sep).join("/");
|
|
11658
|
+
const first = rel.split("/")[0] ?? "";
|
|
11659
|
+
const basename = rel.split("/").at(-1) ?? "";
|
|
11660
|
+
if (allowIgnore && (ignoredNativeRootEntries.has(first) || ignoredNativeFiles.has(basename) || basename === ".env" || basename.startsWith(".env."))) return;
|
|
11661
|
+
if (stat.isSymbolicLink()) {
|
|
11662
|
+
if (allowIgnore) return;
|
|
11663
|
+
throw new ArkmeBundleArtifactError("bundle-link-forbidden", "Bundle source 不允许符号链接");
|
|
11664
|
+
}
|
|
10745
11665
|
const canonical = realpathSync(absolute);
|
|
10746
11666
|
if (!pathInside(canonicalRoot, canonical)) throw new ArkmeBundleArtifactError("bundle-path-invalid", "Bundle source 逃逸 package root");
|
|
10747
11667
|
if (stat.isDirectory()) {
|
|
10748
|
-
for (const child of readdirSync(canonical).sort()) add(join(canonical, child));
|
|
11668
|
+
for (const child of readdirSync(canonical).sort()) add(join(canonical, child), allowIgnore);
|
|
10749
11669
|
return;
|
|
10750
11670
|
}
|
|
10751
11671
|
if (!stat.isFile()) throw new ArkmeBundleArtifactError("bundle-file-type-forbidden", "Bundle source 只允许普通文件");
|
|
10752
|
-
const
|
|
10753
|
-
selected.set(`package/${
|
|
11672
|
+
const canonicalRel = relative(canonicalRoot, canonical).split(sep).join("/");
|
|
11673
|
+
selected.set(`package/${canonicalRel}`, readFileSync(canonical));
|
|
11674
|
+
};
|
|
11675
|
+
const addNativePackageExtras = () => {
|
|
11676
|
+
if (!nativeV3) return;
|
|
11677
|
+
for (const child of readdirSync(canonicalRoot).sort()) if (/^(?:readme|licen[cs]e|copying|notice)(?:\.|$)/i.test(child)) add(join(canonicalRoot, child), true);
|
|
11678
|
+
const paths = /* @__PURE__ */ new Set();
|
|
11679
|
+
if (typeof manifest.main === "string") paths.add(manifest.main);
|
|
11680
|
+
const bin = manifest.bin;
|
|
11681
|
+
if (typeof bin === "string") paths.add(bin);
|
|
11682
|
+
else if (bin !== null && typeof bin === "object" && !Array.isArray(bin)) {
|
|
11683
|
+
for (const value of Object.values(bin)) if (typeof value === "string") paths.add(value);
|
|
11684
|
+
}
|
|
11685
|
+
const patch = manifest.dsh?.bundle?.patch;
|
|
11686
|
+
if (typeof patch === "string") paths.add(patch);
|
|
11687
|
+
for (const entry of paths) {
|
|
11688
|
+
const target = resolve(canonicalRoot, entry);
|
|
11689
|
+
if (pathInside(canonicalRoot, target) && existsSync(target)) add(target, true);
|
|
11690
|
+
}
|
|
10754
11691
|
};
|
|
10755
11692
|
add(join(canonicalRoot, "package.json"));
|
|
10756
|
-
|
|
10757
|
-
|
|
11693
|
+
const entries = manifest.files ?? [];
|
|
11694
|
+
if (entries.length === 0) {
|
|
11695
|
+
if (!nativeV3) throw new ArkmeBundleArtifactError("bundle-files-missing", "Bundle 必须显式声明 files");
|
|
11696
|
+
for (const child of readdirSync(canonicalRoot).sort()) if (child !== "package.json") add(join(canonicalRoot, child), true);
|
|
11697
|
+
addNativePackageExtras();
|
|
11698
|
+
return selected;
|
|
11699
|
+
}
|
|
11700
|
+
let candidates;
|
|
11701
|
+
const listCandidates = () => {
|
|
11702
|
+
if (candidates !== void 0) return candidates;
|
|
11703
|
+
candidates = [];
|
|
11704
|
+
const walk = (directory) => {
|
|
11705
|
+
for (const child of readdirSync(directory).sort()) {
|
|
11706
|
+
const absolute = join(directory, child);
|
|
11707
|
+
const stat = lstatSync(absolute);
|
|
11708
|
+
if (stat.isSymbolicLink()) continue;
|
|
11709
|
+
if (stat.isDirectory()) walk(absolute);
|
|
11710
|
+
else if (stat.isFile()) candidates.push(relative(canonicalRoot, absolute).split(sep).join("/"));
|
|
11711
|
+
}
|
|
11712
|
+
};
|
|
11713
|
+
walk(canonicalRoot);
|
|
11714
|
+
return candidates;
|
|
11715
|
+
};
|
|
11716
|
+
for (const entry of entries) {
|
|
11717
|
+
if (entry.includes("\\") || entry.startsWith("/") || entry.split("/").includes("..")) throw new ArkmeBundleArtifactError("bundle-files-invalid", `Bundle files 路径无效:${entry}`);
|
|
11718
|
+
const hasGlob = entry.includes("*") || entry.includes("?") || entry.includes("[");
|
|
11719
|
+
if (hasGlob && !nativeV3) throw new ArkmeBundleArtifactError("bundle-files-invalid", "Bundle files 暂不接受 glob 或反斜杠");
|
|
11720
|
+
if (hasGlob) {
|
|
11721
|
+
const matcher = globPattern(entry.replace(/^\.\//, ""));
|
|
11722
|
+
const matches = listCandidates().filter((candidate) => matcher.test(candidate));
|
|
11723
|
+
if (matches.length === 0) throw new ArkmeBundleArtifactError("bundle-files-invalid", `Bundle files 路径无效:${entry}`);
|
|
11724
|
+
for (const match of matches) add(resolve(canonicalRoot, match), true);
|
|
11725
|
+
continue;
|
|
11726
|
+
}
|
|
10758
11727
|
const target = resolve(canonicalRoot, entry);
|
|
10759
|
-
if (!pathInside(canonicalRoot, target) || !statSync(target).isFile() && !statSync(target).isDirectory()) throw new ArkmeBundleArtifactError("bundle-files-invalid", `Bundle files 路径无效:${entry}`);
|
|
10760
|
-
add(target);
|
|
11728
|
+
if (!pathInside(canonicalRoot, target) || !existsSync(target) || !statSync(target).isFile() && !statSync(target).isDirectory()) throw new ArkmeBundleArtifactError("bundle-files-invalid", `Bundle files 路径无效:${entry}`);
|
|
11729
|
+
add(target, nativeV3);
|
|
10761
11730
|
}
|
|
11731
|
+
addNativePackageExtras();
|
|
10762
11732
|
return selected;
|
|
10763
11733
|
}
|
|
10764
11734
|
function packBundleFiles(files) {
|
|
@@ -10772,14 +11742,25 @@ function packBundleFiles(files) {
|
|
|
10772
11742
|
}
|
|
10773
11743
|
};
|
|
10774
11744
|
}
|
|
10775
|
-
function
|
|
10776
|
-
|
|
11745
|
+
function packNativeBundleFiles(files) {
|
|
11746
|
+
const bytes = gzipSync(createTar(files), { level: 9 });
|
|
11747
|
+
assertArchiveSize(bytes.byteLength, "Bundle");
|
|
11748
|
+
return {
|
|
11749
|
+
bundle: inspectNativeBundleArtifactV3(bytes),
|
|
11750
|
+
source: {
|
|
11751
|
+
bytes: Buffer.from(bytes),
|
|
11752
|
+
sourceSha256: bundleSha256(bytes)
|
|
11753
|
+
}
|
|
11754
|
+
};
|
|
11755
|
+
}
|
|
11756
|
+
function packLocalNativeBundleDirectoryV3(directory) {
|
|
11757
|
+
return packNativeBundleFiles(collectPackageFiles(directory, readManifest(readFileSync(join(directory, "package.json"))), true));
|
|
10777
11758
|
}
|
|
10778
|
-
function
|
|
11759
|
+
function readNativeBundleTarballV3(path) {
|
|
10779
11760
|
if (!lstatSync(path).isFile()) throw new ArkmeBundleArtifactError("bundle-tarball-invalid", "本地 Bundle tgz 必须是普通文件");
|
|
10780
11761
|
const bytes = readFileSync(path);
|
|
10781
11762
|
return {
|
|
10782
|
-
bundle:
|
|
11763
|
+
bundle: inspectNativeBundleArtifactV3(bytes),
|
|
10783
11764
|
source: {
|
|
10784
11765
|
bytes: Buffer.from(bytes),
|
|
10785
11766
|
sourceSha256: bundleSha256(bytes)
|
|
@@ -10854,6 +11835,39 @@ function verifyBundleResolutionSignature(resolution, trustedKeys) {
|
|
|
10854
11835
|
}
|
|
10855
11836
|
if (!valid) throw new ArkmePluginError("extension-signature-invalid", "扩展 Bundle 平台签名验证失败", false, 403);
|
|
10856
11837
|
}
|
|
11838
|
+
function canonicalNativeBundleSignatureMessage(input) {
|
|
11839
|
+
return Buffer.from(JSON.stringify({
|
|
11840
|
+
format_version: input.artifact_contract_version,
|
|
11841
|
+
artifact_kind: input.artifact_kind,
|
|
11842
|
+
extension_id: input.extension_id,
|
|
11843
|
+
package_name: input.package_name,
|
|
11844
|
+
version: input.version,
|
|
11845
|
+
execution_model: input.execution_model,
|
|
11846
|
+
bundle_sha256: input.bundle_sha256,
|
|
11847
|
+
package_json_sha256: input.package_json_sha256,
|
|
11848
|
+
source_sha256: input.source_sha256,
|
|
11849
|
+
...input.native_capabilities.length === 0 ? {} : { native_capabilities: input.native_capabilities },
|
|
11850
|
+
published_at: input.published_at,
|
|
11851
|
+
signing_key_id: input.signing_key_id
|
|
11852
|
+
}), "utf8");
|
|
11853
|
+
}
|
|
11854
|
+
function verifyNativeBundleResolutionSignature(resolution, trustedKeys) {
|
|
11855
|
+
const capabilities = resolution.native_capabilities;
|
|
11856
|
+
if (resolution.artifact_contract_version !== 3 || resolution.artifact_kind !== "dsh-native-package-tgz" || resolution.package_name.trim() === "" || resolution.execution_model !== "dsh-native" || ![
|
|
11857
|
+
resolution.bundle_sha256,
|
|
11858
|
+
resolution.package_json_sha256,
|
|
11859
|
+
resolution.source_sha256
|
|
11860
|
+
].every((value) => /^[a-f0-9]{64}$/.test(value)) || !Array.isArray(capabilities) || capabilities.some((value) => typeof value !== "string" || value.trim() === "") || capabilities.some((value, index) => index > 0 && capabilities[index - 1] >= value) || !Number.isSafeInteger(resolution.published_at) || resolution.published_at <= 0 || resolution.signature.trim() === "") throw new ArkmePluginError("extension-signature-envelope-invalid", "原生 DSH V3 签名 envelope 字段无效", false, 502);
|
|
11861
|
+
const key = trustedKeys.get(resolution.signing_key_id);
|
|
11862
|
+
if (key === void 0) throw new ArkmePluginError("extension-signing-key-untrusted", `不信任扩展签名密钥 ${resolution.signing_key_id}`, false, 403);
|
|
11863
|
+
let valid = false;
|
|
11864
|
+
try {
|
|
11865
|
+
valid = verify(null, canonicalNativeBundleSignatureMessage(resolution), publicKey(key), Buffer.from(resolution.signature, "base64"));
|
|
11866
|
+
} catch (error) {
|
|
11867
|
+
throw new ArkmePluginError("extension-signature-invalid", "原生 DSH V3 签名格式无效", false, 403, { cause: error });
|
|
11868
|
+
}
|
|
11869
|
+
if (!valid) throw new ArkmePluginError("extension-signature-invalid", "原生 DSH V3 平台签名验证失败", false, 403);
|
|
11870
|
+
}
|
|
10857
11871
|
//#endregion
|
|
10858
11872
|
//#region src/extensions/persistent-client-bundle.ts
|
|
10859
11873
|
/** This function is serialized into each generated browser bundle. It must stay closure-free. */
|
|
@@ -11513,4 +12527,4 @@ async function invokePersistentArkmeExtension(extensionId, method, args) {
|
|
|
11513
12527
|
return jsonValue(await handler(jsonValue(args)));
|
|
11514
12528
|
}
|
|
11515
12529
|
//#endregion
|
|
11516
|
-
export {
|
|
12530
|
+
export { unpackArkmeExtension as A, ARKME_WORLD_PUBLISH_MAX_IMAGE_BYTES as B, packBundleFiles as C, assertExtensionArtifactSize as D, renderArkmeSandboxHostEntry as E, ARKME_EXTENSION_RUNTIME_UNAVAILABLE_MESSAGE as F, ArkmePluginError as H, ArkmeService as I, ARKME_DEFAULT_SHARE_WEBSITE as L, ARKME_EXTENSION_ICON_MAX_BYTES as M, ARKME_EXTENSION_MAX_BYTES as N, canonicalExtensionJson as O, ARKME_EXTENSION_PREVIEW_MAX_BYTES as P, ARKME_PROVIDER_CONTRACT_VERSION as R, inspectNativeBundleArtifactV3 as S, readNativeBundleTarballV3 as T, ArkmeOutgoingCallError as U, isArkmeBotAvatarRef as V, ArkmeBundleArtifactError as _, persistentArkmeExtensionActive as a, canonicalBundleJson as b, readPersistentExtensionActivation as c, verifyBundleResolutionSignature as d, verifyExtensionResolutionSignature as f, ARKME_NATIVE_BUNDLE_ARTIFACT_KIND as g, ARKME_BUNDLE_MAX_BYTES as h, invokePersistentArkmeExtension as i, ARKME_EXTENSION_FORMAT as j, sha256Hex as k, writePersistentExtensionActivation as l, ARKME_BUNDLE_ARTIFACT_KIND as m, applyPersistentArkmeHostExtension as n, persistentArkmeExtensionRuntimeState as o, verifyNativeBundleResolutionSignature as p, deactivatePersistentArkmeExtension as r, materializePersistentExtensionBundle as s, activatePersistentArkmeExtension as t, renderArkmeBundleClientBundle as u, arkmeSandboxEntryId as v, packLocalNativeBundleDirectoryV3 as w, inspectBundleArtifact as x, bundleSha256 as y, ARKME_WORLD_PUBLISH_MAX_IMAGES as z };
|