Merge pull request #3079 from code-yeongyu/refactor/deslop-cli-mcp-openclaw
refactor(cli,mcp,openclaw): remove AI slop from code comments
This commit is contained in:
@@ -103,7 +103,6 @@ export function handleMessagePartUpdated(ctx: RunContext, payload: EventPayload,
|
|||||||
if (payload.type !== "message.part.updated") return
|
if (payload.type !== "message.part.updated") return
|
||||||
|
|
||||||
const props = payload.properties as MessagePartUpdatedProps | undefined
|
const props = payload.properties as MessagePartUpdatedProps | undefined
|
||||||
// Current OpenCode puts sessionID inside part; legacy puts it in info
|
|
||||||
const partSid = getPartSessionId(props)
|
const partSid = getPartSessionId(props)
|
||||||
const infoSid = getInfoSessionId(props)
|
const infoSid = getInfoSessionId(props)
|
||||||
if ((partSid ?? infoSid) !== ctx.sessionID) return
|
if ((partSid ?? infoSid) !== ctx.sessionID) return
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ export interface EventState {
|
|||||||
currentModel: string | null
|
currentModel: string | null
|
||||||
/** Current model variant from the latest assistant message */
|
/** Current model variant from the latest assistant message */
|
||||||
currentVariant: string | null
|
currentVariant: string | null
|
||||||
/** Current message role (user/assistant) — used to filter user messages from display */
|
|
||||||
currentMessageRole: string | null
|
currentMessageRole: string | null
|
||||||
/** Agent profile colors keyed by display name */
|
/** Agent profile colors keyed by display name */
|
||||||
agentColorsByName: Record<string, string>
|
agentColorsByName: Record<string, string>
|
||||||
@@ -39,7 +38,6 @@ export interface EventState {
|
|||||||
textAtLineStart: boolean
|
textAtLineStart: boolean
|
||||||
/** Whether reasoning stream is currently at line start (for padding) */
|
/** Whether reasoning stream is currently at line start (for padding) */
|
||||||
thinkingAtLineStart: boolean
|
thinkingAtLineStart: boolean
|
||||||
/** Current assistant message ID — prevents counter resets on repeated message.updated for same message */
|
|
||||||
currentMessageId: string | null
|
currentMessageId: string | null
|
||||||
/** Assistant message start timestamp by message ID */
|
/** Assistant message start timestamp by message ID */
|
||||||
messageStartedAtById: Record<string, number>
|
messageStartedAtById: Record<string, number>
|
||||||
|
|||||||
@@ -50,7 +50,6 @@ export async function pollForCompletion(
|
|||||||
return 130
|
return 130
|
||||||
}
|
}
|
||||||
|
|
||||||
// ERROR CHECK FIRST — errors must not be masked by other gates
|
|
||||||
if (eventState.mainSessionError) {
|
if (eventState.mainSessionError) {
|
||||||
errorCycleCount++
|
errorCycleCount++
|
||||||
if (errorCycleCount >= ERROR_GRACE_CYCLES) {
|
if (errorCycleCount >= ERROR_GRACE_CYCLES) {
|
||||||
@@ -62,19 +61,15 @@ export async function pollForCompletion(
|
|||||||
)
|
)
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
// Continue polling during grace period to allow recovery
|
|
||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
// Reset error counter when error clears (recovery succeeded)
|
|
||||||
errorCycleCount = 0
|
errorCycleCount = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Watchdog: if no events received for N seconds, verify session status via API
|
|
||||||
let mainSessionStatus: "idle" | "busy" | "retry" | null = null
|
let mainSessionStatus: "idle" | "busy" | "retry" | null = null
|
||||||
if (eventState.lastEventTimestamp !== null) {
|
if (eventState.lastEventTimestamp !== null) {
|
||||||
const timeSinceLastEvent = Date.now() - eventState.lastEventTimestamp
|
const timeSinceLastEvent = Date.now() - eventState.lastEventTimestamp
|
||||||
if (timeSinceLastEvent > eventWatchdogMs) {
|
if (timeSinceLastEvent > eventWatchdogMs) {
|
||||||
// Events stopped coming - verify actual session state
|
|
||||||
console.log(
|
console.log(
|
||||||
pc.yellow(
|
pc.yellow(
|
||||||
`\n No events for ${Math.round(
|
`\n No events for ${Math.round(
|
||||||
@@ -83,7 +78,6 @@ export async function pollForCompletion(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
// Force check session status directly
|
|
||||||
mainSessionStatus = await getMainSessionStatus(ctx)
|
mainSessionStatus = await getMainSessionStatus(ctx)
|
||||||
if (mainSessionStatus === "idle") {
|
if (mainSessionStatus === "idle") {
|
||||||
eventState.mainSessionIdle = true
|
eventState.mainSessionIdle = true
|
||||||
@@ -91,12 +85,10 @@ export async function pollForCompletion(
|
|||||||
eventState.mainSessionIdle = false
|
eventState.mainSessionIdle = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset timestamp to avoid repeated checks
|
|
||||||
eventState.lastEventTimestamp = Date.now()
|
eventState.lastEventTimestamp = Date.now()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only call getMainSessionStatus if watchdog didn't already check
|
|
||||||
if (mainSessionStatus === null) {
|
if (mainSessionStatus === null) {
|
||||||
mainSessionStatus = await getMainSessionStatus(ctx)
|
mainSessionStatus = await getMainSessionStatus(ctx)
|
||||||
}
|
}
|
||||||
@@ -122,15 +114,11 @@ export async function pollForCompletion(
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Secondary timeout: if we've been polling for reasonable time but haven't
|
|
||||||
// received meaningful work via events, check if there's active work via API
|
|
||||||
// Only check once to avoid unnecessary API calls every poll cycle
|
|
||||||
if (
|
if (
|
||||||
Date.now() - pollStartTimestamp > secondaryMeaningfulWorkTimeoutMs &&
|
Date.now() - pollStartTimestamp > secondaryMeaningfulWorkTimeoutMs &&
|
||||||
!secondaryTimeoutChecked
|
!secondaryTimeoutChecked
|
||||||
) {
|
) {
|
||||||
secondaryTimeoutChecked = true
|
secondaryTimeoutChecked = true
|
||||||
// Check if session actually has pending work (children, todos, etc.)
|
|
||||||
const childrenRes = await ctx.client.session.children({
|
const childrenRes = await ctx.client.session.children({
|
||||||
path: { id: ctx.sessionID },
|
path: { id: ctx.sessionID },
|
||||||
query: { directory: ctx.directory },
|
query: { directory: ctx.directory },
|
||||||
@@ -154,7 +142,6 @@ export async function pollForCompletion(
|
|||||||
const hasActiveWork = hasActiveChildren || hasActiveTodos
|
const hasActiveWork = hasActiveChildren || hasActiveTodos
|
||||||
|
|
||||||
if (hasActiveWork) {
|
if (hasActiveWork) {
|
||||||
// Assume meaningful work is happening even without events
|
|
||||||
eventState.hasReceivedMeaningfulWork = true
|
eventState.hasReceivedMeaningfulWork = true
|
||||||
console.log(
|
console.log(
|
||||||
pc.yellow(
|
pc.yellow(
|
||||||
@@ -166,12 +153,10 @@ export async function pollForCompletion(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Track when first meaningful work was received
|
|
||||||
if (firstWorkTimestamp === null) {
|
if (firstWorkTimestamp === null) {
|
||||||
firstWorkTimestamp = Date.now()
|
firstWorkTimestamp = Date.now()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't check completion during stabilization period
|
|
||||||
if (Date.now() - firstWorkTimestamp < minStabilizationMs) {
|
if (Date.now() - firstWorkTimestamp < minStabilizationMs) {
|
||||||
consecutiveCompleteChecks = 0
|
consecutiveCompleteChecks = 0
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -114,7 +114,6 @@ export async function run(options: RunOptions): Promise<number> {
|
|||||||
})
|
})
|
||||||
const exitCode = await pollForCompletion(ctx, eventState, abortController)
|
const exitCode = await pollForCompletion(ctx, eventState, abortController)
|
||||||
|
|
||||||
// Abort the event stream to stop the processor
|
|
||||||
abortController.abort()
|
abortController.abort()
|
||||||
|
|
||||||
await waitForEventProcessorShutdown(eventProcessor)
|
await waitForEventProcessorShutdown(eventProcessor)
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ export async function resolveSession(options: {
|
|||||||
const res = await client.session.create({
|
const res = await client.session.create({
|
||||||
body: {
|
body: {
|
||||||
title: "oh-my-opencode run",
|
title: "oh-my-opencode run",
|
||||||
// In CLI run mode there's no TUI to answer questions.
|
|
||||||
permission: [
|
permission: [
|
||||||
{ permission: "question", action: "deny" as const, pattern: "*" },
|
{ permission: "question", action: "deny" as const, pattern: "*" },
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -81,7 +81,6 @@ export interface MessageUpdatedProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface MessagePartUpdatedProps {
|
export interface MessagePartUpdatedProps {
|
||||||
/** @deprecated Legacy structure — current OpenCode puts sessionID inside part */
|
|
||||||
info?: { sessionID?: string; sessionId?: string; role?: string }
|
info?: { sessionID?: string; sessionId?: string; role?: string }
|
||||||
part?: {
|
part?: {
|
||||||
id?: string
|
id?: string
|
||||||
|
|||||||
@@ -5,6 +5,5 @@ export const context7 = {
|
|||||||
headers: process.env.CONTEXT7_API_KEY
|
headers: process.env.CONTEXT7_API_KEY
|
||||||
? { Authorization: `Bearer ${process.env.CONTEXT7_API_KEY}` }
|
? { Authorization: `Bearer ${process.env.CONTEXT7_API_KEY}` }
|
||||||
: undefined,
|
: undefined,
|
||||||
// Disable OAuth auto-detection - Context7 uses API key header, not OAuth
|
|
||||||
oauth: false as const,
|
oauth: false as const,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ export function createWebsearchConfig(config?: WebsearchConfig): RemoteMcpConfig
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default to Exa
|
|
||||||
return {
|
return {
|
||||||
type: "remote" as const,
|
type: "remote" as const,
|
||||||
url: process.env.EXA_API_KEY
|
url: process.env.EXA_API_KEY
|
||||||
@@ -42,5 +41,4 @@ export function createWebsearchConfig(config?: WebsearchConfig): RemoteMcpConfig
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Backward compatibility: export static instance using default config
|
|
||||||
export const websearch = createWebsearchConfig()
|
export const websearch = createWebsearchConfig()
|
||||||
|
|||||||
@@ -89,11 +89,9 @@ export function resolveGateway(
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate based on gateway type
|
|
||||||
if (gateway.type === "command") {
|
if (gateway.type === "command") {
|
||||||
if (!gateway.command) return null
|
if (!gateway.command) return null
|
||||||
} else {
|
} else {
|
||||||
// HTTP gateway
|
|
||||||
if (!gateway.url) return null
|
if (!gateway.url) return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -134,7 +134,6 @@ export async function wakeCommandGateway(
|
|||||||
try {
|
try {
|
||||||
const timeout = resolveCommandTimeoutMs(gatewayConfig.timeout)
|
const timeout = resolveCommandTimeoutMs(gatewayConfig.timeout)
|
||||||
|
|
||||||
// Interpolate variables with shell escaping
|
|
||||||
const interpolated = gatewayConfig.command.replace(/\{\{(\w+)\}\}/g, (_match, key) => {
|
const interpolated = gatewayConfig.command.replace(/\{\{(\w+)\}\}/g, (_match, key) => {
|
||||||
const value = variables[key]
|
const value = variables[key]
|
||||||
if (value === undefined) return _match
|
if (value === undefined) return _match
|
||||||
|
|||||||
@@ -82,7 +82,6 @@ function writeSecureFile(filePath: string, content: string): void {
|
|||||||
try {
|
try {
|
||||||
chmodSync(filePath, SECURE_FILE_MODE)
|
chmodSync(filePath, SECURE_FILE_MODE)
|
||||||
} catch {
|
} catch {
|
||||||
// Ignore
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,7 +97,6 @@ function rotateLogIfNeeded(logPath: string): void {
|
|||||||
renameSync(logPath, backupPath)
|
renameSync(logPath, backupPath)
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
// Ignore
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,7 +108,6 @@ function log(message: string): void {
|
|||||||
const logLine = `[${timestamp}] ${message}\n`
|
const logLine = `[${timestamp}] ${message}\n`
|
||||||
appendFileSync(LOG_FILE_PATH, logLine, { mode: SECURE_FILE_MODE })
|
appendFileSync(LOG_FILE_PATH, logLine, { mode: SECURE_FILE_MODE })
|
||||||
} catch {
|
} catch {
|
||||||
// Ignore
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,6 +127,31 @@ interface DaemonState {
|
|||||||
lastError?: string
|
lastError?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface TelegramMessage {
|
||||||
|
message_id?: number
|
||||||
|
chat?: { id?: number | string }
|
||||||
|
text?: string
|
||||||
|
reply_to_message?: { message_id?: number }
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TelegramUpdate {
|
||||||
|
update_id?: number
|
||||||
|
message?: TelegramMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TelegramUpdatesResponse {
|
||||||
|
result?: TelegramUpdate[]
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseTelegramUpdatesResponse(body: unknown): TelegramUpdate[] {
|
||||||
|
if (typeof body !== "object" || body === null) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = (body as TelegramUpdatesResponse).result
|
||||||
|
return Array.isArray(result) ? result : []
|
||||||
|
}
|
||||||
|
|
||||||
function readDaemonState(): DaemonState | null {
|
function readDaemonState(): DaemonState | null {
|
||||||
try {
|
try {
|
||||||
if (!existsSync(STATE_FILE_PATH)) return null
|
if (!existsSync(STATE_FILE_PATH)) return null
|
||||||
@@ -195,7 +217,6 @@ export async function isReplyListenerProcess(pid: number): Promise<boolean> {
|
|||||||
const cmdline = readFileSync(`/proc/${pid}/cmdline`, "utf-8")
|
const cmdline = readFileSync(`/proc/${pid}/cmdline`, "utf-8")
|
||||||
return cmdline.includes(DAEMON_IDENTITY_MARKER)
|
return cmdline.includes(DAEMON_IDENTITY_MARKER)
|
||||||
}
|
}
|
||||||
// macOS
|
|
||||||
const proc = spawn(["ps", "-p", String(pid), "-o", "args="], {
|
const proc = spawn(["ps", "-p", String(pid), "-o", "args="], {
|
||||||
stdout: "pipe",
|
stdout: "pipe",
|
||||||
stderr: "ignore",
|
stderr: "ignore",
|
||||||
@@ -222,7 +243,6 @@ export async function isDaemonRunning(): Promise<boolean> {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Input Sanitization
|
|
||||||
export function sanitizeReplyInput(text: string): string {
|
export function sanitizeReplyInput(text: string): string {
|
||||||
return text
|
return text
|
||||||
.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, "")
|
.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, "")
|
||||||
@@ -387,7 +407,6 @@ async function pollDiscord(
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
} catch {
|
} catch {
|
||||||
// Ignore
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
state.errors++
|
state.errors++
|
||||||
@@ -427,52 +446,52 @@ async function pollTelegram(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const body = await response.json() as any
|
const body = await response.json()
|
||||||
const updates = body.result || []
|
const updates = parseTelegramUpdatesResponse(body)
|
||||||
|
|
||||||
for (const update of updates) {
|
for (const update of updates) {
|
||||||
const msg = update.message
|
const msg = update.message
|
||||||
if (!msg) {
|
if (!msg) {
|
||||||
state.telegramLastUpdateId = update.update_id
|
state.telegramLastUpdateId = update.update_id ?? state.telegramLastUpdateId
|
||||||
writeDaemonState(state)
|
writeDaemonState(state)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!msg.reply_to_message?.message_id) {
|
if (msg.reply_to_message?.message_id === undefined) {
|
||||||
state.telegramLastUpdateId = update.update_id
|
state.telegramLastUpdateId = update.update_id ?? state.telegramLastUpdateId
|
||||||
writeDaemonState(state)
|
writeDaemonState(state)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if (String(msg.chat.id) !== replyListener.telegramChatId) {
|
if (String(msg.chat?.id) !== replyListener.telegramChatId) {
|
||||||
state.telegramLastUpdateId = update.update_id
|
state.telegramLastUpdateId = update.update_id ?? state.telegramLastUpdateId
|
||||||
writeDaemonState(state)
|
writeDaemonState(state)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapping = lookupByMessageId("telegram", String(msg.reply_to_message.message_id))
|
const mapping = lookupByMessageId("telegram", String(msg.reply_to_message.message_id))
|
||||||
if (!mapping) {
|
if (!mapping) {
|
||||||
state.telegramLastUpdateId = update.update_id
|
state.telegramLastUpdateId = update.update_id ?? state.telegramLastUpdateId
|
||||||
writeDaemonState(state)
|
writeDaemonState(state)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
const text = msg.text || ""
|
const text = msg.text || ""
|
||||||
if (!text) {
|
if (!text) {
|
||||||
state.telegramLastUpdateId = update.update_id
|
state.telegramLastUpdateId = update.update_id ?? state.telegramLastUpdateId
|
||||||
writeDaemonState(state)
|
writeDaemonState(state)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!rateLimiter.canProceed()) {
|
if (!rateLimiter.canProceed()) {
|
||||||
log(`WARN: Rate limit exceeded, dropping Telegram message ${msg.message_id}`)
|
log(`WARN: Rate limit exceeded, dropping Telegram message ${msg.message_id}`)
|
||||||
state.telegramLastUpdateId = update.update_id
|
state.telegramLastUpdateId = update.update_id ?? state.telegramLastUpdateId
|
||||||
writeDaemonState(state)
|
writeDaemonState(state)
|
||||||
state.errors++
|
state.errors++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
state.telegramLastUpdateId = update.update_id
|
state.telegramLastUpdateId = update.update_id ?? state.telegramLastUpdateId
|
||||||
writeDaemonState(state)
|
writeDaemonState(state)
|
||||||
|
|
||||||
const success = await injectReply(mapping.tmuxPaneId, text, "telegram", config)
|
const success = await injectReply(mapping.tmuxPaneId, text, "telegram", config)
|
||||||
@@ -604,9 +623,6 @@ export async function startReplyListener(config: OpenClawConfig): Promise<{ succ
|
|||||||
const normalizedConfig = normalizeReplyListenerConfig(config)
|
const normalizedConfig = normalizeReplyListenerConfig(config)
|
||||||
const replyListener = normalizedConfig.replyListener
|
const replyListener = normalizedConfig.replyListener
|
||||||
if (!replyListener?.discordBotToken && !replyListener?.telegramBotToken) {
|
if (!replyListener?.discordBotToken && !replyListener?.telegramBotToken) {
|
||||||
// Only warn if no platforms enabled, but user might just want outbound
|
|
||||||
// Actually, instructions say: "Fire-and-forget for outbound, daemon process for inbound"
|
|
||||||
// So if no inbound config, we shouldn't start daemon.
|
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
message: "No enabled reply listener platforms configured (missing bot tokens/channels)",
|
message: "No enabled reply listener platforms configured (missing bot tokens/channels)",
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ function ensureRegistryDir(): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function sleepMs(ms: number): void {
|
function sleepMs(ms: number): void {
|
||||||
// Use Atomics.wait for synchronous sleep
|
|
||||||
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms)
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,7 +78,6 @@ function readLockSnapshot(): LockSnapshot | null {
|
|||||||
typeof parsed.token === "string" && parsed.token.length > 0 ? parsed.token : null
|
typeof parsed.token === "string" && parsed.token.length > 0 ? parsed.token : null
|
||||||
return { raw, pid, token }
|
return { raw, pid, token }
|
||||||
} catch {
|
} catch {
|
||||||
// Legacy format or plain PID
|
|
||||||
const [pidStr] = trimmed.split(":")
|
const [pidStr] = trimmed.split(":")
|
||||||
const parsedPid = Number.parseInt(pidStr ?? "", 10)
|
const parsedPid = Number.parseInt(pidStr ?? "", 10)
|
||||||
return {
|
return {
|
||||||
@@ -132,12 +130,10 @@ function acquireRegistryLock(): LockHandle | null {
|
|||||||
try {
|
try {
|
||||||
closeSync(fd)
|
closeSync(fd)
|
||||||
} catch {
|
} catch {
|
||||||
// Ignore
|
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
unlinkSync(REGISTRY_LOCK_PATH)
|
unlinkSync(REGISTRY_LOCK_PATH)
|
||||||
} catch {
|
} catch {
|
||||||
// Ignore
|
|
||||||
}
|
}
|
||||||
throw writeError
|
throw writeError
|
||||||
}
|
}
|
||||||
@@ -164,7 +160,6 @@ function acquireRegistryLock(): LockHandle | null {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
// Ignore errors
|
|
||||||
}
|
}
|
||||||
sleepMs(LOCK_RETRY_MS)
|
sleepMs(LOCK_RETRY_MS)
|
||||||
}
|
}
|
||||||
@@ -188,8 +183,7 @@ function releaseRegistryLock(lock: LockHandle): void {
|
|||||||
try {
|
try {
|
||||||
closeSync(lock.fd)
|
closeSync(lock.fd)
|
||||||
} catch {
|
} catch {
|
||||||
// Ignore
|
}
|
||||||
}
|
|
||||||
const snapshot = readLockSnapshot()
|
const snapshot = readLockSnapshot()
|
||||||
if (!snapshot || snapshot.token !== lock.token) return
|
if (!snapshot || snapshot.token !== lock.token) return
|
||||||
removeLockIfUnchanged(snapshot)
|
removeLockIfUnchanged(snapshot)
|
||||||
@@ -298,7 +292,6 @@ export function removeSession(sessionId: string): void {
|
|||||||
rewriteRegistryUnsafe(filtered)
|
rewriteRegistryUnsafe(filtered)
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
// Best-effort
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -312,7 +305,6 @@ export function removeMessagesByPane(paneId: string): void {
|
|||||||
rewriteRegistryUnsafe(filtered)
|
rewriteRegistryUnsafe(filtered)
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
// Best-effort
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -334,7 +326,6 @@ export function pruneStale(): void {
|
|||||||
rewriteRegistryUnsafe(filtered)
|
rewriteRegistryUnsafe(filtered)
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
// Best-effort
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ export function getCurrentTmuxSession(): string | null {
|
|||||||
const env = process.env.TMUX
|
const env = process.env.TMUX
|
||||||
if (!env) return null
|
if (!env) return null
|
||||||
const match = env.match(/(\d+)$/)
|
const match = env.match(/(\d+)$/)
|
||||||
return match ? `session-${match[1]}` : null // Wait, TMUX env is /tmp/tmux-501/default,1234,0
|
return match ? `session-${match[1]}` : null
|
||||||
// Reference tmux.js gets session name via `tmux display-message -p '#S'`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getTmuxSessionName(): Promise<string | null> {
|
export async function getTmuxSessionName(): Promise<string | null> {
|
||||||
@@ -17,7 +16,6 @@ export async function getTmuxSessionName(): Promise<string | null> {
|
|||||||
const outputPromise = new Response(proc.stdout).text()
|
const outputPromise = new Response(proc.stdout).text()
|
||||||
await proc.exited
|
await proc.exited
|
||||||
const output = await outputPromise
|
const output = await outputPromise
|
||||||
// Await proc.exited ensures exitCode is set; avoid race condition
|
|
||||||
if (proc.exitCode !== 0) return null
|
if (proc.exitCode !== 0) return null
|
||||||
return output.trim() || null
|
return output.trim() || null
|
||||||
} catch {
|
} catch {
|
||||||
|
|||||||
Reference in New Issue
Block a user