@a5c-ai/krate 5.0.1-staging.3a341c33c
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.
- package/Dockerfile +31 -0
- package/README.md +183 -0
- package/bin/krate-demo.mjs +23 -0
- package/bin/krate-server.mjs +14 -0
- package/dist/krate-controller-ui.json +2455 -0
- package/dist/krate-lifecycle.json +201 -0
- package/dist/krate-runtime-snapshot.json +2955 -0
- package/dist/krate-summary.json +722 -0
- package/docs/README.md +61 -0
- package/docs/agents/README.md +83 -0
- package/docs/agents/acceptance-test-matrix.md +193 -0
- package/docs/agents/agent-mux-adapter-contract.md +167 -0
- package/docs/agents/agent-mux-source-map.md +310 -0
- package/docs/agents/agent-run-memory-import-spec.md +256 -0
- package/docs/agents/agent-stack-management-spec.md +421 -0
- package/docs/agents/api-contract-spec.md +309 -0
- package/docs/agents/artifacts-writeback-spec.md +145 -0
- package/docs/agents/chart-packaging-spec.md +128 -0
- package/docs/agents/ci-orchestration-spec.md +140 -0
- package/docs/agents/context-assembly-spec.md +219 -0
- package/docs/agents/controller-reconciliation-spec.md +255 -0
- package/docs/agents/crd-schema-spec.md +315 -0
- package/docs/agents/decision-log-open-questions.md +169 -0
- package/docs/agents/developer-implementation-checklist.md +329 -0
- package/docs/agents/dispatching-design.md +262 -0
- package/docs/agents/glossary.md +66 -0
- package/docs/agents/implementation-blueprint.md +324 -0
- package/docs/agents/implementation-rollout-slices.md +251 -0
- package/docs/agents/memory-context-integration-spec.md +194 -0
- package/docs/agents/memory-ontology-schema-spec.md +253 -0
- package/docs/agents/memory-operations-runbook.md +121 -0
- package/docs/agents/mvp-vertical-slice-spec.md +146 -0
- package/docs/agents/observability-audit-spec.md +265 -0
- package/docs/agents/operator-runbook.md +174 -0
- package/docs/agents/org-memory-api-payload-examples.md +333 -0
- package/docs/agents/org-memory-controller-sequence-spec.md +181 -0
- package/docs/agents/org-memory-e2e-fixture-plan.md +161 -0
- package/docs/agents/org-memory-ui-implementation-map.md +114 -0
- package/docs/agents/org-memory-vertical-slice-spec.md +168 -0
- package/docs/agents/org-resource-model-delta-spec.md +111 -0
- package/docs/agents/org-route-resource-model-spec.md +183 -0
- package/docs/agents/org-scoping-namespace-spec.md +114 -0
- package/docs/agents/rbac-secrets-management-spec.md +406 -0
- package/docs/agents/repository-page-integration-spec.md +255 -0
- package/docs/agents/resource-contract-examples.md +808 -0
- package/docs/agents/resource-relationship-map.md +190 -0
- package/docs/agents/security-threat-model.md +188 -0
- package/docs/agents/shared-memory-company-brain-spec.md +358 -0
- package/docs/agents/storage-migration-spec.md +168 -0
- package/docs/agents/subagent-orchestration-spec.md +152 -0
- package/docs/agents/system-overview.md +88 -0
- package/docs/agents/tools-mcp-skills-spec.md +189 -0
- package/docs/agents/traceability-matrix.md +79 -0
- package/docs/agents/ui-flow-spec.md +211 -0
- package/docs/agents/ui-ux-system-spec.md +426 -0
- package/docs/agents/workspace-lifecycle-spec.md +166 -0
- package/docs/architecture-spec.md +78 -0
- package/docs/components/control-plane.md +78 -0
- package/docs/components/data-plane.md +69 -0
- package/docs/components/hooks-events.md +67 -0
- package/docs/components/identity-rbac-policy.md +73 -0
- package/docs/components/kubevela-oam.md +70 -0
- package/docs/components/operations-publishing.md +81 -0
- package/docs/components/runners-ci.md +66 -0
- package/docs/components/web-ui.md +94 -0
- package/docs/external/README.md +47 -0
- package/docs/external/bidirectional-sync-design.md +134 -0
- package/docs/external/cicd-interface.md +64 -0
- package/docs/external/external-backend-controllers.md +170 -0
- package/docs/external/external-backend-crds.md +234 -0
- package/docs/external/external-backend-ui-spec.md +151 -0
- package/docs/external/external-backend-ux-flows.md +115 -0
- package/docs/external/external-object-mapping.md +125 -0
- package/docs/external/git-forge-interface.md +68 -0
- package/docs/external/github-integration-design.md +151 -0
- package/docs/external/issue-tracking-interface.md +66 -0
- package/docs/external/provider-capability-manifests.md +204 -0
- package/docs/external/provider-catalog.md +139 -0
- package/docs/external/provider-rollout-testing.md +78 -0
- package/docs/external/research-results.md +48 -0
- package/docs/external/security-auth-permissions.md +81 -0
- package/docs/external/sync-state-machines.md +108 -0
- package/docs/external/unified-external-backend-model.md +107 -0
- package/docs/external/user-facing-changes.md +67 -0
- package/docs/gaps.md +161 -0
- package/docs/install.md +94 -0
- package/docs/krate-design.md +334 -0
- package/docs/local-minikube.md +55 -0
- package/docs/ontology/README.md +32 -0
- package/docs/ontology/bounded-contexts.md +29 -0
- package/docs/ontology/events-and-hooks.md +32 -0
- package/docs/ontology/oam-kubevela.md +32 -0
- package/docs/ontology/operations-and-release.md +25 -0
- package/docs/ontology/personas-and-actors.md +32 -0
- package/docs/ontology/policies-and-invariants.md +33 -0
- package/docs/ontology/problem-space.md +30 -0
- package/docs/ontology/resource-contracts.md +40 -0
- package/docs/ontology/resource-taxonomy.md +42 -0
- package/docs/ontology/runners-and-ci.md +29 -0
- package/docs/ontology/solution-space.md +24 -0
- package/docs/ontology/storage-and-data-boundaries.md +29 -0
- package/docs/ontology/validation-matrix.md +24 -0
- package/docs/ontology/web-ui-excellent-flows.md +32 -0
- package/docs/ontology/workflows.md +39 -0
- package/docs/ontology/world.md +35 -0
- package/docs/product-requirements.md +62 -0
- package/docs/roadmap-mvp.md +87 -0
- package/docs/system-requirements.md +90 -0
- package/docs/tests/README.md +53 -0
- package/docs/tests/agent-qa-plan.md +63 -0
- package/docs/tests/browser-ui-tests.md +62 -0
- package/docs/tests/ci-quality-gates.md +48 -0
- package/docs/tests/coverage-model.md +64 -0
- package/docs/tests/e2e-scenario-tests.md +53 -0
- package/docs/tests/fixtures-test-data.md +63 -0
- package/docs/tests/observability-reliability-tests.md +54 -0
- package/docs/tests/product-test-matrix.md +145 -0
- package/docs/tests/qa-adoption-roadmap.md +130 -0
- package/docs/tests/qa-automation-plan.md +101 -0
- package/docs/tests/security-compliance-tests.md +57 -0
- package/docs/tests/test-framework-tools.md +88 -0
- package/docs/tests/test-suite-layout.md +121 -0
- package/docs/tests/unit-integration-tests.md +48 -0
- package/docs/todo-kyverno +714 -0
- package/docs/user-stories.md +78 -0
- package/examples/minikube-demo.yaml +190 -0
- package/examples/oam-application.yaml +23 -0
- package/examples/policy-kyverno-pr-title.yaml +18 -0
- package/package.json +63 -0
- package/scripts/build.mjs +29 -0
- package/scripts/setup-minikube.mjs +65 -0
- package/scripts/smoke.mjs +37 -0
- package/scripts/validate-doc-coverage.mjs +152 -0
- package/scripts/validate-package.mjs +93 -0
- package/scripts/validate-ui.mjs +207 -0
- package/src/agent-approval-controller.js +123 -0
- package/src/agent-context-bundles.js +242 -0
- package/src/agent-dispatch-controller.js +86 -0
- package/src/agent-memory-controller.js +374 -0
- package/src/agent-mux-client.js +280 -0
- package/src/agent-permission-review.js +162 -0
- package/src/agent-stack-controller.js +296 -0
- package/src/agent-trigger-controller.js +108 -0
- package/src/agent-workspace-controller.js +208 -0
- package/src/api-controller.js +248 -0
- package/src/argocd-gitops.js +43 -0
- package/src/auth.js +265 -0
- package/src/component-catalog.js +41 -0
- package/src/control-plane.js +136 -0
- package/src/controller-client.js +38 -0
- package/src/controller-ui.js +551 -0
- package/src/data-plane.js +178 -0
- package/src/gitea-backend.js +95 -0
- package/src/handoff.js +98 -0
- package/src/hooks-events.js +63 -0
- package/src/http-server.js +151 -0
- package/src/identity-policy.js +86 -0
- package/src/index.js +32 -0
- package/src/kubernetes-controller.js +812 -0
- package/src/kubernetes-resource-gateway.js +48 -0
- package/src/operations.js +112 -0
- package/src/resource-model.js +211 -0
- package/src/runners-ci.js +48 -0
- package/src/runtime.js +196 -0
- package/src/web-ui.js +40 -0
- package/tests/agent-approval-controller.test.js +173 -0
- package/tests/agent-context-bundles.test.js +278 -0
- package/tests/agent-dispatch-controller.test.js +176 -0
- package/tests/agent-memory-controller.test.js +308 -0
- package/tests/agent-mux-client.test.js +204 -0
- package/tests/agent-permission-review.test.js +209 -0
- package/tests/agent-resources.test.js +228 -0
- package/tests/agent-stack-controller.test.js +221 -0
- package/tests/agent-trigger-controller.test.js +211 -0
- package/tests/agent-workspace-controller.test.js +215 -0
- package/tests/deployment.test.js +393 -0
- package/tests/e2e/lifecycle.test.js +117 -0
- package/tests/krate.test.js +727 -0
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { createResource, clone } from './resource-model.js';
|
|
2
|
+
|
|
3
|
+
export const AGENT_WORKSPACE_CONTROLLER_BOUNDARY = {
|
|
4
|
+
role: 'agent-workspace-controller',
|
|
5
|
+
scope: 'Git worktree provisioning, lifecycle management, session/work-item linking',
|
|
6
|
+
owns: ['workspace creation', 'worktree lifecycle', 'session binding', 'work-item linking'],
|
|
7
|
+
delegatesTo: ['resource-model'],
|
|
8
|
+
mustNotOwn: ['git operations', 'Agent Mux sessions', 'secret values']
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export function createAgentWorkspaceController() {
|
|
12
|
+
return {
|
|
13
|
+
role: 'agent-workspace-controller',
|
|
14
|
+
|
|
15
|
+
provisionWorkspace({ repository, ref, branch, dispatchRun, policy, namespace = 'default', organizationRef = 'default' }) {
|
|
16
|
+
if (!repository) {
|
|
17
|
+
return { error: true, reason: 'missing-repository', message: 'repository is required' };
|
|
18
|
+
}
|
|
19
|
+
if (!dispatchRun) {
|
|
20
|
+
return { error: true, reason: 'missing-dispatch-run', message: 'dispatchRun is required' };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const branchName = branch || 'main';
|
|
24
|
+
const workspacePath = `/workspaces/${repository}/${branchName}-${Date.now()}`;
|
|
25
|
+
const workspaceName = `ws-${repository}-${branchName}-${Date.now()}`;
|
|
26
|
+
|
|
27
|
+
const workspace = createResource('AgentWorkspace', { name: workspaceName, namespace }, {
|
|
28
|
+
organizationRef,
|
|
29
|
+
repository,
|
|
30
|
+
workspacePath,
|
|
31
|
+
ownership: dispatchRun,
|
|
32
|
+
ref: ref || undefined,
|
|
33
|
+
branch: branchName,
|
|
34
|
+
policy: policy || undefined
|
|
35
|
+
});
|
|
36
|
+
workspace.status = { phase: 'Active', createdAt: new Date().toISOString(), boundSessions: [] };
|
|
37
|
+
|
|
38
|
+
const runtimeName = `rt-${workspaceName}`;
|
|
39
|
+
const runtime = createResource('AgentWorkspaceRuntime', { name: runtimeName, namespace }, {
|
|
40
|
+
organizationRef,
|
|
41
|
+
workspaceRef: workspaceName,
|
|
42
|
+
status: 'provisioning'
|
|
43
|
+
});
|
|
44
|
+
runtime.status = { phase: 'Provisioning', createdAt: new Date().toISOString() };
|
|
45
|
+
|
|
46
|
+
return { error: false, workspace, runtime };
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
archiveWorkspace({ workspaceName, reason, resources = {} }) {
|
|
50
|
+
if (!workspaceName) {
|
|
51
|
+
return { error: true, reason: 'missing-workspace-name', message: 'workspaceName is required' };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const workspaces = resources.AgentWorkspace || [];
|
|
55
|
+
const workspace = workspaces.find((w) => w.metadata?.name === workspaceName);
|
|
56
|
+
if (!workspace) {
|
|
57
|
+
return { error: true, reason: 'not-found', message: `AgentWorkspace not found: ${workspaceName}` };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const now = new Date().toISOString();
|
|
61
|
+
const updated = clone(workspace);
|
|
62
|
+
updated.status = {
|
|
63
|
+
...updated.status,
|
|
64
|
+
phase: 'Archived',
|
|
65
|
+
archivedAt: now,
|
|
66
|
+
archiveReason: reason || 'No reason provided'
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
return { error: false, workspace: updated };
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
recoverWorkspace({ workspaceName, resources = {} }) {
|
|
73
|
+
if (!workspaceName) {
|
|
74
|
+
return { error: true, reason: 'missing-workspace-name', message: 'workspaceName is required' };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const workspaces = resources.AgentWorkspace || [];
|
|
78
|
+
const workspace = workspaces.find((w) => w.metadata?.name === workspaceName);
|
|
79
|
+
if (!workspace) {
|
|
80
|
+
return { error: true, reason: 'not-found', message: `AgentWorkspace not found: ${workspaceName}` };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (workspace.status?.phase !== 'Archived') {
|
|
84
|
+
return { error: true, reason: 'not-archived', message: `AgentWorkspace ${workspaceName} is not archived (current phase: ${workspace.status?.phase || 'Unknown'})` };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const updated = clone(workspace);
|
|
88
|
+
updated.status = {
|
|
89
|
+
...updated.status,
|
|
90
|
+
phase: 'Active',
|
|
91
|
+
archivedAt: undefined,
|
|
92
|
+
archiveReason: undefined
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
return { error: false, workspace: updated };
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
bindSession({ workspaceName, sessionRef, agent, namespace = 'default', organizationRef = 'default', resources = {} }) {
|
|
99
|
+
if (!workspaceName) {
|
|
100
|
+
return { error: true, reason: 'missing-workspace-name', message: 'workspaceName is required' };
|
|
101
|
+
}
|
|
102
|
+
if (!sessionRef) {
|
|
103
|
+
return { error: true, reason: 'missing-session-ref', message: 'sessionRef is required' };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const workspaces = resources.AgentWorkspace || [];
|
|
107
|
+
const workspace = workspaces.find((w) => w.metadata?.name === workspaceName);
|
|
108
|
+
if (!workspace) {
|
|
109
|
+
return { error: true, reason: 'not-found', message: `AgentWorkspace not found: ${workspaceName}` };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const updated = clone(workspace);
|
|
113
|
+
if (!updated.status) updated.status = {};
|
|
114
|
+
if (!Array.isArray(updated.status.boundSessions)) updated.status.boundSessions = [];
|
|
115
|
+
updated.status.boundSessions.push({
|
|
116
|
+
sessionRef,
|
|
117
|
+
agent: agent || undefined,
|
|
118
|
+
boundAt: new Date().toISOString()
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
return { error: false, workspace: updated };
|
|
122
|
+
},
|
|
123
|
+
|
|
124
|
+
linkWorkItem({ workspaceName, workItemRef, workItemKind, namespace = 'default', organizationRef = 'default' }) {
|
|
125
|
+
if (!workspaceName) {
|
|
126
|
+
return { error: true, reason: 'missing-workspace-name', message: 'workspaceName is required' };
|
|
127
|
+
}
|
|
128
|
+
if (!workItemRef) {
|
|
129
|
+
return { error: true, reason: 'missing-work-item-ref', message: 'workItemRef is required' };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const linkName = `wiwl-${workspaceName}-${workItemRef}-${Date.now()}`;
|
|
133
|
+
const link = createResource('WorkItemWorkspaceLink', { name: linkName, namespace }, {
|
|
134
|
+
organizationRef,
|
|
135
|
+
workItemRef,
|
|
136
|
+
workItemKind: workItemKind || 'Issue',
|
|
137
|
+
workspace: workspaceName
|
|
138
|
+
});
|
|
139
|
+
link.status = { phase: 'Active', createdAt: new Date().toISOString() };
|
|
140
|
+
|
|
141
|
+
return { error: false, link };
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
linkWorkItemToSession({ workItemRef, workItemKind, sessionRef, namespace = 'default', organizationRef = 'default' }) {
|
|
145
|
+
if (!workItemRef) {
|
|
146
|
+
return { error: true, reason: 'missing-work-item-ref', message: 'workItemRef is required' };
|
|
147
|
+
}
|
|
148
|
+
if (!sessionRef) {
|
|
149
|
+
return { error: true, reason: 'missing-session-ref', message: 'sessionRef is required' };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const linkName = `wisl-${sessionRef}-${workItemRef}-${Date.now()}`;
|
|
153
|
+
const link = createResource('WorkItemSessionLink', { name: linkName, namespace }, {
|
|
154
|
+
organizationRef,
|
|
155
|
+
workItemRef,
|
|
156
|
+
workItemKind: workItemKind || 'Issue',
|
|
157
|
+
agentSession: sessionRef
|
|
158
|
+
});
|
|
159
|
+
link.status = { phase: 'Active', createdAt: new Date().toISOString() };
|
|
160
|
+
|
|
161
|
+
return { error: false, link };
|
|
162
|
+
},
|
|
163
|
+
|
|
164
|
+
getWorkspaceStatus({ workspaceName, resources = {} }) {
|
|
165
|
+
if (!workspaceName) {
|
|
166
|
+
return { error: true, reason: 'missing-workspace-name', message: 'workspaceName is required' };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const workspaces = resources.AgentWorkspace || [];
|
|
170
|
+
const workspace = workspaces.find((w) => w.metadata?.name === workspaceName);
|
|
171
|
+
if (!workspace) {
|
|
172
|
+
return { error: true, reason: 'not-found', message: `AgentWorkspace not found: ${workspaceName}` };
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const runtimes = resources.AgentWorkspaceRuntime || [];
|
|
176
|
+
const runtime = runtimes.find((r) => r.spec?.workspaceRef === workspaceName) || null;
|
|
177
|
+
|
|
178
|
+
const sessions = (workspace.status?.boundSessions || []).map((binding) => {
|
|
179
|
+
const allSessions = resources.AgentSession || [];
|
|
180
|
+
const session = allSessions.find((s) => s.metadata?.name === binding.sessionRef);
|
|
181
|
+
return session ? clone(session) : { ref: binding.sessionRef, boundAt: binding.boundAt };
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
const workspaceLinks = (resources.WorkItemWorkspaceLink || []).filter(
|
|
185
|
+
(link) => link.spec?.workspace === workspaceName
|
|
186
|
+
);
|
|
187
|
+
const workItems = workspaceLinks.map(clone);
|
|
188
|
+
|
|
189
|
+
return {
|
|
190
|
+
error: false,
|
|
191
|
+
workspace: clone(workspace),
|
|
192
|
+
runtime: runtime ? clone(runtime) : null,
|
|
193
|
+
sessions,
|
|
194
|
+
workItems
|
|
195
|
+
};
|
|
196
|
+
},
|
|
197
|
+
|
|
198
|
+
listWorkspacesForRepo({ repository, resources = {} }) {
|
|
199
|
+
const workspaces = resources.AgentWorkspace || [];
|
|
200
|
+
return workspaces.filter((w) => w.spec?.repository === repository).map(clone);
|
|
201
|
+
},
|
|
202
|
+
|
|
203
|
+
listWorkspacesForRun({ dispatchRun, resources = {} }) {
|
|
204
|
+
const workspaces = resources.AgentWorkspace || [];
|
|
205
|
+
return workspaces.filter((w) => w.spec?.ownership === dispatchRun).map(clone);
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
}
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { createKubernetesResourceGateway } from './kubernetes-resource-gateway.js';
|
|
2
|
+
import { createPermissionReviewer } from './agent-permission-review.js';
|
|
3
|
+
import { createAgentDispatchController } from './agent-dispatch-controller.js';
|
|
4
|
+
import { createAgentApprovalController } from './agent-approval-controller.js';
|
|
5
|
+
import { createAgentTriggerController } from './agent-trigger-controller.js';
|
|
6
|
+
import { createAgentWorkspaceController } from './agent-workspace-controller.js';
|
|
7
|
+
import { createAgentMemoryController } from './agent-memory-controller.js';
|
|
8
|
+
|
|
9
|
+
export const KRATE_API_CONTROLLER_BOUNDARY = {
|
|
10
|
+
role: 'krate-api-controller',
|
|
11
|
+
scope: 'HTTP/application facade for validation, request orchestration, user-facing DTOs, API errors, and workflow affordances',
|
|
12
|
+
owns: ['input validation', 'forge DTOs', 'API errors', 'workflow affordances', 'controller UI snapshots'],
|
|
13
|
+
delegatesTo: ['kubernetes-resource-gateway', 'git-data-plane'],
|
|
14
|
+
mustNotOwn: ['kubectl process execution', 'Kubernetes reconciliation loops', 'watch stream internals', 'repository storage internals']
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export function createKrateApiController(options = {}) {
|
|
18
|
+
const resourceGateway = options.resourceGateway || createKubernetesResourceGateway(options);
|
|
19
|
+
const namespace = options.namespace || resourceGateway.namespace || process.env.KRATE_NAMESPACE || 'krate-system';
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
role: 'krate-api-controller',
|
|
23
|
+
namespace,
|
|
24
|
+
resourceGateway,
|
|
25
|
+
resourceDefinitions: resourceGateway.resourceDefinitions,
|
|
26
|
+
async snapshot() {
|
|
27
|
+
return withArchitecture(await resourceGateway.snapshot(), namespace);
|
|
28
|
+
},
|
|
29
|
+
async listRepositoriesForForge() {
|
|
30
|
+
const resources = await resourceGateway.list('Repository');
|
|
31
|
+
return normalizeResourceList(resources).map((resource) => repositoryForgeSummary(resource, namespace));
|
|
32
|
+
},
|
|
33
|
+
async getRepositoryForgeView(name) {
|
|
34
|
+
const resource = await resourceGateway.get('Repository', name);
|
|
35
|
+
const repository = resource?.resource || resource;
|
|
36
|
+
return repositoryForgeView(repository, namespace);
|
|
37
|
+
},
|
|
38
|
+
async listResource(kindOrPlural) {
|
|
39
|
+
return resourceGateway.list(kindOrPlural);
|
|
40
|
+
},
|
|
41
|
+
async getResource(kindOrPlural, name) {
|
|
42
|
+
return resourceGateway.get(kindOrPlural, name);
|
|
43
|
+
},
|
|
44
|
+
async applyResource(resource) {
|
|
45
|
+
return resourceGateway.apply(resource);
|
|
46
|
+
},
|
|
47
|
+
async deleteResource(kindOrPlural, name) {
|
|
48
|
+
return resourceGateway.delete(kindOrPlural, name);
|
|
49
|
+
},
|
|
50
|
+
async createRepository(input) {
|
|
51
|
+
const created = await resourceGateway.createRepository(input);
|
|
52
|
+
const repository = created?.resource || created;
|
|
53
|
+
return {
|
|
54
|
+
operation: created?.operation || 'create-repository',
|
|
55
|
+
command: created?.command || 'kubectl apply -f -',
|
|
56
|
+
repository: repositoryForgeSummary(repository, namespace),
|
|
57
|
+
resource: repository
|
|
58
|
+
};
|
|
59
|
+
},
|
|
60
|
+
async createOrganization(input) {
|
|
61
|
+
return resourceGateway.createOrganization(input);
|
|
62
|
+
},
|
|
63
|
+
watchResource(resourcePath, handlers = {}) {
|
|
64
|
+
return resourceGateway.watch(resourcePath, handlers);
|
|
65
|
+
},
|
|
66
|
+
async reviewAgentPermissions(input) {
|
|
67
|
+
const reviewer = createPermissionReviewer();
|
|
68
|
+
const snapshot = await this.snapshot();
|
|
69
|
+
return reviewer.reviewPermissions({
|
|
70
|
+
...input,
|
|
71
|
+
resources: snapshot.resources
|
|
72
|
+
});
|
|
73
|
+
},
|
|
74
|
+
async dispatchAgent(input) {
|
|
75
|
+
const snapshot = await this.snapshot();
|
|
76
|
+
const controller = createAgentDispatchController(input.controllerOptions || {});
|
|
77
|
+
return controller.createManualDispatch({
|
|
78
|
+
...input,
|
|
79
|
+
resources: snapshot.resources
|
|
80
|
+
});
|
|
81
|
+
},
|
|
82
|
+
async approveAgentAction(input) {
|
|
83
|
+
const snapshot = await this.snapshot();
|
|
84
|
+
const approvalController = createAgentApprovalController();
|
|
85
|
+
return approvalController.recordDecision({
|
|
86
|
+
...input,
|
|
87
|
+
decision: 'approve',
|
|
88
|
+
resources: snapshot.resources
|
|
89
|
+
});
|
|
90
|
+
},
|
|
91
|
+
async denyAgentAction(input) {
|
|
92
|
+
const snapshot = await this.snapshot();
|
|
93
|
+
const approvalController = createAgentApprovalController();
|
|
94
|
+
return approvalController.recordDecision({
|
|
95
|
+
...input,
|
|
96
|
+
decision: 'deny',
|
|
97
|
+
resources: snapshot.resources
|
|
98
|
+
});
|
|
99
|
+
},
|
|
100
|
+
async processWebhookEvent(input) {
|
|
101
|
+
const snapshot = await this.snapshot();
|
|
102
|
+
const dispatchController = createAgentDispatchController(input.controllerOptions || {});
|
|
103
|
+
const triggerController = createAgentTriggerController({ dispatchController });
|
|
104
|
+
return triggerController.processEvent({
|
|
105
|
+
event: input.event,
|
|
106
|
+
resources: snapshot.resources,
|
|
107
|
+
namespace: input.namespace || namespace,
|
|
108
|
+
organizationRef: input.organizationRef || 'default',
|
|
109
|
+
});
|
|
110
|
+
},
|
|
111
|
+
async provisionAgentWorkspace(input) {
|
|
112
|
+
const workspaceController = createAgentWorkspaceController();
|
|
113
|
+
return workspaceController.provisionWorkspace({
|
|
114
|
+
...input,
|
|
115
|
+
namespace: input.namespace || namespace,
|
|
116
|
+
organizationRef: input.organizationRef || 'default'
|
|
117
|
+
});
|
|
118
|
+
},
|
|
119
|
+
async archiveAgentWorkspace(input) {
|
|
120
|
+
const snapshot = await this.snapshot();
|
|
121
|
+
const workspaceController = createAgentWorkspaceController();
|
|
122
|
+
return workspaceController.archiveWorkspace({
|
|
123
|
+
...input,
|
|
124
|
+
resources: snapshot.resources
|
|
125
|
+
});
|
|
126
|
+
},
|
|
127
|
+
async linkWorkItem(input) {
|
|
128
|
+
const workspaceController = createAgentWorkspaceController();
|
|
129
|
+
return workspaceController.linkWorkItem({
|
|
130
|
+
...input,
|
|
131
|
+
namespace: input.namespace || namespace,
|
|
132
|
+
organizationRef: input.organizationRef || 'default'
|
|
133
|
+
});
|
|
134
|
+
},
|
|
135
|
+
async queryAgentMemory(input) {
|
|
136
|
+
const memoryController = createAgentMemoryController();
|
|
137
|
+
return memoryController.queryMemory({
|
|
138
|
+
...input,
|
|
139
|
+
namespace: input.namespace || namespace,
|
|
140
|
+
organizationRef: input.organizationRef || 'default'
|
|
141
|
+
});
|
|
142
|
+
},
|
|
143
|
+
async createMemoryImport(input) {
|
|
144
|
+
const memoryController = createAgentMemoryController();
|
|
145
|
+
return memoryController.createImport({
|
|
146
|
+
...input,
|
|
147
|
+
namespace: input.namespace || namespace,
|
|
148
|
+
organizationRef: input.organizationRef || 'default'
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export function withArchitecture(snapshot, namespace = snapshot?.namespace || 'default') {
|
|
155
|
+
return {
|
|
156
|
+
...snapshot,
|
|
157
|
+
architecture: {
|
|
158
|
+
apiController: {
|
|
159
|
+
...KRATE_API_CONTROLLER_BOUNDARY,
|
|
160
|
+
owns: [...KRATE_API_CONTROLLER_BOUNDARY.owns, '/api/controller', '/api/orgs/:org/resources', '/api/orgs/:org/repositories', '/api/watch/orgs/:org/*'],
|
|
161
|
+
scope: `${KRATE_API_CONTROLLER_BOUNDARY.scope}; never owns Kubernetes reconciliation loops`
|
|
162
|
+
},
|
|
163
|
+
resourceGateway: {
|
|
164
|
+
role: 'kubernetes-resource-gateway',
|
|
165
|
+
scope: 'Narrow application port translating API controller intent into Kubernetes resource-client calls',
|
|
166
|
+
namespace,
|
|
167
|
+
delegatesTo: ['kubernetes-resource-client']
|
|
168
|
+
},
|
|
169
|
+
kubernetesClient: {
|
|
170
|
+
role: 'kubernetes-resource-client',
|
|
171
|
+
scope: 'kubectl-backed Kubernetes API discovery, SubjectAccessReview checks, list/get/apply/delete/watch; no UI flow or product workflow ownership',
|
|
172
|
+
namespace,
|
|
173
|
+
owns: ['Krate CRDs', 'aggregated API resources', 'Kubernetes watch streams']
|
|
174
|
+
},
|
|
175
|
+
kubernetesReconciler: {
|
|
176
|
+
role: 'krate-kubernetes-reconciler',
|
|
177
|
+
scope: 'Repository status projection, repository hosting intent, policy projection, and data-plane sync intent; never owns HTTP routes or browser flows',
|
|
178
|
+
namespace,
|
|
179
|
+
delegatesTo: ['kubernetes-resource-gateway', 'git-data-plane']
|
|
180
|
+
},
|
|
181
|
+
dataPlane: {
|
|
182
|
+
role: 'git-data-plane',
|
|
183
|
+
scope: 'Repository streaming, SSH hosting, object storage, search indexing, and warm receive-pack paths',
|
|
184
|
+
boundary: process.env.KRATE_GITEA_HTTP_URL || 'repository service not configured'
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export function repositoryForgeSummary(resource, namespace = 'krate-system') {
|
|
191
|
+
const metadata = resource?.metadata || {};
|
|
192
|
+
const spec = resource?.spec || {};
|
|
193
|
+
const name = metadata.name || 'unknown-repository';
|
|
194
|
+
const repositoryNamespace = metadata.namespace || namespace;
|
|
195
|
+
const org = spec.organizationRef || metadata.labels?.['krate.a5c.ai/org'] || 'default';
|
|
196
|
+
const repoPath = `/orgs/${encodeURIComponent(org)}/repositories/${encodeURIComponent(name)}`;
|
|
197
|
+
return {
|
|
198
|
+
kind: 'Repository',
|
|
199
|
+
name,
|
|
200
|
+
org,
|
|
201
|
+
namespace: repositoryNamespace,
|
|
202
|
+
visibility: spec.visibility || 'internal',
|
|
203
|
+
defaultBranch: spec.defaultBranch || 'main',
|
|
204
|
+
phase: resource?.status?.phase || (resource ? 'Ready' : 'Unknown'),
|
|
205
|
+
href: `${repoPath}/code`,
|
|
206
|
+
cloneUrl: spec.gitHosting?.httpUrl || `<krate-repository-service>/${encodeURIComponent(org)}/${name}.git`,
|
|
207
|
+
actions: {
|
|
208
|
+
code: `${repoPath}/code`,
|
|
209
|
+
pullRequests: `${repoPath}/pull-requests`,
|
|
210
|
+
issues: `${repoPath}/issues`,
|
|
211
|
+
runs: `${repoPath}/runs`,
|
|
212
|
+
pipelines: `${repoPath}/runs`,
|
|
213
|
+
hooks: `${repoPath}/hooks`,
|
|
214
|
+
settings: `${repoPath}/settings`,
|
|
215
|
+
yaml: `/orgs/${encodeURIComponent(org)}/advanced-plans?kind=Repository&name=${encodeURIComponent(name)}`
|
|
216
|
+
},
|
|
217
|
+
kubectl: {
|
|
218
|
+
get: `kubectl get repositories.krate.a5c.ai ${name} -n ${repositoryNamespace} -o yaml`,
|
|
219
|
+
delete: `kubectl delete repositories.krate.a5c.ai ${name} -n ${repositoryNamespace}`
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export function repositoryForgeView(resource, namespace = 'default') {
|
|
225
|
+
const summary = repositoryForgeSummary(resource, namespace);
|
|
226
|
+
return {
|
|
227
|
+
...summary,
|
|
228
|
+
primaryFlow: 'browse-code-open-pr-review-merge',
|
|
229
|
+
emptyState: resource ? null : 'Repository resource is not available from the Kubernetes resource gateway.',
|
|
230
|
+
sections: [
|
|
231
|
+
{ id: 'code', label: 'Code', href: summary.actions.code, state: 'branch-and-path-aware' },
|
|
232
|
+
{ id: 'pull-requests', label: 'Pull requests', href: summary.actions.pullRequests, state: 'review-merge-checks' },
|
|
233
|
+
{ id: 'issues', label: 'Issues', href: summary.actions.issues, state: 'triage-policy-aware' },
|
|
234
|
+
{ id: 'runs', label: 'Runs', href: summary.actions.runs, state: 'runner-and-job-aware' },
|
|
235
|
+
{ id: 'hooks', label: 'Hooks', href: summary.actions.hooks, state: 'delivery-replay-aware' },
|
|
236
|
+
{ id: 'settings', label: 'Settings', href: summary.actions.settings, state: 'branch-protection-rbac-danger-actions' }
|
|
237
|
+
]
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function normalizeResourceList(result) {
|
|
242
|
+
if (Array.isArray(result)) return result;
|
|
243
|
+
if (Array.isArray(result?.items)) return result.items;
|
|
244
|
+
if (Array.isArray(result?.resources)) return result.resources;
|
|
245
|
+
if (result?.resource) return [result.resource];
|
|
246
|
+
return [];
|
|
247
|
+
}
|
|
248
|
+
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export function createArgoCdApplication({
|
|
2
|
+
name = 'krate',
|
|
3
|
+
namespace = 'argocd',
|
|
4
|
+
project = 'default',
|
|
5
|
+
repoURL,
|
|
6
|
+
path = 'charts/krate',
|
|
7
|
+
targetRevision = 'HEAD',
|
|
8
|
+
destinationNamespace = 'krate-system',
|
|
9
|
+
destinationServer = 'https://kubernetes.default.svc',
|
|
10
|
+
automated = true
|
|
11
|
+
} = {}) {
|
|
12
|
+
if (!repoURL) throw new Error('Argo CD Application requires repoURL');
|
|
13
|
+
return {
|
|
14
|
+
apiVersion: 'argoproj.io/v1alpha1',
|
|
15
|
+
kind: 'Application',
|
|
16
|
+
metadata: {
|
|
17
|
+
name,
|
|
18
|
+
namespace,
|
|
19
|
+
labels: {
|
|
20
|
+
'app.kubernetes.io/part-of': 'krate',
|
|
21
|
+
'krate.a5c.ai/gitops-engine': 'argocd'
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
spec: {
|
|
25
|
+
project,
|
|
26
|
+
source: { repoURL, targetRevision, path },
|
|
27
|
+
destination: { server: destinationServer, namespace: destinationNamespace },
|
|
28
|
+
syncPolicy: automated ? {
|
|
29
|
+
automated: { prune: true, selfHeal: true },
|
|
30
|
+
syncOptions: ['CreateNamespace=true']
|
|
31
|
+
} : { syncOptions: ['CreateNamespace=true'] }
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function createKrateGitOpsPlan({ repoURL, namespace = 'krate-system', applicationName = 'krate' }) {
|
|
37
|
+
return {
|
|
38
|
+
engine: 'argocd',
|
|
39
|
+
application: createArgoCdApplication({ name: applicationName, repoURL, destinationNamespace: namespace }),
|
|
40
|
+
requiredClusterResources: ['Application.argoproj.io', 'Namespace', 'ServiceAccount', 'RBAC', 'APIService', 'Krate CRDs'],
|
|
41
|
+
syncGuarantees: ['automated prune', 'automated selfHeal', 'namespace creation']
|
|
42
|
+
};
|
|
43
|
+
}
|