@coffer-org/server 7.3.0 → 7.5.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 +18 -19
- package/dist/compute-unit.js +0 -3
- package/dist/conversation-store.d.ts +61 -0
- package/dist/conversation-store.js +223 -0
- package/dist/entity-schema.d.ts +5 -4
- package/dist/entity-schema.js +32 -41
- package/dist/identity-providers.d.ts +17 -0
- package/dist/identity-providers.js +14 -0
- package/dist/index.js +2 -1
- package/dist/mcp-contract/schema.d.ts +0 -1
- package/dist/mcp-contract/schema.js +0 -2
- package/dist/mutate.js +0 -15
- package/dist/orchestrator/draft-message.d.ts +18 -0
- package/dist/orchestrator/draft-message.js +85 -0
- package/dist/orchestrator/index.d.ts +4 -5
- package/dist/orchestrator/index.js +2 -9
- package/dist/orchestrator/live-message.js +6 -3
- package/dist/orchestrator/pipeline.d.ts +7 -10
- package/dist/orchestrator/pipeline.js +177 -96
- package/dist/orchestrator/registry.d.ts +0 -2
- package/dist/orchestrator/registry.js +0 -9
- package/dist/orchestrator/types.d.ts +17 -25
- package/dist/plugin-hooks.d.ts +12 -1
- package/dist/plugin-hooks.js +4 -0
- package/dist/plugin-runtime.d.ts +4 -0
- package/dist/plugin-runtime.js +11 -7
- package/dist/plugins-api.d.ts +8 -2
- package/dist/plugins-api.js +4 -2
- package/dist/settings-write.d.ts +1 -2
- package/dist/settings-write.js +1 -2
- package/dist/temporal.js +8 -3
- package/package.json +2 -2
package/dist/auth-api.js
CHANGED
|
@@ -2,8 +2,7 @@ import { field } from '@coffer-org/sdk/fields';
|
|
|
2
2
|
import { materializeTree } from '@coffer-org/sdk/materialize/pipeline';
|
|
3
3
|
import { countUsers, createUser, findUserByLogin, findUserById, getPasswordHash, listUsers, updateUser, deleteUser, countAdmins, createSession, resolveSession, deleteSession, createApiToken, resolveApiToken, listApiTokens, revokeApiToken, } from "./auth-store.js";
|
|
4
4
|
import { resolveAccessToken } from "./oauth-store.js";
|
|
5
|
-
import {
|
|
6
|
-
import { listLinkableConnectors, connectorLinkUrl } from "./orchestrator/index.js";
|
|
5
|
+
import { listIdentityProviders, getIdentityProvider } from "./identity-providers.js";
|
|
7
6
|
import { mcpResource } from "./public-url.js";
|
|
8
7
|
import { verifyPassword } from "./auth-crypto.js";
|
|
9
8
|
import { maskSecrets, preserveSecrets } from "./field-masking.js";
|
|
@@ -13,7 +12,6 @@ import { USERS_SHELF } from '@coffer-org/sdk/users-shelf';
|
|
|
13
12
|
const PASSWORD_FIELDS = materializeTree({ password: field.password({}) });
|
|
14
13
|
const SESSION_TTL_MS = 30 * 24 * 60 * 60 * 1000;
|
|
15
14
|
const COOKIE_NAME = 'sid';
|
|
16
|
-
const LINK_CODE_TTL_MS = 10 * 60 * 1000;
|
|
17
15
|
export const PUBLIC_API_PATHS = ['/api/auth/status', '/api/auth/login', '/api/auth/setup'];
|
|
18
16
|
function readCookie(req, name) {
|
|
19
17
|
const header = req.headers.cookie;
|
|
@@ -234,29 +232,30 @@ export async function registerAuthApi(app) {
|
|
|
234
232
|
app.get('/api/auth/links', async (req, reply) => {
|
|
235
233
|
if (!req.user)
|
|
236
234
|
return reply.code(401).send({ error: 'unauthorized' });
|
|
237
|
-
|
|
235
|
+
const providers = listIdentityProviders();
|
|
236
|
+
const links = (await Promise.all(providers.map(async (p) => (await p.listLinks(req.user.id)).map((l) => ({ provider: p.id, ...l }))))).flat();
|
|
237
|
+
return { providers: providers.map(({ id, label, icon }) => ({ id, label, icon })), links };
|
|
238
238
|
});
|
|
239
|
-
app.post('/api/auth/links', async (req, reply) => {
|
|
239
|
+
app.post('/api/auth/links/:provider', async (req, reply) => {
|
|
240
240
|
if (!req.user)
|
|
241
241
|
return reply.code(401).send({ error: 'unauthorized' });
|
|
242
|
-
const
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
return
|
|
242
|
+
const { provider } = req.params;
|
|
243
|
+
const p = getIdentityProvider(provider);
|
|
244
|
+
if (!p)
|
|
245
|
+
return reply.code(404).send({ error: 'not_found' });
|
|
246
|
+
const started = await p.beginLink(req.user.id);
|
|
247
|
+
if (!started)
|
|
248
|
+
return reply.code(503).send({ error: 'unavailable' });
|
|
249
|
+
return started;
|
|
250
250
|
});
|
|
251
|
-
app.delete('/api/auth/links/:
|
|
251
|
+
app.delete('/api/auth/links/:provider/:externalId', async (req, reply) => {
|
|
252
252
|
if (!req.user)
|
|
253
253
|
return reply.code(401).send({ error: 'unauthorized' });
|
|
254
|
-
const {
|
|
255
|
-
const
|
|
256
|
-
|
|
257
|
-
if (!owns)
|
|
254
|
+
const { provider, externalId } = req.params;
|
|
255
|
+
const p = getIdentityProvider(provider);
|
|
256
|
+
if (!p)
|
|
258
257
|
return reply.code(404).send({ error: 'not_found' });
|
|
259
|
-
const ok = await unlink(
|
|
258
|
+
const ok = await p.unlink(req.user.id, externalId);
|
|
260
259
|
if (!ok)
|
|
261
260
|
return reply.code(404).send({ error: 'not_found' });
|
|
262
261
|
return { ok: true };
|
package/dist/compute-unit.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { hasChildren, isNamedField, isCollectionGroup, isEmbeddedGroup, isStorageGroup, } from '@coffer-org/sdk/fields';
|
|
2
|
-
import { mandatedClientParts } from '@coffer-org/sdk/parts';
|
|
3
2
|
import { PartContractError } from "./part-errors.js";
|
|
4
3
|
import { createRefLoader } from "./part-ref.js";
|
|
5
4
|
import { wholeClockColumn, partClockColumn } from "./cache-clock.js";
|
|
@@ -267,8 +266,6 @@ export function materializable(fm) {
|
|
|
267
266
|
const parts = fm.parts ?? [];
|
|
268
267
|
if (!parts.some((p) => p.mode === 'computedStored'))
|
|
269
268
|
return false;
|
|
270
|
-
if (mandatedClientParts(fm.required, parts).length > 0)
|
|
271
|
-
return false;
|
|
272
269
|
return true;
|
|
273
270
|
}
|
|
274
271
|
export function storedFilled(fm, value) {
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export declare const HIDDEN_ROLES: readonly ["reasoning", "suggestions", "context"];
|
|
2
|
+
export declare const SIDECAR_ROLES: readonly ["reasoning", "suggestions"];
|
|
3
|
+
export declare const TITLE_MAX = 60;
|
|
4
|
+
export declare const DEFAULT_MAX_DEPTH = 40;
|
|
5
|
+
export declare const conversationEmitter: EventTarget;
|
|
6
|
+
export interface PutMessageInput {
|
|
7
|
+
conversationId: string;
|
|
8
|
+
msgId: string;
|
|
9
|
+
role: string;
|
|
10
|
+
sender?: string | null;
|
|
11
|
+
text: string;
|
|
12
|
+
attachments?: StoredAttachment[];
|
|
13
|
+
ts: number;
|
|
14
|
+
replyToId?: string | null;
|
|
15
|
+
}
|
|
16
|
+
export declare function onMessage(cb: (m: PutMessageInput) => void | Promise<void>): () => void;
|
|
17
|
+
export interface StoredMsg {
|
|
18
|
+
msgId: string;
|
|
19
|
+
role: string;
|
|
20
|
+
sender: string | null;
|
|
21
|
+
text: string;
|
|
22
|
+
attachments?: StoredAttachment[];
|
|
23
|
+
ts: number;
|
|
24
|
+
replyToId: string | null;
|
|
25
|
+
}
|
|
26
|
+
export interface StoredAttachment {
|
|
27
|
+
name: string;
|
|
28
|
+
mime?: string;
|
|
29
|
+
size?: number;
|
|
30
|
+
label?: string;
|
|
31
|
+
}
|
|
32
|
+
export interface Conversation {
|
|
33
|
+
id: string;
|
|
34
|
+
owner: string | null;
|
|
35
|
+
title: string | null;
|
|
36
|
+
agentId: string | null;
|
|
37
|
+
presetId: string | null;
|
|
38
|
+
visibility: 'private' | null;
|
|
39
|
+
updatedAt: string;
|
|
40
|
+
}
|
|
41
|
+
export interface ConversationSummary extends Conversation {
|
|
42
|
+
lastTs: number;
|
|
43
|
+
count: number;
|
|
44
|
+
firstUserText: string;
|
|
45
|
+
}
|
|
46
|
+
export declare function newConversationId(): string;
|
|
47
|
+
export declare function newMessageId(): string;
|
|
48
|
+
export declare function getConversation(id: string): Promise<Conversation>;
|
|
49
|
+
export declare function setConversation(id: string, patch: Partial<Omit<Conversation, 'id' | 'updatedAt'>>): Promise<void>;
|
|
50
|
+
export declare function readAndTouchConversation(id: string): Promise<Conversation>;
|
|
51
|
+
export declare function listConversations(viewerId: string): Promise<ConversationSummary[]>;
|
|
52
|
+
export declare function putMessage(m: PutMessageInput): Promise<void>;
|
|
53
|
+
export declare const insertMessage: typeof putMessage;
|
|
54
|
+
export declare function getMessage(conversationId: string, msgId: string): Promise<StoredMsg | null>;
|
|
55
|
+
export declare function readConversation(conversationId: string, opts?: {
|
|
56
|
+
limit?: number;
|
|
57
|
+
}): Promise<StoredMsg[]>;
|
|
58
|
+
export declare function readAll(conversationId: string): Promise<StoredMsg[]>;
|
|
59
|
+
export declare function countUserMessages(conversationId: string): Promise<number>;
|
|
60
|
+
export declare function lastMessageId(conversationId: string): Promise<string | null>;
|
|
61
|
+
export declare function pruneConversations(cutoffTs: number, cutoffIso: string): Promise<void>;
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { getEm } from "./db.js";
|
|
3
|
+
import { mayRead } from "./orchestrator/conversation-access.js";
|
|
4
|
+
export const HIDDEN_ROLES = ['reasoning', 'suggestions', 'context'];
|
|
5
|
+
export const SIDECAR_ROLES = ['reasoning', 'suggestions'];
|
|
6
|
+
export const TITLE_MAX = 60;
|
|
7
|
+
export const DEFAULT_MAX_DEPTH = 40;
|
|
8
|
+
export const conversationEmitter = new EventTarget();
|
|
9
|
+
export function onMessage(cb) {
|
|
10
|
+
const handler = (e) => {
|
|
11
|
+
if (e instanceof CustomEvent) {
|
|
12
|
+
void cb(e.detail);
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
conversationEmitter.addEventListener('message', handler);
|
|
16
|
+
return () => {
|
|
17
|
+
conversationEmitter.removeEventListener('message', handler);
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function capTitle(title) {
|
|
21
|
+
const t = title.trim();
|
|
22
|
+
return t.length <= TITLE_MAX ? t : `${t.slice(0, TITLE_MAX - 1)}…`;
|
|
23
|
+
}
|
|
24
|
+
function emptyConversation(id) {
|
|
25
|
+
return {
|
|
26
|
+
id,
|
|
27
|
+
owner: null,
|
|
28
|
+
title: null,
|
|
29
|
+
agentId: null,
|
|
30
|
+
presetId: null,
|
|
31
|
+
visibility: null,
|
|
32
|
+
updatedAt: '',
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function toConversation(row) {
|
|
36
|
+
return {
|
|
37
|
+
id: row.id,
|
|
38
|
+
owner: row.owner,
|
|
39
|
+
title: row.title,
|
|
40
|
+
agentId: row.agent_id,
|
|
41
|
+
presetId: row.preset_id,
|
|
42
|
+
visibility: row.visibility === 'private' ? 'private' : null,
|
|
43
|
+
updatedAt: row.updated_at,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function toStored(r) {
|
|
47
|
+
let attachments;
|
|
48
|
+
if (r.attachments) {
|
|
49
|
+
try {
|
|
50
|
+
const parsed = JSON.parse(r.attachments);
|
|
51
|
+
if (Array.isArray(parsed)) {
|
|
52
|
+
const valid = parsed.filter((v) => typeof v === 'object' && v !== null && typeof v.name === 'string');
|
|
53
|
+
if (valid.length)
|
|
54
|
+
attachments = valid;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
msgId: r.msg_id,
|
|
62
|
+
role: r.role,
|
|
63
|
+
sender: r.sender,
|
|
64
|
+
text: r.text,
|
|
65
|
+
...(attachments ? { attachments } : {}),
|
|
66
|
+
ts: r.ts,
|
|
67
|
+
replyToId: r.reply_to_id,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
export function newConversationId() {
|
|
71
|
+
return randomUUID();
|
|
72
|
+
}
|
|
73
|
+
export function newMessageId() {
|
|
74
|
+
return randomUUID();
|
|
75
|
+
}
|
|
76
|
+
export async function getConversation(id) {
|
|
77
|
+
const em = getEm().fork();
|
|
78
|
+
const row = (await em.findOne('_Conversation', { id }));
|
|
79
|
+
if (!row)
|
|
80
|
+
return emptyConversation(id);
|
|
81
|
+
return toConversation(row);
|
|
82
|
+
}
|
|
83
|
+
export async function setConversation(id, patch) {
|
|
84
|
+
const em = getEm().fork();
|
|
85
|
+
const data = { updated_at: new Date().toISOString() };
|
|
86
|
+
if ('agentId' in patch)
|
|
87
|
+
data['agent_id'] = patch.agentId ?? null;
|
|
88
|
+
if ('presetId' in patch)
|
|
89
|
+
data['preset_id'] = patch.presetId ?? null;
|
|
90
|
+
if ('title' in patch)
|
|
91
|
+
data['title'] = patch.title ? capTitle(patch.title) : null;
|
|
92
|
+
if ('owner' in patch)
|
|
93
|
+
data['owner'] = patch.owner ?? null;
|
|
94
|
+
if ('visibility' in patch)
|
|
95
|
+
data['visibility'] = patch.visibility ?? null;
|
|
96
|
+
const existing = (await em.findOne('_Conversation', { id }));
|
|
97
|
+
if (existing) {
|
|
98
|
+
em.assign(existing, data);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
em.persist(em.create('_Conversation', {
|
|
102
|
+
id,
|
|
103
|
+
agent_id: data['agent_id'] ?? null,
|
|
104
|
+
preset_id: data['preset_id'] ?? null,
|
|
105
|
+
title: data['title'] ?? null,
|
|
106
|
+
owner: data['owner'] ?? null,
|
|
107
|
+
visibility: data['visibility'] ?? null,
|
|
108
|
+
updated_at: data['updated_at'],
|
|
109
|
+
}));
|
|
110
|
+
}
|
|
111
|
+
await em.flush();
|
|
112
|
+
conversationEmitter.dispatchEvent(new CustomEvent('conversation', { detail: { id, patch } }));
|
|
113
|
+
}
|
|
114
|
+
export async function readAndTouchConversation(id) {
|
|
115
|
+
const em = getEm().fork();
|
|
116
|
+
const row = (await em.findOne('_Conversation', { id }));
|
|
117
|
+
if (!row)
|
|
118
|
+
return emptyConversation(id);
|
|
119
|
+
const now = new Date().toISOString();
|
|
120
|
+
em.assign(row, { updated_at: now });
|
|
121
|
+
await em.flush();
|
|
122
|
+
return toConversation(row);
|
|
123
|
+
}
|
|
124
|
+
export async function listConversations(viewerId) {
|
|
125
|
+
const em = getEm().fork();
|
|
126
|
+
const convRows = (await em.find('_Conversation', {}));
|
|
127
|
+
const msgRows = (await em.find('_ConversationMessage', {}, { orderBy: { ts: 'asc', msg_id: 'asc' } }));
|
|
128
|
+
const msgSummaries = new Map();
|
|
129
|
+
for (const r of msgRows) {
|
|
130
|
+
if (HIDDEN_ROLES.includes(r.role))
|
|
131
|
+
continue;
|
|
132
|
+
const cur = msgSummaries.get(r.conversation_id);
|
|
133
|
+
if (!cur) {
|
|
134
|
+
msgSummaries.set(r.conversation_id, {
|
|
135
|
+
lastTs: r.ts,
|
|
136
|
+
count: 1,
|
|
137
|
+
firstUserText: r.role === 'user' ? r.text : '',
|
|
138
|
+
});
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
cur.count += 1;
|
|
142
|
+
if (r.ts > cur.lastTs)
|
|
143
|
+
cur.lastTs = r.ts;
|
|
144
|
+
if (!cur.firstUserText && r.role === 'user')
|
|
145
|
+
cur.firstUserText = r.text;
|
|
146
|
+
}
|
|
147
|
+
const convMap = new Map();
|
|
148
|
+
for (const r of convRows) {
|
|
149
|
+
convMap.set(r.id, toConversation(r));
|
|
150
|
+
}
|
|
151
|
+
const allIds = new Set([...convMap.keys(), ...msgSummaries.keys()]);
|
|
152
|
+
const out = [];
|
|
153
|
+
for (const id of allIds) {
|
|
154
|
+
const conv = convMap.get(id) ?? emptyConversation(id);
|
|
155
|
+
if (!mayRead(conv, viewerId))
|
|
156
|
+
continue;
|
|
157
|
+
const summary = msgSummaries.get(id);
|
|
158
|
+
out.push({
|
|
159
|
+
...conv,
|
|
160
|
+
lastTs: summary?.lastTs ?? 0,
|
|
161
|
+
count: summary?.count ?? 0,
|
|
162
|
+
firstUserText: summary?.firstUserText ?? '',
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
return out.sort((a, b) => {
|
|
166
|
+
if (b.lastTs !== a.lastTs)
|
|
167
|
+
return b.lastTs - a.lastTs;
|
|
168
|
+
return (b.updatedAt || '').localeCompare(a.updatedAt || '') || b.id.localeCompare(a.id);
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
export async function putMessage(m) {
|
|
172
|
+
const em = getEm().fork();
|
|
173
|
+
await em.upsert('_ConversationMessage', {
|
|
174
|
+
conversation_id: m.conversationId,
|
|
175
|
+
msg_id: m.msgId,
|
|
176
|
+
role: m.role,
|
|
177
|
+
sender: m.sender ?? null,
|
|
178
|
+
attachments: m.attachments?.length ? JSON.stringify(m.attachments) : null,
|
|
179
|
+
text: m.text,
|
|
180
|
+
ts: m.ts,
|
|
181
|
+
reply_to_id: m.replyToId ?? null,
|
|
182
|
+
});
|
|
183
|
+
await em.flush();
|
|
184
|
+
conversationEmitter.dispatchEvent(new CustomEvent('message', { detail: m }));
|
|
185
|
+
}
|
|
186
|
+
export const insertMessage = putMessage;
|
|
187
|
+
export async function getMessage(conversationId, msgId) {
|
|
188
|
+
const em = getEm().fork();
|
|
189
|
+
const row = (await em.findOne('_ConversationMessage', { conversation_id: conversationId, msg_id: msgId }));
|
|
190
|
+
return row ? toStored(row) : null;
|
|
191
|
+
}
|
|
192
|
+
export async function readConversation(conversationId, opts) {
|
|
193
|
+
const limit = opts?.limit ?? DEFAULT_MAX_DEPTH;
|
|
194
|
+
const em = getEm().fork();
|
|
195
|
+
const visible = (await em.find('_ConversationMessage', { conversation_id: conversationId, role: { $nin: HIDDEN_ROLES } }, { orderBy: { ts: 'desc', msg_id: 'desc' }, limit }));
|
|
196
|
+
const oldest = visible.at(-1)?.ts;
|
|
197
|
+
const sidecars = oldest === undefined
|
|
198
|
+
? []
|
|
199
|
+
: (await em.find('_ConversationMessage', { conversation_id: conversationId, role: { $in: SIDECAR_ROLES }, ts: { $gte: oldest - 1 } }, { orderBy: { ts: 'desc', msg_id: 'desc' } }));
|
|
200
|
+
return [...visible, ...sidecars]
|
|
201
|
+
.sort((a, b) => b.ts - a.ts || (a.msg_id < b.msg_id ? 1 : a.msg_id > b.msg_id ? -1 : 0))
|
|
202
|
+
.reverse()
|
|
203
|
+
.map(toStored);
|
|
204
|
+
}
|
|
205
|
+
export async function readAll(conversationId) {
|
|
206
|
+
const em = getEm().fork();
|
|
207
|
+
const rows = (await em.find('_ConversationMessage', { conversation_id: conversationId }, { orderBy: { ts: 'asc', msg_id: 'asc' } }));
|
|
208
|
+
return rows.map(toStored);
|
|
209
|
+
}
|
|
210
|
+
export async function countUserMessages(conversationId) {
|
|
211
|
+
const em = getEm().fork();
|
|
212
|
+
return em.count('_ConversationMessage', { conversation_id: conversationId, role: 'user' });
|
|
213
|
+
}
|
|
214
|
+
export async function lastMessageId(conversationId) {
|
|
215
|
+
const em = getEm().fork();
|
|
216
|
+
const row = (await em.findOne('_ConversationMessage', { conversation_id: conversationId, role: { $nin: HIDDEN_ROLES } }, { orderBy: { ts: 'desc', msg_id: 'desc' } }));
|
|
217
|
+
return row ? row.msg_id : null;
|
|
218
|
+
}
|
|
219
|
+
export async function pruneConversations(cutoffTs, cutoffIso) {
|
|
220
|
+
const em = getEm().fork();
|
|
221
|
+
await em.nativeDelete('_ConversationMessage', { ts: { $lt: cutoffTs } });
|
|
222
|
+
await em.nativeDelete('_Conversation', { updated_at: { $lt: cutoffIso }, owner: null, visibility: null });
|
|
223
|
+
}
|
package/dist/entity-schema.d.ts
CHANGED
|
@@ -3,10 +3,13 @@ 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
|
+
import type { PrivateTableDef } from './plugin-hooks.ts';
|
|
6
7
|
export declare function shelfTableName(library: string, shelf: string): string;
|
|
7
8
|
export declare function buildEntitySchema(m: ShelfDef): EntitySchema;
|
|
8
9
|
export declare function buildExtendEntitySchema(e: ExtendDef): EntitySchema;
|
|
9
10
|
export declare function buildSettingsEntitySchema(pluginId: string, settings: SettingsDef): EntitySchema;
|
|
11
|
+
export declare function privateTableName(pluginId: string, name: string): string;
|
|
12
|
+
export declare function buildPrivateTableEntity(pluginId: string, t: PrivateTableDef): EntitySchema;
|
|
10
13
|
export declare function childTableName(library: string, shelf: string, key: string): string;
|
|
11
14
|
export declare function buildCollectionEntities(m: ShelfDef): EntitySchema[];
|
|
12
15
|
export declare function buildExtendCollectionEntities(e: ExtendDef): EntitySchema[];
|
|
@@ -18,15 +21,13 @@ export declare const SeedRowSchema: EntitySchema<any, never, import("@mikro-orm/
|
|
|
18
21
|
export declare const EmbeddingSchema: EntitySchema<any, never, import("@mikro-orm/core").EntityCtor<any>>;
|
|
19
22
|
export declare const PluginStateSchema: EntitySchema<any, never, import("@mikro-orm/core").EntityCtor<any>>;
|
|
20
23
|
export declare const RecordActivitySchema: EntitySchema<any, never, import("@mikro-orm/core").EntityCtor<any>>;
|
|
21
|
-
export declare const
|
|
22
|
-
export declare const
|
|
24
|
+
export declare const ConversationMessageSchema: EntitySchema<any, never, import("@mikro-orm/core").EntityCtor<any>>;
|
|
25
|
+
export declare const ConversationSchema: EntitySchema<any, never, import("@mikro-orm/core").EntityCtor<any>>;
|
|
23
26
|
export declare function buildPluginEntities(plugins: PluginManifest[]): EntitySchema[];
|
|
24
27
|
export declare const MsgLogSchema: EntitySchema<any, never, import("@mikro-orm/core").EntityCtor<any>>;
|
|
25
28
|
export declare const UserSchema: EntitySchema<any, never, import("@mikro-orm/core").EntityCtor<any>>;
|
|
26
29
|
export declare const SessionSchema: EntitySchema<any, never, import("@mikro-orm/core").EntityCtor<any>>;
|
|
27
30
|
export declare const ApiTokenSchema: EntitySchema<any, never, import("@mikro-orm/core").EntityCtor<any>>;
|
|
28
|
-
export declare const IdentityLinkSchema: EntitySchema<any, never, import("@mikro-orm/core").EntityCtor<any>>;
|
|
29
|
-
export declare const LinkCodeSchema: EntitySchema<any, never, import("@mikro-orm/core").EntityCtor<any>>;
|
|
30
31
|
export declare const OAuthClientSchema: EntitySchema<any, never, import("@mikro-orm/core").EntityCtor<any>>;
|
|
31
32
|
export declare const OAuthCodeSchema: EntitySchema<any, never, import("@mikro-orm/core").EntityCtor<any>>;
|
|
32
33
|
export declare const OAuthTokenSchema: EntitySchema<any, never, import("@mikro-orm/core").EntityCtor<any>>;
|
package/dist/entity-schema.js
CHANGED
|
@@ -23,14 +23,14 @@ function columnProp(field, nullable) {
|
|
|
23
23
|
p['default'] = field.default;
|
|
24
24
|
return p;
|
|
25
25
|
}
|
|
26
|
-
function addFieldProps(properties, fields
|
|
26
|
+
function addFieldProps(properties, fields) {
|
|
27
27
|
for (const [key, field] of fieldEntries(fields)) {
|
|
28
28
|
if (field.columns) {
|
|
29
29
|
for (const [col, type] of storageColumns(key, field))
|
|
30
30
|
properties[col] = { type: mikroType(type), nullable: true };
|
|
31
31
|
}
|
|
32
32
|
else {
|
|
33
|
-
properties[key] = columnProp(field,
|
|
33
|
+
properties[key] = columnProp(field, true);
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
for (const col of cacheClockColumns(fields))
|
|
@@ -46,7 +46,7 @@ export function buildEntitySchema(m) {
|
|
|
46
46
|
updated_at: { type: 'text' },
|
|
47
47
|
deleted_at: { type: 'text', nullable: true },
|
|
48
48
|
};
|
|
49
|
-
addFieldProps(properties, m.fields
|
|
49
|
+
addFieldProps(properties, m.fields);
|
|
50
50
|
const name = shelfTableName(m.library, m.shelf);
|
|
51
51
|
return new EntitySchema({ name, tableName: name, properties });
|
|
52
52
|
}
|
|
@@ -54,7 +54,7 @@ export function buildExtendEntitySchema(e) {
|
|
|
54
54
|
const properties = {
|
|
55
55
|
base_id: { type: 'integer', primary: true },
|
|
56
56
|
};
|
|
57
|
-
addFieldProps(properties, e.fields
|
|
57
|
+
addFieldProps(properties, e.fields);
|
|
58
58
|
const name = `extend__${e.id}`;
|
|
59
59
|
return new EntitySchema({ name, tableName: name, properties });
|
|
60
60
|
}
|
|
@@ -62,10 +62,26 @@ export function buildSettingsEntitySchema(pluginId, settings) {
|
|
|
62
62
|
const properties = {
|
|
63
63
|
plugin_id: { type: 'text', primary: true },
|
|
64
64
|
};
|
|
65
|
-
addFieldProps(properties, settings.fields
|
|
65
|
+
addFieldProps(properties, settings.fields);
|
|
66
66
|
const name = `_settings__${pluginId}`;
|
|
67
67
|
return new EntitySchema({ name, tableName: name, properties });
|
|
68
68
|
}
|
|
69
|
+
export function privateTableName(pluginId, name) {
|
|
70
|
+
return `_${pluginId}__${name}`;
|
|
71
|
+
}
|
|
72
|
+
export function buildPrivateTableEntity(pluginId, t) {
|
|
73
|
+
const properties = {
|
|
74
|
+
id: { type: 'integer', primary: true, autoincrement: true },
|
|
75
|
+
};
|
|
76
|
+
addFieldProps(properties, t.fields);
|
|
77
|
+
const name = privateTableName(pluginId, t.name);
|
|
78
|
+
return new EntitySchema({
|
|
79
|
+
name,
|
|
80
|
+
tableName: name,
|
|
81
|
+
properties,
|
|
82
|
+
uniques: (t.unique ?? []).map((properties) => ({ properties })),
|
|
83
|
+
});
|
|
84
|
+
}
|
|
69
85
|
export function childTableName(library, shelf, key) {
|
|
70
86
|
return `${library}__${shelf}__${key}`;
|
|
71
87
|
}
|
|
@@ -75,7 +91,7 @@ function buildChildSchemaAt(prefix, c) {
|
|
|
75
91
|
parent_id: { type: 'integer', index: true },
|
|
76
92
|
position: { type: 'integer' },
|
|
77
93
|
};
|
|
78
|
-
addFieldProps(properties, c.group.fields
|
|
94
|
+
addFieldProps(properties, c.group.fields);
|
|
79
95
|
const name = `${prefix}__${c.key}`;
|
|
80
96
|
return new EntitySchema({ name, tableName: name, properties });
|
|
81
97
|
}
|
|
@@ -186,12 +202,11 @@ export const RecordActivitySchema = new EntitySchema({
|
|
|
186
202
|
last_viewed_at: { type: 'text', nullable: true },
|
|
187
203
|
},
|
|
188
204
|
});
|
|
189
|
-
export const
|
|
190
|
-
name: '
|
|
191
|
-
tableName: '
|
|
205
|
+
export const ConversationMessageSchema = new EntitySchema({
|
|
206
|
+
name: '_ConversationMessage',
|
|
207
|
+
tableName: '_conversation_message',
|
|
192
208
|
properties: {
|
|
193
|
-
|
|
194
|
-
chat_id: { type: 'text', primary: true },
|
|
209
|
+
conversation_id: { type: 'text', primary: true },
|
|
195
210
|
msg_id: { type: 'text', primary: true },
|
|
196
211
|
role: { type: 'text' },
|
|
197
212
|
sender: { type: 'text', nullable: true },
|
|
@@ -201,12 +216,11 @@ export const ThreadMessageSchema = new EntitySchema({
|
|
|
201
216
|
reply_to_id: { type: 'text', nullable: true },
|
|
202
217
|
},
|
|
203
218
|
});
|
|
204
|
-
export const
|
|
205
|
-
name: '
|
|
206
|
-
tableName: '
|
|
219
|
+
export const ConversationSchema = new EntitySchema({
|
|
220
|
+
name: '_Conversation',
|
|
221
|
+
tableName: '_conversation',
|
|
207
222
|
properties: {
|
|
208
|
-
|
|
209
|
-
chat_id: { type: 'text', primary: true },
|
|
223
|
+
id: { type: 'text', primary: true },
|
|
210
224
|
agent_id: { type: 'text', nullable: true },
|
|
211
225
|
preset_id: { type: 'text', nullable: true },
|
|
212
226
|
title: { type: 'text', nullable: true },
|
|
@@ -277,27 +291,6 @@ export const ApiTokenSchema = new EntitySchema({
|
|
|
277
291
|
revoked: { type: 'integer' },
|
|
278
292
|
},
|
|
279
293
|
});
|
|
280
|
-
export const IdentityLinkSchema = new EntitySchema({
|
|
281
|
-
name: '_IdentityLink',
|
|
282
|
-
tableName: '_identity_links',
|
|
283
|
-
properties: {
|
|
284
|
-
id: { type: 'integer', primary: true, autoincrement: true },
|
|
285
|
-
connector: { type: 'text' },
|
|
286
|
-
external_id: { type: 'text' },
|
|
287
|
-
user_id: { type: 'integer' },
|
|
288
|
-
linked_at: { type: 'text' },
|
|
289
|
-
},
|
|
290
|
-
});
|
|
291
|
-
export const LinkCodeSchema = new EntitySchema({
|
|
292
|
-
name: '_LinkCode',
|
|
293
|
-
tableName: '_link_codes',
|
|
294
|
-
properties: {
|
|
295
|
-
code_hash: { type: 'text', primary: true },
|
|
296
|
-
user_id: { type: 'integer' },
|
|
297
|
-
expires_at: { type: 'text' },
|
|
298
|
-
created_at: { type: 'text' },
|
|
299
|
-
},
|
|
300
|
-
});
|
|
301
294
|
export const OAuthClientSchema = new EntitySchema({
|
|
302
295
|
name: '_OAuthClient',
|
|
303
296
|
tableName: '_oauth_clients',
|
|
@@ -353,9 +346,7 @@ export const systemEntities = [
|
|
|
353
346
|
OAuthClientSchema,
|
|
354
347
|
OAuthCodeSchema,
|
|
355
348
|
OAuthTokenSchema,
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
IdentityLinkSchema,
|
|
359
|
-
LinkCodeSchema,
|
|
349
|
+
ConversationMessageSchema,
|
|
350
|
+
ConversationSchema,
|
|
360
351
|
buildSettingsEntitySchema(SYSTEM_SETTINGS_ID, SYSTEM_SETTINGS),
|
|
361
352
|
];
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface IdentityProvider {
|
|
2
|
+
id: string;
|
|
3
|
+
label: string;
|
|
4
|
+
icon: string;
|
|
5
|
+
beginLink(userId: number): Promise<{
|
|
6
|
+
url?: string;
|
|
7
|
+
code?: string;
|
|
8
|
+
} | null>;
|
|
9
|
+
listLinks(userId: number): Promise<{
|
|
10
|
+
externalId: string;
|
|
11
|
+
linkedAt: string;
|
|
12
|
+
}[]>;
|
|
13
|
+
unlink(userId: number, externalId: string): Promise<boolean>;
|
|
14
|
+
}
|
|
15
|
+
export declare function registerIdentityProvider(p: IdentityProvider): () => void;
|
|
16
|
+
export declare function listIdentityProviders(): IdentityProvider[];
|
|
17
|
+
export declare function getIdentityProvider(id: string): IdentityProvider | undefined;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const providers = new Map();
|
|
2
|
+
export function registerIdentityProvider(p) {
|
|
3
|
+
providers.set(p.id, p);
|
|
4
|
+
return () => {
|
|
5
|
+
if (providers.get(p.id) === p)
|
|
6
|
+
providers.delete(p.id);
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
export function listIdentityProviders() {
|
|
10
|
+
return [...providers.values()].sort((a, b) => a.id.localeCompare(b.id));
|
|
11
|
+
}
|
|
12
|
+
export function getIdentityProvider(id) {
|
|
13
|
+
return providers.get(id);
|
|
14
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -8,6 +8,7 @@ import Fastify from 'fastify';
|
|
|
8
8
|
import cors from '@fastify/cors';
|
|
9
9
|
import multipart from '@fastify/multipart';
|
|
10
10
|
import fastifyStatic from '@fastify/static';
|
|
11
|
+
import { layoutToClient } from '@coffer-org/sdk/shelf';
|
|
11
12
|
import { shelfTableName } from "./entity-schema.js";
|
|
12
13
|
import { getEm, closeDb } from "./db.js";
|
|
13
14
|
import { recordCounts } from "./counts.js";
|
|
@@ -306,7 +307,7 @@ app.post('/api/system/orchestrator-diagnostics', async (req, reply) => {
|
|
|
306
307
|
return;
|
|
307
308
|
reply.send(await orchestratorDiagnostics());
|
|
308
309
|
});
|
|
309
|
-
app.get('/api/settings', async () => (await settingsGroups()).map((g) => ({ id: g.id, label: g.label, fields: g.fields })));
|
|
310
|
+
app.get('/api/settings', async () => (await settingsGroups()).map((g) => ({ id: g.id, label: g.label, fields: layoutToClient(g.fields) })));
|
|
310
311
|
app.get('/api/settings/:id', async (req, reply) => {
|
|
311
312
|
if (!requireAdmin(req, reply))
|
|
312
313
|
return;
|
|
@@ -52,7 +52,6 @@ export function flattenFields(items) {
|
|
|
52
52
|
key,
|
|
53
53
|
kind: t.kind,
|
|
54
54
|
prim: t.prim,
|
|
55
|
-
required: t.required,
|
|
56
55
|
...(t.agent ? { agent: t.agent } : {}),
|
|
57
56
|
...(t.relation ? { relation: t.relation } : {}),
|
|
58
57
|
...(t.options ? { options: t.options } : {}),
|
|
@@ -69,7 +68,6 @@ export function flattenFields(items) {
|
|
|
69
68
|
key: it.key,
|
|
70
69
|
kind: 'group',
|
|
71
70
|
prim: 'group',
|
|
72
|
-
required: it.required === true,
|
|
73
71
|
...(it.agent ? { agent: it.agent } : {}),
|
|
74
72
|
...(it.multiple ? { multiple: true } : {}),
|
|
75
73
|
fields: flattenFields(it.fields),
|
package/dist/mutate.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { serialize } from '@mikro-orm/core';
|
|
2
2
|
import { buildZodObject, buildZodObjectPartial, fieldEntries, collectionGroups } from '@coffer-org/sdk/shelf';
|
|
3
3
|
import { isJsonStored, decodeVmsg } from '@coffer-org/sdk/fields';
|
|
4
|
-
import { resolveFlag } from '@coffer-org/sdk/condition';
|
|
5
4
|
import { getEm } from "./db.js";
|
|
6
5
|
import { selectRows } from "./read-rows.js";
|
|
7
6
|
import { normalizeFileFields, dropUnchangedFileFields, touchesFileFields } from "./file-fields.js";
|
|
@@ -190,20 +189,6 @@ export async function updateRecord(m, entityName, id, input, ctx, afterBase) {
|
|
|
190
189
|
throw new ValidationError(fileIssues);
|
|
191
190
|
const { base, collections } = splitCollections(m, { ...patchData });
|
|
192
191
|
const merged = { ...existing, ...base };
|
|
193
|
-
const reqIssues = [];
|
|
194
|
-
for (const [key, f] of fieldEntries(m.fields)) {
|
|
195
|
-
if (f.required === true)
|
|
196
|
-
continue;
|
|
197
|
-
if (key.includes('__'))
|
|
198
|
-
continue;
|
|
199
|
-
if (!resolveFlag(f.required, merged))
|
|
200
|
-
continue;
|
|
201
|
-
const v = merged[key];
|
|
202
|
-
if (v == null || v === '' || (Array.isArray(v) && v.length === 0))
|
|
203
|
-
reqIssues.push({ field: key, code: 'required', path: [key] });
|
|
204
|
-
}
|
|
205
|
-
if (reqIssues.length)
|
|
206
|
-
throw new ValidationError(reqIssues);
|
|
207
192
|
const storedWithMeta = hasStoredPartWork(m.fields)
|
|
208
193
|
? await readCollectionsWithMeta(tx, m, id)
|
|
209
194
|
: { collections: {}, meta: {} };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { TurnSink } from './types.ts';
|
|
2
|
+
import type { RenderFn } from './live-message.ts';
|
|
3
|
+
export interface DraftChannelOps {
|
|
4
|
+
draft(text: string): Promise<void>;
|
|
5
|
+
send(text: string): Promise<string | null>;
|
|
6
|
+
}
|
|
7
|
+
export interface DraftSinkOpts {
|
|
8
|
+
ops: DraftChannelOps;
|
|
9
|
+
throttleMs: number;
|
|
10
|
+
keepAliveMs: number;
|
|
11
|
+
render: RenderFn;
|
|
12
|
+
preview: (r: {
|
|
13
|
+
text: string | null;
|
|
14
|
+
reasoning: string | null;
|
|
15
|
+
}) => string;
|
|
16
|
+
maxLength: number;
|
|
17
|
+
}
|
|
18
|
+
export declare function makeDraftSink(o: DraftSinkOpts): TurnSink;
|