@cat-factory/app 0.37.2 → 0.38.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/app/components/auth/AuthGate.vue +8 -0
- package/app/components/auth/LoginScreen.vue +86 -8
- package/app/components/auth/ResetPasswordScreen.vue +106 -0
- package/app/components/board/nodes/BlockNode.vue +32 -13
- package/app/components/bootstrap/BootstrapModal.vue +10 -6
- package/app/components/documents/DocumentImportModal.vue +11 -7
- package/app/components/github/AddServiceFromRepoModal.vue +9 -5
- package/app/components/github/GitHubPanel.vue +8 -4
- package/app/components/kaizen/KaizenPanel.vue +7 -3
- package/app/components/layout/IntegrationsHub.vue +2 -0
- package/app/components/panels/ObservabilityPanel.vue +12 -7
- package/app/components/providers/VendorCredentialsModal.vue +10 -6
- package/app/components/sandbox/SandboxPanel.vue +30 -19
- package/app/components/settings/IssueTrackerPanel.vue +3 -1
- package/app/components/settings/LocalModeSettingsPanel.vue +7 -3
- package/app/components/settings/LocalModelEndpointsPanel.vue +7 -3
- package/app/components/settings/ModelConfigurationPanel.vue +12 -8
- package/app/components/settings/ObservabilityConnectionPanel.vue +16 -12
- package/app/components/settings/OpenRouterCatalogPanel.vue +14 -9
- package/app/components/settings/ProviderConnectionPanel.vue +4 -4
- package/app/components/settings/UserSecretsSection.vue +7 -3
- package/app/components/settings/WorkspaceSettingsPanel.vue +3 -1
- package/app/components/slack/SlackPanel.vue +2 -0
- package/app/composables/api/auth.ts +11 -0
- package/app/composables/useBlockQueries.ts +31 -9
- package/app/pages/index.vue +103 -51
- package/app/pages/reset-password.vue +7 -0
- package/app/stores/auth.ts +12 -0
- package/app/stores/board.spec.ts +30 -0
- package/app/stores/board.ts +27 -2
- package/app/stores/brainstorm.ts +11 -0
- package/app/stores/clarity.ts +11 -0
- package/app/stores/consensus.ts +7 -1
- package/app/stores/execution.spec.ts +43 -0
- package/app/stores/execution.ts +19 -0
- package/app/stores/github.ts +17 -0
- package/app/stores/requirements.ts +12 -0
- package/app/stores/workspace.ts +17 -0
- package/package.json +2 -2
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest'
|
|
2
|
+
import { useExecutionStore } from '~/stores/execution'
|
|
3
|
+
import type { ExecutionInstance } from '~/types/domain'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Minimal instance shape — the `decisionsByBlock` / `approvalsByBlock` getters only read
|
|
7
|
+
* `id`, `blockId` and each step's `{ decision, approval, agentKind }`, so a cast keeps the
|
|
8
|
+
* fixtures focused on the grouping behaviour rather than the full wire contract.
|
|
9
|
+
*/
|
|
10
|
+
function instance(id: string, blockId: string, steps: unknown[]): ExecutionInstance {
|
|
11
|
+
return { id, blockId, steps } as unknown as ExecutionInstance
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
describe('execution store gate grouping', () => {
|
|
15
|
+
let store: ReturnType<typeof useExecutionStore>
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
store = useExecutionStore()
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it('decisionsByBlock groups open (unchosen) decisions by block', () => {
|
|
21
|
+
store.hydrate([
|
|
22
|
+
instance('e1', 'b1', [
|
|
23
|
+
{ agentKind: 'coder', decision: { id: 'd1', chosen: null } },
|
|
24
|
+
{ agentKind: 'coder', decision: { id: 'd2', chosen: 'yes' } }, // chosen ⇒ excluded
|
|
25
|
+
]),
|
|
26
|
+
instance('e2', 'b2', [{ agentKind: 'architect', decision: { id: 'd3', chosen: null } }]),
|
|
27
|
+
])
|
|
28
|
+
expect(store.decisionsByBlock.get('b1')?.map((d) => d.decision.id)).toEqual(['d1'])
|
|
29
|
+
expect(store.decisionsByBlock.get('b2')?.map((d) => d.decision.id)).toEqual(['d3'])
|
|
30
|
+
expect(store.decisionsByBlock.has('missing')).toBe(false)
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('approvalsByBlock groups pending approvals by block', () => {
|
|
34
|
+
store.hydrate([
|
|
35
|
+
instance('e1', 'b1', [
|
|
36
|
+
{ agentKind: 'merger', approval: { id: 'a1', status: 'pending' } },
|
|
37
|
+
{ agentKind: 'merger', approval: { id: 'a2', status: 'approved' } }, // not pending ⇒ excluded
|
|
38
|
+
]),
|
|
39
|
+
])
|
|
40
|
+
expect(store.approvalsByBlock.get('b1')?.map((a) => a.approval.id)).toEqual(['a1'])
|
|
41
|
+
expect(store.approvalsByBlock.get('b2')).toBeUndefined()
|
|
42
|
+
})
|
|
43
|
+
})
|
package/app/stores/execution.ts
CHANGED
|
@@ -106,6 +106,23 @@ export const useExecutionStore = defineStore('execution', () => {
|
|
|
106
106
|
return out
|
|
107
107
|
})
|
|
108
108
|
|
|
109
|
+
/**
|
|
110
|
+
* Open decisions/approvals grouped by the block they belong to, so a board card
|
|
111
|
+
* resolves its own + its tasks' pending gates with O(1) lookups instead of
|
|
112
|
+
* re-filtering the global lists once per frame on every execution event.
|
|
113
|
+
*/
|
|
114
|
+
function groupByBlock<T extends { blockId: string }>(items: T[]): Map<string, T[]> {
|
|
115
|
+
const map = new Map<string, T[]>()
|
|
116
|
+
for (const item of items) {
|
|
117
|
+
const list = map.get(item.blockId)
|
|
118
|
+
if (list) list.push(item)
|
|
119
|
+
else map.set(item.blockId, [item])
|
|
120
|
+
}
|
|
121
|
+
return map
|
|
122
|
+
}
|
|
123
|
+
const decisionsByBlock = computed(() => groupByBlock(openDecisions.value))
|
|
124
|
+
const approvalsByBlock = computed(() => groupByBlock(openApprovals.value))
|
|
125
|
+
|
|
109
126
|
/**
|
|
110
127
|
* Start `pipeline` against a block; the server marks the block in-progress. A block
|
|
111
128
|
* pinned to an individual-usage model (Claude) needs the initiator's personal
|
|
@@ -279,6 +296,8 @@ export const useExecutionStore = defineStore('execution', () => {
|
|
|
279
296
|
pendingDecisionCount,
|
|
280
297
|
openDecisions,
|
|
281
298
|
openApprovals,
|
|
299
|
+
decisionsByBlock,
|
|
300
|
+
approvalsByBlock,
|
|
282
301
|
pendingApprovalCount,
|
|
283
302
|
start,
|
|
284
303
|
resolveDecision,
|
package/app/stores/github.ts
CHANGED
|
@@ -259,6 +259,22 @@ export const useGitHubStore = defineStore('github', () => {
|
|
|
259
259
|
return api.commentGitHubIssue(workspace.requireId(), repoGithubId, number, body)
|
|
260
260
|
}
|
|
261
261
|
|
|
262
|
+
/**
|
|
263
|
+
* Drop the per-workspace projection + connection state (called on workspace switch)
|
|
264
|
+
* so the prior workspace's repos/PRs/issues don't linger until the re-probe + reload
|
|
265
|
+
* land. `available` returns to `null` (unknown) so the onboarding gate re-resolves.
|
|
266
|
+
*/
|
|
267
|
+
function reset() {
|
|
268
|
+
available.value = null
|
|
269
|
+
connection.value = null
|
|
270
|
+
installations.value = []
|
|
271
|
+
repos.value = []
|
|
272
|
+
availableRepos.value = []
|
|
273
|
+
pulls.value = []
|
|
274
|
+
issues.value = []
|
|
275
|
+
branches.value = {}
|
|
276
|
+
}
|
|
277
|
+
|
|
262
278
|
return {
|
|
263
279
|
available,
|
|
264
280
|
connection,
|
|
@@ -300,5 +316,6 @@ export const useGitHubStore = defineStore('github', () => {
|
|
|
300
316
|
openPullRequest,
|
|
301
317
|
mergePullRequest,
|
|
302
318
|
comment,
|
|
319
|
+
reset,
|
|
303
320
|
}
|
|
304
321
|
})
|
|
@@ -97,6 +97,17 @@ export const useRequirementsStore = defineStore('requirements', () => {
|
|
|
97
97
|
reviews.value = { ...reviews.value, [review.blockId]: review }
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
+
/** Drop all cached reviews + in-flight state (called on workspace switch). */
|
|
101
|
+
function reset() {
|
|
102
|
+
available.value = null
|
|
103
|
+
reviews.value = {}
|
|
104
|
+
reviewing.value = new Set()
|
|
105
|
+
incorporating.value = new Set()
|
|
106
|
+
recommending.value = new Set()
|
|
107
|
+
loadingByBlock.value = new Set()
|
|
108
|
+
inFlight.clear()
|
|
109
|
+
}
|
|
110
|
+
|
|
100
111
|
function withFlag(set: typeof reviewing, key: string, on: boolean) {
|
|
101
112
|
const next = new Set(set.value)
|
|
102
113
|
if (on) next.add(key)
|
|
@@ -265,6 +276,7 @@ export const useRequirementsStore = defineStore('requirements', () => {
|
|
|
265
276
|
acceptRecommendation,
|
|
266
277
|
rejectRecommendation,
|
|
267
278
|
reRequestRecommendation,
|
|
279
|
+
reset,
|
|
268
280
|
// Patch the cache from a live `requirements` stream event.
|
|
269
281
|
upsert: store,
|
|
270
282
|
}
|
package/app/stores/workspace.ts
CHANGED
|
@@ -16,6 +16,11 @@ import { useRecurringPipelinesStore } from '~/stores/recurringPipelines'
|
|
|
16
16
|
import { useServicesStore } from '~/stores/services'
|
|
17
17
|
import { useAgentsStore } from '~/stores/agents'
|
|
18
18
|
import { useTrackerStore } from '~/stores/tracker'
|
|
19
|
+
import { useRequirementsStore } from '~/stores/requirements'
|
|
20
|
+
import { useClarityStore } from '~/stores/clarity'
|
|
21
|
+
import { useBrainstormStore } from '~/stores/brainstorm'
|
|
22
|
+
import { useConsensusStore } from '~/stores/consensus'
|
|
23
|
+
import { useGitHubStore } from '~/stores/github'
|
|
19
24
|
|
|
20
25
|
/**
|
|
21
26
|
* Owns the active workspace and bootstraps the app against the backend. On load
|
|
@@ -58,6 +63,18 @@ export const useWorkspaceStore = defineStore(
|
|
|
58
63
|
|
|
59
64
|
/** Push a snapshot into the data stores. */
|
|
60
65
|
function hydrate(snapshot: WorkspaceSnapshot) {
|
|
66
|
+
// A change of active board (or the first load) — drop the per-block caches that are
|
|
67
|
+
// NOT part of the snapshot (reviews, brainstorm/consensus sessions, the GitHub
|
|
68
|
+
// projection) so a switched-to board never shows the previous one's stale state.
|
|
69
|
+
// These are lazily reloaded/re-probed per board, so clearing on a same-board refresh
|
|
70
|
+
// would needlessly wipe an open review window — hence only on an actual id change.
|
|
71
|
+
if (workspaceId.value !== snapshot.workspace.id) {
|
|
72
|
+
useRequirementsStore().reset()
|
|
73
|
+
useClarityStore().reset()
|
|
74
|
+
useBrainstormStore().reset()
|
|
75
|
+
useConsensusStore().reset()
|
|
76
|
+
useGitHubStore().reset()
|
|
77
|
+
}
|
|
61
78
|
workspaceId.value = snapshot.workspace.id
|
|
62
79
|
spend.value = snapshot.spend ?? null
|
|
63
80
|
// Keep the board list in step (e.g. a freshly created board, or a rename).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cat-factory/app",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.38.0",
|
|
4
4
|
"description": "Reusable Nuxt layer for the Agent Architecture Board SPA (components, stores, composables, pages). Consume it from a thin deployment app via `extends: ['@cat-factory/app']` and point it at your backend with NUXT_PUBLIC_API_BASE. See deploy/frontend for an example.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"pinia-plugin-persistedstate": "^4.7.1",
|
|
33
33
|
"vue": "^3.5.38",
|
|
34
34
|
"wretch": "^3.0.9",
|
|
35
|
-
"@cat-factory/contracts": "0.
|
|
35
|
+
"@cat-factory/contracts": "0.37.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@toad-contracts/testing": "0.3.1",
|