Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ permissions:
contents: read
jobs:
verify:
timeout-minutes: 20
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
Expand Down Expand Up @@ -48,7 +48,7 @@ jobs:
openssh-integration:
name: OpenSSH integration (Python ${{ matrix.python-version }}, ${{ matrix.dependency-label }})
runs-on: ubuntu-latest
timeout-minutes: 25
timeout-minutes: 35
strategy:
fail-fast: false
matrix:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
build:
name: Verify and attest release artifacts
runs-on: ubuntu-latest
timeout-minutes: 20
timeout-minutes: 30
permissions:
contents: read
id-token: write
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,5 @@ htmlcov/
!/unittest/test_update_check.py

!/unittest/test_interactive_console.py

!/unittest/test_embedded_console.py
25 changes: 16 additions & 9 deletions AI_GUIDE.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
# SSHScript for AI agents

**Documentation contract: SSHScript 6.0.0; Python 3.11+.**
**Documentation contract: SSHScript 6.1.0; Python 3.11+.**

This guide targets the 6.0.0 source contract and verified local wheels.
[5.0.0 remains the latest published package](https://pypi.org/project/sshscript/5.0.0/);
source publication and CI do not upload 6.0.0 to PyPI.
This guide targets SSHScript 6.1.0 and its public Console integration API.
Install `sshscript==6.1.0` and verify the imported package version.
For nested SSH, provision known_hosts on each calling host: `"parent"` is the
default. Read the 4.0.2-to-5.0 migration before reusing older automation.

Expand All @@ -19,14 +18,22 @@ workflow. Its folder contains the supporting guide; copy the entire
It teaches SSHScript usage, not development of SSHScript itself or a particular
recipe library. Agents without skill support can read these Markdown files.

## Interactive console (6.0)
## Borrowed Session integration (unified Console API)

Use `from sshscript import Console` and `with Console(existing_session)` for an
existing authorized connection. Read [the full host contract](EMBEDDED_CONSOLE.md)
before driving it. Require structured completion/exit status and confirmed reuse;
keep host account policy and credentials in the resolver. A prompt/silence is not
a success signal. See the bundled skill for the matching agent guidance.

## Interactive console (6.1)

For step-by-step use from a human or UI agent's retained terminal, see the
[console guide](CONSOLE.md) and the agent guide's
[console instructions](skills/sshscript/references/agent-guide.md#interactive-console-60).
[console instructions](skills/sshscript/references/agent-guide.md#interactive-console-61).
Run `python3 -B sshscript.py console` from the matching development checkout;
an installed 6.0.0 wheel provides `sshscript console`. Published 5.0.0 does not
include this console.
an installed 6.1.0 wheel provides `sshscript console`. Use public
Console(session) for host integration; older 6.0 console internals are removed.

Input remains Python with dollar commands: `$hostname` executes a command,
`print($.stdout)` reads its result, and variables persist between inputs.
Expand Down Expand Up @@ -109,4 +116,4 @@ Validate links, skill structure, example behavior, source/ZIP equality and
the rendered website. Source and website publishing are separate operations;
copying files does not publish either of them.

*Last modified: 2026-10-22 23:24:55*
*Last modified: 2026-10-23 18:39:31*
30 changes: 19 additions & 11 deletions API_GUIDE.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Recommended SSHScript API

This is the canonical entry point for new code and AI-generated examples.
**Current source contract: 6.0.0**, Python 3.11+. Sections marked **4.0** require
**Current release contract: 6.1.0**, Python 3.11+. Sections marked **4.0** require
SSHScript 4.0.2 or newer; installing `sshscript==3.1.5` does not provide them.
Examples labelled 3.1.5 remain verified compatibility examples for that baseline.
Pin the exact artifact version and record it with execution diagnostics when
Expand Down Expand Up @@ -31,8 +31,8 @@ identifiers and handoff reports are future work, not a current API guarantee.

## One command, one retained result (4.0; compatible with 3.1.5)

For this source contract, install a verified 6.0.0 wheel. Once published, pin
`sshscript==6.0.0`; the source push does not itself upload a PyPI package.
Install `sshscript==6.1.0` for this contract. Verify the installed version and
artifact before operational use; source publication alone does not install it.
For reproducing only the historical compatibility baseline, explicitly install
`sshscript==3.1.5`; the 4.0 sections require the current release.
An argv list is one command, not a batch. Shell-looking argument text stays data.
Expand Down Expand Up @@ -235,7 +235,7 @@ print(result.stdout)

## 4.0: settings and managed jobs

Use SSHScript 6.0.0 for the following examples. CLI `-v`, `--stderr`, and
Use SSHScript 6.1.0 for the following examples. CLI `-v`, `--stderr`, and
`--debug` override root defaults only for that execution. Child Sessions copy
their parent's settings. `set()` validates before changing anything; `get()`
returns a copy. Explicit command options override Session policy.
Expand All @@ -254,7 +254,7 @@ the settings dictionary at creation; the policy object itself is shared, so
custom policies with mutable state must account for reuse. Changing the setting
does not reconfigure existing connections.

<!-- example: {"id":"settings", "profile":"6.0.0", "stdout":"True\n1\n"} -->
<!-- example: {"id":"settings", "profile":"6.1.0", "stdout":"True\n1\n"} -->
```python
from contextlib import closing
import paramiko
Expand All @@ -276,7 +276,7 @@ semantics; do not combine it with `command_timeout`.
For an indefinite command such as tcpdump, use `start(timeout=None)`. This safe
local example uses a small Python process instead of capturing network traffic:

<!-- example: {"id":"stop", "profile":"6.0.0", "stdout":"cancelled confirmed\n"} -->
<!-- example: {"id":"stop", "profile":"6.1.0", "stdout":"cancelled confirmed\n"} -->
```python
from contextlib import closing
import sys
Expand Down Expand Up @@ -315,7 +315,7 @@ legacy SSH behavior which appended a newline to strings: if a consumer
needs a line, migrate `input=password` to `input=password + "\n"` explicitly.
Published 3.1.5 still has the legacy SSH behavior.

<!-- example: {"id":"exact-stdin", "profile":"6.0.0", "stdout":"'abc'\n'abc\\n'\n"} -->
<!-- example: {"id":"exact-stdin", "profile":"6.1.0", "stdout":"'abc'\n'abc\\n'\n"} -->
```python
from contextlib import closing
import sys
Expand Down Expand Up @@ -384,7 +384,7 @@ or unpack exactly `stdout, stderr, exitcode`. This changes the released 3.1.5
console contract: old `stdout, stderr = shell(command)` must be migrated.
Shell calls accept command strings; argv execution belongs to Session.

<!-- example: {"id":"shell-result", "profile":"6.0.0", "stdout":"first 0\n"} -->
<!-- example: {"id":"shell-result", "profile":"6.1.0", "stdout":"first 0\n"} -->
```python
from contextlib import closing
from sshscript import Session
Expand Down Expand Up @@ -440,7 +440,7 @@ matching: `iter_stdout()` yields chunks, not packets or lines. Put `stop()` in
`finally`, so condition matches, exceptions, and KeyboardInterrupt all trigger
cleanup. This credential-free example uses a continuous Python process:

<!-- example: {"id":"stream-condition", "profile":"6.0.0", "stdout":"cancelled confirmed\n"} -->
<!-- example: {"id":"stream-condition", "profile":"6.1.0", "stdout":"cancelled confirmed\n"} -->
```python
from contextlib import closing
import sys
Expand Down Expand Up @@ -505,7 +505,7 @@ the current Bash console's channel, cwd, environment and identity. It does not
launch through the underlying Session. Ordinary `console(command)` / `$command`
still waits and returns `CommandResult`; no migration is needed for finite commands.

<!-- example: {"id":"console-stream", "profile":"6.0.0", "stdout":"cancelled confirmed\nconsole-alive\n"} -->
<!-- example: {"id":"console-stream", "profile":"6.1.0", "stdout":"cancelled confirmed\nconsole-alive\n"} -->
```python
from contextlib import closing
import shlex
Expand Down Expand Up @@ -765,6 +765,14 @@ The same source is mirrored into the website; edit this file, then use
production details remain in the full documentation.


## Embed an existing Session (unified Console API)

`from sshscript import Console` provides a supported integration surface without
editing reserved variables. `Console(session)` borrows the connection, while
`ownership="owned"` explicitly transfers ownership. Read the complete
[API and lifecycle contract](EMBEDDED_CONSOLE.md) for structured updates,
credentials, identity, PTY limits, exceptions and runnable examples.

## Interactive console and script completion (6.0)

See [the console guide](CONSOLE.md) for the 6.0 Python-first dollar REPL, retained
Expand All @@ -782,4 +790,4 @@ ScriptResult with exitcode, stopped and message attributes; run_file() returns
the integer status. Cleanup failures still propagate. Python SystemExit remains
separate from normal script completion.

*Last modified: 2026-10-22 23:24:55*
*Last modified: 2026-10-23 18:39:31*
25 changes: 24 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,28 @@
# Changelog

## 6.1.0 — 2026-10-23

- Add public `Console(existing_session)` for terminal and host/AI-agent interaction
over an existing Session, including target Sessions reached through jumps,
without reconnecting or changing authentication/host-key sources.
- Retain Python variables, shell cwd/environment and interactive program state.
Expose structured submit/poll/input/interrupt/EOF and completion results.
- Add account credential/policy and switch-result hooks, separate connection login
identity from effective shell identity, and expose the environment stack.
- Borrow Sessions by default. Unwind console-owned layers and confirm Session
reuse; initialization/restoration/cleanup failures report unknown explicitly.
- Fix lost prompt readiness after program Ctrl-C. Keep waiting_input after a
protocol match, running for ignored/unfinished interrupts, and real exit status
for exiting programs; support delayed prompt recovery through poll.
- Use one Console implementation for CLI and embedding. Remove old internal
InteractiveConsole/open()/execute() entry points and :help/:quit aliases; use
Console(session), submit(), help/quit and structured status instead.
- Add complete terminal/agent examples and the matching AI skill. OpsFeast reports
local/simulated and browser UI revalidation passed. This is distinct from real
SSH/jump/PAM/su/sudo validation; release CI records its own disposable native tests.
- Development-stage releases do not guarantee backward compatibility unless
explicitly requested. Minor releases may include the differences listed above.

## 6.0.0 — 2026-10-22 (incompatible source contract)

- Add console helpers: help/help(), help(object), exit/exit() to leave one
Expand Down Expand Up @@ -314,4 +337,4 @@ verified artifacts in the
[Unreleased]: https://github.com/iapyeh/sshscript/compare/v3.1.4...release
[3.1.4]: https://github.com/iapyeh/sshscript/releases/tag/v3.1.4

*Last modified: 2026-10-22 23:24:55*
*Last modified: 2026-10-23 18:39:31*
25 changes: 17 additions & 8 deletions CONSOLE.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,32 @@
# Interactive console (6.0)
# Interactive console (6.1)

The SSHScript 6.0 console runs from the flat development checkout:
The SSHScript 6.1 console runs from the flat development checkout:

```sh
python3 -B sshscript.py console
```

Use Python 3.11 or newer with the project's dependencies. An installed 6.0.0
wheel provides `sshscript console`. Published 5.0.0 lacks this feature; this
source version update does not upload a package. No installation into the
Use Python 3.11 or newer with the project's dependencies. Install
`sshscript==6.1.0` for `sshscript console`. No installation into the flat
development checkout is required.

## Embed an existing Session (new API)

Use the public `sshscript.Console` API for borrowed Sessions, host-driven updates,
credential hooks and explicit cleanup/reuse reporting. Read
[the embedded console contract](EMBEDDED_CONSOLE.md) and its executable examples.
The CLI uses the same Console core and ownership contract. Earlier 6.0 CLI
compatibility is not maintained; see the linked contract for removed entry points
and aliases. This guide describes the 6.1.0 release contract.

## A Python console with dollar syntax

Input is Python by default, with SSHScript dollar syntax available in the same
namespace. No Python/shell language-mode switching is required:

```text
local [user]> hostname
NameError: name 'hostname' is not defined
NameError: operation failed; inspect error for details.
local [user]> hostname = "a Python variable"
local [user]> hostname
'a Python variable'
Expand Down Expand Up @@ -165,7 +173,8 @@ Readiness or silence is not interpreted as a successful process exit.
- In an enter() program, Ctrl-C/Ctrl-D are forwarded as raw terminal control
characters, without an added newline. Only a detected return to the parent
shell closes that program layer; the Python console stays available.
- `:help` and `:quit` remain aliases; `:status` shows the environment stack.
- Use `help`/`help()` and `quit`/`quit()`. The former `:help` and `:quit` aliases
are removed; `:status` shows the environment stack.
The Python helper names can be shadowed by assignment, just like Python names;
the colon aliases and `control.quit()` remain available.
- Python `sys.exit()` retains SystemExit semantics. The interactive boundary
Expand Down Expand Up @@ -205,4 +214,4 @@ Input now follows Python/dollar syntax throughout; prefix shell commands with `$
Type `help` for a reminder. Operation failures display their type without
reprinting credential-bearing input; inspect `error` at the prompt for details.

*Last modified: 2026-10-22 23:24:55*
*Last modified: 2026-10-23 18:39:31*
Loading
Loading