@opensoftware/contracts 2.4.0 → 2.6.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/README.md +22 -1
- package/access/access.schema.json +54 -0
- package/dist/cli.js +3 -6
- package/dist/cli.js.map +1 -1
- package/dist/formats.js +2 -0
- package/dist/formats.js.map +1 -1
- package/dist/generated/types.d.ts +115 -1
- package/dist/index.d.ts +16 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -1
- package/dist/sandbox.d.ts +17 -0
- package/dist/sandbox.js +23 -0
- package/dist/sandbox.js.map +1 -0
- package/factory-definition/factory-definition.schema.json +1 -1
- package/factory-lock/factory-lock.schema.json +32 -0
- package/openapi/openapi.json +263 -2
- package/package.json +2 -2
- package/skill-catalog/skill-catalog.schema.json +177 -0
- package/typespec/access.tsp +44 -0
- package/typespec/factory-definition.tsp +4 -1
- package/typespec/factory-lock.tsp +23 -0
- package/typespec/main.tsp +2 -0
- package/typespec/skill-catalog.tsp +94 -0
package/README.md
CHANGED
|
@@ -28,6 +28,13 @@ const problems = validate("factory-definition", document); // [] when valid, els
|
|
|
28
28
|
`runSpans(record)` reads a run record as runlog writes it: each span with its decoded resource and attributes, or
|
|
29
29
|
`undefined` when the value is no OTLP/JSON trace export request ([ADR 0041](../docs/decisions/0041-run-record-reader.md)).
|
|
30
30
|
|
|
31
|
+
`readDocument(format, file)` reads a YAML or JSON file and gives `{ document, problems }`, the problems as
|
|
32
|
+
`validate` gives them; a file that cannot be read or parsed throws. The sandbox port's command line has two values
|
|
33
|
+
of its own ([ADR 0147](../docs/decisions/0147-sandbox-egress-is-the-egress-proxy.md)): `copyEnds(from, to)` gives the
|
|
34
|
+
ends of `copy <from> <to>`, `{ id, inSandbox, local, into }` with a relative path below `SANDBOX_WORKSPACE`
|
|
35
|
+
(`/workspace`), or `{ problem }` when not exactly one side is `<ID>:<path>`. Each gives a problem and throws no
|
|
36
|
+
usage error: the adapter turns it into its own.
|
|
37
|
+
|
|
31
38
|
The schema files are exported at the paths in the table below, for example
|
|
32
39
|
`@opensoftware/contracts/work-item/work-item.schema.json`, along with `events/asyncapi.yaml`, `openapi/*` and `typespec/*`.
|
|
33
40
|
|
|
@@ -44,7 +51,9 @@ Each format has one schema, generated from `typespec/`, and fixtures that CONTRA
|
|
|
44
51
|
| score | `score/score.schema.json` | `Score` | Subset of Langfuse's create-score request; factory fields in `metadata`. |
|
|
45
52
|
| gate record | `gate-record/gate-record.schema.json` | `GateRecord` | gatekeeper's signed approval record (ADR 0047). |
|
|
46
53
|
| sandbox spec | `sandbox-spec/sandbox-spec.schema.json` | `SandboxSpec` | The input of a sandbox adapter's `create` (ADR 0054). Closed objects: an unknown field is an error. |
|
|
47
|
-
| factory lock | `factory-lock/factory-lock.schema.json` | `FactoryLock` | `factory.lock` next to `factory.yaml`: the registry MCP servers and the
|
|
54
|
+
| factory lock | `factory-lock/factory-lock.schema.json` | `FactoryLock` | `factory.lock` next to `factory.yaml`: the registry MCP servers, the adapters and the skills of other Git repositories that `factory plan` pinned, a skill with its commit and the hash of its files (ADR 0020, ADR 0125, ADR 0146). factory-spec writes it, the foreman reads the adapter pins and the skill pins. |
|
|
55
|
+
| access | `access/access.schema.json` | `Access` | `access.yaml` next to `factory.yaml`: the people who may sign in to the factory UI, each by email with one role, `member`, `factory-engineer` or `admin` (ADR 0134). The console reads it at each request. |
|
|
56
|
+
| skill catalog | `skill-catalog/skill-catalog.schema.json` | `SkillCatalog` | What `factory skills` prints: every skill the factory's agents use, with its needs, its agents and what each lacks, its files, the Markdown of its `SKILL.md` and its problems, and for a skill of another Git repository its URL and commit (ADR 0141, ADR 0143, ADR 0146). factory-spec writes it, the console reads it for `/skills`. |
|
|
48
57
|
| events | `events/<event>/<event>.schema.json`, `events/asyncapi.yaml` | `WorkReceived`, `WorkRetryRequested`, `RunStarted`, `RunCompleted`, `RunBlocked`, `DoctorPing` … `IssueLabeled` | CloudEvents 1.0, structured JSON. |
|
|
49
58
|
|
|
50
59
|
## HTTP APIs
|
|
@@ -155,3 +164,15 @@ None. The CLI reads no environment variables.
|
|
|
155
164
|
| SCN-CTR-010 | implemented | runSpans gives each span of a run record with its resource and decoded attributes |
|
|
156
165
|
| SCN-CTR-011 | implemented | runSpans gives no spans for a value that is not a trace export request |
|
|
157
166
|
| SCN-CTR-012 | implemented | A factory definition with an unknown effort is rejected with the allowed levels |
|
|
167
|
+
| SCN-CTR-013 | implemented | An access file with an admin and a member validates |
|
|
168
|
+
| SCN-CTR-014 | implemented | An access file with an unknown role is rejected with the roles it allows |
|
|
169
|
+
| SCN-CTR-015 | implemented | A skill catalog with a skill that one agent lacks the secret of validates |
|
|
170
|
+
| SCN-CTR-016 | implemented | A skill catalog whose skill has no body is rejected with its path |
|
|
171
|
+
| SCN-CTR-017 | implemented | readDocument gives a file's document and each problem with its path |
|
|
172
|
+
| SCN-CTR-018 | implemented | A copy into a sandbox names a path below its working folder |
|
|
173
|
+
| SCN-CTR-019 | implemented | A copy out of a sandbox keeps an absolute path |
|
|
174
|
+
| SCN-CTR-020 | implemented | A copy that names a sandbox on both sides gives its problem |
|
|
175
|
+
| SCN-CTR-021 | implemented | A factory lock with a pinned skill of another repository validates |
|
|
176
|
+
| SCN-CTR-022 | implemented | A pinned skill with a short commit is rejected with its path |
|
|
177
|
+
| SCN-CTR-023 | implemented | The pins of skills in factory.lock are no breaking change |
|
|
178
|
+
| SCN-CTR-024 | implemented | A skill catalog with a skill of another repository at its commit validates |
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://raw.githubusercontent.com/sesigl/open-software-factory/main/contracts/access/access.schema.json",
|
|
4
|
+
"title": "Access",
|
|
5
|
+
"$comment": "Generated by `pnpm build` in contracts/ from typespec/. Do not edit.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"version": {
|
|
9
|
+
"type": "number",
|
|
10
|
+
"const": 1,
|
|
11
|
+
"description": "The version of the access file's format."
|
|
12
|
+
},
|
|
13
|
+
"people": {
|
|
14
|
+
"type": "array",
|
|
15
|
+
"items": {
|
|
16
|
+
"type": "object",
|
|
17
|
+
"properties": {
|
|
18
|
+
"email": {
|
|
19
|
+
"type": "string",
|
|
20
|
+
"format": "email"
|
|
21
|
+
},
|
|
22
|
+
"name": {
|
|
23
|
+
"type": "string",
|
|
24
|
+
"minLength": 1,
|
|
25
|
+
"description": "The name the UI shows, when the provider gives none."
|
|
26
|
+
},
|
|
27
|
+
"role": {
|
|
28
|
+
"type": "string",
|
|
29
|
+
"enum": [
|
|
30
|
+
"member",
|
|
31
|
+
"factory-engineer",
|
|
32
|
+
"admin"
|
|
33
|
+
],
|
|
34
|
+
"description": "`member` sees every page but Secrets and acts on work, runs and gates; `factory-engineer` also changes the factory\nfrom the UI; `admin` also stores and deletes secrets."
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"required": [
|
|
38
|
+
"email",
|
|
39
|
+
"role"
|
|
40
|
+
],
|
|
41
|
+
"description": "One person: the email the provider verifies, compared in lower case, and their role.",
|
|
42
|
+
"additionalProperties": false
|
|
43
|
+
},
|
|
44
|
+
"minItems": 1,
|
|
45
|
+
"description": "The people who may sign in. An email appears once."
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"required": [
|
|
49
|
+
"version",
|
|
50
|
+
"people"
|
|
51
|
+
],
|
|
52
|
+
"description": "access.yaml next to factory.yaml (ADR 0134): who may sign in to the factory UI, each person with one role. The\nconsole reads it at each request, so a merge that removes a person ends their access.",
|
|
53
|
+
"additionalProperties": false
|
|
54
|
+
}
|
package/dist/cli.js
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// osf-contracts: validate documents against the shared formats, check schema compatibility with the last release,
|
|
3
3
|
// and round-trip the fixtures through the generated clients.
|
|
4
|
-
import { readFileSync } from "node:fs";
|
|
5
|
-
import { parse } from "yaml";
|
|
6
4
|
import { breaking } from "./breaking.js";
|
|
7
5
|
import { FORMATS, formatNamed } from "./formats.js";
|
|
8
|
-
import {
|
|
6
|
+
import { readDocument } from "./index.js";
|
|
9
7
|
import { CLIENTS, roundtrip } from "./roundtrip.js";
|
|
10
8
|
const USAGE = `Usage: osf-contracts <command>
|
|
11
9
|
|
|
@@ -45,15 +43,14 @@ function validateCommand([name, file]) {
|
|
|
45
43
|
const format = name ? formatNamed(name) : undefined;
|
|
46
44
|
if (!format || !file)
|
|
47
45
|
return usage(name && !format ? `Unknown format: ${name}` : "Give a format and a file");
|
|
48
|
-
let
|
|
46
|
+
let problems;
|
|
49
47
|
try {
|
|
50
|
-
|
|
48
|
+
({ problems } = readDocument(format, file));
|
|
51
49
|
}
|
|
52
50
|
catch (error) {
|
|
53
51
|
process.stdout.write(`${file}: not readable as YAML or JSON: ${error.message}\n`);
|
|
54
52
|
return 1;
|
|
55
53
|
}
|
|
56
|
-
const problems = validate(format, document);
|
|
57
54
|
for (const problem of problems)
|
|
58
55
|
process.stdout.write(`${file} ${problem.path}: ${problem.message}\n`);
|
|
59
56
|
if (problems.length > 0)
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,kHAAkH;AAClH,6DAA6D;AAC7D,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/formats.js
CHANGED
|
@@ -9,6 +9,8 @@ export const FORMATS = [
|
|
|
9
9
|
format("gate-record", "GateRecord"),
|
|
10
10
|
format("sandbox-spec", "SandboxSpec"),
|
|
11
11
|
format("factory-lock", "FactoryLock"),
|
|
12
|
+
format("access", "Access"),
|
|
13
|
+
format("skill-catalog", "SkillCatalog"),
|
|
12
14
|
format("events/work-received", "WorkReceived"),
|
|
13
15
|
format("events/work-retry-requested", "WorkRetryRequested"),
|
|
14
16
|
format("events/run-started", "RunStarted"),
|
package/dist/formats.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"formats.js","sourceRoot":"","sources":["../src/formats.ts"],"names":[],"mappings":"AAAA,2GAA2G;AAW3G,MAAM,MAAM,GAAG,CAAC,IAAY,EAAE,KAAa,EAAU,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAY,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AAEjH,MAAM,CAAC,MAAM,OAAO,GAAsB;IACxC,MAAM,CAAC,oBAAoB,EAAE,mBAAmB,CAAC;IACjD,MAAM,CAAC,WAAW,EAAE,UAAU,CAAC;IAC/B,MAAM,CAAC,YAAY,EAAE,WAAW,CAAC;IACjC,MAAM,CAAC,iBAAiB,EAAE,kBAAkB,CAAC;IAC7C,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC;IACxB,MAAM,CAAC,aAAa,EAAE,YAAY,CAAC;IACnC,MAAM,CAAC,cAAc,EAAE,aAAa,CAAC;IACrC,MAAM,CAAC,cAAc,EAAE,aAAa,CAAC;IACrC,MAAM,CAAC,sBAAsB,EAAE,cAAc,CAAC;IAC9C,MAAM,CAAC,6BAA6B,EAAE,oBAAoB,CAAC;IAC3D,MAAM,CAAC,oBAAoB,EAAE,YAAY,CAAC;IAC1C,MAAM,CAAC,sBAAsB,EAAE,cAAc,CAAC;IAC9C,MAAM,CAAC,oBAAoB,EAAE,YAAY,CAAC;IAC1C,MAAM,CAAC,oBAAoB,EAAE,YAAY,CAAC;IAC1C,MAAM,CAAC,uBAAuB,EAAE,eAAe,CAAC;IAChD,MAAM,CAAC,qBAAqB,EAAE,aAAa,CAAC;IAC5C,MAAM,CAAC,uBAAuB,EAAE,eAAe,CAAC;IAChD,MAAM,CAAC,kBAAkB,EAAE,UAAU,CAAC;IACtC,MAAM,CAAC,oBAAoB,EAAE,YAAY,CAAC;IAC1C,MAAM,CAAC,kBAAkB,EAAE,UAAU,CAAC;IACtC,MAAM,CAAC,sBAAsB,EAAE,cAAc,CAAC;CAC/C,CAAC;AAEF,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;AAC9D,CAAC"}
|
|
1
|
+
{"version":3,"file":"formats.js","sourceRoot":"","sources":["../src/formats.ts"],"names":[],"mappings":"AAAA,2GAA2G;AAW3G,MAAM,MAAM,GAAG,CAAC,IAAY,EAAE,KAAa,EAAU,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAY,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AAEjH,MAAM,CAAC,MAAM,OAAO,GAAsB;IACxC,MAAM,CAAC,oBAAoB,EAAE,mBAAmB,CAAC;IACjD,MAAM,CAAC,WAAW,EAAE,UAAU,CAAC;IAC/B,MAAM,CAAC,YAAY,EAAE,WAAW,CAAC;IACjC,MAAM,CAAC,iBAAiB,EAAE,kBAAkB,CAAC;IAC7C,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC;IACxB,MAAM,CAAC,aAAa,EAAE,YAAY,CAAC;IACnC,MAAM,CAAC,cAAc,EAAE,aAAa,CAAC;IACrC,MAAM,CAAC,cAAc,EAAE,aAAa,CAAC;IACrC,MAAM,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC1B,MAAM,CAAC,eAAe,EAAE,cAAc,CAAC;IACvC,MAAM,CAAC,sBAAsB,EAAE,cAAc,CAAC;IAC9C,MAAM,CAAC,6BAA6B,EAAE,oBAAoB,CAAC;IAC3D,MAAM,CAAC,oBAAoB,EAAE,YAAY,CAAC;IAC1C,MAAM,CAAC,sBAAsB,EAAE,cAAc,CAAC;IAC9C,MAAM,CAAC,oBAAoB,EAAE,YAAY,CAAC;IAC1C,MAAM,CAAC,oBAAoB,EAAE,YAAY,CAAC;IAC1C,MAAM,CAAC,uBAAuB,EAAE,eAAe,CAAC;IAChD,MAAM,CAAC,qBAAqB,EAAE,aAAa,CAAC;IAC5C,MAAM,CAAC,uBAAuB,EAAE,eAAe,CAAC;IAChD,MAAM,CAAC,kBAAkB,EAAE,UAAU,CAAC;IACtC,MAAM,CAAC,oBAAoB,EAAE,YAAY,CAAC;IAC1C,MAAM,CAAC,kBAAkB,EAAE,UAAU,CAAC;IACtC,MAAM,CAAC,sBAAsB,EAAE,cAAc,CAAC;CAC/C,CAAC;AAEF,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;AAC9D,CAAC"}
|
|
@@ -222,6 +222,36 @@ export type paths = {
|
|
|
222
222
|
export type webhooks = Record<string, never>;
|
|
223
223
|
export type components = {
|
|
224
224
|
schemas: {
|
|
225
|
+
/**
|
|
226
|
+
* @description access.yaml next to factory.yaml (ADR 0134): who may sign in to the factory UI, each person with one role. The
|
|
227
|
+
* console reads it at each request, so a merge that removes a person ends their access.
|
|
228
|
+
*/
|
|
229
|
+
Access: {
|
|
230
|
+
/**
|
|
231
|
+
* @description The version of the access file's format.
|
|
232
|
+
* @constant
|
|
233
|
+
*/
|
|
234
|
+
version: 1;
|
|
235
|
+
/** @description The people who may sign in. An email appears once. */
|
|
236
|
+
people: components["schemas"]["AccessPerson"][];
|
|
237
|
+
};
|
|
238
|
+
/** @description One person: the email the provider verifies, compared in lower case, and their role. */
|
|
239
|
+
AccessPerson: {
|
|
240
|
+
/** Format: email */
|
|
241
|
+
email: string;
|
|
242
|
+
/** @description The name the UI shows, when the provider gives none. */
|
|
243
|
+
name?: string;
|
|
244
|
+
/**
|
|
245
|
+
* @description `member` sees every page but Secrets and acts on work, runs and gates; `factory-engineer` also changes the factory
|
|
246
|
+
* from the UI; `admin` also stores and deletes secrets.
|
|
247
|
+
*/
|
|
248
|
+
role: components["schemas"]["AccessRole"];
|
|
249
|
+
};
|
|
250
|
+
/**
|
|
251
|
+
* @description The roles of the factory UI (ADR 0134), from the least to the most it may do.
|
|
252
|
+
* @enum {string}
|
|
253
|
+
*/
|
|
254
|
+
AccessRole: "member" | "factory-engineer" | "admin";
|
|
225
255
|
/** @description A person or trigger. RUNTIME-008: every run is attributed to one. */
|
|
226
256
|
Actor: {
|
|
227
257
|
kind: components["schemas"]["ActorKind"];
|
|
@@ -264,7 +294,10 @@ export type components = {
|
|
|
264
294
|
repositories?: string[];
|
|
265
295
|
/** @description MCP servers: `registry:<name>@<version>` or `<name>@<version>`. */
|
|
266
296
|
mcp?: string[];
|
|
267
|
-
/**
|
|
297
|
+
/**
|
|
298
|
+
* @description The agent's skills: each a path to a skill folder, or a folder of another Git repository at a ref,
|
|
299
|
+
* `git+<url>#<ref>:<path>`, which `factory plan` pins in factory.lock (ADR 0146).
|
|
300
|
+
*/
|
|
268
301
|
skills?: string[];
|
|
269
302
|
secrets?: components["schemas"]["SecretReference"][];
|
|
270
303
|
/** @description Runtime of the agent. */
|
|
@@ -314,6 +347,53 @@ export type components = {
|
|
|
314
347
|
BlockedOn: "checks";
|
|
315
348
|
/** @description A path inside the bundle: relative, forward slashes, no "..". */
|
|
316
349
|
BundlePath: string;
|
|
350
|
+
/** @description One skill, also one that lint refuses: then `problems` names why. */
|
|
351
|
+
CatalogSkill: {
|
|
352
|
+
/** @description The last segment of the skill's folder. */
|
|
353
|
+
name: string;
|
|
354
|
+
/** @description The description of its front matter; null without one. */
|
|
355
|
+
description: string | null;
|
|
356
|
+
/** @description Where the skill comes from. */
|
|
357
|
+
source: components["schemas"]["CatalogSkillSource"];
|
|
358
|
+
/** @description What it needs, from the `metadata` of its front matter. */
|
|
359
|
+
needs: components["schemas"]["SkillNeeds"];
|
|
360
|
+
/** @description Each agent that uses it, sorted by name. */
|
|
361
|
+
agents: components["schemas"]["CatalogSkillAgent"][];
|
|
362
|
+
/** @description The files of its folder, by their path below it, sorted. */
|
|
363
|
+
files: string[];
|
|
364
|
+
/**
|
|
365
|
+
* @description The text of SKILL.md below the line that ends its front matter, all of it without front matter; null without a
|
|
366
|
+
* SKILL.md that is UTF-8 text.
|
|
367
|
+
*/
|
|
368
|
+
body: string | null;
|
|
369
|
+
/**
|
|
370
|
+
* @description The problems of its own files as `factory lint` prints them, relative to the folder of factory.yaml, and a folder
|
|
371
|
+
* without a SKILL.md.
|
|
372
|
+
*/
|
|
373
|
+
problems: string[];
|
|
374
|
+
};
|
|
375
|
+
/** @description An agent that uses a skill. */
|
|
376
|
+
CatalogSkillAgent: {
|
|
377
|
+
name: string;
|
|
378
|
+
/** @description The agent's reference whose variable is each secret the skill needs, by the variable. */
|
|
379
|
+
secrets: {
|
|
380
|
+
[key: string]: components["schemas"]["SecretReference"];
|
|
381
|
+
};
|
|
382
|
+
/** @description Each needed secret the agent does not declare, and each needed host its sandbox does not allow. */
|
|
383
|
+
lacks: components["schemas"]["SkillNeeds"];
|
|
384
|
+
};
|
|
385
|
+
/**
|
|
386
|
+
* @description A folder of the factory's repository, relative to the folder of factory.yaml, like "skills/release-notes"; or, for a
|
|
387
|
+
* skill of another Git repository, its folder there with the repository's URL and the commit factory.lock pins
|
|
388
|
+
* (ADR 0146).
|
|
389
|
+
*/
|
|
390
|
+
CatalogSkillSource: {
|
|
391
|
+
folder: string;
|
|
392
|
+
/** @description The URL of the other Git repository, as the entry `git+<url>#<ref>:<path>` names it. */
|
|
393
|
+
repository?: string;
|
|
394
|
+
/** @description The commit of the other repository that factory.lock pins; none while factory.lock has no pin. */
|
|
395
|
+
commit?: string;
|
|
396
|
+
};
|
|
317
397
|
/** @description The name of a check on the forge: a commit status context. */
|
|
318
398
|
CheckName: string;
|
|
319
399
|
/**
|
|
@@ -457,6 +537,14 @@ export type components = {
|
|
|
457
537
|
[key: string]: components["schemas"]["FactoryLockAdapterPin"];
|
|
458
538
|
};
|
|
459
539
|
};
|
|
540
|
+
/**
|
|
541
|
+
* @description The skills of other Git repositories that `factory plan` pinned, by the skill's name (ADR 0146). A pin counts only
|
|
542
|
+
* while its `source` is the entry factory.yaml declares; the foreman and `factory render` fetch its commit and check
|
|
543
|
+
* its hash.
|
|
544
|
+
*/
|
|
545
|
+
skills?: {
|
|
546
|
+
[key: string]: components["schemas"]["FactoryLockSkillPin"];
|
|
547
|
+
};
|
|
460
548
|
};
|
|
461
549
|
/** @description One adapter's npm package `@opensoftware/<port>-<name>` at the newest version inside its range. */
|
|
462
550
|
FactoryLockAdapterPin: {
|
|
@@ -480,6 +568,15 @@ export type components = {
|
|
|
480
568
|
url: string;
|
|
481
569
|
};
|
|
482
570
|
};
|
|
571
|
+
/** @description One skill of another Git repository at the commit its ref had when `factory plan` pinned it. */
|
|
572
|
+
FactoryLockSkillPin: {
|
|
573
|
+
/** @description The entry of `skills` in factory.yaml that the pin was resolved from, `git+<url>#<ref>:<path>`. */
|
|
574
|
+
source: string;
|
|
575
|
+
/** @description The full commit, 40 hexadecimal characters, or 64 in a repository of SHA-256. */
|
|
576
|
+
commit: string;
|
|
577
|
+
/** @description The hash of the skill folder's files in the form of Go's modules: "h1:" and the base64 of a SHA-256. */
|
|
578
|
+
hash: string;
|
|
579
|
+
};
|
|
483
580
|
/**
|
|
484
581
|
* @description The version of a format inside its documents. Only 1 exists; a new major version of a format adds the next.
|
|
485
582
|
* @constant
|
|
@@ -1274,6 +1371,23 @@ export type components = {
|
|
|
1274
1371
|
/** @description A reference to a secret, resolved at the egress proxy, never in the sandbox: "ref:<provider>/<name>". */
|
|
1275
1372
|
SecretReference: string;
|
|
1276
1373
|
Sha256: string;
|
|
1374
|
+
/**
|
|
1375
|
+
* @description What `factory skills` of factory-spec prints (ADR 0141, ADR 0143): every skill the factory's agents use, read with
|
|
1376
|
+
* the rules of `factory lint` (ADR 0140), sorted by name. The console reads it for its pages `/skills` and
|
|
1377
|
+
* `/skills/<name>`, so it never parses a SKILL.md itself.
|
|
1378
|
+
*/
|
|
1379
|
+
SkillCatalog: {
|
|
1380
|
+
skills: components["schemas"]["CatalogSkill"][];
|
|
1381
|
+
};
|
|
1382
|
+
/** @description A host as an entry of `sandbox.egress`, like "api.linear.app". */
|
|
1383
|
+
SkillHost: string;
|
|
1384
|
+
/** @description The secrets a skill needs, by the variable that holds each in a sandbox, and the hosts it reaches (ADR 0140). */
|
|
1385
|
+
SkillNeeds: {
|
|
1386
|
+
secrets: components["schemas"]["SkillSecretName"][];
|
|
1387
|
+
hosts: components["schemas"]["SkillHost"][];
|
|
1388
|
+
};
|
|
1389
|
+
/** @description The variable of a secret in a sandbox (ADR 0068), like "LINEAR_TOKEN". */
|
|
1390
|
+
SkillSecretName: string;
|
|
1277
1391
|
Stage: {
|
|
1278
1392
|
stage: components["schemas"]["Name"];
|
|
1279
1393
|
/** @description Agent that runs the stage. Defaults to the agent with the stage's name. */
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export { diff as breakingChanges, type Schema } from "./breaking.ts";
|
|
|
4
4
|
export { FORMATS, type Format, formatNamed } from "./formats.ts";
|
|
5
5
|
export type { components, paths } from "./generated/types.ts";
|
|
6
6
|
export { type AttributeValue, type RunSpan, runSpans, type SpanAttributes } from "./run-record.ts";
|
|
7
|
+
export { type CopyEnds, copyEnds, SANDBOX_WORKSPACE } from "./sandbox.ts";
|
|
7
8
|
type Schemas = components["schemas"];
|
|
8
9
|
export type FactoryDefinition = Schemas["FactoryDefinition"];
|
|
9
10
|
export type WorkItem = Schemas["WorkItem"];
|
|
@@ -42,6 +43,13 @@ export type RunRetry = Schemas["RunRetry"];
|
|
|
42
43
|
export type RunAction = Schemas["RunAction"];
|
|
43
44
|
export type SandboxSpec = Schemas["SandboxSpec"];
|
|
44
45
|
export type FactoryLock = Schemas["FactoryLock"];
|
|
46
|
+
export type Access = Schemas["Access"];
|
|
47
|
+
export type AccessPerson = Schemas["AccessPerson"];
|
|
48
|
+
export type AccessRole = Schemas["AccessRole"];
|
|
49
|
+
export type SkillCatalog = Schemas["SkillCatalog"];
|
|
50
|
+
export type CatalogSkill = Schemas["CatalogSkill"];
|
|
51
|
+
export type CatalogSkillAgent = Schemas["CatalogSkillAgent"];
|
|
52
|
+
export type SkillNeeds = Schemas["SkillNeeds"];
|
|
45
53
|
export type Health = Schemas["Health"];
|
|
46
54
|
export type HealthCheck = Schemas["HealthCheck"];
|
|
47
55
|
/** One reason a document does not match its format. `path` is a JSON Pointer into the document. */
|
|
@@ -53,5 +61,13 @@ export interface Problem {
|
|
|
53
61
|
export declare function schemaOf(format: Format | string): Record<string, unknown>;
|
|
54
62
|
/** Every problem of the document; empty when it matches the format. */
|
|
55
63
|
export declare function validate(format: Format | string, document: unknown): Problem[];
|
|
64
|
+
/**
|
|
65
|
+
* The document of a YAML or JSON file and every problem of it against the format; JSON is YAML. A file that cannot
|
|
66
|
+
* be read or parsed throws.
|
|
67
|
+
*/
|
|
68
|
+
export declare function readDocument<T = unknown>(format: Format | string, file: string): {
|
|
69
|
+
document: T;
|
|
70
|
+
problems: Problem[];
|
|
71
|
+
};
|
|
56
72
|
/** True when the document matches the format, with its type narrowed. */
|
|
57
73
|
export declare function isValid<T>(format: Format | string, document: unknown): document is T;
|
package/dist/index.js
CHANGED
|
@@ -4,10 +4,12 @@ import { dirname, join } from "node:path";
|
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import Ajv2020, {} from "ajv/dist/2020.js";
|
|
6
6
|
import addFormats from "ajv-formats";
|
|
7
|
+
import { parse } from "yaml";
|
|
7
8
|
import { formatNamed } from "./formats.js";
|
|
8
9
|
export { diff as breakingChanges } from "./breaking.js";
|
|
9
10
|
export { FORMATS, formatNamed } from "./formats.js";
|
|
10
11
|
export { runSpans } from "./run-record.js";
|
|
12
|
+
export { copyEnds, SANDBOX_WORKSPACE } from "./sandbox.js";
|
|
11
13
|
/** The package root: dist/ and src/ both sit one level below it, next to the schema folders. */
|
|
12
14
|
const ROOT = join(dirname(fileURLToPath(import.meta.url)), "..");
|
|
13
15
|
const validators = new Map();
|
|
@@ -31,6 +33,14 @@ export function validate(format, document) {
|
|
|
31
33
|
}
|
|
32
34
|
return check(document) ? [] : problems(check.errors ?? []);
|
|
33
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* The document of a YAML or JSON file and every problem of it against the format; JSON is YAML. A file that cannot
|
|
38
|
+
* be read or parsed throws.
|
|
39
|
+
*/
|
|
40
|
+
export function readDocument(format, file) {
|
|
41
|
+
const document = parse(readFileSync(file, "utf8"));
|
|
42
|
+
return { document, problems: validate(format, document) };
|
|
43
|
+
}
|
|
34
44
|
/** True when the document matches the format, with its type narrowed. */
|
|
35
45
|
export function isValid(format, document) {
|
|
36
46
|
return validate(format, document).length === 0;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,oHAAoH;AACpH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,OAAO,EAAE,EAA2C,MAAM,kBAAkB,CAAC;AACpF,OAAO,UAAU,MAAM,aAAa,CAAC;AACrC,OAAO,EAAe,WAAW,EAAE,MAAM,cAAc,CAAC;AAGxD,OAAO,EAAE,IAAI,IAAI,eAAe,EAAe,MAAM,eAAe,CAAC;AACrE,OAAO,EAAE,OAAO,EAAe,WAAW,EAAE,MAAM,cAAc,CAAC;AAEjE,OAAO,EAAqC,QAAQ,EAAuB,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** The working folder of every command in a sandbox. */
|
|
2
|
+
export declare const SANDBOX_WORKSPACE = "/workspace";
|
|
3
|
+
/** The two ends of a copy. */
|
|
4
|
+
export interface CopyEnds {
|
|
5
|
+
/** The sandbox's ID. */
|
|
6
|
+
id: string;
|
|
7
|
+
/** The absolute path in the sandbox; a relative path is below the working folder. */
|
|
8
|
+
inSandbox: string;
|
|
9
|
+
/** The path on the client's side, as given. */
|
|
10
|
+
local: string;
|
|
11
|
+
/** Whether the copy goes into the sandbox. */
|
|
12
|
+
into: boolean;
|
|
13
|
+
}
|
|
14
|
+
/** The ends of `copy <from> <to>`, or the problem when not exactly one side names a sandbox. */
|
|
15
|
+
export declare function copyEnds(from: string | undefined, to: string | undefined): CopyEnds | {
|
|
16
|
+
problem: string;
|
|
17
|
+
};
|
package/dist/sandbox.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// The sandbox port's command line as its adapters and clients share it (ADR 0054, ADR 0147): the working folder of
|
|
2
|
+
// every sandbox, and the two ends of `copy <from> <to>`, whose sandbox side is `<ID>:<path>`.
|
|
3
|
+
import { posix } from "node:path";
|
|
4
|
+
/** The working folder of every command in a sandbox. */
|
|
5
|
+
export const SANDBOX_WORKSPACE = "/workspace";
|
|
6
|
+
const IN_SANDBOX = /^([0-9a-f]{12}):(.+)$/;
|
|
7
|
+
/** The ends of `copy <from> <to>`, or the problem when not exactly one side names a sandbox. */
|
|
8
|
+
export function copyEnds(from, to) {
|
|
9
|
+
if (!from || !to)
|
|
10
|
+
return { problem: "name what to copy and where to, like copy notes.md <ID>:notes.md" };
|
|
11
|
+
const source = IN_SANDBOX.exec(from);
|
|
12
|
+
const target = IN_SANDBOX.exec(to);
|
|
13
|
+
if (Boolean(source) === Boolean(target))
|
|
14
|
+
return { problem: "name the sandbox on one side only, as <ID>:<path>" };
|
|
15
|
+
const [, id, path] = (source ?? target);
|
|
16
|
+
return {
|
|
17
|
+
id: id,
|
|
18
|
+
inSandbox: posix.resolve(SANDBOX_WORKSPACE, path),
|
|
19
|
+
local: source ? to : from,
|
|
20
|
+
into: !source,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=sandbox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandbox.js","sourceRoot":"","sources":["../src/sandbox.ts"],"names":[],"mappings":"AAAA,mHAAmH;AACnH,8FAA8F;AAC9F,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAElC,wDAAwD;AACxD,MAAM,CAAC,MAAM,iBAAiB,GAAG,YAAY,CAAC;AAc9C,MAAM,UAAU,GAAG,uBAAuB,CAAC;AAE3C,gGAAgG;AAChG,MAAM,UAAU,QAAQ,CAAC,IAAwB,EAAE,EAAsB;IACvE,IAAI,CAAC,IAAI,IAAI,CAAC,EAAE;QAAE,OAAO,EAAE,OAAO,EAAE,kEAAkE,EAAE,CAAC;IACzG,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrC,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACnC,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,EAAE,OAAO,EAAE,mDAAmD,EAAE,CAAC;IACjH,MAAM,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,MAAM,IAAI,MAAM,CAAoB,CAAC;IAC3D,OAAO;QACL,EAAE,EAAE,EAAY;QAChB,SAAS,EAAE,KAAK,CAAC,OAAO,CAAC,iBAAiB,EAAE,IAAc,CAAC;QAC3D,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI;QACzB,IAAI,EAAE,CAAC,MAAM;KACd,CAAC;AACJ,CAAC"}
|
|
@@ -186,7 +186,7 @@
|
|
|
186
186
|
"items": {
|
|
187
187
|
"type": "string"
|
|
188
188
|
},
|
|
189
|
-
"description": "
|
|
189
|
+
"description": "The agent's skills: each a path to a skill folder, or a folder of another Git repository at a ref,\n`git+<url>#<ref>:<path>`, which `factory plan` pins in factory.lock (ADR 0146)."
|
|
190
190
|
},
|
|
191
191
|
"secrets": {
|
|
192
192
|
"type": "array",
|
|
@@ -101,6 +101,38 @@
|
|
|
101
101
|
}
|
|
102
102
|
},
|
|
103
103
|
"description": "The adapters under `providers` that `factory plan --adapters` pinned, per port and adapter name (ADR 0125). A pin\ncounts only while its `range` is the range factory.yaml declares."
|
|
104
|
+
},
|
|
105
|
+
"skills": {
|
|
106
|
+
"type": "object",
|
|
107
|
+
"properties": {},
|
|
108
|
+
"unevaluatedProperties": {
|
|
109
|
+
"type": "object",
|
|
110
|
+
"properties": {
|
|
111
|
+
"source": {
|
|
112
|
+
"type": "string",
|
|
113
|
+
"pattern": "^git\\+.+#[^:]+:.+$",
|
|
114
|
+
"description": "The entry of `skills` in factory.yaml that the pin was resolved from, `git+<url>#<ref>:<path>`."
|
|
115
|
+
},
|
|
116
|
+
"commit": {
|
|
117
|
+
"type": "string",
|
|
118
|
+
"pattern": "^([0-9a-f]{40}|[0-9a-f]{64})$",
|
|
119
|
+
"description": "The full commit, 40 hexadecimal characters, or 64 in a repository of SHA-256."
|
|
120
|
+
},
|
|
121
|
+
"hash": {
|
|
122
|
+
"type": "string",
|
|
123
|
+
"pattern": "^h1:[A-Za-z0-9+/]{43}=$",
|
|
124
|
+
"description": "The hash of the skill folder's files in the form of Go's modules: \"h1:\" and the base64 of a SHA-256."
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
"required": [
|
|
128
|
+
"source",
|
|
129
|
+
"commit",
|
|
130
|
+
"hash"
|
|
131
|
+
],
|
|
132
|
+
"description": "One skill of another Git repository at the commit its ref had when `factory plan` pinned it.",
|
|
133
|
+
"additionalProperties": false
|
|
134
|
+
},
|
|
135
|
+
"description": "The skills of other Git repositories that `factory plan` pinned, by the skill's name (ADR 0146). A pin counts only\nwhile its `source` is the entry factory.yaml declares; the foreman and `factory render` fetch its commit and check\nits hash."
|
|
104
136
|
}
|
|
105
137
|
},
|
|
106
138
|
"required": [
|
package/openapi/openapi.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"openapi": "3.1.0",
|
|
3
3
|
"info": {
|
|
4
4
|
"title": "Open Software Factory contracts",
|
|
5
|
-
"version": "2.
|
|
5
|
+
"version": "2.6.0",
|
|
6
6
|
"description": "The shared formats as named models, and the HTTP APIs of the modules. Generated by `pnpm build` in contracts/ from typespec/. Do not edit.",
|
|
7
7
|
"license": {
|
|
8
8
|
"name": "Apache-2.0",
|
|
@@ -860,6 +860,63 @@
|
|
|
860
860
|
],
|
|
861
861
|
"components": {
|
|
862
862
|
"schemas": {
|
|
863
|
+
"Access": {
|
|
864
|
+
"type": "object",
|
|
865
|
+
"properties": {
|
|
866
|
+
"version": {
|
|
867
|
+
"type": "number",
|
|
868
|
+
"const": 1,
|
|
869
|
+
"description": "The version of the access file's format."
|
|
870
|
+
},
|
|
871
|
+
"people": {
|
|
872
|
+
"type": "array",
|
|
873
|
+
"items": {
|
|
874
|
+
"$ref": "#/components/schemas/AccessPerson"
|
|
875
|
+
},
|
|
876
|
+
"minItems": 1,
|
|
877
|
+
"description": "The people who may sign in. An email appears once."
|
|
878
|
+
}
|
|
879
|
+
},
|
|
880
|
+
"required": [
|
|
881
|
+
"version",
|
|
882
|
+
"people"
|
|
883
|
+
],
|
|
884
|
+
"description": "access.yaml next to factory.yaml (ADR 0134): who may sign in to the factory UI, each person with one role. The\nconsole reads it at each request, so a merge that removes a person ends their access.",
|
|
885
|
+
"additionalProperties": false
|
|
886
|
+
},
|
|
887
|
+
"AccessPerson": {
|
|
888
|
+
"type": "object",
|
|
889
|
+
"properties": {
|
|
890
|
+
"email": {
|
|
891
|
+
"type": "string",
|
|
892
|
+
"format": "email"
|
|
893
|
+
},
|
|
894
|
+
"name": {
|
|
895
|
+
"type": "string",
|
|
896
|
+
"minLength": 1,
|
|
897
|
+
"description": "The name the UI shows, when the provider gives none."
|
|
898
|
+
},
|
|
899
|
+
"role": {
|
|
900
|
+
"$ref": "#/components/schemas/AccessRole",
|
|
901
|
+
"description": "`member` sees every page but Secrets and acts on work, runs and gates; `factory-engineer` also changes the factory\nfrom the UI; `admin` also stores and deletes secrets."
|
|
902
|
+
}
|
|
903
|
+
},
|
|
904
|
+
"required": [
|
|
905
|
+
"email",
|
|
906
|
+
"role"
|
|
907
|
+
],
|
|
908
|
+
"description": "One person: the email the provider verifies, compared in lower case, and their role.",
|
|
909
|
+
"additionalProperties": false
|
|
910
|
+
},
|
|
911
|
+
"AccessRole": {
|
|
912
|
+
"type": "string",
|
|
913
|
+
"enum": [
|
|
914
|
+
"member",
|
|
915
|
+
"factory-engineer",
|
|
916
|
+
"admin"
|
|
917
|
+
],
|
|
918
|
+
"description": "The roles of the factory UI (ADR 0134), from the least to the most it may do."
|
|
919
|
+
},
|
|
863
920
|
"Actor": {
|
|
864
921
|
"type": "object",
|
|
865
922
|
"properties": {
|
|
@@ -985,7 +1042,7 @@
|
|
|
985
1042
|
"items": {
|
|
986
1043
|
"type": "string"
|
|
987
1044
|
},
|
|
988
|
-
"description": "
|
|
1045
|
+
"description": "The agent's skills: each a path to a skill folder, or a folder of another Git repository at a ref,\n`git+<url>#<ref>:<path>`, which `factory plan` pins in factory.lock (ADR 0146)."
|
|
989
1046
|
},
|
|
990
1047
|
"secrets": {
|
|
991
1048
|
"type": "array",
|
|
@@ -1146,6 +1203,128 @@
|
|
|
1146
1203
|
"pattern": "^(?!/)(?!.*(^|/)\\.\\.(/|$))[^\\\\]+$",
|
|
1147
1204
|
"description": "A path inside the bundle: relative, forward slashes, no \"..\"."
|
|
1148
1205
|
},
|
|
1206
|
+
"CatalogSkill": {
|
|
1207
|
+
"type": "object",
|
|
1208
|
+
"properties": {
|
|
1209
|
+
"name": {
|
|
1210
|
+
"type": "string",
|
|
1211
|
+
"minLength": 1,
|
|
1212
|
+
"description": "The last segment of the skill's folder."
|
|
1213
|
+
},
|
|
1214
|
+
"description": {
|
|
1215
|
+
"anyOf": [
|
|
1216
|
+
{
|
|
1217
|
+
"type": "string"
|
|
1218
|
+
},
|
|
1219
|
+
{
|
|
1220
|
+
"type": "null"
|
|
1221
|
+
}
|
|
1222
|
+
],
|
|
1223
|
+
"description": "The description of its front matter; null without one."
|
|
1224
|
+
},
|
|
1225
|
+
"source": {
|
|
1226
|
+
"$ref": "#/components/schemas/CatalogSkillSource",
|
|
1227
|
+
"description": "Where the skill comes from."
|
|
1228
|
+
},
|
|
1229
|
+
"needs": {
|
|
1230
|
+
"$ref": "#/components/schemas/SkillNeeds",
|
|
1231
|
+
"description": "What it needs, from the `metadata` of its front matter."
|
|
1232
|
+
},
|
|
1233
|
+
"agents": {
|
|
1234
|
+
"type": "array",
|
|
1235
|
+
"items": {
|
|
1236
|
+
"$ref": "#/components/schemas/CatalogSkillAgent"
|
|
1237
|
+
},
|
|
1238
|
+
"description": "Each agent that uses it, sorted by name."
|
|
1239
|
+
},
|
|
1240
|
+
"files": {
|
|
1241
|
+
"type": "array",
|
|
1242
|
+
"items": {
|
|
1243
|
+
"type": "string"
|
|
1244
|
+
},
|
|
1245
|
+
"description": "The files of its folder, by their path below it, sorted."
|
|
1246
|
+
},
|
|
1247
|
+
"body": {
|
|
1248
|
+
"anyOf": [
|
|
1249
|
+
{
|
|
1250
|
+
"type": "string"
|
|
1251
|
+
},
|
|
1252
|
+
{
|
|
1253
|
+
"type": "null"
|
|
1254
|
+
}
|
|
1255
|
+
],
|
|
1256
|
+
"description": "The text of SKILL.md below the line that ends its front matter, all of it without front matter; null without a\nSKILL.md that is UTF-8 text."
|
|
1257
|
+
},
|
|
1258
|
+
"problems": {
|
|
1259
|
+
"type": "array",
|
|
1260
|
+
"items": {
|
|
1261
|
+
"type": "string"
|
|
1262
|
+
},
|
|
1263
|
+
"description": "The problems of its own files as `factory lint` prints them, relative to the folder of factory.yaml, and a folder\nwithout a SKILL.md."
|
|
1264
|
+
}
|
|
1265
|
+
},
|
|
1266
|
+
"required": [
|
|
1267
|
+
"name",
|
|
1268
|
+
"description",
|
|
1269
|
+
"source",
|
|
1270
|
+
"needs",
|
|
1271
|
+
"agents",
|
|
1272
|
+
"files",
|
|
1273
|
+
"body",
|
|
1274
|
+
"problems"
|
|
1275
|
+
],
|
|
1276
|
+
"description": "One skill, also one that lint refuses: then `problems` names why."
|
|
1277
|
+
},
|
|
1278
|
+
"CatalogSkillAgent": {
|
|
1279
|
+
"type": "object",
|
|
1280
|
+
"properties": {
|
|
1281
|
+
"name": {
|
|
1282
|
+
"type": "string",
|
|
1283
|
+
"minLength": 1
|
|
1284
|
+
},
|
|
1285
|
+
"secrets": {
|
|
1286
|
+
"type": "object",
|
|
1287
|
+
"properties": {},
|
|
1288
|
+
"unevaluatedProperties": {
|
|
1289
|
+
"$ref": "#/components/schemas/SecretReference"
|
|
1290
|
+
},
|
|
1291
|
+
"description": "The agent's reference whose variable is each secret the skill needs, by the variable."
|
|
1292
|
+
},
|
|
1293
|
+
"lacks": {
|
|
1294
|
+
"$ref": "#/components/schemas/SkillNeeds",
|
|
1295
|
+
"description": "Each needed secret the agent does not declare, and each needed host its sandbox does not allow."
|
|
1296
|
+
}
|
|
1297
|
+
},
|
|
1298
|
+
"required": [
|
|
1299
|
+
"name",
|
|
1300
|
+
"secrets",
|
|
1301
|
+
"lacks"
|
|
1302
|
+
],
|
|
1303
|
+
"description": "An agent that uses a skill."
|
|
1304
|
+
},
|
|
1305
|
+
"CatalogSkillSource": {
|
|
1306
|
+
"type": "object",
|
|
1307
|
+
"properties": {
|
|
1308
|
+
"folder": {
|
|
1309
|
+
"type": "string",
|
|
1310
|
+
"minLength": 1
|
|
1311
|
+
},
|
|
1312
|
+
"repository": {
|
|
1313
|
+
"type": "string",
|
|
1314
|
+
"minLength": 1,
|
|
1315
|
+
"description": "The URL of the other Git repository, as the entry `git+<url>#<ref>:<path>` names it."
|
|
1316
|
+
},
|
|
1317
|
+
"commit": {
|
|
1318
|
+
"type": "string",
|
|
1319
|
+
"pattern": "^([0-9a-f]{40}|[0-9a-f]{64})$",
|
|
1320
|
+
"description": "The commit of the other repository that factory.lock pins; none while factory.lock has no pin."
|
|
1321
|
+
}
|
|
1322
|
+
},
|
|
1323
|
+
"required": [
|
|
1324
|
+
"folder"
|
|
1325
|
+
],
|
|
1326
|
+
"description": "A folder of the factory's repository, relative to the folder of factory.yaml, like \"skills/release-notes\"; or, for a\nskill of another Git repository, its folder there with the repository's URL and the commit factory.lock pins\n(ADR 0146)."
|
|
1327
|
+
},
|
|
1149
1328
|
"CheckName": {
|
|
1150
1329
|
"type": "string",
|
|
1151
1330
|
"minLength": 1,
|
|
@@ -1456,6 +1635,14 @@
|
|
|
1456
1635
|
}
|
|
1457
1636
|
},
|
|
1458
1637
|
"description": "The adapters under `providers` that `factory plan --adapters` pinned, per port and adapter name (ADR 0125). A pin\ncounts only while its `range` is the range factory.yaml declares."
|
|
1638
|
+
},
|
|
1639
|
+
"skills": {
|
|
1640
|
+
"type": "object",
|
|
1641
|
+
"properties": {},
|
|
1642
|
+
"unevaluatedProperties": {
|
|
1643
|
+
"$ref": "#/components/schemas/FactoryLockSkillPin"
|
|
1644
|
+
},
|
|
1645
|
+
"description": "The skills of other Git repositories that `factory plan` pinned, by the skill's name (ADR 0146). A pin counts only\nwhile its `source` is the entry factory.yaml declares; the foreman and `factory render` fetch its commit and check\nits hash."
|
|
1459
1646
|
}
|
|
1460
1647
|
},
|
|
1461
1648
|
"required": [
|
|
@@ -1542,6 +1729,33 @@
|
|
|
1542
1729
|
],
|
|
1543
1730
|
"description": "One registry MCP server at its exact version, with the npm package or the remote it starts."
|
|
1544
1731
|
},
|
|
1732
|
+
"FactoryLockSkillPin": {
|
|
1733
|
+
"type": "object",
|
|
1734
|
+
"properties": {
|
|
1735
|
+
"source": {
|
|
1736
|
+
"type": "string",
|
|
1737
|
+
"pattern": "^git\\+.+#[^:]+:.+$",
|
|
1738
|
+
"description": "The entry of `skills` in factory.yaml that the pin was resolved from, `git+<url>#<ref>:<path>`."
|
|
1739
|
+
},
|
|
1740
|
+
"commit": {
|
|
1741
|
+
"type": "string",
|
|
1742
|
+
"pattern": "^([0-9a-f]{40}|[0-9a-f]{64})$",
|
|
1743
|
+
"description": "The full commit, 40 hexadecimal characters, or 64 in a repository of SHA-256."
|
|
1744
|
+
},
|
|
1745
|
+
"hash": {
|
|
1746
|
+
"type": "string",
|
|
1747
|
+
"pattern": "^h1:[A-Za-z0-9+/]{43}=$",
|
|
1748
|
+
"description": "The hash of the skill folder's files in the form of Go's modules: \"h1:\" and the base64 of a SHA-256."
|
|
1749
|
+
}
|
|
1750
|
+
},
|
|
1751
|
+
"required": [
|
|
1752
|
+
"source",
|
|
1753
|
+
"commit",
|
|
1754
|
+
"hash"
|
|
1755
|
+
],
|
|
1756
|
+
"description": "One skill of another Git repository at the commit its ref had when `factory plan` pinned it.",
|
|
1757
|
+
"additionalProperties": false
|
|
1758
|
+
},
|
|
1545
1759
|
"FormatVersion": {
|
|
1546
1760
|
"type": "integer",
|
|
1547
1761
|
"minimum": 1,
|
|
@@ -3650,6 +3864,53 @@
|
|
|
3650
3864
|
"type": "string",
|
|
3651
3865
|
"pattern": "^[0-9a-f]{64}$"
|
|
3652
3866
|
},
|
|
3867
|
+
"SkillCatalog": {
|
|
3868
|
+
"type": "object",
|
|
3869
|
+
"properties": {
|
|
3870
|
+
"skills": {
|
|
3871
|
+
"type": "array",
|
|
3872
|
+
"items": {
|
|
3873
|
+
"$ref": "#/components/schemas/CatalogSkill"
|
|
3874
|
+
}
|
|
3875
|
+
}
|
|
3876
|
+
},
|
|
3877
|
+
"required": [
|
|
3878
|
+
"skills"
|
|
3879
|
+
],
|
|
3880
|
+
"description": "What `factory skills` of factory-spec prints (ADR 0141, ADR 0143): every skill the factory's agents use, read with\nthe rules of `factory lint` (ADR 0140), sorted by name. The console reads it for its pages `/skills` and\n`/skills/<name>`, so it never parses a SKILL.md itself."
|
|
3881
|
+
},
|
|
3882
|
+
"SkillHost": {
|
|
3883
|
+
"type": "string",
|
|
3884
|
+
"pattern": "^[a-z0-9-]+(\\.[a-z0-9-]+)*$",
|
|
3885
|
+
"description": "A host as an entry of `sandbox.egress`, like \"api.linear.app\"."
|
|
3886
|
+
},
|
|
3887
|
+
"SkillNeeds": {
|
|
3888
|
+
"type": "object",
|
|
3889
|
+
"properties": {
|
|
3890
|
+
"secrets": {
|
|
3891
|
+
"type": "array",
|
|
3892
|
+
"items": {
|
|
3893
|
+
"$ref": "#/components/schemas/SkillSecretName"
|
|
3894
|
+
}
|
|
3895
|
+
},
|
|
3896
|
+
"hosts": {
|
|
3897
|
+
"type": "array",
|
|
3898
|
+
"items": {
|
|
3899
|
+
"$ref": "#/components/schemas/SkillHost"
|
|
3900
|
+
}
|
|
3901
|
+
}
|
|
3902
|
+
},
|
|
3903
|
+
"required": [
|
|
3904
|
+
"secrets",
|
|
3905
|
+
"hosts"
|
|
3906
|
+
],
|
|
3907
|
+
"description": "The secrets a skill needs, by the variable that holds each in a sandbox, and the hosts it reaches (ADR 0140)."
|
|
3908
|
+
},
|
|
3909
|
+
"SkillSecretName": {
|
|
3910
|
+
"type": "string",
|
|
3911
|
+
"pattern": "^[A-Z0-9_]+$",
|
|
3912
|
+
"description": "The variable of a secret in a sandbox (ADR 0068), like \"LINEAR_TOKEN\"."
|
|
3913
|
+
},
|
|
3653
3914
|
"Stage": {
|
|
3654
3915
|
"type": "object",
|
|
3655
3916
|
"properties": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opensoftware/contracts",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.0",
|
|
4
4
|
"description": "The shared formats of the open software factory: JSON Schemas, TypeScript types and the osf-contracts CLI.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"@typespec/openapi": "1.17.0",
|
|
45
45
|
"@typespec/openapi3": "1.17.0",
|
|
46
46
|
"openapi-typescript": "7.13.0",
|
|
47
|
-
"@opensoftware/testkit": "0.
|
|
47
|
+
"@opensoftware/testkit": "0.39.0"
|
|
48
48
|
},
|
|
49
49
|
"scripts": {
|
|
50
50
|
"build": "node scripts/build.ts",
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://raw.githubusercontent.com/sesigl/open-software-factory/main/contracts/skill-catalog/skill-catalog.schema.json",
|
|
4
|
+
"title": "SkillCatalog",
|
|
5
|
+
"$comment": "Generated by `pnpm build` in contracts/ from typespec/. Do not edit.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"skills": {
|
|
9
|
+
"type": "array",
|
|
10
|
+
"items": {
|
|
11
|
+
"type": "object",
|
|
12
|
+
"properties": {
|
|
13
|
+
"name": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"minLength": 1,
|
|
16
|
+
"description": "The last segment of the skill's folder."
|
|
17
|
+
},
|
|
18
|
+
"description": {
|
|
19
|
+
"anyOf": [
|
|
20
|
+
{
|
|
21
|
+
"type": "string"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"type": "null"
|
|
25
|
+
}
|
|
26
|
+
],
|
|
27
|
+
"description": "The description of its front matter; null without one."
|
|
28
|
+
},
|
|
29
|
+
"source": {
|
|
30
|
+
"type": "object",
|
|
31
|
+
"properties": {
|
|
32
|
+
"folder": {
|
|
33
|
+
"type": "string",
|
|
34
|
+
"minLength": 1
|
|
35
|
+
},
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "string",
|
|
38
|
+
"minLength": 1,
|
|
39
|
+
"description": "The URL of the other Git repository, as the entry `git+<url>#<ref>:<path>` names it."
|
|
40
|
+
},
|
|
41
|
+
"commit": {
|
|
42
|
+
"type": "string",
|
|
43
|
+
"pattern": "^([0-9a-f]{40}|[0-9a-f]{64})$",
|
|
44
|
+
"description": "The commit of the other repository that factory.lock pins; none while factory.lock has no pin."
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"required": [
|
|
48
|
+
"folder"
|
|
49
|
+
],
|
|
50
|
+
"description": "Where the skill comes from."
|
|
51
|
+
},
|
|
52
|
+
"needs": {
|
|
53
|
+
"type": "object",
|
|
54
|
+
"properties": {
|
|
55
|
+
"secrets": {
|
|
56
|
+
"type": "array",
|
|
57
|
+
"items": {
|
|
58
|
+
"type": "string",
|
|
59
|
+
"pattern": "^[A-Z0-9_]+$",
|
|
60
|
+
"description": "The variable of a secret in a sandbox (ADR 0068), like \"LINEAR_TOKEN\"."
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
"hosts": {
|
|
64
|
+
"type": "array",
|
|
65
|
+
"items": {
|
|
66
|
+
"type": "string",
|
|
67
|
+
"pattern": "^[a-z0-9-]+(\\.[a-z0-9-]+)*$",
|
|
68
|
+
"description": "A host as an entry of `sandbox.egress`, like \"api.linear.app\"."
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
"required": [
|
|
73
|
+
"secrets",
|
|
74
|
+
"hosts"
|
|
75
|
+
],
|
|
76
|
+
"description": "What it needs, from the `metadata` of its front matter."
|
|
77
|
+
},
|
|
78
|
+
"agents": {
|
|
79
|
+
"type": "array",
|
|
80
|
+
"items": {
|
|
81
|
+
"type": "object",
|
|
82
|
+
"properties": {
|
|
83
|
+
"name": {
|
|
84
|
+
"type": "string",
|
|
85
|
+
"minLength": 1
|
|
86
|
+
},
|
|
87
|
+
"secrets": {
|
|
88
|
+
"type": "object",
|
|
89
|
+
"properties": {},
|
|
90
|
+
"unevaluatedProperties": {
|
|
91
|
+
"type": "string",
|
|
92
|
+
"pattern": "^ref:[a-z][a-z0-9-]*/[A-Za-z0-9_./-]+$",
|
|
93
|
+
"description": "A reference to a secret, resolved at the egress proxy, never in the sandbox: \"ref:<provider>/<name>\"."
|
|
94
|
+
},
|
|
95
|
+
"description": "The agent's reference whose variable is each secret the skill needs, by the variable."
|
|
96
|
+
},
|
|
97
|
+
"lacks": {
|
|
98
|
+
"type": "object",
|
|
99
|
+
"properties": {
|
|
100
|
+
"secrets": {
|
|
101
|
+
"type": "array",
|
|
102
|
+
"items": {
|
|
103
|
+
"type": "string",
|
|
104
|
+
"pattern": "^[A-Z0-9_]+$",
|
|
105
|
+
"description": "The variable of a secret in a sandbox (ADR 0068), like \"LINEAR_TOKEN\"."
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
"hosts": {
|
|
109
|
+
"type": "array",
|
|
110
|
+
"items": {
|
|
111
|
+
"type": "string",
|
|
112
|
+
"pattern": "^[a-z0-9-]+(\\.[a-z0-9-]+)*$",
|
|
113
|
+
"description": "A host as an entry of `sandbox.egress`, like \"api.linear.app\"."
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
"required": [
|
|
118
|
+
"secrets",
|
|
119
|
+
"hosts"
|
|
120
|
+
],
|
|
121
|
+
"description": "Each needed secret the agent does not declare, and each needed host its sandbox does not allow."
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
"required": [
|
|
125
|
+
"name",
|
|
126
|
+
"secrets",
|
|
127
|
+
"lacks"
|
|
128
|
+
],
|
|
129
|
+
"description": "An agent that uses a skill."
|
|
130
|
+
},
|
|
131
|
+
"description": "Each agent that uses it, sorted by name."
|
|
132
|
+
},
|
|
133
|
+
"files": {
|
|
134
|
+
"type": "array",
|
|
135
|
+
"items": {
|
|
136
|
+
"type": "string"
|
|
137
|
+
},
|
|
138
|
+
"description": "The files of its folder, by their path below it, sorted."
|
|
139
|
+
},
|
|
140
|
+
"body": {
|
|
141
|
+
"anyOf": [
|
|
142
|
+
{
|
|
143
|
+
"type": "string"
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
"type": "null"
|
|
147
|
+
}
|
|
148
|
+
],
|
|
149
|
+
"description": "The text of SKILL.md below the line that ends its front matter, all of it without front matter; null without a\nSKILL.md that is UTF-8 text."
|
|
150
|
+
},
|
|
151
|
+
"problems": {
|
|
152
|
+
"type": "array",
|
|
153
|
+
"items": {
|
|
154
|
+
"type": "string"
|
|
155
|
+
},
|
|
156
|
+
"description": "The problems of its own files as `factory lint` prints them, relative to the folder of factory.yaml, and a folder\nwithout a SKILL.md."
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
"required": [
|
|
160
|
+
"name",
|
|
161
|
+
"description",
|
|
162
|
+
"source",
|
|
163
|
+
"needs",
|
|
164
|
+
"agents",
|
|
165
|
+
"files",
|
|
166
|
+
"body",
|
|
167
|
+
"problems"
|
|
168
|
+
],
|
|
169
|
+
"description": "One skill, also one that lint refuses: then `problems` names why."
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
"required": [
|
|
174
|
+
"skills"
|
|
175
|
+
],
|
|
176
|
+
"description": "What `factory skills` of factory-spec prints (ADR 0141, ADR 0143): every skill the factory's agents use, read with\nthe rules of `factory lint` (ADR 0140), sorted by name. The console reads it for its pages `/skills` and\n`/skills/<name>`, so it never parses a SKILL.md itself."
|
|
177
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import "@typespec/json-schema";
|
|
2
|
+
import "./common.tsp";
|
|
3
|
+
|
|
4
|
+
using TypeSpec.JsonSchema;
|
|
5
|
+
|
|
6
|
+
namespace Osf.Contracts;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* access.yaml next to factory.yaml (ADR 0134): who may sign in to the factory UI, each person with one role. The
|
|
10
|
+
* console reads it at each request, so a merge that removes a person ends their access.
|
|
11
|
+
*/
|
|
12
|
+
@extension("additionalProperties", Json<false>)
|
|
13
|
+
model Access {
|
|
14
|
+
/** The version of the access file's format. */
|
|
15
|
+
version: 1;
|
|
16
|
+
|
|
17
|
+
/** The people who may sign in. An email appears once. */
|
|
18
|
+
@minItems(1)
|
|
19
|
+
people: AccessPerson[];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** One person: the email the provider verifies, compared in lower case, and their role. */
|
|
23
|
+
@extension("additionalProperties", Json<false>)
|
|
24
|
+
model AccessPerson {
|
|
25
|
+
@format("email")
|
|
26
|
+
email: string;
|
|
27
|
+
|
|
28
|
+
/** The name the UI shows, when the provider gives none. */
|
|
29
|
+
@minLength(1)
|
|
30
|
+
name?: string;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* `member` sees every page but Secrets and acts on work, runs and gates; `factory-engineer` also changes the factory
|
|
34
|
+
* from the UI; `admin` also stores and deletes secrets.
|
|
35
|
+
*/
|
|
36
|
+
role: AccessRole;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** The roles of the factory UI (ADR 0134), from the least to the most it may do. */
|
|
40
|
+
enum AccessRole {
|
|
41
|
+
member,
|
|
42
|
+
`factory-engineer`,
|
|
43
|
+
admin,
|
|
44
|
+
}
|
|
@@ -110,7 +110,10 @@ model Agent {
|
|
|
110
110
|
/** MCP servers: `registry:<name>@<version>` or `<name>@<version>`. */
|
|
111
111
|
mcp?: string[];
|
|
112
112
|
|
|
113
|
-
/**
|
|
113
|
+
/**
|
|
114
|
+
* The agent's skills: each a path to a skill folder, or a folder of another Git repository at a ref,
|
|
115
|
+
* `git+<url>#<ref>:<path>`, which `factory plan` pins in factory.lock (ADR 0146).
|
|
116
|
+
*/
|
|
114
117
|
skills?: string[];
|
|
115
118
|
|
|
116
119
|
secrets?: SecretReference[];
|
|
@@ -21,6 +21,29 @@ model FactoryLock {
|
|
|
21
21
|
* counts only while its `range` is the range factory.yaml declares.
|
|
22
22
|
*/
|
|
23
23
|
adapters?: Record<Record<FactoryLockAdapterPin>>;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The skills of other Git repositories that `factory plan` pinned, by the skill's name (ADR 0146). A pin counts only
|
|
27
|
+
* while its `source` is the entry factory.yaml declares; the foreman and `factory render` fetch its commit and check
|
|
28
|
+
* its hash.
|
|
29
|
+
*/
|
|
30
|
+
skills?: Record<FactoryLockSkillPin>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** One skill of another Git repository at the commit its ref had when `factory plan` pinned it. */
|
|
34
|
+
@extension("additionalProperties", Json<false>)
|
|
35
|
+
model FactoryLockSkillPin {
|
|
36
|
+
/** The entry of `skills` in factory.yaml that the pin was resolved from, `git+<url>#<ref>:<path>`. */
|
|
37
|
+
@pattern("^git\\+.+#[^:]+:.+$")
|
|
38
|
+
source: string;
|
|
39
|
+
|
|
40
|
+
/** The full commit, 40 hexadecimal characters, or 64 in a repository of SHA-256. */
|
|
41
|
+
@pattern("^([0-9a-f]{40}|[0-9a-f]{64})$")
|
|
42
|
+
commit: string;
|
|
43
|
+
|
|
44
|
+
/** The hash of the skill folder's files in the form of Go's modules: "h1:" and the base64 of a SHA-256. */
|
|
45
|
+
@pattern("^h1:[A-Za-z0-9+/]{43}=$")
|
|
46
|
+
hash: string;
|
|
24
47
|
}
|
|
25
48
|
|
|
26
49
|
/** One registry MCP server at its exact version, with the npm package or the remote it starts. */
|
package/typespec/main.tsp
CHANGED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import "@typespec/json-schema";
|
|
2
|
+
import "./common.tsp";
|
|
3
|
+
import "./factory-definition.tsp";
|
|
4
|
+
|
|
5
|
+
using TypeSpec.JsonSchema;
|
|
6
|
+
|
|
7
|
+
namespace Osf.Contracts;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* What `factory skills` of factory-spec prints (ADR 0141, ADR 0143): every skill the factory's agents use, read with
|
|
11
|
+
* the rules of `factory lint` (ADR 0140), sorted by name. The console reads it for its pages `/skills` and
|
|
12
|
+
* `/skills/<name>`, so it never parses a SKILL.md itself.
|
|
13
|
+
*/
|
|
14
|
+
model SkillCatalog {
|
|
15
|
+
skills: CatalogSkill[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** One skill, also one that lint refuses: then `problems` names why. */
|
|
19
|
+
model CatalogSkill {
|
|
20
|
+
/** The last segment of the skill's folder. */
|
|
21
|
+
@minLength(1)
|
|
22
|
+
name: string;
|
|
23
|
+
|
|
24
|
+
/** The description of its front matter; null without one. */
|
|
25
|
+
description: string | null;
|
|
26
|
+
|
|
27
|
+
/** Where the skill comes from. */
|
|
28
|
+
source: CatalogSkillSource;
|
|
29
|
+
|
|
30
|
+
/** What it needs, from the `metadata` of its front matter. */
|
|
31
|
+
needs: SkillNeeds;
|
|
32
|
+
|
|
33
|
+
/** Each agent that uses it, sorted by name. */
|
|
34
|
+
agents: CatalogSkillAgent[];
|
|
35
|
+
|
|
36
|
+
/** The files of its folder, by their path below it, sorted. */
|
|
37
|
+
files: string[];
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* The text of SKILL.md below the line that ends its front matter, all of it without front matter; null without a
|
|
41
|
+
* SKILL.md that is UTF-8 text.
|
|
42
|
+
*/
|
|
43
|
+
body: string | null;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The problems of its own files as `factory lint` prints them, relative to the folder of factory.yaml, and a folder
|
|
47
|
+
* without a SKILL.md.
|
|
48
|
+
*/
|
|
49
|
+
problems: string[];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* A folder of the factory's repository, relative to the folder of factory.yaml, like "skills/release-notes"; or, for a
|
|
54
|
+
* skill of another Git repository, its folder there with the repository's URL and the commit factory.lock pins
|
|
55
|
+
* (ADR 0146).
|
|
56
|
+
*/
|
|
57
|
+
model CatalogSkillSource {
|
|
58
|
+
@minLength(1)
|
|
59
|
+
folder: string;
|
|
60
|
+
|
|
61
|
+
/** The URL of the other Git repository, as the entry `git+<url>#<ref>:<path>` names it. */
|
|
62
|
+
@minLength(1)
|
|
63
|
+
repository?: string;
|
|
64
|
+
|
|
65
|
+
/** The commit of the other repository that factory.lock pins; none while factory.lock has no pin. */
|
|
66
|
+
@pattern("^([0-9a-f]{40}|[0-9a-f]{64})$")
|
|
67
|
+
commit?: string;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** The secrets a skill needs, by the variable that holds each in a sandbox, and the hosts it reaches (ADR 0140). */
|
|
71
|
+
model SkillNeeds {
|
|
72
|
+
secrets: SkillSecretName[];
|
|
73
|
+
hosts: SkillHost[];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** An agent that uses a skill. */
|
|
77
|
+
model CatalogSkillAgent {
|
|
78
|
+
@minLength(1)
|
|
79
|
+
name: string;
|
|
80
|
+
|
|
81
|
+
/** The agent's reference whose variable is each secret the skill needs, by the variable. */
|
|
82
|
+
secrets: Record<SecretReference>;
|
|
83
|
+
|
|
84
|
+
/** Each needed secret the agent does not declare, and each needed host its sandbox does not allow. */
|
|
85
|
+
lacks: SkillNeeds;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** The variable of a secret in a sandbox (ADR 0068), like "LINEAR_TOKEN". */
|
|
89
|
+
@pattern("^[A-Z0-9_]+$")
|
|
90
|
+
scalar SkillSecretName extends string;
|
|
91
|
+
|
|
92
|
+
/** A host as an entry of `sandbox.egress`, like "api.linear.app". */
|
|
93
|
+
@pattern("^[a-z0-9-]+(\\.[a-z0-9-]+)*$")
|
|
94
|
+
scalar SkillHost extends string;
|