Skip to content

Commit 77afda6

Browse files
Add Vercel project support and Blob-based span export (#43)
* Refactor AgentPond CLI provider and environment handling * Clarify Vercel instrumentation prompt * Remove stale Vercel prompt assertion * Delegate Vercel Blob OIDC token resolution to the SDK
1 parent ca23344 commit 77afda6

43 files changed

Lines changed: 1937 additions & 370 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.changeset/bright-vercel-setup.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
"agentpond": minor
3+
"@agentpond/core": minor
4+
"@agentpond/firebase": minor
5+
"@agentpond/vercel": minor
6+
---
7+
8+
Add automatic Vercel setup with target-aware direct span export to private Blob storage, SDK-managed runtime OIDC refresh, and provider-aware environment selection.

AGENTS.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,14 @@ This repository uses Changesets to generate package changelogs.
1515
- Keep `skills/agentpond/SKILL.md` and `skills/agentpond/references/cli.md` in sync with CLI behavior changes.
1616
- When a PR changes commands, flags, prompts, environment variables, or storage setup, update the skill in the same PR.
1717

18+
## Provider, Project, and Environment Context
19+
20+
- A provider is the static platform adapter registered by the CLI. It supplies platform metadata and opens a detected provider project from the current directory. Opening a project may succeed before the project is fully linked or configured.
21+
- A provider project is the project-bound handle returned by `provider.openProject()`. It owns the detected root and setup label, persistent environment selection, and environment resolution, so commands do not repeatedly pass `cwd` through unrelated provider methods.
22+
- An environment context is the resolved, per-command data-access state for one selected environment. It contains the project root, AgentPond configuration, local cache path, and lazy object-storage resolution. Resolving one may require provider-specific linking or configuration.
23+
- Keep the CLI provider registry, supported-platform parsing, and provider setup selection in `apps/cli/src/providers.ts`. Do not add provider-specific branches to `apps/cli/src/environment-context.ts`.
24+
- Use the provider registry to open a provider project for setup and for rejecting provider-managed projects before full resolution. Use the project handle for environment selection and use an environment context for commands that need resolved cache or storage state.
25+
1826
## Duplicate Code
1927

2028
- Use the `$dry-refactoring` skill when analyzing or fixing duplicate-code findings.
@@ -30,7 +38,7 @@ This repository uses Changesets to generate package changelogs.
3038
<!-- gitnexus:start -->
3139
# GitNexus — Code Intelligence
3240

33-
This project is indexed by GitNexus as **agentpond** (1244 symbols, 2914 relationships, 98 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely.
41+
This project is indexed by GitNexus as **agentpond** (1333 symbols, 3159 relationships, 106 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely.
3442

3543
> Index stale? Run `node .gitnexus/run.cjs analyze` from the project root — it auto-selects an available runner. No `.gitnexus/run.cjs` yet? `npx gitnexus analyze` (npm 11 crash → `npm i -g gitnexus`; #1939).
3644

CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<!-- gitnexus:start -->
22
# GitNexus — Code Intelligence
33

4-
This project is indexed by GitNexus as **agentpond** (1244 symbols, 2914 relationships, 98 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely.
4+
This project is indexed by GitNexus as **agentpond** (1333 symbols, 3159 relationships, 106 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely.
55

66
> Index stale? Run `node .gitnexus/run.cjs analyze` from the project root — it auto-selects an available runner. No `.gitnexus/run.cjs` yet? `npx gitnexus analyze` (npm 11 crash → `npm i -g gitnexus`; #1939).
77

README.md

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,30 +14,30 @@
1414
<a href="https://www.npmjs.com/package/agentpond"><img src="https://img.shields.io/node/v/agentpond.svg" alt="Node.js version"></a>
1515
</p>
1616

17-
AgentPond is a lightweight trace backend and CLI for AI applications. It keeps raw traces in object storage you control and syncs them into a local DuckDB cache for fast analysis by developers and coding agents. Use it with Firebase Storage, Amazon S3, Google Cloud Storage, Vercel Blob, or custom infrastructure. Firebase includes an automated one-command setup; other deployments use the manual setup path.
17+
AgentPond is a lightweight trace backend and CLI for AI applications. It keeps raw traces in object storage you control and syncs them into a local DuckDB cache for fast analysis by developers and coding agents. Use it with Firebase Storage, Amazon S3, Google Cloud Storage, Vercel Blob, or custom infrastructure. Firebase and Vercel include automated setup; other deployments use the manual setup path.
1818

1919
## How it works
2020

2121
![AgentPond data flow from agent traces through object storage and local CLI analysis](https://raw.githubusercontent.com/marcusschiesser/agentpond/main/docs/assets/agentpond-how-it-works.png)
2222

2323
Object storage is the durable source of truth. The local DuckDB database is a rebuildable analytical cache, so production traces stay in your infrastructure without requiring an always-on analytics database.
2424

25-
## Quick start: Firebase in one command
25+
## Getting started
2626

27-
You need Node.js 22 or newer and an initialized Firebase project with an active project selected through `firebase use`.
27+
Start with the [Manual deployment setup](./docs/getting-started/manual-setup.md) to choose a write path, configure object storage, instrument the application, and sync its traces into AgentPond.
2828

29-
From the Firebase project or any nested package, run:
29+
For Firebase and Vercel, AgentPond also provides automated quick starts:
30+
31+
- [Firebase quick start](./docs/getting-started/firebase.md)
32+
- [Vercel quick start](./docs/getting-started/vercel.md)
33+
34+
Both require Node.js 22 or newer. From the Firebase or Vercel project, run:
3035

3136
```sh
3237
npx agentpond init
3338
```
3439

35-
The command detects the Firebase project and installs two project skills:
36-
37-
- `agentpond-instrumentation` teaches a coding agent to add OpenInference tracing and export spans directly with `createFirebaseSpanExporter()`.
38-
- `agentpond` teaches it to sync and analyze the resulting traces.
39-
40-
After installation, copy the prompt printed by `npx agentpond init` into your coding agent. The agent will inspect the server-side application, propose the matching instrumentation, review Firebase Storage access, implement the change, and verify a real trace.
40+
The command detects the platform, installs AgentPond's instrumentation and analytics skills, and prints a prompt for your coding agent. The agent inspects the trusted Node.js application, proposes the provider-specific setup, implements it after confirmation, and verifies a real trace.
4141

4242
Once the application has emitted a trace:
4343

@@ -46,10 +46,6 @@ npx agentpond sync
4646
npx agentpond traces list --limit 10
4747
```
4848

49-
See [Firebase setup](./docs/getting-started/firebase.md) for prerequisites and troubleshooting.
50-
51-
> For Amazon S3, Google Cloud Storage, Vercel Blob, or custom infrastructure, see [Manual deployment setup](./docs/getting-started/manual-setup.md).
52-
5349
## Analyze traces
5450

5551
Use focused commands for individual traces and sessions:
@@ -84,6 +80,7 @@ AgentPond does not provide a web UI, hosted trace storage, prompt management, da
8480
## Documentation
8581

8682
- [Firebase setup](./docs/getting-started/firebase.md)
83+
- [Vercel setup](./docs/getting-started/vercel.md)
8784
- [Manual deployment setup](./docs/getting-started/manual-setup.md)
8885
- [CLI reference](./docs/cli.md)
8986
- [Deployment reference](./docs/deployment.md)

apps/cli/src/command-support.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export type CommandContext = {
2323

2424
export function addGlobalOptions(command: Command): Command {
2525
return command
26-
.option("--env <name>", "use a named AgentPond environment")
26+
.option("--env <name>", "use an environment for this command")
2727
.option("--json", "print machine-readable JSON output");
2828
}
2929

@@ -62,7 +62,7 @@ function logImplicitEnvironment(
6262
json: boolean,
6363
): void {
6464
if (json || options.env || !config.environment) return;
65-
console.error(`Using AgentPond environment: ${config.environment.name}`);
65+
console.error(`Using environment: ${config.environment.name}`);
6666
}
6767

6868
function usesRunningDevCache(config: AgentPondConfig): boolean {

apps/cli/src/commands/dev.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import type { FastifyInstance, FastifyLoggerOptions } from "fastify";
1111
import { CliError, parsePort } from "../cli-support.js";
1212
import { addGlobalOptions } from "../command-support.js";
1313
import { devSdkEnvironment } from "../dev-env.js";
14-
import { environmentContextForCommand } from "../environment-context.js";
14+
import { manualEnvironmentContextForCommand } from "../environment-context.js";
1515

1616
type DevOptions = {
1717
host?: string;
@@ -31,7 +31,8 @@ export function registerDevCommand(program: Command): void {
3131
export async function startDevServer(options: DevOptions): Promise<void> {
3232
const host = options.host ?? "127.0.0.1";
3333
const startPort = parsePort(options.port ?? "4318");
34-
const cwd = environmentContextForCommand({ envName: "dev" }).rootDir;
34+
const context = manualEnvironmentContextForCommand("dev", { envName: "dev" });
35+
const cwd = context.rootDir;
3536
const environment = initAgentPondEnvironment("dev", { cwd });
3637
selectAgentPondEnvironment(environment.name, { cwd });
3738
const devConfig = configFromEnv({

apps/cli/src/commands/env.ts

Lines changed: 45 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,11 @@ import {
1818
type EnvVar,
1919
filterEnvEntries,
2020
} from "../dev-env.js";
21-
import { environmentContextForCommand } from "../environment-context.js";
21+
import {
22+
environmentContextForCommand,
23+
manualEnvironmentContextForCommand,
24+
} from "../environment-context.js";
25+
import { providerForCommand } from "../providers.js";
2226

2327
export type SelectPrompt<T extends string> = (config: {
2428
message: string;
@@ -45,7 +49,7 @@ export function registerEnvCommand(
4549
const promptSelect = options.selectEnvironment ?? select<string>;
4650
const promptStore = options.selectStore ?? select<AgentPondInitStore>;
4751
const env = addGlobalOptions(
48-
program.command("env").description("manage local AgentPond environments"),
52+
program.command("env").description("select and manage environments"),
4953
);
5054

5155
addGlobalOptions(env.command("current"))
@@ -63,19 +67,21 @@ export function registerEnvCommand(
6367
});
6468

6569
addGlobalOptions(env.command("get <name>"))
66-
.description("print shell exports for an environment")
70+
.description("print shell exports for a manual environment")
6771
.option("--langfuse", "print only Langfuse-compatible SDK exports")
6872
.option("--otel", "print only OpenTelemetry SDK exports")
6973
.action((name: string, commandOptions: EnvOptions) => {
70-
const context = environmentContextForCommand({ envName: name });
74+
const context = manualEnvironmentContextForCommand("get", {
75+
envName: name,
76+
});
7177
printEnvironmentExports(name, commandOptions, context.rootDir);
7278
});
7379

7480
addGlobalOptions(env.command("list"))
75-
.description("list local environments")
81+
.description("list manual environments")
7682
.action((_commandOptions: EnvOptions, command: Command) => {
7783
const globalOptions = command.optsWithGlobals<GlobalOptions>();
78-
const context = environmentContextForCommand({
84+
const context = manualEnvironmentContextForCommand("list", {
7985
envName: globalOptions.env,
8086
});
8187
const cwd = context.rootDir;
@@ -89,16 +95,13 @@ export function registerEnvCommand(
8995
});
9096

9197
addGlobalOptions(env.command("init <name>"))
92-
.description("initialize an environment")
93-
.option("--store <store>", "object store: s3, gcs, vercel, or local")
98+
.description("initialize a manual environment")
99+
.option("--store <store>", "object store: s3, gcs, or local")
94100
.action(
95101
async (name: string, commandOptions: EnvOptions, command: Command) => {
96-
const context = environmentContextForCommand({ envName: name });
97-
if (context.kind !== "agentpond") {
98-
throw new CliError(
99-
`npx agentpond env init is not available for ${context.kind} projects; the project environment is detected automatically`,
100-
);
101-
}
102+
const context = manualEnvironmentContextForCommand("init", {
103+
envName: name,
104+
});
102105
const store =
103106
storeFromValue(commandOptions.store) ??
104107
(await promptForStore(promptStore));
@@ -126,36 +129,47 @@ export function registerEnvCommand(
126129
_commandOptions: EnvOptions,
127130
command: Command,
128131
) => {
129-
const context = environmentContextForCommand({ envName: name });
130-
const selectedName =
131-
name ??
132-
(await promptForEnvironmentName(promptSelect, context.rootDir));
133-
const environment = selectAgentPondEnvironment(selectedName, {
134-
cwd: context.rootDir,
135-
});
132+
const selected = await selectEnvironmentForCommand(name, promptSelect);
136133
return print(
137-
{ selected: environment.name },
134+
{ selected },
138135
Boolean(command.optsWithGlobals<GlobalOptions>().json),
139136
);
140137
},
141138
);
142139
}
143140

141+
async function selectEnvironmentForCommand(
142+
name: string | undefined,
143+
promptSelect: SelectEnvironmentPrompt,
144+
): Promise<string> {
145+
const providerContext = providerForCommand();
146+
if (providerContext) {
147+
if (!name) throw new CliError("Missing environment name");
148+
try {
149+
return await providerContext.project.selectEnvironment(name);
150+
} catch (error) {
151+
throw new CliError(
152+
error instanceof Error ? error.message : String(error),
153+
);
154+
}
155+
}
156+
157+
const context = environmentContextForCommand({ envName: name });
158+
const selectedName =
159+
name ?? (await promptForEnvironmentName(promptSelect, context.rootDir));
160+
return selectAgentPondEnvironment(selectedName, {
161+
cwd: context.rootDir,
162+
}).name;
163+
}
164+
144165
function storeFromValue(
145166
value: string | undefined,
146167
): AgentPondInitStore | undefined {
147168
if (value === undefined) return undefined;
148-
if (
149-
value === "s3" ||
150-
value === "gcs" ||
151-
value === "vercel" ||
152-
value === "local"
153-
) {
169+
if (value === "s3" || value === "gcs" || value === "local") {
154170
return value;
155171
}
156-
throw new CliError(
157-
`--store must be s3, gcs, vercel, or local, got "${value}"`,
158-
);
172+
throw new CliError(`--store must be s3, gcs, or local, got "${value}"`);
159173
}
160174

161175
async function promptForStore(
@@ -169,7 +183,6 @@ async function promptForStore(
169183
choices: [
170184
{ name: "AWS S3 (or compatible)", value: "s3" },
171185
{ name: "Google Cloud Storage (GCS)", value: "gcs" },
172-
{ name: "Vercel Blob", value: "vercel" },
173186
{ name: "Local filesystem", value: "local" },
174187
],
175188
});

0 commit comments

Comments
 (0)