neotoma 0.5.0 → 0.5.1
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/actions.d.ts.map +1 -1
- package/dist/actions.js +140 -46
- package/dist/actions.js.map +1 -1
- package/dist/cli/index.d.ts +19 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +211 -23
- package/dist/cli/index.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +4 -9
- package/dist/server.js.map +1 -1
- package/dist/services/entity_queries.d.ts +17 -0
- package/dist/services/entity_queries.d.ts.map +1 -1
- package/dist/services/entity_queries.js +28 -0
- package/dist/services/entity_queries.js.map +1 -1
- package/dist/services/recent_conversations.d.ts +31 -0
- package/dist/services/recent_conversations.d.ts.map +1 -0
- package/dist/services/recent_conversations.js +214 -0
- package/dist/services/recent_conversations.js.map +1 -0
- package/dist/services/recent_record_activity.d.ts +26 -0
- package/dist/services/recent_record_activity.d.ts.map +1 -1
- package/dist/services/recent_record_activity.js +145 -7
- package/dist/services/recent_record_activity.js.map +1 -1
- package/dist/shared/action_schemas.d.ts +6 -0
- package/dist/shared/action_schemas.d.ts.map +1 -1
- package/dist/shared/action_schemas.js +15 -6
- package/dist/shared/action_schemas.js.map +1 -1
- package/dist/shared/contract_mappings.d.ts.map +1 -1
- package/dist/shared/contract_mappings.js +14 -2
- package/dist/shared/contract_mappings.js.map +1 -1
- package/dist/shared/openapi_types.d.ts +191 -2
- package/dist/shared/openapi_types.d.ts.map +1 -1
- package/openapi.yaml +300 -0
- package/package.json +1 -1
package/dist/actions.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../src/actions.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../src/actions.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAuF9B,eAAO,MAAM,GAAG,6CAAY,CAAC;AA2M7B,yGAAyG;AACzG,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,GAAG,OAAO,CAK5D;AAomKD,wBAAsB,eAAe;;;eAgCpC"}
|
package/dist/actions.js
CHANGED
|
@@ -33,6 +33,7 @@ import { prepareEntitySnapshotWithEmbedding, upsertEntitySnapshotWithEmbedding,
|
|
|
33
33
|
import { readOpenApiActionsFile, readOpenApiFile } from "./shared/openapi_file.js";
|
|
34
34
|
import { buildSmitheryServerCard } from "./mcp_server_card.js";
|
|
35
35
|
import { listRecentRecordActivity } from "./services/recent_record_activity.js";
|
|
36
|
+
import { listRecentConversations } from "./services/recent_conversations.js";
|
|
36
37
|
export const app = express();
|
|
37
38
|
// Trust proxy headers (required for express-rate-limit when X-Forwarded-For is present)
|
|
38
39
|
app.set("trust proxy", true);
|
|
@@ -1845,30 +1846,99 @@ app.get("/entities/:id/relationships", async (req, res) => {
|
|
|
1845
1846
|
const idsArr = Array.from(relatedIds);
|
|
1846
1847
|
const [snapshotsResult, entitiesResult] = await Promise.all([
|
|
1847
1848
|
db.from("entity_snapshots").select("*").in("entity_id", idsArr).eq("user_id", userId),
|
|
1848
|
-
db
|
|
1849
|
+
db
|
|
1850
|
+
.from("entities")
|
|
1851
|
+
.select("id, canonical_name, entity_type")
|
|
1852
|
+
.in("id", idsArr)
|
|
1853
|
+
.eq("user_id", userId),
|
|
1849
1854
|
]);
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1855
|
+
// Merge snapshot + entity rows so callers always get canonical_name
|
|
1856
|
+
// and entity_type even when the snapshot row is missing.
|
|
1857
|
+
const canonicalNames = new Map();
|
|
1858
|
+
const entityTypes = new Map();
|
|
1859
|
+
if (!entitiesResult.error && entitiesResult.data) {
|
|
1860
|
+
for (const ent of entitiesResult.data) {
|
|
1861
|
+
if (ent.canonical_name)
|
|
1862
|
+
canonicalNames.set(ent.id, ent.canonical_name);
|
|
1863
|
+
if (ent.entity_type)
|
|
1864
|
+
entityTypes.set(ent.id, ent.entity_type);
|
|
1857
1865
|
}
|
|
1858
|
-
|
|
1866
|
+
}
|
|
1867
|
+
relatedEntities = {};
|
|
1868
|
+
if (!snapshotsResult.error && snapshotsResult.data) {
|
|
1859
1869
|
for (const e of snapshotsResult.data) {
|
|
1860
1870
|
if (!e.canonical_name && canonicalNames.has(e.entity_id)) {
|
|
1861
1871
|
e.canonical_name = canonicalNames.get(e.entity_id);
|
|
1862
1872
|
}
|
|
1873
|
+
if (!e.entity_type && entityTypes.has(e.entity_id)) {
|
|
1874
|
+
e.entity_type = entityTypes.get(e.entity_id);
|
|
1875
|
+
}
|
|
1863
1876
|
relatedEntities[e.entity_id] = e;
|
|
1864
1877
|
}
|
|
1865
1878
|
}
|
|
1879
|
+
// Fill in entities that have no snapshot row yet.
|
|
1880
|
+
for (const rid of idsArr) {
|
|
1881
|
+
if (relatedEntities[rid])
|
|
1882
|
+
continue;
|
|
1883
|
+
relatedEntities[rid] = {
|
|
1884
|
+
entity_id: rid,
|
|
1885
|
+
canonical_name: canonicalNames.get(rid) ?? null,
|
|
1886
|
+
entity_type: entityTypes.get(rid) ?? null,
|
|
1887
|
+
};
|
|
1888
|
+
}
|
|
1889
|
+
// Resolve schema-derived entity_type_label via the registry once per
|
|
1890
|
+
// distinct type (best effort; non-fatal).
|
|
1891
|
+
try {
|
|
1892
|
+
const { SchemaRegistryService } = await import("./services/schema_registry.js");
|
|
1893
|
+
const registry = new SchemaRegistryService();
|
|
1894
|
+
const distinctTypes = [...new Set(Array.from(entityTypes.values()).filter(Boolean))];
|
|
1895
|
+
const labelByType = new Map();
|
|
1896
|
+
for (const t of distinctTypes) {
|
|
1897
|
+
const schema = await registry.loadActiveSchema(t, userId);
|
|
1898
|
+
if (schema?.metadata?.label)
|
|
1899
|
+
labelByType.set(t, schema.metadata.label);
|
|
1900
|
+
}
|
|
1901
|
+
for (const rid of Object.keys(relatedEntities)) {
|
|
1902
|
+
const type = relatedEntities[rid]?.entity_type;
|
|
1903
|
+
if (type && labelByType.has(type)) {
|
|
1904
|
+
relatedEntities[rid].entity_type_label = labelByType.get(type);
|
|
1905
|
+
}
|
|
1906
|
+
}
|
|
1907
|
+
}
|
|
1908
|
+
catch (err) {
|
|
1909
|
+
// Ignore — expansions without labels are still useful.
|
|
1910
|
+
console.warn("Failed to attach entity_type_label to related entities:", err instanceof Error ? err.message : err);
|
|
1911
|
+
}
|
|
1866
1912
|
}
|
|
1867
1913
|
}
|
|
1914
|
+
// Decorate relationship rows with top-level convenience fields so clients
|
|
1915
|
+
// don't have to look up `related_entities` per row. Only populated when
|
|
1916
|
+
// `expand_entities=true`.
|
|
1917
|
+
const decorateRelationship = (rel) => {
|
|
1918
|
+
if (!relatedEntities)
|
|
1919
|
+
return rel;
|
|
1920
|
+
const src = relatedEntities[rel.source_entity_id];
|
|
1921
|
+
const tgt = relatedEntities[rel.target_entity_id];
|
|
1922
|
+
return {
|
|
1923
|
+
...rel,
|
|
1924
|
+
source_entity_name: src?.canonical_name ?? null,
|
|
1925
|
+
source_entity_type: src?.entity_type ?? null,
|
|
1926
|
+
source_entity_type_label: src?.entity_type_label ?? null,
|
|
1927
|
+
target_entity_name: tgt?.canonical_name ?? null,
|
|
1928
|
+
target_entity_type: tgt?.entity_type ?? null,
|
|
1929
|
+
target_entity_type_label: tgt?.entity_type_label ?? null,
|
|
1930
|
+
};
|
|
1931
|
+
};
|
|
1932
|
+
const decoratedOutgoing = expandEntities
|
|
1933
|
+
? formattedOutgoing.map(decorateRelationship)
|
|
1934
|
+
: formattedOutgoing;
|
|
1935
|
+
const decoratedIncoming = expandEntities
|
|
1936
|
+
? formattedIncoming.map(decorateRelationship)
|
|
1937
|
+
: formattedIncoming;
|
|
1868
1938
|
const responseBody = {
|
|
1869
|
-
outgoing:
|
|
1870
|
-
incoming:
|
|
1871
|
-
relationships: [...
|
|
1939
|
+
outgoing: decoratedOutgoing,
|
|
1940
|
+
incoming: decoratedIncoming,
|
|
1941
|
+
relationships: [...decoratedOutgoing, ...decoratedIncoming],
|
|
1872
1942
|
};
|
|
1873
1943
|
if (relatedEntities) {
|
|
1874
1944
|
responseBody.related_entities = relatedEntities;
|
|
@@ -2126,8 +2196,8 @@ app.post("/relationships/snapshot", async (req, res) => {
|
|
|
2126
2196
|
return sendValidationError(res, parsed.error.issues);
|
|
2127
2197
|
}
|
|
2128
2198
|
try {
|
|
2129
|
-
const
|
|
2130
|
-
const
|
|
2199
|
+
const { relationship_type, source_entity_id, target_entity_id, user_id } = parsed.data;
|
|
2200
|
+
const userId = await getAuthenticatedUserId(req, user_id);
|
|
2131
2201
|
const relationshipKey = `${relationship_type}:${source_entity_id}:${target_entity_id}`;
|
|
2132
2202
|
const { data: snapshot, error: snapshotError } = await db
|
|
2133
2203
|
.from("relationship_snapshots")
|
|
@@ -2236,9 +2306,7 @@ app.get("/timeline", async (req, res) => {
|
|
|
2236
2306
|
}
|
|
2237
2307
|
// Enrich events with entity canonical names and types
|
|
2238
2308
|
const events = data || [];
|
|
2239
|
-
const entityIds = [
|
|
2240
|
-
...new Set(events.map((e) => e.entity_id).filter(Boolean)),
|
|
2241
|
-
];
|
|
2309
|
+
const entityIds = [...new Set(events.map((e) => e.entity_id).filter(Boolean))];
|
|
2242
2310
|
const entityLookup = new Map();
|
|
2243
2311
|
if (entityIds.length > 0) {
|
|
2244
2312
|
const { data: entities } = await db
|
|
@@ -2341,6 +2409,19 @@ app.get("/record_activity", async (req, res) => {
|
|
|
2341
2409
|
return handleApiError(req, res, error, "Failed to list recent record activity", "DB_QUERY_FAILED", "APIError:record_activity");
|
|
2342
2410
|
}
|
|
2343
2411
|
});
|
|
2412
|
+
// GET /recent_conversations — Inspector: conversations with nested messages (SQLite)
|
|
2413
|
+
app.get("/recent_conversations", async (req, res) => {
|
|
2414
|
+
try {
|
|
2415
|
+
const userId = await getAuthenticatedUserId(req, req.query.user_id);
|
|
2416
|
+
const limit = parseInt(String(req.query.limit ?? "25"), 10) || 25;
|
|
2417
|
+
const offset = parseInt(String(req.query.offset ?? "0"), 10) || 0;
|
|
2418
|
+
const result = listRecentConversations(userId, limit, offset);
|
|
2419
|
+
return res.json(result);
|
|
2420
|
+
}
|
|
2421
|
+
catch (error) {
|
|
2422
|
+
return handleApiError(req, res, error, "Failed to list recent conversations", "DB_QUERY_FAILED", "APIError:recent_conversations");
|
|
2423
|
+
}
|
|
2424
|
+
});
|
|
2344
2425
|
// GET /api/sources - Get source list (FU-301)
|
|
2345
2426
|
app.get("/sources", async (req, res) => {
|
|
2346
2427
|
try {
|
|
@@ -2660,7 +2741,7 @@ async function storeStructuredForApi(params) {
|
|
|
2660
2741
|
const { userId, entities, sourcePriority, idempotencyKey, originalFilename, relationships, commit: commitInput, strict: strictInput, } = params;
|
|
2661
2742
|
const commit = commitInput !== false;
|
|
2662
2743
|
const strict = strictInput === true;
|
|
2663
|
-
const { resolveEntityWithTrace, CanonicalNameUnresolvedError, MergeRefusedError
|
|
2744
|
+
const { resolveEntityWithTrace, CanonicalNameUnresolvedError, MergeRefusedError } = await import("./services/entity_resolution.js");
|
|
2664
2745
|
const { detectFlatPackedRows, FlatPackedRowsError } = await import("./services/flat_packed_detection.js");
|
|
2665
2746
|
// Reject flat-packed rows (whole tables smuggled into a single entity as
|
|
2666
2747
|
// `<prefix>_<index>_<suffix>` keys). These cannot produce per-row snapshots
|
|
@@ -2689,10 +2770,15 @@ async function storeStructuredForApi(params) {
|
|
|
2689
2770
|
entity_type: obs.entity_type,
|
|
2690
2771
|
observation_id: obs.id,
|
|
2691
2772
|
}));
|
|
2773
|
+
// Idempotency replay: the source row already exists for
|
|
2774
|
+
// (user_id, idempotency_key). No new observations or entities were
|
|
2775
|
+
// written. Callers distinguish this from a zero-commit fresh write via the
|
|
2776
|
+
// `replayed: true` flag (v0.5.1+). See docs/architecture/idempotence_pattern.md.
|
|
2692
2777
|
return {
|
|
2693
2778
|
success: true,
|
|
2779
|
+
replayed: true,
|
|
2694
2780
|
source_id: existingSource.id,
|
|
2695
|
-
entities_created:
|
|
2781
|
+
entities_created: 0,
|
|
2696
2782
|
observations_created: existingEntities.length,
|
|
2697
2783
|
entities: existingEntities,
|
|
2698
2784
|
};
|
|
@@ -2717,6 +2803,24 @@ async function storeStructuredForApi(params) {
|
|
|
2717
2803
|
source_priority: sourcePriority,
|
|
2718
2804
|
},
|
|
2719
2805
|
});
|
|
2806
|
+
// v0.5.1: structured guidance for the v0.5.0 breaking change where callers
|
|
2807
|
+
// nested fields under `attributes`. If resolution failed and the only
|
|
2808
|
+
// observed top-level keys are `attributes` (plus optionally `entity_type`),
|
|
2809
|
+
// surface a hint pointing callers at the flat-payload convention.
|
|
2810
|
+
function buildAttributesHint(seenFields) {
|
|
2811
|
+
if (!Array.isArray(seenFields))
|
|
2812
|
+
return undefined;
|
|
2813
|
+
const keys = seenFields.filter((k) => typeof k === "string");
|
|
2814
|
+
if (keys.length === 0)
|
|
2815
|
+
return undefined;
|
|
2816
|
+
const nonMetaKeys = keys.filter((k) => k !== "entity_type");
|
|
2817
|
+
if (nonMetaKeys.length === 1 && nonMetaKeys[0] === "attributes") {
|
|
2818
|
+
return ("Payload nests fields under `attributes`. Since v0.5.0, /store expects " +
|
|
2819
|
+
"fields at the top level of each entity object (e.g. `{ entity_type, " +
|
|
2820
|
+
"title, canonical_name, ... }`). Move keys out of `attributes` and retry.");
|
|
2821
|
+
}
|
|
2822
|
+
return undefined;
|
|
2823
|
+
}
|
|
2720
2824
|
const resolved = [];
|
|
2721
2825
|
const issues = [];
|
|
2722
2826
|
for (let observation_index = 0; observation_index < entities.length; observation_index++) {
|
|
@@ -2784,6 +2888,7 @@ async function storeStructuredForApi(params) {
|
|
|
2784
2888
|
}
|
|
2785
2889
|
catch (err) {
|
|
2786
2890
|
if (err instanceof CanonicalNameUnresolvedError) {
|
|
2891
|
+
const hint = buildAttributesHint(err.seenFields);
|
|
2787
2892
|
issues.push({
|
|
2788
2893
|
observation_index,
|
|
2789
2894
|
entity_type,
|
|
@@ -2793,6 +2898,7 @@ async function storeStructuredForApi(params) {
|
|
|
2793
2898
|
seen_fields: err.seenFields,
|
|
2794
2899
|
attempted_value: err.attemptedValue,
|
|
2795
2900
|
},
|
|
2901
|
+
...(hint ? { hint } : {}),
|
|
2796
2902
|
});
|
|
2797
2903
|
}
|
|
2798
2904
|
else if (err instanceof MergeRefusedError) {
|
|
@@ -2843,8 +2949,7 @@ async function storeStructuredForApi(params) {
|
|
|
2843
2949
|
const { recomputeSnapshot } = await import("./services/snapshot_computation.js");
|
|
2844
2950
|
const snap = await recomputeSnapshot(r.entity_id, userId);
|
|
2845
2951
|
snapshotAfter =
|
|
2846
|
-
snap
|
|
2847
|
-
?.snapshot ?? null;
|
|
2952
|
+
snap?.snapshot ?? null;
|
|
2848
2953
|
}
|
|
2849
2954
|
catch (snapshotErr) {
|
|
2850
2955
|
logger.warn(`Snapshot recompute failed for ${r.entity_id}: ${snapshotErr}`);
|
|
@@ -2920,11 +3025,11 @@ async function storeStructuredForApi(params) {
|
|
|
2920
3025
|
}
|
|
2921
3026
|
return {
|
|
2922
3027
|
success: true,
|
|
3028
|
+
replayed: false,
|
|
2923
3029
|
commit,
|
|
2924
3030
|
source_id: commit ? storageResult.sourceId : null,
|
|
2925
3031
|
entities_created: commit
|
|
2926
|
-
? createdEntities.filter((e) => e.action === "created" || e.action === "extended")
|
|
2927
|
-
.length
|
|
3032
|
+
? createdEntities.filter((e) => e.action === "created" || e.action === "extended").length
|
|
2928
3033
|
: 0,
|
|
2929
3034
|
observations_created: commit ? createdEntities.length : 0,
|
|
2930
3035
|
entities: createdEntities,
|
|
@@ -2932,7 +3037,7 @@ async function storeStructuredForApi(params) {
|
|
|
2932
3037
|
};
|
|
2933
3038
|
}
|
|
2934
3039
|
async function storeUnstructuredForApi(params) {
|
|
2935
|
-
const { fileContent, fileBuffer, mimeType, idempotencyKey, originalFilename, userId
|
|
3040
|
+
const { fileContent, fileBuffer, mimeType, idempotencyKey, originalFilename, userId } = params;
|
|
2936
3041
|
const resolvedFileBuffer = fileBuffer ?? (fileContent !== undefined ? Buffer.from(fileContent, "base64") : undefined);
|
|
2937
3042
|
if (!resolvedFileBuffer) {
|
|
2938
3043
|
throw new Error("fileContent or fileBuffer is required for unstructured storage");
|
|
@@ -3012,7 +3117,8 @@ app.post("/store", async (req, res) => {
|
|
|
3012
3117
|
fileContent,
|
|
3013
3118
|
fileBuffer: resolvedFileBuffer,
|
|
3014
3119
|
mimeType,
|
|
3015
|
-
idempotencyKey: parsed.data.file_idempotency_key ??
|
|
3120
|
+
idempotencyKey: parsed.data.file_idempotency_key ??
|
|
3121
|
+
(!hasEntities ? parsed.data.idempotency_key : undefined),
|
|
3016
3122
|
originalFilename,
|
|
3017
3123
|
});
|
|
3018
3124
|
}
|
|
@@ -3031,18 +3137,13 @@ app.post("/store", async (req, res) => {
|
|
|
3031
3137
|
if (error instanceof Error && error.message.includes("Not authenticated")) {
|
|
3032
3138
|
return sendError(res, 401, "AUTH_REQUIRED", error.message);
|
|
3033
3139
|
}
|
|
3034
|
-
const errCode = error && typeof error === "object"
|
|
3035
|
-
|
|
3036
|
-
: undefined;
|
|
3037
|
-
if (errCode === "ERR_FORBIDDEN_ENTITY_TYPE" ||
|
|
3038
|
-
errCode === "ERR_PLURAL_ENTITY_TYPE") {
|
|
3140
|
+
const errCode = error && typeof error === "object" ? error.code : undefined;
|
|
3141
|
+
if (errCode === "ERR_FORBIDDEN_ENTITY_TYPE" || errCode === "ERR_PLURAL_ENTITY_TYPE") {
|
|
3039
3142
|
const message = error instanceof Error ? error.message : String(error);
|
|
3040
3143
|
logWarn("EntityTypeGuardError:store", req, { code: errCode, message });
|
|
3041
3144
|
return sendError(res, 400, errCode, message);
|
|
3042
3145
|
}
|
|
3043
|
-
if (error &&
|
|
3044
|
-
typeof error === "object" &&
|
|
3045
|
-
errCode === "ERR_STORE_RESOLUTION_FAILED") {
|
|
3146
|
+
if (error && typeof error === "object" && errCode === "ERR_STORE_RESOLUTION_FAILED") {
|
|
3046
3147
|
const err = error;
|
|
3047
3148
|
logWarn("StoreResolutionError:store", req, {
|
|
3048
3149
|
issue_count: err.issues?.length ?? 0,
|
|
@@ -3055,9 +3156,7 @@ app.post("/store", async (req, res) => {
|
|
|
3055
3156
|
},
|
|
3056
3157
|
});
|
|
3057
3158
|
}
|
|
3058
|
-
if (error &&
|
|
3059
|
-
typeof error === "object" &&
|
|
3060
|
-
errCode === "ERR_FLAT_PACKED_ROWS") {
|
|
3159
|
+
if (error && typeof error === "object" && errCode === "ERR_FLAT_PACKED_ROWS") {
|
|
3061
3160
|
const err = error;
|
|
3062
3161
|
logWarn("FlatPackedRowsError:store", req, {
|
|
3063
3162
|
prefix: err.detection.prefix,
|
|
@@ -3280,10 +3379,7 @@ app.post("/list_observations", async (req, res) => {
|
|
|
3280
3379
|
return sendValidationError(res, parsed.error.issues);
|
|
3281
3380
|
}
|
|
3282
3381
|
const { entity_id, limit = 100, offset = 0, updated_since, created_since } = parsed.data;
|
|
3283
|
-
let query = db
|
|
3284
|
-
.from("observations")
|
|
3285
|
-
.select("*")
|
|
3286
|
-
.eq("entity_id", entity_id);
|
|
3382
|
+
let query = db.from("observations").select("*").eq("entity_id", entity_id);
|
|
3287
3383
|
if (updated_since) {
|
|
3288
3384
|
query = query.gte("observed_at", updated_since);
|
|
3289
3385
|
}
|
|
@@ -3366,10 +3462,10 @@ app.post("/create_relationship", async (req, res) => {
|
|
|
3366
3462
|
});
|
|
3367
3463
|
return sendValidationError(res, parsed.error.issues);
|
|
3368
3464
|
}
|
|
3369
|
-
const { relationship_type, source_entity_id, target_entity_id, source_id, metadata } = parsed.data;
|
|
3370
|
-
const userId = "00000000-0000-0000-0000-000000000000"; // v0.1.0 single-user
|
|
3465
|
+
const { relationship_type, source_entity_id, target_entity_id, source_id, metadata, user_id } = parsed.data;
|
|
3371
3466
|
const { relationshipsService } = await import("./services/relationships.js");
|
|
3372
3467
|
try {
|
|
3468
|
+
const userId = await getAuthenticatedUserId(req, user_id);
|
|
3373
3469
|
const relationship = await relationshipsService.createRelationship({
|
|
3374
3470
|
relationship_type,
|
|
3375
3471
|
source_entity_id,
|
|
@@ -3889,8 +3985,7 @@ app.post("/update_schema_incremental", async (req, res) => {
|
|
|
3889
3985
|
}
|
|
3890
3986
|
catch (err) {
|
|
3891
3987
|
const code = err?.code;
|
|
3892
|
-
if (code === "ERR_FORBIDDEN_ENTITY_TYPE" ||
|
|
3893
|
-
code === "ERR_PLURAL_ENTITY_TYPE") {
|
|
3988
|
+
if (code === "ERR_FORBIDDEN_ENTITY_TYPE" || code === "ERR_PLURAL_ENTITY_TYPE") {
|
|
3894
3989
|
return sendError(res, 400, code, err.message);
|
|
3895
3990
|
}
|
|
3896
3991
|
throw err;
|
|
@@ -3963,8 +4058,7 @@ app.post("/register_schema", async (req, res) => {
|
|
|
3963
4058
|
const code = err?.code;
|
|
3964
4059
|
const message = err instanceof Error ? err.message : String(err);
|
|
3965
4060
|
logWarn("ValidationError:register_schema", req, { error: message });
|
|
3966
|
-
if (code === "ERR_FORBIDDEN_ENTITY_TYPE" ||
|
|
3967
|
-
code === "ERR_PLURAL_ENTITY_TYPE") {
|
|
4061
|
+
if (code === "ERR_FORBIDDEN_ENTITY_TYPE" || code === "ERR_PLURAL_ENTITY_TYPE") {
|
|
3968
4062
|
return sendError(res, 400, code, message);
|
|
3969
4063
|
}
|
|
3970
4064
|
return sendError(res, 400, "SCHEMA_VALIDATION_FAILED", message);
|