Skip to content

Commit 503cb3a

Browse files
authored
Merge pull request #91 from NeuralNomadsAI/dev
Release v0.8.1 - Support apply_patch tool
2 parents 0250c63 + b0eb9ae commit 503cb3a

25 files changed

Lines changed: 1316 additions & 821 deletions

package-lock.json

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "codenomad-workspace",
3-
"version": "0.8.0",
3+
"version": "0.8.1",
44
"private": true,
55
"description": "CodeNomad monorepo workspace",
66
"workspaces": {
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{
2-
"minServerVersion": "0.8.0",
2+
"minServerVersion": "0.8.1",
33
"latestServerUrl": "https://github.com/NeuralNomadsAI/CodeNomad/releases/latest"
44
}

packages/electron-app/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@neuralnomads/codenomad-electron-app",
3-
"version": "0.8.0",
3+
"version": "0.8.1",
44
"description": "CodeNomad - AI coding assistant",
55
"author": {
66
"name": "Neural Nomads",

packages/opencode-config/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@
33
"version": "0.5.0",
44
"private": true,
55
"dependencies": {
6-
"@opencode-ai/plugin": "1.1.16"
6+
"@opencode-ai/plugin": "1.1.30"
77
}
88
}

packages/server/package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/server/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@neuralnomads/codenomad",
3-
"version": "0.8.0",
3+
"version": "0.8.1",
44
"description": "CodeNomad Server",
55
"author": {
66
"name": "Neural Nomads",
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import assert from "node:assert/strict"
2+
import { mkdtempSync, rmSync, writeFileSync } from "node:fs"
3+
import { mkdir } from "node:fs/promises"
4+
import os from "node:os"
5+
import path from "node:path"
6+
import { afterEach, beforeEach, describe, it } from "node:test"
7+
8+
import type { Logger } from "../../logger"
9+
import { resolveUi } from "../remote-ui"
10+
11+
const noopLogger: Logger = {
12+
debug: () => {},
13+
info: () => {},
14+
warn: () => {},
15+
error: () => {},
16+
trace: () => {},
17+
child: () => noopLogger,
18+
isLevelEnabled: () => false,
19+
} as any
20+
21+
let tempRoot: string
22+
23+
beforeEach(() => {
24+
tempRoot = mkdtempSync(path.join(os.tmpdir(), "codenomad-ui-test-"))
25+
})
26+
27+
afterEach(() => {
28+
rmSync(tempRoot, { recursive: true, force: true })
29+
})
30+
31+
describe("resolveUi local version preference", () => {
32+
it("prefers bundled when bundled version is higher", async () => {
33+
const bundledDir = path.join(tempRoot, "bundled")
34+
const configDir = path.join(tempRoot, "config")
35+
const currentDir = path.join(configDir, "ui", "current")
36+
37+
await mkdir(bundledDir, { recursive: true })
38+
await mkdir(currentDir, { recursive: true })
39+
40+
writeFileSync(path.join(bundledDir, "index.html"), "<html>bundled</html>")
41+
writeFileSync(path.join(bundledDir, "ui-version.json"), JSON.stringify({ uiVersion: "0.8.1" }))
42+
43+
writeFileSync(path.join(currentDir, "index.html"), "<html>current</html>")
44+
writeFileSync(path.join(currentDir, "ui-version.json"), JSON.stringify({ uiVersion: "0.8.0" }))
45+
46+
const result = await resolveUi({
47+
serverVersion: "0.8.1",
48+
bundledUiDir: bundledDir,
49+
autoUpdate: false,
50+
configDir,
51+
logger: noopLogger,
52+
})
53+
54+
assert.equal(result.source, "bundled")
55+
assert.equal(result.uiStaticDir, bundledDir)
56+
assert.equal(result.uiVersion, "0.8.1")
57+
})
58+
})

packages/server/src/ui/remote-ui.ts

Lines changed: 98 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -73,23 +73,13 @@ export async function resolveUi(options: RemoteUiOptions): Promise<UiResolution>
7373
const previousDir = path.join(uiRoot, "previous")
7474

7575
if (!options.autoUpdate) {
76-
const local = await resolveStaticUiDir(currentDir)
77-
if (local) {
78-
return {
79-
uiStaticDir: local,
80-
source: "downloaded",
81-
uiVersion: await readUiVersion(local),
82-
supported: true,
83-
}
84-
}
85-
86-
const bundled = await resolveStaticUiDir(options.bundledUiDir)
87-
return {
88-
uiStaticDir: bundled ?? options.bundledUiDir,
89-
source: bundled ? "bundled" : "missing",
90-
uiVersion: bundled ? await readUiVersion(bundled) : undefined,
76+
return await resolveFromCacheOrBundled({
77+
logger: options.logger,
78+
bundledUiDir: options.bundledUiDir,
79+
currentDir,
80+
previousDir,
9181
supported: true,
92-
}
82+
})
9383
}
9484

9585
let manifest: RemoteUiManifest | null = null
@@ -125,20 +115,28 @@ export async function resolveUi(options: RemoteUiOptions): Promise<UiResolution>
125115
})
126116
}
127117

128-
const currentVersion = await readUiVersion(currentDir)
129-
if (currentVersion && currentVersion === manifest.latestUIVersion) {
130-
const currentResolved = await resolveStaticUiDir(currentDir)
131-
if (currentResolved) {
132-
return {
133-
uiStaticDir: currentResolved,
134-
source: "downloaded",
135-
uiVersion: currentVersion,
136-
supported: true,
137-
latestServerVersion: manifest.latestServerVersion,
138-
latestServerUrl: manifest.latestServerUrl,
139-
minServerVersion: manifest.minServerVersion,
140-
}
141-
}
118+
const bestLocal = await pickBestLocalUi({
119+
logger: options.logger,
120+
bundledUiDir: options.bundledUiDir,
121+
currentDir,
122+
previousDir,
123+
})
124+
125+
const remoteIsNewer =
126+
!bestLocal ||
127+
compareSemverMaybe(manifest.latestUIVersion, bestLocal.uiVersion) > 0
128+
129+
if (!remoteIsNewer) {
130+
return await resolveFromCacheOrBundled({
131+
logger: options.logger,
132+
bundledUiDir: options.bundledUiDir,
133+
currentDir,
134+
previousDir,
135+
supported: true,
136+
latestServerVersion: manifest.latestServerVersion,
137+
latestServerUrl: manifest.latestServerUrl,
138+
minServerVersion: manifest.minServerVersion,
139+
})
142140
}
143141

144142
try {
@@ -206,40 +204,18 @@ async function resolveFromCacheOrBundled(args: {
206204
latestServerUrl?: string
207205
minServerVersion?: string
208206
}): Promise<UiResolution> {
209-
const currentResolved = await resolveStaticUiDir(args.currentDir)
210-
if (currentResolved) {
211-
return {
212-
uiStaticDir: currentResolved,
213-
source: "downloaded",
214-
uiVersion: await readUiVersion(currentResolved),
215-
supported: args.supported,
216-
message: args.message,
217-
latestServerVersion: args.latestServerVersion,
218-
latestServerUrl: args.latestServerUrl,
219-
minServerVersion: args.minServerVersion,
220-
}
221-
}
222-
223-
const previousResolved = await resolveStaticUiDir(args.previousDir)
224-
if (previousResolved) {
225-
return {
226-
uiStaticDir: previousResolved,
227-
source: "previous",
228-
uiVersion: await readUiVersion(previousResolved),
229-
supported: args.supported,
230-
message: args.message,
231-
latestServerVersion: args.latestServerVersion,
232-
latestServerUrl: args.latestServerUrl,
233-
minServerVersion: args.minServerVersion,
234-
}
235-
}
207+
const bestLocal = await pickBestLocalUi({
208+
logger: args.logger,
209+
bundledUiDir: args.bundledUiDir,
210+
currentDir: args.currentDir,
211+
previousDir: args.previousDir,
212+
})
236213

237-
const bundledResolved = await resolveStaticUiDir(args.bundledUiDir)
238-
if (bundledResolved) {
214+
if (bestLocal) {
239215
return {
240-
uiStaticDir: bundledResolved,
241-
source: "bundled",
242-
uiVersion: await readUiVersion(bundledResolved),
216+
uiStaticDir: bestLocal.uiStaticDir,
217+
source: bestLocal.source,
218+
uiVersion: bestLocal.uiVersion,
243219
supported: args.supported,
244220
message: args.message,
245221
latestServerVersion: args.latestServerVersion,
@@ -260,6 +236,66 @@ async function resolveFromCacheOrBundled(args: {
260236
}
261237
}
262238

239+
async function pickBestLocalUi(args: {
240+
logger: Logger
241+
bundledUiDir: string
242+
currentDir: string
243+
previousDir: string
244+
}): Promise<{ uiStaticDir: string; source: UiSource; uiVersion?: string } | null> {
245+
const candidates: Array<{ uiStaticDir: string; source: UiSource; uiVersion?: string; priority: number }> = []
246+
247+
const currentResolved = await resolveStaticUiDir(args.currentDir)
248+
if (currentResolved) {
249+
candidates.push({
250+
uiStaticDir: currentResolved,
251+
source: "downloaded",
252+
uiVersion: await readUiVersion(currentResolved),
253+
priority: 2,
254+
})
255+
}
256+
257+
const bundledResolved = await resolveStaticUiDir(args.bundledUiDir)
258+
if (bundledResolved) {
259+
candidates.push({
260+
uiStaticDir: bundledResolved,
261+
source: "bundled",
262+
uiVersion: await readUiVersion(bundledResolved),
263+
priority: 1,
264+
})
265+
}
266+
267+
const previousResolved = await resolveStaticUiDir(args.previousDir)
268+
if (previousResolved) {
269+
candidates.push({
270+
uiStaticDir: previousResolved,
271+
source: "previous",
272+
uiVersion: await readUiVersion(previousResolved),
273+
priority: 0,
274+
})
275+
}
276+
277+
if (candidates.length === 0) {
278+
return null
279+
}
280+
281+
candidates.sort((a, b) => {
282+
const versionCmp = compareSemverMaybe(a.uiVersion, b.uiVersion)
283+
if (versionCmp !== 0) return -versionCmp
284+
return b.priority - a.priority
285+
})
286+
287+
const best = candidates[0]
288+
if (!best) return null
289+
return { uiStaticDir: best.uiStaticDir, source: best.source, uiVersion: best.uiVersion }
290+
}
291+
292+
function compareSemverMaybe(a: string | undefined, b: string | undefined): number {
293+
if (!a && !b) return 0
294+
if (!a) return -1
295+
if (!b) return 1
296+
return compareSemverCore(a, b)
297+
}
298+
263299
async function resolveStaticUiDir(uiDir: string): Promise<string | null> {
264300
try {
265301
const indexPath = path.join(uiDir, "index.html")

packages/tauri-app/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@codenomad/tauri-app",
3-
"version": "0.8.0",
3+
"version": "0.8.1",
44
"private": true,
55
"scripts": {
66
"dev": "tauri dev",

0 commit comments

Comments
 (0)