dsh-deepmoyu 2.3.0 → 2.4.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 (90) hide show
  1. package/AGENTS.md +93 -153
  2. package/CHANGELOG.md +36 -0
  3. package/CONTRIBUTING.md +51 -0
  4. package/CONTRIBUTORS.md +3 -7
  5. package/NOTICE.md +3 -3
  6. package/README.md +143 -81
  7. package/SECURITY.md +21 -0
  8. package/assets/screenshots/memory.png +0 -0
  9. package/assets/screenshots/menu.png +0 -0
  10. package/assets/screenshots/workspace.png +0 -0
  11. package/client.js +1404 -441
  12. package/cordis.patch.yml +1 -1
  13. package/docs/ASSETS.md +25 -0
  14. package/docs/DEPLOYMENT.md +102 -0
  15. package/docs/PRIVACY.md +32 -0
  16. package/docs/RELEASE_AUDIT.md +70 -0
  17. package/docs/RELEASING.md +94 -0
  18. package/docs/TEST_MATRIX.md +77 -0
  19. package/docs/assets-permissions.json +20 -0
  20. package/docs/tool-evidence.json +196 -0
  21. package/index.js +1095 -285
  22. package/lib/asr.py +41 -35
  23. package/lib/background-output.mjs +64 -17
  24. package/lib/background-tools.mjs +1 -0
  25. package/lib/companion.mjs +43 -0
  26. package/lib/config-policy.mjs +20 -0
  27. package/lib/favops.py +9 -2
  28. package/lib/ids.mjs +41 -0
  29. package/lib/login-session.mjs +34 -0
  30. package/lib/login.mjs +5 -4
  31. package/lib/mcp.mjs +3 -2
  32. package/lib/memory-center.mjs +74 -0
  33. package/lib/mode.mjs +6 -7
  34. package/lib/model-relay.mjs +5 -4
  35. package/lib/open-directory.mjs +93 -0
  36. package/lib/pace.mjs +92 -12
  37. package/lib/persona.mjs +46 -0
  38. package/lib/product-status.mjs +123 -0
  39. package/lib/prompts.mjs +40 -5
  40. package/lib/pv-production.mjs +280 -0
  41. package/lib/pv-session.mjs +316 -0
  42. package/lib/pv.mjs +398 -306
  43. package/lib/pv.py +84 -22
  44. package/lib/pv_cover.py +108 -26
  45. package/lib/pv_editorial.py +336 -0
  46. package/lib/pv_verify.py +267 -0
  47. package/lib/python-env.mjs +61 -0
  48. package/lib/reply.mjs +10 -3
  49. package/lib/run-settings.mjs +6 -0
  50. package/lib/sticker-automation.mjs +5 -4
  51. package/lib/sticker.mjs +135 -25
  52. package/lib/tool-scope.mjs +65 -0
  53. package/lib/update.mjs +265 -0
  54. package/lib/uvfetch.mjs +165 -153
  55. package/lib/verify.mjs +111 -127
  56. package/marketplace/pyyxzi__dsh-deepmoyu.yml +7 -0
  57. package/package.json +23 -12
  58. package/presets/deepmoyu.patch.yml +1 -1
  59. package/prompts/duty.md +4 -3
  60. package/screenshots.json +5 -0
  61. package/selfcheck.mjs +21 -6
  62. package/vendor/bilibili-mcp/requirements.in +9 -0
  63. package/vendor/bilibili-mcp/requirements.txt +1936 -11
  64. package/vendor/bilibili-mcp/runtime_guard.py +35 -10
  65. package/lib/__pycache__/pv.cpython-310.pyc +0 -0
  66. package/lib/__pycache__/pv_cover.cpython-310.pyc +0 -0
  67. package/tests/automation-routes.test.mjs +0 -183
  68. package/tests/automation.test.mjs +0 -79
  69. package/tests/background-tools.test.mjs +0 -182
  70. package/tests/background-window.test.mjs +0 -75
  71. package/tests/dup-cross-channel.test.mjs +0 -158
  72. package/tests/manual-browsing.test.mjs +0 -671
  73. package/tests/mcp-restart.test.mjs +0 -131
  74. package/tests/memory-chain.test.mjs +0 -413
  75. package/tests/model-relay.test.mjs +0 -116
  76. package/tests/persona.test.mjs +0 -67
  77. package/tests/pet-run-routes.test.mjs +0 -127
  78. package/tests/pet-run-settings.test.mjs +0 -38
  79. package/tests/pet-session.test.mjs +0 -125
  80. package/tests/pv-stage1.test.mjs +0 -203
  81. package/tests/sticker-routes.test.mjs +0 -141
  82. package/tests/sticker-upload-routes.test.mjs +0 -97
  83. package/tests/sticker-upload.test.mjs +0 -122
  84. package/tests/sticker.test.mjs +0 -169
  85. package/tests/test_guard_cross_channel.py +0 -141
  86. package/tests/test_pv_cover.py +0 -95
  87. package/tests/test_pv_stage1.py +0 -250
  88. package/tests/test_reply_pictures.py +0 -255
  89. package/tests/token-ledger.test.mjs +0 -78
  90. package/tests/verify_pv_sample.py +0 -91
@@ -1,91 +0,0 @@
1
- """Verify an actual local MP4 against measured utterance timing and decoded PCM.
2
-
3
- Usage: python verify_pv_sample.py <sample.mp4> <render-directory>
4
- Produces decoded first/middle/last frames, clean speech WAV, audio-check.json.
5
- It does not perform ASR or claim that an agent listened to the audio.
6
- """
7
- from __future__ import annotations
8
-
9
- import argparse
10
- import array
11
- import json
12
- import math
13
- import subprocess
14
- import wave
15
- from pathlib import Path
16
-
17
- import imageio_ffmpeg
18
- from PIL import Image
19
-
20
-
21
- def waveform(path):
22
- with wave.open(str(path), "rb") as handle:
23
- assert handle.getsampwidth() == 2, "expected 16-bit PCM"
24
- samples = array.array("h")
25
- samples.frombytes(handle.readframes(handle.getnframes()))
26
- channels, rate, frames = handle.getnchannels(), handle.getframerate(), handle.getnframes()
27
- peak = max((abs(value) for value in samples), default=0)
28
- rms = math.sqrt(sum(value * value for value in samples) / max(1, len(samples)))
29
- return {"duration": frames / rate, "sample_rate": rate, "channels": channels,
30
- "peak_dbfs": round(20 * math.log10(max(1, peak) / 32768), 3),
31
- "rms_dbfs": round(20 * math.log10(max(1, rms) / 32768), 3),
32
- "clipped_samples": sum(abs(value) >= 32767 for value in samples),
33
- "sample_count": len(samples)}
34
-
35
-
36
- def verify(video, work):
37
- timeline = json.loads((work / "timeline.json").read_text(encoding="utf-8"))
38
- fps, duration = timeline["fps"], timeline["duration"]
39
- frames_expected = round(fps * duration)
40
- selected = {max(1, int(timeline["cues"][0]["start"] * fps) + 1): "first",
41
- frames_expected // 2: "middle", frames_expected - 2: "last"}
42
- reader = imageio_ffmpeg.read_frames(str(video), pix_fmt="rgb24")
43
- metadata = next(reader)
44
- count = 0
45
- for index, pixels in enumerate(reader):
46
- count += 1
47
- if index in selected:
48
- Image.frombytes("RGB", metadata["size"], pixels).save(work / f"decoded_{selected[index]}.png")
49
- assert count == frames_expected, (count, frames_expected)
50
- assert abs(metadata["fps"] - fps) < .001
51
- assert metadata.get("audio_codec"), "MP4 has no audio stream"
52
-
53
- decoded = work / "decoded_audio.wav"
54
- subprocess.run([imageio_ffmpeg.get_ffmpeg_exe(), "-y", "-v", "error", "-i", str(video),
55
- "-vn", "-acodec", "pcm_s16le", "-ar", "24000", str(decoded)], check=True)
56
- mixed = waveform(decoded)
57
- speech = waveform(work / "narr.wav")
58
- assert abs(mixed["duration"] - duration) < .12, (mixed["duration"], duration)
59
- assert abs(speech["duration"] - duration) <= 1 / 24000
60
- assert mixed["clipped_samples"] == 0
61
- assert mixed["rms_dbfs"] > -40
62
-
63
- checks = []
64
- previous_end = 0
65
- for cue in timeline["cues"]:
66
- take = waveform(work / "wav" / f"{cue['shot']:02d}.wav")
67
- assert abs(take["duration"] - cue["audio_duration"]) <= 1 / 24000
68
- assert abs((cue["end"] - cue["start"]) - take["duration"]) < .000002
69
- assert cue["start"] >= previous_end
70
- assert cue["mouth_frames"] > 0
71
- assert take["rms_dbfs"] > -40
72
- previous_end = cue["end"]
73
- checks.append({"shot": cue["shot"], "speaker": cue["speaker"], "engine": cue["engine"],
74
- "duration": take["duration"], "rms_dbfs": take["rms_dbfs"],
75
- "mouth_frames": cue["mouth_frames"]})
76
- assert duration - previous_end >= .5 - 1 / 24000
77
- result = {"status": "passed", "video": video.name, "frame_count": count,
78
- "size": list(metadata["size"]), "fps": metadata["fps"], "duration": duration,
79
- "audio": mixed, "speech": speech, "cue_checks": checks,
80
- "limitations": ["Acoustic and timing checks only; no ASR or human listening test.",
81
- "Mouth motion uses RMS, not phoneme alignment."]}
82
- (work / "audio-check.json").write_text(json.dumps(result, ensure_ascii=False, indent=2), encoding="utf-8")
83
- print(json.dumps(result, ensure_ascii=False, indent=2))
84
-
85
-
86
- if __name__ == "__main__":
87
- parser = argparse.ArgumentParser()
88
- parser.add_argument("video", type=Path)
89
- parser.add_argument("work", type=Path)
90
- args = parser.parse_args()
91
- verify(args.video, args.work)