Security + upstream fixes

- Network core moves to a root-only folder; BroV talks to a narrow root helper (netctl.py) over a user-only socket; install script verifies the mihomo SHA256 and migrates keys (scripts/netcore)
- Hardened runtime, no get-task-allow; bypassPermissions removed from the chat; concealed clipboard items are not restored; DangerCheck knows core, LaunchAgents and hook paths; dropped-file copies expire after 7 days
- Ported from upstream Coucou: 1h crash fix (d05f22b), safe settings.json writes (918d30e), Escape/fold for pending approvals (6012900, 40e3ba8), auto-close delay + reopen (74984f2, ea244a7), full AskUserQuestion (52b1562)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
maksarsanjeev
2026-10-07 23:49:11 +03:00
parent dc47247340
commit 52e1e8288b
21 changed files with 1070 additions and 188 deletions
+32
View File
@@ -14,9 +14,41 @@ struct AskQuestionItem: Equatable {
var multiSelect: Bool
}
/// Island height of the pending question, readable from the nonisolated `islandSize`. Written on the main actor only.
enum QuestionLayout {
nonisolated(unsafe) static var height: CGFloat?
}
extension AskQuestionItem {
/// True when at least one option carries a description: the card then lists options vertically.
var hasDescriptions: Bool { options.contains { !$0.description.isEmpty } }
}
struct AskQuestion: Equatable {
var questions: [AskQuestionItem] // 1–4 questions
/// Island height that fits the tallest question without truncation (rough estimate, text wraps at ~500 pt).
var estimatedIslandHeight: CGFloat {
func lines(_ text: String, charWidth: CGFloat) -> CGFloat {
max(1, (CGFloat(text.count) * charWidth / 500).rounded(.up))
}
let tallest = questions.map { item -> CGFloat in
var h: CGFloat = 20 + lines(item.question, charWidth: 7) * 17 + 64
if !item.header.isEmpty { h += 14 }
if item.hasDescriptions {
for opt in item.options {
h += 34 + (opt.description.isEmpty ? 0 : lines(opt.description, charWidth: 6.4) * 14)
}
h += 40 // "Другое…" row
} else {
h += item.options.count >= 3 ? 74 : 40
}
if item.multiSelect { h += 34 }
return h
}.max() ?? 160
return min(max(tallest, 160), 560)
}
// MARK: - Parse from tool_input dict
// Returns nil if the payload is malformed (fallback → Allow/Deny card).
static func parse(toolInput: [String: Any]) -> AskQuestion? {