@tonyclaw/llm-inspector 1.15.0 → 1.16.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/.output/cli.js +1 -0
- package/.output/nitro.json +1 -1
- package/.output/public/assets/index-BmkN9DxE.js +107 -0
- package/.output/public/assets/index-DPe3eOih.css +1 -0
- package/.output/public/assets/{main-BLYgekFx.js → main-BjnjXVBU.js} +1 -1
- package/.output/server/_libs/diff.mjs +2 -2
- package/.output/server/_ssr/{index-P66uoVEU.mjs → index-BIOEVAzU.mjs} +783 -588
- package/.output/server/_ssr/index.mjs +2 -2
- package/.output/server/_ssr/{router-DpLCKk51.mjs → router-THS9ptvu.mjs} +439 -177
- package/.output/server/{_tanstack-start-manifest_v-C9Wq6YdJ.mjs → _tanstack-start-manifest_v-BYhN7q_z.mjs} +1 -1
- package/.output/server/index.mjs +31 -31
- package/README.md +200 -113
- package/package.json +1 -1
- package/src/cli.ts +1 -0
- package/src/components/ProxyViewer.tsx +77 -85
- package/src/components/ProxyViewerContainer.tsx +148 -76
- package/src/components/providers/ImportWizardDialog.tsx +27 -3
- package/src/components/proxy-viewer/CompareDrawer.tsx +17 -4
- package/src/components/proxy-viewer/ConversationGroup.tsx +15 -47
- package/src/components/proxy-viewer/ConversationHeader.tsx +58 -5
- package/src/components/proxy-viewer/LogEntry.tsx +297 -329
- package/src/components/proxy-viewer/LogEntryHeader.tsx +126 -137
- package/src/components/proxy-viewer/ResponseView.tsx +14 -34
- package/src/components/proxy-viewer/StreamingChunkSequence.tsx +3 -3
- package/src/components/proxy-viewer/TurnGroup.tsx +25 -21
- package/src/components/proxy-viewer/diff/DiffView.tsx +5 -3
- package/src/components/proxy-viewer/formats/anthropic/ContentBlocks.tsx +13 -9
- package/src/components/proxy-viewer/formats/anthropic/ResponseView.tsx +3 -3
- package/src/components/proxy-viewer/formats/index.tsx +19 -10
- package/src/components/proxy-viewer/formats/openai/ResponseView.tsx +7 -3
- package/src/components/proxy-viewer/log-formats/anthropic.ts +48 -0
- package/src/components/proxy-viewer/log-formats/index.ts +23 -0
- package/src/components/proxy-viewer/log-formats/openai.ts +40 -0
- package/src/components/proxy-viewer/log-formats/types.ts +33 -0
- package/src/components/proxy-viewer/log-formats/unknown.ts +14 -0
- package/src/components/proxy-viewer/viewerState.ts +58 -0
- package/src/components/ui/json-viewer.tsx +3 -3
- package/src/lib/objectUtils.ts +22 -0
- package/src/proxy/claudeCodeStrip.ts +5 -8
- package/src/proxy/formats/index.ts +1 -1
- package/src/proxy/formats/registry.ts +9 -0
- package/src/proxy/handler.ts +2 -8
- package/src/proxy/logIndex.ts +58 -43
- package/src/proxy/logger.ts +51 -27
- package/src/proxy/openaiOrphanToolStrip.ts +11 -17
- package/src/proxy/providerImporters.ts +245 -19
- package/src/proxy/providers.ts +20 -7
- package/src/proxy/schemas.ts +5 -9
- package/src/proxy/socketTracker.ts +109 -78
- package/src/proxy/store.ts +68 -83
- package/src/routes/api/logs.ts +31 -2
- package/styles/globals.css +22 -0
- package/.output/public/assets/index-CMuJQyt1.js +0 -105
- package/.output/public/assets/index-DciyfYBk.css +0 -1
|
@@ -2,15 +2,16 @@ import { c as createRouter, a as createRootRoute, b as createFileRoute, l as laz
|
|
|
2
2
|
import { j as jsxRuntimeExports } from "../_libs/react.mjs";
|
|
3
3
|
import { S as SWRConfig } from "../_libs/swr.mjs";
|
|
4
4
|
import { existsSync, mkdirSync, writeFileSync, renameSync, copyFileSync, unlinkSync, readFileSync } from "node:fs";
|
|
5
|
+
import { mkdir, appendFile, readFile, open, readdir, stat, unlink, writeFile } from "node:fs/promises";
|
|
6
|
+
import { Buffer } from "node:buffer";
|
|
5
7
|
import path, { join, isAbsolute, dirname } from "node:path";
|
|
6
|
-
import { mkdir, appendFile, readFile, readdir, stat, unlink, writeFile } from "node:fs/promises";
|
|
7
8
|
import { C as Conf } from "../_libs/conf.mjs";
|
|
8
9
|
import { randomUUID } from "crypto";
|
|
9
10
|
import { exec } from "node:child_process";
|
|
10
11
|
import { promisify } from "node:util";
|
|
11
12
|
import { M as McpServer, W as WebStandardStreamableHTTPServerTransport } from "../_libs/modelcontextprotocol__server.mjs";
|
|
12
13
|
import { homedir } from "node:os";
|
|
13
|
-
import { d as object, _ as _enum, b as string, a as array, u as union,
|
|
14
|
+
import { d as object, _ as _enum, b as string, a as array, u as union, n as number, c as boolean, r as record, g as discriminatedUnion, l as literal, h as _null, k as lazy, e as unknown } from "../_libs/zod.mjs";
|
|
14
15
|
import "../_libs/tiny-warning.mjs";
|
|
15
16
|
import "../_libs/tanstack__router-core.mjs";
|
|
16
17
|
import "../_libs/cookie-es.mjs";
|
|
@@ -45,7 +46,7 @@ import "../_libs/debounce-fn.mjs";
|
|
|
45
46
|
import "../_libs/mimic-function.mjs";
|
|
46
47
|
import "../_libs/semver.mjs";
|
|
47
48
|
import "../_libs/uint8array-extras.mjs";
|
|
48
|
-
const appCss = "/assets/index-
|
|
49
|
+
const appCss = "/assets/index-DPe3eOih.css";
|
|
49
50
|
const Route$k = createRootRoute({
|
|
50
51
|
head: () => ({
|
|
51
52
|
meta: [
|
|
@@ -69,7 +70,7 @@ function RootDocument({ children }) {
|
|
|
69
70
|
] })
|
|
70
71
|
] });
|
|
71
72
|
}
|
|
72
|
-
const $$splitComponentImporter = () => import("./index-
|
|
73
|
+
const $$splitComponentImporter = () => import("./index-BIOEVAzU.mjs");
|
|
73
74
|
const Route$j = createFileRoute("/")({
|
|
74
75
|
component: lazyRouteComponent($$splitComponentImporter, "component")
|
|
75
76
|
});
|
|
@@ -165,40 +166,57 @@ const logger = {
|
|
|
165
166
|
};
|
|
166
167
|
const MAX_BUFFER_SIZE = 1e3;
|
|
167
168
|
let writeBuffer = [];
|
|
168
|
-
let
|
|
169
|
-
|
|
170
|
-
async function flushWriteBuffer() {
|
|
171
|
-
if (writeBuffer.length === 0) return;
|
|
172
|
-
if (isFlushing) return;
|
|
173
|
-
isFlushing = true;
|
|
169
|
+
let writeQueue = Promise.resolve();
|
|
170
|
+
function drainBuffer() {
|
|
174
171
|
const toWrite = writeBuffer.join("");
|
|
175
172
|
writeBuffer = [];
|
|
173
|
+
return toWrite;
|
|
174
|
+
}
|
|
175
|
+
async function flushWriteBuffer() {
|
|
176
|
+
let toWrite;
|
|
177
|
+
{
|
|
178
|
+
toWrite = drainBuffer();
|
|
179
|
+
}
|
|
180
|
+
if (toWrite.length === 0) return;
|
|
176
181
|
try {
|
|
177
182
|
const filePath = getLogFilePath();
|
|
178
183
|
await mkdir(path.dirname(filePath), { recursive: true });
|
|
179
184
|
await appendFile(filePath, toWrite, "utf-8");
|
|
180
185
|
} catch (err) {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
isFlushing = false;
|
|
186
|
+
writeBuffer.unshift(toWrite);
|
|
187
|
+
console.error("[logger] Failed to flush write buffer, re-queued:", err);
|
|
184
188
|
}
|
|
185
189
|
}
|
|
186
|
-
function scheduleFlush() {
|
|
187
|
-
if (writeScheduled) return;
|
|
188
|
-
writeScheduled = true;
|
|
189
|
-
void Promise.resolve().then(() => {
|
|
190
|
-
writeScheduled = false;
|
|
191
|
-
void flushWriteBuffer();
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
190
|
function appendLogEntry(entry) {
|
|
195
191
|
const line = JSON.stringify(entry) + "\n";
|
|
196
192
|
writeBuffer.push(line);
|
|
197
193
|
if (writeBuffer.length >= MAX_BUFFER_SIZE) {
|
|
198
|
-
|
|
199
|
-
} else {
|
|
200
|
-
|
|
194
|
+
writeQueue = writeQueue.then(() => flushWriteBuffer());
|
|
195
|
+
} else if (writeBuffer.length === 1) {
|
|
196
|
+
writeQueue = writeQueue.then(() => flushWriteBuffer());
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
async function flushLogBuffer() {
|
|
200
|
+
await writeQueue;
|
|
201
|
+
if (writeBuffer.length > 0) {
|
|
202
|
+
await flushWriteBuffer();
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
process.on("exit", () => {
|
|
206
|
+
if (writeBuffer.length > 0) {
|
|
207
|
+
const toWrite = drainBuffer();
|
|
208
|
+
try {
|
|
209
|
+
const filePath = getLogFilePath();
|
|
210
|
+
mkdirSync(path.dirname(filePath), { recursive: true });
|
|
211
|
+
writeFileSync(filePath, toWrite, "utf-8");
|
|
212
|
+
} catch {
|
|
213
|
+
}
|
|
201
214
|
}
|
|
215
|
+
});
|
|
216
|
+
for (const signal of ["SIGINT", "SIGTERM"]) {
|
|
217
|
+
process.on(signal, () => {
|
|
218
|
+
void flushLogBuffer().then(() => process.exit(0));
|
|
219
|
+
});
|
|
202
220
|
}
|
|
203
221
|
const INDEX_VERSION = 1;
|
|
204
222
|
const INDEX_FILE = "logs.idx";
|
|
@@ -255,9 +273,9 @@ async function saveIndex(index) {
|
|
|
255
273
|
logger.error("[logIndex] Failed to save index:", String(err));
|
|
256
274
|
}
|
|
257
275
|
}
|
|
258
|
-
async function addToIndex(id, file,
|
|
276
|
+
async function addToIndex(id, file, byteOffset, byteLength) {
|
|
259
277
|
const index = await loadIndex();
|
|
260
|
-
index.entries[id] = { id, file,
|
|
278
|
+
index.entries[id] = { id, file, byteOffset, byteLength };
|
|
261
279
|
if (id > index.maxId) {
|
|
262
280
|
index.maxId = id;
|
|
263
281
|
}
|
|
@@ -334,6 +352,14 @@ const JsonValueSchema = lazy(
|
|
|
334
352
|
function trustAsJsonValue(value) {
|
|
335
353
|
return value;
|
|
336
354
|
}
|
|
355
|
+
function isPlainRecord(val) {
|
|
356
|
+
return typeof val === "object" && val !== null && !Array.isArray(val);
|
|
357
|
+
}
|
|
358
|
+
function safeGetOwnProperty(obj, key) {
|
|
359
|
+
if (obj === null || typeof obj !== "object" || Array.isArray(obj)) return void 0;
|
|
360
|
+
const desc = Object.getOwnPropertyDescriptor(obj, key);
|
|
361
|
+
return desc?.value;
|
|
362
|
+
}
|
|
337
363
|
const CacheControl = object({
|
|
338
364
|
type: string(),
|
|
339
365
|
ttl: string().optional(),
|
|
@@ -519,8 +545,6 @@ const SseEventSchema = discriminatedUnion("type", [
|
|
|
519
545
|
SsePingEvent,
|
|
520
546
|
SseErrorEvent
|
|
521
547
|
]);
|
|
522
|
-
const InspectorRequestSchema = AnthropicRequestSchema;
|
|
523
|
-
const InspectorResponseSchema = AnthropicResponseSchema$1;
|
|
524
548
|
const OpenAIMessageContent = union([
|
|
525
549
|
string(),
|
|
526
550
|
array(
|
|
@@ -741,17 +765,6 @@ function extractRequestMetadata(body, headers) {
|
|
|
741
765
|
}
|
|
742
766
|
return { model: null, sessionId: null };
|
|
743
767
|
}
|
|
744
|
-
function parseRequest(rawBody) {
|
|
745
|
-
if (rawBody === null) return null;
|
|
746
|
-
try {
|
|
747
|
-
const json = JSON.parse(rawBody);
|
|
748
|
-
const result = InspectorRequestSchema.safeParse(json);
|
|
749
|
-
if (result.success) return result.data;
|
|
750
|
-
return null;
|
|
751
|
-
} catch {
|
|
752
|
-
return null;
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
768
|
const StreamingChunkSchema = object({
|
|
756
769
|
index: number(),
|
|
757
770
|
timestamp: number(),
|
|
@@ -822,38 +835,22 @@ function readChunks(path2) {
|
|
|
822
835
|
}
|
|
823
836
|
const MAX_MEMORY_CACHE = 100;
|
|
824
837
|
const memoryCache = /* @__PURE__ */ new Map();
|
|
825
|
-
let headId = null;
|
|
826
|
-
let tailId = null;
|
|
827
|
-
const prevMap = /* @__PURE__ */ new Map();
|
|
828
|
-
const nextMap = /* @__PURE__ */ new Map();
|
|
829
838
|
function evictOldestIfNeeded() {
|
|
830
|
-
while (memoryCache.size
|
|
831
|
-
const
|
|
832
|
-
|
|
833
|
-
memoryCache.delete(
|
|
834
|
-
prevMap.delete(oldest);
|
|
835
|
-
nextMap.delete(oldest);
|
|
836
|
-
if (next !== null) {
|
|
837
|
-
prevMap.set(next, null);
|
|
838
|
-
headId = next;
|
|
839
|
-
} else {
|
|
840
|
-
headId = null;
|
|
841
|
-
tailId = null;
|
|
842
|
-
}
|
|
839
|
+
while (memoryCache.size > MAX_MEMORY_CACHE) {
|
|
840
|
+
const oldestKey = memoryCache.keys().next().value;
|
|
841
|
+
if (oldestKey === void 0) break;
|
|
842
|
+
memoryCache.delete(oldestKey);
|
|
843
843
|
}
|
|
844
844
|
}
|
|
845
845
|
function addToCache(log) {
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
nextMap.set(tailId, log.id);
|
|
849
|
-
prevMap.set(log.id, tailId);
|
|
850
|
-
} else {
|
|
851
|
-
headId = log.id;
|
|
852
|
-
prevMap.set(log.id, null);
|
|
846
|
+
if (memoryCache.has(log.id)) {
|
|
847
|
+
memoryCache.delete(log.id);
|
|
853
848
|
}
|
|
854
|
-
nextMap.set(log.id, null);
|
|
855
|
-
tailId = log.id;
|
|
856
849
|
memoryCache.set(log.id, log);
|
|
850
|
+
evictOldestIfNeeded();
|
|
851
|
+
}
|
|
852
|
+
function removeFromCache(id) {
|
|
853
|
+
memoryCache.delete(id);
|
|
857
854
|
}
|
|
858
855
|
async function addTestLogEntry(entry) {
|
|
859
856
|
const id = await getNextLogId();
|
|
@@ -933,6 +930,22 @@ async function getLogById(id) {
|
|
|
933
930
|
if (!existsSync(filePath)) {
|
|
934
931
|
return null;
|
|
935
932
|
}
|
|
933
|
+
if (entry.byteOffset >= 0 && entry.byteLength > 0) {
|
|
934
|
+
const fh = await open(filePath, "r");
|
|
935
|
+
try {
|
|
936
|
+
const buffer = Buffer.alloc(entry.byteLength);
|
|
937
|
+
const { bytesRead } = await fh.read(buffer, 0, entry.byteLength, entry.byteOffset);
|
|
938
|
+
if (bytesRead === 0) return null;
|
|
939
|
+
const line = buffer.toString("utf-8", 0, bytesRead).trimEnd();
|
|
940
|
+
if (line === "") return null;
|
|
941
|
+
const parsed = JSON.parse(line);
|
|
942
|
+
const result = CapturedLogSchema.safeParse(parsed);
|
|
943
|
+
if (result.success) return result.data;
|
|
944
|
+
} finally {
|
|
945
|
+
await fh.close();
|
|
946
|
+
}
|
|
947
|
+
return null;
|
|
948
|
+
}
|
|
936
949
|
const content = readFileSync(filePath, "utf-8");
|
|
937
950
|
const lines = content.split("\n");
|
|
938
951
|
let lastMatch = null;
|
|
@@ -985,12 +998,18 @@ function getModels() {
|
|
|
985
998
|
function clearAllLogs() {
|
|
986
999
|
const count = memoryCache.size;
|
|
987
1000
|
memoryCache.clear();
|
|
988
|
-
headId = null;
|
|
989
|
-
tailId = null;
|
|
990
|
-
prevMap.clear();
|
|
991
|
-
nextMap.clear();
|
|
992
1001
|
return { cleared: count };
|
|
993
1002
|
}
|
|
1003
|
+
function clearLogsByIds(ids) {
|
|
1004
|
+
const unique = /* @__PURE__ */ new Set();
|
|
1005
|
+
for (const id of ids) {
|
|
1006
|
+
if (memoryCache.has(id)) unique.add(id);
|
|
1007
|
+
}
|
|
1008
|
+
for (const id of unique) {
|
|
1009
|
+
removeFromCache(id);
|
|
1010
|
+
}
|
|
1011
|
+
return { cleared: unique.size };
|
|
1012
|
+
}
|
|
994
1013
|
const sseHandlers = /* @__PURE__ */ new Set();
|
|
995
1014
|
function onLogUpdate(handler) {
|
|
996
1015
|
sseHandlers.add(handler);
|
|
@@ -1062,6 +1081,9 @@ const formatRegistry = new FormatRegistryImpl();
|
|
|
1062
1081
|
function formatForPath(path2) {
|
|
1063
1082
|
return formatRegistry.getByPath(path2) ?? null;
|
|
1064
1083
|
}
|
|
1084
|
+
function requestFormatForPath(path2) {
|
|
1085
|
+
return formatForPath(path2)?.format ?? "unknown";
|
|
1086
|
+
}
|
|
1065
1087
|
formatRegistry.registerPath(PATH_V1_MESSAGES, "anthropic");
|
|
1066
1088
|
formatRegistry.registerPath(PATH_V1_CHAT_COMPLETIONS, "openai");
|
|
1067
1089
|
formatRegistry.registerPath(PATH_CHAT_COMPLETIONS, "openai");
|
|
@@ -1882,6 +1904,12 @@ function importProviders(json) {
|
|
|
1882
1904
|
};
|
|
1883
1905
|
}
|
|
1884
1906
|
const providerArray = Array.isArray(parseResult.data) ? parseResult.data : parseResult.data.providers;
|
|
1907
|
+
const existingProviders = getProviders();
|
|
1908
|
+
const existingKeys = /* @__PURE__ */ new Set();
|
|
1909
|
+
for (const p of existingProviders) {
|
|
1910
|
+
existingKeys.add(`${p.name}::${p.anthropicBaseUrl ?? ""}`);
|
|
1911
|
+
}
|
|
1912
|
+
const newProviders = [];
|
|
1885
1913
|
for (let i = 0; i < providerArray.length; i++) {
|
|
1886
1914
|
const item = providerArray[i];
|
|
1887
1915
|
const result = ProviderConfigSchema.safeParse(item);
|
|
@@ -1890,10 +1918,8 @@ function importProviders(json) {
|
|
|
1890
1918
|
continue;
|
|
1891
1919
|
}
|
|
1892
1920
|
const provider = result.data;
|
|
1893
|
-
const
|
|
1894
|
-
|
|
1895
|
-
);
|
|
1896
|
-
if (existing) {
|
|
1921
|
+
const key = `${provider.name}::${provider.anthropicBaseUrl ?? ""}`;
|
|
1922
|
+
if (existingKeys.has(key)) {
|
|
1897
1923
|
errors.push(`Provider "${provider.name}" already exists, skipping`);
|
|
1898
1924
|
continue;
|
|
1899
1925
|
}
|
|
@@ -1903,11 +1929,13 @@ function importProviders(json) {
|
|
|
1903
1929
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1904
1930
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1905
1931
|
};
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
store.set("providers", providers);
|
|
1932
|
+
newProviders.push(newProvider);
|
|
1933
|
+
existingKeys.add(key);
|
|
1909
1934
|
imported++;
|
|
1910
1935
|
}
|
|
1936
|
+
if (newProviders.length > 0) {
|
|
1937
|
+
store.set("providers", [...existingProviders, ...newProviders]);
|
|
1938
|
+
}
|
|
1911
1939
|
} catch (err) {
|
|
1912
1940
|
return {
|
|
1913
1941
|
imported: 0,
|
|
@@ -1959,6 +1987,15 @@ function findProviderByModel(model) {
|
|
|
1959
1987
|
const execAsync = promisify(exec);
|
|
1960
1988
|
const cache = /* @__PURE__ */ new Map();
|
|
1961
1989
|
const CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
1990
|
+
const MAX_CACHE_SIZE = 200;
|
|
1991
|
+
const inflight = /* @__PURE__ */ new Map();
|
|
1992
|
+
function evictCacheIfNeeded() {
|
|
1993
|
+
while (cache.size > MAX_CACHE_SIZE) {
|
|
1994
|
+
const oldestKey = cache.keys().next().value;
|
|
1995
|
+
if (oldestKey === void 0) break;
|
|
1996
|
+
cache.delete(oldestKey);
|
|
1997
|
+
}
|
|
1998
|
+
}
|
|
1962
1999
|
function getFromCache(port) {
|
|
1963
2000
|
const entry = cache.get(port);
|
|
1964
2001
|
if (entry && Date.now() < entry.expiresAt) {
|
|
@@ -1969,6 +2006,7 @@ function getFromCache(port) {
|
|
|
1969
2006
|
}
|
|
1970
2007
|
function setCache(port, info) {
|
|
1971
2008
|
cache.set(port, { ...info, expiresAt: Date.now() + CACHE_TTL_MS });
|
|
2009
|
+
evictCacheIfNeeded();
|
|
1972
2010
|
}
|
|
1973
2011
|
function extractRemotePort(request) {
|
|
1974
2012
|
const socket = request.socket;
|
|
@@ -1976,43 +2014,83 @@ function extractRemotePort(request) {
|
|
|
1976
2014
|
if (remotePort !== void 0 && remotePort !== null) return remotePort;
|
|
1977
2015
|
return null;
|
|
1978
2016
|
}
|
|
1979
|
-
async function
|
|
2017
|
+
async function lookupClientInfo(port) {
|
|
1980
2018
|
const platform = process.platform;
|
|
1981
2019
|
try {
|
|
1982
2020
|
if (platform === "win32") {
|
|
1983
|
-
const
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
2021
|
+
const psScript = [
|
|
2022
|
+
`$conn = Get-NetTCPConnection -LocalPort ${port} -State Established -ErrorAction SilentlyContinue | Select-Object -First 1`,
|
|
2023
|
+
`if ($conn) {`,
|
|
2024
|
+
` $pid = $conn.OwningProcess`,
|
|
2025
|
+
` $proc = Get-CimInstance Win32_Process -Filter "ProcessId=$pid" -ErrorAction SilentlyContinue`,
|
|
2026
|
+
` $cmd = if ($proc) { $proc.CommandLine } else { "" }`,
|
|
2027
|
+
` Write-Output "$pid|$cmd"`,
|
|
2028
|
+
`}`
|
|
2029
|
+
].join("; ");
|
|
2030
|
+
const { stdout } = await execAsync(`powershell -NoProfile -Command "${psScript}"`, {
|
|
2031
|
+
windowsHide: true
|
|
2032
|
+
});
|
|
2033
|
+
const trimmed = stdout.trim();
|
|
2034
|
+
if (trimmed === "") {
|
|
2035
|
+
return { port, pid: null, cwd: null, projectFolder: null };
|
|
2036
|
+
}
|
|
2037
|
+
const [pidStr, ...cmdParts] = trimmed.split("|");
|
|
2038
|
+
const pid2 = parseInt(pidStr ?? "", 10);
|
|
2039
|
+
if (isNaN(pid2) || pid2 <= 0) {
|
|
2040
|
+
return { port, pid: null, cwd: null, projectFolder: null };
|
|
2041
|
+
}
|
|
2042
|
+
const commandLine = cmdParts.join("|").trim();
|
|
2043
|
+
const { cwd: cwd2, projectFolder: projectFolder2 } = parseCommandLine(commandLine, "\\");
|
|
2044
|
+
return { port, pid: pid2, cwd: cwd2, projectFolder: projectFolder2 };
|
|
2045
|
+
}
|
|
2046
|
+
let pid = null;
|
|
2047
|
+
if (platform === "darwin") {
|
|
1990
2048
|
const { stdout } = await execAsync(`lsof -i :${port} -sTCP:ESTABLISHED -t 2>/dev/null`, {
|
|
1991
2049
|
shell: "/bin/sh"
|
|
1992
2050
|
});
|
|
1993
|
-
const
|
|
1994
|
-
|
|
2051
|
+
const parsed = parseInt(stdout.trim(), 10);
|
|
2052
|
+
if (!isNaN(parsed) && parsed > 0) pid = parsed;
|
|
1995
2053
|
} else {
|
|
1996
2054
|
try {
|
|
1997
|
-
const { stdout
|
|
2055
|
+
const { stdout } = await execAsync(
|
|
1998
2056
|
`ss -tlnp 'sport = :${port}' 2>/dev/null | grep ESTAB | awk '{print $6}' | grep -o 'pid=[0-9]*' | cut -d= -f2`,
|
|
1999
2057
|
{ shell: "/bin/sh" }
|
|
2000
2058
|
);
|
|
2001
|
-
const
|
|
2002
|
-
if (!isNaN(
|
|
2059
|
+
const parsed = parseInt(stdout.trim(), 10);
|
|
2060
|
+
if (!isNaN(parsed) && parsed > 0) pid = parsed;
|
|
2003
2061
|
} catch {
|
|
2004
2062
|
}
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2063
|
+
if (pid === null) {
|
|
2064
|
+
const { stdout } = await execAsync(
|
|
2065
|
+
`netstat -tan 2>/dev/null | grep ':${port}' | grep ESTABLISHED | awk '{print $7}' | cut -d/ -f1`,
|
|
2066
|
+
{ shell: "/bin/sh" }
|
|
2067
|
+
);
|
|
2068
|
+
const parsed = parseInt(stdout.trim(), 10);
|
|
2069
|
+
if (!isNaN(parsed) && parsed > 0) pid = parsed;
|
|
2070
|
+
}
|
|
2011
2071
|
}
|
|
2072
|
+
if (pid === null) {
|
|
2073
|
+
return { port, pid: null, cwd: null, projectFolder: null };
|
|
2074
|
+
}
|
|
2075
|
+
const { cwd, projectFolder } = await lookupProcessInfo(pid);
|
|
2076
|
+
return { port, pid, cwd, projectFolder };
|
|
2012
2077
|
} catch (err) {
|
|
2013
|
-
logger.debug(`[socketTracker] Failed to lookup
|
|
2014
|
-
return null;
|
|
2078
|
+
logger.debug(`[socketTracker] Failed to lookup info for port ${port}:`, String(err));
|
|
2079
|
+
return { port, pid: null, cwd: null, projectFolder: null };
|
|
2080
|
+
}
|
|
2081
|
+
}
|
|
2082
|
+
function parseCommandLine(commandLine, sep) {
|
|
2083
|
+
if (!commandLine) return { cwd: null, projectFolder: null };
|
|
2084
|
+
const exeMatch = commandLine.match(/^"([^"]+)"/) || commandLine.match(/^([^\s]+)/);
|
|
2085
|
+
if (exeMatch?.[1] !== void 0) {
|
|
2086
|
+
const exePath = exeMatch[1];
|
|
2087
|
+
const lastSep = exePath.lastIndexOf(sep);
|
|
2088
|
+
if (lastSep > 0) {
|
|
2089
|
+
const exeDir = exePath.substring(0, lastSep);
|
|
2090
|
+
return { cwd: exeDir, projectFolder: exeDir.split(sep).pop() ?? null };
|
|
2091
|
+
}
|
|
2015
2092
|
}
|
|
2093
|
+
return { cwd: null, projectFolder: null };
|
|
2016
2094
|
}
|
|
2017
2095
|
async function lookupProcessInfo(pid) {
|
|
2018
2096
|
const platform = process.platform;
|
|
@@ -2027,15 +2105,7 @@ async function lookupProcessInfo(pid) {
|
|
|
2027
2105
|
if (line.includes("=")) {
|
|
2028
2106
|
const commandLine = (line.split("=")[1] ?? "").trim();
|
|
2029
2107
|
if (commandLine) {
|
|
2030
|
-
|
|
2031
|
-
if (exeMatch && exeMatch[1] !== void 0) {
|
|
2032
|
-
const exePath = exeMatch[1];
|
|
2033
|
-
const exeDir = exePath.substring(0, exePath.lastIndexOf("\\"));
|
|
2034
|
-
return {
|
|
2035
|
-
cwd: exeDir,
|
|
2036
|
-
projectFolder: exeDir.split("\\").pop() ?? null
|
|
2037
|
-
};
|
|
2038
|
-
}
|
|
2108
|
+
return parseCommandLine(commandLine, "\\");
|
|
2039
2109
|
}
|
|
2040
2110
|
}
|
|
2041
2111
|
}
|
|
@@ -2046,15 +2116,7 @@ async function lookupProcessInfo(pid) {
|
|
|
2046
2116
|
});
|
|
2047
2117
|
const commandLine = stdout.trim();
|
|
2048
2118
|
if (commandLine) {
|
|
2049
|
-
|
|
2050
|
-
if (exeMatch && exeMatch[1] !== void 0) {
|
|
2051
|
-
const exePath = exeMatch[1];
|
|
2052
|
-
const exeDir = exePath.substring(0, exePath.lastIndexOf("/"));
|
|
2053
|
-
return {
|
|
2054
|
-
cwd: exeDir,
|
|
2055
|
-
projectFolder: exeDir.split("/").pop() ?? null
|
|
2056
|
-
};
|
|
2057
|
-
}
|
|
2119
|
+
return parseCommandLine(commandLine, "/");
|
|
2058
2120
|
}
|
|
2059
2121
|
} catch {
|
|
2060
2122
|
}
|
|
@@ -2064,11 +2126,7 @@ async function lookupProcessInfo(pid) {
|
|
|
2064
2126
|
});
|
|
2065
2127
|
const exePath = stdout.trim();
|
|
2066
2128
|
if (exePath) {
|
|
2067
|
-
|
|
2068
|
-
return {
|
|
2069
|
-
cwd: exeDir,
|
|
2070
|
-
projectFolder: exePath.split("/").pop() ?? null
|
|
2071
|
-
};
|
|
2129
|
+
return parseCommandLine(exePath, "/");
|
|
2072
2130
|
}
|
|
2073
2131
|
}
|
|
2074
2132
|
}
|
|
@@ -2084,16 +2142,21 @@ async function getClientInfo(request) {
|
|
|
2084
2142
|
}
|
|
2085
2143
|
const cached = getFromCache(port);
|
|
2086
2144
|
if (cached) return cached;
|
|
2087
|
-
const
|
|
2088
|
-
if (
|
|
2089
|
-
|
|
2090
|
-
setCache(port,
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2145
|
+
const existing = inflight.get(port);
|
|
2146
|
+
if (existing) return existing;
|
|
2147
|
+
const promise = lookupClientInfo(port).then((info) => {
|
|
2148
|
+
setCache(port, info);
|
|
2149
|
+
inflight.delete(port);
|
|
2150
|
+
return info;
|
|
2151
|
+
}).catch((err) => {
|
|
2152
|
+
inflight.delete(port);
|
|
2153
|
+
logger.debug(`[socketTracker] Lookup failed for port ${port}:`, String(err));
|
|
2154
|
+
const fallback = { port, pid: null, cwd: null, projectFolder: null };
|
|
2155
|
+
setCache(port, fallback);
|
|
2156
|
+
return fallback;
|
|
2157
|
+
});
|
|
2158
|
+
inflight.set(port, promise);
|
|
2159
|
+
return promise;
|
|
2097
2160
|
}
|
|
2098
2161
|
const RuntimeConfigSchema = object({
|
|
2099
2162
|
stripClaudeCodeBillingHeader: boolean()
|
|
@@ -2178,18 +2241,14 @@ function isClaudeCodeBillingHeaderBlock(text) {
|
|
|
2178
2241
|
const trimmed = text.trimStart();
|
|
2179
2242
|
return trimmed.toLowerCase().startsWith(BILLING_HEADER_PREFIX);
|
|
2180
2243
|
}
|
|
2181
|
-
function getOwnProperty$1(obj, key) {
|
|
2182
|
-
const desc = Object.getOwnPropertyDescriptor(obj, key);
|
|
2183
|
-
return desc?.value;
|
|
2184
|
-
}
|
|
2185
2244
|
function isObjectWithSystem(value) {
|
|
2186
|
-
if (
|
|
2245
|
+
if (!isPlainRecord(value)) return false;
|
|
2187
2246
|
return Object.prototype.hasOwnProperty.call(value, "system");
|
|
2188
2247
|
}
|
|
2189
2248
|
function isBillingHeaderTextBlock(block) {
|
|
2190
|
-
if (
|
|
2191
|
-
const typeVal =
|
|
2192
|
-
const textVal =
|
|
2249
|
+
if (!isPlainRecord(block)) return false;
|
|
2250
|
+
const typeVal = safeGetOwnProperty(block, "type");
|
|
2251
|
+
const textVal = safeGetOwnProperty(block, "text");
|
|
2193
2252
|
if (typeof typeVal !== "string" || typeVal !== "text") return false;
|
|
2194
2253
|
if (typeof textVal !== "string") return false;
|
|
2195
2254
|
return isClaudeCodeBillingHeaderBlock(textVal);
|
|
@@ -2229,25 +2288,19 @@ function stripClaudeCodeBillingHeader(rawBody) {
|
|
|
2229
2288
|
}
|
|
2230
2289
|
const ROLE_ASSISTANT = "assistant";
|
|
2231
2290
|
const ROLE_TOOL = "tool";
|
|
2232
|
-
function getOwnProperty(obj, key) {
|
|
2233
|
-
const desc = Object.getOwnPropertyDescriptor(obj, key);
|
|
2234
|
-
return desc?.value;
|
|
2235
|
-
}
|
|
2236
|
-
function isPlainObject(value) {
|
|
2237
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2238
|
-
}
|
|
2239
2291
|
function isObjectWithMessages(value) {
|
|
2240
|
-
if (!
|
|
2292
|
+
if (!isPlainRecord(value)) return false;
|
|
2241
2293
|
if (!Object.prototype.hasOwnProperty.call(value, "messages")) return false;
|
|
2242
|
-
const messages =
|
|
2294
|
+
const messages = safeGetOwnProperty(value, "messages");
|
|
2243
2295
|
return Array.isArray(messages);
|
|
2244
2296
|
}
|
|
2245
2297
|
function collectAssistantToolCallIds(message, into) {
|
|
2246
|
-
|
|
2298
|
+
if (!isPlainRecord(message)) return;
|
|
2299
|
+
const toolCalls = safeGetOwnProperty(message, "tool_calls");
|
|
2247
2300
|
if (!Array.isArray(toolCalls)) return;
|
|
2248
2301
|
for (const tc of toolCalls) {
|
|
2249
|
-
if (!
|
|
2250
|
-
const id =
|
|
2302
|
+
if (!isPlainRecord(tc)) continue;
|
|
2303
|
+
const id = safeGetOwnProperty(tc, "id");
|
|
2251
2304
|
if (typeof id === "string" && id.length > 0) {
|
|
2252
2305
|
into.add(id);
|
|
2253
2306
|
}
|
|
@@ -2259,14 +2312,14 @@ function findOrphanToolMessageIndices(messages) {
|
|
|
2259
2312
|
const orphanIds = [];
|
|
2260
2313
|
for (let i = 0; i < messages.length; i++) {
|
|
2261
2314
|
const msg = messages[i];
|
|
2262
|
-
if (!
|
|
2263
|
-
const role =
|
|
2315
|
+
if (!isPlainRecord(msg)) continue;
|
|
2316
|
+
const role = safeGetOwnProperty(msg, "role");
|
|
2264
2317
|
if (role === ROLE_ASSISTANT) {
|
|
2265
2318
|
collectAssistantToolCallIds(msg, knownToolCallIds);
|
|
2266
2319
|
continue;
|
|
2267
2320
|
}
|
|
2268
2321
|
if (role === ROLE_TOOL) {
|
|
2269
|
-
const id =
|
|
2322
|
+
const id = safeGetOwnProperty(msg, "tool_call_id");
|
|
2270
2323
|
if (typeof id !== "string" || id.length === 0) {
|
|
2271
2324
|
indices.push(i);
|
|
2272
2325
|
orphanIds.push(null);
|
|
@@ -2532,8 +2585,6 @@ async function handleProxy(req) {
|
|
|
2532
2585
|
upstreamHeaders.forEach((value, key) => {
|
|
2533
2586
|
upstreamHeadersObj[key.toLowerCase()] = value;
|
|
2534
2587
|
});
|
|
2535
|
-
const bodyFormat = formatRegistry.detectFormat(requestBody);
|
|
2536
|
-
const displayApiFormat = bodyFormat !== "unknown" ? bodyFormat : formatHandler.format;
|
|
2537
2588
|
const log = await createLog(
|
|
2538
2589
|
req.method,
|
|
2539
2590
|
parsed.apiPath,
|
|
@@ -2542,7 +2593,7 @@ async function handleProxy(req) {
|
|
|
2542
2593
|
clientInfo,
|
|
2543
2594
|
rawHeaders,
|
|
2544
2595
|
upstreamHeadersObj,
|
|
2545
|
-
|
|
2596
|
+
formatHandler.format,
|
|
2546
2597
|
model,
|
|
2547
2598
|
sessionId,
|
|
2548
2599
|
preAcquiredId
|
|
@@ -3166,6 +3217,9 @@ const Route$e = createFileRoute("/api/mcp")({
|
|
|
3166
3217
|
}
|
|
3167
3218
|
}
|
|
3168
3219
|
});
|
|
3220
|
+
const DeleteBodySchema = object({
|
|
3221
|
+
ids: array(number().int().positive()).optional()
|
|
3222
|
+
}).optional();
|
|
3169
3223
|
const Route$d = createFileRoute("/api/logs")({
|
|
3170
3224
|
server: {
|
|
3171
3225
|
handlers: {
|
|
@@ -3184,7 +3238,28 @@ const Route$d = createFileRoute("/api/logs")({
|
|
|
3184
3238
|
limit
|
|
3185
3239
|
});
|
|
3186
3240
|
},
|
|
3187
|
-
DELETE: () => {
|
|
3241
|
+
DELETE: async ({ request }) => {
|
|
3242
|
+
let body = void 0;
|
|
3243
|
+
try {
|
|
3244
|
+
const raw = await request.text();
|
|
3245
|
+
if (raw !== "") {
|
|
3246
|
+
const parsed = JSON.parse(raw);
|
|
3247
|
+
const result2 = DeleteBodySchema.safeParse(parsed);
|
|
3248
|
+
if (!result2.success) {
|
|
3249
|
+
return Response.json(
|
|
3250
|
+
{ error: "Invalid request body", details: result2.error.format() },
|
|
3251
|
+
{ status: 400 }
|
|
3252
|
+
);
|
|
3253
|
+
}
|
|
3254
|
+
body = result2.data;
|
|
3255
|
+
}
|
|
3256
|
+
} catch {
|
|
3257
|
+
return Response.json({ error: "Invalid JSON body" }, { status: 400 });
|
|
3258
|
+
}
|
|
3259
|
+
if (body?.ids !== void 0 && body.ids.length > 0) {
|
|
3260
|
+
const result2 = clearLogsByIds(body.ids);
|
|
3261
|
+
return Response.json({ success: true, cleared: result2.cleared });
|
|
3262
|
+
}
|
|
3188
3263
|
const result = clearAllLogs();
|
|
3189
3264
|
return Response.json({ success: true, cleared: result.cleared });
|
|
3190
3265
|
}
|
|
@@ -3235,21 +3310,18 @@ const Route$b = createFileRoute("/api/config")({
|
|
|
3235
3310
|
}
|
|
3236
3311
|
}
|
|
3237
3312
|
});
|
|
3238
|
-
function isRecord(val) {
|
|
3239
|
-
return val !== null && typeof val === "object" && !Array.isArray(val);
|
|
3240
|
-
}
|
|
3241
3313
|
function readJsonSafe(filePath) {
|
|
3242
3314
|
try {
|
|
3243
3315
|
if (!existsSync(filePath)) return null;
|
|
3244
3316
|
const raw = readFileSync(filePath, "utf-8");
|
|
3245
3317
|
const parsed = JSON.parse(raw);
|
|
3246
|
-
return
|
|
3318
|
+
return isPlainRecord(parsed) ? parsed : null;
|
|
3247
3319
|
} catch {
|
|
3248
3320
|
return null;
|
|
3249
3321
|
}
|
|
3250
3322
|
}
|
|
3251
3323
|
function getEnvValue(envObj, key) {
|
|
3252
|
-
if (!
|
|
3324
|
+
if (!isPlainRecord(envObj)) return void 0;
|
|
3253
3325
|
const val = envObj[key];
|
|
3254
3326
|
return typeof val === "string" ? val.trim() : void 0;
|
|
3255
3327
|
}
|
|
@@ -3294,15 +3366,15 @@ function detectClaudeCodeProviders() {
|
|
|
3294
3366
|
const globalConfig = readJsonSafe(join(home, ".claude", "settings.json"));
|
|
3295
3367
|
const localConfig = readJsonSafe(join(home, ".claude", "settings.local.json"));
|
|
3296
3368
|
const mergedEnv = {};
|
|
3297
|
-
if (
|
|
3369
|
+
if (isPlainRecord(globalConfig)) {
|
|
3298
3370
|
const env = globalConfig["env"];
|
|
3299
|
-
if (
|
|
3371
|
+
if (isPlainRecord(env)) {
|
|
3300
3372
|
Object.assign(mergedEnv, env);
|
|
3301
3373
|
}
|
|
3302
3374
|
}
|
|
3303
|
-
if (
|
|
3375
|
+
if (isPlainRecord(localConfig)) {
|
|
3304
3376
|
const env = localConfig["env"];
|
|
3305
|
-
if (
|
|
3377
|
+
if (isPlainRecord(env)) {
|
|
3306
3378
|
Object.assign(mergedEnv, env);
|
|
3307
3379
|
}
|
|
3308
3380
|
}
|
|
@@ -3343,25 +3415,25 @@ function detectOpenCodeProviders() {
|
|
|
3343
3415
|
if (config === null) return results;
|
|
3344
3416
|
const auth = readJsonSafe(join(home, ".local", "share", "opencode", "auth.json"));
|
|
3345
3417
|
const providersVal = config["provider"];
|
|
3346
|
-
if (!
|
|
3418
|
+
if (!isPlainRecord(providersVal)) {
|
|
3347
3419
|
return results;
|
|
3348
3420
|
}
|
|
3349
3421
|
const providers = providersVal;
|
|
3350
3422
|
for (const [providerId, providerObj] of Object.entries(providers)) {
|
|
3351
|
-
if (!
|
|
3423
|
+
if (!isPlainRecord(providerObj)) continue;
|
|
3352
3424
|
const options = providerObj["options"];
|
|
3353
3425
|
const npm = providerObj["npm"];
|
|
3354
3426
|
const npmStr = typeof npm === "string" ? npm : "";
|
|
3355
3427
|
const format = npmStr === "@ai-sdk/anthropic" ? "anthropic" : "openai";
|
|
3356
3428
|
let baseURL = "";
|
|
3357
3429
|
let apiKey = "";
|
|
3358
|
-
if (
|
|
3430
|
+
if (isPlainRecord(options)) {
|
|
3359
3431
|
baseURL = typeof options["baseURL"] === "string" ? options["baseURL"].trim() : "";
|
|
3360
3432
|
apiKey = typeof options["apiKey"] === "string" ? options["apiKey"].trim() : "";
|
|
3361
3433
|
}
|
|
3362
|
-
if (apiKey === "" &&
|
|
3434
|
+
if (apiKey === "" && isPlainRecord(auth)) {
|
|
3363
3435
|
const cred = auth[providerId];
|
|
3364
|
-
if (
|
|
3436
|
+
if (isPlainRecord(cred)) {
|
|
3365
3437
|
const key = cred["key"];
|
|
3366
3438
|
if (typeof key === "string" && key !== "") apiKey = key;
|
|
3367
3439
|
}
|
|
@@ -3369,11 +3441,11 @@ function detectOpenCodeProviders() {
|
|
|
3369
3441
|
if (baseURL === "" || apiKey === "") continue;
|
|
3370
3442
|
const modelSet = /* @__PURE__ */ new Set();
|
|
3371
3443
|
const modelsObj = providerObj["models"];
|
|
3372
|
-
if (
|
|
3444
|
+
if (isPlainRecord(modelsObj)) {
|
|
3373
3445
|
for (const [modelId, modelDef] of Object.entries(modelsObj)) {
|
|
3374
3446
|
if (typeof modelDef === "string") {
|
|
3375
3447
|
modelSet.add(modelDef);
|
|
3376
|
-
} else if (
|
|
3448
|
+
} else if (isPlainRecord(modelDef)) {
|
|
3377
3449
|
modelSet.add(modelId);
|
|
3378
3450
|
} else {
|
|
3379
3451
|
modelSet.add(modelId);
|
|
@@ -3394,6 +3466,189 @@ function detectOpenCodeProviders() {
|
|
|
3394
3466
|
}
|
|
3395
3467
|
return results;
|
|
3396
3468
|
}
|
|
3469
|
+
function stripJsoncComments(raw) {
|
|
3470
|
+
let result = "";
|
|
3471
|
+
let i = 0;
|
|
3472
|
+
let inString = false;
|
|
3473
|
+
let stringChar = "";
|
|
3474
|
+
while (i < raw.length) {
|
|
3475
|
+
const ch = raw[i];
|
|
3476
|
+
const next = raw[i + 1];
|
|
3477
|
+
if (inString) {
|
|
3478
|
+
if (ch === "\\" && next !== void 0) {
|
|
3479
|
+
result += ch + next;
|
|
3480
|
+
i += 2;
|
|
3481
|
+
continue;
|
|
3482
|
+
}
|
|
3483
|
+
if (ch === stringChar) {
|
|
3484
|
+
inString = false;
|
|
3485
|
+
stringChar = "";
|
|
3486
|
+
}
|
|
3487
|
+
result += ch;
|
|
3488
|
+
i += 1;
|
|
3489
|
+
continue;
|
|
3490
|
+
}
|
|
3491
|
+
if (ch === '"' || ch === "'") {
|
|
3492
|
+
inString = true;
|
|
3493
|
+
stringChar = ch;
|
|
3494
|
+
result += ch;
|
|
3495
|
+
i += 1;
|
|
3496
|
+
continue;
|
|
3497
|
+
}
|
|
3498
|
+
if (ch === "/" && next === "/") {
|
|
3499
|
+
while (i < raw.length && raw[i] !== "\n") {
|
|
3500
|
+
i += 1;
|
|
3501
|
+
}
|
|
3502
|
+
continue;
|
|
3503
|
+
}
|
|
3504
|
+
if (ch === "/" && next === "*") {
|
|
3505
|
+
i += 2;
|
|
3506
|
+
while (i < raw.length - 1 && !(raw[i] === "*" && raw[i + 1] === "/")) {
|
|
3507
|
+
i += 1;
|
|
3508
|
+
}
|
|
3509
|
+
i += 2;
|
|
3510
|
+
continue;
|
|
3511
|
+
}
|
|
3512
|
+
result += ch;
|
|
3513
|
+
i += 1;
|
|
3514
|
+
}
|
|
3515
|
+
return result;
|
|
3516
|
+
}
|
|
3517
|
+
function readJsonCSafe(filePath) {
|
|
3518
|
+
try {
|
|
3519
|
+
if (!existsSync(filePath)) return null;
|
|
3520
|
+
const raw = readFileSync(filePath, "utf-8");
|
|
3521
|
+
const stripped = stripJsoncComments(raw);
|
|
3522
|
+
const parsed = JSON.parse(stripped);
|
|
3523
|
+
return isPlainRecord(parsed) ? parsed : null;
|
|
3524
|
+
} catch {
|
|
3525
|
+
return null;
|
|
3526
|
+
}
|
|
3527
|
+
}
|
|
3528
|
+
function detectMiMoCodeProviders() {
|
|
3529
|
+
const home = homedir();
|
|
3530
|
+
const results = [];
|
|
3531
|
+
const config = readJsonCSafe(join(home, ".config", "mimocode", "mimocode.jsonc")) ?? readJsonCSafe(join(home, ".config", "mimocode", "mimocode.json")) ?? readJsonCSafe(join(home, ".config", "mimocode", "config.jsonc")) ?? readJsonCSafe(join(home, ".config", "mimocode", "config.json")) ?? readJsonCSafe(join(home, ".mimocode", "config.jsonc")) ?? readJsonCSafe(join(home, ".mimocode", "config.json"));
|
|
3532
|
+
const auth = readJsonSafe(join(home, ".local", "share", "mimocode", "auth.json"));
|
|
3533
|
+
const providersVal = config?.["provider"];
|
|
3534
|
+
const configProviders = isPlainRecord(providersVal) ? providersVal : {};
|
|
3535
|
+
for (const [providerId, providerObj] of Object.entries(configProviders)) {
|
|
3536
|
+
if (!isPlainRecord(providerObj)) continue;
|
|
3537
|
+
const options = providerObj["options"];
|
|
3538
|
+
const npm = providerObj["npm"];
|
|
3539
|
+
const npmStr = typeof npm === "string" ? npm : "";
|
|
3540
|
+
const format = npmStr === "@ai-sdk/anthropic" ? "anthropic" : "openai";
|
|
3541
|
+
let baseURL = "";
|
|
3542
|
+
let apiKey = "";
|
|
3543
|
+
if (isPlainRecord(options)) {
|
|
3544
|
+
baseURL = typeof options["baseURL"] === "string" ? options["baseURL"].trim() : "";
|
|
3545
|
+
apiKey = typeof options["apiKey"] === "string" ? options["apiKey"].trim() : "";
|
|
3546
|
+
}
|
|
3547
|
+
if (apiKey === "" && isPlainRecord(auth)) {
|
|
3548
|
+
const cred = auth[providerId];
|
|
3549
|
+
if (isPlainRecord(cred)) {
|
|
3550
|
+
const key = cred["key"];
|
|
3551
|
+
if (typeof key === "string" && key !== "") apiKey = key;
|
|
3552
|
+
}
|
|
3553
|
+
}
|
|
3554
|
+
if (baseURL === "" || apiKey === "") continue;
|
|
3555
|
+
const modelSet = /* @__PURE__ */ new Set();
|
|
3556
|
+
const modelsObj = providerObj["models"];
|
|
3557
|
+
if (isPlainRecord(modelsObj)) {
|
|
3558
|
+
for (const [modelId, modelDef] of Object.entries(modelsObj)) {
|
|
3559
|
+
if (typeof modelDef === "string") {
|
|
3560
|
+
modelSet.add(modelDef);
|
|
3561
|
+
} else if (isPlainRecord(modelDef)) {
|
|
3562
|
+
modelSet.add(modelId);
|
|
3563
|
+
} else {
|
|
3564
|
+
modelSet.add(modelId);
|
|
3565
|
+
}
|
|
3566
|
+
}
|
|
3567
|
+
}
|
|
3568
|
+
const models = modelSet.size > 0 ? [...modelSet] : ["default"];
|
|
3569
|
+
results.push({
|
|
3570
|
+
name: providerId,
|
|
3571
|
+
apiKey: normalizeApiKey(apiKey),
|
|
3572
|
+
format,
|
|
3573
|
+
anthropicBaseUrl: format === "anthropic" ? baseURL : "",
|
|
3574
|
+
openaiBaseUrl: format === "openai" ? baseURL : "",
|
|
3575
|
+
models,
|
|
3576
|
+
sourceTool: "mimo-code",
|
|
3577
|
+
alreadyExists: false
|
|
3578
|
+
});
|
|
3579
|
+
}
|
|
3580
|
+
if (isPlainRecord(auth)) {
|
|
3581
|
+
const knownDefaults = {
|
|
3582
|
+
deepseek: {
|
|
3583
|
+
anthropicBaseUrl: "https://api.deepseek.com/anthropic",
|
|
3584
|
+
openaiBaseUrl: "https://api.deepseek.com",
|
|
3585
|
+
format: "anthropic"
|
|
3586
|
+
},
|
|
3587
|
+
minimax: {
|
|
3588
|
+
anthropicBaseUrl: "https://api.minimaxi.com/anthropic",
|
|
3589
|
+
openaiBaseUrl: "https://api.minimaxi.com",
|
|
3590
|
+
format: "anthropic"
|
|
3591
|
+
},
|
|
3592
|
+
"minimax-coding-plan": {
|
|
3593
|
+
anthropicBaseUrl: "https://api.minimaxi.com/anthropic",
|
|
3594
|
+
openaiBaseUrl: "https://api.minimaxi.com",
|
|
3595
|
+
format: "anthropic"
|
|
3596
|
+
},
|
|
3597
|
+
openai: {
|
|
3598
|
+
anthropicBaseUrl: "",
|
|
3599
|
+
openaiBaseUrl: "https://api.openai.com",
|
|
3600
|
+
format: "openai"
|
|
3601
|
+
},
|
|
3602
|
+
anthropic: {
|
|
3603
|
+
anthropicBaseUrl: "https://api.anthropic.com",
|
|
3604
|
+
openaiBaseUrl: "",
|
|
3605
|
+
format: "anthropic"
|
|
3606
|
+
},
|
|
3607
|
+
groq: {
|
|
3608
|
+
anthropicBaseUrl: "",
|
|
3609
|
+
openaiBaseUrl: "https://api.groq.com/openai",
|
|
3610
|
+
format: "openai"
|
|
3611
|
+
},
|
|
3612
|
+
openrouter: {
|
|
3613
|
+
anthropicBaseUrl: "",
|
|
3614
|
+
openaiBaseUrl: "https://openrouter.ai/api",
|
|
3615
|
+
format: "openai"
|
|
3616
|
+
},
|
|
3617
|
+
together: {
|
|
3618
|
+
anthropicBaseUrl: "",
|
|
3619
|
+
openaiBaseUrl: "https://api.together.xyz",
|
|
3620
|
+
format: "openai"
|
|
3621
|
+
}
|
|
3622
|
+
};
|
|
3623
|
+
for (const [key, cred] of Object.entries(auth)) {
|
|
3624
|
+
if (configProviders[key] !== void 0) continue;
|
|
3625
|
+
if (!isPlainRecord(cred)) continue;
|
|
3626
|
+
const apiKey = cred["key"];
|
|
3627
|
+
if (typeof apiKey !== "string" || apiKey === "") continue;
|
|
3628
|
+
let defaults = knownDefaults[key.toLowerCase()];
|
|
3629
|
+
if (defaults === void 0) {
|
|
3630
|
+
for (const [knownKey, cfg] of Object.entries(knownDefaults)) {
|
|
3631
|
+
if (key.toLowerCase().startsWith(knownKey)) {
|
|
3632
|
+
defaults = cfg;
|
|
3633
|
+
break;
|
|
3634
|
+
}
|
|
3635
|
+
}
|
|
3636
|
+
}
|
|
3637
|
+
if (defaults === void 0) continue;
|
|
3638
|
+
results.push({
|
|
3639
|
+
name: key,
|
|
3640
|
+
apiKey: normalizeApiKey(apiKey),
|
|
3641
|
+
format: defaults.format,
|
|
3642
|
+
anthropicBaseUrl: defaults.anthropicBaseUrl,
|
|
3643
|
+
openaiBaseUrl: defaults.openaiBaseUrl,
|
|
3644
|
+
models: ["default"],
|
|
3645
|
+
sourceTool: "mimo-code",
|
|
3646
|
+
alreadyExists: false
|
|
3647
|
+
});
|
|
3648
|
+
}
|
|
3649
|
+
}
|
|
3650
|
+
return results;
|
|
3651
|
+
}
|
|
3397
3652
|
function scanExternalProviders() {
|
|
3398
3653
|
const warnings = [];
|
|
3399
3654
|
let claudeProviders = [];
|
|
@@ -3408,7 +3663,13 @@ function scanExternalProviders() {
|
|
|
3408
3663
|
} catch (err) {
|
|
3409
3664
|
warnings.push(`OpenCode: ${err instanceof Error ? err.message : String(err)}`);
|
|
3410
3665
|
}
|
|
3411
|
-
|
|
3666
|
+
let mimoProviders = [];
|
|
3667
|
+
try {
|
|
3668
|
+
mimoProviders = detectMiMoCodeProviders();
|
|
3669
|
+
} catch (err) {
|
|
3670
|
+
warnings.push(`MiMo Code: ${err instanceof Error ? err.message : String(err)}`);
|
|
3671
|
+
}
|
|
3672
|
+
const allProviders = [...claudeProviders, ...opencodeProviders, ...mimoProviders];
|
|
3412
3673
|
const filteredProviders = allProviders.filter((p) => {
|
|
3413
3674
|
const url = p.anthropicBaseUrl || p.openaiBaseUrl;
|
|
3414
3675
|
return !/(?:localhost|127\.0\.0\.1)/.test(url);
|
|
@@ -4741,16 +5002,17 @@ const router = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProper
|
|
|
4741
5002
|
getRouter
|
|
4742
5003
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
4743
5004
|
export {
|
|
5005
|
+
AnthropicResponseSchema$1 as A,
|
|
4744
5006
|
CapturedLogSchema as C,
|
|
4745
|
-
InspectorResponseSchema as I,
|
|
4746
5007
|
OpenAIRequestSchema as O,
|
|
4747
5008
|
ProviderTestResultsSchema as P,
|
|
4748
5009
|
RuntimeConfigSchema as R,
|
|
4749
|
-
|
|
5010
|
+
AnthropicRequestSchema as a,
|
|
4750
5011
|
createFailedProviderTestResults as b,
|
|
4751
5012
|
createPendingProviderTestResults as c,
|
|
4752
5013
|
ProviderConfigSchema as d,
|
|
5014
|
+
router as e,
|
|
4753
5015
|
parseOpenAIResponse as p,
|
|
4754
|
-
|
|
5016
|
+
requestFormatForPath as r,
|
|
4755
5017
|
stripClaudeCodeBillingHeader as s
|
|
4756
5018
|
};
|