wikimemory 0.2.5 → 0.2.7
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/npm-cli/scripts/cli.js +14 -1
- package/dist/npm-cli/scripts/deployment-record.js +38 -1
- package/dist/npm-cli/scripts/deployment-wait.js +4 -0
- package/dist/npm-cli/scripts/setup.js +3 -2
- package/dist/npm-cli/scripts/status.js +2 -1
- package/dist/npm-cli/scripts/upgrade.js +5 -3
- package/dist/npm-cli/scripts/web-shell.js +5 -0
- package/dist/npm-cli/src/version.js +1 -1
- package/dist/web/assets/index-9nkjx7L3.css +1 -0
- package/dist/web/assets/index-Cebskfjw.js +72 -0
- package/dist/web/index.html +11 -3
- package/package.json +1 -1
- package/release-manifest.json +1 -1
- package/src/version.ts +1 -1
- package/dist/web/assets/index-CGTHUs4b.js +0 -72
- package/dist/web/assets/index-CjnFBnXp.css +0 -1
|
@@ -3,7 +3,7 @@ import { mkdir } from "node:fs/promises";
|
|
|
3
3
|
import process from "node:process";
|
|
4
4
|
import { WIKIMEMORY_VERSION } from "../src/version.js";
|
|
5
5
|
import { deploymentArguments, installArguments } from "./cli-options.js";
|
|
6
|
-
import { deploymentPaths } from "./deployment-record.js";
|
|
6
|
+
import { deploymentPaths, requireInstalledDeployment } from "./deployment-record.js";
|
|
7
7
|
import { packageRoot } from "./package-root.js";
|
|
8
8
|
import { conciseError } from "./subprocess.js";
|
|
9
9
|
const [command, ...args] = process.argv.slice(2);
|
|
@@ -34,6 +34,19 @@ async function main() {
|
|
|
34
34
|
}
|
|
35
35
|
const parsed = deploymentArguments(args);
|
|
36
36
|
const paths = deploymentPaths(parsed.deployment);
|
|
37
|
+
const requiresInstalledDeployment = !parsed.remaining.includes("--help") &&
|
|
38
|
+
(command === "recover" ||
|
|
39
|
+
command === "status" ||
|
|
40
|
+
command === "passkeys" ||
|
|
41
|
+
command === "connect" ||
|
|
42
|
+
command === "uninstall" ||
|
|
43
|
+
(command === "upgrade" && !parsed.remaining.includes("--record")));
|
|
44
|
+
if (requiresInstalledDeployment) {
|
|
45
|
+
const requirement = command === "recover" || command === "passkeys" || command === "uninstall"
|
|
46
|
+
? "config"
|
|
47
|
+
: "record";
|
|
48
|
+
await requireInstalledDeployment(parsed.deployment, requirement);
|
|
49
|
+
}
|
|
37
50
|
if (command === "install" || command === "recover") {
|
|
38
51
|
await mkdir(paths.directory, { recursive: true, mode: 0o700 });
|
|
39
52
|
process.env["WIKIMEMORY_PACKAGE_ROOT"] = root;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
1
|
+
import { access, mkdir, readdir, readFile, writeFile } from "node:fs/promises";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
4
|
import { z } from "zod";
|
|
@@ -44,6 +44,43 @@ export function deploymentPaths(deployment = "wikimemory") {
|
|
|
44
44
|
passkeyClient: join(directory, "passkey-client.json")
|
|
45
45
|
};
|
|
46
46
|
}
|
|
47
|
+
export async function requireInstalledDeployment(deployment, requirement = "record") {
|
|
48
|
+
const requestedPaths = deploymentPaths(deployment);
|
|
49
|
+
const requested = requirement === "record" ? requestedPaths.record : requestedPaths.config;
|
|
50
|
+
try {
|
|
51
|
+
await access(requested);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
if (!(error instanceof Error && "code" in error && error.code === "ENOENT"))
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
const configRoot = process.env["XDG_CONFIG_HOME"] ?? join(homedir(), ".config");
|
|
59
|
+
const deploymentsRoot = join(configRoot, "wikimemory", "deployments");
|
|
60
|
+
let installed = [];
|
|
61
|
+
try {
|
|
62
|
+
const entries = (await readdir(deploymentsRoot, { withFileTypes: true })).filter((entry) => entry.isDirectory() && DEPLOYMENT_NAME.test(entry.name));
|
|
63
|
+
const candidates = await Promise.all(entries.map(async (entry) => {
|
|
64
|
+
try {
|
|
65
|
+
await access(join(deploymentsRoot, entry.name, "deployment.json"));
|
|
66
|
+
return entry.name;
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT")
|
|
70
|
+
return null;
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
}));
|
|
74
|
+
installed = candidates.filter((name) => name !== null).sort();
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
if (!(error instanceof Error && "code" in error && error.code === "ENOENT"))
|
|
78
|
+
throw error;
|
|
79
|
+
}
|
|
80
|
+
if (installed.length === 0)
|
|
81
|
+
throw new Error(`No deployment named “${deployment}”. Run wikimemory install first.`);
|
|
82
|
+
throw new Error(`No deployment named “${deployment}”. Installed: ${installed.join(", ")}. Use --deployment NAME.`);
|
|
83
|
+
}
|
|
47
84
|
export function deploymentRecordFromConfig(configText, installedVersion, kvName) {
|
|
48
85
|
const config = PRODUCTION_CONFIG_SCHEMA.parse(JSON.parse(configText));
|
|
49
86
|
const database = config.d1_databases.at(0);
|
|
@@ -9,6 +9,7 @@ import { pathToFileURL } from "node:url";
|
|
|
9
9
|
import { z } from "zod";
|
|
10
10
|
import { WIKIMEMORY_VERSION } from "../src/version.js";
|
|
11
11
|
import { deploymentRecordFromConfig, writeDeploymentRecord } from "./deployment-record.js";
|
|
12
|
+
import { DEPLOYMENT_VERIFY_ATTEMPTS, deploymentVerifyDelay } from "./deployment-wait.js";
|
|
12
13
|
import { installedRecordPath, packageRoot, setupRuntime } from "./lifecycle-runtime.js";
|
|
13
14
|
import { commandFailureMessage, conciseError, runCommand } from "./subprocess.js";
|
|
14
15
|
export { commandFailureMessage } from "./subprocess.js";
|
|
@@ -319,7 +320,7 @@ function bootstrapSecret() {
|
|
|
319
320
|
const hash = createHash("sha256").update(raw, "utf8").digest("hex");
|
|
320
321
|
return { raw, hash };
|
|
321
322
|
}
|
|
322
|
-
export async function verifyEndpoint(origin, path, expectedStatus, fetcher = fetch, sleeper = sleep, attempts =
|
|
323
|
+
export async function verifyEndpoint(origin, path, expectedStatus, fetcher = fetch, sleeper = sleep, attempts = DEPLOYMENT_VERIFY_ATTEMPTS) {
|
|
323
324
|
let finalDetail = "no response";
|
|
324
325
|
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
325
326
|
try {
|
|
@@ -344,7 +345,7 @@ export async function verifyEndpoint(origin, path, expectedStatus, fetcher = fet
|
|
|
344
345
|
finalDetail = error instanceof Error ? error.message : "Unknown request failure";
|
|
345
346
|
}
|
|
346
347
|
if (attempt + 1 < attempts)
|
|
347
|
-
await sleeper(
|
|
348
|
+
await sleeper(deploymentVerifyDelay(attempt));
|
|
348
349
|
}
|
|
349
350
|
throw new Error(`Deployment ${path} check failed after ${attempts} attempts: ${finalDetail}`);
|
|
350
351
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { deploymentRecordPath, readDeploymentRecord } from "./deployment-record.js";
|
|
3
|
+
import { isReactApplicationShell } from "./web-shell.js";
|
|
3
4
|
const HEALTH_SCHEMA = z.object({
|
|
4
5
|
status: z.literal("ok"),
|
|
5
6
|
service: z.literal("wikimemory"),
|
|
@@ -29,7 +30,7 @@ export async function deploymentStatus(deployment) {
|
|
|
29
30
|
throw new Error("Health and schema versions disagree");
|
|
30
31
|
if (discovery.resource !== `${record.origin}/mcp`)
|
|
31
32
|
throw new Error("OAuth discovery reports an unexpected MCP resource");
|
|
32
|
-
if (!(await appResponse.text())
|
|
33
|
+
if (!isReactApplicationShell(await appResponse.text()))
|
|
33
34
|
throw new Error("React application shell verification failed");
|
|
34
35
|
return {
|
|
35
36
|
deployment,
|
|
@@ -6,8 +6,10 @@ import process from "node:process";
|
|
|
6
6
|
import { createInterface } from "node:readline/promises";
|
|
7
7
|
import { z } from "zod";
|
|
8
8
|
import { deploymentRecordPath, readDeploymentRecord, writeDeploymentRecord } from "./deployment-record.js";
|
|
9
|
+
import { DEPLOYMENT_VERIFY_ATTEMPTS, deploymentVerifyDelay } from "./deployment-wait.js";
|
|
9
10
|
import { packageRoot } from "./package-root.js";
|
|
10
11
|
import { commandFailureMessage, runCommand } from "./subprocess.js";
|
|
12
|
+
import { isReactApplicationShell } from "./web-shell.js";
|
|
11
13
|
const PACKAGE_ROOT = packageRoot();
|
|
12
14
|
const DEPLOYMENT_NAME = /^[a-z0-9][a-z0-9-]{0,62}$/u;
|
|
13
15
|
const MIGRATION_NAME = /^\d{4}_[a-z0-9_]+\.sql$/u;
|
|
@@ -173,7 +175,7 @@ async function responseJson(response, label) {
|
|
|
173
175
|
throw new Error(`${label} returned non-JSON content`);
|
|
174
176
|
}
|
|
175
177
|
}
|
|
176
|
-
export async function verifyRelease(origin, manifest, fetcher = fetch, sleeper = sleep, attempts =
|
|
178
|
+
export async function verifyRelease(origin, manifest, fetcher = fetch, sleeper = sleep, attempts = DEPLOYMENT_VERIFY_ATTEMPTS) {
|
|
177
179
|
let finalError;
|
|
178
180
|
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
179
181
|
try {
|
|
@@ -203,14 +205,14 @@ export async function verifyRelease(origin, manifest, fetcher = fetch, sleeper =
|
|
|
203
205
|
if (!discovery.ok)
|
|
204
206
|
throw new Error(`OAuth protected-resource discovery returned HTTP ${discovery.status}`);
|
|
205
207
|
const app = await fetcher(`${origin}/app`, { redirect: "error" });
|
|
206
|
-
if (!app.ok || !(await app.text())
|
|
208
|
+
if (!app.ok || !isReactApplicationShell(await app.text()))
|
|
207
209
|
throw new Error("React application verification failed");
|
|
208
210
|
return;
|
|
209
211
|
}
|
|
210
212
|
catch (error) {
|
|
211
213
|
finalError = error;
|
|
212
214
|
if (attempt + 1 < attempts)
|
|
213
|
-
await sleeper(
|
|
215
|
+
await sleeper(deploymentVerifyDelay(attempt));
|
|
214
216
|
}
|
|
215
217
|
}
|
|
216
218
|
const detail = finalError instanceof Error ? finalError.message : "Unknown verification failure";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const WIKIMEMORY_VERSION = "0.2.
|
|
1
|
+
export const WIKIMEMORY_VERSION = "0.2.7";
|
|
2
2
|
export const LATEST_SCHEMA_VERSION = "0004_credential_bound_registration_tokens.sql";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
:root{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light dark;--bg:#f5f6f8;--panel:#fff;--ink:#19211e;--muted:#66716c;--line:#dfe4e1;--accent:#176b52;--danger:#a83a3a}@media (prefers-color-scheme:dark){:root{--lightningcss-light: ;--lightningcss-dark:initial}}html{text-size-adjust:100%}*{box-sizing:border-box}body{background:var(--bg);color:var(--ink);margin:0;font:400 1rem/1.6 ui-sans-serif,system-ui,sans-serif}a{color:var(--accent)}header{border-bottom:1px solid var(--line);background:var(--panel);flex-wrap:wrap;align-items:center;gap:.5rem;padding:.75rem max(1rem,50% - 36rem);display:flex}header a{padding:.4rem;text-decoration:none}.brand{align-items:center;gap:.5rem;margin-right:auto;font-weight:800;display:flex}.brand span{background:var(--accent);color:#fff;border-radius:.55rem;place-items:center;width:2rem;height:2rem;display:grid}main{width:min(72rem,100% - 2rem);margin:auto;padding:3rem 0 6rem}.center{place-items:center;min-height:100vh;padding:1rem;display:grid}.center .panel{width:min(32rem,100%)}h1,h2{line-height:1.2}.lede,.muted,small{color:var(--muted)}.panel,.card{border:1px solid var(--line);background:var(--panel);border-radius:.85rem;padding:1.25rem}.panel{margin-bottom:1rem}.grid{grid-template-columns:repeat(auto-fill,minmax(min(100%,19rem),1fr));gap:1rem;display:grid}.meta{flex-wrap:wrap;gap:.4rem;display:flex}.meta span{background:#e7f4ef;border-radius:999px;padding:.1rem .45rem;font-size:.8rem}.search{gap:.5rem;margin:1rem 0 2rem;display:flex}input{background:var(--panel);width:100%;color:inherit;font:inherit;border:1px solid #b8c2bd;border-radius:.55rem;padding:.7rem}label{margin:1rem 0;font-weight:650;display:block}button,.button{border:1px solid var(--accent);background:var(--accent);color:#fff;cursor:pointer;border-radius:.55rem;padding:.7rem .95rem;font:600 .9rem system-ui;text-decoration:none;display:inline-flex}button:disabled{opacity:.5}.danger{border-color:var(--danger);color:var(--danger);background:0 0;margin-top:.5rem}.list{padding:0;list-style:none}.list li{border-bottom:1px solid var(--line);padding:.75rem 0}.list small{display:block}.notice{border-left:.3rem solid var(--accent);background:#e7f4ef;padding:.7rem 1rem}.document-body{border:1px solid var(--line);background:var(--panel);color:inherit;font:inherit;white-space:pre-wrap;overflow-wrap:anywhere;border-radius:.85rem;margin:2rem 0;padding:1.25rem;overflow-x:auto}.metadata{gap:.5rem;margin:1.5rem 0;display:grid}.metadata div{grid-template-columns:minmax(7rem,.25fr) 1fr;gap:1rem;display:grid}.metadata dt{color:var(--muted);font-weight:650}.metadata dd{margin:0}@media (prefers-color-scheme:dark){:root{--bg:#111714;--panel:#19201d;--ink:#edf3ef;--muted:#a6b0ab;--line:#2c3732;--accent:#74c9aa;--danger:#ef8585}.meta span,.notice{background:#183a2f}}
|