@elevasis/core 0.20.0 → 0.22.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.
Files changed (77) hide show
  1. package/dist/index.d.ts +524 -6
  2. package/dist/index.js +417 -42
  3. package/dist/knowledge/index.d.ts +151 -1
  4. package/dist/organization-model/index.d.ts +524 -6
  5. package/dist/organization-model/index.js +417 -42
  6. package/dist/test-utils/index.d.ts +270 -1
  7. package/dist/test-utils/index.js +407 -41
  8. package/package.json +5 -5
  9. package/src/_gen/__tests__/__snapshots__/contracts.md.snap +501 -303
  10. package/src/auth/multi-tenancy/permissions.ts +20 -8
  11. package/src/business/README.md +2 -2
  12. package/src/business/acquisition/api-schemas.test.ts +198 -0
  13. package/src/business/acquisition/api-schemas.ts +250 -9
  14. package/src/business/acquisition/build-templates.test.ts +28 -0
  15. package/src/business/acquisition/build-templates.ts +20 -8
  16. package/src/business/acquisition/index.ts +12 -0
  17. package/src/business/acquisition/types.ts +6 -1
  18. package/src/business/clients/api-schemas.test.ts +115 -0
  19. package/src/business/clients/api-schemas.ts +158 -0
  20. package/src/business/clients/index.ts +1 -0
  21. package/src/business/deals/api-schemas.ts +8 -0
  22. package/src/business/index.ts +5 -2
  23. package/src/business/projects/types.ts +19 -0
  24. package/src/execution/engine/__tests__/fixtures/test-agents.ts +10 -8
  25. package/src/execution/engine/agent/core/__tests__/agent.test.ts +16 -12
  26. package/src/execution/engine/agent/core/__tests__/error-passthrough.test.ts +4 -3
  27. package/src/execution/engine/agent/core/types.ts +25 -15
  28. package/src/execution/engine/agent/index.ts +6 -4
  29. package/src/execution/engine/agent/reasoning/__tests__/request-builder.test.ts +24 -18
  30. package/src/execution/engine/index.ts +3 -0
  31. package/src/execution/engine/tools/integration/server/adapters/apify/apify-adapter.test.ts +55 -0
  32. package/src/execution/engine/tools/integration/server/adapters/apify/apify-adapter.ts +107 -41
  33. package/src/execution/engine/tools/integration/server/adapters/apollo/apollo-adapter.test.ts +48 -0
  34. package/src/execution/engine/tools/integration/server/adapters/apollo/apollo-adapter.ts +99 -0
  35. package/src/execution/engine/tools/integration/server/adapters/apollo/index.ts +1 -0
  36. package/src/execution/engine/tools/integration/server/adapters/clickup/clickup-adapter.test.ts +18 -0
  37. package/src/execution/engine/tools/integration/server/adapters/clickup/clickup-adapter.ts +194 -0
  38. package/src/execution/engine/tools/integration/server/adapters/clickup/index.ts +7 -0
  39. package/src/execution/engine/workflow/types.ts +7 -0
  40. package/src/integrations/credentials/api-schemas.ts +21 -2
  41. package/src/integrations/credentials/schemas.ts +200 -164
  42. package/src/organization-model/README.md +10 -3
  43. package/src/organization-model/__tests__/defaults.test.ts +6 -0
  44. package/src/organization-model/__tests__/domains/resources.test.ts +188 -0
  45. package/src/organization-model/__tests__/domains/roles.test.ts +402 -347
  46. package/src/organization-model/__tests__/domains/systems.test.ts +193 -0
  47. package/src/organization-model/__tests__/knowledge.test.ts +39 -0
  48. package/src/organization-model/__tests__/prospecting-ssot.test.ts +7 -4
  49. package/src/organization-model/__tests__/resolve.test.ts +1 -1
  50. package/src/organization-model/defaults.ts +24 -3
  51. package/src/organization-model/domains/knowledge.ts +3 -2
  52. package/src/organization-model/domains/prospecting.ts +182 -25
  53. package/src/organization-model/domains/resources.ts +88 -0
  54. package/src/organization-model/domains/roles.ts +93 -55
  55. package/src/organization-model/domains/sales.ts +24 -3
  56. package/src/organization-model/domains/systems.ts +46 -0
  57. package/src/organization-model/icons.ts +1 -0
  58. package/src/organization-model/index.ts +2 -0
  59. package/src/organization-model/organization-model.mdx +33 -14
  60. package/src/organization-model/published.ts +52 -1
  61. package/src/organization-model/schema.ts +121 -0
  62. package/src/organization-model/types.ts +46 -1
  63. package/src/platform/api/types.ts +38 -35
  64. package/src/platform/constants/versions.ts +1 -1
  65. package/src/platform/registry/__tests__/resource-registry.test.ts +2051 -2005
  66. package/src/platform/registry/__tests__/validation.test.ts +1343 -1086
  67. package/src/platform/registry/index.ts +14 -0
  68. package/src/platform/registry/resource-registry.ts +40 -2
  69. package/src/platform/registry/serialization.ts +241 -202
  70. package/src/platform/registry/serialized-types.ts +1 -0
  71. package/src/platform/registry/types.ts +411 -361
  72. package/src/platform/registry/validation.ts +743 -513
  73. package/src/projects/api-schemas.ts +290 -267
  74. package/src/reference/_generated/contracts.md +501 -303
  75. package/src/reference/glossary.md +8 -3
  76. package/src/server.ts +2 -0
  77. package/src/supabase/database.types.ts +121 -0
@@ -1,34 +1,60 @@
1
1
  import type { BaseIntegrationAdapter } from '../../../base-integration-adapter'
2
2
  import { ToolingError } from '../../../../types'
3
- import type { ExecutionContext } from '../../../../../base/types'
4
- import { runActor } from './fetch/run-actor/index'
5
- import { getDatasetItems } from './fetch/get-dataset-items/index'
6
- import { startActor } from './fetch/start-actor/index'
7
- import type { StartActorParams } from '../../../../integration/types/apify'
8
-
9
- interface ApifyCredentials {
10
- apiToken?: string
11
- apiKey?: string
12
- api_token?: string
13
- token?: string
14
- }
15
-
16
- interface RunActorParams {
17
- actorId: string
18
- input?: Record<string, unknown>
19
- timeoutSecs?: number
3
+ import type { ExecutionContext } from '../../../../../base/types'
4
+ import { runActor } from './fetch/run-actor/index'
5
+ import { getDatasetItems } from './fetch/get-dataset-items/index'
6
+ import { startActor } from './fetch/start-actor/index'
7
+ import type { StartActorParams } from '../../../../integration/types/apify'
8
+ import { createHttpError, withRetry, DEFAULT_RETRY_POLICY } from '../../../../../../../platform/resilience'
9
+
10
+ interface ApifyCredentials {
11
+ apiToken?: string
12
+ apiKey?: string
13
+ api_token?: string
14
+ token?: string
15
+ }
16
+
17
+ export interface ApifyVerifyResult {
18
+ ok: true
19
+ provider: 'apify'
20
+ username?: string
21
+ plan?: string
22
+ }
23
+
24
+ type ApifyPlan =
25
+ | string
26
+ | {
27
+ id?: string
28
+ name?: string
29
+ description?: string
30
+ }
31
+
32
+ interface ApifyUserResponse {
33
+ data?: {
34
+ username?: string
35
+ plan?: ApifyPlan
36
+ id?: string
37
+ }
38
+ }
39
+
40
+ interface RunActorParams {
41
+ actorId: string
42
+ input?: Record<string, unknown>
43
+ timeoutSecs?: number
20
44
  pollIntervalSecs?: number
21
45
  maxItems?: number
22
46
  }
23
47
 
24
48
  interface GetDatasetItemsParams {
25
49
  datasetId: string
26
- maxItems?: number
27
- offset?: number
28
- }
29
-
30
- /**
31
- * Apify integration adapter
50
+ maxItems?: number
51
+ offset?: number
52
+ }
53
+
54
+ const APIFY_API_BASE_URL = 'https://api.apify.com/v2'
55
+
56
+ /**
57
+ * Apify integration adapter
32
58
  *
33
59
  * Provides access to Apify actor automation platform for web scraping
34
60
  * and browser automation tasks.
@@ -62,12 +88,14 @@ export class ApifyAdapter implements BaseIntegrationAdapter {
62
88
  // Normalize credentials to consistent field name
63
89
  const normalizedCreds = this.normalizeCredentials(credentials as unknown as ApifyCredentials)
64
90
 
65
- // Route to method handler
66
- switch (method) {
67
- case 'runActor':
68
- return runActor(normalizedCreds, params as RunActorParams, context)
69
- case 'getDatasetItems':
70
- return getDatasetItems(normalizedCreds, params as GetDatasetItemsParams, context)
91
+ // Route to method handler
92
+ switch (method) {
93
+ case 'verify':
94
+ return this.verify(normalizedCreds, context)
95
+ case 'runActor':
96
+ return runActor(normalizedCreds, params as RunActorParams, context)
97
+ case 'getDatasetItems':
98
+ return getDatasetItems(normalizedCreds, params as GetDatasetItemsParams, context)
71
99
  case 'startActor':
72
100
  return startActor(normalizedCreds, params as StartActorParams, context)
73
101
  default:
@@ -79,15 +107,47 @@ export class ApifyAdapter implements BaseIntegrationAdapter {
79
107
  }
80
108
  }
81
109
 
82
- validateCredentials(credentials: Record<string, unknown>): boolean {
83
- const creds = credentials as unknown as ApifyCredentials
84
- const token = creds.apiToken || creds.apiKey || creds.api_token || creds.token
85
- return !!token && typeof token === 'string' && token.length > 0
86
- }
87
-
88
- /**
89
- * Normalize credentials to use consistent field name
90
- */
110
+ validateCredentials(credentials: Record<string, unknown>): boolean {
111
+ const creds = credentials as unknown as ApifyCredentials
112
+ const token = creds.apiToken || creds.apiKey || creds.api_token || creds.token
113
+ return typeof token === 'string' && token.trim().length > 0
114
+ }
115
+
116
+ async verify(credentials: ApifyCredentials, context?: ExecutionContext): Promise<ApifyVerifyResult> {
117
+ const normalizedCreds = this.normalizeCredentials(credentials)
118
+ const response = await withRetry(async () => {
119
+ const result = await fetch(`${APIFY_API_BASE_URL}/users/me`, {
120
+ method: 'GET',
121
+ headers: {
122
+ Authorization: `Bearer ${normalizedCreds.apiToken}`,
123
+ 'Content-Type': 'application/json'
124
+ }
125
+ })
126
+
127
+ if (!result.ok) {
128
+ throw await createHttpError(result, {
129
+ integration: this.name,
130
+ method: 'verify',
131
+ organizationId: context?.organizationId
132
+ })
133
+ }
134
+
135
+ return result
136
+ }, DEFAULT_RETRY_POLICY)
137
+
138
+ const data = (await response.json()) as ApifyUserResponse
139
+
140
+ return {
141
+ ok: true,
142
+ provider: 'apify',
143
+ username: data.data?.username ?? data.data?.id,
144
+ plan: formatApifyPlan(data.data?.plan)
145
+ }
146
+ }
147
+
148
+ /**
149
+ * Normalize credentials to use consistent field name
150
+ */
91
151
  private normalizeCredentials(credentials: ApifyCredentials): { apiToken: string } {
92
152
  const token = credentials.apiToken || credentials.apiKey || credentials.api_token || credentials.token
93
153
  if (!token) {
@@ -95,6 +155,12 @@ export class ApifyAdapter implements BaseIntegrationAdapter {
95
155
  integration: this.name
96
156
  })
97
157
  }
98
- return { apiToken: token }
99
- }
100
- }
158
+ return { apiToken: token.trim() }
159
+ }
160
+ }
161
+
162
+ function formatApifyPlan(plan: ApifyPlan | undefined): string | undefined {
163
+ if (!plan) return undefined
164
+ if (typeof plan === 'string') return plan
165
+ return plan.id ?? plan.name ?? plan.description
166
+ }
@@ -0,0 +1,48 @@
1
+ import { afterEach, describe, expect, it, vi } from 'vitest'
2
+ import { ApolloAdapter } from './apollo-adapter'
3
+
4
+ describe('ApolloAdapter', () => {
5
+ const adapter = new ApolloAdapter()
6
+ const originalFetch = globalThis.fetch
7
+
8
+ afterEach(() => {
9
+ vi.restoreAllMocks()
10
+ globalThis.fetch = originalFetch
11
+ })
12
+
13
+ it('accepts Apollo API keys stored in generic apiKey shape', () => {
14
+ expect(adapter.validateCredentials({ apiKey: 'apollo-key' })).toBe(true)
15
+ })
16
+
17
+ it('accepts Apollo API keys stored in legacy Apollo alias shapes', () => {
18
+ expect(adapter.validateCredentials({ apolloApiKey: 'apollo-key' })).toBe(true)
19
+ expect(adapter.validateCredentials({ APOLLO_API_KEY: 'apollo-key' })).toBe(true)
20
+ expect(adapter.validateCredentials({ key: 'apollo-key' })).toBe(true)
21
+ })
22
+
23
+ it('rejects blank tokens', () => {
24
+ expect(adapter.validateCredentials({ apiKey: ' ' })).toBe(false)
25
+ })
26
+
27
+ it('verifies credentials against the Apollo auth health endpoint', async () => {
28
+ const fetchMock = vi.fn().mockResolvedValue({
29
+ ok: true
30
+ })
31
+ globalThis.fetch = fetchMock as unknown as typeof fetch
32
+
33
+ await expect(adapter.verify({ apiKey: ' apollo-key ' })).resolves.toEqual({
34
+ ok: true,
35
+ provider: 'apollo',
36
+ authenticated: true
37
+ })
38
+
39
+ expect(fetchMock).toHaveBeenCalledWith('https://api.apollo.io/v1/auth/health', {
40
+ method: 'GET',
41
+ headers: {
42
+ accept: 'application/json',
43
+ 'x-api-key': 'apollo-key',
44
+ authorization: 'Bearer apollo-key'
45
+ }
46
+ })
47
+ })
48
+ })
@@ -0,0 +1,99 @@
1
+ import type { BaseIntegrationAdapter } from '../../../base-integration-adapter'
2
+ import { ToolingError } from '../../../../types'
3
+ import type { ExecutionContext } from '../../../../../base/types'
4
+ import { createHttpError, withRetry, DEFAULT_RETRY_POLICY } from '../../../../../../../platform/resilience'
5
+
6
+ export interface ApolloCredentials {
7
+ apiKey?: string
8
+ apolloApiKey?: string
9
+ APOLLO_API_KEY?: string
10
+ key?: string
11
+ token?: string
12
+ }
13
+
14
+ export interface ApolloVerifyResult {
15
+ ok: true
16
+ provider: 'apollo'
17
+ authenticated: boolean
18
+ }
19
+
20
+ const APOLLO_AUTH_HEALTH_URL = 'https://api.apollo.io/v1/auth/health'
21
+
22
+ export class ApolloAdapter implements BaseIntegrationAdapter {
23
+ readonly name = 'apollo'
24
+
25
+ async call(
26
+ method: string,
27
+ params: unknown,
28
+ credentials: Record<string, unknown>,
29
+ context?: ExecutionContext
30
+ ): Promise<unknown> {
31
+ if (!this.validateCredentials(credentials)) {
32
+ throw new ToolingError('credentials_invalid', 'Invalid Apollo credentials', {
33
+ integration: this.name,
34
+ organizationId: context?.organizationId
35
+ })
36
+ }
37
+
38
+ switch (method) {
39
+ case 'verify':
40
+ return this.verify(credentials as unknown as ApolloCredentials, context)
41
+ default:
42
+ throw new ToolingError('method_not_found', `Unknown Apollo method: ${method}`, {
43
+ integration: this.name,
44
+ method,
45
+ organizationId: context?.organizationId
46
+ })
47
+ }
48
+ }
49
+
50
+ validateCredentials(credentials: Record<string, unknown>): boolean {
51
+ const token = this.getToken(credentials as unknown as ApolloCredentials)
52
+ return typeof token === 'string' && token.trim().length > 0
53
+ }
54
+
55
+ async verify(credentials: ApolloCredentials, context?: ExecutionContext): Promise<ApolloVerifyResult> {
56
+ const token = this.normalizeCredentials(credentials).apiKey
57
+ await withRetry(async () => {
58
+ const response = await fetch(APOLLO_AUTH_HEALTH_URL, {
59
+ method: 'GET',
60
+ headers: {
61
+ accept: 'application/json',
62
+ 'x-api-key': token,
63
+ authorization: `Bearer ${token}`
64
+ }
65
+ })
66
+
67
+ if (!response.ok) {
68
+ throw await createHttpError(response, {
69
+ integration: this.name,
70
+ method: 'verify',
71
+ organizationId: context?.organizationId
72
+ })
73
+ }
74
+
75
+ return response
76
+ }, DEFAULT_RETRY_POLICY)
77
+
78
+ return {
79
+ ok: true,
80
+ provider: 'apollo',
81
+ authenticated: true
82
+ }
83
+ }
84
+
85
+ private normalizeCredentials(credentials: ApolloCredentials): { apiKey: string } {
86
+ const token = this.getToken(credentials)
87
+ if (!token) {
88
+ throw new ToolingError('credentials_invalid', 'Missing Apollo API key', {
89
+ integration: this.name
90
+ })
91
+ }
92
+
93
+ return { apiKey: token.trim() }
94
+ }
95
+
96
+ private getToken(credentials: ApolloCredentials): string | undefined {
97
+ return credentials.apiKey ?? credentials.apolloApiKey ?? credentials.APOLLO_API_KEY ?? credentials.key ?? credentials.token
98
+ }
99
+ }
@@ -0,0 +1 @@
1
+ export { ApolloAdapter, type ApolloCredentials, type ApolloVerifyResult } from './apollo-adapter'
@@ -0,0 +1,18 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { ClickUpAdapter } from './clickup-adapter'
3
+
4
+ describe('ClickUpAdapter', () => {
5
+ const adapter = new ClickUpAdapter()
6
+
7
+ it('accepts ClickUp personal tokens stored in provider-specific apiToken shape', () => {
8
+ expect(adapter.validateCredentials({ apiToken: 'pk_test_123' })).toBe(true)
9
+ })
10
+
11
+ it('accepts ClickUp personal tokens stored in generic api-key shape', () => {
12
+ expect(adapter.validateCredentials({ apiKey: 'pk_test_123' })).toBe(true)
13
+ })
14
+
15
+ it('rejects non-ClickUp API keys', () => {
16
+ expect(adapter.validateCredentials({ apiKey: 'sk_test_123' })).toBe(false)
17
+ })
18
+ })
@@ -0,0 +1,194 @@
1
+ import type { BaseIntegrationAdapter } from '../../../base-integration-adapter'
2
+ import { ToolingError } from '../../../../types'
3
+ import type { ExecutionContext } from '../../../../../base/types'
4
+ import { createHttpError, withRetry, DEFAULT_RETRY_POLICY } from '../../../../../../../platform/resilience'
5
+
6
+ export interface ClickUpCredentials {
7
+ apiToken?: string
8
+ apiKey?: string
9
+ api_token?: string
10
+ token?: string
11
+ }
12
+
13
+ export interface ClickUpVerifyResult {
14
+ ok: true
15
+ provider: 'clickup'
16
+ teamCount: number
17
+ teams: Array<{
18
+ id: string
19
+ name: string
20
+ }>
21
+ }
22
+
23
+ export interface ClickUpCreateTaskParams {
24
+ listId: string
25
+ name: string
26
+ markdownContent: string
27
+ }
28
+
29
+ export interface ClickUpCreateTaskResult {
30
+ id: string
31
+ url?: string
32
+ name: string
33
+ }
34
+
35
+ interface ClickUpTeamResponse {
36
+ teams?: Array<{
37
+ id?: string | number
38
+ name?: string
39
+ }>
40
+ }
41
+
42
+ interface ClickUpTaskResponse {
43
+ id?: string
44
+ url?: string
45
+ name?: string
46
+ }
47
+
48
+ const CLICKUP_API_BASE_URL = 'https://api.clickup.com/api/v2'
49
+
50
+ export class ClickUpAdapter implements BaseIntegrationAdapter {
51
+ readonly name = 'clickup'
52
+
53
+ async call(
54
+ method: string,
55
+ params: unknown,
56
+ credentials: Record<string, unknown>,
57
+ context?: ExecutionContext
58
+ ): Promise<unknown> {
59
+ if (!this.validateCredentials(credentials)) {
60
+ throw new ToolingError('credentials_invalid', 'Invalid ClickUp credentials', {
61
+ integration: this.name,
62
+ organizationId: context?.organizationId
63
+ })
64
+ }
65
+
66
+ const creds = this.normalizeCredentials(credentials as unknown as ClickUpCredentials)
67
+
68
+ switch (method) {
69
+ case 'verify':
70
+ return this.verify(creds, context)
71
+ case 'createTask':
72
+ return this.createTask(creds, params as ClickUpCreateTaskParams, context)
73
+ default:
74
+ throw new ToolingError('method_not_found', `Unknown ClickUp method: ${method}`, {
75
+ integration: this.name,
76
+ method,
77
+ organizationId: context?.organizationId
78
+ })
79
+ }
80
+ }
81
+
82
+ validateCredentials(credentials: Record<string, unknown>): boolean {
83
+ const creds = credentials as unknown as ClickUpCredentials
84
+ const token = creds.apiToken || creds.apiKey || creds.api_token || creds.token
85
+ return typeof token === 'string' && token.trim().startsWith('pk_')
86
+ }
87
+
88
+ async verify(credentials: ClickUpCredentials, context?: ExecutionContext): Promise<ClickUpVerifyResult> {
89
+ const response = await this.request('/team', this.normalizeCredentials(credentials), { method: 'GET' }, 'verify', context)
90
+ const data = (await response.json()) as ClickUpTeamResponse
91
+ const teams = (data.teams ?? []).map((team) => ({
92
+ id: String(team.id ?? ''),
93
+ name: team.name ?? ''
94
+ }))
95
+
96
+ return {
97
+ ok: true,
98
+ provider: 'clickup',
99
+ teamCount: teams.length,
100
+ teams: teams.filter((team) => team.id || team.name)
101
+ }
102
+ }
103
+
104
+ async createTask(
105
+ credentials: { apiToken: string },
106
+ params: ClickUpCreateTaskParams,
107
+ context?: ExecutionContext
108
+ ): Promise<ClickUpCreateTaskResult> {
109
+ if (!params.listId || !params.name || !params.markdownContent) {
110
+ throw new ToolingError('validation_error', 'listId, name, and markdownContent are required', {
111
+ integration: this.name,
112
+ method: 'createTask',
113
+ organizationId: context?.organizationId
114
+ })
115
+ }
116
+
117
+ const response = await this.request(
118
+ `/list/${encodeURIComponent(params.listId)}/task`,
119
+ this.normalizeCredentials(credentials),
120
+ {
121
+ method: 'POST',
122
+ body: JSON.stringify({
123
+ name: params.name,
124
+ markdown_content: params.markdownContent
125
+ })
126
+ },
127
+ 'createTask',
128
+ context
129
+ )
130
+ const data = (await response.json()) as ClickUpTaskResponse
131
+
132
+ if (!data.id) {
133
+ throw new ToolingError('api_error', 'ClickUp create task response did not include a task ID', {
134
+ integration: this.name,
135
+ method: 'createTask',
136
+ organizationId: context?.organizationId
137
+ })
138
+ }
139
+
140
+ return {
141
+ id: data.id,
142
+ url: data.url,
143
+ name: data.name ?? params.name
144
+ }
145
+ }
146
+
147
+ private async request(
148
+ path: string,
149
+ credentials: { apiToken: string },
150
+ init: RequestInit,
151
+ method: string,
152
+ context?: ExecutionContext
153
+ ): Promise<Response> {
154
+ if (!this.validateCredentials(credentials as unknown as Record<string, unknown>)) {
155
+ throw new ToolingError('credentials_invalid', 'ClickUp personal API token must start with pk_', {
156
+ integration: this.name,
157
+ method,
158
+ organizationId: context?.organizationId
159
+ })
160
+ }
161
+
162
+ return await withRetry(async () => {
163
+ const response = await fetch(`${CLICKUP_API_BASE_URL}${path}`, {
164
+ ...init,
165
+ headers: {
166
+ Authorization: credentials.apiToken,
167
+ 'Content-Type': 'application/json',
168
+ ...init.headers
169
+ }
170
+ })
171
+
172
+ if (!response.ok) {
173
+ throw await createHttpError(response, {
174
+ integration: this.name,
175
+ method,
176
+ organizationId: context?.organizationId
177
+ })
178
+ }
179
+
180
+ return response
181
+ }, DEFAULT_RETRY_POLICY)
182
+ }
183
+
184
+ private normalizeCredentials(credentials: ClickUpCredentials): { apiToken: string } {
185
+ const token = credentials.apiToken || credentials.apiKey || credentials.api_token || credentials.token
186
+ if (!token || !token.trim().startsWith('pk_')) {
187
+ throw new ToolingError('credentials_invalid', 'ClickUp personal API token must start with pk_', {
188
+ integration: this.name
189
+ })
190
+ }
191
+
192
+ return { apiToken: token.trim() }
193
+ }
194
+ }
@@ -0,0 +1,7 @@
1
+ export { ClickUpAdapter } from './clickup-adapter'
2
+ export type {
3
+ ClickUpCredentials,
4
+ ClickUpVerifyResult,
5
+ ClickUpCreateTaskParams,
6
+ ClickUpCreateTaskResult
7
+ } from './clickup-adapter'
@@ -7,16 +7,23 @@ import type { z } from 'zod'
7
7
 
8
8
  import type { ExecutionContext } from '../base/types'
9
9
  import type { ResourceDefinition } from '../../../platform/registry/types'
10
+ import type { WorkflowResourceEntry } from '../../../organization-model/domains/resources'
10
11
  import type { ResourceMetricsConfig } from '../../../operations/observability/types'
11
12
  import type { ExecutionInterface } from '../interface/types'
12
13
 
13
14
  // Workflow configuration
14
15
  export interface WorkflowConfig extends ResourceDefinition {
15
16
  type: 'workflow'
17
+ /** OM descriptor backing canonical identity and governance metadata. */
18
+ resource?: WorkflowResourceEntry
16
19
  /** Lead-gen capability key for registry derivation (e.g. 'lead-gen.company.apollo-import') */
17
20
  capabilityKey?: string
18
21
  }
19
22
 
23
+ export type DescriptorBackedWorkflowConfig = Omit<WorkflowConfig, 'resourceId' | 'type' | 'resource'> & {
24
+ resource: WorkflowResourceEntry
25
+ }
26
+
20
27
  // Workflow step definition
21
28
  export interface WorkflowStepDefinition {
22
29
  id: string
@@ -18,11 +18,13 @@ import { UuidSchema, CredentialNameSchema } from '../../platform/utils/validatio
18
18
  * - 'oauth': All OAuth providers (notion, google-sheets) store this type
19
19
  * - 'api-key': Generic single-field API key credentials
20
20
  * - 'webhook-secret': Webhook signing secrets for signature validation
21
+ * - 'api-key-secret': API key and secret pair credentials
22
+ * - 'clickup': ClickUp personal-token credentials with provider-specific schema/verification
21
23
  *
22
24
  * Note: Provider-specific identifiers (notion, google-sheets) are CREDENTIAL_SCHEMAS
23
25
  * keys used for UI lookup, NOT stored type values. OAuth credentials store type='oauth'.
24
26
  */
25
- export const CredentialTypeSchema = z.enum(['oauth', 'api-key', 'webhook-secret', 'api-key-secret'])
27
+ export const CredentialTypeSchema = z.enum(['oauth', 'api-key', 'webhook-secret', 'api-key-secret', 'clickup'])
26
28
 
27
29
  /**
28
30
  * Credential value validation
@@ -114,6 +116,21 @@ export const DeleteCredentialParamsSchema = z.object({
114
116
  credentialId: UuidSchema
115
117
  })
116
118
 
119
+ /**
120
+ * POST /api/credentials/:credentialId/verify - Verify credential with provider smoke check
121
+ */
122
+ export const VerifyCredentialParamsSchema = z.object({
123
+ credentialId: UuidSchema
124
+ })
125
+
126
+ export const VerifyCredentialResponseSchema = z.object({
127
+ ok: z.boolean(),
128
+ provider: z.string(),
129
+ checkedAt: z.string().datetime(),
130
+ message: z.string().optional(),
131
+ details: z.record(z.string(), z.unknown()).optional()
132
+ })
133
+
117
134
  /**
118
135
  * Export all schemas for use in routes
119
136
  */
@@ -123,5 +140,7 @@ export const CredentialSchemas = {
123
140
  ListResponse: ListCredentialsResponseSchema,
124
141
  UpdateParams: UpdateCredentialParamsSchema,
125
142
  UpdateRequest: UpdateCredentialRequestSchema,
126
- DeleteParams: DeleteCredentialParamsSchema
143
+ DeleteParams: DeleteCredentialParamsSchema,
144
+ VerifyParams: VerifyCredentialParamsSchema,
145
+ VerifyResponse: VerifyCredentialResponseSchema
127
146
  }