@danceiny/gotry 0.0.1-rc.15 → 0.0.1-rc.17

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 (238) hide show
  1. package/README.md +51 -13
  2. package/README.zh-CN.md +26 -12
  3. package/bin/gotry-booking-copilot.js +53 -0
  4. package/bin/gotry-bootstrap.js +357 -8
  5. package/bin/gotry-inner.js +442 -60
  6. package/bin/gotry-runtime-resolution.d.ts +27 -0
  7. package/bin/gotry-runtime-resolution.js +50 -0
  8. package/bin/gotry.js +1 -1
  9. package/cordis.gotry-patch.yml +15 -1
  10. package/data/airline-airports.json +39 -0
  11. package/dist/capabilities/agent-reach-deep.js +1 -1
  12. package/dist/capabilities/agent-reach.js +1 -1
  13. package/dist/capabilities/anything.js +1 -1
  14. package/dist/capabilities/artifacts.js +1 -1
  15. package/dist/capabilities/effect.js +293 -0
  16. package/dist/capabilities/fact-log.js +41 -0
  17. package/dist/capabilities/flyai.js +20 -6
  18. package/dist/capabilities/hbcli.js +43 -1
  19. package/dist/capabilities/incident-log.js +1 -1
  20. package/dist/capabilities/model-override.js +18 -0
  21. package/dist/capabilities/opensky.js +1 -1
  22. package/dist/capabilities/resilience.js +90 -0
  23. package/dist/capabilities/session/action-cache.js +1 -1
  24. package/dist/capabilities/session/adapters/ctrip-flight.js +1 -1
  25. package/dist/capabilities/session/adapters/meituan-local.js +1 -1
  26. package/dist/capabilities/session/benchmark.js +2 -1
  27. package/dist/capabilities/session/extension-bridge.js +358 -0
  28. package/dist/capabilities/session/extension-channel.js +93 -0
  29. package/dist/capabilities/session/extension-distribution.js +234 -0
  30. package/dist/capabilities/session/extract.js +1 -1
  31. package/dist/capabilities/session/golden-score.js +92 -0
  32. package/dist/capabilities/session/health-watch.js +216 -0
  33. package/dist/capabilities/session/read-guard.js +1 -1
  34. package/dist/capabilities/session/static-flight-golden.js +137 -0
  35. package/dist/capabilities/session/transport.js +1 -1
  36. package/dist/capabilities/session/wizard.js +147 -0
  37. package/dist/capabilities/session-consent.js +1 -1
  38. package/dist/capabilities/session-login.js +83 -1
  39. package/dist/capabilities/session-search.js +111 -2
  40. package/dist/capabilities/weather.js +168 -46
  41. package/dist/data/session-golden-20.json +25 -0
  42. package/dist/data/sf-golden-manifest.json +102 -0
  43. package/dist/data/sf-static-routes.json +91 -0
  44. package/dist/scripts/action-cache-tests.js +1 -1
  45. package/dist/scripts/agent-planning-budget-e2e.js +227 -0
  46. package/dist/scripts/agent-planning-budget-tests.js +173 -0
  47. package/dist/scripts/agent-reach-deep-tests.js +1 -1
  48. package/dist/scripts/agent-reach-tests.js +1 -1
  49. package/dist/scripts/agent-reach-wrapper-tests.js +1 -1
  50. package/dist/scripts/anything-tests.js +1 -1
  51. package/dist/scripts/async-collect.js +1 -1
  52. package/dist/scripts/benchmark-environment-bridge-e2e.js +981 -0
  53. package/dist/scripts/benchmark-environment-bridge-tests.js +2544 -0
  54. package/dist/scripts/booking-copilot-availability-ledger-binding-tests.js +335 -0
  55. package/dist/scripts/booking-copilot-availability-policy-v2-tests.js +1355 -0
  56. package/dist/scripts/booking-copilot-bin-proof-tests.js +89 -0
  57. package/dist/scripts/booking-copilot-crossrepo-fixture-server.js +113 -0
  58. package/dist/scripts/booking-copilot-dsh-core-proof-tests.js +356 -0
  59. package/dist/scripts/booking-copilot-dsh-planner-proof-tests.js +557 -0
  60. package/dist/scripts/booking-copilot-dsh-plugin-proof-tests.js +140 -0
  61. package/dist/scripts/booking-copilot-event-sequence-concurrency-proof-tests.js +202 -0
  62. package/dist/scripts/booking-copilot-gap-code-contract-proof-tests.js +102 -0
  63. package/dist/scripts/booking-copilot-operation-ledger-concurrency-proof-tests.js +374 -0
  64. package/dist/scripts/booking-copilot-receipt-ledger-concurrency-proof-tests.js +447 -0
  65. package/dist/scripts/booking-copilot-runtime-proof-tests.js +205 -0
  66. package/dist/scripts/booking-copilot-server-proof-tests.js +317 -0
  67. package/dist/scripts/booking-copilot-startup-proof-tests.js +283 -0
  68. package/dist/scripts/booking-copilot-v2-runtime-proof-tests.js +3935 -0
  69. package/dist/scripts/booking-saga-tests.js +1 -1
  70. package/dist/scripts/booking-surface-contract-proof-tests.js +393 -0
  71. package/dist/scripts/booking-surface-v2-contract-proof-tests.js +1174 -0
  72. package/dist/scripts/bootstrap-tests.js +59 -3
  73. package/dist/scripts/build-changelog.js +279 -0
  74. package/dist/scripts/changelog-tests.js +165 -0
  75. package/dist/scripts/companion-tests.js +1 -1
  76. package/dist/scripts/diff-test.js +1 -1
  77. package/dist/scripts/dsh-runtime-closure-tests.js +225 -0
  78. package/dist/scripts/dsh-runtime-closure.js +150 -0
  79. package/dist/scripts/effect-tests.js +474 -0
  80. package/dist/scripts/engine-run.js +1 -1
  81. package/dist/scripts/engine-tests.js +1 -1
  82. package/dist/scripts/evaluation-cadence-tests.js +347 -0
  83. package/dist/scripts/evaluation-contract-tests.js +574 -0
  84. package/dist/scripts/extension-distribution-cli.js +35 -0
  85. package/dist/scripts/extension-distribution-tests.js +341 -0
  86. package/dist/scripts/extension-tests.js +573 -0
  87. package/dist/scripts/fact-gate-tests.js +318 -0
  88. package/dist/scripts/flyai-tests.js +1 -1
  89. package/dist/scripts/hbcli-e2e-tests.js +180 -0
  90. package/dist/scripts/hbcli-tests.js +1 -1
  91. package/dist/scripts/health-watch-cli.js +51 -0
  92. package/dist/scripts/i18n-tests.js +1 -1
  93. package/dist/scripts/incident-tests.js +1 -1
  94. package/dist/scripts/journey-tests.js +1 -1
  95. package/dist/scripts/ledger-tests.js +1 -1
  96. package/dist/scripts/ledger-workflow-crash.js +1 -1
  97. package/dist/scripts/memory-capture-tests.js +1 -1
  98. package/dist/scripts/memory-decay-tests.js +1 -1
  99. package/dist/scripts/memory-metrics.js +1 -1
  100. package/dist/scripts/memory-value-report.js +1 -1
  101. package/dist/scripts/model-override-e2e.js +176 -0
  102. package/dist/scripts/nightly-evidence-tests.js +67 -4
  103. package/dist/scripts/nightly-evidence.js +59 -3
  104. package/dist/scripts/nudge-digest.js +1 -1
  105. package/dist/scripts/onboarding-tests.js +186 -0
  106. package/dist/scripts/opensky-check.js +1 -1
  107. package/dist/scripts/opensky-tests.js +1 -1
  108. package/dist/scripts/pnpm-dsh-closure-proof.js +20 -0
  109. package/dist/scripts/price-drift-tests.js +441 -0
  110. package/dist/scripts/price-drift-watch.js +491 -0
  111. package/dist/scripts/probe-poi-tests.js +1 -1
  112. package/dist/scripts/product-metrics.js +1 -1
  113. package/dist/scripts/publish-preverify.js +40 -4
  114. package/dist/scripts/realtime-pricing-tests.js +1 -1
  115. package/dist/scripts/replay-async.js +1 -1
  116. package/dist/scripts/replay-real.js +1 -1
  117. package/dist/scripts/replay.js +1 -1
  118. package/dist/scripts/session-attach-diagnose.js +1 -1
  119. package/dist/scripts/session-attach-poc.js +1 -1
  120. package/dist/scripts/session-benchmark.js +1 -1
  121. package/dist/scripts/session-extract-tests.js +1 -1
  122. package/dist/scripts/session-login.js +1 -1
  123. package/dist/scripts/session-tests.js +70 -20
  124. package/dist/scripts/sf-live-benchmark.js +338 -0
  125. package/dist/scripts/sf-live-cli-tests.js +21 -0
  126. package/dist/scripts/sf-soft-score-tests.js +108 -0
  127. package/dist/scripts/sf-summary.js +93 -0
  128. package/dist/scripts/skeleton-check.js +1 -1
  129. package/dist/scripts/skeleton-integration-test.js +1 -1
  130. package/dist/scripts/skills-contract-tests.js +1 -1
  131. package/dist/scripts/smoke-session-gate-tests.js +29 -0
  132. package/dist/scripts/smoke.js +154 -33
  133. package/dist/scripts/state-cli-tests.js +1 -1
  134. package/dist/scripts/state-cli.js +1 -1
  135. package/dist/scripts/static-golden-tests.js +299 -0
  136. package/dist/scripts/time-eval-tests.js +1 -1
  137. package/dist/scripts/travel-timeline-tests.js +1 -1
  138. package/dist/scripts/unified-tests.js +1 -1
  139. package/dist/scripts/weather-tests.js +694 -44
  140. package/dist/scripts/z3-race-tests.js +1 -1
  141. package/dist/src/artifact-gate.js +310 -0
  142. package/dist/src/benchmark-agent-conformance.js +370 -0
  143. package/dist/src/benchmark-environment-bridge.js +384 -0
  144. package/dist/src/benchmark-headless-child-diagnostics.js +173 -0
  145. package/dist/src/benchmark-tool-isolation.js +124 -0
  146. package/dist/src/bookable-facts.js +349 -0
  147. package/dist/src/booking-saga.js +1 -1
  148. package/dist/src/booking-surface/availability-policy-v2.js +830 -0
  149. package/dist/src/booking-surface/canonical-schema.js +113 -0
  150. package/dist/src/booking-surface/contracts-v2.js +89 -0
  151. package/dist/src/booking-surface/contracts.js +46 -0
  152. package/dist/src/booking-surface/dsh-planner.js +453 -0
  153. package/dist/src/booking-surface/dsh-plugin.js +93 -0
  154. package/dist/src/booking-surface/error-codes.js +94 -0
  155. package/dist/src/booking-surface/index.js +15 -0
  156. package/dist/src/booking-surface/profile.js +68 -0
  157. package/dist/src/booking-surface/runtime-v2.js +1771 -0
  158. package/dist/src/booking-surface/runtime.js +351 -0
  159. package/dist/src/booking-surface/server-v2.js +334 -0
  160. package/dist/src/booking-surface/server.js +302 -0
  161. package/dist/src/booking-surface/startup.js +159 -0
  162. package/dist/src/booking-surface/validation-v2.js +319 -0
  163. package/dist/src/booking-surface/validation.js +809 -0
  164. package/dist/src/bridge.js +1 -1
  165. package/dist/src/companions.js +1 -1
  166. package/dist/src/contracts.js +1 -1
  167. package/dist/src/dsh-llm.js +1 -1
  168. package/dist/src/engine.js +1 -1
  169. package/dist/src/evaluation-cadence.js +234 -0
  170. package/dist/src/evaluation-contracts.js +906 -0
  171. package/dist/src/i18n.js +1 -1
  172. package/dist/src/index.js +234 -60
  173. package/dist/src/journey.js +1 -1
  174. package/dist/src/loop.js +1 -1
  175. package/dist/src/memory-capture.js +1 -1
  176. package/dist/src/memory-decay.js +1 -1
  177. package/dist/src/memory-utility.js +1 -1
  178. package/dist/src/mock-llm.js +1 -1
  179. package/dist/src/model.js +1 -1
  180. package/dist/src/realtime-pricing.js +14 -7
  181. package/dist/src/slot-spec.js +1 -1
  182. package/dist/src/state-ledger.js +2 -1
  183. package/dist/src/time-anchor.js +1 -1
  184. package/dist/src/tool-budget.js +136 -0
  185. package/dist/src/tool-packet.js +1 -1
  186. package/dist/src/travel-slots.js +1 -1
  187. package/dist/src/travel-timeline.js +1 -1
  188. package/dist/src/unified.js +1 -1
  189. package/dist/src/wish-pool.js +1 -1
  190. package/dist/src/z3-shared.js +1 -1
  191. package/extension/README.md +50 -0
  192. package/extension/background.js +160 -0
  193. package/extension/content-bridge.js +41 -0
  194. package/extension/content-main.js +62 -0
  195. package/extension/manifest.json +32 -0
  196. package/package.json +291 -12
  197. package/schemas/booking.surface.v1.schema.json +927 -0
  198. package/schemas/booking.surface.v2.schema.json +61 -0
  199. package/ts/capabilities/fact-log.ts +54 -0
  200. package/ts/capabilities/flyai.ts +16 -3
  201. package/ts/capabilities/hbcli.ts +46 -0
  202. package/ts/capabilities/session/benchmark.ts +4 -1
  203. package/ts/capabilities/session/extension-bridge.ts +377 -0
  204. package/ts/capabilities/session/extension-channel.ts +95 -0
  205. package/ts/capabilities/session/extension-distribution.ts +264 -0
  206. package/ts/capabilities/session/golden-score.ts +139 -0
  207. package/ts/capabilities/session/health-watch.ts +257 -0
  208. package/ts/capabilities/session/static-flight-golden.ts +209 -0
  209. package/ts/capabilities/session/wizard.ts +140 -0
  210. package/ts/capabilities/session-login.ts +63 -2
  211. package/ts/capabilities/session-search.ts +105 -3
  212. package/ts/capabilities/weather.ts +141 -52
  213. package/ts/package.json +3 -3
  214. package/ts/src/artifact-gate.ts +365 -0
  215. package/ts/src/benchmark-agent-conformance.ts +448 -0
  216. package/ts/src/benchmark-environment-bridge.ts +348 -0
  217. package/ts/src/benchmark-headless-child-diagnostics.ts +184 -0
  218. package/ts/src/benchmark-tool-isolation.ts +166 -0
  219. package/ts/src/bookable-facts.ts +557 -0
  220. package/ts/src/booking-surface/availability-policy-v2.ts +523 -0
  221. package/ts/src/booking-surface/canonical-schema.js +113 -0
  222. package/ts/src/booking-surface/contracts-v2.ts +118 -0
  223. package/ts/src/booking-surface/contracts.ts +380 -0
  224. package/ts/src/booking-surface/dsh-planner.ts +452 -0
  225. package/ts/src/booking-surface/dsh-plugin.js +93 -0
  226. package/ts/src/booking-surface/error-codes.ts +101 -0
  227. package/ts/src/booking-surface/index.ts +12 -0
  228. package/ts/src/booking-surface/profile.ts +42 -0
  229. package/ts/src/booking-surface/runtime-v2.ts +1466 -0
  230. package/ts/src/booking-surface/runtime.ts +483 -0
  231. package/ts/src/booking-surface/server-v2.ts +247 -0
  232. package/ts/src/booking-surface/server.ts +324 -0
  233. package/ts/src/booking-surface/startup.ts +196 -0
  234. package/ts/src/booking-surface/validation-v2.ts +205 -0
  235. package/ts/src/booking-surface/validation.ts +453 -0
  236. package/ts/src/index.ts +161 -14
  237. package/ts/src/state-ledger.ts +1 -0
  238. package/ts/src/tool-budget.ts +165 -0
@@ -36,7 +36,8 @@ assert.ok(c1.out.includes('hbcli'), '报告应含 hbcli 节');
36
36
  assert.ok(c1.out.includes('agent-reach'), '报告应含 agent-reach 节');
37
37
  assert.ok(c1.out.includes('flyai'), '报告应含 flyai(无需安装)节');
38
38
  assert.ok(c1.out.includes('dsh-better-sidebar'), '报告应含 dsh-better-sidebar 节(issue #25 产物查看面)');
39
- console.log('1. --check-only 探测报告 exit 0(hbcli/agent-reach/flyai/dsh-better-sidebar 四节齐)OK');
39
+ assert.ok(c1.out.includes('Session Bridge'), '报告应含会话扩展节(issue #21 传输层方案 C)');
40
+ console.log('1. --check-only 探测报告 exit 0(hbcli/agent-reach/flyai/dsh-better-sidebar/会话扩展 五节齐)OK');
40
41
  const c2 = runBootstrap([
41
42
  '--auto'
42
43
  ], {
@@ -60,7 +61,62 @@ assert.equal(c4.code, 0, '单项跳过态应 exit 0');
60
61
  assert.ok(c4.out.includes('GOTRY_SETUP_SIDEBAR=0 跳过'), '应输出侧栏单项跳过说明');
61
62
  assert.ok(c4.out.includes('hbcli'), '其余节不受单项开关影响');
62
63
  console.log('4. GOTRY_SETUP_SIDEBAR=0 单项跳过(其余节照常)OK');
63
- console.log('BOOTSTRAP TESTS: 4/4 OK(探测报告/跳过开关/单项开关/postinstall 非致命)');
64
+ const c5 = runBootstrap([
65
+ '--check-only'
66
+ ], {});
67
+ assert.ok(c5.out.includes('.gotry/extension'), '扩展报告应含落位路径 ~/.gotry/extension(绝对路径形态)');
68
+ const c5b = runBootstrap([
69
+ '--check-only'
70
+ ], {
71
+ GOTRY_SETUP_EXTENSION: '0'
72
+ });
73
+ assert.equal(c5b.code, 0, '扩展单项跳过态应 exit 0');
74
+ assert.ok(c5b.out.includes('GOTRY_SETUP_EXTENSION=0 跳过'), '应输出扩展单项跳过说明');
75
+ console.log('5. 会话扩展节(check-only 报告 + GOTRY_SETUP_EXTENSION=0 单项跳过)OK');
76
+ const c6 = runBootstrap([
77
+ 'wizard',
78
+ '--dry-run'
79
+ ], {});
80
+ assert.equal(c6.code, 0, `wizard --dry-run 应 exit 0,实际 ${c6.code}\n${c6.out}`);
81
+ assert.ok(c6.out.includes('dry-run'), '应输出 dry-run 字样');
82
+ assert.ok(c6.out.includes('gotry-wizard'), '应输出 [gotry-wizard] 标签');
83
+ assert.ok(c6.out.includes('ensure-extension-files'), 'dry-run 应列 ensure-extension-files');
84
+ assert.ok(c6.out.includes('watch-extension-ready'), 'dry-run 应列 watch-extension-ready');
85
+ console.log('6. wizard 子命令(--dry-run 零网络,2 步齐全 + 极简 stdout)OK');
86
+ const c7 = runBootstrap([
87
+ 'wizard'
88
+ ], {
89
+ GOTRY_SETUP_EXTENSION: '0',
90
+ GOTRY_ONBOARDING_TIMEOUT_MS: '600',
91
+ GOTRY_ONBOARDING_INTERVAL_MS: '200'
92
+ });
93
+ assert.equal(c7.code, 1, `wizard(超时)应 exit 1,实际 ${c7.code}\n${c7.out}`);
94
+ assert.ok(c7.out.includes('gotry-wizard'), '应输出 [gotry-wizard] 标签');
95
+ console.log('7. wizard 真实路径(扩展未就绪,exit 1 + 引导标题 + 心跳)OK');
96
+ const c8 = runBootstrap([
97
+ '--check-only',
98
+ '--extension-from=github'
99
+ ], {
100
+ GOTRY_SETUP_HBCLI: '0',
101
+ GOTRY_SETUP_REACH: '0',
102
+ GOTRY_SETUP_SIDEBAR: '0',
103
+ GOTRY_EXTENSION_RELEASE_BASE: 'http://127.0.0.1:1/releases'
104
+ });
105
+ assert.equal(c8.code, 0, `github 通道降级应 exit 0,实际 ${c8.code}\n${c8.out}`);
106
+ assert.ok(c8.out.includes('GitHub Releases 下载通道'), '应打印 github 通道标题');
107
+ assert.ok(c8.out.includes('降级包内副本'), '失败应显式降级 bundled');
108
+ const c8b = runBootstrap([
109
+ '--check-only',
110
+ '--extension-from=不合法值'
111
+ ], {
112
+ GOTRY_SETUP_HBCLI: '0',
113
+ GOTRY_SETUP_REACH: '0',
114
+ GOTRY_SETUP_SIDEBAR: '0'
115
+ });
116
+ assert.equal(c8b.code, 0, `非法 --extension-from 值应回落 bundled 且 exit 0,实际 ${c8b.code}\n${c8b.out}`);
117
+ assert.ok(!c8b.out.includes('下载通道'), '非法值不应进入 github 通道');
118
+ console.log('8. 扩展分发 github 通道(不可达基址即时降级 + 非法值回落 bundled)OK');
119
+ console.log('BOOTSTRAP TESTS: 8/8 OK(探测报告/跳过开关/单项开关/postinstall 非致命/会话扩展/wizard --dry-run/wizard 真实/扩展分发通道)');
64
120
 
65
121
 
66
- //# sourceURL=/Users/bytedance/work/gotry/ts/scripts/bootstrap-tests.ts
122
+ //# sourceURL=ts/scripts/bootstrap-tests.ts
@@ -0,0 +1,279 @@
1
+ import { execFileSync } from 'node:child_process';
2
+ import { readFileSync, writeFileSync, existsSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ const TYPE_TO_KEEP_A_CHANGELOG = {
5
+ feat: 'Added',
6
+ fix: 'Fixed',
7
+ perf: 'Changed',
8
+ refactor: 'Changed',
9
+ docs: 'Documentation',
10
+ test: 'Tests',
11
+ build: 'Build',
12
+ ci: 'CI',
13
+ chore: 'Chore',
14
+ revert: 'Reverted'
15
+ };
16
+ const DISPLAY_ORDER = [
17
+ 'feat',
18
+ 'fix',
19
+ 'perf',
20
+ 'refactor',
21
+ 'revert',
22
+ 'docs',
23
+ 'test',
24
+ 'build',
25
+ 'ci',
26
+ 'chore',
27
+ 'other'
28
+ ];
29
+ const CONVENTIONAL_RE = /^(feat|fix|perf|refactor|docs|test|build|ci|chore|revert)(?:\(([^)]+)\))?(!)?:\s*(.+)$/;
30
+ export function parseCommit(raw) {
31
+ const lines = raw.split('\n');
32
+ const firstLine = lines[0] ?? '';
33
+ if (!firstLine) return null;
34
+ const { head, prRef } = splitSubjectAndPr(firstLine);
35
+ const match = head.match(CONVENTIONAL_RE);
36
+ if (!match) {
37
+ return {
38
+ sha: '',
39
+ type: 'other',
40
+ scope: null,
41
+ subject: head,
42
+ prRef,
43
+ raw
44
+ };
45
+ }
46
+ const [, type, scope] = match;
47
+ const colonIdx = head.indexOf(': ');
48
+ const subject = colonIdx >= 0 ? head.slice(colonIdx + 2).trim() : head;
49
+ return {
50
+ sha: '',
51
+ type: type,
52
+ scope: scope ?? null,
53
+ subject,
54
+ prRef,
55
+ raw
56
+ };
57
+ }
58
+ function splitSubjectAndPr(text) {
59
+ const m = text.match(/\s+\(#(\d+)\)\s*$/);
60
+ if (m) return {
61
+ head: text.slice(0, m.index).trim(),
62
+ prRef: m[1]
63
+ };
64
+ return {
65
+ head: text.trim(),
66
+ prRef: null
67
+ };
68
+ }
69
+ function extractPrRef(text) {
70
+ const m = text.match(/\(#(\d+)\)/);
71
+ return m ? m[1] : null;
72
+ }
73
+ export function listCommits(opts) {
74
+ const args = [
75
+ 'log',
76
+ '--no-merges',
77
+ '--pretty=format:%H%x00%s%x00%b%x00%x1e'
78
+ ];
79
+ if (opts.since) args.push(`${opts.since}..HEAD`);
80
+ const out = execFileSync('git', args, {
81
+ cwd: opts.cwd,
82
+ encoding: 'utf8'
83
+ });
84
+ if (!out) return [];
85
+ return out.split('\u001e').filter(Boolean).map((raw)=>{
86
+ const [sha, subject, body] = raw.split('\u0000').map((s)=>(s ?? '').trim());
87
+ return {
88
+ sha: sha ?? '',
89
+ subject: subject ?? '',
90
+ body: body ?? ''
91
+ };
92
+ });
93
+ }
94
+ export function groupCommits(commits) {
95
+ const sections = new Map();
96
+ for (const c of commits){
97
+ if (!sections.has(c.type)) sections.set(c.type, {
98
+ type: c.type,
99
+ items: []
100
+ });
101
+ sections.get(c.type).items.push({
102
+ subject: c.subject,
103
+ sha: c.sha,
104
+ prRef: c.prRef
105
+ });
106
+ }
107
+ const out = [];
108
+ for (const t of DISPLAY_ORDER){
109
+ const sec = sections.get(t);
110
+ if (sec && sec.items.length > 0) out.push(sec);
111
+ }
112
+ return out;
113
+ }
114
+ export function renderMarkdownEntry(entry) {
115
+ const lines = [];
116
+ lines.push(`## [${entry.version}] - ${entry.date}`);
117
+ lines.push('');
118
+ for (const sec of entry.sections){
119
+ const header = sec.type === 'other' ? 'Other' : TYPE_TO_KEEP_A_CHANGELOG[sec.type] ?? sec.type;
120
+ lines.push(`### ${header}`);
121
+ lines.push('');
122
+ for (const item of sec.items){
123
+ const subject = truncateSubject(item.subject, 120);
124
+ const suffix = item.prRef ? ` (#${item.prRef})` : item.sha ? ` (${item.sha.slice(0, 7)})` : '';
125
+ lines.push(`- ${subject}${suffix}`);
126
+ }
127
+ lines.push('');
128
+ }
129
+ return lines.join('\n');
130
+ }
131
+ function truncateSubject(text, max) {
132
+ if (text.length <= max * 2) {
133
+ const breakers = [
134
+ '。',
135
+ '——',
136
+ '; ',
137
+ ' — ',
138
+ ',',
139
+ ','
140
+ ];
141
+ for (const b of breakers){
142
+ const i = text.indexOf(b);
143
+ if (i > 30 && i <= max * 2) return `${text.slice(0, i).trim()}…`;
144
+ }
145
+ }
146
+ return `${text.slice(0, max).trim()}…`;
147
+ }
148
+ export function buildEntry(opts) {
149
+ return {
150
+ version: opts.version,
151
+ date: opts.date,
152
+ sections: groupCommits(opts.commits)
153
+ };
154
+ }
155
+ const CHANGELOG_HEADER = `# Changelog
156
+
157
+ All notable changes to this project will be documented in this file.
158
+
159
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
160
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
161
+
162
+ `;
163
+ export function splitChangelog(raw) {
164
+ const firstSection = raw.indexOf('\n## [');
165
+ if (firstSection < 0) return {
166
+ preamble: raw.endsWith('\n') ? raw : `${raw}\n`,
167
+ history: []
168
+ };
169
+ const preamble = raw.slice(0, firstSection + 1);
170
+ const history = raw.slice(firstSection + 1);
171
+ return {
172
+ preamble,
173
+ history: [
174
+ history
175
+ ]
176
+ };
177
+ }
178
+ export function prependEntry(raw, entryMarkdown) {
179
+ if (!existsSync(join('..', 'CHANGELOG.md')) && !raw) {
180
+ return CHANGELOG_HEADER + entryMarkdown;
181
+ }
182
+ const split = splitChangelog(raw || CHANGELOG_HEADER + '');
183
+ return split.preamble + entryMarkdown + '\n' + split.history.join('').replace(/^\n/, '');
184
+ }
185
+ function arg(name) {
186
+ const i = process.argv.indexOf(name);
187
+ return i >= 0 ? process.argv[i + 1] : undefined;
188
+ }
189
+ function readPackageVersion() {
190
+ const raw = JSON.parse(readFileSync(join('..', 'package.json'), 'utf8'));
191
+ if (!raw.version) throw new Error('package.json 缺 version 字段');
192
+ return raw.version;
193
+ }
194
+ function gitHeadSha() {
195
+ return execFileSync('git', [
196
+ 'rev-parse',
197
+ 'HEAD'
198
+ ], {
199
+ encoding: 'utf8'
200
+ }).trim();
201
+ }
202
+ export function buildFromGit(opts) {
203
+ const rawCommits = listCommits({
204
+ cwd: opts.cwd,
205
+ since: opts.since
206
+ });
207
+ const parsed = [];
208
+ for (const r of rawCommits){
209
+ const m = parseCommit(r.subject);
210
+ if (!m) continue;
211
+ m.sha = r.sha;
212
+ m.raw = r.body;
213
+ parsed.push(m);
214
+ }
215
+ const entry = buildEntry({
216
+ version: opts.version,
217
+ date: opts.date,
218
+ commits: parsed
219
+ });
220
+ const markdown = renderMarkdownEntry(entry);
221
+ return {
222
+ entry,
223
+ markdown
224
+ };
225
+ }
226
+ async function main() {
227
+ const cwd = join('..');
228
+ const version = arg('--version') ?? readPackageVersion();
229
+ const date = arg('--date') ?? new Date().toISOString().slice(0, 10);
230
+ const since = arg('--since');
231
+ const write = process.argv.includes('--write');
232
+ let sinceTag = since;
233
+ if (!sinceTag) {
234
+ const out = execFileSync('git', [
235
+ 'tag',
236
+ '-l',
237
+ 'v*',
238
+ '--sort=-v:refname'
239
+ ], {
240
+ cwd,
241
+ encoding: 'utf8'
242
+ });
243
+ const tags = out.split('\n').map((t)=>t.trim()).filter(Boolean);
244
+ sinceTag = tags.find((t)=>t !== `v${version}` && tagsAreLessThan(t, `v${version}`)) ?? tags[tags.length - 1];
245
+ }
246
+ if (!sinceTag) {
247
+ console.error('build-changelog: 找不到起始 tag;传 --since <tag> 显式指定');
248
+ process.exit(2);
249
+ }
250
+ const { entry, markdown } = buildFromGit({
251
+ cwd,
252
+ version,
253
+ date,
254
+ since: sinceTag
255
+ });
256
+ const existing = existsSync(join(cwd, 'CHANGELOG.md')) ? readFileSync(join(cwd, 'CHANGELOG.md'), 'utf8') : '';
257
+ const next = write ? prependEntry(existing, markdown) : markdown;
258
+ console.log(`# build-changelog: ${entry.sections.length} sections / ${entry.sections.reduce((n, s)=>n + s.items.length, 0)} commits since ${sinceTag}`);
259
+ for (const sec of entry.sections){
260
+ console.log(`## ${sec.type} (${sec.items.length})`);
261
+ for (const it of sec.items)console.log(` - ${it.subject}${it.prRef ? ` (#${it.prRef})` : ''}`);
262
+ }
263
+ if (write) {
264
+ writeFileSync(join(cwd, 'CHANGELOG.md'), next, 'utf8');
265
+ console.log(`\n>> wrote CHANGELOG.md (${next.length}B) @ ${gitHeadSha().slice(0, 7)}`);
266
+ }
267
+ }
268
+ function tagsAreLessThan(a, b) {
269
+ return a < b;
270
+ }
271
+ if (process.argv[1]?.endsWith('build-changelog.ts')) {
272
+ main().catch((e)=>{
273
+ console.error(`build-changelog error: ${e.message}`);
274
+ process.exit(1);
275
+ });
276
+ }
277
+
278
+
279
+ //# sourceURL=ts/scripts/build-changelog.ts
@@ -0,0 +1,165 @@
1
+ import assert from 'node:assert/strict';
2
+ import { buildEntry, groupCommits, parseCommit, prependEntry, renderMarkdownEntry, splitChangelog } from './build-changelog.js';
3
+ let passed = 0;
4
+ function ok(label) {
5
+ passed += 1;
6
+ console.log(` ok ${label}`);
7
+ }
8
+ const sample = parseCommit('feat(cost): 价表 provider-aware v2 + 价格漂移监测 (#68)');
9
+ assert.ok(sample);
10
+ assert.equal(sample.type, 'feat');
11
+ assert.equal(sample.scope, 'cost');
12
+ assert.equal(sample.subject, '价表 provider-aware v2 + 价格漂移监测');
13
+ assert.equal(sample.prRef, '68');
14
+ ok('parseCommit feat(scope)! + #PR');
15
+ const noScope = parseCommit('fix: 修复登录页可见性');
16
+ assert.equal(noScope.type, 'fix');
17
+ assert.equal(noScope.scope, null);
18
+ assert.equal(noScope.subject, '修复登录页可见性');
19
+ assert.equal(noScope.prRef, null);
20
+ ok('parseCommit fix 无 scope');
21
+ const other = parseCommit('docs: README 更新');
22
+ assert.equal(other.type, 'docs');
23
+ const legacy = parseCommit('feat(issue #21 P3.6): 会话扩展 onboarding UX 闭环(点点点 + 装完零重跑)');
24
+ assert.equal(legacy.type, 'feat');
25
+ assert.equal(legacy.scope, 'issue #21 P3.6');
26
+ const historical = parseCommit('Merge branch main into feat/x');
27
+ assert.equal(historical.type, 'other');
28
+ assert.equal(historical.subject, 'Merge branch main into feat/x');
29
+ ok('parseCommit 历史 commit + 中文 subject + 无 #PR 兼容');
30
+ for (const t of [
31
+ 'perf',
32
+ 'refactor',
33
+ 'docs',
34
+ 'test',
35
+ 'build',
36
+ 'ci',
37
+ 'chore',
38
+ 'revert'
39
+ ]){
40
+ const p = parseCommit(`${t}: 一些改动`);
41
+ assert.equal(p.type, t);
42
+ }
43
+ ok('parseCommit 全 9 类型支持');
44
+ const commits = [
45
+ {
46
+ sha: 'aaa',
47
+ type: 'fix',
48
+ scope: null,
49
+ subject: 'fix bug',
50
+ prRef: '1',
51
+ raw: ''
52
+ },
53
+ {
54
+ sha: 'bbb',
55
+ type: 'feat',
56
+ scope: null,
57
+ subject: 'add feature',
58
+ prRef: '2',
59
+ raw: ''
60
+ },
61
+ {
62
+ sha: 'ccc',
63
+ type: 'docs',
64
+ scope: null,
65
+ subject: 'update docs',
66
+ prRef: null,
67
+ raw: ''
68
+ },
69
+ {
70
+ sha: 'ddd',
71
+ type: 'other',
72
+ scope: null,
73
+ subject: 'random commit',
74
+ prRef: null,
75
+ raw: ''
76
+ }
77
+ ];
78
+ const grouped = groupCommits(commits);
79
+ assert.equal(grouped.length, 4);
80
+ assert.equal(grouped[0].type, 'feat');
81
+ assert.equal(grouped[1].type, 'fix');
82
+ assert.equal(grouped[2].type, 'docs');
83
+ assert.equal(grouped[3].type, 'other');
84
+ ok('groupCommits Keep a Changelog 显示顺序(Added → Fixed → Documentation → Other)');
85
+ const entry = buildEntry({
86
+ version: '0.0.1-rc.16',
87
+ date: '2026-08-30',
88
+ commits: [
89
+ {
90
+ sha: 'aaa',
91
+ type: 'feat',
92
+ scope: null,
93
+ subject: 'add a brand new feature with substantial description text',
94
+ prRef: '2',
95
+ raw: ''
96
+ },
97
+ {
98
+ sha: 'bbb',
99
+ type: 'fix',
100
+ scope: null,
101
+ subject: 'fix a critical bug in the rendering path of the parser',
102
+ prRef: '1',
103
+ raw: ''
104
+ },
105
+ {
106
+ sha: 'ccc',
107
+ type: 'docs',
108
+ scope: null,
109
+ subject: 'update the documentation for the new auth module',
110
+ prRef: null,
111
+ raw: ''
112
+ }
113
+ ]
114
+ });
115
+ const md = renderMarkdownEntry(entry);
116
+ assert.match(md, /^## \[0\.0\.1-rc\.16\] - 2026-08-30/);
117
+ assert.match(md, /### Added/);
118
+ assert.match(md, /### Fixed/);
119
+ assert.match(md, /### Documentation/);
120
+ assert.match(md, /- add a brand new feature[^()]*\(#2\)/);
121
+ assert.match(md, /- fix a critical bug[^()]*\(#1\)/);
122
+ assert.match(md, /- update the documentation[^()]*\(ccc\)/);
123
+ ok('renderMarkdownEntry 节结构 + PR/sha 后缀');
124
+ const existing = `# Changelog
125
+
126
+ All notable changes to this project will be documented in this file.
127
+
128
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
129
+
130
+ ## [0.0.1-rc.15] - 2026-08-29
131
+
132
+ ### Added
133
+ - 预订 saga 状态机
134
+ `;
135
+ const newMd = renderMarkdownEntry(entry);
136
+ const next = prependEntry(existing, newMd);
137
+ assert.match(next, /## \[0\.0\.1-rc\.16\] - 2026-08-30/);
138
+ assert.match(next, /## \[0\.0\.1-rc\.15\] - 2026-08-29/);
139
+ assert.match(next, /预订 saga 状态机/);
140
+ ok('prependEntry:新版本段在顶部,历史段原样保留');
141
+ const fromEmpty = prependEntry('', newMd);
142
+ assert.match(fromEmpty, /Keep a Changelog/);
143
+ assert.match(fromEmpty, /## \[0\.0\.1-rc\.16\]/);
144
+ ok('prependEntry:空文件首次生成');
145
+ const split = splitChangelog(existing);
146
+ assert.ok(split.preamble.includes('All notable changes'));
147
+ assert.ok(split.history[0].includes('## [0.0.1-rc.15]'));
148
+ ok('splitChangelog preamble/history 分隔正确');
149
+ const emptyEntry = buildEntry({
150
+ version: '0.0.1-rc.16',
151
+ date: '2026-08-30',
152
+ commits: []
153
+ });
154
+ const emptyMd = renderMarkdownEntry(emptyEntry);
155
+ assert.match(emptyMd, /## \[0\.0\.1-rc\.16\]/);
156
+ ok('buildEntry 空 commit 列表 → 仍输出版本头(0 section,合规)');
157
+ const breaking = parseCommit('feat(api)!: 重命名端点 /v1/users → /v2/users (#99)');
158
+ assert.equal(breaking.type, 'feat');
159
+ assert.equal(breaking.subject, '重命名端点 /v1/users → /v2/users');
160
+ assert.equal(breaking.prRef, '99');
161
+ ok('parseCommit feat(scope)!: breaking change marker 容错');
162
+ console.log(`changelog tests: ${passed} 组断言全绿(纯函数)`);
163
+
164
+
165
+ //# sourceURL=ts/scripts/changelog-tests.ts
@@ -109,4 +109,4 @@ pass('多人并存与 id 稳定性', ()=>{
109
109
  console.log(`\nCOMPANION TESTS: ${n}/4 OK(memory-design P2 守门面)`);
110
110
 
111
111
 
112
- //# sourceURL=/Users/bytedance/work/gotry/ts/scripts/companion-tests.ts
112
+ //# sourceURL=ts/scripts/companion-tests.ts
@@ -36,4 +36,4 @@ for (const id of Object.keys(tsV)){
36
36
  console.log('\nDIFF TEST OK: 双路径判定一致(纯 TS,无 Python 依赖)');
37
37
 
38
38
 
39
- //# sourceURL=/Users/bytedance/work/gotry/ts/scripts/diff-test.ts
39
+ //# sourceURL=ts/scripts/diff-test.ts