Files
next-tjwater-agent/src/product/server-auth-client.ts
T
jiang 6a49d2209e feat(agent): unify MCP tools and adopt Responses delivery
Move project and host queries into native MCP with host-owned OAuth/context,
while retaining memory, skills, actions and publication plugins. Add nearby-model
queries and explicit chat/task/learning model defaults.

Adopt Responses HTTP/SSE and native steer/queue inbox delivery, preserving scoped
session ownership, pending input edits and investigation communication.

Disable local-provider discovery: Server /health allowed native vLLM to probe
/v1/models on port 8000. A native before/after regression prevents recurrence.

Validation: 170 focused Bun tests; service/plugin typechecks; native discovery,
provider-discovery and input-inbox smoke checks; no paid model inference.
2026-10-19 18:46:58 +08:00

82 lines
2.7 KiB
TypeScript

import { config } from "../config.js";
import type { components } from "../generated/server-api.js";
export type ProductCredential = {
authorization: string;
projectId: string;
expiresAt?: string | null;
};
export type AgentAuthContext = components["schemas"]["AgentAuthContextResponse"];
export class ProductServerClient {
constructor(private readonly baseUrl = config.TJWATER_SERVER_BASE_URL) {}
async authenticateUser(authorization: string): Promise<{ user_id: string }> {
let response: Response;
try {
response = await fetch(new URL("/api/v1/access-context", this.baseUrl), {
headers: { Accept: "application/json", Authorization: authorization },
});
} catch { throw new ProductServerClientError(503, "TJWater Server unavailable"); }
const parsed = parseJson(await response.text());
if (!response.ok) throw new ProductServerClientError(response.status, "TJWater Server rejected identity verification");
if (!parsed || typeof parsed !== "object" || !("user_id" in parsed) || typeof parsed.user_id !== "string" || !parsed.user_id.trim()) {
throw new ProductServerClientError(502, "TJWater Server returned invalid identity");
}
return { user_id: parsed.user_id };
}
async authenticate(credential: ProductCredential): Promise<AgentAuthContext> {
const url = new URL("/api/v1/agent-auth-context", this.baseUrl);
let response: Response;
try {
response = await fetch(url, {
headers: {
Accept: "application/json",
Authorization: credential.authorization,
"X-Project-Id": credential.projectId,
},
});
} catch {
throw new ProductServerClientError(503, "TJWater Server unavailable");
}
const text = await response.text();
const parsed = parseJson(text);
if (!response.ok) {
throw new ProductServerClientError(
response.status,
readErrorMessage(parsed, "TJWater Server rejected identity verification"),
);
}
if (!parsed || typeof parsed !== "object") {
throw new ProductServerClientError(502, "TJWater Server returned invalid JSON");
}
return parsed as AgentAuthContext;
}
}
export class ProductServerClientError extends Error {
constructor(readonly status: number, message: string) {
super(message);
}
}
const parseJson = (text: string): unknown => {
if (!text.trim()) return null;
try {
return JSON.parse(text);
} catch {
return null;
}
};
const readErrorMessage = (value: unknown, fallback: string) => {
if (!value || typeof value !== "object") return fallback;
const record = value as Record<string, unknown>;
if (typeof record.detail === "string") return record.detail;
if (typeof record.message === "string") return record.message;
return fallback;
};