agentplane 0.3.5 → 0.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +103 -75
- package/assets/AGENTS.md +4 -2
- package/bin/dist-guard.js +13 -3
- package/bin/runtime-watch.d.ts +1 -0
- package/bin/runtime-watch.js +22 -5
- package/bin/stale-dist-policy.js +9 -2
- package/dist/.build-manifest.json +251 -821
- package/dist/adapters/task-backend/task-backend-adapter.d.ts +2 -2
- package/dist/adapters/task-backend/task-backend-adapter.d.ts.map +1 -1
- package/dist/adapters/task-backend/task-backend-adapter.js +2 -2
- package/dist/backends/task-backend/local-backend.d.ts +7 -5
- package/dist/backends/task-backend/local-backend.d.ts.map +1 -1
- package/dist/backends/task-backend/local-backend.js +79 -7
- package/dist/backends/task-backend/redmine/env.d.ts +1 -1
- package/dist/backends/task-backend/redmine/env.d.ts.map +1 -1
- package/dist/backends/task-backend/redmine/env.js +3 -0
- package/dist/backends/task-backend/redmine/inspect.d.ts +11 -0
- package/dist/backends/task-backend/redmine/inspect.d.ts.map +1 -0
- package/dist/backends/task-backend/redmine/inspect.js +75 -0
- package/dist/backends/task-backend/redmine/mapping.d.ts.map +1 -1
- package/dist/backends/task-backend/redmine/mapping.js +21 -2
- package/dist/backends/task-backend/redmine/state.d.ts +17 -0
- package/dist/backends/task-backend/redmine/state.d.ts.map +1 -0
- package/dist/backends/task-backend/redmine/state.js +95 -0
- package/dist/backends/task-backend/redmine-backend.d.ts +10 -16
- package/dist/backends/task-backend/redmine-backend.d.ts.map +1 -1
- package/dist/backends/task-backend/redmine-backend.js +205 -15
- package/dist/backends/task-backend/shared/constants.d.ts +1 -1
- package/dist/backends/task-backend/shared/constants.js +1 -1
- package/dist/backends/task-backend/shared/record.d.ts.map +1 -1
- package/dist/backends/task-backend/shared/record.js +20 -1
- package/dist/backends/task-backend/shared/types.d.ts +42 -4
- package/dist/backends/task-backend/shared/types.d.ts.map +1 -1
- package/dist/backends/task-backend/shared.d.ts +1 -1
- package/dist/backends/task-backend/shared.d.ts.map +1 -1
- package/dist/backends/task-backend.d.ts +1 -1
- package/dist/backends/task-backend.d.ts.map +1 -1
- package/dist/backends/task-backend.test-helpers.d.ts +4 -0
- package/dist/backends/task-backend.test-helpers.d.ts.map +1 -0
- package/dist/backends/task-backend.test-helpers.js +33 -0
- package/dist/backends/task-index.d.ts.map +1 -1
- package/dist/backends/task-index.js +1 -0
- package/dist/cli/bootstrap-guide.d.ts.map +1 -1
- package/dist/cli/bootstrap-guide.js +1 -0
- package/dist/cli/command-guide.d.ts.map +1 -1
- package/dist/cli/command-guide.js +3 -2
- package/dist/cli/reason-codes.d.ts.map +1 -1
- package/dist/cli/reason-codes.js +30 -0
- package/dist/cli/run-cli/command-catalog/core.d.ts +3 -0
- package/dist/cli/run-cli/command-catalog/core.d.ts.map +1 -0
- package/dist/cli/run-cli/command-catalog/core.js +137 -0
- package/dist/cli/run-cli/command-catalog/lifecycle.d.ts +3 -0
- package/dist/cli/run-cli/command-catalog/lifecycle.d.ts.map +1 -0
- package/dist/cli/run-cli/command-catalog/lifecycle.js +52 -0
- package/dist/cli/run-cli/command-catalog/project.d.ts +3 -0
- package/dist/cli/run-cli/command-catalog/project.d.ts.map +1 -0
- package/dist/cli/run-cli/command-catalog/project.js +80 -0
- package/dist/cli/run-cli/command-catalog/shared.d.ts +19 -0
- package/dist/cli/run-cli/command-catalog/shared.d.ts.map +1 -0
- package/dist/cli/run-cli/command-catalog/shared.js +9 -0
- package/dist/cli/run-cli/command-catalog/task.d.ts +3 -0
- package/dist/cli/run-cli/command-catalog/task.d.ts.map +1 -0
- package/dist/cli/run-cli/command-catalog/task.js +85 -0
- package/dist/cli/run-cli/command-catalog.d.ts +3 -18
- package/dist/cli/run-cli/command-catalog.d.ts.map +1 -1
- package/dist/cli/run-cli/command-catalog.js +8 -337
- package/dist/cli/run-cli/commands/ide.d.ts.map +1 -1
- package/dist/cli/run-cli/commands/ide.js +64 -2
- package/dist/cli/run-cli/commands/init/ui.d.ts.map +1 -1
- package/dist/cli/run-cli/commands/init/ui.js +33 -13
- package/dist/cli/run-cli/commands/init/write-env.d.ts.map +1 -1
- package/dist/cli/run-cli/commands/init/write-env.js +12 -0
- package/dist/cli/run-cli.core.pr-flow.test-helpers.d.ts +3 -0
- package/dist/cli/run-cli.core.pr-flow.test-helpers.d.ts.map +1 -0
- package/dist/cli/run-cli.core.pr-flow.test-helpers.js +41 -0
- package/dist/cli/run-cli.core.tasks.test-helpers.d.ts +2 -0
- package/dist/cli/run-cli.core.tasks.test-helpers.d.ts.map +1 -0
- package/dist/cli/run-cli.core.tasks.test-helpers.js +6 -0
- package/dist/cli/run-cli.test-helpers.d.ts +3 -0
- package/dist/cli/run-cli.test-helpers.d.ts.map +1 -1
- package/dist/cli/run-cli.test-helpers.js +140 -6
- package/dist/commands/backend/sync.command.d.ts +5 -1
- package/dist/commands/backend/sync.command.d.ts.map +1 -1
- package/dist/commands/backend/sync.command.js +67 -3
- package/dist/commands/backend.d.ts +22 -0
- package/dist/commands/backend.d.ts.map +1 -1
- package/dist/commands/backend.js +110 -1
- package/dist/commands/commit.spec.d.ts.map +1 -1
- package/dist/commands/commit.spec.js +31 -7
- package/dist/commands/doctor/runtime.d.ts.map +1 -1
- package/dist/commands/doctor/runtime.js +3 -6
- package/dist/commands/doctor/workspace.d.ts +8 -0
- package/dist/commands/doctor/workspace.d.ts.map +1 -1
- package/dist/commands/doctor/workspace.js +127 -3
- package/dist/commands/guard/commit.command.d.ts.map +1 -1
- package/dist/commands/guard/commit.command.js +30 -6
- package/dist/commands/guard/impl/allow.d.ts +9 -0
- package/dist/commands/guard/impl/allow.d.ts.map +1 -1
- package/dist/commands/guard/impl/allow.js +26 -10
- package/dist/commands/guard/impl/commands.d.ts.map +1 -1
- package/dist/commands/guard/impl/commands.js +146 -18
- package/dist/commands/guard/impl/comment-commit.d.ts.map +1 -1
- package/dist/commands/guard/impl/comment-commit.js +2 -0
- package/dist/commands/hooks/index.d.ts.map +1 -1
- package/dist/commands/hooks/index.js +8 -35
- package/dist/commands/recipes/impl/apply.d.ts +4 -0
- package/dist/commands/recipes/impl/apply.d.ts.map +1 -1
- package/dist/commands/recipes/impl/apply.js +34 -0
- package/dist/commands/recipes/impl/commands/explain.d.ts.map +1 -1
- package/dist/commands/recipes/impl/commands/explain.js +70 -11
- package/dist/commands/recipes/impl/commands/info.d.ts.map +1 -1
- package/dist/commands/recipes/impl/commands/info.js +24 -12
- package/dist/commands/recipes/impl/commands/install.d.ts.map +1 -1
- package/dist/commands/recipes/impl/commands/install.js +32 -36
- package/dist/commands/recipes/impl/commands/list.d.ts.map +1 -1
- package/dist/commands/recipes/impl/commands/list.js +7 -4
- package/dist/commands/recipes/impl/commands/remove.d.ts.map +1 -1
- package/dist/commands/recipes/impl/commands/remove.js +9 -11
- package/dist/commands/recipes/impl/constants.d.ts +2 -0
- package/dist/commands/recipes/impl/constants.d.ts.map +1 -1
- package/dist/commands/recipes/impl/constants.js +2 -0
- package/dist/commands/recipes/impl/manifest.d.ts.map +1 -1
- package/dist/commands/recipes/impl/manifest.js +219 -23
- package/dist/commands/recipes/impl/normalize.d.ts +3 -0
- package/dist/commands/recipes/impl/normalize.d.ts.map +1 -1
- package/dist/commands/recipes/impl/normalize.js +28 -24
- package/dist/commands/recipes/impl/paths.d.ts +9 -0
- package/dist/commands/recipes/impl/paths.d.ts.map +1 -1
- package/dist/commands/recipes/impl/paths.js +10 -1
- package/dist/commands/recipes/impl/project-installed-recipes.d.ts +7 -0
- package/dist/commands/recipes/impl/project-installed-recipes.d.ts.map +1 -0
- package/dist/commands/recipes/impl/project-installed-recipes.js +102 -0
- package/dist/commands/recipes/impl/resolver.d.ts +20 -0
- package/dist/commands/recipes/impl/resolver.d.ts.map +1 -0
- package/dist/commands/recipes/impl/resolver.js +220 -0
- package/dist/commands/recipes/impl/scenario.d.ts.map +1 -1
- package/dist/commands/recipes/impl/scenario.js +40 -11
- package/dist/commands/recipes/impl/types.d.ts +145 -16
- package/dist/commands/recipes/impl/types.d.ts.map +1 -1
- package/dist/commands/recipes/install.spec.d.ts.map +1 -1
- package/dist/commands/recipes/install.spec.js +3 -2
- package/dist/commands/recipes.d.ts +6 -4
- package/dist/commands/recipes.d.ts.map +1 -1
- package/dist/commands/recipes.js +5 -3
- package/dist/commands/recipes.test-helpers.d.ts +185 -0
- package/dist/commands/recipes.test-helpers.d.ts.map +1 -0
- package/dist/commands/recipes.test-helpers.js +339 -0
- package/dist/commands/scenario/impl/commands.d.ts.map +1 -1
- package/dist/commands/scenario/impl/commands.js +192 -336
- package/dist/commands/scenario/info.command.d.ts.map +1 -1
- package/dist/commands/scenario/info.command.js +7 -2
- package/dist/commands/scenario/list.command.js +2 -2
- package/dist/commands/scenario/run.command.d.ts.map +1 -1
- package/dist/commands/scenario/run.command.js +7 -2
- package/dist/commands/shared/reconcile-check.d.ts.map +1 -1
- package/dist/commands/shared/reconcile-check.js +77 -2
- package/dist/commands/shared/task-backend.d.ts +1 -1
- package/dist/commands/shared/task-backend.d.ts.map +1 -1
- package/dist/commands/shared/task-backend.js +9 -0
- package/dist/commands/shared/task-store.d.ts +92 -2
- package/dist/commands/shared/task-store.d.ts.map +1 -1
- package/dist/commands/shared/task-store.js +405 -43
- package/dist/commands/task/block.d.ts.map +1 -1
- package/dist/commands/task/block.js +84 -46
- package/dist/commands/task/close-duplicate.d.ts.map +1 -1
- package/dist/commands/task/close-duplicate.js +12 -37
- package/dist/commands/task/close-noop.d.ts.map +1 -1
- package/dist/commands/task/close-noop.js +12 -30
- package/dist/commands/task/close-shared.d.ts +14 -0
- package/dist/commands/task/close-shared.d.ts.map +1 -0
- package/dist/commands/task/close-shared.js +73 -0
- package/dist/commands/task/comment.d.ts.map +1 -1
- package/dist/commands/task/comment.js +34 -21
- package/dist/commands/task/derive.command.d.ts +1 -0
- package/dist/commands/task/derive.command.d.ts.map +1 -1
- package/dist/commands/task/derive.command.js +15 -2
- package/dist/commands/task/derive.d.ts +1 -0
- package/dist/commands/task/derive.d.ts.map +1 -1
- package/dist/commands/task/derive.js +27 -4
- package/dist/commands/task/doc-set.command.d.ts +2 -1
- package/dist/commands/task/doc-set.command.d.ts.map +1 -1
- package/dist/commands/task/doc-set.command.js +36 -4
- package/dist/commands/task/doc-template.d.ts.map +1 -1
- package/dist/commands/task/doc-template.js +2 -7
- package/dist/commands/task/doc.command.js +1 -1
- package/dist/commands/task/doc.d.ts +2 -1
- package/dist/commands/task/doc.d.ts.map +1 -1
- package/dist/commands/task/doc.js +139 -76
- package/dist/commands/task/finish.d.ts.map +1 -1
- package/dist/commands/task/finish.js +142 -80
- package/dist/commands/task/migrate-doc.d.ts +15 -0
- package/dist/commands/task/migrate-doc.d.ts.map +1 -1
- package/dist/commands/task/migrate-doc.js +128 -43
- package/dist/commands/task/new.d.ts.map +1 -1
- package/dist/commands/task/new.js +3 -1
- package/dist/commands/task/plan-set.command.js +1 -1
- package/dist/commands/task/plan.command.d.ts +8 -0
- package/dist/commands/task/plan.command.d.ts.map +1 -0
- package/dist/commands/task/plan.command.js +37 -0
- package/dist/commands/task/plan.d.ts.map +1 -1
- package/dist/commands/task/plan.js +198 -101
- package/dist/commands/task/set-status.command.d.ts.map +1 -1
- package/dist/commands/task/set-status.command.js +1 -1
- package/dist/commands/task/set-status.d.ts.map +1 -1
- package/dist/commands/task/set-status.js +115 -35
- package/dist/commands/task/shared/dependencies.d.ts +1 -0
- package/dist/commands/task/shared/dependencies.d.ts.map +1 -1
- package/dist/commands/task/shared/dependencies.js +10 -0
- package/dist/commands/task/shared/docs.d.ts +1 -0
- package/dist/commands/task/shared/docs.d.ts.map +1 -1
- package/dist/commands/task/shared/docs.js +8 -1
- package/dist/commands/task/shared/transitions.d.ts +17 -2
- package/dist/commands/task/shared/transitions.d.ts.map +1 -1
- package/dist/commands/task/shared/transitions.js +20 -13
- package/dist/commands/task/shared.d.ts +3 -3
- package/dist/commands/task/shared.d.ts.map +1 -1
- package/dist/commands/task/shared.js +3 -3
- package/dist/commands/task/start.d.ts.map +1 -1
- package/dist/commands/task/start.js +101 -71
- package/dist/commands/task/task.command.d.ts +8 -0
- package/dist/commands/task/task.command.d.ts.map +1 -0
- package/dist/commands/task/task.command.js +71 -0
- package/dist/commands/task/verify-command-shared.d.ts +16 -0
- package/dist/commands/task/verify-command-shared.d.ts.map +1 -0
- package/dist/commands/task/verify-command-shared.js +53 -0
- package/dist/commands/task/verify-ok.command.d.ts +2 -6
- package/dist/commands/task/verify-ok.command.d.ts.map +1 -1
- package/dist/commands/task/verify-ok.command.js +8 -50
- package/dist/commands/task/verify-record.d.ts.map +1 -1
- package/dist/commands/task/verify-record.js +124 -145
- package/dist/commands/task/verify-rework.command.d.ts +2 -6
- package/dist/commands/task/verify-rework.command.d.ts.map +1 -1
- package/dist/commands/task/verify-rework.command.js +8 -50
- package/dist/commands/upgrade/apply.d.ts +2 -0
- package/dist/commands/upgrade/apply.d.ts.map +1 -1
- package/dist/commands/upgrade/apply.js +33 -1
- package/dist/commands/upgrade.command.d.ts.map +1 -1
- package/dist/commands/upgrade.command.js +25 -0
- package/dist/commands/upgrade.d.ts +1 -0
- package/dist/commands/upgrade.d.ts.map +1 -1
- package/dist/commands/upgrade.js +34 -0
- package/dist/commands/verify.spec.d.ts.map +1 -1
- package/dist/commands/verify.spec.js +3 -12
- package/dist/policy/rules/allowlist.d.ts.map +1 -1
- package/dist/policy/rules/allowlist.js +16 -4
- package/dist/policy/rules/protected-paths.d.ts.map +1 -1
- package/dist/policy/rules/protected-paths.js +6 -1
- package/dist/ports/task-backend-port.d.ts +2 -2
- package/dist/ports/task-backend-port.d.ts.map +1 -1
- package/dist/shared/agent-emoji.d.ts.map +1 -1
- package/dist/shared/protected-paths.d.ts +17 -0
- package/dist/shared/protected-paths.d.ts.map +1 -1
- package/dist/shared/protected-paths.js +59 -10
- package/dist/shared/repo-cli-version.d.ts.map +1 -1
- package/dist/shared/repo-cli-version.js +9 -3
- package/package.json +2 -2
|
@@ -1,40 +1,236 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
import { invalidFieldMessage, requiredFieldMessage } from "../../../cli/output.js";
|
|
3
3
|
import { isRecord } from "../../../shared/guards.js";
|
|
4
|
-
import {
|
|
4
|
+
import { dedupeStrings } from "../../../shared/strings.js";
|
|
5
|
+
import { normalizeAgentId, normalizeRecipeId, normalizeRecipeRelativePath, normalizeRecipeTags, normalizeScenarioId, normalizeSkillId, normalizeToolId, } from "./normalize.js";
|
|
6
|
+
function normalizeRequiredString(raw, field) {
|
|
7
|
+
if (typeof raw !== "string")
|
|
8
|
+
throw new Error(invalidFieldMessage(field, "string"));
|
|
9
|
+
const value = raw.trim();
|
|
10
|
+
if (!value)
|
|
11
|
+
throw new Error(requiredFieldMessage(field));
|
|
12
|
+
return value;
|
|
13
|
+
}
|
|
14
|
+
function normalizeOptionalString(raw, field) {
|
|
15
|
+
if (raw === undefined)
|
|
16
|
+
return undefined;
|
|
17
|
+
if (typeof raw !== "string")
|
|
18
|
+
throw new Error(invalidFieldMessage(field, "string"));
|
|
19
|
+
const value = raw.trim();
|
|
20
|
+
return value || undefined;
|
|
21
|
+
}
|
|
22
|
+
function normalizeStringList(raw, field, opts) {
|
|
23
|
+
if (!Array.isArray(raw))
|
|
24
|
+
throw new Error(invalidFieldMessage(field, "string[]"));
|
|
25
|
+
const values = raw.map((value) => {
|
|
26
|
+
if (typeof value !== "string")
|
|
27
|
+
throw new Error(invalidFieldMessage(field, "string[]"));
|
|
28
|
+
const trimmed = value.trim();
|
|
29
|
+
if (!trimmed)
|
|
30
|
+
throw new Error(invalidFieldMessage(field, "string[]"));
|
|
31
|
+
return trimmed;
|
|
32
|
+
});
|
|
33
|
+
const deduped = dedupeStrings(values);
|
|
34
|
+
if ((opts?.minLength ?? 0) > 0 && deduped.length < (opts?.minLength ?? 0)) {
|
|
35
|
+
throw new Error(invalidFieldMessage(field, `string[${opts?.minLength ?? 0}+]`));
|
|
36
|
+
}
|
|
37
|
+
return deduped;
|
|
38
|
+
}
|
|
39
|
+
function normalizeOptionalStringList(raw, field) {
|
|
40
|
+
if (raw === undefined)
|
|
41
|
+
return undefined;
|
|
42
|
+
return normalizeStringList(raw, field);
|
|
43
|
+
}
|
|
44
|
+
function normalizeBoolean(raw, field) {
|
|
45
|
+
if (raw === undefined)
|
|
46
|
+
return undefined;
|
|
47
|
+
if (typeof raw !== "boolean")
|
|
48
|
+
throw new Error(invalidFieldMessage(field, "boolean"));
|
|
49
|
+
return raw;
|
|
50
|
+
}
|
|
51
|
+
function normalizeNumber(raw, field) {
|
|
52
|
+
if (raw === undefined)
|
|
53
|
+
return undefined;
|
|
54
|
+
if (typeof raw !== "number" || Number.isNaN(raw)) {
|
|
55
|
+
throw new Error(invalidFieldMessage(field, "number"));
|
|
56
|
+
}
|
|
57
|
+
return raw;
|
|
58
|
+
}
|
|
59
|
+
function normalizeCompatibility(raw) {
|
|
60
|
+
if (raw === undefined)
|
|
61
|
+
return undefined;
|
|
62
|
+
if (!isRecord(raw))
|
|
63
|
+
throw new Error(invalidFieldMessage("manifest.compatibility", "object"));
|
|
64
|
+
return {
|
|
65
|
+
min_agentplane_version: normalizeOptionalString(raw.min_agentplane_version, "manifest.compatibility.min_agentplane_version"),
|
|
66
|
+
manifest_api_version: normalizeOptionalString(raw.manifest_api_version, "manifest.compatibility.manifest_api_version"),
|
|
67
|
+
scenario_api_version: normalizeOptionalString(raw.scenario_api_version, "manifest.compatibility.scenario_api_version"),
|
|
68
|
+
runtime_api_version: normalizeOptionalString(raw.runtime_api_version, "manifest.compatibility.runtime_api_version"),
|
|
69
|
+
platforms: normalizeOptionalStringList(raw.platforms, "manifest.compatibility.platforms"),
|
|
70
|
+
repo_types: normalizeOptionalStringList(raw.repo_types, "manifest.compatibility.repo_types"),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function normalizeRunProfile(raw, field) {
|
|
74
|
+
if (!isRecord(raw))
|
|
75
|
+
throw new Error(invalidFieldMessage(field, "object"));
|
|
76
|
+
return {
|
|
77
|
+
mode: normalizeRequiredString(raw.mode, `${field}.mode`),
|
|
78
|
+
sandbox: normalizeOptionalString(raw.sandbox, `${field}.sandbox`),
|
|
79
|
+
network: normalizeBoolean(raw.network, `${field}.network`),
|
|
80
|
+
requires_human_approval: normalizeBoolean(raw.requires_human_approval, `${field}.requires_human_approval`),
|
|
81
|
+
writes_artifacts_to: normalizeOptionalStringList(raw.writes_artifacts_to, `${field}.writes_artifacts_to`),
|
|
82
|
+
expected_exit_contract: normalizeOptionalString(raw.expected_exit_contract, `${field}.expected_exit_contract`),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function normalizeSkills(raw) {
|
|
86
|
+
if (raw === undefined)
|
|
87
|
+
return undefined;
|
|
88
|
+
if (!Array.isArray(raw))
|
|
89
|
+
throw new Error(invalidFieldMessage("manifest.skills", "array"));
|
|
90
|
+
return raw.map((entry, index) => {
|
|
91
|
+
if (!isRecord(entry))
|
|
92
|
+
throw new Error(invalidFieldMessage(`manifest.skills[${index}]`, "object"));
|
|
93
|
+
return {
|
|
94
|
+
id: normalizeSkillId(normalizeRequiredString(entry.id, `manifest.skills[${index}].id`)),
|
|
95
|
+
summary: normalizeRequiredString(entry.summary, `manifest.skills[${index}].summary`),
|
|
96
|
+
kind: normalizeRequiredString(entry.kind, `manifest.skills[${index}].kind`),
|
|
97
|
+
file: normalizeRecipeRelativePath(`manifest.skills[${index}].file`, normalizeRequiredString(entry.file, `manifest.skills[${index}].file`)),
|
|
98
|
+
};
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
function normalizeTools(raw) {
|
|
102
|
+
if (raw === undefined)
|
|
103
|
+
return undefined;
|
|
104
|
+
if (!Array.isArray(raw))
|
|
105
|
+
throw new Error(invalidFieldMessage("manifest.tools", "array"));
|
|
106
|
+
return raw.map((entry, index) => {
|
|
107
|
+
if (!isRecord(entry))
|
|
108
|
+
throw new Error(invalidFieldMessage(`manifest.tools[${index}]`, "object"));
|
|
109
|
+
const runtime = normalizeRequiredString(entry.runtime, `manifest.tools[${index}].runtime`);
|
|
110
|
+
if (runtime !== "node" && runtime !== "bash") {
|
|
111
|
+
throw new Error(invalidFieldMessage(`manifest.tools[${index}].runtime`, '"node" | "bash"'));
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
id: normalizeToolId(normalizeRequiredString(entry.id, `manifest.tools[${index}].id`)),
|
|
115
|
+
summary: normalizeRequiredString(entry.summary, `manifest.tools[${index}].summary`),
|
|
116
|
+
runtime,
|
|
117
|
+
entrypoint: normalizeRecipeRelativePath(`manifest.tools[${index}].entrypoint`, normalizeRequiredString(entry.entrypoint, `manifest.tools[${index}].entrypoint`)),
|
|
118
|
+
permissions: normalizeOptionalStringList(entry.permissions, `manifest.tools[${index}].permissions`),
|
|
119
|
+
timeout_ms: normalizeNumber(entry.timeout_ms, `manifest.tools[${index}].timeout_ms`),
|
|
120
|
+
cwd_policy: normalizeOptionalString(entry.cwd_policy, `manifest.tools[${index}].cwd_policy`),
|
|
121
|
+
};
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
function normalizeAgents(raw) {
|
|
125
|
+
if (raw === undefined)
|
|
126
|
+
return undefined;
|
|
127
|
+
if (!Array.isArray(raw))
|
|
128
|
+
throw new Error(invalidFieldMessage("manifest.agents", "array"));
|
|
129
|
+
return raw.map((entry, index) => {
|
|
130
|
+
if (!isRecord(entry))
|
|
131
|
+
throw new Error(invalidFieldMessage(`manifest.agents[${index}]`, "object"));
|
|
132
|
+
return {
|
|
133
|
+
id: normalizeAgentId(normalizeRequiredString(entry.id, `manifest.agents[${index}].id`)),
|
|
134
|
+
display_name: normalizeRequiredString(entry.display_name, `manifest.agents[${index}].display_name`),
|
|
135
|
+
role: normalizeRequiredString(entry.role, `manifest.agents[${index}].role`),
|
|
136
|
+
summary: normalizeRequiredString(entry.summary, `manifest.agents[${index}].summary`),
|
|
137
|
+
skills: normalizeOptionalStringList(entry.skills, `manifest.agents[${index}].skills`),
|
|
138
|
+
tools: normalizeOptionalStringList(entry.tools, `manifest.agents[${index}].tools`),
|
|
139
|
+
file: normalizeRecipeRelativePath(`manifest.agents[${index}].file`, normalizeRequiredString(entry.file, `manifest.agents[${index}].file`)),
|
|
140
|
+
};
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
function normalizeScenarios(raw) {
|
|
144
|
+
if (!Array.isArray(raw) || raw.length === 0) {
|
|
145
|
+
throw new Error(invalidFieldMessage("manifest.scenarios", "non-empty array"));
|
|
146
|
+
}
|
|
147
|
+
return raw.map((entry, index) => {
|
|
148
|
+
if (!isRecord(entry)) {
|
|
149
|
+
throw new Error(invalidFieldMessage(`manifest.scenarios[${index}]`, "object"));
|
|
150
|
+
}
|
|
151
|
+
return {
|
|
152
|
+
id: normalizeScenarioId(normalizeRequiredString(entry.id, `manifest.scenarios[${index}].id`)),
|
|
153
|
+
name: normalizeRequiredString(entry.name, `manifest.scenarios[${index}].name`),
|
|
154
|
+
summary: normalizeRequiredString(entry.summary, `manifest.scenarios[${index}].summary`),
|
|
155
|
+
description: normalizeOptionalString(entry.description, `manifest.scenarios[${index}].description`),
|
|
156
|
+
use_when: normalizeStringList(entry.use_when, `manifest.scenarios[${index}].use_when`, {
|
|
157
|
+
minLength: 1,
|
|
158
|
+
}),
|
|
159
|
+
avoid_when: normalizeOptionalStringList(entry.avoid_when, `manifest.scenarios[${index}].avoid_when`),
|
|
160
|
+
required_inputs: normalizeStringList(entry.required_inputs, `manifest.scenarios[${index}].required_inputs`),
|
|
161
|
+
outputs: normalizeStringList(entry.outputs, `manifest.scenarios[${index}].outputs`),
|
|
162
|
+
permissions: normalizeStringList(Array.isArray(entry.permissions) ? entry.permissions : [], `manifest.scenarios[${index}].permissions`),
|
|
163
|
+
artifacts: normalizeStringList(Array.isArray(entry.artifacts) ? entry.artifacts : [], `manifest.scenarios[${index}].artifacts`),
|
|
164
|
+
agents_involved: normalizeStringList(entry.agents_involved, `manifest.scenarios[${index}].agents_involved`, { minLength: 1 }),
|
|
165
|
+
skills_used: normalizeStringList(Array.isArray(entry.skills_used) ? entry.skills_used : [], `manifest.scenarios[${index}].skills_used`),
|
|
166
|
+
tools_used: normalizeStringList(Array.isArray(entry.tools_used) ? entry.tools_used : [], `manifest.scenarios[${index}].tools_used`),
|
|
167
|
+
run_profile: normalizeRunProfile(entry.run_profile, `manifest.scenarios[${index}].run_profile`),
|
|
168
|
+
file: normalizeRecipeRelativePath(`manifest.scenarios[${index}].file`, normalizeRequiredString(entry.file, `manifest.scenarios[${index}].file`)),
|
|
169
|
+
};
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
function assertUniqueIds(field, items) {
|
|
173
|
+
const seen = new Set();
|
|
174
|
+
for (const item of items) {
|
|
175
|
+
if (seen.has(item.id)) {
|
|
176
|
+
throw new Error(invalidFieldMessage(field, `unique ids (duplicate: ${item.id})`));
|
|
177
|
+
}
|
|
178
|
+
seen.add(item.id);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
function assertKnownReferences(field, refs, known) {
|
|
182
|
+
if (!refs || refs.length === 0)
|
|
183
|
+
return;
|
|
184
|
+
const missing = refs.filter((ref) => !known.has(ref));
|
|
185
|
+
if (missing.length > 0) {
|
|
186
|
+
throw new Error(invalidFieldMessage(field, `known ids (missing: ${missing.join(", ")})`));
|
|
187
|
+
}
|
|
188
|
+
}
|
|
5
189
|
export function validateRecipeManifest(raw) {
|
|
6
190
|
if (!isRecord(raw))
|
|
7
191
|
throw new Error(invalidFieldMessage("manifest", "object"));
|
|
8
192
|
if (raw.schema_version !== "1")
|
|
9
193
|
throw new Error(invalidFieldMessage("manifest.schema_version", '"1"'));
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
if (typeof raw.version !== "string")
|
|
13
|
-
throw new Error(invalidFieldMessage("manifest.version", "string"));
|
|
14
|
-
if (typeof raw.name !== "string")
|
|
15
|
-
throw new Error(invalidFieldMessage("manifest.name", "string"));
|
|
16
|
-
if (typeof raw.summary !== "string")
|
|
17
|
-
throw new Error(invalidFieldMessage("manifest.summary", "string"));
|
|
18
|
-
if (typeof raw.description !== "string")
|
|
19
|
-
throw new Error(invalidFieldMessage("manifest.description", "string"));
|
|
20
|
-
const id = normalizeRecipeId(raw.id);
|
|
21
|
-
const version = raw.version.trim();
|
|
22
|
-
if (!version)
|
|
23
|
-
throw new Error(requiredFieldMessage("manifest.version"));
|
|
194
|
+
const id = normalizeRecipeId(normalizeRequiredString(raw.id, "manifest.id"));
|
|
195
|
+
const version = normalizeRequiredString(raw.version, "manifest.version");
|
|
24
196
|
const tags = normalizeRecipeTags(raw.tags);
|
|
197
|
+
const compatibility = normalizeCompatibility(raw.compatibility);
|
|
198
|
+
const skills = normalizeSkills(raw.skills);
|
|
199
|
+
const tools = normalizeTools(raw.tools);
|
|
200
|
+
const agents = normalizeAgents(raw.agents);
|
|
201
|
+
const scenarios = normalizeScenarios(raw.scenarios);
|
|
202
|
+
if (skills)
|
|
203
|
+
assertUniqueIds("manifest.skills", skills);
|
|
204
|
+
if (tools)
|
|
205
|
+
assertUniqueIds("manifest.tools", tools);
|
|
206
|
+
if (agents)
|
|
207
|
+
assertUniqueIds("manifest.agents", agents);
|
|
208
|
+
assertUniqueIds("manifest.scenarios", scenarios);
|
|
209
|
+
const skillIds = new Set((skills ?? []).map((skill) => skill.id));
|
|
210
|
+
const toolIds = new Set((tools ?? []).map((tool) => tool.id));
|
|
211
|
+
const agentIds = new Set((agents ?? []).map((agent) => agent.id));
|
|
212
|
+
for (const [index, agent] of (agents ?? []).entries()) {
|
|
213
|
+
assertKnownReferences(`manifest.agents[${index}].skills`, agent.skills, skillIds);
|
|
214
|
+
assertKnownReferences(`manifest.agents[${index}].tools`, agent.tools, toolIds);
|
|
215
|
+
}
|
|
216
|
+
for (const [index, scenario] of scenarios.entries()) {
|
|
217
|
+
assertKnownReferences(`manifest.scenarios[${index}].agents_involved`, scenario.agents_involved, agentIds);
|
|
218
|
+
assertKnownReferences(`manifest.scenarios[${index}].skills_used`, scenario.skills_used, skillIds);
|
|
219
|
+
assertKnownReferences(`manifest.scenarios[${index}].tools_used`, scenario.tools_used, toolIds);
|
|
220
|
+
}
|
|
25
221
|
return {
|
|
26
222
|
schema_version: "1",
|
|
27
223
|
id,
|
|
28
224
|
version,
|
|
29
|
-
name: raw.name.
|
|
30
|
-
summary: raw.summary.
|
|
31
|
-
description: raw.description.
|
|
225
|
+
name: normalizeRequiredString(raw.name, "manifest.name"),
|
|
226
|
+
summary: normalizeRequiredString(raw.summary, "manifest.summary"),
|
|
227
|
+
description: normalizeRequiredString(raw.description, "manifest.description"),
|
|
32
228
|
tags: tags.length > 0 ? tags : undefined,
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
229
|
+
compatibility,
|
|
230
|
+
skills,
|
|
231
|
+
agents,
|
|
232
|
+
tools,
|
|
233
|
+
scenarios,
|
|
38
234
|
};
|
|
39
235
|
}
|
|
40
236
|
export async function readRecipeManifest(manifestPath) {
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
export declare function normalizeRecipeId(value: string): string;
|
|
2
2
|
export declare function normalizeAgentId(value: string): string;
|
|
3
|
+
export declare function normalizeSkillId(value: string): string;
|
|
4
|
+
export declare function normalizeToolId(value: string): string;
|
|
3
5
|
export declare function normalizeScenarioId(value: string): string;
|
|
4
6
|
export declare function normalizeRecipeTags(value: unknown): string[];
|
|
7
|
+
export declare function normalizeRecipeRelativePath(field: string, value: string): string;
|
|
5
8
|
//# sourceMappingURL=normalize.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../../../../src/commands/recipes/impl/normalize.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../../../../src/commands/recipes/impl/normalize.ts"],"names":[],"mappings":"AAmBA,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,CAQ5D;AAED,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAWhF"}
|
|
@@ -1,40 +1,31 @@
|
|
|
1
1
|
import { invalidPathMessage, requiredFieldMessage, invalidFieldMessage, } from "../../../cli/output.js";
|
|
2
2
|
import { dedupeStrings } from "../../../shared/strings.js";
|
|
3
|
-
|
|
3
|
+
function normalizeScopedId(field, value) {
|
|
4
4
|
const trimmed = value.trim();
|
|
5
5
|
if (!trimmed)
|
|
6
|
-
throw new Error(requiredFieldMessage(
|
|
6
|
+
throw new Error(requiredFieldMessage(field));
|
|
7
7
|
if (trimmed.includes("/") || trimmed.includes("\\")) {
|
|
8
|
-
throw new Error(invalidPathMessage(
|
|
8
|
+
throw new Error(invalidPathMessage(field, "must not contain path separators"));
|
|
9
9
|
}
|
|
10
10
|
if (trimmed === "." || trimmed === "..") {
|
|
11
|
-
throw new Error(invalidPathMessage(
|
|
11
|
+
throw new Error(invalidPathMessage(field, "must not be '.' or '..'"));
|
|
12
12
|
}
|
|
13
13
|
return trimmed;
|
|
14
14
|
}
|
|
15
|
+
export function normalizeRecipeId(value) {
|
|
16
|
+
return normalizeScopedId("manifest.id", value);
|
|
17
|
+
}
|
|
15
18
|
export function normalizeAgentId(value) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
throw new Error(invalidPathMessage("agent.id", "must not be '.' or '..'"));
|
|
24
|
-
}
|
|
25
|
-
return trimmed;
|
|
19
|
+
return normalizeScopedId("agent.id", value);
|
|
20
|
+
}
|
|
21
|
+
export function normalizeSkillId(value) {
|
|
22
|
+
return normalizeScopedId("skill.id", value);
|
|
23
|
+
}
|
|
24
|
+
export function normalizeToolId(value) {
|
|
25
|
+
return normalizeScopedId("tool.id", value);
|
|
26
26
|
}
|
|
27
27
|
export function normalizeScenarioId(value) {
|
|
28
|
-
|
|
29
|
-
if (!trimmed)
|
|
30
|
-
throw new Error(requiredFieldMessage("scenario.id"));
|
|
31
|
-
if (trimmed.includes("/") || trimmed.includes("\\")) {
|
|
32
|
-
throw new Error(invalidPathMessage("scenario.id", "must not contain path separators"));
|
|
33
|
-
}
|
|
34
|
-
if (trimmed === "." || trimmed === "..") {
|
|
35
|
-
throw new Error(invalidPathMessage("scenario.id", "must not be '.' or '..'"));
|
|
36
|
-
}
|
|
37
|
-
return trimmed;
|
|
28
|
+
return normalizeScopedId("scenario.id", value);
|
|
38
29
|
}
|
|
39
30
|
export function normalizeRecipeTags(value) {
|
|
40
31
|
if (value === undefined)
|
|
@@ -48,3 +39,16 @@ export function normalizeRecipeTags(value) {
|
|
|
48
39
|
});
|
|
49
40
|
return dedupeStrings(tags);
|
|
50
41
|
}
|
|
42
|
+
export function normalizeRecipeRelativePath(field, value) {
|
|
43
|
+
const trimmed = value.trim().replaceAll("\\", "/");
|
|
44
|
+
if (!trimmed)
|
|
45
|
+
throw new Error(requiredFieldMessage(field));
|
|
46
|
+
if (trimmed.startsWith("/")) {
|
|
47
|
+
throw new Error(invalidPathMessage(field, "must be relative"));
|
|
48
|
+
}
|
|
49
|
+
const segments = trimmed.split("/");
|
|
50
|
+
if (segments.some((segment) => !segment || segment === "." || segment === "..")) {
|
|
51
|
+
throw new Error(invalidPathMessage(field, "must stay within the recipe root"));
|
|
52
|
+
}
|
|
53
|
+
return trimmed;
|
|
54
|
+
}
|
|
@@ -7,6 +7,15 @@ export declare function resolveInstalledRecipeDir(entry: {
|
|
|
7
7
|
id: string;
|
|
8
8
|
version: string;
|
|
9
9
|
}): string;
|
|
10
|
+
export declare function resolveProjectRecipesDir(resolved: {
|
|
11
|
+
agentplaneDir: string;
|
|
12
|
+
}): string;
|
|
13
|
+
export declare function resolveProjectInstalledRecipeDir(resolved: {
|
|
14
|
+
agentplaneDir: string;
|
|
15
|
+
}, recipeId: string): string;
|
|
16
|
+
export declare function resolveProjectRecipeInstallMetaPath(resolved: {
|
|
17
|
+
agentplaneDir: string;
|
|
18
|
+
}, recipeId: string): string;
|
|
10
19
|
export declare function resolveProjectRecipesCacheDir(resolved: {
|
|
11
20
|
agentplaneDir: string;
|
|
12
21
|
}): string;
|
|
@@ -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":"AAcA,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,CAC9C,QAAQ,EAAE;IAAE,aAAa,EAAE,MAAM,CAAA;CAAE,EACnC,QAAQ,EAAE,MAAM,GACf,MAAM,CAER;AAED,wBAAgB,mCAAmC,CACjD,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"}
|
|
@@ -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, PROJECT_RECIPES_CACHE_DIR_NAME, RECIPES_REMOTE_INDEX_NAME, RECIPES_REMOTE_INDEX_SIG_NAME, } from "./constants.js";
|
|
3
|
+
import { AGENTPLANE_HOME_ENV, GLOBAL_RECIPES_DIR_NAME, INSTALLED_RECIPES_NAME, RECIPE_INSTALL_META_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)
|
|
@@ -22,6 +22,15 @@ export function resolveRecipesIndexCacheSigPath() {
|
|
|
22
22
|
export function resolveInstalledRecipeDir(entry) {
|
|
23
23
|
return path.join(resolveGlobalRecipesDir(), entry.id, entry.version);
|
|
24
24
|
}
|
|
25
|
+
export function resolveProjectRecipesDir(resolved) {
|
|
26
|
+
return path.join(resolved.agentplaneDir, RECIPES_DIR_NAME);
|
|
27
|
+
}
|
|
28
|
+
export function resolveProjectInstalledRecipeDir(resolved, recipeId) {
|
|
29
|
+
return path.join(resolveProjectRecipesDir(resolved), recipeId);
|
|
30
|
+
}
|
|
31
|
+
export function resolveProjectRecipeInstallMetaPath(resolved, recipeId) {
|
|
32
|
+
return path.join(resolveProjectInstalledRecipeDir(resolved, recipeId), RECIPE_INSTALL_META_NAME);
|
|
33
|
+
}
|
|
25
34
|
export function resolveProjectRecipesCacheDir(resolved) {
|
|
26
35
|
return path.join(resolved.agentplaneDir, PROJECT_RECIPES_CACHE_DIR_NAME);
|
|
27
36
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { InstalledRecipesFile, RecipeInstallMetadata } from "./types.js";
|
|
2
|
+
export declare function readRecipeInstallMetadata(filePath: string): Promise<RecipeInstallMetadata | null>;
|
|
3
|
+
export declare function writeRecipeInstallMetadata(filePath: string, metadata: RecipeInstallMetadata): Promise<void>;
|
|
4
|
+
export declare function readProjectInstalledRecipes(opts: {
|
|
5
|
+
agentplaneDir: string;
|
|
6
|
+
}): Promise<InstalledRecipesFile>;
|
|
7
|
+
//# sourceMappingURL=project-installed-recipes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-installed-recipes.d.ts","sourceRoot":"","sources":["../../../../src/commands/recipes/impl/project-installed-recipes.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAwB,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAuCpG,wBAAsB,yBAAyB,CAC7C,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC,CASvC;AAED,wBAAsB,0BAA0B,CAC9C,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,qBAAqB,GAC9B,OAAO,CAAC,IAAI,CAAC,CAGf;AAED,wBAAsB,2BAA2B,CAAC,IAAI,EAAE;IACtD,aAAa,EAAE,MAAM,CAAC;CACvB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CA0DhC"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { mkdir, readdir, readFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileExists, getPathKind } from "../../../cli/fs-utils.js";
|
|
4
|
+
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 { readRecipeManifest } from "./manifest.js";
|
|
8
|
+
import { normalizeRecipeTags } from "./normalize.js";
|
|
9
|
+
import { resolveProjectInstalledRecipeDir, resolveProjectRecipeInstallMetaPath, resolveProjectRecipesDir, } from "./paths.js";
|
|
10
|
+
function validateRecipeInstallMetadata(raw) {
|
|
11
|
+
if (!isRecord(raw))
|
|
12
|
+
throw new Error(invalidFieldMessage("recipe install metadata", "object"));
|
|
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
|
+
}
|
|
40
|
+
function sortInstalledRecipes(entries) {
|
|
41
|
+
return [...entries].toSorted((a, b) => a.id.localeCompare(b.id));
|
|
42
|
+
}
|
|
43
|
+
export async function readRecipeInstallMetadata(filePath) {
|
|
44
|
+
try {
|
|
45
|
+
const raw = JSON.parse(await readFile(filePath, "utf8"));
|
|
46
|
+
return validateRecipeInstallMetadata(raw);
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
const code = err?.code;
|
|
50
|
+
if (code === "ENOENT")
|
|
51
|
+
return null;
|
|
52
|
+
throw err;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
export async function writeRecipeInstallMetadata(filePath, metadata) {
|
|
56
|
+
await mkdir(path.dirname(filePath), { recursive: true });
|
|
57
|
+
await writeJsonStableIfChanged(filePath, metadata);
|
|
58
|
+
}
|
|
59
|
+
export async function readProjectInstalledRecipes(opts) {
|
|
60
|
+
const recipesDir = resolveProjectRecipesDir(opts);
|
|
61
|
+
if ((await getPathKind(recipesDir)) !== "dir") {
|
|
62
|
+
return { schema_version: 1, updated_at: "", recipes: [] };
|
|
63
|
+
}
|
|
64
|
+
const entries = [];
|
|
65
|
+
const dirs = await readdir(recipesDir, { withFileTypes: true });
|
|
66
|
+
for (const dirent of dirs) {
|
|
67
|
+
if (!dirent.isDirectory())
|
|
68
|
+
continue;
|
|
69
|
+
const recipeDir = resolveProjectInstalledRecipeDir(opts, dirent.name);
|
|
70
|
+
const manifestPath = path.join(recipeDir, "manifest.json");
|
|
71
|
+
if (!(await fileExists(manifestPath))) {
|
|
72
|
+
throw new Error(missingFileMessage("installed recipe manifest", manifestPath));
|
|
73
|
+
}
|
|
74
|
+
const manifest = await readRecipeManifest(manifestPath);
|
|
75
|
+
if (manifest.id !== dirent.name) {
|
|
76
|
+
throw new Error(invalidFieldMessage(`installed recipe directory ${dirent.name}`, `manifest.id=${dirent.name}`));
|
|
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"));
|
|
81
|
+
}
|
|
82
|
+
entries.push({
|
|
83
|
+
id: manifest.id,
|
|
84
|
+
version: manifest.version,
|
|
85
|
+
source: metadata?.source ?? "project-local",
|
|
86
|
+
installed_at: metadata?.installed_at ?? "",
|
|
87
|
+
tags: normalizeRecipeTags(metadata?.tags ?? manifest.tags ?? []),
|
|
88
|
+
manifest,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
const sorted = sortInstalledRecipes(entries);
|
|
92
|
+
const updatedAt = sorted
|
|
93
|
+
.map((entry) => entry.installed_at)
|
|
94
|
+
.filter(Boolean)
|
|
95
|
+
.toSorted()
|
|
96
|
+
.at(-1);
|
|
97
|
+
return {
|
|
98
|
+
schema_version: 1,
|
|
99
|
+
updated_at: updatedAt ?? "",
|
|
100
|
+
recipes: sorted,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ResolvedProject } from "@agentplaneorg/core";
|
|
2
|
+
import type { RecipeCompatibility, RecipeResolverCompatibility, RecipeResolverContext, RecipeScenarioDescriptor, ResolveRecipeScenarioSelectionFlags, ResolvedRecipeRunProfile, ResolvedRecipeScenario, ResolvedRecipeScenarioSelection } from "./types.js";
|
|
3
|
+
export declare function buildRecipeResolverContext(opts: {
|
|
4
|
+
project: ResolvedProject;
|
|
5
|
+
}): Promise<RecipeResolverContext>;
|
|
6
|
+
export declare function resolveRecipeCompatibility(opts: {
|
|
7
|
+
compatibility?: RecipeCompatibility;
|
|
8
|
+
context: RecipeResolverContext;
|
|
9
|
+
}): RecipeResolverCompatibility;
|
|
10
|
+
export declare function normalizeResolvedRecipeRunProfile(scenario: RecipeScenarioDescriptor): ResolvedRecipeRunProfile;
|
|
11
|
+
export declare function listResolvedRecipeScenarios(opts: {
|
|
12
|
+
project: ResolvedProject;
|
|
13
|
+
recipeId?: string;
|
|
14
|
+
includeIncompatible?: boolean;
|
|
15
|
+
}): Promise<ResolvedRecipeScenario[]>;
|
|
16
|
+
export declare function resolveRecipeScenarioSelection(opts: {
|
|
17
|
+
project: ResolvedProject;
|
|
18
|
+
flags: ResolveRecipeScenarioSelectionFlags;
|
|
19
|
+
}): Promise<ResolvedRecipeScenarioSelection>;
|
|
20
|
+
//# sourceMappingURL=resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../../../../src/commands/recipes/impl/resolver.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAS3D,OAAO,KAAK,EAEV,mBAAmB,EACnB,2BAA2B,EAE3B,qBAAqB,EAErB,wBAAwB,EACxB,mCAAmC,EACnC,wBAAwB,EACxB,sBAAsB,EACtB,+BAA+B,EAChC,MAAM,YAAY,CAAC;AAsBpB,wBAAsB,0BAA0B,CAAC,IAAI,EAAE;IACrD,OAAO,EAAE,eAAe,CAAC;CAC1B,GAAG,OAAO,CAAC,qBAAqB,CAAC,CASjC;AAYD,wBAAgB,0BAA0B,CAAC,IAAI,EAAE;IAC/C,aAAa,CAAC,EAAE,mBAAmB,CAAC;IACpC,OAAO,EAAE,qBAAqB,CAAC;CAChC,GAAG,2BAA2B,CAkG9B;AAED,wBAAgB,iCAAiC,CAC/C,QAAQ,EAAE,wBAAwB,GACjC,wBAAwB,CAmB1B;AA6BD,wBAAsB,2BAA2B,CAAC,IAAI,EAAE;IACtD,OAAO,EAAE,eAAe,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B,GAAG,OAAO,CAAC,sBAAsB,EAAE,CAAC,CAsBpC;AAED,wBAAsB,8BAA8B,CAAC,IAAI,EAAE;IACzD,OAAO,EAAE,eAAe,CAAC;IACzB,KAAK,EAAE,mCAAmC,CAAC;CAC5C,GAAG,OAAO,CAAC,+BAA+B,CAAC,CA2D3C"}
|