Merge pull request #3343 from code-yeongyu/refactor/ultrawork-cleanup-pass

refactor: trim duplicate logic and dead internal exports
This commit is contained in:
YeonGyu-Kim
2026-04-11 23:12:16 +09:00
committed by GitHub
8 changed files with 80 additions and 73 deletions
+1 -1
View File
@@ -9,7 +9,7 @@ import { createAvailableCategories } from "./plugin/available-categories"
import { createSkillContext } from "./plugin/skill-context" import { createSkillContext } from "./plugin/skill-context"
import { createToolRegistry } from "./plugin/tool-registry" import { createToolRegistry } from "./plugin/tool-registry"
export type CreateToolsResult = { type CreateToolsResult = {
filteredTools: ToolsRecord filteredTools: ToolsRecord
mergedSkills: LoadedSkill[] mergedSkills: LoadedSkill[]
availableSkills: AvailableSkill[] availableSkills: AvailableSkill[]
+1 -1
View File
@@ -9,7 +9,7 @@ import { START_WORK_TEMPLATE } from "./templates/start-work"
import { HANDOFF_TEMPLATE } from "./templates/handoff" import { HANDOFF_TEMPLATE } from "./templates/handoff"
import { REMOVE_AI_SLOPS_TEMPLATE } from "./templates/remove-ai-slops" import { REMOVE_AI_SLOPS_TEMPLATE } from "./templates/remove-ai-slops"
export interface LoadBuiltinCommandsOptions { interface LoadBuiltinCommandsOptions {
useRegisteredAgents?: boolean useRegisteredAgents?: boolean
} }
+1 -19
View File
@@ -3,6 +3,7 @@ import type {
OpenClawGateway, OpenClawGateway,
OpenClawReplyListenerConfig, OpenClawReplyListenerConfig,
} from "./types" } from "./types"
export { validateGatewayUrl } from "./gateway-url-validation"
const DEFAULT_REPLY_POLL_INTERVAL_MS = 3000 const DEFAULT_REPLY_POLL_INTERVAL_MS = 3000
const MIN_REPLY_POLL_INTERVAL_MS = 500 const MIN_REPLY_POLL_INTERVAL_MS = 500
@@ -97,22 +98,3 @@ export function resolveGateway(
return { gatewayName: mapping.gateway, gateway, instruction: mapping.instruction } return { gatewayName: mapping.gateway, gateway, instruction: mapping.instruction }
} }
export function validateGatewayUrl(url: string): boolean {
try {
const parsed = new URL(url)
if (parsed.protocol === "https:") return true
if (
parsed.protocol === "http:" &&
(parsed.hostname === "localhost" ||
parsed.hostname === "127.0.0.1" ||
parsed.hostname === "::1" ||
parsed.hostname === "[::1]")
) {
return true
}
return false
} catch {
return false
}
}
+1 -20
View File
@@ -1,30 +1,11 @@
import { spawn } from "bun" import { spawn } from "bun"
import { validateGatewayUrl } from "./gateway-url-validation"
import type { OpenClawGateway, WakeResult } from "./types" import type { OpenClawGateway, WakeResult } from "./types"
const DEFAULT_HTTP_TIMEOUT_MS = 10_000 const DEFAULT_HTTP_TIMEOUT_MS = 10_000
const DEFAULT_COMMAND_TIMEOUT_MS = 5_000 const DEFAULT_COMMAND_TIMEOUT_MS = 5_000
const MIN_COMMAND_TIMEOUT_MS = 100 const MIN_COMMAND_TIMEOUT_MS = 100
const MAX_COMMAND_TIMEOUT_MS = 300_000 const MAX_COMMAND_TIMEOUT_MS = 300_000
const SHELL_METACHAR_RE = /[|&;><`$()]/
export function validateGatewayUrl(url: string): boolean {
try {
const parsed = new URL(url)
if (parsed.protocol === "https:") return true
if (
parsed.protocol === "http:" &&
(parsed.hostname === "localhost" ||
parsed.hostname === "127.0.0.1" ||
parsed.hostname === "::1" ||
parsed.hostname === "[::1]")
) {
return true
}
return false
} catch {
return false
}
}
export function interpolateInstruction( export function interpolateInstruction(
template: string, template: string,
@@ -0,0 +1,34 @@
import { describe, expect, test } from "bun:test"
import { validateGatewayUrl } from "./gateway-url-validation"
describe("validateGatewayUrl", () => {
test("allows https and local http while rejecting remote or invalid urls", () => {
// given representative gateway urls
const httpsRemote = "https://example.com"
const httpRemote = "http://example.com"
const httpLocalhost = "http://localhost:3000"
const httpLoopback = "http://127.0.0.1:3000"
const httpIpv6Loopback = "http://[::1]:3000"
const invalidUrl = "not-a-url"
// when validating each url
const results = {
httpsRemote: validateGatewayUrl(httpsRemote),
httpRemote: validateGatewayUrl(httpRemote),
httpLocalhost: validateGatewayUrl(httpLocalhost),
httpLoopback: validateGatewayUrl(httpLoopback),
httpIpv6Loopback: validateGatewayUrl(httpIpv6Loopback),
invalidUrl: validateGatewayUrl(invalidUrl),
}
// then only https and localhost loopback urls are allowed
expect(results).toEqual({
httpsRemote: true,
httpRemote: false,
httpLocalhost: true,
httpLoopback: true,
httpIpv6Loopback: true,
invalidUrl: false,
})
})
})
+18
View File
@@ -0,0 +1,18 @@
export function validateGatewayUrl(url: string): boolean {
try {
const parsed = new URL(url)
if (parsed.protocol === "https:") return true
if (
parsed.protocol === "http:" &&
(parsed.hostname === "localhost" ||
parsed.hostname === "127.0.0.1" ||
parsed.hostname === "::1" ||
parsed.hostname === "[::1]")
) {
return true
}
return false
} catch {
return false
}
}
+9 -1
View File
@@ -217,6 +217,10 @@ describe("normalizeAgentForPrompt", () => {
it("removes zero-width characters before returning canonical names", () => { it("removes zero-width characters before returning canonical names", () => {
expect(normalizeAgentForPrompt("Sisyphus\u200B - Ultraworker")).toBe("Sisyphus - Ultraworker") expect(normalizeAgentForPrompt("Sisyphus\u200B - Ultraworker")).toBe("Sisyphus - Ultraworker")
}) })
it("converts legacy parenthesized names to canonical display names", () => {
expect(normalizeAgentForPrompt("Atlas (Plan Executor)")).toBe("Atlas - Plan Executor")
})
}) })
describe("normalizeAgentForPromptKey", () => { describe("normalizeAgentForPromptKey", () => {
@@ -224,6 +228,10 @@ describe("normalizeAgentForPromptKey", () => {
expect(normalizeAgentForPromptKey("Sisyphus (Ultraworker)")).toBe("sisyphus") expect(normalizeAgentForPromptKey("Sisyphus (Ultraworker)")).toBe("sisyphus")
}) })
it("strips UI ordering prefixes before returning config keys", () => {
expect(normalizeAgentForPromptKey(getAgentListDisplayName("atlas"))).toBe("atlas")
})
it("preserves custom agents", () => { it("preserves custom agents", () => {
expect(normalizeAgentForPromptKey("MyCustomAgent")).toBe("MyCustomAgent") expect(normalizeAgentForPromptKey("MyCustomAgent")).toBe("MyCustomAgent")
}) })
@@ -259,7 +267,7 @@ describe("AGENT_DISPLAY_NAMES", () => {
const httpHeaderUnsafe = /[()]/ const httpHeaderUnsafe = /[()]/
// when checking each display name // when checking each display name
for (const [key, displayName] of Object.entries(AGENT_DISPLAY_NAMES)) { for (const [, displayName] of Object.entries(AGENT_DISPLAY_NAMES)) {
// then none should contain parentheses // then none should contain parentheses
expect(httpHeaderUnsafe.test(displayName)).toBe(false) expect(httpHeaderUnsafe.test(displayName)).toBe(false)
} }
+15 -31
View File
@@ -94,18 +94,23 @@ const LEGACY_DISPLAY_NAMES: Record<string, string> = {
"athena-junior (council)": "athena-junior", "athena-junior (council)": "athena-junior",
} }
/** function resolveKnownAgentConfigKey(agentName: string): string | undefined {
* Resolve an agent name (display name or config key) to its lowercase config key.
* "Atlas - Plan Executor" -> "atlas", "Atlas (Plan Executor)" -> "atlas", "atlas" -> "atlas"
*/
export function getAgentConfigKey(agentName: string): string {
const lower = stripAgentListSortPrefix(agentName).trim().toLowerCase() const lower = stripAgentListSortPrefix(agentName).trim().toLowerCase()
const reversed = REVERSE_DISPLAY_NAMES[lower] const reversed = REVERSE_DISPLAY_NAMES[lower]
if (reversed !== undefined) return reversed if (reversed !== undefined) return reversed
const legacy = LEGACY_DISPLAY_NAMES[lower] const legacy = LEGACY_DISPLAY_NAMES[lower]
if (legacy !== undefined) return legacy if (legacy !== undefined) return legacy
if (AGENT_DISPLAY_NAMES[lower] !== undefined) return lower if (AGENT_DISPLAY_NAMES[lower] !== undefined) return lower
return lower return undefined
}
/**
* Resolve an agent name (display name or config key) to its lowercase config key.
* "Atlas - Plan Executor" -> "atlas", "Atlas (Plan Executor)" -> "atlas", "atlas" -> "atlas"
*/
export function getAgentConfigKey(agentName: string): string {
const lower = stripAgentListSortPrefix(agentName).trim().toLowerCase()
return resolveKnownAgentConfigKey(agentName) ?? lower
} }
/** /**
@@ -124,17 +129,9 @@ export function normalizeAgentForPrompt(agentName: string | undefined): string |
return undefined return undefined
} }
const lower = trimmed.toLowerCase() const configKey = resolveKnownAgentConfigKey(trimmed)
const reversed = REVERSE_DISPLAY_NAMES[lower] if (configKey !== undefined) {
if (reversed !== undefined) { return AGENT_DISPLAY_NAMES[configKey] ?? trimmed
return AGENT_DISPLAY_NAMES[reversed] ?? trimmed
}
const legacy = LEGACY_DISPLAY_NAMES[lower]
if (legacy !== undefined) {
return AGENT_DISPLAY_NAMES[legacy] ?? trimmed
}
if (AGENT_DISPLAY_NAMES[lower] !== undefined) {
return AGENT_DISPLAY_NAMES[lower]
} }
return trimmed return trimmed
@@ -150,18 +147,5 @@ export function normalizeAgentForPromptKey(agentName: string | undefined): strin
return undefined return undefined
} }
const lower = trimmed.toLowerCase() return resolveKnownAgentConfigKey(trimmed) ?? trimmed
const reversed = REVERSE_DISPLAY_NAMES[lower]
if (reversed !== undefined) {
return reversed
}
const legacy = LEGACY_DISPLAY_NAMES[lower]
if (legacy !== undefined) {
return legacy
}
if (AGENT_DISPLAY_NAMES[lower] !== undefined) {
return lower
}
return trimmed
} }