@renseiai/agentfactory 0.8.18 → 0.8.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/governor/decision-engine-adapter.d.ts +43 -0
- package/dist/src/governor/decision-engine-adapter.d.ts.map +1 -0
- package/dist/src/governor/decision-engine-adapter.js +422 -0
- package/dist/src/governor/decision-engine-adapter.test.d.ts +2 -0
- package/dist/src/governor/decision-engine-adapter.test.d.ts.map +1 -0
- package/dist/src/governor/decision-engine-adapter.test.js +363 -0
- package/dist/src/governor/index.d.ts +1 -0
- package/dist/src/governor/index.d.ts.map +1 -1
- package/dist/src/governor/index.js +1 -0
- package/dist/src/manifest/route-manifest.d.ts.map +1 -1
- package/dist/src/manifest/route-manifest.js +4 -0
- package/dist/src/orchestrator/orchestrator.d.ts +27 -0
- package/dist/src/orchestrator/orchestrator.d.ts.map +1 -1
- package/dist/src/orchestrator/orchestrator.js +289 -86
- package/dist/src/providers/claude-provider.d.ts.map +1 -1
- package/dist/src/providers/claude-provider.js +11 -0
- package/dist/src/providers/codex-app-server-provider.d.ts +201 -0
- package/dist/src/providers/codex-app-server-provider.d.ts.map +1 -0
- package/dist/src/providers/codex-app-server-provider.js +786 -0
- package/dist/src/providers/codex-app-server-provider.test.d.ts +2 -0
- package/dist/src/providers/codex-app-server-provider.test.d.ts.map +1 -0
- package/dist/src/providers/codex-app-server-provider.test.js +529 -0
- package/dist/src/providers/codex-provider.d.ts +24 -4
- package/dist/src/providers/codex-provider.d.ts.map +1 -1
- package/dist/src/providers/codex-provider.js +58 -6
- package/dist/src/providers/index.d.ts +1 -0
- package/dist/src/providers/index.d.ts.map +1 -1
- package/dist/src/providers/index.js +1 -0
- package/dist/src/routing/observation-recorder.test.js +1 -1
- package/dist/src/routing/observation-store.d.ts +15 -1
- package/dist/src/routing/observation-store.d.ts.map +1 -1
- package/dist/src/routing/observation-store.test.js +17 -11
- package/dist/src/templates/index.d.ts +2 -1
- package/dist/src/templates/index.d.ts.map +1 -1
- package/dist/src/templates/index.js +1 -0
- package/dist/src/templates/registry.d.ts +23 -0
- package/dist/src/templates/registry.d.ts.map +1 -1
- package/dist/src/templates/registry.js +80 -0
- package/dist/src/templates/schema.d.ts +31 -0
- package/dist/src/templates/schema.d.ts.map +1 -0
- package/dist/src/templates/schema.js +139 -0
- package/dist/src/templates/schema.test.d.ts +2 -0
- package/dist/src/templates/schema.test.d.ts.map +1 -0
- package/dist/src/templates/schema.test.js +215 -0
- package/package.json +2 -2
|
@@ -1,10 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* OpenAI Codex Agent Provider
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* its JSONL event stream into normalized AgentEvents.
|
|
4
|
+
* Unified Codex provider with two execution modes:
|
|
6
5
|
*
|
|
7
|
-
*
|
|
6
|
+
* 1. **App Server mode** (default when CODEX_USE_APP_SERVER=1):
|
|
7
|
+
* Uses a long-lived `codex app-server` process communicating via JSON-RPC 2.0
|
|
8
|
+
* over stdio. Supports multiple concurrent threads on a single process.
|
|
9
|
+
*
|
|
10
|
+
* 2. **Exec fallback mode** (backward compatibility):
|
|
11
|
+
* Spawns the `codex` CLI as a child process and parses its JSONL event stream.
|
|
12
|
+
* Used for CI/CD or when app-server is unavailable.
|
|
13
|
+
*
|
|
14
|
+
* Mode selection:
|
|
15
|
+
* - CODEX_USE_APP_SERVER=1 → App Server mode (JSON-RPC 2.0)
|
|
16
|
+
* - CODEX_USE_APP_SERVER=0 → Exec fallback mode (CLI JSONL)
|
|
17
|
+
* - Not set → Exec fallback mode (default, backward compatible)
|
|
18
|
+
*
|
|
19
|
+
* Exec CLI invocation patterns:
|
|
8
20
|
* New session: codex exec --json --full-auto -C <cwd> "<prompt>"
|
|
9
21
|
* Resume: codex exec resume --json --full-auto <session_id> "<prompt>"
|
|
10
22
|
*
|
|
@@ -18,6 +30,7 @@
|
|
|
18
30
|
*/
|
|
19
31
|
import { spawn } from 'child_process';
|
|
20
32
|
import { createInterface } from 'readline';
|
|
33
|
+
import { CodexAppServerProvider } from './codex-app-server-provider.js';
|
|
21
34
|
/**
|
|
22
35
|
* Map a single Codex JSONL event to one or more normalized AgentEvents.
|
|
23
36
|
* Exported for unit testing — the AgentHandle uses this internally.
|
|
@@ -186,19 +199,58 @@ export function mapCodexItemEvent(event) {
|
|
|
186
199
|
// ---------------------------------------------------------------------------
|
|
187
200
|
// Provider
|
|
188
201
|
// ---------------------------------------------------------------------------
|
|
202
|
+
/**
|
|
203
|
+
* Check whether App Server mode is enabled.
|
|
204
|
+
*
|
|
205
|
+
* Returns true when CODEX_USE_APP_SERVER=1 (or 'true').
|
|
206
|
+
* The env var can be set globally or per-spawn via config.env.
|
|
207
|
+
*/
|
|
208
|
+
function isAppServerEnabled(config) {
|
|
209
|
+
const envVal = config?.env?.CODEX_USE_APP_SERVER
|
|
210
|
+
?? process.env.CODEX_USE_APP_SERVER
|
|
211
|
+
?? '';
|
|
212
|
+
return envVal === '1' || envVal.toLowerCase() === 'true';
|
|
213
|
+
}
|
|
189
214
|
export class CodexProvider {
|
|
190
215
|
name = 'codex';
|
|
191
216
|
capabilities = {
|
|
192
217
|
supportsMessageInjection: false,
|
|
193
218
|
supportsSessionResume: true,
|
|
194
219
|
};
|
|
220
|
+
/** App Server delegate — created lazily when App Server mode is enabled */
|
|
221
|
+
appServerProvider = null;
|
|
195
222
|
spawn(config) {
|
|
196
|
-
|
|
223
|
+
if (isAppServerEnabled(config)) {
|
|
224
|
+
return this.getAppServerProvider().spawn(config);
|
|
225
|
+
}
|
|
226
|
+
return this.createExecHandle(config);
|
|
197
227
|
}
|
|
198
228
|
resume(sessionId, config) {
|
|
199
|
-
|
|
229
|
+
if (isAppServerEnabled(config)) {
|
|
230
|
+
return this.getAppServerProvider().resume(sessionId, config);
|
|
231
|
+
}
|
|
232
|
+
return this.createExecHandle(config, sessionId);
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Shut down the App Server process if running.
|
|
236
|
+
* No-op if in exec fallback mode.
|
|
237
|
+
*/
|
|
238
|
+
async shutdown() {
|
|
239
|
+
if (this.appServerProvider) {
|
|
240
|
+
await this.appServerProvider.shutdown();
|
|
241
|
+
this.appServerProvider = null;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
getAppServerProvider() {
|
|
245
|
+
if (!this.appServerProvider) {
|
|
246
|
+
this.appServerProvider = new CodexAppServerProvider();
|
|
247
|
+
}
|
|
248
|
+
return this.appServerProvider;
|
|
200
249
|
}
|
|
201
|
-
|
|
250
|
+
// -------------------------------------------------------------------------
|
|
251
|
+
// Exec fallback mode (backward compatibility — SUP-1739)
|
|
252
|
+
// -------------------------------------------------------------------------
|
|
253
|
+
createExecHandle(config, resumeSessionId) {
|
|
202
254
|
const abortController = config.abortController;
|
|
203
255
|
// Resolve the codex binary — prefer CODEX_BIN env var, then fall back to 'codex'
|
|
204
256
|
const codexBin = config.env.CODEX_BIN || process.env.CODEX_BIN || 'codex';
|
|
@@ -32,6 +32,7 @@ export type { AgentInitEvent, AgentSystemEvent, AgentAssistantTextEvent, AgentTo
|
|
|
32
32
|
export type { ActionDefinition, ActionResult, AuthResult, ConditionContext, ConditionDefinition, CredentialDefinition, CredentialField, DynamicOptionField, NormalizedEvent, ProviderExecutionContext, ProviderPlugin, TriggerDefinition, WebhookConfig, WebhookRegistration, } from './plugin-types.js';
|
|
33
33
|
export { ClaudeProvider, createClaudeProvider } from './claude-provider.js';
|
|
34
34
|
export { CodexProvider, createCodexProvider } from './codex-provider.js';
|
|
35
|
+
export { CodexAppServerProvider, createCodexAppServerProvider } from './codex-app-server-provider.js';
|
|
35
36
|
export { AmpProvider, createAmpProvider } from './amp-provider.js';
|
|
36
37
|
export { SpringAiProvider, createSpringAiProvider } from './spring-ai-provider.js';
|
|
37
38
|
export { A2aProvider, createA2aProvider } from './a2a-provider.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/providers/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/providers/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
*/
|
|
30
30
|
export { ClaudeProvider, createClaudeProvider } from './claude-provider.js';
|
|
31
31
|
export { CodexProvider, createCodexProvider } from './codex-provider.js';
|
|
32
|
+
export { CodexAppServerProvider, createCodexAppServerProvider } from './codex-app-server-provider.js';
|
|
32
33
|
export { AmpProvider, createAmpProvider } from './amp-provider.js';
|
|
33
34
|
export { SpringAiProvider, createSpringAiProvider } from './spring-ai-provider.js';
|
|
34
35
|
export { A2aProvider, createA2aProvider } from './a2a-provider.js';
|
|
@@ -14,7 +14,7 @@ function makeProcess(overrides = {}) {
|
|
|
14
14
|
function makeMockStores() {
|
|
15
15
|
const observationStore = {
|
|
16
16
|
recordObservation: vi.fn().mockResolvedValue(undefined),
|
|
17
|
-
getObservations: vi.fn().mockResolvedValue([]),
|
|
17
|
+
getObservations: vi.fn().mockResolvedValue({ observations: [] }),
|
|
18
18
|
getRecentObservations: vi.fn().mockResolvedValue([]),
|
|
19
19
|
};
|
|
20
20
|
const posteriorStore = {
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import type { AgentProviderName } from '../providers/types.js';
|
|
2
2
|
import type { AgentWorkType } from '../orchestrator/work-types.js';
|
|
3
3
|
import type { RoutingObservation } from './types.js';
|
|
4
|
+
/**
|
|
5
|
+
* Result of a paginated observation query.
|
|
6
|
+
*/
|
|
7
|
+
export interface ObservationQueryResult {
|
|
8
|
+
observations: RoutingObservation[];
|
|
9
|
+
/** Opaque cursor for fetching the next page. Undefined when no more results. */
|
|
10
|
+
nextCursor?: string;
|
|
11
|
+
}
|
|
4
12
|
/**
|
|
5
13
|
* Observation Store Interface
|
|
6
14
|
*
|
|
@@ -20,13 +28,19 @@ export interface ObservationStore {
|
|
|
20
28
|
* @param opts.workType - Filter by work type
|
|
21
29
|
* @param opts.limit - Maximum number of observations to return
|
|
22
30
|
* @param opts.since - Only return observations with timestamp >= since (epoch ms)
|
|
31
|
+
* @param opts.from - Start of time range (epoch ms), inclusive
|
|
32
|
+
* @param opts.to - End of time range (epoch ms), inclusive
|
|
33
|
+
* @param opts.cursor - Opaque cursor for pagination (Redis Stream ID)
|
|
23
34
|
*/
|
|
24
35
|
getObservations(opts: {
|
|
25
36
|
provider?: AgentProviderName;
|
|
26
37
|
workType?: AgentWorkType;
|
|
27
38
|
limit?: number;
|
|
28
39
|
since?: number;
|
|
29
|
-
|
|
40
|
+
from?: number;
|
|
41
|
+
to?: number;
|
|
42
|
+
cursor?: string;
|
|
43
|
+
}): Promise<ObservationQueryResult>;
|
|
30
44
|
/**
|
|
31
45
|
* Get the most recent observations for a specific provider + work type pair.
|
|
32
46
|
* Results are returned newest-first, up to `windowSize` entries.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"observation-store.d.ts","sourceRoot":"","sources":["../../../src/routing/observation-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AAC9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAA;AAClE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAEpD;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;OAEG;IACH,iBAAiB,CAAC,GAAG,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAEzD
|
|
1
|
+
{"version":3,"file":"observation-store.d.ts","sourceRoot":"","sources":["../../../src/routing/observation-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AAC9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAA;AAClE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAEpD;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,YAAY,EAAE,kBAAkB,EAAE,CAAA;IAClC,gFAAgF;IAChF,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;OAEG;IACH,iBAAiB,CAAC,GAAG,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAEzD;;;;;;;;;;OAUG;IACH,eAAe,CAAC,IAAI,EAAE;QACpB,QAAQ,CAAC,EAAE,iBAAiB,CAAA;QAC5B,QAAQ,CAAC,EAAE,aAAa,CAAA;QACxB,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,EAAE,CAAC,EAAE,MAAM,CAAA;QACX,MAAM,CAAC,EAAE,MAAM,CAAA;KAChB,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAAA;IAEnC;;;;;;;OAOG;IACH,qBAAqB,CACnB,QAAQ,EAAE,iBAAiB,EAC3B,QAAQ,EAAE,aAAa,EACvB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAAA;CACjC"}
|
|
@@ -41,10 +41,16 @@ function createInMemoryStore() {
|
|
|
41
41
|
if (opts.since) {
|
|
42
42
|
result = result.filter((o) => o.timestamp >= opts.since);
|
|
43
43
|
}
|
|
44
|
+
if (opts.from) {
|
|
45
|
+
result = result.filter((o) => o.timestamp >= opts.from);
|
|
46
|
+
}
|
|
47
|
+
if (opts.to) {
|
|
48
|
+
result = result.filter((o) => o.timestamp <= opts.to);
|
|
49
|
+
}
|
|
44
50
|
if (opts.limit) {
|
|
45
51
|
result = result.slice(0, opts.limit);
|
|
46
52
|
}
|
|
47
|
-
return result;
|
|
53
|
+
return { observations: result };
|
|
48
54
|
},
|
|
49
55
|
async getRecentObservations(provider, workType, windowSize) {
|
|
50
56
|
return observations
|
|
@@ -67,8 +73,8 @@ describe('ObservationStore interface', () => {
|
|
|
67
73
|
const obs = makeObservation();
|
|
68
74
|
await store.recordObservation(obs);
|
|
69
75
|
const all = await store.getObservations({});
|
|
70
|
-
expect(all).toHaveLength(1);
|
|
71
|
-
expect(all[0]).toEqual(obs);
|
|
76
|
+
expect(all.observations).toHaveLength(1);
|
|
77
|
+
expect(all.observations[0]).toEqual(obs);
|
|
72
78
|
});
|
|
73
79
|
it('getObservations filters by provider', async () => {
|
|
74
80
|
const store = createInMemoryStore();
|
|
@@ -76,16 +82,16 @@ describe('ObservationStore interface', () => {
|
|
|
76
82
|
await store.recordObservation(makeObservation({ provider: 'codex' }));
|
|
77
83
|
await store.recordObservation(makeObservation({ provider: 'claude' }));
|
|
78
84
|
const result = await store.getObservations({ provider: 'claude' });
|
|
79
|
-
expect(result).toHaveLength(2);
|
|
80
|
-
expect(result.every((o) => o.provider === 'claude')).toBe(true);
|
|
85
|
+
expect(result.observations).toHaveLength(2);
|
|
86
|
+
expect(result.observations.every((o) => o.provider === 'claude')).toBe(true);
|
|
81
87
|
});
|
|
82
88
|
it('getObservations filters by workType', async () => {
|
|
83
89
|
const store = createInMemoryStore();
|
|
84
90
|
await store.recordObservation(makeObservation({ workType: 'development' }));
|
|
85
91
|
await store.recordObservation(makeObservation({ workType: 'qa' }));
|
|
86
92
|
const result = await store.getObservations({ workType: 'qa' });
|
|
87
|
-
expect(result).toHaveLength(1);
|
|
88
|
-
expect(result[0].workType).toBe('qa');
|
|
93
|
+
expect(result.observations).toHaveLength(1);
|
|
94
|
+
expect(result.observations[0].workType).toBe('qa');
|
|
89
95
|
});
|
|
90
96
|
it('getObservations filters by since', async () => {
|
|
91
97
|
const store = createInMemoryStore();
|
|
@@ -93,8 +99,8 @@ describe('ObservationStore interface', () => {
|
|
|
93
99
|
await store.recordObservation(makeObservation({ timestamp: 2000 }));
|
|
94
100
|
await store.recordObservation(makeObservation({ timestamp: 3000 }));
|
|
95
101
|
const result = await store.getObservations({ since: 2000 });
|
|
96
|
-
expect(result).toHaveLength(2);
|
|
97
|
-
expect(result.every((o) => o.timestamp >= 2000)).toBe(true);
|
|
102
|
+
expect(result.observations).toHaveLength(2);
|
|
103
|
+
expect(result.observations.every((o) => o.timestamp >= 2000)).toBe(true);
|
|
98
104
|
});
|
|
99
105
|
it('getObservations respects limit', async () => {
|
|
100
106
|
const store = createInMemoryStore();
|
|
@@ -102,7 +108,7 @@ describe('ObservationStore interface', () => {
|
|
|
102
108
|
await store.recordObservation(makeObservation({ timestamp: i }));
|
|
103
109
|
}
|
|
104
110
|
const result = await store.getObservations({ limit: 3 });
|
|
105
|
-
expect(result).toHaveLength(3);
|
|
111
|
+
expect(result.observations).toHaveLength(3);
|
|
106
112
|
});
|
|
107
113
|
it('getRecentObservations returns newest-first for a provider+workType pair', async () => {
|
|
108
114
|
const store = createInMemoryStore();
|
|
@@ -128,7 +134,7 @@ describe('ObservationStore interface', () => {
|
|
|
128
134
|
const store = createInMemoryStore();
|
|
129
135
|
await store.recordObservation(makeObservation({ provider: 'claude' }));
|
|
130
136
|
const result = await store.getObservations({ provider: 'codex' });
|
|
131
|
-
expect(result).toEqual([]);
|
|
137
|
+
expect(result.observations).toEqual([]);
|
|
132
138
|
});
|
|
133
139
|
it('getRecentObservations returns empty array when no observations match', async () => {
|
|
134
140
|
const store = createInMemoryStore();
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
*/
|
|
6
6
|
export type { WorkflowTemplate, PartialTemplate, TemplateContext, ToolPermission, ToolPermissionAdapter, TemplateRegistryConfig, } from './types.js';
|
|
7
7
|
export { WorkflowTemplateSchema, PartialTemplateSchema, TemplateContextSchema, AgentWorkTypeSchema, ToolPermissionSchema, validateWorkflowTemplate, validatePartialTemplate, } from './types.js';
|
|
8
|
-
export { TemplateRegistry } from './registry.js';
|
|
8
|
+
export { TemplateRegistry, type TemplateValidationResult } from './registry.js';
|
|
9
|
+
export { generateTemplateSchema, extractTemplateVariables, type TemplateSchemaOptions } from './schema.js';
|
|
9
10
|
export { loadTemplatesFromDir, loadTemplateFile, loadPartialsFromDir, getBuiltinDefaultsDir, getBuiltinPartialsDir, } from './loader.js';
|
|
10
11
|
export { ClaudeToolPermissionAdapter, CodexToolPermissionAdapter, createToolPermissionAdapter } from './adapters.js';
|
|
11
12
|
export { renderPromptWithFallback } from './renderer.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/templates/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,YAAY,EACV,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,cAAc,EACd,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,YAAY,CAAA;AAEnB,OAAO,EACL,sBAAsB,EACtB,qBAAqB,EACrB,qBAAqB,EACrB,mBAAmB,EACnB,oBAAoB,EACpB,wBAAwB,EACxB,uBAAuB,GACxB,MAAM,YAAY,CAAA;AAEnB,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/templates/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,YAAY,EACV,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,cAAc,EACd,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,YAAY,CAAA;AAEnB,OAAO,EACL,sBAAsB,EACtB,qBAAqB,EACrB,qBAAqB,EACrB,mBAAmB,EACnB,oBAAoB,EACpB,wBAAwB,EACxB,uBAAuB,GACxB,MAAM,YAAY,CAAA;AAEnB,OAAO,EAAE,gBAAgB,EAAE,KAAK,wBAAwB,EAAE,MAAM,eAAe,CAAA;AAE/E,OAAO,EAAE,sBAAsB,EAAE,wBAAwB,EAAE,KAAK,qBAAqB,EAAE,MAAM,aAAa,CAAA;AAE1G,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,mBAAmB,EACnB,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,aAAa,CAAA;AAEpB,OAAO,EAAE,2BAA2B,EAAE,0BAA0B,EAAE,2BAA2B,EAAE,MAAM,eAAe,CAAA;AAEpH,OAAO,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAA;AAExD,YAAY,EAAE,eAAe,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAA;AACxF,OAAO,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,gCAAgC,EAAE,MAAM,uBAAuB,CAAA"}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
export { WorkflowTemplateSchema, PartialTemplateSchema, TemplateContextSchema, AgentWorkTypeSchema, ToolPermissionSchema, validateWorkflowTemplate, validatePartialTemplate, } from './types.js';
|
|
7
7
|
export { TemplateRegistry } from './registry.js';
|
|
8
|
+
export { generateTemplateSchema, extractTemplateVariables } from './schema.js';
|
|
8
9
|
export { loadTemplatesFromDir, loadTemplateFile, loadPartialsFromDir, getBuiltinDefaultsDir, getBuiltinPartialsDir, } from './loader.js';
|
|
9
10
|
export { ClaudeToolPermissionAdapter, CodexToolPermissionAdapter, createToolPermissionAdapter } from './adapters.js';
|
|
10
11
|
export { renderPromptWithFallback } from './renderer.js';
|
|
@@ -5,8 +5,10 @@
|
|
|
5
5
|
* Supports layered resolution with built-in defaults, project overrides,
|
|
6
6
|
* and inline config overrides.
|
|
7
7
|
*/
|
|
8
|
+
import type { JSONSchema7 } from 'json-schema';
|
|
8
9
|
import type { AgentWorkType } from '../orchestrator/work-types.js';
|
|
9
10
|
import type { WorkflowTemplate, TemplateContext, TemplateRegistryConfig, ToolPermission, ToolPermissionAdapter } from './types.js';
|
|
11
|
+
import { type TemplateSchemaOptions } from './schema.js';
|
|
10
12
|
/**
|
|
11
13
|
* Template Registry manages workflow templates and renders prompts.
|
|
12
14
|
*
|
|
@@ -76,5 +78,26 @@ export declare class TemplateRegistry {
|
|
|
76
78
|
* Register a partial template for use in Handlebars rendering.
|
|
77
79
|
*/
|
|
78
80
|
registerPartial(name: string, content: string): void;
|
|
81
|
+
/**
|
|
82
|
+
* Get JSON Schema 7 for a template's parameters.
|
|
83
|
+
*
|
|
84
|
+
* The schema is derived from the template's prompt by extracting
|
|
85
|
+
* Handlebars expression references and mapping them to the known
|
|
86
|
+
* TemplateContext fields. Returns null if no template is registered.
|
|
87
|
+
*/
|
|
88
|
+
getSchema(templateName: string, options?: TemplateSchemaOptions): JSONSchema7 | null;
|
|
89
|
+
/**
|
|
90
|
+
* Validate a config object against a template's JSON Schema.
|
|
91
|
+
*
|
|
92
|
+
* Template expressions ({{ }}) in string values are treated as valid
|
|
93
|
+
* placeholders and are not validated against the schema type.
|
|
94
|
+
*
|
|
95
|
+
* Returns a validation result with `valid` boolean and any `errors`.
|
|
96
|
+
*/
|
|
97
|
+
validateConfig(templateName: string, config: Record<string, unknown>): TemplateValidationResult;
|
|
98
|
+
}
|
|
99
|
+
export interface TemplateValidationResult {
|
|
100
|
+
valid: boolean;
|
|
101
|
+
errors: string[];
|
|
79
102
|
}
|
|
80
103
|
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/templates/registry.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAA;AAClE,OAAO,KAAK,EACV,gBAAgB,EAChB,eAAe,EACf,sBAAsB,EACtB,cAAc,EACd,qBAAqB,EACtB,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/templates/registry.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAC9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAA;AAClE,OAAO,KAAK,EACV,gBAAgB,EAChB,eAAe,EACf,sBAAsB,EACtB,cAAc,EACd,qBAAqB,EACtB,MAAM,YAAY,CAAA;AAOnB,OAAO,EAA0B,KAAK,qBAAqB,EAAE,MAAM,aAAa,CAAA;AAEhF;;;;;;GAMG;AACH,qBAAa,gBAAgB;IAC3B;;;OAGG;IACH,OAAO,CAAC,SAAS,CAAsC;IACvD,OAAO,CAAC,UAAU,CAAmB;IACrC,OAAO,CAAC,qBAAqB,CAAC,CAAuB;;IAcrD;;OAEG;IACH,MAAM,CAAC,MAAM,CAAC,MAAM,GAAE,sBAA2B,GAAG,gBAAgB;IAMpE;;;;;;OAMG;IACH,UAAU,CAAC,MAAM,GAAE,sBAA2B,GAAG,IAAI;IA4CrD;;OAEG;IACH,wBAAwB,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI;IAI9D;;;;;;;;OAQG;IACH,WAAW,CAAC,QAAQ,EAAE,aAAa,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS;IASrF;;OAEG;IACH,WAAW,CAAC,QAAQ,EAAE,aAAa,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO;IAQhE;;OAEG;IACH,sBAAsB,IAAI,MAAM,EAAE;IAIlC;;;;OAIG;IACH,YAAY,CAAC,QAAQ,EAAE,aAAa,EAAE,OAAO,EAAE,eAAe,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAkBjG;;;OAGG;IACH,kBAAkB,CAAC,QAAQ,EAAE,aAAa,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS;IAgBpF;;OAEG;IACH,kBAAkB,CAAC,QAAQ,EAAE,aAAa,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,cAAc,EAAE,GAAG,SAAS;IAK5F;;OAEG;IACH,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAIpD;;;;;;OAMG;IACH,SAAS,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,qBAAqB,GAAG,WAAW,GAAG,IAAI;IAMpF;;;;;;;OAOG;IACH,cAAc,CACZ,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,wBAAwB;CAS5B;AAMD,MAAM,WAAW,wBAAwB;IACvC,KAAK,EAAE,OAAO,CAAA;IACd,MAAM,EAAE,MAAM,EAAE,CAAA;CACjB"}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import Handlebars from 'handlebars';
|
|
9
9
|
import { loadTemplatesFromDir, loadPartialsFromDir, getBuiltinDefaultsDir, getBuiltinPartialsDir, } from './loader.js';
|
|
10
|
+
import { generateTemplateSchema } from './schema.js';
|
|
10
11
|
/**
|
|
11
12
|
* Template Registry manages workflow templates and renders prompts.
|
|
12
13
|
*
|
|
@@ -174,4 +175,83 @@ export class TemplateRegistry {
|
|
|
174
175
|
registerPartial(name, content) {
|
|
175
176
|
this.handlebars.registerPartial(name, content);
|
|
176
177
|
}
|
|
178
|
+
/**
|
|
179
|
+
* Get JSON Schema 7 for a template's parameters.
|
|
180
|
+
*
|
|
181
|
+
* The schema is derived from the template's prompt by extracting
|
|
182
|
+
* Handlebars expression references and mapping them to the known
|
|
183
|
+
* TemplateContext fields. Returns null if no template is registered.
|
|
184
|
+
*/
|
|
185
|
+
getSchema(templateName, options) {
|
|
186
|
+
const template = this.templates.get(templateName);
|
|
187
|
+
if (!template)
|
|
188
|
+
return null;
|
|
189
|
+
return generateTemplateSchema(template, options);
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Validate a config object against a template's JSON Schema.
|
|
193
|
+
*
|
|
194
|
+
* Template expressions ({{ }}) in string values are treated as valid
|
|
195
|
+
* placeholders and are not validated against the schema type.
|
|
196
|
+
*
|
|
197
|
+
* Returns a validation result with `valid` boolean and any `errors`.
|
|
198
|
+
*/
|
|
199
|
+
validateConfig(templateName, config) {
|
|
200
|
+
const template = this.templates.get(templateName);
|
|
201
|
+
if (!template) {
|
|
202
|
+
return { valid: false, errors: [`Template "${templateName}" not found`] };
|
|
203
|
+
}
|
|
204
|
+
const schema = generateTemplateSchema(template);
|
|
205
|
+
return validateConfigAgainstSchema(config, schema);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
/** Handlebars expression pattern: {{ ... }} */
|
|
209
|
+
const TEMPLATE_EXPRESSION_RE = /^\{\{.*\}\}$/s;
|
|
210
|
+
/**
|
|
211
|
+
* Validate a config object against a JSON Schema 7.
|
|
212
|
+
* Template expressions ({{ }}) are treated as valid for any field.
|
|
213
|
+
*/
|
|
214
|
+
function validateConfigAgainstSchema(config, schema) {
|
|
215
|
+
const errors = [];
|
|
216
|
+
const properties = schema.properties ?? {};
|
|
217
|
+
const required = schema.required ?? [];
|
|
218
|
+
// Check required fields
|
|
219
|
+
for (const field of required) {
|
|
220
|
+
if (!(field in config)) {
|
|
221
|
+
errors.push(`Missing required field: "${field}"`);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
// Type-check provided fields
|
|
225
|
+
for (const [key, value] of Object.entries(config)) {
|
|
226
|
+
const fieldSchema = properties[key];
|
|
227
|
+
if (!fieldSchema || typeof fieldSchema === 'boolean')
|
|
228
|
+
continue;
|
|
229
|
+
// Template expressions are always valid
|
|
230
|
+
if (typeof value === 'string' && TEMPLATE_EXPRESSION_RE.test(value.trim())) {
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
const typeError = checkType(key, value, fieldSchema);
|
|
234
|
+
if (typeError)
|
|
235
|
+
errors.push(typeError);
|
|
236
|
+
}
|
|
237
|
+
return { valid: errors.length === 0, errors };
|
|
238
|
+
}
|
|
239
|
+
function checkType(key, value, schema) {
|
|
240
|
+
const schemaType = schema.type;
|
|
241
|
+
if (!schemaType)
|
|
242
|
+
return null;
|
|
243
|
+
// Normalize to array of type strings
|
|
244
|
+
const types = Array.isArray(schemaType) ? schemaType : [schemaType];
|
|
245
|
+
const valueType = Array.isArray(value) ? 'array' : typeof value;
|
|
246
|
+
// null is a valid JSON Schema type
|
|
247
|
+
if (value === null && types.includes('null'))
|
|
248
|
+
return null;
|
|
249
|
+
if (value === null)
|
|
250
|
+
return `Field "${key}" is null but expected ${types.join(' | ')}`;
|
|
251
|
+
// 'integer' matches typeof 'number' in JS
|
|
252
|
+
const effectiveTypes = types.map(t => t === 'integer' ? 'number' : t);
|
|
253
|
+
if (!effectiveTypes.includes(valueType)) {
|
|
254
|
+
return `Field "${key}" has type "${valueType}" but expected ${types.join(' | ')}`;
|
|
255
|
+
}
|
|
256
|
+
return null;
|
|
177
257
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Template JSON Schema Generation
|
|
3
|
+
*
|
|
4
|
+
* Generates JSON Schema 7 definitions from WorkflowTemplate objects.
|
|
5
|
+
* Schema is derived from TemplateContext fields referenced in the template's
|
|
6
|
+
* prompt via Handlebars expressions.
|
|
7
|
+
*
|
|
8
|
+
* @see SUP-1758
|
|
9
|
+
*/
|
|
10
|
+
import type { JSONSchema7 } from 'json-schema';
|
|
11
|
+
import type { WorkflowTemplate } from './types.js';
|
|
12
|
+
export interface TemplateSchemaOptions {
|
|
13
|
+
/** Include all TemplateContext fields, not just those referenced in the prompt */
|
|
14
|
+
includeAllFields?: boolean;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Extract Handlebars variable references from a template prompt.
|
|
18
|
+
* Matches {{ varName }}, {{ varName.property }}, and handles
|
|
19
|
+
* conditionals like {{#if varName}} and {{#unless varName}}.
|
|
20
|
+
*
|
|
21
|
+
* Returns the set of top-level variable names referenced.
|
|
22
|
+
*/
|
|
23
|
+
export declare function extractTemplateVariables(prompt: string): Set<string>;
|
|
24
|
+
/**
|
|
25
|
+
* Generate a JSON Schema 7 definition for a WorkflowTemplate's parameters.
|
|
26
|
+
*
|
|
27
|
+
* By default, only includes fields that are referenced in the template's
|
|
28
|
+
* prompt. Set `includeAllFields: true` to include all known TemplateContext fields.
|
|
29
|
+
*/
|
|
30
|
+
export declare function generateTemplateSchema(template: WorkflowTemplate, options?: TemplateSchemaOptions): JSONSchema7;
|
|
31
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/templates/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAyB,MAAM,aAAa,CAAA;AACrE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAMlD,MAAM,WAAW,qBAAqB;IACpC,kFAAkF;IAClF,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAC3B;AA4DD;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CA2BpE;AAMD;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,gBAAgB,EAC1B,OAAO,CAAC,EAAE,qBAAqB,GAC9B,WAAW,CAqCb"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Template JSON Schema Generation
|
|
3
|
+
*
|
|
4
|
+
* Generates JSON Schema 7 definitions from WorkflowTemplate objects.
|
|
5
|
+
* Schema is derived from TemplateContext fields referenced in the template's
|
|
6
|
+
* prompt via Handlebars expressions.
|
|
7
|
+
*
|
|
8
|
+
* @see SUP-1758
|
|
9
|
+
*/
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Known TemplateContext field definitions
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
/**
|
|
14
|
+
* Maps TemplateContext field names to their JSON Schema 7 definitions.
|
|
15
|
+
* This is the source of truth for schema generation.
|
|
16
|
+
*/
|
|
17
|
+
const CONTEXT_FIELD_SCHEMAS = {
|
|
18
|
+
identifier: { type: 'string', description: 'Issue identifier, e.g., "SUP-123"' },
|
|
19
|
+
mentionContext: { type: 'string', description: 'Optional user mention text providing additional context' },
|
|
20
|
+
startStatus: { type: 'string', description: 'Status to show when agent starts, e.g., "Started"' },
|
|
21
|
+
completeStatus: { type: 'string', description: 'Status to show when agent completes, e.g., "Finished"' },
|
|
22
|
+
parentContext: { type: 'string', description: 'Pre-built enriched prompt for parent issues with sub-issues' },
|
|
23
|
+
subIssueList: { type: 'string', description: 'Formatted list of sub-issues with statuses' },
|
|
24
|
+
cycleCount: { type: 'integer', description: 'Current escalation cycle count' },
|
|
25
|
+
strategy: { type: 'string', description: 'Current escalation strategy' },
|
|
26
|
+
failureSummary: { type: 'string', description: 'Accumulated failure summary across cycles' },
|
|
27
|
+
attemptNumber: { type: 'integer', description: 'Attempt number within current phase' },
|
|
28
|
+
previousFailureReasons: {
|
|
29
|
+
type: 'array',
|
|
30
|
+
items: { type: 'string' },
|
|
31
|
+
description: 'List of previous failure reasons',
|
|
32
|
+
},
|
|
33
|
+
totalCostUsd: { type: 'number', description: 'Total cost in USD across all attempts' },
|
|
34
|
+
blockerIdentifier: { type: 'string', description: 'Blocker issue identifier' },
|
|
35
|
+
team: { type: 'string', description: 'Team name' },
|
|
36
|
+
repository: { type: 'string', description: 'Git repository URL pattern' },
|
|
37
|
+
projectPath: { type: 'string', description: 'Root directory for this project within the repo' },
|
|
38
|
+
sharedPaths: {
|
|
39
|
+
type: 'array',
|
|
40
|
+
items: { type: 'string' },
|
|
41
|
+
description: 'Shared directories that any project agent may modify',
|
|
42
|
+
},
|
|
43
|
+
useToolPlugins: { type: 'boolean', description: 'When true, agents use in-process tools instead of CLI' },
|
|
44
|
+
linearCli: { type: 'string', description: 'Command to invoke the Linear CLI (default: "pnpm af-linear")' },
|
|
45
|
+
packageManager: { type: 'string', description: 'Package manager used by the project (default: "pnpm")' },
|
|
46
|
+
buildCommand: { type: 'string', description: 'Build command override' },
|
|
47
|
+
testCommand: { type: 'string', description: 'Test command override' },
|
|
48
|
+
validateCommand: { type: 'string', description: 'Validation command override' },
|
|
49
|
+
phaseOutputs: {
|
|
50
|
+
type: 'object',
|
|
51
|
+
additionalProperties: {
|
|
52
|
+
type: 'object',
|
|
53
|
+
additionalProperties: true,
|
|
54
|
+
},
|
|
55
|
+
description: 'Collected outputs from upstream phases',
|
|
56
|
+
},
|
|
57
|
+
agentBugBacklog: { type: 'string', description: 'Linear project name for agent-improvement issues' },
|
|
58
|
+
};
|
|
59
|
+
/** Fields that are always required in a template config */
|
|
60
|
+
const ALWAYS_REQUIRED = ['identifier'];
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
// Handlebars expression extraction
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
/**
|
|
65
|
+
* Extract Handlebars variable references from a template prompt.
|
|
66
|
+
* Matches {{ varName }}, {{ varName.property }}, and handles
|
|
67
|
+
* conditionals like {{#if varName}} and {{#unless varName}}.
|
|
68
|
+
*
|
|
69
|
+
* Returns the set of top-level variable names referenced.
|
|
70
|
+
*/
|
|
71
|
+
export function extractTemplateVariables(prompt) {
|
|
72
|
+
const vars = new Set();
|
|
73
|
+
// Match {{ expr }}, {{#if expr}}, {{#unless expr}}, {{> partial}}, etc.
|
|
74
|
+
const patterns = [
|
|
75
|
+
/\{\{\s*([a-zA-Z_][a-zA-Z0-9_.]*)\s*\}\}/g, // {{ varName }} or {{ var.prop }}
|
|
76
|
+
/\{\{#(?:if|unless)\s+([a-zA-Z_][a-zA-Z0-9_.]*)/g, // {{#if varName}}
|
|
77
|
+
/\{\{#(?:each)\s+([a-zA-Z_][a-zA-Z0-9_.]*)/g, // {{#each varName}}
|
|
78
|
+
/\{\{#(?:with)\s+([a-zA-Z_][a-zA-Z0-9_.]*)/g, // {{#with varName}}
|
|
79
|
+
/\{\{\s*(?:eq|neq)\s+([a-zA-Z_][a-zA-Z0-9_.]*)/g, // {{ eq varName "value" }}
|
|
80
|
+
/\((?:eq|neq)\s+([a-zA-Z_][a-zA-Z0-9_.]*)/g, // (eq varName "value") — subexpression
|
|
81
|
+
];
|
|
82
|
+
for (const pattern of patterns) {
|
|
83
|
+
let match;
|
|
84
|
+
while ((match = pattern.exec(prompt)) !== null) {
|
|
85
|
+
const varName = match[1];
|
|
86
|
+
// Extract the top-level variable name (before any dot)
|
|
87
|
+
const topLevel = varName.split('.')[0];
|
|
88
|
+
// Skip Handlebars built-ins and partials
|
|
89
|
+
if (topLevel !== 'this' && topLevel !== 'else') {
|
|
90
|
+
vars.add(topLevel);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return vars;
|
|
95
|
+
}
|
|
96
|
+
// ---------------------------------------------------------------------------
|
|
97
|
+
// Schema generation
|
|
98
|
+
// ---------------------------------------------------------------------------
|
|
99
|
+
/**
|
|
100
|
+
* Generate a JSON Schema 7 definition for a WorkflowTemplate's parameters.
|
|
101
|
+
*
|
|
102
|
+
* By default, only includes fields that are referenced in the template's
|
|
103
|
+
* prompt. Set `includeAllFields: true` to include all known TemplateContext fields.
|
|
104
|
+
*/
|
|
105
|
+
export function generateTemplateSchema(template, options) {
|
|
106
|
+
const includeAll = options?.includeAllFields ?? false;
|
|
107
|
+
const properties = {};
|
|
108
|
+
const required = [];
|
|
109
|
+
if (includeAll) {
|
|
110
|
+
// Include all known fields
|
|
111
|
+
for (const [field, schemaDef] of Object.entries(CONTEXT_FIELD_SCHEMAS)) {
|
|
112
|
+
properties[field] = schemaDef;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
// Only include fields referenced in the template prompt
|
|
117
|
+
const referencedVars = extractTemplateVariables(template.prompt);
|
|
118
|
+
for (const varName of referencedVars) {
|
|
119
|
+
if (varName in CONTEXT_FIELD_SCHEMAS) {
|
|
120
|
+
properties[varName] = CONTEXT_FIELD_SCHEMAS[varName];
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
// Always include 'identifier' as required
|
|
125
|
+
for (const field of ALWAYS_REQUIRED) {
|
|
126
|
+
if (field in properties && !required.includes(field)) {
|
|
127
|
+
required.push(field);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
$schema: 'http://json-schema.org/draft-07/schema#',
|
|
132
|
+
type: 'object',
|
|
133
|
+
title: `${template.metadata.name} config`,
|
|
134
|
+
description: template.metadata.description ?? `Configuration schema for ${template.metadata.name} template`,
|
|
135
|
+
properties,
|
|
136
|
+
required: required.length > 0 ? required : undefined,
|
|
137
|
+
additionalProperties: true,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.test.d.ts","sourceRoot":"","sources":["../../../src/templates/schema.test.ts"],"names":[],"mappings":""}
|