interlatch 0.28.2__tar.gz → 0.28.4__tar.gz

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 (498) hide show
  1. {interlatch-0.28.2 → interlatch-0.28.4}/CHANGELOG.md +25 -0
  2. {interlatch-0.28.2 → interlatch-0.28.4}/PKG-INFO +1 -1
  3. interlatch-0.28.4/changelog.d/hub-pages.md +4 -0
  4. {interlatch-0.28.2 → interlatch-0.28.4}/docs/devices.md +10 -10
  5. {interlatch-0.28.2 → interlatch-0.28.4}/docs/docker.md +1 -1
  6. {interlatch-0.28.2 → interlatch-0.28.4}/docs/ja/devices.md +11 -11
  7. {interlatch-0.28.2 → interlatch-0.28.4}/docs/ja/docker.md +1 -1
  8. {interlatch-0.28.2 → interlatch-0.28.4}/docs/ja/join-a-hub.md +5 -5
  9. {interlatch-0.28.2 → interlatch-0.28.4}/docs/ja/profiles.md +2 -2
  10. {interlatch-0.28.2 → interlatch-0.28.4}/docs/ja/troubleshooting.md +1 -1
  11. {interlatch-0.28.2 → interlatch-0.28.4}/docs/join-a-hub.md +5 -5
  12. {interlatch-0.28.2 → interlatch-0.28.4}/docs/profiles.md +6 -3
  13. {interlatch-0.28.2 → interlatch-0.28.4}/docs/troubleshooting.md +1 -1
  14. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/core/i18n/ja.py +3 -0
  15. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/interfaces/cli/commands/sharing.py +4 -1
  16. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/interfaces/web/api/devices.py +2 -0
  17. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/interfaces/web/api/hubs.py +89 -3
  18. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/interfaces/web/api/status.py +26 -4
  19. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/interfaces/web/routes.py +30 -6
  20. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/app.css +6 -0
  21. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/app.js +193 -103
  22. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/ja.js +39 -7
  23. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_profile_hubs.py +114 -0
  24. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_route_matrix.py +1 -1
  25. interlatch-0.28.2/changelog.d/hub-groups.md +0 -6
  26. interlatch-0.28.2/changelog.d/hub-take-back.md +0 -6
  27. interlatch-0.28.2/changelog.d/hubs-dashboard.md +0 -3
  28. {interlatch-0.28.2 → interlatch-0.28.4}/.github/dependabot.yml +0 -0
  29. {interlatch-0.28.2 → interlatch-0.28.4}/.github/windows-ja-qa/driver.ps1 +0 -0
  30. {interlatch-0.28.2 → interlatch-0.28.4}/.github/windows-ja-qa/ja_check.py +0 -0
  31. {interlatch-0.28.2 → interlatch-0.28.4}/.github/windows-ja-qa/report.py +0 -0
  32. {interlatch-0.28.2 → interlatch-0.28.4}/.github/windows-ja-qa/shots.py +0 -0
  33. {interlatch-0.28.2 → interlatch-0.28.4}/.github/windows-ja-qa/user.ps1 +0 -0
  34. {interlatch-0.28.2 → interlatch-0.28.4}/.github/workflows/changelog.yml +0 -0
  35. {interlatch-0.28.2 → interlatch-0.28.4}/.github/workflows/ci.yml +0 -0
  36. {interlatch-0.28.2 → interlatch-0.28.4}/.github/workflows/docs.yml +0 -0
  37. {interlatch-0.28.2 → interlatch-0.28.4}/.github/workflows/release.yml +0 -0
  38. {interlatch-0.28.2 → interlatch-0.28.4}/.github/workflows/windows-ja-qa.yml +0 -0
  39. {interlatch-0.28.2 → interlatch-0.28.4}/.gitignore +0 -0
  40. {interlatch-0.28.2 → interlatch-0.28.4}/.pre-commit-config.yaml +0 -0
  41. {interlatch-0.28.2 → interlatch-0.28.4}/.python-version +0 -0
  42. {interlatch-0.28.2 → interlatch-0.28.4}/CONTRIBUTING.md +0 -0
  43. {interlatch-0.28.2 → interlatch-0.28.4}/LICENSE +0 -0
  44. {interlatch-0.28.2 → interlatch-0.28.4}/README.ja.md +0 -0
  45. {interlatch-0.28.2 → interlatch-0.28.4}/README.md +0 -0
  46. {interlatch-0.28.2 → interlatch-0.28.4}/ROADMAP.ja.md +0 -0
  47. {interlatch-0.28.2 → interlatch-0.28.4}/ROADMAP.md +0 -0
  48. {interlatch-0.28.2 → interlatch-0.28.4}/SECURITY.md +0 -0
  49. {interlatch-0.28.2 → interlatch-0.28.4}/careers/README.md +0 -0
  50. {interlatch-0.28.2 → interlatch-0.28.4}/careers/boards.json +0 -0
  51. {interlatch-0.28.2 → interlatch-0.28.4}/careers/pipeline.py +0 -0
  52. {interlatch-0.28.2 → interlatch-0.28.4}/changelog.d/README.md +0 -0
  53. {interlatch-0.28.2 → interlatch-0.28.4}/dev.sh +0 -0
  54. {interlatch-0.28.2 → interlatch-0.28.4}/docker/.dockerignore +0 -0
  55. {interlatch-0.28.2 → interlatch-0.28.4}/docker/.env.example +0 -0
  56. {interlatch-0.28.2 → interlatch-0.28.4}/docker/Caddyfile +0 -0
  57. {interlatch-0.28.2 → interlatch-0.28.4}/docker/Dockerfile +0 -0
  58. {interlatch-0.28.2 → interlatch-0.28.4}/docker/compose.yaml +0 -0
  59. {interlatch-0.28.2 → interlatch-0.28.4}/docker/wheels/.gitignore +0 -0
  60. {interlatch-0.28.2 → interlatch-0.28.4}/docs/README.md +0 -0
  61. {interlatch-0.28.2 → interlatch-0.28.4}/docs/_site/hooks.py +0 -0
  62. {interlatch-0.28.2 → interlatch-0.28.4}/docs/analysis.md +0 -0
  63. {interlatch-0.28.2 → interlatch-0.28.4}/docs/cli.md +0 -0
  64. {interlatch-0.28.2 → interlatch-0.28.4}/docs/configuration.md +0 -0
  65. {interlatch-0.28.2 → interlatch-0.28.4}/docs/dashboard.md +0 -0
  66. {interlatch-0.28.2 → interlatch-0.28.4}/docs/demo/make_demo.py +0 -0
  67. {interlatch-0.28.2 → interlatch-0.28.4}/docs/demo/record_demo.py +0 -0
  68. {interlatch-0.28.2 → interlatch-0.28.4}/docs/design/innerlatch.md +0 -0
  69. {interlatch-0.28.2 → interlatch-0.28.4}/docs/design/ja/innerlatch.md +0 -0
  70. {interlatch-0.28.2 → interlatch-0.28.4}/docs/design/ja/learning.md +0 -0
  71. {interlatch-0.28.2 → interlatch-0.28.4}/docs/design/learning.md +0 -0
  72. {interlatch-0.28.2 → interlatch-0.28.4}/docs/development.md +0 -0
  73. {interlatch-0.28.2 → interlatch-0.28.4}/docs/diagrams/analysis.excalidraw.svg +0 -0
  74. {interlatch-0.28.2 → interlatch-0.28.4}/docs/diagrams/architecture.excalidraw.svg +0 -0
  75. {interlatch-0.28.2 → interlatch-0.28.4}/docs/faq.md +0 -0
  76. {interlatch-0.28.2 → interlatch-0.28.4}/docs/images/demo.gif +0 -0
  77. {interlatch-0.28.2 → interlatch-0.28.4}/docs/images/home.png +0 -0
  78. {interlatch-0.28.2 → interlatch-0.28.4}/docs/images/map.png +0 -0
  79. {interlatch-0.28.2 → interlatch-0.28.4}/docs/images/palette.png +0 -0
  80. {interlatch-0.28.2 → interlatch-0.28.4}/docs/images/session.png +0 -0
  81. {interlatch-0.28.2 → interlatch-0.28.4}/docs/install.md +0 -0
  82. {interlatch-0.28.2 → interlatch-0.28.4}/docs/ja/README.md +0 -0
  83. {interlatch-0.28.2 → interlatch-0.28.4}/docs/ja/analysis.md +0 -0
  84. {interlatch-0.28.2 → interlatch-0.28.4}/docs/ja/cli.md +0 -0
  85. {interlatch-0.28.2 → interlatch-0.28.4}/docs/ja/configuration.md +0 -0
  86. {interlatch-0.28.2 → interlatch-0.28.4}/docs/ja/dashboard.md +0 -0
  87. {interlatch-0.28.2 → interlatch-0.28.4}/docs/ja/development.md +0 -0
  88. {interlatch-0.28.2 → interlatch-0.28.4}/docs/ja/faq.md +0 -0
  89. {interlatch-0.28.2 → interlatch-0.28.4}/docs/ja/install.md +0 -0
  90. {interlatch-0.28.2 → interlatch-0.28.4}/docs/ja/mcp.md +0 -0
  91. {interlatch-0.28.2 → interlatch-0.28.4}/docs/ja/moving-from-chronicle.md +0 -0
  92. {interlatch-0.28.2 → interlatch-0.28.4}/docs/ja/party.md +0 -0
  93. {interlatch-0.28.2 → interlatch-0.28.4}/docs/ja/postgres.md +0 -0
  94. {interlatch-0.28.2 → interlatch-0.28.4}/docs/ja/privacy.md +0 -0
  95. {interlatch-0.28.2 → interlatch-0.28.4}/docs/ja/sources.md +0 -0
  96. {interlatch-0.28.2 → interlatch-0.28.4}/docs/ja/suggestions.md +0 -0
  97. {interlatch-0.28.2 → interlatch-0.28.4}/docs/ja/vscode.md +0 -0
  98. {interlatch-0.28.2 → interlatch-0.28.4}/docs/mcp.md +0 -0
  99. {interlatch-0.28.2 → interlatch-0.28.4}/docs/moving-from-chronicle.md +0 -0
  100. {interlatch-0.28.2 → interlatch-0.28.4}/docs/party.md +0 -0
  101. {interlatch-0.28.2 → interlatch-0.28.4}/docs/postgres.md +0 -0
  102. {interlatch-0.28.2 → interlatch-0.28.4}/docs/privacy.md +0 -0
  103. {interlatch-0.28.2 → interlatch-0.28.4}/docs/sources.md +0 -0
  104. {interlatch-0.28.2 → interlatch-0.28.4}/docs/suggestions.md +0 -0
  105. {interlatch-0.28.2 → interlatch-0.28.4}/docs/vscode.md +0 -0
  106. {interlatch-0.28.2 → interlatch-0.28.4}/leaderboard/.firebaserc +0 -0
  107. {interlatch-0.28.2 → interlatch-0.28.4}/leaderboard/.gitignore +0 -0
  108. {interlatch-0.28.2 → interlatch-0.28.4}/leaderboard/README.md +0 -0
  109. {interlatch-0.28.2 → interlatch-0.28.4}/leaderboard/firebase.json +0 -0
  110. {interlatch-0.28.2 → interlatch-0.28.4}/leaderboard/firestore.indexes.json +0 -0
  111. {interlatch-0.28.2 → interlatch-0.28.4}/leaderboard/firestore.rules +0 -0
  112. {interlatch-0.28.2 → interlatch-0.28.4}/mkdocs.yml +0 -0
  113. {interlatch-0.28.2 → interlatch-0.28.4}/packaging/icons3d/README.md +0 -0
  114. {interlatch-0.28.2 → interlatch-0.28.4}/packaging/icons3d/art.json +0 -0
  115. {interlatch-0.28.2 → interlatch-0.28.4}/packaging/icons3d/filing.html +0 -0
  116. {interlatch-0.28.2 → interlatch-0.28.4}/packaging/icons3d/loops.json +0 -0
  117. {interlatch-0.28.2 → interlatch-0.28.4}/packaging/icons3d/loops.py +0 -0
  118. {interlatch-0.28.2 → interlatch-0.28.4}/packaging/icons3d/members.js +0 -0
  119. {interlatch-0.28.2 → interlatch-0.28.4}/packaging/icons3d/render.html +0 -0
  120. {interlatch-0.28.2 → interlatch-0.28.4}/packaging/icons3d/render.py +0 -0
  121. {interlatch-0.28.2 → interlatch-0.28.4}/packaging/icons3d/scenes-more.js +0 -0
  122. {interlatch-0.28.2 → interlatch-0.28.4}/packaging/icons3d/scenes.html +0 -0
  123. {interlatch-0.28.2 → interlatch-0.28.4}/packaging/macos/Chronicle.icns +0 -0
  124. {interlatch-0.28.2 → interlatch-0.28.4}/packaging/macos/Interlatch.spec +0 -0
  125. {interlatch-0.28.2 → interlatch-0.28.4}/packaging/macos/build.sh +0 -0
  126. {interlatch-0.28.2 → interlatch-0.28.4}/packaging/macos/entitlements.plist +0 -0
  127. {interlatch-0.28.2 → interlatch-0.28.4}/packaging/macos/icon-3d.webp +0 -0
  128. {interlatch-0.28.2 → interlatch-0.28.4}/packaging/macos/icon-blueprint-prompt.md +0 -0
  129. {interlatch-0.28.2 → interlatch-0.28.4}/packaging/macos/icon-blueprint.webp +0 -0
  130. {interlatch-0.28.2 → interlatch-0.28.4}/packaging/macos/icon.png +0 -0
  131. {interlatch-0.28.2 → interlatch-0.28.4}/packaging/macos/interlatch_app.py +0 -0
  132. {interlatch-0.28.2 → interlatch-0.28.4}/packaging/macos/make_icon.py +0 -0
  133. {interlatch-0.28.2 → interlatch-0.28.4}/packaging/release.py +0 -0
  134. {interlatch-0.28.2 → interlatch-0.28.4}/pyproject.toml +0 -0
  135. {interlatch-0.28.2 → interlatch-0.28.4}/src/chronicle/__init__.py +0 -0
  136. {interlatch-0.28.2 → interlatch-0.28.4}/src/chronicle/__main__.py +0 -0
  137. {interlatch-0.28.2 → interlatch-0.28.4}/src/chronicle/windows.py +0 -0
  138. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/__init__.py +0 -0
  139. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/__main__.py +0 -0
  140. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/analysis/__init__.py +0 -0
  141. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/analysis/digest.py +0 -0
  142. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/analysis/language.py +0 -0
  143. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/analysis/queue.py +0 -0
  144. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/analysis/runners/__init__.py +0 -0
  145. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/analysis/runners/api.py +0 -0
  146. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/analysis/runners/base.py +0 -0
  147. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/analysis/runners/bob.py +0 -0
  148. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/analysis/runners/claude.py +0 -0
  149. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/analysis/runners/codex.py +0 -0
  150. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/analysis/runners/factory.py +0 -0
  151. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/analysis/runners/off.py +0 -0
  152. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/analysis/runners/opencode.py +0 -0
  153. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/analysis/runners/process.py +0 -0
  154. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/analysis/runners/replies.py +0 -0
  155. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/analysis/runners/settings.py +0 -0
  156. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/analysis/screen.py +0 -0
  157. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/analysis/session.py +0 -0
  158. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/analysis/worker.py +0 -0
  159. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/application/__init__.py +0 -0
  160. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/application/profiles.py +0 -0
  161. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/application/records.py +0 -0
  162. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/application/search.py +0 -0
  163. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/application/workflows.py +0 -0
  164. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/artifacts/__init__.py +0 -0
  165. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/artifacts/files.py +0 -0
  166. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/artifacts/queries.py +0 -0
  167. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/cli.py +0 -0
  168. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/core/__init__.py +0 -0
  169. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/core/agents.py +0 -0
  170. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/core/artifact_records.py +0 -0
  171. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/core/config.py +0 -0
  172. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/core/diagrams.py +0 -0
  173. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/core/i18n/__init__.py +0 -0
  174. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/core/machine.py +0 -0
  175. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/core/paths.py +0 -0
  176. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/core/pricing.py +0 -0
  177. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/core/process.py +0 -0
  178. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/core/profiles.py +0 -0
  179. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/core/redact.py +0 -0
  180. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/core/sessions.py +0 -0
  181. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/core/util.py +0 -0
  182. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/ingestion/__init__.py +0 -0
  183. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/ingestion/analyses.py +0 -0
  184. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/ingestion/archive.py +0 -0
  185. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/ingestion/chat_import.py +0 -0
  186. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/ingestion/deletion.py +0 -0
  187. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/ingestion/history.py +0 -0
  188. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/ingestion/memory.py +0 -0
  189. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/ingestion/persistence.py +0 -0
  190. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/ingestion/report.py +0 -0
  191. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/ingestion/service.py +0 -0
  192. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/interfaces/__init__.py +0 -0
  193. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/interfaces/cli/__init__.py +0 -0
  194. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/interfaces/cli/commands/__init__.py +0 -0
  195. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/interfaces/cli/commands/archive.py +0 -0
  196. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/interfaces/cli/commands/hubs.py +0 -0
  197. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/interfaces/cli/commands/knowledge.py +0 -0
  198. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/interfaces/cli/commands/operations.py +0 -0
  199. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/interfaces/cli/commands/recording.py +0 -0
  200. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/interfaces/cli/commands/setup.py +0 -0
  201. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/interfaces/cli/common.py +0 -0
  202. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/interfaces/cli/main.py +0 -0
  203. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/interfaces/cli/parser.py +0 -0
  204. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/interfaces/exports/__init__.py +0 -0
  205. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/interfaces/exports/diagrams.py +0 -0
  206. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/interfaces/exports/markdown.py +0 -0
  207. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/interfaces/exports/sessions.py +0 -0
  208. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/interfaces/exports/vault.py +0 -0
  209. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/interfaces/mcp.py +0 -0
  210. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/interfaces/web/__init__.py +0 -0
  211. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/interfaces/web/api/__init__.py +0 -0
  212. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/interfaces/web/api/artifacts.py +0 -0
  213. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/interfaces/web/api/common.py +0 -0
  214. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/interfaces/web/api/hub_admin.py +0 -0
  215. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/interfaces/web/api/knowledge.py +0 -0
  216. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/interfaces/web/api/leaderboard.py +0 -0
  217. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/interfaces/web/api/party.py +0 -0
  218. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/interfaces/web/api/projects.py +0 -0
  219. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/interfaces/web/api/sessions.py +0 -0
  220. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/interfaces/web/api/settings.py +0 -0
  221. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/interfaces/web/api/sources.py +0 -0
  222. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/interfaces/web/api/storage.py +0 -0
  223. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/interfaces/web/api/team.py +0 -0
  224. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/interfaces/web/app.py +0 -0
  225. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/interfaces/web/assets.py +0 -0
  226. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/interfaces/web/context.py +0 -0
  227. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/interfaces/web/handler.py +0 -0
  228. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/interfaces/web/jobs.py +0 -0
  229. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/interfaces/web/server.py +0 -0
  230. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/knowledge/__init__.py +0 -0
  231. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/knowledge/arena.py +0 -0
  232. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/knowledge/bag.py +0 -0
  233. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/knowledge/cases.py +0 -0
  234. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/knowledge/formation.py +0 -0
  235. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/knowledge/friction.py +0 -0
  236. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/knowledge/glossary.py +0 -0
  237. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/knowledge/group_graph.py +0 -0
  238. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/knowledge/instructions.py +0 -0
  239. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/knowledge/ladder.py +0 -0
  240. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/knowledge/learning.py +0 -0
  241. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/knowledge/market.json +0 -0
  242. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/knowledge/party.py +0 -0
  243. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/knowledge/reviews.py +0 -0
  244. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/knowledge/roadmap.json +0 -0
  245. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/knowledge/study.py +0 -0
  246. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/knowledge/suggest.py +0 -0
  247. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/knowledge/synthesize.py +0 -0
  248. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/knowledge/weekly.py +0 -0
  249. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/projects/__init__.py +0 -0
  250. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/projects/groups.py +0 -0
  251. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/projects/hubgroups.py +0 -0
  252. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/projects/labels.py +0 -0
  253. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/projects/owners.py +0 -0
  254. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/projects/progress.py +0 -0
  255. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/projects/routing.py +0 -0
  256. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/projects/scope.py +0 -0
  257. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/projects/systems.py +0 -0
  258. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/runtime/__init__.py +0 -0
  259. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/runtime/connectors.py +0 -0
  260. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/runtime/container.py +0 -0
  261. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/runtime/hooks.py +0 -0
  262. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/runtime/install.py +0 -0
  263. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/runtime/migrate.py +0 -0
  264. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/runtime/notify.py +0 -0
  265. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/runtime/platforms/__init__.py +0 -0
  266. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/runtime/platforms/desktop.py +0 -0
  267. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/runtime/platforms/menubar.py +0 -0
  268. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/runtime/platforms/windows.py +0 -0
  269. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/runtime/statusline.py +0 -0
  270. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/runtime/tailnet.py +0 -0
  271. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/runtime/update.py +0 -0
  272. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/sharing/__init__.py +0 -0
  273. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/sharing/access.py +0 -0
  274. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/sharing/arena.py +0 -0
  275. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/sharing/client.py +0 -0
  276. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/sharing/identity.py +0 -0
  277. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/sharing/leaderboard.py +0 -0
  278. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/sharing/market.py +0 -0
  279. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/sharing/membership.py +0 -0
  280. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/sharing/news.py +0 -0
  281. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/sharing/people.py +0 -0
  282. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/sharing/projects.py +0 -0
  283. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/sharing/protocol.py +0 -0
  284. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/sharing/pull.py +0 -0
  285. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/sharing/push.py +0 -0
  286. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/sharing/receiver.py +0 -0
  287. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/sources/__init__.py +0 -0
  288. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/sources/antigravity.py +0 -0
  289. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/sources/bob.py +0 -0
  290. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/sources/builder.py +0 -0
  291. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/sources/chatgpt.py +0 -0
  292. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/sources/chats.py +0 -0
  293. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/sources/claude/__init__.py +0 -0
  294. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/sources/claude/export.py +0 -0
  295. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/sources/claude/files.py +0 -0
  296. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/sources/claude/parser.py +0 -0
  297. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/sources/codex/__init__.py +0 -0
  298. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/sources/codex/cloud.py +0 -0
  299. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/sources/codex/parser.py +0 -0
  300. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/sources/copilot.py +0 -0
  301. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/sources/discovery.py +0 -0
  302. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/sources/opencode.py +0 -0
  303. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/sources/prompts.py +0 -0
  304. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/storage/__init__.py +0 -0
  305. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/storage/fts.py +0 -0
  306. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/storage/kv.py +0 -0
  307. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/storage/migrations.py +0 -0
  308. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/storage/mirror.py +0 -0
  309. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/storage/postgres.py +0 -0
  310. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/storage/schema.py +0 -0
  311. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/storage/sqlite.py +0 -0
  312. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/storage/team.py +0 -0
  313. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/AGENT-ICONS.txt +0 -0
  314. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/PLEX-OFL.txt +0 -0
  315. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/adventure.js +0 -0
  316. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/agent-antigravity.svg +0 -0
  317. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/agent-bob.svg +0 -0
  318. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/agent-chatgpt.svg +0 -0
  319. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/agent-claude-ai.svg +0 -0
  320. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/agent-claude.svg +0 -0
  321. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/agent-codex.svg +0 -0
  322. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/agent-copilot.svg +0 -0
  323. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/agent-opencode.svg +0 -0
  324. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/art-book.webp +0 -0
  325. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/art-camera.webp +0 -0
  326. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/art-chip.webp +0 -0
  327. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/art-clock.webp +0 -0
  328. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/art-cloud.webp +0 -0
  329. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/art-coins.webp +0 -0
  330. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/art-container.webp +0 -0
  331. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/art-dashboard.webp +0 -0
  332. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/art-dex.webp +0 -0
  333. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/art-document.webp +0 -0
  334. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/art-flag.webp +0 -0
  335. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/art-folder.webp +0 -0
  336. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/art-gateway.webp +0 -0
  337. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/art-gears.webp +0 -0
  338. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/art-glitch.webp +0 -0
  339. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/art-globe.webp +0 -0
  340. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/art-laptop.webp +0 -0
  341. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/art-log.webp +0 -0
  342. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/art-lumi.webp +0 -0
  343. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/art-magnifier.webp +0 -0
  344. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/art-pip.webp +0 -0
  345. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/art-plug.webp +0 -0
  346. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/art-rack.webp +0 -0
  347. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/art-tink.webp +0 -0
  348. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/battle.js +0 -0
  349. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/boot.js +0 -0
  350. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/favicon.png +0 -0
  351. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/field.js +0 -0
  352. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/i18n.js +0 -0
  353. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/icon-180.png +0 -0
  354. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/icon-192.png +0 -0
  355. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/icon-512.png +0 -0
  356. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/icon.png +0 -0
  357. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/index.html +0 -0
  358. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/innerlatch.css +0 -0
  359. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/innerlatch.js +0 -0
  360. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/learning.js +0 -0
  361. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/manifest.webmanifest +0 -0
  362. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/panel.css +0 -0
  363. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/panel.html +0 -0
  364. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/panel.js +0 -0
  365. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/plex-mono-400.woff2 +0 -0
  366. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/plex-mono-500.woff2 +0 -0
  367. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/plex-mono-600.woff2 +0 -0
  368. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/plex-sans-400.woff2 +0 -0
  369. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/plex-sans-500.woff2 +0 -0
  370. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/plex-sans-600.woff2 +0 -0
  371. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/plex-sans-700.woff2 +0 -0
  372. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/rough.js +0 -0
  373. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/scene.js +0 -0
  374. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/web/sprites.js +0 -0
  375. {interlatch-0.28.2 → interlatch-0.28.4}/src/interlatch/windows.py +0 -0
  376. {interlatch-0.28.2 → interlatch-0.28.4}/tests/antigravity_fixture.py +0 -0
  377. {interlatch-0.28.2 → interlatch-0.28.4}/tests/codex_fixture.py +0 -0
  378. {interlatch-0.28.2 → interlatch-0.28.4}/tests/conftest.py +0 -0
  379. {interlatch-0.28.2 → interlatch-0.28.4}/tests/copilot_fixture.py +0 -0
  380. {interlatch-0.28.2 → interlatch-0.28.4}/tests/friction_fixture.py +0 -0
  381. {interlatch-0.28.2 → interlatch-0.28.4}/tests/golden.py +0 -0
  382. {interlatch-0.28.2 → interlatch-0.28.4}/tests/goldens/cli.json +0 -0
  383. {interlatch-0.28.2 → interlatch-0.28.4}/tests/goldens/diagram.json +0 -0
  384. {interlatch-0.28.2 → interlatch-0.28.4}/tests/goldens/parsers/antigravity.json +0 -0
  385. {interlatch-0.28.2 → interlatch-0.28.4}/tests/goldens/parsers/bob.json +0 -0
  386. {interlatch-0.28.2 → interlatch-0.28.4}/tests/goldens/parsers/claude_code.json +0 -0
  387. {interlatch-0.28.2 → interlatch-0.28.4}/tests/goldens/parsers/codex.json +0 -0
  388. {interlatch-0.28.2 → interlatch-0.28.4}/tests/goldens/parsers/copilot.json +0 -0
  389. {interlatch-0.28.2 → interlatch-0.28.4}/tests/goldens/parsers/opencode.json +0 -0
  390. {interlatch-0.28.2 → interlatch-0.28.4}/tests/goldens/systems.json +0 -0
  391. {interlatch-0.28.2 → interlatch-0.28.4}/tests/hubs_fixture.py +0 -0
  392. {interlatch-0.28.2 → interlatch-0.28.4}/tests/opencode_fixture.py +0 -0
  393. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_adventure_web.py +0 -0
  394. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_analysis.py +0 -0
  395. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_antigravity.py +0 -0
  396. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_architecture.py +0 -0
  397. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_arena.py +0 -0
  398. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_artifacts.py +0 -0
  399. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_bag.py +0 -0
  400. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_battle_web.py +0 -0
  401. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_bob_analysis.py +0 -0
  402. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_boot_web.py +0 -0
  403. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_careers.py +0 -0
  404. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_case_files.py +0 -0
  405. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_cases.py +0 -0
  406. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_chat_import.py +0 -0
  407. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_cli_pins.py +0 -0
  408. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_codex.py +0 -0
  409. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_codex_analysis.py +0 -0
  410. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_codex_cloud.py +0 -0
  411. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_container.py +0 -0
  412. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_copilot_bob.py +0 -0
  413. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_dedicated_hub.py +0 -0
  414. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_dedupe.py +0 -0
  415. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_desktop.py +0 -0
  416. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_diagram.py +0 -0
  417. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_ee_boundary.py +0 -0
  418. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_field_web.py +0 -0
  419. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_fix_effect.py +0 -0
  420. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_formation.py +0 -0
  421. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_friction.py +0 -0
  422. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_game_mode.py +0 -0
  423. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_glossary.py +0 -0
  424. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_group_graph.py +0 -0
  425. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_group_sharing.py +0 -0
  426. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_guidance.py +0 -0
  427. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_home_agents.py +0 -0
  428. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_hub.py +0 -0
  429. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_hub_accept.py +0 -0
  430. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_hub_cookie.py +0 -0
  431. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_hub_groups.py +0 -0
  432. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_hub_inventory_scope.py +0 -0
  433. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_hub_leave_join.py +0 -0
  434. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_hub_news.py +0 -0
  435. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_hub_payload_privacy.py +0 -0
  436. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_hub_projects.py +0 -0
  437. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_hub_projects_listed.py +0 -0
  438. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_hub_purge.py +0 -0
  439. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_hub_send_problems.py +0 -0
  440. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_hub_sharing_race.py +0 -0
  441. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_i18n_web.py +0 -0
  442. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_ingest.py +0 -0
  443. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_instructions.py +0 -0
  444. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_integration.py +0 -0
  445. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_ladder.py +0 -0
  446. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_language.py +0 -0
  447. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_leaderboard.py +0 -0
  448. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_learning_glance.py +0 -0
  449. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_learning_interests.py +0 -0
  450. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_learning_web.py +0 -0
  451. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_leave_project.py +0 -0
  452. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_legacy_package.py +0 -0
  453. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_map_goldens.py +0 -0
  454. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_market.py +0 -0
  455. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_menubar.py +0 -0
  456. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_migrate.py +0 -0
  457. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_mirror.py +0 -0
  458. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_notify.py +0 -0
  459. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_opencode.py +0 -0
  460. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_opencode_analysis.py +0 -0
  461. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_parser.py +0 -0
  462. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_parser_goldens.py +0 -0
  463. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_party.py +0 -0
  464. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_party_map.py +0 -0
  465. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_people.py +0 -0
  466. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_people_hub.py +0 -0
  467. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_people_web.py +0 -0
  468. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_profile_scope.py +0 -0
  469. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_profiles.py +0 -0
  470. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_project_groups.py +0 -0
  471. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_project_progress.py +0 -0
  472. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_providers.py +0 -0
  473. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_queue.py +0 -0
  474. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_regressions.py +0 -0
  475. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_release.py +0 -0
  476. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_roadmap_catalog.py +0 -0
  477. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_screen.py +0 -0
  478. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_security.py +0 -0
  479. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_setup.py +0 -0
  480. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_statusline.py +0 -0
  481. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_study.py +0 -0
  482. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_suggest.py +0 -0
  483. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_suggest_cli.py +0 -0
  484. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_systems.py +0 -0
  485. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_team.py +0 -0
  486. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_team_home.py +0 -0
  487. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_update.py +0 -0
  488. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_weekly_reanalysis.py +0 -0
  489. {interlatch-0.28.2 → interlatch-0.28.4}/tests/test_windows.py +0 -0
  490. {interlatch-0.28.2 → interlatch-0.28.4}/tests/windows_smoke.py +0 -0
  491. {interlatch-0.28.2 → interlatch-0.28.4}/uv.lock +0 -0
  492. {interlatch-0.28.2 → interlatch-0.28.4}/vscode-extension/.vscodeignore +0 -0
  493. {interlatch-0.28.2 → interlatch-0.28.4}/vscode-extension/LICENSE +0 -0
  494. {interlatch-0.28.2 → interlatch-0.28.4}/vscode-extension/README.md +0 -0
  495. {interlatch-0.28.2 → interlatch-0.28.4}/vscode-extension/extension.js +0 -0
  496. {interlatch-0.28.2 → interlatch-0.28.4}/vscode-extension/media/icon.png +0 -0
  497. {interlatch-0.28.2 → interlatch-0.28.4}/vscode-extension/media/interlatch.svg +0 -0
  498. {interlatch-0.28.2 → interlatch-0.28.4}/vscode-extension/package.json +0 -0
@@ -3,6 +3,31 @@
3
3
  <!-- Don't add lines here: a pull request puts its line in a new file in changelog.d/ (see changelog.d/README.md),
4
4
  and each release moves the files it took into a dated section below. -->
5
5
 
6
+ ## 0.28.3 (2026-10-20)
7
+
8
+ - **Settings › Hubs.** Each hub this computer sends to has its own card: whether sending works, the last share,
9
+ **Share now**, its dashboard (signed in as that hub's link), joining and leaving it. The Projects sidebar names the
10
+ hub each group goes to and lists folders waiting for a hub first, and the status bar names every hub
11
+ ([Several hubs on one computer](docs/profiles.md)).
12
+
13
+ ## 0.28.2 (2026-10-20)
14
+
15
+ - **Several hubs: connect your groups.** `interlatch profile` became `interlatch hubs` and `interlatch group`: add a
16
+ hub (`interlatch hubs add Acme <address> --code <code>`), then connect a group of projects to it
17
+ (`interlatch group connect "Acme work" Acme`). What the group holds goes to that hub, and changing the group on the
18
+ dashboard's Projects page moves its sessions with it. A project taken out of a connected group waits for you
19
+ instead of going back to Personal. `--profile` is now `--hub`; the old name still works
20
+ ([Several hubs on one computer](docs/profiles.md)).
21
+ - **Several hubs on the dashboard.** Settings › Devices › Hubs lists this computer's hubs and their groups, adds and
22
+ joins one, and settles folders that wait for you; a group's page has **Goes to**, which connects the group to a hub
23
+ ([Several hubs on one computer](docs/profiles.md)).
24
+ - **Moving a folder takes its sessions back from the hub that had them.** When a group, a project or a waiting folder
25
+ moves to another hub (or a group is disconnected), the hub it left takes back the sessions this computer sent it,
26
+ before anything goes to the new one. One that can't be reached takes them back at the next send. Home now shows
27
+ folders that wait for you to choose a hub under **Needs you**
28
+ ([Moving takes sessions back](docs/profiles.md#moving-takes-sessions-back)). Update your hubs too: an older hub
29
+ can't take sessions back.
30
+
6
31
  ## 0.28.1 (2026-10-19)
7
32
 
8
33
  - **Profiles, first stage:** `interlatch profile create Acme --owns ~/work/acme` makes a named profile that owns a
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: interlatch
3
- Version: 0.28.2
3
+ Version: 0.28.4
4
4
  Summary: Shared memory for your coding agents: records every session (Claude Code, Codex, Copilot, Bob, Antigravity, OpenCode) locally and keeps what they learned, each lesson linked to the session it came from.
5
5
  Project-URL: Homepage, https://interlatch.com
6
6
  Project-URL: Documentation, https://interlatch.com/docs/
@@ -0,0 +1,4 @@
1
+ - **Every hub has the same page.** Settings › Hubs lists each hub, Personal's too, and each one opens a page of its own
2
+ with how sending goes, **Projects on the hub** (join a project with a folder here, add or remove a folder, leave or
3
+ rejoin) and what this computer shares with it. Settings › Devices is about this computer and your phone again
4
+ ([Several hubs on one computer](docs/profiles.md)).
@@ -69,7 +69,7 @@ then on:
69
69
  - **Analysis runs only on the hub.** If the computer already analyzed sessions with its own Interlatch, it hands
70
70
  those analyses over when it joins, so the hub does not pay to analyze them again.
71
71
  - **That computer's own dashboard and MCP tools stop updating.** They keep what they had; open the hub's dashboard
72
- instead (**Settings › Devices** has a link).
72
+ instead (**Settings › Hubs** has a link).
73
73
 
74
74
  ### Same project, different folders
75
75
 
@@ -93,7 +93,7 @@ matching project of its own:
93
93
  already on the hub: they move with their knowledge, and both projects' knowledge bases are rebuilt. The more
94
94
  specific match wins, so a repository inside the folder whose git remote the hub knows still follows its remote. A
95
95
  folder inside a repository the hub files under another project is refused, because a repository belongs to one
96
- project. `interlatch hub folders` shows what goes where. **Settings › Devices › Projects on the hub** does the same
96
+ project. `interlatch hub folders` shows what goes where. **Settings › Hubs › (the hub) › Projects on the hub** does the same
97
97
  from the dashboard: **Join a project** picks one of the hub's projects and a folder here. To take back a folder
98
98
  added by mistake, see [Leaving](#leaving). The hub's **Team › Computers** lists the folders each computer added.
99
99
  On this computer's **Projects** page and sidebar, the hub's projects that folders here go to come first, under
@@ -126,7 +126,7 @@ projects there from its next push, as if you had added each of their folders to
126
126
  above. A project recorded on another computer (such as `server:/root/app`) can't go from here.
127
127
 
128
128
  The project must already be on the hub (a member's computer can't make one), and the choice is made on this computer
129
- itself, not from another device. **Settings › Devices › Projects on the hub** shows the group under that project, and
129
+ itself, not from another device. **Settings › Hubs › (the hub) › Projects on the hub** shows the group under that project, and
130
130
  the Projects page and sidebar list the group's projects under that project as well as in the group.
131
131
 
132
132
  ### What is sent
@@ -153,7 +153,7 @@ A computer can keep its transcripts and still add to the hub's knowledge. Join w
153
153
  interlatch hub join https://pc.tail1234.ts.net --token … --share knowledge
154
154
  ```
155
155
 
156
- (or, on a computer that already joined, `interlatch config set hub.share knowledge`, or **Settings › Devices › What this
156
+ (or, on a computer that already joined, `interlatch config set hub.share knowledge`, or **Settings › Hubs › (the hub) › What this
157
157
  computer sends** in its dashboard). That computer then works like a
158
158
  standalone Interlatch: it records its own sessions, analyzes them itself (with its own Claude Code or Codex login,
159
159
  Bob API key or model provider), and keeps its dashboard and MCP tools up to date. After each analysis it sends the hub only:
@@ -169,7 +169,7 @@ It does that only for sessions the hub files under one of its projects: those in
169
169
  computer, and its push counts them as "kept here". A hub that has never run a session in a repository itself doesn't
170
170
  know its remote, as with a hub in Docker or on a server, so add your folder for each project there. To share
171
171
  sessions from every folder, join with `--all-folders`, run `interlatch config set hub.all_folders true`, or choose
172
- **Every folder** under **Settings › Devices › What this computer sends**.
172
+ **Every folder** under **Settings › Hubs › (the hub) › What this computer sends**.
173
173
 
174
174
  Prompts, shell commands, file paths, transcripts and lessons about you (global ones and preferences) never leave
175
175
  the computer. Sessions from GitHub Copilot, IBM Bob, Google Antigravity and OpenCode are shared too; imported chats are not.
@@ -238,13 +238,13 @@ unless the connection works, the password is never shown again, and only an admi
238
238
  - **Teammates' lessons come back.** Each time it shares, a computer that shares knowledge gets its teammates' lessons
239
239
  for the repositories it has clones of, the projects it shared sessions in, and those it added folders to. It shares
240
240
  after each analysis (when a session ends, and every 15 minutes with the background agent from `interlatch install`),
241
- and at once with `interlatch push` or **Share now** in **Settings › Devices**. They are
241
+ and at once with `interlatch push` or **Share now** in **Settings › Hubs**. They are
242
242
  kept read-only in its own database, under its own folder for that repository, where its MCP tools answer with them
243
243
  (marked as teammates') and the start-of-session notes list them under **From teammates' sessions**. Its dashboard
244
244
  shows them on project pages and in **All knowledge**, marked **from** the computers whose sessions stated them;
245
- the source **From teammates** there lists only them, and **Read them** in **Settings › Devices** opens that list.
245
+ the source **From teammates** there lists only them, and **Read them** in **Settings › Hubs** opens that list.
246
246
  Lessons it stated itself are not sent back. One it dismisses stays dismissed; one the team no longer has disappears.
247
- `interlatch hub status` and its **Settings › Devices** show how many it holds; **Share now** there
247
+ `interlatch hub status` and its **Settings › Hubs** show how many it holds; **Share now** there
248
248
  pushes at once.
249
249
  - **Only the hub connects to the database.** Computers never get its address or password, so it can sit on a
250
250
  network only the hub reaches. Its tables live in the schema `team`, with an audit log of every push and pull
@@ -269,7 +269,7 @@ Devices › Projects on the hub**, each project has these buttons:
269
269
  command line: `interlatch hub remove-folder <folder>`. A computer that sends transcripts keeps them on the hub: the
270
270
  hub files them by their git remote or their own folder again.
271
271
 
272
- `interlatch hub leave`, or **Leave the hub…** in **Settings › Devices**, makes the computer record and analyze its own
272
+ `interlatch hub leave`, or **Leave the hub…** in **Settings › Hubs**, makes the computer record and analyze its own
273
273
  sessions again; the hub keeps what it was sent. It also removes the teammates' lessons that came from the hub, and
274
274
  puts aside what the computer had for that hub (its folders, the projects it left, and its groups shared there) in
275
275
  `~/.interlatch/hub-left/`, so a different hub joined later never gets any of it. Whether it shares knowledge only
@@ -361,7 +361,7 @@ or the id the refused join printed. That code joins that computer only.
361
361
 
362
362
  ### Opening the hub's dashboard
363
363
 
364
- - **From your own Interlatch.** On a computer that joined, **Settings › Devices › Open the hub's dashboard** asks the
364
+ - **From your own Interlatch.** On a computer that joined, **Settings › Hubs › (the hub) › Open the hub's dashboard** asks the
365
365
  hub for a sign-in link, valid for 5 minutes, and opens it in your browser. There is no password.
366
366
  - **With the invite link.** Opening `…/signin?code=…` shows who the code signs in as; **Sign in** signs that browser
367
367
  in. You can also paste the code on the hub's sign-in screen. Use this for a phone, or for someone with no computer
@@ -199,7 +199,7 @@ docker compose exec hub interlatch hub invite "Yuma" --email yuma@example.com --
199
199
 
200
200
  Send them the `interlatch hub join …` line, not the link, with [Joining your team's hub](join-a-hub.md), which
201
201
  walks them through the rest. The code works once, so don't send both. Once their computer has joined, they open the
202
- dashboard from their own Interlatch: **Settings › Devices › Open the hub's dashboard**, or `interlatch hub signin`. For
202
+ dashboard from their own Interlatch: **Settings › Hubs › (the hub) › Open the hub's dashboard**, or `interlatch hub signin`. For
203
203
  a phone, or someone who only views the dashboard, make a new code with
204
204
  `docker compose exec hub interlatch hub invite yuma@example.com` (or **New invite** on their row in **Team › People**)
205
205
  and send its **link**.
@@ -68,7 +68,7 @@ interlatch hub join https://pc.tail1234.ts.net --token …
68
68
  - **分析はハブだけで行います。** そのコンピューターが自分の Interlatch ですでに分析していたセッションは、参加するときにその結果を
69
69
  ハブに引き渡すので、ハブがもう一度費用をかけて分析することはありません。
70
70
  - **そのコンピューター自身のダッシュボードと MCP ツールは更新されなくなります。** それまでの内容は残ります。代わりにハブの
71
- ダッシュボードを開いてください(**Settings › Devices** にリンクがあります)。
71
+ ダッシュボードを開いてください(**Settings › Hubs** にリンクがあります)。
72
72
 
73
73
  ### 同じプロジェクト、別のフォルダー
74
74
 
@@ -90,7 +90,7 @@ interlatch hub join https://pc.tail1234.ts.net --token …
90
90
  両方のプロジェクトのナレッジベースが作り直されます。より具体的な指定が優先されるので、フォルダーの中にあってもハブが
91
91
  git リモートを知っているリポジトリはそのリモートに従います。ハブが別のプロジェクトに入れているリポジトリの中のフォルダーは追加できません
92
92
  (1 つのリポジトリは 1 つのプロジェクトに属します)。`interlatch hub folders` でどこに入るかを確認できます。
93
- ダッシュボードの **Settings › Devices › Projects on the hub** でも同じことができ、**Join a project** でハブの
93
+ ダッシュボードの **Settings › Hubs › (the hub) › Projects on the hub** でも同じことができ、**Join a project** でハブの
94
94
  プロジェクトとこのコンピューターのフォルダーを選びます。間違えて追加したフォルダーを外すには[やめるとき](#やめるとき)を
95
95
  見てください。ハブの **Team › Computers** に、各コンピューターが追加したフォルダーが表示されます。
96
96
  このコンピューターの **Projects** ページとサイドバーでは、ここのフォルダーの送り先になっているハブのプロジェクトが
@@ -123,7 +123,7 @@ interlatch hub join https://pc.tail1234.ts.net --token …
123
123
  別のコンピューターで記録されたプロジェクト(`server:/root/app` など)は、ここからは送れません。
124
124
 
125
125
  プロジェクトはハブにすでにある必要があり(メンバーのコンピューターでは作れません)、選ぶのはこのコンピューター自身で、ほかの
126
- デバイスからはできません。**Settings › Devices › Projects on the hub** ではそのプロジェクトの下にグループが表示され、グループの
126
+ デバイスからはできません。**Settings › Hubs › (the hub) › Projects on the hub** ではそのプロジェクトの下にグループが表示され、グループの
127
127
  プロジェクトは Projects ページとサイドバーで、グループの中に加えてそのプロジェクトの下にも並びます。
128
128
 
129
129
  ### 送られるもの
@@ -147,7 +147,7 @@ interlatch hub join https://pc.tail1234.ts.net --token …
147
147
  interlatch hub join https://pc.tail1234.ts.net --token … --share knowledge
148
148
  ```
149
149
 
150
- (参加済みのコンピューターでは `interlatch config set hub.share knowledge`、またはそのダッシュボードの **Settings › Devices ›
150
+ (参加済みのコンピューターでは `interlatch config set hub.share knowledge`、またはそのダッシュボードの **Settings › Hubs › (the hub) ›
151
151
  What this computer sends**)。そのコンピューターは単独の Interlatch と同じように
152
152
  動きます。自分のセッションを記録し、自分で分析し(自分の Claude Code か Codex のログイン、Bob API キー、または
153
153
  モデルプロバイダーで)、ダッシュボードと MCP ツールも更新され続けます。分析のたびにハブへ送るのは次のものだけです。
@@ -162,7 +162,7 @@ What this computer sends**)。そのコンピューターは単独の Interlat
162
162
  それ以外のフォルダーのセッションはコンピューターに残り、送信の結果には「kept here」として数えられます。Docker やサーバー上の
163
163
  ハブのように、ハブ自身がそのリポジトリでセッションを実行したことがなければ、ハブはその git リモートを知りません。その場合は
164
164
  プロジェクトごとに自分のフォルダーを追加してください。すべてのフォルダーのセッションを共有するには、`--all-folders` を付けて
165
- 参加するか、`interlatch config set hub.all_folders true` を実行するか、**Settings › Devices › What this computer sends** で
165
+ 参加するか、`interlatch config set hub.all_folders true` を実行するか、**Settings › Hubs › (the hub) › What this computer sends** で
166
166
  **すべてのフォルダー** を選びます。
167
167
 
168
168
  プロンプト、シェルのコマンド、ファイルのパス、トランスクリプト、あなた自身についてのナレッジ(全体のものと好み)は
@@ -219,12 +219,12 @@ interlatch hub store # 接続し、テーブルを用
219
219
  - **チームメイトのナレッジが戻ってきます。** ナレッジを共有しているコンピューターは、共有するたびに、クローンを持っている
220
220
  リポジトリ、セッションを共有したプロジェクト、フォルダーを追加したプロジェクトについて、チームメイトのナレッジを受け取り
221
221
  ます。共有するのは分析のたび(セッションが終わったとき、`interlatch install` のバックグラウンドエージェントがあれば
222
- 15 分ごと)と、`interlatch push` または **Settings › Devices** の **Share now** を使ったときです。自分のデータベースに読み取り専用で、そのリポジトリの自分のフォルダーの下に保存されます。MCP ツールはそれも
222
+ 15 分ごと)と、`interlatch push` または **Settings › Hubs** の **Share now** を使ったときです。自分のデータベースに読み取り専用で、そのリポジトリの自分のフォルダーの下に保存されます。MCP ツールはそれも
223
223
  (チームメイトのものと示して)答えに使い、セッション開始時のメモは **From teammates' sessions** の下に並べます。
224
224
  ダッシュボードでも、プロジェクトのページと **All knowledge** に、それを述べたセッションのコンピューター名を **from** で
225
- 示して表示します。そこで取得元に **From teammates** を選ぶとそれだけが並び、**Settings › Devices** の **Read them** で
225
+ 示して表示します。そこで取得元に **From teammates** を選ぶとそれだけが並び、**Settings › Hubs** の **Read them** で
226
226
  その一覧が開きます。自分が述べたナレッジは返ってきません。却下したものは却下されたまま残り、チームから消えたものは消えます。持っている数は
227
- `interlatch hub status` とそのコンピューターの **Settings › Devices** で分かり、そこの **Share now** ですぐに
227
+ `interlatch hub status` とそのコンピューターの **Settings › Hubs** で分かり、そこの **Share now** ですぐに
228
228
  プッシュできます。
229
229
  - **データベースにつなぐのはハブだけです。** 各コンピューターはそのアドレスもパスワードも知らないので、ハブだけが届く
230
230
  ネットワークに置けます。テーブルはスキーマ `team` にあり、プッシュとプルごとの監査ログ(件数のみ)も残ります。更新の
@@ -236,7 +236,7 @@ interlatch hub store # 接続し、テーブルを用
236
236
 
237
237
  ### やめるとき
238
238
 
239
- ナレッジを共有するコンピューターは、ハブのプロジェクトのうち一つから抜けて、ほかには残れます。**Settings › Devices ›
239
+ ナレッジを共有するコンピューターは、ハブのプロジェクトのうち一つから抜けて、ほかには残れます。**Settings › Hubs › (the hub) ›
240
240
  Projects on the hub** で、プロジェクトごとに次のボタンがあります。
241
241
 
242
242
  - **Leave**:このコンピューターはそこへのセッションの共有と、そのプロジェクトのチームメイトのナレッジの受け取りを
@@ -249,7 +249,7 @@ Projects on the hub** で、プロジェクトごとに次のボタンがあり
249
249
  `interlatch hub remove-folder <folder>` です。トランスクリプトを送るコンピューターでは、ハブにトランスクリプトが残り、
250
250
  ハブは git リモートか元のフォルダーで振り分け直します。
251
251
 
252
- `interlatch hub leave`、または **Settings › Devices** の **Leave the hub…** で、そのコンピューターは再び自分でセッションを
252
+ `interlatch hub leave`、または **Settings › Hubs** の **Leave the hub…** で、そのコンピューターは再び自分でセッションを
253
253
  記録・分析します。ハブは受け取ったものを残します。そのハブから来たチームメイトのナレッジも削除し、そのハブのための設定
254
254
  (フォルダー、抜けたプロジェクト、そこに共有したグループ)は `~/.interlatch/hub-left/` に保存するので、
255
255
  あとで参加するほかのハブには渡りません。ナレッジだけを共有するかどうか(`[hub] share`)はそのままです。再び参加するには新しい招待が必要です。同じハブにもう一度参加すると、それらを
@@ -339,7 +339,7 @@ interlatch hub join https://interlatch.example.internal --code K7PQ-M2XD-9HNA --
339
339
 
340
340
  ### ハブのダッシュボードを開く
341
341
 
342
- - **自分の Interlatch から。** 参加したコンピューターで **Settings › Devices › Open the hub's dashboard** を押すと、ハブから
342
+ - **自分の Interlatch から。** 参加したコンピューターで **Settings › Hubs › (the hub) › Open the hub's dashboard** を押すと、ハブから
343
343
  5 分間有効なサインインリンクを受け取り、ブラウザーで開きます。パスワードはありません。
344
344
  - **招待リンクで。** `…/signin?code=…` を開くと、そのコードでだれとしてサインインするかが表示され、**サインイン** を押すと
345
345
  そのブラウザーがサインインします。ハブのサインイン画面にコードを貼り付けても同じです。スマートフォンや、ハブに参加した
@@ -198,7 +198,7 @@ docker compose exec hub interlatch hub invite "Yuma" --email yuma@example.com --
198
198
 
199
199
  リンクではなく `interlatch hub join …` の行を、[チームのハブに参加する](join-a-hub.md)と一緒に送ってください。
200
200
  残りの手順はそのページが案内します。コードは一度しか使えないので、両方は送らないでください。コンピューターが参加したあとは、
201
- 本人の Interlatch からダッシュボードを開けます:**Settings › Devices › Open the hub's dashboard**、または
201
+ 本人の Interlatch からダッシュボードを開けます:**Settings › Hubs › (the hub) › Open the hub's dashboard**、または
202
202
  `interlatch hub signin`。スマートフォン用や、ダッシュボードを見るだけの人には、
203
203
  `docker compose exec hub interlatch hub invite yuma@example.com`(または **Team › People** のその人の行の **New invite**)で
204
204
  新しいコードを作り、その**リンク**を送ります。
@@ -69,7 +69,7 @@ interlatch hub add-folder ~/code/website --project Website
69
69
  これで、そこで分析済みのものが送られます。ハブがすでに知っている git リポジトリのクローンは、この手順がなくても
70
70
  そのプロジェクトに入りますが、実行しても問題ありません。
71
71
 
72
- ダッシュボードでも同じことができます:**Settings › Devices › Projects on the hub › Join a project** で、
72
+ ダッシュボードでも同じことができます:**Settings › Hubs › (the hub) › Projects on the hub › Join a project** で、
73
73
  プロジェクトとあなたのフォルダーを選びます。
74
74
 
75
75
  ハブにそのプロジェクトがないと言われたり、**Join a project** が「一致するものはありません。」となったりしたら、
@@ -83,21 +83,21 @@ interlatch hub status
83
83
  ```
84
84
 
85
85
  ハブのアドレス、最後に送った時刻、受け取ったチームメイトのナレッジの数が表示されます。ダッシュボードの
86
- **Settings › Devices** にも同じものが表示されます。そこの **Open the hub's dashboard** で、あなたのプロジェクトが
86
+ **Settings › Hubs** にも同じものが表示されます。そこの **Open the hub's dashboard** で、あなたのプロジェクトが
87
87
  見えるハブのダッシュボードにサインインできます。
88
88
 
89
89
  ## その後
90
90
 
91
91
  - **分析のたびに、** あなたのコンピューターはプロジェクトの新しい要約とナレッジを送ります。今すぐ送るには
92
- `interlatch push`、または **Settings › Devices** の **Share now** を使います。
92
+ `interlatch push`、または **Settings › Hubs** の **Share now** を使います。
93
93
  - **送るたびに、** チームメイトがそのプロジェクトで学んだことが返ってきます。エージェントは Interlatch の MCP
94
94
  ツールを通じて、チームメイトのものと分かる形でそのナレッジを使えます。セッション開始時のメモを有効にしている場合
95
95
  (`interlatch install --inject-context`)、新しいセッションの "From teammates' sessions" にも並びます。
96
96
  - **あなたのダッシュボード**には、これまでどおりあなた自身のセッションが表示されます。チームメイトのナレッジは、
97
- プロジェクトのページと **All knowledge** に、どのコンピューターから来たかを示して並びます。**Settings › Devices** の
97
+ プロジェクトのページと **All knowledge** に、どのコンピューターから来たかを示して並びます。**Settings › Hubs** の
98
98
  **Read them** でそれだけの一覧が開きます。
99
99
 
100
- 一つのプロジェクトへの共有をやめるには、**Settings › Devices** でその横の **Leave** を選びます(または
100
+ 一つのプロジェクトへの共有をやめるには、**Settings › Hubs** でその横の **Leave** を選びます(または
101
101
  `interlatch hub leave --project <name>`)。そこで共有したものはハブに残ります。間違ったフォルダーを追加したときは、
102
102
  その横の **Remove** でフォルダーを外すと、あなたのコンピューターがそこから共有したものをハブが削除します。送るのを
103
103
  すべてやめるには **Leave the hub…** を選びます(または `interlatch hub leave`)。すでに送ったものはハブに残ります。
@@ -29,7 +29,7 @@
29
29
 
30
30
  グループのフォルダーですでに記録されたセッションは Acme に移り、それ以降は Acme のハブに送られます。Personal のハブがすでに持っているものは、先にそのハブが取り下げます([移すとハブから取り下げる](#moving-takes-sessions-back))。`--dry-run` を付けると、何も変えずに、移るものと、そのうちハブが持っている数を表示します。
31
31
 
32
- **ダッシュボードでは:** **設定 › デバイス › ハブ**でハブを追加し(名前、アドレス、招待コード)、グループのページの**送り先**で、そのグループのセッションを送るハブを選びます。
32
+ **ダッシュボードでは:** **設定 › ハブ**に Personal のものを含むすべてのハブが並び、ハブの追加(名前、アドレス、招待コード)もここで行います。ハブごとに同じ構成のページがあります。送信がうまくいっているか、**今すぐ共有**、ハブのダッシュボード、**ハブのプロジェクト**(ここのフォルダーでハブのプロジェクトに参加する、抜ける)、そのハブと何を共有するか、です。グループのページの**送り先**で、そのグループのセッションを送るハブを選びます。**プロジェクト**のサイドバーでは、ハブにつないだグループにそのハブの名前が表示され、選択を待つフォルダーが先頭に並びます。
33
33
 
34
34
  ほかのコンピューターと同じく、ハブに送られるのは、そのハブのプロジェクトに追加したフォルダーのものだけです(`interlatch --hub Acme hub add-folder ~/work/acme/app --project App`)。`--all-folders` を付けて参加した場合は別です。
35
35
 
@@ -67,7 +67,7 @@ interlatch group choose ~/work/acme/notes Acme # または personal
67
67
  interlatch group where ~/work/acme/app # フォルダーがどのハブに行くか、その理由
68
68
  ```
69
69
 
70
- **設定 › デバイス › ハブ**にも一覧があり、それぞれに**選ぶ**ボタンがあります。**ホーム**の**あなたの対応待ち**にも表示されます。あるフォルダーが選択待ちにしているプロジェクト(つないだグループの中にある、どのハブにもつないでいないグループ)は、そのフォルダーの下にまとめて表示します。
70
+ **設定 › ハブ**にも一覧があり、それぞれに**選ぶ**ボタンがあります。**ホーム**の**あなたの対応待ち**にも表示されます。あるフォルダーが選択待ちにしているプロジェクト(つないだグループの中にある、どのハブにもつないでいないグループ)は、そのフォルダーの下にまとめて表示します。
71
71
 
72
72
  ## エージェントに見えるもの
73
73
 
@@ -87,7 +87,7 @@ Codex にはセッション終了フックがないため、Codex のセッシ
87
87
  ID でそのコンピューター専用の招待を作り(`interlatch hub invite <名前> --computer <ID>`)、そのコードで参加してください
88
88
  ([コンピューターを参加させる](devices.md#コンピューターを参加させる))。
89
89
 
90
- **ステータスバーに「ハブに送信できていません」と表示される。** クリックすると **設定 › デバイス** に理由が出ます。
90
+ **ステータスバーに「ハブに送信できていません」と表示される。** クリックすると **設定 › ハブ** に理由が出ます。
91
91
  「ハブがこのコンピューターを受け付けていません」と出る場合は、ハブがこのコンピューターのトークンを受け付けなくなったため、ここで学んだことが
92
92
  ハブに届いていません。多くの場合、管理者が **チーム › 利用者** でそのコンピューターを取り消したか置き換えています。管理者にこのコンピューター
93
93
  用の新しい招待を頼み、そのコードをハブのカードの **参加し直す** に貼り付けてください(`interlatch hub join <ハブのアドレス> --code <コード>`
@@ -67,7 +67,7 @@ Use your folder for the project and the project's name from step 2. This sends w
67
67
  analyzed there. A clone of a git repository the hub already knows is filed under its project without this step,
68
68
  but running it does no harm.
69
69
 
70
- Your dashboard can do the same: **Settings › Devices › Projects on the hub › Join a project**, then pick the project
70
+ Your dashboard can do the same: **Settings › Hubs › (the hub) › Projects on the hub › Join a project**, then pick the project
71
71
  and your folder.
72
72
 
73
73
  If the hub says it has no such project, or **Join a project** answers "Nothing matches.", you don't see that
@@ -81,21 +81,21 @@ interlatch hub status
81
81
  ```
82
82
 
83
83
  It shows the hub's address, when your computer last sent, and how many of your teammates' lessons it holds. Your
84
- dashboard's **Settings › Devices** shows the same. **Open the hub's dashboard** there signs you in to the hub's
84
+ dashboard's **Settings › Hubs** shows the same. **Open the hub's dashboard** there signs you in to the hub's
85
85
  dashboard, where you see your projects.
86
86
 
87
87
  ## From then on
88
88
 
89
89
  - **After each analysis,** your computer sends the project's new summaries and lessons. `interlatch push`, or
90
- **Share now** in **Settings › Devices**, sends right away.
90
+ **Share now** in **Settings › Hubs**, sends right away.
91
91
  - **Each time it sends,** it gets back what your teammates learned in the project. Your agents find those lessons
92
92
  through Interlatch's MCP tools, marked as teammates'. With start-of-session notes on (`interlatch install
93
93
  --inject-context`), new sessions also list them under "From teammates' sessions".
94
94
  - **Your own dashboard** keeps showing your own sessions, as before. Your teammates' lessons appear on its project
95
- pages and in **All knowledge**, marked with the computers they came from; **Read them** in **Settings › Devices**
95
+ pages and in **All knowledge**, marked with the computers they came from; **Read them** in **Settings › Hubs**
96
96
  lists only those.
97
97
 
98
- To stop sharing to one project, choose **Leave** next to it in **Settings › Devices** (or run `interlatch hub leave
98
+ To stop sharing to one project, choose **Leave** next to it in **Settings › Hubs** (or run `interlatch hub leave
99
99
  --project <name>`). What you shared there stays on the hub. Added the wrong folder? **Remove** next to it takes the
100
100
  folder back, and the hub deletes what your computer shared from it. To stop sending altogether, choose **Leave the
101
101
  hub…** (or run `interlatch hub leave`). What you already sent stays on the hub. Your teammates' lessons from it are
@@ -37,8 +37,11 @@ The sessions already recorded in the group's folders move to Acme, and go to Acm
37
37
  Personal's hub already has, it takes back first (see [Moving takes sessions back](#moving-takes-sessions-back)).
38
38
  `--dry-run` shows what would move, and how many of them a hub has, without changing anything.
39
39
 
40
- **On the dashboard:** **Settings › Devices › Hubs** adds a hub (its name, address and invite code), and a group's page
41
- has **Goes to**, where you pick the hub its sessions go to.
40
+ **On the dashboard:** **Settings › Hubs** lists every hub, Personal's too, and adds one (its name, address and
41
+ invite code). Each hub has a page of its own, the same for every hub: whether sending to it works, **Share now**, its
42
+ dashboard, **Projects on the hub** (join one of its projects with a folder here, or leave one) and what this computer
43
+ shares with it. A group's page has **Goes to**, where you pick the hub its sessions go to. In the **Projects** sidebar,
44
+ a group connected to a hub shows that hub's name, and folders waiting for you are listed first.
42
45
 
43
46
  As on any computer, a hub gets only the folders added to one of its projects
44
47
  (`interlatch --hub Acme hub add-folder ~/work/acme/app --project App`), unless you joined with `--all-folders`.
@@ -84,7 +87,7 @@ interlatch group choose ~/work/acme/notes Acme # or: personal
84
87
  interlatch group where ~/work/acme/app # who gets a folder, and why
85
88
  ```
86
89
 
87
- **Settings › Devices › Hubs** lists them too, with a **Choose** for each, and **Home** shows them under **Needs you**. Projects a folder holds waiting (a group
90
+ **Settings › Hubs** lists them too, with a **Choose** for each, and **Home** shows them under **Needs you**. Projects a folder holds waiting (a group
88
91
  connected to no hub inside a connected one) are listed once, under that folder.
89
92
 
90
93
  ## What agents see
@@ -92,7 +92,7 @@ computer sent to the hub before, and the hub has no key on record from it, or an
92
92
  for that computer, with the id the message shows: `interlatch hub invite <name> --computer <id>`; join with that code
93
93
  ([Joining a computer](devices.md#joining-a-computer)).
94
94
 
95
- **The status bar says "Not sending to the hub".** Click it: **Settings › Devices** says why. "The hub no longer
95
+ **The status bar says "Not sending to the hub".** Click it: **Settings › Hubs** says why. "The hub no longer
96
96
  accepts this computer" means the hub turned this computer's token away, so nothing it learns reaches the hub. Usually
97
97
  an admin revoked or replaced that computer in **Team › People**. Ask an admin for a new invite for this computer, then
98
98
  paste the code into **Join again** in the Hub card, or run `interlatch hub join <hub address> --code <code>`. The
@@ -482,4 +482,7 @@ JA: dict[str, str] = {
482
482
  "Pick a project to move.": "移動するプロジェクトを選んでください。",
483
483
  "This computer doesn't send to a hub.": "このコンピューターはハブに送っていません。",
484
484
  "Chronicle is now Interlatch. Updating moves this install from the agents-chronicle package to interlatch; the chronicle command keeps working.": "Chronicle は Interlatch になりました。更新すると、このインストールは agents-chronicle パッケージから interlatch に移ります。chronicle コマンドはそのまま使えます。",
485
+ "{hub} hasn't joined a hub yet.": "{hub} はまだハブに参加していません。",
486
+ "Change this on the computer itself, not from another device.": "別のデバイスからではなく、このコンピューターで変更してください。",
487
+ "With several hubs, every hub but Personal's gets summaries and lessons only.": "複数のハブがあるときは、Personal 以外のハブには要約とナレッジだけを送ります。",
485
488
  }
@@ -199,7 +199,10 @@ def cmd_hub(args) -> int:
199
199
  console.print(f"Joined the hub at {url}, sharing knowledge only. This computer keeps recording and "
200
200
  "analyzing its own sessions; the hub gets each analyzed session's summary and project "
201
201
  "lessons. Transcripts and lessons about you stay here.", highlight=False)
202
- if cfg.hub_all_folders:
202
+ if cfg.hub_all_folders and cfg.owner != "personal": # another hub: it gets what its groups hold
203
+ console.print("It shares sessions from every folder its groups hold (--all-folders): nothing until you "
204
+ "connect a group to it (`interlatch group connect <group> <hub>`).", highlight=False)
205
+ elif cfg.hub_all_folders:
203
206
  console.print("It shares sessions from every folder on this computer (--all-folders).", highlight=False)
204
207
  elif not args.code or got.get("projects") is None: # a limited person heard which projects above
205
208
  console.print("Only sessions in the hub's projects are shared: add your folder for each one with "
@@ -220,6 +220,8 @@ def action_share_mode(ctx: Context, share: str) -> dict:
220
220
  return {"error": tr("this computer has not joined a hub")}
221
221
  if share not in SHARE_MODES:
222
222
  return {"error": tr("unknown share mode {share!r}", share=share)}
223
+ if share != "knowledge" and ctx.cfg.owner != "personal": # its transcripts would carry every hub's sessions
224
+ return {"error": tr("With several hubs, every hub but Personal's gets summaries and lessons only.")}
223
225
  set_config_value(ctx.cfg, "hub", "share", json.dumps(share))
224
226
  ctx.cfg = load_config(ctx.cfg.home)
225
227
  return {"ok": True, "share": ctx.cfg.hub_share}
@@ -1,8 +1,9 @@
1
- """Several hubs on one computer (docs/profiles.md), on the dashboard: Settings › Devices › Hubs lists them and what
2
- waits for you; a group's page connects the group to one. application.profiles does the work."""
1
+ """Several hubs on one computer (docs/profiles.md), on the dashboard: Settings › Hubs lists them, how each one's sending
2
+ goes and what waits for you; a group's page connects the group to one. application.profiles does the work."""
3
3
 
4
4
  from __future__ import annotations
5
5
 
6
+ from ....core.i18n import tr
6
7
  from ..context import Context
7
8
 
8
9
 
@@ -22,6 +23,7 @@ def hubs(ctx: Context) -> dict:
22
23
  return {"error": str(exc), "hubs": [], "groups": [], "waiting": []}
23
24
  hubs = [r for r in rows if r["id"] != "waiting"]
24
25
  for r in hubs:
26
+ r.update(_sending(_hub_cfg(ctx, r["id"], reg)))
25
27
  r.pop("home", None) # this computer's folders: not needed to show them
26
28
  gs = []
27
29
  for g in groups(ctx.conn):
@@ -30,6 +32,71 @@ def hubs(ctx: Context) -> dict:
30
32
  return {"hubs": hubs, "groups": gs, "waiting": _waiting(waiting(ctx.conn), reg)}
31
33
 
32
34
 
35
+ class HubView:
36
+ """The dashboard as one of this computer's hubs sees it: that hub's config (Personal's, or another's own folder,
37
+ which reads Personal's archive), the same archive, jobs and state. What a device action writes back to its config
38
+ stays with that hub; only Personal's replaces the dashboard's."""
39
+
40
+ def __init__(self, ctx: Context, cfg):
41
+ self._ctx = ctx
42
+ self._cfg = cfg
43
+
44
+ @property
45
+ def cfg(self):
46
+ return self._cfg
47
+
48
+ @cfg.setter
49
+ def cfg(self, cfg) -> None:
50
+ self._cfg = cfg
51
+ if cfg.owner == "personal":
52
+ self._ctx.cfg = cfg
53
+
54
+ def __getattr__(self, name):
55
+ return getattr(self._ctx, name)
56
+
57
+
58
+ def for_hub(ctx: Context, key) -> tuple[object, str | None]:
59
+ """(the dashboard as hub `key` sees it, None), or (None, why not). No key, or "personal": Personal's, as before."""
60
+ from ....application.profiles import ProfileError, find_profile
61
+ from ....core.config import load_config
62
+
63
+ key = str(key or "").strip()
64
+ if not key or key.casefold() == "personal":
65
+ return ctx, None
66
+ try:
67
+ p = find_profile(ctx.cfg.home, key)
68
+ except ProfileError as exc:
69
+ return None, str(exc)
70
+ if not (p.home / "config.toml").exists():
71
+ return None, tr("{hub} hasn't joined a hub yet.", hub=p.label)
72
+ return HubView(ctx, load_config(p.home, create=False)), None
73
+
74
+
75
+ def _hub_cfg(ctx: Context, hub_id: str, reg):
76
+ """The config that sends to hub `hub_id`: Personal's, or that hub's own folder's (None: no such hub)."""
77
+ from ....core.config import load_config
78
+
79
+ if hub_id == "personal":
80
+ return ctx.cfg
81
+ p = reg.profiles.get(hub_id) if reg else None
82
+ return load_config(p.home, create=False) if p and (p.home / "config.toml").exists() else None
83
+
84
+
85
+ def _sending(cfg) -> dict:
86
+ """How sending to the hub goes, as Settings › Devices shows it for Personal's: the hub's name as it last said, the
87
+ last share, a problem worth a warning, and the teammates' lessons it sent back."""
88
+ from ....sharing.projects import last_folders
89
+ from ....sharing.pull import last_team
90
+ from ....sharing.push import last_push, send_problem
91
+
92
+ if cfg is None or not cfg.is_spoke:
93
+ return {"joined": False}
94
+ pushed = last_push(cfg)
95
+ return {"joined": True, "hub_name": (last_folders(cfg) or {}).get("hub"), "share": cfg.hub_share,
96
+ "all_folders": cfg.hub_all_folders, "last_push": pushed, "push_problem": send_problem(pushed),
97
+ "team_lessons": (last_team(cfg) or {}).get("lessons")}
98
+
99
+
33
100
  def _waiting(rows: list[dict], reg=None) -> list[dict]:
34
101
  """What waits, one entry per choice to make: the projects a folder holds waiting (a group connected to no hub
35
102
  inside a connected one, a project picked out of one) together under that folder, with how many projects and
@@ -51,7 +118,8 @@ def _waiting(rows: list[dict], reg=None) -> list[dict]:
51
118
 
52
119
  def action_hubs(ctx: Context, verb: str, body: dict) -> tuple[dict, int]:
53
120
  """add (name, and optionally address and code to join it), remove, connect (group, hub; personal disconnects),
54
- choose (folder, hub or personal). A change the hubs can't make says why, 409, and changes nothing."""
121
+ choose (folder, hub or personal), push (hub: share with it now), leave (hub: stop sending to it). A change the hubs
122
+ can't make says why, 409, and changes nothing."""
55
123
  from ....application import profiles as ops
56
124
  from ....core.profiles import RegistryError
57
125
  from ....projects.groups import GroupError
@@ -87,6 +155,24 @@ def action_hubs(ctx: Context, verb: str, body: dict) -> tuple[dict, int]:
87
155
  moved = ops.connect(ctx.cfg, body.get("group"), hub, leave_on_hub=leave)
88
156
  return {"ok": True, "moved": sum(moved.sessions.values()), "taken": sum(moved.taken.values()),
89
157
  "to": {moved.labels.get(k, k): v for k, v in moved.to.items()}}, 200
158
+ if verb in ("push", "leave"):
159
+ key = str(body.get("hub") or "")
160
+ reg = ctx.cfg.profiles
161
+ hub_id = "personal" if key.strip().casefold() == "personal" else ops.find_profile(ctx.cfg.home, key).id
162
+ hcfg = _hub_cfg(ctx, hub_id, reg)
163
+ if hcfg is None or not hcfg.is_spoke:
164
+ return {"error": tr("{hub} hasn't joined a hub yet.", hub=key)}, 409
165
+ if verb == "push":
166
+ from ....sharing.push import push
167
+
168
+ return {"ok": True, "started": ctx.jobs.start("push", lambda progress: push(hcfg, progress=progress)
169
+ .summary())}, 200
170
+ from ....sharing.client import leave_hub
171
+
172
+ left = leave_hub(hcfg)
173
+ if hub_id == "personal":
174
+ ctx.cfg = left
175
+ return {"ok": True, "hub": hcfg.hub_url}, 200
90
176
  if verb == "choose":
91
177
  moved = ops.choose(ctx.cfg, str(body.get("folder") or ""), str(body.get("hub") or ""), leave_on_hub=leave)
92
178
  return {"ok": True, "moved": sum(moved.sessions.values()), "taken": sum(moved.taken.values())}, 200
@@ -156,12 +156,13 @@ def status_small(ctx: Context, limited: bool = False) -> dict:
156
156
  if limited: # the hub's own queue, jobs and updates are not theirs to see
157
157
  return {"pending": {"ready": 0, "queued": 0}, "analysis": {"auto": False, "max_per_run": 0, "label": ""}, "paused_until": None,
158
158
  "last_sync": None, "jobs": {}, "version": __version__, "update": None, "ui_build": UI_BUILD,
159
- "hub_url": None, "hub_problem": None, "limited": True}
159
+ "hub_url": None, "hub_urls": [], "hub_problem": None, "limited": True}
160
160
 
161
161
  recall(ctx.conn) # a check made by another dashboard process, or before a restart
162
162
  _maybe_check_daily(ctx)
163
163
 
164
164
  pending = count_pending(ctx.conn, ctx.cfg)
165
+ sending = _sending_to(ctx)
165
166
  return {
166
167
  "pending": pending,
167
168
  "analysis": {"auto": ctx.cfg.analysis.auto, "max_per_run": ctx.cfg.analysis.max_per_run,
@@ -175,14 +176,35 @@ def status_small(ctx: Context, limited: bool = False) -> dict:
175
176
  "update": available(),
176
177
  "ui_build": UI_BUILD,
177
178
  "hub_url": ctx.cfg.hub_url or None, # set on a computer that sends its sessions to a hub
178
- "hub_problem": _hub_problem(ctx), # its pushes don't get through: the status bar warns
179
+ "hub_urls": [c.hub_url for c in sending], # and the other hubs it sends to (docs/profiles.md)
180
+ "hub_problem": _hub_problem(sending), # its pushes don't get through: the status bar warns
179
181
  }
180
182
 
181
183
 
182
- def _hub_problem(ctx: Context) -> dict | None:
184
+ def _sending_to(ctx: Context) -> list:
185
+ """The configs that send to a hub: Personal's (when it joined one), then each other hub this computer joined."""
186
+ from ....application.profiles import joined
187
+
188
+ return ([ctx.cfg] if ctx.cfg.is_spoke else []) + joined(ctx.cfg)
189
+
190
+
191
+ def _hub_problem(sending: list) -> dict | None:
192
+ """The first hub whose sends don't get through, with its name when it isn't Personal's (`profile`)."""
193
+ from ....core.profiles import PERSONAL
183
194
  from ....sharing.push import last_push, send_problem
184
195
 
185
- return send_problem(last_push(ctx.cfg)) if ctx.cfg.is_spoke else None
196
+ for cfg in sending:
197
+ if problem := send_problem(last_push(cfg)):
198
+ if cfg.owner != PERSONAL:
199
+ from ....core.profiles import RegistryError
200
+
201
+ try:
202
+ reg = cfg.profiles
203
+ except RegistryError:
204
+ reg = None
205
+ problem["profile"] = reg.label(cfg.owner) if reg else cfg.owner
206
+ return problem
207
+ return None
186
208
 
187
209
 
188
210
  def status(ctx: Context) -> dict: