@coffer-org/server 1.7.1 → 1.8.0
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/auth-api.js +3 -3
- package/dist/background-scheduler.d.ts +22 -0
- package/dist/background-scheduler.js +101 -0
- package/dist/collection-io.d.ts +7 -7
- package/dist/collection-io.js +2 -2
- package/dist/connector-identity.d.ts +5 -0
- package/dist/connector-identity.js +32 -0
- package/dist/embed-openai.d.ts +10 -0
- package/dist/embed-openai.js +28 -0
- package/dist/entity-schema.d.ts +6 -5
- package/dist/entity-schema.js +24 -10
- package/dist/extend-io.d.ts +5 -3
- package/dist/extend-io.js +17 -10
- package/dist/extend-table.d.ts +4 -3
- package/dist/extend-table.js +10 -18
- package/dist/field-masking.d.ts +7 -0
- package/dist/field-masking.js +60 -0
- package/dist/index-signal.d.ts +3 -0
- package/dist/index-signal.js +14 -0
- package/dist/index.js +59 -159
- package/dist/local-api.d.ts +3 -3
- package/dist/local-api.js +6 -6
- package/dist/mcp-http.d.ts +5 -0
- package/dist/mcp-http.js +37 -0
- package/dist/mcp-http.test-helpers.d.ts +17 -0
- package/dist/mcp-http.test-helpers.js +117 -0
- package/dist/mcp-local.d.ts +10 -0
- package/dist/mcp-local.js +57 -0
- package/dist/mcp-tools.d.ts +61 -0
- package/dist/mcp-tools.js +225 -0
- package/dist/msg-log.d.ts +0 -1
- package/dist/msg-log.js +2 -2
- package/dist/mutate.d.ts +7 -5
- package/dist/mutate.js +20 -8
- package/dist/plugin-hooks.d.ts +10 -0
- package/dist/plugin-hooks.js +8 -0
- package/dist/plugin-runtime.d.ts +1 -0
- package/dist/plugin-runtime.js +30 -6
- package/dist/plugins-api.d.ts +1 -1
- package/dist/plugins-api.js +21 -11
- package/dist/records-api.d.ts +8 -8
- package/dist/records-api.js +35 -32
- package/dist/registry-context.d.ts +1 -1
- package/dist/registry-context.js +2 -2
- package/dist/schema-api.js +5 -5
- package/dist/settings-write.d.ts +19 -0
- package/dist/settings-write.js +60 -0
- package/dist/temporal.d.ts +3 -3
- package/dist/temporal.js +1 -1
- package/dist/thread-store.d.ts +20 -0
- package/dist/thread-store.js +27 -0
- package/dist/uploads.d.ts +1 -0
- package/dist/uploads.js +4 -0
- package/package.json +6 -6
package/dist/auth-api.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { field } from '@coffer-org/sdk/fields';
|
|
2
2
|
import { countUsers, createUser, findUserByLogin, findUserById, getPasswordHash, listUsers, updateUser, deleteUser, countAdmins, createSession, resolveSession, deleteSession, createApiToken, resolveApiToken, listApiTokens, revokeApiToken, } from "./auth-store.js";
|
|
3
3
|
import { verifyPassword } from "./auth-crypto.js";
|
|
4
|
-
import { maskSecrets, preserveSecrets } from "./
|
|
4
|
+
import { maskSecrets, preserveSecrets } from "./field-masking.js";
|
|
5
5
|
import { rowMatch } from "./records-api.js";
|
|
6
6
|
import { tokenize } from '@coffer-org/core/search';
|
|
7
|
-
import {
|
|
7
|
+
import { USERS_SHELF } from '@coffer-org/sdk/users-shelf';
|
|
8
8
|
const PASSWORD_FIELDS = [field.password({ key: 'password' })];
|
|
9
9
|
const SESSION_TTL_MS = 30 * 24 * 60 * 60 * 1000;
|
|
10
10
|
const COOKIE_NAME = 'sid';
|
|
@@ -108,7 +108,7 @@ export async function registerAuthApi(app) {
|
|
|
108
108
|
const tokens = q ? tokenize(q) : [];
|
|
109
109
|
let users = await listUsers();
|
|
110
110
|
if (tokens.length > 0) {
|
|
111
|
-
users = users.filter((u) => rowMatch(
|
|
111
|
+
users = users.filter((u) => rowMatch(USERS_SHELF, u, tokens) !== null);
|
|
112
112
|
}
|
|
113
113
|
return users.map(withMaskedPassword);
|
|
114
114
|
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export type TimerHandle = unknown;
|
|
2
|
+
export interface BackgroundTask {
|
|
3
|
+
name: string;
|
|
4
|
+
intervalMs: number;
|
|
5
|
+
run: () => Promise<void>;
|
|
6
|
+
timeoutMs?: number;
|
|
7
|
+
}
|
|
8
|
+
export interface SchedulerOpts {
|
|
9
|
+
startupDelayMs?: number;
|
|
10
|
+
gapMs?: number;
|
|
11
|
+
taskTimeoutMs?: number;
|
|
12
|
+
setTimer?: (cb: () => void, ms: number) => TimerHandle;
|
|
13
|
+
clearTimer?: (h: TimerHandle) => void;
|
|
14
|
+
}
|
|
15
|
+
export interface BackgroundScheduler {
|
|
16
|
+
register(task: BackgroundTask): void;
|
|
17
|
+
start(): void;
|
|
18
|
+
stop(): void;
|
|
19
|
+
}
|
|
20
|
+
export declare function makeScheduler(opts?: SchedulerOpts): BackgroundScheduler;
|
|
21
|
+
export declare function startScheduler(tasks: BackgroundTask[], opts?: SchedulerOpts): void;
|
|
22
|
+
export declare function stopScheduler(): void;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { getLogger } from '@coffer-org/sdk/logger';
|
|
2
|
+
const log = getLogger('scheduler');
|
|
3
|
+
const defaultSetTimer = (cb, ms) => {
|
|
4
|
+
const t = setTimeout(cb, ms);
|
|
5
|
+
if (typeof t.unref === 'function')
|
|
6
|
+
t.unref();
|
|
7
|
+
return t;
|
|
8
|
+
};
|
|
9
|
+
export function makeScheduler(opts = {}) {
|
|
10
|
+
const startupDelayMs = opts.startupDelayMs ?? (Number(process.env['BG_STARTUP_DELAY_MS']) || 600_000);
|
|
11
|
+
const gapMs = opts.gapMs ?? (Number(process.env['BG_GAP_MS']) || 30_000);
|
|
12
|
+
const defaultTimeoutMs = opts.taskTimeoutMs ?? (Number(process.env['BG_TASK_TIMEOUT_MS']) || 600_000);
|
|
13
|
+
const setTimer = opts.setTimer ?? defaultSetTimer;
|
|
14
|
+
const clearTimer = opts.clearTimer ?? ((h) => clearTimeout(h));
|
|
15
|
+
const tasks = [];
|
|
16
|
+
const queue = [];
|
|
17
|
+
const queued = new Set();
|
|
18
|
+
const timers = new Set();
|
|
19
|
+
let running = false;
|
|
20
|
+
let stopped = false;
|
|
21
|
+
let started = false;
|
|
22
|
+
function arm(cb, ms) {
|
|
23
|
+
if (stopped)
|
|
24
|
+
return;
|
|
25
|
+
const h = setTimer(() => { timers.delete(h); cb(); }, ms);
|
|
26
|
+
timers.add(h);
|
|
27
|
+
}
|
|
28
|
+
function enqueue(task) {
|
|
29
|
+
if (stopped || queued.has(task.name))
|
|
30
|
+
return;
|
|
31
|
+
queued.add(task.name);
|
|
32
|
+
queue.push(task);
|
|
33
|
+
pump();
|
|
34
|
+
}
|
|
35
|
+
function pump() {
|
|
36
|
+
if (stopped || running)
|
|
37
|
+
return;
|
|
38
|
+
const task = queue.shift();
|
|
39
|
+
if (!task)
|
|
40
|
+
return;
|
|
41
|
+
queued.delete(task.name);
|
|
42
|
+
running = true;
|
|
43
|
+
void runOne(task).finally(() => {
|
|
44
|
+
running = false;
|
|
45
|
+
arm(() => enqueue(task), task.intervalMs);
|
|
46
|
+
if (queue.length)
|
|
47
|
+
arm(pump, gapMs);
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
async function runOne(task) {
|
|
51
|
+
const timeoutMs = task.timeoutMs ?? defaultTimeoutMs;
|
|
52
|
+
let to;
|
|
53
|
+
const timeout = new Promise((resolve) => {
|
|
54
|
+
to = setTimer(() => { timers.delete(to); log.warn(`${task.name}: timed out after ${timeoutMs}ms`); resolve(); }, timeoutMs);
|
|
55
|
+
timers.add(to);
|
|
56
|
+
});
|
|
57
|
+
try {
|
|
58
|
+
let runPromise;
|
|
59
|
+
try {
|
|
60
|
+
runPromise = task.run();
|
|
61
|
+
}
|
|
62
|
+
catch (e) {
|
|
63
|
+
runPromise = Promise.reject(e);
|
|
64
|
+
}
|
|
65
|
+
await Promise.race([runPromise.catch((e) => log.warn(`${task.name}: ${e.message}`)), timeout]);
|
|
66
|
+
}
|
|
67
|
+
finally {
|
|
68
|
+
clearTimer(to);
|
|
69
|
+
timers.delete(to);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
register(task) { tasks.push(task); },
|
|
74
|
+
start() {
|
|
75
|
+
if (started || stopped)
|
|
76
|
+
return;
|
|
77
|
+
started = true;
|
|
78
|
+
arm(() => { for (const t of tasks)
|
|
79
|
+
enqueue(t); }, startupDelayMs);
|
|
80
|
+
},
|
|
81
|
+
stop() {
|
|
82
|
+
stopped = true;
|
|
83
|
+
for (const h of timers)
|
|
84
|
+
clearTimer(h);
|
|
85
|
+
timers.clear();
|
|
86
|
+
queue.length = 0;
|
|
87
|
+
queued.clear();
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
let current;
|
|
92
|
+
export function startScheduler(tasks, opts = {}) {
|
|
93
|
+
current = makeScheduler(opts);
|
|
94
|
+
for (const t of tasks)
|
|
95
|
+
current.register(t);
|
|
96
|
+
current.start();
|
|
97
|
+
}
|
|
98
|
+
export function stopScheduler() {
|
|
99
|
+
current?.stop();
|
|
100
|
+
current = undefined;
|
|
101
|
+
}
|
package/dist/collection-io.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { EntityManager } from '@mikro-orm/core';
|
|
2
|
-
import type {
|
|
2
|
+
import type { ShelfDef } from '@coffer-org/sdk/shelf';
|
|
3
3
|
import type { LayoutEl } from '@coffer-org/sdk/fields';
|
|
4
4
|
type Row = Record<string, unknown>;
|
|
5
5
|
export declare function scalarKeys(fields: LayoutEl[]): string[];
|
|
@@ -10,15 +10,15 @@ export declare function splitAt(fields: LayoutEl[], input: Row): {
|
|
|
10
10
|
export declare function writeAt(tx: EntityManager, prefix: string, fields: LayoutEl[], parentId: number, collections: Record<string, Row[]>): Promise<void>;
|
|
11
11
|
export declare function readAt(em: EntityManager, prefix: string, fields: LayoutEl[], parentId: number): Promise<Record<string, Row[]>>;
|
|
12
12
|
export declare function deleteAt(tx: EntityManager, prefix: string, fields: LayoutEl[], parentId: number): Promise<void>;
|
|
13
|
-
export declare const splitCollections: (m:
|
|
13
|
+
export declare const splitCollections: (m: ShelfDef, input: Row) => {
|
|
14
14
|
base: Row;
|
|
15
15
|
collections: Record<string, Row[]>;
|
|
16
16
|
};
|
|
17
|
-
export declare const writeCollections: (tx: EntityManager, m:
|
|
18
|
-
export declare const readCollections: (em: EntityManager, m:
|
|
19
|
-
export declare const deleteCollections: (tx: EntityManager, m:
|
|
17
|
+
export declare const writeCollections: (tx: EntityManager, m: ShelfDef, parentId: number, collections: Record<string, Row[]>) => Promise<void>;
|
|
18
|
+
export declare const readCollections: (em: EntityManager, m: ShelfDef, parentId: number) => Promise<Record<string, Row[]>>;
|
|
19
|
+
export declare const deleteCollections: (tx: EntityManager, m: ShelfDef, parentId: number) => Promise<void>;
|
|
20
20
|
export declare function flattenEmbeddedAt(fields: LayoutEl[], input: Row): Row;
|
|
21
21
|
export declare function nestEmbeddedAt(fields: LayoutEl[], row: Row): Row;
|
|
22
|
-
export declare const flattenEmbedded: (m:
|
|
23
|
-
export declare const nestEmbedded: (m:
|
|
22
|
+
export declare const flattenEmbedded: (m: ShelfDef, input: Row) => Row;
|
|
23
|
+
export declare const nestEmbedded: (m: ShelfDef, row: Row) => Row;
|
|
24
24
|
export {};
|
package/dist/collection-io.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { serialize } from '@mikro-orm/core';
|
|
2
|
-
import { collectionGroups, fieldEntries, storageColumns } from '@coffer-org/sdk/
|
|
2
|
+
import { collectionGroups, fieldEntries, storageColumns } from '@coffer-org/sdk/shelf';
|
|
3
3
|
import { isGroup, isEmbeddedGroup, isJsonStored } from '@coffer-org/sdk/fields';
|
|
4
4
|
function flattenLayout(fields) {
|
|
5
5
|
const out = [];
|
|
@@ -88,7 +88,7 @@ export async function deleteAt(tx, prefix, fields, parentId) {
|
|
|
88
88
|
await tx.nativeDelete(table, { parent_id: parentId });
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
|
-
const modPrefix = (m) => `${m.
|
|
91
|
+
const modPrefix = (m) => `${m.library}__${m.shelf}`;
|
|
92
92
|
export const splitCollections = (m, input) => splitAt(m.fields, input);
|
|
93
93
|
export const writeCollections = (tx, m, parentId, collections) => writeAt(tx, modPrefix(m), m.fields, parentId, collections);
|
|
94
94
|
export const readCollections = (em, m, parentId) => readAt(em, modPrefix(m), m.fields, parentId);
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { AuthUser } from './auth-store.ts';
|
|
2
|
+
export declare function linkConnectorIdentity(connector: string, connectorUserId: string, userId: number): Promise<void>;
|
|
3
|
+
export declare function resolveConnectorUser(connector: string, connectorUserId: string): Promise<AuthUser | null>;
|
|
4
|
+
export declare function firstAdmin(): Promise<AuthUser | null>;
|
|
5
|
+
export declare function linkToFirstAdmin(connector: string, connectorUserId: string): Promise<void>;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { getEm } from "./db.js";
|
|
2
|
+
import { findUserById, listUsers } from "./auth-store.js";
|
|
3
|
+
export async function linkConnectorIdentity(connector, connectorUserId, userId) {
|
|
4
|
+
const em = getEm().fork();
|
|
5
|
+
await em.upsert('_ConnectorIdentity', {
|
|
6
|
+
connector,
|
|
7
|
+
connector_user_id: connectorUserId,
|
|
8
|
+
user_id: userId,
|
|
9
|
+
});
|
|
10
|
+
await em.flush();
|
|
11
|
+
}
|
|
12
|
+
export async function resolveConnectorUser(connector, connectorUserId) {
|
|
13
|
+
const em = getEm().fork();
|
|
14
|
+
const row = (await em.findOne('_ConnectorIdentity', {
|
|
15
|
+
connector,
|
|
16
|
+
connector_user_id: connectorUserId,
|
|
17
|
+
}));
|
|
18
|
+
if (!row)
|
|
19
|
+
return null;
|
|
20
|
+
return findUserById(row.user_id);
|
|
21
|
+
}
|
|
22
|
+
export async function firstAdmin() {
|
|
23
|
+
const users = await listUsers();
|
|
24
|
+
return users.find((u) => u.role === 'admin' && !u.disabled) ?? null;
|
|
25
|
+
}
|
|
26
|
+
export async function linkToFirstAdmin(connector, connectorUserId) {
|
|
27
|
+
if (await resolveConnectorUser(connector, connectorUserId))
|
|
28
|
+
return;
|
|
29
|
+
const admin = await firstAdmin();
|
|
30
|
+
if (admin)
|
|
31
|
+
await linkConnectorIdentity(connector, connectorUserId, admin.id);
|
|
32
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const EMBED_MODEL = "text-embedding-3-small";
|
|
2
|
+
export declare const EMBED_DIM = 1024;
|
|
3
|
+
export declare function embedBatch(texts: string[], apiKey: string, fetchImpl?: typeof fetch): Promise<{
|
|
4
|
+
vectors: number[][];
|
|
5
|
+
tokens: number;
|
|
6
|
+
}>;
|
|
7
|
+
export declare function embedOne(text: string, apiKey: string, fetchImpl?: typeof fetch): Promise<{
|
|
8
|
+
vector: number[];
|
|
9
|
+
tokens: number;
|
|
10
|
+
}>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const OPENAI_URL = 'https://api.openai.com/v1/embeddings';
|
|
2
|
+
export const EMBED_MODEL = 'text-embedding-3-small';
|
|
3
|
+
export const EMBED_DIM = 1024;
|
|
4
|
+
export async function embedBatch(texts, apiKey, fetchImpl = fetch) {
|
|
5
|
+
if (texts.length === 0)
|
|
6
|
+
return { vectors: [], tokens: 0 };
|
|
7
|
+
if (!apiKey)
|
|
8
|
+
throw new Error('embeddings: missing API key (OPENAI_API_KEY)');
|
|
9
|
+
const res = await fetchImpl(OPENAI_URL, {
|
|
10
|
+
method: 'POST',
|
|
11
|
+
headers: { 'content-type': 'application/json', authorization: `Bearer ${apiKey}` },
|
|
12
|
+
body: JSON.stringify({ model: EMBED_MODEL, input: texts, dimensions: EMBED_DIM }),
|
|
13
|
+
});
|
|
14
|
+
if (!res.ok) {
|
|
15
|
+
const body = await res.text().catch(() => '');
|
|
16
|
+
throw new Error(`openai embeddings ${res.status}: ${body.slice(0, 200)}`);
|
|
17
|
+
}
|
|
18
|
+
const json = (await res.json());
|
|
19
|
+
const vectors = [...json.data].sort((a, b) => a.index - b.index).map((d) => d.embedding);
|
|
20
|
+
return { vectors, tokens: json.usage?.total_tokens ?? 0 };
|
|
21
|
+
}
|
|
22
|
+
export async function embedOne(text, apiKey, fetchImpl = fetch) {
|
|
23
|
+
const { vectors, tokens } = await embedBatch([text], apiKey, fetchImpl);
|
|
24
|
+
const vector = vectors[0];
|
|
25
|
+
if (!vector)
|
|
26
|
+
throw new Error('embeddings: empty response');
|
|
27
|
+
return { vector, tokens };
|
|
28
|
+
}
|
package/dist/entity-schema.d.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { EntitySchema } from '@mikro-orm/core';
|
|
2
|
-
import type {
|
|
2
|
+
import type { ShelfDef } from '@coffer-org/sdk/shelf';
|
|
3
3
|
import type { ExtendDef } from '@coffer-org/sdk/extend';
|
|
4
4
|
import type { SettingsDef } from '@coffer-org/sdk/settings';
|
|
5
5
|
import type { PluginManifest } from '@coffer-org/sdk/plugin';
|
|
6
|
-
export declare function
|
|
7
|
-
export declare function buildEntitySchema(m:
|
|
6
|
+
export declare function shelfTableName(library: string, shelf: string): string;
|
|
7
|
+
export declare function buildEntitySchema(m: ShelfDef): EntitySchema;
|
|
8
8
|
export declare function buildExtendEntitySchema(e: ExtendDef): EntitySchema;
|
|
9
9
|
export declare function buildSettingsEntitySchema(pluginId: string, settings: SettingsDef): EntitySchema;
|
|
10
|
-
export declare function childTableName(
|
|
11
|
-
export declare function buildCollectionEntities(m:
|
|
10
|
+
export declare function childTableName(library: string, shelf: string, key: string): string;
|
|
11
|
+
export declare function buildCollectionEntities(m: ShelfDef): EntitySchema[];
|
|
12
12
|
export declare function buildExtendCollectionEntities(e: ExtendDef): EntitySchema[];
|
|
13
13
|
export declare const EventSchema: EntitySchema<any, never, import("@mikro-orm/core").EntityCtor<any>>;
|
|
14
14
|
export declare const PluginRowSchema: EntitySchema<any, never, import("@mikro-orm/core").EntityCtor<any>>;
|
|
@@ -16,6 +16,7 @@ export declare const MigrationRowSchema: EntitySchema<any, never, import("@mikro
|
|
|
16
16
|
export declare const SeedRowSchema: EntitySchema<any, never, import("@mikro-orm/core").EntityCtor<any>>;
|
|
17
17
|
export declare const EmbeddingSchema: EntitySchema<any, never, import("@mikro-orm/core").EntityCtor<any>>;
|
|
18
18
|
export declare const PluginStateSchema: EntitySchema<any, never, import("@mikro-orm/core").EntityCtor<any>>;
|
|
19
|
+
export declare const ThreadMessageSchema: EntitySchema<any, never, import("@mikro-orm/core").EntityCtor<any>>;
|
|
19
20
|
export declare function buildPluginEntities(plugins: PluginManifest[]): EntitySchema[];
|
|
20
21
|
export declare const MsgLogSchema: EntitySchema<any, never, import("@mikro-orm/core").EntityCtor<any>>;
|
|
21
22
|
export declare const UserSchema: EntitySchema<any, never, import("@mikro-orm/core").EntityCtor<any>>;
|
package/dist/entity-schema.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { EntitySchema } from '@mikro-orm/core';
|
|
2
|
-
import { fieldEntries, collectionGroups, storageColumns } from '@coffer-org/sdk/
|
|
2
|
+
import { fieldEntries, collectionGroups, storageColumns } from '@coffer-org/sdk/shelf';
|
|
3
3
|
function mikroType(col) {
|
|
4
4
|
if (col === 'integer')
|
|
5
5
|
return 'integer';
|
|
@@ -34,8 +34,8 @@ function addFieldProps(properties, fields, plainNullable) {
|
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
|
-
export function
|
|
38
|
-
return `${
|
|
37
|
+
export function shelfTableName(library, shelf) {
|
|
38
|
+
return `${library}__${shelf}`;
|
|
39
39
|
}
|
|
40
40
|
export function buildEntitySchema(m) {
|
|
41
41
|
const properties = {
|
|
@@ -44,7 +44,7 @@ export function buildEntitySchema(m) {
|
|
|
44
44
|
updated_at: { type: 'text' },
|
|
45
45
|
};
|
|
46
46
|
addFieldProps(properties, m.fields, (f) => !f.required);
|
|
47
|
-
const name =
|
|
47
|
+
const name = shelfTableName(m.library, m.shelf);
|
|
48
48
|
return new EntitySchema({ name, tableName: name, properties });
|
|
49
49
|
}
|
|
50
50
|
export function buildExtendEntitySchema(e) {
|
|
@@ -63,8 +63,8 @@ export function buildSettingsEntitySchema(pluginId, settings) {
|
|
|
63
63
|
const name = `_settings__${pluginId}`;
|
|
64
64
|
return new EntitySchema({ name, tableName: name, properties });
|
|
65
65
|
}
|
|
66
|
-
export function childTableName(
|
|
67
|
-
return `${
|
|
66
|
+
export function childTableName(library, shelf, key) {
|
|
67
|
+
return `${library}__${shelf}__${key}`;
|
|
68
68
|
}
|
|
69
69
|
function buildChildSchemaAt(prefix, c) {
|
|
70
70
|
const properties = {
|
|
@@ -85,7 +85,7 @@ function buildCollectionEntitiesAt(prefix, fields) {
|
|
|
85
85
|
return out;
|
|
86
86
|
}
|
|
87
87
|
export function buildCollectionEntities(m) {
|
|
88
|
-
return buildCollectionEntitiesAt(`${m.
|
|
88
|
+
return buildCollectionEntitiesAt(`${m.library}__${m.shelf}`, m.fields);
|
|
89
89
|
}
|
|
90
90
|
export function buildExtendCollectionEntities(e) {
|
|
91
91
|
return buildCollectionEntitiesAt(`extend__${e.id}`, e.fields);
|
|
@@ -151,10 +151,24 @@ export const PluginStateSchema = new EntitySchema({
|
|
|
151
151
|
value: { type: 'text' },
|
|
152
152
|
},
|
|
153
153
|
});
|
|
154
|
+
export const ThreadMessageSchema = new EntitySchema({
|
|
155
|
+
name: '_ThreadMessage',
|
|
156
|
+
tableName: '_thread_message',
|
|
157
|
+
properties: {
|
|
158
|
+
connector: { type: 'text', primary: true },
|
|
159
|
+
chat_id: { type: 'text', primary: true },
|
|
160
|
+
msg_id: { type: 'text', primary: true },
|
|
161
|
+
role: { type: 'text' },
|
|
162
|
+
sender: { type: 'text', nullable: true },
|
|
163
|
+
text: { type: 'text' },
|
|
164
|
+
ts: { type: 'integer' },
|
|
165
|
+
reply_to_id: { type: 'text', nullable: true },
|
|
166
|
+
},
|
|
167
|
+
});
|
|
154
168
|
export function buildPluginEntities(plugins) {
|
|
155
169
|
return plugins.flatMap((p) => [
|
|
156
|
-
...(p.
|
|
157
|
-
...(p.
|
|
170
|
+
...(p.libraries ?? []).flatMap((v) => v.shelves.flatMap((m) => [buildEntitySchema(m), ...buildCollectionEntities(m)])),
|
|
171
|
+
...(p.libraryShelves ?? []).flatMap((m) => [buildEntitySchema(m), ...buildCollectionEntities(m)]),
|
|
158
172
|
...(p.extends_ ?? []).flatMap((e) => [buildExtendEntitySchema(e), ...buildExtendCollectionEntities(e)]),
|
|
159
173
|
...(p.settings && p.settings.fields.length > 0 ? [buildSettingsEntitySchema(p.id, p.settings)] : []),
|
|
160
174
|
]);
|
|
@@ -170,7 +184,6 @@ export const MsgLogSchema = new EntitySchema({
|
|
|
170
184
|
user_id: { type: 'text' },
|
|
171
185
|
role: { type: 'text' },
|
|
172
186
|
text: { type: 'text' },
|
|
173
|
-
session_id: { type: 'text', nullable: true },
|
|
174
187
|
tokens_in: { type: 'integer', nullable: true },
|
|
175
188
|
tokens_out: { type: 'integer', nullable: true },
|
|
176
189
|
ms: { type: 'integer', nullable: true },
|
|
@@ -216,4 +229,5 @@ export const systemEntities = [
|
|
|
216
229
|
EventSchema, PluginRowSchema, MigrationRowSchema, SeedRowSchema,
|
|
217
230
|
EmbeddingSchema, PluginStateSchema, MsgLogSchema,
|
|
218
231
|
UserSchema, SessionSchema, ApiTokenSchema,
|
|
232
|
+
ThreadMessageSchema,
|
|
219
233
|
];
|
package/dist/extend-io.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import type { EntityManager } from '@mikro-orm/core';
|
|
1
2
|
export declare function splitBody(body: unknown): {
|
|
2
3
|
base: Record<string, unknown>;
|
|
3
4
|
extData: Record<string, Record<string, unknown>>;
|
|
4
5
|
};
|
|
5
|
-
export declare function deleteExtends(
|
|
6
|
-
export declare function
|
|
7
|
-
export declare function
|
|
6
|
+
export declare function deleteExtends(em: EntityManager, library: string, shelf: string, id: number): Promise<void>;
|
|
7
|
+
export declare function readExtends(em: EntityManager, library: string, shelf: string, id: number): Promise<Record<string, unknown>>;
|
|
8
|
+
export declare function validateExtends(library: string, shelf: string, extData: Record<string, Record<string, unknown>>): void;
|
|
9
|
+
export declare function saveExtends(em: EntityManager, library: string, shelf: string, baseId: number, extData: Record<string, Record<string, unknown>>): Promise<void>;
|
package/dist/extend-io.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getExtendsFor } from "./registry-context.js";
|
|
2
2
|
import { buildExtendZodPartial } from '@coffer-org/sdk/extend';
|
|
3
|
-
import { upsertExtendRecord, deleteExtendRecord } from "./extend-table.js";
|
|
3
|
+
import { upsertExtendRecord, deleteExtendRecord, getExtendRecord } from "./extend-table.js";
|
|
4
4
|
import { toIssue, ValidationError } from "./mutate.js";
|
|
5
5
|
export function splitBody(body) {
|
|
6
6
|
const raw = (body ?? {});
|
|
@@ -14,14 +14,21 @@ export function splitBody(body) {
|
|
|
14
14
|
}
|
|
15
15
|
return { base, extData };
|
|
16
16
|
}
|
|
17
|
-
export async function deleteExtends(
|
|
18
|
-
for (const e of getExtendsFor(
|
|
19
|
-
await deleteExtendRecord(e, id);
|
|
17
|
+
export async function deleteExtends(em, library, shelf, id) {
|
|
18
|
+
for (const e of getExtendsFor(library, shelf)) {
|
|
19
|
+
await deleteExtendRecord(em, e, id);
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
|
-
export function
|
|
22
|
+
export async function readExtends(em, library, shelf, id) {
|
|
23
|
+
const out = {};
|
|
24
|
+
for (const e of getExtendsFor(library, shelf)) {
|
|
25
|
+
out[e.id] = (await getExtendRecord(e, id, em)) ?? null;
|
|
26
|
+
}
|
|
27
|
+
return out;
|
|
28
|
+
}
|
|
29
|
+
export function validateExtends(library, shelf, extData) {
|
|
23
30
|
const issues = [];
|
|
24
|
-
for (const e of getExtendsFor(
|
|
31
|
+
for (const e of getExtendsFor(library, shelf)) {
|
|
25
32
|
const data = extData[e.id];
|
|
26
33
|
if (data === undefined)
|
|
27
34
|
continue;
|
|
@@ -33,15 +40,15 @@ export function validateExtends(vault, module, extData) {
|
|
|
33
40
|
if (issues.length)
|
|
34
41
|
throw new ValidationError(issues);
|
|
35
42
|
}
|
|
36
|
-
export async function saveExtends(
|
|
37
|
-
validateExtends(
|
|
38
|
-
for (const e of getExtendsFor(
|
|
43
|
+
export async function saveExtends(em, library, shelf, baseId, extData) {
|
|
44
|
+
validateExtends(library, shelf, extData);
|
|
45
|
+
for (const e of getExtendsFor(library, shelf)) {
|
|
39
46
|
const data = extData[e.id];
|
|
40
47
|
if (data === undefined)
|
|
41
48
|
continue;
|
|
42
49
|
const parsed = buildExtendZodPartial(e).safeParse(data);
|
|
43
50
|
if (parsed.success && Object.keys(parsed.data).length > 0) {
|
|
44
|
-
await upsertExtendRecord(e, baseId, parsed.data);
|
|
51
|
+
await upsertExtendRecord(em, e, baseId, parsed.data);
|
|
45
52
|
}
|
|
46
53
|
}
|
|
47
54
|
}
|
package/dist/extend-table.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import type { EntityManager } from '@mikro-orm/core';
|
|
1
2
|
import type { ExtendDef } from '@coffer-org/sdk/extend';
|
|
2
3
|
type Row = Record<string, unknown>;
|
|
3
4
|
export declare function extendEntityName(e: ExtendDef): string;
|
|
4
|
-
export declare function getExtendRecord(e: ExtendDef, baseId: number): Promise<Row | undefined>;
|
|
5
|
-
export declare function upsertExtendRecord(e: ExtendDef, baseId: number, data: Row): Promise<void>;
|
|
6
|
-
export declare function deleteExtendRecord(e: ExtendDef, baseId: number): Promise<void>;
|
|
5
|
+
export declare function getExtendRecord(e: ExtendDef, baseId: number, em?: EntityManager): Promise<Row | undefined>;
|
|
6
|
+
export declare function upsertExtendRecord(em: EntityManager, e: ExtendDef, baseId: number, data: Row): Promise<void>;
|
|
7
|
+
export declare function deleteExtendRecord(em: EntityManager, e: ExtendDef, baseId: number): Promise<void>;
|
|
7
8
|
export {};
|
package/dist/extend-table.js
CHANGED
|
@@ -4,8 +4,8 @@ import { splitAt, writeAt, readAt, deleteAt } from "./collection-io.js";
|
|
|
4
4
|
export function extendEntityName(e) {
|
|
5
5
|
return `extend__${e.id}`;
|
|
6
6
|
}
|
|
7
|
-
export async function getExtendRecord(e, baseId) {
|
|
8
|
-
const fork = getEm().fork();
|
|
7
|
+
export async function getExtendRecord(e, baseId, em) {
|
|
8
|
+
const fork = em ?? getEm().fork();
|
|
9
9
|
const name = extendEntityName(e);
|
|
10
10
|
const row = await fork.findOne(name, { base_id: baseId });
|
|
11
11
|
if (!row)
|
|
@@ -14,24 +14,16 @@ export async function getExtendRecord(e, baseId) {
|
|
|
14
14
|
const collections = await readAt(fork, name, e.fields, baseId);
|
|
15
15
|
return { ...flat, ...collections };
|
|
16
16
|
}
|
|
17
|
-
export async function upsertExtendRecord(e, baseId, data) {
|
|
17
|
+
export async function upsertExtendRecord(em, e, baseId, data) {
|
|
18
18
|
const name = extendEntityName(e);
|
|
19
19
|
const { base, collections } = splitAt(e.fields, data);
|
|
20
|
-
await
|
|
21
|
-
|
|
22
|
-
.transactional(async (tx) => {
|
|
23
|
-
await tx.upsert(name, { base_id: baseId, ...base });
|
|
24
|
-
await writeAt(tx, name, e.fields, baseId, collections);
|
|
25
|
-
});
|
|
20
|
+
await em.upsert(name, { base_id: baseId, ...base });
|
|
21
|
+
await writeAt(em, name, e.fields, baseId, collections);
|
|
26
22
|
}
|
|
27
|
-
export async function deleteExtendRecord(e, baseId) {
|
|
23
|
+
export async function deleteExtendRecord(em, e, baseId) {
|
|
28
24
|
const name = extendEntityName(e);
|
|
29
|
-
await
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const ref = tx.getReference(name, baseId);
|
|
34
|
-
tx.remove(ref);
|
|
35
|
-
await tx.flush();
|
|
36
|
-
});
|
|
25
|
+
await deleteAt(em, name, e.fields, baseId);
|
|
26
|
+
const ref = em.getReference(name, baseId);
|
|
27
|
+
em.remove(ref);
|
|
28
|
+
await em.flush();
|
|
37
29
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { LayoutEl } from '@coffer-org/sdk/fields';
|
|
2
|
+
export declare const MASK = "********";
|
|
3
|
+
export declare function passwordKeys(fields: LayoutEl[]): string[];
|
|
4
|
+
export declare function maskSecrets(fields: LayoutEl[], row: Record<string, unknown>): Record<string, unknown>;
|
|
5
|
+
export declare function preserveSecrets(fields: LayoutEl[], incoming: Record<string, unknown>, existing: Record<string, unknown>): Record<string, unknown>;
|
|
6
|
+
export declare function maskTree(fields: LayoutEl[], obj: Record<string, unknown>): Record<string, unknown>;
|
|
7
|
+
export declare function preserveTree(fields: LayoutEl[], incoming: Record<string, unknown>, existing: Record<string, unknown>): Record<string, unknown>;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { fieldEntries, collectionGroups } from '@coffer-org/sdk/shelf';
|
|
2
|
+
export const MASK = '********';
|
|
3
|
+
export function passwordKeys(fields) {
|
|
4
|
+
return fieldEntries(fields)
|
|
5
|
+
.filter(([, f]) => f.kind === 'password')
|
|
6
|
+
.map(([k]) => k);
|
|
7
|
+
}
|
|
8
|
+
export function maskSecrets(fields, row) {
|
|
9
|
+
const out = { ...row };
|
|
10
|
+
for (const k of passwordKeys(fields))
|
|
11
|
+
if (out[k])
|
|
12
|
+
out[k] = MASK;
|
|
13
|
+
return out;
|
|
14
|
+
}
|
|
15
|
+
export function preserveSecrets(fields, incoming, existing) {
|
|
16
|
+
const out = { ...incoming };
|
|
17
|
+
for (const k of passwordKeys(fields)) {
|
|
18
|
+
if (out[k] !== MASK)
|
|
19
|
+
continue;
|
|
20
|
+
if (existing[k] !== undefined && existing[k] !== null)
|
|
21
|
+
out[k] = existing[k];
|
|
22
|
+
else
|
|
23
|
+
delete out[k];
|
|
24
|
+
}
|
|
25
|
+
return out;
|
|
26
|
+
}
|
|
27
|
+
export function maskTree(fields, obj) {
|
|
28
|
+
const out = maskSecrets(fields, obj);
|
|
29
|
+
for (const c of collectionGroups(fields)) {
|
|
30
|
+
const key = c.key;
|
|
31
|
+
const sub = c.group.fields;
|
|
32
|
+
const arr = out[key];
|
|
33
|
+
if (Array.isArray(arr)) {
|
|
34
|
+
out[key] = arr.map((item) => item && typeof item === 'object' ? maskTree(sub, item) : item);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return out;
|
|
38
|
+
}
|
|
39
|
+
export function preserveTree(fields, incoming, existing) {
|
|
40
|
+
const out = preserveSecrets(fields, incoming, existing);
|
|
41
|
+
for (const c of collectionGroups(fields)) {
|
|
42
|
+
const key = c.key;
|
|
43
|
+
const sub = c.group.fields;
|
|
44
|
+
const uniq = c.group.unique ?? [];
|
|
45
|
+
const inArr = out[key];
|
|
46
|
+
if (!Array.isArray(inArr))
|
|
47
|
+
continue;
|
|
48
|
+
const exArr = Array.isArray(existing[key]) ? existing[key] : [];
|
|
49
|
+
out[key] = inArr.map((item) => {
|
|
50
|
+
if (!item || typeof item !== 'object')
|
|
51
|
+
return item;
|
|
52
|
+
const rec = item;
|
|
53
|
+
const match = uniq.length > 0
|
|
54
|
+
? exArr.find((e) => e && uniq.every((u) => e[u] === rec[u]))
|
|
55
|
+
: undefined;
|
|
56
|
+
return preserveTree(sub, rec, (match ?? {}));
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
return out;
|
|
60
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { getLogger } from '@coffer-org/sdk/logger';
|
|
2
|
+
const log = getLogger('rag-signal');
|
|
3
|
+
let listener = null;
|
|
4
|
+
export function onRecordsChanged(cb) {
|
|
5
|
+
if (listener)
|
|
6
|
+
log.warn('onRecordsChanged: overwriting an already-registered listener without offRecordsChanged');
|
|
7
|
+
listener = cb;
|
|
8
|
+
}
|
|
9
|
+
export function offRecordsChanged() {
|
|
10
|
+
listener = null;
|
|
11
|
+
}
|
|
12
|
+
export function notifyRecordsChanged() {
|
|
13
|
+
listener?.();
|
|
14
|
+
}
|