66 */
77
88import { spawn } from "node:child_process" ;
9- import { existsSync , mkdirSync , readFileSync } from "node:fs" ;
9+ import {
10+ existsSync ,
11+ mkdirSync ,
12+ readFileSync ,
13+ readdirSync ,
14+ writeFileSync ,
15+ } from "node:fs" ;
1016import path from "node:path" ;
1117import { resolveRepoRootFromImportMeta } from "./lib/repo-root.mjs" ;
1218
@@ -18,9 +24,10 @@ const ELIZA_ROOT = existsSync(
1824 ? ROOT
1925 : path . join ( ROOT , "eliza" ) ;
2026
21- // Target packages to pack. These are the package boundary that Milady consumes
22- // when it runs without a repo-local eliza checkout.
23- const TARGETS = [
27+ // Seed packages to pack. Their local workspace dependencies are added
28+ // automatically so PR tarball install tests do not depend on already-published
29+ // beta packages.
30+ const SEED_TARGETS = [
2431 { label : "@elizaos/core" , dir : path . join ( ELIZA_ROOT , "packages" , "core" ) } ,
2532 {
2633 label : "@elizaos/shared" ,
@@ -78,6 +85,195 @@ function readPackageJson(dir) {
7885 }
7986}
8087
88+ function collectWorkspacePackages ( root ) {
89+ const packageRoots = [
90+ path . join ( root , "packages" ) ,
91+ path . join ( root , "plugins" ) ,
92+ path . join ( root , "cloud" , "packages" ) ,
93+ ] ;
94+ const packages = new Map ( ) ;
95+
96+ for ( const packageRoot of packageRoots ) {
97+ walk ( packageRoot , ( entryPath ) => {
98+ if ( path . basename ( entryPath ) !== "package.json" ) {
99+ return ;
100+ }
101+ const pkgJson = readPackageJson ( path . dirname ( entryPath ) ) ;
102+ if (
103+ ! pkgJson ||
104+ pkgJson . private === true ||
105+ typeof pkgJson . name !== "string" ||
106+ typeof pkgJson . version !== "string"
107+ ) {
108+ return ;
109+ }
110+ if ( ! packages . has ( pkgJson . name ) ) {
111+ packages . set ( pkgJson . name , {
112+ label : pkgJson . name ,
113+ dir : path . dirname ( entryPath ) ,
114+ } ) ;
115+ }
116+ } ) ;
117+ }
118+
119+ for ( const target of SEED_TARGETS ) {
120+ packages . set ( target . label , target ) ;
121+ }
122+
123+ return packages ;
124+ }
125+
126+ function walk ( dirPath , visit ) {
127+ let entries ;
128+ try {
129+ entries = readdirSync ( dirPath , { withFileTypes : true } ) ;
130+ } catch ( error ) {
131+ if (
132+ error &&
133+ typeof error === "object" &&
134+ "code" in error &&
135+ error . code === "ENOENT"
136+ ) {
137+ return ;
138+ }
139+ throw error ;
140+ }
141+
142+ for ( const entry of entries ) {
143+ const entryPath = path . join ( dirPath , entry . name ) ;
144+ if ( entry . isDirectory ( ) ) {
145+ if (
146+ [
147+ "node_modules" ,
148+ "dist" ,
149+ ".git" ,
150+ ".turbo" ,
151+ "android" ,
152+ "ios" ,
153+ "build" ,
154+ ] . includes ( entry . name )
155+ ) {
156+ continue ;
157+ }
158+ walk ( entryPath , visit ) ;
159+ continue ;
160+ }
161+ visit ( entryPath ) ;
162+ }
163+ }
164+
165+ function collectLocalDependencyNames ( pkgJson , workspacePackages ) {
166+ const names = new Set ( ) ;
167+ for ( const sectionName of [ "dependencies" , "optionalDependencies" ] ) {
168+ const section = pkgJson [ sectionName ] ;
169+ if ( ! section || typeof section !== "object" ) {
170+ continue ;
171+ }
172+ for ( const [ name , spec ] of Object . entries ( section ) ) {
173+ if (
174+ typeof spec === "string" &&
175+ spec . startsWith ( "workspace:" ) &&
176+ workspacePackages . has ( name )
177+ ) {
178+ names . add ( name ) ;
179+ }
180+ }
181+ }
182+ return names ;
183+ }
184+
185+ function resolveTargets ( ) {
186+ const workspacePackages = collectWorkspacePackages ( ELIZA_ROOT ) ;
187+ const targets = new Map ( ) ;
188+ const queue = [ ...SEED_TARGETS . map ( ( target ) => target . label ) ] ;
189+
190+ for ( let index = 0 ; index < queue . length ; index += 1 ) {
191+ const label = queue [ index ] ;
192+ if ( targets . has ( label ) ) {
193+ continue ;
194+ }
195+
196+ const target = workspacePackages . get ( label ) ;
197+ if ( ! target ) {
198+ throw new Error ( `[pack-upstreams] Missing local workspace package ${ label } ` ) ;
199+ }
200+
201+ const pkgJson = readPackageJson ( target . dir ) ;
202+ if ( ! pkgJson ) {
203+ throw new Error ( `[pack-upstreams] No package.json found in ${ target . dir } ` ) ;
204+ }
205+
206+ targets . set ( label , target ) ;
207+ for ( const dependencyName of collectLocalDependencyNames (
208+ pkgJson ,
209+ workspacePackages ,
210+ ) ) {
211+ if ( ! targets . has ( dependencyName ) ) {
212+ queue . push ( dependencyName ) ;
213+ }
214+ }
215+ }
216+
217+ return {
218+ targets : [ ...targets . values ( ) ] ,
219+ workspacePackages,
220+ } ;
221+ }
222+
223+ function workspaceSpecToVersion ( spec , version ) {
224+ if ( spec === "workspace:^" ) {
225+ return `^${ version } ` ;
226+ }
227+ if ( spec === "workspace:~" ) {
228+ return `~${ version } ` ;
229+ }
230+ return version ;
231+ }
232+
233+ function rewriteWorkspaceDependencies ( packDir , pkgJson , workspacePackages ) {
234+ let changed = false ;
235+ const nextPkgJson = structuredClone ( pkgJson ) ;
236+ for ( const sectionName of [
237+ "dependencies" ,
238+ "optionalDependencies" ,
239+ "peerDependencies" ,
240+ "devDependencies" ,
241+ ] ) {
242+ const section = nextPkgJson [ sectionName ] ;
243+ if ( ! section || typeof section !== "object" ) {
244+ continue ;
245+ }
246+ for ( const [ name , spec ] of Object . entries ( section ) ) {
247+ if ( typeof spec !== "string" || ! spec . startsWith ( "workspace:" ) ) {
248+ continue ;
249+ }
250+ const workspacePackage = workspacePackages . get ( name ) ;
251+ if ( ! workspacePackage ) {
252+ throw new Error (
253+ `[pack-upstreams] ${ pkgJson . name } depends on unknown workspace package ${ name } ` ,
254+ ) ;
255+ }
256+ const dependencyPkgJson = readPackageJson ( workspacePackage . dir ) ;
257+ if ( ! dependencyPkgJson ?. version ) {
258+ throw new Error (
259+ `[pack-upstreams] Could not resolve version for workspace package ${ name } ` ,
260+ ) ;
261+ }
262+ section [ name ] = workspaceSpecToVersion ( spec , dependencyPkgJson . version ) ;
263+ changed = true ;
264+ }
265+ }
266+
267+ if ( ! changed ) {
268+ return null ;
269+ }
270+
271+ const packageJsonPath = path . join ( packDir , "package.json" ) ;
272+ const originalPackageJson = readFileSync ( packageJsonPath , "utf8" ) ;
273+ writeFileSync ( packageJsonPath , `${ JSON . stringify ( nextPkgJson , null , 2 ) } \n` ) ;
274+ return ( ) => writeFileSync ( packageJsonPath , originalPackageJson ) ;
275+ }
276+
81277function packageTarballName ( pkgJson ) {
82278 return `${ pkgJson . name . replace ( / ^ @ / , "" ) . replace ( "/" , "-" ) } -${ pkgJson . version } .tgz` ;
83279}
@@ -100,7 +296,14 @@ async function packUpstreams() {
100296 mkdirSync ( ARTIFACTS_DIR , { recursive : true } ) ;
101297 }
102298
103- for ( const target of TARGETS ) {
299+ const { targets, workspacePackages } = resolveTargets ( ) ;
300+ console . log (
301+ `[pack-upstreams] Packing ${ targets . length } package(s): ${ targets
302+ . map ( ( target ) => target . label )
303+ . join ( ", " ) } `,
304+ ) ;
305+
306+ for ( const target of targets ) {
104307 const pkgDir = target . dir ;
105308 if ( ! existsSync ( pkgDir ) ) {
106309 throw new Error (
@@ -153,11 +356,20 @@ async function packUpstreams() {
153356 console . log (
154357 `[pack-upstreams] Packing ${ packPkgJson . name } from ${ packDir } ...` ,
155358 ) ;
156- await runCommand (
157- "npm" ,
158- [ "pack" , "--pack-destination" , ARTIFACTS_DIR ] ,
359+ const restorePackageJson = rewriteWorkspaceDependencies (
159360 packDir ,
361+ packPkgJson ,
362+ workspacePackages ,
160363 ) ;
364+ try {
365+ await runCommand (
366+ "npm" ,
367+ [ "pack" , "--pack-destination" , ARTIFACTS_DIR ] ,
368+ packDir ,
369+ ) ;
370+ } finally {
371+ restorePackageJson ?. ( ) ;
372+ }
161373
162374 if ( ! existsSync ( destTarballPath ) ) {
163375 throw new Error (
0 commit comments