claudemesh-cli 1.31.6 → 1.32.1
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/entrypoints/cli.js +605 -415
- package/dist/entrypoints/cli.js.map +14 -14
- package/dist/entrypoints/mcp.js +2 -2
- package/dist/entrypoints/mcp.js.map +1 -1
- package/package.json +1 -1
package/dist/entrypoints/cli.js
CHANGED
|
@@ -104,7 +104,7 @@ __export(exports_urls, {
|
|
|
104
104
|
VERSION: () => VERSION,
|
|
105
105
|
URLS: () => URLS
|
|
106
106
|
});
|
|
107
|
-
var URLS, VERSION = "1.
|
|
107
|
+
var URLS, VERSION = "1.32.1", env;
|
|
108
108
|
var init_urls = __esm(() => {
|
|
109
109
|
URLS = {
|
|
110
110
|
BROKER: process.env.CLAUDEMESH_BROKER_URL ?? "wss://ic.claudemesh.com/ws",
|
|
@@ -3966,6 +3966,252 @@ var init_lifecycle = __esm(() => {
|
|
|
3966
3966
|
init_paths2();
|
|
3967
3967
|
});
|
|
3968
3968
|
|
|
3969
|
+
// src/ui/warnings.ts
|
|
3970
|
+
function warnDaemonState(res, opts = {}) {
|
|
3971
|
+
if (alreadyWarned)
|
|
3972
|
+
return false;
|
|
3973
|
+
if (opts.quiet || opts.json)
|
|
3974
|
+
return false;
|
|
3975
|
+
if (res.state === "up")
|
|
3976
|
+
return false;
|
|
3977
|
+
if (getDaemonPolicy().mode === "strict" && res.state !== "started")
|
|
3978
|
+
return false;
|
|
3979
|
+
alreadyWarned = true;
|
|
3980
|
+
const tag = (label) => `[claudemesh] ${label}`;
|
|
3981
|
+
const hint = (s) => dim(s);
|
|
3982
|
+
switch (res.state) {
|
|
3983
|
+
case "started":
|
|
3984
|
+
process.stderr.write(`${tag("info")} daemon restarted automatically ${hint(`(took ${res.durationMs}ms)`)}
|
|
3985
|
+
`);
|
|
3986
|
+
return true;
|
|
3987
|
+
case "down":
|
|
3988
|
+
process.stderr.write(`${tag("info")} daemon not running — using cold path ${hint("(slower; run `claudemesh daemon up` for warm path)")}
|
|
3989
|
+
`);
|
|
3990
|
+
return true;
|
|
3991
|
+
case "spawn-suppressed":
|
|
3992
|
+
process.stderr.write(`${tag("warn")} ${res.reason ?? "daemon failed to start recently"} — using cold path ${hint("(run `claudemesh doctor`)")}
|
|
3993
|
+
`);
|
|
3994
|
+
return true;
|
|
3995
|
+
case "spawn-failed":
|
|
3996
|
+
process.stderr.write(`${tag("warn")} daemon spawn failed${res.reason ? `: ${res.reason}` : ""} — using cold path ${hint("(check ~/.claudemesh/daemon/daemon.log)")}
|
|
3997
|
+
`);
|
|
3998
|
+
return true;
|
|
3999
|
+
case "service-not-ready":
|
|
4000
|
+
process.stderr.write(`${tag("warn")} ${res.reason ?? "service-managed daemon not responding"} — using cold path ${hint("(check ~/.claudemesh/daemon/daemon.log)")}
|
|
4001
|
+
`);
|
|
4002
|
+
return true;
|
|
4003
|
+
}
|
|
4004
|
+
return false;
|
|
4005
|
+
}
|
|
4006
|
+
var alreadyWarned = false;
|
|
4007
|
+
var init_warnings = __esm(() => {
|
|
4008
|
+
init_policy();
|
|
4009
|
+
init_styles();
|
|
4010
|
+
});
|
|
4011
|
+
|
|
4012
|
+
// src/services/bridge/daemon-route.ts
|
|
4013
|
+
var exports_daemon_route = {};
|
|
4014
|
+
__export(exports_daemon_route, {
|
|
4015
|
+
trySetStateViaDaemon: () => trySetStateViaDaemon,
|
|
4016
|
+
trySendViaDaemon: () => trySendViaDaemon,
|
|
4017
|
+
tryRememberViaDaemon: () => tryRememberViaDaemon,
|
|
4018
|
+
tryRecallViaDaemon: () => tryRecallViaDaemon,
|
|
4019
|
+
tryListStateViaDaemon: () => tryListStateViaDaemon,
|
|
4020
|
+
tryListSkillsViaDaemon: () => tryListSkillsViaDaemon,
|
|
4021
|
+
tryListPeersViaDaemon: () => tryListPeersViaDaemon,
|
|
4022
|
+
tryGetStateViaDaemon: () => tryGetStateViaDaemon,
|
|
4023
|
+
tryGetSkillViaDaemon: () => tryGetSkillViaDaemon,
|
|
4024
|
+
tryForgetViaDaemon: () => tryForgetViaDaemon
|
|
4025
|
+
});
|
|
4026
|
+
function meshQuery(mesh) {
|
|
4027
|
+
return mesh ? `?mesh=${encodeURIComponent(mesh)}` : "";
|
|
4028
|
+
}
|
|
4029
|
+
async function daemonReachable() {
|
|
4030
|
+
const policy2 = getDaemonPolicy();
|
|
4031
|
+
if (policy2.mode === "no-daemon")
|
|
4032
|
+
return false;
|
|
4033
|
+
const res = await ensureDaemonReady({ noAutoSpawn: false });
|
|
4034
|
+
warnDaemonState(res, {});
|
|
4035
|
+
return res.state === "up" || res.state === "started";
|
|
4036
|
+
}
|
|
4037
|
+
async function tryListPeersViaDaemon(mesh) {
|
|
4038
|
+
if (!await daemonReachable())
|
|
4039
|
+
return null;
|
|
4040
|
+
try {
|
|
4041
|
+
const res = await ipc({ path: `/v1/peers${meshQuery(mesh)}`, timeoutMs: 3000 });
|
|
4042
|
+
if (res.status !== 200)
|
|
4043
|
+
return null;
|
|
4044
|
+
return Array.isArray(res.body.peers) ? res.body.peers : [];
|
|
4045
|
+
} catch (err) {
|
|
4046
|
+
const msg = String(err);
|
|
4047
|
+
if (/ENOENT|ECONNREFUSED|ipc_timeout/.test(msg))
|
|
4048
|
+
return null;
|
|
4049
|
+
return null;
|
|
4050
|
+
}
|
|
4051
|
+
}
|
|
4052
|
+
async function tryListSkillsViaDaemon(mesh) {
|
|
4053
|
+
if (!await daemonReachable())
|
|
4054
|
+
return null;
|
|
4055
|
+
try {
|
|
4056
|
+
const res = await ipc({ path: `/v1/skills${meshQuery(mesh)}`, timeoutMs: 3000 });
|
|
4057
|
+
if (res.status !== 200)
|
|
4058
|
+
return null;
|
|
4059
|
+
return Array.isArray(res.body.skills) ? res.body.skills : [];
|
|
4060
|
+
} catch (err) {
|
|
4061
|
+
const msg = String(err);
|
|
4062
|
+
if (/ENOENT|ECONNREFUSED|ipc_timeout/.test(msg))
|
|
4063
|
+
return null;
|
|
4064
|
+
return null;
|
|
4065
|
+
}
|
|
4066
|
+
}
|
|
4067
|
+
async function tryGetSkillViaDaemon(name, mesh) {
|
|
4068
|
+
if (!await daemonReachable())
|
|
4069
|
+
return null;
|
|
4070
|
+
try {
|
|
4071
|
+
const res = await ipc({
|
|
4072
|
+
path: `/v1/skills/${encodeURIComponent(name)}${meshQuery(mesh)}`,
|
|
4073
|
+
timeoutMs: 3000
|
|
4074
|
+
});
|
|
4075
|
+
if (res.status === 404)
|
|
4076
|
+
return null;
|
|
4077
|
+
if (res.status !== 200)
|
|
4078
|
+
return null;
|
|
4079
|
+
return res.body.skill ?? null;
|
|
4080
|
+
} catch {
|
|
4081
|
+
return null;
|
|
4082
|
+
}
|
|
4083
|
+
}
|
|
4084
|
+
async function tryGetStateViaDaemon(key, mesh) {
|
|
4085
|
+
if (!await daemonReachable())
|
|
4086
|
+
return null;
|
|
4087
|
+
try {
|
|
4088
|
+
const path = `/v1/state?key=${encodeURIComponent(key)}${mesh ? `&mesh=${encodeURIComponent(mesh)}` : ""}`;
|
|
4089
|
+
const res = await ipc({ path, timeoutMs: 3000 });
|
|
4090
|
+
if (res.status === 404)
|
|
4091
|
+
return;
|
|
4092
|
+
if (res.status !== 200)
|
|
4093
|
+
return null;
|
|
4094
|
+
return res.body.state ?? undefined;
|
|
4095
|
+
} catch (err) {
|
|
4096
|
+
const msg = String(err);
|
|
4097
|
+
if (/ENOENT|ECONNREFUSED|ipc_timeout/.test(msg))
|
|
4098
|
+
return null;
|
|
4099
|
+
return null;
|
|
4100
|
+
}
|
|
4101
|
+
}
|
|
4102
|
+
async function tryListStateViaDaemon(mesh) {
|
|
4103
|
+
if (!await daemonReachable())
|
|
4104
|
+
return null;
|
|
4105
|
+
try {
|
|
4106
|
+
const res = await ipc({ path: `/v1/state${meshQuery(mesh)}`, timeoutMs: 3000 });
|
|
4107
|
+
if (res.status !== 200)
|
|
4108
|
+
return null;
|
|
4109
|
+
return Array.isArray(res.body.entries) ? res.body.entries : [];
|
|
4110
|
+
} catch (err) {
|
|
4111
|
+
const msg = String(err);
|
|
4112
|
+
if (/ENOENT|ECONNREFUSED|ipc_timeout/.test(msg))
|
|
4113
|
+
return null;
|
|
4114
|
+
return null;
|
|
4115
|
+
}
|
|
4116
|
+
}
|
|
4117
|
+
async function trySetStateViaDaemon(key, value, mesh) {
|
|
4118
|
+
if (!await daemonReachable())
|
|
4119
|
+
return false;
|
|
4120
|
+
try {
|
|
4121
|
+
const res = await ipc({
|
|
4122
|
+
method: "POST",
|
|
4123
|
+
path: "/v1/state",
|
|
4124
|
+
timeoutMs: 3000,
|
|
4125
|
+
body: { key, value, ...mesh ? { mesh } : {} }
|
|
4126
|
+
});
|
|
4127
|
+
return res.status === 200 && res.body.ok === true;
|
|
4128
|
+
} catch {
|
|
4129
|
+
return false;
|
|
4130
|
+
}
|
|
4131
|
+
}
|
|
4132
|
+
async function tryRememberViaDaemon(content, tags, mesh) {
|
|
4133
|
+
if (!await daemonReachable())
|
|
4134
|
+
return null;
|
|
4135
|
+
try {
|
|
4136
|
+
const res = await ipc({
|
|
4137
|
+
method: "POST",
|
|
4138
|
+
path: "/v1/memory",
|
|
4139
|
+
timeoutMs: 5000,
|
|
4140
|
+
body: { content, ...tags?.length ? { tags } : {}, ...mesh ? { mesh } : {} }
|
|
4141
|
+
});
|
|
4142
|
+
if (res.status !== 200 || !res.body.id)
|
|
4143
|
+
return null;
|
|
4144
|
+
return { id: res.body.id, mesh: res.body.mesh };
|
|
4145
|
+
} catch {
|
|
4146
|
+
return null;
|
|
4147
|
+
}
|
|
4148
|
+
}
|
|
4149
|
+
async function tryRecallViaDaemon(query, mesh) {
|
|
4150
|
+
if (!await daemonReachable())
|
|
4151
|
+
return null;
|
|
4152
|
+
try {
|
|
4153
|
+
const path = `/v1/memory?q=${encodeURIComponent(query)}${mesh ? `&mesh=${encodeURIComponent(mesh)}` : ""}`;
|
|
4154
|
+
const res = await ipc({ path, timeoutMs: 5000 });
|
|
4155
|
+
if (res.status !== 200)
|
|
4156
|
+
return null;
|
|
4157
|
+
return Array.isArray(res.body.matches) ? res.body.matches : [];
|
|
4158
|
+
} catch (err) {
|
|
4159
|
+
const msg = String(err);
|
|
4160
|
+
if (/ENOENT|ECONNREFUSED|ipc_timeout/.test(msg))
|
|
4161
|
+
return null;
|
|
4162
|
+
return null;
|
|
4163
|
+
}
|
|
4164
|
+
}
|
|
4165
|
+
async function tryForgetViaDaemon(id, mesh) {
|
|
4166
|
+
if (!await daemonReachable())
|
|
4167
|
+
return false;
|
|
4168
|
+
try {
|
|
4169
|
+
const path = `/v1/memory/${encodeURIComponent(id)}${meshQuery(mesh)}`;
|
|
4170
|
+
const res = await ipc({ method: "DELETE", path, timeoutMs: 3000 });
|
|
4171
|
+
return res.status === 200 && res.body.ok === true;
|
|
4172
|
+
} catch {
|
|
4173
|
+
return false;
|
|
4174
|
+
}
|
|
4175
|
+
}
|
|
4176
|
+
async function trySendViaDaemon(args) {
|
|
4177
|
+
if (!await daemonReachable())
|
|
4178
|
+
return null;
|
|
4179
|
+
try {
|
|
4180
|
+
const res = await ipc({
|
|
4181
|
+
method: "POST",
|
|
4182
|
+
path: "/v1/send",
|
|
4183
|
+
timeoutMs: 3000,
|
|
4184
|
+
body: {
|
|
4185
|
+
to: args.to,
|
|
4186
|
+
message: args.message,
|
|
4187
|
+
priority: args.priority,
|
|
4188
|
+
...args.idempotencyKey ? { client_message_id: args.idempotencyKey } : {},
|
|
4189
|
+
...args.expectedMesh ? { mesh: args.expectedMesh } : {}
|
|
4190
|
+
}
|
|
4191
|
+
});
|
|
4192
|
+
if (res.status === 202 || res.status === 200) {
|
|
4193
|
+
return {
|
|
4194
|
+
ok: true,
|
|
4195
|
+
messageId: res.body.broker_message_id ?? res.body.client_message_id ?? "",
|
|
4196
|
+
duplicate: res.body.duplicate,
|
|
4197
|
+
status: res.body.status
|
|
4198
|
+
};
|
|
4199
|
+
}
|
|
4200
|
+
return { ok: false, error: res.body.error ?? `daemon http ${res.status}` };
|
|
4201
|
+
} catch (err) {
|
|
4202
|
+
const msg = String(err);
|
|
4203
|
+
if (/ENOENT|ECONNREFUSED|ipc_timeout/.test(msg))
|
|
4204
|
+
return null;
|
|
4205
|
+
return { ok: false, error: msg };
|
|
4206
|
+
}
|
|
4207
|
+
}
|
|
4208
|
+
var init_daemon_route = __esm(() => {
|
|
4209
|
+
init_client3();
|
|
4210
|
+
init_lifecycle();
|
|
4211
|
+
init_policy();
|
|
4212
|
+
init_warnings();
|
|
4213
|
+
});
|
|
4214
|
+
|
|
3969
4215
|
// src/services/broker/session-hello-sig.ts
|
|
3970
4216
|
var exports_session_hello_sig = {};
|
|
3971
4217
|
__export(exports_session_hello_sig, {
|
|
@@ -4166,18 +4412,61 @@ async function runLaunchWizard(opts) {
|
|
|
4166
4412
|
exitFullScreen();
|
|
4167
4413
|
return { mesh, role, groups, messageMode, skipPermissions };
|
|
4168
4414
|
}
|
|
4169
|
-
function
|
|
4415
|
+
async function printBrokerWelcome(meshSlug) {
|
|
4170
4416
|
const useColor = !process.env.NO_COLOR && process.env.TERM !== "dumb" && process.stdout.isTTY;
|
|
4171
4417
|
const dim2 = (s) => useColor ? `\x1B[2m${s}\x1B[22m` : s;
|
|
4172
|
-
const
|
|
4173
|
-
const
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
|
|
4178
|
-
|
|
4179
|
-
|
|
4180
|
-
|
|
4418
|
+
const green3 = (s) => useColor ? `\x1B[32m${s}\x1B[22m` : s;
|
|
4419
|
+
const yellow2 = (s) => useColor ? `\x1B[33m${s}\x1B[22m` : s;
|
|
4420
|
+
let brokerState = "unknown";
|
|
4421
|
+
try {
|
|
4422
|
+
const { ipc: ipc2 } = await Promise.resolve().then(() => (init_client3(), exports_client));
|
|
4423
|
+
const res = await ipc2({
|
|
4424
|
+
path: "/v1/health",
|
|
4425
|
+
timeoutMs: 1500
|
|
4426
|
+
});
|
|
4427
|
+
if (res.status === 200 && res.body?.brokers) {
|
|
4428
|
+
brokerState = res.body.brokers[meshSlug] ?? "unknown";
|
|
4429
|
+
}
|
|
4430
|
+
} catch {}
|
|
4431
|
+
let peerCount = -1;
|
|
4432
|
+
try {
|
|
4433
|
+
const { tryListPeersViaDaemon: tryListPeersViaDaemon2 } = await Promise.resolve().then(() => (init_daemon_route(), exports_daemon_route));
|
|
4434
|
+
const peers = await tryListPeersViaDaemon2() ?? [];
|
|
4435
|
+
peerCount = peers.filter((p) => p.channel !== "claudemesh-daemon").length;
|
|
4436
|
+
} catch {}
|
|
4437
|
+
let unread = -1;
|
|
4438
|
+
try {
|
|
4439
|
+
const { ipc: ipc2 } = await Promise.resolve().then(() => (init_client3(), exports_client));
|
|
4440
|
+
const res = await ipc2({
|
|
4441
|
+
path: "/v1/inbox",
|
|
4442
|
+
timeoutMs: 1500
|
|
4443
|
+
});
|
|
4444
|
+
if (res.status === 200 && Array.isArray(res.body?.messages)) {
|
|
4445
|
+
unread = res.body.messages.length;
|
|
4446
|
+
}
|
|
4447
|
+
} catch {}
|
|
4448
|
+
const dot = brokerState === "open" ? green3("●") : yellow2("●");
|
|
4449
|
+
const parts = [];
|
|
4450
|
+
parts.push(`broker ${brokerState === "open" ? "connected" : brokerState}`);
|
|
4451
|
+
if (peerCount >= 0)
|
|
4452
|
+
parts.push(`${peerCount} peer${peerCount === 1 ? "" : "s"} online`);
|
|
4453
|
+
if (unread >= 0)
|
|
4454
|
+
parts.push(`${unread} unread`);
|
|
4455
|
+
console.log(`${dot} ${parts.join(dim2(" · "))}`);
|
|
4456
|
+
console.log("");
|
|
4457
|
+
}
|
|
4458
|
+
function printBanner(name, meshSlug, role, groups, messageMode) {
|
|
4459
|
+
const useColor = !process.env.NO_COLOR && process.env.TERM !== "dumb" && process.stdout.isTTY;
|
|
4460
|
+
const dim2 = (s) => useColor ? `\x1B[2m${s}\x1B[22m` : s;
|
|
4461
|
+
const bold2 = (s) => useColor ? `\x1B[1m${s}\x1B[22m` : s;
|
|
4462
|
+
const roleSuffix = role ? ` (${role})` : "";
|
|
4463
|
+
const groupTags = groups.length ? " [" + groups.map((g) => `@${g.name}${g.role ? `:${g.role}` : ""}`).join(", ") + "]" : "";
|
|
4464
|
+
const rule = "─".repeat(60);
|
|
4465
|
+
console.log(bold2(`claudemesh launch`) + dim2(` — as ${name}${roleSuffix} on ${meshSlug}${groupTags} [${messageMode}]`));
|
|
4466
|
+
console.log(rule);
|
|
4467
|
+
if (messageMode === "push") {
|
|
4468
|
+
console.log("Peer messages arrive as <channel> reminders in real-time.");
|
|
4469
|
+
} else if (messageMode === "inbox") {
|
|
4181
4470
|
console.log("Peer messages held in inbox. Use check_messages to read.");
|
|
4182
4471
|
} else {
|
|
4183
4472
|
console.log("Messages off. Use check_messages to poll manually.");
|
|
@@ -4456,6 +4745,7 @@ async function runLaunch(flags, rawArgs) {
|
|
|
4456
4745
|
} catch {}
|
|
4457
4746
|
if (!args.quiet) {
|
|
4458
4747
|
printBanner(displayName, mesh.slug, role, parsedGroups, messageMode);
|
|
4748
|
+
await printBrokerWelcome(mesh.slug);
|
|
4459
4749
|
}
|
|
4460
4750
|
const meshMcpEntries = [];
|
|
4461
4751
|
if (serviceCatalog.length > 0) {
|
|
@@ -7424,402 +7714,156 @@ function parseStaleMs(input) {
|
|
|
7424
7714
|
if (unit === "h")
|
|
7425
7715
|
return val * 3600000;
|
|
7426
7716
|
return null;
|
|
7427
|
-
}
|
|
7428
|
-
function buildPayload(kind, target, opts) {
|
|
7429
|
-
if (opts.all)
|
|
7430
|
-
return { type: kind, all: true };
|
|
7431
|
-
if (opts.stale) {
|
|
7432
|
-
const ms = parseStaleMs(opts.stale);
|
|
7433
|
-
if (!ms)
|
|
7434
|
-
return { error: `Invalid stale duration: "${opts.stale}". Use e.g. 30m, 1h, 300s.` };
|
|
7435
|
-
return { type: kind, stale: ms };
|
|
7436
|
-
}
|
|
7437
|
-
if (target)
|
|
7438
|
-
return { type: kind, target };
|
|
7439
|
-
return { error: `Usage: claudemesh ${kind} <peer> | --stale 30m | --all` };
|
|
7440
|
-
}
|
|
7441
|
-
async function runDisconnect(target, opts = {}) {
|
|
7442
|
-
const config = readConfig();
|
|
7443
|
-
const meshSlug = opts.mesh ?? config.meshes[0]?.slug;
|
|
7444
|
-
if (!meshSlug) {
|
|
7445
|
-
render.err("No mesh joined.");
|
|
7446
|
-
return EXIT.NOT_FOUND;
|
|
7447
|
-
}
|
|
7448
|
-
const built = buildPayload("disconnect", target, opts);
|
|
7449
|
-
if ("error" in built) {
|
|
7450
|
-
render.err(String(built.error));
|
|
7451
|
-
return EXIT.INVALID_ARGS;
|
|
7452
|
-
}
|
|
7453
|
-
return await withMesh({ meshSlug }, async (client) => {
|
|
7454
|
-
const result = await client.sendAndWait(built);
|
|
7455
|
-
const peers = result?.affected ?? result?.kicked ?? [];
|
|
7456
|
-
if (peers.length === 0)
|
|
7457
|
-
render.info("No peers matched.");
|
|
7458
|
-
else {
|
|
7459
|
-
render.ok(`Disconnected ${peers.length} peer(s): ${peers.join(", ")}`);
|
|
7460
|
-
render.hint("They will auto-reconnect within seconds. For a session-ending kick, use `claudemesh kick`.");
|
|
7461
|
-
}
|
|
7462
|
-
return EXIT.SUCCESS;
|
|
7463
|
-
});
|
|
7464
|
-
}
|
|
7465
|
-
async function runKick(target, opts = {}) {
|
|
7466
|
-
const config = readConfig();
|
|
7467
|
-
const meshSlug = opts.mesh ?? config.meshes[0]?.slug;
|
|
7468
|
-
if (!meshSlug) {
|
|
7469
|
-
render.err("No mesh joined.");
|
|
7470
|
-
return EXIT.NOT_FOUND;
|
|
7471
|
-
}
|
|
7472
|
-
const built = buildPayload("kick", target, opts);
|
|
7473
|
-
if ("error" in built) {
|
|
7474
|
-
render.err(String(built.error));
|
|
7475
|
-
return EXIT.INVALID_ARGS;
|
|
7476
|
-
}
|
|
7477
|
-
return await withMesh({ meshSlug }, async (client) => {
|
|
7478
|
-
const result = await client.sendAndWait(built);
|
|
7479
|
-
const peers = result?.affected ?? result?.kicked ?? [];
|
|
7480
|
-
if (peers.length === 0)
|
|
7481
|
-
render.info("No peers matched.");
|
|
7482
|
-
else {
|
|
7483
|
-
render.ok(`Kicked ${peers.length} peer(s): ${peers.join(", ")}`);
|
|
7484
|
-
render.hint("Their Claude Code session ended. They can rejoin anytime by running `claudemesh`.");
|
|
7485
|
-
}
|
|
7486
|
-
return EXIT.SUCCESS;
|
|
7487
|
-
});
|
|
7488
|
-
}
|
|
7489
|
-
var init_kick = __esm(() => {
|
|
7490
|
-
init_connect();
|
|
7491
|
-
init_facade();
|
|
7492
|
-
init_render();
|
|
7493
|
-
init_exit_codes();
|
|
7494
|
-
});
|
|
7495
|
-
|
|
7496
|
-
// src/commands/ban.ts
|
|
7497
|
-
var exports_ban = {};
|
|
7498
|
-
__export(exports_ban, {
|
|
7499
|
-
runUnban: () => runUnban,
|
|
7500
|
-
runBans: () => runBans,
|
|
7501
|
-
runBan: () => runBan
|
|
7502
|
-
});
|
|
7503
|
-
async function runBan(target, opts = {}) {
|
|
7504
|
-
if (!target) {
|
|
7505
|
-
render.err("Usage: claudemesh ban <peer-name-or-pubkey>");
|
|
7506
|
-
return EXIT.INVALID_ARGS;
|
|
7507
|
-
}
|
|
7508
|
-
const config = readConfig();
|
|
7509
|
-
const meshSlug = opts.mesh ?? config.meshes[0]?.slug;
|
|
7510
|
-
if (!meshSlug) {
|
|
7511
|
-
render.err("No mesh joined.");
|
|
7512
|
-
return EXIT.NOT_FOUND;
|
|
7513
|
-
}
|
|
7514
|
-
return await withMesh({ meshSlug }, async (client) => {
|
|
7515
|
-
const result = await client.sendAndWait({ type: "ban", target });
|
|
7516
|
-
if (result?.banned) {
|
|
7517
|
-
render.ok(`Banned ${result.banned} from ${meshSlug}. They cannot reconnect until unbanned.`);
|
|
7518
|
-
render.hint(`Undo: claudemesh unban ${result.banned} --mesh ${meshSlug}`);
|
|
7519
|
-
} else {
|
|
7520
|
-
render.err(result?.message ?? result?.error ?? result?.code ?? "ban failed");
|
|
7521
|
-
}
|
|
7522
|
-
return result?.banned ? EXIT.SUCCESS : EXIT.INTERNAL_ERROR;
|
|
7523
|
-
});
|
|
7524
|
-
}
|
|
7525
|
-
async function runUnban(target, opts = {}) {
|
|
7526
|
-
if (!target) {
|
|
7527
|
-
render.err("Usage: claudemesh unban <peer-name-or-pubkey>");
|
|
7528
|
-
return EXIT.INVALID_ARGS;
|
|
7529
|
-
}
|
|
7530
|
-
const config = readConfig();
|
|
7531
|
-
const meshSlug = opts.mesh ?? config.meshes[0]?.slug;
|
|
7532
|
-
if (!meshSlug) {
|
|
7533
|
-
render.err("No mesh joined.");
|
|
7534
|
-
return EXIT.NOT_FOUND;
|
|
7535
|
-
}
|
|
7536
|
-
return await withMesh({ meshSlug }, async (client) => {
|
|
7537
|
-
const result = await client.sendAndWait({ type: "unban", target });
|
|
7538
|
-
if (result?.unbanned) {
|
|
7539
|
-
render.ok(`Unbanned ${result.unbanned} from ${meshSlug}. They can rejoin.`);
|
|
7540
|
-
} else {
|
|
7541
|
-
render.err(result?.message ?? result?.error ?? result?.code ?? "unban failed");
|
|
7542
|
-
}
|
|
7543
|
-
return result?.unbanned ? EXIT.SUCCESS : EXIT.INTERNAL_ERROR;
|
|
7544
|
-
});
|
|
7545
|
-
}
|
|
7546
|
-
async function runBans(opts = {}) {
|
|
7547
|
-
const config = readConfig();
|
|
7548
|
-
const meshSlug = opts.mesh ?? config.meshes[0]?.slug;
|
|
7549
|
-
if (!meshSlug) {
|
|
7550
|
-
render.err("No mesh joined.");
|
|
7551
|
-
return EXIT.NOT_FOUND;
|
|
7552
|
-
}
|
|
7553
|
-
return await withMesh({ meshSlug }, async (client) => {
|
|
7554
|
-
const result = await client.sendAndWait({ type: "list_bans" });
|
|
7555
|
-
const bans = result?.bans ?? [];
|
|
7556
|
-
if (opts.json) {
|
|
7557
|
-
process.stdout.write(JSON.stringify(bans, null, 2) + `
|
|
7558
|
-
`);
|
|
7559
|
-
return EXIT.SUCCESS;
|
|
7560
|
-
}
|
|
7561
|
-
if (bans.length === 0) {
|
|
7562
|
-
render.info("No banned members.");
|
|
7563
|
-
return EXIT.SUCCESS;
|
|
7564
|
-
}
|
|
7565
|
-
render.section(`banned members on ${meshSlug}`);
|
|
7566
|
-
for (const b of bans) {
|
|
7567
|
-
render.kv([[b.name, `${b.pubkey.slice(0, 16)}… · banned ${new Date(b.revokedAt).toLocaleDateString()}`]]);
|
|
7568
|
-
}
|
|
7569
|
-
return EXIT.SUCCESS;
|
|
7570
|
-
});
|
|
7571
|
-
}
|
|
7572
|
-
var init_ban = __esm(() => {
|
|
7573
|
-
init_connect();
|
|
7574
|
-
init_facade();
|
|
7575
|
-
init_render();
|
|
7576
|
-
init_exit_codes();
|
|
7577
|
-
});
|
|
7578
|
-
|
|
7579
|
-
// src/ui/warnings.ts
|
|
7580
|
-
function warnDaemonState(res, opts = {}) {
|
|
7581
|
-
if (alreadyWarned)
|
|
7582
|
-
return false;
|
|
7583
|
-
if (opts.quiet || opts.json)
|
|
7584
|
-
return false;
|
|
7585
|
-
if (res.state === "up")
|
|
7586
|
-
return false;
|
|
7587
|
-
if (getDaemonPolicy().mode === "strict" && res.state !== "started")
|
|
7588
|
-
return false;
|
|
7589
|
-
alreadyWarned = true;
|
|
7590
|
-
const tag = (label) => `[claudemesh] ${label}`;
|
|
7591
|
-
const hint = (s) => dim(s);
|
|
7592
|
-
switch (res.state) {
|
|
7593
|
-
case "started":
|
|
7594
|
-
process.stderr.write(`${tag("info")} daemon restarted automatically ${hint(`(took ${res.durationMs}ms)`)}
|
|
7595
|
-
`);
|
|
7596
|
-
return true;
|
|
7597
|
-
case "down":
|
|
7598
|
-
process.stderr.write(`${tag("info")} daemon not running — using cold path ${hint("(slower; run `claudemesh daemon up` for warm path)")}
|
|
7599
|
-
`);
|
|
7600
|
-
return true;
|
|
7601
|
-
case "spawn-suppressed":
|
|
7602
|
-
process.stderr.write(`${tag("warn")} ${res.reason ?? "daemon failed to start recently"} — using cold path ${hint("(run `claudemesh doctor`)")}
|
|
7603
|
-
`);
|
|
7604
|
-
return true;
|
|
7605
|
-
case "spawn-failed":
|
|
7606
|
-
process.stderr.write(`${tag("warn")} daemon spawn failed${res.reason ? `: ${res.reason}` : ""} — using cold path ${hint("(check ~/.claudemesh/daemon/daemon.log)")}
|
|
7607
|
-
`);
|
|
7608
|
-
return true;
|
|
7609
|
-
case "service-not-ready":
|
|
7610
|
-
process.stderr.write(`${tag("warn")} ${res.reason ?? "service-managed daemon not responding"} — using cold path ${hint("(check ~/.claudemesh/daemon/daemon.log)")}
|
|
7611
|
-
`);
|
|
7612
|
-
return true;
|
|
7613
|
-
}
|
|
7614
|
-
return false;
|
|
7615
|
-
}
|
|
7616
|
-
var alreadyWarned = false;
|
|
7617
|
-
var init_warnings = __esm(() => {
|
|
7618
|
-
init_policy();
|
|
7619
|
-
init_styles();
|
|
7620
|
-
});
|
|
7621
|
-
|
|
7622
|
-
// src/services/bridge/daemon-route.ts
|
|
7623
|
-
var exports_daemon_route = {};
|
|
7624
|
-
__export(exports_daemon_route, {
|
|
7625
|
-
trySetStateViaDaemon: () => trySetStateViaDaemon,
|
|
7626
|
-
trySendViaDaemon: () => trySendViaDaemon,
|
|
7627
|
-
tryRememberViaDaemon: () => tryRememberViaDaemon,
|
|
7628
|
-
tryRecallViaDaemon: () => tryRecallViaDaemon,
|
|
7629
|
-
tryListStateViaDaemon: () => tryListStateViaDaemon,
|
|
7630
|
-
tryListSkillsViaDaemon: () => tryListSkillsViaDaemon,
|
|
7631
|
-
tryListPeersViaDaemon: () => tryListPeersViaDaemon,
|
|
7632
|
-
tryGetStateViaDaemon: () => tryGetStateViaDaemon,
|
|
7633
|
-
tryGetSkillViaDaemon: () => tryGetSkillViaDaemon,
|
|
7634
|
-
tryForgetViaDaemon: () => tryForgetViaDaemon
|
|
7635
|
-
});
|
|
7636
|
-
function meshQuery(mesh) {
|
|
7637
|
-
return mesh ? `?mesh=${encodeURIComponent(mesh)}` : "";
|
|
7638
|
-
}
|
|
7639
|
-
async function daemonReachable() {
|
|
7640
|
-
const policy2 = getDaemonPolicy();
|
|
7641
|
-
if (policy2.mode === "no-daemon")
|
|
7642
|
-
return false;
|
|
7643
|
-
const res = await ensureDaemonReady({ noAutoSpawn: false });
|
|
7644
|
-
warnDaemonState(res, {});
|
|
7645
|
-
return res.state === "up" || res.state === "started";
|
|
7646
|
-
}
|
|
7647
|
-
async function tryListPeersViaDaemon(mesh) {
|
|
7648
|
-
if (!await daemonReachable())
|
|
7649
|
-
return null;
|
|
7650
|
-
try {
|
|
7651
|
-
const res = await ipc({ path: `/v1/peers${meshQuery(mesh)}`, timeoutMs: 3000 });
|
|
7652
|
-
if (res.status !== 200)
|
|
7653
|
-
return null;
|
|
7654
|
-
return Array.isArray(res.body.peers) ? res.body.peers : [];
|
|
7655
|
-
} catch (err) {
|
|
7656
|
-
const msg = String(err);
|
|
7657
|
-
if (/ENOENT|ECONNREFUSED|ipc_timeout/.test(msg))
|
|
7658
|
-
return null;
|
|
7659
|
-
return null;
|
|
7660
|
-
}
|
|
7661
|
-
}
|
|
7662
|
-
async function tryListSkillsViaDaemon(mesh) {
|
|
7663
|
-
if (!await daemonReachable())
|
|
7664
|
-
return null;
|
|
7665
|
-
try {
|
|
7666
|
-
const res = await ipc({ path: `/v1/skills${meshQuery(mesh)}`, timeoutMs: 3000 });
|
|
7667
|
-
if (res.status !== 200)
|
|
7668
|
-
return null;
|
|
7669
|
-
return Array.isArray(res.body.skills) ? res.body.skills : [];
|
|
7670
|
-
} catch (err) {
|
|
7671
|
-
const msg = String(err);
|
|
7672
|
-
if (/ENOENT|ECONNREFUSED|ipc_timeout/.test(msg))
|
|
7673
|
-
return null;
|
|
7674
|
-
return null;
|
|
7675
|
-
}
|
|
7676
|
-
}
|
|
7677
|
-
async function tryGetSkillViaDaemon(name, mesh) {
|
|
7678
|
-
if (!await daemonReachable())
|
|
7679
|
-
return null;
|
|
7680
|
-
try {
|
|
7681
|
-
const res = await ipc({
|
|
7682
|
-
path: `/v1/skills/${encodeURIComponent(name)}${meshQuery(mesh)}`,
|
|
7683
|
-
timeoutMs: 3000
|
|
7684
|
-
});
|
|
7685
|
-
if (res.status === 404)
|
|
7686
|
-
return null;
|
|
7687
|
-
if (res.status !== 200)
|
|
7688
|
-
return null;
|
|
7689
|
-
return res.body.skill ?? null;
|
|
7690
|
-
} catch {
|
|
7691
|
-
return null;
|
|
7692
|
-
}
|
|
7693
|
-
}
|
|
7694
|
-
async function tryGetStateViaDaemon(key, mesh) {
|
|
7695
|
-
if (!await daemonReachable())
|
|
7696
|
-
return null;
|
|
7697
|
-
try {
|
|
7698
|
-
const path = `/v1/state?key=${encodeURIComponent(key)}${mesh ? `&mesh=${encodeURIComponent(mesh)}` : ""}`;
|
|
7699
|
-
const res = await ipc({ path, timeoutMs: 3000 });
|
|
7700
|
-
if (res.status === 404)
|
|
7701
|
-
return;
|
|
7702
|
-
if (res.status !== 200)
|
|
7703
|
-
return null;
|
|
7704
|
-
return res.body.state ?? undefined;
|
|
7705
|
-
} catch (err) {
|
|
7706
|
-
const msg = String(err);
|
|
7707
|
-
if (/ENOENT|ECONNREFUSED|ipc_timeout/.test(msg))
|
|
7708
|
-
return null;
|
|
7709
|
-
return null;
|
|
7710
|
-
}
|
|
7711
|
-
}
|
|
7712
|
-
async function tryListStateViaDaemon(mesh) {
|
|
7713
|
-
if (!await daemonReachable())
|
|
7714
|
-
return null;
|
|
7715
|
-
try {
|
|
7716
|
-
const res = await ipc({ path: `/v1/state${meshQuery(mesh)}`, timeoutMs: 3000 });
|
|
7717
|
-
if (res.status !== 200)
|
|
7718
|
-
return null;
|
|
7719
|
-
return Array.isArray(res.body.entries) ? res.body.entries : [];
|
|
7720
|
-
} catch (err) {
|
|
7721
|
-
const msg = String(err);
|
|
7722
|
-
if (/ENOENT|ECONNREFUSED|ipc_timeout/.test(msg))
|
|
7723
|
-
return null;
|
|
7724
|
-
return null;
|
|
7725
|
-
}
|
|
7726
|
-
}
|
|
7727
|
-
async function trySetStateViaDaemon(key, value, mesh) {
|
|
7728
|
-
if (!await daemonReachable())
|
|
7729
|
-
return false;
|
|
7730
|
-
try {
|
|
7731
|
-
const res = await ipc({
|
|
7732
|
-
method: "POST",
|
|
7733
|
-
path: "/v1/state",
|
|
7734
|
-
timeoutMs: 3000,
|
|
7735
|
-
body: { key, value, ...mesh ? { mesh } : {} }
|
|
7736
|
-
});
|
|
7737
|
-
return res.status === 200 && res.body.ok === true;
|
|
7738
|
-
} catch {
|
|
7739
|
-
return false;
|
|
7717
|
+
}
|
|
7718
|
+
function buildPayload(kind, target, opts) {
|
|
7719
|
+
if (opts.all)
|
|
7720
|
+
return { type: kind, all: true };
|
|
7721
|
+
if (opts.stale) {
|
|
7722
|
+
const ms = parseStaleMs(opts.stale);
|
|
7723
|
+
if (!ms)
|
|
7724
|
+
return { error: `Invalid stale duration: "${opts.stale}". Use e.g. 30m, 1h, 300s.` };
|
|
7725
|
+
return { type: kind, stale: ms };
|
|
7740
7726
|
}
|
|
7727
|
+
if (target)
|
|
7728
|
+
return { type: kind, target };
|
|
7729
|
+
return { error: `Usage: claudemesh ${kind} <peer> | --stale 30m | --all` };
|
|
7741
7730
|
}
|
|
7742
|
-
async function
|
|
7743
|
-
|
|
7744
|
-
|
|
7745
|
-
|
|
7746
|
-
|
|
7747
|
-
|
|
7748
|
-
|
|
7749
|
-
|
|
7750
|
-
|
|
7751
|
-
|
|
7752
|
-
|
|
7753
|
-
return null;
|
|
7754
|
-
return { id: res.body.id, mesh: res.body.mesh };
|
|
7755
|
-
} catch {
|
|
7756
|
-
return null;
|
|
7731
|
+
async function runDisconnect(target, opts = {}) {
|
|
7732
|
+
const config = readConfig();
|
|
7733
|
+
const meshSlug = opts.mesh ?? config.meshes[0]?.slug;
|
|
7734
|
+
if (!meshSlug) {
|
|
7735
|
+
render.err("No mesh joined.");
|
|
7736
|
+
return EXIT.NOT_FOUND;
|
|
7737
|
+
}
|
|
7738
|
+
const built = buildPayload("disconnect", target, opts);
|
|
7739
|
+
if ("error" in built) {
|
|
7740
|
+
render.err(String(built.error));
|
|
7741
|
+
return EXIT.INVALID_ARGS;
|
|
7757
7742
|
}
|
|
7743
|
+
return await withMesh({ meshSlug }, async (client) => {
|
|
7744
|
+
const result = await client.sendAndWait(built);
|
|
7745
|
+
const peers = result?.affected ?? result?.kicked ?? [];
|
|
7746
|
+
if (peers.length === 0)
|
|
7747
|
+
render.info("No peers matched.");
|
|
7748
|
+
else {
|
|
7749
|
+
render.ok(`Disconnected ${peers.length} peer(s): ${peers.join(", ")}`);
|
|
7750
|
+
render.hint("They will auto-reconnect within seconds. For a session-ending kick, use `claudemesh kick`.");
|
|
7751
|
+
}
|
|
7752
|
+
return EXIT.SUCCESS;
|
|
7753
|
+
});
|
|
7758
7754
|
}
|
|
7759
|
-
async function
|
|
7760
|
-
|
|
7761
|
-
|
|
7762
|
-
|
|
7763
|
-
|
|
7764
|
-
|
|
7765
|
-
|
|
7766
|
-
|
|
7767
|
-
|
|
7768
|
-
|
|
7769
|
-
|
|
7770
|
-
if (/ENOENT|ECONNREFUSED|ipc_timeout/.test(msg))
|
|
7771
|
-
return null;
|
|
7772
|
-
return null;
|
|
7755
|
+
async function runKick(target, opts = {}) {
|
|
7756
|
+
const config = readConfig();
|
|
7757
|
+
const meshSlug = opts.mesh ?? config.meshes[0]?.slug;
|
|
7758
|
+
if (!meshSlug) {
|
|
7759
|
+
render.err("No mesh joined.");
|
|
7760
|
+
return EXIT.NOT_FOUND;
|
|
7761
|
+
}
|
|
7762
|
+
const built = buildPayload("kick", target, opts);
|
|
7763
|
+
if ("error" in built) {
|
|
7764
|
+
render.err(String(built.error));
|
|
7765
|
+
return EXIT.INVALID_ARGS;
|
|
7773
7766
|
}
|
|
7767
|
+
return await withMesh({ meshSlug }, async (client) => {
|
|
7768
|
+
const result = await client.sendAndWait(built);
|
|
7769
|
+
const peers = result?.affected ?? result?.kicked ?? [];
|
|
7770
|
+
if (peers.length === 0)
|
|
7771
|
+
render.info("No peers matched.");
|
|
7772
|
+
else {
|
|
7773
|
+
render.ok(`Kicked ${peers.length} peer(s): ${peers.join(", ")}`);
|
|
7774
|
+
render.hint("Their Claude Code session ended. They can rejoin anytime by running `claudemesh`.");
|
|
7775
|
+
}
|
|
7776
|
+
return EXIT.SUCCESS;
|
|
7777
|
+
});
|
|
7774
7778
|
}
|
|
7775
|
-
|
|
7776
|
-
|
|
7777
|
-
|
|
7778
|
-
|
|
7779
|
-
|
|
7780
|
-
|
|
7781
|
-
|
|
7782
|
-
|
|
7783
|
-
|
|
7779
|
+
var init_kick = __esm(() => {
|
|
7780
|
+
init_connect();
|
|
7781
|
+
init_facade();
|
|
7782
|
+
init_render();
|
|
7783
|
+
init_exit_codes();
|
|
7784
|
+
});
|
|
7785
|
+
|
|
7786
|
+
// src/commands/ban.ts
|
|
7787
|
+
var exports_ban = {};
|
|
7788
|
+
__export(exports_ban, {
|
|
7789
|
+
runUnban: () => runUnban,
|
|
7790
|
+
runBans: () => runBans,
|
|
7791
|
+
runBan: () => runBan
|
|
7792
|
+
});
|
|
7793
|
+
async function runBan(target, opts = {}) {
|
|
7794
|
+
if (!target) {
|
|
7795
|
+
render.err("Usage: claudemesh ban <peer-name-or-pubkey>");
|
|
7796
|
+
return EXIT.INVALID_ARGS;
|
|
7784
7797
|
}
|
|
7798
|
+
const config = readConfig();
|
|
7799
|
+
const meshSlug = opts.mesh ?? config.meshes[0]?.slug;
|
|
7800
|
+
if (!meshSlug) {
|
|
7801
|
+
render.err("No mesh joined.");
|
|
7802
|
+
return EXIT.NOT_FOUND;
|
|
7803
|
+
}
|
|
7804
|
+
return await withMesh({ meshSlug }, async (client) => {
|
|
7805
|
+
const result = await client.sendAndWait({ type: "ban", target });
|
|
7806
|
+
if (result?.banned) {
|
|
7807
|
+
render.ok(`Banned ${result.banned} from ${meshSlug}. They cannot reconnect until unbanned.`);
|
|
7808
|
+
render.hint(`Undo: claudemesh unban ${result.banned} --mesh ${meshSlug}`);
|
|
7809
|
+
} else {
|
|
7810
|
+
render.err(result?.message ?? result?.error ?? result?.code ?? "ban failed");
|
|
7811
|
+
}
|
|
7812
|
+
return result?.banned ? EXIT.SUCCESS : EXIT.INTERNAL_ERROR;
|
|
7813
|
+
});
|
|
7785
7814
|
}
|
|
7786
|
-
async function
|
|
7787
|
-
if (!
|
|
7788
|
-
|
|
7789
|
-
|
|
7790
|
-
|
|
7791
|
-
|
|
7792
|
-
|
|
7793
|
-
|
|
7794
|
-
|
|
7795
|
-
|
|
7796
|
-
|
|
7797
|
-
|
|
7798
|
-
|
|
7799
|
-
|
|
7800
|
-
}
|
|
7801
|
-
}
|
|
7802
|
-
|
|
7803
|
-
return {
|
|
7804
|
-
ok: true,
|
|
7805
|
-
messageId: res.body.broker_message_id ?? res.body.client_message_id ?? "",
|
|
7806
|
-
duplicate: res.body.duplicate,
|
|
7807
|
-
status: res.body.status
|
|
7808
|
-
};
|
|
7815
|
+
async function runUnban(target, opts = {}) {
|
|
7816
|
+
if (!target) {
|
|
7817
|
+
render.err("Usage: claudemesh unban <peer-name-or-pubkey>");
|
|
7818
|
+
return EXIT.INVALID_ARGS;
|
|
7819
|
+
}
|
|
7820
|
+
const config = readConfig();
|
|
7821
|
+
const meshSlug = opts.mesh ?? config.meshes[0]?.slug;
|
|
7822
|
+
if (!meshSlug) {
|
|
7823
|
+
render.err("No mesh joined.");
|
|
7824
|
+
return EXIT.NOT_FOUND;
|
|
7825
|
+
}
|
|
7826
|
+
return await withMesh({ meshSlug }, async (client) => {
|
|
7827
|
+
const result = await client.sendAndWait({ type: "unban", target });
|
|
7828
|
+
if (result?.unbanned) {
|
|
7829
|
+
render.ok(`Unbanned ${result.unbanned} from ${meshSlug}. They can rejoin.`);
|
|
7830
|
+
} else {
|
|
7831
|
+
render.err(result?.message ?? result?.error ?? result?.code ?? "unban failed");
|
|
7809
7832
|
}
|
|
7810
|
-
return
|
|
7811
|
-
}
|
|
7812
|
-
|
|
7813
|
-
|
|
7814
|
-
|
|
7815
|
-
|
|
7833
|
+
return result?.unbanned ? EXIT.SUCCESS : EXIT.INTERNAL_ERROR;
|
|
7834
|
+
});
|
|
7835
|
+
}
|
|
7836
|
+
async function runBans(opts = {}) {
|
|
7837
|
+
const config = readConfig();
|
|
7838
|
+
const meshSlug = opts.mesh ?? config.meshes[0]?.slug;
|
|
7839
|
+
if (!meshSlug) {
|
|
7840
|
+
render.err("No mesh joined.");
|
|
7841
|
+
return EXIT.NOT_FOUND;
|
|
7816
7842
|
}
|
|
7843
|
+
return await withMesh({ meshSlug }, async (client) => {
|
|
7844
|
+
const result = await client.sendAndWait({ type: "list_bans" });
|
|
7845
|
+
const bans = result?.bans ?? [];
|
|
7846
|
+
if (opts.json) {
|
|
7847
|
+
process.stdout.write(JSON.stringify(bans, null, 2) + `
|
|
7848
|
+
`);
|
|
7849
|
+
return EXIT.SUCCESS;
|
|
7850
|
+
}
|
|
7851
|
+
if (bans.length === 0) {
|
|
7852
|
+
render.info("No banned members.");
|
|
7853
|
+
return EXIT.SUCCESS;
|
|
7854
|
+
}
|
|
7855
|
+
render.section(`banned members on ${meshSlug}`);
|
|
7856
|
+
for (const b of bans) {
|
|
7857
|
+
render.kv([[b.name, `${b.pubkey.slice(0, 16)}… · banned ${new Date(b.revokedAt).toLocaleDateString()}`]]);
|
|
7858
|
+
}
|
|
7859
|
+
return EXIT.SUCCESS;
|
|
7860
|
+
});
|
|
7817
7861
|
}
|
|
7818
|
-
var
|
|
7819
|
-
|
|
7820
|
-
|
|
7821
|
-
|
|
7822
|
-
|
|
7862
|
+
var init_ban = __esm(() => {
|
|
7863
|
+
init_connect();
|
|
7864
|
+
init_facade();
|
|
7865
|
+
init_render();
|
|
7866
|
+
init_exit_codes();
|
|
7823
7867
|
});
|
|
7824
7868
|
|
|
7825
7869
|
// src/services/session/resolve.ts
|
|
@@ -7878,18 +7922,26 @@ async function listPeersForMesh(slug) {
|
|
|
7878
7922
|
const config = readConfig();
|
|
7879
7923
|
const joined = config.meshes.find((m) => m.slug === slug);
|
|
7880
7924
|
const selfMemberPubkey = joined?.pubkey ?? null;
|
|
7925
|
+
let selfSessionPubkey = null;
|
|
7926
|
+
try {
|
|
7927
|
+
const { getSessionInfo: getSessionInfo2 } = await Promise.resolve().then(() => (init_resolve(), exports_resolve));
|
|
7928
|
+
const sess = await getSessionInfo2();
|
|
7929
|
+
if (sess && sess.mesh === slug && sess.presence?.sessionPubkey) {
|
|
7930
|
+
selfSessionPubkey = sess.presence.sessionPubkey;
|
|
7931
|
+
}
|
|
7932
|
+
} catch {}
|
|
7881
7933
|
try {
|
|
7882
7934
|
const { tryListPeersViaDaemon: tryListPeersViaDaemon2 } = await Promise.resolve().then(() => (init_daemon_route(), exports_daemon_route));
|
|
7883
7935
|
const dr = await tryListPeersViaDaemon2();
|
|
7884
7936
|
if (dr !== null) {
|
|
7885
|
-
return dr.map((p) => annotateSelf(p, selfMemberPubkey,
|
|
7937
|
+
return dr.map((p) => annotateSelf(p, selfMemberPubkey, selfSessionPubkey));
|
|
7886
7938
|
}
|
|
7887
7939
|
} catch {}
|
|
7888
7940
|
let result = [];
|
|
7889
7941
|
await withMesh({ meshSlug: slug }, async (client) => {
|
|
7890
7942
|
const all = await client.listPeers();
|
|
7891
|
-
const
|
|
7892
|
-
result = all.map((p) => annotateSelf(p, selfMemberPubkey,
|
|
7943
|
+
const selfSessionPubkey2 = client.getSessionPubkey();
|
|
7944
|
+
result = all.map((p) => annotateSelf(p, selfMemberPubkey, selfSessionPubkey2));
|
|
7893
7945
|
});
|
|
7894
7946
|
return result;
|
|
7895
7947
|
}
|
|
@@ -7925,12 +7977,19 @@ async function runPeers(flags) {
|
|
|
7925
7977
|
allJson.push({ mesh: slug, peers: projected });
|
|
7926
7978
|
continue;
|
|
7927
7979
|
}
|
|
7928
|
-
|
|
7929
|
-
|
|
7980
|
+
const visible = flags.all ? peers : peers.filter((p) => p.channel !== "claudemesh-daemon");
|
|
7981
|
+
const sorted = visible.slice().sort((a, b) => {
|
|
7982
|
+
const score = (p) => p.isThisSession ? 0 : p.isSelf ? 1 : 2;
|
|
7983
|
+
return score(a) - score(b);
|
|
7984
|
+
});
|
|
7985
|
+
const hiddenDaemons = peers.length - visible.length;
|
|
7986
|
+
const header = hiddenDaemons > 0 ? `peers on ${slug} (${sorted.length}, ${hiddenDaemons} daemon hidden — use --all)` : `peers on ${slug} (${sorted.length})`;
|
|
7987
|
+
render.section(header);
|
|
7988
|
+
if (sorted.length === 0) {
|
|
7930
7989
|
render.info(dim(" (no peers connected)"));
|
|
7931
7990
|
continue;
|
|
7932
7991
|
}
|
|
7933
|
-
for (const p of
|
|
7992
|
+
for (const p of sorted) {
|
|
7934
7993
|
const statusDot = p.status === "working" ? yellow("●") : green("●");
|
|
7935
7994
|
const name = bold(p.displayName);
|
|
7936
7995
|
const meta = [];
|
|
@@ -7943,6 +8002,7 @@ async function runPeers(flags) {
|
|
|
7943
8002
|
const metaStr = meta.length ? dim(` (${meta.join(", ")})`) : "";
|
|
7944
8003
|
const summary = p.summary ? dim(` — ${p.summary}`) : "";
|
|
7945
8004
|
const pubkeyTag = dim(` · ${p.pubkey.slice(0, 16)}…`);
|
|
8005
|
+
const sidTag = p.sessionId ? dim(` · sid:${p.sessionId.slice(0, 8)}`) : "";
|
|
7946
8006
|
const selfTag = p.isThisSession ? dim(" ") + yellow("(this session)") : p.isSelf ? dim(" ") + yellow("(your other session)") : "";
|
|
7947
8007
|
const inlineTags = [];
|
|
7948
8008
|
const peerRole = p.profile?.role?.trim();
|
|
@@ -7952,7 +8012,7 @@ async function runPeers(flags) {
|
|
|
7952
8012
|
inlineTags.push(...p.groups.map((g) => `@${g.name}${g.role ? `:${g.role}` : ""}`));
|
|
7953
8013
|
}
|
|
7954
8014
|
const tagsStr = inlineTags.length ? " [" + inlineTags.join(", ") + "]" : "";
|
|
7955
|
-
render.info(`${statusDot} ${name}${selfTag}${tagsStr}${metaStr}${pubkeyTag}${summary}`);
|
|
8015
|
+
render.info(`${statusDot} ${name}${selfTag}${tagsStr}${metaStr}${pubkeyTag}${sidTag}${summary}`);
|
|
7956
8016
|
if (p.cwd)
|
|
7957
8017
|
render.info(dim(` cwd: ${p.cwd}`));
|
|
7958
8018
|
if (!peerRole && p.groups.length === 0) {
|
|
@@ -8023,13 +8083,73 @@ async function runSend(flags, to, message) {
|
|
|
8023
8083
|
to = matches2[0].pubkey ?? to;
|
|
8024
8084
|
} catch {}
|
|
8025
8085
|
}
|
|
8026
|
-
if (
|
|
8086
|
+
if (meshSlug) {
|
|
8027
8087
|
const joined = config.meshes.find((m) => m.slug === meshSlug);
|
|
8028
|
-
|
|
8088
|
+
const isOwnMemberKey = joined && /^[0-9a-f]{64}$/i.test(to) && to.toLowerCase() === joined.pubkey.toLowerCase();
|
|
8089
|
+
if (isOwnMemberKey && !flags.self) {
|
|
8029
8090
|
render.err(`Target "${to.slice(0, 16)}…" is your own member pubkey on mesh "${meshSlug}".`);
|
|
8030
8091
|
render.hint("Pass --self to message a sibling session of your own member, or pick a different peer's pubkey.");
|
|
8031
8092
|
process.exit(1);
|
|
8032
8093
|
}
|
|
8094
|
+
if (isOwnMemberKey && flags.self) {
|
|
8095
|
+
try {
|
|
8096
|
+
const { tryListPeersViaDaemon: tryListPeersViaDaemon2 } = await Promise.resolve().then(() => (init_daemon_route(), exports_daemon_route));
|
|
8097
|
+
const { getSessionInfo: getSessionInfo2 } = await Promise.resolve().then(() => (init_resolve(), exports_resolve));
|
|
8098
|
+
const peers = await tryListPeersViaDaemon2() ?? [];
|
|
8099
|
+
const session = await getSessionInfo2();
|
|
8100
|
+
const ownSessionPk = session?.presence?.sessionPubkey?.toLowerCase();
|
|
8101
|
+
const siblings = peers.filter((p) => {
|
|
8102
|
+
const r = p;
|
|
8103
|
+
if (!r.pubkey)
|
|
8104
|
+
return false;
|
|
8105
|
+
if (ownSessionPk && r.pubkey.toLowerCase() === ownSessionPk)
|
|
8106
|
+
return false;
|
|
8107
|
+
if (r.channel === "claudemesh-daemon")
|
|
8108
|
+
return false;
|
|
8109
|
+
return r.memberPubkey?.toLowerCase() === to.toLowerCase();
|
|
8110
|
+
});
|
|
8111
|
+
if (siblings.length === 0) {
|
|
8112
|
+
render.err(`--self fan-out: no other sibling sessions of your member online.`);
|
|
8113
|
+
process.exit(1);
|
|
8114
|
+
}
|
|
8115
|
+
const results = [];
|
|
8116
|
+
for (const peer of siblings) {
|
|
8117
|
+
const pk = peer.pubkey;
|
|
8118
|
+
const dr = await trySendViaDaemon({ to: pk, message, priority, expectedMesh: meshSlug ?? undefined });
|
|
8119
|
+
if (dr === null) {
|
|
8120
|
+
results.push({ pubkey: pk, ok: false, error: "daemon path unavailable" });
|
|
8121
|
+
continue;
|
|
8122
|
+
}
|
|
8123
|
+
if (dr.ok) {
|
|
8124
|
+
results.push({
|
|
8125
|
+
pubkey: pk,
|
|
8126
|
+
ok: true,
|
|
8127
|
+
...dr.messageId ? { messageId: dr.messageId } : {}
|
|
8128
|
+
});
|
|
8129
|
+
} else {
|
|
8130
|
+
results.push({ pubkey: pk, ok: false, error: dr.error });
|
|
8131
|
+
}
|
|
8132
|
+
}
|
|
8133
|
+
const okCount = results.filter((r) => r.ok).length;
|
|
8134
|
+
if (flags.json) {
|
|
8135
|
+
console.log(JSON.stringify({ ok: okCount > 0, fanout: results, via: "daemon" }));
|
|
8136
|
+
} else if (okCount === results.length) {
|
|
8137
|
+
render.ok(`fanned out to ${okCount} sibling session${okCount === 1 ? "" : "s"} (daemon)`);
|
|
8138
|
+
for (const r of results)
|
|
8139
|
+
render.info(dim(` → ${r.pubkey.slice(0, 16)}… ${r.messageId ? dim(r.messageId.slice(0, 8)) : ""}`));
|
|
8140
|
+
} else {
|
|
8141
|
+
render.warn(`fanned out: ${okCount}/${results.length} delivered`);
|
|
8142
|
+
for (const r of results) {
|
|
8143
|
+
const tag = r.ok ? "✔" : "✘";
|
|
8144
|
+
render.info(` ${tag} ${r.pubkey.slice(0, 16)}… ${r.error ? dim(`— ${r.error}`) : ""}`);
|
|
8145
|
+
}
|
|
8146
|
+
}
|
|
8147
|
+
return;
|
|
8148
|
+
} catch (e) {
|
|
8149
|
+
render.err(`--self fan-out failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
8150
|
+
process.exit(1);
|
|
8151
|
+
}
|
|
8152
|
+
}
|
|
8033
8153
|
}
|
|
8034
8154
|
{
|
|
8035
8155
|
const dr = await trySendViaDaemon({ to, message, priority, expectedMesh: meshSlug ?? undefined });
|
|
@@ -8879,15 +8999,30 @@ __export(exports_whoami, {
|
|
|
8879
8999
|
});
|
|
8880
9000
|
async function whoami(opts) {
|
|
8881
9001
|
const result = await whoAmI();
|
|
9002
|
+
const session = await getSessionInfo();
|
|
8882
9003
|
if (opts.json) {
|
|
8883
|
-
console.log(JSON.stringify({ schema_version: "1.0", ...result }, null, 2));
|
|
8884
|
-
return result.signed_in || result.local ? EXIT.SUCCESS : EXIT.AUTH_FAILED;
|
|
9004
|
+
console.log(JSON.stringify({ schema_version: "1.0", ...result, session }, null, 2));
|
|
9005
|
+
return result.signed_in || result.local || session ? EXIT.SUCCESS : EXIT.AUTH_FAILED;
|
|
8885
9006
|
}
|
|
8886
|
-
if (!result.signed_in && !result.local) {
|
|
9007
|
+
if (!result.signed_in && !result.local && !session) {
|
|
8887
9008
|
render.err("Not signed in", "Run `claudemesh login` to sign in or `claudemesh <invite>` to join.");
|
|
8888
9009
|
return EXIT.AUTH_FAILED;
|
|
8889
9010
|
}
|
|
8890
9011
|
render.section("whoami");
|
|
9012
|
+
if (session) {
|
|
9013
|
+
const sessionPk = session.presence?.sessionPubkey;
|
|
9014
|
+
const groups = (session.groups ?? []).join(", ") || dim("(none)");
|
|
9015
|
+
render.kv([
|
|
9016
|
+
["this session", `${yellow(session.displayName)} on ${bold(session.mesh)}`],
|
|
9017
|
+
["session id", dim(session.sessionId)],
|
|
9018
|
+
...sessionPk ? [["session pubkey", dim(`${sessionPk.slice(0, 16)}… (full: ${sessionPk})`)]] : [],
|
|
9019
|
+
...session.role ? [["role", session.role]] : [],
|
|
9020
|
+
["groups", groups],
|
|
9021
|
+
...session.cwd ? [["cwd", dim(session.cwd)]] : [],
|
|
9022
|
+
["pid", String(session.pid)]
|
|
9023
|
+
]);
|
|
9024
|
+
render.blank();
|
|
9025
|
+
}
|
|
8891
9026
|
if (result.signed_in) {
|
|
8892
9027
|
render.kv([
|
|
8893
9028
|
["user", `${bold(result.user.display_name)} ${dim(`(${result.user.email})`)}`],
|
|
@@ -8913,6 +9048,7 @@ async function whoami(opts) {
|
|
|
8913
9048
|
}
|
|
8914
9049
|
var init_whoami = __esm(() => {
|
|
8915
9050
|
init_facade6();
|
|
9051
|
+
init_resolve();
|
|
8916
9052
|
init_render();
|
|
8917
9053
|
init_styles();
|
|
8918
9054
|
init_exit_codes();
|
|
@@ -10947,6 +11083,10 @@ class SessionBrokerClient {
|
|
|
10947
11083
|
}
|
|
10948
11084
|
return;
|
|
10949
11085
|
}
|
|
11086
|
+
if (msg.type === "push" || msg.type === "inbound") {
|
|
11087
|
+
this.opts.onPush?.(msg);
|
|
11088
|
+
return;
|
|
11089
|
+
}
|
|
10950
11090
|
});
|
|
10951
11091
|
ws.on("close", (code, reason) => {
|
|
10952
11092
|
if (this.helloTimer) {
|
|
@@ -11509,6 +11649,7 @@ async function runDaemon(opts = {}) {
|
|
|
11509
11649
|
sessionBrokers.delete(info.token);
|
|
11510
11650
|
prior.close().catch(() => {});
|
|
11511
11651
|
}
|
|
11652
|
+
const sessionSecretKeyHex = info.presence.sessionSecretKey;
|
|
11512
11653
|
const client = new SessionBrokerClient({
|
|
11513
11654
|
mesh: meshConfig,
|
|
11514
11655
|
sessionPubkey: info.presence.sessionPubkey,
|
|
@@ -11518,7 +11659,16 @@ async function runDaemon(opts = {}) {
|
|
|
11518
11659
|
displayName: info.displayName,
|
|
11519
11660
|
...info.role ? { role: info.role } : {},
|
|
11520
11661
|
...info.cwd ? { cwd: info.cwd } : {},
|
|
11521
|
-
pid: info.pid
|
|
11662
|
+
pid: info.pid,
|
|
11663
|
+
onPush: (m) => {
|
|
11664
|
+
handleBrokerPush(m, {
|
|
11665
|
+
db: inboxDb,
|
|
11666
|
+
bus,
|
|
11667
|
+
meshSlug: meshConfig.slug,
|
|
11668
|
+
recipientSecretKeyHex: meshConfig.secretKey,
|
|
11669
|
+
sessionSecretKeyHex
|
|
11670
|
+
});
|
|
11671
|
+
}
|
|
11522
11672
|
});
|
|
11523
11673
|
sessionBrokers.set(info.token, client);
|
|
11524
11674
|
client.connect().catch((err) => process.stderr.write(JSON.stringify({
|
|
@@ -18339,6 +18489,28 @@ var init_seed_test_mesh = __esm(() => {
|
|
|
18339
18489
|
});
|
|
18340
18490
|
|
|
18341
18491
|
// src/cli/argv.ts
|
|
18492
|
+
var BOOLEAN_FLAGS = new Set([
|
|
18493
|
+
"self",
|
|
18494
|
+
"json",
|
|
18495
|
+
"all",
|
|
18496
|
+
"yes",
|
|
18497
|
+
"y",
|
|
18498
|
+
"help",
|
|
18499
|
+
"h",
|
|
18500
|
+
"version",
|
|
18501
|
+
"v",
|
|
18502
|
+
"quiet",
|
|
18503
|
+
"strict",
|
|
18504
|
+
"continue",
|
|
18505
|
+
"no-daemon",
|
|
18506
|
+
"no-color",
|
|
18507
|
+
"debug",
|
|
18508
|
+
"allow-ci-persistent",
|
|
18509
|
+
"force",
|
|
18510
|
+
"dry-run",
|
|
18511
|
+
"verbose",
|
|
18512
|
+
"skip-service"
|
|
18513
|
+
]);
|
|
18342
18514
|
function parseArgv(argv) {
|
|
18343
18515
|
const args = argv.slice(2);
|
|
18344
18516
|
const flags = {};
|
|
@@ -18346,18 +18518,32 @@ function parseArgv(argv) {
|
|
|
18346
18518
|
let command = "";
|
|
18347
18519
|
for (let i = 0;i < args.length; i++) {
|
|
18348
18520
|
const arg = args[i];
|
|
18521
|
+
if (arg.startsWith("--") && arg.includes("=")) {
|
|
18522
|
+
const eq = arg.indexOf("=");
|
|
18523
|
+
const key = arg.slice(2, eq);
|
|
18524
|
+
flags[key] = arg.slice(eq + 1);
|
|
18525
|
+
continue;
|
|
18526
|
+
}
|
|
18349
18527
|
if (arg.startsWith("--")) {
|
|
18350
18528
|
const key = arg.slice(2);
|
|
18529
|
+
if (BOOLEAN_FLAGS.has(key)) {
|
|
18530
|
+
flags[key] = true;
|
|
18531
|
+
continue;
|
|
18532
|
+
}
|
|
18351
18533
|
const next = args[i + 1];
|
|
18352
|
-
if (next && !next.startsWith("-")) {
|
|
18534
|
+
if (next !== undefined && !next.startsWith("-")) {
|
|
18353
18535
|
flags[key] = next;
|
|
18354
18536
|
i++;
|
|
18355
18537
|
} else
|
|
18356
18538
|
flags[key] = true;
|
|
18357
18539
|
} else if (arg.startsWith("-") && arg.length === 2) {
|
|
18358
18540
|
const key = arg.slice(1);
|
|
18541
|
+
if (BOOLEAN_FLAGS.has(key)) {
|
|
18542
|
+
flags[key] = true;
|
|
18543
|
+
continue;
|
|
18544
|
+
}
|
|
18359
18545
|
const next = args[i + 1];
|
|
18360
|
-
if (next && !next.startsWith("-")) {
|
|
18546
|
+
if (next !== undefined && !next.startsWith("-")) {
|
|
18361
18547
|
flags[key] = next;
|
|
18362
18548
|
i++;
|
|
18363
18549
|
} else
|
|
@@ -18931,6 +19117,10 @@ Peer (resource form, recommended)
|
|
|
18931
19117
|
|
|
18932
19118
|
Message (resource form)
|
|
18933
19119
|
claudemesh message send <to> <m> send a message (alias: send)
|
|
19120
|
+
flags: [--priority now|next|low] [--mesh <slug>]
|
|
19121
|
+
[--self] (allow targeting your own member/session pubkey;
|
|
19122
|
+
fans out to every sibling session of your member)
|
|
19123
|
+
[--json] (machine-readable result)
|
|
18934
19124
|
claudemesh message inbox drain pending (alias: inbox)
|
|
18935
19125
|
claudemesh message status <id> delivery status (alias: msg-status)
|
|
18936
19126
|
|
|
@@ -19279,7 +19469,7 @@ async function main() {
|
|
|
19279
19469
|
}
|
|
19280
19470
|
case "peers": {
|
|
19281
19471
|
const { runPeers: runPeers2 } = await Promise.resolve().then(() => (init_peers(), exports_peers));
|
|
19282
|
-
await runPeers2({ mesh: flags.mesh, json: flags.json });
|
|
19472
|
+
await runPeers2({ mesh: flags.mesh, json: flags.json, all: !!flags.all });
|
|
19283
19473
|
break;
|
|
19284
19474
|
}
|
|
19285
19475
|
case "send": {
|
|
@@ -19508,7 +19698,7 @@ async function main() {
|
|
|
19508
19698
|
}
|
|
19509
19699
|
case "peer": {
|
|
19510
19700
|
const sub = positionals[0];
|
|
19511
|
-
const f = { mesh: flags.mesh, json: flags.json };
|
|
19701
|
+
const f = { mesh: flags.mesh, json: flags.json, all: !!flags.all };
|
|
19512
19702
|
const id = positionals[1] ?? "";
|
|
19513
19703
|
if (sub === "list") {
|
|
19514
19704
|
const { runPeers: runPeers2 } = await Promise.resolve().then(() => (init_peers(), exports_peers));
|
|
@@ -19541,7 +19731,7 @@ async function main() {
|
|
|
19541
19731
|
const sub = positionals[0];
|
|
19542
19732
|
if (sub === "send") {
|
|
19543
19733
|
const { runSend: runSend2 } = await Promise.resolve().then(() => (init_send(), exports_send));
|
|
19544
|
-
await runSend2({ mesh: flags.mesh, priority: flags.priority, json: !!flags.json }, positionals[1] ?? "", positionals.slice(2).join(" "));
|
|
19734
|
+
await runSend2({ mesh: flags.mesh, priority: flags.priority, json: !!flags.json, self: !!flags.self }, positionals[1] ?? "", positionals.slice(2).join(" "));
|
|
19545
19735
|
} else if (sub === "inbox") {
|
|
19546
19736
|
const { runInbox: runInbox2 } = await Promise.resolve().then(() => (init_inbox(), exports_inbox));
|
|
19547
19737
|
await runInbox2({ json: !!flags.json });
|
|
@@ -20097,4 +20287,4 @@ main().catch((err) => {
|
|
|
20097
20287
|
process.exit(EXIT.INTERNAL_ERROR);
|
|
20098
20288
|
});
|
|
20099
20289
|
|
|
20100
|
-
//# debugId=
|
|
20290
|
+
//# debugId=FAFB4F8D5ECBBBB864756E2164756E21
|