@slock-ai/daemon 0.40.2 → 0.41.1-alpha.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/chat-bridge.js +127 -1
- package/dist/{chunk-PB75DRIF.js → chunk-JAB3HALZ.js} +856 -65
- package/dist/cli/index.js +170 -1
- package/dist/core.js +1 -1
- package/dist/index.js +7 -2
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -235,7 +235,7 @@ function formatServerInfo(data) {
|
|
|
235
235
|
const agents = data.agents ?? [];
|
|
236
236
|
const humans = data.humans ?? [];
|
|
237
237
|
text += "### Channels\n";
|
|
238
|
-
text += 'Visible public channels may appear even when `joined=false`. Use `slock message read --channel "#name"` to inspect them. When a channel is not joined, you cannot send messages there or receive ordinary channel delivery until a human adds you to the channel
|
|
238
|
+
text += 'Visible public channels may appear even when `joined=false`. Use `slock message read --channel "#name"` to inspect them. When a channel is not joined, you cannot send messages there or receive ordinary channel delivery until a human adds you to the channel. To leave a channel you have joined, use `slock channel leave --target "#name"`. To stop following a thread, use `slock thread unfollow --target "#name:shortid"`.\n';
|
|
239
239
|
if (channels.length > 0) {
|
|
240
240
|
for (const t of channels) {
|
|
241
241
|
const status = t.joined ? "joined" : "not joined";
|
|
@@ -271,6 +271,63 @@ function formatServerInfo(data) {
|
|
|
271
271
|
}
|
|
272
272
|
return text;
|
|
273
273
|
}
|
|
274
|
+
function formatChannelMembers(data) {
|
|
275
|
+
let text = "## Channel Members\n\n";
|
|
276
|
+
const ref = data.channel?.ref ?? "(unknown)";
|
|
277
|
+
const type = data.channel?.type ? ` (${data.channel.type})` : "";
|
|
278
|
+
const agents = data.agents ?? [];
|
|
279
|
+
const humans = data.humans ?? [];
|
|
280
|
+
text += `Channel: ${ref}${type}
|
|
281
|
+
`;
|
|
282
|
+
text += "Members means join/post authority for this surface.\n\n";
|
|
283
|
+
text += "### Agents\n";
|
|
284
|
+
if (agents.length > 0) {
|
|
285
|
+
for (const a of agents) {
|
|
286
|
+
text += a.description ? ` - @${a.name} (${a.status}) \u2014 ${a.description}
|
|
287
|
+
` : ` - @${a.name} (${a.status})
|
|
288
|
+
`;
|
|
289
|
+
}
|
|
290
|
+
} else {
|
|
291
|
+
text += " (none)\n";
|
|
292
|
+
}
|
|
293
|
+
text += "\n### Humans\n";
|
|
294
|
+
if (humans.length > 0) {
|
|
295
|
+
for (const u of humans) {
|
|
296
|
+
text += u.description ? ` - @${u.name} \u2014 ${u.description}
|
|
297
|
+
` : ` - @${u.name}
|
|
298
|
+
`;
|
|
299
|
+
}
|
|
300
|
+
} else {
|
|
301
|
+
text += " (none)\n";
|
|
302
|
+
}
|
|
303
|
+
return text;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// src/commands/channel/members.ts
|
|
307
|
+
function registerChannelMembersCommand(parent) {
|
|
308
|
+
parent.command("members").description("List agents and humans who are members of a channel, DM, or thread").argument("<target>", "Channel / DM / thread target, e.g. #proj-runtime, dm:@alice, #proj-runtime:abcd1234").action(async (target) => {
|
|
309
|
+
let ctx;
|
|
310
|
+
try {
|
|
311
|
+
ctx = loadAgentContext();
|
|
312
|
+
} catch (err) {
|
|
313
|
+
if (err instanceof AgentBootstrapError) fail(err.code, err.message);
|
|
314
|
+
throw err;
|
|
315
|
+
}
|
|
316
|
+
const client = new ApiClient(ctx);
|
|
317
|
+
const channel = String(target || "").trim();
|
|
318
|
+
if (!channel) fail("MEMBERS_FAILED", "target is required");
|
|
319
|
+
const encoded = encodeURIComponent(channel);
|
|
320
|
+
const res = await client.request(
|
|
321
|
+
"GET",
|
|
322
|
+
`/internal/agent/${encodeURIComponent(ctx.agentId)}/channel-members?channel=${encoded}`
|
|
323
|
+
);
|
|
324
|
+
if (!res.ok) {
|
|
325
|
+
const code = res.status >= 500 ? "SERVER_5XX" : "MEMBERS_FAILED";
|
|
326
|
+
fail(code, res.error ?? `HTTP ${res.status}`);
|
|
327
|
+
}
|
|
328
|
+
process.stdout.write(formatChannelMembers(res.data));
|
|
329
|
+
});
|
|
330
|
+
}
|
|
274
331
|
|
|
275
332
|
// src/commands/server/info.ts
|
|
276
333
|
function registerServerInfoCommand(parent) {
|
|
@@ -295,6 +352,113 @@ function registerServerInfoCommand(parent) {
|
|
|
295
352
|
});
|
|
296
353
|
}
|
|
297
354
|
|
|
355
|
+
// src/commands/channel/leave.ts
|
|
356
|
+
function parseRegularChannelTarget(target) {
|
|
357
|
+
if (!target.startsWith("#")) return null;
|
|
358
|
+
if (target.includes(":")) return null;
|
|
359
|
+
const name = target.slice(1).trim();
|
|
360
|
+
return name.length > 0 ? name : null;
|
|
361
|
+
}
|
|
362
|
+
function formatLeaveChannelResult(target) {
|
|
363
|
+
return `Left ${target}. You can still inspect visible public channel history there, but you can no longer send or receive ordinary channel delivery until a human adds you again.`;
|
|
364
|
+
}
|
|
365
|
+
function formatAlreadyNotJoined(target) {
|
|
366
|
+
return `Already not joined in ${target}.`;
|
|
367
|
+
}
|
|
368
|
+
function registerChannelLeaveCommand(parent) {
|
|
369
|
+
parent.command("leave").description("Leave a regular channel you have joined").requiredOption("--target <target>", "Regular channel to leave, e.g. '#engineering'").action(async (opts) => {
|
|
370
|
+
const channelName = parseRegularChannelTarget(opts.target);
|
|
371
|
+
if (!channelName) {
|
|
372
|
+
fail("INVALID_TARGET", "Target must be a regular channel in the form '#channel-name'. DMs and thread targets are not supported.");
|
|
373
|
+
}
|
|
374
|
+
let ctx;
|
|
375
|
+
try {
|
|
376
|
+
ctx = loadAgentContext();
|
|
377
|
+
} catch (err) {
|
|
378
|
+
if (err instanceof AgentBootstrapError) fail(err.code, err.message);
|
|
379
|
+
throw err;
|
|
380
|
+
}
|
|
381
|
+
const client = new ApiClient(ctx);
|
|
382
|
+
const infoRes = await client.request(
|
|
383
|
+
"GET",
|
|
384
|
+
`/internal/agent/${encodeURIComponent(ctx.agentId)}/server`
|
|
385
|
+
);
|
|
386
|
+
if (!infoRes.ok) {
|
|
387
|
+
const code = infoRes.status >= 500 ? "SERVER_5XX" : "INFO_FAILED";
|
|
388
|
+
fail(code, infoRes.error ?? `HTTP ${infoRes.status}`);
|
|
389
|
+
}
|
|
390
|
+
const channel = (infoRes.data?.channels ?? []).find((candidate) => candidate.name === channelName);
|
|
391
|
+
if (!channel) {
|
|
392
|
+
fail("NOT_FOUND", `Channel not found: ${opts.target}`);
|
|
393
|
+
}
|
|
394
|
+
if (!channel.joined) {
|
|
395
|
+
process.stdout.write(formatAlreadyNotJoined(opts.target) + "\n");
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
const leaveRes = await client.request(
|
|
399
|
+
"POST",
|
|
400
|
+
`/internal/agent/${encodeURIComponent(ctx.agentId)}/channels/${encodeURIComponent(channel.id)}/leave`
|
|
401
|
+
);
|
|
402
|
+
if (!leaveRes.ok) {
|
|
403
|
+
const code = leaveRes.status >= 500 ? "SERVER_5XX" : "LEAVE_FAILED";
|
|
404
|
+
fail(code, leaveRes.error ?? `HTTP ${leaveRes.status}`);
|
|
405
|
+
}
|
|
406
|
+
process.stdout.write(formatLeaveChannelResult(opts.target) + "\n");
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// src/commands/thread/unfollow.ts
|
|
411
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
412
|
+
var SHORT_ID_RE = /^[0-9a-f]{8}$/i;
|
|
413
|
+
function parseThreadTarget(target) {
|
|
414
|
+
const trimmed = target.trim();
|
|
415
|
+
if (UUID_RE.test(trimmed)) return trimmed;
|
|
416
|
+
if (trimmed.startsWith("#")) {
|
|
417
|
+
const rest = trimmed.slice(1);
|
|
418
|
+
const lastColon = rest.lastIndexOf(":");
|
|
419
|
+
if (lastColon > 0 && SHORT_ID_RE.test(rest.slice(lastColon + 1))) {
|
|
420
|
+
return trimmed;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
if (trimmed.startsWith("dm:@") || trimmed.startsWith("DM:@")) {
|
|
424
|
+
const rest = trimmed.slice(4);
|
|
425
|
+
const lastColon = rest.lastIndexOf(":");
|
|
426
|
+
if (lastColon > 0 && SHORT_ID_RE.test(rest.slice(lastColon + 1))) {
|
|
427
|
+
return trimmed;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
return null;
|
|
431
|
+
}
|
|
432
|
+
function formatUnfollowThreadResult(target) {
|
|
433
|
+
return `Unfollowed ${target}. You can still inspect the thread when its parent conversation is visible, but you will no longer receive ordinary thread delivery unless you follow it again or are mentioned.`;
|
|
434
|
+
}
|
|
435
|
+
function registerThreadUnfollowCommand(parent) {
|
|
436
|
+
parent.command("unfollow").description("Stop following a thread you no longer need ordinary delivery for").requiredOption("--target <target>", "Thread target, e.g. '#engineering:abcd1234' or 'dm:@alice:abcd1234'").action(async (opts) => {
|
|
437
|
+
const thread = parseThreadTarget(opts.target);
|
|
438
|
+
if (!thread) {
|
|
439
|
+
fail("INVALID_TARGET", "Thread must be a thread target like '#channel:abcd1234', 'dm:@peer:abcd1234', or a thread channel UUID.");
|
|
440
|
+
}
|
|
441
|
+
let ctx;
|
|
442
|
+
try {
|
|
443
|
+
ctx = loadAgentContext();
|
|
444
|
+
} catch (err) {
|
|
445
|
+
if (err instanceof AgentBootstrapError) fail(err.code, err.message);
|
|
446
|
+
throw err;
|
|
447
|
+
}
|
|
448
|
+
const client = new ApiClient(ctx);
|
|
449
|
+
const res = await client.request(
|
|
450
|
+
"POST",
|
|
451
|
+
`/internal/agent/${encodeURIComponent(ctx.agentId)}/threads/unfollow`,
|
|
452
|
+
{ thread }
|
|
453
|
+
);
|
|
454
|
+
if (!res.ok) {
|
|
455
|
+
const code = res.status >= 500 ? "SERVER_5XX" : "UNFOLLOW_FAILED";
|
|
456
|
+
fail(code, res.error ?? `HTTP ${res.status}`);
|
|
457
|
+
}
|
|
458
|
+
process.stdout.write(formatUnfollowThreadResult(thread) + "\n");
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
|
|
298
462
|
// src/commands/message/_format.ts
|
|
299
463
|
function toLocalTime(iso) {
|
|
300
464
|
const d = new Date(iso);
|
|
@@ -1184,6 +1348,11 @@ program.name("slock").description(
|
|
|
1184
1348
|
).version("0.0.1");
|
|
1185
1349
|
var authCmd = program.command("auth").description("Auth introspection");
|
|
1186
1350
|
registerWhoamiCommand(authCmd);
|
|
1351
|
+
var channelCmd = program.command("channel").description("Channel membership operations");
|
|
1352
|
+
registerChannelMembersCommand(channelCmd);
|
|
1353
|
+
registerChannelLeaveCommand(channelCmd);
|
|
1354
|
+
var threadCmd = program.command("thread").description("Thread attention operations");
|
|
1355
|
+
registerThreadUnfollowCommand(threadCmd);
|
|
1187
1356
|
var serverCmd = program.command("server").description("Server / workspace introspection");
|
|
1188
1357
|
registerServerInfoCommand(serverCmd);
|
|
1189
1358
|
var messageCmd = program.command("message").description("Message operations");
|
package/dist/core.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
DAEMON_CLI_USAGE,
|
|
4
4
|
DaemonCore,
|
|
5
5
|
parseDaemonCliArgs
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-JAB3HALZ.js";
|
|
7
7
|
import "./chunk-JG7ONJZ6.js";
|
|
8
8
|
|
|
9
9
|
// src/index.ts
|
|
@@ -13,7 +13,12 @@ if (!parsedArgs) {
|
|
|
13
13
|
process.exit(1);
|
|
14
14
|
}
|
|
15
15
|
var daemon = new DaemonCore(parsedArgs);
|
|
16
|
-
|
|
16
|
+
try {
|
|
17
|
+
daemon.start();
|
|
18
|
+
} catch (err) {
|
|
19
|
+
console.error(err instanceof Error ? err.message : err);
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
17
22
|
var shutdown = async () => {
|
|
18
23
|
await daemon.stop();
|
|
19
24
|
process.exit(0);
|