feat: add pure ssh2 remote operations

This commit is contained in:
云服务部-叶林立
2026-08-21 19:51:43 +08:00
parent d3bf562189
commit 0ac50eb581
62 changed files with 3701 additions and 47 deletions
+101
View File
@@ -0,0 +1,101 @@
import assert from "node:assert/strict";
import test from "node:test";
import type { PermissionsService } from "@gotgenes/pi-permission-system";
import {
formatSshPermissionInput,
installSshPermissionIntegration,
type SshPermissionConnection,
} from "../permission-integration.ts";
function makeService() {
const formatters = new Map<string, (input: Record<string, unknown>) => string | undefined>();
const extractors = new Map<string, (input: Record<string, unknown>) => string | undefined>();
const service = {
registerToolInputFormatter(name: string, formatter: (input: Record<string, unknown>) => string | undefined) {
formatters.set(name, formatter);
return () => formatters.delete(name);
},
registerToolAccessExtractor(name: string, extractor: (input: Record<string, unknown>) => string | undefined) {
extractors.set(name, extractor);
return () => extractors.delete(name);
},
} as unknown as PermissionsService;
return { service, formatters, extractors };
}
function makePi() {
const hooks = new Map<string, Array<() => void>>();
const eventHooks = new Map<string, Array<() => void>>();
const add = (map: Map<string, Array<() => void>>, name: string, handler: () => void) => {
map.set(name, [...(map.get(name) ?? []), handler]);
};
return {
api: {
on: (name: string, handler: () => void) => add(hooks, name, handler),
events: { on: (name: string, handler: () => void) => add(eventHooks, name, handler) },
},
emit: (name: string) => hooks.get(name)?.forEach((handler) => handler()),
emitEvent: (name: string) => eventHooks.get(name)?.forEach((handler) => handler()),
};
}
const connection: SshPermissionConnection = {
remote: "packaging-server",
port: 2222,
remoteCwd: "/srv/build",
};
test("formats the SSH target and bounded operation details", () => {
assert.equal(
formatSshPermissionInput("ssh_read", { path: "src/main.ts", offset: 5, limit: 20 }, connection),
"SSH target 'packaging-server:2222' in remote cwd '/srv/build'; read remote path 'src/main.ts', offset 5, limit 20",
);
assert.equal(
formatSshPermissionInput("ssh_write", { path: "dist/a.txt", content: "one\ntwo" }, connection),
"SSH target 'packaging-server:2222' in remote cwd '/srv/build'; write remote path 'dist/a.txt' (2 lines, 7 characters)",
);
assert.equal(
formatSshPermissionInput("ssh_grep", { pattern: "TODO", path: "src", include: "*.ts", limit: 25 }, connection),
"SSH target 'packaging-server:2222' in remote cwd '/srv/build'; search remote file contents under 'src', for 'TODO', limit 25, file glob '*.ts'",
);
});
test("registers previews and disables local path extraction for remote file tools", () => {
const { service, formatters, extractors } = makeService();
const pi = makePi();
const dispose = installSshPermissionIntegration(
pi.api as never,
() => connection,
{ getPermissionsService: () => service, permissionsReadyChannel: "permissions:ready" },
);
assert.deepEqual([...formatters.keys()], ["ssh_read", "ssh_write", "ssh_edit", "ssh_find", "ssh_grep", "ssh_bash"]);
assert.deepEqual([...extractors.keys()], ["ssh_read", "ssh_write", "ssh_edit", "ssh_find", "ssh_grep"]);
assert.equal(extractors.get("ssh_read")?.({ path: "/remote/secret" }), undefined);
assert.equal(extractors.get("ssh_grep")?.({ path: "/remote/src" }), undefined);
assert.match(formatters.get("ssh_bash")?.({ command: "git push" }) ?? "", /packaging-server:2222/);
dispose();
assert.equal(formatters.size, 0);
assert.equal(extractors.size, 0);
});
test("registers when the permission service becomes ready and cleans up on shutdown", () => {
const { service, formatters, extractors } = makeService();
const pi = makePi();
let published: PermissionsService | undefined;
installSshPermissionIntegration(pi.api as never, () => connection, {
getPermissionsService: () => published,
permissionsReadyChannel: "permissions:ready",
});
assert.equal(formatters.size, 0);
published = service;
pi.emitEvent("permissions:ready");
assert.equal(formatters.size, 6);
assert.equal(extractors.size, 5);
pi.emit("session_shutdown");
assert.equal(formatters.size, 0);
assert.equal(extractors.size, 0);
});