fix: detect installed Hermes runtime and restore its official icon
This commit is contained in:
@@ -1,15 +1,13 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "HermesAgent.svg",
|
||||
"idiom" : "universal"
|
||||
"filename" : "HermesAgent.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"preserves-vector-representation" : true
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 561 KiB |
@@ -1,7 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
|
||||
<rect width="64" height="64" rx="14" fill="#101820"/>
|
||||
<path d="M20 47V17h7v12h10V17h7v30h-7V35H27v12h-7Z" fill="#F4F7FA"/>
|
||||
<path d="M15 13h34v6H15z" fill="#4AD7D1"/>
|
||||
<path d="M15 45h34v6H15z" fill="#CFA9FF"/>
|
||||
<path d="M18 13l5-6 5 6M36 13l5-6 5 6M18 51l5 6 5-6M36 51l5 6 5-6" fill="none" stroke="#F4F7FA" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 472 B |
@@ -10,7 +10,11 @@ extension CmuxVaultAgentRegistration {
|
||||
defaultValue: "Hermes Agent"
|
||||
),
|
||||
iconAssetName: "AgentIcons/HermesAgent",
|
||||
detect: CmuxVaultAgentDetectRule(processNames: ["hermes", "hermes-agent"]),
|
||||
detect: CmuxVaultAgentDetectRule(
|
||||
processNames: ["hermes", "hermes-agent"],
|
||||
alternateProcessNames: ["python", "python3"],
|
||||
alternateArgvContainsAny: ["hermes-agent/hermes"]
|
||||
),
|
||||
sessionIdSource: .persistedStore(.hermesStateDB),
|
||||
resumeCommand: "{{executable}} --resume {{sessionId}}",
|
||||
cwd: .preserve
|
||||
|
||||
@@ -2,8 +2,14 @@ import Foundation
|
||||
|
||||
extension CmuxVaultAgentRegistration {
|
||||
func processDetectedSnapshotIsRestorable(for process: VaultObservedAgentProcess) -> Bool {
|
||||
guard id == "campfire" else { return true }
|
||||
return process.environment["CAMPFIRE_SESSION_ROLE"] == "host"
|
||||
switch id {
|
||||
case "campfire":
|
||||
return process.environment["CAMPFIRE_SESSION_ROLE"] == "host"
|
||||
case "hermes-agent":
|
||||
return process.isInteractiveHermesAgentInvocation
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,16 @@ struct VaultObservedAgentProcess: Sendable {
|
||||
arguments.piCompatibleSessionID(startingAt: piCompatibleSessionArgumentStartIndex)
|
||||
}
|
||||
|
||||
/// Whether this Hermes process owns an interactive conversation that cmux can restore.
|
||||
///
|
||||
/// The installed launcher execs Python with `hermes-agent/hermes` as its script, so the
|
||||
/// first Hermes argument is not always at index one. Management subcommands such as
|
||||
/// `gateway`, `doctor`, and `update` must not become resumable chat panes.
|
||||
var isInteractiveHermesAgentInvocation: Bool {
|
||||
guard let command = firstHermesAgentPositionalArgument else { return true }
|
||||
return command.compare("chat", options: [.caseInsensitive, .literal]) == .orderedSame
|
||||
}
|
||||
|
||||
var openCodeExecutableArgumentIndex: Int? {
|
||||
if let first = arguments.first,
|
||||
Self.argumentLooksLikeOpenCode(first) {
|
||||
@@ -78,6 +88,57 @@ struct VaultObservedAgentProcess: Sendable {
|
||||
}
|
||||
}
|
||||
|
||||
private var firstHermesAgentPositionalArgument: String? {
|
||||
var index = hermesAgentArgumentStartIndex
|
||||
while index < arguments.endIndex {
|
||||
let argument = arguments[index]
|
||||
if argument == "--" {
|
||||
let nextIndex = arguments.index(after: index)
|
||||
return nextIndex < arguments.endIndex ? arguments[nextIndex] : nil
|
||||
}
|
||||
guard argument.hasPrefix("-") else { return argument }
|
||||
|
||||
let option = argument
|
||||
.split(separator: "=", maxSplits: 1)
|
||||
.first
|
||||
.map(String.init) ?? argument
|
||||
if !argument.contains("="), Self.hermesTopLevelOptionConsumesValue(option) {
|
||||
index = min(index + 2, arguments.endIndex)
|
||||
} else {
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
private var hermesAgentArgumentStartIndex: Int {
|
||||
if let entrypointIndex = arguments.indices.first(where: {
|
||||
Self.argumentLooksLikeHermesAgentEntrypoint(arguments[$0])
|
||||
}) {
|
||||
return arguments.index(after: entrypointIndex)
|
||||
}
|
||||
return arguments.isEmpty
|
||||
? arguments.startIndex
|
||||
: arguments.index(after: arguments.startIndex)
|
||||
}
|
||||
|
||||
private static func argumentLooksLikeHermesAgentEntrypoint(_ argument: String) -> Bool {
|
||||
argument
|
||||
.replacingOccurrences(of: "\\", with: "/")
|
||||
.range(of: "hermes-agent/hermes", options: [.caseInsensitive, .literal]) != nil
|
||||
}
|
||||
|
||||
private static func hermesTopLevelOptionConsumesValue(_ option: String) -> Bool {
|
||||
switch option {
|
||||
case "-z", "--oneshot", "-m", "--model", "--provider", "--reasoning",
|
||||
"-t", "--toolsets", "-r", "--resume", "-c", "--continue",
|
||||
"-s", "--skills", "--usage-file", "-p", "--profile":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
static func argumentLooksLikeOpenCode(_ argument: String) -> Bool {
|
||||
let normalized = argument.lowercased()
|
||||
let pathComponents = (normalized as NSString).pathComponents
|
||||
|
||||
Reference in New Issue
Block a user