remote #
Remote is Python running under another target's identity and capability set, through a host-owned namespace. You get what the target advertises, and a target that is not there is refused rather than emulated locally.
from klbr import remote
async with remote.connect("build-host") as kernel:
print(kernel.identity.version, list(kernel.capabilities))
the trail #
connect ensures a namespace: it starts a real worker on that target and returns a handle
bound to the namespace and generation it was issued. The binding is what makes a later call
addressable, and a call whose binding is gone fails instead of quietly reconnecting.
kernel = await remote.connect("build-host")
print(await kernel.control.eval("sys.version_info[:2]"))
Control calls return handles, not values. Await the handle, poll() for what is ready, or
watch it while it runs:
handle = kernel.control.execute("import time; time.sleep(30)")
print(handle.running, handle.output())
outcome = await handle.acancel()
print(outcome.state, outcome.confirmed)
Cancellation is three-valued on purpose: confirmed, requested, unknown. unknown means
the transport did not confirm it, which is not the same as "stopped" - say so rather than
assume it.
Shell is its own typed call with its own result type:
bash = await kernel.bash("uname -a")
print(bash.exit_code, bash.stdout, bash.stderr_truncated)
the target's tree, with your own verbs #
The five verbs you already have locally exist on the handle too, and they return the same
shapes, because the target runs the same klbr.workspace this cell does:
listing = await kernel.ls("src", recursive=True) # Listing of Entry
found = await kernel.search("needle", ".") # SearchResult of TextMatch
file = await kernel.open_file("src/main.py") # not a read, and not a stat
print(await file.text())
await file.write("print('rewritten')\n")
await file.replace("rewritten", "replaced")
ran = await kernel.run(("cargo", "check"), cwd=".") # RunResult; argv, not a shell string
await kernel.delete("build", recursive=True)
open_file gives back a handle with the local names - .relative .text() .read() .exists() .hashlines() .write() .replace() .edit() - and every call is a remote request.
The refusals are the target's own, because they are the same rule and not a second copy of
it: delete("build") on a non-empty directory comes back saying how many entries are in the
way and to pass recursive=True. A run deadline that expires is reported in the result
(timed_out, completed=False) rather than raised.
Underneath, kernel.pathlib, kernel.os, kernel.sys, kernel.shutil and kernel.bash
stay available as the lower-level mirror of the target's own namespaces, one remote request
per call. Use them for what the five verbs do not cover; use the verbs for the tree.
Advertised capabilities become attributes. If a target publishes a skill, it appears as
kernel.<capability>.<callable>, and calling it returns a handle. Reflection gives you the
real signature instead of a guess:
print(await kernel.some_capability.some_call.signature())
refusals #
There is no SSH fallback and no local fallback. If no placement is configured, every target is
unavailable, and nothing will quietly run the work here instead. A capability that is not
advertised is refused before anything runs. Opaque call dispatch to a relay target is
deliberately refused until that target runs a dispatcher - a refusal, not something to retry.