create-agdf 0.13.5 → 0.13.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 +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 +253 -46
- package/lib/installers/opencode.js +17 -2
- package/lib/installers/plugin-installers.js +88 -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
|
@@ -1,48 +1,60 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
1
|
import {
|
|
3
2
|
cpSync,
|
|
4
3
|
existsSync,
|
|
5
4
|
mkdirSync,
|
|
6
5
|
readFileSync,
|
|
7
|
-
readdirSync,
|
|
8
|
-
renameSync,
|
|
9
6
|
rmSync,
|
|
10
|
-
statSync,
|
|
11
7
|
writeFileSync,
|
|
12
8
|
} from "node:fs";
|
|
13
9
|
import { homedir } from "node:os";
|
|
14
|
-
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
10
|
+
import { dirname, isAbsolute, join, posix, relative, resolve, sep, win32 } from "node:path";
|
|
15
11
|
import process from "node:process";
|
|
16
12
|
import { generatedRoot, packageRoot, pluginDefinition } from "../cli/runtime-context.js";
|
|
13
|
+
import { renameSyncWithRetry } from "../fs-swap.js";
|
|
14
|
+
import {
|
|
15
|
+
INSTALLATION_PROVENANCE_FILE,
|
|
16
|
+
LEGACY_LOCAL_INSTALL_FILE,
|
|
17
|
+
digestDirectory,
|
|
18
|
+
digestNormalizedPluginSource,
|
|
19
|
+
inspectInstallationProvenance,
|
|
20
|
+
validateDistributionProfiles,
|
|
21
|
+
} from "../runtime/plugin-provenance.js";
|
|
17
22
|
|
|
18
23
|
const MARKETPLACE_ID = "agdf";
|
|
19
24
|
const OWNERSHIP_FILE = ".agdf-owned.json";
|
|
20
25
|
const LEGACY_REPOSITORY = "arndtgold/ai-native-governance-delivery-framework";
|
|
26
|
+
export const CODEX_REGISTRATION_REVISION = 1;
|
|
21
27
|
|
|
22
28
|
function pathInside(root, candidate) {
|
|
23
29
|
const rel = relative(root, candidate);
|
|
24
30
|
return rel === "" || (rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute(rel));
|
|
25
31
|
}
|
|
26
32
|
|
|
27
|
-
function
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
45
|
-
|
|
33
|
+
export function digestPluginSource(root, canonicalVersion = pluginDefinition.version) {
|
|
34
|
+
return digestNormalizedPluginSource(root, canonicalVersion);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export { digestDirectory };
|
|
38
|
+
|
|
39
|
+
function semverBase(version) {
|
|
40
|
+
return version.split("+")[0];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function isSemanticVersion(version) {
|
|
44
|
+
return typeof version === "string"
|
|
45
|
+
&& /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/.test(version);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function codexLocalInstallVersion(version, sourceDigest) {
|
|
49
|
+
if (!/^[a-f0-9]{64}$/.test(sourceDigest)) throw new Error("AGDF local source digest must be a deterministic SHA-256 value.");
|
|
50
|
+
return `${semverBase(version)}+codex.local-${sourceDigest.slice(0, 12)}`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function isCodexLocalInstallVersion(version, candidate, sourceDigest = "") {
|
|
54
|
+
if (typeof candidate !== "string") return false;
|
|
55
|
+
if (sourceDigest) return candidate === codexLocalInstallVersion(version, sourceDigest);
|
|
56
|
+
const escaped = semverBase(version).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
57
|
+
return new RegExp(`^${escaped}\\+codex\\.local-[a-f0-9]{12}$`).test(candidate);
|
|
46
58
|
}
|
|
47
59
|
|
|
48
60
|
function readJson(path, label) {
|
|
@@ -53,15 +65,20 @@ function readJson(path, label) {
|
|
|
53
65
|
}
|
|
54
66
|
}
|
|
55
67
|
|
|
68
|
+
function pathForPlatform(platform = process.platform) {
|
|
69
|
+
return platform === "win32" ? win32 : posix;
|
|
70
|
+
}
|
|
71
|
+
|
|
56
72
|
export function defaultAgdfDataRoot({ env = process.env, platform = process.platform, home = homedir() } = {}) {
|
|
57
|
-
|
|
58
|
-
if (
|
|
59
|
-
if (platform === "
|
|
60
|
-
return join(env.
|
|
73
|
+
const targetPath = pathForPlatform(platform);
|
|
74
|
+
if (env.AGDF_DATA_DIR) return targetPath.resolve(env.AGDF_DATA_DIR);
|
|
75
|
+
if (platform === "darwin") return targetPath.join(home, "Library", "Application Support", "agdf");
|
|
76
|
+
if (platform === "win32") return targetPath.join(env.LOCALAPPDATA || env.APPDATA || targetPath.join(home, "AppData", "Local"), "agdf");
|
|
77
|
+
return targetPath.join(env.XDG_DATA_HOME || targetPath.join(home, ".local", "share"), "agdf");
|
|
61
78
|
}
|
|
62
79
|
|
|
63
80
|
export function localMarketplaceRoot(options = {}) {
|
|
64
|
-
return join(defaultAgdfDataRoot(options), "marketplaces", MARKETPLACE_ID);
|
|
81
|
+
return pathForPlatform(options.platform).join(defaultAgdfDataRoot(options), "marketplaces", MARKETPLACE_ID);
|
|
65
82
|
}
|
|
66
83
|
|
|
67
84
|
function ownership(root, { allowBuilding = false } = {}) {
|
|
@@ -93,7 +110,7 @@ function writeJson(path, value) {
|
|
|
93
110
|
function codexMarketplace(definition = pluginDefinition) {
|
|
94
111
|
return {
|
|
95
112
|
name: MARKETPLACE_ID,
|
|
96
|
-
interface: { displayName: definition.
|
|
113
|
+
interface: { displayName: definition.publicDistribution.publicDisplayName },
|
|
97
114
|
plugins: [{
|
|
98
115
|
name: definition.id,
|
|
99
116
|
source: { source: "local", path: "./plugins/agdf" },
|
|
@@ -103,6 +120,13 @@ function codexMarketplace(definition = pluginDefinition) {
|
|
|
103
120
|
};
|
|
104
121
|
}
|
|
105
122
|
|
|
123
|
+
function legacyCodexMarketplace(definition = pluginDefinition) {
|
|
124
|
+
return {
|
|
125
|
+
...codexMarketplace(definition),
|
|
126
|
+
interface: { displayName: definition.displayName },
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
106
130
|
function claudeMarketplace(definition = pluginDefinition) {
|
|
107
131
|
return {
|
|
108
132
|
name: MARKETPLACE_ID,
|
|
@@ -117,9 +141,15 @@ function claudeMarketplace(definition = pluginDefinition) {
|
|
|
117
141
|
};
|
|
118
142
|
}
|
|
119
143
|
|
|
120
|
-
function validateBuiltPlugin(pluginRoot, expectedVersion
|
|
144
|
+
function validateBuiltPlugin(pluginRoot, expectedVersion, expectedCodexInstallVersion = expectedVersion, sourceDigest = "", {
|
|
145
|
+
requireInstallationProvenance = false,
|
|
146
|
+
allowLegacyProvenanceForMigration = false,
|
|
147
|
+
allowPreProvenanceShape = false,
|
|
148
|
+
} = {}) {
|
|
121
149
|
const definition = readJson(join(pluginRoot, "meta", "agdf-plugin.definition.json"), "built plugin definition");
|
|
122
150
|
const runtime = readJson(join(pluginRoot, "runtime", "runtime-manifest.json"), "built runtime manifest");
|
|
151
|
+
const codex = readJson(join(pluginRoot, ".codex-plugin", "plugin.json"), "built Codex plugin manifest");
|
|
152
|
+
const claude = readJson(join(pluginRoot, ".claude-plugin", "plugin.json"), "built Claude plugin manifest");
|
|
123
153
|
for (const required of [
|
|
124
154
|
join(pluginRoot, ".codex-plugin", "plugin.json"),
|
|
125
155
|
join(pluginRoot, ".claude-plugin", "plugin.json"),
|
|
@@ -131,22 +161,112 @@ function validateBuiltPlugin(pluginRoot, expectedVersion) {
|
|
|
131
161
|
if (definition.version !== expectedVersion || runtime.version !== expectedVersion) {
|
|
132
162
|
throw new Error(`Built plugin version mismatch: expected ${expectedVersion}, definition ${definition.version}, runtime ${runtime.version}`);
|
|
133
163
|
}
|
|
164
|
+
const profile = validateDistributionProfiles(definition);
|
|
165
|
+
const isPreProvenanceShape = allowPreProvenanceShape
|
|
166
|
+
&& !Object.hasOwn(definition, "distributionProfiles");
|
|
167
|
+
let provenance = null;
|
|
168
|
+
if (profile.status !== "matched"
|
|
169
|
+
&& requireInstallationProvenance
|
|
170
|
+
&& allowLegacyProvenanceForMigration) {
|
|
171
|
+
provenance = inspectInstallationProvenance(pluginRoot, {
|
|
172
|
+
definition,
|
|
173
|
+
runtimeManifest: runtime,
|
|
174
|
+
pluginVersion: expectedCodexInstallVersion,
|
|
175
|
+
allowLegacy: true,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
if (profile.status !== "matched" && provenance?.status !== "legacy" && !isPreProvenanceShape) {
|
|
179
|
+
throw new Error("Built plugin distribution profile contract is invalid.");
|
|
180
|
+
}
|
|
181
|
+
if (codex.version !== expectedCodexInstallVersion) {
|
|
182
|
+
throw new Error(`Built Codex plugin version mismatch: expected ${expectedCodexInstallVersion}, observed ${codex.version}`);
|
|
183
|
+
}
|
|
184
|
+
if (claude.version !== expectedVersion) {
|
|
185
|
+
throw new Error(`Built Claude plugin version mismatch: expected ${expectedVersion}, observed ${claude.version}`);
|
|
186
|
+
}
|
|
187
|
+
if (expectedCodexInstallVersion !== expectedVersion) {
|
|
188
|
+
if (!isCodexLocalInstallVersion(expectedVersion, expectedCodexInstallVersion, sourceDigest)) {
|
|
189
|
+
throw new Error(`Invalid AGDF Codex local install version: ${expectedCodexInstallVersion}`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
134
192
|
if (!/^[a-f0-9]{64}$/.test(runtime.digest ?? "")) throw new Error("Built plugin runtime digest is invalid.");
|
|
135
193
|
if (digestDirectory(join(pluginRoot, "runtime", "create-agdf")) !== runtime.digest) {
|
|
136
194
|
throw new Error("Built plugin runtime digest does not match its payload.");
|
|
137
195
|
}
|
|
196
|
+
if (requireInstallationProvenance) {
|
|
197
|
+
provenance ??= inspectInstallationProvenance(pluginRoot, {
|
|
198
|
+
definition,
|
|
199
|
+
runtimeManifest: runtime,
|
|
200
|
+
pluginVersion: expectedCodexInstallVersion,
|
|
201
|
+
allowLegacy: allowLegacyProvenanceForMigration,
|
|
202
|
+
});
|
|
203
|
+
if (provenance.status !== "matched"
|
|
204
|
+
&& !(allowLegacyProvenanceForMigration && provenance.status === "legacy")) {
|
|
205
|
+
throw new Error(`Invalid AGDF installation provenance (${provenance.reason}): ${pluginRoot}`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
138
208
|
return definition;
|
|
139
209
|
}
|
|
140
210
|
|
|
141
211
|
function validateMarketplaceRoot(root, definition = pluginDefinition) {
|
|
142
212
|
const codex = readJson(join(root, ".agents", "plugins", "marketplace.json"), "Codex local marketplace manifest");
|
|
143
213
|
const claude = readJson(join(root, ".claude-plugin", "marketplace.json"), "Claude local marketplace manifest");
|
|
144
|
-
|
|
214
|
+
const serializedCodex = JSON.stringify(codex);
|
|
215
|
+
const codexShape = serializedCodex === JSON.stringify(codexMarketplace(definition))
|
|
216
|
+
? "current"
|
|
217
|
+
: serializedCodex === JSON.stringify(legacyCodexMarketplace(definition))
|
|
218
|
+
? "legacy_full_product_label"
|
|
219
|
+
: "invalid";
|
|
220
|
+
if (codexShape === "invalid") {
|
|
145
221
|
throw new Error(`Codex local marketplace manifest is not owned by ${MARKETPLACE_ID}: ${root}`);
|
|
146
222
|
}
|
|
147
223
|
if (JSON.stringify(claude) !== JSON.stringify(claudeMarketplace(definition))) {
|
|
148
224
|
throw new Error(`Claude local marketplace manifest is not owned by ${MARKETPLACE_ID}: ${root}`);
|
|
149
225
|
}
|
|
226
|
+
return Object.freeze({ codexShape });
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function classifyExistingMarketplace(stableRoot, marker) {
|
|
230
|
+
const existingPluginRoot = join(stableRoot, "plugins", MARKETPLACE_ID);
|
|
231
|
+
const existingCodexInstallVersion = marker.codex_install_version ?? marker.version;
|
|
232
|
+
if (!isSemanticVersion(marker.version)
|
|
233
|
+
|| !/^[a-f0-9]{64}$/.test(marker.plugin_digest ?? "")) {
|
|
234
|
+
throw new Error(`Refusing invalid AGDF marketplace ownership evidence: ${stableRoot}`);
|
|
235
|
+
}
|
|
236
|
+
const definition = readJson(
|
|
237
|
+
join(existingPluginRoot, "meta", "agdf-plugin.definition.json"),
|
|
238
|
+
"built plugin definition",
|
|
239
|
+
);
|
|
240
|
+
const hasCurrentProvenance = existsSync(join(existingPluginRoot, INSTALLATION_PROVENANCE_FILE));
|
|
241
|
+
const hasLegacyProvenance = existsSync(join(existingPluginRoot, LEGACY_LOCAL_INSTALL_FILE));
|
|
242
|
+
const preProvenanceCandidate = !hasCurrentProvenance
|
|
243
|
+
&& !hasLegacyProvenance
|
|
244
|
+
&& !Object.hasOwn(definition, "distributionProfiles");
|
|
245
|
+
const existingDefinition = validateBuiltPlugin(
|
|
246
|
+
existingPluginRoot,
|
|
247
|
+
marker.version,
|
|
248
|
+
existingCodexInstallVersion,
|
|
249
|
+
marker.source_digest ?? "",
|
|
250
|
+
preProvenanceCandidate
|
|
251
|
+
? { allowPreProvenanceShape: true }
|
|
252
|
+
: { requireInstallationProvenance: true, allowLegacyProvenanceForMigration: true },
|
|
253
|
+
);
|
|
254
|
+
const marketplace = validateMarketplaceRoot(stableRoot, existingDefinition);
|
|
255
|
+
if (digestDirectory(existingPluginRoot) !== marker.plugin_digest) {
|
|
256
|
+
throw new Error(`Refusing tampered AGDF marketplace root: ${stableRoot}`);
|
|
257
|
+
}
|
|
258
|
+
return Object.freeze({
|
|
259
|
+
classification: preProvenanceCandidate
|
|
260
|
+
? "owned_pre_provenance_rebuild"
|
|
261
|
+
: "current_or_marker_migration",
|
|
262
|
+
definition: existingDefinition,
|
|
263
|
+
marketplace,
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function invalidExistingMarketplace(error) {
|
|
268
|
+
error.existingClassification = "invalid_or_unowned";
|
|
269
|
+
return error;
|
|
150
270
|
}
|
|
151
271
|
|
|
152
272
|
function recoverInterruptedTransaction(stableRoot, stageRoot, backupRoot, failedRoot) {
|
|
@@ -156,9 +276,9 @@ function recoverInterruptedTransaction(stableRoot, stageRoot, backupRoot, failed
|
|
|
156
276
|
if (existsSync(failedRoot)) removeOwnedRoot(failedRoot, parent);
|
|
157
277
|
if (existsSync(stableRoot)) {
|
|
158
278
|
ownership(stableRoot);
|
|
159
|
-
|
|
279
|
+
renameSyncWithRetry(stableRoot, failedRoot);
|
|
160
280
|
}
|
|
161
|
-
|
|
281
|
+
renameSyncWithRetry(backupRoot, stableRoot);
|
|
162
282
|
if (existsSync(failedRoot)) removeOwnedRoot(failedRoot, parent);
|
|
163
283
|
}
|
|
164
284
|
if (existsSync(stageRoot)) removeOwnedRoot(stageRoot, parent, { allowBuilding: true });
|
|
@@ -169,6 +289,8 @@ export function prepareLocalMarketplace({
|
|
|
169
289
|
dataRoot = defaultAgdfDataRoot(),
|
|
170
290
|
builtPluginRoot = join(generatedRoot, "plugins", "agdf"),
|
|
171
291
|
expectedVersion = pluginDefinition.version,
|
|
292
|
+
codexInstallVersion = expectedVersion,
|
|
293
|
+
codexRegistrationRevision = null,
|
|
172
294
|
} = {}) {
|
|
173
295
|
dataRoot = resolve(dataRoot);
|
|
174
296
|
builtPluginRoot = resolve(builtPluginRoot);
|
|
@@ -178,18 +300,37 @@ export function prepareLocalMarketplace({
|
|
|
178
300
|
const backupRoot = `${stableRoot}.backup`;
|
|
179
301
|
const failedRoot = `${stableRoot}.failed`;
|
|
180
302
|
if (!pathInside(dataRoot, stableRoot) || dirname(stableRoot) !== parent) throw new Error(`Unsafe AGDF marketplace root: ${stableRoot}`);
|
|
303
|
+
const sourceDigest = digestPluginSource(builtPluginRoot, expectedVersion);
|
|
304
|
+
if (codexInstallVersion !== expectedVersion
|
|
305
|
+
&& !isCodexLocalInstallVersion(expectedVersion, codexInstallVersion, sourceDigest)) {
|
|
306
|
+
throw new Error(`Invalid AGDF Codex local install version: ${codexInstallVersion}`);
|
|
307
|
+
}
|
|
181
308
|
const targetDefinition = validateBuiltPlugin(builtPluginRoot, expectedVersion);
|
|
182
309
|
mkdirSync(parent, { recursive: true });
|
|
183
310
|
recoverInterruptedTransaction(stableRoot, stageRoot, backupRoot, failedRoot);
|
|
184
311
|
|
|
185
|
-
|
|
312
|
+
let existing = null;
|
|
313
|
+
if (existsSync(stableRoot)) {
|
|
314
|
+
try {
|
|
315
|
+
existing = ownership(stableRoot);
|
|
316
|
+
} catch (error) {
|
|
317
|
+
throw invalidExistingMarketplace(error);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
const targetCodexRegistrationRevision = codexRegistrationRevision
|
|
321
|
+
?? existing?.codex_registration_revision
|
|
322
|
+
?? 0;
|
|
323
|
+
let existingMarketplace = null;
|
|
324
|
+
let existingClassification = "none";
|
|
186
325
|
if (existing) {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
throw
|
|
326
|
+
let classified;
|
|
327
|
+
try {
|
|
328
|
+
classified = classifyExistingMarketplace(stableRoot, existing);
|
|
329
|
+
} catch (error) {
|
|
330
|
+
throw invalidExistingMarketplace(error);
|
|
192
331
|
}
|
|
332
|
+
existingClassification = classified.classification;
|
|
333
|
+
existingMarketplace = classified.marketplace;
|
|
193
334
|
}
|
|
194
335
|
mkdirSync(stageRoot, { recursive: false });
|
|
195
336
|
try {
|
|
@@ -197,7 +338,10 @@ export function prepareLocalMarketplace({
|
|
|
197
338
|
schema_version: 1,
|
|
198
339
|
owner: "create-agdf",
|
|
199
340
|
marketplace_id: MARKETPLACE_ID,
|
|
341
|
+
codex_registration_revision: targetCodexRegistrationRevision,
|
|
200
342
|
version: expectedVersion,
|
|
343
|
+
codex_install_version: codexInstallVersion,
|
|
344
|
+
source_digest: sourceDigest,
|
|
201
345
|
plugin_digest: null,
|
|
202
346
|
source_package_version: readJson(join(packageRoot, "package.json"), "create-agdf package manifest").version,
|
|
203
347
|
staging_state: "building",
|
|
@@ -205,6 +349,25 @@ export function prepareLocalMarketplace({
|
|
|
205
349
|
const stagedPluginRoot = join(stageRoot, "plugins", MARKETPLACE_ID);
|
|
206
350
|
cpSync(builtPluginRoot, stagedPluginRoot, { recursive: true });
|
|
207
351
|
validateBuiltPlugin(stagedPluginRoot, expectedVersion);
|
|
352
|
+
if (codexInstallVersion !== expectedVersion) {
|
|
353
|
+
const codexManifestPath = join(stagedPluginRoot, ".codex-plugin", "plugin.json");
|
|
354
|
+
writeJson(codexManifestPath, { ...readJson(codexManifestPath, "built Codex plugin manifest"), version: codexInstallVersion });
|
|
355
|
+
}
|
|
356
|
+
const runtimeManifest = readJson(join(stagedPluginRoot, "runtime", "runtime-manifest.json"), "built runtime manifest");
|
|
357
|
+
writeJson(join(stagedPluginRoot, INSTALLATION_PROVENANCE_FILE), {
|
|
358
|
+
schema_version: 1,
|
|
359
|
+
owner: "create-agdf",
|
|
360
|
+
profile_id: "runtime-plugin",
|
|
361
|
+
marketplace_id: MARKETPLACE_ID,
|
|
362
|
+
canonical_version: expectedVersion,
|
|
363
|
+
codex_install_version: codexInstallVersion,
|
|
364
|
+
source_digest: sourceDigest,
|
|
365
|
+
runtime_digest: runtimeManifest.digest,
|
|
366
|
+
});
|
|
367
|
+
if (existsSync(join(stagedPluginRoot, LEGACY_LOCAL_INSTALL_FILE))) {
|
|
368
|
+
rmSync(join(stagedPluginRoot, LEGACY_LOCAL_INSTALL_FILE));
|
|
369
|
+
}
|
|
370
|
+
validateBuiltPlugin(stagedPluginRoot, expectedVersion, codexInstallVersion, sourceDigest, { requireInstallationProvenance: true });
|
|
208
371
|
writeJson(join(stageRoot, ".agents", "plugins", "marketplace.json"), codexMarketplace(targetDefinition));
|
|
209
372
|
writeJson(join(stageRoot, ".claude-plugin", "marketplace.json"), claudeMarketplace(targetDefinition));
|
|
210
373
|
validateMarketplaceRoot(stageRoot, targetDefinition);
|
|
@@ -213,31 +376,40 @@ export function prepareLocalMarketplace({
|
|
|
213
376
|
schema_version: 1,
|
|
214
377
|
owner: "create-agdf",
|
|
215
378
|
marketplace_id: MARKETPLACE_ID,
|
|
379
|
+
codex_registration_revision: targetCodexRegistrationRevision,
|
|
216
380
|
version: expectedVersion,
|
|
381
|
+
codex_install_version: codexInstallVersion,
|
|
382
|
+
source_digest: sourceDigest,
|
|
217
383
|
plugin_digest: pluginDigest,
|
|
218
384
|
source_package_version: readJson(join(packageRoot, "package.json"), "create-agdf package manifest").version,
|
|
219
385
|
staging_state: "ready",
|
|
220
386
|
};
|
|
221
387
|
writeJson(join(stageRoot, OWNERSHIP_FILE), marker);
|
|
222
388
|
|
|
223
|
-
if (existing?.version === expectedVersion
|
|
389
|
+
if (existing?.version === expectedVersion
|
|
390
|
+
&& existing?.plugin_digest === pluginDigest
|
|
391
|
+
&& existing?.codex_registration_revision === targetCodexRegistrationRevision
|
|
392
|
+
&& existingMarketplace?.codexShape === "current") {
|
|
224
393
|
removeOwnedRoot(stageRoot, parent);
|
|
225
394
|
return Object.freeze({
|
|
226
395
|
root: stableRoot,
|
|
227
396
|
pluginRoot: join(stableRoot, "plugins", MARKETPLACE_ID),
|
|
228
397
|
version: expectedVersion,
|
|
398
|
+
codexInstallVersion,
|
|
399
|
+
sourceDigest,
|
|
229
400
|
digest: pluginDigest,
|
|
401
|
+
existingClassification,
|
|
230
402
|
changed: false,
|
|
231
403
|
commit() {},
|
|
232
404
|
rollback() {},
|
|
233
405
|
});
|
|
234
406
|
}
|
|
235
407
|
|
|
236
|
-
if (existsSync(stableRoot))
|
|
408
|
+
if (existsSync(stableRoot)) renameSyncWithRetry(stableRoot, backupRoot);
|
|
237
409
|
try {
|
|
238
|
-
|
|
410
|
+
renameSyncWithRetry(stageRoot, stableRoot);
|
|
239
411
|
} catch (error) {
|
|
240
|
-
if (existsSync(backupRoot) && !existsSync(stableRoot))
|
|
412
|
+
if (existsSync(backupRoot) && !existsSync(stableRoot)) renameSyncWithRetry(backupRoot, stableRoot);
|
|
241
413
|
throw error;
|
|
242
414
|
}
|
|
243
415
|
let closed = false;
|
|
@@ -245,7 +417,10 @@ export function prepareLocalMarketplace({
|
|
|
245
417
|
root: stableRoot,
|
|
246
418
|
pluginRoot: join(stableRoot, "plugins", MARKETPLACE_ID),
|
|
247
419
|
version: expectedVersion,
|
|
420
|
+
codexInstallVersion,
|
|
421
|
+
sourceDigest,
|
|
248
422
|
digest: pluginDigest,
|
|
423
|
+
existingClassification,
|
|
249
424
|
changed: true,
|
|
250
425
|
commit() {
|
|
251
426
|
if (closed) return;
|
|
@@ -256,8 +431,8 @@ export function prepareLocalMarketplace({
|
|
|
256
431
|
if (closed) return;
|
|
257
432
|
if (existsSync(backupRoot)) {
|
|
258
433
|
if (existsSync(failedRoot)) removeOwnedRoot(failedRoot, parent);
|
|
259
|
-
if (existsSync(stableRoot))
|
|
260
|
-
|
|
434
|
+
if (existsSync(stableRoot)) renameSyncWithRetry(stableRoot, failedRoot);
|
|
435
|
+
renameSyncWithRetry(backupRoot, stableRoot);
|
|
261
436
|
if (existsSync(failedRoot)) removeOwnedRoot(failedRoot, parent);
|
|
262
437
|
} else if (existsSync(stableRoot)) {
|
|
263
438
|
removeOwnedRoot(stableRoot, parent);
|
|
@@ -271,6 +446,36 @@ export function prepareLocalMarketplace({
|
|
|
271
446
|
}
|
|
272
447
|
}
|
|
273
448
|
|
|
449
|
+
export function inspectLocalMarketplaceProjection({
|
|
450
|
+
dataRoot = defaultAgdfDataRoot(),
|
|
451
|
+
expectedVersion = pluginDefinition.version,
|
|
452
|
+
} = {}) {
|
|
453
|
+
const root = join(resolve(dataRoot), "marketplaces", MARKETPLACE_ID);
|
|
454
|
+
if (!existsSync(root)) return null;
|
|
455
|
+
const marker = ownership(root);
|
|
456
|
+
const codexInstallVersion = marker.codex_install_version ?? marker.version;
|
|
457
|
+
const pluginRoot = join(root, "plugins", MARKETPLACE_ID);
|
|
458
|
+
const definition = validateBuiltPlugin(
|
|
459
|
+
pluginRoot,
|
|
460
|
+
marker.version,
|
|
461
|
+
codexInstallVersion,
|
|
462
|
+
marker.source_digest ?? "",
|
|
463
|
+
{ requireInstallationProvenance: true },
|
|
464
|
+
);
|
|
465
|
+
validateMarketplaceRoot(root, definition);
|
|
466
|
+
if (marker.version !== expectedVersion || digestDirectory(pluginRoot) !== marker.plugin_digest) {
|
|
467
|
+
throw new Error(`Refusing stale or tampered AGDF marketplace projection: ${root}`);
|
|
468
|
+
}
|
|
469
|
+
return Object.freeze({
|
|
470
|
+
root,
|
|
471
|
+
pluginRoot,
|
|
472
|
+
version: marker.version,
|
|
473
|
+
codexInstallVersion,
|
|
474
|
+
sourceDigest: marker.source_digest ?? "",
|
|
475
|
+
digest: marker.plugin_digest,
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
|
|
274
479
|
function normalizedLegacySource(value) {
|
|
275
480
|
if (typeof value !== "string") return "";
|
|
276
481
|
return value.trim().toLowerCase()
|
|
@@ -319,5 +524,7 @@ export function classifyMarketplaceList(surface, output, stableRoot) {
|
|
|
319
524
|
export const localMarketplaceConstants = Object.freeze({
|
|
320
525
|
id: MARKETPLACE_ID,
|
|
321
526
|
ownershipFile: OWNERSHIP_FILE,
|
|
527
|
+
installationProvenanceFile: INSTALLATION_PROVENANCE_FILE,
|
|
528
|
+
legacyLocalInstallFile: LEGACY_LOCAL_INSTALL_FILE,
|
|
322
529
|
legacyRepository: LEGACY_REPOSITORY,
|
|
323
530
|
});
|
|
@@ -6,6 +6,7 @@ import process from "node:process";
|
|
|
6
6
|
import { generatedRoot, pluginDefinition } from "../cli/runtime-context.js";
|
|
7
7
|
import { evaluateOpenCodeRepositoryActivation } from "./opencode-activation.js";
|
|
8
8
|
import { resolveLocalValidator } from "../runtime/local-validator.js";
|
|
9
|
+
import { validateLocalOpenCodePackageSource } from "./local-development.js";
|
|
9
10
|
|
|
10
11
|
const contractModules = ["gate-transition.md", "interaction.md", "modes.md", "quality.md", "context-graph.md", "control-scaffold.md", "closeout.md"];
|
|
11
12
|
const openCodeSkillNames = pluginDefinition.skillSet.map((skill) => pluginDefinition.opencode.skillPrefix + skill.slug);
|
|
@@ -36,7 +37,15 @@ export function defaultOpenCodeConfigDir() {
|
|
|
36
37
|
return process.env.OPENCODE_CONFIG_DIR || join(homedir(), ".config", "opencode");
|
|
37
38
|
}
|
|
38
39
|
|
|
39
|
-
export function
|
|
40
|
+
export function resolveOpenCodeInstallPackageSource(packageSource) {
|
|
41
|
+
const local = packageSource ? validateLocalOpenCodePackageSource(packageSource) : null;
|
|
42
|
+
return Object.freeze({
|
|
43
|
+
local,
|
|
44
|
+
specifier: local?.specifier ?? `${pluginDefinition.opencode.npmPackage}@${pluginDefinition.version}`,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function installOpenCodeGlobalPlugin(configDir, dependencies = {}) {
|
|
40
49
|
try {
|
|
41
50
|
assertGlobalOpenCodeSurfaceWritable(configDir);
|
|
42
51
|
} catch (error) {
|
|
@@ -97,8 +106,11 @@ export function installOpenCodeGlobalPlugin(configDir) {
|
|
|
97
106
|
}
|
|
98
107
|
|
|
99
108
|
mkdirSync(configDir, { recursive: true });
|
|
109
|
+
let localPackageSource = null;
|
|
100
110
|
try {
|
|
101
|
-
const
|
|
111
|
+
const packageSource = resolveOpenCodeInstallPackageSource(dependencies.packageSource);
|
|
112
|
+
localPackageSource = packageSource.local;
|
|
113
|
+
const packageSpecifier = packageSource.specifier;
|
|
102
114
|
const invocation = openCodeNpmInvocation(["install", "--silent", "--save-prod", "--save-exact", packageSpecifier]);
|
|
103
115
|
execFileSync(invocation.executable, invocation.args, {
|
|
104
116
|
cwd: configDir,
|
|
@@ -123,6 +135,9 @@ export function installOpenCodeGlobalPlugin(configDir) {
|
|
|
123
135
|
added: !alreadyInstalled,
|
|
124
136
|
transition: openCodePackageTransition(previousPackage, installedPackage),
|
|
125
137
|
sdk_alignment: sdkAlignment,
|
|
138
|
+
package_source: localPackageSource
|
|
139
|
+
? { kind: "local_checkout", path: localPackageSource.tarball, digest: localPackageSource.digest, version: localPackageSource.version }
|
|
140
|
+
: { kind: "registry", path: "", digest: "", version: pluginDefinition.version },
|
|
126
141
|
};
|
|
127
142
|
}
|
|
128
143
|
|