create-agdf 0.20.0 → 0.20.1

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.
Files changed (39) hide show
  1. package/README.md +32 -18
  2. package/generated/plugins/agdf/.claude-plugin/plugin.json +1 -1
  3. package/generated/plugins/agdf/.codex-plugin/plugin.json +1 -1
  4. package/generated/plugins/agdf/mcp/server/package.json +2 -2
  5. package/generated/plugins/agdf/meta/agdf-mcp-capability.json +1 -1
  6. package/generated/plugins/agdf/meta/agdf-plugin.definition.json +102 -11
  7. package/generated/plugins/agdf/meta/copilot-payload-baseline.json +2 -2
  8. package/generated/plugins/agdf/meta/distribution-profile-history.json +7 -0
  9. package/generated/plugins/agdf/runtime/create-agdf/generated/plugins/agdf/meta/agdf-plugin.definition.json +102 -11
  10. package/generated/plugins/agdf/runtime/create-agdf/package.json +1 -1
  11. package/generated/plugins/agdf/runtime/runtime-manifest.json +2 -2
  12. package/generated/plugins/agdf/submission/openai/availability.md +1 -1
  13. package/generated/plugins/agdf/submission/openai/capability-matrix.json +1 -1
  14. package/generated/plugins/agdf/submission/openai/release-notes.md +1 -1
  15. package/generated/plugins/agdf/submission/openai/reviewer-cases.json +1 -1
  16. package/generated/plugins/copilot/agdf/.agdf-payload-inventory.json +17 -17
  17. package/generated/plugins/copilot/agdf/mcp/server/package.json +2 -2
  18. package/generated/plugins/copilot/agdf/meta/agdf-plugin.definition.json +102 -11
  19. package/generated/plugins/copilot/agdf/meta/distribution-profile-history.json +7 -0
  20. package/generated/plugins/copilot/agdf/plugin.json +1 -1
  21. package/generated/plugins/copilot/agdf/runtime/create-agdf/generated/plugins/agdf/meta/agdf-plugin.definition.json +102 -11
  22. package/generated/plugins/copilot/agdf/runtime/create-agdf/package.json +1 -1
  23. package/generated/plugins/copilot/agdf/runtime/runtime-manifest.json +2 -2
  24. package/generated/submissions/openai/agdf/.codex-plugin/plugin.json +1 -1
  25. package/generated/submissions/openai/agdf/meta/agdf-plugin.definition.json +102 -11
  26. package/generated/submissions/openai/agdf/meta/copilot-payload-baseline.json +2 -2
  27. package/generated/submissions/openai/agdf/meta/distribution-profile-history.json +7 -0
  28. package/generated/submissions/openai/agdf/plugin.json +1 -1
  29. package/generated/submissions/openai/agdf/submission/openai/availability.md +1 -1
  30. package/generated/submissions/openai/agdf/submission/openai/capability-matrix.json +1 -1
  31. package/generated/submissions/openai/agdf/submission/openai/inventory.json +12 -12
  32. package/generated/submissions/openai/agdf/submission/openai/listing.json +1 -1
  33. package/generated/submissions/openai/agdf/submission/openai/readiness.json +2 -2
  34. package/generated/submissions/openai/agdf/submission/openai/readiness.md +2 -2
  35. package/generated/submissions/openai/agdf/submission/openai/release-notes.md +1 -1
  36. package/generated/submissions/openai/agdf/submission/openai/reviewer-cases.json +1 -1
  37. package/lib/host-adapters/codex/config-source.js +48 -0
  38. package/lib/mcp-lifecycle/adapters/codex.js +40 -29
  39. package/package.json +1 -1
@@ -0,0 +1,48 @@
1
+ // A bounded lexical view for the adapter's deliberately narrow TOML readers.
2
+ // Keep UTF-16 offsets and line endings so edits and hashes always use original
3
+ // bytes. Comments and multiline string payloads cannot supply tables, enabled
4
+ // fields or ownership markers. This is not a general TOML parser: unsupported
5
+ // MCP shapes still fail the existing structural/ownership checks.
6
+ export function codexConfigSyntax(content) {
7
+ if (Buffer.byteLength(content) > 65_536) throw Error('AGDF_MCP_CODEX_CONFIG_INVALID');
8
+ const syntax = content.split('');
9
+ const hide = (start, end) => {
10
+ for (let i = start; i < end; i++) if (content[i] !== '\n' && content[i] !== '\r') syntax[i] = ' ';
11
+ };
12
+ for (let i = 0; i < content.length;) {
13
+ if (content[i] === '#') {
14
+ const end = content.indexOf('\n', i);
15
+ hide(i, end < 0 ? content.length : end);
16
+ i = end < 0 ? content.length : end;
17
+ continue;
18
+ }
19
+ const quote = content[i];
20
+ if (quote !== '"' && quote !== "'") { i++; continue; }
21
+ const start = i, multiline = content.startsWith(quote.repeat(3), i);
22
+ i += multiline ? 3 : 1;
23
+ let closed = false;
24
+ while (i < content.length) {
25
+ if (quote === '"' && content[i] === '\\') {
26
+ if (!multiline && /[\r\n]/u.test(content[i + 1] ?? '')) throw Error('AGDF_MCP_CODEX_CONFIG_INVALID');
27
+ i += 2; continue;
28
+ }
29
+ if (!multiline && /[\r\n]/u.test(content[i])) throw Error('AGDF_MCP_CODEX_CONFIG_INVALID');
30
+ if (content[i] !== quote) { i++; continue; }
31
+ if (!multiline) { i++; closed = true; break; }
32
+ let end = i;
33
+ while (content[end] === quote) end++;
34
+ const count = end - i;
35
+ if (count > 5) throw Error('AGDF_MCP_CODEX_CONFIG_INVALID');
36
+ i = end;
37
+ if (count >= 3) { closed = true; break; }
38
+ }
39
+ if (!closed) throw Error('AGDF_MCP_CODEX_CONFIG_INVALID');
40
+ if (multiline) {
41
+ hide(start, i);
42
+ // Keep a non-boolean placeholder so a multiline enabled value is unknown,
43
+ // rather than mistaken for a missing field with its default value.
44
+ syntax[start] = quote; syntax[start + 1] = quote;
45
+ }
46
+ }
47
+ return syntax.join('');
48
+ }
@@ -6,6 +6,7 @@ import { inspectMcpServerPackage, inspectMcpExecutableRoot } from '../package.js
6
6
  import { homedir } from "node:os";
7
7
  import { dirname, basename, join, isAbsolute, resolve, relative, sep } from "node:path";
8
8
  import { fileURLToPath } from 'node:url';
9
+ import { codexConfigSyntax } from '../../host-adapters/codex/config-source.js';
9
10
  import {
10
11
  allSourceResult,
11
12
  atomicWrite,
@@ -49,13 +50,13 @@ function section(spec, { createdConfig = false, createdDirectory = false } = {})
49
50
  }
50
51
 
51
52
  function sectionRange(content, id = 'agdf') {
52
- // This bounded owner edits generated single-line sections only. Headers inside
53
- // TOML multiline strings cannot establish ownership; preserve unsupported sources.
54
- if (/'''|"""/u.test(content)) throw Error('AGDF_MCP_CODEX_CONFIG_INVALID');
53
+ // Ownership remains limited to generated single-line sections. Ignore table
54
+ // examples inside unrelated TOML strings without rewriting those strings.
55
+ const syntax = codexConfigSyntax(content);
55
56
  const escaped = id.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
56
57
  const exactHeader = `[mcp_servers.${id}]`;
57
- const headers = [...content.matchAll(new RegExp('^[ \\t]*\\[mcp_servers\\.' + escaped + '\\][ \\t]*$', 'gm'))];
58
- const unsupported = content.split("\n").some((line) => {
58
+ const headers = [...syntax.matchAll(new RegExp('^[ \\t]*\\[mcp_servers\\.' + escaped + '\\][ \\t]*\\r?$', 'gm'))];
59
+ const unsupported = syntax.split("\n").some((line) => {
59
60
  const trimmed = line.trim();
60
61
  if (!trimmed || trimmed.startsWith("#")) return false;
61
62
  if (trimmed.startsWith("[")) {
@@ -71,12 +72,12 @@ function sectionRange(content, id = 'agdf') {
71
72
  const [header] = headers;
72
73
  if (!header) return null;
73
74
  const sectionStart = header.index;
74
- const next = /^[ \t]*\[[^\n]+\][ \t]*$/gm;
75
+ const next = /^[ \t]*\[[^\r\n]+\][ \t]*\r?$/gm;
75
76
  next.lastIndex = sectionStart + header[0].length;
76
- const nextHeader = next.exec(content);
77
+ const nextHeader = next.exec(syntax);
77
78
  let start = sectionStart;
78
- const marker = content.slice(0, sectionStart).match(/(?:^|\n)(# AGDF-OWNED-MCP: [^\n]+)\n$/);
79
- if (marker) start = sectionStart - marker[0].length + (marker[0].startsWith("\n") ? 1 : 0);
79
+ const marker = content.slice(0, sectionStart).match(/(?:^|\r?\n)(# AGDF-OWNED-MCP: [^\r\n]+)\r?\n$/);
80
+ if (marker) start = sectionStart - marker[0].length + (marker[0].startsWith('\r\n') ? 2 : marker[0].startsWith('\n') ? 1 : 0);
80
81
  return { start, sectionStart, end: nextHeader?.index ?? content.length, marker: marker?.[1] ?? null };
81
82
  }
82
83
 
@@ -215,11 +216,10 @@ export function inspectCodexSetup({ target, env = {}, pluginRoot } = {}) {
215
216
  for (const source of sources({ scope: 'project', target, env })) {
216
217
  try {
217
218
  const snapshot = safeSnapshot(source.path);
218
- if (Buffer.byteLength(snapshot.content) > 65_536) throw Error('bounded_source');
219
- if (/'''|"""/u.test(snapshot.content)) throw Error('unsupported_multiline_source');
219
+ const syntax = codexConfigSyntax(snapshot.content);
220
220
  const sourceDigest = createHash('sha256').update(snapshot.content).digest('hex');
221
221
  checked.push({ source: source.id, path: source.path, status: snapshot.existed ? 'declared' : 'absent', digest: sourceDigest });
222
- const sections = [...snapshot.content.matchAll(/^[ \t]*\[([^\n]+)\][ \t]*$/gm)];
222
+ const sections = [...syntax.matchAll(/^[ \t]*\[([^\r\n]+)\][ \t]*\r?$/gm)];
223
223
  for (let index = 0; index < sections.length; index++) {
224
224
  const header = sections[index][1];
225
225
  const id = /^mcp_servers\.([A-Za-z0-9_-]+)$/u.exec(header)?.[1];
@@ -227,13 +227,13 @@ export function inspectCodexSetup({ target, env = {}, pluginRoot } = {}) {
227
227
  if (!id && !plugin) continue;
228
228
  if (entries.length >= 32) throw Error('bounded_entries');
229
229
  const name = plugin ? 'agdf' : id;
230
- const body = snapshot.content.slice(sections[index].index + sections[index][0].length, sections[index + 1]?.index ?? snapshot.content.length);
230
+ const body = syntax.slice(sections[index].index + sections[index][0].length, sections[index + 1]?.index ?? syntax.length);
231
231
  const role = plugin ? 'product' : ['agdf-cockpit', 'agdf-cockpit-local'].includes(name) ? 'cockpit'
232
232
  : name.startsWith('agdf-test-') || ['agdf-cockpit-draft-check', 'agdf-decision-probe'].includes(name) ? 'diagnostic' : 'legacy_or_conflict';
233
233
  const value = key => new RegExp('^' + key + '[ \\t]*=[ \\t]*([^\\n#]+)', 'm').exec(body)?.[1]?.trim();
234
234
  const declaredEnabled = value('enabled');
235
235
  const enabled = declaredEnabled === 'false' ? false : declaredEnabled === 'true' ? true : declaredEnabled !== undefined || plugin ? null : true;
236
- const beforeHeader = snapshot.content.slice(0, sections[index].index).match(/(# AGDF-OWNED-MCP: [^\n]+)\n$/u)?.[1];
236
+ const beforeHeader = snapshot.content.slice(0, sections[index].index).match(/(# AGDF-OWNED-MCP: [^\r\n]+)\r?\n$/u)?.[1];
237
237
  const mark = identity(beforeHeader);
238
238
  const exactIdOwned = mark && (plugin || name === 'agdf' || mark.server_id === name && mark.role === role);
239
239
  const timeout = key => { const raw = value(key); return raw && /^\d+(?:\.\d+)?$/u.test(raw) ? Number(raw) : null; };
@@ -298,6 +298,24 @@ function mcpTable(header) {
298
298
  return match ? { name: match[1], nested: Boolean(match[2]) } : null;
299
299
  }
300
300
 
301
+ function mcpTables(syntax) {
302
+ const headers = [...syntax.matchAll(/^[ \t]*\[([^\r\n]+)\][ \t]*\r?$/gm)], tables = [], seen = new Set();
303
+ for (let i = 0; i < headers.length; i++) {
304
+ const header = headers[i][1], normalized = header.replace(/[\s"'\[\]]/gu, '');
305
+ if (normalized !== 'mcp_servers' && !normalized.startsWith('mcp_servers.')) continue;
306
+ const table = mcpTable(header);
307
+ if (!table || seen.has(header)) throw Error('unsupported_mcp_header');
308
+ seen.add(header);
309
+ tables.push({ ...table, bodyStart: headers[i].index + headers[i][0].length,
310
+ end: headers[i + 1]?.index ?? syntax.length });
311
+ }
312
+ if (syntax.split('\n').some(line => {
313
+ const key = line.match(/^[ \t]*([^=\n]+)=/u)?.[1]?.replace(/[\s"']/gu, '');
314
+ return key === 'mcp_servers' || key?.startsWith('mcp_servers.');
315
+ })) throw Error('unsupported_mcp_assignment');
316
+ return tables;
317
+ }
318
+
301
319
  export function inspectCodexCockpitPlugin(pluginRoot) {
302
320
  try {
303
321
  if (!isAbsolute(pluginRoot) || portablePath(realpathSync(pluginRoot)) !== portablePath(pluginRoot)) throw Error('plugin_path_invalid');
@@ -390,12 +408,10 @@ export function createCodexCockpitTransition({ target, env = {}, serverId: id, r
390
408
  }
391
409
  // No higher/lower scope same key, aliases or second Cockpit is silently adopted.
392
410
  for (const source of snapshots) {
393
- if (/'''|"""/u.test(source.content) || /^[ \t]*mcp_servers(?:[. \t]|=)/mu.test(source.content))
394
- throw Error('AGDF_MCP_COCKPIT_INVENTORY_UNCONFIRMED');
395
- const headers = [...source.content.matchAll(/^[ \t]*\[mcp_servers\.([^\n\]]+)\][ \t]*$/gm)];
396
- for (const header of headers) {
397
- const table = mcpTable(`mcp_servers.${header[1]}`);
398
- if (!table) throw Error('AGDF_MCP_COCKPIT_INVENTORY_UNCONFIRMED');
411
+ let tables;
412
+ try { tables = mcpTables(codexConfigSyntax(source.content)); }
413
+ catch { throw Error('AGDF_MCP_COCKPIT_INVENTORY_UNCONFIRMED'); }
414
+ for (const table of tables) {
399
415
  if ([potentialAlias('agdf-cockpit'), potentialAlias('agdf-cockpit-local')].includes(potentialAlias(table.name))
400
416
  && !(source.path === path && table.name === id && !table.nested)) throw Error('AGDF_MCP_COCKPIT_NAME_CONFLICT');
401
417
  }
@@ -437,23 +453,18 @@ export function inspectCodexCockpitAdmission({ target, env = {}, exec, manifestR
437
453
  for (const source of sources({ scope: 'project', target, env })) {
438
454
  try {
439
455
  const snapshot = safeSnapshot(source.path), content = snapshot.content;
440
- if (Buffer.byteLength(content) > 65_536 || /'''|"""/u.test(content)) throw Error('unsupported_source');
456
+ let syntax;
457
+ try { syntax = codexConfigSyntax(content); } catch { throw Error('unsupported_source'); }
441
458
  checked.push({ path: source.path, digest: sha(content) });
442
- const headers = [...content.matchAll(/^[ \t]*\[([^\n]+)\][ \t]*$/gm)];
443
- for (let i = 0; i < headers.length; i++) {
444
- const header = headers[i][1];
445
- if (!header.replace(/[\s"']/gu, '').startsWith('mcp_servers.')) continue;
446
- const table = mcpTable(header);
447
- if (!table) throw Error('unsupported_mcp_header');
459
+ for (const table of mcpTables(syntax)) {
448
460
  const name = table.name;
449
461
  if (potentialAlias(name) !== 'agdf_cockpit') continue;
450
462
  if (table.nested) throw Error('unsupported_mcp_header');
451
- const body = content.slice(headers[i].index + headers[i][0].length, headers[i + 1]?.index ?? content.length);
463
+ const body = syntax.slice(table.bodyStart, table.end);
452
464
  const enabled = [...body.matchAll(/^[ \t]*enabled[ \t]*=[ \t]*([^\n#]+)(?:#[^\n]*)?$/gm)];
453
465
  if (enabled.length === 1 && enabled[0][1].trim() === 'false') continue;
454
466
  conflicts.push({ path: source.path, server_id: name, reason: 'same_key_or_possible_alias' });
455
467
  }
456
- if (/^[ \t]*mcp_servers(?:[. \t]|=)/mu.test(content)) throw Error('unsupported_mcp_assignment');
457
468
  } catch (error) { conflicts.push({ path: source.path, reason: error.message }); }
458
469
  }
459
470
  let nativeProviderStatus = 'unobserved';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-agdf",
3
- "version": "0.20.0",
3
+ "version": "0.20.1",
4
4
  "description": "Install AGDF plugins, bootstrap supported repository surfaces and validate durable governance state.",
5
5
  "type": "module",
6
6
  "engines": {