feat(opencode-go): accept oauth credentials and sendable pipe buffer
46 unmodified lines47484950515152535455598 unmodified linesExpand all46 unmodified lines }, .{ .provider = "opencode-go", .providerName = "OpenCode Zen", .credentialKinds = &[_][]const u8{"apiKey"}, .credentialKinds = &[_][]const u8{ "oauth", "apiKey" }, }, .{ .provider = "zai", .providerName = "Z.ai",598 unmodified linesExpand all278 unmodified linesExpand all279280281282283284285286287288289290291292327 unmodified linesExpand all278 unmodified linesExpand all try std.testing.expect(std.mem.indexOf(u8, text, "\"id\":\"rolling\",\"label\":\"Session\",\"usedPercent\":10,\"windowLabel\":\"5h window\",\"windowSeconds\":18000,\"resetsAt\":1789533614000,\"status\":\"ok\"") != null); // rate-limited status -> exhausted even though percent is 50 try std.testing.expect(std.mem.indexOf(u8, text, "\"id\":\"monthly\",\"label\":\"Monthly\",\"usedPercent\":50,\"windowLabel\":\"30d window\",\"windowSeconds\":2592000,\"resetsAt\":1790665316000,\"status\":\"exhausted\"") != null); try std.testing.expect(std.mem.indexOf(u8, text, "\"notes\":[\"OpenCode Go unified models\"]") != null);
const oauth_cred = "{\"kind\":\"oauth\",\"access\":\"opencode-oauth-tok\",\"email\":\"go@example.com\"}"; const oauth_req = try advance(a, "opencode-go", oauth_cred, "[]", 0); try std.testing.expect(std.mem.indexOf(u8, oauth_req, "[\"Authorization\",\"Bearer opencode-oauth-tok\"]") != null); const oauth_rep = try advance(a, "opencode-go", oauth_cred, body, 0); try std.testing.expect(std.mem.indexOf(u8, oauth_rep, "\"email\":\"go@example.com\"") != null);}
test "zai authorization without Bearer, epoch seconds/ms, and ratio fallback" { var arena = std.heap.ArenaAllocator.init(std.testing.allocator);327 unmodified linesExpand all73 unmodified lines74757677787978798081828384858682878889909141 unmodified lines73 unmodified lines const cred_root = usage.asObject(cred_parsed.value) orelse { return .{ .report = .{ .error_message = "invalid credential JSON" } }; }; const kind = usage.asString(cred_root.get("kind")) orelse ""; const is_oauth = std.mem.eql(u8, kind, "oauth"); const is_api_key = std.mem.eql(u8, kind, "apiKey"); if (!std.mem.eql(u8, kind, "apiKey")) { return .{ .report = .{ .error_message = "opencode-go needs an apiKey credential" } }; if (!is_oauth and !is_api_key) { return .{ .report = .{ .error_message = "opencode-go needs an apiKey or OAuth credential" } }; }
const api_key = if (is_oauth) usage.asString(cred_root.get("access")) orelse "" else const api_key = usage.asString(cred_root.get("apiKey")) orelse ""; usage.asString(cred_root.get("apiKey")) orelse ""; const cred_account_id = usage.asString(cred_root.get("accountId")); const cred_email = usage.asString(cred_root.get("email"));
const resp_parsed = std.json.parseFromSlice(std.json.Value, alloc, responses_json, .{}) catch {41 unmodified lines52 unmodified lines5354555657585960616263646566676869707172737475767778798058818283848549 unmodified lines13513613713811611711813914014114212112212312412512612712814314414514613213313413513613713813914714814915015127 unmodified lines17918018118217517617717817918018118218318418518318418518718618718818919019119319219319419519641 unmodified lines52 unmodified lines error: "usage feed for \(provider) did not finish in \(maxRounds) rounds" ) }
private final class PipeBuffer: @unchecked Sendable { private let lock = NSLock() private let maxBytes: Int private var data = Data()
init(maxBytes: Int) { self.maxBytes = maxBytes }
func append(_ chunk: Data) { if chunk.isEmpty { return } lock.withLock { if data.count < maxBytes { data.append(chunk.prefix(maxBytes - data.count)) } } }
func snapshot() -> Data { lock.withLock { data } } }
/// Run a command request using Process with the environment allowlist, timeout, and stdout cap. public static func runCommand(_ request: UsageRequest) async throws -> UsageResponse { @Sendable public static func runCommand(_ request: UsageRequest) async throws -> UsageResponse { guard let cmd = request.command, !cmd.isEmpty else { return UsageResponse(status: 127, body: "no command specified") }
49 unmodified lines }
let timeoutSec: TimeInterval = Double(request.timeoutMs ?? 15_000) / 1000.0 let maxBytes = 64 * 1024 var stdoutData = Data() var stderrData = Data() let lock = NSLock() let stdoutBuffer = PipeBuffer(maxBytes: maxBytes) let stderrBuffer = PipeBuffer(maxBytes: maxBytes)
stdoutPipe.fileHandleForReading.readabilityHandler = { handle in let chunk = handle.availableData if chunk.isEmpty { return } lock.lock() if stdoutData.count < maxBytes { let remaining = maxBytes - stdoutData.count stdoutData.append(chunk.prefix(remaining)) } lock.unlock() stdoutBuffer.append(handle.availableData) }
stderrPipe.fileHandleForReading.readabilityHandler = { handle in let chunk = handle.availableData if chunk.isEmpty { return } lock.lock() if stderrData.count < maxBytes { let remaining = maxBytes - stderrData.count stderrData.append(chunk.prefix(remaining)) } lock.unlock() stderrBuffer.append(handle.availableData) }
do { try process.run()27 unmodified lines
stdoutPipe.fileHandleForReading.readabilityHandler = nil stderrPipe.fileHandleForReading.readabilityHandler = nil
let trailingOut = stdoutPipe.fileHandleForReading.availableData if !trailingOut.isEmpty { lock.lock() if stdoutData.count < maxBytes { let remaining = maxBytes - stdoutData.count stdoutData.append(trailingOut.prefix(remaining)) } lock.unlock() } let trailingErr = stderrPipe.fileHandleForReading.availableData _ = trailingErr stdoutBuffer.append(stdoutPipe.fileHandleForReading.availableData) stderrBuffer.append(stderrPipe.fileHandleForReading.availableData)
var body = String(data: stdoutData, encoding: .utf8) ?? "" var body = String(data: stdoutBuffer.snapshot(), encoding: .utf8) ?? "" let exitCode = Int(process.terminationStatus)
// A command that wrote nothing to stdout but failed still has something to say, and saying it // on stderr is how a CLI reports a missing argument or a bad credential. Without this the feed // only sees an empty body and cannot tell the user why nothing came back. if exitCode != 0, body.isEmpty, let errorText = String(data: stderrData, encoding: .utf8), !errorText.isEmpty { if exitCode != 0, body.isEmpty, let errorText = String(data: stderrBuffer.snapshot(), encoding: .utf8), !errorText.isEmpty { body = errorText }
return UsageResponse(status: exitCode, body: body)41 unmodified lines38 unmodified lines394041424344454647141 unmodified linesExpand all38 unmodified lines
let providers = try UsageCore.providers() check(providers.contains { $0.provider == "openai-codex" }, "codex should be a known provider") check(providers.first { $0.provider == "anthropic" }?.credentialKinds == ["oauth"], "anthropic takes oauth") check(providers.first { $0.provider == "opencode-go" }?.credentialKinds == ["oauth", "apiKey"], "opencode-go takes oauth and apiKey")
let codexCredential = UsageDriver.credentialJSON(kind: "oauth", value: "test-token", email: "codex@example.com") let firstStep = try UsageCore.advance(provider: "openai-codex", credentialJSON: codexCredential, responses: [], nowMs: 1_789_514_000_000) guard case .requests(let requests) = firstStep else { fail("expected a request, not a report") }141 unmodified linesExpand all