Skip to content

Commit aeb4833

Browse files
committed
Make custom tools sources first-class integrations
1 parent 6e2010f commit aeb4833

24 files changed

Lines changed: 1146 additions & 527 deletions

apps/host-selfhost/scripts/custom-tools-demo.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const HOST = "127.0.0.1";
77
const BASE_URL = `http://${HOST}:${PORT}`;
88
const ORIGIN = "http://custom-tools-demo.local";
99
const DEMO_REPO = "RhysSullivan/executor-custom-tools-demo";
10+
const DEMO_APP = "demo-tools";
1011
const ADMIN_EMAIL = "admin@custom-tools-demo.local";
1112
const ADMIN_PASSWORD = "admin-pass-123456";
1213
const USER_EMAIL = "rhys@custom-tools-demo.local";
@@ -232,7 +233,7 @@ const htmlEscape = (value: string): string =>
232233
.replaceAll('"', """);
233234

234235
const toolInvokeCode = (toolName: string, args: unknown): string => `
235-
const found = await tools.search({ namespace: "apps", query: ${JSON.stringify(toolName)}, limit: 20 });
236+
const found = await tools.search({ namespace: ${JSON.stringify(DEMO_APP)}, query: ${JSON.stringify(toolName)}, limit: 20 });
236237
const item = found.items.find((candidate) => candidate.path.endsWith(${JSON.stringify(toolName)}));
237238
if (!item) return { ok: false, missing: ${JSON.stringify(toolName)}, found };
238239
let fn = tools;
@@ -251,10 +252,11 @@ const buildBanner = (input: {
251252
readonly consoleUi: string;
252253
}): string => {
253254
const sync = curlJson("/api/apps/sources/github/sync", {
255+
name: DEMO_APP,
254256
url: input.repoUrl,
255257
token: input.githubToken,
256258
});
257-
const list = `curl -sS '${BASE_URL}/api/tools?integration=apps' | jq`;
259+
const list = `curl -sS '${BASE_URL}/api/tools?integration=${DEMO_APP}' | jq`;
258260
const repoSummary = curlJson("/api/executions", {
259261
code: toolInvokeCode("repo-summary", {
260262
github: input.connectionAddress,
@@ -290,6 +292,9 @@ Auth:
290292
GitHub invocation connection:
291293
${input.connectionAddress}
292294
295+
Custom tools app:
296+
${DEMO_APP}
297+
293298
Sync the repo:
294299
${sync}
295300

apps/host-selfhost/src/apps-route.ts

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
makeScopedExecutor,
99
type Principal,
1010
} from "@executor-js/api/server";
11-
import { ToolAddress } from "@executor-js/sdk";
11+
import { IntegrationSlug, ToolAddress } from "@executor-js/sdk";
1212

1313
import { SelfHostScopedExecutorSeams } from "./execution";
1414
import { SelfHostDb, type SelfHostDbHandle } from "./db/self-host-db";
@@ -43,6 +43,21 @@ const buildExecutor = (principal: Principal, request: Request) => {
4343
: withOrigin;
4444
};
4545

46+
const sourceSlugFromRoute = () =>
47+
HttpRouter.params.pipe(
48+
Effect.map((params) => params.slug ?? ""),
49+
Effect.flatMap((slug) =>
50+
slug.length > 0
51+
? Effect.succeed(slug)
52+
: Effect.fail(
53+
new AppsSyncRouteError({
54+
status: 400,
55+
message: "Missing custom tools source slug",
56+
}),
57+
),
58+
),
59+
);
60+
4661
export interface SelfHostAppsSyncRouteDeps {
4762
readonly identity: Layer.Layer<IdentityProvider>;
4863
readonly db: SelfHostDbHandle;
@@ -118,5 +133,34 @@ export const makeSelfHostAppsSyncRoute = ({ identity, db }: SelfHostAppsSyncRout
118133
}),
119134
),
120135
),
136+
HttpRouter.add(
137+
"GET",
138+
"/api/apps/sources/github/:slug",
139+
routeHandler((request) =>
140+
Effect.gen(function* () {
141+
const identityProvider = yield* IdentityProvider;
142+
const principal = yield* identityProvider.authenticate(request);
143+
const slug = yield* sourceSlugFromRoute();
144+
const executor = yield* buildExecutor(principal, request);
145+
return yield* executor.execute(ToolAddress.make("executor.apps.get_github_source"), {
146+
slug,
147+
});
148+
}),
149+
),
150+
),
151+
HttpRouter.add(
152+
"DELETE",
153+
"/api/apps/sources/github/:slug",
154+
routeHandler((request) =>
155+
Effect.gen(function* () {
156+
const identityProvider = yield* IdentityProvider;
157+
const principal = yield* identityProvider.authenticate(request);
158+
const slug = yield* sourceSlugFromRoute();
159+
const executor = yield* buildExecutor(principal, request);
160+
yield* executor.integrations.remove(IntegrationSlug.make(slug));
161+
return { removed: true };
162+
}),
163+
),
164+
),
121165
).pipe(HttpRouter.provideRequest(services));
122166
};

0 commit comments

Comments
 (0)