mirror of
https://github.com/browseros-ai/BrowserOS.git
synced 2026-05-14 16:14:28 +00:00
Compare commits
1 Commits
fix/debug-
...
feat/progr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1fdad55b4a |
@@ -15,13 +15,23 @@ import type {
|
||||
} from '@browseros/shared/types/role-aware-agents'
|
||||
import { Hono } from 'hono'
|
||||
import { stream } from 'hono/streaming'
|
||||
import { getOpenClawDir } from '../../lib/browseros-dir'
|
||||
import {
|
||||
OpenClawAgentAlreadyExistsError,
|
||||
OpenClawAgentNotFoundError,
|
||||
OpenClawInvalidAgentNameError,
|
||||
OpenClawProtectedAgentError,
|
||||
} from '../services/openclaw/errors'
|
||||
import { getOpenClawService } from '../services/openclaw/openclaw-service'
|
||||
import {
|
||||
getOpenClawService,
|
||||
type OpenClawAgentEntry,
|
||||
} from '../services/openclaw/openclaw-service'
|
||||
import { OpenClawProgramMaterializer } from '../services/openclaw/program-materializer'
|
||||
import { OpenClawProgramStorage } from '../services/openclaw/program-storage'
|
||||
import {
|
||||
validateCreateProgramInput,
|
||||
validateUpdateProgramInput,
|
||||
} from '../services/openclaw/program-validation'
|
||||
|
||||
function isValidBoundaryMode(
|
||||
value: unknown,
|
||||
@@ -40,6 +50,19 @@ function isValidCustomRoleBoundary(value: unknown): boolean {
|
||||
)
|
||||
}
|
||||
|
||||
const openclawProgramStorage = new OpenClawProgramStorage(getOpenClawDir())
|
||||
const openclawProgramMaterializer = new OpenClawProgramMaterializer(
|
||||
getOpenClawDir(),
|
||||
openclawProgramStorage,
|
||||
)
|
||||
|
||||
async function findOpenClawAgent(
|
||||
agentId: string,
|
||||
): Promise<OpenClawAgentEntry | null> {
|
||||
const agents = await getOpenClawService().listAgents()
|
||||
return agents.find((agent) => agent.agentId === agentId) ?? null
|
||||
}
|
||||
|
||||
export function createOpenClawRoutes() {
|
||||
return new Hono()
|
||||
.get('/status', async (c) => {
|
||||
@@ -133,6 +156,116 @@ export function createOpenClawRoutes() {
|
||||
}
|
||||
})
|
||||
|
||||
.get('/agents/:id/programs', async (c) => {
|
||||
try {
|
||||
const agent = await findOpenClawAgent(c.req.param('id'))
|
||||
if (!agent) {
|
||||
return c.json({ error: 'Agent not found' }, 404)
|
||||
}
|
||||
|
||||
const programs = await openclawProgramStorage.listPrograms(agent.name)
|
||||
return c.json({ programs })
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err)
|
||||
return c.json({ error: message }, 500)
|
||||
}
|
||||
})
|
||||
|
||||
.post('/agents/:id/programs', async (c) => {
|
||||
try {
|
||||
const agent = await findOpenClawAgent(c.req.param('id'))
|
||||
if (!agent) {
|
||||
return c.json({ error: 'Agent not found' }, 404)
|
||||
}
|
||||
|
||||
const input = validateCreateProgramInput(await c.req.json())
|
||||
const program = await openclawProgramStorage.createProgram(agent, input)
|
||||
await openclawProgramMaterializer.syncAgentPrograms(agent.name)
|
||||
return c.json({ program }, 201)
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err)
|
||||
if (
|
||||
message.includes('required') ||
|
||||
message.includes('must be') ||
|
||||
message.includes('invalid')
|
||||
) {
|
||||
return c.json({ error: message }, 400)
|
||||
}
|
||||
return c.json({ error: message }, 500)
|
||||
}
|
||||
})
|
||||
|
||||
.patch('/agents/:id/programs/:programId', async (c) => {
|
||||
try {
|
||||
const agent = await findOpenClawAgent(c.req.param('id'))
|
||||
if (!agent) {
|
||||
return c.json({ error: 'Agent not found' }, 404)
|
||||
}
|
||||
|
||||
const input = validateUpdateProgramInput(await c.req.json())
|
||||
const program = await openclawProgramStorage.updateProgram(
|
||||
agent.name,
|
||||
c.req.param('programId'),
|
||||
input,
|
||||
)
|
||||
if (!program) {
|
||||
return c.json({ error: 'Program not found' }, 404)
|
||||
}
|
||||
|
||||
await openclawProgramMaterializer.syncAgentPrograms(agent.name)
|
||||
return c.json({ program })
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err)
|
||||
if (
|
||||
message.includes('required') ||
|
||||
message.includes('must be') ||
|
||||
message.includes('invalid') ||
|
||||
message.includes('At least one')
|
||||
) {
|
||||
return c.json({ error: message }, 400)
|
||||
}
|
||||
return c.json({ error: message }, 500)
|
||||
}
|
||||
})
|
||||
|
||||
.delete('/agents/:id/programs/:programId', async (c) => {
|
||||
try {
|
||||
const agent = await findOpenClawAgent(c.req.param('id'))
|
||||
if (!agent) {
|
||||
return c.json({ error: 'Agent not found' }, 404)
|
||||
}
|
||||
|
||||
const deleted = await openclawProgramStorage.deleteProgram(
|
||||
agent.name,
|
||||
c.req.param('programId'),
|
||||
)
|
||||
if (!deleted) {
|
||||
return c.json({ error: 'Program not found' }, 404)
|
||||
}
|
||||
|
||||
await openclawProgramMaterializer.syncAgentPrograms(agent.name)
|
||||
return c.json({ success: true })
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err)
|
||||
return c.json({ error: message }, 500)
|
||||
}
|
||||
})
|
||||
|
||||
.get('/agents/:id/program-runs', async (c) => {
|
||||
try {
|
||||
const agent = await findOpenClawAgent(c.req.param('id'))
|
||||
if (!agent) {
|
||||
return c.json({ error: 'Agent not found' }, 404)
|
||||
}
|
||||
|
||||
const runs = await openclawProgramStorage.listRuns(agent.name)
|
||||
return c.json({ runs })
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err)
|
||||
return c.json({ error: message }, 500)
|
||||
}
|
||||
})
|
||||
|
||||
.get('/roles', async (c) => {
|
||||
return c.json({
|
||||
roles: BROWSEROS_ROLE_TEMPLATES.map((role) => ({
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
import { mkdir, writeFile } from 'node:fs/promises'
|
||||
import { join } from 'node:path'
|
||||
import type { BrowserOSAgentProgram } from '@browseros/shared/types/role-programs'
|
||||
import type { OpenClawProgramStorage } from './program-storage'
|
||||
|
||||
function describeSchedule(program: BrowserOSAgentProgram): string {
|
||||
switch (program.schedule.type) {
|
||||
case 'manual':
|
||||
return 'manual only'
|
||||
case 'daily': {
|
||||
const weekdaySummary = program.schedule.daysOfWeek?.length
|
||||
? ` on ${program.schedule.daysOfWeek.join(', ')}`
|
||||
: ''
|
||||
return `daily at ${program.schedule.time}${weekdaySummary}`
|
||||
}
|
||||
case 'hourly':
|
||||
return `every ${program.schedule.interval} hour(s)`
|
||||
case 'minutes':
|
||||
return `every ${program.schedule.interval} minute(s)`
|
||||
}
|
||||
}
|
||||
|
||||
function buildProgramsMd(programs: BrowserOSAgentProgram[]): string {
|
||||
const sections =
|
||||
programs.length === 0
|
||||
? ['No BrowserOS-managed programs configured yet.']
|
||||
: programs.map(
|
||||
(program) => `## ${program.name}
|
||||
- Status: ${program.enabled ? 'enabled' : 'disabled'}
|
||||
- Schedule: ${describeSchedule(program)}
|
||||
- Goal: ${program.description}
|
||||
- Prompt: ${program.prompt}
|
||||
`,
|
||||
)
|
||||
|
||||
return `# BrowserOS Programs
|
||||
|
||||
This file is generated by BrowserOS. Edit program settings in BrowserOS, not here.
|
||||
|
||||
${sections.join('\n')}
|
||||
`
|
||||
}
|
||||
|
||||
function buildStandingOrdersMd(programs: BrowserOSAgentProgram[]): string {
|
||||
const sections = programs.flatMap((program) => {
|
||||
if (program.standingOrders.length === 0) return []
|
||||
const lines = program.standingOrders
|
||||
.map(
|
||||
(order) =>
|
||||
`- ${order.title} (${order.enabled ? 'enabled' : 'disabled'}): ${order.instruction}`,
|
||||
)
|
||||
.join('\n')
|
||||
|
||||
return [`## ${program.name}\n${lines}`]
|
||||
})
|
||||
|
||||
return `# Standing Orders
|
||||
|
||||
This file is generated by BrowserOS. Edit standing orders in BrowserOS, not here.
|
||||
|
||||
${sections.length > 0 ? sections.join('\n\n') : 'No standing orders configured yet.'}
|
||||
`
|
||||
}
|
||||
|
||||
function buildProgramsMetadata(
|
||||
agentName: string,
|
||||
programs: BrowserOSAgentProgram[],
|
||||
): string {
|
||||
return `${JSON.stringify(
|
||||
{
|
||||
version: 1,
|
||||
agentName,
|
||||
programs: programs.map((program) => ({
|
||||
id: program.id,
|
||||
name: program.name,
|
||||
enabled: program.enabled,
|
||||
schedule: program.schedule,
|
||||
updatedAt: program.updatedAt,
|
||||
})),
|
||||
},
|
||||
null,
|
||||
2,
|
||||
)}\n`
|
||||
}
|
||||
|
||||
export class OpenClawProgramMaterializer {
|
||||
constructor(
|
||||
private openclawDir: string,
|
||||
private storage: OpenClawProgramStorage,
|
||||
) {}
|
||||
|
||||
private getHostWorkspaceDir(agentName: string): string {
|
||||
return join(
|
||||
this.openclawDir,
|
||||
agentName === 'main' ? 'workspace' : `workspace-${agentName}`,
|
||||
)
|
||||
}
|
||||
|
||||
async syncAgentPrograms(agentName: string): Promise<void> {
|
||||
const programs = await this.storage.listPrograms(agentName)
|
||||
const workspaceDir = this.getHostWorkspaceDir(agentName)
|
||||
await mkdir(workspaceDir, { recursive: true })
|
||||
|
||||
await Promise.all([
|
||||
writeFile(join(workspaceDir, 'PROGRAMS.md'), buildProgramsMd(programs)),
|
||||
writeFile(
|
||||
join(workspaceDir, 'STANDING-ORDERS.md'),
|
||||
buildStandingOrdersMd(programs),
|
||||
),
|
||||
writeFile(
|
||||
join(workspaceDir, '.browseros-programs.json'),
|
||||
buildProgramsMetadata(agentName, programs),
|
||||
),
|
||||
])
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
import { mkdir, readFile, writeFile } from 'node:fs/promises'
|
||||
import { dirname, join } from 'node:path'
|
||||
import type {
|
||||
BrowserOSAgentProgram,
|
||||
BrowserOSProgramRun,
|
||||
CreateAgentProgramInput,
|
||||
UpdateAgentProgramInput,
|
||||
} from '@browseros/shared/types/role-programs'
|
||||
|
||||
interface ProgramStorageAgent {
|
||||
agentId: string
|
||||
name: string
|
||||
role?: {
|
||||
roleId?: string
|
||||
}
|
||||
}
|
||||
|
||||
async function readJsonFile<T>(filePath: string, fallback: T): Promise<T> {
|
||||
try {
|
||||
const content = await readFile(filePath, 'utf-8')
|
||||
return JSON.parse(content) as T
|
||||
} catch {
|
||||
return fallback
|
||||
}
|
||||
}
|
||||
|
||||
async function writeJsonFile(filePath: string, value: unknown): Promise<void> {
|
||||
await mkdir(dirname(filePath), { recursive: true })
|
||||
await writeFile(filePath, `${JSON.stringify(value, null, 2)}\n`)
|
||||
}
|
||||
|
||||
function sortPrograms(programs: BrowserOSAgentProgram[]) {
|
||||
return [...programs].sort((left, right) =>
|
||||
left.createdAt.localeCompare(right.createdAt),
|
||||
)
|
||||
}
|
||||
|
||||
export class OpenClawProgramStorage {
|
||||
constructor(private openclawDir: string) {}
|
||||
|
||||
private getProgramsFile(agentName: string): string {
|
||||
return join(this.openclawDir, 'programs', `${agentName}.json`)
|
||||
}
|
||||
|
||||
private getProgramRunsFile(agentName: string): string {
|
||||
return join(this.openclawDir, 'program-runs', `${agentName}.json`)
|
||||
}
|
||||
|
||||
async listPrograms(agentName: string): Promise<BrowserOSAgentProgram[]> {
|
||||
const programs = await readJsonFile<BrowserOSAgentProgram[]>(
|
||||
this.getProgramsFile(agentName),
|
||||
[],
|
||||
)
|
||||
return sortPrograms(programs)
|
||||
}
|
||||
|
||||
async getProgram(
|
||||
agentName: string,
|
||||
programId: string,
|
||||
): Promise<BrowserOSAgentProgram | null> {
|
||||
const programs = await this.listPrograms(agentName)
|
||||
return programs.find((program) => program.id === programId) ?? null
|
||||
}
|
||||
|
||||
async createProgram(
|
||||
agent: ProgramStorageAgent,
|
||||
input: CreateAgentProgramInput,
|
||||
): Promise<BrowserOSAgentProgram> {
|
||||
const programs = await this.listPrograms(agent.name)
|
||||
const now = new Date().toISOString()
|
||||
|
||||
const program: BrowserOSAgentProgram = {
|
||||
id: crypto.randomUUID(),
|
||||
agentId: agent.agentId,
|
||||
agentName: agent.name,
|
||||
roleId: agent.role?.roleId,
|
||||
name: input.name,
|
||||
description: input.description,
|
||||
prompt: input.prompt,
|
||||
schedule: input.schedule,
|
||||
enabled: input.enabled ?? true,
|
||||
standingOrders: input.standingOrders ?? [],
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
}
|
||||
|
||||
await writeJsonFile(this.getProgramsFile(agent.name), [
|
||||
...programs,
|
||||
program,
|
||||
])
|
||||
return program
|
||||
}
|
||||
|
||||
async updateProgram(
|
||||
agentName: string,
|
||||
programId: string,
|
||||
input: UpdateAgentProgramInput,
|
||||
): Promise<BrowserOSAgentProgram | null> {
|
||||
const programs = await this.listPrograms(agentName)
|
||||
const current = programs.find((program) => program.id === programId)
|
||||
if (!current) return null
|
||||
|
||||
const nextProgram: BrowserOSAgentProgram = {
|
||||
...current,
|
||||
...input,
|
||||
updatedAt: new Date().toISOString(),
|
||||
}
|
||||
|
||||
await writeJsonFile(
|
||||
this.getProgramsFile(agentName),
|
||||
programs.map((program) =>
|
||||
program.id === programId ? nextProgram : program,
|
||||
),
|
||||
)
|
||||
return nextProgram
|
||||
}
|
||||
|
||||
async deleteProgram(agentName: string, programId: string): Promise<boolean> {
|
||||
const programs = await this.listPrograms(agentName)
|
||||
const remaining = programs.filter((program) => program.id !== programId)
|
||||
if (remaining.length === programs.length) return false
|
||||
|
||||
await writeJsonFile(this.getProgramsFile(agentName), remaining)
|
||||
return true
|
||||
}
|
||||
|
||||
async listRuns(agentName: string): Promise<BrowserOSProgramRun[]> {
|
||||
return readJsonFile<BrowserOSProgramRun[]>(
|
||||
this.getProgramRunsFile(agentName),
|
||||
[],
|
||||
)
|
||||
}
|
||||
|
||||
async writeRuns(
|
||||
agentName: string,
|
||||
runs: BrowserOSProgramRun[],
|
||||
): Promise<void> {
|
||||
await writeJsonFile(this.getProgramRunsFile(agentName), runs)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
import type {
|
||||
BrowserOSProgramSchedule,
|
||||
BrowserOSStandingOrder,
|
||||
CreateAgentProgramInput,
|
||||
UpdateAgentProgramInput,
|
||||
} from '@browseros/shared/types/role-programs'
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return !!value && typeof value === 'object' && !Array.isArray(value)
|
||||
}
|
||||
|
||||
function assertNonEmptyString(
|
||||
value: unknown,
|
||||
field: string,
|
||||
): asserts value is string {
|
||||
if (typeof value !== 'string' || value.trim() === '') {
|
||||
throw new Error(`${field} is required`)
|
||||
}
|
||||
}
|
||||
|
||||
function validateStandingOrder(value: unknown): BrowserOSStandingOrder {
|
||||
if (!isRecord(value)) {
|
||||
throw new Error('Standing orders must be objects')
|
||||
}
|
||||
|
||||
assertNonEmptyString(value.title, 'Standing order title')
|
||||
assertNonEmptyString(value.instruction, 'Standing order instruction')
|
||||
|
||||
if (typeof value.enabled !== 'boolean') {
|
||||
throw new Error('Standing order enabled must be a boolean')
|
||||
}
|
||||
|
||||
return {
|
||||
id:
|
||||
typeof value.id === 'string' && value.id.trim() !== ''
|
||||
? value.id
|
||||
: crypto.randomUUID(),
|
||||
title: value.title.trim(),
|
||||
instruction: value.instruction.trim(),
|
||||
enabled: value.enabled,
|
||||
}
|
||||
}
|
||||
|
||||
function validateStandingOrders(
|
||||
value: unknown,
|
||||
): BrowserOSStandingOrder[] | undefined {
|
||||
if (value === undefined) return undefined
|
||||
if (!Array.isArray(value)) {
|
||||
throw new Error('standingOrders must be an array')
|
||||
}
|
||||
|
||||
return value.map(validateStandingOrder)
|
||||
}
|
||||
|
||||
function isValidTime(value: string): boolean {
|
||||
return /^([01]\d|2[0-3]):[0-5]\d$/.test(value)
|
||||
}
|
||||
|
||||
function validateDaysOfWeek(value: unknown): Array<0 | 1 | 2 | 3 | 4 | 5 | 6> {
|
||||
if (!Array.isArray(value)) {
|
||||
throw new Error('schedule.daysOfWeek must be an array')
|
||||
}
|
||||
|
||||
return value.map((day) => {
|
||||
if (
|
||||
typeof day !== 'number' ||
|
||||
!Number.isInteger(day) ||
|
||||
day < 0 ||
|
||||
day > 6
|
||||
) {
|
||||
throw new Error('schedule.daysOfWeek must contain values from 0 to 6')
|
||||
}
|
||||
return day as 0 | 1 | 2 | 3 | 4 | 5 | 6
|
||||
})
|
||||
}
|
||||
|
||||
function validateSchedule(value: unknown): BrowserOSProgramSchedule {
|
||||
if (!isRecord(value) || typeof value.type !== 'string') {
|
||||
throw new Error('schedule is required')
|
||||
}
|
||||
|
||||
switch (value.type) {
|
||||
case 'manual':
|
||||
return { type: 'manual' }
|
||||
case 'daily': {
|
||||
assertNonEmptyString(value.time, 'schedule.time')
|
||||
if (!isValidTime(value.time)) {
|
||||
throw new Error('schedule.time must be in HH:MM format')
|
||||
}
|
||||
return {
|
||||
type: 'daily',
|
||||
time: value.time,
|
||||
daysOfWeek:
|
||||
value.daysOfWeek === undefined
|
||||
? undefined
|
||||
: validateDaysOfWeek(value.daysOfWeek),
|
||||
}
|
||||
}
|
||||
case 'hourly':
|
||||
case 'minutes': {
|
||||
if (
|
||||
typeof value.interval !== 'number' ||
|
||||
!Number.isInteger(value.interval) ||
|
||||
value.interval < 1
|
||||
) {
|
||||
throw new Error('schedule.interval must be an integer >= 1')
|
||||
}
|
||||
|
||||
return {
|
||||
type: value.type,
|
||||
interval: value.interval,
|
||||
}
|
||||
}
|
||||
default:
|
||||
throw new Error('schedule.type is invalid')
|
||||
}
|
||||
}
|
||||
|
||||
export function validateCreateProgramInput(
|
||||
value: unknown,
|
||||
): CreateAgentProgramInput {
|
||||
if (!isRecord(value)) {
|
||||
throw new Error('Program payload must be an object')
|
||||
}
|
||||
|
||||
assertNonEmptyString(value.name, 'name')
|
||||
assertNonEmptyString(value.description, 'description')
|
||||
assertNonEmptyString(value.prompt, 'prompt')
|
||||
|
||||
return {
|
||||
name: value.name.trim(),
|
||||
description: value.description.trim(),
|
||||
prompt: value.prompt.trim(),
|
||||
schedule: validateSchedule(value.schedule),
|
||||
enabled: value.enabled === undefined ? true : !!value.enabled,
|
||||
standingOrders: validateStandingOrders(value.standingOrders) ?? [],
|
||||
}
|
||||
}
|
||||
|
||||
export function validateUpdateProgramInput(
|
||||
value: unknown,
|
||||
): UpdateAgentProgramInput {
|
||||
if (!isRecord(value)) {
|
||||
throw new Error('Program payload must be an object')
|
||||
}
|
||||
|
||||
const output: UpdateAgentProgramInput = {}
|
||||
|
||||
if (value.name !== undefined) {
|
||||
assertNonEmptyString(value.name, 'name')
|
||||
output.name = value.name.trim()
|
||||
}
|
||||
if (value.description !== undefined) {
|
||||
assertNonEmptyString(value.description, 'description')
|
||||
output.description = value.description.trim()
|
||||
}
|
||||
if (value.prompt !== undefined) {
|
||||
assertNonEmptyString(value.prompt, 'prompt')
|
||||
output.prompt = value.prompt.trim()
|
||||
}
|
||||
if (value.enabled !== undefined) {
|
||||
if (typeof value.enabled !== 'boolean') {
|
||||
throw new Error('enabled must be a boolean')
|
||||
}
|
||||
output.enabled = value.enabled
|
||||
}
|
||||
if (value.schedule !== undefined) {
|
||||
output.schedule = validateSchedule(value.schedule)
|
||||
}
|
||||
if (value.standingOrders !== undefined) {
|
||||
output.standingOrders = validateStandingOrders(value.standingOrders)
|
||||
}
|
||||
|
||||
if (Object.keys(output).length === 0) {
|
||||
throw new Error('At least one program field must be provided')
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
@@ -57,6 +57,10 @@
|
||||
"types": "./src/types/role-aware-agents.ts",
|
||||
"default": "./src/types/role-aware-agents.ts"
|
||||
},
|
||||
"./types/role-programs": {
|
||||
"types": "./src/types/role-programs.ts",
|
||||
"default": "./src/types/role-programs.ts"
|
||||
},
|
||||
"./schemas/llm": {
|
||||
"types": "./src/schemas/llm.ts",
|
||||
"default": "./src/schemas/llm.ts"
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
export type BrowserOSProgramSchedule =
|
||||
| {
|
||||
type: 'daily'
|
||||
time: string
|
||||
daysOfWeek?: Array<0 | 1 | 2 | 3 | 4 | 5 | 6>
|
||||
}
|
||||
| {
|
||||
type: 'hourly'
|
||||
interval: number
|
||||
}
|
||||
| {
|
||||
type: 'minutes'
|
||||
interval: number
|
||||
}
|
||||
| {
|
||||
type: 'manual'
|
||||
}
|
||||
|
||||
export interface BrowserOSStandingOrder {
|
||||
id: string
|
||||
title: string
|
||||
instruction: string
|
||||
enabled: boolean
|
||||
}
|
||||
|
||||
export interface BrowserOSAgentProgram {
|
||||
id: string
|
||||
agentId: string
|
||||
agentName: string
|
||||
roleId?: string
|
||||
name: string
|
||||
description: string
|
||||
prompt: string
|
||||
schedule: BrowserOSProgramSchedule
|
||||
enabled: boolean
|
||||
standingOrders: BrowserOSStandingOrder[]
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
lastRunAt?: string
|
||||
}
|
||||
|
||||
export interface BrowserOSProgramRun {
|
||||
id: string
|
||||
programId: string
|
||||
agentId: string
|
||||
startedAt: string
|
||||
completedAt?: string
|
||||
status: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled'
|
||||
trigger: 'manual' | 'schedule' | 'retry'
|
||||
summary?: string
|
||||
finalResult?: string
|
||||
error?: string
|
||||
sessionKey?: string
|
||||
}
|
||||
|
||||
export interface CreateAgentProgramInput {
|
||||
name: string
|
||||
description: string
|
||||
prompt: string
|
||||
schedule: BrowserOSProgramSchedule
|
||||
enabled?: boolean
|
||||
standingOrders?: BrowserOSStandingOrder[]
|
||||
}
|
||||
|
||||
export interface UpdateAgentProgramInput {
|
||||
name?: string
|
||||
description?: string
|
||||
prompt?: string
|
||||
schedule?: BrowserOSProgramSchedule
|
||||
enabled?: boolean
|
||||
standingOrders?: BrowserOSStandingOrder[]
|
||||
}
|
||||
Reference in New Issue
Block a user