agendex-cli 0.6.0 → 0.7.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.
- package/dist/cli.js +244 -192
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2492,6 +2492,227 @@ function startWatching(onChange) {
|
|
|
2492
2492
|
watchDir(dir, (f) => adapter.matches(f), onChange);
|
|
2493
2493
|
}
|
|
2494
2494
|
}
|
|
2495
|
+
// src/api.ts
|
|
2496
|
+
import { request as httpRequest } from "node:http";
|
|
2497
|
+
import { request as httpsRequest } from "node:https";
|
|
2498
|
+
import { hostname as osHostname } from "node:os";
|
|
2499
|
+
|
|
2500
|
+
// src/pid.ts
|
|
2501
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync2, readFileSync as readFileSync3, unlinkSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
2502
|
+
import { homedir as homedir9, hostname } from "node:os";
|
|
2503
|
+
import { dirname, join as join10 } from "node:path";
|
|
2504
|
+
var pidPath = join10(homedir9(), ".agendex", "daemon.pid");
|
|
2505
|
+
function writePid() {
|
|
2506
|
+
mkdirSync2(dirname(pidPath), { recursive: true });
|
|
2507
|
+
const info = {
|
|
2508
|
+
pid: process.pid,
|
|
2509
|
+
startedAtMs: Date.now(),
|
|
2510
|
+
hostname: hostname()
|
|
2511
|
+
};
|
|
2512
|
+
writeFileSync2(pidPath, JSON.stringify(info));
|
|
2513
|
+
}
|
|
2514
|
+
function readPidInfo() {
|
|
2515
|
+
if (!existsSync6(pidPath))
|
|
2516
|
+
return null;
|
|
2517
|
+
const raw = readFileSync3(pidPath, "utf-8").trim();
|
|
2518
|
+
const asNumber = Number(raw);
|
|
2519
|
+
if (Number.isFinite(asNumber) && asNumber > 0 && !raw.startsWith("{")) {
|
|
2520
|
+
return { pid: asNumber };
|
|
2521
|
+
}
|
|
2522
|
+
try {
|
|
2523
|
+
const parsed = JSON.parse(raw);
|
|
2524
|
+
if (Number.isFinite(parsed.pid) && parsed.pid > 0)
|
|
2525
|
+
return parsed;
|
|
2526
|
+
} catch {}
|
|
2527
|
+
return null;
|
|
2528
|
+
}
|
|
2529
|
+
function readPid() {
|
|
2530
|
+
return readPidInfo()?.pid ?? null;
|
|
2531
|
+
}
|
|
2532
|
+
function removePid() {
|
|
2533
|
+
try {
|
|
2534
|
+
unlinkSync(pidPath);
|
|
2535
|
+
} catch {}
|
|
2536
|
+
}
|
|
2537
|
+
function isRunning(pid) {
|
|
2538
|
+
try {
|
|
2539
|
+
process.kill(pid, 0);
|
|
2540
|
+
return true;
|
|
2541
|
+
} catch {
|
|
2542
|
+
return false;
|
|
2543
|
+
}
|
|
2544
|
+
}
|
|
2545
|
+
|
|
2546
|
+
// src/api.ts
|
|
2547
|
+
var cachedDeviceId;
|
|
2548
|
+
function getCloudConfig() {
|
|
2549
|
+
const config = loadConfig();
|
|
2550
|
+
if (!config?.cloudToken)
|
|
2551
|
+
throw new Error("Not logged in. Run `agendex login` first.");
|
|
2552
|
+
if (!config.convexUrl)
|
|
2553
|
+
throw new Error("No Convex URL configured. Run `agendex login` first.");
|
|
2554
|
+
return { token: config.cloudToken, convexUrl: config.convexUrl };
|
|
2555
|
+
}
|
|
2556
|
+
async function syncPlan(plan) {
|
|
2557
|
+
const { token, convexUrl } = getCloudConfig();
|
|
2558
|
+
const url = `${convexUrl}/api/cli/sync`;
|
|
2559
|
+
let activeToken = token;
|
|
2560
|
+
let res = await requestText(url, {
|
|
2561
|
+
method: "POST",
|
|
2562
|
+
headers: {
|
|
2563
|
+
Authorization: `Bearer ${activeToken}`,
|
|
2564
|
+
Connection: "close",
|
|
2565
|
+
"Content-Type": "application/json"
|
|
2566
|
+
},
|
|
2567
|
+
body: JSON.stringify(plan)
|
|
2568
|
+
});
|
|
2569
|
+
if (res.status === 401) {
|
|
2570
|
+
const refreshed = await refreshStoredToken(activeToken, convexUrl);
|
|
2571
|
+
if (refreshed) {
|
|
2572
|
+
activeToken = refreshed;
|
|
2573
|
+
res = await requestText(url, {
|
|
2574
|
+
method: "POST",
|
|
2575
|
+
headers: {
|
|
2576
|
+
Authorization: `Bearer ${activeToken}`,
|
|
2577
|
+
Connection: "close",
|
|
2578
|
+
"Content-Type": "application/json"
|
|
2579
|
+
},
|
|
2580
|
+
body: JSON.stringify(plan)
|
|
2581
|
+
});
|
|
2582
|
+
}
|
|
2583
|
+
}
|
|
2584
|
+
if (res.status < 200 || res.status >= 300) {
|
|
2585
|
+
return { ok: false, error: `${res.status}: ${res.body}` };
|
|
2586
|
+
}
|
|
2587
|
+
return { ok: true };
|
|
2588
|
+
}
|
|
2589
|
+
async function refreshStoredToken(currentToken, convexUrl) {
|
|
2590
|
+
const refreshed = await refreshToken(currentToken, convexUrl);
|
|
2591
|
+
if (!refreshed)
|
|
2592
|
+
return null;
|
|
2593
|
+
const config = loadConfig();
|
|
2594
|
+
if (config) {
|
|
2595
|
+
saveConfig({ ...config, cloudToken: refreshed.token });
|
|
2596
|
+
}
|
|
2597
|
+
return refreshed.token;
|
|
2598
|
+
}
|
|
2599
|
+
async function sendHeartbeat() {
|
|
2600
|
+
try {
|
|
2601
|
+
const { token, convexUrl } = getCloudConfig();
|
|
2602
|
+
const pidInfo = readPidInfo();
|
|
2603
|
+
cachedDeviceId ??= loadOrCreateDeviceId();
|
|
2604
|
+
const heartbeatBody = JSON.stringify({
|
|
2605
|
+
deviceId: cachedDeviceId,
|
|
2606
|
+
hostname: pidInfo?.hostname ?? osHostname(),
|
|
2607
|
+
startedAtMs: pidInfo?.startedAtMs,
|
|
2608
|
+
pid: pidInfo?.pid
|
|
2609
|
+
});
|
|
2610
|
+
let activeToken = token;
|
|
2611
|
+
let res = await requestText(`${convexUrl}/api/cli/heartbeat`, {
|
|
2612
|
+
method: "POST",
|
|
2613
|
+
headers: {
|
|
2614
|
+
Authorization: `Bearer ${activeToken}`,
|
|
2615
|
+
Connection: "close",
|
|
2616
|
+
"Content-Type": "application/json"
|
|
2617
|
+
},
|
|
2618
|
+
body: heartbeatBody
|
|
2619
|
+
});
|
|
2620
|
+
if (res.status === 401) {
|
|
2621
|
+
const refreshedToken = await refreshStoredToken(activeToken, convexUrl);
|
|
2622
|
+
if (!refreshedToken)
|
|
2623
|
+
return;
|
|
2624
|
+
activeToken = refreshedToken;
|
|
2625
|
+
res = await requestText(`${convexUrl}/api/cli/heartbeat`, {
|
|
2626
|
+
method: "POST",
|
|
2627
|
+
headers: {
|
|
2628
|
+
Authorization: `Bearer ${activeToken}`,
|
|
2629
|
+
Connection: "close",
|
|
2630
|
+
"Content-Type": "application/json"
|
|
2631
|
+
},
|
|
2632
|
+
body: heartbeatBody
|
|
2633
|
+
});
|
|
2634
|
+
}
|
|
2635
|
+
if (res.status === 401) {
|
|
2636
|
+
return;
|
|
2637
|
+
}
|
|
2638
|
+
} catch {}
|
|
2639
|
+
}
|
|
2640
|
+
async function refreshToken(currentToken, convexUrl) {
|
|
2641
|
+
const res = await requestText(`${convexUrl}/api/cli/refresh`, {
|
|
2642
|
+
method: "POST",
|
|
2643
|
+
headers: {
|
|
2644
|
+
Authorization: `Bearer ${currentToken}`,
|
|
2645
|
+
Connection: "close",
|
|
2646
|
+
"Content-Type": "application/json"
|
|
2647
|
+
}
|
|
2648
|
+
});
|
|
2649
|
+
if (res.status < 200 || res.status >= 300)
|
|
2650
|
+
return null;
|
|
2651
|
+
const body = JSON.parse(res.body);
|
|
2652
|
+
if (!body.token)
|
|
2653
|
+
return null;
|
|
2654
|
+
return { token: body.token, expiresAt: body.expiresAt ?? 0 };
|
|
2655
|
+
}
|
|
2656
|
+
function requestText(urlString, options) {
|
|
2657
|
+
const url = new URL(urlString);
|
|
2658
|
+
const request = url.protocol === "https:" ? httpsRequest : httpRequest;
|
|
2659
|
+
return new Promise((resolve4, reject) => {
|
|
2660
|
+
const req = request(url, {
|
|
2661
|
+
agent: false,
|
|
2662
|
+
headers: options.headers,
|
|
2663
|
+
method: options.method
|
|
2664
|
+
}, (res) => {
|
|
2665
|
+
let body = "";
|
|
2666
|
+
res.setEncoding("utf8");
|
|
2667
|
+
res.on("data", (chunk) => {
|
|
2668
|
+
body += chunk;
|
|
2669
|
+
});
|
|
2670
|
+
res.on("end", () => {
|
|
2671
|
+
resolve4({
|
|
2672
|
+
status: res.statusCode ?? 0,
|
|
2673
|
+
body
|
|
2674
|
+
});
|
|
2675
|
+
});
|
|
2676
|
+
res.on("error", reject);
|
|
2677
|
+
});
|
|
2678
|
+
req.on("error", reject);
|
|
2679
|
+
if (options.body) {
|
|
2680
|
+
req.write(options.body);
|
|
2681
|
+
}
|
|
2682
|
+
req.end();
|
|
2683
|
+
});
|
|
2684
|
+
}
|
|
2685
|
+
async function fetchDevices() {
|
|
2686
|
+
const { token, convexUrl } = getCloudConfig();
|
|
2687
|
+
const url = `${convexUrl}/api/cli/devices`;
|
|
2688
|
+
let activeToken = token;
|
|
2689
|
+
let res = await requestText(url, {
|
|
2690
|
+
method: "GET",
|
|
2691
|
+
headers: {
|
|
2692
|
+
Authorization: `Bearer ${activeToken}`,
|
|
2693
|
+
Connection: "close"
|
|
2694
|
+
}
|
|
2695
|
+
});
|
|
2696
|
+
if (res.status === 401) {
|
|
2697
|
+
const refreshed = await refreshStoredToken(activeToken, convexUrl);
|
|
2698
|
+
if (refreshed) {
|
|
2699
|
+
activeToken = refreshed;
|
|
2700
|
+
res = await requestText(url, {
|
|
2701
|
+
method: "GET",
|
|
2702
|
+
headers: {
|
|
2703
|
+
Authorization: `Bearer ${activeToken}`,
|
|
2704
|
+
Connection: "close"
|
|
2705
|
+
}
|
|
2706
|
+
});
|
|
2707
|
+
}
|
|
2708
|
+
}
|
|
2709
|
+
if (res.status < 200 || res.status >= 300) {
|
|
2710
|
+
return [];
|
|
2711
|
+
}
|
|
2712
|
+
const body = JSON.parse(res.body);
|
|
2713
|
+
return body.devices ?? [];
|
|
2714
|
+
}
|
|
2715
|
+
|
|
2495
2716
|
// src/auth.ts
|
|
2496
2717
|
import { spawn } from "node:child_process";
|
|
2497
2718
|
import { createServer } from "node:http";
|
|
@@ -2690,197 +2911,6 @@ function spawnBrowser(command, args, options = {}) {
|
|
|
2690
2911
|
import { spawn as spawn2 } from "node:child_process";
|
|
2691
2912
|
import { resolve as resolve4 } from "node:path";
|
|
2692
2913
|
import { fileURLToPath } from "node:url";
|
|
2693
|
-
|
|
2694
|
-
// src/api.ts
|
|
2695
|
-
import { request as httpRequest } from "node:http";
|
|
2696
|
-
import { request as httpsRequest } from "node:https";
|
|
2697
|
-
import { hostname as osHostname } from "node:os";
|
|
2698
|
-
|
|
2699
|
-
// src/pid.ts
|
|
2700
|
-
import { existsSync as existsSync6, mkdirSync as mkdirSync2, readFileSync as readFileSync3, unlinkSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
2701
|
-
import { homedir as homedir9, hostname } from "node:os";
|
|
2702
|
-
import { dirname, join as join10 } from "node:path";
|
|
2703
|
-
var pidPath = join10(homedir9(), ".agendex", "daemon.pid");
|
|
2704
|
-
function writePid() {
|
|
2705
|
-
mkdirSync2(dirname(pidPath), { recursive: true });
|
|
2706
|
-
const info = {
|
|
2707
|
-
pid: process.pid,
|
|
2708
|
-
startedAtMs: Date.now(),
|
|
2709
|
-
hostname: hostname()
|
|
2710
|
-
};
|
|
2711
|
-
writeFileSync2(pidPath, JSON.stringify(info));
|
|
2712
|
-
}
|
|
2713
|
-
function readPidInfo() {
|
|
2714
|
-
if (!existsSync6(pidPath))
|
|
2715
|
-
return null;
|
|
2716
|
-
const raw = readFileSync3(pidPath, "utf-8").trim();
|
|
2717
|
-
const asNumber = Number(raw);
|
|
2718
|
-
if (Number.isFinite(asNumber) && asNumber > 0 && !raw.startsWith("{")) {
|
|
2719
|
-
return { pid: asNumber };
|
|
2720
|
-
}
|
|
2721
|
-
try {
|
|
2722
|
-
const parsed = JSON.parse(raw);
|
|
2723
|
-
if (Number.isFinite(parsed.pid) && parsed.pid > 0)
|
|
2724
|
-
return parsed;
|
|
2725
|
-
} catch {}
|
|
2726
|
-
return null;
|
|
2727
|
-
}
|
|
2728
|
-
function readPid() {
|
|
2729
|
-
return readPidInfo()?.pid ?? null;
|
|
2730
|
-
}
|
|
2731
|
-
function removePid() {
|
|
2732
|
-
try {
|
|
2733
|
-
unlinkSync(pidPath);
|
|
2734
|
-
} catch {}
|
|
2735
|
-
}
|
|
2736
|
-
function isRunning(pid) {
|
|
2737
|
-
try {
|
|
2738
|
-
process.kill(pid, 0);
|
|
2739
|
-
return true;
|
|
2740
|
-
} catch {
|
|
2741
|
-
return false;
|
|
2742
|
-
}
|
|
2743
|
-
}
|
|
2744
|
-
|
|
2745
|
-
// src/api.ts
|
|
2746
|
-
var cachedDeviceId;
|
|
2747
|
-
function getCloudConfig() {
|
|
2748
|
-
const config = loadConfig();
|
|
2749
|
-
if (!config?.cloudToken)
|
|
2750
|
-
throw new Error("Not logged in. Run `agendex login` first.");
|
|
2751
|
-
if (!config.convexUrl)
|
|
2752
|
-
throw new Error("No Convex URL configured. Run `agendex login` first.");
|
|
2753
|
-
return { token: config.cloudToken, convexUrl: config.convexUrl };
|
|
2754
|
-
}
|
|
2755
|
-
async function syncPlan(plan) {
|
|
2756
|
-
const { token, convexUrl } = getCloudConfig();
|
|
2757
|
-
const url = `${convexUrl}/api/cli/sync`;
|
|
2758
|
-
let activeToken = token;
|
|
2759
|
-
let res = await requestText(url, {
|
|
2760
|
-
method: "POST",
|
|
2761
|
-
headers: {
|
|
2762
|
-
Authorization: `Bearer ${activeToken}`,
|
|
2763
|
-
Connection: "close",
|
|
2764
|
-
"Content-Type": "application/json"
|
|
2765
|
-
},
|
|
2766
|
-
body: JSON.stringify(plan)
|
|
2767
|
-
});
|
|
2768
|
-
if (res.status === 401) {
|
|
2769
|
-
const refreshed = await refreshStoredToken(activeToken, convexUrl);
|
|
2770
|
-
if (refreshed) {
|
|
2771
|
-
activeToken = refreshed;
|
|
2772
|
-
res = await requestText(url, {
|
|
2773
|
-
method: "POST",
|
|
2774
|
-
headers: {
|
|
2775
|
-
Authorization: `Bearer ${activeToken}`,
|
|
2776
|
-
Connection: "close",
|
|
2777
|
-
"Content-Type": "application/json"
|
|
2778
|
-
},
|
|
2779
|
-
body: JSON.stringify(plan)
|
|
2780
|
-
});
|
|
2781
|
-
}
|
|
2782
|
-
}
|
|
2783
|
-
if (res.status < 200 || res.status >= 300) {
|
|
2784
|
-
return { ok: false, error: `${res.status}: ${res.body}` };
|
|
2785
|
-
}
|
|
2786
|
-
return { ok: true };
|
|
2787
|
-
}
|
|
2788
|
-
async function refreshStoredToken(currentToken, convexUrl) {
|
|
2789
|
-
const refreshed = await refreshToken(currentToken, convexUrl);
|
|
2790
|
-
if (!refreshed)
|
|
2791
|
-
return null;
|
|
2792
|
-
const config = loadConfig();
|
|
2793
|
-
if (config) {
|
|
2794
|
-
saveConfig({ ...config, cloudToken: refreshed.token });
|
|
2795
|
-
}
|
|
2796
|
-
return refreshed.token;
|
|
2797
|
-
}
|
|
2798
|
-
async function sendHeartbeat() {
|
|
2799
|
-
try {
|
|
2800
|
-
const { token, convexUrl } = getCloudConfig();
|
|
2801
|
-
const pidInfo = readPidInfo();
|
|
2802
|
-
const heartbeatBody = JSON.stringify({
|
|
2803
|
-
deviceId: cachedDeviceId ??= loadOrCreateDeviceId(),
|
|
2804
|
-
hostname: pidInfo?.hostname ?? osHostname(),
|
|
2805
|
-
startedAtMs: pidInfo?.startedAtMs
|
|
2806
|
-
});
|
|
2807
|
-
let activeToken = token;
|
|
2808
|
-
let res = await requestText(`${convexUrl}/api/cli/heartbeat`, {
|
|
2809
|
-
method: "POST",
|
|
2810
|
-
headers: {
|
|
2811
|
-
Authorization: `Bearer ${activeToken}`,
|
|
2812
|
-
Connection: "close",
|
|
2813
|
-
"Content-Type": "application/json"
|
|
2814
|
-
},
|
|
2815
|
-
body: heartbeatBody
|
|
2816
|
-
});
|
|
2817
|
-
if (res.status === 401) {
|
|
2818
|
-
const refreshedToken = await refreshStoredToken(activeToken, convexUrl);
|
|
2819
|
-
if (!refreshedToken)
|
|
2820
|
-
return;
|
|
2821
|
-
activeToken = refreshedToken;
|
|
2822
|
-
res = await requestText(`${convexUrl}/api/cli/heartbeat`, {
|
|
2823
|
-
method: "POST",
|
|
2824
|
-
headers: {
|
|
2825
|
-
Authorization: `Bearer ${activeToken}`,
|
|
2826
|
-
Connection: "close",
|
|
2827
|
-
"Content-Type": "application/json"
|
|
2828
|
-
},
|
|
2829
|
-
body: heartbeatBody
|
|
2830
|
-
});
|
|
2831
|
-
}
|
|
2832
|
-
if (res.status === 401) {
|
|
2833
|
-
return;
|
|
2834
|
-
}
|
|
2835
|
-
} catch {}
|
|
2836
|
-
}
|
|
2837
|
-
async function refreshToken(currentToken, convexUrl) {
|
|
2838
|
-
const res = await requestText(`${convexUrl}/api/cli/refresh`, {
|
|
2839
|
-
method: "POST",
|
|
2840
|
-
headers: {
|
|
2841
|
-
Authorization: `Bearer ${currentToken}`,
|
|
2842
|
-
Connection: "close",
|
|
2843
|
-
"Content-Type": "application/json"
|
|
2844
|
-
}
|
|
2845
|
-
});
|
|
2846
|
-
if (res.status < 200 || res.status >= 300)
|
|
2847
|
-
return null;
|
|
2848
|
-
const body = JSON.parse(res.body);
|
|
2849
|
-
if (!body.token)
|
|
2850
|
-
return null;
|
|
2851
|
-
return { token: body.token, expiresAt: body.expiresAt ?? 0 };
|
|
2852
|
-
}
|
|
2853
|
-
function requestText(urlString, options) {
|
|
2854
|
-
const url = new URL(urlString);
|
|
2855
|
-
const request = url.protocol === "https:" ? httpsRequest : httpRequest;
|
|
2856
|
-
return new Promise((resolve4, reject) => {
|
|
2857
|
-
const req = request(url, {
|
|
2858
|
-
agent: false,
|
|
2859
|
-
headers: options.headers,
|
|
2860
|
-
method: options.method
|
|
2861
|
-
}, (res) => {
|
|
2862
|
-
let body = "";
|
|
2863
|
-
res.setEncoding("utf8");
|
|
2864
|
-
res.on("data", (chunk) => {
|
|
2865
|
-
body += chunk;
|
|
2866
|
-
});
|
|
2867
|
-
res.on("end", () => {
|
|
2868
|
-
resolve4({
|
|
2869
|
-
status: res.statusCode ?? 0,
|
|
2870
|
-
body
|
|
2871
|
-
});
|
|
2872
|
-
});
|
|
2873
|
-
res.on("error", reject);
|
|
2874
|
-
});
|
|
2875
|
-
req.on("error", reject);
|
|
2876
|
-
if (options.body) {
|
|
2877
|
-
req.write(options.body);
|
|
2878
|
-
}
|
|
2879
|
-
req.end();
|
|
2880
|
-
});
|
|
2881
|
-
}
|
|
2882
|
-
|
|
2883
|
-
// src/daemon.ts
|
|
2884
2914
|
var MAX_RESTARTS = 5;
|
|
2885
2915
|
var RESTART_WINDOW_MS = 60000;
|
|
2886
2916
|
var RESTART_DELAY_MS = 5000;
|
|
@@ -3065,7 +3095,7 @@ import { join as join11 } from "node:path";
|
|
|
3065
3095
|
// package.json
|
|
3066
3096
|
var package_default = {
|
|
3067
3097
|
name: "agendex-cli",
|
|
3068
|
-
version: "0.
|
|
3098
|
+
version: "0.7.0",
|
|
3069
3099
|
description: "Agendex CLI for login, sync, and daemon workflows",
|
|
3070
3100
|
homepage: "https://github.com/Tyru5/Agendex#readme",
|
|
3071
3101
|
repository: {
|
|
@@ -3276,6 +3306,28 @@ async function main() {
|
|
|
3276
3306
|
writeStdout(`[agendex] Hostname: n/a`);
|
|
3277
3307
|
}
|
|
3278
3308
|
writeStdout(`[agendex] CLI version: ${CLI_VERSION}`);
|
|
3309
|
+
try {
|
|
3310
|
+
if (config?.cloudToken && config?.convexUrl) {
|
|
3311
|
+
const allDevices = await fetchDevices();
|
|
3312
|
+
if (allDevices.length > 0) {
|
|
3313
|
+
const now = Date.now();
|
|
3314
|
+
writeStdout(`[agendex] All daemons:`);
|
|
3315
|
+
for (const device of allDevices) {
|
|
3316
|
+
const age = device.lastSeenAt ? now - device.lastSeenAt : Number.POSITIVE_INFINITY;
|
|
3317
|
+
const status = age < CLI_DAEMON_STALE_AFTER_MS ? "alive" : "stale";
|
|
3318
|
+
const uptimeStr = device.startedAtMs != null ? formatDuration(now - device.startedAtMs) : "~";
|
|
3319
|
+
const pidStr = device.pid != null ? String(device.pid) : "~";
|
|
3320
|
+
const hostnameStr = device.hostname ?? "~";
|
|
3321
|
+
writeStdout(`- hostname: ${hostnameStr}
|
|
3322
|
+
pid: ${pidStr}
|
|
3323
|
+
uptime: ${uptimeStr}
|
|
3324
|
+
status: ${status}`);
|
|
3325
|
+
}
|
|
3326
|
+
} else {
|
|
3327
|
+
writeStdout(`[agendex] All daemons: none`);
|
|
3328
|
+
}
|
|
3329
|
+
}
|
|
3330
|
+
} catch {}
|
|
3279
3331
|
return 0;
|
|
3280
3332
|
}
|
|
3281
3333
|
case "help":
|