@the-open-engine/zeroshot 5.4.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/CHANGELOG.md +539 -0
- package/LICENSE +21 -0
- package/README.md +508 -0
- package/cli/commands/inspect-render.js +135 -0
- package/cli/commands/inspect.js +294 -0
- package/cli/commands/providers.js +149 -0
- package/cli/index.js +5431 -0
- package/cli/lib/first-run.js +211 -0
- package/cli/lib/update-checker.js +281 -0
- package/cli/message-formatter-utils.js +75 -0
- package/cli/message-formatters-normal.js +275 -0
- package/cli/message-formatters-watch.js +185 -0
- package/cluster-templates/base-templates/debug-workflow.json +422 -0
- package/cluster-templates/base-templates/full-workflow.json +727 -0
- package/cluster-templates/base-templates/heavy-validation.json +272 -0
- package/cluster-templates/base-templates/quick-validation.json +285 -0
- package/cluster-templates/base-templates/single-worker.json +71 -0
- package/cluster-templates/base-templates/worker-validator.json +230 -0
- package/cluster-templates/conductor-bootstrap.json +130 -0
- package/docker/zeroshot-cluster/Dockerfile +153 -0
- package/docker/zeroshot-cluster/pre-baked-deps.json +28 -0
- package/lib/agent-cli-provider/adapters/claude-parser.d.ts +3 -0
- package/lib/agent-cli-provider/adapters/claude-parser.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/claude-parser.js +122 -0
- package/lib/agent-cli-provider/adapters/claude-parser.js.map +1 -0
- package/lib/agent-cli-provider/adapters/claude-recovery.d.ts +15 -0
- package/lib/agent-cli-provider/adapters/claude-recovery.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/claude-recovery.js +165 -0
- package/lib/agent-cli-provider/adapters/claude-recovery.js.map +1 -0
- package/lib/agent-cli-provider/adapters/claude.d.ts +3 -0
- package/lib/agent-cli-provider/adapters/claude.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/claude.js +181 -0
- package/lib/agent-cli-provider/adapters/claude.js.map +1 -0
- package/lib/agent-cli-provider/adapters/codex-parser.d.ts +3 -0
- package/lib/agent-cli-provider/adapters/codex-parser.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/codex-parser.js +192 -0
- package/lib/agent-cli-provider/adapters/codex-parser.js.map +1 -0
- package/lib/agent-cli-provider/adapters/codex.d.ts +3 -0
- package/lib/agent-cli-provider/adapters/codex.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/codex.js +151 -0
- package/lib/agent-cli-provider/adapters/codex.js.map +1 -0
- package/lib/agent-cli-provider/adapters/common.d.ts +28 -0
- package/lib/agent-cli-provider/adapters/common.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/common.js +71 -0
- package/lib/agent-cli-provider/adapters/common.js.map +1 -0
- package/lib/agent-cli-provider/adapters/gemini.d.ts +3 -0
- package/lib/agent-cli-provider/adapters/gemini.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/gemini.js +176 -0
- package/lib/agent-cli-provider/adapters/gemini.js.map +1 -0
- package/lib/agent-cli-provider/adapters/index.d.ts +9 -0
- package/lib/agent-cli-provider/adapters/index.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/index.js +95 -0
- package/lib/agent-cli-provider/adapters/index.js.map +1 -0
- package/lib/agent-cli-provider/adapters/opencode.d.ts +3 -0
- package/lib/agent-cli-provider/adapters/opencode.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/opencode.js +228 -0
- package/lib/agent-cli-provider/adapters/opencode.js.map +1 -0
- package/lib/agent-cli-provider/contract-actions.d.ts +5 -0
- package/lib/agent-cli-provider/contract-actions.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract-actions.js +140 -0
- package/lib/agent-cli-provider/contract-actions.js.map +1 -0
- package/lib/agent-cli-provider/contract-env.d.ts +11 -0
- package/lib/agent-cli-provider/contract-env.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract-env.js +113 -0
- package/lib/agent-cli-provider/contract-env.js.map +1 -0
- package/lib/agent-cli-provider/contract-envelope.d.ts +49 -0
- package/lib/agent-cli-provider/contract-envelope.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract-envelope.js +52 -0
- package/lib/agent-cli-provider/contract-envelope.js.map +1 -0
- package/lib/agent-cli-provider/contract-errors.d.ts +22 -0
- package/lib/agent-cli-provider/contract-errors.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract-errors.js +71 -0
- package/lib/agent-cli-provider/contract-errors.js.map +1 -0
- package/lib/agent-cli-provider/contract-fallback.d.ts +7 -0
- package/lib/agent-cli-provider/contract-fallback.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract-fallback.js +102 -0
- package/lib/agent-cli-provider/contract-fallback.js.map +1 -0
- package/lib/agent-cli-provider/contract-invoke.d.ts +5 -0
- package/lib/agent-cli-provider/contract-invoke.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract-invoke.js +89 -0
- package/lib/agent-cli-provider/contract-invoke.js.map +1 -0
- package/lib/agent-cli-provider/contract-options.d.ts +3 -0
- package/lib/agent-cli-provider/contract-options.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract-options.js +140 -0
- package/lib/agent-cli-provider/contract-options.js.map +1 -0
- package/lib/agent-cli-provider/contract-parse.d.ts +15 -0
- package/lib/agent-cli-provider/contract-parse.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract-parse.js +81 -0
- package/lib/agent-cli-provider/contract-parse.js.map +1 -0
- package/lib/agent-cli-provider/contract-support.d.ts +18 -0
- package/lib/agent-cli-provider/contract-support.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract-support.js +154 -0
- package/lib/agent-cli-provider/contract-support.js.map +1 -0
- package/lib/agent-cli-provider/contract.d.ts +13 -0
- package/lib/agent-cli-provider/contract.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract.js +30 -0
- package/lib/agent-cli-provider/contract.js.map +1 -0
- package/lib/agent-cli-provider/env-safety.d.ts +11 -0
- package/lib/agent-cli-provider/env-safety.d.ts.map +1 -0
- package/lib/agent-cli-provider/env-safety.js +83 -0
- package/lib/agent-cli-provider/env-safety.js.map +1 -0
- package/lib/agent-cli-provider/errors.d.ts +5 -0
- package/lib/agent-cli-provider/errors.d.ts.map +1 -0
- package/lib/agent-cli-provider/errors.js +115 -0
- package/lib/agent-cli-provider/errors.js.map +1 -0
- package/lib/agent-cli-provider/executable.d.ts +3 -0
- package/lib/agent-cli-provider/executable.d.ts.map +1 -0
- package/lib/agent-cli-provider/executable.js +24 -0
- package/lib/agent-cli-provider/executable.js.map +1 -0
- package/lib/agent-cli-provider/index.d.ts +8 -0
- package/lib/agent-cli-provider/index.d.ts.map +1 -0
- package/lib/agent-cli-provider/index.js +31 -0
- package/lib/agent-cli-provider/index.js.map +1 -0
- package/lib/agent-cli-provider/invoke-evidence.d.ts +4 -0
- package/lib/agent-cli-provider/invoke-evidence.d.ts.map +1 -0
- package/lib/agent-cli-provider/invoke-evidence.js +17 -0
- package/lib/agent-cli-provider/invoke-evidence.js.map +1 -0
- package/lib/agent-cli-provider/json.d.ts +16 -0
- package/lib/agent-cli-provider/json.d.ts.map +1 -0
- package/lib/agent-cli-provider/json.js +110 -0
- package/lib/agent-cli-provider/json.js.map +1 -0
- package/lib/agent-cli-provider/log-prefix.d.ts +2 -0
- package/lib/agent-cli-provider/log-prefix.d.ts.map +1 -0
- package/lib/agent-cli-provider/log-prefix.js +22 -0
- package/lib/agent-cli-provider/log-prefix.js.map +1 -0
- package/lib/agent-cli-provider/process-runner.d.ts +17 -0
- package/lib/agent-cli-provider/process-runner.d.ts.map +1 -0
- package/lib/agent-cli-provider/process-runner.js +89 -0
- package/lib/agent-cli-provider/process-runner.js.map +1 -0
- package/lib/agent-cli-provider/redaction.d.ts +9 -0
- package/lib/agent-cli-provider/redaction.d.ts.map +1 -0
- package/lib/agent-cli-provider/redaction.js +227 -0
- package/lib/agent-cli-provider/redaction.js.map +1 -0
- package/lib/agent-cli-provider/schema.d.ts +5 -0
- package/lib/agent-cli-provider/schema.d.ts.map +1 -0
- package/lib/agent-cli-provider/schema.js +136 -0
- package/lib/agent-cli-provider/schema.js.map +1 -0
- package/lib/agent-cli-provider/single-agent-runtime.d.ts +15 -0
- package/lib/agent-cli-provider/single-agent-runtime.d.ts.map +1 -0
- package/lib/agent-cli-provider/single-agent-runtime.js +228 -0
- package/lib/agent-cli-provider/single-agent-runtime.js.map +1 -0
- package/lib/agent-cli-provider/types.d.ts +194 -0
- package/lib/agent-cli-provider/types.d.ts.map +1 -0
- package/lib/agent-cli-provider/types.js +12 -0
- package/lib/agent-cli-provider/types.js.map +1 -0
- package/lib/completion.js +174 -0
- package/lib/detached-startup.js +220 -0
- package/lib/docker-config.js +220 -0
- package/lib/git-remote-utils.js +165 -0
- package/lib/id-detector.js +55 -0
- package/lib/provider-defaults.js +62 -0
- package/lib/provider-detection.js +59 -0
- package/lib/provider-names.js +57 -0
- package/lib/repo-settings.js +69 -0
- package/lib/settings/claude-auth.js +78 -0
- package/lib/settings.js +542 -0
- package/lib/start-cluster.js +321 -0
- package/lib/stream-json-parser.js +67 -0
- package/package.json +162 -0
- package/scripts/fix-node-pty-permissions.js +75 -0
- package/scripts/record-demo.sh +279 -0
- package/scripts/setup-merge-queue.sh +170 -0
- package/scripts/test-install.sh +40 -0
- package/scripts/validate-templates.js +107 -0
- package/src/agent/agent-config.js +266 -0
- package/src/agent/agent-context-builder.js +189 -0
- package/src/agent/agent-context-sections.js +338 -0
- package/src/agent/agent-context-sources.js +147 -0
- package/src/agent/agent-hook-executor.js +721 -0
- package/src/agent/agent-input-injector.js +141 -0
- package/src/agent/agent-lifecycle.js +982 -0
- package/src/agent/agent-quality-gate-schema.js +93 -0
- package/src/agent/agent-quality-gates-context.js +51 -0
- package/src/agent/agent-stuck-detector.js +256 -0
- package/src/agent/agent-task-executor.js +2028 -0
- package/src/agent/agent-trigger-evaluator.js +67 -0
- package/src/agent/context-metrics.js +160 -0
- package/src/agent/context-pack-builder.js +367 -0
- package/src/agent/context-replay-policy.js +51 -0
- package/src/agent/guidance-queue.js +77 -0
- package/src/agent/output-extraction.js +367 -0
- package/src/agent/output-reformatter.js +175 -0
- package/src/agent/pr-verification.js +653 -0
- package/src/agent/rate-limit-backoff.js +82 -0
- package/src/agent/schema-utils.js +146 -0
- package/src/agent/validation-platform.js +35 -0
- package/src/agent-cli-provider/adapters/claude-parser.ts +133 -0
- package/src/agent-cli-provider/adapters/claude-recovery.ts +203 -0
- package/src/agent-cli-provider/adapters/claude.ts +247 -0
- package/src/agent-cli-provider/adapters/codex-parser.ts +211 -0
- package/src/agent-cli-provider/adapters/codex.ts +217 -0
- package/src/agent-cli-provider/adapters/common.ts +124 -0
- package/src/agent-cli-provider/adapters/gemini.ts +243 -0
- package/src/agent-cli-provider/adapters/index.ts +126 -0
- package/src/agent-cli-provider/adapters/opencode.ts +286 -0
- package/src/agent-cli-provider/contract-actions.ts +150 -0
- package/src/agent-cli-provider/contract-env.ts +111 -0
- package/src/agent-cli-provider/contract-envelope.ts +110 -0
- package/src/agent-cli-provider/contract-errors.ts +66 -0
- package/src/agent-cli-provider/contract-fallback.ts +121 -0
- package/src/agent-cli-provider/contract-invoke.ts +104 -0
- package/src/agent-cli-provider/contract-options.ts +173 -0
- package/src/agent-cli-provider/contract-parse.ts +94 -0
- package/src/agent-cli-provider/contract-support.ts +167 -0
- package/src/agent-cli-provider/contract.ts +56 -0
- package/src/agent-cli-provider/env-safety.ts +82 -0
- package/src/agent-cli-provider/errors.ts +122 -0
- package/src/agent-cli-provider/executable.ts +24 -0
- package/src/agent-cli-provider/index.ts +83 -0
- package/src/agent-cli-provider/invoke-evidence.ts +18 -0
- package/src/agent-cli-provider/json.ts +114 -0
- package/src/agent-cli-provider/log-prefix.ts +20 -0
- package/src/agent-cli-provider/process-runner.ts +145 -0
- package/src/agent-cli-provider/redaction.ts +282 -0
- package/src/agent-cli-provider/schema.ts +115 -0
- package/src/agent-cli-provider/single-agent-runtime.ts +311 -0
- package/src/agent-cli-provider/types.ts +237 -0
- package/src/agent-wrapper.js +615 -0
- package/src/agents/git-pusher-template.js +705 -0
- package/src/attach/attach-client.js +438 -0
- package/src/attach/attach-server.js +543 -0
- package/src/attach/index.js +37 -0
- package/src/attach/protocol.js +220 -0
- package/src/attach/ring-buffer.js +121 -0
- package/src/attach/send-input.js +88 -0
- package/src/attach/socket-discovery.js +267 -0
- package/src/claude-task-runner.js +661 -0
- package/src/config-router.js +89 -0
- package/src/config-validator.js +2202 -0
- package/src/copy-worker.js +43 -0
- package/src/guidance-topics.js +10 -0
- package/src/input-helpers.js +65 -0
- package/src/isolation-manager.js +1734 -0
- package/src/issue-providers/README.md +305 -0
- package/src/issue-providers/azure-devops-provider.js +307 -0
- package/src/issue-providers/base-provider.js +232 -0
- package/src/issue-providers/github-provider.js +210 -0
- package/src/issue-providers/gitlab-provider.js +262 -0
- package/src/issue-providers/index.js +196 -0
- package/src/issue-providers/jira-provider.js +260 -0
- package/src/ledger.js +692 -0
- package/src/lib/gc.js +232 -0
- package/src/lib/safe-exec.js +88 -0
- package/src/logic-engine.js +201 -0
- package/src/message-buffer.js +81 -0
- package/src/message-bus-bridge.js +144 -0
- package/src/message-bus.js +256 -0
- package/src/name-generator.js +232 -0
- package/src/orchestrator.js +3924 -0
- package/src/preflight.js +712 -0
- package/src/process-metrics.js +608 -0
- package/src/providers/anthropic/index.js +3 -0
- package/src/providers/base-provider.js +355 -0
- package/src/providers/capabilities.js +60 -0
- package/src/providers/google/index.js +3 -0
- package/src/providers/index.js +293 -0
- package/src/providers/openai/index.js +3 -0
- package/src/providers/opencode/index.js +3 -0
- package/src/quality-gates.js +143 -0
- package/src/schemas/sub-cluster.js +208 -0
- package/src/state-snapshot.js +398 -0
- package/src/state-snapshotter.js +142 -0
- package/src/status-footer.js +1026 -0
- package/src/sub-cluster-wrapper.js +693 -0
- package/src/task-runner.js +30 -0
- package/src/template-resolver.js +425 -0
- package/src/template-validation/index.js +338 -0
- package/src/template-validation/simulate-consensus-gates.js +324 -0
- package/src/template-validation/simulate-random-topology.js +541 -0
- package/src/template-validation/simulate-two-stage-validation.js +270 -0
- package/src/worktree-claude-config.js +135 -0
- package/src/worktree-tooling-env.js +150 -0
- package/task-lib/attachable-watcher.js +381 -0
- package/task-lib/commands/clean.js +50 -0
- package/task-lib/commands/episodes.js +105 -0
- package/task-lib/commands/get-log-path.js +23 -0
- package/task-lib/commands/kill.js +32 -0
- package/task-lib/commands/list.js +105 -0
- package/task-lib/commands/logs.js +439 -0
- package/task-lib/commands/resume.js +42 -0
- package/task-lib/commands/run.js +57 -0
- package/task-lib/commands/schedule.js +105 -0
- package/task-lib/commands/scheduler-cmd.js +96 -0
- package/task-lib/commands/schedules.js +148 -0
- package/task-lib/commands/status.js +44 -0
- package/task-lib/commands/unschedule.js +16 -0
- package/task-lib/completion.js +9 -0
- package/task-lib/config.js +11 -0
- package/task-lib/name-generator.js +230 -0
- package/task-lib/package.json +3 -0
- package/task-lib/provider-helper-runtime.js +29 -0
- package/task-lib/runner.js +190 -0
- package/task-lib/scheduler.js +252 -0
- package/task-lib/store.js +529 -0
- package/task-lib/watcher.js +305 -0
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TemplateResolver - Resolves parameterized cluster templates
|
|
3
|
+
*
|
|
4
|
+
* Takes a base template with {{param}} placeholders and resolves them
|
|
5
|
+
* with provided parameter values. Pure data transformation, no magic.
|
|
6
|
+
*
|
|
7
|
+
* Resolution rules:
|
|
8
|
+
* 1. Load base template JSON
|
|
9
|
+
* 2. Deep clone
|
|
10
|
+
* 3. Walk all values, replace {{param}} with params[param]
|
|
11
|
+
* 4. Handle conditional agents via "condition" field
|
|
12
|
+
* 5. Fail hard if any {{param}} remains unresolved
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
const path = require('path');
|
|
17
|
+
|
|
18
|
+
const COMPARISON_OPERATORS = ['==', '!=', '<=', '>=', '<', '>'];
|
|
19
|
+
|
|
20
|
+
function isIdentifierChar(char) {
|
|
21
|
+
if (!char) return false;
|
|
22
|
+
const code = char.charCodeAt(0);
|
|
23
|
+
return (
|
|
24
|
+
(code >= 48 && code <= 57) ||
|
|
25
|
+
(code >= 65 && code <= 90) ||
|
|
26
|
+
(code >= 97 && code <= 122) ||
|
|
27
|
+
code === 95
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function replaceBareIdentifier(source, name, replacement) {
|
|
32
|
+
let result = '';
|
|
33
|
+
let cursor = 0;
|
|
34
|
+
|
|
35
|
+
while (cursor < source.length) {
|
|
36
|
+
const index = source.indexOf(name, cursor);
|
|
37
|
+
if (index === -1) {
|
|
38
|
+
return result + source.slice(cursor);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const before = source[index - 1];
|
|
42
|
+
const after = source[index + name.length];
|
|
43
|
+
const replace = !isIdentifierChar(before) && !isIdentifierChar(after);
|
|
44
|
+
result += source.slice(cursor, index);
|
|
45
|
+
result += replace ? replacement : name;
|
|
46
|
+
cursor = index + name.length;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function formatConditionValue(value) {
|
|
53
|
+
if (value === undefined) return 'undefined';
|
|
54
|
+
if (typeof value === 'string') return JSON.stringify(value);
|
|
55
|
+
return String(value);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function parseNumberLiteral(value) {
|
|
59
|
+
if (!value) return null;
|
|
60
|
+
const unsigned = value[0] === '-' ? value.slice(1) : value;
|
|
61
|
+
const parts = unsigned.split('.');
|
|
62
|
+
if (parts.length > 2 || parts.some((part) => part.length === 0)) return null;
|
|
63
|
+
const numeric = parts.every((part) =>
|
|
64
|
+
[...part].every((char) => {
|
|
65
|
+
const code = char.charCodeAt(0);
|
|
66
|
+
return code >= 48 && code <= 57;
|
|
67
|
+
})
|
|
68
|
+
);
|
|
69
|
+
return numeric ? Number(value) : null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function compareValues(left, operator, right) {
|
|
73
|
+
switch (operator) {
|
|
74
|
+
case '==':
|
|
75
|
+
return left === right;
|
|
76
|
+
case '!=':
|
|
77
|
+
return left !== right;
|
|
78
|
+
case '<':
|
|
79
|
+
return left < right;
|
|
80
|
+
case '>':
|
|
81
|
+
return left > right;
|
|
82
|
+
case '<=':
|
|
83
|
+
return left <= right;
|
|
84
|
+
case '>=':
|
|
85
|
+
return left >= right;
|
|
86
|
+
default:
|
|
87
|
+
throw new Error(`Unsupported comparison operator: ${operator}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
class TemplateResolver {
|
|
92
|
+
/**
|
|
93
|
+
* @param {string} templatesDir
|
|
94
|
+
*/
|
|
95
|
+
constructor(templatesDir) {
|
|
96
|
+
this.templatesDir = templatesDir;
|
|
97
|
+
this.baseTemplatesDir = path.join(templatesDir, 'base-templates');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Resolve a template with parameters
|
|
102
|
+
* @param {string} baseName - Name of base template (without .json)
|
|
103
|
+
* @param {Object} params - Parameter values to substitute
|
|
104
|
+
* @returns {Object} Resolved cluster config
|
|
105
|
+
*/
|
|
106
|
+
resolve(baseName, params) {
|
|
107
|
+
// Load base template
|
|
108
|
+
const templatePath = path.join(this.baseTemplatesDir, `${baseName}.json`);
|
|
109
|
+
if (!fs.existsSync(templatePath)) {
|
|
110
|
+
throw new Error(`Base template not found: ${baseName} (looked in ${templatePath})`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const templateJson = fs.readFileSync(templatePath, 'utf8');
|
|
114
|
+
const template = JSON.parse(templateJson);
|
|
115
|
+
|
|
116
|
+
return this.resolveTemplate(template, params);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Resolve an already-loaded parameterized template object.
|
|
121
|
+
* @param {Object} template
|
|
122
|
+
* @param {Object} params
|
|
123
|
+
* @returns {Object} Resolved cluster config
|
|
124
|
+
*/
|
|
125
|
+
resolveTemplate(template, params = {}) {
|
|
126
|
+
// Validate required params
|
|
127
|
+
this._validateParams(template, params);
|
|
128
|
+
|
|
129
|
+
// Apply defaults for missing params (e.g., timeout: 0)
|
|
130
|
+
const paramsWithDefaults = this._applyDefaults(template, params);
|
|
131
|
+
|
|
132
|
+
// Deep clone and resolve
|
|
133
|
+
const resolved = this._resolveObject(JSON.parse(JSON.stringify(template)), paramsWithDefaults);
|
|
134
|
+
|
|
135
|
+
// Filter out conditional agents that don't meet their condition
|
|
136
|
+
if (resolved.agents) {
|
|
137
|
+
resolved.agents = resolved.agents.filter((/** @type {any} */ agent) => {
|
|
138
|
+
if (!agent.condition) return true;
|
|
139
|
+
const conditionMet = this._evaluateCondition(agent.condition, paramsWithDefaults);
|
|
140
|
+
delete agent.condition; // Remove condition field from final output
|
|
141
|
+
return conditionMet;
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Verify no unresolved placeholders remain
|
|
146
|
+
this._verifyResolved(resolved);
|
|
147
|
+
|
|
148
|
+
// Remove params schema from output (it's metadata, not config)
|
|
149
|
+
delete resolved.params;
|
|
150
|
+
|
|
151
|
+
return resolved;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Validate that required params are provided
|
|
156
|
+
* @private
|
|
157
|
+
* @param {any} template
|
|
158
|
+
* @param {any} params
|
|
159
|
+
*/
|
|
160
|
+
_validateParams(template, params) {
|
|
161
|
+
if (!template.params) return;
|
|
162
|
+
|
|
163
|
+
const missing = [];
|
|
164
|
+
for (const [name, schema] of Object.entries(template.params)) {
|
|
165
|
+
if (params[name] === undefined && schema.default === undefined) {
|
|
166
|
+
missing.push(name);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (missing.length > 0) {
|
|
171
|
+
throw new Error(`Missing required params: ${missing.join(', ')}`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Apply template defaults for any missing params
|
|
177
|
+
* @private
|
|
178
|
+
* @param {any} template
|
|
179
|
+
* @param {any} params
|
|
180
|
+
* @returns {any} params with defaults applied
|
|
181
|
+
*/
|
|
182
|
+
_applyDefaults(template, params) {
|
|
183
|
+
if (!template.params) return params;
|
|
184
|
+
|
|
185
|
+
const result = { ...params };
|
|
186
|
+
for (const [name, schema] of Object.entries(template.params)) {
|
|
187
|
+
if (result[name] === undefined && schema.default !== undefined) {
|
|
188
|
+
result[name] = schema.default;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return result;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Recursively resolve placeholders in an object
|
|
196
|
+
* @private
|
|
197
|
+
* @param {any} obj
|
|
198
|
+
* @param {any} params
|
|
199
|
+
* @returns {any}
|
|
200
|
+
*/
|
|
201
|
+
_resolveObject(obj, params) {
|
|
202
|
+
if (obj === null || obj === undefined) {
|
|
203
|
+
return obj;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (typeof obj === 'string') {
|
|
207
|
+
return this._resolveString(obj, params);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (Array.isArray(obj)) {
|
|
211
|
+
return obj.map((item) => this._resolveObject(item, params));
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (typeof obj === 'object') {
|
|
215
|
+
/** @type {any} */
|
|
216
|
+
const result = {};
|
|
217
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
218
|
+
result[key] = this._resolveObject(value, params);
|
|
219
|
+
}
|
|
220
|
+
return result;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return obj;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Resolve placeholders in a string
|
|
228
|
+
* Supports: {{param}} and {{#if condition}}...{{/if}}
|
|
229
|
+
* @private
|
|
230
|
+
* @param {any} str
|
|
231
|
+
* @param {any} params
|
|
232
|
+
* @returns {any}
|
|
233
|
+
*/
|
|
234
|
+
_resolveString(str, params) {
|
|
235
|
+
// Handle simple {{param}} substitutions
|
|
236
|
+
let result = str.replace(
|
|
237
|
+
/\{\{(\w+)\}\}/g,
|
|
238
|
+
(/** @type {any} */ _match, /** @type {any} */ paramName) => {
|
|
239
|
+
if (params[paramName] !== undefined) {
|
|
240
|
+
return params[paramName];
|
|
241
|
+
}
|
|
242
|
+
// Return match unchanged if param not found (will be caught by verify)
|
|
243
|
+
return _match;
|
|
244
|
+
}
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
// Handle {{#if condition}}...{{/if}} blocks
|
|
248
|
+
result = result.replace(
|
|
249
|
+
/\{\{#if\s+([^}]+)\}\}([\s\S]*?)\{\{\/if\}\}/g,
|
|
250
|
+
(/** @type {any} */ _match, /** @type {any} */ condition, /** @type {any} */ content) => {
|
|
251
|
+
const conditionMet = this._evaluateCondition(condition, params);
|
|
252
|
+
return conditionMet ? content : '';
|
|
253
|
+
}
|
|
254
|
+
);
|
|
255
|
+
|
|
256
|
+
// Clean up multiple newlines from removed conditionals
|
|
257
|
+
result = result.replace(/\n{3,}/g, '\n\n');
|
|
258
|
+
|
|
259
|
+
return result;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Evaluate a simple condition expression
|
|
264
|
+
* Supports: param == 'value', param != 'value', {{param}} >= N
|
|
265
|
+
* @private
|
|
266
|
+
* @param {any} condition
|
|
267
|
+
* @param {any} params
|
|
268
|
+
* @returns {boolean}
|
|
269
|
+
*/
|
|
270
|
+
_evaluateCondition(condition, params) {
|
|
271
|
+
// Replace {{param}} with actual values first
|
|
272
|
+
let expr = condition.trim();
|
|
273
|
+
|
|
274
|
+
// Replace {{param}} placeholders
|
|
275
|
+
expr = expr.replace(
|
|
276
|
+
/\{\{(\w+)\}\}/g,
|
|
277
|
+
(/** @type {any} */ _match, /** @type {any} */ paramName) => {
|
|
278
|
+
return formatConditionValue(params[paramName]);
|
|
279
|
+
}
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
// Replace bare param names
|
|
283
|
+
for (const [name, value] of Object.entries(params)) {
|
|
284
|
+
expr = replaceBareIdentifier(expr, name, formatConditionValue(value));
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
try {
|
|
288
|
+
// Parse and evaluate simple comparison expressions without eval
|
|
289
|
+
return this._evaluateSimpleExpression(expr);
|
|
290
|
+
} catch {
|
|
291
|
+
console.error(`Failed to evaluate condition: ${condition} (resolved: ${expr})`);
|
|
292
|
+
return false;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Evaluate simple comparison expressions without eval
|
|
298
|
+
* Supports: ==, !=, <, >, <=, >=, &&, ||
|
|
299
|
+
* @private
|
|
300
|
+
* @param {string} expr
|
|
301
|
+
* @returns {boolean}
|
|
302
|
+
*/
|
|
303
|
+
_evaluateSimpleExpression(expr) {
|
|
304
|
+
// Handle logical operators (&&, ||) by splitting and recursing
|
|
305
|
+
if (expr.includes('||')) {
|
|
306
|
+
return expr.split('||').some((part) => this._evaluateSimpleExpression(part.trim()));
|
|
307
|
+
}
|
|
308
|
+
if (expr.includes('&&')) {
|
|
309
|
+
return expr.split('&&').every((part) => this._evaluateSimpleExpression(part.trim()));
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
for (const op of COMPARISON_OPERATORS) {
|
|
313
|
+
if (expr.includes(op)) {
|
|
314
|
+
const [left, right] = expr.split(op).map((s) => s.trim());
|
|
315
|
+
return compareValues(this._parseValue(left), op, this._parseValue(right));
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// If no operator found, treat as boolean literal or truthy value
|
|
320
|
+
return this._parseValue(expr) ? true : false;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Parse a value from string (number, boolean, string literal)
|
|
325
|
+
* @private
|
|
326
|
+
* @param {string} str
|
|
327
|
+
* @returns {any}
|
|
328
|
+
*/
|
|
329
|
+
_parseValue(str) {
|
|
330
|
+
const trimmed = str.trim();
|
|
331
|
+
|
|
332
|
+
// String literals (single or double quotes)
|
|
333
|
+
if (
|
|
334
|
+
(trimmed.startsWith('"') && trimmed.endsWith('"')) ||
|
|
335
|
+
(trimmed.startsWith("'") && trimmed.endsWith("'"))
|
|
336
|
+
) {
|
|
337
|
+
return trimmed.slice(1, -1);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Boolean literals
|
|
341
|
+
if (trimmed === 'true') return true;
|
|
342
|
+
if (trimmed === 'false') return false;
|
|
343
|
+
if (trimmed === 'undefined' || trimmed === 'null') return undefined;
|
|
344
|
+
|
|
345
|
+
const numericValue = parseNumberLiteral(trimmed);
|
|
346
|
+
if (numericValue !== null) return numericValue;
|
|
347
|
+
|
|
348
|
+
// Return as-is for other cases
|
|
349
|
+
return trimmed;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Verify no unresolved placeholders remain
|
|
354
|
+
* @private
|
|
355
|
+
* @param {any} obj
|
|
356
|
+
*/
|
|
357
|
+
_verifyResolved(obj) {
|
|
358
|
+
const unresolved = this._findUnresolved(obj);
|
|
359
|
+
if (unresolved.length > 0) {
|
|
360
|
+
throw new Error(`Unresolved template placeholders: ${unresolved.join(', ')}`);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Find all unresolved {{param}} placeholders
|
|
366
|
+
* @private
|
|
367
|
+
* @param {any} obj
|
|
368
|
+
* @param {string} pathPrefix
|
|
369
|
+
* @returns {string[]}
|
|
370
|
+
*/
|
|
371
|
+
_findUnresolved(obj, pathPrefix = '') {
|
|
372
|
+
const unresolved = [];
|
|
373
|
+
|
|
374
|
+
if (typeof obj === 'string') {
|
|
375
|
+
const matches = obj.match(/\{\{(\w+)\}\}/g);
|
|
376
|
+
if (matches) {
|
|
377
|
+
unresolved.push(...matches.map((m) => `${pathPrefix}: ${m}`));
|
|
378
|
+
}
|
|
379
|
+
} else if (Array.isArray(obj)) {
|
|
380
|
+
obj.forEach((item, i) => {
|
|
381
|
+
unresolved.push(...this._findUnresolved(item, `${pathPrefix}[${i}]`));
|
|
382
|
+
});
|
|
383
|
+
} else if (obj && typeof obj === 'object') {
|
|
384
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
385
|
+
unresolved.push(...this._findUnresolved(value, `${pathPrefix}.${key}`));
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
return unresolved;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* List available base templates
|
|
394
|
+
* @returns {string[]}
|
|
395
|
+
*/
|
|
396
|
+
listTemplates() {
|
|
397
|
+
if (!fs.existsSync(this.baseTemplatesDir)) {
|
|
398
|
+
return [];
|
|
399
|
+
}
|
|
400
|
+
return fs
|
|
401
|
+
.readdirSync(this.baseTemplatesDir)
|
|
402
|
+
.filter((f) => f.endsWith('.json'))
|
|
403
|
+
.map((f) => f.replace('.json', ''));
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Get template metadata (name, description, params)
|
|
408
|
+
* @param {any} baseName
|
|
409
|
+
* @returns {any}
|
|
410
|
+
*/
|
|
411
|
+
getTemplateInfo(baseName) {
|
|
412
|
+
const templatePath = path.join(this.baseTemplatesDir, `${baseName}.json`);
|
|
413
|
+
if (!fs.existsSync(templatePath)) {
|
|
414
|
+
return null;
|
|
415
|
+
}
|
|
416
|
+
const template = JSON.parse(fs.readFileSync(templatePath, 'utf8'));
|
|
417
|
+
return {
|
|
418
|
+
name: template.name,
|
|
419
|
+
description: template.description,
|
|
420
|
+
params: template.params || {},
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
module.exports = TemplateResolver;
|