@log0u7/opencode-mesh 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.
Files changed (64) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/LICENSE +201 -0
  3. package/README.md +88 -0
  4. package/dist/index.d.ts +15 -0
  5. package/dist/index.d.ts.map +1 -0
  6. package/dist/index.js +15 -0
  7. package/dist/index.js.map +1 -0
  8. package/dist/lib/client.d.ts +10 -0
  9. package/dist/lib/client.d.ts.map +1 -0
  10. package/dist/lib/client.js +26 -0
  11. package/dist/lib/client.js.map +1 -0
  12. package/dist/lib/db.d.ts +7 -0
  13. package/dist/lib/db.d.ts.map +1 -0
  14. package/dist/lib/db.js +55 -0
  15. package/dist/lib/db.js.map +1 -0
  16. package/dist/lib/discovery.d.ts +27 -0
  17. package/dist/lib/discovery.d.ts.map +1 -0
  18. package/dist/lib/discovery.js +63 -0
  19. package/dist/lib/discovery.js.map +1 -0
  20. package/dist/lib/identity.d.ts +15 -0
  21. package/dist/lib/identity.d.ts.map +1 -0
  22. package/dist/lib/identity.js +63 -0
  23. package/dist/lib/identity.js.map +1 -0
  24. package/dist/lib/locks.d.ts +17 -0
  25. package/dist/lib/locks.d.ts.map +1 -0
  26. package/dist/lib/locks.js +39 -0
  27. package/dist/lib/locks.js.map +1 -0
  28. package/dist/lib/mail.d.ts +11 -0
  29. package/dist/lib/mail.d.ts.map +1 -0
  30. package/dist/lib/mail.js +42 -0
  31. package/dist/lib/mail.js.map +1 -0
  32. package/dist/lib/peers.d.ts +31 -0
  33. package/dist/lib/peers.d.ts.map +1 -0
  34. package/dist/lib/peers.js +95 -0
  35. package/dist/lib/peers.js.map +1 -0
  36. package/dist/lib/serve.d.ts +12 -0
  37. package/dist/lib/serve.d.ts.map +1 -0
  38. package/dist/lib/serve.js +165 -0
  39. package/dist/lib/serve.js.map +1 -0
  40. package/dist/lib/spawn.d.ts +33 -0
  41. package/dist/lib/spawn.d.ts.map +1 -0
  42. package/dist/lib/spawn.js +130 -0
  43. package/dist/lib/spawn.js.map +1 -0
  44. package/dist/lib/sqlite.d.ts +9 -0
  45. package/dist/lib/sqlite.d.ts.map +1 -0
  46. package/dist/lib/sqlite.js +63 -0
  47. package/dist/lib/sqlite.js.map +1 -0
  48. package/dist/lib/workers.d.ts +29 -0
  49. package/dist/lib/workers.d.ts.map +1 -0
  50. package/dist/lib/workers.js +74 -0
  51. package/dist/lib/workers.js.map +1 -0
  52. package/dist/lib/worktree.d.ts +30 -0
  53. package/dist/lib/worktree.d.ts.map +1 -0
  54. package/dist/lib/worktree.js +43 -0
  55. package/dist/lib/worktree.js.map +1 -0
  56. package/dist/plugin.d.ts +3 -0
  57. package/dist/plugin.d.ts.map +1 -0
  58. package/dist/plugin.js +310 -0
  59. package/dist/plugin.js.map +1 -0
  60. package/dist/types/peer.d.ts +26 -0
  61. package/dist/types/peer.d.ts.map +1 -0
  62. package/dist/types/peer.js +2 -0
  63. package/dist/types/peer.js.map +1 -0
  64. package/package.json +61 -0
@@ -0,0 +1,63 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { chmodSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ export function loadOrCreateIdentity(dataDir) {
5
+ const file = join(dataDir, "node.json");
6
+ const existing = tryRead(file);
7
+ if (existing) {
8
+ return withFingerprint(existing);
9
+ }
10
+ return withFingerprint(generate(dataDir, file));
11
+ }
12
+ function tryRead(file) {
13
+ try {
14
+ const raw = JSON.parse(readFileSync(file, "utf8"));
15
+ if (typeof raw.node_id === "string" && raw.public_key && raw.private_key) {
16
+ return raw;
17
+ }
18
+ return null;
19
+ }
20
+ catch {
21
+ return null;
22
+ }
23
+ }
24
+ function generate(dataDir, file) {
25
+ // Bun implements node:crypto, so this works under OpenCode's runtime and Node.
26
+ const { generateKeyPairSync } = require("node:crypto");
27
+ const { publicKey, privateKey } = generateKeyPairSync("ed25519");
28
+ const publicJwk = publicKey.export({ format: "jwk" });
29
+ const privateJwk = privateKey.export({ format: "jwk" });
30
+ const identity = {
31
+ node_id: randomUUID(),
32
+ hostname: hostName(),
33
+ public_key: publicJwk,
34
+ private_key: privateJwk,
35
+ };
36
+ mkdirSync(dataDir, { recursive: true, mode: 0o700 });
37
+ // Atomic write: temp file + rename, so a crash never leaves a truncated keypair.
38
+ const tmp = `${file}.tmp-${process.pid}`;
39
+ writeFileSync(tmp, JSON.stringify(identity, null, 2), { mode: 0o600 });
40
+ renameSync(tmp, file);
41
+ chmodSync(file, 0o600);
42
+ return identity;
43
+ }
44
+ function withFingerprint(identity) {
45
+ const { createHash } = require("node:crypto");
46
+ // x is the raw public key coordinate in an Ed25519 JWK; hash it for a short
47
+ // operator-verifiable fingerprint.
48
+ const x = identity.public_key.x ?? "";
49
+ return {
50
+ ...identity,
51
+ fingerprint: createHash("sha256").update(x).digest("hex").slice(0, 16),
52
+ };
53
+ }
54
+ function hostName() {
55
+ try {
56
+ const os = require("node:os");
57
+ return os.hostname();
58
+ }
59
+ catch {
60
+ return "unknown";
61
+ }
62
+ }
63
+ //# sourceMappingURL=identity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/lib/identity.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { MeshDb } from "./db.js";
2
+ import type { Lock } from "../types/peer.js";
3
+ export declare function tryLock(db: MeshDb, input: {
4
+ path: string;
5
+ owner_node: string;
6
+ owner_session: string;
7
+ ttl_ms: number;
8
+ now: number;
9
+ }): boolean;
10
+ export declare function releaseLock(db: MeshDb, input: {
11
+ path: string;
12
+ owner_node: string;
13
+ }): boolean;
14
+ export declare function listLocks(db: MeshDb, query: {
15
+ now: number;
16
+ }): Lock[];
17
+ //# sourceMappingURL=locks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"locks.d.ts","sourceRoot":"","sources":["../../src/lib/locks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACtC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAE7C,wBAAgB,OAAO,CACrB,EAAE,EAAE,MAAM,EACV,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAC9F,OAAO,CAwBT;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAM5F;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,EAAE,CAGpE"}
@@ -0,0 +1,39 @@
1
+ export function tryLock(db, input) {
2
+ const expiresAt = input.now + input.ttl_ms;
3
+ db.conn.run("DELETE FROM locks WHERE expires_at <= ?", [input.now]);
4
+ const existing = db.conn.get("SELECT * FROM locks WHERE path = ?", [input.path]);
5
+ if (existing) {
6
+ if (existing.owner_node === input.owner_node) {
7
+ // Same owner refreshes its claim.
8
+ db.conn.run("UPDATE locks SET expires_at = ?, owner_session = ? WHERE path = ?", [
9
+ expiresAt,
10
+ input.owner_session,
11
+ input.path,
12
+ ]);
13
+ return true;
14
+ }
15
+ return false;
16
+ }
17
+ db.conn.run("INSERT INTO locks (path, owner_node, owner_session, expires_at) VALUES (?, ?, ?, ?)", [input.path, input.owner_node, input.owner_session, expiresAt]);
18
+ return true;
19
+ }
20
+ export function releaseLock(db, input) {
21
+ db.conn.run("DELETE FROM locks WHERE path = ? AND owner_node = ?", [
22
+ input.path,
23
+ input.owner_node,
24
+ ]);
25
+ return (db.conn.get("SELECT changes() AS changes")?.changes ?? 0) > 0;
26
+ }
27
+ export function listLocks(db, query) {
28
+ db.conn.run("DELETE FROM locks WHERE expires_at <= ?", [query.now]);
29
+ return db.conn.all("SELECT * FROM locks ORDER BY path").map(rowToLock);
30
+ }
31
+ function rowToLock(row) {
32
+ return {
33
+ path: row.path,
34
+ owner: row.owner_session,
35
+ node_id: row.owner_node,
36
+ expires_at: row.expires_at,
37
+ };
38
+ }
39
+ //# sourceMappingURL=locks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"locks.js","sourceRoot":"","sources":["../../src/lib/locks.ts"],"names":[],"mappings":"AAGA,MAAM,UAAU,OAAO,CACrB,EAAU,EACV,KAA+F;IAE/F,MAAM,SAAS,GAAG,KAAK,CAAC,GAAG,GAAG,KAAK,CAAC,MAAM,CAAC;IAE3C,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,yCAAyC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;IAEpE,MAAM,QAAQ,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,CAAU,oCAAoC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;IAC1F,IAAI,QAAQ,EAAE,CAAC;QACb,IAAI,QAAQ,CAAC,UAAU,KAAK,KAAK,CAAC,UAAU,EAAE,CAAC;YAC7C,kCAAkC;YAClC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,mEAAmE,EAAE;gBAC/E,SAAS;gBACT,KAAK,CAAC,aAAa;gBACnB,KAAK,CAAC,IAAI;aACX,CAAC,CAAC;YACH,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,EAAE,CAAC,IAAI,CAAC,GAAG,CACT,qFAAqF,EACrF,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,aAAa,EAAE,SAAS,CAAC,CAC/D,CAAC;IACF,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,EAAU,EAAE,KAA2C;IACjF,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,qDAAqD,EAAE;QACjE,KAAK,CAAC,IAAI;QACV,KAAK,CAAC,UAAU;KACjB,CAAC,CAAC;IACH,OAAO,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAsB,6BAA6B,CAAC,EAAE,OAAO,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;AAC7F,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,EAAU,EAAE,KAAsB;IAC1D,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,yCAAyC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;IACpE,OAAO,EAAE,CAAC,IAAI,CAAC,GAAG,CAAU,mCAAmC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;AAClF,CAAC;AASD,SAAS,SAAS,CAAC,GAAY;IAC7B,OAAO;QACL,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,KAAK,EAAE,GAAG,CAAC,aAAa;QACxB,OAAO,EAAE,GAAG,CAAC,UAAU;QACvB,UAAU,EAAE,GAAG,CAAC,UAAU;KAC3B,CAAC;AACJ,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { MeshDb } from "./db.js";
2
+ import type { MailMessage } from "../types/peer.js";
3
+ export declare function receiveMessage(db: MeshDb, input: {
4
+ from: string;
5
+ subject: string;
6
+ body: string;
7
+ }): MailMessage;
8
+ export declare function unreadMessages(db: MeshDb): MailMessage[];
9
+ export declare function unreadCount(db: MeshDb): number;
10
+ export declare function ackMessages(db: MeshDb, ids: string[]): void;
11
+ //# sourceMappingURL=mail.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mail.d.ts","sourceRoot":"","sources":["../../src/lib/mail.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACtC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,wBAAgB,cAAc,CAC5B,EAAE,EAAE,MAAM,EACV,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACrD,WAAW,CAkBb;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,WAAW,EAAE,CAIxD;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAK9C;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI,CAI3D"}
@@ -0,0 +1,42 @@
1
+ import { randomUUID } from "node:crypto";
2
+ export function receiveMessage(db, input) {
3
+ const message = {
4
+ id: randomUUID(),
5
+ from: input.from,
6
+ to: "self",
7
+ session_id: null,
8
+ subject: input.subject,
9
+ body: input.body,
10
+ status: "unread",
11
+ created_at: Date.now(),
12
+ };
13
+ db.conn.run("INSERT INTO messages (id, from_node, subject, body, status, created_at) VALUES (?, ?, ?, ?, 'unread', ?)", [message.id, message.from, message.subject, message.body, message.created_at]);
14
+ return message;
15
+ }
16
+ export function unreadMessages(db) {
17
+ return db.conn
18
+ .all("SELECT * FROM messages WHERE status = 'unread' ORDER BY created_at ASC")
19
+ .map(rowToMessage);
20
+ }
21
+ export function unreadCount(db) {
22
+ return (db.conn.get("SELECT COUNT(*) AS count FROM messages WHERE status = 'unread'")
23
+ ?.count ?? 0);
24
+ }
25
+ export function ackMessages(db, ids) {
26
+ for (const id of ids) {
27
+ db.conn.run("UPDATE messages SET status = 'read' WHERE id = ?", [id]);
28
+ }
29
+ }
30
+ function rowToMessage(row) {
31
+ return {
32
+ id: row.id,
33
+ from: row.from_node,
34
+ to: "self",
35
+ session_id: null,
36
+ subject: row.subject,
37
+ body: row.body,
38
+ status: row.status === "read" ? "read" : "unread",
39
+ created_at: row.created_at,
40
+ };
41
+ }
42
+ //# sourceMappingURL=mail.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mail.js","sourceRoot":"","sources":["../../src/lib/mail.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAKzC,MAAM,UAAU,cAAc,CAC5B,EAAU,EACV,KAAsD;IAEtD,MAAM,OAAO,GAAgB;QAC3B,EAAE,EAAE,UAAU,EAAE;QAChB,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,EAAE,EAAE,MAAM;QACV,UAAU,EAAE,IAAI;QAChB,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,MAAM,EAAE,QAAQ;QAChB,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE;KACvB,CAAC;IAEF,EAAE,CAAC,IAAI,CAAC,GAAG,CACT,0GAA0G,EAC1G,CAAC,OAAO,CAAC,EAAE,EAAE,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,UAAU,CAAC,CAC9E,CAAC;IAEF,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,EAAU;IACvC,OAAO,EAAE,CAAC,IAAI;SACX,GAAG,CAAa,wEAAwE,CAAC;SACzF,GAAG,CAAC,YAAY,CAAC,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,EAAU;IACpC,OAAO,CACL,EAAE,CAAC,IAAI,CAAC,GAAG,CAAoB,gEAAgE,CAAC;QAC9F,EAAE,KAAK,IAAI,CAAC,CACf,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,EAAU,EAAE,GAAa;IACnD,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC;QACrB,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,kDAAkD,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACxE,CAAC;AACH,CAAC;AAWD,SAAS,YAAY,CAAC,GAAe;IACnC,OAAO;QACL,EAAE,EAAE,GAAG,CAAC,EAAE;QACV,IAAI,EAAE,GAAG,CAAC,SAAS;QACnB,EAAE,EAAE,MAAM;QACV,UAAU,EAAE,IAAI;QAChB,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,MAAM,EAAE,GAAG,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ;QACjD,UAAU,EAAE,GAAG,CAAC,UAAU;KAC3B,CAAC;AACJ,CAAC"}
@@ -0,0 +1,31 @@
1
+ import type { MeshDb } from "./db.js";
2
+ import type { Peer } from "../types/peer.js";
3
+ export type PeerInput = {
4
+ node_id: string;
5
+ hostname: string;
6
+ fingerprint: string;
7
+ addresses?: string[];
8
+ };
9
+ export declare function pairPeer(db: MeshDb, peer: {
10
+ node_id: string;
11
+ hostname: string;
12
+ fingerprint: string;
13
+ }): string;
14
+ export declare function pairWithToken(db: MeshDb, peer: {
15
+ node_id: string;
16
+ hostname: string;
17
+ fingerprint: string;
18
+ token: string;
19
+ addresses?: string[];
20
+ }): void;
21
+ export declare function createPairingToken(db: MeshDb): string;
22
+ export declare function authenticateToken(db: MeshDb, token: string, from: string | null): Peer | null;
23
+ export declare function listPeers(db: MeshDb): Peer[];
24
+ export declare function findPeerByToken(db: MeshDb, token: string): Peer | null;
25
+ export declare function upsertDiscoveredPeer(db: MeshDb, peer: {
26
+ node_id: string;
27
+ hostname: string;
28
+ fingerprint: string;
29
+ addresses: string[];
30
+ }): void;
31
+ //# sourceMappingURL=peers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"peers.d.ts","sourceRoot":"","sources":["../../src/lib/peers.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACtC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAE7C,MAAM,MAAM,SAAS,GAAG;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB,CAAC;AAIF,wBAAgB,QAAQ,CACtB,EAAE,EAAE,MAAM,EACV,IAAI,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAC/D,MAAM,CAIR;AAGD,wBAAgB,aAAa,CAC3B,EAAE,EAAE,MAAM,EACV,IAAI,EAAE;IACJ,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB,GACA,IAAI,CAkBN;AAID,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAOrD;AAID,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CA4B7F;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,EAAE,CAE5C;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAGtE;AAED,wBAAgB,oBAAoB,CAClC,EAAE,EAAE,MAAM,EACV,IAAI,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,EAAE,CAAA;CAAE,GACpF,IAAI,CASN"}
@@ -0,0 +1,95 @@
1
+ import { randomBytes } from "node:crypto";
2
+ // Pair a peer (or re-pair, rotating the token) and return the new shared token.
3
+ // Only the pairing call and the peer know it; the server compares on every request.
4
+ export function pairPeer(db, peer) {
5
+ const token = randomBytes(32).toString("base64url");
6
+ pairWithToken(db, { ...peer, token });
7
+ return token;
8
+ }
9
+ // Complete pairing with a token the peer generated (shared out of band).
10
+ export function pairWithToken(db, peer) {
11
+ const now = Date.now();
12
+ db.conn.run(`INSERT INTO peers (node_id, hostname, fingerprint, addresses, paired, token, last_seen)
13
+ VALUES (?, ?, ?, ?, 1, ?, ?)
14
+ ON CONFLICT(node_id) DO UPDATE SET hostname = excluded.hostname,
15
+ fingerprint = excluded.fingerprint, paired = 1, token = excluded.token, last_seen = excluded.last_seen`, [
16
+ peer.node_id,
17
+ peer.hostname,
18
+ peer.fingerprint,
19
+ JSON.stringify(peer.addresses ?? []),
20
+ peer.token,
21
+ now,
22
+ ]);
23
+ db.conn.run("DELETE FROM pending_tokens WHERE token = ?", [peer.token]);
24
+ }
25
+ // Generate a token the peer must present with mesh_pair(node_id, token).
26
+ // Until consumed, the server accepts it as proof of pairing.
27
+ export function createPairingToken(db) {
28
+ const token = randomBytes(32).toString("base64url");
29
+ db.conn.run("INSERT OR REPLACE INTO pending_tokens (token, created_at) VALUES (?, ?)", [
30
+ token,
31
+ Date.now(),
32
+ ]);
33
+ return token;
34
+ }
35
+ // Check a bearer token: known paired peer, or a pending pairing token (which
36
+ // then completes pairing for the claimed sender node).
37
+ export function authenticateToken(db, token, from) {
38
+ const paired = findPeerByToken(db, token);
39
+ if (paired) {
40
+ return paired;
41
+ }
42
+ const pending = db.conn.get("SELECT token FROM pending_tokens WHERE token = ?", [token]);
43
+ if (!pending) {
44
+ return null;
45
+ }
46
+ // Pending tokens need the claimed sender id to bind the pairing to a node.
47
+ if (from === null || from.length === 0) {
48
+ return null;
49
+ }
50
+ pairWithToken(db, { node_id: from, hostname: "pending", fingerprint: "pending", token });
51
+ return {
52
+ node_id: from,
53
+ hostname: "pending",
54
+ fingerprint: "pending",
55
+ addresses: [],
56
+ paired: true,
57
+ token,
58
+ last_seen: Date.now(),
59
+ };
60
+ }
61
+ export function listPeers(db) {
62
+ return db.conn.all("SELECT * FROM peers ORDER BY hostname").map(rowToPeer);
63
+ }
64
+ export function findPeerByToken(db, token) {
65
+ const row = db.conn.get("SELECT * FROM peers WHERE token = ? AND paired = 1", [token]);
66
+ return row ? rowToPeer(row) : null;
67
+ }
68
+ export function upsertDiscoveredPeer(db, peer) {
69
+ const now = Date.now();
70
+ db.conn.run(`INSERT INTO peers (node_id, hostname, fingerprint, addresses, paired, token, last_seen)
71
+ VALUES (?, ?, ?, ?, 0, NULL, ?)
72
+ ON CONFLICT(node_id) DO UPDATE SET hostname = excluded.hostname,
73
+ fingerprint = excluded.fingerprint, addresses = excluded.addresses, last_seen = excluded.last_seen`, [peer.node_id, peer.hostname, peer.fingerprint, JSON.stringify(peer.addresses), now]);
74
+ }
75
+ function rowToPeer(row) {
76
+ return {
77
+ node_id: row.node_id,
78
+ hostname: row.hostname,
79
+ fingerprint: row.fingerprint,
80
+ addresses: parseAddresses(row.addresses),
81
+ paired: row.paired === 1,
82
+ token: row.token,
83
+ last_seen: row.last_seen,
84
+ };
85
+ }
86
+ function parseAddresses(raw) {
87
+ try {
88
+ const parsed = JSON.parse(raw);
89
+ return Array.isArray(parsed) ? parsed.filter((a) => typeof a === "string") : [];
90
+ }
91
+ catch {
92
+ return [];
93
+ }
94
+ }
95
+ //# sourceMappingURL=peers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"peers.js","sourceRoot":"","sources":["../../src/lib/peers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { Server } from "node:http";
2
+ import type { MeshDb } from "./db.js";
3
+ export type MeshServerOptions = {
4
+ db: MeshDb;
5
+ port: number;
6
+ };
7
+ export type MeshServer = {
8
+ server: Server;
9
+ port: number;
10
+ };
11
+ export declare function startMeshServer(options: MeshServerOptions): Promise<MeshServer>;
12
+ //# sourceMappingURL=serve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/lib/serve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAmB,MAAM,EAAkB,MAAM,WAAW,CAAC;AAGzE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAMtC,MAAM,MAAM,iBAAiB,GAAG;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAKF,wBAAsB,eAAe,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,UAAU,CAAC,CAuBrF"}
@@ -0,0 +1,165 @@
1
+ import { createServer } from "node:http";
2
+ import { authenticateToken } from "./peers.js";
3
+ import { receiveMessage, unreadCount } from "./mail.js";
4
+ const BODY_LIMIT = 64 * 1024;
5
+ // Bind the mesh HTTP server and resolve the actual port. When the requested
6
+ // port is taken (two mesh nodes on one machine, e.g. a spawned worker loading
7
+ // the same plugin), fall back to an ephemeral port instead of crashing.
8
+ export async function startMeshServer(options) {
9
+ const server = createServer((req, res) => {
10
+ void handle(options.db, req, res);
11
+ });
12
+ const port = await new Promise((resolve, reject) => {
13
+ const tryListen = (requested) => {
14
+ server.once("error", (err) => {
15
+ if (err.code === "EADDRINUSE" && requested !== 0) {
16
+ tryListen(0);
17
+ return;
18
+ }
19
+ reject(err);
20
+ });
21
+ server.listen(requested, () => {
22
+ const address = server.address();
23
+ resolve(address !== null && typeof address !== "string" ? address.port : requested);
24
+ });
25
+ };
26
+ tryListen(options.port);
27
+ });
28
+ return { server, port };
29
+ }
30
+ async function handle(db, req, res) {
31
+ const url = new URL(req.url ?? "/", "http://localhost");
32
+ const contentLength = Number(req.headers["content-length"] ?? "0");
33
+ if (req.method === "POST" && contentLength > BODY_LIMIT) {
34
+ json(res, 413, { error: "body too large" });
35
+ return;
36
+ }
37
+ if (req.method !== "GET" && req.method !== "POST") {
38
+ json(res, 405, { error: "method not allowed" });
39
+ return;
40
+ }
41
+ if (req.method === "POST") {
42
+ const body = await readBody(req);
43
+ if (body === null) {
44
+ json(res, 413, { error: "body too large" });
45
+ return;
46
+ }
47
+ if (url.pathname === "/mail") {
48
+ const parsed = parseMail(body);
49
+ if (!parsed) {
50
+ json(res, 400, { error: "invalid mail body" });
51
+ return;
52
+ }
53
+ // Authenticate with the claimed sender so pending pairing tokens bind
54
+ // to the node id in the message.
55
+ const mailPeer = authenticate(db, req, parsed.from);
56
+ if (!mailPeer) {
57
+ json(res, 401, { error: "unauthorized" });
58
+ return;
59
+ }
60
+ if (parsed.from !== mailPeer.node_id) {
61
+ json(res, 401, { error: "from does not match token" });
62
+ return;
63
+ }
64
+ receiveMessage(db, parsed);
65
+ json(res, 200, { ok: true, unread: unreadCount(db) });
66
+ return;
67
+ }
68
+ const peer = authenticate(db, req);
69
+ if (!peer) {
70
+ json(res, 401, { error: "unauthorized" });
71
+ return;
72
+ }
73
+ if (url.pathname === "/announce") {
74
+ const parsed = parseAnnounce(body);
75
+ if (!parsed) {
76
+ json(res, 400, { error: "invalid announce body" });
77
+ return;
78
+ }
79
+ touchAddress(db, peer.node_id, parsed.addresses);
80
+ json(res, 200, { ok: true });
81
+ return;
82
+ }
83
+ }
84
+ if (req.method === "GET" && url.pathname === "/status") {
85
+ const peer = authenticate(db, req);
86
+ if (!peer) {
87
+ json(res, 401, { error: "unauthorized" });
88
+ return;
89
+ }
90
+ json(res, 200, {
91
+ node_id: peer.node_id,
92
+ unread: unreadCount(db),
93
+ });
94
+ return;
95
+ }
96
+ json(res, 404, { error: "not found" });
97
+ }
98
+ function authenticate(db, req, from = null) {
99
+ const header = req.headers.authorization ?? "";
100
+ const match = /^Bearer (.+)$/.exec(header);
101
+ if (!match?.[1]) {
102
+ return null;
103
+ }
104
+ const peer = authenticateToken(db, match[1], from);
105
+ return peer ? { node_id: peer.node_id } : null;
106
+ }
107
+ function touchAddress(db, nodeId, addresses) {
108
+ db.conn.run("UPDATE peers SET addresses = ?, last_seen = ? WHERE node_id = ?", [
109
+ JSON.stringify(addresses),
110
+ Date.now(),
111
+ nodeId,
112
+ ]);
113
+ }
114
+ function readBody(req) {
115
+ return new Promise((resolve) => {
116
+ const chunks = [];
117
+ let size = 0;
118
+ let limited = false;
119
+ req.on("data", (chunk) => {
120
+ size += chunk.length;
121
+ if (size > BODY_LIMIT) {
122
+ limited = true;
123
+ chunks.length = 0;
124
+ return;
125
+ }
126
+ if (!limited) {
127
+ chunks.push(chunk);
128
+ }
129
+ });
130
+ req.on("end", () => resolve(limited ? null : Buffer.concat(chunks).toString("utf8")));
131
+ req.on("error", () => resolve(null));
132
+ });
133
+ }
134
+ function parseMail(raw) {
135
+ try {
136
+ const parsed = JSON.parse(raw);
137
+ if (typeof parsed.from !== "string" ||
138
+ typeof parsed.subject !== "string" ||
139
+ typeof parsed.body !== "string") {
140
+ return null;
141
+ }
142
+ return { from: parsed.from, subject: parsed.subject, body: parsed.body };
143
+ }
144
+ catch {
145
+ return null;
146
+ }
147
+ }
148
+ function parseAnnounce(raw) {
149
+ try {
150
+ const parsed = JSON.parse(raw);
151
+ if (!Array.isArray(parsed.addresses)) {
152
+ return null;
153
+ }
154
+ const addresses = parsed.addresses.filter((a) => typeof a === "string");
155
+ return { addresses };
156
+ }
157
+ catch {
158
+ return null;
159
+ }
160
+ }
161
+ function json(res, status, body) {
162
+ res.writeHead(status, { "content-type": "application/json" });
163
+ res.end(JSON.stringify(body));
164
+ }
165
+ //# sourceMappingURL=serve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serve.js","sourceRoot":"","sources":["../../src/lib/serve.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import { type MeshDb } from "./db.js";
2
+ import { type Worker } from "./workers.js";
3
+ export type SpawnedProcess = {
4
+ pid: number;
5
+ stdout: {
6
+ on(event: "data", listener: (chunk: string) => void): void;
7
+ };
8
+ on(event: "exit", listener: (code: number | null) => void): void;
9
+ kill(signal?: string): void;
10
+ };
11
+ export type SpawnFn = (argv: string[]) => SpawnedProcess;
12
+ export type KillFn = (id: string, signal: string) => void;
13
+ export declare function nodeSpawn(argv: string[]): SpawnedProcess;
14
+ export type RunWorkerInput = {
15
+ task: string;
16
+ worktree_path: string;
17
+ branch: string;
18
+ model?: string;
19
+ agent?: string;
20
+ auto?: boolean;
21
+ spawnFn: SpawnFn;
22
+ killFn?: KillFn;
23
+ onSpawned?: (worker: Worker, proc: SpawnedProcess) => void;
24
+ };
25
+ export type StopOptions = {
26
+ killFn?: KillFn;
27
+ };
28
+ export declare function runWorker(db: MeshDb, dataDir: string, input: RunWorkerInput): Worker;
29
+ export declare function stopWorker(db: MeshDb, id: string, options?: StopOptions): boolean;
30
+ export declare function defaultKill(id: string, signal: string): void;
31
+ export declare function stopAllWorkers(db: MeshDb, options?: StopOptions): void;
32
+ export declare function liveWorkerIds(): string[];
33
+ //# sourceMappingURL=spawn.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spawn.d.ts","sourceRoot":"","sources":["../../src/lib/spawn.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,SAAS,CAAC;AACtC,OAAO,EAAmD,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AAE5F,MAAM,MAAM,cAAc,GAAG;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE;QACN,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC;KAC5D,CAAC;IACF,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,GAAG,IAAI,CAAC;IACjE,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,OAAO,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,cAAc,CAAC;AACzD,MAAM,MAAM,MAAM,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;AAI1D,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,cAAc,CAmBxD;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;CAC5D,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAKF,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,CAgCpF;AAID,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,CAarF;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAG5D;AAID,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,IAAI,CAQ1E;AAED,wBAAgB,aAAa,IAAI,MAAM,EAAE,CAExC"}