Skip to content

feat: add actor proxy support for HTTP fallback #951

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 28 additions & 28 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,34 +25,34 @@ jobs:
steps:
- uses: actions/checkout@v4

# Setup Node.js
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '22.14'
# Note: We're not using the built-in cache here because we need to use corepack

- name: Setup Corepack
run: corepack enable

- id: yarn-cache-dir-path
name: Get yarn cache directory path
run: echo "dir=$(yarn config get cacheFolder)" >> $GITHUB_OUTPUT

- name: Cache dependencies
uses: actions/cache@v3
id: cache
with:
path: |
${{ steps.yarn-cache-dir-path.outputs.dir }}
.turbo
key: ${{ runner.os }}-deps-${{ hashFiles('**/yarn.lock') }}-${{ github.sha }}
restore-keys: |
${{ runner.os }}-deps-${{ hashFiles('**/yarn.lock') }}-
${{ runner.os }}-deps-

- name: Install dependencies
run: yarn install
# # Setup Node.js
# - name: Set up Node.js
# uses: actions/setup-node@v4
# with:
# node-version: '22.14'
# # Note: We're not using the built-in cache here because we need to use corepack
#
# - name: Setup Corepack
# run: corepack enable
#
# - id: yarn-cache-dir-path
# name: Get yarn cache directory path
# run: echo "dir=$(yarn config get cacheFolder)" >> $GITHUB_OUTPUT
#
# - name: Cache dependencies
# uses: actions/cache@v3
# id: cache
# with:
# path: |
# ${{ steps.yarn-cache-dir-path.outputs.dir }}
# .turbo
# key: ${{ runner.os }}-deps-${{ hashFiles('**/yarn.lock') }}-${{ github.sha }}
# restore-keys: |
# ${{ runner.os }}-deps-${{ hashFiles('**/yarn.lock') }}-
# ${{ runner.os }}-deps-
#
# - name: Install dependencies
# run: yarn install

# - name: Run actor-core tests
# # TODO: Add back
Expand Down
3 changes: 3 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ This ensures imports resolve correctly across different build environments and p
- Extend from `ActorError` base class
- Use `UserError` for client-safe errors
- Use `InternalError` for internal errors
- Don't try to fix type issues by casting to unknown or any. If you need to do this, then stop and ask me to manually intervene.
- Write log messages in lowercase
- Instead of returning raw HTTP responses with c.json, use or write an error in packages/actor-core/src/actor/errors.ts and throw that instead. The middleware will automatically serialize the response for you.

## Project Structure

Expand Down
2 changes: 1 addition & 1 deletion examples/chat-room/scripts/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ async function main() {

// connect to chat room - now accessed via property
// can still pass parameters like room
const chatRoom = await client.chatRoom.connect(room, {
const chatRoom = client.chatRoom.connect(room, {
params: { room },
});

Expand Down
2 changes: 1 addition & 1 deletion examples/chat-room/scripts/connect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ async function main() {
const client = createClient<App>(process.env.ENDPOINT ?? "http://localhost:6420");

// connect to chat room - now accessed via property
const chatRoom = await client.chatRoom.connect();
const chatRoom = client.chatRoom.connect();

// call action to get existing messages
const messages = await chatRoom.getHistory();
Expand Down
2 changes: 1 addition & 1 deletion examples/chat-room/tests/chat-room.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ test("chat room should handle messages", async (test) => {
const { client } = await setupTest(test, app);

// Connect to chat room
const chatRoom = await client.chatRoom.connect();
const chatRoom = client.chatRoom.connect();

// Initial history should be empty
const initialMessages = await chatRoom.getHistory();
Expand Down
2 changes: 1 addition & 1 deletion examples/counter/scripts/connect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type { App } from "../actors/app";
async function main() {
const client = createClient<App>(process.env.ENDPOINT ?? "http://localhost:6420");

const counter = await client.counter.connect()
const counter = client.counter.connect()

counter.on("newCount", (count: number) => console.log("Event:", count));

Expand Down
2 changes: 1 addition & 1 deletion examples/counter/tests/counter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { app } from "../actors/app";

test("it should count", async (test) => {
const { client } = await setupTest(test, app);
const counter = await client.counter.connect();
const counter = client.counter.connect();

// Test initial count
expect(await counter.getCount()).toBe(0);
Expand Down
2 changes: 1 addition & 1 deletion examples/linear-coding-agent/src/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ server.post('/api/webhook/linear', async (c) => {
// Create or get a coding agent instance with the issue ID as a key
// This ensures each issue gets its own actor instance
console.log(`[SERVER] Getting actor for issue: ${issueId}`);
const actorClient = await client.codingAgent.connect(issueId);
const actorClient = client.codingAgent.connect(issueId);

// Initialize the agent if needed
console.log(`[SERVER] Initializing actor for issue: ${issueId}`);
Expand Down
2 changes: 1 addition & 1 deletion examples/resend-streaks/tests/user.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ beforeEach(() => {

test("streak tracking with time zone signups", async (t) => {
const { client } = await setupTest(t, app);
const actor = await client.user.connect();
const actor = client.user.connect();

// Sign up with specific time zone
const signupResult = await actor.completeSignUp(
Expand Down
2 changes: 2 additions & 0 deletions packages/actor-core-cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"bundle-require": "^5.1.0",
"chokidar": "^4.0.3",
"esbuild": "^0.25.1",
"invariant": "^2.2.4",
"open": "^10.1.0",
"yoga-wasm-web": "0.3.3"
},
Expand All @@ -46,6 +47,7 @@
"@rivet-gg/api": "^24.6.2",
"@sentry/esbuild-plugin": "^3.2.0",
"@sentry/node": "^9.3.0",
"@types/invariant": "^2",
"@types/micromatch": "^4",
"@types/react": "^18.3",
"@types/semver": "^7.5.8",
Expand Down
3 changes: 2 additions & 1 deletion packages/actor-core-cli/src/cli.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { PACKAGE_JSON } from "./macros" with { type: "macro" };
import { create, deploy, dev, program } from "./mod";
import { create, deploy, dev, endpoint, program } from "./mod";

export default program
.name(PACKAGE_JSON.name)
Expand All @@ -8,4 +8,5 @@ export default program
.addCommand(deploy)
.addCommand(create)
.addCommand(dev)
.addCommand(endpoint)
.parse();
48 changes: 34 additions & 14 deletions packages/actor-core-cli/src/commands/deploy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,10 @@ export const deploy = new Command()

await workflow(
"Deploy actors to Rivet",
async function* (ctx) {
async function*(ctx) {
const { config, cli } = yield* ctx.task(
"Prepare",
async function* (ctx) {
async function*(ctx) {
const config = yield* validateConfigTask(ctx, cwd, appPath);

const cli = yield* ctx.task("Locale rivet-cli", async (ctx) => {
Expand Down Expand Up @@ -96,7 +96,7 @@ export const deploy = new Command()
);

const { accessToken, projectName, envName, endpoint } =
yield* ctx.task("Auth with Rivet", async function* (ctx) {
yield* ctx.task("Auth with Rivet", async function*(ctx) {
const { stdout } = await exec`${cli} metadata auth-status`;
const isLogged = stdout === "true";

Expand Down Expand Up @@ -164,7 +164,7 @@ export const deploy = new Command()
if (!opts.skipManager) {
manager = yield* ctx.task(
"Deploy ActorCore",
async function* (ctx) {
async function*(ctx) {
yield fs.mkdir(path.join(cwd, ".actorcore"), {
recursive: true,
});
Expand All @@ -185,7 +185,7 @@ export const deploy = new Command()
);

const output =
await exec`${cli} publish manager --env ${envName} --tags access=private ${entrypoint}`;
await exec`${cli} publish manager --env ${envName} --tags role=manager,framework=actor-core,framework-version=${VERSION} --unstable-minify false ${entrypoint}`;
if (output.exitCode !== 0) {
throw ctx.error("Failed to deploy ActorCore.", {
hint: "Check the logs above for more information.",
Expand Down Expand Up @@ -257,11 +257,32 @@ export const deploy = new Command()
environment: envName,
body: {
region: region.id,
tags: { name: "manager", owner: "rivet" },
buildTags: { name: "manager", current: "true" },
tags: {
name: "manager",
role: "manager",
framework: "actor-core",
},
buildTags: {
name: "manager",
role: "manager",
framework: "actor-core",
current: "true",
},
runtime: {
environment: {
RIVET_SERVICE_TOKEN: serviceToken,
...(process.env._RIVET_MANAGER_LOG_LEVEL
? {
_LOG_LEVEL:
process.env._RIVET_MANAGER_LOG_LEVEL,
}
: {}),
...(process.env._RIVET_ACTOR_LOG_LEVEL
? {
_ACTOR_LOG_LEVEL:
process.env._RIVET_ACTOR_LOG_LEVEL,
}
: {}),
},
},
network: {
Expand Down Expand Up @@ -290,10 +311,9 @@ export const deploy = new Command()
config.app.config.actors,
).entries()) {
yield* ctx.task(
`Deploy & upload "${actorName}" build (${idx + 1}/${
Object.keys(config.app.config.actors).length
`Deploy & upload "${actorName}" build (${idx + 1}/${Object.keys(config.app.config.actors).length
})`,
async function* (ctx) {
async function*(ctx) {
yield fs.mkdir(path.join(cwd, ".actorcore"), {
recursive: true,
});
Expand All @@ -317,18 +337,18 @@ export const deploy = new Command()
`,
);

const actorTags = {
access: "public",
const buildTags = {
role: "actor",
framework: "actor-core",
"framework-version": VERSION,
};

const tagsArray = Object.entries(actorTags)
const tagsArray = Object.entries(buildTags)
.map(([key, value]) => `${key}=${value}`)
.join(",");

const output =
await exec`${cli} publish --env=${envName} --tags=${tagsArray} ${actorName} ${entrypoint}`;
await exec`${cli} publish --env=${envName} --tags=${tagsArray} --unstable-minify false ${actorName} ${entrypoint}`;

if (output.exitCode !== 0) {
throw ctx.error("Failed to deploy & upload actors.", {
Expand Down
Loading
Loading