@coffer-org/server 2.6.0 → 2.7.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.
@@ -1,5 +1,8 @@
1
1
  import type { ShelfDef } from '@coffer-org/sdk/shelf';
2
+ import type { LayoutEl } from '@coffer-org/sdk/fields';
2
3
  export declare function dtStringToDate(s: string): Date;
3
4
  export declare function dateToDtString(d: Date): string;
4
- export declare function encodeTemporal(m: ShelfDef, row: Record<string, unknown>): Record<string, unknown>;
5
- export declare function decodeTemporal(m: ShelfDef, row: Record<string, unknown>): Record<string, unknown>;
5
+ export declare function encodeTemporalAt(fields: LayoutEl[], row: Record<string, unknown>): Record<string, unknown>;
6
+ export declare function decodeTemporalAt(fields: LayoutEl[], row: Record<string, unknown>): Record<string, unknown>;
7
+ export declare const encodeTemporal: (m: ShelfDef, row: Record<string, unknown>) => Record<string, unknown>;
8
+ export declare const decodeTemporal: (m: ShelfDef, row: Record<string, unknown>) => Record<string, unknown>;
package/dist/temporal.js CHANGED
@@ -7,22 +7,22 @@ export function dateToDtString(d) {
7
7
  return (`${d.getUTCFullYear()}-${p(d.getUTCMonth() + 1)}-${p(d.getUTCDate())}` +
8
8
  `T${p(d.getUTCHours())}:${p(d.getUTCMinutes())}`);
9
9
  }
10
- function datetimeKeys(m) {
11
- const fm = fieldMap(m.fields);
10
+ function datetimeKeysAt(fields) {
11
+ const fm = fieldMap(fields);
12
12
  return Object.keys(fm).filter((k) => fm[k]?.column === 'datetime');
13
13
  }
14
- export function encodeTemporal(m, row) {
14
+ export function encodeTemporalAt(fields, row) {
15
15
  const out = { ...row };
16
- for (const k of datetimeKeys(m)) {
16
+ for (const k of datetimeKeysAt(fields)) {
17
17
  const v = out[k];
18
18
  if (typeof v === 'string' && v)
19
19
  out[k] = dtStringToDate(v);
20
20
  }
21
21
  return out;
22
22
  }
23
- export function decodeTemporal(m, row) {
23
+ export function decodeTemporalAt(fields, row) {
24
24
  const out = { ...row };
25
- for (const k of datetimeKeys(m)) {
25
+ for (const k of datetimeKeysAt(fields)) {
26
26
  const v = out[k];
27
27
  if (v instanceof Date)
28
28
  out[k] = dateToDtString(v);
@@ -31,3 +31,5 @@ export function decodeTemporal(m, row) {
31
31
  }
32
32
  return out;
33
33
  }
34
+ export const encodeTemporal = (m, row) => encodeTemporalAt(m.fields, row);
35
+ export const decodeTemporal = (m, row) => decodeTemporalAt(m.fields, row);
@@ -0,0 +1,8 @@
1
+ export interface ThreadSelection {
2
+ agentId: string | null;
3
+ presetId: string | null;
4
+ }
5
+ export declare function getThreadState(connector: string, chatId: string): Promise<ThreadSelection>;
6
+ export declare function setThreadState(connector: string, chatId: string, patch: Partial<ThreadSelection>): Promise<void>;
7
+ export declare function readAndTouchThreadState(connector: string, chatId: string): Promise<ThreadSelection>;
8
+ export declare function pruneThreadState(connector: string, cutoffIso: string): Promise<void>;
@@ -0,0 +1,39 @@
1
+ import { getEm } from "./db.js";
2
+ const EMPTY = { agentId: null, presetId: null };
3
+ export async function getThreadState(connector, chatId) {
4
+ const em = getEm().fork();
5
+ const row = (await em.findOne('_ThreadState', { connector, chat_id: chatId }));
6
+ if (!row)
7
+ return { ...EMPTY };
8
+ return { agentId: row.agent_id, presetId: row.preset_id };
9
+ }
10
+ export async function setThreadState(connector, chatId, patch) {
11
+ const em = getEm().fork();
12
+ const current = await getThreadState(connector, chatId);
13
+ const next = {
14
+ agentId: 'agentId' in patch ? (patch.agentId ?? null) : current.agentId,
15
+ presetId: 'presetId' in patch ? (patch.presetId ?? null) : current.presetId,
16
+ };
17
+ const existing = await em.findOne('_ThreadState', { connector, chat_id: chatId });
18
+ const data = { agent_id: next.agentId, preset_id: next.presetId, updated_at: new Date().toISOString() };
19
+ if (existing) {
20
+ em.assign(existing, data);
21
+ }
22
+ else {
23
+ em.persist(em.create('_ThreadState', { connector, chat_id: chatId, ...data }));
24
+ }
25
+ await em.flush();
26
+ }
27
+ export async function readAndTouchThreadState(connector, chatId) {
28
+ const em = getEm().fork();
29
+ const row = (await em.findOne('_ThreadState', { connector, chat_id: chatId }));
30
+ if (!row)
31
+ return { ...EMPTY };
32
+ em.assign(row, { updated_at: new Date().toISOString() });
33
+ await em.flush();
34
+ return { agentId: row.agent_id, presetId: row.preset_id };
35
+ }
36
+ export async function pruneThreadState(connector, cutoffIso) {
37
+ const em = getEm().fork();
38
+ await em.nativeDelete('_ThreadState', { connector, updated_at: { $lt: cutoffIso } });
39
+ }
@@ -51,7 +51,9 @@ export async function listThreadMessages(connector, chatId, limit = 200) {
51
51
  const em = getEm().fork();
52
52
  const visible = (await em.find('_ThreadMessage', { connector, chat_id: chatId, role: { $ne: 'reasoning' } }, { orderBy: { ts: 'desc', msg_id: 'desc' }, limit }));
53
53
  const oldest = visible.at(-1)?.ts;
54
- const reasoning = oldest === undefined ? [] : (await em.find('_ThreadMessage', { connector, chat_id: chatId, role: 'reasoning', ts: { $gte: oldest - 1 } }, { orderBy: { ts: 'desc', msg_id: 'desc' } }));
54
+ const reasoning = oldest === undefined
55
+ ? []
56
+ : (await em.find('_ThreadMessage', { connector, chat_id: chatId, role: 'reasoning', ts: { $gte: oldest - 1 } }, { orderBy: { ts: 'desc', msg_id: 'desc' } }));
55
57
  return [...visible, ...reasoning]
56
58
  .sort((a, b) => b.ts - a.ts || (a.msg_id < b.msg_id ? 1 : a.msg_id > b.msg_id ? -1 : 0))
57
59
  .reverse()
package/dist/uploads.js CHANGED
@@ -28,7 +28,7 @@ export async function saveUploadBytes(bytes, opts = {}) {
28
28
  if (bytes.byteLength > maxBytes)
29
29
  throw new Error(`upload exceeds ${maxBytes} bytes`);
30
30
  const originalExt = opts.originalName ? extname(basename(opts.originalName)).toLowerCase() : '';
31
- const ext = originalExt && /^[.][a-z0-9]{1,10}$/.test(originalExt) ? originalExt : MIME_EXT[opts.mime ?? ''] ?? '';
31
+ const ext = originalExt && /^[.][a-z0-9]{1,10}$/.test(originalExt) ? originalExt : (MIME_EXT[opts.mime ?? ''] ?? '');
32
32
  const name = `${randomUUID()}${ext}`;
33
33
  await writeFile(join(uploadsDir(), name), bytes);
34
34
  const mime = opts.mime === 'application/octet-stream' ? undefined : opts.mime;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coffer-org/server",
3
- "version": "2.6.0",
3
+ "version": "2.7.0",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": ">=24"
@@ -25,7 +25,7 @@
25
25
  },
26
26
  "dependencies": {
27
27
  "@coffer-org/core": "^2.1.4",
28
- "@coffer-org/sdk": "^2.1.3",
28
+ "@coffer-org/sdk": "^2.2.1",
29
29
  "@extractus/oembed-extractor": "^4.1.0",
30
30
  "@fastify/cors": "^11.2.0",
31
31
  "@fastify/multipart": "^10.0.0",