agents-fleet 0.5.0__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 (140) hide show
  1. agents_fleet-0.5.0/.claude-plugin/marketplace.json +35 -0
  2. agents_fleet-0.5.0/.claude-plugin/plugin.json +22 -0
  3. agents_fleet-0.5.0/.gitattributes +14 -0
  4. agents_fleet-0.5.0/.github/workflows/e2e.yml +85 -0
  5. agents_fleet-0.5.0/.github/workflows/release.yml +67 -0
  6. agents_fleet-0.5.0/.github/workflows/test.yml +27 -0
  7. agents_fleet-0.5.0/.gitignore +20 -0
  8. agents_fleet-0.5.0/AGENTS.md +4 -0
  9. agents_fleet-0.5.0/CLAUDE.md +30 -0
  10. agents_fleet-0.5.0/LICENSE +21 -0
  11. agents_fleet-0.5.0/PKG-INFO +232 -0
  12. agents_fleet-0.5.0/README.md +193 -0
  13. agents_fleet-0.5.0/docs/agents.md +249 -0
  14. agents_fleet-0.5.0/docs/assets/fleet-ls.svg +119 -0
  15. agents_fleet-0.5.0/docs/assets/fleet-top.svg +113 -0
  16. agents_fleet-0.5.0/docs/assets/social-preview.png +0 -0
  17. agents_fleet-0.5.0/docs/assets/social-preview.svg +95 -0
  18. agents_fleet-0.5.0/docs/design/access.md +223 -0
  19. agents_fleet-0.5.0/docs/design/layers.md +46 -0
  20. agents_fleet-0.5.0/docs/design/sync-and-secrets.md +176 -0
  21. agents_fleet-0.5.0/docs/getting-started.md +515 -0
  22. agents_fleet-0.5.0/gemini-extension.json +14 -0
  23. agents_fleet-0.5.0/hooks/hooks.json +15 -0
  24. agents_fleet-0.5.0/install.ps1 +202 -0
  25. agents_fleet-0.5.0/install.sh +267 -0
  26. agents_fleet-0.5.0/mcpb/manifest.json +53 -0
  27. agents_fleet-0.5.0/mcpb/pyproject.toml +6 -0
  28. agents_fleet-0.5.0/mcpb/server.py +11 -0
  29. agents_fleet-0.5.0/pyproject.toml +78 -0
  30. agents_fleet-0.5.0/scripts/build_dist.py +139 -0
  31. agents_fleet-0.5.0/scripts/readme_screenshots.py +192 -0
  32. agents_fleet-0.5.0/scripts/social_preview.html +19 -0
  33. agents_fleet-0.5.0/server.json +29 -0
  34. agents_fleet-0.5.0/skills/fleet/SKILL.md +157 -0
  35. agents_fleet-0.5.0/src/fleet/__init__.py +0 -0
  36. agents_fleet-0.5.0/src/fleet/__main__.py +15 -0
  37. agents_fleet-0.5.0/src/fleet/agents/__init__.py +36 -0
  38. agents_fleet-0.5.0/src/fleet/agents/clients.py +165 -0
  39. agents_fleet-0.5.0/src/fleet/agents/docs.py +272 -0
  40. agents_fleet-0.5.0/src/fleet/agents/registry.py +341 -0
  41. agents_fleet-0.5.0/src/fleet/agents/usage.py +247 -0
  42. agents_fleet-0.5.0/src/fleet/cli.py +2216 -0
  43. agents_fleet-0.5.0/src/fleet/config.py +94 -0
  44. agents_fleet-0.5.0/src/fleet/edit.py +155 -0
  45. agents_fleet-0.5.0/src/fleet/install.py +400 -0
  46. agents_fleet-0.5.0/src/fleet/links.py +26 -0
  47. agents_fleet-0.5.0/src/fleet/mcpserver.py +282 -0
  48. agents_fleet-0.5.0/src/fleet/models.py +219 -0
  49. agents_fleet-0.5.0/src/fleet/onboard.py +174 -0
  50. agents_fleet-0.5.0/src/fleet/ops/__init__.py +19 -0
  51. agents_fleet-0.5.0/src/fleet/ops/enrol.py +211 -0
  52. agents_fleet-0.5.0/src/fleet/ops/errors.py +22 -0
  53. agents_fleet-0.5.0/src/fleet/ops/firstrun.py +129 -0
  54. agents_fleet-0.5.0/src/fleet/ops/handover.py +369 -0
  55. agents_fleet-0.5.0/src/fleet/ops/identity.py +63 -0
  56. agents_fleet-0.5.0/src/fleet/ops/join.py +476 -0
  57. agents_fleet-0.5.0/src/fleet/ops/lifecycle.py +186 -0
  58. agents_fleet-0.5.0/src/fleet/ops/member.py +144 -0
  59. agents_fleet-0.5.0/src/fleet/ops/migrate.py +79 -0
  60. agents_fleet-0.5.0/src/fleet/ops/names.py +18 -0
  61. agents_fleet-0.5.0/src/fleet/ops/rows.py +119 -0
  62. agents_fleet-0.5.0/src/fleet/ops/sweep.py +411 -0
  63. agents_fleet-0.5.0/src/fleet/ops/sync.py +301 -0
  64. agents_fleet-0.5.0/src/fleet/probe/__init__.py +0 -0
  65. agents_fleet-0.5.0/src/fleet/probe/parse.py +237 -0
  66. agents_fleet-0.5.0/src/fleet/probe/payload.ps1 +86 -0
  67. agents_fleet-0.5.0/src/fleet/probe/payload.sh +257 -0
  68. agents_fleet-0.5.0/src/fleet/probe/runner.py +383 -0
  69. agents_fleet-0.5.0/src/fleet/reconcile.py +232 -0
  70. agents_fleet-0.5.0/src/fleet/render/__init__.py +11 -0
  71. agents_fleet-0.5.0/src/fleet/render/staleness.py +32 -0
  72. agents_fleet-0.5.0/src/fleet/render/top.py +407 -0
  73. agents_fleet-0.5.0/src/fleet/render/view.py +449 -0
  74. agents_fleet-0.5.0/src/fleet/secrets.py +60 -0
  75. agents_fleet-0.5.0/src/fleet/serve.py +212 -0
  76. agents_fleet-0.5.0/src/fleet/service.py +437 -0
  77. agents_fleet-0.5.0/src/fleet/ssh/__init__.py +15 -0
  78. agents_fleet-0.5.0/src/fleet/ssh/auth.py +74 -0
  79. agents_fleet-0.5.0/src/fleet/ssh/authkeys.py +199 -0
  80. agents_fleet-0.5.0/src/fleet/ssh/cmd.py +421 -0
  81. agents_fleet-0.5.0/src/fleet/ssh/keys.py +342 -0
  82. agents_fleet-0.5.0/src/fleet/state/__init__.py +17 -0
  83. agents_fleet-0.5.0/src/fleet/state/access.py +824 -0
  84. agents_fleet-0.5.0/src/fleet/state/inventory.py +360 -0
  85. agents_fleet-0.5.0/src/fleet/state/invites.py +191 -0
  86. agents_fleet-0.5.0/src/fleet/state/store.py +229 -0
  87. agents_fleet-0.5.0/src/fleet/state/writes.py +286 -0
  88. agents_fleet-0.5.0/src/fleet/ui.py +75 -0
  89. agents_fleet-0.5.0/tests/conftest.py +171 -0
  90. agents_fleet-0.5.0/tests/e2e/host.py +452 -0
  91. agents_fleet-0.5.0/tests/e2e/term.py +166 -0
  92. agents_fleet-0.5.0/tests/fixtures/probe/gpu-box.txt +45 -0
  93. agents_fleet-0.5.0/tests/fixtures/probe/macos-laptop.txt +63 -0
  94. agents_fleet-0.5.0/tests/fixtures/probe/vm-a.txt +34 -0
  95. agents_fleet-0.5.0/tests/fixtures/probe/vm-b.txt +33 -0
  96. agents_fleet-0.5.0/tests/test_access.py +409 -0
  97. agents_fleet-0.5.0/tests/test_advertise.py +35 -0
  98. agents_fleet-0.5.0/tests/test_alias.py +259 -0
  99. agents_fleet-0.5.0/tests/test_authorized_keys.py +247 -0
  100. agents_fleet-0.5.0/tests/test_backoff.py +187 -0
  101. agents_fleet-0.5.0/tests/test_broadcast.py +97 -0
  102. agents_fleet-0.5.0/tests/test_concurrency.py +171 -0
  103. agents_fleet-0.5.0/tests/test_disk_paths.py +431 -0
  104. agents_fleet-0.5.0/tests/test_dist.py +72 -0
  105. agents_fleet-0.5.0/tests/test_edit.py +347 -0
  106. agents_fleet-0.5.0/tests/test_enrolment.py +268 -0
  107. agents_fleet-0.5.0/tests/test_facts.py +806 -0
  108. agents_fleet-0.5.0/tests/test_firstrun.py +83 -0
  109. agents_fleet-0.5.0/tests/test_fixtures_sanitised.py +124 -0
  110. agents_fleet-0.5.0/tests/test_handover.py +128 -0
  111. agents_fleet-0.5.0/tests/test_handover_flow.py +340 -0
  112. agents_fleet-0.5.0/tests/test_help.py +63 -0
  113. agents_fleet-0.5.0/tests/test_install.py +585 -0
  114. agents_fleet-0.5.0/tests/test_inventory.py +197 -0
  115. agents_fleet-0.5.0/tests/test_join.py +646 -0
  116. agents_fleet-0.5.0/tests/test_keys.py +416 -0
  117. agents_fleet-0.5.0/tests/test_layering.py +124 -0
  118. agents_fleet-0.5.0/tests/test_lifecycle.py +261 -0
  119. agents_fleet-0.5.0/tests/test_mcp.py +237 -0
  120. agents_fleet-0.5.0/tests/test_member.py +128 -0
  121. agents_fleet-0.5.0/tests/test_migration.py +101 -0
  122. agents_fleet-0.5.0/tests/test_parse.py +213 -0
  123. agents_fleet-0.5.0/tests/test_reconcile.py +90 -0
  124. agents_fleet-0.5.0/tests/test_render.py +170 -0
  125. agents_fleet-0.5.0/tests/test_self.py +220 -0
  126. agents_fleet-0.5.0/tests/test_serve.py +364 -0
  127. agents_fleet-0.5.0/tests/test_service.py +410 -0
  128. agents_fleet-0.5.0/tests/test_setup.py +796 -0
  129. agents_fleet-0.5.0/tests/test_skill_refresh.py +109 -0
  130. agents_fleet-0.5.0/tests/test_ssh.py +167 -0
  131. agents_fleet-0.5.0/tests/test_sshauth.py +63 -0
  132. agents_fleet-0.5.0/tests/test_store_schema.py +119 -0
  133. agents_fleet-0.5.0/tests/test_sweep.py +429 -0
  134. agents_fleet-0.5.0/tests/test_sync.py +693 -0
  135. agents_fleet-0.5.0/tests/test_top.py +432 -0
  136. agents_fleet-0.5.0/tests/test_top_windows.py +51 -0
  137. agents_fleet-0.5.0/tests/test_update.py +174 -0
  138. agents_fleet-0.5.0/tests/test_view.py +260 -0
  139. agents_fleet-0.5.0/tests/test_writes.py +102 -0
  140. agents_fleet-0.5.0/uv.lock +908 -0
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "fleet",
3
+ "owner": {
4
+ "name": "Lin Zhang",
5
+ "url": "https://github.com/lion-zhang"
6
+ },
7
+ "metadata": {
8
+ "description": "fleet, as a Claude Code plugin."
9
+ },
10
+ "plugins": [
11
+ {
12
+ "name": "fleet",
13
+ "source": "./",
14
+ "description": "Every machine you have, for your coding agent: live GPU, VRAM, RAM and disk, and SSH.",
15
+ "version": "0.5.0",
16
+ "author": {
17
+ "name": "Lin Zhang",
18
+ "url": "https://github.com/lion-zhang"
19
+ },
20
+ "homepage": "https://github.com/lion-zhang/fleet",
21
+ "license": "MIT",
22
+ "keywords": [
23
+ "gpu",
24
+ "inventory",
25
+ "ssh",
26
+ "remote",
27
+ "compute",
28
+ "mcp",
29
+ "homelab",
30
+ "mlops"
31
+ ],
32
+ "category": "devops"
33
+ }
34
+ ]
35
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "fleet",
3
+ "version": "0.5.0",
4
+ "description": "Every machine you have, for your coding agent: live GPU, VRAM, RAM and disk, and SSH.",
5
+ "author": {
6
+ "name": "Lin Zhang",
7
+ "url": "https://github.com/lion-zhang"
8
+ },
9
+ "homepage": "https://github.com/lion-zhang/fleet",
10
+ "repository": "https://github.com/lion-zhang/fleet",
11
+ "license": "MIT",
12
+ "keywords": [
13
+ "gpu",
14
+ "inventory",
15
+ "ssh",
16
+ "remote",
17
+ "compute",
18
+ "mcp",
19
+ "homelab",
20
+ "mlops"
21
+ ]
22
+ }
@@ -0,0 +1,14 @@
1
+ # The probe payloads are sent verbatim over ssh to a shell on the far side, so their
2
+ # line endings are a wire format, not a local preference. Git for Windows defaults to
3
+ # core.autocrlf=true, which rewrote payload.sh to CRLF on checkout -- and every probe
4
+ # from the Windows center then failed with `sh: 8: \r: not found` followed by
5
+ # `Syntax error: "|" unexpected`, because dash reads the CR as part of the command.
6
+ #
7
+ # Declared here rather than fixed on the machine: it is a property of the file, and the
8
+ # next clone on any Windows box would otherwise reintroduce it.
9
+ *.sh text eol=lf
10
+ *.ps1 text eol=lf
11
+
12
+ # Same reasoning, one layer up: these are read and piped to a remote shell too.
13
+ src/fleet/probe/payload.sh text eol=lf
14
+ src/fleet/probe/payload.ps1 text eol=lf
@@ -0,0 +1,85 @@
1
+ name: e2e
2
+
3
+ # fleet installed and used for real on each OS: the installer makes the runner a center,
4
+ # then tests/e2e/host.py runs every command against it -- over ssh to the runner itself
5
+ # as well, so the probe, enrolment and authorized_keys edits go through that OS's sshd
6
+ # and shell. Slower than the unit tests, so only for changes that can break it.
7
+ on:
8
+ workflow_dispatch:
9
+ pull_request:
10
+ paths: ["src/**", "install.sh", "install.ps1", "tests/e2e/**", ".github/workflows/e2e.yml"]
11
+ push:
12
+ branches: [main]
13
+ paths: ["src/**", "install.sh", "install.ps1", "tests/e2e/**", ".github/workflows/e2e.yml"]
14
+
15
+ jobs:
16
+ e2e:
17
+ strategy:
18
+ fail-fast: false
19
+ matrix:
20
+ os: [ubuntu-latest, macos-latest, windows-latest]
21
+ runs-on: ${{ matrix.os }}
22
+ timeout-minutes: 30
23
+ steps:
24
+ - uses: actions/checkout@v4
25
+ - uses: astral-sh/setup-uv@v6
26
+ with:
27
+ python-version: "3.12"
28
+
29
+ - name: sshd (Linux)
30
+ if: runner.os == 'Linux'
31
+ run: |
32
+ sudo apt-get install -y -qq openssh-server >/dev/null
33
+ sudo systemctl start ssh
34
+ - name: sshd (macOS)
35
+ if: runner.os == 'macOS'
36
+ run: |
37
+ sudo launchctl load -w /System/Library/LaunchDaemons/ssh.plist 2>/dev/null \
38
+ || sudo systemsetup -f -setremotelogin on
39
+ for i in $(seq 1 20); do nc -z localhost 22 && break; sleep 1; done
40
+ # sshd refuses a key when the home or ~/.ssh is writable by others (StrictModes);
41
+ # prove a key login works before blaming fleet for one that does not.
42
+ - name: a key login to localhost works
43
+ if: runner.os != 'Windows'
44
+ run: |
45
+ chmod go-w "$HOME"; mkdir -p ~/.ssh; chmod 700 ~/.ssh
46
+ ssh-keygen -q -t ed25519 -N "" -f ~/.ssh/e2e_check
47
+ cat ~/.ssh/e2e_check.pub >> ~/.ssh/authorized_keys; chmod 600 ~/.ssh/authorized_keys
48
+ ls -ld "$HOME" ~/.ssh ~/.ssh/authorized_keys
49
+ ssh -i ~/.ssh/e2e_check -o BatchMode=yes -o StrictHostKeyChecking=accept-new localhost true \
50
+ || { ssh -vv -i ~/.ssh/e2e_check -o BatchMode=yes localhost true 2>&1 | tail -30; exit 1; }
51
+ - name: sshd (Windows)
52
+ if: runner.os == 'Windows'
53
+ shell: pwsh
54
+ run: |
55
+ if (-not (Get-Service sshd -ErrorAction SilentlyContinue)) {
56
+ Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0 | Out-Null
57
+ }
58
+ Start-Service sshd
59
+ Get-Service sshd
60
+
61
+ - name: install.sh (center)
62
+ if: runner.os != 'Windows'
63
+ run: |
64
+ FLEET_SOURCE="$PWD" sh install.sh
65
+ echo "$HOME/.local/bin" >> "$GITHUB_PATH"
66
+ - name: install.ps1 (center)
67
+ if: runner.os == 'Windows'
68
+ shell: pwsh
69
+ run: |
70
+ $env:FLEET_SOURCE = "$PWD"
71
+ ./install.ps1
72
+ uv tool dir --bin | Out-File -Append -Encoding utf8 $env:GITHUB_PATH
73
+
74
+ - name: every command
75
+ if: runner.os != 'Windows'
76
+ run: |
77
+ FLEET_BIN="$HOME/.local/bin/fleet" uv run --no-project --with mcp --with . \
78
+ python tests/e2e/host.py --ssh-user "$USER" --report "$GITHUB_STEP_SUMMARY"
79
+ - name: every command
80
+ if: runner.os == 'Windows'
81
+ shell: pwsh
82
+ run: |
83
+ $env:FLEET_BIN = Join-Path (uv tool dir --bin) 'fleet.exe'
84
+ # pywinpty: a ConPTY pseudo-console, to type into fleet as a person would
85
+ uv run --no-project --with mcp --with pywinpty --with . python tests/e2e/host.py --ssh-user $env:USERNAME --report $env:GITHUB_STEP_SUMMARY
@@ -0,0 +1,67 @@
1
+ name: release
2
+
3
+ # Publishing a GitHub release (tag vX.Y.Z) puts that version on PyPI as agents-fleet and
4
+ # attaches the wheel, the sdist and the Claude Desktop bundle (fleet.mcpb) to it.
5
+ #
6
+ # PyPI is reached by Trusted Publishing: no token is stored anywhere. pypi.org must list
7
+ # this repository, this file and the `pypi` environment as a publisher of agents-fleet.
8
+
9
+ on:
10
+ release:
11
+ types: [published]
12
+
13
+ jobs:
14
+ build:
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+ - uses: astral-sh/setup-uv@v6
19
+ - name: The tag is the version being released
20
+ run: |
21
+ v="$(uv version --short)"
22
+ test "v$v" = "$GITHUB_REF_NAME" || { echo "tag $GITHUB_REF_NAME, pyproject.toml $v"; exit 1; }
23
+ - name: The generated plugin, extension and registry files are current
24
+ run: |
25
+ uv run python scripts/build_dist.py
26
+ git diff --exit-code
27
+ - run: uv build
28
+ - uses: actions/setup-node@v4
29
+ with:
30
+ node-version: 22
31
+ - name: Claude Desktop bundle
32
+ run: |
33
+ npx -y @anthropic-ai/mcpb validate mcpb/manifest.json
34
+ npx -y @anthropic-ai/mcpb pack mcpb dist/fleet.mcpb
35
+ - uses: actions/upload-artifact@v4
36
+ with:
37
+ name: dist
38
+ path: dist/
39
+
40
+ pypi:
41
+ needs: build
42
+ runs-on: ubuntu-latest
43
+ environment: pypi
44
+ permissions:
45
+ id-token: write
46
+ steps:
47
+ - uses: actions/download-artifact@v4
48
+ with:
49
+ name: dist
50
+ path: dist/
51
+ # The bundle is for the release page, not for PyPI.
52
+ - run: rm dist/fleet.mcpb
53
+ - uses: pypa/gh-action-pypi-publish@release/v1
54
+
55
+ assets:
56
+ needs: build
57
+ runs-on: ubuntu-latest
58
+ permissions:
59
+ contents: write
60
+ steps:
61
+ - uses: actions/download-artifact@v4
62
+ with:
63
+ name: dist
64
+ path: dist/
65
+ - run: gh release upload "$GITHUB_REF_NAME" dist/* --clobber --repo "$GITHUB_REPOSITORY"
66
+ env:
67
+ GH_TOKEN: ${{ github.token }}
@@ -0,0 +1,27 @@
1
+ name: tests
2
+
3
+ on:
4
+ # A branch with a pull request is tested by the pull_request run; testing its pushes
5
+ # too ran every commit twice and sent every failure twice.
6
+ push:
7
+ branches: [main]
8
+ pull_request:
9
+ workflow_dispatch:
10
+
11
+ jobs:
12
+ test:
13
+ strategy:
14
+ fail-fast: false
15
+ matrix:
16
+ # Every OS fleet runs on: the user's Windows machine found what Linux and macOS
17
+ # runs could not (top's termios, ssh's exec), so Windows runs the suite too.
18
+ os: [ubuntu-latest, macos-latest, windows-latest]
19
+ python: ["3.12", "3.13"]
20
+ runs-on: ${{ matrix.os }}
21
+ steps:
22
+ - uses: actions/checkout@v4
23
+ - uses: astral-sh/setup-uv@v6
24
+ with:
25
+ python-version: ${{ matrix.python }}
26
+ - run: uv sync --all-extras
27
+ - run: uv run pytest -q
@@ -0,0 +1,20 @@
1
+ __pycache__/
2
+ *.pyc
3
+ .venv/
4
+ dist/
5
+ build/
6
+ *.egg-info/
7
+ .pytest_cache/
8
+
9
+ # Runtime state -- lives in the OS config/state dirs, never in the repo.
10
+ # Listed defensively in case it is ever copied here for debugging.
11
+ inventory.yaml
12
+ config.yaml
13
+ secrets.age
14
+ *.db
15
+ *.db-wal
16
+ *.db-shm
17
+
18
+ # Local data / scratch -- never pushed.
19
+ data/
20
+ .remember/
@@ -0,0 +1,4 @@
1
+ # fleet — notes for agents
2
+
3
+ Read [CLAUDE.md](CLAUDE.md) first: it holds the design decisions for this repo (the three
4
+ layers — core, skill, MCP — and the write-queue rule), and how to work here.
@@ -0,0 +1,30 @@
1
+ # fleet — notes for agents working on this repo
2
+
3
+ ## Design decisions — do not revisit
4
+
5
+ fleet has three layers (full text: [docs/design/layers.md](docs/design/layers.md)):
6
+
7
+ - **Core** — the `fleet` CLI plus its state and configuration, installed once per
8
+ machine. People can always run it by hand.
9
+ - **Skill** — a prompt telling an agent how to use the CLI properly. Text only.
10
+ - **MCP** — runs the same CLI for agents that cannot run commands. A thin wrapper,
11
+ never a second implementation.
12
+
13
+ Consequences:
14
+ - Add a capability to the CLI first; the skill describes it, MCP calls the CLI for it.
15
+ - Reads run in parallel and never wait; state files are only replaced atomically.
16
+ - Writes go through one queue and apply to the state as it is now — never save a copy
17
+ loaded earlier. Nobody blocks on anybody.
18
+ - The installer never overwrites an existing core or touches fleet's state.
19
+
20
+ ## Working here
21
+
22
+ - Tests: `uv run pytest -q`; CI runs them on Linux, macOS and Windows.
23
+ - End to end: `tests/e2e/host.py` runs every command against a real install, on each OS
24
+ (`.github/workflows/e2e.yml`, also from the Actions tab). It drives `fleet top` and
25
+ `fleet ssh` at a real terminal (`tests/e2e/term.py`: a pty, or ConPTY on Windows) —
26
+ agents run fleet with no terminal, people with one, and both must be tested.
27
+ - Generated files, never hand-edited: `skills/fleet/SKILL.md` and the plugin/extension
28
+ manifests (`uv run python scripts/build_dist.py`), the README screenshots
29
+ (`uv run python scripts/readme_screenshots.py`).
30
+ - The two install modes are **center** and **member**; use those words.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Lin Zhang
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,232 @@
1
+ Metadata-Version: 2.5
2
+ Name: agents-fleet
3
+ Version: 0.5.0
4
+ Summary: Give Claude Code, Codex and Gemini every machine you have: a live GPU, VRAM, RAM and disk inventory across your servers, SSH access between them, and an MCP server.
5
+ Project-URL: Homepage, https://github.com/lion-zhang/fleet
6
+ Project-URL: Documentation, https://github.com/lion-zhang/fleet/blob/main/docs/getting-started.md
7
+ Project-URL: Issues, https://github.com/lion-zhang/fleet/issues
8
+ Author: Lin Zhang
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: ai-agents,claude-code,codex,coding-agent,devops,gemini-cli,gpu,gpu-monitoring,homelab,inventory,mcp,mcp-server,mlops,nvidia,remote-execution,runpod,ssh,ssh-keys,vast-ai,vram
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Intended Audience :: Science/Research
16
+ Classifier: Intended Audience :: System Administrators
17
+ Classifier: Operating System :: MacOS
18
+ Classifier: Operating System :: Microsoft :: Windows
19
+ Classifier: Operating System :: POSIX :: Linux
20
+ Classifier: Programming Language :: Python :: 3
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
24
+ Classifier: Topic :: System :: Clustering
25
+ Classifier: Topic :: System :: Monitoring
26
+ Classifier: Topic :: System :: Systems Administration
27
+ Requires-Python: >=3.12
28
+ Requires-Dist: filelock>=3.13
29
+ Requires-Dist: mcp>=2
30
+ Requires-Dist: platformdirs>=4.2
31
+ Requires-Dist: psutil>=5.9
32
+ Requires-Dist: pyyaml>=6.0
33
+ Requires-Dist: rich>=13.7
34
+ Requires-Dist: typer>=0.12
35
+ Provides-Extra: mcp
36
+ Provides-Extra: migrate
37
+ Requires-Dist: pyrage>=1.1; extra == 'migrate'
38
+ Description-Content-Type: text/markdown
39
+
40
+ <!-- mcp-name: io.github.lion-zhang/fleet -->
41
+ <div align="center">
42
+
43
+ # fleet — let your coding agent use every machine you have
44
+
45
+ **Claude Code, Codex and Gemini see one machine: the one they run on.<br>
46
+ fleet shows them all of yours — every GPU, how much is free, and how to get there.**
47
+
48
+ [![tests](https://github.com/lion-zhang/fleet/actions/workflows/test.yml/badge.svg)](https://github.com/lion-zhang/fleet/actions/workflows/test.yml)
49
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
50
+ ![Linux | macOS | Windows](https://img.shields.io/badge/platform-linux%20%7C%20macos%20%7C%20windows-lightgrey.svg)
51
+ ![MCP server](https://img.shields.io/badge/MCP-server-8A2BE2.svg)
52
+
53
+ <img src="docs/assets/fleet-ls.svg" alt="fleet ls: free GPU, VRAM, CPU, RAM and disk across seven machines — an A100 rental with one busy and one idle card, an idle H100 rental flagged as costing money, a busy RTX 3090 box with a nearly full disk, a laptop, a NAS and a machine that is switched off" width="100%">
54
+
55
+ </div>
56
+
57
+ ## The problem
58
+
59
+ Ask your agent to train a model and it starts on your laptop — while a 4090 sits idle
60
+ across the room and a rented A100 bills you by the hour. It cannot use what it cannot see.
61
+
62
+ - **Your agent is stuck on one machine.** It has no idea your other boxes exist.
63
+ - **Finding a free GPU is manual.** SSH into five hosts, run `nvidia-smi`, compare in your head.
64
+ - **Handing it a server means pasting credentials** into the chat, and hoping.
65
+
66
+ ## Install once, then just talk to your agent
67
+
68
+ On the machine you work from:
69
+
70
+ ```bash
71
+ curl -LsSf https://raw.githubusercontent.com/lion-zhang/fleet/main/install.sh | sh
72
+ ```
73
+
74
+ <sub>Windows: `powershell -ExecutionPolicy ByPass -c "irm https://raw.githubusercontent.com/lion-zhang/fleet/main/install.ps1 | iex"`</sub>
75
+
76
+ That is the whole setup. This machine becomes your fleet's **center**, and every
77
+ [supported agent](docs/agents.md) installed on it learns fleet. From here on you say what you want in plain words —
78
+ no commands to remember. When something is missing, the agent asks (illustrative):
79
+
80
+ > **You:** add my new GPU server
81
+ >
82
+ > **Agent:** Sure — how do you usually connect to it? An SSH command like
83
+ > `ssh -p 40001 root@1.2.3.4` is all I need.
84
+ >
85
+ > **You:** `ssh ubuntu@10.0.0.7`
86
+ >
87
+ > **Agent:** Added as `gpu-box`: 2× RTX 4090, both idle, 46 GB free. It's ready to use.
88
+
89
+ > **You:** train `train.py` on whatever has a free 24 GB card
90
+ >
91
+ > **Agent:** `rtx4090` has 23.1 GB free and an idle GPU; `a100-spot` is free too but costs
92
+ > $1.89/hr. Starting on `rtx4090`, logging to `train.log`.
93
+
94
+ | You say | What happens |
95
+ |---|---|
96
+ | "what's free right now?" | every machine checked, the free ones listed |
97
+ | "find me a box with a 24 GB card" | machines matched by what they have, not by name |
98
+ | "run the tests on the Linux box" | run there, results brought back |
99
+ | "what's costing me money?" | idle paid rentals flagged, with their hourly price |
100
+ | "let the laptop reach the NAS" | access granted, applied at once |
101
+ | "add a machine without typing its password" | a one-time line to paste there; it joins by itself |
102
+
103
+ No hostnames, keys or passwords go into the conversation, and anything irreversible
104
+ waits for your yes.
105
+
106
+ Every machine besides the center is a **member**. A member needs nothing installed —
107
+ just sshd. For machines where you also want to *run* fleet, or that you would rather not
108
+ type a password for, the agent gives you an invite line: pasted there, it installs fleet
109
+ and joins by itself.
110
+
111
+ ### Already in your agent? Install from there
112
+
113
+ | Agent | Install |
114
+ |---|---|
115
+ | **Claude Code** | `/plugin marketplace add lion-zhang/fleet` then `/plugin install fleet@fleet` |
116
+ | **Codex** | `codex plugin marketplace add lion-zhang/fleet` then `codex plugin add fleet@fleet` |
117
+ | **Gemini CLI** | `gemini extensions install https://github.com/lion-zhang/fleet` |
118
+ | **GitHub Copilot CLI** | `copilot plugin marketplace add lion-zhang/fleet` then `copilot plugin install fleet@fleet` |
119
+ | **Cursor** | [![Add to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en/install-mcp?name=fleet&config=eyJjb21tYW5kIjoidXZ4IiwiYXJncyI6WyJhZ2VudC1mbGVldCIsIm1jcCJdfQ%3D%3D) |
120
+ | **VS Code** | [![Install in VS Code](https://img.shields.io/badge/VS_Code-Install_fleet-0098FF?logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=fleet&config=%7B%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22agents-fleet%22%2C%22mcp%22%5D%7D) |
121
+ | **Claude Desktop** | open `fleet.mcpb` from the [latest release](https://github.com/lion-zhang/fleet/releases/latest) |
122
+
123
+ Plus OpenCode, Amp, Windsurf, Cline, Zed, Qwen Code, Goose, Kiro, Hermes — 35+ agents in all:
124
+ **[every agent →](docs/agents.md)**
125
+
126
+ On a machine in no fleet yet, these make it a center on first use, like the installer.
127
+ For a member, paste its invite line first.
128
+
129
+ ## Built to be safe
130
+
131
+ - **Nothing to install on your machines.** fleet probes with one script over one SSH
132
+ connection — Linux, macOS or Windows. A NAS or a fresh rental works as it is.
133
+ - **Keys, not passwords.** A password, if needed at all, is typed once by you. Nothing
134
+ that could be stolen is stored, and the agent never sees a credential.
135
+ - **Access you control.** The center decides which machine may reach which, and a revoke
136
+ is pushed at once. `fleet access gpu-box --allow laptop`, `--deny` to take it back.
137
+ - **The agent asks, not guesses.** It is told to ask which machine you mean, and to leave
138
+ irreversible commands to you.
139
+
140
+ ## Prefer the command line?
141
+
142
+ Everything the agent does is a plain `fleet` command, for when you want to drive it
143
+ yourself:
144
+
145
+ ```bash
146
+ fleet ls # every machine, with what is free right now
147
+ fleet ls --tag cuda --tag vram-24g # by capability: NVIDIA, a card of 24 GB or more
148
+ fleet top # live view, like htop for the whole fleet
149
+ fleet show gpu-box # one machine in detail: GPU processes, services, disks
150
+ fleet ssh gpu-box -- nvidia-smi # run something there
151
+ fleet add "ssh ubuntu@10.0.0.7" # add a machine; fleet invite NAME for a join line
152
+ fleet access nas --allow laptop # let one machine reach another
153
+ ```
154
+
155
+ <img src="docs/assets/fleet-top.svg" alt="fleet top: a live view of GPU utilisation, free VRAM, CPU, RAM and disk across all machines" width="100%">
156
+
157
+ Rentals from vast.ai, RunPod or Lambda show their price (`fleet edit a100 --cost 1.89`),
158
+ the fleet's burn rate, and an alert when a paid machine sits idle. On Tailscale, ZeroTier
159
+ or WireGuard? fleet just needs an address it can route to.
160
+
161
+ ## How fleet compares
162
+
163
+ fleet is about the machines you already have. It complements tools that launch new ones.
164
+
165
+ | | `ssh` + `nvidia-smi` | nvitop / gpustat | SkyPilot / dstack | **fleet** |
166
+ |---|:---:|:---:|:---:|:---:|
167
+ | All your machines in one view | ✗ | ✗ one machine | ✓ the ones it manages | ✓ |
168
+ | Built for coding agents (skill / MCP) | ✗ | ✗ | partly | ✓ |
169
+ | Nothing installed on target machines | ✓ | ✗ | ✗ | ✓ |
170
+ | Manages SSH access between machines | ✗ | ✗ | for its own clusters | ✓ |
171
+ | Launches new cloud VMs | ✗ | ✗ | ✓ | ✗ |
172
+
173
+ ## FAQ
174
+
175
+ <details>
176
+ <summary><b>I use Claude Code <i>and</i> Codex (and more) on one machine. Does that work?</b></summary>
177
+
178
+ Yes — that is the normal case. fleet is installed once per machine: one command, one
179
+ inventory, one set of keys. Each agent only gets a small skill or MCP entry pointing at
180
+ it, so Claude Code, Codex, Gemini CLI and a desktop app all see the same machines, and can
181
+ use them at the same time. Install a new agent later? Run `fleet setup` (or ask an agent
182
+ that already has fleet to do it).
183
+ </details>
184
+
185
+ <details>
186
+ <summary><b>What does my agent actually get?</b></summary>
187
+
188
+ Agents with a shell (Claude Code, Codex, Gemini CLI, Copilot CLI, OpenCode, …) get a
189
+ skill — text that tells them the `fleet` commands; it costs nothing until a task needs a
190
+ machine. Apps that cannot run commands (Claude Desktop, Cursor, VS Code, …) get an MCP
191
+ server that runs the same commands for them. The installer sets up the supported agents
192
+ you have; [docs/agents.md](docs/agents.md) has the details for each.
193
+ </details>
194
+
195
+ <details>
196
+ <summary><b>Does it work behind NAT, or across sites?</b></summary>
197
+
198
+ The center needs an address it can route to: a public IP, a LAN, or an overlay such as
199
+ Tailscale. A machine the center cannot dial can still join with the `fleet invite` line
200
+ and report in; granting access to it waits until the center can reach it.
201
+ </details>
202
+
203
+ <details>
204
+ <summary><b>What if the center is off?</b></summary>
205
+
206
+ Normal — it can be a laptop that is closed half the day. Everything already granted keeps
207
+ working; only changes wait for it. Move the role with `fleet center NAME`.
208
+ </details>
209
+
210
+ <details>
211
+ <summary><b>Windows?</b></summary>
212
+
213
+ Yes, both ways. A Windows machine works as a target with OpenSSH Server and nothing else,
214
+ and fleet runs on Windows too, center included: interactive `fleet ssh`, `fleet top` and
215
+ the background service all work there. See [Windows](docs/getting-started.md#windows).
216
+ </details>
217
+
218
+ ## Learn more
219
+
220
+ - [Getting started](docs/getting-started.md) — the full walkthrough, every command
221
+ - [Every agent](docs/agents.md) — install commands and config for 35+ agents
222
+ - [Design](docs/design/layers.md) — one core per machine, a skill per agent, MCP for the rest; and [access](docs/design/access.md) — how access is granted, signed and revoked
223
+
224
+ **Status:** v0.5. The test suite runs on Linux, macOS and Windows, and every command is
225
+ run end to end on a real machine of each OS in CI — from a script, as an agent runs it,
226
+ and at a real terminal, as you do. Multi-machine fleets (key, password, invite, handover)
227
+ are tested on Linux machines built from scratch.
228
+
229
+ Issues and pull requests are welcome — `uv run pytest -q` runs the tests. If fleet saved
230
+ you a GPU-hour, a ⭐ helps other people find it.
231
+
232
+ [MIT](LICENSE)