clikernel 0.2.3__tar.gz → 0.2.4__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.
- {clikernel-0.2.3 → clikernel-0.2.4}/CHANGELOG.md +7 -0
- {clikernel-0.2.3/clikernel.egg-info → clikernel-0.2.4}/PKG-INFO +7 -6
- {clikernel-0.2.3 → clikernel-0.2.4}/README.md +5 -4
- {clikernel-0.2.3 → clikernel-0.2.4}/clikernel/__init__.py +2 -2
- {clikernel-0.2.3 → clikernel-0.2.4}/clikernel/cli.py +3 -3
- {clikernel-0.2.3 → clikernel-0.2.4}/clikernel/core.py +11 -11
- {clikernel-0.2.3 → clikernel-0.2.4}/clikernel/mcp.py +1 -1
- {clikernel-0.2.3 → clikernel-0.2.4}/clikernel/skill.py +3 -3
- {clikernel-0.2.3 → clikernel-0.2.4}/clikernel/stream.py +1 -1
- {clikernel-0.2.3 → clikernel-0.2.4/clikernel.egg-info}/PKG-INFO +7 -6
- {clikernel-0.2.3 → clikernel-0.2.4}/clikernel.egg-info/requires.txt +1 -1
- {clikernel-0.2.3 → clikernel-0.2.4}/pyproject.toml +1 -1
- {clikernel-0.2.3 → clikernel-0.2.4}/tests/test_stream.py +5 -5
- {clikernel-0.2.3 → clikernel-0.2.4}/LICENSE +0 -0
- {clikernel-0.2.3 → clikernel-0.2.4}/MANIFEST.in +0 -0
- {clikernel-0.2.3 → clikernel-0.2.4}/clikernel/_modidx.py +0 -0
- {clikernel-0.2.3 → clikernel-0.2.4}/clikernel.egg-info/SOURCES.txt +0 -0
- {clikernel-0.2.3 → clikernel-0.2.4}/clikernel.egg-info/dependency_links.txt +0 -0
- {clikernel-0.2.3 → clikernel-0.2.4}/clikernel.egg-info/entry_points.txt +0 -0
- {clikernel-0.2.3 → clikernel-0.2.4}/clikernel.egg-info/top_level.txt +0 -0
- {clikernel-0.2.3 → clikernel-0.2.4}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: clikernel
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
4
4
|
Summary: Serve persistent Jupyter kernels to LLMs as concise text, over MCP or a plain stream protocol
|
|
5
5
|
Author: clikernel contributors
|
|
6
6
|
License: Apache-2.0
|
|
@@ -18,7 +18,7 @@ Requires-Dist: aidialog>=0.0.7
|
|
|
18
18
|
Requires-Dist: pillow
|
|
19
19
|
Provides-Extra: dev
|
|
20
20
|
Requires-Dist: fastship; extra == "dev"
|
|
21
|
-
Requires-Dist:
|
|
21
|
+
Requires-Dist: rustygate>=0.1.1; extra == "dev"
|
|
22
22
|
Dynamic: license-file
|
|
23
23
|
|
|
24
24
|
# clikernel
|
|
@@ -26,7 +26,7 @@ Dynamic: license-file
|
|
|
26
26
|
|
|
27
27
|
<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->
|
|
28
28
|
|
|
29
|
-
`clikernel` gives an LLM agent a persistent Python workbench built from two processes. A [
|
|
29
|
+
`clikernel` gives an LLM agent a persistent Python workbench built from two processes. A gateway ([rustygate](https://github.com/AnswerDotAI/rustygate)) runs all the time and hosts real Jupyter kernels ([ipymini](https://github.com/AnswerDotAI/ipymini) by default); kernels live there and persist until explicitly stopped. `clikernel` itself starts and stops with each conversation: a small translator the MCP host launches, speaking MCP to the model and the Jupyter kernels API to the gateway. Outputs come back as concise text — a bare `42` for a single result, tagged sections for several, tracebacks ANSI-stripped and capped.
|
|
30
30
|
|
|
31
31
|
Because kernels outlive conversations, an agent can `connect` back to yesterday’s kernel (or the user’s live solveit kernel) and find its state intact. Kernels created or attached explicitly are never stopped implicitly: only `stop_kernel` ends them. The one exception is the auto kernel: an `execute` with nothing connected creates a kernel scoped to the conversation, stopped again when the conversation ends or the agent connects elsewhere. Creating a kernel runs the user’s `startup.py` and installs their `inspectors.py` cell-checking rules, delivered as source so remote kernels get the same setup as local ones.
|
|
32
32
|
|
|
@@ -36,10 +36,10 @@ Because kernels outlive conversations, an agent can `connect` back to yesterday
|
|
|
36
36
|
pip install clikernel
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
Plus the resident side: [
|
|
39
|
+
Plus the resident side: [rustygate](https://github.com/AnswerDotAI/rustygate) and a kernel ([ipymini](https://github.com/AnswerDotAI/ipymini) by default). Start the gateway (and keep it running, e.g. via launchd/systemd):
|
|
40
40
|
|
|
41
41
|
``` sh
|
|
42
|
-
|
|
42
|
+
rustygate --port 8787
|
|
43
43
|
```
|
|
44
44
|
|
|
45
45
|
## Use with an MCP host
|
|
@@ -50,7 +50,7 @@ Register the stdio server with your MCP host, e.g. for Claude Code:
|
|
|
50
50
|
claude mcp add clikernel -- clikernel-mcp
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
-
The tools mirror
|
|
53
|
+
The tools mirror the gateway’s kernel API plus one composite: `connect` (create a fresh kernel — running `startup.py` and installing inspectors — or attach to an existing one by id), `execute` (run code, get concise text), `list_kernels`, `stop_kernel`, `restart`, and `interrupt`. An `execute` with no kernel connected auto-creates one, scoped to the conversation: it stops at conversation end, or when `connect` moves elsewhere. Kernels made or attached with an explicit `connect` are stopped only by `stop_kernel` — a later conversation reattaches by id and continues where the last one stopped. `$CLIKERNEL_HOST` overrides the default gateway (`http://127.0.0.1:8787`).
|
|
54
54
|
|
|
55
55
|
## Configuration
|
|
56
56
|
|
|
@@ -64,6 +64,7 @@ Three optional files in `$XDG_CONFIG_HOME/clikernel/` (usually `~/.config/cliker
|
|
|
64
64
|
[gateways.solveit]
|
|
65
65
|
url = "https://solveit.example.com/gate"
|
|
66
66
|
token_env = "SOLVEIT_TOKEN"
|
|
67
|
+
verify = false # optional: accept a self-signed certificate
|
|
67
68
|
```
|
|
68
69
|
|
|
69
70
|
## The stream protocol
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->
|
|
5
5
|
|
|
6
|
-
`clikernel` gives an LLM agent a persistent Python workbench built from two processes. A [
|
|
6
|
+
`clikernel` gives an LLM agent a persistent Python workbench built from two processes. A gateway ([rustygate](https://github.com/AnswerDotAI/rustygate)) runs all the time and hosts real Jupyter kernels ([ipymini](https://github.com/AnswerDotAI/ipymini) by default); kernels live there and persist until explicitly stopped. `clikernel` itself starts and stops with each conversation: a small translator the MCP host launches, speaking MCP to the model and the Jupyter kernels API to the gateway. Outputs come back as concise text — a bare `42` for a single result, tagged sections for several, tracebacks ANSI-stripped and capped.
|
|
7
7
|
|
|
8
8
|
Because kernels outlive conversations, an agent can `connect` back to yesterday’s kernel (or the user’s live solveit kernel) and find its state intact. Kernels created or attached explicitly are never stopped implicitly: only `stop_kernel` ends them. The one exception is the auto kernel: an `execute` with nothing connected creates a kernel scoped to the conversation, stopped again when the conversation ends or the agent connects elsewhere. Creating a kernel runs the user’s `startup.py` and installs their `inspectors.py` cell-checking rules, delivered as source so remote kernels get the same setup as local ones.
|
|
9
9
|
|
|
@@ -13,10 +13,10 @@ Because kernels outlive conversations, an agent can `connect` back to yesterday
|
|
|
13
13
|
pip install clikernel
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
-
Plus the resident side: [
|
|
16
|
+
Plus the resident side: [rustygate](https://github.com/AnswerDotAI/rustygate) and a kernel ([ipymini](https://github.com/AnswerDotAI/ipymini) by default). Start the gateway (and keep it running, e.g. via launchd/systemd):
|
|
17
17
|
|
|
18
18
|
``` sh
|
|
19
|
-
|
|
19
|
+
rustygate --port 8787
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
## Use with an MCP host
|
|
@@ -27,7 +27,7 @@ Register the stdio server with your MCP host, e.g. for Claude Code:
|
|
|
27
27
|
claude mcp add clikernel -- clikernel-mcp
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
The tools mirror
|
|
30
|
+
The tools mirror the gateway’s kernel API plus one composite: `connect` (create a fresh kernel — running `startup.py` and installing inspectors — or attach to an existing one by id), `execute` (run code, get concise text), `list_kernels`, `stop_kernel`, `restart`, and `interrupt`. An `execute` with no kernel connected auto-creates one, scoped to the conversation: it stops at conversation end, or when `connect` moves elsewhere. Kernels made or attached with an explicit `connect` are stopped only by `stop_kernel` — a later conversation reattaches by id and continues where the last one stopped. `$CLIKERNEL_HOST` overrides the default gateway (`http://127.0.0.1:8787`).
|
|
31
31
|
|
|
32
32
|
## Configuration
|
|
33
33
|
|
|
@@ -41,6 +41,7 @@ Three optional files in `$XDG_CONFIG_HOME/clikernel/` (usually `~/.config/cliker
|
|
|
41
41
|
[gateways.solveit]
|
|
42
42
|
url = "https://solveit.example.com/gate"
|
|
43
43
|
token_env = "SOLVEIT_TOKEN"
|
|
44
|
+
verify = false # optional: accept a self-signed certificate
|
|
44
45
|
```
|
|
45
46
|
|
|
46
47
|
## The stream protocol
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
Modules:
|
|
4
4
|
|
|
5
5
|
- `clikernel.cli`: The stream-protocol frontend: the service on stdin/stdout for token-reading clients
|
|
6
|
-
- `clikernel.core`: Connect to
|
|
6
|
+
- `clikernel.core`: Connect to gateway-hosted kernels and turn execution into concise text
|
|
7
7
|
- `clikernel.mcp`: The MCP frontend: `Client` as tools on stdio
|
|
8
8
|
- `clikernel.skill`: Use the persistent `clikernel` MCP session as the default workspace for any task advanced through live Python execution -- stateful inspection, file-editing workflows, debugging, experiments, API probes, data transforms, or notebook-style work. Read this before writing, running, or debugging Python code in a session with `clikernel` connected.
|
|
9
9
|
- `clikernel.stream`: Streaming JSON-lines worker protocol: nbformat-shaped output events, and a supervisor for select-based UIs."""
|
|
10
10
|
|
|
11
|
-
__version__ = "0.2.
|
|
11
|
+
__version__ = "0.2.4"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""The stream-protocol frontend: the service on stdin/stdout for token-reading clients
|
|
2
2
|
|
|
3
|
-
The `clikernel` command: the delimiter-framed stdin/stdout protocol v1 established (documented in the README, rationale unchanged — a client that reads stdout as tokens wants no echo, a cheap ack byte, and a per-process random delimiter to read until). The protocol machinery ports from v1 verbatim; underneath, the process is now a thin client of a
|
|
3
|
+
The `clikernel` command: the delimiter-framed stdin/stdout protocol v1 established (documented in the README, rationale unchanged — a client that reads stdout as tokens wants no echo, a cheap ack byte, and a per-process random delimiter to read until). The protocol machinery ports from v1 verbatim; underneath, the process is now a thin client of a gateway kernel. Run bare it creates a kernel and stops it again on exit — whoever ran the command made that decision by running it — while `--kernel` attaches to an existing kernel and leaves it exactly as found. Ctrl-C during a long cell translates into a kernel interrupt, jupyter-console style, instead of killing the process.
|
|
4
4
|
|
|
5
5
|
Docs: https://AnswerDotAI.github.io/clikernel/cli.html.md"""
|
|
6
6
|
|
|
@@ -120,10 +120,10 @@ _EXITS = ('exit', 'exit()', 'quit', 'quit()')
|
|
|
120
120
|
|
|
121
121
|
@call_parse
|
|
122
122
|
def main(
|
|
123
|
-
host:str='', # Gateway: empty for the
|
|
123
|
+
host:str='', # Gateway: empty for the local default, a `gateways.toml` name, or a URL
|
|
124
124
|
kernel:str='', # Kernel id (or unique prefix) to attach to; empty creates a kernel, stopped again on exit
|
|
125
125
|
):
|
|
126
|
-
"The `clikernel` console script: the stream protocol over one
|
|
126
|
+
"The `clikernel` console script: the stream protocol over one gateway kernel"
|
|
127
127
|
signal.signal(signal.SIGINT, signal.default_int_handler)
|
|
128
128
|
print("please wait, loading...", flush=True)
|
|
129
129
|
loop = asyncio.new_event_loop()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
"""Connect to
|
|
1
|
+
"""Connect to gateway-hosted kernels and turn execution into concise text
|
|
2
2
|
|
|
3
|
-
clikernel is the LLM side of a two-process design:
|
|
3
|
+
clikernel is the LLM side of a two-process design: a gateway ([rustygate](https://github.com/AnswerDotAI/rustygate)) runs all the time and hosts the kernels; clikernel starts and stops with each conversation and holds nothing but a pointer. This module is the whole client: gateway resolution from `gateways.toml`, the concise-text rendering contract (ANSI-stripped, capped tracebacks), delivery of `startup.py` and `inspectors.py` into freshly created kernels, and `Client` — connect (create or attach), execute, interrupt, restart, stop, list. The MCP and CLI frontends are thin faces over `Client`; nothing here creates or kills a kernel except when asked, and the one scoped kill is opt-in: a kernel created with `auto=True` belongs to its client, ended by that client's next `connect` or by the frontend on the way out.
|
|
4
4
|
|
|
5
5
|
Docs: https://AnswerDotAI.github.io/clikernel/core.html.md"""
|
|
6
6
|
|
|
@@ -25,17 +25,17 @@ def cfg_dir():
|
|
|
25
25
|
return xdg_config_home()/'clikernel'
|
|
26
26
|
|
|
27
27
|
def gateways(cfgdir=None):
|
|
28
|
-
"Named gateways from `gateways.toml`: `{name: {url, token | token_env}}`"
|
|
28
|
+
"Named gateways from `gateways.toml`: `{name: {url, token | token_env, verify}}`"
|
|
29
29
|
p = (Path(cfgdir) if cfgdir else cfg_dir())/'gateways.toml'
|
|
30
30
|
return tomllib.loads(p.read_text()).get('gateways', {}) if p.exists() else {}
|
|
31
31
|
|
|
32
32
|
def resolve(host='', cfgdir=None):
|
|
33
|
-
"`(url, token)` for `host`: empty = the default local gateway, a URL = itself, else a `gateways.toml` name"
|
|
34
|
-
if not host: return os.environ.get('CLIKERNEL_HOST', DEFAULT_URL), os.environ.get('CLIKERNEL_TOKEN')
|
|
35
|
-
if '://' in host: return host, os.environ.get('CLIKERNEL_TOKEN')
|
|
33
|
+
"`(url, token, verify)` for `host`: empty = the default local gateway, a URL = itself, else a `gateways.toml` name"
|
|
34
|
+
if not host: return os.environ.get('CLIKERNEL_HOST', DEFAULT_URL), os.environ.get('CLIKERNEL_TOKEN'), True
|
|
35
|
+
if '://' in host: return host, os.environ.get('CLIKERNEL_TOKEN'), True
|
|
36
36
|
g = gateways(cfgdir).get(host)
|
|
37
37
|
if g is None: raise ValueError(f"unknown gateway {host!r}: not a URL, and not in {cfg_dir()/'gateways.toml'}")
|
|
38
|
-
return g['url'], g.get('token') or os.environ.get(g.get('token_env','')) or None
|
|
38
|
+
return g['url'], g.get('token') or os.environ.get(g.get('token_env','')) or None, g.get('verify', True)
|
|
39
39
|
|
|
40
40
|
|
|
41
41
|
# %% ../nbs/00_core.ipynb #0d846754
|
|
@@ -115,8 +115,8 @@ async def connect(self:Client, host='', kernel='', auto=False):
|
|
|
115
115
|
except Exception as e:
|
|
116
116
|
self.kc,self.kid,self.auto = None,None,False
|
|
117
117
|
note = f'\nnote: stopping the auto kernel failed ({e})'
|
|
118
|
-
url,tok = resolve(host, self.cfgdir)
|
|
119
|
-
mgr = JupyAsyncMultiKernelManager(url, token=tok)
|
|
118
|
+
url,tok,ver = resolve(host, self.cfgdir)
|
|
119
|
+
mgr = JupyAsyncMultiKernelManager(url, token=tok, verify=ver)
|
|
120
120
|
ks = await mgr.list_kernels() # verify reachability and auth now, loudly
|
|
121
121
|
if kernel:
|
|
122
122
|
kid = first(k['id'] for k in ks if k['id'].startswith(kernel))
|
|
@@ -156,8 +156,8 @@ async def execute(self:Client, code):
|
|
|
156
156
|
async def list_kernels(self:Client, host=''):
|
|
157
157
|
"One line per kernel on the gateway (the current one if `host` is empty and connected)"
|
|
158
158
|
if host or self.mgr is None:
|
|
159
|
-
url,tok = resolve(host, self.cfgdir)
|
|
160
|
-
mgr = JupyAsyncMultiKernelManager(url, token=tok)
|
|
159
|
+
url,tok,ver = resolve(host, self.cfgdir)
|
|
160
|
+
mgr = JupyAsyncMultiKernelManager(url, token=tok, verify=ver)
|
|
161
161
|
ks = await mgr.list_kernels()
|
|
162
162
|
await mgr.aclose()
|
|
163
163
|
else: ks = await self.mgr.list_kernels()
|
|
@@ -33,7 +33,7 @@ def mk_server(c:Client):
|
|
|
33
33
|
"An `MCPServer` whose tools close over `c`: connect, execute, and the lifecycle verbs"
|
|
34
34
|
alock = asyncio.Lock()
|
|
35
35
|
async def connect(
|
|
36
|
-
host:str='', # Gateway: empty for the
|
|
36
|
+
host:str='', # Gateway: empty for the local default, a `gateways.toml` name, or a URL
|
|
37
37
|
kernel:str='', # Kernel id (or unique prefix) to attach to; empty creates a fresh kernel
|
|
38
38
|
)->str:
|
|
39
39
|
"Connect to a kernel. With `kernel`: attach to that existing kernel exactly as it is (nothing is run) - this is how a later conversation returns to live state, and how to reach a kernel someone else created. Without: create a fresh kernel, run the user's startup.py in it, and install their inspectors; the reply includes the new kernel's id (reusable in a later `connect`) and the startup output. Kernels made or attached this way persist until explicitly stopped: disconnecting, switching, and conversation end never kill them. (Connecting also immediately stops the auto kernel, if `execute` had created one.)"
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# Core idea
|
|
4
4
|
|
|
5
|
-
clikernel connects this conversation to Jupyter kernels hosted by a
|
|
5
|
+
clikernel connects this conversation to Jupyter kernels hosted by a gateway server (rustygate) that runs all the time, independently of any conversation. State lasts the whole conversation -- imports, live objects, monkeypatches, cached results -- and a kernel you `connect` explicitly persists across conversations too. Treat it as a notebook-style workbench, not a one-shot script runner.
|
|
6
6
|
|
|
7
7
|
Prefer it over one-off Python scripts (`python -c`, shell heredocs) whenever you need to inspect runtime behavior, test an idea, call a Python API, examine package state, run a live probe, or iterate on an implementation detail. Prefer in-kernel tools over shell equivalents when they exist: file search and directory listing go through the `rgapi` pyskill (`rg()`/`fd()`/`ls()`), and GitHub and local git work through the `ghapi` pyskill, when those are installed. Shell commands remain the right tool for project test/build commands and non-Python tools. Run them through the harness's shell tool, never `subprocess`/`os.system` from the kernel, which would bypass the harness's permission hooks.
|
|
8
8
|
|
|
@@ -14,9 +14,9 @@ The lifecycle has one implicit convenience and no implicit destruction beyond it
|
|
|
14
14
|
- Returning to earlier work (the user asks to continue where a previous conversation left off, or to use their solveit kernel): `list_kernels` to see what's running, then `connect` with the kernel id (or unique prefix). Attach runs nothing -- the kernel's live state is the point.
|
|
15
15
|
- End of work: an auto kernel stops itself with the conversation. `stop_kernel` an explicitly created kernel when it was for this task only; leave it running if the user wants to return to it, and tell the user its id so they can.
|
|
16
16
|
|
|
17
|
-
`restart` gives the current kernel a genuinely fresh interpreter under the same id (redo imports after it); `interrupt` stops a too-long `execute` while keeping state. If a reply says the kernel died, `connect` again. If `connect` fails because the gateway is unreachable, the
|
|
17
|
+
`restart` gives the current kernel a genuinely fresh interpreter under the same id (redo imports after it); `interrupt` stops a too-long `execute` while keeping state. If a reply says the kernel died, `connect` again. If `connect` fails because the gateway is unreachable, the gateway server is not running -- report that to the user rather than working around it.
|
|
18
18
|
|
|
19
|
-
Remote gateways (a
|
|
19
|
+
Remote gateways (a rustygate or solveit instance elsewhere) are the same verbs with a `host`: a name from `~/.config/clikernel/gateways.toml` (`[gateways.<name>]` tables with `url`, `token` or `token_env`, and optional `verify = false` for self-signed TLS) or a URL. Tokens live in the config file, never in tool arguments.
|
|
20
20
|
|
|
21
21
|
# Notebook magics
|
|
22
22
|
|
|
@@ -7,7 +7,7 @@ line in (`{"op":"exec","id":n,"code":...}`, `{"op":"complete","id":n,"code":...,
|
|
|
7
7
|
(stream/display_data/execute_result/error) arrive as `out` events with the
|
|
8
8
|
nbformat dict intact (mime bundles included), then a `done` event. Interrupt is
|
|
9
9
|
not on the wire: the supervisor sends SIGINT to the worker process, which the
|
|
10
|
-
worker maps to a kernel interrupt. The worker connects to
|
|
10
|
+
worker maps to a kernel interrupt. The worker connects to a gateway (URL as
|
|
11
11
|
argv[0], default local; a kernel id as argv[1] attaches instead of creating),
|
|
12
12
|
so the compact protocol exercises the full real stack -- which is its point:
|
|
13
13
|
a self-contained test client, and a reminder to keep the layers flexible.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: clikernel
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
4
4
|
Summary: Serve persistent Jupyter kernels to LLMs as concise text, over MCP or a plain stream protocol
|
|
5
5
|
Author: clikernel contributors
|
|
6
6
|
License: Apache-2.0
|
|
@@ -18,7 +18,7 @@ Requires-Dist: aidialog>=0.0.7
|
|
|
18
18
|
Requires-Dist: pillow
|
|
19
19
|
Provides-Extra: dev
|
|
20
20
|
Requires-Dist: fastship; extra == "dev"
|
|
21
|
-
Requires-Dist:
|
|
21
|
+
Requires-Dist: rustygate>=0.1.1; extra == "dev"
|
|
22
22
|
Dynamic: license-file
|
|
23
23
|
|
|
24
24
|
# clikernel
|
|
@@ -26,7 +26,7 @@ Dynamic: license-file
|
|
|
26
26
|
|
|
27
27
|
<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->
|
|
28
28
|
|
|
29
|
-
`clikernel` gives an LLM agent a persistent Python workbench built from two processes. A [
|
|
29
|
+
`clikernel` gives an LLM agent a persistent Python workbench built from two processes. A gateway ([rustygate](https://github.com/AnswerDotAI/rustygate)) runs all the time and hosts real Jupyter kernels ([ipymini](https://github.com/AnswerDotAI/ipymini) by default); kernels live there and persist until explicitly stopped. `clikernel` itself starts and stops with each conversation: a small translator the MCP host launches, speaking MCP to the model and the Jupyter kernels API to the gateway. Outputs come back as concise text — a bare `42` for a single result, tagged sections for several, tracebacks ANSI-stripped and capped.
|
|
30
30
|
|
|
31
31
|
Because kernels outlive conversations, an agent can `connect` back to yesterday’s kernel (or the user’s live solveit kernel) and find its state intact. Kernels created or attached explicitly are never stopped implicitly: only `stop_kernel` ends them. The one exception is the auto kernel: an `execute` with nothing connected creates a kernel scoped to the conversation, stopped again when the conversation ends or the agent connects elsewhere. Creating a kernel runs the user’s `startup.py` and installs their `inspectors.py` cell-checking rules, delivered as source so remote kernels get the same setup as local ones.
|
|
32
32
|
|
|
@@ -36,10 +36,10 @@ Because kernels outlive conversations, an agent can `connect` back to yesterday
|
|
|
36
36
|
pip install clikernel
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
Plus the resident side: [
|
|
39
|
+
Plus the resident side: [rustygate](https://github.com/AnswerDotAI/rustygate) and a kernel ([ipymini](https://github.com/AnswerDotAI/ipymini) by default). Start the gateway (and keep it running, e.g. via launchd/systemd):
|
|
40
40
|
|
|
41
41
|
``` sh
|
|
42
|
-
|
|
42
|
+
rustygate --port 8787
|
|
43
43
|
```
|
|
44
44
|
|
|
45
45
|
## Use with an MCP host
|
|
@@ -50,7 +50,7 @@ Register the stdio server with your MCP host, e.g. for Claude Code:
|
|
|
50
50
|
claude mcp add clikernel -- clikernel-mcp
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
-
The tools mirror
|
|
53
|
+
The tools mirror the gateway’s kernel API plus one composite: `connect` (create a fresh kernel — running `startup.py` and installing inspectors — or attach to an existing one by id), `execute` (run code, get concise text), `list_kernels`, `stop_kernel`, `restart`, and `interrupt`. An `execute` with no kernel connected auto-creates one, scoped to the conversation: it stops at conversation end, or when `connect` moves elsewhere. Kernels made or attached with an explicit `connect` are stopped only by `stop_kernel` — a later conversation reattaches by id and continues where the last one stopped. `$CLIKERNEL_HOST` overrides the default gateway (`http://127.0.0.1:8787`).
|
|
54
54
|
|
|
55
55
|
## Configuration
|
|
56
56
|
|
|
@@ -64,6 +64,7 @@ Three optional files in `$XDG_CONFIG_HOME/clikernel/` (usually `~/.config/cliker
|
|
|
64
64
|
[gateways.solveit]
|
|
65
65
|
url = "https://solveit.example.com/gate"
|
|
66
66
|
token_env = "SOLVEIT_TOKEN"
|
|
67
|
+
verify = false # optional: accept a self-signed certificate
|
|
67
68
|
```
|
|
68
69
|
|
|
69
70
|
## The stream protocol
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
"The stream protocol round trip: a real worker subprocess against
|
|
1
|
+
"The stream protocol round trip: a real worker subprocess against a spawned rustygate."
|
|
2
2
|
import select, sys, time
|
|
3
3
|
import pytest
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
@pytest.fixture(scope="module")
|
|
7
7
|
def gateway():
|
|
8
|
-
from
|
|
9
|
-
|
|
10
|
-
yield
|
|
11
|
-
|
|
8
|
+
from rustygate.tools import start_gateway
|
|
9
|
+
g = start_gateway()
|
|
10
|
+
yield g.url
|
|
11
|
+
g.stop()
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
def _drain_until(w, pred, timeout=30):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|