@intentius/chant-lexicon-fountain 0.76.0 → 0.78.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 +6 -5
- package/dist/coverage.d.ts +6 -5
- package/dist/coverage.d.ts.map +1 -1
- package/dist/generated/index.d.ts.map +1 -1
- package/dist/integrity.json +10 -9
- package/dist/lint/audit-catalog.d.ts.map +1 -1
- package/dist/lint/post-synth/ftn023-acp-runtime-command.d.ts +3 -2
- package/dist/lint/post-synth/ftn023-acp-runtime-command.d.ts.map +1 -1
- package/dist/lint/post-synth/ftn024-setup-timeout-range.d.ts +3 -0
- package/dist/lint/post-synth/ftn024-setup-timeout-range.d.ts.map +1 -0
- package/dist/lint/post-synth/index.d.ts.map +1 -1
- package/dist/lsp/hover.d.ts.map +1 -1
- package/dist/manifest.json +2 -2
- package/dist/meta.json +14 -2
- package/dist/okf/index.md +2 -1
- package/dist/okf/rules/FTN023.md +2 -2
- package/dist/okf/rules/FTN024.md +15 -0
- package/dist/okf/types/Agent.md +7 -5
- package/dist/okf/types/Environment.md +3 -0
- package/dist/okf/types/Vault.md +1 -0
- package/dist/rules/ftn016-runtime-model-valid.ts +5 -5
- package/dist/rules/ftn023-acp-runtime-command.ts +4 -3
- package/dist/rules/ftn024-setup-timeout-range.ts +48 -0
- package/dist/skills/chant-fountain-ops.md +6 -5
- package/dist/skills/chant-fountain-secrets.md +2 -2
- package/dist/skills/chant-fountain.md +2 -2
- package/dist/spec/fetch.d.ts +18 -3
- package/dist/spec/fetch.d.ts.map +1 -1
- package/dist/spec/parse.d.ts +5 -3
- package/dist/spec/parse.d.ts.map +1 -1
- package/dist/types/index.d.ts +14 -6
- package/package.json +2 -2
- package/src/codegen/docs.ts +2 -2
- package/src/composites/composites.test.ts +19 -0
- package/src/coverage.test.ts +8 -0
- package/src/coverage.ts +24 -8
- package/src/generated/index.d.ts +14 -6
- package/src/generated/index.ts +2 -2
- package/src/generated/lexicon-fountain.json +14 -2
- package/src/lint/audit-catalog.ts +11 -3
- package/src/lint/post-synth/ftn016-runtime-model-valid.ts +5 -5
- package/src/lint/post-synth/ftn023-acp-runtime-command.ts +4 -3
- package/src/lint/post-synth/ftn024-setup-timeout-range.ts +48 -0
- package/src/lint/post-synth/index.ts +2 -0
- package/src/lint/post-synth/post-synth.test.ts +24 -0
- package/src/lsp/hover.test.ts +9 -2
- package/src/lsp/hover.ts +6 -4
- package/src/plugin.test.ts +1 -1
- package/src/serializer.test.ts +2 -2
- package/src/skills/chant-fountain-ops.md +6 -5
- package/src/skills/chant-fountain-secrets.md +2 -2
- package/src/skills/chant-fountain.md +2 -2
- package/src/spec/fetch.ts +46 -6
- package/src/spec/fountain-openapi.snapshot.json +12221 -4739
- package/src/spec/parse.ts +84 -41
- package/src/spec/spec.test.ts +163 -0
package/src/spec/parse.ts
CHANGED
|
@@ -20,9 +20,11 @@
|
|
|
20
20
|
* add one that reaches the API only as a path parameter (a schedule's
|
|
21
21
|
* teammate), into a by-name reference the serializer resolves and FTN021
|
|
22
22
|
* proves resolvable — the same shape an Agent's `environment` has.
|
|
23
|
-
* - Extensions. A prop chant accepts
|
|
24
|
-
* generated type documents it instead of
|
|
25
|
-
* through `metadata
|
|
23
|
+
* - Extensions. A prop chant accepts that the request schema does not
|
|
24
|
+
* describe, declared here so the generated type documents it instead of
|
|
25
|
+
* leaving authors to smuggle it through `metadata` or a cast. Today that is
|
|
26
|
+
* the inline `secrets` on Environment and Vault, which upstream documents
|
|
27
|
+
* on its manifest format rather than on either request schema.
|
|
26
28
|
*/
|
|
27
29
|
|
|
28
30
|
import {
|
|
@@ -96,7 +98,7 @@ interface RefSpec {
|
|
|
96
98
|
description: string;
|
|
97
99
|
}
|
|
98
100
|
|
|
99
|
-
/** A prop chant accepts that the
|
|
101
|
+
/** A prop chant accepts that the kind's request schema does not describe. */
|
|
100
102
|
interface ExtensionSpec {
|
|
101
103
|
name: string;
|
|
102
104
|
tsType: string;
|
|
@@ -109,23 +111,29 @@ interface ResourceSpec {
|
|
|
109
111
|
request: string;
|
|
110
112
|
response: string;
|
|
111
113
|
refs?: RefSpec[];
|
|
112
|
-
/** Extra accepted values, per enum-valued request property. */
|
|
113
|
-
enumExtensions?: Record<string, string[]>;
|
|
114
114
|
extensions?: ExtensionSpec[];
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
/**
|
|
118
|
-
* The
|
|
118
|
+
* The inline secrets a manifest document carries (Environment and Vault).
|
|
119
119
|
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
120
|
+
* Neither request schema has the field: secrets are a write-only
|
|
121
|
+
* sub-resource with routes of their own. Upstream's `ManifestResource` says a
|
|
122
|
+
* manifest spec is the create schema "plus an inline `secrets` map
|
|
123
|
+
* (Environment and Vault)", and `fountainApply` turns the authored list into
|
|
124
|
+
* that map. Without the extension the generated types have no field for what
|
|
125
|
+
* the chant-fountain-secrets skill tells an author to write.
|
|
126
126
|
*/
|
|
127
|
-
const
|
|
128
|
-
"
|
|
127
|
+
const SECRETS_EXTENSION: ExtensionSpec = {
|
|
128
|
+
name: "secrets",
|
|
129
|
+
tsType: "{ key: string; value: string }[]",
|
|
130
|
+
required: false,
|
|
131
|
+
description:
|
|
132
|
+
"Secrets upserted with the resource at apply, as key/value pairs; fountainApply sends them as the manifest's " +
|
|
133
|
+
"inline `secrets` map. Values are write-only upstream and can never be read back or diffed. Write a reference " +
|
|
134
|
+
"that resolves at build (an env var, a secret-manager lookup), never a literal: FTN001 flags a literal here " +
|
|
135
|
+
"as it does anywhere else in a declaration.",
|
|
136
|
+
};
|
|
129
137
|
|
|
130
138
|
const REF_TARGET = {
|
|
131
139
|
agent: `Fountain::${SERVICE}::Agent`,
|
|
@@ -135,22 +143,19 @@ const REF_TARGET = {
|
|
|
135
143
|
};
|
|
136
144
|
|
|
137
145
|
const RESOURCES: ResourceSpec[] = [
|
|
138
|
-
{ typeName: `Fountain::${SERVICE}::Environment`, request: "EnvironmentRequest", response: "Environment" },
|
|
139
|
-
{ typeName: `Fountain::${SERVICE}::Vault`, request: "VaultRequest", response: "Vault" },
|
|
140
146
|
{
|
|
141
|
-
typeName: `Fountain::${SERVICE}::
|
|
142
|
-
request: "
|
|
143
|
-
response: "
|
|
144
|
-
|
|
145
|
-
extensions: [
|
|
146
|
-
{
|
|
147
|
-
name: "runtime_command",
|
|
148
|
-
tsType: "string",
|
|
149
|
-
required: false,
|
|
150
|
-
description: `The command line an "acp" agent speaks the Agent Client Protocol over, e.g. "chant acp". ${ACP_NOTE}`,
|
|
151
|
-
},
|
|
152
|
-
],
|
|
147
|
+
typeName: `Fountain::${SERVICE}::Environment`,
|
|
148
|
+
request: "EnvironmentRequest",
|
|
149
|
+
response: "Environment",
|
|
150
|
+
extensions: [SECRETS_EXTENSION],
|
|
153
151
|
},
|
|
152
|
+
{
|
|
153
|
+
typeName: `Fountain::${SERVICE}::Vault`,
|
|
154
|
+
request: "VaultRequest",
|
|
155
|
+
response: "Vault",
|
|
156
|
+
extensions: [SECRETS_EXTENSION],
|
|
157
|
+
},
|
|
158
|
+
{ typeName: `Fountain::${SERVICE}::Agent`, request: "AgentRequest", response: "Agent" },
|
|
154
159
|
{
|
|
155
160
|
typeName: `Fountain::${SERVICE}::Teammate`,
|
|
156
161
|
request: "TeamAddRequest",
|
|
@@ -254,20 +259,12 @@ export function parseFountainOpenAPI(data: string | Buffer): FountainParseResult
|
|
|
254
259
|
properties.push(refProperty(ref));
|
|
255
260
|
continue;
|
|
256
261
|
}
|
|
257
|
-
const extraEnum = rspec.enumExtensions?.[name];
|
|
258
|
-
const constraints = coreExtractConstraints(prop as JsonSchemaProperty);
|
|
259
|
-
let tsType = resolve(prop);
|
|
260
|
-
if (extraEnum && extraEnum.length > 0) {
|
|
261
|
-
const values = [...(prop.enum ?? []), ...extraEnum];
|
|
262
|
-
constraints.enum = values;
|
|
263
|
-
tsType = [...values].sort().map((v) => JSON.stringify(v)).join(" | ");
|
|
264
|
-
}
|
|
265
262
|
properties.push({
|
|
266
263
|
name,
|
|
267
|
-
tsType,
|
|
264
|
+
tsType: resolve(prop),
|
|
268
265
|
required: requiredSet.has(name),
|
|
269
266
|
description: prop.description,
|
|
270
|
-
constraints,
|
|
267
|
+
constraints: coreExtractConstraints(prop as JsonSchemaProperty),
|
|
271
268
|
});
|
|
272
269
|
}
|
|
273
270
|
|
|
@@ -278,6 +275,15 @@ export function parseFountainOpenAPI(data: string | Buffer): FountainParseResult
|
|
|
278
275
|
}
|
|
279
276
|
|
|
280
277
|
for (const ext of rspec.extensions ?? []) {
|
|
278
|
+
// An extension exists because the spec lacks the prop. Once upstream
|
|
279
|
+
// describes it, the extension would emit a second copy with chant's
|
|
280
|
+
// type in place of upstream's, so the build stops and says so.
|
|
281
|
+
if (ext.name in reqProps) {
|
|
282
|
+
throw new Error(
|
|
283
|
+
`fountain parse: ${rspec.request} now declares "${ext.name}", which ${fountainShortName(rspec.typeName)} ` +
|
|
284
|
+
`carries as a chant extension. Remove the extension from RESOURCES in src/spec/parse.ts.`,
|
|
285
|
+
);
|
|
286
|
+
}
|
|
281
287
|
properties.push({
|
|
282
288
|
name: ext.name,
|
|
283
289
|
tsType: ext.tsType,
|
|
@@ -386,9 +392,33 @@ function collectRefs(node: unknown, acc: Set<string> = new Set()): Set<string> {
|
|
|
386
392
|
return acc;
|
|
387
393
|
}
|
|
388
394
|
|
|
389
|
-
/**
|
|
395
|
+
/**
|
|
396
|
+
* An object schema with properties (not a pure enum, and not an open map).
|
|
397
|
+
*
|
|
398
|
+
* A schema with named properties and a typed `additionalProperties` is a map
|
|
399
|
+
* that reserves a few keys, like v0.21.0's `PermissionPolicy`: tool names to
|
|
400
|
+
* verdicts, plus `ask_timeout`. Emitting it as a class would keep the reserved
|
|
401
|
+
* keys and drop the map, so it resolves to a `Record` instead.
|
|
402
|
+
*/
|
|
390
403
|
function isObjectSchema(def: OpenAPISchema): boolean {
|
|
391
|
-
return
|
|
404
|
+
return (
|
|
405
|
+
!!def.properties &&
|
|
406
|
+
Object.keys(def.properties).length > 0 &&
|
|
407
|
+
!isEnumDefinition(def) &&
|
|
408
|
+
!isOpenMap(def)
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/** An object whose `additionalProperties` is a schema, not `true` or absent. */
|
|
413
|
+
function isOpenMap(def: OpenAPISchema): boolean {
|
|
414
|
+
return !!def.additionalProperties && typeof def.additionalProperties === "object";
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/** The TypeScript union of a set of member types, deduplicated and sorted. */
|
|
418
|
+
function unionOf(types: string[]): string {
|
|
419
|
+
const parts = new Set(types.flatMap((t) => t.split(" | ")));
|
|
420
|
+
if (parts.has("any")) return "any";
|
|
421
|
+
return [...parts].sort().join(" | ");
|
|
392
422
|
}
|
|
393
423
|
|
|
394
424
|
// ── Type resolution ────────────────────────────────────────────────
|
|
@@ -411,6 +441,12 @@ function resolveType(
|
|
|
411
441
|
return [...prop.enum].sort().map((v) => JSON.stringify(v)).join(" | ");
|
|
412
442
|
}
|
|
413
443
|
|
|
444
|
+
const members = (prop as { oneOf?: OpenAPISchema[]; anyOf?: OpenAPISchema[] }).oneOf ??
|
|
445
|
+
(prop as { anyOf?: OpenAPISchema[] }).anyOf;
|
|
446
|
+
if (members && members.length > 0) {
|
|
447
|
+
return unionOf(members.map((m) => resolveType(m, schemas, emitted)));
|
|
448
|
+
}
|
|
449
|
+
|
|
414
450
|
const pt = primaryType(prop.type);
|
|
415
451
|
switch (pt) {
|
|
416
452
|
case "string":
|
|
@@ -450,6 +486,13 @@ function resolveRefType(ref: string, schemas: Record<string, OpenAPISchema>, emi
|
|
|
450
486
|
return [...(def.enum ?? [])].sort().map((v) => JSON.stringify(v)).join(" | ");
|
|
451
487
|
}
|
|
452
488
|
|
|
489
|
+
if (isOpenMap(def)) {
|
|
490
|
+
const value = resolveType(def.additionalProperties as OpenAPISchema, schemas, emitted);
|
|
491
|
+
// Reserved keys ride in the same map, so their types join the value union.
|
|
492
|
+
const reserved = Object.values(def.properties ?? {}).map((p) => resolveType(p, schemas, emitted));
|
|
493
|
+
return `Record<string, ${unionOf([value, ...reserved])}>`;
|
|
494
|
+
}
|
|
495
|
+
|
|
453
496
|
if (def.properties) return "Record<string, any>";
|
|
454
497
|
|
|
455
498
|
const pt = primaryType(def.type);
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { afterAll, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "fs";
|
|
3
|
+
import ts from "typescript";
|
|
4
|
+
import { join, dirname } from "path";
|
|
5
|
+
import { tmpdir } from "os";
|
|
6
|
+
import { fileURLToPath } from "url";
|
|
7
|
+
import { FOUNTAIN_SPEC_VERSION, fetchSchemas, readSnapshot } from "./fetch";
|
|
8
|
+
|
|
9
|
+
// No network here, so fetchSchemas always takes the snapshot fallback.
|
|
10
|
+
vi.mock("@intentius/chant/codegen/fetch", () => ({
|
|
11
|
+
fetchWithCache: () => Promise.reject(new Error("offline (test)")),
|
|
12
|
+
}));
|
|
13
|
+
import { parseFountainOpenAPI, type ParsedProperty } from "./parse";
|
|
14
|
+
|
|
15
|
+
const snapshotFile = join(dirname(fileURLToPath(import.meta.url)), "fountain-openapi.snapshot.json");
|
|
16
|
+
const snapshot = readFileSync(snapshotFile, "utf-8");
|
|
17
|
+
|
|
18
|
+
const scratch = mkdtempSync(join(tmpdir(), "fountain-spec-"));
|
|
19
|
+
afterAll(() => rmSync(scratch, { recursive: true, force: true }));
|
|
20
|
+
|
|
21
|
+
/** Write a spec whose info.version is `version` and return its path. */
|
|
22
|
+
function specAt(version: unknown): string {
|
|
23
|
+
const file = join(scratch, `spec-${String(version)}.json`);
|
|
24
|
+
writeFileSync(file, JSON.stringify({ openapi: "3.0.0", info: { title: "fountain", version } }));
|
|
25
|
+
return file;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
describe("snapshot fallback (#2389)", () => {
|
|
29
|
+
it("is the pinned release", () => {
|
|
30
|
+
// The committed snapshot is what an offline generate reads. If it lags
|
|
31
|
+
// the pin, every other assertion in this file is about the wrong spec.
|
|
32
|
+
expect(() => readSnapshot()).not.toThrow();
|
|
33
|
+
expect(`v${JSON.parse(snapshot).info.version}`).toBe(FOUNTAIN_SPEC_VERSION);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("refuses a snapshot from another release, naming both versions", () => {
|
|
37
|
+
expect(() => readSnapshot(specAt("0.16.0"), "v0.21.0")).toThrow(/0\.16\.0.*v0\.21\.0/);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("is what fetchSchemas falls back through when the pinned release cannot be fetched", async () => {
|
|
41
|
+
await expect(fetchSchemas({ snapshotFile: specAt("0.16.0") })).rejects.toThrow(
|
|
42
|
+
new RegExp(`fountain 0\\.16\\.0, but the pin is ${FOUNTAIN_SPEC_VERSION.replace(/\./g, "\\.")}`),
|
|
43
|
+
);
|
|
44
|
+
const served = await fetchSchemas();
|
|
45
|
+
expect(JSON.parse(served.get("fountain-openapi.json")!.toString("utf-8")).info.version).toBe("0.21.0");
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("refuses a snapshot with no info.version", () => {
|
|
49
|
+
expect(() => readSnapshot(specAt(undefined), "v0.21.0")).toThrow(/no info\.version.*v0\.21\.0/);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("accepts the tag and the bare version as the same release", () => {
|
|
53
|
+
expect(readSnapshot(specAt("0.21.0"), "v0.21.0").length).toBeGreaterThan(0);
|
|
54
|
+
expect(readSnapshot(specAt("v0.21.0"), "v0.21.0").length).toBeGreaterThan(0);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe("the generated surface at v0.21.0", () => {
|
|
59
|
+
const parsed = parseFountainOpenAPI(snapshot);
|
|
60
|
+
const propsOf = (kind: string): Map<string, ParsedProperty> => {
|
|
61
|
+
const result = parsed.find((r) => r.resource.typeName === `Fountain::V1::${kind}`);
|
|
62
|
+
if (!result) throw new Error(`no ${kind} in the parse`);
|
|
63
|
+
return new Map(result.resource.properties.map((p) => [p.name, p]));
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
it("gives Environment setup_timeout_seconds, with upstream's bounds", () => {
|
|
67
|
+
const timeout = propsOf("Environment").get("setup_timeout_seconds");
|
|
68
|
+
expect(timeout?.tsType).toBe("number");
|
|
69
|
+
expect(timeout?.required).toBe(false);
|
|
70
|
+
expect(timeout?.constraints).toMatchObject({ minimum: 1, maximum: 900 });
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it.each(["Environment", "Vault"])("types %s secrets as the authored key/value list", (kind) => {
|
|
74
|
+
const secrets = propsOf(kind).get("secrets");
|
|
75
|
+
expect(secrets?.tsType).toBe("{ key: string; value: string }[]");
|
|
76
|
+
expect(secrets?.required).toBe(false);
|
|
77
|
+
expect(secrets?.description).toContain("FTN001");
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("takes the acp runtime and runtime_command from the spec, with model optional", () => {
|
|
81
|
+
const agent = propsOf("Agent");
|
|
82
|
+
expect(agent.get("runtime")?.tsType).toBe('"acp" | "claude" | "codex" | "gemini" | "opencode"');
|
|
83
|
+
expect(agent.get("runtime")?.required).toBe(true);
|
|
84
|
+
// Upstream's own description, not chant's extension note.
|
|
85
|
+
expect(agent.get("runtime_command")?.description).toContain("Required when runtime is acp");
|
|
86
|
+
expect(agent.get("runtime_command")?.description).not.toContain("extension");
|
|
87
|
+
expect(agent.get("model")?.required).toBe(false);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it("keeps permission_policy a map, with ask_timeout's number in the value union", () => {
|
|
91
|
+
// v0.21.0 moved the policy behind a PermissionPolicy $ref that has one
|
|
92
|
+
// named property and a typed additionalProperties. Emitted as a class it
|
|
93
|
+
// would have only ask_timeout, and { default: "auto_allow" } would stop
|
|
94
|
+
// compiling.
|
|
95
|
+
expect(propsOf("Agent").get("permission_policy")?.tsType).toBe(
|
|
96
|
+
'Record<string, "ask" | "auto_allow" | "auto_deny" | number>',
|
|
97
|
+
);
|
|
98
|
+
expect(parsed.some((r) => r.resource.typeName === "Fountain::V1::PermissionPolicy")).toBe(false);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("refuses an extension once upstream declares the prop itself", () => {
|
|
102
|
+
const spec = JSON.parse(snapshot);
|
|
103
|
+
spec.components.schemas.VaultRequest.properties.secrets = { type: "object" };
|
|
104
|
+
expect(() => parseFountainOpenAPI(JSON.stringify(spec))).toThrow(/VaultRequest now declares "secrets"/);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
describe("the generated declarations accept what the skill tells an author to write", () => {
|
|
109
|
+
// The .d.ts is what a consumer of the published package compiles against.
|
|
110
|
+
// In-repo, "../generated/index" resolves to the untyped runtime barrel, so a
|
|
111
|
+
// plain import would check nothing. This compiles a probe against a copy of
|
|
112
|
+
// the declaration instead. Every accepted line needed a cast at v0.16.0.
|
|
113
|
+
const dts = join(dirname(snapshotFile), "..", "generated", "index.d.ts");
|
|
114
|
+
|
|
115
|
+
function compile(body: string): string[] {
|
|
116
|
+
const dir = mkdtempSync(join(scratch, "dts-"));
|
|
117
|
+
writeFileSync(join(dir, "decl.d.ts"), readFileSync(dts, "utf-8"));
|
|
118
|
+
writeFileSync(
|
|
119
|
+
join(dir, "probe.ts"),
|
|
120
|
+
`import type { Agent, Environment, Vault } from "./decl";\n` +
|
|
121
|
+
`type EnvProps = ConstructorParameters<typeof Environment>[0];\n` +
|
|
122
|
+
`type VaultProps = ConstructorParameters<typeof Vault>[0];\n` +
|
|
123
|
+
`type AgentProps = ConstructorParameters<typeof Agent>[0];\n` +
|
|
124
|
+
body,
|
|
125
|
+
);
|
|
126
|
+
const program = ts.createProgram([join(dir, "probe.ts")], {
|
|
127
|
+
strict: true,
|
|
128
|
+
noEmit: true,
|
|
129
|
+
target: ts.ScriptTarget.ES2022,
|
|
130
|
+
module: ts.ModuleKind.ESNext,
|
|
131
|
+
moduleResolution: ts.ModuleResolutionKind.Bundler,
|
|
132
|
+
types: [],
|
|
133
|
+
});
|
|
134
|
+
return ts
|
|
135
|
+
.getPreEmitDiagnostics(program)
|
|
136
|
+
.map((d) => ts.flattenDiagnosticMessageText(d.messageText, "\n"));
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
it.skipIf(!existsSync(dts))("with no cast", () => {
|
|
140
|
+
const errors = compile(`
|
|
141
|
+
export const env: EnvProps = {
|
|
142
|
+
name: "box",
|
|
143
|
+
setup_timeout_seconds: 900,
|
|
144
|
+
secrets: [{ key: "GITHUB_TOKEN", value: "infisical:///dev/GITHUB_TOKEN" }],
|
|
145
|
+
};
|
|
146
|
+
export const vault: VaultProps = { name: "creds", secrets: [{ key: "NPM_TOKEN", value: "infisical:///dev/NPM_TOKEN" }] };
|
|
147
|
+
// No model: v0.21.0 stopped requiring one, and an acp agent has none.
|
|
148
|
+
export const agent: AgentProps = {
|
|
149
|
+
name: "steward",
|
|
150
|
+
runtime: "acp",
|
|
151
|
+
runtime_command: "chant acp",
|
|
152
|
+
permission_policy: { default: "auto_allow", ask_timeout: 600 },
|
|
153
|
+
};
|
|
154
|
+
`);
|
|
155
|
+
expect(errors).toEqual([]);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it.skipIf(!existsSync(dts))("and refuse the wire map in place of the authored list", () => {
|
|
159
|
+
// The probe has to be able to fail, or the case above proves nothing.
|
|
160
|
+
const errors = compile(`export const vault: VaultProps = { name: "creds", secrets: { NPM_TOKEN: "x" } };`);
|
|
161
|
+
expect(errors.join("\n")).toMatch(/NPM_TOKEN.*\{ key: string; value: string; \}\[\]/);
|
|
162
|
+
});
|
|
163
|
+
});
|