@appsoftwareltd/etherpk-mcp 0.10.0 → 0.10.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 +22 -5
- package/dist/main.js +118 -22
- package/dist/main.js.map +1 -1
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -38,8 +38,10 @@ No sign-in and no Sync Server:
|
|
|
38
38
|
claude mcp add etherpk -- npx @appsoftwareltd/etherpk-mcp serve --folder /path/to/your/notes
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
-
The folder must already be an EtherPK graph,
|
|
42
|
-
|
|
41
|
+
The folder must already be an EtherPK graph, with `pages/` and `journals/` folders in it. EtherPK
|
|
42
|
+
makes them when it opens a folder, and the [agent skill](#the-agent-skill) makes them for a new
|
|
43
|
+
one. **Settings → Agents** shows this command with the path filled in once the folder's path is set
|
|
44
|
+
on the General tab.
|
|
43
45
|
|
|
44
46
|
### Ask about your notes
|
|
45
47
|
|
|
@@ -169,9 +171,13 @@ npx @appsoftwareltd/etherpk-mcp publish --graph <id or name> --publication <id>
|
|
|
169
171
|
|
|
170
172
|
- `--out` is remembered for that graph and publication. Later runs without it, and the agent's
|
|
171
173
|
`publish` tool, write to the same folder.
|
|
172
|
-
- The top-level `.html` files, `assets/`, `theme/` and the search
|
|
173
|
-
|
|
174
|
-
|
|
174
|
+
- The top-level `.html` files, `assets/`, `theme/` and the search and feed files are rewritten,
|
|
175
|
+
and files that no longer belong are removed. Everything else in the folder is left alone.
|
|
176
|
+
- The folder must be one of its own, outside the graph's folder. A publish refuses the graph's
|
|
177
|
+
folder, a folder inside it and a folder that holds it, because it would remove the graph's files.
|
|
178
|
+
- `--dry-run` in place of `--out` renders and checks the publication and prints the whole report,
|
|
179
|
+
with the names of the pages it mentions, without writing anything. It needs no folder. Keep its
|
|
180
|
+
output out of public logs. The agent's `publish` tool takes `dryRun: true` for the same check.
|
|
175
181
|
- Deploying the folder is up to you. Run the command from cron for a site that republishes itself.
|
|
176
182
|
- Mermaid diagrams and maps need a browser. A map is published as a picture of the map, drawn over
|
|
177
183
|
EtherPK's map host unless `ETHERPK_MAP_STYLE_URL` names another style. `diagrams setup` installs a
|
|
@@ -180,6 +186,17 @@ npx @appsoftwareltd/etherpk-mcp publish --graph <id or name> --publication <id>
|
|
|
180
186
|
computer instead, which NixOS needs.
|
|
181
187
|
- `list_publications`, or **Settings → Publish** in EtherPK, shows the publication ids.
|
|
182
188
|
|
|
189
|
+
## The agent skill
|
|
190
|
+
|
|
191
|
+
The `etherpk` agent skill teaches an agent to make a blog, docs site or wiki folder in EtherPK's
|
|
192
|
+
format inside any project, to check it with `publish --dry-run`, and to publish it with this
|
|
193
|
+
command. For a synced graph it sends the agent to the tools above. Install it in the project the
|
|
194
|
+
agent works in, or with `-g` for every project:
|
|
195
|
+
|
|
196
|
+
```sh
|
|
197
|
+
npx skills add appsoftwareltd/etherpk-client
|
|
198
|
+
```
|
|
199
|
+
|
|
183
200
|
## Reference
|
|
184
201
|
|
|
185
202
|
Every command is `npx @appsoftwareltd/etherpk-mcp <command>`. For the short form, `etherpk-mcp`,
|
package/dist/main.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import { createInterface } from "node:readline/promises";
|
|
3
3
|
import { availableParallelism, homedir, hostname, tmpdir } from "node:os";
|
|
4
|
-
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
4
|
+
import nodePath, { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
5
5
|
import { parseArgs } from "node:util";
|
|
6
6
|
import { access, chmod, lstat, mkdir, open, readFile, readdir, realpath, rename, rm, stat, unlink, utimes, writeFile } from "node:fs/promises";
|
|
7
7
|
import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
|
|
@@ -717,7 +717,7 @@ function looksLikeAgentSetupCode(value) {
|
|
|
717
717
|
}
|
|
718
718
|
var package_default = {
|
|
719
719
|
name: "@appsoftwareltd/etherpk-mcp",
|
|
720
|
-
version: "0.10.
|
|
720
|
+
version: "0.10.1",
|
|
721
721
|
license: "Elastic-2.0",
|
|
722
722
|
description: "EtherPK Headless Client: an MCP server over a synced knowledge graph, run beside the agent on the user's own machine.",
|
|
723
723
|
type: "module",
|
|
@@ -14847,6 +14847,45 @@ async function writePublishFolders(path, folders) {
|
|
|
14847
14847
|
function publishFolderOf(folders, graphKey, publicationId) {
|
|
14848
14848
|
return folders.folders[graphKey]?.[publicationId] ?? null;
|
|
14849
14849
|
}
|
|
14850
|
+
/**
|
|
14851
|
+
* How two absolute folder paths relate, by the platform's own `relative` rather than by splitting
|
|
14852
|
+
* on `/` (Windows paths use `\`, and a shared prefix such as `notes` and `notes-site` is not a
|
|
14853
|
+
* parent). Spelling only: {@link publishFolderOverlap} resolves symbolic links first.
|
|
14854
|
+
*/
|
|
14855
|
+
function relateFolders(graphFolder, publishFolder, options = {}) {
|
|
14856
|
+
const path = options.path ?? nodePath;
|
|
14857
|
+
const caseInsensitive = options.caseInsensitive ?? (process.platform === "darwin" || process.platform === "win32");
|
|
14858
|
+
const fold = (p) => caseInsensitive ? path.resolve(p).toLowerCase() : path.resolve(p);
|
|
14859
|
+
const graph = fold(graphFolder);
|
|
14860
|
+
const site = fold(publishFolder);
|
|
14861
|
+
const below = (from, to) => {
|
|
14862
|
+
const rel = path.relative(from, to);
|
|
14863
|
+
return rel !== "" && rel !== ".." && !rel.startsWith(`..${path.sep}`) && !path.isAbsolute(rel);
|
|
14864
|
+
};
|
|
14865
|
+
if (path.relative(graph, site) === "") return "same";
|
|
14866
|
+
if (below(graph, site)) return "inside";
|
|
14867
|
+
if (below(site, graph)) return "contains";
|
|
14868
|
+
return null;
|
|
14869
|
+
}
|
|
14870
|
+
/**
|
|
14871
|
+
* A path with its symbolic links resolved, for a folder that may not exist yet: the nearest
|
|
14872
|
+
* existing ancestor is resolved and the rest appended, so `--out ./site` under a linked parent is
|
|
14873
|
+
* compared where it will really be written.
|
|
14874
|
+
*/
|
|
14875
|
+
async function canonicalFolder(folder) {
|
|
14876
|
+
const absolute = resolve(folder);
|
|
14877
|
+
try {
|
|
14878
|
+
return await realpath(absolute);
|
|
14879
|
+
} catch {
|
|
14880
|
+
const parent = dirname(absolute);
|
|
14881
|
+
if (parent === absolute) return absolute;
|
|
14882
|
+
return join(await canonicalFolder(parent), basename(absolute));
|
|
14883
|
+
}
|
|
14884
|
+
}
|
|
14885
|
+
/** {@link relateFolders} over the folders as they are on disk, symbolic links followed. */
|
|
14886
|
+
async function publishFolderOverlap(graphFolder, publishFolder) {
|
|
14887
|
+
return relateFolders(await canonicalFolder(graphFolder), await canonicalFolder(publishFolder));
|
|
14888
|
+
}
|
|
14850
14889
|
/** The same file with one publication's folder set (made absolute) - the caller writes it. */
|
|
14851
14890
|
function withPublishFolder(folders, graphKey, publicationId, folder) {
|
|
14852
14891
|
return { folders: {
|
|
@@ -17579,6 +17618,26 @@ async function findPublication(graph, id, host) {
|
|
|
17579
17618
|
if (!found) throw publicationNotFound(id, publications, via);
|
|
17580
17619
|
return found;
|
|
17581
17620
|
}
|
|
17621
|
+
var OVERLAP_WORDS = {
|
|
17622
|
+
same: "is the folder of",
|
|
17623
|
+
inside: "is inside the folder of",
|
|
17624
|
+
contains: "holds the folder of"
|
|
17625
|
+
};
|
|
17626
|
+
/**
|
|
17627
|
+
* Refuse a Publish Folder that overlaps a served folder: the graph's own folder, one inside it, or
|
|
17628
|
+
* one holding it. A publish deletes the files it did not write from the folder it writes into
|
|
17629
|
+
* (`isOwnedPath`), which would take the graph's attachments, or mix the site's files into the
|
|
17630
|
+
* graph. Checked where a folder is set (`--out`) and again before every publish, since the
|
|
17631
|
+
* folder may have been set before this check existed. A synced graph has no folder here.
|
|
17632
|
+
*/
|
|
17633
|
+
async function checkPublishFolder(graph, publicationId, folder, host) {
|
|
17634
|
+
if (graph.backend.kind !== "folder") return;
|
|
17635
|
+
const overlap = await publishFolderOverlap(graph.backend.path, folder);
|
|
17636
|
+
if (!overlap) return;
|
|
17637
|
+
const { cmd, via } = hostOf$1(host);
|
|
17638
|
+
const command = `${cmd} publish --folder "${graph.backend.path}" --publication ${publicationId} --out <a folder outside the graph>`;
|
|
17639
|
+
throw new ToolError("publish_folder_overlaps_graph", `${folder} ${OVERLAP_WORDS[overlap]} the graph "${graph.name}" (${graph.backend.path}). A publish deletes the files it did not write from the folder it publishes into, so the site needs a folder of its own outside the graph. Nothing was published. ` + (via === "cli" ? `Run: ${command}` : `Ask the user to run: ${command}`));
|
|
17640
|
+
}
|
|
17582
17641
|
/** The publication page's mapping and body, through the raw handles: what `publish-service` writes to. */
|
|
17583
17642
|
function frontmatterStore(graph) {
|
|
17584
17643
|
return {
|
|
@@ -17710,7 +17769,7 @@ function head(items, n = 20) {
|
|
|
17710
17769
|
/**
|
|
17711
17770
|
* Publish one publication into its Publish Folder and report. The folder is the one a person
|
|
17712
17771
|
* set for this graph and publication on this machine (ADR 0086); a publish with Mermaid needs
|
|
17713
|
-
* the browser (ADR 0084).
|
|
17772
|
+
* the browser (ADR 0084). A dry run does everything but write, so it needs no folder.
|
|
17714
17773
|
*
|
|
17715
17774
|
* The result IS the report, trimmed to counts and first entries: nothing of it is written into
|
|
17716
17775
|
* the folder, because a static host serves whatever the folder holds and the report names every
|
|
@@ -17727,10 +17786,12 @@ async function publish(graph, args, host) {
|
|
|
17727
17786
|
const publication = summary.publications.find((p) => p.id === args.id.trim());
|
|
17728
17787
|
if (!publication) throw publicationNotFound(args.id, summary.publications, via);
|
|
17729
17788
|
const folder = (await foldersFor(graph, env))(publication.id);
|
|
17730
|
-
|
|
17789
|
+
const dryRun = args.dryRun === true;
|
|
17790
|
+
if (!folder && !dryRun) {
|
|
17731
17791
|
const where = graph.backend.kind === "folder" ? `--folder "${graph.backend.path}"` : `--graph "${graph.name}"`;
|
|
17732
17792
|
throw new ToolError("no_publish_folder", `No publish folder is set for "${publication.name}" on this machine. Ask the user to run: ${cmd} publish ${where} --publication ${publication.id} --out <dir> - that publishes once and remembers the folder for this tool.`);
|
|
17733
17793
|
}
|
|
17794
|
+
if (folder && !dryRun) await checkPublishFolder(graph, publication.id, folder, host);
|
|
17734
17795
|
const selected = selectDocuments(source.documents, publication, summary.publications);
|
|
17735
17796
|
const includeTexts = Object.values(publication.includes).map((concept) => source.documents.find((d) => d.concept.toLowerCase() === concept.toLowerCase())?.text ?? "");
|
|
17736
17797
|
const bodies = [...selected.included.map((d) => d.text), ...includeTexts];
|
|
@@ -17761,7 +17822,7 @@ async function publish(graph, args, host) {
|
|
|
17761
17822
|
}),
|
|
17762
17823
|
unsettled
|
|
17763
17824
|
});
|
|
17764
|
-
const written = run.report.ok ? await writeSite(nodeSiteFolder(folder), run.bundle, {
|
|
17825
|
+
const written = run.report.ok && folder && !dryRun ? await writeSite(nodeSiteFolder(folder), run.bundle, {
|
|
17765
17826
|
seeded: run.seeded,
|
|
17766
17827
|
agentsMd: run.agentsMd
|
|
17767
17828
|
}) : null;
|
|
@@ -17769,6 +17830,7 @@ async function publish(graph, args, host) {
|
|
|
17769
17830
|
for (const e of run.report.excluded) excludedByReason[e.reason] = (excludedByReason[e.reason] ?? 0) + 1;
|
|
17770
17831
|
return {
|
|
17771
17832
|
ok: run.report.ok,
|
|
17833
|
+
...dryRun ? { dryRun: true } : {},
|
|
17772
17834
|
publication: run.report.publication,
|
|
17773
17835
|
folder,
|
|
17774
17836
|
written: written ? {
|
|
@@ -18500,7 +18562,7 @@ function createMcpServer(source, info) {
|
|
|
18500
18562
|
"graph_insights says what the links show across the whole graph (the most-linked concepts, pages many documents ask for that do not exist yet, pages linked to nothing, groups of related concepts and the concepts bridging them), and graph_path finds how two concepts are connected. Use them to answer questions about the shape of the graph, or to suggest pages to write or link.",
|
|
18501
18563
|
"Start with graph_info to see what you are connected to. read_documents reads several documents in one call. tasks lists tasks and set_task changes one (status, priority, due and scheduled dates) by document and line.",
|
|
18502
18564
|
"A task reference is how a person hands you one task: its words, then an EtherPK address ending in #task=<line>-<fingerprint>. Pass the whole reference, or the address, to read_task for the task and its detail, to set_task (as reference) to change it, and to add_task_note to write what you did under it. It finds the task after lines are added above it or its tags change; if its words were edited it answers task_not_found, and you should ask the user for a fresh one.",
|
|
18503
|
-
"Publishing: list_publications shows the publications this graph defines (a publication is a page whose frontmatter defines it - its outline is the site navigation) and the public documents none takes. A document is on a site when its frontmatter has public: true and names the publication in publications. create_publication and update_publication change the settings, and publish writes the site into the publish folder the user set for it on this machine with the etherpk-mcp publish command (the tool cannot choose a folder) and returns the report. Diagrams need a browser the user installs once with \"diagrams setup\".",
|
|
18565
|
+
"Publishing: list_publications shows the publications this graph defines (a publication is a page whose frontmatter defines it - its outline is the site navigation) and the public documents none takes. A document is on a site when its frontmatter has public: true and names the publication in publications. create_publication and update_publication change the settings, and publish writes the site into the publish folder the user set for it on this machine with the etherpk-mcp publish command (the tool cannot choose a folder) and returns the report. publish with dryRun: true renders and reports without writing and needs no folder: run it after editing a publication's pages and fix what it names. Diagrams need a browser the user installs once with \"diagrams setup\".",
|
|
18504
18566
|
"Themes: a publication's look is a theme - Mustache templates, a stylesheet, a script and a manifest. list_themes shows the bundled ones (read-only) and the graph's own. read_theme writes a theme's files to a folder on this machine to read and edit. customise_publication_theme copies a publication's bundled theme into the graph and points the publication at the copy (create_theme copies any theme). write_theme_file, delete_theme_file and import_theme_folder change a graph theme. preview_theme renders a theme to a folder (with screenshots when a browser is set up) to check before publish. For a snippet such as an analytics script, an include slot (update_publication includes, e.g. head) filled by a page may be lighter than a theme copy.",
|
|
18505
18567
|
"Images and files are assets: upload_asset adds a file from this machine and returns the markdown to paste into a document. read_asset writes an asset to a local file you can open, and list_assets shows the assets documents reference. An asset is available only where a document you can read references it (or you uploaded it this session). read_asset, read_theme and preview_theme write under the graph's downloads directory and return the path - name a folder relative to it, never elsewhere.",
|
|
18506
18568
|
"The user documentation is at https://docs.etherpk.com."
|
|
@@ -18757,8 +18819,11 @@ function createMcpServer(source, info) {
|
|
|
18757
18819
|
}, async (args) => run((graph) => updatePublication(graph, args, host)));
|
|
18758
18820
|
server.registerTool("publish", {
|
|
18759
18821
|
title: "Publish",
|
|
18760
|
-
description: "Render a publication to its publish folder on this machine and return the report: what was included and why documents were left out, missing links, assets, warnings. The folder is the one the user set with \"etherpk-mcp publish --publication <id> --out <dir>\" (error \"no_publish_folder\" until then - the tool never chooses a folder). Pages with Mermaid diagrams or maps need the browser from \"diagrams setup\" (error \"chromium_unavailable\"). The report is this result, trimmed to counts and first entries - nothing of it is written into the folder, so the site never names the documents it leaves out. Publishing writes files - it does not deploy them.",
|
|
18761
|
-
inputSchema: {
|
|
18822
|
+
description: "Render a publication to its publish folder on this machine and return the report: what was included and why documents were left out, missing links, assets, warnings. The folder is the one the user set with \"etherpk-mcp publish --publication <id> --out <dir>\" (error \"no_publish_folder\" until then - the tool never chooses a folder), and a folder that is the graph's own folder, inside it or holding it is refused (error \"publish_folder_overlaps_graph\"). With dryRun: true it renders and reports but writes nothing and needs no folder: use it to check a publication after editing its pages, and fix what the report names. Pages with Mermaid diagrams or maps need the browser from \"diagrams setup\" (error \"chromium_unavailable\"), for a dry run too. The report is this result, trimmed to counts and first entries - nothing of it is written into the folder, so the site never names the documents it leaves out. Publishing writes files - it does not deploy them.",
|
|
18823
|
+
inputSchema: {
|
|
18824
|
+
id: z.string().min(1),
|
|
18825
|
+
dryRun: z.boolean().optional().describe("Render and report without writing anything. No publish folder needed.")
|
|
18826
|
+
}
|
|
18762
18827
|
}, async (args) => run((graph) => publish(graph, args, host)));
|
|
18763
18828
|
server.registerTool("list_themes", {
|
|
18764
18829
|
title: "List themes",
|
|
@@ -19194,6 +19259,7 @@ async function runGraphHost(deps) {
|
|
|
19194
19259
|
if (stopping) return answer({ etherpk: "stopping" });
|
|
19195
19260
|
return startSession(socket, request, reply);
|
|
19196
19261
|
case "publish":
|
|
19262
|
+
case "publish-check":
|
|
19197
19263
|
if (stopping) return answer({ etherpk: "stopping" });
|
|
19198
19264
|
return runPublish(request, answer);
|
|
19199
19265
|
default: return answer({
|
|
@@ -25208,7 +25274,7 @@ function bindServeLifetime(deps) {
|
|
|
25208
25274
|
* etherpk-mcp stop [--graph <id or name> [--sync-server <url>] | --folder <path> | --all]
|
|
25209
25275
|
* etherpk-mcp logout [--sync-server <url> | --all]
|
|
25210
25276
|
* etherpk-mcp semantic setup | status | remove
|
|
25211
|
-
* etherpk-mcp publish (--graph <id or name> | --folder <path>) --publication <id> [--out <dir>]
|
|
25277
|
+
* etherpk-mcp publish (--graph <id or name> | --folder <path>) --publication <id> [--out <dir> | --dry-run]
|
|
25212
25278
|
* etherpk-mcp diagrams setup | status
|
|
25213
25279
|
*
|
|
25214
25280
|
* One config file holds a login per Sync Server (ADR 0075). `--sync-server` names the one a
|
|
@@ -25259,8 +25325,9 @@ var USAGE = `etherpk-mcp - EtherPK Headless Client (an MCP server over one synce
|
|
|
25259
25325
|
Serve a local graph folder the same way: no sign-in, no server. The agent gets search,
|
|
25260
25326
|
backlinks, tasks and format-safe edits over the folder's markdown, alongside the files
|
|
25261
25327
|
themselves. Edits made in an editor or by the agent directly are picked up as they land.
|
|
25262
|
-
The folder must already be a graph
|
|
25263
|
-
the cache directory, never
|
|
25328
|
+
The folder must already be a graph, with pages/ and journals/ folders in it (EtherPK
|
|
25329
|
+
makes them when it opens a folder) - its index is kept under the cache directory, never
|
|
25330
|
+
in the folder.
|
|
25264
25331
|
${CMD} running
|
|
25265
25332
|
List the graphs served on this computer: each one's background process, its agents and
|
|
25266
25333
|
its log. A background process stops on its own once no agent has used it for five
|
|
@@ -25279,14 +25346,18 @@ var USAGE = `etherpk-mcp - EtherPK Headless Client (an MCP server over one synce
|
|
|
25279
25346
|
to a service to be embedded.
|
|
25280
25347
|
${CMD} semantic status | remove
|
|
25281
25348
|
Whether it is set up here, or delete it (logout --all deletes it too).
|
|
25282
|
-
${CMD} publish (--graph <id or name> | --folder <path>) --publication <id> [--out <dir>]
|
|
25349
|
+
${CMD} publish (--graph <id or name> | --folder <path>) --publication <id> [--out <dir> | --dry-run]
|
|
25283
25350
|
Publish one publication of a graph to a folder on this machine and print the report.
|
|
25284
25351
|
--out sets the publish folder for that graph and publication and is remembered: from
|
|
25285
25352
|
then on this command without --out, and the agent's publish tool, write to the same
|
|
25286
|
-
folder (the agent cannot choose one).
|
|
25287
|
-
|
|
25288
|
-
removed - everything else in it is left
|
|
25289
|
-
(a git push, say) is yours. Run it from
|
|
25353
|
+
folder (the agent cannot choose one). It must be a folder of its own, outside the
|
|
25354
|
+
graph's folder: the folder's owned files (.html at the top, assets/, theme/, the search
|
|
25355
|
+
and feed files) are rewritten and their strays removed - everything else in it is left
|
|
25356
|
+
alone. Publishing writes files - deploying them (a git push, say) is yours. Run it from
|
|
25357
|
+
cron for a site that republishes itself.
|
|
25358
|
+
--dry-run renders and checks the publication and prints the whole report, with the
|
|
25359
|
+
names of the pages it mentions, but writes nothing and needs no folder. Keep its output
|
|
25360
|
+
out of public logs.
|
|
25290
25361
|
${CMD} diagrams setup
|
|
25291
25362
|
Let a publish draw Mermaid diagrams and maps. Downloads a Chromium (about 170 MB) into the
|
|
25292
25363
|
cache directory with Playwright's installer, once per computer - a publish whose pages hold
|
|
@@ -25694,14 +25765,22 @@ async function publishCommand(args) {
|
|
|
25694
25765
|
const request = graphRequest("publish", args);
|
|
25695
25766
|
const publication = args.publication?.trim();
|
|
25696
25767
|
if (!publication) fail("publish needs --publication <id>. The agent's list_publications tool, or Settings → Publish in EtherPK, shows the ids.");
|
|
25697
|
-
const
|
|
25768
|
+
const dryRun = args["dry-run"] === true;
|
|
25698
25769
|
const out = args.out?.trim();
|
|
25699
|
-
|
|
25770
|
+
if (dryRun && out) fail("publish --dry-run writes nothing, so it takes no --out. Leave --out off, or drop --dry-run to publish.");
|
|
25771
|
+
const found = await findGraph(request).catch((error) => fail(`etherpk-mcp: ${describeError(error)}`));
|
|
25772
|
+
const env = publishSettings(process.env);
|
|
25773
|
+
const { reply, socket } = await requestHost(launcherFor(found), dryRun ? {
|
|
25774
|
+
etherpk: "publish-check",
|
|
25775
|
+
protocol: 1,
|
|
25776
|
+
publication,
|
|
25777
|
+
env
|
|
25778
|
+
} : {
|
|
25700
25779
|
etherpk: "publish",
|
|
25701
25780
|
protocol: 1,
|
|
25702
25781
|
publication,
|
|
25703
25782
|
...out ? { out: resolve(out) } : {},
|
|
25704
|
-
env
|
|
25783
|
+
env
|
|
25705
25784
|
}).catch((error) => fail(`etherpk-mcp: ${describeError(error)}`));
|
|
25706
25785
|
socket.destroy();
|
|
25707
25786
|
switch (reply.etherpk) {
|
|
@@ -25711,15 +25790,18 @@ async function publishCommand(args) {
|
|
|
25711
25790
|
process.exitCode = reply.exitCode;
|
|
25712
25791
|
return;
|
|
25713
25792
|
case "refused": return fail(reply.message);
|
|
25714
|
-
case "failed":
|
|
25793
|
+
case "failed":
|
|
25794
|
+
if (dryRun && reply.message === "The request was not understood.") return fail(`etherpk-mcp: this graph is served on this computer by an older etherpk-mcp, which cannot run a dry run. Run: ${CMD} stop, then try again.`);
|
|
25795
|
+
return fail(`etherpk-mcp: ${reply.message}`);
|
|
25715
25796
|
case "unsupported": return fail(`etherpk-mcp: this graph is served on this computer by etherpk-mcp ${reply.version}, which cannot run a publish for this version (${VERSION}). Run: ${CMD} stop, then publish again.`);
|
|
25716
25797
|
default: return fail(`etherpk-mcp: the graph's background process gave an answer this version does not know ("${reply.etherpk}").`);
|
|
25717
25798
|
}
|
|
25718
25799
|
}
|
|
25719
25800
|
/**
|
|
25720
25801
|
* A command-line publish, as the graph's host runs it: the publication first, so a mistyped id is
|
|
25721
|
-
* never remembered as a publish folder, then `--out`
|
|
25722
|
-
* tool runs. The answer is what the command prints, in
|
|
25802
|
+
* never remembered as a publish folder, then `--out` checked against the graph's folder and
|
|
25803
|
+
* remembered, then the publish the agent's tool runs. The answer is what the command prints, in
|
|
25804
|
+
* its own words.
|
|
25723
25805
|
*/
|
|
25724
25806
|
async function publishInHost(graph, request) {
|
|
25725
25807
|
const env = withPublishSettings(process.env, request.env ?? {});
|
|
@@ -25730,10 +25812,23 @@ async function publishInHost(graph, request) {
|
|
|
25730
25812
|
};
|
|
25731
25813
|
const notes = [];
|
|
25732
25814
|
try {
|
|
25815
|
+
if (request.etherpk === "publish-check") {
|
|
25816
|
+
const result = await publish(graph, {
|
|
25817
|
+
id: request.publication,
|
|
25818
|
+
dryRun: true
|
|
25819
|
+
}, host);
|
|
25820
|
+
return {
|
|
25821
|
+
etherpk: "published",
|
|
25822
|
+
output: result,
|
|
25823
|
+
notes: [],
|
|
25824
|
+
exitCode: result.ok ? 0 : 1
|
|
25825
|
+
};
|
|
25826
|
+
}
|
|
25733
25827
|
await findPublication(graph, request.publication, host);
|
|
25734
25828
|
const configPath = defaultPublishFoldersPath(env);
|
|
25735
25829
|
const key = publishGraphKey(graph.backend, graph.graphId);
|
|
25736
25830
|
if (request.out) {
|
|
25831
|
+
await checkPublishFolder(graph, request.publication, request.out, host);
|
|
25737
25832
|
await writePublishFolders(configPath, withPublishFolder(await readPublishFolders(configPath), key, request.publication, request.out));
|
|
25738
25833
|
notes.push(`etherpk-mcp: publish folder for "${request.publication}" of "${graph.name}" set to ${request.out} (remembered in ${configPath}).`);
|
|
25739
25834
|
} else if (!publishFolderOf(await readPublishFolders(configPath), key, request.publication)) return {
|
|
@@ -26256,6 +26351,7 @@ async function main() {
|
|
|
26256
26351
|
"no-semantic": { type: "boolean" },
|
|
26257
26352
|
publication: { type: "string" },
|
|
26258
26353
|
out: { type: "string" },
|
|
26354
|
+
"dry-run": { type: "boolean" },
|
|
26259
26355
|
all: { type: "boolean" },
|
|
26260
26356
|
name: { type: "string" },
|
|
26261
26357
|
help: {
|