dsh-mesh-node 0.0.0-stage → 0.0.2

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 (55) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +8 -2
  3. package/package.json +43 -4
  4. package/src/cli.js +1246 -0
  5. package/src/core/acl.js +81 -0
  6. package/src/core/audit.js +75 -0
  7. package/src/core/autostart.js +119 -0
  8. package/src/core/cluster.js +341 -0
  9. package/src/core/config.js +622 -0
  10. package/src/core/desktop-proxy.js +85 -0
  11. package/src/core/ds-web.js +394 -0
  12. package/src/core/host-agent.js +263 -0
  13. package/src/core/jobs.js +259 -0
  14. package/src/core/pairing.js +141 -0
  15. package/src/core/peer-client.js +273 -0
  16. package/src/core/peer-doctor.js +294 -0
  17. package/src/core/peer-http.js +61 -0
  18. package/src/core/repair.js +93 -0
  19. package/src/core/restart-policy.js +21 -0
  20. package/src/core/result.js +29 -0
  21. package/src/core/sakura-api.js +103 -0
  22. package/src/core/session-profile.js +146 -0
  23. package/src/core/tls.js +160 -0
  24. package/src/core/tunnels.js +212 -0
  25. package/src/core/util.js +68 -0
  26. package/src/e2e-autostart.js +58 -0
  27. package/src/e2e-channel.js +384 -0
  28. package/src/e2e-cluster.js +417 -0
  29. package/src/e2e-host.js +411 -0
  30. package/src/e2e-onboard.js +232 -0
  31. package/src/e2e-pair.js +317 -0
  32. package/src/e2e-plugin.js +395 -0
  33. package/src/e2e-repair.js +297 -0
  34. package/src/index.js +20 -0
  35. package/src/peer-bridge.js +124 -0
  36. package/src/selftest.js +872 -0
  37. package/src/server.js +317 -0
  38. package/src/tools/alerts.js +80 -0
  39. package/src/tools/channel.js +253 -0
  40. package/src/tools/cluster.js +235 -0
  41. package/src/tools/config-patch.js +182 -0
  42. package/src/tools/control-apps.js +469 -0
  43. package/src/tools/control-path.js +160 -0
  44. package/src/tools/control.js +232 -0
  45. package/src/tools/core.js +79 -0
  46. package/src/tools/delegate.js +653 -0
  47. package/src/tools/fs.js +436 -0
  48. package/src/tools/git.js +266 -0
  49. package/src/tools/host.js +305 -0
  50. package/src/tools/index.js +122 -0
  51. package/src/tools/repair-fs.js +130 -0
  52. package/src/tools/repair.js +317 -0
  53. package/src/tools/session-history.js +60 -0
  54. package/src/tools/shell.js +66 -0
  55. package/src/tools/tasks.js +117 -0
package/src/index.js ADDED
@@ -0,0 +1,20 @@
1
+ export { startMeshServer, buildMcpServer } from './server.js'
2
+ export {
3
+ MESH_VERSION,
4
+ CONFIG_VERSION,
5
+ DEFAULT_ALLOW,
6
+ meshHome,
7
+ loadConfig,
8
+ saveConfig,
9
+ loadSecrets,
10
+ configPath,
11
+ secretsPath,
12
+ defaultConfig,
13
+ createConfigLoader,
14
+ verifyBearer,
15
+ isInsideRoots
16
+ } from './core/config.js'
17
+ export { makeAudit, tailAudit, auditPath } from './core/audit.js'
18
+ export { JobRunner, pruneJobs, TERMINAL_STATES } from './core/jobs.js'
19
+ export { buildAll, toolNames, isAllowed, dispatch, registerTools } from './tools/index.js'
20
+ export { runSelftest } from './selftest.js'
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Stdio MCP bridge to a paired peer.
3
+ *
4
+ * Why this exists: the overlay from `peer patch` uses the streamable-http
5
+ * transport, so a self-signed peer only works when the DSH process itself has
6
+ * the peer certificate in NODE_EXTRA_CA_CERTS. When that env is missing (or the
7
+ * overlay was written after the process started), the mcp-client reports only
8
+ * "server is disconnected" and the model never sees a single tool — a silent
9
+ * failure that cost a full debugging session on 2026-10-20.
10
+ *
11
+ * This bridge is launched by DSH as a stdio child. It performs the pinned TLS
12
+ * handshake itself (see core/peer-client.js), so the DSH process needs no CA
13
+ * trust store, and a dead peer becomes a visible child exit that the client's
14
+ * reconnect policy handles instead of an unexplained disconnected server.
15
+ *
16
+ * Wire it with `node src/cli.js peer patch --transport stdio`, then restart the
17
+ * DSH instance so the overlay entry is loaded (overlays are read at boot).
18
+ */
19
+ import { McpServer, fromJsonSchema } from '@modelcontextprotocol/server'
20
+ import { StdioServerTransport } from '@modelcontextprotocol/server/stdio'
21
+ import { MESH_VERSION } from './core/config.js'
22
+ import { connectPeer } from './core/peer-client.js'
23
+
24
+ const EMPTY_OBJECT_SCHEMA = { type: 'object', properties: {} }
25
+
26
+ /**
27
+ * Peer tool JSON Schema -> the Standard Schema that McpServer.registerTool
28
+ * accepts. Tools with no arguments (or a non-object schema) get an empty
29
+ * object schema: passing a schema the SDK cannot convert would otherwise drop
30
+ * the tool entirely.
31
+ */
32
+ export function toToolInputSchema(inputSchema) {
33
+ if (!inputSchema || typeof inputSchema !== 'object' || inputSchema.type !== 'object') {
34
+ return fromJsonSchema(EMPTY_OBJECT_SCHEMA)
35
+ }
36
+ const properties = inputSchema.properties
37
+ if (!properties || typeof properties !== 'object' || Object.keys(properties).length === 0) {
38
+ return fromJsonSchema(EMPTY_OBJECT_SCHEMA)
39
+ }
40
+ return fromJsonSchema(inputSchema)
41
+ }
42
+
43
+ /**
44
+ * Start the stdio bridge and resolve once it is serving.
45
+ *
46
+ * @param peer - paired peer record {name, url, token, certFingerprint}.
47
+ * @param options.serverName - local MCP namespace (DSH shows `mcp__<serverName>__*`).
48
+ * @param options.timeoutMs - per MCP request timeout for calls to the peer.
49
+ * @param options.log - logger; MUST write to stderr, stdout carries the protocol.
50
+ * @returns handles for tests plus the proxied/skipped tool names.
51
+ */
52
+ export async function runPeerBridge(peer, { serverName, timeoutMs = 120000, log = console } = {}) {
53
+ const name = String(peer?.name || peer?.id || 'peer')
54
+ const namespace = String(serverName || `dsh-mesh-${name}`)
55
+ .replace(/[^A-Za-z0-9_-]/g, '-')
56
+ .slice(0, 32)
57
+ const connection = await connectPeer(peer, { timeoutMs, clientName: 'dsh-mesh-peer-bridge' })
58
+ let listed
59
+ try {
60
+ listed = await connection.client.listTools()
61
+ } catch (error) {
62
+ await connection.close()
63
+ throw error
64
+ }
65
+ const peerTools = listed?.tools ?? []
66
+ const server = new McpServer(
67
+ { name: namespace, version: MESH_VERSION },
68
+ {
69
+ instructions: `Tools proxied from dsh-mesh peer "${name}" (${peer.url}). This process pins the peer certificate itself, so it does not depend on NODE_EXTRA_CA_CERTS in the DSH process.`
70
+ }
71
+ )
72
+ const skipped = []
73
+ for (const tool of peerTools) {
74
+ let inputSchema
75
+ try {
76
+ inputSchema = toToolInputSchema(tool.inputSchema)
77
+ } catch (error) {
78
+ skipped.push({ tool: tool.name, reason: error?.message || String(error) })
79
+ continue
80
+ }
81
+ server.registerTool(
82
+ tool.name,
83
+ { description: tool.description || `proxied dsh-mesh tool ${tool.name}`, inputSchema },
84
+ async (args) => {
85
+ try {
86
+ return await connection.client.callTool({ name: tool.name, arguments: args ?? {} })
87
+ } catch (error) {
88
+ const code = typeof error?.code === 'string' ? `[${error.code}] ` : ''
89
+ return {
90
+ content: [{ type: 'text', text: `peer bridge call failed: ${code}${error?.message || error}` }],
91
+ isError: true
92
+ }
93
+ }
94
+ }
95
+ )
96
+ }
97
+
98
+ const transport = new StdioServerTransport()
99
+ let closing = false
100
+ const close = async (reason) => {
101
+ if (closing) return
102
+ closing = true
103
+ log.error?.(`[peer-bridge] ${reason} — exiting`)
104
+ try {
105
+ await server.close()
106
+ } catch {
107
+ /* already closed */
108
+ }
109
+ await connection.close()
110
+ // The bridge is a child of DSH: never linger after the pipe is gone.
111
+ process.exit(0)
112
+ }
113
+ transport.onclose = () => {
114
+ void close('stdio closed by the DSH client')
115
+ }
116
+ connection.client.onclose = () => {
117
+ void close('peer connection dropped')
118
+ }
119
+ await server.connect(transport)
120
+ log.error?.(
121
+ `[peer-bridge] peer="${name}" serverName="${namespace}" tools=${peerTools.length}${skipped.length ? ` skipped=${skipped.length}` : ''}`
122
+ )
123
+ return { server, connection, namespace, toolCount: peerTools.length, skipped }
124
+ }