sshscript 6.0.0__tar.gz → 6.1.0__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.
- {sshscript-6.0.0 → sshscript-6.1.0}/.github/workflows/ci.yml +2 -2
- {sshscript-6.0.0 → sshscript-6.1.0}/.github/workflows/release.yml +1 -1
- {sshscript-6.0.0 → sshscript-6.1.0}/.gitignore +2 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/AI_GUIDE.md +16 -9
- {sshscript-6.0.0 → sshscript-6.1.0}/API_GUIDE.md +19 -11
- {sshscript-6.0.0 → sshscript-6.1.0}/CHANGELOG.md +24 -1
- {sshscript-6.0.0 → sshscript-6.1.0}/CONSOLE.md +17 -8
- sshscript-6.1.0/EMBEDDED_CONSOLE.md +200 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/MANIFEST.in +5 -0
- {sshscript-6.0.0/src/sshscript.egg-info → sshscript-6.1.0}/PKG-INFO +27 -20
- {sshscript-6.0.0 → sshscript-6.1.0}/README.md +25 -18
- {sshscript-6.0.0 → sshscript-6.1.0}/RELEASING.md +3 -1
- {sshscript-6.0.0 → sshscript-6.1.0}/VERSIONING.md +46 -19
- sshscript-6.1.0/examples/embedded_agent.py +36 -0
- sshscript-6.1.0/examples/embedded_terminal.py +21 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/pyproject.toml +1 -1
- {sshscript-6.0.0 → sshscript-6.1.0}/skills/sshscript/SKILL.md +23 -5
- {sshscript-6.0.0 → sshscript-6.1.0}/skills/sshscript/references/agent-guide.md +35 -11
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/__init__.py +3 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/__init__.pyi +2 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/_version.py +1 -1
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/interactive.py +565 -194
- sshscript-6.1.0/src/sshscript/interactive.pyi +71 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/sshscript.py +3 -1
- sshscript-6.1.0/src/sshscript/unittest/test_embedded_console.py +471 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_interactive_console.py +86 -86
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_native_auth_runner.py +2 -2
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_native_console_authentication.py +28 -1
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_openssh_integration.py +10 -1
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_public_types.py +2 -1
- {sshscript-6.0.0 → sshscript-6.1.0/src/sshscript.egg-info}/PKG-INFO +27 -20
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript.egg-info/SOURCES.txt +5 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/tools/check_public_types.py +6 -1
- {sshscript-6.0.0 → sshscript-6.1.0}/tools/prepare_release.py +3 -1
- {sshscript-6.0.0 → sshscript-6.1.0}/tools/sync_ai_docs.py +7 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/tools/sync_api_guide.py +1 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/.github/dependabot.yml +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/.github/pull_request_template.md +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/.github/workflows/codeql.yml +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/CODE_OF_CONDUCT.md +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/CONTRIBUTING.md +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/EXCEPTIONS.md +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/LICENSE.txt +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/SECURITY.md +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/SUPPORT.md +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/setup.cfg +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/_sshscript_cli.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/_runtime.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/channelgeneric.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/channelssh.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/channelsubprocess.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/channelutils.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/commandjob.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/commandjob.pyi +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/commandresult.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/commandresult.pyi +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/consolejob.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/dollar.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/dollarchanger.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/dollarparser.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/errorutils.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/patching.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/py.typed +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/scriptcontrol.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/session.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/session.pyi +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/sessionsettings.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/sessionwrapper.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/sessionwrapper.pyi +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/spyimporter.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/sshconfig.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/stdio.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/tokenparser.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/unittest/README.console-authentication.md +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/unittest/README.console-results.md +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/unittest/README.dollar-syntax-tests.md +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/unittest/README.module-tests.md +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/unittest/check_package_asserts.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/unittest/dollar_syntax.spy +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/unittest/dollar_syntax_fixture.spy +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/unittest/language_fixture.spy +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_channelgeneric_expect.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_check_file.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_command_api.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_command_job.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_console_authentication.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_console_job.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_console_results.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_dollar_command_escapes.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_file_transfer.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_logger_api.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_logger_integration.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_production_contract.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_python_version.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_recommended_examples.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_release_inputs.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_session_close_reporting.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_session_proxy_cleanup.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_session_settings.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_spy_source_mapping.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_spy_thread_session.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_ssh_config.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_ssh_security.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_sshscript_dollar_syntax.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_sshscript_module.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_stdin_contract.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_stdio_dynamic_string.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_syntax_error.spy +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_update_check.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_version_policy.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript.egg-info/dependency_links.txt +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript.egg-info/entry_points.txt +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript.egg-info/requires.txt +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/src/sshscript.egg-info/top_level.txt +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/tools/check_baseline_examples.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/tools/check_release.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/tools/publish_release.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/tools/run_checks.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/tools/run_native_auth_ci.py +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/tools/setup_openssh_ci.sh +0 -0
- {sshscript-6.0.0 → sshscript-6.1.0}/tools/version_policy.py +0 -0
|
@@ -4,7 +4,7 @@ permissions:
|
|
|
4
4
|
contents: read
|
|
5
5
|
jobs:
|
|
6
6
|
verify:
|
|
7
|
-
timeout-minutes:
|
|
7
|
+
timeout-minutes: 30
|
|
8
8
|
strategy:
|
|
9
9
|
fail-fast: false
|
|
10
10
|
matrix:
|
|
@@ -48,7 +48,7 @@ jobs:
|
|
|
48
48
|
openssh-integration:
|
|
49
49
|
name: OpenSSH integration (Python ${{ matrix.python-version }}, ${{ matrix.dependency-label }})
|
|
50
50
|
runs-on: ubuntu-latest
|
|
51
|
-
timeout-minutes:
|
|
51
|
+
timeout-minutes: 35
|
|
52
52
|
strategy:
|
|
53
53
|
fail-fast: false
|
|
54
54
|
matrix:
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
# SSHScript for AI agents
|
|
2
2
|
|
|
3
|
-
**Documentation contract: SSHScript 6.
|
|
3
|
+
**Documentation contract: SSHScript 6.1.0; Python 3.11+.**
|
|
4
4
|
|
|
5
|
-
This guide targets
|
|
6
|
-
|
|
7
|
-
source publication and CI do not upload 6.0.0 to PyPI.
|
|
5
|
+
This guide targets SSHScript 6.1.0 and its public Console integration API.
|
|
6
|
+
Install `sshscript==6.1.0` and verify the imported package version.
|
|
8
7
|
For nested SSH, provision known_hosts on each calling host: `"parent"` is the
|
|
9
8
|
default. Read the 4.0.2-to-5.0 migration before reusing older automation.
|
|
10
9
|
|
|
@@ -19,14 +18,22 @@ workflow. Its folder contains the supporting guide; copy the entire
|
|
|
19
18
|
It teaches SSHScript usage, not development of SSHScript itself or a particular
|
|
20
19
|
recipe library. Agents without skill support can read these Markdown files.
|
|
21
20
|
|
|
22
|
-
##
|
|
21
|
+
## Borrowed Session integration (unified Console API)
|
|
22
|
+
|
|
23
|
+
Use `from sshscript import Console` and `with Console(existing_session)` for an
|
|
24
|
+
existing authorized connection. Read [the full host contract](EMBEDDED_CONSOLE.md)
|
|
25
|
+
before driving it. Require structured completion/exit status and confirmed reuse;
|
|
26
|
+
keep host account policy and credentials in the resolver. A prompt/silence is not
|
|
27
|
+
a success signal. See the bundled skill for the matching agent guidance.
|
|
28
|
+
|
|
29
|
+
## Interactive console (6.1)
|
|
23
30
|
|
|
24
31
|
For step-by-step use from a human or UI agent's retained terminal, see the
|
|
25
32
|
[console guide](CONSOLE.md) and the agent guide's
|
|
26
|
-
[console instructions](skills/sshscript/references/agent-guide.md#interactive-console-
|
|
33
|
+
[console instructions](skills/sshscript/references/agent-guide.md#interactive-console-61).
|
|
27
34
|
Run `python3 -B sshscript.py console` from the matching development checkout;
|
|
28
|
-
an installed 6.
|
|
29
|
-
|
|
35
|
+
an installed 6.1.0 wheel provides `sshscript console`. Use public
|
|
36
|
+
Console(session) for host integration; older 6.0 console internals are removed.
|
|
30
37
|
|
|
31
38
|
Input remains Python with dollar commands: `$hostname` executes a command,
|
|
32
39
|
`print($.stdout)` reads its result, and variables persist between inputs.
|
|
@@ -109,4 +116,4 @@ Validate links, skill structure, example behavior, source/ZIP equality and
|
|
|
109
116
|
the rendered website. Source and website publishing are separate operations;
|
|
110
117
|
copying files does not publish either of them.
|
|
111
118
|
|
|
112
|
-
*Last modified: 2026-10-
|
|
119
|
+
*Last modified: 2026-10-23 18:39:31*
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Recommended SSHScript API
|
|
2
2
|
|
|
3
3
|
This is the canonical entry point for new code and AI-generated examples.
|
|
4
|
-
**Current
|
|
4
|
+
**Current release contract: 6.1.0**, Python 3.11+. Sections marked **4.0** require
|
|
5
5
|
SSHScript 4.0.2 or newer; installing `sshscript==3.1.5` does not provide them.
|
|
6
6
|
Examples labelled 3.1.5 remain verified compatibility examples for that baseline.
|
|
7
7
|
Pin the exact artifact version and record it with execution diagnostics when
|
|
@@ -31,8 +31,8 @@ identifiers and handoff reports are future work, not a current API guarantee.
|
|
|
31
31
|
|
|
32
32
|
## One command, one retained result (4.0; compatible with 3.1.5)
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
Install `sshscript==6.1.0` for this contract. Verify the installed version and
|
|
35
|
+
artifact before operational use; source publication alone does not install it.
|
|
36
36
|
For reproducing only the historical compatibility baseline, explicitly install
|
|
37
37
|
`sshscript==3.1.5`; the 4.0 sections require the current release.
|
|
38
38
|
An argv list is one command, not a batch. Shell-looking argument text stays data.
|
|
@@ -235,7 +235,7 @@ print(result.stdout)
|
|
|
235
235
|
|
|
236
236
|
## 4.0: settings and managed jobs
|
|
237
237
|
|
|
238
|
-
Use SSHScript 6.
|
|
238
|
+
Use SSHScript 6.1.0 for the following examples. CLI `-v`, `--stderr`, and
|
|
239
239
|
`--debug` override root defaults only for that execution. Child Sessions copy
|
|
240
240
|
their parent's settings. `set()` validates before changing anything; `get()`
|
|
241
241
|
returns a copy. Explicit command options override Session policy.
|
|
@@ -254,7 +254,7 @@ the settings dictionary at creation; the policy object itself is shared, so
|
|
|
254
254
|
custom policies with mutable state must account for reuse. Changing the setting
|
|
255
255
|
does not reconfigure existing connections.
|
|
256
256
|
|
|
257
|
-
<!-- example: {"id":"settings", "profile":"6.
|
|
257
|
+
<!-- example: {"id":"settings", "profile":"6.1.0", "stdout":"True\n1\n"} -->
|
|
258
258
|
```python
|
|
259
259
|
from contextlib import closing
|
|
260
260
|
import paramiko
|
|
@@ -276,7 +276,7 @@ semantics; do not combine it with `command_timeout`.
|
|
|
276
276
|
For an indefinite command such as tcpdump, use `start(timeout=None)`. This safe
|
|
277
277
|
local example uses a small Python process instead of capturing network traffic:
|
|
278
278
|
|
|
279
|
-
<!-- example: {"id":"stop", "profile":"6.
|
|
279
|
+
<!-- example: {"id":"stop", "profile":"6.1.0", "stdout":"cancelled confirmed\n"} -->
|
|
280
280
|
```python
|
|
281
281
|
from contextlib import closing
|
|
282
282
|
import sys
|
|
@@ -315,7 +315,7 @@ legacy SSH behavior which appended a newline to strings: if a consumer
|
|
|
315
315
|
needs a line, migrate `input=password` to `input=password + "\n"` explicitly.
|
|
316
316
|
Published 3.1.5 still has the legacy SSH behavior.
|
|
317
317
|
|
|
318
|
-
<!-- example: {"id":"exact-stdin", "profile":"6.
|
|
318
|
+
<!-- example: {"id":"exact-stdin", "profile":"6.1.0", "stdout":"'abc'\n'abc\\n'\n"} -->
|
|
319
319
|
```python
|
|
320
320
|
from contextlib import closing
|
|
321
321
|
import sys
|
|
@@ -384,7 +384,7 @@ or unpack exactly `stdout, stderr, exitcode`. This changes the released 3.1.5
|
|
|
384
384
|
console contract: old `stdout, stderr = shell(command)` must be migrated.
|
|
385
385
|
Shell calls accept command strings; argv execution belongs to Session.
|
|
386
386
|
|
|
387
|
-
<!-- example: {"id":"shell-result", "profile":"6.
|
|
387
|
+
<!-- example: {"id":"shell-result", "profile":"6.1.0", "stdout":"first 0\n"} -->
|
|
388
388
|
```python
|
|
389
389
|
from contextlib import closing
|
|
390
390
|
from sshscript import Session
|
|
@@ -440,7 +440,7 @@ matching: `iter_stdout()` yields chunks, not packets or lines. Put `stop()` in
|
|
|
440
440
|
`finally`, so condition matches, exceptions, and KeyboardInterrupt all trigger
|
|
441
441
|
cleanup. This credential-free example uses a continuous Python process:
|
|
442
442
|
|
|
443
|
-
<!-- example: {"id":"stream-condition", "profile":"6.
|
|
443
|
+
<!-- example: {"id":"stream-condition", "profile":"6.1.0", "stdout":"cancelled confirmed\n"} -->
|
|
444
444
|
```python
|
|
445
445
|
from contextlib import closing
|
|
446
446
|
import sys
|
|
@@ -505,7 +505,7 @@ the current Bash console's channel, cwd, environment and identity. It does not
|
|
|
505
505
|
launch through the underlying Session. Ordinary `console(command)` / `$command`
|
|
506
506
|
still waits and returns `CommandResult`; no migration is needed for finite commands.
|
|
507
507
|
|
|
508
|
-
<!-- example: {"id":"console-stream", "profile":"6.
|
|
508
|
+
<!-- example: {"id":"console-stream", "profile":"6.1.0", "stdout":"cancelled confirmed\nconsole-alive\n"} -->
|
|
509
509
|
```python
|
|
510
510
|
from contextlib import closing
|
|
511
511
|
import shlex
|
|
@@ -765,6 +765,14 @@ The same source is mirrored into the website; edit this file, then use
|
|
|
765
765
|
production details remain in the full documentation.
|
|
766
766
|
|
|
767
767
|
|
|
768
|
+
## Embed an existing Session (unified Console API)
|
|
769
|
+
|
|
770
|
+
`from sshscript import Console` provides a supported integration surface without
|
|
771
|
+
editing reserved variables. `Console(session)` borrows the connection, while
|
|
772
|
+
`ownership="owned"` explicitly transfers ownership. Read the complete
|
|
773
|
+
[API and lifecycle contract](EMBEDDED_CONSOLE.md) for structured updates,
|
|
774
|
+
credentials, identity, PTY limits, exceptions and runnable examples.
|
|
775
|
+
|
|
768
776
|
## Interactive console and script completion (6.0)
|
|
769
777
|
|
|
770
778
|
See [the console guide](CONSOLE.md) for the 6.0 Python-first dollar REPL, retained
|
|
@@ -782,4 +790,4 @@ ScriptResult with exitcode, stopped and message attributes; run_file() returns
|
|
|
782
790
|
the integer status. Cleanup failures still propagate. Python SystemExit remains
|
|
783
791
|
separate from normal script completion.
|
|
784
792
|
|
|
785
|
-
*Last modified: 2026-10-
|
|
793
|
+
*Last modified: 2026-10-23 18:39:31*
|
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 6.1.0 — 2026-10-23
|
|
4
|
+
|
|
5
|
+
- Add public `Console(existing_session)` for terminal and host/AI-agent interaction
|
|
6
|
+
over an existing Session, including target Sessions reached through jumps,
|
|
7
|
+
without reconnecting or changing authentication/host-key sources.
|
|
8
|
+
- Retain Python variables, shell cwd/environment and interactive program state.
|
|
9
|
+
Expose structured submit/poll/input/interrupt/EOF and completion results.
|
|
10
|
+
- Add account credential/policy and switch-result hooks, separate connection login
|
|
11
|
+
identity from effective shell identity, and expose the environment stack.
|
|
12
|
+
- Borrow Sessions by default. Unwind console-owned layers and confirm Session
|
|
13
|
+
reuse; initialization/restoration/cleanup failures report unknown explicitly.
|
|
14
|
+
- Fix lost prompt readiness after program Ctrl-C. Keep waiting_input after a
|
|
15
|
+
protocol match, running for ignored/unfinished interrupts, and real exit status
|
|
16
|
+
for exiting programs; support delayed prompt recovery through poll.
|
|
17
|
+
- Use one Console implementation for CLI and embedding. Remove old internal
|
|
18
|
+
InteractiveConsole/open()/execute() entry points and :help/:quit aliases; use
|
|
19
|
+
Console(session), submit(), help/quit and structured status instead.
|
|
20
|
+
- Add complete terminal/agent examples and the matching AI skill. OpsFeast reports
|
|
21
|
+
local/simulated and browser UI revalidation passed. This is distinct from real
|
|
22
|
+
SSH/jump/PAM/su/sudo validation; release CI records its own disposable native tests.
|
|
23
|
+
- Development-stage releases do not guarantee backward compatibility unless
|
|
24
|
+
explicitly requested. Minor releases may include the differences listed above.
|
|
25
|
+
|
|
3
26
|
## 6.0.0 — 2026-10-22 (incompatible source contract)
|
|
4
27
|
|
|
5
28
|
- Add console helpers: help/help(), help(object), exit/exit() to leave one
|
|
@@ -314,4 +337,4 @@ verified artifacts in the
|
|
|
314
337
|
[Unreleased]: https://github.com/iapyeh/sshscript/compare/v3.1.4...release
|
|
315
338
|
[3.1.4]: https://github.com/iapyeh/sshscript/releases/tag/v3.1.4
|
|
316
339
|
|
|
317
|
-
*Last modified: 2026-10-
|
|
340
|
+
*Last modified: 2026-10-23 18:39:31*
|
|
@@ -1,16 +1,24 @@
|
|
|
1
|
-
# Interactive console (6.
|
|
1
|
+
# Interactive console (6.1)
|
|
2
2
|
|
|
3
|
-
The SSHScript 6.
|
|
3
|
+
The SSHScript 6.1 console runs from the flat development checkout:
|
|
4
4
|
|
|
5
5
|
```sh
|
|
6
6
|
python3 -B sshscript.py console
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
-
Use Python 3.11 or newer with the project's dependencies.
|
|
10
|
-
|
|
11
|
-
source version update does not upload a package. No installation into the
|
|
9
|
+
Use Python 3.11 or newer with the project's dependencies. Install
|
|
10
|
+
`sshscript==6.1.0` for `sshscript console`. No installation into the flat
|
|
12
11
|
development checkout is required.
|
|
13
12
|
|
|
13
|
+
## Embed an existing Session (new API)
|
|
14
|
+
|
|
15
|
+
Use the public `sshscript.Console` API for borrowed Sessions, host-driven updates,
|
|
16
|
+
credential hooks and explicit cleanup/reuse reporting. Read
|
|
17
|
+
[the embedded console contract](EMBEDDED_CONSOLE.md) and its executable examples.
|
|
18
|
+
The CLI uses the same Console core and ownership contract. Earlier 6.0 CLI
|
|
19
|
+
compatibility is not maintained; see the linked contract for removed entry points
|
|
20
|
+
and aliases. This guide describes the 6.1.0 release contract.
|
|
21
|
+
|
|
14
22
|
## A Python console with dollar syntax
|
|
15
23
|
|
|
16
24
|
Input is Python by default, with SSHScript dollar syntax available in the same
|
|
@@ -18,7 +26,7 @@ namespace. No Python/shell language-mode switching is required:
|
|
|
18
26
|
|
|
19
27
|
```text
|
|
20
28
|
local [user]> hostname
|
|
21
|
-
NameError:
|
|
29
|
+
NameError: operation failed; inspect error for details.
|
|
22
30
|
local [user]> hostname = "a Python variable"
|
|
23
31
|
local [user]> hostname
|
|
24
32
|
'a Python variable'
|
|
@@ -165,7 +173,8 @@ Readiness or silence is not interpreted as a successful process exit.
|
|
|
165
173
|
- In an enter() program, Ctrl-C/Ctrl-D are forwarded as raw terminal control
|
|
166
174
|
characters, without an added newline. Only a detected return to the parent
|
|
167
175
|
shell closes that program layer; the Python console stays available.
|
|
168
|
-
-
|
|
176
|
+
- Use `help`/`help()` and `quit`/`quit()`. The former `:help` and `:quit` aliases
|
|
177
|
+
are removed; `:status` shows the environment stack.
|
|
169
178
|
The Python helper names can be shadowed by assignment, just like Python names;
|
|
170
179
|
the colon aliases and `control.quit()` remain available.
|
|
171
180
|
- Python `sys.exit()` retains SystemExit semantics. The interactive boundary
|
|
@@ -205,4 +214,4 @@ Input now follows Python/dollar syntax throughout; prefix shell commands with `$
|
|
|
205
214
|
Type `help` for a reminder. Operation failures display their type without
|
|
206
215
|
reprinting credential-bearing input; inspect `error` at the prompt for details.
|
|
207
216
|
|
|
208
|
-
*Last modified: 2026-10-
|
|
217
|
+
*Last modified: 2026-10-23 18:39:31*
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# Unified Console API
|
|
2
|
+
|
|
3
|
+
**Contract: SSHScript 6.1.0; Python 3.11+.**
|
|
4
|
+
|
|
5
|
+
Design and lifecycle contract, written before implementation.
|
|
6
|
+
|
|
7
|
+
`from sshscript import Console` exposes the shared Python/dollar console
|
|
8
|
+
on an existing `Session`. `Console(session, ownership="borrowed")` opens only a
|
|
9
|
+
new Bash channel, without connect(), authentication or Session settings changes.
|
|
10
|
+
Jump connections and SSH/SFTP login identity remain owned by the caller.
|
|
11
|
+
`ownership="owned"` explicitly transfers the supplied Session (and its children,
|
|
12
|
+
not its ancestors) to the console. Ownership begins at construction, including
|
|
13
|
+
initialization failure. A local Session is also supported.
|
|
14
|
+
|
|
15
|
+
All calls run on the constructing thread. The caller must serialize console and
|
|
16
|
+
Session use until close. Python evaluation and lexical with blocks are synchronous;
|
|
17
|
+
arbitrary Python cannot be safely preempted. `submit(line)` retains the existing
|
|
18
|
+
compiler and namespace. `start_command(command, timeout=60)` starts the existing
|
|
19
|
+
persistent-shell foreground job; `poll()` reads incremental output without waiting.
|
|
20
|
+
`send_input(text, timeout=...)` replies to an enter() program, appending one newline.
|
|
21
|
+
`send_input(text, newline=False)` sends raw text to an enter() program.
|
|
22
|
+
Shell jobs reject stdin writes: a late write could reach the parent shell after
|
|
23
|
+
the process exits. Use enter() for interactive applications.
|
|
24
|
+
`interrupt()` stops a job with bounded recovery, or sends Ctrl-C to a program;
|
|
25
|
+
otherwise it discards unfinished Python input. After program Ctrl-C, a matched
|
|
26
|
+
program prompt returns `waiting_input` and remains ready across polls, even when
|
|
27
|
+
the protocol has consumed the prompt text. A control-read timeout stays `running`;
|
|
28
|
+
a later prompt can restore readiness through `poll()`. `error_type=KeyboardInterrupt`
|
|
29
|
+
records the interruption attempt, not program completion or failure. If the program
|
|
30
|
+
exits, its restored exit status determines `completed` or `failed`. `eof()` leaves program EOF behavior
|
|
31
|
+
unchanged; at the Python prompt it closes the console. `leave()` exits one layer,
|
|
32
|
+
including the root shell; `close()` unwinds everything. `quit` closes everything.
|
|
33
|
+
Python SystemExit is reported as failed input and leaves the console open.
|
|
34
|
+
A host exception in `with Console(...)` runs cleanup; cleanup failure is never
|
|
35
|
+
suppressed. Initialization failure cleans up the acquired channel.
|
|
36
|
+
|
|
37
|
+
Immutable `ConsoleUpdate` records carry state, incremental stdout/stderr, a command result,
|
|
38
|
+
safe error type, and Session reuse status. States are `ready`, `incomplete`,
|
|
39
|
+
`running`, `waiting_input`, `completed`, `failed`, `closed`. A nonzero exit status,
|
|
40
|
+
Python/syntax error, deadline expiry or unconfirmed recovery is failure. `completed`
|
|
41
|
+
requires a real job completion record or a restored program exit status, never silence. `waiting_input` means an
|
|
42
|
+
enter() prompt matched the configured program protocol; it is not proof of command
|
|
43
|
+
success. Silent or timed-out interactive reads remain `running`. A generic process
|
|
44
|
+
cannot reveal whether it is waiting for stdin: shell jobs remain `running` until
|
|
45
|
+
completion. Polling does not infer readiness from text.
|
|
46
|
+
For an ordinary Python cell, completed means evaluation returned without an
|
|
47
|
+
uncaught exception. It does not certify side effects or results of arbitrary
|
|
48
|
+
Python calls. The exit-status classification applies to managed dollar jobs,
|
|
49
|
+
start_command() and restored enter() program results; inspect other returned
|
|
50
|
+
Python API results explicitly. Configure enter() prompts
|
|
51
|
+
for text programs; full-screen terminal emulation is unsupported.
|
|
52
|
+
|
|
53
|
+
`connection` returns the current Session, without changing its login account.
|
|
54
|
+
`environments` returns immutable snapshots with kind, host, connection username,
|
|
55
|
+
effective username and identity verification. Effective names come from `id -un`
|
|
56
|
+
on entry; program layers inherit the last verified name with verification false.
|
|
57
|
+
They are observations, not authorization evidence for subsequent arbitrary Python.
|
|
58
|
+
Leaving a nested layer restores its parent channel; closing the root channel does
|
|
59
|
+
not attempt to preserve that shell's cwd/environment in the Session's later new
|
|
60
|
+
commands. A successful borrowed close additionally probes Session command execution
|
|
61
|
+
with a finite budget; `session_reuse="confirmed"` means cleanup and that probe
|
|
62
|
+
succeeded. `unknown` means the caller must reconcile or discard the connection;
|
|
63
|
+
`closed` means an owned Session was closed. No API can guarantee future network
|
|
64
|
+
availability or remote child-process termination from a channel close alone.
|
|
65
|
+
|
|
66
|
+
`credential_resolver(SwitchRequest)` runs before every managed su/sudo switch,
|
|
67
|
+
including explicit credentials. It may reject by raising or return
|
|
68
|
+
`SwitchCredentials(password=...)`; None keeps explicit credentials. Request metadata
|
|
69
|
+
contains no password. `switch_observer(SwitchOutcome)` receives success/failure,
|
|
70
|
+
request, resulting environment and safe error type, never credential/error text.
|
|
71
|
+
The resolver implements host configuration, candidate ordering and policy outside
|
|
72
|
+
SSHScript. It is called once per attempted switch; there is no automatic retry.
|
|
73
|
+
A missing password in non-terminal mode raises `CredentialsRequiredError`;
|
|
74
|
+
explicit None requests passwordless authentication. Observer failure propagates;
|
|
75
|
+
a successful switch remains active and is visible for cleanup. Explicit password
|
|
76
|
+
arguments and arbitrary Python error objects are sensitive application memory.
|
|
77
|
+
Do not log input source, namespace/error objects, JobResult.command or arbitrary
|
|
78
|
+
program output containing secrets. Use the resolver rather than literals in input.
|
|
79
|
+
Hooks cover managed Console.su/sudo and prompt-level dollar calls; arbitrary Python,
|
|
80
|
+
raw Session access and lexical script with blocks are trusted code, not a policy
|
|
81
|
+
sandbox. Hosts must restrict supplied code if they need enforced authorization.
|
|
82
|
+
|
|
83
|
+
`terminal=False` needs no terminal on the Python process. The new shell still uses
|
|
84
|
+
a PTY by default for su/sudo and reliable job interruption. `get_pty=False` supports
|
|
85
|
+
pipe jobs but cannot safely interrupt a running job; recovery can be unknown.
|
|
86
|
+
Programs/password readers may require a PTY. No hidden getpass prompt is used in
|
|
87
|
+
non-terminal mode. `terminal=True` enables the terminal frontend of the same Console core
|
|
88
|
+
and getpass fallback. Captured event streams and terminal streams are separate;
|
|
89
|
+
headless capture is consumed by each returned update. When a custom stdout or
|
|
90
|
+
stderr stream is supplied (or terminal=True selects terminal streams), that
|
|
91
|
+
stream receives the output and the corresponding update output field is empty. Only one console/redirected
|
|
92
|
+
Python execution may run in a process at a time because Python stdout redirection
|
|
93
|
+
is process-global. This API does not provide a Python sandbox or an async Python
|
|
94
|
+
executor.
|
|
95
|
+
|
|
96
|
+
## One core for CLI and host integration
|
|
97
|
+
|
|
98
|
+
The CLI and embedding applications use the same public `Console` implementation
|
|
99
|
+
in `interactive.py`. There is no legacy InteractiveConsole class, separate embedded
|
|
100
|
+
subclass, or compatibility module. The CLI creates an explicit owned local Session;
|
|
101
|
+
embedding callers normally borrow the Session they already hold. Both frontends
|
|
102
|
+
use submit(), poll(), interrupt(), eof() and the same cleanup and failure policy.
|
|
103
|
+
The CLI drains foreground $.start() jobs through poll() before reading another line.
|
|
104
|
+
|
|
105
|
+
Earlier 6.0 CLI behavior is not a compatibility target. Internal open()/execute()
|
|
106
|
+
entry points are removed: construct Console(session) and call submit(line).
|
|
107
|
+
`:help` and `:quit` aliases are removed; use help/help() and quit/quit(). `:status`
|
|
108
|
+
remains the environment-stack command. Diagnostics always omit source and arbitrary
|
|
109
|
+
exception text, including NameError and SystemExit values. A failed channel remains
|
|
110
|
+
owned until cleanup and is reported as unknown; it is not silently popped to an
|
|
111
|
+
apparently healthy parent layer. SSH/SFTP authentication and Session command APIs
|
|
112
|
+
keep their existing responsibilities.
|
|
113
|
+
|
|
114
|
+
This public API is released in SSHScript 6.1.0. During development, minor releases
|
|
115
|
+
may include incompatible changes; compatibility is required only when explicitly
|
|
116
|
+
requested. Pin the released artifact or record the exact source revision/hash.
|
|
117
|
+
|
|
118
|
+
## Executable examples
|
|
119
|
+
|
|
120
|
+
Run from the flat source checkout with `PYTHONPATH=. python3 -B` followed by
|
|
121
|
+
[examples/embedded_agent.py](examples/embedded_agent.py) or
|
|
122
|
+
[examples/embedded_terminal.py](examples/embedded_terminal.py). With the matching
|
|
123
|
+
installed wheel the PYTHONPATH setting is unnecessary. Both examples create only
|
|
124
|
+
local Sessions; `handover(existing_target)` and `drive(existing_target)` also
|
|
125
|
+
accept a host's already-connected target Session, including a jump target.
|
|
126
|
+
|
|
127
|
+
Terminal handover, followed by reuse:
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
from sshscript import Console
|
|
131
|
+
|
|
132
|
+
def handover(existing_target):
|
|
133
|
+
with Console(existing_target, terminal=True) as console:
|
|
134
|
+
console.interact()
|
|
135
|
+
assert console.session_reuse == "confirmed"
|
|
136
|
+
return existing_target.exec_command("hostname", check=True)
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Non-terminal Python/dollar cells and asynchronous shell output:
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
import time
|
|
143
|
+
from sshscript import Console
|
|
144
|
+
|
|
145
|
+
def drive(existing_target):
|
|
146
|
+
with Console(existing_target) as console:
|
|
147
|
+
assert console.submit("value = 42").state == "completed"
|
|
148
|
+
assert console.submit("$export TASK=value; cd /tmp").state == "completed"
|
|
149
|
+
update = console.start_command('printf "%s" "$TASK"', timeout=3)
|
|
150
|
+
while True:
|
|
151
|
+
consume(update.stdout, update.stderr) # host callback
|
|
152
|
+
if update.state != "running":
|
|
153
|
+
break
|
|
154
|
+
time.sleep(.03)
|
|
155
|
+
update = console.poll()
|
|
156
|
+
if update.state != "completed":
|
|
157
|
+
raise RuntimeError("command failed: " + str(update.error_type))
|
|
158
|
+
assert update.result.exitcode == 0
|
|
159
|
+
return existing_target.exec_command("printf reusable", check=True)
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
For a text program, call `console.enter("python3 -q -u", prompt=">>>", exit="quit()")`,
|
|
163
|
+
then `console.send_input("print(42)", timeout=2)`. A prompt response produces
|
|
164
|
+
`waiting_input`; program exit restores the parent and supplies `result.exitcode`.
|
|
165
|
+
`poll()` can observe a program's later prompt/exit after a timed-out read or raw
|
|
166
|
+
send. Program results snapshot the final input buffers; retain incremental events
|
|
167
|
+
for the full conversation. Job results snapshot the bounded whole command output.
|
|
168
|
+
`poll()` may perform bounded shell recovery when an enter() program has exited;
|
|
169
|
+
it does not block on a running shell job. Program restoration uses the existing
|
|
170
|
+
console exit budget (up to 60 seconds), unlike immediate job polling.
|
|
171
|
+
|
|
172
|
+
Credential and policy integration (no host configuration parser in SSHScript):
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
from sshscript import Console, SwitchCredentials
|
|
176
|
+
|
|
177
|
+
def resolve(request):
|
|
178
|
+
host_policy.require_switch(request.environment.host, request.kind, request.username)
|
|
179
|
+
# Return None only when keeping an explicit password argument. Returning a
|
|
180
|
+
# SwitchCredentials object with password=None deliberately tries passwordless.
|
|
181
|
+
return SwitchCredentials(secret_store.password_for(request.username))
|
|
182
|
+
|
|
183
|
+
with Console(existing_target, credential_resolver=resolve,
|
|
184
|
+
switch_observer=audit_switch_metadata) as console:
|
|
185
|
+
console.su("operator")
|
|
186
|
+
print(console.environments[-1])
|
|
187
|
+
console.leave()
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
The host supplies `consume`, `host_policy`, `secret_store` and
|
|
191
|
+
`audit_switch_metadata`; the linked executable examples need none of these.
|
|
192
|
+
Resolver/observer exceptions from direct method calls propagate. `submit()` reports
|
|
193
|
+
safe error types instead. Cleanup errors always raise `ConsoleCleanupError` with
|
|
194
|
+
`session_reuse="unknown"`; repeated close reports the same failure. Initialization
|
|
195
|
+
raises `ConsoleInitializationError` with unknown reuse, even if channel cleanup
|
|
196
|
+
appeared successful. If the host body and cleanup both fail, both errors are
|
|
197
|
+
preserved in a BaseExceptionGroup. Do not reuse an unknown Session on the strength
|
|
198
|
+
of a prompt or a successful command from an unrelated shell.
|
|
199
|
+
|
|
200
|
+
*Last modified: 2026-10-23 18:39:31*
|
|
@@ -60,3 +60,8 @@ recursive-include src/sshscript *.pyi
|
|
|
60
60
|
|
|
61
61
|
include src/sshscript/unittest/test_interactive_console.py
|
|
62
62
|
include CONSOLE.md
|
|
63
|
+
|
|
64
|
+
include src/sshscript/unittest/test_embedded_console.py
|
|
65
|
+
include EMBEDDED_CONSOLE.md
|
|
66
|
+
include examples/embedded_terminal.py
|
|
67
|
+
include examples/embedded_agent.py
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sshscript
|
|
3
|
-
Version: 6.
|
|
3
|
+
Version: 6.1.0
|
|
4
4
|
Summary: Python automation for local processes, SSH, and SSHScript .spy files
|
|
5
5
|
Author-email: "Yeh, Hsin-Yuan" <iapyeh@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -13,7 +13,7 @@ Project-URL: Issues, https://github.com/iapyeh/sshscript/issues
|
|
|
13
13
|
Project-URL: Security, https://github.com/iapyeh/sshscript/security/policy
|
|
14
14
|
Project-URL: Support, https://github.com/iapyeh/sshscript/discussions
|
|
15
15
|
Keywords: automation,paramiko,remote-execution,sftp,ssh,subprocess,system-administration
|
|
16
|
-
Classifier: Development Status ::
|
|
16
|
+
Classifier: Development Status :: 4 - Beta
|
|
17
17
|
Classifier: Operating System :: MacOS
|
|
18
18
|
Classifier: Operating System :: POSIX :: Linux
|
|
19
19
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -31,7 +31,12 @@ Dynamic: license-file
|
|
|
31
31
|
|
|
32
32
|
# SSHScript
|
|
33
33
|
|
|
34
|
-
SSHScript
|
|
34
|
+
**Development status:** SSHScript is still under active development. Backward
|
|
35
|
+
compatibility is not maintained at this stage. APIs, CLI behavior, configuration
|
|
36
|
+
and data formats may change; we provide only brief notes on major differences.
|
|
37
|
+
Compatibility work is done only when explicitly requested by the maintainer.
|
|
38
|
+
|
|
39
|
+
SSHScript 6.1.0: [borrow an existing Session for interactive operation](EMBEDDED_CONSOLE.md).
|
|
35
40
|
Run `python3 -B sshscript.py console` from the development checkout.
|
|
36
41
|
|
|
37
42
|
|
|
@@ -54,10 +59,9 @@ output and status, distinguish failure from timeout, and make cleanup and
|
|
|
54
59
|
unknown termination explicit. A successful command exit is evidence about
|
|
55
60
|
that command; verify the deployment or other application outcome separately.
|
|
56
61
|
|
|
57
|
-
**
|
|
58
|
-
|
|
59
|
-
**
|
|
60
|
-
(Production/Stable) · **Python:** 3.11 or newer · **Tested:** Python
|
|
62
|
+
**Release contract: 6.1.0.** Install the matching distribution and verify its version.
|
|
63
|
+
[Release notes](https://github.com/iapyeh/sshscript/releases/tag/v6.1.0)
|
|
64
|
+
(development-stage project) · **Python:** 3.11 or newer · **Tested:** Python
|
|
61
65
|
3.11–3.14 on Linux and macOS
|
|
62
66
|
|
|
63
67
|
[Documentation](https://iapyeh.github.io/sshscript/v3a/) ·
|
|
@@ -66,13 +70,14 @@ version; this source update does not publish a package or create a release tag.
|
|
|
66
70
|
[Security](https://github.com/iapyeh/sshscript/security/policy) ·
|
|
67
71
|
[Support](https://github.com/iapyeh/sshscript/blob/release/SUPPORT.md)
|
|
68
72
|
|
|
69
|
-
**Version 6.
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
**Version 6.1.0** adds public `Console(existing_session)` integration for terminals,
|
|
74
|
+
host applications and AI agents. Python variables, shell state and text programs
|
|
75
|
+
persist; structured updates distinguish readiness, execution, failure and cleanup.
|
|
76
|
+
Borrowed Sessions remain owned by the host. Credential/policy hooks and verified
|
|
77
|
+
Session reuse keep integration explicit. Read the [integration contract](EMBEDDED_CONSOLE.md).
|
|
78
|
+
Earlier 6.0 console internals, open()/execute() entry points and :help/:quit aliases
|
|
79
|
+
are removed; use Console(session), submit(), help/quit and structured updates.
|
|
80
|
+
The 5.0 host-key source and SSH/SFTP login identity contracts remain unchanged.
|
|
76
81
|
|
|
77
82
|
## AI agent ready
|
|
78
83
|
|
|
@@ -161,7 +166,7 @@ pin SSHScript and all transitive dependencies in your application's lock file.
|
|
|
161
166
|
To install this release explicitly:
|
|
162
167
|
|
|
163
168
|
```sh
|
|
164
|
-
python3 -m pip install
|
|
169
|
+
python3 -m pip install "sshscript==6.1.0"
|
|
165
170
|
```
|
|
166
171
|
|
|
167
172
|
Use `python3 -m pip install --upgrade sshscript` to upgrade. The optional
|
|
@@ -170,11 +175,9 @@ it never installs an update by itself.
|
|
|
170
175
|
|
|
171
176
|
## 60-second local quickstart
|
|
172
177
|
|
|
173
|
-
|
|
174
|
-
After publication, pin `sshscript==6.0.0` instead. The latest published 5.0.0
|
|
175
|
-
package does not contain the new console.
|
|
178
|
+
Install `sshscript==6.1.0` for the Console integration documented here.
|
|
176
179
|
|
|
177
|
-
This example runs on 6.
|
|
180
|
+
This example runs on 6.1.0 and retains compatibility with the 3.1.5 command API.
|
|
178
181
|
The metadata below records that older compatibility baseline.
|
|
179
182
|
|
|
180
183
|
<!-- example: {"id":"readme-quickstart", "profile":"3.1.5", "stdout":"sshscript is ready\nexit code: 0\n"} -->
|
|
@@ -620,4 +623,8 @@ for maintainer-only release steps.
|
|
|
620
623
|
SSHScript is released under the
|
|
621
624
|
[MIT License](https://github.com/iapyeh/sshscript/blob/release/LICENSE.txt).
|
|
622
625
|
|
|
623
|
-
|
|
626
|
+
For embedding an existing connected Session, see the new public
|
|
627
|
+
[Console integration API](EMBEDDED_CONSOLE.md). CLI and embedding applications
|
|
628
|
+
share one implementation; earlier 6.0 CLI compatibility is not maintained.
|
|
629
|
+
|
|
630
|
+
*Last modified: 2026-10-23 18:39:31*
|