@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,425 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local ambient phase-session wrapper for a BuildStore.
|
|
3
|
+
*
|
|
4
|
+
* This module's "session" is the CLI's validated ambient phase/Harvest
|
|
5
|
+
* identity (agent-session attribution): it scopes a local store handle to the
|
|
6
|
+
* exact build or repository resource and rejects agent-attributed event
|
|
7
|
+
* writes for any other `AB_SESSION`. It is unrelated to the operator-session
|
|
8
|
+
* scope handles in `session-handle.ts` (hosted-only, AUT-339), which scope a
|
|
9
|
+
* store to a single operator session's record.
|
|
10
|
+
*/
|
|
11
|
+
import type { EventType } from '../events/payloads'
|
|
12
|
+
import type { EventWrite } from '../events/catalog'
|
|
13
|
+
import type { RepositoryEventType, RepositoryEventWrite } from '../events/repository'
|
|
14
|
+
import type { AbEvent, EventEnvelope } from '../events/catalog'
|
|
15
|
+
import type { RepositoryEvent, RepositoryEventEnvelope } from '../events/repository'
|
|
16
|
+
import type {
|
|
17
|
+
SessionEvent,
|
|
18
|
+
SessionEventEnvelope,
|
|
19
|
+
SessionEventType,
|
|
20
|
+
SessionEventWrite,
|
|
21
|
+
} from '../events/sessions'
|
|
22
|
+
import type {
|
|
23
|
+
Artifact,
|
|
24
|
+
ArtifactInput,
|
|
25
|
+
ArtifactMeta,
|
|
26
|
+
BuildRecord,
|
|
27
|
+
BuildStore,
|
|
28
|
+
NewBuildInput,
|
|
29
|
+
NewSessionInput,
|
|
30
|
+
RepositoryArtifact,
|
|
31
|
+
RepositoryArtifactMeta,
|
|
32
|
+
RepositoryRecord,
|
|
33
|
+
SessionArtifactMeta,
|
|
34
|
+
SessionRecord,
|
|
35
|
+
SubscribeOptions,
|
|
36
|
+
Unsubscribe,
|
|
37
|
+
} from './types'
|
|
38
|
+
import type {
|
|
39
|
+
StreamChunk,
|
|
40
|
+
StreamOutcome,
|
|
41
|
+
StreamPart,
|
|
42
|
+
StreamRead,
|
|
43
|
+
StreamRecord,
|
|
44
|
+
StreamScope,
|
|
45
|
+
} from './streams/types'
|
|
46
|
+
|
|
47
|
+
export type PhaseSessionScope =
|
|
48
|
+
| { kind: 'build'; id: string; session: string }
|
|
49
|
+
| { kind: 'repo'; id: string; session: string }
|
|
50
|
+
|
|
51
|
+
/** An in-process authority failure for a local phase-session store handle. */
|
|
52
|
+
export class PhaseSessionError extends Error {
|
|
53
|
+
constructor(
|
|
54
|
+
readonly scope: PhaseSessionScope,
|
|
55
|
+
readonly operation: string,
|
|
56
|
+
readonly target?: { kind: 'build' | 'repo' | 'admin'; id?: string },
|
|
57
|
+
message?: string,
|
|
58
|
+
) {
|
|
59
|
+
const authority = `${scope.kind} ${JSON.stringify(scope.id)} and session ${JSON.stringify(scope.session)}`
|
|
60
|
+
const addressed =
|
|
61
|
+
target === undefined
|
|
62
|
+
? operation
|
|
63
|
+
: target.kind === 'admin'
|
|
64
|
+
? `${operation} (admin)`
|
|
65
|
+
: `${operation} targeting ${target.kind} ${JSON.stringify(target.id)}`
|
|
66
|
+
super(message ?? `local phase-session store scoped to ${authority} forbids ${addressed}`)
|
|
67
|
+
this.name = 'PhaseSessionError'
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
type BuildPhaseSessionScope = Extract<PhaseSessionScope, { kind: 'build' }>
|
|
72
|
+
type RepositoryPhaseSessionScope = Extract<PhaseSessionScope, { kind: 'repo' }>
|
|
73
|
+
|
|
74
|
+
/** Nested same-build scoping is idempotent while a foreign scope can never
|
|
75
|
+
* widen this ambient-session handle. */
|
|
76
|
+
export interface BuildPhaseSessionStore extends BuildStore {
|
|
77
|
+
readonly phaseSession: BuildPhaseSessionScope
|
|
78
|
+
readonly buildScope: string
|
|
79
|
+
scopeBuild(slug: string): BuildPhaseSessionStore
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface RepositoryPhaseSessionStore extends BuildStore {
|
|
83
|
+
readonly phaseSession: RepositoryPhaseSessionScope
|
|
84
|
+
scopeBuild(slug: string): never
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export type PhaseSessionStore = BuildPhaseSessionStore | RepositoryPhaseSessionStore
|
|
88
|
+
|
|
89
|
+
function actorSession(actor: unknown): string | null {
|
|
90
|
+
if (typeof actor !== 'object' || actor === null) return null
|
|
91
|
+
const { kind, session } = actor as { kind?: unknown; session?: unknown }
|
|
92
|
+
return kind === 'agent' && typeof session === 'string' ? session : null
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Restrict a local Store handle to the exact resource and session supplied by
|
|
97
|
+
* a validated ambient agent identity. Resource checks apply to every method;
|
|
98
|
+
* the session dimension applies only to agent-attributed event writes so the
|
|
99
|
+
* CLI's trusted KERNEL plumbing remains usable.
|
|
100
|
+
*/
|
|
101
|
+
export function scopeLocalStoreToPhaseSession(
|
|
102
|
+
store: BuildStore,
|
|
103
|
+
scope: BuildPhaseSessionScope,
|
|
104
|
+
): BuildPhaseSessionStore
|
|
105
|
+
export function scopeLocalStoreToPhaseSession(
|
|
106
|
+
store: BuildStore,
|
|
107
|
+
scope: RepositoryPhaseSessionScope,
|
|
108
|
+
): RepositoryPhaseSessionStore
|
|
109
|
+
export function scopeLocalStoreToPhaseSession(
|
|
110
|
+
store: BuildStore,
|
|
111
|
+
scope: PhaseSessionScope,
|
|
112
|
+
): PhaseSessionStore
|
|
113
|
+
export function scopeLocalStoreToPhaseSession(
|
|
114
|
+
store: BuildStore,
|
|
115
|
+
scope: PhaseSessionScope,
|
|
116
|
+
): PhaseSessionStore {
|
|
117
|
+
const target = (kind: 'build' | 'repo', id: string) => ({ kind, id }) as const
|
|
118
|
+
const own = (operation: string, kind: 'build' | 'repo', id: string): void => {
|
|
119
|
+
if (scope.kind !== kind || scope.id !== id) {
|
|
120
|
+
throw new PhaseSessionError(scope, operation, target(kind, id))
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
const admin = (operation: string): never => {
|
|
124
|
+
throw new PhaseSessionError(scope, operation, { kind: 'admin' })
|
|
125
|
+
}
|
|
126
|
+
/** Streams carry no actor, so the session dimension does not gate them —
|
|
127
|
+
* only the exact-resource guard does. Addressed operations resolve the
|
|
128
|
+
* target's scope from its record; an unknown id delegates so the backing
|
|
129
|
+
* store's `unknown stream` feedback survives. */
|
|
130
|
+
const ownStreamScope = (record: StreamRecord): void => {
|
|
131
|
+
const matches =
|
|
132
|
+
scope.kind === 'build'
|
|
133
|
+
? record.scope.kind === 'build' && record.scope.build === scope.id
|
|
134
|
+
: record.scope.kind === 'repo' && record.scope.repo === scope.id
|
|
135
|
+
if (!matches) {
|
|
136
|
+
throw new PhaseSessionError(
|
|
137
|
+
scope,
|
|
138
|
+
'stream',
|
|
139
|
+
record.scope.kind === 'build'
|
|
140
|
+
? ({ kind: 'build', id: record.scope.build } as const)
|
|
141
|
+
: record.scope.kind === 'repo'
|
|
142
|
+
? ({ kind: 'repo', id: record.scope.repo } as const)
|
|
143
|
+
: // Session-scoped streams are hosted-only; an ambient phase
|
|
144
|
+
// session never owns one.
|
|
145
|
+
({ kind: 'admin' } as const),
|
|
146
|
+
)
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
const ownStream = async (streamId: string): Promise<void> => {
|
|
150
|
+
const record = await store.getStream(streamId)
|
|
151
|
+
if (record) ownStreamScope(record)
|
|
152
|
+
}
|
|
153
|
+
const streamScope = (): StreamScope =>
|
|
154
|
+
scope.kind === 'build' ? { kind: 'build', build: scope.id } : { kind: 'repo', repo: scope.id }
|
|
155
|
+
const ownStreamScopeArg = (operation: string, candidate: StreamScope): void => {
|
|
156
|
+
const expected = streamScope()
|
|
157
|
+
if (JSON.stringify(candidate) !== JSON.stringify(expected)) {
|
|
158
|
+
throw new PhaseSessionError(
|
|
159
|
+
scope,
|
|
160
|
+
operation,
|
|
161
|
+
candidate.kind === 'build'
|
|
162
|
+
? ({ kind: 'build', id: candidate.build } as const)
|
|
163
|
+
: candidate.kind === 'repo'
|
|
164
|
+
? ({ kind: 'repo', id: candidate.repo } as const)
|
|
165
|
+
: ({ kind: 'admin' } as const),
|
|
166
|
+
)
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
const authorizeActor = (operation: string, actor: unknown): void => {
|
|
170
|
+
if (
|
|
171
|
+
typeof actor !== 'object' ||
|
|
172
|
+
actor === null ||
|
|
173
|
+
(actor as { kind?: unknown }).kind !== 'agent'
|
|
174
|
+
) {
|
|
175
|
+
return
|
|
176
|
+
}
|
|
177
|
+
const session = actorSession(actor)
|
|
178
|
+
if (session !== scope.session) {
|
|
179
|
+
throw new PhaseSessionError(
|
|
180
|
+
scope,
|
|
181
|
+
operation,
|
|
182
|
+
undefined,
|
|
183
|
+
`local phase-session store scoped to session ${JSON.stringify(scope.session)} may not write events attributed to ${
|
|
184
|
+
session === null
|
|
185
|
+
? 'an agent without a valid session'
|
|
186
|
+
: `session ${JSON.stringify(session)}`
|
|
187
|
+
}`,
|
|
188
|
+
)
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const scoped = {
|
|
193
|
+
phaseSession: scope,
|
|
194
|
+
...(scope.kind === 'build' ? { buildScope: scope.id } : {}),
|
|
195
|
+
|
|
196
|
+
scopeBuild(slug: string): BuildPhaseSessionStore {
|
|
197
|
+
own('scopeBuild', 'build', slug)
|
|
198
|
+
return scoped as BuildPhaseSessionStore
|
|
199
|
+
},
|
|
200
|
+
|
|
201
|
+
async createBuild(_input: NewBuildInput): Promise<BuildRecord> {
|
|
202
|
+
return admin('createBuild')
|
|
203
|
+
},
|
|
204
|
+
async getBuild(slug: string): Promise<BuildRecord | null> {
|
|
205
|
+
own('getBuild', 'build', slug)
|
|
206
|
+
return store.getBuild(slug)
|
|
207
|
+
},
|
|
208
|
+
async listBuilds(): Promise<BuildRecord[]> {
|
|
209
|
+
return admin('listBuilds')
|
|
210
|
+
},
|
|
211
|
+
async append<T extends EventType>(
|
|
212
|
+
slug: string,
|
|
213
|
+
event: EventWrite<T>,
|
|
214
|
+
): Promise<EventEnvelope<T>> {
|
|
215
|
+
own('append', 'build', slug)
|
|
216
|
+
authorizeActor('append', event.actor)
|
|
217
|
+
return store.append(slug, event)
|
|
218
|
+
},
|
|
219
|
+
async appendIfCurrent<T extends EventType>(
|
|
220
|
+
slug: string,
|
|
221
|
+
expectedSeq: number,
|
|
222
|
+
event: EventWrite<T>,
|
|
223
|
+
): Promise<EventEnvelope<T> | null> {
|
|
224
|
+
own('appendIfCurrent', 'build', slug)
|
|
225
|
+
authorizeActor('appendIfCurrent', event.actor)
|
|
226
|
+
return store.appendIfCurrent(slug, expectedSeq, event)
|
|
227
|
+
},
|
|
228
|
+
async appendWithArtifacts<T extends EventType>(
|
|
229
|
+
slug: string,
|
|
230
|
+
artifacts: ArtifactInput[],
|
|
231
|
+
makeEvent: (deposited: ArtifactMeta[]) => EventWrite<T>,
|
|
232
|
+
): Promise<{ event: EventEnvelope<T>; artifacts: ArtifactMeta[] }> {
|
|
233
|
+
own('appendWithArtifacts', 'build', slug)
|
|
234
|
+
return store.appendWithArtifacts(slug, artifacts, (deposited) => {
|
|
235
|
+
const event = makeEvent(deposited)
|
|
236
|
+
authorizeActor('appendWithArtifacts', event.actor)
|
|
237
|
+
return event
|
|
238
|
+
})
|
|
239
|
+
},
|
|
240
|
+
async getEvents(
|
|
241
|
+
slug: string,
|
|
242
|
+
sinceSeq?: number,
|
|
243
|
+
opts?: { waitSeconds?: number; signal?: AbortSignal },
|
|
244
|
+
): Promise<AbEvent[]> {
|
|
245
|
+
own('getEvents', 'build', slug)
|
|
246
|
+
return store.getEvents(slug, sinceSeq, opts)
|
|
247
|
+
},
|
|
248
|
+
async putArtifact(slug: string, artifact: ArtifactInput): Promise<ArtifactMeta> {
|
|
249
|
+
own('putArtifact', 'build', slug)
|
|
250
|
+
return store.putArtifact(slug, artifact)
|
|
251
|
+
},
|
|
252
|
+
async getArtifact(slug: string, kind: string, rev?: number): Promise<Artifact | null> {
|
|
253
|
+
own('getArtifact', 'build', slug)
|
|
254
|
+
return store.getArtifact(slug, kind, rev)
|
|
255
|
+
},
|
|
256
|
+
async listArtifacts(slug: string, kind?: string): Promise<ArtifactMeta[]> {
|
|
257
|
+
own('listArtifacts', 'build', slug)
|
|
258
|
+
return store.listArtifacts(slug, kind)
|
|
259
|
+
},
|
|
260
|
+
async claimLease(slug: string, holder: string, ttlMs: number): Promise<boolean> {
|
|
261
|
+
own('claimLease', 'build', slug)
|
|
262
|
+
return store.claimLease(slug, holder, ttlMs)
|
|
263
|
+
},
|
|
264
|
+
async heartbeat(slug: string, holder: string): Promise<boolean> {
|
|
265
|
+
own('heartbeat', 'build', slug)
|
|
266
|
+
return store.heartbeat(slug, holder)
|
|
267
|
+
},
|
|
268
|
+
async releaseLease(slug: string, holder: string): Promise<void> {
|
|
269
|
+
own('releaseLease', 'build', slug)
|
|
270
|
+
return store.releaseLease(slug, holder)
|
|
271
|
+
},
|
|
272
|
+
subscribe(
|
|
273
|
+
slug: string,
|
|
274
|
+
opts: SubscribeOptions,
|
|
275
|
+
onEvent: (event: AbEvent) => void,
|
|
276
|
+
): Unsubscribe {
|
|
277
|
+
own('subscribe', 'build', slug)
|
|
278
|
+
return store.subscribe(slug, opts, onEvent)
|
|
279
|
+
},
|
|
280
|
+
|
|
281
|
+
async ensureRepo(_repo: string): Promise<RepositoryRecord> {
|
|
282
|
+
return admin('ensureRepo')
|
|
283
|
+
},
|
|
284
|
+
async getRepo(repo: string): Promise<RepositoryRecord | null> {
|
|
285
|
+
own('getRepo', 'repo', repo)
|
|
286
|
+
return store.getRepo(repo)
|
|
287
|
+
},
|
|
288
|
+
async appendRepo<T extends RepositoryEventType>(
|
|
289
|
+
repo: string,
|
|
290
|
+
event: RepositoryEventWrite<T>,
|
|
291
|
+
): Promise<RepositoryEventEnvelope<T>> {
|
|
292
|
+
own('appendRepo', 'repo', repo)
|
|
293
|
+
authorizeActor('appendRepo', event.actor)
|
|
294
|
+
return store.appendRepo(repo, event)
|
|
295
|
+
},
|
|
296
|
+
async appendRepoWithArtifacts<T extends RepositoryEventType>(
|
|
297
|
+
repo: string,
|
|
298
|
+
artifacts: ArtifactInput[],
|
|
299
|
+
makeEvent: (deposited: RepositoryArtifactMeta[]) => RepositoryEventWrite<T>,
|
|
300
|
+
): Promise<{
|
|
301
|
+
event: RepositoryEventEnvelope<T>
|
|
302
|
+
artifacts: RepositoryArtifactMeta[]
|
|
303
|
+
}> {
|
|
304
|
+
own('appendRepoWithArtifacts', 'repo', repo)
|
|
305
|
+
return store.appendRepoWithArtifacts(repo, artifacts, (deposited) => {
|
|
306
|
+
const event = makeEvent(deposited)
|
|
307
|
+
authorizeActor('appendRepoWithArtifacts', event.actor)
|
|
308
|
+
return event
|
|
309
|
+
})
|
|
310
|
+
},
|
|
311
|
+
async getRepoEvents(
|
|
312
|
+
repo: string,
|
|
313
|
+
sinceSeq?: number,
|
|
314
|
+
opts?: { waitSeconds?: number; signal?: AbortSignal },
|
|
315
|
+
): Promise<RepositoryEvent[]> {
|
|
316
|
+
own('getRepoEvents', 'repo', repo)
|
|
317
|
+
return store.getRepoEvents(repo, sinceSeq, opts)
|
|
318
|
+
},
|
|
319
|
+
async putRepoArtifact(repo: string, artifact: ArtifactInput): Promise<RepositoryArtifactMeta> {
|
|
320
|
+
own('putRepoArtifact', 'repo', repo)
|
|
321
|
+
return store.putRepoArtifact(repo, artifact)
|
|
322
|
+
},
|
|
323
|
+
async getRepoArtifact(
|
|
324
|
+
repo: string,
|
|
325
|
+
kind: string,
|
|
326
|
+
rev?: number,
|
|
327
|
+
): Promise<RepositoryArtifact | null> {
|
|
328
|
+
own('getRepoArtifact', 'repo', repo)
|
|
329
|
+
return store.getRepoArtifact(repo, kind, rev)
|
|
330
|
+
},
|
|
331
|
+
async listRepoArtifacts(repo: string, kind?: string): Promise<RepositoryArtifactMeta[]> {
|
|
332
|
+
own('listRepoArtifacts', 'repo', repo)
|
|
333
|
+
return store.listRepoArtifacts(repo, kind)
|
|
334
|
+
},
|
|
335
|
+
async claimRepoLease(repo: string, holder: string, ttlMs: number): Promise<boolean> {
|
|
336
|
+
own('claimRepoLease', 'repo', repo)
|
|
337
|
+
return store.claimRepoLease(repo, holder, ttlMs)
|
|
338
|
+
},
|
|
339
|
+
async heartbeatRepo(repo: string, holder: string): Promise<boolean> {
|
|
340
|
+
own('heartbeatRepo', 'repo', repo)
|
|
341
|
+
return store.heartbeatRepo(repo, holder)
|
|
342
|
+
},
|
|
343
|
+
async releaseRepoLease(repo: string, holder: string): Promise<void> {
|
|
344
|
+
own('releaseRepoLease', 'repo', repo)
|
|
345
|
+
return store.releaseRepoLease(repo, holder)
|
|
346
|
+
},
|
|
347
|
+
|
|
348
|
+
async createStream(candidate: StreamScope, label: string): Promise<StreamRecord> {
|
|
349
|
+
ownStreamScopeArg('createStream', candidate)
|
|
350
|
+
return store.createStream(candidate, label)
|
|
351
|
+
},
|
|
352
|
+
async appendStreamParts(streamId: string, parts: StreamPart[]): Promise<StreamChunk> {
|
|
353
|
+
await ownStream(streamId)
|
|
354
|
+
return store.appendStreamParts(streamId, parts)
|
|
355
|
+
},
|
|
356
|
+
async readStream(
|
|
357
|
+
streamId: string,
|
|
358
|
+
opts?: { since?: number; waitSeconds?: number },
|
|
359
|
+
): Promise<StreamRead> {
|
|
360
|
+
await ownStream(streamId)
|
|
361
|
+
return store.readStream(streamId, opts)
|
|
362
|
+
},
|
|
363
|
+
async closeStream(streamId: string, outcome: StreamOutcome): Promise<StreamRecord> {
|
|
364
|
+
await ownStream(streamId)
|
|
365
|
+
return store.closeStream(streamId, outcome)
|
|
366
|
+
},
|
|
367
|
+
async getStream(streamId: string): Promise<StreamRecord | null> {
|
|
368
|
+
await ownStream(streamId)
|
|
369
|
+
return store.getStream(streamId)
|
|
370
|
+
},
|
|
371
|
+
async listStreams(candidate: StreamScope): Promise<StreamRecord[]> {
|
|
372
|
+
ownStreamScopeArg('listStreams', candidate)
|
|
373
|
+
return store.listStreams(candidate)
|
|
374
|
+
},
|
|
375
|
+
|
|
376
|
+
// Operator sessions are hosted-only: an ambient phase-session handle —
|
|
377
|
+
// a local construct — never creates or touches one.
|
|
378
|
+
createSession(_input: NewSessionInput): Promise<SessionRecord> {
|
|
379
|
+
return admin('createSession')
|
|
380
|
+
},
|
|
381
|
+
getSession(_id: string): Promise<SessionRecord | null> {
|
|
382
|
+
return admin('getSession')
|
|
383
|
+
},
|
|
384
|
+
listSessions(_repo: string): Promise<SessionRecord[]> {
|
|
385
|
+
return admin('listSessions')
|
|
386
|
+
},
|
|
387
|
+
appendSessionEvent<T extends SessionEventType>(
|
|
388
|
+
_id: string,
|
|
389
|
+
_event: SessionEventWrite<T>,
|
|
390
|
+
): Promise<SessionEventEnvelope<T>> {
|
|
391
|
+
return admin('appendSessionEvent')
|
|
392
|
+
},
|
|
393
|
+
getSessionEvents(_id: string, _sinceSeq?: number): Promise<SessionEvent[]> {
|
|
394
|
+
return admin('getSessionEvents')
|
|
395
|
+
},
|
|
396
|
+
appendSessionWithArtifacts<T extends SessionEventType>(
|
|
397
|
+
_id: string,
|
|
398
|
+
_artifacts: ArtifactInput[],
|
|
399
|
+
_makeEvent: (deposited: SessionArtifactMeta[]) => SessionEventWrite<T>,
|
|
400
|
+
): Promise<{ event: SessionEventEnvelope<T>; artifacts: SessionArtifactMeta[] }> {
|
|
401
|
+
return admin('appendSessionWithArtifacts')
|
|
402
|
+
},
|
|
403
|
+
putSessionArtifact(_id: string, _artifact: ArtifactInput): Promise<SessionArtifactMeta> {
|
|
404
|
+
return admin('putSessionArtifact')
|
|
405
|
+
},
|
|
406
|
+
getSessionArtifact(_id: string, _kind: string, _rev?: number): Promise<null> {
|
|
407
|
+
return admin('getSessionArtifact')
|
|
408
|
+
},
|
|
409
|
+
listSessionArtifacts(_id: string, _kind?: string): Promise<SessionArtifactMeta[]> {
|
|
410
|
+
return admin('listSessionArtifacts')
|
|
411
|
+
},
|
|
412
|
+
scopeSession(_id: string): never {
|
|
413
|
+
return admin('scopeSession')
|
|
414
|
+
},
|
|
415
|
+
|
|
416
|
+
close(): Promise<void> {
|
|
417
|
+
return store.close()
|
|
418
|
+
},
|
|
419
|
+
} satisfies BuildStore & {
|
|
420
|
+
readonly phaseSession: PhaseSessionScope
|
|
421
|
+
readonly buildScope?: string
|
|
422
|
+
scopeBuild(slug: string): BuildPhaseSessionStore
|
|
423
|
+
}
|
|
424
|
+
return scoped as PhaseSessionStore
|
|
425
|
+
}
|