refactor(remote): extract shared registerWithCloud() helper

Registration HTTP logic was duplicated three times across listen.ts
(initial + re-register) and listen.tsx. Extracted into
listen-register.ts with proper error handling for non-JSON responses,
response shape validation, and 5 focused tests. Removes ~110 lines
of duplication.

🐛 Generated with [Letta Code](https://letta.com)

Co-Authored-By: Letta <noreply@letta.com>
This commit is contained in:
cpacker
2026-03-01 11:52:42 -08:00
parent b4910cd410
commit 393ab7bddf
4 changed files with 203 additions and 110 deletions

View File

@@ -7,6 +7,7 @@ import { hostname } from "node:os";
import { getServerUrl } from "../../agent/client";
import { settingsManager } from "../../settings-manager";
import { getErrorMessage } from "../../utils/error";
import { registerWithCloud } from "../../websocket/listen-register";
import type { Buffers, Line } from "../helpers/accumulator";
// tiny helper for unique ids
@@ -221,44 +222,14 @@ export async function handleListen(
throw new Error("Missing LETTA_API_KEY");
}
// Call register endpoint
const registerUrl = `${serverUrl}/v1/environments/register`;
const registerResponse = await fetch(registerUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${apiKey}`,
"X-Letta-Source": "letta-code",
},
body: JSON.stringify({
deviceId,
connectionName,
}),
// Register with cloud
const { connectionId, wsUrl } = await registerWithCloud({
serverUrl,
apiKey,
deviceId,
connectionName,
});
if (!registerResponse.ok) {
let errorMessage = `Registration failed (HTTP ${registerResponse.status})`;
try {
const error = (await registerResponse.json()) as { message?: string };
if (error.message) errorMessage = error.message;
} catch {
// Response body is not JSON (e.g. HTML error page from proxy)
const text = await registerResponse.text().catch(() => "");
if (text) errorMessage += `: ${text.slice(0, 200)}`;
}
throw new Error(errorMessage);
}
let registerBody: { connectionId: string; wsUrl: string };
try {
registerBody = (await registerResponse.json()) as typeof registerBody;
} catch {
throw new Error(
"Registration endpoint returned non-JSON response — is the server running?",
);
}
const { connectionId, wsUrl } = registerBody;
updateCommandResult(
ctx.buffersRef,
ctx.refreshDerived,
@@ -352,48 +323,17 @@ export async function handleListen(
);
try {
// Re-register to get new connectionId
const reregisterResponse = await fetch(registerUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${apiKey}`,
"X-Letta-Source": "letta-code",
},
body: JSON.stringify({
deviceId,
connectionName,
}),
const reregisterResult = await registerWithCloud({
serverUrl,
apiKey,
deviceId,
connectionName,
});
if (!reregisterResponse.ok) {
let errorMessage = `Re-registration failed (HTTP ${reregisterResponse.status})`;
try {
const error = (await reregisterResponse.json()) as {
message?: string;
};
if (error.message) errorMessage = error.message;
} catch {
const text = await reregisterResponse.text().catch(() => "");
if (text) errorMessage += `: ${text.slice(0, 200)}`;
}
throw new Error(errorMessage);
}
let reregisterData: { connectionId: string; wsUrl: string };
try {
reregisterData =
(await reregisterResponse.json()) as typeof reregisterData;
} catch {
throw new Error(
"Re-registration endpoint returned non-JSON response — is the server running?",
);
}
// Restart client with new connectionId
await startClient(
reregisterData.connectionId,
reregisterData.wsUrl,
reregisterResult.connectionId,
reregisterResult.wsUrl,
);
} catch (error) {
updateCommandResult(

View File

@@ -11,6 +11,7 @@ import type React from "react";
import { useState } from "react";
import { getServerUrl } from "../../agent/client";
import { settingsManager } from "../../settings-manager";
import { registerWithCloud } from "../../websocket/listen-register";
import { ListenerStatusUI } from "../components/ListenerStatusUI";
/**
@@ -154,50 +155,22 @@ export async function runListenSubcommand(argv: string[]): Promise<number> {
// Register with cloud
const serverUrl = getServerUrl();
const registerUrl = `${serverUrl}/v1/environments/register`;
if (debugMode) {
console.log(`[${formatTimestamp()}] Registering with ${registerUrl}`);
console.log(
`[${formatTimestamp()}] Registering with ${serverUrl}/v1/environments/register`,
);
console.log(`[${formatTimestamp()}] deviceId: ${deviceId}`);
console.log(`[${formatTimestamp()}] connectionName: ${connectionName}`);
}
const registerResponse = await fetch(registerUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${apiKey}`,
"X-Letta-Source": "letta-code",
},
body: JSON.stringify({
deviceId,
connectionName,
}),
const { connectionId, wsUrl } = await registerWithCloud({
serverUrl,
apiKey,
deviceId,
connectionName,
});
if (!registerResponse.ok) {
let errorMessage = `Registration failed (HTTP ${registerResponse.status})`;
try {
const error = (await registerResponse.json()) as { message?: string };
if (error.message) errorMessage = error.message;
} catch {
const text = await registerResponse.text().catch(() => "");
if (text) errorMessage += `: ${text.slice(0, 200)}`;
}
console.error(errorMessage);
return 1;
}
let registerBody: { connectionId: string; wsUrl: string };
try {
registerBody = (await registerResponse.json()) as typeof registerBody;
} catch {
throw new Error(
"Registration endpoint returned non-JSON response — is the server running?",
);
}
const { connectionId, wsUrl } = registerBody;
if (debugMode) {
console.log(`[${formatTimestamp()}] Registered successfully`);
console.log(`[${formatTimestamp()}] connectionId: ${connectionId}`);

View File

@@ -0,0 +1,99 @@
import { beforeEach, describe, expect, it, mock } from "bun:test";
import { registerWithCloud } from "../../websocket/listen-register";
const defaultOpts = {
serverUrl: "https://api.example.com",
apiKey: "sk-test-key",
deviceId: "device-123",
connectionName: "test-machine",
};
const mockFetch = mock(() => {
throw new Error("fetch not mocked for this test");
});
beforeEach(() => {
mockFetch.mockReset();
globalThis.fetch = mockFetch as unknown as typeof globalThis.fetch;
});
describe("registerWithCloud", () => {
it("returns connectionId and wsUrl on successful JSON response", async () => {
mockFetch.mockResolvedValueOnce(
new Response(
JSON.stringify({ connectionId: "conn-1", wsUrl: "wss://example.com" }),
{ status: 200, headers: { "Content-Type": "application/json" } },
),
);
const result = await registerWithCloud(defaultOpts);
expect(result).toEqual({
connectionId: "conn-1",
wsUrl: "wss://example.com",
});
expect(mockFetch).toHaveBeenCalledTimes(1);
const [url, init] = mockFetch.mock.calls[0] as unknown as [
string,
RequestInit,
];
expect(url).toBe("https://api.example.com/v1/environments/register");
expect(init.method).toBe("POST");
expect((init.headers as Record<string, string>).Authorization).toBe(
"Bearer sk-test-key",
);
expect((init.headers as Record<string, string>)["X-Letta-Source"]).toBe(
"letta-code",
);
const body = JSON.parse(init.body as string);
expect(body).toEqual({
deviceId: "device-123",
connectionName: "test-machine",
});
});
it("throws with body message on non-OK response with JSON error", async () => {
mockFetch.mockResolvedValueOnce(
new Response(JSON.stringify({ message: "Unauthorized" }), {
status: 401,
headers: { "Content-Type": "application/json" },
}),
);
await expect(registerWithCloud(defaultOpts)).rejects.toThrow(
"Unauthorized",
);
});
it("throws with HTTP status and truncated body on non-OK non-JSON response", async () => {
mockFetch.mockResolvedValueOnce(
new Response("<html>Bad Gateway</html>", { status: 502 }),
);
await expect(registerWithCloud(defaultOpts)).rejects.toThrow(
"HTTP 502: <html>Bad Gateway</html>",
);
});
it("throws actionable message on 200 with non-JSON body", async () => {
mockFetch.mockResolvedValueOnce(new Response("OK", { status: 200 }));
await expect(registerWithCloud(defaultOpts)).rejects.toThrow(
"is the server running?",
);
});
it("throws on unexpected response shape (missing fields)", async () => {
mockFetch.mockResolvedValueOnce(
new Response(JSON.stringify({ connectionId: "conn-1" }), {
status: 200,
headers: { "Content-Type": "application/json" },
}),
);
await expect(registerWithCloud(defaultOpts)).rejects.toThrow(
"missing connectionId or wsUrl",
);
});
});

View File

@@ -0,0 +1,81 @@
/**
* Shared registration helper for letta remote / /remote command.
* Owns the HTTP request contract and error handling; callers own UX strings and logging.
*/
export interface RegisterResult {
connectionId: string;
wsUrl: string;
}
export interface RegisterOptions {
serverUrl: string;
apiKey: string;
deviceId: string;
connectionName: string;
}
/**
* Register this device with the Letta Cloud environments endpoint.
* Throws on any failure with an error message suitable for wrapping in caller-specific context.
*/
export async function registerWithCloud(
opts: RegisterOptions,
): Promise<RegisterResult> {
const registerUrl = `${opts.serverUrl}/v1/environments/register`;
const response = await fetch(registerUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${opts.apiKey}`,
"X-Letta-Source": "letta-code",
},
body: JSON.stringify({
deviceId: opts.deviceId,
connectionName: opts.connectionName,
}),
});
if (!response.ok) {
let detail = `HTTP ${response.status}`;
const text = await response.text().catch(() => "");
if (text) {
try {
const parsed = JSON.parse(text) as { message?: string };
if (parsed.message) {
detail = parsed.message;
} else {
detail += `: ${text.slice(0, 200)}`;
}
} catch {
detail += `: ${text.slice(0, 200)}`;
}
}
throw new Error(detail);
}
let body: unknown;
try {
body = await response.json();
} catch {
throw new Error(
"Server returned non-JSON response — is the server running?",
);
}
const result = body as Record<string, unknown>;
if (
typeof result.connectionId !== "string" ||
typeof result.wsUrl !== "string"
) {
throw new Error(
"Server returned unexpected response shape (missing connectionId or wsUrl)",
);
}
return {
connectionId: result.connectionId,
wsUrl: result.wsUrl,
};
}