@inkeep/create-agents 0.0.0-dev-20260224200401 → 0.0.0-dev-20260225021710

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.
@@ -494,6 +494,10 @@ describe('syncTemplateDependencies', () => {
494
494
  }
495
495
  beforeEach(() => {
496
496
  vi.clearAllMocks();
497
+ vi.stubGlobal('fetch', vi.fn().mockRejectedValue(new Error('no network in tests')));
498
+ });
499
+ afterEach(() => {
500
+ vi.unstubAllGlobals();
497
501
  });
498
502
  it('should update @inkeep/* dependencies to match CLI version', async () => {
499
503
  const mockPkg = {
@@ -588,21 +592,127 @@ describe('syncTemplateDependencies', () => {
588
592
  },
589
593
  }), { spaces: 2 });
590
594
  });
591
- it('should not update excluded packages like @inkeep/agents-ui', async () => {
595
+ it('should resolve @inkeep/agents-ui version from npm registry', async () => {
596
+ const mockFetch = vi.fn().mockResolvedValue({
597
+ ok: true,
598
+ json: () => Promise.resolve({ version: '0.16.0' }),
599
+ });
600
+ vi.stubGlobal('fetch', mockFetch);
592
601
  const mockPkg = {
593
602
  name: 'test-project',
594
603
  dependencies: {
595
604
  '@inkeep/agents-core': '^0.50.3',
596
- '@inkeep/agents-ui': '^0.50.3',
605
+ '@inkeep/agents-ui': '^0.15.12',
597
606
  '@inkeep/agents-sdk': '^0.50.3',
598
607
  },
599
608
  };
600
609
  setupFlatTemplate(mockPkg);
601
610
  await syncTemplateDependencies('/test/path');
611
+ expect(mockFetch).toHaveBeenCalledWith('https://registry.npmjs.org/@inkeep/agents-ui/latest', expect.objectContaining({ signal: expect.any(AbortSignal) }));
612
+ expect(mockFetch).toHaveBeenCalledTimes(1);
613
+ expect(fs.writeJson).toHaveBeenCalledWith('/test/path/package.json', expect.objectContaining({
614
+ dependencies: {
615
+ '@inkeep/agents-core': '^1.2.3',
616
+ '@inkeep/agents-ui': '^0.16.0',
617
+ '@inkeep/agents-sdk': '^1.2.3',
618
+ },
619
+ }), { spaces: 2 });
620
+ });
621
+ it('should leave divergent package specifier unchanged when registry fetch fails', async () => {
622
+ const mockFetch = vi.fn().mockRejectedValue(new Error('network error'));
623
+ vi.stubGlobal('fetch', mockFetch);
624
+ const mockPkg = {
625
+ name: 'test-project',
626
+ dependencies: {
627
+ '@inkeep/agents-core': '^0.50.3',
628
+ '@inkeep/agents-ui': '^0.15.12',
629
+ },
630
+ };
631
+ setupFlatTemplate(mockPkg);
632
+ await syncTemplateDependencies('/test/path');
633
+ expect(fs.writeJson).toHaveBeenCalledWith('/test/path/package.json', expect.objectContaining({
634
+ dependencies: {
635
+ '@inkeep/agents-core': '^1.2.3',
636
+ '@inkeep/agents-ui': '^0.15.12',
637
+ },
638
+ }), { spaces: 2 });
639
+ });
640
+ it('should leave divergent package specifier unchanged when registry returns non-ok', async () => {
641
+ const mockFetch = vi.fn().mockResolvedValue({
642
+ ok: false,
643
+ status: 404,
644
+ });
645
+ vi.stubGlobal('fetch', mockFetch);
646
+ const mockPkg = {
647
+ name: 'test-project',
648
+ dependencies: {
649
+ '@inkeep/agents-ui': '^0.15.12',
650
+ },
651
+ };
652
+ setupFlatTemplate(mockPkg);
653
+ await syncTemplateDependencies('/test/path');
654
+ expect(fs.writeJson).toHaveBeenCalledWith('/test/path/package.json', expect.objectContaining({
655
+ dependencies: {
656
+ '@inkeep/agents-ui': '^0.15.12',
657
+ },
658
+ }), { spaces: 2 });
659
+ });
660
+ it('should resolve divergent package version in devDependencies from npm registry', async () => {
661
+ const mockFetch = vi.fn().mockResolvedValue({
662
+ ok: true,
663
+ json: () => Promise.resolve({ version: '0.16.0' }),
664
+ });
665
+ vi.stubGlobal('fetch', mockFetch);
666
+ const mockPkg = {
667
+ name: 'test-project',
668
+ dependencies: { '@inkeep/agents-core': '^0.50.3' },
669
+ devDependencies: { '@inkeep/agents-ui': '^0.15.12' },
670
+ };
671
+ setupFlatTemplate(mockPkg);
672
+ await syncTemplateDependencies('/test/path');
673
+ expect(fs.writeJson).toHaveBeenCalledWith('/test/path/package.json', expect.objectContaining({
674
+ dependencies: { '@inkeep/agents-core': '^1.2.3' },
675
+ devDependencies: { '@inkeep/agents-ui': '^0.16.0' },
676
+ }), { spaces: 2 });
677
+ });
678
+ it('should only fetch once when same divergent package appears in multiple files', async () => {
679
+ const mockFetch = vi.fn().mockResolvedValue({
680
+ ok: true,
681
+ json: () => Promise.resolve({ version: '0.16.0' }),
682
+ });
683
+ vi.stubGlobal('fetch', mockFetch);
684
+ const nodeFs = await import('node:fs');
685
+ vi.mocked(nodeFs.readFileSync).mockReturnValue(JSON.stringify({ version: '1.2.3' }));
686
+ const rootPkg = {
687
+ name: 'monorepo',
688
+ dependencies: { '@inkeep/agents-core': '^0.50.3', '@inkeep/agents-ui': '^0.15.12' },
689
+ };
690
+ const nestedPkg = {
691
+ name: 'nested-app',
692
+ dependencies: { '@inkeep/agents-ui': '^0.15.12', '@inkeep/agents-sdk': '^0.50.3' },
693
+ };
694
+ vi.mocked(fs.pathExists).mockImplementation(async (p) => {
695
+ const s = String(p);
696
+ return s === '/test/path/package.json' || s === '/test/path/apps/api/package.json';
697
+ });
698
+ vi.mocked(fs.readdir)
699
+ .mockResolvedValueOnce([mockDirent('apps', true)])
700
+ .mockResolvedValueOnce([mockDirent('api', true)])
701
+ .mockResolvedValueOnce([]);
702
+ vi.mocked(fs.readJson).mockResolvedValueOnce(rootPkg).mockResolvedValueOnce(nestedPkg);
703
+ vi.mocked(fs.writeJson).mockResolvedValue(undefined);
704
+ await syncTemplateDependencies('/test/path');
705
+ expect(mockFetch).toHaveBeenCalledTimes(1);
706
+ expect(fs.writeJson).toHaveBeenCalledTimes(2);
602
707
  expect(fs.writeJson).toHaveBeenCalledWith('/test/path/package.json', expect.objectContaining({
603
708
  dependencies: {
604
709
  '@inkeep/agents-core': '^1.2.3',
605
- '@inkeep/agents-ui': '^0.50.3',
710
+ '@inkeep/agents-ui': '^0.16.0',
711
+ },
712
+ }), { spaces: 2 });
713
+ expect(fs.writeJson).toHaveBeenCalledWith('/test/path/apps/api/package.json', expect.objectContaining({
714
+ dependencies: {
715
+ '@inkeep/agents-ui': '^0.16.0',
606
716
  '@inkeep/agents-sdk': '^1.2.3',
607
717
  },
608
718
  }), { spaces: 2 });
@@ -632,23 +742,22 @@ describe('syncTemplateDependencies', () => {
632
742
  name: 'nested-app',
633
743
  dependencies: { '@inkeep/agents-sdk': '^0.50.3', express: '^4.0.0' },
634
744
  };
635
- vi.mocked(fs.pathExists).mockResolvedValue(true);
745
+ vi.mocked(fs.pathExists).mockImplementation(async (p) => {
746
+ const s = String(p);
747
+ return s === '/test/path/package.json' || s === '/test/path/apps/api/package.json';
748
+ });
636
749
  vi.mocked(fs.readdir)
637
750
  .mockResolvedValueOnce([mockDirent('apps', true), mockDirent('README.md', false)])
638
751
  .mockResolvedValueOnce([mockDirent('api', true)])
639
752
  .mockResolvedValueOnce([]);
640
- vi.mocked(fs.readJson)
641
- .mockResolvedValueOnce(rootPkg)
642
- .mockResolvedValueOnce(nestedPkg)
643
- .mockResolvedValueOnce(nestedPkg);
753
+ vi.mocked(fs.readJson).mockResolvedValueOnce(rootPkg).mockResolvedValueOnce(nestedPkg);
644
754
  vi.mocked(fs.writeJson).mockResolvedValue(undefined);
645
755
  await syncTemplateDependencies('/test/path');
646
- expect(fs.writeJson).toHaveBeenCalledTimes(3);
756
+ expect(fs.writeJson).toHaveBeenCalledTimes(2);
647
757
  expect(fs.writeJson).toHaveBeenCalledWith('/test/path/package.json', expect.objectContaining({
648
758
  dependencies: { '@inkeep/agents-core': '^1.2.3' },
649
759
  }), { spaces: 2 });
650
- expect(fs.writeJson).toHaveBeenCalledWith(expect.stringContaining('apps/package.json'), expect.anything(), { spaces: 2 });
651
- expect(fs.writeJson).toHaveBeenCalledWith(expect.stringContaining('apps/api/package.json'), expect.objectContaining({
760
+ expect(fs.writeJson).toHaveBeenCalledWith('/test/path/apps/api/package.json', expect.objectContaining({
652
761
  dependencies: { '@inkeep/agents-sdk': '^1.2.3', express: '^4.0.0' },
653
762
  }), { spaces: 2 });
654
763
  });
package/dist/utils.js CHANGED
@@ -34,6 +34,7 @@ const agentsTemplateRepo = 'https://github.com/inkeep/agents/create-agents-templ
34
34
  const projectTemplateRepo = 'https://github.com/inkeep/agents/agents-cookbook/template-projects';
35
35
  const execAsync = promisify(exec);
36
36
  const agentsApiPort = '3002';
37
+ const DIVERGENT_PACKAGES = new Set(['@inkeep/agents-ui']);
37
38
  function getCliVersion() {
38
39
  try {
39
40
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
@@ -44,19 +45,66 @@ function getCliVersion() {
44
45
  return '';
45
46
  }
46
47
  }
48
+ async function fetchLatestVersion(packageName) {
49
+ const controller = new AbortController();
50
+ const timeoutId = setTimeout(() => controller.abort(), 10000);
51
+ try {
52
+ const response = await fetch(`https://registry.npmjs.org/${packageName}/latest`, {
53
+ signal: controller.signal,
54
+ });
55
+ if (!response.ok)
56
+ return null;
57
+ const data = (await response.json());
58
+ return data.version ?? null;
59
+ }
60
+ catch {
61
+ return null;
62
+ }
63
+ finally {
64
+ clearTimeout(timeoutId);
65
+ }
66
+ }
47
67
  export async function syncTemplateDependencies(templatePath) {
48
68
  const cliVersion = getCliVersion();
49
69
  if (!cliVersion)
50
70
  return;
51
71
  const packageJsonPaths = await findPackageJsonFiles(templatePath);
52
- await Promise.all(packageJsonPaths.map(async (pkgPath) => {
72
+ const divergentVersions = new Map();
73
+ const allDeps = new Set();
74
+ const pkgDataList = [];
75
+ for (const pkgPath of packageJsonPaths) {
53
76
  const pkg = await fs.readJson(pkgPath);
77
+ pkgDataList.push({ pkgPath, pkg });
54
78
  for (const depType of ['dependencies', 'devDependencies']) {
55
79
  const deps = pkg[depType];
56
80
  if (!deps)
57
81
  continue;
58
82
  for (const name of Object.keys(deps)) {
59
- if (name.startsWith('@inkeep/') && name !== '@inkeep/agents-ui') {
83
+ if (name.startsWith('@inkeep/') && DIVERGENT_PACKAGES.has(name)) {
84
+ allDeps.add(name);
85
+ }
86
+ }
87
+ }
88
+ }
89
+ await Promise.all([...allDeps].map(async (name) => {
90
+ const version = await fetchLatestVersion(name);
91
+ if (version)
92
+ divergentVersions.set(name, version);
93
+ }));
94
+ await Promise.all(pkgDataList.map(async ({ pkgPath, pkg }) => {
95
+ for (const depType of ['dependencies', 'devDependencies']) {
96
+ const deps = pkg[depType];
97
+ if (!deps)
98
+ continue;
99
+ for (const name of Object.keys(deps)) {
100
+ if (!name.startsWith('@inkeep/'))
101
+ continue;
102
+ if (DIVERGENT_PACKAGES.has(name)) {
103
+ const resolved = divergentVersions.get(name);
104
+ if (resolved)
105
+ deps[name] = `^${resolved}`;
106
+ }
107
+ else {
60
108
  deps[name] = `^${cliVersion}`;
61
109
  }
62
110
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inkeep/create-agents",
3
- "version": "0.0.0-dev-20260224200401",
3
+ "version": "0.0.0-dev-20260225021710",
4
4
  "description": "Create an Inkeep Agent Framework project",
5
5
  "type": "module",
6
6
  "bin": {
@@ -33,7 +33,7 @@
33
33
  "degit": "^2.8.4",
34
34
  "fs-extra": "^11.0.0",
35
35
  "picocolors": "^1.0.0",
36
- "@inkeep/agents-core": "0.0.0-dev-20260224200401"
36
+ "@inkeep/agents-core": "0.0.0-dev-20260225021710"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@types/degit": "^2.8.6",