mirror of
https://bitbucket.org/siakitem/my-pi.git
synced 2026-08-28 08:35:57 +00:00
feat: add pure ssh2 remote operations
This commit is contained in:
@@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
- Treat `ssh_bash` as a Bash output-compaction alias for ANSI stripping, build/test/Git/linter aggregation, and bounded truncation without enabling remote command rewriting; keep `ssh_find`, `ssh_grep`, and `ssh_read` outside RTK processing.
|
||||
|
||||
## [0.9.0] - 2026-07-03
|
||||
|
||||
### Changed
|
||||
|
||||
@@ -40,6 +40,8 @@ Multi-stage pipeline to reduce token consumption:
|
||||
| Anchor-Safe Read Compaction | Detects hashline/anchored `read` output and preserves complete edit anchors when filtering or truncating anchored lines |
|
||||
| Hard Truncation | Final character limit enforcement |
|
||||
|
||||
Bash output compaction applies to both the local `bash` tool and `pi-ssh`'s `ssh_bash` output alias. This only sanitizes/compacts returned text: RTK never rewrites remote commands. Structured remote search results from `ssh_find` and `ssh_grep`, and exact remote file reads from `ssh_read`, remain outside RTK processing.
|
||||
|
||||
### Interactive Settings
|
||||
|
||||
- Tabbed TUI settings modal via `/rtk` command
|
||||
|
||||
@@ -318,6 +318,10 @@ await runTest("session_start refreshes RTK provenance and runtime guard skips mi
|
||||
|
||||
assert.equal(rewriteCalls, 1);
|
||||
assert.ok((rewrittenEvent.input as { command: string }).command.includes("rtk git status"));
|
||||
const remoteEvent = { toolName: "ssh_bash", input: { command: "git status" } };
|
||||
await toolCallHandler(remoteEvent, createNotificationContext(notifications));
|
||||
assert.equal(remoteEvent.input.command, "git status");
|
||||
assert.equal(rewriteCalls, 1, "ssh_bash output support must not enable remote command rewriting");
|
||||
assert.ok(execCommands.includes("/opt/rtk/bin/rtk"));
|
||||
writeTestConfig(false);
|
||||
});
|
||||
@@ -367,6 +371,26 @@ await runTest("tool execution lifecycle sanitizes streamed bash output", async (
|
||||
};
|
||||
await endHandler(endEvent, {});
|
||||
assert.equal(firstText(endEvent.result.content), "error: build failed\n");
|
||||
|
||||
await startHandler(
|
||||
{ toolName: "ssh_bash", toolCallId: "ssh-bash-1", args: { command: "npm test" } },
|
||||
{},
|
||||
);
|
||||
const remoteUpdate = {
|
||||
toolName: "ssh_bash",
|
||||
toolCallId: "ssh-bash-1",
|
||||
args: { command: "npm test" },
|
||||
partialResult: { content: [{ type: "text", text: "\x1B[32mremote test\x1B[0m\n" }] },
|
||||
};
|
||||
await updateHandler(remoteUpdate, {});
|
||||
assert.equal(firstText(remoteUpdate.partialResult.content), "remote test\n");
|
||||
const remoteEnd = {
|
||||
toolName: "ssh_bash",
|
||||
toolCallId: "ssh-bash-1",
|
||||
result: { content: [{ type: "text", text: "\x1B[31mremote failed\x1B[0m\n" }] },
|
||||
};
|
||||
await endHandler(remoteEnd, {});
|
||||
assert.equal(firstText(remoteEnd.result.content), "remote failed\n");
|
||||
});
|
||||
|
||||
await runTest("tool_result lifecycle merges compaction metadata with existing details", async () => {
|
||||
|
||||
@@ -234,8 +234,8 @@ export default function rtkIntegrationExtension(pi: ExtensionAPI): void {
|
||||
};
|
||||
|
||||
/**
|
||||
* Shared guard for bash tool-execution events: skips when compaction is
|
||||
* disabled, normalizes the event to a record, tracks the bash command, and
|
||||
* Shared guard for local and SSH bash-output execution events: skips when
|
||||
* compaction is disabled, normalizes the event, tracks the command, and
|
||||
* returns the record for further handler-specific processing.
|
||||
*/
|
||||
const recordBashEventIfEnabled = (
|
||||
@@ -246,7 +246,7 @@ export default function rtkIntegrationExtension(pi: ExtensionAPI): void {
|
||||
}
|
||||
|
||||
const eventRecord = toRecord(event);
|
||||
if (eventRecord.toolName !== "bash") {
|
||||
if (eventRecord.toolName !== "bash" && eventRecord.toolName !== "ssh_bash") {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -409,7 +409,7 @@ export default function rtkIntegrationExtension(pi: ExtensionAPI): void {
|
||||
|
||||
pi.on("tool_execution_end", async (event) => {
|
||||
const eventRecord = toRecord(event);
|
||||
if (eventRecord.toolName !== "bash") {
|
||||
if (eventRecord.toolName !== "bash" && eventRecord.toolName !== "ssh_bash") {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -70,6 +70,34 @@ function assertNoPartialHashlineAnchors(text: string): void {
|
||||
}
|
||||
}
|
||||
|
||||
runTest("ssh_bash output uses Bash compaction while remote search output remains untouched", () => {
|
||||
const config = cloneDefaultConfig();
|
||||
const sshBash = compactToolResult(
|
||||
{
|
||||
toolName: "ssh_bash",
|
||||
input: { command: "npm test" },
|
||||
content: [{ type: "text", text: "\x1B[32mTests: 2 passed\x1B[0m\n" }],
|
||||
},
|
||||
config,
|
||||
);
|
||||
assert.equal(sshBash.changed, true);
|
||||
assert.ok(sshBash.techniques.includes("ansi"));
|
||||
assert.equal(firstTextBlock(sshBash.content).includes("\x1B"), false);
|
||||
|
||||
for (const toolName of ["ssh_find", "ssh_grep"]) {
|
||||
const search = compactToolResult(
|
||||
{
|
||||
toolName,
|
||||
input: { pattern: "TODO" },
|
||||
content: [{ type: "text", text: "\x1B[31mremote result\x1B[0m\n" }],
|
||||
},
|
||||
config,
|
||||
);
|
||||
assert.equal(search.changed, false);
|
||||
assert.deepEqual(search.techniques, []);
|
||||
}
|
||||
});
|
||||
|
||||
runTest("precision read with offset keeps exact output (no source/smart/hard truncation)", () => {
|
||||
const config = cloneDefaultConfig();
|
||||
setReadCompaction(config, true);
|
||||
|
||||
@@ -627,7 +627,7 @@ export function compactToolResult(
|
||||
|
||||
const { changed, mapped: nextContent } = mapTextContentBlocks(sourceContent, (contentBlock) => {
|
||||
let transformed = { text: contentBlock.text, techniques: [] as string[] };
|
||||
if (event.toolName === "bash") {
|
||||
if (event.toolName === "bash" || event.toolName === "ssh_bash") {
|
||||
transformed = compactBashText(contentBlock.text, normalizeCommand(input), config);
|
||||
} else if (event.toolName === "read") {
|
||||
const normalizedPath = normalizePath(input);
|
||||
|
||||
Reference in New Issue
Block a user