From a8f3a9ce6712936ce00797458008050bc78efaa5 Mon Sep 17 00:00:00 2001 From: Nathan Flurry Date: Sun, 9 Nov 2025 17:05:32 -0800 Subject: [PATCH] chore(rivetkit): add listing actors by only name to manager api --- .../cloudflare-workers/src/manager-driver.ts | 9 +++++ .../packages/rivetkit/scripts/dump-openapi.ts | 1 + .../rivetkit/src/driver-helpers/mod.ts | 1 + .../test-inline-client-driver.ts | 4 ++ .../src/drivers/file-system/manager.ts | 26 +++++++++++++ .../packages/rivetkit/src/manager/driver.ts | 9 +++++ .../packages/rivetkit/src/manager/router.ts | 37 +++++++++++-------- .../remote-manager-driver/api-endpoints.ts | 14 ++++++- .../rivetkit/src/remote-manager-driver/mod.ts | 20 ++++++++++ 9 files changed, 105 insertions(+), 16 deletions(-) diff --git a/rivetkit-typescript/packages/cloudflare-workers/src/manager-driver.ts b/rivetkit-typescript/packages/cloudflare-workers/src/manager-driver.ts index 43013b8825..cfdf78ebbe 100644 --- a/rivetkit-typescript/packages/cloudflare-workers/src/manager-driver.ts +++ b/rivetkit-typescript/packages/cloudflare-workers/src/manager-driver.ts @@ -7,6 +7,7 @@ import { type GetOrCreateWithKeyInput, type GetWithKeyInput, generateRandomString, + type ListActorsInput, type ManagerDisplayInformation, type ManagerDriver, WS_PROTOCOL_ACTOR, @@ -348,6 +349,14 @@ export class CloudflareActorsManagerDriver implements ManagerDriver { }; } + async listActors({ c, name }: ListActorsInput): Promise { + logger().warn({ + msg: "listActors not fully implemented for Cloudflare Workers", + name, + }); + return []; + } + // Helper method to build actor output from an ID async #buildActorOutput( c: any, diff --git a/rivetkit-typescript/packages/rivetkit/scripts/dump-openapi.ts b/rivetkit-typescript/packages/rivetkit/scripts/dump-openapi.ts index fa31da47e7..e60a09c436 100644 --- a/rivetkit-typescript/packages/rivetkit/scripts/dump-openapi.ts +++ b/rivetkit-typescript/packages/rivetkit/scripts/dump-openapi.ts @@ -32,6 +32,7 @@ function main() { getWithKey: unimplemented, getOrCreateWithKey: unimplemented, createActor: unimplemented, + listActors: unimplemented, sendRequest: unimplemented, openWebSocket: unimplemented, proxyRequest: unimplemented, diff --git a/rivetkit-typescript/packages/rivetkit/src/driver-helpers/mod.ts b/rivetkit-typescript/packages/rivetkit/src/driver-helpers/mod.ts index 7b36b9bc4f..d6ad658acc 100644 --- a/rivetkit-typescript/packages/rivetkit/src/driver-helpers/mod.ts +++ b/rivetkit-typescript/packages/rivetkit/src/driver-helpers/mod.ts @@ -24,6 +24,7 @@ export type { GetForIdInput, GetOrCreateWithKeyInput, GetWithKeyInput, + ListActorsInput, ManagerDisplayInformation, ManagerDriver, } from "@/manager/driver"; diff --git a/rivetkit-typescript/packages/rivetkit/src/driver-test-suite/test-inline-client-driver.ts b/rivetkit-typescript/packages/rivetkit/src/driver-test-suite/test-inline-client-driver.ts index 076b47b3eb..f25376b2f3 100644 --- a/rivetkit-typescript/packages/rivetkit/src/driver-test-suite/test-inline-client-driver.ts +++ b/rivetkit-typescript/packages/rivetkit/src/driver-test-suite/test-inline-client-driver.ts @@ -25,6 +25,7 @@ import { type GetOrCreateWithKeyInput, type GetWithKeyInput, HEADER_ACTOR_ID, + type ListActorsInput, type ManagerDisplayInformation, type ManagerDriver, } from "@/driver-helpers/mod"; @@ -73,6 +74,9 @@ export function createTestInlineClientDriver( input, ]); }, + listActors(input: ListActorsInput): Promise { + return makeInlineRequest(endpoint, encoding, "listActors", [input]); + }, async sendRequest( actorId: string, actorRequest: Request, diff --git a/rivetkit-typescript/packages/rivetkit/src/drivers/file-system/manager.ts b/rivetkit-typescript/packages/rivetkit/src/drivers/file-system/manager.ts index 07267714b5..d080642900 100644 --- a/rivetkit-typescript/packages/rivetkit/src/drivers/file-system/manager.ts +++ b/rivetkit-typescript/packages/rivetkit/src/drivers/file-system/manager.ts @@ -17,6 +17,7 @@ import type { GetForIdInput, GetOrCreateWithKeyInput, GetWithKeyInput, + ListActorsInput, ManagerDriver, } from "@/driver-helpers/mod"; import { ManagerInspector } from "@/inspector/manager"; @@ -333,6 +334,31 @@ export class FileSystemManagerDriver implements ManagerDriver { }; } + async listActors({ name }: ListActorsInput): Promise { + const actors: ActorOutput[] = []; + const itr = this.#state.getActorsIterator({}); + + for await (const actor of itr) { + if (actor.name === name) { + actors.push({ + actorId: actor.actorId, + name: actor.name, + key: actor.key as string[], + createTs: Number(actor.createdAt), + }); + } + } + + // Sort by create ts desc (most recent first) + actors.sort((a, b) => { + const aTs = a.createTs ?? 0; + const bTs = b.createTs ?? 0; + return bTs - aTs; + }); + + return actors; + } + displayInformation(): ManagerDisplayInformation { return { name: this.#state.persist ? "File System" : "Memory", diff --git a/rivetkit-typescript/packages/rivetkit/src/manager/driver.ts b/rivetkit-typescript/packages/rivetkit/src/manager/driver.ts index fe70445e96..22dcabd1ed 100644 --- a/rivetkit-typescript/packages/rivetkit/src/manager/driver.ts +++ b/rivetkit-typescript/packages/rivetkit/src/manager/driver.ts @@ -14,6 +14,7 @@ export interface ManagerDriver { getWithKey(input: GetWithKeyInput): Promise; getOrCreateWithKey(input: GetOrCreateWithKeyInput): Promise; createActor(input: CreateInput): Promise; + listActors(input: ListActorsInput): Promise; sendRequest(actorId: string, actorRequest: Request): Promise; openWebSocket( @@ -92,8 +93,16 @@ export interface CreateInput { region?: string; } +export interface ListActorsInput { + c?: HonoContext | undefined; + name: string; + key?: string; + includeDestroyed?: boolean; +} + export interface ActorOutput { actorId: string; name: string; key: ActorKey; + createTs?: number; } diff --git a/rivetkit-typescript/packages/rivetkit/src/manager/router.ts b/rivetkit-typescript/packages/rivetkit/src/manager/router.ts index 6d1613697a..b54523335e 100644 --- a/rivetkit-typescript/packages/rivetkit/src/manager/router.ts +++ b/rivetkit-typescript/packages/rivetkit/src/manager/router.ts @@ -289,17 +289,7 @@ function addManagerRoutes( if (key && !name) { return c.json( { - error: "When providing 'key', 'name' must also be provided.", - }, - 400, - ); - } - - // Validate: must provide either actor_ids or (name + key) - if (!actorIdsParsed && !key) { - return c.json( - { - error: "Must provide either 'actor_ids' or both 'name' and 'key'.", + error: "Name is required when key is provided.", }, 400, ); @@ -351,16 +341,33 @@ function addManagerRoutes( } } } - } else if (key) { - // At this point, name is guaranteed to be defined due to validation above + } else if (key && name) { const actorOutput = await managerDriver.getWithKey({ c, - name: name!, + name, key: [key], // Convert string to ActorKey array }); if (actorOutput) { actors.push(actorOutput); } + } else { + if (!name) { + return c.json( + { + error: "Name is required when not using actor_ids.", + }, + 400, + ); + } + + // List all actors with the given name + const actorOutputs = await managerDriver.listActors({ + c, + name, + key, + includeDestroyed: false, + }); + actors.push(...actorOutputs); } return c.json({ @@ -722,7 +729,7 @@ function createApiActor( key: serializeActorKey(actor.key), namespace_id: "default", // Assert default namespace runner_name_selector: runnerName, - create_ts: Date.now(), + create_ts: actor.createTs ?? Date.now(), connectable_ts: null, destroy_ts: null, sleep_ts: null, diff --git a/rivetkit-typescript/packages/rivetkit/src/remote-manager-driver/api-endpoints.ts b/rivetkit-typescript/packages/rivetkit/src/remote-manager-driver/api-endpoints.ts index e23334c3c0..186b802f64 100644 --- a/rivetkit-typescript/packages/rivetkit/src/remote-manager-driver/api-endpoints.ts +++ b/rivetkit-typescript/packages/rivetkit/src/remote-manager-driver/api-endpoints.ts @@ -25,7 +25,7 @@ export async function getActor( ); } -// MARK: Get actor by id +// MARK: Get actor by key export async function getActorByKey( config: ClientConfig, name: string, @@ -39,6 +39,18 @@ export async function getActorByKey( ); } +// MARK: List actors by name +export async function listActorsByName( + config: ClientConfig, + name: string, +): Promise { + return apiCall( + config, + "GET", + `/actors?name=${encodeURIComponent(name)}`, + ); +} + // MARK: Get or create actor by id export async function getOrCreateActor( config: ClientConfig, diff --git a/rivetkit-typescript/packages/rivetkit/src/remote-manager-driver/mod.ts b/rivetkit-typescript/packages/rivetkit/src/remote-manager-driver/mod.ts index 62e734dada..e9eecef6e3 100644 --- a/rivetkit-typescript/packages/rivetkit/src/remote-manager-driver/mod.ts +++ b/rivetkit-typescript/packages/rivetkit/src/remote-manager-driver/mod.ts @@ -12,6 +12,7 @@ import type { GetForIdInput, GetOrCreateWithKeyInput, GetWithKeyInput, + ListActorsInput, ManagerDisplayInformation, ManagerDriver, } from "@/driver-helpers/mod"; @@ -30,6 +31,7 @@ import { getActorByKey, getMetadata, getOrCreateActor, + listActorsByName, } from "./api-endpoints"; import { EngineApiError, getEndpoint } from "./api-utils"; import { logger } from "./log"; @@ -255,6 +257,24 @@ export class RemoteManagerDriver implements ManagerDriver { }; } + async listActors({ c, name }: ListActorsInput): Promise { + // Wait for metadata check to complete if in progress + if (this.#metadataPromise) { + await this.#metadataPromise; + } + + logger().debug({ msg: "listing actors via engine api", name }); + + const response = await listActorsByName(this.#config, name); + + return response.actors.map((actor) => ({ + actorId: actor.actor_id, + name: actor.name, + key: deserializeActorKey(actor.key), + createTs: actor.create_ts, + })); + } + async destroyActor(actorId: string): Promise { // Wait for metadata check to complete if in progress if (this.#metadataPromise) {