@genkaku/protocol 0.1.17 → 0.1.19
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/dist/bridge.d.ts +3 -9
- package/dist/bridge.d.ts.map +1 -1
- package/dist/bridge.js +6 -55
- package/dist/cors.d.ts +3 -3
- package/dist/cors.d.ts.map +1 -1
- package/dist/cors.js +18 -7
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/keys.d.ts +2 -0
- package/dist/keys.d.ts.map +1 -1
- package/dist/keys.js +11 -0
- package/dist/status.d.ts +21 -1
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +27 -4
- package/dist/walletControl.d.ts +17 -0
- package/dist/walletControl.d.ts.map +1 -0
- package/dist/walletControl.js +63 -0
- package/package.json +1 -1
package/dist/bridge.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Libp2p } from 'libp2p';
|
|
2
|
+
import { type WalletControl } from './walletControl.ts';
|
|
2
3
|
import type { Hex } from 'viem';
|
|
3
4
|
import type { ModeManager, RoleMode } from './modeManager.ts';
|
|
4
5
|
import type { NetEnvironment } from './netconfig.ts';
|
|
@@ -39,15 +40,8 @@ export type BridgeOptions = {
|
|
|
39
40
|
settle?: boolean;
|
|
40
41
|
}) => void | Promise<void>;
|
|
41
42
|
/** Wallet operations the panel drives. Key material only ever leaves over
|
|
42
|
-
*
|
|
43
|
-
wallet?:
|
|
44
|
-
address: () => string;
|
|
45
|
-
exportKey: () => string;
|
|
46
|
-
importKey: (privateKey: string) => void;
|
|
47
|
-
rotate: () => void;
|
|
48
|
-
/** testnet only; mints MockUSDC to the current user wallet */
|
|
49
|
-
mint?: (amount: bigint) => Promise<string>;
|
|
50
|
-
};
|
|
43
|
+
* the panel CORS gate, and only after an explicit confirmation flag. */
|
|
44
|
+
wallet?: WalletControl;
|
|
51
45
|
/** P4.9h: derived credit per worker wallet (escrow events, not self-report).
|
|
52
46
|
* Absent = offers keep whatever the worker claimed. */
|
|
53
47
|
credit?: (wallets: string[]) => Map<string, {
|
package/dist/bridge.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAGpC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,MAAM,CAAA;AAc/B,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC7D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AACpD,OAAO,EAAgB,MAAM,EAAc,MAAM,YAAY,CAAA;AAI7D,uEAAuE;AACvE,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,UAAU,CAAC,OAAO,MAAM,CAAC,CAAA;KAAE,CAAC,CAAA;IACjE,GAAG,EAAE,MAAM,CAAA;IACX,8EAA8E;IAC9E,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,GAAG,OAAO,CAAC,CAAA;CAChD,CAAA;AAED,MAAM,MAAM,aAAa,GAAG;IAC1B,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,cAAc,CAAA;IACnB,WAAW,EAAE,WAAW,CAAA;IACxB,OAAO,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAA;IAC9B,OAAO,EAAE,MAAM,GAAG,CAAA;IAClB,yEAAyE;IACzE,MAAM,EAAE,OAAO,CAAA;IACf,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAC3B,wFAAwF;IACxF,mEAAmE;IACnE,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;IACnC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;IAC3F,wDAAwD;IACxD,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACjD;gFAC4E;IAC5E,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACtF;
|
|
1
|
+
{"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAGpC,OAAO,EAAmB,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAA;AACxE,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,MAAM,CAAA;AAc/B,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC7D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AACpD,OAAO,EAAgB,MAAM,EAAc,MAAM,YAAY,CAAA;AAI7D,uEAAuE;AACvE,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,UAAU,CAAC,OAAO,MAAM,CAAC,CAAA;KAAE,CAAC,CAAA;IACjE,GAAG,EAAE,MAAM,CAAA;IACX,8EAA8E;IAC9E,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,GAAG,OAAO,CAAC,CAAA;CAChD,CAAA;AAED,MAAM,MAAM,aAAa,GAAG;IAC1B,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,cAAc,CAAA;IACnB,WAAW,EAAE,WAAW,CAAA;IACxB,OAAO,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAA;IAC9B,OAAO,EAAE,MAAM,GAAG,CAAA;IAClB,yEAAyE;IACzE,MAAM,EAAE,OAAO,CAAA;IACf,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAC3B,wFAAwF;IACxF,mEAAmE;IACnE,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;IACnC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;IAC3F,wDAAwD;IACxD,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACjD;gFAC4E;IAC5E,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACtF;6EACyE;IACzE,MAAM,CAAC,EAAE,aAAa,CAAA;IACtB;4DACwD;IACxD,MAAM,CAAC,EAAE,CACP,OAAO,EAAE,MAAM,EAAE,KACd,GAAG,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAA;CAClH,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC3B,CAAA;AAuJD,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CAskBlF"}
|
package/dist/bridge.js
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
// escrow on Base Sepolia. No provider wire code leaks into dsh.
|
|
5
5
|
import { appendFileSync } from 'node:fs';
|
|
6
6
|
import { createServer } from 'node:http';
|
|
7
|
-
import {
|
|
7
|
+
import { applyPanelCors } from "./cors.js";
|
|
8
|
+
import { runWalletAction } from "./walletControl.js";
|
|
8
9
|
import { openEscrow, publicClientFor, readWorker, refundSession, settleSession, signReceiptPayload, walletAddress, walletClientFor, } from "./chain.js";
|
|
9
10
|
import { dialWorkerAny, fetchOffers, requestSettleCosign } from "./dial.js";
|
|
10
11
|
import { dedupeOffers, toWire } from "./offer.js";
|
|
@@ -533,7 +534,7 @@ export async function startOpenAiBridge(opts) {
|
|
|
533
534
|
void (async () => {
|
|
534
535
|
try {
|
|
535
536
|
const path = (req.url ?? '/').split('?')[0] ?? '/';
|
|
536
|
-
if (!
|
|
537
|
+
if (!applyPanelCors(req, res)) {
|
|
537
538
|
res.writeHead(403, { 'content-type': 'application/json' });
|
|
538
539
|
res.end(JSON.stringify({ error: { message: 'origin not allowed' } }));
|
|
539
540
|
return;
|
|
@@ -579,60 +580,10 @@ export async function startOpenAiBridge(opts) {
|
|
|
579
580
|
failure(res, 400, 'invalid JSON body');
|
|
580
581
|
return;
|
|
581
582
|
}
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
// stray click (or a scripted call without intent) cannot read it.
|
|
585
|
-
if (body.confirm !== true) {
|
|
586
|
-
failure(res, 400, 'export requires confirm: true');
|
|
587
|
-
return;
|
|
588
|
-
}
|
|
583
|
+
const reply = await runWalletAction(action, body, opts.wallet);
|
|
584
|
+
if (reply.status === 200 && action === 'export')
|
|
589
585
|
res.setHeader('cache-control', 'no-store');
|
|
590
|
-
|
|
591
|
-
return;
|
|
592
|
-
}
|
|
593
|
-
if (action === 'import') {
|
|
594
|
-
const key = typeof body.privateKey === 'string' ? body.privateKey.trim() : '';
|
|
595
|
-
if (key === '') {
|
|
596
|
-
failure(res, 400, 'privateKey is required');
|
|
597
|
-
return;
|
|
598
|
-
}
|
|
599
|
-
try {
|
|
600
|
-
opts.wallet.importKey(key);
|
|
601
|
-
}
|
|
602
|
-
catch (error) {
|
|
603
|
-
failure(res, 400, error instanceof Error ? error.message : String(error));
|
|
604
|
-
return;
|
|
605
|
-
}
|
|
606
|
-
json(res, 200, { ok: true, address: opts.wallet.address(), restarting: true });
|
|
607
|
-
return;
|
|
608
|
-
}
|
|
609
|
-
if (action === 'new') {
|
|
610
|
-
opts.wallet.rotate();
|
|
611
|
-
json(res, 200, { ok: true, address: opts.wallet.address(), restarting: true });
|
|
612
|
-
return;
|
|
613
|
-
}
|
|
614
|
-
if (action === 'mint') {
|
|
615
|
-
if (opts.wallet.mint == null) {
|
|
616
|
-
failure(res, 400, 'test USDC mint is only available on base-sepolia');
|
|
617
|
-
return;
|
|
618
|
-
}
|
|
619
|
-
const raw = body.amountUsdc === undefined ? '100' : String(body.amountUsdc);
|
|
620
|
-
const amount = Math.round(Number(raw) * 1e6);
|
|
621
|
-
if (!Number.isFinite(amount) || amount <= 0 || amount > 10_000e6) {
|
|
622
|
-
failure(res, 400, 'amountUsdc must be between 0 and 10000');
|
|
623
|
-
return;
|
|
624
|
-
}
|
|
625
|
-
try {
|
|
626
|
-
const tx = await opts.wallet.mint(BigInt(amount));
|
|
627
|
-
json(res, 200, { ok: true, tx, amountUsdc: Number(raw) });
|
|
628
|
-
}
|
|
629
|
-
catch (error) {
|
|
630
|
-
failure(res, 500, error instanceof Error ? error.message : String(error));
|
|
631
|
-
}
|
|
632
|
-
return;
|
|
633
|
-
}
|
|
634
|
-
failure(res, 404, 'unknown wallet action');
|
|
635
|
-
return;
|
|
586
|
+
json(res, reply.status, reply.doc);
|
|
636
587
|
}
|
|
637
588
|
if (req.method === 'POST' && (path === '/genkaku/settings' || path === '/genkaku/settings/')) {
|
|
638
589
|
let body;
|
package/dist/cors.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
2
|
/**
|
|
3
|
-
* Apply CORS headers when the request comes from a
|
|
4
|
-
* @returns false when a browser origin outside
|
|
3
|
+
* Apply CORS headers when the request comes from a host allowed to show a panel.
|
|
4
|
+
* @returns false when a browser origin outside that set tried to call us.
|
|
5
5
|
*/
|
|
6
|
-
export declare function
|
|
6
|
+
export declare function applyPanelCors(req: IncomingMessage, res: ServerResponse): boolean;
|
|
7
7
|
//# sourceMappingURL=cors.d.ts.map
|
package/dist/cors.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cors.d.ts","sourceRoot":"","sources":["../src/cors.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cors.d.ts","sourceRoot":"","sources":["../src/cors.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAOhE;;;GAGG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAejF"}
|
package/dist/cors.js
CHANGED
|
@@ -1,21 +1,32 @@
|
|
|
1
|
-
// GENKAKU: CORS for the
|
|
1
|
+
// GENKAKU: CORS for the local control planes.
|
|
2
2
|
//
|
|
3
3
|
// The bridge and the worker status server answer on 127.0.0.1 and used to send
|
|
4
4
|
// `access-control-allow-origin: *`. Any website the user visited could then
|
|
5
|
-
// read wallet state and, once wallet endpoints existed, key material.
|
|
6
|
-
//
|
|
5
|
+
// read wallet state and, once wallet endpoints existed, key material. Now only
|
|
6
|
+
// the two hosts that actually render a panel may read these responses:
|
|
7
|
+
//
|
|
8
|
+
// - the DSH web app served from loopback (`dsh web` in a browser), and
|
|
9
|
+
// - the official DSH desktop app, whose renderer runs on `dsh-app://app`.
|
|
10
|
+
// Chromium sends that origin on every cross-origin fetch, and the app's own
|
|
11
|
+
// web route authenticates with the same string.
|
|
12
|
+
//
|
|
13
|
+
// `null` stays rejected. A sandboxed iframe on any website carries that origin,
|
|
14
|
+
// and file:// pages do too, so allowing it would hand every page the key
|
|
15
|
+
// material this gate exists to protect.
|
|
7
16
|
const LOOPBACK = /^https?:\/\/(127\.0\.0\.1|localhost|\[::1\])(:\d+)?$/;
|
|
17
|
+
/** Origin of the official DSH desktop renderer (Electron custom scheme). */
|
|
18
|
+
const DESKTOP_APP = 'dsh-app://app';
|
|
8
19
|
/**
|
|
9
|
-
* Apply CORS headers when the request comes from a
|
|
10
|
-
* @returns false when a browser origin outside
|
|
20
|
+
* Apply CORS headers when the request comes from a host allowed to show a panel.
|
|
21
|
+
* @returns false when a browser origin outside that set tried to call us.
|
|
11
22
|
*/
|
|
12
|
-
export function
|
|
23
|
+
export function applyPanelCors(req, res) {
|
|
13
24
|
const origin = req.headers.origin;
|
|
14
25
|
// curl, the HTML dashboard (same-origin navigation) and node clients send no
|
|
15
26
|
// Origin; they are not subject to the browser's cross-origin rules.
|
|
16
27
|
if (origin == null)
|
|
17
28
|
return true;
|
|
18
|
-
if (!LOOPBACK.test(origin))
|
|
29
|
+
if (!LOOPBACK.test(origin) && origin !== DESKTOP_APP)
|
|
19
30
|
return false;
|
|
20
31
|
res.setHeader('access-control-allow-origin', origin);
|
|
21
32
|
res.setHeader('vary', 'origin');
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA;AACzB,cAAc,aAAa,CAAA;AAC3B,cAAc,WAAW,CAAA;AACzB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA;AACzB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,SAAS,CAAA;AACvB,cAAc,kBAAkB,CAAA;AAChC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,YAAY,CAAA;AAC1B,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAC5B,cAAc,cAAc,CAAA;AAC5B,cAAc,mBAAmB,CAAA;AACjC,cAAc,kBAAkB,CAAA;AAChC,cAAc,eAAe,CAAA;AAC7B,cAAc,eAAe,CAAA;AAC7B,cAAc,UAAU,CAAA;AACxB,cAAc,aAAa,CAAA;AAC3B,cAAc,kBAAkB,CAAA;AAChC,cAAc,oBAAoB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA;AACzB,cAAc,aAAa,CAAA;AAC3B,cAAc,WAAW,CAAA;AACzB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA;AACzB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,SAAS,CAAA;AACvB,cAAc,kBAAkB,CAAA;AAChC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,YAAY,CAAA;AAC1B,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAC5B,cAAc,cAAc,CAAA;AAC5B,cAAc,mBAAmB,CAAA;AACjC,cAAc,kBAAkB,CAAA;AAChC,cAAc,eAAe,CAAA;AAC7B,cAAc,eAAe,CAAA;AAC7B,cAAc,UAAU,CAAA;AACxB,cAAc,aAAa,CAAA;AAC3B,cAAc,kBAAkB,CAAA;AAChC,cAAc,oBAAoB,CAAA;AAClC,cAAc,oBAAoB,CAAA"}
|
package/dist/index.js
CHANGED
package/dist/keys.d.ts
CHANGED
|
@@ -14,6 +14,8 @@ export type KeyVault = {
|
|
|
14
14
|
workerKey(): PrivKey;
|
|
15
15
|
/** replace the user key (import / rotate); refuses while an env key wins */
|
|
16
16
|
setUserKey(key: PrivKey): void;
|
|
17
|
+
/** replace the worker identity key (import / rotate); same env guard */
|
|
18
|
+
setWorkerKey(key: PrivKey): void;
|
|
17
19
|
/** derived addresses only (renderer-safe mirror of the keys) */
|
|
18
20
|
addresses(): {
|
|
19
21
|
user: EthAddress;
|
package/dist/keys.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../src/keys.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,MAAM,CAAA;AAG/B,MAAM,MAAM,OAAO,GAAG,GAAG,CAAA;AACzB,MAAM,MAAM,UAAU,GAAG,KAAK,MAAM,EAAE,CAAA;AAEtC;gFACgF;AAChF,MAAM,MAAM,QAAQ,GAAG;IACrB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAA;IAC9B,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAA;CAC9B,CAAA;AASD,MAAM,MAAM,QAAQ,GAAG;IACrB,2DAA2D;IAC3D,OAAO,IAAI,OAAO,CAAA;IAClB,6DAA6D;IAC7D,SAAS,IAAI,OAAO,CAAA;IACpB,4EAA4E;IAC5E,UAAU,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAAA;IAC9B,gEAAgE;IAChE,SAAS,IAAI;QAAE,IAAI,EAAE,UAAU,CAAC;QAAC,MAAM,EAAE,UAAU,CAAA;KAAE,CAAA;IACrD,qEAAqE;IACrE,QAAQ,CAAC,iBAAiB,EAAE,OAAO,CAAA;CACpC,CAAA;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE;IACnC,0EAA0E;IAC1E,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,CAAC,EAAE,QAAQ,CAAA;CACjB,GAAG,QAAQ,
|
|
1
|
+
{"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../src/keys.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,MAAM,CAAA;AAG/B,MAAM,MAAM,OAAO,GAAG,GAAG,CAAA;AACzB,MAAM,MAAM,UAAU,GAAG,KAAK,MAAM,EAAE,CAAA;AAEtC;gFACgF;AAChF,MAAM,MAAM,QAAQ,GAAG;IACrB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAA;IAC9B,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAA;CAC9B,CAAA;AASD,MAAM,MAAM,QAAQ,GAAG;IACrB,2DAA2D;IAC3D,OAAO,IAAI,OAAO,CAAA;IAClB,6DAA6D;IAC7D,SAAS,IAAI,OAAO,CAAA;IACpB,4EAA4E;IAC5E,UAAU,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAAA;IAC9B,wEAAwE;IACxE,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAAA;IAChC,gEAAgE;IAChE,SAAS,IAAI;QAAE,IAAI,EAAE,UAAU,CAAC;QAAC,MAAM,EAAE,UAAU,CAAA;KAAE,CAAA;IACrD,qEAAqE;IACrE,QAAQ,CAAC,iBAAiB,EAAE,OAAO,CAAA;CACpC,CAAA;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE;IACnC,0EAA0E;IAC1E,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,CAAC,EAAE,QAAQ,CAAA;CACjB,GAAG,QAAQ,CAmIX;AAED,gFAAgF;AAChF,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAQnD"}
|
package/dist/keys.js
CHANGED
|
@@ -108,6 +108,17 @@ export function createKeyVault(opts) {
|
|
|
108
108
|
vault.userKeyEnc = codec.encrypt(next).toString('base64');
|
|
109
109
|
persist();
|
|
110
110
|
},
|
|
111
|
+
setWorkerKey(key) {
|
|
112
|
+
// Same reasoning as setUserKey: an env key is process-scoped, so claiming
|
|
113
|
+
// a successful replacement while the old identity stays live would lie.
|
|
114
|
+
if (envWorker != null) {
|
|
115
|
+
throw new Error('the worker key comes from GENKAKU_WORKER_KEY; unset it before importing or rotating');
|
|
116
|
+
}
|
|
117
|
+
const next = normalizeKey(key);
|
|
118
|
+
workerKey = next;
|
|
119
|
+
vault.workerKeyEnc = codec.encrypt(next).toString('base64');
|
|
120
|
+
persist();
|
|
121
|
+
},
|
|
111
122
|
get usingEnvOverrides() {
|
|
112
123
|
return envUser != null || envWorker != null;
|
|
113
124
|
},
|
package/dist/status.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type WalletControl } from './walletControl.ts';
|
|
1
2
|
import type { RoleMode } from './modeManager.ts';
|
|
2
3
|
import type { OfferWire } from './offer.ts';
|
|
3
4
|
/** One offer row rendered by the dashboard. */
|
|
@@ -26,6 +27,11 @@ export type StatusSnapshot = {
|
|
|
26
27
|
transport?: 'direct' | 'relay' | 'lan';
|
|
27
28
|
/** Relay hops this worker reserved (seed VM), for the panel to explain relay mode. */
|
|
28
29
|
relayHints?: string[];
|
|
30
|
+
/** Live wallet figures, base units as decimal strings (wei / 6-dec USDC). */
|
|
31
|
+
balances?: {
|
|
32
|
+
eth: string;
|
|
33
|
+
usdc: string;
|
|
34
|
+
};
|
|
29
35
|
};
|
|
30
36
|
export type StatusHandle = {
|
|
31
37
|
url: string;
|
|
@@ -60,5 +66,19 @@ export type OllamaControl = {
|
|
|
60
66
|
remove: (model: string) => Promise<void>;
|
|
61
67
|
};
|
|
62
68
|
/** Serve the dashboard and live status JSON on 127.0.0.1. */
|
|
63
|
-
|
|
69
|
+
/** Everything the worker dashboard can drive. Named fields rather than a long
|
|
70
|
+
* positional list: the wallet joined five other callbacks, and a positional
|
|
71
|
+
* boolean trade at the end is how panels end up wiring the wrong handler. */
|
|
72
|
+
export type StatusServerOptions = {
|
|
73
|
+
port: number;
|
|
74
|
+
snapshot: () => StatusSnapshot | Promise<StatusSnapshot>;
|
|
75
|
+
onCatalog?: CatalogUpdateHandler;
|
|
76
|
+
onMode?: ModeUpdateHandler;
|
|
77
|
+
ollama?: OllamaControl;
|
|
78
|
+
onPublish?: PublishUpdateHandler;
|
|
79
|
+
/** Worker identity wallet: export/import/rotate. No mint — a worker earns
|
|
80
|
+
* USDC and only ever needs gas, which it cannot buy from a faucet button. */
|
|
81
|
+
wallet?: WalletControl;
|
|
82
|
+
};
|
|
83
|
+
export declare function startStatusServer(opts: StatusServerOptions): Promise<StatusHandle>;
|
|
64
84
|
//# sourceMappingURL=status.d.ts.map
|
package/dist/status.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../src/status.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAChD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAE3C,+CAA+C;AAC/C,MAAM,MAAM,WAAW,GAAG;IACxB,0EAA0E;IAC1E,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,SAAS,CAAA;CACjB,CAAA;AAED,0EAA0E;AAC1E,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,EAAE,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,WAAW,EAAE,CAAA;IACrB,cAAc,EAAE,MAAM,CAAA;IACtB,4EAA4E;IAC5E,SAAS,EAAE,OAAO,CAAA;IAClB,2EAA2E;IAC3E,cAAc,EAAE,OAAO,CAAA;IACvB,+EAA+E;IAC/E,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,oFAAoF;IACpF,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,iFAAiF;IACjF,SAAS,CAAC,EAAE,QAAQ,GAAG,OAAO,GAAG,KAAK,CAAA;IACtC,sFAAsF;IACtF,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../src/status.ts"],"names":[],"mappings":"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"}
|
package/dist/status.js
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
// GET /genkaku/status (JSON). Snapshot is called per request so the page and
|
|
3
3
|
// the JSON always reflect the live sidecar, never a boot-time copy.
|
|
4
4
|
import { createServer } from 'node:http';
|
|
5
|
-
import {
|
|
5
|
+
import { applyPanelCors } from "./cors.js";
|
|
6
|
+
import { runWalletAction } from "./walletControl.js";
|
|
6
7
|
/** Self-contained dashboard; mirrors the user bridge style and polls the JSON. */
|
|
7
8
|
const DASHBOARD_HTML = `<!doctype html>
|
|
8
9
|
<html lang="en">
|
|
@@ -165,13 +166,13 @@ function parseCatalog(body) {
|
|
|
165
166
|
}
|
|
166
167
|
return { ok: true, catalog };
|
|
167
168
|
}
|
|
168
|
-
|
|
169
|
-
|
|
169
|
+
export async function startStatusServer(opts) {
|
|
170
|
+
const { port, snapshot, onCatalog, onMode, ollama, onPublish, wallet } = opts;
|
|
170
171
|
const server = createServer((req, res) => {
|
|
171
172
|
void (async () => {
|
|
172
173
|
try {
|
|
173
174
|
const path = (req.url ?? '/').split('?')[0] ?? '/';
|
|
174
|
-
if (!
|
|
175
|
+
if (!applyPanelCors(req, res)) {
|
|
175
176
|
res.writeHead(403, { 'content-type': 'application/json' });
|
|
176
177
|
res.end(JSON.stringify({ error: { message: 'origin not allowed' } }));
|
|
177
178
|
return;
|
|
@@ -246,6 +247,28 @@ export async function startStatusServer(port, snapshot, onCatalog, onMode, ollam
|
|
|
246
247
|
json(res, { ok: true });
|
|
247
248
|
return;
|
|
248
249
|
}
|
|
250
|
+
if (req.method === 'POST' && path.startsWith('/genkaku/wallet/')) {
|
|
251
|
+
const action = path.slice('/genkaku/wallet/'.length).replace(/\/$/, '');
|
|
252
|
+
if (wallet == null) {
|
|
253
|
+
failure(res, 501, 'wallet operations are not enabled in this role');
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
let body = {};
|
|
257
|
+
try {
|
|
258
|
+
body = await readJson(req);
|
|
259
|
+
}
|
|
260
|
+
catch {
|
|
261
|
+
failure(res, 400, 'invalid JSON body');
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
const reply = await runWalletAction(action, body, wallet);
|
|
265
|
+
res.writeHead(reply.status, {
|
|
266
|
+
'content-type': 'application/json',
|
|
267
|
+
...(action === 'export' ? { 'cache-control': 'no-store' } : {}),
|
|
268
|
+
});
|
|
269
|
+
res.end(JSON.stringify(reply.doc));
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
249
272
|
if (path === '/genkaku/ollama' || path === '/genkaku/ollama/') {
|
|
250
273
|
if (ollama == null) {
|
|
251
274
|
failure(res, 501, 'ollama onboarding is not enabled in this role');
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Wallet operations a control plane may offer. */
|
|
2
|
+
export type WalletControl = {
|
|
3
|
+
address: () => string;
|
|
4
|
+
exportKey: () => string;
|
|
5
|
+
importKey: (privateKey: string) => void | Promise<void>;
|
|
6
|
+
rotate: () => void | Promise<void>;
|
|
7
|
+
/** base-sepolia only; mints MockUSDC to the panel's own address */
|
|
8
|
+
mint?: (amount: bigint) => Promise<string>;
|
|
9
|
+
};
|
|
10
|
+
/** One action's HTTP answer: status code plus JSON body. */
|
|
11
|
+
export type WalletActionReply = {
|
|
12
|
+
status: number;
|
|
13
|
+
doc: Record<string, unknown>;
|
|
14
|
+
};
|
|
15
|
+
/** Validate and run one `/genkaku/wallet/<action>` call. */
|
|
16
|
+
export declare function runWalletAction(action: string, body: Record<string, unknown>, wallet: WalletControl): Promise<WalletActionReply>;
|
|
17
|
+
//# sourceMappingURL=walletControl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"walletControl.d.ts","sourceRoot":"","sources":["../src/walletControl.ts"],"names":[],"mappings":"AAYA,mDAAmD;AACnD,MAAM,MAAM,aAAa,GAAG;IAC1B,OAAO,EAAE,MAAM,MAAM,CAAA;IACrB,SAAS,EAAE,MAAM,MAAM,CAAA;IACvB,SAAS,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACvD,MAAM,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAClC,mEAAmE;IACnE,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;CAC3C,CAAA;AAED,4DAA4D;AAC5D,MAAM,MAAM,iBAAiB,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAA;AAEhF,4DAA4D;AAC5D,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,iBAAiB,CAAC,CAwD5B"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// GENKAKU: wallet operations shared by both control planes.
|
|
2
|
+
//
|
|
3
|
+
// The user bridge and the worker status server expose the same four actions —
|
|
4
|
+
// export, import, new, mint — against different keys: the buyer's key signs
|
|
5
|
+
// escrows, the worker's key signs receipts and binds the directory entry. The
|
|
6
|
+
// HTTP shape and the safeguards are identical, so they live here instead of
|
|
7
|
+
// being copied into two request handlers that can drift apart.
|
|
8
|
+
//
|
|
9
|
+
// Key material only ever leaves over the panel CORS gate (see cors.ts), and
|
|
10
|
+
// export additionally requires `confirm: true` so a stray click — or a script
|
|
11
|
+
// that reached the port without intent — cannot read it.
|
|
12
|
+
/** Validate and run one `/genkaku/wallet/<action>` call. */
|
|
13
|
+
export async function runWalletAction(action, body, wallet) {
|
|
14
|
+
if (action === 'export') {
|
|
15
|
+
// The key IS the wallet: require an explicit acknowledgement.
|
|
16
|
+
if (body.confirm !== true)
|
|
17
|
+
return { status: 400, doc: { error: { message: 'export requires confirm: true' } } };
|
|
18
|
+
return {
|
|
19
|
+
status: 200,
|
|
20
|
+
doc: { ok: true, address: wallet.address(), privateKey: wallet.exportKey() },
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
if (action === 'import') {
|
|
24
|
+
const key = typeof body.privateKey === 'string' ? body.privateKey.trim() : '';
|
|
25
|
+
if (key === '')
|
|
26
|
+
return { status: 400, doc: { error: { message: 'privateKey is required' } } };
|
|
27
|
+
try {
|
|
28
|
+
await wallet.importKey(key);
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
return { status: 400, doc: { error: { message: error instanceof Error ? error.message : String(error) } } };
|
|
32
|
+
}
|
|
33
|
+
return { status: 200, doc: { ok: true, address: wallet.address(), restarting: true } };
|
|
34
|
+
}
|
|
35
|
+
if (action === 'new') {
|
|
36
|
+
try {
|
|
37
|
+
await wallet.rotate();
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
return { status: 500, doc: { error: { message: error instanceof Error ? error.message : String(error) } } };
|
|
41
|
+
}
|
|
42
|
+
return { status: 200, doc: { ok: true, address: wallet.address(), restarting: true } };
|
|
43
|
+
}
|
|
44
|
+
if (action === 'mint') {
|
|
45
|
+
if (wallet.mint == null) {
|
|
46
|
+
return { status: 400, doc: { error: { message: 'test USDC mint is only available on base-sepolia' } } };
|
|
47
|
+
}
|
|
48
|
+
const raw = body.amountUsdc === undefined ? '100' : String(body.amountUsdc);
|
|
49
|
+
const amount = Math.round(Number(raw) * 1e6);
|
|
50
|
+
if (!Number.isFinite(amount) || amount <= 0 || amount > 10_000e6) {
|
|
51
|
+
return { status: 400, doc: { error: { message: 'amountUsdc must be between 0 and 10000' } } };
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
const tx = await wallet.mint(BigInt(amount));
|
|
55
|
+
return { status: 200, doc: { ok: true, tx, amountUsdc: Number(raw) } };
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
return { status: 500, doc: { error: { message: error instanceof Error ? error.message : String(error) } } };
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return { status: 404, doc: { error: { message: 'unknown wallet action' } } };
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=walletControl.js.map
|
package/package.json
CHANGED