@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,95 @@
1
+ export function createGiteaBackend({ baseUrl = 'http://krate-gitea-http:3000', token, fetchImpl = globalThis.fetch } = {}) {
2
+ if (!fetchImpl) throw new Error('Gitea backend requires a fetch implementation');
3
+ const root = baseUrl.replace(/\/$/, '');
4
+
5
+ async function request(method, path, body) {
6
+ const response = await fetchImpl(`${root}/api/v1${path}`, {
7
+ method,
8
+ headers: {
9
+ Accept: 'application/json',
10
+ 'Content-Type': 'application/json',
11
+ ...(token ? { Authorization: `token ${token}` } : {})
12
+ },
13
+ ...(body === undefined ? {} : { body: JSON.stringify(body) })
14
+ });
15
+ if (!response.ok) throw new Error(`Gitea ${method} ${path} failed with ${response.status}`);
16
+ return response.status === 204 ? null : response.json();
17
+ }
18
+
19
+ return {
20
+ role: 'gitea-backend',
21
+ baseUrl: root,
22
+ createOrganization({ name, fullName = name, description = '', visibility = 'private' }) {
23
+ return request('POST', '/orgs', { username: name, full_name: fullName, description, visibility });
24
+ },
25
+ createUser({ username, email, fullName = username, password, mustChangePassword = true }) {
26
+ return request('POST', '/admin/users', { username, email, full_name: fullName, password, must_change_password: mustChangePassword });
27
+ },
28
+ editUser({ username, email, fullName, active = true, admin = false }) {
29
+ return request('PATCH', `/admin/users/${encodeURIComponent(username)}`, { email, full_name: fullName, active, admin });
30
+ },
31
+ addUserSshKey({ title, key, readOnly = false }) {
32
+ return request('POST', '/user/keys', { title, key, read_only: readOnly });
33
+ },
34
+ createRepository({ owner, name, private: isPrivate = true, defaultBranch = 'main', description = '' }) {
35
+ const path = owner ? `/orgs/${encodeURIComponent(owner)}/repos` : '/user/repos';
36
+ return request('POST', path, { name, private: isPrivate, default_branch: defaultBranch, description, auto_init: false });
37
+ },
38
+ addDeployKey({ owner, repo, title, key, readOnly = true }) {
39
+ return request('POST', `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/keys`, { title, key, read_only: readOnly });
40
+ },
41
+ addCollaborator({ owner, repo, username, permission = 'read' }) {
42
+ return request('PUT', `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/collaborators/${encodeURIComponent(username)}`, { permission });
43
+ },
44
+ addTeamRepository({ org, team, repo, owner = org, permission = 'read' }) {
45
+ return request('PUT', `/teams/${encodeURIComponent(team)}/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}`, { permission });
46
+ },
47
+ createTeam({ org, name, permission = 'read', units = ['repo.code', 'repo.pulls', 'repo.issues'] }) {
48
+ return request('POST', `/orgs/${encodeURIComponent(org)}/teams`, { name, permission, units });
49
+ },
50
+ addTeamMember({ team, username }) {
51
+ return request('PUT', `/teams/${encodeURIComponent(team)}/members/${encodeURIComponent(username)}`);
52
+ },
53
+ protectBranch({ owner, repo, branch = 'main', approvals = 1, statusChecks = [] }) {
54
+ return request('POST', `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/branch_protections`, {
55
+ branch_name: branch,
56
+ enable_push: false,
57
+ enable_push_whitelist: true,
58
+ required_approvals: approvals,
59
+ enable_status_check: statusChecks.length > 0,
60
+ status_check_contexts: statusChecks
61
+ });
62
+ },
63
+ createIssue({ owner, repo, title, body = '', labels = [], assignees = [] }) {
64
+ return request('POST', `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/issues`, { title, body, labels, assignees });
65
+ },
66
+ createPullRequest({ owner, repo, title, head, base = 'main', body = '' }) {
67
+ return request('POST', `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls`, { title, head, base, body });
68
+ },
69
+ createWebhook({ owner, repo, url, events = ['push', 'pull_request'], secret }) {
70
+ return request('POST', `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/hooks`, {
71
+ type: 'gitea',
72
+ active: true,
73
+ events,
74
+ config: { url, content_type: 'json', ...(secret ? { secret } : {}) }
75
+ });
76
+ }
77
+ };
78
+ }
79
+
80
+ export function giteaRepositoryIntegrationPlan({ owner, repo, deployKeyTitle = 'krate-gitops', permission = 'write', branch = 'main', webhookUrl }) {
81
+ return {
82
+ backend: 'gitea',
83
+ operations: [
84
+ { action: 'createOrganization', owner },
85
+ { action: 'createRepository', owner, repo },
86
+ { action: 'ensureUserMappings', owner },
87
+ { action: 'addDeployKey', owner, repo, title: deployKeyTitle, readOnly: false },
88
+ { action: 'addUserSshKey', owner, repo, title: 'developer key' },
89
+ { action: 'addCollaborator', owner, repo, permission },
90
+ { action: 'addTeamRepository', owner, repo, team: 'maintainers', permission: 'admin' },
91
+ { action: 'protectBranch', owner, repo, branch },
92
+ { action: 'createWebhook', owner, repo, url: webhookUrl }
93
+ ]
94
+ };
95
+ }
@@ -0,0 +1,173 @@
1
+ import { createGiteaBackend } from './gitea-backend.js';
2
+
3
+ /**
4
+ * Gitea service layer that wraps gitea-backend.js for repo code-browsing operations.
5
+ *
6
+ * Returns null when Gitea is not configured so call-sites can fall back to mock data.
7
+ *
8
+ * @param {{ giteaUrl?: string, token?: string, fetchImpl?: Function }} [options]
9
+ * @returns {GiteaService|null}
10
+ */
11
+ export function createGiteaService(options = {}) {
12
+ const giteaUrl = options.giteaUrl || process.env.KRATE_GITEA_HTTP_URL;
13
+ if (!giteaUrl) return null; // Gitea not configured — callers must fall back
14
+
15
+ const backend = createGiteaBackend({
16
+ baseUrl: giteaUrl,
17
+ token: options.token || process.env.KRATE_GITEA_TOKEN,
18
+ fetchImpl: options.fetchImpl || globalThis.fetch,
19
+ });
20
+
21
+ // Low-level fetch helper that bypasses the backend's opinionated error handling
22
+ // so service methods can gracefully return null on 404 rather than throwing.
23
+ const root = giteaUrl.replace(/\/$/, '');
24
+ const fetchImpl = options.fetchImpl || globalThis.fetch;
25
+ const token = options.token || process.env.KRATE_GITEA_TOKEN;
26
+
27
+ async function rawRequest(path) {
28
+ if (!fetchImpl) throw new Error('Gitea service requires a fetch implementation');
29
+ const response = await fetchImpl(`${root}/api/v1${path}`, {
30
+ method: 'GET',
31
+ headers: {
32
+ Accept: 'application/json',
33
+ ...(token ? { Authorization: `token ${token}` } : {}),
34
+ },
35
+ });
36
+ if (response.status === 404) return null;
37
+ if (!response.ok) {
38
+ throw new Error(`Gitea GET ${path} failed with ${response.status}`);
39
+ }
40
+ return response.json();
41
+ }
42
+
43
+ async function rawRawRequest(path) {
44
+ if (!fetchImpl) throw new Error('Gitea service requires a fetch implementation');
45
+ const response = await fetchImpl(`${root}/api/v1${path}`, {
46
+ method: 'GET',
47
+ headers: {
48
+ Accept: 'text/plain',
49
+ ...(token ? { Authorization: `token ${token}` } : {}),
50
+ },
51
+ });
52
+ if (response.status === 404) return null;
53
+ if (!response.ok) {
54
+ throw new Error(`Gitea GET ${path} failed with ${response.status}`);
55
+ }
56
+ return response.text();
57
+ }
58
+
59
+ return {
60
+ available: true,
61
+ baseUrl: root,
62
+
63
+ /**
64
+ * List tree entries for a repository path at a given ref.
65
+ * Uses GET /api/v1/repos/{owner}/{repo}/contents/{path}?ref={ref}
66
+ *
67
+ * @param {string} org
68
+ * @param {string} repo
69
+ * @param {string} ref — branch name, tag, or commit SHA
70
+ * @param {string} [path='']
71
+ * @returns {Promise<Array<{path:string,type:'blob'|'tree',size:number}>|null>}
72
+ */
73
+ async listTree(org, repo, ref, path = '') {
74
+ const encodedPath = path ? encodeURIComponent(path).replace(/%2F/g, '/') : '';
75
+ const apiPath = `/repos/${encodeURIComponent(org)}/${encodeURIComponent(repo)}/contents/${encodedPath}?ref=${encodeURIComponent(ref)}`;
76
+ const data = await rawRequest(apiPath);
77
+ if (data === null) return null;
78
+
79
+ // Gitea returns an array for directories, an object for files
80
+ const entries = Array.isArray(data) ? data : [data];
81
+ return entries.map((entry) => ({
82
+ path: entry.path || entry.name,
83
+ type: entry.type === 'dir' ? 'tree' : 'blob',
84
+ size: entry.size || 0,
85
+ sha: entry.sha,
86
+ name: entry.name,
87
+ }));
88
+ },
89
+
90
+ /**
91
+ * Get the raw text content of a file.
92
+ * Uses GET /api/v1/repos/{owner}/{repo}/raw/{filepath}?ref={ref}
93
+ *
94
+ * @param {string} org
95
+ * @param {string} repo
96
+ * @param {string} ref
97
+ * @param {string} path
98
+ * @returns {Promise<string|null>}
99
+ */
100
+ async getBlob(org, repo, ref, path) {
101
+ const encodedPath = path ? encodeURIComponent(path).replace(/%2F/g, '/') : '';
102
+ const apiPath = `/repos/${encodeURIComponent(org)}/${encodeURIComponent(repo)}/raw/${encodedPath}?ref=${encodeURIComponent(ref)}`;
103
+ return rawRawRequest(apiPath);
104
+ },
105
+
106
+ /**
107
+ * List branches for a repository.
108
+ * Uses GET /api/v1/repos/{owner}/{repo}/branches
109
+ *
110
+ * @param {string} org
111
+ * @param {string} repo
112
+ * @returns {Promise<Array<{name:string,sha:string,protected:boolean}>|null>}
113
+ */
114
+ async listBranches(org, repo) {
115
+ const data = await rawRequest(`/repos/${encodeURIComponent(org)}/${encodeURIComponent(repo)}/branches`);
116
+ if (data === null) return null;
117
+ return data.map((b) => ({
118
+ name: b.name,
119
+ sha: b.commit?.id || b.commit?.sha || '',
120
+ protected: b.protected || false,
121
+ }));
122
+ },
123
+
124
+ /**
125
+ * Get file metadata + base64-decoded content for a path.
126
+ * Uses GET /api/v1/repos/{owner}/{repo}/contents/{path}?ref={ref}
127
+ *
128
+ * @param {string} org
129
+ * @param {string} repo
130
+ * @param {string} ref
131
+ * @param {string} path
132
+ * @returns {Promise<{path:string,content:string,size:number,sha:string,encoding:string}|null>}
133
+ */
134
+ async getFileContent(org, repo, ref, path) {
135
+ const encodedPath = path ? encodeURIComponent(path).replace(/%2F/g, '/') : '';
136
+ const apiPath = `/repos/${encodeURIComponent(org)}/${encodeURIComponent(repo)}/contents/${encodedPath}?ref=${encodeURIComponent(ref)}`;
137
+ const data = await rawRequest(apiPath);
138
+ if (data === null || Array.isArray(data)) return null; // null = not found, array = directory
139
+
140
+ // Gitea returns content as base64 — decode it
141
+ const rawContent = data.content
142
+ ? Buffer.from(data.content.replace(/\n/g, ''), 'base64').toString('utf8')
143
+ : '';
144
+
145
+ return {
146
+ path: data.path || path,
147
+ content: rawContent,
148
+ size: data.size || Buffer.byteLength(rawContent, 'utf8'),
149
+ sha: data.sha,
150
+ encoding: 'utf-8',
151
+ lastCommit: data.last_commit_sha || null,
152
+ };
153
+ },
154
+
155
+ /**
156
+ * Create a repository inside an org.
157
+ * Delegates to the underlying gitea-backend.
158
+ *
159
+ * @param {string} org
160
+ * @param {string} name
161
+ * @param {{ private?: boolean, defaultBranch?: string, description?: string }} [repoOptions]
162
+ */
163
+ async createRepository(org, name, repoOptions = {}) {
164
+ return backend.createRepository({
165
+ owner: org,
166
+ name,
167
+ private: repoOptions.private ?? true,
168
+ defaultBranch: repoOptions.defaultBranch || 'main',
169
+ description: repoOptions.description || '',
170
+ });
171
+ },
172
+ };
173
+ }
package/src/handoff.js ADDED
@@ -0,0 +1,98 @@
1
+ export const HANDOFF_COMMANDS = Object.freeze({
2
+ build: 'npm run build',
3
+ demo: 'npm run demo',
4
+ serve: 'npm run serve',
5
+ check: 'npm run check',
6
+ test: 'npm test',
7
+ smoke: 'npm run smoke',
8
+ validateDocs: 'npm run validate:docs',
9
+ e2e: 'npm run e2e',
10
+ packageCheck: 'npm run package:check',
11
+ setupMinikube: 'npm run setup:minikube -- --dry-run',
12
+ dev: 'npm run dev',
13
+ uiBuild: 'npm run ui:build',
14
+ uiValidate: 'npm run ui:validate'
15
+ });
16
+
17
+ export const HANDOFF_DOCS = Object.freeze([
18
+ 'docs/README.md',
19
+ 'docs/product-requirements.md',
20
+ 'docs/system-requirements.md',
21
+ 'docs/architecture-spec.md',
22
+ 'docs/user-stories.md',
23
+ 'docs/roadmap-mvp.md',
24
+ 'docs/local-minikube.md',
25
+ 'docs/install.md',
26
+ 'docs/ontology/README.md'
27
+ ]);
28
+
29
+ export function createKrateHandoffSummary(demo, { packageInfo = {}, generatedAt = new Date().toISOString() } = {}) {
30
+ const smoke = demo.smoke || { ok: true, assertions: [] };
31
+ return {
32
+ project: 'Krate',
33
+ description: 'Kubernetes-native forge runtime with Argo CD and Krate-managed repository hosting',
34
+ package: {
35
+ name: packageInfo.name || 'krate',
36
+ version: packageInfo.version || '0.1.0',
37
+ private: packageInfo.private !== false
38
+ },
39
+ entrypoints: {
40
+ library: './src/index.js',
41
+ cli: './bin/krate-demo.mjs',
42
+ runtimeServer: './bin/krate-server.mjs',
43
+ webPreview: './public/index.html',
44
+ nextUi: './apps/web',
45
+ generatedSummary: './dist/krate-summary.json',
46
+ lifecycleSnapshot: './dist/krate-lifecycle.json',
47
+ helmChart: './charts/krate',
48
+ minikubeSetup: './scripts/setup-minikube.mjs'
49
+ },
50
+ commands: { ...HANDOFF_COMMANDS },
51
+ docs: [...HANDOFF_DOCS],
52
+ components: demo.components || [],
53
+ lifecycle: demo.lifecycle || null,
54
+ resources: Object.fromEntries(Object.entries(demo.resources).map(([key, value]) => [key, value?.kind || 'workflow'])),
55
+ storage: demo.controlPlane.storageReport(),
56
+ excellentFlows: demo.ui.dashboard.excellentFlows,
57
+ agents: demo.agents ? {
58
+ stacks: demo.agents.stacks?.count || 0,
59
+ runs: demo.agents.runs?.count || 0,
60
+ activeRuns: demo.agents.runs?.active?.length || 0,
61
+ rules: demo.agents.rules?.count || 0,
62
+ sessions: demo.agents.sessions?.count || 0,
63
+ workspaces: demo.agents.workspaces?.count || 0,
64
+ pendingApprovals: demo.agents.approvals?.pending?.length || 0
65
+ } : null,
66
+ operations: { chartPackage: demo.operations.chartPackage, localSetup: demo.operations.localSetup },
67
+ releaseGates: demo.operations.releaseGates,
68
+ smoke: {
69
+ ok: smoke.ok,
70
+ assertions: smoke.assertions.map(([name, passed]) => ({ name, passed }))
71
+ },
72
+ generatedAt
73
+ };
74
+ }
75
+
76
+ export function formatHandoffSummary(summary) {
77
+ const lines = [
78
+ `${summary.project} ${summary.package.version} — ${summary.description}`,
79
+ '',
80
+ 'Entrypoints:',
81
+ ...Object.entries(summary.entrypoints).map(([name, target]) => `- ${name}: ${target}`),
82
+ '',
83
+ 'Commands:',
84
+ ...Object.entries(summary.commands).map(([name, command]) => `- ${name}: ${command}`),
85
+ '',
86
+ 'Storage boundary:',
87
+ `- etcd: ${summary.storage.etcd.join(', ')}`,
88
+ `- postgres: ${summary.storage.postgres.join(', ')}`,
89
+ '',
90
+ 'Excellent flows:',
91
+ ...summary.excellentFlows.map((flow) => `- ${flow}`),
92
+ '',
93
+ `Smoke: ${summary.smoke.ok ? 'pass' : 'fail'}`
94
+ ];
95
+ return `${lines.join('\n')}\n`;
96
+ }
97
+
98
+
@@ -0,0 +1,63 @@
1
+ import { createHmac } from 'node:crypto';
2
+ import { createResource } from './resource-model.js';
3
+
4
+ export class WebhookBus {
5
+ constructor({ controlPlane, secret = 'krate-dev-secret' }) { this.controlPlane = controlPlane; this.secret = secret; }
6
+
7
+ subscribe({ name, namespace = 'krate-org-default', organizationRef = 'default', url, events = ['pullrequest.created'], mode = 'active' }, user) {
8
+ return this.controlPlane.create(createResource('WebhookSubscription', { name, namespace }, {
9
+ organizationRef, url, events, signing: { algorithm: 'hmac-sha256', secretRef: `${name}-secret` }, mode
10
+ }, { ready: true }), user);
11
+ }
12
+
13
+ sign(payload) { return createHmac('sha256', this.secret).update(JSON.stringify(payload)).digest('hex'); }
14
+
15
+ deliver({ subscriptionName, namespace = 'krate-org-default', organizationRef = 'default', eventType, payload, response = { status: 202, body: 'accepted' } }, user) {
16
+ const subscription = this.controlPlane.get('WebhookSubscription', namespace, subscriptionName);
17
+ if (!subscription) throw new Error(`WebhookSubscription ${subscriptionName} not found`);
18
+ if (!subscription.spec.events.includes(eventType)) throw new Error(`${subscriptionName} does not subscribe to ${eventType}`);
19
+ const delivery = createResource('WebhookDelivery', {
20
+ name: `${subscriptionName}-${Date.now()}-${this.controlPlane.list('WebhookDelivery', { namespace }).items.length + 1}`,
21
+ namespace,
22
+ labels: { subscription: subscriptionName, eventType }
23
+ }, {
24
+ organizationRef: subscription.spec.organizationRef || organizationRef,
25
+ subscription: subscriptionName,
26
+ url: subscription.spec.url,
27
+ eventType,
28
+ payload,
29
+ signature: this.sign(payload),
30
+ replayOf: payload.replayOf || null
31
+ }, {
32
+ phase: response.status >= 200 && response.status < 300 ? 'Delivered' : 'Failed',
33
+ response,
34
+ attempts: 1
35
+ });
36
+ return this.controlPlane.create(delivery, user);
37
+ }
38
+
39
+ replay(delivery, user) {
40
+ return this.deliver({
41
+ subscriptionName: delivery.spec.subscription,
42
+ namespace: delivery.metadata.namespace,
43
+ organizationRef: delivery.spec.organizationRef,
44
+ eventType: delivery.spec.eventType,
45
+ payload: { ...delivery.spec.payload, replayOf: delivery.metadata.name },
46
+ response: { status: 202, body: 'replayed' }
47
+ }, user);
48
+ }
49
+
50
+ inspect(delivery) {
51
+ return {
52
+ name: delivery.metadata.name,
53
+ subscription: delivery.spec.subscription,
54
+ eventType: delivery.spec.eventType,
55
+ phase: delivery.status.phase,
56
+ attempts: delivery.status.attempts,
57
+ response: delivery.status.response,
58
+ signature: delivery.spec.signature,
59
+ replayOf: delivery.spec.replayOf,
60
+ replayable: Boolean(delivery.spec.subscription && delivery.spec.eventType)
61
+ };
62
+ }
63
+ }