mirror of
https://github.com/stablyai/orca.git
synced 2026-10-29 16:17:51 +00:00
Show automation source context in details
Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
@@ -14,6 +14,7 @@ import {
|
||||
summarizeAutomationRunUsage
|
||||
} from './automation-usage-model'
|
||||
import type { AutomationTargetAvailability } from './automation-target-availability'
|
||||
import { getAutomationSourceDisplay } from './automation-source-display'
|
||||
import { translate } from '@/i18n/i18n'
|
||||
|
||||
type AutomationDetailProps = {
|
||||
@@ -30,11 +31,21 @@ type AutomationDetailProps = {
|
||||
onDelete: (automation: Automation) => void
|
||||
}
|
||||
|
||||
function DetailMetric({ label, value }: { label: string; value: string }): React.JSX.Element {
|
||||
function DetailMetric({
|
||||
label,
|
||||
value,
|
||||
title
|
||||
}: {
|
||||
label: string
|
||||
value: string
|
||||
title?: string
|
||||
}): React.JSX.Element {
|
||||
return (
|
||||
<div className="min-w-0">
|
||||
<div className="text-[11px] font-medium uppercase text-muted-foreground">{label}</div>
|
||||
<div className="mt-1 break-words text-sm font-medium">{value}</div>
|
||||
<div className="mt-1 break-words text-sm font-medium" title={title}>
|
||||
{value}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -118,6 +129,7 @@ export function AutomationDetail({
|
||||
automation.workspaceMode === 'new_per_run'
|
||||
? (automation.baseBranch ?? projectDefaultBaseRef ?? 'Project default')
|
||||
: workspaceName
|
||||
const sourceDisplay = getAutomationSourceDisplay(automation.sourceContext)
|
||||
const runNowDisabled = runNowAvailability?.canRunNow === false
|
||||
|
||||
return (
|
||||
@@ -235,6 +247,13 @@ export function AutomationDetail({
|
||||
label={translate('auto.components.automations.AutomationDetail.15ea446b93', 'Session')}
|
||||
value={automation.reuseSession ? 'Reuse live session' : 'Fresh each run'}
|
||||
/>
|
||||
{sourceDisplay ? (
|
||||
<DetailMetric
|
||||
label={translate('auto.components.automations.AutomationDetail.29baf8f4c2', 'Source')}
|
||||
value={sourceDisplay.label}
|
||||
title={sourceDisplay.title}
|
||||
/>
|
||||
) : null}
|
||||
<DetailMetric
|
||||
label={translate('auto.components.automations.AutomationDetail.620b22145e', 'Grace')}
|
||||
value={formatGrace(automation.missedRunGraceMinutes)}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import type { TaskSourceContext } from '../../../../shared/task-source-context'
|
||||
import { getAutomationSourceDisplay } from './automation-source-display'
|
||||
|
||||
describe('automation source display', () => {
|
||||
it('summarizes repo-backed source context separately from run location', () => {
|
||||
const sourceContext: TaskSourceContext = {
|
||||
kind: 'task-source',
|
||||
provider: 'github',
|
||||
hostId: 'ssh:devbox',
|
||||
projectId: 'github:stablyai/orca',
|
||||
projectHostSetupId: 'setup-devbox',
|
||||
repoId: 'repo-devbox',
|
||||
accountLabel: 'dev@example.com',
|
||||
providerIdentity: {
|
||||
provider: 'github',
|
||||
owner: 'stablyai',
|
||||
repo: 'orca'
|
||||
}
|
||||
}
|
||||
|
||||
expect(getAutomationSourceDisplay(sourceContext)).toEqual({
|
||||
label: 'GitHub · devbox · stablyai/orca',
|
||||
title: 'GitHub source · Host: devbox · Account: dev@example.com · Source: stablyai/orca'
|
||||
})
|
||||
})
|
||||
|
||||
it('uses account identity for Linear sources', () => {
|
||||
const sourceContext: TaskSourceContext = {
|
||||
kind: 'task-source',
|
||||
provider: 'linear',
|
||||
hostId: 'local',
|
||||
projectId: 'repo-1',
|
||||
projectHostSetupId: 'setup-local',
|
||||
repoId: 'repo-1',
|
||||
accountLabel: 'Linear API key',
|
||||
providerIdentity: {
|
||||
provider: 'linear',
|
||||
workspaceId: 'legacy',
|
||||
workspaceName: 'Saved Linear workspace'
|
||||
}
|
||||
}
|
||||
|
||||
expect(getAutomationSourceDisplay(sourceContext)).toEqual({
|
||||
label: 'Linear · Local Mac · Saved Linear workspace',
|
||||
title:
|
||||
'Linear source · Host: Local Mac · Account: Linear API key · Source: Saved Linear workspace'
|
||||
})
|
||||
})
|
||||
|
||||
it('returns null when no source context is saved', () => {
|
||||
expect(getAutomationSourceDisplay(null)).toBeNull()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,62 @@
|
||||
import { getExecutionHostLabel } from '../../../../shared/execution-host'
|
||||
import type { TaskSourceContext } from '../../../../shared/task-source-context'
|
||||
|
||||
export type AutomationSourceDisplay = {
|
||||
label: string
|
||||
title: string
|
||||
}
|
||||
|
||||
export function getAutomationSourceDisplay(
|
||||
sourceContext: TaskSourceContext | null | undefined
|
||||
): AutomationSourceDisplay | null {
|
||||
if (!sourceContext) {
|
||||
return null
|
||||
}
|
||||
const providerLabel = getProviderLabel(sourceContext.provider)
|
||||
const hostLabel = getExecutionHostLabel(sourceContext.hostId)
|
||||
const identityLabel = getSourceIdentityLabel(sourceContext)
|
||||
const label = [providerLabel, hostLabel, identityLabel]
|
||||
.filter((part): part is string => Boolean(part))
|
||||
.join(' · ')
|
||||
const title = [
|
||||
`${providerLabel} source`,
|
||||
`Host: ${hostLabel}`,
|
||||
sourceContext.accountLabel ? `Account: ${sourceContext.accountLabel}` : null,
|
||||
identityLabel ? `Source: ${identityLabel}` : null
|
||||
]
|
||||
.filter((part): part is string => Boolean(part))
|
||||
.join(' · ')
|
||||
return { label, title }
|
||||
}
|
||||
|
||||
function getProviderLabel(provider: TaskSourceContext['provider']): string {
|
||||
switch (provider) {
|
||||
case 'github':
|
||||
return 'GitHub'
|
||||
case 'gitlab':
|
||||
return 'GitLab'
|
||||
case 'linear':
|
||||
return 'Linear'
|
||||
case 'jira':
|
||||
return 'Jira'
|
||||
}
|
||||
}
|
||||
|
||||
function getSourceIdentityLabel(sourceContext: TaskSourceContext): string | null {
|
||||
const identity = sourceContext.providerIdentity
|
||||
if (identity) {
|
||||
switch (identity.provider) {
|
||||
case 'github':
|
||||
return `${identity.owner}/${identity.repo}`
|
||||
case 'gitlab':
|
||||
return identity.namespace && identity.project
|
||||
? `${identity.namespace}/${identity.project}`
|
||||
: (identity.projectId ?? null)
|
||||
case 'linear':
|
||||
return identity.workspaceName ?? identity.workspaceId ?? null
|
||||
case 'jira':
|
||||
return identity.siteUrl ?? identity.siteId ?? null
|
||||
}
|
||||
}
|
||||
return sourceContext.accountLabel ?? sourceContext.repoId ?? null
|
||||
}
|
||||
Reference in New Issue
Block a user