@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,180 @@
1
+ // GitHub CI/CD implementation — Slice 3.3b
2
+ // Implements the cicd interface contract:
3
+ // listWorkflowRuns, listJobs, rerunWorkflow, cancelWorkflow, createCheck, updateCheck
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 {{ id: number, name: string, status: string, conclusion: string|null, headBranch: string, headSha: string, htmlUrl: string, createdAt: string, updatedAt: string }} NormalizedWorkflowRun
11
+ * @typedef {{ id: number, name: string, status: string, conclusion: string|null, startedAt: string, completedAt: string, htmlUrl: string }} NormalizedJob
12
+ * @typedef {{ id: number, name: string, status: string, conclusion: string|null, htmlUrl: string }} NormalizedCheckRun
13
+ */
14
+
15
+ /**
16
+ * GitHub implementation of the cicd interface.
17
+ *
18
+ * @param {{ owner: string, installationToken: string, fetchImpl?: Function }} opts
19
+ */
20
+ export class GitHubCicd {
21
+ constructor({ owner, installationToken, fetchImpl = globalThis.fetch } = {}) {
22
+ if (!owner) throw new Error('GitHubCicd: owner (org or user) is required');
23
+ if (!installationToken) throw new Error('GitHubCicd: installationToken is required');
24
+ if (!fetchImpl) throw new Error('GitHubCicd: a fetch implementation is required');
25
+
26
+ this.role = 'github-cicd';
27
+ this._owner = owner;
28
+ this._token = installationToken;
29
+ this._fetch = fetchImpl;
30
+ }
31
+
32
+ // ---------------------------------------------------------------------------
33
+ // Internal helpers
34
+ // ---------------------------------------------------------------------------
35
+
36
+ _headers() {
37
+ return {
38
+ Accept: 'application/vnd.github+json',
39
+ Authorization: `Bearer ${this._token}`,
40
+ 'X-GitHub-Api-Version': '2022-11-28',
41
+ 'Content-Type': 'application/json'
42
+ };
43
+ }
44
+
45
+ async _request(method, path, body) {
46
+ const url = `${GITHUB_API}${path}`;
47
+ const options = {
48
+ method,
49
+ headers: this._headers(),
50
+ ...(body !== undefined ? { body: JSON.stringify(body) } : {})
51
+ };
52
+ const response = await this._fetch(url, options);
53
+ if (!response.ok) {
54
+ throw new Error(`GitHub ${method} ${path} failed with status ${response.status}`);
55
+ }
56
+ if (response.status === 204) return null;
57
+ return response.json();
58
+ }
59
+
60
+ _repoPath(repo, suffix = '') {
61
+ return `/repos/${encodeURIComponent(this._owner)}/${encodeURIComponent(repo)}${suffix}`;
62
+ }
63
+
64
+ // ---------------------------------------------------------------------------
65
+ // Normalizers
66
+ // ---------------------------------------------------------------------------
67
+
68
+ _normalizeRun(data) {
69
+ return {
70
+ id: data.id,
71
+ name: data.name ?? '',
72
+ status: data.status ?? '',
73
+ conclusion: data.conclusion ?? null,
74
+ headBranch: data.head_branch ?? '',
75
+ headSha: data.head_sha ?? '',
76
+ htmlUrl: data.html_url ?? '',
77
+ createdAt: data.created_at ?? '',
78
+ updatedAt: data.updated_at ?? ''
79
+ };
80
+ }
81
+
82
+ _normalizeJob(data) {
83
+ return {
84
+ id: data.id,
85
+ name: data.name ?? '',
86
+ status: data.status ?? '',
87
+ conclusion: data.conclusion ?? null,
88
+ startedAt: data.started_at ?? '',
89
+ completedAt: data.completed_at ?? '',
90
+ htmlUrl: data.html_url ?? ''
91
+ };
92
+ }
93
+
94
+ _normalizeCheckRun(data) {
95
+ return {
96
+ id: data.id,
97
+ name: data.name ?? '',
98
+ status: data.status ?? '',
99
+ conclusion: data.conclusion ?? null,
100
+ htmlUrl: data.html_url ?? ''
101
+ };
102
+ }
103
+
104
+ // ---------------------------------------------------------------------------
105
+ // Interface methods
106
+ // ---------------------------------------------------------------------------
107
+
108
+ /**
109
+ * List workflow runs for a repository (optionally filtered by workflow file name).
110
+ * @param {{ repo: string, workflowId?: string|number }} opts
111
+ * @returns {Promise<NormalizedWorkflowRun[]>}
112
+ */
113
+ async listWorkflowRuns({ repo, workflowId } = {}) {
114
+ const path = workflowId
115
+ ? this._repoPath(repo, `/actions/workflows/${encodeURIComponent(workflowId)}/runs`)
116
+ : this._repoPath(repo, '/actions/runs');
117
+ const data = await this._request('GET', path);
118
+ const runs = data?.workflow_runs ?? data ?? [];
119
+ return runs.map(r => this._normalizeRun(r));
120
+ }
121
+
122
+ /**
123
+ * List jobs for a specific workflow run.
124
+ * @param {{ repo: string, runId: number }} opts
125
+ * @returns {Promise<NormalizedJob[]>}
126
+ */
127
+ async listJobs({ repo, runId } = {}) {
128
+ const data = await this._request('GET', this._repoPath(repo, `/actions/runs/${runId}/jobs`));
129
+ const jobs = data?.jobs ?? data ?? [];
130
+ return jobs.map(j => this._normalizeJob(j));
131
+ }
132
+
133
+ /**
134
+ * Trigger a re-run of a workflow run.
135
+ * @param {{ repo: string, runId: number }} opts
136
+ * @returns {Promise<{ triggered: boolean, runId: number }>}
137
+ */
138
+ async rerunWorkflow({ repo, runId } = {}) {
139
+ await this._request('POST', this._repoPath(repo, `/actions/runs/${runId}/rerun`));
140
+ return { triggered: true, runId };
141
+ }
142
+
143
+ /**
144
+ * Cancel a workflow run.
145
+ * @param {{ repo: string, runId: number }} opts
146
+ * @returns {Promise<{ cancelled: boolean, runId: number }>}
147
+ */
148
+ async cancelWorkflow({ repo, runId } = {}) {
149
+ await this._request('POST', this._repoPath(repo, `/actions/runs/${runId}/cancel`));
150
+ return { cancelled: true, runId };
151
+ }
152
+
153
+ /**
154
+ * Create a check run on a commit.
155
+ * @param {{ repo: string, name: string, headSha: string, status?: string, conclusion?: string, detailsUrl?: string, output?: object }} opts
156
+ * @returns {Promise<NormalizedCheckRun>}
157
+ */
158
+ async createCheck({ repo, name, headSha, status = 'queued', conclusion, detailsUrl, output } = {}) {
159
+ const payload = { name, head_sha: headSha, status };
160
+ if (conclusion !== undefined) payload.conclusion = conclusion;
161
+ if (detailsUrl !== undefined) payload.details_url = detailsUrl;
162
+ if (output !== undefined) payload.output = output;
163
+ const data = await this._request('POST', this._repoPath(repo, '/check-runs'), payload);
164
+ return this._normalizeCheckRun(data);
165
+ }
166
+
167
+ /**
168
+ * Update an existing check run.
169
+ * @param {{ repo: string, checkRunId: number, status?: string, conclusion?: string, output?: object }} opts
170
+ * @returns {Promise<NormalizedCheckRun>}
171
+ */
172
+ async updateCheck({ repo, checkRunId, status, conclusion, output } = {}) {
173
+ const payload = {};
174
+ if (status !== undefined) payload.status = status;
175
+ if (conclusion !== undefined) payload.conclusion = conclusion;
176
+ if (output !== undefined) payload.output = output;
177
+ const data = await this._request('PATCH', this._repoPath(repo, `/check-runs/${checkRunId}`), payload);
178
+ return this._normalizeCheckRun(data);
179
+ }
180
+ }
@@ -0,0 +1,240 @@
1
+ // GitHub Git Forge implementation — Slice 3.3a
2
+ // Implements the gitForge interface contract:
3
+ // listRepositories, getPullRequest, createPullRequest, mergePullRequest,
4
+ // listRefs, syncDeployKeys, syncBranchProtection
5
+ //
6
+ // All HTTP calls are injected via fetchImpl for full testability.
7
+
8
+ const GITHUB_API = 'https://api.github.com';
9
+ const VALID_MERGE_METHODS = new Set(['merge', 'squash', 'rebase']);
10
+
11
+ /**
12
+ * @typedef {{ name: string, fullName: string, private: boolean, defaultBranch: string, cloneUrl: string }} NormalizedRepo
13
+ * @typedef {{ number: number, title: string, state: string, head: object, base: object, body: string, merged: boolean, htmlUrl: string }} NormalizedPR
14
+ * @typedef {{ name: string, sha: string, protected: boolean }} NormalizedBranch
15
+ * @typedef {{ name: string, sha: string }} NormalizedTag
16
+ */
17
+
18
+ /**
19
+ * GitHub implementation of the git forge interface.
20
+ *
21
+ * @param {{ owner: string, installationToken: string, fetchImpl?: Function }} opts
22
+ */
23
+ export class GitHubGitForge {
24
+ constructor({ owner, installationToken, fetchImpl = globalThis.fetch } = {}) {
25
+ if (!owner) throw new Error('GitHubGitForge: owner (org or user) is required');
26
+ if (!installationToken) throw new Error('GitHubGitForge: installationToken is required');
27
+ if (!fetchImpl) throw new Error('GitHubGitForge: a fetch implementation is required');
28
+
29
+ this.role = 'github-git-forge';
30
+ this._owner = owner;
31
+ this._token = installationToken;
32
+ this._fetch = fetchImpl;
33
+ }
34
+
35
+ // ---------------------------------------------------------------------------
36
+ // Internal helpers
37
+ // ---------------------------------------------------------------------------
38
+
39
+ _headers() {
40
+ return {
41
+ Accept: 'application/vnd.github+json',
42
+ Authorization: `Bearer ${this._token}`,
43
+ 'X-GitHub-Api-Version': '2022-11-28',
44
+ 'Content-Type': 'application/json'
45
+ };
46
+ }
47
+
48
+ async _request(method, path, body) {
49
+ const url = `${GITHUB_API}${path}`;
50
+ const options = {
51
+ method,
52
+ headers: this._headers(),
53
+ ...(body !== undefined ? { body: JSON.stringify(body) } : {})
54
+ };
55
+ const response = await this._fetch(url, options);
56
+ if (!response.ok) {
57
+ throw new Error(`GitHub ${method} ${path} failed with status ${response.status}`);
58
+ }
59
+ if (response.status === 204) return null;
60
+ return response.json();
61
+ }
62
+
63
+ _repoPath(repo, suffix = '') {
64
+ return `/repos/${encodeURIComponent(this._owner)}/${encodeURIComponent(repo)}${suffix}`;
65
+ }
66
+
67
+ // ---------------------------------------------------------------------------
68
+ // Interface methods
69
+ // ---------------------------------------------------------------------------
70
+
71
+ /**
72
+ * List all repositories for the installation.
73
+ * @returns {Promise<NormalizedRepo[]>}
74
+ */
75
+ async listRepositories() {
76
+ const data = await this._request('GET', `/installation/repositories`);
77
+ const repos = data?.repositories ?? data ?? [];
78
+ return repos.map(r => ({
79
+ id: r.id,
80
+ name: r.name,
81
+ fullName: r.full_name,
82
+ private: r.private,
83
+ defaultBranch: r.default_branch,
84
+ cloneUrl: r.clone_url,
85
+ sshUrl: r.ssh_url
86
+ }));
87
+ }
88
+
89
+ /**
90
+ * Get a single pull request by number.
91
+ * @param {{ repo: string, pullNumber: number }} opts
92
+ * @returns {Promise<NormalizedPR>}
93
+ */
94
+ async getPullRequest({ repo, pullNumber }) {
95
+ const data = await this._request('GET', this._repoPath(repo, `/pulls/${pullNumber}`));
96
+ return this._normalizePR(data);
97
+ }
98
+
99
+ /**
100
+ * Create a pull request.
101
+ * @param {{ repo: string, title: string, head: string, base: string, body?: string }} opts
102
+ * @returns {Promise<NormalizedPR>}
103
+ */
104
+ async createPullRequest({ repo, title, head, base, body = '' }) {
105
+ const data = await this._request('POST', this._repoPath(repo, '/pulls'), {
106
+ title,
107
+ head,
108
+ base,
109
+ body
110
+ });
111
+ return this._normalizePR(data);
112
+ }
113
+
114
+ /**
115
+ * Merge a pull request.
116
+ * @param {{ repo: string, pullNumber: number, mergeMethod?: 'merge'|'squash'|'rebase', commitTitle?: string }} opts
117
+ * @returns {Promise<{ merged: boolean, sha: string, message: string }>}
118
+ */
119
+ async mergePullRequest({ repo, pullNumber, mergeMethod = 'merge', commitTitle } = {}) {
120
+ if (!VALID_MERGE_METHODS.has(mergeMethod)) {
121
+ throw new Error(
122
+ `GitHubGitForge.mergePullRequest: invalid mergeMethod "${mergeMethod}". ` +
123
+ `Valid values are: ${[...VALID_MERGE_METHODS].join(', ')}`
124
+ );
125
+ }
126
+ const payload = { merge_method: mergeMethod };
127
+ if (commitTitle) payload.commit_title = commitTitle;
128
+ const data = await this._request('PUT', this._repoPath(repo, `/pulls/${pullNumber}/merge`), payload);
129
+ return {
130
+ merged: data?.merged ?? true,
131
+ sha: data?.sha ?? '',
132
+ message: data?.message ?? ''
133
+ };
134
+ }
135
+
136
+ /**
137
+ * List branches and tags for a repository.
138
+ * @param {{ repo: string }} opts
139
+ * @returns {Promise<{ branches: NormalizedBranch[], tags: NormalizedTag[] }>}
140
+ */
141
+ async listRefs({ repo }) {
142
+ const [branchData, tagData] = await Promise.all([
143
+ this._request('GET', this._repoPath(repo, '/branches')),
144
+ this._request('GET', this._repoPath(repo, '/tags'))
145
+ ]);
146
+
147
+ const branches = (branchData ?? []).map(b => ({
148
+ name: b.name,
149
+ sha: b.commit?.sha ?? b.sha,
150
+ protected: b.protected ?? false
151
+ }));
152
+
153
+ const tags = (tagData ?? []).map(t => ({
154
+ name: t.name,
155
+ sha: t.commit?.sha ?? t.sha
156
+ }));
157
+
158
+ return { branches, tags };
159
+ }
160
+
161
+ /**
162
+ * Sync deploy keys: add missing, remove extra.
163
+ * @param {{ repo: string, desiredKeys: Array<{ title: string, key: string, readOnly: boolean }> }} opts
164
+ * @returns {Promise<{ added: number, removed: number }>}
165
+ */
166
+ async syncDeployKeys({ repo, desiredKeys = [] }) {
167
+ const current = await this._request('GET', this._repoPath(repo, '/keys'));
168
+ const currentList = current ?? [];
169
+
170
+ // Find keys to remove (title not in desired)
171
+ const desiredTitles = new Set(desiredKeys.map(k => k.title));
172
+ const toRemove = currentList.filter(k => !desiredTitles.has(k.title));
173
+
174
+ // Find keys to add (title not in current)
175
+ const currentTitles = new Set(currentList.map(k => k.title));
176
+ const toAdd = desiredKeys.filter(k => !currentTitles.has(k.title));
177
+
178
+ // Perform deletions
179
+ await Promise.all(toRemove.map(k =>
180
+ this._request('DELETE', this._repoPath(repo, `/keys/${k.id}`))
181
+ ));
182
+
183
+ // Perform additions
184
+ await Promise.all(toAdd.map(k =>
185
+ this._request('POST', this._repoPath(repo, '/keys'), {
186
+ title: k.title,
187
+ key: k.key,
188
+ read_only: k.readOnly ?? true
189
+ })
190
+ ));
191
+
192
+ return { added: toAdd.length, removed: toRemove.length };
193
+ }
194
+
195
+ /**
196
+ * Sync branch protection rules.
197
+ * @param {{ repo: string, branch: string, requiredReviews?: number, requiredStatusChecks?: string[], dismissStaleReviews?: boolean, enforceAdmins?: boolean }} opts
198
+ * @returns {Promise<object>}
199
+ */
200
+ async syncBranchProtection({
201
+ repo,
202
+ branch = 'main',
203
+ requiredReviews = 1,
204
+ requiredStatusChecks = [],
205
+ dismissStaleReviews = false,
206
+ enforceAdmins = false
207
+ } = {}) {
208
+ const payload = {
209
+ required_status_checks: requiredStatusChecks.length > 0
210
+ ? { strict: true, contexts: requiredStatusChecks }
211
+ : null,
212
+ enforce_admins: enforceAdmins,
213
+ required_pull_request_reviews: {
214
+ dismiss_stale_reviews: dismissStaleReviews,
215
+ require_code_owner_reviews: false,
216
+ required_approving_review_count: requiredReviews
217
+ },
218
+ restrictions: null
219
+ };
220
+
221
+ return this._request('PUT', this._repoPath(repo, `/branches/${encodeURIComponent(branch)}/protection`), payload);
222
+ }
223
+
224
+ // ---------------------------------------------------------------------------
225
+ // Internal normalizer
226
+ // ---------------------------------------------------------------------------
227
+
228
+ _normalizePR(data) {
229
+ return {
230
+ number: data.number,
231
+ title: data.title,
232
+ state: data.state,
233
+ head: { ref: data.head?.ref, sha: data.head?.sha },
234
+ base: { ref: data.base?.ref, sha: data.base?.sha },
235
+ body: data.body ?? '',
236
+ merged: data.merged ?? false,
237
+ htmlUrl: data.html_url ?? ''
238
+ };
239
+ }
240
+ }
@@ -0,0 +1,144 @@
1
+ // GitHub External Provider — Slice 3.3a / 3.3b
2
+ // Entry point for the GitHub provider adapter.
3
+ // Re-exports auth helpers, git forge class, issue tracking, CI/CD, boundary objects, and provider factory.
4
+
5
+ export { createGitHubJwt, exchangeInstallationToken } from './auth.js';
6
+
7
+ import { GitHubGitForge } from './git-forge.js';
8
+ import { GitHubIssueTracking } from './issue-tracking.js';
9
+ import { GitHubCicd } from './cicd.js';
10
+
11
+ export { GitHubGitForge };
12
+ export { GitHubIssueTracking };
13
+ export { GitHubCicd };
14
+
15
+ // ---------------------------------------------------------------------------
16
+ // Boundary declarations
17
+ // ---------------------------------------------------------------------------
18
+
19
+ export const GITHUB_GIT_FORGE_BOUNDARY = Object.freeze({
20
+ role: 'github-git-forge',
21
+ scope: 'GitHub App authentication (JWT signing, installation token exchange) and Git forge operations (repositories, PRs, refs, deploy keys, branch protection)',
22
+ owns: [
23
+ 'GitHub App JWT creation',
24
+ 'installation token exchange',
25
+ 'repository listing',
26
+ 'pull request lifecycle',
27
+ 'ref enumeration',
28
+ 'deploy key synchronisation',
29
+ 'branch protection synchronisation'
30
+ ],
31
+ delegatesTo: [],
32
+ mustNotOwn: [
33
+ 'GitHub secret storage',
34
+ 'Kubernetes resources',
35
+ 'CI pipeline execution',
36
+ 'webhook delivery'
37
+ ]
38
+ });
39
+
40
+ export const GITHUB_ISSUE_TRACKING_BOUNDARY = Object.freeze({
41
+ role: 'github-issue-tracking',
42
+ scope: 'GitHub Issues API: listing, creating, updating, closing issues and managing issue comments',
43
+ owns: [
44
+ 'issue listing',
45
+ 'issue creation',
46
+ 'issue updates',
47
+ 'issue closing',
48
+ 'comment listing',
49
+ 'comment creation'
50
+ ],
51
+ delegatesTo: [],
52
+ mustNotOwn: [
53
+ 'GitHub secret storage',
54
+ 'Kubernetes resources',
55
+ 'CI pipeline execution',
56
+ 'webhook delivery',
57
+ 'pull requests',
58
+ 'branch protection'
59
+ ]
60
+ });
61
+
62
+ export const GITHUB_CICD_BOUNDARY = Object.freeze({
63
+ role: 'github-cicd',
64
+ scope: 'GitHub Actions API: workflow runs, jobs, rerun/cancel operations, and check runs',
65
+ owns: [
66
+ 'workflow run listing',
67
+ 'job listing',
68
+ 'workflow rerun',
69
+ 'workflow cancellation',
70
+ 'check run creation',
71
+ 'check run updates'
72
+ ],
73
+ delegatesTo: [],
74
+ mustNotOwn: [
75
+ 'GitHub secret storage',
76
+ 'Kubernetes resources',
77
+ 'webhook delivery',
78
+ 'issue tracking',
79
+ 'pull requests'
80
+ ]
81
+ });
82
+
83
+ // ---------------------------------------------------------------------------
84
+ // Provider factory
85
+ // ---------------------------------------------------------------------------
86
+
87
+ /**
88
+ * Create a GitHub ExternalProviderAdapter.
89
+ *
90
+ * Returns a lightweight descriptor that carries GitHub App credentials and
91
+ * exposes factory methods for constructing GitHubGitForge, GitHubIssueTracking,
92
+ * and GitHubCicd instances bound to specific installation tokens.
93
+ *
94
+ * @param {{ appId: string, privateKey: string, installationId?: string, fetchImpl?: Function }} opts
95
+ * @returns {{ type: string, config: object, createForge: Function, createIssueTracker: Function, createCicd: Function }}
96
+ */
97
+ export function createGitHubProvider({ appId, privateKey, installationId, fetchImpl } = {}) {
98
+ if (!appId) throw new Error('createGitHubProvider: appId is required');
99
+ if (!privateKey) throw new Error('createGitHubProvider: privateKey is required');
100
+
101
+ return {
102
+ type: 'github',
103
+ config: Object.freeze({ appId, installationId }),
104
+
105
+ /**
106
+ * Construct a GitHubGitForge for the given owner and installation token.
107
+ * @param {{ owner: string, installationToken: string, fetchImpl?: Function }} forgeOpts
108
+ * @returns {GitHubGitForge}
109
+ */
110
+ createForge({ owner, installationToken, fetchImpl: forgeFetch } = {}) {
111
+ return new GitHubGitForge({
112
+ owner,
113
+ installationToken,
114
+ fetchImpl: forgeFetch ?? fetchImpl ?? globalThis.fetch
115
+ });
116
+ },
117
+
118
+ /**
119
+ * Construct a GitHubIssueTracking for the given owner and installation token.
120
+ * @param {{ owner: string, installationToken: string, fetchImpl?: Function }} opts
121
+ * @returns {GitHubIssueTracking}
122
+ */
123
+ createIssueTracker({ owner, installationToken, fetchImpl: trackerFetch } = {}) {
124
+ return new GitHubIssueTracking({
125
+ owner,
126
+ installationToken,
127
+ fetchImpl: trackerFetch ?? fetchImpl ?? globalThis.fetch
128
+ });
129
+ },
130
+
131
+ /**
132
+ * Construct a GitHubCicd for the given owner and installation token.
133
+ * @param {{ owner: string, installationToken: string, fetchImpl?: Function }} opts
134
+ * @returns {GitHubCicd}
135
+ */
136
+ createCicd({ owner, installationToken, fetchImpl: cicdFetch } = {}) {
137
+ return new GitHubCicd({
138
+ owner,
139
+ installationToken,
140
+ fetchImpl: cicdFetch ?? fetchImpl ?? globalThis.fetch
141
+ });
142
+ }
143
+ };
144
+ }