@boole-digital/cli 0.2.3 → 0.2.5

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/dist/index.js +45 -7
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -347,10 +347,32 @@ var BooleApi = class {
347
347
  }
348
348
  // Returns the plaintext SSH password for a droplet. Field name varies by
349
349
  // server version, so probe the likely shapes.
350
- async getSshPassword(id) {
351
- const body = await this.request(`/api/v1/droplets/${id}/ssh-password`, { method: "POST" });
352
- const pw = body?.password ?? body?.ssh_password ?? body?.data?.password ?? body?.data?.ssh_password;
353
- if (!pw) throw new Error("SSH password not present in API response (check field name in api.ts:getSshPassword).");
350
+ // The ssh-password endpoint is gated by a server-side connect access code
351
+ // (env.CLAUDE_CONNECT_ACCESS_CODE) with brute-force lockout, so we MUST send it
352
+ // in the POST body (the web app does the same). Without it the backend replies
353
+ // "Incorrect access code".
354
+ async getSshPassword(id, accessCode) {
355
+ const body = await this.request(`/api/v1/droplets/${id}/ssh-password`, {
356
+ method: "POST",
357
+ body: JSON.stringify({ accessCode: (accessCode || "").trim() })
358
+ });
359
+ const cand = [
360
+ body?.data?.droplet_password,
361
+ body?.data?.password,
362
+ body?.data?.ssh_password,
363
+ body?.data?.sshPassword,
364
+ body?.droplet_password,
365
+ body?.password,
366
+ body?.ssh_password,
367
+ body?.sshPassword,
368
+ body?.result?.password
369
+ ];
370
+ const pw = cand.find((v) => typeof v === "string" && v.length > 0);
371
+ if (!pw) {
372
+ throw new Error(
373
+ "SSH password not present. topKeys=" + JSON.stringify(Object.keys(body || {})) + " dataKeys=" + JSON.stringify(Object.keys(body?.data || {}))
374
+ );
375
+ }
354
376
  return String(pw);
355
377
  }
356
378
  // ---- talk to the gateway on the box (via the ownership-checked proxy) ---
@@ -600,7 +622,23 @@ async function connect(opts = {}) {
600
622
  if (!READY(target)) die(`Trading computer "${target.name}" is not ready yet (status=${target.status}).`);
601
623
  if (!target.ip_address) die(`Trading computer "${target.name}" has no IP yet. Try again shortly.`);
602
624
  info(`Fetching access for ${c.bold(target.name)}\u2026`);
603
- const password = await api.getSshPassword(target.id);
625
+ let password;
626
+ try {
627
+ password = await api.getSshPassword(target.id, opts.code || "");
628
+ } catch (e) {
629
+ if (!/access code/i.test(e?.message || "")) throw e;
630
+ let code = (opts.code || "").trim();
631
+ if (!code && process.stdin.isTTY) {
632
+ code = (await prompt("This computer needs a connect access code (from the Boole app):")).trim();
633
+ }
634
+ if (!code) die("This computer needs a connect access code: run `boole connect --code <code>` (from the Boole app).");
635
+ try {
636
+ password = await api.getSshPassword(target.id, code);
637
+ } catch (e2) {
638
+ if (/access code/i.test(e2?.message || "")) die("Connect access code was rejected \u2014 re-run `boole connect --code <code>` with the correct code.");
639
+ throw e2;
640
+ }
641
+ }
604
642
  saveSession({ agent: target.name, dropletId: target.id, ip: target.ip_address, password, sshUser: "customer" });
605
643
  ok(`Connected to ${c.bold(target.name)} (${target.ip_address}).`);
606
644
  log("");
@@ -728,7 +766,7 @@ function init(opts = {}) {
728
766
  }
729
767
 
730
768
  // src/index.ts
731
- var VERSION = "0.2.3";
769
+ var VERSION = "0.2.5";
732
770
  function parse(argv) {
733
771
  const _ = [];
734
772
  const flags = {};
@@ -815,7 +853,7 @@ async function main() {
815
853
  await provision({ name: str(flags.name), region: str(flags.region), size: str(flags.size) });
816
854
  break;
817
855
  case "connect":
818
- await connect({ name: _[1] });
856
+ await connect({ name: _[1], code: str(flags.code) });
819
857
  break;
820
858
  case "init":
821
859
  init({ dir: _[1], force: !!flags.force });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@boole-digital/cli",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "Boole — install, sign in, and operate your trading computer from the terminal (Claude Code, Codex, Gemini).",
5
5
  "type": "module",
6
6
  "bin": { "boole": "dist/index.js" },