botrun-horse 2.30.0 → 2.34.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 (664) hide show
  1. package/bin/bh.mjs +37 -170
  2. package/bin/commands/help.mjs +30 -30
  3. package/{lib/flows → modules/_core}/draft-writing.mjs +1 -1
  4. package/{lib/flows → modules/_core}/gemini-ask.mjs +1 -1
  5. package/{lib/flows → modules/_core}/legal-ask.mjs +4 -4
  6. package/{lib/flows → modules/_core}/openai-agent.mjs +2 -2
  7. package/{lib/flows → modules/_core}/openrouter-ask.mjs +1 -1
  8. package/{bin/commands/dag-cmd.mjs → modules/dag/command.mjs} +2 -2
  9. package/modules/dag/index.mjs +30 -0
  10. package/{bin/commands/db-cmd.mjs → modules/db/command.mjs} +3 -3
  11. package/modules/db/index.mjs +26 -0
  12. package/modules/discover.mjs +43 -0
  13. package/{bin/commands/doc.mjs → modules/doc/command.mjs} +5 -5
  14. package/modules/doc/index.mjs +55 -0
  15. package/{lib/doc/index.mjs → modules/doc/ingest.mjs} +1 -1
  16. package/{lib → modules}/doc/office2text.mjs +1 -1
  17. package/{lib → modules}/doc/pdf2text.mjs +1 -1
  18. package/{lib → modules}/doc/split.mjs +1 -1
  19. package/{bin/commands/gemini.mjs → modules/gemini/command.mjs} +3 -3
  20. package/modules/gemini/index.mjs +41 -0
  21. package/modules/imagen/command.mjs +117 -0
  22. package/modules/imagen/imagen-client.mjs +239 -0
  23. package/modules/imagen/index.mjs +60 -0
  24. package/modules/imagen/server.mjs +133 -0
  25. package/{bin/commands/legal.mjs → modules/legal/command.mjs} +5 -5
  26. package/modules/legal/index.mjs +56 -0
  27. package/{bin/commands/nchc.mjs → modules/nchc/command.mjs} +4 -4
  28. package/modules/nchc/index.mjs +48 -0
  29. package/modules/ocr/index.mjs +17 -0
  30. package/{bin/commands/openrouter.mjs → modules/openrouter/command.mjs} +5 -5
  31. package/modules/openrouter/index.mjs +49 -0
  32. package/modules/portal/index.mjs +17 -0
  33. package/{bin/commands/search.mjs → modules/search/command.mjs} +3 -3
  34. package/modules/search/index.mjs +23 -0
  35. package/{lib/search/index.mjs → modules/search/search-lib.mjs} +1 -1
  36. package/{bin/commands/skill.mjs → modules/skill/command.mjs} +46 -9
  37. package/modules/skill/index.mjs +35 -0
  38. package/{lib/prompt → modules/skill}/prompt-search.mjs +1 -1
  39. package/{lib/prompt → modules/skill}/prompt-store.mjs +28 -11
  40. package/modules/skill/prompts/zero-framework/address-matching.md +22 -0
  41. package/modules/skill/prompts/zero-framework/data-integrity.md +15 -0
  42. package/modules/skill/prompts/zero-framework/data-reasonability.md +17 -0
  43. package/modules/skill/prompts/zero-framework/gemini-image-gen.md +20 -0
  44. package/modules/skill/prompts/zero-framework/gemini-search-cli.md +10 -0
  45. package/modules/skill/prompts/zero-framework/html-report.md +15 -0
  46. package/modules/skill/prompts/zero-framework/smart-cache.md +10 -0
  47. package/modules/skill/prompts/zero-framework/trigram-search.md +12 -0
  48. package/modules/skill/prompts/zero-framework/tts-format.md +6 -0
  49. package/modules/skill/prompts/zero-framework/watermelon-sqlite.md +8 -0
  50. package/{bin/commands/writing.mjs → modules/writing/command.mjs} +3 -3
  51. package/{lib → modules}/writing/generate.mjs +2 -2
  52. package/modules/writing/index.mjs +38 -0
  53. package/package.json +3 -1
  54. package/parallel-dag-todo-list/imagen-module.md +51 -0
  55. package/bin/commands/schema.mjs +0 -254
  56. package/botrun-c/.claude/skills/DOCX/351/226/261/350/256/200/346/212/200/350/203/275/SKILL.md +0 -103
  57. package/botrun-c/.claude/skills/DOCX/351/226/261/350/256/200/346/212/200/350/203/275/scripts/docx-to-markdown.mjs +0 -206
  58. package/botrun-c/.claude/skills/GitHub/346/212/200/350/203/275/SKILL.md +0 -1093
  59. package/botrun-c/.claude/skills/GitHub/346/212/200/350/203/275/scripts/github-branch.mjs +0 -73
  60. package/botrun-c/.claude/skills/GitHub/346/212/200/350/203/275/scripts/github-branches.mjs +0 -77
  61. package/botrun-c/.claude/skills/GitHub/346/212/200/350/203/275/scripts/github-checkout.mjs +0 -72
  62. package/botrun-c/.claude/skills/GitHub/346/212/200/350/203/275/scripts/github-clone.mjs +0 -72
  63. package/botrun-c/.claude/skills/GitHub/346/212/200/350/203/275/scripts/github-commit.mjs +0 -75
  64. package/botrun-c/.claude/skills/GitHub/346/212/200/350/203/275/scripts/github-delete-branch.mjs +0 -75
  65. package/botrun-c/.claude/skills/GitHub/346/212/200/350/203/275/scripts/github-delete-file.mjs +0 -72
  66. package/botrun-c/.claude/skills/GitHub/346/212/200/350/203/275/scripts/github-diff.mjs +0 -80
  67. package/botrun-c/.claude/skills/GitHub/346/212/200/350/203/275/scripts/github-list-tree.mjs +0 -336
  68. package/botrun-c/.claude/skills/GitHub/346/212/200/350/203/275/scripts/github-list.mjs +0 -199
  69. package/botrun-c/.claude/skills/GitHub/346/212/200/350/203/275/scripts/github-merge.mjs +0 -86
  70. package/botrun-c/.claude/skills/GitHub/346/212/200/350/203/275/scripts/github-move.mjs +0 -75
  71. package/botrun-c/.claude/skills/GitHub/346/212/200/350/203/275/scripts/github-pr-create.mjs +0 -81
  72. package/botrun-c/.claude/skills/GitHub/346/212/200/350/203/275/scripts/github-pr-list.mjs +0 -74
  73. package/botrun-c/.claude/skills/GitHub/346/212/200/350/203/275/scripts/github-pr-merge.mjs +0 -83
  74. package/botrun-c/.claude/skills/GitHub/346/212/200/350/203/275/scripts/github-pr-view.mjs +0 -71
  75. package/botrun-c/.claude/skills/GitHub/346/212/200/350/203/275/scripts/github-push.mjs +0 -71
  76. package/botrun-c/.claude/skills/GitHub/346/212/200/350/203/275/scripts/github-read.mjs +0 -277
  77. package/botrun-c/.claude/skills/GitHub/346/212/200/350/203/275/scripts/github-search.mjs +0 -370
  78. package/botrun-c/.claude/skills/GitHub/346/212/200/350/203/275/scripts/github-stash.mjs +0 -116
  79. package/botrun-c/.claude/skills/GitHub/346/212/200/350/203/275/scripts/github-sync.mjs +0 -71
  80. package/botrun-c/.claude/skills/GitHub/346/212/200/350/203/275/scripts/github-write.mjs +0 -74
  81. package/botrun-c/.claude/skills/GitHub/346/212/200/350/203/275/scripts/lib/path-validator.mjs +0 -167
  82. package/botrun-c/.claude/skills/GoogleDrive/346/212/200/350/203/275/SKILL.md +0 -605
  83. package/botrun-c/.claude/skills/GoogleDrive/346/212/200/350/203/275/scripts/gdrive-create.mjs +0 -127
  84. package/botrun-c/.claude/skills/GoogleDrive/346/212/200/350/203/275/scripts/gdrive-delete.mjs +0 -77
  85. package/botrun-c/.claude/skills/GoogleDrive/346/212/200/350/203/275/scripts/gdrive-diff.mjs +0 -87
  86. package/botrun-c/.claude/skills/GoogleDrive/346/212/200/350/203/275/scripts/gdrive-history.mjs +0 -99
  87. package/botrun-c/.claude/skills/GoogleDrive/346/212/200/350/203/275/scripts/gdrive-list.mjs +0 -174
  88. package/botrun-c/.claude/skills/GoogleDrive/346/212/200/350/203/275/scripts/gdrive-read.mjs +0 -214
  89. package/botrun-c/.claude/skills/GoogleDrive/346/212/200/350/203/275/scripts/gdrive-restore.mjs +0 -75
  90. package/botrun-c/.claude/skills/GoogleDrive/346/212/200/350/203/275/scripts/gdrive-search.mjs +0 -270
  91. package/botrun-c/.claude/skills/GoogleDrive/346/212/200/350/203/275/scripts/gdrive-sync-back.mjs +0 -155
  92. package/botrun-c/.claude/skills/GoogleDrive/346/212/200/350/203/275/scripts/gdrive-update.mjs +0 -100
  93. package/botrun-c/.claude/skills/HTML/347/224/237/346/210/220/346/212/200/350/203/275/SKILL.md +0 -414
  94. package/botrun-c/.claude/skills/HTML/347/224/237/346/210/220/346/212/200/350/203/275/scripts/create-project.mjs +0 -91
  95. package/botrun-c/.claude/skills/HTML/347/224/237/346/210/220/346/212/200/350/203/275/scripts/finalize-project.mjs +0 -162
  96. package/botrun-c/.claude/skills/PDF/346/212/200/350/203/275/SKILL.md +0 -206
  97. package/botrun-c/.claude/skills/PDF/346/212/200/350/203/275/scripts/package.json +0 -19
  98. package/botrun-c/.claude/skills/PDF/346/212/200/350/203/275/scripts/pdf-analyze.mjs +0 -309
  99. package/botrun-c/.claude/skills/PDF/346/212/200/350/203/275/scripts/pdf-compress.mjs +0 -315
  100. package/botrun-c/.claude/skills/PDF/346/212/200/350/203/275/scripts/pdf-split.mjs +0 -275
  101. package/botrun-c/.claude/skills/PDF/346/212/200/350/203/275/scripts/pdf-to-text.mjs +0 -336
  102. package/botrun-c/.claude/skills/PDF/346/212/200/350/203/275/scripts/test-pdf-scripts.mjs +0 -491
  103. package/botrun-c/.claude/skills/docx-to-markdown/SKILL.md +0 -76
  104. package/botrun-c/.claude/skills/docx-to-markdown/scripts/convert_docx.py +0 -183
  105. package/botrun-c/.claude/skills/gcp-coord-ml-ocr/SKILL.md +0 -133
  106. package/botrun-c/.claude/skills/gcp-coord-ml-ocr/scripts/ocr_processor.py +0 -381
  107. package/botrun-c/.claude/skills/gemini-transcribe/SKILL.md +0 -115
  108. package/botrun-c/.claude/skills/gemini-transcribe/scripts/transcribe.py +0 -499
  109. package/botrun-c/.claude/skills/nchc-transcribe/SKILL.md +0 -131
  110. package/botrun-c/.claude/skills/nchc-transcribe/scripts/transcribe.py +0 -522
  111. package/botrun-c/.claude/skills/p320-moj-review/SKILL.md +0 -200
  112. package/botrun-c/.claude/skills/p320-moj-review/references/guideline.md +0 -118
  113. package/botrun-c/.claude/skills/pdf-multimodal-processor/SKILL.md +0 -186
  114. package/botrun-c/.claude/skills/pdf-multimodal-processor/scripts/process_pdf.py +0 -515
  115. package/botrun-c/.claude/skills/ripgrep/346/220/234/345/260/213/346/212/200/350/203/275/SKILL.md +0 -81
  116. package/botrun-c/.claude/skills/ripgrep/346/220/234/345/260/213/346/212/200/350/203/275/scripts/package.json +0 -13
  117. package/botrun-c/.claude/skills/ripgrep/346/220/234/345/260/213/346/212/200/350/203/275/scripts/secure-search.mjs +0 -232
  118. package/botrun-c/.claude/skills/top100/351/240/230/350/242/226/351/240/220/346/270/254/346/212/200/350/203/275/SKILL.md +0 -518
  119. package/botrun-c/.claude/skills//345/216/273/350/255/230/345/210/245/346/212/200/350/203/275/SKILL.md +0 -125
  120. package/botrun-c/.claude/skills//345/233/236/346/206/266/346/212/200/350/203/275/SKILL.md +0 -123
  121. package/botrun-c/.claude/skills//345/233/236/346/206/266/346/212/200/350/203/275/scripts/recall.mjs +0 -339
  122. package/botrun-c/.claude/skills//345/234/226/347/211/207/347/224/237/346/210/220/346/212/200/350/203/275/SKILL.md +0 -156
  123. package/botrun-c/.claude/skills//345/234/226/347/211/207/347/224/237/346/210/220/346/212/200/350/203/275/scripts/__tests__/utils.test.mjs +0 -139
  124. package/botrun-c/.claude/skills//345/234/226/347/211/207/347/224/237/346/210/220/346/212/200/350/203/275/scripts/constants.mjs +0 -40
  125. package/botrun-c/.claude/skills//345/234/226/347/211/207/347/224/237/346/210/220/346/212/200/350/203/275/scripts/gcs-uploader.mjs +0 -195
  126. package/botrun-c/.claude/skills//345/234/226/347/211/207/347/224/237/346/210/220/346/212/200/350/203/275/scripts/gemini-image-client.mjs +0 -307
  127. package/botrun-c/.claude/skills//345/234/226/347/211/207/347/224/237/346/210/220/346/212/200/350/203/275/scripts/generate-image.mjs +0 -103
  128. package/botrun-c/.claude/skills//345/234/226/347/211/207/347/224/237/346/210/220/346/212/200/350/203/275/scripts/image-session-manager.mjs +0 -219
  129. package/botrun-c/.claude/skills//345/234/226/347/211/207/347/224/237/346/210/220/346/212/200/350/203/275/scripts/output-formatter.mjs +0 -209
  130. package/botrun-c/.claude/skills//345/234/226/347/211/207/347/224/237/346/210/220/346/212/200/350/203/275/scripts/package.json +0 -20
  131. package/botrun-c/.claude/skills//345/234/226/347/211/207/347/224/237/346/210/220/346/212/200/350/203/275/scripts/utils.mjs +0 -115
  132. package/botrun-c/.claude/skills//345/244/232/346/250/241/346/205/213/351/226/261/350/256/200/346/212/200/350/203/275/SKILL.md +0 -86
  133. package/botrun-c/.claude/skills//345/244/232/346/250/241/346/205/213/351/226/261/350/256/200/346/212/200/350/203/275/scripts/multimodal-read.mjs +0 -304
  134. package/botrun-c/.claude/skills//345/244/232/346/250/241/346/205/213/351/226/261/350/256/200/346/212/200/350/203/275/scripts/package.json +0 -17
  135. package/botrun-c/.claude/skills//345/255/265/345/214/226/346/212/200/350/203/275/SKILL.md +0 -131
  136. package/botrun-c/.claude/skills//345/255/265/345/214/226/346/212/200/350/203/275/scripts/skill-manager.mjs +0 -542
  137. package/botrun-c/.claude/skills//346/234/203/350/255/260/350/250/230/351/214/204/346/212/200/350/203/275/SKILL.md +0 -127
  138. package/botrun-c/.claude/skills//347/233/256/351/214/204/345/210/227/350/241/250/346/212/200/350/203/275/SKILL.md +0 -53
  139. package/botrun-c/.claude/skills//347/233/256/351/214/204/345/210/227/350/241/250/346/212/200/350/203/275/scripts/package.json +0 -13
  140. package/botrun-c/.claude/skills//347/233/256/351/214/204/345/210/227/350/241/250/346/212/200/350/203/275/scripts/tree-view.mjs +0 -149
  141. package/botrun-c/.claude/skills//347/264/224/346/226/207/345/255/227/345/244/232/346/252/224/346/241/210/350/256/200/345/217/226/346/212/200/350/203/275/SKILL.md +0 -82
  142. package/botrun-c/.claude/skills//347/264/224/346/226/207/345/255/227/345/244/232/346/252/224/346/241/210/350/256/200/345/217/226/346/212/200/350/203/275/scripts/package.json +0 -16
  143. package/botrun-c/.claude/skills//347/264/224/346/226/207/345/255/227/345/244/232/346/252/224/346/241/210/350/256/200/345/217/226/346/212/200/350/203/275/scripts/secure-read.mjs +0 -260
  144. package/botrun-c/.claude/skills//347/266/262/350/267/257/346/220/234/345/260/213/346/212/200/350/203/275/SKILL.md +0 -156
  145. package/botrun-c/.claude/skills//347/266/262/350/267/257/346/220/234/345/260/213/346/212/200/350/203/275/scripts/package.json +0 -16
  146. package/botrun-c/.claude/skills//347/266/262/350/267/257/346/220/234/345/260/213/346/212/200/350/203/275/scripts/search.js +0 -139
  147. package/botrun-c/.claude/skills//347/266/262/350/267/257/346/220/234/345/260/213/346/212/200/350/203/275/scripts/search.mjs +0 -272
  148. package/botrun-c/.claude/skills//347/266/262/350/267/257/346/220/234/345/260/213/346/212/200/350/203/275/scripts/search.ts +0 -166
  149. package/botrun-c/.claude/skills//350/250/230/346/206/266/346/212/200/350/203/275/SKILL.md +0 -114
  150. package/botrun-c/.claude/skills//350/250/230/346/206/266/346/212/200/350/203/275/scripts/remember.mjs +0 -159
  151. package/botrun-c/.claude/skills//350/250/230/346/206/266/346/212/200/350/203/275/scripts/test-advanced.mjs +0 -342
  152. package/botrun-c/.claude/skills//350/250/230/346/206/266/346/212/200/350/203/275/scripts/test.mjs +0 -430
  153. package/botrun-c/.claude/skills//350/252/236/351/237/263/350/275/211/346/226/207/345/255/227/346/212/200/350/203/275/SKILL.md +0 -30
  154. package/botrun-c/.claude/skills//350/252/236/351/237/263/350/275/211/346/226/207/345/255/227/346/212/200/350/203/275/scripts/package.json +0 -13
  155. package/botrun-c/.claude/skills//350/252/236/351/237/263/350/275/211/346/226/207/345/255/227/346/212/200/350/203/275/scripts/transcribe.mjs +0 -294
  156. package/botrun-c/.claude/skills//351/233/266/345/271/273/350/246/272/350/255/211/346/230/216/346/212/200/350/203/275/SKILL.md +0 -338
  157. package/botrun-c/.dockerignore +0 -131
  158. package/botrun-c/.dockeroptimize +0 -13
  159. package/botrun-c/.firebase/hosting.b3V0.cache +0 -30
  160. package/botrun-c/.firebaserc +0 -5
  161. package/botrun-c/.gcloudignore +0 -56
  162. package/botrun-c/.ruby-version +0 -1
  163. package/botrun-c/CHANGELOG.md +0 -224
  164. package/botrun-c/Dockerfile.gcp +0 -365
  165. package/botrun-c/Dockerfile.slim +0 -195
  166. package/botrun-c/Gemfile +0 -8
  167. package/botrun-c/Gemfile.lock +0 -233
  168. package/botrun-c/LOCAL-DEVELOPMENT-GUIDE.md +0 -393
  169. package/botrun-c/PLAN.md +0 -131
  170. package/botrun-c/README.md +0 -129
  171. package/botrun-c/TODO-CAPACITOR-INTEGRATION.md +0 -482
  172. package/botrun-c/VERIFICATION.md +0 -121
  173. package/botrun-c/admin/RATE_LIMIT_MANAGEMENT.md +0 -312
  174. package/botrun-c/admin/README.md +0 -338
  175. package/botrun-c/admin/botrun-billing-report-1765235244038.csv +0 -22
  176. package/botrun-c/admin/botrun-billing-report-1765235323089.csv +0 -22
  177. package/botrun-c/admin/botrun-dashboard-1765235244039.html +0 -278
  178. package/botrun-c/admin/botrun-dashboard-1765235323089.html +0 -278
  179. package/botrun-c/admin/botrun-timeseries-074xGzIKWyfKTBt1NMIj9lxi5mO2-1765235244039.html +0 -161
  180. package/botrun-c/admin/botrun-timeseries-074xGzIKWyfKTBt1NMIj9lxi5mO2-1765235323090.html +0 -161
  181. package/botrun-c/admin/check-billing.ts +0 -48
  182. package/botrun-c/admin/count-all-users.ts +0 -65
  183. package/botrun-c/admin/generate-analytics-report.ts +0 -188
  184. package/botrun-c/admin/jest.config.ts +0 -43
  185. package/botrun-c/admin/lib/__tests__/formatters.test.ts +0 -264
  186. package/botrun-c/admin/lib/__tests__/mocks/firebase.mock.ts +0 -112
  187. package/botrun-c/admin/lib/__tests__/services/UserService.test.ts +0 -83
  188. package/botrun-c/admin/lib/__tests__/user-resolver.test.ts +0 -439
  189. package/botrun-c/admin/lib/analyzers/BillingAnalyticsService.ts +0 -242
  190. package/botrun-c/admin/lib/analyzers/CsvExporter.ts +0 -263
  191. package/botrun-c/admin/lib/analyzers/HtmlChartGenerator.ts +0 -530
  192. package/botrun-c/admin/lib/analyzers/SpikeAnalyzer.ts +0 -608
  193. package/botrun-c/admin/lib/analyzers/TimeSeriesAnalyzer.ts +0 -344
  194. package/botrun-c/admin/lib/cli/CLIBootstrapper.ts +0 -40
  195. package/botrun-c/admin/lib/commands/AbstractCommand.ts +0 -69
  196. package/botrun-c/admin/lib/commands/AnalyticsCommand.ts +0 -215
  197. package/botrun-c/admin/lib/commands/ListCommand.ts +0 -85
  198. package/botrun-c/admin/lib/commands/QueryCommand.ts +0 -81
  199. package/botrun-c/admin/lib/commands/SpikeCommand.ts +0 -83
  200. package/botrun-c/admin/lib/domain/User.ts +0 -38
  201. package/botrun-c/admin/lib/firebase.ts +0 -92
  202. package/botrun-c/admin/lib/firestore-safe.ts +0 -105
  203. package/botrun-c/admin/lib/formatters.ts +0 -154
  204. package/botrun-c/admin/lib/rate-limit-detailed.ts +0 -355
  205. package/botrun-c/admin/lib/rate-limit.ts +0 -286
  206. package/botrun-c/admin/lib/repositories/UserRepository.ts +0 -104
  207. package/botrun-c/admin/lib/services/UserService.ts +0 -108
  208. package/botrun-c/admin/lib/types.ts +0 -56
  209. package/botrun-c/admin/lib/user-resolver.ts +0 -275
  210. package/botrun-c/admin/manage-auth-domains.js +0 -178
  211. package/botrun-c/admin/migrate-plan-free.sh +0 -64
  212. package/botrun-c/admin/package-lock.json +0 -5656
  213. package/botrun-c/admin/package.json +0 -28
  214. package/botrun-c/admin/rate-limit.sh +0 -55
  215. package/botrun-c/admin/remove-custom-limits.ts +0 -75
  216. package/botrun-c/admin/reset-rate.sh +0 -70
  217. package/botrun-c/admin/reset-trial.sh +0 -68
  218. package/botrun-c/admin/seed-trial-data.sh +0 -38
  219. package/botrun-c/admin/trial.sh +0 -55
  220. package/botrun-c/admin/tsconfig.json +0 -20
  221. package/botrun-c/admin/users-cli.py +0 -298
  222. package/botrun-c/admin/users.sh +0 -12
  223. package/botrun-c/admin/users.ts +0 -1321
  224. package/botrun-c/api/admin-tools/check-whitelist.mjs.migration-bak +0 -74
  225. package/botrun-c/api/admin-tools/create-user.mjs +0 -92
  226. package/botrun-c/api/admin-tools/create-user.mjs.migration-bak +0 -92
  227. package/botrun-c/api/admin-tools/firebase-auth-domains-v2.py +0 -287
  228. package/botrun-c/api/admin-tools/firebase-auth-domains-v2.py.migration-bak +0 -287
  229. package/botrun-c/api/admin-tools/init-firestore.mjs +0 -75
  230. package/botrun-c/api/admin-tools/init-firestore.mjs.migration-bak +0 -101
  231. package/botrun-c/api/admin-tools/list-users.mjs +0 -43
  232. package/botrun-c/api/admin-tools/list-users.mjs.migration-bak +0 -43
  233. package/botrun-c/api/admin-tools/manage-authorized-domains.ts +0 -255
  234. package/botrun-c/api/admin-tools/manage-authorized-domains.ts.migration-bak +0 -255
  235. package/botrun-c/api/admin-tools/setup-custom-claims.ts.migration-bak +0 -155
  236. package/botrun-c/api/admin-tools/test-claims.mjs +0 -69
  237. package/botrun-c/api/admin-tools/test-claims.mjs.migration-bak +0 -69
  238. package/botrun-c/api/admin-tools/update-service-urls-gcloud.mjs +0 -72
  239. package/botrun-c/api/admin-tools/update-service-urls-gcloud.mjs.migration-bak +0 -72
  240. package/botrun-c/api/admin-tools/update-service-urls-to-lb.mjs +0 -159
  241. package/botrun-c/api/admin-tools/update-service-urls-to-lb.mjs.migration-bak +0 -159
  242. package/botrun-c/api/admin-tools/update-service-urls.mjs +0 -82
  243. package/botrun-c/api/admin-tools/update-service-urls.mjs.migration-bak +0 -82
  244. package/botrun-c/api/package-lock.json +0 -5031
  245. package/botrun-c/api/package.json +0 -63
  246. package/botrun-c/api/pnpm-lock.yaml +0 -4157
  247. package/botrun-c/api/rate-limit-export-1763620678737.csv +0 -79
  248. package/botrun-c/api/scripts/README-migrate-plan-free.md +0 -197
  249. package/botrun-c/api/scripts/check-users.ts +0 -45
  250. package/botrun-c/api/scripts/delete-firestore-docs.js +0 -43
  251. package/botrun-c/api/scripts/dump-subscriptions.ts +0 -395
  252. package/botrun-c/api/scripts/list-all-users.ts +0 -65
  253. package/botrun-c/api/scripts/migrate-plan-free-rpd-16to32.ts +0 -262
  254. package/botrun-c/api/scripts/migrate-plan-names-to-lite-max.ts +0 -269
  255. package/botrun-c/api/scripts/query-user-info.ts +0 -304
  256. package/botrun-c/api/scripts/reset-rate-limit.ts +0 -206
  257. package/botrun-c/api/scripts/reset-trial-subscription.ts +0 -166
  258. package/botrun-c/api/scripts/rollback-plan-names-from-lite-max.ts +0 -139
  259. package/botrun-c/api/scripts/seed-rate-limit-test-data.ts +0 -114
  260. package/botrun-c/api/scripts/seed-subscription-test-data.ts +0 -173
  261. package/botrun-c/api/scripts/test-rate-limit-message.ts +0 -108
  262. package/botrun-c/api/test-billing-write.ts +0 -103
  263. package/botrun-c/api/test-billing.ts +0 -81
  264. package/botrun-c/api/test-chat-upload.sh +0 -162
  265. package/botrun-c/api/test-nchc-whisper.ts +0 -87
  266. package/botrun-c/api/test-skills-loading.js +0 -77
  267. package/botrun-c/api/test-skills-symlink.js +0 -118
  268. package/botrun-c/api/test-upload-multimodal.sh +0 -167
  269. package/botrun-c/api/tsconfig.json +0 -27
  270. package/botrun-c/api/verify-form-2-1.mjs +0 -229
  271. package/botrun-c/api/vitest.config.ts +0 -9
  272. package/botrun-c/appium-capabilities.json +0 -20
  273. package/botrun-c/cors.json +0 -26
  274. package/botrun-c/delete-old.sh +0 -44
  275. package/botrun-c/design/google-drive/01-architecture.md +0 -186
  276. package/botrun-c/design/google-drive/02-api-design.md +0 -314
  277. package/botrun-c/design/google-drive/03-frontend-design.md +0 -278
  278. package/botrun-c/design/google-drive/04-skill-design.md +0 -384
  279. package/botrun-c/design/google-drive/05-test-plan.md +0 -507
  280. package/botrun-c/design/google-drive/README.md +0 -32
  281. package/botrun-c/dev/mcp-token-calculator/README.md +0 -92
  282. package/botrun-c/dev/mcp-token-calculator/RESULTS.md +0 -188
  283. package/botrun-c/dev/mcp-token-calculator/calculate-current-tokens.ts +0 -369
  284. package/botrun-c/dev/mcp-token-calculator/calculate-mcp-tokens.ts +0 -464
  285. package/botrun-c/dev/mcp-token-calculator/optimization-proposals.ts +0 -316
  286. package/botrun-c/dev/mcp-token-calculator/package-lock.json +0 -599
  287. package/botrun-c/dev/mcp-token-calculator/package.json +0 -22
  288. package/botrun-c/dev/mcp-token-calculator/test-all-schemas.ts +0 -314
  289. package/botrun-c/dev/mcp-token-calculator/test-schema-correctness.ts +0 -221
  290. package/botrun-c/dev/mcp-token-calculator/verify-optimization.ts +0 -159
  291. package/botrun-c/fastlane/ANDROID-DEPLOYMENT-SETUP.md +0 -825
  292. package/botrun-c/fastlane/IOS-DEPLOYMENT-SETUP.md +0 -431
  293. package/botrun-c/fastlane/Pluginfile +0 -5
  294. package/botrun-c/fastlane/QUICK-START.md +0 -133
  295. package/botrun-c/fastlane/README.md +0 -424
  296. package/botrun-c/fastlane/RUBY-SETUP.md +0 -449
  297. package/botrun-c/fastlane/android/Appfile +0 -11
  298. package/botrun-c/fastlane/android/Fastfile +0 -240
  299. package/botrun-c/firebase.json +0 -46
  300. package/botrun-c/firestore-init-data.json +0 -40
  301. package/botrun-c/firestore.indexes.json +0 -55
  302. package/botrun-c/firestore.rules +0 -53
  303. package/botrun-c/fix-codex-permissions.sh +0 -56
  304. package/botrun-c/gcs-lifecycle-30day-retention.json +0 -12
  305. package/botrun-c/html-architecture/architecture/deployment.html +0 -664
  306. package/botrun-c/html-architecture/architecture/index.html +0 -309
  307. package/botrun-c/html-architecture/architecture/styles.css +0 -500
  308. package/botrun-c/html-architecture/architecture/system-components.html +0 -538
  309. package/botrun-c/html-architecture/architecture/user-flow.html +0 -370
  310. package/botrun-c/mobile-android/FIREBASE-AUTH-SETUP.md +0 -302
  311. package/botrun-c/mobile-android/WSL-ANDROID-SETUP.md +0 -252
  312. package/botrun-c/mobile-android/android/app/build.gradle +0 -75
  313. package/botrun-c/mobile-android/android/app/capacitor.build.gradle +0 -31
  314. package/botrun-c/mobile-android/android/app/proguard-rules.pro +0 -21
  315. package/botrun-c/mobile-android/android/build.gradle +0 -29
  316. package/botrun-c/mobile-android/android/capacitor.settings.gradle +0 -42
  317. package/botrun-c/mobile-android/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  318. package/botrun-c/mobile-android/android/gradle/wrapper/gradle-wrapper.properties +0 -7
  319. package/botrun-c/mobile-android/android/gradle.properties +0 -22
  320. package/botrun-c/mobile-android/android/gradlew +0 -252
  321. package/botrun-c/mobile-android/android/gradlew.bat +0 -94
  322. package/botrun-c/mobile-android/android/settings.gradle +0 -5
  323. package/botrun-c/mobile-android/android/variables.gradle +0 -16
  324. package/botrun-c/mobile-android/capacitor.config.ts +0 -44
  325. package/botrun-c/mobile-android/fastlane/Appfile +0 -11
  326. package/botrun-c/mobile-android/fastlane/Fastfile +0 -202
  327. package/botrun-c/mobile-android/fastlane/README.md +0 -96
  328. package/botrun-c/mobile-android/google-services.json.template +0 -39
  329. package/botrun-c/mobile-android/package.json +0 -32
  330. package/botrun-c/mobile-android/resources/icon.png +0 -0
  331. package/botrun-c/mobile-android/tsconfig.json +0 -15
  332. package/botrun-c/mobile-ios/.ruby-version +0 -1
  333. package/botrun-c/mobile-ios/BUILD-LOG.md +0 -82
  334. package/botrun-c/mobile-ios/Gemfile +0 -4
  335. package/botrun-c/mobile-ios/Gemfile.lock +0 -299
  336. package/botrun-c/mobile-ios/GoogleService-Info.plist.template +0 -34
  337. package/botrun-c/mobile-ios/IOS-PERMISSIONS.md +0 -80
  338. package/botrun-c/mobile-ios/QUICK-START.md +0 -189
  339. package/botrun-c/mobile-ios/README.md +0 -231
  340. package/botrun-c/mobile-ios/capacitor.config.ts +0 -46
  341. package/botrun-c/mobile-ios/fastlane/Fastfile +0 -326
  342. package/botrun-c/mobile-ios/fastlane/README.md +0 -88
  343. package/botrun-c/mobile-ios/ios/App/App/App.entitlements +0 -10
  344. package/botrun-c/mobile-ios/ios/App/App/AppDelegate.swift +0 -49
  345. package/botrun-c/mobile-ios/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png +0 -0
  346. package/botrun-c/mobile-ios/ios/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json +0 -14
  347. package/botrun-c/mobile-ios/ios/App/App/Assets.xcassets/Contents.json +0 -6
  348. package/botrun-c/mobile-ios/ios/App/App/Assets.xcassets/Splash.imageset/Contents.json +0 -56
  349. package/botrun-c/mobile-ios/ios/App/App/Assets.xcassets/Splash.imageset/Default@1x~universal~anyany-dark.png +0 -0
  350. package/botrun-c/mobile-ios/ios/App/App/Assets.xcassets/Splash.imageset/Default@1x~universal~anyany.png +0 -0
  351. package/botrun-c/mobile-ios/ios/App/App/Assets.xcassets/Splash.imageset/Default@2x~universal~anyany-dark.png +0 -0
  352. package/botrun-c/mobile-ios/ios/App/App/Assets.xcassets/Splash.imageset/Default@2x~universal~anyany.png +0 -0
  353. package/botrun-c/mobile-ios/ios/App/App/Assets.xcassets/Splash.imageset/Default@3x~universal~anyany-dark.png +0 -0
  354. package/botrun-c/mobile-ios/ios/App/App/Assets.xcassets/Splash.imageset/Default@3x~universal~anyany.png +0 -0
  355. package/botrun-c/mobile-ios/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-1.png +0 -0
  356. package/botrun-c/mobile-ios/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-2.png +0 -0
  357. package/botrun-c/mobile-ios/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732.png +0 -0
  358. package/botrun-c/mobile-ios/ios/App/App/Base.lproj/LaunchScreen.storyboard +0 -32
  359. package/botrun-c/mobile-ios/ios/App/App/Base.lproj/Main.storyboard +0 -19
  360. package/botrun-c/mobile-ios/ios/App/App/Info.plist +0 -72
  361. package/botrun-c/mobile-ios/ios/App/App.xcodeproj/project.pbxproj +0 -446
  362. package/botrun-c/mobile-ios/ios/App/App.xcodeproj/xcshareddata/xcschemes/App.xcscheme +0 -78
  363. package/botrun-c/mobile-ios/ios/App/App.xcworkspace/contents.xcworkspacedata +0 -10
  364. package/botrun-c/mobile-ios/ios/App/App.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
  365. package/botrun-c/mobile-ios/ios/App/Podfile +0 -52
  366. package/botrun-c/mobile-ios/ios/App/Podfile.lock +0 -205
  367. package/botrun-c/mobile-ios/ios/App/add_plist.rb +0 -30
  368. package/botrun-c/mobile-ios/ios/App/fix_plist_path.rb +0 -22
  369. package/botrun-c/mobile-ios/package.json +0 -29
  370. package/botrun-c/mobile-ios/resources/icon.png +0 -0
  371. package/botrun-c/mobile-ios/run-simulator.sh +0 -34
  372. package/botrun-c/mobile-ios/screenshot-after-clean-rebuild.png +0 -0
  373. package/botrun-c/mobile-ios/screenshot-final-version.png +0 -0
  374. package/botrun-c/mobile-ios/screenshot-simulator.png +0 -0
  375. package/botrun-c/mobile-ios/screenshot-version-1009.png +0 -0
  376. package/botrun-c/mobile-ios/screenshot-with-version.png +0 -0
  377. package/botrun-c/mobile-ios/screenshot-working.png +0 -0
  378. package/botrun-c/mobile-ios/setup-ios.sh +0 -106
  379. package/botrun-c/mobile-ios/tsconfig.json +0 -16
  380. package/botrun-c/org-policy-allow-all-users.yaml +0 -3
  381. package/botrun-c/package.json +0 -68
  382. package/botrun-c/pnpm-lock.yaml +0 -10198
  383. package/botrun-c/pnpm-workspace.yaml +0 -6
  384. package/botrun-c/prompts/BOTRUN-1.md +0 -13
  385. package/botrun-c/prompts/BOTRUN-2.md +0 -70
  386. package/botrun-c/prompts/BOTRUN-3.md +0 -139
  387. package/botrun-c/prototypes/firestore-gax-debug/package-lock.json +0 -1204
  388. package/botrun-c/prototypes/firestore-gax-debug/package.json +0 -12
  389. package/botrun-c/prototypes/firestore-gax-debug/pnpm-workspace.yaml +0 -2
  390. package/botrun-c/prototypes/firestore-gax-debug/test.js +0 -98
  391. package/botrun-c/prototypes/taiwan-haiku/README.md +0 -93
  392. package/botrun-c/prototypes/taiwan-haiku/index.ts +0 -139
  393. package/botrun-c/prototypes/taiwan-haiku/package.json +0 -23
  394. package/botrun-c/prototypes/taiwan-haiku/pnpm-lock.yaml +0 -730
  395. package/botrun-c/prototypes/taiwan-haiku/test-vertex-direct.ts +0 -83
  396. package/botrun-c/prototypes/taiwan-haiku/tsconfig.json +0 -18
  397. package/botrun-c/prototypes/web-search/FINDINGS-STAGE-1.md +0 -48
  398. package/botrun-c/prototypes/web-search/README.md +0 -46
  399. package/botrun-c/prototypes/web-search/VERIFICATION-REPORT.md +0 -310
  400. package/botrun-c/prototypes/web-search/package-lock.json +0 -595
  401. package/botrun-c/prototypes/web-search/package.json +0 -18
  402. package/botrun-c/prototypes/web-search/stage-1.1-basic-connection.js +0 -123
  403. package/botrun-c/prototypes/web-search/stage-1.2-with-websearch.js +0 -154
  404. package/botrun-c/prototypes/web-search/stage-1.3-search-query.js +0 -192
  405. package/botrun-c/prototypes/web-search/stage-2-agent-sdk-websearch.js +0 -265
  406. package/botrun-c/runtime-scripts/env-detect.sh +0 -64
  407. package/botrun-c/scripts/github-collaborator-manager.sh +0 -297
  408. package/botrun-c/scripts/github-collaborator-manager.ts +0 -325
  409. package/botrun-c/scripts/manage-collaborators.sh +0 -91
  410. package/botrun-c/scripts/setup-secrets.sh +0 -202
  411. package/botrun-c/scripts/stress-test-17-users.sh +0 -113
  412. package/botrun-c/scripts/stress-test-api.sh +0 -119
  413. package/botrun-c/scripts/stress-test-full-report.sh +0 -385
  414. package/botrun-c/specs/infra-vm/design.md +0 -0
  415. package/botrun-c/specs/share-skill/design.md +0 -1146
  416. package/botrun-c/specs/share-skill/tasks.md +0 -444
  417. package/botrun-c/stress-test-results/20260121-162630/config.json +0 -7
  418. package/botrun-c/stress-test-results/20260121-162630/user-1-status.txt +0 -1
  419. package/botrun-c/stress-test-results/20260121-162630/user-1-timing.txt +0 -1
  420. package/botrun-c/stress-test-results/20260121-162630/user-1.json +0 -2
  421. package/botrun-c/stress-test-results/20260121-162646/config.json +0 -7
  422. package/botrun-c/stress-test-results/20260121-162646/user-1-status.txt +0 -1
  423. package/botrun-c/stress-test-results/20260121-162646/user-1-timing.txt +0 -1
  424. package/botrun-c/stress-test-results/20260121-162646/user-1.json +0 -2
  425. package/botrun-c/stress-test-results/20260121-162646/user-10-status.txt +0 -1
  426. package/botrun-c/stress-test-results/20260121-162646/user-10-timing.txt +0 -1
  427. package/botrun-c/stress-test-results/20260121-162646/user-10.json +0 -2
  428. package/botrun-c/stress-test-results/20260121-162646/user-11-status.txt +0 -1
  429. package/botrun-c/stress-test-results/20260121-162646/user-11-timing.txt +0 -1
  430. package/botrun-c/stress-test-results/20260121-162646/user-11.json +0 -2
  431. package/botrun-c/stress-test-results/20260121-162646/user-12-status.txt +0 -1
  432. package/botrun-c/stress-test-results/20260121-162646/user-12-timing.txt +0 -1
  433. package/botrun-c/stress-test-results/20260121-162646/user-12.json +0 -2
  434. package/botrun-c/stress-test-results/20260121-162646/user-13-status.txt +0 -1
  435. package/botrun-c/stress-test-results/20260121-162646/user-13-timing.txt +0 -1
  436. package/botrun-c/stress-test-results/20260121-162646/user-13.json +0 -2
  437. package/botrun-c/stress-test-results/20260121-162646/user-14-status.txt +0 -1
  438. package/botrun-c/stress-test-results/20260121-162646/user-14-timing.txt +0 -1
  439. package/botrun-c/stress-test-results/20260121-162646/user-14.json +0 -2
  440. package/botrun-c/stress-test-results/20260121-162646/user-15-status.txt +0 -1
  441. package/botrun-c/stress-test-results/20260121-162646/user-15-timing.txt +0 -1
  442. package/botrun-c/stress-test-results/20260121-162646/user-15.json +0 -2
  443. package/botrun-c/stress-test-results/20260121-162646/user-16-status.txt +0 -1
  444. package/botrun-c/stress-test-results/20260121-162646/user-16-timing.txt +0 -1
  445. package/botrun-c/stress-test-results/20260121-162646/user-16.json +0 -2
  446. package/botrun-c/stress-test-results/20260121-162646/user-17-status.txt +0 -1
  447. package/botrun-c/stress-test-results/20260121-162646/user-17-timing.txt +0 -1
  448. package/botrun-c/stress-test-results/20260121-162646/user-17.json +0 -2
  449. package/botrun-c/stress-test-results/20260121-162646/user-2-status.txt +0 -1
  450. package/botrun-c/stress-test-results/20260121-162646/user-2-timing.txt +0 -1
  451. package/botrun-c/stress-test-results/20260121-162646/user-2.json +0 -2
  452. package/botrun-c/stress-test-results/20260121-162646/user-3-status.txt +0 -1
  453. package/botrun-c/stress-test-results/20260121-162646/user-3-timing.txt +0 -1
  454. package/botrun-c/stress-test-results/20260121-162646/user-3.json +0 -2
  455. package/botrun-c/stress-test-results/20260121-162646/user-4-status.txt +0 -1
  456. package/botrun-c/stress-test-results/20260121-162646/user-4-timing.txt +0 -1
  457. package/botrun-c/stress-test-results/20260121-162646/user-4.json +0 -2
  458. package/botrun-c/stress-test-results/20260121-162646/user-5-status.txt +0 -1
  459. package/botrun-c/stress-test-results/20260121-162646/user-5-timing.txt +0 -1
  460. package/botrun-c/stress-test-results/20260121-162646/user-5.json +0 -2
  461. package/botrun-c/stress-test-results/20260121-162646/user-6-status.txt +0 -1
  462. package/botrun-c/stress-test-results/20260121-162646/user-6-timing.txt +0 -1
  463. package/botrun-c/stress-test-results/20260121-162646/user-6.json +0 -2
  464. package/botrun-c/stress-test-results/20260121-162646/user-7-status.txt +0 -1
  465. package/botrun-c/stress-test-results/20260121-162646/user-7-timing.txt +0 -1
  466. package/botrun-c/stress-test-results/20260121-162646/user-7.json +0 -2
  467. package/botrun-c/stress-test-results/20260121-162646/user-8-status.txt +0 -1
  468. package/botrun-c/stress-test-results/20260121-162646/user-8-timing.txt +0 -1
  469. package/botrun-c/stress-test-results/20260121-162646/user-8.json +0 -2
  470. package/botrun-c/stress-test-results/20260121-162646/user-9-status.txt +0 -1
  471. package/botrun-c/stress-test-results/20260121-162646/user-9-timing.txt +0 -1
  472. package/botrun-c/stress-test-results/20260121-162646/user-9.json +0 -2
  473. package/botrun-c/stress-test-results/20260121-163148/REPORT.html +0 -344
  474. package/botrun-c/stress-test-results/20260121-163148/REPORT.md +0 -93
  475. package/botrun-c/stress-test-results/20260121-163148/REPORT.pdf +0 -0
  476. package/botrun-c/stress-test-results/20260121-163148/config.json +0 -7
  477. package/botrun-c/stress-test-results/20260121-163148/summary.json +0 -26
  478. package/botrun-c/stress-test-results/20260121-163148/user-1-status.txt +0 -1
  479. package/botrun-c/stress-test-results/20260121-163148/user-1-timing.txt +0 -1
  480. package/botrun-c/stress-test-results/20260121-163148/user-1.json +0 -206
  481. package/botrun-c/stress-test-results/20260121-163148/user-10-status.txt +0 -1
  482. package/botrun-c/stress-test-results/20260121-163148/user-10-timing.txt +0 -1
  483. package/botrun-c/stress-test-results/20260121-163148/user-10.json +0 -110
  484. package/botrun-c/stress-test-results/20260121-163148/user-11-status.txt +0 -1
  485. package/botrun-c/stress-test-results/20260121-163148/user-11-timing.txt +0 -1
  486. package/botrun-c/stress-test-results/20260121-163148/user-11.json +0 -104
  487. package/botrun-c/stress-test-results/20260121-163148/user-12-status.txt +0 -1
  488. package/botrun-c/stress-test-results/20260121-163148/user-12-timing.txt +0 -1
  489. package/botrun-c/stress-test-results/20260121-163148/user-12.json +0 -98
  490. package/botrun-c/stress-test-results/20260121-163148/user-13-status.txt +0 -1
  491. package/botrun-c/stress-test-results/20260121-163148/user-13-timing.txt +0 -1
  492. package/botrun-c/stress-test-results/20260121-163148/user-13.json +0 -68
  493. package/botrun-c/stress-test-results/20260121-163148/user-14-status.txt +0 -1
  494. package/botrun-c/stress-test-results/20260121-163148/user-14-timing.txt +0 -1
  495. package/botrun-c/stress-test-results/20260121-163148/user-14.json +0 -116
  496. package/botrun-c/stress-test-results/20260121-163148/user-15-status.txt +0 -1
  497. package/botrun-c/stress-test-results/20260121-163148/user-15-timing.txt +0 -1
  498. package/botrun-c/stress-test-results/20260121-163148/user-15.json +0 -131
  499. package/botrun-c/stress-test-results/20260121-163148/user-16-status.txt +0 -1
  500. package/botrun-c/stress-test-results/20260121-163148/user-16-timing.txt +0 -1
  501. package/botrun-c/stress-test-results/20260121-163148/user-16.json +0 -110
  502. package/botrun-c/stress-test-results/20260121-163148/user-17-status.txt +0 -1
  503. package/botrun-c/stress-test-results/20260121-163148/user-17-timing.txt +0 -1
  504. package/botrun-c/stress-test-results/20260121-163148/user-17.json +0 -116
  505. package/botrun-c/stress-test-results/20260121-163148/user-2-status.txt +0 -1
  506. package/botrun-c/stress-test-results/20260121-163148/user-2-timing.txt +0 -1
  507. package/botrun-c/stress-test-results/20260121-163148/user-2.json +0 -98
  508. package/botrun-c/stress-test-results/20260121-163148/user-3-status.txt +0 -1
  509. package/botrun-c/stress-test-results/20260121-163148/user-3-timing.txt +0 -1
  510. package/botrun-c/stress-test-results/20260121-163148/user-3.json +0 -62
  511. package/botrun-c/stress-test-results/20260121-163148/user-4-status.txt +0 -1
  512. package/botrun-c/stress-test-results/20260121-163148/user-4-timing.txt +0 -1
  513. package/botrun-c/stress-test-results/20260121-163148/user-4.json +0 -113
  514. package/botrun-c/stress-test-results/20260121-163148/user-5-status.txt +0 -1
  515. package/botrun-c/stress-test-results/20260121-163148/user-5-timing.txt +0 -1
  516. package/botrun-c/stress-test-results/20260121-163148/user-5.json +0 -83
  517. package/botrun-c/stress-test-results/20260121-163148/user-6-status.txt +0 -1
  518. package/botrun-c/stress-test-results/20260121-163148/user-6-timing.txt +0 -1
  519. package/botrun-c/stress-test-results/20260121-163148/user-6.json +0 -125
  520. package/botrun-c/stress-test-results/20260121-163148/user-7-status.txt +0 -1
  521. package/botrun-c/stress-test-results/20260121-163148/user-7-timing.txt +0 -1
  522. package/botrun-c/stress-test-results/20260121-163148/user-7.json +0 -62
  523. package/botrun-c/stress-test-results/20260121-163148/user-8-status.txt +0 -1
  524. package/botrun-c/stress-test-results/20260121-163148/user-8-timing.txt +0 -1
  525. package/botrun-c/stress-test-results/20260121-163148/user-8.json +0 -41
  526. package/botrun-c/stress-test-results/20260121-163148/user-9-status.txt +0 -1
  527. package/botrun-c/stress-test-results/20260121-163148/user-9-timing.txt +0 -1
  528. package/botrun-c/stress-test-results/20260121-163148/user-9.json +0 -53
  529. package/botrun-c/test-merge.md +0 -1
  530. package/botrun-c/version.txt +0 -1
  531. package/botrun-c/web/.version +0 -1
  532. package/botrun-c/web/ALLOW-PUBLIC-ACCESS.md +0 -57
  533. package/botrun-c/web/FINAL-SETUP-STEPS.md +0 -71
  534. package/botrun-c/web/FIREBASE-MANUAL-STEPS.md +0 -41
  535. package/botrun-c/web/QUICK-SETUP-HELPER.md +0 -79
  536. package/botrun-c/web/README.md +0 -162
  537. package/botrun-c/web/__tests__/image-extractor.test.ts +0 -147
  538. package/botrun-c/web/__tests__/useGitHub.test.ts +0 -245
  539. package/botrun-c/web/app/favicon.ico +0 -0
  540. package/botrun-c/web/app/globals.css +0 -215
  541. package/botrun-c/web/app/image-preview/page.tsx +0 -213
  542. package/botrun-c/web/app/layout.tsx +0 -46
  543. package/botrun-c/web/app/page.tsx +0 -45
  544. package/botrun-c/web/components/CapacitorProvider.tsx +0 -43
  545. package/botrun-c/web/components/ChatPage.tsx +0 -1736
  546. package/botrun-c/web/components/ConversationList.tsx +0 -237
  547. package/botrun-c/web/components/ConversationListItem.tsx +0 -121
  548. package/botrun-c/web/components/LoginPage.tsx +0 -212
  549. package/botrun-c/web/components/Providers.tsx +0 -12
  550. package/botrun-c/web/components/StatusBarProvider.tsx +0 -16
  551. package/botrun-c/web/components/attachment-preview.tsx +0 -193
  552. package/botrun-c/web/components/botrun-incubator/BotrunIncubator.tsx +0 -266
  553. package/botrun-c/web/components/botrun-incubator/index.ts +0 -1
  554. package/botrun-c/web/components/chat-input.tsx +0 -1251
  555. package/botrun-c/web/components/chat-message.tsx +0 -598
  556. package/botrun-c/web/components/drop-zone.tsx +0 -143
  557. package/botrun-c/web/components/gdrive/gdrive-add-dialog.tsx +0 -254
  558. package/botrun-c/web/components/gdrive/gdrive-drawer.tsx +0 -236
  559. package/botrun-c/web/components/gdrive/gdrive-selector.tsx +0 -226
  560. package/botrun-c/web/components/gdrive/index.ts +0 -7
  561. package/botrun-c/web/components/generated-image-card.tsx +0 -363
  562. package/botrun-c/web/components/github/GitHubConnect.tsx +0 -366
  563. package/botrun-c/web/components/github/index.ts +0 -2
  564. package/botrun-c/web/components/import-skill-dialog.tsx +0 -208
  565. package/botrun-c/web/components/scroll-to-bottom-button.tsx +0 -90
  566. package/botrun-c/web/components/share-skill-dialog.tsx +0 -175
  567. package/botrun-c/web/components/skills-editor.tsx +0 -443
  568. package/botrun-c/web/components/skills-manager-drawer.tsx +0 -586
  569. package/botrun-c/web/components/smart-button-group.tsx +0 -132
  570. package/botrun-c/web/config/smart-buttons.ts +0 -295
  571. package/botrun-c/web/config/welcome-messages.ts +0 -29
  572. package/botrun-c/web/contexts/AuthContext.tsx +0 -220
  573. package/botrun-c/web/hooks/use-toast.ts +0 -187
  574. package/botrun-c/web/hooks/useAppStoreUpdate.ts +0 -356
  575. package/botrun-c/web/hooks/useAppVersion.ts +0 -113
  576. package/botrun-c/web/hooks/useAttachments.ts +0 -269
  577. package/botrun-c/web/hooks/useAuthToken.ts +0 -59
  578. package/botrun-c/web/hooks/useAutoWelcome.ts +0 -89
  579. package/botrun-c/web/hooks/useCapacitorPlatform.ts +0 -113
  580. package/botrun-c/web/hooks/useCapawesomeLiveUpdate.ts +0 -149
  581. package/botrun-c/web/hooks/useClaudeModel.ts +0 -118
  582. package/botrun-c/web/hooks/useConversation.ts +0 -115
  583. package/botrun-c/web/hooks/useConversations.ts +0 -235
  584. package/botrun-c/web/hooks/useGdriveFolders.ts +0 -199
  585. package/botrun-c/web/hooks/useGdriveSync.ts +0 -107
  586. package/botrun-c/web/hooks/useGitHub.ts +0 -409
  587. package/botrun-c/web/hooks/useScrollToBottom.ts +0 -150
  588. package/botrun-c/web/hooks/useStatusBar.ts +0 -42
  589. package/botrun-c/web/hooks/useUserPlan.ts +0 -153
  590. package/botrun-c/web/lib/api-config.ts +0 -162
  591. package/botrun-c/web/lib/api-url.ts +0 -62
  592. package/botrun-c/web/lib/api.ts +0 -105
  593. package/botrun-c/web/lib/attachment-utils.ts +0 -168
  594. package/botrun-c/web/lib/build-version.ts +0 -11
  595. package/botrun-c/web/lib/clipboard-utils.ts +0 -199
  596. package/botrun-c/web/lib/constants.ts +0 -62
  597. package/botrun-c/web/lib/conversation-storage.ts +0 -324
  598. package/botrun-c/web/lib/firebase-capacitor.ts +0 -135
  599. package/botrun-c/web/lib/firebase.ts +0 -137
  600. package/botrun-c/web/lib/firestore-auth.ts +0 -61
  601. package/botrun-c/web/lib/firestore-auth.ts.migration-bak +0 -319
  602. package/botrun-c/web/lib/image-extractor.ts +0 -156
  603. package/botrun-c/web/lib/path-utils.ts +0 -48
  604. package/botrun-c/web/lib/rehype-del-to-mark.ts +0 -19
  605. package/botrun-c/web/lib/upload-service.ts +0 -326
  606. package/botrun-c/web/lib/utils.ts +0 -6
  607. package/botrun-c/web/next.config.js +0 -49
  608. package/botrun-c/web/package-lock.json +0 -6711
  609. package/botrun-c/web/package.json +0 -63
  610. package/botrun-c/web/postcss.config.js +0 -6
  611. package/botrun-c/web/public/apple_icon.png +0 -0
  612. package/botrun-c/web/public/google_icon.png +0 -0
  613. package/botrun-c/web/public/logo-48-small.png +0 -0
  614. package/botrun-c/web/public/logo-48.png +0 -0
  615. package/botrun-c/web/scripts/generate-version.js +0 -36
  616. package/botrun-c/web/scripts/update-build-version.js +0 -41
  617. package/botrun-c/web/tailwind.config.ts +0 -80
  618. package/botrun-c/web/test-welcome-message.mjs +0 -115
  619. package/botrun-c/web/tsconfig.json +0 -42
  620. package/botrun-c/web/types/agent.ts +0 -6
  621. package/botrun-c/web/types/attachment.ts +0 -39
  622. package/botrun-c/web/types/conversation.ts +0 -68
  623. package/botrun-c/web/types/flutter.d.ts +0 -6
  624. package/botrun-c/web/types/project.ts +0 -41
  625. package/botrun-c/web/types/skills.ts +0 -13
  626. package/botrun-c/web//350/250/272/346/226/267-Skills-/345/211/215/347/253/257/351/241/257/347/244/272/345/225/217/351/241/214.md +0 -83
  627. package/lib/writing/index.mjs +0 -5
  628. /package/{lib/core → modules/_core}/adapters/base.mjs +0 -0
  629. /package/{lib/core → modules/_core}/adapters/claude.mjs +0 -0
  630. /package/{lib/core → modules/_core}/adapters/gemini-api.mjs +0 -0
  631. /package/{lib/core → modules/_core}/adapters/gemini-shared.mjs +0 -0
  632. /package/{lib/core → modules/_core}/adapters/gemini-vertex.mjs +0 -0
  633. /package/{lib/core → modules/_core}/adapters/local.mjs +0 -0
  634. /package/{lib/core → modules/_core}/adapters/nchc.mjs +0 -0
  635. /package/{lib/core → modules/_core}/adapters/openai-shared.mjs +0 -0
  636. /package/{lib/core → modules/_core}/adapters/openrouter.mjs +0 -0
  637. /package/{lib/core → modules/_core}/ai-cache.mjs +0 -0
  638. /package/{lib/core → modules/_core}/ai-router.mjs +0 -0
  639. /package/{lib/core → modules/_core}/cli-utils.mjs +0 -0
  640. /package/{lib/core → modules/_core}/dag.mjs +0 -0
  641. /package/{lib/core → modules/_core}/db.mjs +0 -0
  642. /package/{lib/core → modules/_core}/env.mjs +0 -0
  643. /package/{lib/flows → modules/_core}/hatch-portal.mjs +0 -0
  644. /package/{lib/core → modules/_core}/llm.mjs +0 -0
  645. /package/{lib/flows → modules/_core}/opencode-agent.mjs +0 -0
  646. /package/{lib/core → modules/_core}/paths.mjs +0 -0
  647. /package/{lib/core → modules/_core}/proxy.mjs +0 -0
  648. /package/{lib/flows → modules/_core}/review-doc.mjs +0 -0
  649. /package/{lib → modules/_core}/tools/fs-tools.mjs +0 -0
  650. /package/{lib → modules/_core}/tools/index.mjs +0 -0
  651. /package/{lib/core → modules/_core}/watermelon.mjs +0 -0
  652. /package/{lib/ocr/index.mjs → modules/ocr/ocr-lib.mjs} +0 -0
  653. /package/{lib → modules}/portal/hatch.mjs +0 -0
  654. /package/{lib/portal/index.mjs → modules/portal/portal-lib.mjs} +0 -0
  655. /package/{lib → modules}/search/crawler.mjs +0 -0
  656. /package/{lib/prompt → modules/skill}/prompts/zero-framework/coding.md +0 -0
  657. /package/{lib/prompt → modules/skill}/prompts/zero-framework/fullstack.md +0 -0
  658. /package/{lib/prompt → modules/skill}/prompts/zero-framework/search.md +0 -0
  659. /package/{lib/prompt → modules/skill}/prompts/zero-framework/segment.md +0 -0
  660. /package/{lib/prompt → modules/skill}/prompts/zero-framework/slice.md +0 -0
  661. /package/{lib → modules}/writing/generators/nstc-generators.mjs +0 -0
  662. /package/{lib → modules}/writing/generators/nstc-top5.mjs +0 -0
  663. /package/{lib → modules}/writing/layouts/nstc-layout.mjs +0 -0
  664. /package/{lib → modules}/writing/renderer.mjs +0 -0
@@ -1,1146 +0,0 @@
1
- # 技能分享功能:設計規格
2
-
3
- ## 🎯 系統架構概述
4
-
5
- 技能分享功能允許使用者將自己建立的技能透過簡短的分享 ID 分享給其他人,其他人可以透過輸入分享 ID 將技能 clone 到自己的技能集中。
6
-
7
- ### 核心設計理念
8
-
9
- 1. **確定性 Share ID**:使用 MD5(userId + skillName) 作為分享識別碼,支援智慧覆寫
10
- 2. **Clone 而非 Reference**:匯入的技能是完整複製,使用者可自由修改
11
- 3. **Firestore 儲存**:分享記錄儲存在 Firestore,便於管理和查詢
12
- 4. **Shadcn UI 組件**:使用 Dialog、DropdownMenu、Input 等原生組件實現 RWD
13
-
14
- ---
15
-
16
- ## [D-1] Share ID 設計
17
-
18
- ### 設計說明
19
-
20
- **採用 MD5(userId + skillName) 作為 Share ID**,理由如下:
21
-
22
- 1. **確定性(Deterministic)**:同一使用者的同一技能名稱 → 永遠產生相同的 shareId
23
- 2. **自動覆寫**:修改技能內容後再次分享 → 自動覆寫 Firestore(無需查詢)
24
- 3. **自動版本分支**:改變技能名稱 → 產生新的 shareId(新分享記錄)
25
- 4. **查詢效能極佳**:使用 document ID 查詢,O(1) 時間複雜度
26
- 5. **無需額外索引**:不需要建立 `(ownerId, skillName)` 複合索引
27
-
28
- ### Share ID 生成邏輯
29
-
30
- ```typescript
31
- // api/src/utils/share-id.ts
32
- import crypto from 'crypto';
33
-
34
- export function generateShareId(userId: string, skillName: string): string {
35
- // MD5 完全支援 UTF-8(包含中文)
36
- const content = `${userId}:${skillName}`;
37
- return crypto.createHash('md5').update(content, 'utf-8').digest('hex');
38
- }
39
-
40
- // 範例:
41
- // userId = "abc123"
42
- // skillName = "孵化技能"
43
- // shareId = "7a3f8c2e1b4d9f6a5e8c3d2a1f9b7e4c"(32 字元 hex)
44
- ```
45
-
46
- ### 中文支援
47
-
48
- **問題**:中文可以做 MD5 嗎?
49
- **答案**:✅ 完全支援
50
-
51
- ```typescript
52
- // Node.js crypto 模組自動處理 UTF-8 編碼
53
- crypto.createHash('md5').update('孵化技能', 'utf-8').digest('hex');
54
- // 輸出: "7a3f8c2e1b4d9f6a5e8c3d2a1f9b7e4c"
55
- ```
56
-
57
- ### 分享行為
58
-
59
- | 操作 | shareId | Firestore 行為 | 說明 |
60
- |------|---------|---------------|------|
61
- | 首次分享「技能 A」 | `md5(user:技能A)` | 建立新記錄 | `createdAt` = 當前時間 |
62
- | 修改內容後再分享「技能 A」 | `md5(user:技能A)` ✅ 相同 | **覆寫**現有記錄 | `createdAt` 保持不變,`updatedAt` 更新 |
63
- | 重新命名為「技能 B」後分享 | `md5(user:技能B)` ❌ 不同 | 建立新記錄 | 產生新的 shareId |
64
-
65
- ### 優點總結
66
-
67
- 1. **智慧覆寫**:技能內容更新時,分享連結不變(使用者體驗佳)
68
- 2. **改名隔離**:技能改名視為新技能,產生新分享(合理)
69
- 3. **效能最佳**:無需查詢「是否已分享」,直接用 MD5 當 document ID
70
- 4. **儲存節省**:同一技能只有一筆記錄(除非改名)
71
-
72
- ---
73
-
74
- ## [D-2] 資料結構設計
75
-
76
- ### 為什麼需要 `sharedSkills` Collection?
77
-
78
- **現有技能儲存方式**:
79
- - 技能存放在檔案系統:`/mnt/data/{userId}/.claude/skills/{skillId}/SKILL.md`
80
- - 每個使用者只能存取自己的檔案(私有隔離)
81
- - **沒有** Firestore 的 `skills` collection
82
-
83
- **跨使用者分享的挑戰**:
84
- - 使用者 A 的技能檔案位於 `/mnt/data/user_A/.claude/skills/孵化技能/SKILL.md`
85
- - 使用者 B 無法直接存取使用者 A 的檔案系統
86
- - 需要一個**公開的中介儲存層**來傳遞技能內容
87
-
88
- **解決方案:Firestore `sharedSkills` Collection**
89
- - 作為技能內容的「公開快照」儲存
90
- - 任何人都可透過 shareId 讀取(無需知道原擁有者)
91
- - 使用者 B 讀取後 clone 到自己的 GCS 儲存空間
92
-
93
- **技能儲存架構**:
94
- ```
95
- 使用者 A 的技能(原始):
96
- GCS: botrun-c-uploads/user_A/.claude/skills/孵化技能/SKILL.md
97
- 掛載: /mnt/data/user_A/.claude/skills/孵化技能/SKILL.md
98
-
99
- 分享快照(Firestore):
100
- Collection: sharedSkills
101
- Document ID: md5(user_A:孵化技能)
102
- Field: skillContent = "完整 SKILL.md 內容"
103
-
104
- 使用者 B 匯入(clone):
105
- GCS: botrun-c-uploads/user_B/.claude/skills/孵化技能/SKILL.md
106
- 掛載: /mnt/data/user_B/.claude/skills/孵化技能/SKILL.md
107
- ```
108
-
109
- ### Firestore Collection: `sharedSkills`
110
-
111
- ```typescript
112
- // sharedSkills/{shareId} where shareId = md5(userId + skillName)
113
- interface SharedSkill {
114
- shareId: string; // MD5 hash(即 document ID)
115
- ownerId: string; // 分享者 userId(用於審計和防濫用)
116
- skillName: string; // 技能名稱
117
- skillDescription: string; // 技能描述
118
- skillIcon: string; // 技能 icon(emoji)
119
- skillContent: string; // 完整的 SKILL.md 內容(YAML + Markdown)
120
- createdAt: Timestamp; // 首次分享時間
121
- updatedAt: Timestamp; // 最後更新時間
122
- }
123
- ```
124
-
125
- **欄位用途說明**:
126
- - ✅ `shareId`:MD5 hash 作為 document ID,支援自動覆寫
127
- - ✅ `ownerId`:記錄分享者(用於審計、防濫用),但**不用於查詢**
128
- - ✅ `createdAt`:首次分享時間(覆寫時不變)
129
- - ✅ `updatedAt`:內容更新時間(每次覆寫都更新)
130
-
131
- **移除的欄位**:
132
- - ❌ `downloadCount`:不統計下載次數
133
- - ❌ `shareUrl`:現階段不使用完整 URL
134
- - ❌ `expiresAt`:現階段不實作過期機制
135
-
136
- ### Firestore 索引
137
-
138
- ```typescript
139
- // 單一欄位索引:shareId(作為 document ID,自動索引)
140
- // 無需額外建立複合索引
141
- ```
142
-
143
- ---
144
-
145
- ## [D-3] API 設計
146
-
147
- ### 3.1 建立分享
148
-
149
- **Endpoint**: `POST /api/skills/share`
150
-
151
- **Request Body**:
152
- ```typescript
153
- {
154
- skillId: string; // 要分享的技能 ID
155
- }
156
- ```
157
-
158
- **Response**:
159
- ```typescript
160
- {
161
- success: true;
162
- shareId: string; // MD5 hash(32 字元 hex)
163
- }
164
- ```
165
-
166
- **實作邏輯**:
167
- ```typescript
168
- // api/src/routes/skills.ts
169
- import { getFirestore, FieldValue } from 'firebase-admin/firestore';
170
- import { generateShareId } from '../utils/share-id.js';
171
-
172
- router.post('/share', async (req, res) => {
173
- const { skillId } = req.body;
174
- const userId = req.user.uid;
175
-
176
- // 1. 檢查技能是否存在且屬於使用者
177
- const skillsRepo = new SkillsRepository(userId);
178
- const skillExists = await skillsRepo.exists(skillId);
179
-
180
- if (!skillExists) {
181
- return res.status(404).json({
182
- success: false,
183
- error: '找不到此技能'
184
- });
185
- }
186
-
187
- // 2. 讀取完整技能內容
188
- const skillContent = await skillsRepo.getSkillContent(skillId);
189
- const skillName = skillContent.metadata.name;
190
-
191
- // 3. 生成 shareId = md5(userId + skillName)
192
- const shareId = generateShareId(userId, skillName);
193
-
194
- // 4. 檢查是否已存在(用於保留 createdAt)
195
- const db = getFirestore();
196
- const existingDoc = await db.collection('sharedSkills').doc(shareId).get();
197
-
198
- // 5. 儲存到 Firestore(覆寫模式)
199
- await db.collection('sharedSkills').doc(shareId).set({
200
- shareId,
201
- ownerId: userId,
202
- skillName,
203
- skillDescription: skillContent.metadata.description || '',
204
- skillIcon: skillContent.metadata.icon || '🔧',
205
- skillContent: skillContent.rawContent,
206
- createdAt: existingDoc.exists
207
- ? existingDoc.data()!.createdAt
208
- : FieldValue.serverTimestamp(),
209
- updatedAt: FieldValue.serverTimestamp()
210
- });
211
-
212
- return res.json({
213
- success: true,
214
- shareId
215
- });
216
- });
217
- ```
218
-
219
- ### 3.2 取得分享內容(預覽用)
220
-
221
- **Endpoint**: `GET /api/skills/share/:shareId`
222
-
223
- **用途**:供前端在匯入前預覽技能資訊
224
-
225
- **Response**:
226
- ```typescript
227
- {
228
- success: true;
229
- shareId: string;
230
- skillName: string;
231
- skillDescription: string;
232
- skillIcon: string;
233
- createdAt: string; // ISO 8601 格式
234
- }
235
- ```
236
-
237
- **實作邏輯**:
238
- ```typescript
239
- router.get('/share/:shareId', async (req, res) => {
240
- const { shareId } = req.params;
241
-
242
- // 從 Firestore 取得分享記錄
243
- const db = getFirestore();
244
- const doc = await db.collection('sharedSkills').doc(shareId).get();
245
-
246
- if (!doc.exists) {
247
- return res.status(404).json({
248
- success: false,
249
- error: '找不到此分享技能'
250
- });
251
- }
252
-
253
- const data = doc.data()!;
254
-
255
- return res.json({
256
- success: true,
257
- shareId: data.shareId,
258
- skillName: data.skillName,
259
- skillDescription: data.skillDescription,
260
- skillIcon: data.skillIcon,
261
- createdAt: data.createdAt.toDate().toISOString()
262
- });
263
- });
264
- ```
265
-
266
- ### 3.3 匯入分享技能
267
-
268
- **Endpoint**: `POST /api/skills/import/:shareId`
269
-
270
- **用途**:將分享的技能 clone 到使用者自己的 GCS 儲存空間
271
-
272
- **Request Body**:
273
- ```typescript
274
- {
275
- overwrite?: boolean; // 可選,true = 覆寫現有技能,預設 false
276
- }
277
- ```
278
-
279
- **Response**:
280
- ```typescript
281
- // 成功匯入
282
- {
283
- success: true;
284
- skillId: string; // 新建立的技能 ID
285
- skillName: string; // 技能名稱
286
- }
287
-
288
- // 名稱衝突(需要使用者確認)
289
- {
290
- success: false;
291
- conflict: true;
292
- skillName: string; // 衝突的技能名稱
293
- error: "技能名稱已存在"
294
- }
295
- ```
296
-
297
- **實作邏輯**:
298
- ```typescript
299
- router.post('/import/:shareId', async (req, res) => {
300
- const { shareId } = req.params;
301
- const { overwrite = false } = req.body;
302
- const userId = req.user.uid;
303
-
304
- // 1. 從 Firestore 取得分享內容
305
- const db = getFirestore();
306
- const doc = await db.collection('sharedSkills').doc(shareId).get();
307
-
308
- if (!doc.exists) {
309
- return res.status(404).json({
310
- success: false,
311
- error: '找不到此分享技能'
312
- });
313
- }
314
-
315
- const sharedData = doc.data()!;
316
-
317
- // 2. 檢查名稱衝突
318
- const skillsRepo = new SkillsRepository(userId);
319
- const nameExists = await skillsRepo.exists(sharedData.skillName);
320
-
321
- if (nameExists && !overwrite) {
322
- // 名稱衝突,返回 conflict 狀態,讓前端顯示確認對話框
323
- return res.status(409).json({
324
- success: false,
325
- conflict: true,
326
- skillName: sharedData.skillName,
327
- error: '技能名稱已存在'
328
- });
329
- }
330
-
331
- // 3. 處理覆寫
332
- if (nameExists && overwrite) {
333
- // 使用者選擇覆寫:刪除舊技能
334
- await skillsRepo.deleteSkill(sharedData.skillName);
335
- }
336
-
337
- // 4. 建立新技能(clone 完整內容到使用者的 GCS)
338
- const newSkillId = sharedData.skillName.replace(/[^a-zA-Z0-9\u4e00-\u9fa5_-]/g, '-');
339
- await skillsRepo.createSkill(newSkillId, sharedData.skillContent);
340
-
341
- // 實際寫入路徑:
342
- // 檔案系統: /mnt/data/{userId}/.claude/skills/{newSkillId}/SKILL.md
343
- // 實際儲存: GCS bucket "botrun-c-uploads" (透過 GCS FUSE 掛載)
344
-
345
- return res.json({
346
- success: true,
347
- skillId: newSkillId,
348
- skillName: sharedData.skillName
349
- });
350
- });
351
- ```
352
-
353
- ---
354
-
355
- ## [D-4] 前端 UI 設計
356
-
357
- ### 4.0 跨平台剪貼簿支援
358
-
359
- **依賴安裝**(專案尚未安裝):
360
- ```bash
361
- pnpm add @capacitor/clipboard
362
- npx cap sync
363
- ```
364
-
365
- **使用方式**:
366
- ```typescript
367
- import { Clipboard } from '@capacitor/clipboard';
368
-
369
- // 複製到剪貼簿(支援 Web/iOS/Android)
370
- await Clipboard.write({ string: shareId });
371
- ```
372
-
373
- **參考文件**:[Capacitor Clipboard API](https://capacitorjs.com/docs/apis/clipboard)
374
-
375
- ### 4.1 分享按鈕(Share Button)
376
-
377
- 在技能卡片上加入分享按鈕,點擊後顯示分享 Dialog。
378
-
379
- **位置**: `web/components/skills-manager-drawer.tsx`
380
-
381
- ```tsx
382
- // 在 SortableSkillItem 中加入分享按鈕
383
- import { Share2 } from "lucide-react";
384
-
385
- function SortableSkillItem({ skill, onEdit, onDelete, onShare, isDraggable }) {
386
- return (
387
- <div className="flex items-start gap-3 p-4 rounded-lg border">
388
- {/* ... 現有內容 ... */}
389
-
390
- <div className="flex gap-2 shrink-0">
391
- {/* 分享按鈕 - 只有使用者技能才顯示 */}
392
- {skill.source === "user" && (
393
- <Button
394
- variant="ghost"
395
- size="icon"
396
- onClick={() => onShare(skill)}
397
- title="分享技能"
398
- >
399
- <Share2 className="h-4 w-4" />
400
- </Button>
401
- )}
402
-
403
- {/* 編輯/查看按鈕 */}
404
- <Button variant="ghost" size="icon" onClick={() => onEdit(skill)}>
405
- {skill.editable ? <Pencil /> : <Eye />}
406
- </Button>
407
-
408
- {/* 刪除按鈕 */}
409
- {skill.editable && (
410
- <Button variant="ghost" size="icon" onClick={() => onDelete(skill)}>
411
- <Trash2 />
412
- </Button>
413
- )}
414
- </div>
415
- </div>
416
- );
417
- }
418
- ```
419
-
420
- ### 4.2 分享 Dialog (ShareSkillDialog)
421
-
422
- **新檔案**: `web/components/share-skill-dialog.tsx`
423
-
424
- ```tsx
425
- "use client";
426
-
427
- import { useState } from "react";
428
- import {
429
- Dialog,
430
- DialogContent,
431
- DialogDescription,
432
- DialogHeader,
433
- DialogTitle,
434
- } from "@/components/ui/dialog";
435
- import { Button } from "@/components/ui/button";
436
- import { Input } from "@/components/ui/input";
437
- import { Label } from "@/components/ui/label";
438
- import { Copy, Check, Loader2, AlertCircle } from "lucide-react";
439
- import { Clipboard } from "@capacitor/clipboard";
440
-
441
- interface ShareSkillDialogProps {
442
- open: boolean;
443
- onOpenChange: (open: boolean) => void;
444
- skillId: string;
445
- skillName: string;
446
- }
447
-
448
- export function ShareSkillDialog({
449
- open,
450
- onOpenChange,
451
- skillId,
452
- skillName
453
- }: ShareSkillDialogProps) {
454
- const [shareId, setShareId] = useState<string | null>(null);
455
- const [isLoading, setIsLoading] = useState(false);
456
- const [copied, setCopied] = useState(false);
457
- const [error, setError] = useState<string | null>(null);
458
-
459
- // 生成分享連結
460
- const handleCreateShare = async () => {
461
- setIsLoading(true);
462
- setError(null);
463
- try {
464
- const response = await fetch('/api/skills/share', {
465
- method: 'POST',
466
- headers: { 'Content-Type': 'application/json' },
467
- body: JSON.stringify({ skillId })
468
- });
469
-
470
- const data = await response.json();
471
-
472
- if (!data.success) {
473
- throw new Error(data.error || '建立失敗');
474
- }
475
-
476
- setShareId(data.shareId);
477
- } catch (err: any) {
478
- setError(err.message || '建立分享連結失敗');
479
- } finally {
480
- setIsLoading(false);
481
- }
482
- };
483
-
484
- // 複製到剪貼簿(跨平台支援)
485
- const handleCopy = async () => {
486
- if (!shareId) return;
487
-
488
- try {
489
- await Clipboard.write({ string: shareId });
490
- setCopied(true);
491
- setTimeout(() => setCopied(false), 2000);
492
- } catch (err) {
493
- console.error('複製失敗:', err);
494
- }
495
- };
496
-
497
- return (
498
- <Dialog open={open} onOpenChange={onOpenChange}>
499
- <DialogContent className="sm:max-w-[500px]">
500
- <DialogHeader>
501
- <DialogTitle>分享技能:{skillName}</DialogTitle>
502
- <DialogDescription>
503
- 建立分享 ID,其他人可以透過此 ID 將技能匯入到自己的技能集
504
- </DialogDescription>
505
- </DialogHeader>
506
-
507
- <div className="space-y-4 py-4">
508
- {!shareId ? (
509
- <>
510
- <Button
511
- onClick={handleCreateShare}
512
- disabled={isLoading}
513
- className="w-full"
514
- >
515
- {isLoading && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
516
- 建立分享連結
517
- </Button>
518
-
519
- {/* 錯誤訊息 */}
520
- {error && (
521
- <div className="flex items-start gap-2 p-3 bg-destructive/10 text-destructive rounded-md text-sm">
522
- <AlertCircle className="h-4 w-4 mt-0.5 shrink-0" />
523
- <span>{error}</span>
524
- </div>
525
- )}
526
- </>
527
- ) : (
528
- <div className="space-y-2">
529
- <Label>分享 ID</Label>
530
- <div className="flex gap-2">
531
- <Input
532
- value={shareId}
533
- readOnly
534
- className="font-mono text-xs"
535
- />
536
- <Button
537
- variant="outline"
538
- size="sm"
539
- onClick={handleCopy}
540
- className="shrink-0"
541
- >
542
- {copied ? (
543
- <>
544
- <Check className="h-4 w-4 mr-2" />
545
- 已複製!
546
- </>
547
- ) : (
548
- <>
549
- <Copy className="h-4 w-4 mr-2" />
550
- 複製
551
- </>
552
- )}
553
- </Button>
554
- </div>
555
- <p className="text-sm text-muted-foreground">
556
- 將此 ID 分享給其他人,他們可以在「匯入分享技能」中使用
557
- </p>
558
- </div>
559
- )}
560
- </div>
561
- </DialogContent>
562
- </Dialog>
563
- );
564
- }
565
- ```
566
-
567
- **設計重點**:
568
- - ✅ 使用 `@capacitor/clipboard` 支援跨平台複製
569
- - ✅ 移除 `toast`,改用按鈕文字和 icon 變化("複製" → "已複製!")
570
- - ✅ 錯誤訊息內嵌顯示(不使用 toast)
571
- - ✅ MD5 hash 完整顯示(32 字元 hex),字體使用 `font-mono text-xs`
572
-
573
- ### 4.3 新增技能按鈕改為 DropdownMenu
574
-
575
- **位置**: `web/components/skills-manager-drawer.tsx`
576
-
577
- ```tsx
578
- import {
579
- DropdownMenu,
580
- DropdownMenuContent,
581
- DropdownMenuItem,
582
- DropdownMenuTrigger,
583
- } from "@/components/ui/dropdown-menu";
584
- import { Plus, FileDown, ChevronDown } from "lucide-react";
585
-
586
- // 原本的單一按鈕
587
- // <Button onClick={onCreate}>
588
- // <Plus className="h-4 w-4 mr-2" />
589
- // 新增技能
590
- // </Button>
591
-
592
- // 改為 DropdownMenu
593
- <DropdownMenu>
594
- <DropdownMenuTrigger asChild>
595
- <Button>
596
- <Plus className="h-4 w-4 mr-2" />
597
- 新增技能
598
- <ChevronDown className="h-4 w-4 ml-2" />
599
- </Button>
600
- </DropdownMenuTrigger>
601
- <DropdownMenuContent align="end">
602
- <DropdownMenuItem onClick={onCreate}>
603
- <Plus className="h-4 w-4 mr-2" />
604
- 建立新技能
605
- </DropdownMenuItem>
606
- <DropdownMenuItem onClick={() => setImportDialogOpen(true)}>
607
- <FileDown className="h-4 w-4 mr-2" />
608
- 匯入分享技能
609
- </DropdownMenuItem>
610
- </DropdownMenuContent>
611
- </DropdownMenu>
612
- ```
613
-
614
- ### 4.4 匯入技能 Dialog (ImportSkillDialog)
615
-
616
- **新檔案**: `web/components/import-skill-dialog.tsx`
617
-
618
- ```tsx
619
- "use client";
620
-
621
- import { useState } from "react";
622
- import {
623
- Dialog,
624
- DialogContent,
625
- DialogDescription,
626
- DialogHeader,
627
- DialogTitle,
628
- DialogFooter,
629
- } from "@/components/ui/dialog";
630
- import {
631
- AlertDialog,
632
- AlertDialogAction,
633
- AlertDialogCancel,
634
- AlertDialogContent,
635
- AlertDialogDescription,
636
- AlertDialogFooter,
637
- AlertDialogHeader,
638
- AlertDialogTitle,
639
- } from "@/components/ui/alert-dialog";
640
- import { Button } from "@/components/ui/button";
641
- import { Input } from "@/components/ui/input";
642
- import { Label } from "@/components/ui/label";
643
- import { Loader2, AlertCircle } from "lucide-react";
644
-
645
- interface ImportSkillDialogProps {
646
- open: boolean;
647
- onOpenChange: (open: boolean) => void;
648
- onSuccess: () => void; // 匯入成功後重新載入技能列表
649
- }
650
-
651
- interface SkillPreview {
652
- shareId: string;
653
- skillName: string;
654
- skillDescription: string;
655
- skillIcon: string;
656
- createdAt: string;
657
- }
658
-
659
- export function ImportSkillDialog({
660
- open,
661
- onOpenChange,
662
- onSuccess
663
- }: ImportSkillDialogProps) {
664
- const [shareId, setShareId] = useState("");
665
- const [isLoadingPreview, setIsLoadingPreview] = useState(false);
666
- const [isImporting, setIsImporting] = useState(false);
667
- const [preview, setPreview] = useState<SkillPreview | null>(null);
668
- const [error, setError] = useState<string | null>(null);
669
-
670
- // 名稱衝突確認對話框狀態
671
- const [showConflictDialog, setShowConflictDialog] = useState(false);
672
- const [conflictSkillName, setConflictSkillName] = useState<string | null>(null);
673
-
674
- // 預覽分享技能
675
- const handlePreview = async () => {
676
- if (!shareId.trim()) {
677
- setError('請輸入分享 ID');
678
- return;
679
- }
680
-
681
- setIsLoadingPreview(true);
682
- setError(null);
683
- setPreview(null);
684
-
685
- try {
686
- const response = await fetch(`/api/skills/share/${shareId.trim()}`);
687
- const data = await response.json();
688
-
689
- if (!data.success) {
690
- throw new Error(data.error || '找不到此分享技能');
691
- }
692
-
693
- setPreview(data);
694
- } catch (err: any) {
695
- setError(err.message || '載入失敗');
696
- } finally {
697
- setIsLoadingPreview(false);
698
- }
699
- };
700
-
701
- // 確認匯入
702
- const handleImport = async (overwrite?: boolean) => {
703
- setIsImporting(true);
704
- setError(null);
705
-
706
- try {
707
- const response = await fetch(`/api/skills/import/${shareId.trim()}`, {
708
- method: 'POST',
709
- headers: { 'Content-Type': 'application/json' },
710
- body: JSON.stringify({ overwrite })
711
- });
712
-
713
- const data = await response.json();
714
-
715
- // 檢查名稱衝突
716
- if (response.status === 409 && data.conflict) {
717
- setConflictSkillName(data.skillName);
718
- setShowConflictDialog(true);
719
- setIsImporting(false);
720
- return;
721
- }
722
-
723
- if (!data.success) {
724
- throw new Error(data.error || '匯入失敗');
725
- }
726
-
727
- // 成功:關閉 Dialog 並重新載入列表
728
- onOpenChange(false);
729
- onSuccess();
730
- } catch (err: any) {
731
- setError(err.message || '匯入失敗');
732
- } finally {
733
- setIsImporting(false);
734
- }
735
- };
736
-
737
- // 處理衝突選擇
738
- const handleConflictChoice = async (choice: 'overwrite' | 'keep-both' | 'cancel') => {
739
- setShowConflictDialog(false);
740
-
741
- if (choice === 'cancel') {
742
- return;
743
- }
744
-
745
- if (choice === 'overwrite') {
746
- await handleImport(true); // overwrite = true
747
- } else {
748
- // keep-both: 不傳 overwrite,後端自動加數字後綴
749
- await handleImport(false);
750
- }
751
- };
752
-
753
- return (
754
- <>
755
- <Dialog open={open} onOpenChange={onOpenChange}>
756
- <DialogContent className="sm:max-w-[500px]">
757
- <DialogHeader>
758
- <DialogTitle>匯入分享技能</DialogTitle>
759
- <DialogDescription>
760
- 輸入分享 ID 以匯入其他人分享的技能
761
- </DialogDescription>
762
- </DialogHeader>
763
-
764
- <div className="space-y-4 py-4">
765
- {/* 輸入分享 ID */}
766
- <div className="space-y-2">
767
- <Label htmlFor="shareId">分享 ID</Label>
768
- <div className="flex gap-2">
769
- <Input
770
- id="shareId"
771
- placeholder="貼上 32 字元的分享 ID"
772
- value={shareId}
773
- onChange={(e) => {
774
- setShareId(e.target.value);
775
- setPreview(null);
776
- setError(null);
777
- }}
778
- className="font-mono text-xs"
779
- />
780
- <Button
781
- variant="outline"
782
- onClick={handlePreview}
783
- disabled={isLoadingPreview || !shareId.trim()}
784
- className="shrink-0"
785
- >
786
- {isLoadingPreview && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
787
- 預覽
788
- </Button>
789
- </div>
790
- </div>
791
-
792
- {/* 錯誤訊息 */}
793
- {error && (
794
- <div className="flex items-start gap-2 p-3 bg-destructive/10 text-destructive rounded-md text-sm">
795
- <AlertCircle className="h-4 w-4 mt-0.5 shrink-0" />
796
- <span>{error}</span>
797
- </div>
798
- )}
799
-
800
- {/* 預覽區域 */}
801
- {preview && (
802
- <div className="rounded-lg border p-4 space-y-2 bg-muted/50">
803
- <div className="flex items-center gap-2">
804
- <span className="text-2xl">{preview.skillIcon}</span>
805
- <h3 className="font-semibold">{preview.skillName}</h3>
806
- </div>
807
- <p className="text-sm text-muted-foreground">
808
- {preview.skillDescription}
809
- </p>
810
- <p className="text-xs text-muted-foreground">
811
- 分享時間:{new Date(preview.createdAt).toLocaleDateString('zh-TW')}
812
- </p>
813
- </div>
814
- )}
815
- </div>
816
-
817
- <DialogFooter>
818
- <Button variant="outline" onClick={() => onOpenChange(false)}>
819
- 取消
820
- </Button>
821
- <Button
822
- onClick={() => handleImport()}
823
- disabled={!preview || isImporting}
824
- >
825
- {isImporting && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
826
- 確認匯入
827
- </Button>
828
- </DialogFooter>
829
- </DialogContent>
830
- </Dialog>
831
-
832
- {/* 名稱衝突確認對話框 */}
833
- <AlertDialog open={showConflictDialog} onOpenChange={setShowConflictDialog}>
834
- <AlertDialogContent>
835
- <AlertDialogHeader>
836
- <AlertDialogTitle>技能名稱已存在</AlertDialogTitle>
837
- <AlertDialogDescription>
838
- 您已經有一個名為「<strong>{conflictSkillName}</strong>」的技能。請選擇如何處理:
839
- </AlertDialogDescription>
840
- </AlertDialogHeader>
841
- <AlertDialogFooter>
842
- <AlertDialogCancel onClick={() => handleConflictChoice('cancel')}>
843
- 取消匯入
844
- </AlertDialogCancel>
845
- <AlertDialogAction
846
- onClick={() => handleConflictChoice('overwrite')}
847
- disabled={isImporting}
848
- className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
849
- >
850
- {isImporting && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
851
- 覆寫現有技能
852
- </AlertDialogAction>
853
- </AlertDialogFooter>
854
- </AlertDialogContent>
855
- </AlertDialog>
856
- </>
857
- );
858
- }
859
- ```
860
-
861
- **設計重點**:
862
- - ✅ 移除所有 `toast`,錯誤訊息內嵌顯示
863
- - ✅ 匯入成功後直接關閉 Dialog,技能列表自動重新整理(使用者會看到新技能)
864
- - ✅ 顯示「分享時間」(`createdAt`)
865
- - ✅ MD5 hash 輸入框使用 `font-mono text-xs`(32 字元)
866
- - ✅ 預覽卡片增加背景色區分(`bg-muted/50`)
867
- - ✅ **名稱衝突確認**:偵測到衝突時顯示 AlertDialog,提供兩個選項:
868
- - **覆寫現有技能**(紅色按鈕,警示性)
869
- - **取消匯入**
870
-
871
- ---
872
-
873
- ## [D-5] RWD 響應式設計
874
-
875
- ### Shadcn Dialog 原生 RWD 支援
876
-
877
- Shadcn 的 Dialog 組件已內建 RWD 支援(參考:https://ui.shadcn.com/docs/components/dialog):
878
-
879
- ```tsx
880
- // DialogContent 預設樣式已包含響應式設計
881
- <DialogContent className="sm:max-w-[425px]">
882
- {/*
883
- - Mobile: 全螢幕顯示(max-w-full)
884
- - Desktop: 固定寬度 425px(sm:max-w-[425px])
885
- */}
886
- </DialogContent>
887
- ```
888
-
889
- ### DropdownMenu 觸控優化
890
-
891
- 參考 Shadcn DropdownMenu 文件:https://ui.shadcn.com/docs/components/dropdown-menu
892
-
893
- ```tsx
894
- // DropdownMenuContent 自動處理觸控和鍵盤導航
895
- <DropdownMenuContent align="end">
896
- {/*
897
- - 自動調整位置避免超出視窗
898
- - 觸控裝置支援
899
- - 鍵盤導航支援(箭頭鍵、Enter、Escape)
900
- */}
901
- </DropdownMenuContent>
902
- ```
903
-
904
- ---
905
-
906
- ## [D-6] 錯誤處理與邊界情況
907
-
908
- ### 6.1 Share ID 不存在
909
-
910
- ```typescript
911
- // API Response
912
- {
913
- success: false,
914
- error: "找不到此分享技能"
915
- }
916
-
917
- // 前端處理:內嵌錯誤訊息(不使用 toast)
918
- <div className="flex items-start gap-2 p-3 bg-destructive/10 text-destructive rounded-md text-sm">
919
- <AlertCircle className="h-4 w-4 mt-0.5 shrink-0" />
920
- <span>找不到此分享技能</span>
921
- </div>
922
- ```
923
-
924
- ### 6.2 技能名稱衝突
925
-
926
- 當匯入的技能名稱與現有技能重複時,顯示確認對話框讓使用者選擇:
927
-
928
- **流程**:
929
- 1. 使用者點擊「確認匯入」
930
- 2. 後端檢測到名稱衝突,返回 409 狀態碼和 `conflict: true`
931
- 3. 前端顯示 AlertDialog,提供兩個選項:
932
- - **覆寫現有技能**:刪除舊技能,匯入新技能(使用原始名稱)
933
- - **取消匯入**:放棄匯入操作
934
-
935
- **後端處理邏輯**:
936
- ```typescript
937
- // api/src/routes/skills.ts
938
-
939
- // 1. 檢測衝突
940
- const nameExists = await skillsRepo.exists(sharedData.skillName);
941
-
942
- if (nameExists && !overwrite) {
943
- return res.status(409).json({
944
- success: false,
945
- conflict: true,
946
- skillName: sharedData.skillName,
947
- error: '技能名稱已存在'
948
- });
949
- }
950
-
951
- // 2. 處理覆寫
952
- if (nameExists && overwrite) {
953
- await skillsRepo.deleteSkill(sharedData.skillName);
954
- }
955
- ```
956
-
957
- **前端處理邏輯**:
958
- ```tsx
959
- // web/components/import-skill-dialog.tsx
960
-
961
- const handleImport = async (overwrite: boolean = false) => {
962
- const response = await fetch(`/api/skills/import/${shareId}`, {
963
- method: 'POST',
964
- headers: { 'Content-Type': 'application/json' },
965
- body: JSON.stringify({ overwrite })
966
- });
967
-
968
- // 檢查衝突
969
- if (response.status === 409 && data.conflict) {
970
- setConflictSkillName(data.skillName);
971
- setShowConflictDialog(true); // 顯示確認對話框
972
- return;
973
- }
974
- };
975
-
976
- // 使用者選擇
977
- const handleConflictChoice = async (choice: 'overwrite' | 'cancel') => {
978
- if (choice === 'overwrite') {
979
- await handleImport(true); // 覆寫
980
- }
981
- // cancel: 不執行任何動作
982
- };
983
- ```
984
-
985
- ### 6.3 權限檢查
986
-
987
- ```typescript
988
- // 前端:只有使用者技能(source === "user")才顯示分享按鈕
989
- {skill.source === "user" && (
990
- <Button onClick={() => onShare(skill)} title="分享技能">
991
- <Share2 className="h-4 w-4" />
992
- </Button>
993
- )}
994
-
995
- // 後端:檢查技能是否存在且屬於使用者
996
- const skillsRepo = new SkillsRepository(userId);
997
- const skillExists = await skillsRepo.exists(skillId);
998
-
999
- if (!skillExists) {
1000
- return res.status(404).json({
1001
- success: false,
1002
- error: '找不到此技能'
1003
- });
1004
- }
1005
- ```
1006
-
1007
- ---
1008
-
1009
- ## [D-7] 安全性考量
1010
-
1011
- ### 7.1 防止 Share ID 爆破攻擊
1012
-
1013
- ```typescript
1014
- // Rate limiting(使用 express-rate-limit)
1015
- import rateLimit from 'express-rate-limit';
1016
-
1017
- const shareLimiter = rateLimit({
1018
- windowMs: 15 * 60 * 1000, // 15 分鐘
1019
- max: 10, // 最多 10 次請求
1020
- message: '請求過於頻繁,請稍後再試'
1021
- });
1022
-
1023
- router.get('/share/:shareId', shareLimiter, async (req, res) => {
1024
- // ...
1025
- });
1026
- ```
1027
-
1028
- ### 7.2 內容驗證
1029
-
1030
- ```typescript
1031
- // 匯入前驗證 SKILL.md 內容
1032
- function validateSkillContent(content: string): boolean {
1033
- // 檢查是否包含惡意程式碼
1034
- // 檢查檔案大小限制(例如:< 1MB)
1035
- // 檢查 YAML frontmatter 格式
1036
- return true;
1037
- }
1038
- ```
1039
-
1040
- ### 7.3 私有技能保護
1041
-
1042
- ```typescript
1043
- // 使用者可選擇是否公開分享(未來功能)
1044
- interface SharedSkill {
1045
- isPublic: boolean; // 預設 true
1046
- allowedUsers?: string[]; // 允許存取的使用者清單(私有分享)
1047
- }
1048
- ```
1049
-
1050
- ---
1051
-
1052
- ## [D-8] 效能優化
1053
-
1054
- ### 8.1 MD5 生成效能
1055
-
1056
- ```typescript
1057
- // Node.js crypto.createHash('md5') 效能極佳
1058
- import crypto from 'crypto';
1059
- const shareId = crypto.createHash('md5')
1060
- .update(`${userId}:${skillName}`, 'utf-8')
1061
- .digest('hex'); // ~5μs
1062
- ```
1063
-
1064
- ### 8.2 Firestore 寫入優化(覆寫模式)
1065
-
1066
- ```typescript
1067
- // 使用確定性 shareId,直接覆寫(無需先查詢是否存在)
1068
- const shareId = generateShareId(userId, skillName);
1069
-
1070
- // 僅需一次查詢(用於判斷 createdAt)
1071
- const existingDoc = await db.collection('sharedSkills').doc(shareId).get();
1072
-
1073
- // 直接寫入(覆寫)
1074
- await db.collection('sharedSkills').doc(shareId).set({...});
1075
- ```
1076
-
1077
- **效能優勢**:
1078
- - ✅ 無需查詢「是否已分享過」
1079
- - ✅ 無需處理碰撞重試邏輯
1080
- - ✅ document ID 查詢是 Firestore 最快的查詢方式(O(1))
1081
-
1082
- ### 8.3 前端快取策略(未來優化)
1083
-
1084
- ```typescript
1085
- // 可考慮在前端快取已預覽過的分享技能
1086
- const [previewCache, setPreviewCache] = useState<Map<string, SkillPreview>>(new Map());
1087
-
1088
- // 預覽前先檢查快取
1089
- if (previewCache.has(shareId)) {
1090
- setPreview(previewCache.get(shareId)!);
1091
- return;
1092
- }
1093
- ```
1094
-
1095
- ---
1096
-
1097
- ## [D-9] 未來擴展功能
1098
-
1099
- ### 9.1 技能市集(Skill Marketplace)
1100
-
1101
- ```typescript
1102
- // 新增欄位支援市集功能
1103
- interface SharedSkill {
1104
- // ... 現有欄位 ...
1105
-
1106
- // 市集功能
1107
- tags?: string[]; // 技能標籤(例如:["productivity", "automation"])
1108
- rating?: number; // 評分(1-5)
1109
- reviews?: number; // 評論數量
1110
- featured?: boolean; // 是否為精選技能
1111
- thumbnailUrl?: string; // 縮圖 URL
1112
- }
1113
- ```
1114
-
1115
- ### 9.2 完整分享 URL
1116
-
1117
- ```typescript
1118
- // 支援 QR Code 和完整 URL 分享
1119
- const shareUrl = `https://botrun.ai/skills/share/${shareId}`;
1120
-
1121
- // 在分享 Dialog 中顯示 QR Code
1122
- import QRCode from 'qrcode';
1123
-
1124
- const qrCodeDataUrl = await QRCode.toDataURL(shareUrl);
1125
- ```
1126
-
1127
- ### 9.3 版本控制
1128
-
1129
- ```typescript
1130
- // 支援技能更新和版本追蹤
1131
- interface SharedSkill {
1132
- version?: string; // 版本號(例如:"1.0.0")
1133
- previousVersions?: string[]; // 歷史版本的 shareId
1134
- }
1135
- ```
1136
-
1137
- ---
1138
-
1139
- ## 📚 參考資料
1140
-
1141
- - [Capacitor Clipboard API](https://capacitorjs.com/docs/apis/clipboard) - 跨平台剪貼簿官方文件
1142
- - [Shadcn UI Dialog](https://ui.shadcn.com/docs/components/dialog) - Dialog 元件文件
1143
- - [Shadcn UI DropdownMenu](https://ui.shadcn.com/docs/components/dropdown-menu) - DropdownMenu 元件文件
1144
- - [Firestore Data Model](https://firebase.google.com/docs/firestore/data-model) - Firestore 資料模型
1145
- - [Express Rate Limiting](https://www.npmjs.com/package/express-rate-limit) - API 速率限制套件
1146
- - [Node.js crypto.createHash()](https://nodejs.org/api/crypto.html#cryptocreatehashalgorithm-options) - MD5 hash 生成 API