@cat-factory/app 0.36.0 → 0.37.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/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/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/execution.ts +3 -2
- package/app/stores/github.ts +1 -2
- package/app/stores/mergePresets.ts +2 -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/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
|
@@ -1,98 +1,115 @@
|
|
|
1
|
+
import {
|
|
2
|
+
commentGitHubIssueContract,
|
|
3
|
+
commitGitHubFilesContract,
|
|
4
|
+
connectGitHubContract,
|
|
5
|
+
createGitHubBranchContract,
|
|
6
|
+
createGitHubRepoContract,
|
|
7
|
+
disconnectGitHubContract,
|
|
8
|
+
getGitHubConnectionContract,
|
|
9
|
+
getGitHubInstallUrlContract,
|
|
10
|
+
listGitHubAvailableReposContract,
|
|
11
|
+
listGitHubBranchesContract,
|
|
12
|
+
listGitHubInstallationsContract,
|
|
13
|
+
listGitHubIssuesContract,
|
|
14
|
+
listGitHubPullsContract,
|
|
15
|
+
listGitHubReposContract,
|
|
16
|
+
listGitHubRepoTreeContract,
|
|
17
|
+
mergeGitHubPullRequestContract,
|
|
18
|
+
openGitHubPullRequestContract,
|
|
19
|
+
resyncGitHubContract,
|
|
20
|
+
setGitHubLinkedReposContract,
|
|
21
|
+
} from '@cat-factory/contracts'
|
|
1
22
|
import type {
|
|
2
23
|
CommitFilesInput,
|
|
3
24
|
CreateBranchInput,
|
|
4
|
-
CreatedRepo,
|
|
5
|
-
CreateRepoRequest,
|
|
6
|
-
GitHubAvailableRepo,
|
|
7
|
-
GitHubBranch,
|
|
8
|
-
GitHubConnection,
|
|
9
|
-
GitHubInstallationOption,
|
|
10
|
-
GitHubIssue,
|
|
11
|
-
GitHubPullRequest,
|
|
12
|
-
GitHubRepo,
|
|
13
25
|
MergePullRequestInput,
|
|
14
26
|
OpenPullRequestInput,
|
|
15
|
-
RepoTreeEntry,
|
|
16
27
|
ResyncRequest,
|
|
17
28
|
} from '~/types/domain'
|
|
29
|
+
import type { SendParams } from './client'
|
|
18
30
|
import type { ApiContext } from './context'
|
|
19
31
|
|
|
32
|
+
// The create-repo body is typed from the contract's INPUT shape so the
|
|
33
|
+
// valibot-defaulted `private`/`description` stay optional for callers (the exported
|
|
34
|
+
// `CreateRepoRequest` is the post-default OUTPUT shape).
|
|
35
|
+
type CreateRepoBody = NonNullable<SendParams<typeof createGitHubRepoContract>['body']>
|
|
36
|
+
|
|
20
37
|
/**
|
|
21
38
|
* GitHub integration: connection management, the D1-served projection reads
|
|
22
39
|
* (fast, rate-limit-free) and the repo writes (branches/commits/PRs/merges).
|
|
23
40
|
*/
|
|
24
|
-
export function githubApi({
|
|
41
|
+
export function githubApi({ send, ws }: ApiContext) {
|
|
25
42
|
return {
|
|
26
43
|
// ---- github integration ----------------------------------------------
|
|
27
44
|
// Connection management, projection reads (served from D1 — fast and
|
|
28
45
|
// rate-limit-free) and repo writes. A 503 from `getGitHubConnection` means
|
|
29
46
|
// the integration is off (the store hides its UI on any error there).
|
|
30
47
|
getGitHubInstallUrl: (workspaceId: string) =>
|
|
31
|
-
|
|
48
|
+
send(getGitHubInstallUrlContract, { pathPrefix: ws(workspaceId) }),
|
|
32
49
|
|
|
33
50
|
getGitHubConnection: (workspaceId: string) =>
|
|
34
|
-
|
|
51
|
+
send(getGitHubConnectionContract, { pathPrefix: ws(workspaceId) }),
|
|
35
52
|
|
|
36
53
|
listGitHubInstallations: (workspaceId: string) =>
|
|
37
|
-
|
|
38
|
-
`${ws(workspaceId)}/github/installations`,
|
|
39
|
-
),
|
|
54
|
+
send(listGitHubInstallationsContract, { pathPrefix: ws(workspaceId) }),
|
|
40
55
|
|
|
41
56
|
connectGitHub: (workspaceId: string, installationId: number) =>
|
|
42
|
-
|
|
43
|
-
method: 'POST',
|
|
44
|
-
body: { installationId },
|
|
45
|
-
}),
|
|
57
|
+
send(connectGitHubContract, { pathPrefix: ws(workspaceId), body: { installationId } }),
|
|
46
58
|
|
|
47
59
|
disconnectGitHub: (workspaceId: string) =>
|
|
48
|
-
|
|
60
|
+
send(disconnectGitHubContract, { pathPrefix: ws(workspaceId) }),
|
|
49
61
|
|
|
50
62
|
resyncGitHub: (workspaceId: string, body: ResyncRequest = {}) =>
|
|
51
|
-
|
|
63
|
+
send(resyncGitHubContract, { pathPrefix: ws(workspaceId), body }),
|
|
52
64
|
|
|
53
|
-
listGitHubRepos: (workspaceId: string) =>
|
|
65
|
+
listGitHubRepos: (workspaceId: string) =>
|
|
66
|
+
send(listGitHubReposContract, { pathPrefix: ws(workspaceId) }),
|
|
54
67
|
|
|
55
68
|
// Programmatic repo creation (privileged App tier). Only called when the
|
|
56
69
|
// connection reports `canCreateRepos`; otherwise the UI opens GitHub directly.
|
|
57
|
-
createGitHubRepo: (workspaceId: string, body:
|
|
58
|
-
|
|
70
|
+
createGitHubRepo: (workspaceId: string, body: CreateRepoBody) =>
|
|
71
|
+
send(createGitHubRepoContract, { pathPrefix: ws(workspaceId), body }),
|
|
59
72
|
|
|
60
73
|
// Repos the connected installation can access, annotated with whether this
|
|
61
74
|
// workspace links each (drives the per-workspace repo picker).
|
|
62
75
|
listGitHubAvailableRepos: (workspaceId: string) =>
|
|
63
|
-
|
|
76
|
+
send(listGitHubAvailableReposContract, { pathPrefix: ws(workspaceId) }),
|
|
64
77
|
|
|
65
78
|
// Set the exact set of repos this workspace links.
|
|
66
79
|
setGitHubLinkedRepos: (workspaceId: string, repoGithubIds: number[]) =>
|
|
67
|
-
|
|
68
|
-
method: 'PUT',
|
|
69
|
-
body: { repoGithubIds },
|
|
70
|
-
}),
|
|
80
|
+
send(setGitHubLinkedReposContract, { pathPrefix: ws(workspaceId), body: { repoGithubIds } }),
|
|
71
81
|
|
|
72
82
|
// Browse one level of a (monorepo) repo's tree to pin a service to a subdirectory.
|
|
73
83
|
listGitHubRepoTree: (workspaceId: string, repoGithubId: number, path = '') =>
|
|
74
|
-
|
|
75
|
-
|
|
84
|
+
send(listGitHubRepoTreeContract, {
|
|
85
|
+
pathPrefix: ws(workspaceId),
|
|
86
|
+
pathParams: { repoGithubId: String(repoGithubId) },
|
|
87
|
+
queryParams: { path },
|
|
76
88
|
}),
|
|
77
89
|
|
|
78
90
|
listGitHubBranches: (workspaceId: string, repoGithubId: number) =>
|
|
79
|
-
|
|
91
|
+
send(listGitHubBranchesContract, {
|
|
92
|
+
pathPrefix: ws(workspaceId),
|
|
93
|
+
pathParams: { repoGithubId: String(repoGithubId) },
|
|
94
|
+
}),
|
|
80
95
|
|
|
81
96
|
listGitHubPullRequests: (workspaceId: string) =>
|
|
82
|
-
|
|
97
|
+
send(listGitHubPullsContract, { pathPrefix: ws(workspaceId) }),
|
|
83
98
|
|
|
84
99
|
listGitHubIssues: (workspaceId: string) =>
|
|
85
|
-
|
|
100
|
+
send(listGitHubIssuesContract, { pathPrefix: ws(workspaceId) }),
|
|
86
101
|
|
|
87
102
|
createGitHubBranch: (workspaceId: string, repoGithubId: number, body: CreateBranchInput) =>
|
|
88
|
-
|
|
89
|
-
|
|
103
|
+
send(createGitHubBranchContract, {
|
|
104
|
+
pathPrefix: ws(workspaceId),
|
|
105
|
+
pathParams: { repoGithubId: String(repoGithubId) },
|
|
90
106
|
body,
|
|
91
107
|
}),
|
|
92
108
|
|
|
93
109
|
commitGitHubFiles: (workspaceId: string, repoGithubId: number, body: CommitFilesInput) =>
|
|
94
|
-
|
|
95
|
-
|
|
110
|
+
send(commitGitHubFilesContract, {
|
|
111
|
+
pathPrefix: ws(workspaceId),
|
|
112
|
+
pathParams: { repoGithubId: String(repoGithubId) },
|
|
96
113
|
body,
|
|
97
114
|
}),
|
|
98
115
|
|
|
@@ -101,8 +118,9 @@ export function githubApi({ http, ws }: ApiContext) {
|
|
|
101
118
|
repoGithubId: number,
|
|
102
119
|
body: OpenPullRequestInput,
|
|
103
120
|
) =>
|
|
104
|
-
|
|
105
|
-
|
|
121
|
+
send(openGitHubPullRequestContract, {
|
|
122
|
+
pathPrefix: ws(workspaceId),
|
|
123
|
+
pathParams: { repoGithubId: String(repoGithubId) },
|
|
106
124
|
body,
|
|
107
125
|
}),
|
|
108
126
|
|
|
@@ -112,8 +130,9 @@ export function githubApi({ http, ws }: ApiContext) {
|
|
|
112
130
|
number: number,
|
|
113
131
|
body: MergePullRequestInput = {},
|
|
114
132
|
) =>
|
|
115
|
-
|
|
116
|
-
|
|
133
|
+
send(mergeGitHubPullRequestContract, {
|
|
134
|
+
pathPrefix: ws(workspaceId),
|
|
135
|
+
pathParams: { repoGithubId: String(repoGithubId), number: String(number) },
|
|
117
136
|
body,
|
|
118
137
|
}),
|
|
119
138
|
|
|
@@ -123,8 +142,9 @@ export function githubApi({ http, ws }: ApiContext) {
|
|
|
123
142
|
number: number,
|
|
124
143
|
bodyText: string,
|
|
125
144
|
) =>
|
|
126
|
-
|
|
127
|
-
|
|
145
|
+
send(commentGitHubIssueContract, {
|
|
146
|
+
pathPrefix: ws(workspaceId),
|
|
147
|
+
pathParams: { repoGithubId: String(repoGithubId), number: String(number) },
|
|
128
148
|
body: { body: bodyText },
|
|
129
149
|
}),
|
|
130
150
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { requestHumanReviewFixContract } from '@cat-factory/contracts'
|
|
2
2
|
import type { ApiContext } from './context'
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -7,12 +7,13 @@ import type { ApiContext } from './context'
|
|
|
7
7
|
* immediately. Returns the updated execution instance (the gate state rides on its step + the
|
|
8
8
|
* execution stream).
|
|
9
9
|
*/
|
|
10
|
-
export function humanReviewApi({
|
|
10
|
+
export function humanReviewApi({ send, ws }: ApiContext) {
|
|
11
11
|
return {
|
|
12
12
|
requestHumanReviewFix: (workspaceId: string, blockId: string, instructions: string) =>
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
send(requestHumanReviewFixContract, {
|
|
14
|
+
pathPrefix: ws(workspaceId),
|
|
15
|
+
pathParams: { blockId },
|
|
16
|
+
body: { instructions },
|
|
17
|
+
}),
|
|
17
18
|
}
|
|
18
19
|
}
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {
|
|
2
|
+
confirmHumanTestContract,
|
|
3
|
+
destroyHumanTestEnvContract,
|
|
4
|
+
pullMainHumanTestContract,
|
|
5
|
+
recreateHumanTestEnvContract,
|
|
6
|
+
requestHumanTestFixContract,
|
|
7
|
+
} from '@cat-factory/contracts'
|
|
2
8
|
import type { ApiContext } from './context'
|
|
3
9
|
|
|
4
10
|
/**
|
|
@@ -6,32 +12,30 @@ import type { ApiContext } from './context'
|
|
|
6
12
|
* step and returns the updated execution instance (the gate state rides on its current step,
|
|
7
13
|
* and also arrives live via the execution stream).
|
|
8
14
|
*/
|
|
9
|
-
export function humanTestApi({
|
|
10
|
-
const base = (workspaceId: string, blockId: string) =>
|
|
11
|
-
`${ws(workspaceId)}/blocks/${encodeURIComponent(blockId)}/human-test`
|
|
12
|
-
|
|
15
|
+
export function humanTestApi({ send, ws }: ApiContext) {
|
|
13
16
|
return {
|
|
14
17
|
// Confirm the change works: tear the env down and advance the pipeline.
|
|
15
18
|
confirmHumanTest: (workspaceId: string, blockId: string) =>
|
|
16
|
-
|
|
19
|
+
send(confirmHumanTestContract, { pathPrefix: ws(workspaceId), pathParams: { blockId } }),
|
|
17
20
|
|
|
18
21
|
// Submit findings and request a fix (dispatches the Tester's fixer, then rebuilds the env).
|
|
19
22
|
requestHumanTestFix: (workspaceId: string, blockId: string, findings: string) =>
|
|
20
|
-
|
|
21
|
-
|
|
23
|
+
send(requestHumanTestFixContract, {
|
|
24
|
+
pathPrefix: ws(workspaceId),
|
|
25
|
+
pathParams: { blockId },
|
|
22
26
|
body: { findings },
|
|
23
27
|
}),
|
|
24
28
|
|
|
25
29
|
// Pull latest main into the PR branch + redeploy (conflict → conflict-resolver).
|
|
26
30
|
pullMainHumanTest: (workspaceId: string, blockId: string) =>
|
|
27
|
-
|
|
31
|
+
send(pullMainHumanTestContract, { pathPrefix: ws(workspaceId), pathParams: { blockId } }),
|
|
28
32
|
|
|
29
33
|
// Rebuild the ephemeral environment on demand.
|
|
30
34
|
recreateHumanTestEnv: (workspaceId: string, blockId: string) =>
|
|
31
|
-
|
|
35
|
+
send(recreateHumanTestEnvContract, { pathPrefix: ws(workspaceId), pathParams: { blockId } }),
|
|
32
36
|
|
|
33
37
|
// Destroy the ephemeral environment on demand (the run stays parked).
|
|
34
38
|
destroyHumanTestEnv: (workspaceId: string, blockId: string) =>
|
|
35
|
-
|
|
39
|
+
send(destroyHumanTestEnvContract, { pathPrefix: ws(workspaceId), pathParams: { blockId } }),
|
|
36
40
|
}
|
|
37
41
|
}
|
|
@@ -1,16 +1,18 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { getKaizenOverviewContract, getKaizenRunGradingsContract } from '@cat-factory/contracts'
|
|
2
2
|
import type { ApiContext } from './context'
|
|
3
3
|
|
|
4
4
|
/** Kaizen (post-run grading) read endpoints: the screen overview + a run's gradings. */
|
|
5
|
-
export function kaizenApi({
|
|
5
|
+
export function kaizenApi({ send, ws }: ApiContext) {
|
|
6
6
|
return {
|
|
7
7
|
// The Kaizen screen: recent grading history + the verified-combo library.
|
|
8
|
-
getKaizenOverview: (workspaceId: string) =>
|
|
8
|
+
getKaizenOverview: (workspaceId: string) =>
|
|
9
|
+
send(getKaizenOverviewContract, { pathPrefix: ws(workspaceId) }),
|
|
9
10
|
|
|
10
11
|
// The gradings recorded for one run (the run-window status surface).
|
|
11
12
|
getKaizenForExecution: (workspaceId: string, executionId: string) =>
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
send(getKaizenRunGradingsContract, {
|
|
14
|
+
pathPrefix: ws(workspaceId),
|
|
15
|
+
pathParams: { executionId },
|
|
16
|
+
}),
|
|
15
17
|
}
|
|
16
18
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { getLocalSettingsContract, updateLocalSettingsContract } from '@cat-factory/contracts'
|
|
2
|
+
import type { UpdateLocalSettingsInput } from '~/types/localSettings'
|
|
2
3
|
import type { ApiContext } from './context'
|
|
3
4
|
|
|
4
5
|
/**
|
|
@@ -7,11 +8,11 @@ import type { ApiContext } from './context'
|
|
|
7
8
|
* Worker / stock Node facades (the store hides the panel then). No secrets, so the read view
|
|
8
9
|
* is the plain config and the write replaces it wholesale.
|
|
9
10
|
*/
|
|
10
|
-
export function localSettingsApi({
|
|
11
|
+
export function localSettingsApi({ send }: ApiContext) {
|
|
11
12
|
return {
|
|
12
|
-
getLocalSettings: () =>
|
|
13
|
+
getLocalSettings: () => send(getLocalSettingsContract, {}),
|
|
13
14
|
|
|
14
15
|
updateLocalSettings: (body: UpdateLocalSettingsInput) =>
|
|
15
|
-
|
|
16
|
+
send(updateLocalSettingsContract, { body }),
|
|
16
17
|
}
|
|
17
18
|
}
|
|
@@ -1,25 +1,42 @@
|
|
|
1
|
+
import {
|
|
2
|
+
addAccountApiKeyContract,
|
|
3
|
+
addUserApiKeyContract,
|
|
4
|
+
addVendorCredentialContract,
|
|
5
|
+
addWorkspaceApiKeyContract,
|
|
6
|
+
getOpenRouterCatalogContract,
|
|
7
|
+
getServiceFragmentDefaultsContract,
|
|
8
|
+
listAccountApiKeysContract,
|
|
9
|
+
listLocalModelEndpointsContract,
|
|
10
|
+
listModelsContract,
|
|
11
|
+
listPersonalSubscriptionsContract,
|
|
12
|
+
listUserApiKeysContract,
|
|
13
|
+
listVendorCredentialsContract,
|
|
14
|
+
listWorkspaceApiKeysContract,
|
|
15
|
+
listWorkspaceModelsContract,
|
|
16
|
+
refreshOpenRouterCatalogContract,
|
|
17
|
+
removeAccountApiKeyContract,
|
|
18
|
+
removeLocalModelEndpointContract,
|
|
19
|
+
removePersonalSubscriptionContract,
|
|
20
|
+
removeUserApiKeyContract,
|
|
21
|
+
removeVendorCredentialContract,
|
|
22
|
+
removeWorkspaceApiKeyContract,
|
|
23
|
+
setServiceFragmentDefaultsContract,
|
|
24
|
+
storePersonalSubscriptionContract,
|
|
25
|
+
testLocalModelEndpointContract,
|
|
26
|
+
upsertLocalModelEndpointContract,
|
|
27
|
+
upsertOpenRouterCatalogContract,
|
|
28
|
+
} from '@cat-factory/contracts'
|
|
1
29
|
import type {
|
|
2
30
|
AddApiKeyInput,
|
|
3
|
-
ApiKey,
|
|
4
|
-
ModelOption,
|
|
5
|
-
PersonalSubscriptionStatus,
|
|
6
|
-
ServiceFragmentDefaults,
|
|
7
31
|
StorePersonalSubscriptionInput,
|
|
8
32
|
SubscriptionVendor,
|
|
9
|
-
VendorCredential,
|
|
10
33
|
} from '~/types/domain'
|
|
11
34
|
import type {
|
|
12
|
-
LocalModelEndpoint,
|
|
13
|
-
LocalModelEndpointTestResult,
|
|
14
35
|
LocalRunner,
|
|
15
36
|
TestLocalModelEndpointInput,
|
|
16
37
|
UpsertLocalModelEndpointInput,
|
|
17
38
|
} from '~/types/localModels'
|
|
18
|
-
import type {
|
|
19
|
-
OpenRouterCatalog,
|
|
20
|
-
OpenRouterRefreshResult,
|
|
21
|
-
UpsertOpenRouterCatalogInput,
|
|
22
|
-
} from '~/types/openrouter'
|
|
39
|
+
import type { UpsertOpenRouterCatalogInput } from '~/types/openrouter'
|
|
23
40
|
import type { ApiContext } from './context'
|
|
24
41
|
|
|
25
42
|
/**
|
|
@@ -27,104 +44,94 @@ import type { ApiContext } from './context'
|
|
|
27
44
|
* API keys, vendor subscription tokens, per-user personal subscriptions + local
|
|
28
45
|
* runners) + the per-workspace routing/selection defaults.
|
|
29
46
|
*/
|
|
30
|
-
export function modelsApi({
|
|
47
|
+
export function modelsApi({ send, ws }: ApiContext) {
|
|
31
48
|
return {
|
|
32
49
|
// ---- model picker catalog (effective per-deployment flavours) ---------
|
|
33
|
-
getModels: () =>
|
|
50
|
+
getModels: () => send(listModelsContract, {}),
|
|
34
51
|
// Per-workspace catalog: selectability reflects the workspace's (+ account's +
|
|
35
52
|
// caller's) configured API keys and subscription tokens (`available` flag).
|
|
36
|
-
getWorkspaceModels: (workspaceId: string) =>
|
|
53
|
+
getWorkspaceModels: (workspaceId: string) =>
|
|
54
|
+
send(listWorkspaceModelsContract, { pathParams: { workspaceId } }),
|
|
37
55
|
|
|
38
56
|
// ---- direct-provider API keys (the DB-backed pool) --------------------
|
|
39
57
|
// Onboarded via UI, stored encrypted, pooled + rotated. Scoped to a workspace,
|
|
40
58
|
// its owning account, or the signed-in user. Keys are write-only (never returned).
|
|
41
59
|
listWorkspaceApiKeys: (workspaceId: string) =>
|
|
42
|
-
|
|
60
|
+
send(listWorkspaceApiKeysContract, { pathPrefix: ws(workspaceId) }),
|
|
43
61
|
addWorkspaceApiKey: (workspaceId: string, body: AddApiKeyInput) =>
|
|
44
|
-
|
|
62
|
+
send(addWorkspaceApiKeyContract, { pathPrefix: ws(workspaceId), body }),
|
|
45
63
|
removeWorkspaceApiKey: (workspaceId: string, id: string) =>
|
|
46
|
-
|
|
47
|
-
listMyApiKeys: () =>
|
|
48
|
-
addMyApiKey: (body: AddApiKeyInput) =>
|
|
49
|
-
removeMyApiKey: (id: string) =>
|
|
50
|
-
http(`/me/api-keys/${encodeURIComponent(id)}`, { method: 'DELETE' }),
|
|
64
|
+
send(removeWorkspaceApiKeyContract, { pathPrefix: ws(workspaceId), pathParams: { id } }),
|
|
65
|
+
listMyApiKeys: () => send(listUserApiKeysContract, {}),
|
|
66
|
+
addMyApiKey: (body: AddApiKeyInput) => send(addUserApiKeyContract, { body }),
|
|
67
|
+
removeMyApiKey: (id: string) => send(removeUserApiKeyContract, { pathParams: { id } }),
|
|
51
68
|
// Account-scoped keys (shared by every workspace in the account); admin-only.
|
|
52
69
|
listAccountApiKeys: (accountId: string) =>
|
|
53
|
-
|
|
70
|
+
send(listAccountApiKeysContract, { pathParams: { accountId } }),
|
|
54
71
|
addAccountApiKey: (accountId: string, body: AddApiKeyInput) =>
|
|
55
|
-
|
|
72
|
+
send(addAccountApiKeyContract, { pathParams: { accountId }, body }),
|
|
56
73
|
removeAccountApiKey: (accountId: string, id: string) =>
|
|
57
|
-
|
|
58
|
-
method: 'DELETE',
|
|
59
|
-
}),
|
|
74
|
+
send(removeAccountApiKeyContract, { pathParams: { accountId, id } }),
|
|
60
75
|
|
|
61
76
|
// ---- LLM vendor subscription credentials (the token pool) -------------
|
|
62
77
|
listVendorCredentials: (workspaceId: string) =>
|
|
63
|
-
|
|
78
|
+
send(listVendorCredentialsContract, { pathPrefix: ws(workspaceId) }),
|
|
64
79
|
addVendorCredential: (
|
|
65
80
|
workspaceId: string,
|
|
66
81
|
body: { vendor: SubscriptionVendor; label: string; token: string },
|
|
67
|
-
) =>
|
|
82
|
+
) => send(addVendorCredentialContract, { pathPrefix: ws(workspaceId), body }),
|
|
68
83
|
removeVendorCredential: (workspaceId: string, id: string) =>
|
|
69
|
-
|
|
84
|
+
send(removeVendorCredentialContract, { pathPrefix: ws(workspaceId), pathParams: { id } }),
|
|
70
85
|
|
|
71
86
|
// ---- personal (individual-usage) subscriptions (per-user, e.g. Claude) ----
|
|
72
87
|
// Stored per signed-in user, double-encrypted under their personal password.
|
|
73
88
|
// Metadata only is returned (never the token). User-scoped (no workspace).
|
|
74
|
-
listPersonalSubscriptions: () =>
|
|
75
|
-
http<{ subscriptions: PersonalSubscriptionStatus[] }>('/personal-subscriptions'),
|
|
89
|
+
listPersonalSubscriptions: () => send(listPersonalSubscriptionsContract, {}),
|
|
76
90
|
|
|
77
91
|
storePersonalSubscription: (body: StorePersonalSubscriptionInput) =>
|
|
78
|
-
|
|
92
|
+
send(storePersonalSubscriptionContract, { body }),
|
|
79
93
|
|
|
80
94
|
removePersonalSubscription: (vendor: SubscriptionVendor) =>
|
|
81
|
-
|
|
95
|
+
send(removePersonalSubscriptionContract, { pathParams: { vendor } }),
|
|
82
96
|
|
|
83
97
|
// ---- local model runners (per-user, e.g. Ollama / LM Studio) ----------
|
|
84
98
|
// A developer's own-machine LLM endpoints, stored per signed-in user (the API
|
|
85
99
|
// key is write-only, never returned). User-scoped (no workspace). The enabled
|
|
86
100
|
// models then surface automatically in the per-workspace `/models` catalog.
|
|
87
|
-
listLocalModelEndpoints: () =>
|
|
88
|
-
http<{ endpoints: LocalModelEndpoint[] }>('/local-model-endpoints'),
|
|
101
|
+
listLocalModelEndpoints: () => send(listLocalModelEndpointsContract, {}),
|
|
89
102
|
|
|
90
103
|
upsertLocalModelEndpoint: (provider: LocalRunner, body: UpsertLocalModelEndpointInput) =>
|
|
91
|
-
|
|
92
|
-
method: 'PUT',
|
|
93
|
-
body,
|
|
94
|
-
}),
|
|
104
|
+
send(upsertLocalModelEndpointContract, { pathParams: { provider }, body }),
|
|
95
105
|
|
|
96
106
|
deleteLocalModelEndpoint: (provider: LocalRunner) =>
|
|
97
|
-
|
|
107
|
+
send(removeLocalModelEndpointContract, { pathParams: { provider } }),
|
|
98
108
|
|
|
99
109
|
// Probe a runner endpoint for reachability + the models it currently serves
|
|
100
110
|
// (no persistence — drives the "Test connection" model multi-select).
|
|
101
111
|
testLocalModelEndpoint: (body: TestLocalModelEndpointInput) =>
|
|
102
|
-
|
|
103
|
-
method: 'POST',
|
|
104
|
-
body,
|
|
105
|
-
}),
|
|
112
|
+
send(testLocalModelEndpointContract, { body }),
|
|
106
113
|
|
|
107
114
|
// ---- OpenRouter dynamic catalog (per-workspace gateway models) --------
|
|
108
115
|
// Browse OpenRouter's live catalog (`refresh`, leasing the workspace's pooled
|
|
109
116
|
// OpenRouter key server-side) and enable a subset; enabled models then surface
|
|
110
117
|
// in the per-workspace `/models` catalog with their context + price.
|
|
111
118
|
getOpenRouterCatalog: (workspaceId: string) =>
|
|
112
|
-
|
|
119
|
+
send(getOpenRouterCatalogContract, { pathParams: { workspaceId } }),
|
|
113
120
|
|
|
114
121
|
setOpenRouterCatalog: (workspaceId: string, body: UpsertOpenRouterCatalogInput) =>
|
|
115
|
-
|
|
122
|
+
send(upsertOpenRouterCatalogContract, { pathParams: { workspaceId }, body }),
|
|
116
123
|
|
|
117
124
|
refreshOpenRouterCatalog: (workspaceId: string) =>
|
|
118
|
-
|
|
125
|
+
send(refreshOpenRouterCatalogContract, { pathParams: { workspaceId } }),
|
|
119
126
|
|
|
120
127
|
// The workspace's default service-fragment selection (the fragment ids new
|
|
121
128
|
// services inherit). `setServiceFragmentDefaults` replaces the whole list.
|
|
122
129
|
getServiceFragmentDefaults: (workspaceId: string) =>
|
|
123
|
-
|
|
130
|
+
send(getServiceFragmentDefaultsContract, { pathPrefix: ws(workspaceId) }),
|
|
124
131
|
|
|
125
132
|
setServiceFragmentDefaults: (workspaceId: string, fragmentIds: string[]) =>
|
|
126
|
-
|
|
127
|
-
|
|
133
|
+
send(setServiceFragmentDefaultsContract, {
|
|
134
|
+
pathPrefix: ws(workspaceId),
|
|
128
135
|
body: { fragmentIds },
|
|
129
136
|
}),
|
|
130
137
|
}
|
|
@@ -1,23 +1,29 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {
|
|
2
|
+
actNotificationContract,
|
|
3
|
+
dismissNotificationContract,
|
|
4
|
+
listNotificationsContract,
|
|
5
|
+
} from '@cat-factory/contracts'
|
|
2
6
|
import type { ApiContext } from './context'
|
|
3
7
|
|
|
4
8
|
/** The human-actionable notification inbox (act / dismiss). */
|
|
5
|
-
export function notificationsApi({
|
|
9
|
+
export function notificationsApi({ send, ws }: ApiContext) {
|
|
6
10
|
return {
|
|
7
11
|
// ---- notifications (human-actionable board items) ---------------------
|
|
8
12
|
listNotifications: (workspaceId: string) =>
|
|
9
|
-
|
|
13
|
+
send(listNotificationsContract, { pathPrefix: ws(workspaceId) }),
|
|
10
14
|
|
|
11
15
|
// Act on a notification (merge the PR / confirm / retry), then resolve it.
|
|
12
16
|
actNotification: (workspaceId: string, id: string) =>
|
|
13
|
-
|
|
14
|
-
|
|
17
|
+
send(actNotificationContract, {
|
|
18
|
+
pathPrefix: ws(workspaceId),
|
|
19
|
+
pathParams: { notificationId: id },
|
|
15
20
|
}),
|
|
16
21
|
|
|
17
22
|
// Dismiss a notification without acting.
|
|
18
23
|
dismissNotification: (workspaceId: string, id: string) =>
|
|
19
|
-
|
|
20
|
-
|
|
24
|
+
send(dismissNotificationContract, {
|
|
25
|
+
pathPrefix: ws(workspaceId),
|
|
26
|
+
pathParams: { notificationId: id },
|
|
21
27
|
}),
|
|
22
28
|
}
|
|
23
29
|
}
|
|
@@ -1,52 +1,58 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
} from '
|
|
1
|
+
import {
|
|
2
|
+
createMergePresetContract,
|
|
3
|
+
createModelPresetContract,
|
|
4
|
+
deleteMergePresetContract,
|
|
5
|
+
deleteModelPresetContract,
|
|
6
|
+
listMergePresetsContract,
|
|
7
|
+
listModelPresetsContract,
|
|
8
|
+
updateMergePresetContract,
|
|
9
|
+
updateModelPresetContract,
|
|
10
|
+
} from '@cat-factory/contracts'
|
|
11
|
+
import type { UpdateMergePresetInput } from '~/types/merge'
|
|
12
|
+
import type { CreateModelPresetInput, UpdateModelPresetInput } from '~/types/model-presets'
|
|
13
|
+
import type { SendParams } from './client'
|
|
11
14
|
import type { ApiContext } from './context'
|
|
12
15
|
|
|
16
|
+
// The merge-preset create body is typed from the contract's INPUT shape so the
|
|
17
|
+
// valibot-defaulted fields (release/grace windows, isDefault) stay optional for callers
|
|
18
|
+
// (the exported `CreateMergePresetInput` is the post-default OUTPUT shape).
|
|
19
|
+
type CreateMergePresetBody = NonNullable<SendParams<typeof createMergePresetContract>['body']>
|
|
20
|
+
|
|
13
21
|
/** The per-workspace preset libraries: merge-threshold policy + model->agent mapping. */
|
|
14
|
-
export function presetsApi({
|
|
22
|
+
export function presetsApi({ send, ws }: ApiContext) {
|
|
15
23
|
return {
|
|
16
24
|
// ---- merge threshold presets (per-task auto-merge policy library) -----
|
|
17
25
|
listMergePresets: (workspaceId: string) =>
|
|
18
|
-
|
|
26
|
+
send(listMergePresetsContract, { pathPrefix: ws(workspaceId) }),
|
|
19
27
|
|
|
20
|
-
createMergePreset: (workspaceId: string, body:
|
|
21
|
-
|
|
28
|
+
createMergePreset: (workspaceId: string, body: CreateMergePresetBody) =>
|
|
29
|
+
send(createMergePresetContract, { pathPrefix: ws(workspaceId), body }),
|
|
22
30
|
|
|
23
31
|
updateMergePreset: (workspaceId: string, presetId: string, body: UpdateMergePresetInput) =>
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
32
|
+
send(updateMergePresetContract, {
|
|
33
|
+
pathPrefix: ws(workspaceId),
|
|
34
|
+
pathParams: { presetId },
|
|
35
|
+
body,
|
|
36
|
+
}),
|
|
28
37
|
|
|
29
38
|
deleteMergePreset: (workspaceId: string, presetId: string) =>
|
|
30
|
-
|
|
31
|
-
method: 'DELETE',
|
|
32
|
-
}),
|
|
39
|
+
send(deleteMergePresetContract, { pathPrefix: ws(workspaceId), pathParams: { presetId } }),
|
|
33
40
|
|
|
34
41
|
// ---- model presets (per-task model->agent mapping library) ------------
|
|
35
42
|
listModelPresets: (workspaceId: string) =>
|
|
36
|
-
|
|
43
|
+
send(listModelPresetsContract, { pathPrefix: ws(workspaceId) }),
|
|
37
44
|
|
|
38
45
|
createModelPreset: (workspaceId: string, body: CreateModelPresetInput) =>
|
|
39
|
-
|
|
46
|
+
send(createModelPresetContract, { pathPrefix: ws(workspaceId), body }),
|
|
40
47
|
|
|
41
48
|
updateModelPreset: (workspaceId: string, presetId: string, body: UpdateModelPresetInput) =>
|
|
42
|
-
|
|
43
|
-
|
|
49
|
+
send(updateModelPresetContract, {
|
|
50
|
+
pathPrefix: ws(workspaceId),
|
|
51
|
+
pathParams: { presetId },
|
|
44
52
|
body,
|
|
45
53
|
}),
|
|
46
54
|
|
|
47
55
|
deleteModelPreset: (workspaceId: string, presetId: string) =>
|
|
48
|
-
|
|
49
|
-
method: 'DELETE',
|
|
50
|
-
}),
|
|
56
|
+
send(deleteModelPresetContract, { pathPrefix: ws(workspaceId), pathParams: { presetId } }),
|
|
51
57
|
}
|
|
52
58
|
}
|