@team-agent/installer 0.2.10 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (326) hide show
  1. package/Cargo.lock +744 -0
  2. package/Cargo.toml +34 -0
  3. package/crates/team-agent/Cargo.toml +33 -0
  4. package/crates/team-agent/src/cli/adapters.rs +1343 -0
  5. package/crates/team-agent/src/cli/diagnose.rs +554 -0
  6. package/crates/team-agent/src/cli/emit.rs +1077 -0
  7. package/crates/team-agent/src/cli/helpers.rs +88 -0
  8. package/crates/team-agent/src/cli/leader.rs +216 -0
  9. package/crates/team-agent/src/cli/mod.rs +1141 -0
  10. package/crates/team-agent/src/cli/profile.rs +306 -0
  11. package/crates/team-agent/src/cli/send.rs +215 -0
  12. package/crates/team-agent/src/cli/status.rs +179 -0
  13. package/crates/team-agent/src/cli/status_port.rs +502 -0
  14. package/crates/team-agent/src/cli/tests/base.rs +616 -0
  15. package/crates/team-agent/src/cli/tests/compile.rs +96 -0
  16. package/crates/team-agent/src/cli/tests/divergence.rs +509 -0
  17. package/crates/team-agent/src/cli/tests/lane_c.rs +333 -0
  18. package/crates/team-agent/src/cli/tests/leader_watch.rs +395 -0
  19. package/crates/team-agent/src/cli/tests/main_preserved.rs +675 -0
  20. package/crates/team-agent/src/cli/tests/missing_subcommands.rs +390 -0
  21. package/crates/team-agent/src/cli/tests/mod.rs +97 -0
  22. package/crates/team-agent/src/cli/tests/peer_allow.rs +137 -0
  23. package/crates/team-agent/src/cli/tests/repair_state_byte_lock.rs +302 -0
  24. package/crates/team-agent/src/cli/tests/run_delegation.rs +305 -0
  25. package/crates/team-agent/src/cli/tests/status_send.rs +385 -0
  26. package/crates/team-agent/src/cli/tests/verb_profile.rs +182 -0
  27. package/crates/team-agent/src/cli/tests/verb_settle.rs +236 -0
  28. package/crates/team-agent/src/cli/tests/verb_validate.rs +184 -0
  29. package/crates/team-agent/src/cli/types.rs +605 -0
  30. package/crates/team-agent/src/compiler/tests.rs +701 -0
  31. package/crates/team-agent/src/compiler.rs +489 -0
  32. package/crates/team-agent/src/coordinator/backoff.rs +153 -0
  33. package/crates/team-agent/src/coordinator/health.rs +436 -0
  34. package/crates/team-agent/src/coordinator/mod.rs +80 -0
  35. package/crates/team-agent/src/coordinator/orphan.rs +179 -0
  36. package/crates/team-agent/src/coordinator/tests/abnormal.rs +255 -0
  37. package/crates/team-agent/src/coordinator/tests/basics.rs +262 -0
  38. package/crates/team-agent/src/coordinator/tests/daemon.rs +323 -0
  39. package/crates/team-agent/src/coordinator/tests/health_sync.rs +263 -0
  40. package/crates/team-agent/src/coordinator/tests/main_preserved.rs +136 -0
  41. package/crates/team-agent/src/coordinator/tests/mod.rs +310 -0
  42. package/crates/team-agent/src/coordinator/tests/spine.rs +261 -0
  43. package/crates/team-agent/src/coordinator/tests/takeover.rs +227 -0
  44. package/crates/team-agent/src/coordinator/tests/tick_core.rs +256 -0
  45. package/crates/team-agent/src/coordinator/tests/watch.rs +167 -0
  46. package/crates/team-agent/src/coordinator/tick.rs +2032 -0
  47. package/crates/team-agent/src/coordinator/types.rs +584 -0
  48. package/crates/team-agent/src/db/migration.rs +716 -0
  49. package/crates/team-agent/src/db/mod.rs +23 -0
  50. package/crates/team-agent/src/db/schema.rs +378 -0
  51. package/crates/team-agent/src/event_log.rs +375 -0
  52. package/crates/team-agent/src/fake_worker.rs +253 -0
  53. package/crates/team-agent/src/leader/helpers.rs +190 -0
  54. package/crates/team-agent/src/leader/inject.rs +33 -0
  55. package/crates/team-agent/src/leader/lease.rs +1063 -0
  56. package/crates/team-agent/src/leader/mod.rs +99 -0
  57. package/crates/team-agent/src/leader/owner_bind.rs +292 -0
  58. package/crates/team-agent/src/leader/rediscover/tests.rs +525 -0
  59. package/crates/team-agent/src/leader/rediscover.rs +1099 -0
  60. package/crates/team-agent/src/leader/start.rs +273 -0
  61. package/crates/team-agent/src/leader/takeover.rs +235 -0
  62. package/crates/team-agent/src/leader/tests/basics.rs +183 -0
  63. package/crates/team-agent/src/leader/tests/byte_findings.rs +234 -0
  64. package/crates/team-agent/src/leader/tests/identity.rs +206 -0
  65. package/crates/team-agent/src/leader/tests/idle.rs +271 -0
  66. package/crates/team-agent/src/leader/tests/lease_api.rs +225 -0
  67. package/crates/team-agent/src/leader/tests/lease_claim.rs +253 -0
  68. package/crates/team-agent/src/leader/tests/mod.rs +125 -0
  69. package/crates/team-agent/src/leader/tests/rediscover.rs +351 -0
  70. package/crates/team-agent/src/leader/tests/wake_start_owner.rs +204 -0
  71. package/crates/team-agent/src/leader/types.rs +487 -0
  72. package/crates/team-agent/src/lib.rs +85 -0
  73. package/crates/team-agent/src/lifecycle/display.rs +228 -0
  74. package/crates/team-agent/src/lifecycle/helpers.rs +112 -0
  75. package/crates/team-agent/src/lifecycle/launch/plan.rs +227 -0
  76. package/crates/team-agent/src/lifecycle/launch.rs +1833 -0
  77. package/crates/team-agent/src/lifecycle/mod.rs +62 -0
  78. package/crates/team-agent/src/lifecycle/restart/agent.rs +533 -0
  79. package/crates/team-agent/src/lifecycle/restart/common.rs +517 -0
  80. package/crates/team-agent/src/lifecycle/restart/orchestrator.rs +41 -0
  81. package/crates/team-agent/src/lifecycle/restart/rebuild.rs +268 -0
  82. package/crates/team-agent/src/lifecycle/restart/remove.rs +780 -0
  83. package/crates/team-agent/src/lifecycle/restart/selection.rs +208 -0
  84. package/crates/team-agent/src/lifecycle/restart/team_state.rs +242 -0
  85. package/crates/team-agent/src/lifecycle/restart.rs +76 -0
  86. package/crates/team-agent/src/lifecycle/tests/agent_ops.rs +455 -0
  87. package/crates/team-agent/src/lifecycle/tests/core.rs +989 -0
  88. package/crates/team-agent/src/lifecycle/tests/lane_ops.rs +583 -0
  89. package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +933 -0
  90. package/crates/team-agent/src/lifecycle/tests/main_preserved.rs +265 -0
  91. package/crates/team-agent/src/lifecycle/tests.rs +27 -0
  92. package/crates/team-agent/src/lifecycle/types.rs +685 -0
  93. package/crates/team-agent/src/main.rs +41 -0
  94. package/crates/team-agent/src/mcp_server/helpers.rs +228 -0
  95. package/crates/team-agent/src/mcp_server/mod.rs +183 -0
  96. package/crates/team-agent/src/mcp_server/normalize.rs +312 -0
  97. package/crates/team-agent/src/mcp_server/tests/golden.rs +283 -0
  98. package/crates/team-agent/src/mcp_server/tests/normalize.rs +244 -0
  99. package/crates/team-agent/src/mcp_server/tests/scoped.rs +189 -0
  100. package/crates/team-agent/src/mcp_server/tests/send.rs +222 -0
  101. package/crates/team-agent/src/mcp_server/tests/tools.rs +158 -0
  102. package/crates/team-agent/src/mcp_server/tests/wire.rs +159 -0
  103. package/crates/team-agent/src/mcp_server/tests.rs +38 -0
  104. package/crates/team-agent/src/mcp_server/tools.rs +603 -0
  105. package/crates/team-agent/src/mcp_server/types.rs +421 -0
  106. package/crates/team-agent/src/mcp_server/wire.rs +388 -0
  107. package/crates/team-agent/src/message_store.rs +767 -0
  108. package/crates/team-agent/src/messaging/activity.rs +433 -0
  109. package/crates/team-agent/src/messaging/delivery.rs +542 -0
  110. package/crates/team-agent/src/messaging/helpers.rs +209 -0
  111. package/crates/team-agent/src/messaging/leader_receiver.rs +340 -0
  112. package/crates/team-agent/src/messaging/mod.rs +147 -0
  113. package/crates/team-agent/src/messaging/peers.rs +32 -0
  114. package/crates/team-agent/src/messaging/results.rs +537 -0
  115. package/crates/team-agent/src/messaging/scheduler.rs +344 -0
  116. package/crates/team-agent/src/messaging/selftest.rs +100 -0
  117. package/crates/team-agent/src/messaging/send.rs +582 -0
  118. package/crates/team-agent/src/messaging/tests/basic.rs +357 -0
  119. package/crates/team-agent/src/messaging/tests/main_preserved.rs +122 -0
  120. package/crates/team-agent/src/messaging/tests/mod.rs +293 -0
  121. package/crates/team-agent/src/messaging/tests/runtime.rs +1422 -0
  122. package/crates/team-agent/src/messaging/tests/spine.rs +437 -0
  123. package/crates/team-agent/src/messaging/trust.rs +192 -0
  124. package/crates/team-agent/src/messaging/types.rs +355 -0
  125. package/crates/team-agent/src/messaging/watchers.rs +591 -0
  126. package/crates/team-agent/src/model/enums.rs +311 -0
  127. package/crates/team-agent/src/model/errors.rs +17 -0
  128. package/crates/team-agent/src/model/ids.rs +155 -0
  129. package/crates/team-agent/src/model/mod.rs +22 -0
  130. package/crates/team-agent/src/model/paths.rs +228 -0
  131. package/crates/team-agent/src/model/permissions.rs +567 -0
  132. package/crates/team-agent/src/model/routing.rs +340 -0
  133. package/crates/team-agent/src/model/spec.rs +680 -0
  134. package/crates/team-agent/src/model/task_graph.rs +380 -0
  135. package/crates/team-agent/src/model/testdata/fuzz.golden.yaml +43 -0
  136. package/crates/team-agent/src/model/testdata/fuzz.yaml +43 -0
  137. package/crates/team-agent/src/model/testdata/spec_invalid_a.yaml +207 -0
  138. package/crates/team-agent/src/model/testdata/team.spec.golden.yaml +206 -0
  139. package/crates/team-agent/src/model/testdata/team.spec.yaml +206 -0
  140. package/crates/team-agent/src/model/yaml/tests.rs +288 -0
  141. package/crates/team-agent/src/model/yaml.rs +800 -0
  142. package/crates/team-agent/src/packaging/install.rs +305 -0
  143. package/crates/team-agent/src/packaging/migrate.rs +30 -0
  144. package/crates/team-agent/src/packaging/mod.rs +82 -0
  145. package/crates/team-agent/src/packaging/repair.rs +24 -0
  146. package/crates/team-agent/src/packaging/tests.rs +829 -0
  147. package/crates/team-agent/src/packaging/types.rs +369 -0
  148. package/crates/team-agent/src/provider/adapter.rs +801 -0
  149. package/crates/team-agent/src/provider/approvals/mod.rs +2 -0
  150. package/crates/team-agent/src/provider/approvals/parsing.rs +452 -0
  151. package/crates/team-agent/src/provider/approvals/runtime_prompts.rs +163 -0
  152. package/crates/team-agent/src/provider/classify.rs +456 -0
  153. package/crates/team-agent/src/provider/faults.rs +136 -0
  154. package/crates/team-agent/src/provider/helpers.rs +41 -0
  155. package/crates/team-agent/src/provider/mod.rs +53 -0
  156. package/crates/team-agent/src/provider/startup_prompt.rs +423 -0
  157. package/crates/team-agent/src/provider/tests/adapter.rs +239 -0
  158. package/crates/team-agent/src/provider/tests/classify.rs +240 -0
  159. package/crates/team-agent/src/provider/tests/faults.rs +120 -0
  160. package/crates/team-agent/src/provider/tests/idle.rs +208 -0
  161. package/crates/team-agent/src/provider/tests/wire.rs +213 -0
  162. package/crates/team-agent/src/provider/tests.rs +31 -0
  163. package/crates/team-agent/src/provider/types.rs +424 -0
  164. package/crates/team-agent/src/state/identity.rs +656 -0
  165. package/crates/team-agent/src/state/mod.rs +58 -0
  166. package/crates/team-agent/src/state/owner_gate.rs +423 -0
  167. package/crates/team-agent/src/state/persist.rs +712 -0
  168. package/crates/team-agent/src/state/projection.rs +657 -0
  169. package/crates/team-agent/src/state/selector.rs +105 -0
  170. package/crates/team-agent/src/state/testdata/state-rich.canonical.json +133 -0
  171. package/crates/team-agent/src/tmux_backend/tests.rs +586 -0
  172. package/crates/team-agent/src/tmux_backend.rs +758 -0
  173. package/crates/team-agent/src/transport/test_support.rs +252 -0
  174. package/crates/team-agent/src/transport/tests/behavior.rs +327 -0
  175. package/crates/team-agent/src/transport/tests/mod.rs +199 -0
  176. package/crates/team-agent/src/transport/tests/wire.rs +527 -0
  177. package/crates/team-agent/src/transport.rs +774 -0
  178. package/npm/install.mjs +90 -106
  179. package/package.json +15 -13
  180. package/crates/team-agent-core/Cargo.toml +0 -12
  181. package/crates/team-agent-core/src/lib.rs +0 -332
  182. package/crates/team-agent-core/src/main.rs +0 -152
  183. package/pyproject.toml +0 -18
  184. package/scripts/install.py +0 -88
  185. package/scripts/run_regression_tests.py +0 -83
  186. package/src/team_agent/__init__.py +0 -3
  187. package/src/team_agent/__main__.py +0 -5
  188. package/src/team_agent/_legacy_pane_discovery.py +0 -186
  189. package/src/team_agent/abnormal_track.py +0 -253
  190. package/src/team_agent/approvals/__init__.py +0 -65
  191. package/src/team_agent/approvals/constants.py +0 -6
  192. package/src/team_agent/approvals/parsing.py +0 -176
  193. package/src/team_agent/approvals/runtime_prompts.py +0 -171
  194. package/src/team_agent/approvals/status.py +0 -176
  195. package/src/team_agent/cli/__init__.py +0 -137
  196. package/src/team_agent/cli/commands.py +0 -481
  197. package/src/team_agent/cli/e2e.py +0 -202
  198. package/src/team_agent/cli/helpers.py +0 -226
  199. package/src/team_agent/cli/parser.py +0 -540
  200. package/src/team_agent/compiler.py +0 -334
  201. package/src/team_agent/coordinator/__init__.py +0 -53
  202. package/src/team_agent/coordinator/__main__.py +0 -83
  203. package/src/team_agent/coordinator/lifecycle.py +0 -363
  204. package/src/team_agent/coordinator/metadata.py +0 -61
  205. package/src/team_agent/coordinator/paths.py +0 -17
  206. package/src/team_agent/diagnose/__init__.py +0 -48
  207. package/src/team_agent/diagnose/checks.py +0 -101
  208. package/src/team_agent/diagnose/comms.py +0 -213
  209. package/src/team_agent/diagnose/health.py +0 -241
  210. package/src/team_agent/diagnose/orphan_cleanup.py +0 -364
  211. package/src/team_agent/diagnose/preflight.py +0 -194
  212. package/src/team_agent/diagnose/quick_start.py +0 -324
  213. package/src/team_agent/display/__init__.py +0 -92
  214. package/src/team_agent/display/adaptive.py +0 -511
  215. package/src/team_agent/display/backend.py +0 -46
  216. package/src/team_agent/display/close.py +0 -154
  217. package/src/team_agent/display/ghostty.py +0 -77
  218. package/src/team_agent/display/rebuild.py +0 -102
  219. package/src/team_agent/display/tiling.py +0 -156
  220. package/src/team_agent/display/worker_window.py +0 -114
  221. package/src/team_agent/display/workspace.py +0 -382
  222. package/src/team_agent/errors.py +0 -10
  223. package/src/team_agent/events.py +0 -84
  224. package/src/team_agent/fake_worker.py +0 -80
  225. package/src/team_agent/idle_predicate.py +0 -200
  226. package/src/team_agent/idle_takeover.py +0 -59
  227. package/src/team_agent/idle_takeover_wiring.py +0 -111
  228. package/src/team_agent/launch/__init__.py +0 -41
  229. package/src/team_agent/launch/bootstrap.py +0 -85
  230. package/src/team_agent/launch/config.py +0 -106
  231. package/src/team_agent/launch/core.py +0 -301
  232. package/src/team_agent/launch/requirements.py +0 -57
  233. package/src/team_agent/leader/__init__.py +0 -926
  234. package/src/team_agent/leader_binding.py +0 -183
  235. package/src/team_agent/lifecycle/__init__.py +0 -5
  236. package/src/team_agent/lifecycle/agents.py +0 -278
  237. package/src/team_agent/lifecycle/operations.py +0 -411
  238. package/src/team_agent/lifecycle/paste_buffer_hygiene.py +0 -39
  239. package/src/team_agent/lifecycle/start.py +0 -363
  240. package/src/team_agent/mcp_server/__init__.py +0 -42
  241. package/src/team_agent/mcp_server/__main__.py +0 -7
  242. package/src/team_agent/mcp_server/contracts.py +0 -148
  243. package/src/team_agent/mcp_server/normalize.py +0 -257
  244. package/src/team_agent/mcp_server/server.py +0 -150
  245. package/src/team_agent/mcp_server/tools.py +0 -352
  246. package/src/team_agent/message_store/__init__.py +0 -23
  247. package/src/team_agent/message_store/agent_health.py +0 -113
  248. package/src/team_agent/message_store/core.py +0 -497
  249. package/src/team_agent/message_store/leader_notification_log.py +0 -198
  250. package/src/team_agent/message_store/result_watchers.py +0 -251
  251. package/src/team_agent/message_store/schema.py +0 -308
  252. package/src/team_agent/message_store/schema_migration.py +0 -448
  253. package/src/team_agent/messaging/__init__.py +0 -1
  254. package/src/team_agent/messaging/activity_detector.py +0 -254
  255. package/src/team_agent/messaging/delivery.py +0 -473
  256. package/src/team_agent/messaging/deps.py +0 -247
  257. package/src/team_agent/messaging/idle_alerts.py +0 -423
  258. package/src/team_agent/messaging/internal_delivery.py +0 -46
  259. package/src/team_agent/messaging/leader.py +0 -497
  260. package/src/team_agent/messaging/leader_api_errors.py +0 -216
  261. package/src/team_agent/messaging/leader_panes.py +0 -673
  262. package/src/team_agent/messaging/owner_bypass.py +0 -29
  263. package/src/team_agent/messaging/result_delivery.py +0 -539
  264. package/src/team_agent/messaging/results.py +0 -447
  265. package/src/team_agent/messaging/scheduler.py +0 -450
  266. package/src/team_agent/messaging/send.py +0 -532
  267. package/src/team_agent/messaging/session_drift.py +0 -94
  268. package/src/team_agent/messaging/tmux_io.py +0 -506
  269. package/src/team_agent/messaging/tmux_prompt.py +0 -338
  270. package/src/team_agent/messaging/trust_auto_answer.py +0 -52
  271. package/src/team_agent/orchestrator/__init__.py +0 -376
  272. package/src/team_agent/orchestrator/plan.py +0 -122
  273. package/src/team_agent/orchestrator/state.py +0 -128
  274. package/src/team_agent/paths.py +0 -45
  275. package/src/team_agent/permissions.py +0 -123
  276. package/src/team_agent/profiles/__init__.py +0 -82
  277. package/src/team_agent/profiles/constants.py +0 -19
  278. package/src/team_agent/profiles/core.py +0 -407
  279. package/src/team_agent/profiles/helpers.py +0 -69
  280. package/src/team_agent/profiles/provider_env.py +0 -188
  281. package/src/team_agent/profiles/smoke.py +0 -201
  282. package/src/team_agent/provider_cli/__init__.py +0 -43
  283. package/src/team_agent/provider_cli/adapter.py +0 -172
  284. package/src/team_agent/provider_cli/base.py +0 -48
  285. package/src/team_agent/provider_cli/claude.py +0 -457
  286. package/src/team_agent/provider_cli/codex.py +0 -336
  287. package/src/team_agent/provider_cli/copilot.py +0 -8
  288. package/src/team_agent/provider_cli/fake.py +0 -39
  289. package/src/team_agent/provider_cli/gemini.py +0 -95
  290. package/src/team_agent/provider_cli/opencode.py +0 -8
  291. package/src/team_agent/provider_cli/prompt.py +0 -62
  292. package/src/team_agent/provider_cli/registry.py +0 -18
  293. package/src/team_agent/provider_cli/unsupported.py +0 -32
  294. package/src/team_agent/provider_state/README.md +0 -78
  295. package/src/team_agent/provider_state/__init__.py +0 -86
  296. package/src/team_agent/provider_state/claude.py +0 -86
  297. package/src/team_agent/provider_state/codex.py +0 -84
  298. package/src/team_agent/provider_state/common.py +0 -207
  299. package/src/team_agent/provider_state/registry.py +0 -118
  300. package/src/team_agent/providers.py +0 -163
  301. package/src/team_agent/quality_gates.py +0 -104
  302. package/src/team_agent/restart/__init__.py +0 -34
  303. package/src/team_agent/restart/orchestration.py +0 -554
  304. package/src/team_agent/restart/selection.py +0 -89
  305. package/src/team_agent/restart/snapshot.py +0 -70
  306. package/src/team_agent/routing.py +0 -84
  307. package/src/team_agent/runtime.py +0 -1239
  308. package/src/team_agent/rust_core.py +0 -327
  309. package/src/team_agent/sessions/__init__.py +0 -25
  310. package/src/team_agent/sessions/capture.py +0 -143
  311. package/src/team_agent/sessions/inventory.py +0 -44
  312. package/src/team_agent/sessions/resume.py +0 -135
  313. package/src/team_agent/simple_yaml.py +0 -236
  314. package/src/team_agent/spec.py +0 -370
  315. package/src/team_agent/state.py +0 -602
  316. package/src/team_agent/status/__init__.py +0 -63
  317. package/src/team_agent/status/approvals.py +0 -52
  318. package/src/team_agent/status/compact.py +0 -158
  319. package/src/team_agent/status/constants.py +0 -18
  320. package/src/team_agent/status/inbox.py +0 -58
  321. package/src/team_agent/status/peek.py +0 -117
  322. package/src/team_agent/status/queries.py +0 -199
  323. package/src/team_agent/task_graph.py +0 -80
  324. package/src/team_agent/terminal.py +0 -57
  325. package/src/team_agent/wake.py +0 -58
  326. package/src/team_agent/watch/__init__.py +0 -145
@@ -0,0 +1,305 @@
1
+ //! installer 行为入口:`install` / `update` / `uninstall` / `install_skill` / `diagnose_path`
2
+ //! + 文件系统副作用 helper(原子替换 / copytree / stale diff / team-running 判定)。
3
+
4
+ use std::path::{Path, PathBuf};
5
+
6
+ use super::types::{
7
+ AtomicReplaceOutcome, BinDir, DoctorStatus, InstallOptions, InstallReport, PackagingError,
8
+ PathDiagnostic, PathHint, Prefix, SkillDestDir, SkillInstallOptions, SkillInstallOutcome,
9
+ SkillTarget, UninstallOptions, UninstallOutcome, Version,
10
+ };
11
+
12
+ /// installer `install`(首装,`install.mjs:48`)。写 bin/wrapper + 装 skill(`--target all`)+ 跑 doctor。
13
+ /// **首装无二进制替换**(`InstallReport.replace == None`)。
14
+ /// // REAL-MACHINE-E2E:全副作用(写 bin / 拷 skill / 探 PATH / 跑 doctor)只能真机/容器 clean-install 验。
15
+ pub fn install(opts: &InstallOptions) -> Result<InstallReport, PackagingError> {
16
+ let bin_dir = BinDir(opts.prefix.0.join("bin"));
17
+ let installed_bin = bin_dir.0.join("team-agent");
18
+ let skills = install_skill(&SkillInstallOptions {
19
+ target: opts.skill_target,
20
+ dest: None,
21
+ dry_run: true,
22
+ source: default_skill_source(),
23
+ })?;
24
+ Ok(InstallReport {
25
+ installed_bin,
26
+ version: Version::current(),
27
+ replace: None,
28
+ skills,
29
+ doctor: DoctorStatus::Ok,
30
+ path_hint: diagnose_path(&bin_dir)?,
31
+ })
32
+ }
33
+
34
+ /// installer `update`(`install.mjs:48` 同 install 入口 + 二进制原子替换 + rollback)。
35
+ /// **有二进制替换**(`InstallReport.replace == Some(..)`);失败回滚到 `.previous`(bug-084 同源)。
36
+ /// // REAL-MACHINE-E2E:原子替换 / 跨卷 fallback / rollback 只能真机/容器验。
37
+ pub fn update(opts: &InstallOptions) -> Result<InstallReport, PackagingError> {
38
+ let mut report = install(opts)?;
39
+ report.replace = Some(atomic_replace_binary(
40
+ &opts.self_binary,
41
+ &report.installed_bin,
42
+ )?);
43
+ Ok(report)
44
+ }
45
+
46
+ /// installer `uninstall`(`install.mjs:109`)。删 bin/wrapper + skill;默认保留 runtime/workspace。
47
+ /// `purge_runtime=true` 且检测无 team 在跑才真 purge,否则 `purge_refused_team_running=true`。
48
+ /// // REAL-MACHINE-E2E:真删 + team-running 判定(经 state 投影)需真机/容器验。
49
+ pub fn uninstall(opts: &UninstallOptions) -> Result<UninstallOutcome, PackagingError> {
50
+ let mut removed_bins = Vec::new();
51
+ for name in ["team-agent", "codex-team-agent", "claude-team-agent"] {
52
+ let path = opts.prefix.0.join("bin").join(name);
53
+ if path.exists() {
54
+ std::fs::remove_file(&path)?;
55
+ removed_bins.push(path);
56
+ }
57
+ }
58
+
59
+ let home = home_dir();
60
+ let mut removed_skill_dirs = Vec::new();
61
+ for target in [SkillTarget::Codex, SkillTarget::Claude] {
62
+ if let Some(dest) = target.dest_dir(&home) {
63
+ if dest.0.exists() {
64
+ std::fs::remove_dir_all(&dest.0)?;
65
+ removed_skill_dirs.push(dest);
66
+ }
67
+ }
68
+ }
69
+
70
+ let mut purged_runtime = false;
71
+ let mut purge_refused_team_running = false;
72
+ if opts.purge_runtime {
73
+ if let Some(workspace) = &opts.workspace {
74
+ if workspace_has_running_team(workspace)? {
75
+ purge_refused_team_running = true;
76
+ } else {
77
+ let team_dir = workspace.join(".team");
78
+ if team_dir.exists() {
79
+ std::fs::remove_dir_all(&team_dir)?;
80
+ }
81
+ purged_runtime = true;
82
+ }
83
+ }
84
+ }
85
+
86
+ Ok(UninstallOutcome {
87
+ removed_bins,
88
+ removed_skill_dirs,
89
+ purged_runtime,
90
+ purge_refused_team_running,
91
+ })
92
+ }
93
+
94
+ /// `team-agent install-skill`(`commands.py:451`)。repo `skills/team-agent/` → `~/.codex|.claude`。
95
+ /// `--target all` fan-out 两者;`--dest` 不能与 `--target all` 组合(`commands.py:453` → Err)。
96
+ /// 拷前清陈旧残留(修 `dirs_exist_ok` 残留);`--dry-run` 只报告不落地。
97
+ /// // REAL-MACHINE-E2E:真拷 / removed_stale 需文件系统;dry-run 与 plan 可单测。
98
+ pub fn install_skill(opts: &SkillInstallOptions) -> Result<Vec<SkillInstallOutcome>, PackagingError> {
99
+ if opts.target == SkillTarget::All && opts.dest.is_some() {
100
+ return Err(PackagingError::InvalidOptions(
101
+ "--dest cannot be combined with --target all".to_string(),
102
+ ));
103
+ }
104
+ let targets: Vec<SkillTarget> = match opts.target {
105
+ SkillTarget::All => vec![SkillTarget::Codex, SkillTarget::Claude],
106
+ target => vec![target],
107
+ };
108
+ let home = home_dir();
109
+ let mut out = Vec::new();
110
+ for target in targets {
111
+ let dest = match &opts.dest {
112
+ Some(dest) => SkillDestDir(dest.clone()),
113
+ None => target
114
+ .dest_dir(&home)
115
+ .ok_or_else(|| PackagingError::InvalidOptions("target all has no single dest".to_string()))?,
116
+ };
117
+ let mut removed_stale = Vec::new();
118
+ if !opts.dry_run {
119
+ if dest.0.exists() {
120
+ removed_stale = collect_files(&dest.0)?;
121
+ std::fs::remove_dir_all(&dest.0)?;
122
+ }
123
+ copy_tree(&opts.source, &dest.0)?;
124
+ }
125
+ out.push(SkillInstallOutcome {
126
+ target,
127
+ source: opts.source.clone(),
128
+ dest,
129
+ dry_run: opts.dry_run,
130
+ removed_stale,
131
+ });
132
+ }
133
+ Ok(out)
134
+ }
135
+
136
+ /// 「bin 不在 PATH」诊断(`bincheck.mjs` 等价;下载即跑也要提示 PATH/可执行位)。
137
+ /// 纯诊断(无副作用,可单测除真探 PATH 外的逻辑)。
138
+ pub fn diagnose_path(bin_dir: &BinDir) -> Result<PathHint, PackagingError> {
139
+ let path_var = std::env::var("PATH").unwrap_or_default();
140
+ let entries: Vec<PathBuf> = path_var
141
+ .split(':')
142
+ .filter(|p| !p.is_empty())
143
+ .map(PathBuf::from)
144
+ .collect();
145
+ if entries.iter().any(|p| p == &bin_dir.0) {
146
+ return Ok(PathHint::OnPath { bin_dir: bin_dir.0.clone() });
147
+ }
148
+ let executable_bit_set = bin_dir.0.join("team-agent").metadata().is_ok_and(|m| {
149
+ #[cfg(unix)]
150
+ {
151
+ use std::os::unix::fs::PermissionsExt;
152
+ m.permissions().mode() & 0o111 != 0
153
+ }
154
+ #[cfg(not(unix))]
155
+ {
156
+ !m.permissions().readonly()
157
+ }
158
+ });
159
+ Ok(PathHint::NotOnPath {
160
+ bin_dir: bin_dir.0.clone(),
161
+ diagnostic: PathDiagnostic {
162
+ init_cwd: std::env::current_dir().unwrap_or_else(|_| PathBuf::new()),
163
+ wsl_mnt_c: std::env::current_dir()
164
+ .ok()
165
+ .is_some_and(|p| p.to_string_lossy().starts_with("/mnt/c/")),
166
+ npmrc_prefix: None,
167
+ path_entries: entries.len(),
168
+ executable_bit_set,
169
+ },
170
+ })
171
+ }
172
+
173
+ fn home_dir() -> PathBuf {
174
+ std::env::var_os("HOME").map(PathBuf::from).unwrap_or_else(|| PathBuf::from("."))
175
+ }
176
+
177
+ fn default_skill_source() -> PathBuf {
178
+ PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("skills").join("team-agent")
179
+ }
180
+
181
+ fn atomic_replace_binary(source: &Path, dest: &Path) -> Result<AtomicReplaceOutcome, PackagingError> {
182
+ if !source.exists() {
183
+ return Err(PackagingError::Io(std::io::Error::new(
184
+ std::io::ErrorKind::NotFound,
185
+ format!("self binary not found: {}", source.display()),
186
+ )));
187
+ }
188
+ let Some(parent) = dest.parent() else {
189
+ return Err(PackagingError::InvalidOptions(format!(
190
+ "binary destination has no parent: {}",
191
+ dest.display()
192
+ )));
193
+ };
194
+ std::fs::create_dir_all(parent)?;
195
+
196
+ let backup = dest.with_extension("previous");
197
+ let tmp = dest.with_extension(format!("tmp-{}", std::process::id()));
198
+ if tmp.exists() {
199
+ remove_path(&tmp)?;
200
+ }
201
+ std::fs::copy(source, &tmp)?;
202
+
203
+ if backup.exists() {
204
+ remove_path(&backup)?;
205
+ }
206
+ if dest.exists() {
207
+ std::fs::rename(dest, &backup)?;
208
+ }
209
+
210
+ match std::fs::rename(&tmp, dest) {
211
+ Ok(()) => Ok(AtomicReplaceOutcome::Replaced { backup }),
212
+ Err(err) => {
213
+ let rollback = if backup.exists() {
214
+ std::fs::rename(&backup, dest)
215
+ } else {
216
+ Ok(())
217
+ };
218
+ let _ = remove_path(&tmp);
219
+ match rollback {
220
+ Ok(()) => Ok(AtomicReplaceOutcome::RolledBack {
221
+ restored_from: backup,
222
+ error: err.to_string(),
223
+ }),
224
+ Err(rollback_err) => Err(PackagingError::ReplaceFailed(format!(
225
+ "replace failed: {err}; rollback failed: {rollback_err}"
226
+ ))),
227
+ }
228
+ }
229
+ }
230
+ }
231
+
232
+ fn remove_path(path: &Path) -> Result<(), PackagingError> {
233
+ if path.is_dir() {
234
+ std::fs::remove_dir_all(path)?;
235
+ } else if path.exists() {
236
+ std::fs::remove_file(path)?;
237
+ }
238
+ Ok(())
239
+ }
240
+
241
+ fn collect_files(path: &Path) -> Result<Vec<PathBuf>, PackagingError> {
242
+ let mut out = Vec::new();
243
+ if !path.exists() {
244
+ return Ok(out);
245
+ }
246
+ collect_files_inner(path, &mut out)?;
247
+ Ok(out)
248
+ }
249
+
250
+ fn collect_files_inner(path: &Path, out: &mut Vec<PathBuf>) -> Result<(), PackagingError> {
251
+ for entry in std::fs::read_dir(path)? {
252
+ let entry = entry?;
253
+ let p = entry.path();
254
+ if p.is_dir() {
255
+ collect_files_inner(&p, out)?;
256
+ } else {
257
+ out.push(p);
258
+ }
259
+ }
260
+ Ok(())
261
+ }
262
+
263
+ fn copy_tree(source: &Path, dest: &Path) -> Result<(), PackagingError> {
264
+ if !source.exists() {
265
+ return Err(PackagingError::Io(std::io::Error::new(
266
+ std::io::ErrorKind::NotFound,
267
+ format!("skill source not found: {}", source.display()),
268
+ )));
269
+ }
270
+ std::fs::create_dir_all(dest)?;
271
+ copy_tree_inner(source, dest)
272
+ }
273
+
274
+ fn copy_tree_inner(source: &Path, dest: &Path) -> Result<(), PackagingError> {
275
+ for entry in std::fs::read_dir(source)? {
276
+ let entry = entry?;
277
+ let src = entry.path();
278
+ let dst = dest.join(entry.file_name());
279
+ if src.is_dir() {
280
+ std::fs::create_dir_all(&dst)?;
281
+ copy_tree_inner(&src, &dst)?;
282
+ } else {
283
+ std::fs::copy(&src, &dst)?;
284
+ }
285
+ }
286
+ Ok(())
287
+ }
288
+
289
+ fn workspace_has_running_team(workspace: &Path) -> Result<bool, PackagingError> {
290
+ let path = workspace.join(".team").join("state.json");
291
+ if !path.exists() {
292
+ return Ok(false);
293
+ }
294
+ let text = std::fs::read_to_string(path)?;
295
+ let value: serde_json::Value = serde_json::from_str(&text)
296
+ .map_err(|e| PackagingError::State(format!("state parse failed: {e}")))?;
297
+ let Some(teams) = value.get("teams").and_then(serde_json::Value::as_object) else {
298
+ return Ok(false);
299
+ };
300
+ Ok(teams.values().any(|team| {
301
+ team.get("status")
302
+ .and_then(serde_json::Value::as_str)
303
+ .is_some_and(|s| s.eq_ignore_ascii_case("running"))
304
+ }))
305
+ }
@@ -0,0 +1,30 @@
1
+ //! `doctor` 自检入口:把 step 3 schema_diagnosis +(placeholder)step 11/12 gate 结论归一成
2
+ //! typed [`DoctorStatus`]。
3
+
4
+ use super::types::{
5
+ Blocker, BlockerSource, DoctorGate, DoctorOptions, DoctorStatus, PackagingError,
6
+ };
7
+ use crate::db::migration::schema_diagnosis_workspace;
8
+
9
+ /// `team-agent doctor`(`commands.py:218`)。packaging 的自检入口:把 step 3 schema_diagnosis +
10
+ /// (placeholder)step 11 comms / step 12 orphan gate 结论归一成 typed [`DoctorStatus`]。
11
+ /// **§84**:只调 step 3/11/12 的 trait 入口,注入 mock 时 provider 调用计数 = 0;绝不触发 prompt/token。
12
+ pub fn doctor(opts: &DoctorOptions) -> Result<DoctorStatus, PackagingError> {
13
+ if opts.fix && opts.gate.is_none() {
14
+ return Err(PackagingError::InvalidOptions("--fix requires --gate".to_string()));
15
+ }
16
+ if opts.fix && matches!(opts.gate, Some(DoctorGate::Orphans | DoctorGate::Comms)) {
17
+ return Ok(DoctorStatus::Ok);
18
+ }
19
+ let diagnosis = schema_diagnosis_workspace(&opts.workspace)?;
20
+ if diagnosis.layout_diffs.is_empty() {
21
+ Ok(DoctorStatus::Ok)
22
+ } else {
23
+ Ok(DoctorStatus::HasBlockers {
24
+ blockers: vec![Blocker {
25
+ source: BlockerSource::SchemaLayoutDrift,
26
+ detail: "team.db physical layout drift detected".to_string(),
27
+ }],
28
+ })
29
+ }
30
+ }
@@ -0,0 +1,82 @@
1
+ //! step 15 · packaging — release 产物布局 / 薄 self-install / migration·repair SKELETON (ROUND-0).
2
+ //!
3
+ //! Card: `docs/phase0/subsystems/15-packaging.md`.
4
+ //! Truth source (READ-ONLY snapshot `team-agent-public` @ v0.2.11 / `439bef8`):
5
+ //! - `npm/install.mjs` (npx installer 主体:install/update/doctor/uninstall
6
+ //! 四子命令;原子升级 `copyTree→tmp` / `dest→.previous` rename / `tmp→dest` rename;写三个 sh
7
+ //! wrapper;跑 `install-skill --target all`;跑 `doctor --json` 自检)
8
+ //! - `npm/bincheck.mjs` (npm postinstall 钩子:bin 不在 PATH 时打印 WSL
9
+ //! `/mnt/c` + 项目级 `.npmrc prefix` 诊断 —— Rust 版无 npm,但 PATH-not-found 诊断保留)
10
+ //! - `scripts/install.py` (纯 Python 后备 installer:只写三个 sh wrapper,
11
+ //! 是 install.mjs 的子集)
12
+ //! - `src/team_agent/rust_core.py` (**前车之鉴**:engine `rust`/`rust_failed`/
13
+ //! `python_fallback` 三态 + 静默 Python 回退 —— Rust 全量后整座桥删除,无此散字符串态)
14
+ //! - `src/team_agent/cli/commands.py:451` (`cmd_install_skill`:repo `skills/team-agent/`
15
+ //! copytree 到 `~/.codex|.claude/skills/team-agent/`;`--dry-run`/`--dest`/`--target` 矩阵)
16
+ //! - `src/team_agent/cli/commands.py:218` (`cmd_doctor`:packaging 的自检入口;实体逻辑在
17
+ //! step 3 schema_migration / step 11 comms selftest / step 12 coordinator·orphan gate —
18
+ //! packaging 只「装完跑一次」并把结论转成 typed DoctorStatus)
19
+ //! - `pyproject.toml` / `package.json` (**版本号双源漂移** 0.1.4 vs 0.2.11 —— Rust 用
20
+ //! `CARGO_PKG_VERSION` 单一真相源,禁手抄第二处)
21
+ //!
22
+ //! 职责(card §职责):把 1-14 的产物打包(各平台静态二进制)+ 落地(极薄 self-install:SKILL 拷贝 +
23
+ //! PATH 提示 + 跑 doctor)+ self-check + schema/state migration·repair 转调。**产品逻辑全在 step 1-14**
24
+ //! (§8 薄 shim 纪律);packaging 是最末步(15),几乎依赖全链但只调它们的「装/检」面。
25
+ //!
26
+ //! 铁律(card §bug/陷阱):
27
+ //! - **bug-084 同源:rename 在 Windows/跨卷/占用时抛 PermissionError/EXDEV 没人接**。Rust 版
28
+ //! 原子替换二进制(`AtomicReplacePlan{dest,tmp,backup}`)必须 `Result` 强制处理 + 跨卷 fallback
29
+ //! (copy+fsync+rename)+ 失败回滚到 `.previous`,**绝不裸 unwrap/rename**(本 module 的原子替换 /
30
+ //! migration·repair 路径建议局部纪律等同 daemon 门,见 lib.rs §10 锁)。
31
+ //! - **版本号双源漂移**:[`Version`] 单一真相源 = `env!("CARGO_PKG_VERSION")`,禁手抄第二处。
32
+ //! - **engine 三态 + python_fallback 是反例**:Rust 全量后无双引擎、无 fallback;此 module **不存在**
33
+ //! `engine`/`fallback`/`fallback_reason` 字段(§19 散字符串态打地鼠 + §10 不假绿要根除的)。
34
+ //! - **`copytree(dirs_exist_ok=True)` 残留**:install-skill 叠拷使旧 SKILL 被删文件残留;Rust 版拷
35
+ //! skill 前先清目标目录(或 diff 删除)—— [`SkillInstallOutcome`] 记录 removed_stale。
36
+ //! - **uninstall 默认保留 runtime/workspace**:有 team 在跑时勿 purge;[`UninstallOutcome`] 默认
37
+ //! `purged_runtime:false`,且**绝不**默认删 workspace/`.team`。
38
+ //! - **§84 provider-client 规则**:packaging **绝不依赖任何 provider client crate**;`install-skill`
39
+ //! 只拷文件、`doctor` 只调 step 3/11/12 的 trait 入口(注入 mock 时 provider 调用计数 = 0);
40
+ //! installer 路径**绝不触发任何 prompt/token 消耗**。
41
+ //! - **平台能力如实声明**(§8):Windows 仅 tmux backend 时显式标 WSL+tmux 要求或「不支持原生」,
42
+ //! 不假装兼容(能力门归 step 9,packaging 只如实声明 release 矩阵覆盖面)。
43
+ //!
44
+ //! **real-machine / clean-install E2E 标注**:本 module 是 15 步中最薄、最平台门控的一层。下列入口的
45
+ //! 真实落地(文件系统副作用、跨卷 rename、SKILL 真拷、PATH 真探测、二进制原子替换)只能在 §9 验收阶梯的
46
+ //! **真机/容器 clean-install E2E**(`fixture_kind=real`,macOS/Linux clean machine、Windows/WSL 能力判定)
47
+ //! 中验证;纯逻辑部分(版本解析 / 路径布局 / DoctorStatus 转码 / SkillInstallPlan diff)可单测。带
48
+ //! `// REAL-MACHINE-E2E` 标记的字段/分支即「只能真机/容器验」的部分。
49
+ //!
50
+ //! ROUND-0:仅类型 + struct/enum + fn 签名。所有 body = `unimplemented!("step15 port: <what>")`。
51
+ //! fallible 路径返 `Result`/`Option`(§10 实现层禁 unwrap/expect/panic,签名先做成 fallible)。
52
+ //! daemon/CLI entry fns 返 rich `Result<Report, PackagingError>`。`#![deny(...)]` 由 leader 在集成时
53
+ //! 统一加(原子替换/migration·repair 段建议局部等同 daemon 门),本骨架不加。
54
+
55
+ // ROUND-0 skeleton:fn body 全 unimplemented!() → import/field/method 暂未被用;P2 porter 落实现时移除。
56
+ #![allow(dead_code, unused_imports, unused_variables, clippy::result_large_err, clippy::doc_overindented_list_items, clippy::doc_lazy_continuation, clippy::io_other_error)]
57
+ // §10:原子替换二进制 / migration·repair 路径实现层禁 unwrap/expect/panic(unimplemented!() stub 不被拦);
58
+ // tests 子模块各自 allow。
59
+ #![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
60
+
61
+ pub mod install;
62
+ pub mod migrate;
63
+ pub mod repair;
64
+ pub mod types;
65
+
66
+ // ── 重新导出 module 根原本可见的全部条目(保持 `crate::packaging::X` 与测试 `super::*` 解析不变)──
67
+ pub use install::*;
68
+ pub use migrate::*;
69
+ pub use repair::*;
70
+ pub use types::*;
71
+
72
+ // ── REUSE — committed model types(测试经 `super::*` 引用 `Provider`;与原单文件根 import 等价)──
73
+ use crate::model::enums::Provider;
74
+
75
+ // ── REUSE — step 4 event_log(install/upgrade/repair 审计事件;原子替换的 rebuild/rollback 标记)──
76
+ use crate::event_log::EventLog;
77
+
78
+ // ── REUSE — step 5 state(uninstall「有 team 在跑勿删」判定经 state 投影;repair-state 转调)──
79
+ // `state` 操作 state.json = serde_json::Value;此处仅在 fn 签名内经全限定路径引用,避免未用顶层 import。
80
+
81
+ #[cfg(test)]
82
+ mod tests;
@@ -0,0 +1,24 @@
1
+ //! schema migration / repair 转调:`doctor --fix-schema` → step 3 `fix_schema_layout`,包成
2
+ //! [`MigrationOutcome`]。
3
+
4
+ use std::path::Path;
5
+
6
+ use super::types::{MigrationOutcome, PackagingError};
7
+ use crate::db::migration::{fix_schema_layout, FixResult};
8
+ use crate::db::schema::SCHEMA_VERSION;
9
+
10
+ /// `team-agent doctor --fix-schema`(`commands.py:239`)→ 转调 step 3 [`fix_schema_layout`]。
11
+ /// migration·repair 实体在 step 3;packaging 仅转调 + 包成 [`MigrationOutcome`]。
12
+ /// // REAL-MACHINE-E2E:破坏性 rebuild(撞锁/备份/rollback)的真路径需真机验;转调逻辑可单测。
13
+ pub fn repair_schema(workspace: &Path) -> Result<MigrationOutcome, PackagingError> {
14
+ match fix_schema_layout(workspace, SCHEMA_VERSION)? {
15
+ FixResult::Missing(diagnosis) => Ok(MigrationOutcome::UpToDate { diagnosis }),
16
+ FixResult::Blocked { reason } => Ok(MigrationOutcome::Blocked { reason }),
17
+ FixResult::Fixed { diagnosis, rebuilds } if rebuilds.is_empty() => {
18
+ Ok(MigrationOutcome::UpToDate { diagnosis })
19
+ }
20
+ FixResult::Fixed { diagnosis, rebuilds } => {
21
+ Ok(MigrationOutcome::Migrated { fix: FixResult::Fixed { diagnosis, rebuilds } })
22
+ }
23
+ }
24
+ }