cyberdyne-mcp 0.6.7 → 0.6.9
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/cli.js +2 -2
- package/dist/evm-signer.js +4 -1
- package/dist/onboard.js +30 -3
- package/dist/server.js +2 -2
- package/package.json +1 -1
- package/src/cli.ts +3 -3
- package/src/evm-signer.ts +8 -2
- package/src/onboard.ts +28 -3
- package/src/server.ts +3 -3
package/dist/cli.js
CHANGED
|
@@ -133,8 +133,8 @@ export async function runPost(argv) {
|
|
|
133
133
|
console.error("→ signing the budget authorization…");
|
|
134
134
|
const signedPayment = await signAuthCapture(requirements);
|
|
135
135
|
const fee = res.deployFee;
|
|
136
|
-
console.error(`→ paying the deploy fee (${
|
|
137
|
-
const feeTx = await payDeployFee({
|
|
136
|
+
console.error(`→ paying the deploy fee (${fee.amount} ${fee.token} ≈ ${usd(fee.usd)}) from your wallet…`);
|
|
137
|
+
const feeTx = await payDeployFee({ amount: fee.amount, decimals: fee.decimals, recipient: fee.recipient, token: fee.token });
|
|
138
138
|
console.error(` ✓ fee paid — ${feeTx}`);
|
|
139
139
|
console.error("→ freezing the budget (authorize)…");
|
|
140
140
|
const authed = await c.rest("POST", `/api/tasks/${taskId}/authorize`, { body: { signedPayment, fee_tx_hash: feeTx } });
|
package/dist/evm-signer.js
CHANGED
|
@@ -55,11 +55,14 @@ const ERC20_TRANSFER_ABI = [
|
|
|
55
55
|
*/
|
|
56
56
|
export async function payDeployFee(params) {
|
|
57
57
|
const wallet = createWalletClient({ account: account(), chain: chain(), transport: http(process.env.CYBERDYNE_RPC_URL) });
|
|
58
|
+
// Scale by the TOKEN's decimals, not a hardcoded 6. Using 6 for an 18-decimal
|
|
59
|
+
// token (BNKR/GITLAWB) underpaid the fee 10^12× → permanent fee_unverified.
|
|
60
|
+
const value = parseUnits(params.amount.toFixed(params.decimals), params.decimals);
|
|
58
61
|
const hash = await wallet.writeContract({
|
|
59
62
|
address: params.token,
|
|
60
63
|
abi: ERC20_TRANSFER_ABI,
|
|
61
64
|
functionName: "transfer",
|
|
62
|
-
args: [params.recipient,
|
|
65
|
+
args: [params.recipient, value],
|
|
63
66
|
chain: chain(),
|
|
64
67
|
});
|
|
65
68
|
// Wait until the fee tx is ≥1 block deep BEFORE returning, so the platform's
|
package/dist/onboard.js
CHANGED
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
* a DB row. The wallet private key is persisted but NEVER logged to stdout.
|
|
18
18
|
*/
|
|
19
19
|
import { readFileSync } from "node:fs";
|
|
20
|
+
import { homedir } from "node:os";
|
|
21
|
+
import { join } from "node:path";
|
|
20
22
|
import { createInterface } from "node:readline";
|
|
21
23
|
import { generatePrivateKey, privateKeyToAccount, mnemonicToAccount } from "viem/accounts";
|
|
22
24
|
import { bytesToHex } from "viem";
|
|
@@ -111,6 +113,29 @@ function collectCookies(res, jar) {
|
|
|
111
113
|
function cookieHeader(jar) {
|
|
112
114
|
return [...jar.entries()].map(([k, v]) => `${k}=${v}`).join("; ");
|
|
113
115
|
}
|
|
116
|
+
/**
|
|
117
|
+
* Auto-discover the agent's Bankr `bk_` key WITHOUT requiring a flag — so a plain
|
|
118
|
+
* `npx cyberdyne-mcp onboard` links Bankr whenever the agent already has Bankr set up.
|
|
119
|
+
* Order: CYBERDYNE_BANKR_KEY → BANKR_API_KEY (Bankr's own standard env var) →
|
|
120
|
+
* ~/.bankr/config.json (any `bk_`-prefixed value — the same config file Bankr's CLI/SDK use).
|
|
121
|
+
* Returns undefined if nothing is configured (then onboard just skips Bankr linking).
|
|
122
|
+
*/
|
|
123
|
+
function discoverBankrKey(env) {
|
|
124
|
+
const fromEnv = env.CYBERDYNE_BANKR_KEY?.trim() || env.BANKR_API_KEY?.trim();
|
|
125
|
+
if (fromEnv?.startsWith("bk_"))
|
|
126
|
+
return fromEnv;
|
|
127
|
+
try {
|
|
128
|
+
const cfg = JSON.parse(readFileSync(join(homedir(), ".bankr", "config.json"), "utf8"));
|
|
129
|
+
for (const v of Object.values(cfg)) {
|
|
130
|
+
if (typeof v === "string" && v.startsWith("bk_"))
|
|
131
|
+
return v.trim();
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
/* no ~/.bankr/config.json — fine, Bankr just isn't configured */
|
|
136
|
+
}
|
|
137
|
+
return undefined;
|
|
138
|
+
}
|
|
114
139
|
/**
|
|
115
140
|
* Run the full SIWE → mint chain and persist the credentials. Throws on any
|
|
116
141
|
* non-2xx step (with the endpoint + status) so the caller can surface a clear error.
|
|
@@ -185,7 +210,7 @@ export async function onboard(env = process.env, opts = {}) {
|
|
|
185
210
|
// via the fresh cyb_ key. Best-effort: a failure never blocks onboarding, and the
|
|
186
211
|
// bk_ key is never stored (the backend uses it once and discards it).
|
|
187
212
|
let bankr;
|
|
188
|
-
const bankrKey = (opts.bankrKey
|
|
213
|
+
const bankrKey = (opts.bankrKey?.trim() || discoverBankrKey(env) || "");
|
|
189
214
|
if (bankrKey.startsWith("bk_")) {
|
|
190
215
|
try {
|
|
191
216
|
const res = await fetch(`${apiUrl}/api/bankr/connect`, {
|
|
@@ -277,8 +302,10 @@ export const ONBOARD_USAGE = [
|
|
|
277
302
|
" CYBERDYNE_IMPORT_KEY=0x<key> npx cyberdyne-mcp onboard --import",
|
|
278
303
|
" (passing it as an argument leaves the secret in your shell history.)",
|
|
279
304
|
" --create generate a fresh wallet (default in a non-interactive / CI shell).",
|
|
280
|
-
" --bankr <bk_key>
|
|
281
|
-
"
|
|
305
|
+
" --bankr <bk_key> link your Bankr project at onboard. USUALLY UNNEEDED: onboard",
|
|
306
|
+
" auto-discovers your key from BANKR_API_KEY / ~/.bankr/config.json,",
|
|
307
|
+
" so a plain `onboard` links Bankr if you already use it. Used once,",
|
|
308
|
+
" server-side, and never stored.",
|
|
282
309
|
" (no flag, in a terminal) you'll be prompted: paste a key/mnemonic, or press enter to create.",
|
|
283
310
|
"",
|
|
284
311
|
"Either way: SIWE sign-in → mint your cyb_ key → save wallet + key to ~/.cyberdyne/config.json (0600).",
|
package/dist/server.js
CHANGED
|
@@ -151,7 +151,7 @@ async function guard(fn) {
|
|
|
151
151
|
}
|
|
152
152
|
}
|
|
153
153
|
// ---- Server ---------------------------------------------------------------
|
|
154
|
-
const server = new McpServer({ name: "cyberdyne", version: "0.6.
|
|
154
|
+
const server = new McpServer({ name: "cyberdyne", version: "0.6.8" });
|
|
155
155
|
server.tool("list_categories", "List the kinds of real-world work CYBERDYNE humans can do. Static (no network). Use this to learn the valid `category` values before posting a task.", {}, async () => json(Object.entries(CATEGORIES).map(([id, blurb]) => ({ id, blurb }))));
|
|
156
156
|
server.tool("onboard", "BOOTSTRAP (works WITHOUT an existing key — the one tool that self-onboards). Zero-browser: generates a fresh wallet if you don't have one, signs in to CYBERDYNE with it (SIWE), mints your `cyb_` agent API key, and saves both to ~/.cyberdyne/config.json (0600) so every other tool here authenticates automatically. No web dashboard, no env vars. Returns your wallet address, the cyb_ key (shown once), and the next steps (fund your WALLET with USDC + a little ETH for gas on Base → post_task → authorize_task → review_submission → close_task). The non-custodial pool freezes the budget directly from your wallet at deploy — there is no platform treasury to deposit into. The same generated wallet auto-signs pool budgets. To bring your OWN wallet instead, use the CLI: `npx cyberdyne-mcp onboard --import <0xKEY | mnemonic>` (or --create for a fresh one). Idempotent-ish: re-running with a saved wallet reuses it and mints a fresh key.", {}, async () => guard(async () => {
|
|
157
157
|
const r = await onboard();
|
|
@@ -209,7 +209,7 @@ server.tool("authorize_task", "Freeze the bounty budget on-chain (the second ste
|
|
|
209
209
|
}
|
|
210
210
|
if (!feeTx && deploy_fee) {
|
|
211
211
|
const f = deploy_fee;
|
|
212
|
-
feeTx = await payDeployFee({
|
|
212
|
+
feeTx = await payDeployFee({ amount: f.amount, decimals: f.decimals, recipient: f.recipient, token: f.token });
|
|
213
213
|
}
|
|
214
214
|
}
|
|
215
215
|
}
|
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -125,7 +125,7 @@ export async function runPost(argv: string[]): Promise<void> {
|
|
|
125
125
|
const res = await c.rest<{
|
|
126
126
|
task: { id: string; escrow_status?: string };
|
|
127
127
|
authIntent?: { requirements?: unknown };
|
|
128
|
-
deployFee?: { usd: number; recipient: string; token: string };
|
|
128
|
+
deployFee?: { amount: number; decimals: number; usd: number; recipient: string; token: string };
|
|
129
129
|
}>("POST", "/api/tasks", { body });
|
|
130
130
|
const taskId = res.task?.id;
|
|
131
131
|
console.error(` ✓ posted — task ${taskId}`);
|
|
@@ -155,8 +155,8 @@ export async function runPost(argv: string[]): Promise<void> {
|
|
|
155
155
|
const signedPayment = await signAuthCapture(requirements);
|
|
156
156
|
|
|
157
157
|
const fee = res.deployFee;
|
|
158
|
-
console.error(`→ paying the deploy fee (${
|
|
159
|
-
const feeTx = await payDeployFee({
|
|
158
|
+
console.error(`→ paying the deploy fee (${fee.amount} ${fee.token} ≈ ${usd(fee.usd)}) from your wallet…`);
|
|
159
|
+
const feeTx = await payDeployFee({ amount: fee.amount, decimals: fee.decimals, recipient: fee.recipient, token: fee.token });
|
|
160
160
|
console.error(` ✓ fee paid — ${feeTx}`);
|
|
161
161
|
|
|
162
162
|
console.error("→ freezing the budget (authorize)…");
|
package/src/evm-signer.ts
CHANGED
|
@@ -57,16 +57,22 @@ const ERC20_TRANSFER_ABI = [
|
|
|
57
57
|
* this gas (CYBERDYNE absorbs none). USDC is 6-dp (v1 pool settles in USDC).
|
|
58
58
|
*/
|
|
59
59
|
export async function payDeployFee(params: {
|
|
60
|
-
|
|
60
|
+
/** Fee in the FEE TOKEN's own units (NOT USD). The platform pins this at post. */
|
|
61
|
+
amount: number;
|
|
62
|
+
/** The fee token's ERC-20 decimals (USDC=6, BNKR/GITLAWB=18, dynamic varies). */
|
|
63
|
+
decimals: number;
|
|
61
64
|
recipient: string;
|
|
62
65
|
token: string;
|
|
63
66
|
}): Promise<string> {
|
|
64
67
|
const wallet = createWalletClient({ account: account(), chain: chain(), transport: http(process.env.CYBERDYNE_RPC_URL) });
|
|
68
|
+
// Scale by the TOKEN's decimals, not a hardcoded 6. Using 6 for an 18-decimal
|
|
69
|
+
// token (BNKR/GITLAWB) underpaid the fee 10^12× → permanent fee_unverified.
|
|
70
|
+
const value = parseUnits(params.amount.toFixed(params.decimals), params.decimals);
|
|
65
71
|
const hash = await wallet.writeContract({
|
|
66
72
|
address: params.token as `0x${string}`,
|
|
67
73
|
abi: ERC20_TRANSFER_ABI,
|
|
68
74
|
functionName: "transfer",
|
|
69
|
-
args: [params.recipient as `0x${string}`,
|
|
75
|
+
args: [params.recipient as `0x${string}`, value],
|
|
70
76
|
chain: chain(),
|
|
71
77
|
});
|
|
72
78
|
// Wait until the fee tx is ≥1 block deep BEFORE returning, so the platform's
|
package/src/onboard.ts
CHANGED
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
* a DB row. The wallet private key is persisted but NEVER logged to stdout.
|
|
18
18
|
*/
|
|
19
19
|
import { readFileSync } from "node:fs";
|
|
20
|
+
import { homedir } from "node:os";
|
|
21
|
+
import { join } from "node:path";
|
|
20
22
|
import { createInterface } from "node:readline";
|
|
21
23
|
import { generatePrivateKey, privateKeyToAccount, mnemonicToAccount } from "viem/accounts";
|
|
22
24
|
import { bytesToHex } from "viem";
|
|
@@ -131,6 +133,27 @@ function cookieHeader(jar: Map<string, string>): string {
|
|
|
131
133
|
return [...jar.entries()].map(([k, v]) => `${k}=${v}`).join("; ");
|
|
132
134
|
}
|
|
133
135
|
|
|
136
|
+
/**
|
|
137
|
+
* Auto-discover the agent's Bankr `bk_` key WITHOUT requiring a flag — so a plain
|
|
138
|
+
* `npx cyberdyne-mcp onboard` links Bankr whenever the agent already has Bankr set up.
|
|
139
|
+
* Order: CYBERDYNE_BANKR_KEY → BANKR_API_KEY (Bankr's own standard env var) →
|
|
140
|
+
* ~/.bankr/config.json (any `bk_`-prefixed value — the same config file Bankr's CLI/SDK use).
|
|
141
|
+
* Returns undefined if nothing is configured (then onboard just skips Bankr linking).
|
|
142
|
+
*/
|
|
143
|
+
function discoverBankrKey(env: NodeJS.ProcessEnv): string | undefined {
|
|
144
|
+
const fromEnv = env.CYBERDYNE_BANKR_KEY?.trim() || env.BANKR_API_KEY?.trim();
|
|
145
|
+
if (fromEnv?.startsWith("bk_")) return fromEnv;
|
|
146
|
+
try {
|
|
147
|
+
const cfg = JSON.parse(readFileSync(join(homedir(), ".bankr", "config.json"), "utf8")) as Record<string, unknown>;
|
|
148
|
+
for (const v of Object.values(cfg)) {
|
|
149
|
+
if (typeof v === "string" && v.startsWith("bk_")) return v.trim();
|
|
150
|
+
}
|
|
151
|
+
} catch {
|
|
152
|
+
/* no ~/.bankr/config.json — fine, Bankr just isn't configured */
|
|
153
|
+
}
|
|
154
|
+
return undefined;
|
|
155
|
+
}
|
|
156
|
+
|
|
134
157
|
export interface OnboardResult {
|
|
135
158
|
address: string;
|
|
136
159
|
apiKey: string;
|
|
@@ -234,7 +257,7 @@ export async function onboard(
|
|
|
234
257
|
// via the fresh cyb_ key. Best-effort: a failure never blocks onboarding, and the
|
|
235
258
|
// bk_ key is never stored (the backend uses it once and discards it).
|
|
236
259
|
let bankr: OnboardResult["bankr"];
|
|
237
|
-
const bankrKey = (opts.bankrKey
|
|
260
|
+
const bankrKey = (opts.bankrKey?.trim() || discoverBankrKey(env) || "");
|
|
238
261
|
if (bankrKey.startsWith("bk_")) {
|
|
239
262
|
try {
|
|
240
263
|
const res = await fetch(`${apiUrl}/api/bankr/connect`, {
|
|
@@ -327,8 +350,10 @@ export const ONBOARD_USAGE = [
|
|
|
327
350
|
" CYBERDYNE_IMPORT_KEY=0x<key> npx cyberdyne-mcp onboard --import",
|
|
328
351
|
" (passing it as an argument leaves the secret in your shell history.)",
|
|
329
352
|
" --create generate a fresh wallet (default in a non-interactive / CI shell).",
|
|
330
|
-
" --bankr <bk_key>
|
|
331
|
-
"
|
|
353
|
+
" --bankr <bk_key> link your Bankr project at onboard. USUALLY UNNEEDED: onboard",
|
|
354
|
+
" auto-discovers your key from BANKR_API_KEY / ~/.bankr/config.json,",
|
|
355
|
+
" so a plain `onboard` links Bankr if you already use it. Used once,",
|
|
356
|
+
" server-side, and never stored.",
|
|
332
357
|
" (no flag, in a terminal) you'll be prompted: paste a key/mnemonic, or press enter to create.",
|
|
333
358
|
"",
|
|
334
359
|
"Either way: SIWE sign-in → mint your cyb_ key → save wallet + key to ~/.cyberdyne/config.json (0600).",
|
package/src/server.ts
CHANGED
|
@@ -163,7 +163,7 @@ async function guard<T>(fn: () => Promise<T>) {
|
|
|
163
163
|
|
|
164
164
|
// ---- Server ---------------------------------------------------------------
|
|
165
165
|
|
|
166
|
-
const server = new McpServer({ name: "cyberdyne", version: "0.6.
|
|
166
|
+
const server = new McpServer({ name: "cyberdyne", version: "0.6.8" });
|
|
167
167
|
|
|
168
168
|
server.tool(
|
|
169
169
|
"list_categories",
|
|
@@ -254,8 +254,8 @@ server.tool(
|
|
|
254
254
|
payload = await signAuthCapture(requirements);
|
|
255
255
|
}
|
|
256
256
|
if (!feeTx && deploy_fee) {
|
|
257
|
-
const f = deploy_fee as { usd: number; recipient: string; token: string };
|
|
258
|
-
feeTx = await payDeployFee({
|
|
257
|
+
const f = deploy_fee as { amount: number; decimals: number; usd: number; recipient: string; token: string };
|
|
258
|
+
feeTx = await payDeployFee({ amount: f.amount, decimals: f.decimals, recipient: f.recipient, token: f.token });
|
|
259
259
|
}
|
|
260
260
|
}
|
|
261
261
|
}
|