@cat-factory/app 0.303.3 → 0.304.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/composables/api/guidedReview.ts +80 -0
- package/app/composables/useApi.ts +2 -0
- package/app/composables/usePipelineErrorToast.ts +4 -0
- package/app/composables/useWorkspaceStream.ts +4 -0
- package/app/composables/workspaceStream/applyWorkspaceEvent.spec.ts +2 -0
- package/app/composables/workspaceStream/applyWorkspaceEvent.ts +6 -0
- package/app/stores/guidedReview.spec.ts +148 -0
- package/app/stores/guidedReview.ts +200 -0
- package/app/stores/workspace/hydrate.ts +2 -0
- package/app/types/domain.ts +1 -0
- package/app/types/guided-review.ts +14 -0
- package/i18n/locales/de.json +4 -2
- package/i18n/locales/en.json +4 -2
- package/i18n/locales/es.json +4 -2
- package/i18n/locales/fr.json +4 -2
- package/i18n/locales/he.json +4 -2
- package/i18n/locales/it.json +4 -2
- package/i18n/locales/ja.json +4 -2
- package/i18n/locales/pl.json +4 -2
- package/i18n/locales/tr.json +4 -2
- package/i18n/locales/uk.json +4 -2
- package/package.json +2 -2
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import {
|
|
2
|
+
askGuidedReviewContract,
|
|
3
|
+
deleteGuidedReviewContract,
|
|
4
|
+
getGuidedReviewContract,
|
|
5
|
+
getGuidedReviewThreadContract,
|
|
6
|
+
listGuidedReviewsContract,
|
|
7
|
+
openGuidedReviewContract,
|
|
8
|
+
openGuidedReviewThreadContract,
|
|
9
|
+
refreshGuidedReviewContract,
|
|
10
|
+
requestGuidedReviewDraftsContract,
|
|
11
|
+
} from '@cat-factory/contracts'
|
|
12
|
+
import type {
|
|
13
|
+
AskGuidedReviewInput,
|
|
14
|
+
OpenGuidedReviewInput,
|
|
15
|
+
OpenGuidedReviewThreadInput,
|
|
16
|
+
} from '~/types/domain'
|
|
17
|
+
import type { ApiContext } from './context'
|
|
18
|
+
|
|
19
|
+
/** Guided PR review: sessions, their threads, questions and comment-draft requests. */
|
|
20
|
+
export function guidedReviewApi({ send, ws }: ApiContext) {
|
|
21
|
+
return {
|
|
22
|
+
openGuidedReview: (workspaceId: string, body: OpenGuidedReviewInput) =>
|
|
23
|
+
send(openGuidedReviewContract, { pathPrefix: ws(workspaceId), body }),
|
|
24
|
+
|
|
25
|
+
listGuidedReviews: (
|
|
26
|
+
workspaceId: string,
|
|
27
|
+
query: { repoId?: string; prNumber?: string; mine?: 'true' | 'false' } = {},
|
|
28
|
+
) => send(listGuidedReviewsContract, { pathPrefix: ws(workspaceId), queryParams: query }),
|
|
29
|
+
|
|
30
|
+
getGuidedReview: (workspaceId: string, sessionId: string) =>
|
|
31
|
+
send(getGuidedReviewContract, { pathPrefix: ws(workspaceId), pathParams: { sessionId } }),
|
|
32
|
+
|
|
33
|
+
deleteGuidedReview: (workspaceId: string, sessionId: string) =>
|
|
34
|
+
send(deleteGuidedReviewContract, { pathPrefix: ws(workspaceId), pathParams: { sessionId } }),
|
|
35
|
+
|
|
36
|
+
refreshGuidedReview: (workspaceId: string, sessionId: string) =>
|
|
37
|
+
send(refreshGuidedReviewContract, { pathPrefix: ws(workspaceId), pathParams: { sessionId } }),
|
|
38
|
+
|
|
39
|
+
openGuidedReviewThread: (
|
|
40
|
+
workspaceId: string,
|
|
41
|
+
sessionId: string,
|
|
42
|
+
body: OpenGuidedReviewThreadInput,
|
|
43
|
+
) =>
|
|
44
|
+
send(openGuidedReviewThreadContract, {
|
|
45
|
+
pathPrefix: ws(workspaceId),
|
|
46
|
+
pathParams: { sessionId },
|
|
47
|
+
body,
|
|
48
|
+
}),
|
|
49
|
+
|
|
50
|
+
getGuidedReviewThread: (workspaceId: string, sessionId: string, threadId: string) =>
|
|
51
|
+
send(getGuidedReviewThreadContract, {
|
|
52
|
+
pathPrefix: ws(workspaceId),
|
|
53
|
+
pathParams: { sessionId, threadId },
|
|
54
|
+
}),
|
|
55
|
+
|
|
56
|
+
askGuidedReview: (
|
|
57
|
+
workspaceId: string,
|
|
58
|
+
sessionId: string,
|
|
59
|
+
threadId: string,
|
|
60
|
+
body: AskGuidedReviewInput,
|
|
61
|
+
) =>
|
|
62
|
+
send(askGuidedReviewContract, {
|
|
63
|
+
pathPrefix: ws(workspaceId),
|
|
64
|
+
pathParams: { sessionId, threadId },
|
|
65
|
+
body,
|
|
66
|
+
}),
|
|
67
|
+
|
|
68
|
+
requestGuidedReviewDrafts: (
|
|
69
|
+
workspaceId: string,
|
|
70
|
+
sessionId: string,
|
|
71
|
+
threadId: string,
|
|
72
|
+
instructions = '',
|
|
73
|
+
) =>
|
|
74
|
+
send(requestGuidedReviewDraftsContract, {
|
|
75
|
+
pathPrefix: ws(workspaceId),
|
|
76
|
+
pathParams: { sessionId, threadId },
|
|
77
|
+
body: { instructions },
|
|
78
|
+
}),
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -29,6 +29,7 @@ import { humanTestApi } from './api/humanTest'
|
|
|
29
29
|
import { infraHandlersApi } from './api/infraHandlers'
|
|
30
30
|
import { initiativeApi } from './api/initiative'
|
|
31
31
|
import { docInterviewApi } from './api/docInterview'
|
|
32
|
+
import { guidedReviewApi } from './api/guidedReview'
|
|
32
33
|
import { visualConfirmApi } from './api/visualConfirm'
|
|
33
34
|
import { kaizenApi } from './api/kaizen'
|
|
34
35
|
import { localSettingsApi } from './api/localSettings'
|
|
@@ -160,6 +161,7 @@ export function useApi() {
|
|
|
160
161
|
...infraHandlersApi(ctx),
|
|
161
162
|
...initiativeApi(ctx),
|
|
162
163
|
...docInterviewApi(ctx),
|
|
164
|
+
...guidedReviewApi(ctx),
|
|
163
165
|
...provisioningLogsApi(ctx),
|
|
164
166
|
...releaseHealthApi(ctx),
|
|
165
167
|
...validationChecksApi(ctx),
|
|
@@ -171,6 +171,10 @@ const CONFLICT_INFO: Record<Exclude<ConflictReason, BespokeConflictReason>, Conf
|
|
|
171
171
|
titleKey: 'errors.conflict.title.delegated_claim_missing',
|
|
172
172
|
descriptionKey: 'errors.conflict.description.delegated_claim_missing',
|
|
173
173
|
},
|
|
174
|
+
thread_busy: {
|
|
175
|
+
titleKey: 'errors.conflict.title.thread_busy',
|
|
176
|
+
descriptionKey: 'errors.conflict.description.thread_busy',
|
|
177
|
+
},
|
|
174
178
|
task_limit_reached: {
|
|
175
179
|
titleKey: 'errors.conflict.title.task_limit_reached',
|
|
176
180
|
descriptionKey: 'errors.conflict.description.task_limit_reached',
|
|
@@ -34,6 +34,7 @@ export function useWorkspaceStream() {
|
|
|
34
34
|
const kaizen = useKaizenStore()
|
|
35
35
|
const initiatives = useInitiativesStore()
|
|
36
36
|
const docInterview = useDocInterviewStore()
|
|
37
|
+
const guidedReview = useGuidedReviewStore()
|
|
37
38
|
const api = useApi()
|
|
38
39
|
const apiBase = useRuntimeConfig().public.apiBase
|
|
39
40
|
|
|
@@ -89,6 +90,7 @@ export function useWorkspaceStream() {
|
|
|
89
90
|
upsertKaizen: (g) => kaizen.upsert(g),
|
|
90
91
|
upsertInitiative: (i) => initiatives.upsert(i),
|
|
91
92
|
upsertDocInterview: (s) => docInterview.upsert(s),
|
|
93
|
+
guidedReviewChanged: (change) => void guidedReview.applyChange(change),
|
|
92
94
|
refreshBoard: () => coarse.schedule(),
|
|
93
95
|
}
|
|
94
96
|
|
|
@@ -148,6 +150,8 @@ export function useWorkspaceStream() {
|
|
|
148
150
|
// failure (`coarse.withRetry`) so a reconnect no longer presents as fully live while
|
|
149
151
|
// silently missing everything from the outage; `connected` is still set even if every
|
|
150
152
|
// retry fails (we ARE connected; a refresh error must not wedge the indicator/tests).
|
|
153
|
+
// A guided review is not in the snapshot, so whatever this tab has loaded refetches itself.
|
|
154
|
+
void guidedReview.resync()
|
|
151
155
|
void coarse.withRetry(workspaceId).finally(() => {
|
|
152
156
|
// A workspace switch (or stop()) may have happened while the refresh was in
|
|
153
157
|
// flight — don't announce a connection for a socket we've since abandoned.
|
|
@@ -30,6 +30,7 @@ function targets(): WorkspaceEventTargets & { calls: string[] } {
|
|
|
30
30
|
upsertKaizen: record('upsertKaizen'),
|
|
31
31
|
upsertInitiative: record('upsertInitiative'),
|
|
32
32
|
upsertDocInterview: record('upsertDocInterview'),
|
|
33
|
+
guidedReviewChanged: record('guidedReviewChanged'),
|
|
33
34
|
refreshBoard: record('refreshBoard'),
|
|
34
35
|
}
|
|
35
36
|
}
|
|
@@ -129,6 +130,7 @@ describe('applyWorkspaceEvent: the other branches', () => {
|
|
|
129
130
|
[{ type: 'kaizen', grading: {} as never, at: 1 }, 'upsertKaizen'],
|
|
130
131
|
[{ type: 'initiative', initiative: {} as never, at: 1 }, 'upsertInitiative'],
|
|
131
132
|
[{ type: 'docInterview', session: {} as never, at: 1 }, 'upsertDocInterview'],
|
|
133
|
+
[{ type: 'guidedReview', change: {} as never, at: 1 }, 'guidedReviewChanged'],
|
|
132
134
|
]
|
|
133
135
|
|
|
134
136
|
for (const [event, expected] of cases) {
|
|
@@ -16,6 +16,7 @@ import type { BrainstormSession } from '~/types/brainstorm'
|
|
|
16
16
|
import type { ClarityReview } from '~/types/clarity'
|
|
17
17
|
import type { ConsensusSession } from '~/types/consensus'
|
|
18
18
|
import type { DocInterviewSession } from '~/types/doc-interview'
|
|
19
|
+
import type { GuidedReviewChange } from '~/types/guided-review'
|
|
19
20
|
import type { RequirementReview } from '~/types/requirements'
|
|
20
21
|
|
|
21
22
|
/**
|
|
@@ -44,6 +45,8 @@ export interface WorkspaceEventTargets {
|
|
|
44
45
|
upsertKaizen: (g: KaizenGrading) => void
|
|
45
46
|
upsertInitiative: (i: Initiative) => void
|
|
46
47
|
upsertDocInterview: (s: DocInterviewSession) => void
|
|
48
|
+
/** A guided review moved; the store refetches only what this client has loaded. */
|
|
49
|
+
guidedReviewChanged: (change: GuidedReviewChange) => void
|
|
47
50
|
/** Debounced full `workspace.refresh()`: the fallback for a change no payload can state. */
|
|
48
51
|
refreshBoard: () => void
|
|
49
52
|
}
|
|
@@ -151,6 +154,9 @@ export function applyWorkspaceEvent(event: WorkspaceEvent, to: WorkspaceEventTar
|
|
|
151
154
|
// convergence): patch the cache so an open interview window reflects it live.
|
|
152
155
|
to.upsertDocInterview(event.session)
|
|
153
156
|
return
|
|
157
|
+
case 'guidedReview':
|
|
158
|
+
to.guidedReviewChanged(event.change)
|
|
159
|
+
return
|
|
154
160
|
default:
|
|
155
161
|
return dropUnknownEvent(event)
|
|
156
162
|
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest'
|
|
2
|
+
import { ApiError } from '~/composables/api/errors'
|
|
3
|
+
import { useGuidedReviewStore } from '~/stores/guidedReview'
|
|
4
|
+
import { useWorkspaceStore } from '~/stores/workspace'
|
|
5
|
+
|
|
6
|
+
function view(sessionId: string, title: string) {
|
|
7
|
+
return {
|
|
8
|
+
session: { id: sessionId, prTitle: title },
|
|
9
|
+
threads: [],
|
|
10
|
+
drafts: [],
|
|
11
|
+
} as never
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function threadView(threadId: string, messages: number) {
|
|
15
|
+
return {
|
|
16
|
+
thread: { id: threadId, sessionId: 's1' },
|
|
17
|
+
messages: Array.from({ length: messages }, (_, i) => ({ id: `m${i}` })),
|
|
18
|
+
} as never
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function deferred<T>() {
|
|
22
|
+
let resolve!: (value: T) => void
|
|
23
|
+
const promise = new Promise<T>((r) => (resolve = r))
|
|
24
|
+
return { promise, resolve }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
describe('guided review store', () => {
|
|
28
|
+
it('refetches only what this client has loaded when a change arrives', async () => {
|
|
29
|
+
const getGuidedReview = vi.fn(async (_ws: string, id: string) => view(id, 'fresh'))
|
|
30
|
+
const getGuidedReviewThread = vi.fn(async () => threadView('t1', 2))
|
|
31
|
+
vi.stubGlobal('useApi', () => ({ getGuidedReview, getGuidedReviewThread }))
|
|
32
|
+
useWorkspaceStore().workspaceId = 'ws_1'
|
|
33
|
+
const store = useGuidedReviewStore()
|
|
34
|
+
|
|
35
|
+
await store.applyChange({ sessionId: 'elsewhere', scope: 'thread', threadId: 't9' })
|
|
36
|
+
expect(getGuidedReview).not.toHaveBeenCalled()
|
|
37
|
+
expect(getGuidedReviewThread).not.toHaveBeenCalled()
|
|
38
|
+
|
|
39
|
+
await store.loadSession('s1')
|
|
40
|
+
await store.loadThread('s1', 't1')
|
|
41
|
+
getGuidedReview.mockClear()
|
|
42
|
+
getGuidedReviewThread.mockClear()
|
|
43
|
+
await store.applyChange({ sessionId: 's1', scope: 'thread', threadId: 't1' })
|
|
44
|
+
expect(getGuidedReview).toHaveBeenCalledTimes(1)
|
|
45
|
+
expect(getGuidedReviewThread).toHaveBeenCalledTimes(1)
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it('drops a slow reply that a newer fetch overtook', async () => {
|
|
49
|
+
const slow = deferred<never>()
|
|
50
|
+
const replies = [slow.promise, Promise.resolve(view('s1', 'newer'))]
|
|
51
|
+
vi.stubGlobal('useApi', () => ({ getGuidedReview: vi.fn(() => replies.shift()) }))
|
|
52
|
+
useWorkspaceStore().workspaceId = 'ws_1'
|
|
53
|
+
const store = useGuidedReviewStore()
|
|
54
|
+
|
|
55
|
+
const first = store.loadSession('s1')
|
|
56
|
+
await store.loadSession('s1')
|
|
57
|
+
slow.resolve(view('s1', 'stale'))
|
|
58
|
+
await first
|
|
59
|
+
expect(store.sessions.s1?.session.prTitle).toBe('newer')
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it('lets a refresh win over a load that started before it', async () => {
|
|
63
|
+
const slow = deferred<never>()
|
|
64
|
+
vi.stubGlobal('useApi', () => ({
|
|
65
|
+
getGuidedReview: vi.fn(() => slow.promise),
|
|
66
|
+
refreshGuidedReview: vi.fn(async () => view('s1', 'refreshed')),
|
|
67
|
+
}))
|
|
68
|
+
useWorkspaceStore().workspaceId = 'ws_1'
|
|
69
|
+
const store = useGuidedReviewStore()
|
|
70
|
+
|
|
71
|
+
const load = store.loadSession('s1')
|
|
72
|
+
await store.refresh('s1')
|
|
73
|
+
slow.resolve(view('s1', 'stale'))
|
|
74
|
+
await load
|
|
75
|
+
expect(store.sessions.s1?.session.prTitle).toBe('refreshed')
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
it('does not bring back a deleted session through a load still in flight', async () => {
|
|
79
|
+
const slow = deferred<never>()
|
|
80
|
+
vi.stubGlobal('useApi', () => ({ getGuidedReview: vi.fn(() => slow.promise) }))
|
|
81
|
+
useWorkspaceStore().workspaceId = 'ws_1'
|
|
82
|
+
const store = useGuidedReviewStore()
|
|
83
|
+
|
|
84
|
+
const load = store.loadSession('s1')
|
|
85
|
+
await store.applyChange({ sessionId: 's1', scope: 'deleted' })
|
|
86
|
+
slow.resolve(view('s1', 'stale'))
|
|
87
|
+
await load
|
|
88
|
+
expect(store.sessions.s1).toBeUndefined()
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it('refetches the thread a drafts change names', async () => {
|
|
92
|
+
const getGuidedReview = vi.fn(async (_ws: string, id: string) => view(id, 'x'))
|
|
93
|
+
const getGuidedReviewThread = vi.fn(async () => threadView('t1', 3))
|
|
94
|
+
vi.stubGlobal('useApi', () => ({ getGuidedReview, getGuidedReviewThread }))
|
|
95
|
+
useWorkspaceStore().workspaceId = 'ws_1'
|
|
96
|
+
const store = useGuidedReviewStore()
|
|
97
|
+
await store.loadSession('s1')
|
|
98
|
+
await store.loadThread('s1', 't1')
|
|
99
|
+
getGuidedReviewThread.mockClear()
|
|
100
|
+
await store.applyChange({ sessionId: 's1', scope: 'drafts', threadId: 't1' })
|
|
101
|
+
expect(getGuidedReviewThread).toHaveBeenCalledTimes(1)
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
it('records a failed background refetch and clears it when the session lands again', async () => {
|
|
105
|
+
const failure = new ApiError(503, { error: { code: 'unavailable' } })
|
|
106
|
+
const getGuidedReview = vi
|
|
107
|
+
.fn()
|
|
108
|
+
.mockResolvedValueOnce(view('s1', 'x'))
|
|
109
|
+
.mockRejectedValueOnce(failure)
|
|
110
|
+
.mockResolvedValueOnce(view('s1', 'y'))
|
|
111
|
+
vi.stubGlobal('useApi', () => ({ getGuidedReview }))
|
|
112
|
+
useWorkspaceStore().workspaceId = 'ws_1'
|
|
113
|
+
const store = useGuidedReviewStore()
|
|
114
|
+
await store.loadSession('s1')
|
|
115
|
+
await store.applyChange({ sessionId: 's1', scope: 'session' })
|
|
116
|
+
expect(store.sessions.s1?.session.prTitle).toBe('x')
|
|
117
|
+
expect(store.refetchFailures.s1).toBe(failure)
|
|
118
|
+
await store.applyChange({ sessionId: 's1', scope: 'session' })
|
|
119
|
+
expect(store.refetchFailures.s1).toBeUndefined()
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
it('forgets a session whose refetch answers 404', async () => {
|
|
123
|
+
const getGuidedReview = vi
|
|
124
|
+
.fn()
|
|
125
|
+
.mockResolvedValueOnce(view('s1', 'x'))
|
|
126
|
+
.mockRejectedValueOnce(new ApiError(404, { error: { code: 'not_found' } }))
|
|
127
|
+
vi.stubGlobal('useApi', () => ({ getGuidedReview }))
|
|
128
|
+
useWorkspaceStore().workspaceId = 'ws_1'
|
|
129
|
+
const store = useGuidedReviewStore()
|
|
130
|
+
await store.loadSession('s1')
|
|
131
|
+
await store.applyChange({ sessionId: 's1', scope: 'session' })
|
|
132
|
+
expect(store.sessions.s1).toBeUndefined()
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
it('forgets a deleted session and its threads', async () => {
|
|
136
|
+
vi.stubGlobal('useApi', () => ({
|
|
137
|
+
getGuidedReview: vi.fn(async () => view('s1', 'x')),
|
|
138
|
+
getGuidedReviewThread: vi.fn(async () => threadView('t1', 1)),
|
|
139
|
+
}))
|
|
140
|
+
useWorkspaceStore().workspaceId = 'ws_1'
|
|
141
|
+
const store = useGuidedReviewStore()
|
|
142
|
+
await store.loadSession('s1')
|
|
143
|
+
await store.loadThread('s1', 't1')
|
|
144
|
+
await store.applyChange({ sessionId: 's1', scope: 'deleted' })
|
|
145
|
+
expect(store.sessions.s1).toBeUndefined()
|
|
146
|
+
expect(store.threads.t1).toBeUndefined()
|
|
147
|
+
})
|
|
148
|
+
})
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { defineStore } from 'pinia'
|
|
2
|
+
import { ref } from 'vue'
|
|
3
|
+
import type {
|
|
4
|
+
AskGuidedReviewInput,
|
|
5
|
+
GuidedReviewChange,
|
|
6
|
+
GuidedReviewSessionView,
|
|
7
|
+
GuidedReviewThreadView,
|
|
8
|
+
OpenGuidedReviewInput,
|
|
9
|
+
OpenGuidedReviewThreadInput,
|
|
10
|
+
} from '~/types/domain'
|
|
11
|
+
import { ApiError } from '~/composables/api/errors'
|
|
12
|
+
import { useWorkspaceStore } from '~/stores/workspace'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Guided PR review sessions and the threads a window has opened, loaded on demand and kept live
|
|
16
|
+
* by `guidedReview` stream events. An event carries ids only, so `applyChange` refetches what is
|
|
17
|
+
* loaded here and ignores the rest. Every fetch and every write takes a ticket, and a fetch lands
|
|
18
|
+
* only while its ticket is the newest for its key, so a change that arrives mid-fetch is never
|
|
19
|
+
* lost to a stale reply and a forgotten session is never brought back by one.
|
|
20
|
+
*/
|
|
21
|
+
export const useGuidedReviewStore = defineStore('guidedReview', () => {
|
|
22
|
+
const api = useApi()
|
|
23
|
+
const workspace = useWorkspaceStore()
|
|
24
|
+
|
|
25
|
+
const sessions = ref<Record<string, GuidedReviewSessionView>>({})
|
|
26
|
+
const threads = ref<Record<string, GuidedReviewThreadView>>({})
|
|
27
|
+
/**
|
|
28
|
+
* The last error of a background refetch per session, cleared when that session or one of its
|
|
29
|
+
* threads lands again, so a window can say its view may be out of date.
|
|
30
|
+
*/
|
|
31
|
+
const refetchFailures = ref<Record<string, unknown>>({})
|
|
32
|
+
// One counter for every key and never reset, so a key dropped by `forget` or `reset` is never
|
|
33
|
+
// landed on by a fetch that was in flight when it went.
|
|
34
|
+
let issued = 0
|
|
35
|
+
const newest = new Map<string, number>()
|
|
36
|
+
|
|
37
|
+
const sessionKey = (sessionId: string) => `s:${sessionId}`
|
|
38
|
+
const threadKey = (sessionId: string, threadId: string) => `t:${sessionId}:${threadId}`
|
|
39
|
+
|
|
40
|
+
function claim(key: string): number {
|
|
41
|
+
issued += 1
|
|
42
|
+
newest.set(key, issued)
|
|
43
|
+
return issued
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Run `fetch` for `key`, landing its result only if no newer fetch or write for it started. */
|
|
47
|
+
async function latest<T>(key: string, fetch: () => Promise<T>, land: (value: T) => void) {
|
|
48
|
+
const ticket = claim(key)
|
|
49
|
+
const value = await fetch()
|
|
50
|
+
if (newest.get(key) === ticket) land(value)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function putSession(view: GuidedReviewSessionView) {
|
|
54
|
+
sessions.value[view.session.id] = view
|
|
55
|
+
delete refetchFailures.value[view.session.id]
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function putThread(view: GuidedReviewThreadView) {
|
|
59
|
+
threads.value[view.thread.id] = view
|
|
60
|
+
delete refetchFailures.value[view.thread.sessionId]
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function loadSession(sessionId: string): Promise<void> {
|
|
64
|
+
const ws = workspace.requireId()
|
|
65
|
+
return latest(sessionKey(sessionId), () => api.getGuidedReview(ws, sessionId), putSession)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function loadThread(sessionId: string, threadId: string): Promise<void> {
|
|
69
|
+
const ws = workspace.requireId()
|
|
70
|
+
return latest(
|
|
71
|
+
threadKey(sessionId, threadId),
|
|
72
|
+
() => api.getGuidedReviewThread(ws, sessionId, threadId),
|
|
73
|
+
putThread,
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function open(input: OpenGuidedReviewInput): Promise<GuidedReviewSessionView> {
|
|
78
|
+
const view = await api.openGuidedReview(workspace.requireId(), input)
|
|
79
|
+
claim(sessionKey(view.session.id))
|
|
80
|
+
putSession(view)
|
|
81
|
+
return view
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function refresh(sessionId: string): Promise<void> {
|
|
85
|
+
const ws = workspace.requireId()
|
|
86
|
+
return latest(sessionKey(sessionId), () => api.refreshGuidedReview(ws, sessionId), putSession)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async function remove(sessionId: string): Promise<void> {
|
|
90
|
+
await api.deleteGuidedReview(workspace.requireId(), sessionId)
|
|
91
|
+
forget(sessionId)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async function openThread(
|
|
95
|
+
sessionId: string,
|
|
96
|
+
input: OpenGuidedReviewThreadInput,
|
|
97
|
+
): Promise<GuidedReviewThreadView> {
|
|
98
|
+
const view = await api.openGuidedReviewThread(workspace.requireId(), sessionId, input)
|
|
99
|
+
claim(threadKey(sessionId, view.thread.id))
|
|
100
|
+
putThread(view)
|
|
101
|
+
await loadSession(sessionId)
|
|
102
|
+
return view
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function ask(sessionId: string, threadId: string, input: AskGuidedReviewInput) {
|
|
106
|
+
await api.askGuidedReview(workspace.requireId(), sessionId, threadId, input)
|
|
107
|
+
await Promise.all([loadThread(sessionId, threadId), loadSession(sessionId)])
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function requestDrafts(sessionId: string, threadId: string, instructions = '') {
|
|
111
|
+
await api.requestGuidedReviewDrafts(workspace.requireId(), sessionId, threadId, instructions)
|
|
112
|
+
await Promise.all([loadThread(sessionId, threadId), loadSession(sessionId)])
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function forget(sessionId: string) {
|
|
116
|
+
delete sessions.value[sessionId]
|
|
117
|
+
delete refetchFailures.value[sessionId]
|
|
118
|
+
newest.delete(sessionKey(sessionId))
|
|
119
|
+
const threadPrefix = threadKey(sessionId, '')
|
|
120
|
+
for (const key of newest.keys()) {
|
|
121
|
+
if (key.startsWith(threadPrefix)) newest.delete(key)
|
|
122
|
+
}
|
|
123
|
+
for (const [id, view] of Object.entries(threads.value)) {
|
|
124
|
+
if (view.thread.sessionId === sessionId) delete threads.value[id]
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Drop everything on a board switch; fetches still in flight for the old board never land. */
|
|
129
|
+
function reset() {
|
|
130
|
+
sessions.value = {}
|
|
131
|
+
threads.value = {}
|
|
132
|
+
refetchFailures.value = {}
|
|
133
|
+
newest.clear()
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* A refetch nobody awaits (a live event, a reconnect). A session that answers 404 is gone and
|
|
138
|
+
* is forgotten; any other failure keeps the last view and is recorded in `refetchFailures`.
|
|
139
|
+
*/
|
|
140
|
+
async function follow(sessionId: string, load: () => Promise<void>): Promise<void> {
|
|
141
|
+
try {
|
|
142
|
+
await load()
|
|
143
|
+
} catch (error) {
|
|
144
|
+
if (error instanceof ApiError && error.statusCode === 404) {
|
|
145
|
+
forget(sessionId)
|
|
146
|
+
return
|
|
147
|
+
}
|
|
148
|
+
if (sessions.value[sessionId]) refetchFailures.value[sessionId] = error
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** Follow a live `guidedReview` event: refetch what this client has loaded, nothing else. */
|
|
153
|
+
async function applyChange(change: GuidedReviewChange): Promise<void> {
|
|
154
|
+
if (change.scope === 'deleted') {
|
|
155
|
+
forget(change.sessionId)
|
|
156
|
+
return
|
|
157
|
+
}
|
|
158
|
+
const { sessionId } = change
|
|
159
|
+
const loads: Promise<void>[] = []
|
|
160
|
+
// A thread change also moves the session's summary (its pending answer), so both refetch.
|
|
161
|
+
if (sessions.value[sessionId]) {
|
|
162
|
+
loads.push(follow(sessionId, () => loadSession(sessionId)))
|
|
163
|
+
}
|
|
164
|
+
if (change.scope === 'thread' || change.scope === 'drafts') {
|
|
165
|
+
const { threadId } = change
|
|
166
|
+
if (threads.value[threadId]) {
|
|
167
|
+
loads.push(follow(sessionId, () => loadThread(sessionId, threadId)))
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
await Promise.all(loads)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** Refetch everything loaded here, for events missed while the stream was disconnected. */
|
|
174
|
+
async function resync(): Promise<void> {
|
|
175
|
+
const loads = [
|
|
176
|
+
...Object.keys(sessions.value).map((id) => follow(id, () => loadSession(id))),
|
|
177
|
+
...Object.values(threads.value).map(({ thread }) =>
|
|
178
|
+
follow(thread.sessionId, () => loadThread(thread.sessionId, thread.id)),
|
|
179
|
+
),
|
|
180
|
+
]
|
|
181
|
+
await Promise.all(loads)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
sessions,
|
|
186
|
+
threads,
|
|
187
|
+
refetchFailures,
|
|
188
|
+
loadSession,
|
|
189
|
+
loadThread,
|
|
190
|
+
open,
|
|
191
|
+
refresh,
|
|
192
|
+
remove,
|
|
193
|
+
openThread,
|
|
194
|
+
ask,
|
|
195
|
+
requestDrafts,
|
|
196
|
+
applyChange,
|
|
197
|
+
resync,
|
|
198
|
+
reset,
|
|
199
|
+
}
|
|
200
|
+
})
|
|
@@ -10,6 +10,7 @@ import { useEnvironmentTestStore } from '~/stores/environmentTest'
|
|
|
10
10
|
import { useExecutionStore } from '~/stores/execution'
|
|
11
11
|
import { useFragmentsStore } from '~/stores/fragments'
|
|
12
12
|
import { useGitHubStore } from '~/stores/github'
|
|
13
|
+
import { useGuidedReviewStore } from '~/stores/guidedReview'
|
|
13
14
|
import { useInitiativesStore } from '~/stores/initiative'
|
|
14
15
|
import { useKaizenStore } from '~/stores/kaizen'
|
|
15
16
|
import { useObservabilityStore } from '~/stores/observability'
|
|
@@ -45,6 +46,7 @@ export function resetPerBoardCaches() {
|
|
|
45
46
|
useGitHubStore().reset()
|
|
46
47
|
useInitiativesStore().reset()
|
|
47
48
|
useDocInterviewStore().reset()
|
|
49
|
+
useGuidedReviewStore().reset()
|
|
48
50
|
// The per-RUN observability + Kaizen caches. An execution id belongs to the board that owns it
|
|
49
51
|
// and neither store is part of the snapshot, so nothing else ever evicted a key: a session that
|
|
50
52
|
// visited several boards kept every run it had ever opened a panel on.
|
package/app/types/domain.ts
CHANGED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Guided PR review wire shapes, re-exported from the shared contracts package. The SPA imports
|
|
2
|
+
// these through `~/types/domain` like every other domain type.
|
|
3
|
+
export type {
|
|
4
|
+
AskGuidedReviewInput,
|
|
5
|
+
GuidedReviewChange,
|
|
6
|
+
GuidedReviewCommentDraft,
|
|
7
|
+
GuidedReviewExchange,
|
|
8
|
+
GuidedReviewMessage,
|
|
9
|
+
GuidedReviewSession,
|
|
10
|
+
GuidedReviewSessionView,
|
|
11
|
+
GuidedReviewThreadView,
|
|
12
|
+
OpenGuidedReviewInput,
|
|
13
|
+
OpenGuidedReviewThreadInput,
|
|
14
|
+
} from '@cat-factory/contracts'
|
package/i18n/locales/de.json
CHANGED
|
@@ -6313,7 +6313,8 @@
|
|
|
6313
6313
|
"no_host_frame": "Diese Expedition liegt unter keinem Service",
|
|
6314
6314
|
"delegated_executor_unwired": "Externer Executor nicht registriert",
|
|
6315
6315
|
"delegated_step_async_only": "Dieser Schritt wird abgefragt, nicht abgewartet",
|
|
6316
|
-
"delegated_claim_missing": "Für diesen Schritt gibt es keine externe Arbeit zu prüfen"
|
|
6316
|
+
"delegated_claim_missing": "Für diesen Schritt gibt es keine externe Arbeit zu prüfen",
|
|
6317
|
+
"thread_busy": "Dieser Thread antwortet noch"
|
|
6317
6318
|
},
|
|
6318
6319
|
"description": {
|
|
6319
6320
|
"dependencies_unmet": "Diese Aufgabe hängt von anderen ab, die noch nicht abgeschlossen sind. Schließe sie ab oder gib sie frei und starte dann erneut.",
|
|
@@ -6370,7 +6371,8 @@
|
|
|
6370
6371
|
"no_host_frame": "Eine Behebungsaufgabe braucht einen Service als Zuhause und ein Repository zum Beheben. Verschieben Sie die Expeditionsaufgabe unter einen Service-Rahmen und versuchen Sie es erneut.",
|
|
6371
6372
|
"delegated_executor_unwired": "Ein Schritt dieser Pipeline läuft auf einem externen Executor, den diese Installation nicht registriert; es gibt also kein Ziel für die Arbeit. Registriere den Executor im Code oder wähle für den Schritt einen, der hier läuft.",
|
|
6372
6373
|
"delegated_step_async_only": "Dieser Schritt läuft auf einem externen Executor und kann Stunden dauern. Er wird deshalb angestoßen und danach abgefragt, statt innerhalb einer Anfrage ausgeführt zu werden. Starte den Lauf über das Board; wenn du aus eigenem Code hierherkommst, steuere den Schritt über den asynchronen Pfad.",
|
|
6373
|
-
"delegated_claim_missing": "Der Eintrag des Laufs darüber, was an den externen Executor geschickt wurde, fehlt, also gibt es nichts abzufragen. Am Executor selbst ist nichts falsch. Lade den Lauf neu und starte den Schritt erneut, falls er weiterhin hängt."
|
|
6374
|
+
"delegated_claim_missing": "Der Eintrag des Laufs darüber, was an den externen Executor geschickt wurde, fehlt, also gibt es nichts abzufragen. Am Executor selbst ist nichts falsch. Lade den Lauf neu und starte den Schritt erneut, falls er weiterhin hängt.",
|
|
6375
|
+
"thread_busy": "Die vorherige Frage in diesem Thread ist noch nicht beantwortet. Warte auf die Antwort oder frag in einem anderen Thread."
|
|
6374
6376
|
},
|
|
6375
6377
|
"action": {
|
|
6376
6378
|
"connectGitHub": "GitHub verbinden",
|
package/i18n/locales/en.json
CHANGED
|
@@ -803,7 +803,8 @@
|
|
|
803
803
|
"no_host_frame": "This expedition is not under a service",
|
|
804
804
|
"delegated_executor_unwired": "External executor not registered",
|
|
805
805
|
"delegated_step_async_only": "This step is polled, not awaited",
|
|
806
|
-
"delegated_claim_missing": "This step has no external work to check"
|
|
806
|
+
"delegated_claim_missing": "This step has no external work to check",
|
|
807
|
+
"thread_busy": "This thread is still answering"
|
|
807
808
|
},
|
|
808
809
|
"description": {
|
|
809
810
|
"dependencies_unmet": "This task depends on others that aren't finished yet. Complete or unblock them, then start it again.",
|
|
@@ -863,7 +864,8 @@
|
|
|
863
864
|
"no_host_frame": "A fix task needs a service to live under and a repository to fix. Move the expedition task under a service frame and try again.",
|
|
864
865
|
"delegated_executor_unwired": "A step in this pipeline runs on an external executor this deployment does not register, so there is nowhere to send the work. Register the executor in code, or change the step to one that runs here.",
|
|
865
866
|
"delegated_step_async_only": "This step runs on an external executor, which can take hours, so it is dispatched and then polled rather than run inside one request. Start the run from the board; if you reached this from your own code, drive the step through the async path.",
|
|
866
|
-
"delegated_claim_missing": "The run’s record of what it sent to the external executor is missing, so there is nothing to poll. Nothing is wrong with the executor itself. Reload the run, and re-run the step if it is still stuck."
|
|
867
|
+
"delegated_claim_missing": "The run’s record of what it sent to the external executor is missing, so there is nothing to poll. Nothing is wrong with the executor itself. Reload the run, and re-run the step if it is still stuck.",
|
|
868
|
+
"thread_busy": "The previous question in this thread has not been answered yet. Wait for that answer, or ask in another thread."
|
|
867
869
|
},
|
|
868
870
|
"action": {
|
|
869
871
|
"connectGitHub": "Connect GitHub",
|
package/i18n/locales/es.json
CHANGED
|
@@ -725,7 +725,8 @@
|
|
|
725
725
|
"no_host_frame": "Esta expedición no está bajo un servicio",
|
|
726
726
|
"delegated_executor_unwired": "Ejecutor externo no registrado",
|
|
727
727
|
"delegated_step_async_only": "Este paso se consulta, no se espera",
|
|
728
|
-
"delegated_claim_missing": "Este paso no tiene trabajo externo que consultar"
|
|
728
|
+
"delegated_claim_missing": "Este paso no tiene trabajo externo que consultar",
|
|
729
|
+
"thread_busy": "Este hilo todavía está respondiendo"
|
|
729
730
|
},
|
|
730
731
|
"description": {
|
|
731
732
|
"dependencies_unmet": "Esta tarea depende de otras que aún no están terminadas. Complétalas o desbloquéalas y vuelve a iniciarla.",
|
|
@@ -782,7 +783,8 @@
|
|
|
782
783
|
"no_host_frame": "Una tarea de corrección necesita un servicio donde vivir y un repositorio que arreglar. Mueva la tarea de la expedición bajo un marco de servicio e inténtelo de nuevo.",
|
|
783
784
|
"delegated_executor_unwired": "Un paso de esta canalización se ejecuta en un ejecutor externo que esta instalación no registra, así que no hay dónde enviar el trabajo. Registra el ejecutor en el código o cambia el paso por uno que se ejecute aquí.",
|
|
784
785
|
"delegated_step_async_only": "Este paso se ejecuta en un ejecutor externo y puede tardar horas, así que se lanza y luego se consulta en lugar de ejecutarse dentro de una sola petición. Inicia la ejecución desde el tablero; si has llegado aquí desde tu propio código, conduce el paso por la vía asíncrona.",
|
|
785
|
-
"delegated_claim_missing": "Falta el registro de lo que la ejecución envió al ejecutor externo, así que no hay nada que consultar. El ejecutor en sí no tiene ningún problema. Recarga la ejecución y vuelve a lanzar el paso si sigue atascado."
|
|
786
|
+
"delegated_claim_missing": "Falta el registro de lo que la ejecución envió al ejecutor externo, así que no hay nada que consultar. El ejecutor en sí no tiene ningún problema. Recarga la ejecución y vuelve a lanzar el paso si sigue atascado.",
|
|
787
|
+
"thread_busy": "La pregunta anterior de este hilo aún no tiene respuesta. Espera esa respuesta o pregunta en otro hilo."
|
|
786
788
|
},
|
|
787
789
|
"action": {
|
|
788
790
|
"connectGitHub": "Conectar GitHub",
|
package/i18n/locales/fr.json
CHANGED
|
@@ -725,7 +725,8 @@
|
|
|
725
725
|
"no_host_frame": "Cette expédition n'est sous aucun service",
|
|
726
726
|
"delegated_executor_unwired": "Exécuteur externe non enregistré",
|
|
727
727
|
"delegated_step_async_only": "Cette étape est interrogée, pas attendue",
|
|
728
|
-
"delegated_claim_missing": "Cette étape n’a aucun travail externe à interroger"
|
|
728
|
+
"delegated_claim_missing": "Cette étape n’a aucun travail externe à interroger",
|
|
729
|
+
"thread_busy": "Ce fil répond encore"
|
|
729
730
|
},
|
|
730
731
|
"description": {
|
|
731
732
|
"dependencies_unmet": "Cette tâche dépend d'autres qui ne sont pas encore terminées. Terminez-les ou débloquez-les, puis relancez-la.",
|
|
@@ -782,7 +783,8 @@
|
|
|
782
783
|
"no_host_frame": "Une tâche de correction a besoin d'un service où vivre et d'un dépôt à corriger. Déplacez la tâche d'expédition sous un cadre de service et réessayez.",
|
|
783
784
|
"delegated_executor_unwired": "Une étape de ce pipeline s’exécute sur un exécuteur externe que ce déploiement n’enregistre pas : le travail n’a donc nulle part où aller. Enregistrez l’exécuteur dans le code, ou remplacez l’étape par une qui s’exécute ici.",
|
|
784
785
|
"delegated_step_async_only": "Cette étape s’exécute sur un exécuteur externe et peut durer des heures : elle est donc lancée puis interrogée, au lieu d’être exécutée dans une seule requête. Démarrez l’exécution depuis le tableau ; si vous arrivez ici depuis votre propre code, pilotez l’étape par la voie asynchrone.",
|
|
785
|
-
"delegated_claim_missing": "L’enregistrement de ce que l’exécution a envoyé à l’exécuteur externe est absent : il n’y a donc rien à interroger. L’exécuteur lui-même n’a aucun problème. Rechargez l’exécution et relancez l’étape si elle reste bloquée."
|
|
786
|
+
"delegated_claim_missing": "L’enregistrement de ce que l’exécution a envoyé à l’exécuteur externe est absent : il n’y a donc rien à interroger. L’exécuteur lui-même n’a aucun problème. Rechargez l’exécution et relancez l’étape si elle reste bloquée.",
|
|
787
|
+
"thread_busy": "La question précédente de ce fil n'a pas encore reçu de réponse. Attends cette réponse ou pose ta question dans un autre fil."
|
|
786
788
|
},
|
|
787
789
|
"action": {
|
|
788
790
|
"connectGitHub": "Connecter GitHub",
|
package/i18n/locales/he.json
CHANGED
|
@@ -725,7 +725,8 @@
|
|
|
725
725
|
"no_host_frame": "המשלחת הזו אינה תחת שירות",
|
|
726
726
|
"delegated_executor_unwired": "המריץ החיצוני אינו רשום",
|
|
727
727
|
"delegated_step_async_only": "שלב זה נבדק מעת לעת, לא ממתינים לו",
|
|
728
|
-
"delegated_claim_missing": "לשלב זה אין עבודה חיצונית לבדיקה"
|
|
728
|
+
"delegated_claim_missing": "לשלב זה אין עבודה חיצונית לבדיקה",
|
|
729
|
+
"thread_busy": "השרשור הזה עדיין עונה"
|
|
729
730
|
},
|
|
730
731
|
"description": {
|
|
731
732
|
"dependencies_unmet": "משימה זו תלויה במשימות אחרות שטרם הושלמו. השלם או שחרר אותן, ולאחר מכן הפעל אותה שוב.",
|
|
@@ -782,7 +783,8 @@
|
|
|
782
783
|
"no_host_frame": "משימת תיקון זקוקה לשירות שתחתיו תחיה ולמאגר לתקן. העבירו את משימת המשלחת תחת מסגרת שירות ונסו שוב.",
|
|
783
784
|
"delegated_executor_unwired": "שלב בצינור הזה רץ על מריץ חיצוני שההתקנה הזו אינה רושמת, ולכן אין לאן לשלוח את העבודה. רשמו את המריץ בקוד, או החליפו את השלב בשלב שרץ כאן.",
|
|
784
785
|
"delegated_step_async_only": "השלב רץ על מבצע חיצוני ועשוי להימשך שעות, ולכן הוא נשלח ואז נבדק מעת לעת במקום לרוץ בתוך בקשה אחת. התחילו את ההרצה מהלוח; אם הגעתם לכאן מקוד משלכם, הפעילו את השלב דרך המסלול האסינכרוני.",
|
|
785
|
-
"delegated_claim_missing": "הרישום של מה שההרצה שלחה למבצע החיצוני חסר, ולכן אין מה לבדוק. אין שום בעיה במבצע עצמו. טענו מחדש את ההרצה, והריצו את השלב שוב אם הוא עדיין תקוע."
|
|
786
|
+
"delegated_claim_missing": "הרישום של מה שההרצה שלחה למבצע החיצוני חסר, ולכן אין מה לבדוק. אין שום בעיה במבצע עצמו. טענו מחדש את ההרצה, והריצו את השלב שוב אם הוא עדיין תקוע.",
|
|
787
|
+
"thread_busy": "השאלה הקודמת בשרשור הזה עדיין לא נענתה. המתינו לתשובה או שאלו בשרשור אחר."
|
|
786
788
|
},
|
|
787
789
|
"action": {
|
|
788
790
|
"connectGitHub": "חבר את GitHub",
|
package/i18n/locales/it.json
CHANGED
|
@@ -6313,7 +6313,8 @@
|
|
|
6313
6313
|
"no_host_frame": "Questa spedizione non si trova sotto un servizio",
|
|
6314
6314
|
"delegated_executor_unwired": "Esecutore esterno non registrato",
|
|
6315
6315
|
"delegated_step_async_only": "Questo passo viene interrogato, non atteso",
|
|
6316
|
-
"delegated_claim_missing": "Questo passo non ha lavoro esterno da controllare"
|
|
6316
|
+
"delegated_claim_missing": "Questo passo non ha lavoro esterno da controllare",
|
|
6317
|
+
"thread_busy": "Questa discussione sta ancora rispondendo"
|
|
6317
6318
|
},
|
|
6318
6319
|
"description": {
|
|
6319
6320
|
"dependencies_unmet": "Questa attività dipende da altre non ancora completate. Completale o sbloccale, poi avviala di nuovo.",
|
|
@@ -6370,7 +6371,8 @@
|
|
|
6370
6371
|
"no_host_frame": "Un'attività di correzione ha bisogno di un servizio in cui vivere e di un repository da correggere. Sposti l'attività della spedizione sotto un frame di servizio e riprovi.",
|
|
6371
6372
|
"delegated_executor_unwired": "Un passaggio di questa pipeline viene eseguito su un esecutore esterno che questa installazione non registra, quindi il lavoro non ha una destinazione. Registra l’esecutore nel codice oppure sostituisci il passaggio con uno eseguito qui.",
|
|
6372
6373
|
"delegated_step_async_only": "Questo passo gira su un esecutore esterno e può durare ore, quindi viene avviato e poi interrogato invece di essere eseguito dentro una sola richiesta. Avvia l’esecuzione dalla board; se sei arrivato qui dal tuo codice, guida il passo attraverso il percorso asincrono.",
|
|
6373
|
-
"delegated_claim_missing": "Manca la registrazione di ciò che l’esecuzione ha inviato all’esecutore esterno, quindi non c’è nulla da interrogare. L’esecutore in sé non ha alcun problema. Ricarica l’esecuzione e rilancia il passo se resta bloccato."
|
|
6374
|
+
"delegated_claim_missing": "Manca la registrazione di ciò che l’esecuzione ha inviato all’esecutore esterno, quindi non c’è nulla da interrogare. L’esecutore in sé non ha alcun problema. Ricarica l’esecuzione e rilancia il passo se resta bloccato.",
|
|
6375
|
+
"thread_busy": "La domanda precedente in questa discussione non ha ancora una risposta. Attendi la risposta o chiedi in un'altra discussione."
|
|
6374
6376
|
},
|
|
6375
6377
|
"action": {
|
|
6376
6378
|
"connectGitHub": "Collega GitHub",
|
package/i18n/locales/ja.json
CHANGED
|
@@ -725,7 +725,8 @@
|
|
|
725
725
|
"no_host_frame": "この探索はサービスの配下にありません",
|
|
726
726
|
"delegated_executor_unwired": "外部エグゼキューターが登録されていません",
|
|
727
727
|
"delegated_step_async_only": "このステップはポーリングされます。完了を待つことはできません",
|
|
728
|
-
"delegated_claim_missing": "このステップには確認できる外部の処理がありません"
|
|
728
|
+
"delegated_claim_missing": "このステップには確認できる外部の処理がありません",
|
|
729
|
+
"thread_busy": "このスレッドはまだ回答中です"
|
|
729
730
|
},
|
|
730
731
|
"description": {
|
|
731
732
|
"dependencies_unmet": "このタスクは、まだ完了していない他のタスクに依存しています。それらを完了または解除してから、もう一度開始してください。",
|
|
@@ -782,7 +783,8 @@
|
|
|
782
783
|
"no_host_frame": "修正タスクには所属するサービスと修正対象のリポジトリが必要です。探索タスクをサービスフレームの配下へ移動してからやり直してください。",
|
|
783
784
|
"delegated_executor_unwired": "このパイプラインのステップは、このデプロイが登録していない外部エグゼキューターで実行されるため、処理の送り先がありません。エグゼキューターをコードに登録するか、ここで実行されるステップに変更してください。",
|
|
784
785
|
"delegated_step_async_only": "このステップは外部エグゼキューター上で実行され、数時間かかることがあります。そのため、単一のリクエスト内で実行するのではなく、ディスパッチしたうえでポーリングします。実行はボードから開始してください。独自のコードからここに到達した場合は、非同期パスでステップを駆動してください。",
|
|
785
|
-
"delegated_claim_missing": "外部エグゼキューターに何を送ったかという実行の記録が失われているため、ポーリングする対象がありません。エグゼキューター自体に問題はありません。実行を再読み込みし、まだ止まったままならステップを再実行してください。"
|
|
786
|
+
"delegated_claim_missing": "外部エグゼキューターに何を送ったかという実行の記録が失われているため、ポーリングする対象がありません。エグゼキューター自体に問題はありません。実行を再読み込みし、まだ止まったままならステップを再実行してください。",
|
|
787
|
+
"thread_busy": "このスレッドの前の質問にまだ回答がありません。回答を待つか、別のスレッドで質問してください。"
|
|
786
788
|
},
|
|
787
789
|
"action": {
|
|
788
790
|
"connectGitHub": "GitHub に接続",
|
package/i18n/locales/pl.json
CHANGED
|
@@ -725,7 +725,8 @@
|
|
|
725
725
|
"no_host_frame": "Ta wyprawa nie znajduje się pod żadną usługą",
|
|
726
726
|
"delegated_executor_unwired": "Zewnętrzny executor nie jest zarejestrowany",
|
|
727
727
|
"delegated_step_async_only": "Ten krok jest odpytywany, a nie oczekiwany",
|
|
728
|
-
"delegated_claim_missing": "Ten krok nie ma zewnętrznej pracy do sprawdzenia"
|
|
728
|
+
"delegated_claim_missing": "Ten krok nie ma zewnętrznej pracy do sprawdzenia",
|
|
729
|
+
"thread_busy": "Ten wątek wciąż odpowiada"
|
|
729
730
|
},
|
|
730
731
|
"description": {
|
|
731
732
|
"dependencies_unmet": "To zadanie zależy od innych, które nie zostały jeszcze ukończone. Ukończ je lub odblokuj, a następnie uruchom je ponownie.",
|
|
@@ -782,7 +783,8 @@
|
|
|
782
783
|
"no_host_frame": "Zadanie naprawcze potrzebuje usługi, pod którą będzie żyć, i repozytorium do naprawy. Przenieś zadanie wyprawy pod ramkę usługi i spróbuj ponownie.",
|
|
783
784
|
"delegated_executor_unwired": "Krok tego potoku działa na zewnętrznym executorze, którego ta instalacja nie rejestruje, więc pracy nie ma dokąd wysłać. Zarejestruj executor w kodzie albo zmień krok na taki, który działa tutaj.",
|
|
784
785
|
"delegated_step_async_only": "Ten krok działa na zewnętrznym wykonawcy i może trwać godzinami, więc jest wysyłany, a następnie odpytywany, zamiast wykonywany w obrębie jednego żądania. Uruchom przebieg z tablicy; jeśli trafiłeś tu z własnego kodu, steruj krokiem ścieżką asynchroniczną.",
|
|
785
|
-
"delegated_claim_missing": "Brakuje zapisu tego, co przebieg wysłał do zewnętrznego wykonawcy, więc nie ma czego odpytywać. Z samym wykonawcą wszystko jest w porządku. Odśwież przebieg i uruchom krok ponownie, jeśli nadal stoi."
|
|
786
|
+
"delegated_claim_missing": "Brakuje zapisu tego, co przebieg wysłał do zewnętrznego wykonawcy, więc nie ma czego odpytywać. Z samym wykonawcą wszystko jest w porządku. Odśwież przebieg i uruchom krok ponownie, jeśli nadal stoi.",
|
|
787
|
+
"thread_busy": "Poprzednie pytanie w tym wątku nie ma jeszcze odpowiedzi. Poczekaj na nią albo zapytaj w innym wątku."
|
|
786
788
|
},
|
|
787
789
|
"action": {
|
|
788
790
|
"connectGitHub": "Połącz GitHub",
|
package/i18n/locales/tr.json
CHANGED
|
@@ -725,7 +725,8 @@
|
|
|
725
725
|
"no_host_frame": "Bu sefer bir hizmetin altında değil",
|
|
726
726
|
"delegated_executor_unwired": "Dış yürütücü kayıtlı değil",
|
|
727
727
|
"delegated_step_async_only": "Bu adım beklenmez, yoklanır",
|
|
728
|
-
"delegated_claim_missing": "Bu adımın kontrol edilecek harici işi yok"
|
|
728
|
+
"delegated_claim_missing": "Bu adımın kontrol edilecek harici işi yok",
|
|
729
|
+
"thread_busy": "Bu konu hâlâ yanıtlanıyor"
|
|
729
730
|
},
|
|
730
731
|
"description": {
|
|
731
732
|
"dependencies_unmet": "Bu görev henüz tamamlanmamış başka görevlere bağlı. Onları tamamla veya engelini kaldır, ardından yeniden başlat.",
|
|
@@ -782,7 +783,8 @@
|
|
|
782
783
|
"no_host_frame": "Bir düzeltme görevinin altında yaşayacağı bir hizmete ve düzeltilecek bir depoya ihtiyacı var. Sefer görevini bir hizmet çerçevesinin altına taşıyıp yeniden deneyin.",
|
|
783
784
|
"delegated_executor_unwired": "Bu hattın bir adımı, bu kurulumun kaydetmediği bir dış yürütücüde çalışıyor; dolayısıyla işin gönderileceği bir yer yok. Yürütücüyü kodda kaydedin ya da adımı burada çalışan biriyle değiştirin.",
|
|
784
785
|
"delegated_step_async_only": "Bu adım harici bir yürütücüde çalışır ve saatler sürebilir; bu yüzden tek bir istek içinde çalıştırılmak yerine gönderilip ardından yoklanır. Çalıştırmayı panodan başlatın; buraya kendi kodunuzdan geldiyseniz adımı asenkron yoldan sürün.",
|
|
785
|
-
"delegated_claim_missing": "Çalıştırmanın harici yürütücüye ne gönderdiğine dair kaydı eksik, dolayısıyla yoklanacak bir şey yok. Yürütücünün kendisinde bir sorun yok. Çalıştırmayı yeniden yükleyin ve adım hâlâ takılıysa yeniden çalıştırın."
|
|
786
|
+
"delegated_claim_missing": "Çalıştırmanın harici yürütücüye ne gönderdiğine dair kaydı eksik, dolayısıyla yoklanacak bir şey yok. Yürütücünün kendisinde bir sorun yok. Çalıştırmayı yeniden yükleyin ve adım hâlâ takılıysa yeniden çalıştırın.",
|
|
787
|
+
"thread_busy": "Bu konudaki önceki soru henüz yanıtlanmadı. Yanıtı bekleyin ya da başka bir konuda sorun."
|
|
786
788
|
},
|
|
787
789
|
"action": {
|
|
788
790
|
"connectGitHub": "GitHub'ı bağla",
|
package/i18n/locales/uk.json
CHANGED
|
@@ -725,7 +725,8 @@
|
|
|
725
725
|
"no_host_frame": "Ця експедиція не належить жодному сервісу",
|
|
726
726
|
"delegated_executor_unwired": "Зовнішній виконавець не зареєстрований",
|
|
727
727
|
"delegated_step_async_only": "Цей крок опитується, а не очікується",
|
|
728
|
-
"delegated_claim_missing": "У цього кроку немає зовнішньої роботи для перевірки"
|
|
728
|
+
"delegated_claim_missing": "У цього кроку немає зовнішньої роботи для перевірки",
|
|
729
|
+
"thread_busy": "Ця гілка ще відповідає"
|
|
729
730
|
},
|
|
730
731
|
"description": {
|
|
731
732
|
"dependencies_unmet": "Це завдання залежить від інших, які ще не завершені. Заверши або розблокуй їх, а потім запусти його знову.",
|
|
@@ -782,7 +783,8 @@
|
|
|
782
783
|
"no_host_frame": "Задачі виправлення потрібен сервіс, у якому вона житиме, і репозиторій для виправлення. Перемістіть задачу експедиції під рамку сервісу і спробуйте ще раз.",
|
|
783
784
|
"delegated_executor_unwired": "Крок цього конвеєра виконується на зовнішньому виконавці, якого це розгортання не реєструє, тож роботу нікуди надіслати. Зареєструйте виконавця в коді або замініть крок на той, що виконується тут.",
|
|
784
785
|
"delegated_step_async_only": "Цей крок виконується на зовнішньому виконавці й може тривати годинами, тому його надсилають і потім опитують, а не виконують у межах одного запиту. Запустіть прогін з дошки; якщо ви потрапили сюди зі свого коду, керуйте кроком через асинхронний шлях.",
|
|
785
|
-
"delegated_claim_missing": "Запис про те, що прогін надіслав зовнішньому виконавцю, відсутній, тож немає чого опитувати. Із самим виконавцем усе гаразд. Перезавантажте прогін і запустіть крок повторно, якщо він і далі стоїть."
|
|
786
|
+
"delegated_claim_missing": "Запис про те, що прогін надіслав зовнішньому виконавцю, відсутній, тож немає чого опитувати. Із самим виконавцем усе гаразд. Перезавантажте прогін і запустіть крок повторно, якщо він і далі стоїть.",
|
|
787
|
+
"thread_busy": "На попереднє запитання в цій гілці ще немає відповіді. Дочекайтеся її або запитайте в іншій гілці."
|
|
786
788
|
},
|
|
787
789
|
"action": {
|
|
788
790
|
"connectGitHub": "Під'єднати GitHub",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cat-factory/app",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.304.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",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"access": "public"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@cat-factory/contracts": "0.
|
|
21
|
+
"@cat-factory/contracts": "0.359.0",
|
|
22
22
|
"@modular-frontend/core": "0.6.0",
|
|
23
23
|
"@modular-vue/core": "^1.5.0",
|
|
24
24
|
"@modular-vue/journeys": "^1.4.0",
|