cyberdyne-mcp 0.6.6 → 0.6.7

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/onboard.js CHANGED
@@ -180,17 +180,39 @@ export async function onboard(env = process.env, opts = {}) {
180
180
  }
181
181
  // 5. persist BOTH the token and the wallet key atomically (0600)
182
182
  const configPath = saveTokenAndWallet(apiKey, privateKey);
183
- return { address, apiKey, generated, imported, configPath };
183
+ // 6. (optional) auto-link Bankr zero human interaction. If a bk_ key is supplied
184
+ // (opts or CYBERDYNE_BANKR_KEY), use it ONCE to connect the agent's Bankr project
185
+ // via the fresh cyb_ key. Best-effort: a failure never blocks onboarding, and the
186
+ // bk_ key is never stored (the backend uses it once and discards it).
187
+ let bankr;
188
+ const bankrKey = (opts.bankrKey ?? env.CYBERDYNE_BANKR_KEY ?? "").trim();
189
+ if (bankrKey.startsWith("bk_")) {
190
+ try {
191
+ const res = await fetch(`${apiUrl}/api/bankr/connect`, {
192
+ method: "POST",
193
+ headers: { "content-type": "application/json", accept: "application/json", authorization: `Bearer ${apiKey}` },
194
+ body: JSON.stringify({ bk_key: bankrKey }),
195
+ });
196
+ const j = (await res.json().catch(() => null));
197
+ bankr = res.ok && j?.ok
198
+ ? { connected: true, project: j.project?.projectName ?? null, hint: j.hint }
199
+ : { connected: false, hint: j?.hint ?? `connect failed (${res.status})` };
200
+ }
201
+ catch (e) {
202
+ bankr = { connected: false, hint: e instanceof Error ? e.message : "bankr connect error" };
203
+ }
204
+ }
205
+ return { address, apiKey, generated, imported, configPath, bankr };
184
206
  }
185
207
  /** The multi-line "next steps" block shared by the CLI + the MCP tool. */
186
208
  export function nextStepsText() {
187
209
  return [
188
210
  "Next steps (no dashboard needed):",
189
- " 1. fund: get_deposit_address send USDC on Base to that address from this wallet → deposit({ tx_hash }).",
190
- " 2. post_task({ title, category, reward_usd, quantity, duration_min, difficulty }).",
211
+ " 1. Fund THIS wallet with USDC (or BNKR/GITLAWB) + a little ETH for gas on Base the non-custodial pool freezes the budget directly from your wallet (there is no platform treasury).",
212
+ " 2. post_task({ title, category, reward_usd, quantity }) returns the budget authorization + the deploy fee.",
191
213
  " 3. authorize_task (sign budget + pay deploy fee + freeze) → humans submit FCFS → poll get_task.",
192
214
  " 4. review_submission per pending submission (approve pays a unit; reject reopens it) → close_task refunds the rest.",
193
- "The same wallet auto-signs pool budgets (no env vars needed). Trustless backstop: `reclaim` recovers an unfilled budget yourself after the deadline.",
215
+ "The same wallet auto-signs pool budgets. Trustless backstop: `reclaim` recovers an unfilled budget yourself after the deadline.",
194
216
  ].join("\n");
195
217
  }
196
218
  // ── CLI front-end for `onboard` (import / create / prompt) ───────────────────
@@ -201,11 +223,12 @@ export function nextStepsText() {
201
223
  // --import (no value) read the secret from stdin (piped) or CYBERDYNE_IMPORT_KEY
202
224
  // --create force a fresh wallet
203
225
  // (none, interactive TTY) prompt: paste a key/mnemonic, or press enter to create
204
- /** Tiny flag reader for the onboard args (supports `--import`, `--import=x`, `--create`). */
226
+ /** Tiny flag reader for the onboard args (`--import`, `--import=x`, `--create`, `--bankr`). */
205
227
  function parseOnboardFlags(argv) {
206
228
  let importFlag = false;
207
229
  let importValue;
208
230
  let create = false;
231
+ let bankrValue;
209
232
  for (let i = 0; i < argv.length; i++) {
210
233
  const tok = argv[i];
211
234
  if (tok === "--create")
@@ -222,8 +245,18 @@ function parseOnboardFlags(argv) {
222
245
  importFlag = true;
223
246
  importValue = tok.slice("--import=".length);
224
247
  }
248
+ else if (tok === "--bankr") {
249
+ const next = argv[i + 1];
250
+ if (next !== undefined && !next.startsWith("--")) {
251
+ bankrValue = next;
252
+ i++;
253
+ }
254
+ }
255
+ else if (tok.startsWith("--bankr=")) {
256
+ bankrValue = tok.slice("--bankr=".length);
257
+ }
225
258
  }
226
- return { importFlag, importValue, create };
259
+ return { importFlag, importValue, create, bankrValue };
227
260
  }
228
261
  /** Read a single line from stdin (used for the interactive import/create prompt). */
229
262
  function promptLine(question) {
@@ -244,6 +277,8 @@ export const ONBOARD_USAGE = [
244
277
  " CYBERDYNE_IMPORT_KEY=0x<key> npx cyberdyne-mcp onboard --import",
245
278
  " (passing it as an argument leaves the secret in your shell history.)",
246
279
  " --create generate a fresh wallet (default in a non-interactive / CI shell).",
280
+ " --bankr <bk_key> auto-link your Bankr project at onboard (or set CYBERDYNE_BANKR_KEY).",
281
+ " The bk_ key is used ONCE server-side and never stored.",
247
282
  " (no flag, in a terminal) you'll be prompted: paste a key/mnemonic, or press enter to create.",
248
283
  "",
249
284
  "Either way: SIWE sign-in → mint your cyb_ key → save wallet + key to ~/.cyberdyne/config.json (0600).",
@@ -254,9 +289,11 @@ export const ONBOARD_USAGE = [
254
289
  * piped stdin (with --import) → CYBERDYNE_IMPORT_KEY → --import <value> argv → TTY prompt.
255
290
  */
256
291
  export async function onboardCli(argv, env = process.env) {
257
- const { importFlag, importValue, create } = parseOnboardFlags(argv);
292
+ const { importFlag, importValue, create, bankrValue } = parseOnboardFlags(argv);
293
+ // Bankr auto-link key: --bankr <bk_…> (or env CYBERDYNE_BANKR_KEY, read inside onboard()).
294
+ const bankrKey = bankrValue?.trim() || undefined;
258
295
  if (create)
259
- return onboard(env, { forceCreate: true });
296
+ return onboard(env, { forceCreate: true, bankrKey });
260
297
  // Determine the import secret, if the user asked to import.
261
298
  let secret;
262
299
  let fromArgv = false;
@@ -300,7 +337,7 @@ export async function onboardCli(argv, env = process.env) {
300
337
  if (secret) {
301
338
  // Validate early with a clear error before any network call.
302
339
  privateKeyFromSecret(secret);
303
- return onboard(env, { importSecret: secret });
340
+ return onboard(env, { importSecret: secret, bankrKey });
304
341
  }
305
- return onboard(env); // create / reuse-saved, as before
342
+ return onboard(env, { bankrKey }); // create / reuse-saved, as before
306
343
  }
package/dist/server.js CHANGED
@@ -70,6 +70,9 @@ if (process.argv[2] === "onboard") {
70
70
  : r.imported
71
71
  ? `\n (your imported wallet private key was saved to ${r.configPath}; keep that file safe)`
72
72
  : "") +
73
+ (r.bankr
74
+ ? `\n Bankr : ${r.bankr.connected ? `connected${r.bankr.project ? ` · ${r.bankr.project}` : ""}` : "not connected"}${r.bankr.hint ? ` (${r.bankr.hint})` : ""}`
75
+ : "") +
73
76
  `\n\n${nextStepsText()}` +
74
77
  `\n\nThis MCP is already configured for this agent — networked tools will use the saved key.`);
75
78
  process.exit(0);
@@ -148,7 +151,7 @@ async function guard(fn) {
148
151
  }
149
152
  }
150
153
  // ---- Server ---------------------------------------------------------------
151
- const server = new McpServer({ name: "cyberdyne", version: "0.6.6" });
154
+ const server = new McpServer({ name: "cyberdyne", version: "0.6.7" });
152
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 }))));
153
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 () => {
154
157
  const r = await onboard();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cyberdyne-mcp",
3
- "version": "0.6.6",
3
+ "version": "0.6.7",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
package/src/onboard.ts CHANGED
@@ -137,6 +137,8 @@ export interface OnboardResult {
137
137
  generated: boolean;
138
138
  imported: boolean;
139
139
  configPath: string;
140
+ /** Bankr auto-link outcome, when a bk_ key was supplied. Absent if none. */
141
+ bankr?: { connected: boolean; project?: string | null; hint?: string };
140
142
  }
141
143
 
142
144
  export interface OnboardOptions {
@@ -144,6 +146,10 @@ export interface OnboardOptions {
144
146
  importSecret?: string;
145
147
  /** Force a brand-new wallet even if env/config already has one (`onboard --create`). */
146
148
  forceCreate?: boolean;
149
+ /** Optional Bankr `bk_` key: auto-link the agent's Bankr project at onboard, zero
150
+ * human interaction. Used ONCE to call POST /api/bankr/connect with the fresh cyb_
151
+ * key; never stored. Falls back to env CYBERDYNE_BANKR_KEY. */
152
+ bankrKey?: string;
147
153
  }
148
154
 
149
155
  /**
@@ -223,18 +229,40 @@ export async function onboard(
223
229
  // 5. persist BOTH the token and the wallet key atomically (0600)
224
230
  const configPath = saveTokenAndWallet(apiKey, privateKey);
225
231
 
226
- return { address, apiKey, generated, imported, configPath };
232
+ // 6. (optional) auto-link Bankr zero human interaction. If a bk_ key is supplied
233
+ // (opts or CYBERDYNE_BANKR_KEY), use it ONCE to connect the agent's Bankr project
234
+ // via the fresh cyb_ key. Best-effort: a failure never blocks onboarding, and the
235
+ // bk_ key is never stored (the backend uses it once and discards it).
236
+ let bankr: OnboardResult["bankr"];
237
+ const bankrKey = (opts.bankrKey ?? env.CYBERDYNE_BANKR_KEY ?? "").trim();
238
+ if (bankrKey.startsWith("bk_")) {
239
+ try {
240
+ const res = await fetch(`${apiUrl}/api/bankr/connect`, {
241
+ method: "POST",
242
+ headers: { "content-type": "application/json", accept: "application/json", authorization: `Bearer ${apiKey}` },
243
+ body: JSON.stringify({ bk_key: bankrKey }),
244
+ });
245
+ const j = (await res.json().catch(() => null)) as { ok?: boolean; project?: { projectName?: string } | null; hint?: string } | null;
246
+ bankr = res.ok && j?.ok
247
+ ? { connected: true, project: j.project?.projectName ?? null, hint: j.hint }
248
+ : { connected: false, hint: j?.hint ?? `connect failed (${res.status})` };
249
+ } catch (e) {
250
+ bankr = { connected: false, hint: e instanceof Error ? e.message : "bankr connect error" };
251
+ }
252
+ }
253
+
254
+ return { address, apiKey, generated, imported, configPath, bankr };
227
255
  }
228
256
 
229
257
  /** The multi-line "next steps" block shared by the CLI + the MCP tool. */
230
258
  export function nextStepsText(): string {
231
259
  return [
232
260
  "Next steps (no dashboard needed):",
233
- " 1. fund: get_deposit_address send USDC on Base to that address from this wallet → deposit({ tx_hash }).",
234
- " 2. post_task({ title, category, reward_usd, quantity, duration_min, difficulty }).",
261
+ " 1. Fund THIS wallet with USDC (or BNKR/GITLAWB) + a little ETH for gas on Base the non-custodial pool freezes the budget directly from your wallet (there is no platform treasury).",
262
+ " 2. post_task({ title, category, reward_usd, quantity }) returns the budget authorization + the deploy fee.",
235
263
  " 3. authorize_task (sign budget + pay deploy fee + freeze) → humans submit FCFS → poll get_task.",
236
264
  " 4. review_submission per pending submission (approve pays a unit; reject reopens it) → close_task refunds the rest.",
237
- "The same wallet auto-signs pool budgets (no env vars needed). Trustless backstop: `reclaim` recovers an unfilled budget yourself after the deadline.",
265
+ "The same wallet auto-signs pool budgets. Trustless backstop: `reclaim` recovers an unfilled budget yourself after the deadline.",
238
266
  ].join("\n");
239
267
  }
240
268
 
@@ -247,11 +275,12 @@ export function nextStepsText(): string {
247
275
  // --create force a fresh wallet
248
276
  // (none, interactive TTY) prompt: paste a key/mnemonic, or press enter to create
249
277
 
250
- /** Tiny flag reader for the onboard args (supports `--import`, `--import=x`, `--create`). */
251
- function parseOnboardFlags(argv: string[]): { importFlag: boolean; importValue?: string; create: boolean } {
278
+ /** Tiny flag reader for the onboard args (`--import`, `--import=x`, `--create`, `--bankr`). */
279
+ function parseOnboardFlags(argv: string[]): { importFlag: boolean; importValue?: string; create: boolean; bankrValue?: string } {
252
280
  let importFlag = false;
253
281
  let importValue: string | undefined;
254
282
  let create = false;
283
+ let bankrValue: string | undefined;
255
284
  for (let i = 0; i < argv.length; i++) {
256
285
  const tok = argv[i];
257
286
  if (tok === "--create") create = true;
@@ -265,9 +294,17 @@ function parseOnboardFlags(argv: string[]): { importFlag: boolean; importValue?:
265
294
  } else if (tok.startsWith("--import=")) {
266
295
  importFlag = true;
267
296
  importValue = tok.slice("--import=".length);
297
+ } else if (tok === "--bankr") {
298
+ const next = argv[i + 1];
299
+ if (next !== undefined && !next.startsWith("--")) {
300
+ bankrValue = next;
301
+ i++;
302
+ }
303
+ } else if (tok.startsWith("--bankr=")) {
304
+ bankrValue = tok.slice("--bankr=".length);
268
305
  }
269
306
  }
270
- return { importFlag, importValue, create };
307
+ return { importFlag, importValue, create, bankrValue };
271
308
  }
272
309
 
273
310
  /** Read a single line from stdin (used for the interactive import/create prompt). */
@@ -290,6 +327,8 @@ export const ONBOARD_USAGE = [
290
327
  " CYBERDYNE_IMPORT_KEY=0x<key> npx cyberdyne-mcp onboard --import",
291
328
  " (passing it as an argument leaves the secret in your shell history.)",
292
329
  " --create generate a fresh wallet (default in a non-interactive / CI shell).",
330
+ " --bankr <bk_key> auto-link your Bankr project at onboard (or set CYBERDYNE_BANKR_KEY).",
331
+ " The bk_ key is used ONCE server-side and never stored.",
293
332
  " (no flag, in a terminal) you'll be prompted: paste a key/mnemonic, or press enter to create.",
294
333
  "",
295
334
  "Either way: SIWE sign-in → mint your cyb_ key → save wallet + key to ~/.cyberdyne/config.json (0600).",
@@ -301,9 +340,11 @@ export const ONBOARD_USAGE = [
301
340
  * piped stdin (with --import) → CYBERDYNE_IMPORT_KEY → --import <value> argv → TTY prompt.
302
341
  */
303
342
  export async function onboardCli(argv: string[], env: NodeJS.ProcessEnv = process.env): Promise<OnboardResult> {
304
- const { importFlag, importValue, create } = parseOnboardFlags(argv);
343
+ const { importFlag, importValue, create, bankrValue } = parseOnboardFlags(argv);
344
+ // Bankr auto-link key: --bankr <bk_…> (or env CYBERDYNE_BANKR_KEY, read inside onboard()).
345
+ const bankrKey = bankrValue?.trim() || undefined;
305
346
 
306
- if (create) return onboard(env, { forceCreate: true });
347
+ if (create) return onboard(env, { forceCreate: true, bankrKey });
307
348
 
308
349
  // Determine the import secret, if the user asked to import.
309
350
  let secret: string | undefined;
@@ -351,7 +392,7 @@ export async function onboardCli(argv: string[], env: NodeJS.ProcessEnv = proces
351
392
  if (secret) {
352
393
  // Validate early with a clear error before any network call.
353
394
  privateKeyFromSecret(secret);
354
- return onboard(env, { importSecret: secret });
395
+ return onboard(env, { importSecret: secret, bankrKey });
355
396
  }
356
- return onboard(env); // create / reuse-saved, as before
397
+ return onboard(env, { bankrKey }); // create / reuse-saved, as before
357
398
  }
package/src/server.ts CHANGED
@@ -72,6 +72,9 @@ if (process.argv[2] === "onboard") {
72
72
  : r.imported
73
73
  ? `\n (your imported wallet private key was saved to ${r.configPath}; keep that file safe)`
74
74
  : "") +
75
+ (r.bankr
76
+ ? `\n Bankr : ${r.bankr.connected ? `connected${r.bankr.project ? ` · ${r.bankr.project}` : ""}` : "not connected"}${r.bankr.hint ? ` (${r.bankr.hint})` : ""}`
77
+ : "") +
75
78
  `\n\n${nextStepsText()}` +
76
79
  `\n\nThis MCP is already configured for this agent — networked tools will use the saved key.`,
77
80
  );
@@ -160,7 +163,7 @@ async function guard<T>(fn: () => Promise<T>) {
160
163
 
161
164
  // ---- Server ---------------------------------------------------------------
162
165
 
163
- const server = new McpServer({ name: "cyberdyne", version: "0.6.6" });
166
+ const server = new McpServer({ name: "cyberdyne", version: "0.6.7" });
164
167
 
165
168
  server.tool(
166
169
  "list_categories",