@aaac/contracts 0.1.13 → 0.1.15
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/dist/{chunk-MWZFJ2VL.js → chunk-F57V745S.js} +605 -5
- package/dist/chunk-F57V745S.js.map +1 -0
- package/dist/cli/index.js +43 -379
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +93 -1
- package/dist/index.js +11 -1
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
- package/dist/chunk-MWZFJ2VL.js.map +0 -1
|
@@ -64,6 +64,14 @@ var GovernanceSchema = z.object({
|
|
|
64
64
|
guardrails: z.boolean().optional(),
|
|
65
65
|
observability: z.boolean().optional()
|
|
66
66
|
}).optional();
|
|
67
|
+
var BindingRefSchema = z.object({
|
|
68
|
+
/** npm package name implementing the binding (e.g. "@aaac/binding-cursor"). */
|
|
69
|
+
package: z.string().min(1),
|
|
70
|
+
/** CLI command name to expose (defaults to the binding id). */
|
|
71
|
+
command: z.string().optional(),
|
|
72
|
+
/** Binding-specific configuration; validated by the binding package. */
|
|
73
|
+
config: z.record(z.string(), z.unknown()).default({})
|
|
74
|
+
});
|
|
67
75
|
var ComponentSchema = z.object({
|
|
68
76
|
schema: z.string().regex(SCHEMA_VERSION_PATTERN, 'schema must match pattern "aaac/x.y"'),
|
|
69
77
|
info: z.object({
|
|
@@ -77,6 +85,7 @@ var ComponentSchema = z.object({
|
|
|
77
85
|
z.enum(["library", "cli", "claude", "openai"])
|
|
78
86
|
),
|
|
79
87
|
governance: GovernanceSchema,
|
|
88
|
+
bindings: z.record(z.string(), BindingRefSchema).optional(),
|
|
80
89
|
operations: z.record(z.string(), OperationSchema)
|
|
81
90
|
}).superRefine((component, ctx) => {
|
|
82
91
|
const usesAgentic = Object.values(component.operations).some(
|
|
@@ -148,6 +157,7 @@ function normalizeComponent(raw) {
|
|
|
148
157
|
embedded_dsl_dir: raw.embedded_dsl_dir,
|
|
149
158
|
projections: raw.projections,
|
|
150
159
|
governance: raw.governance,
|
|
160
|
+
bindings: raw.bindings,
|
|
151
161
|
operations: Object.keys(normalizedOps).length > 0 ? normalizedOps : raw.operations
|
|
152
162
|
};
|
|
153
163
|
}
|
|
@@ -568,9 +578,21 @@ function compileComponent(dsl, options = {}) {
|
|
|
568
578
|
guardrails: dsl.governance.guardrails,
|
|
569
579
|
observability: dsl.governance.observability
|
|
570
580
|
} : void 0,
|
|
581
|
+
bindings: dsl.bindings ? compileBindings(dsl.bindings) : void 0,
|
|
571
582
|
operations
|
|
572
583
|
};
|
|
573
584
|
}
|
|
585
|
+
function compileBindings(bindings) {
|
|
586
|
+
const result = {};
|
|
587
|
+
for (const [id, binding] of Object.entries(bindings)) {
|
|
588
|
+
result[id] = {
|
|
589
|
+
package: binding.package,
|
|
590
|
+
command: binding.command ?? id,
|
|
591
|
+
config: binding.config ?? {}
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
return result;
|
|
595
|
+
}
|
|
574
596
|
async function maybeValidateComponent(ir, options) {
|
|
575
597
|
if (options.validate === false) {
|
|
576
598
|
return;
|
|
@@ -593,6 +615,44 @@ async function compileComponentFile(filePath, options = {}) {
|
|
|
593
615
|
return ir;
|
|
594
616
|
}
|
|
595
617
|
|
|
618
|
+
// src/generators/bindings-generator.ts
|
|
619
|
+
var bindingsGenerator = {
|
|
620
|
+
generate(ir, _options) {
|
|
621
|
+
if (!ir.bindings || Object.keys(ir.bindings).length === 0) {
|
|
622
|
+
return [];
|
|
623
|
+
}
|
|
624
|
+
const entries = Object.entries(ir.bindings).map(([id, binding]) => {
|
|
625
|
+
return ` ${JSON.stringify(id)}: {
|
|
626
|
+
package: ${JSON.stringify(binding.package)},
|
|
627
|
+
command: ${JSON.stringify(binding.command)},
|
|
628
|
+
config: ${JSON.stringify(binding.config)},
|
|
629
|
+
},`;
|
|
630
|
+
});
|
|
631
|
+
const content = `// Auto-generated by @aaac/contracts. Do not edit.
|
|
632
|
+
|
|
633
|
+
export interface BindingEntry {
|
|
634
|
+
/** npm package implementing the binding. */
|
|
635
|
+
package: string;
|
|
636
|
+
/** CLI command name exposed for this binding. */
|
|
637
|
+
command: string;
|
|
638
|
+
/** Binding-specific configuration (validated by the binding package). */
|
|
639
|
+
config: Record<string, unknown>;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
export const bindings: Record<string, BindingEntry> = {
|
|
643
|
+
${entries.join("\n")}
|
|
644
|
+
};
|
|
645
|
+
`;
|
|
646
|
+
return [
|
|
647
|
+
{
|
|
648
|
+
path: "bindings.generated.ts",
|
|
649
|
+
content,
|
|
650
|
+
overwrite: true
|
|
651
|
+
}
|
|
652
|
+
];
|
|
653
|
+
}
|
|
654
|
+
};
|
|
655
|
+
|
|
596
656
|
// src/generators/cli-generator.ts
|
|
597
657
|
import { stringify as stringifyYaml } from "yaml";
|
|
598
658
|
function unwrapSchema(ref) {
|
|
@@ -878,6 +938,49 @@ function buildPromptCommand() {
|
|
|
878
938
|
function countAgenticOperations(ir) {
|
|
879
939
|
return Object.values(ir.operations).filter((op) => !op.handler).length;
|
|
880
940
|
}
|
|
941
|
+
function buildBindingCommands(ir) {
|
|
942
|
+
const commands = {};
|
|
943
|
+
if (!ir.bindings) {
|
|
944
|
+
return commands;
|
|
945
|
+
}
|
|
946
|
+
for (const [bindingId, binding] of Object.entries(ir.bindings)) {
|
|
947
|
+
const cmd = binding.command;
|
|
948
|
+
commands[`${cmd}.sync`] = {
|
|
949
|
+
summary: `Materialize ${bindingId} binding output files from the embedded DSL`,
|
|
950
|
+
options: [
|
|
951
|
+
{
|
|
952
|
+
name: "dry-run",
|
|
953
|
+
schema: { type: "boolean" },
|
|
954
|
+
description: "Report changes without writing files"
|
|
955
|
+
}
|
|
956
|
+
],
|
|
957
|
+
exits: {
|
|
958
|
+
"0": { description: "Synced" },
|
|
959
|
+
"1": { description: "Materialization failed" }
|
|
960
|
+
},
|
|
961
|
+
"x-agent": {
|
|
962
|
+
risk_level: "low",
|
|
963
|
+
requires_confirmation: false,
|
|
964
|
+
idempotent: true,
|
|
965
|
+
binding: bindingId
|
|
966
|
+
}
|
|
967
|
+
};
|
|
968
|
+
commands[`${cmd}.check`] = {
|
|
969
|
+
summary: `Fail if ${bindingId} binding output drifts from the DSL`,
|
|
970
|
+
exits: {
|
|
971
|
+
"0": { description: "In sync" },
|
|
972
|
+
"2": { description: "Drift detected" }
|
|
973
|
+
},
|
|
974
|
+
"x-agent": {
|
|
975
|
+
risk_level: "low",
|
|
976
|
+
requires_confirmation: false,
|
|
977
|
+
idempotent: true,
|
|
978
|
+
binding: bindingId
|
|
979
|
+
}
|
|
980
|
+
};
|
|
981
|
+
}
|
|
982
|
+
return commands;
|
|
983
|
+
}
|
|
881
984
|
function buildCliContract(ir) {
|
|
882
985
|
const commands = {};
|
|
883
986
|
for (const [name, op] of Object.entries(ir.operations)) {
|
|
@@ -886,6 +989,7 @@ function buildCliContract(ir) {
|
|
|
886
989
|
if (ir.governance?.guardrails === true) {
|
|
887
990
|
Object.assign(commands, buildGuardCommands());
|
|
888
991
|
}
|
|
992
|
+
Object.assign(commands, buildBindingCommands(ir));
|
|
889
993
|
if (countAgenticOperations(ir) >= 2) {
|
|
890
994
|
commands.prompt = buildPromptCommand();
|
|
891
995
|
}
|
|
@@ -1079,7 +1183,7 @@ var clientGenerator = {
|
|
|
1079
1183
|
ctx: AaacInvocationContext,
|
|
1080
1184
|
memoryRef?: string,
|
|
1081
1185
|
): Promise<${names.output}> {
|
|
1082
|
-
return runtime.execute(${JSON.stringify(operationName)}, options, input, ctx, memoryRef)
|
|
1186
|
+
return runtime.execute(${JSON.stringify(operationName)}, options, input, ctx, memoryRef) as Promise<${names.output}>;
|
|
1083
1187
|
}`;
|
|
1084
1188
|
});
|
|
1085
1189
|
if (agenticOpsCount >= 2) {
|
|
@@ -1088,7 +1192,7 @@ var clientGenerator = {
|
|
|
1088
1192
|
input: { message: string },
|
|
1089
1193
|
ctx: AaacInvocationContext,
|
|
1090
1194
|
): Promise<PromptResult> {
|
|
1091
|
-
return runtime.executePrompt(input.message, options, ctx) as Promise<PromptResult>;
|
|
1195
|
+
return runtime.executePrompt(input.message, options as unknown as Record<string, unknown>, ctx) as Promise<PromptResult>;
|
|
1092
1196
|
}`);
|
|
1093
1197
|
}
|
|
1094
1198
|
const runtimeTypeImports = agenticOpsCount >= 2 ? `import type { PromptResult, PromptExecuteOptions } from "@aaac/runtime";
|
|
@@ -1426,7 +1530,8 @@ var ALL_GENERATORS = [
|
|
|
1426
1530
|
runtimeGenerator,
|
|
1427
1531
|
cliGenerator,
|
|
1428
1532
|
handlerGenerator,
|
|
1429
|
-
embeddedDslGenerator
|
|
1533
|
+
embeddedDslGenerator,
|
|
1534
|
+
bindingsGenerator
|
|
1430
1535
|
];
|
|
1431
1536
|
function generateAll(ir, options) {
|
|
1432
1537
|
return ALL_GENERATORS.flatMap((gen) => gen.generate(ir, options));
|
|
@@ -1463,6 +1568,488 @@ async function writeGeneratedFiles(files, outputDir) {
|
|
|
1463
1568
|
return { written, skipped, warnings };
|
|
1464
1569
|
}
|
|
1465
1570
|
|
|
1571
|
+
// src/cli/governance.ts
|
|
1572
|
+
import { createHash } from "crypto";
|
|
1573
|
+
import { existsSync as existsSync3 } from "fs";
|
|
1574
|
+
import { chmod, mkdir as mkdir3, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
|
|
1575
|
+
import { dirname as dirname6, join as join4, resolve as resolve4 } from "path";
|
|
1576
|
+
import { parse as parseYaml4 } from "yaml";
|
|
1577
|
+
import { z as z3 } from "zod";
|
|
1578
|
+
|
|
1579
|
+
// src/cli/observ-config.ts
|
|
1580
|
+
import { existsSync as existsSync2 } from "fs";
|
|
1581
|
+
import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
1582
|
+
import { dirname as dirname5, join as join3, resolve as resolve3 } from "path";
|
|
1583
|
+
import { parse as parseYaml3 } from "yaml";
|
|
1584
|
+
import { z as z2 } from "zod";
|
|
1585
|
+
var OBSERV_CONFIG_DIR = ".agent-logs/config";
|
|
1586
|
+
var EVENT_MAPPING_JSON = `${OBSERV_CONFIG_DIR}/event-mapping.json`;
|
|
1587
|
+
var ARTIFACT_LOOKUP_JSON = `${OBSERV_CONFIG_DIR}/artifact-lookup.json`;
|
|
1588
|
+
var TASK_PATTERNS_JSON = `${OBSERV_CONFIG_DIR}/task-patterns.json`;
|
|
1589
|
+
function generateTaskPatterns() {
|
|
1590
|
+
return [
|
|
1591
|
+
{ tag_prefix: "DSL_TASK", field: "task.id" },
|
|
1592
|
+
{ tag_prefix: "AGENT_ROLE", field: "task.target_agent" },
|
|
1593
|
+
{ tag_prefix: "WORKFLOW_PHASE", field: "workflow.phase" }
|
|
1594
|
+
];
|
|
1595
|
+
}
|
|
1596
|
+
function generateArtifactLookup(artifactDeclarations) {
|
|
1597
|
+
const patterns = [];
|
|
1598
|
+
for (const decl of artifactDeclarations) {
|
|
1599
|
+
for (const pattern of decl.path_patterns) {
|
|
1600
|
+
patterns.push({ artifact_id: decl.artifact_id, pattern });
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
return patterns;
|
|
1604
|
+
}
|
|
1605
|
+
function extractEventMappingRules(parsed) {
|
|
1606
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
1607
|
+
const obj = parsed;
|
|
1608
|
+
const rules = obj.event_mapping ?? obj.mappings;
|
|
1609
|
+
if (rules && typeof rules === "object") {
|
|
1610
|
+
return rules;
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
return {};
|
|
1614
|
+
}
|
|
1615
|
+
var ArtifactContractsSchema = z2.object({
|
|
1616
|
+
artifacts: z2.record(
|
|
1617
|
+
z2.string(),
|
|
1618
|
+
z2.object({ path_patterns: z2.array(z2.string()).optional() }).passthrough()
|
|
1619
|
+
).optional()
|
|
1620
|
+
});
|
|
1621
|
+
function parseArtifactDeclarations(parsed) {
|
|
1622
|
+
const { artifacts } = ArtifactContractsSchema.parse(parsed ?? {});
|
|
1623
|
+
if (!artifacts) return [];
|
|
1624
|
+
const decls = [];
|
|
1625
|
+
for (const [artifact_id, decl] of Object.entries(artifacts)) {
|
|
1626
|
+
if (decl.path_patterns && decl.path_patterns.length > 0) {
|
|
1627
|
+
decls.push({ artifact_id, path_patterns: decl.path_patterns });
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
return decls;
|
|
1631
|
+
}
|
|
1632
|
+
function serialize(value) {
|
|
1633
|
+
return JSON.stringify(value, null, 2) + "\n";
|
|
1634
|
+
}
|
|
1635
|
+
async function writeJsonIfChanged(absPath, value) {
|
|
1636
|
+
const serialized = serialize(value);
|
|
1637
|
+
const existed = existsSync2(absPath);
|
|
1638
|
+
const existing = existed ? await readFile2(absPath, "utf8") : void 0;
|
|
1639
|
+
if (existing === serialized) {
|
|
1640
|
+
return "unchanged";
|
|
1641
|
+
}
|
|
1642
|
+
await mkdir2(dirname5(absPath), { recursive: true });
|
|
1643
|
+
await writeFile2(absPath, serialized, "utf8");
|
|
1644
|
+
return existed ? "updated" : "created";
|
|
1645
|
+
}
|
|
1646
|
+
async function generateObservConfig(options) {
|
|
1647
|
+
const root = resolve3(options.projectRoot);
|
|
1648
|
+
const generated = [];
|
|
1649
|
+
if (options.eventMapping) {
|
|
1650
|
+
const bindingPath = resolve3(root, options.eventMapping);
|
|
1651
|
+
if (existsSync2(bindingPath)) {
|
|
1652
|
+
const parsed = parseYaml3(await readFile2(bindingPath, "utf8"));
|
|
1653
|
+
const mappings = extractEventMappingRules(parsed);
|
|
1654
|
+
const action = await writeJsonIfChanged(
|
|
1655
|
+
join3(root, EVENT_MAPPING_JSON),
|
|
1656
|
+
{ mappings }
|
|
1657
|
+
);
|
|
1658
|
+
generated.push({ path: EVENT_MAPPING_JSON, action });
|
|
1659
|
+
}
|
|
1660
|
+
}
|
|
1661
|
+
if (options.artifactContracts) {
|
|
1662
|
+
const contractsPath = resolve3(root, options.artifactContracts);
|
|
1663
|
+
if (existsSync2(contractsPath)) {
|
|
1664
|
+
const parsed = parseYaml3(await readFile2(contractsPath, "utf8"));
|
|
1665
|
+
const lookup = generateArtifactLookup(parseArtifactDeclarations(parsed));
|
|
1666
|
+
const action = await writeJsonIfChanged(
|
|
1667
|
+
join3(root, ARTIFACT_LOOKUP_JSON),
|
|
1668
|
+
lookup
|
|
1669
|
+
);
|
|
1670
|
+
generated.push({ path: ARTIFACT_LOOKUP_JSON, action });
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
{
|
|
1674
|
+
const action = await writeJsonIfChanged(
|
|
1675
|
+
join3(root, TASK_PATTERNS_JSON),
|
|
1676
|
+
generateTaskPatterns()
|
|
1677
|
+
);
|
|
1678
|
+
generated.push({ path: TASK_PATTERNS_JSON, action });
|
|
1679
|
+
}
|
|
1680
|
+
const lockEntries = generated.map((g) => ({
|
|
1681
|
+
path: g.path,
|
|
1682
|
+
type: "generated"
|
|
1683
|
+
}));
|
|
1684
|
+
return { generated, lockEntries };
|
|
1685
|
+
}
|
|
1686
|
+
|
|
1687
|
+
// src/cli/governance.ts
|
|
1688
|
+
var MANAGED_BLOCK_START = "# >>> aaac-governance:managed (do not edit) >>>";
|
|
1689
|
+
var MANAGED_BLOCK_END = "# <<< aaac-governance:managed <<<";
|
|
1690
|
+
var GOVERNANCE_LOCK_PATH = ".agent-logs/governance-manifest.lock";
|
|
1691
|
+
var DEFAULT_RECORDER = ".cursor/hooks/observ-record.sh";
|
|
1692
|
+
var DEFAULT_CURSOR_HOOKS_JSON = ".cursor/hooks.json";
|
|
1693
|
+
var DEFAULT_BINARY_DEST = "bin/aaac.js";
|
|
1694
|
+
var CURSOR_HOOKS_VERSION = 1;
|
|
1695
|
+
var EnvironmentsSchema = z3.object({
|
|
1696
|
+
git: z3.object({ hooks: z3.array(z3.string()).optional() }).optional(),
|
|
1697
|
+
cursor: z3.object({ hooks_json: z3.string().optional() }).optional()
|
|
1698
|
+
}).optional();
|
|
1699
|
+
var BindingsObservabilitySchema = z3.object({
|
|
1700
|
+
event_mapping: z3.string().optional(),
|
|
1701
|
+
recorder: z3.string().optional()
|
|
1702
|
+
}).optional();
|
|
1703
|
+
var BindingsSchema = z3.object({
|
|
1704
|
+
observability: BindingsObservabilitySchema,
|
|
1705
|
+
hooks: z3.array(z3.string()).optional()
|
|
1706
|
+
}).optional();
|
|
1707
|
+
var InitProjectConfigSchema = z3.object({
|
|
1708
|
+
environments: EnvironmentsSchema,
|
|
1709
|
+
bindings: BindingsSchema,
|
|
1710
|
+
artifacts: z3.string().optional()
|
|
1711
|
+
});
|
|
1712
|
+
async function loadInitConfig(configPath) {
|
|
1713
|
+
const absPath = resolve4(configPath);
|
|
1714
|
+
const raw = await readFile3(absPath, "utf8");
|
|
1715
|
+
const parsed = parseYaml4(raw);
|
|
1716
|
+
return InitProjectConfigSchema.parse(parsed ?? {});
|
|
1717
|
+
}
|
|
1718
|
+
var HookCommandSchema = z3.object({
|
|
1719
|
+
command: z3.string(),
|
|
1720
|
+
description: z3.string().optional()
|
|
1721
|
+
});
|
|
1722
|
+
var HookBindingSchema = z3.object({
|
|
1723
|
+
hooks: z3.record(z3.string(), z3.array(HookCommandSchema)).optional()
|
|
1724
|
+
});
|
|
1725
|
+
async function loadHookBindings(projectRoot, bindingPaths) {
|
|
1726
|
+
const result = {};
|
|
1727
|
+
if (!bindingPaths?.length) return result;
|
|
1728
|
+
const root = resolve4(projectRoot);
|
|
1729
|
+
for (const bindingPath of bindingPaths) {
|
|
1730
|
+
const absPath = resolve4(root, bindingPath);
|
|
1731
|
+
if (!existsSync3(absPath)) continue;
|
|
1732
|
+
const raw = await readFile3(absPath, "utf8");
|
|
1733
|
+
const parsed = HookBindingSchema.parse(parseYaml4(raw) ?? {});
|
|
1734
|
+
if (!parsed.hooks) continue;
|
|
1735
|
+
for (const [hookName, commands] of Object.entries(parsed.hooks)) {
|
|
1736
|
+
const list = result[hookName] ?? [];
|
|
1737
|
+
for (const { command } of commands) {
|
|
1738
|
+
if (!list.includes(command)) list.push(command);
|
|
1739
|
+
}
|
|
1740
|
+
result[hookName] = list;
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1743
|
+
return result;
|
|
1744
|
+
}
|
|
1745
|
+
function escapeRegex(value) {
|
|
1746
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1747
|
+
}
|
|
1748
|
+
function createManagedBlockRegex() {
|
|
1749
|
+
return new RegExp(
|
|
1750
|
+
`${escapeRegex(MANAGED_BLOCK_START)}[\\s\\S]*?${escapeRegex(MANAGED_BLOCK_END)}\\n?`,
|
|
1751
|
+
"g"
|
|
1752
|
+
);
|
|
1753
|
+
}
|
|
1754
|
+
function buildManagedBlock(blockContent) {
|
|
1755
|
+
return `${MANAGED_BLOCK_START}
|
|
1756
|
+
${blockContent.trimEnd()}
|
|
1757
|
+
${MANAGED_BLOCK_END}
|
|
1758
|
+
`;
|
|
1759
|
+
}
|
|
1760
|
+
function injectManagedBlock(existingContent, blockContent) {
|
|
1761
|
+
const managedBlock = buildManagedBlock(blockContent);
|
|
1762
|
+
const re = createManagedBlockRegex();
|
|
1763
|
+
if (re.test(existingContent)) {
|
|
1764
|
+
return existingContent.replace(createManagedBlockRegex(), managedBlock);
|
|
1765
|
+
}
|
|
1766
|
+
const trimmed = existingContent.replace(/\s+$/, "");
|
|
1767
|
+
if (trimmed.length === 0) {
|
|
1768
|
+
return `#!/bin/sh
|
|
1769
|
+
${managedBlock}`;
|
|
1770
|
+
}
|
|
1771
|
+
const separator = trimmed.endsWith("\n") ? "" : "\n";
|
|
1772
|
+
return `${trimmed}${separator}${managedBlock}`;
|
|
1773
|
+
}
|
|
1774
|
+
function removeManagedBlock(existingContent) {
|
|
1775
|
+
if (!existingContent.includes(MANAGED_BLOCK_START)) {
|
|
1776
|
+
return existingContent;
|
|
1777
|
+
}
|
|
1778
|
+
const withoutBlock = existingContent.replace(createManagedBlockRegex(), "");
|
|
1779
|
+
const meaningful = withoutBlock.split("\n").filter((line) => {
|
|
1780
|
+
const t = line.trim();
|
|
1781
|
+
return t.length > 0 && t !== "#!/bin/sh" && t !== "#!/bin/bash";
|
|
1782
|
+
});
|
|
1783
|
+
if (meaningful.length === 0) {
|
|
1784
|
+
return "";
|
|
1785
|
+
}
|
|
1786
|
+
return withoutBlock.replace(/\n{3,}/g, "\n\n").replace(/\s+$/, "\n");
|
|
1787
|
+
}
|
|
1788
|
+
function parseCursorHooksFile(raw) {
|
|
1789
|
+
if (!raw || !raw.trim()) {
|
|
1790
|
+
return { version: CURSOR_HOOKS_VERSION, hooks: {} };
|
|
1791
|
+
}
|
|
1792
|
+
const parsed = JSON.parse(raw);
|
|
1793
|
+
if (Array.isArray(parsed)) {
|
|
1794
|
+
return flatEntriesToFile(
|
|
1795
|
+
parsed.filter(
|
|
1796
|
+
(entry) => entry !== null && typeof entry === "object" && typeof entry.event === "string" && typeof entry.command === "string"
|
|
1797
|
+
)
|
|
1798
|
+
);
|
|
1799
|
+
}
|
|
1800
|
+
if (parsed && typeof parsed === "object") {
|
|
1801
|
+
const obj = parsed;
|
|
1802
|
+
const version = typeof obj.version === "number" ? obj.version : CURSOR_HOOKS_VERSION;
|
|
1803
|
+
const hooks = {};
|
|
1804
|
+
const rawHooks = obj.hooks;
|
|
1805
|
+
if (rawHooks && typeof rawHooks === "object" && !Array.isArray(rawHooks)) {
|
|
1806
|
+
for (const [event, commands] of Object.entries(rawHooks)) {
|
|
1807
|
+
if (Array.isArray(commands)) {
|
|
1808
|
+
hooks[event] = commands.filter(
|
|
1809
|
+
(c) => c !== null && typeof c === "object" && typeof c.command === "string"
|
|
1810
|
+
);
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1813
|
+
}
|
|
1814
|
+
return { version, hooks };
|
|
1815
|
+
}
|
|
1816
|
+
return { version: CURSOR_HOOKS_VERSION, hooks: {} };
|
|
1817
|
+
}
|
|
1818
|
+
function serializeCursorHooksFile(file) {
|
|
1819
|
+
return JSON.stringify(file, null, 2) + "\n";
|
|
1820
|
+
}
|
|
1821
|
+
function flatEntriesToFile(entries) {
|
|
1822
|
+
return mergeCursorHooks({ version: CURSOR_HOOKS_VERSION, hooks: {} }, entries);
|
|
1823
|
+
}
|
|
1824
|
+
function mergeCursorHooks(existing, newEntries) {
|
|
1825
|
+
const hooks = {};
|
|
1826
|
+
for (const [event, commands] of Object.entries(existing.hooks)) {
|
|
1827
|
+
hooks[event] = commands.map((c) => ({ ...c }));
|
|
1828
|
+
}
|
|
1829
|
+
for (const entry of newEntries) {
|
|
1830
|
+
const { event, ...command } = entry;
|
|
1831
|
+
const list = hooks[event] ?? [];
|
|
1832
|
+
const idx = list.findIndex((c) => c.command === command.command);
|
|
1833
|
+
if (idx >= 0) {
|
|
1834
|
+
list[idx] = command;
|
|
1835
|
+
} else {
|
|
1836
|
+
list.push(command);
|
|
1837
|
+
}
|
|
1838
|
+
hooks[event] = list;
|
|
1839
|
+
}
|
|
1840
|
+
return { version: existing.version ?? CURSOR_HOOKS_VERSION, hooks };
|
|
1841
|
+
}
|
|
1842
|
+
function removeCursorHooks(existing, commands) {
|
|
1843
|
+
const removeSet = new Set(commands);
|
|
1844
|
+
const hooks = {};
|
|
1845
|
+
for (const [event, list] of Object.entries(existing.hooks)) {
|
|
1846
|
+
const filtered = list.filter((c) => !removeSet.has(c.command));
|
|
1847
|
+
if (filtered.length > 0) {
|
|
1848
|
+
hooks[event] = filtered;
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1851
|
+
return { version: existing.version ?? CURSOR_HOOKS_VERSION, hooks };
|
|
1852
|
+
}
|
|
1853
|
+
function buildGitHookBlock(hookName, recorder, gateCommands = []) {
|
|
1854
|
+
const lines = [`${recorder} ${hookName} "$@" || true`];
|
|
1855
|
+
for (const command of gateCommands) {
|
|
1856
|
+
lines.push(`${command} || exit 1`);
|
|
1857
|
+
}
|
|
1858
|
+
return lines.join("\n");
|
|
1859
|
+
}
|
|
1860
|
+
var DEFAULT_CURSOR_HOOK_EVENTS = [
|
|
1861
|
+
"sessionStart",
|
|
1862
|
+
"sessionEnd",
|
|
1863
|
+
"subagentStart",
|
|
1864
|
+
"subagentStop",
|
|
1865
|
+
"beforeSubmitPrompt",
|
|
1866
|
+
"afterShellExecution",
|
|
1867
|
+
"preToolUse",
|
|
1868
|
+
"postToolUse",
|
|
1869
|
+
"postToolUseFailure",
|
|
1870
|
+
"afterFileEdit",
|
|
1871
|
+
"preCompact",
|
|
1872
|
+
"stop"
|
|
1873
|
+
];
|
|
1874
|
+
function buildDefaultCursorHooks(recorder) {
|
|
1875
|
+
return DEFAULT_CURSOR_HOOK_EVENTS.map((event) => ({
|
|
1876
|
+
event,
|
|
1877
|
+
command: `${recorder} ${event}`
|
|
1878
|
+
}));
|
|
1879
|
+
}
|
|
1880
|
+
async function ensureParentDir(filePath) {
|
|
1881
|
+
await mkdir3(dirname6(filePath), { recursive: true });
|
|
1882
|
+
}
|
|
1883
|
+
async function readTextFileIfExists(filePath) {
|
|
1884
|
+
if (!existsSync3(filePath)) return void 0;
|
|
1885
|
+
return readFile3(filePath, "utf8");
|
|
1886
|
+
}
|
|
1887
|
+
async function writeGovernanceLock(projectRoot, entries) {
|
|
1888
|
+
const lockPath = join4(projectRoot, GOVERNANCE_LOCK_PATH);
|
|
1889
|
+
const lock = {
|
|
1890
|
+
materialized_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1891
|
+
entries
|
|
1892
|
+
};
|
|
1893
|
+
await ensureParentDir(lockPath);
|
|
1894
|
+
await writeFile3(lockPath, JSON.stringify(lock, null, 2) + "\n", "utf8");
|
|
1895
|
+
return GOVERNANCE_LOCK_PATH;
|
|
1896
|
+
}
|
|
1897
|
+
async function readGovernanceLock(projectRoot) {
|
|
1898
|
+
const lockPath = join4(projectRoot, GOVERNANCE_LOCK_PATH);
|
|
1899
|
+
if (!existsSync3(lockPath)) return void 0;
|
|
1900
|
+
const raw = await readFile3(lockPath, "utf8");
|
|
1901
|
+
if (!raw.trim()) return void 0;
|
|
1902
|
+
return JSON.parse(raw);
|
|
1903
|
+
}
|
|
1904
|
+
async function materializeHooks(options) {
|
|
1905
|
+
const projectRoot = resolve4(options.projectRoot);
|
|
1906
|
+
const result = { materialized: [], lockEntries: [] };
|
|
1907
|
+
const recorder = options.observabilityBinding?.recorder ?? DEFAULT_RECORDER;
|
|
1908
|
+
if (options.environments.git?.hooks?.length) {
|
|
1909
|
+
for (const hookName of options.environments.git.hooks) {
|
|
1910
|
+
const relPath = join4(".git/hooks", hookName);
|
|
1911
|
+
const absPath = join4(projectRoot, relPath);
|
|
1912
|
+
const blockContent = buildGitHookBlock(
|
|
1913
|
+
hookName,
|
|
1914
|
+
recorder,
|
|
1915
|
+
options.hookCommands?.[hookName]
|
|
1916
|
+
);
|
|
1917
|
+
const existing = await readTextFileIfExists(absPath) ?? "";
|
|
1918
|
+
const updated = injectManagedBlock(existing, blockContent);
|
|
1919
|
+
let action = "unchanged";
|
|
1920
|
+
if (!existsSync3(absPath)) {
|
|
1921
|
+
action = "created";
|
|
1922
|
+
} else if (updated !== existing) {
|
|
1923
|
+
action = "updated";
|
|
1924
|
+
}
|
|
1925
|
+
if (action !== "unchanged") {
|
|
1926
|
+
await ensureParentDir(absPath);
|
|
1927
|
+
await writeFile3(absPath, updated, "utf8");
|
|
1928
|
+
await chmod(absPath, 493);
|
|
1929
|
+
}
|
|
1930
|
+
result.materialized.push({ path: relPath, action });
|
|
1931
|
+
result.lockEntries.push({ path: relPath, type: "git-hook" });
|
|
1932
|
+
}
|
|
1933
|
+
}
|
|
1934
|
+
if (options.environments.cursor) {
|
|
1935
|
+
const rawHooksJsonValue = options.environments.cursor.hooks_json;
|
|
1936
|
+
const hooksJsonPath = rawHooksJsonValue ? resolve4(projectRoot, rawHooksJsonValue) : join4(projectRoot, DEFAULT_CURSOR_HOOKS_JSON);
|
|
1937
|
+
const relPath = hooksJsonPath.startsWith(projectRoot + "/") ? hooksJsonPath.slice(projectRoot.length + 1) : DEFAULT_CURSOR_HOOKS_JSON;
|
|
1938
|
+
const newEntries = buildDefaultCursorHooks(recorder);
|
|
1939
|
+
const existingRaw = await readTextFileIfExists(hooksJsonPath);
|
|
1940
|
+
const existing = parseCursorHooksFile(existingRaw);
|
|
1941
|
+
const merged = mergeCursorHooks(existing, newEntries);
|
|
1942
|
+
const serialized = serializeCursorHooksFile(merged);
|
|
1943
|
+
let action = "unchanged";
|
|
1944
|
+
if (!existsSync3(hooksJsonPath)) {
|
|
1945
|
+
action = "created";
|
|
1946
|
+
} else if (serialized !== existingRaw) {
|
|
1947
|
+
action = "updated";
|
|
1948
|
+
}
|
|
1949
|
+
if (action !== "unchanged") {
|
|
1950
|
+
await ensureParentDir(hooksJsonPath);
|
|
1951
|
+
await writeFile3(hooksJsonPath, serialized, "utf8");
|
|
1952
|
+
}
|
|
1953
|
+
result.materialized.push({ path: relPath, action });
|
|
1954
|
+
result.lockEntries.push({ path: relPath, type: "cursor-hook" });
|
|
1955
|
+
}
|
|
1956
|
+
if (options.observabilityBinding) {
|
|
1957
|
+
const { generated, lockEntries } = await generateObservConfig({
|
|
1958
|
+
projectRoot,
|
|
1959
|
+
eventMapping: options.observabilityBinding.event_mapping,
|
|
1960
|
+
artifactContracts: options.artifactContracts
|
|
1961
|
+
});
|
|
1962
|
+
for (const g of generated) {
|
|
1963
|
+
result.materialized.push({ path: g.path, action: g.action });
|
|
1964
|
+
}
|
|
1965
|
+
result.lockEntries.push(...lockEntries);
|
|
1966
|
+
}
|
|
1967
|
+
if (result.lockEntries.length > 0) {
|
|
1968
|
+
const lockExisted = existsSync3(join4(projectRoot, GOVERNANCE_LOCK_PATH));
|
|
1969
|
+
await writeGovernanceLock(projectRoot, result.lockEntries);
|
|
1970
|
+
result.materialized.push({
|
|
1971
|
+
path: GOVERNANCE_LOCK_PATH,
|
|
1972
|
+
action: lockExisted ? "updated" : "created"
|
|
1973
|
+
});
|
|
1974
|
+
result.lockEntries.push({ path: GOVERNANCE_LOCK_PATH, type: "generated" });
|
|
1975
|
+
}
|
|
1976
|
+
return result;
|
|
1977
|
+
}
|
|
1978
|
+
async function unmaterializeHooks(projectRoot, lockEntries) {
|
|
1979
|
+
const root = resolve4(projectRoot);
|
|
1980
|
+
for (const entry of lockEntries) {
|
|
1981
|
+
if (entry.type === "generated") {
|
|
1982
|
+
const absPath = join4(root, entry.path);
|
|
1983
|
+
if (existsSync3(absPath)) {
|
|
1984
|
+
await writeFile3(absPath, "", "utf8");
|
|
1985
|
+
}
|
|
1986
|
+
continue;
|
|
1987
|
+
}
|
|
1988
|
+
if (entry.type === "git-hook") {
|
|
1989
|
+
const absPath = join4(root, entry.path);
|
|
1990
|
+
const existing = await readTextFileIfExists(absPath);
|
|
1991
|
+
if (existing === void 0) continue;
|
|
1992
|
+
const updated = removeManagedBlock(existing);
|
|
1993
|
+
await writeFile3(absPath, updated, "utf8");
|
|
1994
|
+
continue;
|
|
1995
|
+
}
|
|
1996
|
+
if (entry.type === "cursor-hook") {
|
|
1997
|
+
const absPath = join4(root, entry.path);
|
|
1998
|
+
const existingRaw = await readTextFileIfExists(absPath);
|
|
1999
|
+
if (existingRaw === void 0) continue;
|
|
2000
|
+
const existing = parseCursorHooksFile(existingRaw);
|
|
2001
|
+
const commandsToRemove = buildDefaultCursorHooks(DEFAULT_RECORDER).map((h) => h.command);
|
|
2002
|
+
const updated = removeCursorHooks(existing, commandsToRemove);
|
|
2003
|
+
await writeFile3(absPath, serializeCursorHooksFile(updated), "utf8");
|
|
2004
|
+
}
|
|
2005
|
+
}
|
|
2006
|
+
const lockPath = join4(root, GOVERNANCE_LOCK_PATH);
|
|
2007
|
+
if (existsSync3(lockPath)) {
|
|
2008
|
+
await writeFile3(lockPath, "", "utf8");
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
2011
|
+
async function copyCLIBinary(srcPath, projectRoot, destRelPath = DEFAULT_BINARY_DEST) {
|
|
2012
|
+
const dstPath = join4(projectRoot, destRelPath);
|
|
2013
|
+
const srcContent = await readFile3(srcPath);
|
|
2014
|
+
const srcHash = createHash("sha256").update(srcContent).digest("hex");
|
|
2015
|
+
if (existsSync3(dstPath)) {
|
|
2016
|
+
const dstContent = await readFile3(dstPath);
|
|
2017
|
+
const dstHash = createHash("sha256").update(dstContent).digest("hex");
|
|
2018
|
+
if (srcHash === dstHash) {
|
|
2019
|
+
return { path: destRelPath, action: "unchanged" };
|
|
2020
|
+
}
|
|
2021
|
+
await writeFile3(dstPath, srcContent);
|
|
2022
|
+
await chmod(dstPath, 493);
|
|
2023
|
+
return { path: destRelPath, action: "updated" };
|
|
2024
|
+
}
|
|
2025
|
+
await ensureParentDir(dstPath);
|
|
2026
|
+
await writeFile3(dstPath, srcContent);
|
|
2027
|
+
await chmod(dstPath, 493);
|
|
2028
|
+
return { path: destRelPath, action: "created" };
|
|
2029
|
+
}
|
|
2030
|
+
var SCAFFOLD_PROJECT_YAML = `schema: aaac/project/0.1
|
|
2031
|
+
|
|
2032
|
+
# Uncomment and configure to enable git hook governance:
|
|
2033
|
+
#
|
|
2034
|
+
# environments:
|
|
2035
|
+
# git:
|
|
2036
|
+
# # commit-msg/post-commit are observability-only (promotion axis): post-commit
|
|
2037
|
+
# # records promotion.commit + promotion.file, feeding Enricher R1/R2/R5.
|
|
2038
|
+
# hooks: [pre-commit, commit-msg, post-commit, pre-push]
|
|
2039
|
+
# cursor:
|
|
2040
|
+
# hooks_json: .cursor/hooks.json
|
|
2041
|
+
#
|
|
2042
|
+
# bindings:
|
|
2043
|
+
# observability:
|
|
2044
|
+
# event_mapping: ./bindings/observability.yaml
|
|
2045
|
+
# recorder: aaac-observ
|
|
2046
|
+
# hooks:
|
|
2047
|
+
# # Each file declares gate commands per git hook; a non-zero exit
|
|
2048
|
+
# # blocks the commit/push. Only hooks listed in environments.git.hooks
|
|
2049
|
+
# # are materialized.
|
|
2050
|
+
# - ./bindings/git.yaml
|
|
2051
|
+
`;
|
|
2052
|
+
|
|
1466
2053
|
export {
|
|
1467
2054
|
SUPPORTED_SCHEMA_VERSIONS,
|
|
1468
2055
|
SCHEMA_VERSION_PATTERN,
|
|
@@ -1470,6 +2057,7 @@ export {
|
|
|
1470
2057
|
OptionSchema,
|
|
1471
2058
|
ArtifactSlotValueSchema,
|
|
1472
2059
|
OperationSchema,
|
|
2060
|
+
BindingRefSchema,
|
|
1473
2061
|
ComponentSchema,
|
|
1474
2062
|
ComponentParseError,
|
|
1475
2063
|
parseComponentString,
|
|
@@ -1484,12 +2072,24 @@ export {
|
|
|
1484
2072
|
compileComponent,
|
|
1485
2073
|
compileComponentAsync,
|
|
1486
2074
|
compileComponentFile,
|
|
2075
|
+
bindingsGenerator,
|
|
1487
2076
|
cliGenerator,
|
|
1488
2077
|
clientGenerator,
|
|
1489
2078
|
handlerGenerator,
|
|
1490
2079
|
runtimeGenerator,
|
|
1491
2080
|
typesGenerator,
|
|
1492
2081
|
generateAll,
|
|
1493
|
-
writeGeneratedFiles
|
|
2082
|
+
writeGeneratedFiles,
|
|
2083
|
+
GOVERNANCE_LOCK_PATH,
|
|
2084
|
+
loadInitConfig,
|
|
2085
|
+
loadHookBindings,
|
|
2086
|
+
parseCursorHooksFile,
|
|
2087
|
+
serializeCursorHooksFile,
|
|
2088
|
+
mergeCursorHooks,
|
|
2089
|
+
readGovernanceLock,
|
|
2090
|
+
materializeHooks,
|
|
2091
|
+
unmaterializeHooks,
|
|
2092
|
+
copyCLIBinary,
|
|
2093
|
+
SCAFFOLD_PROJECT_YAML
|
|
1494
2094
|
};
|
|
1495
|
-
//# sourceMappingURL=chunk-
|
|
2095
|
+
//# sourceMappingURL=chunk-F57V745S.js.map
|