dsh-deepmoyu 2.3.0 → 2.4.1

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 (89) hide show
  1. package/AGENTS.md +91 -153
  2. package/CHANGELOG.md +27 -0
  3. package/CONTRIBUTING.md +51 -0
  4. package/CONTRIBUTORS.md +3 -7
  5. package/NOTICE.md +3 -3
  6. package/README.md +141 -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 +1348 -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 +953 -270
  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/uvfetch.mjs +165 -153
  54. package/lib/verify.mjs +111 -127
  55. package/marketplace/pyyxzi__dsh-deepmoyu.yml +7 -0
  56. package/package.json +23 -12
  57. package/presets/deepmoyu.patch.yml +1 -1
  58. package/prompts/duty.md +4 -3
  59. package/screenshots.json +5 -0
  60. package/selfcheck.mjs +21 -6
  61. package/vendor/bilibili-mcp/requirements.in +9 -0
  62. package/vendor/bilibili-mcp/requirements.txt +1936 -11
  63. package/vendor/bilibili-mcp/runtime_guard.py +35 -10
  64. package/lib/__pycache__/pv.cpython-310.pyc +0 -0
  65. package/lib/__pycache__/pv_cover.cpython-310.pyc +0 -0
  66. package/tests/automation-routes.test.mjs +0 -183
  67. package/tests/automation.test.mjs +0 -79
  68. package/tests/background-tools.test.mjs +0 -182
  69. package/tests/background-window.test.mjs +0 -75
  70. package/tests/dup-cross-channel.test.mjs +0 -158
  71. package/tests/manual-browsing.test.mjs +0 -671
  72. package/tests/mcp-restart.test.mjs +0 -131
  73. package/tests/memory-chain.test.mjs +0 -413
  74. package/tests/model-relay.test.mjs +0 -116
  75. package/tests/persona.test.mjs +0 -67
  76. package/tests/pet-run-routes.test.mjs +0 -127
  77. package/tests/pet-run-settings.test.mjs +0 -38
  78. package/tests/pet-session.test.mjs +0 -125
  79. package/tests/pv-stage1.test.mjs +0 -203
  80. package/tests/sticker-routes.test.mjs +0 -141
  81. package/tests/sticker-upload-routes.test.mjs +0 -97
  82. package/tests/sticker-upload.test.mjs +0 -122
  83. package/tests/sticker.test.mjs +0 -169
  84. package/tests/test_guard_cross_channel.py +0 -141
  85. package/tests/test_pv_cover.py +0 -95
  86. package/tests/test_pv_stage1.py +0 -250
  87. package/tests/test_reply_pictures.py +0 -255
  88. package/tests/token-ledger.test.mjs +0 -78
  89. 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)