letopis 1.4.2 → 3.0.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/AGENT-CHEATSHEET.md +516 -398
- package/CHANGELOG.md +739 -0
- package/LICENSE +75 -21
- package/LICENSE-COMMERCIAL.md +49 -0
- package/README.md +1312 -1190
- package/api-contract.json +381 -0
- package/dist/acl.d.ts +81 -1
- package/dist/acl.js +281 -13
- package/dist/admin.d.ts +11 -141
- package/dist/admin.js +28 -186
- package/dist/auth.d.ts +20 -5
- package/dist/auth.js +80 -21
- package/dist/cache.d.ts +8 -1
- package/dist/cache.js +30 -8
- package/dist/chain.d.ts +46 -48
- package/dist/chain.js +556 -223
- package/dist/cli.js +73 -96
- package/dist/demo/booking.d.ts +352 -36
- package/dist/describe.d.ts +47 -0
- package/dist/describe.js +195 -0
- package/dist/errors.d.ts +77 -13
- package/dist/errors.js +231 -30
- package/dist/index.d.ts +62 -74
- package/dist/index.js +312 -155
- package/dist/internal.d.ts +7 -0
- package/dist/jsonschema.js +18 -17
- package/dist/load.d.ts +29 -17
- package/dist/load.js +235 -190
- package/dist/log.d.ts +121 -0
- package/dist/log.js +633 -0
- package/dist/mcp.d.ts +5 -7
- package/dist/mcp.js +194 -225
- package/dist/messages/access.d.ts +90 -0
- package/dist/messages/access.js +113 -0
- package/dist/messages/api.d.ts +237 -0
- package/dist/messages/api.js +304 -0
- package/dist/messages/boot.d.ts +201 -0
- package/dist/messages/boot.js +256 -0
- package/dist/messages/codes.d.ts +172 -0
- package/dist/messages/codes.js +173 -0
- package/dist/messages/index.d.ts +98 -0
- package/dist/messages/index.js +121 -0
- package/dist/messages/log.d.ts +229 -0
- package/dist/messages/log.js +102 -0
- package/dist/messages/mcp.d.ts +306 -0
- package/dist/messages/mcp.js +387 -0
- package/dist/messages/model.d.ts +256 -0
- package/dist/messages/model.js +314 -0
- package/dist/messages/query.d.ts +141 -0
- package/dist/messages/query.js +171 -0
- package/dist/messages/sql-access.d.ts +70 -0
- package/dist/messages/sql-access.js +87 -0
- package/dist/messages/sql-core.d.ts +164 -0
- package/dist/messages/sql-core.js +198 -0
- package/dist/messages/sql-time.d.ts +86 -0
- package/dist/messages/sql-time.js +111 -0
- package/dist/messages/sql-write.d.ts +246 -0
- package/dist/messages/sql-write.js +270 -0
- package/dist/messages/time.d.ts +23 -0
- package/dist/messages/time.js +29 -0
- package/dist/messages/types.d.ts +27 -0
- package/dist/messages/types.js +5 -0
- package/dist/messages/write.d.ts +452 -0
- package/dist/messages/write.js +572 -0
- package/dist/model.d.ts +77 -60
- package/dist/model.js +43 -34
- package/dist/ops.d.ts +35 -0
- package/dist/ops.js +101 -0
- package/dist/pglite.d.ts +0 -9
- package/dist/pglite.js +3 -1
- package/dist/registry.d.ts +3 -19
- package/dist/registry.js +3 -16
- package/dist/signals.d.ts +1 -5
- package/dist/signals.js +1 -5
- package/dist/sql.d.ts +9 -1
- package/dist/sql.js +175 -107
- package/dist/sync.d.ts +10 -4
- package/dist/sync.js +90 -17
- package/dist/tx.d.ts +21 -2
- package/dist/tx.js +76 -13
- package/dist/typed.d.ts +252 -53
- package/dist/types.d.ts +93 -90
- package/dist/types.js +17 -14
- package/dist/up.d.ts +56 -35
- package/dist/up.js +229 -83
- package/dist/uuid.d.ts +8 -0
- package/dist/uuid.js +14 -6
- package/dist/validate.d.ts +13 -1
- package/dist/validate.js +82 -33
- package/dist/watch.d.ts +10 -16
- package/dist/watch.js +36 -77
- package/dist/write.d.ts +87 -17
- package/dist/write.js +441 -206
- package/llms.txt +48 -31
- package/package.json +13 -14
- package/sql/10-core.sql +7 -50
- package/sql/15-errors.sql +64 -10
- package/sql/20-context.sql +18 -9
- package/sql/30-validate.sql +79 -30
- package/sql/40-class.sql +41 -115
- package/sql/50-acl.sql +8 -13
- package/sql/60-write.sql +220 -676
- package/sql/70-read.sql +11 -13
- package/sql/80-auth.sql +109 -42
- package/sql/90-time.sql +108 -947
- package/sql/95-seed.system.sql +7 -14
- package/sql/99-revision.sql +6 -1
- package/sql/README.md +32 -24
- package/sql/migrate/0009-restore-rev.tenants-name.sql +10 -0
- package/sql/migrate/0010-impersonation-no-account.sql +5 -0
- package/sql/migrate/0011-key-lock.otp-enabled.sql +7 -0
- package/sql/migrate/README.md +18 -0
- package/AGENT-CHEATSHEET.en.md +0 -488
- package/MIGRATION.md +0 -190
- package/README.en.md +0 -2910
- package/dist/import.d.ts +0 -185
- package/dist/import.js +0 -820
- package/dist/project.d.ts +0 -359
- package/dist/project.js +0 -1258
- package/sql/75-history.sql +0 -436
- package/sql/88-derived.sql +0 -1236
- package/sql/migrate/0006-changed-paths.sql +0 -3
- package/sql/migrate/0007-secret-account-fk.sql +0 -12
package/dist/cli.js
CHANGED
|
@@ -1,57 +1,74 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* CLI letopis (план, этап 2, п. 7; этап 4, п. 11; этап 6, п.
|
|
4
|
-
*
|
|
3
|
+
* CLI letopis (план, этап 2, п. 7; этап 4, п. 11; этап 6, п. 8): `letopis sync`, `types`, `load`, `maintain`; план 1.1,
|
|
4
|
+
* §2.5 — `mcp`; план 2.0, этап 3, п. 8 — `sync --check`.
|
|
5
5
|
*
|
|
6
|
-
* letopis sync <модуль моделей> --dsn … --schema v2.x (--token … | --api-key …) [--apply]
|
|
7
|
-
*
|
|
6
|
+
* letopis sync <модуль моделей> --dsn … --schema v2.x (--token … | --api-key …) [--apply | --check]
|
|
7
|
+
* --check — сухой прогон с кодом 1, если он нашёл изменения (создать или изменить); вместе с --apply — USAGE, код 2
|
|
8
|
+
* letopis types --dsn … --schema v2.x (--token … | --api-key …) [--out types.d.ts] [--system]
|
|
9
|
+
* --system — и классы арендатора System (системные, сиды пакета), без него — только классы арендатора сессии
|
|
8
10
|
* letopis load <файлы NDJSON…> --dsn … --schema v2.x (--token … | --api-key …) [--class C]
|
|
9
|
-
* [--duplicates error|first|last] [--existing error|skip|update] [--commit-every N] [--
|
|
10
|
-
*
|
|
11
|
-
* letopis maintain --dsn … --schema v2.x [--token … | --api-key …]
|
|
12
|
-
* letopis describe --dsn … --schema v2.x (--token … | --api-key …) [--format json|text] [--out файл]
|
|
13
|
-
* letopis import --from <строка 0.21> --from-schema v1.x --dsn … --schema v2.x [--tenant <имя>] [--keyless C]…
|
|
14
|
-
* [--rename Старое=Новое]… [--update] [--commit-every N] [--partition p] [--initial] — код 1, если verify нашёл расхождения
|
|
15
|
-
* --initial — первая заливка (план 1.1, §2.3): пустая схема, одна транзакция, индексы и триггеры — после загрузки
|
|
11
|
+
* [--duplicates error|first|last] [--existing error|skip|update] [--commit-every N] [--initial]
|
|
12
|
+
* --initial — первая заливка (план 1.1, §2.3): пустая схема, одна транзакция, индексы и триггеры — после загрузки
|
|
13
|
+
* letopis maintain --dsn … --schema v2.x [--token … | --api-key …]
|
|
16
14
|
* letopis mcp --dsn … --schema v2.x --token … [--write] — сервер MCP по stdio (план 1.1, §2.5); dsn, schema и
|
|
17
15
|
* token — ещё из LETOPIS_DSN, LETOPIS_SCHEMA, LETOPIS_TOKEN; только токен пользователя: --api-key — отказ
|
|
16
|
+
* --log[=уровень] — у любой команды (логирование 3.0): события JSON-строками в stderr, уровень debug|info|warn|error|off,
|
|
17
|
+
* без значения — info; без флага действует LETOPIS_LOG
|
|
18
|
+
* справка — USAGE (её же читает справочник сайта, guide/scripts/gen-api.ts)
|
|
18
19
|
*/
|
|
19
20
|
import { parseArgs } from 'node:util';
|
|
20
21
|
import { readFile, writeFile } from 'node:fs/promises';
|
|
21
22
|
import { realpathSync } from 'node:fs';
|
|
22
23
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
23
24
|
import { resolve } from 'node:path';
|
|
24
|
-
import { connect
|
|
25
|
+
import { connect } from './index.js';
|
|
25
26
|
import { syncModels, generateTypes } from './sync.js';
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
28
|
-
const USAGE = `letopis sync <модуль> --dsn <строка> --schema <v2.имя> (--token <токен> | --api-key <ключ>) [--apply]
|
|
29
|
-
letopis types --dsn <строка> --schema <v2.имя> (--token <токен> | --api-key <ключ>) [--out <файл>]
|
|
27
|
+
import { lerr, lmsg } from './errors.js';
|
|
28
|
+
import { stderrLog } from './log.js';
|
|
29
|
+
const USAGE = `letopis sync <модуль> --dsn <строка> --schema <v2.имя> (--token <токен> | --api-key <ключ>) [--apply | --check]
|
|
30
|
+
letopis types --dsn <строка> --schema <v2.имя> (--token <токен> | --api-key <ключ>) [--out <файл>] [--system]
|
|
30
31
|
letopis load <файлы…> --dsn <строка> --schema <v2.имя> (--token <токен> | --api-key <ключ>) [--class <Класс>]
|
|
31
|
-
[--duplicates error|first|last] [--existing error|skip|update] [--commit-every <N>] [--
|
|
32
|
-
letopis
|
|
33
|
-
letopis maintain --dsn <строка> --schema <v2.имя> [--token <токен> | --api-key <ключ>] [--batches <N>]
|
|
34
|
-
letopis describe --dsn <строка> --schema <v2.имя> (--token <токен> | --api-key <ключ>) [--format json|text] [--out <файл>]
|
|
35
|
-
letopis import --from <строка 0.21> --from-schema <v1.имя> --dsn <строка> --schema <v2.имя> [--tenant <имя>] [--keyless <Класс>]…
|
|
36
|
-
[--rename <Старое>=<Новое>]… [--update] [--commit-every <N>] [--partition <имя>] [--initial]
|
|
32
|
+
[--duplicates error|first|last] [--existing error|skip|update] [--commit-every <N>] [--initial]
|
|
33
|
+
letopis maintain --dsn <строка> --schema <v2.имя> [--token <токен> | --api-key <ключ>]
|
|
37
34
|
letopis mcp --dsn <строка> --schema <v2.имя> --token <токен пользователя> [--write]
|
|
38
|
-
(или LETOPIS_DSN, LETOPIS_SCHEMA, LETOPIS_TOKEN; ключ сервиса --api-key не принимается)
|
|
35
|
+
(или LETOPIS_DSN, LETOPIS_SCHEMA, LETOPIS_TOKEN; ключ сервиса --api-key не принимается)
|
|
36
|
+
у любой команды: --log[=debug|info|warn|error|off] — события JSON-строками в stderr (без значения — info)`;
|
|
39
37
|
/** Команды CLI: остальное — USAGE с кодом 2 ещё до подключения. */
|
|
40
|
-
const COMMANDS = new Set(['sync', 'types', 'load', '
|
|
38
|
+
const COMMANDS = new Set(['sync', 'types', 'load', 'maintain', 'mcp']);
|
|
41
39
|
const isModel = (v) => typeof v === 'object' && v !== null && typeof v.describe === 'function' && typeof v.name === 'string';
|
|
40
|
+
/** Модели из модуля (letopis sync): экспорты-модели и массивы моделей, без повторов. */
|
|
41
|
+
async function loadModels(path) {
|
|
42
|
+
const mod = await import(pathToFileURL(resolve(path)).href);
|
|
43
|
+
return [...new Set(Object.values(mod).flatMap((v) => (Array.isArray(v) ? v : [v])).filter(isModel))];
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Флаг --log[=уровень] (логирование 3.0): вынимается из аргументов до parseArgs — у строкового флага parseArgs требует
|
|
47
|
+
* значение, а --log без него значит info.
|
|
48
|
+
*/
|
|
49
|
+
function logFlag(argv) {
|
|
50
|
+
let level;
|
|
51
|
+
const args = [];
|
|
52
|
+
for (const a of argv) {
|
|
53
|
+
if (a === '--log')
|
|
54
|
+
level = 'info';
|
|
55
|
+
else if (a.startsWith('--log='))
|
|
56
|
+
level = (a.slice('--log='.length) || 'info');
|
|
57
|
+
else
|
|
58
|
+
args.push(a);
|
|
59
|
+
}
|
|
60
|
+
return { args, log: level === undefined ? {} : { log: stderrLog, logLevel: level } };
|
|
61
|
+
}
|
|
42
62
|
export async function main(argv) {
|
|
63
|
+
const { args, log } = logFlag(argv);
|
|
43
64
|
const { values, positionals } = parseArgs({
|
|
44
|
-
args
|
|
65
|
+
args,
|
|
45
66
|
allowPositionals: true,
|
|
46
67
|
options: {
|
|
47
68
|
dsn: { type: 'string' }, schema: { type: 'string' }, token: { type: 'string' }, 'api-key': { type: 'string' },
|
|
48
69
|
apply: { type: 'boolean' }, out: { type: 'string' }, help: { type: 'boolean' },
|
|
49
70
|
class: { type: 'string' }, duplicates: { type: 'string' }, existing: { type: 'string' }, 'commit-every': { type: 'string' },
|
|
50
|
-
|
|
51
|
-
format: { type: 'string' },
|
|
52
|
-
from: { type: 'string' }, 'from-schema': { type: 'string' }, tenant: { type: 'string' }, partition: { type: 'string' },
|
|
53
|
-
keyless: { type: 'string', multiple: true }, rename: { type: 'string', multiple: true }, update: { type: 'boolean' },
|
|
54
|
-
initial: { type: 'boolean' }, write: { type: 'boolean' },
|
|
71
|
+
initial: { type: 'boolean' }, write: { type: 'boolean' }, system: { type: 'boolean' }, check: { type: 'boolean' },
|
|
55
72
|
},
|
|
56
73
|
});
|
|
57
74
|
const [cmd, arg] = positionals;
|
|
@@ -66,57 +83,35 @@ export async function main(argv) {
|
|
|
66
83
|
console.log(USAGE);
|
|
67
84
|
return 0;
|
|
68
85
|
}
|
|
69
|
-
|
|
86
|
+
// --check — сухой прогон: вместе с --apply смысла нет
|
|
87
|
+
if (!cmd || !COMMANDS.has(cmd) || !values.dsn || !values.schema || (cmd === 'sync' && values.check && values.apply)) {
|
|
70
88
|
console.log(USAGE);
|
|
71
89
|
return cmd ? 2 : 0;
|
|
72
90
|
}
|
|
73
|
-
if (cmd === 'import') {
|
|
74
|
-
// импорт сам открывает администраторские подключения к 0.21 и к 1.0 (загрузчик, функции владельца)
|
|
75
|
-
if (!values.from || !values['from-schema']) {
|
|
76
|
-
console.log(USAGE);
|
|
77
|
-
return 2;
|
|
78
|
-
}
|
|
79
|
-
const rename = {};
|
|
80
|
-
for (const x of values.rename ?? []) {
|
|
81
|
-
const [from, to] = x.split('=');
|
|
82
|
-
if (!from || !to) {
|
|
83
|
-
console.log(USAGE);
|
|
84
|
-
return 2;
|
|
85
|
-
}
|
|
86
|
-
rename[from] = to;
|
|
87
|
-
}
|
|
88
|
-
const r = await import021({
|
|
89
|
-
from: values.from, fromSchema: values['from-schema'], dsn: values.dsn, schema: values.schema, tenant: values.tenant,
|
|
90
|
-
partition: values.partition, keyless: values.keyless, rename, update: values.update,
|
|
91
|
-
commitEvery: values['commit-every'] ? Number(values['commit-every']) : undefined, initial: values.initial,
|
|
92
|
-
});
|
|
93
|
-
console.log(JSON.stringify(r, null, 2));
|
|
94
|
-
return r.verify.ok ? 0 : 1;
|
|
95
|
-
}
|
|
96
91
|
if (cmd === 'mcp')
|
|
97
|
-
return mcp(values);
|
|
92
|
+
return mcp(values, log);
|
|
98
93
|
// загрузчику нужно администраторское подключение (§2.6, п. 1)
|
|
99
|
-
const db = await connect({ dsn: values.dsn, schema: values.schema, token: values.token, apiKey: values['api-key'], max: 2,
|
|
100
|
-
//
|
|
101
|
-
//
|
|
102
|
-
...(cmd === 'load' || cmd === '
|
|
94
|
+
const db = await connect({ dsn: values.dsn, schema: values.schema, token: values.token, apiKey: values['api-key'], max: 2, ...log,
|
|
95
|
+
// maintain работает и на администраторском подключении без сессии; классы со строками синхронизирует
|
|
96
|
+
// администраторское подключение с сессией арендатора
|
|
97
|
+
...(cmd === 'load' || cmd === 'maintain' || cmd === 'sync' ? { allowBypassRls: true } : {}) });
|
|
103
98
|
try {
|
|
104
99
|
if (cmd === 'sync') {
|
|
105
100
|
if (!arg) {
|
|
106
101
|
console.log(USAGE);
|
|
107
102
|
return 2;
|
|
108
103
|
}
|
|
109
|
-
const
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
for (const [k, list] of [['создать', r.created], ['изменить', r.updated], ['системные (меняет установка)', r.skipped]]) {
|
|
104
|
+
const r = await syncModels(db, await loadModels(arg), { apply: values.apply });
|
|
105
|
+
const counts = { created: r.created.length, updated: r.updated.length, unchanged: r.unchanged.length, skipped: r.skipped.length };
|
|
106
|
+
console.log(lmsg(r.applied ? 'model.cli_sync_applied' : 'model.cli_sync_dry_run', counts));
|
|
107
|
+
for (const [key, list] of [['model.cli_sync_list_create', r.created], ['model.cli_sync_list_update', r.updated], ['model.cli_sync_list_system', r.skipped]]) {
|
|
114
108
|
if (list.length)
|
|
115
|
-
console.log(` ${
|
|
109
|
+
console.log(` ${lmsg(key, { names: list.join(', ') })}`);
|
|
116
110
|
}
|
|
117
111
|
for (const l of r.lost)
|
|
118
|
-
console.log(`
|
|
119
|
-
|
|
112
|
+
console.log(` ${lmsg('model.cli_sync_lost', { kind: l.kind, path: l.path })}`);
|
|
113
|
+
// --check (план 2.0, этап 3, п. 8): модели расходятся с базой — код 1, например для шага CI перед --apply
|
|
114
|
+
return values.check && (r.created.length || r.updated.length) ? 1 : 0;
|
|
120
115
|
}
|
|
121
116
|
if (cmd === 'load') {
|
|
122
117
|
const files = positionals.slice(1);
|
|
@@ -124,8 +119,6 @@ export async function main(argv) {
|
|
|
124
119
|
console.log(USAGE);
|
|
125
120
|
return 2;
|
|
126
121
|
}
|
|
127
|
-
if (values.trust)
|
|
128
|
-
throw new LetopisError('removed', '--trust снят в 1.0.0 — запрос по целям вне загрузки стоит 0,1–1,3 % её времени');
|
|
129
122
|
// несколько файлов — одна загрузка и одна транзакция; в файле — объект на строку
|
|
130
123
|
const rows = [];
|
|
131
124
|
for (const f of files) {
|
|
@@ -138,39 +131,23 @@ export async function main(argv) {
|
|
|
138
131
|
duplicates: values.duplicates,
|
|
139
132
|
existing: values.existing,
|
|
140
133
|
commitEvery: values['commit-every'] ? Number(values['commit-every']) : undefined,
|
|
141
|
-
verify: values.verify,
|
|
142
134
|
initial: values.initial,
|
|
143
135
|
});
|
|
144
136
|
console.log(JSON.stringify(r));
|
|
145
137
|
return 0;
|
|
146
138
|
}
|
|
147
|
-
if (cmd === 'verify') {
|
|
148
|
-
const r = await db.verify({ data: values.data });
|
|
149
|
-
console.log(JSON.stringify(r, null, 2));
|
|
150
|
-
return r.ok ? 0 : 1;
|
|
151
|
-
}
|
|
152
139
|
if (cmd === 'maintain') {
|
|
153
|
-
const r = await db.maintain(
|
|
140
|
+
const r = await db.maintain();
|
|
154
141
|
console.log(JSON.stringify(r));
|
|
155
142
|
return 0;
|
|
156
143
|
}
|
|
157
|
-
if (cmd === 'describe' || cmd === 'types') {
|
|
158
|
-
// обе команды читают реестр сессии: без действующей сессии (токен истёк, погашен, неверен или не задан) он пуст,
|
|
159
|
-
// и команда выдала бы пустой результат с кодом 0, а с --out затёрла бы им файл. acl.check ничего не пишет, а без
|
|
160
|
-
// сессии отказывает no_session — тогда выход с кодом 1, файл не тронут
|
|
161
|
-
await db.acl.check('letopis.cli');
|
|
162
|
-
}
|
|
163
|
-
if (cmd === 'describe') {
|
|
164
|
-
const d = db.describe();
|
|
165
|
-
const text = values.format === 'text' ? describeText(d) : JSON.stringify(d, null, 2);
|
|
166
|
-
if (values.out)
|
|
167
|
-
await writeFile(values.out, text);
|
|
168
|
-
else
|
|
169
|
-
console.log(text);
|
|
170
|
-
return 0;
|
|
171
|
-
}
|
|
172
144
|
if (cmd === 'types') {
|
|
173
|
-
|
|
145
|
+
// реестр сессии без действующей сессии (токен истёк, погашен, неверен или не задан) пуст, и команда выдала бы
|
|
146
|
+
// пустой результат с кодом 0, а с --out затёрла бы им файл. acl.check ничего не пишет, а без сессии отказывает
|
|
147
|
+
// no_session — тогда выход с кодом 1, файл не тронут
|
|
148
|
+
await db.acl.check('letopis.cli');
|
|
149
|
+
// --system — и классы арендатора System: системные и классы сидов пакета (например, booking)
|
|
150
|
+
const text = generateTypes(db.registry, { includeSystem: values.system === true });
|
|
174
151
|
if (values.out)
|
|
175
152
|
await writeFile(values.out, text);
|
|
176
153
|
else
|
|
@@ -190,17 +167,17 @@ export async function main(argv) {
|
|
|
190
167
|
* грузятся только здесь: letopis --help и прочие команды работают без SDK. В stdout — только протокол (предупреждения
|
|
191
168
|
* connect() идут в stderr); процесс живёт, пока клиент не закроет stdin.
|
|
192
169
|
*/
|
|
193
|
-
async function mcp(v) {
|
|
170
|
+
async function mcp(v, log = {}) {
|
|
194
171
|
if (v['api-key'] !== undefined) {
|
|
195
|
-
console.error(
|
|
172
|
+
console.error(lerr('model.mcp_api_key_refused').message);
|
|
196
173
|
return 2;
|
|
197
174
|
}
|
|
198
175
|
if (!v.token) {
|
|
199
|
-
console.error('
|
|
176
|
+
console.error(lerr('model.mcp_token_required').message);
|
|
200
177
|
return 2;
|
|
201
178
|
}
|
|
202
179
|
const { createMcpServer } = await import('./mcp.js');
|
|
203
|
-
const db = await connect({ dsn: v.dsn, schema: v.schema, token: v.token, max: 4 });
|
|
180
|
+
const db = await connect({ dsn: v.dsn, schema: v.schema, token: v.token, max: 4, ...log });
|
|
204
181
|
try {
|
|
205
182
|
const opts = { write: v.write === true };
|
|
206
183
|
// учётные данные — до начала протокола: отказ виден кодом выхода и текстом в stderr
|