cotutor 0.2.0 → 0.4.0
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/LICENSE +249 -21
- package/NOTICE +42 -0
- package/README.md +115 -20
- package/avatars/chinese.webp +0 -0
- package/avatars/english.webp +0 -0
- package/avatars/math.webp +0 -0
- package/bin/cotutor.js +15 -5
- package/cards/canvas/card.css +8 -0
- package/cards/canvas/card.md +68 -0
- package/cards/choice/card.css +18 -0
- package/cards/choice/card.md +55 -0
- package/cards/code/card.css +5 -0
- package/cards/code/card.md +46 -0
- package/cards/dictation/card.css +10 -0
- package/cards/dictation/card.md +97 -0
- package/cards/image/card.css +10 -0
- package/cards/image/card.md +52 -0
- package/cards/lecture/card.css +9 -0
- package/cards/lecture/card.md +69 -0
- package/cards/paint/card.css +20 -0
- package/cards/paint/card.md +77 -0
- package/cards/photo/card.css +22 -0
- package/cards/photo/card.md +61 -0
- package/cards/read/card.css +6 -0
- package/cards/read/card.md +51 -0
- package/cards/text/card.css +3 -0
- package/cards/text/card.md +69 -0
- package/cards/tianzige/card.css +17 -0
- package/cards/tianzige/card.md +78 -0
- package/cards/tutor/card.css +79 -0
- package/cards/tutor/card.md +149 -0
- package/cards/word/card.css +24 -0
- package/cards/word/card.md +91 -0
- package/cards//346/235/277/344/271/246/346/200/216/344/271/210/345/206/231.md +159 -0
- package/dist/app/activity-eval-rules.d.ts +62 -0
- package/dist/app/activity-eval-rules.js +276 -0
- package/dist/app/activity-eval.d.ts +65 -0
- package/dist/app/activity-eval.js +159 -0
- package/dist/app/build.d.ts +11 -0
- package/dist/app/build.js +45 -0
- package/dist/app/cli/activities.d.ts +70 -0
- package/dist/app/cli/activities.js +196 -0
- package/dist/app/cli/cert.d.ts +12 -0
- package/dist/app/cli/cert.js +49 -0
- package/dist/app/cli/chalk/bake.d.ts +10 -0
- package/dist/app/cli/chalk/bake.js +58 -0
- package/dist/app/cli/chalk/browser.d.ts +16 -0
- package/dist/app/cli/chalk/browser.js +157 -0
- package/dist/app/cli/chalk/build.d.ts +14 -0
- package/dist/app/cli/chalk/build.js +100 -0
- package/dist/app/cli/chalk/dub.d.ts +51 -0
- package/dist/app/cli/chalk/dub.js +106 -0
- package/dist/app/cli/chalk/load-scene.d.ts +34 -0
- package/dist/app/cli/chalk/load-scene.js +117 -0
- package/dist/app/cli/chalk/main.d.ts +7 -0
- package/dist/app/cli/chalk/main.js +199 -0
- package/dist/app/cli/chalk/serve.d.ts +4 -0
- package/dist/app/cli/chalk/serve.js +28 -0
- package/dist/app/cli/chalk/snap.d.ts +65 -0
- package/dist/app/cli/chalk/snap.js +103 -0
- package/dist/app/cli/chalk/typecheck.d.ts +7 -0
- package/dist/app/cli/chalk/typecheck.js +51 -0
- package/dist/app/cli/doctor.d.ts +24 -0
- package/dist/app/cli/doctor.js +553 -0
- package/dist/app/cli/firewall.d.ts +24 -0
- package/dist/app/cli/firewall.js +79 -0
- package/dist/app/cli/grades.d.ts +14 -0
- package/dist/app/cli/grades.js +44 -0
- package/dist/{cli → app/cli}/init.d.ts +3 -1
- package/dist/app/cli/init.js +158 -0
- package/dist/app/cli/main.js +1038 -0
- package/dist/app/cli/migrate.d.ts +49 -0
- package/dist/app/cli/migrate.js +195 -0
- package/dist/app/cli/port-holder.d.ts +16 -0
- package/dist/app/cli/port-holder.js +34 -0
- package/dist/app/cli/port.d.ts +37 -0
- package/dist/app/cli/port.js +116 -0
- package/dist/app/cli/sandbox.d.ts +25 -0
- package/dist/app/cli/sandbox.js +166 -0
- package/dist/app/cli/serve-log.d.ts +13 -0
- package/dist/app/cli/serve-log.js +90 -0
- package/dist/app/cli/serve.d.ts +51 -0
- package/dist/app/cli/serve.js +168 -0
- package/dist/app/cli/service-windows.d.ts +50 -0
- package/dist/app/cli/service-windows.js +117 -0
- package/dist/app/cli/service.d.ts +138 -0
- package/dist/app/cli/service.js +429 -0
- package/dist/app/cli/shipped.d.ts +30 -0
- package/dist/app/cli/shipped.js +47 -0
- package/dist/app/cli/skeleton.d.ts +38 -0
- package/dist/app/cli/skeleton.js +161 -0
- package/dist/app/cli/skills.d.ts +59 -0
- package/dist/app/cli/skills.js +199 -0
- package/dist/app/cli/themes.d.ts +52 -0
- package/dist/app/cli/themes.js +148 -0
- package/dist/app/cli/tray-icons.d.ts +11 -0
- package/dist/app/cli/tray-icons.js +87 -0
- package/dist/app/cli/tray-ids.d.ts +6 -0
- package/dist/app/cli/tray-ids.js +6 -0
- package/dist/app/cli/tray-mac.d.ts +8 -0
- package/dist/app/cli/tray-mac.js +140 -0
- package/dist/app/cli/tray-run.d.ts +1 -0
- package/dist/app/cli/tray-run.js +33 -0
- package/dist/app/cli/tray-windows.d.ts +5 -0
- package/dist/app/cli/tray-windows.js +166 -0
- package/dist/app/cli/tray.d.ts +205 -0
- package/dist/app/cli/tray.js +618 -0
- package/dist/{cli → app/cli}/tutors.d.ts +15 -26
- package/dist/{cli → app/cli}/tutors.js +86 -110
- package/dist/app/cli/usage.d.ts +2 -0
- package/dist/app/cli/usage.js +35 -0
- package/dist/app/dictation/recognize.d.ts +18 -0
- package/dist/app/dictation/recognize.js +6 -0
- package/dist/app/dictation/route.d.ts +52 -0
- package/dist/app/dictation/route.js +511 -0
- package/dist/app/diff.d.ts +11 -0
- package/dist/app/diff.js +71 -0
- package/dist/app/http/access.d.ts +62 -0
- package/dist/app/http/access.js +180 -0
- package/dist/app/http/app.d.ts +241 -0
- package/dist/app/http/app.js +1159 -0
- package/dist/app/http/avatars.d.ts +17 -0
- package/dist/app/http/avatars.js +35 -0
- package/dist/app/http/dev-activities.d.ts +115 -0
- package/dist/app/http/dev-activities.js +193 -0
- package/dist/app/http/dev-overview.d.ts +108 -0
- package/dist/app/http/dev-overview.js +209 -0
- package/dist/app/http/dev-tutor-files.d.ts +80 -0
- package/dist/app/http/dev-tutor-files.js +151 -0
- package/dist/app/http/parent-home.d.ts +51 -0
- package/dist/app/http/parent-home.js +266 -0
- package/dist/app/http/parent-look.d.ts +2 -0
- package/dist/app/http/parent-look.js +151 -0
- package/dist/app/http/range.d.ts +9 -0
- package/dist/app/http/range.js +19 -0
- package/dist/app/http/raw-view.d.ts +117 -0
- package/dist/app/http/raw-view.js +149 -0
- package/dist/app/http/send-file.d.ts +7 -0
- package/dist/app/http/send-file.js +29 -0
- package/dist/{server → app/http}/stage.d.ts +9 -0
- package/dist/{server → app/http}/stage.js +34 -6
- package/dist/app/http/theme.d.ts +13 -0
- package/dist/app/http/theme.js +41 -0
- package/dist/app/http/voice-preview.d.ts +4 -0
- package/dist/app/http/voice-preview.js +34 -0
- package/dist/app/machine/bailian.d.ts +26 -0
- package/dist/app/machine/bailian.js +87 -0
- package/dist/app/mock/mock-live.d.ts +16 -0
- package/dist/app/mock/mock-live.js +140 -0
- package/dist/{server → app/mock}/mock.d.ts +38 -5
- package/dist/app/mock/mock.js +1125 -0
- package/dist/app/replay.d.ts +94 -0
- package/dist/app/replay.js +170 -0
- package/dist/app/skills/analyze-doc.d.ts +3 -0
- package/dist/app/skills/analyze-doc.js +47 -0
- package/dist/app/skills/home-doc.d.ts +5 -0
- package/dist/app/skills/home-doc.js +112 -0
- package/dist/app/skills/tune-doc.d.ts +3 -0
- package/dist/app/skills/tune-doc.js +80 -0
- package/dist/app/talk/list.d.ts +11 -0
- package/dist/app/talk/list.js +9 -0
- package/dist/app/talk/route.d.ts +16 -0
- package/dist/app/talk/route.js +142 -0
- package/dist/app/workspace.d.ts +63 -0
- package/dist/{cli → app}/workspace.js +100 -52
- package/dist/app/ws/route.d.ts +31 -0
- package/dist/app/ws/route.js +33 -0
- package/dist/app/ws/ws.d.ts +31 -0
- package/dist/app/ws/ws.js +169 -0
- package/dist/board/canvas-chat.d.ts +97 -0
- package/dist/board/canvas-chat.js +175 -0
- package/dist/board/card-guards.d.ts +15 -0
- package/dist/board/card-guards.js +38 -0
- package/dist/board/cards/canvas.d.ts +41 -0
- package/dist/board/cards/canvas.js +83 -0
- package/dist/{cards → board/cards}/choice.js +0 -12
- package/dist/{cards → board/cards}/code.js +0 -3
- package/dist/board/cards/dictation.d.ts +42 -0
- package/dist/board/cards/dictation.js +105 -0
- package/dist/board/cards/docs.d.ts +47 -0
- package/dist/board/cards/docs.js +152 -0
- package/dist/{cards → board/cards}/image.js +1 -10
- package/dist/board/cards/index.d.ts +62 -0
- package/dist/board/cards/index.js +144 -0
- package/dist/{cards → board/cards}/kind.d.ts +10 -4
- package/dist/board/cards/kind.js +9 -0
- package/dist/board/cards/lecture.d.ts +26 -0
- package/dist/board/cards/lecture.js +62 -0
- package/dist/board/cards/paint.d.ts +38 -0
- package/dist/board/cards/paint.js +85 -0
- package/dist/board/cards/photo.d.ts +15 -0
- package/dist/board/cards/photo.js +29 -0
- package/dist/{cards → board/cards}/read.js +1 -10
- package/dist/board/cards/text.d.ts +20 -0
- package/dist/board/cards/text.js +59 -0
- package/dist/board/cards/tianzige.d.ts +16 -0
- package/dist/board/cards/tianzige.js +64 -0
- package/dist/board/cards/tutor.d.ts +72 -0
- package/dist/board/cards/tutor.js +134 -0
- package/dist/board/cards/word.d.ts +20 -0
- package/dist/board/cards/word.js +76 -0
- package/dist/board/lecture.d.ts +100 -0
- package/dist/board/lecture.js +204 -0
- package/dist/board/paper.d.ts +20 -0
- package/dist/board/paper.js +76 -0
- package/dist/board/parse.d.ts +118 -0
- package/dist/board/parse.js +324 -0
- package/dist/board/presentation.d.ts +289 -0
- package/dist/board/presentation.js +613 -0
- package/dist/board/sections.d.ts +16 -0
- package/dist/board/sections.js +130 -0
- package/dist/brain/api-tools.d.ts +8 -0
- package/dist/brain/api-tools.js +191 -0
- package/dist/brain/api.d.ts +57 -0
- package/dist/brain/api.js +394 -0
- package/dist/brain/index.d.ts +97 -0
- package/dist/brain/index.js +11 -0
- package/dist/brain/log.d.ts +5 -0
- package/dist/brain/log.js +22 -0
- package/dist/brain/runtime.d.ts +30 -0
- package/dist/brain/runtime.js +23 -0
- package/dist/brain/stream-json.d.ts +16 -0
- package/dist/brain/stream-json.js +82 -0
- package/dist/brain/stream.d.ts +20 -0
- package/dist/brain/stream.js +70 -0
- package/dist/brain/transcript.d.ts +45 -0
- package/dist/brain/transcript.js +132 -0
- package/dist/brain/types.d.ts +99 -0
- package/dist/brain/types.js +14 -0
- package/dist/chalk/actions.d.ts +37 -0
- package/dist/chalk/actions.js +69 -0
- package/dist/chalk/bundle.d.ts +26 -0
- package/dist/chalk/bundle.js +69 -0
- package/dist/chalk/check.d.ts +28 -0
- package/dist/chalk/check.js +291 -0
- package/dist/chalk/circle.d.ts +64 -0
- package/dist/chalk/circle.js +193 -0
- package/dist/chalk/clock.d.ts +84 -0
- package/dist/chalk/clock.js +84 -0
- package/dist/chalk/geometry.d.ts +44 -0
- package/dist/chalk/geometry.js +139 -0
- package/dist/chalk/glyphs/capitals.d.ts +12 -0
- package/dist/chalk/glyphs/capitals.js +264 -0
- package/dist/chalk/glyphs/index.d.ts +21 -0
- package/dist/chalk/glyphs/index.js +18 -0
- package/dist/chalk/glyphs/letters.d.ts +13 -0
- package/dist/chalk/glyphs/letters.js +280 -0
- package/dist/chalk/glyphs/metrics.d.ts +33 -0
- package/dist/chalk/glyphs/metrics.js +28 -0
- package/dist/chalk/glyphs/registry.d.ts +7 -0
- package/dist/chalk/glyphs/registry.js +8 -0
- package/dist/chalk/glyphs/sample.d.ts +32 -0
- package/dist/chalk/glyphs/sample.js +102 -0
- package/dist/chalk/glyphs/skeleton.d.ts +64 -0
- package/dist/chalk/glyphs/skeleton.js +126 -0
- package/dist/chalk/glyphs/types.d.ts +58 -0
- package/dist/chalk/glyphs/types.js +17 -0
- package/dist/chalk/index.d.ts +23 -0
- package/dist/chalk/index.js +15 -0
- package/dist/chalk/manifest.d.ts +41 -0
- package/dist/chalk/manifest.js +6 -0
- package/dist/chalk/paint.d.ts +180 -0
- package/dist/chalk/paint.js +210 -0
- package/dist/chalk/pointer.d.ts +8 -0
- package/dist/chalk/pointer.js +21 -0
- package/dist/chalk/prepare.d.ts +11 -0
- package/dist/chalk/prepare.js +15 -0
- package/dist/chalk/scene-schema.d.ts +199 -0
- package/dist/chalk/scene-schema.js +91 -0
- package/dist/chalk/seed.d.ts +3 -0
- package/dist/chalk/seed.js +10 -0
- package/dist/chalk/text-metrics.d.ts +8 -0
- package/dist/chalk/text-metrics.js +156 -0
- package/dist/chalk/timeline.d.ts +47 -0
- package/dist/chalk/timeline.js +84 -0
- package/dist/chalk/types.d.ts +150 -0
- package/dist/chalk/types.js +8 -0
- package/dist/lib/index.d.ts +14 -12
- package/dist/lib/index.js +14 -12
- package/dist/media/draw.d.ts +23 -0
- package/dist/media/draw.js +44 -0
- package/dist/media/mp3.d.ts +6 -0
- package/dist/media/mp3.js +58 -0
- package/dist/media/mp4.d.ts +1 -0
- package/dist/media/mp4.js +58 -0
- package/dist/media/tts-voices.d.ts +18 -0
- package/dist/media/tts-voices.js +606 -0
- package/dist/media/tts.d.ts +56 -0
- package/dist/media/tts.js +174 -0
- package/dist/media/vision.d.ts +25 -0
- package/dist/media/vision.js +71 -0
- package/dist/memory/commit.d.ts +67 -0
- package/dist/memory/commit.js +130 -0
- package/dist/memory/context.d.ts +86 -0
- package/dist/memory/context.js +97 -0
- package/dist/memory/diary.d.ts +84 -0
- package/dist/memory/diary.js +183 -0
- package/dist/memory/grade.d.ts +2 -0
- package/dist/memory/grade.js +7 -0
- package/dist/{lib → memory}/plan.d.ts +1 -1
- package/dist/{lib → memory}/plan.js +1 -1
- package/dist/memory/proposals.d.ts +11 -0
- package/dist/memory/proposals.js +43 -0
- package/dist/memory/retrieval.d.ts +44 -0
- package/dist/memory/retrieval.js +157 -0
- package/dist/{lib → memory}/timetable.d.ts +0 -3
- package/dist/{lib → memory}/timetable.js +0 -11
- package/dist/memory/vault-notes.d.ts +165 -0
- package/dist/memory/vault-notes.js +359 -0
- package/dist/memory/vault.d.ts +70 -0
- package/dist/memory/vault.js +169 -0
- package/dist/realtime/bailian.d.ts +120 -0
- package/dist/realtime/bailian.js +289 -0
- package/dist/realtime/cues.d.ts +51 -0
- package/dist/realtime/cues.js +94 -0
- package/dist/realtime/listen.d.ts +32 -0
- package/dist/realtime/listen.js +53 -0
- package/dist/realtime/preview.d.ts +23 -0
- package/dist/realtime/preview.js +38 -0
- package/dist/realtime/prompt.d.ts +8 -0
- package/dist/realtime/prompt.js +13 -0
- package/dist/realtime/voices.d.ts +21 -0
- package/dist/realtime/voices.js +71 -0
- package/dist/runtime/activities.d.ts +29 -0
- package/dist/runtime/activities.js +72 -0
- package/dist/runtime/activity-state.d.ts +22 -0
- package/dist/runtime/activity-state.js +83 -0
- package/dist/runtime/activity.d.ts +63 -0
- package/dist/runtime/activity.js +428 -0
- package/dist/runtime/agent-file.d.ts +24 -0
- package/dist/runtime/agent-file.js +59 -0
- package/dist/runtime/auto-bookkeep.d.ts +30 -0
- package/dist/runtime/auto-bookkeep.js +84 -0
- package/dist/{lib → runtime/context}/context-pack.d.ts +3 -3
- package/dist/runtime/context/context-pack.js +182 -0
- package/dist/runtime/context/thread-digest.d.ts +4 -0
- package/dist/runtime/context/thread-digest.js +35 -0
- package/dist/runtime/context/tutor-rules.d.ts +25 -0
- package/dist/runtime/context/tutor-rules.js +47 -0
- package/dist/runtime/delivery/delivery.d.ts +94 -0
- package/dist/runtime/delivery/delivery.js +145 -0
- package/dist/runtime/delivery/playback.d.ts +25 -0
- package/dist/runtime/delivery/playback.js +29 -0
- package/dist/runtime/delivery/player.d.ts +145 -0
- package/dist/runtime/delivery/player.js +221 -0
- package/dist/runtime/delivery/queue.d.ts +89 -0
- package/dist/runtime/delivery/queue.js +59 -0
- package/dist/runtime/home/edit.d.ts +39 -0
- package/dist/runtime/home/edit.js +132 -0
- package/dist/runtime/home/home.d.ts +191 -0
- package/dist/runtime/home/home.js +318 -0
- package/dist/runtime/home/store.d.ts +142 -0
- package/dist/runtime/home/store.js +455 -0
- package/dist/runtime/journal/conversation.d.ts +46 -0
- package/dist/runtime/journal/conversation.js +89 -0
- package/dist/runtime/journal/events.d.ts +27 -0
- package/dist/runtime/journal/events.js +136 -0
- package/dist/runtime/journal/kid-log.d.ts +105 -0
- package/dist/runtime/journal/kid-log.js +133 -0
- package/dist/runtime/journal/ledger.d.ts +17 -0
- package/dist/{lib → runtime/journal}/ledger.js +7 -40
- package/dist/runtime/journal/mix.d.ts +42 -0
- package/dist/runtime/journal/mix.js +129 -0
- package/dist/runtime/journal/mouth-index.d.ts +26 -0
- package/dist/runtime/journal/mouth-index.js +34 -0
- package/dist/runtime/journal/mouth.d.ts +54 -0
- package/dist/runtime/journal/mouth.js +102 -0
- package/dist/runtime/journal/recorder.d.ts +91 -0
- package/dist/runtime/journal/recorder.js +148 -0
- package/dist/runtime/journal/store.d.ts +123 -0
- package/dist/runtime/journal/store.js +316 -0
- package/dist/runtime/kid-input.d.ts +16 -0
- package/dist/runtime/kid-input.js +194 -0
- package/dist/runtime/kid-view.d.ts +124 -0
- package/dist/runtime/kid-view.js +157 -0
- package/dist/runtime/lecture.d.ts +86 -0
- package/dist/runtime/lecture.js +237 -0
- package/dist/runtime/live/call.d.ts +231 -0
- package/dist/runtime/live/call.js +1244 -0
- package/dist/runtime/live/turns.d.ts +48 -0
- package/dist/runtime/live/turns.js +102 -0
- package/dist/runtime/runner.d.ts +243 -0
- package/dist/runtime/runner.js +777 -0
- package/dist/runtime/talk-run.d.ts +21 -0
- package/dist/runtime/talk-run.js +34 -0
- package/dist/scenes/dictation/match.d.ts +57 -0
- package/dist/scenes/dictation/match.js +255 -0
- package/dist/scenes/dictation/recognize.d.ts +86 -0
- package/dist/scenes/dictation/recognize.js +163 -0
- package/dist/scenes/dictation/store.d.ts +112 -0
- package/dist/scenes/dictation/store.js +99 -0
- package/dist/scenes/look.d.ts +26 -0
- package/dist/scenes/look.js +1 -0
- package/dist/scenes/paint/painter.d.ts +84 -0
- package/dist/scenes/paint/painter.js +171 -0
- package/dist/scenes/paint/prompt.d.ts +23 -0
- package/dist/scenes/paint/prompt.js +36 -0
- package/dist/scenes/talk/en-words.d.ts +8 -0
- package/dist/scenes/talk/en-words.js +44 -0
- package/dist/scenes/talk/list.d.ts +44 -0
- package/dist/scenes/talk/list.js +88 -0
- package/dist/scenes/talk/store.d.ts +27 -0
- package/dist/scenes/talk/store.js +61 -0
- package/dist/scenes/talk/warmup.d.ts +16 -0
- package/dist/scenes/talk/warmup.js +39 -0
- package/dist/schema/activity.d.ts +149 -0
- package/dist/schema/activity.js +113 -0
- package/dist/schema/board.d.ts +143 -2
- package/dist/schema/board.js +15 -3
- package/dist/schema/config.d.ts +339 -103
- package/dist/schema/config.js +269 -84
- package/dist/schema/context-pack.d.ts +76 -14
- package/dist/schema/context-pack.js +91 -10
- package/dist/{lib/conversation.d.ts → schema/conversation-files.d.ts} +11 -33
- package/dist/schema/conversation-files.js +26 -0
- package/dist/schema/conversation.d.ts +391 -49
- package/dist/schema/conversation.js +135 -19
- package/dist/schema/events.d.ts +138 -0
- package/dist/schema/events.js +1 -0
- package/dist/schema/home.d.ts +56 -0
- package/dist/schema/home.js +35 -0
- package/dist/schema/index.d.ts +14 -3
- package/dist/schema/index.js +14 -3
- package/dist/schema/kid-device.d.ts +20 -0
- package/dist/schema/kid-device.js +32 -0
- package/dist/schema/ledger.d.ts +8 -63
- package/dist/schema/ledger.js +7 -31
- package/dist/schema/memory.d.ts +73 -0
- package/dist/schema/memory.js +47 -0
- package/dist/schema/plan.d.ts +2 -2
- package/dist/schema/plan.js +2 -2
- package/dist/schema/sections.d.ts +32 -25
- package/dist/schema/sections.js +21 -22
- package/dist/schema/theme.d.ts +25 -0
- package/dist/schema/theme.js +26 -0
- package/dist/schema/timetable.d.ts +2 -2
- package/dist/schema/timetable.js +2 -2
- package/dist/schema/workspace.d.ts +68 -0
- package/dist/schema/workspace.js +37 -0
- package/dist/shared/default-port.d.ts +2 -0
- package/dist/shared/default-port.js +2 -0
- package/dist/shared/errors.d.ts +8 -0
- package/dist/shared/errors.js +13 -0
- package/dist/shared/home.d.ts +2 -0
- package/dist/shared/home.js +16 -0
- package/dist/shared/human-notes.d.ts +5 -0
- package/dist/shared/human-notes.js +7 -0
- package/dist/shared/qianwen.d.ts +54 -0
- package/dist/shared/qianwen.js +113 -0
- package/dist/shared/site-url.d.ts +2 -0
- package/dist/shared/site-url.js +5 -0
- package/dist/shared/time.d.ts +9 -0
- package/dist/shared/time.js +22 -0
- package/dist/shared/tokens.d.ts +18 -0
- package/dist/shared/tokens.js +11 -0
- package/dist/shared/wav.d.ts +10 -0
- package/dist/shared/wav.js +50 -0
- package/dist/stage/chunk-226TNLQA.js +129 -0
- package/dist/stage/chunk-24LGQEKE.js +1 -0
- package/dist/stage/chunk-25XV6UY3.js +10 -0
- package/dist/stage/chunk-2AZI3NHJ.js +1 -0
- package/dist/stage/chunk-2BQZTTBE.js +1 -0
- package/dist/stage/chunk-2M7CGY3Y.js +11 -0
- package/dist/stage/chunk-2VR4WFQ7.js +10 -0
- package/dist/stage/chunk-2ZYXBUWG.js +9 -0
- package/dist/stage/chunk-32LAWMGY.js +1 -0
- package/dist/stage/chunk-3LEMTSOO.js +11 -0
- package/dist/stage/chunk-3QCKGZYE.js +32 -0
- package/dist/stage/chunk-44P66WFA.js +1 -0
- package/dist/stage/chunk-4AM6FRR6.js +7 -0
- package/dist/stage/chunk-4QDE4SUK.js +1 -0
- package/dist/stage/chunk-4QESHV3U.js +1 -0
- package/dist/stage/chunk-4SG5QPHB.js +2 -0
- package/dist/stage/chunk-4T4UVCOC.js +1 -0
- package/dist/stage/chunk-4WUEPPG3.js +34 -0
- package/dist/stage/chunk-4YCN6UZ5.js +99 -0
- package/dist/stage/chunk-5V7ZY2AP.js +1 -0
- package/dist/stage/chunk-5VS5Y2VJ.js +6 -0
- package/dist/stage/chunk-5W4NO2ZD.js +62 -0
- package/dist/stage/chunk-65CRNICM.js +1 -0
- package/dist/stage/chunk-66MULO7F.js +5 -0
- package/dist/stage/chunk-6COFQTBS.js +162 -0
- package/dist/stage/chunk-6VZG3M3I.js +8 -0
- package/dist/stage/chunk-7HVDH43T.js +1 -0
- package/dist/stage/chunk-7JLJN4EZ.js +8 -0
- package/dist/stage/chunk-7QDSP4LN.js +1 -0
- package/dist/stage/chunk-7R6OQAO3.js +1 -0
- package/dist/stage/chunk-7VTTHZKW.js +7 -0
- package/dist/stage/chunk-ATHR5KEN.js +10 -0
- package/dist/stage/chunk-B6R5LTP2.js +36 -0
- package/dist/stage/chunk-BD4WE264.js +1 -0
- package/dist/stage/chunk-BONANU47.js +206 -0
- package/dist/stage/chunk-BTVW7OFK.js +89 -0
- package/dist/stage/chunk-BUTJIDHG.js +8 -0
- package/dist/stage/chunk-C2IPYMML.js +84 -0
- package/dist/stage/chunk-C54FTPPV.js +9 -0
- package/dist/stage/chunk-C6LOUF2Z.js +90 -0
- package/dist/stage/chunk-CFF3PILO.js +339 -0
- package/dist/stage/chunk-CI2FQ2JY.js +9 -0
- package/dist/stage/chunk-CW5O4RV7.js +12 -0
- package/dist/stage/chunk-CYABNQYD.js +1 -0
- package/dist/stage/chunk-D7FFA4NS.js +15 -0
- package/dist/stage/chunk-DUNNXU6Z.js +8 -0
- package/dist/stage/chunk-E3E4Q6JF.js +6 -0
- package/dist/stage/chunk-EB3U3LH7.js +9 -0
- package/dist/stage/chunk-EGSZ736B.js +8 -0
- package/dist/stage/chunk-EOBXW5VQ.js +2 -0
- package/dist/stage/chunk-ERZC3B7Y.js +11 -0
- package/dist/stage/chunk-ESDOOBIH.js +11 -0
- package/dist/stage/chunk-EVU4QAL4.js +178 -0
- package/dist/stage/chunk-EVVZI2NY.js +39 -0
- package/dist/stage/chunk-FBKAZUKD.js +39 -0
- package/dist/stage/chunk-FDVKYQJN.js +1 -0
- package/dist/stage/chunk-FGPVYHVR.js +5 -0
- package/dist/stage/chunk-FHUD4AZC.js +1 -0
- package/dist/stage/chunk-FKUJ2DMY.js +96 -0
- package/dist/stage/chunk-FNZGVIJV.js +1 -0
- package/dist/stage/chunk-GBYSEGYM.js +78 -0
- package/dist/stage/chunk-GCO4TDHX.js +1 -0
- package/dist/stage/chunk-GKTML2LG.js +10 -0
- package/dist/stage/chunk-GMDIHHXQ.js +11 -0
- package/dist/stage/chunk-GOUNACI3.js +10 -0
- package/dist/stage/chunk-GXAYATPF.js +3 -0
- package/dist/stage/chunk-H2K2DDXV.js +8 -0
- package/dist/stage/chunk-H3UJI6ZY.js +13 -0
- package/dist/stage/chunk-H6PUGCME.js +7 -0
- package/dist/stage/chunk-HNBUIMIG.js +1 -0
- package/dist/stage/chunk-HRZY2BJQ.js +10 -0
- package/dist/stage/chunk-HSPJGALW.js +30 -0
- package/dist/stage/chunk-IBYORHIP.js +25 -0
- package/dist/stage/chunk-IHF4EWTY.js +1 -0
- package/dist/stage/chunk-ISBRPNKI.js +1 -0
- package/dist/stage/chunk-IZ3OI3C2.js +2 -0
- package/dist/stage/chunk-JEFZAC2R.js +1 -0
- package/dist/stage/chunk-JFIGRDPO.js +1 -0
- package/dist/stage/chunk-JFVYBOOC.js +9 -0
- package/dist/stage/chunk-JGD4TUTQ.js +1 -0
- package/dist/stage/chunk-JO4BEHDH.js +4 -0
- package/dist/stage/chunk-JOWBS42C.js +1 -0
- package/dist/stage/chunk-JP3TM2TG.js +1 -0
- package/dist/stage/chunk-JVK2C52O.js +1 -0
- package/dist/stage/chunk-K4I76XBX.js +14 -0
- package/dist/stage/chunk-KA6CZZ4M.js +1 -0
- package/dist/stage/chunk-KESSJ36M.js +1 -0
- package/dist/stage/chunk-LKATEVRU.js +1 -0
- package/dist/stage/chunk-LS4EJ2EI.js +1 -0
- package/dist/stage/chunk-LUKJQC2X.js +70 -0
- package/dist/stage/chunk-LWULR3SM.js +1 -0
- package/dist/stage/chunk-LWVRFMKX.js +2 -0
- package/dist/stage/chunk-LXC4ZBPZ.js +1 -0
- package/dist/stage/chunk-M2UP255H.js +1 -0
- package/dist/stage/chunk-MAPPXUZQ.js +231 -0
- package/dist/stage/chunk-MBMPW7Z5.js +1 -0
- package/dist/stage/chunk-MD3HMMTK.js +1 -0
- package/dist/stage/chunk-MLPCUIKR.js +17 -0
- package/dist/stage/chunk-MNQIRPDQ.js +8 -0
- package/dist/stage/chunk-MRG7MJVS.js +1 -0
- package/dist/stage/chunk-MU6EXW72.js +10 -0
- package/dist/stage/chunk-MVU2X6LU.js +4 -0
- package/dist/stage/chunk-N6UA5CZV.js +36 -0
- package/dist/stage/chunk-NLZIT2TJ.js +7 -0
- package/dist/stage/chunk-NOJR3J7M.js +1 -0
- package/dist/stage/chunk-NT7ARDOL.js +1 -0
- package/dist/stage/chunk-NWOGMPZH.js +1 -0
- package/dist/stage/chunk-NXE4KMG3.js +40 -0
- package/dist/stage/chunk-O4YCZXD2.js +257 -0
- package/dist/stage/chunk-OAJZ7HLK.js +1 -0
- package/dist/stage/chunk-OCFCFASO.js +1 -0
- package/dist/stage/chunk-ODGPG5RY.js +7 -0
- package/dist/stage/chunk-ODUEA52Q.js +3 -0
- package/dist/stage/chunk-OGDJ3FCI.js +1 -0
- package/dist/stage/chunk-OLDJ55TT.js +1 -0
- package/dist/stage/chunk-OOCL4ATI.js +2 -0
- package/dist/stage/chunk-OQEFDUDV.js +1 -0
- package/dist/stage/chunk-OQWVEWG7.js +4 -0
- package/dist/stage/chunk-OUIX6BGK.js +1 -0
- package/dist/stage/chunk-OV7OZYUO.js +7 -0
- package/dist/stage/chunk-PFGDY33O.js +1 -0
- package/dist/stage/chunk-PFJDUJB3.js +139 -0
- package/dist/stage/chunk-PGEW76ER.js +1 -0
- package/dist/stage/chunk-PP76TAQX.js +32 -0
- package/dist/stage/chunk-PTBOP4OT.js +2 -0
- package/dist/stage/chunk-PX2RVHCK.js +1 -0
- package/dist/stage/chunk-Q7K3PNAD.js +1 -0
- package/dist/stage/chunk-QDDPLZDB.js +1 -0
- package/dist/stage/chunk-QLGLZ77F.js +1 -0
- package/dist/stage/chunk-QOPFDFHI.js +1 -0
- package/dist/stage/chunk-QXT4KAEJ.js +1 -0
- package/dist/stage/chunk-R4YVZCIT.js +1 -0
- package/dist/stage/chunk-R6P6EHWA.js +62 -0
- package/dist/stage/chunk-RCH3OXBI.js +9 -0
- package/dist/stage/chunk-RHPE44I2.js +1 -0
- package/dist/stage/chunk-RJ2R4FVC.js +1 -0
- package/dist/stage/chunk-RJH2YVTD.js +1 -0
- package/dist/stage/chunk-RLWWW5OB.js +6 -0
- package/dist/stage/chunk-S3WO35Z7.js +120 -0
- package/dist/stage/chunk-S7ZGX6MW.js +1 -0
- package/dist/stage/chunk-SIAWX27B.js +1 -0
- package/dist/stage/chunk-SLG4X622.js +1 -0
- package/dist/stage/chunk-SLJRBC77.js +168 -0
- package/dist/stage/chunk-SRORWQJZ.js +1 -0
- package/dist/stage/chunk-STZTUIIF.js +2 -0
- package/dist/stage/chunk-SVQ6MWNV.js +9 -0
- package/dist/stage/chunk-TL3HLL2S.js +132 -0
- package/dist/stage/chunk-U3DOE6SF.js +24 -0
- package/dist/stage/chunk-UABQIZKW.js +6 -0
- package/dist/stage/chunk-UDK3R764.js +1 -0
- package/dist/stage/chunk-UFF4E54B.js +41 -0
- package/dist/stage/chunk-UFP5Q4AY.js +1 -0
- package/dist/stage/chunk-UMH2TSW2.js +72 -0
- package/dist/stage/chunk-UTASVNF5.js +10 -0
- package/dist/stage/chunk-UW7ML5ML.js +88 -0
- package/dist/stage/chunk-V3J7F6OP.js +1 -0
- package/dist/stage/chunk-VEHP5B5Q.js +5 -0
- package/dist/stage/chunk-VRXWB733.js +9 -0
- package/dist/stage/chunk-VVDHBBS5.js +5 -0
- package/dist/stage/chunk-VYFOH6FU.js +106 -0
- package/dist/stage/chunk-W3VZTLQS.js +3 -0
- package/dist/stage/chunk-W7WNRWTZ.js +8 -0
- package/dist/stage/chunk-WHUCV53C.js +1 -0
- package/dist/stage/chunk-WULMYHIF.js +1 -0
- package/dist/stage/chunk-X54EX7AW.js +9 -0
- package/dist/stage/chunk-X565ISZM.js +1 -0
- package/dist/stage/chunk-XGZMDECB.js +1 -0
- package/dist/stage/chunk-XHONC4RF.js +172 -0
- package/dist/stage/chunk-XIK4XQ6E.js +24 -0
- package/dist/stage/chunk-XJQQWFTE.js +1 -0
- package/dist/stage/chunk-XM4WOU3I.js +13 -0
- package/dist/stage/chunk-XM7Y5HP5.js +38 -0
- package/dist/stage/chunk-XNYYRGD5.js +292 -0
- package/dist/stage/chunk-XOYVR3SV.js +1 -0
- package/dist/stage/chunk-XQIW5HCE.js +1 -0
- package/dist/stage/chunk-YEWL7FUW.js +8 -0
- package/dist/stage/chunk-YI55PY3V.js +1 -0
- package/dist/stage/chunk-YM63HI6W.js +1 -0
- package/dist/stage/chunk-YNLGLU4P.js +9 -0
- package/dist/stage/chunk-YTNMNOWP.js +1 -0
- package/dist/stage/chunk-YV62XKTC.js +1 -0
- package/dist/stage/chunk-YVBA3QW6.js +1 -0
- package/dist/stage/chunk-Z2SY2HOC.js +1 -0
- package/dist/stage/chunk-ZBTCQEBK.js +10 -0
- package/dist/stage/chunk-ZCUFN74D.js +1 -0
- package/dist/stage/chunk-ZHOULXEJ.js +11 -0
- package/dist/stage/chunk-ZTRZIET2.js +1 -0
- package/dist/stage/index.html +1 -0
- package/dist/stage/stage.css +1 -1
- package/dist/stage/stage.js +1 -4383
- package/dist/ui/dev/dev-page.d.ts +1 -0
- package/dist/ui/dev/dev-page.js +2021 -0
- package/dist/ui/dev/dev-theme.d.ts +6 -0
- package/dist/ui/dev/dev-theme.js +67 -0
- package/dist/ui/dev/voice-gallery-page.d.ts +1 -0
- package/dist/ui/dev/voice-gallery-page.js +121 -0
- package/dist/ui/dev/voice-scenes.d.ts +21 -0
- package/dist/ui/dev/voice-scenes.js +30 -0
- package/dist/ui/dictation/en-box.d.ts +2 -0
- package/dist/ui/dictation/en-box.js +86 -0
- package/dist/ui/dictation/en-page.d.ts +1 -0
- package/dist/ui/dictation/en-page.js +484 -0
- package/dist/ui/dictation/page.d.ts +1 -0
- package/dist/ui/dictation/page.js +1084 -0
- package/dist/ui/dictation/parent-page.d.ts +1 -0
- package/dist/ui/dictation/parent-page.js +234 -0
- package/dist/ui/dictation/reel-mode.d.ts +1 -0
- package/dist/ui/dictation/reel-mode.js +87 -0
- package/dist/ui/dictation/reel-page.d.ts +1 -0
- package/dist/ui/dictation/reel-page.js +62 -0
- package/dist/ui/dictation/reel.d.ts +163 -0
- package/dist/ui/dictation/reel.js +436 -0
- package/dist/ui/dictation/ui.d.ts +44 -0
- package/dist/ui/dictation/ui.js +80 -0
- package/dist/ui/dictation/unlock.d.ts +7 -0
- package/dist/ui/dictation/unlock.js +16 -0
- package/dist/ui/kid/audio-client.d.ts +11 -0
- package/dist/ui/kid/audio-client.js +74 -0
- package/dist/ui/kid/board-mark-art.d.ts +37 -0
- package/dist/ui/kid/board-mark-art.js +5 -0
- package/dist/ui/kid/board-mark.d.ts +24 -0
- package/dist/ui/kid/board-mark.js +58 -0
- package/dist/ui/kid/home-tiles.d.ts +14 -0
- package/dist/ui/kid/home-tiles.js +44 -0
- package/dist/ui/kid/kid-log.d.ts +15 -0
- package/dist/ui/kid/kid-log.js +87 -0
- package/dist/ui/kid/kid-nav.d.ts +11 -0
- package/dist/ui/kid/kid-nav.js +22 -0
- package/dist/ui/kid/kid-page.d.ts +9 -0
- package/dist/ui/kid/kid-page.js +3684 -0
- package/dist/ui/kid/letters.d.ts +26 -0
- package/dist/ui/kid/letters.js +51 -0
- package/dist/ui/kid/photo-edit.d.ts +83 -0
- package/dist/ui/kid/photo-edit.js +106 -0
- package/dist/ui/kid/photo-ui.d.ts +23 -0
- package/dist/ui/kid/photo-ui.js +60 -0
- package/dist/ui/kid/reel.d.ts +419 -0
- package/dist/ui/kid/reel.js +648 -0
- package/dist/ui/kid/tianzige.d.ts +8 -0
- package/dist/ui/kid/tianzige.js +45 -0
- package/dist/ui/kid/tutor-face.d.ts +35 -0
- package/dist/ui/kid/tutor-face.js +84 -0
- package/dist/ui/kid/voice-ui.d.ts +70 -0
- package/dist/ui/kid/voice-ui.js +316 -0
- package/dist/ui/kid/voice-view.d.ts +71 -0
- package/dist/ui/kid/voice-view.js +75 -0
- package/dist/ui/kid/word-draw.d.ts +2 -0
- package/dist/ui/kid/word-draw.js +107 -0
- package/dist/ui/kid/word-emoji.d.ts +10 -0
- package/dist/ui/kid/word-emoji.js +68 -0
- package/dist/ui/kid/word-layout.d.ts +25 -0
- package/dist/ui/kid/word-layout.js +29 -0
- package/dist/ui/parent/parent-home-page.d.ts +1 -0
- package/dist/ui/parent/parent-home-page.js +722 -0
- package/dist/ui/parent/parent-page.d.ts +2 -0
- package/dist/ui/parent/parent-page.js +511 -0
- package/dist/ui/parent/parent-settings-page.d.ts +1 -0
- package/dist/ui/parent/parent-settings-page.js +133 -0
- package/dist/ui/parent/qr-page.d.ts +8 -0
- package/dist/ui/parent/qr-page.js +66 -0
- package/dist/ui/shared/brand.d.ts +38 -0
- package/dist/ui/shared/brand.js +53 -0
- package/dist/ui/shared/favicon.d.ts +12 -0
- package/dist/ui/shared/favicon.js +96 -0
- package/dist/ui/shared/icon.d.ts +9 -0
- package/dist/ui/shared/icon.js +35 -0
- package/dist/ui/shared/inline-lib.d.ts +1 -0
- package/dist/ui/shared/inline-lib.js +28 -0
- package/dist/ui/shared/logo.d.ts +31 -0
- package/dist/ui/shared/logo.js +43 -0
- package/dist/ui/shared/page-dom.d.ts +12 -0
- package/dist/ui/shared/page-dom.js +36 -0
- package/dist/ui/shared/png.d.ts +1 -0
- package/dist/ui/shared/png.js +45 -0
- package/dist/ui/shared/qr-svg.d.ts +3 -0
- package/dist/ui/shared/qr-svg.js +32 -0
- package/dist/ui/shared/qr.d.ts +14 -0
- package/dist/ui/shared/qr.js +281 -0
- package/dist/ui/shared/reel-dock.d.ts +22 -0
- package/dist/ui/shared/reel-dock.js +365 -0
- package/dist/ui/shared/tutor-color.d.ts +15 -0
- package/dist/ui/shared/tutor-color.js +47 -0
- package/dist/ui/talk/page.d.ts +6 -0
- package/dist/ui/talk/page.js +294 -0
- package/grades/grade-2/README.md +11 -0
- package/grades/grade-2/VALIDATION.md +41 -0
- package/grades/grade-2/agents/chinese-tutor/activities//344/275/240/350/257/264/346/210/221/347/224/273.md +157 -0
- package/grades/grade-2/agents/chinese-tutor/activities//344/275/240/350/257/264/346/210/221/347/224/273./344/276/213/345/255/220.md +78 -0
- package/grades/grade-2/agents/chinese-tutor/activities//344/276/213/345/255/220/345/233/276//345/200/237/344/274/236.jpg +0 -0
- package/grades/grade-2/agents/chinese-tutor/activities//344/276/213/345/255/220/345/233/276//345/260/217/347/214/264/346/221/230/346/241/203.jpg +0 -0
- package/grades/grade-2/agents/chinese-tutor/activities//345/206/231/350/257/235.md +64 -0
- package/grades/grade-2/agents/chinese-tutor/activities//345/206/231/350/257/235./344/276/213/345/255/220.md +31 -0
- package/grades/grade-2/agents/chinese-tutor/activities//345/220/254/345/206/231.md +48 -0
- package/grades/grade-2/agents/chinese-tutor/activities//345/244/215/344/271/240.md +79 -0
- package/grades/grade-2/agents/chinese-tutor/activities//345/244/215/344/271/240./344/276/213/345/255/220.md +35 -0
- package/grades/grade-2/agents/chinese-tutor/activities//345/260/217/350/257/276/345/240/202.md +53 -0
- package/grades/grade-2/agents/chinese-tutor/activities//346/243/200/346/237/245/344/275/234/344/270/232.md +81 -0
- package/grades/grade-2/agents/chinese-tutor/activities//346/243/200/346/237/245/344/275/234/344/270/232./344/276/213/345/255/220.md +35 -0
- package/grades/grade-2/agents/chinese-tutor/activities//347/234/213/345/233/276/350/257/264/350/257/235.md +94 -0
- package/grades/grade-2/agents/chinese-tutor/activities//347/234/213/345/233/276/350/257/264/350/257/235./344/276/213/345/255/220.md +63 -0
- package/grades/grade-2/agents/chinese-tutor/activities//351/242/204/344/271/240.md +82 -0
- package/grades/grade-2/agents/chinese-tutor/activities//351/242/204/344/271/240./344/276/213/345/255/220.md +35 -0
- package/grades/grade-2/agents/chinese-tutor.md +59 -0
- package/grades/grade-2/agents/english-tutor/activities//345/217/243/350/257/255/350/257/276.md +149 -0
- package/grades/grade-2/agents/english-tutor/activities//345/217/243/350/257/255/350/257/276./344/276/213/345/255/220.md +13 -0
- package/grades/grade-2/agents/english-tutor/activities//345/220/254/345/206/231.md +47 -0
- package/grades/grade-2/agents/english-tutor/activities//345/244/215/344/271/240.md +78 -0
- package/grades/grade-2/agents/english-tutor/activities//345/244/215/344/271/240./344/276/213/345/255/220.md +35 -0
- package/grades/grade-2/agents/english-tutor/activities//345/260/217/350/257/276/345/240/202.md +53 -0
- package/grades/grade-2/agents/english-tutor/activities//346/243/200/346/237/245/344/275/234/344/270/232.md +80 -0
- package/grades/grade-2/agents/english-tutor/activities//346/243/200/346/237/245/344/275/234/344/270/232./344/276/213/345/255/220.md +35 -0
- package/grades/grade-2/agents/english-tutor/activities//351/242/204/344/271/240.md +86 -0
- package/grades/grade-2/agents/english-tutor/activities//351/242/204/344/271/240./344/276/213/345/255/220.md +35 -0
- package/grades/grade-2/agents/english-tutor.md +57 -0
- package/grades/grade-2/agents/math-tutor/activities//345/244/215/344/271/240.md +78 -0
- package/grades/grade-2/agents/math-tutor/activities//345/244/215/344/271/240./344/276/213/345/255/220.md +35 -0
- package/grades/grade-2/agents/math-tutor/activities//345/260/217/350/257/276/345/240/202.md +56 -0
- package/grades/grade-2/agents/math-tutor/activities//345/260/217/350/257/276/345/240/202./344/276/213/345/255/220.md +52 -0
- package/grades/grade-2/agents/math-tutor/activities//346/243/200/346/237/245/344/275/234/344/270/232.md +80 -0
- package/grades/grade-2/agents/math-tutor/activities//346/243/200/346/237/245/344/275/234/344/270/232./344/276/213/345/255/220.md +35 -0
- package/grades/grade-2/agents/math-tutor/activities//351/242/204/344/271/240.md +81 -0
- package/grades/grade-2/agents/math-tutor/activities//351/242/204/344/271/240./344/276/213/345/255/220.md +35 -0
- package/grades/grade-2/agents/math-tutor.md +51 -0
- package/grades/grade-2/examples/2026-10-27-pingjunfen/audio/index.json +44 -0
- package/grades/grade-2/examples/2026-10-27-pingjunfen/audio/step-1.mp3 +0 -0
- package/grades/grade-2/examples/2026-10-27-pingjunfen/audio/step-2.mp3 +0 -0
- package/grades/grade-2/examples/2026-10-27-pingjunfen/audio/step-3.mp3 +0 -0
- package/grades/grade-2/examples/2026-10-27-pingjunfen/audio/step-4.mp3 +0 -0
- package/grades/grade-2/examples/2026-10-27-pingjunfen/audio/step-5.mp3 +0 -0
- package/grades/grade-2/examples/2026-10-27-pingjunfen/bake.json +1 -0
- package/grades/grade-2/examples/2026-10-27-pingjunfen/manifest.json +63 -0
- package/grades/grade-2/examples/2026-10-27-pingjunfen/scene.json +247 -0
- package/grades/grade-2/examples/2026-10-27-pingjunfen.md +40 -0
- package/grades/grade-2/examples/2026-10-27-pingjunfen.ts +26 -0
- package/grades/grade-2/examples//345/271/263/345/235/207/345/210/206./344/276/213/345/255/220.md +24 -0
- package/grades/grade-2/examples//346/225/260/345/255/246/345/271/263/345/235/207/345/210/206.md +42 -0
- package/grades/grade-2/validation/2026-10-27.json +4410 -0
- package/package.json +27 -15
- package/skills/chalk-cli/SKILL.md +62 -0
- package/skills/chalk-scene/SKILL.md +91 -0
- package/skills/chalk-teaching/SKILL.md +82 -0
- package/skills/chalk-teaching/models//344/272/244/351/224/231/346/225/260/345/210/227/345/210/206/350/241/214.md +8 -0
- package/skills/chalk-teaching/models//345/207/221/345/215/201.md +7 -0
- package/skills/chalk-teaching/models//345/221/250/346/234/237/350/247/204/345/276/213.md +6 -0
- package/skills/chalk-teaching/models//345/256/236/347/211/251/345/233/276-/345/242/236/345/207/217.md +6 -0
- package/skills/chalk-teaching/models//345/256/236/347/211/251/345/233/276-/346/257/224/345/244/232/345/260/221-/346/261/202/345/244/247/346/225/260/346/261/202/345/260/217/346/225/260.md +7 -0
- package/skills/chalk-teaching/models//345/256/236/347/211/251/345/233/276-/346/257/224/345/244/232/345/260/221-/346/261/202/345/267/256.md +6 -0
- package/skills/chalk-teaching/models//345/260/217/346/243/222/350/277/233/344/275/215/345/212/240.md +7 -0
- package/skills/chalk-teaching/models//345/260/217/346/243/222/351/200/200/344/275/215/345/207/217.md +6 -0
- package/skills/chalk-teaching/models//346/216/222/351/230/237/347/202/271/351/230/265.md +7 -0
- package/skills/chalk-teaching/models//346/227/266/351/227/264/346/225/260/350/275/264.md +6 -0
- package/skills/chalk-teaching/models//346/245/274/345/261/202/351/227/264/351/232/224/345/233/276.md +7 -0
- package/skills/chalk-teaching/models//347/240/264/345/215/201.md +7 -0
- package/skills/chalk-teaching/models//347/253/226/345/274/217/351/200/200/344/275/215.md +6 -0
- package/skills/chalk-teaching/models//347/272/277/346/256/265/345/233/276.md +7 -0
- package/skills/chalk-teaching/models-index.md +24 -0
- package/skills/chalk-teaching/references//350/256/262/350/247/243/346/250/241/346/235/277/344/270/211/346/241/243.md +32 -0
- package/skills/chalk-teaching/references//350/257/264/346/230/216/346/226/207/344/273/266.md +102 -0
- package/skills/chalk-verify/SKILL.md +57 -0
- package/skills/chalk-verify/references//350/257/204/345/210/244/346/240/207/345/207/206.md +69 -0
- package/skills/cotutor-analyze/SKILL.md +106 -0
- package/skills/cotutor-analyze/references//345/221/275/344/273/244/344/270/216/346/226/207/344/273/266.md +31 -0
- package/skills/cotutor-board/SKILL.md +373 -0
- package/skills/cotutor-board/references/README.md +38 -0
- package/skills/cotutor-board/references/canvas.md +68 -0
- package/skills/cotutor-board/references/choice.md +55 -0
- package/skills/cotutor-board/references/code.md +46 -0
- package/skills/cotutor-board/references/image.md +52 -0
- package/skills/cotutor-board/references/lecture.md +69 -0
- package/skills/cotutor-board/references/paint.md +77 -0
- package/skills/cotutor-board/references/photo.md +61 -0
- package/skills/cotutor-board/references/read.md +51 -0
- package/skills/cotutor-board/references/text.md +69 -0
- package/skills/cotutor-board/references/tianzige.md +78 -0
- package/skills/cotutor-board/references/word.md +91 -0
- package/skills/cotutor-home/SKILL.md +170 -0
- package/skills/cotutor-home/references/dictation.md +97 -0
- package/skills/cotutor-home/references/tutor.md +149 -0
- package/skills/cotutor-home/references//345/221/275/344/273/244.md +21 -0
- package/skills/cotutor-home/references//351/246/226/351/241/265/350/257/255/346/263/225.md +143 -0
- package/skills/cotutor-tune/SKILL.md +147 -0
- package/skills/cotutor-tune/references//345/255/227/346/256/265.md +53 -0
- package/skills/cotutor-tutor/SKILL.md +253 -0
- package/skills/cotutor-tutor/references//344/270/212/344/270/213/346/226/207/345/214/205.md +78 -0
- package/themes/default/kid.css +86 -0
- package/themes/default/theme.json +26 -0
- package/agents/chinese-tutor.md +0 -67
- package/agents/homework-tutor.md +0 -17
- package/agents/math-tutor.md +0 -68
- package/agents/planner.md +0 -18
- package/agents/reading-tutor.md +0 -65
- package/agents/scene-maker.md +0 -41
- package/dist/cards/canvas.d.ts +0 -22
- package/dist/cards/canvas.js +0 -65
- package/dist/cards/fill.d.ts +0 -14
- package/dist/cards/fill.js +0 -64
- package/dist/cards/index.d.ts +0 -48
- package/dist/cards/index.js +0 -115
- package/dist/cards/kind.js +0 -7
- package/dist/cards/scene.d.ts +0 -24
- package/dist/cards/scene.js +0 -67
- package/dist/cards/text.d.ts +0 -18
- package/dist/cards/text.js +0 -41
- package/dist/cli/cert.d.ts +0 -7
- package/dist/cli/cert.js +0 -36
- package/dist/cli/doctor.d.ts +0 -22
- package/dist/cli/doctor.js +0 -355
- package/dist/cli/init.js +0 -116
- package/dist/cli/main.js +0 -243
- package/dist/cli/serve.d.ts +0 -30
- package/dist/cli/serve.js +0 -56
- package/dist/cli/skeleton.d.ts +0 -39
- package/dist/cli/skeleton.js +0 -184
- package/dist/cli/skills.d.ts +0 -40
- package/dist/cli/skills.js +0 -170
- package/dist/cli/workspace.d.ts +0 -69
- package/dist/lib/agent-file.d.ts +0 -10
- package/dist/lib/agent-file.js +0 -16
- package/dist/lib/board.d.ts +0 -15
- package/dist/lib/board.js +0 -107
- package/dist/lib/context-pack.js +0 -55
- package/dist/lib/conversation.js +0 -88
- package/dist/lib/kid-board.d.ts +0 -148
- package/dist/lib/kid-board.js +0 -294
- package/dist/lib/kid-view.d.ts +0 -63
- package/dist/lib/kid-view.js +0 -93
- package/dist/lib/ledger.d.ts +0 -29
- package/dist/lib/run-plan.d.ts +0 -30
- package/dist/lib/run-plan.js +0 -32
- package/dist/lib/sections.d.ts +0 -12
- package/dist/lib/sections.js +0 -151
- package/dist/lib/stream.d.ts +0 -15
- package/dist/lib/stream.js +0 -69
- package/dist/lib/transcript.d.ts +0 -36
- package/dist/lib/transcript.js +0 -95
- package/dist/server/app.d.ts +0 -74
- package/dist/server/app.js +0 -406
- package/dist/server/kid-page.d.ts +0 -1
- package/dist/server/kid-page.js +0 -768
- package/dist/server/mock.js +0 -403
- package/dist/server/parent-page.d.ts +0 -8
- package/dist/server/parent-page.js +0 -393
- package/dist/server/runner.d.ts +0 -64
- package/dist/server/runner.js +0 -277
- package/dist/server/scene-props.d.ts +0 -16
- package/dist/server/scene-props.js +0 -39
- package/dist/server/store.d.ts +0 -43
- package/dist/server/store.js +0 -210
- package/dist/server/tts.d.ts +0 -51
- package/dist/server/tts.js +0 -99
- /package/dist/{cli → app/cli}/main.d.ts +0 -0
- /package/dist/{cards → board/cards}/choice.d.ts +0 -0
- /package/dist/{cards → board/cards}/code.d.ts +0 -0
- /package/dist/{cards → board/cards}/image.d.ts +0 -0
- /package/dist/{cards → board/cards}/read.d.ts +0 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cotutor activity eval:照例子文件,真老师 + 模拟孩子跑多轮,判只看板书。
|
|
3
|
+
* 跑在 evals/ 里(同回放:不进孩子的对话、不配音、不写记忆);画家是假的(不花钱,当画好了)。一个例子跑几次,次次都过才算过(pass^k)。
|
|
4
|
+
* 模拟孩子交给千问的对话模型(缺省 qwen3.8-flash,有 Token Plan 走它,不然百炼按量);写死的那几句先说完再交给它。
|
|
5
|
+
*/
|
|
6
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
7
|
+
import { dirname, isAbsolute, join } from 'node:path';
|
|
8
|
+
import { chatUrl, qianwenKeys, qianwenLine } from "../shared/qianwen.js";
|
|
9
|
+
import { ACTIVITIES_DIR, activityRunFor } from "../runtime/activities.js";
|
|
10
|
+
import { workspaceGradePack } from "./cli/grades.js";
|
|
11
|
+
import { UsageError } from "../shared/errors.js";
|
|
12
|
+
import { gradeRun, kidPrompt, parseExamples } from "./activity-eval-rules.js";
|
|
13
|
+
import { evalWorkspace, EVALS_DIR } from "./replay.js";
|
|
14
|
+
import { readLecture } from "../runtime/lecture.js";
|
|
15
|
+
import { Runner } from "../runtime/runner.js";
|
|
16
|
+
import { writeCapture } from "../runtime/journal/store.js";
|
|
17
|
+
/** 模拟孩子缺省的模型(2026-10-13 从 qwen-plus 换来:Token Plan 有它,回归跑得最多) */
|
|
18
|
+
export const KID_MODEL = 'qwen3.8-flash';
|
|
19
|
+
export function makeKidModel(opts = {}) {
|
|
20
|
+
const env = opts.env ?? process.env;
|
|
21
|
+
return async (prompt) => {
|
|
22
|
+
const model = opts.model ?? KID_MODEL;
|
|
23
|
+
const line = qianwenLine(model, qianwenKeys(env));
|
|
24
|
+
if (!line)
|
|
25
|
+
return null;
|
|
26
|
+
try {
|
|
27
|
+
const res = await fetch(opts.endpoint ?? chatUrl(line.host), {
|
|
28
|
+
method: 'POST',
|
|
29
|
+
headers: { authorization: `Bearer ${line.key}`, 'content-type': 'application/json' },
|
|
30
|
+
body: JSON.stringify({ model, enable_thinking: false, messages: [{ role: 'user', content: prompt }] }),
|
|
31
|
+
signal: AbortSignal.timeout(opts.timeoutMs ?? 30_000),
|
|
32
|
+
});
|
|
33
|
+
if (!res.ok)
|
|
34
|
+
return null;
|
|
35
|
+
const text = (await res.json()).choices?.[0]?.message?.content ?? '';
|
|
36
|
+
return text.replace(/^["“「]|["”」]$/g, '').replace(/^孩子[:\uFF1A]\s*/, '').trim() || null;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/** 画家是假的:当画好了(1×1 的 png),不花钱 */
|
|
44
|
+
const PNG = Buffer.from('89504e470d0a1a0a0000000d4948445200000001000000010806000000', 'hex');
|
|
45
|
+
const fakeDraw = async ({ model }) => ({ ok: true, data: PNG, model, ms: 1 });
|
|
46
|
+
export function examplesFile(root, tutor, name) {
|
|
47
|
+
return join(root, 'agents', tutor, 'activities', `${name}.例子.md`);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* 跑一个 activity 的例子。onTurn 每一轮回调一次(终端现场打印)。
|
|
51
|
+
* 例子文件不在、写坏、参数不对 → UsageError(不起老师)。
|
|
52
|
+
*/
|
|
53
|
+
export async function evalActivity(ws, tutor, name, opts = {}) {
|
|
54
|
+
if (!ws.config.tutors[tutor])
|
|
55
|
+
throw new UsageError(`没有叫 ${tutor} 的老师`);
|
|
56
|
+
const file = examplesFile(ws.root, tutor, name);
|
|
57
|
+
const text = await readFile(file, 'utf8').catch(() => null);
|
|
58
|
+
if (text === null)
|
|
59
|
+
throw new UsageError(`${file} 不在:写几个例子再跑(写法见出厂的 agents/chinese-tutor/activities/你说我画.例子.md)`);
|
|
60
|
+
const parsed = parseExamples(text);
|
|
61
|
+
if (parsed.issues.length)
|
|
62
|
+
throw new UsageError(`例子文件要改:\n${parsed.issues.map((i) => ` ${i.line ? `第 ${i.line} 行 ` : ''}${i.text}`).join('\n')}`);
|
|
63
|
+
if (parsed.activity !== name)
|
|
64
|
+
throw new UsageError(`例子文件头上写的是 activity: ${parsed.activity},不是 ${name}`);
|
|
65
|
+
const examples = parsed.examples.filter((e) => !opts.only || e.name === opts.only);
|
|
66
|
+
if (!examples.length)
|
|
67
|
+
throw new UsageError(`没有叫「${opts.only}」的例子;有:${parsed.examples.map((e) => e.name).join('、')}`);
|
|
68
|
+
const runsN = opts.runs ?? 3;
|
|
69
|
+
// 参数先都解析一遍:写错了一轮都不跑
|
|
70
|
+
const runsOf = new Map();
|
|
71
|
+
for (const e of examples)
|
|
72
|
+
runsOf.set(e, await activityRunFor(ws.root, tutor, name, e.params));
|
|
73
|
+
// 照片先拷进 captures/(和孩子端拍照、cotutor send --photo 同一条路);读不到一轮都不跑
|
|
74
|
+
const photosOf = new Map();
|
|
75
|
+
for (const e of examples) {
|
|
76
|
+
const saved = [];
|
|
77
|
+
for (const p of e.photos) {
|
|
78
|
+
const src = isAbsolute(p) ? p : join(dirname(file), p);
|
|
79
|
+
// 出厂例子的图跟着包走,不拷进 workspace:workspace 里没有,就找包里同一个位置的
|
|
80
|
+
const data = (await readFile(src).catch(() => null)) ?? (isAbsolute(p) ? null : await readFile(join((await workspaceGradePack(ws.root)).agentsDir, tutor, ACTIVITIES_DIR, p)).catch(() => null));
|
|
81
|
+
if (!data)
|
|
82
|
+
throw new UsageError(`例子「${e.name}」的照片读不到:${src}`);
|
|
83
|
+
saved.push(await writeCapture(ws, new Date(), data, /\.png$/i.test(p) ? 'png' : 'jpg'));
|
|
84
|
+
}
|
|
85
|
+
photosOf.set(e, saved);
|
|
86
|
+
}
|
|
87
|
+
// 先看小课堂的例子:课要在 workspace 里(bundles/ 或 lectures/);读不出来一轮都不跑
|
|
88
|
+
for (const e of examples)
|
|
89
|
+
if (e.lecture && !(await readLecture(ws, e.lecture.bundle)))
|
|
90
|
+
throw new UsageError(`例子「${e.name}」的课 ${e.lecture.bundle} 不在 bundles/ 或 lectures/ 里:拷一份进来,或把「课:」换成你自己的课`);
|
|
91
|
+
const kid = opts.kid ?? makeKidModel({ env: opts.env });
|
|
92
|
+
const evalWs = evalWorkspace(ws);
|
|
93
|
+
const runner = new Runner(() => evalWs, { env: opts.env, draw: opts.draw ?? fakeDraw });
|
|
94
|
+
const out = [];
|
|
95
|
+
for (const e of examples) {
|
|
96
|
+
const run = runsOf.get(e);
|
|
97
|
+
const runs = [];
|
|
98
|
+
for (let k = 0; k < runsN; k++) {
|
|
99
|
+
const turns = [];
|
|
100
|
+
let thread = '';
|
|
101
|
+
let date = '';
|
|
102
|
+
const tokens = { in: 0, out: 0 };
|
|
103
|
+
let error;
|
|
104
|
+
// 第一句是按钮上的字(activity 的名字),和孩子点首页按钮一样;照片随第一句写死的孩子的话发。
|
|
105
|
+
// 先看小课堂的:第一句就是孩子看完问的那句(写死的第一句),带着 lecture(看完了、停过几次、画的几处)
|
|
106
|
+
let scripted = e.lecture ? 1 : 0;
|
|
107
|
+
let say = e.lecture ? e.kid[0] : name;
|
|
108
|
+
let photos = [];
|
|
109
|
+
let lecture = e.lecture ? { bundle: e.lecture.bundle, watchedMs: 0, finished: true, pauses: e.lecture.pauses, marks: e.lecture.marks.map((m) => ({ atMs: m.atMs, strokes: [], hit: m.text })) } : undefined;
|
|
110
|
+
while (say !== null && turns.length < e.maxTurns) {
|
|
111
|
+
// 真孩子都是实时的:模拟孩子的每一句也照实时写(上下文包带 live:、不补提问卡、讲稿不截);嘴不在,接住与打断不带
|
|
112
|
+
const started = await runner.send(tutor, { from: 'kid', text: say, eval: true, live: {}, ...(photos.length ? { photos } : {}), ...(lecture ? { lecture } : {}), ...(thread ? { thread } : { newThread: true, activity: run }) });
|
|
113
|
+
photos = [];
|
|
114
|
+
lecture = undefined;
|
|
115
|
+
thread = started.thread;
|
|
116
|
+
date = started.date;
|
|
117
|
+
const index = await started.done;
|
|
118
|
+
await runner.flush();
|
|
119
|
+
const m = index.messages.find((x) => x.job === started.job);
|
|
120
|
+
tokens.in += m?.tokens?.in ?? 0;
|
|
121
|
+
tokens.out += m?.tokens?.out ?? 0;
|
|
122
|
+
if (!m || m.result !== 'ok') {
|
|
123
|
+
error = `第 ${turns.length + 1} 轮老师出错:${m?.error ?? '未知'}`;
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
const section = m.section ?? null;
|
|
127
|
+
turns.push({ kid: say, section });
|
|
128
|
+
const paint = (section?.cards ?? []).some((c) => c.kind === 'paint');
|
|
129
|
+
opts.onTurn?.({ example: e.name, run: k + 1, turn: turns.length, kid: say, said: (section?.lines ?? []).map((l) => l.text).join(''), paint });
|
|
130
|
+
if (paint)
|
|
131
|
+
break;
|
|
132
|
+
if (scripted < e.kid.length) {
|
|
133
|
+
say = e.kid[scripted++];
|
|
134
|
+
if (scripted === 1)
|
|
135
|
+
photos = photosOf.get(e) ?? [];
|
|
136
|
+
}
|
|
137
|
+
else if (e.persona || e.story) {
|
|
138
|
+
say = await kid(kidPrompt(e, turns));
|
|
139
|
+
if (say === null) {
|
|
140
|
+
error = '模拟孩子不通(百炼 key?)';
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
else
|
|
145
|
+
say = null;
|
|
146
|
+
}
|
|
147
|
+
const grades = gradeRun(e.checks, turns, run.params);
|
|
148
|
+
runs.push({ turns, grades, pass: !error && grades.every((g) => g.pass), ...(error ? { error } : {}), tokens, thread, date });
|
|
149
|
+
}
|
|
150
|
+
out.push({ name: e.name, runs, pass: runs.every((r) => r.pass) });
|
|
151
|
+
}
|
|
152
|
+
const at = new Date().toISOString();
|
|
153
|
+
const rel = join(EVALS_DIR, 'activity-eval', `${tutor}-${name}-${at.replace(/[:.]/g, '-')}.json`);
|
|
154
|
+
const all = out.flatMap((x) => x.runs);
|
|
155
|
+
const report = { tutor, activity: name, at, examples: out, tokens: { in: all.reduce((a, r) => a + r.tokens.in, 0), out: all.reduce((a, r) => a + r.tokens.out, 0) }, file: rel };
|
|
156
|
+
await mkdir(join(ws.root, EVALS_DIR, 'activity-eval'), { recursive: true });
|
|
157
|
+
await writeFile(join(ws.root, rel), `${JSON.stringify(report, null, 2)}\n`);
|
|
158
|
+
return report;
|
|
159
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** 起来那一刻:commit 是完整哈希,显示时取前 7 位 */
|
|
2
|
+
export declare const BUILD: {
|
|
3
|
+
version: string | null;
|
|
4
|
+
commit: string | null;
|
|
5
|
+
dirty: boolean;
|
|
6
|
+
started: string;
|
|
7
|
+
};
|
|
8
|
+
export declare const shortCommit: (c: string | null) => string | null;
|
|
9
|
+
export declare function headNow(): Promise<string | null>;
|
|
10
|
+
/** 代码更新了没有:起来时的提交与仓里现在的都知道、而且不一样 */
|
|
11
|
+
export declare const codeChanged: (started: string | null, head: string | null) => boolean;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 这个 serve 跑的是哪份代码:版本号、git 提交(包目录自己是 git 仓才有;npm 装的没有)、有没有没提交的改动、启动时刻。
|
|
3
|
+
* 仓里的提交后来变了 = 代码更新了,要重起 serve 才用上(serve 只热重载 cotutor.json;Ray 跑的是仓里检出的分支)。
|
|
4
|
+
* 工作台侧栏(/api/health)与孩子端流水的打开那行(srv)用。
|
|
5
|
+
*/
|
|
6
|
+
import { execFile, execFileSync } from 'node:child_process';
|
|
7
|
+
import { readFileSync, realpathSync } from 'node:fs';
|
|
8
|
+
import { fileURLToPath } from 'node:url';
|
|
9
|
+
const ROOT = fileURLToPath(new URL('../..', import.meta.url));
|
|
10
|
+
function readVersion() {
|
|
11
|
+
try {
|
|
12
|
+
return JSON.parse(readFileSync(new URL('../../package.json', import.meta.url), 'utf8')).version;
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/** 包目录自己是 git 仓才认(装在别人的仓里的 node_modules 不算) */
|
|
19
|
+
function readCommit() {
|
|
20
|
+
const git = (args) => execFileSync('git', ['-C', ROOT, ...args], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: 3000 }).trim();
|
|
21
|
+
try {
|
|
22
|
+
if (realpathSync(git(['rev-parse', '--show-toplevel'])) !== realpathSync(ROOT))
|
|
23
|
+
return { commit: null, dirty: false };
|
|
24
|
+
return { commit: git(['rev-parse', 'HEAD']), dirty: git(['status', '--porcelain', '--untracked-files=no']) !== '' };
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return { commit: null, dirty: false };
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/** 起来那一刻:commit 是完整哈希,显示时取前 7 位 */
|
|
31
|
+
export const BUILD = { version: readVersion(), ...readCommit(), started: new Date().toISOString() };
|
|
32
|
+
export const shortCommit = (c) => (c ? c.slice(0, 7) : null);
|
|
33
|
+
/** 仓里现在的提交(十秒内复用);不是 git 仓 → null */
|
|
34
|
+
let cached = { at: 0, head: null };
|
|
35
|
+
export async function headNow() {
|
|
36
|
+
if (!BUILD.commit)
|
|
37
|
+
return null;
|
|
38
|
+
if (Date.now() - cached.at < 10_000)
|
|
39
|
+
return cached.head;
|
|
40
|
+
const head = await new Promise((resolve) => execFile('git', ['-C', ROOT, 'rev-parse', 'HEAD'], { timeout: 3000 }, (err, out) => resolve(err ? null : String(out).trim() || null)));
|
|
41
|
+
cached = { at: Date.now(), head };
|
|
42
|
+
return head;
|
|
43
|
+
}
|
|
44
|
+
/** 代码更新了没有:起来时的提交与仓里现在的都知道、而且不一样 */
|
|
45
|
+
export const codeChanged = (started, head) => Boolean(started && head && started !== head);
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { type ActivityIssue } from '../../runtime/activity.ts';
|
|
2
|
+
import type { Activity } from '../../schema/index.ts';
|
|
3
|
+
import { type TutorState } from './tutors.ts';
|
|
4
|
+
import { ACTIVITIES_DIR, EXAMPLES_SUFFIX, activityFile, activityRunFor, listActivities, type ActivityRef } from '../../runtime/activities.ts';
|
|
5
|
+
export { ACTIVITIES_DIR, EXAMPLES_SUFFIX, activityFile, activityRunFor, listActivities, type ActivityRef };
|
|
6
|
+
/** 出厂的 activity 与它们的例子文件 */
|
|
7
|
+
export declare const shippedActivities: (root?: string) => Promise<ActivityRef[]>;
|
|
8
|
+
export interface ActivityStatus {
|
|
9
|
+
key: string;
|
|
10
|
+
state: TutorState;
|
|
11
|
+
file: string;
|
|
12
|
+
basedOn?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function activityStatuses(root: string): Promise<ActivityStatus[]>;
|
|
15
|
+
export interface ActivityRename {
|
|
16
|
+
/** 新的键「老师/新名」 */
|
|
17
|
+
key: string;
|
|
18
|
+
from: string;
|
|
19
|
+
}
|
|
20
|
+
export interface ActivityInstallStep {
|
|
21
|
+
item: string;
|
|
22
|
+
action: 'created' | 'exists' | 'kept' | 'replaced';
|
|
23
|
+
note?: string;
|
|
24
|
+
}
|
|
25
|
+
/** init 用:缺的拷,有的不动(家长的) */
|
|
26
|
+
export declare function installActivities(root: string): Promise<ActivityInstallStep[]>;
|
|
27
|
+
export interface ActivityUpgradeStep {
|
|
28
|
+
key: string;
|
|
29
|
+
action: 'upgraded' | 'latest' | 'kept-custom' | 'forced' | 'installed' | 'renamed';
|
|
30
|
+
basedOn?: string;
|
|
31
|
+
diff?: string[];
|
|
32
|
+
/** renamed:原来的名字 */
|
|
33
|
+
from?: string;
|
|
34
|
+
}
|
|
35
|
+
/** upgrade 用:同 upgradeTutors;force 里写「老师/名」才覆盖(原文留 .bak) */
|
|
36
|
+
export declare function upgradeActivities(root: string, opts?: {
|
|
37
|
+
force?: string[];
|
|
38
|
+
}): Promise<ActivityUpgradeStep[]>;
|
|
39
|
+
/**
|
|
40
|
+
* 只把一个出厂 activity 换回出厂那份(工作台「恢复出厂」,与 upgrade --force 老师/名 同一条路,但不碰别的):
|
|
41
|
+
* 现在那份和出厂不一样就先存 .bak。不是出厂的 → UsageError。返回 .bak 的路径(没存就是 null)
|
|
42
|
+
*/
|
|
43
|
+
export declare function restoreActivity(root: string, tutor: string, name: string): Promise<{
|
|
44
|
+
bak: string | null;
|
|
45
|
+
}>;
|
|
46
|
+
/** 出厂那份的原文;不是出厂的 → null */
|
|
47
|
+
export declare function shippedActivityText(tutor: string, name: string, root?: string): Promise<string | null>;
|
|
48
|
+
/** workspace 里一个 activity 现在的状态(出厂的看新旧;自家加的是 untracked) */
|
|
49
|
+
export declare function activityStatusOf(root: string, tutor: string, name: string): Promise<ActivityStatus>;
|
|
50
|
+
export interface ActivityCheck {
|
|
51
|
+
tutor: string;
|
|
52
|
+
name: string;
|
|
53
|
+
file: string;
|
|
54
|
+
activity: Activity | null;
|
|
55
|
+
issues: ActivityIssue[];
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* cotutor activity check:workspace 里每个(或某位老师的、某一个)activity 文件解析 + 检查。
|
|
59
|
+
* tutors 是 cotutor.json 的老师表(键 → 这位老师的 cards;没写 cards 是 undefined = 全部);老师不在表里也报一条。
|
|
60
|
+
*/
|
|
61
|
+
export declare function checkActivities(root: string, tutors: Readonly<Record<string, {
|
|
62
|
+
cards?: readonly string[];
|
|
63
|
+
}>>, opts?: {
|
|
64
|
+
tutor?: string;
|
|
65
|
+
name?: string;
|
|
66
|
+
}): Promise<ActivityCheck[]>;
|
|
67
|
+
/**
|
|
68
|
+
* 终端里起一个 activity 话题(cotutor send / pack --activity):读 workspace 里那份,参数照首页按钮的写法给(`名=值`,几个用 ; 隔开),
|
|
69
|
+
* 和孩子点按钮同一条解析(补缺省、按类型转)。不在、参数不对 → UsageError。
|
|
70
|
+
*/
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* activity 文件的安装、升级与检查。和老师文件同一套:**拷贝,不链**。
|
|
3
|
+
* 包里的 grades/grade-<n>/agents/<老师>/activities/<名>.md 是出厂件;init 拷进 workspace 同一个位置(老师的家里,会话 cwd 下),这份是家长的;
|
|
4
|
+
* 出厂 hash 记 .cotutor/shipped.json 的 activities(键「老师/名」),据此分 latest / upgradable / custom / untracked / missing,意思同 tutors.ts。
|
|
5
|
+
*/
|
|
6
|
+
import { existsSync } from 'node:fs';
|
|
7
|
+
import { readFile, readdir, mkdir, writeFile, lstat, unlink } from 'node:fs/promises';
|
|
8
|
+
import { dirname, join } from 'node:path';
|
|
9
|
+
import { lintActivity } from "../../runtime/activity.js";
|
|
10
|
+
import { PACKAGE_AGENTS_DIR, PACKAGE_VERSION } from "./skeleton.js";
|
|
11
|
+
import { lineDiff, readManifest, sha256, writeManifest } from "./tutors.js";
|
|
12
|
+
import { UsageError } from "../workspace.js";
|
|
13
|
+
import { workspaceGradePack } from "./grades.js";
|
|
14
|
+
import { ACTIVITIES_DIR, ACTIVITY_RENAMES, EXAMPLES_SUFFIX, activityFile, activityRunFor, listActivities } from "../../runtime/activities.js";
|
|
15
|
+
// 读的那一半在 src/runtime/activities.ts;照旧从这里转出
|
|
16
|
+
export { ACTIVITIES_DIR, EXAMPLES_SUFFIX, activityFile, activityRunFor, listActivities };
|
|
17
|
+
/** 出厂的 activity 与它们的例子文件 */
|
|
18
|
+
export const shippedActivities = async (root) => listActivities(root ? (await workspaceGradePack(root)).agentsDir : PACKAGE_AGENTS_DIR, undefined, { examples: true });
|
|
19
|
+
const keyOf = (a) => `${a.tutor}/${a.name}`;
|
|
20
|
+
async function readState(root, a, manifest) {
|
|
21
|
+
const key = keyOf(a);
|
|
22
|
+
const file = activityFile(root, a.tutor, a.name);
|
|
23
|
+
if (!(await lstat(file).catch(() => null)))
|
|
24
|
+
return { key, state: 'missing', file };
|
|
25
|
+
let text;
|
|
26
|
+
try {
|
|
27
|
+
text = await readFile(file, 'utf8');
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return { key, state: 'broken', file };
|
|
31
|
+
}
|
|
32
|
+
const h = sha256(text);
|
|
33
|
+
const rec = manifest.activities?.[key];
|
|
34
|
+
if (h === sha256(await readFile(a.file, 'utf8')))
|
|
35
|
+
return { key, state: 'latest', file, basedOn: rec?.version };
|
|
36
|
+
if (rec && rec.hash === h)
|
|
37
|
+
return { key, state: 'upgradable', file, basedOn: rec.version };
|
|
38
|
+
return { key, state: rec ? 'custom' : 'untracked', file, basedOn: rec?.version };
|
|
39
|
+
}
|
|
40
|
+
export async function activityStatuses(root) {
|
|
41
|
+
const manifest = await readManifest(root);
|
|
42
|
+
return Promise.all((await shippedActivities(root)).map((a) => readState(root, a, manifest)));
|
|
43
|
+
}
|
|
44
|
+
async function installOne(root, a, manifest) {
|
|
45
|
+
const file = activityFile(root, a.tutor, a.name);
|
|
46
|
+
await mkdir(dirname(file), { recursive: true });
|
|
47
|
+
const text = await readFile(a.file, 'utf8');
|
|
48
|
+
await writeFile(file, text);
|
|
49
|
+
manifest.teachingPack ??= { grade: (await workspaceGradePack(root)).grade };
|
|
50
|
+
manifest.activities ??= {};
|
|
51
|
+
manifest.activities[keyOf(a)] = { hash: sha256(text), version: PACKAGE_VERSION };
|
|
52
|
+
}
|
|
53
|
+
/** 头上的 name(activity 文件)/ activity(例子文件)从旧名改成新名;别处不动 */
|
|
54
|
+
function renameHead(text, from, to) {
|
|
55
|
+
return text.replace(/^---\n[\s\S]*?\n---/, (fm) => fm.replace(new RegExp(`^(name|activity):[ \t]*${from}[ \t]*$`, 'm'), `$1: ${to}`));
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* 改过名的出厂 activity(ACTIVITY_RENAMES):workspace 里旧名的文件与例子挪成新名,头上的名字跟着改;出厂记录跟着挪——
|
|
59
|
+
* 没改过的照旧算没改过(下面比新旧时换成新版),家长改过的照旧算自定义(内容不动,只改头上的名字)。新名已经有了就不动旧的
|
|
60
|
+
*/
|
|
61
|
+
async function migrateRenames(root, manifest) {
|
|
62
|
+
const out = [];
|
|
63
|
+
const tutors = (await readdir(join(root, 'agents'), { withFileTypes: true }).catch(() => [])).filter((d) => d.isDirectory()).map((d) => d.name).sort();
|
|
64
|
+
for (const t of tutors) {
|
|
65
|
+
for (const [from, to] of Object.entries(ACTIVITY_RENAMES)) {
|
|
66
|
+
for (const [a, b] of [[from, to], [from + EXAMPLES_SUFFIX, to + EXAMPLES_SUFFIX]]) {
|
|
67
|
+
const src = activityFile(root, t, a);
|
|
68
|
+
const dst = activityFile(root, t, b);
|
|
69
|
+
const text = await readFile(src, 'utf8').catch(() => null);
|
|
70
|
+
if (text === null || existsSync(dst))
|
|
71
|
+
continue;
|
|
72
|
+
const next = renameHead(text, from, to);
|
|
73
|
+
await writeFile(dst, next);
|
|
74
|
+
await unlink(src);
|
|
75
|
+
const rec = manifest.activities?.[`${t}/${a}`];
|
|
76
|
+
if (rec && manifest.activities) {
|
|
77
|
+
delete manifest.activities[`${t}/${a}`];
|
|
78
|
+
manifest.activities[`${t}/${b}`] = { ...rec, hash: rec.hash === sha256(text) ? sha256(next) : rec.hash };
|
|
79
|
+
}
|
|
80
|
+
out.push({ key: `${t}/${b}`, from: a });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return out;
|
|
85
|
+
}
|
|
86
|
+
/** init 用:缺的拷,有的不动(家长的) */
|
|
87
|
+
export async function installActivities(root) {
|
|
88
|
+
const steps = [];
|
|
89
|
+
const manifest = await readManifest(root);
|
|
90
|
+
// 改过名的先挪:不然 init 会在旧名旁边再拷一份新名的
|
|
91
|
+
const renamed = await migrateRenames(root, manifest);
|
|
92
|
+
let touched = !manifest.teachingPack || renamed.length > 0;
|
|
93
|
+
manifest.teachingPack ??= { grade: (await workspaceGradePack(root)).grade };
|
|
94
|
+
for (const r of renamed)
|
|
95
|
+
steps.push({ item: `agents/${r.key.replace('/', `/${ACTIVITIES_DIR}/`)}.md`, action: 'replaced', note: `改了名(原来叫「${r.from}」)` });
|
|
96
|
+
for (const a of await shippedActivities(root)) {
|
|
97
|
+
const s = await readState(root, a, manifest);
|
|
98
|
+
const item = `agents/${a.tutor}/${ACTIVITIES_DIR}/${a.name}.md`;
|
|
99
|
+
if (s.state === 'missing' || s.state === 'broken') {
|
|
100
|
+
await installOne(root, a, manifest);
|
|
101
|
+
touched = true;
|
|
102
|
+
steps.push({ item, action: s.state === 'missing' ? 'created' : 'replaced', note: s.state === 'broken' ? '读不到,重新拷贝' : `拷自本包 ${PACKAGE_VERSION}` });
|
|
103
|
+
}
|
|
104
|
+
else
|
|
105
|
+
steps.push({ item, action: s.state === 'untracked' ? 'kept' : 'exists', note: s.state === 'custom' ? `自定义(基于 ${s.basedOn})` : s.state === 'upgradable' ? '可升级(cotutor upgrade)' : s.state === 'untracked' ? '已有(没有出厂记录);升级时当自定义对待' : undefined });
|
|
106
|
+
}
|
|
107
|
+
if (touched)
|
|
108
|
+
await writeManifest(root, { ...manifest, version: PACKAGE_VERSION });
|
|
109
|
+
return steps;
|
|
110
|
+
}
|
|
111
|
+
/** upgrade 用:同 upgradeTutors;force 里写「老师/名」才覆盖(原文留 .bak) */
|
|
112
|
+
export async function upgradeActivities(root, opts = {}) {
|
|
113
|
+
const manifest = await readManifest(root);
|
|
114
|
+
manifest.teachingPack ??= { grade: (await workspaceGradePack(root)).grade };
|
|
115
|
+
const steps = (await migrateRenames(root, manifest)).map((r) => ({ key: r.key, action: 'renamed', from: r.from }));
|
|
116
|
+
for (const a of await shippedActivities(root)) {
|
|
117
|
+
const s = await readState(root, a, manifest);
|
|
118
|
+
if (s.state === 'latest')
|
|
119
|
+
steps.push({ key: s.key, action: 'latest', basedOn: s.basedOn });
|
|
120
|
+
else if (s.state === 'missing' || s.state === 'broken') {
|
|
121
|
+
await installOne(root, a, manifest);
|
|
122
|
+
steps.push({ key: s.key, action: 'installed' });
|
|
123
|
+
}
|
|
124
|
+
else if (s.state === 'upgradable') {
|
|
125
|
+
await installOne(root, a, manifest);
|
|
126
|
+
steps.push({ key: s.key, action: 'upgraded', basedOn: s.basedOn });
|
|
127
|
+
}
|
|
128
|
+
else if (opts.force?.includes(s.key)) {
|
|
129
|
+
await writeFile(`${s.file}.bak`, await readFile(s.file, 'utf8'));
|
|
130
|
+
await installOne(root, a, manifest);
|
|
131
|
+
steps.push({ key: s.key, action: 'forced', basedOn: s.basedOn });
|
|
132
|
+
}
|
|
133
|
+
else
|
|
134
|
+
steps.push({ key: s.key, action: 'kept-custom', basedOn: s.basedOn, diff: lineDiff(await readFile(s.file, 'utf8'), await readFile(a.file, 'utf8')) });
|
|
135
|
+
}
|
|
136
|
+
await writeManifest(root, { ...manifest, version: PACKAGE_VERSION });
|
|
137
|
+
return steps;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* 只把一个出厂 activity 换回出厂那份(工作台「恢复出厂」,与 upgrade --force 老师/名 同一条路,但不碰别的):
|
|
141
|
+
* 现在那份和出厂不一样就先存 .bak。不是出厂的 → UsageError。返回 .bak 的路径(没存就是 null)
|
|
142
|
+
*/
|
|
143
|
+
export async function restoreActivity(root, tutor, name) {
|
|
144
|
+
const ref = (await shippedActivities(root)).find((a) => a.tutor === tutor && a.name === name);
|
|
145
|
+
if (!ref)
|
|
146
|
+
throw new UsageError(`${tutor}/${name} 不是出厂的 activity,没有出厂那份可换`);
|
|
147
|
+
const manifest = await readManifest(root);
|
|
148
|
+
const file = activityFile(root, tutor, name);
|
|
149
|
+
const cur = await readFile(file, 'utf8').catch(() => null);
|
|
150
|
+
let bak = null;
|
|
151
|
+
if (cur !== null && cur !== (await readFile(ref.file, 'utf8'))) {
|
|
152
|
+
bak = `${file}.bak`;
|
|
153
|
+
await writeFile(bak, cur);
|
|
154
|
+
}
|
|
155
|
+
await installOne(root, ref, manifest);
|
|
156
|
+
await writeManifest(root, manifest);
|
|
157
|
+
return { bak };
|
|
158
|
+
}
|
|
159
|
+
/** 出厂那份的原文;不是出厂的 → null */
|
|
160
|
+
export async function shippedActivityText(tutor, name, root) {
|
|
161
|
+
const ref = (await shippedActivities(root)).find((a) => a.tutor === tutor && a.name === name);
|
|
162
|
+
return ref ? readFile(ref.file, 'utf8') : null;
|
|
163
|
+
}
|
|
164
|
+
/** workspace 里一个 activity 现在的状态(出厂的看新旧;自家加的是 untracked) */
|
|
165
|
+
export async function activityStatusOf(root, tutor, name) {
|
|
166
|
+
const ref = (await shippedActivities(root)).find((a) => a.tutor === tutor && a.name === name);
|
|
167
|
+
if (ref)
|
|
168
|
+
return readState(root, ref, await readManifest(root));
|
|
169
|
+
const file = activityFile(root, tutor, name);
|
|
170
|
+
return { key: `${tutor}/${name}`, state: (await lstat(file).catch(() => null)) ? 'untracked' : 'missing', file };
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* cotutor activity check:workspace 里每个(或某位老师的、某一个)activity 文件解析 + 检查。
|
|
174
|
+
* tutors 是 cotutor.json 的老师表(键 → 这位老师的 cards;没写 cards 是 undefined = 全部);老师不在表里也报一条。
|
|
175
|
+
*/
|
|
176
|
+
export async function checkActivities(root, tutors, opts = {}) {
|
|
177
|
+
const refs = (await listActivities(join(root, 'agents'), opts.tutor)).filter((r) => !opts.name || r.name === opts.name);
|
|
178
|
+
const out = [];
|
|
179
|
+
for (const r of refs) {
|
|
180
|
+
const text = await readFile(r.file, 'utf8').catch(() => null);
|
|
181
|
+
if (text === null) {
|
|
182
|
+
out.push({ ...r, activity: null, issues: [{ level: 'fix', text: '读不到' }] });
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
const t = tutors[r.tutor];
|
|
186
|
+
const { activity, issues } = lintActivity(text, { fileName: r.name, tutorCards: t?.cards });
|
|
187
|
+
if (!t)
|
|
188
|
+
issues.unshift({ level: 'fix', text: `cotutor.json 里没有 ${r.tutor} 这位老师,这个 activity 没人带` });
|
|
189
|
+
out.push({ ...r, activity, issues });
|
|
190
|
+
}
|
|
191
|
+
return out;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* 终端里起一个 activity 话题(cotutor send / pack --activity):读 workspace 里那份,参数照首页按钮的写法给(`名=值`,几个用 ; 隔开),
|
|
195
|
+
* 和孩子点按钮同一条解析(补缺省、按类型转)。不在、参数不对 → UsageError。
|
|
196
|
+
*/
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 这个系统上怎么装 mkcert、信任一次根证书(doctor 与 cert 共用)。Windows 上 mkcert -install 装进当前用户的根证书,会弹窗问,点「是」;
|
|
3
|
+
* 没有 winget 的(11-02 那台就没有)下官方的 exe。
|
|
4
|
+
*/
|
|
5
|
+
export declare function mkcertHint(platform?: NodeJS.Platform): string;
|
|
6
|
+
export interface CertResult {
|
|
7
|
+
cert: string;
|
|
8
|
+
key: string;
|
|
9
|
+
hosts: string[];
|
|
10
|
+
caRoot: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function makeCert(extraHosts?: string[]): Promise<CertResult>;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cotutor cert:用 mkcert 给这台机器签一张证书到 ~/.config/cotutor/certs/(cert.pem + key.pem),serve 看到就走 HTTPS。
|
|
3
|
+
* 为什么要:平板、手机浏览器的录音 / 语音识别只在安全上下文里给;局域网 IP 不是 localhost,得自签。
|
|
4
|
+
* 为什么放机器级而不是 workspace:签的是电脑名与局域网 IP,与孩子无关;多个 workspace 共用一张。
|
|
5
|
+
* 主机名 = 电脑名、电脑名.local、所有局域网 IPv4、localhost、127.0.0.1,再加 --host 给的。已有证书就覆盖(证书可再生)。不需要 workspace。
|
|
6
|
+
* 地址认电脑名,IP 变了不用重签;证书里的 IP 只给手输数字地址的退路用。改了电脑名才要重签。
|
|
7
|
+
*/
|
|
8
|
+
import { execFile } from 'node:child_process';
|
|
9
|
+
import { mkdir } from 'node:fs/promises';
|
|
10
|
+
import { hostname } from 'node:os';
|
|
11
|
+
import { join } from 'node:path';
|
|
12
|
+
import { promisify } from 'node:util';
|
|
13
|
+
import { computerName, lanAddresses } from "./serve.js";
|
|
14
|
+
import { USER_CERT_DIR, UsageError } from "../workspace.js";
|
|
15
|
+
const execFileP = promisify(execFile);
|
|
16
|
+
/**
|
|
17
|
+
* 这个系统上怎么装 mkcert、信任一次根证书(doctor 与 cert 共用)。Windows 上 mkcert -install 装进当前用户的根证书,会弹窗问,点「是」;
|
|
18
|
+
* 没有 winget 的(11-02 那台就没有)下官方的 exe。
|
|
19
|
+
*/
|
|
20
|
+
export function mkcertHint(platform = process.platform) {
|
|
21
|
+
if (platform === 'darwin')
|
|
22
|
+
return 'brew install mkcert && mkcert -install';
|
|
23
|
+
if (platform === 'win32')
|
|
24
|
+
return 'winget install FiloSottile.mkcert(没有 winget 就到 https://github.com/FiloSottile/mkcert/releases 下 windows-amd64 那个,改名 mkcert.exe 放进 PATH),重开终端跑 mkcert -install(弹窗问装不装根证书,点「是」)';
|
|
25
|
+
return '用系统的包管理器装 mkcert(如 apt install mkcert),再 mkcert -install';
|
|
26
|
+
}
|
|
27
|
+
export async function makeCert(extraHosts = []) {
|
|
28
|
+
let caRoot;
|
|
29
|
+
try {
|
|
30
|
+
caRoot = (await execFileP('mkcert', ['-CAROOT'], { timeout: 8000 })).stdout.trim();
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
throw new UsageError(`PATH 里没有 mkcert。装:${mkcertHint()}(这台电脑信任一次根证书);平板、手机端见 cert 命令输出的提示与 docs/iPad与iPhone.md。`);
|
|
34
|
+
}
|
|
35
|
+
const name = computerName();
|
|
36
|
+
// os.hostname() 也带上:和电脑名不一样时(macOS 上可能跟着网络变),以前能开的地址别丢
|
|
37
|
+
const hosts = [...new Set([name, `${name}.local`, hostname(), ...lanAddresses(), 'localhost', '127.0.0.1', ...extraHosts])];
|
|
38
|
+
const dir = USER_CERT_DIR;
|
|
39
|
+
await mkdir(dir, { recursive: true });
|
|
40
|
+
const cert = join(dir, 'cert.pem');
|
|
41
|
+
const key = join(dir, 'key.pem');
|
|
42
|
+
try {
|
|
43
|
+
await execFileP('mkcert', ['-cert-file', cert, '-key-file', key, ...hosts], { timeout: 60_000 });
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
throw new UsageError(`mkcert 签证书失败:${err instanceof Error ? err.message : String(err)}\n先跑 mkcert -install,再重试。`);
|
|
47
|
+
}
|
|
48
|
+
return { cert, key, hosts, caRoot };
|
|
49
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const BAKE_FILE = "bake.json";
|
|
2
|
+
/** 课包有没有烤过、烤的是不是现在这幅画面 */
|
|
3
|
+
export declare function bakeIsFresh(bundleDir: string): Promise<boolean>;
|
|
4
|
+
/** 烤一个或几个课包(同一个浏览器);返回每个写到哪 */
|
|
5
|
+
export declare function bakeBundles(bundlesDir: string, ids: readonly string[]): Promise<{
|
|
6
|
+
id: string;
|
|
7
|
+
file: string;
|
|
8
|
+
elements: number;
|
|
9
|
+
bytes: number;
|
|
10
|
+
}[]>;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* bake:课包 → bake.json(格式在 src/chalk/paint.ts)。起临时页面服务(./serve.ts)与本机 Chrome(./browser.ts),开舞台的 ?chalk=bake,
|
|
3
|
+
* 在浏览器里用 excalidraw 静态导出一次、量好路径;孩子端播放(src/ui/stage/chalk/render.ts)与服务端出图(renderFrameSvg)就不用 excalidraw 了。
|
|
4
|
+
* source.hash 是画面的指纹(skeletons + background):scene.json 的画面变了 bakeIsFresh 就是 false,要重烤;配音与时长不算画面。
|
|
5
|
+
* 服务端(src/runtime/lecture.ts 的 ensureBaked)遇到没烤过的课包,起 cotutor chalk bake 补烤。
|
|
6
|
+
*/
|
|
7
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
8
|
+
import { createRequire } from 'node:module';
|
|
9
|
+
import { join } from 'node:path';
|
|
10
|
+
import { bakeMatches, pictureHash } from "../../../chalk/paint.js";
|
|
11
|
+
import { withBrowser } from "./browser.js";
|
|
12
|
+
import { startPageServer } from "./serve.js";
|
|
13
|
+
export const BAKE_FILE = 'bake.json';
|
|
14
|
+
function excalidrawVersion() {
|
|
15
|
+
try {
|
|
16
|
+
return createRequire(import.meta.url)('@excalidraw/excalidraw/package.json').version;
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return 'unknown';
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/** 课包有没有烤过、烤的是不是现在这幅画面 */
|
|
23
|
+
export async function bakeIsFresh(bundleDir) {
|
|
24
|
+
try {
|
|
25
|
+
const scene = JSON.parse(await readFile(join(bundleDir, 'scene.json'), 'utf8'));
|
|
26
|
+
return bakeMatches(JSON.parse(await readFile(join(bundleDir, BAKE_FILE), 'utf8')), scene);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/** 烤一个或几个课包(同一个浏览器);返回每个写到哪 */
|
|
33
|
+
export async function bakeBundles(bundlesDir, ids) {
|
|
34
|
+
const server = await startPageServer(bundlesDir);
|
|
35
|
+
try {
|
|
36
|
+
return await withBrowser(async (page) => {
|
|
37
|
+
const out = [];
|
|
38
|
+
for (const id of ids) {
|
|
39
|
+
await page.goto(`${server.origin}/stage/?chalk=bake&bundle=${encodeURIComponent(`/api/bundles/${id}/`)}`);
|
|
40
|
+
await page.waitFor(`document.body.dataset.chalkDone === '1'`, 120_000);
|
|
41
|
+
const err = await page.evaluate('window.__chalkError ?? null');
|
|
42
|
+
if (err)
|
|
43
|
+
throw new Error(`课包 '${id}' 烤不了:${err}`);
|
|
44
|
+
const picture = JSON.parse(await page.evaluate('JSON.stringify(window.__chalkBake)'));
|
|
45
|
+
const scene = JSON.parse(await readFile(join(bundlesDir, id, 'scene.json'), 'utf8'));
|
|
46
|
+
const baked = { ...picture, source: { excalidraw: excalidrawVersion(), hash: pictureHash(scene) } };
|
|
47
|
+
const file = join(bundlesDir, id, BAKE_FILE);
|
|
48
|
+
const text = `${JSON.stringify(baked)}\n`;
|
|
49
|
+
await writeFile(file, text);
|
|
50
|
+
out.push({ id, file, elements: baked.elements.length, bytes: Buffer.byteLength(text) });
|
|
51
|
+
}
|
|
52
|
+
return out;
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
finally {
|
|
56
|
+
await server.close();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** 本机 Chrome(Windows 上没有 Chrome 就是 Edge)的可执行文件;找不到 null */
|
|
2
|
+
export declare function chromePath(env?: NodeJS.ProcessEnv, platform?: NodeJS.Platform): string | null;
|
|
3
|
+
export interface Page {
|
|
4
|
+
goto(url: string): Promise<void>;
|
|
5
|
+
/** 轮询到页面里这个表达式为真;超时报错 */
|
|
6
|
+
waitFor(expr: string, timeoutMs?: number): Promise<void>;
|
|
7
|
+
evaluate<T>(expr: string): Promise<T>;
|
|
8
|
+
/** 截一个元素(CSS 选择器)那一块,png */
|
|
9
|
+
screenshot(selector: string): Promise<Buffer>;
|
|
10
|
+
}
|
|
11
|
+
/** 起一个 Chrome、开一页交给 fn;不论成败都收干净 */
|
|
12
|
+
export declare function withBrowser<T>(fn: (page: Page) => Promise<T>, opts?: {
|
|
13
|
+
width?: number;
|
|
14
|
+
height?: number;
|
|
15
|
+
env?: NodeJS.ProcessEnv;
|
|
16
|
+
}): Promise<T>;
|