mirror of
https://github.com/Routstr/routstrd.git
synced 2026-08-09 03:44:38 +00:00
Merge branch 'main' of https://github.com/Routstr/routstrd into feat/hermes-integration
This commit is contained in:
@@ -4,10 +4,11 @@
|
||||
"": {
|
||||
"dependencies": {
|
||||
"@cashu/cashu-ts": "^3.1.1",
|
||||
"@routstr/sdk": "^0.3.0",
|
||||
"@routstr/sdk": "^0.3.2",
|
||||
"applesauce-core": "^5.1.0",
|
||||
"applesauce-relay": "^5.1.0",
|
||||
"commander": "^14.0.2",
|
||||
"nostr-tools": "^2.12.0",
|
||||
"qrcode": "^1.5.4",
|
||||
"rxjs": "^7.8.1",
|
||||
"zustand": "^5.0.5",
|
||||
@@ -24,19 +25,19 @@
|
||||
"packages": {
|
||||
"@cashu/cashu-ts": ["@cashu/cashu-ts@3.6.4", "", { "dependencies": { "@noble/curves": "^2.0.1", "@noble/hashes": "^2.0.1", "@scure/base": "^2.0.0", "@scure/bip32": "^2.0.1" } }, "sha512-a6Asqk+wPEk9a6BQmdLMeegngj0KHKicABUEtr1cUMLTAUYHVHfdfEuHDlU+bNUqXXyaTssG7yo/ZhfHrgbSkQ=="],
|
||||
|
||||
"@noble/ciphers": ["@noble/ciphers@0.5.3", "", {}, "sha512-B0+6IIHiqEs3BPMT0hcRmHvEj2QHOLu+uwt+tqDDeVd0oyVzh7BPrDcPjRnV1PV/5LaknXJJQvOuRGR0zQJz+w=="],
|
||||
"@noble/ciphers": ["@noble/ciphers@2.1.1", "", {}, "sha512-bysYuiVfhxNJuldNXlFEitTVdNnYUc+XNJZd7Qm2a5j1vZHgY+fazadNFWFaMK/2vye0JVlxV3gHmC0WDfAOQw=="],
|
||||
|
||||
"@noble/curves": ["@noble/curves@2.2.0", "", { "dependencies": { "@noble/hashes": "2.2.0" } }, "sha512-T/BoHgFXirb0ENSPBquzX0rcjXeM6Lo892a2jlYJkqk83LqZx0l1Of7DzlKJ6jkpvMrkHSnAcgb5JegL8SeIkQ=="],
|
||||
"@noble/curves": ["@noble/curves@2.0.1", "", { "dependencies": { "@noble/hashes": "2.0.1" } }, "sha512-vs1Az2OOTBiP4q0pwjW5aF0xp9n4MxVrmkFBxc6EKZc6ddYx5gaZiAsZoq0uRRXWbi3AT/sBqn05eRPtn1JCPw=="],
|
||||
|
||||
"@noble/hashes": ["@noble/hashes@2.2.0", "", {}, "sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg=="],
|
||||
"@noble/hashes": ["@noble/hashes@2.0.1", "", {}, "sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw=="],
|
||||
|
||||
"@routstr/sdk": ["@routstr/sdk@0.3.1", "", { "dependencies": { "@cashu/cashu-ts": "^3.1.1", "applesauce-core": "^5.1.0", "applesauce-relay": "^5.1.0", "rxjs": "^7.8.1", "zustand": "^5.0.5" }, "optionalDependencies": { "better-sqlite3": "^11.7.2" }, "peerDependencies": { "typescript": ">=5.0.0" } }, "sha512-sd4mOOPevoLUKmr3/GHVdcwLQi8PnddKkvGuQlJFTaKqQBoJYWFnc7+3EPZZmMl0wq03rbk92YV0ZMP0/5Zd7g=="],
|
||||
"@routstr/sdk": ["@routstr/sdk@0.3.2", "", { "dependencies": { "@cashu/cashu-ts": "^3.1.1", "applesauce-core": "^5.1.0", "applesauce-relay": "^5.1.0", "rxjs": "^7.8.1", "zustand": "^5.0.5" }, "optionalDependencies": { "better-sqlite3": "^11.7.2" }, "peerDependencies": { "typescript": ">=5.0.0" } }, "sha512-sNUa48UwNcoFRidfVLOFQ+VGX723G1In5L1nb2w1C45GeiofSTUj9v+eBJCEU12WpBafFahHYaqQaaTzZ8LWKw=="],
|
||||
|
||||
"@scure/base": ["@scure/base@2.2.0", "", {}, "sha512-b8XEupJibegiXV+tDUseI8oLQc8ei3d/4Jkb2RpbHh3MfE054ov3uIz2dhFkB3FI8iwYkEh0gGCApkrYggkPNg=="],
|
||||
"@scure/base": ["@scure/base@2.0.0", "", {}, "sha512-3E1kpuZginKkek01ovG8krQ0Z44E3DHPjc5S2rjJw9lZn3KSQOs8S7wqikF/AH7iRanHypj85uGyxk0XAyC37w=="],
|
||||
|
||||
"@scure/bip32": ["@scure/bip32@2.2.0", "", { "dependencies": { "@noble/curves": "2.2.0", "@noble/hashes": "2.2.0", "@scure/base": "2.2.0" } }, "sha512-zFr7t2F+a9+5tB7QbarF2HQNYrgjCNaoLAupZdKkrFMYMozJf5zqH2WJCQibMzm1qQ0QogrxVGO3qXfQDYMaQg=="],
|
||||
"@scure/bip32": ["@scure/bip32@2.0.1", "", { "dependencies": { "@noble/curves": "2.0.1", "@noble/hashes": "2.0.1", "@scure/base": "2.0.0" } }, "sha512-4Md1NI5BzoVP+bhyJaY3K6yMesEFzNS1sE/cP+9nuvE7p/b0kx9XbpDHHFl8dHtufcbdHRUUQdRqLIPHN/s7yA=="],
|
||||
|
||||
"@scure/bip39": ["@scure/bip39@1.2.1", "", { "dependencies": { "@noble/hashes": "~1.3.0", "@scure/base": "~1.1.0" } }, "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg=="],
|
||||
"@scure/bip39": ["@scure/bip39@2.0.1", "", { "dependencies": { "@noble/hashes": "2.0.1", "@scure/base": "2.0.0" } }, "sha512-PsxdFj/d2AcJcZDX1FXN3dDgitDDTmwf78rKZq1a6c1P1Nan1X/Sxc7667zU3U+AN60g7SxxP0YCVw2H/hBycg=="],
|
||||
|
||||
"@types/bun": ["@types/bun@1.3.13", "", { "dependencies": { "bun-types": "1.3.13" } }, "sha512-9fqXWk5YIHGGnUau9TEi+qdlTYDAnOj+xLCmSTwXfAIqXr2x4tytJb43E9uCvt09zJURKXwAtkoH4nLQfzeTXw=="],
|
||||
|
||||
@@ -126,13 +127,13 @@
|
||||
|
||||
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||
|
||||
"nanoid": ["nanoid@5.1.9", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-ZUvP7KeBLe3OZ1ypw6dI/TzYJuvHP77IM4Ry73waSQTLn8/g8rpdjfyVAh7t1/+FjBtG4lCP42MEbDxOsRpBMw=="],
|
||||
"nanoid": ["nanoid@5.1.11", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-v+KEsUv2ps74PaSKv0gHTxTCgMXOIfBEbaqa6w6ISIGC7ZsvHN4N9oJ8d4cmf0n5oTzQz2SLmThbQWhjd/8eKg=="],
|
||||
|
||||
"napi-build-utils": ["napi-build-utils@2.0.0", "", {}, "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA=="],
|
||||
|
||||
"node-abi": ["node-abi@3.89.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-6u9UwL0HlAl21+agMN3YAMXcKByMqwGx+pq+P76vii5f7hTPtKDp08/H9py6DY+cfDw7kQNTGEj/rly3IgbNQA=="],
|
||||
"node-abi": ["node-abi@3.90.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-pZNQT7UnYlMwMBy5N1lV5X/YLTbZM5ncytN3xL7CHEzhDN8uVe0u55yaPUJICIJjaCW8NrM5BFdqr7HLweStNA=="],
|
||||
|
||||
"nostr-tools": ["nostr-tools@2.19.4", "", { "dependencies": { "@noble/ciphers": "^0.5.1", "@noble/curves": "1.2.0", "@noble/hashes": "1.3.1", "@scure/base": "1.1.1", "@scure/bip32": "1.3.1", "@scure/bip39": "1.2.1", "nostr-wasm": "0.1.0" }, "peerDependencies": { "typescript": ">=5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-qVLfoTpZegNYRJo5j+Oi6RPu0AwLP6jcvzcB3ySMnIT5DrAGNXfs5HNBspB/2HiGfH3GY+v6yXkTtcKSBQZwSg=="],
|
||||
"nostr-tools": ["nostr-tools@2.23.3", "", { "dependencies": { "@noble/ciphers": "2.1.1", "@noble/curves": "2.0.1", "@noble/hashes": "2.0.1", "@scure/base": "2.0.0", "@scure/bip32": "2.0.1", "@scure/bip39": "2.0.1", "nostr-wasm": "0.1.0" }, "peerDependencies": { "typescript": ">=5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-AALyt9k8xPdF4UV2mlLJ2mgCn4kpTB0DZ8t2r6wjdUh6anfx2cTVBsHUlo9U0EY/cKC5wcNyiMAmRJV5OVEalA=="],
|
||||
|
||||
"nostr-wasm": ["nostr-wasm@0.1.0", "", {}, "sha512-78BTryCLcLYv96ONU8Ws3Q1JzjlAt+43pWQhIl86xZmWeegYCNLPml7yQ+gG3vR6V5h4XGj+TxO+SS5dsThQIA=="],
|
||||
|
||||
@@ -210,22 +211,42 @@
|
||||
|
||||
"zustand": ["zustand@5.0.12", "", { "peerDependencies": { "@types/react": ">=18.0.0", "immer": ">=9.0.6", "react": ">=18.0.0", "use-sync-external-store": ">=1.2.0" }, "optionalPeers": ["@types/react", "immer", "react", "use-sync-external-store"] }, "sha512-i77ae3aZq4dhMlRhJVCYgMLKuSiZAaUPAct2AksxQ+gOtimhGMdXljRT21P5BNpeT4kXlLIckvkPM029OljD7g=="],
|
||||
|
||||
"@scure/bip39/@noble/hashes": ["@noble/hashes@1.3.1", "", {}, "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA=="],
|
||||
|
||||
"@scure/bip39/@scure/base": ["@scure/base@1.1.1", "", {}, "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA=="],
|
||||
"applesauce-core/nostr-tools": ["nostr-tools@2.19.4", "", { "dependencies": { "@noble/ciphers": "^0.5.1", "@noble/curves": "1.2.0", "@noble/hashes": "1.3.1", "@scure/base": "1.1.1", "@scure/bip32": "1.3.1", "@scure/bip39": "1.2.1", "nostr-wasm": "0.1.0" }, "peerDependencies": { "typescript": ">=5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-qVLfoTpZegNYRJo5j+Oi6RPu0AwLP6jcvzcB3ySMnIT5DrAGNXfs5HNBspB/2HiGfH3GY+v6yXkTtcKSBQZwSg=="],
|
||||
|
||||
"applesauce-relay/@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="],
|
||||
|
||||
"nostr-tools/@noble/curves": ["@noble/curves@1.2.0", "", { "dependencies": { "@noble/hashes": "1.3.2" } }, "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw=="],
|
||||
"applesauce-relay/nostr-tools": ["nostr-tools@2.19.4", "", { "dependencies": { "@noble/ciphers": "^0.5.1", "@noble/curves": "1.2.0", "@noble/hashes": "1.3.1", "@scure/base": "1.1.1", "@scure/bip32": "1.3.1", "@scure/bip39": "1.2.1", "nostr-wasm": "0.1.0" }, "peerDependencies": { "typescript": ">=5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-qVLfoTpZegNYRJo5j+Oi6RPu0AwLP6jcvzcB3ySMnIT5DrAGNXfs5HNBspB/2HiGfH3GY+v6yXkTtcKSBQZwSg=="],
|
||||
|
||||
"nostr-tools/@noble/hashes": ["@noble/hashes@1.3.1", "", {}, "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA=="],
|
||||
"applesauce-core/nostr-tools/@noble/ciphers": ["@noble/ciphers@0.5.3", "", {}, "sha512-B0+6IIHiqEs3BPMT0hcRmHvEj2QHOLu+uwt+tqDDeVd0oyVzh7BPrDcPjRnV1PV/5LaknXJJQvOuRGR0zQJz+w=="],
|
||||
|
||||
"nostr-tools/@scure/base": ["@scure/base@1.1.1", "", {}, "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA=="],
|
||||
"applesauce-core/nostr-tools/@noble/curves": ["@noble/curves@1.2.0", "", { "dependencies": { "@noble/hashes": "1.3.2" } }, "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw=="],
|
||||
|
||||
"nostr-tools/@scure/bip32": ["@scure/bip32@1.3.1", "", { "dependencies": { "@noble/curves": "~1.1.0", "@noble/hashes": "~1.3.1", "@scure/base": "~1.1.0" } }, "sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A=="],
|
||||
"applesauce-core/nostr-tools/@noble/hashes": ["@noble/hashes@1.3.1", "", {}, "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA=="],
|
||||
|
||||
"nostr-tools/@noble/curves/@noble/hashes": ["@noble/hashes@1.3.2", "", {}, "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ=="],
|
||||
"applesauce-core/nostr-tools/@scure/base": ["@scure/base@1.1.1", "", {}, "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA=="],
|
||||
|
||||
"nostr-tools/@scure/bip32/@noble/curves": ["@noble/curves@1.1.0", "", { "dependencies": { "@noble/hashes": "1.3.1" } }, "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA=="],
|
||||
"applesauce-core/nostr-tools/@scure/bip32": ["@scure/bip32@1.3.1", "", { "dependencies": { "@noble/curves": "~1.1.0", "@noble/hashes": "~1.3.1", "@scure/base": "~1.1.0" } }, "sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A=="],
|
||||
|
||||
"applesauce-core/nostr-tools/@scure/bip39": ["@scure/bip39@1.2.1", "", { "dependencies": { "@noble/hashes": "~1.3.0", "@scure/base": "~1.1.0" } }, "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg=="],
|
||||
|
||||
"applesauce-relay/nostr-tools/@noble/ciphers": ["@noble/ciphers@0.5.3", "", {}, "sha512-B0+6IIHiqEs3BPMT0hcRmHvEj2QHOLu+uwt+tqDDeVd0oyVzh7BPrDcPjRnV1PV/5LaknXJJQvOuRGR0zQJz+w=="],
|
||||
|
||||
"applesauce-relay/nostr-tools/@noble/curves": ["@noble/curves@1.2.0", "", { "dependencies": { "@noble/hashes": "1.3.2" } }, "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw=="],
|
||||
|
||||
"applesauce-relay/nostr-tools/@noble/hashes": ["@noble/hashes@1.3.1", "", {}, "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA=="],
|
||||
|
||||
"applesauce-relay/nostr-tools/@scure/base": ["@scure/base@1.1.1", "", {}, "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA=="],
|
||||
|
||||
"applesauce-relay/nostr-tools/@scure/bip32": ["@scure/bip32@1.3.1", "", { "dependencies": { "@noble/curves": "~1.1.0", "@noble/hashes": "~1.3.1", "@scure/base": "~1.1.0" } }, "sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A=="],
|
||||
|
||||
"applesauce-relay/nostr-tools/@scure/bip39": ["@scure/bip39@1.2.1", "", { "dependencies": { "@noble/hashes": "~1.3.0", "@scure/base": "~1.1.0" } }, "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg=="],
|
||||
|
||||
"applesauce-core/nostr-tools/@noble/curves/@noble/hashes": ["@noble/hashes@1.3.2", "", {}, "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ=="],
|
||||
|
||||
"applesauce-core/nostr-tools/@scure/bip32/@noble/curves": ["@noble/curves@1.1.0", "", { "dependencies": { "@noble/hashes": "1.3.1" } }, "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA=="],
|
||||
|
||||
"applesauce-relay/nostr-tools/@noble/curves/@noble/hashes": ["@noble/hashes@1.3.2", "", {}, "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ=="],
|
||||
|
||||
"applesauce-relay/nostr-tools/@scure/bip32/@noble/curves": ["@noble/curves@1.1.0", "", { "dependencies": { "@noble/hashes": "1.3.1" } }, "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA=="],
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "routstrd",
|
||||
"version": "0.2.12",
|
||||
"version": "0.2.15",
|
||||
"module": "src/index.ts",
|
||||
"type": "module",
|
||||
"private": false,
|
||||
@@ -24,7 +24,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@cashu/cashu-ts": "^3.1.1",
|
||||
"@routstr/sdk": "^0.3.0",
|
||||
"@routstr/sdk": "^0.3.2",
|
||||
"applesauce-core": "^5.1.0",
|
||||
"applesauce-relay": "^5.1.0",
|
||||
"commander": "^14.0.2",
|
||||
|
||||
+63
-19
@@ -7,7 +7,6 @@ import {
|
||||
isDaemonRunning,
|
||||
loadConfig,
|
||||
getDaemonBaseUrl,
|
||||
getNpubSuffix,
|
||||
getUserNpub,
|
||||
} from "./utils/daemon-client";
|
||||
import {
|
||||
@@ -833,14 +832,19 @@ clientsCmd
|
||||
},
|
||||
);
|
||||
|
||||
// Npubs - manage admin npubs
|
||||
// Npubs - manage npubs (admin and user roles)
|
||||
const npubsCmd = program
|
||||
.command("npubs")
|
||||
.description("Manage admin npubs on the daemon");
|
||||
.description("Manage npubs on the daemon (admin or user roles)");
|
||||
|
||||
type NpubEntry = {
|
||||
npub: string;
|
||||
role: string;
|
||||
};
|
||||
|
||||
npubsCmd
|
||||
.command("list")
|
||||
.description("List configured admin npubs")
|
||||
.description("List configured npubs with their roles")
|
||||
.action(async () => {
|
||||
await ensureDaemonRunning();
|
||||
const config = await loadConfig();
|
||||
@@ -851,20 +855,20 @@ npubsCmd
|
||||
process.exit(1);
|
||||
}
|
||||
// Handle both wrapped { output: { npubs } } and direct { npubs } response formats
|
||||
const data = (result.output as { npubs?: string[] } | undefined)?.npubs
|
||||
const data = (result.output as { npubs?: NpubEntry[] } | undefined)?.npubs
|
||||
? result.output
|
||||
: result;
|
||||
const npubs = (data as { npubs?: string[] } | undefined)?.npubs ?? [];
|
||||
const npubs = (data as { npubs?: NpubEntry[] } | undefined)?.npubs ?? [];
|
||||
if (npubs.length === 0) {
|
||||
console.log("No admin npubs configured. Run 'routstrd npubs register' to register yourself as the first admin.");
|
||||
return;
|
||||
}
|
||||
console.log(`Admin npubs (${npubs.length}):`);
|
||||
console.log(`Npubs (${npubs.length}):`);
|
||||
let found = false;
|
||||
for (const npub of npubs) {
|
||||
const marker = npub === userNpub ? " → you" : "";
|
||||
if (npub === userNpub) found = true;
|
||||
console.log(`- ${npub}${marker}`);
|
||||
for (const entry of npubs) {
|
||||
const marker = entry.npub === userNpub ? " → you" : "";
|
||||
if (entry.npub === userNpub) found = true;
|
||||
console.log(`- ${entry.npub} [${entry.role}]${marker}`);
|
||||
}
|
||||
if (userNpub && !found) {
|
||||
console.log("");
|
||||
@@ -893,10 +897,10 @@ npubsCmd
|
||||
console.log(result.error);
|
||||
process.exit(1);
|
||||
}
|
||||
const data = (result.output as { npubs?: string[] } | undefined)?.npubs
|
||||
const data = (result.output as { npubs?: NpubEntry[] } | undefined)?.npubs
|
||||
? result.output
|
||||
: result;
|
||||
const npubs = (data as { npubs?: string[] } | undefined)?.npubs ?? [];
|
||||
const npubs = (data as { npubs?: NpubEntry[] } | undefined)?.npubs ?? [];
|
||||
if (npubs.length > 0) {
|
||||
console.log(`Admin npubs already configured (${npubs.length}). Ask your admin to add your npub. \n Your npub: ${userNpub}`);
|
||||
return;
|
||||
@@ -926,35 +930,75 @@ npubsCmd
|
||||
|
||||
npubsCmd
|
||||
.command("add <npub>")
|
||||
.description("Add an admin npub (hex pubkey or npub1...)")
|
||||
.action(async (npubArg: string) => {
|
||||
.description("Add a npub (hex pubkey or npub1...). Defaults to 'user' role unless --role is specified.")
|
||||
.option("-r, --role <role>", "Role for the npub: 'admin' or 'user' (default: 'user')", "user")
|
||||
.action(async (npubArg: string, options: { role: string }) => {
|
||||
await ensureDaemonRunning();
|
||||
const normalized = normalizeNostrPubkey(npubArg);
|
||||
if (!normalized) {
|
||||
console.error("Invalid npub value. Use npub1... or 64-char hex pubkey.");
|
||||
process.exit(1);
|
||||
}
|
||||
if (options.role !== "admin" && options.role !== "user") {
|
||||
console.error("Invalid role. Expected 'admin' or 'user'.");
|
||||
process.exit(1);
|
||||
}
|
||||
const body: Record<string, string> = { npub: npubFromPubkey(normalized), role: options.role };
|
||||
const result = await callDaemon("/npubs", {
|
||||
method: "POST",
|
||||
body: { npub: npubFromPubkey(normalized) },
|
||||
body,
|
||||
});
|
||||
if (result.error) {
|
||||
console.log(result.error);
|
||||
process.exit(1);
|
||||
}
|
||||
const output = result.output as
|
||||
| { npub?: string; added?: boolean; error?: string }
|
||||
| { npub?: string; role?: string; added?: boolean; error?: string }
|
||||
| undefined;
|
||||
if (output?.npub) {
|
||||
console.log(
|
||||
`${output.added ? "Added" : "Already configured"} admin npub: ${output.npub}`,
|
||||
`${output.added ? "Added" : "Already configured"} npub: ${output.npub} [${output.role ?? "user"}]`,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
npubsCmd
|
||||
.command("update <npub>")
|
||||
.description("Update the role of an existing npub (requires admin)")
|
||||
.requiredOption("-r, --role <role>", "New role: 'admin' or 'user'")
|
||||
.action(async (npubArg: string, options: { role: string }) => {
|
||||
await ensureDaemonRunning();
|
||||
const normalized = normalizeNostrPubkey(npubArg);
|
||||
if (!normalized) {
|
||||
console.error("Invalid npub value. Use npub1... or 64-char hex pubkey.");
|
||||
process.exit(1);
|
||||
}
|
||||
if (options.role !== "admin" && options.role !== "user") {
|
||||
console.error("Invalid role. Expected 'admin' or 'user'.");
|
||||
process.exit(1);
|
||||
}
|
||||
const result = await callDaemon("/npubs", {
|
||||
method: "PATCH",
|
||||
body: { npub: npubFromPubkey(normalized), role: options.role },
|
||||
});
|
||||
if (result.error) {
|
||||
console.log(result.error);
|
||||
process.exit(1);
|
||||
}
|
||||
// PATCH /npubs returns { npub, pubkey, role } at the top level, not wrapped in { output }
|
||||
const data = (result.output ?? result) as
|
||||
| { npub?: string; pubkey?: string; role?: string; error?: string }
|
||||
| undefined;
|
||||
if (data?.npub) {
|
||||
console.log(`Updated npub ${data.npub} role to '${data.role}'.`);
|
||||
} else {
|
||||
console.log("Npub not found or update failed.");
|
||||
}
|
||||
});
|
||||
|
||||
npubsCmd
|
||||
.command("delete <npub>")
|
||||
.description("Delete an admin npub (hex pubkey or npub1...)")
|
||||
.description("Delete an npub (hex pubkey or npub1...)")
|
||||
.action(async (npubArg: string) => {
|
||||
await ensureDaemonRunning();
|
||||
const normalized = normalizeNostrPubkey(npubArg);
|
||||
|
||||
@@ -713,6 +713,7 @@ export function createDaemonRequestHandler(deps: {
|
||||
apiKey: c.apiKey,
|
||||
createdAt: c.createdAt,
|
||||
lastUsed: c.lastUsed,
|
||||
ownerNpub: c.ownerNpub,
|
||||
}));
|
||||
|
||||
res.writeHead(200, { "Content-Type": "application/json" });
|
||||
@@ -737,6 +738,7 @@ export function createDaemonRequestHandler(deps: {
|
||||
const body = bodyText ? JSON.parse(bodyText) : {};
|
||||
const name = body.name as string | undefined;
|
||||
const explicitId = body.id as string | undefined;
|
||||
const ownerNpub = body.ownerNpub as string | undefined;
|
||||
|
||||
if (!name || typeof name !== "string" || name.trim() === "") {
|
||||
res.writeHead(400, { "Content-Type": "application/json" });
|
||||
@@ -800,6 +802,7 @@ export function createDaemonRequestHandler(deps: {
|
||||
name: name.trim(),
|
||||
apiKey,
|
||||
createdAt: Date.now(),
|
||||
...(ownerNpub && typeof ownerNpub === "string" ? { ownerNpub } : {}),
|
||||
};
|
||||
|
||||
deps.store
|
||||
@@ -821,6 +824,7 @@ export function createDaemonRequestHandler(deps: {
|
||||
name: name.trim(),
|
||||
apiKey,
|
||||
createdAt: newClient.createdAt,
|
||||
ownerNpub: newClient.ownerNpub,
|
||||
},
|
||||
},
|
||||
}),
|
||||
|
||||
+4
-15
@@ -1,5 +1,5 @@
|
||||
import type { UsageTrackingEntry } from "../../daemon/types.ts";
|
||||
import { callDaemon, getNpubSuffix, isDaemonRunning, loadConfig } from "../../utils/daemon-client.ts";
|
||||
import { callDaemon, isDaemonRunning } from "../../utils/daemon-client.ts";
|
||||
import type { ClientStats, DayStats, ModelStats, ProviderStats, UsageStats } from "./types.ts";
|
||||
|
||||
export interface BalanceKey {
|
||||
@@ -82,21 +82,10 @@ export async function fetchUsage(limit = 10000): Promise<UsageStats | null> {
|
||||
const result = await callDaemon(`/usage?limit=${limit}`);
|
||||
if (result.error) return null;
|
||||
|
||||
// The daemon returns { output: [...] } where output is the entries array directly.
|
||||
// In remote daemon mode, client IDs are suffixed with the last 7 chars of our npub.
|
||||
// Fetch the full daemon result, but only display/aggregate entries for our clients.
|
||||
// The auth proxy filters usage to the authenticated npub's clients and
|
||||
// returns client IDs without the owner suffix.
|
||||
const entries = result.output as UsageTrackingEntry[] | undefined;
|
||||
const entriesArray = Array.isArray(entries) ? entries : [];
|
||||
const suffix = getNpubSuffix(await loadConfig());
|
||||
const suffixStr = suffix ? `-${suffix}` : null;
|
||||
const visibleEntries = suffixStr
|
||||
? entriesArray
|
||||
.filter((entry) => entry.client?.endsWith(suffixStr))
|
||||
.map((entry) => ({
|
||||
...entry,
|
||||
client: entry.client?.slice(0, -suffixStr.length),
|
||||
}))
|
||||
: entriesArray;
|
||||
const visibleEntries = Array.isArray(entries) ? entries : [];
|
||||
|
||||
// Calculate totals from visible entries
|
||||
const totals = visibleEntries.reduce(
|
||||
|
||||
+19
-61
@@ -4,49 +4,16 @@ import {
|
||||
getDaemonBaseUrl,
|
||||
ensureDaemonRunning,
|
||||
} from "./daemon-client";
|
||||
import {
|
||||
parseSecretKey,
|
||||
npubFromSecretKey,
|
||||
} from "./nip98";
|
||||
import { type RoutstrdConfig } from "./config";
|
||||
import { logger } from "./logger";
|
||||
import { CLIENT_INTEGRATIONS, CLIENT_CONFIGS } from "../integrations/registry";
|
||||
|
||||
export function getNpubSuffix(config: RoutstrdConfig): string | null {
|
||||
if (!config.daemonUrl || !config.nsec) return null;
|
||||
try {
|
||||
const secretKey = parseSecretKey(config.nsec);
|
||||
const npub = npubFromSecretKey(secretKey);
|
||||
return npub.slice(-7);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add suffix to a client ID.
|
||||
*/
|
||||
export function addSuffixToId(id: string, suffix: string): string {
|
||||
return `${id}-${suffix}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove suffix from a client ID if present.
|
||||
*/
|
||||
export function removeSuffixFromId(id: string, suffix: string): string {
|
||||
const suffixStr = `-${suffix}`;
|
||||
if (id.endsWith(suffixStr)) {
|
||||
return id.slice(0, -suffixStr.length);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
export interface ClientEntry {
|
||||
clientId: string;
|
||||
name: string;
|
||||
apiKey: string;
|
||||
createdAt: number;
|
||||
lastUsed?: number | null;
|
||||
ownerNpub?: string;
|
||||
}
|
||||
|
||||
export interface DaemonClient {
|
||||
@@ -55,6 +22,7 @@ export interface DaemonClient {
|
||||
apiKey: string;
|
||||
createdAt: number;
|
||||
lastUsed?: number | null;
|
||||
ownerNpub?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,12 +39,14 @@ export function getClientsFromStore(store: { getState(): any }): ClientEntry[] {
|
||||
apiKey: string;
|
||||
createdAt: number;
|
||||
lastUsed?: number | null;
|
||||
ownerNpub?: string;
|
||||
}) => ({
|
||||
clientId: c.clientId,
|
||||
name: c.name,
|
||||
apiKey: c.apiKey,
|
||||
createdAt: c.createdAt,
|
||||
lastUsed: c.lastUsed,
|
||||
ownerNpub: c.ownerNpub,
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -86,7 +56,6 @@ export function getClientsFromStore(store: { getState(): any }): ClientEntry[] {
|
||||
* Use this when running remotely (CLI in remote mode).
|
||||
*/
|
||||
export async function getClientsList(): Promise<ClientEntry[]> {
|
||||
const config = await loadConfig();
|
||||
const result = await callDaemon("/clients");
|
||||
const clients = (
|
||||
result.output as
|
||||
@@ -97,6 +66,7 @@ export async function getClientsList(): Promise<ClientEntry[]> {
|
||||
apiKey: string;
|
||||
createdAt: number;
|
||||
lastUsed?: number | null;
|
||||
ownerNpub?: string;
|
||||
}>;
|
||||
}
|
||||
| undefined
|
||||
@@ -106,17 +76,14 @@ export async function getClientsList(): Promise<ClientEntry[]> {
|
||||
return [];
|
||||
}
|
||||
|
||||
const suffix = config.daemonUrl ? getNpubSuffix(config) : null;
|
||||
|
||||
return clients
|
||||
.filter((c) => !suffix || c.id.endsWith(`-${suffix}`))
|
||||
.map((c) => ({
|
||||
clientId: suffix ? removeSuffixFromId(c.id, suffix) : c.id,
|
||||
name: c.name,
|
||||
apiKey: c.apiKey,
|
||||
createdAt: c.createdAt,
|
||||
lastUsed: c.lastUsed,
|
||||
}));
|
||||
return clients.map((c) => ({
|
||||
clientId: c.id,
|
||||
name: c.name,
|
||||
apiKey: c.apiKey,
|
||||
createdAt: c.createdAt,
|
||||
lastUsed: c.lastUsed,
|
||||
ownerNpub: c.ownerNpub,
|
||||
}));
|
||||
}
|
||||
|
||||
export async function addDaemonClient(
|
||||
@@ -135,18 +102,14 @@ export async function addDaemonClient(
|
||||
apiKey: existing.apiKey,
|
||||
createdAt: existing.createdAt,
|
||||
lastUsed: existing.lastUsed,
|
||||
ownerNpub: existing.ownerNpub,
|
||||
};
|
||||
return { client, created: false };
|
||||
}
|
||||
|
||||
const config = await loadConfig();
|
||||
const suffix = getNpubSuffix(config);
|
||||
const clientId = suffix ? addSuffixToId(derivedId, suffix) : derivedId;
|
||||
|
||||
|
||||
const result = await callDaemon("/clients/add", {
|
||||
method: "POST",
|
||||
body: { name, id: clientId },
|
||||
body: { name, id: derivedId },
|
||||
});
|
||||
|
||||
|
||||
@@ -196,13 +159,9 @@ export async function listClientsAction(): Promise<void> {
|
||||
export async function deleteClientAction(id: string): Promise<void> {
|
||||
await ensureDaemonRunning();
|
||||
|
||||
const config = await loadConfig();
|
||||
const suffix = getNpubSuffix(config);
|
||||
const resolvedId = suffix ? addSuffixToId(id, suffix) : id;
|
||||
|
||||
const result = await callDaemon("/clients/delete", {
|
||||
method: "POST",
|
||||
body: { id: resolvedId },
|
||||
body: { id },
|
||||
});
|
||||
|
||||
if (result.error) {
|
||||
@@ -233,7 +192,6 @@ export interface AddClientOptions {
|
||||
export async function addClientAction(options: AddClientOptions): Promise<void> {
|
||||
await ensureDaemonRunning();
|
||||
const config = await loadConfig();
|
||||
const suffix = getNpubSuffix(config);
|
||||
|
||||
const integrationKeys: string[] = [];
|
||||
if (options.opencode) integrationKeys.push("opencode");
|
||||
@@ -259,7 +217,7 @@ export async function addClientAction(options: AddClientOptions): Promise<void>
|
||||
await integrationFn(config, client.apiKey, integrationConfig);
|
||||
|
||||
console.log(`\n ${integrationConfig.name}:`);
|
||||
console.log(` Client ID: ${suffix ? removeSuffixFromId(client.id, suffix) : client.id}`);
|
||||
console.log(` Client ID: ${client.id}`);
|
||||
console.log(` API Key: ${client.apiKey}`);
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
@@ -286,7 +244,7 @@ export async function addClientAction(options: AddClientOptions): Promise<void>
|
||||
|
||||
if (!created) {
|
||||
console.log(`Client '${options.name}' already exists.`);
|
||||
console.log(`\n ID: ${suffix ? removeSuffixFromId(client.id, suffix) : client.id}`);
|
||||
console.log(`\n ID: ${client.id}`);
|
||||
console.log(` Name: ${client.name}`);
|
||||
console.log(` API Key: ${client.apiKey}`);
|
||||
return;
|
||||
@@ -295,7 +253,7 @@ export async function addClientAction(options: AddClientOptions): Promise<void>
|
||||
if (message) {
|
||||
console.log(message);
|
||||
}
|
||||
console.log(`\n ID: ${suffix ? removeSuffixFromId(client.id, suffix) : client.id}`);
|
||||
console.log(`\n ID: ${client.id}`);
|
||||
console.log(` Name: ${client.name}`);
|
||||
console.log(` API Key: ${client.apiKey}`);
|
||||
console.log(`\n Access Routstr at: ${getDaemonBaseUrl(config)}/v1`);
|
||||
|
||||
@@ -37,7 +37,7 @@ export function getDaemonBaseUrl(config: RoutstrdConfig): string {
|
||||
|
||||
export async function callDaemon(
|
||||
path: string,
|
||||
options: { method?: "GET" | "POST" | "DELETE"; body?: object } = {},
|
||||
options: { method?: "GET" | "POST" | "PATCH" | "DELETE"; body?: object } = {},
|
||||
): Promise<CommandResponse> {
|
||||
const { method = "GET", body } = options;
|
||||
const config = await loadConfig();
|
||||
|
||||
Reference in New Issue
Block a user