@defrex/autobuild 0.8.0 → 0.9.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.
Files changed (147) hide show
  1. package/README.md +1 -1
  2. package/SPEC.md +313 -26
  3. package/bin/ab-build-runner.ts +8 -2
  4. package/docs/architecture.md +57 -14
  5. package/docs/assets/headline-wide.png +0 -0
  6. package/docs/configuration.md +151 -25
  7. package/docs/hosted-dispatcher.md +69 -13
  8. package/docs/operator-api.md +2 -2
  9. package/docs/remote-store-protocol.md +169 -16
  10. package/docs/remote-ticket-protocol.md +1 -1
  11. package/docs/setup.md +12 -9
  12. package/docs/website.md +10 -0
  13. package/package.json +43 -23
  14. package/packages/core/src/cli/artifact.ts +130 -6
  15. package/packages/core/src/cli/binary.ts +9 -1
  16. package/packages/core/src/cli/build-control.ts +110 -13
  17. package/packages/core/src/cli/context.ts +46 -0
  18. package/packages/core/src/cli/dashboard/model.ts +57 -7
  19. package/packages/core/src/cli/dashboard/poll.ts +69 -10
  20. package/packages/core/src/cli/dashboard/render.ts +34 -5
  21. package/packages/core/src/cli/dispatch-frontend.ts +54 -5
  22. package/packages/core/src/cli/dispatch.ts +320 -46
  23. package/packages/core/src/cli/harvest.ts +122 -8
  24. package/packages/core/src/cli/help.ts +47 -8
  25. package/packages/core/src/cli/init-readiness-remote.ts +110 -0
  26. package/packages/core/src/cli/init-readiness-shared.ts +72 -0
  27. package/packages/core/src/cli/init-validation.ts +248 -222
  28. package/packages/core/src/cli/main.ts +70 -11
  29. package/packages/core/src/cli/mcp.ts +23 -1
  30. package/packages/core/src/cli/plugin.ts +7 -4
  31. package/packages/core/src/cli/remote-poll.ts +277 -0
  32. package/packages/core/src/cli/repo-state.ts +22 -0
  33. package/packages/core/src/cli/repository-status.ts +39 -1
  34. package/packages/core/src/cli/status.ts +149 -27
  35. package/packages/core/src/cli/terminals.ts +2 -1
  36. package/packages/core/src/cli/ticket-assets.ts +247 -0
  37. package/packages/core/src/cli/ticket.ts +307 -7
  38. package/packages/core/src/cli/wait.ts +147 -39
  39. package/packages/core/src/cli/watch.ts +128 -166
  40. package/packages/core/src/config/live.ts +22 -0
  41. package/packages/core/src/config/pipeline-source.ts +376 -0
  42. package/packages/core/src/config/schema.ts +230 -19
  43. package/packages/core/src/events/catalog.ts +1 -0
  44. package/packages/core/src/events/globs.ts +47 -0
  45. package/packages/core/src/events/payloads.ts +52 -2
  46. package/packages/core/src/events/repository.ts +52 -1
  47. package/packages/core/src/events/sessions.ts +69 -10
  48. package/packages/core/src/events/workspace-remote.ts +12 -0
  49. package/packages/core/src/integration/harness.ts +2 -1
  50. package/packages/core/src/kernel/auto-merge-default.ts +123 -0
  51. package/packages/core/src/kernel/phases.ts +16 -2
  52. package/packages/core/src/kernel/reducer.ts +40 -1
  53. package/packages/core/src/operator/annotations.ts +6 -0
  54. package/packages/core/src/operator/control.ts +19 -5
  55. package/packages/core/src/operator/index.ts +71 -6
  56. package/packages/core/src/operator/query.ts +208 -19
  57. package/packages/core/src/operator/registry.ts +55 -10
  58. package/packages/core/src/operator/requests.ts +9 -6
  59. package/packages/core/src/operator/sandbox-host.ts +140 -0
  60. package/packages/core/src/operator/sandbox.ts +247 -14
  61. package/packages/core/src/operator/ticket-source-backend.ts +47 -0
  62. package/packages/core/src/orchestrator/approvals.ts +115 -0
  63. package/packages/core/src/orchestrator/conversation.ts +167 -0
  64. package/packages/core/src/orchestrator/model.ts +86 -0
  65. package/packages/core/src/orchestrator/prompt.ts +61 -0
  66. package/packages/core/src/orchestrator/tools.ts +117 -0
  67. package/packages/core/src/orchestrator/turn-runner.ts +457 -0
  68. package/packages/core/src/plugin-sdk/index.ts +51 -1
  69. package/packages/core/src/plugins/load.ts +135 -7
  70. package/packages/core/src/plugins/manifest.ts +183 -10
  71. package/packages/core/src/plugins/registry.ts +73 -4
  72. package/packages/core/src/ports/forge/create.ts +1 -1
  73. package/packages/core/src/ports/forge/fake.ts +31 -0
  74. package/packages/core/src/ports/forge/github-transport.ts +13 -2
  75. package/packages/core/src/ports/forge/github.ts +13 -2
  76. package/packages/core/src/ports/forge/local-git.ts +31 -1
  77. package/packages/core/src/ports/runner/claude.ts +2 -2
  78. package/packages/core/src/ports/runner/codex.ts +6 -2
  79. package/packages/core/src/ports/runner/fake.ts +7 -1
  80. package/packages/core/src/ports/runner/pi.ts +8 -6
  81. package/packages/core/src/ports/runner/provider-error.ts +13 -3
  82. package/packages/core/src/ports/tickets/{remote-index.ts → hosted-index.ts} +3 -2
  83. package/packages/core/src/ports/types.ts +17 -2
  84. package/packages/core/src/ports/workspace/builtin-capabilities.ts +114 -0
  85. package/packages/core/src/ports/workspace/create.ts +74 -39
  86. package/packages/core/src/ports/workspace/distribution-archive.ts +171 -12
  87. package/packages/core/src/ports/workspace/fake.ts +169 -4
  88. package/packages/core/src/ports/workspace/git-worktree.ts +246 -26
  89. package/packages/core/src/ports/workspace/github-origin.ts +36 -0
  90. package/packages/core/src/ports/workspace/operator-sandbox.ts +7 -1
  91. package/packages/core/src/ports/workspace/provider-capabilities.ts +171 -0
  92. package/packages/core/src/ports/workspace/vercel-capabilities.ts +110 -0
  93. package/packages/core/src/ports/workspace/vercel-sandbox.ts +142 -114
  94. package/packages/core/src/processes/build-child.ts +17 -2
  95. package/packages/core/src/processes/build-execution-state.ts +37 -0
  96. package/packages/core/src/processes/build-runner.ts +54 -3
  97. package/packages/core/src/processes/dispatcher.ts +247 -41
  98. package/packages/core/src/processes/harvest-child.ts +5 -0
  99. package/packages/core/src/processes/harvest-runner.ts +66 -19
  100. package/packages/core/src/processes/harvest.ts +196 -24
  101. package/packages/core/src/processes/orchestrator-tick.ts +319 -0
  102. package/packages/core/src/processes/sandbox-state.ts +10 -0
  103. package/packages/core/src/store/adapter.ts +32 -0
  104. package/packages/core/src/store/build-scope.ts +64 -0
  105. package/packages/core/src/store/contract.ts +868 -0
  106. package/packages/core/src/store/digest.ts +49 -0
  107. package/packages/core/src/store/local/schema.ts +29 -2
  108. package/packages/core/src/store/local/store.ts +338 -4
  109. package/packages/core/src/store/memory.ts +179 -0
  110. package/packages/core/src/store/new-build.ts +17 -0
  111. package/packages/core/src/store/phase-session.ts +71 -0
  112. package/packages/core/src/store/remote/client.ts +260 -1
  113. package/packages/core/src/store/remote/index.ts +12 -2
  114. package/packages/core/src/store/remote/protocol.ts +100 -12
  115. package/packages/core/src/store/remote/version.ts +6 -2
  116. package/packages/core/src/store/repo-state-events.ts +140 -0
  117. package/packages/core/src/store/retention.ts +7 -0
  118. package/packages/core/src/store/session-handle.ts +64 -0
  119. package/packages/core/src/{sessions/reducer.ts → store/session-reducer.ts} +28 -9
  120. package/packages/core/src/store/streams/session-writer.ts +42 -2
  121. package/packages/core/src/store/streams/wait.ts +4 -5
  122. package/packages/core/src/store/ticket-assets.ts +425 -0
  123. package/packages/core/src/store/types.ts +122 -1
  124. package/packages/core/src/testing/index.ts +59 -0
  125. package/skills/code-review/SKILL.md +5 -1
  126. package/skills/guide/SKILL.md +126 -33
  127. package/skills/guide/references/operator-api.md +2 -2
  128. package/skills/guide/references/plugin-authoring.md +17 -0
  129. package/skills/guide/references/remote-store-protocol.md +169 -16
  130. package/skills/guide/references/remote-ticket-protocol.md +1 -1
  131. package/skills/guide/references/setup.md +12 -9
  132. package/skills/implement/SKILL.md +3 -0
  133. package/skills/operate/SKILL.md +126 -0
  134. package/skills/plan/SKILL.md +4 -0
  135. package/skills/plan-review/SKILL.md +5 -1
  136. package/skills/spec/SKILL.md +46 -3
  137. package/packages/core/src/cli/dashboard/frame-image.ts +0 -373
  138. package/packages/core/src/cli/store-opening.contract.ts +0 -289
  139. package/packages/core/src/cli/testkit.ts +0 -194
  140. package/packages/core/src/markdown.ts +0 -67
  141. package/packages/core/src/operator/client.ts +0 -311
  142. package/packages/core/src/operator/server.ts +0 -616
  143. package/packages/core/src/operator/sessions.ts +0 -223
  144. package/packages/core/src/ports/runner/live-contract-fixture.ts +0 -176
  145. package/packages/core/src/ports/tickets/remote-server.ts +0 -185
  146. package/packages/core/src/store/remote/server.ts +0 -923
  147. package/packages/core/src/testing/store-failures.ts +0 -32
@@ -1,311 +0,0 @@
1
- import type { BuildControlResult } from '../cli/build-control'
2
- import type { BulkControlSummary } from '../cli/bulk-control'
3
- import type { HarvestStatusView } from '../cli/harvest'
4
- import type { RepositoryStatus } from '../cli/repository-status'
5
- import type { BuildSummary } from '../cli/status'
6
- import {
7
- AUTOBUILD_VERSION,
8
- AUTOBUILD_VERSION_HEADER,
9
- REMOTE_STORE_PROTOCOL_VERSION,
10
- REMOTE_STORE_PROTOCOL_VERSION_HEADER,
11
- } from '../store/remote/version'
12
- import {
13
- operatorErrorSchema,
14
- type OperatorAnswerRequest,
15
- type OperatorBuildControlRequest,
16
- type OperatorTicketBlockerRequest,
17
- type OperatorTicketCreateRequest,
18
- type OperatorTicketMoveRequest,
19
- type OperatorTicketUpdateRequest,
20
- } from './protocol'
21
- import type { OperatorBuildView, OperatorDashboardSnapshot } from './query'
22
- import type { OperatorTicketDetail, OperatorTicketQueue } from './tickets'
23
- import type { OperatorSessionView } from './sessions'
24
- import type { SessionRecord } from '../store/types'
25
- import type { StreamRead } from '../store/streams/types'
26
- import type {
27
- OperatorSessionApprovalRequest,
28
- OperatorSessionCreateRequest,
29
- OperatorSessionMessageRequest,
30
- OperatorSessionWakeRequest,
31
- } from './protocol'
32
-
33
- export class OperatorApiError extends Error {
34
- constructor(
35
- readonly status: number,
36
- readonly kind: string,
37
- readonly code?: string,
38
- readonly progress?: unknown,
39
- ) {
40
- super(kind)
41
- this.name = 'OperatorApiError'
42
- }
43
- }
44
-
45
- export interface OperatorApiClientOptions {
46
- url: string
47
- token: string
48
- fetchFn?: typeof fetch
49
- }
50
-
51
- export interface DownloadedArtifact {
52
- content: Uint8Array
53
- kind: string
54
- revision: number
55
- blobRef: string
56
- disposition?: string
57
- }
58
-
59
- export class OperatorApiClient {
60
- private readonly base: string
61
- private readonly fetchFn: typeof fetch
62
- constructor(private readonly options: OperatorApiClientOptions) {
63
- this.base = options.url.replace(/\/+$/, '')
64
- this.fetchFn = options.fetchFn ?? fetch
65
- }
66
-
67
- private repoPath(repo: string, suffix: string): string {
68
- return `/operator/v1/repos/${encodeURIComponent(repo)}/${suffix}`
69
- }
70
-
71
- private async request<T>(repo: string, suffix: string, init: RequestInit = {}): Promise<T> {
72
- const headers = new Headers(init.headers)
73
- headers.set(AUTOBUILD_VERSION_HEADER, AUTOBUILD_VERSION)
74
- headers.set(REMOTE_STORE_PROTOCOL_VERSION_HEADER, REMOTE_STORE_PROTOCOL_VERSION)
75
- headers.set('authorization', `Bearer ${this.options.token}`)
76
- if (init.body !== undefined) headers.set('content-type', 'application/json')
77
- const response = await this.fetchFn(`${this.base}${this.repoPath(repo, suffix)}`, {
78
- ...init,
79
- headers,
80
- })
81
- if (!response.ok) {
82
- const parsed = operatorErrorSchema.safeParse(await response.json().catch(() => null))
83
- if (!parsed.success) throw new Error(`operator API returned HTTP ${response.status}`)
84
- const error = new OperatorApiError(
85
- response.status,
86
- parsed.data.kind,
87
- parsed.data.code,
88
- parsed.data.progress,
89
- )
90
- error.message = parsed.data.error
91
- throw error
92
- }
93
- return (await response.json()) as T
94
- }
95
-
96
- listBuilds(repo: string, scope: 'active' | 'queued' | 'all' = 'active'): Promise<BuildSummary[]> {
97
- return this.request(repo, `builds?scope=${scope}`)
98
- }
99
- getBuild(repo: string, slug: string): Promise<OperatorBuildView> {
100
- return this.request(repo, `builds/${encodeURIComponent(slug)}`)
101
- }
102
- dashboard(repo: string): Promise<OperatorDashboardSnapshot> {
103
- return this.request(repo, 'dashboard')
104
- }
105
- repositoryStatus(repo: string): Promise<RepositoryStatus> {
106
- return this.request(repo, 'status')
107
- }
108
- harvestStatus(repo: string): Promise<HarvestStatusView> {
109
- return this.request(repo, 'harvest/status')
110
- }
111
- listTickets(
112
- repo: string,
113
- filters: { state?: string; labels?: string[] } = {},
114
- ): Promise<OperatorTicketQueue> {
115
- const query = new URLSearchParams()
116
- if (filters.state !== undefined) query.set('state', filters.state)
117
- for (const label of filters.labels ?? []) query.append('label', label)
118
- const suffix = query.size > 0 ? `tickets?${query}` : 'tickets'
119
- return this.request(repo, suffix)
120
- }
121
- getTicket(repo: string, id: string): Promise<OperatorTicketDetail> {
122
- return this.request(repo, `tickets/${encodeURIComponent(id)}`)
123
- }
124
- createTicket(repo: string, request: OperatorTicketCreateRequest): Promise<OperatorTicketDetail> {
125
- return this.request(repo, 'tickets', { method: 'POST', body: JSON.stringify(request) })
126
- }
127
- updateTicket(
128
- repo: string,
129
- id: string,
130
- request: OperatorTicketUpdateRequest,
131
- ): Promise<OperatorTicketDetail> {
132
- return this.request(repo, `tickets/${encodeURIComponent(id)}`, {
133
- method: 'PATCH',
134
- body: JSON.stringify(request),
135
- })
136
- }
137
- moveTicket(
138
- repo: string,
139
- id: string,
140
- request: OperatorTicketMoveRequest,
141
- ): Promise<OperatorTicketDetail> {
142
- return this.request(repo, `tickets/${encodeURIComponent(id)}/move`, {
143
- method: 'POST',
144
- body: JSON.stringify(request),
145
- })
146
- }
147
- blockTicket(
148
- repo: string,
149
- id: string,
150
- request: OperatorTicketBlockerRequest,
151
- ): Promise<OperatorTicketDetail> {
152
- return this.request(repo, `tickets/${encodeURIComponent(id)}/block`, {
153
- method: 'POST',
154
- body: JSON.stringify(request),
155
- })
156
- }
157
- unblockTicket(
158
- repo: string,
159
- id: string,
160
- request: OperatorTicketBlockerRequest,
161
- ): Promise<OperatorTicketDetail> {
162
- return this.request(repo, `tickets/${encodeURIComponent(id)}/unblock`, {
163
- method: 'POST',
164
- body: JSON.stringify(request),
165
- })
166
- }
167
- listSessions(repo: string): Promise<SessionRecord[]> {
168
- return this.request(repo, 'sessions')
169
- }
170
- createSession(repo: string, request: OperatorSessionCreateRequest): Promise<SessionRecord> {
171
- return this.request(repo, 'sessions', { method: 'POST', body: JSON.stringify(request) })
172
- }
173
- getSession(repo: string, sid: string): Promise<OperatorSessionView> {
174
- return this.request(repo, `sessions/${encodeURIComponent(sid)}`)
175
- }
176
- postSessionMessage(
177
- repo: string,
178
- sid: string,
179
- request: OperatorSessionMessageRequest,
180
- ): Promise<{ ok: boolean }> {
181
- return this.request(repo, `sessions/${encodeURIComponent(sid)}/messages`, {
182
- method: 'POST',
183
- body: JSON.stringify(request),
184
- })
185
- }
186
- setSessionWake(
187
- repo: string,
188
- sid: string,
189
- request: OperatorSessionWakeRequest,
190
- ): Promise<{ ok: boolean }> {
191
- return this.request(repo, `sessions/${encodeURIComponent(sid)}/wake`, {
192
- method: 'PUT',
193
- body: JSON.stringify(request),
194
- })
195
- }
196
- answerSessionApproval(
197
- repo: string,
198
- sid: string,
199
- request: OperatorSessionApprovalRequest,
200
- ): Promise<{ ok: boolean }> {
201
- return this.request(repo, `sessions/${encodeURIComponent(sid)}/approvals`, {
202
- method: 'POST',
203
- body: JSON.stringify(request),
204
- })
205
- }
206
- archiveSession(repo: string, sid: string): Promise<{ ok: boolean }> {
207
- return this.request(repo, `sessions/${encodeURIComponent(sid)}/archive`, {
208
- method: 'POST',
209
- })
210
- }
211
- readSessionTurnStream(
212
- repo: string,
213
- sid: string,
214
- turn: string,
215
- opts: { since?: number; waitSeconds?: number } = {},
216
- ): Promise<StreamRead> {
217
- const query = new URLSearchParams({ since: String(opts?.since ?? 0) })
218
- if (opts?.waitSeconds !== undefined) query.set('wait', String(opts.waitSeconds))
219
- return this.request(
220
- repo,
221
- `sessions/${encodeURIComponent(sid)}/turns/${encodeURIComponent(turn)}/stream?${query}`,
222
- )
223
- }
224
- controlBuild(
225
- repo: string,
226
- slug: string,
227
- request: OperatorBuildControlRequest,
228
- ): Promise<BuildControlResult> {
229
- return this.request(repo, `builds/${encodeURIComponent(slug)}/control`, {
230
- method: 'POST',
231
- body: JSON.stringify(request),
232
- })
233
- }
234
- /** The registry's generic protocol face: execute any tool from the closed
235
- * table by name. The body is the tool's input (repo included); failures
236
- * parse into OperatorApiError exactly like the typed routes. */
237
- callTool(repo: string, tool: string, input: unknown): Promise<unknown> {
238
- return this.request(repo, `tools/${encodeURIComponent(tool)}`, {
239
- method: 'POST',
240
- body: JSON.stringify(input),
241
- })
242
- }
243
- answer(repo: string, slug: string, request: OperatorAnswerRequest): Promise<BuildControlResult> {
244
- return this.request(repo, `builds/${encodeURIComponent(slug)}/answer`, {
245
- method: 'POST',
246
- body: JSON.stringify(request),
247
- })
248
- }
249
- setIntake(repo: string, enabled: boolean): Promise<unknown> {
250
- return this.request(repo, 'settings/intake', {
251
- method: 'PUT',
252
- body: JSON.stringify({ enabled }),
253
- })
254
- }
255
- toggleIntake(repo: string): Promise<unknown> {
256
- return this.request(repo, 'settings/intake/toggle', { method: 'POST' })
257
- }
258
- setAutoMergeDefault(repo: string, enabled: boolean): Promise<unknown> {
259
- return this.request(repo, 'settings/auto-merge-default', {
260
- method: 'PUT',
261
- body: JSON.stringify({ enabled }),
262
- })
263
- }
264
- toggleAutoMergeDefault(repo: string): Promise<unknown> {
265
- return this.request(repo, 'settings/auto-merge-default/toggle', { method: 'POST' })
266
- }
267
- bulkControl(repo: string, action: 'pause' | 'resume'): Promise<BulkControlSummary> {
268
- return this.request(repo, 'bulk-control', { method: 'POST', body: JSON.stringify({ action }) })
269
- }
270
- toggleHarvest(repo: string): Promise<unknown> {
271
- return this.request(repo, 'harvest/control', {
272
- method: 'POST',
273
- body: JSON.stringify({ action: 'toggle-gate' }),
274
- })
275
- }
276
- controlHarvestRun(repo: string, run: string): Promise<unknown> {
277
- return this.request(repo, 'harvest/control', {
278
- method: 'POST',
279
- body: JSON.stringify({ action: 'run', run }),
280
- })
281
- }
282
- async downloadArtifact(
283
- repo: string,
284
- slug: string,
285
- kind: string,
286
- rev?: number,
287
- ): Promise<DownloadedArtifact> {
288
- const suffix = `builds/${encodeURIComponent(slug)}/artifacts/${encodeURIComponent(kind)}${rev === undefined ? '' : `?rev=${rev}`}`
289
- const headers = new Headers({
290
- [AUTOBUILD_VERSION_HEADER]: AUTOBUILD_VERSION,
291
- [REMOTE_STORE_PROTOCOL_VERSION_HEADER]: REMOTE_STORE_PROTOCOL_VERSION,
292
- authorization: `Bearer ${this.options.token}`,
293
- })
294
- const response = await this.fetchFn(`${this.base}${this.repoPath(repo, suffix)}`, { headers })
295
- if (!response.ok) {
296
- const parsed = operatorErrorSchema.parse(await response.json())
297
- const error = new OperatorApiError(response.status, parsed.kind, parsed.code, parsed.progress)
298
- error.message = parsed.error
299
- throw error
300
- }
301
- return {
302
- content: new Uint8Array(await response.arrayBuffer()),
303
- kind: response.headers.get('x-autobuild-artifact-kind') ?? kind,
304
- revision: Number(response.headers.get('x-autobuild-artifact-revision')),
305
- blobRef: response.headers.get('x-autobuild-artifact-blob-ref') ?? '',
306
- ...(response.headers.get('content-disposition') !== null
307
- ? { disposition: response.headers.get('content-disposition')! }
308
- : {}),
309
- }
310
- }
311
- }