sshscript 5.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.
Files changed (125) hide show
  1. {sshscript-5.0.0 → sshscript-6.1.0}/.github/workflows/ci.yml +2 -2
  2. {sshscript-5.0.0 → sshscript-6.1.0}/.github/workflows/release.yml +1 -1
  3. {sshscript-5.0.0 → sshscript-6.1.0}/.gitignore +4 -0
  4. sshscript-6.1.0/AI_GUIDE.md +119 -0
  5. {sshscript-5.0.0 → sshscript-6.1.0}/API_GUIDE.md +38 -9
  6. {sshscript-5.0.0 → sshscript-6.1.0}/CHANGELOG.md +52 -0
  7. sshscript-6.1.0/CONSOLE.md +217 -0
  8. sshscript-6.1.0/EMBEDDED_CONSOLE.md +200 -0
  9. {sshscript-5.0.0 → sshscript-6.1.0}/MANIFEST.in +8 -0
  10. {sshscript-5.0.0/src/sshscript.egg-info → sshscript-6.1.0}/PKG-INFO +32 -11
  11. {sshscript-5.0.0 → sshscript-6.1.0}/README.md +30 -9
  12. {sshscript-5.0.0 → sshscript-6.1.0}/RELEASING.md +3 -1
  13. {sshscript-5.0.0 → sshscript-6.1.0}/VERSIONING.md +74 -14
  14. sshscript-6.1.0/examples/embedded_agent.py +36 -0
  15. sshscript-6.1.0/examples/embedded_terminal.py +21 -0
  16. {sshscript-5.0.0 → sshscript-6.1.0}/pyproject.toml +1 -1
  17. {sshscript-5.0.0 → sshscript-6.1.0}/skills/sshscript/SKILL.md +35 -4
  18. {sshscript-5.0.0 → sshscript-6.1.0}/skills/sshscript/references/agent-guide.md +99 -3
  19. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/__init__.py +5 -0
  20. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/__init__.pyi +4 -1
  21. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/_version.py +1 -1
  22. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/channelgeneric.py +7 -2
  23. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/dollarparser.py +3 -1
  24. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/errorutils.py +0 -7
  25. sshscript-6.1.0/src/sshscript/interactive.py +977 -0
  26. sshscript-6.1.0/src/sshscript/interactive.pyi +71 -0
  27. sshscript-6.1.0/src/sshscript/scriptcontrol.py +34 -0
  28. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/session.py +24 -25
  29. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/session.pyi +3 -0
  30. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/sessionwrapper.py +3 -4
  31. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/sessionwrapper.pyi +1 -0
  32. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/spyimporter.py +10 -1
  33. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/sshscript.py +27 -22
  34. sshscript-6.1.0/src/sshscript/unittest/test_embedded_console.py +471 -0
  35. sshscript-6.1.0/src/sshscript/unittest/test_interactive_console.py +631 -0
  36. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_native_auth_runner.py +2 -2
  37. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_native_console_authentication.py +28 -1
  38. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_openssh_integration.py +10 -1
  39. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_public_types.py +2 -1
  40. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_session_close_reporting.py +6 -6
  41. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_sshscript_module.py +4 -4
  42. {sshscript-5.0.0 → sshscript-6.1.0/src/sshscript.egg-info}/PKG-INFO +32 -11
  43. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript.egg-info/SOURCES.txt +9 -0
  44. {sshscript-5.0.0 → sshscript-6.1.0}/tools/check_public_types.py +6 -1
  45. {sshscript-5.0.0 → sshscript-6.1.0}/tools/prepare_release.py +6 -3
  46. {sshscript-5.0.0 → sshscript-6.1.0}/tools/sync_ai_docs.py +11 -0
  47. {sshscript-5.0.0 → sshscript-6.1.0}/tools/sync_api_guide.py +2 -0
  48. sshscript-5.0.0/AI_GUIDE.md +0 -58
  49. {sshscript-5.0.0 → sshscript-6.1.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  50. {sshscript-5.0.0 → sshscript-6.1.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  51. {sshscript-5.0.0 → sshscript-6.1.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  52. {sshscript-5.0.0 → sshscript-6.1.0}/.github/dependabot.yml +0 -0
  53. {sshscript-5.0.0 → sshscript-6.1.0}/.github/pull_request_template.md +0 -0
  54. {sshscript-5.0.0 → sshscript-6.1.0}/.github/workflows/codeql.yml +0 -0
  55. {sshscript-5.0.0 → sshscript-6.1.0}/CODE_OF_CONDUCT.md +0 -0
  56. {sshscript-5.0.0 → sshscript-6.1.0}/CONTRIBUTING.md +0 -0
  57. {sshscript-5.0.0 → sshscript-6.1.0}/EXCEPTIONS.md +0 -0
  58. {sshscript-5.0.0 → sshscript-6.1.0}/LICENSE.txt +0 -0
  59. {sshscript-5.0.0 → sshscript-6.1.0}/SECURITY.md +0 -0
  60. {sshscript-5.0.0 → sshscript-6.1.0}/SUPPORT.md +0 -0
  61. {sshscript-5.0.0 → sshscript-6.1.0}/setup.cfg +0 -0
  62. {sshscript-5.0.0 → sshscript-6.1.0}/src/_sshscript_cli.py +0 -0
  63. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/_runtime.py +0 -0
  64. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/channelssh.py +0 -0
  65. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/channelsubprocess.py +0 -0
  66. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/channelutils.py +0 -0
  67. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/commandjob.py +0 -0
  68. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/commandjob.pyi +0 -0
  69. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/commandresult.py +0 -0
  70. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/commandresult.pyi +0 -0
  71. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/consolejob.py +0 -0
  72. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/dollar.py +0 -0
  73. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/dollarchanger.py +0 -0
  74. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/patching.py +0 -0
  75. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/py.typed +0 -0
  76. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/sessionsettings.py +0 -0
  77. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/sshconfig.py +0 -0
  78. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/stdio.py +0 -0
  79. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/tokenparser.py +0 -0
  80. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/unittest/README.console-authentication.md +0 -0
  81. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/unittest/README.console-results.md +0 -0
  82. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/unittest/README.dollar-syntax-tests.md +0 -0
  83. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/unittest/README.module-tests.md +0 -0
  84. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/unittest/check_package_asserts.py +0 -0
  85. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/unittest/dollar_syntax.spy +0 -0
  86. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/unittest/dollar_syntax_fixture.spy +0 -0
  87. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/unittest/language_fixture.spy +0 -0
  88. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_channelgeneric_expect.py +0 -0
  89. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_check_file.py +0 -0
  90. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_command_api.py +0 -0
  91. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_command_job.py +0 -0
  92. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_console_authentication.py +0 -0
  93. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_console_job.py +0 -0
  94. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_console_results.py +0 -0
  95. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_dollar_command_escapes.py +0 -0
  96. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_file_transfer.py +0 -0
  97. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_logger_api.py +0 -0
  98. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_logger_integration.py +0 -0
  99. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_production_contract.py +0 -0
  100. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_python_version.py +0 -0
  101. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_recommended_examples.py +0 -0
  102. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_release_inputs.py +0 -0
  103. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_session_proxy_cleanup.py +0 -0
  104. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_session_settings.py +0 -0
  105. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_spy_source_mapping.py +0 -0
  106. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_spy_thread_session.py +0 -0
  107. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_ssh_config.py +0 -0
  108. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_ssh_security.py +0 -0
  109. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_sshscript_dollar_syntax.py +0 -0
  110. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_stdin_contract.py +0 -0
  111. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_stdio_dynamic_string.py +0 -0
  112. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_syntax_error.spy +0 -0
  113. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_update_check.py +0 -0
  114. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript/unittest/test_version_policy.py +0 -0
  115. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript.egg-info/dependency_links.txt +0 -0
  116. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript.egg-info/entry_points.txt +0 -0
  117. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript.egg-info/requires.txt +0 -0
  118. {sshscript-5.0.0 → sshscript-6.1.0}/src/sshscript.egg-info/top_level.txt +0 -0
  119. {sshscript-5.0.0 → sshscript-6.1.0}/tools/check_baseline_examples.py +0 -0
  120. {sshscript-5.0.0 → sshscript-6.1.0}/tools/check_release.py +0 -0
  121. {sshscript-5.0.0 → sshscript-6.1.0}/tools/publish_release.py +0 -0
  122. {sshscript-5.0.0 → sshscript-6.1.0}/tools/run_checks.py +0 -0
  123. {sshscript-5.0.0 → sshscript-6.1.0}/tools/run_native_auth_ci.py +0 -0
  124. {sshscript-5.0.0 → sshscript-6.1.0}/tools/setup_openssh_ci.sh +0 -0
  125. {sshscript-5.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: 20
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: 25
51
+ timeout-minutes: 35
52
52
  strategy:
53
53
  fail-fast: false
54
54
  matrix:
@@ -12,7 +12,7 @@ jobs:
12
12
  build:
13
13
  name: Verify and attest release artifacts
14
14
  runs-on: ubuntu-latest
15
- timeout-minutes: 20
15
+ timeout-minutes: 30
16
16
  permissions:
17
17
  contents: read
18
18
  id-token: write
@@ -119,3 +119,7 @@ htmlcov/
119
119
  !/unittest/test_stdin_contract.py
120
120
  !/unittest/test_syntax_error.spy
121
121
  !/unittest/test_update_check.py
122
+
123
+ !/unittest/test_interactive_console.py
124
+
125
+ !/unittest/test_embedded_console.py
@@ -0,0 +1,119 @@
1
+ # SSHScript for AI agents
2
+
3
+ **Documentation contract: SSHScript 6.1.0; Python 3.11+.**
4
+
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.
7
+ For nested SSH, provision known_hosts on each calling host: `"parent"` is the
8
+ default. Read the 4.0.2-to-5.0 migration before reusing older automation.
9
+
10
+ Start with the [agent guide](skills/sshscript/references/agent-guide.md).
11
+ It explains version checks, API selection, command results, input, deadlines,
12
+ authentication and cleanup. Read only the task-relevant sections of the
13
+ [canonical API guide](API_GUIDE.md) and [version policy](VERSIONING.md).
14
+
15
+ The [SSHScript skill](skills/sshscript/SKILL.md) supplies a compact operating
16
+ workflow. Its folder contains the supporting guide; copy the entire
17
+ `skills/sshscript/` folder when installing it in an agent's skill directory.
18
+ It teaches SSHScript usage, not development of SSHScript itself or a particular
19
+ recipe library. Agents without skill support can read these Markdown files.
20
+
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)
30
+
31
+ For step-by-step use from a human or UI agent's retained terminal, see the
32
+ [console guide](CONSOLE.md) and the agent guide's
33
+ [console instructions](skills/sshscript/references/agent-guide.md#interactive-console-61).
34
+ Run `python3 -B sshscript.py console` from the matching development checkout;
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.
37
+
38
+ Input remains Python with dollar commands: `$hostname` executes a command,
39
+ `print($.stdout)` reads its result, and variables persist between inputs.
40
+ `exit`/`exit()` leave one layer; `quit`/`quit()` close all layers. Script
41
+ completion uses `$.stop()`; `$.exit()`, `Session.exit()` and wrapper `.exit()`
42
+ are removed. These incompatible 6.0 rules do not describe stable 5.0.0.
43
+
44
+ ## Share one URL
45
+
46
+ The website entry is [SSHScript for AI Agents](https://iapyeh.github.io/sshscript/v3a/ai-agents/).
47
+ Users can give that URL and a task to an agent:
48
+
49
+ > Read the SSHScript AI agent entry at
50
+ > https://iapyeh.github.io/sshscript/v3a/ai-agents/ and follow its reading order.
51
+ > Check the SSHScript version in my environment before selecting APIs. Help me
52
+ > complete this task: [describe the task, target hosts and expected output].
53
+ > If you cannot read a required document, report that limitation rather than
54
+ > guessing its contents. Explain the proposed commands before execution and
55
+ > stay within the operations I authorize.
56
+
57
+ A URL supplies reference material for the current task. It does not install
58
+ SSHScript, install a skill, grant execution permission, or guarantee persistent
59
+ memory. An agent needs URL-reading tools, or the user must provide the files.
60
+
61
+ ## Set up before you have a task
62
+
63
+ Users can also give an agent this prompt before requesting any automation.
64
+ It requests a complete skill installation and a reusable preference where the
65
+ agent supports them; downloading only `SKILL.md` omits the reference files.
66
+
67
+ ```text
68
+ Read https://iapyeh.github.io/sshscript/v3a/ai-agents/ and the Agent Guide to
69
+ understand SSHScript's purpose, APIs and execution limits.
70
+
71
+ If you support installing skills, download the complete skill bundle linked
72
+ on that page, install it using your environment's skill mechanism, and confirm
73
+ that it can be loaded. If skills are unsupported, use that page as a reference
74
+ entry for future SSHScript tasks.
75
+
76
+ If you support persistent preferences or memory, save this preference:
77
+ For future local or remote command execution, automation and interactive shell
78
+ tasks, first assess whether SSHScript is suitable. Use it according to the
79
+ documentation when appropriate; if another tool is more suitable, explain why.
80
+
81
+ For now, only read the documentation and configure the skill and preference.
82
+ Do not connect to remote hosts or run demonstration commands. Report whether
83
+ the skill was installed and loaded and whether the preference was saved.
84
+ Explain any limitations or steps that could not be completed.
85
+ ```
86
+
87
+ Use the agent's own installation and preference mechanisms. Installation paths
88
+ and invocation syntax vary by agent. If it needs user approval for a setup step,
89
+ it should request that approval before proceeding. Reading a URL alone does
90
+ not create persistent memory or authorize later command execution.
91
+
92
+ ## Maintain the source and website
93
+
94
+ Maintain this entry, `skills/sshscript/SKILL.md`, and its reference guide in
95
+ the designated `working_branch` checkout on branch `working_branch`.
96
+ The landing page is maintained in `gh-pages/v3a/ai-agents/index.md` on branch
97
+ `gh-pages`. The API guide remains authoritative for public behavior.
98
+
99
+ From `working_branch`, synchronize the generated website guide, plain-text
100
+ files and portable skill ZIP with:
101
+
102
+ ```sh
103
+ python3 tools/sync_ai_docs.py ../gh-pages/v3a
104
+ python3 tools/sync_ai_docs.py ../gh-pages/v3a --check
105
+ ```
106
+
107
+ The ZIP includes the skill and its reference guide, plus snapshots of
108
+ `API_GUIDE.md` and `VERSIONING.md` for offline use. These snapshots are generated
109
+ from the source documents; do not edit the ZIP or generated website copies.
110
+ The sync command checks the destination branch before writing. Website pages
111
+ keep their Last Updated timestamp aligned with their mtime.
112
+
113
+ When the execution contract changes, review the skill and guide, update their
114
+ contract labels and the landing page, then regenerate the website assets.
115
+ Validate links, skill structure, example behavior, source/ZIP equality and
116
+ the rendered website. Source and website publishing are separate operations;
117
+ copying files does not publish either of them.
118
+
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 contract: 5.0.0**, Python 3.11+. Sections marked **4.0** require
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,7 +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
- Install the current release with `python -m pip install "sshscript==5.0.0"`.
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.
35
36
  For reproducing only the historical compatibility baseline, explicitly install
36
37
  `sshscript==3.1.5`; the 4.0 sections require the current release.
37
38
  An argv list is one command, not a batch. Shell-looking argument text stays data.
@@ -234,7 +235,7 @@ print(result.stdout)
234
235
 
235
236
  ## 4.0: settings and managed jobs
236
237
 
237
- Use SSHScript 5.0.0 for the following examples. CLI `-v`, `--stderr`, and
238
+ Use SSHScript 6.1.0 for the following examples. CLI `-v`, `--stderr`, and
238
239
  `--debug` override root defaults only for that execution. Child Sessions copy
239
240
  their parent's settings. `set()` validates before changing anything; `get()`
240
241
  returns a copy. Explicit command options override Session policy.
@@ -253,7 +254,7 @@ the settings dictionary at creation; the policy object itself is shared, so
253
254
  custom policies with mutable state must account for reuse. Changing the setting
254
255
  does not reconfigure existing connections.
255
256
 
256
- <!-- example: {"id":"settings", "profile":"5.0.0", "stdout":"True\n1\n"} -->
257
+ <!-- example: {"id":"settings", "profile":"6.1.0", "stdout":"True\n1\n"} -->
257
258
  ```python
258
259
  from contextlib import closing
259
260
  import paramiko
@@ -275,7 +276,7 @@ semantics; do not combine it with `command_timeout`.
275
276
  For an indefinite command such as tcpdump, use `start(timeout=None)`. This safe
276
277
  local example uses a small Python process instead of capturing network traffic:
277
278
 
278
- <!-- example: {"id":"stop", "profile":"5.0.0", "stdout":"cancelled confirmed\n"} -->
279
+ <!-- example: {"id":"stop", "profile":"6.1.0", "stdout":"cancelled confirmed\n"} -->
279
280
  ```python
280
281
  from contextlib import closing
281
282
  import sys
@@ -314,7 +315,7 @@ legacy SSH behavior which appended a newline to strings: if a consumer
314
315
  needs a line, migrate `input=password` to `input=password + "\n"` explicitly.
315
316
  Published 3.1.5 still has the legacy SSH behavior.
316
317
 
317
- <!-- example: {"id":"exact-stdin", "profile":"5.0.0", "stdout":"'abc'\n'abc\\n'\n"} -->
318
+ <!-- example: {"id":"exact-stdin", "profile":"6.1.0", "stdout":"'abc'\n'abc\\n'\n"} -->
318
319
  ```python
319
320
  from contextlib import closing
320
321
  import sys
@@ -383,7 +384,7 @@ or unpack exactly `stdout, stderr, exitcode`. This changes the released 3.1.5
383
384
  console contract: old `stdout, stderr = shell(command)` must be migrated.
384
385
  Shell calls accept command strings; argv execution belongs to Session.
385
386
 
386
- <!-- example: {"id":"shell-result", "profile":"5.0.0", "stdout":"first 0\n"} -->
387
+ <!-- example: {"id":"shell-result", "profile":"6.1.0", "stdout":"first 0\n"} -->
387
388
  ```python
388
389
  from contextlib import closing
389
390
  from sshscript import Session
@@ -439,7 +440,7 @@ matching: `iter_stdout()` yields chunks, not packets or lines. Put `stop()` in
439
440
  `finally`, so condition matches, exceptions, and KeyboardInterrupt all trigger
440
441
  cleanup. This credential-free example uses a continuous Python process:
441
442
 
442
- <!-- example: {"id":"stream-condition", "profile":"5.0.0", "stdout":"cancelled confirmed\n"} -->
443
+ <!-- example: {"id":"stream-condition", "profile":"6.1.0", "stdout":"cancelled confirmed\n"} -->
443
444
  ```python
444
445
  from contextlib import closing
445
446
  import sys
@@ -504,7 +505,7 @@ the current Bash console's channel, cwd, environment and identity. It does not
504
505
  launch through the underlying Session. Ordinary `console(command)` / `$command`
505
506
  still waits and returns `CommandResult`; no migration is needed for finite commands.
506
507
 
507
- <!-- example: {"id":"console-stream", "profile":"5.0.0", "stdout":"cancelled confirmed\nconsole-alive\n"} -->
508
+ <!-- example: {"id":"console-stream", "profile":"6.1.0", "stdout":"cancelled confirmed\nconsole-alive\n"} -->
508
509
  ```python
509
510
  from contextlib import closing
510
511
  import shlex
@@ -762,3 +763,31 @@ host prerequisites and are not credential-free examples.
762
763
  The same source is mirrored into the website; edit this file, then use
763
764
  `tools/sync_api_guide.py` to refresh the website copy. Additional protocol and
764
765
  production details remain in the full documentation.
766
+
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
+
776
+ ## Interactive console and script completion (6.0)
777
+
778
+ See [the console guide](CONSOLE.md) for the 6.0 Python-first dollar REPL, retained
779
+ variables, connect/sudo/su layers, text programs and migration from 5.0.0.
780
+ Ordinary input is Python; `$hostname` runs a shell command and `print($.stdout)`
781
+ reads its result. No language-mode switching is needed.
782
+ Bare `exit` and `exit()` leave one managed layer, or close the console at the
783
+ local root. Bare `quit` and `quit()` close the whole console.
784
+ Use `help` or `help()` for console instructions and `help(object)` for Python
785
+ object documentation.
786
+ `$.stop(code=0, message="")` normally completes the nearest script runner;
787
+ `$.exit()`, `Session.exit()` and wrapper `.exit()` are removed in both modes,
788
+ as is the old `$.break()` name. Session.run()/run_script() return a dictionary-like
789
+ ScriptResult with exitcode, stopped and message attributes; run_file() returns
790
+ the integer status. Cleanup failures still propagate. Python SystemExit remains
791
+ separate from normal script completion.
792
+
793
+ *Last modified: 2026-10-23 18:39:31*
@@ -1,5 +1,55 @@
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
+
26
+ ## 6.0.0 — 2026-10-22 (incompatible source contract)
27
+
28
+ - Add console helpers: help/help(), help(object), exit/exit() to leave one
29
+ managed layer, and quit/quit() for complete cleanup and termination.
30
+ - Ctrl-C discards input or interrupts execution while preserving console state;
31
+ Ctrl-D at the Python prompt quits. Inside enter(), both keys are sent as raw
32
+ program control characters, with parent-shell return detected before unwinding.
33
+
34
+ - Add `sshscript console`: a Python REPL with dollar syntax, persistent Bash
35
+ shells, retained variables and command-result snapshots,
36
+ automatic shell entry after connect, sudo/su layers, explicit text-program
37
+ entry, streaming command output and managed Ctrl-C recovery.
38
+ - Replace `$.break()` with `$.stop()`. Normal completion is handled by the
39
+ nearest script/import boundary and preserves cleanup and partial names.
40
+ - Remove `$.exit()`, Session.exit() and wrapper .exit() in both console and
41
+ script mode. Existing script-ending calls must become stop() or Python
42
+ sys.exit(). Remove the old SSHScriptBreak/SSHScriptExit control exceptions
43
+ and partial wrapper exit.
44
+ - Return dictionary-like ScriptResult completion metadata from source runners;
45
+ preserve integer completion status from run_file().
46
+ - Enable terminal line editing, including both Backspace control codes, when
47
+ Python readline support is available.
48
+ - See [console usage](CONSOLE.md) and [6.0 migration](VERSIONING.md#60-console-and-script-completion-migration).
49
+ Source and local artifacts use 6.0.0. PyPI publication and release tags are
50
+ separate operations.
51
+
52
+
3
53
  This project records user-visible changes here. Release artifacts and their
4
54
  provenance are available from the linked GitHub Release and PyPI pages.
5
55
 
@@ -286,3 +336,5 @@ verified artifacts in the
286
336
 
287
337
  [Unreleased]: https://github.com/iapyeh/sshscript/compare/v3.1.4...release
288
338
  [3.1.4]: https://github.com/iapyeh/sshscript/releases/tag/v3.1.4
339
+
340
+ *Last modified: 2026-10-23 18:39:31*
@@ -0,0 +1,217 @@
1
+ # Interactive console (6.1)
2
+
3
+ The SSHScript 6.1 console runs from the flat development checkout:
4
+
5
+ ```sh
6
+ python3 -B sshscript.py console
7
+ ```
8
+
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
11
+ development checkout is required.
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
+
22
+ ## A Python console with dollar syntax
23
+
24
+ Input is Python by default, with SSHScript dollar syntax available in the same
25
+ namespace. No Python/shell language-mode switching is required:
26
+
27
+ ```text
28
+ local [user]> hostname
29
+ NameError: operation failed; inspect error for details.
30
+ local [user]> hostname = "a Python variable"
31
+ local [user]> hostname
32
+ 'a Python variable'
33
+ local [user]> $hostname
34
+ my-host
35
+ local [user]> print($.stdout)
36
+ my-host
37
+ local [user]> actual_hostname = $.stdout.strip()
38
+ local [user]> options = {"port": 2222, "username": "ops", "timeout": 15}
39
+ local [user]> $.connect("my-ssh-alias", **options)
40
+ ```
41
+
42
+ `hostname` reads a Python variable; `$hostname` executes a command. A shell
43
+ command always needs its dollar prefix. `$cd /tmp` and `$export NAME=value`
44
+ retain the shell's directory and environment for later dollar commands. Output
45
+ streams while a command runs; a nonzero exit status is displayed and the console
46
+ remains usable. Shell commands do not display an extra JobResult representation.
47
+
48
+ `$.stdout`, `$.stderr` and `$.exitcode` read the most recent dollar-command result
49
+ in the current managed layer. These are result snapshots, not drained live
50
+ buffers. The `result` Python name retains the latest dollar-command JobResult.
51
+ You can mix dollar properties with Python expressions and f-strings, or unpack
52
+ command results using the normal `.spy` syntax:
53
+
54
+ ```text
55
+ local [user]> out, err, status = $printf ready
56
+ local [user]> summary = f"host={$.stdout.strip()}, status={$.exitcode}"
57
+ local [user]> for index in range(2):
58
+ ... $f"echo item-{index}"
59
+ ...
60
+ ```
61
+
62
+ On a terminal, the console enables Python's optional `readline` module for line
63
+ editing, including Backspace. Python builds without that module retain the
64
+ terminal's basic input editing; piped input does not require it. No history file
65
+ is written.
66
+
67
+ As in Python's REPL, a blank line completes an indented block. Parenthesized
68
+ expressions and parameter dictionaries can span multiple input lines. Functions
69
+ and variables remain available for later inputs. Ordinary Python strings that
70
+ contain dollar characters remain literal strings. `with` blocks use the normal
71
+ `.spy` compiler's lexical context management, rather than leaving a managed
72
+ interactive layer open after the block. Inside lexical contexts, inspect or
73
+ print returned command output explicitly, just as in a script.
74
+
75
+ Reserved names are `control`, `session` (current connection Session), `console`
76
+ (current shell/identity/program), `result` and `error` (latest operation error).
77
+ `session` and `console` are refreshed when environments change. User variables
78
+ survive environment changes; references to exited environments are not reusable.
79
+ `getpass` is provided for reading secrets without terminal echo. No input history
80
+ is saved by SSHScript; host terminal/session recording remains external.
81
+
82
+ ## Connections and identities
83
+
84
+ A connection automatically enters its own persistent Bash shell. One `exit()`
85
+ closes that shell and connection together and restores the parent environment.
86
+ Nested connections are created by the current connection Session. They retain
87
+ the existing SSH config, key ownership and known-host verification contracts.
88
+
89
+ ```sh
90
+ python3 -B sshscript.py console ops@host1 --port 2222 --timeout 15
91
+ python3 -B sshscript.py console my-ssh-alias --ask-pass
92
+ ```
93
+
94
+ CLI options include `--username`, `--port`, `--pkey-path`, `--timeout`,
95
+ `--ssh-config`, `--known-hosts`, `--known-hosts-path` and `--ask-pass`.
96
+ For interactive parameter dictionaries, use the normal Session.connect keyword
97
+ names. Configure a custom trust path with `$.set(known_hosts_path="...")` on the
98
+ calling Session before connecting. Never disable host-key verification merely
99
+ to make a connection succeed.
100
+
101
+ ```text
102
+ host1 [ops]> $.sudo(password=None)
103
+ host1 [root]> $.su("postgres", password=None)
104
+ host1 [postgres]> exit()
105
+ host1 [root]> exit()
106
+ host1 [ops]> exit()
107
+ local [user]>
108
+ ```
109
+
110
+ Omitting an authentication password asks through getpass in a terminal. Use
111
+ `password=None` explicitly for passwordless authentication; nonterminal input
112
+ requires an explicit password argument rather than an invisible password read.
113
+ You can also set a variable with `password = getpass("Password: ")` in Python and reference it in an expression. Do not place real secrets in literal
114
+ commands or shared transcripts.
115
+
116
+ The displayed account is queried after entering the environment, not assumed
117
+ from the requested account. `:status` shows all environment layers. File
118
+ transfers retain the SSH connection account's permissions even inside sudo/su.
119
+ The console rejects connect() from explicit sudo/su/shell/program layers: leave
120
+ that layer first. Elevating a console does not change the Session's SSH
121
+ credentials or the account owning its SFTP/key-file reads.
122
+
123
+ ## Text programs and interruption
124
+
125
+ Use explicit enter() for a program that asks questions or provides a REPL:
126
+
127
+ ```text
128
+ local [user]> $.enter("python3 -q -u", prompt=">>>", exit="quit()")
129
+ local [?] program> print(6 * 7)
130
+ 42
131
+ local [?] program> exit()
132
+ local [user]>
133
+ ```
134
+
135
+ Configure the program's actual readiness prompt and exit sequence. Plain input
136
+ in this layer goes to the program; `exit`, `exit()`, `quit`, `quit()`, lines
137
+ beginning with `$.` and the colon management commands remain reserved. Use
138
+ `$.input(...)` or `$.send(...)` to send text that would otherwise be interpreted as a management expression.
139
+ Ctrl-C/Ctrl-D go to the entered program. In particular, Ctrl-D in an entered
140
+ Python REPL exits that REPL and restores the SSHScript prompt, rather than
141
+ closing the entire SSHScript console. Outside enter(), Ctrl-C requests managed
142
+ shell cancellation or interrupts Python code. An interrupted Python multiline
143
+ input is discarded. A failed/unusable channel
144
+ is removed from the environment stack; the console never silently reconnects
145
+ and claims that the previous state survived. At the Python prompt, use
146
+ `exit` or `exit()` to leave a layer. If a text program returns to its parent
147
+ shell and that return is detected, its program layer is closed automatically.
148
+
149
+ This console supports text commands and explicitly configured text programs,
150
+ not full-screen applications or transparent terminal passthrough. Dollar shell
151
+ commands do not automatically detect interactive programs; use enter() for them.
152
+ Command results retain up to 8 MiB per stream; reaching that capture limit is
153
+ reported and the displayed stream may omit output. PTY stdout/stderr can merge.
154
+ Readiness or silence is not interpreted as a successful process exit.
155
+
156
+ ## Stop a script, leave a layer, or quit
157
+
158
+ - `$.stop(code=0, message="")` normally completes the nearest script execution.
159
+ It is unavailable at the console prompt itself.
160
+ - Bare `exit` and `exit()` leave one managed interactive layer. At the local
161
+ root they close the console. These are console helpers, not script APIs.
162
+ - Bare `help` or `help()` displays console instructions; `help(object)` retains
163
+ Python's object documentation behavior.
164
+ - Bare `quit` and `quit()` close every managed layer. Calling `exit()` or
165
+ `quit()` unwinds the current Python cell, including `finally`/lexical contexts,
166
+ before performing the requested cleanup.
167
+ - Ctrl-C at a Python prompt discards the pending input. During Python execution
168
+ it interrupts that code; during a shell command it requests managed cancellation.
169
+ Variables and usable environments remain available afterward.
170
+ - Ctrl-D/EOF at the Python prompt closes all layers, like quit(). In a terminal,
171
+ EOF is produced by Ctrl-D on an empty input line; partial-line behavior follows
172
+ the terminal's normal line editing.
173
+ - In an enter() program, Ctrl-C/Ctrl-D are forwarded as raw terminal control
174
+ characters, without an added newline. Only a detected return to the parent
175
+ shell closes that program layer; the Python console stays available.
176
+ - Use `help`/`help()` and `quit`/`quit()`. The former `:help` and `:quit` aliases
177
+ are removed; `:status` shows the environment stack.
178
+ The Python helper names can be shadowed by assignment, just like Python names;
179
+ the colon aliases and `control.quit()` remain available.
180
+ - Python `sys.exit()` retains SystemExit semantics. The interactive boundary
181
+ catches it, reports its status and keeps the console open.
182
+
183
+ The console intentionally gives `exit` and `exit()` the same one-layer behavior.
184
+ Use `quit` or `quit()` for all layers, and `$.stop()` for normal script
185
+ completion. `$.exit()` is removed in both console and script mode.
186
+
187
+ `script_result = $.run(source)` executes in the current connection Session; it
188
+ does not inherit an elevated console as its command target. `$.run_file(path)` uses the public
189
+ file runner, which creates a fresh local Session. A stopped script returns to
190
+ the console. Assign its returned ScriptResult to inspect completion and names;
191
+ script results do not automatically replace the console's Python namespace.
192
+ `with` and `finally` still perform cleanup on early completion.
193
+
194
+ `Session.run()` and `run_script()` return ScriptResult, a dictionary of executed
195
+ names with `.exitcode`, `.stopped` and `.message` attributes. `run_file()` returns
196
+ the integer completion status. A stopped imported `.spy` module keeps the names
197
+ initialized before stop(), and its importer continues normally. Cleanup failures
198
+ remain errors even if the script requested normal completion.
199
+
200
+ ## Incompatible changes from 5.0.0
201
+
202
+ Replace `$.break(...)` with `$.stop(...)`. Replace script-ending `$.exit(...)`
203
+ with `$.stop(...)` for normal completion, or Python `sys.exit(...)` when the
204
+ caller should receive SystemExit. The old Break/Exit exception classes and the
205
+ old direct wrapper exit behavior are removed. `$.exit()`, `Session.exit()`
206
+ and wrapper `.exit()` are no longer available. In the console, use `exit` or
207
+ `exit()` to leave one managed layer. In scripts, let Python manage lexical
208
+ `with` blocks. The earlier preview's bare-exit reminder and whole-console
209
+ `exit()` behavior are replaced by this one-layer rule.
210
+
211
+ The initial shell-first preview's `:python` and `:shell` commands are removed.
212
+ Input now follows Python/dollar syntax throughout; prefix shell commands with `$`.
213
+
214
+ Type `help` for a reminder. Operation failures display their type without
215
+ reprinting credential-bearing input; inspect `error` at the prompt for details.
216
+
217
+ *Last modified: 2026-10-23 18:39:31*