create-agdf 0.13.5 → 0.13.6
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 +7 -1
- package/generated/plugins/agdf/.claude-plugin/plugin.json +1 -1
- package/generated/plugins/agdf/.codex-plugin/plugin.json +2 -3
- package/generated/plugins/agdf/hooks/session-start.sh +31 -0
- package/generated/plugins/agdf/meta/agdf-plugin.definition.json +30 -9
- package/generated/plugins/agdf/runtime/agdf-local.js +8 -1
- package/generated/plugins/agdf/runtime/create-agdf/generated/plugins/agdf/meta/agdf-plugin.definition.json +30 -9
- package/generated/plugins/agdf/runtime/create-agdf/lib/runtime/local-validator.js +116 -31
- package/generated/plugins/agdf/runtime/create-agdf/lib/runtime/plugin-provenance.js +206 -0
- package/generated/plugins/agdf/runtime/create-agdf/package.json +1 -1
- package/generated/plugins/agdf/runtime/runtime-manifest.json +2 -2
- package/generated/plugins/agdf/scripts/check-runtime-integrity.mjs +64 -19
- package/generated/plugins/agdf/submission/openai/availability.md +1 -1
- package/generated/plugins/agdf/submission/openai/capability-matrix.json +1 -1
- package/generated/plugins/agdf/submission/openai/release-notes.md +1 -1
- package/generated/plugins/agdf/submission/openai/reviewer-cases.json +1 -1
- package/generated/submissions/openai/agdf/.codex-plugin/plugin.json +1 -1
- package/generated/submissions/openai/agdf/meta/agdf-plugin.definition.json +30 -9
- package/generated/submissions/openai/agdf/submission/openai/availability.md +1 -1
- package/generated/submissions/openai/agdf/submission/openai/capability-matrix.json +1 -1
- package/generated/submissions/openai/agdf/submission/openai/inventory.json +9 -9
- package/generated/submissions/openai/agdf/submission/openai/listing.json +1 -1
- package/generated/submissions/openai/agdf/submission/openai/readiness.json +2 -2
- package/generated/submissions/openai/agdf/submission/openai/readiness.md +2 -2
- package/generated/submissions/openai/agdf/submission/openai/release-notes.md +1 -1
- package/generated/submissions/openai/agdf/submission/openai/reviewer-cases.json +1 -1
- package/lib/cli/application.js +4 -3
- package/lib/fs-swap.js +26 -0
- package/lib/installers/local-development.js +202 -0
- package/lib/installers/local-marketplace.js +181 -36
- package/lib/installers/opencode.js +17 -2
- package/lib/installers/plugin-installers.js +86 -22
- package/lib/lifecycle/operations.js +9 -5
- package/lib/lifecycle/status.js +10 -2
- package/lib/public-plugin/builder.js +5 -4
- package/lib/runtime/local-validator.js +116 -31
- package/lib/runtime/plugin-provenance.js +206 -0
- package/package.json +6 -2
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
3
|
+
import { isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
4
|
+
|
|
5
|
+
export const INSTALLATION_PROVENANCE_FILE = ".agdf-installation.json";
|
|
6
|
+
export const LEGACY_LOCAL_INSTALL_FILE = ".agdf-local-install.json";
|
|
7
|
+
|
|
8
|
+
const EXPECTED_PROFILES = Object.freeze({
|
|
9
|
+
"source-development": Object.freeze({ runtime: "absent", installable: false, machineValidation: "unavailable" }),
|
|
10
|
+
"runtime-plugin": Object.freeze({ runtime: "required", installable: true, machineValidation: "local_exact_version_digest" }),
|
|
11
|
+
"opencode-config-local": Object.freeze({ runtime: "config_local_package", installable: true, machineValidation: "local_exact_version" }),
|
|
12
|
+
"portable-skills": Object.freeze({ runtime: "absent", installable: true, machineValidation: "unavailable_or_external_required" }),
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
function readJson(path) {
|
|
16
|
+
try {
|
|
17
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
18
|
+
} catch {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function digestDirectory(root) {
|
|
24
|
+
const files = [];
|
|
25
|
+
function visit(directory) {
|
|
26
|
+
for (const name of readdirSync(directory).sort()) {
|
|
27
|
+
const path = join(directory, name);
|
|
28
|
+
const stats = statSync(path);
|
|
29
|
+
if (stats.isDirectory()) visit(path);
|
|
30
|
+
else if (stats.isFile()) files.push(path);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
visit(root);
|
|
34
|
+
const hash = createHash("sha256");
|
|
35
|
+
for (const path of files) {
|
|
36
|
+
hash.update(relative(root, path).replaceAll("\\", "/"));
|
|
37
|
+
hash.update("\0");
|
|
38
|
+
hash.update(readFileSync(path));
|
|
39
|
+
hash.update("\0");
|
|
40
|
+
}
|
|
41
|
+
return hash.digest("hex");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function digestNormalizedPluginSource(root, canonicalVersion) {
|
|
45
|
+
const files = [];
|
|
46
|
+
function visit(directory) {
|
|
47
|
+
for (const name of readdirSync(directory).sort()) {
|
|
48
|
+
const path = join(directory, name);
|
|
49
|
+
const stats = statSync(path);
|
|
50
|
+
if (stats.isDirectory()) visit(path);
|
|
51
|
+
else if (stats.isFile()) files.push(path);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
visit(root);
|
|
55
|
+
const hash = createHash("sha256");
|
|
56
|
+
for (const path of files) {
|
|
57
|
+
const normalizedPath = relative(root, path).replaceAll("\\", "/");
|
|
58
|
+
if ([INSTALLATION_PROVENANCE_FILE, LEGACY_LOCAL_INSTALL_FILE].includes(normalizedPath)) continue;
|
|
59
|
+
let content = readFileSync(path);
|
|
60
|
+
if (normalizedPath === ".codex-plugin/plugin.json") {
|
|
61
|
+
const manifest = readJson(path);
|
|
62
|
+
if (!manifest) throw new Error(`Invalid Codex plugin manifest: ${path}`);
|
|
63
|
+
content = `${JSON.stringify({ ...manifest, version: canonicalVersion }, null, 2)}\n`;
|
|
64
|
+
}
|
|
65
|
+
hash.update(normalizedPath);
|
|
66
|
+
hash.update("\0");
|
|
67
|
+
hash.update(content);
|
|
68
|
+
hash.update("\0");
|
|
69
|
+
}
|
|
70
|
+
return hash.digest("hex");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function validateDistributionProfiles(definition) {
|
|
74
|
+
const contract = definition?.distributionProfiles;
|
|
75
|
+
if (contract?.schemaVersion !== 1) return { status: "invalid", reason: "profile_invalid" };
|
|
76
|
+
if (contract.marketplaceIdentities?.durable !== "agdf"
|
|
77
|
+
|| contract.marketplaceIdentities?.generatedRepository !== "agdf-repo") {
|
|
78
|
+
return { status: "invalid", reason: "profile_invalid" };
|
|
79
|
+
}
|
|
80
|
+
for (const [profileId, expected] of Object.entries(EXPECTED_PROFILES)) {
|
|
81
|
+
const observed = contract.profiles?.[profileId];
|
|
82
|
+
if (!observed
|
|
83
|
+
|| Object.keys(observed).length !== Object.keys(expected).length
|
|
84
|
+
|| Object.keys(expected).some((key) => observed[key] !== expected[key])) {
|
|
85
|
+
return { status: "invalid", reason: "profile_invalid" };
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (Object.keys(contract.profiles ?? {}).length !== Object.keys(EXPECTED_PROFILES).length) {
|
|
89
|
+
return { status: "invalid", reason: "profile_invalid" };
|
|
90
|
+
}
|
|
91
|
+
return { status: "matched", contract };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function inspectGeneratedRepositoryMarketplace(targetDir) {
|
|
95
|
+
const marketplacePath = join(targetDir, ".agents", "plugins", "marketplace.json");
|
|
96
|
+
if (!existsSync(marketplacePath)) return { status: "absent", marketplacePath };
|
|
97
|
+
const marketplace = readJson(marketplacePath);
|
|
98
|
+
const plugin = marketplace?.plugins?.length === 1 ? marketplace.plugins[0] : null;
|
|
99
|
+
const pluginPath = plugin?.source?.source === "local" ? plugin.source.path : null;
|
|
100
|
+
if (marketplace?.name !== "agdf-repo" || plugin?.name !== "agdf" || pluginPath !== "./plugins/agdf") {
|
|
101
|
+
return { status: "invalid", reason: "repository_marketplace_invalid", marketplacePath };
|
|
102
|
+
}
|
|
103
|
+
const pluginRoot = resolve(targetDir, pluginPath);
|
|
104
|
+
const rel = relative(resolve(targetDir), pluginRoot);
|
|
105
|
+
if (rel === ".." || rel.startsWith(`..${sep}`) || isAbsolute(rel)) {
|
|
106
|
+
return { status: "invalid", reason: "repository_marketplace_path_escape", marketplacePath };
|
|
107
|
+
}
|
|
108
|
+
const definition = readJson(join(pluginRoot, "meta", "agdf-plugin.definition.json"));
|
|
109
|
+
const runtimeManifest = readJson(join(pluginRoot, "runtime", "runtime-manifest.json"));
|
|
110
|
+
const codexManifest = readJson(join(pluginRoot, ".codex-plugin", "plugin.json"));
|
|
111
|
+
const claudeManifest = readJson(join(pluginRoot, ".claude-plugin", "plugin.json"));
|
|
112
|
+
const runtimeRoot = join(pluginRoot, "runtime");
|
|
113
|
+
const runtimePackageRoot = join(pluginRoot, "runtime", "create-agdf");
|
|
114
|
+
const runtimeEntrypoint = resolve(runtimeRoot, runtimeManifest?.entrypoint ?? "");
|
|
115
|
+
const runtimeEntrypointRel = relative(runtimeRoot, runtimeEntrypoint);
|
|
116
|
+
if (validateDistributionProfiles(definition).status !== "matched"
|
|
117
|
+
|| definition?.version !== runtimeManifest?.version
|
|
118
|
+
|| codexManifest?.version !== definition?.version
|
|
119
|
+
|| claudeManifest?.version !== definition?.version
|
|
120
|
+
|| !/^[a-f0-9]{64}$/.test(runtimeManifest?.digest ?? "")
|
|
121
|
+
|| !existsSync(join(runtimeRoot, "agdf-local.js"))
|
|
122
|
+
|| !runtimeManifest?.entrypoint
|
|
123
|
+
|| runtimeEntrypointRel === ".."
|
|
124
|
+
|| runtimeEntrypointRel.startsWith(`..${sep}`)
|
|
125
|
+
|| isAbsolute(runtimeEntrypointRel)
|
|
126
|
+
|| !existsSync(runtimeEntrypoint)
|
|
127
|
+
|| !existsSync(runtimePackageRoot)) {
|
|
128
|
+
return { status: "invalid", reason: "repository_runtime_incomplete", marketplacePath, pluginRoot };
|
|
129
|
+
}
|
|
130
|
+
let runtimeDigest;
|
|
131
|
+
try { runtimeDigest = digestDirectory(runtimePackageRoot); } catch {
|
|
132
|
+
return { status: "invalid", reason: "repository_runtime_incomplete", marketplacePath, pluginRoot };
|
|
133
|
+
}
|
|
134
|
+
if (runtimeDigest !== runtimeManifest.digest) {
|
|
135
|
+
return { status: "invalid", reason: "runtime_digest_mismatch", marketplacePath, pluginRoot };
|
|
136
|
+
}
|
|
137
|
+
return {
|
|
138
|
+
status: "matched",
|
|
139
|
+
marketplacePath,
|
|
140
|
+
pluginRoot,
|
|
141
|
+
selector: `${plugin.name}@${marketplace.name}`,
|
|
142
|
+
runtimeDigest,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function inspectInstallationProvenance(pluginRoot, {
|
|
147
|
+
definition,
|
|
148
|
+
runtimeManifest,
|
|
149
|
+
pluginVersion,
|
|
150
|
+
allowLegacy = false,
|
|
151
|
+
} = {}) {
|
|
152
|
+
const markerPath = join(pluginRoot, INSTALLATION_PROVENANCE_FILE);
|
|
153
|
+
const legacyPath = join(pluginRoot, LEGACY_LOCAL_INSTALL_FILE);
|
|
154
|
+
if (!existsSync(markerPath)) {
|
|
155
|
+
if (allowLegacy && existsSync(legacyPath)) {
|
|
156
|
+
const marker = readJson(legacyPath);
|
|
157
|
+
if (!marker
|
|
158
|
+
|| marker.schema_version !== 1
|
|
159
|
+
|| marker.owner !== "create-agdf"
|
|
160
|
+
|| marker.kind !== "codex_local_development_projection"
|
|
161
|
+
|| marker.canonical_version !== definition.version
|
|
162
|
+
|| marker.codex_install_version !== pluginVersion
|
|
163
|
+
|| !/^[a-f0-9]{64}$/.test(marker.source_digest ?? "")) {
|
|
164
|
+
return { status: "invalid", reason: "installation_provenance_invalid", marker };
|
|
165
|
+
}
|
|
166
|
+
let observedSourceDigest;
|
|
167
|
+
try { observedSourceDigest = digestNormalizedPluginSource(pluginRoot, definition.version); } catch {
|
|
168
|
+
return { status: "invalid", reason: "source_digest_mismatch", marker };
|
|
169
|
+
}
|
|
170
|
+
return observedSourceDigest === marker.source_digest
|
|
171
|
+
? { status: "legacy", marker, observedSourceDigest, reason: "legacy_installation_provenance" }
|
|
172
|
+
: { status: "invalid", reason: "source_digest_mismatch", marker, observedSourceDigest };
|
|
173
|
+
}
|
|
174
|
+
const profile = validateDistributionProfiles(definition);
|
|
175
|
+
if (profile.status !== "matched") return profile;
|
|
176
|
+
return { status: "missing", reason: "installation_provenance_missing" };
|
|
177
|
+
}
|
|
178
|
+
const profile = validateDistributionProfiles(definition);
|
|
179
|
+
if (profile.status !== "matched") return profile;
|
|
180
|
+
const marker = readJson(markerPath);
|
|
181
|
+
if (!marker
|
|
182
|
+
|| marker.schema_version !== 1
|
|
183
|
+
|| marker.owner !== "create-agdf"
|
|
184
|
+
|| marker.profile_id !== "runtime-plugin"
|
|
185
|
+
|| marker.marketplace_id !== profile.contract.marketplaceIdentities.durable
|
|
186
|
+
|| marker.canonical_version !== definition.version
|
|
187
|
+
|| marker.codex_install_version !== pluginVersion
|
|
188
|
+
|| marker.runtime_digest !== runtimeManifest?.digest
|
|
189
|
+
|| !/^[a-f0-9]{64}$/.test(marker.source_digest ?? "")) {
|
|
190
|
+
return { status: "invalid", reason: "installation_provenance_invalid", marker };
|
|
191
|
+
}
|
|
192
|
+
let observedSourceDigest;
|
|
193
|
+
try {
|
|
194
|
+
observedSourceDigest = digestNormalizedPluginSource(pluginRoot, definition.version);
|
|
195
|
+
} catch {
|
|
196
|
+
return { status: "invalid", reason: "source_digest_mismatch", marker };
|
|
197
|
+
}
|
|
198
|
+
if (observedSourceDigest !== marker.source_digest) {
|
|
199
|
+
return { status: "invalid", reason: "source_digest_mismatch", marker, observedSourceDigest };
|
|
200
|
+
}
|
|
201
|
+
return { status: "matched", marker, observedSourceDigest };
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export const distributionProfileContract = Object.freeze({
|
|
205
|
+
expectedProfiles: EXPECTED_PROFILES,
|
|
206
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-agdf",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.6",
|
|
4
4
|
"description": "Bootstrap AGDF repository-local plugin files, Copilot/OpenCode instructions, skills, control files and machine-readable checks.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -22,6 +22,9 @@
|
|
|
22
22
|
"scripts": {
|
|
23
23
|
"sync-package-assets": "node ./scripts/sync-package-assets.js",
|
|
24
24
|
"build:public-plugin": "node ./scripts/build-public-plugin.js",
|
|
25
|
+
"install:claude": "node ./scripts/install-local-plugin.js claude",
|
|
26
|
+
"install:codex": "node ./scripts/install-local-plugin.js codex",
|
|
27
|
+
"install:opencode": "node ./scripts/install-local-plugin.js opencode",
|
|
25
28
|
"test:release-version-coherence": "node ./scripts/release-version-coherence-test.js",
|
|
26
29
|
"test:public-plugin": "node ./scripts/public-plugin-test.js",
|
|
27
30
|
"release:prepare": "npm run sync-package-assets && npm run test:release-version-coherence && npm run test:public-plugin",
|
|
@@ -32,6 +35,7 @@
|
|
|
32
35
|
"test:cli-modularization": "node ./scripts/cli-modularization-test.js",
|
|
33
36
|
"test:local-validator": "node ./scripts/local-validator-test.js",
|
|
34
37
|
"test:local-marketplace": "node ./scripts/local-marketplace-test.js",
|
|
38
|
+
"test:local-development-install": "npm run release:prepare && node ./scripts/local-development-install-test.js",
|
|
35
39
|
"test:package-contents": "node ./scripts/package-contents-test.js",
|
|
36
40
|
"test:package-build": "node ./scripts/package-build-test.js",
|
|
37
41
|
"test:lifecycle": "node ./scripts/lifecycle-test.js",
|
|
@@ -50,7 +54,7 @@
|
|
|
50
54
|
"test:proportionality": "node ./scripts/proportionality-benchmark-test.js",
|
|
51
55
|
"test:release-bootstrap": "node ./scripts/release-bootstrap-smoke-test.js",
|
|
52
56
|
"test:routing": "npm run sync-package-assets && node ./scripts/test-routing.js",
|
|
53
|
-
"smoke-test": "npm run release:prepare && npm run test:cli-modularization && npm run test:local-validator && npm run test:local-marketplace && npm run test:package-build && npm run test:package-contents && npm run test:lifecycle && npm run test:control-state && npm run test:parent-reconciliation && npm run test:interaction-presentation && npm run test:verified-change && npm run test:runtime-integrity-layout && npm run test:runtime-integrity-negative && npm run test:agent-skills-conformance && npm run test:skill-evals && npm run eval:skills && npm run test:proportionality && npm run test:delivery-path-search && npm run test:delivery-path-search-unit && npm run test:delivery-path-search-generator && npm run test:opencode-hardening && node ./scripts/smoke-test.js && node ./scripts/test-routing.js",
|
|
57
|
+
"smoke-test": "npm run release:prepare && npm run test:cli-modularization && npm run test:local-validator && npm run test:local-marketplace && npm run test:local-development-install && npm run test:package-build && npm run test:package-contents && npm run test:lifecycle && npm run test:control-state && npm run test:parent-reconciliation && npm run test:interaction-presentation && npm run test:verified-change && npm run test:runtime-integrity-layout && npm run test:runtime-integrity-negative && npm run test:agent-skills-conformance && npm run test:skill-evals && npm run eval:skills && npm run test:proportionality && npm run test:delivery-path-search && npm run test:delivery-path-search-unit && npm run test:delivery-path-search-generator && npm run test:opencode-hardening && node ./scripts/smoke-test.js && node ./scripts/test-routing.js",
|
|
54
58
|
"prepack": "npm run release:prepare"
|
|
55
59
|
},
|
|
56
60
|
"keywords": [
|