routstrd 0.2.13 → 0.2.15
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 +41 -20
- package/dist/daemon/index.js +20506 -13853
- package/dist/index.js +3077 -3239
- package/package.json +1 -1
- package/src/cli.ts +19 -15
- package/src/tui/usage/data.ts +4 -15
- package/src/utils/clients.ts +19 -61
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -7,7 +7,6 @@ import {
|
|
|
7
7
|
isDaemonRunning,
|
|
8
8
|
loadConfig,
|
|
9
9
|
getDaemonBaseUrl,
|
|
10
|
-
getNpubSuffix,
|
|
11
10
|
getUserNpub,
|
|
12
11
|
} from "./utils/daemon-client";
|
|
13
12
|
import {
|
|
@@ -833,14 +832,19 @@ clientsCmd
|
|
|
833
832
|
},
|
|
834
833
|
);
|
|
835
834
|
|
|
836
|
-
// Npubs - manage admin
|
|
835
|
+
// Npubs - manage npubs (admin and user roles)
|
|
837
836
|
const npubsCmd = program
|
|
838
837
|
.command("npubs")
|
|
839
|
-
.description("Manage
|
|
838
|
+
.description("Manage npubs on the daemon (admin or user roles)");
|
|
839
|
+
|
|
840
|
+
type NpubEntry = {
|
|
841
|
+
npub: string;
|
|
842
|
+
role: string;
|
|
843
|
+
};
|
|
840
844
|
|
|
841
845
|
npubsCmd
|
|
842
846
|
.command("list")
|
|
843
|
-
.description("List configured
|
|
847
|
+
.description("List configured npubs with their roles")
|
|
844
848
|
.action(async () => {
|
|
845
849
|
await ensureDaemonRunning();
|
|
846
850
|
const config = await loadConfig();
|
|
@@ -851,20 +855,20 @@ npubsCmd
|
|
|
851
855
|
process.exit(1);
|
|
852
856
|
}
|
|
853
857
|
// Handle both wrapped { output: { npubs } } and direct { npubs } response formats
|
|
854
|
-
const data = (result.output as { npubs?:
|
|
858
|
+
const data = (result.output as { npubs?: NpubEntry[] } | undefined)?.npubs
|
|
855
859
|
? result.output
|
|
856
860
|
: result;
|
|
857
|
-
const npubs = (data as { npubs?:
|
|
861
|
+
const npubs = (data as { npubs?: NpubEntry[] } | undefined)?.npubs ?? [];
|
|
858
862
|
if (npubs.length === 0) {
|
|
859
863
|
console.log("No admin npubs configured. Run 'routstrd npubs register' to register yourself as the first admin.");
|
|
860
864
|
return;
|
|
861
865
|
}
|
|
862
|
-
console.log(`
|
|
866
|
+
console.log(`Npubs (${npubs.length}):`);
|
|
863
867
|
let found = false;
|
|
864
|
-
for (const
|
|
865
|
-
const marker = npub === userNpub ? " → you" : "";
|
|
866
|
-
if (npub === userNpub) found = true;
|
|
867
|
-
console.log(`- ${npub}${marker}`);
|
|
868
|
+
for (const entry of npubs) {
|
|
869
|
+
const marker = entry.npub === userNpub ? " → you" : "";
|
|
870
|
+
if (entry.npub === userNpub) found = true;
|
|
871
|
+
console.log(`- ${entry.npub} [${entry.role}]${marker}`);
|
|
868
872
|
}
|
|
869
873
|
if (userNpub && !found) {
|
|
870
874
|
console.log("");
|
|
@@ -893,10 +897,10 @@ npubsCmd
|
|
|
893
897
|
console.log(result.error);
|
|
894
898
|
process.exit(1);
|
|
895
899
|
}
|
|
896
|
-
const data = (result.output as { npubs?:
|
|
900
|
+
const data = (result.output as { npubs?: NpubEntry[] } | undefined)?.npubs
|
|
897
901
|
? result.output
|
|
898
902
|
: result;
|
|
899
|
-
const npubs = (data as { npubs?:
|
|
903
|
+
const npubs = (data as { npubs?: NpubEntry[] } | undefined)?.npubs ?? [];
|
|
900
904
|
if (npubs.length > 0) {
|
|
901
905
|
console.log(`Admin npubs already configured (${npubs.length}). Ask your admin to add your npub. \n Your npub: ${userNpub}`);
|
|
902
906
|
return;
|
|
@@ -926,7 +930,7 @@ npubsCmd
|
|
|
926
930
|
|
|
927
931
|
npubsCmd
|
|
928
932
|
.command("add <npub>")
|
|
929
|
-
.description("Add
|
|
933
|
+
.description("Add a npub (hex pubkey or npub1...). First becomes admin, subsequent become user.")
|
|
930
934
|
.action(async (npubArg: string) => {
|
|
931
935
|
await ensureDaemonRunning();
|
|
932
936
|
const normalized = normalizeNostrPubkey(npubArg);
|
|
@@ -947,7 +951,7 @@ npubsCmd
|
|
|
947
951
|
| undefined;
|
|
948
952
|
if (output?.npub) {
|
|
949
953
|
console.log(
|
|
950
|
-
`${output.added ? "Added" : "Already configured"}
|
|
954
|
+
`${output.added ? "Added" : "Already configured"} npub: ${output.npub}`,
|
|
951
955
|
);
|
|
952
956
|
}
|
|
953
957
|
});
|
package/src/tui/usage/data.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { UsageTrackingEntry } from "../../daemon/types.ts";
|
|
2
|
-
import { callDaemon,
|
|
2
|
+
import { callDaemon, isDaemonRunning } from "../../utils/daemon-client.ts";
|
|
3
3
|
import type { ClientStats, DayStats, ModelStats, ProviderStats, UsageStats } from "./types.ts";
|
|
4
4
|
|
|
5
5
|
export interface BalanceKey {
|
|
@@ -82,21 +82,10 @@ export async function fetchUsage(limit = 10000): Promise<UsageStats | null> {
|
|
|
82
82
|
const result = await callDaemon(`/usage?limit=${limit}`);
|
|
83
83
|
if (result.error) return null;
|
|
84
84
|
|
|
85
|
-
// The
|
|
86
|
-
//
|
|
87
|
-
// Fetch the full daemon result, but only display/aggregate entries for our clients.
|
|
85
|
+
// The auth proxy filters usage to the authenticated npub's clients and
|
|
86
|
+
// returns client IDs without the owner suffix.
|
|
88
87
|
const entries = result.output as UsageTrackingEntry[] | undefined;
|
|
89
|
-
const
|
|
90
|
-
const suffix = getNpubSuffix(await loadConfig());
|
|
91
|
-
const suffixStr = suffix ? `-${suffix}` : null;
|
|
92
|
-
const visibleEntries = suffixStr
|
|
93
|
-
? entriesArray
|
|
94
|
-
.filter((entry) => entry.client?.endsWith(suffixStr))
|
|
95
|
-
.map((entry) => ({
|
|
96
|
-
...entry,
|
|
97
|
-
client: entry.client?.slice(0, -suffixStr.length),
|
|
98
|
-
}))
|
|
99
|
-
: entriesArray;
|
|
88
|
+
const visibleEntries = Array.isArray(entries) ? entries : [];
|
|
100
89
|
|
|
101
90
|
// Calculate totals from visible entries
|
|
102
91
|
const totals = visibleEntries.reduce(
|
package/src/utils/clients.ts
CHANGED
|
@@ -4,49 +4,16 @@ import {
|
|
|
4
4
|
getDaemonBaseUrl,
|
|
5
5
|
ensureDaemonRunning,
|
|
6
6
|
} from "./daemon-client";
|
|
7
|
-
import {
|
|
8
|
-
parseSecretKey,
|
|
9
|
-
npubFromSecretKey,
|
|
10
|
-
} from "./nip98";
|
|
11
|
-
import { type RoutstrdConfig } from "./config";
|
|
12
7
|
import { logger } from "./logger";
|
|
13
8
|
import { CLIENT_INTEGRATIONS, CLIENT_CONFIGS } from "../integrations/registry";
|
|
14
9
|
|
|
15
|
-
export function getNpubSuffix(config: RoutstrdConfig): string | null {
|
|
16
|
-
if (!config.daemonUrl || !config.nsec) return null;
|
|
17
|
-
try {
|
|
18
|
-
const secretKey = parseSecretKey(config.nsec);
|
|
19
|
-
const npub = npubFromSecretKey(secretKey);
|
|
20
|
-
return npub.slice(-7);
|
|
21
|
-
} catch {
|
|
22
|
-
return null;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Add suffix to a client ID.
|
|
28
|
-
*/
|
|
29
|
-
export function addSuffixToId(id: string, suffix: string): string {
|
|
30
|
-
return `${id}-${suffix}`;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Remove suffix from a client ID if present.
|
|
35
|
-
*/
|
|
36
|
-
export function removeSuffixFromId(id: string, suffix: string): string {
|
|
37
|
-
const suffixStr = `-${suffix}`;
|
|
38
|
-
if (id.endsWith(suffixStr)) {
|
|
39
|
-
return id.slice(0, -suffixStr.length);
|
|
40
|
-
}
|
|
41
|
-
return id;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
10
|
export interface ClientEntry {
|
|
45
11
|
clientId: string;
|
|
46
12
|
name: string;
|
|
47
13
|
apiKey: string;
|
|
48
14
|
createdAt: number;
|
|
49
15
|
lastUsed?: number | null;
|
|
16
|
+
ownerNpub?: string;
|
|
50
17
|
}
|
|
51
18
|
|
|
52
19
|
export interface DaemonClient {
|
|
@@ -55,6 +22,7 @@ export interface DaemonClient {
|
|
|
55
22
|
apiKey: string;
|
|
56
23
|
createdAt: number;
|
|
57
24
|
lastUsed?: number | null;
|
|
25
|
+
ownerNpub?: string;
|
|
58
26
|
}
|
|
59
27
|
|
|
60
28
|
/**
|
|
@@ -71,12 +39,14 @@ export function getClientsFromStore(store: { getState(): any }): ClientEntry[] {
|
|
|
71
39
|
apiKey: string;
|
|
72
40
|
createdAt: number;
|
|
73
41
|
lastUsed?: number | null;
|
|
42
|
+
ownerNpub?: string;
|
|
74
43
|
}) => ({
|
|
75
44
|
clientId: c.clientId,
|
|
76
45
|
name: c.name,
|
|
77
46
|
apiKey: c.apiKey,
|
|
78
47
|
createdAt: c.createdAt,
|
|
79
48
|
lastUsed: c.lastUsed,
|
|
49
|
+
ownerNpub: c.ownerNpub,
|
|
80
50
|
}),
|
|
81
51
|
);
|
|
82
52
|
}
|
|
@@ -86,7 +56,6 @@ export function getClientsFromStore(store: { getState(): any }): ClientEntry[] {
|
|
|
86
56
|
* Use this when running remotely (CLI in remote mode).
|
|
87
57
|
*/
|
|
88
58
|
export async function getClientsList(): Promise<ClientEntry[]> {
|
|
89
|
-
const config = await loadConfig();
|
|
90
59
|
const result = await callDaemon("/clients");
|
|
91
60
|
const clients = (
|
|
92
61
|
result.output as
|
|
@@ -97,6 +66,7 @@ export async function getClientsList(): Promise<ClientEntry[]> {
|
|
|
97
66
|
apiKey: string;
|
|
98
67
|
createdAt: number;
|
|
99
68
|
lastUsed?: number | null;
|
|
69
|
+
ownerNpub?: string;
|
|
100
70
|
}>;
|
|
101
71
|
}
|
|
102
72
|
| undefined
|
|
@@ -106,17 +76,14 @@ export async function getClientsList(): Promise<ClientEntry[]> {
|
|
|
106
76
|
return [];
|
|
107
77
|
}
|
|
108
78
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
.
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
createdAt: c.createdAt,
|
|
118
|
-
lastUsed: c.lastUsed,
|
|
119
|
-
}));
|
|
79
|
+
return clients.map((c) => ({
|
|
80
|
+
clientId: c.id,
|
|
81
|
+
name: c.name,
|
|
82
|
+
apiKey: c.apiKey,
|
|
83
|
+
createdAt: c.createdAt,
|
|
84
|
+
lastUsed: c.lastUsed,
|
|
85
|
+
ownerNpub: c.ownerNpub,
|
|
86
|
+
}));
|
|
120
87
|
}
|
|
121
88
|
|
|
122
89
|
export async function addDaemonClient(
|
|
@@ -135,18 +102,14 @@ export async function addDaemonClient(
|
|
|
135
102
|
apiKey: existing.apiKey,
|
|
136
103
|
createdAt: existing.createdAt,
|
|
137
104
|
lastUsed: existing.lastUsed,
|
|
105
|
+
ownerNpub: existing.ownerNpub,
|
|
138
106
|
};
|
|
139
107
|
return { client, created: false };
|
|
140
108
|
}
|
|
141
109
|
|
|
142
|
-
const config = await loadConfig();
|
|
143
|
-
const suffix = getNpubSuffix(config);
|
|
144
|
-
const clientId = suffix ? addSuffixToId(derivedId, suffix) : derivedId;
|
|
145
|
-
|
|
146
|
-
|
|
147
110
|
const result = await callDaemon("/clients/add", {
|
|
148
111
|
method: "POST",
|
|
149
|
-
body: { name, id:
|
|
112
|
+
body: { name, id: derivedId },
|
|
150
113
|
});
|
|
151
114
|
|
|
152
115
|
|
|
@@ -196,13 +159,9 @@ export async function listClientsAction(): Promise<void> {
|
|
|
196
159
|
export async function deleteClientAction(id: string): Promise<void> {
|
|
197
160
|
await ensureDaemonRunning();
|
|
198
161
|
|
|
199
|
-
const config = await loadConfig();
|
|
200
|
-
const suffix = getNpubSuffix(config);
|
|
201
|
-
const resolvedId = suffix ? addSuffixToId(id, suffix) : id;
|
|
202
|
-
|
|
203
162
|
const result = await callDaemon("/clients/delete", {
|
|
204
163
|
method: "POST",
|
|
205
|
-
body: { id
|
|
164
|
+
body: { id },
|
|
206
165
|
});
|
|
207
166
|
|
|
208
167
|
if (result.error) {
|
|
@@ -233,7 +192,6 @@ export interface AddClientOptions {
|
|
|
233
192
|
export async function addClientAction(options: AddClientOptions): Promise<void> {
|
|
234
193
|
await ensureDaemonRunning();
|
|
235
194
|
const config = await loadConfig();
|
|
236
|
-
const suffix = getNpubSuffix(config);
|
|
237
195
|
|
|
238
196
|
const integrationKeys: string[] = [];
|
|
239
197
|
if (options.opencode) integrationKeys.push("opencode");
|
|
@@ -259,7 +217,7 @@ export async function addClientAction(options: AddClientOptions): Promise<void>
|
|
|
259
217
|
await integrationFn(config, client.apiKey, integrationConfig);
|
|
260
218
|
|
|
261
219
|
console.log(`\n ${integrationConfig.name}:`);
|
|
262
|
-
console.log(` Client ID: ${
|
|
220
|
+
console.log(` Client ID: ${client.id}`);
|
|
263
221
|
console.log(` API Key: ${client.apiKey}`);
|
|
264
222
|
} catch (error) {
|
|
265
223
|
logger.error(
|
|
@@ -286,7 +244,7 @@ export async function addClientAction(options: AddClientOptions): Promise<void>
|
|
|
286
244
|
|
|
287
245
|
if (!created) {
|
|
288
246
|
console.log(`Client '${options.name}' already exists.`);
|
|
289
|
-
console.log(`\n ID: ${
|
|
247
|
+
console.log(`\n ID: ${client.id}`);
|
|
290
248
|
console.log(` Name: ${client.name}`);
|
|
291
249
|
console.log(` API Key: ${client.apiKey}`);
|
|
292
250
|
return;
|
|
@@ -295,7 +253,7 @@ export async function addClientAction(options: AddClientOptions): Promise<void>
|
|
|
295
253
|
if (message) {
|
|
296
254
|
console.log(message);
|
|
297
255
|
}
|
|
298
|
-
console.log(`\n ID: ${
|
|
256
|
+
console.log(`\n ID: ${client.id}`);
|
|
299
257
|
console.log(` Name: ${client.name}`);
|
|
300
258
|
console.log(` API Key: ${client.apiKey}`);
|
|
301
259
|
console.log(`\n Access Routstr at: ${getDaemonBaseUrl(config)}/v1`);
|