@okrlinkhub/agent-factory 2.0.3 → 3.0.0

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.
@@ -138,7 +138,6 @@ const globalSkillManifestItemValidator = v.object({
138
138
  version: v.string(),
139
139
  moduleFormat: globalSkillModuleFormatValidator,
140
140
  entryPoint: v.string(),
141
- sourceJs: v.string(),
142
141
  sha256: v.string(),
143
142
  skillDirName: v.string(),
144
143
  files: v.array(globalSkillManifestFileValidator),
@@ -587,7 +586,7 @@ export const deployGlobalSkill = mutation({
587
586
  displayName: v.optional(v.string()),
588
587
  description: v.optional(v.string()),
589
588
  version: v.string(),
590
- sourceJs: v.string(),
589
+ files: v.array(globalSkillManifestFileValidator),
591
590
  entryPoint: v.optional(v.string()),
592
591
  moduleFormat: v.optional(globalSkillModuleFormatValidator),
593
592
  releaseChannel: v.optional(globalSkillReleaseChannelValidator),
@@ -611,23 +610,17 @@ export const deployGlobalSkill = mutation({
611
610
  const releaseChannel = args.releaseChannel ?? "stable";
612
611
  const moduleFormat = args.moduleFormat ?? "esm";
613
612
  const actor = args.actor?.trim() || "system";
614
- const sourceJs = args.sourceJs.trim();
613
+ const files = await normalizeGlobalSkillBundleFiles(args.files, moduleFormat);
615
614
  if (!/^[a-z0-9][a-z0-9-_]{1,127}$/.test(slug)) {
616
615
  throw new Error("Invalid skill slug. Use lowercase letters, numbers, '-' and '_'.");
617
616
  }
618
617
  if (!/^\d+\.\d+\.\d+(?:[-+][A-Za-z0-9.-]+)?$/.test(version)) {
619
618
  throw new Error("Invalid skill version. Use semantic versioning format.");
620
619
  }
621
- if (sourceJs.length < 16) {
622
- throw new Error("Skill source is too short.");
623
- }
624
- if (sourceJs.length > 200_000) {
625
- throw new Error("Skill source too large (max 200KB).");
626
- }
627
620
  if (!entryPoint) {
628
621
  throw new Error("entryPoint is required.");
629
622
  }
630
- const sha256 = await computeSha256Hex(sourceJs);
623
+ const sha256 = await computeGlobalSkillBundleSha256(files);
631
624
  const existingSkill = await ctx.db
632
625
  .query("globalSkills")
633
626
  .withIndex("by_slug", (q) => q.eq("slug", slug))
@@ -663,14 +656,16 @@ export const deployGlobalSkill = mutation({
663
656
  version,
664
657
  moduleFormat,
665
658
  entryPoint,
666
- sourceJs,
659
+ files,
667
660
  sha256,
668
661
  createdBy: actor,
669
662
  createdAt: nowMs,
670
663
  });
671
664
  }
672
- else if (existingVersion.sha256 !== sha256) {
673
- throw new Error(`Skill ${slug}@${version} already exists with a different source.`);
665
+ else if (existingVersion.sha256 !== sha256 ||
666
+ existingVersion.moduleFormat !== moduleFormat ||
667
+ existingVersion.entryPoint !== entryPoint) {
668
+ throw new Error(`Skill ${slug}@${version} already exists with a different bundle.`);
674
669
  }
675
670
  const activeReleases = await ctx.db
676
671
  .query("globalSkillReleases")
@@ -805,7 +800,7 @@ export const getWorkerGlobalSkillsManifest = query({
805
800
  version: version.version,
806
801
  moduleFormat: version.moduleFormat,
807
802
  entryPoint: version.entryPoint,
808
- sourceJs: version.sourceJs,
803
+ files: version.files,
809
804
  sha256: version.sha256,
810
805
  });
811
806
  manifestSkills.push({
@@ -813,7 +808,6 @@ export const getWorkerGlobalSkillsManifest = query({
813
808
  version: version.version,
814
809
  moduleFormat: version.moduleFormat,
815
810
  entryPoint: version.entryPoint,
816
- sourceJs: version.sourceJs,
817
811
  sha256: version.sha256,
818
812
  skillDirName: materializedSkill.skillDirName,
819
813
  files: materializedSkill.files,
@@ -825,7 +819,11 @@ export const getWorkerGlobalSkillsManifest = query({
825
819
  return a.version.localeCompare(b.version);
826
820
  });
827
821
  const fingerprintSeed = manifestSkills
828
- .map((row) => `${row.slug}@${row.version}:${row.sha256}`)
822
+ .map((row) => `${row.slug}@${row.version}:${row.sha256}:${row.files
823
+ .filter((file) => file.path !== ".af-global-skill.json")
824
+ .map((file) => `${file.path}:${file.sha256}`)
825
+ .sort()
826
+ .join(",")}`)
829
827
  .join("|");
830
828
  const manifestVersion = await computeSha256Hex(fingerprintSeed || "empty");
831
829
  return {
@@ -2632,26 +2630,11 @@ function inferMimeTypeFromFilePath(filePath) {
2632
2630
  }
2633
2631
  async function buildGlobalSkillMaterialization(skill) {
2634
2632
  const skillDirName = normalizeGlobalSkillDirName(skill.slug);
2635
- const scriptExt = skill.moduleFormat === "cjs" ? "cjs" : "mjs";
2636
- const scriptRelativePath = `scripts/index.${scriptExt}`;
2637
- const skillMd = [
2638
- "---",
2639
- `name: ${skill.slug}`,
2640
- `description: Global skill ${skill.slug}@${skill.version} provisioned by agent-factory.`,
2641
- "---",
2642
- "",
2643
- "# Global Skill",
2644
- "",
2645
- "This skill is generated automatically from agent-factory globalSkills.",
2646
- "",
2647
- `- slug: ${skill.slug}`,
2648
- `- version: ${skill.version}`,
2649
- `- entryPoint: ${skill.entryPoint}`,
2650
- `- moduleFormat: ${skill.moduleFormat}`,
2651
- "",
2652
- "Do not edit manually.",
2653
- "",
2654
- ].join("\n");
2633
+ const bundleFiles = await normalizeGlobalSkillBundleFiles(skill.files, skill.moduleFormat);
2634
+ const bundleSha256 = await computeGlobalSkillBundleSha256(bundleFiles);
2635
+ if (bundleSha256 !== skill.sha256) {
2636
+ throw new Error(`Global skill bundle checksum mismatch for ${skill.slug}@${skill.version}.`);
2637
+ }
2655
2638
  const markerJson = `${JSON.stringify({
2656
2639
  slug: skill.slug,
2657
2640
  version: skill.version,
@@ -2659,19 +2642,9 @@ async function buildGlobalSkillMaterialization(skill) {
2659
2642
  managedBy: "agent-factory",
2660
2643
  entryPoint: skill.entryPoint,
2661
2644
  moduleFormat: skill.moduleFormat,
2662
- generatedAt: Date.now(),
2663
2645
  }, null, 2)}\n`;
2664
2646
  const files = [
2665
- {
2666
- path: "SKILL.md",
2667
- content: `${skillMd}\n`,
2668
- sha256: await computeSha256Hex(`${skillMd}\n`),
2669
- },
2670
- {
2671
- path: scriptRelativePath,
2672
- content: `${skill.sourceJs.trimEnd()}\n`,
2673
- sha256: await computeSha256Hex(`${skill.sourceJs.trimEnd()}\n`),
2674
- },
2647
+ ...bundleFiles,
2675
2648
  {
2676
2649
  path: ".af-global-skill.json",
2677
2650
  content: markerJson,
@@ -2680,10 +2653,61 @@ async function buildGlobalSkillMaterialization(skill) {
2680
2653
  ];
2681
2654
  return { skillDirName, files };
2682
2655
  }
2656
+ async function normalizeGlobalSkillBundleFiles(rawFiles, moduleFormat) {
2657
+ if (!Array.isArray(rawFiles) || rawFiles.length === 0) {
2658
+ throw new Error("Global skill bundle must contain at least one file.");
2659
+ }
2660
+ const byPath = new Map();
2661
+ let totalBytes = 0;
2662
+ for (const rawFile of rawFiles) {
2663
+ const path = normalizeRelativePath(rawFile.path);
2664
+ if (path === ".af-global-skill.json") {
2665
+ throw new Error("Bundle files must not include .af-global-skill.json.");
2666
+ }
2667
+ if (byPath.has(path)) {
2668
+ throw new Error(`Duplicate bundle file path: ${path}`);
2669
+ }
2670
+ const content = typeof rawFile.content === "string" ? rawFile.content : "";
2671
+ totalBytes += new TextEncoder().encode(content).length;
2672
+ if (totalBytes > 200_000) {
2673
+ throw new Error("Global skill bundle too large (max 200KB).");
2674
+ }
2675
+ const sha256 = typeof rawFile.sha256 === "string" ? rawFile.sha256.trim() : "";
2676
+ if (!sha256) {
2677
+ throw new Error(`Missing bundle checksum for ${path}.`);
2678
+ }
2679
+ const computedSha = await computeSha256Hex(content);
2680
+ if (computedSha !== sha256) {
2681
+ throw new Error(`Global skill checksum mismatch for ${path}.`);
2682
+ }
2683
+ byPath.set(path, { path, content, sha256 });
2684
+ }
2685
+ const entryScriptPath = `scripts/index.${moduleFormat === "cjs" ? "cjs" : "mjs"}`;
2686
+ if (!byPath.has("SKILL.md")) {
2687
+ throw new Error("Global skill bundle must include SKILL.md.");
2688
+ }
2689
+ if (!byPath.has(entryScriptPath)) {
2690
+ throw new Error(`Global skill bundle must include ${entryScriptPath}.`);
2691
+ }
2692
+ return [...byPath.values()].sort((left, right) => left.path.localeCompare(right.path));
2693
+ }
2694
+ async function computeGlobalSkillBundleSha256(files) {
2695
+ const fingerprint = files
2696
+ .map((file) => `${file.path}\n${file.sha256}\n${file.content}`)
2697
+ .join("\n---\n");
2698
+ return await computeSha256Hex(fingerprint);
2699
+ }
2683
2700
  function normalizeGlobalSkillDirName(slug) {
2684
2701
  const normalized = slug.trim().toLowerCase().replace(/[^a-z0-9-_]/g, "-");
2685
2702
  return normalized.length > 0 ? normalized : "unnamed-skill";
2686
2703
  }
2704
+ function normalizeRelativePath(value) {
2705
+ const normalized = String(value || "").replaceAll("\\", "/").replace(/^\/+/, "");
2706
+ if (!normalized || normalized === "." || normalized.includes("../")) {
2707
+ throw new Error(`invalid_relative_path:${value}`);
2708
+ }
2709
+ return normalized;
2710
+ }
2687
2711
  function getBridgeSecretRefsForProfile(agentKey, bridgeConfig) {
2688
2712
  if (!bridgeConfig?.enabled) {
2689
2713
  return [];