Fix cmux omo bootstrap with yanked deps (#2280)
This commit is contained in:
parent
97fee253b5
commit
f0c3ccc314
2 changed files with 243 additions and 58 deletions
267
CLI/cmux.swift
267
CLI/cmux.swift
|
|
@ -9688,6 +9688,163 @@ struct CMUXCLI {
|
||||||
.appendingPathComponent("omo-config", isDirectory: true)
|
.appendingPathComponent("omo-config", isDirectory: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func omoFileType(at url: URL) -> FileAttributeType? {
|
||||||
|
let attrs = try? FileManager.default.attributesOfItem(atPath: url.path)
|
||||||
|
return attrs?[.type] as? FileAttributeType
|
||||||
|
}
|
||||||
|
|
||||||
|
private func omoEnsureShadowPackageManifest(at shadowPackageURL: URL) throws {
|
||||||
|
let fm = FileManager.default
|
||||||
|
if omoFileType(at: shadowPackageURL) == .typeSymbolicLink {
|
||||||
|
try? fm.removeItem(at: shadowPackageURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep the shadow package isolated from stale/yanked pins in the user's
|
||||||
|
// opencode package.json. bun will update this manifest with the resolved
|
||||||
|
// oh-my-opencode version when installation succeeds.
|
||||||
|
let packageManifest: [String: Any] = [
|
||||||
|
"dependencies": [
|
||||||
|
Self.omoPluginName: "latest"
|
||||||
|
],
|
||||||
|
"name": "cmux-omo-shadow",
|
||||||
|
"private": true
|
||||||
|
]
|
||||||
|
let output = try JSONSerialization.data(withJSONObject: packageManifest, options: [.prettyPrinted, .sortedKeys])
|
||||||
|
let existing = try? Data(contentsOf: shadowPackageURL)
|
||||||
|
if existing != output {
|
||||||
|
try output.write(to: shadowPackageURL, options: .atomic)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func omoEnsureShadowNodeModulesSymlink(
|
||||||
|
shadowNodeModules: URL,
|
||||||
|
userNodeModules: URL
|
||||||
|
) throws {
|
||||||
|
let fm = FileManager.default
|
||||||
|
guard fm.fileExists(atPath: userNodeModules.path) else { return }
|
||||||
|
|
||||||
|
if let type = omoFileType(at: shadowNodeModules) {
|
||||||
|
if type == .typeSymbolicLink {
|
||||||
|
let target = try? fm.destinationOfSymbolicLink(atPath: shadowNodeModules.path)
|
||||||
|
if target != userNodeModules.path {
|
||||||
|
try? fm.removeItem(at: shadowNodeModules)
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !fm.fileExists(atPath: shadowNodeModules.path) {
|
||||||
|
try fm.createSymbolicLink(at: shadowNodeModules, withDestinationURL: userNodeModules)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func omoRunPackageInstall(
|
||||||
|
executablePath: String,
|
||||||
|
arguments: [String],
|
||||||
|
currentDirectoryURL: URL
|
||||||
|
) throws -> Int32 {
|
||||||
|
let process = Process()
|
||||||
|
process.currentDirectoryURL = currentDirectoryURL
|
||||||
|
process.executableURL = URL(fileURLWithPath: executablePath)
|
||||||
|
process.arguments = arguments
|
||||||
|
process.standardOutput = FileHandle.standardError
|
||||||
|
process.standardError = FileHandle.standardError
|
||||||
|
try process.run()
|
||||||
|
process.waitUntilExit()
|
||||||
|
return process.terminationStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
private func omoRequestedPort(from commandArgs: [String]) -> String? {
|
||||||
|
for (index, arg) in commandArgs.enumerated() {
|
||||||
|
if arg == "--port" {
|
||||||
|
let nextIndex = commandArgs.index(after: index)
|
||||||
|
guard nextIndex < commandArgs.endIndex else { return nil }
|
||||||
|
let value = commandArgs[nextIndex].trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
|
return value.isEmpty ? nil : value
|
||||||
|
}
|
||||||
|
|
||||||
|
if arg.hasPrefix("--port=") {
|
||||||
|
let value = String(arg.dropFirst("--port=".count)).trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
|
return value.isEmpty ? nil : value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
private func omoBindableLoopbackPort(_ port: UInt16) -> UInt16? {
|
||||||
|
let socketDescriptor = socket(AF_INET, SOCK_STREAM, 0)
|
||||||
|
guard socketDescriptor >= 0 else { return nil }
|
||||||
|
defer { close(socketDescriptor) }
|
||||||
|
|
||||||
|
var reuseAddress: Int32 = 1
|
||||||
|
_ = setsockopt(
|
||||||
|
socketDescriptor,
|
||||||
|
SOL_SOCKET,
|
||||||
|
SO_REUSEADDR,
|
||||||
|
&reuseAddress,
|
||||||
|
socklen_t(MemoryLayout<Int32>.size)
|
||||||
|
)
|
||||||
|
|
||||||
|
var address = sockaddr_in()
|
||||||
|
address.sin_len = UInt8(MemoryLayout<sockaddr_in>.stride)
|
||||||
|
address.sin_family = sa_family_t(AF_INET)
|
||||||
|
address.sin_port = port.bigEndian
|
||||||
|
address.sin_addr = in_addr(s_addr: inet_addr("127.0.0.1"))
|
||||||
|
|
||||||
|
let bindResult = withUnsafePointer(to: &address) {
|
||||||
|
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
|
||||||
|
Darwin.bind(socketDescriptor, $0, socklen_t(MemoryLayout<sockaddr_in>.stride))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
guard bindResult == 0 else { return nil }
|
||||||
|
|
||||||
|
if port != 0 {
|
||||||
|
return port
|
||||||
|
}
|
||||||
|
|
||||||
|
var boundAddress = address
|
||||||
|
var boundAddressLength = socklen_t(MemoryLayout<sockaddr_in>.stride)
|
||||||
|
let nameResult = withUnsafeMutablePointer(to: &boundAddress) {
|
||||||
|
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
|
||||||
|
getsockname(socketDescriptor, $0, &boundAddressLength)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
guard nameResult == 0 else { return nil }
|
||||||
|
|
||||||
|
return UInt16(bigEndian: boundAddress.sin_port)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func omoResolvedPort(
|
||||||
|
commandArgs: [String],
|
||||||
|
processEnvironment: [String: String]
|
||||||
|
) -> String {
|
||||||
|
if let requestedPort = omoRequestedPort(from: commandArgs) {
|
||||||
|
return requestedPort
|
||||||
|
}
|
||||||
|
|
||||||
|
if let environmentPort = processEnvironment["OPENCODE_PORT"]?
|
||||||
|
.trimmingCharacters(in: .whitespacesAndNewlines),
|
||||||
|
let parsedEnvironmentPort = UInt16(environmentPort),
|
||||||
|
parsedEnvironmentPort != 0,
|
||||||
|
omoBindableLoopbackPort(parsedEnvironmentPort) != nil {
|
||||||
|
return environmentPort
|
||||||
|
}
|
||||||
|
|
||||||
|
if let preferredPort = omoBindableLoopbackPort(4096) {
|
||||||
|
return String(preferredPort)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let fallbackPort = omoBindableLoopbackPort(0) {
|
||||||
|
return String(fallbackPort)
|
||||||
|
}
|
||||||
|
|
||||||
|
return "4096"
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates a shadow config directory that layers oh-my-opencode on top of the user's
|
/// Creates a shadow config directory that layers oh-my-opencode on top of the user's
|
||||||
/// existing opencode config without modifying the original. Sets OPENCODE_CONFIG_DIR
|
/// existing opencode config without modifying the original. Sets OPENCODE_CONFIG_DIR
|
||||||
/// to point at the shadow directory.
|
/// to point at the shadow directory.
|
||||||
|
|
@ -9727,27 +9884,15 @@ struct CMUXCLI {
|
||||||
// Symlink node_modules from the user's config dir so installed packages resolve
|
// Symlink node_modules from the user's config dir so installed packages resolve
|
||||||
let shadowNodeModules = shadowDir.appendingPathComponent("node_modules")
|
let shadowNodeModules = shadowDir.appendingPathComponent("node_modules")
|
||||||
let userNodeModules = userDir.appendingPathComponent("node_modules")
|
let userNodeModules = userDir.appendingPathComponent("node_modules")
|
||||||
if fm.fileExists(atPath: userNodeModules.path) {
|
try omoEnsureShadowNodeModulesSymlink(shadowNodeModules: shadowNodeModules, userNodeModules: userNodeModules)
|
||||||
// Remove stale symlink or directory if it exists
|
|
||||||
if let attrs = try? fm.attributesOfItem(atPath: shadowNodeModules.path),
|
|
||||||
attrs[.type] as? FileAttributeType == .typeSymbolicLink {
|
|
||||||
let target = try? fm.destinationOfSymbolicLink(atPath: shadowNodeModules.path)
|
|
||||||
if target != userNodeModules.path {
|
|
||||||
try? fm.removeItem(at: shadowNodeModules)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !fm.fileExists(atPath: shadowNodeModules.path) {
|
|
||||||
try fm.createSymbolicLink(at: shadowNodeModules, withDestinationURL: userNodeModules)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Symlink package.json and bun.lock so bun/npm can resolve in the shadow dir
|
// The shadow config owns its own package metadata so yanked/stale pins in the
|
||||||
for filename in ["package.json", "bun.lock"] {
|
// user's opencode package.json/bun.lock cannot poison plugin installation.
|
||||||
let userFile = userDir.appendingPathComponent(filename)
|
let shadowPackageURL = shadowDir.appendingPathComponent("package.json")
|
||||||
let shadowFile = shadowDir.appendingPathComponent(filename)
|
let shadowBunLockURL = shadowDir.appendingPathComponent("bun.lock")
|
||||||
if fm.fileExists(atPath: userFile.path) && !fm.fileExists(atPath: shadowFile.path) {
|
try omoEnsureShadowPackageManifest(at: shadowPackageURL)
|
||||||
try fm.createSymbolicLink(at: shadowFile, withDestinationURL: userFile)
|
if omoFileType(at: shadowBunLockURL) == .typeSymbolicLink {
|
||||||
}
|
try? fm.removeItem(at: shadowBunLockURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy oh-my-opencode plugin config (jsonc) if the user has one
|
// Copy oh-my-opencode plugin config (jsonc) if the user has one
|
||||||
|
|
@ -9762,37 +9907,43 @@ struct CMUXCLI {
|
||||||
// Install the package if not available via the symlinked node_modules
|
// Install the package if not available via the symlinked node_modules
|
||||||
let pluginPackageDir = shadowNodeModules.appendingPathComponent(Self.omoPluginName)
|
let pluginPackageDir = shadowNodeModules.appendingPathComponent(Self.omoPluginName)
|
||||||
if !fm.fileExists(atPath: pluginPackageDir.path) {
|
if !fm.fileExists(atPath: pluginPackageDir.path) {
|
||||||
// Need to install into the real user config dir so the symlink picks it up
|
let installDir = shadowDir
|
||||||
let installDir = fm.fileExists(atPath: userNodeModules.path) ? userDir : shadowDir
|
|
||||||
// If installing into shadow dir, remove the symlink first
|
|
||||||
if installDir == shadowDir {
|
|
||||||
try? fm.removeItem(at: shadowNodeModules)
|
|
||||||
}
|
|
||||||
let process = Process()
|
|
||||||
process.currentDirectoryURL = installDir
|
|
||||||
if let bunPath = resolveExecutableInPath("bun") {
|
if let bunPath = resolveExecutableInPath("bun") {
|
||||||
process.executableURL = URL(fileURLWithPath: bunPath)
|
FileHandle.standardError.write("Installing oh-my-opencode plugin (this may take a minute on first run)...\n".data(using: .utf8)!)
|
||||||
process.arguments = ["add", Self.omoPluginName]
|
let installArguments = ["add", Self.omoPluginName]
|
||||||
|
let firstAttemptStatus = try omoRunPackageInstall(
|
||||||
|
executablePath: bunPath,
|
||||||
|
arguments: installArguments,
|
||||||
|
currentDirectoryURL: installDir
|
||||||
|
)
|
||||||
|
if firstAttemptStatus != 0 {
|
||||||
|
FileHandle.standardError.write("Retrying oh-my-opencode install with a clean shadow package state...\n".data(using: .utf8)!)
|
||||||
|
try? fm.removeItem(at: shadowBunLockURL)
|
||||||
|
try? fm.removeItem(at: shadowNodeModules)
|
||||||
|
try omoEnsureShadowNodeModulesSymlink(shadowNodeModules: shadowNodeModules, userNodeModules: userNodeModules)
|
||||||
|
let retryStatus = try omoRunPackageInstall(
|
||||||
|
executablePath: bunPath,
|
||||||
|
arguments: installArguments,
|
||||||
|
currentDirectoryURL: installDir
|
||||||
|
)
|
||||||
|
if retryStatus != 0 {
|
||||||
|
throw CLIError(message: "Failed to install oh-my-opencode. Try manually: npm install -g oh-my-opencode")
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if let npmPath = resolveExecutableInPath("npm") {
|
} else if let npmPath = resolveExecutableInPath("npm") {
|
||||||
process.executableURL = URL(fileURLWithPath: npmPath)
|
FileHandle.standardError.write("Installing oh-my-opencode plugin (this may take a minute on first run)...\n".data(using: .utf8)!)
|
||||||
process.arguments = ["install", Self.omoPluginName]
|
let status = try omoRunPackageInstall(
|
||||||
|
executablePath: npmPath,
|
||||||
|
arguments: ["install", Self.omoPluginName],
|
||||||
|
currentDirectoryURL: installDir
|
||||||
|
)
|
||||||
|
if status != 0 {
|
||||||
|
throw CLIError(message: "Failed to install oh-my-opencode. Try manually: npm install -g oh-my-opencode")
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
throw CLIError(message: "Neither bun nor npm found in PATH. Install oh-my-opencode manually: bunx oh-my-opencode install")
|
throw CLIError(message: "Neither bun nor npm found in PATH. Install oh-my-opencode manually: bunx oh-my-opencode install")
|
||||||
}
|
}
|
||||||
// Show install output directly so the user sees progress (npm can take 30s+)
|
|
||||||
process.standardOutput = FileHandle.standardError
|
|
||||||
process.standardError = FileHandle.standardError
|
|
||||||
FileHandle.standardError.write("Installing oh-my-opencode plugin (this may take a minute on first run)...\n".data(using: .utf8)!)
|
|
||||||
try process.run()
|
|
||||||
process.waitUntilExit()
|
|
||||||
if process.terminationStatus != 0 {
|
|
||||||
throw CLIError(message: "Failed to install oh-my-opencode. Try manually: npm install -g oh-my-opencode")
|
|
||||||
}
|
|
||||||
FileHandle.standardError.write("oh-my-opencode plugin installed\n".data(using: .utf8)!)
|
FileHandle.standardError.write("oh-my-opencode plugin installed\n".data(using: .utf8)!)
|
||||||
// Re-create symlink if we installed into user dir
|
|
||||||
if installDir == userDir && !fm.fileExists(atPath: shadowNodeModules.path) {
|
|
||||||
try fm.createSymbolicLink(at: shadowNodeModules, withDestinationURL: userNodeModules)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure tmux mode is enabled in oh-my-opencode config.
|
// Ensure tmux mode is enabled in oh-my-opencode config.
|
||||||
|
|
@ -9857,13 +10008,9 @@ struct CMUXCLI {
|
||||||
executablePath: String,
|
executablePath: String,
|
||||||
socketPath: String,
|
socketPath: String,
|
||||||
explicitPassword: String?,
|
explicitPassword: String?,
|
||||||
focusedContext: TmuxCompatFocusedContext?
|
focusedContext: TmuxCompatFocusedContext?,
|
||||||
|
openCodePort: String
|
||||||
) {
|
) {
|
||||||
// Tell oh-my-opencode the API server port so subagent attach works
|
|
||||||
var extraEnvVars: [(key: String, value: String)] = []
|
|
||||||
if processEnvironment["OPENCODE_PORT"] == nil {
|
|
||||||
extraEnvVars.append((key: "OPENCODE_PORT", value: "4096"))
|
|
||||||
}
|
|
||||||
configureTmuxCompatEnvironment(
|
configureTmuxCompatEnvironment(
|
||||||
processEnvironment: processEnvironment,
|
processEnvironment: processEnvironment,
|
||||||
shimDirectory: shimDirectory,
|
shimDirectory: shimDirectory,
|
||||||
|
|
@ -9874,7 +10021,7 @@ struct CMUXCLI {
|
||||||
tmuxPathPrefix: "cmux-omo",
|
tmuxPathPrefix: "cmux-omo",
|
||||||
cmuxBinEnvVar: "CMUX_OMO_CMUX_BIN",
|
cmuxBinEnvVar: "CMUX_OMO_CMUX_BIN",
|
||||||
termOverrideEnvVar: "CMUX_OMO_TERM",
|
termOverrideEnvVar: "CMUX_OMO_TERM",
|
||||||
extraEnvVars: extraEnvVars
|
extraEnvVars: [(key: "OPENCODE_PORT", value: openCodePort)]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -9916,23 +10063,29 @@ struct CMUXCLI {
|
||||||
processEnvironment: launcherEnvironment,
|
processEnvironment: launcherEnvironment,
|
||||||
explicitPassword: explicitPassword
|
explicitPassword: explicitPassword
|
||||||
)
|
)
|
||||||
|
let openCodePort = omoResolvedPort(
|
||||||
|
commandArgs: commandArgs,
|
||||||
|
processEnvironment: launcherEnvironment
|
||||||
|
)
|
||||||
|
launcherEnvironment["OPENCODE_PORT"] = openCodePort
|
||||||
configureOMOEnvironment(
|
configureOMOEnvironment(
|
||||||
processEnvironment: launcherEnvironment,
|
processEnvironment: launcherEnvironment,
|
||||||
shimDirectory: shimDirectory,
|
shimDirectory: shimDirectory,
|
||||||
executablePath: executablePath,
|
executablePath: executablePath,
|
||||||
socketPath: socketPath,
|
socketPath: socketPath,
|
||||||
explicitPassword: explicitPassword,
|
explicitPassword: explicitPassword,
|
||||||
focusedContext: focusedContext
|
focusedContext: focusedContext,
|
||||||
|
openCodePort: openCodePort
|
||||||
)
|
)
|
||||||
|
|
||||||
let launchPath = openCodeExecutablePath ?? "opencode"
|
let launchPath = openCodeExecutablePath ?? "opencode"
|
||||||
// oh-my-openagent needs the OpenCode API server running to attach
|
// oh-my-openagent needs the OpenCode API server running to attach
|
||||||
// subagent sessions to tmux panes. Inject --port 4096 unless the
|
// subagent sessions to tmux panes. Prefer the historic default port
|
||||||
// user already specified a port.
|
// when it is available, otherwise fall back to a free loopback port.
|
||||||
var effectiveArgs = commandArgs
|
var effectiveArgs = commandArgs
|
||||||
if !commandArgs.contains("--port") {
|
if omoRequestedPort(from: commandArgs) == nil {
|
||||||
effectiveArgs.append("--port")
|
effectiveArgs.append("--port")
|
||||||
effectiveArgs.append("4096")
|
effectiveArgs.append(openCodePort)
|
||||||
}
|
}
|
||||||
var argv = ([launchPath] + effectiveArgs).map { strdup($0) }
|
var argv = ([launchPath] + effectiveArgs).map { strdup($0) }
|
||||||
defer {
|
defer {
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,27 @@ CMUX_DEBUG_LOG=""
|
||||||
CLI_PATH=""
|
CLI_PATH=""
|
||||||
LAST_SOCKET_PATH_DIR="$HOME/Library/Application Support/cmux"
|
LAST_SOCKET_PATH_DIR="$HOME/Library/Application Support/cmux"
|
||||||
LAST_SOCKET_PATH_FILE="${LAST_SOCKET_PATH_DIR}/last-socket-path"
|
LAST_SOCKET_PATH_FILE="${LAST_SOCKET_PATH_DIR}/last-socket-path"
|
||||||
|
AUTO_SKIP_ZIG_BUILD_REASON=""
|
||||||
|
|
||||||
|
should_skip_ghostty_cli_helper_zig_build() {
|
||||||
|
if [[ "${CMUX_SKIP_ZIG_BUILD:-}" == "1" ]]; then
|
||||||
|
AUTO_SKIP_ZIG_BUILD_REASON="CMUX_SKIP_ZIG_BUILD=1"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local product_version zig_version major_version
|
||||||
|
product_version="$(sw_vers -productVersion 2>/dev/null || true)"
|
||||||
|
zig_version="$(zig version 2>/dev/null || true)"
|
||||||
|
major_version="${product_version%%.*}"
|
||||||
|
|
||||||
|
if [[ "$zig_version" == "0.15.2" ]] && [[ "$major_version" =~ ^[0-9]+$ ]] && (( major_version >= 26 )); then
|
||||||
|
AUTO_SKIP_ZIG_BUILD_REASON="macOS ${product_version} + zig ${zig_version}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
AUTO_SKIP_ZIG_BUILD_REASON=""
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
write_dev_cli_shim() {
|
write_dev_cli_shim() {
|
||||||
local target="$1"
|
local target="$1"
|
||||||
|
|
@ -258,6 +279,13 @@ if [[ -z "$TAG" ]]; then
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if should_skip_ghostty_cli_helper_zig_build; then
|
||||||
|
if [[ "${CMUX_SKIP_ZIG_BUILD:-}" != "1" ]]; then
|
||||||
|
echo "Auto-enabling CMUX_SKIP_ZIG_BUILD=1 for Ghostty CLI helper (${AUTO_SKIP_ZIG_BUILD_REASON})"
|
||||||
|
fi
|
||||||
|
export CMUX_SKIP_ZIG_BUILD=1
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ -n "$TAG" ]]; then
|
if [[ -n "$TAG" ]]; then
|
||||||
TAG_ID="$(sanitize_bundle "$TAG")"
|
TAG_ID="$(sanitize_bundle "$TAG")"
|
||||||
TAG_SLUG="$(sanitize_path "$TAG")"
|
TAG_SLUG="$(sanitize_path "$TAG")"
|
||||||
|
|
@ -427,7 +455,11 @@ if [[ -d "$PWD/cmuxd" ]]; then
|
||||||
(cd "$PWD/cmuxd" && zig build -Doptimize=ReleaseFast)
|
(cd "$PWD/cmuxd" && zig build -Doptimize=ReleaseFast)
|
||||||
fi
|
fi
|
||||||
if [[ -d "$PWD/ghostty" ]]; then
|
if [[ -d "$PWD/ghostty" ]]; then
|
||||||
(cd "$PWD/ghostty" && zig build cli-helper -Dapp-runtime=none -Demit-macos-app=false -Demit-xcframework=false -Doptimize=ReleaseFast)
|
if [[ "${CMUX_SKIP_ZIG_BUILD:-}" == "1" ]]; then
|
||||||
|
echo "Skipping direct ghostty CLI helper zig build (CMUX_SKIP_ZIG_BUILD=1)"
|
||||||
|
else
|
||||||
|
(cd "$PWD/ghostty" && zig build cli-helper -Dapp-runtime=none -Demit-macos-app=false -Demit-xcframework=false -Doptimize=ReleaseFast)
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
if [[ -x "$CMUXD_SRC" ]]; then
|
if [[ -x "$CMUXD_SRC" ]]; then
|
||||||
BIN_DIR="$APP_PATH/Contents/Resources/bin"
|
BIN_DIR="$APP_PATH/Contents/Resources/bin"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue