diff options
Diffstat (limited to 'packages/cli/src')
| -rw-r--r-- | packages/cli/src/args.test.ts | 29 | ||||
| -rw-r--r-- | packages/cli/src/args.ts | 23 | ||||
| -rw-r--r-- | packages/cli/src/http.test.ts | 23 | ||||
| -rw-r--r-- | packages/cli/src/http.ts | 10 | ||||
| -rw-r--r-- | packages/cli/src/main.ts | 9 | ||||
| -rw-r--r-- | packages/cli/src/render.test.ts | 1 |
6 files changed, 83 insertions, 12 deletions
diff --git a/packages/cli/src/args.test.ts b/packages/cli/src/args.test.ts index 62bcafd..992d09f 100644 --- a/packages/cli/src/args.test.ts +++ b/packages/cli/src/args.test.ts @@ -190,6 +190,7 @@ describe("parseArgs", () => { expect(parseArgs(["list"], { defaultServer })).toEqual({ kind: "list", server: "http://localhost:24203", + all: false, }); }); @@ -198,6 +199,7 @@ describe("parseArgs", () => { kind: "list", server: "http://localhost:24203", query: "abc12345", + all: false, }); }); @@ -206,9 +208,36 @@ describe("parseArgs", () => { kind: "list", server: "http://s", query: "abc", + all: false, }); }); + it("parses 'list' with --status", () => { + expect(parseArgs(["list", "--status", "closed"], { defaultServer })).toEqual({ + kind: "list", + server: "http://localhost:24203", + status: "closed", + all: false, + }); + }); + + it("parses 'list' with --all", () => { + expect(parseArgs(["list", "--all"], { defaultServer })).toEqual({ + kind: "list", + server: "http://localhost:24203", + all: true, + }); + }); + + it("parses 'list' with --status and --all ( --all takes precedence)", () => { + const result = parseArgs(["list", "--status", "active", "--all"], { defaultServer }); + expect(result.kind).toBe("list"); + if (result.kind === "list") { + expect(result.all).toBe(true); + expect(result.status).toBe("active"); + } + }); + it("errors on a second positional argument", () => { const result = parseArgs(["list", "abc", "def"], { defaultServer }); expect(result.kind).toBe("error"); diff --git a/packages/cli/src/args.ts b/packages/cli/src/args.ts index d4ed0e9..30fa309 100644 --- a/packages/cli/src/args.ts +++ b/packages/cli/src/args.ts @@ -27,7 +27,13 @@ export type ParsedCommand = readonly showReasoning: boolean; readonly open: boolean; } - | { readonly kind: "list"; readonly server: string; readonly query?: string } + | { + readonly kind: "list"; + readonly server: string; + readonly query?: string; + readonly status?: string; + readonly all: boolean; + } | { readonly kind: "open"; readonly server: string; readonly conversationId: string } | { readonly kind: "read"; readonly server: string; readonly conversationId: string } | { @@ -73,11 +79,18 @@ export function parseArgs(argv: readonly string[], opts: ParseOpts): ParsedComma if (first === "list") { let server = opts.defaultServer; let query: string | undefined; + let status: string | undefined; + let all = false; for (let i = 1; i < argv.length; i++) { const arg = argv[i] as string; if (arg === "--server") { if (i + 1 >= argv.length) return { kind: "error", message: "--server requires a value" }; server = argv[++i] as string; + } else if (arg === "--status") { + if (i + 1 >= argv.length) return { kind: "error", message: "--status requires a value" }; + status = argv[++i]; + } else if (arg === "--all") { + all = true; } else if (arg.startsWith("--")) { return { kind: "error", message: `Unknown flag: ${arg}` }; } else if (query !== undefined) { @@ -86,7 +99,13 @@ export function parseArgs(argv: readonly string[], opts: ParseOpts): ParsedComma query = arg; } } - return { kind: "list", server, ...(query !== undefined && { query }) }; + return { + kind: "list", + server, + ...(query !== undefined && { query }), + ...(status !== undefined && { status }), + all, + }; } if (first === "read") { diff --git a/packages/cli/src/http.test.ts b/packages/cli/src/http.test.ts index 013492e..3e7befe 100644 --- a/packages/cli/src/http.test.ts +++ b/packages/cli/src/http.test.ts @@ -251,7 +251,9 @@ describe("fetchConversations", () => { it("requests GET /conversations with no query when query omitted", async () => { let calledUrl: string | undefined; const list: ConversationListResponse = { - conversations: [{ id: "abcdef1234567890", title: "first", createdAt: 1, lastActivityAt: 2 }], + conversations: [ + { id: "abcdef1234567890", title: "first", createdAt: 1, lastActivityAt: 2, status: "idle" }, + ], }; const fakeFetch = (async (url: string | URL | Request): Promise<Response> => { calledUrl = String(url); @@ -277,7 +279,7 @@ describe("fetchConversations", () => { { fetchImpl: fakeFetch }, { server: "http://localhost:24203", query: "abc def" }, ); - expect(calledUrl).toBe("http://localhost:24203/conversations?q=abc%20def"); + expect(calledUrl).toBe("http://localhost:24203/conversations?q=abc+def"); }); it("throws on non-OK status", async () => { @@ -400,7 +402,13 @@ describe("resolveConversationId", () => { it("returns the full id on a single match", async () => { const fetchImpl = listFetch({ conversations: [ - { id: "abcdef1234567890abcdef1234567890", title: "only", createdAt: 1, lastActivityAt: 2 }, + { + id: "abcdef1234567890abcdef1234567890", + title: "only", + createdAt: 1, + lastActivityAt: 2, + status: "idle", + }, ], }); const result = await resolveConversationId( @@ -426,12 +434,19 @@ describe("resolveConversationId", () => { it("returns an error object listing matches on multiple matches", async () => { const fetchImpl = listFetch({ conversations: [ - { id: "abcdef1234567890aaaaaaaaaaaaaaaa", title: "first", createdAt: 1, lastActivityAt: 2 }, + { + id: "abcdef1234567890aaaaaaaaaaaaaaaa", + title: "first", + createdAt: 1, + lastActivityAt: 2, + status: "idle", + }, { id: "abcdef1234567890bbbbbbbbbbbbbbbb", title: "second", createdAt: 1, lastActivityAt: 3, + status: "idle", }, ], }); diff --git a/packages/cli/src/http.ts b/packages/cli/src/http.ts index 8434519..876f570 100644 --- a/packages/cli/src/http.ts +++ b/packages/cli/src/http.ts @@ -96,16 +96,18 @@ export async function fetchModels(deps: FetchDeps, opts: FetchModelsOpts): Promi interface FetchConversationsOpts { readonly server: string; readonly query?: string; + readonly status?: string; } export async function fetchConversations( deps: FetchDeps, opts: FetchConversationsOpts, ): Promise<ConversationListResponse> { - const url = - opts.query !== undefined - ? `${opts.server}/conversations?q=${encodeURIComponent(opts.query)}` - : `${opts.server}/conversations`; + const params = new URLSearchParams(); + if (opts.query !== undefined) params.set("q", opts.query); + if (opts.status !== undefined) params.set("status", opts.status); + const qs = params.toString(); + const url = qs.length > 0 ? `${opts.server}/conversations?${qs}` : `${opts.server}/conversations`; const res = await deps.fetchImpl(url); if (!res.ok) { diff --git a/packages/cli/src/main.ts b/packages/cli/src/main.ts index 6df6e23..e709f21 100644 --- a/packages/cli/src/main.ts +++ b/packages/cli/src/main.ts @@ -22,7 +22,7 @@ import { extractLastText, formatConversationList, renderEvent } from "./render.j const USAGE = `Usage: dispatch models [--server <url>] - dispatch list [<prefix>] [--server <url>] + dispatch list [<prefix>] [--status <active|idle|closed>] [--all] [--server <url>] dispatch read <conversationId> [--server <url>] dispatch open <conversationId> [--server <url>] dispatch send <conversationId> --text "..." [--queue] [--open] [--cwd <dir>] [--effort <level>] [--server <url>] @@ -50,9 +50,14 @@ async function main(): Promise<void> { break; } case "list": { + const status = parsed.all ? undefined : (parsed.status ?? "active,idle"); const result = await fetchConversations( { fetchImpl: globalThis.fetch }, - { server: parsed.server, ...(parsed.query !== undefined && { query: parsed.query }) }, + { + server: parsed.server, + ...(parsed.query !== undefined && { query: parsed.query }), + ...(status !== undefined && { status }), + }, ); const table = formatConversationList(result.conversations, Date.now()); if (table.length > 0) process.stdout.write(`${table}\n`); diff --git a/packages/cli/src/render.test.ts b/packages/cli/src/render.test.ts index 849d33a..eb89300 100644 --- a/packages/cli/src/render.test.ts +++ b/packages/cli/src/render.test.ts @@ -229,6 +229,7 @@ describe("formatConversationList", () => { title, createdAt: now - ageMs - 1000, lastActivityAt: now - ageMs, + status: "idle", }); it("returns empty string for an empty list", () => { |
