@prismshadow/penguin-server 0.2.0 → 0.2.2

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.
@@ -0,0 +1,50 @@
1
+ // src/initial-password.ts
2
+ import fs from "fs";
3
+ import path from "path";
4
+ function initialAdminPasswordPath(root) {
5
+ return path.join(root, "initial-admin-password");
6
+ }
7
+ function storeInitialAdminPassword(root, password) {
8
+ try {
9
+ fs.writeFileSync(initialAdminPasswordPath(root), `${password}
10
+ `, { mode: 384 });
11
+ } catch {
12
+ }
13
+ }
14
+ function readInitialAdminPassword(root) {
15
+ try {
16
+ const value = fs.readFileSync(initialAdminPasswordPath(root), "utf8").trim();
17
+ return value === "" ? null : value;
18
+ } catch {
19
+ return null;
20
+ }
21
+ }
22
+ function clearInitialAdminPassword(root) {
23
+ try {
24
+ fs.rmSync(initialAdminPasswordPath(root), { force: true });
25
+ } catch {
26
+ }
27
+ }
28
+ var NOTICE_PADDING = 3;
29
+ function renderInitialPasswordNotice(userId, password) {
30
+ const lines = [
31
+ `Web sign-in: ${userId} / ${password}`,
32
+ "",
33
+ "This is the initial password and it has not been changed yet.",
34
+ "Please sign in and change it soon (user menu -> Change password)."
35
+ ];
36
+ const width = Math.max(...lines.map((line) => line.length));
37
+ const bar = `+${"-".repeat(width + NOTICE_PADDING * 2)}+`;
38
+ const pad = " ".repeat(NOTICE_PADDING);
39
+ const body = lines.map((line) => `|${pad}${line.padEnd(width)}${pad}|`);
40
+ return [bar, ...body, bar].join("\n");
41
+ }
42
+
43
+ export {
44
+ initialAdminPasswordPath,
45
+ storeInitialAdminPassword,
46
+ readInitialAdminPassword,
47
+ clearInitialAdminPassword,
48
+ renderInitialPasswordNotice
49
+ };
50
+ //# sourceMappingURL=chunk-UUJCF46V.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/initial-password.ts"],"sourcesContent":["/**\n * Initial-admin-password persistence (`<root>/initial-admin-password`) and the startup\n * reminder notice.\n *\n * The seeded admin password used to be printed exactly once, at first seed; if that line\n * scrolled away unread, the credential was unrecoverable. The plaintext is now kept in\n * the data root FOR AS LONG AS the admin password remains the initial one, so every\n * server start — and `penguin web` attaching to an already-running instance — can\n * re-print it inside a hard-to-miss ASCII frame together with the change-it reminder.\n *\n * The file is removed the moment the admin's password is updated by any path (self\n * change, desktop set, or an admin reset picking a new value — the reset re-arms the\n * initial FLAG, but the file's content would be stale, and an admin-chosen password is\n * never persisted). Only the auto-generated / PENGUIN_SEED_ADMIN_PASSWORD seed value is\n * ever stored, with owner-only permissions; the SQLite db next to it already holds\n * strictly more sensitive data. Legacy roots seeded before this file existed simply have\n * no file: the reminder then stays silent (the plaintext is genuinely unrecoverable).\n *\n * Published as `@prismshadow/penguin-server/initial-password` (side-effect-free, like\n * ./lock) so the CLI can print the same notice when it attaches to a live instance.\n */\nimport fs from \"node:fs\";\nimport path from \"node:path\";\n\nexport function initialAdminPasswordPath(root: string): string {\n return path.join(root, \"initial-admin-password\");\n}\n\n/** Persists the seeded plaintext (owner-only file). Best-effort: the seed itself must not fail on an exotic filesystem — the reminder just degrades to first-start-only. */\nexport function storeInitialAdminPassword(root: string, password: string): void {\n try {\n fs.writeFileSync(initialAdminPasswordPath(root), `${password}\\n`, { mode: 0o600 });\n } catch {\n // e.g. a read-only root: the password was still printed once by the seed path.\n }\n}\n\n/** The stored plaintext, or null when absent/unreadable/empty. */\nexport function readInitialAdminPassword(root: string): string | null {\n try {\n const value = fs.readFileSync(initialAdminPasswordPath(root), \"utf8\").trim();\n return value === \"\" ? null : value;\n } catch {\n return null;\n }\n}\n\n/** Removes the stored plaintext (idempotent, best-effort). */\nexport function clearInitialAdminPassword(root: string): void {\n try {\n fs.rmSync(initialAdminPasswordPath(root), { force: true });\n } catch {\n // Best-effort: a leftover file only re-prints a password that no longer works.\n }\n}\n\n/** Interior padding (spaces) between the frame bars and the text on each side. */\nconst NOTICE_PADDING = 3;\n\n/**\n * The framed reminder block, ready for console.log. Plain ASCII (`+`/`-`/`|`) rather\n * than box-drawing characters: the server may run under terminals and log collectors\n * with non-UTF-8 code pages (legacy Windows consoles above all), and a misrendered frame\n * would bury exactly the line it exists to highlight. Width follows the longest line, so\n * a long pinned password widens the frame instead of breaking it.\n */\nexport function renderInitialPasswordNotice(userId: string, password: string): string {\n const lines = [\n `Web sign-in: ${userId} / ${password}`,\n \"\",\n \"This is the initial password and it has not been changed yet.\",\n \"Please sign in and change it soon (user menu -> Change password).\",\n ];\n const width = Math.max(...lines.map((line) => line.length));\n const bar = `+${\"-\".repeat(width + NOTICE_PADDING * 2)}+`;\n const pad = \" \".repeat(NOTICE_PADDING);\n const body = lines.map((line) => `|${pad}${line.padEnd(width)}${pad}|`);\n return [bar, ...body, bar].join(\"\\n\");\n}\n"],"mappings":";AAqBA,OAAO,QAAQ;AACf,OAAO,UAAU;AAEV,SAAS,yBAAyB,MAAsB;AAC7D,SAAO,KAAK,KAAK,MAAM,wBAAwB;AACjD;AAGO,SAAS,0BAA0B,MAAc,UAAwB;AAC9E,MAAI;AACF,OAAG,cAAc,yBAAyB,IAAI,GAAG,GAAG,QAAQ;AAAA,GAAM,EAAE,MAAM,IAAM,CAAC;AAAA,EACnF,QAAQ;AAAA,EAER;AACF;AAGO,SAAS,yBAAyB,MAA6B;AACpE,MAAI;AACF,UAAM,QAAQ,GAAG,aAAa,yBAAyB,IAAI,GAAG,MAAM,EAAE,KAAK;AAC3E,WAAO,UAAU,KAAK,OAAO;AAAA,EAC/B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGO,SAAS,0BAA0B,MAAoB;AAC5D,MAAI;AACF,OAAG,OAAO,yBAAyB,IAAI,GAAG,EAAE,OAAO,KAAK,CAAC;AAAA,EAC3D,QAAQ;AAAA,EAER;AACF;AAGA,IAAM,iBAAiB;AAShB,SAAS,4BAA4B,QAAgB,UAA0B;AACpF,QAAM,QAAQ;AAAA,IACZ,iBAAiB,MAAM,MAAM,QAAQ;AAAA,IACrC;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,QAAM,QAAQ,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC,SAAS,KAAK,MAAM,CAAC;AAC1D,QAAM,MAAM,IAAI,IAAI,OAAO,QAAQ,iBAAiB,CAAC,CAAC;AACtD,QAAM,MAAM,IAAI,OAAO,cAAc;AACrC,QAAM,OAAO,MAAM,IAAI,CAAC,SAAS,IAAI,GAAG,GAAG,KAAK,OAAO,KAAK,CAAC,GAAG,GAAG,GAAG;AACtE,SAAO,CAAC,KAAK,GAAG,MAAM,GAAG,EAAE,KAAK,IAAI;AACtC;","names":[]}