Files
OmniRoute/src/app/authorize/parseCallback.ts
S0yora 8adc0a0d9a feat(oauth): add Trae SOLO provider (work/code modes)
Full SOLO remote-agent integration against solo.trae.ai's reverse-engineered
API (upgrades the previous import_token stub).

- open-sse/executors/trae.ts: streaming executor for the solo_agent_remote API
  (POST /chat_sessions + GET /events SSE -> OpenAI chat.completions), accumulating
  plan_item thoughts and mapping token_usage. Headless Cloud-IDE-JWT refresh via
  the ExchangeToken endpoint.
- Session mode via model id: `trae/work` runs the fast work-mode auto agent;
  `trae/auto` and named models (gpt-5.4, kimi-k2.5, gemini-3.1-pro, ...) run in
  code mode.
- Provider registry entry (models + 272k context) and executor wiring.
- Two credential paths: browser /authorize loopback callback (captures the JWT +
  long-lived refresh token) and manual Cloud-IDE-JWT paste via
  POST /api/oauth/trae/import (Zod-validated).
- TraeAuthModal dashboard UI wired into the provider detail page.
- mapTokens/providerSpecificData carry the SOLO common_params identity fields.
- Tests: executor (stream/non-stream/error/refresh/work-mode + callback parser)
  and updated oauth-trae provider tests.
2026-05-31 04:38:22 +03:00

98 lines
3.0 KiB
TypeScript

/**
* Pure parser for the Trae SOLO /authorize callback query string. Extracted
* from route.ts so it can be unit-tested without touching the DB layer.
*
* Returns the credential bundle that the route hands to createProviderConnection,
* or a structured error if the payload is missing/malformed.
*/
export type ParsedTraeCallback = {
ok: true;
record: {
provider: "trae";
authType: "oauth";
accessToken: string;
refreshToken: string | null;
expiresAt: string | null;
email: string | null;
providerSpecificData: {
userId: string;
tenantId: string;
bizUserId: string;
userUniqueId: string;
webId: string;
scope: "marscode-us";
tenant: "marscode";
region: string;
aiRegion: string;
host: string;
screenName: string | null;
clientId: string;
refreshExpireAt: number | null;
authMethod: "oauth_callback";
};
testStatus: "active";
};
};
export type ParseError = { ok: false; error: string };
export function parseTraeCallbackQuery(q: URLSearchParams): ParsedTraeCallback | ParseError {
const userJwtRaw = q.get("userJwt");
if (!userJwtRaw) return { ok: false, error: "Missing userJwt in callback" };
let userJwt: Record<string, unknown>;
try {
userJwt = JSON.parse(userJwtRaw);
} catch {
return { ok: false, error: "Malformed userJwt payload" };
}
const token = userJwt.Token as string | undefined;
if (!token) return { ok: false, error: "userJwt.Token missing" };
const refresh = (userJwt.RefreshToken as string) || q.get("refreshToken") || null;
const tokenExpiresAtMs = Number(userJwt.TokenExpireAt) || 0;
const refreshExpiresAtMs = Number(userJwt.RefreshExpireAt || q.get("refreshExpireAt")) || 0;
let info: Record<string, unknown> = {};
const userInfoRaw = q.get("userInfo");
if (userInfoRaw) {
try {
info = JSON.parse(userInfoRaw);
} catch {
// userInfo is best-effort metadata — fall back to defaults silently.
}
}
const userId = (info.UserID as string) || "";
const region = (info.Region as string) || "US-East";
return {
ok: true,
record: {
provider: "trae",
authType: "oauth",
accessToken: token,
refreshToken: refresh,
expiresAt: tokenExpiresAtMs ? new Date(tokenExpiresAtMs).toISOString() : null,
email: (info.NonPlainTextEmail as string) || null,
providerSpecificData: {
userId,
tenantId: (info.TenantID as string) || "",
bizUserId: userId,
userUniqueId: userId,
webId: userId,
scope: "marscode-us",
tenant: "marscode",
region,
aiRegion: (info.AIRegion as string) || region,
host: q.get("host") || "https://api-us-east.trae.ai",
screenName: (info.ScreenName as string) || null,
clientId: (userJwt.ClientID as string) || "en1oxy7wnw8j9n",
refreshExpireAt: refreshExpiresAtMs || null,
authMethod: "oauth_callback",
},
testStatus: "active",
},
};
}