botrun-horse 2.31.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} +8 -8
  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 +7 -5
  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/html-report.md +15 -0
  45. package/{bin/commands/writing.mjs → modules/writing/command.mjs} +3 -3
  46. package/{lib → modules}/writing/generate.mjs +2 -2
  47. package/modules/writing/index.mjs +38 -0
  48. package/package.json +3 -1
  49. package/parallel-dag-todo-list/imagen-module.md +51 -0
  50. package/bin/commands/schema.mjs +0 -254
  51. package/botrun-c/.claude/skills/DOCX/351/226/261/350/256/200/346/212/200/350/203/275/SKILL.md +0 -103
  52. 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
  53. package/botrun-c/.claude/skills/GitHub/346/212/200/350/203/275/SKILL.md +0 -1093
  54. package/botrun-c/.claude/skills/GitHub/346/212/200/350/203/275/scripts/github-branch.mjs +0 -73
  55. package/botrun-c/.claude/skills/GitHub/346/212/200/350/203/275/scripts/github-branches.mjs +0 -77
  56. package/botrun-c/.claude/skills/GitHub/346/212/200/350/203/275/scripts/github-checkout.mjs +0 -72
  57. package/botrun-c/.claude/skills/GitHub/346/212/200/350/203/275/scripts/github-clone.mjs +0 -72
  58. package/botrun-c/.claude/skills/GitHub/346/212/200/350/203/275/scripts/github-commit.mjs +0 -75
  59. package/botrun-c/.claude/skills/GitHub/346/212/200/350/203/275/scripts/github-delete-branch.mjs +0 -75
  60. package/botrun-c/.claude/skills/GitHub/346/212/200/350/203/275/scripts/github-delete-file.mjs +0 -72
  61. package/botrun-c/.claude/skills/GitHub/346/212/200/350/203/275/scripts/github-diff.mjs +0 -80
  62. package/botrun-c/.claude/skills/GitHub/346/212/200/350/203/275/scripts/github-list-tree.mjs +0 -336
  63. package/botrun-c/.claude/skills/GitHub/346/212/200/350/203/275/scripts/github-list.mjs +0 -199
  64. package/botrun-c/.claude/skills/GitHub/346/212/200/350/203/275/scripts/github-merge.mjs +0 -86
  65. package/botrun-c/.claude/skills/GitHub/346/212/200/350/203/275/scripts/github-move.mjs +0 -75
  66. package/botrun-c/.claude/skills/GitHub/346/212/200/350/203/275/scripts/github-pr-create.mjs +0 -81
  67. package/botrun-c/.claude/skills/GitHub/346/212/200/350/203/275/scripts/github-pr-list.mjs +0 -74
  68. package/botrun-c/.claude/skills/GitHub/346/212/200/350/203/275/scripts/github-pr-merge.mjs +0 -83
  69. package/botrun-c/.claude/skills/GitHub/346/212/200/350/203/275/scripts/github-pr-view.mjs +0 -71
  70. package/botrun-c/.claude/skills/GitHub/346/212/200/350/203/275/scripts/github-push.mjs +0 -71
  71. package/botrun-c/.claude/skills/GitHub/346/212/200/350/203/275/scripts/github-read.mjs +0 -277
  72. package/botrun-c/.claude/skills/GitHub/346/212/200/350/203/275/scripts/github-search.mjs +0 -370
  73. package/botrun-c/.claude/skills/GitHub/346/212/200/350/203/275/scripts/github-stash.mjs +0 -116
  74. package/botrun-c/.claude/skills/GitHub/346/212/200/350/203/275/scripts/github-sync.mjs +0 -71
  75. package/botrun-c/.claude/skills/GitHub/346/212/200/350/203/275/scripts/github-write.mjs +0 -74
  76. package/botrun-c/.claude/skills/GitHub/346/212/200/350/203/275/scripts/lib/path-validator.mjs +0 -167
  77. package/botrun-c/.claude/skills/GoogleDrive/346/212/200/350/203/275/SKILL.md +0 -605
  78. package/botrun-c/.claude/skills/GoogleDrive/346/212/200/350/203/275/scripts/gdrive-create.mjs +0 -127
  79. package/botrun-c/.claude/skills/GoogleDrive/346/212/200/350/203/275/scripts/gdrive-delete.mjs +0 -77
  80. package/botrun-c/.claude/skills/GoogleDrive/346/212/200/350/203/275/scripts/gdrive-diff.mjs +0 -87
  81. package/botrun-c/.claude/skills/GoogleDrive/346/212/200/350/203/275/scripts/gdrive-history.mjs +0 -99
  82. package/botrun-c/.claude/skills/GoogleDrive/346/212/200/350/203/275/scripts/gdrive-list.mjs +0 -174
  83. package/botrun-c/.claude/skills/GoogleDrive/346/212/200/350/203/275/scripts/gdrive-read.mjs +0 -214
  84. package/botrun-c/.claude/skills/GoogleDrive/346/212/200/350/203/275/scripts/gdrive-restore.mjs +0 -75
  85. package/botrun-c/.claude/skills/GoogleDrive/346/212/200/350/203/275/scripts/gdrive-search.mjs +0 -270
  86. package/botrun-c/.claude/skills/GoogleDrive/346/212/200/350/203/275/scripts/gdrive-sync-back.mjs +0 -155
  87. package/botrun-c/.claude/skills/GoogleDrive/346/212/200/350/203/275/scripts/gdrive-update.mjs +0 -100
  88. package/botrun-c/.claude/skills/HTML/347/224/237/346/210/220/346/212/200/350/203/275/SKILL.md +0 -414
  89. package/botrun-c/.claude/skills/HTML/347/224/237/346/210/220/346/212/200/350/203/275/scripts/create-project.mjs +0 -91
  90. package/botrun-c/.claude/skills/HTML/347/224/237/346/210/220/346/212/200/350/203/275/scripts/finalize-project.mjs +0 -162
  91. package/botrun-c/.claude/skills/PDF/346/212/200/350/203/275/SKILL.md +0 -206
  92. package/botrun-c/.claude/skills/PDF/346/212/200/350/203/275/scripts/package.json +0 -19
  93. package/botrun-c/.claude/skills/PDF/346/212/200/350/203/275/scripts/pdf-analyze.mjs +0 -309
  94. package/botrun-c/.claude/skills/PDF/346/212/200/350/203/275/scripts/pdf-compress.mjs +0 -315
  95. package/botrun-c/.claude/skills/PDF/346/212/200/350/203/275/scripts/pdf-split.mjs +0 -275
  96. package/botrun-c/.claude/skills/PDF/346/212/200/350/203/275/scripts/pdf-to-text.mjs +0 -336
  97. package/botrun-c/.claude/skills/PDF/346/212/200/350/203/275/scripts/test-pdf-scripts.mjs +0 -491
  98. package/botrun-c/.claude/skills/docx-to-markdown/SKILL.md +0 -76
  99. package/botrun-c/.claude/skills/docx-to-markdown/scripts/convert_docx.py +0 -183
  100. package/botrun-c/.claude/skills/gcp-coord-ml-ocr/SKILL.md +0 -133
  101. package/botrun-c/.claude/skills/gcp-coord-ml-ocr/scripts/ocr_processor.py +0 -381
  102. package/botrun-c/.claude/skills/gemini-transcribe/SKILL.md +0 -115
  103. package/botrun-c/.claude/skills/gemini-transcribe/scripts/transcribe.py +0 -499
  104. package/botrun-c/.claude/skills/nchc-transcribe/SKILL.md +0 -131
  105. package/botrun-c/.claude/skills/nchc-transcribe/scripts/transcribe.py +0 -522
  106. package/botrun-c/.claude/skills/p320-moj-review/SKILL.md +0 -200
  107. package/botrun-c/.claude/skills/p320-moj-review/references/guideline.md +0 -118
  108. package/botrun-c/.claude/skills/pdf-multimodal-processor/SKILL.md +0 -186
  109. package/botrun-c/.claude/skills/pdf-multimodal-processor/scripts/process_pdf.py +0 -515
  110. package/botrun-c/.claude/skills/ripgrep/346/220/234/345/260/213/346/212/200/350/203/275/SKILL.md +0 -81
  111. package/botrun-c/.claude/skills/ripgrep/346/220/234/345/260/213/346/212/200/350/203/275/scripts/package.json +0 -13
  112. package/botrun-c/.claude/skills/ripgrep/346/220/234/345/260/213/346/212/200/350/203/275/scripts/secure-search.mjs +0 -232
  113. 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
  114. package/botrun-c/.claude/skills//345/216/273/350/255/230/345/210/245/346/212/200/350/203/275/SKILL.md +0 -125
  115. package/botrun-c/.claude/skills//345/233/236/346/206/266/346/212/200/350/203/275/SKILL.md +0 -123
  116. package/botrun-c/.claude/skills//345/233/236/346/206/266/346/212/200/350/203/275/scripts/recall.mjs +0 -339
  117. 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
  118. 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
  119. 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
  120. 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
  121. 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
  122. 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
  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/image-session-manager.mjs +0 -219
  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/output-formatter.mjs +0 -209
  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/package.json +0 -20
  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/utils.mjs +0 -115
  127. 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
  128. 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
  129. 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
  130. package/botrun-c/.claude/skills//345/255/265/345/214/226/346/212/200/350/203/275/SKILL.md +0 -131
  131. package/botrun-c/.claude/skills//345/255/265/345/214/226/346/212/200/350/203/275/scripts/skill-manager.mjs +0 -542
  132. 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
  133. 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
  134. 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
  135. 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
  136. 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
  137. 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
  138. 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
  139. 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
  140. 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
  141. 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
  142. 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
  143. 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
  144. package/botrun-c/.claude/skills//350/250/230/346/206/266/346/212/200/350/203/275/SKILL.md +0 -114
  145. package/botrun-c/.claude/skills//350/250/230/346/206/266/346/212/200/350/203/275/scripts/remember.mjs +0 -159
  146. package/botrun-c/.claude/skills//350/250/230/346/206/266/346/212/200/350/203/275/scripts/test-advanced.mjs +0 -342
  147. package/botrun-c/.claude/skills//350/250/230/346/206/266/346/212/200/350/203/275/scripts/test.mjs +0 -430
  148. 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
  149. 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
  150. 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
  151. 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
  152. package/botrun-c/.dockerignore +0 -131
  153. package/botrun-c/.dockeroptimize +0 -13
  154. package/botrun-c/.firebase/hosting.b3V0.cache +0 -30
  155. package/botrun-c/.firebaserc +0 -5
  156. package/botrun-c/.gcloudignore +0 -56
  157. package/botrun-c/.ruby-version +0 -1
  158. package/botrun-c/CHANGELOG.md +0 -224
  159. package/botrun-c/Dockerfile.gcp +0 -365
  160. package/botrun-c/Dockerfile.slim +0 -195
  161. package/botrun-c/Gemfile +0 -8
  162. package/botrun-c/Gemfile.lock +0 -233
  163. package/botrun-c/LOCAL-DEVELOPMENT-GUIDE.md +0 -393
  164. package/botrun-c/PLAN.md +0 -131
  165. package/botrun-c/README.md +0 -129
  166. package/botrun-c/TODO-CAPACITOR-INTEGRATION.md +0 -482
  167. package/botrun-c/VERIFICATION.md +0 -121
  168. package/botrun-c/admin/RATE_LIMIT_MANAGEMENT.md +0 -312
  169. package/botrun-c/admin/README.md +0 -338
  170. package/botrun-c/admin/botrun-billing-report-1765235244038.csv +0 -22
  171. package/botrun-c/admin/botrun-billing-report-1765235323089.csv +0 -22
  172. package/botrun-c/admin/botrun-dashboard-1765235244039.html +0 -278
  173. package/botrun-c/admin/botrun-dashboard-1765235323089.html +0 -278
  174. package/botrun-c/admin/botrun-timeseries-074xGzIKWyfKTBt1NMIj9lxi5mO2-1765235244039.html +0 -161
  175. package/botrun-c/admin/botrun-timeseries-074xGzIKWyfKTBt1NMIj9lxi5mO2-1765235323090.html +0 -161
  176. package/botrun-c/admin/check-billing.ts +0 -48
  177. package/botrun-c/admin/count-all-users.ts +0 -65
  178. package/botrun-c/admin/generate-analytics-report.ts +0 -188
  179. package/botrun-c/admin/jest.config.ts +0 -43
  180. package/botrun-c/admin/lib/__tests__/formatters.test.ts +0 -264
  181. package/botrun-c/admin/lib/__tests__/mocks/firebase.mock.ts +0 -112
  182. package/botrun-c/admin/lib/__tests__/services/UserService.test.ts +0 -83
  183. package/botrun-c/admin/lib/__tests__/user-resolver.test.ts +0 -439
  184. package/botrun-c/admin/lib/analyzers/BillingAnalyticsService.ts +0 -242
  185. package/botrun-c/admin/lib/analyzers/CsvExporter.ts +0 -263
  186. package/botrun-c/admin/lib/analyzers/HtmlChartGenerator.ts +0 -530
  187. package/botrun-c/admin/lib/analyzers/SpikeAnalyzer.ts +0 -608
  188. package/botrun-c/admin/lib/analyzers/TimeSeriesAnalyzer.ts +0 -344
  189. package/botrun-c/admin/lib/cli/CLIBootstrapper.ts +0 -40
  190. package/botrun-c/admin/lib/commands/AbstractCommand.ts +0 -69
  191. package/botrun-c/admin/lib/commands/AnalyticsCommand.ts +0 -215
  192. package/botrun-c/admin/lib/commands/ListCommand.ts +0 -85
  193. package/botrun-c/admin/lib/commands/QueryCommand.ts +0 -81
  194. package/botrun-c/admin/lib/commands/SpikeCommand.ts +0 -83
  195. package/botrun-c/admin/lib/domain/User.ts +0 -38
  196. package/botrun-c/admin/lib/firebase.ts +0 -92
  197. package/botrun-c/admin/lib/firestore-safe.ts +0 -105
  198. package/botrun-c/admin/lib/formatters.ts +0 -154
  199. package/botrun-c/admin/lib/rate-limit-detailed.ts +0 -355
  200. package/botrun-c/admin/lib/rate-limit.ts +0 -286
  201. package/botrun-c/admin/lib/repositories/UserRepository.ts +0 -104
  202. package/botrun-c/admin/lib/services/UserService.ts +0 -108
  203. package/botrun-c/admin/lib/types.ts +0 -56
  204. package/botrun-c/admin/lib/user-resolver.ts +0 -275
  205. package/botrun-c/admin/manage-auth-domains.js +0 -178
  206. package/botrun-c/admin/migrate-plan-free.sh +0 -64
  207. package/botrun-c/admin/package-lock.json +0 -5656
  208. package/botrun-c/admin/package.json +0 -28
  209. package/botrun-c/admin/rate-limit.sh +0 -55
  210. package/botrun-c/admin/remove-custom-limits.ts +0 -75
  211. package/botrun-c/admin/reset-rate.sh +0 -70
  212. package/botrun-c/admin/reset-trial.sh +0 -68
  213. package/botrun-c/admin/seed-trial-data.sh +0 -38
  214. package/botrun-c/admin/trial.sh +0 -55
  215. package/botrun-c/admin/tsconfig.json +0 -20
  216. package/botrun-c/admin/users-cli.py +0 -298
  217. package/botrun-c/admin/users.sh +0 -12
  218. package/botrun-c/admin/users.ts +0 -1321
  219. package/botrun-c/api/admin-tools/check-whitelist.mjs.migration-bak +0 -74
  220. package/botrun-c/api/admin-tools/create-user.mjs +0 -92
  221. package/botrun-c/api/admin-tools/create-user.mjs.migration-bak +0 -92
  222. package/botrun-c/api/admin-tools/firebase-auth-domains-v2.py +0 -287
  223. package/botrun-c/api/admin-tools/firebase-auth-domains-v2.py.migration-bak +0 -287
  224. package/botrun-c/api/admin-tools/init-firestore.mjs +0 -75
  225. package/botrun-c/api/admin-tools/init-firestore.mjs.migration-bak +0 -101
  226. package/botrun-c/api/admin-tools/list-users.mjs +0 -43
  227. package/botrun-c/api/admin-tools/list-users.mjs.migration-bak +0 -43
  228. package/botrun-c/api/admin-tools/manage-authorized-domains.ts +0 -255
  229. package/botrun-c/api/admin-tools/manage-authorized-domains.ts.migration-bak +0 -255
  230. package/botrun-c/api/admin-tools/setup-custom-claims.ts.migration-bak +0 -155
  231. package/botrun-c/api/admin-tools/test-claims.mjs +0 -69
  232. package/botrun-c/api/admin-tools/test-claims.mjs.migration-bak +0 -69
  233. package/botrun-c/api/admin-tools/update-service-urls-gcloud.mjs +0 -72
  234. package/botrun-c/api/admin-tools/update-service-urls-gcloud.mjs.migration-bak +0 -72
  235. package/botrun-c/api/admin-tools/update-service-urls-to-lb.mjs +0 -159
  236. package/botrun-c/api/admin-tools/update-service-urls-to-lb.mjs.migration-bak +0 -159
  237. package/botrun-c/api/admin-tools/update-service-urls.mjs +0 -82
  238. package/botrun-c/api/admin-tools/update-service-urls.mjs.migration-bak +0 -82
  239. package/botrun-c/api/package-lock.json +0 -5031
  240. package/botrun-c/api/package.json +0 -63
  241. package/botrun-c/api/pnpm-lock.yaml +0 -4157
  242. package/botrun-c/api/rate-limit-export-1763620678737.csv +0 -79
  243. package/botrun-c/api/scripts/README-migrate-plan-free.md +0 -197
  244. package/botrun-c/api/scripts/check-users.ts +0 -45
  245. package/botrun-c/api/scripts/delete-firestore-docs.js +0 -43
  246. package/botrun-c/api/scripts/dump-subscriptions.ts +0 -395
  247. package/botrun-c/api/scripts/list-all-users.ts +0 -65
  248. package/botrun-c/api/scripts/migrate-plan-free-rpd-16to32.ts +0 -262
  249. package/botrun-c/api/scripts/migrate-plan-names-to-lite-max.ts +0 -269
  250. package/botrun-c/api/scripts/query-user-info.ts +0 -304
  251. package/botrun-c/api/scripts/reset-rate-limit.ts +0 -206
  252. package/botrun-c/api/scripts/reset-trial-subscription.ts +0 -166
  253. package/botrun-c/api/scripts/rollback-plan-names-from-lite-max.ts +0 -139
  254. package/botrun-c/api/scripts/seed-rate-limit-test-data.ts +0 -114
  255. package/botrun-c/api/scripts/seed-subscription-test-data.ts +0 -173
  256. package/botrun-c/api/scripts/test-rate-limit-message.ts +0 -108
  257. package/botrun-c/api/test-billing-write.ts +0 -103
  258. package/botrun-c/api/test-billing.ts +0 -81
  259. package/botrun-c/api/test-chat-upload.sh +0 -162
  260. package/botrun-c/api/test-nchc-whisper.ts +0 -87
  261. package/botrun-c/api/test-skills-loading.js +0 -77
  262. package/botrun-c/api/test-skills-symlink.js +0 -118
  263. package/botrun-c/api/test-upload-multimodal.sh +0 -167
  264. package/botrun-c/api/tsconfig.json +0 -27
  265. package/botrun-c/api/verify-form-2-1.mjs +0 -229
  266. package/botrun-c/api/vitest.config.ts +0 -9
  267. package/botrun-c/appium-capabilities.json +0 -20
  268. package/botrun-c/cors.json +0 -26
  269. package/botrun-c/delete-old.sh +0 -44
  270. package/botrun-c/design/google-drive/01-architecture.md +0 -186
  271. package/botrun-c/design/google-drive/02-api-design.md +0 -314
  272. package/botrun-c/design/google-drive/03-frontend-design.md +0 -278
  273. package/botrun-c/design/google-drive/04-skill-design.md +0 -384
  274. package/botrun-c/design/google-drive/05-test-plan.md +0 -507
  275. package/botrun-c/design/google-drive/README.md +0 -32
  276. package/botrun-c/dev/mcp-token-calculator/README.md +0 -92
  277. package/botrun-c/dev/mcp-token-calculator/RESULTS.md +0 -188
  278. package/botrun-c/dev/mcp-token-calculator/calculate-current-tokens.ts +0 -369
  279. package/botrun-c/dev/mcp-token-calculator/calculate-mcp-tokens.ts +0 -464
  280. package/botrun-c/dev/mcp-token-calculator/optimization-proposals.ts +0 -316
  281. package/botrun-c/dev/mcp-token-calculator/package-lock.json +0 -599
  282. package/botrun-c/dev/mcp-token-calculator/package.json +0 -22
  283. package/botrun-c/dev/mcp-token-calculator/test-all-schemas.ts +0 -314
  284. package/botrun-c/dev/mcp-token-calculator/test-schema-correctness.ts +0 -221
  285. package/botrun-c/dev/mcp-token-calculator/verify-optimization.ts +0 -159
  286. package/botrun-c/fastlane/ANDROID-DEPLOYMENT-SETUP.md +0 -825
  287. package/botrun-c/fastlane/IOS-DEPLOYMENT-SETUP.md +0 -431
  288. package/botrun-c/fastlane/Pluginfile +0 -5
  289. package/botrun-c/fastlane/QUICK-START.md +0 -133
  290. package/botrun-c/fastlane/README.md +0 -424
  291. package/botrun-c/fastlane/RUBY-SETUP.md +0 -449
  292. package/botrun-c/fastlane/android/Appfile +0 -11
  293. package/botrun-c/fastlane/android/Fastfile +0 -240
  294. package/botrun-c/firebase.json +0 -46
  295. package/botrun-c/firestore-init-data.json +0 -40
  296. package/botrun-c/firestore.indexes.json +0 -55
  297. package/botrun-c/firestore.rules +0 -53
  298. package/botrun-c/fix-codex-permissions.sh +0 -56
  299. package/botrun-c/gcs-lifecycle-30day-retention.json +0 -12
  300. package/botrun-c/html-architecture/architecture/deployment.html +0 -664
  301. package/botrun-c/html-architecture/architecture/index.html +0 -309
  302. package/botrun-c/html-architecture/architecture/styles.css +0 -500
  303. package/botrun-c/html-architecture/architecture/system-components.html +0 -538
  304. package/botrun-c/html-architecture/architecture/user-flow.html +0 -370
  305. package/botrun-c/mobile-android/FIREBASE-AUTH-SETUP.md +0 -302
  306. package/botrun-c/mobile-android/WSL-ANDROID-SETUP.md +0 -252
  307. package/botrun-c/mobile-android/android/app/build.gradle +0 -75
  308. package/botrun-c/mobile-android/android/app/capacitor.build.gradle +0 -31
  309. package/botrun-c/mobile-android/android/app/proguard-rules.pro +0 -21
  310. package/botrun-c/mobile-android/android/build.gradle +0 -29
  311. package/botrun-c/mobile-android/android/capacitor.settings.gradle +0 -42
  312. package/botrun-c/mobile-android/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  313. package/botrun-c/mobile-android/android/gradle/wrapper/gradle-wrapper.properties +0 -7
  314. package/botrun-c/mobile-android/android/gradle.properties +0 -22
  315. package/botrun-c/mobile-android/android/gradlew +0 -252
  316. package/botrun-c/mobile-android/android/gradlew.bat +0 -94
  317. package/botrun-c/mobile-android/android/settings.gradle +0 -5
  318. package/botrun-c/mobile-android/android/variables.gradle +0 -16
  319. package/botrun-c/mobile-android/capacitor.config.ts +0 -44
  320. package/botrun-c/mobile-android/fastlane/Appfile +0 -11
  321. package/botrun-c/mobile-android/fastlane/Fastfile +0 -202
  322. package/botrun-c/mobile-android/fastlane/README.md +0 -96
  323. package/botrun-c/mobile-android/google-services.json.template +0 -39
  324. package/botrun-c/mobile-android/package.json +0 -32
  325. package/botrun-c/mobile-android/resources/icon.png +0 -0
  326. package/botrun-c/mobile-android/tsconfig.json +0 -15
  327. package/botrun-c/mobile-ios/.ruby-version +0 -1
  328. package/botrun-c/mobile-ios/BUILD-LOG.md +0 -82
  329. package/botrun-c/mobile-ios/Gemfile +0 -4
  330. package/botrun-c/mobile-ios/Gemfile.lock +0 -299
  331. package/botrun-c/mobile-ios/GoogleService-Info.plist.template +0 -34
  332. package/botrun-c/mobile-ios/IOS-PERMISSIONS.md +0 -80
  333. package/botrun-c/mobile-ios/QUICK-START.md +0 -189
  334. package/botrun-c/mobile-ios/README.md +0 -231
  335. package/botrun-c/mobile-ios/capacitor.config.ts +0 -46
  336. package/botrun-c/mobile-ios/fastlane/Fastfile +0 -326
  337. package/botrun-c/mobile-ios/fastlane/README.md +0 -88
  338. package/botrun-c/mobile-ios/ios/App/App/App.entitlements +0 -10
  339. package/botrun-c/mobile-ios/ios/App/App/AppDelegate.swift +0 -49
  340. package/botrun-c/mobile-ios/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png +0 -0
  341. package/botrun-c/mobile-ios/ios/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json +0 -14
  342. package/botrun-c/mobile-ios/ios/App/App/Assets.xcassets/Contents.json +0 -6
  343. package/botrun-c/mobile-ios/ios/App/App/Assets.xcassets/Splash.imageset/Contents.json +0 -56
  344. package/botrun-c/mobile-ios/ios/App/App/Assets.xcassets/Splash.imageset/Default@1x~universal~anyany-dark.png +0 -0
  345. package/botrun-c/mobile-ios/ios/App/App/Assets.xcassets/Splash.imageset/Default@1x~universal~anyany.png +0 -0
  346. package/botrun-c/mobile-ios/ios/App/App/Assets.xcassets/Splash.imageset/Default@2x~universal~anyany-dark.png +0 -0
  347. package/botrun-c/mobile-ios/ios/App/App/Assets.xcassets/Splash.imageset/Default@2x~universal~anyany.png +0 -0
  348. package/botrun-c/mobile-ios/ios/App/App/Assets.xcassets/Splash.imageset/Default@3x~universal~anyany-dark.png +0 -0
  349. package/botrun-c/mobile-ios/ios/App/App/Assets.xcassets/Splash.imageset/Default@3x~universal~anyany.png +0 -0
  350. package/botrun-c/mobile-ios/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-1.png +0 -0
  351. package/botrun-c/mobile-ios/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-2.png +0 -0
  352. package/botrun-c/mobile-ios/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732.png +0 -0
  353. package/botrun-c/mobile-ios/ios/App/App/Base.lproj/LaunchScreen.storyboard +0 -32
  354. package/botrun-c/mobile-ios/ios/App/App/Base.lproj/Main.storyboard +0 -19
  355. package/botrun-c/mobile-ios/ios/App/App/Info.plist +0 -72
  356. package/botrun-c/mobile-ios/ios/App/App.xcodeproj/project.pbxproj +0 -446
  357. package/botrun-c/mobile-ios/ios/App/App.xcodeproj/xcshareddata/xcschemes/App.xcscheme +0 -78
  358. package/botrun-c/mobile-ios/ios/App/App.xcworkspace/contents.xcworkspacedata +0 -10
  359. package/botrun-c/mobile-ios/ios/App/App.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
  360. package/botrun-c/mobile-ios/ios/App/Podfile +0 -52
  361. package/botrun-c/mobile-ios/ios/App/Podfile.lock +0 -205
  362. package/botrun-c/mobile-ios/ios/App/add_plist.rb +0 -30
  363. package/botrun-c/mobile-ios/ios/App/fix_plist_path.rb +0 -22
  364. package/botrun-c/mobile-ios/package.json +0 -29
  365. package/botrun-c/mobile-ios/resources/icon.png +0 -0
  366. package/botrun-c/mobile-ios/run-simulator.sh +0 -34
  367. package/botrun-c/mobile-ios/screenshot-after-clean-rebuild.png +0 -0
  368. package/botrun-c/mobile-ios/screenshot-final-version.png +0 -0
  369. package/botrun-c/mobile-ios/screenshot-simulator.png +0 -0
  370. package/botrun-c/mobile-ios/screenshot-version-1009.png +0 -0
  371. package/botrun-c/mobile-ios/screenshot-with-version.png +0 -0
  372. package/botrun-c/mobile-ios/screenshot-working.png +0 -0
  373. package/botrun-c/mobile-ios/setup-ios.sh +0 -106
  374. package/botrun-c/mobile-ios/tsconfig.json +0 -16
  375. package/botrun-c/org-policy-allow-all-users.yaml +0 -3
  376. package/botrun-c/package.json +0 -68
  377. package/botrun-c/pnpm-lock.yaml +0 -10198
  378. package/botrun-c/pnpm-workspace.yaml +0 -6
  379. package/botrun-c/prompts/BOTRUN-1.md +0 -13
  380. package/botrun-c/prompts/BOTRUN-2.md +0 -70
  381. package/botrun-c/prompts/BOTRUN-3.md +0 -139
  382. package/botrun-c/prototypes/firestore-gax-debug/package-lock.json +0 -1204
  383. package/botrun-c/prototypes/firestore-gax-debug/package.json +0 -12
  384. package/botrun-c/prototypes/firestore-gax-debug/pnpm-workspace.yaml +0 -2
  385. package/botrun-c/prototypes/firestore-gax-debug/test.js +0 -98
  386. package/botrun-c/prototypes/taiwan-haiku/README.md +0 -93
  387. package/botrun-c/prototypes/taiwan-haiku/index.ts +0 -139
  388. package/botrun-c/prototypes/taiwan-haiku/package.json +0 -23
  389. package/botrun-c/prototypes/taiwan-haiku/pnpm-lock.yaml +0 -730
  390. package/botrun-c/prototypes/taiwan-haiku/test-vertex-direct.ts +0 -83
  391. package/botrun-c/prototypes/taiwan-haiku/tsconfig.json +0 -18
  392. package/botrun-c/prototypes/web-search/FINDINGS-STAGE-1.md +0 -48
  393. package/botrun-c/prototypes/web-search/README.md +0 -46
  394. package/botrun-c/prototypes/web-search/VERIFICATION-REPORT.md +0 -310
  395. package/botrun-c/prototypes/web-search/package-lock.json +0 -595
  396. package/botrun-c/prototypes/web-search/package.json +0 -18
  397. package/botrun-c/prototypes/web-search/stage-1.1-basic-connection.js +0 -123
  398. package/botrun-c/prototypes/web-search/stage-1.2-with-websearch.js +0 -154
  399. package/botrun-c/prototypes/web-search/stage-1.3-search-query.js +0 -192
  400. package/botrun-c/prototypes/web-search/stage-2-agent-sdk-websearch.js +0 -265
  401. package/botrun-c/runtime-scripts/env-detect.sh +0 -64
  402. package/botrun-c/scripts/github-collaborator-manager.sh +0 -297
  403. package/botrun-c/scripts/github-collaborator-manager.ts +0 -325
  404. package/botrun-c/scripts/manage-collaborators.sh +0 -91
  405. package/botrun-c/scripts/setup-secrets.sh +0 -202
  406. package/botrun-c/scripts/stress-test-17-users.sh +0 -113
  407. package/botrun-c/scripts/stress-test-api.sh +0 -119
  408. package/botrun-c/scripts/stress-test-full-report.sh +0 -385
  409. package/botrun-c/specs/infra-vm/design.md +0 -0
  410. package/botrun-c/specs/share-skill/design.md +0 -1146
  411. package/botrun-c/specs/share-skill/tasks.md +0 -444
  412. package/botrun-c/stress-test-results/20260121-162630/config.json +0 -7
  413. package/botrun-c/stress-test-results/20260121-162630/user-1-status.txt +0 -1
  414. package/botrun-c/stress-test-results/20260121-162630/user-1-timing.txt +0 -1
  415. package/botrun-c/stress-test-results/20260121-162630/user-1.json +0 -2
  416. package/botrun-c/stress-test-results/20260121-162646/config.json +0 -7
  417. package/botrun-c/stress-test-results/20260121-162646/user-1-status.txt +0 -1
  418. package/botrun-c/stress-test-results/20260121-162646/user-1-timing.txt +0 -1
  419. package/botrun-c/stress-test-results/20260121-162646/user-1.json +0 -2
  420. package/botrun-c/stress-test-results/20260121-162646/user-10-status.txt +0 -1
  421. package/botrun-c/stress-test-results/20260121-162646/user-10-timing.txt +0 -1
  422. package/botrun-c/stress-test-results/20260121-162646/user-10.json +0 -2
  423. package/botrun-c/stress-test-results/20260121-162646/user-11-status.txt +0 -1
  424. package/botrun-c/stress-test-results/20260121-162646/user-11-timing.txt +0 -1
  425. package/botrun-c/stress-test-results/20260121-162646/user-11.json +0 -2
  426. package/botrun-c/stress-test-results/20260121-162646/user-12-status.txt +0 -1
  427. package/botrun-c/stress-test-results/20260121-162646/user-12-timing.txt +0 -1
  428. package/botrun-c/stress-test-results/20260121-162646/user-12.json +0 -2
  429. package/botrun-c/stress-test-results/20260121-162646/user-13-status.txt +0 -1
  430. package/botrun-c/stress-test-results/20260121-162646/user-13-timing.txt +0 -1
  431. package/botrun-c/stress-test-results/20260121-162646/user-13.json +0 -2
  432. package/botrun-c/stress-test-results/20260121-162646/user-14-status.txt +0 -1
  433. package/botrun-c/stress-test-results/20260121-162646/user-14-timing.txt +0 -1
  434. package/botrun-c/stress-test-results/20260121-162646/user-14.json +0 -2
  435. package/botrun-c/stress-test-results/20260121-162646/user-15-status.txt +0 -1
  436. package/botrun-c/stress-test-results/20260121-162646/user-15-timing.txt +0 -1
  437. package/botrun-c/stress-test-results/20260121-162646/user-15.json +0 -2
  438. package/botrun-c/stress-test-results/20260121-162646/user-16-status.txt +0 -1
  439. package/botrun-c/stress-test-results/20260121-162646/user-16-timing.txt +0 -1
  440. package/botrun-c/stress-test-results/20260121-162646/user-16.json +0 -2
  441. package/botrun-c/stress-test-results/20260121-162646/user-17-status.txt +0 -1
  442. package/botrun-c/stress-test-results/20260121-162646/user-17-timing.txt +0 -1
  443. package/botrun-c/stress-test-results/20260121-162646/user-17.json +0 -2
  444. package/botrun-c/stress-test-results/20260121-162646/user-2-status.txt +0 -1
  445. package/botrun-c/stress-test-results/20260121-162646/user-2-timing.txt +0 -1
  446. package/botrun-c/stress-test-results/20260121-162646/user-2.json +0 -2
  447. package/botrun-c/stress-test-results/20260121-162646/user-3-status.txt +0 -1
  448. package/botrun-c/stress-test-results/20260121-162646/user-3-timing.txt +0 -1
  449. package/botrun-c/stress-test-results/20260121-162646/user-3.json +0 -2
  450. package/botrun-c/stress-test-results/20260121-162646/user-4-status.txt +0 -1
  451. package/botrun-c/stress-test-results/20260121-162646/user-4-timing.txt +0 -1
  452. package/botrun-c/stress-test-results/20260121-162646/user-4.json +0 -2
  453. package/botrun-c/stress-test-results/20260121-162646/user-5-status.txt +0 -1
  454. package/botrun-c/stress-test-results/20260121-162646/user-5-timing.txt +0 -1
  455. package/botrun-c/stress-test-results/20260121-162646/user-5.json +0 -2
  456. package/botrun-c/stress-test-results/20260121-162646/user-6-status.txt +0 -1
  457. package/botrun-c/stress-test-results/20260121-162646/user-6-timing.txt +0 -1
  458. package/botrun-c/stress-test-results/20260121-162646/user-6.json +0 -2
  459. package/botrun-c/stress-test-results/20260121-162646/user-7-status.txt +0 -1
  460. package/botrun-c/stress-test-results/20260121-162646/user-7-timing.txt +0 -1
  461. package/botrun-c/stress-test-results/20260121-162646/user-7.json +0 -2
  462. package/botrun-c/stress-test-results/20260121-162646/user-8-status.txt +0 -1
  463. package/botrun-c/stress-test-results/20260121-162646/user-8-timing.txt +0 -1
  464. package/botrun-c/stress-test-results/20260121-162646/user-8.json +0 -2
  465. package/botrun-c/stress-test-results/20260121-162646/user-9-status.txt +0 -1
  466. package/botrun-c/stress-test-results/20260121-162646/user-9-timing.txt +0 -1
  467. package/botrun-c/stress-test-results/20260121-162646/user-9.json +0 -2
  468. package/botrun-c/stress-test-results/20260121-163148/REPORT.html +0 -344
  469. package/botrun-c/stress-test-results/20260121-163148/REPORT.md +0 -93
  470. package/botrun-c/stress-test-results/20260121-163148/REPORT.pdf +0 -0
  471. package/botrun-c/stress-test-results/20260121-163148/config.json +0 -7
  472. package/botrun-c/stress-test-results/20260121-163148/summary.json +0 -26
  473. package/botrun-c/stress-test-results/20260121-163148/user-1-status.txt +0 -1
  474. package/botrun-c/stress-test-results/20260121-163148/user-1-timing.txt +0 -1
  475. package/botrun-c/stress-test-results/20260121-163148/user-1.json +0 -206
  476. package/botrun-c/stress-test-results/20260121-163148/user-10-status.txt +0 -1
  477. package/botrun-c/stress-test-results/20260121-163148/user-10-timing.txt +0 -1
  478. package/botrun-c/stress-test-results/20260121-163148/user-10.json +0 -110
  479. package/botrun-c/stress-test-results/20260121-163148/user-11-status.txt +0 -1
  480. package/botrun-c/stress-test-results/20260121-163148/user-11-timing.txt +0 -1
  481. package/botrun-c/stress-test-results/20260121-163148/user-11.json +0 -104
  482. package/botrun-c/stress-test-results/20260121-163148/user-12-status.txt +0 -1
  483. package/botrun-c/stress-test-results/20260121-163148/user-12-timing.txt +0 -1
  484. package/botrun-c/stress-test-results/20260121-163148/user-12.json +0 -98
  485. package/botrun-c/stress-test-results/20260121-163148/user-13-status.txt +0 -1
  486. package/botrun-c/stress-test-results/20260121-163148/user-13-timing.txt +0 -1
  487. package/botrun-c/stress-test-results/20260121-163148/user-13.json +0 -68
  488. package/botrun-c/stress-test-results/20260121-163148/user-14-status.txt +0 -1
  489. package/botrun-c/stress-test-results/20260121-163148/user-14-timing.txt +0 -1
  490. package/botrun-c/stress-test-results/20260121-163148/user-14.json +0 -116
  491. package/botrun-c/stress-test-results/20260121-163148/user-15-status.txt +0 -1
  492. package/botrun-c/stress-test-results/20260121-163148/user-15-timing.txt +0 -1
  493. package/botrun-c/stress-test-results/20260121-163148/user-15.json +0 -131
  494. package/botrun-c/stress-test-results/20260121-163148/user-16-status.txt +0 -1
  495. package/botrun-c/stress-test-results/20260121-163148/user-16-timing.txt +0 -1
  496. package/botrun-c/stress-test-results/20260121-163148/user-16.json +0 -110
  497. package/botrun-c/stress-test-results/20260121-163148/user-17-status.txt +0 -1
  498. package/botrun-c/stress-test-results/20260121-163148/user-17-timing.txt +0 -1
  499. package/botrun-c/stress-test-results/20260121-163148/user-17.json +0 -116
  500. package/botrun-c/stress-test-results/20260121-163148/user-2-status.txt +0 -1
  501. package/botrun-c/stress-test-results/20260121-163148/user-2-timing.txt +0 -1
  502. package/botrun-c/stress-test-results/20260121-163148/user-2.json +0 -98
  503. package/botrun-c/stress-test-results/20260121-163148/user-3-status.txt +0 -1
  504. package/botrun-c/stress-test-results/20260121-163148/user-3-timing.txt +0 -1
  505. package/botrun-c/stress-test-results/20260121-163148/user-3.json +0 -62
  506. package/botrun-c/stress-test-results/20260121-163148/user-4-status.txt +0 -1
  507. package/botrun-c/stress-test-results/20260121-163148/user-4-timing.txt +0 -1
  508. package/botrun-c/stress-test-results/20260121-163148/user-4.json +0 -113
  509. package/botrun-c/stress-test-results/20260121-163148/user-5-status.txt +0 -1
  510. package/botrun-c/stress-test-results/20260121-163148/user-5-timing.txt +0 -1
  511. package/botrun-c/stress-test-results/20260121-163148/user-5.json +0 -83
  512. package/botrun-c/stress-test-results/20260121-163148/user-6-status.txt +0 -1
  513. package/botrun-c/stress-test-results/20260121-163148/user-6-timing.txt +0 -1
  514. package/botrun-c/stress-test-results/20260121-163148/user-6.json +0 -125
  515. package/botrun-c/stress-test-results/20260121-163148/user-7-status.txt +0 -1
  516. package/botrun-c/stress-test-results/20260121-163148/user-7-timing.txt +0 -1
  517. package/botrun-c/stress-test-results/20260121-163148/user-7.json +0 -62
  518. package/botrun-c/stress-test-results/20260121-163148/user-8-status.txt +0 -1
  519. package/botrun-c/stress-test-results/20260121-163148/user-8-timing.txt +0 -1
  520. package/botrun-c/stress-test-results/20260121-163148/user-8.json +0 -41
  521. package/botrun-c/stress-test-results/20260121-163148/user-9-status.txt +0 -1
  522. package/botrun-c/stress-test-results/20260121-163148/user-9-timing.txt +0 -1
  523. package/botrun-c/stress-test-results/20260121-163148/user-9.json +0 -53
  524. package/botrun-c/test-merge.md +0 -1
  525. package/botrun-c/version.txt +0 -1
  526. package/botrun-c/web/.version +0 -1
  527. package/botrun-c/web/ALLOW-PUBLIC-ACCESS.md +0 -57
  528. package/botrun-c/web/FINAL-SETUP-STEPS.md +0 -71
  529. package/botrun-c/web/FIREBASE-MANUAL-STEPS.md +0 -41
  530. package/botrun-c/web/QUICK-SETUP-HELPER.md +0 -79
  531. package/botrun-c/web/README.md +0 -162
  532. package/botrun-c/web/__tests__/image-extractor.test.ts +0 -147
  533. package/botrun-c/web/__tests__/useGitHub.test.ts +0 -245
  534. package/botrun-c/web/app/favicon.ico +0 -0
  535. package/botrun-c/web/app/globals.css +0 -215
  536. package/botrun-c/web/app/image-preview/page.tsx +0 -213
  537. package/botrun-c/web/app/layout.tsx +0 -46
  538. package/botrun-c/web/app/page.tsx +0 -45
  539. package/botrun-c/web/components/CapacitorProvider.tsx +0 -43
  540. package/botrun-c/web/components/ChatPage.tsx +0 -1736
  541. package/botrun-c/web/components/ConversationList.tsx +0 -237
  542. package/botrun-c/web/components/ConversationListItem.tsx +0 -121
  543. package/botrun-c/web/components/LoginPage.tsx +0 -212
  544. package/botrun-c/web/components/Providers.tsx +0 -12
  545. package/botrun-c/web/components/StatusBarProvider.tsx +0 -16
  546. package/botrun-c/web/components/attachment-preview.tsx +0 -193
  547. package/botrun-c/web/components/botrun-incubator/BotrunIncubator.tsx +0 -266
  548. package/botrun-c/web/components/botrun-incubator/index.ts +0 -1
  549. package/botrun-c/web/components/chat-input.tsx +0 -1251
  550. package/botrun-c/web/components/chat-message.tsx +0 -598
  551. package/botrun-c/web/components/drop-zone.tsx +0 -143
  552. package/botrun-c/web/components/gdrive/gdrive-add-dialog.tsx +0 -254
  553. package/botrun-c/web/components/gdrive/gdrive-drawer.tsx +0 -236
  554. package/botrun-c/web/components/gdrive/gdrive-selector.tsx +0 -226
  555. package/botrun-c/web/components/gdrive/index.ts +0 -7
  556. package/botrun-c/web/components/generated-image-card.tsx +0 -363
  557. package/botrun-c/web/components/github/GitHubConnect.tsx +0 -366
  558. package/botrun-c/web/components/github/index.ts +0 -2
  559. package/botrun-c/web/components/import-skill-dialog.tsx +0 -208
  560. package/botrun-c/web/components/scroll-to-bottom-button.tsx +0 -90
  561. package/botrun-c/web/components/share-skill-dialog.tsx +0 -175
  562. package/botrun-c/web/components/skills-editor.tsx +0 -443
  563. package/botrun-c/web/components/skills-manager-drawer.tsx +0 -586
  564. package/botrun-c/web/components/smart-button-group.tsx +0 -132
  565. package/botrun-c/web/config/smart-buttons.ts +0 -295
  566. package/botrun-c/web/config/welcome-messages.ts +0 -29
  567. package/botrun-c/web/contexts/AuthContext.tsx +0 -220
  568. package/botrun-c/web/hooks/use-toast.ts +0 -187
  569. package/botrun-c/web/hooks/useAppStoreUpdate.ts +0 -356
  570. package/botrun-c/web/hooks/useAppVersion.ts +0 -113
  571. package/botrun-c/web/hooks/useAttachments.ts +0 -269
  572. package/botrun-c/web/hooks/useAuthToken.ts +0 -59
  573. package/botrun-c/web/hooks/useAutoWelcome.ts +0 -89
  574. package/botrun-c/web/hooks/useCapacitorPlatform.ts +0 -113
  575. package/botrun-c/web/hooks/useCapawesomeLiveUpdate.ts +0 -149
  576. package/botrun-c/web/hooks/useClaudeModel.ts +0 -118
  577. package/botrun-c/web/hooks/useConversation.ts +0 -115
  578. package/botrun-c/web/hooks/useConversations.ts +0 -235
  579. package/botrun-c/web/hooks/useGdriveFolders.ts +0 -199
  580. package/botrun-c/web/hooks/useGdriveSync.ts +0 -107
  581. package/botrun-c/web/hooks/useGitHub.ts +0 -409
  582. package/botrun-c/web/hooks/useScrollToBottom.ts +0 -150
  583. package/botrun-c/web/hooks/useStatusBar.ts +0 -42
  584. package/botrun-c/web/hooks/useUserPlan.ts +0 -153
  585. package/botrun-c/web/lib/api-config.ts +0 -162
  586. package/botrun-c/web/lib/api-url.ts +0 -62
  587. package/botrun-c/web/lib/api.ts +0 -105
  588. package/botrun-c/web/lib/attachment-utils.ts +0 -168
  589. package/botrun-c/web/lib/build-version.ts +0 -11
  590. package/botrun-c/web/lib/clipboard-utils.ts +0 -199
  591. package/botrun-c/web/lib/constants.ts +0 -62
  592. package/botrun-c/web/lib/conversation-storage.ts +0 -324
  593. package/botrun-c/web/lib/firebase-capacitor.ts +0 -135
  594. package/botrun-c/web/lib/firebase.ts +0 -137
  595. package/botrun-c/web/lib/firestore-auth.ts +0 -61
  596. package/botrun-c/web/lib/firestore-auth.ts.migration-bak +0 -319
  597. package/botrun-c/web/lib/image-extractor.ts +0 -156
  598. package/botrun-c/web/lib/path-utils.ts +0 -48
  599. package/botrun-c/web/lib/rehype-del-to-mark.ts +0 -19
  600. package/botrun-c/web/lib/upload-service.ts +0 -326
  601. package/botrun-c/web/lib/utils.ts +0 -6
  602. package/botrun-c/web/next.config.js +0 -49
  603. package/botrun-c/web/package-lock.json +0 -6711
  604. package/botrun-c/web/package.json +0 -63
  605. package/botrun-c/web/postcss.config.js +0 -6
  606. package/botrun-c/web/public/apple_icon.png +0 -0
  607. package/botrun-c/web/public/google_icon.png +0 -0
  608. package/botrun-c/web/public/logo-48-small.png +0 -0
  609. package/botrun-c/web/public/logo-48.png +0 -0
  610. package/botrun-c/web/scripts/generate-version.js +0 -36
  611. package/botrun-c/web/scripts/update-build-version.js +0 -41
  612. package/botrun-c/web/tailwind.config.ts +0 -80
  613. package/botrun-c/web/test-welcome-message.mjs +0 -115
  614. package/botrun-c/web/tsconfig.json +0 -42
  615. package/botrun-c/web/types/agent.ts +0 -6
  616. package/botrun-c/web/types/attachment.ts +0 -39
  617. package/botrun-c/web/types/conversation.ts +0 -68
  618. package/botrun-c/web/types/flutter.d.ts +0 -6
  619. package/botrun-c/web/types/project.ts +0 -41
  620. package/botrun-c/web/types/skills.ts +0 -13
  621. 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
  622. package/lib/writing/index.mjs +0 -5
  623. /package/{lib/core → modules/_core}/adapters/base.mjs +0 -0
  624. /package/{lib/core → modules/_core}/adapters/claude.mjs +0 -0
  625. /package/{lib/core → modules/_core}/adapters/gemini-api.mjs +0 -0
  626. /package/{lib/core → modules/_core}/adapters/gemini-shared.mjs +0 -0
  627. /package/{lib/core → modules/_core}/adapters/gemini-vertex.mjs +0 -0
  628. /package/{lib/core → modules/_core}/adapters/local.mjs +0 -0
  629. /package/{lib/core → modules/_core}/adapters/nchc.mjs +0 -0
  630. /package/{lib/core → modules/_core}/adapters/openai-shared.mjs +0 -0
  631. /package/{lib/core → modules/_core}/adapters/openrouter.mjs +0 -0
  632. /package/{lib/core → modules/_core}/ai-cache.mjs +0 -0
  633. /package/{lib/core → modules/_core}/ai-router.mjs +0 -0
  634. /package/{lib/core → modules/_core}/cli-utils.mjs +0 -0
  635. /package/{lib/core → modules/_core}/dag.mjs +0 -0
  636. /package/{lib/core → modules/_core}/db.mjs +0 -0
  637. /package/{lib/core → modules/_core}/env.mjs +0 -0
  638. /package/{lib/flows → modules/_core}/hatch-portal.mjs +0 -0
  639. /package/{lib/core → modules/_core}/llm.mjs +0 -0
  640. /package/{lib/flows → modules/_core}/opencode-agent.mjs +0 -0
  641. /package/{lib/core → modules/_core}/paths.mjs +0 -0
  642. /package/{lib/core → modules/_core}/proxy.mjs +0 -0
  643. /package/{lib/flows → modules/_core}/review-doc.mjs +0 -0
  644. /package/{lib → modules/_core}/tools/fs-tools.mjs +0 -0
  645. /package/{lib → modules/_core}/tools/index.mjs +0 -0
  646. /package/{lib/core → modules/_core}/watermelon.mjs +0 -0
  647. /package/{lib/ocr/index.mjs → modules/ocr/ocr-lib.mjs} +0 -0
  648. /package/{lib → modules}/portal/hatch.mjs +0 -0
  649. /package/{lib/portal/index.mjs → modules/portal/portal-lib.mjs} +0 -0
  650. /package/{lib → modules}/search/crawler.mjs +0 -0
  651. /package/{lib/prompt → modules/skill}/prompts/zero-framework/coding.md +0 -0
  652. /package/{lib/prompt → modules/skill}/prompts/zero-framework/fullstack.md +0 -0
  653. /package/{lib/prompt → modules/skill}/prompts/zero-framework/gemini-search-cli.md +0 -0
  654. /package/{lib/prompt → modules/skill}/prompts/zero-framework/search.md +0 -0
  655. /package/{lib/prompt → modules/skill}/prompts/zero-framework/segment.md +0 -0
  656. /package/{lib/prompt → modules/skill}/prompts/zero-framework/slice.md +0 -0
  657. /package/{lib/prompt → modules/skill}/prompts/zero-framework/smart-cache.md +0 -0
  658. /package/{lib/prompt → modules/skill}/prompts/zero-framework/trigram-search.md +0 -0
  659. /package/{lib/prompt → modules/skill}/prompts/zero-framework/tts-format.md +0 -0
  660. /package/{lib/prompt → modules/skill}/prompts/zero-framework/watermelon-sqlite.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