@senguoyun/dsh-arkme 0.1.28 → 0.1.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/client.js +4257 -3404
- package/lib/client.js.map +1 -1
- package/lib/extension-profile-restart-helper.js +1 -1
- package/lib/index.js +314 -18
- package/lib/persistent-extension.js +1 -1
- package/lib/{persistent-runtime-CaPUsLwg.js → persistent-runtime-DLKV_LTp.js} +768 -380
- package/lib/{plugin-updater-helper-Ddppbids.js → plugin-updater-helper-BByro9U_.js} +1 -1
- package/lib/{plugin-updater-helper-Lmz7W2ph.js → plugin-updater-helper-BByw0ZyG.js} +171 -13
- package/lib/plugin-updater-helper.js +1 -1
- package/lib/types/arkme-service.d.ts +17 -4
- package/lib/types/arkme-service.d.ts.map +1 -1
- package/lib/types/billing-gateway.d.ts +28 -0
- package/lib/types/billing-gateway.d.ts.map +1 -0
- package/lib/types/client/ArkmeBillingSettings.d.ts +52 -0
- package/lib/types/client/ArkmeBillingSettings.d.ts.map +1 -0
- package/lib/types/client/ArkmeCallSurface.d.ts.map +1 -1
- package/lib/types/client/ArkmeLogin.d.ts +3 -1
- package/lib/types/client/ArkmeLogin.d.ts.map +1 -1
- package/lib/types/client/ArkmeSettingsSurface.d.ts +18 -0
- package/lib/types/client/ArkmeSettingsSurface.d.ts.map +1 -1
- package/lib/types/client/ArkmeSidebar.d.ts +3 -3
- package/lib/types/client/ArkmeSidebar.d.ts.map +1 -1
- package/lib/types/client/arkme-auth-flow.d.ts +1 -0
- package/lib/types/client/arkme-auth-flow.d.ts.map +1 -1
- package/lib/types/client/arkme-billing.d.ts +37 -0
- package/lib/types/client/arkme-billing.d.ts.map +1 -0
- package/lib/types/client/arkme-login-locales.d.ts +5 -0
- package/lib/types/client/arkme-login-locales.d.ts.map +1 -1
- package/lib/types/client/chat-directory-store.d.ts.map +1 -1
- package/lib/types/host-api.d.ts.map +1 -1
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/managed-ai/adapter.d.ts +20 -0
- package/lib/types/managed-ai/adapter.d.ts.map +1 -0
- package/lib/types/managed-ai/credential.d.ts +11 -0
- package/lib/types/managed-ai/credential.d.ts.map +1 -0
- package/lib/types/plugin-update-lifecycle-log.d.ts +17 -0
- package/lib/types/plugin-update-lifecycle-log.d.ts.map +1 -0
- package/lib/types/plugin-update.d.ts +3 -0
- package/lib/types/plugin-update.d.ts.map +1 -1
- package/lib/types/plugin-updater-helper.d.ts +1 -2
- package/lib/types/plugin-updater-helper.d.ts.map +1 -1
- package/lib/types/profile-plugin-entry.d.ts +6 -0
- package/lib/types/profile-plugin-entry.d.ts.map +1 -0
- package/lib/types/services/auth-service.d.ts +10 -0
- package/lib/types/services/auth-service.d.ts.map +1 -1
- package/lib/types/types.d.ts +56 -1
- package/lib/types/types.d.ts.map +1 -1
- package/package.json +12 -6
|
@@ -10,6 +10,37 @@ import { createContext, runInContext } from "node:vm";
|
|
|
10
10
|
import { Context } from "@deepseek-ai/cordis";
|
|
11
11
|
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
12
12
|
import { parseDocument } from "yaml";
|
|
13
|
+
//#region src/secret-value.ts
|
|
14
|
+
var SecretValue = class {
|
|
15
|
+
#value;
|
|
16
|
+
constructor(value) {
|
|
17
|
+
this.#value = value;
|
|
18
|
+
}
|
|
19
|
+
reveal() {
|
|
20
|
+
return this.#value;
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
//#endregion
|
|
24
|
+
//#region src/managed-ai/credential.ts
|
|
25
|
+
const MAX_JWT_PAYLOAD_CHARS = 16 * 1024;
|
|
26
|
+
/** Resolve the Host-owned bearer, refreshing a near-expiry JWT before it reaches the relay. */
|
|
27
|
+
async function resolveManagedAccessCredential(runtime, refreshWindowMillis = 6e4) {
|
|
28
|
+
let session = await runtime.requireSession();
|
|
29
|
+
if (managedAccessTokenExpiresWithin(session.accessToken, refreshWindowMillis)) session = await runtime.refreshAccessToken(session);
|
|
30
|
+
return new SecretValue(session.accessToken);
|
|
31
|
+
}
|
|
32
|
+
/** Treat a parseable JWT expiry as a refresh hint; opaque access tokens remain usable. */
|
|
33
|
+
function managedAccessTokenExpiresWithin(accessToken, windowMillis, nowMillis = Date.now()) {
|
|
34
|
+
const payload = accessToken.split(".")[1];
|
|
35
|
+
if (payload === void 0 || payload === "" || payload.length > MAX_JWT_PAYLOAD_CHARS) return false;
|
|
36
|
+
try {
|
|
37
|
+
const value = JSON.parse(Buffer.from(payload, "base64url").toString("utf8"));
|
|
38
|
+
return typeof value.exp === "number" && Number.isSafeInteger(value.exp) && value.exp > 0 && value.exp * 1e3 - nowMillis < windowMillis;
|
|
39
|
+
} catch {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
//#endregion
|
|
13
44
|
//#region src/outgoing-call-contract.ts
|
|
14
45
|
var ArkmeOutgoingCallError = class extends Error {
|
|
15
46
|
code;
|
|
@@ -276,373 +307,33 @@ var ArkmeOutgoingCallBroker = class {
|
|
|
276
307
|
}
|
|
277
308
|
}
|
|
278
309
|
}
|
|
279
|
-
expireLease(userId) {
|
|
280
|
-
const current = this.leases.get(userId);
|
|
281
|
-
if (current !== void 0 && current.expiresAtMillis <= this.now()) {
|
|
282
|
-
this.leases.delete(userId);
|
|
283
|
-
callDiag$1("lease_expired", {
|
|
284
|
-
userId,
|
|
285
|
-
callRequestId: current.callRequestId,
|
|
286
|
-
expiresAtMillis: current.expiresAtMillis
|
|
287
|
-
});
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
removeIntent(intent) {
|
|
291
|
-
this.intents.delete(intent.intentId);
|
|
292
|
-
this.clearTimer(intent.timer);
|
|
293
|
-
this.detachAbort(intent);
|
|
294
|
-
callDiag$1("intent_removed", {
|
|
295
|
-
userId: intent.userId,
|
|
296
|
-
intentId: intent.intentId,
|
|
297
|
-
callRequestId: intent.callRequestId,
|
|
298
|
-
claimed: intent.claimed,
|
|
299
|
-
toolSettled: intent.toolSettled
|
|
300
|
-
});
|
|
301
|
-
}
|
|
302
|
-
detachAbort(intent) {
|
|
303
|
-
if (intent.signal !== void 0 && intent.abort !== void 0) intent.signal.removeEventListener("abort", intent.abort);
|
|
304
|
-
}
|
|
305
|
-
};
|
|
306
|
-
const contentCopies = {
|
|
307
|
-
unclear: {
|
|
308
|
-
prompt: "有点看不懂,反而更想听TA怎么说",
|
|
309
|
-
alternatePrompts: ["越看不懂,越想听TA亲口表达", "这段有点跳脱,想听TA怎么读"],
|
|
310
|
-
invitationReactions: [
|
|
311
|
-
"我有点没看懂,却不是想划走,反而更想听你亲口解释它是什么意思。",
|
|
312
|
-
"这段像藏着一点只有你知道的意思,让我很好奇你会怎么说出来。",
|
|
313
|
-
"它读起来有点跳脱,也正因为这样,我更想听见你自己的语气。",
|
|
314
|
-
"我盯着这段看了一会儿,还是觉得由你亲口讲出来一定更明白。"
|
|
315
|
-
]
|
|
316
|
-
},
|
|
317
|
-
numeric: {
|
|
318
|
-
prompt: "像一串暗号,想听TA怎么读",
|
|
319
|
-
alternatePrompts: ["这串数字藏着什么,想听TA念出来", "看起来像个暗号,想听TA怎么解开"],
|
|
320
|
-
invitationReactions: [
|
|
321
|
-
"这串内容看起来像个小暗号,我很好奇它从你嘴里说出来会是什么语气。",
|
|
322
|
-
"我看到它时忍不住猜了好一会儿,真想听你亲口把这个暗号解开。",
|
|
323
|
-
"只有这些字符,反而给人留下了很多想象,我想听听你会怎么读。",
|
|
324
|
-
"它越简短越让人好奇,感觉只有听你说出来才算完整。"
|
|
325
|
-
]
|
|
326
|
-
},
|
|
327
|
-
short: {
|
|
328
|
-
prompt: "字不多,更想听TA怎么说",
|
|
329
|
-
alternatePrompts: ["只有几个字,反而更好奇TA的语气", "话很短,想听TA会怎么念"],
|
|
330
|
-
invitationReactions: [
|
|
331
|
-
"你只写了短短几个字,反而让我更好奇你当时真正的语气。",
|
|
332
|
-
"这句话很短,却挺有余味的,我很想听你亲口说一遍。",
|
|
333
|
-
"字不多,但感觉里面还有没写出来的意思,想听听你会怎么表达。",
|
|
334
|
-
"我看到这句时停了一下,总觉得带上你的声音会更有感觉。"
|
|
335
|
-
]
|
|
336
|
-
},
|
|
337
|
-
sad: {
|
|
338
|
-
prompt: "文字里有些情绪,更想听见TA的语气",
|
|
339
|
-
alternatePrompts: ["这段情绪藏在字里,想听TA怎么说", "有些感受看得见,也想听得见"],
|
|
340
|
-
invitationReactions: [
|
|
341
|
-
"读到这里有点心疼。文字能写下经历,但我更想听见你当时真正的语气。",
|
|
342
|
-
"这段话里的情绪很真实,让我没有办法只把它当成普通文字划过去。",
|
|
343
|
-
"看到这些话,我很想更认真地听你说说,而不只是隔着文字猜你的感受。",
|
|
344
|
-
"有些心情写出来已经很不容易了,如果能听见你的声音,也许会更懂你。"
|
|
345
|
-
]
|
|
346
|
-
},
|
|
347
|
-
funny: {
|
|
348
|
-
prompt: "这段很有画面,带上声音会更有趣",
|
|
349
|
-
alternatePrompts: ["光看文字就有画面,想听TA怎么演绎", "这段有点好玩,想听TA会怎么说"],
|
|
350
|
-
invitationReactions: [
|
|
351
|
-
"光看文字就已经很有画面了,我开始好奇你亲口讲出来会有多好玩。",
|
|
352
|
-
"我看到这里真的笑了一下,要是配上你的语气,感觉一定更有意思。",
|
|
353
|
-
"这段太适合听你现场讲了,文字已经有趣,声音应该会更生动。",
|
|
354
|
-
"你把这件事写得很有意思,我很想知道你说到这里时会是什么语气。"
|
|
355
|
-
]
|
|
356
|
-
},
|
|
357
|
-
opinion: {
|
|
358
|
-
prompt: "这段很有态度,想听TA怎么说",
|
|
359
|
-
alternatePrompts: ["观点写在字里,也想听见TA的语气", "这几句话很有主张,想听TA亲口说"],
|
|
360
|
-
invitationReactions: [
|
|
361
|
-
"这个想法很有你的态度,我很想听你亲口说出来时的语气和停顿。",
|
|
362
|
-
"你的观点让我停下来想了想,也让我好奇你会怎样当面讲清楚它。",
|
|
363
|
-
"这几句话很有主张,不只是想看文字,也想听你完整地表达一次。",
|
|
364
|
-
"我能从字里看到你的判断,但还是很想听见这份态度本来的声音。"
|
|
365
|
-
]
|
|
366
|
-
},
|
|
367
|
-
travel: {
|
|
368
|
-
prompt: "这段旅途很有画面,想听TA亲口讲",
|
|
369
|
-
alternatePrompts: ["风景写进文字里,也想听TA说出来", "这段经历,让人想听见沿途的心情"],
|
|
370
|
-
invitationReactions: [
|
|
371
|
-
"你写的这段经历特别有画面感,我读完真的很想听你亲口讲讲当时的见闻。",
|
|
372
|
-
"看到这些沿途的片段,我好像也跟着走了一小段,很想听听你当时的心情。",
|
|
373
|
-
"这段旅途被你写得很有感觉,如果配上你的讲述,应该会更像身临其境。",
|
|
374
|
-
"我很喜欢你记录风景的方式,也开始期待从你的声音里再走一遍这段路。"
|
|
375
|
-
]
|
|
376
|
-
},
|
|
377
|
-
food: {
|
|
378
|
-
prompt: "这份味道写得很诱人,想听TA聊聊",
|
|
379
|
-
alternatePrompts: ["光看就有点馋,想听TA亲口安利", "这段吃喝日常,也想听见TA的满足"],
|
|
380
|
-
invitationReactions: [
|
|
381
|
-
"你写得我都有点馋了,很想听你亲口讲讲它到底有多好吃。",
|
|
382
|
-
"光看这段分享就能想象到味道,如果听你安利,应该会更有感染力。",
|
|
383
|
-
"这份吃喝日常很有生活气,我好奇你说起它时是不是也会忍不住开心。",
|
|
384
|
-
"你把这份味道写得很诱人,我真的很想听到你的现场评价。"
|
|
385
|
-
]
|
|
386
|
-
},
|
|
387
|
-
workStudy: {
|
|
388
|
-
prompt: "这段思考很认真,想听TA展开说说",
|
|
389
|
-
alternatePrompts: ["字里有不少思考,也想听TA讲明白", "这段成长和复盘,值得被亲口说出来"],
|
|
390
|
-
invitationReactions: [
|
|
391
|
-
"这段记录里有很多认真思考,我很想听你用自己的方式再展开讲讲。",
|
|
392
|
-
"能看出你在这件事上花了不少心思,我也很好奇你一路是怎么想过来的。",
|
|
393
|
-
"这份复盘很真诚,文字说清了结果,我还想听见你经历过程时的感受。",
|
|
394
|
-
"你的思路让我很受启发,如果由你亲口讲出来,应该会更清楚也更有力量。"
|
|
395
|
-
]
|
|
396
|
-
},
|
|
397
|
-
creative: {
|
|
398
|
-
prompt: "这份创作有自己的味道,想听TA聊聊",
|
|
399
|
-
alternatePrompts: ["作品被看见了,也想听见背后的想法", "这份表达不只有画面,也该有声音"],
|
|
400
|
-
invitationReactions: [
|
|
401
|
-
"这份表达很有你自己的味道,我不只想看作品,也想听你聊聊背后的想法。",
|
|
402
|
-
"我很喜欢你记录和创作的方式,也开始好奇你会怎样介绍它。",
|
|
403
|
-
"作品已经让人停下来看了,如果再听见你的讲述,感觉会更完整。",
|
|
404
|
-
"这段内容有很鲜明的个人感觉,我想听听你创作它时真正想表达什么。"
|
|
405
|
-
]
|
|
406
|
-
},
|
|
407
|
-
pet: {
|
|
408
|
-
prompt: "这份可爱很有画面,想听TA聊聊",
|
|
409
|
-
alternatePrompts: ["小家伙被记录下来,也想听TA的宠爱", "光看就很可爱,想听TA亲口分享"],
|
|
410
|
-
invitationReactions: [
|
|
411
|
-
"这个小家伙也太可爱了,我很想听你亲口讲讲它平时是什么样子。",
|
|
412
|
-
"看得出来你很喜欢它,照片和文字之外,我也想听见你说起它时的语气。",
|
|
413
|
-
"这份可爱让我忍不住多看了一会儿,感觉听你分享一定更有意思。",
|
|
414
|
-
"你记录它的方式很温柔,我也开始期待听你聊聊你们之间的小故事。"
|
|
415
|
-
]
|
|
416
|
-
},
|
|
417
|
-
story: {
|
|
418
|
-
prompt: "这个故事,也让人想听见",
|
|
419
|
-
alternatePrompts: ["故事写下来了,也想听TA讲出来", "这段像在讲故事,想听TA亲口说"],
|
|
420
|
-
invitationReactions: [
|
|
421
|
-
"这段经历读起来像一个有画面的故事,我很想听你亲口把它讲出来。",
|
|
422
|
-
"你写下来的这些细节让我跟着看了进去,也开始好奇你的真实语气。",
|
|
423
|
-
"故事写在这里已经很动人,如果听你亲自讲,感觉会离它更近一点。",
|
|
424
|
-
"我读完还在想后面的画面,很想听你用自己的声音把这段经历补完整。"
|
|
425
|
-
]
|
|
426
|
-
},
|
|
427
|
-
warm: {
|
|
428
|
-
prompt: "这段很温柔,带上声音会更完整",
|
|
429
|
-
alternatePrompts: ["文字里有温度,想听TA怎么说", "这份温柔,也想从TA的声音里听见"],
|
|
430
|
-
invitationReactions: [
|
|
431
|
-
"这段文字很温柔,我也很想从你的声音里听见这份温度。",
|
|
432
|
-
"读到这里心里会松一下,如果由你亲口说出来,应该会更治愈。",
|
|
433
|
-
"你的表达让人觉得很舒服,我开始期待听见你说这些话时的声音。",
|
|
434
|
-
"这份温柔不该只停在文字里,我很想听你亲自把它说出来。"
|
|
435
|
-
]
|
|
436
|
-
},
|
|
437
|
-
useful: {
|
|
438
|
-
prompt: "这段分享,听起来可能更容易懂",
|
|
439
|
-
alternatePrompts: ["这段很有用,想听TA讲得更明白", "不只想看这段分享,也想听TA说"],
|
|
440
|
-
invitationReactions: [
|
|
441
|
-
"这段分享很有用,我也很想听你用自己的方式再讲一遍。",
|
|
442
|
-
"你整理得很清楚,看完之后还会想听你补充那些文字没展开的细节。",
|
|
443
|
-
"这个方法让我觉得很受用,如果听你亲口解释,应该会更容易记住。",
|
|
444
|
-
"能感受到你是真心想把经验分享出来,我也很期待听见你的讲解。"
|
|
445
|
-
]
|
|
446
|
-
},
|
|
447
|
-
default: {
|
|
448
|
-
prompt: "想让这段文字带上TA的声音",
|
|
449
|
-
alternatePrompts: ["这段话如果有声音,会是什么感觉", "看见了TA的文字,也想听见TA的声音"],
|
|
450
|
-
invitationReactions: [
|
|
451
|
-
"这段话让我停下来多看了一会儿,也开始好奇,如果由你亲口说出来会是什么感觉。",
|
|
452
|
-
"我不是只想看完就划过去,而是真的很想听听你当时是怎样说这些话的。",
|
|
453
|
-
"文字让我看见了你的表达,也让我开始期待它带上属于你的声音。",
|
|
454
|
-
"看到这条快记时,我第一反应就是:如果能听见你亲口说,应该会更有感觉。"
|
|
455
|
-
]
|
|
456
|
-
}
|
|
457
|
-
};
|
|
458
|
-
const categoryKeywords = {
|
|
459
|
-
sad: [
|
|
460
|
-
"难过",
|
|
461
|
-
"悲伤",
|
|
462
|
-
"遗憾",
|
|
463
|
-
"绝望",
|
|
464
|
-
"失去",
|
|
465
|
-
"离开",
|
|
466
|
-
"哭",
|
|
467
|
-
"痛苦",
|
|
468
|
-
"疲惫",
|
|
469
|
-
"好难",
|
|
470
|
-
"难熬"
|
|
471
|
-
],
|
|
472
|
-
funny: [
|
|
473
|
-
"哈哈",
|
|
474
|
-
"笑死",
|
|
475
|
-
"搞笑",
|
|
476
|
-
"好玩",
|
|
477
|
-
"有趣",
|
|
478
|
-
"逗"
|
|
479
|
-
],
|
|
480
|
-
opinion: [
|
|
481
|
-
"我觉得",
|
|
482
|
-
"我认为",
|
|
483
|
-
"观点",
|
|
484
|
-
"想法",
|
|
485
|
-
"其实",
|
|
486
|
-
"应该",
|
|
487
|
-
"不能"
|
|
488
|
-
],
|
|
489
|
-
travel: [
|
|
490
|
-
"旅行",
|
|
491
|
-
"旅途",
|
|
492
|
-
"出发",
|
|
493
|
-
"风景",
|
|
494
|
-
"景色",
|
|
495
|
-
"海边",
|
|
496
|
-
"爬山",
|
|
497
|
-
"城市",
|
|
498
|
-
"远方",
|
|
499
|
-
"路上",
|
|
500
|
-
"散步"
|
|
501
|
-
],
|
|
502
|
-
food: [
|
|
503
|
-
"好吃",
|
|
504
|
-
"美食",
|
|
505
|
-
"餐厅",
|
|
506
|
-
"咖啡",
|
|
507
|
-
"甜品",
|
|
508
|
-
"味道",
|
|
509
|
-
"烤肉",
|
|
510
|
-
"火锅",
|
|
511
|
-
"做饭"
|
|
512
|
-
],
|
|
513
|
-
workStudy: [
|
|
514
|
-
"工作",
|
|
515
|
-
"项目",
|
|
516
|
-
"学习",
|
|
517
|
-
"论文",
|
|
518
|
-
"考试",
|
|
519
|
-
"复盘",
|
|
520
|
-
"成长",
|
|
521
|
-
"加班",
|
|
522
|
-
"研究",
|
|
523
|
-
"产品"
|
|
524
|
-
],
|
|
525
|
-
creative: [
|
|
526
|
-
"摄影",
|
|
527
|
-
"音乐",
|
|
528
|
-
"电影",
|
|
529
|
-
"读书",
|
|
530
|
-
"作品",
|
|
531
|
-
"创作",
|
|
532
|
-
"设计",
|
|
533
|
-
"照片",
|
|
534
|
-
"画画"
|
|
535
|
-
],
|
|
536
|
-
pet: [
|
|
537
|
-
"小猫",
|
|
538
|
-
"小狗",
|
|
539
|
-
"猫咪",
|
|
540
|
-
"狗狗",
|
|
541
|
-
"宠物",
|
|
542
|
-
"毛孩子"
|
|
543
|
-
],
|
|
544
|
-
useful: [
|
|
545
|
-
"教程",
|
|
546
|
-
"方法",
|
|
547
|
-
"步骤",
|
|
548
|
-
"经验",
|
|
549
|
-
"分享",
|
|
550
|
-
"建议",
|
|
551
|
-
"怎么做"
|
|
552
|
-
],
|
|
553
|
-
warm: [
|
|
554
|
-
"温柔",
|
|
555
|
-
"治愈",
|
|
556
|
-
"希望",
|
|
557
|
-
"拥抱",
|
|
558
|
-
"阳光",
|
|
559
|
-
"安慰"
|
|
560
|
-
],
|
|
561
|
-
story: [
|
|
562
|
-
"曾经",
|
|
563
|
-
"后来",
|
|
564
|
-
"那年",
|
|
565
|
-
"小时候",
|
|
566
|
-
"有一天",
|
|
567
|
-
"今天",
|
|
568
|
-
"昨天",
|
|
569
|
-
"故事"
|
|
570
|
-
]
|
|
571
|
-
};
|
|
572
|
-
const invitationClosings = [
|
|
573
|
-
"如果你愿意,来录入一下声纹吧。以后再看到你的世界快记,我也能直接听见你的声音:",
|
|
574
|
-
"愿意的话,也来录入声纹,给这条快记添上你的声音吧。我很期待以后不只看到文字,也能听见你:",
|
|
575
|
-
"所以想认真邀请你录入声纹。这样下次在世界遇见你的文字,也能听到你亲口表达:",
|
|
576
|
-
"要是你愿意,就来留下声纹吧。以后你的世界快记不只有文字,也会有属于你的声音:"
|
|
577
|
-
];
|
|
578
|
-
const urlPattern = /(?:(?:https?|ftp):\/\/|www\.)[^\s,。!?;:、"“”‘’()【】《》<>]+/gi;
|
|
579
|
-
const trailingUrlPunctuation = /[.,;:!?,。;:!?))\]}>…]+$/;
|
|
580
|
-
const whitespace = /\s+/g;
|
|
581
|
-
const meaningfulCharacter = /[\p{L}\p{N}]/gu;
|
|
582
|
-
const digitCharacter = /\p{N}/u;
|
|
583
|
-
const latinCharacter = /[a-z]/i;
|
|
584
|
-
const chineseCharacter = /[\u3400-\u9fff]/;
|
|
585
|
-
const mathSymbol = /[+\-×÷*/=<>]/;
|
|
586
|
-
function worldVoiceprintReadableText(value) {
|
|
587
|
-
const normalized = value.replace(urlPattern, (matchedUrl) => matchedUrl.match(trailingUrlPunctuation)?.[0] ?? "").replace(/<>/g, " ").replace(whitespace, " ").trim();
|
|
588
|
-
return /[\p{L}\p{N}]/u.test(normalized) ? normalized : "";
|
|
589
|
-
}
|
|
590
|
-
function buildWorldVoiceprintInviteMessage(input) {
|
|
591
|
-
const preview = input.textPreview?.replace(whitespace, " ").trim();
|
|
592
|
-
const variant = normalizedVariant(input.variantIndex ?? 0, 4);
|
|
593
|
-
const copy = contentCopies[worldVoiceprintContentKind(preview ?? "")];
|
|
594
|
-
const name = input.peerDisplayName.replace(whitespace, " ").trim();
|
|
595
|
-
const greeting = name === "" || name === "这位用户" ? "我是看到你在世界发的这条快记才来找你的:" : `${name},我是看到你在世界发的这条快记才来找你的:`;
|
|
596
|
-
const media = mediaSummary(input);
|
|
597
|
-
return `${greeting}\n\n【来自世界的快记】\n${preview === void 0 || preview === "" ? `(你发布了一条${media === "" ? "" : `${media}的`}世界快记)` : `“${preview}”${media === "" ? "" : `\n(还包含${media})`}`}\n\n${copy.invitationReactions[variant]}\n\n${invitationClosings[variant]}\n${input.inviteUrl}`;
|
|
598
|
-
}
|
|
599
|
-
function mediaSummary(input) {
|
|
600
|
-
const parts = [];
|
|
601
|
-
if ((input.imageCount ?? 0) > 0) parts.push(`${String(Math.trunc(input.imageCount))}张图片`);
|
|
602
|
-
if ((input.videoCount ?? 0) > 0) parts.push(`${String(Math.trunc(input.videoCount))}段视频`);
|
|
603
|
-
if ((input.voiceCount ?? 0) > 0) parts.push(`${String(Math.trunc(input.voiceCount))}段语音`);
|
|
604
|
-
return parts.join("、");
|
|
605
|
-
}
|
|
606
|
-
function normalizedVariant(value, count) {
|
|
607
|
-
if (!Number.isFinite(value)) return 0;
|
|
608
|
-
return (Math.trunc(value) % count + count) % count;
|
|
609
|
-
}
|
|
610
|
-
function worldVoiceprintContentKind(rawText) {
|
|
611
|
-
const text = worldVoiceprintReadableText(rawText).toLowerCase();
|
|
612
|
-
const meaningful = text.match(meaningfulCharacter) ?? [];
|
|
613
|
-
if (meaningful.length === 0) return "unclear";
|
|
614
|
-
if (looksNumericOrFormula(text, meaningful)) return "numeric";
|
|
615
|
-
if (looksUnclear(text, meaningful)) return "unclear";
|
|
616
|
-
if (meaningful.length <= 6) return "short";
|
|
617
|
-
return bestMatchingCategory(text) ?? "default";
|
|
618
|
-
}
|
|
619
|
-
function looksNumericOrFormula(text, meaningful) {
|
|
620
|
-
const digitCount = meaningful.filter((character) => digitCharacter.test(character)).length;
|
|
621
|
-
if (digitCount === 0) return false;
|
|
622
|
-
if (mathSymbol.test(text)) return true;
|
|
623
|
-
return digitCount / meaningful.length >= .7;
|
|
624
|
-
}
|
|
625
|
-
function looksUnclear(text, meaningful) {
|
|
626
|
-
const uniqueCharacters = new Set(meaningful);
|
|
627
|
-
if (meaningful.length >= 5 && uniqueCharacters.size === 1) return true;
|
|
628
|
-
if (meaningful.length >= 8 && uniqueCharacters.size <= 2) return true;
|
|
629
|
-
const hasChinese = chineseCharacter.test(text);
|
|
630
|
-
const hasLatin = latinCharacter.test(text);
|
|
631
|
-
const hasDigit = digitCharacter.test(text);
|
|
632
|
-
return !hasChinese && hasLatin && hasDigit && !text.includes(" ");
|
|
633
|
-
}
|
|
634
|
-
function bestMatchingCategory(text) {
|
|
635
|
-
let bestCategory;
|
|
636
|
-
let bestScore = 0;
|
|
637
|
-
for (const [category, keywords] of Object.entries(categoryKeywords)) {
|
|
638
|
-
const score = keywords.filter((keyword) => text.includes(keyword)).length;
|
|
639
|
-
if (score > bestScore) {
|
|
640
|
-
bestCategory = category;
|
|
641
|
-
bestScore = score;
|
|
310
|
+
expireLease(userId) {
|
|
311
|
+
const current = this.leases.get(userId);
|
|
312
|
+
if (current !== void 0 && current.expiresAtMillis <= this.now()) {
|
|
313
|
+
this.leases.delete(userId);
|
|
314
|
+
callDiag$1("lease_expired", {
|
|
315
|
+
userId,
|
|
316
|
+
callRequestId: current.callRequestId,
|
|
317
|
+
expiresAtMillis: current.expiresAtMillis
|
|
318
|
+
});
|
|
642
319
|
}
|
|
643
320
|
}
|
|
644
|
-
|
|
645
|
-
|
|
321
|
+
removeIntent(intent) {
|
|
322
|
+
this.intents.delete(intent.intentId);
|
|
323
|
+
this.clearTimer(intent.timer);
|
|
324
|
+
this.detachAbort(intent);
|
|
325
|
+
callDiag$1("intent_removed", {
|
|
326
|
+
userId: intent.userId,
|
|
327
|
+
intentId: intent.intentId,
|
|
328
|
+
callRequestId: intent.callRequestId,
|
|
329
|
+
claimed: intent.claimed,
|
|
330
|
+
toolSettled: intent.toolSettled
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
detachAbort(intent) {
|
|
334
|
+
if (intent.signal !== void 0 && intent.abort !== void 0) intent.signal.removeEventListener("abort", intent.abort);
|
|
335
|
+
}
|
|
336
|
+
};
|
|
646
337
|
//#endregion
|
|
647
338
|
//#region src/request-coordinator.ts
|
|
648
339
|
const DEFAULT_LANE_LIMITS = {
|
|
@@ -1570,7 +1261,555 @@ var ServiceRuntime = class {
|
|
|
1570
1261
|
return await this.post(baseUrl, path, body, session.accessToken, [0], signal, false, requestOptions(), true, true);
|
|
1571
1262
|
}
|
|
1572
1263
|
}
|
|
1573
|
-
};
|
|
1264
|
+
};
|
|
1265
|
+
//#endregion
|
|
1266
|
+
//#region src/billing-gateway.ts
|
|
1267
|
+
var ArkmeBillingUnavailableError = class extends Error {
|
|
1268
|
+
constructor() {
|
|
1269
|
+
super("购买服务暂不可用");
|
|
1270
|
+
this.name = "ArkmeBillingUnavailableError";
|
|
1271
|
+
}
|
|
1272
|
+
};
|
|
1273
|
+
function nanoCny(value, field) {
|
|
1274
|
+
const normalized = stringValue$1(value).trim();
|
|
1275
|
+
if (!/^(?:0|[1-9]\d*)$/.test(normalized)) throw new ArkmePluginError("billing-contract-invalid", `余额接口字段 ${field} 无效`, true, 502);
|
|
1276
|
+
return normalized;
|
|
1277
|
+
}
|
|
1278
|
+
function requiredString$1(value, field) {
|
|
1279
|
+
const normalized = stringValue$1(value).trim();
|
|
1280
|
+
if (normalized === "") throw new ArkmePluginError("billing-contract-invalid", `支付接口字段 ${field} 无效`, true, 502);
|
|
1281
|
+
return normalized;
|
|
1282
|
+
}
|
|
1283
|
+
function positiveSafeInteger(value, field) {
|
|
1284
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value <= 0) throw new ArkmePluginError("billing-contract-invalid", `支付接口字段 ${field} 无效`, true, 502);
|
|
1285
|
+
return value;
|
|
1286
|
+
}
|
|
1287
|
+
function optionalTimestampMillis(value, field) {
|
|
1288
|
+
if (value === void 0 || value === null || value === "") return void 0;
|
|
1289
|
+
const millis = Date.parse(requiredString$1(value, field));
|
|
1290
|
+
if (!Number.isFinite(millis)) throw new ArkmePluginError("billing-contract-invalid", `支付接口字段 ${field} 无效`, true, 502);
|
|
1291
|
+
return millis;
|
|
1292
|
+
}
|
|
1293
|
+
function uuid(value, field, errorCode = "billing-contract-invalid") {
|
|
1294
|
+
const normalized = stringValue$1(value).trim().toLowerCase();
|
|
1295
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(normalized)) {
|
|
1296
|
+
const upstreamContract = errorCode === "billing-contract-invalid";
|
|
1297
|
+
throw new ArkmePluginError(errorCode, `支付接口字段 ${field} 无效`, upstreamContract, upstreamContract ? 502 : 400);
|
|
1298
|
+
}
|
|
1299
|
+
return normalized;
|
|
1300
|
+
}
|
|
1301
|
+
function paymentMethods(value) {
|
|
1302
|
+
if (!Array.isArray(value)) throw new ArkmePluginError("billing-contract-invalid", "支付接口字段 payment_methods 无效", true, 502);
|
|
1303
|
+
const result = [];
|
|
1304
|
+
for (const rawMethod of value) {
|
|
1305
|
+
const method = objectValue$1(rawMethod);
|
|
1306
|
+
const id = requiredString$1(method.id, "payment_methods.id");
|
|
1307
|
+
const provider = requiredString$1(method.provider, "payment_methods.provider");
|
|
1308
|
+
const actionType = requiredString$1(method.action_type, "payment_methods.action_type");
|
|
1309
|
+
if (!(id === "wechat_native" && provider === "wechat" && actionType === "display_qr" || id === "alipay_pc_web" && provider === "alipay" && actionType === "open_url")) throw new ArkmePluginError("billing-contract-invalid", "支付接口包含未知支付方式", true, 502);
|
|
1310
|
+
if (!result.some((item) => item.id === id)) result.push({
|
|
1311
|
+
id,
|
|
1312
|
+
provider,
|
|
1313
|
+
actionType
|
|
1314
|
+
});
|
|
1315
|
+
}
|
|
1316
|
+
if (result.length === 0) throw new ArkmePluginError("billing-contract-invalid", "支付接口未返回可用渠道", true, 502);
|
|
1317
|
+
return result;
|
|
1318
|
+
}
|
|
1319
|
+
function productSnapshot(value, currency) {
|
|
1320
|
+
const product = objectValue$1(value);
|
|
1321
|
+
const description = stringValue$1(product.description).trim();
|
|
1322
|
+
return {
|
|
1323
|
+
productId: requiredString$1(product.product_code, "product_code"),
|
|
1324
|
+
title: requiredString$1(product.title, "title"),
|
|
1325
|
+
...description === "" ? {} : { description },
|
|
1326
|
+
creditNanoCny: nanoCny(product.credit_nano_cny, "credit_nano_cny"),
|
|
1327
|
+
priceMinor: positiveSafeInteger(product.price_cent, "price_cent"),
|
|
1328
|
+
currency,
|
|
1329
|
+
paymentMethods: paymentMethods(product.payment_methods),
|
|
1330
|
+
enabled: true
|
|
1331
|
+
};
|
|
1332
|
+
}
|
|
1333
|
+
function paymentMethodContract(paymentMethod, provider, actionType) {
|
|
1334
|
+
return paymentMethod === "wechat_native" && provider === "wechat" && actionType === "display_qr" || paymentMethod === "alipay_pc_web" && provider === "alipay" && actionType === "open_url";
|
|
1335
|
+
}
|
|
1336
|
+
function paymentAction(value) {
|
|
1337
|
+
const action = objectValue$1(value);
|
|
1338
|
+
const type = requiredString$1(action.type, "payment_action.type");
|
|
1339
|
+
if (type === "open_url") {
|
|
1340
|
+
const url = requiredString$1(action.url, "payment_action.url");
|
|
1341
|
+
let parsed;
|
|
1342
|
+
try {
|
|
1343
|
+
parsed = new URL(url);
|
|
1344
|
+
} catch {
|
|
1345
|
+
throw new ArkmePluginError("billing-contract-invalid", "支付接口字段 payment_action.url 无效", true, 502);
|
|
1346
|
+
}
|
|
1347
|
+
if (parsed.protocol !== "https:" || parsed.username !== "" || parsed.password !== "") throw new ArkmePluginError("billing-contract-invalid", "支付接口字段 payment_action.url 无效", true, 502);
|
|
1348
|
+
return {
|
|
1349
|
+
type,
|
|
1350
|
+
url: parsed.href
|
|
1351
|
+
};
|
|
1352
|
+
}
|
|
1353
|
+
if (type === "display_qr") return {
|
|
1354
|
+
type,
|
|
1355
|
+
qrContent: requiredString$1(action.qr_code_content, "payment_action.qr_code_content")
|
|
1356
|
+
};
|
|
1357
|
+
throw new ArkmePluginError("billing-contract-invalid", "支付接口返回未知支付动作", true, 502);
|
|
1358
|
+
}
|
|
1359
|
+
function orderSnapshot(value) {
|
|
1360
|
+
const order = objectValue$1(value);
|
|
1361
|
+
const mappedStatus = {
|
|
1362
|
+
pending_payment: "pending",
|
|
1363
|
+
crediting: "crediting",
|
|
1364
|
+
credited: "paid",
|
|
1365
|
+
closed: "closed"
|
|
1366
|
+
}[requiredString$1(order.status, "status")];
|
|
1367
|
+
if (mappedStatus === void 0) throw new ArkmePluginError("billing-contract-invalid", "支付接口返回未知订单状态", true, 502);
|
|
1368
|
+
const paymentProvider = requiredString$1(order.payment_provider, "payment_provider");
|
|
1369
|
+
const paymentMethod = requiredString$1(order.payment_method, "payment_method");
|
|
1370
|
+
const rawAction = order.payment_action;
|
|
1371
|
+
const action = rawAction === void 0 || rawAction === null ? void 0 : paymentAction(rawAction);
|
|
1372
|
+
if (!paymentMethodContract(paymentMethod, paymentProvider, action?.type ?? (paymentMethod === "wechat_native" ? "display_qr" : paymentMethod === "alipay_pc_web" ? "open_url" : ""))) throw new ArkmePluginError("billing-contract-invalid", "支付接口返回未知支付方式", true, 502);
|
|
1373
|
+
if (requiredString$1(order.currency, "currency") !== "CNY") throw new ArkmePluginError("billing-contract-invalid", "支付接口币种无效", true, 502);
|
|
1374
|
+
const expiresAtMillis = optionalTimestampMillis(order.expires_at, "expires_at");
|
|
1375
|
+
if (expiresAtMillis === void 0) throw new ArkmePluginError("billing-contract-invalid", "支付接口字段 expires_at 无效", true, 502);
|
|
1376
|
+
if (mappedStatus === "pending" && action === void 0) throw new ArkmePluginError("billing-contract-invalid", "支付接口未返回有效支付动作", true, 502);
|
|
1377
|
+
const pollInterval = order.poll_interval_ms;
|
|
1378
|
+
const pollIntervalMillis = typeof pollInterval === "number" && Number.isSafeInteger(pollInterval) && pollInterval > 0 ? Math.min(1e4, Math.max(500, pollInterval)) : void 0;
|
|
1379
|
+
return {
|
|
1380
|
+
orderId: uuid(order.order_uid, "order_uid"),
|
|
1381
|
+
paymentProvider,
|
|
1382
|
+
paymentMethod,
|
|
1383
|
+
status: mappedStatus,
|
|
1384
|
+
amountMinor: positiveSafeInteger(order.price_cent, "price_cent"),
|
|
1385
|
+
currency: "CNY",
|
|
1386
|
+
creditNanoCny: nanoCny(order.credit_nano_cny, "credit_nano_cny"),
|
|
1387
|
+
expiresAtMillis,
|
|
1388
|
+
...action === void 0 ? {} : { paymentAction: action },
|
|
1389
|
+
...pollIntervalMillis === void 0 ? {} : { pollIntervalMillis },
|
|
1390
|
+
...optionalTimestamp("paidAtMillis", order.paid_at, "paid_at"),
|
|
1391
|
+
...optionalTimestamp("creditedAtMillis", order.credited_at, "credited_at")
|
|
1392
|
+
};
|
|
1393
|
+
}
|
|
1394
|
+
function optionalTimestamp(key, value, field) {
|
|
1395
|
+
const millis = optionalTimestampMillis(value, field);
|
|
1396
|
+
return millis === void 0 ? {} : { [key]: millis };
|
|
1397
|
+
}
|
|
1398
|
+
var HttpArkmeBillingGateway = class {
|
|
1399
|
+
runtime;
|
|
1400
|
+
constructor(runtime) {
|
|
1401
|
+
this.runtime = runtime;
|
|
1402
|
+
}
|
|
1403
|
+
async post(service, path, body, lane, signal, key) {
|
|
1404
|
+
let session = await this.runtime.requireSession();
|
|
1405
|
+
const request = async () => await this.runtime.post(service === "auth" ? this.runtime.config.authBaseUrl : this.runtime.config.intelligentBaseUrl, path, body, session.accessToken, [200], signal, true, this.runtime.authenticatedRequestOptions(session, service, lane, {
|
|
1406
|
+
lane,
|
|
1407
|
+
...key === void 0 ? {} : {
|
|
1408
|
+
key,
|
|
1409
|
+
bypassCache: true
|
|
1410
|
+
}
|
|
1411
|
+
}));
|
|
1412
|
+
try {
|
|
1413
|
+
return await request();
|
|
1414
|
+
} catch (error) {
|
|
1415
|
+
if (!(error instanceof ArkmePluginError) || ![
|
|
1416
|
+
"auth-http-401",
|
|
1417
|
+
"auth-http-403",
|
|
1418
|
+
"invalid_access_token"
|
|
1419
|
+
].includes(error.code)) throw this.normalizeError(error);
|
|
1420
|
+
session = await this.runtime.refreshAccessToken(session);
|
|
1421
|
+
try {
|
|
1422
|
+
return await request();
|
|
1423
|
+
} catch (retryError) {
|
|
1424
|
+
throw this.normalizeError(retryError);
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
normalizeError(error) {
|
|
1429
|
+
if (!(error instanceof ArkmePluginError)) return error;
|
|
1430
|
+
const retryable = [
|
|
1431
|
+
"order_processing",
|
|
1432
|
+
"purchase_unavailable",
|
|
1433
|
+
"balance_unavailable",
|
|
1434
|
+
"arkme-network-error",
|
|
1435
|
+
"arkme-timeout"
|
|
1436
|
+
].includes(error.code);
|
|
1437
|
+
if (retryable === error.retryable) return error;
|
|
1438
|
+
return new ArkmePluginError(error.code, error.message, retryable, error.httpStatus, { cause: error });
|
|
1439
|
+
}
|
|
1440
|
+
async quota(signal) {
|
|
1441
|
+
const data = objectValue$1(await this.post("intelligent", "/api/v1/managed-ai/balance/query", {}, "interactive-read", signal, "managed-ai-balance"));
|
|
1442
|
+
const currency = stringValue$1(data.currency).trim();
|
|
1443
|
+
if (currency !== "CNY") throw new ArkmePluginError("billing-contract-invalid", "余额接口币种无效", true, 502);
|
|
1444
|
+
const availableNanoCny = nanoCny(data.available_nano_cny, "available_nano_cny");
|
|
1445
|
+
const totalNanoCny = nanoCny(data.total_nano_cny, "total_nano_cny");
|
|
1446
|
+
const reservedNanoCny = nanoCny(data.reserved_nano_cny, "reserved_nano_cny");
|
|
1447
|
+
if (BigInt(availableNanoCny) + BigInt(reservedNanoCny) !== BigInt(totalNanoCny)) throw new ArkmePluginError("billing-contract-invalid", "余额接口金额不一致", true, 502);
|
|
1448
|
+
return {
|
|
1449
|
+
availableNanoCny,
|
|
1450
|
+
totalNanoCny,
|
|
1451
|
+
reservedNanoCny,
|
|
1452
|
+
currency
|
|
1453
|
+
};
|
|
1454
|
+
}
|
|
1455
|
+
async products(signal) {
|
|
1456
|
+
const data = objectValue$1(await this.post("auth", "/api/v1/managed-ai/products/query", {}, "interactive-read", signal, "managed-ai-products"));
|
|
1457
|
+
if (stringValue$1(data.currency).trim() !== "CNY" || !Array.isArray(data.products)) throw new ArkmePluginError("billing-contract-invalid", "商品接口响应无效", true, 502);
|
|
1458
|
+
return { items: data.products.map((product) => productSnapshot(product, "CNY")) };
|
|
1459
|
+
}
|
|
1460
|
+
async createOrder(input, signal) {
|
|
1461
|
+
const clientOrderUid = uuid(input.clientRequestId, "client_order_uid", "billing-client-request-id-invalid");
|
|
1462
|
+
return orderSnapshot(await this.post("auth", "/api/v1/managed-ai/purchase/create", {
|
|
1463
|
+
client_order_uid: clientOrderUid,
|
|
1464
|
+
product_code: requiredString$1(input.productId, "product_code"),
|
|
1465
|
+
payment_method: input.paymentMethod
|
|
1466
|
+
}, "write", signal));
|
|
1467
|
+
}
|
|
1468
|
+
async orderStatus(orderId, signal) {
|
|
1469
|
+
const orderUid = uuid(orderId, "order_uid", "billing-order-id-invalid");
|
|
1470
|
+
return orderSnapshot(await this.post("auth", "/api/v1/managed-ai/purchase/status", { order_uid: orderUid }, "interactive-read", signal));
|
|
1471
|
+
}
|
|
1472
|
+
};
|
|
1473
|
+
const contentCopies = {
|
|
1474
|
+
unclear: {
|
|
1475
|
+
prompt: "有点看不懂,反而更想听TA怎么说",
|
|
1476
|
+
alternatePrompts: ["越看不懂,越想听TA亲口表达", "这段有点跳脱,想听TA怎么读"],
|
|
1477
|
+
invitationReactions: [
|
|
1478
|
+
"我有点没看懂,却不是想划走,反而更想听你亲口解释它是什么意思。",
|
|
1479
|
+
"这段像藏着一点只有你知道的意思,让我很好奇你会怎么说出来。",
|
|
1480
|
+
"它读起来有点跳脱,也正因为这样,我更想听见你自己的语气。",
|
|
1481
|
+
"我盯着这段看了一会儿,还是觉得由你亲口讲出来一定更明白。"
|
|
1482
|
+
]
|
|
1483
|
+
},
|
|
1484
|
+
numeric: {
|
|
1485
|
+
prompt: "像一串暗号,想听TA怎么读",
|
|
1486
|
+
alternatePrompts: ["这串数字藏着什么,想听TA念出来", "看起来像个暗号,想听TA怎么解开"],
|
|
1487
|
+
invitationReactions: [
|
|
1488
|
+
"这串内容看起来像个小暗号,我很好奇它从你嘴里说出来会是什么语气。",
|
|
1489
|
+
"我看到它时忍不住猜了好一会儿,真想听你亲口把这个暗号解开。",
|
|
1490
|
+
"只有这些字符,反而给人留下了很多想象,我想听听你会怎么读。",
|
|
1491
|
+
"它越简短越让人好奇,感觉只有听你说出来才算完整。"
|
|
1492
|
+
]
|
|
1493
|
+
},
|
|
1494
|
+
short: {
|
|
1495
|
+
prompt: "字不多,更想听TA怎么说",
|
|
1496
|
+
alternatePrompts: ["只有几个字,反而更好奇TA的语气", "话很短,想听TA会怎么念"],
|
|
1497
|
+
invitationReactions: [
|
|
1498
|
+
"你只写了短短几个字,反而让我更好奇你当时真正的语气。",
|
|
1499
|
+
"这句话很短,却挺有余味的,我很想听你亲口说一遍。",
|
|
1500
|
+
"字不多,但感觉里面还有没写出来的意思,想听听你会怎么表达。",
|
|
1501
|
+
"我看到这句时停了一下,总觉得带上你的声音会更有感觉。"
|
|
1502
|
+
]
|
|
1503
|
+
},
|
|
1504
|
+
sad: {
|
|
1505
|
+
prompt: "文字里有些情绪,更想听见TA的语气",
|
|
1506
|
+
alternatePrompts: ["这段情绪藏在字里,想听TA怎么说", "有些感受看得见,也想听得见"],
|
|
1507
|
+
invitationReactions: [
|
|
1508
|
+
"读到这里有点心疼。文字能写下经历,但我更想听见你当时真正的语气。",
|
|
1509
|
+
"这段话里的情绪很真实,让我没有办法只把它当成普通文字划过去。",
|
|
1510
|
+
"看到这些话,我很想更认真地听你说说,而不只是隔着文字猜你的感受。",
|
|
1511
|
+
"有些心情写出来已经很不容易了,如果能听见你的声音,也许会更懂你。"
|
|
1512
|
+
]
|
|
1513
|
+
},
|
|
1514
|
+
funny: {
|
|
1515
|
+
prompt: "这段很有画面,带上声音会更有趣",
|
|
1516
|
+
alternatePrompts: ["光看文字就有画面,想听TA怎么演绎", "这段有点好玩,想听TA会怎么说"],
|
|
1517
|
+
invitationReactions: [
|
|
1518
|
+
"光看文字就已经很有画面了,我开始好奇你亲口讲出来会有多好玩。",
|
|
1519
|
+
"我看到这里真的笑了一下,要是配上你的语气,感觉一定更有意思。",
|
|
1520
|
+
"这段太适合听你现场讲了,文字已经有趣,声音应该会更生动。",
|
|
1521
|
+
"你把这件事写得很有意思,我很想知道你说到这里时会是什么语气。"
|
|
1522
|
+
]
|
|
1523
|
+
},
|
|
1524
|
+
opinion: {
|
|
1525
|
+
prompt: "这段很有态度,想听TA怎么说",
|
|
1526
|
+
alternatePrompts: ["观点写在字里,也想听见TA的语气", "这几句话很有主张,想听TA亲口说"],
|
|
1527
|
+
invitationReactions: [
|
|
1528
|
+
"这个想法很有你的态度,我很想听你亲口说出来时的语气和停顿。",
|
|
1529
|
+
"你的观点让我停下来想了想,也让我好奇你会怎样当面讲清楚它。",
|
|
1530
|
+
"这几句话很有主张,不只是想看文字,也想听你完整地表达一次。",
|
|
1531
|
+
"我能从字里看到你的判断,但还是很想听见这份态度本来的声音。"
|
|
1532
|
+
]
|
|
1533
|
+
},
|
|
1534
|
+
travel: {
|
|
1535
|
+
prompt: "这段旅途很有画面,想听TA亲口讲",
|
|
1536
|
+
alternatePrompts: ["风景写进文字里,也想听TA说出来", "这段经历,让人想听见沿途的心情"],
|
|
1537
|
+
invitationReactions: [
|
|
1538
|
+
"你写的这段经历特别有画面感,我读完真的很想听你亲口讲讲当时的见闻。",
|
|
1539
|
+
"看到这些沿途的片段,我好像也跟着走了一小段,很想听听你当时的心情。",
|
|
1540
|
+
"这段旅途被你写得很有感觉,如果配上你的讲述,应该会更像身临其境。",
|
|
1541
|
+
"我很喜欢你记录风景的方式,也开始期待从你的声音里再走一遍这段路。"
|
|
1542
|
+
]
|
|
1543
|
+
},
|
|
1544
|
+
food: {
|
|
1545
|
+
prompt: "这份味道写得很诱人,想听TA聊聊",
|
|
1546
|
+
alternatePrompts: ["光看就有点馋,想听TA亲口安利", "这段吃喝日常,也想听见TA的满足"],
|
|
1547
|
+
invitationReactions: [
|
|
1548
|
+
"你写得我都有点馋了,很想听你亲口讲讲它到底有多好吃。",
|
|
1549
|
+
"光看这段分享就能想象到味道,如果听你安利,应该会更有感染力。",
|
|
1550
|
+
"这份吃喝日常很有生活气,我好奇你说起它时是不是也会忍不住开心。",
|
|
1551
|
+
"你把这份味道写得很诱人,我真的很想听到你的现场评价。"
|
|
1552
|
+
]
|
|
1553
|
+
},
|
|
1554
|
+
workStudy: {
|
|
1555
|
+
prompt: "这段思考很认真,想听TA展开说说",
|
|
1556
|
+
alternatePrompts: ["字里有不少思考,也想听TA讲明白", "这段成长和复盘,值得被亲口说出来"],
|
|
1557
|
+
invitationReactions: [
|
|
1558
|
+
"这段记录里有很多认真思考,我很想听你用自己的方式再展开讲讲。",
|
|
1559
|
+
"能看出你在这件事上花了不少心思,我也很好奇你一路是怎么想过来的。",
|
|
1560
|
+
"这份复盘很真诚,文字说清了结果,我还想听见你经历过程时的感受。",
|
|
1561
|
+
"你的思路让我很受启发,如果由你亲口讲出来,应该会更清楚也更有力量。"
|
|
1562
|
+
]
|
|
1563
|
+
},
|
|
1564
|
+
creative: {
|
|
1565
|
+
prompt: "这份创作有自己的味道,想听TA聊聊",
|
|
1566
|
+
alternatePrompts: ["作品被看见了,也想听见背后的想法", "这份表达不只有画面,也该有声音"],
|
|
1567
|
+
invitationReactions: [
|
|
1568
|
+
"这份表达很有你自己的味道,我不只想看作品,也想听你聊聊背后的想法。",
|
|
1569
|
+
"我很喜欢你记录和创作的方式,也开始好奇你会怎样介绍它。",
|
|
1570
|
+
"作品已经让人停下来看了,如果再听见你的讲述,感觉会更完整。",
|
|
1571
|
+
"这段内容有很鲜明的个人感觉,我想听听你创作它时真正想表达什么。"
|
|
1572
|
+
]
|
|
1573
|
+
},
|
|
1574
|
+
pet: {
|
|
1575
|
+
prompt: "这份可爱很有画面,想听TA聊聊",
|
|
1576
|
+
alternatePrompts: ["小家伙被记录下来,也想听TA的宠爱", "光看就很可爱,想听TA亲口分享"],
|
|
1577
|
+
invitationReactions: [
|
|
1578
|
+
"这个小家伙也太可爱了,我很想听你亲口讲讲它平时是什么样子。",
|
|
1579
|
+
"看得出来你很喜欢它,照片和文字之外,我也想听见你说起它时的语气。",
|
|
1580
|
+
"这份可爱让我忍不住多看了一会儿,感觉听你分享一定更有意思。",
|
|
1581
|
+
"你记录它的方式很温柔,我也开始期待听你聊聊你们之间的小故事。"
|
|
1582
|
+
]
|
|
1583
|
+
},
|
|
1584
|
+
story: {
|
|
1585
|
+
prompt: "这个故事,也让人想听见",
|
|
1586
|
+
alternatePrompts: ["故事写下来了,也想听TA讲出来", "这段像在讲故事,想听TA亲口说"],
|
|
1587
|
+
invitationReactions: [
|
|
1588
|
+
"这段经历读起来像一个有画面的故事,我很想听你亲口把它讲出来。",
|
|
1589
|
+
"你写下来的这些细节让我跟着看了进去,也开始好奇你的真实语气。",
|
|
1590
|
+
"故事写在这里已经很动人,如果听你亲自讲,感觉会离它更近一点。",
|
|
1591
|
+
"我读完还在想后面的画面,很想听你用自己的声音把这段经历补完整。"
|
|
1592
|
+
]
|
|
1593
|
+
},
|
|
1594
|
+
warm: {
|
|
1595
|
+
prompt: "这段很温柔,带上声音会更完整",
|
|
1596
|
+
alternatePrompts: ["文字里有温度,想听TA怎么说", "这份温柔,也想从TA的声音里听见"],
|
|
1597
|
+
invitationReactions: [
|
|
1598
|
+
"这段文字很温柔,我也很想从你的声音里听见这份温度。",
|
|
1599
|
+
"读到这里心里会松一下,如果由你亲口说出来,应该会更治愈。",
|
|
1600
|
+
"你的表达让人觉得很舒服,我开始期待听见你说这些话时的声音。",
|
|
1601
|
+
"这份温柔不该只停在文字里,我很想听你亲自把它说出来。"
|
|
1602
|
+
]
|
|
1603
|
+
},
|
|
1604
|
+
useful: {
|
|
1605
|
+
prompt: "这段分享,听起来可能更容易懂",
|
|
1606
|
+
alternatePrompts: ["这段很有用,想听TA讲得更明白", "不只想看这段分享,也想听TA说"],
|
|
1607
|
+
invitationReactions: [
|
|
1608
|
+
"这段分享很有用,我也很想听你用自己的方式再讲一遍。",
|
|
1609
|
+
"你整理得很清楚,看完之后还会想听你补充那些文字没展开的细节。",
|
|
1610
|
+
"这个方法让我觉得很受用,如果听你亲口解释,应该会更容易记住。",
|
|
1611
|
+
"能感受到你是真心想把经验分享出来,我也很期待听见你的讲解。"
|
|
1612
|
+
]
|
|
1613
|
+
},
|
|
1614
|
+
default: {
|
|
1615
|
+
prompt: "想让这段文字带上TA的声音",
|
|
1616
|
+
alternatePrompts: ["这段话如果有声音,会是什么感觉", "看见了TA的文字,也想听见TA的声音"],
|
|
1617
|
+
invitationReactions: [
|
|
1618
|
+
"这段话让我停下来多看了一会儿,也开始好奇,如果由你亲口说出来会是什么感觉。",
|
|
1619
|
+
"我不是只想看完就划过去,而是真的很想听听你当时是怎样说这些话的。",
|
|
1620
|
+
"文字让我看见了你的表达,也让我开始期待它带上属于你的声音。",
|
|
1621
|
+
"看到这条快记时,我第一反应就是:如果能听见你亲口说,应该会更有感觉。"
|
|
1622
|
+
]
|
|
1623
|
+
}
|
|
1624
|
+
};
|
|
1625
|
+
const categoryKeywords = {
|
|
1626
|
+
sad: [
|
|
1627
|
+
"难过",
|
|
1628
|
+
"悲伤",
|
|
1629
|
+
"遗憾",
|
|
1630
|
+
"绝望",
|
|
1631
|
+
"失去",
|
|
1632
|
+
"离开",
|
|
1633
|
+
"哭",
|
|
1634
|
+
"痛苦",
|
|
1635
|
+
"疲惫",
|
|
1636
|
+
"好难",
|
|
1637
|
+
"难熬"
|
|
1638
|
+
],
|
|
1639
|
+
funny: [
|
|
1640
|
+
"哈哈",
|
|
1641
|
+
"笑死",
|
|
1642
|
+
"搞笑",
|
|
1643
|
+
"好玩",
|
|
1644
|
+
"有趣",
|
|
1645
|
+
"逗"
|
|
1646
|
+
],
|
|
1647
|
+
opinion: [
|
|
1648
|
+
"我觉得",
|
|
1649
|
+
"我认为",
|
|
1650
|
+
"观点",
|
|
1651
|
+
"想法",
|
|
1652
|
+
"其实",
|
|
1653
|
+
"应该",
|
|
1654
|
+
"不能"
|
|
1655
|
+
],
|
|
1656
|
+
travel: [
|
|
1657
|
+
"旅行",
|
|
1658
|
+
"旅途",
|
|
1659
|
+
"出发",
|
|
1660
|
+
"风景",
|
|
1661
|
+
"景色",
|
|
1662
|
+
"海边",
|
|
1663
|
+
"爬山",
|
|
1664
|
+
"城市",
|
|
1665
|
+
"远方",
|
|
1666
|
+
"路上",
|
|
1667
|
+
"散步"
|
|
1668
|
+
],
|
|
1669
|
+
food: [
|
|
1670
|
+
"好吃",
|
|
1671
|
+
"美食",
|
|
1672
|
+
"餐厅",
|
|
1673
|
+
"咖啡",
|
|
1674
|
+
"甜品",
|
|
1675
|
+
"味道",
|
|
1676
|
+
"烤肉",
|
|
1677
|
+
"火锅",
|
|
1678
|
+
"做饭"
|
|
1679
|
+
],
|
|
1680
|
+
workStudy: [
|
|
1681
|
+
"工作",
|
|
1682
|
+
"项目",
|
|
1683
|
+
"学习",
|
|
1684
|
+
"论文",
|
|
1685
|
+
"考试",
|
|
1686
|
+
"复盘",
|
|
1687
|
+
"成长",
|
|
1688
|
+
"加班",
|
|
1689
|
+
"研究",
|
|
1690
|
+
"产品"
|
|
1691
|
+
],
|
|
1692
|
+
creative: [
|
|
1693
|
+
"摄影",
|
|
1694
|
+
"音乐",
|
|
1695
|
+
"电影",
|
|
1696
|
+
"读书",
|
|
1697
|
+
"作品",
|
|
1698
|
+
"创作",
|
|
1699
|
+
"设计",
|
|
1700
|
+
"照片",
|
|
1701
|
+
"画画"
|
|
1702
|
+
],
|
|
1703
|
+
pet: [
|
|
1704
|
+
"小猫",
|
|
1705
|
+
"小狗",
|
|
1706
|
+
"猫咪",
|
|
1707
|
+
"狗狗",
|
|
1708
|
+
"宠物",
|
|
1709
|
+
"毛孩子"
|
|
1710
|
+
],
|
|
1711
|
+
useful: [
|
|
1712
|
+
"教程",
|
|
1713
|
+
"方法",
|
|
1714
|
+
"步骤",
|
|
1715
|
+
"经验",
|
|
1716
|
+
"分享",
|
|
1717
|
+
"建议",
|
|
1718
|
+
"怎么做"
|
|
1719
|
+
],
|
|
1720
|
+
warm: [
|
|
1721
|
+
"温柔",
|
|
1722
|
+
"治愈",
|
|
1723
|
+
"希望",
|
|
1724
|
+
"拥抱",
|
|
1725
|
+
"阳光",
|
|
1726
|
+
"安慰"
|
|
1727
|
+
],
|
|
1728
|
+
story: [
|
|
1729
|
+
"曾经",
|
|
1730
|
+
"后来",
|
|
1731
|
+
"那年",
|
|
1732
|
+
"小时候",
|
|
1733
|
+
"有一天",
|
|
1734
|
+
"今天",
|
|
1735
|
+
"昨天",
|
|
1736
|
+
"故事"
|
|
1737
|
+
]
|
|
1738
|
+
};
|
|
1739
|
+
const invitationClosings = [
|
|
1740
|
+
"如果你愿意,来录入一下声纹吧。以后再看到你的世界快记,我也能直接听见你的声音:",
|
|
1741
|
+
"愿意的话,也来录入声纹,给这条快记添上你的声音吧。我很期待以后不只看到文字,也能听见你:",
|
|
1742
|
+
"所以想认真邀请你录入声纹。这样下次在世界遇见你的文字,也能听到你亲口表达:",
|
|
1743
|
+
"要是你愿意,就来留下声纹吧。以后你的世界快记不只有文字,也会有属于你的声音:"
|
|
1744
|
+
];
|
|
1745
|
+
const urlPattern = /(?:(?:https?|ftp):\/\/|www\.)[^\s,。!?;:、"“”‘’()【】《》<>]+/gi;
|
|
1746
|
+
const trailingUrlPunctuation = /[.,;:!?,。;:!?))\]}>…]+$/;
|
|
1747
|
+
const whitespace = /\s+/g;
|
|
1748
|
+
const meaningfulCharacter = /[\p{L}\p{N}]/gu;
|
|
1749
|
+
const digitCharacter = /\p{N}/u;
|
|
1750
|
+
const latinCharacter = /[a-z]/i;
|
|
1751
|
+
const chineseCharacter = /[\u3400-\u9fff]/;
|
|
1752
|
+
const mathSymbol = /[+\-×÷*/=<>]/;
|
|
1753
|
+
function worldVoiceprintReadableText(value) {
|
|
1754
|
+
const normalized = value.replace(urlPattern, (matchedUrl) => matchedUrl.match(trailingUrlPunctuation)?.[0] ?? "").replace(/<>/g, " ").replace(whitespace, " ").trim();
|
|
1755
|
+
return /[\p{L}\p{N}]/u.test(normalized) ? normalized : "";
|
|
1756
|
+
}
|
|
1757
|
+
function buildWorldVoiceprintInviteMessage(input) {
|
|
1758
|
+
const preview = input.textPreview?.replace(whitespace, " ").trim();
|
|
1759
|
+
const variant = normalizedVariant(input.variantIndex ?? 0, 4);
|
|
1760
|
+
const copy = contentCopies[worldVoiceprintContentKind(preview ?? "")];
|
|
1761
|
+
const name = input.peerDisplayName.replace(whitespace, " ").trim();
|
|
1762
|
+
const greeting = name === "" || name === "这位用户" ? "我是看到你在世界发的这条快记才来找你的:" : `${name},我是看到你在世界发的这条快记才来找你的:`;
|
|
1763
|
+
const media = mediaSummary(input);
|
|
1764
|
+
return `${greeting}\n\n【来自世界的快记】\n${preview === void 0 || preview === "" ? `(你发布了一条${media === "" ? "" : `${media}的`}世界快记)` : `“${preview}”${media === "" ? "" : `\n(还包含${media})`}`}\n\n${copy.invitationReactions[variant]}\n\n${invitationClosings[variant]}\n${input.inviteUrl}`;
|
|
1765
|
+
}
|
|
1766
|
+
function mediaSummary(input) {
|
|
1767
|
+
const parts = [];
|
|
1768
|
+
if ((input.imageCount ?? 0) > 0) parts.push(`${String(Math.trunc(input.imageCount))}张图片`);
|
|
1769
|
+
if ((input.videoCount ?? 0) > 0) parts.push(`${String(Math.trunc(input.videoCount))}段视频`);
|
|
1770
|
+
if ((input.voiceCount ?? 0) > 0) parts.push(`${String(Math.trunc(input.voiceCount))}段语音`);
|
|
1771
|
+
return parts.join("、");
|
|
1772
|
+
}
|
|
1773
|
+
function normalizedVariant(value, count) {
|
|
1774
|
+
if (!Number.isFinite(value)) return 0;
|
|
1775
|
+
return (Math.trunc(value) % count + count) % count;
|
|
1776
|
+
}
|
|
1777
|
+
function worldVoiceprintContentKind(rawText) {
|
|
1778
|
+
const text = worldVoiceprintReadableText(rawText).toLowerCase();
|
|
1779
|
+
const meaningful = text.match(meaningfulCharacter) ?? [];
|
|
1780
|
+
if (meaningful.length === 0) return "unclear";
|
|
1781
|
+
if (looksNumericOrFormula(text, meaningful)) return "numeric";
|
|
1782
|
+
if (looksUnclear(text, meaningful)) return "unclear";
|
|
1783
|
+
if (meaningful.length <= 6) return "short";
|
|
1784
|
+
return bestMatchingCategory(text) ?? "default";
|
|
1785
|
+
}
|
|
1786
|
+
function looksNumericOrFormula(text, meaningful) {
|
|
1787
|
+
const digitCount = meaningful.filter((character) => digitCharacter.test(character)).length;
|
|
1788
|
+
if (digitCount === 0) return false;
|
|
1789
|
+
if (mathSymbol.test(text)) return true;
|
|
1790
|
+
return digitCount / meaningful.length >= .7;
|
|
1791
|
+
}
|
|
1792
|
+
function looksUnclear(text, meaningful) {
|
|
1793
|
+
const uniqueCharacters = new Set(meaningful);
|
|
1794
|
+
if (meaningful.length >= 5 && uniqueCharacters.size === 1) return true;
|
|
1795
|
+
if (meaningful.length >= 8 && uniqueCharacters.size <= 2) return true;
|
|
1796
|
+
const hasChinese = chineseCharacter.test(text);
|
|
1797
|
+
const hasLatin = latinCharacter.test(text);
|
|
1798
|
+
const hasDigit = digitCharacter.test(text);
|
|
1799
|
+
return !hasChinese && hasLatin && hasDigit && !text.includes(" ");
|
|
1800
|
+
}
|
|
1801
|
+
function bestMatchingCategory(text) {
|
|
1802
|
+
let bestCategory;
|
|
1803
|
+
let bestScore = 0;
|
|
1804
|
+
for (const [category, keywords] of Object.entries(categoryKeywords)) {
|
|
1805
|
+
const score = keywords.filter((keyword) => text.includes(keyword)).length;
|
|
1806
|
+
if (score > bestScore) {
|
|
1807
|
+
bestCategory = category;
|
|
1808
|
+
bestScore = score;
|
|
1809
|
+
}
|
|
1810
|
+
}
|
|
1811
|
+
return bestCategory;
|
|
1812
|
+
}
|
|
1574
1813
|
//#endregion
|
|
1575
1814
|
//#region src/services/ai-video-service.ts
|
|
1576
1815
|
function numberValue$24(value) {
|
|
@@ -2714,18 +2953,33 @@ const ARKME_PHONE_BIND_CODE_ERR = 3;
|
|
|
2714
2953
|
function numberValue$21(value) {
|
|
2715
2954
|
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
2716
2955
|
}
|
|
2956
|
+
function jiwoScanLoginAvailable(config) {
|
|
2957
|
+
try {
|
|
2958
|
+
const authUrl = new URL(config.authBaseUrl);
|
|
2959
|
+
const shareUrl = new URL(config.shareWebsite ?? "");
|
|
2960
|
+
if ([authUrl, shareUrl].some((url) => url.protocol !== "https:" || url.port !== "" || url.username !== "" || url.password !== "")) return false;
|
|
2961
|
+
const authHost = authUrl.hostname.toLowerCase();
|
|
2962
|
+
const shareHost = shareUrl.hostname.toLowerCase();
|
|
2963
|
+
return config.environment === "prod" ? authHost === "api.jotmo.cc" && shareHost === "jiwo.cc" : authHost === "jotmo.senguo.me" && shareHost === "jotmo-app.senguo.me";
|
|
2964
|
+
} catch {
|
|
2965
|
+
return false;
|
|
2966
|
+
}
|
|
2967
|
+
}
|
|
2717
2968
|
var AuthService = class {
|
|
2718
2969
|
runtime;
|
|
2719
2970
|
profile;
|
|
2720
2971
|
lifecycle;
|
|
2721
2972
|
attempts = /* @__PURE__ */ new Map();
|
|
2973
|
+
jiwoAttemptGeneration = 0;
|
|
2722
2974
|
constructor(runtime, profile, lifecycle) {
|
|
2723
2975
|
this.runtime = runtime;
|
|
2724
2976
|
this.profile = profile;
|
|
2725
2977
|
this.lifecycle = lifecycle;
|
|
2726
2978
|
}
|
|
2727
2979
|
dispose() {
|
|
2728
|
-
this.
|
|
2980
|
+
this.jiwoAttemptGeneration += 1;
|
|
2981
|
+
this.cancelAllJiwoLoginAttempts();
|
|
2982
|
+
for (const [attemptId, attempt] of this.attempts) if (attempt.kind === "wechat") this.attempts.delete(attemptId);
|
|
2729
2983
|
}
|
|
2730
2984
|
async logout() {
|
|
2731
2985
|
const activeSession = await this.runtime.sessionStore.read();
|
|
@@ -2738,6 +2992,8 @@ var AuthService = class {
|
|
|
2738
2992
|
await this.runtime.sessionStore.delete();
|
|
2739
2993
|
await this.runtime.clearPendingBindingSession();
|
|
2740
2994
|
this.profile.invalidate();
|
|
2995
|
+
this.jiwoAttemptGeneration += 1;
|
|
2996
|
+
await this.cancelAllJiwoLoginAttempts();
|
|
2741
2997
|
this.attempts.clear();
|
|
2742
2998
|
this.lifecycle.clearAccountState(userIds);
|
|
2743
2999
|
this.lifecycle.reconnectChatRealtime();
|
|
@@ -2773,6 +3029,8 @@ var AuthService = class {
|
|
|
2773
3029
|
};
|
|
2774
3030
|
}
|
|
2775
3031
|
async beginWechatLogin() {
|
|
3032
|
+
this.jiwoAttemptGeneration += 1;
|
|
3033
|
+
await this.cancelAllJiwoLoginAttempts();
|
|
2776
3034
|
const data = await this.runtime.post(this.runtime.config.authBaseUrl, "/api/public/v1/auth/wechat-oauth-login-qrcode", {}, void 0, [200]);
|
|
2777
3035
|
const sceneStr = stringValue$1(data.scene_str).trim();
|
|
2778
3036
|
const pollToken = stringValue$1(data.poll_token).trim();
|
|
@@ -2782,6 +3040,7 @@ var AuthService = class {
|
|
|
2782
3040
|
if (sceneStr === "" || pollToken === "" || qrContent === "") throw new ArkmePluginError("login-contract-invalid", "Arkme 登录二维码响应不完整", true, 502);
|
|
2783
3041
|
const attemptId = crypto.randomUUID();
|
|
2784
3042
|
const attempt = {
|
|
3043
|
+
kind: "wechat",
|
|
2785
3044
|
attemptId,
|
|
2786
3045
|
sceneStr,
|
|
2787
3046
|
pollToken,
|
|
@@ -2800,7 +3059,7 @@ var AuthService = class {
|
|
|
2800
3059
|
}
|
|
2801
3060
|
async pollWechatLogin(attemptId) {
|
|
2802
3061
|
const attempt = this.attempts.get(attemptId);
|
|
2803
|
-
if (attempt === void 0) throw new ArkmePluginError("login-attempt-not-found", "登录二维码已失效,请重新获取", false, 404);
|
|
3062
|
+
if (attempt === void 0 || attempt.kind !== "wechat") throw new ArkmePluginError("login-attempt-not-found", "登录二维码已失效,请重新获取", false, 404);
|
|
2804
3063
|
if (Date.now() >= attempt.expiresAtMillis) {
|
|
2805
3064
|
this.attempts.delete(attemptId);
|
|
2806
3065
|
return {
|
|
@@ -2834,6 +3093,111 @@ var AuthService = class {
|
|
|
2834
3093
|
this.attempts.delete(attemptId);
|
|
2835
3094
|
return await this.acceptLoginSession(session);
|
|
2836
3095
|
}
|
|
3096
|
+
async beginJiwoLogin() {
|
|
3097
|
+
if (!jiwoScanLoginAvailable(this.runtime.config)) throw new ArkmePluginError("jiwo-scan-login-disabled", "即我扫码登录当前未启用", false, 403);
|
|
3098
|
+
const attemptGeneration = ++this.jiwoAttemptGeneration;
|
|
3099
|
+
await this.cancelAllJiwoLoginAttempts();
|
|
3100
|
+
const data = await this.runtime.post(this.runtime.config.authBaseUrl, "/api/public/v1/auth/app-scan-login/start", {
|
|
3101
|
+
unique_code: await this.runtime.stateStore.uniqueCode(),
|
|
3102
|
+
ref: 0,
|
|
3103
|
+
keep_cancel: true
|
|
3104
|
+
}, void 0, [200]);
|
|
3105
|
+
const ticket = stringValue$1(data.ticket).trim();
|
|
3106
|
+
const pollSecret = stringValue$1(data.poll_secret).trim();
|
|
3107
|
+
const expiresAtMillis = numberValue$21(data.expires_at);
|
|
3108
|
+
if (!/^[A-Za-z0-9_-]{43}$/.test(ticket) || !/^[A-Za-z0-9_-]{43}$/.test(pollSecret) || expiresAtMillis <= Date.now()) throw new ArkmePluginError("jiwo-login-contract-invalid", "即我登录二维码响应不完整", true, 502);
|
|
3109
|
+
if (attemptGeneration !== this.jiwoAttemptGeneration) {
|
|
3110
|
+
await this.cancelJiwoCredentials(ticket, pollSecret);
|
|
3111
|
+
throw new ArkmePluginError("login-attempt-canceled", "登录方式已切换", false, 409);
|
|
3112
|
+
}
|
|
3113
|
+
const shareWebsite = this.runtime.config.shareWebsite ?? "";
|
|
3114
|
+
const qrUrl = new URL("/login/desktop", shareWebsite);
|
|
3115
|
+
qrUrl.searchParams.set("ticket", ticket);
|
|
3116
|
+
const attemptId = crypto.randomUUID();
|
|
3117
|
+
const attempt = {
|
|
3118
|
+
kind: "jiwo",
|
|
3119
|
+
attemptId,
|
|
3120
|
+
ticket,
|
|
3121
|
+
pollSecret,
|
|
3122
|
+
qrContent: qrUrl.toString(),
|
|
3123
|
+
expiresAtMillis
|
|
3124
|
+
};
|
|
3125
|
+
this.attempts.clear();
|
|
3126
|
+
this.attempts.set(attemptId, attempt);
|
|
3127
|
+
return this.jiwoPendingSnapshot(attempt);
|
|
3128
|
+
}
|
|
3129
|
+
async pollJiwoLogin(attemptId) {
|
|
3130
|
+
const attempt = this.attempts.get(attemptId);
|
|
3131
|
+
if (attempt === void 0 || attempt.kind !== "jiwo") throw new ArkmePluginError("login-attempt-not-found", "登录二维码已失效,请重新获取", false, 404);
|
|
3132
|
+
if (Date.now() >= attempt.expiresAtMillis) {
|
|
3133
|
+
await this.cancelJiwoLogin(attemptId);
|
|
3134
|
+
return {
|
|
3135
|
+
status: "expired",
|
|
3136
|
+
environment: this.runtime.config.environment
|
|
3137
|
+
};
|
|
3138
|
+
}
|
|
3139
|
+
const data = await this.runtime.post(this.runtime.config.authBaseUrl, "/api/public/v1/auth/app-scan-login/poll", {
|
|
3140
|
+
ticket: attempt.ticket,
|
|
3141
|
+
poll_secret: attempt.pollSecret
|
|
3142
|
+
}, void 0, [200]);
|
|
3143
|
+
if (this.attempts.get(attemptId) !== attempt) return {
|
|
3144
|
+
status: "expired",
|
|
3145
|
+
environment: this.runtime.config.environment
|
|
3146
|
+
};
|
|
3147
|
+
const status = stringValue$1(data.status).trim();
|
|
3148
|
+
if (status === "pending") return this.jiwoPendingSnapshot(attempt);
|
|
3149
|
+
if (status === "expired") {
|
|
3150
|
+
this.attempts.delete(attemptId);
|
|
3151
|
+
return {
|
|
3152
|
+
status: "expired",
|
|
3153
|
+
environment: this.runtime.config.environment
|
|
3154
|
+
};
|
|
3155
|
+
}
|
|
3156
|
+
if (status !== "authenticated") throw new ArkmePluginError("jiwo-login-contract-invalid", "即我扫码登录状态无效", true, 502);
|
|
3157
|
+
const userId = numberValue$21(data.user_id);
|
|
3158
|
+
const accessToken = stringValue$1(data.access_token);
|
|
3159
|
+
const refreshToken = stringValue$1(data.refresh_token);
|
|
3160
|
+
if (userId <= 0 || accessToken === "" || refreshToken === "") throw new ArkmePluginError("login-contract-invalid", "Arkme 登录成功响应缺少凭据", false, 502);
|
|
3161
|
+
this.attempts.delete(attemptId);
|
|
3162
|
+
return await this.acceptLoginSession({
|
|
3163
|
+
accessToken,
|
|
3164
|
+
refreshToken,
|
|
3165
|
+
userId
|
|
3166
|
+
});
|
|
3167
|
+
}
|
|
3168
|
+
async cancelJiwoLogin(attemptId) {
|
|
3169
|
+
if (attemptId.trim() === "") return { canceled: true };
|
|
3170
|
+
const attempt = this.attempts.get(attemptId);
|
|
3171
|
+
if (attempt === void 0 || attempt.kind !== "jiwo") return { canceled: true };
|
|
3172
|
+
this.jiwoAttemptGeneration += 1;
|
|
3173
|
+
this.attempts.delete(attemptId);
|
|
3174
|
+
await this.cancelJiwoCredentials(attempt.ticket, attempt.pollSecret);
|
|
3175
|
+
return { canceled: true };
|
|
3176
|
+
}
|
|
3177
|
+
async cancelJiwoCredentials(ticket, pollSecret) {
|
|
3178
|
+
try {
|
|
3179
|
+
await this.runtime.post(this.runtime.config.authBaseUrl, "/api/public/v1/auth/app-scan-login/cancel", {
|
|
3180
|
+
ticket,
|
|
3181
|
+
poll_secret: pollSecret
|
|
3182
|
+
}, void 0, [200]);
|
|
3183
|
+
} catch {}
|
|
3184
|
+
}
|
|
3185
|
+
jiwoPendingSnapshot(attempt) {
|
|
3186
|
+
return {
|
|
3187
|
+
status: "pending",
|
|
3188
|
+
environment: this.runtime.config.environment,
|
|
3189
|
+
attemptId: attempt.attemptId,
|
|
3190
|
+
qrContent: attempt.qrContent,
|
|
3191
|
+
expiresAtMillis: attempt.expiresAtMillis
|
|
3192
|
+
};
|
|
3193
|
+
}
|
|
3194
|
+
async cancelAllJiwoLoginAttempts() {
|
|
3195
|
+
const attempts = [...this.attempts.values()].filter((attempt) => attempt.kind === "jiwo");
|
|
3196
|
+
for (const attempt of attempts) this.attempts.delete(attempt.attemptId);
|
|
3197
|
+
await Promise.all(attempts.map(async (attempt) => {
|
|
3198
|
+
await this.cancelJiwoCredentials(attempt.ticket, attempt.pollSecret);
|
|
3199
|
+
}));
|
|
3200
|
+
}
|
|
2837
3201
|
async testLogin(userId) {
|
|
2838
3202
|
if (this.runtime.config.environment !== "test") throw new ArkmePluginError("test-login-disabled", "测试账号登录仅允许测试环境使用", false, 403);
|
|
2839
3203
|
if (!Number.isSafeInteger(userId) || userId <= 0) throw new ArkmePluginError("test-user-id-invalid", "请输入有效的测试账号 user_id", false);
|
|
@@ -2964,17 +3328,6 @@ function isArkmeBotAvatarRef(value) {
|
|
|
2964
3328
|
return ARKME_BOT_AVATAR_REF_PATTERN.test(value.trim());
|
|
2965
3329
|
}
|
|
2966
3330
|
//#endregion
|
|
2967
|
-
//#region src/secret-value.ts
|
|
2968
|
-
var SecretValue = class {
|
|
2969
|
-
#value;
|
|
2970
|
-
constructor(value) {
|
|
2971
|
-
this.#value = value;
|
|
2972
|
-
}
|
|
2973
|
-
reveal() {
|
|
2974
|
-
return this.#value;
|
|
2975
|
-
}
|
|
2976
|
-
};
|
|
2977
|
-
//#endregion
|
|
2978
3331
|
//#region src/services/privacy-visibility.ts
|
|
2979
3332
|
const CONTENT_ACCESS_PROTECTED = 2;
|
|
2980
3333
|
const TOPIC_PRIVACY_PRIVATE = 2;
|
|
@@ -16076,6 +16429,7 @@ var ArkmeService = class {
|
|
|
16076
16429
|
fetchImpl;
|
|
16077
16430
|
pendingSessionStore;
|
|
16078
16431
|
runtime;
|
|
16432
|
+
billingGateway;
|
|
16079
16433
|
aiVideo;
|
|
16080
16434
|
arrangement;
|
|
16081
16435
|
calendar;
|
|
@@ -16106,13 +16460,14 @@ var ArkmeService = class {
|
|
|
16106
16460
|
unmarkedSpeaker;
|
|
16107
16461
|
voiceprint;
|
|
16108
16462
|
worldVoiceprintInviteVariantIndex = 0;
|
|
16109
|
-
constructor(config, sessionStore, stateStore, fetchImpl = fetch, pendingSessionStore, outgoingCallBroker = new ArkmeOutgoingCallBroker()) {
|
|
16463
|
+
constructor(config, sessionStore, stateStore, fetchImpl = fetch, pendingSessionStore, outgoingCallBroker = new ArkmeOutgoingCallBroker(), billingGateway) {
|
|
16110
16464
|
this.config = config;
|
|
16111
16465
|
this.sessionStore = sessionStore;
|
|
16112
16466
|
this.stateStore = stateStore;
|
|
16113
16467
|
this.fetchImpl = fetchImpl;
|
|
16114
16468
|
this.pendingSessionStore = pendingSessionStore;
|
|
16115
16469
|
this.runtime = new ServiceRuntime(config, sessionStore, stateStore, fetchImpl, pendingSessionStore);
|
|
16470
|
+
this.billingGateway = billingGateway ?? new HttpArkmeBillingGateway(this.runtime);
|
|
16116
16471
|
this.privacy = new ArkmePrivacyVisibilityService(this.runtime);
|
|
16117
16472
|
this.aiVideo = new AiVideoService(this.runtime);
|
|
16118
16473
|
this.arrangement = new ArrangementService(this.runtime);
|
|
@@ -16179,6 +16534,9 @@ var ArkmeService = class {
|
|
|
16179
16534
|
chatRealtimeState() {
|
|
16180
16535
|
return this.realtime.chatRealtimeState();
|
|
16181
16536
|
}
|
|
16537
|
+
async resolveManagedAccessCredential() {
|
|
16538
|
+
return await resolveManagedAccessCredential(this.runtime);
|
|
16539
|
+
}
|
|
16182
16540
|
subscribeChatRealtime(listener) {
|
|
16183
16541
|
return this.realtime.subscribeChatRealtime(listener);
|
|
16184
16542
|
}
|
|
@@ -16262,11 +16620,32 @@ var ArkmeService = class {
|
|
|
16262
16620
|
captchaId: this.config.geetestCaptchaId,
|
|
16263
16621
|
environment: this.config.environment,
|
|
16264
16622
|
testLoginEnabled: this.config.environment === "test",
|
|
16623
|
+
jiwoScanLoginEnabled: jiwoScanLoginAvailable(this.config),
|
|
16265
16624
|
callAssetBasePath: `${this.config.routePath}/call`,
|
|
16266
16625
|
voiceprintEnrollmentPath: `${this.config.routePath}/voiceprint/enroll`,
|
|
16267
16626
|
shareWebsite: this.config.shareWebsite ?? "https://app.arkme.ai"
|
|
16268
16627
|
};
|
|
16269
16628
|
}
|
|
16629
|
+
async billingQuota(signal) {
|
|
16630
|
+
return await this.callBillingGateway(() => this.billingGateway.quota(signal));
|
|
16631
|
+
}
|
|
16632
|
+
async billingProducts(signal) {
|
|
16633
|
+
return await this.callBillingGateway(() => this.billingGateway.products(signal));
|
|
16634
|
+
}
|
|
16635
|
+
async createBillingOrder(input, signal) {
|
|
16636
|
+
return await this.callBillingGateway(() => this.billingGateway.createOrder(input, signal));
|
|
16637
|
+
}
|
|
16638
|
+
async billingOrderStatus(orderId, signal) {
|
|
16639
|
+
return await this.callBillingGateway(() => this.billingGateway.orderStatus(orderId, signal));
|
|
16640
|
+
}
|
|
16641
|
+
async callBillingGateway(operation) {
|
|
16642
|
+
try {
|
|
16643
|
+
return await operation();
|
|
16644
|
+
} catch (error) {
|
|
16645
|
+
if (error instanceof ArkmeBillingUnavailableError) throw new ArkmePluginError("billing-unavailable", error.message, true, 503, { cause: error });
|
|
16646
|
+
throw error;
|
|
16647
|
+
}
|
|
16648
|
+
}
|
|
16270
16649
|
providerCapabilities() {
|
|
16271
16650
|
return {
|
|
16272
16651
|
contractVersion: 1,
|
|
@@ -16762,6 +17141,15 @@ var ArkmeService = class {
|
|
|
16762
17141
|
async pollWechatLogin(attemptId) {
|
|
16763
17142
|
return await this.auth.pollWechatLogin(attemptId);
|
|
16764
17143
|
}
|
|
17144
|
+
async beginJiwoLogin() {
|
|
17145
|
+
return await this.auth.beginJiwoLogin();
|
|
17146
|
+
}
|
|
17147
|
+
async pollJiwoLogin(attemptId) {
|
|
17148
|
+
return await this.auth.pollJiwoLogin(attemptId);
|
|
17149
|
+
}
|
|
17150
|
+
async cancelJiwoLogin(attemptId) {
|
|
17151
|
+
return await this.auth.cancelJiwoLogin(attemptId);
|
|
17152
|
+
}
|
|
16765
17153
|
async testLogin(userId) {
|
|
16766
17154
|
return await this.auth.testLogin(userId);
|
|
16767
17155
|
}
|