claudemesh-cli 1.31.6 → 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 +582 -406
- 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) {
|
|
@@ -7431,395 +7721,149 @@ function buildPayload(kind, target, opts) {
|
|
|
7431
7721
|
if (opts.stale) {
|
|
7432
7722
|
const ms = parseStaleMs(opts.stale);
|
|
7433
7723
|
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;
|
|
7724
|
+
return { error: `Invalid stale duration: "${opts.stale}". Use e.g. 30m, 1h, 300s.` };
|
|
7725
|
+
return { type: kind, stale: ms };
|
|
7725
7726
|
}
|
|
7727
|
+
if (target)
|
|
7728
|
+
return { type: kind, target };
|
|
7729
|
+
return { error: `Usage: claudemesh ${kind} <peer> | --stale 30m | --all` };
|
|
7726
7730
|
}
|
|
7727
|
-
async function
|
|
7728
|
-
|
|
7729
|
-
|
|
7730
|
-
|
|
7731
|
-
|
|
7732
|
-
|
|
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;
|
|
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;
|
|
7740
7737
|
}
|
|
7741
|
-
|
|
7742
|
-
|
|
7743
|
-
|
|
7744
|
-
return
|
|
7745
|
-
try {
|
|
7746
|
-
const res = await ipc({
|
|
7747
|
-
method: "POST",
|
|
7748
|
-
path: "/v1/memory",
|
|
7749
|
-
timeoutMs: 5000,
|
|
7750
|
-
body: { content, ...tags?.length ? { tags } : {}, ...mesh ? { mesh } : {} }
|
|
7751
|
-
});
|
|
7752
|
-
if (res.status !== 200 || !res.body.id)
|
|
7753
|
-
return null;
|
|
7754
|
-
return { id: res.body.id, mesh: res.body.mesh };
|
|
7755
|
-
} catch {
|
|
7756
|
-
return null;
|
|
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) {
|
|
@@ -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();
|
|
@@ -18339,6 +18475,28 @@ var init_seed_test_mesh = __esm(() => {
|
|
|
18339
18475
|
});
|
|
18340
18476
|
|
|
18341
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
|
+
]);
|
|
18342
18500
|
function parseArgv(argv) {
|
|
18343
18501
|
const args = argv.slice(2);
|
|
18344
18502
|
const flags = {};
|
|
@@ -18346,18 +18504,32 @@ function parseArgv(argv) {
|
|
|
18346
18504
|
let command = "";
|
|
18347
18505
|
for (let i = 0;i < args.length; i++) {
|
|
18348
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
|
+
}
|
|
18349
18513
|
if (arg.startsWith("--")) {
|
|
18350
18514
|
const key = arg.slice(2);
|
|
18515
|
+
if (BOOLEAN_FLAGS.has(key)) {
|
|
18516
|
+
flags[key] = true;
|
|
18517
|
+
continue;
|
|
18518
|
+
}
|
|
18351
18519
|
const next = args[i + 1];
|
|
18352
|
-
if (next && !next.startsWith("-")) {
|
|
18520
|
+
if (next !== undefined && !next.startsWith("-")) {
|
|
18353
18521
|
flags[key] = next;
|
|
18354
18522
|
i++;
|
|
18355
18523
|
} else
|
|
18356
18524
|
flags[key] = true;
|
|
18357
18525
|
} else if (arg.startsWith("-") && arg.length === 2) {
|
|
18358
18526
|
const key = arg.slice(1);
|
|
18527
|
+
if (BOOLEAN_FLAGS.has(key)) {
|
|
18528
|
+
flags[key] = true;
|
|
18529
|
+
continue;
|
|
18530
|
+
}
|
|
18359
18531
|
const next = args[i + 1];
|
|
18360
|
-
if (next && !next.startsWith("-")) {
|
|
18532
|
+
if (next !== undefined && !next.startsWith("-")) {
|
|
18361
18533
|
flags[key] = next;
|
|
18362
18534
|
i++;
|
|
18363
18535
|
} else
|
|
@@ -18931,6 +19103,10 @@ Peer (resource form, recommended)
|
|
|
18931
19103
|
|
|
18932
19104
|
Message (resource form)
|
|
18933
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)
|
|
18934
19110
|
claudemesh message inbox drain pending (alias: inbox)
|
|
18935
19111
|
claudemesh message status <id> delivery status (alias: msg-status)
|
|
18936
19112
|
|
|
@@ -19279,7 +19455,7 @@ async function main() {
|
|
|
19279
19455
|
}
|
|
19280
19456
|
case "peers": {
|
|
19281
19457
|
const { runPeers: runPeers2 } = await Promise.resolve().then(() => (init_peers(), exports_peers));
|
|
19282
|
-
await runPeers2({ mesh: flags.mesh, json: flags.json });
|
|
19458
|
+
await runPeers2({ mesh: flags.mesh, json: flags.json, all: !!flags.all });
|
|
19283
19459
|
break;
|
|
19284
19460
|
}
|
|
19285
19461
|
case "send": {
|
|
@@ -19508,7 +19684,7 @@ async function main() {
|
|
|
19508
19684
|
}
|
|
19509
19685
|
case "peer": {
|
|
19510
19686
|
const sub = positionals[0];
|
|
19511
|
-
const f = { mesh: flags.mesh, json: flags.json };
|
|
19687
|
+
const f = { mesh: flags.mesh, json: flags.json, all: !!flags.all };
|
|
19512
19688
|
const id = positionals[1] ?? "";
|
|
19513
19689
|
if (sub === "list") {
|
|
19514
19690
|
const { runPeers: runPeers2 } = await Promise.resolve().then(() => (init_peers(), exports_peers));
|
|
@@ -19541,7 +19717,7 @@ async function main() {
|
|
|
19541
19717
|
const sub = positionals[0];
|
|
19542
19718
|
if (sub === "send") {
|
|
19543
19719
|
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(" "));
|
|
19720
|
+
await runSend2({ mesh: flags.mesh, priority: flags.priority, json: !!flags.json, self: !!flags.self }, positionals[1] ?? "", positionals.slice(2).join(" "));
|
|
19545
19721
|
} else if (sub === "inbox") {
|
|
19546
19722
|
const { runInbox: runInbox2 } = await Promise.resolve().then(() => (init_inbox(), exports_inbox));
|
|
19547
19723
|
await runInbox2({ json: !!flags.json });
|
|
@@ -20097,4 +20273,4 @@ main().catch((err) => {
|
|
|
20097
20273
|
process.exit(EXIT.INTERNAL_ERROR);
|
|
20098
20274
|
});
|
|
20099
20275
|
|
|
20100
|
-
//# debugId=
|
|
20276
|
+
//# debugId=9045DA825D3D7C3164756E2164756E21
|