routstrd 0.1.7 → 0.1.9
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/.claude/settings.local.json +4 -1
- package/bun.lock +2 -2
- package/dist/daemon/index.js +298 -302
- package/dist/index.js +161 -72
- package/package.json +3 -2
- package/src/cli.ts +113 -65
- package/src/daemon/http/index.ts +0 -236
- package/src/daemon/wallet/index.ts +1 -1
- package/src/integrations/claudecode.ts +77 -0
- package/src/integrations/index.ts +9 -2
- package/src/integrations/registry.ts +7 -0
package/dist/daemon/index.js
CHANGED
|
@@ -28662,7 +28662,7 @@ var require_file_uri_to_path = __commonJS((exports, module) => {
|
|
|
28662
28662
|
|
|
28663
28663
|
// ../routstr-chat/node_modules/bindings/bindings.js
|
|
28664
28664
|
var require_bindings = __commonJS((exports, module) => {
|
|
28665
|
-
var __filename = "/Users/r/projects/routstr_main/routstr-chat/node_modules/bindings/bindings.js";
|
|
28665
|
+
var __filename = "/Users/r/projects/routstr_main/screaming-bug/routstr-chat/node_modules/bindings/bindings.js";
|
|
28666
28666
|
var fs2 = __require("fs");
|
|
28667
28667
|
var path = __require("path");
|
|
28668
28668
|
var fileURLToPath = require_file_uri_to_path();
|
|
@@ -29413,6 +29413,9 @@ __export(exports_dist, {
|
|
|
29413
29413
|
BalanceManager: () => BalanceManager
|
|
29414
29414
|
});
|
|
29415
29415
|
import { Transform, Readable } from "stream";
|
|
29416
|
+
import * as fs2 from "fs";
|
|
29417
|
+
import * as path from "path";
|
|
29418
|
+
import * as os2 from "os";
|
|
29416
29419
|
function isNetworkErrorMessage(message) {
|
|
29417
29420
|
return message.includes("NetworkError when attempting to fetch resource") || message.includes("Failed to fetch") || message.includes("Load failed") || message.includes("ERR_TLS_CERT_ALTNAME_INVALID") || message.includes("ERR_TLS_CERT_NOT_YET_VALID") || message.includes("ERR_TLS_CERT_EXPIRED") || message.includes("UNABLE_TO_VERIFY_LEAF_SIGNATURE") || message.includes("SELF_SIGNED_CERT_IN_CHAIN");
|
|
29418
29421
|
}
|
|
@@ -29475,17 +29478,49 @@ function extractResponseId(body) {
|
|
|
29475
29478
|
return trimmed.length > 0 ? trimmed : undefined;
|
|
29476
29479
|
}
|
|
29477
29480
|
function extractUsageFromSSEJson(parsed, fallbackSatsCost = 0) {
|
|
29478
|
-
if (!parsed || typeof parsed !== "object"
|
|
29481
|
+
if (!parsed || typeof parsed !== "object") {
|
|
29482
|
+
return null;
|
|
29483
|
+
}
|
|
29484
|
+
if (!parsed.usage && parsed.cost && typeof parsed.cost === "object") {
|
|
29485
|
+
const costObj = parsed.cost;
|
|
29486
|
+
const msats2 = costObj.total_msats ?? 0;
|
|
29487
|
+
const cost2 = costObj.total_usd ?? 0;
|
|
29488
|
+
if (msats2 === 0 && cost2 === 0)
|
|
29489
|
+
return null;
|
|
29490
|
+
return {
|
|
29491
|
+
promptTokens: Number(costObj.input_tokens ?? 0),
|
|
29492
|
+
completionTokens: Number(costObj.output_tokens ?? 0),
|
|
29493
|
+
totalTokens: Number((costObj.input_tokens ?? 0) + (costObj.output_tokens ?? 0)),
|
|
29494
|
+
cost: Number(cost2),
|
|
29495
|
+
satsCost: msats2 > 0 ? msats2 / 1000 : fallbackSatsCost
|
|
29496
|
+
};
|
|
29497
|
+
}
|
|
29498
|
+
if (!parsed.usage) {
|
|
29479
29499
|
return null;
|
|
29480
29500
|
}
|
|
29481
29501
|
const usage = parsed.usage;
|
|
29482
29502
|
const usageCost = usage.cost;
|
|
29483
|
-
|
|
29484
|
-
|
|
29503
|
+
let cost = 0;
|
|
29504
|
+
let msats = 0;
|
|
29505
|
+
if (typeof usageCost === "number") {
|
|
29506
|
+
cost = usageCost;
|
|
29507
|
+
} else if (usageCost && typeof usageCost === "object") {
|
|
29508
|
+
cost = usageCost.total_usd ?? 0;
|
|
29509
|
+
msats = usageCost.total_msats ?? 0;
|
|
29510
|
+
}
|
|
29511
|
+
if (cost === 0) {
|
|
29512
|
+
cost = parsed.metadata?.routstr?.cost?.total_usd ?? 0;
|
|
29513
|
+
}
|
|
29514
|
+
if (msats === 0) {
|
|
29515
|
+
msats = parsed.metadata?.routstr?.cost?.total_msats ?? (typeof usage.cost_sats === "number" ? usage.cost_sats * 1000 : 0);
|
|
29516
|
+
}
|
|
29517
|
+
const promptTokens = Number(usage.prompt_tokens ?? usage.input_tokens ?? 0);
|
|
29518
|
+
const completionTokens = Number(usage.completion_tokens ?? usage.output_tokens ?? 0);
|
|
29519
|
+
const totalTokens = Number(usage.total_tokens ?? promptTokens + completionTokens);
|
|
29485
29520
|
const result = {
|
|
29486
|
-
promptTokens
|
|
29487
|
-
completionTokens
|
|
29488
|
-
totalTokens
|
|
29521
|
+
promptTokens,
|
|
29522
|
+
completionTokens,
|
|
29523
|
+
totalTokens,
|
|
29489
29524
|
cost: Number(cost ?? 0),
|
|
29490
29525
|
satsCost: msats > 0 ? msats / 1000 : fallbackSatsCost
|
|
29491
29526
|
};
|
|
@@ -29651,73 +29686,104 @@ async function createBunSqliteDriver(dbPath) {
|
|
|
29651
29686
|
}
|
|
29652
29687
|
function createSSEParserTransform(onUsage, onResponseId) {
|
|
29653
29688
|
let buffer = "";
|
|
29654
|
-
let
|
|
29689
|
+
let capturedUsage = null;
|
|
29655
29690
|
let responseIdCaptured = false;
|
|
29656
|
-
const
|
|
29691
|
+
const mergeUsage = (previous, next) => {
|
|
29692
|
+
if (!previous)
|
|
29693
|
+
return next;
|
|
29694
|
+
return {
|
|
29695
|
+
promptTokens: next.promptTokens > 0 ? next.promptTokens : previous.promptTokens,
|
|
29696
|
+
completionTokens: next.completionTokens > 0 ? next.completionTokens : previous.completionTokens,
|
|
29697
|
+
totalTokens: next.totalTokens > 0 ? next.totalTokens : previous.totalTokens,
|
|
29698
|
+
cost: next.cost > 0 ? next.cost : previous.cost,
|
|
29699
|
+
satsCost: next.satsCost > 0 ? next.satsCost : previous.satsCost
|
|
29700
|
+
};
|
|
29701
|
+
};
|
|
29702
|
+
const hasUsageChanged = (previous, next) => {
|
|
29703
|
+
if (!previous)
|
|
29704
|
+
return true;
|
|
29705
|
+
return previous.promptTokens !== next.promptTokens || previous.completionTokens !== next.completionTokens || previous.totalTokens !== next.totalTokens || previous.cost !== next.cost || previous.satsCost !== next.satsCost;
|
|
29706
|
+
};
|
|
29707
|
+
const inspectDataPayload = (jsonText) => {
|
|
29708
|
+
if (responseIdCaptured && capturedUsage?.satsCost && capturedUsage.totalTokens) {
|
|
29709
|
+
return;
|
|
29710
|
+
}
|
|
29711
|
+
const trimmed = jsonText.trim();
|
|
29712
|
+
if (!trimmed || trimmed === "[DONE]")
|
|
29713
|
+
return;
|
|
29714
|
+
if (!trimmed.startsWith("{") && !trimmed.startsWith("["))
|
|
29715
|
+
return;
|
|
29657
29716
|
try {
|
|
29658
|
-
const data = JSON.parse(
|
|
29659
|
-
|
|
29660
|
-
|
|
29661
|
-
|
|
29662
|
-
|
|
29717
|
+
const data = JSON.parse(trimmed);
|
|
29718
|
+
if (!responseIdCaptured) {
|
|
29719
|
+
const responseId = data?.id;
|
|
29720
|
+
if (typeof responseId === "string" && responseId.trim().length > 0) {
|
|
29721
|
+
onResponseId?.(responseId.trim());
|
|
29722
|
+
responseIdCaptured = true;
|
|
29723
|
+
}
|
|
29663
29724
|
}
|
|
29664
29725
|
const usage = extractUsageFromSSEJson(data);
|
|
29665
29726
|
if (usage) {
|
|
29666
|
-
|
|
29667
|
-
|
|
29727
|
+
const mergedUsage = mergeUsage(capturedUsage, usage);
|
|
29728
|
+
if (hasUsageChanged(capturedUsage, mergedUsage)) {
|
|
29729
|
+
capturedUsage = mergedUsage;
|
|
29730
|
+
onUsage(mergedUsage);
|
|
29731
|
+
}
|
|
29668
29732
|
}
|
|
29669
29733
|
} catch {}
|
|
29670
29734
|
};
|
|
29671
|
-
const
|
|
29672
|
-
|
|
29673
|
-
if (!trimmed) {
|
|
29735
|
+
const inspectEventBlock = (eventBlock) => {
|
|
29736
|
+
if (responseIdCaptured && capturedUsage?.satsCost && capturedUsage.totalTokens) {
|
|
29674
29737
|
return;
|
|
29675
29738
|
}
|
|
29676
|
-
|
|
29677
|
-
|
|
29678
|
-
|
|
29679
|
-
|
|
29680
|
-
|
|
29681
|
-
|
|
29682
|
-
|
|
29683
|
-
|
|
29684
|
-
if (dataStr === "[DONE]") {
|
|
29685
|
-
self.push(`data: [DONE]
|
|
29686
|
-
|
|
29687
|
-
`);
|
|
29688
|
-
return;
|
|
29739
|
+
const lines = eventBlock.split(/\r?\n/);
|
|
29740
|
+
const dataParts = [];
|
|
29741
|
+
for (const line of lines) {
|
|
29742
|
+
if (!line || line.startsWith(":"))
|
|
29743
|
+
continue;
|
|
29744
|
+
if (line.startsWith("data:")) {
|
|
29745
|
+
const value = line.startsWith("data: ") ? line.slice(6) : line.slice(5);
|
|
29746
|
+
dataParts.push(value);
|
|
29689
29747
|
}
|
|
29690
|
-
maybeCaptureUsageFromJson(dataStr);
|
|
29691
|
-
self.push(`data: ${dataStr}
|
|
29692
|
-
|
|
29693
|
-
`);
|
|
29694
|
-
return;
|
|
29695
29748
|
}
|
|
29696
|
-
if (
|
|
29697
|
-
|
|
29698
|
-
|
|
29699
|
-
|
|
29749
|
+
if (dataParts.length === 0)
|
|
29750
|
+
return;
|
|
29751
|
+
const payload = dataParts.join(`
|
|
29700
29752
|
`);
|
|
29753
|
+
inspectDataPayload(payload);
|
|
29754
|
+
};
|
|
29755
|
+
const emitEventBlock = (self, eventBlock) => {
|
|
29756
|
+
if (eventBlock.length === 0)
|
|
29701
29757
|
return;
|
|
29702
|
-
|
|
29703
|
-
self.push(
|
|
29758
|
+
inspectEventBlock(eventBlock);
|
|
29759
|
+
self.push(eventBlock + `
|
|
29760
|
+
|
|
29704
29761
|
`);
|
|
29705
29762
|
};
|
|
29706
29763
|
return new Transform({
|
|
29707
|
-
transform(chunk,
|
|
29764
|
+
transform(chunk, _encoding, callback) {
|
|
29708
29765
|
buffer += chunk.toString();
|
|
29709
|
-
const
|
|
29710
|
-
|
|
29711
|
-
|
|
29712
|
-
|
|
29766
|
+
const terminator = /\r?\n\r?\n/g;
|
|
29767
|
+
let lastIndex = 0;
|
|
29768
|
+
let match;
|
|
29769
|
+
while ((match = terminator.exec(buffer)) !== null) {
|
|
29770
|
+
const block = buffer.slice(lastIndex, match.index);
|
|
29771
|
+
lastIndex = match.index + match[0].length;
|
|
29772
|
+
emitEventBlock(this, block);
|
|
29773
|
+
}
|
|
29774
|
+
if (lastIndex > 0) {
|
|
29775
|
+
buffer = buffer.slice(lastIndex);
|
|
29713
29776
|
}
|
|
29714
29777
|
callback();
|
|
29715
29778
|
},
|
|
29716
29779
|
flush(callback) {
|
|
29717
|
-
if (buffer.
|
|
29718
|
-
|
|
29780
|
+
if (buffer.length > 0) {
|
|
29781
|
+
const tail = buffer.replace(/\r?\n+$/, "");
|
|
29782
|
+
if (tail.length > 0) {
|
|
29783
|
+
emitEventBlock(this, tail);
|
|
29784
|
+
}
|
|
29785
|
+
buffer = "";
|
|
29719
29786
|
}
|
|
29720
|
-
buffer = "";
|
|
29721
29787
|
callback();
|
|
29722
29788
|
}
|
|
29723
29789
|
});
|
|
@@ -29726,7 +29792,7 @@ async function resolveRouteRequestContext(options) {
|
|
|
29726
29792
|
const {
|
|
29727
29793
|
modelId,
|
|
29728
29794
|
requestBody,
|
|
29729
|
-
path = "/v1/chat/completions",
|
|
29795
|
+
path: path2 = "/v1/chat/completions",
|
|
29730
29796
|
headers = {},
|
|
29731
29797
|
forcedProvider,
|
|
29732
29798
|
walletAdapter,
|
|
@@ -29814,17 +29880,17 @@ async function resolveRouteRequestContext(options) {
|
|
|
29814
29880
|
client: client2,
|
|
29815
29881
|
baseUrl,
|
|
29816
29882
|
mintUrl,
|
|
29817
|
-
path,
|
|
29883
|
+
path: path2,
|
|
29818
29884
|
headers,
|
|
29819
29885
|
modelId,
|
|
29820
29886
|
proxiedBody
|
|
29821
29887
|
};
|
|
29822
29888
|
}
|
|
29823
29889
|
async function routeRequests(options) {
|
|
29824
|
-
const { client: client2, baseUrl, mintUrl, path, headers, modelId, proxiedBody } = await resolveRouteRequestContext(options);
|
|
29890
|
+
const { client: client2, baseUrl, mintUrl, path: path2, headers, modelId, proxiedBody } = await resolveRouteRequestContext(options);
|
|
29825
29891
|
try {
|
|
29826
29892
|
const response = await client2.routeRequest({
|
|
29827
|
-
path,
|
|
29893
|
+
path: path2,
|
|
29828
29894
|
method: "POST",
|
|
29829
29895
|
body: proxiedBody,
|
|
29830
29896
|
headers,
|
|
@@ -29845,10 +29911,10 @@ async function routeRequests(options) {
|
|
|
29845
29911
|
}
|
|
29846
29912
|
async function routeRequestsToNodeResponse(options) {
|
|
29847
29913
|
const { res } = options;
|
|
29848
|
-
const { client: client2, baseUrl, mintUrl, path, headers, modelId, proxiedBody } = await resolveRouteRequestContext(options);
|
|
29914
|
+
const { client: client2, baseUrl, mintUrl, path: path2, headers, modelId, proxiedBody } = await resolveRouteRequestContext(options);
|
|
29849
29915
|
try {
|
|
29850
29916
|
await client2.routeRequestToNodeResponse({
|
|
29851
|
-
path,
|
|
29917
|
+
path: path2,
|
|
29852
29918
|
method: "POST",
|
|
29853
29919
|
body: proxiedBody,
|
|
29854
29920
|
headers,
|
|
@@ -30362,10 +30428,10 @@ var import_rxjs24, InsufficientBalanceError, ProviderError, MintUnreachableError
|
|
|
30362
30428
|
`;
|
|
30363
30429
|
if (typeof window === "undefined") {
|
|
30364
30430
|
try {
|
|
30365
|
-
const
|
|
30366
|
-
const
|
|
30367
|
-
const logPath =
|
|
30368
|
-
|
|
30431
|
+
const fs22 = await import("fs");
|
|
30432
|
+
const path2 = await import("path");
|
|
30433
|
+
const logPath = path2.join(process.cwd(), "audit.log");
|
|
30434
|
+
fs22.appendFileSync(logPath, logLine);
|
|
30369
30435
|
} catch (error) {
|
|
30370
30436
|
console.error("[AuditLogger] Failed to write to file:", error);
|
|
30371
30437
|
}
|
|
@@ -31428,11 +31494,14 @@ var import_rxjs24, InsufficientBalanceError, ProviderError, MintUnreachableError
|
|
|
31428
31494
|
if (parsed.choices?.[0]?.delta?.reasoning) {
|
|
31429
31495
|
result.reasoning = parsed.choices[0].delta.reasoning;
|
|
31430
31496
|
}
|
|
31431
|
-
|
|
31432
|
-
|
|
31433
|
-
|
|
31434
|
-
|
|
31435
|
-
|
|
31497
|
+
const extractedUsage = extractUsageFromSSEJson(parsed);
|
|
31498
|
+
if (extractedUsage) {
|
|
31499
|
+
result.usage = toUsageStats(extractedUsage);
|
|
31500
|
+
} else if (parsed.usage) {
|
|
31501
|
+
result.usage = {
|
|
31502
|
+
total_tokens: parsed.usage.total_tokens ?? parsed.usage.input_tokens + parsed.usage.output_tokens,
|
|
31503
|
+
prompt_tokens: parsed.usage.prompt_tokens ?? parsed.usage.input_tokens,
|
|
31504
|
+
completion_tokens: parsed.usage.completion_tokens ?? parsed.usage.output_tokens
|
|
31436
31505
|
};
|
|
31437
31506
|
}
|
|
31438
31507
|
if (parsed.id) {
|
|
@@ -31626,6 +31695,10 @@ var import_rxjs24, InsufficientBalanceError, ProviderError, MintUnreachableError
|
|
|
31626
31695
|
const isExpired = age >= _ProviderManager.COOLDOWN_DURATION_MS;
|
|
31627
31696
|
if (isExpired) {
|
|
31628
31697
|
console.log(`[cleanupExpiredCooldowns:${this.instanceId}] Removing expired cooldown for ${url2} (age: ${age}ms, cooldown: ${_ProviderManager.COOLDOWN_DURATION_MS}ms)`);
|
|
31698
|
+
this.failedProviders.delete(url2);
|
|
31699
|
+
if (this.store) {
|
|
31700
|
+
this.store.getState().removeFailedProvider(url2);
|
|
31701
|
+
}
|
|
31629
31702
|
}
|
|
31630
31703
|
return !isExpired;
|
|
31631
31704
|
});
|
|
@@ -31725,23 +31798,39 @@ var import_rxjs24, InsufficientBalanceError, ProviderError, MintUnreachableError
|
|
|
31725
31798
|
try {
|
|
31726
31799
|
const torMode = isTorContext();
|
|
31727
31800
|
const disabledProviders = new Set(this.providerRegistry.getDisabledProviders());
|
|
31801
|
+
console.log(`[findNextBestProvider:${this.instanceId}] Starting search for model: ${modelId}`);
|
|
31802
|
+
console.log(`[findNextBestProvider:${this.instanceId}] disabledProviders: ${[...disabledProviders]}`);
|
|
31803
|
+
console.log(`[findNextBestProvider:${this.instanceId}] providersOnCooldown: ${this.providersOnCoolDown.map(([url2]) => url2)}`);
|
|
31728
31804
|
const allProviders = this.providerRegistry.getAllProvidersModels();
|
|
31805
|
+
console.log(`[findNextBestProvider:${this.instanceId}] Total providers in registry: ${Object.keys(allProviders).length}`);
|
|
31729
31806
|
const candidates = [];
|
|
31730
31807
|
for (const [baseUrl, models] of Object.entries(allProviders)) {
|
|
31731
|
-
if (baseUrl === currentBaseUrl
|
|
31808
|
+
if (baseUrl === currentBaseUrl) {
|
|
31809
|
+
console.log(`[findNextBestProvider:${this.instanceId}] SKIP (current): ${baseUrl}`);
|
|
31810
|
+
continue;
|
|
31811
|
+
}
|
|
31812
|
+
if (disabledProviders.has(baseUrl)) {
|
|
31813
|
+
continue;
|
|
31814
|
+
}
|
|
31815
|
+
if (this.isOnCooldown(baseUrl)) {
|
|
31732
31816
|
continue;
|
|
31733
31817
|
}
|
|
31734
31818
|
if (!torMode && (isOnionUrl(baseUrl) || isInsecureHttpUrl(baseUrl))) {
|
|
31735
31819
|
continue;
|
|
31736
31820
|
}
|
|
31737
31821
|
const model2 = models.find((m2) => m2.id === modelId);
|
|
31738
|
-
if (!model2)
|
|
31822
|
+
if (!model2) {
|
|
31739
31823
|
continue;
|
|
31824
|
+
}
|
|
31740
31825
|
const cost = model2.sats_pricing?.completion ?? 0;
|
|
31741
31826
|
candidates.push({ baseUrl, model: model2, cost });
|
|
31742
31827
|
}
|
|
31743
31828
|
candidates.sort((a, b) => a.cost - b.cost);
|
|
31744
|
-
|
|
31829
|
+
if (candidates.length > 0) {
|
|
31830
|
+
return candidates[0].baseUrl;
|
|
31831
|
+
} else {
|
|
31832
|
+
return null;
|
|
31833
|
+
}
|
|
31745
31834
|
} catch (error) {
|
|
31746
31835
|
console.error("Error finding next best provider:", error);
|
|
31747
31836
|
return null;
|
|
@@ -33417,7 +33506,7 @@ var import_rxjs24, InsufficientBalanceError, ProviderError, MintUnreachableError
|
|
|
33417
33506
|
}
|
|
33418
33507
|
async _prepareRoutedRequest(params) {
|
|
33419
33508
|
const {
|
|
33420
|
-
path,
|
|
33509
|
+
path: requestPath,
|
|
33421
33510
|
method,
|
|
33422
33511
|
body,
|
|
33423
33512
|
headers = {},
|
|
@@ -33452,7 +33541,7 @@ var import_rxjs24, InsufficientBalanceError, ProviderError, MintUnreachableError
|
|
|
33452
33541
|
const baseHeaders = this._buildBaseHeaders();
|
|
33453
33542
|
const requestHeaders = this._withAuthHeader(baseHeaders, token);
|
|
33454
33543
|
const response = await this._makeRequest({
|
|
33455
|
-
path,
|
|
33544
|
+
path: requestPath,
|
|
33456
33545
|
method,
|
|
33457
33546
|
body: method === "GET" ? undefined : requestBody,
|
|
33458
33547
|
baseUrl,
|
|
@@ -33471,7 +33560,21 @@ var import_rxjs24, InsufficientBalanceError, ProviderError, MintUnreachableError
|
|
|
33471
33560
|
let capturedUsage;
|
|
33472
33561
|
let capturedResponseId;
|
|
33473
33562
|
if (contentType.includes("text/event-stream") && response.body) {
|
|
33563
|
+
const logDir = path.join(os2.homedir(), ".routstrd", "stream-response");
|
|
33564
|
+
if (!fs2.existsSync(logDir)) {
|
|
33565
|
+
fs2.mkdirSync(logDir, { recursive: true });
|
|
33566
|
+
}
|
|
33567
|
+
const logFile = path.join(logDir, `${Date.now()}.jsonl`);
|
|
33568
|
+
const logStream = fs2.createWriteStream(logFile);
|
|
33474
33569
|
const nodeReadable = Readable.fromWeb(response.body);
|
|
33570
|
+
const loggingTransform = new Transform({
|
|
33571
|
+
transform(chunk, encoding, callback) {
|
|
33572
|
+
const raw = chunk.toString();
|
|
33573
|
+
logStream.write(JSON.stringify({ raw, timestamp: Date.now() }) + `
|
|
33574
|
+
`);
|
|
33575
|
+
callback(null, chunk);
|
|
33576
|
+
}
|
|
33577
|
+
});
|
|
33475
33578
|
const sseParser = createSSEParserTransform((usage) => {
|
|
33476
33579
|
capturedUsage = usage;
|
|
33477
33580
|
processedResponse.usage = usage;
|
|
@@ -33479,7 +33582,7 @@ var import_rxjs24, InsufficientBalanceError, ProviderError, MintUnreachableError
|
|
|
33479
33582
|
capturedResponseId = responseId;
|
|
33480
33583
|
processedResponse.requestId = responseId;
|
|
33481
33584
|
});
|
|
33482
|
-
const transformed = nodeReadable.pipe(sseParser, { end: true });
|
|
33585
|
+
const transformed = nodeReadable.pipe(loggingTransform).pipe(sseParser, { end: true });
|
|
33483
33586
|
const webStream = Readable.toWeb(transformed);
|
|
33484
33587
|
processedResponse = new Response(webStream, {
|
|
33485
33588
|
status: response.status,
|
|
@@ -33536,7 +33639,6 @@ var import_rxjs24, InsufficientBalanceError, ProviderError, MintUnreachableError
|
|
|
33536
33639
|
callbacks.onTokenCreated?.(this._getPendingCashuTokenAmount());
|
|
33537
33640
|
const baseHeaders = this._buildBaseHeaders(headers);
|
|
33538
33641
|
const requestHeaders = this._withAuthHeader(baseHeaders, token);
|
|
33539
|
-
this.providerManager.resetFailedProviders();
|
|
33540
33642
|
const providerInfo = await this.providerRegistry.getProviderInfo(baseUrl);
|
|
33541
33643
|
const providerVersion = providerInfo?.version ?? "";
|
|
33542
33644
|
let modelIdForRequest = selectedModel.id;
|
|
@@ -33624,9 +33726,9 @@ var import_rxjs24, InsufficientBalanceError, ProviderError, MintUnreachableError
|
|
|
33624
33726
|
}
|
|
33625
33727
|
}
|
|
33626
33728
|
async _makeRequest(params) {
|
|
33627
|
-
const { path, method, body, baseUrl, token, headers } = params;
|
|
33729
|
+
const { path: path2, method, body, baseUrl, token, headers } = params;
|
|
33628
33730
|
try {
|
|
33629
|
-
const url2 = `${baseUrl.replace(/\/$/, "")}${
|
|
33731
|
+
const url2 = `${baseUrl.replace(/\/$/, "")}${path2}`;
|
|
33630
33732
|
if (this.mode === "xcashu")
|
|
33631
33733
|
this._log("DEBUG", "HEADERS,", headers);
|
|
33632
33734
|
const response = await fetch(url2, {
|
|
@@ -33658,9 +33760,10 @@ var import_rxjs24, InsufficientBalanceError, ProviderError, MintUnreachableError
|
|
|
33658
33760
|
}
|
|
33659
33761
|
async _handleErrorResponse(params, token, status, requestId, xCashuRefundToken, responseBody, retryCount = 0) {
|
|
33660
33762
|
const MAX_RETRIES_PER_PROVIDER = 2;
|
|
33661
|
-
const { path, method, body, selectedModel, baseUrl, mintUrl } = params;
|
|
33763
|
+
const { path: path2, method, body, selectedModel, baseUrl, mintUrl } = params;
|
|
33662
33764
|
let tryNextProvider = false;
|
|
33663
|
-
|
|
33765
|
+
const errorMessage = responseBody;
|
|
33766
|
+
this._log("DEBUG", `[RoutstrClient] _handleErrorResponse: status=${status}, baseUrl=${baseUrl}, mode=${this.mode}, token preview=${token}, requestId=${requestId}, errorMessage=${errorMessage}`);
|
|
33664
33767
|
this._log("DEBUG", `[RoutstrClient] _handleErrorResponse: Attempting to receive/restore token for ${baseUrl}`);
|
|
33665
33768
|
if (params.token.startsWith("cashu")) {
|
|
33666
33769
|
const receiveResult = await this.cashuSpender.receiveToken(params.token);
|
|
@@ -33823,7 +33926,7 @@ var import_rxjs24, InsufficientBalanceError, ProviderError, MintUnreachableError
|
|
|
33823
33926
|
});
|
|
33824
33927
|
return this._makeRequest({
|
|
33825
33928
|
...params,
|
|
33826
|
-
path,
|
|
33929
|
+
path: path2,
|
|
33827
33930
|
method,
|
|
33828
33931
|
body,
|
|
33829
33932
|
baseUrl: nextProvider,
|
|
@@ -34297,7 +34400,7 @@ var init_dist3 = __esm(() => {
|
|
|
34297
34400
|
// src/daemon/index.ts
|
|
34298
34401
|
init_dist3();
|
|
34299
34402
|
import { createServer } from "http";
|
|
34300
|
-
import { existsSync as
|
|
34403
|
+
import { existsSync as existsSync8 } from "fs";
|
|
34301
34404
|
|
|
34302
34405
|
// src/utils/config.ts
|
|
34303
34406
|
var HOME = process.env.HOME || process.env.USERPROFILE || "";
|
|
@@ -34316,19 +34419,19 @@ var DEFAULT_CONFIG = {
|
|
|
34316
34419
|
|
|
34317
34420
|
// src/utils/logger.ts
|
|
34318
34421
|
import { appendFile, mkdir } from "fs/promises";
|
|
34319
|
-
import { existsSync } from "fs";
|
|
34320
|
-
import { join as
|
|
34422
|
+
import { existsSync as existsSync2 } from "fs";
|
|
34423
|
+
import { join as join4 } from "path";
|
|
34321
34424
|
var HOME2 = process.env.HOME || process.env.USERPROFILE || "";
|
|
34322
34425
|
var LOG_DIR = process.env.ROUTSTRD_DIR || `${HOME2}/.routstrd`;
|
|
34323
|
-
var LOGS_DIR2 =
|
|
34426
|
+
var LOGS_DIR2 = join4(LOG_DIR, "logs");
|
|
34324
34427
|
function getLogFileForDate(date = new Date) {
|
|
34325
34428
|
const year = date.getFullYear();
|
|
34326
34429
|
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
34327
34430
|
const day = String(date.getDate()).padStart(2, "0");
|
|
34328
|
-
return
|
|
34431
|
+
return join4(LOGS_DIR2, `${year}-${month}-${day}.log`);
|
|
34329
34432
|
}
|
|
34330
34433
|
async function ensureLogDir() {
|
|
34331
|
-
if (!
|
|
34434
|
+
if (!existsSync2(LOGS_DIR2)) {
|
|
34332
34435
|
await mkdir(LOGS_DIR2, { recursive: true });
|
|
34333
34436
|
}
|
|
34334
34437
|
}
|
|
@@ -34388,7 +34491,7 @@ function parseArgs(argv) {
|
|
|
34388
34491
|
|
|
34389
34492
|
// src/daemon/config-store.ts
|
|
34390
34493
|
import { mkdir as mkdir2 } from "fs/promises";
|
|
34391
|
-
import { existsSync as
|
|
34494
|
+
import { existsSync as existsSync3 } from "fs";
|
|
34392
34495
|
var REQUESTS_DIR = `${CONFIG_DIR}/requests`;
|
|
34393
34496
|
async function ensureDirs() {
|
|
34394
34497
|
try {
|
|
@@ -34398,7 +34501,7 @@ async function ensureDirs() {
|
|
|
34398
34501
|
}
|
|
34399
34502
|
async function loadDaemonConfig() {
|
|
34400
34503
|
try {
|
|
34401
|
-
if (
|
|
34504
|
+
if (existsSync3(CONFIG_FILE)) {
|
|
34402
34505
|
const content2 = await Bun.file(CONFIG_FILE).text();
|
|
34403
34506
|
return { ...DEFAULT_CONFIG, ...JSON.parse(content2) };
|
|
34404
34507
|
}
|
|
@@ -34710,7 +34813,7 @@ function alphabet3(letters) {
|
|
|
34710
34813
|
}
|
|
34711
34814
|
};
|
|
34712
34815
|
}
|
|
34713
|
-
function
|
|
34816
|
+
function join5(separator = "") {
|
|
34714
34817
|
astr2("join", separator);
|
|
34715
34818
|
return {
|
|
34716
34819
|
encode: (from7) => {
|
|
@@ -34913,12 +35016,12 @@ function checksum2(len, fn) {
|
|
|
34913
35016
|
}
|
|
34914
35017
|
};
|
|
34915
35018
|
}
|
|
34916
|
-
var base163 = chain3(radix23(4), alphabet3("0123456789ABCDEF"),
|
|
34917
|
-
var base323 = chain3(radix23(5), alphabet3("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"), padding3(5),
|
|
34918
|
-
var base32nopad2 = chain3(radix23(5), alphabet3("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"),
|
|
34919
|
-
var base32hex3 = chain3(radix23(5), alphabet3("0123456789ABCDEFGHIJKLMNOPQRSTUV"), padding3(5),
|
|
34920
|
-
var base32hexnopad2 = chain3(radix23(5), alphabet3("0123456789ABCDEFGHIJKLMNOPQRSTUV"),
|
|
34921
|
-
var base32crockford3 = chain3(radix23(5), alphabet3("0123456789ABCDEFGHJKMNPQRSTVWXYZ"),
|
|
35019
|
+
var base163 = chain3(radix23(4), alphabet3("0123456789ABCDEF"), join5(""));
|
|
35020
|
+
var base323 = chain3(radix23(5), alphabet3("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"), padding3(5), join5(""));
|
|
35021
|
+
var base32nopad2 = chain3(radix23(5), alphabet3("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"), join5(""));
|
|
35022
|
+
var base32hex3 = chain3(radix23(5), alphabet3("0123456789ABCDEFGHIJKLMNOPQRSTUV"), padding3(5), join5(""));
|
|
35023
|
+
var base32hexnopad2 = chain3(radix23(5), alphabet3("0123456789ABCDEFGHIJKLMNOPQRSTUV"), join5(""));
|
|
35024
|
+
var base32crockford3 = chain3(radix23(5), alphabet3("0123456789ABCDEFGHJKMNPQRSTVWXYZ"), join5(""), normalize3((s) => s.toUpperCase().replace(/O/g, "0").replace(/[IL]/g, "1")));
|
|
34922
35025
|
var hasBase64Builtin2 = /* @__PURE__ */ (() => typeof Uint8Array.from([]).toBase64 === "function" && typeof Uint8Array.fromBase64 === "function")();
|
|
34923
35026
|
var decodeBase64Builtin2 = (s, isUrl) => {
|
|
34924
35027
|
astr2("base64", s);
|
|
@@ -34936,8 +35039,8 @@ var base643 = hasBase64Builtin2 ? {
|
|
|
34936
35039
|
decode(s) {
|
|
34937
35040
|
return decodeBase64Builtin2(s, false);
|
|
34938
35041
|
}
|
|
34939
|
-
} : chain3(radix23(6), alphabet3("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"), padding3(6),
|
|
34940
|
-
var base64nopad2 = chain3(radix23(6), alphabet3("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"),
|
|
35042
|
+
} : chain3(radix23(6), alphabet3("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"), padding3(6), join5(""));
|
|
35043
|
+
var base64nopad2 = chain3(radix23(6), alphabet3("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"), join5(""));
|
|
34941
35044
|
var base64url3 = hasBase64Builtin2 ? {
|
|
34942
35045
|
encode(b) {
|
|
34943
35046
|
abytes4(b);
|
|
@@ -34946,14 +35049,14 @@ var base64url3 = hasBase64Builtin2 ? {
|
|
|
34946
35049
|
decode(s) {
|
|
34947
35050
|
return decodeBase64Builtin2(s, true);
|
|
34948
35051
|
}
|
|
34949
|
-
} : chain3(radix23(6), alphabet3("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"), padding3(6),
|
|
34950
|
-
var base64urlnopad2 = chain3(radix23(6), alphabet3("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"),
|
|
34951
|
-
var genBase583 = (abc) => chain3(radix5(58), alphabet3(abc),
|
|
35052
|
+
} : chain3(radix23(6), alphabet3("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"), padding3(6), join5(""));
|
|
35053
|
+
var base64urlnopad2 = chain3(radix23(6), alphabet3("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"), join5(""));
|
|
35054
|
+
var genBase583 = (abc) => chain3(radix5(58), alphabet3(abc), join5(""));
|
|
34952
35055
|
var base583 = genBase583("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz");
|
|
34953
35056
|
var base58flickr3 = genBase583("123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ");
|
|
34954
35057
|
var base58xrp3 = genBase583("rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz");
|
|
34955
35058
|
var createBase58check2 = (sha2565) => chain3(checksum2(4, (data) => sha2565(sha2565(data))), base583);
|
|
34956
|
-
var BECH_ALPHABET3 = chain3(alphabet3("qpzry9x8gf2tvdw0s3jn54khce6mua7l"),
|
|
35059
|
+
var BECH_ALPHABET3 = chain3(alphabet3("qpzry9x8gf2tvdw0s3jn54khce6mua7l"), join5(""));
|
|
34957
35060
|
var POLYMOD_GENERATORS3 = [996825010, 642813549, 513874426, 1027748829, 705979059];
|
|
34958
35061
|
function bech32Polymod3(pre) {
|
|
34959
35062
|
const b = pre >> 25;
|
|
@@ -35057,7 +35160,7 @@ var hexBuiltin2 = {
|
|
|
35057
35160
|
return Uint8Array.fromHex(s);
|
|
35058
35161
|
}
|
|
35059
35162
|
};
|
|
35060
|
-
var hex3 = hasHexBuiltin3 ? hexBuiltin2 : chain3(radix23(4), alphabet3("0123456789abcdef"),
|
|
35163
|
+
var hex3 = hasHexBuiltin3 ? hexBuiltin2 : chain3(radix23(4), alphabet3("0123456789abcdef"), join5(""), normalize3((s) => {
|
|
35061
35164
|
if (typeof s !== "string" || s.length % 2 !== 0)
|
|
35062
35165
|
throw new TypeError(`hex.decode: expected string, got ${typeof s} with length ${s.length}`);
|
|
35063
35166
|
return s.toLowerCase();
|
|
@@ -37913,14 +38016,14 @@ class HDKey2 {
|
|
|
37913
38016
|
}
|
|
37914
38017
|
this.pubHash = hash1602(this._publicKey);
|
|
37915
38018
|
}
|
|
37916
|
-
derive(
|
|
37917
|
-
if (!/^[mM]'?/.test(
|
|
38019
|
+
derive(path2) {
|
|
38020
|
+
if (!/^[mM]'?/.test(path2)) {
|
|
37918
38021
|
throw new Error('Path must start with "m" or "M"');
|
|
37919
38022
|
}
|
|
37920
|
-
if (/^[mM]'?$/.test(
|
|
38023
|
+
if (/^[mM]'?$/.test(path2)) {
|
|
37921
38024
|
return this;
|
|
37922
38025
|
}
|
|
37923
|
-
const parts =
|
|
38026
|
+
const parts = path2.replace(/^[mM]'?\//, "").split("/");
|
|
37924
38027
|
let child = this;
|
|
37925
38028
|
for (const c of parts) {
|
|
37926
38029
|
const m2 = /^(\d+)('?)$/.exec(c);
|
|
@@ -38279,7 +38382,7 @@ function bt(s, t) {
|
|
|
38279
38382
|
case 2:
|
|
38280
38383
|
return is(s, t, r);
|
|
38281
38384
|
case 3:
|
|
38282
|
-
return
|
|
38385
|
+
return os3(s, t, r);
|
|
38283
38386
|
case 4:
|
|
38284
38387
|
return as2(s, t, r);
|
|
38285
38388
|
case 5:
|
|
@@ -38330,7 +38433,7 @@ function is(s, t, e) {
|
|
|
38330
38433
|
throw new Error("Byte string length exceeds data length");
|
|
38331
38434
|
return { value: new Uint8Array(s.buffer, s.byteOffset + r, n), offset: r + n };
|
|
38332
38435
|
}
|
|
38333
|
-
function
|
|
38436
|
+
function os3(s, t, e) {
|
|
38334
38437
|
const { value: n, offset: r } = ot2(s, t, e);
|
|
38335
38438
|
if (r + n > s.byteLength)
|
|
38336
38439
|
throw new Error("String length exceeds data length");
|
|
@@ -40100,25 +40203,25 @@ function createCocodClient(options = {}) {
|
|
|
40100
40203
|
return proc;
|
|
40101
40204
|
});
|
|
40102
40205
|
let startPromise = null;
|
|
40103
|
-
async function fetchJson(
|
|
40206
|
+
async function fetchJson(path2, init = {}) {
|
|
40104
40207
|
const method = init.method || "GET";
|
|
40105
40208
|
const requestInit = {
|
|
40106
40209
|
...init,
|
|
40107
40210
|
unix: socketPath
|
|
40108
40211
|
};
|
|
40109
|
-
const response = await fetchImpl(`http://localhost${
|
|
40212
|
+
const response = await fetchImpl(`http://localhost${path2}`, requestInit);
|
|
40110
40213
|
const rawText = await response.text();
|
|
40111
40214
|
if (!rawText.trim()) {
|
|
40112
|
-
throw new CocodHttpError(response.ok ? 502 : response.status, `Empty response from cocod for ${method} ${
|
|
40215
|
+
throw new CocodHttpError(response.ok ? 502 : response.status, `Empty response from cocod for ${method} ${path2}`);
|
|
40113
40216
|
}
|
|
40114
40217
|
let data;
|
|
40115
40218
|
try {
|
|
40116
40219
|
data = JSON.parse(rawText);
|
|
40117
40220
|
} catch {
|
|
40118
|
-
throw new CocodHttpError(response.ok ? 502 : response.status, `Invalid JSON response from cocod for ${method} ${
|
|
40221
|
+
throw new CocodHttpError(response.ok ? 502 : response.status, `Invalid JSON response from cocod for ${method} ${path2}`);
|
|
40119
40222
|
}
|
|
40120
40223
|
if (!data || typeof data !== "object") {
|
|
40121
|
-
throw new CocodHttpError(response.ok ? 502 : response.status, `Unexpected response shape from cocod for ${method} ${
|
|
40224
|
+
throw new CocodHttpError(response.ok ? 502 : response.status, `Unexpected response shape from cocod for ${method} ${path2}`);
|
|
40122
40225
|
}
|
|
40123
40226
|
const errorMessage = toErrorText(data.error);
|
|
40124
40227
|
if (errorMessage) {
|
|
@@ -40169,13 +40272,13 @@ function createCocodClient(options = {}) {
|
|
|
40169
40272
|
}
|
|
40170
40273
|
await startPromise;
|
|
40171
40274
|
}
|
|
40172
|
-
async function callDaemon(
|
|
40275
|
+
async function callDaemon(path2, init = {}) {
|
|
40173
40276
|
await ensureDaemonRunning();
|
|
40174
|
-
const response = await fetchJson(
|
|
40277
|
+
const response = await fetchJson(path2, init);
|
|
40175
40278
|
return response.output;
|
|
40176
40279
|
}
|
|
40177
|
-
function post(
|
|
40178
|
-
return callDaemon(
|
|
40280
|
+
function post(path2, body) {
|
|
40281
|
+
return callDaemon(path2, {
|
|
40179
40282
|
method: "POST",
|
|
40180
40283
|
headers: { "Content-Type": "application/json" },
|
|
40181
40284
|
body: JSON.stringify(body)
|
|
@@ -40293,7 +40396,7 @@ async function createWalletAdapter(options = {}) {
|
|
|
40293
40396
|
return { success: true, amount, unit, message };
|
|
40294
40397
|
} catch (error) {
|
|
40295
40398
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
40296
|
-
logger3.error("Error in walletAdapter receiveToken:",
|
|
40399
|
+
logger3.error("Error in walletAdapter receiveToken:", errorMessage);
|
|
40297
40400
|
return { success: false, amount: 0, unit: "sat", message: errorMessage };
|
|
40298
40401
|
}
|
|
40299
40402
|
}
|
|
@@ -40972,173 +41075,6 @@ function createDaemonRequestHandler(deps) {
|
|
|
40972
41075
|
}
|
|
40973
41076
|
return;
|
|
40974
41077
|
}
|
|
40975
|
-
if (req.method === "POST" && url2.pathname === "/providers/disable") {
|
|
40976
|
-
try {
|
|
40977
|
-
const bodyText = await readBody(req);
|
|
40978
|
-
const body = bodyText ? JSON.parse(bodyText) : {};
|
|
40979
|
-
const indices = body.indices;
|
|
40980
|
-
if (!Array.isArray(indices)) {
|
|
40981
|
-
res.writeHead(400, { "Content-Type": "application/json" });
|
|
40982
|
-
res.end(JSON.stringify({
|
|
40983
|
-
error: "Missing or invalid 'indices' field (expected number[])."
|
|
40984
|
-
}));
|
|
40985
|
-
return;
|
|
40986
|
-
}
|
|
40987
|
-
const state = deps.store.getState();
|
|
40988
|
-
const baseUrlsList = state.baseUrlsList || [];
|
|
40989
|
-
const disabledProviders = [
|
|
40990
|
-
...state.disabledProviders || []
|
|
40991
|
-
];
|
|
40992
|
-
const toDisable = [];
|
|
40993
|
-
for (const idx of indices) {
|
|
40994
|
-
if (typeof idx === "number" && idx >= 0 && idx < baseUrlsList.length) {
|
|
40995
|
-
const baseUrl = baseUrlsList[idx];
|
|
40996
|
-
if (!disabledProviders.includes(baseUrl)) {
|
|
40997
|
-
disabledProviders.push(baseUrl);
|
|
40998
|
-
toDisable.push(baseUrl);
|
|
40999
|
-
}
|
|
41000
|
-
}
|
|
41001
|
-
}
|
|
41002
|
-
deps.store.getState().setDisabledProviders(disabledProviders);
|
|
41003
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
41004
|
-
res.end(JSON.stringify({
|
|
41005
|
-
output: {
|
|
41006
|
-
message: `Disabled ${toDisable.length} provider(s)`,
|
|
41007
|
-
disabled: toDisable
|
|
41008
|
-
}
|
|
41009
|
-
}));
|
|
41010
|
-
} catch (error) {
|
|
41011
|
-
res.writeHead(500, { "Content-Type": "application/json" });
|
|
41012
|
-
res.end(JSON.stringify({ error: String(error) }));
|
|
41013
|
-
}
|
|
41014
|
-
return;
|
|
41015
|
-
}
|
|
41016
|
-
if (req.method === "POST" && url2.pathname === "/providers/enable") {
|
|
41017
|
-
try {
|
|
41018
|
-
const bodyText = await readBody(req);
|
|
41019
|
-
const body = bodyText ? JSON.parse(bodyText) : {};
|
|
41020
|
-
const indices = body.indices;
|
|
41021
|
-
if (!Array.isArray(indices)) {
|
|
41022
|
-
res.writeHead(400, { "Content-Type": "application/json" });
|
|
41023
|
-
res.end(JSON.stringify({
|
|
41024
|
-
error: "Missing or invalid 'indices' field (expected number[])."
|
|
41025
|
-
}));
|
|
41026
|
-
return;
|
|
41027
|
-
}
|
|
41028
|
-
const state = deps.store.getState();
|
|
41029
|
-
const baseUrlsList = state.baseUrlsList || [];
|
|
41030
|
-
const disabledProviders = [
|
|
41031
|
-
...state.disabledProviders || []
|
|
41032
|
-
];
|
|
41033
|
-
const toEnable = [];
|
|
41034
|
-
for (const idx of indices) {
|
|
41035
|
-
if (typeof idx === "number" && idx >= 0 && idx < baseUrlsList.length) {
|
|
41036
|
-
const baseUrl = baseUrlsList[idx];
|
|
41037
|
-
const pos = disabledProviders.indexOf(baseUrl);
|
|
41038
|
-
if (pos !== -1) {
|
|
41039
|
-
disabledProviders.splice(pos, 1);
|
|
41040
|
-
toEnable.push(baseUrl);
|
|
41041
|
-
}
|
|
41042
|
-
}
|
|
41043
|
-
}
|
|
41044
|
-
deps.store.getState().setDisabledProviders(disabledProviders);
|
|
41045
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
41046
|
-
res.end(JSON.stringify({
|
|
41047
|
-
output: {
|
|
41048
|
-
message: `Enabled ${toEnable.length} provider(s)`,
|
|
41049
|
-
enabled: toEnable
|
|
41050
|
-
}
|
|
41051
|
-
}));
|
|
41052
|
-
} catch (error) {
|
|
41053
|
-
res.writeHead(500, { "Content-Type": "application/json" });
|
|
41054
|
-
res.end(JSON.stringify({ error: String(error) }));
|
|
41055
|
-
}
|
|
41056
|
-
return;
|
|
41057
|
-
}
|
|
41058
|
-
if (req.method === "GET" && url2.pathname === "/clients") {
|
|
41059
|
-
try {
|
|
41060
|
-
const state = deps.store.getState();
|
|
41061
|
-
const clientIds = state.clientIds || [];
|
|
41062
|
-
const clients = clientIds.map((c) => ({
|
|
41063
|
-
id: c.clientId,
|
|
41064
|
-
name: c.name,
|
|
41065
|
-
apiKey: c.apiKey,
|
|
41066
|
-
createdAt: c.createdAt,
|
|
41067
|
-
lastUsed: c.lastUsed
|
|
41068
|
-
}));
|
|
41069
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
41070
|
-
res.end(JSON.stringify({
|
|
41071
|
-
output: {
|
|
41072
|
-
clients,
|
|
41073
|
-
totalCount: clients.length
|
|
41074
|
-
}
|
|
41075
|
-
}));
|
|
41076
|
-
} catch (error) {
|
|
41077
|
-
res.writeHead(500, { "Content-Type": "application/json" });
|
|
41078
|
-
res.end(JSON.stringify({ error: String(error) }));
|
|
41079
|
-
}
|
|
41080
|
-
return;
|
|
41081
|
-
}
|
|
41082
|
-
if (req.method === "POST" && url2.pathname === "/clients/add") {
|
|
41083
|
-
try {
|
|
41084
|
-
const bodyText = await readBody(req);
|
|
41085
|
-
const body = bodyText ? JSON.parse(bodyText) : {};
|
|
41086
|
-
const name = body.name;
|
|
41087
|
-
if (!name || typeof name !== "string" || name.trim() === "") {
|
|
41088
|
-
res.writeHead(400, { "Content-Type": "application/json" });
|
|
41089
|
-
res.end(JSON.stringify({
|
|
41090
|
-
error: "Missing required 'name' field (must be a non-empty string)."
|
|
41091
|
-
}));
|
|
41092
|
-
return;
|
|
41093
|
-
}
|
|
41094
|
-
const clientId = name.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "");
|
|
41095
|
-
if (!clientId) {
|
|
41096
|
-
res.writeHead(400, { "Content-Type": "application/json" });
|
|
41097
|
-
res.end(JSON.stringify({
|
|
41098
|
-
error: "Invalid client name. Must contain alphanumeric characters."
|
|
41099
|
-
}));
|
|
41100
|
-
return;
|
|
41101
|
-
}
|
|
41102
|
-
const state = deps.store.getState();
|
|
41103
|
-
const existingClients = state.clientIds || [];
|
|
41104
|
-
const existingClient = existingClients.find((c) => c.clientId === clientId);
|
|
41105
|
-
if (existingClient) {
|
|
41106
|
-
res.writeHead(409, { "Content-Type": "application/json" });
|
|
41107
|
-
res.end(JSON.stringify({
|
|
41108
|
-
error: `Client with id '${clientId}' already exists.`
|
|
41109
|
-
}));
|
|
41110
|
-
return;
|
|
41111
|
-
}
|
|
41112
|
-
const apiKey = generateApiKey();
|
|
41113
|
-
const newClient = {
|
|
41114
|
-
clientId,
|
|
41115
|
-
name: name.trim(),
|
|
41116
|
-
apiKey,
|
|
41117
|
-
createdAt: Date.now()
|
|
41118
|
-
};
|
|
41119
|
-
deps.store.getState().setClientIds((prev) => [
|
|
41120
|
-
...prev || [],
|
|
41121
|
-
newClient
|
|
41122
|
-
]);
|
|
41123
|
-
logger3.log(`Added client '${name}' with id '${clientId}'`);
|
|
41124
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
41125
|
-
res.end(JSON.stringify({
|
|
41126
|
-
output: {
|
|
41127
|
-
message: `Client '${name}' added successfully`,
|
|
41128
|
-
client: {
|
|
41129
|
-
id: clientId,
|
|
41130
|
-
name: name.trim(),
|
|
41131
|
-
apiKey,
|
|
41132
|
-
createdAt: newClient.createdAt
|
|
41133
|
-
}
|
|
41134
|
-
}
|
|
41135
|
-
}));
|
|
41136
|
-
} catch (error) {
|
|
41137
|
-
res.writeHead(500, { "Content-Type": "application/json" });
|
|
41138
|
-
res.end(JSON.stringify({ error: String(error) }));
|
|
41139
|
-
}
|
|
41140
|
-
return;
|
|
41141
|
-
}
|
|
41142
41078
|
if (req.method === "GET" && url2.pathname === "/usage") {
|
|
41143
41079
|
try {
|
|
41144
41080
|
const output4 = await deps.usageTrackingDriver.list({
|
|
@@ -41260,16 +41196,16 @@ function createDaemonRequestHandler(deps) {
|
|
|
41260
41196
|
}
|
|
41261
41197
|
|
|
41262
41198
|
// src/integrations/opencode.ts
|
|
41263
|
-
import { existsSync as
|
|
41264
|
-
import { readFile as
|
|
41265
|
-
import { dirname as
|
|
41199
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync5 } from "fs";
|
|
41200
|
+
import { readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
|
|
41201
|
+
import { dirname as dirname4 } from "path";
|
|
41266
41202
|
|
|
41267
41203
|
// src/integrations/registry.ts
|
|
41268
41204
|
import { randomBytes as randomBytes6 } from "crypto";
|
|
41269
|
-
import { join as
|
|
41205
|
+
import { join as join6 } from "path";
|
|
41270
41206
|
|
|
41271
41207
|
// src/integrations/pi.ts
|
|
41272
|
-
import { existsSync as
|
|
41208
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync2 } from "fs";
|
|
41273
41209
|
import { readFile, writeFile } from "fs/promises";
|
|
41274
41210
|
import { dirname } from "path";
|
|
41275
41211
|
async function installPiIntegration(config, store, integrationConfig) {
|
|
@@ -41299,7 +41235,7 @@ Installing routstr models in pi models.json...`);
|
|
|
41299
41235
|
}
|
|
41300
41236
|
let piConfig = {};
|
|
41301
41237
|
try {
|
|
41302
|
-
if (
|
|
41238
|
+
if (existsSync4(configPath)) {
|
|
41303
41239
|
const content2 = await readFile(configPath, "utf-8");
|
|
41304
41240
|
piConfig = JSON.parse(content2);
|
|
41305
41241
|
}
|
|
@@ -41310,7 +41246,7 @@ Installing routstr models in pi models.json...`);
|
|
|
41310
41246
|
piConfig.providers = {};
|
|
41311
41247
|
}
|
|
41312
41248
|
try {
|
|
41313
|
-
|
|
41249
|
+
mkdirSync2(dirname(configPath), { recursive: true });
|
|
41314
41250
|
const response = await fetch(`http://localhost:${port}/models`);
|
|
41315
41251
|
const data = await response.json();
|
|
41316
41252
|
const models = data.output?.models || [];
|
|
@@ -41335,7 +41271,7 @@ Installing routstr models in pi models.json...`);
|
|
|
41335
41271
|
}
|
|
41336
41272
|
|
|
41337
41273
|
// src/integrations/openclaw.ts
|
|
41338
|
-
import { existsSync as
|
|
41274
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync3 } from "fs";
|
|
41339
41275
|
import { readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
41340
41276
|
import { dirname as dirname2 } from "path";
|
|
41341
41277
|
var OPENCLAW_PROVIDER_ID = "routstr";
|
|
@@ -41367,7 +41303,7 @@ Installing routstr models in openclaw.json...`);
|
|
|
41367
41303
|
}
|
|
41368
41304
|
let openclawConfig = {};
|
|
41369
41305
|
try {
|
|
41370
|
-
if (
|
|
41306
|
+
if (existsSync5(configPath)) {
|
|
41371
41307
|
const content2 = await readFile2(configPath, "utf-8");
|
|
41372
41308
|
openclawConfig = JSON.parse(content2);
|
|
41373
41309
|
}
|
|
@@ -41387,7 +41323,7 @@ Installing routstr models in openclaw.json...`);
|
|
|
41387
41323
|
openclawConfig.agents.defaults = {};
|
|
41388
41324
|
}
|
|
41389
41325
|
try {
|
|
41390
|
-
|
|
41326
|
+
mkdirSync3(dirname2(configPath), { recursive: true });
|
|
41391
41327
|
const response = await fetch(`http://localhost:${port}/models`);
|
|
41392
41328
|
const data = await response.json();
|
|
41393
41329
|
const models = data.output?.models || [];
|
|
@@ -41427,6 +41363,60 @@ Installing routstr models in openclaw.json...`);
|
|
|
41427
41363
|
}
|
|
41428
41364
|
}
|
|
41429
41365
|
|
|
41366
|
+
// src/integrations/claudecode.ts
|
|
41367
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync4 } from "fs";
|
|
41368
|
+
import { readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
|
|
41369
|
+
import { dirname as dirname3 } from "path";
|
|
41370
|
+
async function installClaudeCodeIntegration(config, store, integrationConfig) {
|
|
41371
|
+
const { clientId, name, configPath } = integrationConfig;
|
|
41372
|
+
logger3.log(`
|
|
41373
|
+
Installing routstr configuration in ${configPath}...`);
|
|
41374
|
+
const port = config.port || 8008;
|
|
41375
|
+
const state = store.getState();
|
|
41376
|
+
const existingClient = (state.clientIds || []).find((c) => c.clientId === clientId);
|
|
41377
|
+
let apiKey;
|
|
41378
|
+
if (existingClient) {
|
|
41379
|
+
apiKey = existingClient.apiKey;
|
|
41380
|
+
logger3.log(`Using existing API key for ${name}`);
|
|
41381
|
+
} else {
|
|
41382
|
+
apiKey = generateApiKey2();
|
|
41383
|
+
store.getState().setClientIds((prev) => [
|
|
41384
|
+
...prev || [],
|
|
41385
|
+
{
|
|
41386
|
+
clientId,
|
|
41387
|
+
name,
|
|
41388
|
+
apiKey,
|
|
41389
|
+
createdAt: Date.now()
|
|
41390
|
+
}
|
|
41391
|
+
]);
|
|
41392
|
+
logger3.log(`Created new API key for ${name}`);
|
|
41393
|
+
}
|
|
41394
|
+
let settings = {};
|
|
41395
|
+
try {
|
|
41396
|
+
if (existsSync6(configPath)) {
|
|
41397
|
+
const content2 = await readFile3(configPath, "utf-8");
|
|
41398
|
+
settings = JSON.parse(content2);
|
|
41399
|
+
}
|
|
41400
|
+
} catch (error) {
|
|
41401
|
+
logger3.error(`Error reading ${configPath}, creating new one.`);
|
|
41402
|
+
}
|
|
41403
|
+
if (!settings.env) {
|
|
41404
|
+
settings.env = {};
|
|
41405
|
+
}
|
|
41406
|
+
settings.env["ANTHROPIC_AUTH_TOKEN"] = apiKey;
|
|
41407
|
+
settings.env["ANTHROPIC_BASE_URL"] = `http://localhost:${port}`;
|
|
41408
|
+
settings.env["ANTHROPIC_DEFAULT_SONNET_MODEL"] = "gpt-5.4";
|
|
41409
|
+
settings.env["ANTHROPIC_DEFAULT_OPUS_MODEL"] = "claude-opus-4.7";
|
|
41410
|
+
settings.env["ANTHROPIC_DEFAULT_HAIKU_MODEL"] = "minimax-m2.7";
|
|
41411
|
+
try {
|
|
41412
|
+
mkdirSync4(dirname3(configPath), { recursive: true });
|
|
41413
|
+
await writeFile3(configPath, JSON.stringify(settings, null, 2));
|
|
41414
|
+
logger3.log(`Successfully updated ${configPath} with routstr settings.`);
|
|
41415
|
+
} catch (error) {
|
|
41416
|
+
logger3.error(`Failed to write to ${configPath}:`, error);
|
|
41417
|
+
}
|
|
41418
|
+
}
|
|
41419
|
+
|
|
41430
41420
|
// src/integrations/registry.ts
|
|
41431
41421
|
function generateApiKey2() {
|
|
41432
41422
|
const bytes4 = randomBytes6(24);
|
|
@@ -41436,23 +41426,29 @@ var CLIENT_CONFIGS = {
|
|
|
41436
41426
|
opencode: {
|
|
41437
41427
|
clientId: "opencode",
|
|
41438
41428
|
name: "OpenCode",
|
|
41439
|
-
configPath:
|
|
41429
|
+
configPath: join6(process.env.HOME || "", ".config/opencode/opencode.json")
|
|
41440
41430
|
},
|
|
41441
41431
|
"pi-agent": {
|
|
41442
41432
|
clientId: "pi-agent",
|
|
41443
41433
|
name: "Pi Agent",
|
|
41444
|
-
configPath:
|
|
41434
|
+
configPath: join6(process.env.HOME || "", ".pi/agent/models.json")
|
|
41445
41435
|
},
|
|
41446
41436
|
openclaw: {
|
|
41447
41437
|
clientId: "openclaw",
|
|
41448
41438
|
name: "OpenClaw",
|
|
41449
|
-
configPath:
|
|
41439
|
+
configPath: join6(process.env.HOME || "", ".openclaw/openclaw.json")
|
|
41440
|
+
},
|
|
41441
|
+
"claude-code": {
|
|
41442
|
+
clientId: "claude-code",
|
|
41443
|
+
name: "Claude Code",
|
|
41444
|
+
configPath: join6(process.env.HOME || "", ".claude/settings.json")
|
|
41450
41445
|
}
|
|
41451
41446
|
};
|
|
41452
41447
|
var CLIENT_INTEGRATIONS = {
|
|
41453
41448
|
opencode: installOpencodeIntegration,
|
|
41454
41449
|
"pi-agent": installPiIntegration,
|
|
41455
|
-
openclaw: installOpenClawIntegration
|
|
41450
|
+
openclaw: installOpenClawIntegration,
|
|
41451
|
+
"claude-code": installClaudeCodeIntegration
|
|
41456
41452
|
};
|
|
41457
41453
|
async function runIntegrationsForClients(clientIds, config, store) {
|
|
41458
41454
|
for (const client2 of clientIds) {
|
|
@@ -41496,8 +41492,8 @@ Installing routstr models in opencode.json...`);
|
|
|
41496
41492
|
}
|
|
41497
41493
|
let opencodeConfig;
|
|
41498
41494
|
try {
|
|
41499
|
-
if (
|
|
41500
|
-
const content2 = await
|
|
41495
|
+
if (existsSync7(configPath)) {
|
|
41496
|
+
const content2 = await readFile4(configPath, "utf-8");
|
|
41501
41497
|
opencodeConfig = JSON.parse(content2);
|
|
41502
41498
|
} else {
|
|
41503
41499
|
opencodeConfig = { provider: {} };
|
|
@@ -41509,7 +41505,7 @@ Installing routstr models in opencode.json...`);
|
|
|
41509
41505
|
opencodeConfig.provider = {};
|
|
41510
41506
|
}
|
|
41511
41507
|
try {
|
|
41512
|
-
|
|
41508
|
+
mkdirSync5(dirname4(configPath), { recursive: true });
|
|
41513
41509
|
const response = await fetch(`http://localhost:${port}/models`);
|
|
41514
41510
|
const data = await response.json();
|
|
41515
41511
|
const models = data.output?.models || [];
|
|
@@ -41532,7 +41528,7 @@ Installing routstr models in opencode.json...`);
|
|
|
41532
41528
|
models: modelsObj
|
|
41533
41529
|
};
|
|
41534
41530
|
opencodeConfig.small_model = OPENCODE_SMALL_MODEL;
|
|
41535
|
-
await
|
|
41531
|
+
await writeFile4(configPath, JSON.stringify(opencodeConfig, null, 2));
|
|
41536
41532
|
logger3.log(`Added "routstr" provider with ${models.length} models to opencode.json`);
|
|
41537
41533
|
} catch (error) {
|
|
41538
41534
|
logger3.error("Failed to install models in opencode.json:", error);
|
|
@@ -41587,7 +41583,7 @@ async function main() {
|
|
|
41587
41583
|
}));
|
|
41588
41584
|
Bun.write(PID_FILE, String(process.pid));
|
|
41589
41585
|
try {
|
|
41590
|
-
if (
|
|
41586
|
+
if (existsSync8(SOCKET_PATH)) {
|
|
41591
41587
|
Bun.spawn(["rm", SOCKET_PATH]);
|
|
41592
41588
|
}
|
|
41593
41589
|
} catch {}
|