sshscript 4.0.1__tar.gz → 5.0.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-5.0.0/AI_GUIDE.md +58 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/API_GUIDE.md +148 -13
- {sshscript-4.0.1 → sshscript-5.0.0}/CHANGELOG.md +22 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/MANIFEST.in +3 -0
- {sshscript-4.0.1/src/sshscript.egg-info → sshscript-5.0.0}/PKG-INFO +66 -8
- {sshscript-4.0.1 → sshscript-5.0.0}/README.md +65 -7
- {sshscript-4.0.1 → sshscript-5.0.0}/SECURITY.md +11 -2
- {sshscript-4.0.1 → sshscript-5.0.0}/VERSIONING.md +21 -4
- sshscript-5.0.0/skills/sshscript/SKILL.md +76 -0
- sshscript-5.0.0/skills/sshscript/references/agent-guide.md +360 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/_version.py +1 -1
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/session.py +100 -2
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/session.pyi +15 -3
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/sessionsettings.py +17 -1
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/unittest/test_command_job.py +5 -1
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/unittest/test_logger_integration.py +16 -3
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/unittest/test_openssh_integration.py +30 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/unittest/test_recommended_examples.py +22 -1
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/unittest/test_ssh_config.py +7 -1
- sshscript-5.0.0/src/sshscript/unittest/test_ssh_security.py +531 -0
- {sshscript-4.0.1 → sshscript-5.0.0/src/sshscript.egg-info}/PKG-INFO +66 -8
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript.egg-info/SOURCES.txt +4 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/tools/check_public_types.py +14 -5
- {sshscript-4.0.1 → sshscript-5.0.0}/tools/prepare_release.py +3 -0
- sshscript-5.0.0/tools/sync_ai_docs.py +97 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/tools/sync_api_guide.py +1 -1
- sshscript-4.0.1/src/sshscript/unittest/test_ssh_security.py +0 -295
- {sshscript-4.0.1 → sshscript-5.0.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/.github/dependabot.yml +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/.github/pull_request_template.md +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/.github/workflows/ci.yml +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/.github/workflows/codeql.yml +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/.github/workflows/release.yml +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/.gitignore +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/CODE_OF_CONDUCT.md +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/CONTRIBUTING.md +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/EXCEPTIONS.md +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/LICENSE.txt +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/RELEASING.md +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/SUPPORT.md +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/pyproject.toml +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/setup.cfg +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/_sshscript_cli.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/__init__.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/__init__.pyi +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/_runtime.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/channelgeneric.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/channelssh.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/channelsubprocess.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/channelutils.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/commandjob.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/commandjob.pyi +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/commandresult.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/commandresult.pyi +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/consolejob.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/dollar.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/dollarchanger.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/dollarparser.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/errorutils.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/patching.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/py.typed +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/sessionwrapper.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/sessionwrapper.pyi +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/spyimporter.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/sshconfig.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/sshscript.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/stdio.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/tokenparser.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/unittest/README.console-authentication.md +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/unittest/README.console-results.md +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/unittest/README.dollar-syntax-tests.md +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/unittest/README.module-tests.md +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/unittest/check_package_asserts.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/unittest/dollar_syntax.spy +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/unittest/dollar_syntax_fixture.spy +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/unittest/language_fixture.spy +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/unittest/test_channelgeneric_expect.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/unittest/test_check_file.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/unittest/test_command_api.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/unittest/test_console_authentication.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/unittest/test_console_job.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/unittest/test_console_results.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/unittest/test_dollar_command_escapes.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/unittest/test_file_transfer.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/unittest/test_logger_api.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/unittest/test_native_auth_runner.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/unittest/test_native_console_authentication.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/unittest/test_production_contract.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/unittest/test_public_types.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/unittest/test_python_version.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/unittest/test_release_inputs.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/unittest/test_session_close_reporting.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/unittest/test_session_proxy_cleanup.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/unittest/test_session_settings.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/unittest/test_spy_source_mapping.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/unittest/test_spy_thread_session.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/unittest/test_sshscript_dollar_syntax.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/unittest/test_sshscript_module.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/unittest/test_stdin_contract.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/unittest/test_stdio_dynamic_string.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/unittest/test_syntax_error.spy +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/unittest/test_update_check.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript/unittest/test_version_policy.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript.egg-info/dependency_links.txt +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript.egg-info/entry_points.txt +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript.egg-info/requires.txt +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/src/sshscript.egg-info/top_level.txt +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/tools/check_baseline_examples.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/tools/check_release.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/tools/publish_release.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/tools/run_checks.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/tools/run_native_auth_ci.py +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/tools/setup_openssh_ci.sh +0 -0
- {sshscript-4.0.1 → sshscript-5.0.0}/tools/version_policy.py +0 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# SSHScript for AI agents
|
|
2
|
+
|
|
3
|
+
**Documentation contract: SSHScript 5.0.0; Python 3.11+.**
|
|
4
|
+
|
|
5
|
+
Start with the [agent guide](skills/sshscript/references/agent-guide.md).
|
|
6
|
+
It explains version checks, API selection, command results, input, deadlines,
|
|
7
|
+
authentication and cleanup. Read only the task-relevant sections of the
|
|
8
|
+
[canonical API guide](API_GUIDE.md) and [version policy](VERSIONING.md).
|
|
9
|
+
|
|
10
|
+
The [SSHScript skill](skills/sshscript/SKILL.md) supplies a compact operating
|
|
11
|
+
workflow. Its folder contains the supporting guide; copy the entire
|
|
12
|
+
`skills/sshscript/` folder when installing it in an agent's skill directory.
|
|
13
|
+
It teaches SSHScript usage, not development of SSHScript itself or a particular
|
|
14
|
+
recipe library. Agents without skill support can read these Markdown files.
|
|
15
|
+
|
|
16
|
+
## Share one URL
|
|
17
|
+
|
|
18
|
+
The website entry is [SSHScript for AI Agents](https://iapyeh.github.io/sshscript/v3a/ai-agents/).
|
|
19
|
+
After these files are published, users can give that URL and a task to an agent:
|
|
20
|
+
|
|
21
|
+
> Read the SSHScript AI agent entry at
|
|
22
|
+
> https://iapyeh.github.io/sshscript/v3a/ai-agents/ and follow its reading order.
|
|
23
|
+
> Check the SSHScript version in my environment before selecting APIs. Help me
|
|
24
|
+
> complete this task: [describe the task, target hosts and expected output].
|
|
25
|
+
> If you cannot read a required document, report that limitation rather than
|
|
26
|
+
> guessing its contents. Explain the proposed commands before execution and
|
|
27
|
+
> stay within the operations I authorize.
|
|
28
|
+
|
|
29
|
+
A URL supplies reference material for the current task. It does not install
|
|
30
|
+
SSHScript, install a skill, grant execution permission, or guarantee persistent
|
|
31
|
+
memory. An agent needs URL-reading tools, or the user must provide the files.
|
|
32
|
+
|
|
33
|
+
## Maintain the source and website
|
|
34
|
+
|
|
35
|
+
Maintain this entry, `skills/sshscript/SKILL.md`, and its reference guide in
|
|
36
|
+
the designated `working_branch` checkout on branch `working_branch`.
|
|
37
|
+
The landing page is maintained in `gh-pages/v3a/ai-agents/index.md` on branch
|
|
38
|
+
`gh-pages`. The API guide remains authoritative for public behavior.
|
|
39
|
+
|
|
40
|
+
From `working_branch`, synchronize the generated website guide, plain-text
|
|
41
|
+
files and portable skill ZIP with:
|
|
42
|
+
|
|
43
|
+
```sh
|
|
44
|
+
python3 tools/sync_ai_docs.py ../gh-pages/v3a
|
|
45
|
+
python3 tools/sync_ai_docs.py ../gh-pages/v3a --check
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The ZIP includes the skill and its reference guide, plus snapshots of
|
|
49
|
+
`API_GUIDE.md` and `VERSIONING.md` for offline use. These snapshots are generated
|
|
50
|
+
from the source documents; do not edit the ZIP or generated website copies.
|
|
51
|
+
The sync command checks the destination branch before writing. Website pages
|
|
52
|
+
keep their Last Updated timestamp aligned with their mtime.
|
|
53
|
+
|
|
54
|
+
When the execution contract changes, review the skill and guide, update their
|
|
55
|
+
contract labels and the landing page, then regenerate the website assets.
|
|
56
|
+
Validate links, skill structure, example behavior, source/ZIP equality and
|
|
57
|
+
the rendered website. Source and website publishing are separate operations;
|
|
58
|
+
copying files does not publish either of them.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Recommended SSHScript API
|
|
2
2
|
|
|
3
3
|
This is the canonical entry point for new code and AI-generated examples.
|
|
4
|
-
**Current contract:
|
|
5
|
-
SSHScript 4.0.
|
|
4
|
+
**Current contract: 5.0.0**, Python 3.11+. Sections marked **4.0** require
|
|
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
|
|
8
8
|
handing off automation. See [version policy and migration](VERSIONING.md).
|
|
@@ -29,9 +29,11 @@ identifiers and handoff reports are future work, not a current API guarantee.
|
|
|
29
29
|
| Long-running program | **4.0:** `start(timeout=None)`, `stop()`, `wait()` |
|
|
30
30
|
| Session-wide failure/output policy | **4.0:** `set(...)`, `get(...)`, or matching properties |
|
|
31
31
|
|
|
32
|
-
##
|
|
32
|
+
## One command, one retained result (4.0; compatible with 3.1.5)
|
|
33
33
|
|
|
34
|
-
Install the
|
|
34
|
+
Install the current release with `python -m pip install "sshscript==5.0.0"`.
|
|
35
|
+
For reproducing only the historical compatibility baseline, explicitly install
|
|
36
|
+
`sshscript==3.1.5`; the 4.0 sections require the current release.
|
|
35
37
|
An argv list is one command, not a batch. Shell-looking argument text stays data.
|
|
36
38
|
|
|
37
39
|
<!-- example: {"id":"argv", "profile":"3.1.5", "stdout":"a; b\n"} -->
|
|
@@ -67,7 +69,7 @@ with closing(Session()) as local:
|
|
|
67
69
|
print(f"failed: {error.returncode}")
|
|
68
70
|
```
|
|
69
71
|
|
|
70
|
-
##
|
|
72
|
+
## SSH and optional dollar syntax (4.0; compatible with 3.1.5)
|
|
71
73
|
|
|
72
74
|
Replace the example host with your configured host and verified known-host key.
|
|
73
75
|
CI executes this block with a local SSH protocol fixture in place of the login;
|
|
@@ -86,6 +88,139 @@ with closing(Session()) as local:
|
|
|
86
88
|
print(result.stdout)
|
|
87
89
|
```
|
|
88
90
|
|
|
91
|
+
### Private-key paths and nested connections
|
|
92
|
+
|
|
93
|
+
`parent.connect(..., pkey_path=path)` loads an RSA private key from the
|
|
94
|
+
**calling parent Session's host**. A local Session reads the file on localhost;
|
|
95
|
+
a Session connected to host1 reads it on host1 through SFTP before connecting
|
|
96
|
+
to host2. `connect()` returns a child Session and leaves the parent on its
|
|
97
|
+
original host. Calling the original local Session again therefore still reads
|
|
98
|
+
the key on localhost.
|
|
99
|
+
|
|
100
|
+
For example, `local.connect("host1", pkey_path="/local/key_rsa")` reads
|
|
101
|
+
`/local/key_rsa` locally and returns `host1`. Then
|
|
102
|
+
`host1.connect("host2", pkey_path="/remote/key_rsa")` reads `/remote/key_rsa`
|
|
103
|
+
on host1. In `.spy`, an inner `with $.connect(...)` uses the outer scope's
|
|
104
|
+
current Session as its parent.
|
|
105
|
+
|
|
106
|
+
`key_filename=` is forwarded to Paramiko and reads from the local Python
|
|
107
|
+
process's filesystem even for nested connections; it supports additional key
|
|
108
|
+
formats. For an encrypted RSA key, use
|
|
109
|
+
`pkey=parent.pkey(path, password=key_passphrase)` to retain the same host-based
|
|
110
|
+
reading rule. Supplying both `pkey_path` and `pkey` raises `ValueError`.
|
|
111
|
+
See the [agent guide's complete nested example](https://iapyeh.github.io/sshscript/v3a/ai-agents/guide/#ssh-connections-and-private-key-paths).
|
|
112
|
+
|
|
113
|
+
<a id="host-key-sources-unreleased"></a>
|
|
114
|
+
|
|
115
|
+
### Host-key sources (5.0)
|
|
116
|
+
|
|
117
|
+
This section requires SSHScript 5.0.0 or newer.
|
|
118
|
+
`known_hosts` selects the host-key trust source independently of `policy`:
|
|
119
|
+
|
|
120
|
+
| Value | Sources, in order |
|
|
121
|
+
| --- | --- |
|
|
122
|
+
| `"parent"` (new default) | Only the calling Session's host |
|
|
123
|
+
| `"local"` | Only the root localhost Session |
|
|
124
|
+
| `"chain"` | Calling Session, then each ancestor through localhost |
|
|
125
|
+
|
|
126
|
+
For localhost → host1 → host2 → host3, `host2.connect("host3",
|
|
127
|
+
known_hosts="chain")` consults host2, host1, then localhost. The nearest source
|
|
128
|
+
with a record for the resolved target hostname and port is authoritative.
|
|
129
|
+
A matching key is accepted; a changed key is rejected even if a more distant
|
|
130
|
+
source matches. Only absence of a target record permits continuing the chain.
|
|
131
|
+
Different key algorithms do not permit falling back to another source.
|
|
132
|
+
|
|
133
|
+
Each source reads its own `known_hosts_path`, or `~/.ssh/known_hosts` if unset.
|
|
134
|
+
Remote files are read over SFTP as that Session's original SSH login account;
|
|
135
|
+
SSH handshakes, user authentication and host-key verification still run in the
|
|
136
|
+
local Python process. Neither sudo/su nor a nested connection changes the SFTP
|
|
137
|
+
account. Remote `~/` paths refer to the login account's SFTP home; `~user` paths
|
|
138
|
+
are unsupported. Relative remote paths use the SFTP working directory.
|
|
139
|
+
|
|
140
|
+
```py
|
|
141
|
+
local.set(known_hosts="chain")
|
|
142
|
+
# After connecting to host1 and host2:
|
|
143
|
+
host1.set(known_hosts_path="/home/ops/.ssh/known_hosts")
|
|
144
|
+
host2.set(known_hosts_path="/etc/ssh/ssh_known_hosts")
|
|
145
|
+
with host2.connect("deploy@host3", pkey_path="/home/ops/.ssh/host3_rsa") as host3:
|
|
146
|
+
result = host3.exec_command(["hostname"], check=True, command_timeout=10)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
The strategy is inherited by future child Sessions; the path is host-specific
|
|
150
|
+
and is **not inherited**. `known_hosts_path=None` restores the standard path.
|
|
151
|
+
A `connect(known_hosts=...)` override affects only that connection; it does not
|
|
152
|
+
change the parent setting or the child's inherited strategy. `set()`, `get()`
|
|
153
|
+
and matching properties expose both settings. `known_hosts_path` accepts text
|
|
154
|
+
paths and `PathLike[str]`; it is a Session setting, not a `connect()` keyword.
|
|
155
|
+
|
|
156
|
+
`pkey_path` always reads only from the calling Session's host, including with
|
|
157
|
+
`known_hosts="chain"`; private keys are never searched through ancestors.
|
|
158
|
+
|
|
159
|
+
An absent trust file, permission failure, read failure, invalid UTF-8 or invalid
|
|
160
|
+
entry raises an error; no distant source or permissive policy bypasses it.
|
|
161
|
+
Empty files, comments, plain host-key entries (including comma-separated names)
|
|
162
|
+
and hashed hostnames are supported. OpenSSH markers such as `@cert-authority`
|
|
163
|
+
and `@revoked`, wildcard/negated host patterns and unsupported key types raise
|
|
164
|
+
an error rather than being silently ignored. A source file is fully parsed
|
|
165
|
+
before its records are used. Nonstandard ports use `[hostname]:port` records.
|
|
166
|
+
|
|
167
|
+
When every selected source lacks the target, `policy` handles the unknown key.
|
|
168
|
+
`AutoAddPolicy` accepts it into the new client's memory only; these trust files
|
|
169
|
+
are read-only and are not automatically updated locally or remotely.
|
|
170
|
+
|
|
171
|
+
**Migration from 4.0.2:** earlier releases always load localhost host keys. The new default
|
|
172
|
+
`"parent"` makes nested host-key sources consistent with `pkey_path`. To retain
|
|
173
|
+
the previous behavior, set `local.set(known_hosts="local")` before connecting.
|
|
174
|
+
All strategies require a readable, valid file, including the default localhost
|
|
175
|
+
path. Unlike 4.0.2, a missing local file is an error; create an empty
|
|
176
|
+
file if the configured missing-key policy should handle unknown hosts.
|
|
177
|
+
|
|
178
|
+
### Private keys readable only in a privileged console
|
|
179
|
+
|
|
180
|
+
Remote `pkey()` and `pkey_path` use SFTP with the original SSH login account.
|
|
181
|
+
Entering `sudo()` or `su()` does not change that identity, so even
|
|
182
|
+
`$.pkey("/root/.ssh/id_rsa")` inside a root console can fail with a permission
|
|
183
|
+
error. When authorized, read the file with a console command and construct
|
|
184
|
+
the Paramiko key from its captured text:
|
|
185
|
+
|
|
186
|
+
This manual example requires a real privileged console and SSH endpoints;
|
|
187
|
+
it is excluded from credential-free executable examples.
|
|
188
|
+
|
|
189
|
+
```py
|
|
190
|
+
from contextlib import closing
|
|
191
|
+
from io import StringIO
|
|
192
|
+
import paramiko
|
|
193
|
+
from sshscript import Session
|
|
194
|
+
|
|
195
|
+
with closing(Session()) as local:
|
|
196
|
+
with local.connect("ops@host1.example.net", timeout=10,
|
|
197
|
+
banner_timeout=10, auth_timeout=10) as host1:
|
|
198
|
+
with host1.sudo() as root:
|
|
199
|
+
key_result = root("cat /root/.ssh/id_rsa", check=True, command_timeout=10)
|
|
200
|
+
key = paramiko.RSAKey.from_private_key(StringIO(key_result.stdout))
|
|
201
|
+
with host1.connect("deploy@host2.example.net", pkey=key, timeout=10,
|
|
202
|
+
banner_timeout=10, auth_timeout=10) as host2:
|
|
203
|
+
result = host2.exec_command(["hostname"], check=True, command_timeout=10)
|
|
204
|
+
print(result.stdout)
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
The example requires authorized sudo/SSH access and verified host keys.
|
|
208
|
+
Configure output logging/display to keep private-key text out of logs before
|
|
209
|
+
running the read; do not print or report `key_result` or its stdout. Key text
|
|
210
|
+
also remains in console buffers. Use the matching Paramiko key class for
|
|
211
|
+
other formats; encrypted RSA keys need `password=` on `from_private_key()`.
|
|
212
|
+
An authorized `su()` console can supply the text in the same way.
|
|
213
|
+
|
|
214
|
+
The console returned by `shell()`/`sudo()`/`su()`/`enter()` has no `connect()`
|
|
215
|
+
method, so `$.connect()` is unavailable while a console is current in `.spy`.
|
|
216
|
+
A retained Python Session can still call `connect()` during an active console;
|
|
217
|
+
the connection uses its original SSH transport and does not inherit console
|
|
218
|
+
privilege. The example leaves the console first and calls the retained
|
|
219
|
+
`host1.connect(pkey=key)`, which needs no SFTP key read. See the
|
|
220
|
+
[agent guide's `.spy` equivalent](https://iapyeh.github.io/sshscript/v3a/ai-agents/guide/#keys-readable-only-after-sudosu).
|
|
221
|
+
|
|
222
|
+
### Optional dollar syntax
|
|
223
|
+
|
|
89
224
|
Save dollar syntax as `.spy`, run with `sshscript file.spy`, and syntax-check
|
|
90
225
|
without execution using `sshscript --check file.spy`.
|
|
91
226
|
|
|
@@ -99,7 +234,7 @@ print(result.stdout)
|
|
|
99
234
|
|
|
100
235
|
## 4.0: settings and managed jobs
|
|
101
236
|
|
|
102
|
-
Use SSHScript
|
|
237
|
+
Use SSHScript 5.0.0 for the following examples. CLI `-v`, `--stderr`, and
|
|
103
238
|
`--debug` override root defaults only for that execution. Child Sessions copy
|
|
104
239
|
their parent's settings. `set()` validates before changing anything; `get()`
|
|
105
240
|
returns a copy. Explicit command options override Session policy.
|
|
@@ -118,7 +253,7 @@ the settings dictionary at creation; the policy object itself is shared, so
|
|
|
118
253
|
custom policies with mutable state must account for reuse. Changing the setting
|
|
119
254
|
does not reconfigure existing connections.
|
|
120
255
|
|
|
121
|
-
<!-- example: {"id":"settings", "profile":"
|
|
256
|
+
<!-- example: {"id":"settings", "profile":"5.0.0", "stdout":"True\n1\n"} -->
|
|
122
257
|
```python
|
|
123
258
|
from contextlib import closing
|
|
124
259
|
import paramiko
|
|
@@ -140,7 +275,7 @@ semantics; do not combine it with `command_timeout`.
|
|
|
140
275
|
For an indefinite command such as tcpdump, use `start(timeout=None)`. This safe
|
|
141
276
|
local example uses a small Python process instead of capturing network traffic:
|
|
142
277
|
|
|
143
|
-
<!-- example: {"id":"stop", "profile":"
|
|
278
|
+
<!-- example: {"id":"stop", "profile":"5.0.0", "stdout":"cancelled confirmed\n"} -->
|
|
144
279
|
```python
|
|
145
280
|
from contextlib import closing
|
|
146
281
|
import sys
|
|
@@ -179,7 +314,7 @@ legacy SSH behavior which appended a newline to strings: if a consumer
|
|
|
179
314
|
needs a line, migrate `input=password` to `input=password + "\n"` explicitly.
|
|
180
315
|
Published 3.1.5 still has the legacy SSH behavior.
|
|
181
316
|
|
|
182
|
-
<!-- example: {"id":"exact-stdin", "profile":"
|
|
317
|
+
<!-- example: {"id":"exact-stdin", "profile":"5.0.0", "stdout":"'abc'\n'abc\\n'\n"} -->
|
|
183
318
|
```python
|
|
184
319
|
from contextlib import closing
|
|
185
320
|
import sys
|
|
@@ -248,7 +383,7 @@ or unpack exactly `stdout, stderr, exitcode`. This changes the released 3.1.5
|
|
|
248
383
|
console contract: old `stdout, stderr = shell(command)` must be migrated.
|
|
249
384
|
Shell calls accept command strings; argv execution belongs to Session.
|
|
250
385
|
|
|
251
|
-
<!-- example: {"id":"shell-result", "profile":"
|
|
386
|
+
<!-- example: {"id":"shell-result", "profile":"5.0.0", "stdout":"first 0\n"} -->
|
|
252
387
|
```python
|
|
253
388
|
from contextlib import closing
|
|
254
389
|
from sshscript import Session
|
|
@@ -304,7 +439,7 @@ matching: `iter_stdout()` yields chunks, not packets or lines. Put `stop()` in
|
|
|
304
439
|
`finally`, so condition matches, exceptions, and KeyboardInterrupt all trigger
|
|
305
440
|
cleanup. This credential-free example uses a continuous Python process:
|
|
306
441
|
|
|
307
|
-
<!-- example: {"id":"stream-condition", "profile":"
|
|
442
|
+
<!-- example: {"id":"stream-condition", "profile":"5.0.0", "stdout":"cancelled confirmed\n"} -->
|
|
308
443
|
```python
|
|
309
444
|
from contextlib import closing
|
|
310
445
|
import sys
|
|
@@ -369,7 +504,7 @@ the current Bash console's channel, cwd, environment and identity. It does not
|
|
|
369
504
|
launch through the underlying Session. Ordinary `console(command)` / `$command`
|
|
370
505
|
still waits and returns `CommandResult`; no migration is needed for finite commands.
|
|
371
506
|
|
|
372
|
-
<!-- example: {"id":"console-stream", "profile":"
|
|
507
|
+
<!-- example: {"id":"console-stream", "profile":"5.0.0", "stdout":"cancelled confirmed\nconsole-alive\n"} -->
|
|
373
508
|
```python
|
|
374
509
|
from contextlib import closing
|
|
375
510
|
import shlex
|
|
@@ -509,7 +644,7 @@ remains available for other authentication prompt formats.
|
|
|
509
644
|
These transcripts require a real local account and its authentication policy;
|
|
510
645
|
they are manual examples, separate from the credential-free CI examples above.
|
|
511
646
|
Read passwords with `getpass` rather than storing them in the script. The new
|
|
512
|
-
behavior requires 4.0.
|
|
647
|
+
behavior requires 4.0.2 or newer and does not apply to the installed 3.1.5 release.
|
|
513
648
|
|
|
514
649
|
Open a root console through sudo and allow up to 15 seconds for entry:
|
|
515
650
|
|
|
@@ -3,6 +3,28 @@
|
|
|
3
3
|
This project records user-visible changes here. Release artifacts and their
|
|
4
4
|
provenance are available from the linked GitHub Release and PyPI pages.
|
|
5
5
|
|
|
6
|
+
## 5.0.0 — 2026-10-10
|
|
7
|
+
|
|
8
|
+
- Add `known_hosts="parent"` (new default), `"local"` and `"chain"` strategies.
|
|
9
|
+
Nested connections now use the calling host's trust file by default, matching
|
|
10
|
+
`pkey_path` ownership. Set `known_hosts="local"` for previous behavior.
|
|
11
|
+
- Add per-Session `known_hosts_path` without path inheritance. Chain lookup uses
|
|
12
|
+
the nearest target record; changed keys and read/parse errors never fall back.
|
|
13
|
+
Authentication and verification still run locally; trust files are read-only.
|
|
14
|
+
- Isolate connection-timeout tests from runner HOME and verify client cleanup.
|
|
15
|
+
- Document host-key source selection, strict file errors and migration in the
|
|
16
|
+
API, website and portable AI agent skill.
|
|
17
|
+
|
|
18
|
+
## 4.0.2 — 2026-10-09
|
|
19
|
+
|
|
20
|
+
- Align the README, current installation commands, website title, navigation,
|
|
21
|
+
footer, and version metadata with the current 4.0 release.
|
|
22
|
+
- Point general PyPI and GitHub Release links to current entry points; retain
|
|
23
|
+
explicitly labelled historical releases and existing documentation URLs.
|
|
24
|
+
- Check current website version labels and release links during documentation
|
|
25
|
+
validation, including rendered header/footer links and anchors.
|
|
26
|
+
- Preserve the 4.0.1 public execution contract; no runtime behavior changes.
|
|
27
|
+
|
|
6
28
|
## 4.0.1 — 2026-10-08
|
|
7
29
|
|
|
8
30
|
- Settle the legacy command worker before propagating timeout/transport errors;
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
include README.md LICENSE.txt RELEASING.md
|
|
2
|
+
include AI_GUIDE.md
|
|
3
|
+
include skills/sshscript/SKILL.md
|
|
4
|
+
include skills/sshscript/references/agent-guide.md
|
|
2
5
|
include CHANGELOG.md CODE_OF_CONDUCT.md CONTRIBUTING.md EXCEPTIONS.md SECURITY.md SUPPORT.md
|
|
3
6
|
recursive-include tools *.py
|
|
4
7
|
include tools/setup_openssh_ci.sh
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sshscript
|
|
3
|
-
Version:
|
|
3
|
+
Version: 5.0.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
|
|
@@ -37,9 +37,12 @@ Dynamic: license-file
|
|
|
37
37
|
[](https://github.com/iapyeh/sshscript/actions/workflows/codeql.yml)
|
|
38
38
|
[](https://github.com/iapyeh/sshscript/blob/release/LICENSE.txt)
|
|
39
39
|
|
|
40
|
+
**AI agent ready automation for local and remote commands.**
|
|
41
|
+
|
|
40
42
|
SSHScript is a Python automation library for running commands locally and over
|
|
41
|
-
SSH through one `Session` API. It
|
|
42
|
-
|
|
43
|
+
SSH through one `Session` API. It provides an agent guide, a portable skill
|
|
44
|
+
bundle, and copyable prompts to help your AI agent use its execution contract.
|
|
45
|
+
Optional dollar syntax supports compact `.spy` automation files.
|
|
43
46
|
|
|
44
47
|
SSHScript aims to help engineers and AI agents execute, understand, and hand
|
|
45
48
|
off automation reliably: identify the execution host, retain each command's
|
|
@@ -47,7 +50,7 @@ output and status, distinguish failure from timeout, and make cleanup and
|
|
|
47
50
|
unknown termination explicit. A successful command exit is evidence about
|
|
48
51
|
that command; verify the deployment or other application outcome separately.
|
|
49
52
|
|
|
50
|
-
**Current release:** [
|
|
53
|
+
**Current release:** [5.0.0](https://github.com/iapyeh/sshscript/releases/tag/v5.0.0)
|
|
51
54
|
(Production/Stable) · **Python:** 3.11 or newer · **Tested:** Python
|
|
52
55
|
3.11–3.14 on Linux and macOS
|
|
53
56
|
|
|
@@ -57,10 +60,45 @@ that command; verify the deployment or other application outcome separately.
|
|
|
57
60
|
[Security](https://github.com/iapyeh/sshscript/security/policy) ·
|
|
58
61
|
[Support](https://github.com/iapyeh/sshscript/blob/release/SUPPORT.md)
|
|
59
62
|
|
|
60
|
-
**Version
|
|
63
|
+
**Version 5.0.0** preserves the 4.0 command execution contract and changes the
|
|
64
|
+
default host-key source to the calling host. Provision its known_hosts file or
|
|
65
|
+
select `known_hosts="local"` when migrating existing nested connections.
|
|
61
66
|
Upgrade 3.1.5 automation using the migration guide and application regressions.
|
|
62
67
|
See [version policy and migration](VERSIONING.md) before upgrading automation.
|
|
63
68
|
|
|
69
|
+
## AI agent ready
|
|
70
|
+
|
|
71
|
+
**Give your agent one URL:**
|
|
72
|
+
[SSHScript for AI Agents](https://iapyeh.github.io/sshscript/v3a/ai-agents/).
|
|
73
|
+
Prepare it for future automation or give it a task to complete now.
|
|
74
|
+
|
|
75
|
+
- **A guide for agents:** version-aware API selection, exact input handling,
|
|
76
|
+
retained command results, timeouts, authentication and cleanup.
|
|
77
|
+
- **A portable skill bundle:** `SKILL.md` with the agent guide and API and
|
|
78
|
+
version-policy references, ready to install through your agent's skill
|
|
79
|
+
mechanism.
|
|
80
|
+
- **Prompts for humans:** copy a setup prompt to request skill installation and
|
|
81
|
+
a saved tool preference, or a task prompt to describe hosts, inputs and
|
|
82
|
+
expected results.
|
|
83
|
+
|
|
84
|
+
Start with this prompt, even before you have a task:
|
|
85
|
+
|
|
86
|
+
```text
|
|
87
|
+
Read https://iapyeh.github.io/sshscript/v3a/ai-agents/ and the Agent Guide.
|
|
88
|
+
If you support skills, download and install the complete skill bundle. If you
|
|
89
|
+
support persistent preferences, save this preference: for future local or
|
|
90
|
+
remote command execution and automation, first assess whether SSHScript is
|
|
91
|
+
suitable and use it when appropriate. Report whether the skill can be loaded
|
|
92
|
+
and the preference was saved, and explain any limits.
|
|
93
|
+
For now, do not connect to remote hosts or run demonstration commands.
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
The guides are ordinary Markdown and web pages for agents that can read files
|
|
97
|
+
or URLs. Skill installation and persistent preferences depend on the agent's
|
|
98
|
+
capabilities. See the [repository AI entry](AI_GUIDE.md),
|
|
99
|
+
[agent guide](skills/sshscript/references/agent-guide.md) and
|
|
100
|
+
[skill instructions](skills/sshscript/SKILL.md) for the source documents.
|
|
101
|
+
|
|
64
102
|
## Recommended API
|
|
65
103
|
|
|
66
104
|
Start with [the canonical API guide](API_GUIDE.md): version-labelled examples,
|
|
@@ -82,6 +120,7 @@ and [the cross-system test guide](src/sshscript/unittest/README.console-authenti
|
|
|
82
120
|
|
|
83
121
|
## Why SSHScript?
|
|
84
122
|
|
|
123
|
+
- Prepare your AI agent with a dedicated guide, portable skill and setup prompts.
|
|
85
124
|
- Use the same interface for local subprocesses and remote SSH commands.
|
|
86
125
|
- Traverse nested SSH connections without rebuilding connection logic.
|
|
87
126
|
- Keep ordinary Python functions, packages, exceptions, data processing, and
|
|
@@ -114,7 +153,7 @@ pin SSHScript and all transitive dependencies in your application's lock file.
|
|
|
114
153
|
To install this release explicitly:
|
|
115
154
|
|
|
116
155
|
```sh
|
|
117
|
-
python3 -m pip install "sshscript==
|
|
156
|
+
python3 -m pip install "sshscript==5.0.0"
|
|
118
157
|
```
|
|
119
158
|
|
|
120
159
|
Use `python3 -m pip install --upgrade sshscript` to upgrade. The optional
|
|
@@ -123,7 +162,8 @@ it never installs an update by itself.
|
|
|
123
162
|
|
|
124
163
|
## 60-second local quickstart
|
|
125
164
|
|
|
126
|
-
This example
|
|
165
|
+
This example runs on 5.0.0 and retains compatibility with the 3.1.5 command API.
|
|
166
|
+
The metadata below records that older compatibility baseline.
|
|
127
167
|
|
|
128
168
|
<!-- example: {"id":"readme-quickstart", "profile":"3.1.5", "stdout":"sshscript is ready\nexit code: 0\n"} -->
|
|
129
169
|
```python
|
|
@@ -374,10 +414,28 @@ Unknown and changed host keys are rejected unless the caller explicitly
|
|
|
374
414
|
supplies a different Paramiko policy. Do not use automatic key acceptance in a
|
|
375
415
|
production workflow unless a separate trusted bootstrap process has already
|
|
376
416
|
verified the key. See the
|
|
377
|
-
[SSHScript
|
|
417
|
+
[SSHScript documentation](https://iapyeh.github.io/sshscript/v3a/) for
|
|
378
418
|
nested connections, timeouts, file transfer, `sudo`, `su`, and interactive
|
|
379
419
|
programs.
|
|
380
420
|
|
|
421
|
+
## Host-key sources in 5.0
|
|
422
|
+
|
|
423
|
+
SSHScript 5.0.0 uses `known_hosts="parent"` as the default: a local
|
|
424
|
+
Session reads localhost's trust file; `host1.connect("host2")` reads host1's
|
|
425
|
+
trust file over SFTP. This matches the host ownership of `pkey_path`.
|
|
426
|
+
SSHScript 4.0.2 and earlier use localhost for all host-key checks.
|
|
427
|
+
|
|
428
|
+
Set `session.set(known_hosts="chain")` to search from the calling Session
|
|
429
|
+
through its ancestors to localhost. The nearest layer with a target record
|
|
430
|
+
wins; a mismatch or read/parse error stops the connection. Set
|
|
431
|
+
`session.set(known_hosts="local")` to preserve previous behavior.
|
|
432
|
+
Each Session can set its own `known_hosts_path`; this path is not inherited.
|
|
433
|
+
`connect(known_hosts=...)` overrides only one connection. `pkey_path` remains
|
|
434
|
+
on the calling host for every strategy. `policy` handles only unknown keys;
|
|
435
|
+
`AutoAddPolicy` does not write these trust files. See the
|
|
436
|
+
[host-key source contract](API_GUIDE.md#host-key-sources-unreleased) for supported
|
|
437
|
+
file formats, strict error handling and migration.
|
|
438
|
+
|
|
381
439
|
## Reusing SSH configuration
|
|
382
440
|
|
|
383
441
|
Connections from a local session read `~/.ssh/config` if it exists. Supported
|
|
@@ -6,9 +6,12 @@
|
|
|
6
6
|
[](https://github.com/iapyeh/sshscript/actions/workflows/codeql.yml)
|
|
7
7
|
[](https://github.com/iapyeh/sshscript/blob/release/LICENSE.txt)
|
|
8
8
|
|
|
9
|
+
**AI agent ready automation for local and remote commands.**
|
|
10
|
+
|
|
9
11
|
SSHScript is a Python automation library for running commands locally and over
|
|
10
|
-
SSH through one `Session` API. It
|
|
11
|
-
|
|
12
|
+
SSH through one `Session` API. It provides an agent guide, a portable skill
|
|
13
|
+
bundle, and copyable prompts to help your AI agent use its execution contract.
|
|
14
|
+
Optional dollar syntax supports compact `.spy` automation files.
|
|
12
15
|
|
|
13
16
|
SSHScript aims to help engineers and AI agents execute, understand, and hand
|
|
14
17
|
off automation reliably: identify the execution host, retain each command's
|
|
@@ -16,7 +19,7 @@ output and status, distinguish failure from timeout, and make cleanup and
|
|
|
16
19
|
unknown termination explicit. A successful command exit is evidence about
|
|
17
20
|
that command; verify the deployment or other application outcome separately.
|
|
18
21
|
|
|
19
|
-
**Current release:** [
|
|
22
|
+
**Current release:** [5.0.0](https://github.com/iapyeh/sshscript/releases/tag/v5.0.0)
|
|
20
23
|
(Production/Stable) · **Python:** 3.11 or newer · **Tested:** Python
|
|
21
24
|
3.11–3.14 on Linux and macOS
|
|
22
25
|
|
|
@@ -26,10 +29,45 @@ that command; verify the deployment or other application outcome separately.
|
|
|
26
29
|
[Security](https://github.com/iapyeh/sshscript/security/policy) ·
|
|
27
30
|
[Support](https://github.com/iapyeh/sshscript/blob/release/SUPPORT.md)
|
|
28
31
|
|
|
29
|
-
**Version
|
|
32
|
+
**Version 5.0.0** preserves the 4.0 command execution contract and changes the
|
|
33
|
+
default host-key source to the calling host. Provision its known_hosts file or
|
|
34
|
+
select `known_hosts="local"` when migrating existing nested connections.
|
|
30
35
|
Upgrade 3.1.5 automation using the migration guide and application regressions.
|
|
31
36
|
See [version policy and migration](VERSIONING.md) before upgrading automation.
|
|
32
37
|
|
|
38
|
+
## AI agent ready
|
|
39
|
+
|
|
40
|
+
**Give your agent one URL:**
|
|
41
|
+
[SSHScript for AI Agents](https://iapyeh.github.io/sshscript/v3a/ai-agents/).
|
|
42
|
+
Prepare it for future automation or give it a task to complete now.
|
|
43
|
+
|
|
44
|
+
- **A guide for agents:** version-aware API selection, exact input handling,
|
|
45
|
+
retained command results, timeouts, authentication and cleanup.
|
|
46
|
+
- **A portable skill bundle:** `SKILL.md` with the agent guide and API and
|
|
47
|
+
version-policy references, ready to install through your agent's skill
|
|
48
|
+
mechanism.
|
|
49
|
+
- **Prompts for humans:** copy a setup prompt to request skill installation and
|
|
50
|
+
a saved tool preference, or a task prompt to describe hosts, inputs and
|
|
51
|
+
expected results.
|
|
52
|
+
|
|
53
|
+
Start with this prompt, even before you have a task:
|
|
54
|
+
|
|
55
|
+
```text
|
|
56
|
+
Read https://iapyeh.github.io/sshscript/v3a/ai-agents/ and the Agent Guide.
|
|
57
|
+
If you support skills, download and install the complete skill bundle. If you
|
|
58
|
+
support persistent preferences, save this preference: for future local or
|
|
59
|
+
remote command execution and automation, first assess whether SSHScript is
|
|
60
|
+
suitable and use it when appropriate. Report whether the skill can be loaded
|
|
61
|
+
and the preference was saved, and explain any limits.
|
|
62
|
+
For now, do not connect to remote hosts or run demonstration commands.
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
The guides are ordinary Markdown and web pages for agents that can read files
|
|
66
|
+
or URLs. Skill installation and persistent preferences depend on the agent's
|
|
67
|
+
capabilities. See the [repository AI entry](AI_GUIDE.md),
|
|
68
|
+
[agent guide](skills/sshscript/references/agent-guide.md) and
|
|
69
|
+
[skill instructions](skills/sshscript/SKILL.md) for the source documents.
|
|
70
|
+
|
|
33
71
|
## Recommended API
|
|
34
72
|
|
|
35
73
|
Start with [the canonical API guide](API_GUIDE.md): version-labelled examples,
|
|
@@ -51,6 +89,7 @@ and [the cross-system test guide](src/sshscript/unittest/README.console-authenti
|
|
|
51
89
|
|
|
52
90
|
## Why SSHScript?
|
|
53
91
|
|
|
92
|
+
- Prepare your AI agent with a dedicated guide, portable skill and setup prompts.
|
|
54
93
|
- Use the same interface for local subprocesses and remote SSH commands.
|
|
55
94
|
- Traverse nested SSH connections without rebuilding connection logic.
|
|
56
95
|
- Keep ordinary Python functions, packages, exceptions, data processing, and
|
|
@@ -83,7 +122,7 @@ pin SSHScript and all transitive dependencies in your application's lock file.
|
|
|
83
122
|
To install this release explicitly:
|
|
84
123
|
|
|
85
124
|
```sh
|
|
86
|
-
python3 -m pip install "sshscript==
|
|
125
|
+
python3 -m pip install "sshscript==5.0.0"
|
|
87
126
|
```
|
|
88
127
|
|
|
89
128
|
Use `python3 -m pip install --upgrade sshscript` to upgrade. The optional
|
|
@@ -92,7 +131,8 @@ it never installs an update by itself.
|
|
|
92
131
|
|
|
93
132
|
## 60-second local quickstart
|
|
94
133
|
|
|
95
|
-
This example
|
|
134
|
+
This example runs on 5.0.0 and retains compatibility with the 3.1.5 command API.
|
|
135
|
+
The metadata below records that older compatibility baseline.
|
|
96
136
|
|
|
97
137
|
<!-- example: {"id":"readme-quickstart", "profile":"3.1.5", "stdout":"sshscript is ready\nexit code: 0\n"} -->
|
|
98
138
|
```python
|
|
@@ -343,10 +383,28 @@ Unknown and changed host keys are rejected unless the caller explicitly
|
|
|
343
383
|
supplies a different Paramiko policy. Do not use automatic key acceptance in a
|
|
344
384
|
production workflow unless a separate trusted bootstrap process has already
|
|
345
385
|
verified the key. See the
|
|
346
|
-
[SSHScript
|
|
386
|
+
[SSHScript documentation](https://iapyeh.github.io/sshscript/v3a/) for
|
|
347
387
|
nested connections, timeouts, file transfer, `sudo`, `su`, and interactive
|
|
348
388
|
programs.
|
|
349
389
|
|
|
390
|
+
## Host-key sources in 5.0
|
|
391
|
+
|
|
392
|
+
SSHScript 5.0.0 uses `known_hosts="parent"` as the default: a local
|
|
393
|
+
Session reads localhost's trust file; `host1.connect("host2")` reads host1's
|
|
394
|
+
trust file over SFTP. This matches the host ownership of `pkey_path`.
|
|
395
|
+
SSHScript 4.0.2 and earlier use localhost for all host-key checks.
|
|
396
|
+
|
|
397
|
+
Set `session.set(known_hosts="chain")` to search from the calling Session
|
|
398
|
+
through its ancestors to localhost. The nearest layer with a target record
|
|
399
|
+
wins; a mismatch or read/parse error stops the connection. Set
|
|
400
|
+
`session.set(known_hosts="local")` to preserve previous behavior.
|
|
401
|
+
Each Session can set its own `known_hosts_path`; this path is not inherited.
|
|
402
|
+
`connect(known_hosts=...)` overrides only one connection. `pkey_path` remains
|
|
403
|
+
on the calling host for every strategy. `policy` handles only unknown keys;
|
|
404
|
+
`AutoAddPolicy` does not write these trust files. See the
|
|
405
|
+
[host-key source contract](API_GUIDE.md#host-key-sources-unreleased) for supported
|
|
406
|
+
file formats, strict error handling and migration.
|
|
407
|
+
|
|
350
408
|
## Reusing SSH configuration
|
|
351
409
|
|
|
352
410
|
Connections from a local session read `~/.ssh/config` if it exists. Supported
|
|
@@ -65,11 +65,20 @@ termination. Verify application outcomes and reconcile unknown termination.
|
|
|
65
65
|
|
|
66
66
|
### SSH identity and host keys
|
|
67
67
|
|
|
68
|
-
SSH connections
|
|
69
|
-
|
|
68
|
+
SSH connections reject unknown or changed keys by default. SSHScript 4.0.2 and earlier
|
|
69
|
+
load localhost system host keys. SSHScript 5.0.0 defaults to
|
|
70
|
+
`known_hosts="parent"`, reading the calling Session host's trust file. Remote
|
|
71
|
+
trust files are read over SFTP using the original SSH login account; verification
|
|
72
|
+
still runs locally. `"chain"` trusts the nearest source with a target record;
|
|
73
|
+
it never falls back after a mismatch or file error. Trusting a parent's records
|
|
74
|
+
also trusts that host/account to maintain them. Use `"local"` to retain the
|
|
75
|
+
previous source selection. See the [host-key source contract](API_GUIDE.md#host-key-sources-unreleased).
|
|
76
|
+
|
|
77
|
+
The application is responsible for provisioning `known_hosts` and
|
|
70
78
|
verifying a new fingerprint through an independent channel. Supplying an
|
|
71
79
|
automatic-acceptance Paramiko policy is an explicit opt-out from this default
|
|
72
80
|
and is not recommended for production enrollment.
|
|
81
|
+
AutoAddPolicy does not persist keys into these local or remote trust files.
|
|
73
82
|
|
|
74
83
|
SSH authentication ultimately follows Paramiko and server policy. Prefer an
|
|
75
84
|
SSH agent, managed key, or secret manager. Never store passwords or private
|