routstrd 0.4.10 → 0.4.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +55 -15
- package/SECURITY.md +0 -1
- package/SKILL.md +267 -52
- package/dist/daemon/index.js +93747 -70861
- package/dist/index.js +65958 -42561
- package/package.json +3 -2
- package/src/cli.ts +358 -23
- package/src/daemon/http/index.ts +323 -38
- package/src/daemon/http/request-body.ts +67 -0
- package/src/daemon/http/request-path.ts +39 -0
- package/src/daemon/index.ts +1 -1
- package/src/daemon/models.ts +46 -11
- package/src/daemon/wallet/auto-refill.ts +20 -8
- package/src/daemon/wallet/cleanup.ts +16 -2
- package/src/daemon/wallet/coco-client.ts +941 -115
- package/src/daemon/wallet/index.ts +168 -50
- package/src/daemon/wallet/mint-quote-recovery.ts +119 -0
- package/src/daemon/wallet/recovery-probe.ts +312 -0
- package/src/daemon/wallet/recovery-work.ts +45 -0
- package/src/daemon/wallet/testing/fake-mint.ts +347 -0
- package/src/daemon/wallet/trusted-mints.ts +87 -0
- package/src/daemon/wallet/wallet-client.ts +206 -0
- package/src/integrations/pi.ts +189 -34
- package/src/integrations/registry.ts +14 -0
- package/src/tui/usage/render.ts +161 -22
- package/src/utils/config.ts +3 -2
- package/src/utils/cooldowns.ts +134 -0
- package/src/utils/daemon-client.ts +53 -9
- package/src/utils/history.ts +9 -0
- package/src/utils/with-timeout.ts +21 -0
- package/src/TUI refactor.md +0 -113
- package/src/daemon/wallet/cocod-client.ts +0 -505
package/src/daemon/models.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
canonicalIdForModel,
|
|
3
|
+
findModelForId,
|
|
4
|
+
ModelManager,
|
|
5
|
+
ProviderManager,
|
|
6
|
+
type Model,
|
|
7
|
+
type SdkStore,
|
|
8
|
+
} from "@routstr/sdk";
|
|
2
9
|
import type { ExposedModel } from "./types";
|
|
3
10
|
import { logger } from "../utils/logger";
|
|
4
11
|
|
|
@@ -66,13 +73,16 @@ export function createModelService(
|
|
|
66
73
|
|
|
67
74
|
for (const model of models as PricedModel[]) {
|
|
68
75
|
if (!model.sats_pricing) continue;
|
|
69
|
-
|
|
76
|
+
// Group by canonical id so providers serving the same model under a
|
|
77
|
+
// mapped variant id or alias fold into one aggregate entry.
|
|
78
|
+
const canonicalId = canonicalIdForModel(model as Model);
|
|
79
|
+
const existing = bestById.get(canonicalId);
|
|
70
80
|
if (
|
|
71
81
|
!existing ||
|
|
72
82
|
(model.sats_pricing.completion ?? 0) <
|
|
73
83
|
(existing.sats_pricing?.completion ?? 0)
|
|
74
84
|
) {
|
|
75
|
-
bestById.set(
|
|
85
|
+
bestById.set(canonicalId, model);
|
|
76
86
|
}
|
|
77
87
|
}
|
|
78
88
|
}
|
|
@@ -155,11 +165,19 @@ export function createModelService(
|
|
|
155
165
|
);
|
|
156
166
|
}
|
|
157
167
|
|
|
158
|
-
|
|
159
|
-
|
|
168
|
+
// Resolve each routstr21 id against the aggregated provider models. The
|
|
169
|
+
// aggregation folds providers' mapped variant ids/aliases into one entry
|
|
170
|
+
// per canonical id, but the surviving entry is the provider's raw model,
|
|
171
|
+
// whose `.id` may be the variant (e.g. z-ai-glm-5-3-flash) — while still
|
|
172
|
+
// carrying the full metadata (context_length, architecture, reasoning).
|
|
173
|
+
// A plain id map would miss and degrade the entry to a bare stub, so
|
|
174
|
+
// resolve through the SDK's model mappings (exact native id wins, same as
|
|
175
|
+
// getModelProviders) and always expose the requested canonical id.
|
|
160
176
|
return routstr21ModelIds.map((modelId) => {
|
|
161
|
-
const model =
|
|
162
|
-
return model
|
|
177
|
+
const model = findModelForId(discoveredModels as Model[], modelId);
|
|
178
|
+
return model
|
|
179
|
+
? { ...model, id: modelId }
|
|
180
|
+
: { id: modelId, name: modelId };
|
|
163
181
|
});
|
|
164
182
|
};
|
|
165
183
|
|
|
@@ -201,18 +219,35 @@ export function createModelService(
|
|
|
201
219
|
return null;
|
|
202
220
|
}
|
|
203
221
|
|
|
204
|
-
// Get model metadata
|
|
222
|
+
// Get model metadata. Prefer a provider that serves the requested
|
|
223
|
+
// canonical id natively (nicest name/description); fall back to the
|
|
224
|
+
// cheapest provider matched through the SDK's model mappings, since it
|
|
225
|
+
// may only know the model under a mapped variant id or alias.
|
|
205
226
|
const cheapest = providers[0]!;
|
|
206
227
|
const allModels = modelManager.getAllCachedModels();
|
|
207
|
-
|
|
208
|
-
|
|
228
|
+
let modelInfo: Model | undefined;
|
|
229
|
+
for (const p of providers) {
|
|
230
|
+
const exact = (allModels[p.baseUrl] as Model[] | undefined)?.find(
|
|
231
|
+
(m) => m.id === modelId,
|
|
232
|
+
);
|
|
233
|
+
if (exact) {
|
|
234
|
+
modelInfo = exact;
|
|
235
|
+
break;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
if (!modelInfo) {
|
|
239
|
+
const firstProvider = allModels[cheapest.baseUrl];
|
|
240
|
+
modelInfo = firstProvider
|
|
241
|
+
? findModelForId(firstProvider as Model[], modelId)
|
|
242
|
+
: undefined;
|
|
243
|
+
}
|
|
209
244
|
|
|
210
245
|
if (!modelInfo) {
|
|
211
246
|
return null;
|
|
212
247
|
}
|
|
213
248
|
|
|
214
249
|
return {
|
|
215
|
-
id:
|
|
250
|
+
id: modelId,
|
|
216
251
|
name: modelInfo.name,
|
|
217
252
|
description: modelInfo.description,
|
|
218
253
|
context_length: modelInfo.context_length,
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
//
|
|
5
5
|
// Uses applesauce-wallet-connect (same approach as nwc_integration/pay_invoice.mts).
|
|
6
6
|
|
|
7
|
-
import type {
|
|
7
|
+
import type { WalletClient } from "./wallet-client";
|
|
8
8
|
import type { WalletConnect } from "applesauce-wallet-connect";
|
|
9
9
|
import { logger } from "../../utils/logger";
|
|
10
10
|
|
|
@@ -33,10 +33,19 @@ function isFatalError(message: string): boolean {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
export function startAutoRefillLoop(
|
|
36
|
-
|
|
36
|
+
walletClient: WalletClient,
|
|
37
37
|
getWallet: () => WalletConnect | undefined,
|
|
38
38
|
getConfig: () => AutoRefillConfig | undefined,
|
|
39
39
|
intervalMs: number = 5000,
|
|
40
|
+
payInvoice: (
|
|
41
|
+
invoice: string,
|
|
42
|
+
) => Promise<{ preimage?: string; fees_paid?: number }> = (invoice) => {
|
|
43
|
+
const wallet = getWallet();
|
|
44
|
+
if (!wallet?.service) {
|
|
45
|
+
return Promise.reject(new Error("NWC not connected"));
|
|
46
|
+
}
|
|
47
|
+
return wallet.payInvoice(invoice);
|
|
48
|
+
},
|
|
40
49
|
): () => void {
|
|
41
50
|
let lastRefillAt = 0;
|
|
42
51
|
let lastAttemptAt = 0; // tracks last attempt (success or failure) for backoff
|
|
@@ -79,7 +88,7 @@ export function startAutoRefillLoop(
|
|
|
79
88
|
checkInProgress = true;
|
|
80
89
|
|
|
81
90
|
try {
|
|
82
|
-
const balances = await
|
|
91
|
+
const balances = await walletClient.getBalances();
|
|
83
92
|
const totalBalance = Object.values(balances).reduce<number>(
|
|
84
93
|
(sum, b) => sum + (typeof b === "number" ? b : 0),
|
|
85
94
|
0,
|
|
@@ -94,17 +103,20 @@ export function startAutoRefillLoop(
|
|
|
94
103
|
`[auto-refill] Balance ${totalBalance} sats < threshold ${config.threshold}. Refilling ${config.amount} sats...`,
|
|
95
104
|
);
|
|
96
105
|
|
|
97
|
-
const mintUrl = await
|
|
106
|
+
const mintUrl = await walletClient.getDefaultMint();
|
|
98
107
|
if (!mintUrl) {
|
|
99
108
|
logger.error("[auto-refill] No default mint configured");
|
|
100
109
|
return;
|
|
101
110
|
}
|
|
102
111
|
|
|
103
|
-
// Step 1: Create a BOLT-11 invoice via
|
|
112
|
+
// Step 1: Create a BOLT-11 invoice via the wallet to fund the Cashu wallet
|
|
104
113
|
logger.log(
|
|
105
114
|
`[auto-refill] Creating BOLT-11 invoice for ${config.amount} sats via ${mintUrl}...`,
|
|
106
115
|
);
|
|
107
|
-
const { invoice } = await
|
|
116
|
+
const { invoice } = await walletClient.receiveBolt11(
|
|
117
|
+
config.amount,
|
|
118
|
+
mintUrl,
|
|
119
|
+
);
|
|
108
120
|
|
|
109
121
|
// Step 2: Pay the invoice via NWC (applesauce)
|
|
110
122
|
logger.log(`[auto-refill] Paying invoice via NWC...`);
|
|
@@ -113,10 +125,10 @@ export function startAutoRefillLoop(
|
|
|
113
125
|
logger.log("[auto-refill] Wallet disconnected during refill check");
|
|
114
126
|
return;
|
|
115
127
|
}
|
|
116
|
-
const payment = await
|
|
128
|
+
const payment = await payInvoice(invoice);
|
|
117
129
|
|
|
118
130
|
// Step 3: The Cashu mint should automatically detect the paid invoice
|
|
119
|
-
// and issue tokens. We don't need to explicitly mint here;
|
|
131
|
+
// and issue tokens. We don't need to explicitly mint here; the wallet
|
|
120
132
|
// handles this on its end when the mint sees the payment.
|
|
121
133
|
const preimage = payment.preimage;
|
|
122
134
|
if (preimage) {
|
|
@@ -64,8 +64,8 @@ export interface CleanupSelection<
|
|
|
64
64
|
* can have happened before expiry while the daemon was down, leaving no
|
|
65
65
|
* local observation. Callers that fail quotes automatically at startup must
|
|
66
66
|
* therefore confirm UNPAID with the mint first (see
|
|
67
|
-
*
|
|
68
|
-
*
|
|
67
|
+
* failExpiredMintQuoteIfUnpaid in coco-client.ts). Explicit cleanup follows
|
|
68
|
+
* the same rule unless the operator opts into unsafe `--force` behaviour.
|
|
69
69
|
* - Pending sends are reclaimed (rolled back) only when they are older than
|
|
70
70
|
* `minAgeMs`, so we never roll back a token that a receiver might still
|
|
71
71
|
* legitimately claim.
|
|
@@ -102,3 +102,17 @@ export function selectCleanupOperations<
|
|
|
102
102
|
|
|
103
103
|
return { mintsToFail, sendsToReclaim, meltsToCancel };
|
|
104
104
|
}
|
|
105
|
+
|
|
106
|
+
/** Keep a local-only dry-run preview distinct from mint-confirmed outcomes. */
|
|
107
|
+
export function summarizeMintCleanup(input: {
|
|
108
|
+
dryRun: boolean;
|
|
109
|
+
candidates: number;
|
|
110
|
+
failed: number;
|
|
111
|
+
leftForRecovery: number;
|
|
112
|
+
}) {
|
|
113
|
+
return {
|
|
114
|
+
mintQuoteCandidates: input.candidates,
|
|
115
|
+
failedMintQuotes: input.dryRun ? 0 : input.failed,
|
|
116
|
+
leftForRecovery: input.dryRun ? 0 : input.leftForRecovery,
|
|
117
|
+
};
|
|
118
|
+
}
|