History

cleanup

Rhys Sullivan committed 7 months ago

Commit 543ca871 parent 67590a29 files · +0 −258Browse files

Unified · Split

9 files
  1. .executor/executor.jsonc +0 −13
  2. .tmp-openapi-helper-probe.ts +0 −30
  3. AGENTS.md +0 −1
  4. CLAUDE.md +0 −7
  5. PLAN.md +0 −82
  6. TRACING.md +0 −43
  7. personal-notes/otel-tracing-for-source-connect.md +0 −50
  8. ralph.ts +0 −22
  9. skills-lock.json +0 −10
.executor/executor.jsonc+0−13
@@ -1,13 +0,0 @@
1{
2 "sources": {
3 "github-v3-rest-api": {
4 "name": "GitHub v3 REST API",
5 "kind": "openapi"
6 },
7 "axiom-mcp": {
8 "kind": "mcp",
9 "name": "Axiom MCP",
10 "namespace": "axiom.mcp"
11 }
12 }
13}
.tmp-openapi-helper-probe.ts+0−30
@@ -1,30 +0,0 @@
1import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiSchema } from '@effect/platform';
2import { startOpenApiTestServer } from '@executor/effect-test-utils';
3import * as Effect from 'effect/Effect';
4import * as Layer from 'effect/Layer';
5import * as Schema from 'effect/Schema';
6
7const ownerParam = HttpApiSchema.param('owner', Schema.String);
8const repoParam = HttpApiSchema.param('repo', Schema.String);
9class DemoReposApi extends HttpApiGroup.make('repos').add(
10 HttpApiEndpoint.get('getRepo')`/repos/${ownerParam}/${repoParam}`.addSuccess(
11 Schema.Struct({ full_name: Schema.String, private: Schema.Boolean }),
12 ),
13) {}
14class DemoApi extends HttpApi.make('demo').add(DemoReposApi) {}
15const live = HttpApiBuilder.group(DemoApi, 'repos', (handlers) =>
16 handlers.handle('getRepo', ({ path, request }) => {
17 console.log('auth', request.headers.authorization);
18 return Effect.succeed({ full_name: `${path.owner}/${path.repo}`, private: false });
19 }),
20);
21const main = async () => {
22 const server = await startOpenApiTestServer({ apiLayer: HttpApiBuilder.api(DemoApi).pipe(Layer.provide(live)) });
23 console.log('base', server.baseUrl, server.specUrl);
24 const spec = await fetch(server.specUrl);
25 console.log('spec', spec.status, await spec.text());
26 const resp = await fetch(`${server.baseUrl}/repos/vercel/ai`, { headers: { authorization: 'Bearer test' } });
27 console.log('repo', resp.status, await resp.text());
28 await server.close();
29};
30main().catch((e) => { console.error(e); process.exit(1); });
AGENTS.md+0−1
@@ -1 +0,0 @@
1CLAUDE.md
CLAUDE.md+0−7
@@ -1,7 +0,0 @@
1# Repo Agent Notes
2
3- When a test needs an OpenAPI spec, generate it from Effect `HttpApi` with `OpenApi.fromApi(...)`.
4- When a test needs to serve an Effect `HttpApi` plus `/openapi.json`, use `@executor/effect-test-utils` via `startOpenApiTestServer(...)` or `makeOpenApiTestServer(...)`.
5- Do not hand-author inline OpenAPI documents in normal tests.
6- Do not hand-roll `HttpApiBuilder.toWebHandler(...)` to Node HTTP bridges for OpenAPI test servers.
7- Raw OpenAPI parser/import-fidelity tests are the exception; those may keep literal OpenAPI documents where the point of the test is the raw document itself.
PLAN.md+0−82
@@ -1,82 +0,0 @@
1Executor:
2
3Project goal: Build an execution environment for agents to interact with APIs, MCPs, GraphQL, and other services via writing TypeScript.
4
5The way things work today is if people want to interact with services, they either add an mcp or they call it through a cli. for mcp servers when someone adds it to their agent, it blows up the context window of tokens. the agent isn't able to call it in an efficient way and it adds a lot of bloat so people have ended up using clis instead.
6
7the problem with the cli is people run things in dangerously allowable permissions. along with that you don't have typing and information about what clis are installed, what they can do, et cetera. by creating this typescript code execution environment we're able to handle both those problems. you can read the article code mode to understand deeper the benefits of this approach.
8
9the basic premise is that you give the agent the ability to search over what can be called from typescript and write code to call it. it can see the typings properly through typescript.
10
11let's look at some examples.
12
13let's say the agent wants to list the github issues for a repository. the agent call is executed through an mcp call and it parses typescript code from that. that typescript code will basically just be
14
15`tools.discover` and then the query which is github issues list.
16
17so `tools.discover("github issues list")`
18
19that then returns the callable paths and then the agent writes another line of code. that is await tools.github.issues.list. that executes in a sandbox and it calls a proxy object which makes a fetch to run the call and then that runs that call in the secure environment. that call completes and the results are given to the sandbox and the sandbox resumes.
20
21another instance is the case where a tool needs some form of user action. the example that i will give is approvals and if it's a create issue for example, the agent would write `await tools.github.issues.create`. that would then call the proxy object. the proxy object would trigger the interaction and then once the user approves that interaction it resumes.
22
23a note on the interaction model: i believe we want to model this off of mcp elicitation which you can see the spec for in references. the reason we want to model it off of mcp elicitation is that for apis for example that will just be a standard approve/deny model but for something like this since we support calling mcp servers those mcp servers could actually try and elicit input for the user.
24
25So let's talk about interaction model a little bit. Executor is going to be configurable through Executor, and so you would be able to add a source, for example, by just prompting: "Hey, please add Axiom as a source." What it would do is it would call tools.executor.AddSource, which calls the MCP server. That MCP server matches kind of the web flow where there's information or an action you need to elicit from the user. What that information action looks like is in the OAUTH flow. It's: they have to go sign in somewhere; there's some callback that happens, and then something's created for that. It's details on that, but we get them onto a web page in the browser to sign in. A similar thing would be if a source required an API key. It's the same concept if you open up a web page to securely input that.
26
27Current state:
28
29We are on v3 of this codebase which is a completely fresh start, the original codebase is in legacy/, the v2 is in legacy2/.
30
31Architecture I like:
32
33Database:
34Local-file-backed control plane for local use, with room for future hosted backends
35
36Server:
37
38- API
39 The web dashboard calls the API via effect-atom.
40
41- MCP
42 Executor is configurable via executor. A user can enter the prompt "Please add the https://mcp.notion.com/mcp to my workspace"
43
44We use MCP here instead of the API as the user has to perform actions in many cases. An example of this is OAuthing to an MCP server or opening a page to set a secret in. This prevents the secrets from being pasted over a chat
45
46Doing MCP here w/ MCP elicitation allows us to not give special treatment to the executor app while also giving a nice UX
47
48Ideally the MCP server shares a lot of logic between API
49
50Clients:
51
52- Web client
53 Cloud product hosted at executor.sh, local one just on localhost
54 This is a next.js app, so we will actually host the api/ in a route handler on it however it's important we implement the API in a standalone package and it just exposes a standard web request / response handler
55- CLI
56 Connects to either the cloud api endpoint
57
58Standalone:
59
60- SDK
61 Allows for people to use everything we've built in their own apps, including ingesting API specs / MCPs for calling
62
63Runtimes:
64
65- Deno
66- In process
67- Cloudflare worker runtime
68
69Other:
70
71- Avoid hardcoding strings in the core libraries, prefer programming to interfaces, this is because it allows people to build custom adapters without having to modify the core
72- Aim for composability of adapters. For example, the cloud product may eventually store secrets in a hosted backend while still supporting BYO 1Password. This does not need to be implemented today but is worth noting.
73- This is a fresh start so we can make whatever changes we need.
74
75Rough architecture is: Turborepo monorepo, effect-vitest for testing, bun for package manager / running apps (but we leverage Effect wherever possible rather than bun's apis i.e for the server), Next.js for web app
76
77References:
78https://blog.cloudflare.com/code-mode/ - concept we are implementing
79https://mcp.axiom.co/mcp - MCP server that requires auth
80https://modelcontextprotocol.io/specification/draft/client/elicitation - How we are going to be handling interactions from the user
81legacy/ original implementation
82legacy2
TRACING.md+0−43
@@ -1,43 +0,0 @@
1# Local Tracing
2
3Start Jaeger locally:
4
5```bash
6bun run trace:up
7```
8
9Run the web/server dev process with tracing enabled:
10
11```bash
12EXECUTOR_TRACE_ENABLED=1 \
13EXECUTOR_TRACE_SERVICE_NAME=executor-local \
14EXECUTOR_TRACE_OTLP_ENDPOINT=http://127.0.0.1:4317 \
15bun run dev
16```
17
18Then open Jaeger:
19
20- UI: `http://127.0.0.1:16686/search?service=executor-local`
21
22You can also query traces over HTTP:
23
24```bash
25curl "http://127.0.0.1:16686/api/traces?service=executor-local&limit=20"
26```
27
28Useful operations to search for:
29
30- `source.connect.http`
31- `source.catalog.sync`
32- `graphql.syncCatalog`
33- `graphql.introspection.fetch`
34- `graphql.manifest.extract`
35- `graphql.definitions.compile`
36- `graphql.operations.build`
37- `graphql.snapshot.build`
38
39Stop Jaeger:
40
41```bash
42bun run trace:down
43```
personal-notes/otel-tracing-for-source-connect.md+0−50
@@ -1,50 +0,0 @@
1# OTel Tracing For Source Connect
2
3## Setup
4
5- Use the Effect-native OTel layer from `@effect/opentelemetry`, not a hand-rolled tracer bridge.
6- Start Jaeger with `bun run trace:up`.
7- Run the dev server with:
8
9```bash
10EXECUTOR_TRACE_ENABLED=1 \
11EXECUTOR_TRACE_SERVICE_NAME=executor-local \
12EXECUTOR_TRACE_OTLP_ENDPOINT=http://127.0.0.1:4317 \
13bun run dev
14```
15
16- Query traces via:
17
18```bash
19curl "http://127.0.0.1:16686/api/traces?service=executor-local&limit=20"
20```
21
22## Useful spans
23
24- `source.connect.http`
25- `source.catalog.sync`
26- `source.store.persist`
27- `source.store.load_by_id`
28- `source.status.update`
29- `graphql.syncCatalog`
30- `graphql.introspection.fetch`
31- `graphql.manifest.extract`
32- `graphql.definitions.compile`
33- `graphql.operations.build`
34- `graphql.snapshot.build`
35
36## What the traces showed
37
38- Raw Linear introspection fetch is fast, around sub-second to about one second.
39- The biggest remaining cost in the GraphQL import path is `graphql.operations.build`, around 5s on Linear.
40- A large earlier regression was not GraphQL import itself. It was declaration refresh churn interfering with the connect flow.
41- The fix was:
42 - keep `.d.ts` generation backgrounded
43 - debounce it with Effect fibers / `Effect.sleep`
44 - avoid scheduling workspace-wide declaration refresh during normal `probing` / `connected` source persists
45
46## Current state
47
48- Fresh Linear `connect` is back down to about 7-8s instead of ~70s.
49- `linear.d.ts` is much smaller now because source-level shared shapes are deduped.
50- The next performance target is `graphql.operations.build`, not tracing or `.d.ts` scheduling.
ralph.ts+0−22
@@ -1,22 +0,0 @@
1import { $ } from "bun";
2
3let prompt = `
4You're running in a while loop with new sesions each time, you will have to check to see what the progress so far that has been made is.
5
6We are migrating from an old, very messy model to a plugin system for OpenAPI, GraphQL, MCP, and Google Discovery. You'll see that we've got the OpenAPI implemented. If you look at the Git diff on this branch, you'll see a bunch of deleted files and functionality around the rest of the product. We have to bring back all of the functionality that was removed for GraphQL and MCP of an API or Discovery. The thing is that it has to be now localised to those plugins, so you can read the plugin and the functionality will go away. Some examples of that are:
7
8- how Google Discovery has the or credentials and that or flow; that all gets implemented in the Google Discovery plugin
9- the MCP OAuth stuff gets implemented in the MCP plugin
10
11Plugins can own storage; they can own API routes. They're meant to extend the product. If the product does not match what the plugins need, then we can extend the core, but the core should not be plugin-specific. What you need to do is work until this is fully migrated to this new model. Once it is fully migrated, output <STOP-TOKEN-124124> 3 times so you stop working.
12
13Feel free to autonomously test the app yourself via calling the API or running the local CLI
14
15You can take notes in agent-notes/
16`
17let output = ''
18
19do {
20 output = await $`codex exec --yolo "${prompt}"`.then(result => String(result.stdout));
21} while (// stop token has to show up more than 1 time
22 output.split("\n").filter(line => line.includes("<STOP-TOKEN-124124>")).length < 2);
skills-lock.json+0−10
@@ -1,10 +0,0 @@
1{
2 "version": 1,
3 "skills": {
4 "mintlify": {
5 "source": "mintlify.com",
6 "sourceType": "well-known",
7 "computedHash": "fc61ab6703f692dc2c215e74327a8998d02987ca9571084440be20e7ecad6b46"
8 }
9 }
10}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t