@@ -4,9 +4,13 @@ const fs = require('fs');
44const fsPromise = require ( 'fs' ) . promises ;
55const path = require ( 'path' ) ;
66const os = require ( 'os' ) ;
7+ const sudo = require ( '@expo/sudo-prompt' ) ;
78const { lintFile} = require ( "./ESLint/service" ) ;
89let openModule , open ; // dynamic import when needed
910
11+ const options = { name : 'Phoenix Code' } ;
12+ const content = '{"licensedDevice": true}\n' ;
13+
1014async function _importOpen ( ) {
1115 if ( open ) {
1216 return open ;
@@ -269,6 +273,82 @@ async function getEnvironmentVariable(varName) {
269273 return process . env [ varName ] ;
270274}
271275
276+ function getLicensePath ( ) {
277+ switch ( os . platform ( ) ) {
278+ case 'win32' :
279+ return 'C:\\Program Files\\Phoenix Code Control\\device-license' ;
280+ case 'darwin' :
281+ return '/Library/Application Support/phoenix-code-control/device-license' ;
282+ case 'linux' :
283+ return '/etc/phoenix-code-control/device-license' ;
284+ default :
285+ throw new Error ( `Unsupported platform: ${ os . platform ( ) } ` ) ;
286+ }
287+ }
288+
289+ function sudoExec ( command ) {
290+ return new Promise ( ( resolve , reject ) => {
291+ sudo . exec ( command , options , ( error , stdout , stderr ) => {
292+ if ( error ) {
293+ return reject ( error ) ;
294+ }
295+ resolve ( { stdout, stderr } ) ;
296+ } ) ;
297+ } ) ;
298+ }
299+
300+ function readFileUtf8 ( p ) {
301+ return new Promise ( ( resolve , reject ) => {
302+ fs . readFile ( p , 'utf8' , ( err , data ) => ( err ? reject ( err ) : resolve ( data ) ) ) ;
303+ } ) ;
304+ }
305+
306+ async function addDeviceLicense ( ) {
307+ const targetPath = getLicensePath ( ) ;
308+ let command ;
309+
310+ if ( os . platform ( ) === 'win32' ) {
311+ // PowerShell with explicit dirs and safe quoting
312+ const dir = 'C:\\Program Files\\Phoenix Code Control' ;
313+ const psContent = content . replace ( / ' / g, "''" ) ;
314+ command = `powershell -Command "New-Item -ItemType Directory -Force '${ dir } ' | Out-Null; Set-Content -Path '${ targetPath } ' -Value '${ psContent } ' -Encoding UTF8"` ;
315+ } else {
316+ const dir = path . dirname ( targetPath ) ;
317+ // POSIX: mkdir + printf (use absolute paths)
318+ const safe = content . replace ( / ' / g, `'\\''` ) ;
319+ command = `/bin/mkdir -p "${ dir } " && /bin/printf '%s' '${ safe } ' > "${ targetPath } "` ;
320+ }
321+
322+ await sudoExec ( command ) ;
323+ return targetPath ;
324+ }
325+
326+ async function removeDeviceLicense ( ) {
327+ const targetPath = getLicensePath ( ) ;
328+ let command ;
329+
330+ if ( os . platform ( ) === 'win32' ) {
331+ command = `powershell -Command "if (Test-Path '${ targetPath } ') { Remove-Item -Path '${ targetPath } ' -Force }"` ;
332+ } else {
333+ command = `/bin/rm -f "${ targetPath } "` ;
334+ }
335+
336+ await sudoExec ( command ) ;
337+ return targetPath ;
338+ }
339+
340+ async function isLicensedDevice ( ) {
341+ const targetPath = getLicensePath ( ) ;
342+ try {
343+ const data = await readFileUtf8 ( targetPath ) ;
344+ const json = JSON . parse ( data ) ;
345+ return json && json . licensedDevice === true ;
346+ } catch {
347+ // file missing, unreadable, or invalid JSON
348+ return false ;
349+ }
350+ }
351+
272352exports . getURLContent = getURLContent ;
273353exports . setLocaleStrings = setLocaleStrings ;
274354exports . getPhoenixBinaryVersion = getPhoenixBinaryVersion ;
@@ -278,5 +358,8 @@ exports.getEnvironmentVariable = getEnvironmentVariable;
278358exports . ESLintFile = ESLintFile ;
279359exports . openNativeTerminal = openNativeTerminal ;
280360exports . openInDefaultApp = openInDefaultApp ;
361+ exports . addDeviceLicense = addDeviceLicense ;
362+ exports . removeDeviceLicense = removeDeviceLicense ;
363+ exports . isLicensedDevice = isLicensedDevice ;
281364exports . _loadNodeExtensionModule = _loadNodeExtensionModule ;
282365exports . _npmInstallInFolder = _npmInstallInFolder ;
0 commit comments