mirror of
https://github.com/Sea-Haven-Industries/open-swe.git
synced 2026-09-30 12:43:16 +00:00
feat: include installation name in headers (#358)
* feat: include installation id in headers * drop console logs * fix req from frontend
This commit is contained in:
parent
19206f0c34
commit
4c53b1a4e3
14 changed files with 189 additions and 75 deletions
|
|
@ -4,12 +4,6 @@ import {
|
|||
ManagerGraphUpdate,
|
||||
} from "@open-swe/shared/open-swe/manager/types";
|
||||
import { createLangGraphClient } from "../../../utils/langgraph-client.js";
|
||||
import {
|
||||
GITHUB_INSTALLATION_TOKEN_COOKIE,
|
||||
GITHUB_TOKEN_COOKIE,
|
||||
GITHUB_USER_ID_HEADER,
|
||||
GITHUB_USER_LOGIN_HEADER,
|
||||
} from "@open-swe/shared/constants";
|
||||
import {
|
||||
BaseMessage,
|
||||
HumanMessage,
|
||||
|
|
@ -32,6 +26,7 @@ import {
|
|||
extractIssueTitleAndContentFromMessage,
|
||||
formatContentForIssueBody,
|
||||
} from "../../../utils/github/issue-messages.js";
|
||||
import { getDefaultHeaders } from "../../../utils/default-headers.js";
|
||||
|
||||
// This should not be shown to the user if the programmer is running
|
||||
const PLAN_ROUTING_OPTION = `- plan: Call this route if the user's message is a complete request which you can use to kickoff a new planning session (only if one is not already running), or it's an entirely new request which you should also start a new planning session for (only if both the planner and programmer are not running). You may also call this route if the planner is running, and the user's message contains updated instructions, or additional context which may be relevant/helpful to the planner.`;
|
||||
|
|
@ -219,15 +214,7 @@ export async function classifyMessage(
|
|||
}
|
||||
|
||||
const langGraphClient = createLangGraphClient({
|
||||
defaultHeaders: {
|
||||
[GITHUB_TOKEN_COOKIE]: config.configurable?.[GITHUB_TOKEN_COOKIE] ?? "",
|
||||
[GITHUB_INSTALLATION_TOKEN_COOKIE]:
|
||||
config.configurable?.[GITHUB_INSTALLATION_TOKEN_COOKIE] ?? "",
|
||||
[GITHUB_USER_ID_HEADER]:
|
||||
config.configurable?.[GITHUB_USER_ID_HEADER] ?? "",
|
||||
[GITHUB_USER_LOGIN_HEADER]:
|
||||
config.configurable?.[GITHUB_USER_LOGIN_HEADER] ?? "",
|
||||
},
|
||||
defaultHeaders: getDefaultHeaders(config),
|
||||
});
|
||||
|
||||
const [programmerThread, plannerThread] = await Promise.all([
|
||||
|
|
|
|||
|
|
@ -5,13 +5,7 @@ import {
|
|||
ManagerGraphUpdate,
|
||||
} from "@open-swe/shared/open-swe/manager/types";
|
||||
import { createIssueTitleAndBodyFromMessages } from "../utils/generate-issue-fields.js";
|
||||
import {
|
||||
GITHUB_INSTALLATION_TOKEN_COOKIE,
|
||||
GITHUB_TOKEN_COOKIE,
|
||||
GITHUB_USER_ID_HEADER,
|
||||
GITHUB_USER_LOGIN_HEADER,
|
||||
MANAGER_GRAPH_ID,
|
||||
} from "@open-swe/shared/constants";
|
||||
import { MANAGER_GRAPH_ID } from "@open-swe/shared/constants";
|
||||
import { createLangGraphClient } from "../../../utils/langgraph-client.js";
|
||||
import { createIssue } from "../../../utils/github/api.js";
|
||||
import { getGitHubTokensFromConfig } from "../../../utils/github-tokens.js";
|
||||
|
|
@ -24,6 +18,7 @@ import {
|
|||
formatContentForIssueBody,
|
||||
} from "../../../utils/github/issue-messages.js";
|
||||
import { getBranchName } from "../../../utils/github/git.js";
|
||||
import { getDefaultHeaders } from "../../../utils/default-headers.js";
|
||||
|
||||
/**
|
||||
* Create new manager session.
|
||||
|
|
@ -74,15 +69,7 @@ ${ISSUE_CONTENT_CLOSE_TAG}`,
|
|||
];
|
||||
|
||||
const langGraphClient = createLangGraphClient({
|
||||
defaultHeaders: {
|
||||
[GITHUB_TOKEN_COOKIE]: config.configurable?.[GITHUB_TOKEN_COOKIE] ?? "",
|
||||
[GITHUB_INSTALLATION_TOKEN_COOKIE]:
|
||||
config.configurable?.[GITHUB_INSTALLATION_TOKEN_COOKIE] ?? "",
|
||||
[GITHUB_USER_ID_HEADER]:
|
||||
config.configurable?.[GITHUB_USER_ID_HEADER] ?? "",
|
||||
[GITHUB_USER_LOGIN_HEADER]:
|
||||
config.configurable?.[GITHUB_USER_LOGIN_HEADER] ?? "",
|
||||
},
|
||||
defaultHeaders: getDefaultHeaders(config),
|
||||
});
|
||||
|
||||
const newManagerThreadId = uuidv4();
|
||||
|
|
|
|||
|
|
@ -5,16 +5,11 @@ import {
|
|||
ManagerGraphUpdate,
|
||||
} from "@open-swe/shared/open-swe/manager/types";
|
||||
import { createLangGraphClient } from "../../../utils/langgraph-client.js";
|
||||
import {
|
||||
GITHUB_INSTALLATION_TOKEN_COOKIE,
|
||||
GITHUB_TOKEN_COOKIE,
|
||||
GITHUB_USER_ID_HEADER,
|
||||
GITHUB_USER_LOGIN_HEADER,
|
||||
PLANNER_GRAPH_ID,
|
||||
} from "@open-swe/shared/constants";
|
||||
import { PLANNER_GRAPH_ID } from "@open-swe/shared/constants";
|
||||
import { createLogger, LogLevel } from "../../../utils/logger.js";
|
||||
import { getBranchName } from "../../../utils/github/git.js";
|
||||
import { PlannerGraphUpdate } from "@open-swe/shared/open-swe/planner/types";
|
||||
import { getDefaultHeaders } from "../../../utils/default-headers.js";
|
||||
|
||||
const logger = createLogger(LogLevel.INFO, "StartPlanner");
|
||||
|
||||
|
|
@ -27,15 +22,7 @@ export async function startPlanner(
|
|||
config: GraphConfig,
|
||||
): Promise<ManagerGraphUpdate> {
|
||||
const langGraphClient = createLangGraphClient({
|
||||
defaultHeaders: {
|
||||
[GITHUB_TOKEN_COOKIE]: config.configurable?.[GITHUB_TOKEN_COOKIE] ?? "",
|
||||
[GITHUB_INSTALLATION_TOKEN_COOKIE]:
|
||||
config.configurable?.[GITHUB_INSTALLATION_TOKEN_COOKIE] ?? "",
|
||||
[GITHUB_USER_ID_HEADER]:
|
||||
config.configurable?.[GITHUB_USER_ID_HEADER] ?? "",
|
||||
[GITHUB_USER_LOGIN_HEADER]:
|
||||
config.configurable?.[GITHUB_USER_LOGIN_HEADER] ?? "",
|
||||
},
|
||||
defaultHeaders: getDefaultHeaders(config),
|
||||
});
|
||||
|
||||
const plannerThreadId = state.plannerSession?.threadId ?? uuidv4();
|
||||
|
|
|
|||
|
|
@ -16,10 +16,6 @@ import { startSandbox } from "../../../utils/sandbox.js";
|
|||
import { createNewTask } from "@open-swe/shared/open-swe/tasks";
|
||||
import { getUserRequest } from "../../../utils/user-request.js";
|
||||
import {
|
||||
GITHUB_INSTALLATION_TOKEN_COOKIE,
|
||||
GITHUB_TOKEN_COOKIE,
|
||||
GITHUB_USER_ID_HEADER,
|
||||
GITHUB_USER_LOGIN_HEADER,
|
||||
PLAN_INTERRUPT_ACTION_TITLE,
|
||||
PLAN_INTERRUPT_DELIMITER,
|
||||
DO_NOT_RENDER_ID_PREFIX,
|
||||
|
|
@ -36,6 +32,7 @@ import {
|
|||
ACCEPTED_PLAN_NODE_ID,
|
||||
CustomNodeEvent,
|
||||
} from "@open-swe/shared/open-swe/custom-node-events";
|
||||
import { getDefaultHeaders } from "../../../utils/default-headers.js";
|
||||
|
||||
const logger = createLogger(LogLevel.INFO, "ProposedPlan");
|
||||
|
||||
|
|
@ -77,15 +74,7 @@ async function startProgrammerRun(input: {
|
|||
}) {
|
||||
const { runInput, state, config, newMessages } = input;
|
||||
const langGraphClient = createLangGraphClient({
|
||||
defaultHeaders: {
|
||||
[GITHUB_TOKEN_COOKIE]: config.configurable?.[GITHUB_TOKEN_COOKIE] ?? "",
|
||||
[GITHUB_INSTALLATION_TOKEN_COOKIE]:
|
||||
config.configurable?.[GITHUB_INSTALLATION_TOKEN_COOKIE] ?? "",
|
||||
[GITHUB_USER_ID_HEADER]:
|
||||
config.configurable?.[GITHUB_USER_ID_HEADER] ?? "",
|
||||
[GITHUB_USER_LOGIN_HEADER]:
|
||||
config.configurable?.[GITHUB_USER_LOGIN_HEADER] ?? "",
|
||||
},
|
||||
defaultHeaders: getDefaultHeaders(config),
|
||||
});
|
||||
|
||||
const programmerThreadId = uuidv4();
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { GitHubApp } from "../../utils/github-app.js";
|
|||
import { Webhooks } from "@octokit/webhooks";
|
||||
import { createLangGraphClient } from "../../utils/langgraph-client.js";
|
||||
import {
|
||||
GITHUB_INSTALLATION_NAME,
|
||||
GITHUB_INSTALLATION_TOKEN_COOKIE,
|
||||
GITHUB_USER_ID_HEADER,
|
||||
GITHUB_USER_LOGIN_HEADER,
|
||||
|
|
@ -121,6 +122,7 @@ webhooks.on("issues.labeled", async ({ payload }) => {
|
|||
token,
|
||||
process.env.GITHUB_TOKEN_ENCRYPTION_KEY,
|
||||
),
|
||||
[GITHUB_INSTALLATION_NAME]: issueData.owner,
|
||||
[GITHUB_USER_ID_HEADER]: issueData.userId.toString(),
|
||||
[GITHUB_USER_LOGIN_HEADER]: issueData.userLogin,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import {
|
|||
verifyGithubUserId,
|
||||
} from "@open-swe/shared/github/verify-user";
|
||||
import {
|
||||
GITHUB_INSTALLATION_NAME,
|
||||
GITHUB_INSTALLATION_TOKEN_COOKIE,
|
||||
GITHUB_TOKEN_COOKIE,
|
||||
GITHUB_USER_ID_HEADER,
|
||||
|
|
@ -14,14 +15,31 @@ import { decryptGitHubToken } from "@open-swe/shared/crypto";
|
|||
import { verifyGitHubWebhookOrThrow } from "./github.js";
|
||||
import { createWithOwnerMetadata, createOwnerFilter } from "./utils.js";
|
||||
|
||||
// TODO: Export from LangGraph SDK
|
||||
export interface BaseAuthReturn {
|
||||
is_authenticated?: boolean;
|
||||
display_name?: string;
|
||||
identity: string;
|
||||
permissions: string[];
|
||||
}
|
||||
|
||||
interface AuthenticateReturn extends BaseAuthReturn {
|
||||
metadata: {
|
||||
installation_name: string;
|
||||
};
|
||||
}
|
||||
|
||||
export const auth = new Auth()
|
||||
.authenticate(async (request: Request) => {
|
||||
.authenticate<AuthenticateReturn>(async (request: Request) => {
|
||||
if (request.method === "OPTIONS") {
|
||||
return {
|
||||
identity: "anonymous",
|
||||
permissions: [],
|
||||
is_authenticated: false,
|
||||
display_name: "CORS Preflight",
|
||||
metadata: {
|
||||
installation_name: "n/a",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -38,6 +56,15 @@ export const auth = new Auth()
|
|||
);
|
||||
}
|
||||
|
||||
const installationNameHeader = request.headers.get(
|
||||
GITHUB_INSTALLATION_NAME,
|
||||
);
|
||||
if (!installationNameHeader) {
|
||||
throw new HTTPException(401, {
|
||||
message: "GitHub installation name header missing",
|
||||
});
|
||||
}
|
||||
|
||||
// We don't do anything with this token right now, but still confirm it
|
||||
// exists as it will cause issues later on if it's not present.
|
||||
const encryptedInstallationToken = request.headers.get(
|
||||
|
|
@ -84,6 +111,9 @@ export const auth = new Auth()
|
|||
identity: user.id.toString(),
|
||||
is_authenticated: true,
|
||||
display_name: user.login,
|
||||
metadata: {
|
||||
installation_name: installationNameHeader,
|
||||
},
|
||||
permissions: [
|
||||
"threads:create",
|
||||
"threads:create_run",
|
||||
|
|
|
|||
|
|
@ -38,6 +38,9 @@ export async function verifyGitHubWebhookOrThrow(request: Request) {
|
|||
identity: "x-internal-github-bot",
|
||||
is_authenticated: true,
|
||||
display_name: "GitHub Bot",
|
||||
metadata: {
|
||||
installation_name: "n/a",
|
||||
},
|
||||
permissions: [
|
||||
"threads:create",
|
||||
"threads:create_run",
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ export function createOwnerFilter(user: { identity: string }) {
|
|||
// Helper function for create operations that set metadata
|
||||
export function createWithOwnerMetadata(
|
||||
value: any,
|
||||
user: { identity: string },
|
||||
user: { identity: string; metadata: { installation_name: string } },
|
||||
) {
|
||||
if (isStudioUser(user.identity)) {
|
||||
return;
|
||||
|
|
@ -24,5 +24,6 @@ export function createWithOwnerMetadata(
|
|||
|
||||
value.metadata ??= {};
|
||||
value.metadata.owner = user.identity;
|
||||
value.metadata.installation_name = user.metadata.installation_name;
|
||||
return { owner: user.identity };
|
||||
}
|
||||
|
|
|
|||
35
apps/open-swe/src/utils/default-headers.ts
Normal file
35
apps/open-swe/src/utils/default-headers.ts
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
import { GraphConfig } from "@open-swe/shared/open-swe/types";
|
||||
import {
|
||||
GITHUB_INSTALLATION_TOKEN_COOKIE,
|
||||
GITHUB_TOKEN_COOKIE,
|
||||
GITHUB_USER_ID_HEADER,
|
||||
GITHUB_USER_LOGIN_HEADER,
|
||||
GITHUB_INSTALLATION_NAME,
|
||||
} from "@open-swe/shared/constants";
|
||||
|
||||
export function getDefaultHeaders(config: GraphConfig) {
|
||||
const githubInstallationTokenCookie =
|
||||
config.configurable?.[GITHUB_INSTALLATION_TOKEN_COOKIE];
|
||||
const githubInstallationName =
|
||||
config.configurable?.[GITHUB_INSTALLATION_NAME];
|
||||
|
||||
if (!githubInstallationTokenCookie || !githubInstallationName) {
|
||||
throw new Error("Missing required headers");
|
||||
}
|
||||
|
||||
const githubTokenCookie = config.configurable?.[GITHUB_TOKEN_COOKIE] ?? "";
|
||||
const githubUserIdHeader = config.configurable?.[GITHUB_USER_ID_HEADER] ?? "";
|
||||
const githubUserLoginHeader =
|
||||
config.configurable?.[GITHUB_USER_LOGIN_HEADER] ?? "";
|
||||
|
||||
return {
|
||||
// Required headers
|
||||
[GITHUB_INSTALLATION_TOKEN_COOKIE]: githubInstallationTokenCookie,
|
||||
[GITHUB_INSTALLATION_NAME]: githubInstallationName,
|
||||
|
||||
// Optional headers
|
||||
[GITHUB_TOKEN_COOKIE]: githubTokenCookie,
|
||||
[GITHUB_USER_ID_HEADER]: githubUserIdHeader,
|
||||
[GITHUB_USER_LOGIN_HEADER]: githubUserLoginHeader,
|
||||
};
|
||||
}
|
||||
|
|
@ -22,6 +22,7 @@
|
|||
"@langchain/core": "^0.3.57",
|
||||
"@langchain/langgraph": "^0.3.3",
|
||||
"@langchain/langgraph-sdk": "^0.0.85",
|
||||
"@octokit/app": "^16.0.1",
|
||||
"@open-swe/shared": "*",
|
||||
"@radix-ui/react-alert-dialog": "^1.1.14",
|
||||
"@radix-ui/react-avatar": "^1.1.3",
|
||||
|
|
|
|||
|
|
@ -3,10 +3,13 @@ import {
|
|||
GITHUB_TOKEN_COOKIE,
|
||||
GITHUB_INSTALLATION_ID_COOKIE,
|
||||
GITHUB_INSTALLATION_TOKEN_COOKIE,
|
||||
GITHUB_INSTALLATION_NAME,
|
||||
} from "@open-swe/shared/constants";
|
||||
import { encryptGitHubToken } from "@open-swe/shared/crypto";
|
||||
import { NextRequest } from "next/server";
|
||||
import { getInstallationToken } from "@/utils/github";
|
||||
import { App } from "@octokit/app";
|
||||
import { validate } from "uuid";
|
||||
|
||||
function getGitHubAccessTokenOrThrow(
|
||||
req: NextRequest,
|
||||
|
|
@ -24,19 +27,9 @@ function getGitHubAccessTokenOrThrow(
|
|||
}
|
||||
|
||||
async function getGitHubInstallationTokenOrThrow(
|
||||
req: NextRequest,
|
||||
installationIdCookie: string,
|
||||
encryptionKey: string,
|
||||
): Promise<string> {
|
||||
const installationIdCookie = req.cookies.get(
|
||||
GITHUB_INSTALLATION_ID_COOKIE,
|
||||
)?.value;
|
||||
|
||||
if (!installationIdCookie) {
|
||||
throw new Error(
|
||||
"No GitHub installation ID found. GitHub App must be installed first.",
|
||||
);
|
||||
}
|
||||
|
||||
const appId = process.env.GITHUB_APP_ID;
|
||||
const privateAppKey = process.env.GITHUB_APP_PRIVATE_KEY;
|
||||
|
||||
|
|
@ -52,6 +45,77 @@ async function getGitHubInstallationTokenOrThrow(
|
|||
return encryptGitHubToken(token, encryptionKey);
|
||||
}
|
||||
|
||||
async function getInstallationName(installationId: string) {
|
||||
if (!process.env.GITHUB_APP_ID || !process.env.GITHUB_APP_PRIVATE_KEY) {
|
||||
throw new Error("GitHub App ID or Private App Key is not configured.");
|
||||
}
|
||||
const app = new App({
|
||||
appId: process.env.GITHUB_APP_ID,
|
||||
privateKey: process.env.GITHUB_APP_PRIVATE_KEY,
|
||||
});
|
||||
|
||||
// Get installation details
|
||||
const { data } = await app.octokit.request(
|
||||
"GET /app/installations/{installation_id}",
|
||||
{
|
||||
installation_id: Number(installationId),
|
||||
},
|
||||
);
|
||||
|
||||
const installationName =
|
||||
data.account && "name" in data.account
|
||||
? data.account.name
|
||||
: data.account?.login;
|
||||
|
||||
return installationName ?? "";
|
||||
}
|
||||
|
||||
const isNewRunRequest = (reqUrlStr: string, reqMethod: string) => {
|
||||
try {
|
||||
const reqPathnameParts = new URL(reqUrlStr).pathname.split("/");
|
||||
const isCreateNewRunReq =
|
||||
reqPathnameParts?.[1] === "api" &&
|
||||
reqPathnameParts?.[2] === "threads" &&
|
||||
validate(reqPathnameParts?.[3]) &&
|
||||
reqPathnameParts?.[4] === "runs" &&
|
||||
reqPathnameParts.length === 5 &&
|
||||
reqMethod.toLowerCase() === "post";
|
||||
const isStreamRunReq =
|
||||
reqPathnameParts?.[1] === "api" &&
|
||||
reqPathnameParts?.[2] === "threads" &&
|
||||
validate(reqPathnameParts?.[3]) &&
|
||||
reqPathnameParts?.[4] === "runs" &&
|
||||
validate(reqPathnameParts?.[5]) &&
|
||||
reqPathnameParts?.[6]?.startsWith("stream") &&
|
||||
reqMethod.toLowerCase() === "get";
|
||||
return isCreateNewRunReq || isStreamRunReq;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
async function getInstallationNameFromReq(
|
||||
req: Request,
|
||||
installationId: string,
|
||||
): Promise<string> {
|
||||
try {
|
||||
const requestJson = await req.json();
|
||||
const installationName = requestJson?.input?.targetRepository?.owner;
|
||||
return installationName;
|
||||
} catch {
|
||||
// no-op
|
||||
}
|
||||
|
||||
try {
|
||||
if (isNewRunRequest(req.url, req.method)) {
|
||||
return await getInstallationName(installationId);
|
||||
}
|
||||
return "";
|
||||
} catch {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
// This file acts as a proxy for requests to your LangGraph server.
|
||||
// Read the [Going to Production](https://github.com/langchain-ai/agent-chat-ui?tab=readme-ov-file#going-to-production) section for more information.
|
||||
|
||||
|
|
@ -67,11 +131,24 @@ export const { GET, POST, PUT, PATCH, DELETE, OPTIONS, runtime } =
|
|||
"GITHUB_TOKEN_ENCRYPTION_KEY environment variable is required",
|
||||
);
|
||||
}
|
||||
const installationIdCookie = req.cookies.get(
|
||||
GITHUB_INSTALLATION_ID_COOKIE,
|
||||
)?.value;
|
||||
|
||||
if (!installationIdCookie) {
|
||||
throw new Error(
|
||||
"No GitHub installation ID found. GitHub App must be installed first.",
|
||||
);
|
||||
}
|
||||
const [installationToken, installationName] = await Promise.all([
|
||||
getGitHubInstallationTokenOrThrow(installationIdCookie, encryptionKey),
|
||||
getInstallationNameFromReq(req.clone(), installationIdCookie),
|
||||
]);
|
||||
|
||||
return {
|
||||
[GITHUB_TOKEN_COOKIE]: getGitHubAccessTokenOrThrow(req, encryptionKey),
|
||||
[GITHUB_INSTALLATION_TOKEN_COOKIE]:
|
||||
await getGitHubInstallationTokenOrThrow(req, encryptionKey),
|
||||
[GITHUB_INSTALLATION_TOKEN_COOKIE]: installationToken,
|
||||
[GITHUB_INSTALLATION_NAME]: installationName,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ export const PLAN_INTERRUPT_ACTION_TITLE = "Approve/Edit Plan";
|
|||
// Prefix the access token with `x-` so that it's included in requests to the LangGraph server.
|
||||
export const GITHUB_TOKEN_COOKIE = "x-github-access-token";
|
||||
export const GITHUB_INSTALLATION_TOKEN_COOKIE = "x-github-installation-token";
|
||||
export const GITHUB_INSTALLATION_NAME = "x-github-installation-name";
|
||||
|
||||
export const DO_NOT_RENDER_ID_PREFIX = "do-not-render-";
|
||||
export const GITHUB_AUTH_STATE_COOKIE = "github_auth_state";
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import {
|
|||
type RemoveUIMessage,
|
||||
} from "@langchain/langgraph-sdk/react-ui";
|
||||
import {
|
||||
GITHUB_INSTALLATION_NAME,
|
||||
GITHUB_INSTALLATION_TOKEN_COOKIE,
|
||||
GITHUB_TOKEN_COOKIE,
|
||||
GITHUB_USER_ID_HEADER,
|
||||
|
|
@ -407,6 +408,11 @@ export const GraphConfigurationMetadata: {
|
|||
type: "hidden",
|
||||
},
|
||||
},
|
||||
[GITHUB_INSTALLATION_NAME]: {
|
||||
x_open_swe_ui_config: {
|
||||
type: "hidden",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const GraphConfiguration = z.object({
|
||||
|
|
@ -576,6 +582,13 @@ export const GraphConfiguration = z.object({
|
|||
.string()
|
||||
.optional()
|
||||
.langgraph.metadata(GraphConfigurationMetadata[GITHUB_USER_LOGIN_HEADER]),
|
||||
/**
|
||||
* The installation name of the GitHub app. Required when creating runs triggered by a bot (e.g. GitHub issue)
|
||||
*/
|
||||
[GITHUB_INSTALLATION_NAME]: z
|
||||
.string()
|
||||
.optional()
|
||||
.langgraph.metadata(GraphConfigurationMetadata[GITHUB_INSTALLATION_NAME]),
|
||||
});
|
||||
|
||||
export type GraphConfig = LangGraphRunnableConfig<
|
||||
|
|
|
|||
|
|
@ -2479,6 +2479,7 @@ __metadata:
|
|||
"@langchain/core": ^0.3.57
|
||||
"@langchain/langgraph": ^0.3.3
|
||||
"@langchain/langgraph-sdk": ^0.0.85
|
||||
"@octokit/app": ^16.0.1
|
||||
"@open-swe/shared": "*"
|
||||
"@radix-ui/react-alert-dialog": ^1.1.14
|
||||
"@radix-ui/react-avatar": ^1.1.3
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue