@neuralnomads/codenomad-dev 0.10.3-dev-20260213-ba418a85 → 0.10.3-dev-20260213-e9f281a6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/public/apple-touch-icon-180x180.png +0 -0
- package/public/assets/{main-CSlDZj4f.js → main-crtt5pqm.js} +82 -80
- package/public/index.html +1 -1
- package/public/sw.js +1 -1
- package/public/ui-version.json +1 -1
- package/dist/integrations/github/bot-signature.js +0 -11
- package/dist/integrations/github/git-ops.js +0 -133
- package/dist/integrations/github/github-types.js +0 -1
- package/dist/integrations/github/job-runner.js +0 -608
- package/dist/integrations/github/octokit.js +0 -58
- package/dist/integrations/github/sanitize-webhook.js +0 -42
- package/dist/integrations/github/webhook-verify.js +0 -21
- package/dist/integrations/github/workspace-context.js +0 -10
- package/dist/integrations/github/worktree-context.js +0 -15
- package/dist/opencode/request-context.js +0 -39
- package/dist/opencode/worktree-directory.js +0 -42
- package/dist/opencode-config-template/README.md +0 -32
- package/dist/opencode-config-template/opencode.jsonc +0 -3
- package/dist/opencode-config-template/plugin/codenomad.ts +0 -40
- package/dist/opencode-config-template/plugin/lib/background-process.ts +0 -160
- package/dist/opencode-config-template/plugin/lib/client.ts +0 -165
- package/dist/server/routes/github-plugin.js +0 -215
- package/dist/server/routes/github-webhook.js +0 -32
- package/scripts/copy-auth-pages.mjs +0 -22
- package/scripts/copy-opencode-config.mjs +0 -61
- package/scripts/copy-ui-dist.mjs +0 -21
- package/src/api-types.ts +0 -326
- package/src/auth/auth-store.ts +0 -175
- package/src/auth/http-auth.ts +0 -38
- package/src/auth/manager.ts +0 -163
- package/src/auth/password-hash.ts +0 -49
- package/src/auth/session-manager.ts +0 -23
- package/src/auth/token-manager.ts +0 -32
- package/src/background-processes/manager.ts +0 -519
- package/src/bin.ts +0 -29
- package/src/config/binaries.ts +0 -192
- package/src/config/location.ts +0 -78
- package/src/config/schema.ts +0 -104
- package/src/config/store.ts +0 -244
- package/src/events/bus.ts +0 -45
- package/src/filesystem/__tests__/search-cache.test.ts +0 -61
- package/src/filesystem/browser.ts +0 -353
- package/src/filesystem/search-cache.ts +0 -66
- package/src/filesystem/search.ts +0 -184
- package/src/index.ts +0 -540
- package/src/launcher.ts +0 -177
- package/src/loader.ts +0 -21
- package/src/logger.ts +0 -133
- package/src/opencode-config.ts +0 -31
- package/src/plugins/channel.ts +0 -55
- package/src/plugins/handlers.ts +0 -36
- package/src/releases/dev-release-monitor.ts +0 -118
- package/src/releases/release-monitor.ts +0 -149
- package/src/server/http-server.ts +0 -693
- package/src/server/network-addresses.ts +0 -75
- package/src/server/routes/auth-pages/login.html +0 -134
- package/src/server/routes/auth-pages/token.html +0 -93
- package/src/server/routes/auth.ts +0 -164
- package/src/server/routes/background-processes.ts +0 -85
- package/src/server/routes/config.ts +0 -76
- package/src/server/routes/events.ts +0 -61
- package/src/server/routes/filesystem.ts +0 -54
- package/src/server/routes/meta.ts +0 -58
- package/src/server/routes/plugin.ts +0 -75
- package/src/server/routes/storage.ts +0 -66
- package/src/server/routes/workspaces.ts +0 -113
- package/src/server/routes/worktrees.ts +0 -195
- package/src/server/tls.ts +0 -283
- package/src/storage/instance-store.ts +0 -64
- package/src/ui/__tests__/remote-ui.test.ts +0 -58
- package/src/ui/remote-ui.ts +0 -571
- package/src/workspaces/git-worktrees.ts +0 -241
- package/src/workspaces/instance-events.ts +0 -226
- package/src/workspaces/manager.ts +0 -493
- package/src/workspaces/opencode-auth.ts +0 -22
- package/src/workspaces/runtime.ts +0 -428
- package/src/workspaces/worktree-map.ts +0 -129
- package/tsconfig.json +0 -17
package/src/server/tls.ts
DELETED
|
@@ -1,283 +0,0 @@
|
|
|
1
|
-
import crypto from "crypto"
|
|
2
|
-
import fs from "fs"
|
|
3
|
-
import path from "path"
|
|
4
|
-
import { createRequire } from "module"
|
|
5
|
-
import type { Logger } from "../logger"
|
|
6
|
-
|
|
7
|
-
const require = createRequire(import.meta.url)
|
|
8
|
-
|
|
9
|
-
type Forge = typeof import("node-forge")
|
|
10
|
-
|
|
11
|
-
function loadForge(): Forge {
|
|
12
|
-
// node-forge is CJS in many installs; require keeps this compatible with our ESM output.
|
|
13
|
-
return require("node-forge") as Forge
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface ResolvedHttpsOptions {
|
|
17
|
-
httpsOptions: { key: string | Buffer; cert: string | Buffer; ca?: string | Buffer }
|
|
18
|
-
/** Path to CA certificate suitable for NODE_EXTRA_CA_CERTS. */
|
|
19
|
-
caCertPath?: string
|
|
20
|
-
mode: "provided" | "generated"
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export interface ResolveHttpsOptionsArgs {
|
|
24
|
-
enabled: boolean
|
|
25
|
-
configDir: string
|
|
26
|
-
host: string
|
|
27
|
-
tlsKeyPath?: string
|
|
28
|
-
tlsCertPath?: string
|
|
29
|
-
tlsCaPath?: string
|
|
30
|
-
tlsSANs?: string
|
|
31
|
-
logger: Logger
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const LEAF_VALIDITY_DAYS = 30
|
|
35
|
-
const ROTATE_IF_EXPIRES_WITHIN_DAYS = 3
|
|
36
|
-
|
|
37
|
-
const CA_VALIDITY_DAYS = 365
|
|
38
|
-
|
|
39
|
-
export function resolveHttpsOptions(args: ResolveHttpsOptionsArgs): ResolvedHttpsOptions | null {
|
|
40
|
-
if (!args.enabled) {
|
|
41
|
-
return null
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const hasProvided = Boolean(args.tlsKeyPath && args.tlsCertPath)
|
|
45
|
-
if (hasProvided) {
|
|
46
|
-
const key = fs.readFileSync(args.tlsKeyPath!, "utf-8")
|
|
47
|
-
const cert = fs.readFileSync(args.tlsCertPath!, "utf-8")
|
|
48
|
-
const ca = args.tlsCaPath ? fs.readFileSync(args.tlsCaPath, "utf-8") : undefined
|
|
49
|
-
return {
|
|
50
|
-
httpsOptions: { key, cert, ca },
|
|
51
|
-
caCertPath: args.tlsCaPath,
|
|
52
|
-
mode: "provided",
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return ensureGeneratedTls(args)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function ensureGeneratedTls(args: ResolveHttpsOptionsArgs): ResolvedHttpsOptions {
|
|
60
|
-
const tlsDir = path.join(args.configDir, "tls")
|
|
61
|
-
const caKeyPath = path.join(tlsDir, "ca-key.pem")
|
|
62
|
-
const caCertPath = path.join(tlsDir, "ca-cert.pem")
|
|
63
|
-
const keyPath = path.join(tlsDir, "server-key.pem")
|
|
64
|
-
const certPath = path.join(tlsDir, "server-cert.pem")
|
|
65
|
-
|
|
66
|
-
fs.mkdirSync(tlsDir, { recursive: true })
|
|
67
|
-
|
|
68
|
-
const shouldRotateLeaf = () => {
|
|
69
|
-
try {
|
|
70
|
-
if (!fs.existsSync(certPath)) return true
|
|
71
|
-
const pem = fs.readFileSync(certPath, "utf-8")
|
|
72
|
-
const x509 = new crypto.X509Certificate(pem)
|
|
73
|
-
const validToMs = Date.parse(x509.validTo)
|
|
74
|
-
if (!Number.isFinite(validToMs)) return true
|
|
75
|
-
const rotateAt = validToMs - ROTATE_IF_EXPIRES_WITHIN_DAYS * 24 * 60 * 60 * 1000
|
|
76
|
-
return Date.now() >= rotateAt
|
|
77
|
-
} catch {
|
|
78
|
-
return true
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const shouldRotateCa = () => {
|
|
83
|
-
try {
|
|
84
|
-
if (!fs.existsSync(caCertPath)) return true
|
|
85
|
-
const pem = fs.readFileSync(caCertPath, "utf-8")
|
|
86
|
-
const x509 = new crypto.X509Certificate(pem)
|
|
87
|
-
const validToMs = Date.parse(x509.validTo)
|
|
88
|
-
if (!Number.isFinite(validToMs)) return true
|
|
89
|
-
// CA rotates only when expired.
|
|
90
|
-
return Date.now() >= validToMs
|
|
91
|
-
} catch {
|
|
92
|
-
return true
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
if (shouldRotateCa() || !fs.existsSync(caKeyPath)) {
|
|
97
|
-
const { caKeyPem, caCertPem } = generateCaCertificate()
|
|
98
|
-
writePemFile(caKeyPath, caKeyPem, 0o600)
|
|
99
|
-
writePemFile(caCertPath, caCertPem, 0o644)
|
|
100
|
-
args.logger.info({ caCertPath }, "Generated self-signed CodeNomad CA certificate")
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
if (shouldRotateLeaf() || !fs.existsSync(keyPath)) {
|
|
104
|
-
const caKeyPem = fs.readFileSync(caKeyPath, "utf-8")
|
|
105
|
-
const caCertPem = fs.readFileSync(caCertPath, "utf-8")
|
|
106
|
-
|
|
107
|
-
const { keyPem, certPem } = generateServerCertificate({
|
|
108
|
-
host: args.host,
|
|
109
|
-
tlsSANs: args.tlsSANs,
|
|
110
|
-
caKeyPem,
|
|
111
|
-
caCertPem,
|
|
112
|
-
})
|
|
113
|
-
|
|
114
|
-
writePemFile(keyPath, keyPem, 0o600)
|
|
115
|
-
writePemFile(certPath, certPem, 0o644)
|
|
116
|
-
args.logger.info({ certPath }, "Generated CodeNomad HTTPS certificate")
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const key = fs.readFileSync(keyPath, "utf-8")
|
|
120
|
-
const cert = fs.readFileSync(certPath, "utf-8")
|
|
121
|
-
const ca = fs.readFileSync(caCertPath, "utf-8")
|
|
122
|
-
|
|
123
|
-
// Present the CA as part of the chain.
|
|
124
|
-
const chainedCert = `${cert.trim()}\n${ca.trim()}\n`
|
|
125
|
-
|
|
126
|
-
return {
|
|
127
|
-
httpsOptions: {
|
|
128
|
-
key,
|
|
129
|
-
cert: chainedCert,
|
|
130
|
-
},
|
|
131
|
-
caCertPath,
|
|
132
|
-
mode: "generated",
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
function writePemFile(filePath: string, content: string, mode: number) {
|
|
137
|
-
fs.writeFileSync(filePath, content, { encoding: "utf-8", mode })
|
|
138
|
-
try {
|
|
139
|
-
fs.chmodSync(filePath, mode)
|
|
140
|
-
} catch {
|
|
141
|
-
// best effort on platforms that ignore chmod
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
function generateCaCertificate(): { caKeyPem: string; caCertPem: string } {
|
|
146
|
-
const forge = loadForge()
|
|
147
|
-
|
|
148
|
-
const keys = forge.pki.rsa.generateKeyPair(2048)
|
|
149
|
-
const cert = forge.pki.createCertificate()
|
|
150
|
-
cert.publicKey = keys.publicKey
|
|
151
|
-
cert.serialNumber = crypto.randomBytes(16).toString("hex")
|
|
152
|
-
|
|
153
|
-
const now = new Date()
|
|
154
|
-
const notBefore = new Date(now.getTime() - 60_000)
|
|
155
|
-
const notAfter = new Date(now.getTime() + CA_VALIDITY_DAYS * 24 * 60 * 60 * 1000)
|
|
156
|
-
cert.validity.notBefore = notBefore
|
|
157
|
-
cert.validity.notAfter = notAfter
|
|
158
|
-
|
|
159
|
-
const attrs = [{ name: "commonName", value: "CodeNomad Local CA" }]
|
|
160
|
-
cert.setSubject(attrs)
|
|
161
|
-
cert.setIssuer(attrs)
|
|
162
|
-
|
|
163
|
-
cert.setExtensions([
|
|
164
|
-
{ name: "basicConstraints", cA: true },
|
|
165
|
-
{ name: "keyUsage", keyCertSign: true, cRLSign: true, digitalSignature: true },
|
|
166
|
-
{ name: "subjectKeyIdentifier" },
|
|
167
|
-
])
|
|
168
|
-
|
|
169
|
-
cert.sign(keys.privateKey, forge.md.sha256.create())
|
|
170
|
-
|
|
171
|
-
return {
|
|
172
|
-
caKeyPem: forge.pki.privateKeyToPem(keys.privateKey),
|
|
173
|
-
caCertPem: forge.pki.certificateToPem(cert),
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
function generateServerCertificate(args: {
|
|
178
|
-
host: string
|
|
179
|
-
tlsSANs?: string
|
|
180
|
-
caKeyPem: string
|
|
181
|
-
caCertPem: string
|
|
182
|
-
}): { keyPem: string; certPem: string } {
|
|
183
|
-
const forge = loadForge()
|
|
184
|
-
|
|
185
|
-
const caKey = forge.pki.privateKeyFromPem(args.caKeyPem)
|
|
186
|
-
const caCert = forge.pki.certificateFromPem(args.caCertPem)
|
|
187
|
-
|
|
188
|
-
const keys = forge.pki.rsa.generateKeyPair(2048)
|
|
189
|
-
const cert = forge.pki.createCertificate()
|
|
190
|
-
cert.publicKey = keys.publicKey
|
|
191
|
-
cert.serialNumber = crypto.randomBytes(16).toString("hex")
|
|
192
|
-
|
|
193
|
-
const now = new Date()
|
|
194
|
-
const notBefore = new Date(now.getTime() - 60_000)
|
|
195
|
-
const notAfter = new Date(now.getTime() + LEAF_VALIDITY_DAYS * 24 * 60 * 60 * 1000)
|
|
196
|
-
cert.validity.notBefore = notBefore
|
|
197
|
-
cert.validity.notAfter = notAfter
|
|
198
|
-
|
|
199
|
-
const commonName = pickCommonName(args.host)
|
|
200
|
-
cert.setSubject([{ name: "commonName", value: commonName }])
|
|
201
|
-
cert.setIssuer(caCert.subject.attributes)
|
|
202
|
-
|
|
203
|
-
const san = buildSubjectAltNames(args.host, args.tlsSANs)
|
|
204
|
-
|
|
205
|
-
cert.setExtensions([
|
|
206
|
-
{ name: "basicConstraints", cA: false },
|
|
207
|
-
{ name: "keyUsage", digitalSignature: true, keyEncipherment: true },
|
|
208
|
-
{ name: "extKeyUsage", serverAuth: true },
|
|
209
|
-
{ name: "subjectAltName", altNames: san },
|
|
210
|
-
{ name: "subjectKeyIdentifier" },
|
|
211
|
-
])
|
|
212
|
-
|
|
213
|
-
cert.sign(caKey, forge.md.sha256.create())
|
|
214
|
-
|
|
215
|
-
return {
|
|
216
|
-
keyPem: forge.pki.privateKeyToPem(keys.privateKey),
|
|
217
|
-
certPem: forge.pki.certificateToPem(cert),
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
function pickCommonName(host: string): string {
|
|
222
|
-
if (!host || host === "0.0.0.0") {
|
|
223
|
-
return "localhost"
|
|
224
|
-
}
|
|
225
|
-
if (host === "127.0.0.1") {
|
|
226
|
-
return "localhost"
|
|
227
|
-
}
|
|
228
|
-
return host
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
function buildSubjectAltNames(host: string, tlsSANs?: string): Array<{ type: number; value?: string; ip?: string }> {
|
|
232
|
-
const dns = new Set<string>()
|
|
233
|
-
const ips = new Set<string>()
|
|
234
|
-
|
|
235
|
-
dns.add("localhost")
|
|
236
|
-
ips.add("127.0.0.1")
|
|
237
|
-
|
|
238
|
-
if (host && host !== "0.0.0.0") {
|
|
239
|
-
if (isIPv4(host)) {
|
|
240
|
-
ips.add(host)
|
|
241
|
-
} else {
|
|
242
|
-
dns.add(host)
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
for (const token of splitList(tlsSANs)) {
|
|
247
|
-
if (isIPv4(token)) {
|
|
248
|
-
ips.add(token)
|
|
249
|
-
} else if (token) {
|
|
250
|
-
dns.add(token)
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
const altNames: Array<{ type: number; value?: string; ip?: string }> = []
|
|
255
|
-
|
|
256
|
-
// 2 = DNS, 7 = IP
|
|
257
|
-
for (const name of Array.from(dns)) {
|
|
258
|
-
altNames.push({ type: 2, value: name })
|
|
259
|
-
}
|
|
260
|
-
for (const ip of Array.from(ips)) {
|
|
261
|
-
altNames.push({ type: 7, ip })
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
return altNames
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
function splitList(input: string | undefined): string[] {
|
|
268
|
-
if (!input) return []
|
|
269
|
-
return input
|
|
270
|
-
.split(",")
|
|
271
|
-
.map((part) => part.trim())
|
|
272
|
-
.filter(Boolean)
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
function isIPv4(value: string): boolean {
|
|
276
|
-
const parts = value.split(".")
|
|
277
|
-
if (parts.length !== 4) return false
|
|
278
|
-
return parts.every((part) => {
|
|
279
|
-
if (!/^[0-9]+$/.test(part)) return false
|
|
280
|
-
const num = Number(part)
|
|
281
|
-
return Number.isInteger(num) && num >= 0 && num <= 255
|
|
282
|
-
})
|
|
283
|
-
}
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import fs from "fs"
|
|
2
|
-
import { promises as fsp } from "fs"
|
|
3
|
-
import os from "os"
|
|
4
|
-
import path from "path"
|
|
5
|
-
import type { InstanceData } from "../api-types"
|
|
6
|
-
|
|
7
|
-
const DEFAULT_INSTANCE_DATA: InstanceData = {
|
|
8
|
-
messageHistory: [],
|
|
9
|
-
agentModelSelections: {},
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export class InstanceStore {
|
|
13
|
-
private readonly instancesDir: string
|
|
14
|
-
|
|
15
|
-
constructor(baseDir = path.join(os.homedir(), ".config", "codenomad", "instances")) {
|
|
16
|
-
this.instancesDir = baseDir
|
|
17
|
-
fs.mkdirSync(this.instancesDir, { recursive: true })
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
async read(id: string): Promise<InstanceData> {
|
|
21
|
-
try {
|
|
22
|
-
const filePath = this.resolvePath(id)
|
|
23
|
-
const content = await fsp.readFile(filePath, "utf-8")
|
|
24
|
-
const parsed = JSON.parse(content)
|
|
25
|
-
return { ...DEFAULT_INSTANCE_DATA, ...parsed }
|
|
26
|
-
} catch (error) {
|
|
27
|
-
if ((error as NodeJS.ErrnoException).code === "ENOENT") {
|
|
28
|
-
return DEFAULT_INSTANCE_DATA
|
|
29
|
-
}
|
|
30
|
-
throw error
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
async write(id: string, data: InstanceData): Promise<void> {
|
|
35
|
-
const filePath = this.resolvePath(id)
|
|
36
|
-
await fsp.mkdir(path.dirname(filePath), { recursive: true })
|
|
37
|
-
await fsp.writeFile(filePath, JSON.stringify(data, null, 2), "utf-8")
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
async delete(id: string): Promise<void> {
|
|
41
|
-
try {
|
|
42
|
-
const filePath = this.resolvePath(id)
|
|
43
|
-
await fsp.unlink(filePath)
|
|
44
|
-
} catch (error) {
|
|
45
|
-
if ((error as NodeJS.ErrnoException).code !== "ENOENT") {
|
|
46
|
-
throw error
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
private resolvePath(id: string): string {
|
|
52
|
-
const filename = this.sanitizeId(id)
|
|
53
|
-
return path.join(this.instancesDir, `${filename}.json`)
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
private sanitizeId(id: string): string {
|
|
57
|
-
return id
|
|
58
|
-
.replace(/[\\/]/g, "_")
|
|
59
|
-
.replace(/[^a-zA-Z0-9_.-]/g, "_")
|
|
60
|
-
.replace(/_{2,}/g, "_")
|
|
61
|
-
.replace(/^_|_$/g, "")
|
|
62
|
-
.toLowerCase()
|
|
63
|
-
}
|
|
64
|
-
}
|
|
@@ -1,58 +0,0 @@
|
|
|
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
|
-
})
|