@fg-lab/amadeus-qq 1.2.1 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +55 -53
  3. package/README.zh-CN.md +51 -51
  4. package/REFERENCE.md +49 -49
  5. package/cordis.patch.yml +6 -6
  6. package/docs/DATA_MIGRATION.md +26 -26
  7. package/docs/INSTALL_V1_2.md +22 -22
  8. package/docs/PUBLISHING.md +28 -26
  9. package/docs/RELEASE_CHECKLIST.md +23 -23
  10. package/docs/RELEASE_NOTES.md +38 -38
  11. package/docs/UPGRADE_POLICY.md +26 -26
  12. package/docs/verification/IMAGE_READ_FIX_2026-11-07.md +22 -0
  13. package/docs/verification/LIFE_QQ_BOUNDARY_2026-11-07.md +20 -0
  14. package/lib/client.js +372 -372
  15. package/package.json +94 -91
  16. package/preset.patch.yml +63 -0
  17. package/scripts/symlink-security.mjs +1 -1
  18. package/src/account-profile.js +223 -223
  19. package/src/admin-http.js +361 -361
  20. package/src/audio.js +428 -428
  21. package/src/capabilities.js +785 -785
  22. package/src/client/factory.template.js +82 -82
  23. package/src/client/logic.js +290 -290
  24. package/src/config.js +2 -0
  25. package/src/console/console.css +28 -28
  26. package/src/console/console.js +3 -0
  27. package/src/console/index.html +67 -67
  28. package/src/core.js +21 -4
  29. package/src/files.js +230 -230
  30. package/src/index.js +135 -8
  31. package/src/lease.js +42 -42
  32. package/src/media.js +66 -3
  33. package/src/napcat-action-names.js +186 -186
  34. package/src/native-asr.js +112 -112
  35. package/src/onebot.js +378 -378
  36. package/src/outbound-media.js +309 -309
  37. package/src/owner-workspace.js +394 -394
  38. package/src/permissions.js +20 -0
  39. package/src/preset-guard.mjs +16 -2
  40. package/src/qzone-provider.js +628 -628
  41. package/src/session-provenance.js +74 -74
  42. package/src/setup.js +255 -255
  43. package/src/web-console.js +9 -9
@@ -1,74 +1,74 @@
1
- import fs from 'node:fs'
2
- import path from 'node:path'
3
- import { randomUUID } from 'node:crypto'
4
-
5
- // This journal is separate from replaceable scene configuration. Never infer
6
- // ordinary provenance from absence in a potentially incomplete historical index.
7
- export class SessionProvenance {
8
- constructor(file, { sessions, store, now = () => Date.now() }) {
9
- this.file = file
10
- this.sessions = sessions
11
- this.store = store
12
- this.now = now
13
- this.observedSince = now()
14
- this.pending = new Set()
15
- this.state = fs.existsSync(file) ? JSON.parse(fs.readFileSync(file, 'utf8')) :
16
- { version: 1, observedSince: now(), reserved: [] }
17
- if (this.state.version !== 1 || !Number.isSafeInteger(this.state.observedSince) ||
18
- !Array.isArray(this.state.reserved) || this.state.reserved.some(id => typeof id !== 'string')) {
19
- throw new Error('QQ_PROVENANCE_INVALID: restore the provenance backup; refusing reset')
20
- }
21
- if (!fs.existsSync(file)) this.persist(this.state)
22
- this.state.ordinary ??= {}
23
- if (typeof this.state.ordinary !== 'object' || Array.isArray(this.state.ordinary) || this.state.ordinary === null) {
24
- throw new Error('QQ_PROVENANCE_INVALID: invalid ordinary attestations')
25
- }
26
- }
27
- persist(state) {
28
- fs.mkdirSync(path.dirname(this.file), { recursive: true })
29
- const temp = `${this.file}.${randomUUID()}.tmp`
30
- const fd = fs.openSync(temp, 'wx', 0o600)
31
- try { fs.writeFileSync(fd, JSON.stringify(state)); fs.fsyncSync(fd) } finally { fs.closeSync(fd) }
32
- fs.renameSync(temp, this.file)
33
- }
34
- reserve() {
35
- const id = `session-${randomUUID()}`
36
- const next = { ...this.state, reserved: [...this.state.reserved, id] }
37
- this.persist(next) // Must precede even the first Host create await.
38
- this.state = next
39
- this.pending.add(id)
40
- return id
41
- }
42
- complete(id, returnedId) {
43
- if (returnedId !== undefined && returnedId !== id) {
44
- const next = { ...this.state, unsupportedHost: true }
45
- this.persist(next)
46
- this.state = next
47
- }
48
- this.pending.delete(id)
49
- }
50
- classify(sessionId) {
51
- const session = this.sessions()?.get?.(sessionId)
52
- if (!session || session.id !== sessionId) return { kind: 'unknown', reason: 'session-unavailable' }
53
- if (this.store.isKnownSession(sessionId) || this.state.reserved.includes(sessionId)) {
54
- return { kind: 'qq', reason: 'durable-qq-reservation' }
55
- }
56
- const raw = session.header?.createdAt
57
- const created = typeof raw === 'number' ? raw : typeof raw === 'string' ? Date.parse(raw) : NaN
58
- // Before journal enrollment, failed/unbound historical QQ creation is not
59
- // reconstructible. Require a fresh Host Session instead of guessing.
60
- if (Number.isFinite(created) && Object.hasOwn(this.state.ordinary, sessionId) && this.state.ordinary[sessionId] === created) {
61
- return { kind: 'ordinary', reason: 'durable-host-attestation' }
62
- }
63
- if (this.pending.size || this.state.unsupportedHost) return { kind: 'unknown', reason: 'qq-creation-contract-unverified' }
64
- // A previous process's observation interval is not evidence for Sessions
65
- // created while it was absent (or an older connector was running).
66
- if (!Number.isFinite(created) || created <= this.observedSince || created > this.now()) {
67
- return { kind: 'unknown', reason: 'historical-provenance-unverified' }
68
- }
69
- const next = { ...this.state, ordinary: { ...this.state.ordinary, [sessionId]: created } }
70
- this.persist(next)
71
- this.state = next
72
- return { kind: 'ordinary', reason: 'host-created-after-reservation-contract' }
73
- }
74
- }
1
+ import fs from 'node:fs'
2
+ import path from 'node:path'
3
+ import { randomUUID } from 'node:crypto'
4
+
5
+ // This journal is separate from replaceable scene configuration. Never infer
6
+ // ordinary provenance from absence in a potentially incomplete historical index.
7
+ export class SessionProvenance {
8
+ constructor(file, { sessions, store, now = () => Date.now() }) {
9
+ this.file = file
10
+ this.sessions = sessions
11
+ this.store = store
12
+ this.now = now
13
+ this.observedSince = now()
14
+ this.pending = new Set()
15
+ this.state = fs.existsSync(file) ? JSON.parse(fs.readFileSync(file, 'utf8')) :
16
+ { version: 1, observedSince: now(), reserved: [] }
17
+ if (this.state.version !== 1 || !Number.isSafeInteger(this.state.observedSince) ||
18
+ !Array.isArray(this.state.reserved) || this.state.reserved.some(id => typeof id !== 'string')) {
19
+ throw new Error('QQ_PROVENANCE_INVALID: restore the provenance backup; refusing reset')
20
+ }
21
+ if (!fs.existsSync(file)) this.persist(this.state)
22
+ this.state.ordinary ??= {}
23
+ if (typeof this.state.ordinary !== 'object' || Array.isArray(this.state.ordinary) || this.state.ordinary === null) {
24
+ throw new Error('QQ_PROVENANCE_INVALID: invalid ordinary attestations')
25
+ }
26
+ }
27
+ persist(state) {
28
+ fs.mkdirSync(path.dirname(this.file), { recursive: true })
29
+ const temp = `${this.file}.${randomUUID()}.tmp`
30
+ const fd = fs.openSync(temp, 'wx', 0o600)
31
+ try { fs.writeFileSync(fd, JSON.stringify(state)); fs.fsyncSync(fd) } finally { fs.closeSync(fd) }
32
+ fs.renameSync(temp, this.file)
33
+ }
34
+ reserve() {
35
+ const id = `session-${randomUUID()}`
36
+ const next = { ...this.state, reserved: [...this.state.reserved, id] }
37
+ this.persist(next) // Must precede even the first Host create await.
38
+ this.state = next
39
+ this.pending.add(id)
40
+ return id
41
+ }
42
+ complete(id, returnedId) {
43
+ if (returnedId !== undefined && returnedId !== id) {
44
+ const next = { ...this.state, unsupportedHost: true }
45
+ this.persist(next)
46
+ this.state = next
47
+ }
48
+ this.pending.delete(id)
49
+ }
50
+ classify(sessionId) {
51
+ const session = this.sessions()?.get?.(sessionId)
52
+ if (!session || session.id !== sessionId) return { kind: 'unknown', reason: 'session-unavailable' }
53
+ if (this.store.isKnownSession(sessionId) || this.state.reserved.includes(sessionId)) {
54
+ return { kind: 'qq', reason: 'durable-qq-reservation' }
55
+ }
56
+ const raw = session.header?.createdAt
57
+ const created = typeof raw === 'number' ? raw : typeof raw === 'string' ? Date.parse(raw) : NaN
58
+ // Before journal enrollment, failed/unbound historical QQ creation is not
59
+ // reconstructible. Require a fresh Host Session instead of guessing.
60
+ if (Number.isFinite(created) && Object.hasOwn(this.state.ordinary, sessionId) && this.state.ordinary[sessionId] === created) {
61
+ return { kind: 'ordinary', reason: 'durable-host-attestation' }
62
+ }
63
+ if (this.pending.size || this.state.unsupportedHost) return { kind: 'unknown', reason: 'qq-creation-contract-unverified' }
64
+ // A previous process's observation interval is not evidence for Sessions
65
+ // created while it was absent (or an older connector was running).
66
+ if (!Number.isFinite(created) || created <= this.observedSince || created > this.now()) {
67
+ return { kind: 'unknown', reason: 'historical-provenance-unverified' }
68
+ }
69
+ const next = { ...this.state, ordinary: { ...this.state.ordinary, [sessionId]: created } }
70
+ this.persist(next)
71
+ this.state = next
72
+ return { kind: 'ordinary', reason: 'host-created-after-reservation-contract' }
73
+ }
74
+ }