fix(session): propagate network context
This commit is contained in:
@@ -35,12 +35,18 @@ function appendParams(url: URL, params: Record<string, unknown> = {}): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function withNetworkParam(params: Record<string, unknown> | undefined, network: string): Record<string, unknown> {
|
||||||
|
if (params?.network !== undefined || params?.network_name !== undefined || params?.name !== undefined) return params ?? {};
|
||||||
|
return { ...(params ?? {}), network };
|
||||||
|
}
|
||||||
|
|
||||||
export async function requestJson(ctx: RuntimeContext, request: RequestOptions): Promise<[unknown, number]> {
|
export async function requestJson(ctx: RuntimeContext, request: RequestOptions): Promise<[unknown, number]> {
|
||||||
const { method, path, params, body, requireAuth = true, requireProject = false, requireNetworkCtx = false, requireUsernameCtx = false } = request;
|
const { method, path, params, body, requireAuth = true, requireProject = false, requireNetworkCtx = false, requireUsernameCtx = false } = request;
|
||||||
if (requireNetworkCtx) requireNetwork(ctx);
|
const network = requireNetworkCtx ? requireNetwork(ctx) : null;
|
||||||
if (requireUsernameCtx) requireUsername(ctx);
|
if (requireUsernameCtx) requireUsername(ctx);
|
||||||
const url = new URL(`/api/v1${path}`, ctx.server.replace(/\/+$/, ""));
|
const url = new URL(`/api/v1${path}`, ctx.server.replace(/\/+$/, ""));
|
||||||
appendParams(url, params);
|
const requestParams = network && (params !== undefined || body === undefined) ? withNetworkParam(params, network) : params;
|
||||||
|
appendParams(url, requestParams);
|
||||||
const started = performance.now();
|
const started = performance.now();
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
const timer = setTimeout(() => controller.abort(), ctx.timeout * 1000);
|
const timer = setTimeout(() => controller.abort(), ctx.timeout * 1000);
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
"dev": "bun --watch src/server.ts",
|
"dev": "bun --watch src/server.ts",
|
||||||
"build": "bun run check",
|
"build": "bun run check",
|
||||||
"check": "bun run typecheck && bun run typecheck:opencode",
|
"check": "bun run typecheck && bun run typecheck:opencode",
|
||||||
|
"migrate:session-identity": "bun scripts/migrate-session-identity.ts",
|
||||||
"pipeline:trigger": "bash scripts/trigger-gitea-pipeline.sh",
|
"pipeline:trigger": "bash scripts/trigger-gitea-pipeline.sh",
|
||||||
"start": "bun src/server.ts",
|
"start": "bun src/server.ts",
|
||||||
"start:prod": "bun run check && bun src/server.ts"
|
"start:prod": "bun run check && bun src/server.ts"
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ export type AgentAuthContext = {
|
|||||||
role: string;
|
role: string;
|
||||||
isSuperuser: boolean;
|
isSuperuser: boolean;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
|
network: string;
|
||||||
projectRole: string;
|
projectRole: string;
|
||||||
tokenExpiresAt?: string;
|
tokenExpiresAt?: string;
|
||||||
};
|
};
|
||||||
@@ -22,6 +23,7 @@ type AgentAuthContextResponse = {
|
|||||||
role?: unknown;
|
role?: unknown;
|
||||||
is_superuser?: unknown;
|
is_superuser?: unknown;
|
||||||
project_id?: unknown;
|
project_id?: unknown;
|
||||||
|
network?: unknown;
|
||||||
project_role?: unknown;
|
project_role?: unknown;
|
||||||
token_expires_at?: unknown;
|
token_expires_at?: unknown;
|
||||||
};
|
};
|
||||||
@@ -53,6 +55,7 @@ const normalizeAgentAuthContext = (
|
|||||||
typeof payload.role !== "string" ||
|
typeof payload.role !== "string" ||
|
||||||
typeof payload.is_superuser !== "boolean" ||
|
typeof payload.is_superuser !== "boolean" ||
|
||||||
typeof payload.project_id !== "string" ||
|
typeof payload.project_id !== "string" ||
|
||||||
|
typeof payload.network !== "string" ||
|
||||||
typeof payload.project_role !== "string"
|
typeof payload.project_role !== "string"
|
||||||
) {
|
) {
|
||||||
return null;
|
return null;
|
||||||
@@ -65,6 +68,7 @@ const normalizeAgentAuthContext = (
|
|||||||
role: payload.role,
|
role: payload.role,
|
||||||
isSuperuser: payload.is_superuser,
|
isSuperuser: payload.is_superuser,
|
||||||
projectId: payload.project_id,
|
projectId: payload.project_id,
|
||||||
|
network: payload.network,
|
||||||
projectRole: payload.project_role,
|
projectRole: payload.project_role,
|
||||||
tokenExpiresAt:
|
tokenExpiresAt:
|
||||||
typeof payload.token_expires_at === "string"
|
typeof payload.token_expires_at === "string"
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ export type SessionBinding = {
|
|||||||
export type SessionContext = {
|
export type SessionContext = {
|
||||||
clientSessionId: string;
|
clientSessionId: string;
|
||||||
accessToken?: string;
|
accessToken?: string;
|
||||||
|
network?: string;
|
||||||
projectId?: string;
|
projectId?: string;
|
||||||
tokenExpiresAt?: string;
|
tokenExpiresAt?: string;
|
||||||
userId?: string;
|
userId?: string;
|
||||||
@@ -36,6 +37,7 @@ export class ChatSessionBridge {
|
|||||||
async resolve(context: {
|
async resolve(context: {
|
||||||
sessionId?: string;
|
sessionId?: string;
|
||||||
accessToken?: string;
|
accessToken?: string;
|
||||||
|
network?: string;
|
||||||
projectId?: string;
|
projectId?: string;
|
||||||
traceId?: string;
|
traceId?: string;
|
||||||
tokenExpiresAt?: string;
|
tokenExpiresAt?: string;
|
||||||
@@ -71,6 +73,7 @@ export class ChatSessionBridge {
|
|||||||
allowLearningWrite: true,
|
allowLearningWrite: true,
|
||||||
clientSessionId: requestContext.clientSessionId,
|
clientSessionId: requestContext.clientSessionId,
|
||||||
learningMode: "interactive",
|
learningMode: "interactive",
|
||||||
|
network: requestContext.network,
|
||||||
projectId: requestContext.projectId,
|
projectId: requestContext.projectId,
|
||||||
projectKey: requestContext.projectKey,
|
projectKey: requestContext.projectKey,
|
||||||
sessionId,
|
sessionId,
|
||||||
@@ -144,6 +147,7 @@ export class ChatSessionBridge {
|
|||||||
private buildRequestContext(context: {
|
private buildRequestContext(context: {
|
||||||
sessionId?: string;
|
sessionId?: string;
|
||||||
accessToken?: string;
|
accessToken?: string;
|
||||||
|
network?: string;
|
||||||
projectId?: string;
|
projectId?: string;
|
||||||
traceId?: string;
|
traceId?: string;
|
||||||
tokenExpiresAt?: string;
|
tokenExpiresAt?: string;
|
||||||
@@ -154,6 +158,7 @@ export class ChatSessionBridge {
|
|||||||
clientSessionId: sessionId || this.createClientSessionId(),
|
clientSessionId: sessionId || this.createClientSessionId(),
|
||||||
accessToken: context.accessToken,
|
accessToken: context.accessToken,
|
||||||
actorKey: toActorKey(context.userId),
|
actorKey: toActorKey(context.userId),
|
||||||
|
network: context.network,
|
||||||
projectId: context.projectId,
|
projectId: context.projectId,
|
||||||
projectKey: toProjectKey(context.projectId),
|
projectKey: toProjectKey(context.projectId),
|
||||||
tokenExpiresAt: context.tokenExpiresAt,
|
tokenExpiresAt: context.tokenExpiresAt,
|
||||||
|
|||||||
@@ -525,6 +525,7 @@ export const buildChatRouter = (
|
|||||||
const { binding, requestContext, created } = await sessionBridge.resolve({
|
const { binding, requestContext, created } = await sessionBridge.resolve({
|
||||||
sessionId: requestedSessionId,
|
sessionId: requestedSessionId,
|
||||||
accessToken,
|
accessToken,
|
||||||
|
network: authContext.network,
|
||||||
projectId,
|
projectId,
|
||||||
traceId,
|
traceId,
|
||||||
tokenExpiresAt: authContext.tokenExpiresAt,
|
tokenExpiresAt: authContext.tokenExpiresAt,
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ export type RuntimeSessionContext = {
|
|||||||
clientSessionId: string;
|
clientSessionId: string;
|
||||||
learningMode?: "interactive" | "review";
|
learningMode?: "interactive" | "review";
|
||||||
memoryListReadScopes?: Partial<Record<"user" | "workspace", boolean>>;
|
memoryListReadScopes?: Partial<Record<"user" | "workspace", boolean>>;
|
||||||
|
network?: string;
|
||||||
projectId?: string;
|
projectId?: string;
|
||||||
projectKey: string;
|
projectKey: string;
|
||||||
sessionId: string;
|
sessionId: string;
|
||||||
|
|||||||
+9
-1
@@ -101,11 +101,19 @@ app.post("/internal/tools/tjwater-cli-call", async (req, res) => {
|
|||||||
const timeoutSec =
|
const timeoutSec =
|
||||||
typeof req.body?.timeout === "number" && req.body.timeout > 0 ? req.body.timeout : 120;
|
typeof req.body?.timeout === "number" && req.body.timeout > 0 ? req.body.timeout : 120;
|
||||||
|
|
||||||
|
if (!context.network) {
|
||||||
|
res.status(400).json({
|
||||||
|
message: "runtime network missing; refresh chat context",
|
||||||
|
detail: sessionId,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const authJson = JSON.stringify({
|
const authJson = JSON.stringify({
|
||||||
server: config.TJWATER_API_BASE_URL,
|
server: config.TJWATER_API_BASE_URL,
|
||||||
access_token: context.accessToken,
|
access_token: context.accessToken,
|
||||||
project_id: context.projectId,
|
project_id: context.projectId,
|
||||||
network:"tjwater",
|
network: context.network,
|
||||||
});
|
});
|
||||||
|
|
||||||
const cliArgs = ["--auth-stdin", ...command.split(/\s+/).filter(Boolean)];
|
const cliArgs = ["--auth-stdin", ...command.split(/\s+/).filter(Boolean)];
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ describe("runtime session context", () => {
|
|||||||
allowLearningWrite: true,
|
allowLearningWrite: true,
|
||||||
clientSessionId: "chat-session-1",
|
clientSessionId: "chat-session-1",
|
||||||
learningMode: "interactive",
|
learningMode: "interactive",
|
||||||
|
network: "fengyang",
|
||||||
projectId: "project-id-1",
|
projectId: "project-id-1",
|
||||||
projectKey: "project-1",
|
projectKey: "project-1",
|
||||||
sessionId: "runtime-session-1",
|
sessionId: "runtime-session-1",
|
||||||
@@ -24,6 +25,7 @@ describe("runtime session context", () => {
|
|||||||
|
|
||||||
expect(runtimeContext?.accessToken).toBe("token-1");
|
expect(runtimeContext?.accessToken).toBe("token-1");
|
||||||
expect(runtimeContext?.clientSessionId).toBe("chat-session-1");
|
expect(runtimeContext?.clientSessionId).toBe("chat-session-1");
|
||||||
|
expect(runtimeContext?.network).toBe("fengyang");
|
||||||
expect(runtimeContext?.sessionId).toBe("runtime-session-1");
|
expect(runtimeContext?.sessionId).toBe("runtime-session-1");
|
||||||
|
|
||||||
removeRuntimeSessionContext("runtime-session-1");
|
removeRuntimeSessionContext("runtime-session-1");
|
||||||
|
|||||||
Reference in New Issue
Block a user