@rubytech/create-realagent 1.0.832 → 1.0.833
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/index.js +131 -9
- package/package.json +1 -1
- package/payload/platform/lib/admins-write/dist/index.d.ts +87 -0
- package/payload/platform/lib/admins-write/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/admins-write/dist/index.js +248 -0
- package/payload/platform/lib/admins-write/dist/index.js.map +1 -0
- package/payload/platform/lib/admins-write/src/index.ts +311 -0
- package/payload/platform/lib/admins-write/tsconfig.json +8 -0
- package/payload/platform/neo4j/migrations/009-conversation-archive-title.ts +197 -0
- package/payload/platform/neo4j/schema.cypher +1 -1
- package/payload/platform/package.json +2 -2
- package/payload/platform/plugins/admin/PLUGIN.md +1 -1
- package/payload/platform/plugins/admin/mcp/dist/index.js +37 -44
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/docs/references/internals.md +4 -3
- package/payload/platform/plugins/memory/bin/conversation-archive-ingest.mjs +215 -43
- package/payload/platform/plugins/memory/bin/conversation-archive-ingest.sh +7 -2
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/llm-classifier.test.js +75 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/llm-classifier.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.d.ts +16 -10
- package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.js +155 -100
- package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/llm-ranker.d.ts +13 -5
- package/payload/platform/plugins/memory/mcp/dist/lib/llm-ranker.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/llm-ranker.js +53 -59
- package/payload/platform/plugins/memory/mcp/dist/lib/llm-ranker.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.d.ts +24 -7
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js +23 -5
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js.map +1 -1
- package/payload/platform/plugins/memory/skills/conversation-archive/SKILL.md +28 -8
- package/payload/platform/scripts/lib/resolve-account-dir.sh +3 -1
- package/payload/platform/scripts/migrate-import.sh +3 -1
- package/payload/platform/scripts/seed-neo4j.sh +13 -3
- package/payload/server/chunk-CRAIGEXY.js +654 -0
- package/payload/server/chunk-I2NOLBQA.js +2123 -0
- package/payload/server/chunk-IVTESKFR.js +9961 -0
- package/payload/server/chunk-KD3XP4IK.js +1116 -0
- package/payload/server/chunk-OJZPS4BL.js +367 -0
- package/payload/server/client-pool-J5BCVVI2.js +32 -0
- package/payload/server/cloudflare-task-tracker-XCUO4N74.js +19 -0
- package/payload/server/maxy-edge.js +6 -5
- package/payload/server/neo4j-migrations-5AN2U3YO.js +664 -0
- package/payload/server/public/assets/{admin-BNwPsMhJ.js → admin-CgGQafDG.js} +1 -1
- package/payload/server/public/assets/{graph-N_Bw-8oT.js → graph-BlrcvwWP.js} +1 -1
- package/payload/server/public/assets/{page-BKLGP-th.js → page-ClTUYMK8.js} +1 -1
- package/payload/server/public/graph.html +2 -2
- package/payload/server/public/index.html +2 -2
- package/payload/server/server.js +373 -167
- package/payload/platform/plugins/admin/mcp/dist/lib/review-tools.d.ts +0 -31
- package/payload/platform/plugins/admin/mcp/dist/lib/review-tools.d.ts.map +0 -1
- package/payload/platform/plugins/admin/mcp/dist/lib/review-tools.js +0 -666
- package/payload/platform/plugins/admin/mcp/dist/lib/review-tools.js.map +0 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/semantic-chunker.d.ts +0 -61
- package/payload/platform/plugins/memory/mcp/dist/lib/semantic-chunker.d.ts.map +0 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/semantic-chunker.js +0 -266
- package/payload/platform/plugins/memory/mcp/dist/lib/semantic-chunker.js.map +0 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-insight-pass.d.ts +0 -27
- package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-insight-pass.d.ts.map +0 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-insight-pass.js +0 -477
- package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-insight-pass.js.map +0 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-insight-write.d.ts +0 -27
- package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-insight-write.d.ts.map +0 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-insight-write.js +0 -160
- package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-insight-write.js.map +0 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-parse.d.ts +0 -10
- package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-parse.d.ts.map +0 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-parse.js +0 -29
- package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-parse.js.map +0 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-preview.d.ts +0 -28
- package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-preview.d.ts.map +0 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-preview.js +0 -34
- package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-preview.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { execFileSync, spawn, spawnSync } from "node:child_process";
|
|
3
|
-
import { existsSync, mkdirSync, writeFileSync, cpSync, readFileSync, rmSync, readdirSync, appendFileSync, openSync, closeSync, chmodSync, symlinkSync, unlinkSync, lstatSync, readlinkSync, accessSync, constants as fsConstants } from "node:fs";
|
|
3
|
+
import { existsSync, mkdirSync, writeFileSync, cpSync, readFileSync, rmSync, readdirSync, appendFileSync, openSync, closeSync, chmodSync, symlinkSync, unlinkSync, lstatSync, statSync, readlinkSync, accessSync, constants as fsConstants } from "node:fs";
|
|
4
4
|
import { resolve, join, dirname } from "node:path";
|
|
5
5
|
import { randomBytes } from "node:crypto";
|
|
6
6
|
import { resolveInstallPortFromFs, buildMaxyUnitFile } from "./port-resolution.js";
|
|
@@ -1334,6 +1334,90 @@ function provisionRemoteSessionSecret() {
|
|
|
1334
1334
|
writeFileSync(secretFile, randomBytes(32).toString("hex"), { mode: 0o600 });
|
|
1335
1335
|
console.log(` [install] remote-session-secret provisioned path=${secretFile}`);
|
|
1336
1336
|
}
|
|
1337
|
+
// Task 904 — install-time admin-auth invariant. Walks every account.json
|
|
1338
|
+
// under accountsDir and compares its admins[] to the persistent users.json,
|
|
1339
|
+
// emitting one [install-invariant] line per divergence and one summary line.
|
|
1340
|
+
// Log-only (no install abort) so pre-Task-904 devices with already-divergent
|
|
1341
|
+
// state still upgrade; future sprint can promote to refuse-or-degrade once
|
|
1342
|
+
// the deployed fleet is audited clean. Mirror of
|
|
1343
|
+
// checkAdminAuthInvariant() in platform/lib/admins-write/src/index.ts.
|
|
1344
|
+
function runInstallInvariantCheck(usersFile, accountsDir) {
|
|
1345
|
+
const TAG = "[install-invariant]";
|
|
1346
|
+
const usersUserIds = new Set();
|
|
1347
|
+
if (existsSync(usersFile)) {
|
|
1348
|
+
try {
|
|
1349
|
+
const raw = readFileSync(usersFile, "utf-8").trim();
|
|
1350
|
+
if (raw) {
|
|
1351
|
+
const users = JSON.parse(raw);
|
|
1352
|
+
for (const u of users) {
|
|
1353
|
+
if (typeof u.userId === "string")
|
|
1354
|
+
usersUserIds.add(u.userId);
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
catch (err) {
|
|
1359
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1360
|
+
console.log(` ${TAG} users.json unreadable usersFile=${usersFile} error=${msg}`);
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
const accountUserIds = new Set();
|
|
1364
|
+
let divergences = 0;
|
|
1365
|
+
if (existsSync(accountsDir)) {
|
|
1366
|
+
let entries = [];
|
|
1367
|
+
try {
|
|
1368
|
+
entries = readdirSync(accountsDir);
|
|
1369
|
+
}
|
|
1370
|
+
catch (err) {
|
|
1371
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1372
|
+
console.log(` ${TAG} accounts-dir unreadable accountsDir=${accountsDir} error=${msg}`);
|
|
1373
|
+
console.log(` ${TAG} check complete divergences=0 (accounts-dir unreadable)`);
|
|
1374
|
+
return;
|
|
1375
|
+
}
|
|
1376
|
+
for (const entry of entries) {
|
|
1377
|
+
if (entry.startsWith("."))
|
|
1378
|
+
continue;
|
|
1379
|
+
const accountDir = join(accountsDir, entry);
|
|
1380
|
+
try {
|
|
1381
|
+
if (!statSync(accountDir).isDirectory())
|
|
1382
|
+
continue;
|
|
1383
|
+
}
|
|
1384
|
+
catch {
|
|
1385
|
+
continue;
|
|
1386
|
+
}
|
|
1387
|
+
const accountJsonPath = join(accountDir, "account.json");
|
|
1388
|
+
if (!existsSync(accountJsonPath))
|
|
1389
|
+
continue;
|
|
1390
|
+
let admins = [];
|
|
1391
|
+
try {
|
|
1392
|
+
const config = JSON.parse(readFileSync(accountJsonPath, "utf-8"));
|
|
1393
|
+
admins = (config.admins ?? []);
|
|
1394
|
+
}
|
|
1395
|
+
catch (err) {
|
|
1396
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1397
|
+
console.log(` ${TAG} account.json unreadable source=${accountJsonPath} error=${msg}`);
|
|
1398
|
+
continue;
|
|
1399
|
+
}
|
|
1400
|
+
for (const a of admins) {
|
|
1401
|
+
if (typeof a.userId !== "string")
|
|
1402
|
+
continue;
|
|
1403
|
+
accountUserIds.add(a.userId);
|
|
1404
|
+
if (!usersUserIds.has(a.userId)) {
|
|
1405
|
+
const userIdShort = a.userId.slice(0, 8);
|
|
1406
|
+
console.log(` ${TAG} direction=account-without-users userId=${userIdShort} source=${accountJsonPath}`);
|
|
1407
|
+
divergences += 1;
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
for (const uid of usersUserIds) {
|
|
1413
|
+
if (!accountUserIds.has(uid)) {
|
|
1414
|
+
const userIdShort = uid.slice(0, 8);
|
|
1415
|
+
console.log(` ${TAG} direction=users-without-account userId=${userIdShort} source=${usersFile}`);
|
|
1416
|
+
divergences += 1;
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
console.log(` ${TAG} check complete divergences=${divergences}`);
|
|
1420
|
+
}
|
|
1337
1421
|
function deployPayload() {
|
|
1338
1422
|
log("8", TOTAL, `Deploying ${BRAND.productName}...`);
|
|
1339
1423
|
if (!existsSync(PAYLOAD_DIR)) {
|
|
@@ -1354,14 +1438,24 @@ function deployPayload() {
|
|
|
1354
1438
|
mkdirSync(persistentDir, { recursive: true });
|
|
1355
1439
|
cpSync(oldAccountsDir, persistentAccountsDir, { recursive: true });
|
|
1356
1440
|
}
|
|
1357
|
-
//
|
|
1358
|
-
//
|
|
1441
|
+
// Task 904 — users.json lives at <persistentDir>/users.json. Pre-Task-904
|
|
1442
|
+
// installs wrote to <INSTALL_DIR>/platform/config/users.json (the wipe zone)
|
|
1443
|
+
// and relied on a one-shot copy-up at first install plus a copy-back after
|
|
1444
|
+
// every wipe. The copy-back overwrote LIVE with the FROZEN-AT-FIRST-INSTALL
|
|
1445
|
+
// backup, dropping every admin row added since first install. Writers (paths.ts,
|
|
1446
|
+
// admin-add, set-pin, seed-neo4j.sh, migrate-import.sh) now target the
|
|
1447
|
+
// persistent file directly — see comment in platform/ui/app/lib/paths.ts.
|
|
1448
|
+
//
|
|
1449
|
+
// Legacy pickup: if a pre-Task-904 install left users.json inside the wipe
|
|
1450
|
+
// zone, copy it up once. Idempotent — guarded by !existsSync(persistentUsersFile).
|
|
1451
|
+
// Safe to delete this block one release after Task 904 has been deployed
|
|
1452
|
+
// everywhere; until then it absorbs upgrades from any pre-Task-904 version.
|
|
1359
1453
|
const persistentUsersFile = join(persistentDir, "users.json");
|
|
1360
1454
|
const oldUsersFile = join(INSTALL_DIR, "platform/config/users.json");
|
|
1361
1455
|
if (existsSync(oldUsersFile) && !existsSync(persistentUsersFile)) {
|
|
1362
1456
|
mkdirSync(persistentDir, { recursive: true });
|
|
1363
1457
|
cpSync(oldUsersFile, persistentUsersFile);
|
|
1364
|
-
console.log(" Migrated users.json to persistent storage.");
|
|
1458
|
+
console.log(" Migrated pre-Task-904 users.json to persistent storage.");
|
|
1365
1459
|
}
|
|
1366
1460
|
// Brand isolation: installer does not read ~/.maxy/, ~/.cloudflared/, or
|
|
1367
1461
|
// ~/.cloudflare/ on non-default brands. These are peer-brand or shared-singleton
|
|
@@ -1440,13 +1534,41 @@ function deployPayload() {
|
|
|
1440
1534
|
cpSync(persistentPasswordFile, join(configDir, ".neo4j-password"));
|
|
1441
1535
|
console.log(" Restored Neo4j password.");
|
|
1442
1536
|
}
|
|
1537
|
+
// Task 904 — users.json is read directly from persistentDir by both
|
|
1538
|
+
// platform/ui/app/lib/paths.ts (USERS_FILE = MAXY_DIR/users.json) and the
|
|
1539
|
+
// admin MCP plugin (USERS_FILE = CONFIG_DIR/users.json). No copy into the
|
|
1540
|
+
// wipe zone — the pre-Task-904 cpSync to platform/config/users.json was the
|
|
1541
|
+
// copy that overwrote new admin rows on every install. The line below
|
|
1542
|
+
// observes the row count + userId prefixes so a future regression is grep-
|
|
1543
|
+
// detectable in the install log without any runtime change (singular
|
|
1544
|
+
// "userId preserved" hid the multi-row failure mode).
|
|
1443
1545
|
if (existsSync(persistentUsersFile)) {
|
|
1444
|
-
|
|
1445
|
-
|
|
1546
|
+
try {
|
|
1547
|
+
const raw = readFileSync(persistentUsersFile, "utf-8").trim();
|
|
1548
|
+
const rows = raw ? JSON.parse(raw) : [];
|
|
1549
|
+
const ids = rows
|
|
1550
|
+
.map(r => (typeof r.userId === "string" ? r.userId.slice(0, 8) : "?"))
|
|
1551
|
+
.join(",");
|
|
1552
|
+
console.log(` [install] users.json preserved: rows=${rows.length} userIds=${ids}`);
|
|
1553
|
+
}
|
|
1554
|
+
catch (err) {
|
|
1555
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1556
|
+
console.log(` [install] users.json preserved: rows=? parse-failed error=${msg}`);
|
|
1557
|
+
}
|
|
1446
1558
|
}
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1559
|
+
else {
|
|
1560
|
+
console.log(" [install] users.json: no persistent file (fresh install — set-pin will create it)");
|
|
1561
|
+
}
|
|
1562
|
+
// Task 904 item 3 — install-time admin-auth invariant check. Walks every
|
|
1563
|
+
// data/accounts/*/account.json admins[] and compares to the persistent
|
|
1564
|
+
// users.json. Log-only (does NOT refuse install) so pre-Task-904 devices
|
|
1565
|
+
// with already-divergent state still upgrade; the [install-invariant]
|
|
1566
|
+
// line surfaces the bug to the operator without bricking the device.
|
|
1567
|
+
// Inline rather than imported from platform/lib/admins-write because the
|
|
1568
|
+
// installer is its own npm package and doesn't bundle the platform lib.
|
|
1569
|
+
// Mirrors checkAdminAuthInvariant() in platform/lib/admins-write/src/index.ts;
|
|
1570
|
+
// future divergence between the two should be caught by the test suite.
|
|
1571
|
+
runInstallInvariantCheck(persistentUsersFile, join(INSTALL_DIR, "data", "accounts"));
|
|
1450
1572
|
// Write version marker so the running platform knows which create-maxy produced this deployment
|
|
1451
1573
|
writeFileSync(join(configDir, `.${BRAND.hostname}-version`), PKG_VERSION, "utf-8");
|
|
1452
1574
|
console.log(` Deployed to ${INSTALL_DIR}`);
|
package/package.json
CHANGED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
export interface UserEntry {
|
|
2
|
+
userId: string;
|
|
3
|
+
pin: string;
|
|
4
|
+
name?: string;
|
|
5
|
+
}
|
|
6
|
+
export interface AdminEntry {
|
|
7
|
+
userId: string;
|
|
8
|
+
role: "owner" | "admin";
|
|
9
|
+
}
|
|
10
|
+
export interface AdminWriteInput {
|
|
11
|
+
userId: string;
|
|
12
|
+
pin: string;
|
|
13
|
+
role: "owner" | "admin";
|
|
14
|
+
usersFile: string;
|
|
15
|
+
accountDir: string;
|
|
16
|
+
caller: string;
|
|
17
|
+
}
|
|
18
|
+
export interface AdminWriteResult {
|
|
19
|
+
usersJsonResult: "ok" | "fail" | "noop";
|
|
20
|
+
accountJsonResult: "ok" | "fail" | "noop";
|
|
21
|
+
usersError?: string;
|
|
22
|
+
accountError?: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Write a single admin entry across both auth stores (users.json + account.json admins[]).
|
|
26
|
+
*
|
|
27
|
+
* Idempotent on userId: if the userId already exists in users.json, the PIN
|
|
28
|
+
* field is updated in place rather than duplicated; admins[] is checked for
|
|
29
|
+
* presence before pushing.
|
|
30
|
+
*
|
|
31
|
+
* Always emits exactly one `[admins-write] caller=… userId=<prefix> usersJsonResult=… accountJsonResult=…`
|
|
32
|
+
* line — success and failure paths both fire it. Operators grep for this single
|
|
33
|
+
* predicate when reconstructing a partial-write incident.
|
|
34
|
+
*/
|
|
35
|
+
export declare function writeAdminEntry(input: AdminWriteInput): AdminWriteResult;
|
|
36
|
+
export interface AdminRemoveInput {
|
|
37
|
+
userId: string;
|
|
38
|
+
accountDir: string;
|
|
39
|
+
caller: string;
|
|
40
|
+
}
|
|
41
|
+
export interface AdminRemoveResult {
|
|
42
|
+
accountJsonResult: "ok" | "fail" | "noop";
|
|
43
|
+
accountError?: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Remove a single admin entry from account.json admins[] — does NOT touch
|
|
47
|
+
* users.json (the device-level entry is preserved because the user may admin
|
|
48
|
+
* other accounts). Single-file operation; the [admins-write] line records
|
|
49
|
+
* `usersJsonResult=skip` so the grep-by-helper-call still picks it up.
|
|
50
|
+
*/
|
|
51
|
+
export declare function removeAdminFromAccount(input: AdminRemoveInput): AdminRemoveResult;
|
|
52
|
+
export interface InvariantCheckInput {
|
|
53
|
+
/** Absolute path to users.json (persistent location). */
|
|
54
|
+
usersFile: string;
|
|
55
|
+
/** Absolute path to the accounts root (e.g. <INSTALL_DIR>/data/accounts). */
|
|
56
|
+
accountsDir: string;
|
|
57
|
+
/** Tag for the log prefix — `install-invariant` or `admin-invariant`. */
|
|
58
|
+
tag: "install-invariant" | "admin-invariant";
|
|
59
|
+
}
|
|
60
|
+
export interface InvariantCheckResult {
|
|
61
|
+
divergences: number;
|
|
62
|
+
/** Userids in account.json admins[] that have no row in users.json. */
|
|
63
|
+
accountWithoutUsers: Array<{
|
|
64
|
+
userId: string;
|
|
65
|
+
source: string;
|
|
66
|
+
}>;
|
|
67
|
+
/** Userids in users.json that no account.json admins[] references. */
|
|
68
|
+
usersWithoutAccount: Array<{
|
|
69
|
+
userId: string;
|
|
70
|
+
}>;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Walk every account.json under accountsDir and compare its admins[] to
|
|
74
|
+
* users.json. Emits one log line per divergence and one summary line.
|
|
75
|
+
*
|
|
76
|
+
* Behaviour: log-only — does NOT throw, does NOT refuse boot or install.
|
|
77
|
+
* Pre-Task-904 installs that already diverged would otherwise brick on the
|
|
78
|
+
* first boot of a Task-904 device. The summary line `divergences=<n>` is
|
|
79
|
+
* always emitted (wired-up assertion); per-divergence lines fire only when
|
|
80
|
+
* divergences > 0. Operators grep `[install-invariant]` or `[admin-invariant]`
|
|
81
|
+
* to detect the regression class without reading every install log.
|
|
82
|
+
*
|
|
83
|
+
* Promotion to refuse-or-degrade is a future sprint; capture as a TODO once
|
|
84
|
+
* the deployed fleet has been audited clean.
|
|
85
|
+
*/
|
|
86
|
+
export declare function checkAdminAuthInvariant(input: InvariantCheckInput): InvariantCheckResult;
|
|
87
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAwBA,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IAGZ,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,eAAe,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,CAAC;IACxC,iBAAiB,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,CAAC;IAC1C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAmBD;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,eAAe,GAAG,gBAAgB,CA4DxE;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,iBAAiB;IAChC,iBAAiB,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,CAAC;IAC1C,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,gBAAgB,GAAG,iBAAiB,CA+BjF;AAED,MAAM,WAAW,mBAAmB;IAClC,yDAAyD;IACzD,SAAS,EAAE,MAAM,CAAC;IAClB,6EAA6E;IAC7E,WAAW,EAAE,MAAM,CAAC;IACpB,yEAAyE;IACzE,GAAG,EAAE,mBAAmB,GAAG,iBAAiB,CAAC;CAC9C;AAED,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,uEAAuE;IACvE,mBAAmB,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/D,sEAAsE;IACtE,mBAAmB,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAChD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,mBAAmB,GAAG,oBAAoB,CAsFxF"}
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// admins-write — single chokepoint for the dual-file admin write
|
|
3
|
+
// (users.json device-level PIN auth + account.json account-level role).
|
|
4
|
+
//
|
|
5
|
+
// Task 904 background: pre-Task-904 the two writers — `admin-add`
|
|
6
|
+
// (platform/plugins/admin/mcp/src/index.ts) and `set-pin` POST
|
|
7
|
+
// (platform/ui/server/routes/onboarding.ts) — each performed the dual write
|
|
8
|
+
// inline. They drifted: admin-add returned `is_error: true` with a per-leg
|
|
9
|
+
// `[admin-auth-store]` line on partial failure; set-pin logged on stderr but
|
|
10
|
+
// otherwise rolled forward. This module collapses both into one routed write
|
|
11
|
+
// so the static check `grep -rnE 'admins\.push|config\.admins\s*=' platform/`
|
|
12
|
+
// returns 0 outside this file (excluding tests, scripts, and the reader paths
|
|
13
|
+
// which never push).
|
|
14
|
+
//
|
|
15
|
+
// Atomicity contract — single file: write-temp + rename, durable.
|
|
16
|
+
// Atomicity contract — across files: NOT atomic. POSIX has no two-file
|
|
17
|
+
// transaction. The helper writes users.json first, then account.json. On
|
|
18
|
+
// account.json failure with users.json already written, the result names the
|
|
19
|
+
// partial state. Callers MUST surface partial state to the operator (admin-add
|
|
20
|
+
// returns is_error: true with the [admins-write] line; set-pin returns 500
|
|
21
|
+
// with the same). Same trust model as Task 850's [admin-auth-store] pair.
|
|
22
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
+
exports.writeAdminEntry = writeAdminEntry;
|
|
24
|
+
exports.removeAdminFromAccount = removeAdminFromAccount;
|
|
25
|
+
exports.checkAdminAuthInvariant = checkAdminAuthInvariant;
|
|
26
|
+
const node_fs_1 = require("node:fs");
|
|
27
|
+
const node_path_1 = require("node:path");
|
|
28
|
+
function logLine(input, result) {
|
|
29
|
+
const userIdShort = input.userId.slice(0, 8);
|
|
30
|
+
console.error(`[admins-write] caller=${input.caller} userId=${userIdShort} ` +
|
|
31
|
+
`usersJsonResult=${result.usersJsonResult} accountJsonResult=${result.accountJsonResult}` +
|
|
32
|
+
(result.usersError ? ` usersError=${result.usersError}` : "") +
|
|
33
|
+
(result.accountError ? ` accountError=${result.accountError}` : ""));
|
|
34
|
+
}
|
|
35
|
+
function writeFileAtomic(filePath, contents, mode) {
|
|
36
|
+
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(filePath), { recursive: true });
|
|
37
|
+
const tempPath = `${filePath}.tmp-${process.pid}-${Date.now()}`;
|
|
38
|
+
(0, node_fs_1.writeFileSync)(tempPath, contents, mode !== undefined ? { mode } : undefined);
|
|
39
|
+
(0, node_fs_1.renameSync)(tempPath, filePath);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Write a single admin entry across both auth stores (users.json + account.json admins[]).
|
|
43
|
+
*
|
|
44
|
+
* Idempotent on userId: if the userId already exists in users.json, the PIN
|
|
45
|
+
* field is updated in place rather than duplicated; admins[] is checked for
|
|
46
|
+
* presence before pushing.
|
|
47
|
+
*
|
|
48
|
+
* Always emits exactly one `[admins-write] caller=… userId=<prefix> usersJsonResult=… accountJsonResult=…`
|
|
49
|
+
* line — success and failure paths both fire it. Operators grep for this single
|
|
50
|
+
* predicate when reconstructing a partial-write incident.
|
|
51
|
+
*/
|
|
52
|
+
function writeAdminEntry(input) {
|
|
53
|
+
const result = { usersJsonResult: "noop", accountJsonResult: "noop" };
|
|
54
|
+
// 1. users.json — read-modify-write atomically.
|
|
55
|
+
try {
|
|
56
|
+
let users = [];
|
|
57
|
+
if ((0, node_fs_1.existsSync)(input.usersFile)) {
|
|
58
|
+
const raw = (0, node_fs_1.readFileSync)(input.usersFile, "utf-8").trim();
|
|
59
|
+
if (raw) {
|
|
60
|
+
const parsed = JSON.parse(raw);
|
|
61
|
+
if (!Array.isArray(parsed)) {
|
|
62
|
+
throw new Error("users.json is not an array");
|
|
63
|
+
}
|
|
64
|
+
users = parsed;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
const existing = users.findIndex(u => u.userId === input.userId);
|
|
68
|
+
if (existing === -1) {
|
|
69
|
+
users.push({ userId: input.userId, pin: input.pin });
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
// Preserve any sibling fields that other writers may have set, but
|
|
73
|
+
// overwrite pin. Strip the deprecated `name` field on touch (Task 829).
|
|
74
|
+
const merged = { ...users[existing], pin: input.pin };
|
|
75
|
+
delete merged.name;
|
|
76
|
+
users[existing] = merged;
|
|
77
|
+
}
|
|
78
|
+
writeFileAtomic(input.usersFile, JSON.stringify(users, null, 2) + "\n", 0o600);
|
|
79
|
+
result.usersJsonResult = "ok";
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
result.usersJsonResult = "fail";
|
|
83
|
+
result.usersError = err instanceof Error ? err.message : String(err);
|
|
84
|
+
logLine(input, result);
|
|
85
|
+
return result;
|
|
86
|
+
}
|
|
87
|
+
// 2. account.json — read-modify-write atomically.
|
|
88
|
+
try {
|
|
89
|
+
const accountJsonPath = (0, node_path_1.join)(input.accountDir, "account.json");
|
|
90
|
+
if (!(0, node_fs_1.existsSync)(accountJsonPath)) {
|
|
91
|
+
throw new Error(`account.json not found at ${accountJsonPath}`);
|
|
92
|
+
}
|
|
93
|
+
const config = JSON.parse((0, node_fs_1.readFileSync)(accountJsonPath, "utf-8"));
|
|
94
|
+
const admins = (config.admins ?? []);
|
|
95
|
+
if (admins.some(a => a.userId === input.userId)) {
|
|
96
|
+
result.accountJsonResult = "noop";
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
admins.push({ userId: input.userId, role: input.role });
|
|
100
|
+
config.admins = admins;
|
|
101
|
+
writeFileAtomic(accountJsonPath, JSON.stringify(config, null, 2) + "\n");
|
|
102
|
+
result.accountJsonResult = "ok";
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
result.accountJsonResult = "fail";
|
|
107
|
+
result.accountError = err instanceof Error ? err.message : String(err);
|
|
108
|
+
}
|
|
109
|
+
logLine(input, result);
|
|
110
|
+
return result;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Remove a single admin entry from account.json admins[] — does NOT touch
|
|
114
|
+
* users.json (the device-level entry is preserved because the user may admin
|
|
115
|
+
* other accounts). Single-file operation; the [admins-write] line records
|
|
116
|
+
* `usersJsonResult=skip` so the grep-by-helper-call still picks it up.
|
|
117
|
+
*/
|
|
118
|
+
function removeAdminFromAccount(input) {
|
|
119
|
+
const result = { accountJsonResult: "noop" };
|
|
120
|
+
const userIdShort = input.userId.slice(0, 8);
|
|
121
|
+
try {
|
|
122
|
+
const accountJsonPath = (0, node_path_1.join)(input.accountDir, "account.json");
|
|
123
|
+
if (!(0, node_fs_1.existsSync)(accountJsonPath)) {
|
|
124
|
+
throw new Error(`account.json not found at ${accountJsonPath}`);
|
|
125
|
+
}
|
|
126
|
+
const config = JSON.parse((0, node_fs_1.readFileSync)(accountJsonPath, "utf-8"));
|
|
127
|
+
const admins = (config.admins ?? []);
|
|
128
|
+
const targetIndex = admins.findIndex(a => a.userId === input.userId);
|
|
129
|
+
if (targetIndex === -1) {
|
|
130
|
+
result.accountJsonResult = "noop";
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
admins.splice(targetIndex, 1);
|
|
134
|
+
config.admins = admins;
|
|
135
|
+
writeFileAtomic(accountJsonPath, JSON.stringify(config, null, 2) + "\n");
|
|
136
|
+
result.accountJsonResult = "ok";
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
catch (err) {
|
|
140
|
+
result.accountJsonResult = "fail";
|
|
141
|
+
result.accountError = err instanceof Error ? err.message : String(err);
|
|
142
|
+
}
|
|
143
|
+
console.error(`[admins-write] caller=${input.caller} userId=${userIdShort} ` +
|
|
144
|
+
`usersJsonResult=skip accountJsonResult=${result.accountJsonResult}` +
|
|
145
|
+
(result.accountError ? ` accountError=${result.accountError}` : ""));
|
|
146
|
+
return result;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Walk every account.json under accountsDir and compare its admins[] to
|
|
150
|
+
* users.json. Emits one log line per divergence and one summary line.
|
|
151
|
+
*
|
|
152
|
+
* Behaviour: log-only — does NOT throw, does NOT refuse boot or install.
|
|
153
|
+
* Pre-Task-904 installs that already diverged would otherwise brick on the
|
|
154
|
+
* first boot of a Task-904 device. The summary line `divergences=<n>` is
|
|
155
|
+
* always emitted (wired-up assertion); per-divergence lines fire only when
|
|
156
|
+
* divergences > 0. Operators grep `[install-invariant]` or `[admin-invariant]`
|
|
157
|
+
* to detect the regression class without reading every install log.
|
|
158
|
+
*
|
|
159
|
+
* Promotion to refuse-or-degrade is a future sprint; capture as a TODO once
|
|
160
|
+
* the deployed fleet has been audited clean.
|
|
161
|
+
*/
|
|
162
|
+
function checkAdminAuthInvariant(input) {
|
|
163
|
+
const result = {
|
|
164
|
+
divergences: 0,
|
|
165
|
+
accountWithoutUsers: [],
|
|
166
|
+
usersWithoutAccount: [],
|
|
167
|
+
};
|
|
168
|
+
// Read users.json userIds (persistent file). Absent file == empty set; do
|
|
169
|
+
// not abort — first-boot before set-pin has run is a legitimate state.
|
|
170
|
+
const usersUserIds = new Set();
|
|
171
|
+
if ((0, node_fs_1.existsSync)(input.usersFile)) {
|
|
172
|
+
try {
|
|
173
|
+
const raw = (0, node_fs_1.readFileSync)(input.usersFile, "utf-8").trim();
|
|
174
|
+
if (raw) {
|
|
175
|
+
const users = JSON.parse(raw);
|
|
176
|
+
for (const u of users) {
|
|
177
|
+
if (typeof u.userId === "string")
|
|
178
|
+
usersUserIds.add(u.userId);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
catch (err) {
|
|
183
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
184
|
+
console.error(`[${input.tag}] users.json unreadable usersFile=${input.usersFile} error=${msg}`);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
// Walk account dirs. Skip dotfiles (.trash/) and missing account.json.
|
|
188
|
+
const accountUserIds = new Set();
|
|
189
|
+
if ((0, node_fs_1.existsSync)(input.accountsDir)) {
|
|
190
|
+
let entries;
|
|
191
|
+
try {
|
|
192
|
+
entries = (0, node_fs_1.readdirSync)(input.accountsDir);
|
|
193
|
+
}
|
|
194
|
+
catch (err) {
|
|
195
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
196
|
+
console.error(`[${input.tag}] accounts-dir unreadable accountsDir=${input.accountsDir} error=${msg}`);
|
|
197
|
+
console.error(`[${input.tag}] check complete divergences=0 (accounts-dir unreadable)`);
|
|
198
|
+
return result;
|
|
199
|
+
}
|
|
200
|
+
for (const entry of entries) {
|
|
201
|
+
if (entry.startsWith("."))
|
|
202
|
+
continue;
|
|
203
|
+
const accountDir = (0, node_path_1.join)(input.accountsDir, entry);
|
|
204
|
+
try {
|
|
205
|
+
if (!(0, node_fs_1.statSync)(accountDir).isDirectory())
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
catch {
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
const accountJsonPath = (0, node_path_1.join)(accountDir, "account.json");
|
|
212
|
+
if (!(0, node_fs_1.existsSync)(accountJsonPath))
|
|
213
|
+
continue;
|
|
214
|
+
let admins = [];
|
|
215
|
+
try {
|
|
216
|
+
const config = JSON.parse((0, node_fs_1.readFileSync)(accountJsonPath, "utf-8"));
|
|
217
|
+
admins = (config.admins ?? []);
|
|
218
|
+
}
|
|
219
|
+
catch (err) {
|
|
220
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
221
|
+
console.error(`[${input.tag}] account.json unreadable source=${accountJsonPath} error=${msg}`);
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
for (const a of admins) {
|
|
225
|
+
if (typeof a.userId !== "string")
|
|
226
|
+
continue;
|
|
227
|
+
accountUserIds.add(a.userId);
|
|
228
|
+
if (!usersUserIds.has(a.userId)) {
|
|
229
|
+
const userIdShort = a.userId.slice(0, 8);
|
|
230
|
+
console.error(`[${input.tag}] direction=account-without-users userId=${userIdShort} source=${accountJsonPath}`);
|
|
231
|
+
result.accountWithoutUsers.push({ userId: a.userId, source: accountJsonPath });
|
|
232
|
+
result.divergences += 1;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
for (const uid of usersUserIds) {
|
|
238
|
+
if (!accountUserIds.has(uid)) {
|
|
239
|
+
const userIdShort = uid.slice(0, 8);
|
|
240
|
+
console.error(`[${input.tag}] direction=users-without-account userId=${userIdShort} source=${input.usersFile}`);
|
|
241
|
+
result.usersWithoutAccount.push({ userId: uid });
|
|
242
|
+
result.divergences += 1;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
console.error(`[${input.tag}] check complete divergences=${result.divergences}`);
|
|
246
|
+
return result;
|
|
247
|
+
}
|
|
248
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|