

























@@ -70,7 +70,6 @@ import kotlinx.serialization.json.JsonArray
7070import kotlinx.serialization.json.JsonObject
7171import kotlinx.serialization.json.JsonPrimitive
7272import kotlinx.serialization.json.buildJsonObject
73-import java.time.Instant
7473import java.util.UUID
7574import java.util.concurrent.atomic.AtomicLong
7675@@ -323,8 +322,6 @@ class NodeRuntime(
323322val cronJobs: StateFlow<List<GatewayCronJobSummary>> = _cronJobs.asStateFlow()
324323private val _cronRefreshing = MutableStateFlow(false)
325324val cronRefreshing: StateFlow<Boolean> = _cronRefreshing.asStateFlow()
326-private val _cronSaving = MutableStateFlow(false)
327-val cronSaving: StateFlow<Boolean> = _cronSaving.asStateFlow()
328325private val _cronErrorText = MutableStateFlow<String?>(null)
329326val cronErrorText: StateFlow<String?> = _cronErrorText.asStateFlow()
330327private val _usageSummary = MutableStateFlow(GatewayUsageSummary(updatedAtMs = null, providers = emptyList()))
@@ -413,6 +410,8 @@ class NodeRuntime(
413410_gatewayVersion.value = null
414411_gatewayUpdateAvailable.value = null
415412_seamColorArgb.value = DEFAULT_SEAM_COLOR_ARGB
413+_gatewayDefaultAgentId.value = null
414+_gatewayAgents.value = emptyList()
416415_modelCatalog.value = emptyList()
417416_modelAuthProviders.value = emptyList()
418417_cronStatus.value = GatewayCronStatus(enabled = false, jobs = 0, nextWakeAtMs = null)
@@ -722,22 +721,6 @@ class NodeRuntime(
722721 }
723722 }
724723725-fun createCronJob(
726-name: String,
727-message: String,
728-scheduleKind: String,
729-scheduleValue: String,
730- ) {
731- scope.launch {
732- createCronJobOnGateway(
733- name = name,
734- message = message,
735- scheduleKind = scheduleKind,
736- scheduleValue = scheduleValue,
737- )
738- }
739- }
740-741724fun refreshUsage() {
742725 scope.launch {
743726 refreshUsageFromGateway()
@@ -1102,6 +1085,12 @@ class NodeRuntime(
11021085 setVoiceCaptureMode(if (value) VoiceCaptureMode.ManualMic else VoiceCaptureMode.Off)
11031086 }
110410871088+fun cancelMicCapture() {
1089+ micCapture.cancelMicCapture()
1090+ setVoiceCaptureMode(VoiceCaptureMode.Off, persistManualMic = false)
1091+ prefs.setVoiceMicEnabled(false)
1092+ }
1093+11051094fun setTalkModeEnabled(value: Boolean) {
11061095 setVoiceCaptureMode(if (value) VoiceCaptureMode.TalkMode else VoiceCaptureMode.Off)
11071096 }
@@ -1763,119 +1752,6 @@ class NodeRuntime(
17631752 }
17641753 }
176517541766-private suspend fun createCronJobOnGateway(
1767-name: String,
1768-message: String,
1769-scheduleKind: String,
1770-scheduleValue: String,
1771- ) {
1772-_cronSaving.value = true
1773-_cronErrorText.value = null
1774-if (!operatorConnected) {
1775-_cronErrorText.value = "Connect the gateway before creating a cron job."
1776-_cronSaving.value = false
1777-return
1778- }
1779-try {
1780-val cleanName = name.trim()
1781-val cleanMessage = message.trim()
1782-if (cleanName.isEmpty()) {
1783-throw IllegalArgumentException("Add a job name.")
1784- }
1785-if (cleanMessage.isEmpty()) {
1786-throw IllegalArgumentException("Add the message OpenClaw should run.")
1787- }
1788-val schedule = buildCronCreateSchedule(scheduleKind = scheduleKind, scheduleValue = scheduleValue.trim())
1789-val params =
1790- buildJsonObject {
1791- put("name", JsonPrimitive(cleanName))
1792- put("enabled", JsonPrimitive(true))
1793- put("deleteAfterRun", JsonPrimitive(scheduleKind == "Once"))
1794- put("sessionTarget", JsonPrimitive("isolated"))
1795- put("wakeMode", JsonPrimitive("now"))
1796- put("schedule", schedule)
1797- put(
1798-"payload",
1799- buildJsonObject {
1800- put("kind", JsonPrimitive("agentTurn"))
1801- put("message", JsonPrimitive(cleanMessage))
1802- },
1803- )
1804- put(
1805-"delivery",
1806- buildJsonObject {
1807- put("mode", JsonPrimitive("announce"))
1808- put("channel", JsonPrimitive("last"))
1809- put("bestEffort", JsonPrimitive(true))
1810- },
1811- )
1812- }
1813- operatorSession.request("cron.add", params.toString())
1814- refreshCronFromGateway()
1815- } catch (err: IllegalArgumentException) {
1816-_cronErrorText.value = err.message ?: "Could not create cron job."
1817- } catch (_: Throwable) {
1818-_cronErrorText.value = "Could not create cron job."
1819- } finally {
1820-_cronSaving.value = false
1821- }
1822- }
1823-1824-private fun buildCronCreateSchedule(
1825-scheduleKind: String,
1826-scheduleValue: String,
1827- ): JsonObject =
1828-when (scheduleKind) {
1829-"Every" ->
1830- buildJsonObject {
1831- put("kind", JsonPrimitive("every"))
1832- put("everyMs", JsonPrimitive(parseCronDurationMs(scheduleValue)))
1833- }
1834-"Once" ->
1835- buildJsonObject {
1836- put("kind", JsonPrimitive("at"))
1837- put("at", JsonPrimitive(resolveCronAtIso(scheduleValue)))
1838- }
1839-"Cron" ->
1840- buildJsonObject {
1841-val expr = scheduleValue.takeIf { it.isNotEmpty() } ?: throw IllegalArgumentException("Add a cron expression.")
1842- put("kind", JsonPrimitive("cron"))
1843- put("expr", JsonPrimitive(expr))
1844- put("staggerMs", JsonPrimitive(0))
1845- }
1846-else -> throw IllegalArgumentException("Choose a schedule.")
1847- }
1848-1849-private fun resolveCronAtIso(value: String): String {
1850-val clean = value.trim()
1851-if (clean.startsWith("+")) {
1852-return Instant.ofEpochMilli(System.currentTimeMillis() + parseCronDurationMs(clean.drop(1))).toString()
1853- }
1854-return try {
1855-Instant.parse(clean).toString()
1856- } catch (_: Throwable) {
1857-throw IllegalArgumentException("Use ISO time or +30m.")
1858- }
1859- }
1860-1861-private fun parseCronDurationMs(value: String): Long {
1862-val match =
1863-Regex("""^(\d+)\s*([mhd])$""")
1864- .matchEntire(value.trim().lowercase())
1865-?: throw IllegalArgumentException("Use 15m, 1h, or 1d.")
1866-val amount = match.groupValues[1].toLong()
1867-if (amount <= 0) {
1868-throw IllegalArgumentException("Use a duration greater than zero.")
1869- }
1870-val unit = match.groupValues[2]
1871-return amount *
1872-when (unit) {
1873-"m" -> 60_000L
1874-"h" -> 3_600_000L
1875-else -> 86_400_000L
1876- }
1877- }
1878-18791755private suspend fun refreshUsageFromGateway() {
18801756_usageRefreshing.value = true
18811757_usageErrorText.value = null
@@ -2151,12 +2027,7 @@ class NodeRuntime(
21512027 scheduleLabel = cronScheduleLabel(schedule),
21522028 promptPreview = cronPayloadPreview(payload),
21532029 nextRunAtMs = state.long("nextRunAtMs"),
2154- lastRunStatus =
2155- state
2156- ?.get("lastRunStatus")
2157- .asStringOrNull()
2158- ?.trim()
2159- ?.takeIf { it.isNotEmpty() },
2030+ lastRunStatus = cronJobLastRunStatus(state),
21602031 )
21612032 }.orEmpty()
21622033@@ -2649,11 +2520,7 @@ internal fun resolveOperatorSessionConnectAuth(
2649252026502521val explicitBootstrapToken = auth.bootstrapToken?.trim()?.takeIf { it.isNotEmpty() }
26512522if (explicitBootstrapToken != null) {
2652-return NodeRuntime.GatewayConnectAuth(
2653- token = null,
2654- bootstrapToken = explicitBootstrapToken,
2655- password = null,
2656- )
2523+return null
26572524 }
2658252526592526return NodeRuntime.GatewayConnectAuth(
@@ -2867,6 +2734,18 @@ private fun JsonObject?.double(key: String): Double? = (this?.get(key) as? JsonP
2867273428682735private fun JsonObject?.boolean(key: String): Boolean = (this?.get(key) as? JsonPrimitive)?.content?.trim() == "true"
286927362737+internal fun cronJobLastRunStatus(state: JsonObject?): String? =
2738+ state
2739+ .cronStatus("lastStatus")
2740+?: state.cronStatus("lastRunStatus")
2741+2742+private fun JsonObject?.cronStatus(key: String): String? =
2743+this
2744+ ?.get(key)
2745+ .asStringOrNull()
2746+ ?.trim()
2747+ ?.takeIf { it.isNotEmpty() }
2748+28702749fun providerDisplayName(provider: String): String =
28712750when (provider.trim().lowercase()) {
28722751"openai" -> "OpenAI"
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。