bots-garden/sidekickpublic⑂ Fork 0
⑂ feature/security
Commits
⬇ Clone ▾
git clone https://git.rickub.com/bots-garden/sidekick.git
git clone ssh://git@rickub.com/bots-garden/sidekick.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

🛟 Updated. 7954965 · on feature/security · k33g · 2d ago
README.md · 120 lines · 7.6 KBmarkdown
Blame HistoryOpen raw

mm-web-client

A web client for the Mini-Me ACP agent, providing a fancy interface similar to Claude Desktop.

Installation

Ensure you have Go installed on your system.

  1. Navigate to the directory:

    cd mm-web-client
    
  2. Download dependencies:

    go mod download
    

Usage

The server acts as a bridge between the web interface and the Mini-Me agent process.

Running the server

You can run the server directly using go run:

go run ./cmd/server [-port <port>] [-cwd <dir>] [-web <dir>] <agent_path> [agent_args...]
  • -host: address to listen on (default 127.0.0.1: only this machine can connect). In a VM or a container, use -host 0.0.0.0 so the host machine can reach it (port forwarding, or the VM's IP) — anyone else who can reach that address can too.
  • -token: the access token (see below). Prefer the SIDEKICK_TOKEN environment variable: a flag is visible in the process list.
  • -allow-host: host names (comma-separated) the browser may use to reach sidekick, besides localhost and IP addresses — e.g. -allow-host my-vm.local. Any other name is refused (protection against DNS rebinding).
  • -port: HTTP port (default 6767 — not 8080, which is llama-server's default port).
  • -cwd: the project directory the agent works in (default: the directory the server is started from). ACP requires an absolute path: this is where the agent runs its commands and stores its sessions (.mm/sessions).
  • -web: serve the web UI from this directory instead of the one embedded in the binary (useful while editing web/: a reload shows the changes without rebuilding).
  • -version: print the version and exit: the release tag (from release.env) for a release build, otherwise the commit id (-dirty if the working tree had changes), or dev under go run.

The agent's logs (banner, warnings, [acp] trail) are printed on the server's stderr.

Context and tokens

Next to the model, the header shows the context window. When the agent reports its usage (ACP usage_update, experimental), a bar shows how full it is (orange from 70%, red from 90%) with the tokens in context and the session's cost if any; otherwise only the window size from the agent's banner (ctx 8.2k). Hovering it gives the details. When the agent returns token counts with its answer (usage in the prompt response), each turn ends with a line such as ↑ 4.0k in · ↓ 500 out · 150 thinking tokens. Nothing is estimated: what the agent does not send is not shown.

Access token

sidekick gives a shell, the agent and the files of the working directory to whoever talks to it, so every request needs a token. At start-up it prints the URL to open:

🔑 Open http://localhost:6767/?token=q3Xf9…kL2w

Opening it stores the token in a cookie (HttpOnly, SameSite=Strict) and removes it from the address bar; reloads and new tabs then work as long as the browser keeps the session. Without the cookie, everything is refused (401), static files and WebSockets included.

A new token is made at each start: open the new URL after a restart (tabs already open reconnect by themselves once it is opened). To keep the same URL across restarts, set it yourself:

export SIDEKICK_TOKEN="$(openssl rand -base64 32 | tr '+/' '-_' | tr -d '=')"

Files, editor and terminal

📁 Files (in the header) shows a tree of the agent's working directory (-cwd):

  • clicking a file opens it in a Monaco tab; Ctrl/Cmd+S or Save writes it back;
  • +📄 / +📁 in the tree's header, or a right click, create a file or a folder (in the selected folder); a right click also offers Rename and Delete. With the tree focused, F2 renames and Delete deletes the selected entry. Open tabs follow a rename;
  • after each agent tool call (and each command run in the terminal), the tree and the tabs without local changes are reloaded from disk. If a file you are editing was changed on disk meanwhile, saving asks before overwriting it.

Drag and drop:

  • files and folders dropped from your computer onto the tree are copied into the folder under the pointer (or at the top); an existing file is only replaced after confirmation;
  • a row of the tree dragged onto a folder moves it there (open tabs follow);
  • files dropped onto the chat — or picked with 📎 — are copied into .sidekick/uploads/ (never replacing a file: spec.md becomes spec-1.md) and attached to the next message; a row dragged from the tree onto the chat is attached as is. Attachments are sent as ACP resource_links (file:// URIs) and listed in the message text. Uploads are limited to 200 MB per file.

⌨️ Terminal opens a shell ($SHELL, as a login shell) in the working directory. Hiding the panel keeps it running; when it exits, Enter starts a new one. Not available on Windows.

Only the working directory is reachable from the tree and the editor: ../ and symlinks pointing outside it are refused. Binary files and files over 2 MB are not opened; .git is not listed. The panels can be resized by dragging their edges.

The tree uses the icons of VS Code's Material Icon Theme (MIT), with the same rules: by file name, else by extension, folders by name, with their light-theme variants.

All the libraries the web UI uses (Tailwind, marked, highlight.js, Monaco, xterm.js, the Material icons) are embedded in the binary, in web/vendor/: the page loads nothing from the network, and works offline.

./vendor.sh writes web/vendor/ from the versions pinned at its top. To update a library: ./vendor.sh --outdated lists the pinned and published versions; change the version in the script (its comments say which updates need more than that), run ./vendor.sh, check the UI, commit web/vendor/.

Security: sidekick gives a shell, the agent and the files of the working directory to whoever talks to it, hence the access token on every request. By default it only listens on 127.0.0.1; with -host 0.0.0.0, anyone who can reach the address can try (a warning is printed), and the token travels in clear over HTTP: on a shared network, keep 127.0.0.1 in the VM and use an SSH tunnel (ssh -L 6767:localhost:6767 my-vm). Requests must name the server as localhost, an IP address or a -allow-host name (protection against DNS rebinding), and the WebSockets only accept pages served by sidekick itself.

What the agent writes is untrusted (it may repeat HTML read in a file or a web page): the chat filters it with DOMPurify — no scripts, event handlers, javascript: links, styles, frames or forms; links open in a new tab. Behind that, a Content-Security-Policy only lets the page run the scripts sidekick serves and load nothing from elsewhere (so an image URL in a message cannot carry data out), and the page cannot be shown in another site's frame.

Examples

1. Using the default port (6767):

go run ./cmd/server mm --config config.yaml

2. Specifying a different port (e.g., 9000):

go run ./cmd/server -port 9000 mm --config config.yaml

3. If mm is in your PATH and you want to use a config file in the current directory:

go run ./cmd/server -port 9000 mm --config config.yaml

4. If you are pointing to a specific binary path:

go run ./cmd/server -port 9000 ./path/to/mm --config ./config.yaml

Building the binary

To compile the server into an executable:

go build -o mm-web-client ./cmd/server
./mm-web-client [-port <port>] <agent_path> [agent_args...]

Once running, the web interface will be available at http://localhost:<port>.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# mm-web-client

A web client for the Mini-Me ACP agent, providing a fancy interface similar to Claude Desktop.

## Installation

Ensure you have [Go](https://go.dev/) installed on your system.

1. Navigate to the directory:
   ```bash
   cd mm-web-client
   ```

2. Download dependencies:
   ```bash
   go mod download
   ```

## Usage

The server acts as a bridge between the web interface and the Mini-Me agent process.

### Running the server

You can run the server directly using `go run`:

```bash
go run ./cmd/server [-port <port>] [-cwd <dir>] [-web <dir>] <agent_path> [agent_args...]
```

- `-host`: address to listen on (default `127.0.0.1`: only this machine can connect). In a VM or a container, use `-host 0.0.0.0` so the host machine can reach it (port forwarding, or the VM's IP) — anyone else who can reach that address can too.
- `-token`: the access token (see below). Prefer the `SIDEKICK_TOKEN` environment variable: a flag is visible in the process list.
- `-allow-host`: host names (comma-separated) the browser may use to reach sidekick, besides `localhost` and IP addresses — e.g. `-allow-host my-vm.local`. Any other name is refused (protection against DNS rebinding).
- `-port`: HTTP port (default `6767` — not 8080, which is llama-server's default port).
- `-cwd`: the project directory the agent works in (default: the directory the server is started from). ACP requires an absolute path: this is where the agent runs its commands and stores its sessions (`.mm/sessions`).
- `-web`: serve the web UI from this directory instead of the one embedded in the binary (useful while editing `web/`: a reload shows the changes without rebuilding).
- `-version`: print the version and exit: the release tag (from `release.env`) for a release build, otherwise the commit id (`-dirty` if the working tree had changes), or `dev` under `go run`.

The agent's logs (banner, warnings, `[acp]` trail) are printed on the server's stderr.

### Context and tokens

Next to the model, the header shows the context window. When the agent reports its usage (ACP `usage_update`, experimental), a bar shows how full it is (orange from 70%, red from 90%) with the tokens in context and the session's cost if any; otherwise only the window size from the agent's banner (`ctx 8.2k`). Hovering it gives the details. When the agent returns token counts with its answer (`usage` in the prompt response), each turn ends with a line such as `↑ 4.0k in · ↓ 500 out · 150 thinking tokens`. Nothing is estimated: what the agent does not send is not shown.

### Access token

sidekick gives a shell, the agent and the files of the working directory to whoever talks to it, so every request needs a token. At start-up it prints the URL to open:

```
🔑 Open http://localhost:6767/?token=q3Xf9…kL2w
```

Opening it stores the token in a cookie (`HttpOnly`, `SameSite=Strict`) and removes it from the address bar; reloads and new tabs then work as long as the browser keeps the session. Without the cookie, everything is refused (401), static files and WebSockets included.

A new token is made at each start: open the new URL after a restart (tabs already open reconnect by themselves once it is opened). To keep the same URL across restarts, set it yourself:

```bash
export SIDEKICK_TOKEN="$(openssl rand -base64 32 | tr '+/' '-_' | tr -d '=')"
```

### Files, editor and terminal

**📁 Files** (in the header) shows a tree of the agent's working directory (`-cwd`):

- clicking a file opens it in a [Monaco](https://microsoft.github.io/monaco-editor/) tab; **Ctrl/Cmd+S** or **Save** writes it back;
- **+📄** / **+📁** in the tree's header, or a right click, create a file or a folder (in the selected folder); a right click also offers **Rename** and **Delete**. With the tree focused, **F2** renames and **Delete** deletes the selected entry. Open tabs follow a rename;
- after each agent tool call (and each command run in the terminal), the tree and the tabs without local changes are reloaded from disk. If a file you are editing was changed on disk meanwhile, saving asks before overwriting it.

**Drag and drop:**

- files and folders dropped from your computer onto the tree are copied into the folder under the pointer (or at the top); an existing file is only replaced after confirmation;
- a row of the tree dragged onto a folder moves it there (open tabs follow);
- files dropped onto the chat — or picked with 📎 — are copied into `.sidekick/uploads/` (never replacing a file: `spec.md` becomes `spec-1.md`) and attached to the next message; a row dragged from the tree onto the chat is attached as is. Attachments are sent as ACP `resource_link`s (`file://` URIs) and listed in the message text. Uploads are limited to 200 MB per file.

**⌨️ Terminal** opens a shell (`$SHELL`, as a login shell) in the working directory. Hiding the panel keeps it running; when it exits, Enter starts a new one. Not available on Windows.

Only the working directory is reachable from the tree and the editor: `../` and symlinks pointing outside it are refused. Binary files and files over 2 MB are not opened; `.git` is not listed. The panels can be resized by dragging their edges.

The tree uses the icons of VS Code's [Material Icon Theme](https://github.com/material-extensions/vscode-material-icon-theme) (MIT), with the same rules: by file name, else by extension, folders by name, with their light-theme variants.

All the libraries the web UI uses (Tailwind, marked, highlight.js, Monaco, xterm.js, the Material icons) are embedded in the binary, in `web/vendor/`: the page loads nothing from the network, and works offline.

`./vendor.sh` writes `web/vendor/` from the versions pinned at its top. To update a library: `./vendor.sh --outdated` lists the pinned and published versions; change the version in the script (its comments say which updates need more than that), run `./vendor.sh`, check the UI, commit `web/vendor/`.

**Security:** sidekick gives a shell, the agent and the files of the working directory to whoever talks to it, hence the access token on every request. By default it only listens on `127.0.0.1`; with `-host 0.0.0.0`, anyone who can reach the address can try (a warning is printed), and the token travels in clear over HTTP: on a shared network, keep `127.0.0.1` in the VM and use an SSH tunnel (`ssh -L 6767:localhost:6767 my-vm`). Requests must name the server as `localhost`, an IP address or a `-allow-host` name (protection against DNS rebinding), and the WebSockets only accept pages served by sidekick itself.

What the agent writes is untrusted (it may repeat HTML read in a file or a web page): the chat filters it with DOMPurify — no scripts, event handlers, `javascript:` links, styles, frames or forms; links open in a new tab. Behind that, a Content-Security-Policy only lets the page run the scripts sidekick serves and load nothing from elsewhere (so an image URL in a message cannot carry data out), and the page cannot be shown in another site's frame.

### Examples

**1. Using the default port (6767):**
```bash
go run ./cmd/server mm --config config.yaml
```

**2. Specifying a different port (e.g., 9000):**
```bash
go run ./cmd/server -port 9000 mm --config config.yaml
```

**3. If `mm` is in your PATH and you want to use a config file in the current directory:**
```bash
go run ./cmd/server -port 9000 mm --config config.yaml
```

**4. If you are pointing to a specific binary path:**
```bash
go run ./cmd/server -port 9000 ./path/to/mm --config ./config.yaml
```

### Building the binary

To compile the server into an executable:

```bash
go build -o mm-web-client ./cmd/server
./mm-web-client [-port <port>] <agent_path> [agent_args...]
```

Once running, the web interface will be available at `http://localhost:<port>`.