refactor timeline loading to cursor-based fetch API
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
import type { Command } from 'commander'
|
||||
import { connectToDaemon, getDaemonHost } from '../../utils/client.js'
|
||||
import { fetchProjectedTimelineItems } from '../../utils/timeline.js'
|
||||
import type {
|
||||
DaemonClient,
|
||||
AgentStreamMessage,
|
||||
AgentStreamSnapshotMessage,
|
||||
AgentStreamEventPayload,
|
||||
AgentTimelineItem,
|
||||
} from '@getpaseo/server'
|
||||
@@ -131,37 +131,19 @@ export async function runAttachCommand(
|
||||
console.log(`Attaching to agent ${resolvedId.substring(0, 7)}...`)
|
||||
console.log(`(Press Ctrl+C to detach)\n`)
|
||||
|
||||
// Get existing output from snapshot
|
||||
const snapshotPromise = new Promise<void>((resolve) => {
|
||||
const timeout = setTimeout(() => resolve(), 5000)
|
||||
|
||||
const unsubscribe = client.on('agent_stream_snapshot', (msg: unknown) => {
|
||||
const message = msg as AgentStreamSnapshotMessage
|
||||
if (message.type !== 'agent_stream_snapshot') return
|
||||
if (message.payload.agentId !== resolvedId) return
|
||||
|
||||
clearTimeout(timeout)
|
||||
unsubscribe()
|
||||
|
||||
// Print recent events from snapshot
|
||||
for (const e of message.payload.events) {
|
||||
printStreamEvent(e.event)
|
||||
}
|
||||
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
|
||||
// Initialize agent to trigger snapshot
|
||||
// Print existing output from timeline fetch.
|
||||
try {
|
||||
await client.initializeAgent(resolvedId)
|
||||
} catch {
|
||||
// Agent might already be initialized, continue
|
||||
const timelineItems = await fetchProjectedTimelineItems({
|
||||
client,
|
||||
agentId: resolvedId,
|
||||
})
|
||||
for (const item of timelineItems) {
|
||||
printTimelineItem(item)
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Warning: failed to fetch existing timeline', error)
|
||||
}
|
||||
|
||||
// Wait for snapshot
|
||||
await snapshotPromise
|
||||
|
||||
// Subscribe to new events
|
||||
const unsubscribe = client.on('agent_stream', (msg: unknown) => {
|
||||
const message = msg as AgentStreamMessage
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { Command } from 'commander'
|
||||
import { connectToDaemon, getDaemonHost } from '../../utils/client.js'
|
||||
import type { CommandOptions } from '../../output/index.js'
|
||||
import { fetchProjectedTimelineItems } from '../../utils/timeline.js'
|
||||
import type {
|
||||
DaemonClient,
|
||||
AgentStreamMessage,
|
||||
AgentStreamSnapshotMessage,
|
||||
AgentTimelineItem,
|
||||
} from '@getpaseo/server'
|
||||
import { curateAgentActivity } from '@getpaseo/server'
|
||||
@@ -54,19 +54,6 @@ function matchesFilter(item: AgentTimelineItem, filter?: string): boolean {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract timeline items from an agent_stream_snapshot message
|
||||
*/
|
||||
function extractTimelineFromSnapshot(message: AgentStreamSnapshotMessage): AgentTimelineItem[] {
|
||||
const items: AgentTimelineItem[] = []
|
||||
for (const e of message.payload.events) {
|
||||
if (e.event.type === 'timeline') {
|
||||
items.push(e.event.item)
|
||||
}
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
export async function runLogsCommand(
|
||||
id: string,
|
||||
options: AgentLogsOptions,
|
||||
@@ -112,32 +99,12 @@ export async function runLogsCommand(
|
||||
return
|
||||
}
|
||||
|
||||
// For non-follow mode, initialize the agent to get timeline snapshot
|
||||
// Set up handler for timeline events before initializing
|
||||
const snapshotPromise = new Promise<AgentTimelineItem[]>((resolve) => {
|
||||
const timeout = setTimeout(() => resolve([]), 10000)
|
||||
|
||||
const unsubscribe = client.on('agent_stream_snapshot', (msg: unknown) => {
|
||||
const message = msg as AgentStreamSnapshotMessage
|
||||
if (message.type !== 'agent_stream_snapshot') return
|
||||
if (message.payload.agentId !== resolvedId) return
|
||||
|
||||
clearTimeout(timeout)
|
||||
unsubscribe()
|
||||
resolve(extractTimelineFromSnapshot(message))
|
||||
})
|
||||
// Fetch timeline directly via cursor RPC.
|
||||
let timelineItems = await fetchProjectedTimelineItems({
|
||||
client,
|
||||
agentId: resolvedId,
|
||||
})
|
||||
|
||||
// Initialize agent to trigger timeline snapshot
|
||||
try {
|
||||
await client.initializeAgent(resolvedId)
|
||||
} catch {
|
||||
// Agent might already be initialized, continue to collect from queue
|
||||
}
|
||||
|
||||
// Get timeline from snapshot
|
||||
let timelineItems = await snapshotPromise
|
||||
|
||||
// Apply filter
|
||||
if (options.filter) {
|
||||
timelineItems = timelineItems.filter((item) => matchesFilter(item, options.filter))
|
||||
@@ -179,31 +146,12 @@ async function runFollowMode(
|
||||
const DEFAULT_FOLLOW_TAIL = 10
|
||||
const tailCount = parseTailCount(options.tail) ?? DEFAULT_FOLLOW_TAIL
|
||||
|
||||
// First, get existing timeline
|
||||
const snapshotPromise = new Promise<AgentTimelineItem[]>((resolve) => {
|
||||
const timeout = setTimeout(() => resolve([]), 10000)
|
||||
|
||||
const unsubscribe = client.on('agent_stream_snapshot', (msg: unknown) => {
|
||||
const message = msg as AgentStreamSnapshotMessage
|
||||
if (message.type !== 'agent_stream_snapshot') return
|
||||
if (message.payload.agentId !== agentId) return
|
||||
|
||||
clearTimeout(timeout)
|
||||
unsubscribe()
|
||||
resolve(extractTimelineFromSnapshot(message))
|
||||
})
|
||||
// First, get existing timeline.
|
||||
let existingItems = await fetchProjectedTimelineItems({
|
||||
client,
|
||||
agentId,
|
||||
})
|
||||
|
||||
// Initialize agent to trigger timeline snapshot
|
||||
try {
|
||||
await client.initializeAgent(agentId)
|
||||
} catch {
|
||||
// Agent might already be initialized
|
||||
}
|
||||
|
||||
// Get existing timeline
|
||||
let existingItems = await snapshotPromise
|
||||
|
||||
// Apply filter to existing items
|
||||
if (options.filter) {
|
||||
existingItems = existingItems.filter((item) => matchesFilter(item, options.filter))
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import type { AgentTimelineItem, DaemonClient } from '@getpaseo/server'
|
||||
|
||||
type TimelineFetchResult = {
|
||||
entries: Array<{ item: AgentTimelineItem }>
|
||||
}
|
||||
|
||||
type TimelineFetchClient = {
|
||||
fetchAgentTimeline: (
|
||||
agentId: string,
|
||||
options: {
|
||||
direction: 'tail'
|
||||
limit: 0
|
||||
projection: 'projected'
|
||||
}
|
||||
) => Promise<TimelineFetchResult>
|
||||
}
|
||||
|
||||
type FetchProjectedTimelineItemsInput = {
|
||||
client: DaemonClient
|
||||
agentId: string
|
||||
}
|
||||
|
||||
export async function fetchProjectedTimelineItems(
|
||||
input: FetchProjectedTimelineItemsInput
|
||||
): Promise<AgentTimelineItem[]> {
|
||||
const timelineClient = input.client as unknown as TimelineFetchClient
|
||||
const timeline = await timelineClient.fetchAgentTimeline(input.agentId, {
|
||||
direction: 'tail',
|
||||
limit: 0,
|
||||
projection: 'projected',
|
||||
})
|
||||
return timeline.entries.map((entry) => entry.item)
|
||||
}
|
||||
Reference in New Issue
Block a user