@genkaku/dsh-user-plugin 0.1.18 → 0.1.20
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/lib/client.js +105 -25
- package/package.json +2 -2
package/lib/client.js
CHANGED
|
@@ -78,9 +78,48 @@ function Icon({ size = 16 }) {
|
|
|
78
78
|
var row = { display: "flex", justifyContent: "space-between", gap: 12, padding: "6px 0" };
|
|
79
79
|
var dim = { opacity: 0.6 };
|
|
80
80
|
var box = { border: "1px solid rgba(255,255,255,0.12)", borderRadius: 10, padding: 12, marginTop: 12 };
|
|
81
|
+
function useScrollThumb() {
|
|
82
|
+
const [node, setNode] = (0, import_react.useState)(null);
|
|
83
|
+
const nodeRef = (0, import_react.useRef)(null);
|
|
84
|
+
const [thumb, setThumb] = (0, import_react.useState)(null);
|
|
85
|
+
const attach = (0, import_react.useCallback)((next) => {
|
|
86
|
+
nodeRef.current = next;
|
|
87
|
+
setNode(next);
|
|
88
|
+
}, []);
|
|
89
|
+
const update = (0, import_react.useCallback)(() => {
|
|
90
|
+
const el = nodeRef.current;
|
|
91
|
+
if (el == null) return;
|
|
92
|
+
const ratio = el.scrollHeight > el.clientHeight + 1 ? el.clientHeight / el.scrollHeight : 1;
|
|
93
|
+
setThumb(ratio >= 1 ? null : { top: el.scrollTop / el.scrollHeight * 100, height: Math.max(ratio * 100, 8) });
|
|
94
|
+
}, []);
|
|
95
|
+
(0, import_react.useEffect)(() => {
|
|
96
|
+
if (node == null) return;
|
|
97
|
+
update();
|
|
98
|
+
node.addEventListener("scroll", update, { passive: true });
|
|
99
|
+
const ro = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(update);
|
|
100
|
+
ro?.observe(node);
|
|
101
|
+
const mo = typeof MutationObserver === "undefined" ? null : new MutationObserver(update);
|
|
102
|
+
mo?.observe(node, { childList: true, subtree: true });
|
|
103
|
+
return () => {
|
|
104
|
+
node.removeEventListener("scroll", update);
|
|
105
|
+
ro?.disconnect();
|
|
106
|
+
mo?.disconnect();
|
|
107
|
+
};
|
|
108
|
+
}, [node, update]);
|
|
109
|
+
const jump = (0, import_react.useCallback)((event) => {
|
|
110
|
+
const el = nodeRef.current;
|
|
111
|
+
const rail = event.currentTarget;
|
|
112
|
+
if (el == null || rail == null) return;
|
|
113
|
+
const rect = rail.getBoundingClientRect();
|
|
114
|
+
if (rect.height <= 0) return;
|
|
115
|
+
el.scrollTop = Math.max(0, (event.clientY - rect.top) / rect.height) * el.scrollHeight;
|
|
116
|
+
}, []);
|
|
117
|
+
return { attach, thumb, update, jump };
|
|
118
|
+
}
|
|
81
119
|
function Page() {
|
|
82
120
|
const [status, setStatus] = (0, import_react.useState)(null);
|
|
83
121
|
const [error, setError] = (0, import_react.useState)(null);
|
|
122
|
+
const models = useScrollThumb();
|
|
84
123
|
const load = (0, import_react.useCallback)(async () => {
|
|
85
124
|
for (const port of CANDIDATE_PORTS) {
|
|
86
125
|
try {
|
|
@@ -118,44 +157,85 @@ function Page() {
|
|
|
118
157
|
}
|
|
119
158
|
if (status == null) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { padding: 16, ...dim }, children: "Loading worker status\u2026" });
|
|
120
159
|
const offers = status.offers ?? [];
|
|
121
|
-
|
|
160
|
+
const price = (micro) => {
|
|
161
|
+
const n = Number(micro);
|
|
162
|
+
if (!Number.isFinite(n)) return "\u2014";
|
|
163
|
+
return n === 0 ? "free" : `${(n / 1e6).toFixed(2)} USDC`;
|
|
164
|
+
};
|
|
165
|
+
const eth = (wei) => {
|
|
166
|
+
const n = Number(wei);
|
|
167
|
+
return Number.isFinite(n) ? `${(n / 1e18).toFixed(4)} ETH` : "\u2014";
|
|
168
|
+
};
|
|
169
|
+
const usdc = (micro) => {
|
|
170
|
+
const n = Number(micro);
|
|
171
|
+
return Number.isFinite(n) ? `${(n / 1e6).toFixed(2)} USDC` : "\u2014";
|
|
172
|
+
};
|
|
173
|
+
const short = (addr) => addr == null || addr.length < 12 ? addr ?? "\u2014" : `${addr.slice(0, 6)}\u2026${addr.slice(-4)}`;
|
|
174
|
+
const wallets = new Set(offers.map((o) => o.offer?.workerWallet).filter((w) => typeof w === "string" && w !== ""));
|
|
175
|
+
const workerCount = wallets.size > 0 ? wallets.size : (status.workers ?? []).length;
|
|
176
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { padding: 16, maxWidth: 760 }, children: [
|
|
177
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: `
|
|
178
|
+
.gk-net-wrap { position: relative; padding-right: 12px; }
|
|
179
|
+
.gk-net-models { max-height: min(380px, 48vh); overflow-y: auto; overscroll-behavior: contain; scrollbar-width: none; }
|
|
180
|
+
.gk-net-models::-webkit-scrollbar { display: none; }
|
|
181
|
+
.gk-net-rail { position: absolute; top: 2px; bottom: 2px; right: 2px; width: 8px; border-radius: 4px; background: rgba(127,137,153,.16); cursor: pointer; }
|
|
182
|
+
.gk-net-thumb { position: absolute; left: 0; width: 8px; min-height: 16px; border-radius: 4px; background: rgba(127,137,153,.7); }
|
|
183
|
+
.gk-net-rail:hover .gk-net-thumb { background: rgba(127,137,153,.9); }
|
|
184
|
+
.gk-net-row { display: grid; grid-template-columns: minmax(0, 1fr) 92px 116px; gap: 10px; align-items: center; padding: 7px 0; border-top: 0.5px solid rgba(127,137,153,.18); }
|
|
185
|
+
.gk-net-row:first-child { border-top: none; }
|
|
186
|
+
` }),
|
|
122
187
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { style: { margin: 0 }, children: "GenKaKu network" }),
|
|
123
|
-
/* @__PURE__ */ (0, import_jsx_runtime.
|
|
188
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { ...dim, marginTop: 4 }, children: [
|
|
189
|
+
"user \xB7 ",
|
|
190
|
+
status.env ?? "base-sepolia"
|
|
191
|
+
] }),
|
|
124
192
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: box, children: [
|
|
125
193
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: row, children: [
|
|
126
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: dim, children: "
|
|
127
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", {
|
|
194
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: dim, children: "Wallet" }),
|
|
195
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { title: status.wallet, children: short(status.wallet) })
|
|
196
|
+
] }),
|
|
197
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: row, children: [
|
|
198
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: dim, children: "Balance" }),
|
|
199
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
|
|
200
|
+
eth(status.balances?.eth),
|
|
201
|
+
" \xB7 ",
|
|
202
|
+
usdc(status.balances?.usdc)
|
|
203
|
+
] })
|
|
128
204
|
] }),
|
|
129
205
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: row, children: [
|
|
130
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: dim, children: "
|
|
131
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: status.
|
|
206
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: dim, children: "Payments" }),
|
|
207
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: status.settle === true ? "escrow enabled" : "free / dev" })
|
|
132
208
|
] }),
|
|
133
209
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: row, children: [
|
|
134
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: dim, children: "
|
|
135
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children:
|
|
210
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: dim, children: "Workers discovered" }),
|
|
211
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: workerCount })
|
|
136
212
|
] }),
|
|
137
213
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: row, children: [
|
|
138
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: dim, children: "
|
|
139
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children:
|
|
214
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: dim, children: "Models offered" }),
|
|
215
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: offers.length })
|
|
140
216
|
] })
|
|
141
217
|
] }),
|
|
142
218
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: box, children: [
|
|
143
219
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { ...dim, fontSize: 12, textTransform: "uppercase", letterSpacing: 1 }, children: "Models on the network" }),
|
|
144
|
-
/* @__PURE__ */ (0, import_jsx_runtime.
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
.
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
220
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "gk-net-wrap", children: [
|
|
221
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "gk-net-models", ref: models.attach, onScroll: models.update, children: offers.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { ...dim, marginBottom: 0 }, children: "Nothing offered yet. A worker lists its local Ollama models on start." }) : offers.map((entry, i) => {
|
|
222
|
+
const offer = entry.offer ?? {};
|
|
223
|
+
const transport = entry.addr == null ? null : status.transports?.[entry.addr];
|
|
224
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "gk-net-row", children: [
|
|
225
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { style: { minWidth: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: [
|
|
226
|
+
offer.model ?? "model name missing",
|
|
227
|
+
offer.tier === "chat" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { ...dim, fontSize: 11, marginLeft: 8 }, children: "not for agents" }) : offer.tier != null ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { ...dim, fontSize: 11, marginLeft: 8 }, children: offer.tier }) : null
|
|
228
|
+
] }),
|
|
229
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { ...dim, textAlign: "right", fontSize: 12 }, children: offer.workerName ?? "\u2014" }),
|
|
230
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { style: { textAlign: "right", fontSize: 12 }, children: [
|
|
231
|
+
price(offer.pricePerHour),
|
|
232
|
+
" ",
|
|
233
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: dim, children: transport === "relay" ? "\xB7 via seed VM" : transport === "direct" ? "\xB7 direct" : "" })
|
|
234
|
+
] })
|
|
235
|
+
] }, `${entry.addr ?? ""}:${offer.model ?? i}`);
|
|
236
|
+
}) }),
|
|
237
|
+
models.thumb != null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "gk-net-rail", "aria-hidden": "true", onPointerDown: (e) => models.jump(e), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "gk-net-thumb", style: { top: `${models.thumb.top}%`, height: `${models.thumb.height}%` } }) })
|
|
238
|
+
] })
|
|
159
239
|
] })
|
|
160
240
|
] });
|
|
161
241
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@genkaku/dsh-user-plugin",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.20",
|
|
4
4
|
"description": "Buy peer-to-peer inference from the GenKaKu network",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"@deepseek-ai/dsh-llm": "0.2.2-rc.1",
|
|
42
|
-
"@genkaku/dsh-runtime": "0.1.
|
|
42
|
+
"@genkaku/dsh-runtime": "0.1.20",
|
|
43
43
|
"@genkaku/protocol": "0.1.13"
|
|
44
44
|
},
|
|
45
45
|
"peerDependencies": {
|