Skip to content
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
21 changes: 18 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,33 @@ It's the difference between "I built on Resend and rebuilt my own thread/label/d
npm install @agentmail/convex
```

Wire it into your Convex app:
Requires Convex 1.39.1 or later. Wire it into your Convex app:

```ts
// convex/convex.config.ts
import { defineApp } from "convex/server";
import { v } from "convex/values";
import agentmail from "@agentmail/convex/convex.config";

const app = defineApp();
app.use(agentmail);
const app = defineApp({
env: {
AGENTMAIL_API_KEY: v.string(),
AGENTMAIL_BASE_URL: v.optional(v.string()),
},
});
app.use(agentmail, {
env: {
AGENTMAIL_API_KEY: app.env.AGENTMAIL_API_KEY,
AGENTMAIL_BASE_URL: app.env.AGENTMAIL_BASE_URL,
},
});
export default app;
```

Components do not inherit the app's environment variables automatically. The
bindings above pass them without using function arguments. The webhook secret
is read by the app-side client and does not need a component binding.

Set credentials on your Convex deployment (kept out of mutation args so they don't appear in function logs):

```bash
Expand Down
15 changes: 13 additions & 2 deletions example/convex/convex.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
import { defineApp } from "convex/server";
import { v } from "convex/values";
import agentmail from "@agentmail/convex/convex.config";

const app = defineApp();
app.use(agentmail);
const app = defineApp({
env: {
AGENTMAIL_API_KEY: v.string(),
AGENTMAIL_BASE_URL: v.optional(v.string()),
},
});
app.use(agentmail, {
env: {
AGENTMAIL_API_KEY: app.env.AGENTMAIL_API_KEY,
AGENTMAIL_BASE_URL: app.env.AGENTMAIL_BASE_URL,
},
});

export default app;
2 changes: 1 addition & 1 deletion example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
},
"dependencies": {
"@agentmail/convex": "*",
"convex": "^1.35.0"
"convex": "^1.45.0"
},
"devDependencies": {
"typescript": "~5.9.0"
Expand Down
20 changes: 10 additions & 10 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,14 @@
}
},
"peerDependencies": {
"convex": "^1.24.8",
"convex": "^1.39.1",
"convex-helpers": "^0.1.106"
},
"devDependencies": {
"@edge-runtime/vm": "^5.0.0",
"@types/node": "^24.0.0",
"chokidar-cli": "^3.0.0",
"convex": "^1.35.0",
"convex": "^1.45.0",
"convex-helpers": "^0.1.106",
"convex-test": "^0.0.51",
"tsx": "^4.21.0",
Expand Down
21 changes: 21 additions & 0 deletions src/component/_generated/server.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,19 @@ import {
} from "convex/server";
import type { DataModel } from "./dataModel.js";

/**
* Typesafe environment variables.
*
* This includes platform-provided env vars and any variables declared in
* `convex.config.ts`.
*/
type Env = {
readonly CONVEX_CLOUD_URL: string;
readonly CONVEX_SITE_URL: string;
readonly AGENTMAIL_API_KEY: string;
readonly AGENTMAIL_BASE_URL: string | undefined;
};

/**
* Define a query in this Convex app's public API.
*
Expand Down Expand Up @@ -95,6 +108,14 @@ export declare const internalAction: ActionBuilder<DataModel, "internal">;
*/
export declare const httpAction: HttpActionBuilder;

/**
* Typesafe environment variables.
*
* This includes platform-provided env vars and any variables declared in
* `convex.config.ts`.
*/
export declare const env: Env;

/**
* A set of services for use within Convex query functions.
*
Expand Down
8 changes: 8 additions & 0 deletions src/component/_generated/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,11 @@ export const internalAction = internalActionGeneric;
* @returns The wrapped function. Import this function from `convex/http.js` and route it to hook it up.
*/
export const httpAction = httpActionGeneric;

/**
* Typesafe environment variables.
*
* This includes platform-provided env vars and any variables declared in
* `convex.config.ts`.
*/
export const env = process.env;
8 changes: 7 additions & 1 deletion src/component/convex.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { defineComponent } from "convex/server";
import { v } from "convex/values";
import workpool from "@convex-dev/workpool/convex.config";

const component = defineComponent("agentmail");
const component = defineComponent("agentmail", {
env: {
AGENTMAIL_API_KEY: v.string(),
AGENTMAIL_BASE_URL: v.optional(v.string()),
},
});
component.use(workpool, { name: "sendPool" });
component.use(workpool, { name: "callbackPool" });

Expand Down
2 changes: 1 addition & 1 deletion src/component/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export type AgentMailEvent = Infer<typeof vEvent>;

// Non-sensitive per-instance tuning params + user callback handles. API
// credentials are deliberately *not* in here: they're read from
// process.env on the component side so they don't appear in Convex
// typed env on the component side so they don't appear in Convex
// function logs (which record every mutation's args).
export const vRuntimeConfig = v.object({
retryAttempts: v.number(),
Expand Down
6 changes: 4 additions & 2 deletions src/component/utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { env } from "./_generated/server.js";

const PERMANENT_STATUSES = new Set([
400, 401, 404, 405, 410, 413, 414, 415, 422,
]);
Expand Down Expand Up @@ -40,14 +42,14 @@ export async function agentmailFetch(
path: string,
opts: FetchOptions,
): Promise<unknown> {
const apiKey = process.env.AGENTMAIL_API_KEY;
const apiKey = env.AGENTMAIL_API_KEY;
if (!apiKey) {
throw new Error(
"AGENTMAIL_API_KEY is not set on this Convex deployment. Run " +
"`npx convex env set AGENTMAIL_API_KEY <key>`.",
);
}
const baseUrl = (process.env.AGENTMAIL_BASE_URL ?? DEFAULT_BASE_URL).replace(
const baseUrl = (env.AGENTMAIL_BASE_URL ?? DEFAULT_BASE_URL).replace(
/\/$/,
"",
);
Expand Down