kache-ssh 0.1.0__py3-none-any.whl
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.
- kache/AGENTS.md +212 -0
- kache/__init__.py +17 -0
- kache/__main__.py +3 -0
- kache/agent.py +594 -0
- kache/cli.py +590 -0
- kache/client.py +352 -0
- kache/config.py +151 -0
- kache/evidence.py +112 -0
- kache/keys.py +42 -0
- kache/mcp.py +277 -0
- kache/network.py +148 -0
- kache/probe.py +176 -0
- kache/service.py +88 -0
- kache/telemetry.py +121 -0
- kache/tui.py +836 -0
- kache_ssh-0.1.0.dist-info/METADATA +174 -0
- kache_ssh-0.1.0.dist-info/RECORD +20 -0
- kache_ssh-0.1.0.dist-info/WHEEL +4 -0
- kache_ssh-0.1.0.dist-info/entry_points.txt +2 -0
- kache_ssh-0.1.0.dist-info/licenses/LICENSE +21 -0
kache/AGENTS.md
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# Kache guide for scripts and AI agents
|
|
2
|
+
|
|
3
|
+
Kache sends shell commands to a person's own devices (their "paired"
|
|
4
|
+
devices) and keeps a live feed of every order. You can use all of it from a
|
|
5
|
+
shell (`kache ... --json`) or as MCP tools (`kache mcp`). This guide is
|
|
6
|
+
everything you need; `kache --help` and `kache <command> --help` list flags.
|
|
7
|
+
|
|
8
|
+
## How it works
|
|
9
|
+
|
|
10
|
+
- Every device runs a **gatekeeper** (an SSH server on TCP port 7722). It
|
|
11
|
+
executes orders only from devices it has paired with, and only after a
|
|
12
|
+
person said yes to the pairing on that device.
|
|
13
|
+
- An **order** is one command line. It runs exactly as written in the
|
|
14
|
+
device's own shell: **PowerShell on Windows**, the user's shell
|
|
15
|
+
(`$SHELL -c`) on Linux and macOS. Check a device's `os` before writing
|
|
16
|
+
commands for it.
|
|
17
|
+
- **Sessions:** `cd` is remembered per sender and session name, so
|
|
18
|
+
`cd C:\proj` followed by `git status` in the same session works like a
|
|
19
|
+
terminal. Other shell state (variables, activated venvs) does not carry
|
|
20
|
+
over: each order is a fresh shell started in the session folder.
|
|
21
|
+
- **Every order is visible.** Each one appears in the feed of the device
|
|
22
|
+
that ran it, with the exact command, who sent it and how it ended. The
|
|
23
|
+
person who owns the devices may be watching live.
|
|
24
|
+
- **Labels.** Orders carry a label naming who gave them. It is taken from
|
|
25
|
+
`--as`, then `$KACHE_AS`, then an AI agent's own name (`$AI_AGENT`,
|
|
26
|
+
`CLAUDECODE`). Through MCP the label is the MCP client's name. Never
|
|
27
|
+
pretend to be the human.
|
|
28
|
+
- **Failures come with evidence.** When an order fails, is refused or is
|
|
29
|
+
stopped, the gatekeeper writes an evidence report: hints, the last output
|
|
30
|
+
lines, and the machine's state at that moment.
|
|
31
|
+
- **Addresses.** A paired device is remembered with all of its addresses
|
|
32
|
+
(LAN IP, Tailscale IP, Tailscale name). Kache tries the one that worked
|
|
33
|
+
last first, then the others, so devices stay reachable away from home
|
|
34
|
+
when both run Tailscale.
|
|
35
|
+
|
|
36
|
+
## Rules for agents
|
|
37
|
+
|
|
38
|
+
1. Look before you act. Start with `kache devices --json`. It shows what
|
|
39
|
+
exists, which devices are online, their OS, and how loaded they are.
|
|
40
|
+
2. Prefer read-only commands until you know the state. Ask the person before
|
|
41
|
+
anything destructive: deleting, overwriting, rebooting, killing processes,
|
|
42
|
+
or changing system settings.
|
|
43
|
+
3. Always pass a timeout (`--timeout` / `timeout_seconds`). Never start
|
|
44
|
+
interactive programs; they wait forever for input. Kache has no TTY yet.
|
|
45
|
+
For long jobs, start them in the background on the device (for example
|
|
46
|
+
`Start-Process`, `nohup ... &`, or a scheduled task) and poll.
|
|
47
|
+
4. Check a device's health before heavy work. A hot GPU or full RAM shows in
|
|
48
|
+
`telemetry`.
|
|
49
|
+
5. Pairing needs a person. You may start one (`kache pair` / `kache_pair`)
|
|
50
|
+
and must tell the person the 6-digit code. Only they can accept it on
|
|
51
|
+
the other device. Accepting, denying and revoking are not offered to
|
|
52
|
+
agents through MCP. Do not use `kache accept` unless the person
|
|
53
|
+
explicitly tells you to.
|
|
54
|
+
6. When a device cannot be reached, run `kache doctor DEVICE --json` and
|
|
55
|
+
follow its `fix` fields. Do not guess.
|
|
56
|
+
|
|
57
|
+
## Commands
|
|
58
|
+
|
|
59
|
+
All commands accept `--json`. JSON output is one object (pretty-printed)
|
|
60
|
+
with `"v"` (the schema version, currently 1). In JSON mode, errors are
|
|
61
|
+
`{"v":1,"ok":false,"error":"..."}` on stdout, with exit code 1.
|
|
62
|
+
|
|
63
|
+
| Command | Does |
|
|
64
|
+
|---|---|
|
|
65
|
+
| `kache devices` | this device and every paired device: status and health |
|
|
66
|
+
| `kache run TARGET [--as NAME] [--session S] [--timeout SEC] -- COMMAND` | send an order. TARGET is a device name, `a,b,c`, `all` (every paired device) or `this` |
|
|
67
|
+
| `kache feed [-d DEVICE\|all] [-n N] [-f]` | recent events; `-f` follows live. `--json` prints JSON lines, one event per line |
|
|
68
|
+
| `kache evidence ID -d DEVICE` | a failure report (always JSON) |
|
|
69
|
+
| `kache doctor [DEVICE]` | connectivity diagnosis with fixes |
|
|
70
|
+
| `kache discover` | Tailscale devices, and which run Kache |
|
|
71
|
+
| `kache pair ADDRESS` | ask a device to accept this one (a person must accept there) |
|
|
72
|
+
| `kache pending` | pair requests waiting on this device |
|
|
73
|
+
| `kache whoami` | this device: name, addresses, fingerprint, listen mode |
|
|
74
|
+
| `kache rename OLD NEW`, `kache remove NAME` | manage your device list |
|
|
75
|
+
| `kache trusted`, `kache revoke NAME` | who may command this device |
|
|
76
|
+
| `kache listen [all\|tailscale\|IP]` | which networks this gatekeeper answers on |
|
|
77
|
+
| `kache name NEW` | rename this device |
|
|
78
|
+
| `kache agent`, `kache service install`, `kache stop` | run, autostart, stop the gatekeeper |
|
|
79
|
+
| `kache mcp` | MCP server on stdio |
|
|
80
|
+
| `kache` | the interactive Kache terminal (for people) |
|
|
81
|
+
|
|
82
|
+
### `kache run ... --json`
|
|
83
|
+
|
|
84
|
+
```json
|
|
85
|
+
{"v": 1, "ok": false, "command": "python train.py", "label": "claude-code",
|
|
86
|
+
"results": [
|
|
87
|
+
{"device": "desktop", "exit": 1, "duration": 0.84, "cwd": "C:\\proj",
|
|
88
|
+
"evidence": "E-3f2a9c", "summary": "A Python module is missing on this device.",
|
|
89
|
+
"rejected": null, "timed_out": false,
|
|
90
|
+
"stdout": "...", "stderr": "...ModuleNotFoundError: No module named 'torch'\n",
|
|
91
|
+
"evidence_report": {"...": "see Evidence below"}},
|
|
92
|
+
{"device": "pi", "exit": null, "error": "cannot reach pi (...)", "unreachable": true}
|
|
93
|
+
]}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
- `results` always holds one entry per target, in target order.
|
|
97
|
+
- `exit` is `null` when the device could not be reached.
|
|
98
|
+
- `rejected` is set when the gatekeeper refused the order before running
|
|
99
|
+
it, for example a command that is not installed on Linux or macOS, or a
|
|
100
|
+
Kache version mismatch.
|
|
101
|
+
|
|
102
|
+
### `kache devices --json`
|
|
103
|
+
|
|
104
|
+
```json
|
|
105
|
+
{"v": 1, "devices": [
|
|
106
|
+
{"name": "desktop", "this_device": false, "online": true,
|
|
107
|
+
"address": "100.81.247.101", "addresses": ["100.81.247.101", "192.168.50.152"], "port": 7722,
|
|
108
|
+
"fingerprint": "SHA256:...", "os": "Windows 11", "kache": "0.1.0", "trusts_you": true,
|
|
109
|
+
"telemetry": {"cpu": 12.5, "cpu_temp": null, "ram": 41.0, "ram_used": 13.1, "ram_total": 31.8,
|
|
110
|
+
"disk": 63.2, "uptime": 86400, "os": "Windows 11", "kache": "0.1.0", "time": 1760000000.0,
|
|
111
|
+
"gpus": [{"name": "NVIDIA GeForce RTX 4060 Ti", "temp": 54, "util": 97,
|
|
112
|
+
"mem_used": 5.2, "mem_total": 8.0}]},
|
|
113
|
+
"error": null}]}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
- `cpu`, `ram` and `disk` are percentages; memory is in GiB.
|
|
117
|
+
- `cpu_temp` is in °C, or `null` where the OS does not expose it (Windows).
|
|
118
|
+
- `trusts_you: false` means the other device removed this one. Pair again.
|
|
119
|
+
|
|
120
|
+
### Feed events (`kache feed --json`, `kache_feed`)
|
|
121
|
+
|
|
122
|
+
One JSON object per line. All of them have `v`, `kind`, `time` (unix
|
|
123
|
+
seconds), `device` (the gatekeeper that saw it) and `device_fp`. Events
|
|
124
|
+
replayed from history on connect have `"replay": true`.
|
|
125
|
+
|
|
126
|
+
| kind | extra fields |
|
|
127
|
+
|---|---|
|
|
128
|
+
| `order` | `id`, `cmd` (exact command), `sender` {`name`, `label`, `via` (`cli`/`terminal`/`mcp`), `fingerprint`, `addr`}, `cwd`, `session` |
|
|
129
|
+
| `done` | `id`, `cmd`, `sender`, `exit`, `duration`, `session`; `evidence` + `summary` on failure; `rejected`; `cancelled: true` when stopped |
|
|
130
|
+
| `evidence` | `id` (E-...), `order` (order id), `cmd`, `sender`, `summary`, `exit` |
|
|
131
|
+
| `pair-request` | `id` (P-...), `name`, `os`, `fingerprint`, `addr`, `code` |
|
|
132
|
+
| `paired` | `name`, `fingerprint`, `addr` |
|
|
133
|
+
| `pair-declined` | `name`, `fingerprint`, `reason` |
|
|
134
|
+
| `unpaired` | `sender`, `text` |
|
|
135
|
+
| `unreachable` | `text` (from the CLI or MCP, when a device's feed could not be opened) |
|
|
136
|
+
|
|
137
|
+
### Evidence reports
|
|
138
|
+
|
|
139
|
+
```json
|
|
140
|
+
{"id": "E-3f2a9c", "time": 1760000000.0, "device": "desktop",
|
|
141
|
+
"order": {"id": "O-1a2b3c", "cmd": "python train.py", "sender": {"...": "..."}, "cwd": "C:\\proj"},
|
|
142
|
+
"exit": 1, "duration": 0.84, "summary": "A Python module is missing on this device.",
|
|
143
|
+
"hints": [{"kind": "python-module", "text": "The Python module 'torch' is not installed on this device."}],
|
|
144
|
+
"stdout_tail": ["..."], "stderr_tail": ["..."], "command_found": true,
|
|
145
|
+
"system": {"os": "Windows 11", "machine": "AMD64", "python": "3.13.1", "kache": "0.1.0",
|
|
146
|
+
"shell": "powershell.exe", "telemetry": {"...": "..."}, "top_processes": [{"...": "..."}]}}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Hint kinds: `command-not-found`, `permission`, `disk-full`,
|
|
150
|
+
`python-module`, `network`, `port-in-use`, `python-exception`,
|
|
151
|
+
`out-of-memory`, `signal`, `cancelled`.
|
|
152
|
+
|
|
153
|
+
## Exit codes (`kache run`)
|
|
154
|
+
|
|
155
|
+
| code | meaning |
|
|
156
|
+
|---|---|
|
|
157
|
+
| 0 | the order succeeded on every target |
|
|
158
|
+
| n | the order's own exit code (the highest one across several devices) |
|
|
159
|
+
| 1 | Kache error: bad input, gatekeeper not running, pairing refused... |
|
|
160
|
+
| 2 | at least one device could not be reached |
|
|
161
|
+
| 77 | the device does not accept orders from this one (not paired) |
|
|
162
|
+
| 124 | `--timeout` expired; the order was stopped on the device |
|
|
163
|
+
| 126 | refused: Kache versions differ |
|
|
164
|
+
| 127 | refused: the command does not exist on that device (Linux/macOS; on Windows a missing command exits 1 with a `command-not-found` hint) |
|
|
165
|
+
|
|
166
|
+
## MCP
|
|
167
|
+
|
|
168
|
+
Add Kache to any MCP client. In Claude Code:
|
|
169
|
+
`claude mcp add kache -- kache mcp`
|
|
170
|
+
|
|
171
|
+
| Tool | Arguments |
|
|
172
|
+
|---|---|
|
|
173
|
+
| `kache_devices` | none |
|
|
174
|
+
| `kache_run` | `target`, `command`, optional `session`, `timeout_seconds` (default 120, max 3600) |
|
|
175
|
+
| `kache_feed` | optional `device` (default `all`), `limit` (default 30, max 60) |
|
|
176
|
+
| `kache_evidence` | `id`, `device` |
|
|
177
|
+
| `kache_doctor` | optional `device` |
|
|
178
|
+
| `kache_discover` | none |
|
|
179
|
+
| `kache_pending` | none |
|
|
180
|
+
| `kache_pair` | `address` |
|
|
181
|
+
|
|
182
|
+
- Results are the same JSON as the CLI, in `structuredContent`, with a
|
|
183
|
+
text copy in `content`.
|
|
184
|
+
- Each output stream keeps its last 60,000 characters per device; anything
|
|
185
|
+
cut is reported in `stdout_truncated` / `stderr_truncated`.
|
|
186
|
+
- The default session is `mcp-<client name>`, so `cd` persists across
|
|
187
|
+
calls.
|
|
188
|
+
|
|
189
|
+
## Files (read-only for agents)
|
|
190
|
+
|
|
191
|
+
`kache whoami` prints the state folder: `%LOCALAPPDATA%\kache` on Windows,
|
|
192
|
+
`~/.local/share/kache` on Linux, `~/Library/Application Support/kache` on
|
|
193
|
+
macOS, or `$KACHE_HOME` if set.
|
|
194
|
+
|
|
195
|
+
| file | contents |
|
|
196
|
+
|---|---|
|
|
197
|
+
| `feed.jsonl` | every event this gatekeeper emitted, one JSON per line (rotated at 5 MB to `feed.old.jsonl`) |
|
|
198
|
+
| `evidence/E-*.json` | evidence reports |
|
|
199
|
+
| `devices.json` | devices this one commands: addresses, port, pinned fingerprint |
|
|
200
|
+
| `trusted.json` | devices allowed to command this one |
|
|
201
|
+
| `device.json` | this device's name, port and listen mode |
|
|
202
|
+
| `device_key` | this device's private key. Never read, copy or print it |
|
|
203
|
+
|
|
204
|
+
## Troubleshooting
|
|
205
|
+
|
|
206
|
+
| Symptom | Cause / fix |
|
|
207
|
+
|---|---|
|
|
208
|
+
| `cannot reach X (... refused)` | the device is on, but Kache is not running there: `kache service install` on it |
|
|
209
|
+
| `cannot reach X (... timed out)` | the device is off or asleep, on another network without Tailscale, or a firewall drops port 7722 |
|
|
210
|
+
| `answered with a different key` | the device was reinstalled, or this is not the same device. Remove it and pair again; never ignore this |
|
|
211
|
+
| exit 77 | that device no longer trusts this one: pair again |
|
|
212
|
+
| a command works by hand but not through Kache | Kache uses a non-interactive shell without your profile (PowerShell `-NoProfile`). Use full paths or set variables in the command |
|
kache/__init__.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Kache: one terminal to command all your devices.
|
|
2
|
+
|
|
3
|
+
Every device runs a gatekeeper agent (an SSH server with its own rules);
|
|
4
|
+
the Kache terminal pairs devices, shows their health, sends orders to one
|
|
5
|
+
device or all of them, and keeps a live feed of every order anyone sends.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
__version__ = "0.1.0"
|
|
9
|
+
|
|
10
|
+
# Bumped whenever devices running different Kache versions could
|
|
11
|
+
# misunderstand each other. The gatekeeper refuses orders from other
|
|
12
|
+
# protocol versions instead of guessing.
|
|
13
|
+
PROTOCOL = 1
|
|
14
|
+
|
|
15
|
+
# Version of the JSON shapes scripts and AI agents read (feed events, CLI
|
|
16
|
+
# --json output, MCP tool results). Every feed event carries it as "v".
|
|
17
|
+
SCHEMA = 1
|
kache/__main__.py
ADDED