joinhive 2.0.1 → 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 +5 -3
- package/bin/derive-evm-key.mjs +13 -0
- package/bin/hive +26 -6
- package/bin/hive-buzz.mjs +73 -0
- package/bin/hive-join.mjs +342 -91
- package/bin/hive-key.mjs +131 -0
- package/bin/hive-net.mjs +13 -0
- package/daemon/fanout.mjs +5 -2
- package/daemon/hived.mjs +21 -2
- package/docs/cli.md +3 -1
- package/package.json +3 -2
- package/server/api.mjs +41 -1
- package/server/join-page.mjs +7 -5
- package/server/provision.mjs +140 -4
- package/server/supervisor.mjs +27 -0
- package/shared/prompt.mjs +168 -0
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
|
|
20
|
+
Someone sends you an invite — one command, no install, **no API key required**:
|
|
21
21
|
|
|
22
22
|
```bash
|
|
23
|
-
|
|
23
|
+
npx joinhive join --invite <code> --server https://<their-bee-host>
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
|
|
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
|
-
|
|
|
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
|
;;
|
|
@@ -187,7 +192,8 @@ case "$cmd" in
|
|
|
187
192
|
case "$sub" in
|
|
188
193
|
invite) shift; exec node "$PACK_DIR/bin/hive-net.mjs" admin-invite "$@" ;;
|
|
189
194
|
retire) shift; exec node "$PACK_DIR/bin/hive-net.mjs" admin-retire "$@" ;;
|
|
190
|
-
|
|
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 ;;
|
|
191
197
|
esac
|
|
192
198
|
;;
|
|
193
199
|
doctor) # doctor — identity, relay, wallet, gas, tokens, daemon, stores in one look
|
|
@@ -773,7 +779,9 @@ case "$cmd" in
|
|
|
773
779
|
docs: https://docs.joinhive.fun
|
|
774
780
|
|
|
775
781
|
MEMBERSHIP
|
|
776
|
-
join --invite <code>
|
|
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
|
|
777
785
|
connect <url> [--invite <code>] point this endpoint at a community relay
|
|
778
786
|
start [--down] run a LOCAL community relay in Docker (:3000)
|
|
779
787
|
whoami | doctor identity / full health check
|
|
@@ -812,9 +820,21 @@ EOF
|
|
|
812
820
|
version|--version|-v)
|
|
813
821
|
node -e "try{console.log(require('$PACK_DIR/package.json').version)}catch{console.log('dev')}"
|
|
814
822
|
;;
|
|
815
|
-
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
|
+
*)
|
|
816
837
|
# Pass everything else straight to buzz-cli under the Hive identity.
|
|
817
|
-
[[ "$cmd" == "buzz" ]] && shift
|
|
818
838
|
BUZZ="$(find_buzz)"; [[ -n "$BUZZ" ]] || { echo '{"error":"buzz binary not found; build buzz-cli or set BUZZ_BIN"}' >&2; exit 1; }
|
|
819
839
|
[[ -f "$IDENTITY" ]] || { echo '{"error":"no identity; run: hive keygen"}' >&2; exit 1; }
|
|
820
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)));
|