feat: add chat and website updates

This commit is contained in:
Mohamed Boudra
2026-03-29 12:49:05 +07:00
parent 147482d6ed
commit 094864d779
26 changed files with 677 additions and 126 deletions
+5 -1
View File
@@ -1,7 +1,9 @@
import type { Command } from "commander";
import type { SingleResult } from "../../output/index.js";
import {
attachAgentNamesToMessages,
connectChatClient,
resolveChatAuthorAgentId,
toChatCommandError,
type ChatCommandOptions,
} from "./shared.js";
@@ -22,11 +24,13 @@ export async function runPostCommand(
const payload = await client.postChatMessage({
room,
body,
authorAgentId: resolveChatAuthorAgentId(),
replyToMessageId: options.replyTo,
});
const [message] = await attachAgentNamesToMessages(client, [toChatMessageRow(payload.message!)]);
return {
type: "single",
data: toChatMessageRow(payload.message!),
data: message!,
schema: chatMessageSchema,
};
} catch (err) {
+6 -1
View File
@@ -1,6 +1,7 @@
import type { Command } from "commander";
import type { ListResult } from "../../output/index.js";
import {
attachAgentNamesToMessages,
connectChatClient,
parseSinceValue,
toChatCommandError,
@@ -42,9 +43,13 @@ export async function runReadCommand(
since: parseSinceValue(options.since),
authorAgentId: options.agent,
});
const messages = await attachAgentNamesToMessages(
client,
payload.messages.map(toChatMessageRow),
);
return {
type: "list",
data: payload.messages.map(toChatMessageRow),
data: messages,
schema: chatMessageSchema,
};
} catch (err) {
+54 -3
View File
@@ -1,4 +1,5 @@
import type { OutputSchema } from "../../output/index.js";
import type { AnyCommandResult, OutputOptions } from "../../output/index.js";
export interface ChatRoomRow {
name: string;
@@ -11,9 +12,11 @@ export interface ChatRoomRow {
export interface ChatMessageRow {
id: string;
author: string;
authorName: string | null;
createdAt: string;
replyTo: string;
mentions: string;
mentionAgentIds: string[];
mentionLabels: string[];
body: string;
}
@@ -33,13 +36,59 @@ export const chatMessageSchema: OutputSchema<ChatMessageRow> = {
columns: [
{ header: "ID", field: "id", width: 36 },
{ header: "AUTHOR", field: "author", width: 16 },
{ header: "AUTHOR NAME", field: (row) => row.authorName ?? "-", width: 20 },
{ header: "CREATED", field: "createdAt", width: 24 },
{ header: "REPLY TO", field: "replyTo", width: 36 },
{ header: "MENTIONS", field: "mentions", width: 24 },
{ header: "MENTIONS", field: (row) => row.mentionLabels.join(", ") || "-", width: 24 },
{ header: "MESSAGE", field: "body", width: 60 },
],
renderHuman: renderChatTranscript,
};
function renderChatTranscript(
result: AnyCommandResult<ChatMessageRow>,
_options: OutputOptions,
): string {
const data = result.type === "list" ? result.data : [result.data];
if (data.length === 0) {
return "";
}
return data.map(renderChatMessageBlock).join("\n\n");
}
function renderChatMessageBlock(message: ChatMessageRow): string {
const authorLabel = message.authorName
? `${message.authorName} (${message.author})`
: message.author;
const lines = [`┌─ ${authorLabel} ── ${formatTimestamp(message.createdAt)} ── [msg ${message.id}]`];
if (message.replyTo !== "-") {
lines.push(`│ reply-to: msg ${message.replyTo}`);
}
if (message.mentionLabels.length > 0) {
lines.push(`│ mentions: ${message.mentionLabels.join(", ")}`);
}
lines.push("│");
const bodyLines = message.body.split(/\r?\n/);
for (const line of bodyLines) {
lines.push(`│ ${line}`);
}
lines.push("│");
lines.push("└─");
return lines.join("\n");
}
function formatTimestamp(input: string): string {
const date = new Date(input);
if (Number.isNaN(date.getTime())) {
return input;
}
return date.toISOString().replace("T", " ").replace(".000Z", "Z");
}
export function toChatRoomRow(room: {
id: string;
name: string;
@@ -67,9 +116,11 @@ export function toChatMessageRow(message: {
return {
id: message.id,
author: message.authorAgentId,
authorName: null,
createdAt: message.createdAt,
replyTo: message.replyToMessageId ?? "-",
mentions: message.mentionAgentIds.length > 0 ? message.mentionAgentIds.join(",") : "-",
mentionAgentIds: message.mentionAgentIds,
mentionLabels: message.mentionAgentIds,
body: message.body,
};
}
+43
View File
@@ -1,6 +1,7 @@
import { connectToDaemon, getDaemonHost } from "../../utils/client.js";
import { parseDuration } from "../../utils/duration.js";
import type { CommandError, CommandOptions } from "../../output/index.js";
import type { ChatMessageRow } from "./schema.js";
export interface ChatCommandOptions extends CommandOptions {
host?: string;
@@ -22,6 +23,43 @@ export async function connectChatClient(host?: string) {
}
}
export async function attachAgentNamesToMessages(
client: Awaited<ReturnType<typeof connectToDaemon>>,
messages: ChatMessageRow[],
): Promise<ChatMessageRow[]> {
const agentIds = new Set<string>();
for (const message of messages) {
agentIds.add(message.author);
for (const mentionId of message.mentionAgentIds) {
agentIds.add(mentionId);
}
}
if (agentIds.size === 0) {
return messages;
}
const payload = await client.fetchAgents({
filter: { includeArchived: true },
});
const agentNames = new Map<string, string>();
for (const entry of payload.entries) {
const title = entry.agent.title?.trim();
if (title) {
agentNames.set(entry.agent.id, title);
}
}
return messages.map((message) => ({
...message,
authorName: agentNames.get(message.author) ?? null,
mentionLabels: message.mentionAgentIds.map((agentId) => {
const name = agentNames.get(agentId);
return name ? `${name} (${agentId})` : agentId;
}),
}));
}
export function toChatCommandError(code: string, action: string, err: unknown): CommandError {
if (err && typeof err === "object" && "code" in err && "message" in err) {
return err as CommandError;
@@ -82,3 +120,8 @@ export function parseTimeoutMs(input?: string): number | undefined {
throw error;
}
}
export function resolveChatAuthorAgentId(): string {
const agentId = process.env.PASEO_AGENT_ID?.trim();
return agentId && agentId.length > 0 ? agentId : "manual";
}
+6 -1
View File
@@ -1,6 +1,7 @@
import type { Command } from "commander";
import type { ListResult } from "../../output/index.js";
import {
attachAgentNamesToMessages,
connectChatClient,
parseTimeoutMs,
toChatCommandError,
@@ -29,9 +30,13 @@ export async function runWaitCommand(
afterMessageId,
timeoutMs: parseTimeoutMs(options.timeout),
});
const messages = await attachAgentNamesToMessages(
client,
payload.messages.map(toChatMessageRow),
);
return {
type: "list",
data: payload.messages.map(toChatMessageRow),
data: messages,
schema: chatMessageSchema,
};
} catch (err) {
+3
View File
@@ -40,6 +40,9 @@ export function render<T>(
return renderYaml(result, opts);
case "table":
default:
if (result.schema.renderHuman) {
return result.schema.renderHuman(result, opts);
}
return renderTable(result, opts);
}
}
+2
View File
@@ -40,6 +40,8 @@ export interface OutputSchema<T> {
idField: keyof T | ((item: T) => string);
/** Column definitions for table output */
columns: ColumnDef<T>[];
/** Optional: custom renderer for human/table output */
renderHuman?: (result: AnyCommandResult<T>, options: OutputOptions) => string;
/** Optional: transform data before JSON/YAML output */
serialize?: (data: T) => unknown;
}