@wfm-cn/dsh-storage 0.0.0-stage → 0.3.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/CHANGELOG.md +23 -0
- package/LICENSE +21 -0
- package/README.md +137 -2
- package/cordis.patch.yml +3 -0
- package/lib/backends/database.js +578 -0
- package/lib/backfill.js +331 -0
- package/lib/client/index.js +79 -0
- package/lib/client/panel.js +299 -0
- package/lib/client/toast.js +66 -0
- package/lib/client.js +1 -0
- package/lib/compaction.js +108 -0
- package/lib/config.js +171 -0
- package/lib/ddl.js +323 -0
- package/lib/generated/mysql/browser.js +17 -0
- package/lib/generated/mysql/client.js +34 -0
- package/lib/generated/mysql/commonInputTypes.js +10 -0
- package/lib/generated/mysql/enums.js +10 -0
- package/lib/generated/mysql/internal/class.js +49 -0
- package/lib/generated/mysql/internal/prismaNamespace.js +146 -0
- package/lib/generated/mysql/internal/prismaNamespaceBrowser.js +117 -0
- package/lib/generated/mysql/models/AiChatHistory.js +1 -0
- package/lib/generated/mysql/models/AiMessage.js +1 -0
- package/lib/generated/mysql/models.js +1 -0
- package/lib/generated/postgresql/browser.js +17 -0
- package/lib/generated/postgresql/client.js +34 -0
- package/lib/generated/postgresql/commonInputTypes.js +10 -0
- package/lib/generated/postgresql/enums.js +10 -0
- package/lib/generated/postgresql/internal/class.js +49 -0
- package/lib/generated/postgresql/internal/prismaNamespace.js +131 -0
- package/lib/generated/postgresql/internal/prismaNamespaceBrowser.js +102 -0
- package/lib/generated/postgresql/models/AiChatHistory.js +1 -0
- package/lib/generated/postgresql/models/AiMessage.js +1 -0
- package/lib/generated/postgresql/models.js +1 -0
- package/lib/generated/sqlite/browser.js +17 -0
- package/lib/generated/sqlite/client.js +34 -0
- package/lib/generated/sqlite/commonInputTypes.js +10 -0
- package/lib/generated/sqlite/enums.js +10 -0
- package/lib/generated/sqlite/internal/class.js +49 -0
- package/lib/generated/sqlite/internal/prismaNamespace.js +128 -0
- package/lib/generated/sqlite/internal/prismaNamespaceBrowser.js +99 -0
- package/lib/generated/sqlite/models/AiChatHistory.js +1 -0
- package/lib/generated/sqlite/models/AiMessage.js +1 -0
- package/lib/generated/sqlite/models.js +1 -0
- package/lib/generated/sqlserver/browser.js +17 -0
- package/lib/generated/sqlserver/client.js +34 -0
- package/lib/generated/sqlserver/commonInputTypes.js +10 -0
- package/lib/generated/sqlserver/enums.js +10 -0
- package/lib/generated/sqlserver/internal/class.js +49 -0
- package/lib/generated/sqlserver/internal/prismaNamespace.js +119 -0
- package/lib/generated/sqlserver/internal/prismaNamespaceBrowser.js +90 -0
- package/lib/generated/sqlserver/models/AiChatHistory.js +1 -0
- package/lib/generated/sqlserver/models/AiMessage.js +1 -0
- package/lib/generated/sqlserver/models.js +1 -0
- package/lib/health.js +180 -0
- package/lib/index.js +681 -0
- package/lib/memory-tools.js +365 -0
- package/lib/notify.js +56 -0
- package/lib/projector.js +191 -0
- package/lib/status-route.js +172 -0
- package/lib/types/backends/database.d.ts +78 -0
- package/lib/types/backfill.d.ts +67 -0
- package/lib/types/client/index.d.ts +12 -0
- package/lib/types/client/panel.d.ts +79 -0
- package/lib/types/client/toast.d.ts +38 -0
- package/lib/types/compaction.d.ts +3 -0
- package/lib/types/config.d.ts +52 -0
- package/lib/types/ddl.d.ts +49 -0
- package/lib/types/generated/mysql/browser.d.ts +14 -0
- package/lib/types/generated/mysql/client.d.ts +33 -0
- package/lib/types/generated/mysql/commonInputTypes.d.ts +396 -0
- package/lib/types/generated/mysql/enums.d.ts +1 -0
- package/lib/types/generated/mysql/internal/class.d.ts +143 -0
- package/lib/types/generated/mysql/internal/prismaNamespace.d.ts +709 -0
- package/lib/types/generated/mysql/internal/prismaNamespaceBrowser.d.ts +112 -0
- package/lib/types/generated/mysql/models/AiChatHistory.d.ts +1258 -0
- package/lib/types/generated/mysql/models/AiMessage.d.ts +1359 -0
- package/lib/types/generated/mysql/models.d.ts +3 -0
- package/lib/types/generated/postgresql/browser.d.ts +14 -0
- package/lib/types/generated/postgresql/client.d.ts +33 -0
- package/lib/types/generated/postgresql/commonInputTypes.d.ts +392 -0
- package/lib/types/generated/postgresql/enums.d.ts +1 -0
- package/lib/types/generated/postgresql/internal/class.d.ts +143 -0
- package/lib/types/generated/postgresql/internal/prismaNamespace.d.ts +728 -0
- package/lib/types/generated/postgresql/internal/prismaNamespaceBrowser.d.ts +95 -0
- package/lib/types/generated/postgresql/models/AiChatHistory.d.ts +1373 -0
- package/lib/types/generated/postgresql/models/AiMessage.d.ts +1496 -0
- package/lib/types/generated/postgresql/models.d.ts +3 -0
- package/lib/types/generated/sqlite/browser.d.ts +14 -0
- package/lib/types/generated/sqlite/client.d.ts +33 -0
- package/lib/types/generated/sqlite/commonInputTypes.d.ts +373 -0
- package/lib/types/generated/sqlite/enums.d.ts +1 -0
- package/lib/types/generated/sqlite/internal/class.d.ts +143 -0
- package/lib/types/generated/sqlite/internal/prismaNamespace.d.ts +705 -0
- package/lib/types/generated/sqlite/internal/prismaNamespaceBrowser.d.ts +92 -0
- package/lib/types/generated/sqlite/models/AiChatHistory.d.ts +1371 -0
- package/lib/types/generated/sqlite/models/AiMessage.d.ts +1493 -0
- package/lib/types/generated/sqlite/models.d.ts +3 -0
- package/lib/types/generated/sqlserver/browser.d.ts +14 -0
- package/lib/types/generated/sqlserver/client.d.ts +33 -0
- package/lib/types/generated/sqlserver/commonInputTypes.d.ts +333 -0
- package/lib/types/generated/sqlserver/enums.d.ts +1 -0
- package/lib/types/generated/sqlserver/internal/class.d.ts +143 -0
- package/lib/types/generated/sqlserver/internal/prismaNamespace.d.ts +669 -0
- package/lib/types/generated/sqlserver/internal/prismaNamespaceBrowser.d.ts +80 -0
- package/lib/types/generated/sqlserver/models/AiChatHistory.d.ts +1257 -0
- package/lib/types/generated/sqlserver/models/AiMessage.d.ts +1375 -0
- package/lib/types/generated/sqlserver/models.d.ts +3 -0
- package/lib/types/health.d.ts +52 -0
- package/lib/types/index.d.ts +91 -0
- package/lib/types/memory-tools.d.ts +168 -0
- package/lib/types/notify.d.ts +35 -0
- package/lib/types/projector.d.ts +51 -0
- package/lib/types/status-route.d.ts +48 -0
- package/lib/types/types.d.ts +58 -0
- package/lib/types.js +8 -0
- package/package.json +127 -4
- package/preview.png +0 -0
- package/prisma/schema.mysql.prisma +78 -0
- package/prisma/schema.postgresql.prisma +70 -0
- package/prisma/schema.sqlite.prisma +70 -0
- package/prisma/schema.sqlserver.prisma +76 -0
package/lib/config.js
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standalone sync configuration persisted as JSON at
|
|
3
|
+
* `$DSH_HOME/dsh-storage.json` (DSH_HOME env var, default ~/.dsh).
|
|
4
|
+
*
|
|
5
|
+
* The settings panel writes this file; the backend watches it to rebuild
|
|
6
|
+
* database connections without a restart. Loading never throws: corrupt or
|
|
7
|
+
* wrong-shape files fall back to defaults with an error flag.
|
|
8
|
+
*/
|
|
9
|
+
import { chmodSync, readFileSync, renameSync, watch, writeFileSync } from 'node:fs';
|
|
10
|
+
import { hostname, homedir } from 'node:os';
|
|
11
|
+
import { basename, dirname, join } from 'node:path';
|
|
12
|
+
/**
|
|
13
|
+
* Default device identifier: the OS hostname, truncated to the column width
|
|
14
|
+
* (VARCHAR(100)). Empty hostnames (theoretically possible) fall back to a
|
|
15
|
+
* stable placeholder so rows are never stamped with ''.
|
|
16
|
+
*/
|
|
17
|
+
export function defaultDevice() {
|
|
18
|
+
const name = hostname().trim().slice(0, 100);
|
|
19
|
+
return name || 'unknown-device';
|
|
20
|
+
}
|
|
21
|
+
export const DEFAULT_CONFIG = {
|
|
22
|
+
enabled: false,
|
|
23
|
+
provider: 'mysql',
|
|
24
|
+
host: '127.0.0.1',
|
|
25
|
+
port: 3306,
|
|
26
|
+
user: '',
|
|
27
|
+
password: '',
|
|
28
|
+
database: '',
|
|
29
|
+
timezone: '+08:00',
|
|
30
|
+
device: defaultDevice(),
|
|
31
|
+
};
|
|
32
|
+
/** Resolve the DSH home directory: $DSH_HOME or ~/.dsh. */
|
|
33
|
+
export function dshHome() {
|
|
34
|
+
return process.env.DSH_HOME || join(homedir(), '.dsh');
|
|
35
|
+
}
|
|
36
|
+
export function defaultConfigPath() {
|
|
37
|
+
return join(dshHome(), 'dsh-storage.json');
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Parse a config timezone string ('+08:00', '-05:00', '+05:30', 'Z'/'UTC')
|
|
41
|
+
* into signed minutes east of UTC. Invalid or absent input → 0 (the caller
|
|
42
|
+
* decides whether to warn). This mirrors what the patched
|
|
43
|
+
* @prisma/adapter-mariadb reads via globalThis.__DSH_STORAGE_TZ_OFFSET_MIN__.
|
|
44
|
+
*/
|
|
45
|
+
export function tzOffsetMinutes(tz) {
|
|
46
|
+
if (tz === 'UTC' || tz === 'Z')
|
|
47
|
+
return 0;
|
|
48
|
+
const m = /^[+-](\d{2}):(\d{2})$/.exec(tz);
|
|
49
|
+
if (!m)
|
|
50
|
+
return 0;
|
|
51
|
+
const sign = tz[0] === '-' ? -1 : 1;
|
|
52
|
+
return sign * (Number(m[1]) * 60 + Number(m[2]));
|
|
53
|
+
}
|
|
54
|
+
/** Valid TCP port for the form: a finite integer in [0, 65535] (0 allowed for sqlite configs). */
|
|
55
|
+
export function isValidPort(value) {
|
|
56
|
+
return (typeof value === 'number' &&
|
|
57
|
+
Number.isInteger(value) &&
|
|
58
|
+
value >= 0 &&
|
|
59
|
+
value <= 65535);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* First shape problem with a candidate sync config, or undefined when valid.
|
|
63
|
+
* Field-specific reasons keep RPC rejections honest ('cfg.port must be…'
|
|
64
|
+
* instead of a generic 'requires a cfg object').
|
|
65
|
+
*/
|
|
66
|
+
export function syncConfigProblem(value) {
|
|
67
|
+
if (typeof value !== 'object' || value === null)
|
|
68
|
+
return 'cfg must be an object';
|
|
69
|
+
const c = value;
|
|
70
|
+
if (typeof c.enabled !== 'boolean')
|
|
71
|
+
return 'cfg.enabled must be a boolean';
|
|
72
|
+
if (c.provider !== 'mysql' &&
|
|
73
|
+
c.provider !== 'postgresql' &&
|
|
74
|
+
c.provider !== 'sqlite' &&
|
|
75
|
+
c.provider !== 'sqlserver')
|
|
76
|
+
return 'cfg.provider must be one of mysql/postgresql/sqlite/sqlserver';
|
|
77
|
+
if (typeof c.host !== 'string')
|
|
78
|
+
return 'cfg.host must be a string';
|
|
79
|
+
if (!isValidPort(c.port))
|
|
80
|
+
return 'cfg.port must be an integer between 0 and 65535 (NaN/empty is not a port)';
|
|
81
|
+
if (typeof c.user !== 'string')
|
|
82
|
+
return 'cfg.user must be a string';
|
|
83
|
+
if (typeof c.password !== 'string')
|
|
84
|
+
return 'cfg.password must be a string';
|
|
85
|
+
if (typeof c.database !== 'string')
|
|
86
|
+
return 'cfg.database must be a string';
|
|
87
|
+
if (typeof c.timezone !== 'string')
|
|
88
|
+
return 'cfg.timezone must be a string';
|
|
89
|
+
// `device` is deliberately NOT validated: configs written before this
|
|
90
|
+
// field existed (and hand-edited files) load with the hostname default
|
|
91
|
+
// instead of surfacing a missing-field error.
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
function isSyncConfig(value) {
|
|
95
|
+
return syncConfigProblem(value) === undefined;
|
|
96
|
+
}
|
|
97
|
+
export function loadConfig(path = defaultConfigPath()) {
|
|
98
|
+
try {
|
|
99
|
+
const raw = readFileSync(path, 'utf-8');
|
|
100
|
+
let parsed;
|
|
101
|
+
try {
|
|
102
|
+
parsed = JSON.parse(raw);
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return { config: { ...DEFAULT_CONFIG }, error: 'parse' };
|
|
106
|
+
}
|
|
107
|
+
if (!isSyncConfig(parsed)) {
|
|
108
|
+
return { config: { ...DEFAULT_CONFIG }, error: 'missing-field' };
|
|
109
|
+
}
|
|
110
|
+
return { config: withDevice(parsed) };
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
// Missing or unreadable file → plain defaults, no error.
|
|
114
|
+
return { config: { ...DEFAULT_CONFIG } };
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Fill in a missing/empty `device` on a validated config (pre-device JSON
|
|
119
|
+
* files load without a missing-field error; the hostname default stamps
|
|
120
|
+
* their rows). A stored non-empty string wins — that is the multi-device case.
|
|
121
|
+
*/
|
|
122
|
+
function withDevice(c) {
|
|
123
|
+
const device = typeof c.device === 'string' ? c.device.trim().slice(0, 100) : '';
|
|
124
|
+
return device ? { ...c, device } : { ...c, device: defaultDevice() };
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Atomic write: temp file in the same directory + rename. The file holds the
|
|
128
|
+
* database password, so it is created owner-only (0600) — writeFileSync's
|
|
129
|
+
* mode is masked by umask, so chmod the temp file explicitly before rename
|
|
130
|
+
* (rename keeps the temp file's mode, so rewrites stay 0600 too).
|
|
131
|
+
*/
|
|
132
|
+
export function saveConfig(c, path = defaultConfigPath()) {
|
|
133
|
+
const tmp = join(dirname(path), `.${basename(path)}.${process.pid}.${Date.now()}.tmp`);
|
|
134
|
+
writeFileSync(tmp, JSON.stringify(c, null, 2) + '\n', { mode: 0o600 });
|
|
135
|
+
chmodSync(tmp, 0o600);
|
|
136
|
+
renameSync(tmp, path);
|
|
137
|
+
}
|
|
138
|
+
/** Watch the config file; bursts are debounced (300ms). Returns a disposer. */
|
|
139
|
+
export function watchConfig(path, cb) {
|
|
140
|
+
let timer;
|
|
141
|
+
const watcher = watch(dirname(path), (event, filename) => {
|
|
142
|
+
if (filename !== basename(path))
|
|
143
|
+
return;
|
|
144
|
+
if (event !== 'change' && event !== 'rename')
|
|
145
|
+
return;
|
|
146
|
+
if (timer)
|
|
147
|
+
clearTimeout(timer);
|
|
148
|
+
timer = setTimeout(() => {
|
|
149
|
+
timer = undefined;
|
|
150
|
+
cb(loadConfig(path).config);
|
|
151
|
+
}, 300);
|
|
152
|
+
});
|
|
153
|
+
return () => {
|
|
154
|
+
if (timer)
|
|
155
|
+
clearTimeout(timer);
|
|
156
|
+
watcher.close();
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
/** Percent-encode a URL component (protects / : @ % etc.). */
|
|
160
|
+
function enc(s) {
|
|
161
|
+
return encodeURIComponent(s);
|
|
162
|
+
}
|
|
163
|
+
/** Build a connection URL from discrete form fields; sqlite → file:<path>. */
|
|
164
|
+
export function configToUrl(c) {
|
|
165
|
+
// Percent-encode each path segment: spaces/?/#/% would otherwise be parsed
|
|
166
|
+
// as URL structure (query/fragment) or misread by the sqlite adapter.
|
|
167
|
+
if (c.provider === 'sqlite')
|
|
168
|
+
return `file:${c.database.split('/').map(enc).join('/')}`;
|
|
169
|
+
const auth = c.user || c.password ? `${enc(c.user)}:${enc(c.password)}@` : '';
|
|
170
|
+
return `${c.provider}://${auth}${c.host}:${c.port}/${enc(c.database)}`;
|
|
171
|
+
}
|
package/lib/ddl.js
ADDED
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Table creation for the settings panel's 「建表」 button.
|
|
3
|
+
*
|
|
4
|
+
* The DDL is EMBEDDED in the package (brief: “SQL 内嵌于包”): `prisma` is a
|
|
5
|
+
* devDependency, so end users don't have the CLI — shelling out to
|
|
6
|
+
* `npx prisma db push` would download prisma from the network at
|
|
7
|
+
* button-click (slow, offline-fail). Instead, the statements prisma emits
|
|
8
|
+
* were captured locally (Prisma 7.9 `prisma db push` against a throwaway
|
|
9
|
+
* database per provider) and are executed over the same driver-adapter
|
|
10
|
+
* connection path the health probe uses (`createAdapter` → `connect()` →
|
|
11
|
+
* raw execution) — no prisma CLI, no network, no generated client.
|
|
12
|
+
*
|
|
13
|
+
* Providers with embedded DDL: mysql, sqlite (captured from
|
|
14
|
+
* prisma/schema.{mysql,sqlite}.prisma). postgresql/sqlserver fall back to
|
|
15
|
+
* the packaged-schema `prisma db push` route; on failure the `fail:`
|
|
16
|
+
* message states that the prisma CLI is required.
|
|
17
|
+
*
|
|
18
|
+
* Every failure maps to a `fail:<reason>` string; this never throws.
|
|
19
|
+
*/
|
|
20
|
+
import { execFile } from 'node:child_process';
|
|
21
|
+
import { fileURLToPath } from 'node:url';
|
|
22
|
+
import { dirname, join } from 'node:path';
|
|
23
|
+
import { createAdapter } from './backends/database.js';
|
|
24
|
+
import { defaultDevice, configToUrl } from './config.js';
|
|
25
|
+
/**
|
|
26
|
+
* MySQL DDL — captured from Prisma 7.9 `db push` of
|
|
27
|
+
* prisma/schema.mysql.prisma (MySQL 8.4) via SHOW CREATE TABLE. Column
|
|
28
|
+
* types must match the schema exactly: char(36) ids, varchar lengths,
|
|
29
|
+
* longtext content, json columns, timestamp(0) message timestamps,
|
|
30
|
+
* datetime(6) history timestamps. ai_chat_histories is created first —
|
|
31
|
+
* ai_messages carries a foreign key into it.
|
|
32
|
+
*/
|
|
33
|
+
export const MYSQL_DDL = [
|
|
34
|
+
`CREATE TABLE IF NOT EXISTS \`ai_chat_histories\` (
|
|
35
|
+
\`id\` char(36) COLLATE utf8mb4_unicode_ci NOT NULL,
|
|
36
|
+
\`session_id\` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
|
|
37
|
+
\`title\` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
|
38
|
+
\`summary\` text COLLATE utf8mb4_unicode_ci,
|
|
39
|
+
\`message_count\` int NOT NULL DEFAULT '0',
|
|
40
|
+
\`total_tokens\` bigint NOT NULL DEFAULT '0',
|
|
41
|
+
\`first_message_at\` datetime(6) DEFAULT NULL,
|
|
42
|
+
\`last_message_at\` datetime(6) DEFAULT NULL,
|
|
43
|
+
\`metadata\` json DEFAULT NULL,
|
|
44
|
+
\`deleted_at\` datetime(6) DEFAULT NULL,
|
|
45
|
+
\`device\` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
|
46
|
+
PRIMARY KEY (\`id\`),
|
|
47
|
+
KEY \`idx_chat_history_session\` (\`session_id\`),
|
|
48
|
+
KEY \`idx_chat_history_deleted\` (\`deleted_at\`)
|
|
49
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci`,
|
|
50
|
+
`CREATE TABLE IF NOT EXISTS \`ai_messages\` (
|
|
51
|
+
\`id\` char(36) COLLATE utf8mb4_unicode_ci NOT NULL,
|
|
52
|
+
\`session_id\` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
|
|
53
|
+
\`history_id\` char(36) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
|
54
|
+
\`type\` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL,
|
|
55
|
+
\`content\` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
|
|
56
|
+
\`thoughts\` json DEFAULT NULL,
|
|
57
|
+
\`model\` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
|
58
|
+
\`tokens\` json DEFAULT NULL,
|
|
59
|
+
\`tool_calls\` json DEFAULT NULL,
|
|
60
|
+
\`agent_id\` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
|
61
|
+
\`metadata\` json DEFAULT NULL,
|
|
62
|
+
\`created_at\` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
63
|
+
\`updated_at\` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
64
|
+
\`deleted_at\` timestamp NULL DEFAULT NULL,
|
|
65
|
+
\`device\` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
|
66
|
+
PRIMARY KEY (\`id\`),
|
|
67
|
+
KEY \`idx_session\` (\`session_id\`),
|
|
68
|
+
KEY \`idx_history\` (\`history_id\`),
|
|
69
|
+
KEY \`idx_created\` (\`created_at\`),
|
|
70
|
+
KEY \`idx_deleted\` (\`deleted_at\`),
|
|
71
|
+
KEY \`idx_agent\` (\`agent_id\`),
|
|
72
|
+
KEY \`idx_session_agent_type_created\` (\`session_id\`,\`agent_id\`,\`type\`,\`created_at\`),
|
|
73
|
+
KEY \`idx_tool_call_lookup\` (\`session_id\`,\`history_id\`,\`deleted_at\`,\`type\`,\`created_at\`,\`id\`),
|
|
74
|
+
KEY \`idx_archive_recovery\` (\`session_id\`,\`deleted_at\`,\`updated_at\`,\`id\`),
|
|
75
|
+
CONSTRAINT \`ai_messages_history_id_fkey\` FOREIGN KEY (\`history_id\`) REFERENCES \`ai_chat_histories\` (\`id\`) ON UPDATE CASCADE
|
|
76
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci`,
|
|
77
|
+
];
|
|
78
|
+
/**
|
|
79
|
+
* SQLite DDL — captured from Prisma 7.9 `db push` of
|
|
80
|
+
* prisma/schema.sqlite.prisma (sqlite_master dump), converted to
|
|
81
|
+
* IF NOT EXISTS forms. The histories table precedes the FK-carrying
|
|
82
|
+
* messages table.
|
|
83
|
+
*/
|
|
84
|
+
export const SQLITE_DDL = [
|
|
85
|
+
`CREATE TABLE IF NOT EXISTS "ai_chat_histories" (
|
|
86
|
+
"id" TEXT NOT NULL PRIMARY KEY,
|
|
87
|
+
"session_id" TEXT NOT NULL,
|
|
88
|
+
"title" TEXT,
|
|
89
|
+
"summary" TEXT,
|
|
90
|
+
"message_count" INTEGER NOT NULL DEFAULT 0,
|
|
91
|
+
"total_tokens" BIGINT NOT NULL DEFAULT 0,
|
|
92
|
+
"first_message_at" DATETIME,
|
|
93
|
+
"last_message_at" DATETIME,
|
|
94
|
+
"metadata" JSONB,
|
|
95
|
+
"deleted_at" DATETIME,
|
|
96
|
+
"device" TEXT
|
|
97
|
+
)`,
|
|
98
|
+
`CREATE TABLE IF NOT EXISTS "ai_messages" (
|
|
99
|
+
"id" TEXT NOT NULL PRIMARY KEY,
|
|
100
|
+
"session_id" TEXT NOT NULL,
|
|
101
|
+
"history_id" TEXT,
|
|
102
|
+
"type" TEXT NOT NULL,
|
|
103
|
+
"content" TEXT NOT NULL,
|
|
104
|
+
"thoughts" JSONB,
|
|
105
|
+
"model" TEXT,
|
|
106
|
+
"tokens" JSONB,
|
|
107
|
+
"tool_calls" JSONB,
|
|
108
|
+
"agent_id" TEXT,
|
|
109
|
+
"metadata" JSONB,
|
|
110
|
+
"created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
111
|
+
"updated_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
112
|
+
"deleted_at" DATETIME,
|
|
113
|
+
"device" TEXT,
|
|
114
|
+
CONSTRAINT "ai_messages_history_id_fkey" FOREIGN KEY ("history_id") REFERENCES "ai_chat_histories" ("id") ON DELETE NO ACTION ON UPDATE CASCADE
|
|
115
|
+
)`,
|
|
116
|
+
`CREATE INDEX IF NOT EXISTS "idx_session" ON "ai_messages"("session_id")`,
|
|
117
|
+
`CREATE INDEX IF NOT EXISTS "idx_history" ON "ai_messages"("history_id")`,
|
|
118
|
+
`CREATE INDEX IF NOT EXISTS "idx_created" ON "ai_messages"("created_at")`,
|
|
119
|
+
`CREATE INDEX IF NOT EXISTS "idx_deleted" ON "ai_messages"("deleted_at")`,
|
|
120
|
+
`CREATE INDEX IF NOT EXISTS "idx_agent" ON "ai_messages"("agent_id")`,
|
|
121
|
+
`CREATE INDEX IF NOT EXISTS "idx_session_agent_type_created" ON "ai_messages"("session_id", "agent_id", "type", "created_at")`,
|
|
122
|
+
`CREATE INDEX IF NOT EXISTS "idx_tool_call_lookup" ON "ai_messages"("session_id", "history_id", "deleted_at", "type", "created_at", "id")`,
|
|
123
|
+
`CREATE INDEX IF NOT EXISTS "idx_archive_recovery" ON "ai_messages"("session_id", "deleted_at", "updated_at", "id")`,
|
|
124
|
+
`CREATE INDEX IF NOT EXISTS "idx_chat_history_session" ON "ai_chat_histories"("session_id")`,
|
|
125
|
+
`CREATE INDEX IF NOT EXISTS "idx_chat_history_deleted" ON "ai_chat_histories"("deleted_at")`,
|
|
126
|
+
];
|
|
127
|
+
const EMBEDDED_DDL = {
|
|
128
|
+
mysql: MYSQL_DDL,
|
|
129
|
+
sqlite: SQLITE_DDL,
|
|
130
|
+
};
|
|
131
|
+
/** Embedded DDL statements for a provider; undefined → prisma-CLI fallback. */
|
|
132
|
+
export function ddlStatementsFor(provider) {
|
|
133
|
+
return EMBEDDED_DDL[provider];
|
|
134
|
+
}
|
|
135
|
+
/** Create the tables by running the embedded DDL over a driver adapter. */
|
|
136
|
+
async function runEmbeddedDdl(cfg, statements) {
|
|
137
|
+
let connection;
|
|
138
|
+
try {
|
|
139
|
+
const factory = (await createAdapter({
|
|
140
|
+
provider: cfg.provider,
|
|
141
|
+
url: configToUrl(cfg),
|
|
142
|
+
timezone: cfg.timezone,
|
|
143
|
+
}));
|
|
144
|
+
connection = await factory.connect();
|
|
145
|
+
for (const statement of statements) {
|
|
146
|
+
await connection.executeRaw({ sql: statement, args: [] });
|
|
147
|
+
}
|
|
148
|
+
return 'ok';
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
152
|
+
return `fail:${message.slice(0, 300)}`;
|
|
153
|
+
}
|
|
154
|
+
finally {
|
|
155
|
+
await connection?.dispose?.().catch((error) => {
|
|
156
|
+
console.error('[dsh-storage] connection dispose failed:', error);
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
/* ------------------------------------------------------------------ *
|
|
161
|
+
* Device-column migration (multi-device shared database support).
|
|
162
|
+
* ------------------------------------------------------------------ */
|
|
163
|
+
const DEVICE_MIGRATION_DDL = [
|
|
164
|
+
'ALTER TABLE `ai_messages` ADD COLUMN `device` varchar(100) NULL',
|
|
165
|
+
'ALTER TABLE `ai_chat_histories` ADD COLUMN `device` varchar(100) NULL',
|
|
166
|
+
];
|
|
167
|
+
/**
|
|
168
|
+
* Ensure both tables carry the nullable `device` column, then stamp every
|
|
169
|
+
* still-NULL row with `device` (the current machine's identifier — at
|
|
170
|
+
* migration time this machine owns all pre-feature rows). Executed by the
|
|
171
|
+
* panel's 「建表」 (createTables) and once per mirror startup; a no-op when
|
|
172
|
+
* the columns already exist (cheap information_schema check). mysql only —
|
|
173
|
+
* the other providers gain the column via their prisma schema `db push`.
|
|
174
|
+
*
|
|
175
|
+
* Returns 'ok' or a `fail:<reason>` string; never throws.
|
|
176
|
+
*/
|
|
177
|
+
export async function ensureDeviceColumn(cfg, device = defaultDevice()) {
|
|
178
|
+
if (cfg.provider !== 'mysql')
|
|
179
|
+
return 'ok';
|
|
180
|
+
let connection;
|
|
181
|
+
try {
|
|
182
|
+
const factory = (await createAdapter({
|
|
183
|
+
provider: cfg.provider,
|
|
184
|
+
url: cfg.url,
|
|
185
|
+
timezone: cfg.timezone,
|
|
186
|
+
}));
|
|
187
|
+
connection = await factory.connect();
|
|
188
|
+
// How many of the two tables already have `device`? One aggregate
|
|
189
|
+
// query over information_schema — cheap on every mirror start.
|
|
190
|
+
const present = countOf(await runQuery(connection, `SELECT COUNT(*) AS n FROM information_schema.columns
|
|
191
|
+
WHERE table_schema = DATABASE() AND column_name = 'device'
|
|
192
|
+
AND table_name IN ('ai_messages', 'ai_chat_histories')`));
|
|
193
|
+
if (present < DEVICE_MIGRATION_DDL.length) {
|
|
194
|
+
for (const statement of DEVICE_MIGRATION_DDL)
|
|
195
|
+
await connection.executeRaw({ sql: statement, args: [] });
|
|
196
|
+
}
|
|
197
|
+
// Stamp pre-feature rows with this machine's identifier (NULL means
|
|
198
|
+
// "unknown/pre-feature" only until the owner migrates). Rows written
|
|
199
|
+
// after the feature always carry their writer's device, so this only
|
|
200
|
+
// ever touches pre-feature rows — including databases whose columns
|
|
201
|
+
// were created earlier (e.g. by prisma db push) but never stamped.
|
|
202
|
+
const unstamped = countOf(await runQuery(connection, `SELECT (SELECT COUNT(*) FROM \`ai_messages\` WHERE \`device\` IS NULL)
|
|
203
|
+
+ (SELECT COUNT(*) FROM \`ai_chat_histories\` WHERE \`device\` IS NULL) AS n`));
|
|
204
|
+
if (unstamped > 0) {
|
|
205
|
+
// The mariadb adapter's executeRaw indexes query.argTypes[i], which
|
|
206
|
+
// only the prisma engine populates — a direct adapter call with
|
|
207
|
+
// bound parameters throws. Inline a sanitized literal instead
|
|
208
|
+
// (device identifiers are hostnames; quotes/backticks/NUL are
|
|
209
|
+
// stripped before embedding).
|
|
210
|
+
const literal = sqlString(device);
|
|
211
|
+
for (const table of ['ai_messages', 'ai_chat_histories'])
|
|
212
|
+
await connection.executeRaw({
|
|
213
|
+
sql: `UPDATE \`${table}\` SET \`device\` = '${literal}' WHERE \`device\` IS NULL`,
|
|
214
|
+
args: [],
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
return 'ok';
|
|
218
|
+
}
|
|
219
|
+
catch (error) {
|
|
220
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
221
|
+
return `fail:${message.slice(0, 300)}`;
|
|
222
|
+
}
|
|
223
|
+
finally {
|
|
224
|
+
await connection?.dispose?.().catch((error) => {
|
|
225
|
+
console.error('[dsh-storage] connection dispose failed:', error);
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Sanitize a device identifier for direct embedding into the migration SQL
|
|
231
|
+
* (no bound parameters — see ensureDeviceColumn). Strips quote, backslash,
|
|
232
|
+
* backtick and NUL characters and truncates to the column width; the value
|
|
233
|
+
* is still wrapped in single quotes at the call site.
|
|
234
|
+
*/
|
|
235
|
+
export function sqlString(device) {
|
|
236
|
+
return device.replace(/['"\\`\u0000]/g, '').slice(0, 100);
|
|
237
|
+
}
|
|
238
|
+
/** Run a read query over the adapter connection (queryRaw when available). */
|
|
239
|
+
async function runQuery(connection, sql) {
|
|
240
|
+
const query = { sql, args: [] };
|
|
241
|
+
return connection.queryRaw ? connection.queryRaw(query) : connection.executeRaw(query);
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Read the scalar COUNT(*) out of an adapter query result. Shapes seen in the
|
|
245
|
+
* wild: a plain array of row objects ([[{n:2}]]) and the mariadb adapter's
|
|
246
|
+
* result set ({ columnNames, rows } with positional row arrays).
|
|
247
|
+
*/
|
|
248
|
+
function countOf(rows) {
|
|
249
|
+
let data = rows;
|
|
250
|
+
if (data && typeof data === 'object' && !Array.isArray(data))
|
|
251
|
+
data = data.rows ?? data;
|
|
252
|
+
const first = Array.isArray(data) ? data[0] : data;
|
|
253
|
+
if (Array.isArray(first))
|
|
254
|
+
return countOf(first);
|
|
255
|
+
if (first && typeof first === 'object') {
|
|
256
|
+
const value = Object.values(first)[0];
|
|
257
|
+
const count = Number(value);
|
|
258
|
+
return Number.isFinite(count) ? count : 0;
|
|
259
|
+
}
|
|
260
|
+
return Number(first) || 0;
|
|
261
|
+
}
|
|
262
|
+
/* ------------------------------------------------------------------ *
|
|
263
|
+
* prisma-CLI fallback (postgresql / sqlserver — no embedded DDL yet).
|
|
264
|
+
* ------------------------------------------------------------------ */
|
|
265
|
+
/** The package's prisma/ directory: lib/../prisma installed, src/../prisma in dev. */
|
|
266
|
+
function prismaDir() {
|
|
267
|
+
return join(dirname(fileURLToPath(import.meta.url)), '..', 'prisma');
|
|
268
|
+
}
|
|
269
|
+
function run(args, opts) {
|
|
270
|
+
return new Promise((resolve) => {
|
|
271
|
+
execFile('npx', args, { cwd: opts.cwd, timeout: opts.timeoutMs, windowsHide: true }, (error, stdout, stderr) => {
|
|
272
|
+
// execFile reports a nonzero exit as an Error carrying the streams.
|
|
273
|
+
if (error && typeof error.code !== 'number') {
|
|
274
|
+
resolve({ code: -1, stdout: String(stdout ?? ''), stderr: String(stderr ?? error.message) });
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
resolve({
|
|
278
|
+
code: error ? Number(error.code) : 0,
|
|
279
|
+
stdout: String(stdout ?? ''),
|
|
280
|
+
stderr: String(stderr ?? ''),
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
/** First non-empty line of the prisma CLI output, for a short fail: reason. */
|
|
286
|
+
function firstReason(parts) {
|
|
287
|
+
for (const part of parts) {
|
|
288
|
+
for (const line of part.split('\n')) {
|
|
289
|
+
const trimmed = line.trim();
|
|
290
|
+
if (trimmed)
|
|
291
|
+
return trimmed.slice(0, 300);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return 'unknown prisma db push failure';
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Create the ai_messages / ai_chat_histories tables for `cfg.provider`.
|
|
298
|
+
* mysql/sqlite run embedded idempotent DDL over the adapter connection;
|
|
299
|
+
* postgresql/sqlserver shell out to `prisma db push` against the packaged
|
|
300
|
+
* schema variant (requires the prisma CLI — a devDependency).
|
|
301
|
+
*/
|
|
302
|
+
export async function createTables(cfg) {
|
|
303
|
+
const statements = ddlStatementsFor(cfg.provider);
|
|
304
|
+
if (statements) {
|
|
305
|
+
const created = await runEmbeddedDdl(cfg, statements);
|
|
306
|
+
if (created !== 'ok')
|
|
307
|
+
return created;
|
|
308
|
+
// Fresh tables may predate the device column on an existing database —
|
|
309
|
+
// run the migration after the DDL (it is a no-op when columns exist).
|
|
310
|
+
return ensureDeviceColumn({ provider: cfg.provider, url: configToUrl(cfg), timezone: cfg.timezone });
|
|
311
|
+
}
|
|
312
|
+
const provider = cfg.provider;
|
|
313
|
+
const schema = join(prismaDir(), `schema.${provider}.prisma`);
|
|
314
|
+
try {
|
|
315
|
+
const { code, stdout, stderr } = await run(['prisma', 'db', 'push', '--schema', schema, '--url', configToUrl(cfg), '--accept-data-loss'], { cwd: prismaDir(), timeoutMs: 120_000 });
|
|
316
|
+
if (code === 0)
|
|
317
|
+
return 'ok';
|
|
318
|
+
return `fail:${provider} 建表需要 prisma CLI(开发依赖,未随包分发)——${firstReason([stderr, stdout])}`;
|
|
319
|
+
}
|
|
320
|
+
catch (error) {
|
|
321
|
+
return `fail:${provider} 建表需要 prisma CLI(开发依赖,未随包分发)——${error instanceof Error ? error.message : String(error)}`;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
|
|
2
|
+
/* eslint-disable */
|
|
3
|
+
// biome-ignore-all lint: generated file
|
|
4
|
+
// @ts-nocheck
|
|
5
|
+
/*
|
|
6
|
+
* This file should be your main import to use Prisma-related types and utilities in a browser.
|
|
7
|
+
* Use it to get access to models, enums, and input types.
|
|
8
|
+
*
|
|
9
|
+
* This file does not contain a `PrismaClient` class, nor several other helpers that are intended as server-side only.
|
|
10
|
+
* See `client.ts` for the standard, server-side entry point.
|
|
11
|
+
*
|
|
12
|
+
* 🟢 You can import this file directly.
|
|
13
|
+
*/
|
|
14
|
+
import * as Prisma from './internal/prismaNamespaceBrowser.js';
|
|
15
|
+
export { Prisma };
|
|
16
|
+
export * as $Enums from './enums.js';
|
|
17
|
+
export * from './enums.js';
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
|
|
2
|
+
/* eslint-disable */
|
|
3
|
+
// biome-ignore-all lint: generated file
|
|
4
|
+
// @ts-nocheck
|
|
5
|
+
/*
|
|
6
|
+
* This file should be your main import to use Prisma. Through it you get access to all the models, enums, and input types.
|
|
7
|
+
* If you're looking for something you can import in the client-side of your application, please refer to the `browser.ts` file instead.
|
|
8
|
+
*
|
|
9
|
+
* 🟢 You can import this file directly.
|
|
10
|
+
*/
|
|
11
|
+
import * as path from 'node:path';
|
|
12
|
+
import { fileURLToPath } from 'node:url';
|
|
13
|
+
globalThis['__dirname'] = path.dirname(fileURLToPath(import.meta.url));
|
|
14
|
+
import * as $Class from "./internal/class.js";
|
|
15
|
+
import * as Prisma from "./internal/prismaNamespace.js";
|
|
16
|
+
export * as $Enums from './enums.js';
|
|
17
|
+
export * from "./enums.js";
|
|
18
|
+
/**
|
|
19
|
+
* ## Prisma Client
|
|
20
|
+
*
|
|
21
|
+
* Type-safe database client for TypeScript
|
|
22
|
+
* @example
|
|
23
|
+
* ```
|
|
24
|
+
* const prisma = new PrismaClient({
|
|
25
|
+
* adapter: new PrismaPg({ connectionString: process.env.DATABASE_URL })
|
|
26
|
+
* })
|
|
27
|
+
* // Fetch zero or more AiMessages
|
|
28
|
+
* const aiMessages = await prisma.aiMessage.findMany()
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* Read more in our [docs](https://pris.ly/d/client).
|
|
32
|
+
*/
|
|
33
|
+
export const PrismaClient = $Class.getPrismaClientClass();
|
|
34
|
+
export { Prisma };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
|
|
2
|
+
/* eslint-disable */
|
|
3
|
+
// biome-ignore-all lint: generated file
|
|
4
|
+
// @ts-nocheck
|
|
5
|
+
/*
|
|
6
|
+
* This file exports various common sort, input & filter types that are not directly linked to a particular model.
|
|
7
|
+
*
|
|
8
|
+
* 🟢 You can import this file directly.
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
|
|
2
|
+
/* eslint-disable */
|
|
3
|
+
// biome-ignore-all lint: generated file
|
|
4
|
+
// @ts-nocheck
|
|
5
|
+
/*
|
|
6
|
+
* This file exports all enum related types from the schema.
|
|
7
|
+
*
|
|
8
|
+
* 🟢 You can import this file directly.
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
|
|
2
|
+
/* eslint-disable */
|
|
3
|
+
// biome-ignore-all lint: generated file
|
|
4
|
+
// @ts-nocheck
|
|
5
|
+
/*
|
|
6
|
+
* WARNING: This is an internal file that is subject to change!
|
|
7
|
+
*
|
|
8
|
+
* 🛑 Under no circumstances should you import this file directly! 🛑
|
|
9
|
+
*
|
|
10
|
+
* Please import the `PrismaClient` class from the `client.ts` file instead.
|
|
11
|
+
*/
|
|
12
|
+
import * as runtime from "@prisma/client/runtime/client";
|
|
13
|
+
const config = {
|
|
14
|
+
"previewFeatures": [],
|
|
15
|
+
"clientVersion": "7.10.1",
|
|
16
|
+
"engineVersion": "d1b3f377ca835d73fd50954a5e22128b9ebdd6b8",
|
|
17
|
+
"activeProvider": "mysql",
|
|
18
|
+
"inlineSchema": "// Prisma schema for the dsh-storage database backend — MySQL variant (Prisma 7).\n//\n// Table layout matches the source project's ai_messages / ai_chat_histories\n// (packages/core/prisma/schema.prisma) so existing data stays compatible:\n// - `type` values come from the dsh session event map\n// ('user' | 'model' | 'tool' | ...) instead of gemini-cli message roles.\n// - `agent_id` distinguishes the main agent from subagent writers.\n// - no user_id: tenancy is carried by session_id, not a column.\n//\n// The client is pre-generated at package build time (`pnpm generate`, output\n// src/generated/mysql, compiled into lib/generated) — consumers do NOT run\n// `prisma generate`. To create/upgrade tables, run `prisma db push` /\n// migrations against this file; the connection URL lives in prisma.config.ts\n// (Prisma 7 dropped datasource.url from the schema).\n\ngenerator client {\n provider = \"prisma-client\"\n output = \"../src/generated/mysql\"\n importFileExtension = \"js\"\n}\n\ndatasource db {\n provider = \"mysql\"\n}\n\n/// User and assistant messages; tool results update the assistant row's tool_calls.\nmodel AiMessage {\n id String @id @default(cuid()) @db.Char(36)\n sessionId String @map(\"session_id\") @db.VarChar(100)\n historyId String? @map(\"history_id\") @db.Char(36)\n type String @db.VarChar(50)\n content String @db.LongText\n thoughts Json?\n model String? @db.VarChar(100)\n tokens Json?\n toolCalls Json? @map(\"tool_calls\")\n agentId String? @map(\"agent_id\") @db.VarChar(100)\n metadata Json?\n createdAt DateTime @default(now()) @map(\"created_at\") @db.Timestamp(0)\n updatedAt DateTime @default(now()) @updatedAt @map(\"updated_at\") @db.Timestamp(0)\n deletedAt DateTime? @map(\"deleted_at\") @db.Timestamp(0)\n\n // Relations\n history AiChatHistory? @relation(fields: [historyId], references: [id], onDelete: NoAction)\n\n @@index([sessionId], map: \"idx_session\")\n @@index([historyId], map: \"idx_history\")\n @@index([createdAt], map: \"idx_created\")\n @@index([deletedAt], map: \"idx_deleted\")\n @@index([agentId], map: \"idx_agent\")\n @@index([sessionId, agentId, type, createdAt], map: \"idx_session_agent_type_created\")\n @@index([sessionId, historyId, deletedAt, type, createdAt, id], map: \"idx_tool_call_lookup\")\n @@index([sessionId, deletedAt, updatedAt, id], map: \"idx_archive_recovery\")\n @@map(\"ai_messages\")\n}\n\n/// Per-session rollup, maintained incrementally by the projector.\nmodel AiChatHistory {\n id String @id @default(cuid()) @db.Char(36)\n sessionId String @map(\"session_id\") @db.VarChar(100)\n title String? @db.VarChar(255)\n summary String? @db.Text\n messageCount Int @default(0) @map(\"message_count\")\n totalTokens BigInt @default(0) @map(\"total_tokens\")\n firstMessageAt DateTime? @map(\"first_message_at\") @db.DateTime(6)\n lastMessageAt DateTime? @map(\"last_message_at\") @db.DateTime(6)\n metadata Json?\n deletedAt DateTime? @map(\"deleted_at\") @db.DateTime(6)\n\n // Relations\n messages AiMessage[]\n\n @@index([sessionId], map: \"idx_chat_history_session\")\n @@index([deletedAt], map: \"idx_chat_history_deleted\")\n @@map(\"ai_chat_histories\")\n}\n",
|
|
19
|
+
"runtimeDataModel": {
|
|
20
|
+
"models": {},
|
|
21
|
+
"enums": {},
|
|
22
|
+
"types": {}
|
|
23
|
+
},
|
|
24
|
+
"parameterizationSchema": {
|
|
25
|
+
"strings": [],
|
|
26
|
+
"graph": ""
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
config.runtimeDataModel = JSON.parse("{\"models\":{\"AiMessage\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"sessionId\",\"kind\":\"scalar\",\"type\":\"String\",\"dbName\":\"session_id\"},{\"name\":\"historyId\",\"kind\":\"scalar\",\"type\":\"String\",\"dbName\":\"history_id\"},{\"name\":\"type\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"content\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"thoughts\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"model\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"tokens\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"toolCalls\",\"kind\":\"scalar\",\"type\":\"Json\",\"dbName\":\"tool_calls\"},{\"name\":\"agentId\",\"kind\":\"scalar\",\"type\":\"String\",\"dbName\":\"agent_id\"},{\"name\":\"metadata\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\",\"dbName\":\"created_at\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\",\"dbName\":\"updated_at\"},{\"name\":\"deletedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\",\"dbName\":\"deleted_at\"},{\"name\":\"history\",\"kind\":\"object\",\"type\":\"AiChatHistory\",\"relationName\":\"AiChatHistoryToAiMessage\"}],\"dbName\":\"ai_messages\",\"schema\":null},\"AiChatHistory\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"sessionId\",\"kind\":\"scalar\",\"type\":\"String\",\"dbName\":\"session_id\"},{\"name\":\"title\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"summary\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"messageCount\",\"kind\":\"scalar\",\"type\":\"Int\",\"dbName\":\"message_count\"},{\"name\":\"totalTokens\",\"kind\":\"scalar\",\"type\":\"BigInt\",\"dbName\":\"total_tokens\"},{\"name\":\"firstMessageAt\",\"kind\":\"scalar\",\"type\":\"DateTime\",\"dbName\":\"first_message_at\"},{\"name\":\"lastMessageAt\",\"kind\":\"scalar\",\"type\":\"DateTime\",\"dbName\":\"last_message_at\"},{\"name\":\"metadata\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"deletedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\",\"dbName\":\"deleted_at\"},{\"name\":\"messages\",\"kind\":\"object\",\"type\":\"AiMessage\",\"relationName\":\"AiChatHistoryToAiMessage\"}],\"dbName\":\"ai_chat_histories\",\"schema\":null}},\"enums\":{},\"types\":{}}");
|
|
30
|
+
config.parameterizationSchema = {
|
|
31
|
+
strings: JSON.parse("[\"where\",\"orderBy\",\"cursor\",\"messages\",\"_count\",\"history\",\"AiMessage.findUnique\",\"AiMessage.findUniqueOrThrow\",\"AiMessage.findFirst\",\"AiMessage.findFirstOrThrow\",\"AiMessage.findMany\",\"data\",\"AiMessage.createOne\",\"AiMessage.createMany\",\"AiMessage.updateOne\",\"AiMessage.updateMany\",\"create\",\"update\",\"AiMessage.upsertOne\",\"AiMessage.deleteOne\",\"AiMessage.deleteMany\",\"having\",\"_min\",\"_max\",\"AiMessage.groupBy\",\"AiMessage.aggregate\",\"AiChatHistory.findUnique\",\"AiChatHistory.findUniqueOrThrow\",\"AiChatHistory.findFirst\",\"AiChatHistory.findFirstOrThrow\",\"AiChatHistory.findMany\",\"AiChatHistory.createOne\",\"AiChatHistory.createMany\",\"AiChatHistory.updateOne\",\"AiChatHistory.updateMany\",\"AiChatHistory.upsertOne\",\"AiChatHistory.deleteOne\",\"AiChatHistory.deleteMany\",\"_avg\",\"_sum\",\"AiChatHistory.groupBy\",\"AiChatHistory.aggregate\",\"AND\",\"OR\",\"NOT\",\"id\",\"sessionId\",\"title\",\"summary\",\"messageCount\",\"totalTokens\",\"firstMessageAt\",\"lastMessageAt\",\"metadata\",\"deletedAt\",\"equals\",\"string_contains\",\"string_starts_with\",\"string_ends_with\",\"array_starts_with\",\"array_ends_with\",\"array_contains\",\"lt\",\"lte\",\"gt\",\"gte\",\"not\",\"in\",\"notIn\",\"contains\",\"startsWith\",\"endsWith\",\"search\",\"every\",\"some\",\"none\",\"historyId\",\"type\",\"content\",\"thoughts\",\"model\",\"tokens\",\"toolCalls\",\"agentId\",\"createdAt\",\"updatedAt\",\"is\",\"isNot\",\"connectOrCreate\",\"upsert\",\"createMany\",\"set\",\"disconnect\",\"delete\",\"connect\",\"updateMany\",\"deleteMany\",\"_relevance\",\"increment\",\"decrement\",\"multiply\",\"divide\"]"),
|
|
32
|
+
graph: "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-ACAWAAA-ACAXAAA-ACAmAAA_ACAnAAA-ACA3AgAAAAE-AgAAAAE_AgAAAAFAAgAAAAFBAgAAAAFCAgBBACFDAgAAAAREAgAAAAQNBAAAPgAgFgAAQAAgFwAAQAAgJgAAPwAgJwAAQAAgNwQAAAABPgQAAAABPwQAAAABQAQAAAABQQQAAAABQgQAPQAhQwQAAAAERAQAAAAECwQAADkAIBYAADwAIBcAADwAIDdAAAAAAT5AAAAAAT9AAAAAAUBAAAAAAUFAAAAAAUJAADsAIUNAAAAABURAAAAABQ8EAAA5ACAWAAA6ACAXAAA6ACA3gAAAAAE4AQAAAAE5AQAAAAE6AQAAAAE7gAAAAAE8gAAAAAE9gAAAAAE-gAAAAAE_gAAAAAFAgAAAAAFBgAAAAAFCgAAAAAEINwIAAAABPgIAAAABPwIAAAABQAIAAAABQQIAAAABQgIAOQAhQwIAAAAFRAIAAAAFDDeAAAAAATgBAAAAATkBAAAAAToBAAAAATuAAAAAATyAAAAAAT2AAAAAAT6AAAAAAT-AAAAAAUCAAAAAAUGAAAAAAUKAAAAAAQsEAAA5ACAWAAA8ACAXAAA8ACA3QAAAAAE-QAAAAAE_QAAAAAFAQAAAAAFBQAAAAAFCQAA7ACFDQAAAAAVEQAAAAAUIN0AAAAABPkAAAAABP0AAAAABQEAAAAABQUAAAAABQkAAPAAhQ0AAAAAFREAAAAAFDQQAAD4AIBYAAEAAIBcAAEAAICYAAD8AICcAAEAAIDcEAAAAAT4EAAAAAT8EAAAAAUAEAAAAAUEEAAAAAUIEAD0AIUMEAAAABEQEAAAABAg3AgAAAAE-AgAAAAE_AgAAAAFAAgAAAAFBAgAAAAFCAgA-ACFDAgAAAAREAgAAAAQINwgAAAABPggAAAABPwgAAAABQAgAAAABQQgAAAABQggAPwAhQwgAAAAERAgAAAAECDcEAAAAAT4EAAAAAT8EAAAAAUAEAAAAAUEEAAAAAUIEAEAAIUMEAAAABEQEAAAABA0EAAA-ACAWAAA-ACAXAAA-ACAmAAA_ACAnAAA-ACA3AgAAAAE-AgAAAAE_AgAAAAFAAgAAAAFBAgAAAAFCAgBBACFDAgAAAAREAgAAAAQPBAAAOQAgFgAAQwAgFwAAQwAgNwEAAAABPgEAAAABPwEAAAABQAEAAAABQQEAAAABQgEAQgAhQwEAAAAFRAEAAAAFRQEAAAABRgEAAAABRwEAAAABSAEAAAABDDcBAAAAAT4BAAAAAT8BAAAAAUABAAAAAUEBAAAAAUIBAEMAIUMBAAAABUQBAAAABUUBAAAAAUYBAAAAAUcBAAAAAUgBAAAAAQ8EAAA-ACAWAABFACAXAABFACA3AQAAAAE-AQAAAAE_AQAAAAFAAQAAAAFBAQAAAAFCAQBEACFDAQAAAAREAQAAAARFAQAAAAFGAQAAAAFHAQAAAAFIAQAAAAEMNwEAAAABPgEAAAABPwEAAAABQAEAAAABQQEAAAABQgEARQAhQwEAAAAERAEAAAAERQEAAAABRgEAAAABRwEAAAABSAEAAAABDgMAAE0AICoAAEYAMCsAAAMAECwAAEYAMC0BAEcAIS4BAEcAIS8BAEgAITABAEgAITECAEkAITIEAEoAITNAAEsAITRAAEsAITUAAEwAIDZAAEsAIQw3AQAAAAE-AQAAAAE_AQAAAAFAAQAAAAFBAQAAAAFCAQBFACFDAQAAAAREAQAAAARFAQAAAAFGAQAAAAFHAQAAAAFIAQAAAAEMNwEAAAABPgEAAAABPwEAAAABQAEAAAABQQEAAAABQgEAQwAhQwEAAAAFRAEAAAAFRQEAAAABRgEAAAABRwEAAAABSAEAAAABCDcCAAAAAT4CAAAAAT8CAAAAAUACAAAAAUECAAAAAUICAD4AIUMCAAAABEQCAAAABAg3BAAAAAE-BAAAAAE_BAAAAAFABAAAAAFBBAAAAAFCBABAACFDBAAAAAREBAAAAAQIN0AAAAABPkAAAAABP0AAAAABQEAAAAABQUAAAAABQkAAPAAhQ0AAAAAFREAAAAAFDDeAAAAAATgBAAAAATkBAAAAAToBAAAAATuAAAAAATyAAAAAAT2AAAAAAT6AAAAAAT-AAAAAAUCAAAAAAUGAAAAAAUKAAAAAAQNJAAAFACBKAAAFACBLAAAFACARKgAATgAwKwAAGQAQLAAATgAwLQEAMwAhLgEAMwAhNQAAOAAgNkAANwAhTAEANAAhTQEAMwAhTgEAMwAhTwAAOAAgUAEANAAhUQAAOAAgUgAAOAAgUwEANAAhVEAATwAhVUAATwAhCwQAAD4AIBYAAFEAIBcAAFEAIDdAAAAAAT5AAAAAAT9AAAAAAUBAAAAAAUFAAAAAAUJAAFAAIUNAAAAABERAAAAABAsEAAA-ACAWAABRACAXAABRACA3QAAAAAE-QAAAAAE_QAAAAAFAQAAAAAFBQAAAAAFCQABQACFDQAAAAAREQAAAAAQIN0AAAAABPkAAAAABP0AAAAABQEAAAAABQUAAAAABQkAAUQAhQ0AAAAAEREAAAAAEEgUAAFQAICoAAFIAMCsAAAUAECwAAFIAMC0BAEcAIS4BAEcAITUAAEwAIDZAAEsAIUwBAEgAIU0BAEcAIU4BAEcAIU8AAEwAIFABAEgAIVEAAEwAIFIAAEwAIFMBAEgAIVRAAFMAIVVAAFMAIQg3QAAAAAE-QAAAAAE_QAAAAAFAQAAAAAFBQAAAAAFCQABRACFDQAAAAAREQAAAAAQQAwAATQAgKgAARgAwKwAAAwAQLAAARgAwLQEARwAhLgEARwAhLwEASAAhMAEASAAhMQIASQAhMgQASgAhM0AASwAhNEAASwAhNQAATAAgNkAASwAhVgAAAwAgVwAAAwAgAAAAAAAAAVsBAAAAAQFbAQAAAAEFWwIAAAABYgIAAAABYwIAAAABZAIAAAABZQIAAAABBVsEAAAAAWIEAAAAAWMEAAAAAWQEAAAAAWUEAAAAAQFbQAAAAAELEAAAYQAwEQAAZgAwWAAAYgAwWQAAYwAwWgAAZAAgWwAAZQAwXAAAZQAwXQAAZQAwXgAAZQAwXwAAZwAwYAAAaAAwDS0BAAAAAS4BAAAAATWAAAAAATZAAAAAAU0BAAAAAU4BAAAAAU-AAAAAAVABAAAAAVGAAAAAAVKAAAAAAVMBAAAAAVRAAAAAAVVAAAAAAQIAAAABACAQAABtACADAAAAAQAgEAAAbQAgEQAAbAAgAQsAAH0AMBIFAABUACAqAABSADArAAAFABAsAABSADAtAQAAAAEuAQBHACE1AABMACA2QABLACFMAQBIACFNAQBHACFOAQBHACFPAABMACBQAQBIACFRAABMACBSAABMACBTAQBIACFUQABTACFVQABTACECAAAAAQAgCwAAbAAgAgAAAGkAIAsAAGoAIBEqAABoADArAABpABAsAABoADAtAQBHACEuAQBHACE1AABMACA2QABLACFMAQBIACFNAQBHACFOAQBHACFPAABMACBQAQBIACFRAABMACBSAABMACBTAQBIACFUQABTACFVQABTACERKgAAaAAwKwAAaQAQLAAAaAAwLQEARwAhLgEARwAhNQAATAAgNkAASwAhTAEASAAhTQEARwAhTgEARwAhTwAATAAgUAEASAAhUQAATAAgUgAATAAgUwEASAAhVEAAUwAhVUAAUwAhDS0BAFsAIS4BAFsAITWAAAAAATZAAF8AIU0BAFsAIU4BAFsAIU-AAAAAAVABAFwAIVGAAAAAAVKAAAAAAVMBAFwAIVRAAGsAIVVAAGsAIQFbQAAAAAENLQEAWwAhLgEAWwAhNYAAAAABNkAAXwAhTQEAWwAhTgEAWwAhT4AAAAABUAEAXAAhUYAAAAABUoAAAAABUwEAXAAhVEAAawAhVUAAawAhDS0BAAAAAS4BAAAAATWAAAAAATZAAAAAAU0BAAAAAU4BAAAAAU-AAAAAAVABAAAAAVGAAAAAAVKAAAAAAVMBAAAAAVRAAAAAAVVAAAAAAQQQAABhADBYAABiADBaAABkACBeAABlADAAAUgBAAAAAQAAAAcQAAB4ACARAAB7ACBYAAB5ACBZAAB6ACBcAAADACBdAAADACBeAAAcACADEAAAeAAgWAAAeQAgXgAAHAAgCAMAAG8AIC8AAFUAIDAAAFUAIDMAAFUAIDQAAFUAIDUAAFUAIDYAAFUAIGEAAHAAIAFIAQAAAAEKLQEAAAABLgEAAAABLwEAAAABMAEAAAABMQIAAAABMgQAAAABM0AAAAABNEAAAAABNYAAAAABNkAAAAABAgAAABwAIBAAAHgAIAMAAAADACAQAAB4ACARAAB8ACAMAAAAAwAgCwAAfAAgLQEAWwAhLgEAWwAhLwEAXAAhMAEAXAAhMQIAXQAhMgQAXgAhM0AAXwAhNEAAXwAhNYAAAAABNkAAXwAhCi0BAFsAIS4BAFsAIS8BAFwAITABAFwAITECAF0AITIEAF4AITNAAF8AITRAAF8AITWAAAAAATZAAF8AIQ0tAQAAAAEuAQAAAAE1gAAAAAE2QAAAAAFNAQAAAAFOAQAAAAFPgAAAAAFQAQAAAAFRgAAAAAFSgAAAAAFTAQAAAAFUQAAAAAFVQAAAAAEBBQQCAgMHAQQAAwEDCAAAAwQABhYABxcACAAAAAMEAAYWAAcXAAgFBAALFgAOFwAPJgAMJwANAAAAAAAFBAALFgAOFwAPJgAMJwANBgIBBwkBCAoBCQsBCgwBDA4BDRAEDhIBDxQEEhUBExYBFBcEGBoFGRsJGh0CGx4CHCACHSECHiICHyQCICYEISgCIioEIysCJCwCJS0EKDAKKTEQ"
|
|
33
|
+
};
|
|
34
|
+
async function decodeBase64AsWasm(wasmBase64) {
|
|
35
|
+
const { Buffer } = await import('node:buffer');
|
|
36
|
+
const wasmArray = Buffer.from(wasmBase64, 'base64');
|
|
37
|
+
return new WebAssembly.Module(wasmArray);
|
|
38
|
+
}
|
|
39
|
+
config.compilerWasm = {
|
|
40
|
+
getRuntime: async () => await import("@prisma/client/runtime/query_compiler_fast_bg.mysql.mjs"),
|
|
41
|
+
getQueryCompilerWasmModule: async () => {
|
|
42
|
+
const { wasm } = await import("@prisma/client/runtime/query_compiler_fast_bg.mysql.wasm-base64.mjs");
|
|
43
|
+
return await decodeBase64AsWasm(wasm);
|
|
44
|
+
},
|
|
45
|
+
importName: "./query_compiler_fast_bg.js"
|
|
46
|
+
};
|
|
47
|
+
export function getPrismaClientClass() {
|
|
48
|
+
return runtime.getPrismaClient(config);
|
|
49
|
+
}
|