@phystack/phyctl-phyos 4.5.64-dev → 5.0.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.
package/bin/index.js CHANGED
@@ -1,23 +1,27 @@
1
1
  #!/usr/bin/env node
2
- const { spawnSync } = require('child_process');
3
- const path = require('path');
2
+ const { spawnSync } = require("child_process");
3
+ const path = require("path");
4
4
 
5
- const BINARY = 'phyctl';
5
+ const BINARY = "phyctl";
6
6
  const key = `${process.platform}-${process.arch}`;
7
7
  const pkg = `@phystack/${BINARY}-${key}`;
8
8
 
9
9
  let binPath;
10
10
  try {
11
- binPath = path.join(path.dirname(require.resolve(`${pkg}/package.json`)), 'bin', BINARY);
11
+ binPath = path.join(
12
+ path.dirname(require.resolve(`${pkg}/package.json`)),
13
+ "bin",
14
+ BINARY,
15
+ );
12
16
  } catch {
13
17
  console.error(
14
18
  `Unsupported or missing platform package: ${pkg}\n` +
15
- `Platform: ${key}\n\n` +
16
- `Install manually: npm install ${pkg}\n` +
17
- `Or download: npx ${pkg}`
19
+ `Platform: ${key}\n\n` +
20
+ `Install manually: npm install ${pkg}\n` +
21
+ `Or download: bunx ${pkg}`,
18
22
  );
19
23
  process.exit(1);
20
24
  }
21
25
 
22
- const result = spawnSync(binPath, process.argv.slice(2), { stdio: 'inherit' });
26
+ const result = spawnSync(binPath, process.argv.slice(2), { stdio: "inherit" });
23
27
  process.exit(result.status ?? 1);
package/dist/index.js ADDED
@@ -0,0 +1,618 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const inquirer_1 = __importDefault(require("inquirer"));
7
+ const hub_client_1 = require("@phystack/hub-client");
8
+ const child_process_1 = require("child_process");
9
+ const fs_1 = __importDefault(require("fs"));
10
+ const path_1 = __importDefault(require("path"));
11
+ const readline_1 = __importDefault(require("readline"));
12
+ const wifi_dialog_1 = require("./dialog/network/wifi.dialog");
13
+ const lan_dialog_1 = require("./dialog/network/lan.dialog");
14
+ const proxy_dialog_1 = require("./dialog/network/proxy.dialog");
15
+ const chalk_1 = __importDefault(require("chalk"));
16
+ const commander_1 = require("commander");
17
+ const local_1 = require("./commands/local");
18
+ const program = new commander_1.Command();
19
+ let phyClientInstance;
20
+ let deviceStatus;
21
+ let inMenu = false;
22
+ setInterval(() => { }, 1000);
23
+ function getVersion() {
24
+ if (typeof __PKG_VERSION__ !== 'undefined') {
25
+ return __PKG_VERSION__;
26
+ }
27
+ const pkgPath = path_1.default.resolve(__dirname, '../package.json');
28
+ try {
29
+ const content = fs_1.default.readFileSync(pkgPath, 'utf-8');
30
+ const pkg = JSON.parse(content);
31
+ return pkg.version || 'N/A';
32
+ }
33
+ catch (error) {
34
+ console.error('Failed to read version from package.json:', error);
35
+ return 'N/A';
36
+ }
37
+ }
38
+ async function getGlobalDNSServers() {
39
+ return new Promise((resolve, reject) => {
40
+ (0, child_process_1.exec)('systemd-resolve --status', (error, stdout, _stderr) => {
41
+ if (error) {
42
+ return reject(error);
43
+ }
44
+ const lines = stdout.split('\n');
45
+ let fallbackLine = lines.find(line => line.trim().startsWith('Fallback DNS Servers:'));
46
+ let servers = [];
47
+ if (fallbackLine) {
48
+ fallbackLine = fallbackLine.replace('Fallback DNS Servers:', '').trim();
49
+ servers = fallbackLine.split(/\s+/).map(entry => entry.split('#')[0]).filter(entry => entry);
50
+ }
51
+ resolve(servers);
52
+ });
53
+ });
54
+ }
55
+ async function getGatewayForInterface(iface) {
56
+ return new Promise((resolve, _reject) => {
57
+ (0, child_process_1.exec)(`ip route show default dev ${iface}`, (error, stdout, _stderr) => {
58
+ if (error || !stdout) {
59
+ return resolve('N/A');
60
+ }
61
+ const match = stdout.match(/default via (\S+)/);
62
+ if (match) {
63
+ resolve(match[1]);
64
+ }
65
+ else {
66
+ resolve('N/A');
67
+ }
68
+ });
69
+ });
70
+ }
71
+ async function getDNSServersForInterface(iface) {
72
+ return new Promise((resolve) => {
73
+ (0, child_process_1.exec)('systemd-resolve --status', (error, stdout) => {
74
+ if (error || !stdout) {
75
+ return resolve([]);
76
+ }
77
+ const lines = stdout.split('\n');
78
+ let blockStarted = false;
79
+ let dnsServers = [];
80
+ for (let i = 0; i < lines.length; i++) {
81
+ const line = lines[i];
82
+ if (line.startsWith('Link') && line.includes(`(${iface})`)) {
83
+ blockStarted = true;
84
+ continue;
85
+ }
86
+ if (blockStarted) {
87
+ if (line.trim() === '' || line.startsWith('Link')) {
88
+ break;
89
+ }
90
+ if (line.trim().startsWith('DNS Servers:')) {
91
+ let serversLine = line.replace('DNS Servers:', '').trim();
92
+ dnsServers = serversLine.split(/\s+/).map(entry => entry.split('#')[0]).filter(entry => entry);
93
+ }
94
+ else if (dnsServers.length && line.startsWith(' ')) {
95
+ let additional = line.trim().split(/\s+/).map(entry => entry.split('#')[0]).filter(entry => entry);
96
+ dnsServers = dnsServers.concat(additional);
97
+ }
98
+ }
99
+ }
100
+ resolve(dnsServers);
101
+ });
102
+ });
103
+ }
104
+ async function main() {
105
+ program.version(getVersion());
106
+ const args = process.argv.slice(2);
107
+ await connectToPhyClientAndUpdateStatus();
108
+ const phyClient = await (0, hub_client_1.connectPhyClient)({ moduleName: 'phyctl' });
109
+ program.addCommand((0, local_1.setupLocalCommands)(phyClient));
110
+ if (args.length > 0) {
111
+ await program.parseAsync(process.argv);
112
+ return process.exit(0);
113
+ }
114
+ watchDeviceStatus();
115
+ handleUserInput();
116
+ }
117
+ async function connectToPhyClientAndUpdateStatus() {
118
+ console.log('Connecting to PhyClient...');
119
+ phyClientInstance = await (0, hub_client_1.connectPhyClient)({ instanceId: 'phyctl' });
120
+ if (phyClientInstance) {
121
+ try {
122
+ while (true) {
123
+ if (phyClientInstance.isConnected()) {
124
+ deviceStatus = await phyClientInstance.getDeviceStatus();
125
+ break;
126
+ }
127
+ console.error('Socket not connected to PhyClient');
128
+ await new Promise((resolve) => setTimeout(resolve, 5000));
129
+ }
130
+ }
131
+ catch (error) {
132
+ console.error('Failed to get status from PhyClient:', error);
133
+ }
134
+ }
135
+ else {
136
+ console.error('Failed to connect to PhyClient');
137
+ }
138
+ }
139
+ function watchDeviceStatus() {
140
+ setInterval(async () => {
141
+ if (!phyClientInstance || !phyClientInstance.isConnected() || inMenu) {
142
+ return;
143
+ }
144
+ try {
145
+ const newStatus = await phyClientInstance.getDeviceStatus();
146
+ if (JSON.stringify(newStatus) !== JSON.stringify(deviceStatus)) {
147
+ deviceStatus = newStatus;
148
+ displayInfo();
149
+ }
150
+ }
151
+ catch (error) {
152
+ console.error('Failed to refresh status from PhyClient:', error);
153
+ }
154
+ }, 5000);
155
+ displayInfo();
156
+ }
157
+ function displayInfo() {
158
+ console.clear();
159
+ console.log('=====================================');
160
+ console.log('| PhyOS Device Configuration |');
161
+ console.log('=====================================');
162
+ if (!phyClientInstance || !phyClientInstance.isConnected()) {
163
+ console.log('PhyDevice is not running or disconnected');
164
+ console.log('=====================================');
165
+ console.log('\nPress ENTER to open the menu (or press Ctrl+C to exit):');
166
+ return;
167
+ }
168
+ const { deviceId, deviceSerial, gridEnv, osVersion, socketConnected, socketAuthenticated, provisioningCode, displayName, } = deviceStatus || {};
169
+ if (socketConnected) {
170
+ console.log(`Device Name: ${displayName || 'N/A'}`);
171
+ }
172
+ console.log(`Device Serial: ${deviceSerial || 'N/A'}`);
173
+ console.log(`OS Version: ${osVersion || 'N/A'}`);
174
+ console.log(`PhyCTL Version: ${getVersion() || 'N/A'}`);
175
+ console.log(`Grid Environment: ${gridEnv || 'N/A'}`);
176
+ console.log(`Socket Connected: ${socketConnected || 'false'}`);
177
+ if (socketConnected) {
178
+ console.log(`Device ID: ${deviceId || 'N/A'}`);
179
+ console.log(`Socket Authenticated: ${socketAuthenticated || 'false'}`);
180
+ }
181
+ else {
182
+ console.log(`Provisioning Code: ${deviceId ? 'PROVISIONED' : provisioningCode || 'N/A'}`);
183
+ }
184
+ console.log('=====================================');
185
+ console.log('\nPress ENTER to open the menu (or press Ctrl+C to exit):');
186
+ }
187
+ async function showMenu() {
188
+ const menuChoices = [
189
+ { name: 'Shell (Open shell)', value: 'shell' },
190
+ { name: 'Networking (setup network)', value: 'network' },
191
+ { name: 'Exit menu', value: 'exit' },
192
+ new inquirer_1.default.Separator(),
193
+ { name: 'Advanced', value: 'advanced' },
194
+ { name: 'Reboot', value: 'reboot' },
195
+ new inquirer_1.default.Separator(),
196
+ ];
197
+ while (true) {
198
+ if (!phyClientInstance || !phyClientInstance.isConnected()) {
199
+ console.log('PhyDevice is not running or disconnected');
200
+ break;
201
+ }
202
+ const answers = await inquirer_1.default.prompt([
203
+ {
204
+ type: 'list',
205
+ name: 'menu',
206
+ message: 'Select an option:',
207
+ choices: [...menuChoices],
208
+ },
209
+ ]);
210
+ switch (answers.menu) {
211
+ case 'network':
212
+ await setupNetwork();
213
+ break;
214
+ case 'shell':
215
+ await openShell();
216
+ break;
217
+ case 'advanced':
218
+ await showAdvancedMenu();
219
+ break;
220
+ case 'reboot':
221
+ await rebootDevice();
222
+ break;
223
+ case 'exit':
224
+ displayInfo();
225
+ return;
226
+ }
227
+ }
228
+ }
229
+ async function setupNetwork() {
230
+ while (true) {
231
+ if (!phyClientInstance || !phyClientInstance.isConnected()) {
232
+ console.log('PhyDevice is not running or disconnected');
233
+ break;
234
+ }
235
+ const networkAnswers = await inquirer_1.default.prompt([
236
+ {
237
+ type: 'list',
238
+ name: 'network',
239
+ message: 'Select network type:',
240
+ choices: [
241
+ { name: 'Ethernet', value: 'ethernet' },
242
+ { name: 'WiFi', value: 'wifi' },
243
+ { name: 'View current network configuration', value: 'viewConfig' },
244
+ new inquirer_1.default.Separator(),
245
+ { name: 'Go back', value: 'back' },
246
+ new inquirer_1.default.Separator(),
247
+ ],
248
+ },
249
+ ]);
250
+ if (networkAnswers.network === 'back') {
251
+ break;
252
+ }
253
+ switch (networkAnswers.network) {
254
+ case 'wifi':
255
+ await (0, wifi_dialog_1.setupWifi)(phyClientInstance);
256
+ break;
257
+ case 'ethernet':
258
+ await (0, lan_dialog_1.setupEthernet)(phyClientInstance);
259
+ break;
260
+ case 'viewConfig':
261
+ await viewNetworkConfiguration(phyClientInstance);
262
+ break;
263
+ }
264
+ }
265
+ }
266
+ async function showAdvancedMenu() {
267
+ const advancedChoices = [
268
+ { name: 'Change Phystack environment', value: 'changeEnv' },
269
+ { name: 'Enable Developer Mode', value: 'enableDevMode' },
270
+ { name: 'CA (add certificate authority trust)', value: 'ca' },
271
+ { name: 'Set Custom Proxy', value: 'proxy' },
272
+ new inquirer_1.default.Separator(),
273
+ { name: 'Go back', value: 'back' },
274
+ ];
275
+ while (true) {
276
+ if (!phyClientInstance || !phyClientInstance.isConnected()) {
277
+ console.log('PhyDevice is not running or disconnected');
278
+ break;
279
+ }
280
+ const advancedAnswers = await inquirer_1.default.prompt([
281
+ {
282
+ type: 'list',
283
+ name: 'advanced',
284
+ message: 'Select an advanced option:',
285
+ choices: [...advancedChoices],
286
+ },
287
+ ]);
288
+ if (advancedAnswers.advanced === 'back') {
289
+ break;
290
+ }
291
+ switch (advancedAnswers.advanced) {
292
+ case 'proxy':
293
+ await (0, proxy_dialog_1.setupProxy)(phyClientInstance);
294
+ break;
295
+ case 'ca':
296
+ await addCertificateAuthority();
297
+ break;
298
+ case 'enableDevMode':
299
+ await enableDeveloperMode();
300
+ break;
301
+ case 'changeEnv':
302
+ await changePhygridEnvironment();
303
+ break;
304
+ }
305
+ }
306
+ }
307
+ async function changePhygridEnvironment() {
308
+ const envChoices = [
309
+ { name: 'LOCAL', value: 'LOCAL' },
310
+ { name: 'DEV', value: 'DEV' },
311
+ { name: 'QA', value: 'QA' },
312
+ { name: 'PROD', value: 'PROD' },
313
+ new inquirer_1.default.Separator(),
314
+ { name: 'Go back', value: 'back' },
315
+ ];
316
+ const envAnswers = await inquirer_1.default.prompt([
317
+ {
318
+ type: 'list',
319
+ name: 'env',
320
+ message: 'Select Phystack environment:',
321
+ choices: envChoices,
322
+ },
323
+ ]);
324
+ if (envAnswers.env === 'back') {
325
+ return;
326
+ }
327
+ const confirmAnswer = await inquirer_1.default.prompt([
328
+ {
329
+ type: 'confirm',
330
+ name: 'confirm',
331
+ message: `WARNING: Changing the environment to ${envAnswers.env} will:\n` +
332
+ '1. Delete all device settings\n' +
333
+ '2. Require device reprovisioning\n' +
334
+ '3. Restart the device services\n' +
335
+ '\nAre you sure you want to continue?',
336
+ default: false,
337
+ },
338
+ ]);
339
+ if (!confirmAnswer.confirm) {
340
+ console.log('Environment change cancelled');
341
+ return;
342
+ }
343
+ console.log(`Setting Phystack environment to ${envAnswers.env}...`);
344
+ await new Promise((resolve) => {
345
+ phyClientInstance.emit('setEnv', {
346
+ data: {
347
+ env: envAnswers.env
348
+ }
349
+ }, (response) => {
350
+ if (response.status === 'success') {
351
+ console.log(`Phystack environment set to ${envAnswers.env}`);
352
+ console.log('Device will now unprovision...');
353
+ setTimeout(() => process.exit(0), 1000);
354
+ }
355
+ else {
356
+ console.error('Failed to set environment:', response.message);
357
+ }
358
+ resolve();
359
+ });
360
+ });
361
+ }
362
+ async function enableDeveloperMode() {
363
+ try {
364
+ console.log(chalk_1.default.yellow('Enabling developer mode...'));
365
+ const authMethod = await inquirer_1.default.prompt([
366
+ {
367
+ type: 'list',
368
+ name: 'method',
369
+ message: 'Choose authentication method:',
370
+ choices: [
371
+ { name: 'Set password', value: 'password' },
372
+ { name: 'Provide SSH public key', value: 'ssh' },
373
+ { name: 'Use default password (123)', value: 'default' },
374
+ ],
375
+ },
376
+ ]);
377
+ let payload = {};
378
+ if (authMethod.method === 'password') {
379
+ const passwordPrompt = await inquirer_1.default.prompt([
380
+ {
381
+ type: 'password',
382
+ name: 'password',
383
+ message: 'Enter new password for dev user:',
384
+ mask: '*',
385
+ validate: (input) => {
386
+ if (input.length < 6) {
387
+ return 'Password must be at least 6 characters long';
388
+ }
389
+ return true;
390
+ },
391
+ },
392
+ {
393
+ type: 'password',
394
+ name: 'confirmPassword',
395
+ message: 'Confirm password:',
396
+ mask: '*',
397
+ validate: (input, answers) => {
398
+ if (input !== answers.password) {
399
+ return 'Passwords do not match';
400
+ }
401
+ return true;
402
+ },
403
+ },
404
+ ]);
405
+ payload = {
406
+ authType: 'password',
407
+ password: passwordPrompt.password,
408
+ };
409
+ }
410
+ else if (authMethod.method === 'ssh') {
411
+ const sshPrompt = await inquirer_1.default.prompt([
412
+ {
413
+ type: 'input',
414
+ name: 'sshKey',
415
+ message: 'Enter your SSH public key:',
416
+ validate: (input) => {
417
+ if (!input.trim().startsWith('ssh-')) {
418
+ return 'Invalid SSH public key format';
419
+ }
420
+ return true;
421
+ },
422
+ },
423
+ ]);
424
+ payload = {
425
+ authType: 'ssh',
426
+ sshKey: sshPrompt.sshKey,
427
+ };
428
+ }
429
+ else {
430
+ payload = {
431
+ authType: 'password',
432
+ password: '123',
433
+ };
434
+ }
435
+ await phyClientInstance.emit('devdevice', payload);
436
+ console.log(chalk_1.default.green('Developer mode enabled successfully.'));
437
+ if (authMethod.method === 'default') {
438
+ console.log(chalk_1.default.yellow('\nDev user credentials:'));
439
+ console.log(chalk_1.default.yellow('Username: dev'));
440
+ console.log(chalk_1.default.yellow('Password: 123'));
441
+ }
442
+ else if (authMethod.method === 'password') {
443
+ console.log(chalk_1.default.yellow('\nDev user credentials:'));
444
+ console.log(chalk_1.default.yellow('Username: dev'));
445
+ console.log(chalk_1.default.yellow('Password: <your chosen password>'));
446
+ }
447
+ else {
448
+ console.log(chalk_1.default.yellow('\nSSH key has been configured for the dev user'));
449
+ }
450
+ }
451
+ catch (error) {
452
+ console.error(chalk_1.default.red('Failed to enable developer mode:'), error);
453
+ }
454
+ }
455
+ async function addCertificateAuthority() {
456
+ const urlAnswer = await inquirer_1.default.prompt({
457
+ type: 'input',
458
+ name: 'certUrl',
459
+ message: 'Enter URL of the CA certificate:',
460
+ validate: (input) => {
461
+ try {
462
+ new URL(input);
463
+ return true;
464
+ }
465
+ catch {
466
+ return 'Invalid URL';
467
+ }
468
+ },
469
+ });
470
+ const proxyAnswer = await inquirer_1.default.prompt({
471
+ type: 'confirm',
472
+ name: 'useProxy',
473
+ message: 'Do you need to use a proxy to download the certificate?',
474
+ default: false,
475
+ });
476
+ let proxyDetails = {};
477
+ if (proxyAnswer.useProxy) {
478
+ const proxyPrompts = await inquirer_1.default.prompt([
479
+ {
480
+ type: 'input',
481
+ name: 'hostname',
482
+ message: 'Enter Proxy Hostname for Certificate Download:',
483
+ validate: (input) => input.trim() !== '' || 'Hostname cannot be empty',
484
+ },
485
+ {
486
+ type: 'input',
487
+ name: 'port',
488
+ message: 'Enter Proxy Port for Certificate Download:',
489
+ validate: (input) => !isNaN(Number(input)) && input.trim() !== '' || 'Port must be a valid number',
490
+ },
491
+ {
492
+ type: 'input',
493
+ name: 'username',
494
+ message: 'Enter Proxy Username for Certificate Download (optional):',
495
+ },
496
+ {
497
+ type: 'password',
498
+ name: 'password',
499
+ message: 'Enter Proxy Password for Certificate Download (if username provided):',
500
+ mask: '*',
501
+ when: (answers) => answers.username && answers.username.trim() !== '',
502
+ }
503
+ ]);
504
+ proxyDetails = {
505
+ hostname: proxyPrompts.hostname,
506
+ port: Number(proxyPrompts.port),
507
+ username: proxyPrompts.username,
508
+ password: proxyPrompts.password,
509
+ };
510
+ }
511
+ phyClientInstance.emit('setCACertificate', {
512
+ data: {
513
+ certUrl: urlAnswer.certUrl,
514
+ proxy: proxyDetails,
515
+ },
516
+ }, () => {
517
+ console.log('Certificate installation completed.');
518
+ });
519
+ }
520
+ async function openShell() {
521
+ console.log('Opening shell...');
522
+ const shell = (0, child_process_1.spawn)('bash', ['-c', 'sudo su -'], {
523
+ stdio: 'inherit',
524
+ });
525
+ await new Promise((resolve) => {
526
+ shell.on('exit', (code) => {
527
+ console.log(`Shell exited with code ${code}`);
528
+ resolve();
529
+ });
530
+ });
531
+ }
532
+ async function rebootDevice() {
533
+ const confirmAnswers = await inquirer_1.default.prompt([
534
+ {
535
+ type: 'input',
536
+ name: 'confirm',
537
+ message: 'Type "yes" to confirm reboot:',
538
+ },
539
+ ]);
540
+ if (confirmAnswers.confirm.toLowerCase() === 'yes') {
541
+ console.log('Rebooting device...');
542
+ phyClientInstance.emit('reboot', async () => {
543
+ console.log('Reboot command sent');
544
+ });
545
+ }
546
+ else {
547
+ console.log('Reboot cancelled.');
548
+ }
549
+ }
550
+ function handleUserInput() {
551
+ function setupReadline() {
552
+ const rl = readline_1.default.createInterface({
553
+ input: process.stdin,
554
+ output: process.stdout,
555
+ });
556
+ rl.on('line', async () => {
557
+ if (!inMenu) {
558
+ inMenu = true;
559
+ console.clear();
560
+ await showMenu();
561
+ inMenu = false;
562
+ displayInfo();
563
+ rl.close();
564
+ setupReadline();
565
+ }
566
+ });
567
+ }
568
+ setupReadline();
569
+ }
570
+ async function viewNetworkConfiguration(phyClientInstance) {
571
+ if (!phyClientInstance || !phyClientInstance.isConnected()) {
572
+ console.log('PhyDevice is not running or disconnected');
573
+ return;
574
+ }
575
+ const deviceNetworksResponse = await phyClientInstance.getDeviceNetworks();
576
+ const networks = deviceNetworksResponse.data;
577
+ console.clear();
578
+ console.log('=== Current Network Configuration ===');
579
+ if (networks.networkInterfaces && networks.networkInterfaces.length > 0) {
580
+ for (const ni of networks.networkInterfaces) {
581
+ const gateway = await getGatewayForInterface(ni.iface);
582
+ const dnsServers = await getDNSServersForInterface(ni.iface);
583
+ console.log(`Interface: ${ni.ifaceName} (${ni.iface})`);
584
+ console.log(` Mode: ${ni.dhcp ? 'DHCP' : 'Static'}`);
585
+ console.log(` IP Address: ${ni.ip4 || 'N/A'}`);
586
+ console.log(` Netmask: ${ni.ip4subnet || 'N/A'}`);
587
+ console.log(` Gateway: ${gateway}`);
588
+ console.log(` Default Route: ${ni.default ? 'Yes' : 'No'}`);
589
+ console.log(` DNS Server(s): ${dnsServers.length > 0 ? dnsServers.join(', ') : 'N/A'}`);
590
+ console.log('');
591
+ }
592
+ }
593
+ else {
594
+ console.log('No network interfaces found.');
595
+ }
596
+ const defaultInterface = networks.networkInterfaces.find((ni) => ni.default);
597
+ if (defaultInterface) {
598
+ console.log('Global Default Route:');
599
+ console.log(` Interface: ${defaultInterface.ifaceName} (${defaultInterface.iface})`);
600
+ console.log(` IP Address: ${defaultInterface.ip4 || 'N/A'}`);
601
+ }
602
+ else {
603
+ console.log('No default route found.');
604
+ }
605
+ try {
606
+ const globalDNS = await getGlobalDNSServers();
607
+ console.log(`Global DNS Server(s): ${globalDNS.length > 0 ? globalDNS.join(', ') : 'N/A'}`);
608
+ }
609
+ catch (error) {
610
+ console.log('Global DNS Server: N/A');
611
+ }
612
+ console.log('=====================================');
613
+ await inquirer_1.default.prompt([{ type: 'input', name: 'continue', message: 'Press ENTER to continue' }]);
614
+ }
615
+ main().catch((error) => {
616
+ console.error('An error occurred:', error);
617
+ });
618
+ //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,39 +1,32 @@
1
1
  {
2
2
  "name": "@phystack/phyctl-phyos",
3
- "version": "4.5.64-dev",
4
- "exports": "./index.js",
5
- "main": "index.js",
3
+ "version": "5.0.1",
4
+ "description": "PhyOS device management CLI",
5
+ "main": "dist/index.js",
6
+ "license": "UNLICENSED",
6
7
  "publishConfig": {
7
8
  "access": "public"
8
9
  },
9
- "compilerOptions": {
10
- "module": "ESNext",
11
- "moduleResolution": "node"
12
- },
13
- "scripts": {
14
- "test": "echo \"Error: no test specified\" && exit 1",
15
- "build": "rimraf dist && tsc",
16
- "build:binary": "bash build-binary.sh",
17
- "start": "yarn build && node --inspect --enable-source-maps dist/index",
18
- "dev": "NODE_ENV=development yarn start"
19
- },
20
10
  "bin": {
21
11
  "phyctl": "./bin/index.js"
22
12
  },
23
- "engines": {
24
- "node": ">=20.0.0"
25
- },
26
13
  "files": [
27
14
  "bin/**/*"
28
15
  ],
29
- "author": "PhyStack.com",
30
- "license": "MIT",
31
- "description": "",
32
- "optionalDependencies": {
33
- "@phystack/phyctl-darwin-arm64": "4.5.64-dev",
34
- "@phystack/phyctl-darwin-x64": "4.5.64-dev",
35
- "@phystack/phyctl-linux-arm64": "4.5.64-dev",
36
- "@phystack/phyctl-linux-x64": "4.5.64-dev"
16
+ "engines": {
17
+ "node": ">=20.0.0"
37
18
  },
38
- "gitHead": "e29cb3adee4f709ac3f5f2b3de62fe7f14a7a7b3"
19
+ "scripts": {
20
+ "build": "tsc",
21
+ "build:binary": "bash build-binary.sh",
22
+ "dev": "bun --watch src/index.ts",
23
+ "lint": "tsc --noEmit",
24
+ "test:ci": "echo 'no tests' && exit 0"
25
+ },
26
+ "optionalDependencies": {
27
+ "@phystack/phyctl-darwin-arm64": "5.0.1",
28
+ "@phystack/phyctl-darwin-x64": "5.0.1",
29
+ "@phystack/phyctl-linux-arm64": "5.0.1",
30
+ "@phystack/phyctl-linux-x64": "5.0.1"
31
+ }
39
32
  }
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2025 PhyStack.com
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
package/README.md DELETED
@@ -1,62 +0,0 @@
1
- # phyctl
2
-
3
- PhyStack control plane CLI for managing PhyOS devices. Compiled as a standalone Bun binary.
4
-
5
- ## Installation
6
-
7
- ### Via npm (recommended)
8
-
9
- ```bash
10
- npm install -g @phystack/phyctl-phyos
11
- ```
12
-
13
- npm automatically downloads the correct binary for your platform.
14
-
15
- ### Direct binary download (no npm required)
16
-
17
- ```bash
18
- VERSION=4.5.62-dev # replace with desired version
19
-
20
- # Linux ARM64
21
- sudo curl -sL "https://registry.npmjs.org/@phystack/phyctl-linux-arm64/-/phyctl-linux-arm64-${VERSION}.tgz" | sudo tar xz --strip-components=2 -C /usr/bin package/bin/phyctl
22
-
23
- # Linux x86_64
24
- sudo curl -sL "https://registry.npmjs.org/@phystack/phyctl-linux-x64/-/phyctl-linux-x64-${VERSION}.tgz" | sudo tar xz --strip-components=2 -C /usr/bin package/bin/phyctl
25
-
26
- # macOS Apple Silicon
27
- curl -sL "https://registry.npmjs.org/@phystack/phyctl-darwin-arm64/-/phyctl-darwin-arm64-${VERSION}.tgz" | tar xz --strip-components=2 -C /usr/local/bin package/bin/phyctl
28
-
29
- # macOS Intel
30
- curl -sL "https://registry.npmjs.org/@phystack/phyctl-darwin-x64/-/phyctl-darwin-x64-${VERSION}.tgz" | tar xz --strip-components=2 -C /usr/local/bin package/bin/phyctl
31
- ```
32
-
33
- To check available versions:
34
-
35
- ```bash
36
- npm view @phystack/phyctl-linux-arm64 versions --json
37
- ```
38
-
39
- ## Supported platforms
40
-
41
- | Platform | Architecture | npm package |
42
- |----------|-------------|-------------|
43
- | Linux | ARM64 | `@phystack/phyctl-linux-arm64` |
44
- | Linux | x86_64 | `@phystack/phyctl-linux-x64` |
45
- | macOS | ARM64 | `@phystack/phyctl-darwin-arm64` |
46
- | macOS | x86_64 | `@phystack/phyctl-darwin-x64` |
47
-
48
- ## Building from source
49
-
50
- ```bash
51
- yarn build:binary # compile for current platform
52
- ```
53
-
54
- Or use the central build script for cross-compilation:
55
-
56
- ```bash
57
- # from repo root
58
- yarn build:binaries # current platform
59
- yarn build:binaries:cross # all platforms
60
- ```
61
-
62
- Output: `dist-binaries/<platform>/phyctl`