mirror of
https://github.com/stablyai/orca.git
synced 2026-09-22 00:02:31 +00:00
fix: allow hermes log paths with dot-dot names
This commit is contained in:
@@ -174,6 +174,36 @@ Run summary: monitor automation completed successfully.
|
||||
)
|
||||
})
|
||||
|
||||
it('hydrates referenced logs in valid dot-dot-prefixed Hermes subdirectories', async () => {
|
||||
const home = await createHermesHome()
|
||||
const outputDir = join(home, 'cron', 'output', 'job-1')
|
||||
const scriptLogPath = join(home, '..logs', 'x-monitor.log')
|
||||
await mkdir(outputDir, { recursive: true })
|
||||
await mkdir(dirname(scriptLogPath), { recursive: true })
|
||||
await writeFile(scriptLogPath, 'dot-dot-prefixed log line\n', 'utf-8')
|
||||
await writeFile(
|
||||
join(outputDir, '2026-05-15_09-02-00.md'),
|
||||
`# Cron Job: Monitor automation
|
||||
|
||||
## Response
|
||||
|
||||
Latest log path: ${scriptLogPath}
|
||||
Run summary: monitor automation completed successfully.
|
||||
`,
|
||||
'utf-8'
|
||||
)
|
||||
|
||||
const { readHermesCronOutputRunsPage } = await loadReader()
|
||||
const page = await readHermesCronOutputRunsPage('job-1', { page: 1, pageSize: 25 })
|
||||
|
||||
expect((page.runs[0] as { output_content?: string }).output_content).toContain(
|
||||
'## Latest log file'
|
||||
)
|
||||
expect((page.runs[0] as { output_content?: string }).output_content).toContain(
|
||||
'dot-dot-prefixed log line'
|
||||
)
|
||||
})
|
||||
|
||||
it('uses a count-only path when page size is zero', async () => {
|
||||
const home = await createHermesHome()
|
||||
const outputDir = join(home, 'cron', 'output', 'job-1')
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { existsSync } from 'fs'
|
||||
import { open, readdir, readFile, realpath, stat } from 'fs/promises'
|
||||
import { homedir } from 'os'
|
||||
import { isAbsolute, join, relative, resolve } from 'path'
|
||||
import { isAbsolute, join, relative, resolve, sep } from 'path'
|
||||
import Database from '../sqlite/sync-database'
|
||||
|
||||
const HERMES_HOME = process.env.HERMES_HOME?.trim() || join(homedir(), '.hermes')
|
||||
@@ -162,7 +162,11 @@ async function readReferencedLogFile(content: string): Promise<{
|
||||
const relativeToHermesHome = relative(resolve(homeRealPath), resolve(logRealPath))
|
||||
// Why: the output body can contain agent-authored text, so only hydrate
|
||||
// referenced files that resolve inside Hermes' own data directory.
|
||||
if (relativeToHermesHome.startsWith('..') || isAbsolute(relativeToHermesHome)) {
|
||||
if (
|
||||
relativeToHermesHome === '..' ||
|
||||
relativeToHermesHome.startsWith(`..${sep}`) ||
|
||||
isAbsolute(relativeToHermesHome)
|
||||
) {
|
||||
return null
|
||||
}
|
||||
const logStat = await stat(logPath)
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import { mkdtemp, mkdir, rm, writeFile } from 'fs/promises'
|
||||
import { tmpdir } from 'os'
|
||||
import { dirname, join } from 'path'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { RelayDispatcher } from './dispatcher'
|
||||
|
||||
type ReferencedLogReader = {
|
||||
readReferencedLogFile(content: string): Promise<{ content: string } | null>
|
||||
}
|
||||
|
||||
describe('ExternalAutomationsHandler referenced log paths', () => {
|
||||
let hermesHome: string
|
||||
let previousHermesHome: string | undefined
|
||||
|
||||
beforeEach(async () => {
|
||||
previousHermesHome = process.env.HERMES_HOME
|
||||
hermesHome = await mkdtemp(join(tmpdir(), 'relay-hermes-output-'))
|
||||
process.env.HERMES_HOME = hermesHome
|
||||
vi.resetModules()
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
if (previousHermesHome === undefined) {
|
||||
delete process.env.HERMES_HOME
|
||||
} else {
|
||||
process.env.HERMES_HOME = previousHermesHome
|
||||
}
|
||||
await rm(hermesHome, { recursive: true, force: true })
|
||||
vi.resetModules()
|
||||
})
|
||||
|
||||
it('hydrates referenced logs in valid dot-dot-prefixed Hermes subdirectories', async () => {
|
||||
const logPath = join(hermesHome, '..logs', 'x-monitor.log')
|
||||
await mkdir(dirname(logPath), { recursive: true })
|
||||
await writeFile(logPath, 'remote dot-dot-prefixed log line\n', 'utf-8')
|
||||
|
||||
const { ExternalAutomationsHandler } = await import('./external-automations-handler')
|
||||
const handler = new ExternalAutomationsHandler({
|
||||
onRequest: () => {}
|
||||
} as unknown as RelayDispatcher) as unknown as ReferencedLogReader
|
||||
|
||||
const result = await handler.readReferencedLogFile(`Latest log path: ${logPath}
|
||||
Run summary: monitor automation completed successfully.`)
|
||||
|
||||
expect(result?.content).toBe('remote dot-dot-prefixed log line\n')
|
||||
})
|
||||
})
|
||||
@@ -5,7 +5,7 @@ import { existsSync } from 'fs'
|
||||
import { open, readdir, readFile, realpath, stat } from 'fs/promises'
|
||||
import { createRequire } from 'module'
|
||||
import { homedir } from 'os'
|
||||
import { isAbsolute, join, relative, resolve } from 'path'
|
||||
import { isAbsolute, join, relative, resolve, sep } from 'path'
|
||||
import { promisify } from 'util'
|
||||
import type { RelayDispatcher } from './dispatcher'
|
||||
|
||||
@@ -246,7 +246,11 @@ export class ExternalAutomationsHandler {
|
||||
const relativeToHermesHome = relative(resolve(homeRealPath), resolve(logRealPath))
|
||||
// Why: the output body can contain agent-authored text, so only hydrate
|
||||
// referenced files that resolve inside Hermes' own data directory.
|
||||
if (relativeToHermesHome.startsWith('..') || isAbsolute(relativeToHermesHome)) {
|
||||
if (
|
||||
relativeToHermesHome === '..' ||
|
||||
relativeToHermesHome.startsWith(`..${sep}`) ||
|
||||
isAbsolute(relativeToHermesHome)
|
||||
) {
|
||||
return null
|
||||
}
|
||||
const logStat = await stat(logPath)
|
||||
|
||||
Reference in New Issue
Block a user