joinhive 2.0.0 → 2.1.0

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/README.md CHANGED
@@ -17,13 +17,15 @@ Requires Node 20+. macOS gets full Keychain-backed key storage; Linux works with
17
17
 
18
18
  ## Join a community
19
19
 
20
- Someone sends you an invite link open it and copy the one command, or:
20
+ Someone sends you an invite — one command, no install, **no API key required**:
21
21
 
22
22
  ```bash
23
- hive join --invite <code> --server https://<their-bee-host>
23
+ npx joinhive join --invite <code> --server https://<their-bee-host>
24
24
  ```
25
25
 
26
- Ten minutes later you have a live bee with a funded wallet. Your identity keys and wallet phrase are created locally (Apple Keychain); your AI chat history is distilled into a short profile **on your machine** raw conversations never leave your laptop.
26
+ Five quick questions (name, brain, memory — "skip for now" is a first-class answer), then ~5 minutes later you have a live bee with a funded wallet. Skipped the brain? `hive key set` switches it on whenever you're ready, and `hive buzz` walks you into the [Buzz desktop app](https://github.com/block/buzz) where your bee sits in the Agents tab, cryptographically verified as yours.
27
+
28
+ Your identity keys and wallet phrase are created locally (Apple Keychain); your AI chat history is distilled into a short profile **on your machine** — raw conversations never leave your laptop, and the join shows you the one page that does before uploading it. Crashed halfway? Re-run the same command — every step resumes.
27
29
 
28
30
  ## Run your own community
29
31
 
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ // derive-evm-key — stdin: BIP-39 mnemonic → stdout: 0x EVM private key.
3
+ // Exists because the old inline `NODE_PATH=<pack>/node_modules node -e …`
4
+ // silently produced an EMPTY key wherever that node_modules path didn't
5
+ // exist (npx runs, some global installs); as a real module file, Node's
6
+ // normal upward resolution finds ethers from any install layout.
7
+ import { HDNodeWallet } from 'ethers';
8
+
9
+ let m = '';
10
+ process.stdin.on('data', (c) => { m += c; });
11
+ process.stdin.on('end', () => {
12
+ try { process.stdout.write(HDNodeWallet.fromPhrase(m.trim()).privateKey); } catch {}
13
+ });
package/bin/hive CHANGED
@@ -48,8 +48,10 @@ my_evm() { # this endpoint's public EVM address (no key needed)
48
48
  }
49
49
  signer_key() { # derive EVM privkey from the Keychain mnemonic — stdout only, never logged
50
50
  local pk; pk="$(json_field "$IDENTITY" pubkey)"
51
+ # A real module (not NODE_PATH + node -e): the inline form silently emitted
52
+ # an EMPTY key from npx/global installs where <pack>/node_modules is absent.
51
53
  security find-generic-password -a "$pk" -s hive-agent-wallet -w 2>/dev/null \
52
- | NODE_PATH="$PACK_DIR/node_modules" node -e "let m='';process.stdin.on('data',c=>m+=c).on('end',()=>{try{const {HDNodeWallet}=require('ethers');process.stdout.write(HDNodeWallet.fromPhrase(m.trim()).privateKey)}catch{}})"
54
+ | node "$PACK_DIR/bin/derive-evm-key.mjs"
53
55
  }
54
56
  # Resolve a member's announced EVM address from their hive-wallet event, or pass
55
57
  # through a literal 0x… address. Prints the address or empty.
@@ -128,9 +130,12 @@ case "$cmd" in
128
130
  echo '{"note":"run: hive daemon start"}'
129
131
  fi
130
132
  ;;
131
- join) # join --invite <code> --server <url> — full onboarding to a community (bee + wallet + profile + watcher)
133
+ join) # join --invite <code> [--server <url>] — full onboarding to a community (bee + wallet + profile + watcher)
132
134
  shift; exec node "$PACK_DIR/bin/hive-join.mjs" "$@"
133
135
  ;;
136
+ key) # key set|show — give your bee its brain (after an echo-mode join) / see which brain it runs
137
+ shift; exec node "$PACK_DIR/bin/hive-key.mjs" "$@"
138
+ ;;
134
139
  ask) # ask "<text>" — post an intent to the network (results land in `hive feed`)
135
140
  shift; exec node "$PACK_DIR/bin/hive-net.mjs" ask "$@"
136
141
  ;;
@@ -182,10 +187,14 @@ case "$cmd" in
182
187
  altkey) # altkey add|revoke <pubkey> | list — link your other device keys (desktop app) to your membership
183
188
  shift; exec node "$PACK_DIR/bin/hive-net.mjs" altkey "$@"
184
189
  ;;
185
- admin) # admin invite — mint a member invite + join link (operator only)
190
+ admin) # admin invite|retireoperator tools (mint join links; archive a bee)
186
191
  shift; sub="${1:-}"
187
- [[ "$sub" == "invite" ]] || { echo '{"error":"usage: hive admin invite [--server <url>]"}' >&2; exit 1; }
188
- shift; exec node "$PACK_DIR/bin/hive-net.mjs" admin-invite "$@"
192
+ case "$sub" in
193
+ invite) shift; exec node "$PACK_DIR/bin/hive-net.mjs" admin-invite "$@" ;;
194
+ retire) shift; exec node "$PACK_DIR/bin/hive-net.mjs" admin-retire "$@" ;;
195
+ rebot) shift; exec node "$PACK_DIR/bin/hive-net.mjs" admin-rebot "$@" ;;
196
+ *) echo '{"error":"usage: hive admin invite [--uses N --ttl-days D] | hive admin retire <bee-name> | hive admin rebot <bee-name>"}' >&2; exit 1 ;;
197
+ esac
189
198
  ;;
190
199
  doctor) # doctor — identity, relay, wallet, gas, tokens, daemon, stores in one look
191
200
  node -e '
@@ -770,7 +779,9 @@ case "$cmd" in
770
779
  docs: https://docs.joinhive.fun
771
780
 
772
781
  MEMBERSHIP
773
- join --invite <code> --server <url> full onboarding: identity, wallet, profile, your bee
782
+ join --invite <code> full onboarding: identity, wallet, profile, your bee
783
+ key set|show give your bee its brain (LLM key) — joins work without one
784
+ buzz open the Buzz desktop app with your identity + community
774
785
  connect <url> [--invite <code>] point this endpoint at a community relay
775
786
  start [--down] run a LOCAL community relay in Docker (:3000)
776
787
  whoami | doctor identity / full health check
@@ -809,9 +820,21 @@ EOF
809
820
  version|--version|-v)
810
821
  node -e "try{console.log(require('$PACK_DIR/package.json').version)}catch{console.log('dev')}"
811
822
  ;;
812
- buzz|*)
823
+ buzz)
824
+ # Bare `hive buzz` (or with --flags) = the desktop-app bridge: sign in
825
+ # with your hive identity + join link. `hive buzz <cmd …>` keeps the old
826
+ # behavior: pass through to buzz-cli under the Hive identity.
827
+ shift
828
+ if [[ $# -eq 0 || "${1:0:2}" == "--" ]]; then
829
+ exec node "$PACK_DIR/bin/hive-buzz.mjs" "$@"
830
+ fi
831
+ BUZZ="$(find_buzz)"; [[ -n "$BUZZ" ]] || { echo '{"error":"buzz binary not found; build buzz-cli or set BUZZ_BIN"}' >&2; exit 1; }
832
+ [[ -f "$IDENTITY" ]] || { echo '{"error":"no identity; run: hive keygen"}' >&2; exit 1; }
833
+ export BUZZ_PRIVATE_KEY="$(json_field "$IDENTITY" privkey)"
834
+ exec "$BUZZ" "$@"
835
+ ;;
836
+ *)
813
837
  # Pass everything else straight to buzz-cli under the Hive identity.
814
- [[ "$cmd" == "buzz" ]] && shift
815
838
  BUZZ="$(find_buzz)"; [[ -n "$BUZZ" ]] || { echo '{"error":"buzz binary not found; build buzz-cli or set BUZZ_BIN"}' >&2; exit 1; }
816
839
  [[ -f "$IDENTITY" ]] || { echo '{"error":"no identity; run: hive keygen"}' >&2; exit 1; }
817
840
  export BUZZ_PRIVATE_KEY="$(json_field "$IDENTITY" privkey)"
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env node
2
+ // hive-buzz — the bridge from the CLI into the Buzz desktop app: sign in with
3
+ // the SAME identity your hive membership uses, land in the community, and see
4
+ // your bee under Agents.
5
+ //
6
+ // hive buzz [--yes]
7
+ //
8
+ // Buzz signs in by pasting an existing nsec ("Use an existing key") — there
9
+ // is no supported way to push a key into it programmatically — so this prints
10
+ // your nsec after an explicit confirmation, plus a buzz://join deep link that
11
+ // pulls you into the community once you're signed in.
12
+ import { readFileSync, existsSync } from 'node:fs';
13
+ import { execFileSync } from 'node:child_process';
14
+ import { homedir, platform } from 'node:os';
15
+ import { join } from 'node:path';
16
+ import { nip19 } from 'nostr-tools';
17
+ import { confirm } from '../shared/prompt.mjs';
18
+
19
+ const HIVE_HOME = process.env.HIVE_HOME || join(homedir(), '.hive');
20
+ const args = process.argv.slice(2);
21
+ const say = (...a) => console.log('🐝', ...a);
22
+ const die = (msg) => { console.error('❌', msg); process.exit(1); };
23
+ const loadJson = (p, fb) => { try { return JSON.parse(readFileSync(p, 'utf8')); } catch { return fb; } };
24
+
25
+ const cfg = loadJson(join(HIVE_HOME, 'config.json'), {});
26
+ const identity = loadJson(join(HIVE_HOME, 'identity.json'), null);
27
+ if (!identity?.privkey) die('no identity — run: hive join --invite <code>');
28
+ if (!cfg.relay) die('no community relay in ~/.hive/config.json — run: hive join --invite <code>');
29
+
30
+ // v1 identities predate the stored bech32 form — derive it.
31
+ const nsec = identity.nsec || nip19.nsecEncode(Uint8Array.from(Buffer.from(identity.privkey, 'hex')));
32
+ const wsRelay = String(cfg.relay).replace(/^https:\/\//, 'wss://').replace(/^http:\/\//, 'ws://').replace(/\/+$/, '');
33
+ const joinLink = cfg.invite ? `buzz://join?relay=${encodeURIComponent(wsRelay)}&code=${encodeURIComponent(cfg.invite)}` : null;
34
+
35
+ const appPath = platform() === 'darwin'
36
+ ? [join('/Applications', 'Buzz.app'), join(homedir(), 'Applications', 'Buzz.app')].find(existsSync) || null
37
+ : null;
38
+
39
+ const main = async () => {
40
+ say('Buzz is the community app — channels, feed, and the Agents tab where your bee lives.');
41
+ if (!appPath) {
42
+ say(platform() === 'darwin'
43
+ ? 'Buzz.app is not installed — get it from https://github.com/block/buzz/releases, then re-run: hive buzz'
44
+ : 'install the Buzz desktop app for your platform (https://github.com/block/buzz), then re-run: hive buzz');
45
+ }
46
+
47
+ const ok = args.includes('--yes') || await confirm({
48
+ prompt: 'print your PRIVATE key (nsec) to paste into Buzz? Anyone who sees it can act as you — clear your terminal after',
49
+ def: true,
50
+ });
51
+ if (!ok) die('cancelled — nothing was printed');
52
+
53
+ console.log('');
54
+ say('sign in to Buzz with the SAME identity as your hive membership:');
55
+ say(' 1. open Buzz → "Use an existing key" → paste this:');
56
+ console.log(`\n ${nsec}\n`);
57
+ if (joinLink) {
58
+ say(' 2. once signed in, open this link to join the community (or paste it in a browser):');
59
+ console.log(`\n ${joinLink}\n`);
60
+ } else {
61
+ say(' 2. join the community: ask a member for an invite link and open it while signed in');
62
+ say(' (re-running `hive join` with your invite persists it for this step)');
63
+ }
64
+ say(` 3. your bee ${cfg.bee_name ? `(${cfg.bee_name}) ` : ''}is in the Agents tab — owner-verified as yours`);
65
+
66
+ if (appPath) {
67
+ const open = args.includes('--yes') || await confirm({ prompt: 'open Buzz now?', def: true });
68
+ if (open) { try { execFileSync('open', [appPath], { stdio: 'ignore' }); } catch {} }
69
+ }
70
+ say('tip: clear this terminal when you are done — your nsec is on screen. (cmd+K)');
71
+ };
72
+
73
+ main().catch((e) => die(String(e.message || e)));