Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions .github/workflows/desktop-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ jobs:
# Keep one artifact per OS to stay under the repo's tight artifact quota.
# macOS arm64 only - Apple Silicon covers current Macs. The Intel (macos-13)
# runner is queue-starved and the x64 cross-compile is unreliable, so it is dropped.
mac='{"host":"macos-14","target":"darwin-arm64","platform_flag":"--mac dmg --arm64","group":"mac"}'
mac='{"host":"macos-14","target":"darwin-arm64","platform_flag":"--mac dmg zip --arm64","group":"mac"}'
linux='{"host":"ubuntu-24.04","target":"linux-amd64","platform_flag":"--linux deb --x64","group":"linux"}'
win='{"host":"windows-2025","target":"win-x64","platform_flag":"--win nsis --x64","group":"win"}'
case "$PLATFORMS" in
Expand Down Expand Up @@ -154,7 +154,10 @@ jobs:
compression-level: 9
retention-days: 1
path: |
packages/desktop/dist/latest*.yml
packages/desktop/dist/*.blockmap
packages/desktop/dist/*.dmg
packages/desktop/dist/*.zip
packages/desktop/dist/*.deb
packages/desktop/dist/*.exe

Expand All @@ -181,11 +184,17 @@ jobs:
version=$(node -p 'require("./packages/desktop/package.json").version.replace(/-.*/, "")')
tag="app-v${version}-main.${GITHUB_RUN_NUMBER}"

mapfile -t assets < <(find release-assets -type f \( -name '*.dmg' -o -name '*.deb' -o -name '*.exe' \) | sort)
mapfile -t assets < <(find release-assets -type f \( -name 'latest*.yml' -o -name '*.blockmap' -o -name '*.dmg' -o -name '*.zip' -o -name '*.deb' -o -name '*.exe' \) | sort)
if [ "${#assets[@]}" -eq 0 ]; then
echo "No desktop release assets found"
exit 1
fi
for required in latest.yml latest-mac.yml; do
if ! find release-assets -type f -name "$required" | grep -q .; then
echo "Missing updater metadata: $required"
exit 1
fi
done

gh release list --repo "$REPOSITORY" --limit 100 --json tagName \
--jq '.[] | select(.tagName | test("^app-v[0-9]+\\.[0-9]+\\.[0-9]+-main\\.")) | .tagName' \
Expand Down
2 changes: 1 addition & 1 deletion bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions packages/app/src/components/dialog-select-model.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { describe, expect, test } from "bun:test"
import { showFreeModelTag } from "./model-tags"

describe("showFreeModelTag", () => {
test("only labels DeepAgent managed zero-cost models as free", () => {
expect(showFreeModelTag({ provider: { id: "deepagent-code" }, cost: { input: 0 } })).toBe(true)
expect(showFreeModelTag({ provider: { id: "zhipuai" }, cost: { input: 0 } })).toBe(false)
expect(showFreeModelTag({ provider: { id: "deepagent-code" }, cost: { input: 1 } })).toBe(false)
})
})
7 changes: 3 additions & 4 deletions packages/app/src/components/dialog-select-model.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ import { List } from "@deepagent-code/ui/list"
import { Tooltip } from "@deepagent-code/ui/tooltip"
import { ModelTooltip } from "./model-tooltip"
import { useLanguage } from "@/context/language"

const isFree = (cost: { input: number } | undefined) => cost?.input === 0
import { showFreeModelTag } from "./model-tags"

type ModelState = ReturnType<typeof useLocal>["model"]

Expand Down Expand Up @@ -57,7 +56,7 @@ const ModelList: Component<{
class="w-full"
placement="right-start"
gutter={12}
value={<ModelTooltip model={item} latest={item.latest} free={isFree(item.cost)} />}
value={<ModelTooltip model={item} latest={item.latest} free={showFreeModelTag(item)} />}
>
{node}
</Tooltip>
Expand All @@ -72,7 +71,7 @@ const ModelList: Component<{
{(i) => (
<div class="w-full flex items-center gap-x-2 text-13-regular">
<span class="truncate">{i.name}</span>
<Show when={isFree(i.cost)}>
<Show when={showFreeModelTag(i)}>
<Tag>{language.t("model.tag.free")}</Tag>
</Show>
<Show when={i.latest}>
Expand Down
3 changes: 3 additions & 0 deletions packages/app/src/components/model-tags.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function showFreeModelTag(model: { cost?: { input: number }; provider: { id: string } }) {
return model.provider.id === "deepagent-code" && model.cost?.input === 0
}
6 changes: 6 additions & 0 deletions packages/app/src/components/updater-action.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ describe("updaterAction", () => {
label: "toast.update.action.installRestart",
run: "install",
})
expect(
updaterAction({ status: "ready", version: "2.0.0", manualUrl: "https://example.com/deepagent-code.deb" }),
).toEqual({
label: "toast.update.action.downloadDeb",
run: "install",
})
expect(updaterAction({ status: "installing", version: "2.0.0" })).toEqual({
label: "settings.updates.action.installing",
})
Expand Down
1 change: 1 addition & 0 deletions packages/app/src/components/updater-action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export function updaterAction(state: UpdaterState | undefined) {
case "downloading":
return { label: "settings.updates.action.downloading" as const }
case "ready":
if (state.manualUrl) return { label: "toast.update.action.downloadDeb" as const, run: "install" as const }
return { label: "toast.update.action.installRestart" as const, run: "install" as const }
case "installing":
return { label: "settings.updates.action.installing" as const }
Expand Down
2 changes: 1 addition & 1 deletion packages/app/src/context/local.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({

const validModel = (model: ModelKey) => {
const provider = providers.all().get(model.providerID)
return !!provider?.models[model.modelID] && connected().has(model.providerID)
return !!provider?.models[model.modelID] && connected().has(model.providerID) && models.visible(model)
}

const firstModel = (...items: Array<() => ModelKey | undefined>) => {
Expand Down
2 changes: 2 additions & 0 deletions packages/app/src/i18n/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -571,7 +571,9 @@ export const dict = {

"toast.update.title": "Update available",
"toast.update.description": "A new version of DeepAgent Code ({{version}}) is now available to install.",
"toast.update.description.deb": "A new version of DeepAgent Code ({{version}}) is available as a Linux .deb package.",
"toast.update.action.installRestart": "Install and restart",
"toast.update.action.downloadDeb": "Download .deb",
"toast.update.action.notYet": "Not yet",

"error.page.title": "Something went wrong",
Expand Down
2 changes: 2 additions & 0 deletions packages/app/src/i18n/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -507,7 +507,9 @@ export const dict = {
"toast.session.listFailed.title": "无法加载 {{project}} 的会话",
"toast.update.title": "有可用更新",
"toast.update.description": "DeepAgent Code 有新版本 ({{version}}) 可安装。",
"toast.update.description.deb": "DeepAgent Code 有新版本 ({{version}}) 可下载 Linux .deb 安装包。",
"toast.update.action.installRestart": "安装并重启",
"toast.update.action.downloadDeb": "下载 .deb",
"toast.update.action.notYet": "稍后",

"error.page.title": "出了点问题",
Expand Down
2 changes: 2 additions & 0 deletions packages/app/src/i18n/zht.ts
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,9 @@ export const dict = {

"toast.update.title": "有可用更新",
"toast.update.description": "DeepAgent Code 有新版本 ({{version}}) 可安裝。",
"toast.update.description.deb": "DeepAgent Code 有新版本 ({{version}}) 可下載 Linux .deb 安裝包。",
"toast.update.action.installRestart": "安裝並重新啟動",
"toast.update.action.downloadDeb": "下載 .deb",
"toast.update.action.notYet": "稍後",

"error.page.title": "出了點問題",
Expand Down
20 changes: 12 additions & 8 deletions packages/app/src/pages/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ import { Titlebar, type TitlebarUpdate } from "@/components/titlebar"
import { useDirectoryPicker } from "@/components/directory-picker"
import { ServerConnection, useServer } from "@/context/server"
import { useLanguage, type Locale } from "@/context/language"
import type { UpdaterState } from "@/updater"
import { pathKey } from "@/utils/path-key"
import {
displayName,
Expand Down Expand Up @@ -167,14 +168,14 @@ export default function Layout(props: ParentProps) {
peeked: false,
})

const updateVersion = () => {
const readyUpdate = () => {
const state = platform.updater?.state()
if (state?.status !== "ready") return
return state.version
return state
}
const installUpdate = () => void platform.updater?.install()
const titlebarUpdate: TitlebarUpdate = {
version: updateVersion,
version: () => readyUpdate()?.version,
installing: () => platform.updater?.state().status === "installing",
install: installUpdate,
}
Expand Down Expand Up @@ -2378,8 +2379,8 @@ export default function Layout(props: ParentProps) {
<div class="relative bg-background-base flex-1 min-h-0 min-w-0 flex flex-col select-none [&_input]:select-text [&_textarea]:select-text [&_[contenteditable]]:select-text">
{autoselecting() ?? ""}
<Titlebar update={titlebarUpdate} />
<Show when={updateVersion() !== undefined}>
<UpdateAvailableToast version={updateVersion() ?? ""} install={installUpdate} language={language} />
<Show when={readyUpdate()}>
{(update) => <UpdateAvailableToast update={update()} install={installUpdate} language={language} />}
</Show>
<div class="flex-1 min-h-0 min-w-0 flex">
<div class="flex-1 min-h-0 relative">
Expand Down Expand Up @@ -2530,21 +2531,24 @@ export default function Layout(props: ParentProps) {
}

function UpdateAvailableToast(props: {
version: string
update: Extract<UpdaterState, { status: "ready" }>
install: () => void
language: ReturnType<typeof useLanguage>
}) {
let toastId: number | undefined

onMount(() => {
const manual = Boolean(props.update.manualUrl)
toastId = showToast({
persistent: true,
icon: "download",
title: props.language.t("toast.update.title"),
description: props.language.t("toast.update.description", { version: props.version }),
description: props.language.t(manual ? "toast.update.description.deb" : "toast.update.description", {
version: props.update.version,
}),
actions: [
{
label: props.language.t("toast.update.action.installRestart"),
label: props.language.t(manual ? "toast.update.action.downloadDeb" : "toast.update.action.installRestart"),
onClick: props.install,
},
{
Expand Down
4 changes: 2 additions & 2 deletions packages/app/src/updater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ export type UpdaterState =
| { status: "idle" }
| { status: "checking" }
| { status: "downloading"; version: string; percent?: number }
| { status: "ready"; version: string }
| { status: "ready"; version: string; manualUrl?: string }
| { status: "up-to-date" }
| { status: "installing"; version: string }
| { status: "installing"; version: string; manualUrl?: string }
| { status: "error"; message: string }

export type UpdaterPlatform = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,13 @@ const prodAssetNames: Record<string, string> = {
"darwin-x64-dmg": "deepagent-code-desktop-mac-x64.dmg",
"windows-x64-nsis": "deepagent-code-desktop-win-x64.exe",
"linux-x64-deb": "deepagent-code-desktop-linux-amd64.deb",
"linux-x64-appimage": "deepagent-code-desktop-linux-x86_64.AppImage",
"linux-x64-rpm": "deepagent-code-desktop-linux-x86_64.rpm",
} satisfies Record<DownloadPlatform, string>

const betaAssetNames: Record<string, string> = {
"darwin-aarch64-dmg": "deepagent-code-desktop-mac-arm64.dmg",
"darwin-x64-dmg": "deepagent-code-desktop-mac-x64.dmg",
"windows-x64-nsis": "deepagent-code-desktop-win-x64.exe",
"linux-x64-deb": "deepagent-code-desktop-linux-amd64.deb",
"linux-x64-appimage": "deepagent-code-desktop-linux-x86_64.AppImage",
"linux-x64-rpm": "deepagent-code-desktop-linux-x86_64.rpm",
} satisfies Record<DownloadPlatform, string>

// Doing this on the server lets us preserve the original name for platforms we don't care to rename for
Expand Down
Loading
Loading