mirror of
https://bitbucket.org/siakitem/my-pi.git
synced 2026-10-26 10:45:22 +00:00
从官方 Git 仓库导入 chandra447/pi-hermes-memory 在 tag v0.9.10 (df624056a2e8c68bc89ba0a81e1c120856ec9665,2026-10-02)的源码快照。 本次仅导入源码,尚未接入组合包:package.json、加载顺序、依赖与权限策略 均未改动。 - 原样导入 src/、tests/、docs/、scripts/、README、CHANGELOG、PLAN、 AGENTS 与 LICENSE;该快照共 156 个上游文件。 - 剔除嵌套 .git、上游 .github 工作流、.ralph/、.claude/、node_modules、 覆盖率与构建产物。 - 新增中文 UPSTREAM.md,记录来源,以及启用前必须完成的待决事项: 在 pi-compaction 之前注册加载顺序、better-sqlite3 版本与根 allowScripts 对齐、strip-ansi 解析确认。 - 说明选择 tag 而非 main 的原因:导入时上游 main 已领先至 24fcd8ad26fc27badb8d7dbaf8034645de07b893(2026-10-11),选 tag 是为 对应一个已发布、可复现的版本。 尚未接线。启用前还需反转 tests/extension-load.test.ts 中"不得加载 Hermes"的断言,并同步 pi-tool-search 分组、权限策略与文档。 验证:快照 156 个上游文件全部导入,目录内仅多出 UPSTREAM.md 一份; 已确认与上游 main 内容存在差异,证明取的是 tag 而非 main。
108 lines
5.1 KiB
JavaScript
108 lines
5.1 KiB
JavaScript
#!/usr/bin/env node
|
|
// Exercise Pi's real jiti loader and completion SDK without package-local peers.
|
|
// npm access installs production dependencies; model traffic is loopback-only.
|
|
import assert from "node:assert/strict";
|
|
import { execFileSync } from "node:child_process";
|
|
import { mkdtemp, mkdir, writeFile, rm } from "node:fs/promises";
|
|
import { createServer } from "node:http";
|
|
import { createRequire } from "node:module";
|
|
import { tmpdir } from "node:os";
|
|
import { dirname, join } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
const repo = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
const root = await mkdtemp(join(tmpdir(), "pi-hermes-production-"));
|
|
const previousAgentDir = process.env.PI_CODING_AGENT_DIR;
|
|
let requests = 0;
|
|
const server = createServer((req, res) => {
|
|
req.resume();
|
|
req.on("end", () => {
|
|
if (req.url !== "/v1/chat/completions") {
|
|
res.writeHead(404).end();
|
|
return;
|
|
}
|
|
requests++;
|
|
res.writeHead(200, { "content-type": "text/event-stream" });
|
|
const chunk = (delta, finish_reason) => ({
|
|
id: "smoke", object: "chat.completion.chunk", created: 0, model: "smoke",
|
|
choices: [{ index: 0, delta, finish_reason }],
|
|
});
|
|
res.end([
|
|
`data: ${JSON.stringify(chunk({ role: "assistant", content: '{"operations":[]}' }, null))}`,
|
|
`data: ${JSON.stringify(chunk({}, "stop"))}`,
|
|
"data: [DONE]", "",
|
|
].join("\n\n"));
|
|
});
|
|
});
|
|
|
|
function npm(args, cwd) {
|
|
return execFileSync(process.platform === "win32" ? "npm.cmd" : "npm", args, {
|
|
cwd, encoding: "utf8", timeout: 180_000, stdio: ["ignore", "pipe", "inherit"],
|
|
});
|
|
}
|
|
|
|
try {
|
|
process.env.PI_CODING_AGENT_DIR = join(root, "agent");
|
|
await mkdir(process.env.PI_CODING_AGENT_DIR);
|
|
const [packed] = JSON.parse(npm(["pack", "--ignore-scripts", "--json", "--pack-destination", root], repo));
|
|
const install = join(root, "install");
|
|
await mkdir(install);
|
|
await writeFile(join(install, "package.json"), JSON.stringify({ name: "production-smoke", private: true }));
|
|
npm(["install", "--omit=dev", "--omit=peer", "--ignore-scripts", "--no-audit", "--no-fund", join(root, packed.filename)], install);
|
|
const installed = join(install, "node_modules", "pi-hermes-memory");
|
|
const requireFromPackage = createRequire(join(installed, "package.json"));
|
|
for (const specifier of ["@earendil-works/pi-ai/compat", "@earendil-works/pi-coding-agent"]) {
|
|
assert.throws(() => requireFromPackage.resolve(specifier), { code: "MODULE_NOT_FOUND" });
|
|
}
|
|
|
|
await new Promise((resolve, reject) => {
|
|
server.once("error", reject);
|
|
server.listen(0, "127.0.0.1", resolve);
|
|
});
|
|
const baseUrl = `http://127.0.0.1:${server.address().port}/v1`;
|
|
const probe = join(installed, "production-probe.ts");
|
|
await writeFile(probe, `
|
|
import { Type } from "typebox";
|
|
import { runDirectMemoryCompletion } from "./src/handlers/review-memory-ops.js";
|
|
export default function (pi) {
|
|
pi.registerTool({
|
|
name: "production_probe", label: "Probe", description: "Synthetic direct completion", parameters: Type.Object({}),
|
|
async execute() {
|
|
const model = {
|
|
provider: "openai", api: "openai-completions", id: "smoke", name: "Smoke", baseUrl: ${JSON.stringify(baseUrl)},
|
|
reasoning: false, input: ["text"], contextWindow: 8192, maxTokens: 1024,
|
|
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
};
|
|
const result = await runDirectMemoryCompletion({
|
|
model, modelRegistry: { getApiKeyAndHeaders: async () => ({ ok: true, apiKey: "synthetic-smoke-key" }) },
|
|
}, {}, null, { userPrompt: "Return no memory operations.", systemPrompt: "Synthetic test.", config: {}, timeoutMs: 10000 });
|
|
return { content: [{ type: "text", text: JSON.stringify(result) }], details: result };
|
|
},
|
|
});
|
|
}
|
|
`);
|
|
|
|
// The host SDK is installed in the development checkout, not beside the
|
|
// production package. Only Pi's loader can provide its aliases to the probe.
|
|
const hostSdk = import.meta.resolve("@earendil-works/pi-coding-agent");
|
|
const { loadExtensions } = await import(new URL("./core/extensions/loader.js", hostSdk).href);
|
|
const loaded = await loadExtensions([join(installed, "src", "index.ts"), probe], install);
|
|
assert.deepEqual(loaded.errors, []);
|
|
const tool = loaded.extensions.find((extension) => extension.tools.has("production_probe"))
|
|
?.tools.get("production_probe").definition;
|
|
assert.ok(tool, "production probe registered through Pi");
|
|
for (const operation of ["review", "flush", "correction", "consolidation"]) {
|
|
const result = await tool.execute(operation, {});
|
|
assert.equal(result.details.ok, true, JSON.stringify(result.details));
|
|
assert.equal(result.details.appliedCount, 0);
|
|
}
|
|
assert.equal(requests, 4);
|
|
console.log("Production install passed: no package-local SDK peers; Pi jiti load and four loopback direct completions succeeded.");
|
|
} finally {
|
|
server.closeAllConnections();
|
|
await new Promise((resolve) => server.close(resolve));
|
|
if (previousAgentDir === undefined) delete process.env.PI_CODING_AGENT_DIR;
|
|
else process.env.PI_CODING_AGENT_DIR = previousAgentDir;
|
|
await rm(root, { recursive: true, force: true });
|
|
}
|