claudemesh-cli 1.31.5 → 1.32.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/entrypoints/cli.js +619 -412
- package/dist/entrypoints/cli.js.map +12 -12
- 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.0", 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) {
|
|
@@ -7426,400 +7716,154 @@ function parseStaleMs(input) {
|
|
|
7426
7716
|
return null;
|
|
7427
7717
|
}
|
|
7428
7718
|
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;
|
|
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;
|
|
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;
|
|
7784
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) {
|
|
@@ -7992,13 +8052,104 @@ async function runSend(flags, to, message) {
|
|
|
7992
8052
|
const priority = flags.priority === "now" ? "now" : flags.priority === "low" ? "low" : "next";
|
|
7993
8053
|
const config = readConfig();
|
|
7994
8054
|
const meshSlug = flags.mesh ?? (config.meshes.length === 1 ? config.meshes[0].slug : null);
|
|
7995
|
-
if (!
|
|
8055
|
+
if (!to.startsWith("@") && !to.startsWith("#") && to !== "*" && /^[0-9a-f]{4,63}$/i.test(to)) {
|
|
8056
|
+
try {
|
|
8057
|
+
const { tryListPeersViaDaemon: tryListPeersViaDaemon2 } = await Promise.resolve().then(() => (init_daemon_route(), exports_daemon_route));
|
|
8058
|
+
const peers = await tryListPeersViaDaemon2() ?? [];
|
|
8059
|
+
const lower = to.toLowerCase();
|
|
8060
|
+
const matches2 = peers.filter((p) => {
|
|
8061
|
+
const pk = p.pubkey ?? "";
|
|
8062
|
+
const mpk = p.memberPubkey ?? "";
|
|
8063
|
+
return pk.toLowerCase().startsWith(lower) || mpk.toLowerCase().startsWith(lower);
|
|
8064
|
+
});
|
|
8065
|
+
if (matches2.length === 0) {
|
|
8066
|
+
render.err(`No peer matches hex prefix "${to}".`);
|
|
8067
|
+
const names = peers.map((p) => p.displayName).filter(Boolean).join(", ");
|
|
8068
|
+
if (names)
|
|
8069
|
+
render.hint(`online: ${names}`);
|
|
8070
|
+
process.exit(1);
|
|
8071
|
+
}
|
|
8072
|
+
if (matches2.length > 1) {
|
|
8073
|
+
const candidates = matches2.map((p) => {
|
|
8074
|
+
const pk = p.pubkey ?? "";
|
|
8075
|
+
const dn = p.displayName ?? "?";
|
|
8076
|
+
return `${dn} ${pk.slice(0, 16)}…`;
|
|
8077
|
+
}).join(", ");
|
|
8078
|
+
render.err(`Ambiguous hex prefix "${to}" — matches ${matches2.length} peers.`);
|
|
8079
|
+
render.hint(`candidates: ${candidates}`);
|
|
8080
|
+
render.hint("Use a longer prefix or paste the full 64-char pubkey.");
|
|
8081
|
+
process.exit(1);
|
|
8082
|
+
}
|
|
8083
|
+
to = matches2[0].pubkey ?? to;
|
|
8084
|
+
} catch {}
|
|
8085
|
+
}
|
|
8086
|
+
if (meshSlug) {
|
|
7996
8087
|
const joined = config.meshes.find((m) => m.slug === meshSlug);
|
|
7997
|
-
|
|
8088
|
+
const isOwnMemberKey = joined && /^[0-9a-f]{64}$/i.test(to) && to.toLowerCase() === joined.pubkey.toLowerCase();
|
|
8089
|
+
if (isOwnMemberKey && !flags.self) {
|
|
7998
8090
|
render.err(`Target "${to.slice(0, 16)}…" is your own member pubkey on mesh "${meshSlug}".`);
|
|
7999
8091
|
render.hint("Pass --self to message a sibling session of your own member, or pick a different peer's pubkey.");
|
|
8000
8092
|
process.exit(1);
|
|
8001
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
|
+
}
|
|
8002
8153
|
}
|
|
8003
8154
|
{
|
|
8004
8155
|
const dr = await trySendViaDaemon({ to, message, priority, expectedMesh: meshSlug ?? undefined });
|
|
@@ -8848,15 +8999,30 @@ __export(exports_whoami, {
|
|
|
8848
8999
|
});
|
|
8849
9000
|
async function whoami(opts) {
|
|
8850
9001
|
const result = await whoAmI();
|
|
9002
|
+
const session = await getSessionInfo();
|
|
8851
9003
|
if (opts.json) {
|
|
8852
|
-
console.log(JSON.stringify({ schema_version: "1.0", ...result }, null, 2));
|
|
8853
|
-
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;
|
|
8854
9006
|
}
|
|
8855
|
-
if (!result.signed_in && !result.local) {
|
|
9007
|
+
if (!result.signed_in && !result.local && !session) {
|
|
8856
9008
|
render.err("Not signed in", "Run `claudemesh login` to sign in or `claudemesh <invite>` to join.");
|
|
8857
9009
|
return EXIT.AUTH_FAILED;
|
|
8858
9010
|
}
|
|
8859
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
|
+
}
|
|
8860
9026
|
if (result.signed_in) {
|
|
8861
9027
|
render.kv([
|
|
8862
9028
|
["user", `${bold(result.user.display_name)} ${dim(`(${result.user.email})`)}`],
|
|
@@ -8882,6 +9048,7 @@ async function whoami(opts) {
|
|
|
8882
9048
|
}
|
|
8883
9049
|
var init_whoami = __esm(() => {
|
|
8884
9050
|
init_facade6();
|
|
9051
|
+
init_resolve();
|
|
8885
9052
|
init_render();
|
|
8886
9053
|
init_styles();
|
|
8887
9054
|
init_exit_codes();
|
|
@@ -18308,6 +18475,28 @@ var init_seed_test_mesh = __esm(() => {
|
|
|
18308
18475
|
});
|
|
18309
18476
|
|
|
18310
18477
|
// src/cli/argv.ts
|
|
18478
|
+
var BOOLEAN_FLAGS = new Set([
|
|
18479
|
+
"self",
|
|
18480
|
+
"json",
|
|
18481
|
+
"all",
|
|
18482
|
+
"yes",
|
|
18483
|
+
"y",
|
|
18484
|
+
"help",
|
|
18485
|
+
"h",
|
|
18486
|
+
"version",
|
|
18487
|
+
"v",
|
|
18488
|
+
"quiet",
|
|
18489
|
+
"strict",
|
|
18490
|
+
"continue",
|
|
18491
|
+
"no-daemon",
|
|
18492
|
+
"no-color",
|
|
18493
|
+
"debug",
|
|
18494
|
+
"allow-ci-persistent",
|
|
18495
|
+
"force",
|
|
18496
|
+
"dry-run",
|
|
18497
|
+
"verbose",
|
|
18498
|
+
"skip-service"
|
|
18499
|
+
]);
|
|
18311
18500
|
function parseArgv(argv) {
|
|
18312
18501
|
const args = argv.slice(2);
|
|
18313
18502
|
const flags = {};
|
|
@@ -18315,18 +18504,32 @@ function parseArgv(argv) {
|
|
|
18315
18504
|
let command = "";
|
|
18316
18505
|
for (let i = 0;i < args.length; i++) {
|
|
18317
18506
|
const arg = args[i];
|
|
18507
|
+
if (arg.startsWith("--") && arg.includes("=")) {
|
|
18508
|
+
const eq = arg.indexOf("=");
|
|
18509
|
+
const key = arg.slice(2, eq);
|
|
18510
|
+
flags[key] = arg.slice(eq + 1);
|
|
18511
|
+
continue;
|
|
18512
|
+
}
|
|
18318
18513
|
if (arg.startsWith("--")) {
|
|
18319
18514
|
const key = arg.slice(2);
|
|
18515
|
+
if (BOOLEAN_FLAGS.has(key)) {
|
|
18516
|
+
flags[key] = true;
|
|
18517
|
+
continue;
|
|
18518
|
+
}
|
|
18320
18519
|
const next = args[i + 1];
|
|
18321
|
-
if (next && !next.startsWith("-")) {
|
|
18520
|
+
if (next !== undefined && !next.startsWith("-")) {
|
|
18322
18521
|
flags[key] = next;
|
|
18323
18522
|
i++;
|
|
18324
18523
|
} else
|
|
18325
18524
|
flags[key] = true;
|
|
18326
18525
|
} else if (arg.startsWith("-") && arg.length === 2) {
|
|
18327
18526
|
const key = arg.slice(1);
|
|
18527
|
+
if (BOOLEAN_FLAGS.has(key)) {
|
|
18528
|
+
flags[key] = true;
|
|
18529
|
+
continue;
|
|
18530
|
+
}
|
|
18328
18531
|
const next = args[i + 1];
|
|
18329
|
-
if (next && !next.startsWith("-")) {
|
|
18532
|
+
if (next !== undefined && !next.startsWith("-")) {
|
|
18330
18533
|
flags[key] = next;
|
|
18331
18534
|
i++;
|
|
18332
18535
|
} else
|
|
@@ -18900,6 +19103,10 @@ Peer (resource form, recommended)
|
|
|
18900
19103
|
|
|
18901
19104
|
Message (resource form)
|
|
18902
19105
|
claudemesh message send <to> <m> send a message (alias: send)
|
|
19106
|
+
flags: [--priority now|next|low] [--mesh <slug>]
|
|
19107
|
+
[--self] (allow targeting your own member/session pubkey;
|
|
19108
|
+
fans out to every sibling session of your member)
|
|
19109
|
+
[--json] (machine-readable result)
|
|
18903
19110
|
claudemesh message inbox drain pending (alias: inbox)
|
|
18904
19111
|
claudemesh message status <id> delivery status (alias: msg-status)
|
|
18905
19112
|
|
|
@@ -19248,7 +19455,7 @@ async function main() {
|
|
|
19248
19455
|
}
|
|
19249
19456
|
case "peers": {
|
|
19250
19457
|
const { runPeers: runPeers2 } = await Promise.resolve().then(() => (init_peers(), exports_peers));
|
|
19251
|
-
await runPeers2({ mesh: flags.mesh, json: flags.json });
|
|
19458
|
+
await runPeers2({ mesh: flags.mesh, json: flags.json, all: !!flags.all });
|
|
19252
19459
|
break;
|
|
19253
19460
|
}
|
|
19254
19461
|
case "send": {
|
|
@@ -19477,7 +19684,7 @@ async function main() {
|
|
|
19477
19684
|
}
|
|
19478
19685
|
case "peer": {
|
|
19479
19686
|
const sub = positionals[0];
|
|
19480
|
-
const f = { mesh: flags.mesh, json: flags.json };
|
|
19687
|
+
const f = { mesh: flags.mesh, json: flags.json, all: !!flags.all };
|
|
19481
19688
|
const id = positionals[1] ?? "";
|
|
19482
19689
|
if (sub === "list") {
|
|
19483
19690
|
const { runPeers: runPeers2 } = await Promise.resolve().then(() => (init_peers(), exports_peers));
|
|
@@ -19510,7 +19717,7 @@ async function main() {
|
|
|
19510
19717
|
const sub = positionals[0];
|
|
19511
19718
|
if (sub === "send") {
|
|
19512
19719
|
const { runSend: runSend2 } = await Promise.resolve().then(() => (init_send(), exports_send));
|
|
19513
|
-
await runSend2({ mesh: flags.mesh, priority: flags.priority, json: !!flags.json }, positionals[1] ?? "", positionals.slice(2).join(" "));
|
|
19720
|
+
await runSend2({ mesh: flags.mesh, priority: flags.priority, json: !!flags.json, self: !!flags.self }, positionals[1] ?? "", positionals.slice(2).join(" "));
|
|
19514
19721
|
} else if (sub === "inbox") {
|
|
19515
19722
|
const { runInbox: runInbox2 } = await Promise.resolve().then(() => (init_inbox(), exports_inbox));
|
|
19516
19723
|
await runInbox2({ json: !!flags.json });
|
|
@@ -20066,4 +20273,4 @@ main().catch((err) => {
|
|
|
20066
20273
|
process.exit(EXIT.INTERNAL_ERROR);
|
|
20067
20274
|
});
|
|
20068
20275
|
|
|
20069
|
-
//# debugId=
|
|
20276
|
+
//# debugId=9045DA825D3D7C3164756E2164756E21
|