@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.
Files changed (79) hide show
  1. package/package.json +1 -1
  2. package/public/apple-touch-icon-180x180.png +0 -0
  3. package/public/assets/{main-CSlDZj4f.js → main-crtt5pqm.js} +82 -80
  4. package/public/index.html +1 -1
  5. package/public/sw.js +1 -1
  6. package/public/ui-version.json +1 -1
  7. package/dist/integrations/github/bot-signature.js +0 -11
  8. package/dist/integrations/github/git-ops.js +0 -133
  9. package/dist/integrations/github/github-types.js +0 -1
  10. package/dist/integrations/github/job-runner.js +0 -608
  11. package/dist/integrations/github/octokit.js +0 -58
  12. package/dist/integrations/github/sanitize-webhook.js +0 -42
  13. package/dist/integrations/github/webhook-verify.js +0 -21
  14. package/dist/integrations/github/workspace-context.js +0 -10
  15. package/dist/integrations/github/worktree-context.js +0 -15
  16. package/dist/opencode/request-context.js +0 -39
  17. package/dist/opencode/worktree-directory.js +0 -42
  18. package/dist/opencode-config-template/README.md +0 -32
  19. package/dist/opencode-config-template/opencode.jsonc +0 -3
  20. package/dist/opencode-config-template/plugin/codenomad.ts +0 -40
  21. package/dist/opencode-config-template/plugin/lib/background-process.ts +0 -160
  22. package/dist/opencode-config-template/plugin/lib/client.ts +0 -165
  23. package/dist/server/routes/github-plugin.js +0 -215
  24. package/dist/server/routes/github-webhook.js +0 -32
  25. package/scripts/copy-auth-pages.mjs +0 -22
  26. package/scripts/copy-opencode-config.mjs +0 -61
  27. package/scripts/copy-ui-dist.mjs +0 -21
  28. package/src/api-types.ts +0 -326
  29. package/src/auth/auth-store.ts +0 -175
  30. package/src/auth/http-auth.ts +0 -38
  31. package/src/auth/manager.ts +0 -163
  32. package/src/auth/password-hash.ts +0 -49
  33. package/src/auth/session-manager.ts +0 -23
  34. package/src/auth/token-manager.ts +0 -32
  35. package/src/background-processes/manager.ts +0 -519
  36. package/src/bin.ts +0 -29
  37. package/src/config/binaries.ts +0 -192
  38. package/src/config/location.ts +0 -78
  39. package/src/config/schema.ts +0 -104
  40. package/src/config/store.ts +0 -244
  41. package/src/events/bus.ts +0 -45
  42. package/src/filesystem/__tests__/search-cache.test.ts +0 -61
  43. package/src/filesystem/browser.ts +0 -353
  44. package/src/filesystem/search-cache.ts +0 -66
  45. package/src/filesystem/search.ts +0 -184
  46. package/src/index.ts +0 -540
  47. package/src/launcher.ts +0 -177
  48. package/src/loader.ts +0 -21
  49. package/src/logger.ts +0 -133
  50. package/src/opencode-config.ts +0 -31
  51. package/src/plugins/channel.ts +0 -55
  52. package/src/plugins/handlers.ts +0 -36
  53. package/src/releases/dev-release-monitor.ts +0 -118
  54. package/src/releases/release-monitor.ts +0 -149
  55. package/src/server/http-server.ts +0 -693
  56. package/src/server/network-addresses.ts +0 -75
  57. package/src/server/routes/auth-pages/login.html +0 -134
  58. package/src/server/routes/auth-pages/token.html +0 -93
  59. package/src/server/routes/auth.ts +0 -164
  60. package/src/server/routes/background-processes.ts +0 -85
  61. package/src/server/routes/config.ts +0 -76
  62. package/src/server/routes/events.ts +0 -61
  63. package/src/server/routes/filesystem.ts +0 -54
  64. package/src/server/routes/meta.ts +0 -58
  65. package/src/server/routes/plugin.ts +0 -75
  66. package/src/server/routes/storage.ts +0 -66
  67. package/src/server/routes/workspaces.ts +0 -113
  68. package/src/server/routes/worktrees.ts +0 -195
  69. package/src/server/tls.ts +0 -283
  70. package/src/storage/instance-store.ts +0 -64
  71. package/src/ui/__tests__/remote-ui.test.ts +0 -58
  72. package/src/ui/remote-ui.ts +0 -571
  73. package/src/workspaces/git-worktrees.ts +0 -241
  74. package/src/workspaces/instance-events.ts +0 -226
  75. package/src/workspaces/manager.ts +0 -493
  76. package/src/workspaces/opencode-auth.ts +0 -22
  77. package/src/workspaces/runtime.ts +0 -428
  78. package/src/workspaces/worktree-map.ts +0 -129
  79. 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
- })