@cat-factory/contracts 0.274.0 → 0.276.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/dist/accountSettings.d.ts +156 -22
- package/dist/accountSettings.d.ts.map +1 -1
- package/dist/accountSettings.js +48 -5
- package/dist/accountSettings.js.map +1 -1
- package/dist/errors.d.ts +16 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +16 -0
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/notification-routing.d.ts +100 -0
- package/dist/notification-routing.d.ts.map +1 -0
- package/dist/notification-routing.js +155 -0
- package/dist/notification-routing.js.map +1 -0
- package/dist/public-api.d.ts +137 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +74 -0
- package/dist/public-api.js.map +1 -1
- package/dist/public-board.d.ts +276 -0
- package/dist/public-board.d.ts.map +1 -0
- package/dist/public-board.js +215 -0
- package/dist/public-board.js.map +1 -0
- package/dist/public-evidence.d.ts +30 -3
- package/dist/public-evidence.d.ts.map +1 -1
- package/dist/public-evidence.js +27 -3
- package/dist/public-evidence.js.map +1 -1
- package/dist/requests.d.ts +19 -2
- package/dist/requests.d.ts.map +1 -1
- package/dist/requests.js +18 -1
- package/dist/requests.js.map +1 -1
- package/dist/routes/accounts.d.ts +81 -15
- package/dist/routes/accounts.d.ts.map +1 -1
- package/dist/routes/board.d.ts +6 -2
- package/dist/routes/board.d.ts.map +1 -1
- package/dist/routes/index.d.ts +1 -0
- package/dist/routes/index.d.ts.map +1 -1
- package/dist/routes/index.js +1 -0
- package/dist/routes/index.js.map +1 -1
- package/dist/routes/notifications.d.ts +76 -0
- package/dist/routes/notifications.d.ts.map +1 -1
- package/dist/routes/notifications.js +15 -0
- package/dist/routes/notifications.js.map +1 -1
- package/dist/routes/public-api.d.ts +18 -0
- package/dist/routes/public-api.d.ts.map +1 -1
- package/dist/routes/public-board.d.ts +369 -0
- package/dist/routes/public-board.d.ts.map +1 -0
- package/dist/routes/public-board.js +112 -0
- package/dist/routes/public-board.js.map +1 -0
- package/dist/routes/public-evidence.d.ts +1 -0
- package/dist/routes/public-evidence.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/gate-parking.d.ts +0 -30
- package/dist/gate-parking.d.ts.map +0 -1
- package/dist/gate-parking.js +0 -58
- package/dist/gate-parking.js.map +0 -1
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import * as v from 'valibot';
|
|
2
|
+
/**
|
|
3
|
+
* The repository a new service frame is backed by.
|
|
4
|
+
*
|
|
5
|
+
* `repoId` is the provider's own id for the repo, as `GET /api/v1/repos` serves it: the neutral
|
|
6
|
+
* name for what the internal projection calls `githubId`. It is deliberately not an `owner/name`
|
|
7
|
+
* pair: a repo can be renamed or transferred without changing its id, and a caller that held a name
|
|
8
|
+
* would silently create a service against a different repository after such a move.
|
|
9
|
+
*/
|
|
10
|
+
export declare const publicServiceRepoSchema: v.ObjectSchema<{
|
|
11
|
+
/** The repo's provider id, from `GET /api/v1/repos`. */
|
|
12
|
+
readonly repoId: v.NumberSchema<undefined>;
|
|
13
|
+
/**
|
|
14
|
+
* For a MONOREPO, the subdirectory (relative to the repo root) this service lives in, e.g.
|
|
15
|
+
* `packages/api`. Required when the repo is a monorepo and refused for a whole-repo service,
|
|
16
|
+
* because it is what scopes each agent's working directory: a monorepo backs one service per
|
|
17
|
+
* subdirectory, and two services claiming the same one would fight over the same subtree.
|
|
18
|
+
*/
|
|
19
|
+
readonly directory: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.TrimAction, v.MaxLengthAction<string, 400, undefined>]>, undefined>;
|
|
20
|
+
/**
|
|
21
|
+
* Whether the repository hosts SEVERAL services. Sent with the create rather than as a separate
|
|
22
|
+
* up-front write; when supplied it is persisted on the repo, so the flag a later create reads is
|
|
23
|
+
* the one this call set. Omitted ⇒ whatever the repo already says.
|
|
24
|
+
*/
|
|
25
|
+
readonly monorepo: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
|
|
26
|
+
}, undefined>;
|
|
27
|
+
export type PublicServiceRepo = v.InferOutput<typeof publicServiceRepoSchema>;
|
|
28
|
+
/**
|
|
29
|
+
* Create a board service (a service frame), optionally backed by a repository.
|
|
30
|
+
*
|
|
31
|
+
* Without `repo` the frame is a structural placeholder: it can hold tasks, and a run started on one
|
|
32
|
+
* of them is REFUSED, because execution resolves a task's repository by walking up to the enclosing
|
|
33
|
+
* service frame and there is nothing there. That is a legitimate intermediate state (a caller
|
|
34
|
+
* mapping out a board before its repositories exist) and it is why the field is optional rather
|
|
35
|
+
* than why it should usually be omitted.
|
|
36
|
+
*
|
|
37
|
+
* There is deliberately no `position`: see the note at the top of this file.
|
|
38
|
+
*/
|
|
39
|
+
export declare const createPublicServiceSchema: v.ObjectSchema<{
|
|
40
|
+
/**
|
|
41
|
+
* The service's name. Optional, because a repo-backed service is named after the repository (or,
|
|
42
|
+
* for a monorepo service, after its subdirectory) exactly as the app's import does, and a caller
|
|
43
|
+
* that has no better name should get that one rather than being made to invent it.
|
|
44
|
+
*/
|
|
45
|
+
readonly title: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.TrimAction, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 200, undefined>]>, undefined>;
|
|
46
|
+
/** What the service is, for the agents that read it as context. Defaults to a generated line. */
|
|
47
|
+
readonly description: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.TrimAction, v.MaxLengthAction<string, 2000, undefined>]>, undefined>;
|
|
48
|
+
/**
|
|
49
|
+
* The service's architectural role. Omitted ⇒ `service`.
|
|
50
|
+
*
|
|
51
|
+
* Narrower than the board's own block types on purpose: this is the set a REPOSITORY can back,
|
|
52
|
+
* which is the only kind of frame this endpoint creates. A `database` or `queue` frame documents
|
|
53
|
+
* infrastructure for the agents to read and runs nothing, so nothing here would create one.
|
|
54
|
+
*/
|
|
55
|
+
readonly type: v.OptionalSchema<v.PicklistSchema<["service", "frontend", "library", "document"], undefined>, undefined>;
|
|
56
|
+
/** The repository backing the service. Omitted ⇒ an unlinked frame (see above). */
|
|
57
|
+
readonly repo: v.OptionalSchema<v.ObjectSchema<{
|
|
58
|
+
/** The repo's provider id, from `GET /api/v1/repos`. */
|
|
59
|
+
readonly repoId: v.NumberSchema<undefined>;
|
|
60
|
+
/**
|
|
61
|
+
* For a MONOREPO, the subdirectory (relative to the repo root) this service lives in, e.g.
|
|
62
|
+
* `packages/api`. Required when the repo is a monorepo and refused for a whole-repo service,
|
|
63
|
+
* because it is what scopes each agent's working directory: a monorepo backs one service per
|
|
64
|
+
* subdirectory, and two services claiming the same one would fight over the same subtree.
|
|
65
|
+
*/
|
|
66
|
+
readonly directory: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.TrimAction, v.MaxLengthAction<string, 400, undefined>]>, undefined>;
|
|
67
|
+
/**
|
|
68
|
+
* Whether the repository hosts SEVERAL services. Sent with the create rather than as a separate
|
|
69
|
+
* up-front write; when supplied it is persisted on the repo, so the flag a later create reads is
|
|
70
|
+
* the one this call set. Omitted ⇒ whatever the repo already says.
|
|
71
|
+
*/
|
|
72
|
+
readonly monorepo: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
|
|
73
|
+
}, undefined>, undefined>;
|
|
74
|
+
}, undefined>;
|
|
75
|
+
export type CreatePublicServiceInput = v.InferOutput<typeof createPublicServiceSchema>;
|
|
76
|
+
/**
|
|
77
|
+
* A repository this workspace can back a service with, as `GET /api/v1/repos` lists it.
|
|
78
|
+
*
|
|
79
|
+
* The discovery half of {@link publicServiceRepoSchema}, and it is why service creation is usable
|
|
80
|
+
* headlessly at all: the create takes a `repoId`, and until this existed the only way to learn one
|
|
81
|
+
* was to open the app. Deliberately a small projection (enough to recognise a repository and pass
|
|
82
|
+
* it back), not a mirror of the internal projection row, which carries installation ids and
|
|
83
|
+
* sync bookkeeping that are this platform's business rather than a caller's.
|
|
84
|
+
*/
|
|
85
|
+
export declare const publicRepoSchema: v.ObjectSchema<{
|
|
86
|
+
/** The provider's id for the repo: the value to pass as `repo.repoId`. */
|
|
87
|
+
readonly repoId: v.NumberSchema<undefined>;
|
|
88
|
+
/** Which provider it lives on (`github` / `gitlab`). */
|
|
89
|
+
readonly provider: v.StringSchema<undefined>;
|
|
90
|
+
readonly owner: v.StringSchema<undefined>;
|
|
91
|
+
readonly name: v.StringSchema<undefined>;
|
|
92
|
+
/**
|
|
93
|
+
* The branch a run's work is based on and merged into, or EMPTY when the projection has not
|
|
94
|
+
* recorded one yet (a repository connected moments ago, before its first sync). Empty rather
|
|
95
|
+
* than null because there is nothing here that could invent a default, and a caller reading it
|
|
96
|
+
* to name a base needs to see that it has to ask the provider rather than assume `main`.
|
|
97
|
+
*/
|
|
98
|
+
readonly defaultBranch: v.StringSchema<undefined>;
|
|
99
|
+
/** Whether the repository is private on its provider. */
|
|
100
|
+
readonly private: v.BooleanSchema<undefined>;
|
|
101
|
+
/** Whether the repo is flagged as hosting several services (see `repo.monorepo`). */
|
|
102
|
+
readonly monorepo: v.BooleanSchema<undefined>;
|
|
103
|
+
/**
|
|
104
|
+
* The service this repository already backs ON THIS BOARD, or null.
|
|
105
|
+
*
|
|
106
|
+
* Present because a whole-repo repository backs at most ONE service, so a caller choosing one to
|
|
107
|
+
* create against needs to know which choices are already spent, and, more usefully, because a
|
|
108
|
+
* caller re-running its provisioning finds the service it created last time here rather than
|
|
109
|
+
* discovering it through a `409`. A monorepo answers null even when its subdirectories back
|
|
110
|
+
* services, since it can back more.
|
|
111
|
+
*
|
|
112
|
+
* Null and {@link linkedElsewhere} together are the honest answer when the service is homed on
|
|
113
|
+
* another board of the account: read the flag before treating null as "available".
|
|
114
|
+
*/
|
|
115
|
+
readonly serviceId: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
116
|
+
/**
|
|
117
|
+
* True when this repository already backs a whole-repo service homed on ANOTHER board of the
|
|
118
|
+
* account, so `POST /api/v1/services` will refuse it (`reason: repo_service_homed_elsewhere`).
|
|
119
|
+
*
|
|
120
|
+
* A service is account-owned and a board can MOUNT one homed elsewhere, but every read on this
|
|
121
|
+
* API is scoped to the calling key's own workspace, so a frame homed on another board has no id
|
|
122
|
+
* this surface could hand back: it would not appear in `GET /api/v1/services` and
|
|
123
|
+
* `POST /api/v1/services/{serviceId}/tasks` would 404 on it. Hence a flag rather than a second
|
|
124
|
+
* id field — this states that the choice is spent without naming an address that does not work
|
|
125
|
+
* here. Use the board that homes the service, or a key scoped to it.
|
|
126
|
+
*/
|
|
127
|
+
readonly linkedElsewhere: v.BooleanSchema<undefined>;
|
|
128
|
+
}, undefined>;
|
|
129
|
+
export type PublicRepo = v.InferOutput<typeof publicRepoSchema>;
|
|
130
|
+
export declare const publicRepoListSchema: v.ObjectSchema<{
|
|
131
|
+
readonly repos: v.ArraySchema<v.ObjectSchema<{
|
|
132
|
+
/** The provider's id for the repo: the value to pass as `repo.repoId`. */
|
|
133
|
+
readonly repoId: v.NumberSchema<undefined>;
|
|
134
|
+
/** Which provider it lives on (`github` / `gitlab`). */
|
|
135
|
+
readonly provider: v.StringSchema<undefined>;
|
|
136
|
+
readonly owner: v.StringSchema<undefined>;
|
|
137
|
+
readonly name: v.StringSchema<undefined>;
|
|
138
|
+
/**
|
|
139
|
+
* The branch a run's work is based on and merged into, or EMPTY when the projection has not
|
|
140
|
+
* recorded one yet (a repository connected moments ago, before its first sync). Empty rather
|
|
141
|
+
* than null because there is nothing here that could invent a default, and a caller reading it
|
|
142
|
+
* to name a base needs to see that it has to ask the provider rather than assume `main`.
|
|
143
|
+
*/
|
|
144
|
+
readonly defaultBranch: v.StringSchema<undefined>;
|
|
145
|
+
/** Whether the repository is private on its provider. */
|
|
146
|
+
readonly private: v.BooleanSchema<undefined>;
|
|
147
|
+
/** Whether the repo is flagged as hosting several services (see `repo.monorepo`). */
|
|
148
|
+
readonly monorepo: v.BooleanSchema<undefined>;
|
|
149
|
+
/**
|
|
150
|
+
* The service this repository already backs ON THIS BOARD, or null.
|
|
151
|
+
*
|
|
152
|
+
* Present because a whole-repo repository backs at most ONE service, so a caller choosing one to
|
|
153
|
+
* create against needs to know which choices are already spent, and, more usefully, because a
|
|
154
|
+
* caller re-running its provisioning finds the service it created last time here rather than
|
|
155
|
+
* discovering it through a `409`. A monorepo answers null even when its subdirectories back
|
|
156
|
+
* services, since it can back more.
|
|
157
|
+
*
|
|
158
|
+
* Null and {@link linkedElsewhere} together are the honest answer when the service is homed on
|
|
159
|
+
* another board of the account: read the flag before treating null as "available".
|
|
160
|
+
*/
|
|
161
|
+
readonly serviceId: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
162
|
+
/**
|
|
163
|
+
* True when this repository already backs a whole-repo service homed on ANOTHER board of the
|
|
164
|
+
* account, so `POST /api/v1/services` will refuse it (`reason: repo_service_homed_elsewhere`).
|
|
165
|
+
*
|
|
166
|
+
* A service is account-owned and a board can MOUNT one homed elsewhere, but every read on this
|
|
167
|
+
* API is scoped to the calling key's own workspace, so a frame homed on another board has no id
|
|
168
|
+
* this surface could hand back: it would not appear in `GET /api/v1/services` and
|
|
169
|
+
* `POST /api/v1/services/{serviceId}/tasks` would 404 on it. Hence a flag rather than a second
|
|
170
|
+
* id field — this states that the choice is spent without naming an address that does not work
|
|
171
|
+
* here. Use the board that homes the service, or a key scoped to it.
|
|
172
|
+
*/
|
|
173
|
+
readonly linkedElsewhere: v.BooleanSchema<undefined>;
|
|
174
|
+
}, undefined>, undefined>;
|
|
175
|
+
}, undefined>;
|
|
176
|
+
export type PublicRepoList = v.InferOutput<typeof publicRepoListSchema>;
|
|
177
|
+
/**
|
|
178
|
+
* Declare that a task must wait for another one.
|
|
179
|
+
*
|
|
180
|
+
* The gap it closes: an integration filing five related tasks and starting them got five runs
|
|
181
|
+
* racing against one repository, each opening a pull request against a base the others were
|
|
182
|
+
* moving. The platform has had the mechanism to serialise them (the engine's start gate refuses a
|
|
183
|
+
* task whose blockers are not `done`, and `autoStartDependents` starts a task when its blocker
|
|
184
|
+
* merges) and no way for an external caller to be told about it.
|
|
185
|
+
*
|
|
186
|
+
* The edge is stored on the DEPENDENT: `POST /api/v1/tasks/{taskId}/dependencies` says "this task
|
|
187
|
+
* waits for `dependsOnTaskId`". Both ends must be tasks (only a task ever reaches `done`, so an
|
|
188
|
+
* edge onto a service or a module would wedge the run's start gate forever), and an edge that would
|
|
189
|
+
* close a cycle is refused, so the gate and the auto-start can never deadlock.
|
|
190
|
+
*/
|
|
191
|
+
export declare const publicTaskDependencySchema: v.ObjectSchema<{
|
|
192
|
+
/** The task that must finish first. Must be a task in the same workspace, and not this one. */
|
|
193
|
+
readonly dependsOnTaskId: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.TrimAction, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 120, undefined>]>;
|
|
194
|
+
}, undefined>;
|
|
195
|
+
export type PublicTaskDependencyInput = v.InferOutput<typeof publicTaskDependencySchema>;
|
|
196
|
+
/**
|
|
197
|
+
* A document attached to a task as agent context, as the task's document list serves it.
|
|
198
|
+
*
|
|
199
|
+
* Identified by `(source, externalId)` rather than by an id of its own, because that pair IS a
|
|
200
|
+
* projected document's identity: a re-import of the same page lands on the same row, which is what
|
|
201
|
+
* makes attaching one idempotent. `source` is `upload` for a body a caller carried rather than
|
|
202
|
+
* named, and such a document has no page behind it, which is why `url` is empty there rather than
|
|
203
|
+
* absent.
|
|
204
|
+
*/
|
|
205
|
+
export declare const publicAttachedDocumentSchema: v.ObjectSchema<{
|
|
206
|
+
/** Which source the document came from, or `upload` for one a caller carried inline. */
|
|
207
|
+
readonly source: v.PicklistSchema<["confluence", "notion", "github", "figma", "zeplin", "linear", "upload"], undefined>;
|
|
208
|
+
/** The source's stable id for the page: the value to pass back to detach it. */
|
|
209
|
+
readonly externalId: v.StringSchema<undefined>;
|
|
210
|
+
readonly title: v.StringSchema<undefined>;
|
|
211
|
+
/** Canonical URL on the source; EMPTY for an `upload`, which has no page to link back to. */
|
|
212
|
+
readonly url: v.StringSchema<undefined>;
|
|
213
|
+
/** A short plain-text excerpt of the body (the full text reaches the run, not this list). */
|
|
214
|
+
readonly excerpt: v.StringSchema<undefined>;
|
|
215
|
+
}, undefined>;
|
|
216
|
+
export type PublicAttachedDocument = v.InferOutput<typeof publicAttachedDocumentSchema>;
|
|
217
|
+
export declare const publicAttachedDocumentListSchema: v.ObjectSchema<{
|
|
218
|
+
readonly documents: v.ArraySchema<v.ObjectSchema<{
|
|
219
|
+
/** Which source the document came from, or `upload` for one a caller carried inline. */
|
|
220
|
+
readonly source: v.PicklistSchema<["confluence", "notion", "github", "figma", "zeplin", "linear", "upload"], undefined>;
|
|
221
|
+
/** The source's stable id for the page: the value to pass back to detach it. */
|
|
222
|
+
readonly externalId: v.StringSchema<undefined>;
|
|
223
|
+
readonly title: v.StringSchema<undefined>;
|
|
224
|
+
/** Canonical URL on the source; EMPTY for an `upload`, which has no page to link back to. */
|
|
225
|
+
readonly url: v.StringSchema<undefined>;
|
|
226
|
+
/** A short plain-text excerpt of the body (the full text reaches the run, not this list). */
|
|
227
|
+
readonly excerpt: v.StringSchema<undefined>;
|
|
228
|
+
}, undefined>, undefined>;
|
|
229
|
+
}, undefined>;
|
|
230
|
+
export type PublicAttachedDocumentList = v.InferOutput<typeof publicAttachedDocumentListSchema>;
|
|
231
|
+
/**
|
|
232
|
+
* Attach a requirements document to a task that already exists.
|
|
233
|
+
*
|
|
234
|
+
* The same two forms creation takes ({@link publicTaskDocumentSchema}): NAME a page in a connected
|
|
235
|
+
* document source, or CARRY the text. What it adds is the moment: a task's spec routinely arrives
|
|
236
|
+
* after the task does (a ticket filed first and specified later, a PRD that lands mid-review), and
|
|
237
|
+
* until this existed the only way to attach one was to delete the task and file it again, losing
|
|
238
|
+
* the id every stored reference points at, its ticket claim (which then refuses every future filing
|
|
239
|
+
* of that ticket) and the documents it already carried.
|
|
240
|
+
*
|
|
241
|
+
* A document a DIFFERENT live task already holds is refused rather than moved: a document row
|
|
242
|
+
* carries exactly one attachment, so moving it would strip the other task of a document it was
|
|
243
|
+
* created with, with nothing in its next run reporting the absence.
|
|
244
|
+
*/
|
|
245
|
+
export declare const attachPublicTaskDocumentSchema: v.ObjectSchema<{
|
|
246
|
+
readonly document: v.VariantSchema<"kind", [v.ObjectSchema<{
|
|
247
|
+
readonly kind: v.LiteralSchema<"source", undefined>;
|
|
248
|
+
readonly source: v.PicklistSchema<["confluence", "notion", "github", "figma", "zeplin", "linear"], undefined>;
|
|
249
|
+
readonly ref: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.TrimAction, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 500, undefined>]>;
|
|
250
|
+
}, undefined>, v.ObjectSchema<{
|
|
251
|
+
readonly kind: v.LiteralSchema<"upload", undefined>;
|
|
252
|
+
readonly title: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.TrimAction, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 200, undefined>]>;
|
|
253
|
+
readonly content: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.TrimAction, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 100000, undefined>]>;
|
|
254
|
+
}, undefined>], undefined>;
|
|
255
|
+
}, undefined>;
|
|
256
|
+
export type AttachPublicTaskDocumentInput = v.InferOutput<typeof attachPublicTaskDocumentSchema>;
|
|
257
|
+
/**
|
|
258
|
+
* Detach a document from a task, naming it by the `(source, externalId)` pair the task's document
|
|
259
|
+
* list serves.
|
|
260
|
+
*
|
|
261
|
+
* A POST with a body rather than a `DELETE .../documents/{id}`, because a document's identity is
|
|
262
|
+
* two values and one of them is a free-form external id: a Confluence page id is fine in a path
|
|
263
|
+
* segment and a GitHub docs path (`docs/architecture/adr-0001.md`) is not, so half the sources
|
|
264
|
+
* would need escaping rules a caller has to get right to address its own document.
|
|
265
|
+
*
|
|
266
|
+
* The document itself SURVIVES: it stays in the workspace exactly as the app's own detach leaves
|
|
267
|
+
* it, so re-attaching it costs no re-import. Idempotent: detaching a document this task does not
|
|
268
|
+
* hold is a no-op rather than an error, because a caller retrying after a timeout should converge
|
|
269
|
+
* rather than have to distinguish "it was never attached" from "I already detached it".
|
|
270
|
+
*/
|
|
271
|
+
export declare const detachPublicTaskDocumentSchema: v.ObjectSchema<{
|
|
272
|
+
readonly source: v.PicklistSchema<["confluence", "notion", "github", "figma", "zeplin", "linear", "upload"], undefined>;
|
|
273
|
+
readonly externalId: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.TrimAction, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 500, undefined>]>;
|
|
274
|
+
}, undefined>;
|
|
275
|
+
export type DetachPublicTaskDocumentInput = v.InferOutput<typeof detachPublicTaskDocumentSchema>;
|
|
276
|
+
//# sourceMappingURL=public-board.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"public-board.d.ts","sourceRoot":"","sources":["../src/public-board.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AA8B5B;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB;IAClC,wDAAwD;;IAExD;;;;;OAKG;;IAEH;;;;OAIG;;aAEH,CAAA;AACF,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,uBAAuB,CAAC,CAAA;AAE7E;;;;;;;;;;GAUG;AACH,eAAO,MAAM,yBAAyB;IACpC;;;;OAIG;;IAEH,iGAAiG;;IAEjG;;;;;;OAMG;;IAEH,mFAAmF;;QA9CnF,wDAAwD;;QAExD;;;;;WAKG;;QAEH;;;;WAIG;;;aAmCH,CAAA;AACF,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,yBAAyB,CAAC,CAAA;AAEtF;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB;IAC3B,0EAA0E;;IAE1E,wDAAwD;;;;IAIxD;;;;;OAKG;;IAEH,yDAAyD;;IAEzD,qFAAqF;;IAErF;;;;;;;;;;;OAWG;;IAEH;;;;;;;;;;OAUG;;aAEH,CAAA;AACF,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,gBAAgB,CAAC,CAAA;AAE/D,eAAO,MAAM,oBAAoB;;QA7C/B,0EAA0E;;QAE1E,wDAAwD;;;;QAIxD;;;;;WAKG;;QAEH,yDAAyD;;QAEzD,qFAAqF;;QAErF;;;;;;;;;;;WAWG;;QAEH;;;;;;;;;;WAUG;;;aAK6E,CAAA;AAClF,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,oBAAoB,CAAC,CAAA;AAEvE;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,0BAA0B;IACrC,+FAA+F;;aAE/F,CAAA;AACF,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,0BAA0B,CAAC,CAAA;AAExF;;;;;;;;GAQG;AACH,eAAO,MAAM,4BAA4B;IACvC,wFAAwF;;IAExF,gFAAgF;;;IAGhF,6FAA6F;;IAE7F,6FAA6F;;aAE7F,CAAA;AACF,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,4BAA4B,CAAC,CAAA;AAEvF,eAAO,MAAM,gCAAgC;;QAZ3C,wFAAwF;;QAExF,gFAAgF;;;QAGhF,6FAA6F;;QAE7F,6FAA6F;;;aAO7F,CAAA;AACF,MAAM,MAAM,0BAA0B,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,gCAAgC,CAAC,CAAA;AAE/F;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,8BAA8B;;;;;;;;;;aAEzC,CAAA;AACF,MAAM,MAAM,6BAA6B,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,8BAA8B,CAAC,CAAA;AAEhG;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,8BAA8B;;;aAGzC,CAAA;AACF,MAAM,MAAM,6BAA6B,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,8BAA8B,CAAC,CAAA"}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import * as v from 'valibot';
|
|
2
|
+
import { documentOriginSchema } from './documents.js';
|
|
3
|
+
import { frameRepoTypeSchema } from './primitives.js';
|
|
4
|
+
import { publicTaskDocumentSchema } from './public-api.js';
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// Public-API wire contracts for PROVISIONING the board a headless deployment drives, and for
|
|
7
|
+
// the two task relationships that outlive a single create call.
|
|
8
|
+
//
|
|
9
|
+
// `/api/v1` could list services and create a task under one, and nothing could create a service,
|
|
10
|
+
// link a repository to one, express an ordering between two tasks, or change a task's attached
|
|
11
|
+
// documents after it was filed. That is the same class of gap the outbound webhook and headless key
|
|
12
|
+
// provisioning each closed for themselves: a deployment whose operator is headless could drive
|
|
13
|
+
// every part of this API except the one act it had to open a browser for.
|
|
14
|
+
//
|
|
15
|
+
// Three rules shape everything here, and each is a boundary rather than a preference:
|
|
16
|
+
//
|
|
17
|
+
// 1. **No board COORDINATES.** Positions, sizes and reparenting are ergonomics for a human looking
|
|
18
|
+
// at a canvas. Publishing a coordinate system into a surface that is frozen forever buys an
|
|
19
|
+
// integration nothing and costs the board its freedom to change how it lays itself out, so a
|
|
20
|
+
// service created here is laid out by the platform exactly as the app's own import button
|
|
21
|
+
// lays one out.
|
|
22
|
+
// 2. **The REPO LINK is the load-bearing half.** A service frame with no linked repository cannot
|
|
23
|
+
// run anything (`resolveRepoTarget` throws by design, deliberately with no first-repo
|
|
24
|
+
// fallback), so an endpoint that created frames and could not link them would ship output
|
|
25
|
+
// that is unusable by the very surface that made it.
|
|
26
|
+
// 3. **One rule at every door.** Every write here delegates to the SAME service method the SPA's
|
|
27
|
+
// own controller calls, so an invariant cannot differ by surface.
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
/**
|
|
30
|
+
* The repository a new service frame is backed by.
|
|
31
|
+
*
|
|
32
|
+
* `repoId` is the provider's own id for the repo, as `GET /api/v1/repos` serves it: the neutral
|
|
33
|
+
* name for what the internal projection calls `githubId`. It is deliberately not an `owner/name`
|
|
34
|
+
* pair: a repo can be renamed or transferred without changing its id, and a caller that held a name
|
|
35
|
+
* would silently create a service against a different repository after such a move.
|
|
36
|
+
*/
|
|
37
|
+
export const publicServiceRepoSchema = v.object({
|
|
38
|
+
/** The repo's provider id, from `GET /api/v1/repos`. */
|
|
39
|
+
repoId: v.number(),
|
|
40
|
+
/**
|
|
41
|
+
* For a MONOREPO, the subdirectory (relative to the repo root) this service lives in, e.g.
|
|
42
|
+
* `packages/api`. Required when the repo is a monorepo and refused for a whole-repo service,
|
|
43
|
+
* because it is what scopes each agent's working directory: a monorepo backs one service per
|
|
44
|
+
* subdirectory, and two services claiming the same one would fight over the same subtree.
|
|
45
|
+
*/
|
|
46
|
+
directory: v.optional(v.pipe(v.string(), v.trim(), v.maxLength(400))),
|
|
47
|
+
/**
|
|
48
|
+
* Whether the repository hosts SEVERAL services. Sent with the create rather than as a separate
|
|
49
|
+
* up-front write; when supplied it is persisted on the repo, so the flag a later create reads is
|
|
50
|
+
* the one this call set. Omitted ⇒ whatever the repo already says.
|
|
51
|
+
*/
|
|
52
|
+
monorepo: v.optional(v.boolean()),
|
|
53
|
+
});
|
|
54
|
+
/**
|
|
55
|
+
* Create a board service (a service frame), optionally backed by a repository.
|
|
56
|
+
*
|
|
57
|
+
* Without `repo` the frame is a structural placeholder: it can hold tasks, and a run started on one
|
|
58
|
+
* of them is REFUSED, because execution resolves a task's repository by walking up to the enclosing
|
|
59
|
+
* service frame and there is nothing there. That is a legitimate intermediate state (a caller
|
|
60
|
+
* mapping out a board before its repositories exist) and it is why the field is optional rather
|
|
61
|
+
* than why it should usually be omitted.
|
|
62
|
+
*
|
|
63
|
+
* There is deliberately no `position`: see the note at the top of this file.
|
|
64
|
+
*/
|
|
65
|
+
export const createPublicServiceSchema = v.object({
|
|
66
|
+
/**
|
|
67
|
+
* The service's name. Optional, because a repo-backed service is named after the repository (or,
|
|
68
|
+
* for a monorepo service, after its subdirectory) exactly as the app's import does, and a caller
|
|
69
|
+
* that has no better name should get that one rather than being made to invent it.
|
|
70
|
+
*/
|
|
71
|
+
title: v.optional(v.pipe(v.string(), v.trim(), v.minLength(1), v.maxLength(200))),
|
|
72
|
+
/** What the service is, for the agents that read it as context. Defaults to a generated line. */
|
|
73
|
+
description: v.optional(v.pipe(v.string(), v.trim(), v.maxLength(2000))),
|
|
74
|
+
/**
|
|
75
|
+
* The service's architectural role. Omitted ⇒ `service`.
|
|
76
|
+
*
|
|
77
|
+
* Narrower than the board's own block types on purpose: this is the set a REPOSITORY can back,
|
|
78
|
+
* which is the only kind of frame this endpoint creates. A `database` or `queue` frame documents
|
|
79
|
+
* infrastructure for the agents to read and runs nothing, so nothing here would create one.
|
|
80
|
+
*/
|
|
81
|
+
type: v.optional(frameRepoTypeSchema),
|
|
82
|
+
/** The repository backing the service. Omitted ⇒ an unlinked frame (see above). */
|
|
83
|
+
repo: v.optional(publicServiceRepoSchema),
|
|
84
|
+
});
|
|
85
|
+
/**
|
|
86
|
+
* A repository this workspace can back a service with, as `GET /api/v1/repos` lists it.
|
|
87
|
+
*
|
|
88
|
+
* The discovery half of {@link publicServiceRepoSchema}, and it is why service creation is usable
|
|
89
|
+
* headlessly at all: the create takes a `repoId`, and until this existed the only way to learn one
|
|
90
|
+
* was to open the app. Deliberately a small projection (enough to recognise a repository and pass
|
|
91
|
+
* it back), not a mirror of the internal projection row, which carries installation ids and
|
|
92
|
+
* sync bookkeeping that are this platform's business rather than a caller's.
|
|
93
|
+
*/
|
|
94
|
+
export const publicRepoSchema = v.object({
|
|
95
|
+
/** The provider's id for the repo: the value to pass as `repo.repoId`. */
|
|
96
|
+
repoId: v.number(),
|
|
97
|
+
/** Which provider it lives on (`github` / `gitlab`). */
|
|
98
|
+
provider: v.string(),
|
|
99
|
+
owner: v.string(),
|
|
100
|
+
name: v.string(),
|
|
101
|
+
/**
|
|
102
|
+
* The branch a run's work is based on and merged into, or EMPTY when the projection has not
|
|
103
|
+
* recorded one yet (a repository connected moments ago, before its first sync). Empty rather
|
|
104
|
+
* than null because there is nothing here that could invent a default, and a caller reading it
|
|
105
|
+
* to name a base needs to see that it has to ask the provider rather than assume `main`.
|
|
106
|
+
*/
|
|
107
|
+
defaultBranch: v.string(),
|
|
108
|
+
/** Whether the repository is private on its provider. */
|
|
109
|
+
private: v.boolean(),
|
|
110
|
+
/** Whether the repo is flagged as hosting several services (see `repo.monorepo`). */
|
|
111
|
+
monorepo: v.boolean(),
|
|
112
|
+
/**
|
|
113
|
+
* The service this repository already backs ON THIS BOARD, or null.
|
|
114
|
+
*
|
|
115
|
+
* Present because a whole-repo repository backs at most ONE service, so a caller choosing one to
|
|
116
|
+
* create against needs to know which choices are already spent, and, more usefully, because a
|
|
117
|
+
* caller re-running its provisioning finds the service it created last time here rather than
|
|
118
|
+
* discovering it through a `409`. A monorepo answers null even when its subdirectories back
|
|
119
|
+
* services, since it can back more.
|
|
120
|
+
*
|
|
121
|
+
* Null and {@link linkedElsewhere} together are the honest answer when the service is homed on
|
|
122
|
+
* another board of the account: read the flag before treating null as "available".
|
|
123
|
+
*/
|
|
124
|
+
serviceId: v.nullable(v.string()),
|
|
125
|
+
/**
|
|
126
|
+
* True when this repository already backs a whole-repo service homed on ANOTHER board of the
|
|
127
|
+
* account, so `POST /api/v1/services` will refuse it (`reason: repo_service_homed_elsewhere`).
|
|
128
|
+
*
|
|
129
|
+
* A service is account-owned and a board can MOUNT one homed elsewhere, but every read on this
|
|
130
|
+
* API is scoped to the calling key's own workspace, so a frame homed on another board has no id
|
|
131
|
+
* this surface could hand back: it would not appear in `GET /api/v1/services` and
|
|
132
|
+
* `POST /api/v1/services/{serviceId}/tasks` would 404 on it. Hence a flag rather than a second
|
|
133
|
+
* id field — this states that the choice is spent without naming an address that does not work
|
|
134
|
+
* here. Use the board that homes the service, or a key scoped to it.
|
|
135
|
+
*/
|
|
136
|
+
linkedElsewhere: v.boolean(),
|
|
137
|
+
});
|
|
138
|
+
export const publicRepoListSchema = v.object({ repos: v.array(publicRepoSchema) });
|
|
139
|
+
/**
|
|
140
|
+
* Declare that a task must wait for another one.
|
|
141
|
+
*
|
|
142
|
+
* The gap it closes: an integration filing five related tasks and starting them got five runs
|
|
143
|
+
* racing against one repository, each opening a pull request against a base the others were
|
|
144
|
+
* moving. The platform has had the mechanism to serialise them (the engine's start gate refuses a
|
|
145
|
+
* task whose blockers are not `done`, and `autoStartDependents` starts a task when its blocker
|
|
146
|
+
* merges) and no way for an external caller to be told about it.
|
|
147
|
+
*
|
|
148
|
+
* The edge is stored on the DEPENDENT: `POST /api/v1/tasks/{taskId}/dependencies` says "this task
|
|
149
|
+
* waits for `dependsOnTaskId`". Both ends must be tasks (only a task ever reaches `done`, so an
|
|
150
|
+
* edge onto a service or a module would wedge the run's start gate forever), and an edge that would
|
|
151
|
+
* close a cycle is refused, so the gate and the auto-start can never deadlock.
|
|
152
|
+
*/
|
|
153
|
+
export const publicTaskDependencySchema = v.object({
|
|
154
|
+
/** The task that must finish first. Must be a task in the same workspace, and not this one. */
|
|
155
|
+
dependsOnTaskId: v.pipe(v.string(), v.trim(), v.minLength(1), v.maxLength(120)),
|
|
156
|
+
});
|
|
157
|
+
/**
|
|
158
|
+
* A document attached to a task as agent context, as the task's document list serves it.
|
|
159
|
+
*
|
|
160
|
+
* Identified by `(source, externalId)` rather than by an id of its own, because that pair IS a
|
|
161
|
+
* projected document's identity: a re-import of the same page lands on the same row, which is what
|
|
162
|
+
* makes attaching one idempotent. `source` is `upload` for a body a caller carried rather than
|
|
163
|
+
* named, and such a document has no page behind it, which is why `url` is empty there rather than
|
|
164
|
+
* absent.
|
|
165
|
+
*/
|
|
166
|
+
export const publicAttachedDocumentSchema = v.object({
|
|
167
|
+
/** Which source the document came from, or `upload` for one a caller carried inline. */
|
|
168
|
+
source: documentOriginSchema,
|
|
169
|
+
/** The source's stable id for the page: the value to pass back to detach it. */
|
|
170
|
+
externalId: v.string(),
|
|
171
|
+
title: v.string(),
|
|
172
|
+
/** Canonical URL on the source; EMPTY for an `upload`, which has no page to link back to. */
|
|
173
|
+
url: v.string(),
|
|
174
|
+
/** A short plain-text excerpt of the body (the full text reaches the run, not this list). */
|
|
175
|
+
excerpt: v.string(),
|
|
176
|
+
});
|
|
177
|
+
export const publicAttachedDocumentListSchema = v.object({
|
|
178
|
+
documents: v.array(publicAttachedDocumentSchema),
|
|
179
|
+
});
|
|
180
|
+
/**
|
|
181
|
+
* Attach a requirements document to a task that already exists.
|
|
182
|
+
*
|
|
183
|
+
* The same two forms creation takes ({@link publicTaskDocumentSchema}): NAME a page in a connected
|
|
184
|
+
* document source, or CARRY the text. What it adds is the moment: a task's spec routinely arrives
|
|
185
|
+
* after the task does (a ticket filed first and specified later, a PRD that lands mid-review), and
|
|
186
|
+
* until this existed the only way to attach one was to delete the task and file it again, losing
|
|
187
|
+
* the id every stored reference points at, its ticket claim (which then refuses every future filing
|
|
188
|
+
* of that ticket) and the documents it already carried.
|
|
189
|
+
*
|
|
190
|
+
* A document a DIFFERENT live task already holds is refused rather than moved: a document row
|
|
191
|
+
* carries exactly one attachment, so moving it would strip the other task of a document it was
|
|
192
|
+
* created with, with nothing in its next run reporting the absence.
|
|
193
|
+
*/
|
|
194
|
+
export const attachPublicTaskDocumentSchema = v.object({
|
|
195
|
+
document: publicTaskDocumentSchema,
|
|
196
|
+
});
|
|
197
|
+
/**
|
|
198
|
+
* Detach a document from a task, naming it by the `(source, externalId)` pair the task's document
|
|
199
|
+
* list serves.
|
|
200
|
+
*
|
|
201
|
+
* A POST with a body rather than a `DELETE .../documents/{id}`, because a document's identity is
|
|
202
|
+
* two values and one of them is a free-form external id: a Confluence page id is fine in a path
|
|
203
|
+
* segment and a GitHub docs path (`docs/architecture/adr-0001.md`) is not, so half the sources
|
|
204
|
+
* would need escaping rules a caller has to get right to address its own document.
|
|
205
|
+
*
|
|
206
|
+
* The document itself SURVIVES: it stays in the workspace exactly as the app's own detach leaves
|
|
207
|
+
* it, so re-attaching it costs no re-import. Idempotent: detaching a document this task does not
|
|
208
|
+
* hold is a no-op rather than an error, because a caller retrying after a timeout should converge
|
|
209
|
+
* rather than have to distinguish "it was never attached" from "I already detached it".
|
|
210
|
+
*/
|
|
211
|
+
export const detachPublicTaskDocumentSchema = v.object({
|
|
212
|
+
source: documentOriginSchema,
|
|
213
|
+
externalId: v.pipe(v.string(), v.trim(), v.minLength(1), v.maxLength(500)),
|
|
214
|
+
});
|
|
215
|
+
//# sourceMappingURL=public-board.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"public-board.js","sourceRoot":"","sources":["../src/public-board.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAC5B,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAA;AACrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AACrD,OAAO,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAA;AAE1D,8EAA8E;AAC9E,6FAA6F;AAC7F,gEAAgE;AAChE,EAAE;AACF,iGAAiG;AACjG,+FAA+F;AAC/F,oGAAoG;AACpG,+FAA+F;AAC/F,0EAA0E;AAC1E,EAAE;AACF,sFAAsF;AACtF,EAAE;AACF,oGAAoG;AACpG,gGAAgG;AAChG,iGAAiG;AACjG,8FAA8F;AAC9F,oBAAoB;AACpB,mGAAmG;AACnG,0FAA0F;AAC1F,8FAA8F;AAC9F,yDAAyD;AACzD,kGAAkG;AAClG,sEAAsE;AACtE,8EAA8E;AAE9E;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9C,wDAAwD;IACxD,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;IAClB;;;;;OAKG;IACH,SAAS,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;IACrE;;;;OAIG;IACH,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;CAClC,CAAC,CAAA;AAGF;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC,MAAM,CAAC;IAChD;;;;OAIG;IACH,KAAK,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;IACjF,iGAAiG;IACjG,WAAW,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;IACxE;;;;;;OAMG;IACH,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,mBAAmB,CAAC;IACrC,mFAAmF;IACnF,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,uBAAuB,CAAC;CAC1C,CAAC,CAAA;AAGF;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC;IACvC,0EAA0E;IAC1E,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;IAClB,wDAAwD;IACxD,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;IACpB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;IACjB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB;;;;;OAKG;IACH,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE;IACzB,yDAAyD;IACzD,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE;IACpB,qFAAqF;IACrF,QAAQ,EAAE,CAAC,CAAC,OAAO,EAAE;IACrB;;;;;;;;;;;OAWG;IACH,SAAS,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IACjC;;;;;;;;;;OAUG;IACH,eAAe,EAAE,CAAC,CAAC,OAAO,EAAE;CAC7B,CAAC,CAAA;AAGF,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAA;AAGlF;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,CAAC,MAAM,CAAC;IACjD,+FAA+F;IAC/F,eAAe,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;CAChF,CAAC,CAAA;AAGF;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,CAAC,MAAM,CAAC;IACnD,wFAAwF;IACxF,MAAM,EAAE,oBAAoB;IAC5B,gFAAgF;IAChF,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;IACtB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;IACjB,6FAA6F;IAC7F,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE;IACf,6FAA6F;IAC7F,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;CACpB,CAAC,CAAA;AAGF,MAAM,CAAC,MAAM,gCAAgC,GAAG,CAAC,CAAC,MAAM,CAAC;IACvD,SAAS,EAAE,CAAC,CAAC,KAAK,CAAC,4BAA4B,CAAC;CACjD,CAAC,CAAA;AAGF;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,8BAA8B,GAAG,CAAC,CAAC,MAAM,CAAC;IACrD,QAAQ,EAAE,wBAAwB;CACnC,CAAC,CAAA;AAGF;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,8BAA8B,GAAG,CAAC,CAAC,MAAM,CAAC;IACrD,MAAM,EAAE,oBAAoB;IAC5B,UAAU,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;CAC3E,CAAC,CAAA"}
|
|
@@ -6,11 +6,30 @@ import * as v from 'valibot';
|
|
|
6
6
|
*/
|
|
7
7
|
export declare const publicArtifactKindSchema: v.PicklistSchema<["screenshot", "reference"], undefined>;
|
|
8
8
|
export type PublicArtifactKind = v.InferOutput<typeof publicArtifactKindSchema>;
|
|
9
|
+
/**
|
|
10
|
+
* What an artifact is ANCHORED on, which is what says whether the run produced it:
|
|
11
|
+
*
|
|
12
|
+
* - `run`: captured by THIS run. It carries the run's id, and a re-run captures its own.
|
|
13
|
+
* - `task`: attached to the run's task and outliving any single run of it. A reference design a
|
|
14
|
+
* person uploaded before the first run is the case that matters: it is what the run's
|
|
15
|
+
* screenshots are judged against, and it is deliberately not run-anchored, because uploading one
|
|
16
|
+
* per attempt is exactly what a reference exists to avoid.
|
|
17
|
+
*
|
|
18
|
+
* Stated per row rather than folded away, because the two sets answer different questions and a
|
|
19
|
+
* silent union would make "the run captured 3 screenshots" unreadable off a list of 5. It is also
|
|
20
|
+
* why the list is not simply the run's own rows: a caller enumerating a run's artifacts to compare
|
|
21
|
+
* a screenshot against its reference saw only one half, so the reference rendered as ABSENT on the
|
|
22
|
+
* one surface whose job is to say what a run proved, while being individually fetchable all along.
|
|
23
|
+
*/
|
|
24
|
+
export declare const publicArtifactScopeSchema: v.PicklistSchema<["run", "task"], undefined>;
|
|
25
|
+
export type PublicArtifactScope = v.InferOutput<typeof publicArtifactScopeSchema>;
|
|
9
26
|
/** One binary artifact a run produced (metadata; the bytes are a separate fetch). */
|
|
10
27
|
export declare const publicRunArtifactSchema: v.ObjectSchema<{
|
|
11
28
|
/** The id to pass to `GET /api/v1/artifacts/{artifactId}/blob`. */
|
|
12
29
|
readonly artifactId: v.StringSchema<undefined>;
|
|
13
30
|
readonly kind: v.PicklistSchema<["screenshot", "reference"], undefined>;
|
|
31
|
+
/** Which anchor this row came from ({@link publicArtifactScopeSchema}). */
|
|
32
|
+
readonly scope: v.PicklistSchema<["run", "task"], undefined>;
|
|
14
33
|
/** Logical view name, which is what pairs a screenshot with its reference. Null when unnamed. */
|
|
15
34
|
readonly view: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
16
35
|
/** The MIME type the blob endpoint will answer with (always a raster image today). */
|
|
@@ -26,17 +45,25 @@ export declare const publicRunArtifactSchema: v.ObjectSchema<{
|
|
|
26
45
|
}, undefined>;
|
|
27
46
|
export type PublicRunArtifact = v.InferOutput<typeof publicRunArtifactSchema>;
|
|
28
47
|
/**
|
|
29
|
-
* A run's artifacts,
|
|
48
|
+
* A run's artifacts (the ones it CAPTURED plus the ones attached to its task, each saying which
|
|
49
|
+
* it is: {@link publicArtifactScopeSchema}), whole rather than paginated.
|
|
30
50
|
*
|
|
31
51
|
* Deliberately unpaged where every sibling list is keyset-paginated: the capture path enforces
|
|
32
|
-
* a per-run ceiling, so the row count is bounded by
|
|
33
|
-
* computable before the request. A cursor here would be a
|
|
52
|
+
* a per-run ceiling and a task's uploads are a human-sized set, so the row count is bounded by
|
|
53
|
+
* construction and the response size is computable before the request. A cursor here would be a
|
|
54
|
+
* page-2 that structurally cannot exist.
|
|
55
|
+
*
|
|
56
|
+
* An artifact that is BOTH (a screenshot a run captured against its own task) appears ONCE, as
|
|
57
|
+
* `run`: the run is the more specific anchor, and a row appearing twice under two scopes would
|
|
58
|
+
* make every count off this list wrong in the direction that reads as extra evidence.
|
|
34
59
|
*/
|
|
35
60
|
export declare const publicRunArtifactListSchema: v.ObjectSchema<{
|
|
36
61
|
readonly artifacts: v.ArraySchema<v.ObjectSchema<{
|
|
37
62
|
/** The id to pass to `GET /api/v1/artifacts/{artifactId}/blob`. */
|
|
38
63
|
readonly artifactId: v.StringSchema<undefined>;
|
|
39
64
|
readonly kind: v.PicklistSchema<["screenshot", "reference"], undefined>;
|
|
65
|
+
/** Which anchor this row came from ({@link publicArtifactScopeSchema}). */
|
|
66
|
+
readonly scope: v.PicklistSchema<["run", "task"], undefined>;
|
|
40
67
|
/** Logical view name, which is what pairs a screenshot with its reference. Null when unnamed. */
|
|
41
68
|
readonly view: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
42
69
|
/** The MIME type the blob endpoint will answer with (always a raster image today). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"public-evidence.d.ts","sourceRoot":"","sources":["../src/public-evidence.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AA0B5B;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,0DAA0C,CAAA;AAC/E,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,wBAAwB,CAAC,CAAA;AAE/E,qFAAqF;AACrF,eAAO,MAAM,uBAAuB;IAClC,mEAAmE;;;IAGnE,iGAAiG;;IAEjG,sFAAsF;;IAEtF;;;OAGG;;IAEH,0FAA0F;;;aAG1F,CAAA;AACF,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,uBAAuB,CAAC,CAAA;AAE7E
|
|
1
|
+
{"version":3,"file":"public-evidence.d.ts","sourceRoot":"","sources":["../src/public-evidence.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AA0B5B;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,0DAA0C,CAAA;AAC/E,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,wBAAwB,CAAC,CAAA;AAE/E;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,yBAAyB,8CAA8B,CAAA;AACpE,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,yBAAyB,CAAC,CAAA;AAEjF,qFAAqF;AACrF,eAAO,MAAM,uBAAuB;IAClC,mEAAmE;;;IAGnE,2EAA2E;;IAE3E,iGAAiG;;IAEjG,sFAAsF;;IAEtF;;;OAGG;;IAEH,0FAA0F;;;aAG1F,CAAA;AACF,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,uBAAuB,CAAC,CAAA;AAE7E;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,2BAA2B;;QAjCtC,mEAAmE;;;QAGnE,2EAA2E;;QAE3E,iGAAiG;;QAEjG,sFAAsF;;QAEtF;;;WAGG;;QAEH,0FAA0F;;;;aAqB1F,CAAA;AACF,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,2BAA2B,CAAC,CAAA"}
|
package/dist/public-evidence.js
CHANGED
|
@@ -28,11 +28,29 @@ import * as v from 'valibot';
|
|
|
28
28
|
* human uploaded for the run to be judged against.
|
|
29
29
|
*/
|
|
30
30
|
export const publicArtifactKindSchema = v.picklist(['screenshot', 'reference']);
|
|
31
|
+
/**
|
|
32
|
+
* What an artifact is ANCHORED on, which is what says whether the run produced it:
|
|
33
|
+
*
|
|
34
|
+
* - `run`: captured by THIS run. It carries the run's id, and a re-run captures its own.
|
|
35
|
+
* - `task`: attached to the run's task and outliving any single run of it. A reference design a
|
|
36
|
+
* person uploaded before the first run is the case that matters: it is what the run's
|
|
37
|
+
* screenshots are judged against, and it is deliberately not run-anchored, because uploading one
|
|
38
|
+
* per attempt is exactly what a reference exists to avoid.
|
|
39
|
+
*
|
|
40
|
+
* Stated per row rather than folded away, because the two sets answer different questions and a
|
|
41
|
+
* silent union would make "the run captured 3 screenshots" unreadable off a list of 5. It is also
|
|
42
|
+
* why the list is not simply the run's own rows: a caller enumerating a run's artifacts to compare
|
|
43
|
+
* a screenshot against its reference saw only one half, so the reference rendered as ABSENT on the
|
|
44
|
+
* one surface whose job is to say what a run proved, while being individually fetchable all along.
|
|
45
|
+
*/
|
|
46
|
+
export const publicArtifactScopeSchema = v.picklist(['run', 'task']);
|
|
31
47
|
/** One binary artifact a run produced (metadata; the bytes are a separate fetch). */
|
|
32
48
|
export const publicRunArtifactSchema = v.object({
|
|
33
49
|
/** The id to pass to `GET /api/v1/artifacts/{artifactId}/blob`. */
|
|
34
50
|
artifactId: v.string(),
|
|
35
51
|
kind: publicArtifactKindSchema,
|
|
52
|
+
/** Which anchor this row came from ({@link publicArtifactScopeSchema}). */
|
|
53
|
+
scope: publicArtifactScopeSchema,
|
|
36
54
|
/** Logical view name, which is what pairs a screenshot with its reference. Null when unnamed. */
|
|
37
55
|
view: v.nullable(v.string()),
|
|
38
56
|
/** The MIME type the blob endpoint will answer with (always a raster image today). */
|
|
@@ -47,11 +65,17 @@ export const publicRunArtifactSchema = v.object({
|
|
|
47
65
|
createdAt: v.number(),
|
|
48
66
|
});
|
|
49
67
|
/**
|
|
50
|
-
* A run's artifacts,
|
|
68
|
+
* A run's artifacts (the ones it CAPTURED plus the ones attached to its task, each saying which
|
|
69
|
+
* it is: {@link publicArtifactScopeSchema}), whole rather than paginated.
|
|
51
70
|
*
|
|
52
71
|
* Deliberately unpaged where every sibling list is keyset-paginated: the capture path enforces
|
|
53
|
-
* a per-run ceiling, so the row count is bounded by
|
|
54
|
-
* computable before the request. A cursor here would be a
|
|
72
|
+
* a per-run ceiling and a task's uploads are a human-sized set, so the row count is bounded by
|
|
73
|
+
* construction and the response size is computable before the request. A cursor here would be a
|
|
74
|
+
* page-2 that structurally cannot exist.
|
|
75
|
+
*
|
|
76
|
+
* An artifact that is BOTH (a screenshot a run captured against its own task) appears ONCE, as
|
|
77
|
+
* `run`: the run is the more specific anchor, and a row appearing twice under two scopes would
|
|
78
|
+
* make every count off this list wrong in the direction that reads as extra evidence.
|
|
55
79
|
*/
|
|
56
80
|
export const publicRunArtifactListSchema = v.object({
|
|
57
81
|
artifacts: v.array(publicRunArtifactSchema),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"public-evidence.js","sourceRoot":"","sources":["../src/public-evidence.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAE5B,8EAA8E;AAC9E,wFAAwF;AACxF,kDAAkD;AAClD,EAAE;AACF,2FAA2F;AAC3F,wFAAwF;AACxF,uFAAuF;AACvF,wFAAwF;AACxF,0FAA0F;AAC1F,oBAAoB;AACpB,EAAE;AACF,6CAA6C;AAC7C,EAAE;AACF,6FAA6F;AAC7F,6FAA6F;AAC7F,uFAAuF;AACvF,0FAA0F;AAC1F,yEAAyE;AACzE,yFAAyF;AACzF,8FAA8F;AAC9F,4FAA4F;AAC5F,mEAAmE;AACnE,8EAA8E;AAE9E;;;;GAIG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC,CAAA;AAG/E,qFAAqF;AACrF,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9C,mEAAmE;IACnE,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;IACtB,IAAI,EAAE,wBAAwB;IAC9B,iGAAiG;IACjG,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAC5B,sFAAsF;IACtF,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;IACvB;;;OAGG;IACH,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;IACpB,0FAA0F;IAC1F,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE;CACtB,CAAC,CAAA;AAGF
|
|
1
|
+
{"version":3,"file":"public-evidence.js","sourceRoot":"","sources":["../src/public-evidence.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAE5B,8EAA8E;AAC9E,wFAAwF;AACxF,kDAAkD;AAClD,EAAE;AACF,2FAA2F;AAC3F,wFAAwF;AACxF,uFAAuF;AACvF,wFAAwF;AACxF,0FAA0F;AAC1F,oBAAoB;AACpB,EAAE;AACF,6CAA6C;AAC7C,EAAE;AACF,6FAA6F;AAC7F,6FAA6F;AAC7F,uFAAuF;AACvF,0FAA0F;AAC1F,yEAAyE;AACzE,yFAAyF;AACzF,8FAA8F;AAC9F,4FAA4F;AAC5F,mEAAmE;AACnE,8EAA8E;AAE9E;;;;GAIG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC,CAAA;AAG/E;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAA;AAGpE,qFAAqF;AACrF,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9C,mEAAmE;IACnE,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;IACtB,IAAI,EAAE,wBAAwB;IAC9B,2EAA2E;IAC3E,KAAK,EAAE,yBAAyB;IAChC,iGAAiG;IACjG,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAC5B,sFAAsF;IACtF,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;IACvB;;;OAGG;IACH,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;IACpB,0FAA0F;IAC1F,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE;CACtB,CAAC,CAAA;AAGF;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,CAAC,MAAM,CAAC;IAClD,SAAS,EAAE,CAAC,CAAC,KAAK,CAAC,uBAAuB,CAAC;CAC5C,CAAC,CAAA"}
|