claudemesh-cli 0.7.0 → 0.8.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/dist/index.js +611 -31
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -47478,6 +47478,10 @@ var TOOLS = [
|
|
|
47478
47478
|
required: ["name", "description", "inputSchema"]
|
|
47479
47479
|
},
|
|
47480
47480
|
description: "Tool definitions to expose"
|
|
47481
|
+
},
|
|
47482
|
+
persistent: {
|
|
47483
|
+
type: "boolean",
|
|
47484
|
+
description: "If true, registration survives peer disconnect. Other peers see it as 'offline' until you reconnect. Default: false"
|
|
47481
47485
|
}
|
|
47482
47486
|
},
|
|
47483
47487
|
required: ["server_name", "description", "tools"]
|
|
@@ -47659,6 +47663,75 @@ var TOOLS = [
|
|
|
47659
47663
|
},
|
|
47660
47664
|
required: ["name"]
|
|
47661
47665
|
}
|
|
47666
|
+
},
|
|
47667
|
+
{
|
|
47668
|
+
name: "mesh_mcp_deploy",
|
|
47669
|
+
description: "Deploy an MCP server to the mesh from a zip file or git repo. Runs on the broker VPS, persists across peer sessions. Default scope: private (only you).",
|
|
47670
|
+
inputSchema: {
|
|
47671
|
+
type: "object",
|
|
47672
|
+
properties: {
|
|
47673
|
+
server_name: { type: "string", description: "Unique name for the server in this mesh" },
|
|
47674
|
+
file_id: { type: "string", description: "File ID of uploaded zip (from share_file)" },
|
|
47675
|
+
git_url: { type: "string", description: "Git repo URL" },
|
|
47676
|
+
git_branch: { type: "string", description: "Branch to clone (default: main)" },
|
|
47677
|
+
env: { type: "object", description: "Environment variables. Use $vault:<key> for vault secrets." },
|
|
47678
|
+
runtime: { type: "string", enum: ["node", "python", "bun"], description: "Runtime (auto-detected if omitted)" },
|
|
47679
|
+
memory_mb: { type: "number", description: "Memory limit in MB (default: 256)" },
|
|
47680
|
+
network_allow: { type: "array", items: { type: "string" }, description: "Allowed outbound hosts (default: none)" },
|
|
47681
|
+
scope: { description: "Visibility: 'peer' (default), 'mesh', or {group/groups/role/peers}" }
|
|
47682
|
+
},
|
|
47683
|
+
required: ["server_name"]
|
|
47684
|
+
}
|
|
47685
|
+
},
|
|
47686
|
+
{
|
|
47687
|
+
name: "mesh_mcp_undeploy",
|
|
47688
|
+
description: "Stop and remove a managed MCP server from the mesh.",
|
|
47689
|
+
inputSchema: { type: "object", properties: { server_name: { type: "string" } }, required: ["server_name"] }
|
|
47690
|
+
},
|
|
47691
|
+
{
|
|
47692
|
+
name: "mesh_mcp_update",
|
|
47693
|
+
description: "Pull latest code and restart a git-sourced MCP server.",
|
|
47694
|
+
inputSchema: { type: "object", properties: { server_name: { type: "string" } }, required: ["server_name"] }
|
|
47695
|
+
},
|
|
47696
|
+
{
|
|
47697
|
+
name: "mesh_mcp_logs",
|
|
47698
|
+
description: "View recent logs from a managed MCP server.",
|
|
47699
|
+
inputSchema: { type: "object", properties: { server_name: { type: "string" }, lines: { type: "number", description: "Lines (default: 50, max: 1000)" } }, required: ["server_name"] }
|
|
47700
|
+
},
|
|
47701
|
+
{
|
|
47702
|
+
name: "mesh_mcp_scope",
|
|
47703
|
+
description: "Get or set the visibility scope of a deployed MCP server.",
|
|
47704
|
+
inputSchema: { type: "object", properties: { server_name: { type: "string" }, scope: { description: "New scope to set. Omit to read current." } }, required: ["server_name"] }
|
|
47705
|
+
},
|
|
47706
|
+
{
|
|
47707
|
+
name: "mesh_mcp_schema",
|
|
47708
|
+
description: "Inspect tool schemas for a deployed MCP server.",
|
|
47709
|
+
inputSchema: { type: "object", properties: { server_name: { type: "string" }, tool_name: { type: "string", description: "Specific tool (omit for all)" } }, required: ["server_name"] }
|
|
47710
|
+
},
|
|
47711
|
+
{
|
|
47712
|
+
name: "mesh_mcp_catalog",
|
|
47713
|
+
description: "List all deployed services in the mesh with status, scope, and tool count.",
|
|
47714
|
+
inputSchema: { type: "object", properties: {} }
|
|
47715
|
+
},
|
|
47716
|
+
{
|
|
47717
|
+
name: "mesh_skill_deploy",
|
|
47718
|
+
description: "Deploy a multi-file skill bundle from a zip or git repo.",
|
|
47719
|
+
inputSchema: { type: "object", properties: { file_id: { type: "string" }, git_url: { type: "string" }, git_branch: { type: "string" } } }
|
|
47720
|
+
},
|
|
47721
|
+
{
|
|
47722
|
+
name: "vault_set",
|
|
47723
|
+
description: "Store an encrypted credential in your vault. Reference in mesh_mcp_deploy with $vault:<key>.",
|
|
47724
|
+
inputSchema: { type: "object", properties: { key: { type: "string" }, value: { type: "string", description: "Secret value or local file path (for type=file)" }, type: { type: "string", enum: ["env", "file"] }, mount_path: { type: "string" }, description: { type: "string" } }, required: ["key", "value"] }
|
|
47725
|
+
},
|
|
47726
|
+
{
|
|
47727
|
+
name: "vault_list",
|
|
47728
|
+
description: "List your vault entries (keys and metadata only, no secret values).",
|
|
47729
|
+
inputSchema: { type: "object", properties: {} }
|
|
47730
|
+
},
|
|
47731
|
+
{
|
|
47732
|
+
name: "vault_delete",
|
|
47733
|
+
description: "Remove a credential from your vault.",
|
|
47734
|
+
inputSchema: { type: "object", properties: { key: { type: "string" } }, required: ["key"] }
|
|
47662
47735
|
}
|
|
47663
47736
|
];
|
|
47664
47737
|
|
|
@@ -47747,6 +47820,10 @@ class BrokerClient {
|
|
|
47747
47820
|
peerFileResponseResolvers = new Map;
|
|
47748
47821
|
peerDirResponseResolvers = new Map;
|
|
47749
47822
|
sharedDirs = [process.cwd()];
|
|
47823
|
+
_serviceCatalog = [];
|
|
47824
|
+
get serviceCatalog() {
|
|
47825
|
+
return this._serviceCatalog;
|
|
47826
|
+
}
|
|
47750
47827
|
closed = false;
|
|
47751
47828
|
reconnectAttempt = 0;
|
|
47752
47829
|
helloTimer = null;
|
|
@@ -47810,6 +47887,7 @@ class BrokerClient {
|
|
|
47810
47887
|
sessionId: `${process.pid}-${Date.now()}`,
|
|
47811
47888
|
pid: process.pid,
|
|
47812
47889
|
cwd: process.cwd(),
|
|
47890
|
+
hostname: __require("os").hostname(),
|
|
47813
47891
|
peerType: "ai",
|
|
47814
47892
|
channel: "claude-code",
|
|
47815
47893
|
model: process.env.CLAUDE_MODEL || undefined,
|
|
@@ -47841,6 +47919,21 @@ class BrokerClient {
|
|
|
47841
47919
|
this.reconnectAttempt = 0;
|
|
47842
47920
|
this.flushOutbound();
|
|
47843
47921
|
this.startStatsReporting();
|
|
47922
|
+
if (msg.restored) {
|
|
47923
|
+
const groups = msg.restoredGroups ? msg.restoredGroups.map((g) => g.role ? `@${g.name}:${g.role}` : `@${g.name}`).join(", ") : "none";
|
|
47924
|
+
process.stderr.write(`[claudemesh] session restored — last seen ${msg.lastSeenAt ?? "unknown"}, groups: ${groups}
|
|
47925
|
+
`);
|
|
47926
|
+
if (msg.restoredStats) {
|
|
47927
|
+
const rs = msg.restoredStats;
|
|
47928
|
+
this._statsCounters.messagesIn = rs.messagesIn ?? 0;
|
|
47929
|
+
this._statsCounters.messagesOut = rs.messagesOut ?? 0;
|
|
47930
|
+
this._statsCounters.toolCalls = rs.toolCalls ?? 0;
|
|
47931
|
+
this._statsCounters.errors = rs.errors ?? 0;
|
|
47932
|
+
}
|
|
47933
|
+
}
|
|
47934
|
+
if (msg.services) {
|
|
47935
|
+
this._serviceCatalog = msg.services;
|
|
47936
|
+
}
|
|
47844
47937
|
resolve();
|
|
47845
47938
|
return;
|
|
47846
47939
|
}
|
|
@@ -48129,6 +48222,14 @@ class BrokerClient {
|
|
|
48129
48222
|
mcpListResolvers = new Map;
|
|
48130
48223
|
mcpCallResolvers = new Map;
|
|
48131
48224
|
mcpCallForwardHandler = null;
|
|
48225
|
+
vaultAckResolvers = new Map;
|
|
48226
|
+
vaultListResolvers = new Map;
|
|
48227
|
+
mcpDeployResolvers = new Map;
|
|
48228
|
+
mcpLogsResolvers = new Map;
|
|
48229
|
+
mcpSchemaServiceResolvers = new Map;
|
|
48230
|
+
mcpCatalogResolvers = new Map;
|
|
48231
|
+
mcpScopeResolvers = new Map;
|
|
48232
|
+
skillDeployResolvers = new Map;
|
|
48132
48233
|
async messageStatus(messageId) {
|
|
48133
48234
|
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
48134
48235
|
return null;
|
|
@@ -48427,7 +48528,7 @@ class BrokerClient {
|
|
|
48427
48528
|
this.stateChangeHandlers.add(handler);
|
|
48428
48529
|
return () => this.stateChangeHandlers.delete(handler);
|
|
48429
48530
|
}
|
|
48430
|
-
async mcpRegister(serverName, description, tools) {
|
|
48531
|
+
async mcpRegister(serverName, description, tools, persistent) {
|
|
48431
48532
|
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
48432
48533
|
return null;
|
|
48433
48534
|
return new Promise((resolve) => {
|
|
@@ -48436,7 +48537,7 @@ class BrokerClient {
|
|
|
48436
48537
|
if (this.mcpRegisterResolvers.delete(reqId))
|
|
48437
48538
|
resolve(null);
|
|
48438
48539
|
}, 5000) });
|
|
48439
|
-
this.ws.send(JSON.stringify({ type: "mcp_register", serverName, description, tools, _reqId: reqId }));
|
|
48540
|
+
this.ws.send(JSON.stringify({ type: "mcp_register", serverName, description, tools, ...persistent ? { persistent: true } : {}, _reqId: reqId }));
|
|
48440
48541
|
});
|
|
48441
48542
|
}
|
|
48442
48543
|
async mcpUnregister(serverName) {
|
|
@@ -48661,6 +48762,138 @@ class BrokerClient {
|
|
|
48661
48762
|
this.ws.send(JSON.stringify({ type: "peer_dir_request", targetPubkey, dirPath, ...pattern ? { pattern } : {}, _reqId: reqId }));
|
|
48662
48763
|
});
|
|
48663
48764
|
}
|
|
48765
|
+
async vaultSet(key, ciphertext, nonce, sealedKey, entryType, mountPath, description) {
|
|
48766
|
+
return new Promise((resolve) => {
|
|
48767
|
+
const reqId = `vset_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
48768
|
+
const timer = setTimeout(() => {
|
|
48769
|
+
this.vaultAckResolvers.delete(reqId);
|
|
48770
|
+
resolve(false);
|
|
48771
|
+
}, 1e4);
|
|
48772
|
+
this.vaultAckResolvers.set(reqId, { resolve, timer });
|
|
48773
|
+
this.sendRaw({ type: "vault_set", key, ciphertext, nonce, sealed_key: sealedKey, entry_type: entryType, mount_path: mountPath, description, _reqId: reqId });
|
|
48774
|
+
});
|
|
48775
|
+
}
|
|
48776
|
+
async vaultList() {
|
|
48777
|
+
return new Promise((resolve) => {
|
|
48778
|
+
const reqId = `vlist_${Date.now()}`;
|
|
48779
|
+
const timer = setTimeout(() => {
|
|
48780
|
+
this.vaultListResolvers.delete(reqId);
|
|
48781
|
+
resolve([]);
|
|
48782
|
+
}, 1e4);
|
|
48783
|
+
this.vaultListResolvers.set(reqId, { resolve, timer });
|
|
48784
|
+
this.sendRaw({ type: "vault_list", _reqId: reqId });
|
|
48785
|
+
});
|
|
48786
|
+
}
|
|
48787
|
+
async vaultDelete(key) {
|
|
48788
|
+
return new Promise((resolve) => {
|
|
48789
|
+
const reqId = `vdel_${Date.now()}`;
|
|
48790
|
+
const timer = setTimeout(() => {
|
|
48791
|
+
this.vaultAckResolvers.delete(reqId);
|
|
48792
|
+
resolve(false);
|
|
48793
|
+
}, 1e4);
|
|
48794
|
+
this.vaultAckResolvers.set(reqId, { resolve, timer });
|
|
48795
|
+
this.sendRaw({ type: "vault_delete", key, _reqId: reqId });
|
|
48796
|
+
});
|
|
48797
|
+
}
|
|
48798
|
+
async mcpDeploy(serverName, source, config2, scope) {
|
|
48799
|
+
return new Promise((resolve) => {
|
|
48800
|
+
const reqId = `deploy_${Date.now()}`;
|
|
48801
|
+
const timer = setTimeout(() => {
|
|
48802
|
+
this.mcpDeployResolvers.delete(reqId);
|
|
48803
|
+
resolve({ status: "timeout" });
|
|
48804
|
+
}, 60000);
|
|
48805
|
+
this.mcpDeployResolvers.set(reqId, { resolve, timer });
|
|
48806
|
+
this.sendRaw({ type: "mcp_deploy", server_name: serverName, source, config: config2, scope, _reqId: reqId });
|
|
48807
|
+
});
|
|
48808
|
+
}
|
|
48809
|
+
async mcpUndeploy(serverName) {
|
|
48810
|
+
return new Promise((resolve) => {
|
|
48811
|
+
const reqId = `undeploy_${Date.now()}`;
|
|
48812
|
+
const timer = setTimeout(() => {
|
|
48813
|
+
this.mcpDeployResolvers.delete(reqId);
|
|
48814
|
+
resolve(false);
|
|
48815
|
+
}, 1e4);
|
|
48816
|
+
this.mcpDeployResolvers.set(reqId, { resolve: (r) => resolve(r.status === "stopped"), timer });
|
|
48817
|
+
this.sendRaw({ type: "mcp_undeploy", server_name: serverName, _reqId: reqId });
|
|
48818
|
+
});
|
|
48819
|
+
}
|
|
48820
|
+
async mcpUpdate(serverName) {
|
|
48821
|
+
return new Promise((resolve) => {
|
|
48822
|
+
const reqId = `update_${Date.now()}`;
|
|
48823
|
+
const timer = setTimeout(() => {
|
|
48824
|
+
this.mcpDeployResolvers.delete(reqId);
|
|
48825
|
+
resolve({ status: "timeout" });
|
|
48826
|
+
}, 60000);
|
|
48827
|
+
this.mcpDeployResolvers.set(reqId, { resolve, timer });
|
|
48828
|
+
this.sendRaw({ type: "mcp_update", server_name: serverName, _reqId: reqId });
|
|
48829
|
+
});
|
|
48830
|
+
}
|
|
48831
|
+
async mcpLogs(serverName, lines) {
|
|
48832
|
+
return new Promise((resolve) => {
|
|
48833
|
+
const reqId = `logs_${Date.now()}`;
|
|
48834
|
+
const timer = setTimeout(() => {
|
|
48835
|
+
this.mcpLogsResolvers.delete(reqId);
|
|
48836
|
+
resolve([]);
|
|
48837
|
+
}, 1e4);
|
|
48838
|
+
this.mcpLogsResolvers.set(reqId, { resolve, timer });
|
|
48839
|
+
this.sendRaw({ type: "mcp_logs", server_name: serverName, lines, _reqId: reqId });
|
|
48840
|
+
});
|
|
48841
|
+
}
|
|
48842
|
+
async mcpScope(serverName, scope) {
|
|
48843
|
+
return new Promise((resolve) => {
|
|
48844
|
+
const reqId = `scope_${Date.now()}`;
|
|
48845
|
+
const timer = setTimeout(() => {
|
|
48846
|
+
this.mcpScopeResolvers.delete(reqId);
|
|
48847
|
+
resolve({ scope: { type: "peer" }, deployed_by: "unknown" });
|
|
48848
|
+
}, 1e4);
|
|
48849
|
+
this.mcpScopeResolvers.set(reqId, { resolve, timer });
|
|
48850
|
+
this.sendRaw({ type: "mcp_scope", server_name: serverName, scope, _reqId: reqId });
|
|
48851
|
+
});
|
|
48852
|
+
}
|
|
48853
|
+
async mcpServiceSchema(serverName, toolName) {
|
|
48854
|
+
return new Promise((resolve) => {
|
|
48855
|
+
const reqId = `schema_${Date.now()}`;
|
|
48856
|
+
const timer = setTimeout(() => {
|
|
48857
|
+
this.mcpSchemaServiceResolvers.delete(reqId);
|
|
48858
|
+
resolve([]);
|
|
48859
|
+
}, 1e4);
|
|
48860
|
+
this.mcpSchemaServiceResolvers.set(reqId, { resolve, timer });
|
|
48861
|
+
this.sendRaw({ type: "mcp_schema", server_name: serverName, tool_name: toolName, _reqId: reqId });
|
|
48862
|
+
});
|
|
48863
|
+
}
|
|
48864
|
+
async mcpCatalog() {
|
|
48865
|
+
return new Promise((resolve) => {
|
|
48866
|
+
const reqId = `catalog_${Date.now()}`;
|
|
48867
|
+
const timer = setTimeout(() => {
|
|
48868
|
+
this.mcpCatalogResolvers.delete(reqId);
|
|
48869
|
+
resolve([]);
|
|
48870
|
+
}, 1e4);
|
|
48871
|
+
this.mcpCatalogResolvers.set(reqId, { resolve, timer });
|
|
48872
|
+
this.sendRaw({ type: "mcp_catalog", _reqId: reqId });
|
|
48873
|
+
});
|
|
48874
|
+
}
|
|
48875
|
+
async skillDeploy(source) {
|
|
48876
|
+
return new Promise((resolve) => {
|
|
48877
|
+
const reqId = `skilldeploy_${Date.now()}`;
|
|
48878
|
+
const timer = setTimeout(() => {
|
|
48879
|
+
this.skillDeployResolvers.delete(reqId);
|
|
48880
|
+
resolve({ name: "unknown", files: [] });
|
|
48881
|
+
}, 30000);
|
|
48882
|
+
this.skillDeployResolvers.set(reqId, { resolve, timer });
|
|
48883
|
+
this.sendRaw({ type: "skill_deploy", source, _reqId: reqId });
|
|
48884
|
+
});
|
|
48885
|
+
}
|
|
48886
|
+
async getServiceTools(serviceName) {
|
|
48887
|
+
const cached2 = this._serviceCatalog.find((s) => s.name === serviceName);
|
|
48888
|
+
if (cached2?.tools?.length)
|
|
48889
|
+
return cached2.tools;
|
|
48890
|
+
return this.mcpServiceSchema(serviceName);
|
|
48891
|
+
}
|
|
48892
|
+
sendRaw(payload) {
|
|
48893
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
48894
|
+
return;
|
|
48895
|
+
this.ws.send(JSON.stringify(payload));
|
|
48896
|
+
}
|
|
48664
48897
|
close() {
|
|
48665
48898
|
this.closed = true;
|
|
48666
48899
|
this.stopStatsReporting();
|
|
@@ -49162,6 +49395,78 @@ class BrokerClient {
|
|
|
49162
49395
|
this.resolveFromMap(this.webhookListResolvers, msgReqId, webhooks);
|
|
49163
49396
|
return;
|
|
49164
49397
|
}
|
|
49398
|
+
if (msg.type === "vault_ack") {
|
|
49399
|
+
const reqId = msg._reqId;
|
|
49400
|
+
if (reqId && this.vaultAckResolvers.has(reqId)) {
|
|
49401
|
+
const r = this.vaultAckResolvers.get(reqId);
|
|
49402
|
+
clearTimeout(r.timer);
|
|
49403
|
+
this.vaultAckResolvers.delete(reqId);
|
|
49404
|
+
r.resolve(msg.action !== "not_found");
|
|
49405
|
+
}
|
|
49406
|
+
}
|
|
49407
|
+
if (msg.type === "vault_list_result") {
|
|
49408
|
+
const reqId = msg._reqId;
|
|
49409
|
+
if (reqId && this.vaultListResolvers.has(reqId)) {
|
|
49410
|
+
const r = this.vaultListResolvers.get(reqId);
|
|
49411
|
+
clearTimeout(r.timer);
|
|
49412
|
+
this.vaultListResolvers.delete(reqId);
|
|
49413
|
+
r.resolve(msg.entries ?? []);
|
|
49414
|
+
}
|
|
49415
|
+
}
|
|
49416
|
+
if (msg.type === "mcp_deploy_status") {
|
|
49417
|
+
const reqId = msg._reqId;
|
|
49418
|
+
if (reqId && this.mcpDeployResolvers.has(reqId)) {
|
|
49419
|
+
const r = this.mcpDeployResolvers.get(reqId);
|
|
49420
|
+
clearTimeout(r.timer);
|
|
49421
|
+
this.mcpDeployResolvers.delete(reqId);
|
|
49422
|
+
r.resolve({ status: msg.status, tools: msg.tools, error: msg.error });
|
|
49423
|
+
}
|
|
49424
|
+
}
|
|
49425
|
+
if (msg.type === "mcp_logs_result") {
|
|
49426
|
+
const reqId = msg._reqId;
|
|
49427
|
+
if (reqId && this.mcpLogsResolvers.has(reqId)) {
|
|
49428
|
+
const r = this.mcpLogsResolvers.get(reqId);
|
|
49429
|
+
clearTimeout(r.timer);
|
|
49430
|
+
this.mcpLogsResolvers.delete(reqId);
|
|
49431
|
+
r.resolve(msg.lines ?? []);
|
|
49432
|
+
}
|
|
49433
|
+
}
|
|
49434
|
+
if (msg.type === "mcp_schema_result") {
|
|
49435
|
+
const reqId = msg._reqId;
|
|
49436
|
+
if (reqId && this.mcpSchemaServiceResolvers.has(reqId)) {
|
|
49437
|
+
const r = this.mcpSchemaServiceResolvers.get(reqId);
|
|
49438
|
+
clearTimeout(r.timer);
|
|
49439
|
+
this.mcpSchemaServiceResolvers.delete(reqId);
|
|
49440
|
+
r.resolve(msg.tools ?? []);
|
|
49441
|
+
}
|
|
49442
|
+
}
|
|
49443
|
+
if (msg.type === "mcp_catalog_result") {
|
|
49444
|
+
const reqId = msg._reqId;
|
|
49445
|
+
if (reqId && this.mcpCatalogResolvers.has(reqId)) {
|
|
49446
|
+
const r = this.mcpCatalogResolvers.get(reqId);
|
|
49447
|
+
clearTimeout(r.timer);
|
|
49448
|
+
this.mcpCatalogResolvers.delete(reqId);
|
|
49449
|
+
r.resolve(msg.services ?? []);
|
|
49450
|
+
}
|
|
49451
|
+
}
|
|
49452
|
+
if (msg.type === "mcp_scope_result") {
|
|
49453
|
+
const reqId = msg._reqId;
|
|
49454
|
+
if (reqId && this.mcpScopeResolvers.has(reqId)) {
|
|
49455
|
+
const r = this.mcpScopeResolvers.get(reqId);
|
|
49456
|
+
clearTimeout(r.timer);
|
|
49457
|
+
this.mcpScopeResolvers.delete(reqId);
|
|
49458
|
+
r.resolve({ scope: msg.scope, deployed_by: msg.deployed_by });
|
|
49459
|
+
}
|
|
49460
|
+
}
|
|
49461
|
+
if (msg.type === "skill_deploy_ack") {
|
|
49462
|
+
const reqId = msg._reqId;
|
|
49463
|
+
if (reqId && this.skillDeployResolvers.has(reqId)) {
|
|
49464
|
+
const r = this.skillDeployResolvers.get(reqId);
|
|
49465
|
+
clearTimeout(r.timer);
|
|
49466
|
+
this.skillDeployResolvers.delete(reqId);
|
|
49467
|
+
r.resolve({ name: msg.name, files: msg.files ?? [] });
|
|
49468
|
+
}
|
|
49469
|
+
}
|
|
49165
49470
|
if (msg.type === "error") {
|
|
49166
49471
|
this.debug(`broker error: ${msg.code} ${msg.message}`);
|
|
49167
49472
|
const id = msg.id ? String(msg.id) : null;
|
|
@@ -49215,7 +49520,15 @@ class BrokerClient {
|
|
|
49215
49520
|
[this.peerFileResponseResolvers, { error: "broker error" }],
|
|
49216
49521
|
[this.peerDirResponseResolvers, { error: "broker error" }],
|
|
49217
49522
|
[this.webhookAckResolvers, null],
|
|
49218
|
-
[this.webhookListResolvers, []]
|
|
49523
|
+
[this.webhookListResolvers, []],
|
|
49524
|
+
[this.vaultAckResolvers, false],
|
|
49525
|
+
[this.vaultListResolvers, []],
|
|
49526
|
+
[this.mcpDeployResolvers, { status: "error" }],
|
|
49527
|
+
[this.mcpLogsResolvers, []],
|
|
49528
|
+
[this.mcpSchemaServiceResolvers, []],
|
|
49529
|
+
[this.mcpCatalogResolvers, []],
|
|
49530
|
+
[this.mcpScopeResolvers, { scope: { type: "peer" }, deployed_by: "unknown" }],
|
|
49531
|
+
[this.skillDeployResolvers, { name: "unknown", files: [] }]
|
|
49219
49532
|
];
|
|
49220
49533
|
for (const [map2, defaultVal] of allMaps) {
|
|
49221
49534
|
const first = map2.entries().next().value;
|
|
@@ -49312,6 +49625,25 @@ function stopAll() {
|
|
|
49312
49625
|
}
|
|
49313
49626
|
|
|
49314
49627
|
// src/mcp/server.ts
|
|
49628
|
+
function relativeTime(isoStr) {
|
|
49629
|
+
const then = new Date(isoStr).getTime();
|
|
49630
|
+
if (isNaN(then))
|
|
49631
|
+
return "unknown";
|
|
49632
|
+
const diffMs = Date.now() - then;
|
|
49633
|
+
if (diffMs < 0)
|
|
49634
|
+
return "just now";
|
|
49635
|
+
const seconds = Math.floor(diffMs / 1000);
|
|
49636
|
+
if (seconds < 60)
|
|
49637
|
+
return `${seconds}s ago`;
|
|
49638
|
+
const minutes = Math.floor(seconds / 60);
|
|
49639
|
+
if (minutes < 60)
|
|
49640
|
+
return `${minutes}m ago`;
|
|
49641
|
+
const hours = Math.floor(minutes / 60);
|
|
49642
|
+
if (hours < 24)
|
|
49643
|
+
return `${hours}h ago`;
|
|
49644
|
+
const days = Math.floor(hours / 24);
|
|
49645
|
+
return `${days} day${days !== 1 ? "s" : ""} ago`;
|
|
49646
|
+
}
|
|
49315
49647
|
function text(msg, isError = false) {
|
|
49316
49648
|
return {
|
|
49317
49649
|
content: [{ type: "text", text: msg }],
|
|
@@ -49392,6 +49724,10 @@ function formatPush(p, meshSlug) {
|
|
|
49392
49724
|
${body}`;
|
|
49393
49725
|
}
|
|
49394
49726
|
async function startMcpServer() {
|
|
49727
|
+
const serviceIdx = process.argv.indexOf("--service");
|
|
49728
|
+
if (serviceIdx !== -1 && process.argv[serviceIdx + 1]) {
|
|
49729
|
+
return startServiceProxy(process.argv[serviceIdx + 1]);
|
|
49730
|
+
}
|
|
49395
49731
|
const config2 = loadConfig();
|
|
49396
49732
|
const myName = config2.displayName ?? "unnamed";
|
|
49397
49733
|
const myRole = config2.role ?? process.env.CLAUDEMESH_ROLE ?? null;
|
|
@@ -49484,9 +49820,14 @@ Shared key-value store scoped to the mesh. Use get_state/set_state for live coor
|
|
|
49484
49820
|
## Memory
|
|
49485
49821
|
Persistent knowledge that survives across sessions. Use remember(content, tags?) to store lessons, decisions, and incidents. Use recall(query) to search before asking peers. New peers should recall at session start to load institutional knowledge.
|
|
49486
49822
|
|
|
49487
|
-
##
|
|
49488
|
-
|
|
49489
|
-
|
|
49823
|
+
## File access — decision guide
|
|
49824
|
+
Three ways to access files. Pick the right one:
|
|
49825
|
+
|
|
49826
|
+
1. **Local peer (same machine, [local] tag):** Read files directly via filesystem using their \`cwd\` path from list_peers. No limit, instant. This is the default for local peers.
|
|
49827
|
+
2. **Remote peer (different machine, [remote] tag):** Use \`read_peer_file(peer, path)\` — relays through the mesh. **1 MB limit**, base64 encoded. Use \`list_peer_files\` to browse first.
|
|
49828
|
+
3. **Persistent sharing (any peer):** Use \`share_file(path)\` — uploads to mesh storage (MinIO). **No size limit**. All peers can download anytime via \`get_file\`. Use for files that need to persist or be shared with multiple peers.
|
|
49829
|
+
|
|
49830
|
+
**Rule of thumb:** local peer → filesystem. Remote peer, small file → read_peer_file. Large file or needs to persist → share_file.
|
|
49490
49831
|
|
|
49491
49832
|
## Vectors
|
|
49492
49833
|
Store and search semantic embeddings. Use vector_store to index content, vector_search to find similar content.
|
|
@@ -49583,10 +49924,12 @@ No peers connected.`);
|
|
|
49583
49924
|
meta2.push(`model:${p.model}`);
|
|
49584
49925
|
const metaStr = meta2.length ? ` {${meta2.join(", ")}}` : "";
|
|
49585
49926
|
const cwdStr = p.cwd ? ` cwd:${p.cwd}` : "";
|
|
49927
|
+
const locality = p.hostname && p.hostname === __require("os").hostname() ? "local" : "remote";
|
|
49928
|
+
const localityTag = ` [${locality}]`;
|
|
49586
49929
|
const profileAvatar = p.profile?.avatar ? `${p.profile.avatar} ` : "";
|
|
49587
49930
|
const profileTitle = p.profile?.title ? ` (${p.profile.title})` : "";
|
|
49588
49931
|
const hiddenTag = p.visible === false ? " [hidden]" : "";
|
|
49589
|
-
return `- ${profileAvatar}**${p.displayName}**${profileTitle} [${p.status}]${hiddenTag}${groupsStr}${metaStr} (${p.pubkey.slice(0, 12)}…)${cwdStr}${summary}`;
|
|
49932
|
+
return `- ${profileAvatar}**${p.displayName}**${profileTitle} [${p.status}]${localityTag}${hiddenTag}${groupsStr}${metaStr} (${p.pubkey.slice(0, 12)}…)${cwdStr}${summary}`;
|
|
49590
49933
|
});
|
|
49591
49934
|
sections.push(`${header}
|
|
49592
49935
|
${peerLines.join(`
|
|
@@ -50423,16 +50766,17 @@ ${lines.join(`
|
|
|
50423
50766
|
`));
|
|
50424
50767
|
}
|
|
50425
50768
|
case "mesh_mcp_register": {
|
|
50426
|
-
const { server_name, description, tools: regTools } = args ?? {};
|
|
50769
|
+
const { server_name, description, tools: regTools, persistent: regPersistent } = args ?? {};
|
|
50427
50770
|
if (!server_name || !description || !regTools?.length)
|
|
50428
50771
|
return text("mesh_mcp_register: `server_name`, `description`, and `tools` required", true);
|
|
50429
50772
|
const client2 = allClients()[0];
|
|
50430
50773
|
if (!client2)
|
|
50431
50774
|
return text("mesh_mcp_register: not connected", true);
|
|
50432
|
-
const result = await client2.mcpRegister(server_name, description, regTools);
|
|
50775
|
+
const result = await client2.mcpRegister(server_name, description, regTools, regPersistent);
|
|
50433
50776
|
if (!result)
|
|
50434
50777
|
return text("mesh_mcp_register: broker did not acknowledge", true);
|
|
50435
|
-
|
|
50778
|
+
const persistLabel = regPersistent ? " (persistent — survives disconnect)" : "";
|
|
50779
|
+
return text(`Registered MCP server "${result.serverName}" with ${result.toolCount} tool(s)${persistLabel}. Other peers can now call its tools via mesh_tool_call.`);
|
|
50436
50780
|
}
|
|
50437
50781
|
case "mesh_mcp_list": {
|
|
50438
50782
|
const client2 = allClients()[0];
|
|
@@ -50444,7 +50788,8 @@ ${lines.join(`
|
|
|
50444
50788
|
const lines = servers.map((s) => {
|
|
50445
50789
|
const toolList = s.tools.map((t) => ` - **${t.name}**: ${t.description}`).join(`
|
|
50446
50790
|
`);
|
|
50447
|
-
|
|
50791
|
+
const status = s.online === false ? ` [OFFLINE${s.offlineSince ? ` since ${s.offlineSince}` : ""}]` : "";
|
|
50792
|
+
return `- **${s.name}** (hosted by ${s.hostedBy})${status}: ${s.description}
|
|
50448
50793
|
${toolList}`;
|
|
50449
50794
|
});
|
|
50450
50795
|
return text(`${servers.length} MCP server(s) in mesh:
|
|
@@ -50531,6 +50876,17 @@ ${lines.join(`
|
|
|
50531
50876
|
targetPubkey = match.pubkey;
|
|
50532
50877
|
}
|
|
50533
50878
|
}
|
|
50879
|
+
const resolvedPeer = peers.find((p) => p.pubkey === targetPubkey);
|
|
50880
|
+
const isLocal = resolvedPeer?.hostname && resolvedPeer.hostname === __require("os").hostname();
|
|
50881
|
+
let localHint = "";
|
|
50882
|
+
if (isLocal && resolvedPeer?.cwd) {
|
|
50883
|
+
const directPath = __require("path").resolve(resolvedPeer.cwd, filePath);
|
|
50884
|
+
localHint = `
|
|
50885
|
+
|
|
50886
|
+
> **Hint:** This peer is LOCAL (same machine). Next time, read directly: \`${directPath}\` — faster, no size limit.
|
|
50887
|
+
|
|
50888
|
+
`;
|
|
50889
|
+
}
|
|
50534
50890
|
const result = await client2.requestFile(targetPubkey, filePath);
|
|
50535
50891
|
if (result.error)
|
|
50536
50892
|
return text(`read_peer_file: ${result.error}`, true);
|
|
@@ -50538,7 +50894,7 @@ ${lines.join(`
|
|
|
50538
50894
|
return text("read_peer_file: empty response from peer", true);
|
|
50539
50895
|
try {
|
|
50540
50896
|
const decoded = Buffer.from(result.content, "base64").toString("utf-8");
|
|
50541
|
-
return text(decoded);
|
|
50897
|
+
return text(localHint + decoded);
|
|
50542
50898
|
} catch {
|
|
50543
50899
|
return text("read_peer_file: failed to decode file content (binary file?)", true);
|
|
50544
50900
|
}
|
|
@@ -50638,8 +50994,21 @@ ${lines.join(`
|
|
|
50638
50994
|
content2 = `[heartbeat] tick ${tick} | sim time: ${simTime} | speed: x${speed}`;
|
|
50639
50995
|
} else if (eventName === "peer_joined") {
|
|
50640
50996
|
content2 = `[system] Peer "${data.name ?? "unknown"}" joined the mesh`;
|
|
50997
|
+
} else if (eventName === "peer_returned") {
|
|
50998
|
+
const peerName = String(data.name ?? "unknown");
|
|
50999
|
+
const lastSeenAt = data.lastSeenAt ? relativeTime(String(data.lastSeenAt)) : "unknown";
|
|
51000
|
+
const groups = Array.isArray(data.groups) ? data.groups.map((g) => g.role ? `@${g.name}:${g.role}` : `@${g.name}`).join(", ") : "";
|
|
51001
|
+
const summary = data.summary ? ` Summary: "${data.summary}"` : "";
|
|
51002
|
+
content2 = `[system] Welcome back, "${peerName}"! Last seen ${lastSeenAt}.${groups ? ` Restored: ${groups}` : ""}${summary}`;
|
|
50641
51003
|
} else if (eventName === "peer_left") {
|
|
50642
51004
|
content2 = `[system] Peer "${data.name ?? "unknown"}" left the mesh`;
|
|
51005
|
+
} else if (eventName === "mcp_registered") {
|
|
51006
|
+
const tools = Array.isArray(data.tools) ? data.tools.join(", ") : "";
|
|
51007
|
+
content2 = `[system] New MCP server available: "${data.serverName}" (hosted by ${data.hostedBy}). Tools: ${tools}. Use mesh_tool_call to invoke.`;
|
|
51008
|
+
} else if (eventName === "mcp_unregistered") {
|
|
51009
|
+
content2 = `[system] MCP server "${data.serverName}" removed (was hosted by ${data.hostedBy})`;
|
|
51010
|
+
} else if (eventName === "mcp_restored") {
|
|
51011
|
+
content2 = `[system] MCP server "${data.serverName}" is back online (hosted by ${data.hostedBy})`;
|
|
50643
51012
|
} else {
|
|
50644
51013
|
content2 = `[system] ${eventName}: ${JSON.stringify(data)}`;
|
|
50645
51014
|
}
|
|
@@ -50761,6 +51130,120 @@ ${lines.join(`
|
|
|
50761
51130
|
process.on("SIGTERM", shutdown);
|
|
50762
51131
|
process.on("SIGINT", shutdown);
|
|
50763
51132
|
}
|
|
51133
|
+
async function startServiceProxy(serviceName) {
|
|
51134
|
+
const config2 = loadConfig();
|
|
51135
|
+
if (config2.meshes.length === 0) {
|
|
51136
|
+
process.stderr.write(`[mesh:${serviceName}] no meshes joined
|
|
51137
|
+
`);
|
|
51138
|
+
process.exit(1);
|
|
51139
|
+
}
|
|
51140
|
+
const mesh = config2.meshes[0];
|
|
51141
|
+
const client2 = new BrokerClient(mesh, {
|
|
51142
|
+
displayName: config2.displayName ?? `proxy:${serviceName}`
|
|
51143
|
+
});
|
|
51144
|
+
try {
|
|
51145
|
+
await client2.connect();
|
|
51146
|
+
} catch (e) {
|
|
51147
|
+
process.stderr.write(`[mesh:${serviceName}] broker connect failed: ${e instanceof Error ? e.message : String(e)}
|
|
51148
|
+
`);
|
|
51149
|
+
process.exit(1);
|
|
51150
|
+
}
|
|
51151
|
+
await new Promise((r) => setTimeout(r, 1500));
|
|
51152
|
+
let tools = [];
|
|
51153
|
+
try {
|
|
51154
|
+
const fetched = await client2.getServiceTools(serviceName);
|
|
51155
|
+
tools = fetched;
|
|
51156
|
+
} catch {
|
|
51157
|
+
const cached2 = client2.serviceCatalog.find((s) => s.name === serviceName);
|
|
51158
|
+
if (cached2) {
|
|
51159
|
+
tools = cached2.tools;
|
|
51160
|
+
}
|
|
51161
|
+
}
|
|
51162
|
+
if (tools.length === 0) {
|
|
51163
|
+
process.stderr.write(`[mesh:${serviceName}] no tools found — service may not be running
|
|
51164
|
+
`);
|
|
51165
|
+
}
|
|
51166
|
+
const server = new Server({ name: `mesh:${serviceName}`, version: "0.1.0" }, { capabilities: { tools: {} } });
|
|
51167
|
+
server.setRequestHandler(ListToolsRequestSchema, () => ({
|
|
51168
|
+
tools: tools.map((t) => ({
|
|
51169
|
+
name: t.name,
|
|
51170
|
+
description: `[mesh:${serviceName}] ${t.description}`,
|
|
51171
|
+
inputSchema: t.inputSchema
|
|
51172
|
+
}))
|
|
51173
|
+
}));
|
|
51174
|
+
server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
51175
|
+
const toolName = req.params.name;
|
|
51176
|
+
const args = req.params.arguments ?? {};
|
|
51177
|
+
if (client2.status !== "open") {
|
|
51178
|
+
let waited = 0;
|
|
51179
|
+
while (client2.status !== "open" && waited < 1e4) {
|
|
51180
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
51181
|
+
waited += 500;
|
|
51182
|
+
}
|
|
51183
|
+
if (client2.status !== "open") {
|
|
51184
|
+
return {
|
|
51185
|
+
content: [
|
|
51186
|
+
{
|
|
51187
|
+
type: "text",
|
|
51188
|
+
text: `Service temporarily unavailable — broker reconnecting. Retry in a few seconds.`
|
|
51189
|
+
}
|
|
51190
|
+
],
|
|
51191
|
+
isError: true
|
|
51192
|
+
};
|
|
51193
|
+
}
|
|
51194
|
+
}
|
|
51195
|
+
try {
|
|
51196
|
+
const result = await client2.mcpCall(serviceName, toolName, args);
|
|
51197
|
+
if (result.error) {
|
|
51198
|
+
return {
|
|
51199
|
+
content: [{ type: "text", text: `Error: ${result.error}` }],
|
|
51200
|
+
isError: true
|
|
51201
|
+
};
|
|
51202
|
+
}
|
|
51203
|
+
const resultText = typeof result.result === "string" ? result.result : JSON.stringify(result.result, null, 2);
|
|
51204
|
+
return {
|
|
51205
|
+
content: [{ type: "text", text: resultText }]
|
|
51206
|
+
};
|
|
51207
|
+
} catch (e) {
|
|
51208
|
+
return {
|
|
51209
|
+
content: [
|
|
51210
|
+
{
|
|
51211
|
+
type: "text",
|
|
51212
|
+
text: `Call failed: ${e instanceof Error ? e.message : String(e)}`
|
|
51213
|
+
}
|
|
51214
|
+
],
|
|
51215
|
+
isError: true
|
|
51216
|
+
};
|
|
51217
|
+
}
|
|
51218
|
+
});
|
|
51219
|
+
client2.onPush((push) => {
|
|
51220
|
+
if (push.event === "mcp_undeployed" && push.eventData?.name === serviceName) {
|
|
51221
|
+
process.stderr.write(`[mesh:${serviceName}] service undeployed — exiting
|
|
51222
|
+
`);
|
|
51223
|
+
client2.close();
|
|
51224
|
+
process.exit(0);
|
|
51225
|
+
}
|
|
51226
|
+
if (push.event === "mcp_updated" && push.eventData?.name === serviceName) {
|
|
51227
|
+
const newTools = push.eventData?.tools;
|
|
51228
|
+
if (Array.isArray(newTools)) {
|
|
51229
|
+
tools = newTools;
|
|
51230
|
+
server.notification({
|
|
51231
|
+
method: "notifications/tools/list_changed"
|
|
51232
|
+
}).catch(() => {});
|
|
51233
|
+
}
|
|
51234
|
+
}
|
|
51235
|
+
});
|
|
51236
|
+
const transport = new StdioServerTransport;
|
|
51237
|
+
await server.connect(transport);
|
|
51238
|
+
const keepalive = setInterval(() => {}, 1000);
|
|
51239
|
+
const shutdown = () => {
|
|
51240
|
+
clearInterval(keepalive);
|
|
51241
|
+
client2.close();
|
|
51242
|
+
process.exit(0);
|
|
51243
|
+
};
|
|
51244
|
+
process.on("SIGTERM", shutdown);
|
|
51245
|
+
process.on("SIGINT", shutdown);
|
|
51246
|
+
}
|
|
50764
51247
|
|
|
50765
51248
|
// src/commands/install.ts
|
|
50766
51249
|
init_config();
|
|
@@ -51457,8 +51940,8 @@ async function runHook(args) {
|
|
|
51457
51940
|
// src/commands/launch.ts
|
|
51458
51941
|
init_config();
|
|
51459
51942
|
import { spawn } from "node:child_process";
|
|
51460
|
-
import { mkdtempSync, writeFileSync as writeFileSync4, rmSync, readdirSync, statSync } from "node:fs";
|
|
51461
|
-
import { tmpdir, hostname as hostname2 } from "node:os";
|
|
51943
|
+
import { mkdtempSync, writeFileSync as writeFileSync4, rmSync, readdirSync, statSync, existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
|
|
51944
|
+
import { tmpdir, hostname as hostname2, homedir as homedir4 } from "node:os";
|
|
51462
51945
|
import { join as join4 } from "node:path";
|
|
51463
51946
|
import { createInterface } from "node:readline";
|
|
51464
51947
|
async function pickMesh(meshes) {
|
|
@@ -51657,6 +52140,44 @@ async function runLaunch(flags, rawArgs) {
|
|
|
51657
52140
|
rmSync(full, { recursive: true, force: true });
|
|
51658
52141
|
}
|
|
51659
52142
|
} catch {}
|
|
52143
|
+
try {
|
|
52144
|
+
const claudeConfigPath = join4(homedir4(), ".claude.json");
|
|
52145
|
+
if (existsSync3(claudeConfigPath)) {
|
|
52146
|
+
const claudeConfig = JSON.parse(readFileSync3(claudeConfigPath, "utf-8"));
|
|
52147
|
+
const mcpServers = claudeConfig.mcpServers ?? {};
|
|
52148
|
+
let cleaned = 0;
|
|
52149
|
+
for (const key of Object.keys(mcpServers)) {
|
|
52150
|
+
if (!key.startsWith("mesh:"))
|
|
52151
|
+
continue;
|
|
52152
|
+
const meta2 = mcpServers[key]?._meshSession;
|
|
52153
|
+
if (!meta2?.pid)
|
|
52154
|
+
continue;
|
|
52155
|
+
try {
|
|
52156
|
+
process.kill(meta2.pid, 0);
|
|
52157
|
+
} catch {
|
|
52158
|
+
delete mcpServers[key];
|
|
52159
|
+
cleaned++;
|
|
52160
|
+
}
|
|
52161
|
+
}
|
|
52162
|
+
if (cleaned > 0) {
|
|
52163
|
+
claudeConfig.mcpServers = mcpServers;
|
|
52164
|
+
writeFileSync4(claudeConfigPath, JSON.stringify(claudeConfig, null, 2) + `
|
|
52165
|
+
`, "utf-8");
|
|
52166
|
+
}
|
|
52167
|
+
}
|
|
52168
|
+
} catch {}
|
|
52169
|
+
let serviceCatalog = [];
|
|
52170
|
+
try {
|
|
52171
|
+
const tmpClient = new BrokerClient(mesh, { displayName });
|
|
52172
|
+
await tmpClient.connect();
|
|
52173
|
+
await new Promise((r) => setTimeout(r, 2000));
|
|
52174
|
+
serviceCatalog = tmpClient.serviceCatalog;
|
|
52175
|
+
tmpClient.close();
|
|
52176
|
+
} catch {
|
|
52177
|
+
if (!args.quiet) {
|
|
52178
|
+
console.log(" (Could not fetch service catalog — mesh services won't be natively available)");
|
|
52179
|
+
}
|
|
52180
|
+
}
|
|
51660
52181
|
const tmpDir = mkdtempSync(join4(tmpdir(), "claudemesh-"));
|
|
51661
52182
|
const sessionConfig = {
|
|
51662
52183
|
version: 1,
|
|
@@ -51674,6 +52195,50 @@ async function runLaunch(flags, rawArgs) {
|
|
|
51674
52195
|
await confirmPermissions();
|
|
51675
52196
|
}
|
|
51676
52197
|
}
|
|
52198
|
+
const meshMcpEntries = [];
|
|
52199
|
+
if (serviceCatalog.length > 0) {
|
|
52200
|
+
const claudeConfigPath = join4(homedir4(), ".claude.json");
|
|
52201
|
+
let claudeConfig = {};
|
|
52202
|
+
try {
|
|
52203
|
+
claudeConfig = JSON.parse(readFileSync3(claudeConfigPath, "utf-8"));
|
|
52204
|
+
} catch {
|
|
52205
|
+
claudeConfig = {};
|
|
52206
|
+
}
|
|
52207
|
+
const mcpServers = claudeConfig.mcpServers ?? {};
|
|
52208
|
+
const sessionTag = `${process.pid}`;
|
|
52209
|
+
for (const svc of serviceCatalog) {
|
|
52210
|
+
if (svc.status !== "running")
|
|
52211
|
+
continue;
|
|
52212
|
+
const entryKey = `mesh:${svc.name}:${sessionTag}`;
|
|
52213
|
+
const entry = {
|
|
52214
|
+
command: "claudemesh",
|
|
52215
|
+
args: ["mcp", "--service", svc.name],
|
|
52216
|
+
env: {
|
|
52217
|
+
CLAUDEMESH_CONFIG_DIR: tmpDir
|
|
52218
|
+
},
|
|
52219
|
+
_meshSession: {
|
|
52220
|
+
pid: process.pid,
|
|
52221
|
+
meshSlug: mesh.slug,
|
|
52222
|
+
serviceName: svc.name,
|
|
52223
|
+
createdAt: new Date().toISOString()
|
|
52224
|
+
}
|
|
52225
|
+
};
|
|
52226
|
+
mcpServers[entryKey] = entry;
|
|
52227
|
+
meshMcpEntries.push({ key: entryKey, entry });
|
|
52228
|
+
}
|
|
52229
|
+
claudeConfig.mcpServers = mcpServers;
|
|
52230
|
+
writeFileSync4(claudeConfigPath, JSON.stringify(claudeConfig, null, 2) + `
|
|
52231
|
+
`, "utf-8");
|
|
52232
|
+
if (!args.quiet && meshMcpEntries.length > 0) {
|
|
52233
|
+
console.log(` ${meshMcpEntries.length} mesh service(s) registered as native MCPs:`);
|
|
52234
|
+
for (const { key } of meshMcpEntries) {
|
|
52235
|
+
const svcName = key.split(":")[1];
|
|
52236
|
+
const svc = serviceCatalog.find((s) => s.name === svcName);
|
|
52237
|
+
console.log(` ${svcName} (${svc?.tools.length ?? 0} tools)`);
|
|
52238
|
+
}
|
|
52239
|
+
console.log("");
|
|
52240
|
+
}
|
|
52241
|
+
}
|
|
51677
52242
|
const filtered = [];
|
|
51678
52243
|
for (let i = 0;i < args.claudeArgs.length; i++) {
|
|
51679
52244
|
if (args.claudeArgs[i] === "--dangerously-load-development-channels" || args.claudeArgs[i] === "--dangerously-skip-permissions") {
|
|
@@ -51698,10 +52263,25 @@ async function runLaunch(flags, rawArgs) {
|
|
|
51698
52263
|
...process.env,
|
|
51699
52264
|
CLAUDEMESH_CONFIG_DIR: tmpDir,
|
|
51700
52265
|
CLAUDEMESH_DISPLAY_NAME: displayName,
|
|
52266
|
+
MCP_TIMEOUT: process.env.MCP_TIMEOUT ?? "30000",
|
|
52267
|
+
MAX_MCP_OUTPUT_TOKENS: process.env.MAX_MCP_OUTPUT_TOKENS ?? "50000",
|
|
51701
52268
|
...role ? { CLAUDEMESH_ROLE: role } : {}
|
|
51702
52269
|
}
|
|
51703
52270
|
});
|
|
51704
52271
|
const cleanup = () => {
|
|
52272
|
+
if (meshMcpEntries.length > 0) {
|
|
52273
|
+
try {
|
|
52274
|
+
const claudeConfigPath = join4(homedir4(), ".claude.json");
|
|
52275
|
+
const claudeConfig = JSON.parse(readFileSync3(claudeConfigPath, "utf-8"));
|
|
52276
|
+
const mcpServers = claudeConfig.mcpServers ?? {};
|
|
52277
|
+
for (const { key } of meshMcpEntries) {
|
|
52278
|
+
delete mcpServers[key];
|
|
52279
|
+
}
|
|
52280
|
+
claudeConfig.mcpServers = mcpServers;
|
|
52281
|
+
writeFileSync4(claudeConfigPath, JSON.stringify(claudeConfig, null, 2) + `
|
|
52282
|
+
`, "utf-8");
|
|
52283
|
+
} catch {}
|
|
52284
|
+
}
|
|
51705
52285
|
try {
|
|
51706
52286
|
rmSync(tmpDir, { recursive: true, force: true });
|
|
51707
52287
|
} catch {}
|
|
@@ -51734,12 +52314,12 @@ async function runLaunch(flags, rawArgs) {
|
|
|
51734
52314
|
}
|
|
51735
52315
|
|
|
51736
52316
|
// src/commands/status.ts
|
|
51737
|
-
import { statSync as statSync2, existsSync as
|
|
52317
|
+
import { statSync as statSync2, existsSync as existsSync4 } from "node:fs";
|
|
51738
52318
|
init_config();
|
|
51739
52319
|
// package.json
|
|
51740
52320
|
var package_default = {
|
|
51741
52321
|
name: "claudemesh-cli",
|
|
51742
|
-
version: "0.
|
|
52322
|
+
version: "0.8.0",
|
|
51743
52323
|
description: "Claude Code MCP client for claudemesh — peer mesh messaging between Claude sessions.",
|
|
51744
52324
|
keywords: [
|
|
51745
52325
|
"claude-code",
|
|
@@ -51840,7 +52420,7 @@ async function runStatus() {
|
|
|
51840
52420
|
console.log("─".repeat(60));
|
|
51841
52421
|
const configPath = getConfigPath();
|
|
51842
52422
|
let configPerms = "missing";
|
|
51843
|
-
if (
|
|
52423
|
+
if (existsSync4(configPath)) {
|
|
51844
52424
|
const st = statSync2(configPath);
|
|
51845
52425
|
const mode = (st.mode & 511).toString(8).padStart(4, "0");
|
|
51846
52426
|
configPerms = mode === "0600" ? `${mode} ✓` : `${mode} ⚠ (expected 0600)`;
|
|
@@ -51889,8 +52469,8 @@ async function runStatus() {
|
|
|
51889
52469
|
|
|
51890
52470
|
// src/commands/doctor.ts
|
|
51891
52471
|
init_config();
|
|
51892
|
-
import { existsSync as
|
|
51893
|
-
import { homedir as
|
|
52472
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4, statSync as statSync3 } from "node:fs";
|
|
52473
|
+
import { homedir as homedir5, platform as platform2 } from "node:os";
|
|
51894
52474
|
import { join as join5 } from "node:path";
|
|
51895
52475
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
51896
52476
|
function checkNode() {
|
|
@@ -51915,8 +52495,8 @@ function checkClaudeOnPath() {
|
|
|
51915
52495
|
};
|
|
51916
52496
|
}
|
|
51917
52497
|
function checkMcpRegistered() {
|
|
51918
|
-
const claudeConfig = join5(
|
|
51919
|
-
if (!
|
|
52498
|
+
const claudeConfig = join5(homedir5(), ".claude.json");
|
|
52499
|
+
if (!existsSync5(claudeConfig)) {
|
|
51920
52500
|
return {
|
|
51921
52501
|
name: "claudemesh MCP registered in ~/.claude.json",
|
|
51922
52502
|
pass: false,
|
|
@@ -51924,7 +52504,7 @@ function checkMcpRegistered() {
|
|
|
51924
52504
|
};
|
|
51925
52505
|
}
|
|
51926
52506
|
try {
|
|
51927
|
-
const cfg = JSON.parse(
|
|
52507
|
+
const cfg = JSON.parse(readFileSync4(claudeConfig, "utf-8"));
|
|
51928
52508
|
const registered = Boolean(cfg.mcpServers?.["claudemesh"]);
|
|
51929
52509
|
return {
|
|
51930
52510
|
name: "claudemesh MCP registered in ~/.claude.json",
|
|
@@ -51941,8 +52521,8 @@ function checkMcpRegistered() {
|
|
|
51941
52521
|
}
|
|
51942
52522
|
}
|
|
51943
52523
|
function checkHooksRegistered() {
|
|
51944
|
-
const settings = join5(
|
|
51945
|
-
if (!
|
|
52524
|
+
const settings = join5(homedir5(), ".claude", "settings.json");
|
|
52525
|
+
if (!existsSync5(settings)) {
|
|
51946
52526
|
return {
|
|
51947
52527
|
name: "Status hooks registered in ~/.claude/settings.json",
|
|
51948
52528
|
pass: false,
|
|
@@ -51950,7 +52530,7 @@ function checkHooksRegistered() {
|
|
|
51950
52530
|
};
|
|
51951
52531
|
}
|
|
51952
52532
|
try {
|
|
51953
|
-
const raw =
|
|
52533
|
+
const raw = readFileSync4(settings, "utf-8");
|
|
51954
52534
|
const has = raw.includes("claudemesh hook ");
|
|
51955
52535
|
return {
|
|
51956
52536
|
name: "Status hooks registered in ~/.claude/settings.json",
|
|
@@ -51967,7 +52547,7 @@ function checkHooksRegistered() {
|
|
|
51967
52547
|
}
|
|
51968
52548
|
function checkConfigFile() {
|
|
51969
52549
|
const path = getConfigPath();
|
|
51970
|
-
if (!
|
|
52550
|
+
if (!existsSync5(path)) {
|
|
51971
52551
|
return {
|
|
51972
52552
|
name: "~/.claudemesh/config.json exists and parses",
|
|
51973
52553
|
pass: true,
|
|
@@ -52071,15 +52651,15 @@ async function runDoctor() {
|
|
|
52071
52651
|
|
|
52072
52652
|
// src/commands/welcome.ts
|
|
52073
52653
|
init_config();
|
|
52074
|
-
import { existsSync as
|
|
52075
|
-
import { homedir as
|
|
52654
|
+
import { existsSync as existsSync6, readFileSync as readFileSync5 } from "node:fs";
|
|
52655
|
+
import { homedir as homedir6 } from "node:os";
|
|
52076
52656
|
import { join as join6 } from "node:path";
|
|
52077
52657
|
function detectState() {
|
|
52078
|
-
const claudeConfig = join6(
|
|
52658
|
+
const claudeConfig = join6(homedir6(), ".claude.json");
|
|
52079
52659
|
let mcpRegistered = false;
|
|
52080
|
-
if (
|
|
52660
|
+
if (existsSync6(claudeConfig)) {
|
|
52081
52661
|
try {
|
|
52082
|
-
const cfg = JSON.parse(
|
|
52662
|
+
const cfg = JSON.parse(readFileSync5(claudeConfig, "utf-8"));
|
|
52083
52663
|
mcpRegistered = Boolean(cfg.mcpServers?.["claudemesh"]);
|
|
52084
52664
|
} catch {}
|
|
52085
52665
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claudemesh-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Claude Code MCP client for claudemesh — peer mesh messaging between Claude sessions.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude-code",
|
|
@@ -48,10 +48,10 @@
|
|
|
48
48
|
"prettier": "3.6.2",
|
|
49
49
|
"typescript": "5.9.3",
|
|
50
50
|
"vitest": "4.0.14",
|
|
51
|
-
"@turbostarter/
|
|
51
|
+
"@turbostarter/eslint-config": "0.1.0",
|
|
52
52
|
"@turbostarter/prettier-config": "0.1.0",
|
|
53
|
-
"@turbostarter/
|
|
54
|
-
"@turbostarter/
|
|
53
|
+
"@turbostarter/tsconfig": "0.1.0",
|
|
54
|
+
"@turbostarter/vitest-config": "0.1.0"
|
|
55
55
|
},
|
|
56
56
|
"scripts": {
|
|
57
57
|
"build": "bun build src/index.ts --target=node --outfile dist/index.js --banner \"#!/usr/bin/env node\" && chmod +x dist/index.js",
|