opencastle 0.26.1 → 0.27.1
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 +7 -1
- package/bin/cli.mjs +10 -0
- package/dist/cli/agents.d.ts +3 -0
- package/dist/cli/agents.d.ts.map +1 -0
- package/dist/cli/agents.js +161 -0
- package/dist/cli/agents.js.map +1 -0
- package/dist/cli/baselines.d.ts +3 -0
- package/dist/cli/baselines.d.ts.map +1 -0
- package/dist/cli/baselines.js +128 -0
- package/dist/cli/baselines.js.map +1 -0
- package/dist/cli/convoy/engine.d.ts +68 -2
- package/dist/cli/convoy/engine.d.ts.map +1 -1
- package/dist/cli/convoy/engine.js +2102 -26
- package/dist/cli/convoy/engine.js.map +1 -1
- package/dist/cli/convoy/engine.test.js +1572 -70
- package/dist/cli/convoy/engine.test.js.map +1 -1
- package/dist/cli/convoy/events.d.ts +4 -1
- package/dist/cli/convoy/events.d.ts.map +1 -1
- package/dist/cli/convoy/events.js +74 -13
- package/dist/cli/convoy/events.js.map +1 -1
- package/dist/cli/convoy/events.test.js +154 -27
- package/dist/cli/convoy/events.test.js.map +1 -1
- package/dist/cli/convoy/expertise.d.ts +16 -0
- package/dist/cli/convoy/expertise.d.ts.map +1 -0
- package/dist/cli/convoy/expertise.js +121 -0
- package/dist/cli/convoy/expertise.js.map +1 -0
- package/dist/cli/convoy/expertise.test.d.ts +2 -0
- package/dist/cli/convoy/expertise.test.d.ts.map +1 -0
- package/dist/cli/convoy/expertise.test.js +96 -0
- package/dist/cli/convoy/expertise.test.js.map +1 -0
- package/dist/cli/convoy/export.test.js +1 -0
- package/dist/cli/convoy/export.test.js.map +1 -1
- package/dist/cli/convoy/formula.d.ts +19 -0
- package/dist/cli/convoy/formula.d.ts.map +1 -0
- package/dist/cli/convoy/formula.js +142 -0
- package/dist/cli/convoy/formula.js.map +1 -0
- package/dist/cli/convoy/formula.test.d.ts +2 -0
- package/dist/cli/convoy/formula.test.d.ts.map +1 -0
- package/dist/cli/convoy/formula.test.js +342 -0
- package/dist/cli/convoy/formula.test.js.map +1 -0
- package/dist/cli/convoy/gates.d.ts +128 -0
- package/dist/cli/convoy/gates.d.ts.map +1 -0
- package/dist/cli/convoy/gates.js +606 -0
- package/dist/cli/convoy/gates.js.map +1 -0
- package/dist/cli/convoy/gates.test.d.ts +2 -0
- package/dist/cli/convoy/gates.test.d.ts.map +1 -0
- package/dist/cli/convoy/gates.test.js +976 -0
- package/dist/cli/convoy/gates.test.js.map +1 -0
- package/dist/cli/convoy/health.d.ts +11 -0
- package/dist/cli/convoy/health.d.ts.map +1 -1
- package/dist/cli/convoy/health.js +54 -0
- package/dist/cli/convoy/health.js.map +1 -1
- package/dist/cli/convoy/health.test.js +56 -1
- package/dist/cli/convoy/health.test.js.map +1 -1
- package/dist/cli/convoy/issues.d.ts +8 -0
- package/dist/cli/convoy/issues.d.ts.map +1 -0
- package/dist/cli/convoy/issues.js +98 -0
- package/dist/cli/convoy/issues.js.map +1 -0
- package/dist/cli/convoy/issues.test.d.ts +2 -0
- package/dist/cli/convoy/issues.test.d.ts.map +1 -0
- package/dist/cli/convoy/issues.test.js +107 -0
- package/dist/cli/convoy/issues.test.js.map +1 -0
- package/dist/cli/convoy/knowledge.d.ts +5 -0
- package/dist/cli/convoy/knowledge.d.ts.map +1 -0
- package/dist/cli/convoy/knowledge.js +116 -0
- package/dist/cli/convoy/knowledge.js.map +1 -0
- package/dist/cli/convoy/knowledge.test.d.ts +2 -0
- package/dist/cli/convoy/knowledge.test.d.ts.map +1 -0
- package/dist/cli/convoy/knowledge.test.js +87 -0
- package/dist/cli/convoy/knowledge.test.js.map +1 -0
- package/dist/cli/convoy/lessons.d.ts +17 -0
- package/dist/cli/convoy/lessons.d.ts.map +1 -0
- package/dist/cli/convoy/lessons.js +149 -0
- package/dist/cli/convoy/lessons.js.map +1 -0
- package/dist/cli/convoy/lessons.test.d.ts +2 -0
- package/dist/cli/convoy/lessons.test.d.ts.map +1 -0
- package/dist/cli/convoy/lessons.test.js +135 -0
- package/dist/cli/convoy/lessons.test.js.map +1 -0
- package/dist/cli/convoy/lock.d.ts +13 -0
- package/dist/cli/convoy/lock.d.ts.map +1 -0
- package/dist/cli/convoy/lock.js +88 -0
- package/dist/cli/convoy/lock.js.map +1 -0
- package/dist/cli/convoy/lock.test.d.ts +2 -0
- package/dist/cli/convoy/lock.test.d.ts.map +1 -0
- package/dist/cli/convoy/lock.test.js +136 -0
- package/dist/cli/convoy/lock.test.js.map +1 -0
- package/dist/cli/convoy/merge.d.ts +4 -0
- package/dist/cli/convoy/merge.d.ts.map +1 -1
- package/dist/cli/convoy/merge.js +18 -1
- package/dist/cli/convoy/merge.js.map +1 -1
- package/dist/cli/convoy/merge.test.js +6 -7
- package/dist/cli/convoy/merge.test.js.map +1 -1
- package/dist/cli/convoy/partition.d.ts +51 -0
- package/dist/cli/convoy/partition.d.ts.map +1 -0
- package/dist/cli/convoy/partition.js +186 -0
- package/dist/cli/convoy/partition.js.map +1 -0
- package/dist/cli/convoy/partition.test.d.ts +2 -0
- package/dist/cli/convoy/partition.test.d.ts.map +1 -0
- package/dist/cli/convoy/partition.test.js +315 -0
- package/dist/cli/convoy/partition.test.js.map +1 -0
- package/dist/cli/convoy/pipeline.test.js +6 -0
- package/dist/cli/convoy/pipeline.test.js.map +1 -1
- package/dist/cli/convoy/store.d.ts +47 -5
- package/dist/cli/convoy/store.d.ts.map +1 -1
- package/dist/cli/convoy/store.js +525 -19
- package/dist/cli/convoy/store.js.map +1 -1
- package/dist/cli/convoy/store.test.js +1345 -12
- package/dist/cli/convoy/store.test.js.map +1 -1
- package/dist/cli/convoy/types.d.ts +156 -2
- package/dist/cli/convoy/types.d.ts.map +1 -1
- package/dist/cli/destroy.d.ts +3 -0
- package/dist/cli/destroy.d.ts.map +1 -0
- package/dist/cli/destroy.js +69 -0
- package/dist/cli/destroy.js.map +1 -0
- package/dist/cli/destroy.test.d.ts +2 -0
- package/dist/cli/destroy.test.d.ts.map +1 -0
- package/dist/cli/destroy.test.js +116 -0
- package/dist/cli/destroy.test.js.map +1 -0
- package/dist/cli/gitignore.d.ts +9 -0
- package/dist/cli/gitignore.d.ts.map +1 -1
- package/dist/cli/gitignore.js +29 -0
- package/dist/cli/gitignore.js.map +1 -1
- package/dist/cli/plan.d.ts +3 -0
- package/dist/cli/plan.d.ts.map +1 -0
- package/dist/cli/plan.js +288 -0
- package/dist/cli/plan.js.map +1 -0
- package/dist/cli/run/adapters/claude.d.ts +2 -0
- package/dist/cli/run/adapters/claude.d.ts.map +1 -1
- package/dist/cli/run/adapters/claude.js +89 -49
- package/dist/cli/run/adapters/claude.js.map +1 -1
- package/dist/cli/run/adapters/claude.test.d.ts +2 -0
- package/dist/cli/run/adapters/claude.test.d.ts.map +1 -0
- package/dist/cli/run/adapters/claude.test.js +205 -0
- package/dist/cli/run/adapters/claude.test.js.map +1 -0
- package/dist/cli/run/adapters/copilot.d.ts +1 -0
- package/dist/cli/run/adapters/copilot.d.ts.map +1 -1
- package/dist/cli/run/adapters/copilot.js +84 -46
- package/dist/cli/run/adapters/copilot.js.map +1 -1
- package/dist/cli/run/adapters/copilot.test.d.ts +2 -0
- package/dist/cli/run/adapters/copilot.test.d.ts.map +1 -0
- package/dist/cli/run/adapters/copilot.test.js +195 -0
- package/dist/cli/run/adapters/copilot.test.js.map +1 -0
- package/dist/cli/run/adapters/cursor.d.ts +1 -0
- package/dist/cli/run/adapters/cursor.d.ts.map +1 -1
- package/dist/cli/run/adapters/cursor.js +83 -47
- package/dist/cli/run/adapters/cursor.js.map +1 -1
- package/dist/cli/run/adapters/cursor.test.d.ts +2 -0
- package/dist/cli/run/adapters/cursor.test.d.ts.map +1 -0
- package/dist/cli/run/adapters/cursor.test.js +129 -0
- package/dist/cli/run/adapters/cursor.test.js.map +1 -0
- package/dist/cli/run/adapters/opencode.d.ts +1 -0
- package/dist/cli/run/adapters/opencode.d.ts.map +1 -1
- package/dist/cli/run/adapters/opencode.js +81 -47
- package/dist/cli/run/adapters/opencode.js.map +1 -1
- package/dist/cli/run/adapters/opencode.test.d.ts +2 -0
- package/dist/cli/run/adapters/opencode.test.d.ts.map +1 -0
- package/dist/cli/run/adapters/opencode.test.js +119 -0
- package/dist/cli/run/adapters/opencode.test.js.map +1 -0
- package/dist/cli/run/executor.js +1 -1
- package/dist/cli/run/executor.js.map +1 -1
- package/dist/cli/run/schema.d.ts.map +1 -1
- package/dist/cli/run/schema.js +245 -4
- package/dist/cli/run/schema.js.map +1 -1
- package/dist/cli/run/schema.test.js +669 -0
- package/dist/cli/run/schema.test.js.map +1 -1
- package/dist/cli/run.d.ts.map +1 -1
- package/dist/cli/run.js +362 -22
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/types.d.ts +85 -2
- package/dist/cli/types.d.ts.map +1 -1
- package/dist/cli/types.js.map +1 -1
- package/dist/cli/watch.d.ts +15 -0
- package/dist/cli/watch.d.ts.map +1 -0
- package/dist/cli/watch.js +279 -0
- package/dist/cli/watch.js.map +1 -0
- package/package.json +1 -1
- package/src/cli/agents.ts +177 -0
- package/src/cli/baselines.ts +143 -0
- package/src/cli/convoy/engine.test.ts +1839 -70
- package/src/cli/convoy/engine.ts +2417 -38
- package/src/cli/convoy/events.test.ts +179 -38
- package/src/cli/convoy/events.ts +88 -16
- package/src/cli/convoy/expertise.test.ts +128 -0
- package/src/cli/convoy/expertise.ts +163 -0
- package/src/cli/convoy/export.test.ts +1 -0
- package/src/cli/convoy/formula.test.ts +405 -0
- package/src/cli/convoy/formula.ts +174 -0
- package/src/cli/convoy/gates.test.ts +1169 -0
- package/src/cli/convoy/gates.ts +774 -0
- package/src/cli/convoy/health.test.ts +64 -2
- package/src/cli/convoy/health.ts +80 -2
- package/src/cli/convoy/issues.test.ts +143 -0
- package/src/cli/convoy/issues.ts +136 -0
- package/src/cli/convoy/knowledge.test.ts +101 -0
- package/src/cli/convoy/knowledge.ts +132 -0
- package/src/cli/convoy/lessons.test.ts +188 -0
- package/src/cli/convoy/lessons.ts +164 -0
- package/src/cli/convoy/lock.test.ts +181 -0
- package/src/cli/convoy/lock.ts +103 -0
- package/src/cli/convoy/merge.test.ts +6 -7
- package/src/cli/convoy/merge.ts +19 -1
- package/src/cli/convoy/partition.test.ts +423 -0
- package/src/cli/convoy/partition.ts +232 -0
- package/src/cli/convoy/pipeline.test.ts +6 -0
- package/src/cli/convoy/store.test.ts +1512 -14
- package/src/cli/convoy/store.ts +676 -30
- package/src/cli/convoy/types.ts +170 -1
- package/src/cli/destroy.test.ts +141 -0
- package/src/cli/destroy.ts +88 -0
- package/src/cli/gitignore.ts +36 -0
- package/src/cli/plan.ts +316 -0
- package/src/cli/run/adapters/claude.test.ts +234 -0
- package/src/cli/run/adapters/claude.ts +45 -5
- package/src/cli/run/adapters/copilot.test.ts +224 -0
- package/src/cli/run/adapters/copilot.ts +34 -4
- package/src/cli/run/adapters/cursor.test.ts +144 -0
- package/src/cli/run/adapters/cursor.ts +33 -2
- package/src/cli/run/adapters/opencode.test.ts +135 -0
- package/src/cli/run/adapters/opencode.ts +30 -2
- package/src/cli/run/executor.ts +1 -1
- package/src/cli/run/schema.test.ts +758 -0
- package/src/cli/run/schema.ts +300 -25
- package/src/cli/run.ts +341 -21
- package/src/cli/types.ts +86 -1
- package/src/cli/watch.ts +298 -0
- package/src/dashboard/node_modules/.vite/deps/_metadata.json +6 -6
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { parse as yamlParse, stringify as yamlStringify } from 'yaml';
|
|
3
|
+
import { parseTaskSpecText } from '../run/schema.js';
|
|
4
|
+
export class FormulaValidationError extends Error {
|
|
5
|
+
missingVariables;
|
|
6
|
+
constructor(missingVariables) {
|
|
7
|
+
super(`Missing required formula variables: ${missingVariables.join(', ')}`);
|
|
8
|
+
this.name = 'FormulaValidationError';
|
|
9
|
+
this.missingVariables = missingVariables;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
// Matches {{varname}} and {{varname | filter}} with optional whitespace
|
|
13
|
+
const PLACEHOLDER_RE = /\{\{(\s*\w+\s*(?:\|\s*\w+\s*)?)\}\}/g;
|
|
14
|
+
function toKebab(value) {
|
|
15
|
+
return value
|
|
16
|
+
.replace(/([a-z])([A-Z])/g, '$1-$2')
|
|
17
|
+
.replace(/[\s_]+/g, '-')
|
|
18
|
+
.toLowerCase();
|
|
19
|
+
}
|
|
20
|
+
function toSnake(value) {
|
|
21
|
+
return value
|
|
22
|
+
.replace(/([a-z])([A-Z])/g, '$1_$2')
|
|
23
|
+
.replace(/[\s-]+/g, '_')
|
|
24
|
+
.toLowerCase();
|
|
25
|
+
}
|
|
26
|
+
function toUpper(value) {
|
|
27
|
+
return value
|
|
28
|
+
.replace(/[\s-]+/g, '_')
|
|
29
|
+
.toUpperCase();
|
|
30
|
+
}
|
|
31
|
+
export function parseFormula(templatePath) {
|
|
32
|
+
let raw;
|
|
33
|
+
try {
|
|
34
|
+
raw = readFileSync(templatePath, 'utf8');
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
throw new Error(`Cannot read formula file: ${err.message}`);
|
|
38
|
+
}
|
|
39
|
+
let parsed;
|
|
40
|
+
try {
|
|
41
|
+
parsed = yamlParse(raw);
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
throw new Error(`Formula YAML parse error: ${err.message}`);
|
|
45
|
+
}
|
|
46
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
47
|
+
throw new Error('Formula file must be a YAML mapping');
|
|
48
|
+
}
|
|
49
|
+
const obj = parsed;
|
|
50
|
+
if (!obj.name || typeof obj.name !== 'string') {
|
|
51
|
+
throw new Error('Formula template must have a "name" field');
|
|
52
|
+
}
|
|
53
|
+
if (obj.spec === undefined || obj.spec === null) {
|
|
54
|
+
throw new Error('Formula template must have a "spec" field');
|
|
55
|
+
}
|
|
56
|
+
const variables = {};
|
|
57
|
+
if (obj.variables && typeof obj.variables === 'object' && !Array.isArray(obj.variables)) {
|
|
58
|
+
for (const [key, val] of Object.entries(obj.variables)) {
|
|
59
|
+
if (!val || typeof val !== 'object' || Array.isArray(val))
|
|
60
|
+
continue;
|
|
61
|
+
const v = val;
|
|
62
|
+
variables[key] = {
|
|
63
|
+
description: typeof v.description === 'string' ? v.description : undefined,
|
|
64
|
+
required: v.required === true,
|
|
65
|
+
default: typeof v.default === 'string' ? v.default : undefined,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
name: obj.name,
|
|
71
|
+
description: typeof obj.description === 'string' ? obj.description : undefined,
|
|
72
|
+
variables,
|
|
73
|
+
spec: obj.spec,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
export function substituteVariables(template, vars) {
|
|
77
|
+
const specYaml = yamlStringify(template.spec);
|
|
78
|
+
const missing = [];
|
|
79
|
+
const result = specYaml.replace(PLACEHOLDER_RE, (match, inner) => {
|
|
80
|
+
const parts = inner.split('|').map((p) => p.trim());
|
|
81
|
+
const varName = parts[0];
|
|
82
|
+
const filter = parts[1] ?? null;
|
|
83
|
+
let value;
|
|
84
|
+
if (varName in vars) {
|
|
85
|
+
value = vars[varName];
|
|
86
|
+
}
|
|
87
|
+
else if (varName in template.variables) {
|
|
88
|
+
const def = template.variables[varName];
|
|
89
|
+
if (def.required) {
|
|
90
|
+
missing.push(varName);
|
|
91
|
+
return match; // keep placeholder; collect all missing vars
|
|
92
|
+
}
|
|
93
|
+
value = def.default ?? '';
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
value = '';
|
|
97
|
+
}
|
|
98
|
+
if (filter === 'kebab')
|
|
99
|
+
return toKebab(value);
|
|
100
|
+
if (filter === 'snake')
|
|
101
|
+
return toSnake(value);
|
|
102
|
+
if (filter === 'upper')
|
|
103
|
+
return toUpper(value);
|
|
104
|
+
return value;
|
|
105
|
+
});
|
|
106
|
+
if (missing.length > 0) {
|
|
107
|
+
throw new FormulaValidationError(missing);
|
|
108
|
+
}
|
|
109
|
+
return parseTaskSpecText(result);
|
|
110
|
+
}
|
|
111
|
+
export function validateTemplate(template) {
|
|
112
|
+
const errors = [];
|
|
113
|
+
const VALID_IDENTIFIER = /^[a-zA-Z0-9_]+$/;
|
|
114
|
+
if (!template.name || typeof template.name !== 'string') {
|
|
115
|
+
errors.push('"name" field is required');
|
|
116
|
+
}
|
|
117
|
+
if (template.spec === undefined || template.spec === null) {
|
|
118
|
+
errors.push('"spec" field is required');
|
|
119
|
+
}
|
|
120
|
+
for (const key of Object.keys(template.variables)) {
|
|
121
|
+
if (!VALID_IDENTIFIER.test(key)) {
|
|
122
|
+
errors.push(`Variable name "${key}" is not a valid identifier (alphanumeric + underscore only)`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// Warn on undeclared {{variable}} placeholders in spec
|
|
126
|
+
if (template.spec !== undefined && template.spec !== null) {
|
|
127
|
+
try {
|
|
128
|
+
const specYaml = yamlStringify(template.spec);
|
|
129
|
+
for (const match of specYaml.matchAll(PLACEHOLDER_RE)) {
|
|
130
|
+
const varName = match[1].split('|')[0].trim();
|
|
131
|
+
if (!(varName in template.variables)) {
|
|
132
|
+
process.stderr.write(`Warning: template contains undeclared placeholder "{{${varName}}}"\n`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
// If stringify fails, skip placeholder checking
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return { valid: errors.length === 0, errors };
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=formula.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formula.js","sourceRoot":"","sources":["../../../src/cli/convoy/formula.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formula.test.d.ts","sourceRoot":"","sources":["../../../src/cli/convoy/formula.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
import { mkdtempSync, rmSync, realpathSync, writeFileSync, } from 'node:fs';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
5
|
+
import { parseFormula, substituteVariables, validateTemplate, FormulaValidationError, } from './formula.js';
|
|
6
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
7
|
+
function makeBase() {
|
|
8
|
+
return realpathSync(mkdtempSync(join(tmpdir(), 'formula-test-')));
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Minimal valid TaskSpec content for use in formula `spec:` sections.
|
|
12
|
+
* After substitution, this must pass parseTaskSpecText validation.
|
|
13
|
+
*/
|
|
14
|
+
const MINIMAL_SPEC = {
|
|
15
|
+
name: 'Test convoy',
|
|
16
|
+
version: 1,
|
|
17
|
+
tasks: [{ id: 'task-1', prompt: 'Do the thing' }],
|
|
18
|
+
};
|
|
19
|
+
function writeFormula(dir, content) {
|
|
20
|
+
const path = join(dir, 'formula.convoy.yml');
|
|
21
|
+
writeFileSync(path, content);
|
|
22
|
+
return path;
|
|
23
|
+
}
|
|
24
|
+
let tmpDir;
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
tmpDir = makeBase();
|
|
27
|
+
});
|
|
28
|
+
afterEach(() => {
|
|
29
|
+
rmSync(tmpDir, { recursive: true, force: true });
|
|
30
|
+
});
|
|
31
|
+
// ── parseFormula ──────────────────────────────────────────────────────────────
|
|
32
|
+
describe('parseFormula', () => {
|
|
33
|
+
it('reads a formula file and returns a FormulaTemplate', () => {
|
|
34
|
+
const path = writeFormula(tmpDir, [
|
|
35
|
+
'name: My Formula',
|
|
36
|
+
'description: A test formula',
|
|
37
|
+
'variables:',
|
|
38
|
+
' app_name:',
|
|
39
|
+
' description: App name',
|
|
40
|
+
' required: true',
|
|
41
|
+
'spec:',
|
|
42
|
+
' name: Test convoy',
|
|
43
|
+
' version: 1',
|
|
44
|
+
' tasks:',
|
|
45
|
+
' - id: task-1',
|
|
46
|
+
' prompt: Do the thing',
|
|
47
|
+
].join('\n'));
|
|
48
|
+
const template = parseFormula(path);
|
|
49
|
+
expect(template.name).toBe('My Formula');
|
|
50
|
+
expect(template.description).toBe('A test formula');
|
|
51
|
+
expect(template.variables['app_name']).toMatchObject({ required: true });
|
|
52
|
+
expect(template.spec).toBeTruthy();
|
|
53
|
+
});
|
|
54
|
+
it('throws when file does not exist', () => {
|
|
55
|
+
expect(() => parseFormula(join(tmpDir, 'missing.yml'))).toThrow(/Cannot read formula file/);
|
|
56
|
+
});
|
|
57
|
+
it('throws on invalid YAML', () => {
|
|
58
|
+
const path = writeFormula(tmpDir, ': {{{');
|
|
59
|
+
expect(() => parseFormula(path)).toThrow(/Formula YAML parse error/);
|
|
60
|
+
});
|
|
61
|
+
it('throws when name field is missing', () => {
|
|
62
|
+
const path = writeFormula(tmpDir, 'spec:\n name: Test convoy\n version: 1\n tasks:\n - id: t1\n prompt: p\n');
|
|
63
|
+
expect(() => parseFormula(path)).toThrow(/must have a "name" field/);
|
|
64
|
+
});
|
|
65
|
+
it('throws when spec field is missing', () => {
|
|
66
|
+
const path = writeFormula(tmpDir, 'name: My Formula\n');
|
|
67
|
+
expect(() => parseFormula(path)).toThrow(/must have a "spec" field/);
|
|
68
|
+
});
|
|
69
|
+
it('returns empty variables when no variables section', () => {
|
|
70
|
+
const path = writeFormula(tmpDir, 'name: Simple\nspec:\n name: Test\n version: 1\n tasks:\n - id: t1\n prompt: p\n');
|
|
71
|
+
const template = parseFormula(path);
|
|
72
|
+
expect(template.variables).toEqual({});
|
|
73
|
+
});
|
|
74
|
+
it('parses optional flag and default value', () => {
|
|
75
|
+
const path = writeFormula(tmpDir, [
|
|
76
|
+
'name: With defaults',
|
|
77
|
+
'variables:',
|
|
78
|
+
' env:',
|
|
79
|
+
' required: false',
|
|
80
|
+
' default: staging',
|
|
81
|
+
'spec:',
|
|
82
|
+
' name: Test',
|
|
83
|
+
' version: 1',
|
|
84
|
+
' tasks:',
|
|
85
|
+
' - id: t1',
|
|
86
|
+
' prompt: p',
|
|
87
|
+
].join('\n'));
|
|
88
|
+
const template = parseFormula(path);
|
|
89
|
+
expect(template.variables['env']).toMatchObject({ required: false, default: 'staging' });
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
// ── substituteVariables ───────────────────────────────────────────────────────
|
|
93
|
+
describe('substituteVariables', () => {
|
|
94
|
+
function makeTemplate(overrides = {}) {
|
|
95
|
+
return {
|
|
96
|
+
name: 'Test Formula',
|
|
97
|
+
variables: {},
|
|
98
|
+
spec: MINIMAL_SPEC,
|
|
99
|
+
...overrides,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
it('substitutes a plain variable', () => {
|
|
103
|
+
const template = makeTemplate({
|
|
104
|
+
variables: { feature: { required: true } },
|
|
105
|
+
spec: {
|
|
106
|
+
name: '{{feature}}',
|
|
107
|
+
version: 1,
|
|
108
|
+
tasks: [{ id: 'task-1', prompt: 'Build {{feature}}' }],
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
const result = substituteVariables(template, { feature: 'auth' });
|
|
112
|
+
expect(result.name).toBe('auth');
|
|
113
|
+
});
|
|
114
|
+
it('applies kebab filter', () => {
|
|
115
|
+
const template = makeTemplate({
|
|
116
|
+
variables: { feature: { required: true } },
|
|
117
|
+
spec: {
|
|
118
|
+
name: 'test',
|
|
119
|
+
version: 1,
|
|
120
|
+
tasks: [{ id: 'task-1', prompt: '{{feature | kebab}}' }],
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
const result = substituteVariables(template, { feature: 'MyFeature Name' });
|
|
124
|
+
expect(result.tasks[0].prompt).toContain('my-feature-name');
|
|
125
|
+
});
|
|
126
|
+
it('applies snake filter', () => {
|
|
127
|
+
const template = makeTemplate({
|
|
128
|
+
variables: { feature: { required: true } },
|
|
129
|
+
spec: {
|
|
130
|
+
name: 'test',
|
|
131
|
+
version: 1,
|
|
132
|
+
tasks: [{ id: 'task-1', prompt: '{{feature | snake}}' }],
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
const result = substituteVariables(template, { feature: 'MyFeature Name' });
|
|
136
|
+
expect(result.tasks[0].prompt).toContain('my_feature_name');
|
|
137
|
+
});
|
|
138
|
+
it('applies upper filter', () => {
|
|
139
|
+
const template = makeTemplate({
|
|
140
|
+
variables: { feature: { required: true } },
|
|
141
|
+
spec: {
|
|
142
|
+
name: 'test',
|
|
143
|
+
version: 1,
|
|
144
|
+
tasks: [{ id: 'task-1', prompt: '{{feature | upper}}' }],
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
const result = substituteVariables(template, { feature: 'my feature' });
|
|
148
|
+
expect(result.tasks[0].prompt).toContain('MY_FEATURE');
|
|
149
|
+
});
|
|
150
|
+
it('throws FormulaValidationError for missing required variable', () => {
|
|
151
|
+
const template = makeTemplate({
|
|
152
|
+
variables: { required_var: { required: true } },
|
|
153
|
+
spec: {
|
|
154
|
+
name: 'test',
|
|
155
|
+
version: 1,
|
|
156
|
+
tasks: [{ id: 'task-1', prompt: '{{required_var}}' }],
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
expect(() => substituteVariables(template, {})).toThrow(FormulaValidationError);
|
|
160
|
+
});
|
|
161
|
+
it('includes missing variable name in FormulaValidationError', () => {
|
|
162
|
+
const template = makeTemplate({
|
|
163
|
+
variables: { missing_var: { required: true } },
|
|
164
|
+
spec: {
|
|
165
|
+
name: 'test',
|
|
166
|
+
version: 1,
|
|
167
|
+
tasks: [{ id: 'task-1', prompt: '{{missing_var}}' }],
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
let err;
|
|
171
|
+
try {
|
|
172
|
+
substituteVariables(template, {});
|
|
173
|
+
}
|
|
174
|
+
catch (e) {
|
|
175
|
+
if (e instanceof FormulaValidationError)
|
|
176
|
+
err = e;
|
|
177
|
+
}
|
|
178
|
+
expect(err).toBeDefined();
|
|
179
|
+
expect(err.missingVariables).toContain('missing_var');
|
|
180
|
+
});
|
|
181
|
+
it('collects all missing required variables', () => {
|
|
182
|
+
const template = makeTemplate({
|
|
183
|
+
variables: {
|
|
184
|
+
var_a: { required: true },
|
|
185
|
+
var_b: { required: true },
|
|
186
|
+
},
|
|
187
|
+
spec: {
|
|
188
|
+
name: 'test',
|
|
189
|
+
version: 1,
|
|
190
|
+
tasks: [{ id: 'task-1', prompt: '{{var_a}} and {{var_b}}' }],
|
|
191
|
+
},
|
|
192
|
+
});
|
|
193
|
+
let err;
|
|
194
|
+
try {
|
|
195
|
+
substituteVariables(template, {});
|
|
196
|
+
}
|
|
197
|
+
catch (e) {
|
|
198
|
+
if (e instanceof FormulaValidationError)
|
|
199
|
+
err = e;
|
|
200
|
+
}
|
|
201
|
+
expect(err.missingVariables).toEqual(expect.arrayContaining(['var_a', 'var_b']));
|
|
202
|
+
});
|
|
203
|
+
it('uses default value for optional variable without provided value', () => {
|
|
204
|
+
const template = makeTemplate({
|
|
205
|
+
variables: { env: { required: false, default: 'production' } },
|
|
206
|
+
spec: {
|
|
207
|
+
name: 'test',
|
|
208
|
+
version: 1,
|
|
209
|
+
tasks: [{ id: 'task-1', prompt: 'Deploy to {{env}}' }],
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
const result = substituteVariables(template, {});
|
|
213
|
+
expect(result.tasks[0].prompt).toContain('production');
|
|
214
|
+
});
|
|
215
|
+
it('uses empty string for optional variable with no default', () => {
|
|
216
|
+
const template = makeTemplate({
|
|
217
|
+
variables: { opt: { required: false } },
|
|
218
|
+
spec: {
|
|
219
|
+
name: 'test-task',
|
|
220
|
+
version: 1,
|
|
221
|
+
tasks: [{ id: 'task-1', prompt: 'prefix-{{opt}}-suffix' }],
|
|
222
|
+
},
|
|
223
|
+
});
|
|
224
|
+
const result = substituteVariables(template, {});
|
|
225
|
+
expect(result.tasks[0].prompt).toContain('prefix--suffix');
|
|
226
|
+
});
|
|
227
|
+
it('returns a valid TaskSpec', () => {
|
|
228
|
+
const template = makeTemplate({
|
|
229
|
+
variables: { app: { required: true } },
|
|
230
|
+
spec: {
|
|
231
|
+
name: 'Deploy {{app}}',
|
|
232
|
+
version: 1,
|
|
233
|
+
tasks: [{ id: 'task-1', prompt: 'Deploy {{app}}' }],
|
|
234
|
+
},
|
|
235
|
+
});
|
|
236
|
+
const result = substituteVariables(template, { app: 'myapp' });
|
|
237
|
+
expect(result).toMatchObject({ name: expect.any(String) });
|
|
238
|
+
expect(Array.isArray(result.tasks)).toBe(true);
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
// ── validateTemplate ──────────────────────────────────────────────────────────
|
|
242
|
+
describe('validateTemplate', () => {
|
|
243
|
+
function makeTemplate(overrides = {}) {
|
|
244
|
+
return {
|
|
245
|
+
name: 'Valid Template',
|
|
246
|
+
variables: {},
|
|
247
|
+
spec: MINIMAL_SPEC,
|
|
248
|
+
...overrides,
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
it('returns valid for a correct template', () => {
|
|
252
|
+
const result = validateTemplate(makeTemplate());
|
|
253
|
+
expect(result.valid).toBe(true);
|
|
254
|
+
expect(result.errors).toHaveLength(0);
|
|
255
|
+
});
|
|
256
|
+
it('returns error when name is missing', () => {
|
|
257
|
+
const template = makeTemplate({ name: '' });
|
|
258
|
+
const result = validateTemplate(template);
|
|
259
|
+
expect(result.valid).toBe(false);
|
|
260
|
+
expect(result.errors.some(e => e.includes('"name"'))).toBe(true);
|
|
261
|
+
});
|
|
262
|
+
it('returns error when spec is null', () => {
|
|
263
|
+
const template = makeTemplate({ spec: null });
|
|
264
|
+
const result = validateTemplate(template);
|
|
265
|
+
expect(result.valid).toBe(false);
|
|
266
|
+
expect(result.errors.some(e => e.includes('"spec"'))).toBe(true);
|
|
267
|
+
});
|
|
268
|
+
it('returns error for invalid variable identifier', () => {
|
|
269
|
+
const template = makeTemplate({
|
|
270
|
+
variables: { 'invalid-name': { required: false } },
|
|
271
|
+
});
|
|
272
|
+
const result = validateTemplate(template);
|
|
273
|
+
expect(result.valid).toBe(false);
|
|
274
|
+
expect(result.errors.some(e => e.includes('invalid-name'))).toBe(true);
|
|
275
|
+
});
|
|
276
|
+
it('allows valid variable identifiers', () => {
|
|
277
|
+
const template = makeTemplate({
|
|
278
|
+
variables: {
|
|
279
|
+
app_name: { required: true },
|
|
280
|
+
env2: { required: false },
|
|
281
|
+
MY_VAR: { required: false },
|
|
282
|
+
},
|
|
283
|
+
});
|
|
284
|
+
const result = validateTemplate(template);
|
|
285
|
+
expect(result.valid).toBe(true);
|
|
286
|
+
});
|
|
287
|
+
it('writes a warning for undeclared placeholder', () => {
|
|
288
|
+
const stderrSpy = vi.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
289
|
+
const template = makeTemplate({
|
|
290
|
+
variables: {},
|
|
291
|
+
spec: {
|
|
292
|
+
name: 'test',
|
|
293
|
+
version: 1,
|
|
294
|
+
tasks: [{ id: 'task-1', prompt: 'Do {{undeclared_var}}' }],
|
|
295
|
+
},
|
|
296
|
+
});
|
|
297
|
+
validateTemplate(template);
|
|
298
|
+
expect(stderrSpy).toHaveBeenCalledWith(expect.stringContaining('undeclared_var'));
|
|
299
|
+
stderrSpy.mockRestore();
|
|
300
|
+
});
|
|
301
|
+
it('does not warn for declared placeholders', () => {
|
|
302
|
+
const stderrSpy = vi.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
303
|
+
const template = makeTemplate({
|
|
304
|
+
variables: { declared_var: { required: true } },
|
|
305
|
+
spec: {
|
|
306
|
+
name: 'test',
|
|
307
|
+
version: 1,
|
|
308
|
+
tasks: [{ id: 'task-1', prompt: 'Do {{declared_var}}' }],
|
|
309
|
+
},
|
|
310
|
+
});
|
|
311
|
+
validateTemplate(template);
|
|
312
|
+
expect(stderrSpy).not.toHaveBeenCalled();
|
|
313
|
+
stderrSpy.mockRestore();
|
|
314
|
+
});
|
|
315
|
+
});
|
|
316
|
+
// ── Filter helpers ────────────────────────────────────────────────────────────
|
|
317
|
+
describe('filter conversions via substituteVariables', () => {
|
|
318
|
+
function makeFilterTemplate(filter) {
|
|
319
|
+
return {
|
|
320
|
+
name: 'Filter Test',
|
|
321
|
+
variables: { val: { required: true } },
|
|
322
|
+
spec: {
|
|
323
|
+
name: `{{val | ${filter}}}`,
|
|
324
|
+
version: 1,
|
|
325
|
+
tasks: [{ id: 'task-1', prompt: 'x' }],
|
|
326
|
+
},
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
it('kebab: converts spaces and underscores to hyphens, lowercases', () => {
|
|
330
|
+
const result = substituteVariables(makeFilterTemplate('kebab'), { val: 'Hello World_Test' });
|
|
331
|
+
expect(result.name).toBe('hello-world-test');
|
|
332
|
+
});
|
|
333
|
+
it('snake: converts spaces and hyphens to underscores, lowercases', () => {
|
|
334
|
+
const result = substituteVariables(makeFilterTemplate('snake'), { val: 'Hello World-Test' });
|
|
335
|
+
expect(result.name).toBe('hello_world_test');
|
|
336
|
+
});
|
|
337
|
+
it('upper: converts to uppercase with underscores', () => {
|
|
338
|
+
const result = substituteVariables(makeFilterTemplate('upper'), { val: 'hello world' });
|
|
339
|
+
expect(result.name).toBe('HELLO_WORLD');
|
|
340
|
+
});
|
|
341
|
+
});
|
|
342
|
+
//# sourceMappingURL=formula.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formula.test.js","sourceRoot":"","sources":["../../../src/cli/convoy/formula.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import type { BrowserTestConfig, MCPServerConfig } from './types.js';
|
|
2
|
+
export interface SecretScanResult {
|
|
3
|
+
clean: boolean;
|
|
4
|
+
findings: Array<{
|
|
5
|
+
pattern: string;
|
|
6
|
+
file: string;
|
|
7
|
+
line: number;
|
|
8
|
+
snippet: string;
|
|
9
|
+
}>;
|
|
10
|
+
}
|
|
11
|
+
/** The config path used for the allowlist. Override for testing. */
|
|
12
|
+
export declare let _allowlistConfigPath: string;
|
|
13
|
+
/** Reset the allowlist cache (for testing). */
|
|
14
|
+
export declare function _resetAllowlistCache(): void;
|
|
15
|
+
/** Override the allowlist config path and reset cache (for testing). */
|
|
16
|
+
export declare function _setAllowlistConfigPath(path: string): void;
|
|
17
|
+
/**
|
|
18
|
+
* Scan text content line-by-line for secrets using the default pattern set.
|
|
19
|
+
* Allowlist entries in `.opencastle/secret-scan-config.yml` suppress false positives.
|
|
20
|
+
*/
|
|
21
|
+
export declare function scanForSecrets(content: string, filePath?: string): SecretScanResult;
|
|
22
|
+
/**
|
|
23
|
+
* Guard a write action with a secret scan.
|
|
24
|
+
* Calls `writeAction` if content is clean; calls `onBlock` with findings otherwise.
|
|
25
|
+
*/
|
|
26
|
+
export declare function withSecretScan(content: string, writeAction: () => void, onBlock: (findings: SecretScanResult['findings']) => void): void;
|
|
27
|
+
export interface GateCommandResult {
|
|
28
|
+
stdout: string;
|
|
29
|
+
stderr: string;
|
|
30
|
+
exitCode: number;
|
|
31
|
+
timedOut: boolean;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Run a shell command with SIGTERM → SIGKILL timeout escalation.
|
|
35
|
+
* On timeout: sends SIGTERM immediately, then SIGKILL after 5 s if still running.
|
|
36
|
+
*/
|
|
37
|
+
export declare function runGateCommand(command: string, args: string[], cwd: string, timeoutMs?: number): Promise<GateCommandResult>;
|
|
38
|
+
/** Scan a list of changed files in the worktree for secrets. */
|
|
39
|
+
export declare function runSecretScanGate(changedFiles: string[], worktreePath: string): Promise<{
|
|
40
|
+
passed: boolean;
|
|
41
|
+
output: string;
|
|
42
|
+
}>;
|
|
43
|
+
/**
|
|
44
|
+
* Analyze a git diff for blast radius.
|
|
45
|
+
* WARN at 200+ lines OR 5+ files; BLOCK at 500+ lines OR 10+ files.
|
|
46
|
+
*/
|
|
47
|
+
export declare function runBlastRadiusGate(diff: string): {
|
|
48
|
+
passed: boolean;
|
|
49
|
+
output: string;
|
|
50
|
+
level: 'ok' | 'warn' | 'block';
|
|
51
|
+
};
|
|
52
|
+
/** Run npm audit in the worktree to detect high/critical vulnerabilities. */
|
|
53
|
+
export declare function runDependencyAuditGate(worktreePath: string): Promise<{
|
|
54
|
+
passed: boolean;
|
|
55
|
+
output: string;
|
|
56
|
+
}>;
|
|
57
|
+
/** Run the test suite in the worktree directory. */
|
|
58
|
+
export declare function runRegressionTestGate(worktreePath: string, testCommand?: string): Promise<{
|
|
59
|
+
passed: boolean;
|
|
60
|
+
output: string;
|
|
61
|
+
}>;
|
|
62
|
+
/**
|
|
63
|
+
* Compare two PNG buffers pixel-by-pixel and return the fraction (0–1) of differing pixels.
|
|
64
|
+
* Returns 1.0 if dimensions differ, buffers are invalid, or an error occurs.
|
|
65
|
+
*/
|
|
66
|
+
export declare function pixelDiffPercentage(baselineBuffer: Buffer, currentBuffer: Buffer): number;
|
|
67
|
+
export interface VisualDiffContext {
|
|
68
|
+
screenshotBuffer: Buffer;
|
|
69
|
+
baselinePath: string;
|
|
70
|
+
threshold: number;
|
|
71
|
+
}
|
|
72
|
+
export interface VisualDiffResult {
|
|
73
|
+
passed: boolean;
|
|
74
|
+
diffPercent: number;
|
|
75
|
+
output: string;
|
|
76
|
+
}
|
|
77
|
+
/** Compare a screenshot buffer against a saved baseline PNG. */
|
|
78
|
+
export declare function computeVisualDiff(context: VisualDiffContext): Promise<VisualDiffResult>;
|
|
79
|
+
/**
|
|
80
|
+
* Persist a screenshot buffer as a PNG baseline file.
|
|
81
|
+
* Secret-scans the buffer (as base64) before writing.
|
|
82
|
+
*/
|
|
83
|
+
export declare function captureAndPersistBaseline(screenshotBuffer: Buffer, slug: string, basePath?: string): {
|
|
84
|
+
persisted: boolean;
|
|
85
|
+
reason?: string;
|
|
86
|
+
};
|
|
87
|
+
export type A11ySeverity = 'critical' | 'serious' | 'moderate' | 'minor';
|
|
88
|
+
export interface A11yFinding {
|
|
89
|
+
id: string;
|
|
90
|
+
impact: A11ySeverity;
|
|
91
|
+
description: string;
|
|
92
|
+
nodes: number;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Map a list of a11y findings against a severity threshold.
|
|
96
|
+
* Returns passed:false if any finding meets or exceeds the threshold.
|
|
97
|
+
*/
|
|
98
|
+
export declare function mapA11ySeverity(findings: A11yFinding[], threshold: A11ySeverity): {
|
|
99
|
+
passed: boolean;
|
|
100
|
+
output: string;
|
|
101
|
+
findings: A11yFinding[];
|
|
102
|
+
};
|
|
103
|
+
export interface A11yAuditContext {
|
|
104
|
+
mcpServers: MCPServerConfig[];
|
|
105
|
+
url: string;
|
|
106
|
+
severityThreshold: A11ySeverity;
|
|
107
|
+
}
|
|
108
|
+
/** Run an a11y audit via a browser-capable MCP server. */
|
|
109
|
+
export declare function runA11yAudit(context: A11yAuditContext): Promise<{
|
|
110
|
+
passed: boolean;
|
|
111
|
+
output: string;
|
|
112
|
+
findings: A11yFinding[];
|
|
113
|
+
}>;
|
|
114
|
+
export interface BrowserTestContext {
|
|
115
|
+
mcpServers: MCPServerConfig[];
|
|
116
|
+
taskConfig: BrowserTestConfig;
|
|
117
|
+
worktreePath: string;
|
|
118
|
+
approvalTimeout?: number;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Run browser-based tests against localhost URLs using an MCP browser server.
|
|
122
|
+
* All URLs are validated to be local addresses (SSRF prevention).
|
|
123
|
+
*/
|
|
124
|
+
export declare function browserTestGate(context: BrowserTestContext): Promise<{
|
|
125
|
+
passed: boolean;
|
|
126
|
+
output: string;
|
|
127
|
+
}>;
|
|
128
|
+
//# sourceMappingURL=gates.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gates.d.ts","sourceRoot":"","sources":["../../../src/cli/convoy/gates.ts"],"names":[],"mappings":"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"}
|