Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
a8cf0bf
chore: rename package to xscope0
Laurent-xscope Jul 1, 2026
2546ca0
fix: allow local backup password reauth
Laurent-xscope Jul 1, 2026
c99fa4a
feat: improve provider proxy rotation
Laurent-xscope Jul 1, 2026
c10baa3
feat: globalize proxy rotation controls
Laurent-xscope Jul 1, 2026
56f9983
refactor: compact proxy pools header buttons
Laurent-xscope Jul 1, 2026
b4f6eb7
feat: finish provider proxy controls
Laurent-xscope Jul 2, 2026
46bcd74
fix: keep provider controls visible
Laurent-xscope Jul 2, 2026
8364cee
feat: add usage view switch
Laurent-xscope Jul 2, 2026
1236fe6
fix: prevent duplicate tray process
Laurent-xscope Jul 2, 2026
0baf072
feat: inject web search token saver
Laurent-xscope Jul 2, 2026
d6d38f7
fix: prevent proxy pools controls stacking
Laurent-xscope Jul 2, 2026
9abf301
fix: clarify proxy relay controls
Laurent-xscope Jul 2, 2026
be0d943
fix: improve proxy import and locale controls
Laurent-xscope Jul 2, 2026
a32abaf
fix: guard headroom port startup
Laurent-xscope Jul 2, 2026
c78355c
fix: sync token saver style prompts
Laurent-xscope Jul 2, 2026
182614b
feat: add token saver injection toggle
Laurent-xscope Jul 3, 2026
bb6f3b5
feat: improve provider connection testing
Laurent-xscope Jul 3, 2026
3c01e85
feat: prepare router release
Laurent-xscope Jul 3, 2026
f34152a
fix: exclude cli build cache from package
Laurent-xscope Jul 3, 2026
e50f4e7
fix: honor proxy rotation and background mode
Laurent-xscope Jul 3, 2026
546edfc
fix: restore upstream CLI background flow
Laurent-xscope Jul 3, 2026
482e445
fix: port upstream usage and provider updates
Laurent-xscope Jul 4, 2026
5897249
fix: add antigravity bulk dashboard
Laurent-xscope Jul 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
app/.next
app/.next/**
app/.next-cli-build/cache
app/.next-cli-build/cache/**
app/.next-cli-build/trace
app/.next-cli-build/trace-build
app/cli/.build-home
app/cli/.build-home/**
app/node_modules
app/node_modules/**
*.tgz
.DS_Store
.serena
.serena/**
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,14 @@
## Quick Start

```bash
npm install -g xscope0-modifed-router
npm install -g xscope0
xscope0-router
```

Or run directly:

```bash
npx xscope0-modifed-router
npx xscope0
```

| Endpoint | URL |
Expand Down Expand Up @@ -129,6 +129,8 @@ npx xscope0-modifed-router
- Caveman mode (ultra-compressed prompts)
- Ponytail mode (minimal output)
- Visible request logs for Caveman / Ponytail in dashboard
- Adaptive style injection option: choose Old or Adaptive placement in Token Saver.
- Terse, Caveman, and Ponytail style prompts are mutually exclusive in the shared injection path.

**Account Pool**
- Bulk import API keys (appends, never replaces)
Expand All @@ -141,6 +143,7 @@ npx xscope0-modifed-router
- Custom branding and provider icons
- Pi provider endpoint: `http://localhost:20128/v1`
- Donate / Remote UI removed
- CLI interactive menu removed; launcher starts the local server and tray directly.

---

Expand All @@ -159,7 +162,7 @@ xScope0-Router/
│ ├── sqliteRuntime.js # SQLite native modules
│ └── trayRuntime.js # System tray binary
├── scripts/
│ └── build-cli.js # esbuild bundler
│ └── build-cli.mjs # Next.js CLI build packager
├── assets/
│ ├── logo.png
│ └── preview/
Expand Down
10 changes: 10 additions & 0 deletions app/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.next
.next/**
node_modules
node_modules/**
cli/.build-home
cli/.build-home/**
.next-cli-build/cache
.next-cli-build/cache/**
.next-cli-build/trace
.next-cli-build/trace-build
85 changes: 85 additions & 0 deletions app/custom-server.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,90 @@
const fs = require("fs");
const http = require("http");
const path = require("path");

const origCreate = http.createServer.bind(http);
const staticDirs = [
path.join(__dirname, ".next-cli-build", "static"),
path.join(__dirname, ".next", "static"),
];

const mimeTypes = {
".js": "application/javascript; charset=utf-8",
".mjs": "application/javascript; charset=utf-8",
".css": "text/css; charset=utf-8",
".json": "application/json; charset=utf-8",
".map": "application/json; charset=utf-8",
".txt": "text/plain; charset=utf-8",
".svg": "image/svg+xml",
".png": "image/png",
".jpg": "image/jpeg",
".jpeg": "image/jpeg",
".gif": "image/gif",
".webp": "image/webp",
".ico": "image/x-icon",
".woff": "font/woff",
".woff2": "font/woff2",
".ttf": "font/ttf",
};

function sendStatic(req, res) {
if (req.method !== "GET" && req.method !== "HEAD") return false;

let pathname;
try {
pathname = new URL(req.url, "http://localhost").pathname;
} catch {
return false;
}

const prefix = "/_next/static/";
if (!pathname.startsWith(prefix)) return false;

let rel;
try {
rel = decodeURIComponent(pathname.slice(prefix.length));
} catch {
res.statusCode = 400;
res.end("Bad Request");
return true;
}

if (!rel || rel.includes("\0") || path.isAbsolute(rel)) {
res.statusCode = 400;
res.end("Bad Request");
return true;
}

for (const dir of staticDirs) {
const file = path.resolve(dir, rel);
const root = path.resolve(dir);
if (file !== root && !file.startsWith(root + path.sep)) continue;
let stat;
try {
stat = fs.statSync(file);
} catch {
continue;
}
if (!stat.isFile()) continue;

res.statusCode = 200;
res.setHeader("Content-Type", mimeTypes[path.extname(file).toLowerCase()] || "application/octet-stream");
res.setHeader("Content-Length", String(stat.size));
res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
res.setHeader("Last-Modified", stat.mtime.toUTCString());
if (req.method === "HEAD") {
res.end();
return true;
}
fs.createReadStream(file).on("error", () => {
if (!res.headersSent) res.statusCode = 500;
res.end();
}).pipe(res);
return true;
}

return false;
}

// Wrap Next standalone HTTP server: derive client IP from the TCP socket
// (unspoofable) and strip client-supplied forwarding headers so downstream
Expand All @@ -24,6 +108,7 @@ http.createServer = (...args) => {
delete req.headers["x-9r-via-proxy"];
req.headers["x-9r-real-ip"] = ip;
if (viaProxy) req.headers["x-9r-via-proxy"] = "1";
if (sendStatic(req, res)) return;
return handler(req, res);
};
return origCreate(...rest, wrapped);
Expand Down
21 changes: 12 additions & 9 deletions app/open-sse/executors/github.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,13 @@ export class GithubExecutor extends BaseExecutor {
// The endpoint only accepts 'text' and 'image_url' content part types.
// Tool-related content (tool_use, tool_result, thinking) must be serialized as text.
sanitizeMessagesForChatCompletions(body) {
if (!body?.messages) return body;
if (!Array.isArray(body?.messages)) return body;

const sanitized = {
...body,
messages: body.messages.map((message) => ({ ...message })),
};

const sanitized = { ...body };

// Handle response_format for Claude models via GitHub
// GitHub's internal translation doesn't respect response_format, so we inject it as a system prompt
// AND prepend a reminder to the last user message for maximum effectiveness
Expand All @@ -60,23 +63,23 @@ export class GithubExecutor extends BaseExecutor {
}
if (systemInstruction) {
// Add to system message
const systemIdx = body.messages.findIndex(m => m.role === 'system');
const systemIdx = sanitized.messages.findIndex(m => m.role === 'system');
if (systemIdx >= 0) {
body.messages[systemIdx].content = systemInstruction + '\n\n' + body.messages[systemIdx].content;
sanitized.messages[systemIdx].content = systemInstruction + '\n\n' + sanitized.messages[systemIdx].content;
} else {
body.messages.unshift({ role: 'system', content: systemInstruction });
sanitized.messages.unshift({ role: 'system', content: systemInstruction });
}

// Also prepend to the last user message as a reminder
const lastUserIdx = body.messages.map((m, i) => m.role === 'user' ? i : -1).filter(i => i >= 0).pop();
const lastUserIdx = sanitized.messages.map((m, i) => m.role === 'user' ? i : -1).filter(i => i >= 0).pop();
if (lastUserIdx >= 0) {
const userMsg = body.messages[lastUserIdx];
const userMsg = sanitized.messages[lastUserIdx];
const userContent = typeof userMsg.content === 'string' ? userMsg.content : JSON.stringify(userMsg.content);
userMsg.content = 'Respond with ONLY raw JSON (no markdown, no backticks, no code blocks): ' + userContent;
}
}
}
sanitized.messages = body.messages.map(msg => {
sanitized.messages = sanitized.messages.map(msg => {
// assistant messages with only tool_calls have content: null — leave as-is
if (!msg.content) return msg;

Expand Down
60 changes: 44 additions & 16 deletions app/open-sse/handlers/chatCore.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { detectLoop } from "../utils/loopGuard.js";
import { injectCaveman } from "../rtk/caveman.js";
import { injectPonytail } from "../rtk/ponytail.js";
import { injectTerse } from "../rtk/terse.js";
import { applyWebSearchSaver } from "../rtk/webSearchSaver.js";
import { injectTerminationPrompt, injectToolProtocolPrompt } from "../rtk/terminationPrompt.js";
import { compressMessages, formatRtkLog } from "../rtk/index.js";
import { compressWithHeadroom, formatHeadroomLog, formatHeadroomSizeLog, isHeadroomPhantomSavings } from "../rtk/headroom.js";
Expand Down Expand Up @@ -92,14 +93,34 @@ export function applyLoopGuard(translatedBody, finalFormat, provider, model, log
return true;
}

export function applyStylePromptInjection(body, format, settings, log) {
const styleInjectionOptions = { method: settings.styleInjectionMethod };
if (settings.ponytailEnabled && settings.ponytailLevel) {
injectPonytail(body, format, settings.ponytailLevel, styleInjectionOptions);
log?.info?.("PONYTAIL", `active ${settings.ponytailLevel} | ${format}`);
return "ponytail";
}
if (settings.cavemanEnabled && settings.cavemanLevel) {
injectCaveman(body, format, settings.cavemanLevel, styleInjectionOptions);
log?.info?.("CAVEMAN", `active ${settings.cavemanLevel} | ${format}`);
return "caveman";
}
if (settings.terseEnabled && settings.terseLevel) {
injectTerse(body, format, settings.terseLevel, styleInjectionOptions);
log?.info?.("TERSE", `active ${settings.terseLevel} | ${format}`);
return "terse";
}
return null;
}

/**
* Core chat handler - shared between SSE and Worker
* @param {object} options.body - Request body
* @param {object} options.modelInfo - { provider, model }
* @param {object} options.credentials - Provider credentials
* @param {string} options.sourceFormatOverride - Override detected source format (e.g. "openai-responses")
*/
export async function handleChatCore({ body, modelInfo, credentials, log, onCredentialsRefreshed, onRequestSuccess, onDisconnect, clientRawRequest, connectionId, userAgent, apiKey, ccFilterNaming, rtkEnabled, headroomEnabled, headroomUrl, headroomCompressUserMessages, terseEnabled, terseLevel, cavemanEnabled, cavemanLevel, ponytailEnabled, ponytailLevel, sourceFormatOverride, providerThinking }) {
export async function handleChatCore({ body, modelInfo, credentials, log, onCredentialsRefreshed, onRequestSuccess, onDisconnect, clientRawRequest, connectionId, userAgent, apiKey, ccFilterNaming, rtkEnabled, headroomEnabled, headroomUrl, headroomCompressUserMessages, terseEnabled, terseLevel, cavemanEnabled, cavemanLevel, ponytailEnabled, ponytailLevel, styleInjectionMethod, webSearchSaverEnabled, sourceFormatOverride, providerThinking, externalSignal }) {
const { provider, model, accountCount = 0 } = modelInfo;
const requestStartTime = Date.now();

Expand Down Expand Up @@ -254,23 +275,21 @@ export async function handleChatCore({ body, modelInfo, credentials, log, onCred
}
} else if (headroomEnabled) log?.warn?.("HEADROOM", `skipped: ${headroomDiagnostics.reason || "compression unavailable"}${headroomDiagnostics.endpoint ? ` (${headroomDiagnostics.endpoint})` : ""}`);

// Terse: inject concise-output system prompt
if (terseEnabled && terseLevel) {
injectTerse(translatedBody, finalFormat, terseLevel);
log?.info?.("TERSE", `active ${terseLevel} | ${finalFormat}`);
// Web Search Saver: inject search discipline and compact web tool results in the real outbound body.
const webSearchSaverStats = applyWebSearchSaver(translatedBody, finalFormat, webSearchSaverEnabled !== false);
if (webSearchSaverStats) {
log?.info?.("WEBSEARCHSAVER", `active | ${finalFormat} | hits=${webSearchSaverStats.hits}`);
}

// Caveman: inject terse-style system prompt
if (cavemanEnabled && cavemanLevel) {
injectCaveman(translatedBody, finalFormat, cavemanLevel);
log?.info?.("CAVEMAN", `active ${cavemanLevel} | ${finalFormat}`);
}

// Ponytail: inject lazy-senior-dev system prompt
if (ponytailEnabled && ponytailLevel) {
injectPonytail(translatedBody, finalFormat, ponytailLevel);
log?.info?.("PONYTAIL", `active ${ponytailLevel} | ${finalFormat}`);
}
applyStylePromptInjection(translatedBody, finalFormat, {
ponytailEnabled,
ponytailLevel,
cavemanEnabled,
cavemanLevel,
terseEnabled,
terseLevel,
styleInjectionMethod,
}, log);

if (TOOL_PROTOCOL_PROMPT_PROVIDERS.has(provider)) {
injectToolProtocolPrompt(translatedBody, finalFormat, extractToolNames(translatedBody.tools));
Expand Down Expand Up @@ -323,9 +342,18 @@ export async function handleChatCore({ body, modelInfo, credentials, log, onCred
log, provider, model
});

if (externalSignal) {
if (externalSignal.aborted) {
streamController.abort();
} else {
externalSignal.addEventListener("abort", () => streamController.abort(), { once: true });
}
}

const proxyOptions = {
connectionProxyEnabled: credentials?.providerSpecificData?.connectionProxyEnabled === true,
connectionProxyUrl: credentials?.providerSpecificData?.connectionProxyUrl || "",
connectionProxyUrls: Array.isArray(credentials?.providerSpecificData?.connectionProxyUrls) ? credentials.providerSpecificData.connectionProxyUrls : [],
connectionNoProxy: credentials?.providerSpecificData?.connectionNoProxy || "",
vercelRelayUrl: credentials?.providerSpecificData?.vercelRelayUrl || "",
};
Expand Down
16 changes: 11 additions & 5 deletions app/open-sse/handlers/chatCore/requestDetail.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { saveRequestUsage, appendRequestLog, saveRequestDetail } from "@/lib/usageDb.js";
import { COLORS } from "../../utils/stream.js";
import { saveRequestUsage, appendRequestLog, saveRequestDetail } from "@/lib/usageDb.js";
import { COLORS } from "../../utils/stream.js";

const LOG_USAGE_EVENTS = process.env.USAGE_LOG_REQUESTS === "true";

const OPTIONAL_PARAMS = [
"temperature", "top_p", "top_k",
Expand Down Expand Up @@ -82,9 +84,13 @@ export function saveUsageStats({ provider, model, tokens, connectionId, apiKey,

if (inTokens === 0 && outTokens === 0) return;

const time = new Date().toLocaleTimeString("en-US", { hour12: false, hour: "2-digit", minute: "2-digit", second: "2-digit" });
const accountSuffix = connectionId ? ` | account=${connectionId.slice(0, 8)}...` : "";
console.log(`${COLORS.green}[${time}] 📊 [${label}] ${provider.toUpperCase()} | in=${inTokens} | out=${outTokens}${accountSuffix}${COLORS.reset}`);
if (LOG_USAGE_EVENTS) {
const d = new Date();
const pad = (n) => String(n).padStart(2, "0");
const time = `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
const accountSuffix = connectionId ? ` | account=${connectionId.slice(0, 8)}...` : "";
console.log(`${COLORS.green}[${time}] 📊 [${label}] ${provider.toUpperCase()} | in=${inTokens} | out=${outTokens}${accountSuffix}${COLORS.reset}`);
}

// Normalize to OpenAI token shape for storage
const normalized = {
Expand Down
5 changes: 5 additions & 0 deletions app/open-sse/providers/capabilities.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ export const MODEL_CAPABILITIES = {
"claude-opus-4-8-thinking": { vision: true, reasoning: true, search: true, thinkingFormat: "claude-adaptive", contextWindow: 1000000, maxOutput: 128000 },
"claude-sonnet-4.6": { vision: true, reasoning: true, search: true, thinkingFormat: "claude-adaptive", contextWindow: 1000000, maxOutput: 128000 },
"claude-sonnet-4-6": { vision: true, reasoning: true, search: true, thinkingFormat: "claude-adaptive", contextWindow: 1000000, maxOutput: 128000 },
"claude-sonnet-5": { vision: true, reasoning: true, search: true, thinkingFormat: "claude-adaptive", contextWindow: 1000000, maxOutput: 128000 },

// Gemini image-gen / OpenAI image / xai image variants
"gpt-image-1": { imageOutput: true, tools: false },
Expand Down Expand Up @@ -138,6 +139,9 @@ export const PROVIDER_CAPABILITIES = {
"deepseek-v4-flash": { vision: true, reasoning: true, thinkingFormat: "openai", thinkingCanDisable: false, contextWindow: 1000000, maxOutput: 50000 },
"deepseek-v3-2-volc": { reasoning: true, thinkingFormat: "openai", thinkingCanDisable: false, contextWindow: 96000, maxOutput: 32000 },
},
nvidia: {
"minimaxai/minimax-m3": { vision: true, reasoning: true, thinkingFormat: "openai", contextWindow: 512000, maxOutput: 131072 },
},
kimi: {
"kimi-k2.7": { vision: true, reasoning: true, thinkingFormat: "kimi", thinkingCanDisable: true, contextWindow: 262144, maxOutput: 262144 },
"kimi-k2.6": { vision: true, reasoning: true, thinkingFormat: "kimi", thinkingCanDisable: true, contextWindow: 262144, maxOutput: 262144 },
Expand All @@ -157,6 +161,7 @@ export const PATTERN_CAPABILITIES = [
{ pattern: "*claude*opus-4.8*", caps: { vision: true, reasoning: true, search: true, thinkingFormat: "claude-adaptive" } },
{ pattern: "*claude*sonnet-4.6*", caps: { vision: true, reasoning: true, search: true, thinkingFormat: "claude-adaptive" } },
{ pattern: "*claude*sonnet-4.7*", caps: { vision: true, reasoning: true, search: true, thinkingFormat: "claude-adaptive" } },
{ pattern: "*claude*sonnet-5*", caps: { vision: true, reasoning: true, search: true, thinkingFormat: "claude-adaptive", contextWindow: 1000000, maxOutput: 128000 } },
{ pattern: "*claude*haiku*", caps: { vision: true, reasoning: true, search: true, thinkingFormat: "claude-budget" } },
{ pattern: "*claude*opus*", caps: { vision: true, reasoning: true, search: true, thinkingFormat: "claude-budget" } },
{ pattern: "*claude*sonnet*", caps: { vision: true, reasoning: true, search: true, thinkingFormat: "claude-budget" } },
Expand Down
4 changes: 2 additions & 2 deletions app/open-sse/providers/pricing.js
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,8 @@ export function calculateCostFromTokens(tokens, pricing) {

const inputTokens = tokens.prompt_tokens || tokens.input_tokens || 0;
const cachedTokens = tokens.cached_tokens || tokens.cache_read_input_tokens || 0;
const nonCachedInput = Math.max(0, inputTokens - cachedTokens);
const cacheCreationTokens = tokens.cache_creation_input_tokens || 0;
const nonCachedInput = Math.max(0, inputTokens - cachedTokens - cacheCreationTokens);

cost += nonCachedInput * (pricing.input / 1000000);

Expand All @@ -295,7 +296,6 @@ export function calculateCostFromTokens(tokens, pricing) {
cost += reasoningTokens * ((pricing.reasoning || pricing.output) / 1000000);
}

const cacheCreationTokens = tokens.cache_creation_input_tokens || 0;
if (cacheCreationTokens > 0) {
cost += cacheCreationTokens * ((pricing.cache_creation || pricing.input) / 1000000);
}
Expand Down
Loading