diffwiki 0.3.0 → 0.4.0-rc.202607220518.0ade9d3
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/dist/bin.js +207 -5
- package/package.json +2 -2
package/dist/bin.js
CHANGED
|
@@ -9,9 +9,15 @@ import {
|
|
|
9
9
|
doctor,
|
|
10
10
|
getConfigValue,
|
|
11
11
|
initRepoWiki,
|
|
12
|
+
installPlugin,
|
|
12
13
|
listCollections,
|
|
14
|
+
listPlugins,
|
|
15
|
+
listSearchModes,
|
|
16
|
+
loadSearchProvider,
|
|
13
17
|
query,
|
|
18
|
+
registerPlugin,
|
|
14
19
|
removeArticle,
|
|
20
|
+
removePlugin,
|
|
15
21
|
removeTags,
|
|
16
22
|
setConfigValue,
|
|
17
23
|
setTags,
|
|
@@ -45,6 +51,104 @@ function withErrors(fn) {
|
|
|
45
51
|
var collect = (value, previous) => previous.concat([value]);
|
|
46
52
|
var splitTags = (raw) => raw.flatMap((t) => t.split(",")).map((t) => t.trim()).filter((t) => t.length > 0);
|
|
47
53
|
|
|
54
|
+
// src/ui-server.ts
|
|
55
|
+
import { spawn } from "child_process";
|
|
56
|
+
import { platform } from "os";
|
|
57
|
+
import fs from "fs/promises";
|
|
58
|
+
import path from "path";
|
|
59
|
+
import http from "http";
|
|
60
|
+
import { resolveHome } from "diffwiki-core";
|
|
61
|
+
var DEFAULT_PORT = 4321;
|
|
62
|
+
function statePath() {
|
|
63
|
+
return path.join(resolveHome(), "ui.json");
|
|
64
|
+
}
|
|
65
|
+
async function readState() {
|
|
66
|
+
try {
|
|
67
|
+
return JSON.parse(await fs.readFile(statePath(), "utf8"));
|
|
68
|
+
} catch {
|
|
69
|
+
return void 0;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
async function writeState(state) {
|
|
73
|
+
await fs.mkdir(resolveHome(), { recursive: true });
|
|
74
|
+
await fs.writeFile(statePath(), `${JSON.stringify(state, null, 2)}
|
|
75
|
+
`, "utf8");
|
|
76
|
+
}
|
|
77
|
+
async function clearState() {
|
|
78
|
+
await fs.rm(statePath(), { force: true });
|
|
79
|
+
}
|
|
80
|
+
function isAlive(pid) {
|
|
81
|
+
try {
|
|
82
|
+
process.kill(pid, 0);
|
|
83
|
+
return true;
|
|
84
|
+
} catch {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function openCommand() {
|
|
89
|
+
switch (platform()) {
|
|
90
|
+
case "darwin":
|
|
91
|
+
return { cmd: "open", args: (u) => [u] };
|
|
92
|
+
case "win32":
|
|
93
|
+
return { cmd: "cmd", args: (u) => ["/c", "start", "", u] };
|
|
94
|
+
default:
|
|
95
|
+
return { cmd: "xdg-open", args: (u) => [u] };
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function openBrowser(url) {
|
|
99
|
+
const { cmd, args } = openCommand();
|
|
100
|
+
spawn(cmd, args(url), { stdio: "ignore", detached: true }).unref();
|
|
101
|
+
}
|
|
102
|
+
function waitForServer(url, timeoutMs = 3e4) {
|
|
103
|
+
const deadline = Date.now() + timeoutMs;
|
|
104
|
+
return new Promise((resolve) => {
|
|
105
|
+
const tick = () => {
|
|
106
|
+
const req = http.get(url, (res) => {
|
|
107
|
+
res.resume();
|
|
108
|
+
resolve(true);
|
|
109
|
+
});
|
|
110
|
+
req.on("error", () => {
|
|
111
|
+
if (Date.now() > deadline) resolve(false);
|
|
112
|
+
else setTimeout(tick, 500);
|
|
113
|
+
});
|
|
114
|
+
};
|
|
115
|
+
tick();
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
async function startUi(port = DEFAULT_PORT) {
|
|
119
|
+
const child = spawn("npx", ["-y", "diffwiki-app@latest", "--port", String(port)], {
|
|
120
|
+
env: { ...process.env, PORT: String(port), HOST: "127.0.0.1" },
|
|
121
|
+
detached: true,
|
|
122
|
+
stdio: "ignore"
|
|
123
|
+
});
|
|
124
|
+
if (child.pid === void 0) {
|
|
125
|
+
throw new Error("failed to spawn diffwiki-app");
|
|
126
|
+
}
|
|
127
|
+
child.on("close", () => {
|
|
128
|
+
void clearState();
|
|
129
|
+
});
|
|
130
|
+
child.unref();
|
|
131
|
+
const state = {
|
|
132
|
+
pid: child.pid,
|
|
133
|
+
port,
|
|
134
|
+
url: `http://127.0.0.1:${port}`,
|
|
135
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
136
|
+
};
|
|
137
|
+
await writeState(state);
|
|
138
|
+
return state;
|
|
139
|
+
}
|
|
140
|
+
async function stopUi() {
|
|
141
|
+
const state = await readState();
|
|
142
|
+
if (!state) return void 0;
|
|
143
|
+
try {
|
|
144
|
+
if (platform() === "win32") process.kill(state.pid);
|
|
145
|
+
else process.kill(-state.pid);
|
|
146
|
+
} catch {
|
|
147
|
+
}
|
|
148
|
+
await clearState();
|
|
149
|
+
return state;
|
|
150
|
+
}
|
|
151
|
+
|
|
48
152
|
// src/cli.ts
|
|
49
153
|
var icon = (level) => level === "ok" ? "\u2713" : level === "warn" ? "!" : "\u2716";
|
|
50
154
|
function createProgram() {
|
|
@@ -104,15 +208,72 @@ function createProgram() {
|
|
|
104
208
|
registerTagCommand("add-tags", addTags);
|
|
105
209
|
registerTagCommand("remove-tags", removeTags);
|
|
106
210
|
registerTagCommand("update-tags", setTags);
|
|
107
|
-
program.command("
|
|
211
|
+
program.command("index").description("Rebuild the search index via the active plugin").option("--embed", "also build vector embeddings (may download models on first run)").action(
|
|
212
|
+
withErrors(async (opts) => {
|
|
213
|
+
const provider = await loadSearchProvider();
|
|
214
|
+
if (!provider) {
|
|
215
|
+
const installable = (await listSearchModes()).filter((m) => m.plugin !== "native");
|
|
216
|
+
console.log(
|
|
217
|
+
installable.length > 0 ? `No index-backed plugin active. Available modes: ${installable.map((m) => `${m.plugin}\xB7${m.type}`).join(", ")}` : "No search plugin installed \u2014 using native search (nothing to index). Install one with: diffwiki plugin install <spec>"
|
|
218
|
+
);
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
try {
|
|
222
|
+
const indexed = await provider.reindex(await listCollections(), { embed: !!opts.embed });
|
|
223
|
+
console.log(`Indexed ${indexed} document(s).`);
|
|
224
|
+
} finally {
|
|
225
|
+
await provider.close();
|
|
226
|
+
}
|
|
227
|
+
})
|
|
228
|
+
);
|
|
229
|
+
program.command("query").description("Search articles via the resolved search engine (native fallback if none)").option("--engine <name>", "search engine/plugin (default: config defaultSearch, else native)").option("-c, --collection <name>", "limit to a collection").option("-t, --type <type>", "search type (engine-defined, e.g. keyword|semantic|hybrid)").argument("[term]", "search term", "").action(
|
|
108
230
|
withErrors(async (term, opts) => {
|
|
109
|
-
const
|
|
110
|
-
|
|
231
|
+
const queryOpts = { engine: opts.engine, collection: opts.collection, type: opts.type };
|
|
232
|
+
const hits = await query(term, queryOpts);
|
|
233
|
+
console.log(`${hits.length} result(s)`);
|
|
111
234
|
for (const hit of hits) {
|
|
112
|
-
|
|
235
|
+
const line = `${hit.collection} ${hit.title} ${hit.path}`;
|
|
236
|
+
console.log(hit.snippet ? `${line} ${hit.snippet}` : line);
|
|
113
237
|
}
|
|
114
238
|
})
|
|
115
239
|
);
|
|
240
|
+
const plugin = program.command("plugin").description("Manage search plugins");
|
|
241
|
+
plugin.command("install").description("Install a search plugin from an npm spec and provision it (setup)").argument("<spec>", "npm package spec (name, name@version, or path)").option("--no-setup", "skip the end-to-end setup after install").option("--prefetch-models", "pre-download models during setup (heavy)").action(
|
|
242
|
+
withErrors(async (spec, opts) => {
|
|
243
|
+
const record = await installPlugin(spec, {
|
|
244
|
+
setup: opts.setup,
|
|
245
|
+
prefetchModels: opts.prefetchModels
|
|
246
|
+
});
|
|
247
|
+
console.log(`Installed "${record.name}"${record.version ? ` v${record.version}` : ""}`);
|
|
248
|
+
if (!opts.setup) console.log("Skipped setup (--no-setup) \u2014 run: diffwiki index");
|
|
249
|
+
})
|
|
250
|
+
);
|
|
251
|
+
plugin.command("register").description("Register an existing executable as a search plugin").argument("<name>", "plugin name").requiredOption("--command <cmd...>", "command argv to spawn the plugin (e.g. node /abs/adapter.js)").action(
|
|
252
|
+
withErrors(async (name, opts) => {
|
|
253
|
+
const record = await registerPlugin(name, opts.command);
|
|
254
|
+
console.log(`Registered "${record.name}" \u2192 ${record.command.join(" ")}`);
|
|
255
|
+
})
|
|
256
|
+
);
|
|
257
|
+
plugin.command("list").description("List registered search plugins").action(
|
|
258
|
+
withErrors(async () => {
|
|
259
|
+
const plugins = await listPlugins();
|
|
260
|
+
if (plugins.length === 0) {
|
|
261
|
+
console.log("No plugins registered. Install one with: diffwiki plugin install <spec>");
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
for (const p of plugins) {
|
|
265
|
+
console.log(
|
|
266
|
+
`${p.name} ${p.kind} ${p.version ?? "-"} enabled=${p.enabled} managed=${!!p.managed} spawnable=${p.spawnable}`
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
})
|
|
270
|
+
);
|
|
271
|
+
plugin.command("remove").description("Remove a search plugin").argument("<name>", "plugin name").action(
|
|
272
|
+
withErrors(async (name) => {
|
|
273
|
+
await removePlugin(name);
|
|
274
|
+
console.log(`Removed "${name}"`);
|
|
275
|
+
})
|
|
276
|
+
);
|
|
116
277
|
program.command("init").description("Initialize an in-repo wiki and register it globally").option("-c, --collection <name>", "collection name (default: repo name)").option("-p, --path <path>", "wiki path within the repo (default: wiki)").action(
|
|
117
278
|
withErrors(async (opts) => {
|
|
118
279
|
const entry = await initRepoWiki({
|
|
@@ -124,7 +285,7 @@ function createProgram() {
|
|
|
124
285
|
console.log("Registered globally \u2014 discoverable via: diffwiki list");
|
|
125
286
|
})
|
|
126
287
|
);
|
|
127
|
-
program.command("config-set").description("Set a config value (e.g. defaultCollection)").argument("<key>", "config key").argument("<value>", "config value").action(
|
|
288
|
+
program.command("config-set").description("Set a config value (e.g. defaultCollection, or defaultSearch <engine[:type]>)").argument("<key>", "config key").argument("<value>", "config value").action(
|
|
128
289
|
withErrors(async (key, value) => {
|
|
129
290
|
await setConfigValue(key, value);
|
|
130
291
|
console.log(`Set ${key} = ${value}`);
|
|
@@ -147,6 +308,47 @@ function createProgram() {
|
|
|
147
308
|
}
|
|
148
309
|
})
|
|
149
310
|
);
|
|
311
|
+
program.command("up").description("Start the diffwiki UI (latest published diffwiki-app)").option("-p, --port <port>", "port to serve on", "4321").action(
|
|
312
|
+
withErrors(async (opts) => {
|
|
313
|
+
const existing = await readState();
|
|
314
|
+
if (existing && isAlive(existing.pid)) {
|
|
315
|
+
console.log(`UI already running at ${existing.url} (pid ${existing.pid})`);
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
const state = await startUi(Number(opts.port));
|
|
319
|
+
const ok = await waitForServer(state.url);
|
|
320
|
+
console.log(
|
|
321
|
+
ok ? `UI started at ${state.url} (pid ${state.pid})` : `Launched (pid ${state.pid}) but ${state.url} did not respond yet \u2014 check \`diffwiki status\``
|
|
322
|
+
);
|
|
323
|
+
})
|
|
324
|
+
);
|
|
325
|
+
program.command("down").description("Stop the running diffwiki UI").action(
|
|
326
|
+
withErrors(async () => {
|
|
327
|
+
const stopped = await stopUi();
|
|
328
|
+
console.log(stopped ? `Stopped UI (pid ${stopped.pid})` : "UI is not running.");
|
|
329
|
+
})
|
|
330
|
+
);
|
|
331
|
+
program.command("status").description("Show whether the diffwiki UI is running").action(
|
|
332
|
+
withErrors(async () => {
|
|
333
|
+
const state = await readState();
|
|
334
|
+
if (state && isAlive(state.pid)) {
|
|
335
|
+
console.log(`running \xB7 ${state.url} \xB7 pid ${state.pid} \xB7 since ${state.startedAt}`);
|
|
336
|
+
} else {
|
|
337
|
+
console.log("stopped");
|
|
338
|
+
}
|
|
339
|
+
})
|
|
340
|
+
);
|
|
341
|
+
program.command("open").description("Open the diffwiki UI in the browser (starting it if needed)").option("-p, --port <port>", "port to serve on", "4321").action(
|
|
342
|
+
withErrors(async (opts) => {
|
|
343
|
+
let state = await readState();
|
|
344
|
+
if (!state || !isAlive(state.pid)) {
|
|
345
|
+
state = await startUi(Number(opts.port));
|
|
346
|
+
await waitForServer(state.url);
|
|
347
|
+
}
|
|
348
|
+
openBrowser(state.url);
|
|
349
|
+
console.log(`Opening ${state.url}`);
|
|
350
|
+
})
|
|
351
|
+
);
|
|
150
352
|
return program;
|
|
151
353
|
}
|
|
152
354
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "diffwiki",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0-rc.202607220518.0ade9d3",
|
|
4
4
|
"description": "diffwiki command-line interface",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"commander": "^13.0.0",
|
|
18
|
-
"diffwiki-core": "0.
|
|
18
|
+
"diffwiki-core": "0.4.0-rc.202607220518.0ade9d3"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@types/node": "^22.0.0"
|