pi-ssh-remote 0.1.8 → 0.1.10
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.
- package/CHANGELOG.md +31 -1
- package/LICENSE +0 -0
- package/README.md +5 -5
- package/README.zh-CN.md +5 -5
- package/index.ts +40 -11
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -18,6 +18,34 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Rel
|
|
|
18
18
|
|
|
19
19
|
- None.
|
|
20
20
|
|
|
21
|
+
## [0.1.10] - 2026-08-16
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
- None.
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
|
|
29
|
+
- None.
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
|
|
33
|
+
- Prevented the agent-facing `memory` action from clearing or replacing server memory accidentally: omission now inspects the saved memory, non-empty text appends idempotently, empty text is rejected, and clearing requires an explicit `--clear` value.
|
|
34
|
+
|
|
35
|
+
## [0.1.9] - 2026-08-12
|
|
36
|
+
|
|
37
|
+
### Added
|
|
38
|
+
|
|
39
|
+
- None.
|
|
40
|
+
|
|
41
|
+
### Changed
|
|
42
|
+
|
|
43
|
+
- Removed the redundant `SSH remote` prefix from footer status labels.
|
|
44
|
+
|
|
45
|
+
### Fixed
|
|
46
|
+
|
|
47
|
+
- Kept injected server memory as a single stable context prefix so tool continuations do not repeatedly move it to the end of the conversation or invalidate cached provider context.
|
|
48
|
+
|
|
21
49
|
## [0.1.8] - 2026-08-10
|
|
22
50
|
|
|
23
51
|
### Added
|
|
@@ -161,7 +189,9 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Rel
|
|
|
161
189
|
|
|
162
190
|
- None.
|
|
163
191
|
|
|
164
|
-
[Unreleased]: https://github.com/petrichor20211/pi-ssh-remote/compare/v0.1.
|
|
192
|
+
[Unreleased]: https://github.com/petrichor20211/pi-ssh-remote/compare/v0.1.10...HEAD
|
|
193
|
+
[0.1.10]: https://github.com/petrichor20211/pi-ssh-remote/compare/v0.1.9...v0.1.10
|
|
194
|
+
[0.1.9]: https://github.com/petrichor20211/pi-ssh-remote/compare/v0.1.8...v0.1.9
|
|
165
195
|
[0.1.8]: https://github.com/petrichor20211/pi-ssh-remote/compare/v0.1.7...v0.1.8
|
|
166
196
|
[0.1.7]: https://github.com/petrichor20211/pi-ssh-remote/compare/v0.1.6...v0.1.7
|
|
167
197
|
[0.1.6]: https://github.com/petrichor20211/pi-ssh-remote/compare/v0.1.5...v0.1.6
|
package/LICENSE
CHANGED
|
File without changes
|
package/README.md
CHANGED
|
@@ -73,7 +73,7 @@ Requires Node.js 20+. The remote server must provide Bash, SFTP, and GNU `timeou
|
|
|
73
73
|
From this point, normal Pi operations target `/srv/project` on the remote server. The footer makes that routing explicit:
|
|
74
74
|
|
|
75
75
|
```text
|
|
76
|
-
|
|
76
|
+
H100 training server (root@gpu-box.example.com:2202):/srv/project
|
|
77
77
|
```
|
|
78
78
|
|
|
79
79
|
Return to local tools with:
|
|
@@ -164,7 +164,7 @@ Forward the remote service on port 8000 to localhost:8000, but keep my coding
|
|
|
164
164
|
tools on the local repository.
|
|
165
165
|
```
|
|
166
166
|
|
|
167
|
-
These requests are handled through the agent-facing `remote` tool; slash commands are optional.
|
|
167
|
+
These requests are handled through the agent-facing `remote` tool; slash commands are optional. For safety, calling its `memory` action without a `memory` argument only reads the current server memory. Non-empty text is appended to the existing memory instead of replacing it; retrying the same final entry is a no-op. Clearing still requires the explicit `--clear` value.
|
|
168
168
|
|
|
169
169
|
## Command reference
|
|
170
170
|
|
|
@@ -215,13 +215,13 @@ The extension currently supports direct SSH commands with `-p`, `-l`, and `-i`.
|
|
|
215
215
|
|
|
216
216
|
## Releases
|
|
217
217
|
|
|
218
|
-
Latest release: [v0.1.
|
|
218
|
+
Latest release: [v0.1.10](https://github.com/petrichor20211/pi-ssh-remote/releases/tag/v0.1.10)
|
|
219
219
|
|
|
220
220
|
| Version | Date | Highlights |
|
|
221
221
|
|---|---|---|
|
|
222
|
+
| [0.1.10](CHANGELOG.md#0110---2026-08-16) | 2026-08-16 | Safe inspect-by-default and append-only agent updates for server memory |
|
|
223
|
+
| [0.1.9](CHANGELOG.md#019---2026-08-12) | 2026-08-12 | Stable deduplicated server-memory context and shorter footer labels |
|
|
222
224
|
| [0.1.8](CHANGELOG.md#018---2026-08-10) | 2026-08-10 | Explicit private-key login, cross-port server memory, and the shorter `remote` tool name |
|
|
223
|
-
| [0.1.7](CHANGELOG.md#017---2026-08-05) | 2026-08-05 | Session-aware SSH workspace restoration for new, forked, and resumed sessions |
|
|
224
|
-
| [0.1.6](CHANGELOG.md#016---2026-08-05) | 2026-08-05 | Bounded remote reads, streamed command output, and per-turn output budgets |
|
|
225
225
|
|
|
226
226
|
See [CHANGELOG.md](CHANGELOG.md) for the complete release history, including additions, behavior changes, and bug fixes.
|
|
227
227
|
|
package/README.zh-CN.md
CHANGED
|
@@ -39,7 +39,7 @@ ssh user@host "cd /path && ..."
|
|
|
39
39
|
插件会把当前远程目录和服务器信息写入 Pi 的上下文,并显示在底部状态栏:
|
|
40
40
|
|
|
41
41
|
```text
|
|
42
|
-
|
|
42
|
+
H100 训练机 (root@gpu-box.example.com:2202):/srv/project
|
|
43
43
|
```
|
|
44
44
|
|
|
45
45
|
这样无论是用户还是 Agent,都能随时确认当前操作发生在本地还是远端,减少误操作。
|
|
@@ -218,7 +218,7 @@ pi install npm:pi-ssh-remote
|
|
|
218
218
|
把远端 8000 端口转发到本地 8000,但代码工具继续留在本地。
|
|
219
219
|
```
|
|
220
220
|
|
|
221
|
-
Pi 会通过插件提供的 `remote`
|
|
221
|
+
Pi 会通过插件提供的 `remote` 工具完成这些操作。为防止误删,调用 `memory` action 但不传 `memory` 参数时只会读取当前服务器记忆;传入非空内容时会追加到现有记忆,而不是覆盖,重复提交相同的末尾内容不会再次追加;清除时仍必须显式传入 `--clear`。
|
|
222
222
|
|
|
223
223
|
## 命令说明
|
|
224
224
|
|
|
@@ -294,13 +294,13 @@ Pi 会通过插件提供的 `remote` 工具完成这些操作。
|
|
|
294
294
|
|
|
295
295
|
## 版本发布
|
|
296
296
|
|
|
297
|
-
最新版本:[v0.1.
|
|
297
|
+
最新版本:[v0.1.10](https://github.com/petrichor20211/pi-ssh-remote/releases/tag/v0.1.10)
|
|
298
298
|
|
|
299
299
|
| 版本 | 日期 | 主要内容 |
|
|
300
300
|
|---|---|---|
|
|
301
|
+
| [0.1.10](CHANGELOG.md#0110---2026-08-16) | 2026-08-16 | 默认安全读取且仅追加的服务器记忆 agent 更新逻辑 |
|
|
302
|
+
| [0.1.9](CHANGELOG.md#019---2026-08-12) | 2026-08-12 | 稳定且去重的服务器记忆上下文与更短的状态栏标签 |
|
|
301
303
|
| [0.1.8](CHANGELOG.md#018---2026-08-10) | 2026-08-10 | 显式私钥登录、跨端口服务器记忆与更短的 `remote` 工具名 |
|
|
302
|
-
| [0.1.7](CHANGELOG.md#017---2026-08-05) | 2026-08-05 | 新建、分叉和恢复 session 时自动恢复对应 SSH 工作区 |
|
|
303
|
-
| [0.1.6](CHANGELOG.md#016---2026-08-05) | 2026-08-05 | 有界远端读取、流式命令输出与每轮输出预算 |
|
|
304
304
|
|
|
305
305
|
完整的新增内容、行为变更和 Bug 修复记录请查看 [CHANGELOG.md](CHANGELOG.md)。
|
|
306
306
|
|
package/index.ts
CHANGED
|
@@ -931,8 +931,8 @@ export default function sshRemoteExtension(pi: ExtensionAPI) {
|
|
|
931
931
|
const status = (ctx: any) => {
|
|
932
932
|
currentCtx = ctx;
|
|
933
933
|
if (!remote) ctx.ui.setStatus("ssh-remote", undefined);
|
|
934
|
-
else if (routeRemoteTools) ctx.ui.setStatus("ssh-remote", ctx.ui.theme.fg("accent",
|
|
935
|
-
else ctx.ui.setStatus("ssh-remote", ctx.ui.theme.fg("accent", `
|
|
934
|
+
else if (routeRemoteTools) ctx.ui.setStatus("ssh-remote", ctx.ui.theme.fg("accent", `${endpointDisplayLabel(remote)}:${remote.cwd}`));
|
|
935
|
+
else ctx.ui.setStatus("ssh-remote", ctx.ui.theme.fg("accent", `tunnel ${[...forwardServers.keys()].join(",") || endpointDisplayLabel(remote)}`));
|
|
936
936
|
};
|
|
937
937
|
|
|
938
938
|
const attachClient = (state: RemoteState) => {
|
|
@@ -940,7 +940,7 @@ export default function sshRemoteExtension(pi: ExtensionAPI) {
|
|
|
940
940
|
client.on("close", () => {
|
|
941
941
|
if (remote?.client !== client) return;
|
|
942
942
|
if (currentCtx) {
|
|
943
|
-
currentCtx.ui.setStatus("ssh-remote", currentCtx.ui.theme.fg("warning", `
|
|
943
|
+
currentCtx.ui.setStatus("ssh-remote", currentCtx.ui.theme.fg("warning", `reconnecting ${endpointDisplayLabel(state)}…`));
|
|
944
944
|
}
|
|
945
945
|
void reconnectRemote().catch((error) => {
|
|
946
946
|
if (currentCtx) currentCtx.ui.notify(`SSH remote automatic reconnection failed: ${(error as Error).message}`, "error");
|
|
@@ -1055,7 +1055,7 @@ export default function sshRemoteExtension(pi: ExtensionAPI) {
|
|
|
1055
1055
|
}
|
|
1056
1056
|
|
|
1057
1057
|
let password = getCachedPassword(parsed);
|
|
1058
|
-
ctx.ui.setStatus("ssh-remote", ctx.ui.theme.fg("warning", `
|
|
1058
|
+
ctx.ui.setStatus("ssh-remote", ctx.ui.theme.fg("warning", `connecting ${endpointDisplayLabel(parsed)}…`));
|
|
1059
1059
|
try {
|
|
1060
1060
|
let authentication = parsed.identityFile
|
|
1061
1061
|
? await privateKeyAuthentication(parsed, ctx)
|
|
@@ -1299,6 +1299,7 @@ export default function sshRemoteExtension(pi: ExtensionAPI) {
|
|
|
1299
1299
|
promptGuidelines: [
|
|
1300
1300
|
"Use remote when the user asks the agent to enter, reconnect, inspect, or leave a remote SSH environment.",
|
|
1301
1301
|
"Use remote with action chdir when the user asks to change the remote working directory; do not emulate a persistent directory change with action exec and a one-command cwd.",
|
|
1302
|
+
"Use remote with action memory and no memory argument to inspect server memory. Non-empty memory text appends to the existing memory; clear it only by passing --clear explicitly.",
|
|
1302
1303
|
`Always set timeout for remote exec commands; it defaults to ${DEFAULT_REMOTE_TIMEOUT_SECONDS} seconds when omitted.`,
|
|
1303
1304
|
"Keep remote exec output narrow with tail, sed, rg limits, or similarly bounded commands; never cat large logs or emit broad file listings.",
|
|
1304
1305
|
"Use remote with action disconnect after remote work when the user asks to return to the local environment.",
|
|
@@ -1307,7 +1308,7 @@ export default function sshRemoteExtension(pi: ExtensionAPI) {
|
|
|
1307
1308
|
action: StringEnum(["connect", "reconnect", "status", "disconnect", "forget", "forward", "unforward", "exec", "chdir", "note", "memory"] as const),
|
|
1308
1309
|
command: Type.Optional(Type.String({ description: "SSH command for connect, such as ssh root@host -p 22 or ssh -i ~/.ssh/id_ed25519 root@host; optionally selects the endpoint for note or memory" })),
|
|
1309
1310
|
note: Type.Optional(Type.String({ description: "Endpoint note for the note action; omit or use an empty string to clear it" })),
|
|
1310
|
-
memory: Type.Optional(Type.String({ description: "Persistent context shared by the endpoint's user@host across SSH ports; omit
|
|
1311
|
+
memory: Type.Optional(Type.String({ description: "Persistent context shared by the endpoint's user@host across SSH ports; omit to inspect, provide non-empty text to append without overwriting existing memory, or use --clear to clear explicitly" })),
|
|
1311
1312
|
cwd: Type.Optional(Type.String({ description: "Remote working directory; required for chdir, and a one-command override for exec" })),
|
|
1312
1313
|
forwards: Type.Optional(Type.String({ description: "Space-separated LOCAL_PORT:REMOTE_HOST:REMOTE_PORT mappings; defaults to ssh-remote-config.json" })),
|
|
1313
1314
|
remoteCommand: Type.Optional(Type.String({ description: "Remote shell command for the exec action" })),
|
|
@@ -1366,12 +1367,37 @@ export default function sshRemoteExtension(pi: ExtensionAPI) {
|
|
|
1366
1367
|
details: { endpoint: label, note },
|
|
1367
1368
|
};
|
|
1368
1369
|
}
|
|
1369
|
-
const
|
|
1370
|
-
const server = serverMemoryId(
|
|
1370
|
+
const parsed = parseSshCommand(command);
|
|
1371
|
+
const server = serverMemoryId(parsed);
|
|
1372
|
+
if (params.memory === undefined) {
|
|
1373
|
+
const memory = endpointMemory(parsed);
|
|
1374
|
+
return {
|
|
1375
|
+
content: [{ type: "text", text: memory ? `SSH remote server memory (${server}):\n${memory}` : `No SSH remote server memory is configured (${server}).` }],
|
|
1376
|
+
details: { endpoint: label, server, memory },
|
|
1377
|
+
};
|
|
1378
|
+
}
|
|
1379
|
+
const addition = params.memory.trim();
|
|
1380
|
+
if (!addition) throw new Error("memory must contain text; omit it to inspect the current memory or use --clear to clear it explicitly");
|
|
1381
|
+
if (addition.toLowerCase() === "--clear") {
|
|
1382
|
+
saveServerMemory(command, undefined);
|
|
1383
|
+
return {
|
|
1384
|
+
content: [{ type: "text", text: `SSH remote server memory cleared (${server}).` }],
|
|
1385
|
+
details: { endpoint: label, server, memory: undefined },
|
|
1386
|
+
};
|
|
1387
|
+
}
|
|
1388
|
+
const currentMemory = endpointMemory(parsed);
|
|
1389
|
+
const duplicate = currentMemory === addition || currentMemory?.endsWith(`\n\n${addition}`);
|
|
1390
|
+
if (duplicate) {
|
|
1391
|
+
return {
|
|
1392
|
+
content: [{ type: "text", text: `SSH remote server memory already ends with this content (${server}); nothing was changed.` }],
|
|
1393
|
+
details: { endpoint: label, server, memory: currentMemory, addedMemory: addition, changed: false },
|
|
1394
|
+
};
|
|
1395
|
+
}
|
|
1396
|
+
const memory = currentMemory ? `${currentMemory}\n\n${addition}` : addition;
|
|
1371
1397
|
saveServerMemory(command, memory);
|
|
1372
1398
|
return {
|
|
1373
|
-
content: [{ type: "text", text:
|
|
1374
|
-
details: { endpoint: label, server, memory },
|
|
1399
|
+
content: [{ type: "text", text: `SSH remote server memory appended (${server}). It applies to every port for this user and host.` }],
|
|
1400
|
+
details: { endpoint: label, server, memory, addedMemory: addition, changed: true },
|
|
1375
1401
|
};
|
|
1376
1402
|
}
|
|
1377
1403
|
if (params.action === "exec") {
|
|
@@ -1679,14 +1705,17 @@ export default function sshRemoteExtension(pi: ExtensionAPI) {
|
|
|
1679
1705
|
if (!remote || !routeRemoteTools) return undefined;
|
|
1680
1706
|
const content = serverMemoryContext(remote);
|
|
1681
1707
|
if (!content) return undefined;
|
|
1708
|
+
const messages = event.messages.filter((message) =>
|
|
1709
|
+
message.role !== "custom" || message.customType !== "ssh-remote-server-memory"
|
|
1710
|
+
);
|
|
1682
1711
|
return {
|
|
1683
|
-
messages: [
|
|
1712
|
+
messages: [{
|
|
1684
1713
|
role: "custom",
|
|
1685
1714
|
customType: "ssh-remote-server-memory",
|
|
1686
1715
|
content,
|
|
1687
1716
|
display: false,
|
|
1688
1717
|
timestamp: Date.now(),
|
|
1689
|
-
}],
|
|
1718
|
+
}, ...messages],
|
|
1690
1719
|
};
|
|
1691
1720
|
});
|
|
1692
1721
|
}
|