@a5c-ai/krate 5.0.1-staging.04a3db697

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 (246) hide show
  1. package/Dockerfile +31 -0
  2. package/README.md +183 -0
  3. package/bin/krate-demo.mjs +23 -0
  4. package/bin/krate-server.mjs +14 -0
  5. package/dist/krate-controller-ui.json +3067 -0
  6. package/dist/krate-lifecycle.json +201 -0
  7. package/dist/krate-runtime-snapshot.json +2955 -0
  8. package/dist/krate-summary.json +722 -0
  9. package/docs/README.md +61 -0
  10. package/docs/agents/README.md +83 -0
  11. package/docs/agents/acceptance-test-matrix.md +193 -0
  12. package/docs/agents/agent-mux-adapter-contract.md +167 -0
  13. package/docs/agents/agent-mux-source-map.md +310 -0
  14. package/docs/agents/agent-run-memory-import-spec.md +256 -0
  15. package/docs/agents/agent-stack-management-spec.md +421 -0
  16. package/docs/agents/api-contract-spec.md +309 -0
  17. package/docs/agents/artifacts-writeback-spec.md +145 -0
  18. package/docs/agents/chart-packaging-spec.md +128 -0
  19. package/docs/agents/ci-orchestration-spec.md +140 -0
  20. package/docs/agents/context-assembly-spec.md +219 -0
  21. package/docs/agents/controller-reconciliation-spec.md +255 -0
  22. package/docs/agents/crd-schema-spec.md +315 -0
  23. package/docs/agents/decision-log-open-questions.md +169 -0
  24. package/docs/agents/developer-implementation-checklist.md +329 -0
  25. package/docs/agents/dispatching-design.md +262 -0
  26. package/docs/agents/gaps-agent-mux-to-krate-crds.md +298 -0
  27. package/docs/agents/glossary.md +66 -0
  28. package/docs/agents/implementation-blueprint.md +324 -0
  29. package/docs/agents/implementation-rollout-slices.md +251 -0
  30. package/docs/agents/memory-context-integration-spec.md +194 -0
  31. package/docs/agents/memory-ontology-schema-spec.md +253 -0
  32. package/docs/agents/memory-operations-runbook.md +121 -0
  33. package/docs/agents/mvp-vertical-slice-spec.md +146 -0
  34. package/docs/agents/observability-audit-spec.md +265 -0
  35. package/docs/agents/operator-runbook.md +174 -0
  36. package/docs/agents/org-memory-api-payload-examples.md +333 -0
  37. package/docs/agents/org-memory-controller-sequence-spec.md +181 -0
  38. package/docs/agents/org-memory-e2e-fixture-plan.md +161 -0
  39. package/docs/agents/org-memory-ui-implementation-map.md +114 -0
  40. package/docs/agents/org-memory-vertical-slice-spec.md +168 -0
  41. package/docs/agents/org-resource-model-delta-spec.md +111 -0
  42. package/docs/agents/org-route-resource-model-spec.md +183 -0
  43. package/docs/agents/org-scoping-namespace-spec.md +114 -0
  44. package/docs/agents/rbac-secrets-management-spec.md +406 -0
  45. package/docs/agents/repository-page-integration-spec.md +255 -0
  46. package/docs/agents/resource-contract-examples.md +808 -0
  47. package/docs/agents/resource-relationship-map.md +190 -0
  48. package/docs/agents/security-threat-model.md +188 -0
  49. package/docs/agents/shared-memory-company-brain-spec.md +358 -0
  50. package/docs/agents/storage-migration-spec.md +168 -0
  51. package/docs/agents/subagent-orchestration-spec.md +152 -0
  52. package/docs/agents/system-overview.md +88 -0
  53. package/docs/agents/tools-mcp-skills-spec.md +189 -0
  54. package/docs/agents/traceability-matrix.md +79 -0
  55. package/docs/agents/ui-flow-spec.md +211 -0
  56. package/docs/agents/ui-ux-system-spec.md +426 -0
  57. package/docs/agents/workspace-lifecycle-spec.md +166 -0
  58. package/docs/architecture-spec.md +78 -0
  59. package/docs/components/control-plane.md +78 -0
  60. package/docs/components/data-plane.md +69 -0
  61. package/docs/components/hooks-events.md +67 -0
  62. package/docs/components/identity-rbac-policy.md +73 -0
  63. package/docs/components/kubevela-oam.md +70 -0
  64. package/docs/components/operations-publishing.md +81 -0
  65. package/docs/components/runners-ci.md +66 -0
  66. package/docs/components/web-ui.md +94 -0
  67. package/docs/external/README.md +47 -0
  68. package/docs/external/bidirectional-sync-design.md +134 -0
  69. package/docs/external/cicd-interface.md +64 -0
  70. package/docs/external/external-backend-controllers.md +170 -0
  71. package/docs/external/external-backend-crds.md +234 -0
  72. package/docs/external/external-backend-ui-spec.md +151 -0
  73. package/docs/external/external-backend-ux-flows.md +115 -0
  74. package/docs/external/external-object-mapping.md +125 -0
  75. package/docs/external/git-forge-interface.md +68 -0
  76. package/docs/external/github-integration-design.md +151 -0
  77. package/docs/external/issue-tracking-interface.md +66 -0
  78. package/docs/external/provider-capability-manifests.md +204 -0
  79. package/docs/external/provider-catalog.md +139 -0
  80. package/docs/external/provider-rollout-testing.md +78 -0
  81. package/docs/external/research-results.md +48 -0
  82. package/docs/external/security-auth-permissions.md +81 -0
  83. package/docs/external/sync-state-machines.md +108 -0
  84. package/docs/external/unified-external-backend-model.md +107 -0
  85. package/docs/external/user-facing-changes.md +67 -0
  86. package/docs/gaps.md +161 -0
  87. package/docs/install.md +94 -0
  88. package/docs/krate-design.md +334 -0
  89. package/docs/local-minikube.md +55 -0
  90. package/docs/ontology/README.md +32 -0
  91. package/docs/ontology/bounded-contexts.md +29 -0
  92. package/docs/ontology/events-and-hooks.md +32 -0
  93. package/docs/ontology/oam-kubevela.md +32 -0
  94. package/docs/ontology/operations-and-release.md +25 -0
  95. package/docs/ontology/personas-and-actors.md +32 -0
  96. package/docs/ontology/policies-and-invariants.md +33 -0
  97. package/docs/ontology/problem-space.md +30 -0
  98. package/docs/ontology/resource-contracts.md +40 -0
  99. package/docs/ontology/resource-taxonomy.md +42 -0
  100. package/docs/ontology/runners-and-ci.md +29 -0
  101. package/docs/ontology/solution-space.md +24 -0
  102. package/docs/ontology/storage-and-data-boundaries.md +29 -0
  103. package/docs/ontology/validation-matrix.md +24 -0
  104. package/docs/ontology/web-ui-excellent-flows.md +32 -0
  105. package/docs/ontology/workflows.md +39 -0
  106. package/docs/ontology/world.md +35 -0
  107. package/docs/product-requirements.md +62 -0
  108. package/docs/roadmap-mvp.md +87 -0
  109. package/docs/system-requirements.md +90 -0
  110. package/docs/tests/README.md +53 -0
  111. package/docs/tests/agent-qa-plan.md +63 -0
  112. package/docs/tests/browser-ui-tests.md +62 -0
  113. package/docs/tests/ci-quality-gates.md +48 -0
  114. package/docs/tests/coverage-model.md +64 -0
  115. package/docs/tests/e2e-scenario-tests.md +53 -0
  116. package/docs/tests/fixtures-test-data.md +63 -0
  117. package/docs/tests/observability-reliability-tests.md +54 -0
  118. package/docs/tests/product-test-matrix.md +145 -0
  119. package/docs/tests/qa-adoption-roadmap.md +130 -0
  120. package/docs/tests/qa-automation-plan.md +101 -0
  121. package/docs/tests/security-compliance-tests.md +57 -0
  122. package/docs/tests/test-framework-tools.md +88 -0
  123. package/docs/tests/test-suite-layout.md +121 -0
  124. package/docs/tests/unit-integration-tests.md +48 -0
  125. package/docs/todo-kyverno +714 -0
  126. package/docs/todos.md +4 -0
  127. package/docs/user-stories.md +78 -0
  128. package/examples/minikube-demo.yaml +190 -0
  129. package/examples/oam-application.yaml +23 -0
  130. package/examples/policy-kyverno-pr-title.yaml +18 -0
  131. package/package.json +63 -0
  132. package/scripts/build.mjs +29 -0
  133. package/scripts/setup-minikube.mjs +65 -0
  134. package/scripts/smoke.mjs +37 -0
  135. package/scripts/validate-doc-coverage.mjs +152 -0
  136. package/scripts/validate-package.mjs +93 -0
  137. package/scripts/validate-ui.mjs +236 -0
  138. package/src/agent-adapter-controller.js +169 -0
  139. package/src/agent-approval-controller.js +170 -0
  140. package/src/agent-context-bundles.js +242 -0
  141. package/src/agent-dispatch-controller.js +209 -0
  142. package/src/agent-gateway-config-controller.js +147 -0
  143. package/src/agent-memory-controller.js +357 -0
  144. package/src/agent-memory-import.js +327 -0
  145. package/src/agent-memory-query.js +292 -0
  146. package/src/agent-memory-repository-source-controller.js +255 -0
  147. package/src/agent-mux-client.js +280 -0
  148. package/src/agent-permission-review.js +250 -0
  149. package/src/agent-project-controller.js +117 -0
  150. package/src/agent-provider-config-controller.js +150 -0
  151. package/src/agent-secret-config-grant-controller.js +282 -0
  152. package/src/agent-session-transcript-controller.js +189 -0
  153. package/src/agent-stack-controller.js +347 -0
  154. package/src/agent-subagent-controller.js +160 -0
  155. package/src/agent-transport-binding-controller.js +121 -0
  156. package/src/agent-trigger-controller.js +321 -0
  157. package/src/agent-workspace-controller.js +447 -0
  158. package/src/agent-writeback-controller.js +302 -0
  159. package/src/api-controller.js +541 -0
  160. package/src/argocd-gitops.js +43 -0
  161. package/src/async-controller.js +207 -0
  162. package/src/audit-controller.js +191 -0
  163. package/src/auth.js +307 -0
  164. package/src/component-catalog.js +41 -0
  165. package/src/control-plane.js +136 -0
  166. package/src/controller-client.js +50 -0
  167. package/src/controller-ui.js +551 -0
  168. package/src/data-plane.js +178 -0
  169. package/src/event-bus.js +61 -0
  170. package/src/external/conflict-controller.js +225 -0
  171. package/src/external/github/auth.js +96 -0
  172. package/src/external/github/cicd.js +180 -0
  173. package/src/external/github/git-forge.js +240 -0
  174. package/src/external/github/index.js +144 -0
  175. package/src/external/github/issue-tracking.js +163 -0
  176. package/src/external/provider-adapter.js +161 -0
  177. package/src/external/provider-resource-factory.js +161 -0
  178. package/src/external/sync-controller.js +235 -0
  179. package/src/external/webhook-controller.js +144 -0
  180. package/src/external/write-controller.js +283 -0
  181. package/src/gitea-backend.js +95 -0
  182. package/src/gitea-service.js +173 -0
  183. package/src/handoff.js +98 -0
  184. package/src/hooks-events.js +63 -0
  185. package/src/http-server.js +377 -0
  186. package/src/identity-policy.js +86 -0
  187. package/src/index.js +55 -0
  188. package/src/kubernetes-controller-async.js +511 -0
  189. package/src/kubernetes-controller.js +878 -0
  190. package/src/kubernetes-resource-gateway.js +48 -0
  191. package/src/operations.js +112 -0
  192. package/src/org-scoping.js +5 -0
  193. package/src/resource-model.js +221 -0
  194. package/src/runners-ci.js +48 -0
  195. package/src/runtime.js +196 -0
  196. package/src/snapshot-cache.js +157 -0
  197. package/src/web-ui.js +40 -0
  198. package/tests/agent-adapter-controller.test.js +361 -0
  199. package/tests/agent-approval-controller.test.js +173 -0
  200. package/tests/agent-context-bundles.test.js +278 -0
  201. package/tests/agent-dispatch-controller.test.js +315 -0
  202. package/tests/agent-gateway-config-controller.test.js +386 -0
  203. package/tests/agent-memory-controller.test.js +308 -0
  204. package/tests/agent-memory-import-snapshot.test.js +477 -0
  205. package/tests/agent-memory-query.test.js +404 -0
  206. package/tests/agent-memory-repository-source.test.js +514 -0
  207. package/tests/agent-mux-client.test.js +204 -0
  208. package/tests/agent-permission-review-v2.test.js +317 -0
  209. package/tests/agent-permission-review.test.js +209 -0
  210. package/tests/agent-project-controller.test.js +302 -0
  211. package/tests/agent-provider-config-controller.test.js +376 -0
  212. package/tests/agent-resources.test.js +228 -0
  213. package/tests/agent-secret-config-grant.test.js +231 -0
  214. package/tests/agent-session-transcript-controller.test.js +499 -0
  215. package/tests/agent-stack-controller.test.js +221 -0
  216. package/tests/agent-subagent-controller.test.js +201 -0
  217. package/tests/agent-transport-binding-controller.test.js +294 -0
  218. package/tests/agent-trigger-controller.test.js +211 -0
  219. package/tests/agent-trigger-routes.test.js +190 -0
  220. package/tests/agent-trigger-sources.test.js +245 -0
  221. package/tests/agent-workspace-controller.test.js +181 -0
  222. package/tests/agent-writeback.test.js +292 -0
  223. package/tests/approval-persistence.test.js +171 -0
  224. package/tests/async-controller.test.js +252 -0
  225. package/tests/audit-controller.test.js +227 -0
  226. package/tests/deployment.test.js +396 -0
  227. package/tests/e2e/lifecycle.test.js +117 -0
  228. package/tests/external-github-forge.test.js +560 -0
  229. package/tests/external-github-issues-cicd.test.js +520 -0
  230. package/tests/external-integration.test.js +470 -0
  231. package/tests/external-persistence.test.js +340 -0
  232. package/tests/external-provider-adapter.test.js +365 -0
  233. package/tests/external-resource-model.test.js +215 -0
  234. package/tests/external-webhook-sync.test.js +287 -0
  235. package/tests/external-write-conflict.test.js +353 -0
  236. package/tests/gitea-service.test.js +253 -0
  237. package/tests/health-check-real.test.js +165 -0
  238. package/tests/integration/full-flow.test.js +266 -0
  239. package/tests/krate.test.js +727 -0
  240. package/tests/memory-search-wiring.test.js +270 -0
  241. package/tests/org-scoping.test.js +687 -0
  242. package/tests/session-cookie-hmac.test.js +151 -0
  243. package/tests/snapshot-performance.test.js +247 -0
  244. package/tests/sse-events.test.js +107 -0
  245. package/tests/workspace-volumes.test.js +312 -0
  246. package/tests/writeback-persistence.test.js +207 -0
@@ -0,0 +1,163 @@
1
+ // GitHub Issue Tracking implementation — Slice 3.3b
2
+ // Implements the issueTracking interface contract:
3
+ // listIssues, createIssue, updateIssue, closeIssue, listComments, createComment
4
+ //
5
+ // All HTTP calls are injected via fetchImpl for full testability.
6
+
7
+ const GITHUB_API = 'https://api.github.com';
8
+
9
+ /**
10
+ * @typedef {{ number: number, title: string, state: string, body: string, labels: string[], author: string, htmlUrl: string }} NormalizedIssue
11
+ * @typedef {{ id: number, body: string, author: string, createdAt: string, htmlUrl: string }} NormalizedComment
12
+ */
13
+
14
+ /**
15
+ * GitHub implementation of the issueTracking interface.
16
+ *
17
+ * @param {{ owner: string, installationToken: string, fetchImpl?: Function }} opts
18
+ */
19
+ export class GitHubIssueTracking {
20
+ constructor({ owner, installationToken, fetchImpl = globalThis.fetch } = {}) {
21
+ if (!owner) throw new Error('GitHubIssueTracking: owner (org or user) is required');
22
+ if (!installationToken) throw new Error('GitHubIssueTracking: installationToken is required');
23
+ if (!fetchImpl) throw new Error('GitHubIssueTracking: a fetch implementation is required');
24
+
25
+ this.role = 'github-issue-tracking';
26
+ this._owner = owner;
27
+ this._token = installationToken;
28
+ this._fetch = fetchImpl;
29
+ }
30
+
31
+ // ---------------------------------------------------------------------------
32
+ // Internal helpers
33
+ // ---------------------------------------------------------------------------
34
+
35
+ _headers() {
36
+ return {
37
+ Accept: 'application/vnd.github+json',
38
+ Authorization: `Bearer ${this._token}`,
39
+ 'X-GitHub-Api-Version': '2022-11-28',
40
+ 'Content-Type': 'application/json'
41
+ };
42
+ }
43
+
44
+ async _request(method, path, body) {
45
+ const url = `${GITHUB_API}${path}`;
46
+ const options = {
47
+ method,
48
+ headers: this._headers(),
49
+ ...(body !== undefined ? { body: JSON.stringify(body) } : {})
50
+ };
51
+ const response = await this._fetch(url, options);
52
+ if (!response.ok) {
53
+ throw new Error(`GitHub ${method} ${path} failed with status ${response.status}`);
54
+ }
55
+ if (response.status === 204) return null;
56
+ return response.json();
57
+ }
58
+
59
+ _repoPath(repo, suffix = '') {
60
+ return `/repos/${encodeURIComponent(this._owner)}/${encodeURIComponent(repo)}${suffix}`;
61
+ }
62
+
63
+ // ---------------------------------------------------------------------------
64
+ // Normalizers
65
+ // ---------------------------------------------------------------------------
66
+
67
+ _normalizeIssue(data) {
68
+ return {
69
+ id: data.id,
70
+ number: data.number,
71
+ title: data.title,
72
+ state: data.state,
73
+ body: data.body ?? '',
74
+ labels: (data.labels ?? []).map(l => (typeof l === 'string' ? l : l.name)),
75
+ author: data.user?.login ?? '',
76
+ htmlUrl: data.html_url ?? ''
77
+ };
78
+ }
79
+
80
+ _normalizeComment(data) {
81
+ return {
82
+ id: data.id,
83
+ body: data.body ?? '',
84
+ author: data.user?.login ?? '',
85
+ createdAt: data.created_at ?? '',
86
+ htmlUrl: data.html_url ?? ''
87
+ };
88
+ }
89
+
90
+ // ---------------------------------------------------------------------------
91
+ // Interface methods
92
+ // ---------------------------------------------------------------------------
93
+
94
+ /**
95
+ * List issues for a repository.
96
+ * @param {{ repo: string, state?: 'open'|'closed'|'all' }} opts
97
+ * @returns {Promise<NormalizedIssue[]>}
98
+ */
99
+ async listIssues({ repo, state = 'open' } = {}) {
100
+ const data = await this._request('GET', this._repoPath(repo, `/issues?state=${state}`));
101
+ const issues = data ?? [];
102
+ return issues.map(i => this._normalizeIssue(i));
103
+ }
104
+
105
+ /**
106
+ * Create an issue.
107
+ * @param {{ repo: string, title: string, body?: string, labels?: string[] }} opts
108
+ * @returns {Promise<NormalizedIssue>}
109
+ */
110
+ async createIssue({ repo, title, body = '', labels = [] }) {
111
+ const data = await this._request('POST', this._repoPath(repo, '/issues'), {
112
+ title,
113
+ body,
114
+ ...(labels.length > 0 ? { labels } : {})
115
+ });
116
+ return this._normalizeIssue(data);
117
+ }
118
+
119
+ /**
120
+ * Update an issue (title, body, labels).
121
+ * @param {{ repo: string, issueNumber: number, title?: string, body?: string, labels?: string[] }} opts
122
+ * @returns {Promise<NormalizedIssue>}
123
+ */
124
+ async updateIssue({ repo, issueNumber, title, body, labels } = {}) {
125
+ const payload = {};
126
+ if (title !== undefined) payload.title = title;
127
+ if (body !== undefined) payload.body = body;
128
+ if (labels !== undefined) payload.labels = labels;
129
+ const data = await this._request('PATCH', this._repoPath(repo, `/issues/${issueNumber}`), payload);
130
+ return this._normalizeIssue(data);
131
+ }
132
+
133
+ /**
134
+ * Close an issue by setting its state to 'closed'.
135
+ * @param {{ repo: string, issueNumber: number }} opts
136
+ * @returns {Promise<NormalizedIssue>}
137
+ */
138
+ async closeIssue({ repo, issueNumber } = {}) {
139
+ const data = await this._request('PATCH', this._repoPath(repo, `/issues/${issueNumber}`), { state: 'closed' });
140
+ return this._normalizeIssue(data);
141
+ }
142
+
143
+ /**
144
+ * List comments for an issue.
145
+ * @param {{ repo: string, issueNumber: number }} opts
146
+ * @returns {Promise<NormalizedComment[]>}
147
+ */
148
+ async listComments({ repo, issueNumber } = {}) {
149
+ const data = await this._request('GET', this._repoPath(repo, `/issues/${issueNumber}/comments`));
150
+ const comments = data ?? [];
151
+ return comments.map(c => this._normalizeComment(c));
152
+ }
153
+
154
+ /**
155
+ * Create a comment on an issue.
156
+ * @param {{ repo: string, issueNumber: number, body: string }} opts
157
+ * @returns {Promise<NormalizedComment>}
158
+ */
159
+ async createComment({ repo, issueNumber, body }) {
160
+ const data = await this._request('POST', this._repoPath(repo, `/issues/${issueNumber}/comments`), { body });
161
+ return this._normalizeComment(data);
162
+ }
163
+ }
@@ -0,0 +1,161 @@
1
+ // External Provider Adapter — Slice 3.2
2
+ // Defines the ExternalProviderAdapter contract and ProviderRegistry.
3
+ //
4
+ // Each provider (GitHub, GitLab, Gitea, etc.) implements this interface to
5
+ // integrate with the Krate platform.
6
+
7
+ /**
8
+ * Valid optional interface keys that a provider adapter may implement.
9
+ */
10
+ const VALID_INTERFACES = ['issueTracking', 'cicd', 'gitForge'];
11
+
12
+ /**
13
+ * Valid health status values returned by health().
14
+ */
15
+ const VALID_HEALTH_STATUSES = ['healthy', 'degraded', 'unavailable'];
16
+
17
+ // ---------------------------------------------------------------------------
18
+ // Provider Registry
19
+ // ---------------------------------------------------------------------------
20
+
21
+ /**
22
+ * Create a provider registry that stores and retrieves ExternalProviderAdapter
23
+ * instances by provider type string.
24
+ *
25
+ * @returns {{ register(type: string, adapter: object): void, get(type: string): object|null, list(): string[] }}
26
+ */
27
+ export function createProviderRegistry() {
28
+ /** @type {Map<string, object>} */
29
+ const adapters = new Map();
30
+
31
+ return {
32
+ /**
33
+ * Register a provider adapter under the given type key.
34
+ * Re-registering the same type replaces the existing adapter.
35
+ * @param {string} type
36
+ * @param {object} adapter
37
+ */
38
+ register(type, adapter) {
39
+ adapters.set(type, adapter);
40
+ },
41
+
42
+ /**
43
+ * Retrieve a registered adapter by provider type, or null if not found.
44
+ * @param {string} type
45
+ * @returns {object|null}
46
+ */
47
+ get(type) {
48
+ return adapters.has(type) ? adapters.get(type) : null;
49
+ },
50
+
51
+ /**
52
+ * List all registered provider type keys.
53
+ * @returns {string[]}
54
+ */
55
+ list() {
56
+ return [...adapters.keys()];
57
+ }
58
+ };
59
+ }
60
+
61
+ // ---------------------------------------------------------------------------
62
+ // Adapter validation
63
+ // ---------------------------------------------------------------------------
64
+
65
+ /**
66
+ * Validate an ExternalProviderAdapter object against the contract.
67
+ *
68
+ * Required contract:
69
+ * - descriptor() → { providerType, displayName, hosting, authModes, apiCapabilities }
70
+ * - health() → { status: 'healthy'|'degraded'|'unavailable', message }
71
+ * - at least one of: issueTracking, cicd, gitForge
72
+ * - normalizeWebhook(payload) → NormalizedEvent[]
73
+ * - verifyWebhook(request) → { valid, reason }
74
+ *
75
+ * @param {object} adapter
76
+ * @returns {{ valid: boolean, errors: string[] }}
77
+ */
78
+ export function validateProviderAdapter(adapter) {
79
+ const errors = [];
80
+
81
+ if (adapter == null) {
82
+ errors.push('adapter must not be null or undefined');
83
+ return { valid: false, errors };
84
+ }
85
+
86
+ // descriptor must be a function
87
+ if (typeof adapter.descriptor !== 'function') {
88
+ errors.push('adapter.descriptor must be a function returning { providerType, displayName, hosting, authModes }');
89
+ }
90
+
91
+ // health must be a function
92
+ if (typeof adapter.health !== 'function') {
93
+ errors.push('adapter.health must be a function returning { status, message }');
94
+ }
95
+
96
+ // At least one optional interface must be present
97
+ const presentInterfaces = VALID_INTERFACES.filter((key) => adapter[key] != null);
98
+ if (presentInterfaces.length === 0) {
99
+ errors.push(
100
+ `adapter must implement at least one provider interface: ${VALID_INTERFACES.join(', ')} (issueTracking, cicd, or gitForge)`
101
+ );
102
+ }
103
+
104
+ // normalizeWebhook must be a function
105
+ if (typeof adapter.normalizeWebhook !== 'function') {
106
+ errors.push('adapter.normalizeWebhook must be a function(payload) → NormalizedEvent[]');
107
+ }
108
+
109
+ // verifyWebhook must be a function
110
+ if (typeof adapter.verifyWebhook !== 'function') {
111
+ errors.push('adapter.verifyWebhook must be a function(request) → { valid, reason }');
112
+ }
113
+
114
+ return { valid: errors.length === 0, errors };
115
+ }
116
+
117
+ // ---------------------------------------------------------------------------
118
+ // Capability manifest validation
119
+ // ---------------------------------------------------------------------------
120
+
121
+ /**
122
+ * Validate a capability manifest object.
123
+ *
124
+ * A capability manifest declares which provider interfaces a concrete adapter
125
+ * supports, so the platform can route requests accordingly.
126
+ *
127
+ * Required shape:
128
+ * { providerType: string, interfaces: string[] }
129
+ *
130
+ * @param {object} manifest
131
+ * @returns {{ valid: boolean, errors: string[] }}
132
+ */
133
+ export function validateCapabilityManifest(manifest) {
134
+ const errors = [];
135
+
136
+ if (manifest == null) {
137
+ errors.push('manifest must not be null or undefined');
138
+ return { valid: false, errors };
139
+ }
140
+
141
+ // providerType is required
142
+ if (!manifest.providerType || typeof manifest.providerType !== 'string') {
143
+ errors.push('manifest.providerType is required and must be a non-empty string');
144
+ }
145
+
146
+ // interfaces must be a non-empty array of known interface names
147
+ if (!Array.isArray(manifest.interfaces) || manifest.interfaces.length === 0) {
148
+ errors.push(
149
+ `manifest.interfaces must be a non-empty array; supported interfaces are: ${VALID_INTERFACES.join(', ')}`
150
+ );
151
+ } else {
152
+ const unknown = manifest.interfaces.filter((iface) => !VALID_INTERFACES.includes(iface));
153
+ if (unknown.length > 0) {
154
+ errors.push(
155
+ `manifest.interfaces contains unknown or invalid interface(s): ${unknown.join(', ')}; valid interfaces are: ${VALID_INTERFACES.join(', ')}`
156
+ );
157
+ }
158
+ }
159
+
160
+ return { valid: errors.length === 0, errors };
161
+ }
@@ -0,0 +1,161 @@
1
+ // External Provider Resource Factory — Slice B3+D1
2
+ //
3
+ // Provides:
4
+ // - createDefaultProviderRegistry() — auto-registers GitHub provider
5
+ // - createExternalBackendProvider() — creates ExternalBackendProvider CRD resources
6
+
7
+ import { createProviderRegistry } from './provider-adapter.js';
8
+ import { GitHubGitForge } from './github/git-forge.js';
9
+ import { GitHubIssueTracking } from './github/issue-tracking.js';
10
+ import { GitHubCicd } from './github/cicd.js';
11
+
12
+ // Re-export for consumers that want the real classes from this module
13
+ export { GitHubGitForge, GitHubIssueTracking, GitHubCicd };
14
+
15
+ // ---------------------------------------------------------------------------
16
+ // GitHub provider descriptor — wraps real GitHub classes
17
+ // ---------------------------------------------------------------------------
18
+
19
+ /**
20
+ * Build a GitHub provider adapter descriptor for the registry.
21
+ * This descriptor exposes real GitHubGitForge, GitHubIssueTracking, and
22
+ * GitHubCicd classes as factory methods. Credential-free listing operations
23
+ * return empty arrays; authenticated operations are created via createGitHubProvider().
24
+ *
25
+ * @returns {object} ExternalProviderAdapter descriptor
26
+ */
27
+ function buildGitHubAdapterDescriptor() {
28
+ return {
29
+ descriptor() {
30
+ return {
31
+ providerType: 'github',
32
+ displayName: 'GitHub',
33
+ hosting: ['cloud', 'self-hosted'],
34
+ authModes: ['github-app', 'pat'],
35
+ interfaces: ['gitForge', 'issueTracking', 'cicd']
36
+ };
37
+ },
38
+
39
+ health() {
40
+ return { status: 'healthy', message: 'GitHub provider registered (no live connection)' };
41
+ },
42
+
43
+ /**
44
+ * Create a GitHubGitForge instance bound to specific credentials.
45
+ *
46
+ * @param {{ owner: string, installationToken: string, fetchImpl?: Function }} opts
47
+ * @returns {GitHubGitForge}
48
+ */
49
+ createForge({ owner, installationToken, fetchImpl } = {}) {
50
+ return new GitHubGitForge({ owner, installationToken, fetchImpl: fetchImpl ?? globalThis.fetch });
51
+ },
52
+
53
+ /**
54
+ * Create a GitHubIssueTracking instance bound to specific credentials.
55
+ *
56
+ * @param {{ owner: string, installationToken: string, fetchImpl?: Function }} opts
57
+ * @returns {GitHubIssueTracking}
58
+ */
59
+ createIssueTracker({ owner, installationToken, fetchImpl } = {}) {
60
+ return new GitHubIssueTracking({ owner, installationToken, fetchImpl: fetchImpl ?? globalThis.fetch });
61
+ },
62
+
63
+ /**
64
+ * Create a GitHubCicd instance bound to specific credentials.
65
+ *
66
+ * @param {{ owner: string, installationToken: string, fetchImpl?: Function }} opts
67
+ * @returns {GitHubCicd}
68
+ */
69
+ createCicd({ owner, installationToken, fetchImpl } = {}) {
70
+ return new GitHubCicd({ owner, installationToken, fetchImpl: fetchImpl ?? globalThis.fetch });
71
+ },
72
+
73
+ // Credential-free listing stubs — real operations require authenticated instances
74
+ gitForge: {
75
+ listRepositories: async () => [],
76
+ createRepository: async () => { throw new Error('Use createForge() for authenticated git forge operations'); }
77
+ },
78
+
79
+ issueTracking: {
80
+ listIssues: async () => [],
81
+ createIssue: async () => { throw new Error('Use createIssueTracker() for authenticated issue tracking operations'); }
82
+ },
83
+
84
+ cicd: {
85
+ listWorkflowRuns: async () => [],
86
+ triggerWorkflow: async () => { throw new Error('Use createCicd() for authenticated CI/CD operations'); }
87
+ },
88
+
89
+ normalizeWebhook(payload) {
90
+ // Passthrough — real normalization lives in the GitHub webhook controller
91
+ return [{ type: payload?.action ?? 'unknown', payload }];
92
+ },
93
+
94
+ verifyWebhook(_request) {
95
+ // Default: unverified; real HMAC verification is in webhook-controller
96
+ return { valid: false, reason: 'use webhook-controller for HMAC verification' };
97
+ }
98
+ };
99
+ }
100
+
101
+ // ---------------------------------------------------------------------------
102
+ // D1: createDefaultProviderRegistry
103
+ // ---------------------------------------------------------------------------
104
+
105
+ /**
106
+ * Create a provider registry pre-loaded with the GitHub provider.
107
+ *
108
+ * @returns {object} ProviderRegistry with github auto-registered
109
+ */
110
+ export function createDefaultProviderRegistry() {
111
+ const registry = createProviderRegistry();
112
+ registry.register('github', buildGitHubAdapterDescriptor());
113
+ return registry;
114
+ }
115
+
116
+ // ---------------------------------------------------------------------------
117
+ // D1: createExternalBackendProvider
118
+ // ---------------------------------------------------------------------------
119
+
120
+ /**
121
+ * Create an ExternalBackendProvider CRD resource.
122
+ * Used by the external backend wizard to persist provider registrations.
123
+ *
124
+ * @param {{ name: string, namespace?: string, providerType: string,
125
+ * displayName: string, config?: object, organizationRef?: string }} opts
126
+ * @returns {object} K8s-style ExternalBackendProvider resource
127
+ */
128
+ export function createExternalBackendProvider({
129
+ name,
130
+ namespace = 'default',
131
+ providerType,
132
+ displayName,
133
+ config = {},
134
+ organizationRef = 'default'
135
+ } = {}) {
136
+ if (!name) throw new Error('createExternalBackendProvider: name is required');
137
+ if (!providerType) throw new Error('createExternalBackendProvider: providerType is required');
138
+
139
+ const now = new Date().toISOString();
140
+
141
+ return {
142
+ apiVersion: 'krate.a5c.ai/v1alpha1',
143
+ kind: 'ExternalBackendProvider',
144
+ metadata: {
145
+ name,
146
+ namespace,
147
+ labels: {},
148
+ annotations: {}
149
+ },
150
+ spec: {
151
+ organizationRef,
152
+ providerType,
153
+ displayName: displayName || providerType,
154
+ config: { ...config }
155
+ },
156
+ status: {
157
+ phase: 'Pending',
158
+ createdAt: now
159
+ }
160
+ };
161
+ }