Compare commits

...

2 Commits

Author SHA1 Message Date
Tak Hoffman
da116019d3 macos: fix swiftformat lint drift 2026-03-22 11:22:45 -05:00
Tak Hoffman
fd472b0d57 ci: avoid full build in PR test shards 2026-03-22 10:25:44 -05:00
12 changed files with 84 additions and 53 deletions

View File

@@ -272,7 +272,7 @@ jobs:
- name: Build dist
if: github.event_name != 'push' && matrix.task == 'test' && matrix.runtime == 'node'
run: pnpm build
run: pnpm build:plugin-sdk:test-dist
- name: Run ${{ matrix.task }} (${{ matrix.runtime }})
if: github.event_name != 'pull_request' || (matrix.runtime != 'bun' && matrix.task != 'compat-node22')
@@ -711,7 +711,7 @@ jobs:
- name: Build dist (Windows)
if: github.event_name != 'push' && matrix.task == 'test'
run: pnpm build
run: pnpm build:plugin-sdk:test-dist
- name: Run ${{ matrix.task }} (${{ matrix.runtime }})
run: ${{ matrix.command }}
@@ -736,7 +736,7 @@ jobs:
install-bun: "false"
- name: Build dist (macOS)
run: pnpm build
run: pnpm build:plugin-sdk:test-dist
# --- Run all checks sequentially (fast gates first) ---
- name: TS tests (macOS)

View File

@@ -783,7 +783,7 @@ extension AppState {
remoteToken: String,
remoteTokenDirty: Bool) -> [String: Any]
{
Self.updatedRemoteGatewayConfig(
updatedRemoteGatewayConfig(
current: current,
transport: transport,
remoteUrl: remoteUrl,
@@ -791,7 +791,8 @@ extension AppState {
remoteTarget: remoteTarget,
remoteIdentity: remoteIdentity,
remoteToken: remoteToken,
remoteTokenDirty: remoteTokenDirty).remote
remoteTokenDirty: remoteTokenDirty
).remote
}
static func _testSyncedGatewayRoot(
@@ -804,7 +805,7 @@ extension AppState {
remoteToken: String,
remoteTokenDirty: Bool) -> [String: Any]
{
Self.syncedGatewayRoot(
syncedGatewayRoot(
currentRoot: currentRoot,
connectionMode: connectionMode,
remoteTransport: remoteTransport,
@@ -812,7 +813,8 @@ extension AppState {
remoteIdentity: remoteIdentity,
remoteUrl: remoteUrl,
remoteToken: remoteToken,
remoteTokenDirty: remoteTokenDirty).root
remoteTokenDirty: remoteTokenDirty
).root
}
}
#endif

View File

@@ -8,8 +8,8 @@ final class CanvasA2UIActionMessageHandler: NSObject, WKScriptMessageHandler {
static let messageName = "openclawCanvasA2UIAction"
static let allMessageNames = [messageName]
// Compatibility helper for debug/test shims. Runtime dispatch remains
// limited to in-app canvas schemes in `didReceive`.
// Compatibility helper for debug/test shims.
// Runtime dispatch remains limited to in-app canvas schemes in `didReceive`.
static func isLocalNetworkCanvasURL(_ url: URL) -> Bool {
guard let scheme = url.scheme?.lowercased(), scheme == "http" || scheme == "https" else {
return false

View File

@@ -54,11 +54,13 @@ final class CanvasWindowController: NSWindowController, WKNavigationDelegate, NS
// expose unattended deep-link credentials to page JavaScript.
canvasWindowLogger.debug("CanvasWindowController init building A2UI bridge script")
let injectedSessionKey = sessionKey.trimmingCharacters(in: .whitespacesAndNewlines).nonEmpty ?? "main"
let allowedSchemesJSON = (
try? String(
data: JSONSerialization.data(withJSONObject: CanvasScheme.allSchemes),
encoding: .utf8)
) ?? "[]"
let allowedSchemesJSON =
(
try? String(
data: JSONSerialization.data(withJSONObject: CanvasScheme.allSchemes),
encoding: .utf8
)
) ?? "[]"
let bridgeScript = """
(() => {
try {

View File

@@ -147,7 +147,8 @@ extension CronJobEditor {
throw NSError(
domain: "Cron",
code: 0,
userInfo: [NSLocalizedDescriptionKey: "Invalid every duration (use 10m, 1h, 1d)."])
userInfo: [NSLocalizedDescriptionKey: "Invalid every duration (use 10m, 1h, 1d)."]
)
}
return ["kind": "every", "everyMs": ms]
case .cron:
@@ -265,7 +266,11 @@ extension CronJobEditor {
}
var effectiveSessionTargetRaw: String {
if self.sessionTarget == .isolated, let preserved = self.preservedSessionTargetRaw?.trimmingCharacters(in: .whitespacesAndNewlines), !preserved.isEmpty {
if self.sessionTarget == .isolated,
let preserved = self.preservedSessionTargetRaw?
.trimmingCharacters(in: .whitespacesAndNewlines),
!preserved.isEmpty
{
return preserved
}
return self.sessionTarget.rawValue

View File

@@ -16,10 +16,10 @@ enum CronCustomSessionTarget: Codable, Equatable {
var rawValue: String {
switch self {
case .predefined(let target):
return target.rawValue
case .session(let id):
return "session:\(id)"
case let .predefined(target):
target.rawValue
case let .session(id):
"session:\(id)"
}
}
@@ -96,20 +96,24 @@ enum CronSchedule: Codable, Equatable {
throw DecodingError.dataCorruptedError(
forKey: .at,
in: container,
debugDescription: "Missing schedule.at")
debugDescription: "Missing schedule.at"
)
case "every":
self = try .every(
everyMs: container.decode(Int.self, forKey: .everyMs),
anchorMs: container.decodeIfPresent(Int.self, forKey: .anchorMs))
anchorMs: container.decodeIfPresent(Int.self, forKey: .anchorMs)
)
case "cron":
self = try .cron(
expr: container.decode(String.self, forKey: .expr),
tz: container.decodeIfPresent(String.self, forKey: .tz))
tz: container.decodeIfPresent(String.self, forKey: .tz)
)
default:
throw DecodingError.dataCorruptedError(
forKey: .kind,
in: container,
debugDescription: "Unknown schedule kind: \(kind)")
debugDescription: "Unknown schedule kind: \(kind)"
)
}
}
@@ -185,12 +189,14 @@ enum CronPayload: Codable, Equatable {
channel: container.decodeIfPresent(String.self, forKey: .channel)
?? container.decodeIfPresent(String.self, forKey: .provider),
to: container.decodeIfPresent(String.self, forKey: .to),
bestEffortDeliver: container.decodeIfPresent(Bool.self, forKey: .bestEffortDeliver))
bestEffortDeliver: container.decodeIfPresent(Bool.self, forKey: .bestEffortDeliver)
)
default:
throw DecodingError.dataCorruptedError(
forKey: .kind,
in: container,
debugDescription: "Unknown payload kind: \(kind)")
debugDescription: "Unknown payload kind: \(kind)"
)
}
}
@@ -328,10 +334,10 @@ struct CronJob: Identifiable, Codable, Equatable {
/// predefined enum.
var sessionTarget: CronSessionTarget {
switch self.parsedSessionTarget {
case .predefined(let target):
return target
case let .predefined(target):
target
case .session:
return .isolated
.isolated
}
}
@@ -345,8 +351,8 @@ struct CronJob: Identifiable, Codable, Equatable {
return nil
case .predefined(.isolated), .predefined(.current):
return "cron:\(self.id)"
case .session(let id):
return id
case let .session(id):
id
}
}
@@ -361,7 +367,7 @@ struct CronJob: Identifiable, Codable, Equatable {
var displayName: String {
let trimmed = self.name.trimmingCharacters(in: .whitespacesAndNewlines)
return trimmed.isEmpty ? "Untitled job" : trimmed
trimmed.isEmpty ? "Untitled job" : trimmed
}
var nextRunDate: Date? {

View File

@@ -113,27 +113,29 @@ final class ExecApprovalsGatewayPrompter {
let mode = AppStateStore.shared.connectionMode
let activeSession = WebChatManager.shared.activeSessionKey?.trimmingCharacters(in: .whitespacesAndNewlines)
let requestSession = request.request.sessionKey?.trimmingCharacters(in: .whitespacesAndNewlines)
// Read-only resolve to avoid disk writes on the MainActor
let approvals = ExecApprovalsStore.resolveReadOnly(agentId: request.request.agentId)
let security = approvals.agent.security
let ask = approvals.agent.ask
let shouldAsk = Self.shouldAsk(security: security, ask: ask)
let canPresent = shouldAsk && Self.shouldPresent(
mode: mode,
activeSession: activeSession,
requestSession: requestSession,
lastInputSeconds: Self.lastInputSeconds(),
thresholdSeconds: 120)
thresholdSeconds: 120
)
return PresentationDecision(
shouldAsk: shouldAsk,
canPresent: canPresent,
security: security,
askFallback: approvals.agent.askFallback,
allowlist: approvals.allowlist)
allowlist: approvals.allowlist
)
}
private static func fallbackDecision(

View File

@@ -147,7 +147,10 @@ actor MacNodeBrowserProxy {
}
if method != "GET", let body = params.body {
request.httpBody = try JSONSerialization.data(withJSONObject: body.foundationValue, options: [.fragmentsAllowed])
request.httpBody = try JSONSerialization.data(
withJSONObject: body.foundationValue,
options: [.fragmentsAllowed]
)
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
}

View File

@@ -337,7 +337,6 @@ extension OnboardingView {
self.remoteProbePreflightMessage == nil && self.remoteProbeState != .checking
}
@ViewBuilder
private func remoteConnectionSection() -> some View {
VStack(alignment: .leading, spacing: 10) {
HStack(alignment: .top, spacing: 12) {
@@ -440,7 +439,7 @@ extension OnboardingView {
private func remoteAuthPromptView(issue: RemoteGatewayAuthIssue) -> some View {
let promptStyle = Self.remoteAuthPromptStyle(for: issue)
return HStack(alignment: .top, spacing: 10) {
HStack(alignment: .top, spacing: 10) {
Image(systemName: promptStyle.systemImage)
.font(.caption.weight(.semibold))
.foregroundStyle(promptStyle.tint)
@@ -503,17 +502,17 @@ extension OnboardingView {
{
switch issue {
case .tokenRequired:
return ("key.fill", .orange)
("key.fill", .orange)
case .tokenMismatch:
return ("exclamationmark.triangle.fill", .orange)
("exclamationmark.triangle.fill", .orange)
case .gatewayTokenNotConfigured:
return ("wrench.and.screwdriver.fill", .orange)
("wrench.and.screwdriver.fill", .orange)
case .setupCodeExpired:
return ("qrcode.viewfinder", .orange)
("qrcode.viewfinder", .orange)
case .passwordRequired:
return ("lock.slash.fill", .orange)
("lock.slash.fill", .orange)
case .pairingRequired:
return ("link.badge.plus", .orange)
("link.badge.plus", .orange)
}
}

View File

@@ -155,7 +155,9 @@ enum RemoteGatewayProbe {
return .failed("Set a gateway URL first")
}
guard self.isValidWsUrl(trimmedUrl) else {
return .failed("Gateway URL must use wss:// for remote hosts (ws:// only for localhost)")
return .failed(
"Gateway URL must use wss:// for remote hosts (ws:// only for localhost)"
)
}
} else {
let trimmedTarget = settings.target.trimmingCharacters(in: .whitespacesAndNewlines)
@@ -173,7 +175,8 @@ enum RemoteGatewayProbe {
command: sshCommand,
cwd: nil,
env: nil,
timeout: 8)
timeout: 8
)
guard sshResult.ok else {
return .failed(self.formatSSHFailure(sshResult, target: settings.target))
}
@@ -221,7 +224,10 @@ enum RemoteGatewayProbe {
trimmed.localizedCaseInsensitiveContains("host key verification failed")
{
let host = CommandResolver.parseSSHTarget(target)?.host ?? target
return "SSH check failed: Host key verification failed. Remove the old key with ssh-keygen -R \(host) and try again."
return """
SSH check failed: Host key verification failed. Remove the old key with \
ssh-keygen -R \(host) and try again.
"""
}
if let trimmed, !trimmed.isEmpty {
if let message = response.message, message.hasPrefix("exit ") {

View File

@@ -26,12 +26,16 @@ enum TalkModeGatewayConfigParser {
envApiKey: String?
) -> TalkModeGatewayConfigState {
let talk = snapshot.config?["talk"]?.dictionaryValue
let selection = TalkConfigParsing.selectProviderConfig(talk, defaultProvider: defaultProvider)
let selection = TalkConfigParsing.selectProviderConfig(
talk,
defaultProvider: defaultProvider
)
let activeProvider = selection?.provider ?? defaultProvider
let activeConfig = selection?.config
let silenceTimeoutMs = TalkConfigParsing.resolvedSilenceTimeoutMs(
talk,
fallback: defaultSilenceTimeoutMs)
fallback: defaultSilenceTimeoutMs
)
let ui = snapshot.config?["ui"]?.dictionaryValue
let rawSeam = ui?["seamColor"]?.stringValue?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
let voice = activeConfig?["voiceId"]?.stringValue
@@ -73,7 +77,8 @@ enum TalkModeGatewayConfigParser {
interruptOnSpeech: interrupt ?? true,
silenceTimeoutMs: silenceTimeoutMs,
apiKey: resolvedApiKey,
seamColorHex: rawSeam.isEmpty ? nil : rawSeam)
seamColorHex: rawSeam.isEmpty ? nil : rawSeam
)
}
static func fallback(

View File

@@ -557,6 +557,7 @@
"build": "pnpm canvas:a2ui:bundle && node scripts/tsdown-build.mjs && node scripts/runtime-postbuild.mjs && pnpm build:plugin-sdk:dts && node --import tsx scripts/write-plugin-sdk-entry-dts.ts && node --import tsx scripts/canvas-a2ui-copy.ts && node --import tsx scripts/copy-hook-metadata.ts && node --import tsx scripts/copy-export-html-templates.ts && node --import tsx scripts/write-build-info.ts && node --import tsx scripts/write-cli-startup-metadata.ts && node --import tsx scripts/write-cli-compat.ts",
"build:docker": "node scripts/tsdown-build.mjs && node scripts/runtime-postbuild.mjs && node --import tsx scripts/canvas-a2ui-copy.ts && node --import tsx scripts/copy-hook-metadata.ts && node --import tsx scripts/copy-export-html-templates.ts && node --import tsx scripts/write-build-info.ts && node --import tsx scripts/write-cli-startup-metadata.ts && node --import tsx scripts/write-cli-compat.ts",
"build:plugin-sdk:dts": "tsc -p tsconfig.plugin-sdk.dts.json",
"build:plugin-sdk:test-dist": "pnpm canvas:a2ui:bundle && node scripts/tsdown-build.mjs && node scripts/copy-plugin-sdk-root-alias.mjs",
"build:strict-smoke": "pnpm canvas:a2ui:bundle && node scripts/tsdown-build.mjs && node scripts/runtime-postbuild.mjs && pnpm build:plugin-sdk:dts",
"canvas:a2ui:bundle": "bash scripts/bundle-a2ui.sh",
"check": "pnpm check:host-env-policy:swift && pnpm check:bundled-provider-auth-env-vars && pnpm format:check && pnpm tsgo && pnpm plugin-sdk:check-exports && pnpm lint && pnpm lint:tmp:no-random-messaging && pnpm lint:tmp:channel-agnostic-boundaries && pnpm lint:tmp:no-raw-channel-fetch && pnpm lint:agent:ingress-owner && pnpm lint:plugins:no-register-http-handler && pnpm lint:plugins:no-monolithic-plugin-sdk-entry-imports && pnpm lint:plugins:no-extension-src-imports && pnpm lint:plugins:no-extension-test-core-imports && pnpm lint:plugins:no-extension-imports && pnpm lint:plugins:plugin-sdk-subpaths-exported && pnpm lint:extensions:no-src-outside-plugin-sdk && pnpm lint:extensions:no-plugin-sdk-internal && pnpm lint:extensions:no-relative-outside-package && pnpm lint:web-search-provider-boundaries && pnpm lint:webhook:no-low-level-body-read && pnpm lint:auth:no-pairing-store-group && pnpm lint:auth:pairing-account-scope",