style(macos): tidy settings and CLI
This commit is contained in:
@@ -38,4 +38,3 @@ enum ClawdisConfigFile {
|
||||
self.saveDict(root)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -203,17 +203,17 @@ struct ConfigSettings: View {
|
||||
.toggleStyle(.checkbox)
|
||||
.disabled(!self.browserEnabled)
|
||||
.onChange(of: self.browserAttachOnly) { _, _ in self.autosaveConfig() }
|
||||
.help("When enabled, the browser server will only connect if the clawd browser is already running.")
|
||||
.help(
|
||||
"When enabled, the browser server will only connect if the clawd browser is already running.")
|
||||
}
|
||||
GridRow {
|
||||
Color.clear
|
||||
.frame(width: self.labelColumnWidth, height: 1)
|
||||
Text(
|
||||
"Clawd uses a separate Chrome profile and ports (default 18791/18792) so it won’t interfere with your daily browser."
|
||||
)
|
||||
.font(.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
"Clawd uses a separate Chrome profile and ports (default 18791/18792) so it won’t interfere with your daily browser.")
|
||||
.font(.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -525,7 +525,8 @@ private struct CronJobEditor: View {
|
||||
VStack(alignment: .leading, spacing: 6) {
|
||||
Text(self.job == nil ? "New cron job" : "Edit cron job")
|
||||
.font(.title3.weight(.semibold))
|
||||
Text("Create a schedule that wakes clawd via the Gateway. Use an isolated session for agent turns so your main chat stays clean.")
|
||||
Text(
|
||||
"Create a schedule that wakes clawd via the Gateway. Use an isolated session for agent turns so your main chat stays clean.")
|
||||
.font(.callout)
|
||||
.foregroundStyle(.secondary)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
@@ -570,7 +571,8 @@ private struct CronJobEditor: View {
|
||||
GridRow {
|
||||
Color.clear
|
||||
.frame(width: self.labelColumnWidth, height: 1)
|
||||
Text("Main jobs post a system event into the current main session. Isolated jobs run clawd in a dedicated session and can deliver results (WhatsApp/Telegram/etc).")
|
||||
Text(
|
||||
"Main jobs post a system event into the current main session. Isolated jobs run clawd in a dedicated session and can deliver results (WhatsApp/Telegram/etc).")
|
||||
.font(.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
@@ -594,7 +596,8 @@ private struct CronJobEditor: View {
|
||||
GridRow {
|
||||
Color.clear
|
||||
.frame(width: self.labelColumnWidth, height: 1)
|
||||
Text("“At” runs once, “Every” repeats with a duration, “Cron” uses a 5-field Unix expression.")
|
||||
Text(
|
||||
"“At” runs once, “Every” repeats with a duration, “Cron” uses a 5-field Unix expression.")
|
||||
.font(.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
@@ -604,7 +607,10 @@ private struct CronJobEditor: View {
|
||||
case .at:
|
||||
GridRow {
|
||||
self.gridLabel("At")
|
||||
DatePicker("", selection: self.$atDate, displayedComponents: [.date, .hourAndMinute])
|
||||
DatePicker(
|
||||
"",
|
||||
selection: self.$atDate,
|
||||
displayedComponents: [.date, .hourAndMinute])
|
||||
.labelsHidden()
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
@@ -635,7 +641,8 @@ private struct CronJobEditor: View {
|
||||
GroupBox("Payload") {
|
||||
VStack(alignment: .leading, spacing: 10) {
|
||||
if self.sessionTarget == .isolated {
|
||||
Text("Isolated jobs always run an agent turn. The result can be delivered to a surface, and a short summary is posted back to your main chat.")
|
||||
Text(
|
||||
"Isolated jobs always run an agent turn. The result can be delivered to a surface, and a short summary is posted back to your main chat.")
|
||||
.font(.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
@@ -655,7 +662,8 @@ private struct CronJobEditor: View {
|
||||
GridRow {
|
||||
Color.clear
|
||||
.frame(width: self.labelColumnWidth, height: 1)
|
||||
Text("System events are injected into the current main session. Agent turns require an isolated session target.")
|
||||
Text(
|
||||
"System events are injected into the current main session. Agent turns require an isolated session target.")
|
||||
.font(.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
@@ -687,7 +695,8 @@ private struct CronJobEditor: View {
|
||||
GridRow {
|
||||
Color.clear
|
||||
.frame(width: self.labelColumnWidth, height: 1)
|
||||
Text("Controls the label used when posting the completion summary back to the main session.")
|
||||
Text(
|
||||
"Controls the label used when posting the completion summary back to the main session.")
|
||||
.font(.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
|
||||
@@ -45,13 +45,13 @@ struct MenuContent: View {
|
||||
WebChatManager.shared.show(sessionKey: WebChatManager.shared.preferredSessionKey())
|
||||
}
|
||||
}
|
||||
Toggle(isOn: Binding(
|
||||
get: { self.browserControlEnabled },
|
||||
set: { enabled in
|
||||
self.browserControlEnabled = enabled
|
||||
ClawdisConfigFile.setBrowserControlEnabled(enabled)
|
||||
})
|
||||
) {
|
||||
Toggle(
|
||||
isOn: Binding(
|
||||
get: { self.browserControlEnabled },
|
||||
set: { enabled in
|
||||
self.browserControlEnabled = enabled
|
||||
ClawdisConfigFile.setBrowserControlEnabled(enabled)
|
||||
})) {
|
||||
Text("Browser Control")
|
||||
}
|
||||
Toggle(isOn: Binding(get: { self.state.canvasEnabled }, set: { self.state.canvasEnabled = $0 })) {
|
||||
@@ -110,7 +110,9 @@ struct MenuContent: View {
|
||||
await self.reloadSessionMenu()
|
||||
}
|
||||
} label: {
|
||||
Label(level.capitalized, systemImage: row.thinkingLevel == normalized ? "checkmark" : "")
|
||||
Label(
|
||||
level.capitalized,
|
||||
systemImage: row.thinkingLevel == normalized ? "checkmark" : "")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -126,7 +128,9 @@ struct MenuContent: View {
|
||||
await self.reloadSessionMenu()
|
||||
}
|
||||
} label: {
|
||||
Label(level.capitalized, systemImage: row.verboseLevel == normalized ? "checkmark" : "")
|
||||
Label(
|
||||
level.capitalized,
|
||||
systemImage: row.verboseLevel == normalized ? "checkmark" : "")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,8 @@ final class PeekabooBridgeHostCoordinator {
|
||||
self.host = host
|
||||
|
||||
await host.start()
|
||||
self.logger.info("PeekabooBridge host started at \(PeekabooBridgeConstants.clawdisSocketPath, privacy: .public)")
|
||||
self.logger
|
||||
.info("PeekabooBridge host started at \(PeekabooBridgeConstants.clawdisSocketPath, privacy: .public)")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,4 +28,3 @@ extension View {
|
||||
self.modifier(PointingHandCursorModifier())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,4 +15,3 @@ enum ScreenshotSize {
|
||||
return Size(width: width, height: height)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -224,26 +224,26 @@ struct ToolsSettings: View {
|
||||
EmptyView()
|
||||
} else {
|
||||
VStack(alignment: .leading, spacing: 10) {
|
||||
Text(title)
|
||||
.font(.callout.weight(.semibold))
|
||||
.padding(.top, 6)
|
||||
Text(title)
|
||||
.font(.callout.weight(.semibold))
|
||||
.padding(.top, 6)
|
||||
|
||||
VStack(spacing: 8) {
|
||||
ForEach(filtered) { tool in
|
||||
ToolRow(
|
||||
tool: tool,
|
||||
state: self.binding(for: tool),
|
||||
packageManager: self.packageManager,
|
||||
refreshState: { await self.refresh(tool: tool) })
|
||||
.padding(10)
|
||||
.background(Color(nsColor: .controlBackgroundColor))
|
||||
.clipShape(RoundedRectangle(cornerRadius: 10))
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 10)
|
||||
.stroke(Color.secondary.opacity(0.15), lineWidth: 1))
|
||||
VStack(spacing: 8) {
|
||||
ForEach(filtered) { tool in
|
||||
ToolRow(
|
||||
tool: tool,
|
||||
state: self.binding(for: tool),
|
||||
packageManager: self.packageManager,
|
||||
refreshState: { await self.refresh(tool: tool) })
|
||||
.padding(10)
|
||||
.background(Color(nsColor: .controlBackgroundColor))
|
||||
.clipShape(RoundedRectangle(cornerRadius: 10))
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 10)
|
||||
.stroke(Color.secondary.opacity(0.15), lineWidth: 1))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -639,9 +639,9 @@ enum CommandResolver {
|
||||
return URL(fileURLWithPath: expanded)
|
||||
}
|
||||
|
||||
#if SWIFT_PACKAGE
|
||||
#if SWIFT_PACKAGE
|
||||
static func _testNodeManagerBinPaths(home: URL) -> [String] {
|
||||
self.nodeManagerBinPaths(home: home)
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -36,8 +36,8 @@ struct GatewayChatMessage: Codable, Identifiable {
|
||||
id: UUID = .init(),
|
||||
role: String,
|
||||
content: [GatewayChatMessageContent],
|
||||
timestamp: Double?
|
||||
) {
|
||||
timestamp: Double?)
|
||||
{
|
||||
self.id = id
|
||||
self.role = role
|
||||
self.content = content
|
||||
@@ -124,6 +124,7 @@ final class WebChatViewModel: ObservableObject {
|
||||
private var pendingRuns = Set<String>() {
|
||||
didSet { self.pendingRunCount = self.pendingRuns.count }
|
||||
}
|
||||
|
||||
private var lastHealthPollAt: Date?
|
||||
|
||||
init(sessionKey: String) {
|
||||
@@ -162,7 +163,8 @@ final class WebChatViewModel: ObservableObject {
|
||||
do {
|
||||
let data = try await Task.detached { try Data(contentsOf: url) }.value
|
||||
guard data.count <= 5_000_000 else {
|
||||
await MainActor.run { self.errorText = "Attachment \(url.lastPathComponent) exceeds 5 MB limit" }
|
||||
await MainActor
|
||||
.run { self.errorText = "Attachment \(url.lastPathComponent) exceeds 5 MB limit" }
|
||||
continue
|
||||
}
|
||||
let uti = UTType(filenameExtension: url.pathExtension) ?? .data
|
||||
@@ -445,7 +447,8 @@ struct WebChatView: View {
|
||||
.foregroundStyle(Color.accentColor.opacity(0.9))
|
||||
Text("Say hi to Clawd")
|
||||
.font(.headline)
|
||||
Text(self.viewModel.healthOK ? "This is the native SwiftUI debug chat." : "Connecting to the gateway…")
|
||||
Text(self.viewModel
|
||||
.healthOK ? "This is the native SwiftUI debug chat." : "Connecting to the gateway…")
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
@@ -458,7 +461,9 @@ struct WebChatView: View {
|
||||
} else {
|
||||
ForEach(self.viewModel.messages) { msg in
|
||||
MessageBubble(message: msg)
|
||||
.frame(maxWidth: .infinity, alignment: msg.role.lowercased() == "user" ? .trailing : .leading)
|
||||
.frame(
|
||||
maxWidth: .infinity,
|
||||
alignment: msg.role.lowercased() == "user" ? .trailing : .leading)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -673,7 +678,7 @@ private struct ChatMessageBody: View {
|
||||
switch block.kind {
|
||||
case .text:
|
||||
MarkdownTextView(text: block.text)
|
||||
case .code(let language):
|
||||
case let .code(language):
|
||||
CodeBlockView(code: block.text, language: language)
|
||||
}
|
||||
}
|
||||
@@ -747,7 +752,7 @@ private struct AttachmentRow: View {
|
||||
var body: some View {
|
||||
HStack(spacing: 8) {
|
||||
Image(systemName: "paperclip")
|
||||
Text(att.fileName ?? "Attachment")
|
||||
Text(self.att.fileName ?? "Attachment")
|
||||
.font(.footnote)
|
||||
.lineLimit(1)
|
||||
Spacer()
|
||||
|
||||
@@ -83,25 +83,34 @@ enum BrowserCLI {
|
||||
do {
|
||||
switch sub {
|
||||
case "status":
|
||||
self.printResult(
|
||||
try await self.printResult(
|
||||
jsonOutput: jsonOutput,
|
||||
res: try await self.httpJSON(method: "GET", url: baseURL.appendingPathComponent("/")))
|
||||
res: self.httpJSON(method: "GET", url: baseURL.appendingPathComponent("/")))
|
||||
return 0
|
||||
|
||||
case "start":
|
||||
self.printResult(
|
||||
try await self.printResult(
|
||||
jsonOutput: jsonOutput,
|
||||
res: try await self.httpJSON(method: "POST", url: baseURL.appendingPathComponent("/start"), timeoutInterval: 15.0))
|
||||
res: self.httpJSON(
|
||||
method: "POST",
|
||||
url: baseURL.appendingPathComponent("/start"),
|
||||
timeoutInterval: 15.0))
|
||||
return 0
|
||||
|
||||
case "stop":
|
||||
self.printResult(
|
||||
try await self.printResult(
|
||||
jsonOutput: jsonOutput,
|
||||
res: try await self.httpJSON(method: "POST", url: baseURL.appendingPathComponent("/stop"), timeoutInterval: 15.0))
|
||||
res: self.httpJSON(
|
||||
method: "POST",
|
||||
url: baseURL.appendingPathComponent("/stop"),
|
||||
timeoutInterval: 15.0))
|
||||
return 0
|
||||
|
||||
case "tabs":
|
||||
let res = try await self.httpJSON(method: "GET", url: baseURL.appendingPathComponent("/tabs"), timeoutInterval: 3.0)
|
||||
let res = try await self.httpJSON(
|
||||
method: "GET",
|
||||
url: baseURL.appendingPathComponent("/tabs"),
|
||||
timeoutInterval: 3.0)
|
||||
if jsonOutput {
|
||||
self.printJSON(ok: true, result: res)
|
||||
} else {
|
||||
@@ -114,9 +123,9 @@ enum BrowserCLI {
|
||||
self.printHelp()
|
||||
return 2
|
||||
}
|
||||
self.printResult(
|
||||
try await self.printResult(
|
||||
jsonOutput: jsonOutput,
|
||||
res: try await self.httpJSON(
|
||||
res: self.httpJSON(
|
||||
method: "POST",
|
||||
url: baseURL.appendingPathComponent("/tabs/open"),
|
||||
body: ["url": url],
|
||||
@@ -128,9 +137,9 @@ enum BrowserCLI {
|
||||
self.printHelp()
|
||||
return 2
|
||||
}
|
||||
self.printResult(
|
||||
try await self.printResult(
|
||||
jsonOutput: jsonOutput,
|
||||
res: try await self.httpJSON(
|
||||
res: self.httpJSON(
|
||||
method: "POST",
|
||||
url: baseURL.appendingPathComponent("/tabs/focus"),
|
||||
body: ["targetId": id],
|
||||
@@ -142,9 +151,9 @@ enum BrowserCLI {
|
||||
self.printHelp()
|
||||
return 2
|
||||
}
|
||||
self.printResult(
|
||||
try await self.printResult(
|
||||
jsonOutput: jsonOutput,
|
||||
res: try await self.httpJSON(
|
||||
res: self.httpJSON(
|
||||
method: "DELETE",
|
||||
url: baseURL.appendingPathComponent("/tabs/\(id)"),
|
||||
timeoutInterval: 5.0))
|
||||
@@ -345,8 +354,8 @@ enum BrowserCLI {
|
||||
method: String,
|
||||
url: URL,
|
||||
body: [String: Any]? = nil,
|
||||
timeoutInterval: TimeInterval = 2.0
|
||||
) async throws -> [String: Any] {
|
||||
timeoutInterval: TimeInterval = 2.0) async throws -> [String: Any]
|
||||
{
|
||||
var req = URLRequest(url: url, timeoutInterval: timeoutInterval)
|
||||
req.httpMethod = method
|
||||
if let body {
|
||||
@@ -369,7 +378,7 @@ enum BrowserCLI {
|
||||
])
|
||||
}
|
||||
|
||||
if status >= 200 && status < 300 {
|
||||
if status >= 200, status < 300 {
|
||||
return obj
|
||||
}
|
||||
|
||||
@@ -517,11 +526,11 @@ enum BrowserCLI {
|
||||
}
|
||||
}
|
||||
|
||||
#if SWIFT_PACKAGE
|
||||
#if SWIFT_PACKAGE
|
||||
static func _testFormatTabs(res: [String: Any]) -> [String] {
|
||||
self.formatTabs(res: res)
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
private static func printJSON(ok: Bool, result: Any) {
|
||||
let obj: [String: Any] = ["ok": ok, "result": result]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Foundation
|
||||
import Darwin
|
||||
import Foundation
|
||||
import PeekabooAutomationKit
|
||||
import PeekabooBridge
|
||||
import PeekabooFoundation
|
||||
@@ -94,7 +94,7 @@ enum UICLI {
|
||||
|
||||
private static func runPermissions(args: [String], jsonOutput: Bool, context: Context) async throws -> Int32 {
|
||||
let sub = args.first ?? "status"
|
||||
if sub != "status" && sub != "--help" && sub != "-h" && sub != "help" {
|
||||
if sub != "status", sub != "--help", sub != "-h", sub != "help" {
|
||||
self.printHelp()
|
||||
return 1
|
||||
}
|
||||
@@ -103,7 +103,7 @@ enum UICLI {
|
||||
try self.writeJSON([
|
||||
"ok": true,
|
||||
"host": context.hostDescription,
|
||||
"result": try self.toJSONObject(status),
|
||||
"result": self.toJSONObject(status),
|
||||
])
|
||||
} else {
|
||||
FileHandle.standardOutput.write(Data((self.formatPermissions(status) + "\n").utf8))
|
||||
@@ -123,7 +123,7 @@ enum UICLI {
|
||||
try self.writeJSON([
|
||||
"ok": true,
|
||||
"host": context.hostDescription,
|
||||
"app": try self.toJSONObject(app),
|
||||
"app": self.toJSONObject(app),
|
||||
"window": windowObject,
|
||||
])
|
||||
} else {
|
||||
@@ -131,7 +131,7 @@ enum UICLI {
|
||||
let line = "\(bundle) (pid \(app.processIdentifier))"
|
||||
FileHandle.standardOutput.write(Data((line + "\n").utf8))
|
||||
if let window {
|
||||
FileHandle.standardOutput.write(Data(("window \(window.windowID): \(window.title)\n").utf8))
|
||||
FileHandle.standardOutput.write(Data("window \(window.windowID): \(window.title)\n".utf8))
|
||||
}
|
||||
}
|
||||
return 0
|
||||
@@ -143,12 +143,12 @@ enum UICLI {
|
||||
try self.writeJSON([
|
||||
"ok": true,
|
||||
"host": context.hostDescription,
|
||||
"result": try self.toJSONObject(apps),
|
||||
"result": self.toJSONObject(apps),
|
||||
])
|
||||
} else {
|
||||
for app in apps {
|
||||
let bundle = app.bundleIdentifier ?? "<unknown>"
|
||||
FileHandle.standardOutput.write(Data(("\(bundle)\t\(app.name)\n").utf8))
|
||||
FileHandle.standardOutput.write(Data("\(bundle)\t\(app.name)\n".utf8))
|
||||
}
|
||||
}
|
||||
return 0
|
||||
@@ -176,11 +176,11 @@ enum UICLI {
|
||||
try self.writeJSON([
|
||||
"ok": true,
|
||||
"host": context.hostDescription,
|
||||
"result": try self.toJSONObject(windows),
|
||||
"result": self.toJSONObject(windows),
|
||||
])
|
||||
} else {
|
||||
for window in windows {
|
||||
FileHandle.standardOutput.write(Data(("\(window.windowID)\t\(window.title)\n").utf8))
|
||||
FileHandle.standardOutput.write(Data("\(window.windowID)\t\(window.title)\n".utf8))
|
||||
}
|
||||
}
|
||||
return 0
|
||||
@@ -217,20 +217,19 @@ enum UICLI {
|
||||
}
|
||||
}
|
||||
|
||||
let capture: CaptureResult
|
||||
if let bundleId, !bundleId.isEmpty {
|
||||
capture = try await context.client.captureWindow(
|
||||
let capture: CaptureResult = if let bundleId, !bundleId.isEmpty {
|
||||
try await context.client.captureWindow(
|
||||
appIdentifier: bundleId,
|
||||
windowIndex: windowIndex,
|
||||
visualizerMode: mode,
|
||||
scale: scale)
|
||||
} else if displayIndex != nil {
|
||||
capture = try await context.client.captureScreen(
|
||||
try await context.client.captureScreen(
|
||||
displayIndex: displayIndex,
|
||||
visualizerMode: mode,
|
||||
scale: scale)
|
||||
} else {
|
||||
capture = try await context.client.captureFrontmost(visualizerMode: mode, scale: scale)
|
||||
try await context.client.captureFrontmost(visualizerMode: mode, scale: scale)
|
||||
}
|
||||
|
||||
let path = try self.writeTempPNG(capture.imageData)
|
||||
@@ -240,7 +239,7 @@ enum UICLI {
|
||||
"ok": true,
|
||||
"host": context.hostDescription,
|
||||
"path": path,
|
||||
"metadata": try self.toJSONObject(capture.metadata),
|
||||
"metadata": self.toJSONObject(capture.metadata),
|
||||
"warning": capture.warning ?? "",
|
||||
])
|
||||
} else {
|
||||
@@ -287,7 +286,8 @@ enum UICLI {
|
||||
let resolvedSnapshotId: String = if let snapshotId, !snapshotId.isEmpty {
|
||||
snapshotId
|
||||
} else if let bundleId, !bundleId.isEmpty, let existing = try? await context.client
|
||||
.getMostRecentSnapshot(applicationBundleId: bundleId) {
|
||||
.getMostRecentSnapshot(applicationBundleId: bundleId)
|
||||
{
|
||||
existing
|
||||
} else {
|
||||
try await context.client.createSnapshot()
|
||||
@@ -321,7 +321,7 @@ enum UICLI {
|
||||
"host": context.hostDescription,
|
||||
"snapshotId": resolvedSnapshotId,
|
||||
"screenshotPath": screenshotPath,
|
||||
"result": try self.toJSONObject(detection),
|
||||
"result": self.toJSONObject(detection),
|
||||
])
|
||||
} else {
|
||||
FileHandle.standardOutput.write(Data((screenshotPath + "\n").utf8))
|
||||
@@ -494,7 +494,7 @@ enum UICLI {
|
||||
try self.writeJSON([
|
||||
"ok": true,
|
||||
"host": context.hostDescription,
|
||||
"result": try self.toJSONObject(result),
|
||||
"result": self.toJSONObject(result),
|
||||
])
|
||||
} else {
|
||||
FileHandle.standardOutput.write(Data((result.found ? "found\n" : "not found\n").utf8))
|
||||
@@ -549,7 +549,7 @@ enum UICLI {
|
||||
return "\(sr) \(ax) \(ascr)"
|
||||
}
|
||||
|
||||
private static func toJSONObject<T: Encodable>(_ value: T) throws -> Any {
|
||||
private static func toJSONObject(_ value: some Encodable) throws -> Any {
|
||||
let encoder = JSONEncoder()
|
||||
encoder.dateEncodingStrategy = .iso8601
|
||||
let data = try encoder.encode(value)
|
||||
|
||||
@@ -15,8 +15,10 @@
|
||||
"clawdis": "tsx src/index.ts",
|
||||
"clawdis:rpc": "tsx src/index.ts agent --mode rpc --json",
|
||||
"lint": "biome check src",
|
||||
"lint:swift": "swiftlint lint --config .swiftlint.yml && (cd apps/ios && swiftlint lint --config .swiftlint.yml)",
|
||||
"lint:fix": "biome check --write --unsafe src && biome format --write src",
|
||||
"format": "biome format src",
|
||||
"format:swift": "swiftformat --lint --config .swiftformat apps/macos/Sources apps/ios/Sources apps/shared/ClawdisKit/Sources",
|
||||
"format:fix": "biome format src --write",
|
||||
"test": "vitest",
|
||||
"test:force": "tsx scripts/test-force.ts",
|
||||
|
||||
Reference in New Issue
Block a user