@xiaobuyu/nodesign 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (282) hide show
  1. package/README.md +156 -106
  2. package/bin/nodesign.js +0 -0
  3. package/package.json +1 -1
  4. package/server/api/README.md +45 -45
  5. package/server/api/_guard.js +75 -75
  6. package/server/api/admin.js +187 -187
  7. package/server/api/assets/docx-page.js +103 -103
  8. package/server/api/assets/helpers.js +22 -22
  9. package/server/api/assets/notes.js +144 -144
  10. package/server/api/assets.js +896 -896
  11. package/server/api/board.js +57 -57
  12. package/server/api/browse.js +138 -138
  13. package/server/api/canvas.js +444 -444
  14. package/server/api/chatai.js +324 -324
  15. package/server/api/exports/build-standalone.js +980 -980
  16. package/server/api/exports/cards.js +118 -118
  17. package/server/api/exports/docx-pdf.js +46 -46
  18. package/server/api/exports/export-page.js +142 -142
  19. package/server/api/exports/handoff.js +147 -147
  20. package/server/api/exports.js +690 -690
  21. package/server/api/instruction.js +75 -75
  22. package/server/api/me.js +116 -116
  23. package/server/api/memory.js +185 -185
  24. package/server/api/pending-changes.js +366 -366
  25. package/server/api/plugins.js +164 -164
  26. package/server/api/projects.js +220 -220
  27. package/server/api/publish.js +64 -64
  28. package/server/api/recent.js +73 -73
  29. package/server/api/sessions.js +589 -589
  30. package/server/api/skills.js +77 -77
  31. package/server/api/standalone-fit.js +168 -168
  32. package/server/api/turn-compose.js +180 -180
  33. package/server/api/turn-inflight.js +44 -44
  34. package/server/auth/README.md +17 -17
  35. package/server/auth/middleware.js +138 -138
  36. package/server/auth/origin-guard.js +71 -71
  37. package/server/auth/session.js +108 -108
  38. package/server/auth/tier.js +113 -113
  39. package/server/auth/users-store.js +348 -348
  40. package/server/edit/README.md +29 -29
  41. package/server/engine/README.md +113 -113
  42. package/server/engine/agent/agent-shared.js +550 -550
  43. package/server/engine/agent/auto-mode-default-hard-deny.txt +4 -4
  44. package/server/engine/agent/auto-mode-rules.js +78 -78
  45. package/server/engine/agent/context.js +418 -418
  46. package/server/engine/agent/events.js +464 -464
  47. package/server/engine/agent/hooks/canvas-validate.js +199 -199
  48. package/server/engine/agent/hooks/failure.js +120 -120
  49. package/server/engine/agent/hooks/file-events.js +75 -75
  50. package/server/engine/agent/hooks/lifecycle.js +233 -233
  51. package/server/engine/agent/hooks/post-canvas-focus.js +80 -80
  52. package/server/engine/agent/hooks/post-guidance.js +206 -206
  53. package/server/engine/agent/hooks/post-subagent-report.js +104 -104
  54. package/server/engine/agent/hooks/post-trim.js +58 -58
  55. package/server/engine/agent/hooks/pre-board-neighborhood.js +34 -34
  56. package/server/engine/agent/hooks/pre-defaults.js +84 -84
  57. package/server/engine/agent/hooks/pre-injectors.js +277 -277
  58. package/server/engine/agent/hooks/pre-performance-log-guard.js +78 -78
  59. package/server/engine/agent/hooks/pre-starter-files.js +61 -61
  60. package/server/engine/agent/hooks/pre-workspace-scope-guard.js +78 -78
  61. package/server/engine/agent/hooks/site-validate.js +94 -94
  62. package/server/engine/agent/hooks/tool-prompts.js +31 -31
  63. package/server/engine/agent/hooks/turn-state-memory.js +56 -56
  64. package/server/engine/agent/hooks/user-prompt-submit.js +215 -215
  65. package/server/engine/agent/hooks.js +354 -354
  66. package/server/engine/agent/init-contract.js +69 -69
  67. package/server/engine/agent/isolation.js +159 -159
  68. package/server/engine/agent/model-table.js +292 -292
  69. package/server/engine/agent/plugin-loader.js +266 -266
  70. package/server/engine/agent/prompts/nodesign-prelude.md +336 -336
  71. package/server/engine/agent/prompts/tools/ask-user-question-protocol.md +71 -71
  72. package/server/engine/agent/prompts/tools/direct-edit-protocol.md +98 -98
  73. package/server/engine/agent/prompts/tools/generate-image-cookbook.gemini-gateway.md +583 -583
  74. package/server/engine/agent/prompts/tools/generate-image-cookbook.md +483 -483
  75. package/server/engine/agent/prompts/tools/hybrid-reference.md +100 -100
  76. package/server/engine/agent/prompts/tools/paint-still-cookbook.md +270 -270
  77. package/server/engine/agent/prompts/tools/roll-film-cookbook.md +70 -70
  78. package/server/engine/agent/prompts/tools/site-reference.md +106 -106
  79. package/server/engine/agent/prompts/tools/tweaks-syntax.md +125 -125
  80. package/server/engine/agent/prompts/tools/vision-checker-dispatch.md +48 -48
  81. package/server/engine/agent/session-loop.js +1024 -1024
  82. package/server/engine/agent/session-model.js +164 -164
  83. package/server/engine/agent/skill.js +204 -204
  84. package/server/engine/agent/task-events.js +78 -78
  85. package/server/engine/agents/ds-extractor.md +132 -132
  86. package/server/engine/agents/explorer.md +190 -190
  87. package/server/engine/agents/index.js +230 -230
  88. package/server/engine/agents/schemas/design-system.json +156 -156
  89. package/server/engine/agents/schemas/tweak-schema.json +113 -113
  90. package/server/engine/agents/tweak-proposer.md +127 -127
  91. package/server/engine/agents/vision-checker.md +254 -254
  92. package/server/engine/browse/capture.js +422 -422
  93. package/server/engine/browse/card.js +137 -137
  94. package/server/engine/browse/handover.js +59 -59
  95. package/server/engine/browse/page-digest.js +119 -119
  96. package/server/engine/browse/refs.js +196 -196
  97. package/server/engine/browse/registry.js +307 -307
  98. package/server/engine/browse/screencast.js +170 -170
  99. package/server/engine/browse/state.js +121 -121
  100. package/server/engine/chatai/chat-log.js +79 -79
  101. package/server/engine/chatai/index.js +193 -193
  102. package/server/engine/chatai/openai-compat.js +152 -152
  103. package/server/engine/chatai/orchestrate.js +309 -309
  104. package/server/engine/chatai/perform.js +40 -40
  105. package/server/engine/chatai/summarize.js +79 -79
  106. package/server/engine/mcp/capability-gate.js +52 -52
  107. package/server/engine/mcp/index.js +339 -339
  108. package/server/engine/mcp/param-sanitizer.js +142 -142
  109. package/server/engine/mcp/tools/arrange-on-board.js +124 -124
  110. package/server/engine/mcp/tools/artifact-session.js +278 -278
  111. package/server/engine/mcp/tools/browse-computer.js +421 -421
  112. package/server/engine/mcp/tools/browse-find-batch.js +183 -183
  113. package/server/engine/mcp/tools/browse-screenshot.js +157 -157
  114. package/server/engine/mcp/tools/browse.js +480 -480
  115. package/server/engine/mcp/tools/build-docx.js +101 -101
  116. package/server/engine/mcp/tools/clear-pending-changes.js +99 -99
  117. package/server/engine/mcp/tools/create-on-board.js +155 -155
  118. package/server/engine/mcp/tools/crystallize-skill.js +168 -168
  119. package/server/engine/mcp/tools/explain-style.js +237 -237
  120. package/server/engine/mcp/tools/export-handoff.js +133 -133
  121. package/server/engine/mcp/tools/expose-tweaks.js +149 -149
  122. package/server/engine/mcp/tools/generate-image.js +842 -842
  123. package/server/engine/mcp/tools/get-computed-styles.js +164 -164
  124. package/server/engine/mcp/tools/get-pending-changes.js +205 -205
  125. package/server/engine/mcp/tools/h3box-ssh.js +84 -84
  126. package/server/engine/mcp/tools/helpers/acquire-page.js +82 -82
  127. package/server/engine/mcp/tools/helpers/motion-lab.js +554 -554
  128. package/server/engine/mcp/tools/helpers/motion-scroll.js +112 -112
  129. package/server/engine/mcp/tools/helpers/perception-page.js +215 -215
  130. package/server/engine/mcp/tools/helpers/reference-download.js +103 -103
  131. package/server/engine/mcp/tools/helpers/rembg-bridge.py +80 -80
  132. package/server/engine/mcp/tools/helpers/shot-pipeline.js +280 -280
  133. package/server/engine/mcp/tools/highlight.js +68 -68
  134. package/server/engine/mcp/tools/list-pages.js +222 -222
  135. package/server/engine/mcp/tools/lookup-tags.js +59 -59
  136. package/server/engine/mcp/tools/navigate-to-page.js +60 -60
  137. package/server/engine/mcp/tools/organize-board.js +64 -64
  138. package/server/engine/mcp/tools/paint-still.js +434 -434
  139. package/server/engine/mcp/tools/pin-to-board.js +130 -130
  140. package/server/engine/mcp/tools/preview-deck.js +130 -130
  141. package/server/engine/mcp/tools/profile-scroll.js +305 -305
  142. package/server/engine/mcp/tools/publish-site.js +102 -102
  143. package/server/engine/mcp/tools/query-elements.js +195 -195
  144. package/server/engine/mcp/tools/read-board.js +103 -103
  145. package/server/engine/mcp/tools/read-document.js +93 -93
  146. package/server/engine/mcp/tools/read-page.js +264 -264
  147. package/server/engine/mcp/tools/read-tavern-json.js +143 -143
  148. package/server/engine/mcp/tools/record-decision.js +140 -140
  149. package/server/engine/mcp/tools/relate-on-board.js +110 -110
  150. package/server/engine/mcp/tools/report-issue.js +115 -115
  151. package/server/engine/mcp/tools/roll-film.js +266 -266
  152. package/server/engine/mcp/tools/screenshot-docx.js +138 -138
  153. package/server/engine/mcp/tools/screenshot-url.js +195 -195
  154. package/server/engine/mcp/tools/screenshot.js +583 -583
  155. package/server/engine/mcp/tools/tier-gate.js +96 -96
  156. package/server/engine/mcp/tools/trace-motion.js +215 -215
  157. package/server/engine/mcp/tools/web-search.js +548 -548
  158. package/server/engine/motion/inventory.js +349 -349
  159. package/server/engine/perception/session.js +235 -235
  160. package/server/engine/plugins/nodesign/.claude-plugin/plugin.json +5 -5
  161. package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/SKILL.md +192 -192
  162. package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/canvas.template.html +667 -667
  163. package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/hybrid-grid.md +22 -22
  164. package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/image-led-cover.md +25 -25
  165. package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/portrait.md +23 -23
  166. package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/quote-backdrop.md +21 -21
  167. package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/section-divider.md +23 -23
  168. package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/text-led.md +21 -21
  169. package/server/engine/plugins/nodesign/skills/docx-craft/SKILL.md +162 -162
  170. package/server/engine/plugins/nodesign/skills/docx-craft/references/token-schema.md +407 -407
  171. package/server/engine/plugins/nodesign/skills/docx-craft//346/226/207/346/241/243.template.json +80 -80
  172. package/server/engine/plugins/nodesign/skills/rp-craft/SKILL.md +228 -228
  173. package/server/engine/plugins/nodesign/skills/rp-craft/patterns//346/274/224/345/207/272/351/241/265-/346/234/200/345/260/217/345/256/236/347/216/260.html +78 -78
  174. package/server/engine/plugins/nodesign/skills/rp-craft//346/274/224/345/207/272.template.js +268 -268
  175. package/server/engine/plugins/nodesign/skills/site-craft/SKILL.md +323 -323
  176. package/server/engine/plugins/nodesign/skills/site-craft/patterns/build-lane.md +134 -134
  177. package/server/engine/plugins/nodesign/skills/site-craft/patterns/cutout-collage.md +61 -61
  178. package/server/engine/plugins/nodesign/skills/site-craft/patterns/mock-app.md +163 -163
  179. package/server/engine/plugins/nodesign/skills/site-craft/patterns/page-transitions.md +86 -86
  180. package/server/engine/runs/active-runs.js +698 -698
  181. package/server/engine/runs/live-turn.js +252 -252
  182. package/server/engine/runs/store.js +364 -364
  183. package/server/engine/runs/turn-relay.js +217 -217
  184. package/server/engine/runtime/workspace.js +164 -164
  185. package/server/lib/artifact-file-path.js +80 -80
  186. package/server/lib/artifact-target.js +284 -284
  187. package/server/lib/asset-refs.js +239 -239
  188. package/server/lib/async-queue.js +104 -104
  189. package/server/lib/auto-relations.js +130 -130
  190. package/server/lib/binding-types.js +60 -60
  191. package/server/lib/board-kind-sizes.js +62 -62
  192. package/server/lib/board-relations.js +133 -133
  193. package/server/lib/browse-proxy.js +180 -180
  194. package/server/lib/canvas-id.js +36 -36
  195. package/server/lib/cover.js +227 -227
  196. package/server/lib/danbooru-tags.js +291 -291
  197. package/server/lib/doc-extract.js +156 -156
  198. package/server/lib/docx/build-from-source.js +260 -260
  199. package/server/lib/docx/build.js +490 -490
  200. package/server/lib/docx/dump-styles.js +287 -287
  201. package/server/lib/docx/fonts/nodesign-cjk.conf +164 -164
  202. package/server/lib/docx/merge-runs.js +129 -129
  203. package/server/lib/docx/numbering.js +218 -218
  204. package/server/lib/docx/order.js +152 -152
  205. package/server/lib/docx/rawzip.js +172 -172
  206. package/server/lib/docx/render.js +92 -92
  207. package/server/lib/docx/text-lint.js +225 -225
  208. package/server/lib/docx/tokens.js +335 -335
  209. package/server/lib/docx/units.js +29 -29
  210. package/server/lib/docx/xml.js +291 -291
  211. package/server/lib/docx-pages.js +187 -187
  212. package/server/lib/export-collect.js +289 -289
  213. package/server/lib/export-package.js +210 -210
  214. package/server/lib/html-srcset.js +145 -145
  215. package/server/lib/image-variant.js +460 -460
  216. package/server/lib/ingress/forward-openai-chat.js +305 -305
  217. package/server/lib/ingress/openai-chat.js +491 -491
  218. package/server/lib/ingress/session-notice.js +66 -66
  219. package/server/lib/ingress/session-routes.js +68 -68
  220. package/server/lib/ingress/slot-probe.js +130 -130
  221. package/server/lib/ingress/upstream-billing.js +52 -52
  222. package/server/lib/ingress/upstream-fail-streak.js +72 -72
  223. package/server/lib/ingress/upstream-truncation.js +48 -48
  224. package/server/lib/issues-store.js +182 -182
  225. package/server/lib/kinds/deck.js +72 -72
  226. package/server/lib/kinds/docx.js +283 -283
  227. package/server/lib/kinds/file-kinds.js +113 -113
  228. package/server/lib/kinds/index.js +202 -202
  229. package/server/lib/kinds/site.js +161 -161
  230. package/server/lib/model-ingress.js +543 -543
  231. package/server/lib/moderation.js +255 -255
  232. package/server/lib/notice-store.js +103 -103
  233. package/server/lib/plugin-install.js +104 -104
  234. package/server/lib/plugin-validator.js +721 -721
  235. package/server/lib/publish-store.js +115 -115
  236. package/server/lib/quick-summary.js +52 -52
  237. package/server/lib/quota.js +270 -270
  238. package/server/lib/rate-window.js +29 -29
  239. package/server/lib/reference-assets.js +92 -92
  240. package/server/lib/region-shot.js +135 -135
  241. package/server/lib/safe-path.js +73 -73
  242. package/server/lib/sdk-session.js +33 -33
  243. package/server/lib/showcase-store.js +92 -92
  244. package/server/lib/site-publish.js +465 -465
  245. package/server/lib/ssrf-guard.js +432 -432
  246. package/server/lib/task-scan.js +158 -158
  247. package/server/lib/tavern-json.js +154 -154
  248. package/server/lib/video-variant.js +237 -237
  249. package/server/lib/workspace-path.js +33 -33
  250. package/server/ops/fix-sdk-musl.mjs +40 -40
  251. package/server/ops/install-macos-fonts.sh +114 -114
  252. package/server/ops/macos-fonts.conf +336 -336
  253. package/server/ops/sandbox-shim/bwrap +57 -57
  254. package/server/projects/assets-summary.js +119 -119
  255. package/server/projects/auto-name.js +53 -53
  256. package/server/projects/board-store.js +640 -640
  257. package/server/projects/move-entry.js +103 -103
  258. package/server/projects/store.js +270 -270
  259. package/server/projects/ui-config.js +54 -54
  260. package/server/projects/workspace-templates.js +53 -53
  261. package/server/projects/workspace.js +1025 -1025
  262. package/server/runtime/local-config.js +177 -177
  263. package/server/runtime/profile.js +72 -72
  264. package/server/services/rembg-service.py +334 -334
  265. package/server/shared/README.md +25 -25
  266. package/server/shared/deck.js +54 -54
  267. package/server/shared/time.js +49 -49
  268. package/server/style-pipeline/README.md +53 -53
  269. package/server/ws/broker.js +36 -36
  270. package/server/ws/browse-channel.js +177 -177
  271. package/server/ws/index.js +386 -386
  272. package/web/README.md +89 -89
  273. package/web/dist/assets/BrowserWindow-BYCgizHw.js +11 -0
  274. package/web/dist/assets/{DeckWindow-4G0xX7P3.js → DeckWindow-pNydEM4L.js} +1 -1
  275. package/web/dist/assets/{DocxWindow-BXQsoPkh.js → DocxWindow-PPgzh3Gq.js} +1 -1
  276. package/web/dist/assets/{SiteWindow-D6eS-2gM.js → SiteWindow-DM0LQPCO.js} +1 -1
  277. package/web/dist/assets/{index-Cbl3ATvp.js → index-BHTBJlpY.js} +32 -32
  278. package/web/dist/assets/{pending-edit-apply-DTjo2geW.js → pending-edit-apply-C0I13CbD.js} +1 -1
  279. package/web/dist/assets/{rotate-cw-LMT3x1Zj.js → rotate-cw-B1UQ8Hld.js} +1 -1
  280. package/web/dist/assets/{square-dashed-mouse-pointer-CBoAkfhi.js → square-dashed-mouse-pointer-8lrgqmfv.js} +1 -1
  281. package/web/dist/index.html +17 -17
  282. package/web/dist/assets/BrowserWindow-Dc7J7CYc.js +0 -11
@@ -1,292 +1,292 @@
1
- /**
2
- * server/engine/agent/model-table.js — 内置模型表与上游注册表(**只放数据,不放逻辑**)。
3
- *
4
- * 08-22 从 model-context.js 拆出来:那边顶在 600 行棘轮上,而本地分发版要在内置行之外合并用户
5
- * 自己的插槽(runtime/local-config.js)。派生索引、断言、路由查表、picker 闸门全留在 model-context.js,
6
- * 它 import 这两张表再与外部行合并成 UPSTREAMS / MODELS。改行仍然只改这里;加一家 brand 也在这里。
7
- *
8
- * 字段说明见 model-context.js 文件头(两条通路 / spoofing / 记账)。
9
- */
10
-
11
- /**
12
- * API 上游注册表。keyEnv 是 env 变量名(真钥匙在 .env,不进代码不进 git)。
13
- * authStyle:'x-api-key'(Anthropic 原生头)| 'bearer'(Authorization: Bearer)。
14
- * countTokens:上游有没有 /v1/messages/count_tokens。false = 入口直接本地估算;
15
- * true = 先转发,404 再回退本地(capability 探针缓存见 model-ingress.js)。
16
- */
17
- export const UPSTREAMS_BUILTIN = Object.freeze({
18
- lament: Object.freeze({
19
- label: '中转站 api.lament0.link',
20
- baseUrl: 'https://api.lament0.link',
21
- keyEnv: 'NODESIGN_UPSTREAM_LAMENT_KEY',
22
- authStyle: 'x-api-key',
23
- countTokens: false, // 08-19 探针:404
24
- }),
25
- // 本地盒子(featurize 租的 5090 跑 llama-server,SSH 隧道 -L 到本机)。
26
- // llama.cpp 2025-11-28 起原生带 /v1/messages(含 count_tokens、SSE、tool_use、
27
- // vision;工具调用要 --jinja)—— 不需要任何协议转换层。authStyle 'none':
28
- // llama-server 无鉴权,隧道只绑环回。箱子不开机时请求 ECONNREFUSED → 502,
29
- // fail-loud 语义正确。
30
- qwenLocal: Object.freeze({
31
- label: '本地 llama-server(SSH 隧道)',
32
- baseUrl: process.env.NODESIGN_UPSTREAM_QWEN_LOCAL_URL || 'http://127.0.0.1:8080',
33
- keyEnv: null,
34
- authStyle: 'none',
35
- countTokens: true,
36
- // ⚠️ llama.cpp 的图片解码走 stb_image,**它不认 webp**。而本站 turn-compose 的
37
- // 白名单是放 webp 进来的(封面和截图链路正是产 webp)。解不开时 mtmd 会顺序
38
- // 兜底 image → audio → video,最后那条要 ffprobe,盒上没装,于是上游返回的是
39
- // 一句看不出真因的 400「Failed to load image or audio file」——
40
- // 08-19 生产真撞过两次,日志里翻到 mtmd_helper 才定位到。
41
- //
42
- // 声明成"这个上游真解得开什么",入口负责把不在表里的转码过去(见
43
- // model-ingress.normalizeImages)。不填 = 什么都能吃,中转站那两个上游维持原样。
44
- imageFormats: Object.freeze(['image/png', 'image/jpeg']),
45
- }),
46
- // OpenCode Zen(08-21):免费 stealth 模型 Ox Alpha 只有 OpenAI chat 格式能用工具
47
- // (它的 /v1/messages 桥一带 tools 就 [1210],四种写法探死)。protocol 'openai-chat'
48
- // 让 ingress 走 lib/ingress/openai-chat.js 的协议转换而不是透传;其余上游没有这个
49
- // 字段 = 透传 Anthropic。钥匙在 .env(NODESIGN_UPSTREAM_ZEN_KEY)。
50
- zen: Object.freeze({
51
- label: 'OpenCode Zen',
52
- // 覆盖旋钮跟 qwenLocal 同款,**只给探针用**(假上游跑真 ingress+转换层,见 _probe-truncation-e2e.mjs);
53
- // 生产 .env 里不设 → 真地址。
54
- baseUrl: process.env.NODESIGN_UPSTREAM_ZEN_URL || 'https://opencode.ai/zen/v1',
55
- keyEnv: 'NODESIGN_UPSTREAM_ZEN_KEY',
56
- authStyle: 'bearer',
57
- protocol: 'openai-chat',
58
- countTokens: false, // 08-21 探针:404
59
- }),
60
- // 08-21 晚:Zen 第二入口 /zen/go(= OpenCode Go 订阅入口)。同钥匙、目录不同(Ox 叫 ox-alpha-free,x-preview-f-free 它回 401);
61
- // 响应带 `cost`(流式在 [DONE] 之后补 {"choices":[],"cost":"…"})与 cached_tokens → lib/ingress/upstream-billing.js。'zen' 留着可切回
62
- zenGo: Object.freeze({
63
- label: 'OpenCode Zen Go',
64
- baseUrl: process.env.NODESIGN_UPSTREAM_ZEN_GO_URL || 'https://opencode.ai/zen/go/v1', // 探针覆盖,同上
65
- keyEnv: 'NODESIGN_UPSTREAM_ZEN_KEY',
66
- authStyle: 'bearer',
67
- protocol: 'openai-chat',
68
- countTokens: false,
69
- }),
70
- });
71
-
72
- /**
73
- * 模型总表。字段:
74
- * id appModel —— 全站唯一标识(session-config / NODESIGN_MODEL / 计量落表都用它)
75
- * window 真实 context window(ContextUsageBar 分母 + hooks 警告分档)
76
- * select 出现在前端 picker 的 {label, desc};没有 = 不对用户暴露
77
- * api API 通路配置(没有 = 订阅通路):
78
- * upstream UPSTREAMS 的 key
79
- * wireModel 发给上游的真模型名(入口出口替换)
80
- * sdkAlias 喂 SDK 的 spoof 名(必须是 SDK 认识的 Claude 名;⚠️全表唯一)
81
- * fastModel 该路的 helper/subagent 模型(必须也是本表可路由的 id;
82
- * 订阅的 haiku 在 API 模式不可用 —— binary 见 API key 即弃 OAuth,
83
- * helper 请求同样走唯一的 BASE_URL)
84
- * thinking 'strip'(出口删 thinking 字段,上游自决)| 'enabled8k'
85
- * (出口把 adaptive 改写成 enabled+budget 8192,Kimi 实测需要)
86
- * liftImages tool_result 里的图提升到 user message 顶层(Kimi 与 Gemini 桥
87
- * 都丢 tool_result 图,08-19 探针实锤 + 修法验证)
88
- * prices 每 1M token 的 USD {input, output, cacheRead, cacheWrite};
89
- * 没填 = 沿用 SDK 按 alias 算的虚价(接真流量前先填)
90
- */
91
-
92
- /**
93
- * 模型出自谁家 —— 前端据此画身份标(picker 图标 / 画布精灵 / 舞台徽记)。
94
- *
95
- * **声明,不推断**:不许前端按 id 前缀猜(`/^claude-/` 那种),下一个模型名一变就全错。
96
- * 每行必须写 brand,加载时断言(下面的派生循环),拼错当场炸。
97
- * 新增一家 = 这里加一个名字 + 前端 ui/ModelMark.jsx 加一枚标;两边由
98
- * web/src/components/ui/ModelMark.lint.test.js 对账(它直接读本文件的 BRANDS)。
99
- *
100
- * 口径(08-21 用户拍板):有自己标的用自己的(deepseek 蓝鲸、gemini 星),
101
- * **隐身/神秘的免费行一律用供应商 OpenCode 的方块标**(Ox 这类不公开身份的模型)。
102
- */
103
- // 'custom':本地分发版用户自己配的插槽(runtime/local-config.js)没填 brand 时的默认牌子,前端用通用标
104
- export const BRANDS = Object.freeze(['claude', 'deepseek', 'opencode', 'gemini', 'qwen', 'custom']);
105
-
106
- export const MODELS_BUILTIN = Object.freeze([
107
- // ── 订阅通路(Claude 真名,零注入)──
108
- {
109
- id: 'claude-sonnet-5[1m]', window: 1_000_000, brand: 'claude',
110
- select: { label: 'Sonnet 5', desc: '快 · 日常改稿和铺页够用', gate: 'subscription' },
111
- },
112
- {
113
- id: 'claude-opus-5[1m]', window: 1_000_000, brand: 'claude',
114
- select: { label: 'Opus 5', desc: '前端与审美更强 · 烧订阅额度快得多,重活再开', gate: 'subscription' },
115
- },
116
- { id: 'claude-sonnet-5', window: 200_000, brand: 'claude' },
117
- { id: 'claude-opus-5', window: 200_000, brand: 'claude' },
118
- { id: 'claude-opus-4-7[1m]', window: 1_000_000, brand: 'claude' },
119
- { id: 'claude-sonnet-4-6[1m]', window: 1_000_000, brand: 'claude' },
120
- { id: 'claude-opus-4-7', window: 200_000, brand: 'claude' },
121
- { id: 'claude-sonnet-4-6', window: 200_000, brand: 'claude' },
122
- { id: 'claude-haiku-4-5', window: 200_000, brand: 'claude' },
123
- // 只当 alias 用的订阅名(08-20):SDK 二进制认识的 1M 名里还空着的一个(strings 扫过:
124
- // opus-4-6/4-7/4-8/5、sonnet-4-5-20250929/4-6/5 七个 [1m]),给 gemini-3.7-flash 行做 spoof。
125
- { id: 'claude-opus-4-6[1m]', window: 1_000_000, brand: 'claude' },
126
- // 同上,给 ox-alpha 行做 spoof(08-21)
127
- { id: 'claude-opus-4-8[1m]', window: 1_000_000, brand: 'claude' },
128
- // 同上,七个里最后一个空着的,给 ox-alpha-max 行做 spoof(08-21 晚)
129
- { id: 'claude-sonnet-4-5-20250929[1m]', window: 1_000_000, brand: 'claude' },
130
- // alias 池现状(08-21 深夜清槽后):opus-4-6[1m]→gemini-3.7-flash、opus-4-7[1m]→deepseek-v4-flash-vision、opus-4-8[1m]→ox-alpha、
131
- // opus-5[1m]→qwen、sonnet-4-5-20250929[1m]→ox-alpha-max、haiku-4-5→ox-alpha-helper;**sonnet-4-6[1m] 空着备用**;sonnet-5[1m] 是订阅默认行不许被路由
132
-
133
- // ── API 通路 ──
134
- // kimi-k2.6 行与 moonshot 上游 08-21 深夜清掉(用户:「把 kimi 3.1pro 的槽都清理一下」):NoDesk 退役后没走过流量,
135
- // 它的 alias claude-opus-4-7[1m] 转给 deepseek-v4-flash-vision。'enabled8k' 的 thinking 档逻辑留在 transformForUpstream 里备用。
136
- // 本地 Qwen(HauhauCS/Qwen3.8-27B-Uncensored-…-Aggressive-MTP-GGUF,底座官方
137
- // Qwen3.8-27B,有视觉)。⚠️ window 必须跟箱子 llama-server 的 -c 一致:低了
138
- // 会在 SDK 触发 auto-compact 之前先撞上游 400。262144 = 该模型原生上限
139
- // (YaRN 可外推到 1M,但那要额外开 rope 参数且短上下文质量有代价,不默认走)。
140
- // alias 用 1M 档:SDK 按 alias 查 rawMaxTokens,用 200k 名会让 auto-compact 在
141
- // ~180k 就触发,白扔 80k。⚠️ 这个 alias 同时是线上可选的订阅模型名,安全性靠两点
142
- // (改动前先确认它们还成立):①订阅会话根本不进 ingress,WIRE_LOOKUP 只服务
143
- // API 会话;②repriceUsageDeltas 先看会话通路,订阅会话原样早退不 remap。
144
- {
145
- // window 必须等于盒上 llama-server 启动日志里的 `n_ctx_slot`(每槽上下文),低了 SDK
146
- // 在 auto-compact 之前先撞上游 400。08-20 起盒子是 RTX 5090 32G:OrcaRouter Q5_K_M +
147
- // 视觉 + MTP 投机 + 1 槽 × 131072,再留 ~5G 给同卡的 ComfyUI(noobai)。换回 96G 盒子
148
- // 就是 262_144 × 3 槽。盒上配置住 ops/qwen-box/(serve-prod.sh),两边要一起改。
149
- id: 'qwen3.8-27b', window: 131_072, brand: 'qwen',
150
- // ⏸ **08-20 用户拍板从 picker 摘牌**(盒子按小时租,已关机)。删掉 `select` 一处,
151
- // 三个消费方一起拒:GET /api/me/models 的清单、PUT /model 的校验、turn.js 的
152
- // body.model 校验(都走 selectableModelsFor —— 所以摘牌不会留后门)。
153
- // **线路原样留着**:下面 api 字段一个字没动,WIRE_LOOKUP / resolveSessionWire /
154
- // 记账 reprice 全照旧;已经钉在 qwen 的老会话仍会路由过去,盒子没开就 502 fail-loud
155
- // (这是设计,不是 bug)。同理 gemini-3.1-pro 那行也是「留行不留牌」,先例在下面。
156
- // 复牌 = 把这一行放回来,别的都不用动:
157
- // select: { label: 'Qwen3.8 27B(本地)', desc: '本地盒子 · 无审查 · 盒子没开时不可用', gate: 'localGen' },
158
- // ⚠️ 复牌时别丢 `gate: 'localGen'` —— 跟 roll_film / paint_still 同一套批准制
159
- // (admin 免批),它本来就跑在同一台本地盒子上,语义天然一致;没这个闸就是对
160
- // 所有账号露出一个「一按就 502」的按钮。
161
- // 无审查权重跑在自己租的盒子上(不出网、零成本、只对获批账号开)。这条路上
162
- // prelude 的整节「底线」不注入 —— 站主 08-19 拍板,理由是那节是**平台对外
163
- // 开放**才需要的产物政策(产物能一键挂到站主域名下),而这台盒子上跑的是
164
- // 个人写作/角色扮演,那节只会让模型对正常输入畏手畏脚。
165
- //
166
- // 标记位住在表里而不是写成 `if (model === 'qwen3.8-27b')`:它是**模型属性**,
167
- // 跟 gate / prices 同级。散在 session-loop 里就是给这张表开第二个真相源,
168
- // 这个仓库为「同一件东西有多个实例」付过最贵的学费。以后再接一个无审查模型
169
- // 只加这一个字段,一行逻辑都不用动。
170
- uncensored: true,
171
- // ⭐ **盒上 llama-server 的 `-np`(slot 数)应当等于 `NODESIGN_MAX_CONCURRENT_RUNS`。**
172
- // slot 比闸多 → 白占显存(每路一份满窗 KV)
173
- // slot 比闸少 → 请求在 llama-server 里排队,而 Nodesign 以为自己还有余量,
174
- // 用户看到的是无解释的慢,不是「现在有点挤」那句诚实的 BUSY
175
- // 08-19 的 96G 盒子两边都是 3(巧合不是设计);**08-20 起的 5090 32G 盒子是 `-np 1`
176
- // 而闸仍是 3 —— 已知走偏**,出路是按模型给 maxConcurrent(⏸ 未拍板),在那之前
177
- // 第 2 个 qwen 请求就是在盒上排队。盒上脚本在 ops/qwen-box/(serve.sh=96G,
178
- // serve-prod.sh=5090),改任何一边都要改另一边;这条契约没法用 lint 拦 ——
179
- // `server/lib/_ingress-check.mjs` 第 6 项会真查 /slots 比对,换机后跑一次。
180
- api: {
181
- upstream: 'qwenLocal', wireModel: 'qwen3.8-27b',
182
- sdkAlias: 'claude-opus-5[1m]',
183
- fastModel: 'qwen3.8-27b',
184
- thinking: 'enabled8k',
185
- // ⭐ 08-19 盒上体检 9/9:llama.cpp 的 /v1/messages **原生直通 tool_result 图片**
186
- // (中转站 Gemini 桥正是死在这一项)。原样直通比提升到顶层更忠实,故关掉 lift。
187
- liftImages: false,
188
- prices: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, // 本地盒子按租金付费,token 记 0(不然按 opus-5 虚价记账)
189
- },
190
- },
191
- // gemini-3.1-pro 行(中转-gemini-3.1-pro-preview,alias claude-sonnet-4-6[1m])08-21 深夜清掉:退了 picker 后只做体检对照,
192
- // 对照改用 3.7 Flash 行;sonnet-4-6[1m] 这个 alias 名腾出来备用。中转站 thinking 参数零效果的结论见 08-20 记录。
193
- {
194
- id: 'gemini-3.7-flash', window: 1_000_000, brand: 'gemini',
195
- // 08-20 用户拍板:要 3.7 Flash,先用中转站 + lift shim 顶着。它只在中转站的「反重力-」
196
- // 通道上有(转卖 Antigravity OAuth 额度),今天体检 6/9:文本/视觉/非流式 tool_use/
197
- // prompt cache 真命中(cache_read 8162)都好;流式 stop_reason 恒=end_turn(假上游实验证明
198
- // CLI 认块不认 stop_reason,无功能后果);tool_result 图丢靠 liftImages 修。⛔硬伤是
199
- // 「当前无可用凭证」500 说来就来、不分请求大小、一来就是整段时间 —— 所以同 qwen 走
200
- // localGen 闸,label 写明不稳定,只给自己人。思考档在模型名里(-high/-medium/-low),
201
- // 选 high 即"默认高";thinking 参数照旧 strip。
202
- select: { label: 'Gemini 3.7 Flash(中转)', desc: '反重力通道 · 随时可能 500 · 思考档 high', gate: 'localGen' },
203
- api: {
204
- upstream: 'lament', wireModel: '反重力-流式抗截断/gemini-3.7-flash-high',
205
- sdkAlias: 'claude-opus-4-6[1m]', // 3.7 Flash 真 1M 窗口,alias 诚实;见上面那行订阅名的注释
206
- fastModel: 'gemini-3.7-flash',
207
- thinking: 'strip',
208
- liftImages: true,
209
- // 官方促销价(2027-01-01 起翻倍 $1.5/$7.5);缓存命中按输入价一折。中转站计量单位不明,
210
- // 这里的 USD 仍是配额/展示用的近似。
211
- prices: { input: 0.75, output: 3.75, cacheRead: 0.075, cacheWrite: 0 },
212
- },
213
- },
214
- // ── OpenCode Go · DeepSeek V4 Flash Vision Exp(08-21 深夜,第一条付费行)── /zen/go = OpenCode Go 订阅($10/月换 $12/5h·$30/周·$60/月):
215
- // 额度内上游 cost 报 0、余额不扣 → 记账按**表价**(高峰价;北京 09-12/14-18 是高峰)让每用户日限跟 Go 池子一起受控,cost>0 以上游为准
216
- // (context.applyUpstreamBilling)。探针:文本/图(webp)/工具/流式全通,首字 ~450ms,reasoning_effort 收;DeepSeek ZDR。先 gate localGen 试跑,过关改 'subscription'
217
- {
218
- // 真窗口 1M;用户 08-21 深夜拍板压缩窗口 272k(省钱:携带成本 ≈ 1M 的 1/4、缓存失手最坏 $0.12/轮;近 14 天 649 回合只压缩过 11 次)
219
- id: 'deepseek-v4-flash-vision', window: 272_000, brand: 'deepseek',
220
- // 08-21 深夜开闸给所有档(含 basic):basic 的 $5/天日限 + 表价记账管着它;pro/admin 不限
221
- select: { label: 'DeepSeek V4 Flash · 视觉', desc: '快 · 有视觉 · 272k 上下文 · 按用量计入每日额度(高峰 $0.44/$1.32 缓存 $0.014)' },
222
- api: {
223
- upstream: 'zenGo', wireModel: 'deepseek-v4-flash-vision-exp',
224
- sdkAlias: 'claude-opus-4-7[1m]', // kimi 退役腾出来的 1M 名;窗口由 CLAUDE_CODE_AUTO_COMPACT_WINDOW=272k 钉住
225
- fastModel: 'ox-alpha-helper', // 一句话的活仍走免费 Ox helper
226
- thinking: 'strip',
227
- reasoningEffort: 'high',
228
- maxOutput: 128_000,
229
- prices: { input: 0.44, output: 1.32, cacheRead: 0.014, cacheWrite: 0 },
230
- },
231
- },
232
- // ── OpenCode Zen · Ox Alpha(08-21,用户称「大事」的第一块)──
233
- // 真 id x-preview-f-free(models.dev name "Ox Alpha Free";/models 列表里没有带 ox 的名,别按名猜)。
234
- // 1M ctx / 131k out / 图+视频 / 工具 / reasoning effort low|high|max / 价 0;官方「free for
235
- // the next week」(08-20 公告)+ zero-retention 不训练。大概率 GLM 系 stealth(错误码体系 +
236
- // 社区指纹)。⚠️ 一周后要么变付费 GLM 要么下架 —— 这行是插件,闸和转换层才是耐久资产。
237
- // 上线顺序:先 gate localGen 给 admin 试跑真任务,过关再开闸并设为全员默认。
238
- {
239
- id: 'ox-alpha', window: 1_000_000, brand: 'opencode',
240
- // 08-21 经营态拍板:全员默认模型(default: true),公开注册号只能用它这类免费行。
241
- select: { label: 'Ox Alpha(免费)', desc: '限时免费 · 1M 上下文 · 有视觉 · 人人可用 · 思考档 high', default: true },
242
- api: {
243
- upstream: 'zenGo', wireModel: 'ox-alpha-free', // 08-21 晚切 /zen/go 入口(主入口名 x-preview-f-free,upstream 'zen')
244
- emptyRetries: 6, retryBudgetMs: 360_000, // 就地重发放宽,理由见 resolveWireModel 的 emptyRetries 注释
245
- sdkAlias: 'claude-opus-4-8[1m]',
246
- fastModel: 'ox-alpha-helper', // helper 走独立行才分得出 role(会话 env SMALL_FAST_MODEL 发的是 app id)
247
- thinking: 'strip', // 出口不带 Anthropic thinking 字段;转换层按 reasoningEffort 发 reasoning_effort
248
- // Ox 三档 low|high|max。08-21 小题实测 reasoning_tokens:不传≈27、low=0、high=3、max=27;
249
- // 但上生产后 'max' 在真会话里想了 28,930 字 / 4 分 20 秒才出第一个字(用户看到的是
250
- // "只有绿点没有回复")。改 'high':还想,但不把一轮耗在想上
251
- reasoningEffort: 'high',
252
- maxOutput: 131_072,
253
- // 不设 liftImages:openai-chat 转换层本身就把 tool_result 里的图搬进随后的 user 消息
254
- // (OpenAI 的 tool 角色消息装不下图,上游放了会挂死 120s)。同一件事只留一条路。
255
- prices: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
256
- },
257
- },
258
- {
259
- // 同一个 Ox,思考档 max:真会话里 max 想过 28,930 字 / 4 分 20 秒才出首字,所以不做默认、
260
- // 单独一行给愿意等的人。两行 protocol 同为 openai-chat,会话中途互切不触发 LANE_SWITCH
261
- id: 'ox-alpha-max', window: 1_000_000, brand: 'opencode',
262
- select: { label: 'Ox Alpha · 深想(免费)', desc: '同一个 Ox · 思考档 max · 想得久,首字可能等几分钟 · 重活再开' },
263
- api: {
264
- upstream: 'zenGo', wireModel: 'ox-alpha-free', // 08-21 晚切 /zen/go 入口(主入口名 x-preview-f-free,upstream 'zen')
265
- emptyRetries: 6, retryBudgetMs: 360_000, // 就地重发放宽,理由见 resolveWireModel 的 emptyRetries 注释
266
- sdkAlias: 'claude-sonnet-4-5-20250929[1m]',
267
- fastModel: 'ox-alpha-helper', // helper 一句话的活用 low 档那行,不跟着深想
268
- thinking: 'strip',
269
- reasoningEffort: 'max',
270
- maxOutput: 131_072,
271
- prices: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
272
- },
273
- },
274
- {
275
- // Ox 的 helper 行(08-21 晚):不进 picker(无 select),只做两个 Ox 主行的 fastModel。
276
- // 为什么要单独一行:session-loop 给 CLI 的 ANTHROPIC_SMALL_FAST_MODEL 是 app id,
277
- // ox-alpha 主行自己当 fast 时 helper 请求和主请求同名,ingress 分不出 role、helper
278
- // 也跟着 high 想。独立行 + alias haiku(表内空着的订阅名,helper 不需要 1M 窗)。
279
- id: 'ox-alpha-helper', window: 1_000_000, brand: 'opencode',
280
- api: {
281
- upstream: 'zenGo', wireModel: 'ox-alpha-free', // 08-21 晚切 /zen/go 入口(主入口名 x-preview-f-free,upstream 'zen')
282
- // helper 不放宽重发(走全局默认 2 次):它就是标题/分类器一句话的活,
283
- // 接不上无所谓,多打几发只是白占上游并发。
284
- sdkAlias: 'claude-haiku-4-5',
285
- fastModel: 'ox-alpha-helper',
286
- thinking: 'strip',
287
- reasoningEffort: 'low',
288
- maxOutput: 131_072,
289
- prices: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
290
- },
291
- },
292
- ]);
1
+ /**
2
+ * server/engine/agent/model-table.js — 内置模型表与上游注册表(**只放数据,不放逻辑**)。
3
+ *
4
+ * 08-22 从 model-context.js 拆出来:那边顶在 600 行棘轮上,而本地分发版要在内置行之外合并用户
5
+ * 自己的插槽(runtime/local-config.js)。派生索引、断言、路由查表、picker 闸门全留在 model-context.js,
6
+ * 它 import 这两张表再与外部行合并成 UPSTREAMS / MODELS。改行仍然只改这里;加一家 brand 也在这里。
7
+ *
8
+ * 字段说明见 model-context.js 文件头(两条通路 / spoofing / 记账)。
9
+ */
10
+
11
+ /**
12
+ * API 上游注册表。keyEnv 是 env 变量名(真钥匙在 .env,不进代码不进 git)。
13
+ * authStyle:'x-api-key'(Anthropic 原生头)| 'bearer'(Authorization: Bearer)。
14
+ * countTokens:上游有没有 /v1/messages/count_tokens。false = 入口直接本地估算;
15
+ * true = 先转发,404 再回退本地(capability 探针缓存见 model-ingress.js)。
16
+ */
17
+ export const UPSTREAMS_BUILTIN = Object.freeze({
18
+ lament: Object.freeze({
19
+ label: '中转站 api.lament0.link',
20
+ baseUrl: 'https://api.lament0.link',
21
+ keyEnv: 'NODESIGN_UPSTREAM_LAMENT_KEY',
22
+ authStyle: 'x-api-key',
23
+ countTokens: false, // 08-19 探针:404
24
+ }),
25
+ // 本地盒子(featurize 租的 5090 跑 llama-server,SSH 隧道 -L 到本机)。
26
+ // llama.cpp 2025-11-28 起原生带 /v1/messages(含 count_tokens、SSE、tool_use、
27
+ // vision;工具调用要 --jinja)—— 不需要任何协议转换层。authStyle 'none':
28
+ // llama-server 无鉴权,隧道只绑环回。箱子不开机时请求 ECONNREFUSED → 502,
29
+ // fail-loud 语义正确。
30
+ qwenLocal: Object.freeze({
31
+ label: '本地 llama-server(SSH 隧道)',
32
+ baseUrl: process.env.NODESIGN_UPSTREAM_QWEN_LOCAL_URL || 'http://127.0.0.1:8080',
33
+ keyEnv: null,
34
+ authStyle: 'none',
35
+ countTokens: true,
36
+ // ⚠️ llama.cpp 的图片解码走 stb_image,**它不认 webp**。而本站 turn-compose 的
37
+ // 白名单是放 webp 进来的(封面和截图链路正是产 webp)。解不开时 mtmd 会顺序
38
+ // 兜底 image → audio → video,最后那条要 ffprobe,盒上没装,于是上游返回的是
39
+ // 一句看不出真因的 400「Failed to load image or audio file」——
40
+ // 08-19 生产真撞过两次,日志里翻到 mtmd_helper 才定位到。
41
+ //
42
+ // 声明成"这个上游真解得开什么",入口负责把不在表里的转码过去(见
43
+ // model-ingress.normalizeImages)。不填 = 什么都能吃,中转站那两个上游维持原样。
44
+ imageFormats: Object.freeze(['image/png', 'image/jpeg']),
45
+ }),
46
+ // OpenCode Zen(08-21):免费 stealth 模型 Ox Alpha 只有 OpenAI chat 格式能用工具
47
+ // (它的 /v1/messages 桥一带 tools 就 [1210],四种写法探死)。protocol 'openai-chat'
48
+ // 让 ingress 走 lib/ingress/openai-chat.js 的协议转换而不是透传;其余上游没有这个
49
+ // 字段 = 透传 Anthropic。钥匙在 .env(NODESIGN_UPSTREAM_ZEN_KEY)。
50
+ zen: Object.freeze({
51
+ label: 'OpenCode Zen',
52
+ // 覆盖旋钮跟 qwenLocal 同款,**只给探针用**(假上游跑真 ingress+转换层,见 _probe-truncation-e2e.mjs);
53
+ // 生产 .env 里不设 → 真地址。
54
+ baseUrl: process.env.NODESIGN_UPSTREAM_ZEN_URL || 'https://opencode.ai/zen/v1',
55
+ keyEnv: 'NODESIGN_UPSTREAM_ZEN_KEY',
56
+ authStyle: 'bearer',
57
+ protocol: 'openai-chat',
58
+ countTokens: false, // 08-21 探针:404
59
+ }),
60
+ // 08-21 晚:Zen 第二入口 /zen/go(= OpenCode Go 订阅入口)。同钥匙、目录不同(Ox 叫 ox-alpha-free,x-preview-f-free 它回 401);
61
+ // 响应带 `cost`(流式在 [DONE] 之后补 {"choices":[],"cost":"…"})与 cached_tokens → lib/ingress/upstream-billing.js。'zen' 留着可切回
62
+ zenGo: Object.freeze({
63
+ label: 'OpenCode Zen Go',
64
+ baseUrl: process.env.NODESIGN_UPSTREAM_ZEN_GO_URL || 'https://opencode.ai/zen/go/v1', // 探针覆盖,同上
65
+ keyEnv: 'NODESIGN_UPSTREAM_ZEN_KEY',
66
+ authStyle: 'bearer',
67
+ protocol: 'openai-chat',
68
+ countTokens: false,
69
+ }),
70
+ });
71
+
72
+ /**
73
+ * 模型总表。字段:
74
+ * id appModel —— 全站唯一标识(session-config / NODESIGN_MODEL / 计量落表都用它)
75
+ * window 真实 context window(ContextUsageBar 分母 + hooks 警告分档)
76
+ * select 出现在前端 picker 的 {label, desc};没有 = 不对用户暴露
77
+ * api API 通路配置(没有 = 订阅通路):
78
+ * upstream UPSTREAMS 的 key
79
+ * wireModel 发给上游的真模型名(入口出口替换)
80
+ * sdkAlias 喂 SDK 的 spoof 名(必须是 SDK 认识的 Claude 名;⚠️全表唯一)
81
+ * fastModel 该路的 helper/subagent 模型(必须也是本表可路由的 id;
82
+ * 订阅的 haiku 在 API 模式不可用 —— binary 见 API key 即弃 OAuth,
83
+ * helper 请求同样走唯一的 BASE_URL)
84
+ * thinking 'strip'(出口删 thinking 字段,上游自决)| 'enabled8k'
85
+ * (出口把 adaptive 改写成 enabled+budget 8192,Kimi 实测需要)
86
+ * liftImages tool_result 里的图提升到 user message 顶层(Kimi 与 Gemini 桥
87
+ * 都丢 tool_result 图,08-19 探针实锤 + 修法验证)
88
+ * prices 每 1M token 的 USD {input, output, cacheRead, cacheWrite};
89
+ * 没填 = 沿用 SDK 按 alias 算的虚价(接真流量前先填)
90
+ */
91
+
92
+ /**
93
+ * 模型出自谁家 —— 前端据此画身份标(picker 图标 / 画布精灵 / 舞台徽记)。
94
+ *
95
+ * **声明,不推断**:不许前端按 id 前缀猜(`/^claude-/` 那种),下一个模型名一变就全错。
96
+ * 每行必须写 brand,加载时断言(下面的派生循环),拼错当场炸。
97
+ * 新增一家 = 这里加一个名字 + 前端 ui/ModelMark.jsx 加一枚标;两边由
98
+ * web/src/components/ui/ModelMark.lint.test.js 对账(它直接读本文件的 BRANDS)。
99
+ *
100
+ * 口径(08-21 用户拍板):有自己标的用自己的(deepseek 蓝鲸、gemini 星),
101
+ * **隐身/神秘的免费行一律用供应商 OpenCode 的方块标**(Ox 这类不公开身份的模型)。
102
+ */
103
+ // 'custom':本地分发版用户自己配的插槽(runtime/local-config.js)没填 brand 时的默认牌子,前端用通用标
104
+ export const BRANDS = Object.freeze(['claude', 'deepseek', 'opencode', 'gemini', 'qwen', 'custom']);
105
+
106
+ export const MODELS_BUILTIN = Object.freeze([
107
+ // ── 订阅通路(Claude 真名,零注入)──
108
+ {
109
+ id: 'claude-sonnet-5[1m]', window: 1_000_000, brand: 'claude',
110
+ select: { label: 'Sonnet 5', desc: '快 · 日常改稿和铺页够用', gate: 'subscription' },
111
+ },
112
+ {
113
+ id: 'claude-opus-5[1m]', window: 1_000_000, brand: 'claude',
114
+ select: { label: 'Opus 5', desc: '前端与审美更强 · 烧订阅额度快得多,重活再开', gate: 'subscription' },
115
+ },
116
+ { id: 'claude-sonnet-5', window: 200_000, brand: 'claude' },
117
+ { id: 'claude-opus-5', window: 200_000, brand: 'claude' },
118
+ { id: 'claude-opus-4-7[1m]', window: 1_000_000, brand: 'claude' },
119
+ { id: 'claude-sonnet-4-6[1m]', window: 1_000_000, brand: 'claude' },
120
+ { id: 'claude-opus-4-7', window: 200_000, brand: 'claude' },
121
+ { id: 'claude-sonnet-4-6', window: 200_000, brand: 'claude' },
122
+ { id: 'claude-haiku-4-5', window: 200_000, brand: 'claude' },
123
+ // 只当 alias 用的订阅名(08-20):SDK 二进制认识的 1M 名里还空着的一个(strings 扫过:
124
+ // opus-4-6/4-7/4-8/5、sonnet-4-5-20250929/4-6/5 七个 [1m]),给 gemini-3.7-flash 行做 spoof。
125
+ { id: 'claude-opus-4-6[1m]', window: 1_000_000, brand: 'claude' },
126
+ // 同上,给 ox-alpha 行做 spoof(08-21)
127
+ { id: 'claude-opus-4-8[1m]', window: 1_000_000, brand: 'claude' },
128
+ // 同上,七个里最后一个空着的,给 ox-alpha-max 行做 spoof(08-21 晚)
129
+ { id: 'claude-sonnet-4-5-20250929[1m]', window: 1_000_000, brand: 'claude' },
130
+ // alias 池现状(08-21 深夜清槽后):opus-4-6[1m]→gemini-3.7-flash、opus-4-7[1m]→deepseek-v4-flash-vision、opus-4-8[1m]→ox-alpha、
131
+ // opus-5[1m]→qwen、sonnet-4-5-20250929[1m]→ox-alpha-max、haiku-4-5→ox-alpha-helper;**sonnet-4-6[1m] 空着备用**;sonnet-5[1m] 是订阅默认行不许被路由
132
+
133
+ // ── API 通路 ──
134
+ // kimi-k2.6 行与 moonshot 上游 08-21 深夜清掉(用户:「把 kimi 3.1pro 的槽都清理一下」):NoDesk 退役后没走过流量,
135
+ // 它的 alias claude-opus-4-7[1m] 转给 deepseek-v4-flash-vision。'enabled8k' 的 thinking 档逻辑留在 transformForUpstream 里备用。
136
+ // 本地 Qwen(HauhauCS/Qwen3.8-27B-Uncensored-…-Aggressive-MTP-GGUF,底座官方
137
+ // Qwen3.8-27B,有视觉)。⚠️ window 必须跟箱子 llama-server 的 -c 一致:低了
138
+ // 会在 SDK 触发 auto-compact 之前先撞上游 400。262144 = 该模型原生上限
139
+ // (YaRN 可外推到 1M,但那要额外开 rope 参数且短上下文质量有代价,不默认走)。
140
+ // alias 用 1M 档:SDK 按 alias 查 rawMaxTokens,用 200k 名会让 auto-compact 在
141
+ // ~180k 就触发,白扔 80k。⚠️ 这个 alias 同时是线上可选的订阅模型名,安全性靠两点
142
+ // (改动前先确认它们还成立):①订阅会话根本不进 ingress,WIRE_LOOKUP 只服务
143
+ // API 会话;②repriceUsageDeltas 先看会话通路,订阅会话原样早退不 remap。
144
+ {
145
+ // window 必须等于盒上 llama-server 启动日志里的 `n_ctx_slot`(每槽上下文),低了 SDK
146
+ // 在 auto-compact 之前先撞上游 400。08-20 起盒子是 RTX 5090 32G:OrcaRouter Q5_K_M +
147
+ // 视觉 + MTP 投机 + 1 槽 × 131072,再留 ~5G 给同卡的 ComfyUI(noobai)。换回 96G 盒子
148
+ // 就是 262_144 × 3 槽。盒上配置住 ops/qwen-box/(serve-prod.sh),两边要一起改。
149
+ id: 'qwen3.8-27b', window: 131_072, brand: 'qwen',
150
+ // ⏸ **08-20 用户拍板从 picker 摘牌**(盒子按小时租,已关机)。删掉 `select` 一处,
151
+ // 三个消费方一起拒:GET /api/me/models 的清单、PUT /model 的校验、turn.js 的
152
+ // body.model 校验(都走 selectableModelsFor —— 所以摘牌不会留后门)。
153
+ // **线路原样留着**:下面 api 字段一个字没动,WIRE_LOOKUP / resolveSessionWire /
154
+ // 记账 reprice 全照旧;已经钉在 qwen 的老会话仍会路由过去,盒子没开就 502 fail-loud
155
+ // (这是设计,不是 bug)。同理 gemini-3.1-pro 那行也是「留行不留牌」,先例在下面。
156
+ // 复牌 = 把这一行放回来,别的都不用动:
157
+ // select: { label: 'Qwen3.8 27B(本地)', desc: '本地盒子 · 无审查 · 盒子没开时不可用', gate: 'localGen' },
158
+ // ⚠️ 复牌时别丢 `gate: 'localGen'` —— 跟 roll_film / paint_still 同一套批准制
159
+ // (admin 免批),它本来就跑在同一台本地盒子上,语义天然一致;没这个闸就是对
160
+ // 所有账号露出一个「一按就 502」的按钮。
161
+ // 无审查权重跑在自己租的盒子上(不出网、零成本、只对获批账号开)。这条路上
162
+ // prelude 的整节「底线」不注入 —— 站主 08-19 拍板,理由是那节是**平台对外
163
+ // 开放**才需要的产物政策(产物能一键挂到站主域名下),而这台盒子上跑的是
164
+ // 个人写作/角色扮演,那节只会让模型对正常输入畏手畏脚。
165
+ //
166
+ // 标记位住在表里而不是写成 `if (model === 'qwen3.8-27b')`:它是**模型属性**,
167
+ // 跟 gate / prices 同级。散在 session-loop 里就是给这张表开第二个真相源,
168
+ // 这个仓库为「同一件东西有多个实例」付过最贵的学费。以后再接一个无审查模型
169
+ // 只加这一个字段,一行逻辑都不用动。
170
+ uncensored: true,
171
+ // ⭐ **盒上 llama-server 的 `-np`(slot 数)应当等于 `NODESIGN_MAX_CONCURRENT_RUNS`。**
172
+ // slot 比闸多 → 白占显存(每路一份满窗 KV)
173
+ // slot 比闸少 → 请求在 llama-server 里排队,而 Nodesign 以为自己还有余量,
174
+ // 用户看到的是无解释的慢,不是「现在有点挤」那句诚实的 BUSY
175
+ // 08-19 的 96G 盒子两边都是 3(巧合不是设计);**08-20 起的 5090 32G 盒子是 `-np 1`
176
+ // 而闸仍是 3 —— 已知走偏**,出路是按模型给 maxConcurrent(⏸ 未拍板),在那之前
177
+ // 第 2 个 qwen 请求就是在盒上排队。盒上脚本在 ops/qwen-box/(serve.sh=96G,
178
+ // serve-prod.sh=5090),改任何一边都要改另一边;这条契约没法用 lint 拦 ——
179
+ // `server/lib/_ingress-check.mjs` 第 6 项会真查 /slots 比对,换机后跑一次。
180
+ api: {
181
+ upstream: 'qwenLocal', wireModel: 'qwen3.8-27b',
182
+ sdkAlias: 'claude-opus-5[1m]',
183
+ fastModel: 'qwen3.8-27b',
184
+ thinking: 'enabled8k',
185
+ // ⭐ 08-19 盒上体检 9/9:llama.cpp 的 /v1/messages **原生直通 tool_result 图片**
186
+ // (中转站 Gemini 桥正是死在这一项)。原样直通比提升到顶层更忠实,故关掉 lift。
187
+ liftImages: false,
188
+ prices: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, // 本地盒子按租金付费,token 记 0(不然按 opus-5 虚价记账)
189
+ },
190
+ },
191
+ // gemini-3.1-pro 行(中转-gemini-3.1-pro-preview,alias claude-sonnet-4-6[1m])08-21 深夜清掉:退了 picker 后只做体检对照,
192
+ // 对照改用 3.7 Flash 行;sonnet-4-6[1m] 这个 alias 名腾出来备用。中转站 thinking 参数零效果的结论见 08-20 记录。
193
+ {
194
+ id: 'gemini-3.7-flash', window: 1_000_000, brand: 'gemini',
195
+ // 08-20 用户拍板:要 3.7 Flash,先用中转站 + lift shim 顶着。它只在中转站的「反重力-」
196
+ // 通道上有(转卖 Antigravity OAuth 额度),今天体检 6/9:文本/视觉/非流式 tool_use/
197
+ // prompt cache 真命中(cache_read 8162)都好;流式 stop_reason 恒=end_turn(假上游实验证明
198
+ // CLI 认块不认 stop_reason,无功能后果);tool_result 图丢靠 liftImages 修。⛔硬伤是
199
+ // 「当前无可用凭证」500 说来就来、不分请求大小、一来就是整段时间 —— 所以同 qwen 走
200
+ // localGen 闸,label 写明不稳定,只给自己人。思考档在模型名里(-high/-medium/-low),
201
+ // 选 high 即"默认高";thinking 参数照旧 strip。
202
+ select: { label: 'Gemini 3.7 Flash(中转)', desc: '反重力通道 · 随时可能 500 · 思考档 high', gate: 'localGen' },
203
+ api: {
204
+ upstream: 'lament', wireModel: '反重力-流式抗截断/gemini-3.7-flash-high',
205
+ sdkAlias: 'claude-opus-4-6[1m]', // 3.7 Flash 真 1M 窗口,alias 诚实;见上面那行订阅名的注释
206
+ fastModel: 'gemini-3.7-flash',
207
+ thinking: 'strip',
208
+ liftImages: true,
209
+ // 官方促销价(2027-01-01 起翻倍 $1.5/$7.5);缓存命中按输入价一折。中转站计量单位不明,
210
+ // 这里的 USD 仍是配额/展示用的近似。
211
+ prices: { input: 0.75, output: 3.75, cacheRead: 0.075, cacheWrite: 0 },
212
+ },
213
+ },
214
+ // ── OpenCode Go · DeepSeek V4 Flash Vision Exp(08-21 深夜,第一条付费行)── /zen/go = OpenCode Go 订阅($10/月换 $12/5h·$30/周·$60/月):
215
+ // 额度内上游 cost 报 0、余额不扣 → 记账按**表价**(高峰价;北京 09-12/14-18 是高峰)让每用户日限跟 Go 池子一起受控,cost>0 以上游为准
216
+ // (context.applyUpstreamBilling)。探针:文本/图(webp)/工具/流式全通,首字 ~450ms,reasoning_effort 收;DeepSeek ZDR。先 gate localGen 试跑,过关改 'subscription'
217
+ {
218
+ // 真窗口 1M;用户 08-21 深夜拍板压缩窗口 272k(省钱:携带成本 ≈ 1M 的 1/4、缓存失手最坏 $0.12/轮;近 14 天 649 回合只压缩过 11 次)
219
+ id: 'deepseek-v4-flash-vision', window: 272_000, brand: 'deepseek',
220
+ // 08-21 深夜开闸给所有档(含 basic):basic 的 $5/天日限 + 表价记账管着它;pro/admin 不限
221
+ select: { label: 'DeepSeek V4 Flash · 视觉', desc: '快 · 有视觉 · 272k 上下文 · 按用量计入每日额度(高峰 $0.44/$1.32 缓存 $0.014)' },
222
+ api: {
223
+ upstream: 'zenGo', wireModel: 'deepseek-v4-flash-vision-exp',
224
+ sdkAlias: 'claude-opus-4-7[1m]', // kimi 退役腾出来的 1M 名;窗口由 CLAUDE_CODE_AUTO_COMPACT_WINDOW=272k 钉住
225
+ fastModel: 'ox-alpha-helper', // 一句话的活仍走免费 Ox helper
226
+ thinking: 'strip',
227
+ reasoningEffort: 'high',
228
+ maxOutput: 128_000,
229
+ prices: { input: 0.44, output: 1.32, cacheRead: 0.014, cacheWrite: 0 },
230
+ },
231
+ },
232
+ // ── OpenCode Zen · Ox Alpha(08-21,用户称「大事」的第一块)──
233
+ // 真 id x-preview-f-free(models.dev name "Ox Alpha Free";/models 列表里没有带 ox 的名,别按名猜)。
234
+ // 1M ctx / 131k out / 图+视频 / 工具 / reasoning effort low|high|max / 价 0;官方「free for
235
+ // the next week」(08-20 公告)+ zero-retention 不训练。大概率 GLM 系 stealth(错误码体系 +
236
+ // 社区指纹)。⚠️ 一周后要么变付费 GLM 要么下架 —— 这行是插件,闸和转换层才是耐久资产。
237
+ // 上线顺序:先 gate localGen 给 admin 试跑真任务,过关再开闸并设为全员默认。
238
+ {
239
+ id: 'ox-alpha', window: 1_000_000, brand: 'opencode',
240
+ // 08-21 经营态拍板:全员默认模型(default: true),公开注册号只能用它这类免费行。
241
+ select: { label: 'Ox Alpha(免费)', desc: '限时免费 · 1M 上下文 · 有视觉 · 人人可用 · 思考档 high', default: true },
242
+ api: {
243
+ upstream: 'zenGo', wireModel: 'ox-alpha-free', // 08-21 晚切 /zen/go 入口(主入口名 x-preview-f-free,upstream 'zen')
244
+ emptyRetries: 6, retryBudgetMs: 360_000, // 就地重发放宽,理由见 resolveWireModel 的 emptyRetries 注释
245
+ sdkAlias: 'claude-opus-4-8[1m]',
246
+ fastModel: 'ox-alpha-helper', // helper 走独立行才分得出 role(会话 env SMALL_FAST_MODEL 发的是 app id)
247
+ thinking: 'strip', // 出口不带 Anthropic thinking 字段;转换层按 reasoningEffort 发 reasoning_effort
248
+ // Ox 三档 low|high|max。08-21 小题实测 reasoning_tokens:不传≈27、low=0、high=3、max=27;
249
+ // 但上生产后 'max' 在真会话里想了 28,930 字 / 4 分 20 秒才出第一个字(用户看到的是
250
+ // "只有绿点没有回复")。改 'high':还想,但不把一轮耗在想上
251
+ reasoningEffort: 'high',
252
+ maxOutput: 131_072,
253
+ // 不设 liftImages:openai-chat 转换层本身就把 tool_result 里的图搬进随后的 user 消息
254
+ // (OpenAI 的 tool 角色消息装不下图,上游放了会挂死 120s)。同一件事只留一条路。
255
+ prices: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
256
+ },
257
+ },
258
+ {
259
+ // 同一个 Ox,思考档 max:真会话里 max 想过 28,930 字 / 4 分 20 秒才出首字,所以不做默认、
260
+ // 单独一行给愿意等的人。两行 protocol 同为 openai-chat,会话中途互切不触发 LANE_SWITCH
261
+ id: 'ox-alpha-max', window: 1_000_000, brand: 'opencode',
262
+ select: { label: 'Ox Alpha · 深想(免费)', desc: '同一个 Ox · 思考档 max · 想得久,首字可能等几分钟 · 重活再开' },
263
+ api: {
264
+ upstream: 'zenGo', wireModel: 'ox-alpha-free', // 08-21 晚切 /zen/go 入口(主入口名 x-preview-f-free,upstream 'zen')
265
+ emptyRetries: 6, retryBudgetMs: 360_000, // 就地重发放宽,理由见 resolveWireModel 的 emptyRetries 注释
266
+ sdkAlias: 'claude-sonnet-4-5-20250929[1m]',
267
+ fastModel: 'ox-alpha-helper', // helper 一句话的活用 low 档那行,不跟着深想
268
+ thinking: 'strip',
269
+ reasoningEffort: 'max',
270
+ maxOutput: 131_072,
271
+ prices: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
272
+ },
273
+ },
274
+ {
275
+ // Ox 的 helper 行(08-21 晚):不进 picker(无 select),只做两个 Ox 主行的 fastModel。
276
+ // 为什么要单独一行:session-loop 给 CLI 的 ANTHROPIC_SMALL_FAST_MODEL 是 app id,
277
+ // ox-alpha 主行自己当 fast 时 helper 请求和主请求同名,ingress 分不出 role、helper
278
+ // 也跟着 high 想。独立行 + alias haiku(表内空着的订阅名,helper 不需要 1M 窗)。
279
+ id: 'ox-alpha-helper', window: 1_000_000, brand: 'opencode',
280
+ api: {
281
+ upstream: 'zenGo', wireModel: 'ox-alpha-free', // 08-21 晚切 /zen/go 入口(主入口名 x-preview-f-free,upstream 'zen')
282
+ // helper 不放宽重发(走全局默认 2 次):它就是标题/分类器一句话的活,
283
+ // 接不上无所谓,多打几发只是白占上游并发。
284
+ sdkAlias: 'claude-haiku-4-5',
285
+ fastModel: 'ox-alpha-helper',
286
+ thinking: 'strip',
287
+ reasoningEffort: 'low',
288
+ maxOutput: 131_072,
289
+ prices: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
290
+ },
291
+ },
292
+ ]);