@cat-factory/app 0.36.0 → 0.37.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/app/components/auth/UserMenu.vue +11 -1
  2. package/app/components/brainstorm/BrainstormWindow.vue +2 -1
  3. package/app/components/clarity/ClarityReviewWindow.vue +2 -1
  4. package/app/components/layout/IntegrationBackTitle.vue +12 -7
  5. package/app/components/layout/IntegrationsHub.vue +191 -43
  6. package/app/components/layout/PersonalSetupModal.vue +141 -0
  7. package/app/components/pipeline/PipelineBuilder.vue +1 -1
  8. package/app/components/providers/VendorCredentialsModal.vue +7 -2
  9. package/app/composables/api/accounts.ts +36 -51
  10. package/app/composables/api/auth.ts +20 -19
  11. package/app/composables/api/board.ts +60 -40
  12. package/app/composables/api/bootstrap.ts +25 -22
  13. package/app/composables/api/client.ts +102 -0
  14. package/app/composables/api/context.ts +25 -6
  15. package/app/composables/api/documents.ts +36 -34
  16. package/app/composables/api/execution.ts +65 -48
  17. package/app/composables/api/followUps.ts +26 -26
  18. package/app/composables/api/fragments.ts +47 -34
  19. package/app/composables/api/github.ts +65 -45
  20. package/app/composables/api/humanReview.ts +7 -6
  21. package/app/composables/api/humanTest.ts +15 -11
  22. package/app/composables/api/kaizen.ts +8 -6
  23. package/app/composables/api/localSettings.ts +5 -4
  24. package/app/composables/api/models.ts +58 -51
  25. package/app/composables/api/notifications.ts +13 -7
  26. package/app/composables/api/presets.ts +34 -28
  27. package/app/composables/api/providerConnections.ts +68 -26
  28. package/app/composables/api/recurring.ts +40 -30
  29. package/app/composables/api/releaseHealth.ts +28 -26
  30. package/app/composables/api/reviews.ts +136 -114
  31. package/app/composables/api/sandbox.ts +52 -34
  32. package/app/composables/api/slack.ts +22 -25
  33. package/app/composables/api/spec.ts +3 -3
  34. package/app/composables/api/tasks.ts +42 -41
  35. package/app/composables/api/userSecrets.ts +12 -17
  36. package/app/composables/api/workspaces.ts +21 -15
  37. package/app/composables/useApi.ts +9 -1
  38. package/app/composables/useIntegrationBack.ts +9 -3
  39. package/app/composables/useSourceIntegration.ts +107 -0
  40. package/app/composables/useUpsertList.spec.ts +60 -0
  41. package/app/composables/useUpsertList.ts +57 -0
  42. package/app/pages/index.vue +2 -0
  43. package/app/stores/auth.ts +2 -1
  44. package/app/stores/board.ts +2 -1
  45. package/app/stores/brainstorm.ts +2 -2
  46. package/app/stores/clarity.ts +6 -2
  47. package/app/stores/documents.ts +27 -62
  48. package/app/stores/execution.ts +3 -2
  49. package/app/stores/github.ts +1 -2
  50. package/app/stores/mergePresets.ts +2 -6
  51. package/app/stores/notifications.ts +9 -6
  52. package/app/stores/pipelines.ts +1 -1
  53. package/app/stores/recurringPipelines.ts +2 -7
  54. package/app/stores/sandbox.ts +1 -2
  55. package/app/stores/tasks.ts +25 -76
  56. package/app/stores/ui.ts +62 -19
  57. package/app/types/accountSettings.ts +11 -36
  58. package/app/types/accounts.ts +16 -71
  59. package/app/types/bootstrap.ts +13 -75
  60. package/app/types/brainstorm.ts +13 -38
  61. package/app/types/clarity.ts +12 -43
  62. package/app/types/consensus.ts +16 -89
  63. package/app/types/documents.ts +19 -94
  64. package/app/types/domain.ts +54 -586
  65. package/app/types/execution.ts +48 -515
  66. package/app/types/fragments.ts +15 -83
  67. package/app/types/github.ts +25 -161
  68. package/app/types/incidentEnrichment.ts +10 -25
  69. package/app/types/localModels.ts +11 -61
  70. package/app/types/localSettings.ts +9 -26
  71. package/app/types/merge.ts +10 -68
  72. package/app/types/model-presets.ts +7 -28
  73. package/app/types/models.ts +16 -164
  74. package/app/types/notifications.ts +18 -77
  75. package/app/types/openrouter.ts +8 -34
  76. package/app/types/providerConnections.ts +21 -41
  77. package/app/types/provisioningLogs.ts +9 -29
  78. package/app/types/recurring.ts +10 -63
  79. package/app/types/releaseHealth.ts +15 -39
  80. package/app/types/requirements.ts +14 -84
  81. package/app/types/sandbox.ts +45 -161
  82. package/app/types/services.ts +3 -22
  83. package/app/types/slack.ts +10 -47
  84. package/app/types/spec.ts +15 -68
  85. package/app/types/tasks.ts +15 -111
  86. package/app/types/tracker.ts +9 -24
  87. package/app/types/userSecrets.ts +12 -47
  88. package/package.json +9 -2
@@ -1,61 +1,103 @@
1
+ import {
2
+ describeEnvironmentProviderContract,
3
+ describeRunnerPoolProviderContract,
4
+ getEnvironmentConnectionContract,
5
+ getRunnerPoolConnectionContract,
6
+ registerEnvironmentProviderContract,
7
+ registerRunnerPoolContract,
8
+ testEnvironmentConnectionContract,
9
+ testRunnerPoolConnectionContract,
10
+ unregisterEnvironmentProviderContract,
11
+ unregisterRunnerPoolContract,
12
+ updateEnvironmentSecretsContract,
13
+ updateRunnerPoolSecretsContract,
14
+ } from '@cat-factory/contracts'
15
+ import type {
16
+ RegisterEnvironmentProviderInput,
17
+ RegisterRunnerPoolInput,
18
+ TestEnvironmentConnectionInput,
19
+ TestRunnerPoolConnectionInput,
20
+ } from '@cat-factory/contracts'
1
21
  import type {
2
- ConnectionTestResult,
3
- ProviderConnection,
4
22
  ProviderConnectionKind,
5
- ProviderDescriptor,
6
23
  RegisterProviderInput,
7
24
  TestProviderInput,
8
25
  } from '~/types/providerConnections'
9
26
  import type { ApiContext } from './context'
10
27
 
11
28
  // The two infrastructure providers share an identical REST surface, differing only in the
12
- // path segment (`environments` vs `runner-pool`). One factory serves both so a single
13
- // generic store + connect form drives either.
14
- const SEGMENT: Record<ProviderConnectionKind, string> = {
15
- environment: 'environments',
16
- 'runner-pool': 'runner-pool',
17
- }
29
+ // path segment (`environments` vs `runner-pool`). The contracts split into per-kind names,
30
+ // so a small lookup table maps each kind to its four contracts; one factory then serves both
31
+ // so a single generic store + connect form drives either.
32
+ const CONTRACTS = {
33
+ environment: {
34
+ describe: describeEnvironmentProviderContract,
35
+ get: getEnvironmentConnectionContract,
36
+ register: registerEnvironmentProviderContract,
37
+ updateSecrets: updateEnvironmentSecretsContract,
38
+ test: testEnvironmentConnectionContract,
39
+ unregister: unregisterEnvironmentProviderContract,
40
+ },
41
+ 'runner-pool': {
42
+ describe: describeRunnerPoolProviderContract,
43
+ get: getRunnerPoolConnectionContract,
44
+ register: registerRunnerPoolContract,
45
+ updateSecrets: updateRunnerPoolSecretsContract,
46
+ test: testRunnerPoolConnectionContract,
47
+ unregister: unregisterRunnerPoolContract,
48
+ },
49
+ } satisfies Record<ProviderConnectionKind, unknown>
18
50
 
19
51
  /** Environment-provider + runner-pool connection endpoints (self-describe + register/test). */
20
- export function providerConnectionsApi({ http, ws }: ApiContext) {
21
- const base = (workspaceId: string, kind: ProviderConnectionKind) =>
22
- `${ws(workspaceId)}/${SEGMENT[kind]}`
23
-
52
+ export function providerConnectionsApi({ send, ws }: ApiContext) {
24
53
  return {
25
54
  describeProvider: (workspaceId: string, kind: ProviderConnectionKind) =>
26
- http<ProviderDescriptor>(`${base(workspaceId, kind)}/provider`),
55
+ send(CONTRACTS[kind].describe, { pathPrefix: ws(workspaceId) }),
27
56
 
28
57
  getProviderConnection: (workspaceId: string, kind: ProviderConnectionKind) =>
29
- http<{ connection: ProviderConnection | null }>(`${base(workspaceId, kind)}/connection`),
58
+ send(CONTRACTS[kind].get, { pathPrefix: ws(workspaceId) }),
30
59
 
60
+ // The connect form builds the manifest dynamically from a server-provided scaffold, so
61
+ // the FE input keeps `manifest` opaque (`Record<string, unknown>`); narrow on the kind
62
+ // and cast to the matching per-kind contract input at this single boundary (the backend
63
+ // re-validates the manifest against the precise contract on receipt).
31
64
  registerProviderConnection: (
32
65
  workspaceId: string,
33
66
  kind: ProviderConnectionKind,
34
67
  body: RegisterProviderInput,
35
68
  ) =>
36
- http<ProviderConnection>(`${base(workspaceId, kind)}/connection`, { method: 'POST', body }),
69
+ kind === 'environment'
70
+ ? send(CONTRACTS.environment.register, {
71
+ pathPrefix: ws(workspaceId),
72
+ body: body as RegisterEnvironmentProviderInput,
73
+ })
74
+ : send(CONTRACTS['runner-pool'].register, {
75
+ pathPrefix: ws(workspaceId),
76
+ body: body as RegisterRunnerPoolInput,
77
+ }),
37
78
 
38
79
  updateProviderSecrets: (
39
80
  workspaceId: string,
40
81
  kind: ProviderConnectionKind,
41
82
  secrets: Record<string, string>,
42
- ) =>
43
- http<ProviderConnection>(`${base(workspaceId, kind)}/connection/secrets`, {
44
- method: 'PUT',
45
- body: { secrets },
46
- }),
83
+ ) => send(CONTRACTS[kind].updateSecrets, { pathPrefix: ws(workspaceId), body: { secrets } }),
47
84
 
48
85
  testProviderConnection: (
49
86
  workspaceId: string,
50
87
  kind: ProviderConnectionKind,
51
88
  body: TestProviderInput,
52
89
  ) =>
53
- http<ConnectionTestResult>(`${base(workspaceId, kind)}/connection/test`, {
54
- method: 'POST',
55
- body,
56
- }),
90
+ kind === 'environment'
91
+ ? send(CONTRACTS.environment.test, {
92
+ pathPrefix: ws(workspaceId),
93
+ body: body as TestEnvironmentConnectionInput,
94
+ })
95
+ : send(CONTRACTS['runner-pool'].test, {
96
+ pathPrefix: ws(workspaceId),
97
+ body: body as TestRunnerPoolConnectionInput,
98
+ }),
57
99
 
58
100
  deleteProviderConnection: (workspaceId: string, kind: ProviderConnectionKind) =>
59
- http(`${base(workspaceId, kind)}/connection`, { method: 'DELETE' }),
101
+ send(CONTRACTS[kind].unregister, { pathPrefix: ws(workspaceId) }),
60
102
  }
61
103
  }
@@ -1,67 +1,77 @@
1
- import type { Service, WorkspaceMount } from '~/types/domain'
2
- import type {
3
- CreateScheduleInput,
4
- PipelineSchedule,
5
- ScheduleRun,
6
- UpdateScheduleInput,
7
- } from '~/types/recurring'
1
+ import {
2
+ createScheduleContract,
3
+ deleteScheduleContract,
4
+ listSchedulesContract,
5
+ listScheduleRunsContract,
6
+ listServiceCatalogContract,
7
+ listServiceMountsContract,
8
+ mountServiceContract,
9
+ runScheduleNowContract,
10
+ unmountServiceContract,
11
+ updateScheduleContract,
12
+ updateServiceMountLayoutContract,
13
+ } from '@cat-factory/contracts'
14
+ import type { UpdateScheduleInput } from '~/types/recurring'
15
+ import type { SendParams } from './client'
8
16
  import type { ApiContext, Position } from './context'
9
17
 
18
+ // The create-schedule body is typed from the contract's INPUT shape so the
19
+ // valibot-defaulted `enabled` stays optional for callers (the exported
20
+ // `CreateScheduleInput` is the post-default OUTPUT shape).
21
+ type CreateScheduleBody = NonNullable<SendParams<typeof createScheduleContract>['body']>
22
+
10
23
  /** Recurring (scheduled) pipelines + the in-org shared-service mount catalog. */
11
- export function recurringApi({ http, ws }: ApiContext) {
24
+ export function recurringApi({ send, ws }: ApiContext) {
12
25
  return {
13
26
  // ---- recurring pipelines (scheduled runs against a service) -----------
14
27
  listRecurringPipelines: (workspaceId: string) =>
15
- http<PipelineSchedule[]>(`${ws(workspaceId)}/recurring-pipelines`),
28
+ send(listSchedulesContract, { pathPrefix: ws(workspaceId) }),
16
29
 
17
- createRecurringPipeline: (workspaceId: string, body: CreateScheduleInput) =>
18
- http<PipelineSchedule>(`${ws(workspaceId)}/recurring-pipelines`, { method: 'POST', body }),
30
+ createRecurringPipeline: (workspaceId: string, body: CreateScheduleBody) =>
31
+ send(createScheduleContract, { pathPrefix: ws(workspaceId), body }),
19
32
 
20
33
  updateRecurringPipeline: (workspaceId: string, id: string, body: UpdateScheduleInput) =>
21
- http<PipelineSchedule>(`${ws(workspaceId)}/recurring-pipelines/${encodeURIComponent(id)}`, {
22
- method: 'PATCH',
34
+ send(updateScheduleContract, {
35
+ pathPrefix: ws(workspaceId),
36
+ pathParams: { scheduleId: id },
23
37
  body,
24
38
  }),
25
39
 
26
40
  deleteRecurringPipeline: (workspaceId: string, id: string) =>
27
- http(`${ws(workspaceId)}/recurring-pipelines/${encodeURIComponent(id)}`, {
28
- method: 'DELETE',
29
- }),
41
+ send(deleteScheduleContract, { pathPrefix: ws(workspaceId), pathParams: { scheduleId: id } }),
30
42
 
31
43
  listScheduleRuns: (workspaceId: string, id: string) =>
32
- http<ScheduleRun[]>(`${ws(workspaceId)}/recurring-pipelines/${encodeURIComponent(id)}/runs`),
44
+ send(listScheduleRunsContract, {
45
+ pathPrefix: ws(workspaceId),
46
+ pathParams: { scheduleId: id },
47
+ }),
33
48
 
34
49
  runScheduleNow: (workspaceId: string, id: string) =>
35
- http<PipelineSchedule>(
36
- `${ws(workspaceId)}/recurring-pipelines/${encodeURIComponent(id)}/run-now`,
37
- { method: 'POST' },
38
- ),
50
+ send(runScheduleNowContract, { pathPrefix: ws(workspaceId), pathParams: { scheduleId: id } }),
39
51
 
40
52
  // ---- in-org shared services (mount/unmount + org catalog) -------------
41
53
  // The services this workspace mounts, and the org catalog it can mount from. A 503
42
54
  // means the feature isn't wired (the store hides its UI on any error here).
43
55
  listServiceMounts: (workspaceId: string) =>
44
- http<WorkspaceMount[]>(`${ws(workspaceId)}/services`),
56
+ send(listServiceMountsContract, { pathPrefix: ws(workspaceId) }),
45
57
 
46
58
  listServiceCatalog: (workspaceId: string) =>
47
- http<Service[]>(`${ws(workspaceId)}/services/catalog`),
59
+ send(listServiceCatalogContract, { pathPrefix: ws(workspaceId) }),
48
60
 
49
61
  mountService: (workspaceId: string, serviceId: string, body: { position?: Position } = {}) =>
50
- http<WorkspaceMount>(`${ws(workspaceId)}/services/${encodeURIComponent(serviceId)}`, {
51
- method: 'POST',
52
- body,
53
- }),
62
+ send(mountServiceContract, { pathPrefix: ws(workspaceId), pathParams: { serviceId }, body }),
54
63
 
55
64
  unmountService: (workspaceId: string, serviceId: string) =>
56
- http(`${ws(workspaceId)}/services/${encodeURIComponent(serviceId)}`, { method: 'DELETE' }),
65
+ send(unmountServiceContract, { pathPrefix: ws(workspaceId), pathParams: { serviceId } }),
57
66
 
58
67
  updateMountLayout: (
59
68
  workspaceId: string,
60
69
  serviceId: string,
61
70
  body: { position?: Position; size?: { w: number; h: number } | null },
62
71
  ) =>
63
- http<WorkspaceMount>(`${ws(workspaceId)}/services/${encodeURIComponent(serviceId)}/layout`, {
64
- method: 'PATCH',
72
+ send(updateServiceMountLayoutContract, {
73
+ pathPrefix: ws(workspaceId),
74
+ pathParams: { serviceId },
65
75
  body,
66
76
  }),
67
77
  }
@@ -1,61 +1,63 @@
1
+ import {
2
+ deleteIncidentEnrichmentContract,
3
+ deleteObservabilityConnectionContract,
4
+ deleteReleaseHealthConfigContract,
5
+ getIncidentEnrichmentContract,
6
+ getObservabilityConnectionContract,
7
+ listReleaseHealthConfigsContract,
8
+ setIncidentEnrichmentContract,
9
+ setObservabilityConnectionContract,
10
+ upsertReleaseHealthConfigContract,
11
+ } from '@cat-factory/contracts'
1
12
  import type {
2
- ObservabilityConnectionView,
3
- ReleaseHealthConfig,
4
13
  UpsertObservabilityConnectionInput,
5
14
  UpsertReleaseHealthConfigInput,
6
15
  } from '~/types/releaseHealth'
7
- import type {
8
- IncidentEnrichmentView,
9
- UpsertIncidentEnrichmentInput,
10
- } from '~/types/incidentEnrichment'
16
+ import type { UpsertIncidentEnrichmentInput } from '~/types/incidentEnrichment'
11
17
  import type { ApiContext } from './context'
12
18
 
13
19
  /** Post-release-health: the observability connection + per-block monitor/SLO mapping. */
14
- export function releaseHealthApi({ http, ws }: ApiContext) {
20
+ export function releaseHealthApi({ send, ws }: ApiContext) {
15
21
  return {
16
22
  // ---- Observability connection ------------------------------------------
17
23
  getObservabilityConnection: (workspaceId: string) =>
18
- http<ObservabilityConnectionView>(`${ws(workspaceId)}/observability/connection`),
24
+ send(getObservabilityConnectionContract, { pathPrefix: ws(workspaceId) }),
19
25
 
20
26
  setObservabilityConnection: (workspaceId: string, body: UpsertObservabilityConnectionInput) =>
21
- http<ObservabilityConnectionView>(`${ws(workspaceId)}/observability/connection`, {
22
- method: 'PUT',
23
- body,
24
- }),
27
+ send(setObservabilityConnectionContract, { pathPrefix: ws(workspaceId), body }),
25
28
 
26
29
  deleteObservabilityConnection: (workspaceId: string) =>
27
- http(`${ws(workspaceId)}/observability/connection`, { method: 'DELETE' }),
30
+ send(deleteObservabilityConnectionContract, { pathPrefix: ws(workspaceId) }),
28
31
 
29
32
  // ---- Per-block monitor/SLO config --------------------------------------
30
33
  listReleaseHealthConfigs: (workspaceId: string) =>
31
- http<ReleaseHealthConfig[]>(`${ws(workspaceId)}/release-health-configs`),
34
+ send(listReleaseHealthConfigsContract, { pathPrefix: ws(workspaceId) }),
32
35
 
33
36
  upsertReleaseHealthConfig: (
34
37
  workspaceId: string,
35
38
  blockId: string,
36
39
  body: UpsertReleaseHealthConfigInput,
37
40
  ) =>
38
- http<ReleaseHealthConfig>(
39
- `${ws(workspaceId)}/release-health-configs/${encodeURIComponent(blockId)}`,
40
- { method: 'PUT', body },
41
- ),
41
+ send(upsertReleaseHealthConfigContract, {
42
+ pathPrefix: ws(workspaceId),
43
+ pathParams: { blockId },
44
+ body,
45
+ }),
42
46
 
43
47
  deleteReleaseHealthConfig: (workspaceId: string, blockId: string) =>
44
- http(`${ws(workspaceId)}/release-health-configs/${encodeURIComponent(blockId)}`, {
45
- method: 'DELETE',
48
+ send(deleteReleaseHealthConfigContract, {
49
+ pathPrefix: ws(workspaceId),
50
+ pathParams: { blockId },
46
51
  }),
47
52
 
48
53
  // ---- Incident enrichment (PagerDuty + incident.io, write-only secrets) --
49
54
  getIncidentEnrichment: (workspaceId: string) =>
50
- http<IncidentEnrichmentView>(`${ws(workspaceId)}/incident-enrichment`),
55
+ send(getIncidentEnrichmentContract, { pathPrefix: ws(workspaceId) }),
51
56
 
52
57
  setIncidentEnrichment: (workspaceId: string, body: UpsertIncidentEnrichmentInput) =>
53
- http<IncidentEnrichmentView>(`${ws(workspaceId)}/incident-enrichment`, {
54
- method: 'PUT',
55
- body,
56
- }),
58
+ send(setIncidentEnrichmentContract, { pathPrefix: ws(workspaceId), body }),
57
59
 
58
60
  deleteIncidentEnrichment: (workspaceId: string) =>
59
- http(`${ws(workspaceId)}/incident-enrichment`, { method: 'DELETE' }),
61
+ send(deleteIncidentEnrichmentContract, { pathPrefix: ws(workspaceId) }),
60
62
  }
61
63
  }