@decentnetwork/beagle 0.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 +64 -0
- package/config/bootstrap-nodes.yaml +50 -0
- package/config/default-exits.yaml +49 -0
- package/dist/carrier-node.d.ts +108 -0
- package/dist/carrier-node.js +209 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +152 -0
- package/dist/desktop/app.js +3379 -0
- package/dist/desktop/index.html +55 -0
- package/dist/desktop/vendor/peer-webrtc.js +1070 -0
- package/dist/desktop/vendor/qrcode.js +2297 -0
- package/dist/desktop/vendor/react-dom.production.min.js +267 -0
- package/dist/desktop/vendor/react.production.min.js +31 -0
- package/dist/embedded-host.d.ts +37 -0
- package/dist/embedded-host.js +473 -0
- package/dist/exits.d.ts +7 -0
- package/dist/exits.js +24 -0
- package/dist/friend-meta.d.ts +36 -0
- package/dist/friend-meta.js +97 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +5 -0
- package/dist/ipc.d.ts +41 -0
- package/dist/ipc.js +76 -0
- package/dist/logger.d.ts +20 -0
- package/dist/logger.js +43 -0
- package/dist/message-store.d.ts +104 -0
- package/dist/message-store.js +201 -0
- package/dist/node-config.d.ts +16 -0
- package/dist/node-config.js +53 -0
- package/dist/peer-host.d.ts +49 -0
- package/dist/peer-host.js +102 -0
- package/dist/server.d.ts +29 -0
- package/dist/server.js +1187 -0
- package/package.json +45 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// PeerHost — the one abstraction the split turns on.
|
|
2
|
+
//
|
|
3
|
+
// Beagle needs a live Carrier identity to chat, send files and place calls.
|
|
4
|
+
// There are two ways to have one, and they are mutually exclusive:
|
|
5
|
+
//
|
|
6
|
+
// backend B (daemon) — a decentlan daemon is already running; talk to it
|
|
7
|
+
// over IPC. The user gets the virtual LAN too.
|
|
8
|
+
// backend A (embedded) — no daemon; beagle opens its own Peer in-process.
|
|
9
|
+
// Plain user privilege, no TUN, no sudo. (Phase 2)
|
|
10
|
+
//
|
|
11
|
+
// The hard constraint that forces this shape: ONE identity must never have two
|
|
12
|
+
// live Carrier peers. Two peers on the same keypair race on session
|
|
13
|
+
// establishment and scramble each other's net_crypto state — decentlan has an
|
|
14
|
+
// explicit refuse-second-peer guard for exactly this. So beagle picks a backend
|
|
15
|
+
// and holds it; it never runs both, and switching is a sequenced handoff
|
|
16
|
+
// (stop one, wait, start the other), never an overlap.
|
|
17
|
+
//
|
|
18
|
+
// Phase 1 ships backend B only. The interface is defined now so backend A can
|
|
19
|
+
// drop in without touching the HTTP layer above it.
|
|
20
|
+
import { homedir } from "node:os";
|
|
21
|
+
import { resolve } from "node:path";
|
|
22
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
23
|
+
import yaml from "js-yaml";
|
|
24
|
+
import { daemonIsRunning, ipcCall, ipcSocketPath } from "./ipc.js";
|
|
25
|
+
/** Where decentlan keeps its Carrier identity and daemon socket. Beagle reuses
|
|
26
|
+
* this identity when it exists so a user's friends carry over — the whole
|
|
27
|
+
* point of the split is that it is the same node, not a new one. */
|
|
28
|
+
export function decentlanCarrierDir(configDir) {
|
|
29
|
+
const cfg = resolve(configDir, "config.yaml");
|
|
30
|
+
if (existsSync(cfg)) {
|
|
31
|
+
try {
|
|
32
|
+
const parsed = yaml.load(readFileSync(cfg, "utf-8"));
|
|
33
|
+
if (parsed?.carrier?.dataDir)
|
|
34
|
+
return parsed.carrier.dataDir;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// fall through to the conventional path
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return resolve(configDir, "carrier");
|
|
41
|
+
}
|
|
42
|
+
export function defaultConfigDir() {
|
|
43
|
+
return process.env.AGENTNET_CONFIG_DIR || resolve(homedir(), ".agentnet");
|
|
44
|
+
}
|
|
45
|
+
/** Backend B: every op is forwarded to the running decentlan daemon. */
|
|
46
|
+
class DaemonHost {
|
|
47
|
+
kind = "daemon";
|
|
48
|
+
hasVirtualLan = true;
|
|
49
|
+
#dataDir;
|
|
50
|
+
constructor(dataDir) {
|
|
51
|
+
this.#dataDir = dataDir;
|
|
52
|
+
}
|
|
53
|
+
call(req) {
|
|
54
|
+
return ipcCall(this.#dataDir, req);
|
|
55
|
+
}
|
|
56
|
+
async stop() {
|
|
57
|
+
// Nothing to tear down: we don't own the daemon's lifecycle. Beagle must
|
|
58
|
+
// never start or stop the user's daemon as a side effect.
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Pick a backend.
|
|
63
|
+
*
|
|
64
|
+
* Daemon-first when one is running: it already holds this identity's Carrier
|
|
65
|
+
* peer, and starting a second peer on the same keypair is the one thing that
|
|
66
|
+
* must never happen. If there's no daemon, run the peer ourselves — no root,
|
|
67
|
+
* no TUN, just messaging.
|
|
68
|
+
*/
|
|
69
|
+
export async function openPeerHost(opts) {
|
|
70
|
+
const dataDir = decentlanCarrierDir(opts.configDir);
|
|
71
|
+
if (opts.force !== "embedded" && daemonIsRunning(dataDir)) {
|
|
72
|
+
return { host: new DaemonHost(dataDir), why: `decentlan daemon at ${dataDir}` };
|
|
73
|
+
}
|
|
74
|
+
if (opts.force === "daemon") {
|
|
75
|
+
throw new Error(`--backend daemon was requested but no decentlan daemon is running (looked for ${ipcSocketPath(dataDir)}).`);
|
|
76
|
+
}
|
|
77
|
+
// Reuse decentlan's identity when it exists so friends carry over — this is
|
|
78
|
+
// the same node, not a new one. The daemon is not running, so nothing else
|
|
79
|
+
// holds this keypair.
|
|
80
|
+
const keyFile = resolve(dataDir, "keypair.json");
|
|
81
|
+
// Sample this BEFORE start(): the SDK creates the keyfile on first run, so
|
|
82
|
+
// checking afterwards always reports "reusing" and the banner would claim we
|
|
83
|
+
// kept an identity we had in fact just minted.
|
|
84
|
+
const reusedIdentity = existsSync(keyFile);
|
|
85
|
+
const { EmbeddedHost } = await import("./embedded-host.js");
|
|
86
|
+
const host = new EmbeddedHost({
|
|
87
|
+
configDir: opts.configDir,
|
|
88
|
+
keyFile,
|
|
89
|
+
bootstrapNodes: opts.bootstrapNodes,
|
|
90
|
+
expressNodes: opts.expressNodes,
|
|
91
|
+
nickname: opts.nickname,
|
|
92
|
+
statusMessage: opts.statusMessage,
|
|
93
|
+
autoAcceptFriends: opts.autoAcceptFriends ?? true,
|
|
94
|
+
});
|
|
95
|
+
await host.start();
|
|
96
|
+
return {
|
|
97
|
+
host,
|
|
98
|
+
why: reusedIdentity
|
|
99
|
+
? `embedded peer, reusing the identity in ${keyFile}`
|
|
100
|
+
: `embedded peer, new identity at ${keyFile}`,
|
|
101
|
+
};
|
|
102
|
+
}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { IpcRequest, IpcResponse } from "./ipc.js";
|
|
2
|
+
export interface BeagleServerOptions {
|
|
3
|
+
/** Call the running daemon over IPC (injected so this module stays
|
|
4
|
+
* decoupled from the CLI's ipc client). */
|
|
5
|
+
call: (req: IpcRequest) => Promise<IpcResponse>;
|
|
6
|
+
/** Path to ~/.agentnet/routes.yaml — the exit-routing table the UI edits. */
|
|
7
|
+
routesPath: string;
|
|
8
|
+
/** If this node also runs a dora server, the path to its roster.yaml — the
|
|
9
|
+
* UI shows the allocation table (userid → IP → name). Undefined = no dora. */
|
|
10
|
+
doraRosterPath?: string;
|
|
11
|
+
/** Version/wire strings for the desktop "my node" panel — the daemon's diag
|
|
12
|
+
* doesn't carry them, so the CLI reads them off the installed packages and
|
|
13
|
+
* passes them in. */
|
|
14
|
+
meExtra?: {
|
|
15
|
+
lanVer?: string;
|
|
16
|
+
peerVer?: string;
|
|
17
|
+
wire?: string;
|
|
18
|
+
channel?: string;
|
|
19
|
+
};
|
|
20
|
+
/** Directory where the daemon saves received files (<configDir>/downloads).
|
|
21
|
+
* Used to serve GET /api/file-download. Undefined disables downloads. */
|
|
22
|
+
downloadsDir?: string;
|
|
23
|
+
listenHost?: string;
|
|
24
|
+
listenPort?: number;
|
|
25
|
+
log?: (msg: string) => void;
|
|
26
|
+
}
|
|
27
|
+
export declare function startBeagleServer(opts: BeagleServerOptions): {
|
|
28
|
+
stop: () => void;
|
|
29
|
+
};
|