Rename CLI package to @neuralnomads/codenomad and bin codenomad
This commit is contained in:
2
BUILD.md
2
BUILD.md
@@ -77,7 +77,7 @@ bun run build:all
|
|||||||
|
|
||||||
The build script performs these steps:
|
The build script performs these steps:
|
||||||
|
|
||||||
1. **Build @codenomad/cli** → Produces the CLI `dist/` bundle (also rebuilds the UI assets it serves)
|
1. **Build @neuralnomads/codenomad** → Produces the CLI `dist/` bundle (also rebuilds the UI assets it serves)
|
||||||
2. **Compile TypeScript + bundle with Vite** → Electron main, preload, and renderer output in `dist/`
|
2. **Compile TypeScript + bundle with Vite** → Electron main, preload, and renderer output in `dist/`
|
||||||
3. **Package with electron-builder** → Platform-specific binaries
|
3. **Package with electron-builder** → Platform-specific binaries
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ Grab the latest build for macOS, Windows, and Linux from the [GitHub Releases pa
|
|||||||
|
|
||||||
## CLI Server Flags
|
## CLI Server Flags
|
||||||
|
|
||||||
The bundled CLI server (`@codenomad/cli`) controls which folders the UI can browse when you pick a workspace:
|
The bundled CLI server (`@neuralnomads/codenomad`) controls which folders the UI can browse when you pick a workspace:
|
||||||
|
|
||||||
- `--workspace-root <path>` (default: current working directory) scopes browsing to a safe subtree. The UI can only see folders beneath this root.
|
- `--workspace-root <path>` (default: current working directory) scopes browsing to a safe subtree. The UI can only see folders beneath this root.
|
||||||
- `--unrestricted-root` explicitly allows full-machine browsing for the current process. In this mode the UI starts from the host home directory, adds a "parent" option so you can reach `/` on macOS/Linux, and lists drives/UNC paths on Windows. The flag is runtime-only—restart the CLI without it to go back to restricted mode.
|
- `--unrestricted-root` explicitly allows full-machine browsing for the current process. In this mode the UI starts from the host home directory, adds a "parent" option so you can reach `/` on macOS/Linux, and lists drives/UNC paths on Windows. The flag is runtime-only—restart the CLI without it to go back to restricted mode.
|
||||||
|
|||||||
16
package-lock.json
generated
16
package-lock.json
generated
@@ -313,8 +313,8 @@
|
|||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@codenomad/cli": {
|
"node_modules/@neuralnomads/codenomad": {
|
||||||
"resolved": "packages/cli",
|
"resolved": "packages/server",
|
||||||
"link": true
|
"link": true
|
||||||
},
|
},
|
||||||
"node_modules/@codenomad/electron-app": {
|
"node_modules/@codenomad/electron-app": {
|
||||||
@@ -8390,8 +8390,8 @@
|
|||||||
"url": "https://github.com/sponsors/wooorm"
|
"url": "https://github.com/sponsors/wooorm"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"packages/cli": {
|
"packages/server": {
|
||||||
"name": "@codenomad/cli",
|
"name": "@neuralnomads/codenomad",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fastify/cors": "^8.5.0",
|
"@fastify/cors": "^8.5.0",
|
||||||
@@ -8405,7 +8405,7 @@
|
|||||||
"zod": "^3.23.8"
|
"zod": "^3.23.8"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"codenomad-cli": "dist/bin.js"
|
"codenomad": "dist/bin.js"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
@@ -8414,7 +8414,7 @@
|
|||||||
"typescript": "^5.6.3"
|
"typescript": "^5.6.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"packages/cli/node_modules/commander": {
|
"packages/server/node_modules/commander": {
|
||||||
"version": "12.1.0",
|
"version": "12.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
|
||||||
"integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
|
"integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
|
||||||
@@ -8423,7 +8423,7 @@
|
|||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"packages/cli/node_modules/fuzzysort": {
|
"packages/server/node_modules/fuzzysort": {
|
||||||
"version": "2.0.4",
|
"version": "2.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/fuzzysort/-/fuzzysort-2.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/fuzzysort/-/fuzzysort-2.0.4.tgz",
|
||||||
"integrity": "sha512-Api1mJL+Ad7W7vnDZnWq5pGaXJjyencT+iKGia2PlHUcSsSzWwIQ3S1isiMpwpavjYtGd2FzhUIhnnhOULZgDw==",
|
"integrity": "sha512-Api1mJL+Ad7W7vnDZnWq5pGaXJjyencT+iKGia2PlHUcSsSzWwIQ3S1isiMpwpavjYtGd2FzhUIhnnhOULZgDw==",
|
||||||
@@ -8433,7 +8433,7 @@
|
|||||||
"name": "@codenomad/electron-app",
|
"name": "@codenomad/electron-app",
|
||||||
"version": "0.1.2",
|
"version": "0.1.2",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codenomad/cli": "file:../cli",
|
"@neuralnomads/codenomad": "file:../server",
|
||||||
"@codenomad/ui": "file:../ui"
|
"@codenomad/ui": "file:../ui"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -264,9 +264,9 @@ export class CliProcessManager extends EventEmitter {
|
|||||||
if (options.dev) {
|
if (options.dev) {
|
||||||
const tsxPath = this.resolveTsx()
|
const tsxPath = this.resolveTsx()
|
||||||
const sourceCandidates = [
|
const sourceCandidates = [
|
||||||
path.resolve(app.getAppPath(), "..", "cli", "src", "index.ts"),
|
path.resolve(app.getAppPath(), "..", "server", "src", "index.ts"),
|
||||||
path.resolve(app.getAppPath(), "..", "packages", "cli", "src", "index.ts"),
|
path.resolve(app.getAppPath(), "..", "packages", "server", "src", "index.ts"),
|
||||||
path.resolve(process.cwd(), "packages", "cli", "src", "index.ts"),
|
path.resolve(process.cwd(), "packages", "server", "src", "index.ts"),
|
||||||
]
|
]
|
||||||
const sourceEntry = sourceCandidates.find((candidate) => existsSync(candidate))
|
const sourceEntry = sourceCandidates.find((candidate) => existsSync(candidate))
|
||||||
if (tsxPath && sourceEntry) {
|
if (tsxPath && sourceEntry) {
|
||||||
@@ -279,7 +279,7 @@ export class CliProcessManager extends EventEmitter {
|
|||||||
return { entry: dist, runner: "node" }
|
return { entry: dist, runner: "node" }
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error("Unable to locate CodeNomad CLI build (dist/bin.js). Please build @codenomad/cli.")
|
throw new Error("Unable to locate CodeNomad CLI build (dist/bin.js). Please build @neuralnomads/codenomad.")
|
||||||
}
|
}
|
||||||
|
|
||||||
private resolveTsx(): string | null {
|
private resolveTsx(): string | null {
|
||||||
@@ -296,12 +296,12 @@ export class CliProcessManager extends EventEmitter {
|
|||||||
|
|
||||||
private tryResolveDist(): string | null {
|
private tryResolveDist(): string | null {
|
||||||
const candidates: Array<string | (() => string)> = [
|
const candidates: Array<string | (() => string)> = [
|
||||||
() => nodeRequire.resolve("@codenomad/cli/dist/bin.js"),
|
() => nodeRequire.resolve("@neuralnomads/codenomad/dist/bin.js"),
|
||||||
() => nodeRequire.resolve("@codenomad/cli/dist/bin.js", { paths: [app.getAppPath()] }),
|
() => nodeRequire.resolve("@neuralnomads/codenomad/dist/bin.js", { paths: [app.getAppPath()] }),
|
||||||
path.join(app.getAppPath(), "node_modules", "@codenomad", "cli", "dist", "bin.js"),
|
path.join(app.getAppPath(), "node_modules", "@neuralnomads", "codenomad", "dist", "bin.js"),
|
||||||
path.resolve(app.getAppPath(), "..", "cli", "dist", "bin.js"),
|
path.resolve(app.getAppPath(), "..", "server", "dist", "bin.js"),
|
||||||
path.resolve(app.getAppPath(), "..", "packages", "cli", "dist", "bin.js"),
|
path.resolve(app.getAppPath(), "..", "packages", "server", "dist", "bin.js"),
|
||||||
path.join(process.resourcesPath, "app.asar.unpacked", "node_modules", "@codenomad", "cli", "dist", "bin.js"),
|
path.join(process.resourcesPath, "app.asar.unpacked", "node_modules", "@neuralnomads", "codenomad", "dist", "bin.js"),
|
||||||
]
|
]
|
||||||
|
|
||||||
for (const candidate of candidates) {
|
for (const candidate of candidates) {
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
"package:linux": "electron-builder --linux"
|
"package:linux": "electron-builder --linux"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codenomad/cli": "file:../cli",
|
"@neuralnomads/codenomad": "file:../server",
|
||||||
"@codenomad/ui": "file:../ui"
|
"@codenomad/ui": "file:../ui"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ async function build(platform) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
console.log("📦 Step 1/3: Building CLI dependency...\n")
|
console.log("📦 Step 1/3: Building CLI dependency...\n")
|
||||||
await run(npmCmd, ["run", "build", "--workspace", "@codenomad/cli"], {
|
await run(npmCmd, ["run", "build", "--workspace", "@neuralnomads/codenomad"], {
|
||||||
cwd: workspaceRoot,
|
cwd: workspaceRoot,
|
||||||
env: { NODE_PATH: workspaceNodeModulesPath },
|
env: { NODE_PATH: workspaceNodeModulesPath },
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "@codenomad/cli",
|
"name": "@neuralnomads/codenomad",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@codenomad/cli",
|
"name": "@neuralnomads/codenomad",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fastify/cors": "^8.5.0",
|
"@fastify/cors": "^8.5.0",
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "@codenomad/cli",
|
"name": "@neuralnomads/codenomad",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "CodeNomad CLI server for HTTP/SSE control plane",
|
"description": "CodeNomad CLI server for HTTP/SSE control plane",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"bin": {
|
"bin": {
|
||||||
"codenomad-cli": "dist/bin.js"
|
"codenomad": "dist/bin.js"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "npm run build:ui && npm run prepare-ui && tsc -p tsconfig.json",
|
"build": "npm run build:ui && npm run prepare-ui && tsc -p tsconfig.json",
|
||||||
@@ -15,6 +15,7 @@ import { EventBus } from "./events/bus"
|
|||||||
import { ServerMeta } from "./api-types"
|
import { ServerMeta } from "./api-types"
|
||||||
import { InstanceStore } from "./storage/instance-store"
|
import { InstanceStore } from "./storage/instance-store"
|
||||||
import { createLogger } from "./logger"
|
import { createLogger } from "./logger"
|
||||||
|
import { launchInBrowser } from "./launcher"
|
||||||
|
|
||||||
const require = createRequire(import.meta.url)
|
const require = createRequire(import.meta.url)
|
||||||
const packageJson = require("../package.json") as { version: string }
|
const packageJson = require("../package.json") as { version: string }
|
||||||
@@ -32,6 +33,7 @@ interface CliOptions {
|
|||||||
logDestination?: string
|
logDestination?: string
|
||||||
uiStaticDir: string
|
uiStaticDir: string
|
||||||
uiDevServer?: string
|
uiDevServer?: string
|
||||||
|
launch: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_PORT = 9898
|
const DEFAULT_PORT = 9898
|
||||||
@@ -40,7 +42,7 @@ const DEFAULT_CONFIG_PATH = "~/.config/codenomad/config.json"
|
|||||||
|
|
||||||
function parseCliOptions(argv: string[]): CliOptions {
|
function parseCliOptions(argv: string[]): CliOptions {
|
||||||
const program = new Command()
|
const program = new Command()
|
||||||
.name("codenomad-cli")
|
.name("codenomad")
|
||||||
.description("CodeNomad CLI server")
|
.description("CodeNomad CLI server")
|
||||||
.version(packageJson.version, "-v, --version", "Show the CLI version")
|
.version(packageJson.version, "-v, --version", "Show the CLI version")
|
||||||
.addOption(new Option("--host <host>", "Host interface to bind").env("CLI_HOST").default(DEFAULT_HOST))
|
.addOption(new Option("--host <host>", "Host interface to bind").env("CLI_HOST").default(DEFAULT_HOST))
|
||||||
@@ -57,6 +59,7 @@ function parseCliOptions(argv: string[]): CliOptions {
|
|||||||
new Option("--ui-dir <path>", "Directory containing the built UI bundle").env("CLI_UI_DIR").default(DEFAULT_UI_STATIC_DIR),
|
new Option("--ui-dir <path>", "Directory containing the built UI bundle").env("CLI_UI_DIR").default(DEFAULT_UI_STATIC_DIR),
|
||||||
)
|
)
|
||||||
.addOption(new Option("--ui-dev-server <url>", "Proxy UI requests to a running dev server").env("CLI_UI_DEV_SERVER"))
|
.addOption(new Option("--ui-dev-server <url>", "Proxy UI requests to a running dev server").env("CLI_UI_DEV_SERVER"))
|
||||||
|
.addOption(new Option("--launch", "Launch the UI in a browser after start").env("CLI_LAUNCH").default(false))
|
||||||
|
|
||||||
program.parse(argv, { from: "user" })
|
program.parse(argv, { from: "user" })
|
||||||
const parsed = program.opts<{
|
const parsed = program.opts<{
|
||||||
@@ -70,6 +73,7 @@ function parseCliOptions(argv: string[]): CliOptions {
|
|||||||
logDestination?: string
|
logDestination?: string
|
||||||
uiDir: string
|
uiDir: string
|
||||||
uiDevServer?: string
|
uiDevServer?: string
|
||||||
|
launch?: boolean
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const resolvedRoot = parsed.workspaceRoot ?? parsed.root ?? process.cwd()
|
const resolvedRoot = parsed.workspaceRoot ?? parsed.root ?? process.cwd()
|
||||||
@@ -84,6 +88,7 @@ function parseCliOptions(argv: string[]): CliOptions {
|
|||||||
logDestination: parsed.logDestination,
|
logDestination: parsed.logDestination,
|
||||||
uiStaticDir: parsed.uiDir,
|
uiStaticDir: parsed.uiDir,
|
||||||
uiDevServer: parsed.uiDevServer,
|
uiDevServer: parsed.uiDevServer,
|
||||||
|
launch: Boolean(parsed.launch),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,11 +144,13 @@ async function main() {
|
|||||||
logger,
|
logger,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const startInfo = await server.start()
|
||||||
|
logger.info({ port: startInfo.port, host: options.host }, "HTTP server listening")
|
||||||
|
console.log(`CodeNomad Server is ready at ${startInfo.url}`)
|
||||||
|
|
||||||
await server.start()
|
if (options.launch) {
|
||||||
logger.info({ port: options.port, host: options.host }, "HTTP server listening")
|
await launchInBrowser(startInfo.url, logger.child({ component: "launcher" }))
|
||||||
const displayHost = options.host === "127.0.0.1" || options.host === "0.0.0.0" ? "localhost" : options.host
|
}
|
||||||
console.log(`CodeNomad Server is ready at http://${displayHost}:${options.port}`)
|
|
||||||
|
|
||||||
let shuttingDown = false
|
let shuttingDown = false
|
||||||
|
|
||||||
177
packages/server/src/launcher.ts
Normal file
177
packages/server/src/launcher.ts
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
import { spawn } from "child_process"
|
||||||
|
import os from "os"
|
||||||
|
import path from "path"
|
||||||
|
import type { Logger } from "./logger"
|
||||||
|
|
||||||
|
interface BrowserCandidate {
|
||||||
|
name: string
|
||||||
|
command: string
|
||||||
|
args: (url: string) => string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const APP_ARGS = (url: string) => [`--app=${url}`, "--new-window"]
|
||||||
|
|
||||||
|
export async function launchInBrowser(url: string, logger: Logger): Promise<boolean> {
|
||||||
|
const { platform, candidates, manualExamples } = buildPlatformCandidates(url)
|
||||||
|
|
||||||
|
console.log(`Attempting to launch browser (${platform}) using:`)
|
||||||
|
candidates.forEach((candidate) => console.log(` - ${candidate.name}: ${candidate.command}`))
|
||||||
|
|
||||||
|
for (const candidate of candidates) {
|
||||||
|
const success = await tryLaunch(candidate, url, logger)
|
||||||
|
if (success) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.error(
|
||||||
|
"No supported browser found to launch. Run without --launch and use one of the commands below or install a compatible browser.",
|
||||||
|
)
|
||||||
|
if (manualExamples.length > 0) {
|
||||||
|
console.error("Manual launch commands:")
|
||||||
|
manualExamples.forEach((line) => console.error(` ${line}`))
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
async function tryLaunch(candidate: BrowserCandidate, url: string, logger: Logger): Promise<boolean> {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
let resolved = false
|
||||||
|
try {
|
||||||
|
const args = candidate.args(url)
|
||||||
|
const child = spawn(candidate.command, args, { stdio: "ignore", detached: true })
|
||||||
|
|
||||||
|
child.once("error", (error) => {
|
||||||
|
if (resolved) return
|
||||||
|
resolved = true
|
||||||
|
logger.debug({ err: error, candidate: candidate.name, command: candidate.command, args }, "Browser launch failed")
|
||||||
|
resolve(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
child.once("spawn", () => {
|
||||||
|
if (resolved) return
|
||||||
|
resolved = true
|
||||||
|
logger.info(
|
||||||
|
{
|
||||||
|
browser: candidate.name,
|
||||||
|
command: candidate.command,
|
||||||
|
args,
|
||||||
|
fullCommand: [candidate.command, ...args].join(" "),
|
||||||
|
},
|
||||||
|
"Launched browser in app mode",
|
||||||
|
)
|
||||||
|
child.unref()
|
||||||
|
resolve(true)
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
if (resolved) return
|
||||||
|
resolved = true
|
||||||
|
logger.debug({ err: error, candidate: candidate.name, command: candidate.command }, "Browser spawn threw")
|
||||||
|
resolve(false)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildPlatformCandidates(url: string) {
|
||||||
|
switch (os.platform()) {
|
||||||
|
case "darwin":
|
||||||
|
return {
|
||||||
|
platform: "macOS",
|
||||||
|
candidates: buildMacCandidates(),
|
||||||
|
manualExamples: buildMacManualExamples(url),
|
||||||
|
}
|
||||||
|
case "win32":
|
||||||
|
return {
|
||||||
|
platform: "Windows",
|
||||||
|
candidates: buildWindowsCandidates(),
|
||||||
|
manualExamples: buildWindowsManualExamples(url),
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return {
|
||||||
|
platform: "Linux",
|
||||||
|
candidates: buildLinuxCandidates(),
|
||||||
|
manualExamples: buildLinuxManualExamples(url),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildMacCandidates(): BrowserCandidate[] {
|
||||||
|
const apps = [
|
||||||
|
{ name: "Google Chrome", path: "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" },
|
||||||
|
{ name: "Google Chrome Canary", path: "/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary" },
|
||||||
|
{ name: "Microsoft Edge", path: "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge" },
|
||||||
|
{ name: "Brave Browser", path: "/Applications/Brave Browser.app/Contents/MacOS/Brave Browser" },
|
||||||
|
{ name: "Chromium", path: "/Applications/Chromium.app/Contents/MacOS/Chromium" },
|
||||||
|
{ name: "Vivaldi", path: "/Applications/Vivaldi.app/Contents/MacOS/Vivaldi" },
|
||||||
|
{ name: "Arc", path: "/Applications/Arc.app/Contents/MacOS/Arc" },
|
||||||
|
]
|
||||||
|
|
||||||
|
return apps.map((entry) => ({ name: entry.name, command: entry.path, args: APP_ARGS }))
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildWindowsCandidates(): BrowserCandidate[] {
|
||||||
|
const programFiles = process.env["ProgramFiles"]
|
||||||
|
const programFilesX86 = process.env["ProgramFiles(x86)"]
|
||||||
|
const localAppData = process.env["LocalAppData"]
|
||||||
|
|
||||||
|
const paths = [
|
||||||
|
[programFiles, "Google/Chrome/Application/chrome.exe", "Google Chrome"],
|
||||||
|
[programFilesX86, "Google/Chrome/Application/chrome.exe", "Google Chrome (x86)"],
|
||||||
|
[localAppData, "Google/Chrome/Application/chrome.exe", "Google Chrome (User)"],
|
||||||
|
[programFiles, "Microsoft/Edge/Application/msedge.exe", "Microsoft Edge"],
|
||||||
|
[programFilesX86, "Microsoft/Edge/Application/msedge.exe", "Microsoft Edge (x86)"],
|
||||||
|
[localAppData, "Microsoft/Edge/Application/msedge.exe", "Microsoft Edge (User)"],
|
||||||
|
[programFiles, "BraveSoftware/Brave-Browser/Application/brave.exe", "Brave"],
|
||||||
|
[localAppData, "BraveSoftware/Brave-Browser/Application/brave.exe", "Brave (User)"],
|
||||||
|
[programFiles, "Chromium/Application/chromium.exe", "Chromium"],
|
||||||
|
] as const
|
||||||
|
|
||||||
|
return paths
|
||||||
|
.filter(([root]) => Boolean(root))
|
||||||
|
.map(([root, rel, name]) => ({
|
||||||
|
name,
|
||||||
|
command: path.join(root as string, rel),
|
||||||
|
args: APP_ARGS,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildLinuxCandidates(): BrowserCandidate[] {
|
||||||
|
const names = [
|
||||||
|
"google-chrome",
|
||||||
|
"google-chrome-stable",
|
||||||
|
"chromium",
|
||||||
|
"chromium-browser",
|
||||||
|
"brave-browser",
|
||||||
|
"microsoft-edge",
|
||||||
|
"microsoft-edge-stable",
|
||||||
|
"vivaldi",
|
||||||
|
]
|
||||||
|
|
||||||
|
return names.map((name) => ({ name, command: name, args: APP_ARGS }))
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildMacManualExamples(url: string) {
|
||||||
|
return [
|
||||||
|
`"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" --app="${url}" --new-window`,
|
||||||
|
`"/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge" --app="${url}" --new-window`,
|
||||||
|
`"/Applications/Brave Browser.app/Contents/MacOS/Brave Browser" --app="${url}" --new-window`,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildWindowsManualExamples(url: string) {
|
||||||
|
return [
|
||||||
|
`"%ProgramFiles%\\Google\\Chrome\\Application\\chrome.exe" --app="${url}" --new-window`,
|
||||||
|
`"%ProgramFiles%\\Microsoft\\Edge\\Application\\msedge.exe" --app="${url}" --new-window`,
|
||||||
|
`"%ProgramFiles%\\BraveSoftware\\Brave-Browser\\Application\\brave.exe" --app="${url}" --new-window`,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildLinuxManualExamples(url: string) {
|
||||||
|
return [
|
||||||
|
`google-chrome --app="${url}" --new-window`,
|
||||||
|
`chromium --app="${url}" --new-window`,
|
||||||
|
`brave-browser --app="${url}" --new-window`,
|
||||||
|
`microsoft-edge --app="${url}" --new-window`,
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import Fastify, { type FastifyInstance, type FastifyReply, type FastifyRequest } from "fastify"
|
import Fastify, { type FastifyInstance, type FastifyReply, type FastifyRequest } from "fastify"
|
||||||
import cors from "@fastify/cors"
|
import cors from "@fastify/cors"
|
||||||
import fastifyStatic from "@fastify/static"
|
import fastifyStatic from "@fastify/static"
|
||||||
import replyFrom, { type FastifyReplyFromOptions } from "@fastify/reply-from"
|
import replyFrom from "@fastify/reply-from"
|
||||||
import fs from "fs"
|
import fs from "fs"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import { fetch } from "undici"
|
import { fetch } from "undici"
|
||||||
@@ -36,6 +36,11 @@ interface HttpServerDeps {
|
|||||||
logger: Logger
|
logger: Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface HttpServerStartResult {
|
||||||
|
port: number
|
||||||
|
url: string
|
||||||
|
displayHost: string
|
||||||
|
}
|
||||||
|
|
||||||
export function createHttpServer(deps: HttpServerDeps) {
|
export function createHttpServer(deps: HttpServerDeps) {
|
||||||
const app = Fastify({ logger: false })
|
const app = Fastify({ logger: false })
|
||||||
@@ -83,8 +88,9 @@ export function createHttpServer(deps: HttpServerDeps) {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
instance: app,
|
instance: app,
|
||||||
start: async () => {
|
start: async (): Promise<HttpServerStartResult> => {
|
||||||
const addressInfo = await app.listen({ port: deps.port, host: deps.host })
|
const addressInfo = await app.listen({ port: deps.port, host: deps.host })
|
||||||
|
|
||||||
let actualPort = deps.port
|
let actualPort = deps.port
|
||||||
|
|
||||||
if (typeof addressInfo === "string") {
|
if (typeof addressInfo === "string") {
|
||||||
@@ -101,13 +107,14 @@ export function createHttpServer(deps: HttpServerDeps) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const displayHost = deps.host === "0.0.0.0" ? "127.0.0.1" : deps.host
|
const displayHost = deps.host === "0.0.0.0" ? "127.0.0.1" : deps.host === "127.0.0.1" ? "localhost" : deps.host
|
||||||
|
const serverUrl = `http://${displayHost}:${actualPort}`
|
||||||
|
|
||||||
deps.serverMeta.httpBaseUrl = `http://${displayHost}:${actualPort}`
|
deps.serverMeta.httpBaseUrl = serverUrl
|
||||||
deps.logger.info({ port: actualPort, host: deps.host }, "HTTP server listening")
|
deps.logger.info({ port: actualPort, host: deps.host }, "HTTP server listening")
|
||||||
console.log(`CodeNomad Server is ready at http://${displayHost}:${actualPort}`)
|
console.log(`CodeNomad Server is ready at ${serverUrl}`)
|
||||||
|
|
||||||
return actualPort
|
return { port: actualPort, url: serverUrl, displayHost }
|
||||||
},
|
},
|
||||||
stop: () => {
|
stop: () => {
|
||||||
closeSseClients()
|
closeSseClients()
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Component, Show, For, createSignal, createMemo, createEffect, onCleanup } from "solid-js"
|
import { Component, Show, For, createSignal, createMemo, createEffect, onCleanup } from "solid-js"
|
||||||
import { ArrowUpLeft, Folder as FolderIcon, Loader2, X } from "lucide-solid"
|
import { ArrowUpLeft, Folder as FolderIcon, Loader2, X } from "lucide-solid"
|
||||||
import type { FileSystemEntry, FileSystemListingMetadata } from "../../../cli/src/api-types"
|
import type { FileSystemEntry, FileSystemListingMetadata } from "../../../server/src/api-types"
|
||||||
import { WINDOWS_DRIVES_ROOT } from "../../../cli/src/api-types"
|
import { WINDOWS_DRIVES_ROOT } from "../../../server/src/api-types"
|
||||||
import { cliApi } from "../lib/api-client"
|
import { cliApi } from "../lib/api-client"
|
||||||
|
|
||||||
function normalizePathKey(input?: string | null) {
|
function normalizePathKey(input?: string | null) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Component, Show, For, createSignal, createMemo, createEffect, onCleanup } from "solid-js"
|
import { Component, Show, For, createSignal, createMemo, createEffect, onCleanup } from "solid-js"
|
||||||
import { Folder as FolderIcon, File as FileIcon, Loader2, Search, X, ArrowUpLeft } from "lucide-solid"
|
import { Folder as FolderIcon, File as FileIcon, Loader2, Search, X, ArrowUpLeft } from "lucide-solid"
|
||||||
import type { FileSystemEntry, FileSystemListingMetadata } from "../../../cli/src/api-types"
|
import type { FileSystemEntry, FileSystemListingMetadata } from "../../../server/src/api-types"
|
||||||
import { cliApi } from "../lib/api-client"
|
import { cliApi } from "../lib/api-client"
|
||||||
|
|
||||||
const MAX_RESULTS = 200
|
const MAX_RESULTS = 200
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import type {
|
|||||||
WorkspaceLogEntry,
|
WorkspaceLogEntry,
|
||||||
WorkspaceEventPayload,
|
WorkspaceEventPayload,
|
||||||
WorkspaceEventType,
|
WorkspaceEventType,
|
||||||
} from "../../../cli/src/api-types"
|
} from "../../../server/src/api-types"
|
||||||
|
|
||||||
const FALLBACK_API_BASE = "http://127.0.0.1:9898"
|
const FALLBACK_API_BASE = "http://127.0.0.1:9898"
|
||||||
const RUNTIME_BASE = typeof window !== "undefined" ? window.location?.origin : undefined
|
const RUNTIME_BASE = typeof window !== "undefined" ? window.location?.origin : undefined
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { WorkspaceEventPayload, WorkspaceEventType } from "../../../cli/src/api-types"
|
import type { WorkspaceEventPayload, WorkspaceEventType } from "../../../server/src/api-types"
|
||||||
import { cliApi } from "./api-client"
|
import { cliApi } from "./api-client"
|
||||||
|
|
||||||
const RETRY_BASE_DELAY = 1000
|
const RETRY_BASE_DELAY = 1000
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { ServerMeta } from "../../../cli/src/api-types"
|
import type { ServerMeta } from "../../../server/src/api-types"
|
||||||
import { cliApi } from "./api-client"
|
import { cliApi } from "./api-client"
|
||||||
|
|
||||||
let cachedMeta: ServerMeta | null = null
|
let cachedMeta: ServerMeta | null = null
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { AppConfig, InstanceData } from "../../../cli/src/api-types"
|
import type { AppConfig, InstanceData } from "../../../server/src/api-types"
|
||||||
import { cliApi } from "./api-client"
|
import { cliApi } from "./api-client"
|
||||||
import { cliEvents } from "./cli-events"
|
import { cliEvents } from "./cli-events"
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { createContext, createMemo, createSignal, onCleanup, type Accessor, type ParentComponent, useContext } from "solid-js"
|
import { createContext, createMemo, createSignal, onCleanup, type Accessor, type ParentComponent, useContext } from "solid-js"
|
||||||
import type { InstanceData } from "../../../cli/src/api-types"
|
import type { InstanceData } from "../../../server/src/api-types"
|
||||||
import { storage } from "../lib/storage"
|
import { storage } from "../lib/storage"
|
||||||
|
|
||||||
const DEFAULT_INSTANCE_DATA: InstanceData = { messageHistory: [], agentModelSelections: {} }
|
const DEFAULT_INSTANCE_DATA: InstanceData = { messageHistory: [], agentModelSelections: {} }
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { sdkManager } from "../lib/sdk-manager"
|
|||||||
import { sseManager } from "../lib/sse-manager"
|
import { sseManager } from "../lib/sse-manager"
|
||||||
import { cliApi } from "../lib/api-client"
|
import { cliApi } from "../lib/api-client"
|
||||||
import { cliEvents } from "../lib/cli-events"
|
import { cliEvents } from "../lib/cli-events"
|
||||||
import type { WorkspaceDescriptor, WorkspaceEventPayload, WorkspaceLogEntry } from "../../../cli/src/api-types"
|
import type { WorkspaceDescriptor, WorkspaceEventPayload, WorkspaceLogEntry } from "../../../server/src/api-types"
|
||||||
import { ensureInstanceConfigLoaded } from "./instance-config"
|
import { ensureInstanceConfigLoaded } from "./instance-config"
|
||||||
import {
|
import {
|
||||||
fetchSessions,
|
fetchSessions,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { InstanceData } from "../../../cli/src/api-types"
|
import type { InstanceData } from "../../../server/src/api-types"
|
||||||
import {
|
import {
|
||||||
ensureInstanceConfigLoaded,
|
ensureInstanceConfigLoaded,
|
||||||
getInstanceConfig,
|
getInstanceConfig,
|
||||||
|
|||||||
Reference in New Issue
Block a user