bamboo-coding 0.1.1__tar.gz → 0.1.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 (81) hide show
  1. bamboo_coding-0.1.2/PKG-INFO +29 -0
  2. bamboo_coding-0.1.2/README.md +18 -0
  3. bamboo_coding-0.1.2/pyproject.toml +23 -0
  4. {bamboo_coding-0.1.1/bamboo_coding/client → bamboo_coding-0.1.2/src/bamboo_coding}/capabilities/repo.py +2 -2
  5. {bamboo_coding-0.1.1/bamboo_coding/client → bamboo_coding-0.1.2/src/bamboo_coding}/config.py +79 -12
  6. {bamboo_coding-0.1.1/bamboo_coding/client → bamboo_coding-0.1.2/src/bamboo_coding}/controller_client.py +1 -4
  7. {bamboo_coding-0.1.1/bamboo_coding/client → bamboo_coding-0.1.2/src/bamboo_coding}/main.py +364 -104
  8. {bamboo_coding-0.1.1/agent/bamboo_git_agent → bamboo_coding-0.1.2/src/bamboo_coding}/status.py +1 -1
  9. {bamboo_coding-0.1.1/bamboo_coding/client → bamboo_coding-0.1.2/src/bamboo_coding}/terminal_runtime.py +1 -1
  10. bamboo_coding-0.1.2/src/bamboo_coding.egg-info/PKG-INFO +29 -0
  11. bamboo_coding-0.1.2/src/bamboo_coding.egg-info/SOURCES.txt +20 -0
  12. bamboo_coding-0.1.2/src/bamboo_coding.egg-info/entry_points.txt +2 -0
  13. bamboo_coding-0.1.2/src/bamboo_coding.egg-info/requires.txt +4 -0
  14. bamboo_coding-0.1.2/src/bamboo_coding.egg-info/top_level.txt +1 -0
  15. bamboo_coding-0.1.1/PKG-INFO +0 -284
  16. bamboo_coding-0.1.1/README.md +0 -270
  17. bamboo_coding-0.1.1/agent/__init__.py +0 -0
  18. bamboo_coding-0.1.1/agent/bamboo_git_agent/__init__.py +0 -0
  19. bamboo_coding-0.1.1/agent/bamboo_git_agent/capabilities/__init__.py +0 -0
  20. bamboo_coding-0.1.1/agent/bamboo_git_agent/capabilities/repo.py +0 -189
  21. bamboo_coding-0.1.1/agent/bamboo_git_agent/config.py +0 -76
  22. bamboo_coding-0.1.1/agent/bamboo_git_agent/controller_client.py +0 -73
  23. bamboo_coding-0.1.1/agent/bamboo_git_agent/main.py +0 -164
  24. bamboo_coding-0.1.1/agent/bamboo_git_agent/protocol.py +0 -21
  25. bamboo_coding-0.1.1/api/__init__.py +0 -2
  26. bamboo_coding-0.1.1/api/commit.py +0 -82
  27. bamboo_coding-0.1.1/api/repository.py +0 -592
  28. bamboo_coding-0.1.1/bamboo_coding/__init__.py +0 -3
  29. bamboo_coding-0.1.1/bamboo_coding/client/journal.py +0 -1
  30. bamboo_coding-0.1.1/bamboo_coding/client/status.py +0 -59
  31. bamboo_coding-0.1.1/bamboo_coding/server/__init__.py +0 -3
  32. bamboo_coding-0.1.1/bamboo_coding/server/core/__init__.py +0 -1
  33. bamboo_coding-0.1.1/bamboo_coding/server/core/config.py +0 -1
  34. bamboo_coding-0.1.1/bamboo_coding/server/main.py +0 -39
  35. bamboo_coding-0.1.1/bamboo_coding/shared/__init__.py +0 -39
  36. bamboo_coding-0.1.1/bamboo_coding/shared/protocol.py +0 -1
  37. bamboo_coding-0.1.1/bamboo_coding.egg-info/PKG-INFO +0 -284
  38. bamboo_coding-0.1.1/bamboo_coding.egg-info/SOURCES.txt +0 -71
  39. bamboo_coding-0.1.1/bamboo_coding.egg-info/entry_points.txt +0 -3
  40. bamboo_coding-0.1.1/bamboo_coding.egg-info/requires.txt +0 -7
  41. bamboo_coding-0.1.1/bamboo_coding.egg-info/top_level.txt +0 -6
  42. bamboo_coding-0.1.1/controller/__init__.py +0 -0
  43. bamboo_coding-0.1.1/controller/app/__init__.py +0 -0
  44. bamboo_coding-0.1.1/controller/app/api/__init__.py +0 -0
  45. bamboo_coding-0.1.1/controller/app/api/agents/__init__.py +0 -3
  46. bamboo_coding-0.1.1/controller/app/api/agents/ws.py +0 -139
  47. bamboo_coding-0.1.1/controller/app/api/public/__init__.py +0 -13
  48. bamboo_coding-0.1.1/controller/app/api/public/repos.py +0 -228
  49. bamboo_coding-0.1.1/controller/app/api/public/terminals.py +0 -82
  50. bamboo_coding-0.1.1/controller/app/core/__init__.py +0 -0
  51. bamboo_coding-0.1.1/controller/app/core/config.py +0 -20
  52. bamboo_coding-0.1.1/controller/app/core/errors.py +0 -14
  53. bamboo_coding-0.1.1/controller/app/db/__init__.py +0 -0
  54. bamboo_coding-0.1.1/controller/app/db/models.py +0 -42
  55. bamboo_coding-0.1.1/controller/app/db/session.py +0 -74
  56. bamboo_coding-0.1.1/controller/app/main.py +0 -80
  57. bamboo_coding-0.1.1/controller/app/schemas/__init__.py +0 -23
  58. bamboo_coding-0.1.1/controller/app/schemas/agent_messages.py +0 -21
  59. bamboo_coding-0.1.1/controller/app/schemas/public.py +0 -3
  60. bamboo_coding-0.1.1/controller/app/services/__init__.py +0 -0
  61. bamboo_coding-0.1.1/controller/app/services/agents.py +0 -108
  62. bamboo_coding-0.1.1/controller/app/services/registrations.py +0 -17
  63. bamboo_coding-0.1.1/controller/app/services/repositories.py +0 -85
  64. bamboo_coding-0.1.1/controller/app/services/router.py +0 -140
  65. bamboo_coding-0.1.1/controller/app/services/tasks.py +0 -104
  66. bamboo_coding-0.1.1/controller/app/services/terminals.py +0 -331
  67. bamboo_coding-0.1.1/pyproject.toml +0 -29
  68. bamboo_coding-0.1.1/shared/__init__.py +0 -39
  69. bamboo_coding-0.1.1/tests/test_api.py +0 -140
  70. bamboo_coding-0.1.1/tests/test_container_contract.py +0 -7
  71. bamboo_coding-0.1.1/tests/test_frontend_contract.py +0 -3275
  72. bamboo_coding-0.1.1/tests/test_git_utils.py +0 -286
  73. bamboo_coding-0.1.1/tests/test_makefile.py +0 -62
  74. {bamboo_coding-0.1.1 → bamboo_coding-0.1.2}/setup.cfg +0 -0
  75. {bamboo_coding-0.1.1/bamboo_coding/client → bamboo_coding-0.1.2/src/bamboo_coding}/__init__.py +0 -0
  76. {bamboo_coding-0.1.1/bamboo_coding/client → bamboo_coding-0.1.2/src/bamboo_coding}/capabilities/__init__.py +0 -0
  77. {bamboo_coding-0.1.1 → bamboo_coding-0.1.2/src/bamboo_coding}/git_utils.py +0 -0
  78. {bamboo_coding-0.1.1/agent/bamboo_git_agent → bamboo_coding-0.1.2/src/bamboo_coding}/journal.py +0 -0
  79. {bamboo_coding-0.1.1/shared → bamboo_coding-0.1.2/src/bamboo_coding}/protocol.py +0 -0
  80. {bamboo_coding-0.1.1/bamboo_coding/client → bamboo_coding-0.1.2/src/bamboo_coding}/scrollback_buffer.py +0 -0
  81. {bamboo_coding-0.1.1 → bamboo_coding-0.1.2/src}/bamboo_coding.egg-info/dependency_links.txt +0 -0
@@ -0,0 +1,29 @@
1
+ Metadata-Version: 2.4
2
+ Name: bamboo-coding
3
+ Version: 0.1.2
4
+ Summary: Bamboo Coding outbound client
5
+ Requires-Python: >=3.12
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: gitpython==3.1.40
8
+ Requires-Dist: websockets==13.1
9
+ Requires-Dist: pydantic>=2.0
10
+ Requires-Dist: bamboo-ssh
11
+
12
+ # bamboo-coding
13
+
14
+ Outbound client for the Bamboo Coding controller.
15
+
16
+ ## Install
17
+
18
+ ```bash
19
+ pip install bamboo-coding
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ ```bash
25
+ bamboo-coding setup # configure default profile
26
+ bamboo-coding run # connect to the controller
27
+ ```
28
+
29
+ The client reads its configuration from `~/.bamboo-coding/bamboo-coding.toml`.
@@ -0,0 +1,18 @@
1
+ # bamboo-coding
2
+
3
+ Outbound client for the Bamboo Coding controller.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pip install bamboo-coding
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```bash
14
+ bamboo-coding setup # configure default profile
15
+ bamboo-coding run # connect to the controller
16
+ ```
17
+
18
+ The client reads its configuration from `~/.bamboo-coding/bamboo-coding.toml`.
@@ -0,0 +1,23 @@
1
+ [build-system]
2
+ requires = ["setuptools>=69", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "bamboo-coding"
7
+ version = "0.1.2"
8
+ description = "Bamboo Coding outbound client"
9
+ readme = "README.md"
10
+ requires-python = ">=3.12"
11
+ dependencies = [
12
+ "gitpython==3.1.40",
13
+ "websockets==13.1",
14
+ "pydantic>=2.0",
15
+ "bamboo-ssh",
16
+ ]
17
+
18
+ [project.scripts]
19
+ bamboo-coding = "bamboo_coding.main:main"
20
+
21
+ [tool.setuptools.packages.find]
22
+ where = ["src"]
23
+ include = ["bamboo_coding*"]
@@ -3,8 +3,8 @@ from __future__ import annotations
3
3
  from pathlib import Path
4
4
  from typing import Callable
5
5
 
6
- import git_utils
7
- from bamboo_coding.shared.protocol import TaskMessage
6
+ from .. import git_utils
7
+ from ..protocol import TaskMessage
8
8
 
9
9
 
10
10
  def _allowed_roots(repositories: dict[str, str] | list[str] | tuple[str, ...]) -> list[str]:
@@ -13,6 +13,8 @@ DEFAULT_CONFIG_DIRNAME = ".bamboo-coding"
13
13
  DEFAULT_CONFIG_FILENAME = "bamboo-coding.toml"
14
14
  DEFAULT_CONTROLLER_URL = "ws://127.0.0.1:8100/ws/agents"
15
15
  DEFAULT_CLIENT_ID = "bamboo-coding-client"
16
+ DEFAULT_PROFILE = "default"
17
+ SERVERS_DIRNAME = "servers"
16
18
 
17
19
 
18
20
  @dataclass(frozen=True)
@@ -32,6 +34,10 @@ class AgentConfig:
32
34
  token_path: Path
33
35
  journal_path: Path
34
36
  config_path: Path = field(default_factory=Path)
37
+ profile: str = DEFAULT_PROFILE
38
+ profile_exists: bool = True
39
+ legacy_client_section: bool = False
40
+ available_profiles: tuple[str, ...] = field(default_factory=tuple)
35
41
  max_concurrent_tasks: int = 1
36
42
  allowed_roots: tuple[str, ...] = field(default_factory=tuple)
37
43
  repositories: tuple[RepositoryConfig, ...] = field(default_factory=tuple)
@@ -107,7 +113,39 @@ def _load_legacy_token(default_token_path: Path) -> str:
107
113
  return token
108
114
 
109
115
 
110
- def load_config() -> AgentConfig:
116
+ def _resolve_profile_section(raw_config: dict, profile: str) -> tuple[dict | None, bool]:
117
+ """Return (profile_dict, is_legacy_client_section).
118
+
119
+ profile_dict is None if no matching profile was found.
120
+ """
121
+ servers = raw_config.get("servers")
122
+ if isinstance(servers, dict):
123
+ candidate = servers.get(profile)
124
+ if isinstance(candidate, dict):
125
+ return candidate, False
126
+ if profile == DEFAULT_PROFILE:
127
+ legacy = raw_config.get("client")
128
+ if isinstance(legacy, dict):
129
+ return legacy, True
130
+ return None, False
131
+
132
+
133
+ def _list_available_profiles(raw_config: dict) -> tuple[str, ...]:
134
+ names: list[str] = []
135
+ servers = raw_config.get("servers")
136
+ if isinstance(servers, dict):
137
+ names.extend(name for name in servers if isinstance(servers.get(name), dict))
138
+ if DEFAULT_PROFILE not in names and isinstance(raw_config.get("client"), dict):
139
+ names.insert(0, DEFAULT_PROFILE)
140
+ return tuple(names)
141
+
142
+
143
+ def _profile_storage_dir(storage_home: Path, profile: str) -> Path:
144
+ return storage_home / SERVERS_DIRNAME / profile
145
+
146
+
147
+ def load_config(profile: str | None = None) -> AgentConfig:
148
+ active_profile = profile or DEFAULT_PROFILE
111
149
  config_home = Path(_first_env("BAMBOO_CODING_HOME") or (Path.home() / DEFAULT_CONFIG_DIRNAME)).expanduser().resolve()
112
150
  config_path = _resolve_path(
113
151
  _first_env("BAMBOO_CODING_CONFIG_PATH"),
@@ -115,45 +153,70 @@ def load_config() -> AgentConfig:
115
153
  base_dir=config_home,
116
154
  )
117
155
  raw_config = _load_toml(config_path)
118
- client_config = raw_config.get("client") if isinstance(raw_config.get("client"), dict) else {}
119
- storage_config = raw_config.get("storage") if isinstance(raw_config.get("storage"), dict) else {}
156
+ profile_section, is_legacy_client = _resolve_profile_section(raw_config, active_profile)
157
+ available_profiles = _list_available_profiles(raw_config)
158
+ profile_exists = profile_section is not None
159
+ section = profile_section if isinstance(profile_section, dict) else {}
160
+
161
+ explicit_new_layout = profile_exists and not is_legacy_client
162
+ storage_config = (
163
+ raw_config.get("storage")
164
+ if not explicit_new_layout and isinstance(raw_config.get("storage"), dict)
165
+ else {}
166
+ )
120
167
 
121
168
  storage_home_value = _first_env("BAMBOO_CODING_HOME", "AGENT_HOME")
122
169
  storage_home = Path(storage_home_value).expanduser().resolve() if storage_home_value else config_home
123
170
 
171
+ if explicit_new_layout:
172
+ profile_dir = _profile_storage_dir(storage_home, active_profile)
173
+ default_token_path = profile_dir / "token.json"
174
+ default_journal_path = profile_dir / "journal.db"
175
+ elif active_profile == DEFAULT_PROFILE:
176
+ default_token_path = storage_home / "token.json"
177
+ default_journal_path = storage_home / "journal.db"
178
+ else:
179
+ profile_dir = _profile_storage_dir(storage_home, active_profile)
180
+ default_token_path = profile_dir / "token.json"
181
+ default_journal_path = profile_dir / "journal.db"
182
+
124
183
  controller_url = _first_env("BAMBOO_CODING_CONTROLLER_URL", "CONTROLLER_URL") or str(
125
- client_config.get("controller_url") or DEFAULT_CONTROLLER_URL
184
+ section.get("controller_url") or DEFAULT_CONTROLLER_URL
126
185
  )
127
186
  client_id = _first_env("BAMBOO_CODING_CLIENT_ID", "AGENT_CLIENT_ID") or str(
128
- client_config.get("client_id") or platform.node() or DEFAULT_CLIENT_ID
187
+ section.get("client_id") or platform.node() or DEFAULT_CLIENT_ID
129
188
  )
130
189
  hostname = _first_env("BAMBOO_CODING_HOSTNAME", "AGENT_HOSTNAME") or str(
131
- client_config.get("hostname") or platform.node() or client_id
190
+ section.get("hostname") or platform.node() or client_id
132
191
  )
133
- version = _first_env("BAMBOO_CODING_VERSION", "AGENT_VERSION") or str(client_config.get("version") or _default_version())
192
+ version = _first_env("BAMBOO_CODING_VERSION", "AGENT_VERSION") or str(section.get("version") or _default_version())
134
193
  allowed_roots = _coerce_allowed_roots(
135
194
  _first_env("BAMBOO_CODING_ALLOWED_ROOTS", "AGENT_ALLOWED_ROOTS", "AGENT_ROOT", "AGENT_REPO_PATH")
136
- or client_config.get("allowed_roots")
195
+ or section.get("allowed_roots")
137
196
  )
138
197
  max_concurrent_tasks = int(
139
198
  _first_env("BAMBOO_CODING_MAX_CONCURRENT_TASKS", "AGENT_MAX_CONCURRENT_TASKS")
140
- or client_config.get("max_concurrent_tasks")
199
+ or section.get("max_concurrent_tasks")
141
200
  or 1
142
201
  )
143
202
 
144
203
  token_path = _resolve_path(
145
204
  _first_env("BAMBOO_CODING_TOKEN_PATH", "AGENT_TOKEN_PATH") or storage_config.get("token_path"),
146
- default=storage_home / "token.json",
205
+ default=default_token_path,
147
206
  base_dir=config_path.parent,
148
207
  )
149
208
  journal_path = _resolve_path(
150
209
  _first_env("BAMBOO_CODING_JOURNAL_PATH", "AGENT_JOURNAL_PATH") or storage_config.get("journal_path"),
151
- default=storage_home / "journal.db",
210
+ default=default_journal_path,
152
211
  base_dir=config_path.parent,
153
212
  )
154
213
 
155
214
  token = _first_env("BAMBOO_CODING_TOKEN", "AGENT_TOKEN") or _load_stored_token(token_path)
156
- if not token and not _first_env("BAMBOO_CODING_TOKEN_PATH", "AGENT_TOKEN_PATH", "AGENT_HOME"):
215
+ if (
216
+ not token
217
+ and active_profile == DEFAULT_PROFILE
218
+ and not _first_env("BAMBOO_CODING_TOKEN_PATH", "AGENT_TOKEN_PATH", "AGENT_HOME")
219
+ ):
157
220
  token = _load_legacy_token(token_path)
158
221
 
159
222
  repositories = tuple(
@@ -173,6 +236,10 @@ def load_config() -> AgentConfig:
173
236
  token_path=token_path,
174
237
  journal_path=journal_path,
175
238
  config_path=config_path,
239
+ profile=active_profile,
240
+ profile_exists=profile_exists,
241
+ legacy_client_section=is_legacy_client,
242
+ available_profiles=available_profiles,
176
243
  max_concurrent_tasks=max_concurrent_tasks,
177
244
  allowed_roots=allowed_roots,
178
245
  repositories=repositories,
@@ -11,7 +11,7 @@ from websockets.exceptions import ConnectionClosed
11
11
 
12
12
  LOGGER = logging.getLogger(__name__)
13
13
 
14
- from bamboo_coding.shared.protocol import (
14
+ from .protocol import (
15
15
  AgentRegisterMessage,
16
16
  ClientStatusMessage,
17
17
  TaskEventMessage,
@@ -131,9 +131,6 @@ class ControllerConnection:
131
131
  try:
132
132
  await self.websocket.send(json.dumps(payload))
133
133
  except ConnectionClosed:
134
- # Controller is gone; the run loop will reconnect. Drop the
135
- # outbound frame so cleanup paths (heartbeats, terminal_closed)
136
- # don't crash the runtime during server restarts.
137
134
  LOGGER.debug('skipping outbound message; controller connection is closed')
138
135
 
139
136
  async def _receive(self) -> dict: