@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 @@
|
|
|
1
|
+
{"version":3,"file":"ftn012-no-cloud-credential-env.d.ts","sourceRoot":"","sources":["../../../src/lint/post-synth/ftn012-no-cloud-credential-env.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAyC,MAAM,kCAAkC,CAAC;AAgB9G,eAAO,MAAM,yBAAyB,EAAE,cAyCvC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ftn013-substitution-resolvable.d.ts","sourceRoot":"","sources":["../../../src/lint/post-synth/ftn013-substitution-resolvable.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAyC,MAAM,kCAAkC,CAAC;AA8C9G,eAAO,MAAM,2BAA2B,EAAE,cA8CzC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ftn014-vault-shadowing.d.ts","sourceRoot":"","sources":["../../../src/lint/post-synth/ftn014-vault-shadowing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAyC,MAAM,kCAAkC,CAAC;AA0B9G,eAAO,MAAM,mBAAmB,EAAE,cAkCjC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ftn015-mcp-secret-env-substitution.d.ts","sourceRoot":"","sources":["../../../src/lint/post-synth/ftn015-mcp-secret-env-substitution.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAyC,MAAM,kCAAkC,CAAC;AAe9G,eAAO,MAAM,6BAA6B,EAAE,cAiC3C,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ftn016-runtime-model-valid.d.ts","sourceRoot":"","sources":["../../../src/lint/post-synth/ftn016-runtime-model-valid.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAyC,MAAM,kCAAkC,CAAC;AAc9G,eAAO,MAAM,sBAAsB,EAAE,cAmCpC,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { PostSynthCheck } from "@intentius/chant/lint/post-synth";
|
|
2
|
+
/**
|
|
3
|
+
* FTN017: resource names must be unique per kind.
|
|
4
|
+
*
|
|
5
|
+
* fountainApply reconciles by name; two entities of the same kind
|
|
6
|
+
* resolving to the same fountain name would fight over one live
|
|
7
|
+
* resource — the second apply step silently becomes an update of the
|
|
8
|
+
* first one's creation.
|
|
9
|
+
*/
|
|
10
|
+
export declare const uniqueResourceNamesCheck: PostSynthCheck;
|
|
11
|
+
//# sourceMappingURL=ftn017-unique-resource-names.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ftn017-unique-resource-names.d.ts","sourceRoot":"","sources":["../../../src/lint/post-synth/ftn017-unique-resource-names.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAyC,MAAM,kCAAkC,CAAC;AAG9G;;;;;;;GAOG;AACH,eAAO,MAAM,wBAAwB,EAAE,cA+BtC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lint/post-synth/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AAUvE,eAAO,MAAM,eAAe,EAAE,cAAc,EAS3C,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ftn001-no-secret-literals.d.ts","sourceRoot":"","sources":["../../../src/lint/rules/ftn001-no-secret-literals.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAA+B,MAAM,4BAA4B,CAAC;AAyBxF,eAAO,MAAM,oBAAoB,EAAE,QA4ClC,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { LintRule } from "@intentius/chant/lint/rule";
|
|
2
|
+
export { noSecretLiteralsRule } from "./ftn001-no-secret-literals.js";
|
|
3
|
+
/** All lint rules provided by this lexicon (imported by plugin.ts's lintRules()). */
|
|
4
|
+
export declare const rules: LintRule[];
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lint/rules/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAG3D,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAEnE,qFAAqF;AACrF,eAAO,MAAM,KAAK,EAAE,QAAQ,EAA2B,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { CompletionContext, CompletionItem } from "@intentius/chant/lsp/types";
|
|
2
|
+
/**
|
|
3
|
+
* Provide LSP completions for fountain resources — class names after
|
|
4
|
+
* `new `, property names inside a constructor.
|
|
5
|
+
*/
|
|
6
|
+
export declare function completions(ctx: CompletionContext): CompletionItem[];
|
|
7
|
+
//# sourceMappingURL=completions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"completions.d.ts","sourceRoot":"","sources":["../../src/lsp/completions.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAapF;;;GAGG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,iBAAiB,GAAG,cAAc,EAAE,CAEpE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hover.d.ts","sourceRoot":"","sources":["../../src/lsp/hover.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAiE1E,4DAA4D;AAC5D,wBAAgB,KAAK,CAAC,GAAG,EAAE,YAAY,GAAG,SAAS,GAAG,SAAS,CAE9D"}
|
package/dist/meta.json
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"Agent": {
|
|
3
|
+
"resourceType": "Fountain::V1::Agent",
|
|
4
|
+
"kind": "resource",
|
|
5
|
+
"lexicon": "fountain",
|
|
6
|
+
"propertyConstraints": {
|
|
7
|
+
"environment_id": {
|
|
8
|
+
"format": "uuid"
|
|
9
|
+
},
|
|
10
|
+
"model": {
|
|
11
|
+
"pattern": "^[a-z0-9_-]+/[a-z0-9._-]+$"
|
|
12
|
+
},
|
|
13
|
+
"name": {
|
|
14
|
+
"minLength": 1,
|
|
15
|
+
"maxLength": 200
|
|
16
|
+
},
|
|
17
|
+
"runtime": {
|
|
18
|
+
"enum": [
|
|
19
|
+
"claude",
|
|
20
|
+
"codex",
|
|
21
|
+
"gemini",
|
|
22
|
+
"opencode"
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"Environment": {
|
|
28
|
+
"resourceType": "Fountain::V1::Environment",
|
|
29
|
+
"kind": "resource",
|
|
30
|
+
"lexicon": "fountain",
|
|
31
|
+
"propertyConstraints": {
|
|
32
|
+
"name": {
|
|
33
|
+
"minLength": 1,
|
|
34
|
+
"maxLength": 200
|
|
35
|
+
},
|
|
36
|
+
"networking_type": {
|
|
37
|
+
"enum": [
|
|
38
|
+
"unrestricted",
|
|
39
|
+
"limited"
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"Repository": {
|
|
45
|
+
"resourceType": "Fountain::V1::Repository",
|
|
46
|
+
"kind": "property",
|
|
47
|
+
"lexicon": "fountain",
|
|
48
|
+
"propertyConstraints": {
|
|
49
|
+
"mount_path": {
|
|
50
|
+
"pattern": "^/"
|
|
51
|
+
},
|
|
52
|
+
"url": {
|
|
53
|
+
"pattern": "^https://",
|
|
54
|
+
"format": "uri"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
"Vault": {
|
|
59
|
+
"resourceType": "Fountain::V1::Vault",
|
|
60
|
+
"kind": "resource",
|
|
61
|
+
"lexicon": "fountain",
|
|
62
|
+
"propertyConstraints": {
|
|
63
|
+
"name": {
|
|
64
|
+
"minLength": 1,
|
|
65
|
+
"maxLength": 200
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fountain native applier.
|
|
3
|
+
*
|
|
4
|
+
* Direct-REST reconciler against fountain's API (the same surface
|
|
5
|
+
* `fountain apply` uses, but API-first: create-if-new, update-if-changed
|
|
6
|
+
* by name, optional owned-only prune). Input is the serializer's
|
|
7
|
+
* `fountain-plan.json` sidecar — entity name → { kind, spec } — so no
|
|
8
|
+
* YAML parsing happens on the apply side.
|
|
9
|
+
*
|
|
10
|
+
* Apply order is Environment → Vault → Agent: agents reference their
|
|
11
|
+
* environment by entity name in the plan, resolved to the live id here
|
|
12
|
+
* (mirroring the CLI's name→id resolution in apply.go).
|
|
13
|
+
*
|
|
14
|
+
* Secrets: `spec.secrets` entries ({key, value}) are stripped from the
|
|
15
|
+
* resource body and upserted through the secrets sub-resource. Values are
|
|
16
|
+
* write-only upstream, so this is upsert-always — a changed value cannot
|
|
17
|
+
* be detected, only overwritten. (BinaryBourbon/fountain#148's reference
|
|
18
|
+
* model would make this a plain diff; until then, upsert-always is the
|
|
19
|
+
* honest semantic.)
|
|
20
|
+
*
|
|
21
|
+
* Endpoint/token resolution: explicit args win, then FOUNTAIN_ENDPOINT /
|
|
22
|
+
* FOUNTAIN_TOKEN, then the hosted default endpoint.
|
|
23
|
+
*/
|
|
24
|
+
export declare const DEFAULT_FOUNTAIN_BASE_URL = "https://founta.inevitable.fyi";
|
|
25
|
+
/** Ownership marker checked by the owned-only prune. */
|
|
26
|
+
export declare const OWNERSHIP_KEY = "managed-by";
|
|
27
|
+
export declare const OWNERSHIP_VALUE = "chant";
|
|
28
|
+
export interface PlanEntry {
|
|
29
|
+
kind: string;
|
|
30
|
+
spec: Record<string, unknown>;
|
|
31
|
+
}
|
|
32
|
+
export type FountainPlan = Record<string, PlanEntry>;
|
|
33
|
+
export interface FountainHttp {
|
|
34
|
+
(method: string, path: string, body?: unknown): Promise<{
|
|
35
|
+
status: number;
|
|
36
|
+
json: unknown;
|
|
37
|
+
}>;
|
|
38
|
+
}
|
|
39
|
+
export interface FountainApplyArgs {
|
|
40
|
+
/** Path to the serializer's fountain-plan.json. */
|
|
41
|
+
planPath?: string;
|
|
42
|
+
/** Inline plan content (takes precedence over planPath). */
|
|
43
|
+
planContent?: string;
|
|
44
|
+
endpoint?: string;
|
|
45
|
+
token?: string;
|
|
46
|
+
/** Delete chant-owned resources absent from the plan. Off by default. */
|
|
47
|
+
prune?: boolean;
|
|
48
|
+
}
|
|
49
|
+
export interface FountainApplySummary {
|
|
50
|
+
created: string[];
|
|
51
|
+
updated: string[];
|
|
52
|
+
pruned: string[];
|
|
53
|
+
secretsUpserted: number;
|
|
54
|
+
}
|
|
55
|
+
export declare function resolveEndpoint(args?: {
|
|
56
|
+
endpoint?: string;
|
|
57
|
+
}, env?: NodeJS.ProcessEnv): string;
|
|
58
|
+
export declare function resolveToken(args?: {
|
|
59
|
+
token?: string;
|
|
60
|
+
}, env?: NodeJS.ProcessEnv): string;
|
|
61
|
+
export declare function parsePlan(content: string): FountainPlan;
|
|
62
|
+
/** Split spec.secrets ({key, value}[]) from the resource body. Pure. */
|
|
63
|
+
export declare function splitSecrets(spec: Record<string, unknown>): {
|
|
64
|
+
body: Record<string, unknown>;
|
|
65
|
+
secrets: Array<{
|
|
66
|
+
key: string;
|
|
67
|
+
value: string;
|
|
68
|
+
}>;
|
|
69
|
+
};
|
|
70
|
+
/** Is a live resource chant-owned (by its metadata marker)? Pure. */
|
|
71
|
+
export declare function isChantOwned(resource: {
|
|
72
|
+
metadata?: unknown;
|
|
73
|
+
}): boolean;
|
|
74
|
+
export declare function defaultFountainHttp(endpoint: string, token: string): FountainHttp;
|
|
75
|
+
export declare function fountainApply(args: FountainApplyArgs, http?: FountainHttp): Promise<FountainApplySummary>;
|
|
76
|
+
//# sourceMappingURL=fountain-apply.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fountain-apply.d.ts","sourceRoot":"","sources":["../../../src/op/activities/fountain-apply.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAIH,eAAO,MAAM,yBAAyB,kCAAkC,CAAC;AAEzE,wDAAwD;AACxD,eAAO,MAAM,aAAa,eAAe,CAAC;AAC1C,eAAO,MAAM,eAAe,UAAU,CAAC;AAWvC,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AAED,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;AAErD,MAAM,WAAW,YAAY;IAC3B,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CAC5F;AAED,MAAM,WAAW,iBAAiB;IAChC,mDAAmD;IACnD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4DAA4D;IAC5D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;CACzB;AAID,wBAAgB,eAAe,CAC7B,IAAI,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,EAChC,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,MAAM,CAGR;AAED,wBAAgB,YAAY,CAC1B,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,EAC7B,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,MAAM,CAIR;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,CAEvD;AAED,wEAAwE;AACxE,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IAC3D,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,OAAO,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAChD,CAQA;AAED,qEAAqE;AACrE,wBAAgB,YAAY,CAAC,QAAQ,EAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAItE;AAID,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,YAAY,CAkBjF;AAiBD,wBAAsB,aAAa,CACjC,IAAI,EAAE,iBAAiB,EACvB,IAAI,CAAC,EAAE,YAAY,GAClB,OAAO,CAAC,oBAAoB,CAAC,CA8F/B"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fountainRun — start a conversation from a declared Agent and follow it
|
|
3
|
+
* to a terminal status.
|
|
4
|
+
*
|
|
5
|
+
* Conversations are runs, not declarables (the one fountain kind the
|
|
6
|
+
* lexicon deliberately does not model as a resource). This op is the
|
|
7
|
+
* imperative half: resolve the agent by name, POST the conversation,
|
|
8
|
+
* poll until `completed | failed | timed_out | terminated`, and terminate
|
|
9
|
+
* on deadline so a hung run never outlives the op that started it.
|
|
10
|
+
*/
|
|
11
|
+
import { type FountainHttp } from "./fountain-apply.js";
|
|
12
|
+
export declare const TERMINAL_STATUSES: Set<string>;
|
|
13
|
+
export interface FountainRunArgs {
|
|
14
|
+
/** Agent name (resolved against /api/agents) or a raw agent id. */
|
|
15
|
+
agent: string;
|
|
16
|
+
prompt?: string;
|
|
17
|
+
/** Optional vault to attach (subject to the agent's allowlist upstream). */
|
|
18
|
+
vaultId?: string;
|
|
19
|
+
endpoint?: string;
|
|
20
|
+
token?: string;
|
|
21
|
+
/** Give up (and terminate the conversation) after this long. Default 10 min. */
|
|
22
|
+
timeoutMs?: number;
|
|
23
|
+
/** Poll interval. Default 5s. */
|
|
24
|
+
pollMs?: number;
|
|
25
|
+
/** Injectable clock/sleep for tests. */
|
|
26
|
+
sleep?: (ms: number) => Promise<void>;
|
|
27
|
+
}
|
|
28
|
+
export interface FountainRunResult {
|
|
29
|
+
conversationId: string;
|
|
30
|
+
status: string;
|
|
31
|
+
/** True when the op hit its deadline and terminated the conversation. */
|
|
32
|
+
terminatedByDeadline: boolean;
|
|
33
|
+
}
|
|
34
|
+
export declare function resolveAgentId(http: FountainHttp, agent: string): Promise<string>;
|
|
35
|
+
export declare function fountainRun(args: FountainRunArgs, http?: FountainHttp): Promise<FountainRunResult>;
|
|
36
|
+
//# sourceMappingURL=fountain-run.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fountain-run.d.ts","sourceRoot":"","sources":["../../../src/op/activities/fountain-run.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAIL,KAAK,YAAY,EAClB,MAAM,kBAAkB,CAAC;AAE1B,eAAO,MAAM,iBAAiB,aAA8D,CAAC;AAE7F,MAAM,WAAW,eAAe;IAC9B,mEAAmE;IACnE,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4EAA4E;IAC5E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gFAAgF;IAChF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iCAAiC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wCAAwC;IACxC,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC;AAED,MAAM,WAAW,iBAAiB;IAChC,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,oBAAoB,EAAE,OAAO,CAAC;CAC/B;AAID,wBAAsB,cAAc,CAAC,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAQvF;AAED,wBAAsB,WAAW,CAC/B,IAAI,EAAE,eAAe,EACrB,IAAI,CAAC,EAAE,YAAY,GAClB,OAAO,CAAC,iBAAiB,CAAC,CAoC5B"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fountain Op activities — resolved by the core activity registry when a
|
|
3
|
+
* project's `chant.config.ts` lists the `fountain` lexicon. Contributes
|
|
4
|
+
* the native applier (`fountainApply` — direct REST against fountain's
|
|
5
|
+
* API, no CLI, no state file) and the conversation runner (`fountainRun`).
|
|
6
|
+
*/
|
|
7
|
+
export { fountainApply, resolveEndpoint, resolveToken, parsePlan, splitSecrets, isChantOwned, defaultFountainHttp, DEFAULT_FOUNTAIN_BASE_URL, OWNERSHIP_KEY, OWNERSHIP_VALUE, } from "./fountain-apply.js";
|
|
8
|
+
export type { FountainApplyArgs, FountainApplySummary, FountainPlan, PlanEntry, FountainHttp, } from "./fountain-apply.js";
|
|
9
|
+
export { fountainRun, resolveAgentId, TERMINAL_STATUSES } from "./fountain-run.js";
|
|
10
|
+
export type { FountainRunArgs, FountainRunResult } from "./fountain-run.js";
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/op/activities/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EACL,aAAa,EACb,eAAe,EACf,YAAY,EACZ,SAAS,EACT,YAAY,EACZ,YAAY,EACZ,mBAAmB,EACnB,yBAAyB,EACzB,aAAa,EACb,eAAe,GAChB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACV,iBAAiB,EACjB,oBAAoB,EACpB,YAAY,EACZ,SAAS,EACT,YAAY,GACb,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAChF,YAAY,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package-cli.d.ts","sourceRoot":"","sources":["../src/package-cli.ts"],"names":[],"mappings":""}
|
package/dist/plugin.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAiC,MAAM,0BAA0B,CAAC;AAsB7F;;;;GAIG;AACH,eAAO,MAAM,cAAc,EAAE,aA8I5B,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ReferenceCatalog } from "@intentius/chant/lexicon";
|
|
2
|
+
/**
|
|
3
|
+
* Fountain reference catalog — how observed fountain resources reference
|
|
4
|
+
* each other, so `chant graph --live` reconstructs the topology.
|
|
5
|
+
*
|
|
6
|
+
* One relationship matters: an Agent runs in an Environment
|
|
7
|
+
* (`environment_id`). `describeResources` puts `environment_id` in the
|
|
8
|
+
* agent's observed attributes and indexes environments by `id` (their
|
|
9
|
+
* physicalId), so the edge resolves with no enrichment pass.
|
|
10
|
+
*
|
|
11
|
+
* Vaults are deliberately edge-free here: vault↔agent binding is a
|
|
12
|
+
* conversation-time choice (scoped by `allowed_vault_ids`), not standing
|
|
13
|
+
* topology.
|
|
14
|
+
*/
|
|
15
|
+
export declare const fountainReferenceCatalog: ReferenceCatalog;
|
|
16
|
+
//# sourceMappingURL=reference-catalog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reference-catalog.d.ts","sourceRoot":"","sources":["../src/reference-catalog.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAEjE;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,wBAAwB,EAAE,gBAWtC,CAAC"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
import type { LintRule, LintDiagnostic, LintContext } from "@intentius/chant/lint/rule";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* FTN001: no secret literals in fountain resource declarations.
|
|
6
|
+
*
|
|
7
|
+
* Environment/Vault secrets and MCP server env values must be substitution
|
|
8
|
+
* references (`${VAR}`) or provider references — never literal credential
|
|
9
|
+
* values. A literal in source is a credential in git history.
|
|
10
|
+
*
|
|
11
|
+
* Fires on string literals matching well-known credential shapes anywhere
|
|
12
|
+
* inside a `new Environment/Vault/Agent(...)` expression.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const FOUNTAIN_KINDS = new Set(["Environment", "Vault", "Agent"]);
|
|
16
|
+
|
|
17
|
+
const CREDENTIAL_SHAPES: Array<{ pattern: RegExp; label: string }> = [
|
|
18
|
+
{ pattern: /^AKIA[0-9A-Z]{16}$/, label: "AWS access key id" },
|
|
19
|
+
{ pattern: /^(ghp|gho|ghs|ghu)_[A-Za-z0-9]{20,}$/, label: "GitHub token" },
|
|
20
|
+
{ pattern: /^github_pat_[A-Za-z0-9_]{20,}$/, label: "GitHub fine-grained token" },
|
|
21
|
+
{ pattern: /^sk-[A-Za-z0-9_-]{20,}$/, label: "secret API key (sk-)" },
|
|
22
|
+
{ pattern: /^ftn_[A-Za-z0-9]{16,}$/, label: "fountain API key" },
|
|
23
|
+
{ pattern: /^xox[baprs]-[A-Za-z0-9-]{10,}$/, label: "Slack token" },
|
|
24
|
+
{ pattern: /^-----BEGIN [A-Z ]*PRIVATE KEY-----/, label: "private key material" },
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
export const noSecretLiteralsRule: LintRule = {
|
|
28
|
+
id: "FTN001",
|
|
29
|
+
severity: "error",
|
|
30
|
+
category: "security",
|
|
31
|
+
description:
|
|
32
|
+
"No literal credential values in fountain declarations — use ${VAR} substitution or a secret provider",
|
|
33
|
+
|
|
34
|
+
check(context: LintContext): LintDiagnostic[] {
|
|
35
|
+
const diagnostics: LintDiagnostic[] = [];
|
|
36
|
+
const source = context.sourceFile;
|
|
37
|
+
|
|
38
|
+
const visit = (node: ts.Node, insideFountainNew: boolean) => {
|
|
39
|
+
let inside = insideFountainNew;
|
|
40
|
+
|
|
41
|
+
if (ts.isNewExpression(node)) {
|
|
42
|
+
const name = node.expression.getText(source);
|
|
43
|
+
const short = name.split(".").pop() ?? name;
|
|
44
|
+
if (FOUNTAIN_KINDS.has(short)) inside = true;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (inside && (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node))) {
|
|
48
|
+
const value = node.text;
|
|
49
|
+
for (const { pattern, label } of CREDENTIAL_SHAPES) {
|
|
50
|
+
if (pattern.test(value)) {
|
|
51
|
+
const { line, character } = source.getLineAndCharacterOfPosition(node.getStart(source));
|
|
52
|
+
diagnostics.push({
|
|
53
|
+
ruleId: "FTN001",
|
|
54
|
+
severity: "error",
|
|
55
|
+
message: `Literal ${label} in a fountain declaration — use \${VAR} substitution or a secret provider`,
|
|
56
|
+
file: context.filePath,
|
|
57
|
+
line: line + 1,
|
|
58
|
+
column: character + 1,
|
|
59
|
+
});
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
ts.forEachChild(node, (child) => visit(child, inside));
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
visit(source, false);
|
|
69
|
+
return diagnostics;
|
|
70
|
+
},
|
|
71
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
2
|
+
import { propsOf } from "../../entity-props";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* FTN010: Environments must state their networking intent.
|
|
6
|
+
*
|
|
7
|
+
* Fountain defaults `networking_type` to `unrestricted` when omitted —
|
|
8
|
+
* an open-network sandbox by silence. Every declared Environment must set
|
|
9
|
+
* `networking_type` explicitly so the security posture is a reviewed,
|
|
10
|
+
* diffable decision.
|
|
11
|
+
*/
|
|
12
|
+
export const networkingExplicitCheck: PostSynthCheck = {
|
|
13
|
+
id: "FTN010",
|
|
14
|
+
description: "Environment declarations must set networking_type explicitly",
|
|
15
|
+
|
|
16
|
+
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
17
|
+
const diagnostics: PostSynthDiagnostic[] = [];
|
|
18
|
+
|
|
19
|
+
for (const [name, entity] of ctx.entities) {
|
|
20
|
+
if (entity.entityType !== "Fountain::V1::Environment") continue;
|
|
21
|
+
const props = propsOf(entity);
|
|
22
|
+
if (props.networking_type === undefined) {
|
|
23
|
+
diagnostics.push({
|
|
24
|
+
checkId: "FTN010",
|
|
25
|
+
severity: "warning",
|
|
26
|
+
message:
|
|
27
|
+
`Environment "${name}" does not set networking_type — fountain defaults to ` +
|
|
28
|
+
`"unrestricted" (open network). State the intent explicitly.`,
|
|
29
|
+
entity: name,
|
|
30
|
+
lexicon: "fountain",
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return diagnostics;
|
|
36
|
+
},
|
|
37
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
2
|
+
import { propsOf } from "../../entity-props";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* FTN011: `unrestricted` networking is a warning by default.
|
|
6
|
+
*
|
|
7
|
+
* Fountain sandboxes run untrusted agent code; an open-network sandbox is
|
|
8
|
+
* a real posture some workloads need, but it should read as a deliberate
|
|
9
|
+
* exception in review. Projects hosting untrusted-agent environments
|
|
10
|
+
* (concierge-class) should promote this to error via project rules.
|
|
11
|
+
*/
|
|
12
|
+
export const noUnrestrictedNetworkingCheck: PostSynthCheck = {
|
|
13
|
+
id: "FTN011",
|
|
14
|
+
description: "Environments should prefer networking_type: limited over unrestricted",
|
|
15
|
+
|
|
16
|
+
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
17
|
+
const diagnostics: PostSynthDiagnostic[] = [];
|
|
18
|
+
for (const [name, entity] of ctx.entities) {
|
|
19
|
+
if (entity.entityType !== "Fountain::V1::Environment") continue;
|
|
20
|
+
const props = propsOf(entity);
|
|
21
|
+
if (props.networking_type === "unrestricted") {
|
|
22
|
+
diagnostics.push({
|
|
23
|
+
checkId: "FTN011",
|
|
24
|
+
severity: "warning",
|
|
25
|
+
message:
|
|
26
|
+
`Environment "${name}" uses networking_type: unrestricted — an open-network ` +
|
|
27
|
+
`sandbox. Prefer "limited" with allowed_hosts; keep unrestricted only as a ` +
|
|
28
|
+
`reviewed exception.`,
|
|
29
|
+
entity: name,
|
|
30
|
+
lexicon: "fountain",
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return diagnostics;
|
|
35
|
+
},
|
|
36
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
2
|
+
import { propsOf } from "../../entity-props";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* FTN012: no cloud-credential-shaped values in Environment env_vars.
|
|
6
|
+
*
|
|
7
|
+
* Everything materialized into a sandbox must be presumed exfiltrated the
|
|
8
|
+
* moment untrusted agent code starts. `env_vars` is plaintext config —
|
|
9
|
+
* anything credential-shaped belongs in secrets at minimum, and long-lived
|
|
10
|
+
* cloud credentials should not enter the sandbox at all (see
|
|
11
|
+
* BinaryBourbon/fountain#148 for the token-only model).
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const CREDENTIAL_KEYS = /^(AWS_SECRET_ACCESS_KEY|AWS_ACCESS_KEY_ID|AWS_SESSION_TOKEN|GOOGLE_APPLICATION_CREDENTIALS|AZURE_CLIENT_SECRET)$/i;
|
|
15
|
+
const CREDENTIAL_VALUES = /^(AKIA[0-9A-Z]{16}|(ghp|gho|ghs|ghu)_[A-Za-z0-9]{20,}|github_pat_[A-Za-z0-9_]{20,}|sk-[A-Za-z0-9_-]{20,}|xox[baprs]-[A-Za-z0-9-]{10,})$/;
|
|
16
|
+
|
|
17
|
+
export const noCloudCredentialEnvCheck: PostSynthCheck = {
|
|
18
|
+
id: "FTN012",
|
|
19
|
+
description: "Environment env_vars must not carry cloud-credential-shaped keys or values",
|
|
20
|
+
|
|
21
|
+
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
22
|
+
const diagnostics: PostSynthDiagnostic[] = [];
|
|
23
|
+
|
|
24
|
+
for (const [name, entity] of ctx.entities) {
|
|
25
|
+
if (entity.entityType !== "Fountain::V1::Environment") continue;
|
|
26
|
+
const envVars = propsOf(entity).env_vars as Record<string, unknown> | undefined;
|
|
27
|
+
if (!envVars || typeof envVars !== "object") continue;
|
|
28
|
+
|
|
29
|
+
for (const [key, value] of Object.entries(envVars)) {
|
|
30
|
+
if (CREDENTIAL_KEYS.test(key)) {
|
|
31
|
+
diagnostics.push({
|
|
32
|
+
checkId: "FTN012",
|
|
33
|
+
severity: "error",
|
|
34
|
+
message:
|
|
35
|
+
`Environment "${name}" env_vars carries cloud-credential key "${key}" — ` +
|
|
36
|
+
`credentials must not be materialized into the sandbox`,
|
|
37
|
+
entity: name,
|
|
38
|
+
lexicon: "fountain",
|
|
39
|
+
});
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (typeof value === "string" && CREDENTIAL_VALUES.test(value)) {
|
|
43
|
+
diagnostics.push({
|
|
44
|
+
checkId: "FTN012",
|
|
45
|
+
severity: "error",
|
|
46
|
+
message:
|
|
47
|
+
`Environment "${name}" env_vars["${key}"] looks like a literal credential — ` +
|
|
48
|
+
`use secrets or a \${VAR} reference, never a plaintext value`,
|
|
49
|
+
entity: name,
|
|
50
|
+
lexicon: "fountain",
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return diagnostics;
|
|
57
|
+
},
|
|
58
|
+
};
|