Spaces:
Running
Running
Document storage bucket setup for template duplicates
Browse files- README.md +64 -16
- entrypoint.sh +1 -2
- setup.py +26 -8
- web/src/components/Locked.tsx +17 -2
README.md
CHANGED
|
@@ -28,31 +28,79 @@ tmux-backed, so they survive disconnects. A **Skills** page distributes reusable
|
|
| 28 |
## Run your own (private) instance
|
| 29 |
|
| 30 |
**Option A — one click:** press **⋮ → Duplicate this Space** at the top of this
|
| 31 |
-
page. Keep visibility **Private**.
|
|
|
|
| 32 |
|
| 33 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
|
| 35 |
```python
|
| 36 |
-
from huggingface_hub import
|
| 37 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
```
|
| 39 |
|
| 40 |
Then open your new private Space and **log in to each agent inside its terminal**
|
| 41 |
-
(run `claude`, `codex`, etc. and follow the prompt)
|
| 42 |
-
|
| 43 |
-
|
| 44 |
|
| 45 |
-
##
|
| 46 |
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
the Space
|
|
|
|
|
|
|
| 50 |
|
| 51 |
-
|
| 52 |
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 56 |
|
| 57 |
Everything durable lives under `/data`: `sessions.json`, `groups.json`,
|
| 58 |
`workspaces/<folder>/` (per-agent working dirs + shared `skills/`), and each
|
|
@@ -80,7 +128,7 @@ sessions are pinned to a stable per-session id so grouped agents don't collide).
|
|
| 80 |
| Var | Default | Purpose |
|
| 81 |
|---|---|---|
|
| 82 |
| `PORT` | `7860` | HTTP + WS port (HF `app_port`) |
|
| 83 |
-
| `DATA_DIR` | `/data` | Durable root (
|
| 84 |
| `USE_TMUX` | auto | `1`/`0` to force tmux on/off (off = direct PTY, no persistence) |
|
| 85 |
| `ANTHROPIC_API_KEY` | — | Claude Code / opencode / Hermes (Space **secret**) |
|
| 86 |
| `OPENAI_API_KEY` / `CODEX_API_KEY` | — | Codex (Space secret) |
|
|
|
|
| 28 |
## Run your own (private) instance
|
| 29 |
|
| 30 |
**Option A — one click:** press **⋮ → Duplicate this Space** at the top of this
|
| 31 |
+
page. Keep visibility **Private**. Then create a private Storage Bucket and mount
|
| 32 |
+
it at `/data` before logging in:
|
| 33 |
|
| 34 |
+
```python
|
| 35 |
+
from huggingface_hub import HfApi, Volume, create_bucket
|
| 36 |
+
|
| 37 |
+
api = HfApi()
|
| 38 |
+
space_id = "your-username/agent-manager"
|
| 39 |
+
bucket_id = "your-username/agent-manager-data"
|
| 40 |
+
|
| 41 |
+
create_bucket(bucket_id, private=True, exist_ok=True)
|
| 42 |
+
api.set_space_volumes(
|
| 43 |
+
space_id,
|
| 44 |
+
volumes=[
|
| 45 |
+
Volume(type="bucket", source=bucket_id, mount_path="/data"),
|
| 46 |
+
],
|
| 47 |
+
)
|
| 48 |
+
api.restart_space(space_id)
|
| 49 |
+
```
|
| 50 |
+
|
| 51 |
+
**Option B — one script** (needs `pip install -U huggingface_hub` and
|
| 52 |
+
`hf auth login`):
|
| 53 |
|
| 54 |
```python
|
| 55 |
+
from huggingface_hub import HfApi, Volume, create_bucket
|
| 56 |
+
|
| 57 |
+
api = HfApi()
|
| 58 |
+
space_id = "your-username/agent-manager"
|
| 59 |
+
bucket_id = "your-username/agent-manager-data"
|
| 60 |
+
|
| 61 |
+
create_bucket(bucket_id, private=True, exist_ok=True)
|
| 62 |
+
api.duplicate_repo(
|
| 63 |
+
from_id="lvwerra/agent-manager-template",
|
| 64 |
+
to_id=space_id,
|
| 65 |
+
repo_type="space",
|
| 66 |
+
private=True,
|
| 67 |
+
space_volumes=[
|
| 68 |
+
Volume(type="bucket", source=bucket_id, mount_path="/data"),
|
| 69 |
+
],
|
| 70 |
+
)
|
| 71 |
```
|
| 72 |
|
| 73 |
Then open your new private Space and **log in to each agent inside its terminal**
|
| 74 |
+
(run `claude`, `codex`, etc. and follow the prompt). You can also set provider
|
| 75 |
+
keys as Space **secrets** (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, …) instead of
|
| 76 |
+
logging in interactively.
|
| 77 |
|
| 78 |
+
## Storage bucket (required for real use)
|
| 79 |
|
| 80 |
+
Agent Manager expects a private Storage Bucket mounted read-write at `/data`.
|
| 81 |
+
That bucket stores your sessions, workspaces, skills, CLI credentials, and
|
| 82 |
+
history. Without the bucket mount, the Space can still boot for preview, but it
|
| 83 |
+
falls back to ephemeral disk: sessions, logins, and history reset whenever the
|
| 84 |
+
Space sleeps or rebuilds.
|
| 85 |
|
| 86 |
+
If you duplicated first, you can add or replace the mount later:
|
| 87 |
|
| 88 |
+
```python
|
| 89 |
+
from huggingface_hub import HfApi, Volume, create_bucket
|
| 90 |
+
|
| 91 |
+
api = HfApi()
|
| 92 |
+
space_id = "your-username/agent-manager"
|
| 93 |
+
bucket_id = "your-username/agent-manager-data"
|
| 94 |
+
|
| 95 |
+
create_bucket(bucket_id, private=True, exist_ok=True)
|
| 96 |
+
api.set_space_volumes(
|
| 97 |
+
space_id,
|
| 98 |
+
volumes=[
|
| 99 |
+
Volume(type="bucket", source=bucket_id, mount_path="/data"),
|
| 100 |
+
],
|
| 101 |
+
)
|
| 102 |
+
api.restart_space(space_id)
|
| 103 |
+
```
|
| 104 |
|
| 105 |
Everything durable lives under `/data`: `sessions.json`, `groups.json`,
|
| 106 |
`workspaces/<folder>/` (per-agent working dirs + shared `skills/`), and each
|
|
|
|
| 128 |
| Var | Default | Purpose |
|
| 129 |
|---|---|---|
|
| 130 |
| `PORT` | `7860` | HTTP + WS port (HF `app_port`) |
|
| 131 |
+
| `DATA_DIR` | `/data` | Durable root (mounted private Storage Bucket) |
|
| 132 |
| `USE_TMUX` | auto | `1`/`0` to force tmux on/off (off = direct PTY, no persistence) |
|
| 133 |
| `ANTHROPIC_API_KEY` | — | Claude Code / opencode / Hermes (Space **secret**) |
|
| 134 |
| `OPENAI_API_KEY` / `CODEX_API_KEY` | — | Codex (Space secret) |
|
entrypoint.sh
CHANGED
|
@@ -4,8 +4,7 @@
|
|
| 4 |
DATA_DIR="${DATA_DIR:-/data}"
|
| 5 |
if ! mkdir -p "$DATA_DIR/workspaces" 2>/dev/null; then
|
| 6 |
echo "WARN: $DATA_DIR is not writable — running EPHEMERAL (sessions/logins reset on restart)."
|
| 7 |
-
echo " For durability,
|
| 8 |
-
echo " or attach a private bucket at /data."
|
| 9 |
DATA_DIR="$HOME/data"
|
| 10 |
mkdir -p "$DATA_DIR/workspaces"
|
| 11 |
fi
|
|
|
|
| 4 |
DATA_DIR="${DATA_DIR:-/data}"
|
| 5 |
if ! mkdir -p "$DATA_DIR/workspaces" 2>/dev/null; then
|
| 6 |
echo "WARN: $DATA_DIR is not writable — running EPHEMERAL (sessions/logins reset on restart)."
|
| 7 |
+
echo " For durability, attach a private Storage Bucket mounted read-write at /data."
|
|
|
|
| 8 |
DATA_DIR="$HOME/data"
|
| 9 |
mkdir -p "$DATA_DIR/workspaces"
|
| 10 |
fi
|
setup.py
CHANGED
|
@@ -1,22 +1,40 @@
|
|
| 1 |
#!/usr/bin/env python3
|
| 2 |
-
"""Spin up your own PRIVATE Agent Manager Space
|
| 3 |
|
| 4 |
-
pip install huggingface_hub
|
| 5 |
hf auth login # or: huggingface-cli login
|
| 6 |
-
python setup.py
|
| 7 |
|
| 8 |
-
The
|
| 9 |
-
|
| 10 |
-
|
| 11 |
"""
|
| 12 |
import sys
|
| 13 |
-
from huggingface_hub import
|
| 14 |
|
| 15 |
TEMPLATE = "lvwerra/agent-manager-template"
|
| 16 |
|
| 17 |
if __name__ == "__main__":
|
| 18 |
target = sys.argv[1] if len(sys.argv) > 1 else None # optional "you/your-space"
|
| 19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
url = getattr(res, "url", res)
|
| 21 |
print(f"\n✅ Created your private Space: {url}")
|
|
|
|
| 22 |
print(" Keep it PRIVATE — this app has no authentication.")
|
|
|
|
| 1 |
#!/usr/bin/env python3
|
| 2 |
+
"""Spin up your own PRIVATE Agent Manager Space from the public template.
|
| 3 |
|
| 4 |
+
pip install -U huggingface_hub
|
| 5 |
hf auth login # or: huggingface-cli login
|
| 6 |
+
python setup.py [your-username/agent-manager] [your-username/agent-manager-data]
|
| 7 |
|
| 8 |
+
The script creates a private Storage Bucket and mounts it read-write at /data
|
| 9 |
+
while duplicating the Space. Open the new private Space only after that, then
|
| 10 |
+
log in to each agent inside its terminal (run `claude`, `codex`, ...).
|
| 11 |
"""
|
| 12 |
import sys
|
| 13 |
+
from huggingface_hub import HfApi, Volume, create_bucket
|
| 14 |
|
| 15 |
TEMPLATE = "lvwerra/agent-manager-template"
|
| 16 |
|
| 17 |
if __name__ == "__main__":
|
| 18 |
target = sys.argv[1] if len(sys.argv) > 1 else None # optional "you/your-space"
|
| 19 |
+
api = HfApi()
|
| 20 |
+
if target and "/" in target:
|
| 21 |
+
namespace, space_name = target.split("/", 1)
|
| 22 |
+
else:
|
| 23 |
+
namespace = api.whoami()["name"]
|
| 24 |
+
space_name = target or TEMPLATE.rsplit("/", 1)[1]
|
| 25 |
+
bucket_id = sys.argv[2] if len(sys.argv) > 2 else f"{namespace}/{space_name}-data"
|
| 26 |
+
|
| 27 |
+
create_bucket(bucket_id, private=True, exist_ok=True)
|
| 28 |
+
res = api.duplicate_repo(
|
| 29 |
+
from_id=TEMPLATE,
|
| 30 |
+
to_id=target,
|
| 31 |
+
repo_type="space",
|
| 32 |
+
private=True,
|
| 33 |
+
space_volumes=[
|
| 34 |
+
Volume(type="bucket", source=bucket_id, mount_path="/data"),
|
| 35 |
+
],
|
| 36 |
+
)
|
| 37 |
url = getattr(res, "url", res)
|
| 38 |
print(f"\n✅ Created your private Space: {url}")
|
| 39 |
+
print(f" Mounted private bucket at /data: {bucket_id}")
|
| 40 |
print(" Keep it PRIVATE — this app has no authentication.")
|
web/src/components/Locked.tsx
CHANGED
|
@@ -4,7 +4,22 @@ import { useState } from 'react';
|
|
| 4 |
// disabled server-side; this explains how to run a private copy or relock.
|
| 5 |
export default function Locked({ spaceId }: { spaceId?: string | null }) {
|
| 6 |
const id = spaceId || 'owner/space-name';
|
| 7 |
-
const cmd = `from huggingface_hub import
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
const [copied, setCopied] = useState(false);
|
| 9 |
const copy = () => {
|
| 10 |
navigator.clipboard?.writeText(cmd).then(() => { setCopied(true); setTimeout(() => setCopied(false), 1500); }).catch(() => {});
|
|
@@ -22,7 +37,7 @@ export default function Locked({ spaceId }: { spaceId?: string | null }) {
|
|
| 22 |
|
| 23 |
<h3>Run your own private copy</h3>
|
| 24 |
<p className="locked-sub">
|
| 25 |
-
|
| 26 |
</p>
|
| 27 |
<div className="locked-cmd">
|
| 28 |
<pre><code>{cmd}</code></pre>
|
|
|
|
| 4 |
// disabled server-side; this explains how to run a private copy or relock.
|
| 5 |
export default function Locked({ spaceId }: { spaceId?: string | null }) {
|
| 6 |
const id = spaceId || 'owner/space-name';
|
| 7 |
+
const cmd = `from huggingface_hub import HfApi, Volume, create_bucket
|
| 8 |
+
|
| 9 |
+
api = HfApi()
|
| 10 |
+
space_id = "your-username/agent-manager"
|
| 11 |
+
bucket_id = "your-username/agent-manager-data"
|
| 12 |
+
|
| 13 |
+
create_bucket(bucket_id, private=True, exist_ok=True)
|
| 14 |
+
api.duplicate_repo(
|
| 15 |
+
from_id="${id}",
|
| 16 |
+
to_id=space_id,
|
| 17 |
+
repo_type="space",
|
| 18 |
+
private=True,
|
| 19 |
+
space_volumes=[
|
| 20 |
+
Volume(type="bucket", source=bucket_id, mount_path="/data"),
|
| 21 |
+
],
|
| 22 |
+
)`;
|
| 23 |
const [copied, setCopied] = useState(false);
|
| 24 |
const copy = () => {
|
| 25 |
navigator.clipboard?.writeText(cmd).then(() => { setCopied(true); setTimeout(() => setCopied(false), 1500); }).catch(() => {});
|
|
|
|
| 37 |
|
| 38 |
<h3>Run your own private copy</h3>
|
| 39 |
<p className="locked-sub">
|
| 40 |
+
Create a private Storage Bucket mounted at <b>/data</b>, then duplicate this Space:
|
| 41 |
</p>
|
| 42 |
<div className="locked-cmd">
|
| 43 |
<pre><code>{cmd}</code></pre>
|