@defrex/autobuild 0.8.0
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/LICENSE +201 -0
- package/README.md +192 -0
- package/SPEC.md +2269 -0
- package/bin/ab-build-runner.ts +47 -0
- package/bin/ab-dev.ts +27 -0
- package/bin/ab-dispatch-kernel.ts +92 -0
- package/bin/ab-harvest-runner.ts +29 -0
- package/bin/ab-init-probe.ts +19 -0
- package/bin/ab-process-group-reaper.ts +32 -0
- package/bin/ab.ts +6 -0
- package/bin/agent/ab +3 -0
- package/docs/answering-blocked-builds.md +89 -0
- package/docs/architecture.md +726 -0
- package/docs/assets/headline-wide.png +0 -0
- package/docs/configuration.md +1623 -0
- package/docs/hosted-dispatcher-cutover-validation.md +178 -0
- package/docs/hosted-dispatcher.md +204 -0
- package/docs/mcp.md +119 -0
- package/docs/operator-api.md +186 -0
- package/docs/remote-store-protocol.md +972 -0
- package/docs/remote-ticket-protocol.md +79 -0
- package/docs/setup.md +289 -0
- package/docs/spec-standard.md +63 -0
- package/docs/ticket-dependencies.md +59 -0
- package/docs/vercel-sandbox-rollout-probe.md +7 -0
- package/package.json +135 -0
- package/packages/core/src/cli/args.ts +74 -0
- package/packages/core/src/cli/artifact.ts +182 -0
- package/packages/core/src/cli/binary.ts +198 -0
- package/packages/core/src/cli/build-control.ts +789 -0
- package/packages/core/src/cli/build-progress.ts +55 -0
- package/packages/core/src/cli/bulk-control.ts +385 -0
- package/packages/core/src/cli/context.ts +470 -0
- package/packages/core/src/cli/dashboard/actions.ts +59 -0
- package/packages/core/src/cli/dashboard/cells.ts +92 -0
- package/packages/core/src/cli/dashboard/composer.ts +230 -0
- package/packages/core/src/cli/dashboard/detail.ts +100 -0
- package/packages/core/src/cli/dashboard/frame-image.ts +373 -0
- package/packages/core/src/cli/dashboard/live.ts +152 -0
- package/packages/core/src/cli/dashboard/model.ts +1173 -0
- package/packages/core/src/cli/dashboard/poll.ts +196 -0
- package/packages/core/src/cli/dashboard/render.ts +1568 -0
- package/packages/core/src/cli/dashboard/selection.ts +66 -0
- package/packages/core/src/cli/dashboard/session-feed.ts +244 -0
- package/packages/core/src/cli/dashboard/session-view.ts +506 -0
- package/packages/core/src/cli/dashboard/transcript.ts +108 -0
- package/packages/core/src/cli/dev.ts +46 -0
- package/packages/core/src/cli/dispatch-frontend.ts +1045 -0
- package/packages/core/src/cli/dispatch-process.ts +259 -0
- package/packages/core/src/cli/dispatch.ts +3692 -0
- package/packages/core/src/cli/dotenv.ts +48 -0
- package/packages/core/src/cli/env.ts +206 -0
- package/packages/core/src/cli/harvest.ts +698 -0
- package/packages/core/src/cli/help.ts +713 -0
- package/packages/core/src/cli/init-agent.ts +106 -0
- package/packages/core/src/cli/init-validation.ts +583 -0
- package/packages/core/src/cli/init.ts +756 -0
- package/packages/core/src/cli/installation.ts +333 -0
- package/packages/core/src/cli/keyboard.ts +261 -0
- package/packages/core/src/cli/main.ts +1399 -0
- package/packages/core/src/cli/mcp.ts +279 -0
- package/packages/core/src/cli/models.ts +65 -0
- package/packages/core/src/cli/observe.ts +55 -0
- package/packages/core/src/cli/plugin.ts +227 -0
- package/packages/core/src/cli/pr-attachments.ts +138 -0
- package/packages/core/src/cli/pr-summary.ts +124 -0
- package/packages/core/src/cli/publication-settlement.ts +188 -0
- package/packages/core/src/cli/repo-state.ts +213 -0
- package/packages/core/src/cli/repository-status.ts +80 -0
- package/packages/core/src/cli/self-update.ts +443 -0
- package/packages/core/src/cli/status.ts +895 -0
- package/packages/core/src/cli/store-opening.contract.ts +289 -0
- package/packages/core/src/cli/store-opening.ts +167 -0
- package/packages/core/src/cli/store-ref.ts +33 -0
- package/packages/core/src/cli/terminal-restore.ts +174 -0
- package/packages/core/src/cli/terminal.ts +361 -0
- package/packages/core/src/cli/terminals.ts +995 -0
- package/packages/core/src/cli/testkit.ts +194 -0
- package/packages/core/src/cli/ticket.ts +484 -0
- package/packages/core/src/cli/upgrade-agent.ts +180 -0
- package/packages/core/src/cli/upgrade-commit.ts +468 -0
- package/packages/core/src/cli/upgrade-notice.ts +91 -0
- package/packages/core/src/cli/upgrade-progress.ts +110 -0
- package/packages/core/src/cli/upgrade.ts +888 -0
- package/packages/core/src/cli/wait.ts +587 -0
- package/packages/core/src/cli/watch.ts +932 -0
- package/packages/core/src/config/live.ts +255 -0
- package/packages/core/src/config/load.ts +98 -0
- package/packages/core/src/config/roles.ts +438 -0
- package/packages/core/src/config/schema.ts +774 -0
- package/packages/core/src/distribution.ts +24 -0
- package/packages/core/src/events/catalog.ts +201 -0
- package/packages/core/src/events/envelope.ts +59 -0
- package/packages/core/src/events/payloads.ts +618 -0
- package/packages/core/src/events/repository.ts +559 -0
- package/packages/core/src/events/sessions.ts +180 -0
- package/packages/core/src/harvest/schema.ts +147 -0
- package/packages/core/src/ids.ts +45 -0
- package/packages/core/src/integration/harness.ts +681 -0
- package/packages/core/src/kernel/auto-merge.ts +259 -0
- package/packages/core/src/kernel/converge.ts +117 -0
- package/packages/core/src/kernel/dispatch-settings.ts +60 -0
- package/packages/core/src/kernel/dispatch-status.ts +141 -0
- package/packages/core/src/kernel/engine.ts +1047 -0
- package/packages/core/src/kernel/harvest.ts +823 -0
- package/packages/core/src/kernel/origin.ts +69 -0
- package/packages/core/src/kernel/phases.ts +196 -0
- package/packages/core/src/kernel/plan-verify-selection.ts +105 -0
- package/packages/core/src/kernel/pr-attachments.ts +82 -0
- package/packages/core/src/kernel/reducer.ts +752 -0
- package/packages/core/src/kernel/stall.ts +155 -0
- package/packages/core/src/kernel/verify-applicability.ts +52 -0
- package/packages/core/src/markdown.ts +67 -0
- package/packages/core/src/ontology.ts +288 -0
- package/packages/core/src/open-map.ts +203 -0
- package/packages/core/src/operator/annotations.ts +187 -0
- package/packages/core/src/operator/client.ts +311 -0
- package/packages/core/src/operator/control.ts +117 -0
- package/packages/core/src/operator/index.ts +62 -0
- package/packages/core/src/operator/presentation.ts +22 -0
- package/packages/core/src/operator/protocol.ts +98 -0
- package/packages/core/src/operator/query.ts +201 -0
- package/packages/core/src/operator/registry.ts +1043 -0
- package/packages/core/src/operator/requests.ts +138 -0
- package/packages/core/src/operator/sandbox.ts +458 -0
- package/packages/core/src/operator/server.ts +616 -0
- package/packages/core/src/operator/sessions.ts +223 -0
- package/packages/core/src/operator/tickets.ts +177 -0
- package/packages/core/src/plugin-sdk/index.ts +174 -0
- package/packages/core/src/plugins/contract-entry.ts +74 -0
- package/packages/core/src/plugins/contract-env.ts +3 -0
- package/packages/core/src/plugins/load.ts +259 -0
- package/packages/core/src/plugins/manifest.ts +236 -0
- package/packages/core/src/plugins/registry.ts +274 -0
- package/packages/core/src/plugins/runtimes.ts +75 -0
- package/packages/core/src/ports/forge/contract.ts +258 -0
- package/packages/core/src/ports/forge/create.ts +74 -0
- package/packages/core/src/ports/forge/fake.ts +488 -0
- package/packages/core/src/ports/forge/github-pr-attachments.ts +351 -0
- package/packages/core/src/ports/forge/github-transport.ts +352 -0
- package/packages/core/src/ports/forge/github.ts +1162 -0
- package/packages/core/src/ports/forge/local-git.ts +568 -0
- package/packages/core/src/ports/runner/claude.ts +873 -0
- package/packages/core/src/ports/runner/cli-stream.ts +90 -0
- package/packages/core/src/ports/runner/codex.ts +873 -0
- package/packages/core/src/ports/runner/contract.ts +492 -0
- package/packages/core/src/ports/runner/fake.ts +213 -0
- package/packages/core/src/ports/runner/json-record.ts +2 -0
- package/packages/core/src/ports/runner/live-contract-fixture.ts +176 -0
- package/packages/core/src/ports/runner/one-shot.ts +29 -0
- package/packages/core/src/ports/runner/pi-bridge-core.ts +136 -0
- package/packages/core/src/ports/runner/pi-bridge.js +6 -0
- package/packages/core/src/ports/runner/pi-rpc.ts +454 -0
- package/packages/core/src/ports/runner/pi.ts +617 -0
- package/packages/core/src/ports/runner/production.ts +74 -0
- package/packages/core/src/ports/runner/provider-error.ts +121 -0
- package/packages/core/src/ports/runner/routing.ts +234 -0
- package/packages/core/src/ports/runner/runtime.ts +214 -0
- package/packages/core/src/ports/runner/session-env.ts +34 -0
- package/packages/core/src/ports/runner/stream-parts.ts +103 -0
- package/packages/core/src/ports/tickets/contract.ts +570 -0
- package/packages/core/src/ports/tickets/create.ts +135 -0
- package/packages/core/src/ports/tickets/fake.ts +215 -0
- package/packages/core/src/ports/tickets/file.ts +559 -0
- package/packages/core/src/ports/tickets/linear.ts +811 -0
- package/packages/core/src/ports/tickets/operations.ts +161 -0
- package/packages/core/src/ports/tickets/remote-index.ts +13 -0
- package/packages/core/src/ports/tickets/remote-protocol.ts +80 -0
- package/packages/core/src/ports/tickets/remote-server.ts +185 -0
- package/packages/core/src/ports/tickets/remote.ts +130 -0
- package/packages/core/src/ports/tickets/update.ts +51 -0
- package/packages/core/src/ports/types.ts +528 -0
- package/packages/core/src/ports/workspace/build-execution.ts +123 -0
- package/packages/core/src/ports/workspace/contract.ts +136 -0
- package/packages/core/src/ports/workspace/create.ts +132 -0
- package/packages/core/src/ports/workspace/distribution-archive.ts +317 -0
- package/packages/core/src/ports/workspace/fake.ts +432 -0
- package/packages/core/src/ports/workspace/git-worktree.ts +848 -0
- package/packages/core/src/ports/workspace/harvest-execution.ts +111 -0
- package/packages/core/src/ports/workspace/in-process-build-execution.ts +30 -0
- package/packages/core/src/ports/workspace/local-build-execution.ts +88 -0
- package/packages/core/src/ports/workspace/operator-sandbox.ts +142 -0
- package/packages/core/src/ports/workspace/phase-scratch.ts +156 -0
- package/packages/core/src/ports/workspace/vercel-sandbox.ts +2541 -0
- package/packages/core/src/processes/build-child-exit.ts +78 -0
- package/packages/core/src/processes/build-child.ts +119 -0
- package/packages/core/src/processes/build-execution-state.ts +95 -0
- package/packages/core/src/processes/build-parent-watch.ts +35 -0
- package/packages/core/src/processes/build-runner.ts +2614 -0
- package/packages/core/src/processes/dispatcher.ts +2825 -0
- package/packages/core/src/processes/execution-settlement.ts +171 -0
- package/packages/core/src/processes/harvest-child.ts +115 -0
- package/packages/core/src/processes/harvest-execution-state.ts +121 -0
- package/packages/core/src/processes/harvest-runner.ts +1656 -0
- package/packages/core/src/processes/harvest.ts +595 -0
- package/packages/core/src/processes/infrastructure-failure-budget.ts +146 -0
- package/packages/core/src/processes/phase-failure-budget.ts +52 -0
- package/packages/core/src/processes/process-group-reaper.ts +35 -0
- package/packages/core/src/processes/process-group.ts +65 -0
- package/packages/core/src/processes/publication-loss.ts +62 -0
- package/packages/core/src/processes/publication-state.ts +93 -0
- package/packages/core/src/processes/sandbox-state.ts +101 -0
- package/packages/core/src/registry.ts +24 -0
- package/packages/core/src/sessions/reducer.ts +185 -0
- package/packages/core/src/skills.ts +7 -0
- package/packages/core/src/spec-standard.ts +47 -0
- package/packages/core/src/store/adapter.ts +80 -0
- package/packages/core/src/store/build-scope.ts +272 -0
- package/packages/core/src/store/contract.ts +2581 -0
- package/packages/core/src/store/local/blobs.ts +38 -0
- package/packages/core/src/store/local/schema.ts +180 -0
- package/packages/core/src/store/local/store.ts +1713 -0
- package/packages/core/src/store/memory.ts +1138 -0
- package/packages/core/src/store/phase-session.ts +425 -0
- package/packages/core/src/store/remote/client.ts +780 -0
- package/packages/core/src/store/remote/index.ts +12 -0
- package/packages/core/src/store/remote/protocol.ts +348 -0
- package/packages/core/src/store/remote/server.ts +923 -0
- package/packages/core/src/store/remote/token.ts +137 -0
- package/packages/core/src/store/remote/version.ts +10 -0
- package/packages/core/src/store/retention.ts +91 -0
- package/packages/core/src/store/session-handle.ts +265 -0
- package/packages/core/src/store/streams/assemble.ts +79 -0
- package/packages/core/src/store/streams/lock.ts +42 -0
- package/packages/core/src/store/streams/session-writer.ts +306 -0
- package/packages/core/src/store/streams/types.ts +171 -0
- package/packages/core/src/store/streams/wait.ts +127 -0
- package/packages/core/src/store/subscribe.ts +105 -0
- package/packages/core/src/store/types.ts +466 -0
- package/packages/core/src/testing/fixed.ts +44 -0
- package/packages/core/src/testing/store-failures.ts +32 -0
- package/packages/core/src/zod-issues.ts +91 -0
- package/skills/code-review/SKILL.md +100 -0
- package/skills/finalize/SKILL.md +44 -0
- package/skills/guide/SKILL.md +1781 -0
- package/skills/guide/references/operator-api.md +164 -0
- package/skills/guide/references/plugin-authoring.md +354 -0
- package/skills/guide/references/remote-store-protocol.md +972 -0
- package/skills/guide/references/remote-ticket-protocol.md +79 -0
- package/skills/guide/references/setup.md +289 -0
- package/skills/guide/references/spec-standard.md +63 -0
- package/skills/guide/references/ticket-dependencies.md +59 -0
- package/skills/harvest/SKILL.md +66 -0
- package/skills/harvest-review/SKILL.md +63 -0
- package/skills/implement/SKILL.md +82 -0
- package/skills/plan/SKILL.md +123 -0
- package/skills/plan-review/SKILL.md +121 -0
- package/skills/reconcile/SKILL.md +65 -0
- package/skills/spec/SKILL.md +132 -0
- package/skills/tickets/SKILL.md +139 -0
- package/templates/autobuild.toml +36 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { humanActor, type Via } from '../events/envelope'
|
|
2
|
+
import type { RepositoryEventEnvelope } from '../events/repository'
|
|
3
|
+
import { reduceDispatchSettings } from '../kernel/dispatch-settings'
|
|
4
|
+
import { reduceHarvest } from '../kernel/harvest'
|
|
5
|
+
import type { BuildStore } from '../store/types'
|
|
6
|
+
import { projectHarvest, type HarvestRunAction } from '../cli/dashboard/model'
|
|
7
|
+
|
|
8
|
+
export class OperatorControlError extends Error {
|
|
9
|
+
constructor(
|
|
10
|
+
readonly code:
|
|
11
|
+
| 'stale-harvest-run'
|
|
12
|
+
| 'harvest-paused'
|
|
13
|
+
| 'harvest-pending'
|
|
14
|
+
| 'harvest-unavailable',
|
|
15
|
+
message: string,
|
|
16
|
+
) {
|
|
17
|
+
super(message)
|
|
18
|
+
this.name = 'OperatorControlError'
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type RepositorySetting = 'intake' | 'auto-merge-default'
|
|
23
|
+
|
|
24
|
+
export async function setRepositorySetting(opts: {
|
|
25
|
+
store: BuildStore
|
|
26
|
+
repo: string
|
|
27
|
+
user: string
|
|
28
|
+
setting: RepositorySetting
|
|
29
|
+
enabled: boolean
|
|
30
|
+
/** Delegated-write attribution marker threaded onto the event's actor. */
|
|
31
|
+
via?: Via
|
|
32
|
+
}): Promise<{ enabled: boolean; event: RepositoryEventEnvelope }> {
|
|
33
|
+
await opts.store.ensureRepo(opts.repo)
|
|
34
|
+
const event =
|
|
35
|
+
opts.setting === 'intake'
|
|
36
|
+
? await opts.store.appendRepo(opts.repo, {
|
|
37
|
+
actor: humanActor(opts.user, opts.via),
|
|
38
|
+
type: 'dispatcher.intake-set',
|
|
39
|
+
payload: { enabled: opts.enabled },
|
|
40
|
+
})
|
|
41
|
+
: await opts.store.appendRepo(opts.repo, {
|
|
42
|
+
actor: humanActor(opts.user, opts.via),
|
|
43
|
+
type: 'dispatcher.auto-merge-default-set',
|
|
44
|
+
payload: { enabled: opts.enabled },
|
|
45
|
+
})
|
|
46
|
+
return { enabled: opts.enabled, event }
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export async function toggleRepositorySetting(opts: {
|
|
50
|
+
store: BuildStore
|
|
51
|
+
repo: string
|
|
52
|
+
user: string
|
|
53
|
+
setting: RepositorySetting
|
|
54
|
+
via?: Via
|
|
55
|
+
}): Promise<{ enabled: boolean; event: RepositoryEventEnvelope }> {
|
|
56
|
+
await opts.store.ensureRepo(opts.repo)
|
|
57
|
+
const settings = reduceDispatchSettings(await opts.store.getRepoEvents(opts.repo))
|
|
58
|
+
const enabled = opts.setting === 'intake' ? !settings.intake : !settings.defaultAutoMerge
|
|
59
|
+
return setRepositorySetting({ ...opts, enabled })
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export async function toggleHarvestGate(opts: {
|
|
63
|
+
store: BuildStore
|
|
64
|
+
repo: string
|
|
65
|
+
user: string
|
|
66
|
+
via?: Via
|
|
67
|
+
}): Promise<{ command: 'pause' | 'resume'; event: RepositoryEventEnvelope }> {
|
|
68
|
+
await opts.store.ensureRepo(opts.repo)
|
|
69
|
+
const state = reduceHarvest(await opts.store.getRepoEvents(opts.repo))
|
|
70
|
+
const pending = state.pendingCommands.at(-1)
|
|
71
|
+
const requestedPaused = pending === undefined ? state.paused : pending.command === 'pause'
|
|
72
|
+
const command = requestedPaused ? 'resume' : 'pause'
|
|
73
|
+
const event = await opts.store.appendRepo(opts.repo, {
|
|
74
|
+
actor: humanActor(opts.user, opts.via),
|
|
75
|
+
type: command === 'resume' ? 'harvest.resume-requested' : 'harvest.pause-requested',
|
|
76
|
+
payload: {},
|
|
77
|
+
})
|
|
78
|
+
return { command, event }
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Act only on the concrete dashboard run captured by the caller. */
|
|
82
|
+
export async function controlHarvestRun(opts: {
|
|
83
|
+
store: BuildStore
|
|
84
|
+
repo: string
|
|
85
|
+
user: string
|
|
86
|
+
run: string
|
|
87
|
+
via?: Via
|
|
88
|
+
}): Promise<{ action: HarvestRunAction; event: RepositoryEventEnvelope }> {
|
|
89
|
+
await opts.store.ensureRepo(opts.repo)
|
|
90
|
+
const events = await opts.store.getRepoEvents(opts.repo)
|
|
91
|
+
const state = reduceHarvest(events)
|
|
92
|
+
const projected = projectHarvest(events)
|
|
93
|
+
if (projected === undefined || projected.run !== opts.run) {
|
|
94
|
+
throw new OperatorControlError(
|
|
95
|
+
'stale-harvest-run',
|
|
96
|
+
'harvest run action ignored: selected run is no longer active',
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
if (state.paused) {
|
|
100
|
+
throw new OperatorControlError(
|
|
101
|
+
'harvest-paused',
|
|
102
|
+
'harvest run action unavailable while harvest is OFF; select Dispatcher and press h',
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
if (state.pendingCommands.some((command) => command.command === 'resume')) {
|
|
106
|
+
throw new OperatorControlError('harvest-pending', 'harvest run: resume acknowledgement pending')
|
|
107
|
+
}
|
|
108
|
+
if (projected.action === undefined) {
|
|
109
|
+
throw new OperatorControlError('harvest-unavailable', 'harvest run has no available action')
|
|
110
|
+
}
|
|
111
|
+
const event = await opts.store.appendRepo(opts.repo, {
|
|
112
|
+
actor: humanActor(opts.user, opts.via),
|
|
113
|
+
type: 'harvest.resume-requested',
|
|
114
|
+
payload: {},
|
|
115
|
+
})
|
|
116
|
+
return { action: projected.action, event }
|
|
117
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export { OperatorApiClient, OperatorApiError } from './client'
|
|
2
|
+
export type { DownloadedArtifact, OperatorApiClientOptions } from './client'
|
|
3
|
+
export { createOperatorServer } from './server'
|
|
4
|
+
export type { OperatorServerOptions } from './server'
|
|
5
|
+
export {
|
|
6
|
+
answerRequestSchema,
|
|
7
|
+
buildControlRequestSchema,
|
|
8
|
+
buildListScopeSchema,
|
|
9
|
+
bulkControlRequestSchema,
|
|
10
|
+
harvestControlRequestSchema,
|
|
11
|
+
operatorErrorSchema,
|
|
12
|
+
settingRequestSchema,
|
|
13
|
+
ticketBlockerRequestSchema,
|
|
14
|
+
ticketCreateRequestSchema,
|
|
15
|
+
ticketMoveRequestSchema,
|
|
16
|
+
ticketUpdateRequestSchema,
|
|
17
|
+
} from './protocol'
|
|
18
|
+
export type {
|
|
19
|
+
OperatorAnswerRequest,
|
|
20
|
+
OperatorBuildControlRequest,
|
|
21
|
+
OperatorErrorBody,
|
|
22
|
+
OperatorTicketBlockerRequest,
|
|
23
|
+
OperatorTicketCreateRequest,
|
|
24
|
+
OperatorTicketMoveRequest,
|
|
25
|
+
OperatorTicketUpdateRequest,
|
|
26
|
+
} from './protocol'
|
|
27
|
+
export type {
|
|
28
|
+
BuildListScope,
|
|
29
|
+
OperatorBuildView,
|
|
30
|
+
OperatorDashboardSnapshot,
|
|
31
|
+
} from './query'
|
|
32
|
+
export { OperatorControlError } from './control'
|
|
33
|
+
export { OperatorQueryError } from './query'
|
|
34
|
+
export {
|
|
35
|
+
OPERATOR_NOTES_ARTIFACT,
|
|
36
|
+
TOOLS,
|
|
37
|
+
attributed,
|
|
38
|
+
buildRegistry,
|
|
39
|
+
RegistryError,
|
|
40
|
+
} from './registry'
|
|
41
|
+
export type {
|
|
42
|
+
OperatorToolRegistry,
|
|
43
|
+
RegistryErrorReason,
|
|
44
|
+
RegistryOptions,
|
|
45
|
+
ToolApproval,
|
|
46
|
+
ToolContext,
|
|
47
|
+
ToolEntry,
|
|
48
|
+
ToolFailureBody,
|
|
49
|
+
ToolHandler,
|
|
50
|
+
} from './registry'
|
|
51
|
+
export { OPERATOR_TOOL_ANNOTATIONS } from './annotations'
|
|
52
|
+
export type { OperatorToolName, ToolAnnotationsTableEntry } from './annotations'
|
|
53
|
+
export type { RouteRefusal } from './requests'
|
|
54
|
+
export { RouteRefusalError } from './requests'
|
|
55
|
+
export type {
|
|
56
|
+
OperatorTicketBackend,
|
|
57
|
+
OperatorTicketBuild,
|
|
58
|
+
OperatorTicketContext,
|
|
59
|
+
OperatorTicketDetail,
|
|
60
|
+
OperatorTicketQueue,
|
|
61
|
+
} from './tickets'
|
|
62
|
+
export * from './presentation'
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export {
|
|
2
|
+
buildActionAvailability,
|
|
3
|
+
dashboardBuildControl,
|
|
4
|
+
harvestActionAvailability,
|
|
5
|
+
repositoryActionAvailability,
|
|
6
|
+
} from '../cli/dashboard/actions'
|
|
7
|
+
export type { DashboardActionAvailability, DashboardBuildControl } from '../cli/dashboard/actions'
|
|
8
|
+
export type {
|
|
9
|
+
DashboardBuild,
|
|
10
|
+
DashboardHarvest,
|
|
11
|
+
DashboardModel,
|
|
12
|
+
EffectiveStatus,
|
|
13
|
+
PipelineStep,
|
|
14
|
+
StepState,
|
|
15
|
+
StepTiming,
|
|
16
|
+
} from '../cli/dashboard/model'
|
|
17
|
+
export { parseTranscript } from '../cli/dashboard/transcript'
|
|
18
|
+
export type {
|
|
19
|
+
TranscriptPresentation,
|
|
20
|
+
TranscriptTurn,
|
|
21
|
+
TranscriptUsage,
|
|
22
|
+
} from '../cli/dashboard/transcript'
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
export const buildListScopeSchema = z.enum(['active', 'queued', 'all'])
|
|
4
|
+
|
|
5
|
+
export const buildControlRequestSchema = z.discriminatedUnion('action', [
|
|
6
|
+
z.strictObject({ action: z.literal('pause') }),
|
|
7
|
+
z.strictObject({ action: z.literal('cancel-pause') }),
|
|
8
|
+
z.strictObject({ action: z.literal('resume') }),
|
|
9
|
+
z.strictObject({ action: z.literal('abort') }),
|
|
10
|
+
z.strictObject({ action: z.literal('discard') }),
|
|
11
|
+
z.strictObject({ action: z.literal('auto-merge-on') }),
|
|
12
|
+
z.strictObject({ action: z.literal('auto-merge-off') }),
|
|
13
|
+
])
|
|
14
|
+
export type OperatorBuildControlRequest = z.infer<typeof buildControlRequestSchema>
|
|
15
|
+
|
|
16
|
+
const optionalText = z.string().optional()
|
|
17
|
+
export const answerRequestSchema = z.union([
|
|
18
|
+
z.strictObject({ resolution: z.literal('guidance'), text: z.string().trim().min(1) }),
|
|
19
|
+
z.strictObject({ resolution: z.literal('retry') }),
|
|
20
|
+
z.strictObject({ resolution: z.literal('dismiss'), text: optionalText }),
|
|
21
|
+
z.strictObject({
|
|
22
|
+
resolution: z.literal('review-round-ceiling'),
|
|
23
|
+
ceiling: z.number().int().positive(),
|
|
24
|
+
text: optionalText,
|
|
25
|
+
}),
|
|
26
|
+
z.strictObject({
|
|
27
|
+
resolution: z.literal('revise-spec'),
|
|
28
|
+
origin: z.literal('body'),
|
|
29
|
+
body: z.string(),
|
|
30
|
+
text: optionalText,
|
|
31
|
+
/** Accepted only so the shared core can return its exact incompatible-options refusal. */
|
|
32
|
+
ceiling: z.number().int().positive().optional(),
|
|
33
|
+
}),
|
|
34
|
+
z.strictObject({
|
|
35
|
+
resolution: z.literal('revise-spec'),
|
|
36
|
+
origin: z.literal('ticket'),
|
|
37
|
+
body: z.string(),
|
|
38
|
+
text: optionalText,
|
|
39
|
+
ceiling: z.number().int().positive().optional(),
|
|
40
|
+
}),
|
|
41
|
+
])
|
|
42
|
+
export type OperatorAnswerRequest = z.infer<typeof answerRequestSchema>
|
|
43
|
+
|
|
44
|
+
export const settingRequestSchema = z.strictObject({ enabled: z.boolean() })
|
|
45
|
+
export const bulkControlRequestSchema = z.strictObject({ action: z.enum(['pause', 'resume']) })
|
|
46
|
+
export const harvestControlRequestSchema = z.discriminatedUnion('action', [
|
|
47
|
+
z.strictObject({ action: z.literal('toggle-gate') }),
|
|
48
|
+
z.strictObject({ action: z.literal('run'), run: z.string().min(1) }),
|
|
49
|
+
])
|
|
50
|
+
|
|
51
|
+
export const ticketCreateRequestSchema = z.strictObject({
|
|
52
|
+
title: z.string(),
|
|
53
|
+
body: z.string(),
|
|
54
|
+
labels: z.array(z.string()).optional(),
|
|
55
|
+
state: z.string().min(1).optional(),
|
|
56
|
+
blockedBy: z.array(z.string().min(1)).optional(),
|
|
57
|
+
})
|
|
58
|
+
export const ticketUpdateRequestSchema = z
|
|
59
|
+
.strictObject({
|
|
60
|
+
title: z.string().optional(),
|
|
61
|
+
body: z.string().optional(),
|
|
62
|
+
labels: z.array(z.string()).optional(),
|
|
63
|
+
})
|
|
64
|
+
.refine((value) => Object.keys(value).length > 0, 'update must name at least one field')
|
|
65
|
+
export const ticketMoveRequestSchema = z.strictObject({ state: z.string().min(1) })
|
|
66
|
+
export const ticketBlockerRequestSchema = z.strictObject({
|
|
67
|
+
blockerIds: z.array(z.string().min(1)).min(1),
|
|
68
|
+
})
|
|
69
|
+
export type OperatorTicketCreateRequest = z.infer<typeof ticketCreateRequestSchema>
|
|
70
|
+
export type OperatorTicketUpdateRequest = z.infer<typeof ticketUpdateRequestSchema>
|
|
71
|
+
export type OperatorTicketMoveRequest = z.infer<typeof ticketMoveRequestSchema>
|
|
72
|
+
export type OperatorTicketBlockerRequest = z.infer<typeof ticketBlockerRequestSchema>
|
|
73
|
+
|
|
74
|
+
export const operatorErrorSchema = z.strictObject({
|
|
75
|
+
kind: z.enum(['validation', 'auth', 'not-found', 'conflict', 'refusal', 'internal']),
|
|
76
|
+
error: z.string(),
|
|
77
|
+
code: z.string().optional(),
|
|
78
|
+
progress: z.unknown().optional(),
|
|
79
|
+
})
|
|
80
|
+
export type OperatorErrorBody = z.infer<typeof operatorErrorSchema>
|
|
81
|
+
|
|
82
|
+
export const sessionCreateRequestSchema = z.strictObject({ title: z.string().min(1).optional() })
|
|
83
|
+
export type OperatorSessionCreateRequest = z.infer<typeof sessionCreateRequestSchema>
|
|
84
|
+
|
|
85
|
+
export const sessionMessageRequestSchema = z.strictObject({ text: z.string().trim().min(1) })
|
|
86
|
+
export type OperatorSessionMessageRequest = z.infer<typeof sessionMessageRequestSchema>
|
|
87
|
+
|
|
88
|
+
export const sessionWakeRequestSchema = z.strictObject({
|
|
89
|
+
globs: z.array(z.string().min(1)).max(100),
|
|
90
|
+
})
|
|
91
|
+
export type OperatorSessionWakeRequest = z.infer<typeof sessionWakeRequestSchema>
|
|
92
|
+
|
|
93
|
+
export const sessionApprovalRequestSchema = z.strictObject({
|
|
94
|
+
turn: z.string().min(1),
|
|
95
|
+
toolCallId: z.string().min(1),
|
|
96
|
+
decision: z.enum(['approve', 'deny']),
|
|
97
|
+
})
|
|
98
|
+
export type OperatorSessionApprovalRequest = z.infer<typeof sessionApprovalRequestSchema>
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { configSchema, type Config } from '../config/schema'
|
|
2
|
+
import { detail, statusFilter, summarize, type BuildDetail, type BuildSummary } from '../cli/status'
|
|
3
|
+
import { projectRepositoryStatus, type RepositoryStatus } from '../cli/repository-status'
|
|
4
|
+
import { projectHarvestStatus, type HarvestStatusView } from '../cli/harvest'
|
|
5
|
+
import {
|
|
6
|
+
buildDashboardFromProjected,
|
|
7
|
+
projectBuild,
|
|
8
|
+
type DashboardBuild,
|
|
9
|
+
type DashboardModel,
|
|
10
|
+
} from '../cli/dashboard/model'
|
|
11
|
+
import { reduceBuild } from '../kernel/reducer'
|
|
12
|
+
import { reduceDispatchStatus } from '../kernel/dispatch-status'
|
|
13
|
+
import { scanUnclaimedObservations } from '../processes/harvest'
|
|
14
|
+
import type { BuildStore, Clock } from '../store/types'
|
|
15
|
+
|
|
16
|
+
export type BuildListScope = 'active' | 'queued' | 'all'
|
|
17
|
+
|
|
18
|
+
export class OperatorQueryError extends Error {
|
|
19
|
+
constructor(
|
|
20
|
+
readonly code: 'not-found' | 'effective-config-unavailable',
|
|
21
|
+
message: string,
|
|
22
|
+
) {
|
|
23
|
+
super(message)
|
|
24
|
+
this.name = 'OperatorQueryError'
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function repoEvents(store: BuildStore, repo: string) {
|
|
29
|
+
return (await store.getRepo(repo)) === null ? [] : store.getRepoEvents(repo)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export async function listOperatorBuilds(opts: {
|
|
33
|
+
store: BuildStore
|
|
34
|
+
repo: string
|
|
35
|
+
scope: BuildListScope
|
|
36
|
+
now: Date
|
|
37
|
+
}): Promise<BuildSummary[]> {
|
|
38
|
+
const statuses = new Set(statusFilter(opts.scope === 'all', opts.scope === 'queued'))
|
|
39
|
+
const output: BuildSummary[] = []
|
|
40
|
+
for (const record of await opts.store.listBuilds()) {
|
|
41
|
+
if (record.repo !== opts.repo) continue
|
|
42
|
+
const projected = summarize(record, await opts.store.getEvents(record.slug), opts.now)
|
|
43
|
+
if (statuses.has(projected.status)) output.push(projected)
|
|
44
|
+
}
|
|
45
|
+
return output.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt))
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function requireBuild(store: BuildStore, repo: string, slug: string) {
|
|
49
|
+
const record = await store.getBuild(slug)
|
|
50
|
+
if (record === null) throw new OperatorQueryError('not-found', `no build "${slug}" in this store`)
|
|
51
|
+
if (record.repo !== repo) {
|
|
52
|
+
throw new OperatorQueryError('not-found', `unknown build "${slug}"`)
|
|
53
|
+
}
|
|
54
|
+
return record
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export async function effectiveConfig(
|
|
58
|
+
store: BuildStore,
|
|
59
|
+
repo: string,
|
|
60
|
+
): Promise<{
|
|
61
|
+
config: Config
|
|
62
|
+
repositoryEvents: Awaited<ReturnType<typeof repoEvents>>
|
|
63
|
+
status: ReturnType<typeof reduceDispatchStatus>
|
|
64
|
+
}> {
|
|
65
|
+
const repositoryEvents = await repoEvents(store, repo)
|
|
66
|
+
let latestRun: string | undefined
|
|
67
|
+
for (const event of repositoryEvents) {
|
|
68
|
+
if (event.type === 'dispatcher.run-started') latestRun = event.payload.run
|
|
69
|
+
}
|
|
70
|
+
if (latestRun === undefined) {
|
|
71
|
+
throw new OperatorQueryError(
|
|
72
|
+
'effective-config-unavailable',
|
|
73
|
+
`effective config unavailable for repository "${repo}": no dispatcher run is recorded`,
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
const status = reduceDispatchStatus(repositoryEvents, latestRun)
|
|
77
|
+
const ref = status.effectiveConfig
|
|
78
|
+
if (ref === undefined) {
|
|
79
|
+
throw new OperatorQueryError(
|
|
80
|
+
'effective-config-unavailable',
|
|
81
|
+
`effective config unavailable for repository "${repo}"`,
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
const artifact = await store.getRepoArtifact(repo, ref.kind, ref.rev)
|
|
85
|
+
if (artifact === null) {
|
|
86
|
+
throw new OperatorQueryError(
|
|
87
|
+
'effective-config-unavailable',
|
|
88
|
+
`effective config ${ref.kind}@${ref.rev} is not retrievable`,
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
let raw: unknown
|
|
92
|
+
try {
|
|
93
|
+
raw = JSON.parse(new TextDecoder().decode(artifact.content))
|
|
94
|
+
} catch {
|
|
95
|
+
throw new OperatorQueryError(
|
|
96
|
+
'effective-config-unavailable',
|
|
97
|
+
`effective config ${ref.kind}@${ref.rev} is not valid JSON`,
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
const parsed = configSchema.safeParse(raw)
|
|
101
|
+
if (!parsed.success) {
|
|
102
|
+
throw new OperatorQueryError(
|
|
103
|
+
'effective-config-unavailable',
|
|
104
|
+
`effective config ${ref.kind}@${ref.rev} is invalid: ${parsed.error.message}`,
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
return { config: parsed.data, repositoryEvents, status }
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export interface OperatorBuildView {
|
|
111
|
+
detail: BuildDetail
|
|
112
|
+
dashboardRow: DashboardBuild | null
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export async function getOperatorBuild(opts: {
|
|
116
|
+
store: BuildStore
|
|
117
|
+
repo: string
|
|
118
|
+
slug: string
|
|
119
|
+
now: Date
|
|
120
|
+
}): Promise<OperatorBuildView> {
|
|
121
|
+
const record = await requireBuild(opts.store, opts.repo, opts.slug)
|
|
122
|
+
const events = await opts.store.getEvents(opts.slug)
|
|
123
|
+
const state = reduceBuild(events)
|
|
124
|
+
const { config } = await effectiveConfig(opts.store, opts.repo)
|
|
125
|
+
const dashboardRow = projectBuild(record, state, config, events)
|
|
126
|
+
return { detail: detail(record, events, opts.now), dashboardRow }
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export async function getRepositoryStatus(
|
|
130
|
+
store: BuildStore,
|
|
131
|
+
repo: string,
|
|
132
|
+
): Promise<RepositoryStatus> {
|
|
133
|
+
return projectRepositoryStatus(repo, await repoEvents(store, repo))
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export async function getHarvestStatus(
|
|
137
|
+
store: BuildStore,
|
|
138
|
+
repo: string,
|
|
139
|
+
): Promise<HarvestStatusView> {
|
|
140
|
+
return projectHarvestStatus(repo, await repoEvents(store, repo))
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export interface OperatorDashboardSnapshot {
|
|
144
|
+
generatedAt: string
|
|
145
|
+
model: DashboardModel
|
|
146
|
+
settingsHeader: {
|
|
147
|
+
intake: boolean
|
|
148
|
+
repositoryPaused: boolean
|
|
149
|
+
defaultAutoMerge: boolean
|
|
150
|
+
harvestPaused: boolean
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export async function getOperatorDashboard(opts: {
|
|
155
|
+
store: BuildStore
|
|
156
|
+
repo: string
|
|
157
|
+
clock: Clock
|
|
158
|
+
}): Promise<OperatorDashboardSnapshot> {
|
|
159
|
+
const { config, repositoryEvents, status } = await effectiveConfig(opts.store, opts.repo)
|
|
160
|
+
const projected: DashboardBuild[] = []
|
|
161
|
+
let activeCount = 0
|
|
162
|
+
for (const record of await opts.store.listBuilds()) {
|
|
163
|
+
if (record.repo !== opts.repo) continue
|
|
164
|
+
const events = await opts.store.getEvents(record.slug)
|
|
165
|
+
const state = reduceBuild(events)
|
|
166
|
+
if (state.status !== 'done' && state.status !== 'aborted') activeCount += 1
|
|
167
|
+
const row = projectBuild(record, state, config, events)
|
|
168
|
+
if (row !== null) projected.push(row)
|
|
169
|
+
}
|
|
170
|
+
const scan = await scanUnclaimedObservations(opts.store, opts.repo)
|
|
171
|
+
const warningLines = [
|
|
172
|
+
...status.roleWarnings,
|
|
173
|
+
...(status.warningNotice !== undefined ? [status.warningNotice] : []),
|
|
174
|
+
]
|
|
175
|
+
const model = buildDashboardFromProjected(
|
|
176
|
+
projected,
|
|
177
|
+
{
|
|
178
|
+
repo: opts.repo,
|
|
179
|
+
queued: status.queued ?? 0,
|
|
180
|
+
activeCount,
|
|
181
|
+
capacity: config.capacity,
|
|
182
|
+
observationCount: scan.observations.length,
|
|
183
|
+
observationLimit: config.policy.harvestThreshold,
|
|
184
|
+
...(status.availableUpgrade !== undefined
|
|
185
|
+
? { availableUpgrade: status.availableUpgrade }
|
|
186
|
+
: {}),
|
|
187
|
+
...(warningLines.length > 0 ? { warningLines } : {}),
|
|
188
|
+
},
|
|
189
|
+
repositoryEvents,
|
|
190
|
+
)
|
|
191
|
+
return {
|
|
192
|
+
generatedAt: opts.clock().toISOString(),
|
|
193
|
+
model,
|
|
194
|
+
settingsHeader: {
|
|
195
|
+
intake: !model.drained,
|
|
196
|
+
repositoryPaused: model.repositoryPaused,
|
|
197
|
+
defaultAutoMerge: model.defaultAutoMerge,
|
|
198
|
+
harvestPaused: model.harvestPaused,
|
|
199
|
+
},
|
|
200
|
+
}
|
|
201
|
+
}
|