import Foundation struct GeminiTestEnvironment { enum GeminiCLILayout { case npmNested case nixShare case fnmBundle case homebrewBundle } let homeURL: URL private let geminiDir: URL private let antigravityDir: URL init() throws { let root = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString) try FileManager.default.createDirectory(at: root, withIntermediateDirectories: true) let geminiDir = root.appendingPathComponent(".gemini") try FileManager.default.createDirectory(at: geminiDir, withIntermediateDirectories: true) let antigravityDir = root .appendingPathComponent(".codexbar") .appendingPathComponent("antigravity") try FileManager.default.createDirectory(at: antigravityDir, withIntermediateDirectories: true) self.homeURL = root self.geminiDir = geminiDir self.antigravityDir = antigravityDir } func cleanup() { try? FileManager.default.removeItem(at: self.homeURL) } func writeSettings(authType: String) throws { let payload: [String: Any] = [ "security": [ "auth": [ "selectedType": authType, ], ], ] let data = try JSONSerialization.data(withJSONObject: payload) try data.write(to: self.geminiDir.appendingPathComponent("settings.json"), options: .atomic) } func writeCredentials( accessToken: String?, refreshToken: String?, expiry: Date, idToken: String?) throws { var payload: [String: Any] = [ "expiry_date": expiry.timeIntervalSince1970 * 1000, ] if let accessToken { payload["access_token"] = accessToken } if let refreshToken { payload["refresh_token"] = refreshToken } if let idToken { payload["id_token"] = idToken } let data = try JSONSerialization.data(withJSONObject: payload) try data.write(to: self.geminiDir.appendingPathComponent("oauth_creds.json"), options: .atomic) } func readCredentials() throws -> [String: Any] { let url = self.geminiDir.appendingPathComponent("oauth_creds.json") let data = try Data(contentsOf: url) let object = try JSONSerialization.jsonObject(with: data) return object as? [String: Any] ?? [:] } func writeAntigravityCredentials( accessToken: String, refreshToken: String?, expiry: Date, idToken: String? = nil, email: String? = nil, projectID: String? = nil, clientID: String? = nil, clientSecret: String? = nil) throws { var payload: [String: Any] = [ "access_token": accessToken, "expiry_date": expiry.timeIntervalSince1970 * 1000, ] if let refreshToken { payload["refresh_token"] = refreshToken } if let idToken { payload["id_token"] = idToken } if let email { payload["email"] = email } if let projectID { payload["project_id"] = projectID } if let clientID { payload["client_id"] = clientID } if let clientSecret { payload["client_secret"] = clientSecret } let data = try JSONSerialization.data(withJSONObject: payload) try data.write(to: self.antigravityDir.appendingPathComponent("oauth_creds.json"), options: .atomic) } func readAntigravityCredentials() throws -> [String: Any] { let url = self.antigravityDir.appendingPathComponent("oauth_creds.json") let data = try Data(contentsOf: url) let object = try JSONSerialization.jsonObject(with: data) return object as? [String: Any] ?? [:] } func writeFakeGeminiCLI(includeOAuth: Bool = true, layout: GeminiCLILayout = .npmNested) throws -> URL { let base = self.homeURL.appendingPathComponent("gemini-cli") let binDir = base.appendingPathComponent("bin") try FileManager.default.createDirectory(at: binDir, withIntermediateDirectories: true) switch layout { case .npmNested: let oauthPath = base .appendingPathComponent("lib") .appendingPathComponent("node_modules") .appendingPathComponent("@google") .appendingPathComponent("gemini-cli") .appendingPathComponent("node_modules") .appendingPathComponent("@google") .appendingPathComponent("gemini-cli-core") .appendingPathComponent("dist") .appendingPathComponent("src") .appendingPathComponent("code_assist") .appendingPathComponent("oauth2.js") if includeOAuth { try FileManager.default.createDirectory( at: oauthPath.deletingLastPathComponent(), withIntermediateDirectories: true) let oauthContent = """ const OAUTH_CLIENT_ID = 'test-client-id'; const OAUTH_CLIENT_SECRET = 'test-client-secret'; """ try oauthContent.write(to: oauthPath, atomically: true, encoding: .utf8) } let geminiBinary = binDir.appendingPathComponent("gemini") try "#!/bin/bash\nexit 0\n".write(to: geminiBinary, atomically: true, encoding: .utf8) try FileManager.default.setAttributes( [.posixPermissions: 0o755], ofItemAtPath: geminiBinary.path) return geminiBinary case .nixShare: let oauthPath = base .appendingPathComponent("share") .appendingPathComponent("gemini-cli") .appendingPathComponent("node_modules") .appendingPathComponent("@google") .appendingPathComponent("gemini-cli-core") .appendingPathComponent("dist") .appendingPathComponent("src") .appendingPathComponent("code_assist") .appendingPathComponent("oauth2.js") if includeOAuth { try FileManager.default.createDirectory( at: oauthPath.deletingLastPathComponent(), withIntermediateDirectories: true) let oauthContent = """ const OAUTH_CLIENT_ID = 'test-client-id'; const OAUTH_CLIENT_SECRET = 'test-client-secret'; """ try oauthContent.write(to: oauthPath, atomically: true, encoding: .utf8) } let geminiBinary = binDir.appendingPathComponent("gemini") try "#!/bin/bash\nexit 0\n".write(to: geminiBinary, atomically: true, encoding: .utf8) try FileManager.default.setAttributes( [.posixPermissions: 0o755], ofItemAtPath: geminiBinary.path) return geminiBinary case .fnmBundle: // Mirror a real fnm multishell layout: bin/gemini is a single relative // symlink into the same multishell's lib/node_modules/@google/gemini-cli, // which is a plain directory with the real package.json + bundle/*.js. let multishellRoot = self.homeURL .appendingPathComponent("Library") .appendingPathComponent("Caches") .appendingPathComponent("fnm_multishells") .appendingPathComponent("12345_67890") let binDir = multishellRoot.appendingPathComponent("bin") let packageRoot = multishellRoot .appendingPathComponent("lib") .appendingPathComponent("node_modules") .appendingPathComponent("@google") .appendingPathComponent("gemini-cli") let bundleDir = packageRoot.appendingPathComponent("bundle") try FileManager.default.createDirectory(at: binDir, withIntermediateDirectories: true) try FileManager.default.createDirectory(at: bundleDir, withIntermediateDirectories: true) let packageJSON = """ { "name": "@google/gemini-cli" } """ try packageJSON.write( to: packageRoot.appendingPathComponent("package.json"), atomically: true, encoding: .utf8) let chunkName = "chunk-TEST123.js" let geminiEntry = bundleDir.appendingPathComponent("gemini.js") let geminiContent = """ #!/usr/bin/env node import { start } from "./\(chunkName)"; start(); """ try geminiContent.write(to: geminiEntry, atomically: true, encoding: .utf8) try FileManager.default.setAttributes( [.posixPermissions: 0o755], ofItemAtPath: geminiEntry.path) let chunkContent = if includeOAuth { """ export const start = () => {}; const OAUTH_CLIENT_ID = 'test-client-id'; const OAUTH_CLIENT_SECRET = 'test-client-secret'; """ } else { "export const start = () => {};\n" } try chunkContent.write( to: bundleDir.appendingPathComponent(chunkName), atomically: true, encoding: .utf8) // Relative symlink matching what fnm actually creates: // fnm_multishells/XXX/bin/gemini -> ../lib/node_modules/@google/gemini-cli/bundle/gemini.js // Use the path-based API so the target is stored as a literal relative // string; the URL-based API resolves URL(fileURLWithPath: "../...") against // the process CWD, which produces a bogus absolute target. let geminiBinary = binDir.appendingPathComponent("gemini") try FileManager.default.createSymbolicLink( atPath: geminiBinary.path, withDestinationPath: "../lib/node_modules/@google/gemini-cli/bundle/gemini.js") return geminiBinary case .homebrewBundle: return try self.writeFakeHomebrewGeminiCLI(base: base, includeOAuth: includeOAuth) } } private func writeFakeHomebrewGeminiCLI(base: URL, includeOAuth: Bool) throws -> URL { let cellarRoot = base .appendingPathComponent("Cellar") .appendingPathComponent("gemini-cli") .appendingPathComponent("0.41.2") let binDir = cellarRoot.appendingPathComponent("bin") let packageRoot = cellarRoot .appendingPathComponent("libexec") .appendingPathComponent("lib") .appendingPathComponent("node_modules") .appendingPathComponent("@google") .appendingPathComponent("gemini-cli") let bundleDir = packageRoot.appendingPathComponent("bundle") try FileManager.default.createDirectory(at: binDir, withIntermediateDirectories: true) try FileManager.default.createDirectory(at: bundleDir, withIntermediateDirectories: true) let packageJSON = """ { "name": "@google/gemini-cli" } """ try packageJSON.write( to: packageRoot.appendingPathComponent("package.json"), atomically: true, encoding: .utf8) let entry = bundleDir.appendingPathComponent("gemini.js") try "#!/usr/bin/env node\nawait import('./gemini-HASH.js');\n".write( to: entry, atomically: true, encoding: .utf8) try FileManager.default.setAttributes( [.posixPermissions: 0o755], ofItemAtPath: entry.path) try "export const run = () => {};\n".write( to: bundleDir.appendingPathComponent("gemini-HASH.js"), atomically: true, encoding: .utf8) let chunkContent = if includeOAuth { """ var OAUTH_CLIENT_ID = "test-client-id"; var OAUTH_CLIENT_SECRET = "test-client-secret"; """ } else { "export const unrelated = true;\n" } try chunkContent.write( to: bundleDir.appendingPathComponent("chunk-OAUTH.js"), atomically: true, encoding: .utf8) let geminiBinary = binDir.appendingPathComponent("gemini") try FileManager.default.createSymbolicLink( atPath: geminiBinary.path, withDestinationPath: "../libexec/lib/node_modules/@google/gemini-cli/bundle/gemini.js") return geminiBinary } func writeFakeFnm( currentVersion: String = "v24.6.0", npmRoot: String? = nil, geminiPackageJSONPath: String, holdNpmRootStdoutOpen: Bool = false) throws -> URL { let binDir = self.homeURL.appendingPathComponent("bin") try FileManager.default.createDirectory(at: binDir, withIntermediateDirectories: true) let fnmPath = binDir.appendingPathComponent("fnm") let stdoutHolder = holdNpmRootStdoutOpen ? #""" python3 - <<'PY' import os import subprocess import sys child = subprocess.Popen( [sys.executable, "-c", "import time; time.sleep(5)"], stdin=subprocess.DEVNULL, stderr=subprocess.DEVNULL, start_new_session=True, ) with open(os.environ["CODEXBAR_TEST_CHILD_PID_FILE"], "w") as handle: handle.write(str(child.pid)) PY """# : ":" let script = if let npmRoot { """ #!/bin/bash if [ "$1" = "current" ]; then printf '%s\n' "\(currentVersion)" exit 0 fi if [ "$1" = "exec" ] && [ "$4" = "npm" ] && [ "$5" = "root" ] && [ "$6" = "-g" ]; then \(stdoutHolder) printf '%s\n' "\(npmRoot)" exit 0 fi if [ "$1" = "exec" ] && [ "$4" = "node" ]; then printf '%s\n' "\(geminiPackageJSONPath)" exit 0 fi exit 1 """ } else { """ #!/bin/bash if [ "$1" = "current" ]; then printf '%s\n' "\(currentVersion)" exit 0 fi if [ "$1" = "exec" ]; then printf '%s\n' "\(geminiPackageJSONPath)" exit 0 fi exit 1 """ } try script.write(to: fnmPath, atomically: true, encoding: .utf8) try FileManager.default.setAttributes( [.posixPermissions: 0o755], ofItemAtPath: fnmPath.path) return fnmPath }}
import Foundation
struct GeminiTestEnvironment {
enum GeminiCLILayout {
case npmNested
case nixShare
case fnmBundle
case homebrewBundle
}
let homeURL: URL
private let geminiDir: URL
private let antigravityDir: URL
init() throws {
let root = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
try FileManager.default.createDirectory(at: root, withIntermediateDirectories: true)
let geminiDir = root.appendingPathComponent(".gemini")
try FileManager.default.createDirectory(at: geminiDir, withIntermediateDirectories: true)
let antigravityDir = root
.appendingPathComponent(".codexbar")
.appendingPathComponent("antigravity")
try FileManager.default.createDirectory(at: antigravityDir, withIntermediateDirectories: true)
self.homeURL = root
self.geminiDir = geminiDir
self.antigravityDir = antigravityDir
}
func cleanup() {
try? FileManager.default.removeItem(at: self.homeURL)
}
func writeSettings(authType: String) throws {
let payload: [String: Any] = [
"security": [
"auth": [
"selectedType": authType,
],
],
]
let data = try JSONSerialization.data(withJSONObject: payload)
try data.write(to: self.geminiDir.appendingPathComponent("settings.json"), options: .atomic)
}
func writeCredentials(
accessToken: String?,
refreshToken: String?,
expiry: Date,
idToken: String?) throws
{
var payload: [String: Any] = [
"expiry_date": expiry.timeIntervalSince1970 * 1000,
]
if let accessToken { payload["access_token"] = accessToken }
if let refreshToken { payload["refresh_token"] = refreshToken }
if let idToken { payload["id_token"] = idToken }
let data = try JSONSerialization.data(withJSONObject: payload)
try data.write(to: self.geminiDir.appendingPathComponent("oauth_creds.json"), options: .atomic)
}
func readCredentials() throws -> [String: Any] {
let url = self.geminiDir.appendingPathComponent("oauth_creds.json")
let data = try Data(contentsOf: url)
let object = try JSONSerialization.jsonObject(with: data)
return object as? [String: Any] ?? [:]
}
func writeAntigravityCredentials(
accessToken: String,
refreshToken: String?,
expiry: Date,
idToken: String? = nil,
email: String? = nil,
projectID: String? = nil,
clientID: String? = nil,
clientSecret: String? = nil) throws
{
var payload: [String: Any] = [
"access_token": accessToken,
"expiry_date": expiry.timeIntervalSince1970 * 1000,
]
if let refreshToken { payload["refresh_token"] = refreshToken }
if let idToken { payload["id_token"] = idToken }
if let email { payload["email"] = email }
if let projectID { payload["project_id"] = projectID }
if let clientID { payload["client_id"] = clientID }
if let clientSecret { payload["client_secret"] = clientSecret }
let data = try JSONSerialization.data(withJSONObject: payload)
try data.write(to: self.antigravityDir.appendingPathComponent("oauth_creds.json"), options: .atomic)
}
func readAntigravityCredentials() throws -> [String: Any] {
let url = self.antigravityDir.appendingPathComponent("oauth_creds.json")
let data = try Data(contentsOf: url)
let object = try JSONSerialization.jsonObject(with: data)
return object as? [String: Any] ?? [:]
}
func writeFakeGeminiCLI(includeOAuth: Bool = true, layout: GeminiCLILayout = .npmNested) throws -> URL {
let base = self.homeURL.appendingPathComponent("gemini-cli")
let binDir = base.appendingPathComponent("bin")
try FileManager.default.createDirectory(at: binDir, withIntermediateDirectories: true)
switch layout {
case .npmNested:
let oauthPath = base
.appendingPathComponent("lib")
.appendingPathComponent("node_modules")
.appendingPathComponent("@google")
.appendingPathComponent("gemini-cli")
.appendingPathComponent("node_modules")
.appendingPathComponent("@google")
.appendingPathComponent("gemini-cli-core")
.appendingPathComponent("dist")
.appendingPathComponent("src")
.appendingPathComponent("code_assist")
.appendingPathComponent("oauth2.js")
if includeOAuth {
try FileManager.default.createDirectory(
at: oauthPath.deletingLastPathComponent(),
withIntermediateDirectories: true)
let oauthContent = """
const OAUTH_CLIENT_ID = 'test-client-id';
const OAUTH_CLIENT_SECRET = 'test-client-secret';
"""
try oauthContent.write(to: oauthPath, atomically: true, encoding: .utf8)
}
let geminiBinary = binDir.appendingPathComponent("gemini")
try "#!/bin/bash\nexit 0\n".write(to: geminiBinary, atomically: true, encoding: .utf8)
try FileManager.default.setAttributes(
[.posixPermissions: 0o755],
ofItemAtPath: geminiBinary.path)
return geminiBinary
case .nixShare:
let oauthPath = base
.appendingPathComponent("share")
.appendingPathComponent("gemini-cli")
.appendingPathComponent("node_modules")
.appendingPathComponent("@google")
.appendingPathComponent("gemini-cli-core")
.appendingPathComponent("dist")
.appendingPathComponent("src")
.appendingPathComponent("code_assist")
.appendingPathComponent("oauth2.js")
if includeOAuth {
try FileManager.default.createDirectory(
at: oauthPath.deletingLastPathComponent(),
withIntermediateDirectories: true)
let oauthContent = """
const OAUTH_CLIENT_ID = 'test-client-id';
const OAUTH_CLIENT_SECRET = 'test-client-secret';
"""
try oauthContent.write(to: oauthPath, atomically: true, encoding: .utf8)
}
let geminiBinary = binDir.appendingPathComponent("gemini")
try "#!/bin/bash\nexit 0\n".write(to: geminiBinary, atomically: true, encoding: .utf8)
try FileManager.default.setAttributes(
[.posixPermissions: 0o755],
ofItemAtPath: geminiBinary.path)
return geminiBinary
case .fnmBundle:
// Mirror a real fnm multishell layout: bin/gemini is a single relative
// symlink into the same multishell's lib/node_modules/@google/gemini-cli,
// which is a plain directory with the real package.json + bundle/*.js.
let multishellRoot = self.homeURL
.appendingPathComponent("Library")
.appendingPathComponent("Caches")
.appendingPathComponent("fnm_multishells")
.appendingPathComponent("12345_67890")
let binDir = multishellRoot.appendingPathComponent("bin")
let packageRoot = multishellRoot
.appendingPathComponent("lib")
.appendingPathComponent("node_modules")
.appendingPathComponent("@google")
.appendingPathComponent("gemini-cli")
let bundleDir = packageRoot.appendingPathComponent("bundle")
try FileManager.default.createDirectory(at: binDir, withIntermediateDirectories: true)
try FileManager.default.createDirectory(at: bundleDir, withIntermediateDirectories: true)
let packageJSON = """
{
"name": "@google/gemini-cli"
}
"""
try packageJSON.write(
to: packageRoot.appendingPathComponent("package.json"),
atomically: true,
encoding: .utf8)
let chunkName = "chunk-TEST123.js"
let geminiEntry = bundleDir.appendingPathComponent("gemini.js")
let geminiContent = """
#!/usr/bin/env node
import { start } from "./\(chunkName)";
start();
"""
try geminiContent.write(to: geminiEntry, atomically: true, encoding: .utf8)
try FileManager.default.setAttributes(
[.posixPermissions: 0o755],
ofItemAtPath: geminiEntry.path)
let chunkContent = if includeOAuth {
"""
export const start = () => {};
const OAUTH_CLIENT_ID = 'test-client-id';
const OAUTH_CLIENT_SECRET = 'test-client-secret';
"""
} else {
"export const start = () => {};\n"
}
try chunkContent.write(
to: bundleDir.appendingPathComponent(chunkName),
atomically: true,
encoding: .utf8)
// Relative symlink matching what fnm actually creates:
// fnm_multishells/XXX/bin/gemini -> ../lib/node_modules/@google/gemini-cli/bundle/gemini.js
// Use the path-based API so the target is stored as a literal relative
// string; the URL-based API resolves URL(fileURLWithPath: "../...") against
// the process CWD, which produces a bogus absolute target.
let geminiBinary = binDir.appendingPathComponent("gemini")
try FileManager.default.createSymbolicLink(
atPath: geminiBinary.path,
withDestinationPath: "../lib/node_modules/@google/gemini-cli/bundle/gemini.js")
return geminiBinary
case .homebrewBundle:
return try self.writeFakeHomebrewGeminiCLI(base: base, includeOAuth: includeOAuth)
}
}
private func writeFakeHomebrewGeminiCLI(base: URL, includeOAuth: Bool) throws -> URL {
let cellarRoot = base
.appendingPathComponent("Cellar")
.appendingPathComponent("gemini-cli")
.appendingPathComponent("0.41.2")
let binDir = cellarRoot.appendingPathComponent("bin")
let packageRoot = cellarRoot
.appendingPathComponent("libexec")
.appendingPathComponent("lib")
.appendingPathComponent("node_modules")
.appendingPathComponent("@google")
.appendingPathComponent("gemini-cli")
let bundleDir = packageRoot.appendingPathComponent("bundle")
try FileManager.default.createDirectory(at: binDir, withIntermediateDirectories: true)
try FileManager.default.createDirectory(at: bundleDir, withIntermediateDirectories: true)
let packageJSON = """
{
"name": "@google/gemini-cli"
}
"""
try packageJSON.write(
to: packageRoot.appendingPathComponent("package.json"),
atomically: true,
encoding: .utf8)
let entry = bundleDir.appendingPathComponent("gemini.js")
try "#!/usr/bin/env node\nawait import('./gemini-HASH.js');\n".write(
to: entry,
atomically: true,
encoding: .utf8)
try FileManager.default.setAttributes(
[.posixPermissions: 0o755],
ofItemAtPath: entry.path)
try "export const run = () => {};\n".write(
to: bundleDir.appendingPathComponent("gemini-HASH.js"),
atomically: true,
encoding: .utf8)
let chunkContent = if includeOAuth {
"""
var OAUTH_CLIENT_ID = "test-client-id";
var OAUTH_CLIENT_SECRET = "test-client-secret";
"""
} else {
"export const unrelated = true;\n"
}
try chunkContent.write(
to: bundleDir.appendingPathComponent("chunk-OAUTH.js"),
atomically: true,
encoding: .utf8)
let geminiBinary = binDir.appendingPathComponent("gemini")
try FileManager.default.createSymbolicLink(
atPath: geminiBinary.path,
withDestinationPath: "../libexec/lib/node_modules/@google/gemini-cli/bundle/gemini.js")
return geminiBinary
}
func writeFakeFnm(
currentVersion: String = "v24.6.0",
npmRoot: String? = nil,
geminiPackageJSONPath: String,
holdNpmRootStdoutOpen: Bool = false) throws -> URL
{
let binDir = self.homeURL.appendingPathComponent("bin")
try FileManager.default.createDirectory(at: binDir, withIntermediateDirectories: true)
let fnmPath = binDir.appendingPathComponent("fnm")
let stdoutHolder = holdNpmRootStdoutOpen
? #"""
python3 - <<'PY'
import os
import subprocess
import sys
child = subprocess.Popen(
[sys.executable, "-c", "import time; time.sleep(5)"],
stdin=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
start_new_session=True,
)
with open(os.environ["CODEXBAR_TEST_CHILD_PID_FILE"], "w") as handle:
handle.write(str(child.pid))
PY
"""#
: ":"
let script = if let npmRoot {
"""
#!/bin/bash
if [ "$1" = "current" ]; then
printf '%s\n' "\(currentVersion)"
exit 0
fi
if [ "$1" = "exec" ] && [ "$4" = "npm" ] && [ "$5" = "root" ] && [ "$6" = "-g" ]; then
\(stdoutHolder)
printf '%s\n' "\(npmRoot)"
exit 0
fi
if [ "$1" = "exec" ] && [ "$4" = "node" ]; then
printf '%s\n' "\(geminiPackageJSONPath)"
exit 0
fi
exit 1
"""
} else {
"""
#!/bin/bash
if [ "$1" = "current" ]; then
printf '%s\n' "\(currentVersion)"
exit 0
fi
if [ "$1" = "exec" ]; then
printf '%s\n' "\(geminiPackageJSONPath)"
exit 0
fi
exit 1
"""
}
try script.write(to: fnmPath, atomically: true, encoding: .utf8)
try FileManager.default.setAttributes(
[.posixPermissions: 0o755],
ofItemAtPath: fnmPath.path)
return fnmPath
}
}