brains-mcp 1.65.2 → 1.65.4
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/tools/createPage.d.ts.map +1 -1
- package/dist/tools/createPage.js +44 -5
- package/dist/tools/createPage.js.map +1 -1
- package/dist/tools/updatePage.d.ts +3 -3
- package/dist/tools/updatePage.d.ts.map +1 -1
- package/dist/tools/updatePage.js +64 -11
- package/dist/tools/updatePage.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createPage.d.ts","sourceRoot":"","sources":["../../src/tools/createPage.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"createPage.d.ts","sourceRoot":"","sources":["../../src/tools/createPage.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,KAAK,aAAa,EAA4B,MAAM,sBAAsB,CAAC;AAapF,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;EAShC,CAAC;AAEH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE,wBAAsB,UAAU,CAC9B,KAAK,EAAE,aAAa,EACpB,KAAK,EAAE,eAAe,EACtB,MAAM,SAAK,GACV,OAAO,CAAC,MAAM,CAAC,CAqIjB"}
|
package/dist/tools/createPage.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import crypto from "crypto";
|
|
1
2
|
import { z } from "zod";
|
|
2
3
|
import { resolveIndexOwnerSubject } from "../storage/client.js";
|
|
3
4
|
import { buildNewPage, parsePage } from "../wiki/frontmatter.js";
|
|
@@ -9,6 +10,8 @@ import { appendLogEntry } from "../wiki/log.js";
|
|
|
9
10
|
import { getPostgresSearchIndex } from "../search/postgresIndex.js";
|
|
10
11
|
import { assertWithinLimits, assertPageLimit, assertPageSize } from "../wiki/storageLimits.js";
|
|
11
12
|
import { dispatchPageWebhook } from "../webhooks/dispatch.js";
|
|
13
|
+
import { flushWithRetry } from "../storage/writeBehindRetry.js";
|
|
14
|
+
import { DrivePermissionError } from "../drive/errors.js";
|
|
12
15
|
export const createPageInputSchema = z.object({
|
|
13
16
|
name: z
|
|
14
17
|
.string()
|
|
@@ -34,16 +37,52 @@ export async function createPage(drive, input, userId = "") {
|
|
|
34
37
|
tags: input.tags,
|
|
35
38
|
body: input.body,
|
|
36
39
|
});
|
|
37
|
-
const writeResult = await drive.createFile(normalizedName, content);
|
|
38
40
|
const pgIndex = getPostgresSearchIndex();
|
|
39
41
|
// Cross-account sharing (BRA-381): attribute index/webhook writes to the
|
|
40
42
|
// true storage owner, not the raw authenticated actor — otherwise a grant
|
|
41
43
|
// owner never sees a grantee's contributions via list_pages/search_pages.
|
|
42
44
|
const indexOwner = resolveIndexOwnerSubject(drive, normalizedName, userId);
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
let writeResult;
|
|
46
|
+
if (pgIndex.isConfigured()) {
|
|
47
|
+
// DB-primary write (GH #548), matching patch_page/update_page's
|
|
48
|
+
// convention: Postgres is what read_page/search actually read from, so
|
|
49
|
+
// it must be the write of record, with Storage as an eventually
|
|
50
|
+
// consistent write-behind mirror. casInsertPage is a conditional insert
|
|
51
|
+
// (INSERT ... ON CONFLICT DO NOTHING) — it reports false rather than
|
|
52
|
+
// clobbering a row a concurrent create already landed, which is the
|
|
53
|
+
// DB-primary analogue of Storage's exclusive-create ("wx" flag) check
|
|
54
|
+
// that this write path no longer performs synchronously.
|
|
55
|
+
const revision = crypto.createHash("sha256").update(content, "utf8").digest("hex").slice(0, 16);
|
|
56
|
+
const modifiedTime = new Date().toISOString();
|
|
57
|
+
const casApplied = await pgIndex.casInsertPage(normalizedName, parsedFrontmatter, content, revision, modifiedTime, indexOwner);
|
|
58
|
+
if (!casApplied) {
|
|
59
|
+
throw new DrivePermissionError(`File '${normalizedName}' already exists. Use update_page instead.`);
|
|
60
|
+
}
|
|
61
|
+
writeResult = { fileId: normalizedName, revision, modifiedTime };
|
|
62
|
+
// Write-behind: keep the Storage mirror eventually consistent
|
|
63
|
+
// (non-blocking). Retries transient failures with bounded backoff off
|
|
64
|
+
// this synchronous path; on exhaustion it logs a terminal
|
|
65
|
+
// STORAGE_WRITE_BEHIND_FAILED signal instead of letting the mirror
|
|
66
|
+
// silently drift (GH #547).
|
|
67
|
+
//
|
|
68
|
+
// The DB row is what "already exists" means now, so casInsertPage above
|
|
69
|
+
// can win even when Storage already has an unindexed object at this path
|
|
70
|
+
// (a legacy write audit_index hasn't reconciled yet). A bare createFile
|
|
71
|
+
// there throws EEXIST forever and the mirror never converges — fall back
|
|
72
|
+
// to an overwrite so the DB row (source of truth) wins.
|
|
73
|
+
flushWithRetry(() => drive.createFile(normalizedName, content).catch((err) => {
|
|
74
|
+
if (err instanceof DrivePermissionError) {
|
|
75
|
+
return drive.updateFile(normalizedName, content, "");
|
|
76
|
+
}
|
|
77
|
+
throw err;
|
|
78
|
+
}), normalizedName);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
// Storage-primary fallback when Postgres isn't configured.
|
|
82
|
+
const storageResult = await drive.createFile(normalizedName, content);
|
|
83
|
+
writeResult = storageResult;
|
|
84
|
+
pgIndex.upsertPage(normalizedName, parsedFrontmatter, content, storageResult.revision, storageResult.modifiedTime, indexOwner).catch(() => { });
|
|
85
|
+
}
|
|
47
86
|
// Fire-and-forget: the link graph is a derived index, and nothing in this
|
|
48
87
|
// response depends on it. Awaiting it put three sequential Supabase round
|
|
49
88
|
// trips (~350-525ms) on the caller's critical path purely to decide whether
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createPage.js","sourceRoot":"","sources":["../../src/tools/createPage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAsB,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AACpF,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC/F,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"createPage.js","sourceRoot":"","sources":["../../src/tools/createPage.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,23 +2,23 @@ import { z } from "zod";
|
|
|
2
2
|
import { type StorageClient } from "../storage/client.js";
|
|
3
3
|
export declare const updatePageInputSchema: z.ZodObject<{
|
|
4
4
|
name: z.ZodString;
|
|
5
|
-
fileId: z.ZodString
|
|
5
|
+
fileId: z.ZodOptional<z.ZodString>;
|
|
6
6
|
revision: z.ZodString;
|
|
7
7
|
body: z.ZodString;
|
|
8
8
|
frontmatterPatch: z.ZodOptional<z.ZodEffects<z.ZodRecord<z.ZodString, z.ZodUnknown>, Record<string, unknown>, unknown>>;
|
|
9
9
|
summary: z.ZodOptional<z.ZodString>;
|
|
10
10
|
}, "strip", z.ZodTypeAny, {
|
|
11
|
-
fileId: string;
|
|
12
11
|
body: string;
|
|
13
12
|
name: string;
|
|
14
13
|
revision: string;
|
|
14
|
+
fileId?: string | undefined;
|
|
15
15
|
summary?: string | undefined;
|
|
16
16
|
frontmatterPatch?: Record<string, unknown> | undefined;
|
|
17
17
|
}, {
|
|
18
|
-
fileId: string;
|
|
19
18
|
body: string;
|
|
20
19
|
name: string;
|
|
21
20
|
revision: string;
|
|
21
|
+
fileId?: string | undefined;
|
|
22
22
|
summary?: string | undefined;
|
|
23
23
|
frontmatterPatch?: unknown;
|
|
24
24
|
}>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"updatePage.d.ts","sourceRoot":"","sources":["../../src/tools/updatePage.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"updatePage.d.ts","sourceRoot":"","sources":["../../src/tools/updatePage.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,KAAK,aAAa,EAA4B,MAAM,sBAAsB,CAAC;AAapF,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;EAiBhC,CAAC;AAEH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE,wBAAsB,UAAU,CAC9B,KAAK,EAAE,aAAa,EACpB,KAAK,EAAE,eAAe,EACtB,MAAM,SAAK,GACV,OAAO,CAAC,MAAM,CAAC,CAiLjB"}
|
package/dist/tools/updatePage.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import crypto from "crypto";
|
|
1
2
|
import { z } from "zod";
|
|
2
3
|
import { resolveIndexOwnerSubject } from "../storage/client.js";
|
|
3
4
|
import { parsePage, serializePage } from "../wiki/frontmatter.js";
|
|
@@ -10,9 +11,13 @@ import { assertPageSize } from "../wiki/storageLimits.js";
|
|
|
10
11
|
import { tryParseJson } from "./coerce.js";
|
|
11
12
|
import { dispatchPageWebhook } from "../webhooks/dispatch.js";
|
|
12
13
|
import { snapshotPage } from "./pageHistory.js";
|
|
14
|
+
import { flushWithRetry } from "../storage/writeBehindRetry.js";
|
|
13
15
|
export const updatePageInputSchema = z.object({
|
|
14
16
|
name: z.string().describe("Relative path of the page to update (e.g. 'projects/my-topic.md')"),
|
|
15
|
-
fileId: z
|
|
17
|
+
fileId: z
|
|
18
|
+
.string()
|
|
19
|
+
.optional()
|
|
20
|
+
.describe("Optional integrity check: when supplied, validated against the file ID on the row you read. Omit if you don't have it."),
|
|
16
21
|
revision: z
|
|
17
22
|
.string()
|
|
18
23
|
.describe("Revision from your last read_page call — used for conflict detection"),
|
|
@@ -46,6 +51,23 @@ export async function updatePage(drive, input, userId = "") {
|
|
|
46
51
|
currentRevision: dbRow.revision,
|
|
47
52
|
};
|
|
48
53
|
}
|
|
54
|
+
// fileId is optional and, when supplied, is validated against the DB row
|
|
55
|
+
// rather than trusted (GH #548). A row with no fileId recorded (Postgres
|
|
56
|
+
// not configured, or no row yet) has nothing to validate against — skip
|
|
57
|
+
// rather than block a legitimate write. A mismatch means the caller read a
|
|
58
|
+
// different file than the one it's about to overwrite; that's exactly the
|
|
59
|
+
// corruption this parameter exists to catch, so refuse with a structured
|
|
60
|
+
// error rather than throwing or silently proceeding.
|
|
61
|
+
if (input.fileId !== undefined && dbRow && dbRow.fileId !== input.fileId) {
|
|
62
|
+
return {
|
|
63
|
+
success: false,
|
|
64
|
+
fileIdMismatch: true,
|
|
65
|
+
name: normalizedName,
|
|
66
|
+
reason: "Supplied fileId does not match the file this page's current DB row points to. Call read_page again and retry with the fileId (or omit it) from the fresh read.",
|
|
67
|
+
yourFileId: input.fileId,
|
|
68
|
+
currentFileId: dbRow.fileId,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
49
71
|
const { frontmatter } = parsePage(existing.content);
|
|
50
72
|
const mergedFrontmatter = {
|
|
51
73
|
...frontmatter,
|
|
@@ -64,16 +86,47 @@ export async function updatePage(drive, input, userId = "") {
|
|
|
64
86
|
// will see afterward (GH #464).
|
|
65
87
|
const { warnings } = parsePage(newContent);
|
|
66
88
|
await snapshotPage(drive, normalizedName, existing.content);
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
89
|
+
let writeResult;
|
|
90
|
+
if (pgIndex.isConfigured()) {
|
|
91
|
+
// DB-primary write (GH #548), matching patch_page's convention: Postgres
|
|
92
|
+
// is what read_page/patch_page actually read from, so it must be the
|
|
93
|
+
// write of record. The revision check above is only an optimistic
|
|
94
|
+
// pre-check with a window for a concurrent writer to land in between —
|
|
95
|
+
// this compare-and-swap is what actually enforces it atomically. No
|
|
96
|
+
// retry-with-reapply loop like patch_page's: input.body is a full
|
|
97
|
+
// replacement the caller already decided on, so a losing CAS is reported
|
|
98
|
+
// as a conflict rather than blindly retried against fresh content.
|
|
99
|
+
const newRevision = crypto.createHash("sha256").update(newContent, "utf8").digest("hex").slice(0, 16);
|
|
100
|
+
const modifiedTime = new Date().toISOString();
|
|
101
|
+
const casApplied = dbRow
|
|
102
|
+
? await pgIndex.casUpdatePage(normalizedName, mergedFrontmatter, newContent, dbRow.revision, newRevision, modifiedTime, indexOwner)
|
|
103
|
+
: await pgIndex.casInsertPage(normalizedName, mergedFrontmatter, newContent, newRevision, modifiedTime, indexOwner);
|
|
104
|
+
if (!casApplied) {
|
|
105
|
+
return {
|
|
106
|
+
success: false,
|
|
107
|
+
conflict: true,
|
|
108
|
+
name: normalizedName,
|
|
109
|
+
reason: "Page was modified by someone else while this update was being applied. Call read_page again to see the current content, then retry with the new revision.",
|
|
110
|
+
yourRevision: input.revision,
|
|
111
|
+
currentRevision: (await pgIndex.fetchPageRow(normalizedName, indexOwner).catch(() => null))?.revision ?? "",
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
writeResult = { fileId: normalizedName, revision: newRevision, modifiedTime };
|
|
115
|
+
// Write-behind: keep the Storage mirror eventually consistent
|
|
116
|
+
// (non-blocking). Retries transient failures with bounded backoff off
|
|
117
|
+
// this synchronous path; on exhaustion it logs a terminal
|
|
118
|
+
// STORAGE_WRITE_BEHIND_FAILED signal instead of letting the mirror
|
|
119
|
+
// silently drift (GH #547). The page already exists in Storage (this is
|
|
120
|
+
// an update, not a create), so updateFile — not createFile — is correct
|
|
121
|
+
// here even on the "no DB row yet" branch above.
|
|
122
|
+
flushWithRetry(() => drive.updateFile(normalizedName, newContent, ""), normalizedName);
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
// Storage-primary fallback when Postgres isn't configured.
|
|
126
|
+
const storageResult = await drive.updateFile(input.fileId ?? normalizedName, newContent, input.revision);
|
|
127
|
+
writeResult = storageResult;
|
|
128
|
+
pgIndex.upsertPage(normalizedName, mergedFrontmatter, newContent, storageResult.revision, storageResult.modifiedTime, indexOwner).catch(() => { });
|
|
129
|
+
}
|
|
77
130
|
// Fire-and-forget: the link graph is a derived index, and nothing in this
|
|
78
131
|
// response depends on it. Awaiting it put three sequential Supabase round
|
|
79
132
|
// trips (~350-525ms) on the caller's critical path purely to decide whether
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"updatePage.js","sourceRoot":"","sources":["../../src/tools/updatePage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAsB,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AACpF,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AACpE,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"updatePage.js","sourceRoot":"","sources":["../../src/tools/updatePage.ts"],"names":[],"mappings":"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"}
|