@genkaku/dsh-user-plugin 0.1.23 → 0.1.25

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.
Files changed (2) hide show
  1. package/lib/client.js +159 -0
  2. package/package.json +3 -3
package/lib/client.js CHANGED
@@ -126,6 +126,9 @@ function Page() {
126
126
  const [notice, setNotice] = (0, import_react.useState)(null);
127
127
  const [busy, setBusy] = (0, import_react.useState)(null);
128
128
  const [newWorker, setNewWorker] = (0, import_react.useState)("");
129
+ const [exported, setExported] = (0, import_react.useState)(null);
130
+ const [importText, setImportText] = (0, import_react.useState)("");
131
+ const [showImport, setShowImport] = (0, import_react.useState)(false);
129
132
  const originRef = (0, import_react.useRef)(null);
130
133
  const models = useScrollThumb();
131
134
  const load = (0, import_react.useCallback)(async () => {
@@ -164,6 +167,7 @@ function Page() {
164
167
  });
165
168
  const doc = await res.json().catch(() => null);
166
169
  if (!res.ok) throw new Error(doc?.error?.message ?? `HTTP ${res.status}`);
170
+ return doc ?? {};
167
171
  }, []);
168
172
  const saveWorkers = (0, import_react.useCallback)(async (next) => {
169
173
  setBusy("workers");
@@ -195,6 +199,78 @@ function Page() {
195
199
  setBusy(null);
196
200
  }
197
201
  }, [load, post]);
202
+ const exportKey = (0, import_react.useCallback)(async () => {
203
+ setBusy("export");
204
+ setNotice(null);
205
+ try {
206
+ const doc = await post("/genkaku/wallet/export", { confirm: true });
207
+ setExported(typeof doc.privateKey === "string" ? doc.privateKey : null);
208
+ setNotice(t(
209
+ "Never share this key. Anyone who has it owns the wallet and its funds.",
210
+ "\u7D55\u5C0D\u4E0D\u8981\u5206\u4EAB\u9019\u628A\u79C1\u9470\u3002\u62FF\u5230\u5B83\u7684\u4EBA\u5C31\u7B49\u65BC\u64C1\u6709\u9019\u500B\u9322\u5305\u8207\u88E1\u9762\u7684\u8CC7\u91D1\u3002"
211
+ ));
212
+ } catch (e) {
213
+ setNotice(e instanceof Error ? e.message : String(e));
214
+ } finally {
215
+ setBusy(null);
216
+ }
217
+ }, [post]);
218
+ const importKey = (0, import_react.useCallback)(async () => {
219
+ if (!window.confirm(t(
220
+ "Importing a key replaces this profile's user wallet. The current address keeps its funds and is no longer used. Continue?",
221
+ "\u532F\u5165\u79C1\u9470\u6703\u53D6\u4EE3\u6B64 profile \u7684\u4F7F\u7528\u8005\u9322\u5305\u3002\u76EE\u524D\u4F4D\u5740\u88E1\u7684\u8CC7\u91D1\u4E0D\u6703\u8F49\u79FB\uFF0C\u4E14\u4E0D\u518D\u4F7F\u7528\u3002\u8981\u7E7C\u7E8C\u55CE\uFF1F"
222
+ ))) return;
223
+ setBusy("import");
224
+ setNotice(null);
225
+ try {
226
+ await post("/genkaku/wallet/import", { privateKey: importText.trim() });
227
+ setImportText("");
228
+ setShowImport(false);
229
+ setNotice(t("Wallet imported. The bridge is restarting.", "\u5DF2\u532F\u5165\u9322\u5305\uFF0Cbridge \u6B63\u5728\u91CD\u65B0\u555F\u52D5\u3002"));
230
+ setTimeout(() => {
231
+ void load();
232
+ }, 3e3);
233
+ } catch (e) {
234
+ setNotice(e instanceof Error ? e.message : String(e));
235
+ } finally {
236
+ setBusy(null);
237
+ }
238
+ }, [importText, load, post]);
239
+ const rotateKey = (0, import_react.useCallback)(async () => {
240
+ if (!window.confirm(t(
241
+ "Generate a fresh wallet? The old address keeps its ETH and USDC; this profile stops using it.",
242
+ "\u8981\u7522\u751F\u65B0\u7684\u9322\u5305\u55CE\uFF1F\u820A\u4F4D\u5740\u7684 ETH \u8207 USDC \u6703\u7559\u5728\u539F\u8655\uFF0C\u6B64 profile \u4E0D\u518D\u4F7F\u7528\u5B83\u3002"
243
+ ))) return;
244
+ setBusy("rotate");
245
+ setNotice(null);
246
+ try {
247
+ await post("/genkaku/wallet/new", {});
248
+ setExported(null);
249
+ setNotice(t("New wallet generated. The bridge is restarting.", "\u5DF2\u7522\u751F\u65B0\u9322\u5305\uFF0Cbridge \u6B63\u5728\u91CD\u65B0\u555F\u52D5\u3002"));
250
+ setTimeout(() => {
251
+ void load();
252
+ }, 3e3);
253
+ } catch (e) {
254
+ setNotice(e instanceof Error ? e.message : String(e));
255
+ } finally {
256
+ setBusy(null);
257
+ }
258
+ }, [load, post]);
259
+ const mintTestUsdc = (0, import_react.useCallback)(async () => {
260
+ setBusy("mint");
261
+ setNotice(null);
262
+ try {
263
+ const doc = await post("/genkaku/wallet/mint", { amountUsdc: "100" });
264
+ setNotice(t(`Minted 100 test USDC (tx ${String(doc.tx).slice(0, 12)}\u2026).`, `\u5DF2\u9818\u53D6 100 \u6E2C\u8A66 USDC\uFF08tx ${String(doc.tx).slice(0, 12)}\u2026\uFF09\u3002`));
265
+ setTimeout(() => {
266
+ void load();
267
+ }, 4e3);
268
+ } catch (e) {
269
+ setNotice(e instanceof Error ? e.message : String(e));
270
+ } finally {
271
+ setBusy(null);
272
+ }
273
+ }, [load, post]);
198
274
  const refreshNetwork = (0, import_react.useCallback)(async () => {
199
275
  setBusy("refresh");
200
276
  setNotice(null);
@@ -235,6 +311,13 @@ function Page() {
235
311
  const n = Number(micro);
236
312
  return Number.isFinite(n) ? `${(n / 1e6).toFixed(2)} USDC` : "\u2014";
237
313
  };
314
+ const noGas = (() => {
315
+ try {
316
+ return BigInt(status.balances?.eth ?? "0") === 0n;
317
+ } catch {
318
+ return true;
319
+ }
320
+ })();
238
321
  const short = (addr) => addr == null || addr.length < 12 ? addr ?? "\u2014" : `${addr.slice(0, 6)}\u2026${addr.slice(-4)}`;
239
322
  const wallets = new Set(offers.map((o) => o.offer?.workerWallet).filter((w) => typeof w === "string" && w !== ""));
240
323
  const workerCount = wallets.size > 0 ? wallets.size : (status.workers ?? []).length;
@@ -295,6 +378,82 @@ function Page() {
295
378
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: row, children: [
296
379
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: dim, children: "Models offered" }),
297
380
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: offers.length })
381
+ ] }),
382
+ noGas && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { ...dim, fontSize: 12, marginTop: 8 }, children: [
383
+ t(
384
+ "This wallet has no Base Sepolia ETH yet. Get test ETH for gas from a faucet, then mint test USDC here.",
385
+ "\u9019\u500B\u9322\u5305\u9084\u6C92\u6709 Base Sepolia ETH\u3002\u5148\u5230 faucet \u9818\u6E2C\u8A66 ETH \u4ED8 gas\uFF0C\u518D\u56DE\u4F86\u9818\u6E2C\u8A66 USDC\u3002"
386
+ ),
387
+ " ",
388
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { href: "https://www.alchemy.com/faucets/base-sepolia", target: "_blank", rel: "noreferrer", children: t("Open faucet", "\u958B\u555F faucet") })
389
+ ] }),
390
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", flexWrap: "wrap", gap: 8, marginTop: 10 }, children: [
391
+ status.env === "base-sepolia" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
392
+ import_dsh_client_ui_primitives.Button,
393
+ {
394
+ variant: "outline",
395
+ size: "sm",
396
+ disabled: busy !== null || noGas,
397
+ onClick: () => {
398
+ void mintTestUsdc();
399
+ },
400
+ children: busy === "mint" ? t("Minting\u2026", "\u9818\u53D6\u4E2D\u2026") : t("Mint 100 test USDC", "\u9818\u53D6 100 \u6E2C\u8A66 USDC")
401
+ }
402
+ ),
403
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", size: "sm", disabled: busy !== null, onClick: () => {
404
+ void exportKey();
405
+ }, children: busy === "export" ? t("Revealing\u2026", "\u8B80\u53D6\u4E2D\u2026") : t("Export key", "\u532F\u51FA\u79C1\u9470") }),
406
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", size: "sm", disabled: busy !== null, onClick: () => setShowImport((v) => !v), children: t("Import key", "\u532F\u5165\u79C1\u9470") }),
407
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", size: "sm", disabled: busy !== null, onClick: () => {
408
+ void rotateKey();
409
+ }, children: t("New wallet", "\u7522\u751F\u65B0\u9322\u5305") })
410
+ ] }),
411
+ exported != null && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { marginTop: 10 }, children: [
412
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { ...dim, fontSize: 12, marginBottom: 4 }, children: t("Private key \u2014 keep it offline. Testnet only.", "\u79C1\u9470\u2014\u2014\u8ACB\u96E2\u7DDA\u4FDD\u5B58\u3002\u50C5\u9650\u6E2C\u8A66\u7DB2\u3002") }),
413
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", gap: 8 }, children: [
414
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { readOnly: true, value: exported, onFocus: (e) => e.currentTarget.select(), "aria-label": t("Private key", "\u79C1\u9470") }),
415
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
416
+ import_dsh_client_ui_primitives.Button,
417
+ {
418
+ variant: "ghost",
419
+ size: "sm",
420
+ onClick: () => {
421
+ if (navigator.clipboard != null) {
422
+ void navigator.clipboard.writeText(exported);
423
+ setNotice(t("Private key copied.", "\u5DF2\u8907\u88FD\u79C1\u9470\u3002"));
424
+ }
425
+ },
426
+ children: t("copy", "\u8907\u88FD")
427
+ }
428
+ ),
429
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "ghost", size: "sm", onClick: () => setExported(null), children: t("hide", "\u96B1\u85CF") })
430
+ ] })
431
+ ] }),
432
+ showImport && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { marginTop: 10 }, children: [
433
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { ...dim, fontSize: 12, marginBottom: 4 }, children: t("Paste a 0x\u2026 32-byte private key", "\u8CBC\u4E0A 0x\u2026 32 byte \u79C1\u9470") }),
434
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", gap: 8 }, children: [
435
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
436
+ import_dsh_client_ui_primitives.Input,
437
+ {
438
+ value: importText,
439
+ placeholder: "0x\u2026",
440
+ onChange: (e) => setImportText(e.target.value),
441
+ "aria-label": t("Private key to import", "\u8981\u532F\u5165\u7684\u79C1\u9470")
442
+ }
443
+ ),
444
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
445
+ import_dsh_client_ui_primitives.Button,
446
+ {
447
+ variant: "primary",
448
+ size: "sm",
449
+ disabled: busy !== null || !/^(0x)?[0-9a-fA-F]{64}$/.test(importText.trim()),
450
+ onClick: () => {
451
+ void importKey();
452
+ },
453
+ children: busy === "import" ? t("Importing\u2026", "\u532F\u5165\u4E2D\u2026") : t("Import", "\u532F\u5165")
454
+ }
455
+ )
456
+ ] })
298
457
  ] })
299
458
  ] }),
300
459
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: box, children: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@genkaku/dsh-user-plugin",
3
- "version": "0.1.23",
3
+ "version": "0.1.25",
4
4
  "description": "Buy peer-to-peer inference from the GenKaKu network",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -39,8 +39,8 @@
39
39
  },
40
40
  "dependencies": {
41
41
  "@deepseek-ai/dsh-llm": "0.2.2-rc.1",
42
- "@genkaku/dsh-runtime": "0.1.22",
43
- "@genkaku/protocol": "0.1.15"
42
+ "@genkaku/dsh-runtime": "0.1.24",
43
+ "@genkaku/protocol": "0.1.17"
44
44
  },
45
45
  "peerDependencies": {
46
46
  "@deepseek-ai/cordis": "^4.1.0",