@oscharko-dev/keiko-server 0.2.7 → 0.2.9
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/dist/.tsbuildinfo +1 -1
- package/dist/chat-handlers.d.ts +18 -2
- package/dist/chat-handlers.d.ts.map +1 -1
- package/dist/chat-handlers.js +185 -3
- package/dist/command-runner-errors.d.ts +17 -0
- package/dist/command-runner-errors.d.ts.map +1 -0
- package/dist/command-runner-errors.js +37 -0
- package/dist/command-runner-evidence.d.ts +23 -0
- package/dist/command-runner-evidence.d.ts.map +1 -0
- package/dist/command-runner-evidence.js +69 -0
- package/dist/command-runner-routes.d.ts +7 -0
- package/dist/command-runner-routes.d.ts.map +1 -0
- package/dist/command-runner-routes.js +175 -0
- package/dist/command-runner.d.ts +29 -0
- package/dist/command-runner.d.ts.map +1 -0
- package/dist/command-runner.js +348 -0
- package/dist/conversation-prompt.d.ts +2 -2
- package/dist/conversation-prompt.d.ts.map +1 -1
- package/dist/conversation-prompt.js +17 -1
- package/dist/csp.d.ts.map +1 -1
- package/dist/csp.js +3 -0
- package/dist/deps.d.ts +28 -1
- package/dist/deps.d.ts.map +1 -1
- package/dist/deps.js +288 -13
- package/dist/discussion-prompt.d.ts +4 -0
- package/dist/discussion-prompt.d.ts.map +1 -0
- package/dist/discussion-prompt.js +19 -0
- package/dist/editor/agentActionAudit.d.ts +18 -0
- package/dist/editor/agentActionAudit.d.ts.map +1 -0
- package/dist/editor/agentActionAudit.js +80 -0
- package/dist/editor/agentRoutes.d.ts +1 -0
- package/dist/editor/agentRoutes.d.ts.map +1 -1
- package/dist/editor/agentRoutes.js +292 -55
- package/dist/editor/agentSessionRegistry.d.ts +35 -0
- package/dist/editor/agentSessionRegistry.d.ts.map +1 -0
- package/dist/editor/agentSessionRegistry.js +243 -0
- package/dist/editor/completionRoutes.d.ts.map +1 -1
- package/dist/editor/completionRoutes.js +5 -10
- package/dist/editor/languageRoutes.d.ts +12 -1
- package/dist/editor/languageRoutes.d.ts.map +1 -1
- package/dist/editor/languageRoutes.js +71 -8
- package/dist/editor/languageService.d.ts +3 -2
- package/dist/editor/languageService.d.ts.map +1 -1
- package/dist/editor/languageService.js +41 -3
- package/dist/editor/languageServiceHost.d.ts.map +1 -1
- package/dist/editor/languageServiceHost.js +2 -2
- package/dist/editor/lsp/hostLanguageOperation.d.ts +17 -0
- package/dist/editor/lsp/hostLanguageOperation.d.ts.map +1 -0
- package/dist/editor/lsp/hostLanguageOperation.js +436 -0
- package/dist/editor/lsp/hostLanguageProviders.d.ts +26 -0
- package/dist/editor/lsp/hostLanguageProviders.d.ts.map +1 -0
- package/dist/editor/lsp/hostLanguageProviders.js +161 -0
- package/dist/editor/lsp/lspFrameCodec.d.ts +13 -0
- package/dist/editor/lsp/lspFrameCodec.d.ts.map +1 -0
- package/dist/editor/lsp/lspFrameCodec.js +164 -0
- package/dist/editor/lsp/lspJsonRpcClient.d.ts +34 -0
- package/dist/editor/lsp/lspJsonRpcClient.d.ts.map +1 -0
- package/dist/editor/lsp/lspJsonRpcClient.js +173 -0
- package/dist/editor/lsp/lspLanguageProvider.d.ts +7 -0
- package/dist/editor/lsp/lspLanguageProvider.d.ts.map +1 -0
- package/dist/editor/lsp/lspLanguageProvider.js +29 -0
- package/dist/editor/lsp/lspLifecycleLedger.d.ts +5 -0
- package/dist/editor/lsp/lspLifecycleLedger.d.ts.map +1 -0
- package/dist/editor/lsp/lspLifecycleLedger.js +37 -0
- package/dist/editor/lsp/lspNodeAdapter.d.ts +31 -0
- package/dist/editor/lsp/lspNodeAdapter.d.ts.map +1 -0
- package/dist/editor/lsp/lspNodeAdapter.js +230 -0
- package/dist/editor/lsp/lspProcessManager.d.ts +24 -0
- package/dist/editor/lsp/lspProcessManager.d.ts.map +1 -0
- package/dist/editor/lsp/lspProcessManager.js +255 -0
- package/dist/editor/lsp/lspRestartThrottle.d.ts +6 -0
- package/dist/editor/lsp/lspRestartThrottle.d.ts.map +1 -0
- package/dist/editor/lsp/lspRestartThrottle.js +24 -0
- package/dist/editor/lsp/lspStatusRoute.d.ts +8 -0
- package/dist/editor/lsp/lspStatusRoute.d.ts.map +1 -0
- package/dist/editor/lsp/lspStatusRoute.js +22 -0
- package/dist/editor/lsp/lspTransport.d.ts +19 -0
- package/dist/editor/lsp/lspTransport.d.ts.map +1 -0
- package/dist/editor/lsp/lspTransport.js +55 -0
- package/dist/editor/lsp/testing/fakeLspProcess.d.ts +23 -0
- package/dist/editor/lsp/testing/fakeLspProcess.d.ts.map +1 -0
- package/dist/editor/lsp/testing/fakeLspProcess.js +132 -0
- package/dist/files.d.ts +63 -0
- package/dist/files.d.ts.map +1 -1
- package/dist/files.js +799 -1
- package/dist/gateway-readiness.d.ts +6 -0
- package/dist/gateway-readiness.d.ts.map +1 -0
- package/dist/gateway-readiness.js +624 -0
- package/dist/gateway-setup.d.ts +2 -0
- package/dist/gateway-setup.d.ts.map +1 -1
- package/dist/gateway-setup.js +275 -11
- package/dist/gitDelivery/actionSheetProjection.d.ts +30 -0
- package/dist/gitDelivery/actionSheetProjection.d.ts.map +1 -0
- package/dist/gitDelivery/actionSheetProjection.js +206 -0
- package/dist/gitDelivery/actionSheetRoutes.d.ts +29 -0
- package/dist/gitDelivery/actionSheetRoutes.d.ts.map +1 -0
- package/dist/gitDelivery/actionSheetRoutes.js +293 -0
- package/dist/gitDelivery/agentOperationsRoutes.d.ts +33 -0
- package/dist/gitDelivery/agentOperationsRoutes.d.ts.map +1 -0
- package/dist/gitDelivery/agentOperationsRoutes.js +405 -0
- package/dist/gitDelivery/commitRoutes.d.ts +23 -0
- package/dist/gitDelivery/commitRoutes.d.ts.map +1 -0
- package/dist/gitDelivery/commitRoutes.js +204 -0
- package/dist/gitDelivery/evidenceRoutes.d.ts +9 -0
- package/dist/gitDelivery/evidenceRoutes.d.ts.map +1 -0
- package/dist/gitDelivery/evidenceRoutes.js +101 -0
- package/dist/gitDelivery/execution.d.ts +38 -0
- package/dist/gitDelivery/execution.d.ts.map +1 -0
- package/dist/gitDelivery/execution.js +117 -0
- package/dist/gitDelivery/localMutationRoutes.d.ts +30 -0
- package/dist/gitDelivery/localMutationRoutes.d.ts.map +1 -0
- package/dist/gitDelivery/localMutationRoutes.js +165 -0
- package/dist/gitDelivery/mergeExecution.d.ts +63 -0
- package/dist/gitDelivery/mergeExecution.d.ts.map +1 -0
- package/dist/gitDelivery/mergeExecution.js +168 -0
- package/dist/gitDelivery/mergeRoutes.d.ts +12 -0
- package/dist/gitDelivery/mergeRoutes.d.ts.map +1 -0
- package/dist/gitDelivery/mergeRoutes.js +218 -0
- package/dist/gitDelivery/mutationEvidenceLedger.d.ts +23 -0
- package/dist/gitDelivery/mutationEvidenceLedger.d.ts.map +1 -0
- package/dist/gitDelivery/mutationEvidenceLedger.js +87 -0
- package/dist/gitDelivery/prExecution.d.ts +54 -0
- package/dist/gitDelivery/prExecution.d.ts.map +1 -0
- package/dist/gitDelivery/prExecution.js +192 -0
- package/dist/gitDelivery/prRoutes.d.ts +12 -0
- package/dist/gitDelivery/prRoutes.d.ts.map +1 -0
- package/dist/gitDelivery/prRoutes.js +256 -0
- package/dist/gitDelivery/pushExecution.d.ts +43 -0
- package/dist/gitDelivery/pushExecution.d.ts.map +1 -0
- package/dist/gitDelivery/pushExecution.js +124 -0
- package/dist/gitDelivery/pushRoutes.d.ts +12 -0
- package/dist/gitDelivery/pushRoutes.d.ts.map +1 -0
- package/dist/gitDelivery/pushRoutes.js +200 -0
- package/dist/gitDelivery/requestGuards.d.ts +15 -0
- package/dist/gitDelivery/requestGuards.d.ts.map +1 -0
- package/dist/gitDelivery/requestGuards.js +97 -0
- package/dist/gitDelivery/syncEvidence.d.ts +37 -0
- package/dist/gitDelivery/syncEvidence.d.ts.map +1 -0
- package/dist/gitDelivery/syncEvidence.js +85 -0
- package/dist/gitDelivery/syncExecution.d.ts +30 -0
- package/dist/gitDelivery/syncExecution.d.ts.map +1 -0
- package/dist/gitDelivery/syncExecution.js +266 -0
- package/dist/gitDelivery/syncRoutes.d.ts +13 -0
- package/dist/gitDelivery/syncRoutes.d.ts.map +1 -0
- package/dist/gitDelivery/syncRoutes.js +200 -0
- package/dist/gitPorcelainStatus.d.ts +15 -0
- package/dist/gitPorcelainStatus.d.ts.map +1 -0
- package/dist/gitPorcelainStatus.js +104 -0
- package/dist/gitRepositoryReads.d.ts +10 -0
- package/dist/gitRepositoryReads.d.ts.map +1 -0
- package/dist/gitRepositoryReads.js +314 -0
- package/dist/gitRepositoryRoutes.d.ts +7 -0
- package/dist/gitRepositoryRoutes.d.ts.map +1 -0
- package/dist/gitRepositoryRoutes.js +221 -0
- package/dist/gitRoutes.d.ts +66 -0
- package/dist/gitRoutes.d.ts.map +1 -0
- package/dist/gitRoutes.js +543 -0
- package/dist/governed-workflow.d.ts +2 -0
- package/dist/governed-workflow.d.ts.map +1 -1
- package/dist/governed-workflow.js +4 -0
- package/dist/grounded-qa-hybrid.d.ts.map +1 -1
- package/dist/grounded-qa-hybrid.js +2 -0
- package/dist/grounded-qa-multi-source.d.ts.map +1 -1
- package/dist/grounded-qa-multi-source.js +1 -0
- package/dist/grounded-qa.d.ts +11 -0
- package/dist/grounded-qa.d.ts.map +1 -1
- package/dist/grounded-qa.js +14 -4
- package/dist/headers.d.ts +4 -1
- package/dist/headers.d.ts.map +1 -1
- package/dist/headers.js +11 -4
- package/dist/index.d.ts +8 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -1
- package/dist/local-knowledge-grounded-qa.d.ts.map +1 -1
- package/dist/local-knowledge-grounded-qa.js +11 -2
- package/dist/qualityIntelligence/figmaSnapshotRoutes.d.ts +1 -1
- package/dist/qualityIntelligence/figmaSnapshotRoutes.d.ts.map +1 -1
- package/dist/qualityIntelligence/figmaSnapshotRoutes.js +1 -1
- package/dist/read-handlers.d.ts +5 -0
- package/dist/read-handlers.d.ts.map +1 -1
- package/dist/read-handlers.js +57 -1
- package/dist/routes.d.ts.map +1 -1
- package/dist/routes.js +260 -12
- package/dist/run-engine.d.ts.map +1 -1
- package/dist/run-engine.js +3 -0
- package/dist/run-handlers.d.ts +0 -1
- package/dist/run-handlers.d.ts.map +1 -1
- package/dist/run-handlers.js +64 -211
- package/dist/run-request.d.ts +11 -0
- package/dist/run-request.d.ts.map +1 -1
- package/dist/run-request.js +158 -10
- package/dist/runtime/capabilityDetector.d.ts +38 -0
- package/dist/runtime/capabilityDetector.d.ts.map +1 -0
- package/dist/runtime/capabilityDetector.js +443 -0
- package/dist/runtime/capabilityRoutes.d.ts +9 -0
- package/dist/runtime/capabilityRoutes.d.ts.map +1 -0
- package/dist/runtime/capabilityRoutes.js +45 -0
- package/dist/runtime/containerEngineDetector.d.ts +17 -0
- package/dist/runtime/containerEngineDetector.d.ts.map +1 -0
- package/dist/runtime/containerEngineDetector.js +222 -0
- package/dist/runtime/containerRoutes.d.ts +8 -0
- package/dist/runtime/containerRoutes.d.ts.map +1 -0
- package/dist/runtime/containerRoutes.js +207 -0
- package/dist/runtime/containerRunner-errors.d.ts +18 -0
- package/dist/runtime/containerRunner-errors.d.ts.map +1 -0
- package/dist/runtime/containerRunner-errors.js +42 -0
- package/dist/runtime/containerRunner-evidence.d.ts +24 -0
- package/dist/runtime/containerRunner-evidence.d.ts.map +1 -0
- package/dist/runtime/containerRunner-evidence.js +74 -0
- package/dist/runtime/containerRunner.d.ts +37 -0
- package/dist/runtime/containerRunner.d.ts.map +1 -0
- package/dist/runtime/containerRunner.js +443 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +24 -4
- package/dist/store/db.d.ts.map +1 -1
- package/dist/store/db.js +2 -1
- package/dist/store/index.d.ts +1 -1
- package/dist/store/index.d.ts.map +1 -1
- package/dist/store/messages.d.ts +2 -1
- package/dist/store/messages.d.ts.map +1 -1
- package/dist/store/messages.js +46 -4
- package/dist/store/schema.d.ts +1 -1
- package/dist/store/schema.d.ts.map +1 -1
- package/dist/store/schema.js +68 -1
- package/dist/store/types.d.ts +3 -2
- package/dist/store/types.d.ts.map +1 -1
- package/dist/task-workspace/active-store.d.ts +21 -0
- package/dist/task-workspace/active-store.d.ts.map +1 -0
- package/dist/task-workspace/active-store.js +55 -0
- package/dist/task-workspace/authorization.d.ts +7 -0
- package/dist/task-workspace/authorization.d.ts.map +1 -0
- package/dist/task-workspace/authorization.js +54 -0
- package/dist/task-workspace/binding.d.ts +3 -0
- package/dist/task-workspace/binding.d.ts.map +1 -0
- package/dist/task-workspace/binding.js +22 -0
- package/dist/task-workspace/cleanup.d.ts +4 -0
- package/dist/task-workspace/cleanup.d.ts.map +1 -0
- package/dist/task-workspace/cleanup.js +428 -0
- package/dist/task-workspace/errors.d.ts +14 -0
- package/dist/task-workspace/errors.d.ts.map +1 -0
- package/dist/task-workspace/errors.js +81 -0
- package/dist/task-workspace/evidence.d.ts +32 -0
- package/dist/task-workspace/evidence.d.ts.map +1 -0
- package/dist/task-workspace/evidence.js +52 -0
- package/dist/task-workspace/field-safety.d.ts +3 -0
- package/dist/task-workspace/field-safety.d.ts.map +1 -0
- package/dist/task-workspace/field-safety.js +42 -0
- package/dist/task-workspace/health.d.ts +4 -0
- package/dist/task-workspace/health.d.ts.map +1 -0
- package/dist/task-workspace/health.js +163 -0
- package/dist/task-workspace/lifecycle.d.ts +3 -0
- package/dist/task-workspace/lifecycle.d.ts.map +1 -0
- package/dist/task-workspace/lifecycle.js +248 -0
- package/dist/task-workspace/locks.d.ts +13 -0
- package/dist/task-workspace/locks.d.ts.map +1 -0
- package/dist/task-workspace/locks.js +44 -0
- package/dist/task-workspace/managed-root.d.ts +7 -0
- package/dist/task-workspace/managed-root.d.ts.map +1 -0
- package/dist/task-workspace/managed-root.js +98 -0
- package/dist/task-workspace/mutex.d.ts +8 -0
- package/dist/task-workspace/mutex.d.ts.map +1 -0
- package/dist/task-workspace/mutex.js +82 -0
- package/dist/task-workspace/naming.d.ts +15 -0
- package/dist/task-workspace/naming.d.ts.map +1 -0
- package/dist/task-workspace/naming.js +0 -0
- package/dist/task-workspace/provisioning.d.ts +3 -0
- package/dist/task-workspace/provisioning.d.ts.map +1 -0
- package/dist/task-workspace/provisioning.js +528 -0
- package/dist/task-workspace/reconciliation.d.ts +15 -0
- package/dist/task-workspace/reconciliation.d.ts.map +1 -0
- package/dist/task-workspace/reconciliation.js +274 -0
- package/dist/task-workspace/repair.d.ts +3 -0
- package/dist/task-workspace/repair.d.ts.map +1 -0
- package/dist/task-workspace/repair.js +286 -0
- package/dist/task-workspace/routes.d.ts +19 -0
- package/dist/task-workspace/routes.d.ts.map +1 -0
- package/dist/task-workspace/routes.js +481 -0
- package/dist/task-workspace/store.d.ts +12 -0
- package/dist/task-workspace/store.d.ts.map +1 -0
- package/dist/task-workspace/store.js +128 -0
- package/dist/task-workspace/types.d.ts +170 -0
- package/dist/task-workspace/types.d.ts.map +1 -0
- package/dist/task-workspace/types.js +5 -0
- package/dist/voice-action-governance.d.ts +23 -0
- package/dist/voice-action-governance.d.ts.map +1 -0
- package/dist/voice-action-governance.js +126 -0
- package/dist/voice-handlers.d.ts +6 -0
- package/dist/voice-handlers.d.ts.map +1 -0
- package/dist/voice-handlers.js +570 -0
- package/dist/voice-realtime-grounded-tool.d.ts +31 -0
- package/dist/voice-realtime-grounded-tool.d.ts.map +1 -0
- package/dist/voice-realtime-grounded-tool.js +322 -0
- package/dist/voice-realtime.d.ts +69 -0
- package/dist/voice-realtime.d.ts.map +1 -0
- package/dist/voice-realtime.js +787 -0
- package/dist/workspace-state-handlers.d.ts +5 -0
- package/dist/workspace-state-handlers.d.ts.map +1 -0
- package/dist/workspace-state-handlers.js +106 -0
- package/package.json +20 -19
- package/dist/grounded-handoff.d.ts +0 -4
- package/dist/grounded-handoff.d.ts.map +0 -1
- package/dist/grounded-handoff.js +0 -445
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
// Governed Git mutation EVIDENCE export route (Issue #474, Epic #470).
|
|
2
|
+
//
|
|
3
|
+
// Single additive, READ-ONLY HTTP handler:
|
|
4
|
+
// * GET /api/git-delivery/evidence
|
|
5
|
+
//
|
|
6
|
+
// It returns the exportable GitDeliveryAuditPacket — the redacted records, per-class and
|
|
7
|
+
// per-disposition counts, and the honest known-limitations list — that compliance and support
|
|
8
|
+
// workflows inspect without replaying raw logs or shell transcripts. It mutates nothing and reads
|
|
9
|
+
// only the bounded, date-bucketed evidence ledger written by mutationEvidenceLedger.ts.
|
|
10
|
+
//
|
|
11
|
+
// ACCESS: the route is read-only and returns only the bounded, redacted evidence packet. A GET is not
|
|
12
|
+
// state-changing, so the central CSRF guard does not cover it; the records themselves remain
|
|
13
|
+
// content-free and are revalidated before export.
|
|
14
|
+
//
|
|
15
|
+
// DEFENCE IN DEPTH: every record is re-validated through the contract guard and the whole packet is
|
|
16
|
+
// re-run through deps.redactor before it leaves the server, so a record that somehow escaped
|
|
17
|
+
// build-time or persist-time redaction is scrubbed on the way out, and a tampered/malformed record is
|
|
18
|
+
// dropped rather than served.
|
|
19
|
+
import { buildGitDeliveryAuditPacket, isGitDeliveryEvidenceRecord, } from "@oscharko-dev/keiko-contracts";
|
|
20
|
+
import { GIT_DELIVERY_EVIDENCE_RUNID_PREFIX } from "./mutationEvidenceLedger.js";
|
|
21
|
+
// ─── Read-window bounds ──────────────────────────────────────────────────────────────────
|
|
22
|
+
const DAY_MS = 86_400_000;
|
|
23
|
+
const DEFAULT_BUCKET_DAYS = 7;
|
|
24
|
+
const MAX_BUCKET_DAYS = 30;
|
|
25
|
+
const DEFAULT_EXPORT_RECORDS = 200;
|
|
26
|
+
const MAX_EXPORT_RECORDS = 500;
|
|
27
|
+
function clampInt(raw, fallback, min, max) {
|
|
28
|
+
if (raw === null) {
|
|
29
|
+
return fallback;
|
|
30
|
+
}
|
|
31
|
+
const parsed = Number.parseInt(raw, 10);
|
|
32
|
+
if (!Number.isFinite(parsed)) {
|
|
33
|
+
return fallback;
|
|
34
|
+
}
|
|
35
|
+
return Math.min(Math.max(parsed, min), max);
|
|
36
|
+
}
|
|
37
|
+
// Date-bucket run ids for the most recent `days` UTC days, OLDEST first so concatenated records stay
|
|
38
|
+
// chronological and the tail slice keeps the most recent.
|
|
39
|
+
function recentRunIds(nowMs, days) {
|
|
40
|
+
const ids = [];
|
|
41
|
+
for (let offset = days - 1; offset >= 0; offset -= 1) {
|
|
42
|
+
const iso = new Date(nowMs - offset * DAY_MS).toISOString();
|
|
43
|
+
ids.push(`${GIT_DELIVERY_EVIDENCE_RUNID_PREFIX}${iso.slice(0, 10)}`);
|
|
44
|
+
}
|
|
45
|
+
return ids;
|
|
46
|
+
}
|
|
47
|
+
function isPlainObject(value) {
|
|
48
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
49
|
+
}
|
|
50
|
+
// Reads a single bucket, dropping a corrupt/unparseable document rather than failing the whole export,
|
|
51
|
+
// and re-validating each record through the contract guard (tamper/malformation rejection on read).
|
|
52
|
+
function readBucket(store, runId) {
|
|
53
|
+
const json = store.get(runId);
|
|
54
|
+
if (json === undefined) {
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
let parsed;
|
|
58
|
+
try {
|
|
59
|
+
parsed = JSON.parse(json);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return [];
|
|
63
|
+
}
|
|
64
|
+
if (!isPlainObject(parsed) || !Array.isArray(parsed.records)) {
|
|
65
|
+
return [];
|
|
66
|
+
}
|
|
67
|
+
return parsed.records.filter(isGitDeliveryEvidenceRecord);
|
|
68
|
+
}
|
|
69
|
+
function collectRecords(store, runIds) {
|
|
70
|
+
const out = [];
|
|
71
|
+
for (const runId of runIds) {
|
|
72
|
+
out.push(...readBucket(store, runId));
|
|
73
|
+
}
|
|
74
|
+
return out;
|
|
75
|
+
}
|
|
76
|
+
export const createHandleGitDeliveryEvidenceExport = (options = {}) => {
|
|
77
|
+
const now = options.now ?? (() => Date.now());
|
|
78
|
+
return (ctx, deps) => {
|
|
79
|
+
const days = clampInt(ctx.url.searchParams.get("days"), DEFAULT_BUCKET_DAYS, 1, MAX_BUCKET_DAYS);
|
|
80
|
+
const limit = clampInt(ctx.url.searchParams.get("limit"), DEFAULT_EXPORT_RECORDS, 1, MAX_EXPORT_RECORDS);
|
|
81
|
+
const nowMs = now();
|
|
82
|
+
const all = collectRecords(deps.evidenceStore, recentRunIds(nowMs, days));
|
|
83
|
+
const truncated = all.length > limit;
|
|
84
|
+
const records = truncated ? all.slice(all.length - limit) : all;
|
|
85
|
+
const windowNote = `This export is a bounded window: the most recent ${String(records.length)} record(s) ` +
|
|
86
|
+
`across the last ${String(days)} day(s)` +
|
|
87
|
+
(truncated ? `, truncated to the ${String(limit)}-record limit.` : ".");
|
|
88
|
+
const packet = buildGitDeliveryAuditPacket(records, nowMs, [windowNote]);
|
|
89
|
+
return { status: 200, body: deps.redactor(packet) };
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
export const handleGitDeliveryEvidenceExport = createHandleGitDeliveryEvidenceExport();
|
|
93
|
+
// Mechanically-mergeable route group. routes.ts spreads this into API_ROUTES alongside
|
|
94
|
+
// GIT_DELIVERY_ACTION_SHEET_ROUTE_GROUP so concurrent #470 epic merges stay merge-safe.
|
|
95
|
+
export const GIT_DELIVERY_EVIDENCE_ROUTE_GROUP = [
|
|
96
|
+
{
|
|
97
|
+
method: "GET",
|
|
98
|
+
pattern: "/api/git-delivery/evidence",
|
|
99
|
+
handler: handleGitDeliveryEvidenceExport,
|
|
100
|
+
},
|
|
101
|
+
];
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { WorkspaceInfo } from "@oscharko-dev/keiko-workspace";
|
|
2
|
+
import { type GitDeliveryActionKind, type GitDeliveryApprovalRequirement, type GitDeliveryRepoPolicyPack } from "@oscharko-dev/keiko-contracts";
|
|
3
|
+
import { type GitLocalMutationAdapter, type GitMutationCommand, type GitMutationLifecycleResult, type GitMutationOutcome, type GitWorktreeSnapshot } from "@oscharko-dev/keiko-tools";
|
|
4
|
+
import type { UiHandlerDeps } from "../deps.js";
|
|
5
|
+
import type { GitDeliveryTrustedPolicyPacks } from "./actionSheetProjection.js";
|
|
6
|
+
export declare const KEIKO_DEFAULT_LOCAL_GIT_POLICY_PACK: GitDeliveryRepoPolicyPack;
|
|
7
|
+
export interface GitDeliveryExecutionSeams {
|
|
8
|
+
readonly adapterFactory?: ((workspace: WorkspaceInfo) => GitLocalMutationAdapter) | undefined;
|
|
9
|
+
readonly snapshotReader?: ((workspace: WorkspaceInfo) => Promise<GitWorktreeSnapshot>) | undefined;
|
|
10
|
+
readonly stagedPathsReader?: ((workspace: WorkspaceInfo) => Promise<readonly string[]>) | undefined;
|
|
11
|
+
readonly policyPacks?: GitDeliveryTrustedPolicyPacks | undefined;
|
|
12
|
+
readonly now?: (() => number) | undefined;
|
|
13
|
+
readonly newActionId?: (() => string) | undefined;
|
|
14
|
+
}
|
|
15
|
+
export declare function resolveProjectWorkspace(deps: Pick<UiHandlerDeps, "managedTaskWorkspaceRoot" | "store" | "workspaceProvisioning">, projectId: string): WorkspaceInfo | undefined;
|
|
16
|
+
export declare function readWorktreeSnapshotFor(workspace: WorkspaceInfo, seams: GitDeliveryExecutionSeams, now: () => number): Promise<GitWorktreeSnapshot>;
|
|
17
|
+
export declare function readStagedPathsFor(workspace: WorkspaceInfo, seams: GitDeliveryExecutionSeams, now: () => number): Promise<readonly string[]>;
|
|
18
|
+
export declare function defaultGitDeliveryActionId(command: unknown, nowMs: number): string;
|
|
19
|
+
export declare function persistGitDeliveryEvidence(deps: Pick<UiHandlerDeps, "evidenceStore" | "redactor">, result: GitMutationLifecycleResult, snapshot: GitWorktreeSnapshot, repoId: string, now: () => number): void;
|
|
20
|
+
/**
|
|
21
|
+
* Runs ONE governed local mutation end-to-end: live snapshot → kernel (preflight + policy + approval +
|
|
22
|
+
* execute) → evidence. Returns the kernel lifecycle result; the caller projects it into a content-free
|
|
23
|
+
* HTTP body. Evidence is appended best-effort BEFORE the caller responds.
|
|
24
|
+
*/
|
|
25
|
+
export declare function executeGovernedMutation(command: GitMutationCommand, approval: GitDeliveryApprovalRequirement, workspace: WorkspaceInfo, deps: Pick<UiHandlerDeps, "evidenceStore" | "redactor">, seams: GitDeliveryExecutionSeams): Promise<GitMutationLifecycleResult>;
|
|
26
|
+
export interface GitDeliveryMutationResponseBody {
|
|
27
|
+
readonly schemaVersion: "1";
|
|
28
|
+
readonly status: GitMutationOutcome["status"];
|
|
29
|
+
readonly actionKind: GitDeliveryActionKind;
|
|
30
|
+
readonly phaseReached: GitMutationLifecycleResult["phaseReached"];
|
|
31
|
+
readonly policyOutcome: GitMutationLifecycleResult["envelope"]["policyDecision"]["outcome"];
|
|
32
|
+
readonly blockReason?: string;
|
|
33
|
+
readonly preflightFindingCodes?: readonly string[];
|
|
34
|
+
readonly requiredApprovers?: readonly string[];
|
|
35
|
+
readonly executionErrorCode?: string;
|
|
36
|
+
}
|
|
37
|
+
export declare function gitDeliveryMutationResponse(result: GitMutationLifecycleResult): GitDeliveryMutationResponseBody;
|
|
38
|
+
//# sourceMappingURL=execution.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"execution.d.ts","sourceRoot":"","sources":["../../src/gitDelivery/execution.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// Governed local Git execution core for the #475 branch/staging/commit routes (Epic #470).
|
|
2
|
+
//
|
|
3
|
+
// Both #475 route groups (localMutationRoutes, commitRoutes) share ONE execution path: resolve and
|
|
4
|
+
// authorize the project workspace, build a TRUSTWORTHY snapshot from the live worktree, drive the
|
|
5
|
+
// #472 kernel `runGitMutation` (the sole execution authority — preflight + policy + approval gates),
|
|
6
|
+
// and append a content-free evidence record through the #474 ledger. No second orchestrator, no
|
|
7
|
+
// generic shell, no terminal-allowlist widening. The git Node effect (adapter + reader) is injected
|
|
8
|
+
// via seams so route tests run deterministically against a fake repository.
|
|
9
|
+
import { GIT_DELIVERY_POLICY_SCHEMA_VERSION, } from "@oscharko-dev/keiko-contracts";
|
|
10
|
+
import { sha256Hex } from "@oscharko-dev/keiko-security";
|
|
11
|
+
import { buildGitDeliveryEvidenceRecord, runGitMutation, } from "@oscharko-dev/keiko-tools";
|
|
12
|
+
import { createNodeGitMutationAdapter, readGitWorktreeSnapshot, readStagedPaths, } from "@oscharko-dev/keiko-tools/internal/git-mutation";
|
|
13
|
+
import { resolveRegisteredOrManagedWorkspaceRoot } from "../task-workspace/authorization.js";
|
|
14
|
+
import { recordGitDeliveryMutationEvidence } from "./mutationEvidenceLedger.js";
|
|
15
|
+
// Default trusted policy: PERMIT the lowest risk class (local-mutation = branch create/switch, stage,
|
|
16
|
+
// unstage, commit) and fail-closed for everything else (publish / protected-or-merge / recovery). It
|
|
17
|
+
// applies when no stricter pack is configured. The decision is still EVALUATED for every action —
|
|
18
|
+
// governance is preserved.
|
|
19
|
+
export const KEIKO_DEFAULT_LOCAL_GIT_POLICY_PACK = {
|
|
20
|
+
schemaVersion: GIT_DELIVERY_POLICY_SCHEMA_VERSION,
|
|
21
|
+
repoId: "keiko-local-default",
|
|
22
|
+
rules: [],
|
|
23
|
+
defaultRule: {
|
|
24
|
+
decision: "constrained",
|
|
25
|
+
constraints: [{ kind: "risk-class-ceiling", maxRiskClass: "local-mutation" }],
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
// projectId IS the workspace root path (mirrors the terminal execution manager). Resolving it through
|
|
29
|
+
// the UI project store both AUTHORIZES the path (only a registered project may be mutated) and yields
|
|
30
|
+
// the WorkspaceInfo the spawn boundary runs in.
|
|
31
|
+
export function resolveProjectWorkspace(deps, projectId) {
|
|
32
|
+
return resolveRegisteredOrManagedWorkspaceRoot(deps, projectId);
|
|
33
|
+
}
|
|
34
|
+
export function readWorktreeSnapshotFor(workspace, seams, now) {
|
|
35
|
+
if (seams.snapshotReader !== undefined)
|
|
36
|
+
return seams.snapshotReader(workspace);
|
|
37
|
+
return readGitWorktreeSnapshot({ workspace, processEnv: process.env, now });
|
|
38
|
+
}
|
|
39
|
+
export function readStagedPathsFor(workspace, seams, now) {
|
|
40
|
+
if (seams.stagedPathsReader !== undefined)
|
|
41
|
+
return seams.stagedPathsReader(workspace);
|
|
42
|
+
return readStagedPaths({ workspace, processEnv: process.env, now });
|
|
43
|
+
}
|
|
44
|
+
function adapterFor(workspace, seams, now) {
|
|
45
|
+
if (seams.adapterFactory !== undefined)
|
|
46
|
+
return seams.adapterFactory(workspace);
|
|
47
|
+
return createNodeGitMutationAdapter({ workspace, processEnv: process.env, now });
|
|
48
|
+
}
|
|
49
|
+
export function defaultGitDeliveryActionId(command, nowMs) {
|
|
50
|
+
return `gde-action-${sha256Hex(`${JSON.stringify(command)}:${String(nowMs)}`).slice(0, 24)}`;
|
|
51
|
+
}
|
|
52
|
+
// String-leaf redactor for the evidence ledger, reusing the deps payload redactor (deepRedactStrings
|
|
53
|
+
// applies the inner audit redactString to a top-level string leaf). No new regex.
|
|
54
|
+
function redactStringFor(deps) {
|
|
55
|
+
return (input) => deps.redactor(input);
|
|
56
|
+
}
|
|
57
|
+
export function persistGitDeliveryEvidence(deps, result, snapshot, repoId, now) {
|
|
58
|
+
const record = buildGitDeliveryEvidenceRecord({
|
|
59
|
+
result,
|
|
60
|
+
snapshot: {
|
|
61
|
+
headDetached: snapshot.headDetached,
|
|
62
|
+
...(snapshot.currentBranchName !== undefined
|
|
63
|
+
? { currentBranchName: snapshot.currentBranchName }
|
|
64
|
+
: {}),
|
|
65
|
+
stagedFileCount: snapshot.stagedFileCount,
|
|
66
|
+
unstagedFileCount: snapshot.unstagedFileCount,
|
|
67
|
+
untrackedFileCount: snapshot.untrackedFileCount,
|
|
68
|
+
},
|
|
69
|
+
workflowRunId: `local-git-delivery:${repoId}`,
|
|
70
|
+
repoId,
|
|
71
|
+
}, { now });
|
|
72
|
+
recordGitDeliveryMutationEvidence({ evidenceStore: deps.evidenceStore, redactString: redactStringFor(deps) }, record);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Runs ONE governed local mutation end-to-end: live snapshot → kernel (preflight + policy + approval +
|
|
76
|
+
* execute) → evidence. Returns the kernel lifecycle result; the caller projects it into a content-free
|
|
77
|
+
* HTTP body. Evidence is appended best-effort BEFORE the caller responds.
|
|
78
|
+
*/
|
|
79
|
+
export async function executeGovernedMutation(command, approval, workspace, deps, seams) {
|
|
80
|
+
const now = seams.now ?? Date.now;
|
|
81
|
+
const snapshot = await readWorktreeSnapshotFor(workspace, seams, now);
|
|
82
|
+
const adapter = adapterFor(workspace, seams, now);
|
|
83
|
+
const packs = seams.policyPacks ?? { repoPack: KEIKO_DEFAULT_LOCAL_GIT_POLICY_PACK };
|
|
84
|
+
const newActionId = seams.newActionId ?? (() => defaultGitDeliveryActionId(command, now()));
|
|
85
|
+
const result = await runGitMutation({ command, approval }, {
|
|
86
|
+
adapter,
|
|
87
|
+
snapshot,
|
|
88
|
+
...(packs.orgPack !== undefined ? { orgPolicyPack: packs.orgPack } : {}),
|
|
89
|
+
...(packs.repoPack !== undefined ? { repoPolicyPack: packs.repoPack } : {}),
|
|
90
|
+
now,
|
|
91
|
+
newActionId,
|
|
92
|
+
});
|
|
93
|
+
persistGitDeliveryEvidence(deps, result, snapshot, workspace.root, now);
|
|
94
|
+
return result;
|
|
95
|
+
}
|
|
96
|
+
export function gitDeliveryMutationResponse(result) {
|
|
97
|
+
const { outcome, envelope, phaseReached, preflight } = result;
|
|
98
|
+
const base = {
|
|
99
|
+
schemaVersion: "1",
|
|
100
|
+
status: outcome.status,
|
|
101
|
+
actionKind: envelope.kind,
|
|
102
|
+
phaseReached,
|
|
103
|
+
policyOutcome: envelope.policyDecision.outcome,
|
|
104
|
+
};
|
|
105
|
+
if (outcome.status === "blocked") {
|
|
106
|
+
return outcome.category === "policy-block"
|
|
107
|
+
? { ...base, blockReason: outcome.blockReason }
|
|
108
|
+
: { ...base, preflightFindingCodes: preflight.blocking.map((f) => f.code) };
|
|
109
|
+
}
|
|
110
|
+
if (outcome.status === "approval-required") {
|
|
111
|
+
return { ...base, requiredApprovers: outcome.requiredApprovers };
|
|
112
|
+
}
|
|
113
|
+
if (outcome.status === "failed" || outcome.status === "recovery-required") {
|
|
114
|
+
return { ...base, executionErrorCode: outcome.executionResult.errorCode ?? "internal-error" };
|
|
115
|
+
}
|
|
116
|
+
return base;
|
|
117
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { GitMutationCommand } from "@oscharko-dev/keiko-tools";
|
|
2
|
+
import type { RouteContext, RouteDefinition, RouteResult } from "../routes.js";
|
|
3
|
+
import type { UiHandlerDeps } from "../deps.js";
|
|
4
|
+
import { type GitDeliveryExecutionSeams } from "./execution.js";
|
|
5
|
+
export type GitDeliveryLocalErrorCode = "GIT_DELIVERY_LOCAL_BAD_REQUEST" | "GIT_DELIVERY_LOCAL_PAYLOAD_TOO_LARGE" | "GIT_DELIVERY_LOCAL_FORBIDDEN_PAYLOAD" | "GIT_DELIVERY_LOCAL_UNKNOWN_PROJECT" | "GIT_DELIVERY_LOCAL_WORKTREE_UNAVAILABLE";
|
|
6
|
+
export interface GitDeliveryLocalErrorBody {
|
|
7
|
+
readonly error: {
|
|
8
|
+
readonly code: GitDeliveryLocalErrorCode;
|
|
9
|
+
readonly message: string;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
type ParsedCommand = {
|
|
13
|
+
readonly ok: true;
|
|
14
|
+
readonly command: GitMutationCommand;
|
|
15
|
+
} | {
|
|
16
|
+
readonly ok: false;
|
|
17
|
+
};
|
|
18
|
+
interface LocalMutationSpec {
|
|
19
|
+
readonly pattern: string;
|
|
20
|
+
readonly allowedKeys: ReadonlySet<string>;
|
|
21
|
+
readonly parse: (obj: Record<string, unknown>) => ParsedCommand;
|
|
22
|
+
}
|
|
23
|
+
export interface GitDeliveryLocalRouteOptions {
|
|
24
|
+
readonly execution?: GitDeliveryExecutionSeams;
|
|
25
|
+
}
|
|
26
|
+
export declare const createHandleLocalMutation: (spec: LocalMutationSpec, options?: GitDeliveryLocalRouteOptions) => ((ctx: RouteContext, deps: UiHandlerDeps) => Promise<RouteResult>);
|
|
27
|
+
export declare const createGitDeliveryLocalMutationRouteGroup: (options?: GitDeliveryLocalRouteOptions) => readonly RouteDefinition[];
|
|
28
|
+
export declare const GIT_DELIVERY_LOCAL_MUTATION_ROUTE_GROUP: readonly RouteDefinition[];
|
|
29
|
+
export {};
|
|
30
|
+
//# sourceMappingURL=localMutationRoutes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"localMutationRoutes.d.ts","sourceRoot":"","sources":["../../src/gitDelivery/localMutationRoutes.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC/E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAIL,KAAK,yBAAyB,EAC/B,MAAM,gBAAgB,CAAC;AAcxB,MAAM,MAAM,yBAAyB,GACjC,gCAAgC,GAChC,sCAAsC,GACtC,sCAAsC,GACtC,oCAAoC,GACpC,yCAAyC,CAAC;AAE9C,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,yBAAyB,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CACxF;AAsBD,KAAK,aAAa,GACd;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,kBAAkB,CAAA;CAAE,GAC3D;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAA;CAAE,CAAC;AAE3B,UAAU,iBAAiB;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC1C,QAAQ,CAAC,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,aAAa,CAAC;CACjE;AAwGD,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,CAAC,SAAS,CAAC,EAAE,yBAAyB,CAAC;CAChD;AAwBD,eAAO,MAAM,yBAAyB,GACpC,MAAM,iBAAiB,EACvB,UAAS,4BAAiC,KACzC,CAAC,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,KAAK,OAAO,CAAC,WAAW,CAAC,CAqBnE,CAAC;AAEF,eAAO,MAAM,wCAAwC,GACnD,UAAS,4BAAiC,KACzC,SAAS,eAAe,EAKtB,CAAC;AAGN,eAAO,MAAM,uCAAuC,EAAE,SAAS,eAAe,EAClC,CAAC"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
// Governed local Git branch + staging execution routes (Issue #475, Epic #470).
|
|
2
|
+
//
|
|
3
|
+
// Four additive POST handlers that EXECUTE a governed local mutation through the #472 kernel:
|
|
4
|
+
// * POST /api/git-delivery/local-branch/create
|
|
5
|
+
// * POST /api/git-delivery/local-branch/switch
|
|
6
|
+
// * POST /api/git-delivery/staging/stage
|
|
7
|
+
// * POST /api/git-delivery/staging/unstage
|
|
8
|
+
//
|
|
9
|
+
// Every handler runs the SAME chain: bounded body + envelope hardening (allowed keys,
|
|
10
|
+
// credential-shape + unsafe-format-char scans) →
|
|
11
|
+
// typed field validation → project authorization → executeGovernedMutation (live snapshot → kernel
|
|
12
|
+
// preflight/policy/approval/execute → evidence) → redacted content-free response. CSRF + JSON content
|
|
13
|
+
// type are enforced centrally by server.ts.
|
|
14
|
+
import { isGitDeliveryApprovalRequirement, } from "@oscharko-dev/keiko-contracts";
|
|
15
|
+
import { executeGovernedMutation, gitDeliveryMutationResponse, resolveProjectWorkspace, } from "./execution.js";
|
|
16
|
+
import { GitDeliveryBodyTooLargeError, hasOnlyAllowedKeys, isContainedPathspec, isNonEmptyString, isPlainObject, readGitDeliveryBody, scanForbiddenStrings, scanUnsafeFormatChars, } from "./requestGuards.js";
|
|
17
|
+
const SAFE_MESSAGES = {
|
|
18
|
+
GIT_DELIVERY_LOCAL_BAD_REQUEST: "The request body is not a valid governed local Git request.",
|
|
19
|
+
GIT_DELIVERY_LOCAL_PAYLOAD_TOO_LARGE: "The governed local Git request exceeds the maximum permitted size.",
|
|
20
|
+
GIT_DELIVERY_LOCAL_FORBIDDEN_PAYLOAD: "The request contained a forbidden field. Requests may not carry credentials, headers, or URLs.",
|
|
21
|
+
GIT_DELIVERY_LOCAL_UNKNOWN_PROJECT: "The requested project is not a known workspace.",
|
|
22
|
+
GIT_DELIVERY_LOCAL_WORKTREE_UNAVAILABLE: "The repository worktree could not be inspected. Confirm the project is a Git repository.",
|
|
23
|
+
};
|
|
24
|
+
const errResult = (status, code) => ({
|
|
25
|
+
status,
|
|
26
|
+
body: { error: { code, message: SAFE_MESSAGES[code] } },
|
|
27
|
+
});
|
|
28
|
+
const sharedKeys = ["schemaVersion", "projectId", "approval"];
|
|
29
|
+
const BRANCH_CREATE_SPEC = {
|
|
30
|
+
pattern: "/api/git-delivery/local-branch/create",
|
|
31
|
+
allowedKeys: new Set([...sharedKeys, "branchName", "baseBranchName", "startPointRefHash"]),
|
|
32
|
+
parse: (obj) => {
|
|
33
|
+
if (isNonEmptyString(obj.branchName) &&
|
|
34
|
+
isNonEmptyString(obj.baseBranchName) &&
|
|
35
|
+
isNonEmptyString(obj.startPointRefHash)) {
|
|
36
|
+
return {
|
|
37
|
+
ok: true,
|
|
38
|
+
command: {
|
|
39
|
+
kind: "branch-create",
|
|
40
|
+
branchName: obj.branchName,
|
|
41
|
+
baseBranchName: obj.baseBranchName,
|
|
42
|
+
startPointRefHash: obj.startPointRefHash,
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
return { ok: false };
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
const BRANCH_SWITCH_SPEC = {
|
|
50
|
+
pattern: "/api/git-delivery/local-branch/switch",
|
|
51
|
+
allowedKeys: new Set([...sharedKeys, "branchName"]),
|
|
52
|
+
parse: (obj) => isNonEmptyString(obj.branchName)
|
|
53
|
+
? { ok: true, command: { kind: "branch-switch", branchName: obj.branchName } }
|
|
54
|
+
: { ok: false },
|
|
55
|
+
};
|
|
56
|
+
function parsePathspecs(value) {
|
|
57
|
+
if (!Array.isArray(value) || value.length === 0)
|
|
58
|
+
return undefined;
|
|
59
|
+
if (!value.every(isContainedPathspec))
|
|
60
|
+
return undefined;
|
|
61
|
+
return value;
|
|
62
|
+
}
|
|
63
|
+
const STAGE_SPEC = {
|
|
64
|
+
pattern: "/api/git-delivery/staging/stage",
|
|
65
|
+
allowedKeys: new Set([...sharedKeys, "pathspecs", "includeUntracked"]),
|
|
66
|
+
parse: (obj) => {
|
|
67
|
+
const pathspecs = parsePathspecs(obj.pathspecs);
|
|
68
|
+
if (pathspecs === undefined || typeof obj.includeUntracked !== "boolean")
|
|
69
|
+
return { ok: false };
|
|
70
|
+
return {
|
|
71
|
+
ok: true,
|
|
72
|
+
command: { kind: "stage", pathspecs, includeUntracked: obj.includeUntracked },
|
|
73
|
+
};
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
const UNSTAGE_SPEC = {
|
|
77
|
+
pattern: "/api/git-delivery/staging/unstage",
|
|
78
|
+
allowedKeys: new Set([...sharedKeys, "pathspecs"]),
|
|
79
|
+
parse: (obj) => {
|
|
80
|
+
const pathspecs = parsePathspecs(obj.pathspecs);
|
|
81
|
+
return pathspecs === undefined
|
|
82
|
+
? { ok: false }
|
|
83
|
+
: { ok: true, command: { kind: "unstage", pathspecs } };
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
function parseApproval(value) {
|
|
87
|
+
if (value === undefined)
|
|
88
|
+
return { required: false };
|
|
89
|
+
return isGitDeliveryApprovalRequirement(value) ? value : undefined;
|
|
90
|
+
}
|
|
91
|
+
function validate(spec, parsed) {
|
|
92
|
+
const bad = {
|
|
93
|
+
kind: "err",
|
|
94
|
+
result: errResult(400, "GIT_DELIVERY_LOCAL_BAD_REQUEST"),
|
|
95
|
+
};
|
|
96
|
+
if (!isPlainObject(parsed) || !hasOnlyAllowedKeys(parsed, spec.allowedKeys))
|
|
97
|
+
return bad;
|
|
98
|
+
if (parsed.schemaVersion !== "1")
|
|
99
|
+
return bad;
|
|
100
|
+
if (scanForbiddenStrings(parsed)) {
|
|
101
|
+
return { kind: "err", result: errResult(400, "GIT_DELIVERY_LOCAL_FORBIDDEN_PAYLOAD") };
|
|
102
|
+
}
|
|
103
|
+
if (scanUnsafeFormatChars(parsed))
|
|
104
|
+
return bad;
|
|
105
|
+
if (!isNonEmptyString(parsed.projectId))
|
|
106
|
+
return bad;
|
|
107
|
+
const approval = parseApproval(parsed.approval);
|
|
108
|
+
if (approval === undefined)
|
|
109
|
+
return bad;
|
|
110
|
+
const command = spec.parse(parsed);
|
|
111
|
+
if (!command.ok)
|
|
112
|
+
return bad;
|
|
113
|
+
return { kind: "ok", value: { projectId: parsed.projectId, command: command.command, approval } };
|
|
114
|
+
}
|
|
115
|
+
async function readParsed(req) {
|
|
116
|
+
let raw;
|
|
117
|
+
try {
|
|
118
|
+
raw = await readGitDeliveryBody(req);
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
const result = error instanceof GitDeliveryBodyTooLargeError
|
|
122
|
+
? errResult(413, "GIT_DELIVERY_LOCAL_PAYLOAD_TOO_LARGE")
|
|
123
|
+
: errResult(400, "GIT_DELIVERY_LOCAL_BAD_REQUEST");
|
|
124
|
+
return { ok: false, result };
|
|
125
|
+
}
|
|
126
|
+
try {
|
|
127
|
+
return { ok: true, value: JSON.parse(raw) };
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
return { ok: false, result: errResult(400, "GIT_DELIVERY_LOCAL_BAD_REQUEST") };
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
export const createHandleLocalMutation = (spec, options = {}) => {
|
|
134
|
+
const seams = options.execution ?? {};
|
|
135
|
+
return async (ctx, deps) => {
|
|
136
|
+
const read = await readParsed(ctx.req);
|
|
137
|
+
if (!read.ok)
|
|
138
|
+
return read.result;
|
|
139
|
+
const validation = validate(spec, read.value);
|
|
140
|
+
if (validation.kind === "err")
|
|
141
|
+
return validation.result;
|
|
142
|
+
const { projectId, command, approval } = validation.value;
|
|
143
|
+
const workspace = resolveProjectWorkspace(deps, projectId);
|
|
144
|
+
if (workspace === undefined)
|
|
145
|
+
return errResult(404, "GIT_DELIVERY_LOCAL_UNKNOWN_PROJECT");
|
|
146
|
+
let result;
|
|
147
|
+
try {
|
|
148
|
+
result = await executeGovernedMutation(command, approval, workspace, deps, seams);
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
// The live worktree could not be read (not a git repository, git unavailable). The kernel itself
|
|
152
|
+
// never throws — only the read-only snapshot step can — so this is a precondition failure, not a
|
|
153
|
+
// mutation; nothing was executed.
|
|
154
|
+
return errResult(409, "GIT_DELIVERY_LOCAL_WORKTREE_UNAVAILABLE");
|
|
155
|
+
}
|
|
156
|
+
return { status: 200, body: deps.redactor(gitDeliveryMutationResponse(result)) };
|
|
157
|
+
};
|
|
158
|
+
};
|
|
159
|
+
export const createGitDeliveryLocalMutationRouteGroup = (options = {}) => [BRANCH_CREATE_SPEC, BRANCH_SWITCH_SPEC, STAGE_SPEC, UNSTAGE_SPEC].map((spec) => ({
|
|
160
|
+
method: "POST",
|
|
161
|
+
pattern: spec.pattern,
|
|
162
|
+
handler: createHandleLocalMutation(spec, options),
|
|
163
|
+
}));
|
|
164
|
+
// Mechanically-mergeable route group spread into API_ROUTES by routes.ts.
|
|
165
|
+
export const GIT_DELIVERY_LOCAL_MUTATION_ROUTE_GROUP = createGitDeliveryLocalMutationRouteGroup();
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { WorkspaceInfo } from "@oscharko-dev/keiko-workspace";
|
|
2
|
+
import { type GitDeliveryApprovalRequirement, type GitDeliveryMergeStrategyHint, type GitDeliveryRepoPolicyPack, type GitDeliveryRiskClass, type GitMergeStrategyPolicy } from "@oscharko-dev/keiko-contracts";
|
|
3
|
+
import { type GitMergeAdapter, type GitMergeCommand, type GitMergeLifecycleResult, type GitMergeProviderReadiness, type GitWorktreeSnapshot } from "@oscharko-dev/keiko-tools";
|
|
4
|
+
import type { UiHandlerDeps } from "../deps.js";
|
|
5
|
+
import type { GitDeliveryTrustedPolicyPacks } from "./actionSheetProjection.js";
|
|
6
|
+
import { type GitDeliveryMutationResponseBody } from "./execution.js";
|
|
7
|
+
export declare const KEIKO_DEFAULT_MERGE_POLICY_PACK: GitDeliveryRepoPolicyPack;
|
|
8
|
+
export interface GitDeliveryMergeSeams {
|
|
9
|
+
readonly mergeAdapterFactory?: ((workspace: WorkspaceInfo) => GitMergeAdapter) | undefined;
|
|
10
|
+
readonly snapshotReader?: ((workspace: WorkspaceInfo) => Promise<GitWorktreeSnapshot>) | undefined;
|
|
11
|
+
readonly policyPacks?: GitDeliveryTrustedPolicyPacks | undefined;
|
|
12
|
+
readonly strategyPolicy?: GitMergeStrategyPolicy | undefined;
|
|
13
|
+
readonly now?: (() => number) | undefined;
|
|
14
|
+
readonly newActionId?: (() => string) | undefined;
|
|
15
|
+
}
|
|
16
|
+
export declare function readMergeProviderReadiness(command: GitMergeCommand, workspace: WorkspaceInfo, seams: GitDeliveryMergeSeams, now: () => number): Promise<GitMergeProviderReadiness>;
|
|
17
|
+
/**
|
|
18
|
+
* Runs ONE governed merge end-to-end: live snapshot → merge gateway (preflight + policy + approval +
|
|
19
|
+
* readiness gate + execute) → evidence. Returns the gateway lifecycle result; the caller projects it into
|
|
20
|
+
* a content-free HTTP body. Evidence is appended best-effort BEFORE the caller responds, for allowed AND
|
|
21
|
+
* blocked alike.
|
|
22
|
+
*/
|
|
23
|
+
export declare function executeGovernedMerge(command: GitMergeCommand, approval: GitDeliveryApprovalRequirement, workspace: WorkspaceInfo, deps: Pick<UiHandlerDeps, "evidenceStore" | "redactor">, seams: GitDeliveryMergeSeams): Promise<GitMergeLifecycleResult>;
|
|
24
|
+
export interface GitDeliveryMergeBlockerView {
|
|
25
|
+
readonly code: string;
|
|
26
|
+
readonly severity: string;
|
|
27
|
+
readonly remediation: string;
|
|
28
|
+
readonly actionHint?: string;
|
|
29
|
+
}
|
|
30
|
+
export interface GitDeliveryMergeReadinessBody {
|
|
31
|
+
readonly mergeable: boolean;
|
|
32
|
+
readonly blockers: readonly GitDeliveryMergeBlockerView[];
|
|
33
|
+
}
|
|
34
|
+
export interface GitDeliveryMergePreviewBody {
|
|
35
|
+
readonly schemaVersion: "1";
|
|
36
|
+
readonly actionKind: "merge";
|
|
37
|
+
readonly baseBranchName: string;
|
|
38
|
+
readonly headBranchName: string;
|
|
39
|
+
readonly prExternalId: string;
|
|
40
|
+
readonly riskClass: GitDeliveryRiskClass;
|
|
41
|
+
readonly riskSeverity: number;
|
|
42
|
+
readonly requestedStrategy: GitDeliveryMergeStrategyHint;
|
|
43
|
+
readonly eligibleStrategies: readonly GitDeliveryMergeStrategyHint[];
|
|
44
|
+
readonly selectedDefaultStrategy?: GitDeliveryMergeStrategyHint;
|
|
45
|
+
readonly requestedStrategyEligible: boolean;
|
|
46
|
+
readonly policyOutcome: string;
|
|
47
|
+
readonly policyBlockReason?: string;
|
|
48
|
+
readonly requiresApproval: boolean;
|
|
49
|
+
readonly readiness: GitDeliveryMergeReadinessBody;
|
|
50
|
+
readonly recommendation: string;
|
|
51
|
+
}
|
|
52
|
+
export declare function buildGitDeliveryMergePreview(command: GitMergeCommand, provider: GitMergeProviderReadiness, packs: GitDeliveryTrustedPolicyPacks, strategyPolicy: GitMergeStrategyPolicy): GitDeliveryMergePreviewBody;
|
|
53
|
+
export interface GitDeliveryMergeExecuteResponseBody extends GitDeliveryMutationResponseBody {
|
|
54
|
+
readonly mergeRejectionReason?: string;
|
|
55
|
+
readonly recoveryDisposition?: string;
|
|
56
|
+
readonly recoveryActionHint?: string;
|
|
57
|
+
readonly mergeable?: boolean;
|
|
58
|
+
readonly readinessBlockers?: readonly GitDeliveryMergeBlockerView[];
|
|
59
|
+
readonly merged?: boolean;
|
|
60
|
+
readonly branchDeleted?: boolean;
|
|
61
|
+
}
|
|
62
|
+
export declare function gitDeliveryMergeExecuteResponse(result: GitMergeLifecycleResult): GitDeliveryMergeExecuteResponseBody;
|
|
63
|
+
//# sourceMappingURL=mergeExecution.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mergeExecution.d.ts","sourceRoot":"","sources":["../../src/gitDelivery/mergeExecution.ts"],"names":[],"mappings":"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"}
|