@afps-spec/schema 1.0.0 → 1.0.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@afps-spec/schema",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "src",
@@ -11,12 +11,17 @@
11
11
  "./v1/*": "./v1/*"
12
12
  },
13
13
  "scripts": {
14
- "generate": "bun src/generate.ts"
14
+ "generate": "bun src/generate.ts",
15
+ "check": "bun src/generate.ts --check",
16
+ "test": "bun test",
17
+ "test:conformance": "bun test tests/conformance.test.ts"
15
18
  },
16
19
  "dependencies": {
20
+ "semver": "^7.7.4",
17
21
  "zod": "^4.3.6"
18
22
  },
19
23
  "devDependencies": {
24
+ "@types/semver": "^7.7.1",
20
25
  "bun-types": "latest"
21
26
  }
22
27
  }
package/src/generate.ts CHANGED
@@ -3,12 +3,14 @@
3
3
  *
4
4
  * Change MAJOR to generate schemas for a different spec version.
5
5
  *
6
- * Usage: bun src/generate.ts (from schema/)
6
+ * Usage:
7
+ * bun src/generate.ts Generate/update JSON schemas
8
+ * bun src/generate.ts --check Verify committed schemas match Zod source (CI)
7
9
  */
8
10
 
9
11
  import { toJSONSchema } from "zod/v4/core";
10
12
  import { resolve, dirname } from "node:path";
11
- import { writeFile, mkdir } from "node:fs/promises";
13
+ import { writeFile, mkdir, readFile } from "node:fs/promises";
12
14
  import { createSchemas } from "./schemas.ts";
13
15
 
14
16
  const MAJOR = 1;
@@ -16,6 +18,8 @@ const VERSION_TAG = `v${MAJOR}`;
16
18
  const BASE_URL = "https://afps.appstrate.dev/schema";
17
19
  const OUTPUT_DIR = resolve(dirname(import.meta.filename!), "..", VERSION_TAG);
18
20
 
21
+ const isCheck = process.argv.includes("--check");
22
+
19
23
  const { flowManifestSchema, skillManifestSchema, toolManifestSchema, providerManifestSchema } =
20
24
  createSchemas(MAJOR);
21
25
 
@@ -54,7 +58,11 @@ const entries = [
54
58
  },
55
59
  ];
56
60
 
57
- await mkdir(OUTPUT_DIR, { recursive: true });
61
+ if (!isCheck) {
62
+ await mkdir(OUTPUT_DIR, { recursive: true });
63
+ }
64
+
65
+ let mismatch = false;
58
66
 
59
67
  for (const entry of entries) {
60
68
  const jsonSchema = toJSONSchema(entry.schema, {
@@ -72,9 +80,36 @@ for (const entry of entries) {
72
80
  ...jsonSchema,
73
81
  };
74
82
 
83
+ const generated = JSON.stringify(final, null, 2) + "\n";
75
84
  const filePath = resolve(OUTPUT_DIR, entry.filename);
76
- await writeFile(filePath, JSON.stringify(final, null, 2) + "\n");
77
- console.log(` ✓ ${VERSION_TAG}/${entry.filename}`);
85
+
86
+ if (isCheck) {
87
+ let committed: string;
88
+ try {
89
+ committed = await readFile(filePath, "utf-8");
90
+ } catch {
91
+ console.error(` ✗ ${VERSION_TAG}/${entry.filename} — file missing`);
92
+ mismatch = true;
93
+ continue;
94
+ }
95
+ if (committed !== generated) {
96
+ console.error(` ✗ ${VERSION_TAG}/${entry.filename} — out of date`);
97
+ mismatch = true;
98
+ } else {
99
+ console.log(` ✓ ${VERSION_TAG}/${entry.filename}`);
100
+ }
101
+ } else {
102
+ await writeFile(filePath, generated);
103
+ console.log(` ✓ ${VERSION_TAG}/${entry.filename}`);
104
+ }
78
105
  }
79
106
 
80
- console.log(`\nGenerated ${entries.length} schemas in ${OUTPUT_DIR}`);
107
+ if (isCheck) {
108
+ if (mismatch) {
109
+ console.error("\nJSON schemas are out of date. Run `bun run generate` to update.");
110
+ process.exit(1);
111
+ }
112
+ console.log("\nAll JSON schemas are up to date.");
113
+ } else {
114
+ console.log(`\nGenerated ${entries.length} schemas in ${OUTPUT_DIR}`);
115
+ }
package/src/index.ts CHANGED
@@ -11,4 +11,14 @@ export {
11
11
  skillManifestSchema,
12
12
  toolManifestSchema,
13
13
  providerManifestSchema,
14
+ // Shared sub-schemas — reusable by consumers
15
+ authModeEnum,
16
+ providerDefinition,
17
+ oauth2Config,
18
+ oauth1Config,
19
+ credentialsConfig,
20
+ providerConfiguration,
21
+ setupGuide,
22
+ schemaProperty,
23
+ schemaObject,
14
24
  } from "./schemas.ts";
package/src/schemas.ts CHANGED
@@ -11,6 +11,7 @@
11
11
  */
12
12
 
13
13
  import { z } from "zod";
14
+ import semver from "semver";
14
15
 
15
16
  // ─────────────────────────────────────────────
16
17
  // Shared patterns and primitives
@@ -19,8 +20,17 @@ import { z } from "zod";
19
20
  const SLUG_PATTERN = "[a-z0-9]([a-z0-9-]*[a-z0-9])?";
20
21
  const SCOPED_NAME_REGEX = new RegExp(`^@${SLUG_PATTERN}\\/${SLUG_PATTERN}$`);
21
22
 
22
- const scopedName = z.string().regex(SCOPED_NAME_REGEX);
23
- const semverRange = z.string().min(1);
23
+ const scopedName = z.string().regex(SCOPED_NAME_REGEX, {
24
+ error: "Must follow @scope/name format",
25
+ });
26
+
27
+ const semverVersion = z.string().refine((v) => semver.valid(v) !== null, {
28
+ error: "Must be a valid semver version (e.g. 1.0.0)",
29
+ });
30
+
31
+ const semverRange = z.string().refine((v) => semver.validRange(v) !== null, {
32
+ error: "Must be a valid semver range (e.g. ^1.0.0, ~2.1, >=3.0.0)",
33
+ });
24
34
 
25
35
  // ─────────────────────────────────────────────
26
36
  // Schema system (§5)
@@ -28,7 +38,7 @@ const semverRange = z.string().min(1);
28
38
 
29
39
  const fieldTypeEnum = z.enum(["string", "number", "boolean", "array", "object", "file"]);
30
40
 
31
- const schemaProperty = z.looseObject({
41
+ export const schemaProperty = z.looseObject({
32
42
  type: fieldTypeEnum,
33
43
  description: z.string().optional(),
34
44
  default: z.unknown().optional(),
@@ -38,10 +48,10 @@ const schemaProperty = z.looseObject({
38
48
  accept: z.string().optional(),
39
49
  maxSize: z.number().positive().optional(),
40
50
  multiple: z.boolean().optional(),
41
- maxFiles: z.number().positive().optional(),
51
+ maxFiles: z.number().int().positive().optional(),
42
52
  });
43
53
 
44
- const schemaObject = z.looseObject({
54
+ export const schemaObject = z.looseObject({
45
55
  type: z.literal("object"),
46
56
  properties: z.record(z.string(), schemaProperty),
47
57
  required: z.array(z.string()).optional(),
@@ -68,7 +78,7 @@ const dependenciesSchema = z
68
78
  // Provider configuration (§4.4)
69
79
  // ─────────────────────────────────────────────
70
80
 
71
- const providerConfiguration = z.looseObject({
81
+ export const providerConfiguration = z.looseObject({
72
82
  scopes: z.array(z.string()).optional(),
73
83
  connectionMode: z.enum(["user", "admin"]).optional(),
74
84
  });
@@ -87,31 +97,36 @@ const toolInterface = z.object({
87
97
  // Auth and provider internals (§3.5 + §7)
88
98
  // ─────────────────────────────────────────────
89
99
 
90
- const authModeEnum = z.enum(["oauth2", "oauth1", "api_key", "basic", "custom"]);
100
+ export const authModeEnum = z.enum(["oauth2", "oauth1", "api_key", "basic", "custom"]);
101
+
102
+ /** OAuth2 configuration sub-object. Required fields per §7.2; extensible for implementation-specific fields. */
103
+ export const oauth2Config = z.looseObject({
104
+ authorizationUrl: z.string(),
105
+ tokenUrl: z.string(),
106
+ });
91
107
 
92
- const providerDefinition = z.looseObject({
108
+ /** OAuth1 configuration sub-object. Required fields per §7.3; extensible for implementation-specific fields. */
109
+ export const oauth1Config = z.looseObject({
110
+ requestTokenUrl: z.string(),
111
+ accessTokenUrl: z.string(),
112
+ });
113
+
114
+ /** Credential configuration sub-object. Required fields per §7.4; extensible for implementation-specific fields. */
115
+ export const credentialsConfig = z.looseObject({
116
+ schema: z.record(z.string(), z.unknown()),
117
+ });
118
+
119
+ export const providerDefinition = z.looseObject({
93
120
  authMode: authModeEnum,
94
- authorizationUrl: z.string().optional(),
95
- tokenUrl: z.string().optional(),
96
- refreshUrl: z.string().optional(),
97
- defaultScopes: z.array(z.string()).optional(),
98
- scopeSeparator: z.string().optional(),
99
- pkceEnabled: z.boolean().optional(),
100
- tokenAuthMethod: z.string().optional(),
101
- authorizationParams: z.record(z.string(), z.unknown()).optional(),
102
- tokenParams: z.record(z.string(), z.unknown()).optional(),
103
- requestTokenUrl: z.string().optional(),
104
- accessTokenUrl: z.string().optional(),
105
- credentialSchema: z.record(z.string(), z.unknown()).optional(),
106
- credentialFieldName: z.string().optional(),
107
- credentialHeaderName: z.string().optional(),
108
- credentialHeaderPrefix: z.string().optional(),
121
+ oauth2: oauth2Config.optional(),
122
+ oauth1: oauth1Config.optional(),
123
+ credentials: credentialsConfig.optional(),
109
124
  authorizedUris: z.array(z.string()).optional(),
110
125
  allowAllUris: z.boolean().optional(),
111
126
  availableScopes: z.array(z.unknown()).optional(),
112
127
  });
113
128
 
114
- const setupGuide = z
129
+ export const setupGuide = z
115
130
  .object({
116
131
  callbackUrlHint: z.string().optional(),
117
132
  steps: z
@@ -132,11 +147,13 @@ const setupGuide = z
132
147
  export function createSchemas(majorVersion: number) {
133
148
  const schemaVersionField = z
134
149
  .string()
135
- .regex(new RegExp(`^${majorVersion}\\.(0|[1-9]\\d*)$`));
150
+ .regex(new RegExp(`^${majorVersion}\\.(0|[1-9]\\d*)$`), {
151
+ error: `Must follow MAJOR.MINOR format (e.g. "${majorVersion}.0")`,
152
+ });
136
153
 
137
154
  const commonFields = {
138
155
  name: scopedName,
139
- version: z.string().min(1),
156
+ version: semverVersion,
140
157
  type: z.enum(["flow", "skill", "tool", "provider"]),
141
158
  displayName: z.string().optional(),
142
159
  description: z.string().optional(),
@@ -152,7 +169,7 @@ export function createSchemas(majorVersion: number) {
152
169
  type: z.literal("flow"),
153
170
  schemaVersion: schemaVersionField,
154
171
  displayName: z.string().min(1),
155
- author: z.string(),
172
+ author: z.string().min(1),
156
173
  providersConfiguration: z.record(scopedName, providerConfiguration).optional(),
157
174
  input: schemaWrapper.optional(),
158
175
  output: schemaWrapper.optional(),
@@ -172,15 +189,80 @@ export function createSchemas(majorVersion: number) {
172
189
  tool: toolInterface,
173
190
  });
174
191
 
175
- const providerManifestSchema = z.looseObject({
176
- ...commonFields,
177
- type: z.literal("provider"),
178
- iconUrl: z.string().optional(),
179
- categories: z.array(z.string()).optional(),
180
- docsUrl: z.string().optional(),
181
- definition: providerDefinition,
182
- setupGuide: setupGuide,
183
- });
192
+ const providerManifestSchema = z
193
+ .looseObject({
194
+ ...commonFields,
195
+ type: z.literal("provider"),
196
+ iconUrl: z.string().optional(),
197
+ categories: z.array(z.string()).optional(),
198
+ docsUrl: z.string().optional(),
199
+ definition: providerDefinition,
200
+ setupGuide: setupGuide,
201
+ })
202
+ .superRefine((val, ctx) => {
203
+ const mode = val.definition?.authMode;
204
+ if (mode === "oauth2") {
205
+ if (!val.definition.oauth2) {
206
+ ctx.addIssue({
207
+ code: "custom",
208
+ path: ["definition", "oauth2"],
209
+ message: "oauth2 configuration object is required for oauth2 authMode",
210
+ });
211
+ } else {
212
+ if (!val.definition.oauth2.authorizationUrl) {
213
+ ctx.addIssue({
214
+ code: "custom",
215
+ path: ["definition", "oauth2", "authorizationUrl"],
216
+ message: "Required for oauth2 authMode",
217
+ });
218
+ }
219
+ if (!val.definition.oauth2.tokenUrl) {
220
+ ctx.addIssue({
221
+ code: "custom",
222
+ path: ["definition", "oauth2", "tokenUrl"],
223
+ message: "Required for oauth2 authMode",
224
+ });
225
+ }
226
+ }
227
+ } else if (mode === "oauth1") {
228
+ if (!val.definition.oauth1) {
229
+ ctx.addIssue({
230
+ code: "custom",
231
+ path: ["definition", "oauth1"],
232
+ message: "oauth1 configuration object is required for oauth1 authMode",
233
+ });
234
+ } else {
235
+ if (!val.definition.oauth1.requestTokenUrl) {
236
+ ctx.addIssue({
237
+ code: "custom",
238
+ path: ["definition", "oauth1", "requestTokenUrl"],
239
+ message: "Required for oauth1 authMode",
240
+ });
241
+ }
242
+ if (!val.definition.oauth1.accessTokenUrl) {
243
+ ctx.addIssue({
244
+ code: "custom",
245
+ path: ["definition", "oauth1", "accessTokenUrl"],
246
+ message: "Required for oauth1 authMode",
247
+ });
248
+ }
249
+ }
250
+ } else if (mode === "api_key" || mode === "basic" || mode === "custom") {
251
+ if (!val.definition.credentials) {
252
+ ctx.addIssue({
253
+ code: "custom",
254
+ path: ["definition", "credentials"],
255
+ message: `credentials configuration object is required for ${mode} authMode`,
256
+ });
257
+ } else if (!val.definition.credentials.schema) {
258
+ ctx.addIssue({
259
+ code: "custom",
260
+ path: ["definition", "credentials", "schema"],
261
+ message: `credentials.schema is required for ${mode} authMode`,
262
+ });
263
+ }
264
+ }
265
+ });
184
266
 
185
267
  return { flowManifestSchema, skillManifestSchema, toolManifestSchema, providerManifestSchema };
186
268
  }
@@ -10,8 +10,7 @@
10
10
  "pattern": "^@[a-z0-9]([a-z0-9-]*[a-z0-9])?\\/[a-z0-9]([a-z0-9-]*[a-z0-9])?$"
11
11
  },
12
12
  "version": {
13
- "type": "string",
14
- "minLength": 1
13
+ "type": "string"
15
14
  },
16
15
  "type": {
17
16
  "type": "string",
@@ -50,8 +49,7 @@
50
49
  "pattern": "^@[a-z0-9]([a-z0-9-]*[a-z0-9])?\\/[a-z0-9]([a-z0-9-]*[a-z0-9])?$"
51
50
  },
52
51
  "additionalProperties": {
53
- "type": "string",
54
- "minLength": 1
52
+ "type": "string"
55
53
  }
56
54
  },
57
55
  "tools": {
@@ -61,8 +59,7 @@
61
59
  "pattern": "^@[a-z0-9]([a-z0-9-]*[a-z0-9])?\\/[a-z0-9]([a-z0-9-]*[a-z0-9])?$"
62
60
  },
63
61
  "additionalProperties": {
64
- "type": "string",
65
- "minLength": 1
62
+ "type": "string"
66
63
  }
67
64
  },
68
65
  "providers": {
@@ -72,15 +69,15 @@
72
69
  "pattern": "^@[a-z0-9]([a-z0-9-]*[a-z0-9])?\\/[a-z0-9]([a-z0-9-]*[a-z0-9])?$"
73
70
  },
74
71
  "additionalProperties": {
75
- "type": "string",
76
- "minLength": 1
72
+ "type": "string"
77
73
  }
78
74
  }
79
75
  },
80
76
  "additionalProperties": {}
81
77
  },
82
78
  "author": {
83
- "type": "string"
79
+ "type": "string",
80
+ "minLength": 1
84
81
  },
85
82
  "providersConfiguration": {
86
83
  "type": "object",
@@ -162,8 +159,9 @@
162
159
  "type": "boolean"
163
160
  },
164
161
  "maxFiles": {
165
- "type": "number",
166
- "exclusiveMinimum": 0
162
+ "type": "integer",
163
+ "exclusiveMinimum": 0,
164
+ "maximum": 9007199254740991
167
165
  }
168
166
  },
169
167
  "required": [
@@ -251,8 +249,9 @@
251
249
  "type": "boolean"
252
250
  },
253
251
  "maxFiles": {
254
- "type": "number",
255
- "exclusiveMinimum": 0
252
+ "type": "integer",
253
+ "exclusiveMinimum": 0,
254
+ "maximum": 9007199254740991
256
255
  }
257
256
  },
258
257
  "required": [
@@ -340,8 +339,9 @@
340
339
  "type": "boolean"
341
340
  },
342
341
  "maxFiles": {
343
- "type": "number",
344
- "exclusiveMinimum": 0
342
+ "type": "integer",
343
+ "exclusiveMinimum": 0,
344
+ "maximum": 9007199254740991
345
345
  }
346
346
  },
347
347
  "required": [
@@ -10,8 +10,7 @@
10
10
  "pattern": "^@[a-z0-9]([a-z0-9-]*[a-z0-9])?\\/[a-z0-9]([a-z0-9-]*[a-z0-9])?$"
11
11
  },
12
12
  "version": {
13
- "type": "string",
14
- "minLength": 1
13
+ "type": "string"
15
14
  },
16
15
  "type": {
17
16
  "type": "string",
@@ -49,8 +48,7 @@
49
48
  "pattern": "^@[a-z0-9]([a-z0-9-]*[a-z0-9])?\\/[a-z0-9]([a-z0-9-]*[a-z0-9])?$"
50
49
  },
51
50
  "additionalProperties": {
52
- "type": "string",
53
- "minLength": 1
51
+ "type": "string"
54
52
  }
55
53
  },
56
54
  "tools": {
@@ -60,8 +58,7 @@
60
58
  "pattern": "^@[a-z0-9]([a-z0-9-]*[a-z0-9])?\\/[a-z0-9]([a-z0-9-]*[a-z0-9])?$"
61
59
  },
62
60
  "additionalProperties": {
63
- "type": "string",
64
- "minLength": 1
61
+ "type": "string"
65
62
  }
66
63
  },
67
64
  "providers": {
@@ -71,8 +68,7 @@
71
68
  "pattern": "^@[a-z0-9]([a-z0-9-]*[a-z0-9])?\\/[a-z0-9]([a-z0-9-]*[a-z0-9])?$"
72
69
  },
73
70
  "additionalProperties": {
74
- "type": "string",
75
- "minLength": 1
71
+ "type": "string"
76
72
  }
77
73
  }
78
74
  },
@@ -103,66 +99,54 @@
103
99
  "custom"
104
100
  ]
105
101
  },
106
- "authorizationUrl": {
107
- "type": "string"
108
- },
109
- "tokenUrl": {
110
- "type": "string"
111
- },
112
- "refreshUrl": {
113
- "type": "string"
114
- },
115
- "defaultScopes": {
116
- "type": "array",
117
- "items": {
118
- "type": "string"
119
- }
120
- },
121
- "scopeSeparator": {
122
- "type": "string"
123
- },
124
- "pkceEnabled": {
125
- "type": "boolean"
126
- },
127
- "tokenAuthMethod": {
128
- "type": "string"
129
- },
130
- "authorizationParams": {
102
+ "oauth2": {
131
103
  "type": "object",
132
- "propertyNames": {
133
- "type": "string"
104
+ "properties": {
105
+ "authorizationUrl": {
106
+ "type": "string"
107
+ },
108
+ "tokenUrl": {
109
+ "type": "string"
110
+ }
134
111
  },
112
+ "required": [
113
+ "authorizationUrl",
114
+ "tokenUrl"
115
+ ],
135
116
  "additionalProperties": {}
136
117
  },
137
- "tokenParams": {
118
+ "oauth1": {
138
119
  "type": "object",
139
- "propertyNames": {
140
- "type": "string"
120
+ "properties": {
121
+ "requestTokenUrl": {
122
+ "type": "string"
123
+ },
124
+ "accessTokenUrl": {
125
+ "type": "string"
126
+ }
141
127
  },
128
+ "required": [
129
+ "requestTokenUrl",
130
+ "accessTokenUrl"
131
+ ],
142
132
  "additionalProperties": {}
143
133
  },
144
- "requestTokenUrl": {
145
- "type": "string"
146
- },
147
- "accessTokenUrl": {
148
- "type": "string"
149
- },
150
- "credentialSchema": {
134
+ "credentials": {
151
135
  "type": "object",
152
- "propertyNames": {
153
- "type": "string"
136
+ "properties": {
137
+ "schema": {
138
+ "type": "object",
139
+ "propertyNames": {
140
+ "type": "string"
141
+ },
142
+ "additionalProperties": {}
143
+ }
154
144
  },
145
+ "required": [
146
+ "schema"
147
+ ],
155
148
  "additionalProperties": {}
156
149
  },
157
- "credentialFieldName": {
158
- "type": "string"
159
- },
160
- "credentialHeaderName": {
161
- "type": "string"
162
- },
163
- "credentialHeaderPrefix": {
164
- "type": "string"
165
- },
166
150
  "authorizedUris": {
167
151
  "type": "array",
168
152
  "items": {
@@ -10,8 +10,7 @@
10
10
  "pattern": "^@[a-z0-9]([a-z0-9-]*[a-z0-9])?\\/[a-z0-9]([a-z0-9-]*[a-z0-9])?$"
11
11
  },
12
12
  "version": {
13
- "type": "string",
14
- "minLength": 1
13
+ "type": "string"
15
14
  },
16
15
  "type": {
17
16
  "type": "string",
@@ -49,8 +48,7 @@
49
48
  "pattern": "^@[a-z0-9]([a-z0-9-]*[a-z0-9])?\\/[a-z0-9]([a-z0-9-]*[a-z0-9])?$"
50
49
  },
51
50
  "additionalProperties": {
52
- "type": "string",
53
- "minLength": 1
51
+ "type": "string"
54
52
  }
55
53
  },
56
54
  "tools": {
@@ -60,8 +58,7 @@
60
58
  "pattern": "^@[a-z0-9]([a-z0-9-]*[a-z0-9])?\\/[a-z0-9]([a-z0-9-]*[a-z0-9])?$"
61
59
  },
62
60
  "additionalProperties": {
63
- "type": "string",
64
- "minLength": 1
61
+ "type": "string"
65
62
  }
66
63
  },
67
64
  "providers": {
@@ -71,8 +68,7 @@
71
68
  "pattern": "^@[a-z0-9]([a-z0-9-]*[a-z0-9])?\\/[a-z0-9]([a-z0-9-]*[a-z0-9])?$"
72
69
  },
73
70
  "additionalProperties": {
74
- "type": "string",
75
- "minLength": 1
71
+ "type": "string"
76
72
  }
77
73
  }
78
74
  },
@@ -10,8 +10,7 @@
10
10
  "pattern": "^@[a-z0-9]([a-z0-9-]*[a-z0-9])?\\/[a-z0-9]([a-z0-9-]*[a-z0-9])?$"
11
11
  },
12
12
  "version": {
13
- "type": "string",
14
- "minLength": 1
13
+ "type": "string"
15
14
  },
16
15
  "type": {
17
16
  "type": "string",
@@ -49,8 +48,7 @@
49
48
  "pattern": "^@[a-z0-9]([a-z0-9-]*[a-z0-9])?\\/[a-z0-9]([a-z0-9-]*[a-z0-9])?$"
50
49
  },
51
50
  "additionalProperties": {
52
- "type": "string",
53
- "minLength": 1
51
+ "type": "string"
54
52
  }
55
53
  },
56
54
  "tools": {
@@ -60,8 +58,7 @@
60
58
  "pattern": "^@[a-z0-9]([a-z0-9-]*[a-z0-9])?\\/[a-z0-9]([a-z0-9-]*[a-z0-9])?$"
61
59
  },
62
60
  "additionalProperties": {
63
- "type": "string",
64
- "minLength": 1
61
+ "type": "string"
65
62
  }
66
63
  },
67
64
  "providers": {
@@ -71,8 +68,7 @@
71
68
  "pattern": "^@[a-z0-9]([a-z0-9-]*[a-z0-9])?\\/[a-z0-9]([a-z0-9-]*[a-z0-9])?$"
72
69
  },
73
70
  "additionalProperties": {
74
- "type": "string",
75
- "minLength": 1
71
+ "type": "string"
76
72
  }
77
73
  }
78
74
  },