fix: restore tsc build and plugin install tests

This commit is contained in:
Peter Steinberger
2026-01-31 07:51:26 +00:00
parent c4feb7a457
commit a42e1c82d9
26 changed files with 90 additions and 86 deletions

View File

@@ -1083,10 +1083,11 @@ export function createExecTool(
timeoutMs: DEFAULT_APPROVAL_TIMEOUT_MS, timeoutMs: DEFAULT_APPROVAL_TIMEOUT_MS,
}, },
); );
decision = const decisionValue =
decisionResult && typeof decisionResult === "object" decisionResult && typeof decisionResult === "object"
? (decisionResult.decision ?? null) ? (decisionResult as { decision?: unknown }).decision
: null; : undefined;
decision = typeof decisionValue === "string" ? decisionValue : null;
} catch { } catch {
emitExecSystemEvent( emitExecSystemEvent(
`Exec denied (node=${nodeId} id=${approvalId}, approval-request-failed): ${commandText}`, `Exec denied (node=${nodeId} id=${approvalId}, approval-request-failed): ${commandText}`,
@@ -1177,28 +1178,32 @@ export function createExecTool(
} }
const startedAt = Date.now(); const startedAt = Date.now();
const raw = await callGatewayTool<{ const raw = await callGatewayTool(
payload: { "node.invoke",
exitCode: number; { timeoutMs: invokeTimeoutMs },
success?: string; buildInvokeParams(false, null),
stdout?: string; );
stderr?: string; const payload =
error?: string; raw && typeof raw === "object" ? (raw as { payload?: unknown }).payload : undefined;
}; const payloadObj =
}>("node.invoke", { timeoutMs: invokeTimeoutMs }, buildInvokeParams(false, null)); payload && typeof payload === "object" ? (payload as Record<string, unknown>) : {};
const payload = raw?.payload ?? {}; const stdout = typeof payloadObj.stdout === "string" ? payloadObj.stdout : "";
const stderr = typeof payloadObj.stderr === "string" ? payloadObj.stderr : "";
const errorText = typeof payloadObj.error === "string" ? payloadObj.error : "";
const success = typeof payloadObj.success === "boolean" ? payloadObj.success : false;
const exitCode = typeof payloadObj.exitCode === "number" ? payloadObj.exitCode : null;
return { return {
content: [ content: [
{ {
type: "text", type: "text",
text: payload.stdout || payload.stderr || payload.error || "", text: stdout || stderr || errorText || "",
}, },
], ],
details: { details: {
status: payload.success ? "completed" : "failed", status: success ? "completed" : "failed",
exitCode: payload.exitCode ?? null, exitCode,
durationMs: Date.now() - startedAt, durationMs: Date.now() - startedAt,
aggregated: [payload.stdout, payload.stderr, payload.error].filter(Boolean).join("\n"), aggregated: [stdout, stderr, errorText].filter(Boolean).join("\n"),
cwd: workdir, cwd: workdir,
} satisfies ExecToolDetails, } satisfies ExecToolDetails,
}; };
@@ -1261,10 +1266,11 @@ export function createExecTool(
timeoutMs: DEFAULT_APPROVAL_TIMEOUT_MS, timeoutMs: DEFAULT_APPROVAL_TIMEOUT_MS,
}, },
); );
decision = const decisionValue =
decisionResult && typeof decisionResult === "object" decisionResult && typeof decisionResult === "object"
? (decisionResult.decision ?? null) ? (decisionResult as { decision?: unknown }).decision
: null; : undefined;
decision = typeof decisionValue === "string" ? decisionValue : null;
} catch { } catch {
emitExecSystemEvent( emitExecSystemEvent(
`Exec denied (gateway id=${approvalId}, approval-request-failed): ${commandText}`, `Exec denied (gateway id=${approvalId}, approval-request-failed): ${commandText}`,

View File

@@ -361,7 +361,7 @@ async function mapWithConcurrency<T, R>(
opts?: { onProgress?: (completed: number, total: number) => void }, opts?: { onProgress?: (completed: number, total: number) => void },
): Promise<R[]> { ): Promise<R[]> {
const limit = Math.max(1, Math.floor(concurrency)); const limit = Math.max(1, Math.floor(concurrency));
const results: R[] = Array.from({ length: items.length }); const results: R[] = Array.from({ length: items.length }, () => undefined as R);
let nextIndex = 0; let nextIndex = 0;
let completed = 0; let completed = 0;

View File

@@ -869,7 +869,7 @@ export async function runEmbeddedAttempt(
const lastAssistant = messagesSnapshot const lastAssistant = messagesSnapshot
.slice() .slice()
.toReversed() .toReversed()
.find((m) => m?.role === "assistant"); .find((m) => m.role === "assistant");
const toolMetasNormalized = toolMetas const toolMetasNormalized = toolMetas
.filter( .filter(

View File

@@ -1,5 +1,5 @@
import type { OpenClawConfig } from "../config/config.js"; import type { OpenClawConfig } from "../config/config.js";
import { SkillsInstallPreferences } from "./skills/types.js"; import type { SkillsInstallPreferences } from "./skills/types.js";
export { export {
hasBinary, hasBinary,
@@ -38,7 +38,7 @@ export function resolveSkillsInstallPreferences(config?: OpenClawConfig): Skills
const preferBrew = raw?.preferBrew ?? true; const preferBrew = raw?.preferBrew ?? true;
const managerRaw = typeof raw?.nodeManager === "string" ? raw.nodeManager.trim() : ""; const managerRaw = typeof raw?.nodeManager === "string" ? raw.nodeManager.trim() : "";
const manager = managerRaw.toLowerCase(); const manager = managerRaw.toLowerCase();
const nodeManager = const nodeManager: SkillsInstallPreferences["nodeManager"] =
manager === "pnpm" || manager === "yarn" || manager === "bun" || manager === "npm" manager === "pnpm" || manager === "yarn" || manager === "bun" || manager === "npm"
? manager ? manager
: "npm"; : "npm";

View File

@@ -382,10 +382,11 @@ export async function runSubagentAnnounceFlow(params: {
}, },
timeoutMs: waitMs + 2000, timeoutMs: waitMs + 2000,
}); });
const waitError = typeof wait?.error === "string" ? wait.error : undefined;
if (wait?.status === "timeout") { if (wait?.status === "timeout") {
outcome = { status: "timeout" }; outcome = { status: "timeout" };
} else if (wait?.status === "error") { } else if (wait?.status === "error") {
outcome = { status: "error", error: wait.error }; outcome = { status: "error", error: waitError };
} else if (wait?.status === "ok") { } else if (wait?.status === "ok") {
outcome = { status: "ok" }; outcome = { status: "ok" };
} }

View File

@@ -355,8 +355,9 @@ async function waitForSubagentCompletion(runId: string, waitTimeoutMs: number) {
entry.endedAt = Date.now(); entry.endedAt = Date.now();
mutated = true; mutated = true;
} }
const waitError = typeof wait.error === "string" ? wait.error : undefined;
entry.outcome = entry.outcome =
wait.status === "error" ? { status: "error", error: wait.error } : { status: "ok" }; wait.status === "error" ? { status: "error", error: waitError } : { status: "ok" };
mutated = true; mutated = true;
if (mutated) { if (mutated) {
persistSubagentRuns(); persistSubagentRuns();

View File

@@ -149,10 +149,10 @@ async function callBrowserProxy(params: {
(typeof payload?.payloadJSON === "string" && payload.payloadJSON (typeof payload?.payloadJSON === "string" && payload.payloadJSON
? (JSON.parse(payload.payloadJSON) as BrowserProxyResult) ? (JSON.parse(payload.payloadJSON) as BrowserProxyResult)
: null); : null);
if (!parsed || typeof parsed !== "object") { if (!parsed || typeof parsed !== "object" || !("result" in parsed)) {
throw new Error("browser proxy failed"); throw new Error("browser proxy failed");
} }
return parsed; return parsed as BrowserProxyResult;
} }
async function persistProxyFiles(files: BrowserProxyFile[] | undefined) { async function persistProxyFiles(files: BrowserProxyFile[] | undefined) {

View File

@@ -26,7 +26,7 @@ export function resolveGatewayOptions(opts?: GatewayCallOptions) {
return { url, token, timeoutMs }; return { url, token, timeoutMs };
} }
export async function callGatewayTool<T = unknown>( export async function callGatewayTool<T = Record<string, unknown>>(
method: string, method: string,
opts: GatewayCallOptions, opts: GatewayCallOptions,
params?: unknown, params?: unknown,

View File

@@ -383,9 +383,8 @@ export const handleSubagentsCommand: CommandHandler = async (params, allowTextCo
}, },
timeoutMs: 10_000, timeoutMs: 10_000,
}); });
if (response?.runId) { const responseRunId = typeof response?.runId === "string" ? response.runId : undefined;
runId = response.runId; if (responseRunId) runId = responseRunId;
}
} catch (err) { } catch (err) {
const messageText = const messageText =
err instanceof Error ? err.message : typeof err === "string" ? err : "error"; err instanceof Error ? err.message : typeof err === "string" ? err : "error";
@@ -405,10 +404,11 @@ export const handleSubagentsCommand: CommandHandler = async (params, allowTextCo
}; };
} }
if (wait?.status === "error") { if (wait?.status === "error") {
const waitError = typeof wait.error === "string" ? wait.error : "unknown error";
return { return {
shouldContinue: false, shouldContinue: false,
reply: { reply: {
text: `⚠️ Subagent error: ${wait.error ?? "unknown error"} (run ${runId.slice(0, 8)}).`, text: `⚠️ Subagent error: ${waitError} (run ${runId.slice(0, 8)}).`,
}, },
}; };
} }

View File

@@ -254,8 +254,7 @@ export function registerGatewayCli(program: Command) {
return; return;
} }
const rich = isRich(); const rich = isRich();
const obj = const obj: Record<string, unknown> = result && typeof result === "object" ? result : {};
result && typeof result === "object" ? (result as Record<string, unknown>) : {};
const durationMs = typeof obj.durationMs === "number" ? obj.durationMs : null; const durationMs = typeof obj.durationMs === "number" ? obj.durationMs : null;
defaultRuntime.log(colorize(rich, theme.heading, "Gateway Health")); defaultRuntime.log(colorize(rich, theme.heading, "Gateway Health"));
defaultRuntime.log( defaultRuntime.log(

View File

@@ -107,10 +107,8 @@ export function registerNodesStatusCommands(nodes: Command) {
const connectedOnly = Boolean(opts.connected); const connectedOnly = Boolean(opts.connected);
const sinceMs = parseSinceMs(opts.lastConnected, "Invalid --last-connected"); const sinceMs = parseSinceMs(opts.lastConnected, "Invalid --last-connected");
const result = await callGatewayCli("node.list", opts, {}); const result = await callGatewayCli("node.list", opts, {});
const obj = const obj: Record<string, unknown> =
typeof result === "object" && result !== null typeof result === "object" && result !== null ? result : {};
? (result as Record<string, unknown>)
: {};
const { ok, warn, muted } = getNodesTheme(); const { ok, warn, muted } = getNodesTheme();
const tableWidth = Math.max(60, (process.stdout.columns ?? 120) - 1); const tableWidth = Math.max(60, (process.stdout.columns ?? 120) - 1);
const now = Date.now(); const now = Date.now();
@@ -227,10 +225,8 @@ export function registerNodesStatusCommands(nodes: Command) {
return; return;
} }
const obj = const obj: Record<string, unknown> =
typeof result === "object" && result !== null typeof result === "object" && result !== null ? result : {};
? (result as Record<string, unknown>)
: {};
const displayName = typeof obj.displayName === "string" ? obj.displayName : nodeId; const displayName = typeof obj.displayName === "string" ? obj.displayName : nodeId;
const connected = Boolean(obj.connected); const connected = Boolean(obj.connected);
const paired = Boolean(obj.paired); const paired = Boolean(obj.paired);

View File

@@ -268,7 +268,7 @@ export async function channelsStatusCommand(
runtime.log(JSON.stringify(payload, null, 2)); runtime.log(JSON.stringify(payload, null, 2));
return; return;
} }
runtime.log(formatGatewayChannelsStatusLines(payload as Record<string, unknown>).join("\n")); runtime.log(formatGatewayChannelsStatusLines(payload).join("\n"));
} catch (err) { } catch (err) {
runtime.error(`Gateway not reachable: ${String(err)}`); runtime.error(`Gateway not reachable: ${String(err)}`);
const cfg = await requireValidConfig(runtime); const cfg = await requireValidConfig(runtime);

View File

@@ -30,7 +30,7 @@ export async function checkGatewayHealth(params: {
if (healthOk) { if (healthOk) {
try { try {
const status = await callGateway<Record<string, unknown>>({ const status = await callGateway({
method: "channels.status", method: "channels.status",
params: { probe: true, timeoutMs: 5000 }, params: { probe: true, timeoutMs: 5000 },
timeoutMs: 6000, timeoutMs: 6000,

View File

@@ -230,7 +230,7 @@ export async function statusAllCommand(
: { error: gatewayProbe?.error ?? "gateway unreachable" }; : { error: gatewayProbe?.error ?? "gateway unreachable" };
const channelsStatus = gatewayReachable const channelsStatus = gatewayReachable
? await callGateway<Record<string, unknown>>({ ? await callGateway({
method: "channels.status", method: "channels.status",
params: { probe: false, timeoutMs: opts?.timeoutMs ?? 10_000 }, params: { probe: false, timeoutMs: opts?.timeoutMs ?? 10_000 },
timeoutMs: Math.min(8000, opts?.timeoutMs ?? 10_000), timeoutMs: Math.min(8000, opts?.timeoutMs ?? 10_000),

View File

@@ -127,7 +127,7 @@ export async function scanStatus(
progress.setLabel("Querying channel status…"); progress.setLabel("Querying channel status…");
const channelsStatus = gatewayReachable const channelsStatus = gatewayReachable
? await callGateway<Record<string, unknown>>({ ? await callGateway({
method: "channels.status", method: "channels.status",
params: { params: {
probe: false, probe: false,

View File

@@ -16,6 +16,14 @@ type DiscordChannelSummary = {
archived?: boolean; archived?: boolean;
}; };
type DiscordChannelPayload = {
id?: string;
name?: string;
type?: number;
guild_id?: string;
thread_metadata?: { archived?: boolean };
};
export type DiscordChannelResolution = { export type DiscordChannelResolution = {
input: string; input: string;
resolved: boolean; resolved: boolean;
@@ -83,27 +91,23 @@ async function listGuildChannels(
fetcher: typeof fetch, fetcher: typeof fetch,
guildId: string, guildId: string,
): Promise<DiscordChannelSummary[]> { ): Promise<DiscordChannelSummary[]> {
const raw = await fetchDiscord<Array<DiscordChannelSummary>>( const raw = await fetchDiscord<DiscordChannelPayload[]>(
`/guilds/${guildId}/channels`, `/guilds/${guildId}/channels`,
token, token,
fetcher, fetcher,
); );
return raw return raw
.filter((channel) => Boolean(channel.id) && "name" in channel)
.map((channel) => { .map((channel) => {
const archived = const archived = channel.thread_metadata?.archived;
"thread_metadata" in channel
? (channel as { thread_metadata?: { archived?: boolean } }).thread_metadata?.archived
: undefined;
return { return {
id: channel.id, id: typeof channel.id === "string" ? channel.id : "",
name: "name" in channel ? (channel.name ?? "") : "", name: typeof channel.name === "string" ? channel.name : "",
guildId, guildId,
type: channel.type, type: channel.type,
archived, archived,
}; };
}) })
.filter((channel) => Boolean(channel.name)); .filter((channel) => Boolean(channel.id) && Boolean(channel.name));
} }
async function fetchChannel( async function fetchChannel(
@@ -111,18 +115,12 @@ async function fetchChannel(
fetcher: typeof fetch, fetcher: typeof fetch,
channelId: string, channelId: string,
): Promise<DiscordChannelSummary | null> { ): Promise<DiscordChannelSummary | null> {
const raw = await fetchDiscord<DiscordChannelSummary & { guild_id: string }>( const raw = await fetchDiscord<DiscordChannelPayload>(`/channels/${channelId}`, token, fetcher);
`/channels/${channelId}`, if (!raw || typeof raw.guild_id !== "string" || typeof raw.id !== "string") return null;
token,
fetcher,
);
if (!raw || !("guild_id" in raw)) {
return null;
}
return { return {
id: raw.id, id: raw.id,
name: "name" in raw ? (raw.name ?? "") : "", name: typeof raw.name === "string" ? raw.name : "",
guildId: raw.guild_id ?? "", guildId: raw.guild_id,
type: raw.type, type: raw.type,
}; };
} }

View File

@@ -109,7 +109,9 @@ export function buildGatewayConnectionDetails(
}; };
} }
export async function callGateway<T = unknown>(opts: CallGatewayOptions): Promise<T> { export async function callGateway<T = Record<string, unknown>>(
opts: CallGatewayOptions,
): Promise<T> {
const timeoutMs = opts.timeoutMs ?? 10_000; const timeoutMs = opts.timeoutMs ?? 10_000;
const config = opts.config ?? loadConfig(); const config = opts.config ?? loadConfig();
const isRemoteMode = config.gateway?.mode === "remote"; const isRemoteMode = config.gateway?.mode === "remote";

View File

@@ -411,7 +411,7 @@ export class GatewayClient {
return null; return null;
} }
async request<T = unknown>( async request<T = Record<string, unknown>>(
method: string, method: string,
params?: unknown, params?: unknown,
opts?: { expectFinal?: boolean }, opts?: { expectFinal?: boolean },

View File

@@ -329,7 +329,7 @@ describeLive("gateway live (cli backend)", () => {
providerId === "codex-cli" providerId === "codex-cli"
? `Please include the token CLI-BACKEND-${nonce} in your reply.` ? `Please include the token CLI-BACKEND-${nonce} in your reply.`
: `Reply with exactly: CLI backend OK ${nonce}.`; : `Reply with exactly: CLI backend OK ${nonce}.`;
const payload = await client.request<Record<string, unknown>>( const payload = await client.request(
"agent", "agent",
{ {
sessionKey, sessionKey,
@@ -356,7 +356,7 @@ describeLive("gateway live (cli backend)", () => {
providerId === "codex-cli" providerId === "codex-cli"
? `Please include the token CLI-RESUME-${resumeNonce} in your reply.` ? `Please include the token CLI-RESUME-${resumeNonce} in your reply.`
: `Reply with exactly: CLI backend RESUME OK ${resumeNonce}.`; : `Reply with exactly: CLI backend RESUME OK ${resumeNonce}.`;
const resumePayload = await client.request<Record<string, unknown>>( const resumePayload = await client.request(
"agent", "agent",
{ {
sessionKey, sessionKey,
@@ -383,7 +383,7 @@ describeLive("gateway live (cli backend)", () => {
const imageBase64 = renderCatNoncePngBase64(imageCode); const imageBase64 = renderCatNoncePngBase64(imageCode);
const runIdImage = randomUUID(); const runIdImage = randomUUID();
const imageProbe = await client.request<Record<string, unknown>>( const imageProbe = await client.request(
"agent", "agent",
{ {
sessionKey, sessionKey,

View File

@@ -603,10 +603,10 @@ async function runGatewayModelSuite(params: GatewayModelSuiteParams) {
// Ensure session exists + override model for this run. // Ensure session exists + override model for this run.
// Reset between models: avoids cross-provider transcript incompatibilities // Reset between models: avoids cross-provider transcript incompatibilities
// (notably OpenAI Responses requiring reasoning replay for function_call items). // (notably OpenAI Responses requiring reasoning replay for function_call items).
await client.request<Record<string, unknown>>("sessions.reset", { await client.request("sessions.reset", {
key: sessionKey, key: sessionKey,
}); });
await client.request<Record<string, unknown>>("sessions.patch", { await client.request("sessions.patch", {
key: sessionKey, key: sessionKey,
model: modelKey, model: modelKey,
}); });
@@ -1164,11 +1164,11 @@ describeLive("gateway live (dev agent, profile keys)", () => {
try { try {
const sessionKey = `agent:${agentId}:live-zai-fallback`; const sessionKey = `agent:${agentId}:live-zai-fallback`;
await client.request<Record<string, unknown>>("sessions.patch", { await client.request("sessions.patch", {
key: sessionKey, key: sessionKey,
model: "anthropic/claude-opus-4-5", model: "anthropic/claude-opus-4-5",
}); });
await client.request<Record<string, unknown>>("sessions.reset", { await client.request("sessions.reset", {
key: sessionKey, key: sessionKey,
}); });
@@ -1200,7 +1200,7 @@ describeLive("gateway live (dev agent, profile keys)", () => {
throw new Error(`anthropic tool probe missing nonce: ${toolText}`); throw new Error(`anthropic tool probe missing nonce: ${toolText}`);
} }
await client.request<Record<string, unknown>>("sessions.patch", { await client.request("sessions.patch", {
key: sessionKey, key: sessionKey,
model: "zai/glm-4.7", model: "zai/glm-4.7",
}); });

View File

@@ -109,7 +109,7 @@ describe("gateway e2e", () => {
try { try {
const sessionKey = "agent:dev:mock-openai"; const sessionKey = "agent:dev:mock-openai";
await client.request<Record<string, unknown>>("sessions.patch", { await client.request("sessions.patch", {
key: sessionKey, key: sessionKey,
model: "openai/gpt-5.2", model: "openai/gpt-5.2",
}); });

View File

@@ -125,7 +125,7 @@ export async function sendMessageIMessage(
const client = opts.client ?? (await createIMessageRpcClient({ cliPath, dbPath })); const client = opts.client ?? (await createIMessageRpcClient({ cliPath, dbPath }));
const shouldClose = !opts.client; const shouldClose = !opts.client;
try { try {
const result = await client.request<Record<string, unknown>>("send", params, { const result = await client.request("send", params, {
timeoutMs: opts.timeoutMs, timeoutMs: opts.timeoutMs,
}); });
const resolvedId = resolveMessageId(result); const resolvedId = resolveMessageId(result);

View File

@@ -106,7 +106,7 @@ describe("installPluginFromArchive", () => {
}), }),
"utf-8", "utf-8",
); );
fs.writeFileSync(path.join(pkgDir, "dist", "index.js"), "export {};", "utf-8"); fs.writeFileSync(path.join(pkgDir, "dist", "index.mjs"), "export {};", "utf-8");
const archivePath = packToArchive({ const archivePath = packToArchive({
pkgDir, pkgDir,
@@ -127,7 +127,7 @@ describe("installPluginFromArchive", () => {
expect(result.pluginId).toBe("voice-call"); expect(result.pluginId).toBe("voice-call");
expect(result.targetDir).toBe(path.join(stateDir, "extensions", "voice-call")); expect(result.targetDir).toBe(path.join(stateDir, "extensions", "voice-call"));
expect(fs.existsSync(path.join(result.targetDir, "package.json"))).toBe(true); expect(fs.existsSync(path.join(result.targetDir, "package.json"))).toBe(true);
expect(fs.existsSync(path.join(result.targetDir, "dist", "index.js"))).toBe(true); expect(fs.existsSync(path.join(result.targetDir, "dist", "index.mjs"))).toBe(true);
}); });
it("rejects installing when plugin already exists", async () => { it("rejects installing when plugin already exists", async () => {
@@ -203,7 +203,7 @@ describe("installPluginFromArchive", () => {
expect(result.pluginId).toBe("zipper"); expect(result.pluginId).toBe("zipper");
expect(result.targetDir).toBe(path.join(stateDir, "extensions", "zipper")); expect(result.targetDir).toBe(path.join(stateDir, "extensions", "zipper"));
expect(fs.existsSync(path.join(result.targetDir, "package.json"))).toBe(true); expect(fs.existsSync(path.join(result.targetDir, "package.json"))).toBe(true);
expect(fs.existsSync(path.join(result.targetDir, "dist", "index.js"))).toBe(true); expect(fs.existsSync(path.join(result.targetDir, "dist", "index.mjs"))).toBe(true);
}); });
it("allows updates when mode is update", async () => { it("allows updates when mode is update", async () => {

View File

@@ -45,10 +45,10 @@ export async function configureGatewayForOnboarding(
10, 10,
); );
let bind = let bind: GatewayWizardSettings["bind"] =
flow === "quickstart" flow === "quickstart"
? quickstartGateway.bind ? quickstartGateway.bind
: await prompter.select({ : await prompter.select<GatewayWizardSettings["bind"]>({
message: "Gateway bind", message: "Gateway bind",
options: [ options: [
{ value: "loopback", label: "Loopback (127.0.0.1)" }, { value: "loopback", label: "Loopback (127.0.0.1)" },
@@ -107,10 +107,10 @@ export async function configureGatewayForOnboarding(
initialValue: "token", initialValue: "token",
})) as GatewayAuthChoice); })) as GatewayAuthChoice);
const tailscaleMode = const tailscaleMode: GatewayWizardSettings["tailscaleMode"] =
flow === "quickstart" flow === "quickstart"
? quickstartGateway.tailscaleMode ? quickstartGateway.tailscaleMode
: await prompter.select({ : await prompter.select<GatewayWizardSettings["tailscaleMode"]>({
message: "Tailscale exposure", message: "Tailscale exposure",
options: [ options: [
{ value: "off", label: "Off", hint: "No Tailscale exposure" }, { value: "off", label: "Off", hint: "No Tailscale exposure" },

View File

@@ -240,7 +240,7 @@ describe("provider timeouts (e2e)", () => {
try { try {
const sessionKey = "agent:dev:timeout-fallback"; const sessionKey = "agent:dev:timeout-fallback";
await client.request<Record<string, unknown>>("sessions.patch", { await client.request("sessions.patch", {
key: sessionKey, key: sessionKey,
model: "primary/gpt-5.2", model: "primary/gpt-5.2",
}); });

View File

@@ -5,6 +5,7 @@
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"module": "NodeNext", "module": "NodeNext",
"moduleResolution": "NodeNext", "moduleResolution": "NodeNext",
"lib": ["DOM", "DOM.Iterable", "ES2023", "ScriptHost"],
"noEmit": true, "noEmit": true,
"noEmitOnError": true, "noEmitOnError": true,
"outDir": "dist", "outDir": "dist",