agent-assh 1.0.0 → 1.0.2
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.
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# assh Agent Instructions
|
|
2
|
+
|
|
3
|
+
`assh` is the SSH workflow helper for LLM agents. Start with `assh connect` so access, key setup, tmux, cleanup, and the first persistent session are prepared in one step.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm i -g agent-assh
|
|
9
|
+
assh version
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## First SSH Step
|
|
13
|
+
|
|
14
|
+
If the user pasted a provider server-info block, write the block to a temporary file with mode `0600`, run:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
assh connect-info --file /path/to/tmp-server-info -n NAME
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Then delete the temporary file. Server-info formats vary; if parsing fails, extract host, user, and password yourself, put the password in an environment variable, and use `assh connect -E PASSWORD_ENV`.
|
|
21
|
+
|
|
22
|
+
If first-contact password access may be needed:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
assh connect -H HOST -u root -E PASSWORD_ENV -n NAME
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
If key login is already configured:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
assh connect -H HOST -u root -i ~/.ssh/id_agent_ed25519 -n NAME
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Use the returned `sid` and `next_commands`.
|
|
35
|
+
|
|
36
|
+
## Normal Workflow
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
assh session exec -s SID -- "pwd"
|
|
40
|
+
assh session read -s SID --seq 1 --limit 50
|
|
41
|
+
assh session exec -s SID --timeout 600 -- "git pull"
|
|
42
|
+
assh session read -s SID --seq 2 --stream stderr --limit 50
|
|
43
|
+
assh session close -s SID
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Clean up stale trusted sessions:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
assh session gc --older-than 24h --execute
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## One-Off Commands
|
|
53
|
+
|
|
54
|
+
Use `exec/read` when no persistent directory or environment is needed:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
assh exec -H HOST -u root -i KEY -- "df -h"
|
|
58
|
+
assh read --id OUTPUT_ID --limit 50
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## JSON Rules
|
|
62
|
+
|
|
63
|
+
- Operational commands emit one JSON value by default.
|
|
64
|
+
- Errors use `{"ok":false,"error":"code","message":"..."}`.
|
|
65
|
+
- `session exec` responses include `rc`, `seq`, `stdout_lines`, `stderr_lines`, `sid`, and `session`.
|
|
66
|
+
- Remote non-zero exit status is a command result, not a transport failure.
|
|
67
|
+
|
|
68
|
+
## Context Discipline
|
|
69
|
+
|
|
70
|
+
If `stdout_lines` or `stderr_lines` is large, do not read all output. Use targeted windows with `--limit`, `--offset`, and `--stream`. Use `read --raw` or `session read --raw` only when piping or exact output is required.
|
|
71
|
+
|
|
72
|
+
## Security Rules
|
|
73
|
+
|
|
74
|
+
- Never put passwords in command arguments.
|
|
75
|
+
- Passwords are passed only through env vars named by `--password-env`.
|
|
76
|
+
- For pasted server-info blocks, prefer `connect-info --file`; remove the temporary file after connect.
|
|
77
|
+
- If key login works, `connect` does not read the password env var.
|
|
78
|
+
- Prefer `--host-key-policy strict` when host keys are already managed.
|
|
79
|
+
- Treat `--host-key-policy no-check` as unsafe and only for disposable lab/dev hosts.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
## assh SSH Workflow
|
|
2
|
+
|
|
3
|
+
Use `assh` for SSH work so large remote output stays out of the agent context.
|
|
4
|
+
|
|
5
|
+
Install when missing:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm i -g agent-assh
|
|
9
|
+
assh version
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
### Agent Algorithm
|
|
13
|
+
|
|
14
|
+
```text
|
|
15
|
+
Need SSH?
|
|
16
|
+
If the user pasted provider server info:
|
|
17
|
+
save it to a 0600 temp file
|
|
18
|
+
assh connect-info --file TMP -n NAME
|
|
19
|
+
delete TMP after connect
|
|
20
|
+
if parsing fails, extract host/user/password, put password in env, then use connect
|
|
21
|
+
|
|
22
|
+
First step:
|
|
23
|
+
assh connect -H HOST -u root -E PASSWORD_ENV -n NAME
|
|
24
|
+
or, when key login already works:
|
|
25
|
+
assh connect -H HOST -u root -i KEY -n NAME
|
|
26
|
+
|
|
27
|
+
Continue with returned sid:
|
|
28
|
+
assh session exec -s SID -- "pwd"
|
|
29
|
+
assh session read -s SID --seq 1 --limit 50
|
|
30
|
+
assh session exec -s SID --timeout 600 -- "git pull"
|
|
31
|
+
assh session read -s SID --seq 2 --stream stderr --limit 50
|
|
32
|
+
assh session close -s SID
|
|
33
|
+
|
|
34
|
+
Cleanup:
|
|
35
|
+
assh session gc --older-than 24h --execute
|
|
36
|
+
|
|
37
|
+
Audit:
|
|
38
|
+
assh audit --last 20 --host HOST --failed
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### JSON Contract
|
|
42
|
+
|
|
43
|
+
`assh connect` returns a session id and next commands:
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
{"ok":true,"sid":"f7a2b3c4","session":"deploy","tmux_name":"assh_f7a2b3c4","next_commands":{"exec":"assh session exec -s f7a2b3c4 -- \"pwd\"","read":"assh session read -s f7a2b3c4 --seq 1 --limit 50","close":"assh session close -s f7a2b3c4"}}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
`assh session exec` returns command metadata:
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{"ok":true,"rc":0,"seq":2,"stdout_lines":15,"stderr_lines":0,"sid":"f7a2b3c4","session":"deploy"}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Rules:
|
|
56
|
+
|
|
57
|
+
- Operational commands emit one JSON value by default.
|
|
58
|
+
- `read --raw` and `session read --raw` print only content.
|
|
59
|
+
- Remote non-zero status is a command result, not a transport failure.
|
|
60
|
+
- Passwords are only accepted through environment variables; never put passwords in command arguments.
|
|
61
|
+
- Command text is not written to audit logs.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-assh",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "SSH workflow helper for LLM agents",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -34,6 +34,8 @@
|
|
|
34
34
|
"scripts",
|
|
35
35
|
"README.md",
|
|
36
36
|
"README.en.md",
|
|
37
|
+
"AGENT_INSTRUCTIONS.md",
|
|
38
|
+
"SYSTEM_PROMPT_snippet.md",
|
|
37
39
|
"LICENSE"
|
|
38
40
|
]
|
|
39
41
|
}
|
|
@@ -91,11 +91,6 @@ function ensureReleaseGitState(version = pkg.version) {
|
|
|
91
91
|
if (!hasGitRef(`refs/tags/${tag}`)) {
|
|
92
92
|
git(['tag', tag, 'HEAD']);
|
|
93
93
|
addedTag = true;
|
|
94
|
-
} else {
|
|
95
|
-
const pointsAtHead = git(['tag', '--points-at', 'HEAD']).split(/\r?\n/).includes(tag);
|
|
96
|
-
if (!pointsAtHead) {
|
|
97
|
-
throw new Error(`tag ${tag} exists but does not point at HEAD`);
|
|
98
|
-
}
|
|
99
94
|
}
|
|
100
95
|
|
|
101
96
|
return cleanup;
|
package/scripts/smoke-test.js
CHANGED
|
@@ -8,8 +8,12 @@ const { target } = require('./platform');
|
|
|
8
8
|
const { expectedArchives, verifyArtifactFiles } = require('./release-contract-test');
|
|
9
9
|
|
|
10
10
|
const root = path.join(__dirname, '..');
|
|
11
|
+
const pkg = require(path.join(root, 'package.json'));
|
|
11
12
|
const nativeDir = path.join(root, 'native');
|
|
12
13
|
|
|
14
|
+
assert.ok(pkg.files.includes('AGENT_INSTRUCTIONS.md'), 'package files must include AGENT_INSTRUCTIONS.md');
|
|
15
|
+
assert.ok(pkg.files.includes('SYSTEM_PROMPT_snippet.md'), 'package files must include SYSTEM_PROMPT_snippet.md');
|
|
16
|
+
|
|
13
17
|
assert.deepEqual(target('linux', 'x64'), {
|
|
14
18
|
os: 'linux',
|
|
15
19
|
arch: 'amd64',
|