@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.
- 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 +3067 -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/gaps-agent-mux-to-krate-crds.md +298 -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/todos.md +4 -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 +236 -0
- package/src/agent-adapter-controller.js +169 -0
- package/src/agent-approval-controller.js +170 -0
- package/src/agent-context-bundles.js +242 -0
- package/src/agent-dispatch-controller.js +209 -0
- package/src/agent-gateway-config-controller.js +147 -0
- package/src/agent-memory-controller.js +357 -0
- package/src/agent-memory-import.js +327 -0
- package/src/agent-memory-query.js +292 -0
- package/src/agent-memory-repository-source-controller.js +255 -0
- package/src/agent-mux-client.js +280 -0
- package/src/agent-permission-review.js +250 -0
- package/src/agent-project-controller.js +117 -0
- package/src/agent-provider-config-controller.js +150 -0
- package/src/agent-secret-config-grant-controller.js +282 -0
- package/src/agent-session-transcript-controller.js +189 -0
- package/src/agent-stack-controller.js +347 -0
- package/src/agent-subagent-controller.js +160 -0
- package/src/agent-transport-binding-controller.js +121 -0
- package/src/agent-trigger-controller.js +321 -0
- package/src/agent-workspace-controller.js +447 -0
- package/src/agent-writeback-controller.js +302 -0
- package/src/api-controller.js +541 -0
- package/src/argocd-gitops.js +43 -0
- package/src/async-controller.js +207 -0
- package/src/audit-controller.js +191 -0
- package/src/auth.js +307 -0
- package/src/component-catalog.js +41 -0
- package/src/control-plane.js +136 -0
- package/src/controller-client.js +50 -0
- package/src/controller-ui.js +551 -0
- package/src/data-plane.js +178 -0
- package/src/event-bus.js +61 -0
- package/src/external/conflict-controller.js +225 -0
- package/src/external/github/auth.js +96 -0
- package/src/external/github/cicd.js +180 -0
- package/src/external/github/git-forge.js +240 -0
- package/src/external/github/index.js +144 -0
- package/src/external/github/issue-tracking.js +163 -0
- package/src/external/provider-adapter.js +161 -0
- package/src/external/provider-resource-factory.js +161 -0
- package/src/external/sync-controller.js +235 -0
- package/src/external/webhook-controller.js +144 -0
- package/src/external/write-controller.js +283 -0
- package/src/gitea-backend.js +95 -0
- package/src/gitea-service.js +173 -0
- package/src/handoff.js +98 -0
- package/src/hooks-events.js +63 -0
- package/src/http-server.js +377 -0
- package/src/identity-policy.js +86 -0
- package/src/index.js +55 -0
- package/src/kubernetes-controller-async.js +511 -0
- package/src/kubernetes-controller.js +878 -0
- package/src/kubernetes-resource-gateway.js +48 -0
- package/src/operations.js +112 -0
- package/src/org-scoping.js +5 -0
- package/src/resource-model.js +221 -0
- package/src/runners-ci.js +48 -0
- package/src/runtime.js +196 -0
- package/src/snapshot-cache.js +157 -0
- package/src/web-ui.js +40 -0
- package/tests/agent-adapter-controller.test.js +361 -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 +315 -0
- package/tests/agent-gateway-config-controller.test.js +386 -0
- package/tests/agent-memory-controller.test.js +308 -0
- package/tests/agent-memory-import-snapshot.test.js +477 -0
- package/tests/agent-memory-query.test.js +404 -0
- package/tests/agent-memory-repository-source.test.js +514 -0
- package/tests/agent-mux-client.test.js +204 -0
- package/tests/agent-permission-review-v2.test.js +317 -0
- package/tests/agent-permission-review.test.js +209 -0
- package/tests/agent-project-controller.test.js +302 -0
- package/tests/agent-provider-config-controller.test.js +376 -0
- package/tests/agent-resources.test.js +228 -0
- package/tests/agent-secret-config-grant.test.js +231 -0
- package/tests/agent-session-transcript-controller.test.js +499 -0
- package/tests/agent-stack-controller.test.js +221 -0
- package/tests/agent-subagent-controller.test.js +201 -0
- package/tests/agent-transport-binding-controller.test.js +294 -0
- package/tests/agent-trigger-controller.test.js +211 -0
- package/tests/agent-trigger-routes.test.js +190 -0
- package/tests/agent-trigger-sources.test.js +245 -0
- package/tests/agent-workspace-controller.test.js +181 -0
- package/tests/agent-writeback.test.js +292 -0
- package/tests/approval-persistence.test.js +171 -0
- package/tests/async-controller.test.js +252 -0
- package/tests/audit-controller.test.js +227 -0
- package/tests/deployment.test.js +396 -0
- package/tests/e2e/lifecycle.test.js +117 -0
- package/tests/external-github-forge.test.js +560 -0
- package/tests/external-github-issues-cicd.test.js +520 -0
- package/tests/external-integration.test.js +470 -0
- package/tests/external-persistence.test.js +340 -0
- package/tests/external-provider-adapter.test.js +365 -0
- package/tests/external-resource-model.test.js +215 -0
- package/tests/external-webhook-sync.test.js +287 -0
- package/tests/external-write-conflict.test.js +353 -0
- package/tests/gitea-service.test.js +253 -0
- package/tests/health-check-real.test.js +165 -0
- package/tests/integration/full-flow.test.js +266 -0
- package/tests/krate.test.js +727 -0
- package/tests/memory-search-wiring.test.js +270 -0
- package/tests/org-scoping.test.js +687 -0
- package/tests/session-cookie-hmac.test.js +151 -0
- package/tests/snapshot-performance.test.js +247 -0
- package/tests/sse-events.test.js +107 -0
- package/tests/workspace-volumes.test.js +312 -0
- package/tests/writeback-persistence.test.js +207 -0
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
import { createServer } from 'node:http';
|
|
2
|
+
import { createKrateRuntime } from './runtime.js';
|
|
3
|
+
import { createControllerUiModel } from './controller-ui.js';
|
|
4
|
+
import { createKrateApiController } from './api-controller.js';
|
|
5
|
+
import { createKubernetesResourceGateway } from './kubernetes-resource-gateway.js';
|
|
6
|
+
import { orgNamespaceName } from './kubernetes-controller.js';
|
|
7
|
+
import { globalEventBus } from './event-bus.js';
|
|
8
|
+
|
|
9
|
+
const jsonHeaders = { 'content-type': 'application/json; charset=utf-8' };
|
|
10
|
+
|
|
11
|
+
export function createKrateHttpHandler({ runtime = createKrateRuntime(), controller = createKrateApiController({ resourceGateway: createKubernetesResourceGateway() }) } = {}) {
|
|
12
|
+
return async function handleKrateRequest(request, response) {
|
|
13
|
+
try {
|
|
14
|
+
const url = new URL(request.url || '/', 'http://localhost');
|
|
15
|
+
if (request.method === 'GET' && url.pathname === '/healthz') return send(response, 200, { ok: true, project: 'Krate' });
|
|
16
|
+
if (request.method === 'GET' && url.pathname === '/api/controller') return send(response, 200, createControllerUiModel(await controller.snapshot(), { organization: url.searchParams.get('org') || undefined }));
|
|
17
|
+
if (url.pathname === '/api/orgs') {
|
|
18
|
+
if (request.method === 'GET') return send(response, 200, { organizations: createControllerUiModel(await controller.snapshot()).orgs });
|
|
19
|
+
if (request.method === 'POST') return send(response, 201, await controller.createOrganization(await readJson(request)));
|
|
20
|
+
}
|
|
21
|
+
const orgResourceCollectionMatch = url.pathname.match(/^\/api\/orgs\/([^/]+)\/resources$/);
|
|
22
|
+
if (orgResourceCollectionMatch) {
|
|
23
|
+
const org = orgResourceCollectionMatch[1];
|
|
24
|
+
const scopedController = createKrateApiController({ namespace: orgNamespaceName(org) });
|
|
25
|
+
if (request.method === 'GET') return send(response, 200, await scopedController.listResource(url.searchParams.get('kind') || 'Repository'));
|
|
26
|
+
if (request.method === 'POST') return send(response, 201, await scopedController.applyResource(scopeResource(await readJson(request), org)));
|
|
27
|
+
}
|
|
28
|
+
const orgResourceMatch = url.pathname.match(/^\/api\/orgs\/([^/]+)\/resources\/([^/]+)\/([^/]+)$/);
|
|
29
|
+
if (orgResourceMatch) {
|
|
30
|
+
const scopedController = createKrateApiController({ namespace: orgNamespaceName(orgResourceMatch[1]) });
|
|
31
|
+
if (request.method === 'GET') return send(response, 200, await scopedController.getResource(orgResourceMatch[2], orgResourceMatch[3]));
|
|
32
|
+
if (request.method === 'DELETE') return send(response, 200, await scopedController.deleteResource(orgResourceMatch[2], orgResourceMatch[3]));
|
|
33
|
+
}
|
|
34
|
+
const orgRepositoryCollectionMatch = url.pathname.match(/^\/api\/orgs\/([^/]+)\/repositories$/);
|
|
35
|
+
if (orgRepositoryCollectionMatch) {
|
|
36
|
+
const org = orgRepositoryCollectionMatch[1];
|
|
37
|
+
const scopedController = createKrateApiController({ namespace: orgNamespaceName(org) });
|
|
38
|
+
if (request.method === 'GET') return send(response, 200, await scopedController.listResource('Repository'));
|
|
39
|
+
if (request.method === 'POST') return send(response, 201, await scopedController.createRepository({ ...(await readJson(request)), organizationRef: org }));
|
|
40
|
+
}
|
|
41
|
+
const orgRepositoryMatch = url.pathname.match(/^\/api\/orgs\/([^/]+)\/repositories\/([^/]+)$/);
|
|
42
|
+
if (orgRepositoryMatch) {
|
|
43
|
+
const scopedController = createKrateApiController({ namespace: orgNamespaceName(orgRepositoryMatch[1]) });
|
|
44
|
+
if (request.method === 'GET') return send(response, 200, await scopedController.getResource('Repository', orgRepositoryMatch[2]));
|
|
45
|
+
if (request.method === 'DELETE') return send(response, 200, await scopedController.deleteResource('Repository', orgRepositoryMatch[2]));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const orgSnapshotMatch = url.pathname.match(/^\/api\/orgs\/([^/]+)\/snapshot$/);
|
|
49
|
+
if (orgSnapshotMatch) {
|
|
50
|
+
ensureRuntimeOrg(runtime, orgSnapshotMatch[1]);
|
|
51
|
+
if (request.method === 'GET') return send(response, 200, runtime.snapshot());
|
|
52
|
+
if (request.method === 'POST') return send(response, 200, runtime.importSnapshot(await readJson(request)));
|
|
53
|
+
}
|
|
54
|
+
const runtimeResourceMatch = url.pathname.match(/^\/api\/orgs\/([^/]+)\/runtime-resources\/([^/]+)$/);
|
|
55
|
+
if (request.method === 'GET' && runtimeResourceMatch) {
|
|
56
|
+
ensureRuntimeOrg(runtime, runtimeResourceMatch[1]);
|
|
57
|
+
return send(response, 200, runtime.controlPlane.list(runtimeResourceMatch[2], { namespace: orgNamespaceName(runtimeResourceMatch[1]) }));
|
|
58
|
+
}
|
|
59
|
+
const objectMatch = url.pathname.match(/^\/api\/orgs\/([^/]+)\/repositories\/([^/]+)\/objects$/);
|
|
60
|
+
if (request.method === 'POST' && objectMatch) {
|
|
61
|
+
ensureRuntimeOrg(runtime, objectMatch[1]);
|
|
62
|
+
ensureRepository(runtime, objectMatch[2]);
|
|
63
|
+
return send(response, 201, runtime.git.recordObject({ organizationRef: objectMatch[1], repository: objectMatch[2], namespace: orgNamespaceName(objectMatch[1]), ...(await readJson(request)) }));
|
|
64
|
+
}
|
|
65
|
+
const searchIndexMatch = url.pathname.match(/^\/api\/orgs\/([^/]+)\/repositories\/([^/]+)\/search-index$/);
|
|
66
|
+
if (request.method === 'POST' && searchIndexMatch) {
|
|
67
|
+
ensureRuntimeOrg(runtime, searchIndexMatch[1]);
|
|
68
|
+
ensureRepository(runtime, searchIndexMatch[2]);
|
|
69
|
+
return send(response, 202, runtime.git.enqueueSearchIndex({ organizationRef: searchIndexMatch[1], repository: searchIndexMatch[2], namespace: orgNamespaceName(searchIndexMatch[1]), ...(await readJson(request)) }));
|
|
70
|
+
}
|
|
71
|
+
const pullRequestCollectionMatch = url.pathname.match(/^\/api\/orgs\/([^/]+)\/pullrequests$/);
|
|
72
|
+
if (request.method === 'POST' && pullRequestCollectionMatch) {
|
|
73
|
+
ensureRuntimeOrg(runtime, pullRequestCollectionMatch[1]);
|
|
74
|
+
return send(response, 201, runtime.createPullRequest({ ...(await readJson(request)), organizationRef: pullRequestCollectionMatch[1] }));
|
|
75
|
+
}
|
|
76
|
+
const reviewMatch = url.pathname.match(/^\/api\/orgs\/([^/]+)\/pullrequests\/([^/]+)\/reviews$/);
|
|
77
|
+
if (request.method === 'POST' && reviewMatch) {
|
|
78
|
+
ensureRuntimeOrg(runtime, reviewMatch[1]);
|
|
79
|
+
return send(response, 201, runtime.addReview({ ...(await readJson(request)), pullRequest: reviewMatch[2] }));
|
|
80
|
+
}
|
|
81
|
+
const completeMatch = url.pathname.match(/^\/api\/orgs\/([^/]+)\/pullrequests\/([^/]+)\/checks\/complete$/);
|
|
82
|
+
if (request.method === 'POST' && completeMatch) {
|
|
83
|
+
ensureRuntimeOrg(runtime, completeMatch[1]);
|
|
84
|
+
return send(response, 200, runtime.completePipeline({ pipeline: `pipeline-${completeMatch[2]}`, ...(await readJson(request)) }));
|
|
85
|
+
}
|
|
86
|
+
const mergeMatch = url.pathname.match(/^\/api\/orgs\/([^/]+)\/pullrequests\/([^/]+)\/merge$/);
|
|
87
|
+
if (request.method === 'POST' && mergeMatch) {
|
|
88
|
+
ensureRuntimeOrg(runtime, mergeMatch[1]);
|
|
89
|
+
return send(response, 200, runtime.mergePullRequest({ ...(await readJson(request)), pullRequest: mergeMatch[2] }));
|
|
90
|
+
}
|
|
91
|
+
const agentApprovalDecideMatch = url.pathname.match(/^\/api\/orgs\/([^/]+)\/agents\/approvals\/([^/]+)\/decide$/);
|
|
92
|
+
if (request.method === 'POST' && agentApprovalDecideMatch) {
|
|
93
|
+
const org = agentApprovalDecideMatch[1];
|
|
94
|
+
const approvalName = agentApprovalDecideMatch[2];
|
|
95
|
+
const body = await readJson(request);
|
|
96
|
+
const scopedController = createKrateApiController({ namespace: orgNamespaceName(org) });
|
|
97
|
+
const input = { approvalName, decidedBy: body.decidedBy || 'unknown', reason: body.reason || '' };
|
|
98
|
+
const result = body.decision === 'approve'
|
|
99
|
+
? await scopedController.approveAgentAction(input)
|
|
100
|
+
: await scopedController.denyAgentAction(input);
|
|
101
|
+
return send(response, result.error ? 400 : 200, result);
|
|
102
|
+
}
|
|
103
|
+
// Agent webhook ingress
|
|
104
|
+
const webhookIngestMatch = url.pathname.match(/^\/api\/orgs\/([^/]+)\/agents\/webhooks\/ingest$/);
|
|
105
|
+
if (request.method === 'POST' && webhookIngestMatch) {
|
|
106
|
+
const org = webhookIngestMatch[1];
|
|
107
|
+
const body = await readJson(request);
|
|
108
|
+
const event = normalizeWebhookEvent(body, org);
|
|
109
|
+
const scopedController = createKrateApiController({ namespace: orgNamespaceName(org) });
|
|
110
|
+
const result = await scopedController.processWebhookEvent({ event, organizationRef: org, namespace: orgNamespaceName(org) });
|
|
111
|
+
return send(response, 200, result);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Pipeline failure event
|
|
115
|
+
const pipelineFailMatch = url.pathname.match(/^\/api\/orgs\/([^/]+)\/agents\/events\/pipeline-failure$/);
|
|
116
|
+
if (request.method === 'POST' && pipelineFailMatch) {
|
|
117
|
+
const org = pipelineFailMatch[1];
|
|
118
|
+
const body = await readJson(request);
|
|
119
|
+
const event = { type: 'ci-failure', source: { kind: 'Pipeline', name: body.name || 'unknown', namespace: body.namespace }, repository: body.repository || '', ref: body.ref || 'main', actor: body.actor || 'system', payload: body };
|
|
120
|
+
const scopedController = createKrateApiController({ namespace: orgNamespaceName(org) });
|
|
121
|
+
const result = await scopedController.processWebhookEvent({ event, organizationRef: org, namespace: orgNamespaceName(org) });
|
|
122
|
+
return send(response, 200, result);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Comment event
|
|
126
|
+
const commentMatch = url.pathname.match(/^\/api\/orgs\/([^/]+)\/agents\/events\/comment$/);
|
|
127
|
+
if (request.method === 'POST' && commentMatch) {
|
|
128
|
+
const org = commentMatch[1];
|
|
129
|
+
const body = await readJson(request);
|
|
130
|
+
const event = { type: 'comment', source: { kind: body.kind || 'Issue', name: body.name || 'unknown' }, repository: body.repository || '', ref: body.ref || 'main', actor: body.actor || 'system', payload: { body: body.body || '' } };
|
|
131
|
+
const scopedController = createKrateApiController({ namespace: orgNamespaceName(org) });
|
|
132
|
+
const result = await scopedController.processWebhookEvent({ event, organizationRef: org, namespace: orgNamespaceName(org) });
|
|
133
|
+
return send(response, 200, result);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Label event
|
|
137
|
+
const labelMatch = url.pathname.match(/^\/api\/orgs\/([^/]+)\/agents\/events\/label$/);
|
|
138
|
+
if (request.method === 'POST' && labelMatch) {
|
|
139
|
+
const org = labelMatch[1];
|
|
140
|
+
const body = await readJson(request);
|
|
141
|
+
const event = { type: 'label-added', source: { kind: body.kind || 'Issue', name: body.name || 'unknown' }, repository: body.repository || '', ref: body.ref || 'main', actor: body.actor || 'system', payload: { label: body.label || '' } };
|
|
142
|
+
const scopedController = createKrateApiController({ namespace: orgNamespaceName(org) });
|
|
143
|
+
const result = await scopedController.processWebhookEvent({ event, organizationRef: org, namespace: orgNamespaceName(org) });
|
|
144
|
+
return send(response, 200, result);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const agentTriggerProcessMatch = url.pathname.match(/^\/api\/orgs\/([^/]+)\/agents\/triggers\/process$/);
|
|
148
|
+
if (request.method === 'POST' && agentTriggerProcessMatch) {
|
|
149
|
+
const org = agentTriggerProcessMatch[1];
|
|
150
|
+
const scopedController = createKrateApiController({ namespace: orgNamespaceName(org) });
|
|
151
|
+
const body = await readJson(request);
|
|
152
|
+
const result = await scopedController.processWebhookEvent({ ...body, organizationRef: org });
|
|
153
|
+
return send(response, 200, result);
|
|
154
|
+
}
|
|
155
|
+
const memoryQueryMatch = url.pathname.match(/^\/api\/orgs\/([^/]+)\/agents\/memory\/query$/);
|
|
156
|
+
if (request.method === 'POST' && memoryQueryMatch) {
|
|
157
|
+
const org = memoryQueryMatch[1];
|
|
158
|
+
const body = await readJson(request);
|
|
159
|
+
const scopedController = createKrateApiController({ namespace: orgNamespaceName(org) });
|
|
160
|
+
const result = await scopedController.queryAgentMemory({ ...body, organizationRef: org });
|
|
161
|
+
return send(response, 200, result);
|
|
162
|
+
}
|
|
163
|
+
// --- Secrets management routes ---
|
|
164
|
+
const orgSecretsMatch = url.pathname.match(/^\/api\/orgs\/([^/]+)\/secrets$/);
|
|
165
|
+
if (orgSecretsMatch) {
|
|
166
|
+
const org = orgSecretsMatch[1];
|
|
167
|
+
const scopedController = createKrateApiController({ namespace: orgNamespaceName(org) });
|
|
168
|
+
if (request.method === 'GET') {
|
|
169
|
+
const result = await scopedController.listResourceForOrg(org, 'AgentSecretGrant');
|
|
170
|
+
const items = Array.isArray(result?.items) ? result.items : [];
|
|
171
|
+
return send(response, 200, {
|
|
172
|
+
secrets: items.map((item) => ({
|
|
173
|
+
name: item.spec?.secretName || item.spec?.secretRef || item.metadata?.name,
|
|
174
|
+
type: item.spec?.type || 'Opaque',
|
|
175
|
+
createdAt: item.status?.createdAt || item.metadata?.creationTimestamp || null,
|
|
176
|
+
namespace: orgNamespaceName(org),
|
|
177
|
+
grants: []
|
|
178
|
+
}))
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
if (request.method === 'POST') {
|
|
182
|
+
const body = await readJson(request);
|
|
183
|
+
const secretResource = {
|
|
184
|
+
apiVersion: 'krate.a5c.ai/v1alpha1',
|
|
185
|
+
kind: 'AgentSecretGrant',
|
|
186
|
+
metadata: { name: body.name, namespace: orgNamespaceName(org) },
|
|
187
|
+
spec: {
|
|
188
|
+
organizationRef: org,
|
|
189
|
+
secretName: body.name,
|
|
190
|
+
secretRef: body.name,
|
|
191
|
+
grantedTo: body.grantedTo || 'system',
|
|
192
|
+
subject: body.grantedTo || 'system',
|
|
193
|
+
permissions: body.permissions || ['read'],
|
|
194
|
+
purpose: 'read',
|
|
195
|
+
data: body.data || {}
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
const result = await scopedController.applyResourceForOrg(org, secretResource);
|
|
199
|
+
return send(response, 201, result);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const orgSecretMatch = url.pathname.match(/^\/api\/orgs\/([^/]+)\/secrets\/([^/]+)$/);
|
|
204
|
+
if (orgSecretMatch) {
|
|
205
|
+
const org = orgSecretMatch[1];
|
|
206
|
+
const secretName = orgSecretMatch[2];
|
|
207
|
+
const scopedController = createKrateApiController({ namespace: orgNamespaceName(org) });
|
|
208
|
+
if (request.method === 'DELETE') {
|
|
209
|
+
const result = await scopedController.deleteResourceForOrg(org, 'AgentSecretGrant', secretName);
|
|
210
|
+
return send(response, 200, result);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const orgSecretGrantsMatch = url.pathname.match(/^\/api\/orgs\/([^/]+)\/secret-grants$/);
|
|
215
|
+
if (orgSecretGrantsMatch) {
|
|
216
|
+
const org = orgSecretGrantsMatch[1];
|
|
217
|
+
const scopedController = createKrateApiController({ namespace: orgNamespaceName(org) });
|
|
218
|
+
if (request.method === 'GET') {
|
|
219
|
+
return send(response, 200, await scopedController.listResourceForOrg(org, 'AgentSecretGrant'));
|
|
220
|
+
}
|
|
221
|
+
if (request.method === 'POST') {
|
|
222
|
+
const body = await readJson(request);
|
|
223
|
+
const grant = {
|
|
224
|
+
apiVersion: 'krate.a5c.ai/v1alpha1',
|
|
225
|
+
kind: 'AgentSecretGrant',
|
|
226
|
+
metadata: { name: body.name || `grant-${Date.now()}`, namespace: orgNamespaceName(org) },
|
|
227
|
+
spec: {
|
|
228
|
+
organizationRef: org,
|
|
229
|
+
secretName: body.secretName,
|
|
230
|
+
secretRef: body.secretName,
|
|
231
|
+
grantedTo: body.grantedTo,
|
|
232
|
+
subject: body.grantedTo,
|
|
233
|
+
permissions: body.permissions || ['read'],
|
|
234
|
+
purpose: (body.permissions || ['read']).join(',')
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
return send(response, 201, await scopedController.applyResourceForOrg(org, grant));
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// --- External integration routes ---
|
|
242
|
+
const externalSyncMatch = url.pathname.match(/^\/api\/orgs\/([^/]+)\/external\/sync$/);
|
|
243
|
+
if (request.method === 'POST' && externalSyncMatch) {
|
|
244
|
+
const org = externalSyncMatch[1];
|
|
245
|
+
const body = await readJson(request);
|
|
246
|
+
const scopedController = createKrateApiController({ namespace: orgNamespaceName(org) });
|
|
247
|
+
const bindingName = body.bindingName || '';
|
|
248
|
+
const result = await scopedController.syncExternalBinding(bindingName, {
|
|
249
|
+
kind: body.kind,
|
|
250
|
+
localName: body.localName,
|
|
251
|
+
namespace: orgNamespaceName(org),
|
|
252
|
+
spec: body.spec || {},
|
|
253
|
+
externalEnvelope: body.externalEnvelope || { nativeId: '', url: '', etag: '', providerRef: '' },
|
|
254
|
+
watermark: body.watermark
|
|
255
|
+
});
|
|
256
|
+
return send(response, 200, result);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const externalConflictResolveMatch = url.pathname.match(/^\/api\/orgs\/([^/]+)\/external\/conflicts\/([^/]+)\/resolve$/);
|
|
260
|
+
if (request.method === 'POST' && externalConflictResolveMatch) {
|
|
261
|
+
const org = externalConflictResolveMatch[1];
|
|
262
|
+
const conflictName = externalConflictResolveMatch[2];
|
|
263
|
+
const body = await readJson(request);
|
|
264
|
+
const scopedController = createKrateApiController({ namespace: orgNamespaceName(org) });
|
|
265
|
+
const result = await scopedController.resolveExternalConflict({ conflictName, strategy: body.strategy, resolvedValue: body.resolvedValue, resources: body.resources || {} });
|
|
266
|
+
return send(response, 200, result);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const externalWriteIntentApproveMatch = url.pathname.match(/^\/api\/orgs\/([^/]+)\/external\/write-intents\/([^/]+)\/approve$/);
|
|
270
|
+
if (request.method === 'POST' && externalWriteIntentApproveMatch) {
|
|
271
|
+
const org = externalWriteIntentApproveMatch[1];
|
|
272
|
+
const intentName = externalWriteIntentApproveMatch[2];
|
|
273
|
+
const body = await readJson(request);
|
|
274
|
+
const scopedController = createKrateApiController({ namespace: orgNamespaceName(org) });
|
|
275
|
+
const result = await scopedController.approveExternalWriteIntent({ intentName, approvedBy: body.approvedBy || 'unknown', resources: body.resources || {} });
|
|
276
|
+
return send(response, 200, result);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const externalWriteIntentCancelMatch = url.pathname.match(/^\/api\/orgs\/([^/]+)\/external\/write-intents\/([^/]+)\/cancel$/);
|
|
280
|
+
if (request.method === 'POST' && externalWriteIntentCancelMatch) {
|
|
281
|
+
const org = externalWriteIntentCancelMatch[1];
|
|
282
|
+
const intentName = externalWriteIntentCancelMatch[2];
|
|
283
|
+
const body = await readJson(request);
|
|
284
|
+
const scopedController = createKrateApiController({ namespace: orgNamespaceName(org) });
|
|
285
|
+
const result = await scopedController.cancelExternalWriteIntent({ intentName, cancelledBy: body.cancelledBy || 'unknown', resources: body.resources || {} });
|
|
286
|
+
return send(response, 200, result);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const sseMatch = url.pathname.match(/^\/api\/orgs\/([^/]+)\/agents\/events\/stream$/);
|
|
290
|
+
if (request.method === 'GET' && sseMatch) {
|
|
291
|
+
response.writeHead(200, {
|
|
292
|
+
'Content-Type': 'text/event-stream',
|
|
293
|
+
'Cache-Control': 'no-cache',
|
|
294
|
+
'Connection': 'keep-alive',
|
|
295
|
+
'X-Accel-Buffering': 'no',
|
|
296
|
+
});
|
|
297
|
+
response.write('data: {"type":"connected"}\n\n');
|
|
298
|
+
const writer = (event) => {
|
|
299
|
+
response.write(`data: ${JSON.stringify(event)}\n\n`);
|
|
300
|
+
};
|
|
301
|
+
globalEventBus.subscribe(writer);
|
|
302
|
+
const interval = setInterval(() => {
|
|
303
|
+
response.write('data: {"type":"heartbeat"}\n\n');
|
|
304
|
+
}, 30000);
|
|
305
|
+
request.on('close', () => {
|
|
306
|
+
clearInterval(interval);
|
|
307
|
+
globalEventBus.unsubscribe(writer);
|
|
308
|
+
});
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
return send(response, 404, { error: 'not_found', method: request.method, path: url.pathname });
|
|
312
|
+
} catch (error) {
|
|
313
|
+
return send(response, 400, { error: 'bad_request', message: error.message });
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
export function createKrateHttpServer(options = {}) {
|
|
319
|
+
return createServer(createKrateHttpHandler(options));
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
function scopeResource(resource, org) {
|
|
324
|
+
const namespace = orgNamespaceName(org);
|
|
325
|
+
return {
|
|
326
|
+
...resource,
|
|
327
|
+
metadata: { ...(resource.metadata || {}), namespace, labels: { ...(resource.metadata?.labels || {}), 'krate.a5c.ai/org': org, 'krate.a5c.ai/namespace': namespace } },
|
|
328
|
+
spec: { ...(resource.spec || {}), organizationRef: org }
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function ensureRuntimeOrg(runtime, org) {
|
|
333
|
+
if (runtime.organizationRef !== org || runtime.namespace !== orgNamespaceName(org)) throw new Error(`Runtime is scoped to ${runtime.organizationRef}`);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function ensureRepository(runtime, repository) {
|
|
337
|
+
const existing = runtime.controlPlane.get('Repository', runtime.namespace, repository);
|
|
338
|
+
if (!existing) throw new Error(`Repository ${repository} not found`);
|
|
339
|
+
return existing;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
async function readJson(request) {
|
|
343
|
+
const chunks = [];
|
|
344
|
+
for await (const chunk of request) chunks.push(chunk);
|
|
345
|
+
const text = Buffer.concat(chunks).toString('utf8');
|
|
346
|
+
return text ? JSON.parse(text) : {};
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function send(response, status, body) {
|
|
350
|
+
response.writeHead(status, jsonHeaders);
|
|
351
|
+
response.end(JSON.stringify(body, null, 2));
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
export function normalizeWebhookEvent(body, org) {
|
|
355
|
+
// GitHub/Gitea webhook normalization
|
|
356
|
+
if (body.action === 'completed' && body.workflow_run?.conclusion === 'failure') {
|
|
357
|
+
return { type: 'ci-failure', source: { kind: 'Pipeline', name: body.workflow_run?.name || 'unknown' }, repository: body.repository?.full_name || '', ref: body.workflow_run?.head_branch || 'main', actor: body.sender?.login || 'system', payload: body };
|
|
358
|
+
}
|
|
359
|
+
if (body.action === 'opened' && body.pull_request) {
|
|
360
|
+
return { type: 'pr-opened', source: { kind: 'PullRequest', name: String(body.pull_request.number) }, repository: body.repository?.full_name || '', ref: body.pull_request?.head?.ref || 'main', actor: body.sender?.login || 'system', payload: body };
|
|
361
|
+
}
|
|
362
|
+
if (body.action === 'created' && body.comment) {
|
|
363
|
+
const kind = body.issue?.pull_request ? 'PullRequest' : 'Issue';
|
|
364
|
+
return { type: 'comment', source: { kind, name: String(body.issue?.number || 'unknown') }, repository: body.repository?.full_name || '', ref: 'main', actor: body.sender?.login || body.comment?.user?.login || 'system', payload: { body: body.comment?.body || '' } };
|
|
365
|
+
}
|
|
366
|
+
if (body.action === 'labeled') {
|
|
367
|
+
return { type: 'label-added', source: { kind: body.pull_request ? 'PullRequest' : 'Issue', name: String(body.issue?.number || body.pull_request?.number || 'unknown') }, repository: body.repository?.full_name || '', ref: 'main', actor: body.sender?.login || 'system', payload: { label: body.label?.name || '' } };
|
|
368
|
+
}
|
|
369
|
+
if (body.action === 'opened' && body.issue && !body.pull_request) {
|
|
370
|
+
return { type: 'issue-created', source: { kind: 'Issue', name: String(body.issue.number) }, repository: body.repository?.full_name || '', ref: 'main', actor: body.sender?.login || 'system', payload: body };
|
|
371
|
+
}
|
|
372
|
+
if (body.ref && body.commits) {
|
|
373
|
+
return { type: 'push', source: { kind: 'Repository', name: body.repository?.full_name || '' }, repository: body.repository?.full_name || '', ref: body.ref?.replace('refs/heads/', '') || 'main', actor: body.sender?.login || body.pusher?.name || 'system', payload: body };
|
|
374
|
+
}
|
|
375
|
+
// Generic fallback
|
|
376
|
+
return { type: 'webhook', source: { kind: 'WebhookDelivery', name: 'unknown' }, repository: body.repository?.full_name || '', ref: 'main', actor: body.sender?.login || 'system', payload: body };
|
|
377
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { clone } from './resource-model.js';
|
|
2
|
+
|
|
3
|
+
export function mapOidcIdentity({ subject, email, groups = [] }) {
|
|
4
|
+
if (!subject && !email) throw new Error('OIDC identity requires subject or email');
|
|
5
|
+
const name = email || subject;
|
|
6
|
+
return { name, uid: subject || email, groups: [...new Set(['system:authenticated', ...groups])], extra: { email } };
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export class RbacAuthorizer {
|
|
10
|
+
constructor(bindings = []) { this.bindings = bindings; }
|
|
11
|
+
allow(subject, rule) { this.bindings.push({ subject, rule }); return this; }
|
|
12
|
+
can(user, verb, kind, namespace = 'default') {
|
|
13
|
+
const subjects = new Set([user?.name, ...(user?.groups || [])]);
|
|
14
|
+
return this.bindings.some(({ subject, rule }) => {
|
|
15
|
+
if (!subjects.has(subject)) return false;
|
|
16
|
+
if (rule.namespace && rule.namespace !== namespace) return false;
|
|
17
|
+
const verbs = new Set(rule.verbs || []);
|
|
18
|
+
const kinds = new Set(rule.kinds || []);
|
|
19
|
+
return (verbs.has('*') || verbs.has(verb)) && (kinds.has('*') || kinds.has(kind));
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function defaultAuthorizer() {
|
|
25
|
+
return new RbacAuthorizer()
|
|
26
|
+
.allow('system:authenticated', { verbs: ['get', 'list', 'watch'], kinds: ['*'] })
|
|
27
|
+
.allow('krate:developers', { verbs: ['create', 'update'], kinds: ['PullRequest', 'Issue', 'Review', 'Pipeline', 'Job'] })
|
|
28
|
+
.allow('krate:repo-admins', { verbs: ['create', 'update', 'delete'], kinds: ['Organization', 'User', 'Team', 'Invite', 'IdentityMapping', 'AuthProvider', 'Repository', 'SSHKey', 'RepositoryPermission', 'BranchProtection', 'RefPolicy', 'WebhookSubscription', 'WebhookDelivery', 'View', 'Selector', 'PullRequest', 'Issue', 'Review', 'Pipeline', 'Job'] })
|
|
29
|
+
.allow('krate:platform-engineers', { verbs: ['*'], kinds: ['*'] });
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function createAdmissionPolicy({ name, mode = 'enforce', match, validate, message }) {
|
|
33
|
+
if (!name) throw new Error('admission policy requires name');
|
|
34
|
+
if (!['audit', 'enforce'].includes(mode)) throw new Error('mode must be audit or enforce');
|
|
35
|
+
return { name, mode, match, validate, message };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function evaluateAdmission(policies, request) {
|
|
39
|
+
const warnings = [];
|
|
40
|
+
const violations = [];
|
|
41
|
+
for (const policy of policies) {
|
|
42
|
+
if (policy.match && !policy.match(request)) continue;
|
|
43
|
+
const valid = policy.validate ? policy.validate(request) : true;
|
|
44
|
+
if (valid) continue;
|
|
45
|
+
const entry = { policy: policy.name, mode: policy.mode, message: policy.message || `admission policy ${policy.name} rejected request` };
|
|
46
|
+
if (policy.mode === 'audit') warnings.push(entry); else violations.push(entry);
|
|
47
|
+
}
|
|
48
|
+
return { allowed: violations.length === 0, warnings, violations };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function serviceAccountForJob({ namespace = 'default', repository, pipeline, trustTier = 'trusted' }) {
|
|
52
|
+
return {
|
|
53
|
+
name: `krate-job-${pipeline}`,
|
|
54
|
+
namespace,
|
|
55
|
+
groups: ['system:serviceaccounts', `system:serviceaccounts:${namespace}`, 'krate:ci-jobs'],
|
|
56
|
+
trustTier,
|
|
57
|
+
scopes: trustTier === 'untrusted'
|
|
58
|
+
? { repository, pipeline, secrets: false, clusterApi: false }
|
|
59
|
+
: { repository, pipeline, secrets: true, clusterApi: 'scoped' }
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function scalarToYaml(value) {
|
|
64
|
+
if (value === null || value === undefined) return 'null';
|
|
65
|
+
if (typeof value === 'number' || typeof value === 'boolean') return String(value);
|
|
66
|
+
if (typeof value === 'string' && /^[a-zA-Z0-9_.:/-]+$/.test(value)) return value;
|
|
67
|
+
return JSON.stringify(value);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function toResourceYaml(value, indent = 0) {
|
|
71
|
+
const spaces = ' '.repeat(indent);
|
|
72
|
+
if (Array.isArray(value)) {
|
|
73
|
+
if (value.length === 0) return '[]';
|
|
74
|
+
return value.map((item) => item && typeof item === 'object'
|
|
75
|
+
? `${spaces}- ${toResourceYaml(item, indent + 2).trimStart()}`
|
|
76
|
+
: `${spaces}- ${scalarToYaml(item)}`).join('\n');
|
|
77
|
+
}
|
|
78
|
+
if (value && typeof value === 'object') {
|
|
79
|
+
return Object.entries(clone(value)).map(([key, child]) => {
|
|
80
|
+
if (child && typeof child === 'object') return `${spaces}${key}:\n${toResourceYaml(child, indent + 2)}`;
|
|
81
|
+
return `${spaces}${key}: ${scalarToYaml(child)}`;
|
|
82
|
+
}).join('\n');
|
|
83
|
+
}
|
|
84
|
+
return `${spaces}${scalarToYaml(value)}`;
|
|
85
|
+
}
|
|
86
|
+
|
package/src/index.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export * from './resource-model.js';
|
|
2
|
+
export * from './identity-policy.js';
|
|
3
|
+
export * from './auth.js';
|
|
4
|
+
export * from './control-plane.js';
|
|
5
|
+
export * from './data-plane.js';
|
|
6
|
+
export * from './runners-ci.js';
|
|
7
|
+
export * from './hooks-events.js';
|
|
8
|
+
export * from './web-ui.js';
|
|
9
|
+
export * from './operations.js';
|
|
10
|
+
export * from './component-catalog.js';
|
|
11
|
+
|
|
12
|
+
export * from './handoff.js';
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
export * from './runtime.js';
|
|
16
|
+
export * from './http-server.js';
|
|
17
|
+
export * from './controller-ui.js';
|
|
18
|
+
export * from './controller-client.js';
|
|
19
|
+
export * from './api-controller.js';
|
|
20
|
+
export * from './kubernetes-controller.js';
|
|
21
|
+
export * from './kubernetes-resource-gateway.js';
|
|
22
|
+
export * from './gitea-backend.js';
|
|
23
|
+
export * from './gitea-service.js';
|
|
24
|
+
export * from './argocd-gitops.js';
|
|
25
|
+
export * from './agent-permission-review.js';
|
|
26
|
+
export * from './agent-stack-controller.js';
|
|
27
|
+
export * from './agent-context-bundles.js';
|
|
28
|
+
export * from './agent-mux-client.js';
|
|
29
|
+
export * from './agent-dispatch-controller.js';
|
|
30
|
+
export * from './agent-approval-controller.js';
|
|
31
|
+
export * from './agent-trigger-controller.js';
|
|
32
|
+
export * from './agent-workspace-controller.js';
|
|
33
|
+
export * from './agent-memory-controller.js';
|
|
34
|
+
export * from './agent-memory-query.js';
|
|
35
|
+
export * from './agent-memory-repository-source-controller.js';
|
|
36
|
+
export * from './agent-adapter-controller.js';
|
|
37
|
+
export * from './agent-transport-binding-controller.js';
|
|
38
|
+
export * from './agent-provider-config-controller.js';
|
|
39
|
+
export * from './agent-project-controller.js';
|
|
40
|
+
export * from './agent-gateway-config-controller.js';
|
|
41
|
+
export * from './agent-session-transcript-controller.js';
|
|
42
|
+
export * from './agent-writeback-controller.js';
|
|
43
|
+
export * from './agent-memory-import.js';
|
|
44
|
+
export * from './agent-subagent-controller.js';
|
|
45
|
+
export * from './external/provider-adapter.js';
|
|
46
|
+
export * from './external/provider-resource-factory.js';
|
|
47
|
+
export * from './external/github/index.js';
|
|
48
|
+
export * from './external/webhook-controller.js';
|
|
49
|
+
export * from './external/sync-controller.js';
|
|
50
|
+
export * from './external/write-controller.js';
|
|
51
|
+
export * from './external/conflict-controller.js';
|
|
52
|
+
export * from './audit-controller.js';
|
|
53
|
+
export * from './async-controller.js';
|
|
54
|
+
export * from './event-bus.js';
|
|
55
|
+
export * from './agent-secret-config-grant-controller.js';
|