@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
package/dist/plugin.js ADDED
@@ -0,0 +1,310 @@
1
+ import { tool } from "@opencode-ai/plugin";
2
+ import { Bonjour } from "bonjour-service";
3
+ import { rememberPeer } from "./lib/discovery.js";
4
+ import { sendMail } from "./lib/client.js";
5
+ import { openMeshDb } from "./lib/db.js";
6
+ import { advertiseMesh, browseMesh, tailnetAddresses, } from "./lib/discovery.js";
7
+ import { loadOrCreateIdentity } from "./lib/identity.js";
8
+ import { ackMessages, unreadMessages } from "./lib/mail.js";
9
+ import { releaseLock, tryLock } from "./lib/locks.js";
10
+ import { createPairingToken, listPeers, pairWithToken } from "./lib/peers.js";
11
+ import { startMeshServer } from "./lib/serve.js";
12
+ import { nodeSpawn, runWorker, stopAllWorkers, stopWorker } from "./lib/spawn.js";
13
+ import { createWorkerWorktree, defaultWorktreeFactory, extractClientFetch, removeWorkerWorktree, } from "./lib/worktree.js";
14
+ import { deleteWorker, getWorker, listWorkers } from "./lib/workers.js";
15
+ import { readFileSync } from "node:fs";
16
+ import { join } from "node:path";
17
+ const DEFAULT_PORT = 4399;
18
+ export const MeshPlugin = async (input, options) => {
19
+ const dataDir = options?.dataDir ?? defaultDataDir();
20
+ const identity = loadOrCreateIdentity(dataDir);
21
+ const db = openMeshDb(`${dataDir}/mesh.db`);
22
+ const { server, port } = await startMeshServer({ db, port: options?.port ?? DEFAULT_PORT });
23
+ const worktreeClient = options?.worktreeClient ??
24
+ defaultWorktreeFactory(input.serverUrl.toString(), input.directory, extractClientFetch(input.client));
25
+ const spawnFn = options?.spawnFn ?? nodeSpawn;
26
+ let advertiseHandle = null;
27
+ let browseHandle = null;
28
+ if (options?.advertise !== false) {
29
+ const bonjour = new Bonjour();
30
+ advertiseHandle = advertiseMesh({
31
+ bonjour,
32
+ port,
33
+ node_id: identity.node_id,
34
+ fingerprint: identity.fingerprint,
35
+ });
36
+ browseHandle = browseMesh({
37
+ bonjour,
38
+ onDiscovered: (peer) => {
39
+ if (peer.node_id !== identity.node_id) {
40
+ rememberPeer(db, peer);
41
+ }
42
+ },
43
+ });
44
+ }
45
+ const hooks = {
46
+ tool: {
47
+ mesh_status: tool({
48
+ description: "Show this node's identity, server address, and known peers.",
49
+ args: {},
50
+ execute: async () => {
51
+ const peers = listPeers(db);
52
+ const tailnet = await tailnetAddresses();
53
+ return JSON.stringify({
54
+ node_id: identity.node_id,
55
+ hostname: identity.hostname,
56
+ fingerprint: identity.fingerprint,
57
+ server: `127.0.0.1:${port}`,
58
+ tailnet_addresses: tailnet,
59
+ peers: peers.map((p) => ({
60
+ node_id: p.node_id,
61
+ hostname: p.hostname,
62
+ paired: p.paired,
63
+ addresses: p.addresses,
64
+ })),
65
+ }, null, 2);
66
+ },
67
+ }),
68
+ mesh_pair: tool({
69
+ description: "Pair with a peer. With no args: emit a pairing offer (token, node id, server address) for the peer's mesh_pair. With node_id, token, and address: complete pairing started by the other side.",
70
+ args: {
71
+ node_id: tool.schema
72
+ .string()
73
+ .optional()
74
+ .describe("Peer node id, from its mesh_pair output"),
75
+ token: tool.schema.string().optional().describe("Pairing token from the peer's offer"),
76
+ address: tool.schema.string().optional().describe("Peer server address as host:port"),
77
+ },
78
+ execute: async (args) => {
79
+ if (typeof args.token === "string" && typeof args.node_id === "string") {
80
+ pairWithToken(db, {
81
+ node_id: args.node_id,
82
+ hostname: "manual",
83
+ fingerprint: "manual",
84
+ token: args.token,
85
+ addresses: args.address !== undefined ? [args.address] : [],
86
+ });
87
+ return `paired with ${args.node_id}`;
88
+ }
89
+ const token = createPairingToken(db);
90
+ return `peer should run: mesh_pair(node_id="${identity.node_id}", token="${token}", address="127.0.0.1:${port}")`;
91
+ },
92
+ }),
93
+ mesh_send: tool({
94
+ description: "Send a message to a paired peer's mailbox.",
95
+ args: {
96
+ peer: tool.schema.string().describe("Peer node id or hostname"),
97
+ subject: tool.schema.string().describe("Short subject"),
98
+ body: tool.schema.string().describe("Message body"),
99
+ },
100
+ execute: async (args) => {
101
+ const peer = findSendablePeer(db, args.peer);
102
+ if (peer === null) {
103
+ return `error: no paired peer matching ${args.peer}; run mesh_status`;
104
+ }
105
+ const address = peer.addresses[0];
106
+ if (address === undefined) {
107
+ return `error: peer ${peer.node_id} has no known address; share addresses manually or wait for discovery`;
108
+ }
109
+ const host = address.includes(":") ? address : `${address}:${port}`;
110
+ const result = await sendMail({
111
+ url: `http://${host}`,
112
+ token: peer.token ?? "",
113
+ from: identity.node_id,
114
+ subject: args.subject,
115
+ body: args.body,
116
+ });
117
+ return `sent to ${peer.node_id}: ${result}`;
118
+ },
119
+ }),
120
+ mesh_inbox: tool({
121
+ description: "List unread messages from peers and mark them read.",
122
+ args: {
123
+ ack: tool.schema.boolean().optional().describe("Mark messages read (default true)"),
124
+ },
125
+ execute: async (args) => {
126
+ const messages = unreadMessages(db);
127
+ if (messages.length === 0) {
128
+ return "inbox empty";
129
+ }
130
+ if (args.ack !== false) {
131
+ ackMessages(db, messages.map((m) => m.id));
132
+ }
133
+ return JSON.stringify(messages, null, 2);
134
+ },
135
+ }),
136
+ mesh_lock: tool({
137
+ description: "Claim exclusive ownership of a file or worktree path so other sessions wait.",
138
+ args: {
139
+ path: tool.schema.string().describe("Path to lock (relative to worktree or absolute)"),
140
+ ttl: tool.schema
141
+ .number()
142
+ .optional()
143
+ .describe("Seconds before the lock expires (default 600)"),
144
+ },
145
+ execute: async (args, context) => {
146
+ const granted = tryLock(db, {
147
+ path: args.path,
148
+ owner_node: identity.node_id,
149
+ owner_session: context.sessionID,
150
+ ttl_ms: (args.ttl ?? 600) * 1000,
151
+ now: Date.now(),
152
+ });
153
+ return granted
154
+ ? `locked ${args.path}`
155
+ : `error: ${args.path} is locked by another session; try later or run mesh_status`;
156
+ },
157
+ }),
158
+ mesh_unlock: tool({
159
+ description: "Release a claim made with mesh_lock.",
160
+ args: {
161
+ path: tool.schema.string().describe("Path to unlock"),
162
+ },
163
+ execute: async (args) => {
164
+ const released = releaseLock(db, { path: args.path, owner_node: identity.node_id });
165
+ return released
166
+ ? `released ${args.path}`
167
+ : `error: ${args.path} was not locked by this node`;
168
+ },
169
+ }),
170
+ mesh_spawn: tool({
171
+ description: "Run a task as a background worker in an isolated opencode worktree (headless, --auto permissions by default). Returns worker id, worktree, and branch.",
172
+ args: {
173
+ task: tool.schema.string().describe("Task description given to the worker"),
174
+ name: tool.schema
175
+ .string()
176
+ .optional()
177
+ .describe("Worktree/branch name (random by default)"),
178
+ model: tool.schema.string().optional().describe("provider/model for the worker"),
179
+ agent: tool.schema.string().optional().describe("Agent for the worker"),
180
+ auto: tool.schema
181
+ .boolean()
182
+ .optional()
183
+ .describe("Grant all permissions to the worker (default true)"),
184
+ },
185
+ execute: async (args) => {
186
+ try {
187
+ const info = await createWorkerWorktree(worktreeClient, {
188
+ ...(args.name !== undefined ? { name: args.name } : {}),
189
+ });
190
+ const worker = runWorker(db, dataDir, {
191
+ task: args.task,
192
+ worktree_path: info.directory,
193
+ branch: info.branch ?? info.name,
194
+ ...(args.model !== undefined ? { model: args.model } : {}),
195
+ ...(args.agent !== undefined ? { agent: args.agent } : {}),
196
+ ...(args.auto !== undefined ? { auto: args.auto } : {}),
197
+ spawnFn,
198
+ });
199
+ return JSON.stringify({
200
+ worker_id: worker.id,
201
+ worktree: info.directory,
202
+ branch: worker.branch,
203
+ pid: worker.pid,
204
+ }, null, 2);
205
+ }
206
+ catch (error) {
207
+ return `error: ${error instanceof Error ? error.message : String(error)}`;
208
+ }
209
+ },
210
+ }),
211
+ mesh_workers: tool({
212
+ description: "List spawned workers with their lifecycle state.",
213
+ args: {},
214
+ execute: async () => {
215
+ const workers = listWorkers(db);
216
+ if (workers.length === 0) {
217
+ return "[]";
218
+ }
219
+ return JSON.stringify(workers, null, 2);
220
+ },
221
+ }),
222
+ mesh_worker_logs: tool({
223
+ description: "Show the tail of a worker's captured output.",
224
+ args: {
225
+ worker_id: tool.schema.string().describe("Worker id, from mesh_spawn or mesh_workers"),
226
+ tail: tool.schema.number().optional().describe("Number of lines (default 50)"),
227
+ },
228
+ execute: async (args) => {
229
+ const worker = getWorker(db, args.worker_id);
230
+ if (!worker) {
231
+ return `error: unknown worker ${args.worker_id}`;
232
+ }
233
+ const path = join(dataDir, "workers", `${worker.id}.log`);
234
+ let content;
235
+ try {
236
+ content = readFileSync(path, "utf8");
237
+ }
238
+ catch {
239
+ return "no log output yet";
240
+ }
241
+ const lines = content.split("\n");
242
+ const limit = args.tail ?? 50;
243
+ return lines.slice(-limit - 1, -1).join("\n") || "no log output yet";
244
+ },
245
+ }),
246
+ mesh_worker_stop: tool({
247
+ description: "Stop a running worker (SIGTERM).",
248
+ args: {
249
+ worker_id: tool.schema.string().describe("Worker id"),
250
+ },
251
+ execute: async (args) => {
252
+ try {
253
+ stopWorker(db, args.worker_id);
254
+ return `stopped ${args.worker_id}`;
255
+ }
256
+ catch (error) {
257
+ return `error: ${error instanceof Error ? error.message : String(error)}`;
258
+ }
259
+ },
260
+ }),
261
+ mesh_worker_remove: tool({
262
+ description: "Remove a finished worker: deletes the worktree (native API) and the registry row.",
263
+ args: {
264
+ worker_id: tool.schema.string().describe("Worker id"),
265
+ force: tool.schema.boolean().optional().describe("Stop a running worker before removal"),
266
+ },
267
+ execute: async (args) => {
268
+ const worker = getWorker(db, args.worker_id);
269
+ if (!worker) {
270
+ return `error: unknown worker ${args.worker_id}`;
271
+ }
272
+ if (worker.status === "running") {
273
+ if (args.force !== true) {
274
+ return `error: worker ${worker.id} is running; use force=true to stop and remove it`;
275
+ }
276
+ // force: terminate first so no process keeps writing into a deleted worktree.
277
+ stopWorker(db, worker.id);
278
+ }
279
+ try {
280
+ await removeWorkerWorktree(worktreeClient, { directory: worker.worktree_path });
281
+ }
282
+ catch (error) {
283
+ return `error: worktree remove failed: ${error instanceof Error ? error.message : String(error)}`;
284
+ }
285
+ deleteWorker(db, args.worker_id);
286
+ return `removed ${worker.id} (worktree and registry row deleted)`;
287
+ },
288
+ }),
289
+ },
290
+ };
291
+ return {
292
+ ...hooks,
293
+ dispose: async () => {
294
+ stopAllWorkers(db);
295
+ advertiseHandle?.close();
296
+ browseHandle?.close();
297
+ await new Promise((resolve) => server.close(() => resolve()));
298
+ db.close();
299
+ },
300
+ };
301
+ };
302
+ function defaultDataDir() {
303
+ const dataHome = process.env.XDG_DATA_HOME ?? `${process.env.HOME ?? ""}/.local/share`;
304
+ return `${dataHome}/opencode-mesh`;
305
+ }
306
+ function findSendablePeer(db, query) {
307
+ const peers = listPeers(db).filter((p) => p.paired);
308
+ return peers.find((p) => p.node_id === query) ?? peers.find((p) => p.hostname === query) ?? null;
309
+ }
310
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ export type Peer = {
2
+ node_id: string;
3
+ hostname: string;
4
+ fingerprint: string;
5
+ addresses: string[];
6
+ paired: boolean;
7
+ token: string | null;
8
+ last_seen: number;
9
+ };
10
+ export type MailMessage = {
11
+ id: string;
12
+ from: string;
13
+ to: string;
14
+ session_id: string | null;
15
+ subject: string;
16
+ body: string;
17
+ status: "unread" | "read";
18
+ created_at: number;
19
+ };
20
+ export type Lock = {
21
+ path: string;
22
+ owner: string;
23
+ node_id: string;
24
+ expires_at: number;
25
+ };
26
+ //# sourceMappingURL=peer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"peer.d.ts","sourceRoot":"","sources":["../../src/types/peer.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,IAAI,GAAG;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,QAAQ,GAAG,MAAM,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,IAAI,GAAG;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=peer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"peer.js","sourceRoot":"","sources":["../../src/types/peer.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@log0u7/opencode-mesh",
3
+ "version": "0.1.0",
4
+ "description": "Multi-session coordination for OpenCode: session coordination on one machine and a peer pool across LAN or Tailscale, with pairing, mailbox, and worktree locks.",
5
+ "type": "module",
6
+ "license": "Apache-2.0",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/log0u7/opencode-mesh.git"
10
+ },
11
+ "main": "./dist/index.js",
12
+ "types": "./dist/index.d.ts",
13
+ "exports": {
14
+ ".": {
15
+ "types": "./dist/index.d.ts",
16
+ "default": "./dist/index.js"
17
+ },
18
+ "./server": {
19
+ "types": "./dist/index.d.ts",
20
+ "default": "./dist/index.js"
21
+ }
22
+ },
23
+ "oc-plugin": [
24
+ "server"
25
+ ],
26
+ "files": [
27
+ "dist",
28
+ "README.md",
29
+ "LICENSE",
30
+ "CHANGELOG.md"
31
+ ],
32
+ "scripts": {
33
+ "build": "tsc -p tsconfig.json",
34
+ "build:check": "pnpm run build && pnpm pack --dry-run",
35
+ "check": "biome check .",
36
+ "format": "biome format --write .",
37
+ "typecheck": "tsc --noEmit",
38
+ "test": "vitest run",
39
+ "test:watch": "vitest",
40
+ "test:coverage": "vitest run --coverage",
41
+ "verify": "pnpm run check && pnpm run typecheck && pnpm run test",
42
+ "prepare": "lefthook install"
43
+ },
44
+ "packageManager": "pnpm@11.0.0",
45
+ "engines": {
46
+ "node": ">=22.0.0"
47
+ },
48
+ "dependencies": {
49
+ "bonjour-service": "^1.3.0"
50
+ },
51
+ "devDependencies": {
52
+ "@biomejs/biome": "2.5.13",
53
+ "@opencode-ai/plugin": "1.18.30",
54
+ "@opencode-ai/sdk": "1.18.30",
55
+ "@types/node": "^22.0.0",
56
+ "@vitest/coverage-v8": "^5.0.1",
57
+ "lefthook": "2.1.12",
58
+ "typescript": "7.0.2",
59
+ "vitest": "^5.0.0"
60
+ }
61
+ }