cotutor 0.3.0 → 0.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 (209) hide show
  1. package/LICENSE +9 -7
  2. package/NOTICE +3 -3
  3. package/README.md +65 -18
  4. package/avatars/chinese.webp +0 -0
  5. package/avatars/english.webp +0 -0
  6. package/avatars/math.webp +0 -0
  7. package/bin/cotutor.js +13 -3
  8. package/cards/photo/card.css +10 -3
  9. package/cards/photo/card.md +2 -2
  10. package/cards/tutor/card.css +13 -0
  11. package/cards/tutor/card.md +2 -1
  12. package/cards//346/235/277/344/271/246/346/200/216/344/271/210/345/206/231.md +1 -1
  13. package/dist/app/activity-eval.js +3 -2
  14. package/dist/app/cli/activities.d.ts +8 -1
  15. package/dist/app/cli/activities.js +42 -4
  16. package/dist/app/cli/cert.d.ts +11 -0
  17. package/dist/app/cli/cert.js +36 -9
  18. package/dist/app/cli/chalk/browser.d.ts +2 -2
  19. package/dist/app/cli/chalk/browser.js +22 -7
  20. package/dist/app/cli/chalk/main.js +3 -2
  21. package/dist/app/cli/doctor.d.ts +11 -5
  22. package/dist/app/cli/doctor.js +156 -181
  23. package/dist/app/cli/firewall.d.ts +24 -0
  24. package/dist/app/cli/firewall.js +79 -0
  25. package/dist/app/cli/init.js +8 -5
  26. package/dist/app/cli/main.js +379 -27
  27. package/dist/app/cli/migrate.d.ts +2 -11
  28. package/dist/app/cli/migrate.js +1 -59
  29. package/dist/app/cli/port-holder.d.ts +16 -0
  30. package/dist/app/cli/port-holder.js +34 -0
  31. package/dist/app/cli/port.d.ts +37 -0
  32. package/dist/app/cli/port.js +116 -0
  33. package/dist/app/cli/sandbox.d.ts +25 -0
  34. package/dist/app/cli/sandbox.js +166 -0
  35. package/dist/app/cli/serve-log.d.ts +13 -0
  36. package/dist/app/cli/serve-log.js +90 -0
  37. package/dist/app/cli/serve.d.ts +18 -4
  38. package/dist/app/cli/serve.js +78 -8
  39. package/dist/app/cli/service-windows.d.ts +50 -0
  40. package/dist/app/cli/service-windows.js +117 -0
  41. package/dist/app/cli/service.d.ts +152 -0
  42. package/dist/app/cli/service.js +443 -0
  43. package/dist/app/cli/skeleton.d.ts +1 -1
  44. package/dist/app/cli/skeleton.js +13 -13
  45. package/dist/app/cli/skills.d.ts +5 -1
  46. package/dist/app/cli/skills.js +14 -4
  47. package/dist/app/cli/tray-icons.d.ts +11 -0
  48. package/dist/app/cli/tray-icons.js +87 -0
  49. package/dist/app/cli/tray-ids.d.ts +6 -0
  50. package/dist/app/cli/tray-ids.js +6 -0
  51. package/dist/app/cli/tray-mac.d.ts +8 -0
  52. package/dist/app/cli/tray-mac.js +140 -0
  53. package/dist/app/cli/tray-run.d.ts +1 -0
  54. package/dist/app/cli/tray-run.js +42 -0
  55. package/dist/app/cli/tray-windows.d.ts +5 -0
  56. package/dist/app/cli/tray-windows.js +166 -0
  57. package/dist/app/cli/tray.d.ts +236 -0
  58. package/dist/app/cli/tray.js +670 -0
  59. package/dist/app/cli/usage.d.ts +1 -1
  60. package/dist/app/cli/usage.js +11 -6
  61. package/dist/app/dictation/route.js +3 -2
  62. package/dist/app/http/app.d.ts +5 -10
  63. package/dist/app/http/app.js +47 -98
  64. package/dist/app/http/avatars.d.ts +17 -0
  65. package/dist/app/http/avatars.js +35 -0
  66. package/dist/app/http/dev-tutor-files.js +1 -1
  67. package/dist/app/http/parent-home.d.ts +14 -0
  68. package/dist/app/http/parent-home.js +69 -6
  69. package/dist/app/http/parent-look.d.ts +2 -0
  70. package/dist/app/http/parent-look.js +151 -0
  71. package/dist/app/http/raw-view.js +3 -3
  72. package/dist/app/http/voice-preview.d.ts +4 -0
  73. package/dist/app/http/voice-preview.js +34 -0
  74. package/dist/app/machine/bailian.d.ts +0 -2
  75. package/dist/app/machine/bailian.js +5 -6
  76. package/dist/app/machine/update.d.ts +25 -0
  77. package/dist/app/machine/update.js +88 -0
  78. package/dist/app/mock/mock-live.js +1 -1
  79. package/dist/app/mock/mock.d.ts +4 -2
  80. package/dist/app/mock/mock.js +20 -12
  81. package/dist/app/replay.js +4 -4
  82. package/dist/app/skills/home-doc.js +1 -1
  83. package/dist/app/workspace.d.ts +9 -4
  84. package/dist/app/workspace.js +33 -10
  85. package/dist/app/ws/route.d.ts +8 -2
  86. package/dist/app/ws/route.js +7 -3
  87. package/dist/board/parse.d.ts +0 -2
  88. package/dist/board/parse.js +0 -6
  89. package/dist/board/presentation.d.ts +9 -15
  90. package/dist/board/presentation.js +8 -10
  91. package/dist/brain/api-tools.js +2 -1
  92. package/dist/brain/api.d.ts +12 -17
  93. package/dist/brain/api.js +16 -33
  94. package/dist/brain/index.d.ts +23 -71
  95. package/dist/brain/index.js +6 -69
  96. package/dist/brain/log.d.ts +5 -0
  97. package/dist/brain/log.js +22 -0
  98. package/dist/brain/runtime.d.ts +30 -0
  99. package/dist/brain/runtime.js +23 -0
  100. package/dist/brain/{cli/stream-json.d.ts → stream-json.d.ts} +2 -4
  101. package/dist/brain/{cli/stream-json.js → stream-json.js} +0 -4
  102. package/dist/brain/stream.d.ts +5 -5
  103. package/dist/brain/stream.js +7 -7
  104. package/dist/brain/transcript.d.ts +3 -3
  105. package/dist/brain/transcript.js +6 -6
  106. package/dist/brain/{cli/types.d.ts → types.d.ts} +3 -48
  107. package/dist/brain/types.js +14 -0
  108. package/dist/chalk/glyphs/skeleton.d.ts +3 -3
  109. package/dist/chalk/glyphs/skeleton.js +1 -1
  110. package/dist/lib/index.d.ts +1 -1
  111. package/dist/lib/index.js +1 -1
  112. package/dist/memory/diary.js +3 -3
  113. package/dist/realtime/bailian.d.ts +1 -1
  114. package/dist/realtime/bailian.js +3 -2
  115. package/dist/realtime/preview.js +1 -1
  116. package/dist/realtime/voices.d.ts +2 -0
  117. package/dist/realtime/voices.js +57 -57
  118. package/dist/runtime/activities.d.ts +13 -1
  119. package/dist/runtime/activities.js +28 -2
  120. package/dist/runtime/activity-state.js +2 -2
  121. package/dist/runtime/agent-file.d.ts +3 -3
  122. package/dist/runtime/agent-file.js +3 -3
  123. package/dist/runtime/context/tutor-rules.d.ts +2 -2
  124. package/dist/runtime/context/tutor-rules.js +3 -3
  125. package/dist/runtime/home/home.d.ts +12 -1
  126. package/dist/runtime/home/home.js +8 -1
  127. package/dist/runtime/home/store.d.ts +1 -1
  128. package/dist/runtime/home/store.js +36 -21
  129. package/dist/runtime/journal/mix.d.ts +15 -6
  130. package/dist/runtime/journal/mix.js +60 -19
  131. package/dist/runtime/journal/recorder.d.ts +58 -13
  132. package/dist/runtime/journal/recorder.js +106 -16
  133. package/dist/runtime/journal/store.d.ts +4 -4
  134. package/dist/runtime/journal/store.js +6 -6
  135. package/dist/runtime/kid-input.js +2 -3
  136. package/dist/runtime/live/call.d.ts +12 -0
  137. package/dist/runtime/live/call.js +51 -18
  138. package/dist/runtime/live/turns.js +1 -1
  139. package/dist/runtime/runner.d.ts +9 -55
  140. package/dist/runtime/runner.js +52 -173
  141. package/dist/scenes/paint/painter.js +4 -3
  142. package/dist/schema/config.d.ts +43 -22
  143. package/dist/schema/config.js +45 -47
  144. package/dist/schema/conversation.d.ts +6 -0
  145. package/dist/schema/home.d.ts +11 -1
  146. package/dist/schema/home.js +7 -2
  147. package/dist/schema/workspace.d.ts +27 -0
  148. package/dist/schema/workspace.js +28 -0
  149. package/dist/shared/default-port.d.ts +2 -0
  150. package/dist/shared/default-port.js +2 -0
  151. package/dist/shared/qianwen.d.ts +14 -10
  152. package/dist/shared/qianwen.js +26 -22
  153. package/dist/shared/site-url.d.ts +2 -0
  154. package/dist/shared/site-url.js +5 -0
  155. package/dist/shared/upgrade.d.ts +9 -0
  156. package/dist/shared/upgrade.js +23 -0
  157. package/dist/ui/dev/dev-page.js +25 -252
  158. package/dist/ui/dev/dev-theme.d.ts +1 -1
  159. package/dist/ui/dev/dev-theme.js +1 -1
  160. package/dist/ui/dev/voice-gallery-page.js +1 -1
  161. package/dist/ui/kid/board-mark.d.ts +2 -2
  162. package/dist/ui/kid/board-mark.js +2 -2
  163. package/dist/ui/kid/home-tiles.d.ts +14 -0
  164. package/dist/ui/kid/home-tiles.js +44 -0
  165. package/dist/ui/kid/kid-page.js +68 -103
  166. package/dist/ui/kid/voice-ui.d.ts +1 -1
  167. package/dist/ui/kid/voice-ui.js +1 -1
  168. package/dist/ui/kid/word-emoji.js +5 -3
  169. package/dist/ui/parent/parent-home-page.js +337 -7
  170. package/dist/ui/parent/parent-page.js +6 -2
  171. package/dist/ui/parent/parent-settings-page.d.ts +1 -0
  172. package/dist/ui/parent/parent-settings-page.js +133 -0
  173. package/dist/ui/{dev → parent}/qr-page.d.ts +2 -2
  174. package/dist/ui/{dev → parent}/qr-page.js +19 -14
  175. package/dist/ui/shared/brand.d.ts +3 -1
  176. package/dist/ui/shared/brand.js +3 -1
  177. package/grades/grade-2/README.md +1 -1
  178. package/grades/grade-2/VALIDATION.md +1 -1
  179. package/grades/grade-2/agents/chinese-tutor/activities//345/244/215/344/271/240.md +1 -1
  180. package/grades/grade-2/agents/chinese-tutor/activities//346/{211/271/346/224/271.md → 243/200/346/237/245/344/275/234/344/270/232.md} +4 -4
  181. package/grades/grade-2/agents/chinese-tutor/activities//346/{211/271/346/224/271. → 243/200/346/237/245/344/275/234/344/270/232.}/344/276/213/345/255/220.md +2 -2
  182. package/grades/grade-2/agents/chinese-tutor/activities//351/242/204/344/271/240.md +1 -1
  183. package/grades/grade-2/agents/english-tutor/activities//345/244/215/344/271/240.md +1 -1
  184. package/grades/grade-2/agents/english-tutor/activities//346/{211/271/346/224/271.md → 243/200/346/237/245/344/275/234/344/270/232.md} +4 -4
  185. package/grades/grade-2/agents/english-tutor/activities//346/{211/271/346/224/271. → 243/200/346/237/245/344/275/234/344/270/232.}/344/276/213/345/255/220.md +2 -2
  186. package/grades/grade-2/agents/english-tutor/activities//351/242/204/344/271/240.md +1 -1
  187. package/grades/grade-2/agents/math-tutor/activities//345/244/215/344/271/240.md +1 -1
  188. package/grades/grade-2/agents/math-tutor/activities//346/{211/271/346/224/271.md → 243/200/346/237/245/344/275/234/344/270/232.md} +4 -4
  189. package/grades/grade-2/agents/math-tutor/activities//346/{211/271/346/224/271. → 243/200/346/237/245/344/275/234/344/270/232.}/344/276/213/345/255/220.md +2 -2
  190. package/grades/grade-2/agents/math-tutor/activities//351/242/204/344/271/240.md +1 -1
  191. package/package.json +6 -2
  192. package/skills/cotutor-analyze/references//345/221/275/344/273/244/344/270/216/346/226/207/344/273/266.md +2 -2
  193. package/skills/cotutor-board/references/photo.md +2 -2
  194. package/skills/cotutor-home/SKILL.md +1 -0
  195. package/skills/cotutor-home/references/tutor.md +2 -1
  196. package/skills/cotutor-home/references//351/246/226/351/241/265/350/257/255/346/263/225.md +2 -1
  197. package/skills/cotutor-tune/references//345/255/227/346/256/265.md +6 -3
  198. package/skills/cotutor-tutor/SKILL.md +5 -13
  199. package/dist/brain/cli/adapters.d.ts +0 -60
  200. package/dist/brain/cli/adapters.js +0 -91
  201. package/dist/brain/cli/qwen.d.ts +0 -11
  202. package/dist/brain/cli/qwen.js +0 -141
  203. package/dist/brain/cli/run-plan.d.ts +0 -59
  204. package/dist/brain/cli/run-plan.js +0 -71
  205. package/dist/brain/cli/run.d.ts +0 -35
  206. package/dist/brain/cli/run.js +0 -256
  207. package/dist/brain/cli/types.js +0 -3
  208. package/dist/brain/cli/warm.d.ts +0 -53
  209. package/dist/brain/cli/warm.js +0 -103
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Windows 防火墙放没放行 cotutor:平板连不上多半是它。Windows 只在第一次监听时弹一次「允许访问」,家长点了取消(或没看见)就再也连不上,
3
+ * cotutor 什么都不说(2026-10-31 实测:服务在本机回 200,从局域网超时;网络是「专用」,没有 node 的规则,也没有端口的规则)。
4
+ * 2026-11-06 实测(Windows 11 build 26300,普通权限的账户):node 第一次监听的同一秒,防火墙服务自己建好两条 Block 规则(名字带 Query User)再弹窗问;点「允许」才改成 Allow。
5
+ * 所以刚起来时查到「拦着」,可能只是弹窗还没回答,不一定点了取消。
6
+ * 用 PowerShell 的 NetSecurity 命令查(输出与系统语言无关;netsh 的输出是本地化的,中文 Windows 上解析不了);查不了(没有 PowerShell、超时)就不报,不瞎猜。
7
+ */
8
+ import { execFile } from 'node:child_process';
9
+ const PS_TIMEOUT_MS = 20_000;
10
+ /** 查一次的 PowerShell 脚本;node 的路径与端口写进去(单引号里的单引号写两个) */
11
+ export function firewallScript(node, port) {
12
+ const q = (s) => `'${s.replaceAll("'", "''")}'`;
13
+ return [
14
+ "$ErrorActionPreference = 'SilentlyContinue'",
15
+ `$node = ${q(node)}`,
16
+ `$port = ${q(String(port))}`,
17
+ '$nets = @(Get-NetConnectionProfile | ForEach-Object { [string]$_.NetworkCategory })',
18
+ "$on = @(Get-NetFirewallProfile | Where-Object { [string]$_.Enabled -eq 'True' } | ForEach-Object { [string]$_.Name })",
19
+ // 规则里的程序可能写成 %ProgramFiles%\…,也可能是小写(Windows 弹窗建的):展开了比,-eq 本来就不分大小写
20
+ '$byApp = @(Get-NetFirewallApplicationFilter | Where-Object { [Environment]::ExpandEnvironmentVariables([string]$_.Program) -eq $node } | Get-NetFirewallRule)',
21
+ '$byPort = @(Get-NetFirewallPortFilter -Protocol TCP | Where-Object { @($_.LocalPort) -contains $port } | Get-NetFirewallRule)',
22
+ "$live = { [string]$_.Direction -eq 'Inbound' -and [string]$_.Enabled -eq 'True' }",
23
+ "$allow = @(($byApp + $byPort) | Where-Object $live | Where-Object { [string]$_.Action -eq 'Allow' } | ForEach-Object { [string]$_.Profile })",
24
+ "$block = @($byApp | Where-Object $live | Where-Object { [string]$_.Action -eq 'Block' } | ForEach-Object { [string]$_.Profile })",
25
+ 'ConvertTo-Json -Compress -InputObject @{ networks = $nets; enabled = $on; allowed = $allow; blocked = $block }',
26
+ ].join('\n');
27
+ }
28
+ /** PowerShell 回的 JSON → 样子;一个元素的数组可能被拆成字符串,null 当空 */
29
+ export function parseFirewall(text) {
30
+ let raw;
31
+ try {
32
+ raw = JSON.parse(text.trim());
33
+ }
34
+ catch {
35
+ return null;
36
+ }
37
+ if (!raw || typeof raw !== 'object')
38
+ return null;
39
+ const list = (v) => (Array.isArray(v) ? v.filter((x) => typeof x === 'string') : typeof v === 'string' ? [v] : []);
40
+ const r = raw;
41
+ return { networks: list(r.networks), enabled: list(r.enabled), allowed: list(r.allowed), blocked: list(r.blocked) };
42
+ }
43
+ /** 平板连不连得进来,怎么修 */
44
+ export function judgeFirewall(s, port, node) {
45
+ const nets = [...new Set(s.networks.map((n) => (n === 'DomainAuthenticated' ? 'Domain' : n)))];
46
+ const covers = (rules, profile) => rules.some((r) => r.split(/,\s*/).some((p) => p === 'Any' || p === profile));
47
+ const guarded = nets.filter((n) => s.enabled.includes(n));
48
+ const allowCmd = `netsh advfirewall firewall add rule name="cotutor" dir=in action=allow protocol=TCP localport=${port} profile=private`;
49
+ if (!nets.length)
50
+ return { ok: true, detail: '没连上网络,查不了防火墙(连上 Wi-Fi 再跑一次 doctor)' };
51
+ if (!guarded.length)
52
+ return { ok: true, detail: `防火墙在连着的网络上关着(${nets.join('、')}),平板连得进来` };
53
+ if (guarded.some((n) => covers(s.blocked, n))) {
54
+ return {
55
+ ok: false,
56
+ detail: '防火墙拦着这个 node(第一次起服务时 Windows 先拦着、再弹窗问「允许访问」:还没回答,或者点了取消):平板连不上,这台电脑上照常',
57
+ fix: `屏幕上有「允许访问」的窗口就点「允许」;没有窗口(点过取消了):管理员终端里先删拦截规则:netsh advfirewall firewall delete rule name=all dir=in program="${node}";再放行:${allowCmd}`,
58
+ };
59
+ }
60
+ if (guarded.every((n) => covers(s.allowed, n)))
61
+ return { ok: true, detail: `防火墙放行了(端口 ${port} 或这个 node)` };
62
+ if (guarded.includes('Public') && !covers(s.allowed, 'Public')) {
63
+ return {
64
+ ok: false,
65
+ detail: '这台电脑连的网络是「公用」,防火墙不让平板连进来;这台电脑上照常',
66
+ fix: `设置 › 网络和 Internet › 这个网络的属性,改成「专用」;再在管理员终端里放行:${allowCmd}`,
67
+ };
68
+ }
69
+ return { ok: false, detail: `防火墙没放行端口 ${port}:平板连不上,这台电脑上照常`, fix: `管理员终端(右键「终端」选「以管理员身份运行」)里跑:${allowCmd}` };
70
+ }
71
+ /** 真查一次(只在 Windows 上);查不了是 null */
72
+ export function probeFirewall(port, node = process.execPath) {
73
+ const encoded = Buffer.from(firewallScript(node, port), 'utf16le').toString('base64');
74
+ return new Promise((resolve) => {
75
+ execFile('powershell.exe', ['-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-EncodedCommand', encoded], { timeout: PS_TIMEOUT_MS, windowsHide: true }, (err, stdout) => {
76
+ resolve(err ? null : parseFirewall(String(stdout)));
77
+ });
78
+ });
79
+ }
@@ -12,6 +12,7 @@ import { DEFAULT_GRADE, LEGACY_GRADE, requireGradePack } from "./grades.js";
12
12
  import { installActivities } from "./activities.js";
13
13
  import { installSkills, linkClaudeSkills } from "./skills.js";
14
14
  import { migrateLayout } from "./migrate.js";
15
+ import { choiceNote, choosePort, knownPorts } from "./port.js";
15
16
  import { installThemes } from "./themes.js";
16
17
  import { scanVault } from "../../memory/vault.js";
17
18
  import { CONFIG_FILE, ConfigError, UsageError, HOME_ROOT, USER_CONFIG, expandPath, loadWorkspace, parseConfig, readJson, redactHome } from "../workspace.js";
@@ -53,8 +54,10 @@ export async function initWorkspace(opts) {
53
54
  if (await exists(config))
54
55
  steps.push({ item: CONFIG_FILE, action: 'kept', note: '政策文件不覆盖(家长的决定)' });
55
56
  else {
56
- await writeFile(config, configTemplate({ slug: opts.slug, name: opts.name, port: opts.port, tutors: agents }));
57
- steps.push({ item: CONFIG_FILE, action: 'created' });
57
+ // 端口只在这里选一次,写进文件以后不变(port.ts);--port 给了就听它的
58
+ const chosen = opts.port === undefined ? await choosePort(knownPorts()) : null;
59
+ await writeFile(config, configTemplate({ slug: opts.slug, name: opts.name, port: opts.port ?? chosen?.port, tutors: agents }));
60
+ steps.push({ item: CONFIG_FILE, action: 'created', ...(chosen ? { note: choiceNote(chosen) } : {}) });
58
61
  }
59
62
  // 老师按 cotutor.json 里有谁走(家长自己加的也补目录与链);配置坏了就只管出厂的,doctor 去报
60
63
  let tutorNames = [];
@@ -147,9 +150,9 @@ export async function initWorkspace(opts) {
147
150
  if (!(await exists(join(root, '.git'))))
148
151
  suggestions.push('git init(建议:老师记忆、账本、政策都是不可再生状态,git 是破坏后的兜底)');
149
152
  suggestions.push('档案里填 school_start(读一年级的年月,如 2025-09),老师才知道现在是几年级哪个学期;想用自己的 Obsidian 库,改 cotutor.json 的 paths.vault');
150
- suggestions.push('cotutor serve 起服务,在这台电脑上打开家长端 /parent 的「首页」,填百炼的按量 API Key 与业务空间 ID(老师的嘴和脑都用它;没填孩子端只出「老师们还没到」)');
151
- suggestions.push('在 iPad / iPhone 上用:cotutor cert 签证书(要 mkcert),根证书装到设备上并打开信任,步骤见 README 里的《iPad 与 iPhone》');
152
- suggestions.push('可选:点读、单词卡的配音用百炼(和老师的嘴同一把 key),到工作台 /dev 的「音色」页给老师挑声音;cotutor pin 给家长端与工作台加锁');
153
+ suggestions.push('cotutor service install 装成后台服务(登录后自己起来、关了终端照样跑;在终端里调试用 cotutor serve),第一次装会打开家长端(以后 cotutor open),在「首页」填百炼的按量 API Key 与业务空间 ID(老师的嘴和脑都用它;没填孩子端只出「老师们还没到」)');
154
+ suggestions.push('在平板、手机上用:cotutor cert 签证书(要 mkcert),根证书装到设备上并打开信任,iPad / iPhone 的步骤见 README 里的《iPad 与 iPhone》');
155
+ suggestions.push('可选:点读、单词卡的配音用百炼(和老师的嘴同一把 key),到家长端首页点老师的「样子与声音」挑声音;cotutor pin 给家长端与工作台加锁');
153
156
  suggestions.push('cotutor doctor 逐项体检');
154
157
  return { root, steps, suggestions };
155
158
  }