@aaac/contracts 0.1.13 → 0.1.14
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-JWJLY3HJ.js} +467 -5
- package/dist/chunk-JWJLY3HJ.js.map +1 -0
- package/dist/cli/index.js +41 -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 +1 -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,350 @@ 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 existsSync2 } from "fs";
|
|
1574
|
+
import { chmod, mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
1575
|
+
import { dirname as dirname5, join as join3, resolve as resolve3 } from "path";
|
|
1576
|
+
import { parse as parseYaml3 } from "yaml";
|
|
1577
|
+
import { z as z2 } from "zod";
|
|
1578
|
+
var MANAGED_BLOCK_START = "# >>> aaac-governance:managed (do not edit) >>>";
|
|
1579
|
+
var MANAGED_BLOCK_END = "# <<< aaac-governance:managed <<<";
|
|
1580
|
+
var GOVERNANCE_LOCK_PATH = ".agent-logs/governance-manifest.lock";
|
|
1581
|
+
var DEFAULT_RECORDER = ".cursor/hooks/observ-record.sh";
|
|
1582
|
+
var DEFAULT_CURSOR_HOOKS_JSON = ".cursor/hooks.json";
|
|
1583
|
+
var DEFAULT_BINARY_DEST = "bin/aaac.js";
|
|
1584
|
+
var CURSOR_HOOKS_VERSION = 1;
|
|
1585
|
+
var EnvironmentsSchema = z2.object({
|
|
1586
|
+
git: z2.object({ hooks: z2.array(z2.string()).optional() }).optional(),
|
|
1587
|
+
cursor: z2.object({ hooks_json: z2.string().optional() }).optional()
|
|
1588
|
+
}).optional();
|
|
1589
|
+
var BindingsObservabilitySchema = z2.object({
|
|
1590
|
+
event_mapping: z2.string().optional(),
|
|
1591
|
+
recorder: z2.string().optional()
|
|
1592
|
+
}).optional();
|
|
1593
|
+
var BindingsSchema = z2.object({
|
|
1594
|
+
observability: BindingsObservabilitySchema,
|
|
1595
|
+
hooks: z2.array(z2.string()).optional()
|
|
1596
|
+
}).optional();
|
|
1597
|
+
var InitProjectConfigSchema = z2.object({
|
|
1598
|
+
environments: EnvironmentsSchema,
|
|
1599
|
+
bindings: BindingsSchema
|
|
1600
|
+
});
|
|
1601
|
+
async function loadInitConfig(configPath) {
|
|
1602
|
+
const absPath = resolve3(configPath);
|
|
1603
|
+
const raw = await readFile2(absPath, "utf8");
|
|
1604
|
+
const parsed = parseYaml3(raw);
|
|
1605
|
+
return InitProjectConfigSchema.parse(parsed ?? {});
|
|
1606
|
+
}
|
|
1607
|
+
var HookCommandSchema = z2.object({
|
|
1608
|
+
command: z2.string(),
|
|
1609
|
+
description: z2.string().optional()
|
|
1610
|
+
});
|
|
1611
|
+
var HookBindingSchema = z2.object({
|
|
1612
|
+
hooks: z2.record(z2.string(), z2.array(HookCommandSchema)).optional()
|
|
1613
|
+
});
|
|
1614
|
+
async function loadHookBindings(projectRoot, bindingPaths) {
|
|
1615
|
+
const result = {};
|
|
1616
|
+
if (!bindingPaths?.length) return result;
|
|
1617
|
+
const root = resolve3(projectRoot);
|
|
1618
|
+
for (const bindingPath of bindingPaths) {
|
|
1619
|
+
const absPath = resolve3(root, bindingPath);
|
|
1620
|
+
if (!existsSync2(absPath)) continue;
|
|
1621
|
+
const raw = await readFile2(absPath, "utf8");
|
|
1622
|
+
const parsed = HookBindingSchema.parse(parseYaml3(raw) ?? {});
|
|
1623
|
+
if (!parsed.hooks) continue;
|
|
1624
|
+
for (const [hookName, commands] of Object.entries(parsed.hooks)) {
|
|
1625
|
+
const list = result[hookName] ?? [];
|
|
1626
|
+
for (const { command } of commands) {
|
|
1627
|
+
if (!list.includes(command)) list.push(command);
|
|
1628
|
+
}
|
|
1629
|
+
result[hookName] = list;
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
return result;
|
|
1633
|
+
}
|
|
1634
|
+
function escapeRegex(value) {
|
|
1635
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1636
|
+
}
|
|
1637
|
+
function createManagedBlockRegex() {
|
|
1638
|
+
return new RegExp(
|
|
1639
|
+
`${escapeRegex(MANAGED_BLOCK_START)}[\\s\\S]*?${escapeRegex(MANAGED_BLOCK_END)}\\n?`,
|
|
1640
|
+
"g"
|
|
1641
|
+
);
|
|
1642
|
+
}
|
|
1643
|
+
function buildManagedBlock(blockContent) {
|
|
1644
|
+
return `${MANAGED_BLOCK_START}
|
|
1645
|
+
${blockContent.trimEnd()}
|
|
1646
|
+
${MANAGED_BLOCK_END}
|
|
1647
|
+
`;
|
|
1648
|
+
}
|
|
1649
|
+
function injectManagedBlock(existingContent, blockContent) {
|
|
1650
|
+
const managedBlock = buildManagedBlock(blockContent);
|
|
1651
|
+
const re = createManagedBlockRegex();
|
|
1652
|
+
if (re.test(existingContent)) {
|
|
1653
|
+
return existingContent.replace(createManagedBlockRegex(), managedBlock);
|
|
1654
|
+
}
|
|
1655
|
+
const trimmed = existingContent.replace(/\s+$/, "");
|
|
1656
|
+
if (trimmed.length === 0) {
|
|
1657
|
+
return `#!/bin/sh
|
|
1658
|
+
${managedBlock}`;
|
|
1659
|
+
}
|
|
1660
|
+
const separator = trimmed.endsWith("\n") ? "" : "\n";
|
|
1661
|
+
return `${trimmed}${separator}${managedBlock}`;
|
|
1662
|
+
}
|
|
1663
|
+
function removeManagedBlock(existingContent) {
|
|
1664
|
+
if (!existingContent.includes(MANAGED_BLOCK_START)) {
|
|
1665
|
+
return existingContent;
|
|
1666
|
+
}
|
|
1667
|
+
const withoutBlock = existingContent.replace(createManagedBlockRegex(), "");
|
|
1668
|
+
const meaningful = withoutBlock.split("\n").filter((line) => {
|
|
1669
|
+
const t = line.trim();
|
|
1670
|
+
return t.length > 0 && t !== "#!/bin/sh" && t !== "#!/bin/bash";
|
|
1671
|
+
});
|
|
1672
|
+
if (meaningful.length === 0) {
|
|
1673
|
+
return "";
|
|
1674
|
+
}
|
|
1675
|
+
return withoutBlock.replace(/\n{3,}/g, "\n\n").replace(/\s+$/, "\n");
|
|
1676
|
+
}
|
|
1677
|
+
function parseCursorHooksFile(raw) {
|
|
1678
|
+
if (!raw || !raw.trim()) {
|
|
1679
|
+
return { version: CURSOR_HOOKS_VERSION, hooks: {} };
|
|
1680
|
+
}
|
|
1681
|
+
const parsed = JSON.parse(raw);
|
|
1682
|
+
if (Array.isArray(parsed)) {
|
|
1683
|
+
return flatEntriesToFile(
|
|
1684
|
+
parsed.filter(
|
|
1685
|
+
(entry) => entry !== null && typeof entry === "object" && typeof entry.event === "string" && typeof entry.command === "string"
|
|
1686
|
+
)
|
|
1687
|
+
);
|
|
1688
|
+
}
|
|
1689
|
+
if (parsed && typeof parsed === "object") {
|
|
1690
|
+
const obj = parsed;
|
|
1691
|
+
const version = typeof obj.version === "number" ? obj.version : CURSOR_HOOKS_VERSION;
|
|
1692
|
+
const hooks = {};
|
|
1693
|
+
const rawHooks = obj.hooks;
|
|
1694
|
+
if (rawHooks && typeof rawHooks === "object" && !Array.isArray(rawHooks)) {
|
|
1695
|
+
for (const [event, commands] of Object.entries(rawHooks)) {
|
|
1696
|
+
if (Array.isArray(commands)) {
|
|
1697
|
+
hooks[event] = commands.filter(
|
|
1698
|
+
(c) => c !== null && typeof c === "object" && typeof c.command === "string"
|
|
1699
|
+
);
|
|
1700
|
+
}
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1703
|
+
return { version, hooks };
|
|
1704
|
+
}
|
|
1705
|
+
return { version: CURSOR_HOOKS_VERSION, hooks: {} };
|
|
1706
|
+
}
|
|
1707
|
+
function serializeCursorHooksFile(file) {
|
|
1708
|
+
return JSON.stringify(file, null, 2) + "\n";
|
|
1709
|
+
}
|
|
1710
|
+
function flatEntriesToFile(entries) {
|
|
1711
|
+
return mergeCursorHooks({ version: CURSOR_HOOKS_VERSION, hooks: {} }, entries);
|
|
1712
|
+
}
|
|
1713
|
+
function mergeCursorHooks(existing, newEntries) {
|
|
1714
|
+
const hooks = {};
|
|
1715
|
+
for (const [event, commands] of Object.entries(existing.hooks)) {
|
|
1716
|
+
hooks[event] = commands.map((c) => ({ ...c }));
|
|
1717
|
+
}
|
|
1718
|
+
for (const entry of newEntries) {
|
|
1719
|
+
const { event, ...command } = entry;
|
|
1720
|
+
const list = hooks[event] ?? [];
|
|
1721
|
+
const idx = list.findIndex((c) => c.command === command.command);
|
|
1722
|
+
if (idx >= 0) {
|
|
1723
|
+
list[idx] = command;
|
|
1724
|
+
} else {
|
|
1725
|
+
list.push(command);
|
|
1726
|
+
}
|
|
1727
|
+
hooks[event] = list;
|
|
1728
|
+
}
|
|
1729
|
+
return { version: existing.version ?? CURSOR_HOOKS_VERSION, hooks };
|
|
1730
|
+
}
|
|
1731
|
+
function removeCursorHooks(existing, commands) {
|
|
1732
|
+
const removeSet = new Set(commands);
|
|
1733
|
+
const hooks = {};
|
|
1734
|
+
for (const [event, list] of Object.entries(existing.hooks)) {
|
|
1735
|
+
const filtered = list.filter((c) => !removeSet.has(c.command));
|
|
1736
|
+
if (filtered.length > 0) {
|
|
1737
|
+
hooks[event] = filtered;
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1740
|
+
return { version: existing.version ?? CURSOR_HOOKS_VERSION, hooks };
|
|
1741
|
+
}
|
|
1742
|
+
function buildGitHookBlock(hookName, recorder, gateCommands = []) {
|
|
1743
|
+
const lines = [`${recorder} ${hookName} "$@" || true`];
|
|
1744
|
+
for (const command of gateCommands) {
|
|
1745
|
+
lines.push(`${command} || exit 1`);
|
|
1746
|
+
}
|
|
1747
|
+
return lines.join("\n");
|
|
1748
|
+
}
|
|
1749
|
+
function buildDefaultCursorHooks(recorder) {
|
|
1750
|
+
return [
|
|
1751
|
+
{ event: "beforeSubmitPrompt", command: `${recorder} beforeSubmitPrompt` },
|
|
1752
|
+
{ event: "afterShellExecution", command: `${recorder} afterShellExecution` }
|
|
1753
|
+
];
|
|
1754
|
+
}
|
|
1755
|
+
async function ensureParentDir(filePath) {
|
|
1756
|
+
await mkdir2(dirname5(filePath), { recursive: true });
|
|
1757
|
+
}
|
|
1758
|
+
async function readTextFileIfExists(filePath) {
|
|
1759
|
+
if (!existsSync2(filePath)) return void 0;
|
|
1760
|
+
return readFile2(filePath, "utf8");
|
|
1761
|
+
}
|
|
1762
|
+
async function writeGovernanceLock(projectRoot, entries) {
|
|
1763
|
+
const lockPath = join3(projectRoot, GOVERNANCE_LOCK_PATH);
|
|
1764
|
+
const lock = {
|
|
1765
|
+
materialized_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1766
|
+
entries
|
|
1767
|
+
};
|
|
1768
|
+
await ensureParentDir(lockPath);
|
|
1769
|
+
await writeFile2(lockPath, JSON.stringify(lock, null, 2) + "\n", "utf8");
|
|
1770
|
+
return GOVERNANCE_LOCK_PATH;
|
|
1771
|
+
}
|
|
1772
|
+
async function readGovernanceLock(projectRoot) {
|
|
1773
|
+
const lockPath = join3(projectRoot, GOVERNANCE_LOCK_PATH);
|
|
1774
|
+
if (!existsSync2(lockPath)) return void 0;
|
|
1775
|
+
const raw = await readFile2(lockPath, "utf8");
|
|
1776
|
+
if (!raw.trim()) return void 0;
|
|
1777
|
+
return JSON.parse(raw);
|
|
1778
|
+
}
|
|
1779
|
+
async function materializeHooks(options) {
|
|
1780
|
+
const projectRoot = resolve3(options.projectRoot);
|
|
1781
|
+
const result = { materialized: [], lockEntries: [] };
|
|
1782
|
+
const recorder = options.observabilityBinding?.recorder ?? DEFAULT_RECORDER;
|
|
1783
|
+
if (options.environments.git?.hooks?.length) {
|
|
1784
|
+
for (const hookName of options.environments.git.hooks) {
|
|
1785
|
+
const relPath = join3(".git/hooks", hookName);
|
|
1786
|
+
const absPath = join3(projectRoot, relPath);
|
|
1787
|
+
const blockContent = buildGitHookBlock(
|
|
1788
|
+
hookName,
|
|
1789
|
+
recorder,
|
|
1790
|
+
options.hookCommands?.[hookName]
|
|
1791
|
+
);
|
|
1792
|
+
const existing = await readTextFileIfExists(absPath) ?? "";
|
|
1793
|
+
const updated = injectManagedBlock(existing, blockContent);
|
|
1794
|
+
let action = "unchanged";
|
|
1795
|
+
if (!existsSync2(absPath)) {
|
|
1796
|
+
action = "created";
|
|
1797
|
+
} else if (updated !== existing) {
|
|
1798
|
+
action = "updated";
|
|
1799
|
+
}
|
|
1800
|
+
if (action !== "unchanged") {
|
|
1801
|
+
await ensureParentDir(absPath);
|
|
1802
|
+
await writeFile2(absPath, updated, "utf8");
|
|
1803
|
+
await chmod(absPath, 493);
|
|
1804
|
+
}
|
|
1805
|
+
result.materialized.push({ path: relPath, action });
|
|
1806
|
+
result.lockEntries.push({ path: relPath, type: "git-hook" });
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
if (options.environments.cursor) {
|
|
1810
|
+
const rawHooksJsonValue = options.environments.cursor.hooks_json;
|
|
1811
|
+
const hooksJsonPath = rawHooksJsonValue ? resolve3(projectRoot, rawHooksJsonValue) : join3(projectRoot, DEFAULT_CURSOR_HOOKS_JSON);
|
|
1812
|
+
const relPath = hooksJsonPath.startsWith(projectRoot + "/") ? hooksJsonPath.slice(projectRoot.length + 1) : DEFAULT_CURSOR_HOOKS_JSON;
|
|
1813
|
+
const newEntries = buildDefaultCursorHooks(recorder);
|
|
1814
|
+
const existingRaw = await readTextFileIfExists(hooksJsonPath);
|
|
1815
|
+
const existing = parseCursorHooksFile(existingRaw);
|
|
1816
|
+
const merged = mergeCursorHooks(existing, newEntries);
|
|
1817
|
+
const serialized = serializeCursorHooksFile(merged);
|
|
1818
|
+
let action = "unchanged";
|
|
1819
|
+
if (!existsSync2(hooksJsonPath)) {
|
|
1820
|
+
action = "created";
|
|
1821
|
+
} else if (serialized !== existingRaw) {
|
|
1822
|
+
action = "updated";
|
|
1823
|
+
}
|
|
1824
|
+
if (action !== "unchanged") {
|
|
1825
|
+
await ensureParentDir(hooksJsonPath);
|
|
1826
|
+
await writeFile2(hooksJsonPath, serialized, "utf8");
|
|
1827
|
+
}
|
|
1828
|
+
result.materialized.push({ path: relPath, action });
|
|
1829
|
+
result.lockEntries.push({ path: relPath, type: "cursor-hook" });
|
|
1830
|
+
}
|
|
1831
|
+
if (result.lockEntries.length > 0) {
|
|
1832
|
+
const lockExisted = existsSync2(join3(projectRoot, GOVERNANCE_LOCK_PATH));
|
|
1833
|
+
await writeGovernanceLock(projectRoot, result.lockEntries);
|
|
1834
|
+
result.materialized.push({
|
|
1835
|
+
path: GOVERNANCE_LOCK_PATH,
|
|
1836
|
+
action: lockExisted ? "updated" : "created"
|
|
1837
|
+
});
|
|
1838
|
+
result.lockEntries.push({ path: GOVERNANCE_LOCK_PATH, type: "generated" });
|
|
1839
|
+
}
|
|
1840
|
+
return result;
|
|
1841
|
+
}
|
|
1842
|
+
async function unmaterializeHooks(projectRoot, lockEntries) {
|
|
1843
|
+
const root = resolve3(projectRoot);
|
|
1844
|
+
for (const entry of lockEntries) {
|
|
1845
|
+
if (entry.type === "generated") {
|
|
1846
|
+
const absPath = join3(root, entry.path);
|
|
1847
|
+
if (existsSync2(absPath)) {
|
|
1848
|
+
await writeFile2(absPath, "", "utf8");
|
|
1849
|
+
}
|
|
1850
|
+
continue;
|
|
1851
|
+
}
|
|
1852
|
+
if (entry.type === "git-hook") {
|
|
1853
|
+
const absPath = join3(root, entry.path);
|
|
1854
|
+
const existing = await readTextFileIfExists(absPath);
|
|
1855
|
+
if (existing === void 0) continue;
|
|
1856
|
+
const updated = removeManagedBlock(existing);
|
|
1857
|
+
await writeFile2(absPath, updated, "utf8");
|
|
1858
|
+
continue;
|
|
1859
|
+
}
|
|
1860
|
+
if (entry.type === "cursor-hook") {
|
|
1861
|
+
const absPath = join3(root, entry.path);
|
|
1862
|
+
const existingRaw = await readTextFileIfExists(absPath);
|
|
1863
|
+
if (existingRaw === void 0) continue;
|
|
1864
|
+
const existing = parseCursorHooksFile(existingRaw);
|
|
1865
|
+
const commandsToRemove = buildDefaultCursorHooks(DEFAULT_RECORDER).map((h) => h.command);
|
|
1866
|
+
const updated = removeCursorHooks(existing, commandsToRemove);
|
|
1867
|
+
await writeFile2(absPath, serializeCursorHooksFile(updated), "utf8");
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
const lockPath = join3(root, GOVERNANCE_LOCK_PATH);
|
|
1871
|
+
if (existsSync2(lockPath)) {
|
|
1872
|
+
await writeFile2(lockPath, "", "utf8");
|
|
1873
|
+
}
|
|
1874
|
+
}
|
|
1875
|
+
async function copyCLIBinary(srcPath, projectRoot, destRelPath = DEFAULT_BINARY_DEST) {
|
|
1876
|
+
const dstPath = join3(projectRoot, destRelPath);
|
|
1877
|
+
const srcContent = await readFile2(srcPath);
|
|
1878
|
+
const srcHash = createHash("sha256").update(srcContent).digest("hex");
|
|
1879
|
+
if (existsSync2(dstPath)) {
|
|
1880
|
+
const dstContent = await readFile2(dstPath);
|
|
1881
|
+
const dstHash = createHash("sha256").update(dstContent).digest("hex");
|
|
1882
|
+
if (srcHash === dstHash) {
|
|
1883
|
+
return { path: destRelPath, action: "unchanged" };
|
|
1884
|
+
}
|
|
1885
|
+
await writeFile2(dstPath, srcContent);
|
|
1886
|
+
await chmod(dstPath, 493);
|
|
1887
|
+
return { path: destRelPath, action: "updated" };
|
|
1888
|
+
}
|
|
1889
|
+
await ensureParentDir(dstPath);
|
|
1890
|
+
await writeFile2(dstPath, srcContent);
|
|
1891
|
+
await chmod(dstPath, 493);
|
|
1892
|
+
return { path: destRelPath, action: "created" };
|
|
1893
|
+
}
|
|
1894
|
+
var SCAFFOLD_PROJECT_YAML = `schema: aaac/project/0.1
|
|
1895
|
+
|
|
1896
|
+
# Uncomment and configure to enable git hook governance:
|
|
1897
|
+
#
|
|
1898
|
+
# environments:
|
|
1899
|
+
# git:
|
|
1900
|
+
# hooks: [pre-commit, pre-push, post-commit]
|
|
1901
|
+
# cursor:
|
|
1902
|
+
# hooks_json: .cursor/hooks.json
|
|
1903
|
+
#
|
|
1904
|
+
# bindings:
|
|
1905
|
+
# observability:
|
|
1906
|
+
# event_mapping: ./bindings/observability.yaml
|
|
1907
|
+
# recorder: aaac-observ
|
|
1908
|
+
# hooks:
|
|
1909
|
+
# # Each file declares gate commands per git hook; a non-zero exit
|
|
1910
|
+
# # blocks the commit/push. Only hooks listed in environments.git.hooks
|
|
1911
|
+
# # are materialized.
|
|
1912
|
+
# - ./bindings/git.yaml
|
|
1913
|
+
`;
|
|
1914
|
+
|
|
1466
1915
|
export {
|
|
1467
1916
|
SUPPORTED_SCHEMA_VERSIONS,
|
|
1468
1917
|
SCHEMA_VERSION_PATTERN,
|
|
@@ -1470,6 +1919,7 @@ export {
|
|
|
1470
1919
|
OptionSchema,
|
|
1471
1920
|
ArtifactSlotValueSchema,
|
|
1472
1921
|
OperationSchema,
|
|
1922
|
+
BindingRefSchema,
|
|
1473
1923
|
ComponentSchema,
|
|
1474
1924
|
ComponentParseError,
|
|
1475
1925
|
parseComponentString,
|
|
@@ -1484,12 +1934,24 @@ export {
|
|
|
1484
1934
|
compileComponent,
|
|
1485
1935
|
compileComponentAsync,
|
|
1486
1936
|
compileComponentFile,
|
|
1937
|
+
bindingsGenerator,
|
|
1487
1938
|
cliGenerator,
|
|
1488
1939
|
clientGenerator,
|
|
1489
1940
|
handlerGenerator,
|
|
1490
1941
|
runtimeGenerator,
|
|
1491
1942
|
typesGenerator,
|
|
1492
1943
|
generateAll,
|
|
1493
|
-
writeGeneratedFiles
|
|
1944
|
+
writeGeneratedFiles,
|
|
1945
|
+
GOVERNANCE_LOCK_PATH,
|
|
1946
|
+
loadInitConfig,
|
|
1947
|
+
loadHookBindings,
|
|
1948
|
+
parseCursorHooksFile,
|
|
1949
|
+
serializeCursorHooksFile,
|
|
1950
|
+
mergeCursorHooks,
|
|
1951
|
+
readGovernanceLock,
|
|
1952
|
+
materializeHooks,
|
|
1953
|
+
unmaterializeHooks,
|
|
1954
|
+
copyCLIBinary,
|
|
1955
|
+
SCAFFOLD_PROJECT_YAML
|
|
1494
1956
|
};
|
|
1495
|
-
//# sourceMappingURL=chunk-
|
|
1957
|
+
//# sourceMappingURL=chunk-JWJLY3HJ.js.map
|