@intentius/chant-lexicon-fountain 0.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +63 -0
- package/dist/codegen/docs-cli.d.ts +3 -0
- package/dist/codegen/docs-cli.d.ts.map +1 -0
- package/dist/codegen/docs.d.ts +16 -0
- package/dist/codegen/docs.d.ts.map +1 -0
- package/dist/codegen/generate-cli.d.ts +3 -0
- package/dist/codegen/generate-cli.d.ts.map +1 -0
- package/dist/codegen/generate-lexicon.d.ts +24 -0
- package/dist/codegen/generate-lexicon.d.ts.map +1 -0
- package/dist/codegen/generate-typescript.d.ts +11 -0
- package/dist/codegen/generate-typescript.d.ts.map +1 -0
- package/dist/codegen/generate.d.ts +15 -0
- package/dist/codegen/generate.d.ts.map +1 -0
- package/dist/codegen/naming.d.ts +14 -0
- package/dist/codegen/naming.d.ts.map +1 -0
- package/dist/codegen/package.d.ts +17 -0
- package/dist/codegen/package.d.ts.map +1 -0
- package/dist/composites/concierge-stack.d.ts +44 -0
- package/dist/composites/concierge-stack.d.ts.map +1 -0
- package/dist/coverage.d.ts +56 -0
- package/dist/coverage.d.ts.map +1 -0
- package/dist/describe-resources.d.ts +45 -0
- package/dist/describe-resources.d.ts.map +1 -0
- package/dist/detect.d.ts +10 -0
- package/dist/detect.d.ts.map +1 -0
- package/dist/entity-props.d.ts +11 -0
- package/dist/entity-props.d.ts.map +1 -0
- package/dist/export-resources.d.ts +29 -0
- package/dist/export-resources.d.ts.map +1 -0
- package/dist/generated/index.d.ts +5 -0
- package/dist/generated/index.d.ts.map +1 -0
- package/dist/generated/runtime.d.ts +5 -0
- package/dist/generated/runtime.d.ts.map +1 -0
- package/dist/import/generator.d.ts +15 -0
- package/dist/import/generator.d.ts.map +1 -0
- package/dist/import/parser.d.ts +19 -0
- package/dist/import/parser.d.ts.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/init-templates.d.ts +13 -0
- package/dist/init-templates.d.ts.map +1 -0
- package/dist/integrity.json +21 -0
- package/dist/lint/audit-catalog.d.ts +15 -0
- package/dist/lint/audit-catalog.d.ts.map +1 -0
- package/dist/lint/post-synth/ftn010-networking-explicit.d.ts +11 -0
- package/dist/lint/post-synth/ftn010-networking-explicit.d.ts.map +1 -0
- package/dist/lint/post-synth/ftn011-no-unrestricted-networking.d.ts +11 -0
- package/dist/lint/post-synth/ftn011-no-unrestricted-networking.d.ts.map +1 -0
- package/dist/lint/post-synth/ftn012-no-cloud-credential-env.d.ts +3 -0
- package/dist/lint/post-synth/ftn012-no-cloud-credential-env.d.ts.map +1 -0
- package/dist/lint/post-synth/ftn013-substitution-resolvable.d.ts +3 -0
- package/dist/lint/post-synth/ftn013-substitution-resolvable.d.ts.map +1 -0
- package/dist/lint/post-synth/ftn014-vault-shadowing.d.ts +3 -0
- package/dist/lint/post-synth/ftn014-vault-shadowing.d.ts.map +1 -0
- package/dist/lint/post-synth/ftn015-mcp-secret-env-substitution.d.ts +3 -0
- package/dist/lint/post-synth/ftn015-mcp-secret-env-substitution.d.ts.map +1 -0
- package/dist/lint/post-synth/ftn016-runtime-model-valid.d.ts +3 -0
- package/dist/lint/post-synth/ftn016-runtime-model-valid.d.ts.map +1 -0
- package/dist/lint/post-synth/ftn017-unique-resource-names.d.ts +11 -0
- package/dist/lint/post-synth/ftn017-unique-resource-names.d.ts.map +1 -0
- package/dist/lint/post-synth/index.d.ts +3 -0
- package/dist/lint/post-synth/index.d.ts.map +1 -0
- package/dist/lint/rules/ftn001-no-secret-literals.d.ts +3 -0
- package/dist/lint/rules/ftn001-no-secret-literals.d.ts.map +1 -0
- package/dist/lint/rules/index.d.ts +5 -0
- package/dist/lint/rules/index.d.ts.map +1 -0
- package/dist/lsp/completions.d.ts +7 -0
- package/dist/lsp/completions.d.ts.map +1 -0
- package/dist/lsp/hover.d.ts +4 -0
- package/dist/lsp/hover.d.ts.map +1 -0
- package/dist/manifest.json +6 -0
- package/dist/meta.json +69 -0
- package/dist/op/activities/fountain-apply.d.ts +76 -0
- package/dist/op/activities/fountain-apply.d.ts.map +1 -0
- package/dist/op/activities/fountain-run.d.ts +36 -0
- package/dist/op/activities/fountain-run.d.ts.map +1 -0
- package/dist/op/activities/index.d.ts +11 -0
- package/dist/op/activities/index.d.ts.map +1 -0
- package/dist/package-cli.d.ts +3 -0
- package/dist/package-cli.d.ts.map +1 -0
- package/dist/plugin.d.ts +8 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/reference-catalog.d.ts +16 -0
- package/dist/reference-catalog.d.ts.map +1 -0
- package/dist/rules/ftn001-no-secret-literals.ts +71 -0
- package/dist/rules/ftn010-networking-explicit.ts +37 -0
- package/dist/rules/ftn011-no-unrestricted-networking.ts +36 -0
- package/dist/rules/ftn012-no-cloud-credential-env.ts +58 -0
- package/dist/rules/ftn013-substitution-resolvable.ts +93 -0
- package/dist/rules/ftn014-vault-shadowing.ts +61 -0
- package/dist/rules/ftn015-mcp-secret-env-substitution.ts +49 -0
- package/dist/rules/ftn016-runtime-model-valid.ts +50 -0
- package/dist/rules/ftn017-unique-resource-names.ts +43 -0
- package/dist/serializer.d.ts +3 -0
- package/dist/serializer.d.ts.map +1 -0
- package/dist/skills/chant-fountain-locked-sandboxes.md +34 -0
- package/dist/skills/chant-fountain-secrets.md +29 -0
- package/dist/skills/chant-fountain.md +45 -0
- package/dist/spec/fetch.d.ts +28 -0
- package/dist/spec/fetch.d.ts.map +1 -0
- package/dist/spec/parse.d.ts +58 -0
- package/dist/spec/parse.d.ts.map +1 -0
- package/dist/types/index.d.ts +60 -0
- package/dist/validate-cli.d.ts +3 -0
- package/dist/validate-cli.d.ts.map +1 -0
- package/dist/validate.d.ts +15 -0
- package/dist/validate.d.ts.map +1 -0
- package/package.json +61 -0
- package/src/codegen/docs-cli.ts +7 -0
- package/src/codegen/docs.ts +278 -0
- package/src/codegen/generate-cli.ts +8 -0
- package/src/codegen/generate-lexicon.ts +58 -0
- package/src/codegen/generate-typescript.ts +69 -0
- package/src/codegen/generate.ts +109 -0
- package/src/codegen/naming.ts +45 -0
- package/src/codegen/package.ts +32 -0
- package/src/composites/composites.test.ts +35 -0
- package/src/composites/concierge-stack.ts +71 -0
- package/src/coverage.test.ts +63 -0
- package/src/coverage.ts +178 -0
- package/src/describe-resources.test.ts +203 -0
- package/src/describe-resources.ts +192 -0
- package/src/detect.ts +37 -0
- package/src/entity-props.ts +22 -0
- package/src/export-resources.ts +103 -0
- package/src/generated/.gitkeep +0 -0
- package/src/generated/index.d.ts +60 -0
- package/src/generated/index.ts +10 -0
- package/src/generated/lexicon-fountain.json +69 -0
- package/src/generated/runtime.ts +4 -0
- package/src/import/generator.ts +112 -0
- package/src/import/parser.ts +83 -0
- package/src/import/roundtrip.test.ts +113 -0
- package/src/index.ts +17 -0
- package/src/init-templates.ts +60 -0
- package/src/lint/audit-catalog.ts +107 -0
- package/src/lint/post-synth/ftn010-networking-explicit.ts +37 -0
- package/src/lint/post-synth/ftn011-no-unrestricted-networking.ts +36 -0
- package/src/lint/post-synth/ftn012-no-cloud-credential-env.ts +58 -0
- package/src/lint/post-synth/ftn013-substitution-resolvable.ts +93 -0
- package/src/lint/post-synth/ftn014-vault-shadowing.ts +61 -0
- package/src/lint/post-synth/ftn015-mcp-secret-env-substitution.ts +49 -0
- package/src/lint/post-synth/ftn016-runtime-model-valid.ts +50 -0
- package/src/lint/post-synth/ftn017-unique-resource-names.ts +43 -0
- package/src/lint/post-synth/index.ts +21 -0
- package/src/lint/post-synth/post-synth.test.ts +133 -0
- package/src/lint/rules/ftn001-no-secret-literals.ts +71 -0
- package/src/lint/rules/index.ts +7 -0
- package/src/lint/rules/rules.test.ts +126 -0
- package/src/lsp/completions.test.ts +51 -0
- package/src/lsp/completions.ts +21 -0
- package/src/lsp/hover.test.ts +47 -0
- package/src/lsp/hover.ts +70 -0
- package/src/op/activities/fountain-apply.test.ts +208 -0
- package/src/op/activities/fountain-apply.ts +249 -0
- package/src/op/activities/fountain-run.ts +95 -0
- package/src/op/activities/index.ts +28 -0
- package/src/package-cli.ts +26 -0
- package/src/plugin.test.ts +73 -0
- package/src/plugin.ts +170 -0
- package/src/reference-catalog.ts +27 -0
- package/src/serializer.test.ts +109 -0
- package/src/serializer.ts +143 -0
- package/src/skills/chant-fountain-locked-sandboxes.md +34 -0
- package/src/skills/chant-fountain-secrets.md +29 -0
- package/src/skills/chant-fountain.md +45 -0
- package/src/spec/fetch.ts +53 -0
- package/src/spec/fountain-openapi.snapshot.json +1 -0
- package/src/spec/parse.ts +309 -0
- package/src/validate-cli.ts +13 -0
- package/src/validate.ts +34 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
2
|
+
import { propsOf } from "../../entity-props";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* FTN013: `${VAR}` references in an Agent's config should resolve against
|
|
6
|
+
* its Environment's declared keys.
|
|
7
|
+
*
|
|
8
|
+
* Fountain validates substitution fail-complete at *spawn* time; this
|
|
9
|
+
* moves the same check to build time. Warning severity: a vault attached
|
|
10
|
+
* at conversation create can legitimately supply keys the environment
|
|
11
|
+
* does not declare (vault values win on collision), so an unresolved
|
|
12
|
+
* reference is suspicious, not certainly wrong.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const VAR_RE = /\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g;
|
|
16
|
+
|
|
17
|
+
function collectVars(node: unknown, acc: Set<string>): void {
|
|
18
|
+
if (typeof node === "string") {
|
|
19
|
+
for (const m of node.matchAll(VAR_RE)) acc.add(m[1]);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
if (Array.isArray(node)) {
|
|
23
|
+
for (const item of node) collectVars(item, acc);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (node && typeof node === "object") {
|
|
27
|
+
for (const v of Object.values(node)) collectVars(v, acc);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function declaredKeys(env: Record<string, unknown>): Set<string> {
|
|
32
|
+
const keys = new Set<string>();
|
|
33
|
+
const envVars = env.env_vars;
|
|
34
|
+
if (envVars && typeof envVars === "object") {
|
|
35
|
+
for (const k of Object.keys(envVars)) keys.add(k);
|
|
36
|
+
}
|
|
37
|
+
const secrets = env.secrets;
|
|
38
|
+
if (Array.isArray(secrets)) {
|
|
39
|
+
for (const s of secrets) {
|
|
40
|
+
const key = (s as { key?: unknown })?.key;
|
|
41
|
+
if (typeof key === "string") keys.add(key);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return keys;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const substitutionResolvableCheck: PostSynthCheck = {
|
|
48
|
+
id: "FTN013",
|
|
49
|
+
description: "Agent ${VAR} references should resolve against the environment's declared keys",
|
|
50
|
+
|
|
51
|
+
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
52
|
+
const diagnostics: PostSynthDiagnostic[] = [];
|
|
53
|
+
|
|
54
|
+
for (const [name, entity] of ctx.entities) {
|
|
55
|
+
if (entity.entityType !== "Fountain::V1::Agent") continue;
|
|
56
|
+
const agent = propsOf(entity);
|
|
57
|
+
|
|
58
|
+
// Resolve the referenced environment entity when it is declared here.
|
|
59
|
+
const envRef = agent.environment;
|
|
60
|
+
let envEntity: Record<string, unknown> | undefined;
|
|
61
|
+
if (envRef && typeof envRef === "object") {
|
|
62
|
+
envEntity = propsOf(envRef);
|
|
63
|
+
} else if (typeof envRef === "string") {
|
|
64
|
+
const byName = ctx.entities.get(envRef);
|
|
65
|
+
if (byName?.entityType === "Fountain::V1::Environment") {
|
|
66
|
+
envEntity = propsOf(byName);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (!envEntity) continue; // external environment — nothing to check against
|
|
70
|
+
|
|
71
|
+
const keys = declaredKeys(envEntity);
|
|
72
|
+
const used = new Set<string>();
|
|
73
|
+
collectVars(agent.mcp_servers, used);
|
|
74
|
+
collectVars(agent.system, used);
|
|
75
|
+
|
|
76
|
+
for (const v of used) {
|
|
77
|
+
if (!keys.has(v)) {
|
|
78
|
+
diagnostics.push({
|
|
79
|
+
checkId: "FTN013",
|
|
80
|
+
severity: "warning",
|
|
81
|
+
message:
|
|
82
|
+
`Agent "${name}" references \${${v}} but its environment declares no such ` +
|
|
83
|
+
`key — it will only resolve if a vault supplies it at conversation create`,
|
|
84
|
+
entity: name,
|
|
85
|
+
lexicon: "fountain",
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return diagnostics;
|
|
92
|
+
},
|
|
93
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
2
|
+
import { propsOf } from "../../entity-props";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* FTN014: a Vault key that collides with a declared Environment key.
|
|
6
|
+
*
|
|
7
|
+
* Vault values win on key collision at spawn — silently. Shadowing a
|
|
8
|
+
* reviewed environment value is sometimes exactly the point (staging
|
|
9
|
+
* overrides) and sometimes an accident; either way it should be visible
|
|
10
|
+
* in review, so collisions warn.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
function keysOf(spec: Record<string, unknown>): Set<string> {
|
|
14
|
+
const keys = new Set<string>();
|
|
15
|
+
const envVars = spec.env_vars;
|
|
16
|
+
if (envVars && typeof envVars === "object") for (const k of Object.keys(envVars)) keys.add(k);
|
|
17
|
+
const secrets = spec.secrets;
|
|
18
|
+
if (Array.isArray(secrets)) {
|
|
19
|
+
for (const s of secrets) {
|
|
20
|
+
const key = (s as { key?: unknown })?.key;
|
|
21
|
+
if (typeof key === "string") keys.add(key);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return keys;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const vaultShadowingCheck: PostSynthCheck = {
|
|
28
|
+
id: "FTN014",
|
|
29
|
+
description: "Vault keys shadowing a declared Environment key should be visible in review",
|
|
30
|
+
|
|
31
|
+
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
32
|
+
const diagnostics: PostSynthDiagnostic[] = [];
|
|
33
|
+
const environments: Array<[string, Set<string>]> = [];
|
|
34
|
+
const vaults: Array<[string, Set<string>]> = [];
|
|
35
|
+
|
|
36
|
+
for (const [name, entity] of ctx.entities) {
|
|
37
|
+
const spec = propsOf(entity);
|
|
38
|
+
if (entity.entityType === "Fountain::V1::Environment") environments.push([name, keysOf(spec)]);
|
|
39
|
+
if (entity.entityType === "Fountain::V1::Vault") vaults.push([name, keysOf(spec)]);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
for (const [vaultName, vaultKeys] of vaults) {
|
|
43
|
+
for (const [envName, envKeys] of environments) {
|
|
44
|
+
const shadowed = [...vaultKeys].filter((k) => envKeys.has(k));
|
|
45
|
+
if (shadowed.length > 0) {
|
|
46
|
+
diagnostics.push({
|
|
47
|
+
checkId: "FTN014",
|
|
48
|
+
severity: "warning",
|
|
49
|
+
message:
|
|
50
|
+
`Vault "${vaultName}" shadows ${shadowed.map((k) => `"${k}"`).join(", ")} from ` +
|
|
51
|
+
`environment "${envName}" — vault values win silently on collision at spawn`,
|
|
52
|
+
entity: vaultName,
|
|
53
|
+
lexicon: "fountain",
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return diagnostics;
|
|
60
|
+
},
|
|
61
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
2
|
+
import { propsOf } from "../../entity-props";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* FTN015: secret-shaped MCP server env keys must use `${VAR}` references.
|
|
6
|
+
*
|
|
7
|
+
* A literal value under TOKEN/SECRET/KEY/PASSWORD-shaped keys in
|
|
8
|
+
* `mcp_servers.*.env` is a credential in source and in fountain's stored
|
|
9
|
+
* agent config. FTN001 catches known credential *shapes* at the AST;
|
|
10
|
+
* this catches the key-name signal regardless of value shape.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const SECRET_KEY_RE = /(TOKEN|SECRET|PASSWORD|API_?KEY|CREDENTIAL)/i;
|
|
14
|
+
const SUBSTITUTION_RE = /^\$\{[A-Za-z_][A-Za-z0-9_]*\}$/;
|
|
15
|
+
|
|
16
|
+
export const mcpSecretEnvSubstitutionCheck: PostSynthCheck = {
|
|
17
|
+
id: "FTN015",
|
|
18
|
+
description: "Secret-shaped MCP env keys must be ${VAR} references, not literals",
|
|
19
|
+
|
|
20
|
+
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
21
|
+
const diagnostics: PostSynthDiagnostic[] = [];
|
|
22
|
+
|
|
23
|
+
for (const [name, entity] of ctx.entities) {
|
|
24
|
+
if (entity.entityType !== "Fountain::V1::Agent") continue;
|
|
25
|
+
const servers = propsOf(entity).mcp_servers as Record<string, unknown> | undefined;
|
|
26
|
+
if (!servers || typeof servers !== "object") continue;
|
|
27
|
+
|
|
28
|
+
for (const [serverName, server] of Object.entries(servers)) {
|
|
29
|
+
const env = (server as { env?: Record<string, unknown> })?.env;
|
|
30
|
+
if (!env || typeof env !== "object") continue;
|
|
31
|
+
for (const [key, value] of Object.entries(env)) {
|
|
32
|
+
if (!SECRET_KEY_RE.test(key)) continue;
|
|
33
|
+
if (typeof value === "string" && SUBSTITUTION_RE.test(value)) continue;
|
|
34
|
+
diagnostics.push({
|
|
35
|
+
checkId: "FTN015",
|
|
36
|
+
severity: "error",
|
|
37
|
+
message:
|
|
38
|
+
`Agent "${name}" mcp_servers.${serverName}.env.${key} looks secret-shaped ` +
|
|
39
|
+
`but is not a \${VAR} reference — literals here land in stored agent config`,
|
|
40
|
+
entity: name,
|
|
41
|
+
lexicon: "fountain",
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return diagnostics;
|
|
48
|
+
},
|
|
49
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
2
|
+
import { propsOf } from "../../entity-props";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* FTN016: runtime and model must be valid before apply.
|
|
6
|
+
*
|
|
7
|
+
* The generated types make these compile-time checks for typed authoring;
|
|
8
|
+
* this backstops untyped construction (imported templates, hand-built
|
|
9
|
+
* plans) so a typo fails the build instead of a 422 at apply.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const RUNTIMES = new Set(["claude", "codex", "gemini", "opencode"]);
|
|
13
|
+
const MODEL_RE = /^[a-z0-9_-]+\/[a-z0-9._-]+$/;
|
|
14
|
+
|
|
15
|
+
export const runtimeModelValidCheck: PostSynthCheck = {
|
|
16
|
+
id: "FTN016",
|
|
17
|
+
description: "Agent runtime must be a known runtime and model canonical provider/model_id",
|
|
18
|
+
|
|
19
|
+
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
20
|
+
const diagnostics: PostSynthDiagnostic[] = [];
|
|
21
|
+
|
|
22
|
+
for (const [name, entity] of ctx.entities) {
|
|
23
|
+
if (entity.entityType !== "Fountain::V1::Agent") continue;
|
|
24
|
+
const agent = propsOf(entity) as { runtime?: unknown; model?: unknown };
|
|
25
|
+
|
|
26
|
+
if (typeof agent.runtime === "string" && !RUNTIMES.has(agent.runtime)) {
|
|
27
|
+
diagnostics.push({
|
|
28
|
+
checkId: "FTN016",
|
|
29
|
+
severity: "error",
|
|
30
|
+
message:
|
|
31
|
+
`Agent "${name}" runtime "${agent.runtime}" is not one of ` +
|
|
32
|
+
`${[...RUNTIMES].join(", ")}`,
|
|
33
|
+
entity: name,
|
|
34
|
+
lexicon: "fountain",
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
if (typeof agent.model === "string" && !MODEL_RE.test(agent.model)) {
|
|
38
|
+
diagnostics.push({
|
|
39
|
+
checkId: "FTN016",
|
|
40
|
+
severity: "error",
|
|
41
|
+
message: `Agent "${name}" model "${agent.model}" is not canonical provider/model_id form`,
|
|
42
|
+
entity: name,
|
|
43
|
+
lexicon: "fountain",
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return diagnostics;
|
|
49
|
+
},
|
|
50
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
2
|
+
import { propsOf } from "../../entity-props";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* FTN017: resource names must be unique per kind.
|
|
6
|
+
*
|
|
7
|
+
* fountainApply reconciles by name; two entities of the same kind
|
|
8
|
+
* resolving to the same fountain name would fight over one live
|
|
9
|
+
* resource — the second apply step silently becomes an update of the
|
|
10
|
+
* first one's creation.
|
|
11
|
+
*/
|
|
12
|
+
export const uniqueResourceNamesCheck: PostSynthCheck = {
|
|
13
|
+
id: "FTN017",
|
|
14
|
+
description: "Two declarations of the same kind must not resolve to the same fountain name",
|
|
15
|
+
|
|
16
|
+
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
17
|
+
const diagnostics: PostSynthDiagnostic[] = [];
|
|
18
|
+
const seen = new Map<string, string>();
|
|
19
|
+
|
|
20
|
+
for (const [entityName, entity] of ctx.entities) {
|
|
21
|
+
if (!entity.entityType.startsWith("Fountain::V1::")) continue;
|
|
22
|
+
const props = propsOf(entity);
|
|
23
|
+
const resourceName = typeof props.name === "string" ? props.name : entityName;
|
|
24
|
+
const key = `${entity.entityType}/${resourceName}`;
|
|
25
|
+
const first = seen.get(key);
|
|
26
|
+
if (first) {
|
|
27
|
+
diagnostics.push({
|
|
28
|
+
checkId: "FTN017",
|
|
29
|
+
severity: "error",
|
|
30
|
+
message:
|
|
31
|
+
`"${entityName}" and "${first}" both resolve to ${key} — apply would ` +
|
|
32
|
+
`reconcile them onto one live resource`,
|
|
33
|
+
entity: entityName,
|
|
34
|
+
lexicon: "fountain",
|
|
35
|
+
});
|
|
36
|
+
} else {
|
|
37
|
+
seen.set(key, entityName);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return diagnostics;
|
|
42
|
+
},
|
|
43
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serializer.d.ts","sourceRoot":"","sources":["../src/serializer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAgC,MAAM,kBAAkB,CAAC;AAsBjF,eAAO,MAAM,kBAAkB,EAAE,UAwDhC,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
skill: chant-fountain-locked-sandboxes
|
|
3
|
+
description: Declare locked-down fountain environments for untrusted or security-sensitive agents, and run conversations against them
|
|
4
|
+
user-invocable: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Locked-Down Sandboxes and Conversation Runs
|
|
8
|
+
|
|
9
|
+
## The posture
|
|
10
|
+
|
|
11
|
+
For an agent handling anything sensitive (a repo checkout, an internal API), declare the environment closed by construction:
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
export const lockedEnv = new Environment({
|
|
15
|
+
name: "concierge-env",
|
|
16
|
+
networking_type: "limited",
|
|
17
|
+
networking_config: { allowed_hosts: ["registry.npmjs.org", "github.com"] },
|
|
18
|
+
metadata: { "managed-by": "chant" },
|
|
19
|
+
});
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Semantics (from fountain's own schema): `unrestricted` is a no-op — sprites are open by default. `limited` restricts egress to `allowed_hosts`; with **no** hosts (or an empty list) it denies all egress — deny-all, not allow-all. So `limited` + `[]` is the isolation mode.
|
|
23
|
+
|
|
24
|
+
Pair it with a closed vault policy on the agent — `allowed_vault_ids: []` — so no conversation can override the reviewed environment at spawn, and give the sandbox **no cloud credentials of any kind**: anything readable inside is exfiltratable by prompt injection. Services the agent needs live outside the sandbox behind their own auth; the sandbox gets at most a conversation-scoped token.
|
|
25
|
+
|
|
26
|
+
Lint posture: FTN010/FTN011 fire on missing/unrestricted networking at warning severity by default. For repos whose environments are all concierge-class, promote them to error via project rules.
|
|
27
|
+
|
|
28
|
+
## Drift is the alarm
|
|
29
|
+
|
|
30
|
+
The environment's config is the enforcement boundary, so watch it: `chant lifecycle diff --live` flags a UI edit that adds a secret, opens networking, or drops the marker. Wire it into a scheduled watch — an out-of-band change to a locked environment is an incident, not housekeeping.
|
|
31
|
+
|
|
32
|
+
## Running conversations
|
|
33
|
+
|
|
34
|
+
Conversations are runs, not resources. Use the `fountainRun` activity: resolves the agent by name, starts (optionally with a prompt and an allowlisted vault), polls to `completed | failed | timed_out`, and terminates at its deadline so a hung sandbox never outlives the op. Multi-turn interaction (follow-up prompts, interrupt) is fountain's own conversations API — keep chant to the lifecycle edges.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
skill: chant-fountain-secrets
|
|
3
|
+
description: Handle fountain secrets, env vars, and ${VAR} substitution safely from chant
|
|
4
|
+
user-invocable: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Fountain Secrets and Substitution
|
|
8
|
+
|
|
9
|
+
## The model
|
|
10
|
+
|
|
11
|
+
Everything materialized into a fountain sandbox must be presumed exfiltrated once untrusted agent code runs. Order of preference:
|
|
12
|
+
|
|
13
|
+
1. **`${VAR}` substitution references** in agent config (MCP server env, system prompts). Resolved at spawn from the merged environment + vault sets. Never a value in source.
|
|
14
|
+
2. **Environment secrets** (`spec.secrets`) — encrypted at rest, write-only over the API (values are never returned once stored). `fountainApply` upserts them through the sub-resource; a changed value cannot be detected, only overwritten.
|
|
15
|
+
3. **`env_vars`** — plaintext config only. FTN012 errors on credential-shaped keys or values here.
|
|
16
|
+
|
|
17
|
+
Never put a literal credential anywhere in a declaration: FTN001 catches known shapes (AWS keys, GitHub/Slack tokens, `sk-`/`ftn_` keys, private key material) at the AST; FTN015 errors on secret-shaped MCP env keys that are not `${VAR}` references.
|
|
18
|
+
|
|
19
|
+
## Vault semantics
|
|
20
|
+
|
|
21
|
+
Vault values **win on key collision** with the environment, silently, at spawn. FTN014 warns when a declared vault shadows a declared environment key, so the override is visible in review. Which vaults may attach to an agent is scoped by `allowed_vault_ids` (nullable: `null` = any tenant vault, `[]` = none, list = allowlist) — set `[]` on agents whose environment must not be overridable.
|
|
22
|
+
|
|
23
|
+
## Build-time resolution check
|
|
24
|
+
|
|
25
|
+
FTN013 warns when an agent references `${VAR}` and its declared environment has no such key (env_vars or secret keys). A vault can legitimately supply it at conversation create — the warning is "confirm this is intentional," not "this is broken."
|
|
26
|
+
|
|
27
|
+
## Round-trips and their limits
|
|
28
|
+
|
|
29
|
+
`chant import --from` exports live resources but never secrets: values are write-only upstream, and secret keys are not on the typed request surface. Re-declare imported environments' secrets through your secret provider. Upstream discussion of a reference-based model that would fix this: BinaryBourbon/fountain#148.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
skill: chant-fountain
|
|
3
|
+
description: Declare, lint, and reconcile fountain Environments, Vaults, and Agents from a chant project
|
|
4
|
+
user-invocable: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Fountain Workloads as Typed Estate
|
|
8
|
+
|
|
9
|
+
## What this lexicon covers
|
|
10
|
+
|
|
11
|
+
[fountain](https://github.com/BinaryBourbon/fountain) runs coding agents in sandboxed VMs. Its workload layer is three kinds — `Environment` (sandbox baseline), `Vault` (env-var overrides), `Agent` (a runnable agent config) — and this lexicon declares them as typed chant resources. Conversations are runs, not resources: start them with the `fountainRun` op, never declare them.
|
|
12
|
+
|
|
13
|
+
The source of truth is the TypeScript in `src/`. `chant build` serializes it to fountain's own manifest YAML (ejectable — `fountain apply -f` accepts it verbatim) plus a `fountain-plan.json` sidecar that `fountainApply` reconciles against the API directly: create-if-new, update-by-name, opt-in owned-only prune keyed on the `managed-by: chant` metadata marker.
|
|
14
|
+
|
|
15
|
+
## Authoring
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { Environment, Agent } from "@intentius/chant-lexicon-fountain";
|
|
19
|
+
|
|
20
|
+
export const env = new Environment({
|
|
21
|
+
name: "team-env",
|
|
22
|
+
networking_type: "limited", // FTN010 requires explicit intent
|
|
23
|
+
networking_config: { allowed_hosts: ["github.com"] },
|
|
24
|
+
metadata: { "managed-by": "chant" }, // enables owned-only reconcile/prune
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
export const helper = new Agent({
|
|
28
|
+
name: "helper",
|
|
29
|
+
model: "anthropic/claude-sonnet-4-6",
|
|
30
|
+
runtime: "claude",
|
|
31
|
+
environment: env, // typed ref — dangling name = build error
|
|
32
|
+
skills: [{ source: "owner/skills-repo", ref: "v1.0.0" }], // always pin a ref
|
|
33
|
+
});
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Endpoint and auth
|
|
37
|
+
|
|
38
|
+
`FOUNTAIN_ENDPOINT` (defaults to the hosted instance) + `FOUNTAIN_TOKEN` (mint via `POST /api/auth/token` with email+password, or the account UI). The same code applies to a local `mix phx.server` fountain by pointing `FOUNTAIN_ENDPOINT` at it — registration and token mint work headless, so CI needs no browser.
|
|
39
|
+
|
|
40
|
+
## The loop
|
|
41
|
+
|
|
42
|
+
1. `chant build` — synthesize + lint (FTN rules catch open networking, credential literals, unresolvable `${VAR}` refs before review).
|
|
43
|
+
2. `chant run <apply op>` or call `fountainApply` — reconcile. Idempotent by name.
|
|
44
|
+
3. `chant lifecycle diff --live` — drift: a UI edit to an owned Environment shows up here.
|
|
45
|
+
4. `chant import --from` — adopt UI-built resources into typed files (secrets stay behind; see the secrets skill).
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetch the fountain OpenAPI spec.
|
|
3
|
+
*
|
|
4
|
+
* Fountain serves its OpenAPI 3.1 spec from the running app at
|
|
5
|
+
* /api/openapi.json (generated from code via OpenApiSpex; public, no auth).
|
|
6
|
+
* This is an unversioned live endpoint: it always serves the spec of
|
|
7
|
+
* whatever is currently deployed, with no release tag to pin — fountain
|
|
8
|
+
* follows the rolling-spec model (like fly). A versioned spec artifact per
|
|
9
|
+
* release shipped upstream as a release asset (BinaryBourbon/fountain#140);
|
|
10
|
+
* when the hosted endpoint is reliably current this fetcher can prefer it.
|
|
11
|
+
*
|
|
12
|
+
* Fallback: a committed snapshot (fountain-openapi.snapshot.json) keeps
|
|
13
|
+
* generation hermetic when the live endpoint is unreachable — CI runs,
|
|
14
|
+
* offline dev, or the current state where the hosted instance predates the
|
|
15
|
+
* endpoint. The snapshot is refreshed alongside surface.snapshot.json when
|
|
16
|
+
* upstream moves (regenerate from a fountain checkout via
|
|
17
|
+
* `mix openapi.spec.json --spec FountainWeb.ApiSpec`).
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Fetch the fountain OpenAPI spec with caching, falling back to the
|
|
21
|
+
* committed snapshot when the live endpoint is unreachable. Returns a
|
|
22
|
+
* single-entry map — the whole spec is one document; the parser fans it
|
|
23
|
+
* out into per-kind results.
|
|
24
|
+
*/
|
|
25
|
+
export declare function fetchSchemas(options?: {
|
|
26
|
+
force?: boolean;
|
|
27
|
+
}): Promise<Map<string, Buffer>>;
|
|
28
|
+
//# sourceMappingURL=fetch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../src/spec/fetch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAcH;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAe9F"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fountain OpenAPI parser.
|
|
3
|
+
*
|
|
4
|
+
* Fountain's spec (OpenApiSpex, served at /api/openapi.json) is
|
|
5
|
+
* request/response DTOs, so we generate a curated set of resources — the
|
|
6
|
+
* three kinds `fountain apply` reconciles: Environment, Vault, Agent.
|
|
7
|
+
* Each pairs a request schema (writable authoring surface) with a response
|
|
8
|
+
* schema (read-only attributes). Object schemas reachable from the request
|
|
9
|
+
* schemas (e.g. Repository) are emitted as standalone property-type classes.
|
|
10
|
+
*
|
|
11
|
+
* Conversations are deliberately not a resource: they are runs with a
|
|
12
|
+
* status lifecycle, modeled as ops, not declarables.
|
|
13
|
+
*/
|
|
14
|
+
import { type PropertyConstraints } from "@intentius/chant/codegen/json-schema";
|
|
15
|
+
export type { PropertyConstraints };
|
|
16
|
+
export interface ParsedProperty {
|
|
17
|
+
name: string;
|
|
18
|
+
tsType: string;
|
|
19
|
+
required: boolean;
|
|
20
|
+
description?: string;
|
|
21
|
+
constraints: PropertyConstraints;
|
|
22
|
+
}
|
|
23
|
+
export interface ParsedResource {
|
|
24
|
+
typeName: string;
|
|
25
|
+
description?: string;
|
|
26
|
+
properties: ParsedProperty[];
|
|
27
|
+
attributes: Array<{
|
|
28
|
+
name: string;
|
|
29
|
+
tsType: string;
|
|
30
|
+
}>;
|
|
31
|
+
}
|
|
32
|
+
export interface FountainParseResult {
|
|
33
|
+
resource: ParsedResource;
|
|
34
|
+
/** Always empty — fountain emits property types as standalone results. */
|
|
35
|
+
propertyTypes: Array<{
|
|
36
|
+
name: string;
|
|
37
|
+
defType: string;
|
|
38
|
+
}>;
|
|
39
|
+
/** Always empty — enums are inlined as string-literal unions. */
|
|
40
|
+
enums: Array<{
|
|
41
|
+
name: string;
|
|
42
|
+
values: string[];
|
|
43
|
+
}>;
|
|
44
|
+
/** Whether this entity is a property type (nested config shape). */
|
|
45
|
+
isProperty?: boolean;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Parse the fountain OpenAPI spec into the curated resources and the
|
|
49
|
+
* property types reachable from their request schemas.
|
|
50
|
+
*/
|
|
51
|
+
export declare function parseFountainOpenAPI(data: string | Buffer): FountainParseResult[];
|
|
52
|
+
/** Convert a schema name into a PascalCase class-name segment. */
|
|
53
|
+
export declare function schemaToClassName(schemaName: string): string;
|
|
54
|
+
/** Extract short name: "Fountain::V1::Agent" → "Agent". */
|
|
55
|
+
export declare function fountainShortName(typeName: string): string;
|
|
56
|
+
/** Extract service name: "Fountain::V1::Agent" → "V1". */
|
|
57
|
+
export declare function fountainServiceName(typeName: string): string;
|
|
58
|
+
//# sourceMappingURL=parse.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../src/spec/parse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAKL,KAAK,mBAAmB,EACzB,MAAM,sCAAsC,CAAC;AAI9C,YAAY,EAAE,mBAAmB,EAAE,CAAC;AAEpC,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,mBAAmB,CAAC;CAClC;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,cAAc,EAAE,CAAC;IAC7B,UAAU,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACrD;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,cAAc,CAAC;IACzB,0EAA0E;IAC1E,aAAa,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACxD,iEAAiE;IACjE,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;IACjD,oEAAoE;IACpE,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAqCD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,mBAAmB,EAAE,CAwEjF;AAmID,kEAAkE;AAClE,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED,2DAA2D;AAC3D,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAG1D;AAED,0DAA0D;AAC1D,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAG5D"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// Code generated by chant fly generate. DO NOT EDIT.
|
|
2
|
+
|
|
3
|
+
// --- Fly Resource classes ---
|
|
4
|
+
|
|
5
|
+
export declare class Agent {
|
|
6
|
+
constructor(props: {
|
|
7
|
+
model: string;
|
|
8
|
+
name: string;
|
|
9
|
+
runtime: "claude" | "codex" | "gemini" | "opencode";
|
|
10
|
+
/** Vaults a conversation may attach to this agent. null (default) allows any vault the tenant owns; an empty list forbids attaching any vault; a non-empty list is an allowlist. */
|
|
11
|
+
allowed_vault_ids?: string[];
|
|
12
|
+
description?: string;
|
|
13
|
+
environment_id?: string;
|
|
14
|
+
mcp_servers?: Record<string, unknown>;
|
|
15
|
+
metadata?: Record<string, unknown>;
|
|
16
|
+
/** Each entry is either inline (`{name, content}` — full SKILL.md text written to the sprite) or github (`{source, ref?, name?}` — installed on the sprite via the skills.sh CLI, optionally pinned to a tag/branch/sha via `ref`). Exactly one of `content` or `source` must be set on each entry. */
|
|
17
|
+
skills?: Record<string, unknown>[];
|
|
18
|
+
system?: string;
|
|
19
|
+
});
|
|
20
|
+
readonly id: string;
|
|
21
|
+
readonly inserted_at: string;
|
|
22
|
+
readonly updated_at: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export declare class Environment {
|
|
26
|
+
constructor(props: {
|
|
27
|
+
name: string;
|
|
28
|
+
env_vars?: Record<string, string>;
|
|
29
|
+
metadata?: Record<string, unknown>;
|
|
30
|
+
/** Refines networking_type: limited. allowed_hosts is the only key honored today; unknown keys are ignored. Under limited, egress is restricted to the allowlisted domains. With no allowed_hosts (or an empty list), the sandbox denies all egress by default — this is a deny-all, not an allow-all. */
|
|
31
|
+
networking_config?: Record<string, unknown>;
|
|
32
|
+
networking_type?: "limited" | "unrestricted";
|
|
33
|
+
packages?: Record<string, unknown>;
|
|
34
|
+
repositories?: Repository[];
|
|
35
|
+
setup_script?: string;
|
|
36
|
+
});
|
|
37
|
+
readonly id: string;
|
|
38
|
+
readonly inserted_at: string;
|
|
39
|
+
readonly updated_at: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export declare class Vault {
|
|
43
|
+
constructor(props: {
|
|
44
|
+
name: string;
|
|
45
|
+
description?: string;
|
|
46
|
+
metadata?: Record<string, unknown>;
|
|
47
|
+
});
|
|
48
|
+
readonly id: string;
|
|
49
|
+
readonly inserted_at: string;
|
|
50
|
+
readonly updated_at: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// --- Property type classes ---
|
|
54
|
+
|
|
55
|
+
export declare class Repository {
|
|
56
|
+
constructor(props: {
|
|
57
|
+
mount_path: string;
|
|
58
|
+
url: string;
|
|
59
|
+
});
|
|
60
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-cli.d.ts","sourceRoot":"","sources":["../src/validate-cli.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate generated lexicon-fountain artifacts.
|
|
3
|
+
*
|
|
4
|
+
* Thin wrapper around the core validation framework
|
|
5
|
+
* with fountain-specific configuration.
|
|
6
|
+
*/
|
|
7
|
+
import { type ValidateResult } from "@intentius/chant/codegen/validate";
|
|
8
|
+
export type { ValidateCheck, ValidateResult } from "@intentius/chant/codegen/validate";
|
|
9
|
+
/**
|
|
10
|
+
* Validate the generated lexicon-fountain artifacts.
|
|
11
|
+
*/
|
|
12
|
+
export declare function validate(opts?: {
|
|
13
|
+
basePath?: string;
|
|
14
|
+
}): Promise<ValidateResult>;
|
|
15
|
+
//# sourceMappingURL=validate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAA4B,KAAK,cAAc,EAAE,MAAM,mCAAmC,CAAC;AAElG,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AAWvF;;GAEG;AACH,wBAAsB,QAAQ,CAAC,IAAI,CAAC,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,cAAc,CAAC,CAQpF"}
|