@cat-factory/app 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +88 -0
- package/app/app.config.ts +8 -0
- package/app/app.vue +11 -0
- package/app/assets/css/main.css +100 -0
- package/app/components/auth/AuthGate.vue +24 -0
- package/app/components/auth/LoginScreen.vue +143 -0
- package/app/components/auth/UserMenu.vue +39 -0
- package/app/components/board/AddTaskModal.vue +444 -0
- package/app/components/board/AgentFailureCard.vue +97 -0
- package/app/components/board/AgentStopButton.vue +61 -0
- package/app/components/board/BoardCanvas.vue +183 -0
- package/app/components/board/ContextPicker.vue +367 -0
- package/app/components/board/RecurringPipelineModal.vue +219 -0
- package/app/components/board/TaskDependencyEdges.vue +132 -0
- package/app/components/board/nodes/AgentChip.vue +59 -0
- package/app/components/board/nodes/BlockNode.vue +433 -0
- package/app/components/board/nodes/DecisionBadge.vue +27 -0
- package/app/components/board/nodes/DraggableTask.vue +48 -0
- package/app/components/board/nodes/ModuleFrame.vue +97 -0
- package/app/components/board/nodes/TaskCard.vue +359 -0
- package/app/components/board/nodes/TaskPipelineMini.vue +159 -0
- package/app/components/bootstrap/BootstrapModal.vue +665 -0
- package/app/components/clarity/ClarityReviewWindow.vue +611 -0
- package/app/components/consensus/ConsensusSessionWindow.vue +210 -0
- package/app/components/documents/DocumentImportModal.vue +161 -0
- package/app/components/documents/DocumentSourceConnectModal.vue +127 -0
- package/app/components/documents/SpawnPreviewModal.vue +161 -0
- package/app/components/documents/TaskContextDocs.vue +83 -0
- package/app/components/focus/BlockFocusView.vue +171 -0
- package/app/components/fragments/FragmentLibraryPanel.vue +340 -0
- package/app/components/gates/GateResultView.vue +282 -0
- package/app/components/github/AddServiceFromRepoModal.vue +354 -0
- package/app/components/github/GitHubConnect.vue +183 -0
- package/app/components/github/GitHubOnboarding.vue +45 -0
- package/app/components/github/GitHubPanel.vue +584 -0
- package/app/components/github/RepoTreeBrowser.vue +171 -0
- package/app/components/layout/AccountTeamSettings.vue +237 -0
- package/app/components/layout/BoardSwitcher.vue +280 -0
- package/app/components/layout/BoardToolbar.vue +156 -0
- package/app/components/layout/CommandBar.vue +336 -0
- package/app/components/layout/GitHubPatBanner.vue +73 -0
- package/app/components/layout/NotificationsInbox.vue +175 -0
- package/app/components/layout/SideBar.vue +314 -0
- package/app/components/layout/SpendWarningBanner.vue +107 -0
- package/app/components/observability/StepMetricsBar.vue +102 -0
- package/app/components/palettes/AgentPalette.vue +86 -0
- package/app/components/panels/AgentStepDetail.vue +737 -0
- package/app/components/panels/DecisionModal.vue +71 -0
- package/app/components/panels/InspectorPanel.vue +465 -0
- package/app/components/panels/ObservabilityPanel.vue +351 -0
- package/app/components/panels/StepMetadataCard.vue +253 -0
- package/app/components/panels/StepRestartControl.vue +90 -0
- package/app/components/panels/StepResultViewHost.vue +40 -0
- package/app/components/panels/StepTestReport.vue +84 -0
- package/app/components/panels/inspector/ContainerSummary.vue +74 -0
- package/app/components/panels/inspector/RecurringScheduleSettings.vue +178 -0
- package/app/components/panels/inspector/ServiceFragments.vue +82 -0
- package/app/components/panels/inspector/ServiceTestConfig.vue +198 -0
- package/app/components/panels/inspector/TaskAgentConfig.vue +81 -0
- package/app/components/panels/inspector/TaskDependencies.vue +70 -0
- package/app/components/panels/inspector/TaskEstimateBadge.vue +56 -0
- package/app/components/panels/inspector/TaskExecution.vue +364 -0
- package/app/components/panels/inspector/TaskRunSettings.vue +187 -0
- package/app/components/panels/inspector/TaskStructure.vue +96 -0
- package/app/components/pipeline/AgentKindIcon.vue +30 -0
- package/app/components/pipeline/IterationCapPrompt.vue +70 -0
- package/app/components/pipeline/PipelineBuilder.vue +817 -0
- package/app/components/pipeline/PipelineProgress.vue +484 -0
- package/app/components/providers/ApiKeysSection.vue +273 -0
- package/app/components/providers/PersonalCredentialModal.vue +128 -0
- package/app/components/providers/PersonalSubscriptionSection.vue +225 -0
- package/app/components/providers/VendorCredentialsModal.vue +197 -0
- package/app/components/recurring/RecurrenceEditor.vue +124 -0
- package/app/components/requirements/RequirementsReviewWindow.vue +620 -0
- package/app/components/settings/DatadogPanel.vue +213 -0
- package/app/components/settings/LocalModelEndpointsPanel.vue +286 -0
- package/app/components/settings/MergeThresholdsPanel.vue +378 -0
- package/app/components/settings/ModelDefaultsPanel.vue +250 -0
- package/app/components/settings/ServiceFragmentDefaultsPanel.vue +124 -0
- package/app/components/settings/WorkspaceSettingsPanel.vue +142 -0
- package/app/components/slack/SlackPanel.vue +299 -0
- package/app/components/tasks/TaskContextIssues.vue +88 -0
- package/app/components/tasks/TaskImportModal.vue +207 -0
- package/app/components/tasks/TaskSourceConnectModal.vue +133 -0
- package/app/components/testing/TestReportWindow.vue +404 -0
- package/app/composables/api/accounts.ts +81 -0
- package/app/composables/api/auth.ts +45 -0
- package/app/composables/api/board.ts +101 -0
- package/app/composables/api/bootstrap.ts +62 -0
- package/app/composables/api/context.ts +25 -0
- package/app/composables/api/documents.ts +74 -0
- package/app/composables/api/execution.ts +127 -0
- package/app/composables/api/fragments.ts +71 -0
- package/app/composables/api/github.ts +131 -0
- package/app/composables/api/models.ts +127 -0
- package/app/composables/api/notifications.ts +23 -0
- package/app/composables/api/presets.ts +29 -0
- package/app/composables/api/recurring.ts +68 -0
- package/app/composables/api/releaseHealth.ts +43 -0
- package/app/composables/api/reviews.ts +146 -0
- package/app/composables/api/slack.ts +54 -0
- package/app/composables/api/tasks.ts +72 -0
- package/app/composables/api/workspaces.ts +36 -0
- package/app/composables/useApi.ts +89 -0
- package/app/composables/useBlockDrag.ts +90 -0
- package/app/composables/useBlockQueries.ts +154 -0
- package/app/composables/useBoardFlow.ts +11 -0
- package/app/composables/useContextLinking.ts +65 -0
- package/app/composables/useDepLabels.ts +26 -0
- package/app/composables/useFrameResize.ts +54 -0
- package/app/composables/useResultView.ts +48 -0
- package/app/composables/useReviewStage.ts +40 -0
- package/app/composables/useSemanticZoom.ts +31 -0
- package/app/composables/useStepApproval.ts +233 -0
- package/app/composables/useStepProse.ts +78 -0
- package/app/composables/useStepTimer.ts +63 -0
- package/app/composables/useTaskExpansion.ts +92 -0
- package/app/composables/useWorkspaceStream.ts +155 -0
- package/app/docs/architecture.md +31 -0
- package/app/pages/index.vue +141 -0
- package/app/stores/accounts.ts +152 -0
- package/app/stores/agentConfig.ts +35 -0
- package/app/stores/agentRuns.ts +122 -0
- package/app/stores/agents.ts +40 -0
- package/app/stores/apiKeys.ts +108 -0
- package/app/stores/auth.ts +166 -0
- package/app/stores/board.spec.ts +205 -0
- package/app/stores/board.ts +286 -0
- package/app/stores/bootstrap.ts +97 -0
- package/app/stores/clarity.ts +196 -0
- package/app/stores/consensus.ts +60 -0
- package/app/stores/documents.ts +176 -0
- package/app/stores/execution.ts +273 -0
- package/app/stores/fragmentLibrary.ts +147 -0
- package/app/stores/fragments.ts +40 -0
- package/app/stores/github.ts +305 -0
- package/app/stores/localModels.ts +51 -0
- package/app/stores/mergePresets.ts +58 -0
- package/app/stores/modelDefaults.ts +76 -0
- package/app/stores/models.ts +134 -0
- package/app/stores/notifications.ts +70 -0
- package/app/stores/observability.ts +144 -0
- package/app/stores/personalSubscriptions.ts +215 -0
- package/app/stores/pipelines.ts +327 -0
- package/app/stores/recurringPipelines.ts +112 -0
- package/app/stores/releaseHealth.ts +75 -0
- package/app/stores/requirements.spec.ts +94 -0
- package/app/stores/requirements.ts +208 -0
- package/app/stores/serviceFragmentDefaults.ts +29 -0
- package/app/stores/services.ts +87 -0
- package/app/stores/slack.ts +142 -0
- package/app/stores/taskExpansion.ts +36 -0
- package/app/stores/tasks.spec.ts +71 -0
- package/app/stores/tasks.ts +176 -0
- package/app/stores/tracker.ts +27 -0
- package/app/stores/ui.ts +434 -0
- package/app/stores/vendorCredentials.ts +54 -0
- package/app/stores/workspace.ts +215 -0
- package/app/stores/workspaceSettings.ts +36 -0
- package/app/types/accounts.ts +77 -0
- package/app/types/bootstrap.ts +83 -0
- package/app/types/clarity.ts +59 -0
- package/app/types/consensus.ts +91 -0
- package/app/types/documents.ts +104 -0
- package/app/types/domain.ts +495 -0
- package/app/types/execution.ts +383 -0
- package/app/types/fragments.ts +72 -0
- package/app/types/github.ts +173 -0
- package/app/types/localModels.ts +73 -0
- package/app/types/merge.ts +71 -0
- package/app/types/models.ts +157 -0
- package/app/types/notifications.ts +74 -0
- package/app/types/recurring.ts +69 -0
- package/app/types/releaseHealth.ts +31 -0
- package/app/types/requirements.ts +61 -0
- package/app/types/services.ts +27 -0
- package/app/types/slack.ts +57 -0
- package/app/types/tasks.ts +82 -0
- package/app/types/tracker.ts +18 -0
- package/app/utils/agentOutput.spec.ts +128 -0
- package/app/utils/agentOutput.ts +173 -0
- package/app/utils/catalog.spec.ts +112 -0
- package/app/utils/catalog.ts +455 -0
- package/app/utils/dnd.ts +29 -0
- package/app/utils/observability.ts +52 -0
- package/app/utils/pipelineRender.ts +151 -0
- package/nuxt.config.ts +55 -0
- package/package.json +45 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// Import an issue from a connected task source (by key or URL) and review the
|
|
3
|
+
// issues already imported into the workspace. An imported issue can be attached
|
|
4
|
+
// to an existing task for context from the inspector (see TaskContextIssues.vue),
|
|
5
|
+
// or turned directly into a new board task here — pick a container (service frame
|
|
6
|
+
// or module) and "Create task", which seeds a leaf block from the issue and links
|
|
7
|
+
// the issue to it for context.
|
|
8
|
+
import type { Block, TaskSourceKind } from '~/types/domain'
|
|
9
|
+
|
|
10
|
+
const ui = useUiStore()
|
|
11
|
+
const tasks = useTasksStore()
|
|
12
|
+
const board = useBoardStore()
|
|
13
|
+
const toast = useToast()
|
|
14
|
+
|
|
15
|
+
const open = computed({
|
|
16
|
+
get: () => ui.taskImport !== null,
|
|
17
|
+
set: (v: boolean) => {
|
|
18
|
+
if (!v) ui.closeTaskImport()
|
|
19
|
+
},
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
const source = ref<TaskSourceKind | undefined>(undefined)
|
|
23
|
+
const ref_ = ref('')
|
|
24
|
+
const importing = ref(false)
|
|
25
|
+
|
|
26
|
+
const sourceItems = computed(() =>
|
|
27
|
+
tasks.connectedSources.map((s) => ({ label: s.label, value: s.source })),
|
|
28
|
+
)
|
|
29
|
+
const descriptor = computed(() => (source.value ? tasks.descriptorFor(source.value) : undefined))
|
|
30
|
+
|
|
31
|
+
const sourceTasks = computed(() =>
|
|
32
|
+
source.value ? tasks.tasks.filter((t) => t.source === source.value) : [],
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
// Containers a new task can be created in: every service frame and module on the
|
|
36
|
+
// board. Modules are labelled with their parent frame so the choice is unambiguous.
|
|
37
|
+
const containerId = ref<string | undefined>(undefined)
|
|
38
|
+
const containerItems = computed(() =>
|
|
39
|
+
board.blocks
|
|
40
|
+
.filter((b) => b.level === 'frame' || b.level === 'module')
|
|
41
|
+
.map((b) => ({
|
|
42
|
+
label:
|
|
43
|
+
b.level === 'module'
|
|
44
|
+
? `${board.getBlock(b.parentId ?? '')?.title ?? '?'} › ${b.title}`
|
|
45
|
+
: b.title,
|
|
46
|
+
value: b.id,
|
|
47
|
+
})),
|
|
48
|
+
)
|
|
49
|
+
// The issue currently being turned into a task (its row shows a spinner).
|
|
50
|
+
const creatingId = ref<string | null>(null)
|
|
51
|
+
|
|
52
|
+
watch(open, (isOpen) => {
|
|
53
|
+
if (isOpen) {
|
|
54
|
+
ref_.value = ''
|
|
55
|
+
source.value = ui.taskImport?.source ?? tasks.connectedSources[0]?.source ?? undefined
|
|
56
|
+
containerId.value = containerItems.value[0]?.value
|
|
57
|
+
creatingId.value = null
|
|
58
|
+
tasks.loadTasks().catch(() => {})
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
async function createTask(externalId: string) {
|
|
63
|
+
if (!source.value || !containerId.value) return
|
|
64
|
+
creatingId.value = externalId
|
|
65
|
+
try {
|
|
66
|
+
const { block } = await tasks.createTaskFromIssue(source.value, externalId, containerId.value)
|
|
67
|
+
board.upsert(block as Block)
|
|
68
|
+
toast.add({ title: `Created task "${block.title}"`, icon: 'i-lucide-square-check' })
|
|
69
|
+
} catch (e) {
|
|
70
|
+
toast.add({
|
|
71
|
+
title: 'Could not create task',
|
|
72
|
+
description: e instanceof Error ? e.message : String(e),
|
|
73
|
+
icon: 'i-lucide-triangle-alert',
|
|
74
|
+
color: 'error',
|
|
75
|
+
})
|
|
76
|
+
} finally {
|
|
77
|
+
creatingId.value = null
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function doImport() {
|
|
82
|
+
const value = ref_.value.trim()
|
|
83
|
+
if (!value || !source.value) return
|
|
84
|
+
importing.value = true
|
|
85
|
+
try {
|
|
86
|
+
const task = await tasks.importTask(source.value, value)
|
|
87
|
+
ref_.value = ''
|
|
88
|
+
toast.add({ title: `Imported "${task.title}"`, icon: 'i-lucide-file-down' })
|
|
89
|
+
} catch (e) {
|
|
90
|
+
toast.add({
|
|
91
|
+
title: 'Import failed',
|
|
92
|
+
description: e instanceof Error ? e.message : String(e),
|
|
93
|
+
icon: 'i-lucide-triangle-alert',
|
|
94
|
+
color: 'error',
|
|
95
|
+
})
|
|
96
|
+
} finally {
|
|
97
|
+
importing.value = false
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
</script>
|
|
101
|
+
|
|
102
|
+
<template>
|
|
103
|
+
<UModal v-model:open="open" title="Import from a task source">
|
|
104
|
+
<template #body>
|
|
105
|
+
<!-- Empty state: no connections -->
|
|
106
|
+
<div v-if="!tasks.anyConnected" class="space-y-3 text-center">
|
|
107
|
+
<UIcon name="i-lucide-plug" class="mx-auto h-8 w-8 text-slate-500" />
|
|
108
|
+
<p class="text-sm text-slate-400">Connect a task source first.</p>
|
|
109
|
+
<div class="flex justify-center gap-2">
|
|
110
|
+
<UButton
|
|
111
|
+
v-for="s in tasks.sources"
|
|
112
|
+
:key="s.source"
|
|
113
|
+
color="primary"
|
|
114
|
+
variant="soft"
|
|
115
|
+
:icon="s.icon"
|
|
116
|
+
@click="ui.openTaskConnect(s.source)"
|
|
117
|
+
>
|
|
118
|
+
Connect {{ s.label }}
|
|
119
|
+
</UButton>
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
|
|
123
|
+
<!-- Main form -->
|
|
124
|
+
<div v-else class="space-y-4">
|
|
125
|
+
<UFormField v-if="sourceItems.length > 1" label="Source">
|
|
126
|
+
<USelect v-model="source" :items="sourceItems" class="w-full" />
|
|
127
|
+
</UFormField>
|
|
128
|
+
|
|
129
|
+
<div class="flex items-end gap-2">
|
|
130
|
+
<UFormField :label="descriptor?.refLabel ?? 'Issue key or URL'" class="flex-1">
|
|
131
|
+
<UInput
|
|
132
|
+
v-model="ref_"
|
|
133
|
+
:placeholder="descriptor?.refPlaceholder"
|
|
134
|
+
class="w-full"
|
|
135
|
+
@keydown.enter="doImport"
|
|
136
|
+
/>
|
|
137
|
+
</UFormField>
|
|
138
|
+
<UButton
|
|
139
|
+
color="primary"
|
|
140
|
+
icon="i-lucide-file-down"
|
|
141
|
+
:loading="importing"
|
|
142
|
+
:disabled="!ref_.trim()"
|
|
143
|
+
@click="doImport"
|
|
144
|
+
>
|
|
145
|
+
Import
|
|
146
|
+
</UButton>
|
|
147
|
+
</div>
|
|
148
|
+
|
|
149
|
+
<!-- List of already-imported issues -->
|
|
150
|
+
<div v-if="sourceTasks.length" class="space-y-2">
|
|
151
|
+
<div class="flex items-end justify-between gap-3">
|
|
152
|
+
<h3 class="text-[11px] font-semibold uppercase tracking-wide text-slate-400">
|
|
153
|
+
Imported issues
|
|
154
|
+
</h3>
|
|
155
|
+
<UFormField v-if="containerItems.length" label="Create tasks in" size="xs" class="w-56">
|
|
156
|
+
<USelect
|
|
157
|
+
v-model="containerId"
|
|
158
|
+
:items="containerItems"
|
|
159
|
+
placeholder="Pick a frame or module"
|
|
160
|
+
class="w-full"
|
|
161
|
+
/>
|
|
162
|
+
</UFormField>
|
|
163
|
+
</div>
|
|
164
|
+
<div
|
|
165
|
+
v-for="task in sourceTasks"
|
|
166
|
+
:key="`${task.source}:${task.externalId}`"
|
|
167
|
+
class="rounded-lg border border-slate-800 bg-slate-900/60 p-3"
|
|
168
|
+
>
|
|
169
|
+
<div class="flex items-start justify-between gap-2">
|
|
170
|
+
<div class="min-w-0">
|
|
171
|
+
<a
|
|
172
|
+
:href="task.url"
|
|
173
|
+
target="_blank"
|
|
174
|
+
rel="noopener"
|
|
175
|
+
class="truncate text-sm font-medium text-white hover:underline"
|
|
176
|
+
>
|
|
177
|
+
{{ task.externalId }} · {{ task.title }}
|
|
178
|
+
</a>
|
|
179
|
+
<p class="mt-0.5 line-clamp-2 text-xs text-slate-500">{{ task.excerpt }}</p>
|
|
180
|
+
</div>
|
|
181
|
+
<div class="flex shrink-0 items-center gap-2">
|
|
182
|
+
<UBadge color="neutral" variant="soft" size="xs">
|
|
183
|
+
{{ task.status }}
|
|
184
|
+
</UBadge>
|
|
185
|
+
<UButton
|
|
186
|
+
color="primary"
|
|
187
|
+
variant="soft"
|
|
188
|
+
size="xs"
|
|
189
|
+
icon="i-lucide-square-check"
|
|
190
|
+
:loading="creatingId === task.externalId"
|
|
191
|
+
:disabled="!containerId || creatingId !== null"
|
|
192
|
+
@click="createTask(task.externalId)"
|
|
193
|
+
>
|
|
194
|
+
Create task
|
|
195
|
+
</UButton>
|
|
196
|
+
</div>
|
|
197
|
+
</div>
|
|
198
|
+
</div>
|
|
199
|
+
<p v-if="!containerItems.length" class="text-[11px] text-slate-500">
|
|
200
|
+
Add a service frame to the board first to create tasks from issues.
|
|
201
|
+
</p>
|
|
202
|
+
</div>
|
|
203
|
+
<p v-else class="text-center text-xs text-slate-500">No issues imported yet.</p>
|
|
204
|
+
</div>
|
|
205
|
+
</template>
|
|
206
|
+
</UModal>
|
|
207
|
+
</template>
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// Connect (or disconnect) the workspace to a task source. The form is rendered
|
|
3
|
+
// generically from the source's descriptor (credential fields), so the same
|
|
4
|
+
// modal serves Jira and any future tracker. Secret credentials are write-only —
|
|
5
|
+
// the backend never returns them, so on reload we show "Connected" with empty
|
|
6
|
+
// fields.
|
|
7
|
+
const ui = useUiStore()
|
|
8
|
+
const tasks = useTasksStore()
|
|
9
|
+
const toast = useToast()
|
|
10
|
+
|
|
11
|
+
const source = computed(() => ui.taskConnect?.source ?? null)
|
|
12
|
+
const descriptor = computed(() => (source.value ? tasks.descriptorFor(source.value) : undefined))
|
|
13
|
+
const connection = computed(() => (source.value ? tasks.connectionFor(source.value) : undefined))
|
|
14
|
+
const connected = computed(() => connection.value !== undefined)
|
|
15
|
+
|
|
16
|
+
const open = computed({
|
|
17
|
+
get: () => ui.taskConnect !== null,
|
|
18
|
+
set: (v: boolean) => {
|
|
19
|
+
if (!v) ui.closeTaskConnect()
|
|
20
|
+
},
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
/** One value per credential field, reset whenever the modal (re)opens. */
|
|
24
|
+
const values = ref<Record<string, string>>({})
|
|
25
|
+
const saving = ref(false)
|
|
26
|
+
|
|
27
|
+
watch(open, (isOpen) => {
|
|
28
|
+
if (isOpen) values.value = {}
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
// A source with no credential fields (e.g. GitHub, which reuses the workspace's
|
|
32
|
+
// installed GitHub App) connects with an empty bag — there is nothing to fill in,
|
|
33
|
+
// so the button is enabled as long as it isn't already connected.
|
|
34
|
+
const credentialless = computed(() => (descriptor.value?.credentialFields.length ?? 0) === 0)
|
|
35
|
+
|
|
36
|
+
const canSubmit = computed(() => {
|
|
37
|
+
const fields = descriptor.value?.credentialFields ?? []
|
|
38
|
+
if (credentialless.value) return !connected.value
|
|
39
|
+
return fields.every((f) => (values.value[f.key] ?? '').trim())
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
async function submit() {
|
|
43
|
+
if (!canSubmit.value || !source.value) return
|
|
44
|
+
const credentials: Record<string, string> = {}
|
|
45
|
+
for (const f of descriptor.value!.credentialFields) {
|
|
46
|
+
credentials[f.key] = values.value[f.key]!.trim()
|
|
47
|
+
}
|
|
48
|
+
saving.value = true
|
|
49
|
+
try {
|
|
50
|
+
await tasks.connect(source.value, credentials)
|
|
51
|
+
toast.add({
|
|
52
|
+
title: `${descriptor.value!.label} connected`,
|
|
53
|
+
icon: 'i-lucide-check',
|
|
54
|
+
color: 'success',
|
|
55
|
+
})
|
|
56
|
+
ui.closeTaskConnect()
|
|
57
|
+
} catch (e) {
|
|
58
|
+
toast.add({
|
|
59
|
+
title: 'Could not connect',
|
|
60
|
+
description: e instanceof Error ? e.message : String(e),
|
|
61
|
+
icon: 'i-lucide-triangle-alert',
|
|
62
|
+
color: 'error',
|
|
63
|
+
})
|
|
64
|
+
} finally {
|
|
65
|
+
saving.value = false
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async function disconnect() {
|
|
70
|
+
if (!source.value) return
|
|
71
|
+
await tasks.disconnect(source.value)
|
|
72
|
+
toast.add({
|
|
73
|
+
title: `${descriptor.value?.label ?? 'Source'} disconnected`,
|
|
74
|
+
icon: 'i-lucide-unplug',
|
|
75
|
+
})
|
|
76
|
+
ui.closeTaskConnect()
|
|
77
|
+
}
|
|
78
|
+
</script>
|
|
79
|
+
|
|
80
|
+
<template>
|
|
81
|
+
<UModal v-model:open="open" :title="descriptor?.label ?? 'Connect source'">
|
|
82
|
+
<template #body>
|
|
83
|
+
<div v-if="descriptor" class="space-y-4">
|
|
84
|
+
<p class="text-sm text-slate-400">
|
|
85
|
+
Connect {{ descriptor.label }} to import issues and attach them to tasks as agent context.
|
|
86
|
+
</p>
|
|
87
|
+
|
|
88
|
+
<p v-if="credentialless" class="text-[11px] text-slate-500">
|
|
89
|
+
This source uses the GitHub App already installed on your workspace — there are no
|
|
90
|
+
credentials to enter. Connecting just enables linking its issues to tasks.
|
|
91
|
+
</p>
|
|
92
|
+
|
|
93
|
+
<div v-else class="space-y-3">
|
|
94
|
+
<UFormField
|
|
95
|
+
v-for="field in descriptor.credentialFields"
|
|
96
|
+
:key="field.key"
|
|
97
|
+
:label="field.label"
|
|
98
|
+
:help="field.help"
|
|
99
|
+
>
|
|
100
|
+
<UInput
|
|
101
|
+
v-model="values[field.key]"
|
|
102
|
+
:type="field.secret ? 'password' : 'text'"
|
|
103
|
+
:placeholder="field.placeholder"
|
|
104
|
+
class="w-full"
|
|
105
|
+
/>
|
|
106
|
+
</UFormField>
|
|
107
|
+
</div>
|
|
108
|
+
|
|
109
|
+
<div class="flex items-center justify-between gap-2 pt-1">
|
|
110
|
+
<UButton
|
|
111
|
+
v-if="connected"
|
|
112
|
+
color="error"
|
|
113
|
+
variant="ghost"
|
|
114
|
+
icon="i-lucide-unplug"
|
|
115
|
+
@click="disconnect"
|
|
116
|
+
>
|
|
117
|
+
Disconnect
|
|
118
|
+
</UButton>
|
|
119
|
+
<div v-else />
|
|
120
|
+
<UButton
|
|
121
|
+
color="primary"
|
|
122
|
+
icon="i-lucide-plug"
|
|
123
|
+
:loading="saving"
|
|
124
|
+
:disabled="!canSubmit"
|
|
125
|
+
@click="submit"
|
|
126
|
+
>
|
|
127
|
+
{{ connected ? 'Update connection' : 'Connect' }}
|
|
128
|
+
</UButton>
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
</template>
|
|
132
|
+
</UModal>
|
|
133
|
+
</template>
|