langbot-plugin 0.4.1__tar.gz → 0.4.2__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 (243) hide show
  1. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/AGENTS.md +173 -83
  2. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/PKG-INFO +2 -1
  3. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/pyproject.toml +2 -1
  4. langbot_plugin-0.4.2/src/langbot_plugin/assets/templates/.github/workflows/release.yml.example +77 -0
  5. langbot_plugin-0.4.2/src/langbot_plugin/assets/templates/README.md.example +15 -0
  6. langbot_plugin-0.4.2/src/langbot_plugin/assets/templates/readme/README_zh_Hans.md.example +15 -0
  7. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/box/client.py +6 -0
  8. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/box/nsjail_backend.py +80 -14
  9. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/cli/commands/initplugin.py +4 -0
  10. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/cli/gen/renderer.py +2 -0
  11. langbot_plugin-0.4.2/src/langbot_plugin/entities/io/errors.py +91 -0
  12. langbot_plugin-0.4.2/src/langbot_plugin/runtime/helper/pkgmgr.py +369 -0
  13. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/runtime/plugin/mgr.py +78 -12
  14. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/tests/box/test_nsjail_backend.py +118 -18
  15. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/tests/cli/test_initplugin.py +2 -0
  16. langbot_plugin-0.4.2/tests/entities/io/test_dependency_errors.py +59 -0
  17. langbot_plugin-0.4.2/tests/runtime/helper/test_pkgmgr.py +388 -0
  18. langbot_plugin-0.4.2/tests/runtime/io/test_handler.py +578 -0
  19. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/tests/runtime/plugin/test_manager.py +62 -2
  20. langbot_plugin-0.4.1/src/langbot_plugin/assets/templates/README.md.example +0 -3
  21. langbot_plugin-0.4.1/src/langbot_plugin/entities/io/errors.py +0 -31
  22. langbot_plugin-0.4.1/src/langbot_plugin/runtime/helper/pkgmgr.py +0 -141
  23. langbot_plugin-0.4.1/tests/runtime/helper/test_pkgmgr.py +0 -109
  24. langbot_plugin-0.4.1/tests/runtime/io/test_handler.py +0 -228
  25. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/.github/workflows/publish-pypi.yaml +0 -0
  26. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/.github/workflows/test.yml +0 -0
  27. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/.gitignore +0 -0
  28. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/.python-version +0 -0
  29. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/CLAUDE.md +0 -0
  30. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/LICENSE +0 -0
  31. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/README.md +0 -0
  32. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/data/.env.example +0 -0
  33. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/docs/Message.md +0 -0
  34. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/docs/PluginPages.md +0 -0
  35. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/docs/communication/apis/lb2rt.md +0 -0
  36. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/docs/communication/runtime_plugin.md +0 -0
  37. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/docs/dependency-management.md +0 -0
  38. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/docs/langbot-plugin-social.png +0 -0
  39. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/__init__.py +0 -0
  40. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/__init__.py +0 -0
  41. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/definition/__init__.py +0 -0
  42. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/definition/abstract/__init__.py +0 -0
  43. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/definition/abstract/platform/__init__.py +0 -0
  44. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/definition/abstract/platform/adapter.py +0 -0
  45. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/definition/abstract/platform/event_logger.py +0 -0
  46. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/definition/components/__init__.py +0 -0
  47. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/definition/components/base.py +0 -0
  48. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/definition/components/command/__init__.py +0 -0
  49. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/definition/components/command/command.py +0 -0
  50. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/definition/components/common/__init__.py +0 -0
  51. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/definition/components/common/event_listener.py +0 -0
  52. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/definition/components/knowledge_engine/__init__.py +0 -0
  53. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/definition/components/knowledge_engine/engine.py +0 -0
  54. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/definition/components/manifest.py +0 -0
  55. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/definition/components/page/__init__.py +0 -0
  56. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/definition/components/parser/__init__.py +0 -0
  57. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/definition/components/parser/parser.py +0 -0
  58. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/definition/components/tool/__init__.py +0 -0
  59. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/definition/components/tool/tool.py +0 -0
  60. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/definition/plugin.py +0 -0
  61. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/entities/__init__.py +0 -0
  62. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/entities/builtin/__init__.py +0 -0
  63. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/entities/builtin/command/__init__.py +0 -0
  64. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/entities/builtin/command/context.py +0 -0
  65. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/entities/builtin/command/errors.py +0 -0
  66. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/entities/builtin/pipeline/__init__.py +0 -0
  67. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/entities/builtin/pipeline/query.py +0 -0
  68. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/entities/builtin/platform/__init__.py +0 -0
  69. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/entities/builtin/platform/entities.py +0 -0
  70. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/entities/builtin/platform/events.py +0 -0
  71. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/entities/builtin/platform/logger.py +0 -0
  72. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/entities/builtin/platform/message.py +0 -0
  73. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/entities/builtin/provider/__init__.py +0 -0
  74. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/entities/builtin/provider/message.py +0 -0
  75. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/entities/builtin/provider/prompt.py +0 -0
  76. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/entities/builtin/provider/session.py +0 -0
  77. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/entities/builtin/rag/__init__.py +0 -0
  78. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/entities/builtin/rag/context.py +0 -0
  79. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/entities/builtin/rag/enums.py +0 -0
  80. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/entities/builtin/rag/errors.py +0 -0
  81. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/entities/builtin/rag/models.py +0 -0
  82. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/entities/builtin/resource/__init__.py +0 -0
  83. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/entities/builtin/resource/tool.py +0 -0
  84. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/entities/context.py +0 -0
  85. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/entities/events.py +0 -0
  86. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/proxies/__init__.py +0 -0
  87. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/proxies/base.py +0 -0
  88. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/proxies/event_context.py +0 -0
  89. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/proxies/execute_context.py +0 -0
  90. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/proxies/langbot_api.py +0 -0
  91. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/api/proxies/query_based_api.py +0 -0
  92. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/assets/__init__.py +0 -0
  93. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/assets/langbot-page-sdk.js +0 -0
  94. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/assets/templates/.env.example.example +0 -0
  95. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/assets/templates/.gitignore.example +0 -0
  96. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/assets/templates/.vscode/launch.json.example +0 -0
  97. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/assets/templates/__init__.py +0 -0
  98. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/assets/templates/assets/icon.svg.example +0 -0
  99. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/assets/templates/components/__init__.py +0 -0
  100. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/assets/templates/components/commands/__init__.py +0 -0
  101. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/assets/templates/components/commands/{cmd_name}.py.example +0 -0
  102. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/assets/templates/components/commands/{cmd_name}.yaml.example +0 -0
  103. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/assets/templates/components/event_listener/__init__.py +0 -0
  104. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/assets/templates/components/event_listener/default.py.example +0 -0
  105. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/assets/templates/components/event_listener/default.yaml.example +0 -0
  106. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/assets/templates/components/knowledge_engine/__init__.py +0 -0
  107. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/assets/templates/components/knowledge_engine/{knowledge_engine_name}.py.example +0 -0
  108. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/assets/templates/components/knowledge_engine/{knowledge_engine_name}.yaml.example +0 -0
  109. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/assets/templates/components/pages/{page_name}.html.example +0 -0
  110. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/assets/templates/components/pages/{page_name}.yaml.example +0 -0
  111. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/assets/templates/components/parser/__init__.py +0 -0
  112. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/assets/templates/components/parser/{parser_name}.py.example +0 -0
  113. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/assets/templates/components/parser/{parser_name}.yaml.example +0 -0
  114. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/assets/templates/components/tools/__init__.py +0 -0
  115. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/assets/templates/components/tools/{tool_name}.py.example +0 -0
  116. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/assets/templates/components/tools/{tool_name}.yaml.example +0 -0
  117. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/assets/templates/main.py.example +0 -0
  118. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/assets/templates/manifest.yaml.example +0 -0
  119. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/assets/templates/requirements.txt.example +0 -0
  120. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/box/__init__.py +0 -0
  121. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/box/actions.py +0 -0
  122. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/box/backend.py +0 -0
  123. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/box/e2b_backend.py +0 -0
  124. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/box/errors.py +0 -0
  125. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/box/models.py +0 -0
  126. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/box/runtime.py +0 -0
  127. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/box/security.py +0 -0
  128. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/box/server.py +0 -0
  129. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/box/skill_store.py +0 -0
  130. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/cli/__init__.py +0 -0
  131. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/cli/__main__.py +0 -0
  132. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/cli/commands/__init__.py +0 -0
  133. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/cli/commands/buildplugin.py +0 -0
  134. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/cli/commands/gencomponent.py +0 -0
  135. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/cli/commands/login.py +0 -0
  136. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/cli/commands/logout.py +0 -0
  137. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/cli/commands/publish.py +0 -0
  138. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/cli/commands/runplugin.py +0 -0
  139. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/cli/gen/__init__.py +0 -0
  140. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/cli/i18n.py +0 -0
  141. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/cli/locales/__init__.py +0 -0
  142. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/cli/locales/en_US.py +0 -0
  143. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/cli/locales/es_ES.py +0 -0
  144. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/cli/locales/ja_JP.py +0 -0
  145. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/cli/locales/th_TH.py +0 -0
  146. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/cli/locales/vi_VN.py +0 -0
  147. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/cli/locales/zh_Hans.py +0 -0
  148. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/cli/locales/zh_Hant.py +0 -0
  149. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/cli/run/__init__.py +0 -0
  150. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/cli/run/controller.py +0 -0
  151. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/cli/run/handler.py +0 -0
  152. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/cli/run/hotreload.py +0 -0
  153. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/cli/utils/__init__.py +0 -0
  154. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/cli/utils/cloudsv.py +0 -0
  155. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/cli/utils/form.py +0 -0
  156. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/cli/utils/page_components.py +0 -0
  157. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/entities/__init__.py +0 -0
  158. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/entities/io/__init__.py +0 -0
  159. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/entities/io/actions/__init__.py +0 -0
  160. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/entities/io/actions/enums.py +0 -0
  161. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/entities/io/req.py +0 -0
  162. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/entities/io/resp.py +0 -0
  163. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/entities/marketplace.py +0 -0
  164. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/runtime/LICENSE +0 -0
  165. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/runtime/README.md +0 -0
  166. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/runtime/__init__.py +0 -0
  167. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/runtime/app.py +0 -0
  168. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/runtime/context.py +0 -0
  169. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/runtime/helper/__init__.py +0 -0
  170. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/runtime/helper/marketplace.py +0 -0
  171. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/runtime/io/__init__.py +0 -0
  172. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/runtime/io/connection.py +0 -0
  173. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/runtime/io/connections/__init__.py +0 -0
  174. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/runtime/io/connections/stdio.py +0 -0
  175. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/runtime/io/connections/ws.py +0 -0
  176. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/runtime/io/controller.py +0 -0
  177. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/runtime/io/controllers/__init__.py +0 -0
  178. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/runtime/io/controllers/stdio/__init__.py +0 -0
  179. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/runtime/io/controllers/stdio/client.py +0 -0
  180. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/runtime/io/controllers/stdio/server.py +0 -0
  181. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/runtime/io/controllers/ws/__init__.py +0 -0
  182. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/runtime/io/controllers/ws/client.py +0 -0
  183. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/runtime/io/controllers/ws/server.py +0 -0
  184. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/runtime/io/handler.py +0 -0
  185. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/runtime/io/handlers/__init__.py +0 -0
  186. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/runtime/io/handlers/control.py +0 -0
  187. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/runtime/io/handlers/plugin.py +0 -0
  188. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/runtime/plugin/__init__.py +0 -0
  189. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/runtime/plugin/container.py +0 -0
  190. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/runtime/settings.py +0 -0
  191. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/utils/__init__.py +0 -0
  192. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/utils/discover/__init__.py +0 -0
  193. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/utils/discover/engine.py +0 -0
  194. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/utils/importutil.py +0 -0
  195. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/utils/log.py +0 -0
  196. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/utils/platform.py +0 -0
  197. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/src/langbot_plugin/version.py +0 -0
  198. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/tests/__init__.py +0 -0
  199. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/tests/api/definition/components/test_components.py +0 -0
  200. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/tests/api/definition/test_manifest.py +0 -0
  201. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/tests/api/entities/builtin/test_command_context.py +0 -0
  202. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/tests/api/entities/builtin/test_platform_logger.py +0 -0
  203. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/tests/api/entities/builtin/test_provider_message.py +0 -0
  204. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/tests/api/entities/builtin/test_rag_models.py +0 -0
  205. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/tests/api/entities/test_context.py +0 -0
  206. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/tests/api/entities/test_events.py +0 -0
  207. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/tests/api/proxies/test_base.py +0 -0
  208. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/tests/api/proxies/test_langbot_api.py +0 -0
  209. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/tests/api/proxies/test_query_based_api.py +0 -0
  210. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/tests/box/__init__.py +0 -0
  211. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/tests/box/test_backend.py +0 -0
  212. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/tests/box/test_backend_selection.py +0 -0
  213. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/tests/box/test_client.py +0 -0
  214. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/tests/box/test_e2b_backend.py +0 -0
  215. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/tests/box/test_runtime.py +0 -0
  216. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/tests/box/test_server.py +0 -0
  217. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/tests/box/test_skill_store.py +0 -0
  218. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/tests/cli/run/test_controller.py +0 -0
  219. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/tests/cli/run/test_runtime_handler.py +0 -0
  220. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/tests/cli/test_buildplugin.py +0 -0
  221. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/tests/cli/test_gencomponent.py +0 -0
  222. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/tests/cli/test_i18n_form.py +0 -0
  223. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/tests/cli/test_login.py +0 -0
  224. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/tests/cli/test_logout_publish.py +0 -0
  225. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/tests/cli/test_page_components.py +0 -0
  226. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/tests/cli/test_renderer.py +0 -0
  227. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/tests/cli/test_runplugin.py +0 -0
  228. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/tests/entities/io/test_protocol.py +0 -0
  229. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/tests/helpers/__init__.py +0 -0
  230. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/tests/helpers/protocol.py +0 -0
  231. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/tests/runtime/helper/test_marketplace.py +0 -0
  232. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/tests/runtime/io/handlers/test_control_handler.py +0 -0
  233. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/tests/runtime/io/handlers/test_import_contracts.py +0 -0
  234. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/tests/runtime/io/handlers/test_plugin_handler.py +0 -0
  235. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/tests/runtime/io/test_connections.py +0 -0
  236. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/tests/runtime/io/test_controllers.py +0 -0
  237. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/tests/runtime/plugin/test_container.py +0 -0
  238. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/tests/runtime/test_app.py +0 -0
  239. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/tests/test_log.py +0 -0
  240. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/tests/test_message.py +0 -0
  241. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/tests/utils/test_discovery.py +0 -0
  242. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/tests/utils/test_importutil.py +0 -0
  243. {langbot_plugin-0.4.1 → langbot_plugin-0.4.2}/tests/utils/test_platform.py +0 -0
@@ -1,21 +1,29 @@
1
1
  # AGENTS.md
2
2
 
3
- This file is for guiding code agents (like Claude Code, GitHub Copilot, OpenAI Codex, etc.) to work in the langbot-plugin-sdk project.
3
+ This file is for guiding code agents (like Claude Code, GitHub Copilot, OpenAI Codex, etc.) to work in the langbot-plugin-sdk project. `CLAUDE.md` is a symlink to this file.
4
4
 
5
- **IMPORTANT**: This document may contain outdated information and may differ from the actual content. Please refer to the actual situation for accuracy.
5
+ > Always verify specifics (file paths, CLI flags, function signatures) against the actual source before relying on them. When you find this doc out of date, fix it as part of your change.
6
6
 
7
7
  ## Project Overview
8
8
 
9
9
  LangBot Plugin SDK is the infrastructure for LangBot's plugin system, providing:
10
10
  - **Plugin SDK**: Python APIs and interfaces for plugin development
11
- - **Plugin Runtime**: Execution environment managing plugin lifecycle
12
- - **CLI Tools**: `lbp` command for scaffolding, building, and debugging plugins
11
+ - **Plugin Runtime**: Execution environment managing plugin lifecycle (`lbp rt`)
12
+ - **Box Runtime**: Code-sandbox runtime service backing LangBot's Box subsystem (`lbp box`)
13
+ - **CLI Tools**: `lbp` command for scaffolding, building, debugging, and running runtimes
13
14
  - **Communication Protocol**: Bidirectional action-based protocol (stdio/WebSocket)
14
15
 
15
- The SDK enables developers to extend LangBot with custom:
16
- - **Commands**: User-triggered actions (e.g., `!weather tokyo`)
17
- - **Tools**: LLM-callable functions for AI agents (e.g., web search, database queries)
18
- - **Event Listeners**: Handlers for message events (e.g., auto-reply, content filtering)
16
+ This SDK is the shared dependency between LangBot and plugins; LangBot pins it as `langbot-plugin==<x.y.z>` in its `pyproject.toml`. The package version lives in this repo's `pyproject.toml`.
17
+
18
+ The SDK enables developers to extend LangBot with custom components. Six component types are supported (scaffold any of them with `lbp comp <Type>`):
19
+ - **Command**: User-triggered actions (e.g., `!weather tokyo`)
20
+ - **Tool**: LLM-callable functions for AI agents (e.g., web search, database queries)
21
+ - **EventListener**: Handlers for message-pipeline events (e.g., auto-reply, content filtering)
22
+ - **KnowledgeEngine**: Custom knowledge-base retrieval/integration used by RAG
23
+ - **Parser**: Custom parsing of messages/content
24
+ - **Page**: Custom web page embeddable in the LangBot admin panel
25
+
26
+ Component base classes live in `src/langbot_plugin/api/definition/components/` (`command/`, `tool/`, `common/event_listener.py`, `knowledge_engine/`, `parser/`, `page/`).
19
27
 
20
28
  ## Technology Stack
21
29
 
@@ -47,31 +55,33 @@ langbot-plugin-sdk/
47
55
  │ │ │ ├── command/ # CommandReturn
48
56
  │ │ │ └── provider/ # Session, Conversation
49
57
  │ │ └── proxies/ # API proxy classes
50
- │ │ ├── langbot_api.py # LangBotAPIProxy
51
- │ │ └── query_based.py # QueryBasedAPIProxy
52
58
  │ ├── runtime/ # Plugin runtime system
53
- │ │ ├── plugin/ # Plugin management
54
- │ │ ├── mgr.py # PluginManager
55
- │ │ │ ├── container.py # PluginContainer
56
- │ │ │ └── installer.py # Plugin installation
59
+ │ │ ├── app.py # `lbp rt` entrypoint (runtime_app.main)
60
+ │ │ ├── plugin/ # Plugin management (mgr.py, container.py, installer)
57
61
  │ │ ├── io/ # Communication layer
58
- │ │ │ ├── handler.py # Action handler
59
- │ │ │ ├── stdio.py # Stdio transport
60
- │ │ │ └── websocket.py # WebSocket transport
61
- │ │ └── event/ # Event dispatching
62
- ├── cli/ # Command-line tools
63
- ├── main.py # lbp entrypoint
64
- │ │ ├── init.py # Plugin initialization
65
- │ │ ├── gen/ # Component generation
66
- │ │ └── run.py # Plugin debugging
62
+ │ │ │ ├── connection.py # Base connection
63
+ │ │ │ ├── connections/ # stdio / ws connection impls
64
+ │ │ │ ├── controller.py , controllers/ # client/server controllers (stdio, ws)
65
+ │ │ │ ├── handler.py , handlers/ # action handlers
66
+ │ └── helper/
67
+ │ ├── box/ # Box (code-sandbox) runtime — `lbp box`
68
+ │ │ ├── server.py # `lbp box` entrypoint (box_main)
69
+ │ │ ├── backend.py # Backend abstraction + selection (Docker handled here/runtime)
70
+ │ │ ├── nsjail_backend.py / e2b_backend.py # sandbox backend impls
71
+ │ │ ├── runtime.py , client.py , actions.py , models.py , errors.py , security.py , skill_store.py
72
+ │ ├── cli/ # Command-line tools (`lbp`)
73
+ │ │ ├── __init__.py # lbp entrypoint + argparse subcommands
74
+ │ │ ├── commands/ # init / comp / build / publish / login / logout impls
75
+ │ │ ├── run/ # plugin run (remote-debug) impl
76
+ │ │ ├── gen/ # component generation (Jinja2)
77
+ │ │ ├── i18n.py , locales/ # CLI i18n
78
+ │ │ └── utils/
67
79
  │ ├── entities/ # Internal data structures
68
- │ │ ├── io/ # Communication protocol
69
- │ └── plugin/ # Plugin metadata
70
- │ └── utils/ # Utilities
71
- │ ├── discover/ # Component discovery
72
- │ └── network/ # Network helpers
80
+ │ │ └── io/ # Communication protocol (actions, errors, resp)
81
+ ├── assets/templates/ # Plugin scaffolding templates
82
+ │ └── utils/ # Utilities (discover/, network, log, ...)
73
83
  ├── docs/ # Documentation
74
- ├── pyproject.toml # Python project config
84
+ ├── pyproject.toml # Python project config (version, requires-python >=3.10)
75
85
  └── README.md
76
86
  ```
77
87
 
@@ -113,12 +123,12 @@ langbot-plugin-sdk/
113
123
 
114
124
  ```
115
125
  BasePlugin
116
- ├── Command (1+ per plugin)
117
- │ └── Subcommand handlers
118
- ├── Tool (0+ per plugin)
119
- │ └── call() method
120
- └── EventListener (1 per plugin)
121
- └── Event handlers
126
+ ├── Command Subcommand handlers
127
+ ├── Tool → call() method
128
+ ├── EventListener → event handlers
129
+ ├── KnowledgeEngine → knowledge-base retrieval/integration (RAG)
130
+ ├── Parser → message/content parsing
131
+ └── Page → custom admin-panel web page
122
132
  ```
123
133
 
124
134
  ## SDK Development
@@ -806,84 +816,164 @@ Encapsulates plugin instance with:
806
816
 
807
817
  ### Communication Protocol (`runtime/io/`)
808
818
 
809
- **Action-Based Protocol**:
810
- - Bidirectional JSON messages
811
- - Request-response with sequence IDs
812
- - Streaming support via chunk status
813
- - File transfer (16KB chunks)
814
-
815
- **Action Structure**:
819
+ This protocol is **JSON-RPC 2.0 in all but field names** — a bidirectional, action-based
820
+ RPC carried over a single connection. It is the same shape the Language Server Protocol
821
+ (LSP) and the Model Context Protocol (MCP) use: JSON-RPC 2.0 over stdio / WebSocket, with
822
+ either peer free to initiate calls. We did not adopt the spec verbatim, but the semantics
823
+ map 1:1, and it is useful to think of it that way:
824
+
825
+ | Our field (`entities/io`) | JSON-RPC 2.0 | Meaning |
826
+ |----------------------------|--------------------------|---------|
827
+ | `seq_id` | `id` | Correlates a response back to its request |
828
+ | `action` | `method` | Which action to invoke |
829
+ | `data` (in a request) | `params` | Call arguments |
830
+ | `data` (in a response) | `result` | Return value (when `code == 0`) |
831
+ | `code` + `message` | `error.{code,message}` | `code == 0` is success; non-zero carries the failure in `message` |
832
+ | `chunk_status` | (MCP-style streaming ext.) | `continue` per chunk, `end` closes a streamed response |
833
+
834
+ Both peers share one connection and may call each other's actions. Requests and responses
835
+ are told apart by shape: a message carrying `action` is an inbound request, one carrying
836
+ `code` is a response. Each side allocates `seq_id` from its own incrementing counter, so the
837
+ two id spaces are dispatched independently and never collide.
838
+
839
+ **Request** (`ActionRequest`, `entities/io/req.py`):
816
840
  ```json
817
- {
818
- "action": "action_name",
819
- "seq": 12345,
820
- "data": {...},
821
- "chunk_status": "start|continue|end"
822
- }
841
+ { "seq_id": 12345, "action": "action_name", "data": {} }
823
842
  ```
824
843
 
825
- **Response Structure**:
844
+ **Response** (`ActionResponse`, `entities/io/resp.py`):
826
845
  ```json
827
- {
828
- "seq": 12345,
829
- "ok": true,
830
- "data": {...}
831
- }
846
+ { "seq_id": 12345, "code": 0, "message": "success", "data": {}, "chunk_status": "continue" }
832
847
  ```
833
848
 
834
- **Transport Implementations**:
835
- - `StdioHandler`: stdin/stdout communication
836
- - `WebSocketHandler`: WebSocket client/server
849
+ - `code == 0` → success, payload in `data`; non-zero → error, human-readable text in `message`.
850
+ - **Streaming**: the responder emits N chunks with `chunk_status: "continue"`, then a final
851
+ `chunk_status: "end"` (consumed by `Handler.call_action_generator`).
852
+ - **File transfer**: large blobs travel as `__file_chunk` actions — 16KB base64 chunks,
853
+ reassembled to `data/temp/lbp/<file_key>` on the receiving side.
854
+
855
+ **Core engine**: `runtime/io/handler.py` (`Handler`) owns `seq_id` allocation, the
856
+ `resp_waiters` (single response) and `resp_queues` (streamed response) correlation maps,
857
+ timeouts, and the receive loop (`Handler.run`).
858
+
859
+ **Transport Implementations** (`Connection` subclasses, `runtime/io/connections/`):
860
+ - `stdio.py`: stdin/stdout — default for subprocess (personal / lightweight) plugins
861
+ - `ws.py`: WebSocket — for containerized / remote plugin deployments
837
862
 
838
863
  ## CLI Tools
839
864
 
840
- ### lbp Commands
865
+ ### `lbp` Commands
841
866
 
842
867
  ```bash
843
- # Initialize new plugin
844
- lbp init [plugin_name]
868
+ lbp init [plugin_name] # Scaffold a new plugin
869
+ lbp comp <Command|Tool|EventListener> # Generate a component
870
+ lbp run [-s] # Run/remote-debug the plugin against a running Runtime
871
+ lbp build [-o dist] # Build the plugin into a zip
872
+ lbp publish [-o dist] # Publish to LangBot Marketplace (login first: lbp login)
873
+ lbp login [-t TOKEN] / lbp logout # Marketplace account auth
874
+ lbp rt [...] # Run the Plugin Runtime (see below)
875
+ lbp box [...] # Run the Box (sandbox) Runtime (see below)
876
+ lbp ver # Print SDK version
877
+ ```
845
878
 
846
- # Generate component
847
- lbp comp [Command|Tool|EventListener]
879
+ The CLI entrypoint is `src/langbot_plugin/cli/__init__.py` (argparse). `lbp rt` dispatches to `langbot_plugin.runtime.app.main`; `lbp box` dispatches to `langbot_plugin.box.server.main`. Subcommand implementations live under `cli/commands/`, `cli/run/`, and `cli/gen/`.
848
880
 
849
- # Run plugin locally (requires running LangBot)
850
- lbp run [-s]
881
+ ## Debugging the Runtime, CLI & SDK
851
882
 
852
- # Build plugin for distribution
853
- lbp build
883
+ This is the canonical place for runtime/CLI/box debugging detail (LangBot's own `AGENTS.md` only indexes this section). The corresponding user-facing wiki page is ["调试插件运行时、CLI、SDK"](https://docs.langbot.app/zh/develop/plugin-runtime).
854
884
 
855
- # Publish to marketplace
856
- lbp publish
885
+ ### Recommended workspace layout
886
+
887
+ Because LangBot depends on entities defined here, clone both repos as siblings under one parent dir and open that parent in your editor:
888
+
889
+ ```
890
+ langbot-projects/
891
+ ├── LangBot
892
+ └── langbot-plugin-sdk
893
+ ```
894
+
895
+ Set up LangBot's venv (`cd LangBot && uv sync --dev`) and point your editor's Python interpreter at LangBot's `.venv`.
896
+
897
+ ### Plugin Runtime — `lbp rt`
898
+
899
+ Start a standalone Plugin Runtime from this repo:
857
900
 
858
- # Start runtime (for WebSocket mode)
859
- lbp rt
901
+ ```bash
902
+ uv run --no-sync lbp rt
903
+ # equivalent: python -m langbot_plugin.cli.__init__ rt
860
904
  ```
861
905
 
862
- ### Template System (`cli/gen/`)
906
+ `lbp rt` flags (see `cli/__init__.py`):
907
+
908
+ - `-s`, `--stdio-control`: use stdio for the control connection. **Production only** — in a container LangBot pipes the runtime over stdio.
909
+ - `--ws-control-port` (default `5400`): control port LangBot's main process connects to.
910
+ - `--ws-debug-port` (default `5401`): debug port for `lbp run` to attach a plugin under development.
911
+ - `--debug-only`: do not auto-start plugins in `data/plugins/`; only accept plugins via debug connections.
912
+ - `--skip-deps-check`: skip the per-startup check/install of every installed plugin's dependencies.
913
+ - `--pypi-index-url` / `--pypi-trusted-host`: customize the index used for plugin dependency installs.
863
914
 
864
- - Jinja2-based templates in `assets/templates/`
865
- - Variables: plugin_name, plugin_author, component_name
866
- - Generates boilerplate code and manifests
915
+ > **Transport note:** When LangBot is run directly (not in a container), it spawns the runtime itself and talks **stdio**, which **cannot auto-reconnect** — on disconnect it logs "please restart LangBot". A frequent cause is an **orphan runtime** from a previous backend still holding `5400`/`5401`; kill it and restart.
867
916
 
868
- ## Testing & Debugging
917
+ ### Make LangBot use your locally-modified SDK
869
918
 
870
- ### Local Testing
919
+ If you change shared definitions (message entities, plugin data models, etc.), install your local SDK into LangBot's environment so data formats stay compatible at runtime:
871
920
 
872
- 1. Start LangBot:
873
921
  ```bash
874
- cd /path/to/LangBot
875
- uv run main.py
922
+ # In a terminal where LangBot's .venv is ACTIVE, from the langbot-plugin-sdk dir:
923
+ uv pip install .
924
+ ```
925
+
926
+ ### Make LangBot connect to your standalone Runtime (WebSocket)
927
+
928
+ 1. In LangBot's `data/config.yaml`, set the control URL:
929
+
930
+ ```yaml
931
+ plugin:
932
+ runtime_ws_url: ws://localhost:5400/control/ws
933
+ ```
934
+
935
+ 2. Launch LangBot with `--standalone-runtime` and `--no-sync` (the latter prevents `uv` from overwriting your locally-installed SDK):
936
+
937
+ ```bash
938
+ uv run --no-sync main.py --standalone-runtime
939
+ ```
940
+
941
+ Restart LangBot; it now connects to your `lbp rt` over WebSocket, so you can iterate on the runtime/SDK independently.
942
+
943
+ ### Box Runtime — `lbp box`
944
+
945
+ The Box Runtime is the sandbox service backing LangBot's `pkg/box` subsystem (`src/langbot_plugin/box/`). Start it with:
946
+
947
+ ```bash
948
+ lbp box # WebSocket control transport (default)
949
+ lbp box -s # stdio control transport
876
950
  ```
877
951
 
878
- 2. Run plugin:
952
+ `lbp box` flags:
953
+
954
+ - `--host` (default `0.0.0.0`): bind address.
955
+ - `-s`, `--stdio-control`: use stdio for the control connection (instead of WebSocket).
956
+ - `--ws-control-port` (default `5410`): control port.
957
+
958
+ All Box WebSocket endpoints share one port (default `5410`): `/rpc/ws` (action RPC control channel) plus managed-process stdio relay endpoints under `/v1/sessions/...`. There is **no** `python -m langbot_plugin.box` launch path — `lbp box` is the only supported entrypoint.
959
+
960
+ Box selects the first available sandbox backend among **Docker / nsjail / E2B**. Backends live in `box/backend.py`, `box/nsjail_backend.py`, `box/e2b_backend.py`. A common false "no backend" on the LangBot side is Docker running but the user not in the `docker` group (socket permission denied) — that's a host-permissions issue, not a Box bug.
961
+
962
+ To make LangBot connect to a standalone Box runtime (mirrors the plugin-runtime flow): in LangBot's `data/config.yaml` set `box.runtime.endpoint` to the runtime's base URL (e.g. `ws://127.0.0.1:5410`), pick `box.backend` (`'local'`/`'docker'`/`'nsjail'`/`'e2b'`), and start LangBot with `--standalone-box`. In Docker deployments LangBot reaches the Box runtime at host `langbot_box:5410`.
963
+
964
+ ### Plugin development loop
965
+
879
966
  ```bash
967
+ # 1. Start a LangBot instance (which spawns or connects to a Runtime)
968
+ cd /path/to/LangBot
969
+ uv run main.py
970
+
971
+ # 2. In your plugin dir, attach it to the Runtime's debug port for live debugging
880
972
  cd /path/to/your-plugin
881
973
  lbp run
882
974
  ```
883
975
 
884
- Plugin connects to LangBot via stdio for testing.
885
-
886
- ### Debugging Tips
976
+ ### Debugging tips
887
977
 
888
978
  - Use `print()` statements (output to stderr for visibility)
889
979
  - Check LangBot logs for plugin errors
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langbot-plugin
3
- Version: 0.4.1
3
+ Version: 0.4.2
4
4
  Summary: This package contains the SDK, CLI for building plugins for LangBot, plus the runtime for hosting LangBot plugins
5
5
  Project-URL: Homepage, https://langbot.app
6
6
  Project-URL: Repository, https://github.com/langbot-app/langbot-plugin-sdk
@@ -14,6 +14,7 @@ Requires-Dist: dotenv>=0.9.9
14
14
  Requires-Dist: e2b>=2.15
15
15
  Requires-Dist: httpx>=0.28.1
16
16
  Requires-Dist: jinja2>=3.1.6
17
+ Requires-Dist: packaging>=24.0
17
18
  Requires-Dist: pip>=25.2
18
19
  Requires-Dist: pydantic-settings>=2.10.1
19
20
  Requires-Dist: pydantic>=2.11.5
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "langbot-plugin"
3
- version = "0.4.1"
3
+ version = "0.4.2"
4
4
  description = "This package contains the SDK, CLI for building plugins for LangBot, plus the runtime for hosting LangBot plugins"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -14,6 +14,7 @@ dependencies = [
14
14
  "e2b>=2.15",
15
15
  "httpx>=0.28.1",
16
16
  "jinja2>=3.1.6",
17
+ "packaging>=24.0",
17
18
  "pip>=25.2",
18
19
  "pydantic>=2.11.5",
19
20
  "pydantic-settings>=2.10.1",
@@ -0,0 +1,77 @@
1
+ {% raw %}name: Release Plugin
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ - master
8
+ paths:
9
+ - "manifest.yaml"
10
+ workflow_dispatch:
11
+ inputs:
12
+ release:
13
+ description: "Create a new release"
14
+ required: true
15
+ type: boolean
16
+ default: true
17
+
18
+ permissions:
19
+ contents: write
20
+
21
+ jobs:
22
+ release:
23
+ runs-on: ubuntu-latest
24
+ steps:
25
+ - name: Checkout
26
+ uses: actions/checkout@v4
27
+
28
+ - name: Setup Python
29
+ uses: actions/setup-python@v5
30
+ with:
31
+ python-version: "3.12"
32
+
33
+ - name: Install lbp CLI
34
+ run: pip install langbot-plugin
35
+
36
+ - name: Get metadata
37
+ id: meta
38
+ run: |
39
+ PLUGIN_NAME=$(grep -m1 'name:' manifest.yaml | awk '{print $2}' | tr -d '"'"'"'')
40
+ VERSION=$(grep -m1 'version:' manifest.yaml | awk '{print $2}' | tr -d '"'"'"'')
41
+ AUTHOR=$(grep -m1 'author:' manifest.yaml | awk '{print $2}' | tr -d '"'"'"'')
42
+ echo "name=$PLUGIN_NAME" >> "$GITHUB_OUTPUT"
43
+ echo "version=$VERSION" >> "$GITHUB_OUTPUT"
44
+ echo "tag=v$VERSION" >> "$GITHUB_OUTPUT"
45
+ echo "artifact=${AUTHOR}-${PLUGIN_NAME}-${VERSION}" >> "$GITHUB_OUTPUT"
46
+
47
+ - name: Check release exists
48
+ id: check
49
+ run: |
50
+ if gh release view "$TAG" --json tagName > /dev/null 2>&1; then
51
+ echo "exists=true" >> "$GITHUB_OUTPUT"
52
+ else
53
+ echo "exists=false" >> "$GITHUB_OUTPUT"
54
+ fi
55
+ env:
56
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
57
+ TAG: ${{ steps.meta.outputs.tag }}
58
+
59
+ - name: Build plugin
60
+ if: steps.check.outputs.exists == 'false'
61
+ run: lbp build -o dist
62
+
63
+ - name: Create release
64
+ if: steps.check.outputs.exists == 'false'
65
+ uses: softprops/action-gh-release@v2
66
+ with:
67
+ tag_name: ${{ steps.meta.outputs.tag }}
68
+ name: ${{ steps.meta.outputs.artifact }}
69
+ body: |
70
+ ## ${{ steps.meta.outputs.artifact }}
71
+
72
+ ### Installation
73
+ Download `${{ steps.meta.outputs.artifact }}.lbpkg` and install via LangBot plugin marketplace.
74
+ files: dist/${{ steps.meta.outputs.artifact }}.lbpkg
75
+ draft: false
76
+ prerelease: false
77
+ {% endraw %}
@@ -0,0 +1,15 @@
1
+ # {{ plugin_name }}
2
+
3
+ {{ plugin_description }}
4
+
5
+ ## Installation
6
+
7
+ Install this plugin via the LangBot plugin marketplace.
8
+
9
+ ## Configuration
10
+
11
+ Configure the plugin parameters in the LangBot admin panel.
12
+
13
+ ## Usage
14
+
15
+ <!-- TODO: Add usage instructions for your plugin here -->
@@ -0,0 +1,15 @@
1
+ # {{ plugin_name }}
2
+
3
+ {{ plugin_description }}
4
+
5
+ ## 安装
6
+
7
+ 通过 LangBot 插件市场安装本插件。
8
+
9
+ ## 配置
10
+
11
+ 在 LangBot 管理面板中配置此插件的参数。
12
+
13
+ ## 使用方法
14
+
15
+ <!-- TODO: 在此添加插件的使用说明 -->
@@ -233,6 +233,12 @@ class ActionRPCBoxClient(BoxRuntimeClient):
233
233
  data = await self._call(
234
234
  LangBotToBoxAction.START_MANAGED_PROCESS,
235
235
  {"session_id": session_id, "spec": spec.model_dump(mode="json")},
236
+ # Starting a managed process can involve a cold dependency bootstrap
237
+ # (e.g. `uvx <pkg>` downloading the package + interpreter on first
238
+ # run), which easily exceeds the default 15s action timeout and
239
+ # caused stdio MCP servers to be torn down mid-install. Give it
240
+ # headroom so the first launch can complete.
241
+ timeout=30.0,
236
242
  )
237
243
  return BoxManagedProcessInfo.model_validate(data)
238
244
 
@@ -44,6 +44,23 @@ _READONLY_ETC_ENTRIES: list[str] = [
44
44
  '/etc/resolv.conf', # needed when network=ON
45
45
  ]
46
46
 
47
+ # Essential character devices bind-mounted into the sandbox's /dev.
48
+ # /dev is a fresh empty tmpfs (see _build_args), so these nodes do not exist
49
+ # unless we bind them in from the host. Tooling that shells out for probes
50
+ # relies on them — notably `uv`/`uvx` redirects its glibc/musl detection
51
+ # subprocess to /dev/null; without it uv fails with "Could not detect either
52
+ # glibc version nor musl libc version" and the process exits before it can do
53
+ # anything (e.g. an stdio MCP server dies before the initialize handshake,
54
+ # surfacing as a misleading "Connection closed / please check URL").
55
+ _DEV_NODES: list[str] = [
56
+ '/dev/null',
57
+ '/dev/zero',
58
+ '/dev/full',
59
+ '/dev/random',
60
+ '/dev/urandom',
61
+ '/dev/tty',
62
+ ]
63
+
47
64
  _DEFAULT_BASE_DIR = '/tmp/langbot-box-nsjail'
48
65
 
49
66
 
@@ -116,6 +133,19 @@ class NsjailBackend(BaseSandboxBackend):
116
133
  for d in (root_dir, workspace_dir, tmp_dir, home_dir):
117
134
  d.mkdir(parents=True, exist_ok=True)
118
135
 
136
+ # When a host_path is mounted into the sandbox it becomes the nsjail
137
+ # bind-mount source (see _build_mounts). nsjail requires the source to
138
+ # already exist on the host, otherwise the bind-mount fails and the
139
+ # command exits 255 with no stdout/stderr. The per-session loop above
140
+ # never creates host_path (it lives outside session_dir), so ensure it
141
+ # exists here. Read-only mounts intentionally are NOT auto-created: a
142
+ # missing read-only source is a caller error that should surface.
143
+ if (
144
+ spec.host_path is not None
145
+ and spec.host_path_mode == BoxHostMountMode.READ_WRITE
146
+ ):
147
+ os.makedirs(spec.host_path, exist_ok=True)
148
+
119
149
  # If host_path is specified, we will use it directly instead of the
120
150
  # per-session workspace when building nsjail args (see _build_mounts).
121
151
  meta = {
@@ -317,6 +347,15 @@ class NsjailBackend(BaseSandboxBackend):
317
347
  args.extend(['--mount', 'none:/proc:proc:rw'])
318
348
  args.extend(['--mount', 'none:/dev:tmpfs:rw'])
319
349
 
350
+ # /dev is a fresh empty tmpfs, so bind in the essential character
351
+ # devices. Without /dev/null in particular, uv's glibc/musl detection
352
+ # subprocess fails and any uvx-launched process (e.g. stdio MCP servers)
353
+ # exits before doing useful work. Mounted read-write so writes to
354
+ # /dev/null behave normally.
355
+ for dev in _DEV_NODES:
356
+ if os.path.exists(dev):
357
+ args.extend(['--bindmount', f'{dev}:{dev}'])
358
+
320
359
  # Working directory.
321
360
  args.extend(['--cwd', spec.workdir])
322
361
 
@@ -424,14 +463,24 @@ class NsjailBackend(BaseSandboxBackend):
424
463
  args: list[str] = []
425
464
 
426
465
  if self._cgroup_v2_available:
427
- # cgroup v2 – precise limits.
466
+ # cgroup v2 – precise limits. nsjail defaults to the legacy cgroup
467
+ # v1 layout, so we MUST opt into v2 explicitly; without this flag
468
+ # nsjail tries to mkdir under /sys/fs/cgroup/<controller>/... (v1
469
+ # paths) and aborts on a v2-only host. The writability of the v2
470
+ # root is already verified in _detect_cgroup_v2().
471
+ args.append('--use_cgroupv2')
428
472
  memory_bytes = spec.memory_mb * 1024 * 1024
429
473
  args.extend(['--cgroup_mem_max', str(memory_bytes)])
430
474
  args.extend(['--cgroup_pids_max', str(spec.pids_limit)])
431
475
  cpu_ms = int(spec.cpus * 1000)
432
476
  args.extend(['--cgroup_cpu_ms_per_sec', str(cpu_ms)])
433
477
  else:
434
- # rlimit fallback – best-effort.
478
+ # rlimit fallback – best-effort. Used whenever the cgroup v2 root is
479
+ # not writable (the typical containerized case: /sys/fs/cgroup is
480
+ # mounted read-only), so the sandbox still launches with coarse
481
+ # resource caps instead of failing outright.
482
+ # --rlimit_as takes a value in MB (nsjail interprets the bare number
483
+ # as megabytes), matching spec.memory_mb directly.
435
484
  args.extend(['--rlimit_as', str(spec.memory_mb)])
436
485
  args.extend(['--rlimit_nproc', str(spec.pids_limit)])
437
486
 
@@ -478,25 +527,42 @@ class NsjailBackend(BaseSandboxBackend):
478
527
 
479
528
  @staticmethod
480
529
  def _detect_cgroup_v2() -> bool:
481
- """Check whether the host runs cgroup v2 and we can write to it."""
530
+ """Check whether cgroup v2 is present AND nsjail can create a cgroup.
531
+
532
+ nsjail (with ``--use_cgroupv2``) creates its own child cgroup by
533
+ ``mkdir``-ing directly under the cgroup v2 mount root, then writes to
534
+ ``cgroup.subtree_control``. Merely detecting a cgroup v2 hierarchy is
535
+ not enough: inside most containers (Docker/k8s default) ``/sys/fs/cgroup``
536
+ is mounted **read-only**, so the mkdir fails and the whole sandbox
537
+ aborts. We must therefore probe real writability — running as root is
538
+ NOT sufficient, because a read-only bind mount denies root too.
539
+
540
+ Returns True only when we can actually create (and remove) a directory
541
+ under the cgroup v2 root, which is exactly what nsjail needs.
542
+ """
482
543
  cgroup_mount = pathlib.Path('/sys/fs/cgroup')
483
544
  if not cgroup_mount.exists():
484
545
  return False
485
- # cgroup v2 has a single hierarchy with cgroup.controllers file.
546
+ # cgroup v2 has a single hierarchy with a cgroup.controllers file.
486
547
  controllers = cgroup_mount / 'cgroup.controllers'
487
548
  if not controllers.exists():
488
549
  return False
489
- # Check if we can write to a cgroup subtree (needed for nsjail).
490
- # A rough heuristic: if the user owns a cgroup directory we're probably
491
- # running under systemd user delegation.
492
- user_slice = cgroup_mount / f'user.slice/user-{os.getuid()}.slice'
493
- if user_slice.exists() and os.access(user_slice, os.W_OK):
494
- return True
495
- # If running as root (uid 0), cgroup v2 is always usable.
496
- if os.getuid() == 0:
550
+ # Authoritative writability probe: try to create and remove a throwaway
551
+ # cgroup directory under the root, mirroring what nsjail does. This
552
+ # correctly reports False on a read-only /sys/fs/cgroup (the common
553
+ # containerized case) so the backend falls back to rlimit limits
554
+ # instead of selecting a cgroup path that is guaranteed to fail.
555
+ probe = cgroup_mount / f'.langbot-box-probe-{os.getpid()}'
556
+ try:
557
+ probe.mkdir(mode=0o700)
558
+ except Exception:
559
+ return False
560
+ else:
561
+ try:
562
+ probe.rmdir()
563
+ except Exception:
564
+ pass
497
565
  return True
498
- # Conservative: if we can't confirm writability, report unavailable.
499
- return False
500
566
 
501
567
  async def _kill_session_processes(self, session_dir: pathlib.Path) -> None:
502
568
  """Best-effort kill of nsjail processes associated with a session dir.
@@ -155,6 +155,10 @@ def init_plugin_process(
155
155
  os.makedirs(assets_dir, exist_ok=True)
156
156
  vscode_dir = os.path.join(plugin_dir, ".vscode")
157
157
  os.makedirs(vscode_dir, exist_ok=True)
158
+ readme_dir = os.path.join(plugin_dir, "readme")
159
+ os.makedirs(readme_dir, exist_ok=True)
160
+ github_workflows_dir = os.path.join(plugin_dir, ".github", "workflows")
161
+ os.makedirs(github_workflows_dir, exist_ok=True)
158
162
 
159
163
  # Create all files from templates
160
164
  for file in init_plugin_files:
@@ -42,11 +42,13 @@ init_plugin_files = [
42
42
  "manifest.yaml",
43
43
  "main.py",
44
44
  "README.md",
45
+ "readme/README_zh_Hans.md",
45
46
  "requirements.txt",
46
47
  ".env.example",
47
48
  ".gitignore",
48
49
  "assets/icon.svg",
49
50
  ".vscode/launch.json",
51
+ ".github/workflows/release.yml",
50
52
  ]
51
53
 
52
54