@rynfar/meridian 1.34.1 → 1.37.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/README.md +144 -27
- package/dist/cli-p9swy5t3.js +67 -0
- package/dist/{cli-g9ypdz51.js → cli-pr79d7nw.js} +9 -5
- package/dist/{cli-bwchvbfb.js → cli-ygx1djsx.js} +1838 -147
- package/dist/cli.js +9 -9
- package/dist/{profileCli-5e3p99k0.js → profileCli-5f15dx7k.js} +1 -1
- package/dist/{profilePage-9nkbct3w.js → profilePage-g5t5t6av.js} +4 -2
- package/dist/{profiles-ntgacztq.js → profiles-edzz1ffd.js} +1 -1
- package/dist/proxy/adapter.d.ts +17 -0
- package/dist/proxy/adapter.d.ts.map +1 -1
- package/dist/proxy/adapters/crush.d.ts.map +1 -1
- package/dist/proxy/auth.d.ts +27 -0
- package/dist/proxy/auth.d.ts.map +1 -0
- package/dist/proxy/errors.d.ts +1 -1
- package/dist/proxy/errors.d.ts.map +1 -1
- package/dist/proxy/query.d.ts +21 -1
- package/dist/proxy/query.d.ts.map +1 -1
- package/dist/proxy/sdkFeatures.d.ts +56 -0
- package/dist/proxy/sdkFeatures.d.ts.map +1 -0
- package/dist/proxy/server.d.ts.map +1 -1
- package/dist/proxy/types.d.ts +2 -0
- package/dist/proxy/types.d.ts.map +1 -1
- package/dist/server.js +3 -3
- package/dist/{setup-5x116vbs.js → setup-v5pnqe04.js} +1 -1
- package/dist/telemetry/index.d.ts +9 -4
- package/dist/telemetry/index.d.ts.map +1 -1
- package/dist/telemetry/logStore.d.ts +4 -25
- package/dist/telemetry/logStore.d.ts.map +1 -1
- package/dist/telemetry/percentiles.d.ts +12 -0
- package/dist/telemetry/percentiles.d.ts.map +1 -0
- package/dist/telemetry/profileBar.d.ts +1 -1
- package/dist/telemetry/profileBar.d.ts.map +1 -1
- package/dist/telemetry/prometheus.d.ts +10 -0
- package/dist/telemetry/prometheus.d.ts.map +1 -0
- package/dist/telemetry/routes.d.ts.map +1 -1
- package/dist/telemetry/settingsPage.d.ts +6 -0
- package/dist/telemetry/settingsPage.d.ts.map +1 -0
- package/dist/telemetry/sqlite.d.ts +7 -0
- package/dist/telemetry/sqlite.d.ts.map +1 -0
- package/dist/telemetry/store.d.ts +3 -3
- package/dist/telemetry/store.d.ts.map +1 -1
- package/dist/telemetry/types.d.ts +51 -0
- package/dist/telemetry/types.d.ts.map +1 -1
- package/dist/{tokenRefresh-ywwpe8k2.js → tokenRefresh-y7d1qvb3.js} +1 -1
- package/package.json +4 -3
- package/dist/cli-a05ws7rb.js +0 -18
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
|
+
init_profileBar,
|
|
2
3
|
profileBarCss,
|
|
3
4
|
profileBarHtml,
|
|
4
5
|
profileBarJs
|
|
5
|
-
} from "./cli-
|
|
6
|
+
} from "./cli-pr79d7nw.js";
|
|
6
7
|
import {
|
|
7
8
|
checkPluginConfigured
|
|
8
9
|
} from "./cli-rtab0qa6.js";
|
|
@@ -20,9 +21,1519 @@ import {
|
|
|
20
21
|
setActiveProfile
|
|
21
22
|
} from "./cli-vdp9s10c.js";
|
|
22
23
|
import {
|
|
24
|
+
__commonJS,
|
|
25
|
+
__esm,
|
|
23
26
|
__export,
|
|
24
|
-
__require
|
|
25
|
-
|
|
27
|
+
__require,
|
|
28
|
+
__toCommonJS,
|
|
29
|
+
__toESM
|
|
30
|
+
} from "./cli-p9swy5t3.js";
|
|
31
|
+
|
|
32
|
+
// src/telemetry/percentiles.ts
|
|
33
|
+
function computePercentiles(values) {
|
|
34
|
+
if (values.length === 0)
|
|
35
|
+
return { p50: 0, p95: 0, p99: 0, min: 0, max: 0, avg: 0 };
|
|
36
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
37
|
+
const sum = sorted.reduce((a, b) => a + b, 0);
|
|
38
|
+
return {
|
|
39
|
+
p50: sorted[Math.floor(sorted.length * 0.5)],
|
|
40
|
+
p95: sorted[Math.floor(sorted.length * 0.95)],
|
|
41
|
+
p99: sorted[Math.floor(sorted.length * 0.99)],
|
|
42
|
+
min: sorted[0],
|
|
43
|
+
max: sorted[sorted.length - 1],
|
|
44
|
+
avg: Math.round(sum / sorted.length)
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function computeSummary(metrics, windowMs) {
|
|
48
|
+
if (metrics.length === 0) {
|
|
49
|
+
const emptyPhase = { p50: 0, p95: 0, p99: 0, min: 0, max: 0, avg: 0 };
|
|
50
|
+
return {
|
|
51
|
+
windowMs,
|
|
52
|
+
totalRequests: 0,
|
|
53
|
+
errorCount: 0,
|
|
54
|
+
requestsPerMinute: 0,
|
|
55
|
+
queueWait: emptyPhase,
|
|
56
|
+
proxyOverhead: emptyPhase,
|
|
57
|
+
ttfb: emptyPhase,
|
|
58
|
+
upstreamDuration: emptyPhase,
|
|
59
|
+
totalDuration: emptyPhase,
|
|
60
|
+
byModel: {},
|
|
61
|
+
byMode: {},
|
|
62
|
+
tokenUsage: {
|
|
63
|
+
totalInputTokens: 0,
|
|
64
|
+
totalOutputTokens: 0,
|
|
65
|
+
totalCacheReadTokens: 0,
|
|
66
|
+
totalCacheCreationTokens: 0,
|
|
67
|
+
avgCacheHitRate: 0,
|
|
68
|
+
cacheMissOnResumeCount: 0
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
const errorCount = metrics.filter((m) => m.error !== null).length;
|
|
73
|
+
const oldest = metrics[metrics.length - 1].timestamp;
|
|
74
|
+
const newest = metrics[0].timestamp;
|
|
75
|
+
const spanMs = Math.max(newest - oldest, 1);
|
|
76
|
+
const requestsPerMinute = metrics.length / spanMs * 60000;
|
|
77
|
+
const queueWaits = metrics.map((m) => m.queueWaitMs);
|
|
78
|
+
const overheads = metrics.map((m) => m.proxyOverheadMs);
|
|
79
|
+
const ttfbs = metrics.filter((m) => m.ttfbMs !== null).map((m) => m.ttfbMs);
|
|
80
|
+
const upstreams = metrics.map((m) => m.upstreamDurationMs);
|
|
81
|
+
const totals = metrics.map((m) => m.totalDurationMs);
|
|
82
|
+
const byModel = {};
|
|
83
|
+
for (const m of metrics) {
|
|
84
|
+
const modelKey = m.requestModel || m.model;
|
|
85
|
+
const entry = byModel[modelKey] ??= { count: 0, totalMs: 0 };
|
|
86
|
+
entry.count++;
|
|
87
|
+
entry.totalMs += m.totalDurationMs;
|
|
88
|
+
}
|
|
89
|
+
const byMode = {};
|
|
90
|
+
for (const m of metrics) {
|
|
91
|
+
const entry = byMode[m.mode] ??= { count: 0, totalMs: 0 };
|
|
92
|
+
entry.count++;
|
|
93
|
+
entry.totalMs += m.totalDurationMs;
|
|
94
|
+
}
|
|
95
|
+
let totalInputTokens = 0;
|
|
96
|
+
let totalOutputTokens = 0;
|
|
97
|
+
let totalCacheReadTokens = 0;
|
|
98
|
+
let totalCacheCreationTokens = 0;
|
|
99
|
+
let cacheHitRateSum = 0;
|
|
100
|
+
let cacheHitRateCount = 0;
|
|
101
|
+
let cacheMissOnResumeCount = 0;
|
|
102
|
+
for (const m of metrics) {
|
|
103
|
+
totalInputTokens += m.inputTokens ?? 0;
|
|
104
|
+
totalOutputTokens += m.outputTokens ?? 0;
|
|
105
|
+
totalCacheReadTokens += m.cacheReadInputTokens ?? 0;
|
|
106
|
+
totalCacheCreationTokens += m.cacheCreationInputTokens ?? 0;
|
|
107
|
+
if (m.cacheHitRate !== undefined) {
|
|
108
|
+
cacheHitRateSum += m.cacheHitRate;
|
|
109
|
+
cacheHitRateCount++;
|
|
110
|
+
}
|
|
111
|
+
if (m.isResume && m.cacheHitRate !== undefined && m.cacheHitRate === 0) {
|
|
112
|
+
cacheMissOnResumeCount++;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
windowMs,
|
|
117
|
+
totalRequests: metrics.length,
|
|
118
|
+
errorCount,
|
|
119
|
+
requestsPerMinute: Math.round(requestsPerMinute * 100) / 100,
|
|
120
|
+
queueWait: computePercentiles(queueWaits),
|
|
121
|
+
proxyOverhead: computePercentiles(overheads),
|
|
122
|
+
ttfb: ttfbs.length > 0 ? computePercentiles(ttfbs) : { p50: 0, p95: 0, p99: 0, min: 0, max: 0, avg: 0 },
|
|
123
|
+
upstreamDuration: computePercentiles(upstreams),
|
|
124
|
+
totalDuration: computePercentiles(totals),
|
|
125
|
+
byModel: Object.fromEntries(Object.entries(byModel).map(([k, v]) => [k, { count: v.count, avgTotalMs: Math.round(v.totalMs / v.count) }])),
|
|
126
|
+
byMode: Object.fromEntries(Object.entries(byMode).map(([k, v]) => [k, { count: v.count, avgTotalMs: Math.round(v.totalMs / v.count) }])),
|
|
127
|
+
tokenUsage: {
|
|
128
|
+
totalInputTokens,
|
|
129
|
+
totalOutputTokens,
|
|
130
|
+
totalCacheReadTokens,
|
|
131
|
+
totalCacheCreationTokens,
|
|
132
|
+
avgCacheHitRate: cacheHitRateCount > 0 ? Math.round(cacheHitRateSum / cacheHitRateCount * 100) / 100 : 0,
|
|
133
|
+
cacheMissOnResumeCount
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
var init_percentiles = () => {};
|
|
138
|
+
|
|
139
|
+
// node_modules/@neon-rs/load/dist/index.js
|
|
140
|
+
var require_dist = __commonJS((exports) => {
|
|
141
|
+
var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m, k, k2) {
|
|
142
|
+
if (k2 === undefined)
|
|
143
|
+
k2 = k;
|
|
144
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
145
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
146
|
+
desc = { enumerable: true, get: function() {
|
|
147
|
+
return m[k];
|
|
148
|
+
} };
|
|
149
|
+
}
|
|
150
|
+
Object.defineProperty(o, k2, desc);
|
|
151
|
+
} : function(o, m, k, k2) {
|
|
152
|
+
if (k2 === undefined)
|
|
153
|
+
k2 = k;
|
|
154
|
+
o[k2] = m[k];
|
|
155
|
+
});
|
|
156
|
+
var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? function(o, v) {
|
|
157
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
158
|
+
} : function(o, v) {
|
|
159
|
+
o["default"] = v;
|
|
160
|
+
});
|
|
161
|
+
var __importStar = exports && exports.__importStar || function(mod) {
|
|
162
|
+
if (mod && mod.__esModule)
|
|
163
|
+
return mod;
|
|
164
|
+
var result = {};
|
|
165
|
+
if (mod != null) {
|
|
166
|
+
for (var k in mod)
|
|
167
|
+
if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k))
|
|
168
|
+
__createBinding(result, mod, k);
|
|
169
|
+
}
|
|
170
|
+
__setModuleDefault(result, mod);
|
|
171
|
+
return result;
|
|
172
|
+
};
|
|
173
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
174
|
+
exports.load = exports.currentTarget = undefined;
|
|
175
|
+
var path = __importStar(__require("path"));
|
|
176
|
+
var fs = __importStar(__require("fs"));
|
|
177
|
+
function currentTarget() {
|
|
178
|
+
let os = null;
|
|
179
|
+
switch (process.platform) {
|
|
180
|
+
case "android":
|
|
181
|
+
switch (process.arch) {
|
|
182
|
+
case "arm":
|
|
183
|
+
return "android-arm-eabi";
|
|
184
|
+
case "arm64":
|
|
185
|
+
return "android-arm64";
|
|
186
|
+
}
|
|
187
|
+
os = "Android";
|
|
188
|
+
break;
|
|
189
|
+
case "win32":
|
|
190
|
+
switch (process.arch) {
|
|
191
|
+
case "x64":
|
|
192
|
+
return "win32-x64-msvc";
|
|
193
|
+
case "arm64":
|
|
194
|
+
return "win32-arm64-msvc";
|
|
195
|
+
case "ia32":
|
|
196
|
+
return "win32-ia32-msvc";
|
|
197
|
+
}
|
|
198
|
+
os = "Windows";
|
|
199
|
+
break;
|
|
200
|
+
case "darwin":
|
|
201
|
+
switch (process.arch) {
|
|
202
|
+
case "x64":
|
|
203
|
+
return "darwin-x64";
|
|
204
|
+
case "arm64":
|
|
205
|
+
return "darwin-arm64";
|
|
206
|
+
}
|
|
207
|
+
os = "macOS";
|
|
208
|
+
break;
|
|
209
|
+
case "linux":
|
|
210
|
+
switch (process.arch) {
|
|
211
|
+
case "x64":
|
|
212
|
+
case "arm64":
|
|
213
|
+
return isGlibc() ? `linux-${process.arch}-gnu` : `linux-${process.arch}-musl`;
|
|
214
|
+
case "arm":
|
|
215
|
+
return "linux-arm-gnueabihf";
|
|
216
|
+
}
|
|
217
|
+
os = "Linux";
|
|
218
|
+
break;
|
|
219
|
+
case "freebsd":
|
|
220
|
+
if (process.arch === "x64") {
|
|
221
|
+
return "freebsd-x64";
|
|
222
|
+
}
|
|
223
|
+
os = "FreeBSD";
|
|
224
|
+
break;
|
|
225
|
+
}
|
|
226
|
+
if (os) {
|
|
227
|
+
throw new Error(`Neon: unsupported ${os} architecture: ${process.arch}`);
|
|
228
|
+
}
|
|
229
|
+
throw new Error(`Neon: unsupported system: ${process.platform}`);
|
|
230
|
+
}
|
|
231
|
+
exports.currentTarget = currentTarget;
|
|
232
|
+
function isGlibc() {
|
|
233
|
+
const report = process.report?.getReport();
|
|
234
|
+
if (typeof report !== "object" || !report || !("header" in report)) {
|
|
235
|
+
return false;
|
|
236
|
+
}
|
|
237
|
+
const header = report.header;
|
|
238
|
+
return typeof header === "object" && !!header && "glibcVersionRuntime" in header;
|
|
239
|
+
}
|
|
240
|
+
function load(dirname2) {
|
|
241
|
+
const m = path.join(dirname2, "index.node");
|
|
242
|
+
return fs.existsSync(m) ? __require(m) : null;
|
|
243
|
+
}
|
|
244
|
+
exports.load = load;
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
// node_modules/detect-libc/lib/process.js
|
|
248
|
+
var require_process = __commonJS((exports, module) => {
|
|
249
|
+
var isLinux = () => process.platform === "linux";
|
|
250
|
+
var report = null;
|
|
251
|
+
var getReport = () => {
|
|
252
|
+
if (!report) {
|
|
253
|
+
report = isLinux() && process.report ? process.report.getReport() : {};
|
|
254
|
+
}
|
|
255
|
+
return report;
|
|
256
|
+
};
|
|
257
|
+
module.exports = { isLinux, getReport };
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
// node_modules/detect-libc/lib/filesystem.js
|
|
261
|
+
var require_filesystem = __commonJS((exports, module) => {
|
|
262
|
+
var fs = __require("fs");
|
|
263
|
+
var LDD_PATH = "/usr/bin/ldd";
|
|
264
|
+
var readFileSync2 = (path) => fs.readFileSync(path, "utf-8");
|
|
265
|
+
var readFile = (path) => new Promise((resolve2, reject) => {
|
|
266
|
+
fs.readFile(path, "utf-8", (err, data) => {
|
|
267
|
+
if (err) {
|
|
268
|
+
reject(err);
|
|
269
|
+
} else {
|
|
270
|
+
resolve2(data);
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
module.exports = {
|
|
275
|
+
LDD_PATH,
|
|
276
|
+
readFileSync: readFileSync2,
|
|
277
|
+
readFile
|
|
278
|
+
};
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
// node_modules/detect-libc/lib/detect-libc.js
|
|
282
|
+
var require_detect_libc = __commonJS((exports, module) => {
|
|
283
|
+
var childProcess = __require("child_process");
|
|
284
|
+
var { isLinux, getReport } = require_process();
|
|
285
|
+
var { LDD_PATH, readFile, readFileSync: readFileSync2 } = require_filesystem();
|
|
286
|
+
var cachedFamilyFilesystem;
|
|
287
|
+
var cachedVersionFilesystem;
|
|
288
|
+
var command = "getconf GNU_LIBC_VERSION 2>&1 || true; ldd --version 2>&1 || true";
|
|
289
|
+
var commandOut = "";
|
|
290
|
+
var safeCommand = () => {
|
|
291
|
+
if (!commandOut) {
|
|
292
|
+
return new Promise((resolve2) => {
|
|
293
|
+
childProcess.exec(command, (err, out) => {
|
|
294
|
+
commandOut = err ? " " : out;
|
|
295
|
+
resolve2(commandOut);
|
|
296
|
+
});
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
return commandOut;
|
|
300
|
+
};
|
|
301
|
+
var safeCommandSync = () => {
|
|
302
|
+
if (!commandOut) {
|
|
303
|
+
try {
|
|
304
|
+
commandOut = childProcess.execSync(command, { encoding: "utf8" });
|
|
305
|
+
} catch (_err) {
|
|
306
|
+
commandOut = " ";
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
return commandOut;
|
|
310
|
+
};
|
|
311
|
+
var GLIBC = "glibc";
|
|
312
|
+
var RE_GLIBC_VERSION = /GLIBC\s(\d+\.\d+)/;
|
|
313
|
+
var MUSL = "musl";
|
|
314
|
+
var GLIBC_ON_LDD = GLIBC.toUpperCase();
|
|
315
|
+
var MUSL_ON_LDD = MUSL.toLowerCase();
|
|
316
|
+
var isFileMusl = (f) => f.includes("libc.musl-") || f.includes("ld-musl-");
|
|
317
|
+
var familyFromReport = () => {
|
|
318
|
+
const report = getReport();
|
|
319
|
+
if (report.header && report.header.glibcVersionRuntime) {
|
|
320
|
+
return GLIBC;
|
|
321
|
+
}
|
|
322
|
+
if (Array.isArray(report.sharedObjects)) {
|
|
323
|
+
if (report.sharedObjects.some(isFileMusl)) {
|
|
324
|
+
return MUSL;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return null;
|
|
328
|
+
};
|
|
329
|
+
var familyFromCommand = (out) => {
|
|
330
|
+
const [getconf, ldd1] = out.split(/[\r\n]+/);
|
|
331
|
+
if (getconf && getconf.includes(GLIBC)) {
|
|
332
|
+
return GLIBC;
|
|
333
|
+
}
|
|
334
|
+
if (ldd1 && ldd1.includes(MUSL)) {
|
|
335
|
+
return MUSL;
|
|
336
|
+
}
|
|
337
|
+
return null;
|
|
338
|
+
};
|
|
339
|
+
var getFamilyFromLddContent = (content) => {
|
|
340
|
+
if (content.includes(MUSL_ON_LDD)) {
|
|
341
|
+
return MUSL;
|
|
342
|
+
}
|
|
343
|
+
if (content.includes(GLIBC_ON_LDD)) {
|
|
344
|
+
return GLIBC;
|
|
345
|
+
}
|
|
346
|
+
return null;
|
|
347
|
+
};
|
|
348
|
+
var familyFromFilesystem = async () => {
|
|
349
|
+
if (cachedFamilyFilesystem !== undefined) {
|
|
350
|
+
return cachedFamilyFilesystem;
|
|
351
|
+
}
|
|
352
|
+
cachedFamilyFilesystem = null;
|
|
353
|
+
try {
|
|
354
|
+
const lddContent = await readFile(LDD_PATH);
|
|
355
|
+
cachedFamilyFilesystem = getFamilyFromLddContent(lddContent);
|
|
356
|
+
} catch (e) {}
|
|
357
|
+
return cachedFamilyFilesystem;
|
|
358
|
+
};
|
|
359
|
+
var familyFromFilesystemSync = () => {
|
|
360
|
+
if (cachedFamilyFilesystem !== undefined) {
|
|
361
|
+
return cachedFamilyFilesystem;
|
|
362
|
+
}
|
|
363
|
+
cachedFamilyFilesystem = null;
|
|
364
|
+
try {
|
|
365
|
+
const lddContent = readFileSync2(LDD_PATH);
|
|
366
|
+
cachedFamilyFilesystem = getFamilyFromLddContent(lddContent);
|
|
367
|
+
} catch (e) {}
|
|
368
|
+
return cachedFamilyFilesystem;
|
|
369
|
+
};
|
|
370
|
+
var family = async () => {
|
|
371
|
+
let family2 = null;
|
|
372
|
+
if (isLinux()) {
|
|
373
|
+
family2 = await familyFromFilesystem();
|
|
374
|
+
if (!family2) {
|
|
375
|
+
family2 = familyFromReport();
|
|
376
|
+
}
|
|
377
|
+
if (!family2) {
|
|
378
|
+
const out = await safeCommand();
|
|
379
|
+
family2 = familyFromCommand(out);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
return family2;
|
|
383
|
+
};
|
|
384
|
+
var familySync = () => {
|
|
385
|
+
let family2 = null;
|
|
386
|
+
if (isLinux()) {
|
|
387
|
+
family2 = familyFromFilesystemSync();
|
|
388
|
+
if (!family2) {
|
|
389
|
+
family2 = familyFromReport();
|
|
390
|
+
}
|
|
391
|
+
if (!family2) {
|
|
392
|
+
const out = safeCommandSync();
|
|
393
|
+
family2 = familyFromCommand(out);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
return family2;
|
|
397
|
+
};
|
|
398
|
+
var isNonGlibcLinux = async () => isLinux() && await family() !== GLIBC;
|
|
399
|
+
var isNonGlibcLinuxSync = () => isLinux() && familySync() !== GLIBC;
|
|
400
|
+
var versionFromFilesystem = async () => {
|
|
401
|
+
if (cachedVersionFilesystem !== undefined) {
|
|
402
|
+
return cachedVersionFilesystem;
|
|
403
|
+
}
|
|
404
|
+
cachedVersionFilesystem = null;
|
|
405
|
+
try {
|
|
406
|
+
const lddContent = await readFile(LDD_PATH);
|
|
407
|
+
const versionMatch = lddContent.match(RE_GLIBC_VERSION);
|
|
408
|
+
if (versionMatch) {
|
|
409
|
+
cachedVersionFilesystem = versionMatch[1];
|
|
410
|
+
}
|
|
411
|
+
} catch (e) {}
|
|
412
|
+
return cachedVersionFilesystem;
|
|
413
|
+
};
|
|
414
|
+
var versionFromFilesystemSync = () => {
|
|
415
|
+
if (cachedVersionFilesystem !== undefined) {
|
|
416
|
+
return cachedVersionFilesystem;
|
|
417
|
+
}
|
|
418
|
+
cachedVersionFilesystem = null;
|
|
419
|
+
try {
|
|
420
|
+
const lddContent = readFileSync2(LDD_PATH);
|
|
421
|
+
const versionMatch = lddContent.match(RE_GLIBC_VERSION);
|
|
422
|
+
if (versionMatch) {
|
|
423
|
+
cachedVersionFilesystem = versionMatch[1];
|
|
424
|
+
}
|
|
425
|
+
} catch (e) {}
|
|
426
|
+
return cachedVersionFilesystem;
|
|
427
|
+
};
|
|
428
|
+
var versionFromReport = () => {
|
|
429
|
+
const report = getReport();
|
|
430
|
+
if (report.header && report.header.glibcVersionRuntime) {
|
|
431
|
+
return report.header.glibcVersionRuntime;
|
|
432
|
+
}
|
|
433
|
+
return null;
|
|
434
|
+
};
|
|
435
|
+
var versionSuffix = (s) => s.trim().split(/\s+/)[1];
|
|
436
|
+
var versionFromCommand = (out) => {
|
|
437
|
+
const [getconf, ldd1, ldd2] = out.split(/[\r\n]+/);
|
|
438
|
+
if (getconf && getconf.includes(GLIBC)) {
|
|
439
|
+
return versionSuffix(getconf);
|
|
440
|
+
}
|
|
441
|
+
if (ldd1 && ldd2 && ldd1.includes(MUSL)) {
|
|
442
|
+
return versionSuffix(ldd2);
|
|
443
|
+
}
|
|
444
|
+
return null;
|
|
445
|
+
};
|
|
446
|
+
var version = async () => {
|
|
447
|
+
let version2 = null;
|
|
448
|
+
if (isLinux()) {
|
|
449
|
+
version2 = await versionFromFilesystem();
|
|
450
|
+
if (!version2) {
|
|
451
|
+
version2 = versionFromReport();
|
|
452
|
+
}
|
|
453
|
+
if (!version2) {
|
|
454
|
+
const out = await safeCommand();
|
|
455
|
+
version2 = versionFromCommand(out);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
return version2;
|
|
459
|
+
};
|
|
460
|
+
var versionSync = () => {
|
|
461
|
+
let version2 = null;
|
|
462
|
+
if (isLinux()) {
|
|
463
|
+
version2 = versionFromFilesystemSync();
|
|
464
|
+
if (!version2) {
|
|
465
|
+
version2 = versionFromReport();
|
|
466
|
+
}
|
|
467
|
+
if (!version2) {
|
|
468
|
+
const out = safeCommandSync();
|
|
469
|
+
version2 = versionFromCommand(out);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
return version2;
|
|
473
|
+
};
|
|
474
|
+
module.exports = {
|
|
475
|
+
GLIBC,
|
|
476
|
+
MUSL,
|
|
477
|
+
family,
|
|
478
|
+
familySync,
|
|
479
|
+
isNonGlibcLinux,
|
|
480
|
+
isNonGlibcLinuxSync,
|
|
481
|
+
version,
|
|
482
|
+
versionSync
|
|
483
|
+
};
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
// node_modules/libsql/auth.js
|
|
487
|
+
var require_auth = __commonJS((exports, module) => {
|
|
488
|
+
var Authorization = {
|
|
489
|
+
ALLOW: 0,
|
|
490
|
+
DENY: 1
|
|
491
|
+
};
|
|
492
|
+
module.exports = Authorization;
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
// node_modules/libsql/sqlite-error.js
|
|
496
|
+
var require_sqlite_error = __commonJS((exports, module) => {
|
|
497
|
+
var descriptor = { value: "SqliteError", writable: true, enumerable: false, configurable: true };
|
|
498
|
+
function SqliteError(message, code, rawCode) {
|
|
499
|
+
if (new.target !== SqliteError) {
|
|
500
|
+
return new SqliteError(message, code);
|
|
501
|
+
}
|
|
502
|
+
if (typeof code !== "string") {
|
|
503
|
+
throw new TypeError("Expected second argument to be a string");
|
|
504
|
+
}
|
|
505
|
+
Error.call(this, message);
|
|
506
|
+
descriptor.value = "" + message;
|
|
507
|
+
Object.defineProperty(this, "message", descriptor);
|
|
508
|
+
Error.captureStackTrace(this, SqliteError);
|
|
509
|
+
this.code = code;
|
|
510
|
+
this.rawCode = rawCode;
|
|
511
|
+
}
|
|
512
|
+
Object.setPrototypeOf(SqliteError, Error);
|
|
513
|
+
Object.setPrototypeOf(SqliteError.prototype, Error.prototype);
|
|
514
|
+
Object.defineProperty(SqliteError.prototype, "name", descriptor);
|
|
515
|
+
module.exports = SqliteError;
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
// node_modules/libsql/index.js
|
|
519
|
+
var require_libsql = __commonJS((exports, module) => {
|
|
520
|
+
var __dirname = "/home/runner/work/meridian/meridian/node_modules/libsql";
|
|
521
|
+
var { load, currentTarget } = require_dist();
|
|
522
|
+
var { familySync, GLIBC, MUSL } = require_detect_libc();
|
|
523
|
+
function requireNative() {
|
|
524
|
+
if (process.env.LIBSQL_JS_DEV) {
|
|
525
|
+
return load(__dirname);
|
|
526
|
+
}
|
|
527
|
+
let target = currentTarget();
|
|
528
|
+
if (familySync() == GLIBC) {
|
|
529
|
+
switch (target) {
|
|
530
|
+
case "linux-x64-musl":
|
|
531
|
+
target = "linux-x64-gnu";
|
|
532
|
+
break;
|
|
533
|
+
case "linux-arm64-musl":
|
|
534
|
+
target = "linux-arm64-gnu";
|
|
535
|
+
break;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
if (target === "linux-arm-gnueabihf" && familySync() == MUSL) {
|
|
539
|
+
target = "linux-arm-musleabihf";
|
|
540
|
+
}
|
|
541
|
+
return __require(`@libsql/${target}`);
|
|
542
|
+
}
|
|
543
|
+
var {
|
|
544
|
+
databaseOpen,
|
|
545
|
+
databaseOpenWithSync,
|
|
546
|
+
databaseInTransaction,
|
|
547
|
+
databaseInterrupt,
|
|
548
|
+
databaseClose,
|
|
549
|
+
databaseSyncSync,
|
|
550
|
+
databaseSyncUntilSync,
|
|
551
|
+
databaseExecSync,
|
|
552
|
+
databasePrepareSync,
|
|
553
|
+
databaseDefaultSafeIntegers,
|
|
554
|
+
databaseAuthorizer,
|
|
555
|
+
databaseLoadExtension,
|
|
556
|
+
databaseMaxWriteReplicationIndex,
|
|
557
|
+
statementRaw,
|
|
558
|
+
statementIsReader,
|
|
559
|
+
statementGet,
|
|
560
|
+
statementRun,
|
|
561
|
+
statementInterrupt,
|
|
562
|
+
statementRowsSync,
|
|
563
|
+
statementColumns,
|
|
564
|
+
statementSafeIntegers,
|
|
565
|
+
rowsNext
|
|
566
|
+
} = requireNative();
|
|
567
|
+
var Authorization = require_auth();
|
|
568
|
+
var SqliteError = require_sqlite_error();
|
|
569
|
+
function convertError(err) {
|
|
570
|
+
if (err.libsqlError) {
|
|
571
|
+
return new SqliteError(err.message, err.code, err.rawCode);
|
|
572
|
+
}
|
|
573
|
+
return err;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
class Database {
|
|
577
|
+
constructor(path, opts) {
|
|
578
|
+
const encryptionCipher = opts?.encryptionCipher ?? "aes256cbc";
|
|
579
|
+
if (opts && opts.syncUrl) {
|
|
580
|
+
var authToken = "";
|
|
581
|
+
if (opts.syncAuth) {
|
|
582
|
+
console.warn("Warning: The `syncAuth` option is deprecated, please use `authToken` option instead.");
|
|
583
|
+
authToken = opts.syncAuth;
|
|
584
|
+
} else if (opts.authToken) {
|
|
585
|
+
authToken = opts.authToken;
|
|
586
|
+
}
|
|
587
|
+
const encryptionKey = opts?.encryptionKey ?? "";
|
|
588
|
+
const syncPeriod = opts?.syncPeriod ?? 0;
|
|
589
|
+
const readYourWrites = opts?.readYourWrites ?? true;
|
|
590
|
+
const offline = opts?.offline ?? false;
|
|
591
|
+
const remoteEncryptionKey = opts?.remoteEncryptionKey ?? "";
|
|
592
|
+
this.db = databaseOpenWithSync(path, opts.syncUrl, authToken, encryptionCipher, encryptionKey, syncPeriod, readYourWrites, offline, remoteEncryptionKey);
|
|
593
|
+
} else {
|
|
594
|
+
const authToken2 = opts?.authToken ?? "";
|
|
595
|
+
const encryptionKey = opts?.encryptionKey ?? "";
|
|
596
|
+
const timeout = opts?.timeout ?? 0;
|
|
597
|
+
const remoteEncryptionKey = opts?.remoteEncryptionKey ?? "";
|
|
598
|
+
this.db = databaseOpen(path, authToken2, encryptionCipher, encryptionKey, timeout, remoteEncryptionKey);
|
|
599
|
+
}
|
|
600
|
+
this.memory = path === ":memory:";
|
|
601
|
+
this.readonly = false;
|
|
602
|
+
this.name = "";
|
|
603
|
+
this.open = true;
|
|
604
|
+
const db = this.db;
|
|
605
|
+
Object.defineProperties(this, {
|
|
606
|
+
inTransaction: {
|
|
607
|
+
get() {
|
|
608
|
+
return databaseInTransaction(db);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
sync() {
|
|
614
|
+
return databaseSyncSync.call(this.db);
|
|
615
|
+
}
|
|
616
|
+
syncUntil(replicationIndex) {
|
|
617
|
+
return databaseSyncUntilSync.call(this.db, replicationIndex);
|
|
618
|
+
}
|
|
619
|
+
prepare(sql) {
|
|
620
|
+
try {
|
|
621
|
+
const stmt = databasePrepareSync.call(this.db, sql);
|
|
622
|
+
return new Statement(stmt);
|
|
623
|
+
} catch (err) {
|
|
624
|
+
throw convertError(err);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
transaction(fn) {
|
|
628
|
+
if (typeof fn !== "function")
|
|
629
|
+
throw new TypeError("Expected first argument to be a function");
|
|
630
|
+
const db = this;
|
|
631
|
+
const wrapTxn = (mode) => {
|
|
632
|
+
return (...bindParameters) => {
|
|
633
|
+
db.exec("BEGIN " + mode);
|
|
634
|
+
try {
|
|
635
|
+
const result = fn(...bindParameters);
|
|
636
|
+
db.exec("COMMIT");
|
|
637
|
+
return result;
|
|
638
|
+
} catch (err) {
|
|
639
|
+
db.exec("ROLLBACK");
|
|
640
|
+
throw err;
|
|
641
|
+
}
|
|
642
|
+
};
|
|
643
|
+
};
|
|
644
|
+
const properties = {
|
|
645
|
+
default: { value: wrapTxn("") },
|
|
646
|
+
deferred: { value: wrapTxn("DEFERRED") },
|
|
647
|
+
immediate: { value: wrapTxn("IMMEDIATE") },
|
|
648
|
+
exclusive: { value: wrapTxn("EXCLUSIVE") },
|
|
649
|
+
database: { value: this, enumerable: true }
|
|
650
|
+
};
|
|
651
|
+
Object.defineProperties(properties.default.value, properties);
|
|
652
|
+
Object.defineProperties(properties.deferred.value, properties);
|
|
653
|
+
Object.defineProperties(properties.immediate.value, properties);
|
|
654
|
+
Object.defineProperties(properties.exclusive.value, properties);
|
|
655
|
+
return properties.default.value;
|
|
656
|
+
}
|
|
657
|
+
pragma(source, options) {
|
|
658
|
+
if (options == null)
|
|
659
|
+
options = {};
|
|
660
|
+
if (typeof source !== "string")
|
|
661
|
+
throw new TypeError("Expected first argument to be a string");
|
|
662
|
+
if (typeof options !== "object")
|
|
663
|
+
throw new TypeError("Expected second argument to be an options object");
|
|
664
|
+
const simple = options["simple"];
|
|
665
|
+
const stmt = this.prepare(`PRAGMA ${source}`, this, true);
|
|
666
|
+
return simple ? stmt.pluck().get() : stmt.all();
|
|
667
|
+
}
|
|
668
|
+
backup(filename, options) {
|
|
669
|
+
throw new Error("not implemented");
|
|
670
|
+
}
|
|
671
|
+
serialize(options) {
|
|
672
|
+
throw new Error("not implemented");
|
|
673
|
+
}
|
|
674
|
+
function(name, options, fn) {
|
|
675
|
+
if (options == null)
|
|
676
|
+
options = {};
|
|
677
|
+
if (typeof options === "function") {
|
|
678
|
+
fn = options;
|
|
679
|
+
options = {};
|
|
680
|
+
}
|
|
681
|
+
if (typeof name !== "string")
|
|
682
|
+
throw new TypeError("Expected first argument to be a string");
|
|
683
|
+
if (typeof fn !== "function")
|
|
684
|
+
throw new TypeError("Expected last argument to be a function");
|
|
685
|
+
if (typeof options !== "object")
|
|
686
|
+
throw new TypeError("Expected second argument to be an options object");
|
|
687
|
+
if (!name)
|
|
688
|
+
throw new TypeError("User-defined function name cannot be an empty string");
|
|
689
|
+
throw new Error("not implemented");
|
|
690
|
+
}
|
|
691
|
+
aggregate(name, options) {
|
|
692
|
+
if (typeof name !== "string")
|
|
693
|
+
throw new TypeError("Expected first argument to be a string");
|
|
694
|
+
if (typeof options !== "object" || options === null)
|
|
695
|
+
throw new TypeError("Expected second argument to be an options object");
|
|
696
|
+
if (!name)
|
|
697
|
+
throw new TypeError("User-defined function name cannot be an empty string");
|
|
698
|
+
throw new Error("not implemented");
|
|
699
|
+
}
|
|
700
|
+
table(name, factory) {
|
|
701
|
+
if (typeof name !== "string")
|
|
702
|
+
throw new TypeError("Expected first argument to be a string");
|
|
703
|
+
if (!name)
|
|
704
|
+
throw new TypeError("Virtual table module name cannot be an empty string");
|
|
705
|
+
throw new Error("not implemented");
|
|
706
|
+
}
|
|
707
|
+
authorizer(rules) {
|
|
708
|
+
databaseAuthorizer.call(this.db, rules);
|
|
709
|
+
}
|
|
710
|
+
loadExtension(...args) {
|
|
711
|
+
databaseLoadExtension.call(this.db, ...args);
|
|
712
|
+
}
|
|
713
|
+
maxWriteReplicationIndex() {
|
|
714
|
+
return databaseMaxWriteReplicationIndex.call(this.db);
|
|
715
|
+
}
|
|
716
|
+
exec(sql) {
|
|
717
|
+
try {
|
|
718
|
+
databaseExecSync.call(this.db, sql);
|
|
719
|
+
} catch (err) {
|
|
720
|
+
throw convertError(err);
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
interrupt() {
|
|
724
|
+
databaseInterrupt.call(this.db);
|
|
725
|
+
}
|
|
726
|
+
close() {
|
|
727
|
+
databaseClose.call(this.db);
|
|
728
|
+
this.open = false;
|
|
729
|
+
}
|
|
730
|
+
defaultSafeIntegers(toggle) {
|
|
731
|
+
databaseDefaultSafeIntegers.call(this.db, toggle ?? true);
|
|
732
|
+
return this;
|
|
733
|
+
}
|
|
734
|
+
unsafeMode(...args) {
|
|
735
|
+
throw new Error("not implemented");
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
class Statement {
|
|
740
|
+
constructor(stmt) {
|
|
741
|
+
this.stmt = stmt;
|
|
742
|
+
this.pluckMode = false;
|
|
743
|
+
}
|
|
744
|
+
raw(raw2) {
|
|
745
|
+
statementRaw.call(this.stmt, raw2 ?? true);
|
|
746
|
+
return this;
|
|
747
|
+
}
|
|
748
|
+
pluck(pluckMode) {
|
|
749
|
+
this.pluckMode = pluckMode ?? true;
|
|
750
|
+
return this;
|
|
751
|
+
}
|
|
752
|
+
get reader() {
|
|
753
|
+
return statementIsReader.call(this.stmt);
|
|
754
|
+
}
|
|
755
|
+
run(...bindParameters) {
|
|
756
|
+
try {
|
|
757
|
+
if (bindParameters.length == 1 && typeof bindParameters[0] === "object") {
|
|
758
|
+
return statementRun.call(this.stmt, bindParameters[0]);
|
|
759
|
+
} else {
|
|
760
|
+
return statementRun.call(this.stmt, bindParameters.flat());
|
|
761
|
+
}
|
|
762
|
+
} catch (err) {
|
|
763
|
+
throw convertError(err);
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
get(...bindParameters) {
|
|
767
|
+
try {
|
|
768
|
+
if (bindParameters.length == 1 && typeof bindParameters[0] === "object") {
|
|
769
|
+
return statementGet.call(this.stmt, bindParameters[0]);
|
|
770
|
+
} else {
|
|
771
|
+
return statementGet.call(this.stmt, bindParameters.flat());
|
|
772
|
+
}
|
|
773
|
+
} catch (err) {
|
|
774
|
+
throw convertError(err);
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
iterate(...bindParameters) {
|
|
778
|
+
var rows = undefined;
|
|
779
|
+
if (bindParameters.length == 1 && typeof bindParameters[0] === "object") {
|
|
780
|
+
rows = statementRowsSync.call(this.stmt, bindParameters[0]);
|
|
781
|
+
} else {
|
|
782
|
+
rows = statementRowsSync.call(this.stmt, bindParameters.flat());
|
|
783
|
+
}
|
|
784
|
+
const iter = {
|
|
785
|
+
nextRows: Array(100),
|
|
786
|
+
nextRowIndex: 100,
|
|
787
|
+
next() {
|
|
788
|
+
try {
|
|
789
|
+
if (this.nextRowIndex === 100) {
|
|
790
|
+
rowsNext.call(rows, this.nextRows);
|
|
791
|
+
this.nextRowIndex = 0;
|
|
792
|
+
}
|
|
793
|
+
const row = this.nextRows[this.nextRowIndex];
|
|
794
|
+
this.nextRows[this.nextRowIndex] = undefined;
|
|
795
|
+
if (!row) {
|
|
796
|
+
return { done: true };
|
|
797
|
+
}
|
|
798
|
+
this.nextRowIndex++;
|
|
799
|
+
return { value: row, done: false };
|
|
800
|
+
} catch (err) {
|
|
801
|
+
throw convertError(err);
|
|
802
|
+
}
|
|
803
|
+
},
|
|
804
|
+
[Symbol.iterator]() {
|
|
805
|
+
return this;
|
|
806
|
+
}
|
|
807
|
+
};
|
|
808
|
+
return iter;
|
|
809
|
+
}
|
|
810
|
+
all(...bindParameters) {
|
|
811
|
+
try {
|
|
812
|
+
const result = [];
|
|
813
|
+
for (const row of this.iterate(...bindParameters)) {
|
|
814
|
+
if (this.pluckMode) {
|
|
815
|
+
result.push(row[Object.keys(row)[0]]);
|
|
816
|
+
} else {
|
|
817
|
+
result.push(row);
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
return result;
|
|
821
|
+
} catch (err) {
|
|
822
|
+
throw convertError(err);
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
interrupt() {
|
|
826
|
+
statementInterrupt.call(this.stmt);
|
|
827
|
+
}
|
|
828
|
+
columns() {
|
|
829
|
+
return statementColumns.call(this.stmt);
|
|
830
|
+
}
|
|
831
|
+
safeIntegers(toggle) {
|
|
832
|
+
statementSafeIntegers.call(this.stmt, toggle ?? true);
|
|
833
|
+
return this;
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
module.exports = Database;
|
|
837
|
+
module.exports.Authorization = Authorization;
|
|
838
|
+
module.exports.SqliteError = SqliteError;
|
|
839
|
+
});
|
|
840
|
+
|
|
841
|
+
// src/telemetry/sqlite.ts
|
|
842
|
+
var exports_sqlite = {};
|
|
843
|
+
__export(exports_sqlite, {
|
|
844
|
+
createSqliteStores: () => createSqliteStores
|
|
845
|
+
});
|
|
846
|
+
function openDatabase(dbPath) {
|
|
847
|
+
const db = new import_libsql.default(dbPath);
|
|
848
|
+
db.pragma("journal_mode = WAL");
|
|
849
|
+
db.pragma("synchronous = NORMAL");
|
|
850
|
+
db.exec(METRICS_SCHEMA);
|
|
851
|
+
db.exec(LOGS_SCHEMA);
|
|
852
|
+
return db;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
class SqliteTelemetryStore {
|
|
856
|
+
db;
|
|
857
|
+
retentionMs;
|
|
858
|
+
insertCount = 0;
|
|
859
|
+
insertStmt;
|
|
860
|
+
countStmt;
|
|
861
|
+
constructor(db, retentionDays) {
|
|
862
|
+
this.db = db;
|
|
863
|
+
this.retentionMs = retentionDays * 24 * 60 * 60 * 1000;
|
|
864
|
+
this.insertStmt = db.prepare(`
|
|
865
|
+
INSERT INTO metrics (
|
|
866
|
+
request_id, timestamp, adapter, model, request_model, mode,
|
|
867
|
+
is_resume, is_passthrough, lineage_type,
|
|
868
|
+
has_deferred_tools, deferred_tool_count, tool_count, discovered_tools, session_discovered_count,
|
|
869
|
+
message_count, sdk_session_id,
|
|
870
|
+
status, queue_wait_ms, proxy_overhead_ms, ttfb_ms,
|
|
871
|
+
upstream_duration_ms, total_duration_ms, content_blocks, text_events, error,
|
|
872
|
+
input_tokens, output_tokens, cache_read_input_tokens,
|
|
873
|
+
cache_creation_input_tokens, cache_hit_rate
|
|
874
|
+
) VALUES (
|
|
875
|
+
@requestId, @timestamp, @adapter, @model, @requestModel, @mode,
|
|
876
|
+
@isResume, @isPassthrough, @lineageType,
|
|
877
|
+
@hasDeferredTools, @deferredToolCount, @toolCount, @discoveredTools, @sessionDiscoveredCount,
|
|
878
|
+
@messageCount, @sdkSessionId,
|
|
879
|
+
@status, @queueWaitMs, @proxyOverheadMs, @ttfbMs,
|
|
880
|
+
@upstreamDurationMs, @totalDurationMs, @contentBlocks, @textEvents, @error,
|
|
881
|
+
@inputTokens, @outputTokens, @cacheReadInputTokens,
|
|
882
|
+
@cacheCreationInputTokens, @cacheHitRate
|
|
883
|
+
)
|
|
884
|
+
`);
|
|
885
|
+
this.countStmt = db.prepare("SELECT COUNT(*) as cnt FROM metrics");
|
|
886
|
+
}
|
|
887
|
+
record(metric) {
|
|
888
|
+
try {
|
|
889
|
+
this.insertStmt.run({
|
|
890
|
+
requestId: metric.requestId,
|
|
891
|
+
timestamp: metric.timestamp,
|
|
892
|
+
adapter: metric.adapter ?? null,
|
|
893
|
+
model: metric.model,
|
|
894
|
+
requestModel: metric.requestModel ?? null,
|
|
895
|
+
mode: metric.mode,
|
|
896
|
+
isResume: metric.isResume ? 1 : 0,
|
|
897
|
+
isPassthrough: metric.isPassthrough ? 1 : 0,
|
|
898
|
+
lineageType: metric.lineageType ?? null,
|
|
899
|
+
hasDeferredTools: metric.hasDeferredTools ? 1 : metric.hasDeferredTools === false ? 0 : null,
|
|
900
|
+
deferredToolCount: metric.deferredToolCount ?? null,
|
|
901
|
+
toolCount: metric.toolCount ?? null,
|
|
902
|
+
discoveredTools: metric.discoveredTools ? JSON.stringify(metric.discoveredTools) : null,
|
|
903
|
+
sessionDiscoveredCount: metric.sessionDiscoveredCount ?? null,
|
|
904
|
+
messageCount: metric.messageCount ?? null,
|
|
905
|
+
sdkSessionId: metric.sdkSessionId ?? null,
|
|
906
|
+
status: metric.status,
|
|
907
|
+
queueWaitMs: metric.queueWaitMs,
|
|
908
|
+
proxyOverheadMs: metric.proxyOverheadMs,
|
|
909
|
+
ttfbMs: metric.ttfbMs ?? null,
|
|
910
|
+
upstreamDurationMs: metric.upstreamDurationMs,
|
|
911
|
+
totalDurationMs: metric.totalDurationMs,
|
|
912
|
+
contentBlocks: metric.contentBlocks,
|
|
913
|
+
textEvents: metric.textEvents,
|
|
914
|
+
error: metric.error ?? null,
|
|
915
|
+
inputTokens: metric.inputTokens ?? null,
|
|
916
|
+
outputTokens: metric.outputTokens ?? null,
|
|
917
|
+
cacheReadInputTokens: metric.cacheReadInputTokens ?? null,
|
|
918
|
+
cacheCreationInputTokens: metric.cacheCreationInputTokens ?? null,
|
|
919
|
+
cacheHitRate: metric.cacheHitRate ?? null
|
|
920
|
+
});
|
|
921
|
+
} catch (err) {
|
|
922
|
+
console.error("[telemetry] SQLite write failed, skipping:", err);
|
|
923
|
+
return;
|
|
924
|
+
}
|
|
925
|
+
if (++this.insertCount % CLEANUP_INTERVAL === 0) {
|
|
926
|
+
this.cleanup();
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
get size() {
|
|
930
|
+
try {
|
|
931
|
+
return this.countStmt.get().cnt;
|
|
932
|
+
} catch {
|
|
933
|
+
return 0;
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
getRecent(options = {}) {
|
|
937
|
+
const { limit = 50, since, model } = options;
|
|
938
|
+
const conditions = [];
|
|
939
|
+
const params = { limit };
|
|
940
|
+
if (since !== undefined) {
|
|
941
|
+
conditions.push("timestamp >= @since");
|
|
942
|
+
params.since = since;
|
|
943
|
+
}
|
|
944
|
+
if (model !== undefined) {
|
|
945
|
+
conditions.push("model = @model");
|
|
946
|
+
params.model = model;
|
|
947
|
+
}
|
|
948
|
+
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
949
|
+
const sql = `SELECT * FROM metrics ${where} ORDER BY timestamp DESC, id DESC LIMIT @limit`;
|
|
950
|
+
try {
|
|
951
|
+
const rows = this.db.prepare(sql).all(params);
|
|
952
|
+
return rows.map(rowToMetric);
|
|
953
|
+
} catch {
|
|
954
|
+
return [];
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
getLastForSession(sdkSessionId) {
|
|
958
|
+
try {
|
|
959
|
+
const row = this.db.prepare(`SELECT * FROM metrics WHERE sdk_session_id = ? AND error IS NULL ORDER BY timestamp DESC, id DESC LIMIT 1`).get(sdkSessionId);
|
|
960
|
+
return row ? rowToMetric(row) : undefined;
|
|
961
|
+
} catch {
|
|
962
|
+
return;
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
summarize(windowMs = 60 * 60 * 1000) {
|
|
966
|
+
const since = Date.now() - windowMs;
|
|
967
|
+
const metrics = this.getRecent({ limit: 1e5, since });
|
|
968
|
+
return computeSummary(metrics, windowMs);
|
|
969
|
+
}
|
|
970
|
+
clear() {
|
|
971
|
+
try {
|
|
972
|
+
this.db.exec("DELETE FROM metrics");
|
|
973
|
+
} catch {}
|
|
974
|
+
}
|
|
975
|
+
cleanup() {
|
|
976
|
+
try {
|
|
977
|
+
const cutoff = Date.now() - this.retentionMs;
|
|
978
|
+
this.db.prepare("DELETE FROM metrics WHERE timestamp < ?").run(cutoff);
|
|
979
|
+
this.db.prepare("DELETE FROM diagnostic_logs WHERE timestamp < ?").run(cutoff);
|
|
980
|
+
this.db.pragma("wal_checkpoint(TRUNCATE)");
|
|
981
|
+
} catch (err) {
|
|
982
|
+
console.error("[telemetry] SQLite cleanup failed:", err);
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
class SqliteDiagnosticLogStore {
|
|
988
|
+
db;
|
|
989
|
+
insertStmt;
|
|
990
|
+
constructor(db) {
|
|
991
|
+
this.db = db;
|
|
992
|
+
this.insertStmt = db.prepare(`
|
|
993
|
+
INSERT INTO diagnostic_logs (timestamp, level, category, request_id, message)
|
|
994
|
+
VALUES (@timestamp, @level, @category, @requestId, @message)
|
|
995
|
+
`);
|
|
996
|
+
}
|
|
997
|
+
log(entry) {
|
|
998
|
+
try {
|
|
999
|
+
this.insertStmt.run({
|
|
1000
|
+
timestamp: Date.now(),
|
|
1001
|
+
level: entry.level,
|
|
1002
|
+
category: entry.category,
|
|
1003
|
+
requestId: entry.requestId ?? null,
|
|
1004
|
+
message: entry.message
|
|
1005
|
+
});
|
|
1006
|
+
} catch (err) {
|
|
1007
|
+
console.error("[telemetry] SQLite log write failed:", err);
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
session(message, requestId) {
|
|
1011
|
+
this.log({ level: "info", category: "session", message, requestId });
|
|
1012
|
+
}
|
|
1013
|
+
lineage(message, requestId) {
|
|
1014
|
+
this.log({ level: "warn", category: "lineage", message, requestId });
|
|
1015
|
+
}
|
|
1016
|
+
error(message, requestId) {
|
|
1017
|
+
this.log({ level: "error", category: "error", message, requestId });
|
|
1018
|
+
}
|
|
1019
|
+
getRecent(options = {}) {
|
|
1020
|
+
const { limit = 100, since, category } = options;
|
|
1021
|
+
const conditions = [];
|
|
1022
|
+
const params = { limit };
|
|
1023
|
+
if (since !== undefined) {
|
|
1024
|
+
conditions.push("timestamp >= @since");
|
|
1025
|
+
params.since = since;
|
|
1026
|
+
}
|
|
1027
|
+
if (category !== undefined) {
|
|
1028
|
+
conditions.push("category = @category");
|
|
1029
|
+
params.category = category;
|
|
1030
|
+
}
|
|
1031
|
+
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
1032
|
+
const sql = `SELECT * FROM diagnostic_logs ${where} ORDER BY timestamp DESC, id DESC LIMIT @limit`;
|
|
1033
|
+
try {
|
|
1034
|
+
const rows = this.db.prepare(sql).all(params);
|
|
1035
|
+
return rows.map((r) => ({
|
|
1036
|
+
timestamp: r.timestamp,
|
|
1037
|
+
level: r.level,
|
|
1038
|
+
category: r.category,
|
|
1039
|
+
requestId: r.request_id ?? undefined,
|
|
1040
|
+
message: r.message
|
|
1041
|
+
}));
|
|
1042
|
+
} catch {
|
|
1043
|
+
return [];
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
clear() {
|
|
1047
|
+
try {
|
|
1048
|
+
this.db.exec("DELETE FROM diagnostic_logs");
|
|
1049
|
+
} catch {}
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
function rowToMetric(r) {
|
|
1053
|
+
return {
|
|
1054
|
+
requestId: r.request_id,
|
|
1055
|
+
timestamp: r.timestamp,
|
|
1056
|
+
adapter: r.adapter ?? undefined,
|
|
1057
|
+
model: r.model,
|
|
1058
|
+
requestModel: r.request_model ?? undefined,
|
|
1059
|
+
mode: r.mode,
|
|
1060
|
+
isResume: r.is_resume === 1,
|
|
1061
|
+
isPassthrough: r.is_passthrough === 1,
|
|
1062
|
+
lineageType: r.lineage_type ?? undefined,
|
|
1063
|
+
hasDeferredTools: r.has_deferred_tools === 1 ? true : r.has_deferred_tools === 0 ? false : undefined,
|
|
1064
|
+
deferredToolCount: r.deferred_tool_count ?? undefined,
|
|
1065
|
+
toolCount: r.tool_count ?? undefined,
|
|
1066
|
+
discoveredTools: r.discovered_tools ? JSON.parse(r.discovered_tools) : undefined,
|
|
1067
|
+
sessionDiscoveredCount: r.session_discovered_count ?? undefined,
|
|
1068
|
+
messageCount: r.message_count ?? undefined,
|
|
1069
|
+
sdkSessionId: r.sdk_session_id ?? undefined,
|
|
1070
|
+
status: r.status,
|
|
1071
|
+
queueWaitMs: r.queue_wait_ms,
|
|
1072
|
+
proxyOverheadMs: r.proxy_overhead_ms,
|
|
1073
|
+
ttfbMs: r.ttfb_ms ?? null,
|
|
1074
|
+
upstreamDurationMs: r.upstream_duration_ms,
|
|
1075
|
+
totalDurationMs: r.total_duration_ms,
|
|
1076
|
+
contentBlocks: r.content_blocks,
|
|
1077
|
+
textEvents: r.text_events,
|
|
1078
|
+
error: r.error ?? null,
|
|
1079
|
+
inputTokens: r.input_tokens ?? undefined,
|
|
1080
|
+
outputTokens: r.output_tokens ?? undefined,
|
|
1081
|
+
cacheReadInputTokens: r.cache_read_input_tokens ?? undefined,
|
|
1082
|
+
cacheCreationInputTokens: r.cache_creation_input_tokens ?? undefined,
|
|
1083
|
+
cacheHitRate: r.cache_hit_rate ?? undefined
|
|
1084
|
+
};
|
|
1085
|
+
}
|
|
1086
|
+
function createSqliteStores(dbPath, retentionDays) {
|
|
1087
|
+
const db = openDatabase(dbPath);
|
|
1088
|
+
return {
|
|
1089
|
+
telemetry: new SqliteTelemetryStore(db, retentionDays),
|
|
1090
|
+
diagnostics: new SqliteDiagnosticLogStore(db),
|
|
1091
|
+
close: () => {
|
|
1092
|
+
try {
|
|
1093
|
+
db.close();
|
|
1094
|
+
} catch {}
|
|
1095
|
+
}
|
|
1096
|
+
};
|
|
1097
|
+
}
|
|
1098
|
+
var import_libsql, METRICS_SCHEMA = `
|
|
1099
|
+
CREATE TABLE IF NOT EXISTS metrics (
|
|
1100
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
1101
|
+
request_id TEXT NOT NULL,
|
|
1102
|
+
timestamp INTEGER NOT NULL,
|
|
1103
|
+
adapter TEXT,
|
|
1104
|
+
model TEXT NOT NULL,
|
|
1105
|
+
request_model TEXT,
|
|
1106
|
+
mode TEXT NOT NULL,
|
|
1107
|
+
is_resume INTEGER NOT NULL,
|
|
1108
|
+
is_passthrough INTEGER NOT NULL,
|
|
1109
|
+
lineage_type TEXT,
|
|
1110
|
+
has_deferred_tools INTEGER,
|
|
1111
|
+
deferred_tool_count INTEGER,
|
|
1112
|
+
tool_count INTEGER,
|
|
1113
|
+
discovered_tools TEXT,
|
|
1114
|
+
session_discovered_count INTEGER,
|
|
1115
|
+
message_count INTEGER,
|
|
1116
|
+
sdk_session_id TEXT,
|
|
1117
|
+
status INTEGER NOT NULL,
|
|
1118
|
+
queue_wait_ms REAL NOT NULL,
|
|
1119
|
+
proxy_overhead_ms REAL NOT NULL,
|
|
1120
|
+
ttfb_ms REAL,
|
|
1121
|
+
upstream_duration_ms REAL NOT NULL,
|
|
1122
|
+
total_duration_ms REAL NOT NULL,
|
|
1123
|
+
content_blocks INTEGER NOT NULL,
|
|
1124
|
+
text_events INTEGER NOT NULL,
|
|
1125
|
+
error TEXT,
|
|
1126
|
+
input_tokens INTEGER,
|
|
1127
|
+
output_tokens INTEGER,
|
|
1128
|
+
cache_read_input_tokens INTEGER,
|
|
1129
|
+
cache_creation_input_tokens INTEGER,
|
|
1130
|
+
cache_hit_rate REAL
|
|
1131
|
+
);
|
|
1132
|
+
CREATE INDEX IF NOT EXISTS idx_metrics_ts ON metrics(timestamp);
|
|
1133
|
+
CREATE INDEX IF NOT EXISTS idx_metrics_model ON metrics(model);
|
|
1134
|
+
CREATE INDEX IF NOT EXISTS idx_metrics_session_success ON metrics(sdk_session_id, timestamp DESC, id DESC);
|
|
1135
|
+
`, LOGS_SCHEMA = `
|
|
1136
|
+
CREATE TABLE IF NOT EXISTS diagnostic_logs (
|
|
1137
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
1138
|
+
timestamp INTEGER NOT NULL,
|
|
1139
|
+
level TEXT NOT NULL,
|
|
1140
|
+
category TEXT NOT NULL,
|
|
1141
|
+
request_id TEXT,
|
|
1142
|
+
message TEXT NOT NULL
|
|
1143
|
+
);
|
|
1144
|
+
CREATE INDEX IF NOT EXISTS idx_logs_ts ON diagnostic_logs(timestamp);
|
|
1145
|
+
CREATE INDEX IF NOT EXISTS idx_logs_cat ON diagnostic_logs(category);
|
|
1146
|
+
`, CLEANUP_INTERVAL = 1000;
|
|
1147
|
+
var init_sqlite = __esm(() => {
|
|
1148
|
+
init_percentiles();
|
|
1149
|
+
import_libsql = __toESM(require_libsql(), 1);
|
|
1150
|
+
});
|
|
1151
|
+
|
|
1152
|
+
// src/proxy/sdkFeatures.ts
|
|
1153
|
+
var exports_sdkFeatures = {};
|
|
1154
|
+
__export(exports_sdkFeatures, {
|
|
1155
|
+
validateFeatureUpdate: () => validateFeatureUpdate,
|
|
1156
|
+
updateAdapterFeatures: () => updateAdapterFeatures,
|
|
1157
|
+
resetAdapterFeatures: () => resetAdapterFeatures,
|
|
1158
|
+
getFeaturesForAdapter: () => getFeaturesForAdapter,
|
|
1159
|
+
getAllFeatureConfigs: () => getAllFeatureConfigs
|
|
1160
|
+
});
|
|
1161
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2, renameSync as renameSync2 } from "node:fs";
|
|
1162
|
+
import { join as join5 } from "node:path";
|
|
1163
|
+
import { homedir as homedir4 } from "node:os";
|
|
1164
|
+
function getConfigPath() {
|
|
1165
|
+
const dir = join5(homedir4(), ".config", "meridian");
|
|
1166
|
+
if (!existsSync4(dir))
|
|
1167
|
+
mkdirSync2(dir, { recursive: true });
|
|
1168
|
+
return join5(dir, "sdk-features.json");
|
|
1169
|
+
}
|
|
1170
|
+
function readConfig() {
|
|
1171
|
+
const now = Date.now();
|
|
1172
|
+
if (cachedConfig && now - lastReadTime < CACHE_TTL_MS)
|
|
1173
|
+
return cachedConfig;
|
|
1174
|
+
const path3 = getConfigPath();
|
|
1175
|
+
try {
|
|
1176
|
+
if (existsSync4(path3)) {
|
|
1177
|
+
cachedConfig = JSON.parse(readFileSync3(path3, "utf-8"));
|
|
1178
|
+
} else {
|
|
1179
|
+
cachedConfig = {};
|
|
1180
|
+
}
|
|
1181
|
+
} catch {
|
|
1182
|
+
cachedConfig = {};
|
|
1183
|
+
}
|
|
1184
|
+
lastReadTime = now;
|
|
1185
|
+
return cachedConfig;
|
|
1186
|
+
}
|
|
1187
|
+
function writeConfig(config) {
|
|
1188
|
+
const path3 = getConfigPath();
|
|
1189
|
+
const tmp = `${path3}.tmp`;
|
|
1190
|
+
try {
|
|
1191
|
+
writeFileSync2(tmp, JSON.stringify(config, null, 2));
|
|
1192
|
+
renameSync2(tmp, path3);
|
|
1193
|
+
cachedConfig = config;
|
|
1194
|
+
lastReadTime = Date.now();
|
|
1195
|
+
} catch (e) {
|
|
1196
|
+
console.error("[sdk-features] write failed:", e.message);
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
function getFeaturesForAdapter(adapterName) {
|
|
1200
|
+
const config = readConfig();
|
|
1201
|
+
const userOverrides = config[adapterName] ?? {};
|
|
1202
|
+
const adapterDefaults = ADAPTER_DEFAULTS[adapterName] ?? {};
|
|
1203
|
+
return {
|
|
1204
|
+
...DEFAULT_FEATURES,
|
|
1205
|
+
...adapterDefaults,
|
|
1206
|
+
...userOverrides
|
|
1207
|
+
};
|
|
1208
|
+
}
|
|
1209
|
+
function getAllFeatureConfigs() {
|
|
1210
|
+
const adapters = ["opencode", "crush", "forgecode", "pi", "droid", "passthrough"];
|
|
1211
|
+
const result = {};
|
|
1212
|
+
for (const name of adapters) {
|
|
1213
|
+
result[name] = getFeaturesForAdapter(name);
|
|
1214
|
+
}
|
|
1215
|
+
return result;
|
|
1216
|
+
}
|
|
1217
|
+
function validateFeatureUpdate(raw2) {
|
|
1218
|
+
if (raw2 === null || typeof raw2 !== "object" || Array.isArray(raw2)) {
|
|
1219
|
+
throw new Error("body must be a JSON object");
|
|
1220
|
+
}
|
|
1221
|
+
const input = raw2;
|
|
1222
|
+
const result = {};
|
|
1223
|
+
for (const [key, value] of Object.entries(input)) {
|
|
1224
|
+
if (!(key in DEFAULT_FEATURES))
|
|
1225
|
+
continue;
|
|
1226
|
+
const expected = typeof DEFAULT_FEATURES[key];
|
|
1227
|
+
if (key === "claudeMd") {
|
|
1228
|
+
if (typeof value !== "string" || !VALID_CLAUDE_MD_VALUES.has(value)) {
|
|
1229
|
+
throw new Error(`claudeMd must be one of: ${[...VALID_CLAUDE_MD_VALUES].join(", ")}`);
|
|
1230
|
+
}
|
|
1231
|
+
result[key] = value;
|
|
1232
|
+
} else if (key === "thinking") {
|
|
1233
|
+
if (typeof value !== "string" || !VALID_THINKING_VALUES.has(value)) {
|
|
1234
|
+
throw new Error(`thinking must be one of: ${[...VALID_THINKING_VALUES].join(", ")}`);
|
|
1235
|
+
}
|
|
1236
|
+
result[key] = value;
|
|
1237
|
+
} else if (expected === "boolean") {
|
|
1238
|
+
if (typeof value !== "boolean")
|
|
1239
|
+
throw new Error(`${key} must be a boolean`);
|
|
1240
|
+
result[key] = value;
|
|
1241
|
+
} else if (expected === "number") {
|
|
1242
|
+
if (typeof value !== "number" || !isFinite(value))
|
|
1243
|
+
throw new Error(`${key} must be a finite number`);
|
|
1244
|
+
result[key] = value;
|
|
1245
|
+
} else if (expected === "string") {
|
|
1246
|
+
if (typeof value !== "string")
|
|
1247
|
+
throw new Error(`${key} must be a string`);
|
|
1248
|
+
result[key] = value;
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
return result;
|
|
1252
|
+
}
|
|
1253
|
+
function updateAdapterFeatures(adapterName, features) {
|
|
1254
|
+
const config = readConfig();
|
|
1255
|
+
config[adapterName] = { ...config[adapterName] ?? {}, ...features };
|
|
1256
|
+
writeConfig(config);
|
|
1257
|
+
}
|
|
1258
|
+
function resetAdapterFeatures(adapterName) {
|
|
1259
|
+
const config = readConfig();
|
|
1260
|
+
delete config[adapterName];
|
|
1261
|
+
writeConfig(config);
|
|
1262
|
+
}
|
|
1263
|
+
var DEFAULT_FEATURES, ADAPTER_DEFAULTS, cachedConfig = null, lastReadTime = 0, CACHE_TTL_MS = 5000, VALID_CLAUDE_MD_VALUES, VALID_THINKING_VALUES;
|
|
1264
|
+
var init_sdkFeatures = __esm(() => {
|
|
1265
|
+
DEFAULT_FEATURES = {
|
|
1266
|
+
codeSystemPrompt: false,
|
|
1267
|
+
clientSystemPrompt: true,
|
|
1268
|
+
claudeMd: "off",
|
|
1269
|
+
memory: false,
|
|
1270
|
+
dreaming: false,
|
|
1271
|
+
thinking: "disabled",
|
|
1272
|
+
thinkingPassthrough: false,
|
|
1273
|
+
sharedMemory: false,
|
|
1274
|
+
maxBudgetUsd: 0,
|
|
1275
|
+
fallbackModel: "",
|
|
1276
|
+
sdkDebug: false,
|
|
1277
|
+
additionalDirectories: ""
|
|
1278
|
+
};
|
|
1279
|
+
ADAPTER_DEFAULTS = {};
|
|
1280
|
+
VALID_CLAUDE_MD_VALUES = new Set(["off", "project", "full"]);
|
|
1281
|
+
VALID_THINKING_VALUES = new Set(["adaptive", "enabled", "disabled"]);
|
|
1282
|
+
});
|
|
1283
|
+
|
|
1284
|
+
// src/telemetry/settingsPage.ts
|
|
1285
|
+
var exports_settingsPage = {};
|
|
1286
|
+
__export(exports_settingsPage, {
|
|
1287
|
+
settingsPageHtml: () => settingsPageHtml
|
|
1288
|
+
});
|
|
1289
|
+
var settingsPageHtml;
|
|
1290
|
+
var init_settingsPage = __esm(() => {
|
|
1291
|
+
init_profileBar();
|
|
1292
|
+
settingsPageHtml = `<!DOCTYPE html>
|
|
1293
|
+
<html lang="en">
|
|
1294
|
+
<head>
|
|
1295
|
+
<meta charset="utf-8">
|
|
1296
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
1297
|
+
<title>Meridian — SDK Features</title>
|
|
1298
|
+
<link rel="icon" type="image/svg+xml" href="/telemetry/icon.svg">
|
|
1299
|
+
<style>
|
|
1300
|
+
:root {
|
|
1301
|
+
--bg: #0d1117; --surface: #161b22; --border: #30363d;
|
|
1302
|
+
--text: #e6edf3; --muted: #8b949e; --accent: #58a6ff;
|
|
1303
|
+
--green: #3fb950; --yellow: #d29922; --red: #f85149;
|
|
1304
|
+
--purple: #bc8cff;
|
|
1305
|
+
}
|
|
1306
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
1307
|
+
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
|
|
1308
|
+
background: var(--bg); color: var(--text); padding: 0; line-height: 1.5; }
|
|
1309
|
+
${profileBarCss}
|
|
1310
|
+
.content { max-width: 900px; margin: 0 auto; padding: 24px; }
|
|
1311
|
+
h1 { font-size: 20px; font-weight: 600; margin-bottom: 4px; }
|
|
1312
|
+
.subtitle { color: var(--muted); font-size: 13px; margin-bottom: 24px; }
|
|
1313
|
+
.nav { display: flex; gap: 16px; margin-bottom: 24px; font-size: 13px; }
|
|
1314
|
+
.nav a { color: var(--muted); text-decoration: none; }
|
|
1315
|
+
.nav a:hover { color: var(--accent); }
|
|
1316
|
+
.nav a.active { color: var(--accent); }
|
|
1317
|
+
|
|
1318
|
+
.adapter-card {
|
|
1319
|
+
background: var(--surface); border: 1px solid var(--border); border-radius: 8px;
|
|
1320
|
+
padding: 20px; margin-bottom: 16px;
|
|
1321
|
+
}
|
|
1322
|
+
.adapter-header {
|
|
1323
|
+
display: flex; align-items: center; justify-content: space-between;
|
|
1324
|
+
margin-bottom: 16px;
|
|
1325
|
+
}
|
|
1326
|
+
.adapter-name { font-size: 16px; font-weight: 600; }
|
|
1327
|
+
.adapter-badge {
|
|
1328
|
+
font-size: 10px; padding: 2px 8px; border-radius: 10px;
|
|
1329
|
+
text-transform: uppercase; letter-spacing: 0.5px;
|
|
1330
|
+
}
|
|
1331
|
+
.badge-active { background: rgba(63, 185, 80, 0.15); color: var(--green); }
|
|
1332
|
+
.badge-inactive { background: rgba(139, 148, 158, 0.15); color: var(--muted); }
|
|
1333
|
+
|
|
1334
|
+
.feature-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
|
|
1335
|
+
@media (max-width: 600px) { .feature-grid { grid-template-columns: 1fr; } }
|
|
1336
|
+
|
|
1337
|
+
.feature-row {
|
|
1338
|
+
display: flex; align-items: center; justify-content: space-between;
|
|
1339
|
+
padding: 10px 14px; border-radius: 6px;
|
|
1340
|
+
background: var(--bg); border: 1px solid var(--border);
|
|
1341
|
+
}
|
|
1342
|
+
.feature-info { display: flex; flex-direction: column; }
|
|
1343
|
+
.feature-label { font-size: 13px; font-weight: 500; }
|
|
1344
|
+
.feature-desc { font-size: 11px; color: var(--muted); margin-top: 2px; }
|
|
1345
|
+
|
|
1346
|
+
/* Toggle switch */
|
|
1347
|
+
.toggle { position: relative; width: 36px; height: 20px; flex-shrink: 0; }
|
|
1348
|
+
.toggle input { opacity: 0; width: 0; height: 0; }
|
|
1349
|
+
.toggle-track {
|
|
1350
|
+
position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0;
|
|
1351
|
+
background: var(--border); border-radius: 10px; transition: background 0.2s;
|
|
1352
|
+
}
|
|
1353
|
+
.toggle-track::after {
|
|
1354
|
+
content: ""; position: absolute; height: 14px; width: 14px;
|
|
1355
|
+
left: 3px; bottom: 3px; background: var(--muted); border-radius: 50%;
|
|
1356
|
+
transition: transform 0.2s, background 0.2s;
|
|
1357
|
+
}
|
|
1358
|
+
.toggle input:checked + .toggle-track { background: var(--accent); }
|
|
1359
|
+
.toggle input:checked + .toggle-track::after {
|
|
1360
|
+
transform: translateX(16px); background: var(--text);
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
/* Select dropdown */
|
|
1364
|
+
.feature-select {
|
|
1365
|
+
background: var(--surface); color: var(--text); border: 1px solid var(--border);
|
|
1366
|
+
border-radius: 6px; padding: 4px 8px; font-size: 12px; cursor: pointer;
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
.save-indicator {
|
|
1370
|
+
position: fixed; bottom: 24px; right: 24px;
|
|
1371
|
+
background: var(--green); color: #000; padding: 8px 16px;
|
|
1372
|
+
border-radius: 6px; font-size: 13px; font-weight: 500;
|
|
1373
|
+
opacity: 0; transition: opacity 0.3s; pointer-events: none;
|
|
1374
|
+
}
|
|
1375
|
+
.save-indicator.visible { opacity: 1; }
|
|
1376
|
+
|
|
1377
|
+
.reset-btn {
|
|
1378
|
+
background: none; border: 1px solid var(--border); color: var(--muted);
|
|
1379
|
+
border-radius: 6px; padding: 4px 12px; font-size: 11px; cursor: pointer;
|
|
1380
|
+
}
|
|
1381
|
+
.reset-btn:hover { border-color: var(--red); color: var(--red); }
|
|
1382
|
+
</style>
|
|
1383
|
+
</head>
|
|
1384
|
+
<body>
|
|
1385
|
+
${profileBarHtml}
|
|
1386
|
+
<div class="content">
|
|
1387
|
+
<h1>SDK Features <span style="font-size:11px;padding:2px 8px;border-radius:10px;background:rgba(210,153,34,0.15);color:var(--yellow);vertical-align:middle;margin-left:8px">Experimental</span></h1>
|
|
1388
|
+
<p class="subtitle" style="max-width:720px;line-height:1.6">
|
|
1389
|
+
Unlock Claude Code features for any connected agent. Capabilities like auto-memory, dreaming, and CLAUDE.md — normally
|
|
1390
|
+
exclusive to Claude Code — become available to OpenCode, Crush, Droid, and any other harness routed through Meridian.
|
|
1391
|
+
Each agent keeps its own toolchain while gaining access to these additional features.<br><br>
|
|
1392
|
+
<strong style="color:var(--text)">System prompts:</strong> For these features to work correctly, both the Claude Code prompt and your client prompt
|
|
1393
|
+
should be enabled. When both are active, they are appended together — Claude Code's base instructions come first,
|
|
1394
|
+
followed by your agent's specific instructions.
|
|
1395
|
+
</p>
|
|
1396
|
+
|
|
1397
|
+
<div id="adapters"></div>
|
|
1398
|
+
</div>
|
|
1399
|
+
|
|
1400
|
+
<div class="save-indicator" id="saveIndicator">Saved</div>
|
|
1401
|
+
|
|
1402
|
+
<script>
|
|
1403
|
+
const FEATURES = [
|
|
1404
|
+
{ key: 'codeSystemPrompt', label: 'Claude Code Prompt', desc: 'Include the built-in Claude Code system prompt (tool usage rules, safety guidelines, coding best practices)', type: 'toggle' },
|
|
1405
|
+
{ key: 'clientSystemPrompt', label: 'Client Prompt', desc: 'Include the system prompt sent by the connecting agent (e.g. OpenCode or Crush instructions)', type: 'toggle' },
|
|
1406
|
+
{ key: 'claudeMd', label: 'CLAUDE.md', desc: 'Load CLAUDE.md instruction files — Off: none, Project: ./CLAUDE.md only, Full: ~/.claude/CLAUDE.md + ./CLAUDE.md', type: 'select', options: ['off', 'project', 'full'] },
|
|
1407
|
+
{ key: 'memory', label: 'Memory', desc: 'Read and write memories across sessions', type: 'toggle' },
|
|
1408
|
+
{ key: 'dreaming', label: 'Auto-Dream', desc: 'Background memory consolidation', type: 'toggle' },
|
|
1409
|
+
{ key: 'thinking', label: 'Thinking', desc: 'Extended thinking mode', type: 'select', options: ['disabled', 'adaptive', 'enabled'] },
|
|
1410
|
+
{ key: 'thinkingPassthrough', label: 'Thinking Passthrough', desc: 'Forward thinking blocks to the client', type: 'toggle' },
|
|
1411
|
+
{ key: 'sharedMemory', label: 'Shared Memory', desc: 'Share memory with Claude Code (~/.claude) instead of isolated storage', type: 'toggle' },
|
|
1412
|
+
{ key: 'maxBudgetUsd', label: 'Max Budget (USD)', desc: 'Per-request cost cap — query aborts if exceeded (0 = disabled)', type: 'number' },
|
|
1413
|
+
{ key: 'fallbackModel', label: 'Fallback Model', desc: 'Auto-fallback model if primary fails', type: 'select', options: ['', 'sonnet', 'opus', 'haiku', 'sonnet[1m]', 'opus[1m]'] },
|
|
1414
|
+
{ key: 'sdkDebug', label: 'SDK Debug Logging', desc: 'Enable verbose SDK debug output to proxy stderr', type: 'toggle' },
|
|
1415
|
+
{ key: 'additionalDirectories', label: 'Additional Directories', desc: 'Comma-separated extra paths Claude can access (monorepo libs, etc.)', type: 'text' },
|
|
1416
|
+
];
|
|
1417
|
+
|
|
1418
|
+
const ADAPTER_LABELS = {
|
|
1419
|
+
opencode: 'OpenCode',
|
|
1420
|
+
crush: 'Crush',
|
|
1421
|
+
forgecode: 'ForgeCode',
|
|
1422
|
+
pi: 'Pi',
|
|
1423
|
+
droid: 'Droid',
|
|
1424
|
+
passthrough: 'LiteLLM / Passthrough',
|
|
1425
|
+
};
|
|
1426
|
+
|
|
1427
|
+
let currentConfig = {};
|
|
1428
|
+
|
|
1429
|
+
async function loadConfig() {
|
|
1430
|
+
const res = await fetch('/settings/api/features');
|
|
1431
|
+
currentConfig = await res.json();
|
|
1432
|
+
render();
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
async function saveFeature(adapter, key, value) {
|
|
1436
|
+
const patch = {};
|
|
1437
|
+
patch[key] = value;
|
|
1438
|
+
await fetch('/settings/api/features/' + adapter, {
|
|
1439
|
+
method: 'PATCH',
|
|
1440
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1441
|
+
body: JSON.stringify(patch),
|
|
1442
|
+
});
|
|
1443
|
+
currentConfig[adapter][key] = value;
|
|
1444
|
+
showSaved();
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
async function resetAdapter(adapter) {
|
|
1448
|
+
await fetch('/settings/api/features/' + adapter, { method: 'DELETE' });
|
|
1449
|
+
await loadConfig();
|
|
1450
|
+
showSaved();
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1453
|
+
function showSaved() {
|
|
1454
|
+
const el = document.getElementById('saveIndicator');
|
|
1455
|
+
el.classList.add('visible');
|
|
1456
|
+
setTimeout(() => el.classList.remove('visible'), 1500);
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
function hasAnyEnabled(features) {
|
|
1460
|
+
return features.codeSystemPrompt || !features.clientSystemPrompt || features.claudeMd !== 'off' || features.memory || features.dreaming ||
|
|
1461
|
+
features.thinking !== 'disabled' || features.thinkingPassthrough ||
|
|
1462
|
+
features.sharedMemory || features.maxBudgetUsd > 0 ||
|
|
1463
|
+
features.fallbackModel || features.sdkDebug ||
|
|
1464
|
+
features.additionalDirectories;
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
function render() {
|
|
1468
|
+
const container = document.getElementById('adapters');
|
|
1469
|
+
container.innerHTML = '';
|
|
1470
|
+
|
|
1471
|
+
for (const [adapter, label] of Object.entries(ADAPTER_LABELS)) {
|
|
1472
|
+
const features = currentConfig[adapter] || {};
|
|
1473
|
+
const active = hasAnyEnabled(features);
|
|
1474
|
+
|
|
1475
|
+
const card = document.createElement('div');
|
|
1476
|
+
card.className = 'adapter-card';
|
|
1477
|
+
card.innerHTML = '<div class="adapter-header">' +
|
|
1478
|
+
'<span class="adapter-name">' + label + '</span>' +
|
|
1479
|
+
'<div style="display:flex;gap:8px;align-items:center">' +
|
|
1480
|
+
'<span class="adapter-badge ' + (active ? 'badge-active' : 'badge-inactive') + '">' +
|
|
1481
|
+
(active ? 'Active' : 'Default') +
|
|
1482
|
+
'</span>' +
|
|
1483
|
+
'<button class="reset-btn" onclick="resetAdapter(\\''+adapter+'\\')">Reset</button>' +
|
|
1484
|
+
'</div>' +
|
|
1485
|
+
'</div>';
|
|
1486
|
+
|
|
1487
|
+
const grid = document.createElement('div');
|
|
1488
|
+
grid.className = 'feature-grid';
|
|
1489
|
+
|
|
1490
|
+
for (const feat of FEATURES) {
|
|
1491
|
+
const row = document.createElement('div');
|
|
1492
|
+
row.className = 'feature-row';
|
|
1493
|
+
|
|
1494
|
+
const info = '<div class="feature-info"><span class="feature-label">' +
|
|
1495
|
+
feat.label + '</span><span class="feature-desc">' + feat.desc + '</span></div>';
|
|
1496
|
+
|
|
1497
|
+
if (feat.type === 'toggle') {
|
|
1498
|
+
const checked = features[feat.key] ? 'checked' : '';
|
|
1499
|
+
row.innerHTML = info +
|
|
1500
|
+
'<label class="toggle"><input type="checkbox" ' + checked +
|
|
1501
|
+
' onchange="saveFeature(\\''+adapter+'\\', \\''+feat.key+'\\', this.checked)">' +
|
|
1502
|
+
'<span class="toggle-track"></span></label>';
|
|
1503
|
+
} else if (feat.type === 'select') {
|
|
1504
|
+
const options = feat.options.map(o => {
|
|
1505
|
+
const label = o === '' ? '(None)' : o.charAt(0).toUpperCase()+o.slice(1);
|
|
1506
|
+
return '<option value="'+o+'"'+(features[feat.key]===o?' selected':'')+'>'+label+'</option>';
|
|
1507
|
+
}).join('');
|
|
1508
|
+
row.innerHTML = info +
|
|
1509
|
+
'<select class="feature-select" onchange="saveFeature(\\''+adapter+'\\', \\''+feat.key+'\\', this.value)">' +
|
|
1510
|
+
options + '</select>';
|
|
1511
|
+
} else if (feat.type === 'number') {
|
|
1512
|
+
const value = features[feat.key] ?? 0;
|
|
1513
|
+
row.innerHTML = info +
|
|
1514
|
+
'<input type="number" class="feature-select" style="width:80px;text-align:right" min="0" step="0.01" value="'+value+'"' +
|
|
1515
|
+
' onchange="saveFeature(\\''+adapter+'\\', \\''+feat.key+'\\', parseFloat(this.value)||0)">';
|
|
1516
|
+
} else if (feat.type === 'text') {
|
|
1517
|
+
const value = (features[feat.key] ?? '').toString().replace(/"/g, '"');
|
|
1518
|
+
row.innerHTML = info +
|
|
1519
|
+
'<input type="text" class="feature-select" style="width:180px" value="'+value+'"' +
|
|
1520
|
+
' onchange="saveFeature(\\''+adapter+'\\', \\''+feat.key+'\\', this.value)">';
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
grid.appendChild(row);
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
card.appendChild(grid);
|
|
1527
|
+
container.appendChild(card);
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
loadConfig();
|
|
1532
|
+
${profileBarJs}
|
|
1533
|
+
</script>
|
|
1534
|
+
</body>
|
|
1535
|
+
</html>`;
|
|
1536
|
+
});
|
|
26
1537
|
|
|
27
1538
|
// node_modules/hono/dist/compose.js
|
|
28
1539
|
var compose = (middleware, onError, onNotFound) => {
|
|
@@ -2187,7 +3698,8 @@ var DEFAULT_PROXY_CONFIG = {
|
|
|
2187
3698
|
idleTimeoutSeconds: 120,
|
|
2188
3699
|
silent: false,
|
|
2189
3700
|
profiles: undefined,
|
|
2190
|
-
defaultProfile: undefined
|
|
3701
|
+
defaultProfile: undefined,
|
|
3702
|
+
version: undefined
|
|
2191
3703
|
};
|
|
2192
3704
|
|
|
2193
3705
|
// src/env.ts
|
|
@@ -2198,6 +3710,13 @@ function envBool(suffix) {
|
|
|
2198
3710
|
const val = env(suffix);
|
|
2199
3711
|
return val === "1" || val === "true" || val === "yes";
|
|
2200
3712
|
}
|
|
3713
|
+
function envInt(suffix, defaultValue) {
|
|
3714
|
+
const val = env(suffix);
|
|
3715
|
+
if (!val)
|
|
3716
|
+
return defaultValue;
|
|
3717
|
+
const parsed = parseInt(val, 10);
|
|
3718
|
+
return Number.isFinite(parsed) ? parsed : defaultValue;
|
|
3719
|
+
}
|
|
2201
3720
|
|
|
2202
3721
|
// src/proxy/server.ts
|
|
2203
3722
|
import { exec as execCallback2 } from "child_process";
|
|
@@ -6270,7 +7789,12 @@ function stripMcpPrefix(toolName) {
|
|
|
6270
7789
|
return toolName;
|
|
6271
7790
|
}
|
|
6272
7791
|
|
|
7792
|
+
// src/telemetry/index.ts
|
|
7793
|
+
import { join } from "node:path";
|
|
7794
|
+
import { homedir } from "node:os";
|
|
7795
|
+
|
|
6273
7796
|
// src/telemetry/store.ts
|
|
7797
|
+
init_percentiles();
|
|
6274
7798
|
var DEFAULT_CAPACITY = 1000;
|
|
6275
7799
|
function getCapacity() {
|
|
6276
7800
|
const raw2 = process.env.MERIDIAN_TELEMETRY_SIZE ?? process.env.CLAUDE_PROXY_TELEMETRY_SIZE;
|
|
@@ -6282,7 +7806,7 @@ function getCapacity() {
|
|
|
6282
7806
|
return parsed;
|
|
6283
7807
|
}
|
|
6284
7808
|
|
|
6285
|
-
class
|
|
7809
|
+
class MemoryTelemetryStore {
|
|
6286
7810
|
buffer;
|
|
6287
7811
|
head = 0;
|
|
6288
7812
|
count = 0;
|
|
@@ -6329,94 +7853,7 @@ class TelemetryStore {
|
|
|
6329
7853
|
summarize(windowMs = 60 * 60 * 1000) {
|
|
6330
7854
|
const since = Date.now() - windowMs;
|
|
6331
7855
|
const metrics = this.getRecent({ limit: this.capacity, since });
|
|
6332
|
-
|
|
6333
|
-
const emptyPhase = { p50: 0, p95: 0, p99: 0, min: 0, max: 0, avg: 0 };
|
|
6334
|
-
return {
|
|
6335
|
-
windowMs,
|
|
6336
|
-
totalRequests: 0,
|
|
6337
|
-
errorCount: 0,
|
|
6338
|
-
requestsPerMinute: 0,
|
|
6339
|
-
queueWait: emptyPhase,
|
|
6340
|
-
proxyOverhead: emptyPhase,
|
|
6341
|
-
ttfb: emptyPhase,
|
|
6342
|
-
upstreamDuration: emptyPhase,
|
|
6343
|
-
totalDuration: emptyPhase,
|
|
6344
|
-
byModel: {},
|
|
6345
|
-
byMode: {},
|
|
6346
|
-
tokenUsage: {
|
|
6347
|
-
totalInputTokens: 0,
|
|
6348
|
-
totalOutputTokens: 0,
|
|
6349
|
-
totalCacheReadTokens: 0,
|
|
6350
|
-
totalCacheCreationTokens: 0,
|
|
6351
|
-
avgCacheHitRate: 0,
|
|
6352
|
-
cacheMissOnResumeCount: 0
|
|
6353
|
-
}
|
|
6354
|
-
};
|
|
6355
|
-
}
|
|
6356
|
-
const errorCount = metrics.filter((m) => m.error !== null).length;
|
|
6357
|
-
const oldest = metrics[metrics.length - 1].timestamp;
|
|
6358
|
-
const newest = metrics[0].timestamp;
|
|
6359
|
-
const spanMs = Math.max(newest - oldest, 1);
|
|
6360
|
-
const requestsPerMinute = metrics.length / spanMs * 60000;
|
|
6361
|
-
const queueWaits = metrics.map((m) => m.queueWaitMs);
|
|
6362
|
-
const overheads = metrics.map((m) => m.proxyOverheadMs);
|
|
6363
|
-
const ttfbs = metrics.filter((m) => m.ttfbMs !== null).map((m) => m.ttfbMs);
|
|
6364
|
-
const upstreams = metrics.map((m) => m.upstreamDurationMs);
|
|
6365
|
-
const totals = metrics.map((m) => m.totalDurationMs);
|
|
6366
|
-
const byModel = {};
|
|
6367
|
-
for (const m of metrics) {
|
|
6368
|
-
const modelKey = m.requestModel || m.model;
|
|
6369
|
-
const entry = byModel[modelKey] ??= { count: 0, totalMs: 0 };
|
|
6370
|
-
entry.count++;
|
|
6371
|
-
entry.totalMs += m.totalDurationMs;
|
|
6372
|
-
}
|
|
6373
|
-
const byMode = {};
|
|
6374
|
-
for (const m of metrics) {
|
|
6375
|
-
const entry = byMode[m.mode] ??= { count: 0, totalMs: 0 };
|
|
6376
|
-
entry.count++;
|
|
6377
|
-
entry.totalMs += m.totalDurationMs;
|
|
6378
|
-
}
|
|
6379
|
-
let totalInputTokens = 0;
|
|
6380
|
-
let totalOutputTokens = 0;
|
|
6381
|
-
let totalCacheReadTokens = 0;
|
|
6382
|
-
let totalCacheCreationTokens = 0;
|
|
6383
|
-
let cacheHitRateSum = 0;
|
|
6384
|
-
let cacheHitRateCount = 0;
|
|
6385
|
-
let cacheMissOnResumeCount = 0;
|
|
6386
|
-
for (const m of metrics) {
|
|
6387
|
-
totalInputTokens += m.inputTokens ?? 0;
|
|
6388
|
-
totalOutputTokens += m.outputTokens ?? 0;
|
|
6389
|
-
totalCacheReadTokens += m.cacheReadInputTokens ?? 0;
|
|
6390
|
-
totalCacheCreationTokens += m.cacheCreationInputTokens ?? 0;
|
|
6391
|
-
if (m.cacheHitRate !== undefined) {
|
|
6392
|
-
cacheHitRateSum += m.cacheHitRate;
|
|
6393
|
-
cacheHitRateCount++;
|
|
6394
|
-
}
|
|
6395
|
-
if (m.isResume && m.cacheHitRate !== undefined && m.cacheHitRate === 0) {
|
|
6396
|
-
cacheMissOnResumeCount++;
|
|
6397
|
-
}
|
|
6398
|
-
}
|
|
6399
|
-
return {
|
|
6400
|
-
windowMs,
|
|
6401
|
-
totalRequests: metrics.length,
|
|
6402
|
-
errorCount,
|
|
6403
|
-
requestsPerMinute: Math.round(requestsPerMinute * 100) / 100,
|
|
6404
|
-
queueWait: computePercentiles(queueWaits),
|
|
6405
|
-
proxyOverhead: computePercentiles(overheads),
|
|
6406
|
-
ttfb: ttfbs.length > 0 ? computePercentiles(ttfbs) : { p50: 0, p95: 0, p99: 0, min: 0, max: 0, avg: 0 },
|
|
6407
|
-
upstreamDuration: computePercentiles(upstreams),
|
|
6408
|
-
totalDuration: computePercentiles(totals),
|
|
6409
|
-
byModel: Object.fromEntries(Object.entries(byModel).map(([k, v]) => [k, { count: v.count, avgTotalMs: Math.round(v.totalMs / v.count) }])),
|
|
6410
|
-
byMode: Object.fromEntries(Object.entries(byMode).map(([k, v]) => [k, { count: v.count, avgTotalMs: Math.round(v.totalMs / v.count) }])),
|
|
6411
|
-
tokenUsage: {
|
|
6412
|
-
totalInputTokens,
|
|
6413
|
-
totalOutputTokens,
|
|
6414
|
-
totalCacheReadTokens,
|
|
6415
|
-
totalCacheCreationTokens,
|
|
6416
|
-
avgCacheHitRate: cacheHitRateCount > 0 ? Math.round(cacheHitRateSum / cacheHitRateCount * 100) / 100 : 0,
|
|
6417
|
-
cacheMissOnResumeCount
|
|
6418
|
-
}
|
|
6419
|
-
};
|
|
7856
|
+
return computeSummary(metrics, windowMs);
|
|
6420
7857
|
}
|
|
6421
7858
|
clear() {
|
|
6422
7859
|
this.buffer = new Array(this.capacity).fill(null);
|
|
@@ -6424,25 +7861,12 @@ class TelemetryStore {
|
|
|
6424
7861
|
this.count = 0;
|
|
6425
7862
|
}
|
|
6426
7863
|
}
|
|
6427
|
-
|
|
6428
|
-
|
|
6429
|
-
return { p50: 0, p95: 0, p99: 0, min: 0, max: 0, avg: 0 };
|
|
6430
|
-
const sorted = [...values].sort((a, b) => a - b);
|
|
6431
|
-
const sum = sorted.reduce((a, b) => a + b, 0);
|
|
6432
|
-
return {
|
|
6433
|
-
p50: sorted[Math.floor(sorted.length * 0.5)],
|
|
6434
|
-
p95: sorted[Math.floor(sorted.length * 0.95)],
|
|
6435
|
-
p99: sorted[Math.floor(sorted.length * 0.99)],
|
|
6436
|
-
min: sorted[0],
|
|
6437
|
-
max: sorted[sorted.length - 1],
|
|
6438
|
-
avg: Math.round(sum / sorted.length)
|
|
6439
|
-
};
|
|
6440
|
-
}
|
|
6441
|
-
var telemetryStore = new TelemetryStore;
|
|
7864
|
+
var telemetryStore = new MemoryTelemetryStore;
|
|
7865
|
+
|
|
6442
7866
|
// src/telemetry/logStore.ts
|
|
6443
7867
|
var DEFAULT_CAPACITY2 = 500;
|
|
6444
7868
|
|
|
6445
|
-
class
|
|
7869
|
+
class MemoryDiagnosticLogStore {
|
|
6446
7870
|
buffer;
|
|
6447
7871
|
head = 0;
|
|
6448
7872
|
count = 0;
|
|
@@ -6488,13 +7912,14 @@ class DiagnosticLogStore {
|
|
|
6488
7912
|
this.count = 0;
|
|
6489
7913
|
}
|
|
6490
7914
|
}
|
|
6491
|
-
var diagnosticLog = new
|
|
7915
|
+
var diagnosticLog = new MemoryDiagnosticLogStore;
|
|
6492
7916
|
// src/telemetry/routes.ts
|
|
6493
7917
|
import { existsSync, readFileSync } from "node:fs";
|
|
6494
7918
|
import { resolve, dirname } from "node:path";
|
|
6495
7919
|
import { fileURLToPath } from "node:url";
|
|
6496
7920
|
|
|
6497
7921
|
// src/telemetry/dashboard.ts
|
|
7922
|
+
init_profileBar();
|
|
6498
7923
|
var dashboardHtml = `<!DOCTYPE html>
|
|
6499
7924
|
<html lang="en">
|
|
6500
7925
|
<head>
|
|
@@ -6866,7 +8291,7 @@ function createTelemetryRoutes() {
|
|
|
6866
8291
|
const limit = Number.parseInt(c.req.query("limit") || "50", 10);
|
|
6867
8292
|
const since = c.req.query("since") ? Number.parseInt(c.req.query("since"), 10) : undefined;
|
|
6868
8293
|
const model = c.req.query("model") || undefined;
|
|
6869
|
-
const requests =
|
|
8294
|
+
const requests = telemetryStore2.getRecent({
|
|
6870
8295
|
limit: Math.min(limit, 500),
|
|
6871
8296
|
since,
|
|
6872
8297
|
model
|
|
@@ -6875,14 +8300,14 @@ function createTelemetryRoutes() {
|
|
|
6875
8300
|
});
|
|
6876
8301
|
routes.get("/summary", (c) => {
|
|
6877
8302
|
const windowMs = Number.parseInt(c.req.query("window") || "3600000", 10);
|
|
6878
|
-
const summary =
|
|
8303
|
+
const summary = telemetryStore2.summarize(windowMs);
|
|
6879
8304
|
return c.json(summary);
|
|
6880
8305
|
});
|
|
6881
8306
|
routes.get("/logs", (c) => {
|
|
6882
8307
|
const limit = Number.parseInt(c.req.query("limit") || "100", 10);
|
|
6883
8308
|
const since = c.req.query("since") ? Number.parseInt(c.req.query("since"), 10) : undefined;
|
|
6884
8309
|
const category = c.req.query("category") || undefined;
|
|
6885
|
-
const logs =
|
|
8310
|
+
const logs = diagnosticLog2.getRecent({
|
|
6886
8311
|
limit: Math.min(limit, 500),
|
|
6887
8312
|
since,
|
|
6888
8313
|
category
|
|
@@ -6892,6 +8317,7 @@ function createTelemetryRoutes() {
|
|
|
6892
8317
|
return routes;
|
|
6893
8318
|
}
|
|
6894
8319
|
// src/telemetry/landing.ts
|
|
8320
|
+
init_profileBar();
|
|
6895
8321
|
var landingHtml = `<!DOCTYPE html>
|
|
6896
8322
|
<html lang="en">
|
|
6897
8323
|
<head>
|
|
@@ -7004,7 +8430,7 @@ function render(h,s){
|
|
|
7004
8430
|
o+='</div>';
|
|
7005
8431
|
if(s.byModel&&Object.keys(s.byModel).length>0){o+='<div class="section"><div class="section-title">Models (24h)</div><div class="grid">';for(const[n,d]of Object.entries(s.byModel))o+=card(n,d.count,'avg '+ms(d.avgTotalMs),'');o+='</div></div>'}
|
|
7006
8432
|
o+='<div class="section"><div class="section-title">Connect an Agent</div><div class="snippet"><div class="snippet-tabs"><div class="snippet-tab active" onclick="showTab(this,'opencode')">OpenCode</div><div class="snippet-tab" onclick="showTab(this,'crush')">Crush</div><div class="snippet-tab" onclick="showTab(this,'generic')">Any Tool</div></div><div id="tab-opencode"><code>ANTHROPIC_API_KEY=x ANTHROPIC_BASE_URL=http://'+location.host+' opencode</code></div><div id="tab-crush" style="display:none"><code>'+JSON.stringify({providers:{meridian:{type:"anthropic",base_url:"http://"+location.host,api_key:"x",models:[{id:"claude-sonnet-4-5-20250514",name:"Sonnet 4.5"}]}}},null,2)+'</code></div><div id="tab-generic" style="display:none"><code>export ANTHROPIC_API_KEY=x\\nexport ANTHROPIC_BASE_URL=http://'+location.host+'</code></div></div></div>';
|
|
7007
|
-
o+='<div class="links"><a href="/telemetry" class="link">\uD83D\uDCCA Telemetry</a><a href="/profiles" class="link">\uD83D\uDC64 Profiles</a><a href="/health" class="link">\uD83E\uDE7A Health</a><a href="/telemetry/summary" class="link">\uD83D\uDCC8 Stats API</a><a href="https://github.com/rynfar/meridian" class="link">⚙️ GitHub</a></div>';
|
|
8433
|
+
o+='<div class="links"><a href="/telemetry" class="link">\uD83D\uDCCA Telemetry</a><a href="/settings" class="link">\uD83D\uDD27 Settings</a><a href="/profiles" class="link">\uD83D\uDC64 Profiles</a><a href="/health" class="link">\uD83E\uDE7A Health</a><a href="/telemetry/summary" class="link">\uD83D\uDCC8 Stats API</a><a href="https://github.com/rynfar/meridian" class="link">⚙️ GitHub</a></div>';
|
|
7008
8434
|
o+='<div class="footer">Meridian · Built on the <a href="https://github.com/anthropics/claude-code-sdk-js">Claude Code SDK</a></div>';
|
|
7009
8435
|
document.getElementById('content').innerHTML=o;
|
|
7010
8436
|
}
|
|
@@ -7014,6 +8440,95 @@ refresh();setInterval(refresh,10000);
|
|
|
7014
8440
|
</script>
|
|
7015
8441
|
</body>
|
|
7016
8442
|
</html>`;
|
|
8443
|
+
|
|
8444
|
+
// src/telemetry/index.ts
|
|
8445
|
+
init_percentiles();
|
|
8446
|
+
|
|
8447
|
+
// src/telemetry/prometheus.ts
|
|
8448
|
+
var DURATION_BUCKETS = [10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 1e4, 30000];
|
|
8449
|
+
var PHASES = [
|
|
8450
|
+
{ key: "queue_wait", extract: (m) => m.queueWaitMs },
|
|
8451
|
+
{ key: "proxy_overhead", extract: (m) => m.proxyOverheadMs },
|
|
8452
|
+
{ key: "ttfb", extract: (m) => m.ttfbMs },
|
|
8453
|
+
{ key: "upstream", extract: (m) => m.upstreamDurationMs },
|
|
8454
|
+
{ key: "total", extract: (m) => m.totalDurationMs }
|
|
8455
|
+
];
|
|
8456
|
+
function escapeLabelValue(v) {
|
|
8457
|
+
return v.replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/\n/g, "\\n");
|
|
8458
|
+
}
|
|
8459
|
+
function formatLabels(labels) {
|
|
8460
|
+
return Object.entries(labels).map(([k, v]) => `${k}="${escapeLabelValue(v)}"`).join(",");
|
|
8461
|
+
}
|
|
8462
|
+
function renderPrometheusMetrics(store) {
|
|
8463
|
+
const metrics = store.getRecent({ limit: 1e4 });
|
|
8464
|
+
const lines = [];
|
|
8465
|
+
lines.push("# HELP meridian_requests_total Total proxy requests");
|
|
8466
|
+
lines.push("# TYPE meridian_requests_total counter");
|
|
8467
|
+
const counters = new Map;
|
|
8468
|
+
for (const m of metrics) {
|
|
8469
|
+
const key = `${m.model}\x00${m.mode}\x00${m.status}`;
|
|
8470
|
+
counters.set(key, (counters.get(key) ?? 0) + 1);
|
|
8471
|
+
}
|
|
8472
|
+
for (const [key, count] of counters) {
|
|
8473
|
+
const [model, mode, status] = key.split("\x00");
|
|
8474
|
+
lines.push(`meridian_requests_total{${formatLabels({ model, mode, status })}} ${count}`);
|
|
8475
|
+
}
|
|
8476
|
+
lines.push("");
|
|
8477
|
+
lines.push("# HELP meridian_request_duration_ms Request duration by phase in milliseconds");
|
|
8478
|
+
lines.push("# TYPE meridian_request_duration_ms histogram");
|
|
8479
|
+
for (const phase of PHASES) {
|
|
8480
|
+
const values = [];
|
|
8481
|
+
for (const m of metrics) {
|
|
8482
|
+
const v = phase.extract(m);
|
|
8483
|
+
if (v !== null)
|
|
8484
|
+
values.push(v);
|
|
8485
|
+
}
|
|
8486
|
+
const phaseLabel = `phase="${escapeLabelValue(phase.key)}"`;
|
|
8487
|
+
for (const le of DURATION_BUCKETS) {
|
|
8488
|
+
const count = values.filter((v) => v <= le).length;
|
|
8489
|
+
lines.push(`meridian_request_duration_ms_bucket{${phaseLabel},le="${le}"} ${count}`);
|
|
8490
|
+
}
|
|
8491
|
+
lines.push(`meridian_request_duration_ms_bucket{${phaseLabel},le="+Inf"} ${values.length}`);
|
|
8492
|
+
const sum = values.reduce((a, b) => a + b, 0);
|
|
8493
|
+
lines.push(`meridian_request_duration_ms_sum{${phaseLabel}} ${sum}`);
|
|
8494
|
+
lines.push(`meridian_request_duration_ms_count{${phaseLabel}} ${values.length}`);
|
|
8495
|
+
}
|
|
8496
|
+
lines.push("");
|
|
8497
|
+
return lines.join(`
|
|
8498
|
+
`);
|
|
8499
|
+
}
|
|
8500
|
+
|
|
8501
|
+
// src/telemetry/index.ts
|
|
8502
|
+
init_sqlite();
|
|
8503
|
+
function getDefaultDbPath() {
|
|
8504
|
+
return join(homedir(), ".config", "meridian", "telemetry.db");
|
|
8505
|
+
}
|
|
8506
|
+
function createStores() {
|
|
8507
|
+
if (!envBool("TELEMETRY_PERSIST")) {
|
|
8508
|
+
return {
|
|
8509
|
+
telemetry: new MemoryTelemetryStore,
|
|
8510
|
+
diagnostics: new MemoryDiagnosticLogStore
|
|
8511
|
+
};
|
|
8512
|
+
}
|
|
8513
|
+
try {
|
|
8514
|
+
const { createSqliteStores: createSqliteStores2 } = (init_sqlite(), __toCommonJS(exports_sqlite));
|
|
8515
|
+
const dbPath = env("TELEMETRY_DB") ?? getDefaultDbPath();
|
|
8516
|
+
const retention = envInt("TELEMETRY_RETENTION_DAYS", 7);
|
|
8517
|
+
const stores = createSqliteStores2(dbPath, retention);
|
|
8518
|
+
console.error(`[telemetry] SQLite persistence enabled: ${dbPath} (${retention}d retention)`);
|
|
8519
|
+
return { telemetry: stores.telemetry, diagnostics: stores.diagnostics };
|
|
8520
|
+
} catch {
|
|
8521
|
+
console.warn("[telemetry] MERIDIAN_TELEMETRY_PERSIST is set but libsql is not installed. Run: npm install libsql");
|
|
8522
|
+
return {
|
|
8523
|
+
telemetry: new MemoryTelemetryStore,
|
|
8524
|
+
diagnostics: new MemoryDiagnosticLogStore
|
|
8525
|
+
};
|
|
8526
|
+
}
|
|
8527
|
+
}
|
|
8528
|
+
var stores = createStores();
|
|
8529
|
+
var telemetryStore2 = stores.telemetry;
|
|
8530
|
+
var diagnosticLog2 = stores.diagnostics;
|
|
8531
|
+
|
|
7017
8532
|
// src/proxy/errors.ts
|
|
7018
8533
|
function classifyError(errMsg) {
|
|
7019
8534
|
const lower = errMsg.toLowerCase();
|
|
@@ -7106,7 +8621,8 @@ function isExpiredTokenError(errMsg) {
|
|
|
7106
8621
|
function isStaleSessionError(error) {
|
|
7107
8622
|
if (!(error instanceof Error))
|
|
7108
8623
|
return false;
|
|
7109
|
-
|
|
8624
|
+
const msg = error.message;
|
|
8625
|
+
return msg.includes("No message found with message.uuid") || msg.includes("No conversation found with session ID") || msg.includes("No conversation found to continue") || msg.includes("No conversations found to resume");
|
|
7110
8626
|
}
|
|
7111
8627
|
function isRateLimitError(errMsg) {
|
|
7112
8628
|
const lower = errMsg.toLowerCase();
|
|
@@ -7121,7 +8637,7 @@ function isExtraUsageRequiredError(errMsg) {
|
|
|
7121
8637
|
import { exec as execCallback } from "child_process";
|
|
7122
8638
|
import { existsSync as existsSync2 } from "fs";
|
|
7123
8639
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
7124
|
-
import { join, dirname as dirname2 } from "path";
|
|
8640
|
+
import { join as join2, dirname as dirname2 } from "path";
|
|
7125
8641
|
import { promisify } from "util";
|
|
7126
8642
|
var exec = promisify(execCallback);
|
|
7127
8643
|
var AUTH_STATUS_CACHE_TTL_MS = 60000;
|
|
@@ -7263,7 +8779,7 @@ async function resolveClaudeExecutableAsync() {
|
|
|
7263
8779
|
if (runningUnderBun) {
|
|
7264
8780
|
try {
|
|
7265
8781
|
const sdkPath = fileURLToPath2(import.meta.resolve("@anthropic-ai/claude-agent-sdk"));
|
|
7266
|
-
const sdkCliJs =
|
|
8782
|
+
const sdkCliJs = join2(dirname2(sdkPath), "cli.js");
|
|
7267
8783
|
if (existsSync2(sdkCliJs)) {
|
|
7268
8784
|
cachedClaudePath = sdkCliJs;
|
|
7269
8785
|
return sdkCliJs;
|
|
@@ -7281,7 +8797,7 @@ async function resolveClaudeExecutableAsync() {
|
|
|
7281
8797
|
if (!runningUnderBun) {
|
|
7282
8798
|
try {
|
|
7283
8799
|
const sdkPath = fileURLToPath2(import.meta.resolve("@anthropic-ai/claude-agent-sdk"));
|
|
7284
|
-
const sdkCliJs =
|
|
8800
|
+
const sdkCliJs = join2(dirname2(sdkPath), "cli.js");
|
|
7285
8801
|
if (existsSync2(sdkCliJs)) {
|
|
7286
8802
|
cachedClaudePath = sdkCliJs;
|
|
7287
8803
|
return sdkCliJs;
|
|
@@ -7479,6 +8995,42 @@ function getLastUserMessage(messages) {
|
|
|
7479
8995
|
return messages.slice(-1);
|
|
7480
8996
|
}
|
|
7481
8997
|
|
|
8998
|
+
// src/proxy/auth.ts
|
|
8999
|
+
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
9000
|
+
function getConfiguredKey() {
|
|
9001
|
+
return process.env.MERIDIAN_API_KEY || undefined;
|
|
9002
|
+
}
|
|
9003
|
+
function safeCompare(a, b) {
|
|
9004
|
+
const hashA = createHmac("sha256", "meridian").update(a).digest();
|
|
9005
|
+
const hashB = createHmac("sha256", "meridian").update(b).digest();
|
|
9006
|
+
return timingSafeEqual(hashA, hashB);
|
|
9007
|
+
}
|
|
9008
|
+
function extractKey(c) {
|
|
9009
|
+
const apiKey = c.req.header("x-api-key");
|
|
9010
|
+
if (apiKey)
|
|
9011
|
+
return apiKey;
|
|
9012
|
+
const auth = c.req.header("authorization");
|
|
9013
|
+
if (auth?.startsWith("Bearer "))
|
|
9014
|
+
return auth.slice(7);
|
|
9015
|
+
return;
|
|
9016
|
+
}
|
|
9017
|
+
async function requireAuth(c, next) {
|
|
9018
|
+
const key = getConfiguredKey();
|
|
9019
|
+
if (!key)
|
|
9020
|
+
return next();
|
|
9021
|
+
const provided = extractKey(c);
|
|
9022
|
+
if (!provided || !safeCompare(provided, key)) {
|
|
9023
|
+
return c.json({
|
|
9024
|
+
type: "error",
|
|
9025
|
+
error: {
|
|
9026
|
+
type: "authentication_error",
|
|
9027
|
+
message: "Invalid or missing API key"
|
|
9028
|
+
}
|
|
9029
|
+
}, 401);
|
|
9030
|
+
}
|
|
9031
|
+
return next();
|
|
9032
|
+
}
|
|
9033
|
+
|
|
7482
9034
|
// src/proxy/fileChanges.ts
|
|
7483
9035
|
function extractFileChange(toolName, toolInput, mcpPrefix) {
|
|
7484
9036
|
if (!toolName.startsWith(mcpPrefix))
|
|
@@ -7965,6 +9517,9 @@ var crushAdapter = {
|
|
|
7965
9517
|
buildSystemContextAddendum(_body, _sdkAgents) {
|
|
7966
9518
|
return "";
|
|
7967
9519
|
},
|
|
9520
|
+
supportsThinking() {
|
|
9521
|
+
return true;
|
|
9522
|
+
},
|
|
7968
9523
|
extractFileChangesFromToolUse(toolName, toolInput) {
|
|
7969
9524
|
const input = toolInput;
|
|
7970
9525
|
const filePath = input?.file_path ?? input?.path;
|
|
@@ -8249,6 +9804,10 @@ function detectAdapter(c) {
|
|
|
8249
9804
|
return defaultAdapter;
|
|
8250
9805
|
}
|
|
8251
9806
|
|
|
9807
|
+
// src/proxy/query.ts
|
|
9808
|
+
import { join as join3 } from "node:path";
|
|
9809
|
+
import { homedir as homedir2 } from "node:os";
|
|
9810
|
+
|
|
8252
9811
|
// src/mcpTools.ts
|
|
8253
9812
|
import { createSdkMcpServer as createSdkMcpServer2, tool } from "@anthropic-ai/claude-agent-sdk";
|
|
8254
9813
|
import * as fs from "node:fs/promises";
|
|
@@ -13941,6 +15500,18 @@ function createOpencodeMcpServer() {
|
|
|
13941
15500
|
}
|
|
13942
15501
|
|
|
13943
15502
|
// src/proxy/query.ts
|
|
15503
|
+
function resolveSystemPrompt(systemContext, passthrough, settingSources, codeSystemPrompt, clientSystemPrompt) {
|
|
15504
|
+
const hasSettings = settingSources != null && settingSources.length > 0;
|
|
15505
|
+
const usePreset = codeSystemPrompt ?? (hasSettings || !passthrough && !!systemContext);
|
|
15506
|
+
const includeClient = clientSystemPrompt ?? true;
|
|
15507
|
+
const clientContext = includeClient ? systemContext : undefined;
|
|
15508
|
+
if (usePreset) {
|
|
15509
|
+
return clientContext ? { systemPrompt: { type: "preset", preset: "claude_code", append: clientContext } } : { systemPrompt: { type: "preset", preset: "claude_code" } };
|
|
15510
|
+
}
|
|
15511
|
+
if (clientContext)
|
|
15512
|
+
return { systemPrompt: clientContext };
|
|
15513
|
+
return {};
|
|
15514
|
+
}
|
|
13944
15515
|
function buildQueryOptions(ctx) {
|
|
13945
15516
|
const {
|
|
13946
15517
|
prompt,
|
|
@@ -13963,7 +15534,17 @@ function buildQueryOptions(ctx) {
|
|
|
13963
15534
|
effort,
|
|
13964
15535
|
thinking,
|
|
13965
15536
|
taskBudget,
|
|
13966
|
-
betas
|
|
15537
|
+
betas,
|
|
15538
|
+
settingSources,
|
|
15539
|
+
codeSystemPrompt,
|
|
15540
|
+
clientSystemPrompt,
|
|
15541
|
+
memory,
|
|
15542
|
+
dreaming,
|
|
15543
|
+
sharedMemory,
|
|
15544
|
+
maxBudgetUsd,
|
|
15545
|
+
fallbackModel,
|
|
15546
|
+
sdkDebug,
|
|
15547
|
+
additionalDirectories
|
|
13967
15548
|
} = ctx;
|
|
13968
15549
|
const blockedTools = [...adapter.getBlockedBuiltinTools(), ...adapter.getAgentIncompatibleTools()];
|
|
13969
15550
|
const mcpServerName = adapter.getMcpServerName();
|
|
@@ -13979,9 +15560,7 @@ function buildQueryOptions(ctx) {
|
|
|
13979
15560
|
...stream2 ? { includePartialMessages: true } : {},
|
|
13980
15561
|
permissionMode: "bypassPermissions",
|
|
13981
15562
|
allowDangerouslySkipPermissions: true,
|
|
13982
|
-
...systemContext
|
|
13983
|
-
systemPrompt: passthrough ? systemContext : { type: "preset", preset: "claude_code", append: systemContext }
|
|
13984
|
-
} : {},
|
|
15563
|
+
...resolveSystemPrompt(systemContext, passthrough, settingSources, codeSystemPrompt, clientSystemPrompt),
|
|
13985
15564
|
...passthrough ? {
|
|
13986
15565
|
disallowedTools: blockedTools,
|
|
13987
15566
|
...passthroughMcp ? {
|
|
@@ -13994,11 +15573,19 @@ function buildQueryOptions(ctx) {
|
|
|
13994
15573
|
mcpServers: { [mcpServerName]: createOpencodeMcpServer() }
|
|
13995
15574
|
},
|
|
13996
15575
|
plugins: [],
|
|
15576
|
+
...settingSources && settingSources.length > 0 ? {
|
|
15577
|
+
settingSources,
|
|
15578
|
+
settings: {
|
|
15579
|
+
autoMemoryEnabled: ctx.memory ?? true,
|
|
15580
|
+
autoDreamEnabled: ctx.dreaming ?? false
|
|
15581
|
+
}
|
|
15582
|
+
} : {},
|
|
13997
15583
|
...onStderr ? { stderr: onStderr } : {},
|
|
13998
15584
|
env: {
|
|
13999
15585
|
...cleanEnv,
|
|
14000
15586
|
ENABLE_TOOL_SEARCH: hasDeferredTools ? "true" : "false",
|
|
14001
15587
|
...passthrough ? { ENABLE_CLAUDEAI_MCP_SERVERS: "false" } : {},
|
|
15588
|
+
...sharedMemory ? { CLAUDE_CONFIG_DIR: join3(homedir2(), ".claude") } : {},
|
|
14002
15589
|
...process.getuid?.() === 0 ? { IS_SANDBOX: "1" } : {}
|
|
14003
15590
|
},
|
|
14004
15591
|
...Object.keys(sdkAgents).length > 0 ? { agents: sdkAgents } : {},
|
|
@@ -14008,7 +15595,11 @@ function buildQueryOptions(ctx) {
|
|
|
14008
15595
|
...effort ? { effort } : {},
|
|
14009
15596
|
...thinking ? { thinking } : {},
|
|
14010
15597
|
...taskBudget ? { taskBudget } : {},
|
|
14011
|
-
...betas && betas.length > 0 ? { betas } : {}
|
|
15598
|
+
...betas && betas.length > 0 ? { betas } : {},
|
|
15599
|
+
...maxBudgetUsd && maxBudgetUsd > 0 ? { maxBudgetUsd } : {},
|
|
15600
|
+
...fallbackModel ? { fallbackModel } : {},
|
|
15601
|
+
...sdkDebug ? { debug: true } : {},
|
|
15602
|
+
...additionalDirectories && additionalDirectories.length > 0 ? { additionalDirectories } : {}
|
|
14012
15603
|
}
|
|
14013
15604
|
};
|
|
14014
15605
|
}
|
|
@@ -14268,7 +15859,7 @@ function verifyLineage(cached, messages, cacheKey2, cache) {
|
|
|
14268
15859
|
if (suffixOverlap >= MIN_SUFFIX_FOR_COMPACTION && cached.messageHashes.length >= MIN_STORED_FOR_COMPACTION && suffixStartInIncoming > 0) {
|
|
14269
15860
|
const compactionMsg = `Compaction detected (key=${cacheKey2.slice(0, 8)}…): suffix overlap ${suffixOverlap}/${cached.messageHashes.length}. Allowing resume.`;
|
|
14270
15861
|
console.error(`[PROXY] ${compactionMsg}`);
|
|
14271
|
-
|
|
15862
|
+
diagnosticLog2.lineage(compactionMsg);
|
|
14272
15863
|
cached.lineageHash = computeLineageHash(messages);
|
|
14273
15864
|
cached.messageHashes = incomingHashes;
|
|
14274
15865
|
cached.messageCount = messages.length;
|
|
@@ -14286,13 +15877,13 @@ function verifyLineage(cached, messages, cacheKey2, cache) {
|
|
|
14286
15877
|
}
|
|
14287
15878
|
const undoMsg = `Undo detected (key=${cacheKey2.slice(0, 8)}…): prefix overlap ${prefixOverlap}/${cached.messageHashes.length}, rollback UUID: ${rollbackUuid || "none (legacy session)"}.`;
|
|
14288
15879
|
console.error(`[PROXY] ${undoMsg}`);
|
|
14289
|
-
|
|
15880
|
+
diagnosticLog2.lineage(undoMsg);
|
|
14290
15881
|
return { type: "undo", session: cached, prefixOverlap, rollbackUuid };
|
|
14291
15882
|
}
|
|
14292
15883
|
if (prefixOverlap > 0 && messages.length > cached.messageCount) {
|
|
14293
15884
|
const modifiedMsg = `Modified continuation (key=${cacheKey2.slice(0, 8)}…): prefix overlap ${prefixOverlap}/${cached.messageHashes.length}, incoming ${messages.length} msgs. Allowing resume.`;
|
|
14294
15885
|
console.error(`[PROXY] ${modifiedMsg}`);
|
|
14295
|
-
|
|
15886
|
+
diagnosticLog2.lineage(modifiedMsg);
|
|
14296
15887
|
cached.lineageHash = computeLineageHash(messages.slice(0, messages.length));
|
|
14297
15888
|
cached.messageHashes = incomingHashes;
|
|
14298
15889
|
cached.messageCount = messages.length;
|
|
@@ -14379,8 +15970,8 @@ import {
|
|
|
14379
15970
|
unlinkSync,
|
|
14380
15971
|
writeFileSync
|
|
14381
15972
|
} from "node:fs";
|
|
14382
|
-
import { homedir } from "node:os";
|
|
14383
|
-
import { join as
|
|
15973
|
+
import { homedir as homedir3 } from "node:os";
|
|
15974
|
+
import { join as join4 } from "node:path";
|
|
14384
15975
|
var DEFAULT_MAX_STORED_SESSIONS = 1e4;
|
|
14385
15976
|
var STALE_LOCK_THRESHOLD_MS = 30000;
|
|
14386
15977
|
function getMaxStoredSessions() {
|
|
@@ -14431,11 +16022,11 @@ function getStorePath() {
|
|
|
14431
16022
|
if (!existsSync3(dir)) {
|
|
14432
16023
|
mkdirSync(dir, { recursive: true });
|
|
14433
16024
|
}
|
|
14434
|
-
return
|
|
16025
|
+
return join4(dir, "sessions.json");
|
|
14435
16026
|
}
|
|
14436
16027
|
function getDefaultCacheDir() {
|
|
14437
|
-
const newDir =
|
|
14438
|
-
const oldDir =
|
|
16028
|
+
const newDir = join4(homedir3(), ".cache", "meridian");
|
|
16029
|
+
const oldDir = join4(homedir3(), ".cache", "opencode-claude-max-proxy");
|
|
14439
16030
|
if (existsSync3(newDir))
|
|
14440
16031
|
return newDir;
|
|
14441
16032
|
if (existsSync3(oldDir)) {
|
|
@@ -14867,7 +16458,7 @@ function checkTokenHealth(requestId, sdkSessionId, usage, turnNumber, isResume,
|
|
|
14867
16458
|
isResume,
|
|
14868
16459
|
isPassthrough
|
|
14869
16460
|
};
|
|
14870
|
-
const prevMetric =
|
|
16461
|
+
const prevMetric = telemetryStore2.getLastForSession(sdkSessionId);
|
|
14871
16462
|
const previous = prevMetric ? {
|
|
14872
16463
|
requestId: prevMetric.requestId,
|
|
14873
16464
|
turnNumber: turnNumber - 1,
|
|
@@ -14886,7 +16477,7 @@ function checkTokenHealth(requestId, sdkSessionId, usage, turnNumber, isResume,
|
|
|
14886
16477
|
console.error(line);
|
|
14887
16478
|
}
|
|
14888
16479
|
for (const a of anomalies) {
|
|
14889
|
-
|
|
16480
|
+
diagnosticLog2.log({
|
|
14890
16481
|
level: a.severity === "critical" ? "error" : "warn",
|
|
14891
16482
|
category: "token",
|
|
14892
16483
|
message: `${requestId} ${a.type}: ${a.detail}`,
|
|
@@ -14897,10 +16488,19 @@ function checkTokenHealth(requestId, sdkSessionId, usage, turnNumber, isResume,
|
|
|
14897
16488
|
}
|
|
14898
16489
|
function createProxyServer(config = {}) {
|
|
14899
16490
|
const finalConfig = { ...DEFAULT_PROXY_CONFIG, ...config };
|
|
16491
|
+
const serverVersion = finalConfig.version ?? "unknown";
|
|
14900
16492
|
restoreActiveProfile(finalConfig.profiles);
|
|
14901
16493
|
const sessionDiscoveredTools = new Map;
|
|
14902
16494
|
const app = new Hono2;
|
|
14903
16495
|
app.use("*", cors());
|
|
16496
|
+
app.use("/v1/*", requireAuth);
|
|
16497
|
+
app.use("/messages", requireAuth);
|
|
16498
|
+
app.use("/telemetry/*", requireAuth);
|
|
16499
|
+
app.use("/telemetry", requireAuth);
|
|
16500
|
+
app.use("/metrics", requireAuth);
|
|
16501
|
+
app.use("/profiles/*", requireAuth);
|
|
16502
|
+
app.use("/profiles", requireAuth);
|
|
16503
|
+
app.use("/auth/*", requireAuth);
|
|
14904
16504
|
app.get("/", (c) => {
|
|
14905
16505
|
const accept = c.req.header("accept") || "";
|
|
14906
16506
|
if (accept.includes("application/json") && !accept.includes("text/html")) {
|
|
@@ -14908,7 +16508,7 @@ function createProxyServer(config = {}) {
|
|
|
14908
16508
|
status: "ok",
|
|
14909
16509
|
service: "meridian",
|
|
14910
16510
|
format: "anthropic",
|
|
14911
|
-
endpoints: ["/v1/messages", "/messages", "/v1/chat/completions", "/v1/models", "/telemetry", "/health"]
|
|
16511
|
+
endpoints: ["/v1/messages", "/messages", "/v1/chat/completions", "/v1/models", "/telemetry", "/metrics", "/health"]
|
|
14912
16512
|
});
|
|
14913
16513
|
}
|
|
14914
16514
|
return c.html(landingHtml);
|
|
@@ -15003,6 +16603,14 @@ function createProxyServer(config = {}) {
|
|
|
15003
16603
|
console.error(`[PROXY] ${requestMeta.requestId} ignoring malformed x-opencode-thinking header: ${e instanceof Error ? e.message : String(e)}`);
|
|
15004
16604
|
}
|
|
15005
16605
|
}
|
|
16606
|
+
const { getFeaturesForAdapter: getFeaturesForAdapter2 } = (init_sdkFeatures(), __toCommonJS(exports_sdkFeatures));
|
|
16607
|
+
const sdkFeatures = getFeaturesForAdapter2(adapter.name);
|
|
16608
|
+
if (!thinking) {
|
|
16609
|
+
if (sdkFeatures.thinking === "adaptive")
|
|
16610
|
+
thinking = { type: "adaptive" };
|
|
16611
|
+
else if (sdkFeatures.thinking === "enabled")
|
|
16612
|
+
thinking = { type: "enabled" };
|
|
16613
|
+
}
|
|
15006
16614
|
const thinkingBetaStripped = betaFilter.stripped.some((b) => b.startsWith("interleaved-thinking"));
|
|
15007
16615
|
if (thinkingBetaStripped) {
|
|
15008
16616
|
thinking = { type: "disabled" };
|
|
@@ -15031,14 +16639,14 @@ function createProxyServer(config = {}) {
|
|
|
15031
16639
|
const toolCount = body.tools?.length ?? 0;
|
|
15032
16640
|
const requestLogLine = `${requestMeta.requestId} adapter=${adapter.name} model=${model} stream=${stream2} tools=${toolCount} lineage=${lineageType} session=${resumeSessionId?.slice(0, 8) || "new"}${isUndo && undoRollbackUuid ? ` rollback=${undoRollbackUuid.slice(0, 8)}` : ""}${agentMode ? ` agent=${agentMode}` : ""} active=${activeSessions}/${MAX_CONCURRENT_SESSIONS} msgCount=${msgCount}`;
|
|
15033
16641
|
console.error(`[PROXY] ${requestLogLine} msgs=${msgSummary}`);
|
|
15034
|
-
|
|
16642
|
+
diagnosticLog2.session(`${requestLogLine}`, requestMeta.requestId);
|
|
15035
16643
|
if (lineageResult.type === "diverged" && profileSessionId) {
|
|
15036
16644
|
const recovery = lookupSessionRecovery(profileSessionId);
|
|
15037
16645
|
if (recovery) {
|
|
15038
16646
|
const prevId = recovery.previousClaudeSessionId || recovery.claudeSessionId;
|
|
15039
16647
|
const recoveryMsg = `${requestMeta.requestId} SESSION RECOVERY: previous conversation available. Run: claude --resume ${prevId}`;
|
|
15040
16648
|
console.error(`[PROXY] ${recoveryMsg}`);
|
|
15041
|
-
|
|
16649
|
+
diagnosticLog2.session(recoveryMsg, requestMeta.requestId);
|
|
15042
16650
|
}
|
|
15043
16651
|
}
|
|
15044
16652
|
claudeLog("request.received", {
|
|
@@ -15155,6 +16763,7 @@ function createProxyServer(config = {}) {
|
|
|
15155
16763
|
}
|
|
15156
16764
|
const adapterPassthrough = adapter.usesPassthrough?.();
|
|
15157
16765
|
const passthrough = adapterPassthrough !== undefined ? adapterPassthrough : envBool("PASSTHROUGH");
|
|
16766
|
+
const settingSources = envBool("LOAD_CONTEXT") || sdkFeatures.claudeMd === "full" ? ["user", "project"] : sdkFeatures.claudeMd === "project" ? ["project"] : adapter.getSettingSources?.() ?? [];
|
|
15158
16767
|
const capturedToolUses = [];
|
|
15159
16768
|
const fileChanges = [];
|
|
15160
16769
|
let passthroughMcp;
|
|
@@ -15246,7 +16855,17 @@ function createProxyServer(config = {}) {
|
|
|
15246
16855
|
effort,
|
|
15247
16856
|
thinking,
|
|
15248
16857
|
taskBudget,
|
|
15249
|
-
betas
|
|
16858
|
+
betas,
|
|
16859
|
+
settingSources,
|
|
16860
|
+
codeSystemPrompt: sdkFeatures.codeSystemPrompt ? true : undefined,
|
|
16861
|
+
clientSystemPrompt: sdkFeatures.clientSystemPrompt === false ? false : undefined,
|
|
16862
|
+
memory: sdkFeatures.memory,
|
|
16863
|
+
dreaming: sdkFeatures.dreaming,
|
|
16864
|
+
sharedMemory: sdkFeatures.sharedMemory,
|
|
16865
|
+
maxBudgetUsd: sdkFeatures.maxBudgetUsd,
|
|
16866
|
+
fallbackModel: sdkFeatures.fallbackModel,
|
|
16867
|
+
sdkDebug: sdkFeatures.sdkDebug,
|
|
16868
|
+
additionalDirectories: sdkFeatures.additionalDirectories ? sdkFeatures.additionalDirectories.split(",").map((d) => d.trim()).filter(Boolean) : undefined
|
|
15250
16869
|
}))) {
|
|
15251
16870
|
if (event.type === "assistant" && !event.error) {
|
|
15252
16871
|
didYieldContent = true;
|
|
@@ -15290,7 +16909,17 @@ function createProxyServer(config = {}) {
|
|
|
15290
16909
|
effort,
|
|
15291
16910
|
thinking,
|
|
15292
16911
|
taskBudget,
|
|
15293
|
-
betas
|
|
16912
|
+
betas,
|
|
16913
|
+
settingSources,
|
|
16914
|
+
codeSystemPrompt: sdkFeatures.codeSystemPrompt ? true : undefined,
|
|
16915
|
+
clientSystemPrompt: sdkFeatures.clientSystemPrompt === false ? false : undefined,
|
|
16916
|
+
memory: sdkFeatures.memory,
|
|
16917
|
+
dreaming: sdkFeatures.dreaming,
|
|
16918
|
+
sharedMemory: sdkFeatures.sharedMemory,
|
|
16919
|
+
maxBudgetUsd: sdkFeatures.maxBudgetUsd,
|
|
16920
|
+
fallbackModel: sdkFeatures.fallbackModel,
|
|
16921
|
+
sdkDebug: sdkFeatures.sdkDebug,
|
|
16922
|
+
additionalDirectories: sdkFeatures.additionalDirectories ? sdkFeatures.additionalDirectories.split(",").map((d) => d.trim()).filter(Boolean) : undefined
|
|
15294
16923
|
}));
|
|
15295
16924
|
return;
|
|
15296
16925
|
}
|
|
@@ -15375,7 +17004,7 @@ function createProxyServer(config = {}) {
|
|
|
15375
17004
|
claudeLog("passthrough.toolsearch_filtered", { mode: "non_stream" });
|
|
15376
17005
|
continue;
|
|
15377
17006
|
}
|
|
15378
|
-
if (passthrough && !adapter.supportsThinking?.() && (b.type === "thinking" || b.type === "redacted_thinking")) {
|
|
17007
|
+
if (passthrough && !adapter.supportsThinking?.() && !sdkFeatures.thinkingPassthrough && (b.type === "thinking" || b.type === "redacted_thinking")) {
|
|
15379
17008
|
claudeLog("passthrough.thinking_stripped", { mode: "non_stream", type: b.type });
|
|
15380
17009
|
continue;
|
|
15381
17010
|
}
|
|
@@ -15471,7 +17100,7 @@ Subprocess stderr: ${stderrOutput}`;
|
|
|
15471
17100
|
});
|
|
15472
17101
|
const nonStreamQueueWaitMs = requestMeta.queueStartedAt - requestMeta.queueEnteredAt;
|
|
15473
17102
|
checkTokenHealth(requestMeta.requestId, currentSessionId || resumeSessionId, lastUsage, allMessages.length, isResume, passthrough);
|
|
15474
|
-
|
|
17103
|
+
telemetryStore2.record({
|
|
15475
17104
|
requestId: requestMeta.requestId,
|
|
15476
17105
|
timestamp: Date.now(),
|
|
15477
17106
|
adapter: adapter.name,
|
|
@@ -15514,7 +17143,12 @@ Subprocess stderr: ${stderrOutput}`;
|
|
|
15514
17143
|
content: contentBlocks,
|
|
15515
17144
|
model: body.model,
|
|
15516
17145
|
stop_reason: stopReason,
|
|
15517
|
-
usage: {
|
|
17146
|
+
usage: {
|
|
17147
|
+
input_tokens: lastUsage?.input_tokens ?? 0,
|
|
17148
|
+
output_tokens: lastUsage?.output_tokens ?? 0,
|
|
17149
|
+
cache_read_input_tokens: lastUsage?.cache_read_input_tokens,
|
|
17150
|
+
cache_creation_input_tokens: lastUsage?.cache_creation_input_tokens
|
|
17151
|
+
}
|
|
15518
17152
|
}), {
|
|
15519
17153
|
headers: {
|
|
15520
17154
|
"Content-Type": "application/json",
|
|
@@ -15590,7 +17224,17 @@ Subprocess stderr: ${stderrOutput}`;
|
|
|
15590
17224
|
effort,
|
|
15591
17225
|
thinking,
|
|
15592
17226
|
taskBudget,
|
|
15593
|
-
betas
|
|
17227
|
+
betas,
|
|
17228
|
+
settingSources,
|
|
17229
|
+
codeSystemPrompt: sdkFeatures.codeSystemPrompt ? true : undefined,
|
|
17230
|
+
clientSystemPrompt: sdkFeatures.clientSystemPrompt === false ? false : undefined,
|
|
17231
|
+
memory: sdkFeatures.memory,
|
|
17232
|
+
dreaming: sdkFeatures.dreaming,
|
|
17233
|
+
sharedMemory: sdkFeatures.sharedMemory,
|
|
17234
|
+
maxBudgetUsd: sdkFeatures.maxBudgetUsd,
|
|
17235
|
+
fallbackModel: sdkFeatures.fallbackModel,
|
|
17236
|
+
sdkDebug: sdkFeatures.sdkDebug,
|
|
17237
|
+
additionalDirectories: sdkFeatures.additionalDirectories ? sdkFeatures.additionalDirectories.split(",").map((d) => d.trim()).filter(Boolean) : undefined
|
|
15594
17238
|
}))) {
|
|
15595
17239
|
if (event.type === "stream_event") {
|
|
15596
17240
|
didYieldClientEvent = true;
|
|
@@ -15634,7 +17278,17 @@ Subprocess stderr: ${stderrOutput}`;
|
|
|
15634
17278
|
effort,
|
|
15635
17279
|
thinking,
|
|
15636
17280
|
taskBudget,
|
|
15637
|
-
betas
|
|
17281
|
+
betas,
|
|
17282
|
+
settingSources,
|
|
17283
|
+
codeSystemPrompt: sdkFeatures.codeSystemPrompt ? true : undefined,
|
|
17284
|
+
clientSystemPrompt: sdkFeatures.clientSystemPrompt === false ? false : undefined,
|
|
17285
|
+
memory: sdkFeatures.memory,
|
|
17286
|
+
dreaming: sdkFeatures.dreaming,
|
|
17287
|
+
sharedMemory: sdkFeatures.sharedMemory,
|
|
17288
|
+
maxBudgetUsd: sdkFeatures.maxBudgetUsd,
|
|
17289
|
+
fallbackModel: sdkFeatures.fallbackModel,
|
|
17290
|
+
sdkDebug: sdkFeatures.sdkDebug,
|
|
17291
|
+
additionalDirectories: sdkFeatures.additionalDirectories ? sdkFeatures.additionalDirectories.split(",").map((d) => d.trim()).filter(Boolean) : undefined
|
|
15638
17292
|
}));
|
|
15639
17293
|
return;
|
|
15640
17294
|
}
|
|
@@ -15771,7 +17425,7 @@ data: ${JSON.stringify({ type: "message_stop" })}
|
|
|
15771
17425
|
}
|
|
15772
17426
|
if (eventType === "content_block_start") {
|
|
15773
17427
|
const block = event.content_block;
|
|
15774
|
-
if (passthrough && !adapter.supportsThinking?.() && (block?.type === "thinking" || block?.type === "redacted_thinking")) {
|
|
17428
|
+
if (passthrough && !adapter.supportsThinking?.() && !sdkFeatures.thinkingPassthrough && (block?.type === "thinking" || block?.type === "redacted_thinking")) {
|
|
15775
17429
|
if (eventIndex !== undefined)
|
|
15776
17430
|
skipBlockIndices.add(eventIndex);
|
|
15777
17431
|
claudeLog("passthrough.thinking_stripped", { mode: "stream", type: block.type, index: eventIndex });
|
|
@@ -15969,7 +17623,7 @@ data: {"type":"message_stop"}
|
|
|
15969
17623
|
});
|
|
15970
17624
|
const streamQueueWaitMs = requestMeta.queueStartedAt - requestMeta.queueEnteredAt;
|
|
15971
17625
|
checkTokenHealth(requestMeta.requestId, currentSessionId || resumeSessionId, lastUsage, allMessages.length, isResume, passthrough);
|
|
15972
|
-
|
|
17626
|
+
telemetryStore2.record({
|
|
15973
17627
|
requestId: requestMeta.requestId,
|
|
15974
17628
|
timestamp: Date.now(),
|
|
15975
17629
|
adapter: adapter.name,
|
|
@@ -16088,7 +17742,7 @@ data: ${JSON.stringify({
|
|
|
16088
17742
|
const classified = classifyError(errMsg);
|
|
16089
17743
|
claudeLog("proxy.error", { error: errMsg, classified: classified.type });
|
|
16090
17744
|
const errorQueueWaitMs = requestMeta.queueStartedAt - requestMeta.queueEnteredAt;
|
|
16091
|
-
|
|
17745
|
+
telemetryStore2.record({
|
|
16092
17746
|
requestId: requestMeta.requestId,
|
|
16093
17747
|
timestamp: Date.now(),
|
|
16094
17748
|
adapter: adapter.name,
|
|
@@ -16132,6 +17786,39 @@ data: ${JSON.stringify({
|
|
|
16132
17786
|
app.post("/v1/messages", (c) => handleWithQueue(c, "/v1/messages"));
|
|
16133
17787
|
app.post("/messages", (c) => handleWithQueue(c, "/messages"));
|
|
16134
17788
|
app.route("/telemetry", createTelemetryRoutes());
|
|
17789
|
+
app.get("/settings", (c) => {
|
|
17790
|
+
const { settingsPageHtml: settingsPageHtml2 } = (init_settingsPage(), __toCommonJS(exports_settingsPage));
|
|
17791
|
+
return c.html(settingsPageHtml2);
|
|
17792
|
+
});
|
|
17793
|
+
app.get("/settings/api/features", (c) => {
|
|
17794
|
+
const { getAllFeatureConfigs: getAllFeatureConfigs2 } = (init_sdkFeatures(), __toCommonJS(exports_sdkFeatures));
|
|
17795
|
+
return c.json(getAllFeatureConfigs2());
|
|
17796
|
+
});
|
|
17797
|
+
app.patch("/settings/api/features/:adapter", async (c) => {
|
|
17798
|
+
const { validateFeatureUpdate: validateFeatureUpdate2, updateAdapterFeatures: updateAdapterFeatures2 } = (init_sdkFeatures(), __toCommonJS(exports_sdkFeatures));
|
|
17799
|
+
const adapter = c.req.param("adapter");
|
|
17800
|
+
const body = await c.req.json();
|
|
17801
|
+
let validated;
|
|
17802
|
+
try {
|
|
17803
|
+
validated = validateFeatureUpdate2(body);
|
|
17804
|
+
} catch (e) {
|
|
17805
|
+
return c.json({ error: e.message }, 400);
|
|
17806
|
+
}
|
|
17807
|
+
updateAdapterFeatures2(adapter, validated);
|
|
17808
|
+
return c.json({ ok: true });
|
|
17809
|
+
});
|
|
17810
|
+
app.delete("/settings/api/features/:adapter", (c) => {
|
|
17811
|
+
const { resetAdapterFeatures: resetAdapterFeatures2 } = (init_sdkFeatures(), __toCommonJS(exports_sdkFeatures));
|
|
17812
|
+
const adapter = c.req.param("adapter");
|
|
17813
|
+
resetAdapterFeatures2(adapter);
|
|
17814
|
+
return c.json({ ok: true });
|
|
17815
|
+
});
|
|
17816
|
+
app.get("/metrics", (c) => {
|
|
17817
|
+
const body = renderPrometheusMetrics(telemetryStore2);
|
|
17818
|
+
return c.body(body, 200, {
|
|
17819
|
+
"Content-Type": "text/plain; version=0.0.4; charset=utf-8"
|
|
17820
|
+
});
|
|
17821
|
+
});
|
|
16135
17822
|
app.get("/health", async (c) => {
|
|
16136
17823
|
try {
|
|
16137
17824
|
const healthProfile = resolveProfile(finalConfig.profiles, finalConfig.defaultProfile);
|
|
@@ -16140,6 +17827,7 @@ data: ${JSON.stringify({
|
|
|
16140
17827
|
if (!auth) {
|
|
16141
17828
|
return c.json({
|
|
16142
17829
|
status: "degraded",
|
|
17830
|
+
version: serverVersion,
|
|
16143
17831
|
error: "Could not verify auth status",
|
|
16144
17832
|
mode: envBool("PASSTHROUGH") ? "passthrough" : "internal"
|
|
16145
17833
|
});
|
|
@@ -16147,12 +17835,14 @@ data: ${JSON.stringify({
|
|
|
16147
17835
|
if (!auth.loggedIn) {
|
|
16148
17836
|
return c.json({
|
|
16149
17837
|
status: "unhealthy",
|
|
17838
|
+
version: serverVersion,
|
|
16150
17839
|
error: "Not logged in. Run: claude login",
|
|
16151
17840
|
auth: { loggedIn: false }
|
|
16152
17841
|
}, 503);
|
|
16153
17842
|
}
|
|
16154
17843
|
return c.json({
|
|
16155
17844
|
status: "healthy",
|
|
17845
|
+
version: serverVersion,
|
|
16156
17846
|
auth: {
|
|
16157
17847
|
loggedIn: true,
|
|
16158
17848
|
email: auth.email,
|
|
@@ -16164,6 +17854,7 @@ data: ${JSON.stringify({
|
|
|
16164
17854
|
} catch {
|
|
16165
17855
|
return c.json({
|
|
16166
17856
|
status: "degraded",
|
|
17857
|
+
version: serverVersion,
|
|
16167
17858
|
error: "Could not verify auth status",
|
|
16168
17859
|
mode: envBool("PASSTHROUGH") ? "passthrough" : "internal"
|
|
16169
17860
|
});
|
|
@@ -16191,7 +17882,7 @@ data: ${JSON.stringify({
|
|
|
16191
17882
|
});
|
|
16192
17883
|
});
|
|
16193
17884
|
app.get("/profiles", async (c) => {
|
|
16194
|
-
const { profilePageHtml } = await import("./profilePage-
|
|
17885
|
+
const { profilePageHtml } = await import("./profilePage-g5t5t6av.js");
|
|
16195
17886
|
return c.html(profilePageHtml);
|
|
16196
17887
|
});
|
|
16197
17888
|
app.post("/profiles/active", async (c) => {
|