Skip to content

Commit f6120d3

Browse files
authored
Merge branch 'anomalyco:dev' into dev
2 parents f8d09d6 + f8cfb69 commit f6120d3

8 files changed

Lines changed: 134 additions & 13 deletions

File tree

packages/app/src/context/global-sync.tsx

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import type { ProjectMeta } from "./global-sync/types"
3636
import { SESSION_RECENT_LIMIT } from "./global-sync/types"
3737
import { sanitizeProject } from "./global-sync/utils"
3838
import { usePlatform } from "./platform"
39+
import { formatServerError } from "@/utils/server-errors"
3940

4041
type GlobalStore = {
4142
ready: boolean
@@ -51,12 +52,6 @@ type GlobalStore = {
5152
reload: undefined | "pending" | "complete"
5253
}
5354

54-
function errorMessage(error: unknown) {
55-
if (error instanceof Error && error.message) return error.message
56-
if (typeof error === "string" && error) return error
57-
return "Unknown error"
58-
}
59-
6055
function createGlobalSync() {
6156
const globalSDK = useGlobalSDK()
6257
const platform = usePlatform()
@@ -207,8 +202,9 @@ function createGlobalSync() {
207202
console.error("Failed to load sessions", err)
208203
const project = getFilename(directory)
209204
showToast({
205+
variant: "error",
210206
title: language.t("toast.session.listFailed.title", { project }),
211-
description: errorMessage(err),
207+
description: formatServerError(err),
212208
})
213209
})
214210

packages/app/src/context/global-sync/bootstrap.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { batch } from "solid-js"
1616
import { reconcile, type SetStoreFunction, type Store } from "solid-js/store"
1717
import type { State, VcsCache } from "./types"
1818
import { cmp, normalizeProviderList } from "./utils"
19+
import { formatServerError } from "@/utils/server-errors"
1920

2021
type GlobalStore = {
2122
ready: boolean
@@ -133,8 +134,11 @@ export async function bootstrapDirectory(input: {
133134
} catch (err) {
134135
console.error("Failed to bootstrap instance", err)
135136
const project = getFilename(input.directory)
136-
const message = err instanceof Error ? err.message : String(err)
137-
showToast({ title: `Failed to reload ${project}`, description: message })
137+
showToast({
138+
variant: "error",
139+
title: `Failed to reload ${project}`,
140+
description: formatServerError(err),
141+
})
138142
input.setStore("status", "partial")
139143
return
140144
}

packages/app/src/pages/session/file-tabs.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,12 @@ export function FileTabContent(props: { tab: string }) {
371371
})
372372
}
373373

374+
const cancelCommenting = () => {
375+
const p = path()
376+
if (p) file.setSelectedLines(p, null)
377+
setNote("commenting", null)
378+
}
379+
374380
createEffect(
375381
on(
376382
() => state()?.loaded,
@@ -484,7 +490,7 @@ export function FileTabContent(props: { tab: string }) {
484490
value={note.draft}
485491
selection={formatCommentLabel(range())}
486492
onInput={(value) => setNote("draft", value)}
487-
onCancel={() => setCommenting(null)}
493+
onCancel={cancelCommenting}
488494
onSubmit={(value) => {
489495
const p = path()
490496
if (!p) return
@@ -498,7 +504,7 @@ export function FileTabContent(props: { tab: string }) {
498504

499505
setTimeout(() => {
500506
if (!document.activeElement || !current.contains(document.activeElement)) {
501-
setCommenting(null)
507+
cancelCommenting()
502508
}
503509
}, 0)
504510
}}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { describe, expect, test } from "bun:test"
2+
import type { ConfigInvalidError } from "./server-errors"
3+
import { formatServerError, parseReabaleConfigInvalidError } from "./server-errors"
4+
5+
describe("parseReabaleConfigInvalidError", () => {
6+
test("formats issues with file path", () => {
7+
const error = {
8+
name: "ConfigInvalidError",
9+
data: {
10+
path: "opencode.config.ts",
11+
issues: [
12+
{ path: ["settings", "host"], message: "Required" },
13+
{ path: ["mode"], message: "Invalid" },
14+
],
15+
},
16+
} satisfies ConfigInvalidError
17+
18+
const result = parseReabaleConfigInvalidError(error)
19+
20+
expect(result).toBe(
21+
["Invalid configuration", "opencode.config.ts", "settings.host: Required", "mode: Invalid"].join("\n"),
22+
)
23+
})
24+
25+
test("uses trimmed message when issues are missing", () => {
26+
const error = {
27+
name: "ConfigInvalidError",
28+
data: {
29+
path: "config",
30+
message: " Bad value ",
31+
},
32+
} satisfies ConfigInvalidError
33+
34+
const result = parseReabaleConfigInvalidError(error)
35+
36+
expect(result).toBe(["Invalid configuration", "Bad value"].join("\n"))
37+
})
38+
})
39+
40+
describe("formatServerError", () => {
41+
test("formats config invalid errors", () => {
42+
const error = {
43+
name: "ConfigInvalidError",
44+
data: {
45+
message: "Missing host",
46+
},
47+
} satisfies ConfigInvalidError
48+
49+
const result = formatServerError(error)
50+
51+
expect(result).toBe(["Invalid configuration", "Missing host"].join("\n"))
52+
})
53+
54+
test("returns error messages", () => {
55+
expect(formatServerError(new Error("Request failed with status 503"))).toBe("Request failed with status 503")
56+
})
57+
58+
test("returns provided string errors", () => {
59+
expect(formatServerError("Failed to connect to server")).toBe("Failed to connect to server")
60+
})
61+
62+
test("falls back to unknown", () => {
63+
expect(formatServerError(0)).toBe("Unknown error")
64+
})
65+
66+
test("falls back for unknown error objects and names", () => {
67+
expect(formatServerError({ name: "ServerTimeoutError", data: { seconds: 30 } })).toBe("Unknown error")
68+
})
69+
})
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
export type ConfigInvalidError = {
2+
name: "ConfigInvalidError"
3+
data: {
4+
path?: string
5+
message?: string
6+
issues?: Array<{ message: string; path: string[] }>
7+
}
8+
}
9+
10+
export function formatServerError(error: unknown) {
11+
if (isConfigInvalidErrorLike(error)) return parseReabaleConfigInvalidError(error)
12+
if (error instanceof Error && error.message) return error.message
13+
if (typeof error === "string" && error) return error
14+
return "Unknown error"
15+
}
16+
17+
function isConfigInvalidErrorLike(error: unknown): error is ConfigInvalidError {
18+
if (typeof error !== "object" || error === null) return false
19+
const o = error as Record<string, unknown>
20+
return o.name === "ConfigInvalidError" && typeof o.data === "object" && o.data !== null
21+
}
22+
23+
export function parseReabaleConfigInvalidError(errorInput: ConfigInvalidError) {
24+
const head = "Invalid configuration"
25+
const file = errorInput.data.path && errorInput.data.path !== "config" ? errorInput.data.path : ""
26+
const detail = errorInput.data.message?.trim() ?? ""
27+
const issues = (errorInput.data.issues ?? []).map((issue) => {
28+
return `${issue.path.join(".")}: ${issue.message}`
29+
})
30+
if (issues.length) return [head, file, "", ...issues].filter(Boolean).join("\n")
31+
return [head, file, detail].filter(Boolean).join("\n")
32+
}

packages/console/app/src/routes/zen/util/handler.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import { createDataDumper } from "./dataDumper"
3434
import { createTrialLimiter } from "./trialLimiter"
3535
import { createStickyTracker } from "./stickyProviderTracker"
3636
import { LiteData } from "@opencode-ai/console-core/lite.js"
37+
import { Resource } from "@opencode-ai/console-resource"
3738

3839
type ZenData = Awaited<ReturnType<typeof ZenData.list>>
3940
type RetryOptions = {
@@ -59,7 +60,7 @@ export async function handler(
5960

6061
const MAX_FAILOVER_RETRIES = 3
6162
const MAX_429_RETRIES = 3
62-
const FREE_WORKSPACES = [
63+
const ADMIN_WORKSPACES = [
6364
"wrk_01K46JDFR0E75SG2Q8K172KF3Y", // frank
6465
"wrk_01K6W1A3VE0KMNVSCQT43BG2SX", // opencode bench
6566
]
@@ -520,6 +521,13 @@ export async function handler(
520521
)
521522

522523
if (!data) throw new AuthError("Invalid API key.")
524+
if (
525+
modelInfo.id.startsWith("alpha-") &&
526+
Resource.App.stage === "production" &&
527+
!ADMIN_WORKSPACES.includes(data.workspaceID)
528+
)
529+
throw new AuthError(`Model ${modelInfo.id} not supported`)
530+
523531
logger.metric({
524532
api_key: data.apiKey,
525533
workspace: data.workspaceID,
@@ -546,7 +554,7 @@ export async function handler(
546554
black: data.black,
547555
lite: data.lite,
548556
provider: data.provider,
549-
isFree: FREE_WORKSPACES.includes(data.workspaceID),
557+
isFree: ADMIN_WORKSPACES.includes(data.workspaceID),
550558
isDisabled: !!data.timeDisabled,
551559
}
552560
}

packages/console/app/src/routes/zen/v1/models.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export async function GET(input: APIEvent) {
2525
object: "list",
2626
data: Object.entries(zenData.models)
2727
.filter(([id]) => !disabledModels.includes(id))
28+
.filter(([id]) => !id.startsWith("alpha-"))
2829
.map(([id, _model]) => ({
2930
id,
3031
object: "model",

packages/ui/src/components/session-review.css

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@
1010
display: none;
1111
}
1212

13+
.scroll-view__viewport {
14+
display: flex;
15+
flex-direction: column;
16+
}
17+
1318
[data-slot="session-review-container"] {
1419
flex: 1 1 auto;
1520
padding-right: 4px;

0 commit comments

Comments
 (0)