@opensoftware/contracts 2.5.0 → 2.7.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 +27 -1
- package/dist/cli.js +3 -6
- package/dist/cli.js.map +1 -1
- package/dist/formats.js +3 -0
- package/dist/formats.js.map +1 -1
- package/dist/generated/types.d.ts +136 -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 +330 -2
- package/package.json +2 -2
- package/person-keys/person-keys.schema.json +59 -0
- package/person-proof/person-proof.schema.json +75 -0
- package/skill-catalog/skill-catalog.schema.json +177 -0
- package/typespec/factory-definition.tsp +4 -1
- package/typespec/factory-lock.tsp +23 -0
- package/typespec/main.tsp +2 -0
- package/typespec/person-proof.tsp +79 -0
- package/typespec/skill-catalog.tsp +94 -0
|
@@ -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
|
+
}
|
|
@@ -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,79 @@
|
|
|
1
|
+
import "@typespec/json-schema";
|
|
2
|
+
import "./common.tsp";
|
|
3
|
+
import "./access.tsp";
|
|
4
|
+
|
|
5
|
+
using TypeSpec.JsonSchema;
|
|
6
|
+
|
|
7
|
+
namespace Osf.Contracts;
|
|
8
|
+
|
|
9
|
+
/** 43 characters of base64url without padding: 32 bytes, like a SHA-256 or an Ed25519 public key. */
|
|
10
|
+
@pattern("^[A-Za-z0-9_-]{43}$")
|
|
11
|
+
scalar Base64url32 extends string;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The claims of a person proof (ADR 0151): the JSON Web Token that the console signs for one call of a module API in
|
|
15
|
+
* the name of a person, sent in the header `OSF-Person`, signed with `alg` "Ed25519" and `typ` "osf-person+jwt". It is
|
|
16
|
+
* bound to one module, one request and 30 seconds. Open: a module ignores a claim it does not know.
|
|
17
|
+
*/
|
|
18
|
+
model PersonProof {
|
|
19
|
+
/** The issuer: "console" for the console, or the name a team gives its own tool in the modules' setting. */
|
|
20
|
+
iss: Name;
|
|
21
|
+
|
|
22
|
+
/** The person, as the module records the actor's `id`: an email in lower case, or the sign-in proxy's value. */
|
|
23
|
+
@minLength(1)
|
|
24
|
+
sub: string;
|
|
25
|
+
|
|
26
|
+
/** The name to show, recorded as the actor's `displayName`. */
|
|
27
|
+
@minLength(1)
|
|
28
|
+
name?: string;
|
|
29
|
+
|
|
30
|
+
/** The person's role from the access file at the time of the call. */
|
|
31
|
+
role: AccessRole;
|
|
32
|
+
|
|
33
|
+
/** The module the call goes to: "foreman", "intake" or "gatekeeper". */
|
|
34
|
+
aud: Name;
|
|
35
|
+
|
|
36
|
+
/** When it was signed, in seconds since 1970-01-01T00:00:00Z. */
|
|
37
|
+
@minValue(0)
|
|
38
|
+
iat: safeint;
|
|
39
|
+
|
|
40
|
+
/** When it ends, in seconds since 1970-01-01T00:00:00Z; at most 60 seconds after `iat`. */
|
|
41
|
+
@minValue(0)
|
|
42
|
+
exp: safeint;
|
|
43
|
+
|
|
44
|
+
/** The request's method in upper case. */
|
|
45
|
+
@pattern("^[A-Z]+$")
|
|
46
|
+
method: string;
|
|
47
|
+
|
|
48
|
+
/** The request's path as sent in the request line, without the query. */
|
|
49
|
+
@pattern("^/[^?#]*$")
|
|
50
|
+
path: string;
|
|
51
|
+
|
|
52
|
+
/** The SHA-256 of the request's body bytes as sent, in base64url without padding. */
|
|
53
|
+
bodyDigest: Base64url32;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* The public keys of an issuer of person proofs (ADR 0151): a JSON Web Key Set (RFC 7517), as the console publishes it
|
|
58
|
+
* at `/.well-known/jwks.json`. The first key of the issuer's setting signs; every key here verifies.
|
|
59
|
+
*/
|
|
60
|
+
@extension("additionalProperties", Json<false>)
|
|
61
|
+
model PersonKeys {
|
|
62
|
+
@minItems(1)
|
|
63
|
+
keys: PersonKey[];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** One Ed25519 public key. Closed: a key with its private part `d` or any other member is invalid. */
|
|
67
|
+
@extension("additionalProperties", Json<false>)
|
|
68
|
+
model PersonKey {
|
|
69
|
+
kty: "OKP";
|
|
70
|
+
crv: "Ed25519";
|
|
71
|
+
alg: "Ed25519";
|
|
72
|
+
use: "sig";
|
|
73
|
+
|
|
74
|
+
/** The key's JWK thumbprint (RFC 7638) with SHA-256; a proof names it in its `kid`. */
|
|
75
|
+
kid: Base64url32;
|
|
76
|
+
|
|
77
|
+
/** The public key. */
|
|
78
|
+
x: Base64url32;
|
|
79
|
+
}
|
|
@@ -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;
|