routstrd 0.2.0 → 0.2.1
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/bun.lock +198 -2
- package/dist/daemon/index.js +360 -231
- package/dist/index.js +57 -3
- package/package.json +2 -3
- package/src/cli.ts +79 -1
- package/src/daemon/http/index.ts +55 -3
package/dist/daemon/index.js
CHANGED
|
@@ -29366,12 +29366,12 @@ var require_lib = __commonJS((exports, module) => {
|
|
|
29366
29366
|
var exports_dist = {};
|
|
29367
29367
|
__export(exports_dist, {
|
|
29368
29368
|
setDefaultUsageTrackingDriver: () => setDefaultUsageTrackingDriver,
|
|
29369
|
-
routeRequestsToNodeResponse: () => routeRequestsToNodeResponse,
|
|
29370
29369
|
routeRequests: () => routeRequests,
|
|
29371
29370
|
normalizeProviderUrl: () => normalizeProviderUrl,
|
|
29372
29371
|
localStorageDriver: () => localStorageDriver,
|
|
29373
29372
|
isTorContext: () => isTorContext,
|
|
29374
29373
|
isOnionUrl: () => isOnionUrl,
|
|
29374
|
+
inspectSSEWebStream: () => inspectSSEWebStream,
|
|
29375
29375
|
getProviderEndpoints: () => getProviderEndpoints,
|
|
29376
29376
|
getDefaultUsageTrackingDriver: () => getDefaultUsageTrackingDriver,
|
|
29377
29377
|
getDefaultStorageAdapter: () => getDefaultStorageAdapter,
|
|
@@ -29412,10 +29412,8 @@ __export(exports_dist, {
|
|
|
29412
29412
|
CashuSpender: () => CashuSpender,
|
|
29413
29413
|
BalanceManager: () => BalanceManager
|
|
29414
29414
|
});
|
|
29415
|
-
import { Transform
|
|
29416
|
-
import
|
|
29417
|
-
import * as path from "path";
|
|
29418
|
-
import * as os2 from "os";
|
|
29415
|
+
import { Transform } from "stream";
|
|
29416
|
+
import { StringDecoder } from "string_decoder";
|
|
29419
29417
|
function isNetworkErrorMessage(message) {
|
|
29420
29418
|
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");
|
|
29421
29419
|
}
|
|
@@ -29684,28 +29682,125 @@ async function createBunSqliteDriver(dbPath) {
|
|
|
29684
29682
|
}
|
|
29685
29683
|
};
|
|
29686
29684
|
}
|
|
29687
|
-
function
|
|
29685
|
+
function mergeUsage(previous, next) {
|
|
29686
|
+
if (!previous)
|
|
29687
|
+
return next;
|
|
29688
|
+
return {
|
|
29689
|
+
promptTokens: next.promptTokens > 0 ? next.promptTokens : previous.promptTokens,
|
|
29690
|
+
completionTokens: next.completionTokens > 0 ? next.completionTokens : previous.completionTokens,
|
|
29691
|
+
totalTokens: next.totalTokens > 0 ? next.totalTokens : previous.totalTokens,
|
|
29692
|
+
cost: next.cost > 0 ? next.cost : previous.cost,
|
|
29693
|
+
satsCost: next.satsCost > 0 ? next.satsCost : previous.satsCost
|
|
29694
|
+
};
|
|
29695
|
+
}
|
|
29696
|
+
function hasUsageChanged(previous, next) {
|
|
29697
|
+
if (!previous)
|
|
29698
|
+
return true;
|
|
29699
|
+
return previous.promptTokens !== next.promptTokens || previous.completionTokens !== next.completionTokens || previous.totalTokens !== next.totalTokens || previous.cost !== next.cost || previous.satsCost !== next.satsCost;
|
|
29700
|
+
}
|
|
29701
|
+
async function inspectSSEWebStream(stream, onUsage, onResponseId) {
|
|
29702
|
+
const reader = stream.getReader();
|
|
29703
|
+
const decoder = new TextDecoder("utf-8");
|
|
29688
29704
|
let buffer = "";
|
|
29689
29705
|
let capturedUsage = null;
|
|
29706
|
+
let capturedResponseId;
|
|
29690
29707
|
let responseIdCaptured = false;
|
|
29691
|
-
const
|
|
29692
|
-
if (
|
|
29693
|
-
return
|
|
29694
|
-
|
|
29695
|
-
|
|
29696
|
-
|
|
29697
|
-
|
|
29698
|
-
|
|
29699
|
-
|
|
29700
|
-
|
|
29708
|
+
const inspectDataPayload = (jsonText) => {
|
|
29709
|
+
if (responseIdCaptured && capturedUsage && capturedUsage.totalTokens > 0) {
|
|
29710
|
+
return;
|
|
29711
|
+
}
|
|
29712
|
+
const trimmed = jsonText.trim();
|
|
29713
|
+
if (!trimmed || trimmed === "[DONE]")
|
|
29714
|
+
return;
|
|
29715
|
+
if (!trimmed.startsWith("{") && !trimmed.startsWith("["))
|
|
29716
|
+
return;
|
|
29717
|
+
try {
|
|
29718
|
+
const data = JSON.parse(trimmed);
|
|
29719
|
+
if (!responseIdCaptured) {
|
|
29720
|
+
const responseId = data?.id;
|
|
29721
|
+
if (typeof responseId === "string" && responseId.trim().length > 0) {
|
|
29722
|
+
capturedResponseId = responseId.trim();
|
|
29723
|
+
onResponseId?.(capturedResponseId);
|
|
29724
|
+
responseIdCaptured = true;
|
|
29725
|
+
}
|
|
29726
|
+
}
|
|
29727
|
+
const usage = extractUsageFromSSEJson(data);
|
|
29728
|
+
if (usage) {
|
|
29729
|
+
const merged = mergeUsage(capturedUsage, usage);
|
|
29730
|
+
if (hasUsageChanged(capturedUsage, merged)) {
|
|
29731
|
+
capturedUsage = merged;
|
|
29732
|
+
onUsage(merged);
|
|
29733
|
+
}
|
|
29734
|
+
}
|
|
29735
|
+
} catch {}
|
|
29701
29736
|
};
|
|
29702
|
-
const
|
|
29703
|
-
if (
|
|
29704
|
-
return
|
|
29705
|
-
|
|
29737
|
+
const inspectEventBlock = (eventBlock) => {
|
|
29738
|
+
if (responseIdCaptured && capturedUsage && capturedUsage.totalTokens > 0) {
|
|
29739
|
+
return;
|
|
29740
|
+
}
|
|
29741
|
+
const lines = eventBlock.split(/\r?\n/);
|
|
29742
|
+
const dataParts = [];
|
|
29743
|
+
for (const line of lines) {
|
|
29744
|
+
if (!line || line.startsWith(":"))
|
|
29745
|
+
continue;
|
|
29746
|
+
if (line.startsWith("data:")) {
|
|
29747
|
+
const value = line.startsWith("data: ") ? line.slice(6) : line.slice(5);
|
|
29748
|
+
dataParts.push(value);
|
|
29749
|
+
}
|
|
29750
|
+
}
|
|
29751
|
+
if (dataParts.length === 0)
|
|
29752
|
+
return;
|
|
29753
|
+
inspectDataPayload(dataParts.join(`
|
|
29754
|
+
`));
|
|
29755
|
+
};
|
|
29756
|
+
const drainBufferedEvents = () => {
|
|
29757
|
+
const terminator = /\r?\n\r?\n/g;
|
|
29758
|
+
let lastIndex = 0;
|
|
29759
|
+
let match;
|
|
29760
|
+
while ((match = terminator.exec(buffer)) !== null) {
|
|
29761
|
+
const block = buffer.slice(lastIndex, match.index);
|
|
29762
|
+
lastIndex = match.index + match[0].length;
|
|
29763
|
+
if (block.length > 0)
|
|
29764
|
+
inspectEventBlock(block);
|
|
29765
|
+
}
|
|
29766
|
+
if (lastIndex > 0)
|
|
29767
|
+
buffer = buffer.slice(lastIndex);
|
|
29768
|
+
};
|
|
29769
|
+
try {
|
|
29770
|
+
while (true) {
|
|
29771
|
+
const { value, done } = await reader.read();
|
|
29772
|
+
if (done)
|
|
29773
|
+
break;
|
|
29774
|
+
if (value && value.byteLength > 0) {
|
|
29775
|
+
buffer += decoder.decode(value, { stream: true });
|
|
29776
|
+
drainBufferedEvents();
|
|
29777
|
+
}
|
|
29778
|
+
}
|
|
29779
|
+
buffer += decoder.decode();
|
|
29780
|
+
drainBufferedEvents();
|
|
29781
|
+
if (buffer.length > 0) {
|
|
29782
|
+
const tail = buffer.replace(/\r?\n+$/, "");
|
|
29783
|
+
if (tail.length > 0)
|
|
29784
|
+
inspectEventBlock(tail);
|
|
29785
|
+
buffer = "";
|
|
29786
|
+
}
|
|
29787
|
+
} catch {} finally {
|
|
29788
|
+
try {
|
|
29789
|
+
reader.releaseLock();
|
|
29790
|
+
} catch {}
|
|
29791
|
+
}
|
|
29792
|
+
return {
|
|
29793
|
+
capturedUsage: capturedUsage ?? undefined,
|
|
29794
|
+
capturedResponseId
|
|
29706
29795
|
};
|
|
29796
|
+
}
|
|
29797
|
+
function createSSEParserTransform(onUsage, onResponseId) {
|
|
29798
|
+
let buffer = "";
|
|
29799
|
+
const decoder = new StringDecoder("utf8");
|
|
29800
|
+
let capturedUsage = null;
|
|
29801
|
+
let responseIdCaptured = false;
|
|
29707
29802
|
const inspectDataPayload = (jsonText) => {
|
|
29708
|
-
if (responseIdCaptured && capturedUsage
|
|
29803
|
+
if (responseIdCaptured && capturedUsage && capturedUsage.totalTokens > 0) {
|
|
29709
29804
|
return;
|
|
29710
29805
|
}
|
|
29711
29806
|
const trimmed = jsonText.trim();
|
|
@@ -29733,7 +29828,7 @@ function createSSEParserTransform(onUsage, onResponseId) {
|
|
|
29733
29828
|
} catch {}
|
|
29734
29829
|
};
|
|
29735
29830
|
const inspectEventBlock = (eventBlock) => {
|
|
29736
|
-
if (responseIdCaptured && capturedUsage
|
|
29831
|
+
if (responseIdCaptured && capturedUsage && capturedUsage.totalTokens > 0) {
|
|
29737
29832
|
return;
|
|
29738
29833
|
}
|
|
29739
29834
|
const lines = eventBlock.split(/\r?\n/);
|
|
@@ -29752,35 +29847,35 @@ function createSSEParserTransform(onUsage, onResponseId) {
|
|
|
29752
29847
|
`);
|
|
29753
29848
|
inspectDataPayload(payload);
|
|
29754
29849
|
};
|
|
29755
|
-
const
|
|
29756
|
-
|
|
29757
|
-
|
|
29758
|
-
|
|
29759
|
-
|
|
29760
|
-
|
|
29761
|
-
|
|
29850
|
+
const processBufferedEvents = () => {
|
|
29851
|
+
const terminator = /\r?\n\r?\n/g;
|
|
29852
|
+
let lastIndex = 0;
|
|
29853
|
+
let match;
|
|
29854
|
+
while ((match = terminator.exec(buffer)) !== null) {
|
|
29855
|
+
const block = buffer.slice(lastIndex, match.index);
|
|
29856
|
+
lastIndex = match.index + match[0].length;
|
|
29857
|
+
if (block.length > 0) {
|
|
29858
|
+
inspectEventBlock(block);
|
|
29859
|
+
}
|
|
29860
|
+
}
|
|
29861
|
+
if (lastIndex > 0) {
|
|
29862
|
+
buffer = buffer.slice(lastIndex);
|
|
29863
|
+
}
|
|
29762
29864
|
};
|
|
29763
29865
|
return new Transform({
|
|
29764
29866
|
transform(chunk, _encoding, callback) {
|
|
29765
|
-
|
|
29766
|
-
|
|
29767
|
-
|
|
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);
|
|
29776
|
-
}
|
|
29867
|
+
this.push(chunk);
|
|
29868
|
+
buffer += decoder.write(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
29869
|
+
processBufferedEvents();
|
|
29777
29870
|
callback();
|
|
29778
29871
|
},
|
|
29779
29872
|
flush(callback) {
|
|
29873
|
+
buffer += decoder.end();
|
|
29874
|
+
processBufferedEvents();
|
|
29780
29875
|
if (buffer.length > 0) {
|
|
29781
29876
|
const tail = buffer.replace(/\r?\n+$/, "");
|
|
29782
29877
|
if (tail.length > 0) {
|
|
29783
|
-
|
|
29878
|
+
inspectEventBlock(tail);
|
|
29784
29879
|
}
|
|
29785
29880
|
buffer = "";
|
|
29786
29881
|
}
|
|
@@ -29792,7 +29887,7 @@ async function resolveRouteRequestContext(options) {
|
|
|
29792
29887
|
const {
|
|
29793
29888
|
modelId,
|
|
29794
29889
|
requestBody,
|
|
29795
|
-
path
|
|
29890
|
+
path = "/v1/chat/completions",
|
|
29796
29891
|
headers = {},
|
|
29797
29892
|
forcedProvider,
|
|
29798
29893
|
walletAdapter,
|
|
@@ -29880,17 +29975,17 @@ async function resolveRouteRequestContext(options) {
|
|
|
29880
29975
|
client: client2,
|
|
29881
29976
|
baseUrl,
|
|
29882
29977
|
mintUrl,
|
|
29883
|
-
path
|
|
29978
|
+
path,
|
|
29884
29979
|
headers,
|
|
29885
29980
|
modelId,
|
|
29886
29981
|
proxiedBody
|
|
29887
29982
|
};
|
|
29888
29983
|
}
|
|
29889
29984
|
async function routeRequests(options) {
|
|
29890
|
-
const { client: client2, baseUrl, mintUrl, path
|
|
29985
|
+
const { client: client2, baseUrl, mintUrl, path, headers, modelId, proxiedBody } = await resolveRouteRequestContext(options);
|
|
29891
29986
|
try {
|
|
29892
29987
|
const response = await client2.routeRequest({
|
|
29893
|
-
path
|
|
29988
|
+
path,
|
|
29894
29989
|
method: "POST",
|
|
29895
29990
|
body: proxiedBody,
|
|
29896
29991
|
headers,
|
|
@@ -29909,27 +30004,6 @@ async function routeRequests(options) {
|
|
|
29909
30004
|
throw error;
|
|
29910
30005
|
}
|
|
29911
30006
|
}
|
|
29912
|
-
async function routeRequestsToNodeResponse(options) {
|
|
29913
|
-
const { res } = options;
|
|
29914
|
-
const { client: client2, baseUrl, mintUrl, path: path2, headers, modelId, proxiedBody } = await resolveRouteRequestContext(options);
|
|
29915
|
-
try {
|
|
29916
|
-
await client2.routeRequestToNodeResponse({
|
|
29917
|
-
path: path2,
|
|
29918
|
-
method: "POST",
|
|
29919
|
-
body: proxiedBody,
|
|
29920
|
-
headers,
|
|
29921
|
-
baseUrl,
|
|
29922
|
-
mintUrl,
|
|
29923
|
-
modelId,
|
|
29924
|
-
res
|
|
29925
|
-
});
|
|
29926
|
-
} catch (error) {
|
|
29927
|
-
if (error instanceof Error && (error.message.includes("401") || error.message.includes("402") || error.message.includes("403"))) {
|
|
29928
|
-
throw new Error(`Authentication failed: ${error.message}`);
|
|
29929
|
-
}
|
|
29930
|
-
throw error;
|
|
29931
|
-
}
|
|
29932
|
-
}
|
|
29933
30007
|
function extractMaxTokens(requestBody) {
|
|
29934
30008
|
if (!requestBody || typeof requestBody !== "object") {
|
|
29935
30009
|
return;
|
|
@@ -30428,10 +30502,10 @@ var import_rxjs24, InsufficientBalanceError, ProviderError, MintUnreachableError
|
|
|
30428
30502
|
`;
|
|
30429
30503
|
if (typeof window === "undefined") {
|
|
30430
30504
|
try {
|
|
30431
|
-
const
|
|
30432
|
-
const
|
|
30433
|
-
const logPath =
|
|
30434
|
-
|
|
30505
|
+
const fs2 = await import("fs");
|
|
30506
|
+
const path = await import("path");
|
|
30507
|
+
const logPath = path.join(process.cwd(), "audit.log");
|
|
30508
|
+
fs2.appendFileSync(logPath, logLine);
|
|
30435
30509
|
} catch (error) {
|
|
30436
30510
|
console.error("[AuditLogger] Failed to write to file:", error);
|
|
30437
30511
|
}
|
|
@@ -30870,7 +30944,7 @@ var import_rxjs24, InsufficientBalanceError, ProviderError, MintUnreachableError
|
|
|
30870
30944
|
}
|
|
30871
30945
|
return 0;
|
|
30872
30946
|
}
|
|
30873
|
-
}, BalanceManager = class {
|
|
30947
|
+
}, BalanceManager = class _BalanceManager {
|
|
30874
30948
|
constructor(walletAdapter, storageAdapter, providerRegistry, cashuSpender) {
|
|
30875
30949
|
this.walletAdapter = walletAdapter;
|
|
30876
30950
|
this.storageAdapter = storageAdapter;
|
|
@@ -30882,6 +30956,41 @@ var import_rxjs24, InsufficientBalanceError, ProviderError, MintUnreachableError
|
|
|
30882
30956
|
}
|
|
30883
30957
|
}
|
|
30884
30958
|
cashuSpender;
|
|
30959
|
+
providerWalletOps = /* @__PURE__ */ new Map;
|
|
30960
|
+
static PROVIDER_WALLET_COOLDOWN_MS = 1e4;
|
|
30961
|
+
_canRunProviderWalletOperation(baseUrl, type) {
|
|
30962
|
+
const existing = this.providerWalletOps.get(baseUrl);
|
|
30963
|
+
if (!existing) {
|
|
30964
|
+
return { allowed: true };
|
|
30965
|
+
}
|
|
30966
|
+
if (existing.type === type) {
|
|
30967
|
+
return { allowed: true };
|
|
30968
|
+
}
|
|
30969
|
+
if (!existing.endTime) {
|
|
30970
|
+
return {
|
|
30971
|
+
allowed: false,
|
|
30972
|
+
reason: `Provider wallet operation locked; ${existing.type} in progress`
|
|
30973
|
+
};
|
|
30974
|
+
}
|
|
30975
|
+
const elapsed = Date.now() - existing.endTime;
|
|
30976
|
+
if (elapsed < _BalanceManager.PROVIDER_WALLET_COOLDOWN_MS) {
|
|
30977
|
+
return {
|
|
30978
|
+
allowed: false,
|
|
30979
|
+
reason: `Provider wallet operation locked; recent ${existing.type} completed ${Math.round(elapsed / 1000)}s ago`
|
|
30980
|
+
};
|
|
30981
|
+
}
|
|
30982
|
+
this.providerWalletOps.delete(baseUrl);
|
|
30983
|
+
return { allowed: true };
|
|
30984
|
+
}
|
|
30985
|
+
_beginProviderWalletOperation(baseUrl, type) {
|
|
30986
|
+
this.providerWalletOps.set(baseUrl, { type, startTime: Date.now() });
|
|
30987
|
+
}
|
|
30988
|
+
_endProviderWalletOperation(baseUrl, type) {
|
|
30989
|
+
const existing = this.providerWalletOps.get(baseUrl);
|
|
30990
|
+
if (existing && existing.type === type) {
|
|
30991
|
+
existing.endTime = Date.now();
|
|
30992
|
+
}
|
|
30993
|
+
}
|
|
30885
30994
|
async getBalanceState() {
|
|
30886
30995
|
const mintBalances = await this.walletAdapter.getBalances();
|
|
30887
30996
|
const units = this.walletAdapter.getMintUnits();
|
|
@@ -30911,6 +31020,20 @@ var import_rxjs24, InsufficientBalanceError, ProviderError, MintUnreachableError
|
|
|
30911
31020
|
};
|
|
30912
31021
|
}
|
|
30913
31022
|
async refundApiKey(options) {
|
|
31023
|
+
const { mintUrl, baseUrl, apiKey, forceRefund } = options;
|
|
31024
|
+
const guard = this._canRunProviderWalletOperation(baseUrl, "refund");
|
|
31025
|
+
if (!guard.allowed) {
|
|
31026
|
+
console.log(`[BalanceManager] Skipping refund for ${baseUrl} - ${guard.reason}`);
|
|
31027
|
+
return { success: false, message: guard.reason };
|
|
31028
|
+
}
|
|
31029
|
+
this._beginProviderWalletOperation(baseUrl, "refund");
|
|
31030
|
+
try {
|
|
31031
|
+
return await this._refundApiKeyImpl({ mintUrl, baseUrl, apiKey, forceRefund });
|
|
31032
|
+
} finally {
|
|
31033
|
+
this._endProviderWalletOperation(baseUrl, "refund");
|
|
31034
|
+
}
|
|
31035
|
+
}
|
|
31036
|
+
async _refundApiKeyImpl(options) {
|
|
30914
31037
|
const { mintUrl, baseUrl, apiKey, forceRefund } = options;
|
|
30915
31038
|
if (!apiKey) {
|
|
30916
31039
|
return { success: false, message: "No API key to refund" };
|
|
@@ -31029,6 +31152,20 @@ var import_rxjs24, InsufficientBalanceError, ProviderError, MintUnreachableError
|
|
|
31029
31152
|
}
|
|
31030
31153
|
}
|
|
31031
31154
|
async topUp(options) {
|
|
31155
|
+
const { mintUrl, baseUrl, amount, token: providedToken } = options;
|
|
31156
|
+
const guard = this._canRunProviderWalletOperation(baseUrl, "topup");
|
|
31157
|
+
if (!guard.allowed) {
|
|
31158
|
+
console.log(`[BalanceManager] Skipping topup for ${baseUrl} - ${guard.reason}`);
|
|
31159
|
+
return { success: false, message: guard.reason };
|
|
31160
|
+
}
|
|
31161
|
+
this._beginProviderWalletOperation(baseUrl, "topup");
|
|
31162
|
+
try {
|
|
31163
|
+
return await this._topUpImpl({ mintUrl, baseUrl, amount, token: providedToken });
|
|
31164
|
+
} finally {
|
|
31165
|
+
this._endProviderWalletOperation(baseUrl, "topup");
|
|
31166
|
+
}
|
|
31167
|
+
}
|
|
31168
|
+
async _topUpImpl(options) {
|
|
31032
31169
|
const { mintUrl, baseUrl, amount, token: providedToken } = options;
|
|
31033
31170
|
if (!amount || amount <= 0) {
|
|
31034
31171
|
return { success: false, message: "Invalid top up amount" };
|
|
@@ -31153,7 +31290,7 @@ var import_rxjs24, InsufficientBalanceError, ProviderError, MintUnreachableError
|
|
|
31153
31290
|
try {
|
|
31154
31291
|
console.log(`[BalanceManager.createProviderToken] Attempting mint: ${candidateMint}, amount: ${requiredAmount}`);
|
|
31155
31292
|
const token = await this.walletAdapter.sendToken(candidateMint, requiredAmount, p2pkPubkey);
|
|
31156
|
-
console.log(`[BalanceManager.createProviderToken] SUCCESS: Token created from mint ${candidateMint}`);
|
|
31293
|
+
console.log(`[BalanceManager.createProviderToken] SUCCESS: Token created from mint ${candidateMint}, all mint balances: ${JSON.stringify(Object.fromEntries(Object.entries(balances).map(([mint, balance]) => [mint, getBalanceInSats(balance, units[mint])])))}`);
|
|
31157
31294
|
return {
|
|
31158
31295
|
success: true,
|
|
31159
31296
|
token,
|
|
@@ -31963,8 +32100,9 @@ var import_rxjs24, InsufficientBalanceError, ProviderError, MintUnreachableError
|
|
|
31963
32100
|
const approximateTokens = apiMessagesNoImages ? Math.ceil(JSON.stringify(apiMessagesNoImages, null, 2).length / 2.84) : 1e4;
|
|
31964
32101
|
const totalInputTokens = approximateTokens + imageTokens;
|
|
31965
32102
|
const sp = model2?.sats_pricing;
|
|
31966
|
-
if (!sp)
|
|
32103
|
+
if (!sp) {
|
|
31967
32104
|
return 0;
|
|
32105
|
+
}
|
|
31968
32106
|
if (!sp.max_completion_cost) {
|
|
31969
32107
|
return sp.max_cost ?? 50;
|
|
31970
32108
|
}
|
|
@@ -33424,31 +33562,12 @@ var import_rxjs24, InsufficientBalanceError, ProviderError, MintUnreachableError
|
|
|
33424
33562
|
}
|
|
33425
33563
|
async routeRequest(params) {
|
|
33426
33564
|
const prepared = await this._prepareRoutedRequest(params);
|
|
33427
|
-
const
|
|
33428
|
-
|
|
33429
|
-
|
|
33430
|
-
|
|
33431
|
-
|
|
33432
|
-
|
|
33433
|
-
modelId: prepared.modelId,
|
|
33434
|
-
usage: prepared.capturedUsage,
|
|
33435
|
-
requestId: prepared.capturedResponseId,
|
|
33436
|
-
clientApiKey: prepared.clientApiKey
|
|
33437
|
-
});
|
|
33438
|
-
prepared.response.satsSpent = satsSpent;
|
|
33439
|
-
prepared.response.usage = prepared.capturedUsage;
|
|
33440
|
-
prepared.response.requestId = prepared.capturedResponseId;
|
|
33441
|
-
return prepared.response;
|
|
33442
|
-
}
|
|
33443
|
-
async routeRequestToNodeResponse(params) {
|
|
33444
|
-
const { res } = params;
|
|
33445
|
-
const prepared = await this._prepareRoutedRequest(params);
|
|
33446
|
-
res.statusCode = prepared.response.status;
|
|
33447
|
-
prepared.response.headers.forEach((value, key) => {
|
|
33448
|
-
res.setHeader(key, value);
|
|
33449
|
-
});
|
|
33450
|
-
const body = prepared.response.body;
|
|
33451
|
-
if (!body) {
|
|
33565
|
+
const contentType = prepared.response.headers.get("content-type") || "";
|
|
33566
|
+
const isSSE = contentType.includes("text/event-stream");
|
|
33567
|
+
const runFinalize = async () => {
|
|
33568
|
+
const { capturedUsage, capturedResponseId } = await prepared.usagePromise;
|
|
33569
|
+
const usage = capturedUsage ?? prepared.capturedUsage;
|
|
33570
|
+
const requestId = capturedResponseId ?? prepared.capturedResponseId;
|
|
33452
33571
|
const satsSpent = await this._handlePostResponseBalanceUpdate({
|
|
33453
33572
|
token: prepared.tokenUsed,
|
|
33454
33573
|
baseUrl: prepared.baseUrlUsed,
|
|
@@ -33456,53 +33575,25 @@ var import_rxjs24, InsufficientBalanceError, ProviderError, MintUnreachableError
|
|
|
33456
33575
|
initialTokenBalance: prepared.tokenBalanceInSats,
|
|
33457
33576
|
response: prepared.response,
|
|
33458
33577
|
modelId: prepared.modelId,
|
|
33459
|
-
usage
|
|
33460
|
-
requestId
|
|
33578
|
+
usage,
|
|
33579
|
+
requestId,
|
|
33461
33580
|
clientApiKey: prepared.clientApiKey
|
|
33462
33581
|
});
|
|
33463
33582
|
prepared.response.satsSpent = satsSpent;
|
|
33464
|
-
|
|
33465
|
-
|
|
33583
|
+
prepared.response.usage = usage;
|
|
33584
|
+
prepared.response.requestId = requestId;
|
|
33585
|
+
return satsSpent;
|
|
33586
|
+
};
|
|
33587
|
+
if (isSSE) {
|
|
33588
|
+
const finalizePromise = runFinalize().catch((error) => {
|
|
33589
|
+
this._log("ERROR", "[RoutstrClient] SSE finalize failed:", error);
|
|
33590
|
+
return 0;
|
|
33591
|
+
});
|
|
33592
|
+
prepared.response.finalize = () => finalizePromise;
|
|
33593
|
+
return prepared.response;
|
|
33466
33594
|
}
|
|
33467
|
-
|
|
33468
|
-
|
|
33469
|
-
let settled = false;
|
|
33470
|
-
const finish = async () => {
|
|
33471
|
-
if (settled)
|
|
33472
|
-
return;
|
|
33473
|
-
settled = true;
|
|
33474
|
-
try {
|
|
33475
|
-
const satsSpent = await this._handlePostResponseBalanceUpdate({
|
|
33476
|
-
token: prepared.tokenUsed,
|
|
33477
|
-
baseUrl: prepared.baseUrlUsed,
|
|
33478
|
-
mintUrl: params.mintUrl,
|
|
33479
|
-
initialTokenBalance: prepared.tokenBalanceInSats,
|
|
33480
|
-
response: prepared.response,
|
|
33481
|
-
modelId: prepared.modelId,
|
|
33482
|
-
usage: prepared.capturedUsage,
|
|
33483
|
-
requestId: prepared.capturedResponseId,
|
|
33484
|
-
clientApiKey: prepared.clientApiKey
|
|
33485
|
-
});
|
|
33486
|
-
prepared.response.satsSpent = satsSpent;
|
|
33487
|
-
prepared.response.usage = prepared.capturedUsage;
|
|
33488
|
-
prepared.response.requestId = prepared.capturedResponseId;
|
|
33489
|
-
resolve();
|
|
33490
|
-
} catch (error) {
|
|
33491
|
-
reject(error);
|
|
33492
|
-
}
|
|
33493
|
-
};
|
|
33494
|
-
const fail = (error) => {
|
|
33495
|
-
if (settled)
|
|
33496
|
-
return;
|
|
33497
|
-
settled = true;
|
|
33498
|
-
reject(error);
|
|
33499
|
-
};
|
|
33500
|
-
res.once("finish", finish);
|
|
33501
|
-
res.once("close", finish);
|
|
33502
|
-
res.once("error", fail);
|
|
33503
|
-
nodeReadable.once("error", fail);
|
|
33504
|
-
nodeReadable.pipe(res);
|
|
33505
|
-
});
|
|
33595
|
+
await runFinalize();
|
|
33596
|
+
return prepared.response;
|
|
33506
33597
|
}
|
|
33507
33598
|
async _prepareRoutedRequest(params) {
|
|
33508
33599
|
const {
|
|
@@ -33523,7 +33614,14 @@ var import_rxjs24, InsufficientBalanceError, ProviderError, MintUnreachableError
|
|
|
33523
33614
|
const providerModel = await this.providerManager.getModelForProvider(baseUrl, modelId);
|
|
33524
33615
|
selectedModel = providerModel ?? undefined;
|
|
33525
33616
|
if (selectedModel) {
|
|
33526
|
-
|
|
33617
|
+
const requestMessages = Array.isArray(body?.messages) ? body.messages : [];
|
|
33618
|
+
const requestMaxTokens = typeof body?.max_tokens === "number" ? body.max_tokens : undefined;
|
|
33619
|
+
this._log("DEBUG", "[RoutstrClient] generic request pricing input", {
|
|
33620
|
+
modelId: selectedModel.id,
|
|
33621
|
+
messageCount: requestMessages.length,
|
|
33622
|
+
maxTokens: requestMaxTokens
|
|
33623
|
+
});
|
|
33624
|
+
requiredSats = this.providerManager.getRequiredSatsForModel(selectedModel, requestMessages, requestMaxTokens);
|
|
33527
33625
|
}
|
|
33528
33626
|
}
|
|
33529
33627
|
const { token, tokenBalance, tokenBalanceUnit } = await this._spendToken({
|
|
@@ -33559,38 +33657,24 @@ var import_rxjs24, InsufficientBalanceError, ProviderError, MintUnreachableError
|
|
|
33559
33657
|
let processedResponse = response;
|
|
33560
33658
|
let capturedUsage;
|
|
33561
33659
|
let capturedResponseId;
|
|
33660
|
+
let usagePromise = Promise.resolve({});
|
|
33562
33661
|
if (contentType.includes("text/event-stream") && response.body) {
|
|
33563
|
-
const
|
|
33564
|
-
|
|
33565
|
-
|
|
33566
|
-
|
|
33567
|
-
|
|
33568
|
-
const logStream = fs2.createWriteStream(logFile);
|
|
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
|
-
}
|
|
33662
|
+
const [clientStream, inspectStream] = response.body.tee();
|
|
33663
|
+
processedResponse = new Response(clientStream, {
|
|
33664
|
+
status: response.status,
|
|
33665
|
+
statusText: response.statusText,
|
|
33666
|
+
headers: response.headers
|
|
33577
33667
|
});
|
|
33578
|
-
|
|
33668
|
+
processedResponse.baseUrl = response.baseUrl;
|
|
33669
|
+
processedResponse.token = response.token;
|
|
33670
|
+
usagePromise = inspectSSEWebStream(inspectStream, (usage) => {
|
|
33579
33671
|
capturedUsage = usage;
|
|
33580
33672
|
processedResponse.usage = usage;
|
|
33581
33673
|
}, (responseId) => {
|
|
33582
33674
|
capturedResponseId = responseId;
|
|
33583
33675
|
processedResponse.requestId = responseId;
|
|
33584
33676
|
});
|
|
33585
|
-
|
|
33586
|
-
const webStream = Readable.toWeb(transformed);
|
|
33587
|
-
processedResponse = new Response(webStream, {
|
|
33588
|
-
status: response.status,
|
|
33589
|
-
statusText: response.statusText,
|
|
33590
|
-
headers: response.headers
|
|
33591
|
-
});
|
|
33592
|
-
processedResponse.baseUrl = response.baseUrl;
|
|
33593
|
-
processedResponse.token = response.token;
|
|
33677
|
+
processedResponse.usagePromise = usagePromise;
|
|
33594
33678
|
}
|
|
33595
33679
|
return {
|
|
33596
33680
|
response: processedResponse,
|
|
@@ -33600,7 +33684,8 @@ var import_rxjs24, InsufficientBalanceError, ProviderError, MintUnreachableError
|
|
|
33600
33684
|
modelId,
|
|
33601
33685
|
capturedUsage,
|
|
33602
33686
|
capturedResponseId,
|
|
33603
|
-
clientApiKey
|
|
33687
|
+
clientApiKey,
|
|
33688
|
+
usagePromise
|
|
33604
33689
|
};
|
|
33605
33690
|
}
|
|
33606
33691
|
_extractClientApiKey(headers) {
|
|
@@ -33726,9 +33811,9 @@ var import_rxjs24, InsufficientBalanceError, ProviderError, MintUnreachableError
|
|
|
33726
33811
|
}
|
|
33727
33812
|
}
|
|
33728
33813
|
async _makeRequest(params) {
|
|
33729
|
-
const { path
|
|
33814
|
+
const { path, method, body, baseUrl, token, headers } = params;
|
|
33730
33815
|
try {
|
|
33731
|
-
const url2 = `${baseUrl.replace(/\/$/, "")}${
|
|
33816
|
+
const url2 = `${baseUrl.replace(/\/$/, "")}${path}`;
|
|
33732
33817
|
if (this.mode === "xcashu")
|
|
33733
33818
|
this._log("DEBUG", "HEADERS,", headers);
|
|
33734
33819
|
const response = await fetch(url2, {
|
|
@@ -33760,7 +33845,7 @@ var import_rxjs24, InsufficientBalanceError, ProviderError, MintUnreachableError
|
|
|
33760
33845
|
}
|
|
33761
33846
|
async _handleErrorResponse(params, token, status, requestId, xCashuRefundToken, responseBody, retryCount = 0) {
|
|
33762
33847
|
const MAX_RETRIES_PER_PROVIDER = 2;
|
|
33763
|
-
const { path
|
|
33848
|
+
const { path, method, body, selectedModel, baseUrl, mintUrl } = params;
|
|
33764
33849
|
let tryNextProvider = false;
|
|
33765
33850
|
const errorMessage = responseBody;
|
|
33766
33851
|
this._log("DEBUG", `[RoutstrClient] _handleErrorResponse: status=${status}, baseUrl=${baseUrl}, mode=${this.mode}, token preview=${token}, requestId=${requestId}, errorMessage=${errorMessage}`);
|
|
@@ -33797,7 +33882,7 @@ var import_rxjs24, InsufficientBalanceError, ProviderError, MintUnreachableError
|
|
|
33797
33882
|
const currentBalanceInfo = await this.balanceManager.getTokenBalance(params.token, baseUrl);
|
|
33798
33883
|
const currentBalance = currentBalanceInfo.unit === "msat" ? currentBalanceInfo.amount / 1000 : currentBalanceInfo.amount;
|
|
33799
33884
|
const shortfall = Math.max(0, params.requiredSats - currentBalance);
|
|
33800
|
-
topupAmount = shortfall > 0 ? shortfall : params.requiredSats;
|
|
33885
|
+
topupAmount = shortfall > 0.21 * params.requiredSats ? shortfall : 0.21 * params.requiredSats;
|
|
33801
33886
|
this._log("DEBUG", `The shortfall is: ${shortfall}. requiredSats: ${params.requiredSats}. Current Balance: ${currentBalance} `);
|
|
33802
33887
|
} catch (e) {
|
|
33803
33888
|
this._log("WARN", "Could not get current token balance for topup calculation:", e);
|
|
@@ -33926,7 +34011,7 @@ var import_rxjs24, InsufficientBalanceError, ProviderError, MintUnreachableError
|
|
|
33926
34011
|
});
|
|
33927
34012
|
return this._makeRequest({
|
|
33928
34013
|
...params,
|
|
33929
|
-
path
|
|
34014
|
+
path,
|
|
33930
34015
|
method,
|
|
33931
34016
|
body,
|
|
33932
34017
|
baseUrl: nextProvider,
|
|
@@ -34400,7 +34485,7 @@ var init_dist3 = __esm(() => {
|
|
|
34400
34485
|
// src/daemon/index.ts
|
|
34401
34486
|
init_dist3();
|
|
34402
34487
|
import { createServer } from "http";
|
|
34403
|
-
import { existsSync as
|
|
34488
|
+
import { existsSync as existsSync7 } from "fs";
|
|
34404
34489
|
|
|
34405
34490
|
// src/utils/config.ts
|
|
34406
34491
|
var HOME = process.env.HOME || process.env.USERPROFILE || "";
|
|
@@ -34419,19 +34504,19 @@ var DEFAULT_CONFIG = {
|
|
|
34419
34504
|
|
|
34420
34505
|
// src/utils/logger.ts
|
|
34421
34506
|
import { appendFile, mkdir } from "fs/promises";
|
|
34422
|
-
import { existsSync
|
|
34423
|
-
import { join as
|
|
34507
|
+
import { existsSync } from "fs";
|
|
34508
|
+
import { join as join3 } from "path";
|
|
34424
34509
|
var HOME2 = process.env.HOME || process.env.USERPROFILE || "";
|
|
34425
34510
|
var LOG_DIR = process.env.ROUTSTRD_DIR || `${HOME2}/.routstrd`;
|
|
34426
|
-
var LOGS_DIR2 =
|
|
34511
|
+
var LOGS_DIR2 = join3(LOG_DIR, "logs");
|
|
34427
34512
|
function getLogFileForDate(date = new Date) {
|
|
34428
34513
|
const year = date.getFullYear();
|
|
34429
34514
|
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
34430
34515
|
const day = String(date.getDate()).padStart(2, "0");
|
|
34431
|
-
return
|
|
34516
|
+
return join3(LOGS_DIR2, `${year}-${month}-${day}.log`);
|
|
34432
34517
|
}
|
|
34433
34518
|
async function ensureLogDir() {
|
|
34434
|
-
if (!
|
|
34519
|
+
if (!existsSync(LOGS_DIR2)) {
|
|
34435
34520
|
await mkdir(LOGS_DIR2, { recursive: true });
|
|
34436
34521
|
}
|
|
34437
34522
|
}
|
|
@@ -34491,7 +34576,7 @@ function parseArgs(argv) {
|
|
|
34491
34576
|
|
|
34492
34577
|
// src/daemon/config-store.ts
|
|
34493
34578
|
import { mkdir as mkdir2 } from "fs/promises";
|
|
34494
|
-
import { existsSync as
|
|
34579
|
+
import { existsSync as existsSync2 } from "fs";
|
|
34495
34580
|
var REQUESTS_DIR = `${CONFIG_DIR}/requests`;
|
|
34496
34581
|
async function ensureDirs() {
|
|
34497
34582
|
try {
|
|
@@ -34501,7 +34586,7 @@ async function ensureDirs() {
|
|
|
34501
34586
|
}
|
|
34502
34587
|
async function loadDaemonConfig() {
|
|
34503
34588
|
try {
|
|
34504
|
-
if (
|
|
34589
|
+
if (existsSync2(CONFIG_FILE)) {
|
|
34505
34590
|
const content2 = await Bun.file(CONFIG_FILE).text();
|
|
34506
34591
|
return { ...DEFAULT_CONFIG, ...JSON.parse(content2) };
|
|
34507
34592
|
}
|
|
@@ -34813,7 +34898,7 @@ function alphabet3(letters) {
|
|
|
34813
34898
|
}
|
|
34814
34899
|
};
|
|
34815
34900
|
}
|
|
34816
|
-
function
|
|
34901
|
+
function join4(separator = "") {
|
|
34817
34902
|
astr2("join", separator);
|
|
34818
34903
|
return {
|
|
34819
34904
|
encode: (from7) => {
|
|
@@ -35016,12 +35101,12 @@ function checksum2(len, fn) {
|
|
|
35016
35101
|
}
|
|
35017
35102
|
};
|
|
35018
35103
|
}
|
|
35019
|
-
var base163 = chain3(radix23(4), alphabet3("0123456789ABCDEF"),
|
|
35020
|
-
var base323 = chain3(radix23(5), alphabet3("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"), padding3(5),
|
|
35021
|
-
var base32nopad2 = chain3(radix23(5), alphabet3("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"),
|
|
35022
|
-
var base32hex3 = chain3(radix23(5), alphabet3("0123456789ABCDEFGHIJKLMNOPQRSTUV"), padding3(5),
|
|
35023
|
-
var base32hexnopad2 = chain3(radix23(5), alphabet3("0123456789ABCDEFGHIJKLMNOPQRSTUV"),
|
|
35024
|
-
var base32crockford3 = chain3(radix23(5), alphabet3("0123456789ABCDEFGHJKMNPQRSTVWXYZ"),
|
|
35104
|
+
var base163 = chain3(radix23(4), alphabet3("0123456789ABCDEF"), join4(""));
|
|
35105
|
+
var base323 = chain3(radix23(5), alphabet3("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"), padding3(5), join4(""));
|
|
35106
|
+
var base32nopad2 = chain3(radix23(5), alphabet3("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"), join4(""));
|
|
35107
|
+
var base32hex3 = chain3(radix23(5), alphabet3("0123456789ABCDEFGHIJKLMNOPQRSTUV"), padding3(5), join4(""));
|
|
35108
|
+
var base32hexnopad2 = chain3(radix23(5), alphabet3("0123456789ABCDEFGHIJKLMNOPQRSTUV"), join4(""));
|
|
35109
|
+
var base32crockford3 = chain3(radix23(5), alphabet3("0123456789ABCDEFGHJKMNPQRSTVWXYZ"), join4(""), normalize3((s) => s.toUpperCase().replace(/O/g, "0").replace(/[IL]/g, "1")));
|
|
35025
35110
|
var hasBase64Builtin2 = /* @__PURE__ */ (() => typeof Uint8Array.from([]).toBase64 === "function" && typeof Uint8Array.fromBase64 === "function")();
|
|
35026
35111
|
var decodeBase64Builtin2 = (s, isUrl) => {
|
|
35027
35112
|
astr2("base64", s);
|
|
@@ -35039,8 +35124,8 @@ var base643 = hasBase64Builtin2 ? {
|
|
|
35039
35124
|
decode(s) {
|
|
35040
35125
|
return decodeBase64Builtin2(s, false);
|
|
35041
35126
|
}
|
|
35042
|
-
} : chain3(radix23(6), alphabet3("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"), padding3(6),
|
|
35043
|
-
var base64nopad2 = chain3(radix23(6), alphabet3("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"),
|
|
35127
|
+
} : chain3(radix23(6), alphabet3("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"), padding3(6), join4(""));
|
|
35128
|
+
var base64nopad2 = chain3(radix23(6), alphabet3("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"), join4(""));
|
|
35044
35129
|
var base64url3 = hasBase64Builtin2 ? {
|
|
35045
35130
|
encode(b) {
|
|
35046
35131
|
abytes4(b);
|
|
@@ -35049,14 +35134,14 @@ var base64url3 = hasBase64Builtin2 ? {
|
|
|
35049
35134
|
decode(s) {
|
|
35050
35135
|
return decodeBase64Builtin2(s, true);
|
|
35051
35136
|
}
|
|
35052
|
-
} : chain3(radix23(6), alphabet3("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"), padding3(6),
|
|
35053
|
-
var base64urlnopad2 = chain3(radix23(6), alphabet3("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"),
|
|
35054
|
-
var genBase583 = (abc) => chain3(radix5(58), alphabet3(abc),
|
|
35137
|
+
} : chain3(radix23(6), alphabet3("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"), padding3(6), join4(""));
|
|
35138
|
+
var base64urlnopad2 = chain3(radix23(6), alphabet3("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"), join4(""));
|
|
35139
|
+
var genBase583 = (abc) => chain3(radix5(58), alphabet3(abc), join4(""));
|
|
35055
35140
|
var base583 = genBase583("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz");
|
|
35056
35141
|
var base58flickr3 = genBase583("123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ");
|
|
35057
35142
|
var base58xrp3 = genBase583("rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz");
|
|
35058
35143
|
var createBase58check2 = (sha2565) => chain3(checksum2(4, (data) => sha2565(sha2565(data))), base583);
|
|
35059
|
-
var BECH_ALPHABET3 = chain3(alphabet3("qpzry9x8gf2tvdw0s3jn54khce6mua7l"),
|
|
35144
|
+
var BECH_ALPHABET3 = chain3(alphabet3("qpzry9x8gf2tvdw0s3jn54khce6mua7l"), join4(""));
|
|
35060
35145
|
var POLYMOD_GENERATORS3 = [996825010, 642813549, 513874426, 1027748829, 705979059];
|
|
35061
35146
|
function bech32Polymod3(pre) {
|
|
35062
35147
|
const b = pre >> 25;
|
|
@@ -35160,7 +35245,7 @@ var hexBuiltin2 = {
|
|
|
35160
35245
|
return Uint8Array.fromHex(s);
|
|
35161
35246
|
}
|
|
35162
35247
|
};
|
|
35163
|
-
var hex3 = hasHexBuiltin3 ? hexBuiltin2 : chain3(radix23(4), alphabet3("0123456789abcdef"),
|
|
35248
|
+
var hex3 = hasHexBuiltin3 ? hexBuiltin2 : chain3(radix23(4), alphabet3("0123456789abcdef"), join4(""), normalize3((s) => {
|
|
35164
35249
|
if (typeof s !== "string" || s.length % 2 !== 0)
|
|
35165
35250
|
throw new TypeError(`hex.decode: expected string, got ${typeof s} with length ${s.length}`);
|
|
35166
35251
|
return s.toLowerCase();
|
|
@@ -38016,14 +38101,14 @@ class HDKey2 {
|
|
|
38016
38101
|
}
|
|
38017
38102
|
this.pubHash = hash1602(this._publicKey);
|
|
38018
38103
|
}
|
|
38019
|
-
derive(
|
|
38020
|
-
if (!/^[mM]'?/.test(
|
|
38104
|
+
derive(path) {
|
|
38105
|
+
if (!/^[mM]'?/.test(path)) {
|
|
38021
38106
|
throw new Error('Path must start with "m" or "M"');
|
|
38022
38107
|
}
|
|
38023
|
-
if (/^[mM]'?$/.test(
|
|
38108
|
+
if (/^[mM]'?$/.test(path)) {
|
|
38024
38109
|
return this;
|
|
38025
38110
|
}
|
|
38026
|
-
const parts =
|
|
38111
|
+
const parts = path.replace(/^[mM]'?\//, "").split("/");
|
|
38027
38112
|
let child = this;
|
|
38028
38113
|
for (const c of parts) {
|
|
38029
38114
|
const m2 = /^(\d+)('?)$/.exec(c);
|
|
@@ -38382,7 +38467,7 @@ function bt(s, t) {
|
|
|
38382
38467
|
case 2:
|
|
38383
38468
|
return is(s, t, r);
|
|
38384
38469
|
case 3:
|
|
38385
|
-
return
|
|
38470
|
+
return os2(s, t, r);
|
|
38386
38471
|
case 4:
|
|
38387
38472
|
return as2(s, t, r);
|
|
38388
38473
|
case 5:
|
|
@@ -38433,7 +38518,7 @@ function is(s, t, e) {
|
|
|
38433
38518
|
throw new Error("Byte string length exceeds data length");
|
|
38434
38519
|
return { value: new Uint8Array(s.buffer, s.byteOffset + r, n), offset: r + n };
|
|
38435
38520
|
}
|
|
38436
|
-
function
|
|
38521
|
+
function os2(s, t, e) {
|
|
38437
38522
|
const { value: n, offset: r } = ot2(s, t, e);
|
|
38438
38523
|
if (r + n > s.byteLength)
|
|
38439
38524
|
throw new Error("String length exceeds data length");
|
|
@@ -40203,25 +40288,25 @@ function createCocodClient(options = {}) {
|
|
|
40203
40288
|
return proc;
|
|
40204
40289
|
});
|
|
40205
40290
|
let startPromise = null;
|
|
40206
|
-
async function fetchJson(
|
|
40291
|
+
async function fetchJson(path, init = {}) {
|
|
40207
40292
|
const method = init.method || "GET";
|
|
40208
40293
|
const requestInit = {
|
|
40209
40294
|
...init,
|
|
40210
40295
|
unix: socketPath
|
|
40211
40296
|
};
|
|
40212
|
-
const response = await fetchImpl(`http://localhost${
|
|
40297
|
+
const response = await fetchImpl(`http://localhost${path}`, requestInit);
|
|
40213
40298
|
const rawText = await response.text();
|
|
40214
40299
|
if (!rawText.trim()) {
|
|
40215
|
-
throw new CocodHttpError(response.ok ? 502 : response.status, `Empty response from cocod for ${method} ${
|
|
40300
|
+
throw new CocodHttpError(response.ok ? 502 : response.status, `Empty response from cocod for ${method} ${path}`);
|
|
40216
40301
|
}
|
|
40217
40302
|
let data;
|
|
40218
40303
|
try {
|
|
40219
40304
|
data = JSON.parse(rawText);
|
|
40220
40305
|
} catch {
|
|
40221
|
-
throw new CocodHttpError(response.ok ? 502 : response.status, `Invalid JSON response from cocod for ${method} ${
|
|
40306
|
+
throw new CocodHttpError(response.ok ? 502 : response.status, `Invalid JSON response from cocod for ${method} ${path}`);
|
|
40222
40307
|
}
|
|
40223
40308
|
if (!data || typeof data !== "object") {
|
|
40224
|
-
throw new CocodHttpError(response.ok ? 502 : response.status, `Unexpected response shape from cocod for ${method} ${
|
|
40309
|
+
throw new CocodHttpError(response.ok ? 502 : response.status, `Unexpected response shape from cocod for ${method} ${path}`);
|
|
40225
40310
|
}
|
|
40226
40311
|
const errorMessage = toErrorText(data.error);
|
|
40227
40312
|
if (errorMessage) {
|
|
@@ -40272,13 +40357,13 @@ function createCocodClient(options = {}) {
|
|
|
40272
40357
|
}
|
|
40273
40358
|
await startPromise;
|
|
40274
40359
|
}
|
|
40275
|
-
async function callDaemon(
|
|
40360
|
+
async function callDaemon(path, init = {}) {
|
|
40276
40361
|
await ensureDaemonRunning();
|
|
40277
|
-
const response = await fetchJson(
|
|
40362
|
+
const response = await fetchJson(path, init);
|
|
40278
40363
|
return response.output;
|
|
40279
40364
|
}
|
|
40280
|
-
function post(
|
|
40281
|
-
return callDaemon(
|
|
40365
|
+
function post(path, body) {
|
|
40366
|
+
return callDaemon(path, {
|
|
40282
40367
|
method: "POST",
|
|
40283
40368
|
headers: { "Content-Type": "application/json" },
|
|
40284
40369
|
body: JSON.stringify(body)
|
|
@@ -40489,6 +40574,7 @@ function createModelService(modelManager) {
|
|
|
40489
40574
|
// src/daemon/http/index.ts
|
|
40490
40575
|
init_dist3();
|
|
40491
40576
|
import { randomBytes as randomBytes5 } from "crypto";
|
|
40577
|
+
import { Readable } from "stream";
|
|
40492
40578
|
function generateApiKey() {
|
|
40493
40579
|
const bytes4 = randomBytes5(24);
|
|
40494
40580
|
return `sk-${bytes4.toString("hex")}`;
|
|
@@ -41154,7 +41240,7 @@ function createDaemonRequestHandler(deps) {
|
|
|
41154
41240
|
try {
|
|
41155
41241
|
await deps.ensureProvidersBootstrapped();
|
|
41156
41242
|
logger3.log("Routing request with path: ", url2.pathname);
|
|
41157
|
-
await
|
|
41243
|
+
const response = await routeRequests({
|
|
41158
41244
|
modelId,
|
|
41159
41245
|
requestBody,
|
|
41160
41246
|
path: url2.pathname,
|
|
@@ -41169,9 +41255,52 @@ function createDaemonRequestHandler(deps) {
|
|
|
41169
41255
|
mode: deps.mode,
|
|
41170
41256
|
usageTrackingDriver: deps.usageTrackingDriver,
|
|
41171
41257
|
sdkStore: deps.store,
|
|
41172
|
-
providerManager: deps.providerManager
|
|
41173
|
-
|
|
41258
|
+
providerManager: deps.providerManager
|
|
41259
|
+
});
|
|
41260
|
+
res.statusCode = response.status;
|
|
41261
|
+
response.headers.forEach((value, key) => {
|
|
41262
|
+
res.setHeader(key, value);
|
|
41174
41263
|
});
|
|
41264
|
+
const finalize6 = response.finalize;
|
|
41265
|
+
if (!response.body) {
|
|
41266
|
+
res.end();
|
|
41267
|
+
if (finalize6) {
|
|
41268
|
+
try {
|
|
41269
|
+
await finalize6();
|
|
41270
|
+
} catch (err) {
|
|
41271
|
+
logger3.error(`[daemon] finalize error: ${toErrorMessage(err)}`);
|
|
41272
|
+
}
|
|
41273
|
+
}
|
|
41274
|
+
return;
|
|
41275
|
+
}
|
|
41276
|
+
const nodeReadable = Readable.fromWeb(response.body);
|
|
41277
|
+
await new Promise((resolve, reject) => {
|
|
41278
|
+
let settled = false;
|
|
41279
|
+
const finish = () => {
|
|
41280
|
+
if (settled)
|
|
41281
|
+
return;
|
|
41282
|
+
settled = true;
|
|
41283
|
+
resolve();
|
|
41284
|
+
};
|
|
41285
|
+
const fail = (err) => {
|
|
41286
|
+
if (settled)
|
|
41287
|
+
return;
|
|
41288
|
+
settled = true;
|
|
41289
|
+
reject(err);
|
|
41290
|
+
};
|
|
41291
|
+
res.once("finish", finish);
|
|
41292
|
+
res.once("close", finish);
|
|
41293
|
+
res.once("error", fail);
|
|
41294
|
+
nodeReadable.once("error", fail);
|
|
41295
|
+
nodeReadable.pipe(res);
|
|
41296
|
+
});
|
|
41297
|
+
if (finalize6) {
|
|
41298
|
+
try {
|
|
41299
|
+
await finalize6();
|
|
41300
|
+
} catch (err) {
|
|
41301
|
+
logger3.error(`[daemon] finalize error: ${toErrorMessage(err)}`);
|
|
41302
|
+
}
|
|
41303
|
+
}
|
|
41175
41304
|
return;
|
|
41176
41305
|
} catch (error) {
|
|
41177
41306
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -41196,16 +41325,16 @@ function createDaemonRequestHandler(deps) {
|
|
|
41196
41325
|
}
|
|
41197
41326
|
|
|
41198
41327
|
// src/integrations/opencode.ts
|
|
41199
|
-
import { existsSync as
|
|
41328
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync4 } from "fs";
|
|
41200
41329
|
import { readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
|
|
41201
41330
|
import { dirname as dirname4 } from "path";
|
|
41202
41331
|
|
|
41203
41332
|
// src/integrations/registry.ts
|
|
41204
41333
|
import { randomBytes as randomBytes6 } from "crypto";
|
|
41205
|
-
import { join as
|
|
41334
|
+
import { join as join5 } from "path";
|
|
41206
41335
|
|
|
41207
41336
|
// src/integrations/pi.ts
|
|
41208
|
-
import { existsSync as
|
|
41337
|
+
import { existsSync as existsSync3, mkdirSync } from "fs";
|
|
41209
41338
|
import { readFile, writeFile } from "fs/promises";
|
|
41210
41339
|
import { dirname } from "path";
|
|
41211
41340
|
async function installPiIntegration(config, store, integrationConfig) {
|
|
@@ -41235,7 +41364,7 @@ Installing routstr models in pi models.json...`);
|
|
|
41235
41364
|
}
|
|
41236
41365
|
let piConfig = {};
|
|
41237
41366
|
try {
|
|
41238
|
-
if (
|
|
41367
|
+
if (existsSync3(configPath)) {
|
|
41239
41368
|
const content2 = await readFile(configPath, "utf-8");
|
|
41240
41369
|
piConfig = JSON.parse(content2);
|
|
41241
41370
|
}
|
|
@@ -41246,7 +41375,7 @@ Installing routstr models in pi models.json...`);
|
|
|
41246
41375
|
piConfig.providers = {};
|
|
41247
41376
|
}
|
|
41248
41377
|
try {
|
|
41249
|
-
|
|
41378
|
+
mkdirSync(dirname(configPath), { recursive: true });
|
|
41250
41379
|
const response = await fetch(`http://localhost:${port}/models`);
|
|
41251
41380
|
const data = await response.json();
|
|
41252
41381
|
const models = data.output?.models || [];
|
|
@@ -41271,7 +41400,7 @@ Installing routstr models in pi models.json...`);
|
|
|
41271
41400
|
}
|
|
41272
41401
|
|
|
41273
41402
|
// src/integrations/openclaw.ts
|
|
41274
|
-
import { existsSync as
|
|
41403
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync2 } from "fs";
|
|
41275
41404
|
import { readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
41276
41405
|
import { dirname as dirname2 } from "path";
|
|
41277
41406
|
var OPENCLAW_PROVIDER_ID = "routstr";
|
|
@@ -41303,7 +41432,7 @@ Installing routstr models in openclaw.json...`);
|
|
|
41303
41432
|
}
|
|
41304
41433
|
let openclawConfig = {};
|
|
41305
41434
|
try {
|
|
41306
|
-
if (
|
|
41435
|
+
if (existsSync4(configPath)) {
|
|
41307
41436
|
const content2 = await readFile2(configPath, "utf-8");
|
|
41308
41437
|
openclawConfig = JSON.parse(content2);
|
|
41309
41438
|
}
|
|
@@ -41323,7 +41452,7 @@ Installing routstr models in openclaw.json...`);
|
|
|
41323
41452
|
openclawConfig.agents.defaults = {};
|
|
41324
41453
|
}
|
|
41325
41454
|
try {
|
|
41326
|
-
|
|
41455
|
+
mkdirSync2(dirname2(configPath), { recursive: true });
|
|
41327
41456
|
const response = await fetch(`http://localhost:${port}/models`);
|
|
41328
41457
|
const data = await response.json();
|
|
41329
41458
|
const models = data.output?.models || [];
|
|
@@ -41364,7 +41493,7 @@ Installing routstr models in openclaw.json...`);
|
|
|
41364
41493
|
}
|
|
41365
41494
|
|
|
41366
41495
|
// src/integrations/claudecode.ts
|
|
41367
|
-
import { existsSync as
|
|
41496
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync3 } from "fs";
|
|
41368
41497
|
import { readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
|
|
41369
41498
|
import { dirname as dirname3 } from "path";
|
|
41370
41499
|
async function installClaudeCodeIntegration(config, store, integrationConfig) {
|
|
@@ -41393,7 +41522,7 @@ Installing routstr configuration in ${configPath}...`);
|
|
|
41393
41522
|
}
|
|
41394
41523
|
let settings = {};
|
|
41395
41524
|
try {
|
|
41396
|
-
if (
|
|
41525
|
+
if (existsSync5(configPath)) {
|
|
41397
41526
|
const content2 = await readFile3(configPath, "utf-8");
|
|
41398
41527
|
settings = JSON.parse(content2);
|
|
41399
41528
|
}
|
|
@@ -41409,7 +41538,7 @@ Installing routstr configuration in ${configPath}...`);
|
|
|
41409
41538
|
settings.env["ANTHROPIC_DEFAULT_OPUS_MODEL"] = "claude-opus-4.7";
|
|
41410
41539
|
settings.env["ANTHROPIC_DEFAULT_HAIKU_MODEL"] = "minimax-m2.7";
|
|
41411
41540
|
try {
|
|
41412
|
-
|
|
41541
|
+
mkdirSync3(dirname3(configPath), { recursive: true });
|
|
41413
41542
|
await writeFile3(configPath, JSON.stringify(settings, null, 2));
|
|
41414
41543
|
logger3.log(`Successfully updated ${configPath} with routstr settings.`);
|
|
41415
41544
|
} catch (error) {
|
|
@@ -41426,22 +41555,22 @@ var CLIENT_CONFIGS = {
|
|
|
41426
41555
|
opencode: {
|
|
41427
41556
|
clientId: "opencode",
|
|
41428
41557
|
name: "OpenCode",
|
|
41429
|
-
configPath:
|
|
41558
|
+
configPath: join5(process.env.HOME || "", ".config/opencode/opencode.json")
|
|
41430
41559
|
},
|
|
41431
41560
|
"pi-agent": {
|
|
41432
41561
|
clientId: "pi-agent",
|
|
41433
41562
|
name: "Pi Agent",
|
|
41434
|
-
configPath:
|
|
41563
|
+
configPath: join5(process.env.HOME || "", ".pi/agent/models.json")
|
|
41435
41564
|
},
|
|
41436
41565
|
openclaw: {
|
|
41437
41566
|
clientId: "openclaw",
|
|
41438
41567
|
name: "OpenClaw",
|
|
41439
|
-
configPath:
|
|
41568
|
+
configPath: join5(process.env.HOME || "", ".openclaw/openclaw.json")
|
|
41440
41569
|
},
|
|
41441
41570
|
"claude-code": {
|
|
41442
41571
|
clientId: "claude-code",
|
|
41443
41572
|
name: "Claude Code",
|
|
41444
|
-
configPath:
|
|
41573
|
+
configPath: join5(process.env.HOME || "", ".claude/settings.json")
|
|
41445
41574
|
}
|
|
41446
41575
|
};
|
|
41447
41576
|
var CLIENT_INTEGRATIONS = {
|
|
@@ -41492,7 +41621,7 @@ Installing routstr models in opencode.json...`);
|
|
|
41492
41621
|
}
|
|
41493
41622
|
let opencodeConfig;
|
|
41494
41623
|
try {
|
|
41495
|
-
if (
|
|
41624
|
+
if (existsSync6(configPath)) {
|
|
41496
41625
|
const content2 = await readFile4(configPath, "utf-8");
|
|
41497
41626
|
opencodeConfig = JSON.parse(content2);
|
|
41498
41627
|
} else {
|
|
@@ -41505,7 +41634,7 @@ Installing routstr models in opencode.json...`);
|
|
|
41505
41634
|
opencodeConfig.provider = {};
|
|
41506
41635
|
}
|
|
41507
41636
|
try {
|
|
41508
|
-
|
|
41637
|
+
mkdirSync4(dirname4(configPath), { recursive: true });
|
|
41509
41638
|
const response = await fetch(`http://localhost:${port}/models`);
|
|
41510
41639
|
const data = await response.json();
|
|
41511
41640
|
const models = data.output?.models || [];
|
|
@@ -41583,7 +41712,7 @@ async function main() {
|
|
|
41583
41712
|
}));
|
|
41584
41713
|
Bun.write(PID_FILE, String(process.pid));
|
|
41585
41714
|
try {
|
|
41586
|
-
if (
|
|
41715
|
+
if (existsSync7(SOCKET_PATH)) {
|
|
41587
41716
|
Bun.spawn(["rm", SOCKET_PATH]);
|
|
41588
41717
|
}
|
|
41589
41718
|
} catch {}
|