agentplane 0.3.12 → 0.3.13
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/assets/RUNNER.md +1 -1
- package/assets/agents/ORCHESTRATOR.json +1 -1
- package/assets/codex-plugin/assets/header.png +0 -0
- package/assets/codex-plugin/assets/icon.svg +1 -0
- package/assets/codex-plugin/assets/logo.svg +1 -0
- package/assets/codex-plugin/skills/agentplane/SKILL.md +35 -0
- package/assets/policy/governance.md +4 -2
- package/assets/policy/incidents.md +3 -20
- package/assets/policy/workflow.release.md +5 -2
- package/dist/.build-manifest.json +203 -113
- package/dist/cli/exit-codes.d.ts.map +1 -1
- package/dist/cli/exit-codes.js +1 -0
- package/dist/cli/reason-codes.d.ts +1 -1
- package/dist/cli/reason-codes.d.ts.map +1 -1
- package/dist/cli/reason-codes.js +12 -0
- package/dist/cli/run-cli/command-catalog/core.d.ts +1 -1
- package/dist/cli/run-cli/command-catalog/core.d.ts.map +1 -1
- package/dist/cli/run-cli/command-catalog/core.js +16 -0
- package/dist/cli/run-cli/command-catalog/project.d.ts +1 -1
- package/dist/cli/run-cli/command-catalog/project.d.ts.map +1 -1
- package/dist/cli/run-cli/command-catalog/project.js +21 -3
- package/dist/cli/run-cli/command-catalog.d.ts +1 -1
- package/dist/cli/run-cli/command-catalog.d.ts.map +1 -1
- package/dist/cli/run-cli/commands/codex.d.ts +14 -0
- package/dist/cli/run-cli/commands/codex.d.ts.map +1 -0
- package/dist/cli/run-cli/commands/codex.js +100 -0
- package/dist/cli/run-cli/commands/core.d.ts +1 -0
- package/dist/cli/run-cli/commands/core.d.ts.map +1 -1
- package/dist/cli/run-cli/commands/core.js +1 -0
- package/dist/cli/run-cli/commands/init/recipes.d.ts +9 -1
- package/dist/cli/run-cli/commands/init/recipes.d.ts.map +1 -1
- package/dist/cli/run-cli/commands/init/recipes.js +32 -22
- package/dist/cli/run-cli/commands/init.d.ts.map +1 -1
- package/dist/cli/run-cli/commands/init.js +26 -21
- package/dist/cli/run-cli/error-guidance.js +20 -0
- package/dist/cli/run-cli.test-helpers.d.ts.map +1 -1
- package/dist/cli/run-cli.test-helpers.js +22 -19
- package/dist/commands/codex/plugin-install.d.ts +26 -0
- package/dist/commands/codex/plugin-install.d.ts.map +1 -0
- package/dist/commands/codex/plugin-install.js +209 -0
- package/dist/commands/pr/integrate/cmd.d.ts.map +1 -1
- package/dist/commands/pr/integrate/cmd.js +81 -5
- package/dist/commands/pr/integrate/internal/prepare.d.ts.map +1 -1
- package/dist/commands/pr/integrate/internal/prepare.js +38 -7
- package/dist/commands/pr/internal/auto-commit.d.ts.map +1 -1
- package/dist/commands/pr/internal/auto-commit.js +11 -6
- package/dist/commands/pr/internal/sync.d.ts.map +1 -1
- package/dist/commands/pr/internal/sync.js +5 -1
- package/dist/commands/pr/open.d.ts.map +1 -1
- package/dist/commands/pr/open.js +46 -8
- package/dist/commands/recipes/active.command.d.ts +7 -0
- package/dist/commands/recipes/active.command.d.ts.map +1 -0
- package/dist/commands/recipes/active.command.js +12 -0
- package/dist/commands/recipes/add.command.d.ts +8 -0
- package/dist/commands/recipes/add.command.d.ts.map +1 -0
- package/dist/commands/recipes/add.command.js +33 -0
- package/dist/commands/recipes/detach.command.d.ts +7 -0
- package/dist/commands/recipes/detach.command.d.ts.map +1 -0
- package/dist/commands/recipes/detach.command.js +19 -0
- package/dist/commands/recipes/disable.command.d.ts +7 -0
- package/dist/commands/recipes/disable.command.d.ts.map +1 -0
- package/dist/commands/recipes/disable.command.js +10 -0
- package/dist/commands/recipes/enable.command.d.ts +7 -0
- package/dist/commands/recipes/enable.command.d.ts.map +1 -0
- package/dist/commands/recipes/enable.command.js +10 -0
- package/dist/commands/recipes/explain-active.command.d.ts +5 -0
- package/dist/commands/recipes/explain-active.command.d.ts.map +1 -0
- package/dist/commands/recipes/explain-active.command.js +11 -0
- package/dist/commands/recipes/explain.command.d.ts.map +1 -1
- package/dist/commands/recipes/explain.command.js +4 -2
- package/dist/commands/recipes/impl/apply.d.ts.map +1 -1
- package/dist/commands/recipes/impl/apply.js +33 -14
- package/dist/commands/recipes/impl/commands/active.d.ts +6 -0
- package/dist/commands/recipes/impl/commands/active.d.ts.map +1 -0
- package/dist/commands/recipes/impl/commands/active.js +46 -0
- package/dist/commands/recipes/impl/commands/add.d.ts +7 -0
- package/dist/commands/recipes/impl/commands/add.d.ts.map +1 -0
- package/dist/commands/recipes/impl/commands/add.js +100 -0
- package/dist/commands/recipes/impl/commands/detach.d.ts +6 -0
- package/dist/commands/recipes/impl/commands/detach.d.ts.map +1 -0
- package/dist/commands/recipes/impl/commands/detach.js +85 -0
- package/dist/commands/recipes/impl/commands/disable.d.ts +6 -0
- package/dist/commands/recipes/impl/commands/disable.d.ts.map +1 -0
- package/dist/commands/recipes/impl/commands/disable.js +21 -0
- package/dist/commands/recipes/impl/commands/enable.d.ts +6 -0
- package/dist/commands/recipes/impl/commands/enable.d.ts.map +1 -0
- package/dist/commands/recipes/impl/commands/enable.js +39 -0
- package/dist/commands/recipes/impl/commands/explain-active.d.ts +5 -0
- package/dist/commands/recipes/impl/commands/explain-active.d.ts.map +1 -0
- package/dist/commands/recipes/impl/commands/explain-active.js +20 -0
- package/dist/commands/recipes/impl/commands/explain.d.ts.map +1 -1
- package/dist/commands/recipes/impl/commands/explain.js +40 -3
- package/dist/commands/recipes/impl/commands/info.d.ts.map +1 -1
- package/dist/commands/recipes/impl/commands/info.js +21 -8
- package/dist/commands/recipes/impl/commands/install.d.ts.map +1 -1
- package/dist/commands/recipes/impl/commands/install.js +32 -29
- package/dist/commands/recipes/impl/commands/list.d.ts.map +1 -1
- package/dist/commands/recipes/impl/commands/list.js +11 -11
- package/dist/commands/recipes/impl/commands/remove.d.ts.map +1 -1
- package/dist/commands/recipes/impl/commands/remove.js +5 -0
- package/dist/commands/recipes/impl/commands/update.d.ts +7 -0
- package/dist/commands/recipes/impl/commands/update.d.ts.map +1 -0
- package/dist/commands/recipes/impl/commands/update.js +93 -0
- package/dist/commands/recipes/impl/commands.d.ts +7 -0
- package/dist/commands/recipes/impl/commands.d.ts.map +1 -1
- package/dist/commands/recipes/impl/commands.js +7 -0
- package/dist/commands/recipes/impl/constants.d.ts +1 -14
- package/dist/commands/recipes/impl/constants.d.ts.map +1 -1
- package/dist/commands/recipes/impl/constants.js +1 -18
- package/dist/commands/recipes/impl/manifest.d.ts +2 -2
- package/dist/commands/recipes/impl/manifest.d.ts.map +1 -1
- package/dist/commands/recipes/impl/manifest.js +4 -226
- package/dist/commands/recipes/impl/overlay-project.d.ts +32 -0
- package/dist/commands/recipes/impl/overlay-project.d.ts.map +1 -0
- package/dist/commands/recipes/impl/overlay-project.js +282 -0
- package/dist/commands/recipes/impl/paths.d.ts +20 -2
- package/dist/commands/recipes/impl/paths.d.ts.map +1 -1
- package/dist/commands/recipes/impl/paths.js +23 -5
- package/dist/commands/recipes/impl/project-installed-recipes.d.ts +2 -4
- package/dist/commands/recipes/impl/project-installed-recipes.d.ts.map +1 -1
- package/dist/commands/recipes/impl/project-installed-recipes.js +30 -74
- package/dist/commands/recipes/impl/project-recipe-state.d.ts +18 -0
- package/dist/commands/recipes/impl/project-recipe-state.d.ts.map +1 -0
- package/dist/commands/recipes/impl/project-recipe-state.js +94 -0
- package/dist/commands/recipes/impl/project-registry.d.ts +20 -0
- package/dist/commands/recipes/impl/project-registry.d.ts.map +1 -0
- package/dist/commands/recipes/impl/project-registry.js +104 -0
- package/dist/commands/recipes/impl/resolver.d.ts.map +1 -1
- package/dist/commands/recipes/impl/resolver.js +5 -3
- package/dist/commands/recipes/impl/types.d.ts +1 -240
- package/dist/commands/recipes/impl/types.d.ts.map +1 -1
- package/dist/commands/recipes/info.command.js +2 -2
- package/dist/commands/recipes/install.spec.js +4 -4
- package/dist/commands/recipes/list.command.js +4 -4
- package/dist/commands/recipes/remove.command.js +2 -2
- package/dist/commands/recipes/update.command.d.ts +8 -0
- package/dist/commands/recipes/update.command.d.ts.map +1 -0
- package/dist/commands/recipes/update.command.js +35 -0
- package/dist/commands/recipes.d.ts +7 -4
- package/dist/commands/recipes.d.ts.map +1 -1
- package/dist/commands/recipes.js +6 -3
- package/dist/commands/recipes.test-helpers.d.ts +3 -3
- package/dist/commands/recipes.test-helpers.d.ts.map +1 -1
- package/dist/commands/recipes.test-helpers.js +105 -15
- package/dist/commands/scenario/execute.command.js +4 -4
- package/dist/commands/scenario/impl/commands.js +4 -4
- package/dist/commands/scenario/info.command.js +4 -4
- package/dist/commands/scenario/list.command.js +3 -3
- package/dist/commands/scenario/run.command.js +5 -5
- package/dist/commands/scenario/scenario.command.js +7 -7
- package/dist/commands/shared/task-handoff.d.ts +2 -1
- package/dist/commands/shared/task-handoff.d.ts.map +1 -1
- package/dist/commands/shared/task-handoff.js +15 -0
- package/dist/commands/task/handoff-show.command.d.ts.map +1 -1
- package/dist/commands/task/handoff-show.command.js +24 -0
- package/dist/runner/context/base-prompts.d.ts +2 -1
- package/dist/runner/context/base-prompts.d.ts.map +1 -1
- package/dist/runner/context/base-prompts.js +109 -13
- package/dist/runner/context/recipe-context.d.ts.map +1 -1
- package/dist/runner/context/recipe-context.js +40 -8
- package/dist/runner/types.d.ts +4 -0
- package/dist/runner/types.d.ts.map +1 -1
- package/dist/runner/usecases/task-run.d.ts.map +1 -1
- package/dist/runner/usecases/task-run.js +2 -1
- package/dist/runtime/behavior/resolve.d.ts +2 -1
- package/dist/runtime/behavior/resolve.d.ts.map +1 -1
- package/dist/runtime/behavior/resolve.js +25 -5
- package/dist/runtime/behavior/types.d.ts +1 -0
- package/dist/runtime/behavior/types.d.ts.map +1 -1
- package/dist/runtime/capabilities/recipe.d.ts +2 -1
- package/dist/runtime/capabilities/recipe.d.ts.map +1 -1
- package/dist/runtime/capabilities/recipe.js +88 -28
- package/dist/shared/errors.d.ts +1 -1
- package/dist/shared/errors.d.ts.map +1 -1
- package/dist/shared/runtime-source.d.ts.map +1 -1
- package/dist/shared/runtime-source.js +8 -3
- package/package.json +3 -2
- package/dist/cli/recipes-bundled.d.ts +0 -10
- package/dist/cli/recipes-bundled.d.ts.map +0 -1
- package/dist/cli/recipes-bundled.js +0 -36
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import { mkdir, readFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { createEmptyOverlayBundle, hashOverlayInputs, } from "@agentplaneorg/recipes";
|
|
4
|
+
import { writeJsonStableIfChanged } from "../../../shared/write-if-changed.js";
|
|
5
|
+
import { readProjectInstalledRecipes } from "./project-installed-recipes.js";
|
|
6
|
+
import { readProjectRecipesRegistry, writeProjectRecipesRegistry } from "./project-registry.js";
|
|
7
|
+
import { resolveProjectInstalledRecipeDir, resolveProjectOverlayBundlePath, resolveProjectRecipeAssetsPath, resolveProjectRecipesDir, resolveProjectRecipesLockPath, } from "./paths.js";
|
|
8
|
+
function ensureTrailingNewline(text) {
|
|
9
|
+
return text.endsWith("\n") ? text : `${text}\n`;
|
|
10
|
+
}
|
|
11
|
+
function stripMarkdownFrontmatter(text) {
|
|
12
|
+
if (!text.startsWith("---\n"))
|
|
13
|
+
return text;
|
|
14
|
+
const end = text.indexOf("\n---\n", 4);
|
|
15
|
+
if (end === -1)
|
|
16
|
+
return text;
|
|
17
|
+
return text.slice(end + 5);
|
|
18
|
+
}
|
|
19
|
+
function normalizePromptContent(text) {
|
|
20
|
+
return ensureTrailingNewline(stripMarkdownFrontmatter(text).trim());
|
|
21
|
+
}
|
|
22
|
+
function normalizeRecipeAssetContent(text) {
|
|
23
|
+
return ensureTrailingNewline(text.trimEnd());
|
|
24
|
+
}
|
|
25
|
+
function uniqueById(items) {
|
|
26
|
+
const seen = new Set();
|
|
27
|
+
const result = [];
|
|
28
|
+
for (const item of items) {
|
|
29
|
+
if (seen.has(item.id))
|
|
30
|
+
continue;
|
|
31
|
+
seen.add(item.id);
|
|
32
|
+
result.push(item);
|
|
33
|
+
}
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
36
|
+
function recipeAssetId(recipeId, kind, assetId) {
|
|
37
|
+
return `recipe:${recipeId}/${kind}:${assetId}`;
|
|
38
|
+
}
|
|
39
|
+
function relativeSource(project, absolutePath) {
|
|
40
|
+
return path.relative(project.agentplaneDir, absolutePath).replaceAll("\\", "/");
|
|
41
|
+
}
|
|
42
|
+
async function compileProjectRecipeAssets(opts) {
|
|
43
|
+
const entries = [];
|
|
44
|
+
for (const recipe of opts.installed.recipes) {
|
|
45
|
+
const recipeDir = recipe.project_path
|
|
46
|
+
? path.join(resolveProjectRecipesDir(opts.project), recipe.project_path)
|
|
47
|
+
: resolveProjectInstalledRecipeDir(opts.project, recipe.id);
|
|
48
|
+
const manifest = recipe.manifest;
|
|
49
|
+
for (const agent of manifest.agents ?? []) {
|
|
50
|
+
const sourcePath = path.join(recipeDir, agent.file);
|
|
51
|
+
entries.push({
|
|
52
|
+
id: recipeAssetId(recipe.id, "agent", agent.id),
|
|
53
|
+
kind: "agent",
|
|
54
|
+
recipe_id: recipe.id,
|
|
55
|
+
recipe_version: recipe.version,
|
|
56
|
+
recipe_name: manifest.name,
|
|
57
|
+
asset_id: agent.id,
|
|
58
|
+
source: relativeSource(opts.project, sourcePath),
|
|
59
|
+
summary: agent.summary,
|
|
60
|
+
definition: agent,
|
|
61
|
+
content: normalizeRecipeAssetContent(await readFile(sourcePath, "utf8")),
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
for (const skill of manifest.skills ?? []) {
|
|
65
|
+
const sourcePath = path.join(recipeDir, skill.file);
|
|
66
|
+
entries.push({
|
|
67
|
+
id: recipeAssetId(recipe.id, "skill", skill.id),
|
|
68
|
+
kind: "skill",
|
|
69
|
+
recipe_id: recipe.id,
|
|
70
|
+
recipe_version: recipe.version,
|
|
71
|
+
recipe_name: manifest.name,
|
|
72
|
+
asset_id: skill.id,
|
|
73
|
+
source: relativeSource(opts.project, sourcePath),
|
|
74
|
+
summary: skill.summary,
|
|
75
|
+
definition: skill,
|
|
76
|
+
content: normalizeRecipeAssetContent(await readFile(sourcePath, "utf8")),
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
for (const tool of manifest.tools ?? []) {
|
|
80
|
+
entries.push({
|
|
81
|
+
id: recipeAssetId(recipe.id, "tool", tool.id),
|
|
82
|
+
kind: "tool",
|
|
83
|
+
recipe_id: recipe.id,
|
|
84
|
+
recipe_version: recipe.version,
|
|
85
|
+
recipe_name: manifest.name,
|
|
86
|
+
asset_id: tool.id,
|
|
87
|
+
source: relativeSource(opts.project, path.join(recipeDir, tool.entrypoint)),
|
|
88
|
+
summary: tool.summary,
|
|
89
|
+
definition: tool,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
for (const scenario of manifest.scenarios ?? []) {
|
|
93
|
+
entries.push({
|
|
94
|
+
id: recipeAssetId(recipe.id, "scenario", scenario.id),
|
|
95
|
+
kind: "scenario",
|
|
96
|
+
recipe_id: recipe.id,
|
|
97
|
+
recipe_version: recipe.version,
|
|
98
|
+
recipe_name: manifest.name,
|
|
99
|
+
asset_id: scenario.id,
|
|
100
|
+
source: relativeSource(opts.project, path.join(recipeDir, scenario.file)),
|
|
101
|
+
summary: scenario.summary,
|
|
102
|
+
definition: scenario,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
for (const [templateKey, content] of Object.entries(manifest.templates ?? {})) {
|
|
106
|
+
entries.push({
|
|
107
|
+
id: recipeAssetId(recipe.id, "template", templateKey),
|
|
108
|
+
kind: "template",
|
|
109
|
+
recipe_id: recipe.id,
|
|
110
|
+
recipe_version: recipe.version,
|
|
111
|
+
recipe_name: manifest.name,
|
|
112
|
+
asset_id: templateKey,
|
|
113
|
+
source: relativeSource(opts.project, path.join(recipeDir, "manifest.json")),
|
|
114
|
+
content,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
schema_version: 1,
|
|
120
|
+
kind: "recipe_asset_registry",
|
|
121
|
+
entries: uniqueById(entries).toSorted((left, right) => left.id.localeCompare(right.id)),
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
export async function readActiveRecipeIds(project) {
|
|
125
|
+
const registry = await readProjectRecipesRegistry(project);
|
|
126
|
+
return registry.recipes
|
|
127
|
+
.filter((entry) => entry.active)
|
|
128
|
+
.map((entry) => entry.id)
|
|
129
|
+
.toSorted();
|
|
130
|
+
}
|
|
131
|
+
export async function setRecipeActive(opts) {
|
|
132
|
+
const registry = await readProjectRecipesRegistry(opts.project);
|
|
133
|
+
const current = new Set();
|
|
134
|
+
const recipes = registry.recipes.map((entry) => {
|
|
135
|
+
const isTarget = entry.id === opts.recipeId;
|
|
136
|
+
const active = isTarget ? opts.active : entry.active;
|
|
137
|
+
if (active)
|
|
138
|
+
current.add(entry.id);
|
|
139
|
+
return isTarget ? { ...entry, active } : entry;
|
|
140
|
+
});
|
|
141
|
+
await writeProjectRecipesRegistry(opts.project, {
|
|
142
|
+
schema_version: 1,
|
|
143
|
+
updated_at: registry.updated_at,
|
|
144
|
+
recipes,
|
|
145
|
+
});
|
|
146
|
+
return [...current].toSorted();
|
|
147
|
+
}
|
|
148
|
+
export async function compileProjectOverlayArtifacts(project) {
|
|
149
|
+
const [activeIds, installed] = await Promise.all([
|
|
150
|
+
readActiveRecipeIds(project),
|
|
151
|
+
readProjectInstalledRecipes(project),
|
|
152
|
+
]);
|
|
153
|
+
const bundle = createEmptyOverlayBundle();
|
|
154
|
+
const lock = { schema_version: 1, active: [] };
|
|
155
|
+
for (const recipeId of activeIds) {
|
|
156
|
+
const entry = installed.recipes.find((candidate) => candidate.id === recipeId);
|
|
157
|
+
if (!entry) {
|
|
158
|
+
throw new Error(`Active overlay is not installed: ${recipeId}`);
|
|
159
|
+
}
|
|
160
|
+
if (entry.manifest.kind !== "project_overlay") {
|
|
161
|
+
throw new Error(`Active recipe ${recipeId} is not a project overlay`);
|
|
162
|
+
}
|
|
163
|
+
const manifest = entry.manifest;
|
|
164
|
+
const recipeDir = entry.project_path
|
|
165
|
+
? path.join(resolveProjectRecipesDir(project), entry.project_path)
|
|
166
|
+
: resolveProjectInstalledRecipeDir(project, entry.id);
|
|
167
|
+
const promptInputs = [];
|
|
168
|
+
if (manifest.requires) {
|
|
169
|
+
for (const required of manifest.requires) {
|
|
170
|
+
if (!activeIds.includes(required)) {
|
|
171
|
+
throw new Error(`Overlay ${manifest.id} requires active overlay ${required}`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
if (manifest.conflicts) {
|
|
176
|
+
for (const conflict of manifest.conflicts) {
|
|
177
|
+
if (activeIds.includes(conflict.recipe_id)) {
|
|
178
|
+
throw new Error(`Overlay ${manifest.id} conflicts with ${conflict.recipe_id}: ${conflict.reason}`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
for (const fragment of manifest.prompts ?? []) {
|
|
183
|
+
const absPath = path.join(recipeDir, fragment.file);
|
|
184
|
+
const content = normalizePromptContent(await readFile(absPath, "utf8"));
|
|
185
|
+
promptInputs.push({ id: fragment.id, content });
|
|
186
|
+
bundle.surfaces[fragment.surface].push({
|
|
187
|
+
...fragment,
|
|
188
|
+
recipe_id: manifest.id,
|
|
189
|
+
recipe_version: manifest.version,
|
|
190
|
+
recipe_name: manifest.name,
|
|
191
|
+
summary: manifest.summary,
|
|
192
|
+
source: path.relative(project.agentplaneDir, absPath).replaceAll("\\", "/"),
|
|
193
|
+
content,
|
|
194
|
+
});
|
|
195
|
+
bundle.trace.push({
|
|
196
|
+
recipe_id: manifest.id,
|
|
197
|
+
recipe_version: manifest.version,
|
|
198
|
+
accepted: true,
|
|
199
|
+
reason: "compiled prompt fragment",
|
|
200
|
+
source: fragment.file,
|
|
201
|
+
surface: fragment.surface,
|
|
202
|
+
fragment_id: fragment.id,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
for (const validator of manifest.validators ?? []) {
|
|
206
|
+
bundle.validators.push({
|
|
207
|
+
...validator,
|
|
208
|
+
recipe_id: manifest.id,
|
|
209
|
+
recipe_version: manifest.version,
|
|
210
|
+
});
|
|
211
|
+
bundle.trace.push({
|
|
212
|
+
recipe_id: manifest.id,
|
|
213
|
+
recipe_version: manifest.version,
|
|
214
|
+
accepted: true,
|
|
215
|
+
reason: "compiled validator",
|
|
216
|
+
validator_id: validator.id,
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
bundle.active.push({
|
|
220
|
+
id: manifest.id,
|
|
221
|
+
version: manifest.version,
|
|
222
|
+
name: manifest.name,
|
|
223
|
+
summary: manifest.summary,
|
|
224
|
+
});
|
|
225
|
+
bundle.agents.push(...(manifest.agents ?? []));
|
|
226
|
+
bundle.tools.push(...(manifest.tools ?? []));
|
|
227
|
+
Object.assign(bundle.templates, manifest.templates ?? {});
|
|
228
|
+
lock.active.push({
|
|
229
|
+
id: manifest.id,
|
|
230
|
+
version: manifest.version,
|
|
231
|
+
kind: manifest.kind,
|
|
232
|
+
source: entry.source,
|
|
233
|
+
hash: hashOverlayInputs({ manifest, prompts: promptInputs }),
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
bundle.agents = uniqueById(bundle.agents);
|
|
237
|
+
bundle.tools = uniqueById(bundle.tools);
|
|
238
|
+
for (const surface of Object.keys(bundle.surfaces)) {
|
|
239
|
+
bundle.surfaces[surface] = bundle.surfaces[surface].toSorted((left, right) => (left.order ?? 0) - (right.order ?? 0) ||
|
|
240
|
+
left.recipe_id.localeCompare(right.recipe_id) ||
|
|
241
|
+
left.id.localeCompare(right.id));
|
|
242
|
+
}
|
|
243
|
+
lock.active = lock.active.toSorted((left, right) => left.id.localeCompare(right.id));
|
|
244
|
+
return {
|
|
245
|
+
bundle,
|
|
246
|
+
lock,
|
|
247
|
+
assets: await compileProjectRecipeAssets({ project, installed }),
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
export async function refreshProjectOverlayArtifacts(project) {
|
|
251
|
+
const compiled = await compileProjectOverlayArtifacts(project);
|
|
252
|
+
const bundlePath = resolveProjectOverlayBundlePath(project);
|
|
253
|
+
const lockPath = resolveProjectRecipesLockPath(project);
|
|
254
|
+
const assetsPath = resolveProjectRecipeAssetsPath(project);
|
|
255
|
+
await mkdir(path.dirname(bundlePath), { recursive: true });
|
|
256
|
+
await writeJsonStableIfChanged(bundlePath, compiled.bundle);
|
|
257
|
+
await writeJsonStableIfChanged(lockPath, compiled.lock);
|
|
258
|
+
await writeJsonStableIfChanged(assetsPath, compiled.assets);
|
|
259
|
+
return compiled;
|
|
260
|
+
}
|
|
261
|
+
export async function readProjectOverlayBundle(project) {
|
|
262
|
+
try {
|
|
263
|
+
return JSON.parse(await readFile(resolveProjectOverlayBundlePath(project), "utf8"));
|
|
264
|
+
}
|
|
265
|
+
catch (err) {
|
|
266
|
+
const code = err?.code;
|
|
267
|
+
if (code === "ENOENT")
|
|
268
|
+
return null;
|
|
269
|
+
throw err;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
export async function readProjectRecipeAssetRegistry(project) {
|
|
273
|
+
try {
|
|
274
|
+
return JSON.parse(await readFile(resolveProjectRecipeAssetsPath(project), "utf8"));
|
|
275
|
+
}
|
|
276
|
+
catch (err) {
|
|
277
|
+
const code = err?.code;
|
|
278
|
+
if (code === "ENOENT")
|
|
279
|
+
return null;
|
|
280
|
+
throw err;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
@@ -10,13 +10,31 @@ export declare function resolveInstalledRecipeDir(entry: {
|
|
|
10
10
|
export declare function resolveProjectRecipesDir(resolved: {
|
|
11
11
|
agentplaneDir: string;
|
|
12
12
|
}): string;
|
|
13
|
-
export declare function
|
|
13
|
+
export declare function resolveProjectRecipesPackagesDir(resolved: {
|
|
14
|
+
agentplaneDir: string;
|
|
15
|
+
}): string;
|
|
16
|
+
export declare function resolveProjectRecipesRegistryPath(resolved: {
|
|
17
|
+
agentplaneDir: string;
|
|
18
|
+
}): string;
|
|
19
|
+
export declare function resolveProjectVendoredRecipeDir(resolved: {
|
|
14
20
|
agentplaneDir: string;
|
|
15
21
|
}, recipeId: string): string;
|
|
16
|
-
export declare function
|
|
22
|
+
export declare function resolveProjectInstalledRecipeDir(resolved: {
|
|
17
23
|
agentplaneDir: string;
|
|
18
24
|
}, recipeId: string): string;
|
|
19
25
|
export declare function resolveProjectRecipesCacheDir(resolved: {
|
|
20
26
|
agentplaneDir: string;
|
|
21
27
|
}): string;
|
|
28
|
+
export declare function resolveProjectRecipesLockPath(resolved: {
|
|
29
|
+
agentplaneDir: string;
|
|
30
|
+
}): string;
|
|
31
|
+
export declare function resolveProjectGeneratedDir(resolved: {
|
|
32
|
+
agentplaneDir: string;
|
|
33
|
+
}): string;
|
|
34
|
+
export declare function resolveProjectOverlayBundlePath(resolved: {
|
|
35
|
+
agentplaneDir: string;
|
|
36
|
+
}): string;
|
|
37
|
+
export declare function resolveProjectRecipeAssetsPath(resolved: {
|
|
38
|
+
agentplaneDir: string;
|
|
39
|
+
}): string;
|
|
22
40
|
//# sourceMappingURL=paths.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../../../src/commands/recipes/impl/paths.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../../../src/commands/recipes/impl/paths.ts"],"names":[],"mappings":"AAeA,wBAAgB,qBAAqB,IAAI,MAAM,CAI9C;AAED,wBAAgB,uBAAuB,IAAI,MAAM,CAEhD;AAED,wBAAgB,2BAA2B,IAAI,MAAM,CAEpD;AAED,wBAAgB,4BAA4B,IAAI,MAAM,CAErD;AAED,wBAAgB,+BAA+B,IAAI,MAAM,CAExD;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAExF;AAED,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE;IAAE,aAAa,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAEpF;AAED,wBAAgB,gCAAgC,CAAC,QAAQ,EAAE;IAAE,aAAa,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAE5F;AAED,wBAAgB,iCAAiC,CAAC,QAAQ,EAAE;IAAE,aAAa,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAE7F;AAED,wBAAgB,+BAA+B,CAC7C,QAAQ,EAAE;IAAE,aAAa,EAAE,MAAM,CAAA;CAAE,EACnC,QAAQ,EAAE,MAAM,GACf,MAAM,CAER;AAED,wBAAgB,gCAAgC,CAC9C,QAAQ,EAAE;IAAE,aAAa,EAAE,MAAM,CAAA;CAAE,EACnC,QAAQ,EAAE,MAAM,GACf,MAAM,CAER;AAED,wBAAgB,6BAA6B,CAAC,QAAQ,EAAE;IAAE,aAAa,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAEzF;AAED,wBAAgB,6BAA6B,CAAC,QAAQ,EAAE;IAAE,aAAa,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAEzF;AAED,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE;IAAE,aAAa,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAEtF;AAED,wBAAgB,+BAA+B,CAAC,QAAQ,EAAE;IAAE,aAAa,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAE3F;AAED,wBAAgB,8BAA8B,CAAC,QAAQ,EAAE;IAAE,aAAa,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAE1F"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import os from "node:os";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import { AGENTPLANE_HOME_ENV, GLOBAL_RECIPES_DIR_NAME, INSTALLED_RECIPES_NAME,
|
|
3
|
+
import { AGENTPLANE_HOME_ENV, GLOBAL_RECIPES_DIR_NAME, INSTALLED_RECIPES_NAME, PROJECT_RECIPES_PACKAGES_DIR_NAME, PROJECT_RECIPES_REGISTRY_NAME, PROJECT_RECIPES_CACHE_DIR_NAME, RECIPES_DIR_NAME, RECIPES_REMOTE_INDEX_NAME, RECIPES_REMOTE_INDEX_SIG_NAME, } from "./constants.js";
|
|
4
4
|
export function resolveAgentplaneHome() {
|
|
5
5
|
const overridden = process.env[AGENTPLANE_HOME_ENV]?.trim();
|
|
6
6
|
if (overridden)
|
|
@@ -25,12 +25,30 @@ export function resolveInstalledRecipeDir(entry) {
|
|
|
25
25
|
export function resolveProjectRecipesDir(resolved) {
|
|
26
26
|
return path.join(resolved.agentplaneDir, RECIPES_DIR_NAME);
|
|
27
27
|
}
|
|
28
|
-
export function
|
|
29
|
-
return path.join(resolveProjectRecipesDir(resolved),
|
|
28
|
+
export function resolveProjectRecipesPackagesDir(resolved) {
|
|
29
|
+
return path.join(resolveProjectRecipesDir(resolved), PROJECT_RECIPES_PACKAGES_DIR_NAME);
|
|
30
|
+
}
|
|
31
|
+
export function resolveProjectRecipesRegistryPath(resolved) {
|
|
32
|
+
return path.join(resolveProjectRecipesDir(resolved), PROJECT_RECIPES_REGISTRY_NAME);
|
|
30
33
|
}
|
|
31
|
-
export function
|
|
32
|
-
return path.join(
|
|
34
|
+
export function resolveProjectVendoredRecipeDir(resolved, recipeId) {
|
|
35
|
+
return path.join(resolveProjectRecipesPackagesDir(resolved), recipeId);
|
|
36
|
+
}
|
|
37
|
+
export function resolveProjectInstalledRecipeDir(resolved, recipeId) {
|
|
38
|
+
return resolveProjectVendoredRecipeDir(resolved, recipeId);
|
|
33
39
|
}
|
|
34
40
|
export function resolveProjectRecipesCacheDir(resolved) {
|
|
35
41
|
return path.join(resolved.agentplaneDir, PROJECT_RECIPES_CACHE_DIR_NAME);
|
|
36
42
|
}
|
|
43
|
+
export function resolveProjectRecipesLockPath(resolved) {
|
|
44
|
+
return path.join(resolved.agentplaneDir, "recipes.lock.json");
|
|
45
|
+
}
|
|
46
|
+
export function resolveProjectGeneratedDir(resolved) {
|
|
47
|
+
return path.join(resolved.agentplaneDir, "generated");
|
|
48
|
+
}
|
|
49
|
+
export function resolveProjectOverlayBundlePath(resolved) {
|
|
50
|
+
return path.join(resolveProjectGeneratedDir(resolved), "overlay-bundle.json");
|
|
51
|
+
}
|
|
52
|
+
export function resolveProjectRecipeAssetsPath(resolved) {
|
|
53
|
+
return path.join(resolveProjectGeneratedDir(resolved), "recipe-assets.json");
|
|
54
|
+
}
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export declare function readRecipeInstallMetadata(filePath: string): Promise<RecipeInstallMetadata | null>;
|
|
3
|
-
export declare function writeRecipeInstallMetadata(filePath: string, metadata: RecipeInstallMetadata): Promise<void>;
|
|
1
|
+
import type { ProjectInstalledRecipesFile } from "./types.js";
|
|
4
2
|
export declare function readProjectInstalledRecipes(opts: {
|
|
5
3
|
agentplaneDir: string;
|
|
6
|
-
}): Promise<
|
|
4
|
+
}): Promise<ProjectInstalledRecipesFile>;
|
|
7
5
|
//# sourceMappingURL=project-installed-recipes.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-installed-recipes.d.ts","sourceRoot":"","sources":["../../../../src/commands/recipes/impl/project-installed-recipes.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"project-installed-recipes.d.ts","sourceRoot":"","sources":["../../../../src/commands/recipes/impl/project-installed-recipes.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAA+B,2BAA2B,EAAE,MAAM,YAAY,CAAC;AAiB3F,wBAAsB,2BAA2B,CAAC,IAAI,EAAE;IACtD,aAAa,EAAE,MAAM,CAAC;CACvB,GAAG,OAAO,CAAC,2BAA2B,CAAC,CA8CvC"}
|
|
@@ -1,102 +1,58 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { stat } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import { fileExists, getPathKind } from "../../../cli/fs-utils.js";
|
|
4
3
|
import { invalidFieldMessage, missingFileMessage } from "../../../cli/output.js";
|
|
5
|
-
import { isRecord } from "../../../shared/guards.js";
|
|
6
|
-
import { writeJsonStableIfChanged } from "../../../shared/write-if-changed.js";
|
|
7
|
-
import { resolveProjectInstalledRecipeDir, resolveProjectRecipeInstallMetaPath, resolveProjectRecipesDir, } from "./paths.js";
|
|
8
|
-
import { readRecipeManifest } from "./manifest.js";
|
|
9
4
|
import { normalizeRecipeTags } from "./normalize.js";
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const id = typeof raw.id === "string" ? raw.id.trim() : "";
|
|
14
|
-
const version = typeof raw.version === "string" ? raw.version.trim() : "";
|
|
15
|
-
const source = typeof raw.source === "string" ? raw.source.trim() : "";
|
|
16
|
-
const installedAt = typeof raw.installed_at === "string" ? raw.installed_at.trim() : "";
|
|
17
|
-
const installMode = raw.install_mode === undefined || raw.install_mode === "project-local"
|
|
18
|
-
? raw.install_mode
|
|
19
|
-
: null;
|
|
20
|
-
if (raw.schema_version !== 1) {
|
|
21
|
-
throw new Error(invalidFieldMessage("recipe install metadata.schema_version", "1"));
|
|
22
|
-
}
|
|
23
|
-
if (!id || !version || !source || !installedAt) {
|
|
24
|
-
throw new Error(invalidFieldMessage("recipe install metadata", "id, version, source, installed_at"));
|
|
25
|
-
}
|
|
26
|
-
if (installMode === null) {
|
|
27
|
-
throw new Error(invalidFieldMessage("recipe install metadata.install_mode", '"project-local"'));
|
|
28
|
-
}
|
|
29
|
-
const tags = normalizeRecipeTags(raw.tags ?? []);
|
|
30
|
-
return {
|
|
31
|
-
schema_version: 1,
|
|
32
|
-
id,
|
|
33
|
-
version,
|
|
34
|
-
source,
|
|
35
|
-
installed_at: installedAt,
|
|
36
|
-
tags,
|
|
37
|
-
install_mode: installMode,
|
|
38
|
-
};
|
|
39
|
-
}
|
|
5
|
+
import { readProjectRecipesRegistry } from "./project-registry.js";
|
|
6
|
+
import { readRecipeManifest } from "./manifest.js";
|
|
7
|
+
import { resolveProjectRecipesDir } from "./paths.js";
|
|
40
8
|
function sortInstalledRecipes(entries) {
|
|
41
|
-
return [...entries].toSorted((
|
|
9
|
+
return [...entries].toSorted((left, right) => left.id.localeCompare(right.id));
|
|
42
10
|
}
|
|
43
|
-
|
|
11
|
+
async function pathExistsAsKind(filePath, kind) {
|
|
44
12
|
try {
|
|
45
|
-
const
|
|
46
|
-
return
|
|
13
|
+
const stats = await stat(filePath);
|
|
14
|
+
return kind === "dir" ? stats.isDirectory() : stats.isFile();
|
|
47
15
|
}
|
|
48
|
-
catch
|
|
49
|
-
|
|
50
|
-
if (code === "ENOENT")
|
|
51
|
-
return null;
|
|
52
|
-
throw err;
|
|
16
|
+
catch {
|
|
17
|
+
return false;
|
|
53
18
|
}
|
|
54
19
|
}
|
|
55
|
-
export async function writeRecipeInstallMetadata(filePath, metadata) {
|
|
56
|
-
await mkdir(path.dirname(filePath), { recursive: true });
|
|
57
|
-
await writeJsonStableIfChanged(filePath, metadata);
|
|
58
|
-
}
|
|
59
20
|
export async function readProjectInstalledRecipes(opts) {
|
|
60
|
-
const
|
|
61
|
-
if (
|
|
21
|
+
const registry = await readProjectRecipesRegistry(opts);
|
|
22
|
+
if (registry.recipes.length === 0) {
|
|
62
23
|
return { schema_version: 1, updated_at: "", recipes: [] };
|
|
63
24
|
}
|
|
64
25
|
const entries = [];
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
if (!
|
|
68
|
-
|
|
69
|
-
|
|
26
|
+
for (const registryEntry of registry.recipes) {
|
|
27
|
+
const recipeDir = path.join(resolveProjectRecipesDir(opts), registryEntry.path);
|
|
28
|
+
if (!(await pathExistsAsKind(recipeDir, "dir"))) {
|
|
29
|
+
throw new Error(missingFileMessage("vendored recipe directory", recipeDir));
|
|
30
|
+
}
|
|
70
31
|
const manifestPath = path.join(recipeDir, "manifest.json");
|
|
71
|
-
if (!(await
|
|
32
|
+
if (!(await pathExistsAsKind(manifestPath, "file"))) {
|
|
72
33
|
throw new Error(missingFileMessage("installed recipe manifest", manifestPath));
|
|
73
34
|
}
|
|
74
35
|
const manifest = await readRecipeManifest(manifestPath);
|
|
75
|
-
if (manifest.id !==
|
|
76
|
-
throw new Error(invalidFieldMessage(`installed recipe directory ${
|
|
77
|
-
}
|
|
78
|
-
const metadata = await readRecipeInstallMetadata(resolveProjectRecipeInstallMetaPath(opts, dirent.name));
|
|
79
|
-
if (metadata && (metadata.id !== manifest.id || metadata.version !== manifest.version)) {
|
|
80
|
-
throw new Error(invalidFieldMessage(`recipe install metadata ${dirent.name}`, "id/version matching manifest"));
|
|
36
|
+
if (manifest.id !== registryEntry.id || manifest.version !== registryEntry.version) {
|
|
37
|
+
throw new Error(invalidFieldMessage(`installed recipe directory ${registryEntry.id}`, `manifest.id=${registryEntry.id} and manifest.version=${registryEntry.version}`));
|
|
81
38
|
}
|
|
82
39
|
entries.push({
|
|
83
40
|
id: manifest.id,
|
|
84
41
|
version: manifest.version,
|
|
85
|
-
source:
|
|
86
|
-
|
|
87
|
-
|
|
42
|
+
source: registryEntry.source_ref,
|
|
43
|
+
source_ref: registryEntry.source_ref,
|
|
44
|
+
source_sha256: registryEntry.source_sha256,
|
|
45
|
+
vendored_sha256: registryEntry.vendored_sha256,
|
|
46
|
+
materialization: registryEntry.materialization,
|
|
47
|
+
installed_at: registryEntry.installed_at,
|
|
48
|
+
project_path: registryEntry.path,
|
|
49
|
+
tags: normalizeRecipeTags(registryEntry.tags ?? manifest.tags ?? []),
|
|
88
50
|
manifest,
|
|
89
51
|
});
|
|
90
52
|
}
|
|
91
|
-
const sorted = sortInstalledRecipes(entries);
|
|
92
|
-
const updatedAt = sorted
|
|
93
|
-
.map((entry) => entry.installed_at)
|
|
94
|
-
.filter(Boolean)
|
|
95
|
-
.toSorted()
|
|
96
|
-
.at(-1);
|
|
97
53
|
return {
|
|
98
54
|
schema_version: 1,
|
|
99
|
-
updated_at:
|
|
100
|
-
recipes:
|
|
55
|
+
updated_at: registry.updated_at,
|
|
56
|
+
recipes: sortInstalledRecipes(entries),
|
|
101
57
|
};
|
|
102
58
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ProjectInstalledRecipeEntry, ProjectRecipeState } from "./types.js";
|
|
2
|
+
export declare function hashRecipeTree(rootDir: string): Promise<string>;
|
|
3
|
+
export type ProjectRecipeInspection = {
|
|
4
|
+
entry: ProjectInstalledRecipeEntry;
|
|
5
|
+
recipe_dir: string;
|
|
6
|
+
source_dir: string;
|
|
7
|
+
cache_present: boolean;
|
|
8
|
+
current_source_sha256?: string;
|
|
9
|
+
current_vendored_sha256: string;
|
|
10
|
+
state: ProjectRecipeState;
|
|
11
|
+
};
|
|
12
|
+
export declare function inspectProjectRecipe(opts: {
|
|
13
|
+
project: {
|
|
14
|
+
agentplaneDir: string;
|
|
15
|
+
};
|
|
16
|
+
recipeId: string;
|
|
17
|
+
}): Promise<ProjectRecipeInspection>;
|
|
18
|
+
//# sourceMappingURL=project-recipe-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-recipe-state.d.ts","sourceRoot":"","sources":["../../../../src/commands/recipes/impl/project-recipe-state.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,2BAA2B,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AA+BlF,wBAAsB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAIrE;AAED,MAAM,MAAM,uBAAuB,GAAG;IACpC,KAAK,EAAE,2BAA2B,CAAC;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,OAAO,CAAC;IACvB,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,uBAAuB,EAAE,MAAM,CAAC;IAChC,KAAK,EAAE,kBAAkB,CAAC;CAC3B,CAAC;AA2BF,wBAAsB,oBAAoB,CAAC,IAAI,EAAE;IAC/C,OAAO,EAAE;QAAE,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC;IACnC,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAwCnC"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { lstat, readFile, readdir } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { readProjectInstalledRecipes } from "./project-installed-recipes.js";
|
|
5
|
+
import { resolveInstalledRecipeDir, resolveProjectVendoredRecipeDir } from "./paths.js";
|
|
6
|
+
async function hashTreeEntry(rootDir, relativeDir, hash) {
|
|
7
|
+
const directoryPath = relativeDir ? path.join(rootDir, relativeDir) : rootDir;
|
|
8
|
+
const directoryEntries = await readdir(directoryPath, { withFileTypes: true });
|
|
9
|
+
const entries = directoryEntries.toSorted((left, right) => left.name.localeCompare(right.name));
|
|
10
|
+
for (const entry of entries) {
|
|
11
|
+
const relativePath = relativeDir ? path.posix.join(relativeDir, entry.name) : entry.name;
|
|
12
|
+
const absolutePath = path.join(rootDir, relativePath);
|
|
13
|
+
if (entry.isDirectory()) {
|
|
14
|
+
hash.update(`dir:${relativePath}\n`);
|
|
15
|
+
await hashTreeEntry(rootDir, relativePath, hash);
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
if (entry.isSymbolicLink()) {
|
|
19
|
+
hash.update(`symlink:${relativePath}\n`);
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
const data = await readFile(absolutePath);
|
|
23
|
+
hash.update(`file:${relativePath}\n`);
|
|
24
|
+
hash.update(data);
|
|
25
|
+
hash.update("\n");
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export async function hashRecipeTree(rootDir) {
|
|
29
|
+
const hash = createHash("sha256");
|
|
30
|
+
await hashTreeEntry(rootDir, "", hash);
|
|
31
|
+
return hash.digest("hex");
|
|
32
|
+
}
|
|
33
|
+
function classifyRecipeState(opts) {
|
|
34
|
+
if (opts.entry.materialization === "link" && !opts.vendoredPathIsSymlink) {
|
|
35
|
+
return "modified";
|
|
36
|
+
}
|
|
37
|
+
const vendoredModified = opts.currentVendoredSha256 !== opts.entry.vendored_sha256;
|
|
38
|
+
const cacheDiverged = opts.cachePresent && opts.currentSourceSha256 !== undefined
|
|
39
|
+
? opts.currentSourceSha256 !== opts.entry.source_sha256
|
|
40
|
+
: false;
|
|
41
|
+
if (opts.entry.materialization === "link") {
|
|
42
|
+
if (cacheDiverged)
|
|
43
|
+
return "diverged_from_cache";
|
|
44
|
+
if (vendoredModified)
|
|
45
|
+
return "modified";
|
|
46
|
+
return "clean";
|
|
47
|
+
}
|
|
48
|
+
if (vendoredModified)
|
|
49
|
+
return "modified";
|
|
50
|
+
if (cacheDiverged)
|
|
51
|
+
return "diverged_from_cache";
|
|
52
|
+
return "clean";
|
|
53
|
+
}
|
|
54
|
+
export async function inspectProjectRecipe(opts) {
|
|
55
|
+
const installed = await readProjectInstalledRecipes(opts.project);
|
|
56
|
+
const entry = installed.recipes.find((recipe) => recipe.id === opts.recipeId);
|
|
57
|
+
if (!entry) {
|
|
58
|
+
throw new Error(`Recipe not installed: ${opts.recipeId}`);
|
|
59
|
+
}
|
|
60
|
+
const recipeDir = resolveProjectVendoredRecipeDir(opts.project, entry.id);
|
|
61
|
+
const sourceDir = resolveInstalledRecipeDir({ id: entry.id, version: entry.version });
|
|
62
|
+
const vendoredPathStat = await lstat(recipeDir);
|
|
63
|
+
const vendoredPathIsSymlink = vendoredPathStat.isSymbolicLink();
|
|
64
|
+
const currentVendoredSha256 = await hashRecipeTree(recipeDir);
|
|
65
|
+
let cachePresent = false;
|
|
66
|
+
let currentSourceSha256;
|
|
67
|
+
try {
|
|
68
|
+
const sourceStat = await lstat(sourceDir);
|
|
69
|
+
if (sourceStat.isDirectory() || sourceStat.isSymbolicLink()) {
|
|
70
|
+
cachePresent = true;
|
|
71
|
+
currentSourceSha256 = await hashRecipeTree(sourceDir);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
const code = error?.code;
|
|
76
|
+
if (code !== "ENOENT")
|
|
77
|
+
throw error;
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
entry,
|
|
81
|
+
recipe_dir: recipeDir,
|
|
82
|
+
source_dir: sourceDir,
|
|
83
|
+
cache_present: cachePresent,
|
|
84
|
+
current_source_sha256: currentSourceSha256,
|
|
85
|
+
current_vendored_sha256: currentVendoredSha256,
|
|
86
|
+
state: classifyRecipeState({
|
|
87
|
+
entry,
|
|
88
|
+
cachePresent,
|
|
89
|
+
currentSourceSha256,
|
|
90
|
+
currentVendoredSha256,
|
|
91
|
+
vendoredPathIsSymlink,
|
|
92
|
+
}),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ProjectRecipeRegistryEntry, ProjectRecipesRegistryFile } from "./types.js";
|
|
2
|
+
export declare function readProjectRecipesRegistry(opts: {
|
|
3
|
+
agentplaneDir: string;
|
|
4
|
+
}): Promise<ProjectRecipesRegistryFile>;
|
|
5
|
+
export declare function writeProjectRecipesRegistry(opts: {
|
|
6
|
+
agentplaneDir: string;
|
|
7
|
+
}, file: ProjectRecipesRegistryFile): Promise<void>;
|
|
8
|
+
export declare function upsertProjectRecipeRegistryEntry(opts: {
|
|
9
|
+
project: {
|
|
10
|
+
agentplaneDir: string;
|
|
11
|
+
};
|
|
12
|
+
entry: ProjectRecipeRegistryEntry;
|
|
13
|
+
}): Promise<ProjectRecipesRegistryFile>;
|
|
14
|
+
export declare function removeProjectRecipeRegistryEntry(opts: {
|
|
15
|
+
project: {
|
|
16
|
+
agentplaneDir: string;
|
|
17
|
+
};
|
|
18
|
+
recipeId: string;
|
|
19
|
+
}): Promise<ProjectRecipesRegistryFile>;
|
|
20
|
+
//# sourceMappingURL=project-registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-registry.d.ts","sourceRoot":"","sources":["../../../../src/commands/recipes/impl/project-registry.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAEV,0BAA0B,EAC1B,0BAA0B,EAC3B,MAAM,YAAY,CAAC;AAsDpB,wBAAsB,0BAA0B,CAAC,IAAI,EAAE;IACrD,aAAa,EAAE,MAAM,CAAC;CACvB,GAAG,OAAO,CAAC,0BAA0B,CAAC,CAwBtC;AAED,wBAAsB,2BAA2B,CAC/C,IAAI,EAAE;IAAE,aAAa,EAAE,MAAM,CAAA;CAAE,EAC/B,IAAI,EAAE,0BAA0B,GAC/B,OAAO,CAAC,IAAI,CAAC,CASf;AAED,wBAAsB,gCAAgC,CAAC,IAAI,EAAE;IAC3D,OAAO,EAAE;QAAE,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC;IACnC,KAAK,EAAE,0BAA0B,CAAC;CACnC,GAAG,OAAO,CAAC,0BAA0B,CAAC,CAOtC;AAED,wBAAsB,gCAAgC,CAAC,IAAI,EAAE;IAC3D,OAAO,EAAE;QAAE,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC;IACnC,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,OAAO,CAAC,0BAA0B,CAAC,CAStC"}
|