@appsoftwareltd/etherpk-mcp 0.7.0 → 0.7.1

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/README.md CHANGED
@@ -198,6 +198,12 @@ pnpm publish --access public --no-git-checks # prepack builds; pnpm
198
198
  npx -y @appsoftwareltd/etherpk-mcp@<version> --version # verify, once the registry lists it
199
199
  ```
200
200
 
201
+ From outside the `apps/mcp` directory:
202
+
203
+ ```sh
204
+ npx -y @appsoftwareltd/etherpk-mcp@<version> --version # verify, once the registry lists it
205
+ ```
206
+
201
207
  The end-to-end specs in `tests-sync/agents/` build and drive this bundle against a real Sync
202
208
  Server and a real folder; the technical notes are in the repo under
203
209
  `docs/docs/technical/Headless Client.md`.
package/dist/main.js CHANGED
@@ -31,7 +31,7 @@ import katex from "katex";
31
31
  import Mustache from "mustache";
32
32
  var package_default = {
33
33
  name: "@appsoftwareltd/etherpk-mcp",
34
- version: "0.7.0",
34
+ version: "0.7.1",
35
35
  license: "Elastic-2.0",
36
36
  description: "EtherPK Headless Client: an MCP server over a synced knowledge graph, run beside the agent on the user's own machine.",
37
37
  type: "module",
@@ -3854,19 +3854,25 @@ function folderCacheDir(env, folderPath) {
3854
3854
  function cacheRoot(env) {
3855
3855
  return env.ETHERPK_MCP_CACHE_DIR?.trim() || join(env.XDG_CACHE_HOME?.trim() || join(homedir(), ".cache"), "etherpk", "mcp");
3856
3856
  }
3857
+ /**
3858
+ * The files this build reads and writes in a graph's directory, by name: the cache, the index,
3859
+ * and the [[Embedding]] store beside the index with a stamp of its own (ADR 0076) - an index
3860
+ * schema bump discards `index.v<N>` and leaves the vectors alone, because their rows cost
3861
+ * minutes to re-derive where the index's cost a second. `removeStaleFiles` keeps exactly these
3862
+ * names, so they are declared once here and joined to a directory below; a name is never
3863
+ * recovered from a joined path (see there).
3864
+ */
3865
+ var CACHE_FILE_NAME = `local-cache.v${CACHE_DB_VERSION}.bin`;
3866
+ var INDEX_FILE_NAME = `index.v11.sqlite`;
3867
+ var VECTORS_FILE_NAME = `vectors.v1.sqlite`;
3857
3868
  function cacheFile(dir) {
3858
- return join(dir, `local-cache.v${CACHE_DB_VERSION}.bin`);
3869
+ return join(dir, CACHE_FILE_NAME);
3859
3870
  }
3860
3871
  function indexFile(dir) {
3861
- return join(dir, `index.v11.sqlite`);
3872
+ return join(dir, INDEX_FILE_NAME);
3862
3873
  }
3863
- /**
3864
- * The [[Embedding]] store, beside the index but with its own stamp (ADR 0076): an index
3865
- * schema bump discards `index.v<N>` and leaves this file alone, because its rows cost minutes
3866
- * to re-derive where the index's cost a second.
3867
- */
3868
3874
  function vectorsFile(dir) {
3869
- return join(dir, `vectors.v1.sqlite`);
3875
+ return join(dir, VECTORS_FILE_NAME);
3870
3876
  }
3871
3877
  function request(req) {
3872
3878
  return new Promise((resolve, reject) => {
@@ -3997,10 +4003,10 @@ var ABANDONED_TMP_AFTER_MS = 10 * 6e4;
3997
4003
  */
3998
4004
  async function removeStaleFiles(dir, now = Date.now()) {
3999
4005
  const keep = new Set([
4000
- cacheFile(dir),
4001
- indexFile(dir),
4002
- vectorsFile(dir)
4003
- ].map((f) => f.split("/").pop()));
4006
+ CACHE_FILE_NAME,
4007
+ INDEX_FILE_NAME,
4008
+ VECTORS_FILE_NAME
4009
+ ]);
4004
4010
  const removed = [];
4005
4011
  for (const name of await readdir(dir).catch(() => [])) {
4006
4012
  const stale = /^(index|vectors)\.v\d+\.sqlite$/.test(name) && !keep.has(name);
@@ -17363,6 +17369,24 @@ async function isGraphFolder(root) {
17363
17369
  return true;
17364
17370
  }
17365
17371
  //#endregion
17372
+ //#region src/serve-lifetime.ts
17373
+ /** Bind every end to one single-flight shutdown; returns it, for an end the caller finds itself. */
17374
+ function bindServeLifetime(deps) {
17375
+ let ending;
17376
+ const shutdown = (end) => ending ??= deps.shutdown(end);
17377
+ let spoken = false;
17378
+ deps.stdin.once("data", () => {
17379
+ spoken = true;
17380
+ });
17381
+ deps.stdin.once("end", () => {
17382
+ if (spoken) shutdown("the client closed stdin");
17383
+ });
17384
+ deps.signals.on("SIGINT", () => void shutdown("SIGINT"));
17385
+ deps.signals.on("SIGTERM", () => void shutdown("SIGTERM"));
17386
+ deps.transportClosed(() => void shutdown("the transport closed"));
17387
+ return shutdown;
17388
+ }
17389
+ //#endregion
17366
17390
  //#region src/main.ts
17367
17391
  /**
17368
17392
  * `etherpk-mcp`: the [[Headless Client]]'s command line (ADR 0072).
@@ -17830,14 +17854,19 @@ async function serveGraph(graph, graphName, args) {
17830
17854
  cmd: CMD
17831
17855
  });
17832
17856
  const transport = new StdioServerTransport();
17833
- const shutdown = async () => {
17834
- await graph.settle().catch(() => {});
17835
- await graph.dispose().catch(() => {});
17836
- process.exit(0);
17837
- };
17838
- process.on("SIGINT", () => void shutdown());
17839
- process.on("SIGTERM", () => void shutdown());
17840
- transport.onclose = () => void shutdown();
17857
+ bindServeLifetime({
17858
+ signals: process,
17859
+ stdin: process.stdin,
17860
+ transportClosed: (listener) => {
17861
+ transport.onclose = listener;
17862
+ },
17863
+ async shutdown(end) {
17864
+ console.error(`etherpk-mcp: ${end}; flushing and exiting.`);
17865
+ await graph.settle().catch(() => {});
17866
+ await graph.dispose().catch(() => {});
17867
+ process.exit(0);
17868
+ }
17869
+ });
17841
17870
  await server.connect(transport);
17842
17871
  console.error(`etherpk-mcp: serving "${graphName}" over stdio as "Agent on ${hostname()}".`);
17843
17872
  chromiumStatus(process.env, CMD).then((chromium) => {