cortico 0.1.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.
Files changed (311) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +47 -0
  3. package/package.json +43 -0
  4. package/src/boot.ts +46 -0
  5. package/src/bot.ts +1342 -0
  6. package/src/config-file.ts +41 -0
  7. package/src/core/README.md +92 -0
  8. package/src/core/billing.ts +26 -0
  9. package/src/core/blobs.ts +106 -0
  10. package/src/core/bus.ts +311 -0
  11. package/src/core/config-schema.ts +201 -0
  12. package/src/core/config.ts +262 -0
  13. package/src/core/core.ts +699 -0
  14. package/src/core/cost.ts +244 -0
  15. package/src/core/event-store.ts +304 -0
  16. package/src/core/fork.ts +113 -0
  17. package/src/core/generation.ts +118 -0
  18. package/src/core/instance-lock.ts +215 -0
  19. package/src/core/ipc-logger.ts +105 -0
  20. package/src/core/language.ts +62 -0
  21. package/src/core/log-context.ts +38 -0
  22. package/src/core/loop.ts +1858 -0
  23. package/src/core/markers.ts +48 -0
  24. package/src/core/prefix.ts +121 -0
  25. package/src/core/run.ts +95 -0
  26. package/src/core/secrets.ts +18 -0
  27. package/src/core/session.ts +56 -0
  28. package/src/core/sessions.ts +261 -0
  29. package/src/core/state.ts +104 -0
  30. package/src/core/template.ts +72 -0
  31. package/src/core/timers.ts +129 -0
  32. package/src/core/tool-log.ts +135 -0
  33. package/src/core/transcript.ts +92 -0
  34. package/src/core/truncate.ts +121 -0
  35. package/src/core/types.ts +1140 -0
  36. package/src/core/usage-log.ts +79 -0
  37. package/src/core/util.ts +386 -0
  38. package/src/deploy-listing.ts +160 -0
  39. package/src/deploy.ts +162 -0
  40. package/src/extensions/README.md +72 -0
  41. package/src/extensions/dry-mount.ts +390 -0
  42. package/src/extensions/manifest.ts +153 -0
  43. package/src/extensions/runtime.ts +42 -0
  44. package/src/extensions.ts +519 -0
  45. package/src/launcher.ts +285 -0
  46. package/src/paths.ts +155 -0
  47. package/src/protocol/open-responses/LICENSE +201 -0
  48. package/src/protocol/open-responses/README.md +14 -0
  49. package/src/protocol/open-responses/context-helpers.ts +27 -0
  50. package/src/protocol/open-responses/context-log.ts +59 -0
  51. package/src/protocol/open-responses/context.ts +89 -0
  52. package/src/protocol/open-responses/generated.ts +115 -0
  53. package/src/protocol/open-responses/index.ts +33 -0
  54. package/src/protocol/open-responses/openapi.json +4230 -0
  55. package/src/protocol/open-responses/stream.ts +146 -0
  56. package/src/protocol/open-responses/tokens.ts +31 -0
  57. package/src/providers/README.md +86 -0
  58. package/src/providers/base.ts +111 -0
  59. package/src/providers/configuration.ts +97 -0
  60. package/src/providers/console/config.ts +21 -0
  61. package/src/providers/console/settings.ts +443 -0
  62. package/src/providers/console/strings.ts +74 -0
  63. package/src/providers/console/types.ts +11 -0
  64. package/src/providers/llamacpp/archive.ts +148 -0
  65. package/src/providers/llamacpp/catalog.ts +247 -0
  66. package/src/providers/llamacpp/config.ts +36 -0
  67. package/src/providers/llamacpp/console/client.ts +6 -0
  68. package/src/providers/llamacpp/console/models-panel.ts +120 -0
  69. package/src/providers/llamacpp/console/runtime-panel.ts +173 -0
  70. package/src/providers/llamacpp/console/server.ts +153 -0
  71. package/src/providers/llamacpp/index.ts +95 -0
  72. package/src/providers/llamacpp/native.ts +51 -0
  73. package/src/providers/llamacpp/options.ts +117 -0
  74. package/src/providers/llamacpp/runtime-store.ts +139 -0
  75. package/src/providers/llamacpp/runtime.ts +214 -0
  76. package/src/providers/llamacpp/server.ts +286 -0
  77. package/src/providers/llamacpp/strings.ts +230 -0
  78. package/src/providers/openai-responses-compat/config.ts +20 -0
  79. package/src/providers/openai-responses-compat/index.ts +89 -0
  80. package/src/providers/openai-responses-compat/native.ts +127 -0
  81. package/src/providers/openai-responses-compat/strings.ts +19 -0
  82. package/src/providers/pricebook.ts +75 -0
  83. package/src/providers/registry.ts +137 -0
  84. package/src/providers/strings.ts +71 -0
  85. package/src/providers/transport/chat.ts +156 -0
  86. package/src/providers/transport/errors.ts +48 -0
  87. package/src/providers/transport/history.ts +68 -0
  88. package/src/providers/transport/native-input.ts +83 -0
  89. package/src/providers/transport/native-types.ts +13 -0
  90. package/src/providers/transport/response-assembly.ts +191 -0
  91. package/src/providers/transport/response-http.ts +196 -0
  92. package/src/providers/transport/response-meters.ts +27 -0
  93. package/src/providers/transport/responses-input.ts +56 -0
  94. package/src/web/README.md +84 -0
  95. package/src/web/client/console-pages/builtins/llm-settings/panel.ts +625 -0
  96. package/src/web/client/console-pages/builtins/llm-settings/pricing-panel.ts +168 -0
  97. package/src/web/client/console-pages/builtins/llm-settings/strings.ts +213 -0
  98. package/src/web/client/console-pages/builtins.ts +10 -0
  99. package/src/web/client/console-pages/context.ts +135 -0
  100. package/src/web/client/console-pages/host.ts +633 -0
  101. package/src/web/client/console-pages/loader.ts +132 -0
  102. package/src/web/client/console-pages/strings.ts +90 -0
  103. package/src/web/client/console-pages/tools/strings.ts +49 -0
  104. package/src/web/client/console-pages/tools/view.ts +215 -0
  105. package/src/web/client/core/api.ts +200 -0
  106. package/src/web/client/core/language.ts +56 -0
  107. package/src/web/client/core/lifecycle.ts +132 -0
  108. package/src/web/client/core/router.ts +199 -0
  109. package/src/web/client/core/stream.ts +157 -0
  110. package/src/web/client/core/websocket.ts +82 -0
  111. package/src/web/client/features/appearance/index.ts +333 -0
  112. package/src/web/client/features/appearance/strings.ts +87 -0
  113. package/src/web/client/features/config/index.ts +11 -0
  114. package/src/web/client/features/config/strings.ts +41 -0
  115. package/src/web/client/features/config/view.ts +428 -0
  116. package/src/web/client/features/core/events.ts +197 -0
  117. package/src/web/client/features/core/index.ts +254 -0
  118. package/src/web/client/features/core/run.ts +56 -0
  119. package/src/web/client/features/core/runlog.ts +214 -0
  120. package/src/web/client/features/core/sessions.ts +53 -0
  121. package/src/web/client/features/core/strings.ts +157 -0
  122. package/src/web/client/features/extensions/index.ts +422 -0
  123. package/src/web/client/features/extensions/strings.ts +164 -0
  124. package/src/web/client/features/feature.ts +93 -0
  125. package/src/web/client/features/live/context.ts +280 -0
  126. package/src/web/client/features/live/fork.ts +156 -0
  127. package/src/web/client/features/live/index.ts +392 -0
  128. package/src/web/client/features/live/onboarding.ts +113 -0
  129. package/src/web/client/features/live/protocol.ts +122 -0
  130. package/src/web/client/features/live/sessions.ts +71 -0
  131. package/src/web/client/features/live/status.ts +68 -0
  132. package/src/web/client/features/live/strings.ts +252 -0
  133. package/src/web/client/features/live/timeline.ts +590 -0
  134. package/src/web/client/features/prompts/editor.ts +292 -0
  135. package/src/web/client/features/prompts/index.ts +346 -0
  136. package/src/web/client/features/prompts/strings.ts +136 -0
  137. package/src/web/client/features/prompts/view.ts +256 -0
  138. package/src/web/client/features/providers/index.ts +100 -0
  139. package/src/web/client/features/providers/strings.ts +23 -0
  140. package/src/web/client/features/settings/general.ts +28 -0
  141. package/src/web/client/features/settings/index.ts +82 -0
  142. package/src/web/client/features/settings/strings.ts +33 -0
  143. package/src/web/client/features/storage/index.ts +1 -0
  144. package/src/web/client/features/storage/strings.ts +45 -0
  145. package/src/web/client/features/storage/view.ts +154 -0
  146. package/src/web/client/features/usage/chart.ts +486 -0
  147. package/src/web/client/features/usage/index.ts +407 -0
  148. package/src/web/client/features/usage/labels.ts +46 -0
  149. package/src/web/client/features/usage/range.ts +36 -0
  150. package/src/web/client/features/usage/state.ts +37 -0
  151. package/src/web/client/features/usage/strings.ts +191 -0
  152. package/src/web/client/features/usage/tooltip.ts +86 -0
  153. package/src/web/client/features/usage/types.ts +96 -0
  154. package/src/web/client/features/worlds/index.ts +424 -0
  155. package/src/web/client/features/worlds/strings.ts +121 -0
  156. package/src/web/client/main.ts +273 -0
  157. package/src/web/client/shell/avatar.ts +233 -0
  158. package/src/web/client/shell/index.ts +549 -0
  159. package/src/web/client/shell/strings.ts +89 -0
  160. package/src/web/client/theme/handoff.ts +43 -0
  161. package/src/web/client/theme/palette.ts +109 -0
  162. package/src/web/client/theme/registry.ts +130 -0
  163. package/src/web/client/theme/storage.ts +72 -0
  164. package/src/web/client/theme/strings.ts +123 -0
  165. package/src/web/client/theme/studio.ts +366 -0
  166. package/src/web/client/ui/actions.ts +109 -0
  167. package/src/web/client/ui/data.ts +185 -0
  168. package/src/web/client/ui/dom.ts +23 -0
  169. package/src/web/client/ui/fields.ts +191 -0
  170. package/src/web/client/ui/format.ts +75 -0
  171. package/src/web/client/ui/icons.ts +183 -0
  172. package/src/web/client/ui/images.ts +91 -0
  173. package/src/web/client/ui/index.ts +89 -0
  174. package/src/web/client/ui/lamp.ts +125 -0
  175. package/src/web/client/ui/log.ts +98 -0
  176. package/src/web/client/ui/overlay.ts +184 -0
  177. package/src/web/client/ui/page.ts +8 -0
  178. package/src/web/client/ui/prompt-input.tsx +311 -0
  179. package/src/web/client/ui/sheet.ts +131 -0
  180. package/src/web/client/ui/strings.ts +71 -0
  181. package/src/web/console-pages.ts +367 -0
  182. package/src/web/files.ts +107 -0
  183. package/src/web/path-picker.ts +296 -0
  184. package/src/web/public/index.html +17 -0
  185. package/src/web/public/styles.css +1715 -0
  186. package/src/web/server.ts +1883 -0
  187. package/src/web/shared/client-panel.ts +603 -0
  188. package/src/web/shared/console-protocol.ts +560 -0
  189. package/src/web/shared/css.d.ts +1 -0
  190. package/src/web/shared/path-picker.ts +23 -0
  191. package/src/web/shared/theme.ts +256 -0
  192. package/src/web/theme-store.ts +37 -0
  193. package/src/world.ts +449 -0
  194. package/src/worlds/bilibili/ENV_PROMPT.md +5 -0
  195. package/src/worlds/bilibili/README.md +176 -0
  196. package/src/worlds/bilibili/audience-admission.ts +995 -0
  197. package/src/worlds/bilibili/client.ts +528 -0
  198. package/src/worlds/bilibili/coalescing-buffer.ts +125 -0
  199. package/src/worlds/bilibili/config.ts +185 -0
  200. package/src/worlds/bilibili/console/client.ts +202 -0
  201. package/src/worlds/bilibili/definition.ts +35 -0
  202. package/src/worlds/bilibili/gift-frame.ts +131 -0
  203. package/src/worlds/bilibili/normalize.ts +452 -0
  204. package/src/worlds/bilibili/overlay/announcement.ts +76 -0
  205. package/src/worlds/bilibili/overlay/assets.ts +82 -0
  206. package/src/worlds/bilibili/overlay/model.ts +494 -0
  207. package/src/worlds/bilibili/overlay/project.ts +202 -0
  208. package/src/worlds/bilibili/overlay/server.ts +286 -0
  209. package/src/worlds/bilibili/overlay/types.ts +217 -0
  210. package/src/worlds/bilibili/overlay/web/app.js +690 -0
  211. package/src/worlds/bilibili/overlay/web/editor.css +297 -0
  212. package/src/worlds/bilibili/overlay/web/editor.html +102 -0
  213. package/src/worlds/bilibili/overlay/web/editor.js +2116 -0
  214. package/src/worlds/bilibili/overlay/web/overlay.html +13 -0
  215. package/src/worlds/bilibili/overlay/web/styles.css +258 -0
  216. package/src/worlds/bilibili/protobuf.ts +123 -0
  217. package/src/worlds/bilibili/wire.ts +94 -0
  218. package/src/worlds/bilibili/world.ts +1389 -0
  219. package/src/worlds/console-fixture/ENV_PROMPT.md +1 -0
  220. package/src/worlds/console-fixture/console/client.ts +72 -0
  221. package/src/worlds/console-fixture/world.ts +73 -0
  222. package/src/worlds/index.ts +15 -0
  223. package/src/worlds/minecraft/ENV_PROMPT.md +32 -0
  224. package/src/worlds/minecraft/ENV_PROMPT_CAMERA.md +2 -0
  225. package/src/worlds/minecraft/LICENSE-mineflayer-pathfinder.txt +21 -0
  226. package/src/worlds/minecraft/README.md +917 -0
  227. package/src/worlds/minecraft/blueprint-plan.ts +915 -0
  228. package/src/worlds/minecraft/blueprint-registry.ts +566 -0
  229. package/src/worlds/minecraft/blueprint-repair.ts +236 -0
  230. package/src/worlds/minecraft/blueprint-resource.ts +272 -0
  231. package/src/worlds/minecraft/blueprint.ts +492 -0
  232. package/src/worlds/minecraft/body-lease.ts +340 -0
  233. package/src/worlds/minecraft/bridge.ts +749 -0
  234. package/src/worlds/minecraft/check.ts +651 -0
  235. package/src/worlds/minecraft/chests.ts +292 -0
  236. package/src/worlds/minecraft/client-launch.ts +272 -0
  237. package/src/worlds/minecraft/client-options.ts +85 -0
  238. package/src/worlds/minecraft/client-skins.ts +164 -0
  239. package/src/worlds/minecraft/client-window.ps1 +91 -0
  240. package/src/worlds/minecraft/client.ts +423 -0
  241. package/src/worlds/minecraft/combat-context.ts +109 -0
  242. package/src/worlds/minecraft/combat.ts +1552 -0
  243. package/src/worlds/minecraft/config.ts +499 -0
  244. package/src/worlds/minecraft/console/access.ts +218 -0
  245. package/src/worlds/minecraft/console/client.ts +244 -0
  246. package/src/worlds/minecraft/console/log.ts +110 -0
  247. package/src/worlds/minecraft/console/mount.ts +420 -0
  248. package/src/worlds/minecraft/console/skin.ts +257 -0
  249. package/src/worlds/minecraft/console/style.css +154 -0
  250. package/src/worlds/minecraft/console/world.ts +449 -0
  251. package/src/worlds/minecraft/deaths.ts +135 -0
  252. package/src/worlds/minecraft/definition.ts +18 -0
  253. package/src/worlds/minecraft/engine-child.ts +274 -0
  254. package/src/worlds/minecraft/engine-ipc.ts +98 -0
  255. package/src/worlds/minecraft/entity-facts.ts +132 -0
  256. package/src/worlds/minecraft/escape.ts +407 -0
  257. package/src/worlds/minecraft/executor.ts +14494 -0
  258. package/src/worlds/minecraft/explored.ts +156 -0
  259. package/src/worlds/minecraft/geometry.ts +205 -0
  260. package/src/worlds/minecraft/goal-plan.ts +647 -0
  261. package/src/worlds/minecraft/item-break.ts +126 -0
  262. package/src/worlds/minecraft/item-facts.ts +146 -0
  263. package/src/worlds/minecraft/item-pick.ts +67 -0
  264. package/src/worlds/minecraft/level-dat.ts +176 -0
  265. package/src/worlds/minecraft/log.ts +132 -0
  266. package/src/worlds/minecraft/mineflayer-fixes.ts +1180 -0
  267. package/src/worlds/minecraft/names.ts +377 -0
  268. package/src/worlds/minecraft/pathfinder-lib.d.ts +13 -0
  269. package/src/worlds/minecraft/pathfinder-perf.ts +796 -0
  270. package/src/worlds/minecraft/piglin.ts +49 -0
  271. package/src/worlds/minecraft/policy.ts +547 -0
  272. package/src/worlds/minecraft/precheck.ts +655 -0
  273. package/src/worlds/minecraft/proxy.ts +445 -0
  274. package/src/worlds/minecraft/ranged.ts +635 -0
  275. package/src/worlds/minecraft/readouts.ts +102 -0
  276. package/src/worlds/minecraft/round.ts +26 -0
  277. package/src/worlds/minecraft/search-observation.ts +92 -0
  278. package/src/worlds/minecraft/server-config.ts +467 -0
  279. package/src/worlds/minecraft/server.ts +658 -0
  280. package/src/worlds/minecraft/show.ts +84 -0
  281. package/src/worlds/minecraft/skills.ts +1763 -0
  282. package/src/worlds/minecraft/terrain.ts +1735 -0
  283. package/src/worlds/minecraft/window.ts +96 -0
  284. package/src/worlds/minecraft/works.ts +179 -0
  285. package/src/worlds/minecraft/world.ts +6438 -0
  286. package/src/worlds/qq/ENV_PROMPT.md +5 -0
  287. package/src/worlds/qq/config.ts +108 -0
  288. package/src/worlds/qq/console/client.ts +126 -0
  289. package/src/worlds/qq/console/events.ts +155 -0
  290. package/src/worlds/qq/console/gate.ts +232 -0
  291. package/src/worlds/qq/console/roster.ts +203 -0
  292. package/src/worlds/qq/console/style.css +65 -0
  293. package/src/worlds/qq/conversation.ts +28 -0
  294. package/src/worlds/qq/definition.ts +62 -0
  295. package/src/worlds/qq/driver.ts +375 -0
  296. package/src/worlds/qq/history-tools.ts +209 -0
  297. package/src/worlds/qq/normalize.ts +323 -0
  298. package/src/worlds/qq/qface-map.ts +282 -0
  299. package/src/worlds/qq/vision-prompt.ts +27 -0
  300. package/src/worlds/qq/vision.ts +642 -0
  301. package/src/worlds/qq/vlm.ts +142 -0
  302. package/src/worlds/qq/world.ts +1503 -0
  303. package/src/worlds/terminal/ENV_PROMPT.md +11 -0
  304. package/src/worlds/terminal/config.ts +9 -0
  305. package/src/worlds/terminal/definition.ts +14 -0
  306. package/src/worlds/terminal/world.ts +700 -0
  307. package/src/worlds/websearch/ENV_PROMPT.md +1 -0
  308. package/src/worlds/websearch/brave-client.ts +261 -0
  309. package/src/worlds/websearch/config.ts +75 -0
  310. package/src/worlds/websearch/definition.ts +20 -0
  311. package/src/worlds/websearch/world.ts +171 -0
@@ -0,0 +1,156 @@
1
+ /** 按世界与维度分别记录 8 个方向的历史最远距离与末端群系。 */
2
+ import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
3
+ import { dirname } from 'node:path';
4
+ import { normalizeDimension } from './escape.ts';
5
+ import { zhBiome, zhDimension } from './names.ts';
6
+ import { DIRECTION_ZH, type Direction } from './terrain.ts';
7
+
8
+ interface ExploreRecord {
9
+ distance: number;
10
+ biome: string;
11
+ at: number;
12
+ }
13
+
14
+ type ExploreMap = Partial<Record<Direction, ExploreRecord>>;
15
+
16
+ interface ExploreLedger {
17
+ version: 2;
18
+ currentRealm: string;
19
+ realms: Record<string, Record<string, ExploreMap>>;
20
+ }
21
+
22
+ const COMPASS_ORDER: Direction[] = [
23
+ 'north', 'northeast', 'east', 'southeast', 'south', 'southwest', 'west', 'northwest',
24
+ ];
25
+
26
+ /** 无探索记录时返回空串。 */
27
+ export function renderExploredSummary(map: ExploreMap): string {
28
+ const seen: string[] = [];
29
+ const unseen: string[] = [];
30
+ for (const dir of COMPASS_ORDER) {
31
+ const r = map[dir];
32
+ if (r) seen.push(`${DIRECTION_ZH[dir]}${r.distance}${r.biome === 'unknown' ? '' : `(${zhBiome(r.biome)})`}`);
33
+ else unseen.push(DIRECTION_ZH[dir]);
34
+ }
35
+ if (seen.length === 0) return '';
36
+ const head = `探过:${seen.join('/')}`;
37
+ return unseen.length > 0 ? `${head};${unseen.join('、')}没去过` : head;
38
+ }
39
+
40
+ /** 代理尚无当前维度时,列出当前世界有记录的全部维度。 */
41
+ export function renderExploredLedger(ledger: ExploreLedger): string {
42
+ const realm = ledger.realms[ledger.currentRealm] ?? {};
43
+ return Object.entries(realm)
44
+ .map(([dimension, map]) => {
45
+ const line = renderExploredSummary(map);
46
+ return line ? `[${zhDimension(dimension)}] ${line}` : '';
47
+ })
48
+ .filter(Boolean)
49
+ .join('\n');
50
+ }
51
+
52
+ function cleanMap(data: unknown): ExploreMap {
53
+ if (!data || typeof data !== 'object' || Array.isArray(data)) return {};
54
+ const raw = data as Record<string, ExploreRecord>;
55
+ const map: ExploreMap = {};
56
+ for (const dir of COMPASS_ORDER) {
57
+ const v = raw[dir];
58
+ if (v && typeof v.distance === 'number' && v.distance > 0) {
59
+ map[dir] = {
60
+ distance: Math.round(v.distance),
61
+ biome: typeof v.biome === 'string' && v.biome !== '' ? v.biome : 'unknown',
62
+ at: typeof v.at === 'number' ? v.at : 0,
63
+ };
64
+ }
65
+ }
66
+ return map;
67
+ }
68
+
69
+ /** 无法读取 v2 格式时使用空记录。 */
70
+ export function loadExplored(file: string | null): ExploreLedger {
71
+ const empty = (): ExploreLedger => ({ version: 2, currentRealm: '', realms: {} });
72
+ if (!file || !existsSync(file)) return empty();
73
+ let data: unknown;
74
+ try {
75
+ const raw = readFileSync(file, 'utf8').trim();
76
+ if (!raw) return empty();
77
+ data = JSON.parse(raw);
78
+ } catch {
79
+ return empty();
80
+ }
81
+ if (!data || typeof data !== 'object' || Array.isArray(data)) return empty();
82
+ const body = data as Record<string, unknown>;
83
+ if (body.version !== 2 || !body.realms || typeof body.realms !== 'object') return empty();
84
+ const realms: Record<string, Record<string, ExploreMap>> = {};
85
+ for (const [realm, dimensions] of Object.entries(body.realms as Record<string, unknown>)) {
86
+ if (!dimensions || typeof dimensions !== 'object' || Array.isArray(dimensions)) continue;
87
+ const scoped: Record<string, ExploreMap> = {};
88
+ for (const [dimension, map] of Object.entries(dimensions as Record<string, unknown>)) {
89
+ const cleaned = cleanMap(map);
90
+ if (Object.keys(cleaned).length > 0) scoped[normalizeDimension(dimension)] = cleaned;
91
+ }
92
+ if (Object.keys(scoped).length > 0) realms[realm] = scoped;
93
+ }
94
+ return {
95
+ version: 2,
96
+ currentRealm: typeof body.currentRealm === 'string' ? body.currentRealm : '',
97
+ realms,
98
+ };
99
+ }
100
+
101
+ export class ExploreBook {
102
+ private ledger: ExploreLedger;
103
+
104
+ constructor(private readonly file: string | null) {
105
+ this.ledger = loadExplored(file);
106
+ }
107
+
108
+ useRealm(realm: string): void {
109
+ if (this.ledger.currentRealm === realm) return;
110
+ this.ledger.currentRealm = realm;
111
+ this.save();
112
+ }
113
+
114
+ record(dimension: string, direction: Direction, distance: number, biome: string, now = Date.now()): void {
115
+ const d = Math.round(distance);
116
+ if (d <= 0) return;
117
+ const realm = this.ledger.currentRealm;
118
+ const dim = normalizeDimension(dimension);
119
+ const dimensions = this.ledger.realms[realm] ??= {};
120
+ const map = dimensions[dim] ??= {};
121
+ const prev = map[direction];
122
+ map[direction] = !prev || d >= prev.distance
123
+ ? { distance: d, biome, at: now }
124
+ : { ...prev, at: now };
125
+ this.save();
126
+ }
127
+
128
+ summary(dimension: string): string {
129
+ const map = this.ledger.realms[this.ledger.currentRealm]?.[normalizeDimension(dimension)] ?? {};
130
+ return renderExploredSummary(map);
131
+ }
132
+
133
+ stat(): string {
134
+ const scoped = Object.values(this.ledger.realms)
135
+ .flatMap((dimensions) => Object.values(dimensions))
136
+ .reduce((n, map) => n + Object.keys(map).length, 0);
137
+ if (!this.file || !existsSync(this.file)) return scoped === 0 ? '(无文件)' : `${scoped} 个已归属方向(未落盘)`;
138
+ const kb = (statSync(this.file).size / 1024).toFixed(1);
139
+ return `${scoped} 个已归属方向 / ${kb}KB`;
140
+ }
141
+
142
+ clear(): string {
143
+ const n = Object.values(this.ledger.realms)
144
+ .flatMap((dimensions) => Object.values(dimensions))
145
+ .reduce((sum, map) => sum + Object.keys(map).length, 0);
146
+ this.ledger = { version: 2, currentRealm: this.ledger.currentRealm, realms: {} };
147
+ this.save();
148
+ return n === 0 ? '探索账本是空的' : `探索账本已清空(${n} 个方向)`;
149
+ }
150
+
151
+ private save(): void {
152
+ if (!this.file) return;
153
+ mkdirSync(dirname(this.file), { recursive: true });
154
+ writeFileSync(this.file, `${JSON.stringify(this.ledger)}\n`, 'utf8');
155
+ }
156
+ }
@@ -0,0 +1,205 @@
1
+ /**
2
+ * build、excavate 与 probe 共用的坐标解析和栅格化。
3
+ * 相对坐标“~”/“~±n”按调用方传入的 origin 解析。
4
+ */
5
+
6
+ export interface Cell { x: number; y: number; z: number }
7
+
8
+ /** 一个坐标分量:绝对数字,或 "~"/"~±n" 相对写法 */
9
+ export type AnchorCoord = number | string;
10
+ export type Anchor = [AnchorCoord, AnchorCoord, AnchorCoord];
11
+
12
+ /**
13
+ * 六面向量为外法向量:参照方块坐标加向量得到放置坐标。
14
+ * 声明顺序对应协议 Direction 的 0–5。
15
+ */
16
+ export type BlockFace = 'down' | 'up' | 'north' | 'south' | 'west' | 'east';
17
+
18
+ export const BLOCK_FACES: Record<BlockFace, readonly [number, number, number]> = {
19
+ down: [0, -1, 0], up: [0, 1, 0], north: [0, 0, -1],
20
+ south: [0, 0, 1], west: [-1, 0, 0], east: [1, 0, 0],
21
+ };
22
+
23
+ export const FACE_NAMES = Object.keys(BLOCK_FACES) as BlockFace[];
24
+
25
+ export function cellOnFace(ref: Cell, face: BlockFace): Cell {
26
+ const [dx, dy, dz] = BLOCK_FACES[face];
27
+ return { x: ref.x + dx, y: ref.y + dy, z: ref.z + dz };
28
+ }
29
+
30
+ export type ShapeName = 'line' | 'rect' | 'triangle' | 'arc' | 'box';
31
+
32
+ /** solid 填充全部格;outline 仅外壳、保留内部;edges 仅十二条棱。 */
33
+ export type BoxFill = 'solid' | 'outline' | 'edges';
34
+
35
+ export const ANCHOR_COUNT: Record<ShapeName, number> = { line: 2, rect: 2, triangle: 3, arc: 3, box: 2 };
36
+
37
+ export const SHAPE_NAMES = Object.keys(ANCHOR_COUNT) as ShapeName[];
38
+
39
+ /** 单个坐标分量:数字向下取整,"~±n" 相对 origin 解析;认不出返回 null */
40
+ function resolveCoord(v: AnchorCoord, origin: number): number | null {
41
+ if (typeof v === 'number') return Number.isFinite(v) ? Math.floor(v) : null;
42
+ const s = v.trim();
43
+ if (s === '~') return origin;
44
+ if (s.startsWith('~')) {
45
+ const off = Number(s.slice(1));
46
+ return Number.isFinite(off) ? origin + Math.floor(off) : null;
47
+ }
48
+ const abs = Number(s);
49
+ return Number.isFinite(abs) && s !== '' ? Math.floor(abs) : null;
50
+ }
51
+
52
+ /** 按 origin 解析锚点;失败返回错误说明。 */
53
+ export function resolveAnchors(
54
+ anchors: readonly Anchor[],
55
+ origin: Cell,
56
+ ): Cell[] | { error: string } {
57
+ const out: Cell[] = [];
58
+ for (const [i, a] of anchors.entries()) {
59
+ const x = resolveCoord(a[0], origin.x);
60
+ const y = resolveCoord(a[1], origin.y);
61
+ const z = resolveCoord(a[2], origin.z);
62
+ if (x === null || y === null || z === null) {
63
+ return { error: `第 ${i + 1} 个锚点认不出来(写数字,或 "~"/"~-3")` };
64
+ }
65
+ out.push({ x, y, z });
66
+ }
67
+ return out;
68
+ }
69
+
70
+ const key = (c: Cell): string => `${c.x},${c.y},${c.z}`;
71
+
72
+ class CellSet {
73
+ private seen = new Set<string>();
74
+ readonly cells: Cell[] = [];
75
+ add(x: number, y: number, z: number): void {
76
+ const c = { x: Math.round(x), y: Math.round(y), z: Math.round(z) };
77
+ const k = key(c);
78
+ if (this.seen.has(k)) return;
79
+ this.seen.add(k);
80
+ this.cells.push(c);
81
+ }
82
+ }
83
+
84
+ /** 两点连线:按最长轴步进插值取整,连续无跳格 */
85
+ function lineInto(out: CellSet, a: Cell, b: Cell): void {
86
+ const n = Math.max(Math.abs(b.x - a.x), Math.abs(b.y - a.y), Math.abs(b.z - a.z));
87
+ if (n === 0) { out.add(a.x, a.y, a.z); return; }
88
+ for (let i = 0; i <= n; i++) {
89
+ const t = i / n;
90
+ out.add(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t, a.z + (b.z - a.z) * t);
91
+ }
92
+ }
93
+
94
+ type Vec = [number, number, number];
95
+ const sub = (a: Cell, b: Cell): Vec => [a.x - b.x, a.y - b.y, a.z - b.z];
96
+ const cross = (a: Vec, b: Vec): Vec => [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]];
97
+ const dot = (a: Vec, b: Vec): number => a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
98
+ const scale = (a: Vec, s: number): Vec => [a[0] * s, a[1] * s, a[2] * s];
99
+ const add3 = (a: Vec, b: Vec): Vec => [a[0] + b[0], a[1] + b[1], a[2] + b[2]];
100
+
101
+ /**
102
+ * 过三点的圆弧:从第 1 点经第 2 点到第 3 点。三点共线铺不出圆,返回错误。
103
+ * 圆心用外心公式在三点平面内解;采样步距 <1 格保证连续。
104
+ */
105
+ function arcInto(out: CellSet, a: Cell, b: Cell, c: Cell): { error: string } | null {
106
+ const v1 = sub(b, a);
107
+ const v2 = sub(c, a);
108
+ const n = cross(v1, v2);
109
+ const n2 = dot(n, n);
110
+ if (n2 < 1e-9) return { error: '三个锚点在一条直线上,画不出弧——直的用 line' };
111
+ // 外心 = a + (|v1|²(v2×n) + |v2|²(n×v1)) / (2|n|²)
112
+ const center = add3(
113
+ [a.x, a.y, a.z],
114
+ scale(add3(scale(cross(v2, n), dot(v1, v1)), scale(cross(n, v1), dot(v2, v2))), 1 / (2 * n2)),
115
+ );
116
+ const ca: Vec = [a.x - center[0], a.y - center[1], a.z - center[2]];
117
+ const r = Math.sqrt(dot(ca, ca));
118
+ // 平面内正交基:u 指向起点,w 与之垂直;点的角度 = atan2(·w, ·u)
119
+ const u = scale(ca, 1 / r);
120
+ const nHat = scale(n, 1 / Math.sqrt(n2));
121
+ const w = cross(nHat, u);
122
+ const angleOf = (p: Cell): number => {
123
+ const v: Vec = [p.x - center[0], p.y - center[1], p.z - center[2]];
124
+ const ang = Math.atan2(dot(v, w), dot(v, u));
125
+ return ang < 0 ? ang + 2 * Math.PI : ang;
126
+ };
127
+ const angB = angleOf(b);
128
+ const angC = angleOf(c);
129
+ // 起点角为 0;扫向必须经过 b:b 在 c 之前就正着扫,否则反着扫
130
+ const sweep = angB <= angC ? angC : angC - 2 * Math.PI;
131
+ const steps = Math.max(2, Math.ceil(r * Math.abs(sweep) * 1.5));
132
+ for (let i = 0; i <= steps; i++) {
133
+ const ang = (sweep * i) / steps;
134
+ const cos = Math.cos(ang);
135
+ const sin = Math.sin(ang);
136
+ out.add(
137
+ center[0] + r * (u[0] * cos + w[0] * sin),
138
+ center[1] + r * (u[1] * cos + w[1] * sin),
139
+ center[2] + r * (u[2] * cos + w[2] * sin),
140
+ );
141
+ }
142
+ return null;
143
+ }
144
+
145
+ /**
146
+ * 形状 → 格子清单。锚点须已解析为绝对格坐标(resolveAnchors)。
147
+ * 返回顺序:line/arc 沿走向,rect/box 逐层,triangle 扇形填充。
148
+ */
149
+ export function rasterize(
150
+ shape: ShapeName,
151
+ anchors: readonly Cell[],
152
+ fill: BoxFill = 'solid',
153
+ ): Cell[] | { error: string } {
154
+ if (anchors.length !== ANCHOR_COUNT[shape]) {
155
+ return { error: `${shape} 要 ${ANCHOR_COUNT[shape]} 个锚点,给了 ${anchors.length} 个` };
156
+ }
157
+ const out = new CellSet();
158
+ switch (shape) {
159
+ case 'line': {
160
+ lineInto(out, anchors[0], anchors[1]);
161
+ break;
162
+ }
163
+ case 'rect': {
164
+ const [a, b] = anchors;
165
+ const flat = (['x', 'y', 'z'] as const).filter((ax) => a[ax] === b[ax]);
166
+ if (flat.length === 0) {
167
+ return { error: 'rect 两个锚点要有一轴相等(相等的那一轴就是面的朝向)——斜面用 triangle 拼' };
168
+ }
169
+ for (let x = Math.min(a.x, b.x); x <= Math.max(a.x, b.x); x++) {
170
+ for (let y = Math.min(a.y, b.y); y <= Math.max(a.y, b.y); y++) {
171
+ for (let z = Math.min(a.z, b.z); z <= Math.max(a.z, b.z); z++) out.add(x, y, z);
172
+ }
173
+ }
174
+ break;
175
+ }
176
+ case 'triangle': {
177
+ const edge = new CellSet();
178
+ lineInto(edge, anchors[1], anchors[2]);
179
+ for (const p of edge.cells) lineInto(out, anchors[0], p);
180
+ break;
181
+ }
182
+ case 'arc': {
183
+ const err = arcInto(out, anchors[0], anchors[1], anchors[2]);
184
+ if (err) return err;
185
+ break;
186
+ }
187
+ case 'box': {
188
+ const [a, b] = anchors;
189
+ const lo = { x: Math.min(a.x, b.x), y: Math.min(a.y, b.y), z: Math.min(a.z, b.z) };
190
+ const hi = { x: Math.max(a.x, b.x), y: Math.max(a.y, b.y), z: Math.max(a.z, b.z) };
191
+ for (let x = lo.x; x <= hi.x; x++) {
192
+ for (let y = lo.y; y <= hi.y; y++) {
193
+ for (let z = lo.z; z <= hi.z; z++) {
194
+ const onFace = (x === lo.x || x === hi.x ? 1 : 0)
195
+ + (y === lo.y || y === hi.y ? 1 : 0)
196
+ + (z === lo.z || z === hi.z ? 1 : 0);
197
+ if (fill === 'solid' || (fill === 'outline' ? onFace >= 1 : onFace >= 2)) out.add(x, y, z);
198
+ }
199
+ }
200
+ }
201
+ break;
202
+ }
203
+ }
204
+ return out.cells;
205
+ }