@cat-factory/app 0.36.0 → 0.37.1
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/app/components/auth/UserMenu.vue +11 -1
- package/app/components/brainstorm/BrainstormWindow.vue +2 -1
- package/app/components/clarity/ClarityReviewWindow.vue +2 -1
- package/app/components/layout/IntegrationBackTitle.vue +12 -7
- package/app/components/layout/IntegrationsHub.vue +191 -43
- package/app/components/layout/PersonalSetupModal.vue +141 -0
- package/app/components/pipeline/PipelineBuilder.vue +1 -1
- package/app/components/providers/VendorCredentialsModal.vue +7 -2
- package/app/composables/api/accounts.ts +36 -51
- package/app/composables/api/auth.ts +20 -19
- package/app/composables/api/board.ts +60 -40
- package/app/composables/api/bootstrap.ts +25 -22
- package/app/composables/api/client.ts +102 -0
- package/app/composables/api/context.ts +25 -6
- package/app/composables/api/documents.ts +36 -34
- package/app/composables/api/execution.ts +65 -48
- package/app/composables/api/followUps.ts +26 -26
- package/app/composables/api/fragments.ts +47 -34
- package/app/composables/api/github.ts +65 -45
- package/app/composables/api/humanReview.ts +7 -6
- package/app/composables/api/humanTest.ts +15 -11
- package/app/composables/api/kaizen.ts +8 -6
- package/app/composables/api/localSettings.ts +5 -4
- package/app/composables/api/models.ts +58 -51
- package/app/composables/api/notifications.ts +13 -7
- package/app/composables/api/presets.ts +34 -28
- package/app/composables/api/providerConnections.ts +68 -26
- package/app/composables/api/recurring.ts +40 -30
- package/app/composables/api/releaseHealth.ts +28 -26
- package/app/composables/api/reviews.ts +136 -114
- package/app/composables/api/sandbox.ts +52 -34
- package/app/composables/api/slack.ts +22 -25
- package/app/composables/api/spec.ts +3 -3
- package/app/composables/api/tasks.ts +42 -41
- package/app/composables/api/userSecrets.ts +12 -17
- package/app/composables/api/workspaces.ts +21 -15
- package/app/composables/useApi.ts +9 -1
- package/app/composables/useIntegrationBack.ts +9 -3
- package/app/composables/useSourceIntegration.ts +107 -0
- package/app/composables/useUpsertList.spec.ts +60 -0
- package/app/composables/useUpsertList.ts +57 -0
- package/app/pages/index.vue +2 -0
- package/app/stores/auth.ts +2 -1
- package/app/stores/board.ts +2 -1
- package/app/stores/brainstorm.ts +2 -2
- package/app/stores/clarity.ts +6 -2
- package/app/stores/documents.ts +27 -62
- package/app/stores/execution.ts +3 -2
- package/app/stores/github.ts +1 -2
- package/app/stores/mergePresets.ts +2 -6
- package/app/stores/notifications.ts +9 -6
- package/app/stores/pipelines.ts +1 -1
- package/app/stores/recurringPipelines.ts +2 -7
- package/app/stores/sandbox.ts +1 -2
- package/app/stores/tasks.ts +25 -76
- package/app/stores/ui.ts +62 -19
- package/app/types/accountSettings.ts +11 -36
- package/app/types/accounts.ts +16 -71
- package/app/types/bootstrap.ts +13 -75
- package/app/types/brainstorm.ts +13 -38
- package/app/types/clarity.ts +12 -43
- package/app/types/consensus.ts +16 -89
- package/app/types/documents.ts +19 -94
- package/app/types/domain.ts +54 -586
- package/app/types/execution.ts +48 -515
- package/app/types/fragments.ts +15 -83
- package/app/types/github.ts +25 -161
- package/app/types/incidentEnrichment.ts +10 -25
- package/app/types/localModels.ts +11 -61
- package/app/types/localSettings.ts +9 -26
- package/app/types/merge.ts +10 -68
- package/app/types/model-presets.ts +7 -28
- package/app/types/models.ts +16 -164
- package/app/types/notifications.ts +18 -77
- package/app/types/openrouter.ts +8 -34
- package/app/types/providerConnections.ts +21 -41
- package/app/types/provisioningLogs.ts +9 -29
- package/app/types/recurring.ts +10 -63
- package/app/types/releaseHealth.ts +15 -39
- package/app/types/requirements.ts +14 -84
- package/app/types/sandbox.ts +45 -161
- package/app/types/services.ts +3 -22
- package/app/types/slack.ts +10 -47
- package/app/types/spec.ts +15 -68
- package/app/types/tasks.ts +15 -111
- package/app/types/tracker.ts +9 -24
- package/app/types/userSecrets.ts +12 -47
- package/package.json +9 -2
package/app/stores/documents.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { defineStore } from 'pinia'
|
|
2
|
-
import {
|
|
2
|
+
import { ref } from 'vue'
|
|
3
3
|
import type {
|
|
4
4
|
DocumentBoardPlan,
|
|
5
5
|
DocumentConnection,
|
|
@@ -8,6 +8,8 @@ import type {
|
|
|
8
8
|
DocumentSourceKind,
|
|
9
9
|
SourceDocument,
|
|
10
10
|
} from '~/types/domain'
|
|
11
|
+
import { useSourceIntegration } from '~/composables/useSourceIntegration'
|
|
12
|
+
import { useUpsertList } from '~/composables/useUpsertList'
|
|
11
13
|
import { useWorkspaceStore } from '~/stores/workspace'
|
|
12
14
|
|
|
13
15
|
/**
|
|
@@ -24,83 +26,46 @@ export const useDocumentsStore = defineStore('documents', () => {
|
|
|
24
26
|
const api = useApi()
|
|
25
27
|
const workspace = useWorkspaceStore()
|
|
26
28
|
|
|
27
|
-
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
// Shared opt-in / probe / connections lifecycle (see `useSourceIntegration`).
|
|
30
|
+
const integration = useSourceIntegration<
|
|
31
|
+
DocumentSourceKind,
|
|
32
|
+
DocumentConnection,
|
|
33
|
+
DocumentSourceDescriptor
|
|
34
|
+
>({
|
|
35
|
+
enabled: () => !!workspace.workspaceId,
|
|
36
|
+
fetch: async () => {
|
|
37
|
+
const [{ sources }, { connections }] = await Promise.all([
|
|
38
|
+
api.listDocumentSources(workspace.requireId()),
|
|
39
|
+
api.listDocumentConnections(workspace.requireId()),
|
|
40
|
+
])
|
|
41
|
+
return { sources, connections }
|
|
42
|
+
},
|
|
43
|
+
})
|
|
44
|
+
const { available, sources, connections, connectedSources, anyConnected } = integration
|
|
45
|
+
const { descriptorFor, connectionFor, isConnected, probe } = integration
|
|
46
|
+
|
|
47
|
+
const { items: documents, upsert: upsertDoc } = useUpsertList<SourceDocument>({
|
|
48
|
+
key: (d) => `${d.source}:${d.externalId}`,
|
|
49
|
+
prepend: true,
|
|
50
|
+
})
|
|
34
51
|
const loading = ref(false)
|
|
35
52
|
|
|
36
|
-
/** Sources the workspace currently has a live connection to. */
|
|
37
|
-
const connectedSources = computed(() =>
|
|
38
|
-
sources.value.filter((s) => connections.value.some((c) => c.source === s.source)),
|
|
39
|
-
)
|
|
40
|
-
const anyConnected = computed(() => connections.value.length > 0)
|
|
41
|
-
|
|
42
|
-
function descriptorFor(source: DocumentSourceKind): DocumentSourceDescriptor | undefined {
|
|
43
|
-
return sources.value.find((s) => s.source === source)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function connectionFor(source: DocumentSourceKind): DocumentConnection | undefined {
|
|
47
|
-
return connections.value.find((c) => c.source === source)
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function isConnected(source: DocumentSourceKind): boolean {
|
|
51
|
-
return connectionFor(source) !== undefined
|
|
52
|
-
}
|
|
53
|
-
|
|
54
53
|
/** Imported documents currently attached to a given block. */
|
|
55
54
|
function docsForBlock(blockId: string): SourceDocument[] {
|
|
56
55
|
return documents.value.filter((d) => d.linkedBlockId === blockId)
|
|
57
56
|
}
|
|
58
57
|
|
|
59
|
-
/** Merge a document returned by the backend into the local cache. */
|
|
60
|
-
function upsertDoc(doc: SourceDocument) {
|
|
61
|
-
const i = documents.value.findIndex(
|
|
62
|
-
(d) => d.source === doc.source && d.externalId === doc.externalId,
|
|
63
|
-
)
|
|
64
|
-
if (i >= 0) documents.value[i] = doc
|
|
65
|
-
else documents.value.unshift(doc)
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function upsertConnection(conn: DocumentConnection) {
|
|
69
|
-
const i = connections.value.findIndex((c) => c.source === conn.source)
|
|
70
|
-
if (i >= 0) connections.value[i] = conn
|
|
71
|
-
else connections.value.push(conn)
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/** Probe the integration: resolves `available`, the sources and connections. */
|
|
75
|
-
async function probe() {
|
|
76
|
-
if (!workspace.workspaceId) return
|
|
77
|
-
try {
|
|
78
|
-
const [{ sources: srcs }, { connections: conns }] = await Promise.all([
|
|
79
|
-
api.listDocumentSources(workspace.requireId()),
|
|
80
|
-
api.listDocumentConnections(workspace.requireId()),
|
|
81
|
-
])
|
|
82
|
-
available.value = true
|
|
83
|
-
sources.value = srcs
|
|
84
|
-
connections.value = conns
|
|
85
|
-
} catch {
|
|
86
|
-
// 503 (integration disabled) or any error → hide the UI entry points.
|
|
87
|
-
available.value = false
|
|
88
|
-
sources.value = []
|
|
89
|
-
connections.value = []
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
58
|
/** Connect the workspace to a source with its credential bag. */
|
|
94
59
|
async function connect(source: DocumentSourceKind, credentials: Record<string, string>) {
|
|
95
60
|
const conn = await api.connectDocumentSource(workspace.requireId(), source, credentials)
|
|
96
|
-
upsertConnection(conn)
|
|
61
|
+
integration.upsertConnection(conn)
|
|
97
62
|
available.value = true
|
|
98
63
|
}
|
|
99
64
|
|
|
100
65
|
/** Disconnect the workspace from a source. */
|
|
101
66
|
async function disconnect(source: DocumentSourceKind) {
|
|
102
67
|
await api.disconnectDocumentSource(workspace.requireId(), source)
|
|
103
|
-
|
|
68
|
+
integration.removeConnection(source)
|
|
104
69
|
}
|
|
105
70
|
|
|
106
71
|
/** Load the imported documents for the workspace (across sources). */
|
package/app/stores/execution.ts
CHANGED
|
@@ -7,7 +7,8 @@ import type {
|
|
|
7
7
|
PipelineStep,
|
|
8
8
|
StepApproval,
|
|
9
9
|
} from '~/types/domain'
|
|
10
|
-
import type {
|
|
10
|
+
import type { RequestStepChangesInput } from '@cat-factory/contracts'
|
|
11
|
+
import type { IterationCapChoice } from '~/types/execution'
|
|
11
12
|
import { useWorkspaceStore } from '~/stores/workspace'
|
|
12
13
|
|
|
13
14
|
/**
|
|
@@ -163,7 +164,7 @@ export const useExecutionStore = defineStore('execution', () => {
|
|
|
163
164
|
async function requestStepChanges(
|
|
164
165
|
instanceId: string,
|
|
165
166
|
approvalId: string,
|
|
166
|
-
review:
|
|
167
|
+
review: RequestStepChangesInput,
|
|
167
168
|
) {
|
|
168
169
|
const ws = useWorkspaceStore()
|
|
169
170
|
const personal = usePersonalSubscriptionsStore()
|
package/app/stores/github.ts
CHANGED
|
@@ -2,7 +2,6 @@ import { defineStore } from 'pinia'
|
|
|
2
2
|
import { computed, ref } from 'vue'
|
|
3
3
|
import type {
|
|
4
4
|
CreateBranchInput,
|
|
5
|
-
CreateRepoRequest,
|
|
6
5
|
GitHubAvailableRepo,
|
|
7
6
|
GitHubBranch,
|
|
8
7
|
GitHubConnection,
|
|
@@ -224,7 +223,7 @@ export const useGitHubStore = defineStore('github', () => {
|
|
|
224
223
|
* meaningful when `canCreateRepos`; the backend 409s otherwise. Returns the
|
|
225
224
|
* created repo so the caller can confirm/link it.
|
|
226
225
|
*/
|
|
227
|
-
function createRepo(input:
|
|
226
|
+
function createRepo(input: Parameters<typeof api.createGitHubRepo>[1]) {
|
|
228
227
|
return api.createGitHubRepo(workspace.requireId(), input)
|
|
229
228
|
}
|
|
230
229
|
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import { defineStore } from 'pinia'
|
|
2
2
|
import { computed, ref } from 'vue'
|
|
3
|
-
import type {
|
|
4
|
-
CreateMergePresetInput,
|
|
5
|
-
MergeThresholdPreset,
|
|
6
|
-
UpdateMergePresetInput,
|
|
7
|
-
} from '~/types/domain'
|
|
3
|
+
import type { MergeThresholdPreset, UpdateMergePresetInput } from '~/types/domain'
|
|
8
4
|
import { useWorkspaceStore } from '~/stores/workspace'
|
|
9
5
|
|
|
10
6
|
/**
|
|
@@ -34,7 +30,7 @@ export const useMergePresetsStore = defineStore('mergePresets', () => {
|
|
|
34
30
|
return defaultPreset.value
|
|
35
31
|
}
|
|
36
32
|
|
|
37
|
-
async function create(input:
|
|
33
|
+
async function create(input: Parameters<typeof api.createMergePreset>[1]) {
|
|
38
34
|
const ws = useWorkspaceStore()
|
|
39
35
|
const created = await api.createMergePreset(ws.requireId(), input)
|
|
40
36
|
await ws.refresh()
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { defineStore } from 'pinia'
|
|
2
|
-
import { computed
|
|
2
|
+
import { computed } from 'vue'
|
|
3
3
|
import type { Notification } from '~/types/domain'
|
|
4
|
+
import { useUpsertList } from '~/composables/useUpsertList'
|
|
4
5
|
import { useWorkspaceStore } from '~/stores/workspace'
|
|
5
6
|
|
|
6
7
|
/**
|
|
@@ -14,7 +15,11 @@ export const useNotificationsStore = defineStore('notifications', () => {
|
|
|
14
15
|
const api = useApi()
|
|
15
16
|
|
|
16
17
|
/** All open notifications, newest-first. */
|
|
17
|
-
const
|
|
18
|
+
const {
|
|
19
|
+
items: open,
|
|
20
|
+
upsert: upsertOpen,
|
|
21
|
+
remove,
|
|
22
|
+
} = useUpsertList<Notification>({ key: (n) => n.id, prepend: true })
|
|
18
23
|
|
|
19
24
|
/** Replace the cache from a server snapshot. */
|
|
20
25
|
function hydrate(notifications: Notification[]) {
|
|
@@ -28,13 +33,11 @@ export const useNotificationsStore = defineStore('notifications', () => {
|
|
|
28
33
|
* replaced in place; a resolved one (acted/dismissed) is removed from the inbox.
|
|
29
34
|
*/
|
|
30
35
|
function upsert(notification: Notification) {
|
|
31
|
-
const i = open.value.findIndex((n) => n.id === notification.id)
|
|
32
36
|
if (notification.status !== 'open') {
|
|
33
|
-
|
|
37
|
+
remove(notification.id)
|
|
34
38
|
return
|
|
35
39
|
}
|
|
36
|
-
|
|
37
|
-
else open.value.unshift(notification)
|
|
40
|
+
upsertOpen(notification)
|
|
38
41
|
}
|
|
39
42
|
|
|
40
43
|
/** Open notifications for a given block (for the board card badge). */
|
package/app/stores/pipelines.ts
CHANGED
|
@@ -132,7 +132,7 @@ export const usePipelinesStore = defineStore('pipelines', () => {
|
|
|
132
132
|
function toggleDraftGating(index: number) {
|
|
133
133
|
draftGating.value[index] = draftGating.value[index]?.enabled
|
|
134
134
|
? null
|
|
135
|
-
: { enabled: true, minRisk: 0.5, minImpact: 0.5 }
|
|
135
|
+
: { enabled: true, minRisk: 0.5, minImpact: 0.5, onMissingEstimate: 'run' }
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
/**
|
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
import { defineStore } from 'pinia'
|
|
2
2
|
import { computed, ref } from 'vue'
|
|
3
|
-
import type {
|
|
4
|
-
CreateScheduleInput,
|
|
5
|
-
PipelineSchedule,
|
|
6
|
-
ScheduleRun,
|
|
7
|
-
UpdateScheduleInput,
|
|
8
|
-
} from '~/types/recurring'
|
|
3
|
+
import type { PipelineSchedule, ScheduleRun, UpdateScheduleInput } from '~/types/recurring'
|
|
9
4
|
import { useWorkspaceStore } from '~/stores/workspace'
|
|
10
5
|
import { useBoardStore } from '~/stores/board'
|
|
11
6
|
|
|
@@ -39,7 +34,7 @@ export const useRecurringPipelinesStore = defineStore('recurringPipelines', () =
|
|
|
39
34
|
return schedules.value.find((s) => s.blockId === blockId)
|
|
40
35
|
}
|
|
41
36
|
|
|
42
|
-
async function create(input:
|
|
37
|
+
async function create(input: Parameters<typeof api.createRecurringPipeline>[1]) {
|
|
43
38
|
const ws = useWorkspaceStore()
|
|
44
39
|
const created = await api.createRecurringPipeline(ws.requireId(), input)
|
|
45
40
|
await ws.refresh()
|
package/app/stores/sandbox.ts
CHANGED
|
@@ -2,7 +2,6 @@ import { defineStore } from 'pinia'
|
|
|
2
2
|
import { computed, ref } from 'vue'
|
|
3
3
|
import type { ModelOption } from '~/types/domain'
|
|
4
4
|
import type {
|
|
5
|
-
CreateSandboxExperimentInput,
|
|
6
5
|
SandboxAgentKindMeta,
|
|
7
6
|
SandboxExperiment,
|
|
8
7
|
SandboxExperimentDetail,
|
|
@@ -118,7 +117,7 @@ export const useSandboxStore = defineStore('sandbox', () => {
|
|
|
118
117
|
await load()
|
|
119
118
|
}
|
|
120
119
|
|
|
121
|
-
async function createExperiment(input:
|
|
120
|
+
async function createExperiment(input: Parameters<typeof api.createSandboxExperiment>[1]) {
|
|
122
121
|
const ws = useWorkspaceStore()
|
|
123
122
|
const created = await api.createSandboxExperiment(ws.requireId(), input)
|
|
124
123
|
await load()
|
package/app/stores/tasks.ts
CHANGED
|
@@ -8,6 +8,8 @@ import type {
|
|
|
8
8
|
TaskSourceKind,
|
|
9
9
|
TaskSourceState,
|
|
10
10
|
} from '~/types/domain'
|
|
11
|
+
import { useSourceIntegration } from '~/composables/useSourceIntegration'
|
|
12
|
+
import { useUpsertList } from '~/composables/useUpsertList'
|
|
11
13
|
import { useWorkspaceStore } from '~/stores/workspace'
|
|
12
14
|
import { useBoardStore } from '~/stores/board'
|
|
13
15
|
|
|
@@ -27,95 +29,42 @@ export const useTasksStore = defineStore('tasks', () => {
|
|
|
27
29
|
const api = useApi()
|
|
28
30
|
const workspace = useWorkspaceStore()
|
|
29
31
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
32
|
+
// Shared opt-in / probe / connections lifecycle (see `useSourceIntegration`). Its
|
|
33
|
+
// `probeError` is what lets the settings panel explain *why* nothing is surfaced
|
|
34
|
+
// (integration disabled vs a server/backend error) instead of "install it first".
|
|
35
|
+
const integration = useSourceIntegration<TaskSourceKind, TaskConnection, TaskSourceState>({
|
|
36
|
+
enabled: () => !!workspace.workspaceId,
|
|
37
|
+
fetch: async () => {
|
|
38
|
+
const [{ sources }, { connections }] = await Promise.all([
|
|
39
|
+
api.listTaskSources(workspace.requireId()),
|
|
40
|
+
api.listTaskConnections(workspace.requireId()),
|
|
41
|
+
])
|
|
42
|
+
return { sources, connections }
|
|
43
|
+
},
|
|
44
|
+
})
|
|
45
|
+
const { available, probeError, sources, connections, connectedSources, anyConnected } =
|
|
46
|
+
integration
|
|
47
|
+
const { descriptorFor, connectionFor, isConnected, probe } = integration
|
|
48
|
+
|
|
49
|
+
const { items: tasks, upsert: upsertTask } = useUpsertList<SourceTask>({
|
|
50
|
+
key: (t) => `${t.source}:${t.externalId}`,
|
|
51
|
+
prepend: true,
|
|
52
|
+
})
|
|
43
53
|
/** The last live setup-check verdict per source (from `checkSetup`). */
|
|
44
54
|
const diagnostics = ref<Partial<Record<TaskSourceKind, TaskSourceDiagnostic>>>({})
|
|
45
55
|
/** The source currently running a setup check, if any. */
|
|
46
56
|
const checking = ref<TaskSourceKind | null>(null)
|
|
47
57
|
const loading = ref(false)
|
|
48
58
|
|
|
49
|
-
/** Sources the workspace currently has a live connection to. */
|
|
50
|
-
const connectedSources = computed(() =>
|
|
51
|
-
sources.value.filter((s) => connections.value.some((c) => c.source === s.source)),
|
|
52
|
-
)
|
|
53
|
-
const anyConnected = computed(() => connections.value.length > 0)
|
|
54
|
-
|
|
55
59
|
/** Sources offered for import: available (connected / App installed) AND enabled. */
|
|
56
60
|
const offeredSources = computed(() => sources.value.filter((s) => s.available && s.enabled))
|
|
57
61
|
const anyOffered = computed(() => offeredSources.value.length > 0)
|
|
58
62
|
|
|
59
|
-
function descriptorFor(source: TaskSourceKind): TaskSourceState | undefined {
|
|
60
|
-
return sources.value.find((s) => s.source === source)
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function connectionFor(source: TaskSourceKind): TaskConnection | undefined {
|
|
64
|
-
return connections.value.find((c) => c.source === source)
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function isConnected(source: TaskSourceKind): boolean {
|
|
68
|
-
return connectionFor(source) !== undefined
|
|
69
|
-
}
|
|
70
|
-
|
|
71
63
|
/** Imported issues currently attached to a given block. */
|
|
72
64
|
function tasksForBlock(blockId: string): SourceTask[] {
|
|
73
65
|
return tasks.value.filter((t) => t.linkedBlockId === blockId)
|
|
74
66
|
}
|
|
75
67
|
|
|
76
|
-
/** Merge an issue returned by the backend into the local cache. */
|
|
77
|
-
function upsertTask(task: SourceTask) {
|
|
78
|
-
const i = tasks.value.findIndex(
|
|
79
|
-
(t) => t.source === task.source && t.externalId === task.externalId,
|
|
80
|
-
)
|
|
81
|
-
if (i >= 0) tasks.value[i] = task
|
|
82
|
-
else tasks.value.unshift(task)
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function upsertConnection(conn: TaskConnection) {
|
|
86
|
-
const i = connections.value.findIndex((c) => c.source === conn.source)
|
|
87
|
-
if (i >= 0) connections.value[i] = conn
|
|
88
|
-
else connections.value.push(conn)
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/** Probe the integration: resolves `available`, the sources and connections. */
|
|
92
|
-
async function probe() {
|
|
93
|
-
if (!workspace.workspaceId) return
|
|
94
|
-
try {
|
|
95
|
-
const [{ sources: srcs }, { connections: conns }] = await Promise.all([
|
|
96
|
-
api.listTaskSources(workspace.requireId()),
|
|
97
|
-
api.listTaskConnections(workspace.requireId()),
|
|
98
|
-
])
|
|
99
|
-
available.value = true
|
|
100
|
-
probeError.value = null
|
|
101
|
-
sources.value = srcs
|
|
102
|
-
connections.value = conns
|
|
103
|
-
} catch (e) {
|
|
104
|
-
// 503 (integration disabled) or any error → hide the UI entry points, but keep
|
|
105
|
-
// the reason so the settings panel can explain it (a 503 is "turned off on this
|
|
106
|
-
// deployment"; a 500 is "the backend errored — e.g. a migration isn't applied").
|
|
107
|
-
available.value = false
|
|
108
|
-
const err = e as { statusCode?: number; data?: { error?: { message?: string } } }
|
|
109
|
-
const serverMessage = err?.data?.error?.message
|
|
110
|
-
probeError.value = {
|
|
111
|
-
status: err?.statusCode ?? null,
|
|
112
|
-
message: serverMessage || (e instanceof Error ? e.message : String(e)),
|
|
113
|
-
}
|
|
114
|
-
sources.value = []
|
|
115
|
-
connections.value = []
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
68
|
/**
|
|
120
69
|
* Run a live setup check for a source (authenticate + read), caching the verdict
|
|
121
70
|
* so the panel can show exactly what's wrong (missing App / wrong token / lacking
|
|
@@ -137,14 +86,14 @@ export const useTasksStore = defineStore('tasks', () => {
|
|
|
137
86
|
/** Connect the workspace to a source with its credential bag. */
|
|
138
87
|
async function connect(source: TaskSourceKind, credentials: Record<string, string>) {
|
|
139
88
|
const conn = await api.connectTaskSource(workspace.requireId(), source, credentials)
|
|
140
|
-
upsertConnection(conn)
|
|
89
|
+
integration.upsertConnection(conn)
|
|
141
90
|
available.value = true
|
|
142
91
|
}
|
|
143
92
|
|
|
144
93
|
/** Disconnect the workspace from a source. */
|
|
145
94
|
async function disconnect(source: TaskSourceKind) {
|
|
146
95
|
await api.disconnectTaskSource(workspace.requireId(), source)
|
|
147
|
-
|
|
96
|
+
integration.removeConnection(source)
|
|
148
97
|
}
|
|
149
98
|
|
|
150
99
|
/** Enable or disable a source for the workspace (the per-workspace toggle). */
|
package/app/stores/ui.ts
CHANGED
|
@@ -91,6 +91,14 @@ export const useUiStore = defineStore('ui', () => {
|
|
|
91
91
|
// is one. Every direct `open*` below resets it; `openFromIntegrations` sets it.
|
|
92
92
|
const cameFromIntegrations = ref(false)
|
|
93
93
|
|
|
94
|
+
// Personal "My setup" hub: a user-scoped sibling of the Integrations hub, listing the
|
|
95
|
+
// signed-in user's own connections (GitHub PAT, local runners, personal subscriptions)
|
|
96
|
+
// separated out of the workspace-scoped Integrations hub. `cameFromPersonal` is the
|
|
97
|
+
// symmetric came-from marker, so a panel reached from here renders a "Back to My setup"
|
|
98
|
+
// control instead of "Back to Integrations".
|
|
99
|
+
const personalSetupOpen = ref(false)
|
|
100
|
+
const cameFromPersonal = ref(false)
|
|
101
|
+
|
|
94
102
|
// Workspace-settings modal: a single tabbed window gathering the workspace-wide
|
|
95
103
|
// config (workspace / merge thresholds / issue writeback / service best practices).
|
|
96
104
|
// `workspaceSettingsTab` lets other surfaces deep-link straight to a tab.
|
|
@@ -112,8 +120,10 @@ export const useUiStore = defineStore('ui', () => {
|
|
|
112
120
|
const providerConnectionKind = ref<'environment' | 'runner-pool' | null>(null)
|
|
113
121
|
const modelConfigOpen = ref(false)
|
|
114
122
|
// LLM-vendor subscription credentials (the token pool powering the Claude Code
|
|
115
|
-
// / Codex harnesses).
|
|
123
|
+
// / Codex harnesses). `vendorCredentialsTab` lets a caller deep-link to one tab —
|
|
124
|
+
// the user-scoped "My subscriptions" entry opens straight onto the `personal` tab.
|
|
116
125
|
const vendorCredentialsOpen = ref(false)
|
|
126
|
+
const vendorCredentialsTab = ref('pool')
|
|
117
127
|
// Per-user settings panel: the signed-in user's own-machine local model runners.
|
|
118
128
|
const localModelsOpen = ref(false)
|
|
119
129
|
// Local-mode-only settings panel: the warm-container pool sizing + per-repo checkout reuse
|
|
@@ -267,7 +277,7 @@ export const useUiStore = defineStore('ui', () => {
|
|
|
267
277
|
}
|
|
268
278
|
|
|
269
279
|
function openDocumentConnect(source: DocumentSourceKind) {
|
|
270
|
-
|
|
280
|
+
resetHubReturn()
|
|
271
281
|
documentConnect.value = { source }
|
|
272
282
|
}
|
|
273
283
|
function closeDocumentConnect() {
|
|
@@ -277,7 +287,7 @@ export const useUiStore = defineStore('ui', () => {
|
|
|
277
287
|
targetFrameId: string | null = null,
|
|
278
288
|
source: DocumentSourceKind | null = null,
|
|
279
289
|
) {
|
|
280
|
-
|
|
290
|
+
resetHubReturn()
|
|
281
291
|
documentImport.value = { source, targetFrameId }
|
|
282
292
|
}
|
|
283
293
|
function closeDocumentImport() {
|
|
@@ -294,14 +304,14 @@ export const useUiStore = defineStore('ui', () => {
|
|
|
294
304
|
spawnPreview.value = null
|
|
295
305
|
}
|
|
296
306
|
function openTaskConnect(source: TaskSourceKind) {
|
|
297
|
-
|
|
307
|
+
resetHubReturn()
|
|
298
308
|
taskConnect.value = { source }
|
|
299
309
|
}
|
|
300
310
|
function closeTaskConnect() {
|
|
301
311
|
taskConnect.value = null
|
|
302
312
|
}
|
|
303
313
|
function openTaskImport(source: TaskSourceKind | null = null, containerId: string | null = null) {
|
|
304
|
-
|
|
314
|
+
resetHubReturn()
|
|
305
315
|
taskImport.value = { source, containerId }
|
|
306
316
|
}
|
|
307
317
|
function closeTaskImport() {
|
|
@@ -334,14 +344,14 @@ export const useUiStore = defineStore('ui', () => {
|
|
|
334
344
|
addServiceOpen.value = false
|
|
335
345
|
}
|
|
336
346
|
function openGitHub() {
|
|
337
|
-
|
|
347
|
+
resetHubReturn()
|
|
338
348
|
githubOpen.value = true
|
|
339
349
|
}
|
|
340
350
|
function closeGitHub() {
|
|
341
351
|
githubOpen.value = false
|
|
342
352
|
}
|
|
343
353
|
function openSlack() {
|
|
344
|
-
|
|
354
|
+
resetHubReturn()
|
|
345
355
|
slackOpen.value = true
|
|
346
356
|
}
|
|
347
357
|
function closeSlack() {
|
|
@@ -362,15 +372,37 @@ export const useUiStore = defineStore('ui', () => {
|
|
|
362
372
|
function toggleCommandBar() {
|
|
363
373
|
commandBarOpen.value = !commandBarOpen.value
|
|
364
374
|
}
|
|
375
|
+
// Clear BOTH hub came-from markers. Every direct `open*` below calls this so that a
|
|
376
|
+
// panel opened outside the hubs never grows a dead Back control, and so switching from
|
|
377
|
+
// one hub's panel to the other's clears the stale marker.
|
|
378
|
+
function resetHubReturn() {
|
|
379
|
+
resetHubReturn()
|
|
380
|
+
cameFromPersonal.value = false
|
|
381
|
+
}
|
|
365
382
|
function openIntegrations() {
|
|
366
383
|
// Reaching the hub itself (fresh, or via a panel's Back control) clears the
|
|
367
|
-
// came-from
|
|
368
|
-
|
|
384
|
+
// came-from markers — we're at the hub, not inside a hub-spawned panel.
|
|
385
|
+
resetHubReturn()
|
|
369
386
|
integrationsOpen.value = true
|
|
370
387
|
}
|
|
371
388
|
function closeIntegrations() {
|
|
372
389
|
integrationsOpen.value = false
|
|
373
390
|
}
|
|
391
|
+
function openPersonalSetup() {
|
|
392
|
+
resetHubReturn()
|
|
393
|
+
personalSetupOpen.value = true
|
|
394
|
+
}
|
|
395
|
+
function closePersonalSetup() {
|
|
396
|
+
personalSetupOpen.value = false
|
|
397
|
+
}
|
|
398
|
+
// Open a user-scoped panel FROM the My-setup hub: run its open handler (which resets the
|
|
399
|
+
// markers), then mark that we came from My setup and dismiss it, so the panel's
|
|
400
|
+
// IntegrationBackTitle returns here rather than to the workspace Integrations hub.
|
|
401
|
+
function openFromPersonal(open: () => void) {
|
|
402
|
+
open()
|
|
403
|
+
cameFromPersonal.value = true
|
|
404
|
+
personalSetupOpen.value = false
|
|
405
|
+
}
|
|
374
406
|
// Open an integration's own panel FROM the hub: run its open handler (which resets
|
|
375
407
|
// `cameFromIntegrations`), then mark that we came from the hub and dismiss it. The
|
|
376
408
|
// panel reads `cameFromIntegrations` to show its Back control.
|
|
@@ -380,7 +412,7 @@ export const useUiStore = defineStore('ui', () => {
|
|
|
380
412
|
integrationsOpen.value = false
|
|
381
413
|
}
|
|
382
414
|
function openWorkspaceSettings(tab = 'workspace') {
|
|
383
|
-
|
|
415
|
+
resetHubReturn()
|
|
384
416
|
workspaceSettingsTab.value = tab
|
|
385
417
|
workspaceSettingsOpen.value = true
|
|
386
418
|
}
|
|
@@ -391,7 +423,7 @@ export const useUiStore = defineStore('ui', () => {
|
|
|
391
423
|
workspaceSettingsTab.value = tab
|
|
392
424
|
}
|
|
393
425
|
function openAccountSettings(tab = 'team') {
|
|
394
|
-
|
|
426
|
+
resetHubReturn()
|
|
395
427
|
accountSettingsTab.value = tab
|
|
396
428
|
accountSettingsOpen.value = true
|
|
397
429
|
}
|
|
@@ -402,14 +434,14 @@ export const useUiStore = defineStore('ui', () => {
|
|
|
402
434
|
accountSettingsTab.value = tab
|
|
403
435
|
}
|
|
404
436
|
function openObservabilityConnection() {
|
|
405
|
-
|
|
437
|
+
resetHubReturn()
|
|
406
438
|
observabilityConnectionOpen.value = true
|
|
407
439
|
}
|
|
408
440
|
function closeObservabilityConnection() {
|
|
409
441
|
observabilityConnectionOpen.value = false
|
|
410
442
|
}
|
|
411
443
|
function openProviderConnection(kind: 'environment' | 'runner-pool') {
|
|
412
|
-
|
|
444
|
+
resetHubReturn()
|
|
413
445
|
providerConnectionKind.value = kind
|
|
414
446
|
}
|
|
415
447
|
function closeProviderConnection() {
|
|
@@ -421,22 +453,26 @@ export const useUiStore = defineStore('ui', () => {
|
|
|
421
453
|
function closeModelConfig() {
|
|
422
454
|
modelConfigOpen.value = false
|
|
423
455
|
}
|
|
424
|
-
function openVendorCredentials() {
|
|
425
|
-
|
|
456
|
+
function openVendorCredentials(tab = 'pool') {
|
|
457
|
+
resetHubReturn()
|
|
458
|
+
vendorCredentialsTab.value = tab
|
|
426
459
|
vendorCredentialsOpen.value = true
|
|
427
460
|
}
|
|
461
|
+
function setVendorCredentialsTab(tab: string) {
|
|
462
|
+
vendorCredentialsTab.value = tab
|
|
463
|
+
}
|
|
428
464
|
function closeVendorCredentials() {
|
|
429
465
|
vendorCredentialsOpen.value = false
|
|
430
466
|
}
|
|
431
467
|
function openLocalModels() {
|
|
432
|
-
|
|
468
|
+
resetHubReturn()
|
|
433
469
|
localModelsOpen.value = true
|
|
434
470
|
}
|
|
435
471
|
function closeLocalModels() {
|
|
436
472
|
localModelsOpen.value = false
|
|
437
473
|
}
|
|
438
474
|
function openLocalModeSettings() {
|
|
439
|
-
|
|
475
|
+
resetHubReturn()
|
|
440
476
|
localModeSettingsOpen.value = true
|
|
441
477
|
}
|
|
442
478
|
function closeLocalModeSettings() {
|
|
@@ -449,14 +485,14 @@ export const useUiStore = defineStore('ui', () => {
|
|
|
449
485
|
sandboxOpen.value = false
|
|
450
486
|
}
|
|
451
487
|
function openUserSecrets() {
|
|
452
|
-
|
|
488
|
+
resetHubReturn()
|
|
453
489
|
userSecretsOpen.value = true
|
|
454
490
|
}
|
|
455
491
|
function closeUserSecrets() {
|
|
456
492
|
userSecretsOpen.value = false
|
|
457
493
|
}
|
|
458
494
|
function openOpenRouter() {
|
|
459
|
-
|
|
495
|
+
resetHubReturn()
|
|
460
496
|
openRouterOpen.value = true
|
|
461
497
|
}
|
|
462
498
|
function closeOpenRouter() {
|
|
@@ -581,6 +617,8 @@ export const useUiStore = defineStore('ui', () => {
|
|
|
581
617
|
commandBarOpen,
|
|
582
618
|
integrationsOpen,
|
|
583
619
|
cameFromIntegrations,
|
|
620
|
+
personalSetupOpen,
|
|
621
|
+
cameFromPersonal,
|
|
584
622
|
workspaceSettingsOpen,
|
|
585
623
|
workspaceSettingsTab,
|
|
586
624
|
accountSettingsOpen,
|
|
@@ -589,6 +627,7 @@ export const useUiStore = defineStore('ui', () => {
|
|
|
589
627
|
providerConnectionKind,
|
|
590
628
|
modelConfigOpen,
|
|
591
629
|
vendorCredentialsOpen,
|
|
630
|
+
vendorCredentialsTab,
|
|
592
631
|
localModelsOpen,
|
|
593
632
|
localModeSettingsOpen,
|
|
594
633
|
sandboxOpen,
|
|
@@ -645,6 +684,9 @@ export const useUiStore = defineStore('ui', () => {
|
|
|
645
684
|
openIntegrations,
|
|
646
685
|
closeIntegrations,
|
|
647
686
|
openFromIntegrations,
|
|
687
|
+
openPersonalSetup,
|
|
688
|
+
closePersonalSetup,
|
|
689
|
+
openFromPersonal,
|
|
648
690
|
openWorkspaceSettings,
|
|
649
691
|
closeWorkspaceSettings,
|
|
650
692
|
setWorkspaceSettingsTab,
|
|
@@ -658,6 +700,7 @@ export const useUiStore = defineStore('ui', () => {
|
|
|
658
700
|
openModelConfig,
|
|
659
701
|
closeModelConfig,
|
|
660
702
|
openVendorCredentials,
|
|
703
|
+
setVendorCredentialsTab,
|
|
661
704
|
closeVendorCredentials,
|
|
662
705
|
openLocalModels,
|
|
663
706
|
closeLocalModels,
|