unitup 0.0.12 → 0.1.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/README.md +125 -6
- package/SECURITY.md +21 -0
- package/docs/index.html +68 -4
- package/index.d.ts +137 -0
- package/package.json +1 -1
- package/src/cli.js +318 -20
- package/src/index.js +53 -3
- package/src/schedule.js +459 -0
- package/src/systemd.js +126 -0
- package/src/unit.js +238 -6
- package/src/utils.js +254 -1
package/src/cli.js
CHANGED
|
@@ -19,9 +19,20 @@ import {
|
|
|
19
19
|
inspectService,
|
|
20
20
|
getServiceFailures,
|
|
21
21
|
setServiceLimits,
|
|
22
|
-
executeJournalctlMaintenance
|
|
22
|
+
executeJournalctlMaintenance,
|
|
23
|
+
getServiceMemoryUsage,
|
|
24
|
+
getAllServicesMemoryUsage
|
|
23
25
|
} from './systemd.js';
|
|
24
|
-
import {
|
|
26
|
+
import {
|
|
27
|
+
createSchedule,
|
|
28
|
+
listSchedules,
|
|
29
|
+
getScheduleStatus,
|
|
30
|
+
runSchedule,
|
|
31
|
+
enableSchedule,
|
|
32
|
+
disableSchedule,
|
|
33
|
+
removeSchedule
|
|
34
|
+
} from './schedule.js';
|
|
35
|
+
import { sanitizeServiceName, formatTable, readGlobalConfig, saveGlobalConfig, validateMemorySize } from './utils.js';
|
|
25
36
|
|
|
26
37
|
/**
|
|
27
38
|
* Custom light CLI argument parser without runtime dependencies.
|
|
@@ -58,6 +69,8 @@ export function parseArgs(argv) {
|
|
|
58
69
|
memoryMax: '',
|
|
59
70
|
memorySwapMax: '',
|
|
60
71
|
resetMemory: false,
|
|
72
|
+
defaultMemory: '',
|
|
73
|
+
resetDefaultMemory: false,
|
|
61
74
|
since: '',
|
|
62
75
|
until: '',
|
|
63
76
|
priority: '',
|
|
@@ -69,7 +82,13 @@ export function parseArgs(argv) {
|
|
|
69
82
|
time: '',
|
|
70
83
|
files: '',
|
|
71
84
|
yes: false,
|
|
72
|
-
dryRun: false
|
|
85
|
+
dryRun: false,
|
|
86
|
+
every: '',
|
|
87
|
+
calendar: '',
|
|
88
|
+
onBoot: '',
|
|
89
|
+
onActive: '',
|
|
90
|
+
randomDelay: '',
|
|
91
|
+
persistent: false
|
|
73
92
|
}
|
|
74
93
|
};
|
|
75
94
|
|
|
@@ -198,6 +217,21 @@ export function parseArgs(argv) {
|
|
|
198
217
|
} else if (arg === '--reset-memory') {
|
|
199
218
|
result.flags.resetMemory = true;
|
|
200
219
|
i++;
|
|
220
|
+
} else if (arg === '--default-memory') {
|
|
221
|
+
const next = argv[i + 1];
|
|
222
|
+
if (next && !next.startsWith('-')) {
|
|
223
|
+
result.flags.defaultMemory = next;
|
|
224
|
+
i += 2;
|
|
225
|
+
} else {
|
|
226
|
+
result.flags.defaultMemory = '1G';
|
|
227
|
+
i++;
|
|
228
|
+
}
|
|
229
|
+
} else if (arg.startsWith('--default-memory=')) {
|
|
230
|
+
result.flags.defaultMemory = arg.slice(17) || '1G';
|
|
231
|
+
i++;
|
|
232
|
+
} else if (arg === '--reset-default-memory') {
|
|
233
|
+
result.flags.resetDefaultMemory = true;
|
|
234
|
+
i++;
|
|
201
235
|
} else if (arg === '--since') {
|
|
202
236
|
result.flags.since = argv[i + 1] || '';
|
|
203
237
|
i += 2;
|
|
@@ -255,6 +289,39 @@ export function parseArgs(argv) {
|
|
|
255
289
|
} else if (arg === '--dry-run') {
|
|
256
290
|
result.flags.dryRun = true;
|
|
257
291
|
i++;
|
|
292
|
+
} else if (arg === '--every') {
|
|
293
|
+
result.flags.every = argv[i + 1] || '';
|
|
294
|
+
i += 2;
|
|
295
|
+
} else if (arg.startsWith('--every=')) {
|
|
296
|
+
result.flags.every = arg.slice(8);
|
|
297
|
+
i++;
|
|
298
|
+
} else if (arg === '--calendar') {
|
|
299
|
+
result.flags.calendar = argv[i + 1] || '';
|
|
300
|
+
i += 2;
|
|
301
|
+
} else if (arg.startsWith('--calendar=')) {
|
|
302
|
+
result.flags.calendar = arg.slice(11);
|
|
303
|
+
i++;
|
|
304
|
+
} else if (arg === '--on-boot') {
|
|
305
|
+
result.flags.onBoot = argv[i + 1] || '';
|
|
306
|
+
i += 2;
|
|
307
|
+
} else if (arg.startsWith('--on-boot=')) {
|
|
308
|
+
result.flags.onBoot = arg.slice(10);
|
|
309
|
+
i++;
|
|
310
|
+
} else if (arg === '--on-active') {
|
|
311
|
+
result.flags.onActive = argv[i + 1] || '';
|
|
312
|
+
i += 2;
|
|
313
|
+
} else if (arg.startsWith('--on-active=')) {
|
|
314
|
+
result.flags.onActive = arg.slice(12);
|
|
315
|
+
i++;
|
|
316
|
+
} else if (arg === '--random-delay') {
|
|
317
|
+
result.flags.randomDelay = argv[i + 1] || '';
|
|
318
|
+
i += 2;
|
|
319
|
+
} else if (arg.startsWith('--random-delay=')) {
|
|
320
|
+
result.flags.randomDelay = arg.slice(15);
|
|
321
|
+
i++;
|
|
322
|
+
} else if (arg === '--persistent') {
|
|
323
|
+
result.flags.persistent = true;
|
|
324
|
+
i++;
|
|
258
325
|
} else if (!arg.startsWith('-')) {
|
|
259
326
|
if (!result.command) {
|
|
260
327
|
result.command = arg;
|
|
@@ -287,25 +354,39 @@ Usage:
|
|
|
287
354
|
unitup failures List all failed services with exit code & restarts
|
|
288
355
|
unitup remove <name|@group> Stop, disable and delete a service
|
|
289
356
|
unitup list / unitup ls List all services (--group <group>)
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
357
|
+
unitup memory / unitup top Show memory usage for all apps or a specific app
|
|
358
|
+
unitup config Manage global configuration (--default-memory 1G)
|
|
359
|
+
|
|
360
|
+
Schedule Commands:
|
|
361
|
+
unitup schedule <script> [options] Create a systemd timer schedule
|
|
362
|
+
unitup schedules / unitup timers List all schedule timers
|
|
363
|
+
unitup schedule-status <name> Show detailed schedule timer status
|
|
364
|
+
unitup schedule-run <name> Manually run schedule service unit once
|
|
365
|
+
unitup schedule-enable <name> Enable and start schedule timer
|
|
366
|
+
unitup schedule-disable <name> Disable and stop schedule timer
|
|
367
|
+
unitup schedule-remove <name> Remove schedule timer and service
|
|
368
|
+
unitup schedule-logs <name> View logs for a scheduled task
|
|
369
|
+
|
|
370
|
+
Memory Options:
|
|
371
|
+
--memory-high <size> Soft memory limit (e.g. 400M, 1G)
|
|
372
|
+
--memory-max <size> Hard memory limit (e.g. 512M, 1G)
|
|
373
|
+
--default-memory [sz] Apply default memory limit (defaults to 1G)
|
|
374
|
+
|
|
375
|
+
Schedule Options:
|
|
376
|
+
--every <duration> Execution interval (e.g., 30s, 10m, 2h, 1d)
|
|
377
|
+
--calendar <expr> Calendar expression (e.g., daily, "Mon..Fri 09:00")
|
|
378
|
+
--on-boot <duration> Delay relative to system boot (e.g., 5m)
|
|
379
|
+
--on-active <duration> Delay relative to timer activation
|
|
380
|
+
--random-delay <dur> Random delay to prevent stampedes (e.g., 10m)
|
|
381
|
+
--persistent Run immediately if a scheduled run was missed
|
|
382
|
+
--start Enable and start timer immediately after creation
|
|
303
383
|
|
|
304
384
|
Examples:
|
|
305
|
-
unitup add server.js --runtime node
|
|
306
|
-
unitup
|
|
307
|
-
unitup
|
|
308
|
-
unitup
|
|
385
|
+
unitup add server.js --runtime node --default-memory 1G
|
|
386
|
+
unitup config --default-memory 1G
|
|
387
|
+
unitup schedule cleanup.js --every 30m --default-memory --start
|
|
388
|
+
unitup schedule backup.py --calendar daily --random-delay 10m --start
|
|
389
|
+
unitup schedule report.js --calendar "Mon..Fri 09:00" --start
|
|
309
390
|
`);
|
|
310
391
|
}
|
|
311
392
|
|
|
@@ -417,6 +498,7 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
417
498
|
memoryHigh: flags.memoryHigh,
|
|
418
499
|
memoryMax: flags.memoryMax,
|
|
419
500
|
memorySwapMax: flags.memorySwapMax,
|
|
501
|
+
defaultMemory: flags.defaultMemory,
|
|
420
502
|
force: flags.force
|
|
421
503
|
});
|
|
422
504
|
|
|
@@ -656,6 +738,222 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
656
738
|
break;
|
|
657
739
|
}
|
|
658
740
|
|
|
741
|
+
case 'schedule': {
|
|
742
|
+
const scriptArg = positionals[0];
|
|
743
|
+
const envObj = {};
|
|
744
|
+
for (const e of flags.env) {
|
|
745
|
+
const idx = e.indexOf('=');
|
|
746
|
+
if (idx !== -1) {
|
|
747
|
+
envObj[e.slice(0, idx)] = e.slice(idx + 1);
|
|
748
|
+
} else {
|
|
749
|
+
envObj[e] = '';
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
const res = await createSchedule({
|
|
754
|
+
name: flags.name,
|
|
755
|
+
script: scriptArg,
|
|
756
|
+
command: flags.command,
|
|
757
|
+
runtime: flags.runtime,
|
|
758
|
+
runtimeArgs: flags.runtimeArgs,
|
|
759
|
+
args: flags.args,
|
|
760
|
+
cwd: flags.cwd,
|
|
761
|
+
env: envObj,
|
|
762
|
+
envFile: flags.envFile,
|
|
763
|
+
group: flags.group,
|
|
764
|
+
memoryHigh: flags.memoryHigh,
|
|
765
|
+
memoryMax: flags.memoryMax,
|
|
766
|
+
memorySwapMax: flags.memorySwapMax,
|
|
767
|
+
defaultMemory: flags.defaultMemory,
|
|
768
|
+
every: flags.every,
|
|
769
|
+
calendar: flags.calendar,
|
|
770
|
+
onBoot: flags.onBoot,
|
|
771
|
+
onActive: flags.onActive,
|
|
772
|
+
randomDelay: flags.randomDelay,
|
|
773
|
+
persistent: flags.persistent,
|
|
774
|
+
start: flags.start || flags.enable,
|
|
775
|
+
enable: flags.enable
|
|
776
|
+
});
|
|
777
|
+
|
|
778
|
+
console.log(`✓ Schedule "${res.name}" created.`);
|
|
779
|
+
console.log(`Service unit: ${res.servicePath}`);
|
|
780
|
+
console.log(`Timer unit: ${res.timerPath}`);
|
|
781
|
+
if (flags.start || flags.enable) {
|
|
782
|
+
console.log(`✓ Timer "${res.name}" enabled and started.`);
|
|
783
|
+
} else {
|
|
784
|
+
console.log(`Run "unitup schedule-enable ${res.name}" to enable and start the timer.`);
|
|
785
|
+
}
|
|
786
|
+
break;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
case 'timers':
|
|
790
|
+
case 'schedules': {
|
|
791
|
+
const schedules = await listSchedules(flags.group);
|
|
792
|
+
if (schedules.length === 0) {
|
|
793
|
+
if (flags.group) {
|
|
794
|
+
console.log(`No schedules found in group "${flags.group}".`);
|
|
795
|
+
} else {
|
|
796
|
+
console.log('No unitup schedule timers found.');
|
|
797
|
+
}
|
|
798
|
+
break;
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
const table = formatTable(schedules, [
|
|
802
|
+
{ key: 'name', label: 'NAME' },
|
|
803
|
+
{ key: 'group', label: 'GROUP' },
|
|
804
|
+
{ key: 'schedule', label: 'SCHEDULE' },
|
|
805
|
+
{ key: 'nextRun', label: 'NEXT RUN' },
|
|
806
|
+
{ key: 'lastRun', label: 'LAST RUN' },
|
|
807
|
+
{ key: 'status', label: 'STATUS' }
|
|
808
|
+
]);
|
|
809
|
+
console.log(table);
|
|
810
|
+
break;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
case 'timer-status':
|
|
814
|
+
case 'schedule-status': {
|
|
815
|
+
const nameArg = positionals[0];
|
|
816
|
+
if (!nameArg) {
|
|
817
|
+
throw new Error('Schedule name is required.\nExample: unitup schedule-status cleanup');
|
|
818
|
+
}
|
|
819
|
+
const status = await getScheduleStatus(nameArg);
|
|
820
|
+
console.log(`Name: ${status.name}`);
|
|
821
|
+
console.log(`Group: ${status.group}`);
|
|
822
|
+
console.log(`Schedule: ${status.schedule}`);
|
|
823
|
+
console.log(`Next Run: ${status.nextRun}`);
|
|
824
|
+
console.log(`Last Run: ${status.lastRun}`);
|
|
825
|
+
console.log(`Status: ${status.status}`);
|
|
826
|
+
console.log(`Timer Unit: ${status.timerUnit}`);
|
|
827
|
+
console.log(`Service Unit: ${status.serviceUnit}`);
|
|
828
|
+
break;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
case 'schedule-run': {
|
|
832
|
+
const nameArg = positionals[0];
|
|
833
|
+
if (!nameArg) {
|
|
834
|
+
throw new Error('Schedule name is required.\nExample: unitup schedule-run cleanup');
|
|
835
|
+
}
|
|
836
|
+
await runSchedule(nameArg);
|
|
837
|
+
console.log(`✓ Service unit for schedule "${sanitizeServiceName(nameArg)}" triggered.`);
|
|
838
|
+
break;
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
case 'schedule-enable': {
|
|
842
|
+
const nameArg = positionals[0];
|
|
843
|
+
if (!nameArg) {
|
|
844
|
+
throw new Error('Schedule name is required.\nExample: unitup schedule-enable cleanup');
|
|
845
|
+
}
|
|
846
|
+
await enableSchedule(nameArg);
|
|
847
|
+
console.log(`✓ Schedule timer "${sanitizeServiceName(nameArg)}" enabled and started.`);
|
|
848
|
+
break;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
case 'schedule-disable': {
|
|
852
|
+
const nameArg = positionals[0];
|
|
853
|
+
if (!nameArg) {
|
|
854
|
+
throw new Error('Schedule name is required.\nExample: unitup schedule-disable cleanup');
|
|
855
|
+
}
|
|
856
|
+
await disableSchedule(nameArg);
|
|
857
|
+
console.log(`✓ Schedule timer "${sanitizeServiceName(nameArg)}" disabled.`);
|
|
858
|
+
break;
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
case 'schedule-remove': {
|
|
862
|
+
const nameArg = positionals[0];
|
|
863
|
+
if (!nameArg) {
|
|
864
|
+
throw new Error('Schedule name is required.\nExample: unitup schedule-remove cleanup');
|
|
865
|
+
}
|
|
866
|
+
await removeSchedule(nameArg, { force: flags.force });
|
|
867
|
+
console.log(`✓ Schedule "${sanitizeServiceName(nameArg)}" removed.`);
|
|
868
|
+
break;
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
case 'schedule-logs': {
|
|
872
|
+
const nameArg = positionals[0];
|
|
873
|
+
if (!nameArg) {
|
|
874
|
+
throw new Error('Schedule name is required.\nExample: unitup schedule-logs cleanup');
|
|
875
|
+
}
|
|
876
|
+
const output = await runJournalctlLogs(nameArg, {
|
|
877
|
+
follow: flags.follow,
|
|
878
|
+
lines: flags.lines,
|
|
879
|
+
cat: flags.cat,
|
|
880
|
+
output: flags.output,
|
|
881
|
+
since: flags.since,
|
|
882
|
+
until: flags.until,
|
|
883
|
+
priority: flags.priority,
|
|
884
|
+
grep: flags.grep,
|
|
885
|
+
boot: flags.boot,
|
|
886
|
+
json: flags.json,
|
|
887
|
+
diskUsage: flags.diskUsage
|
|
888
|
+
});
|
|
889
|
+
if (typeof output === 'string') {
|
|
890
|
+
console.log(output);
|
|
891
|
+
}
|
|
892
|
+
break;
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
case 'config': {
|
|
896
|
+
if (flags.resetDefaultMemory) {
|
|
897
|
+
saveGlobalConfig({ defaultMemory: null });
|
|
898
|
+
console.log('✓ Default memory limit reset.');
|
|
899
|
+
} else if (flags.defaultMemory) {
|
|
900
|
+
const validSize = validateMemorySize(flags.defaultMemory, 'Default memory limit');
|
|
901
|
+
saveGlobalConfig({ defaultMemory: validSize });
|
|
902
|
+
console.log(`✓ Default memory limit set to ${validSize}.`);
|
|
903
|
+
} else {
|
|
904
|
+
const cfg = readGlobalConfig();
|
|
905
|
+
console.log(`Default Memory: ${cfg.defaultMemory || 'not set (1G on demand)'}`);
|
|
906
|
+
}
|
|
907
|
+
break;
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
case 'top':
|
|
911
|
+
case 'mem':
|
|
912
|
+
case 'memory': {
|
|
913
|
+
const nameArg = positionals[0];
|
|
914
|
+
if (nameArg) {
|
|
915
|
+
const targetNames = await resolveTargetNames(nameArg);
|
|
916
|
+
for (const name of targetNames) {
|
|
917
|
+
const mem = await getServiceMemoryUsage(name);
|
|
918
|
+
console.log(`=== Memory Overview: ${mem.name} ===`);
|
|
919
|
+
console.log(`Group: ${mem.group}`);
|
|
920
|
+
console.log(`Type: ${mem.type}`);
|
|
921
|
+
console.log(`Status: ${mem.status}`);
|
|
922
|
+
console.log(`PID: ${mem.pid}`);
|
|
923
|
+
console.log(`Current Memory: ${mem.memory}`);
|
|
924
|
+
console.log(`Peak Memory: ${mem.memoryPeak}`);
|
|
925
|
+
console.log(`Soft Limit (High): ${mem.memoryHigh}`);
|
|
926
|
+
console.log(`Hard Limit (Max): ${mem.memoryMax}`);
|
|
927
|
+
console.log(`Swap Limit: ${mem.memorySwapMax}`);
|
|
928
|
+
if (targetNames.length > 1) console.log('');
|
|
929
|
+
}
|
|
930
|
+
} else {
|
|
931
|
+
const overview = await getAllServicesMemoryUsage({ group: flags.group });
|
|
932
|
+
if (overview.items.length === 0) {
|
|
933
|
+
if (flags.group) {
|
|
934
|
+
console.log(`No services or schedules found in group "${flags.group}".`);
|
|
935
|
+
} else {
|
|
936
|
+
console.log('No unitup user services or schedules found.');
|
|
937
|
+
}
|
|
938
|
+
break;
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
const table = formatTable(overview.items, [
|
|
942
|
+
{ key: 'name', label: 'NAME' },
|
|
943
|
+
{ key: 'group', label: 'GROUP' },
|
|
944
|
+
{ key: 'type', label: 'TYPE' },
|
|
945
|
+
{ key: 'status', label: 'STATUS' },
|
|
946
|
+
{ key: 'pid', label: 'PID' },
|
|
947
|
+
{ key: 'memory', label: 'MEMORY' },
|
|
948
|
+
{ key: 'memoryPeak', label: 'PEAK' },
|
|
949
|
+
{ key: 'memoryMax', label: 'LIMIT (MAX)' }
|
|
950
|
+
]);
|
|
951
|
+
console.log(table);
|
|
952
|
+
console.log(`\nTotal Memory Usage across ${overview.runningCount} active app(s): ${overview.totalMemory}`);
|
|
953
|
+
}
|
|
954
|
+
break;
|
|
955
|
+
}
|
|
956
|
+
|
|
659
957
|
default: {
|
|
660
958
|
console.error(`Unknown command: "${command}"\n`);
|
|
661
959
|
printHelp();
|
package/src/index.js
CHANGED
|
@@ -20,25 +20,50 @@ import {
|
|
|
20
20
|
checkUserLinger,
|
|
21
21
|
checkNodeDiagnostics,
|
|
22
22
|
findNodeExecutable,
|
|
23
|
+
getServiceMemoryUsage,
|
|
24
|
+
getAllServicesMemoryUsage,
|
|
23
25
|
setCommandRunner,
|
|
24
26
|
resetCommandRunner
|
|
25
27
|
} from './systemd.js';
|
|
26
28
|
import { getDoctorInfo } from './doctor.js';
|
|
29
|
+
import {
|
|
30
|
+
createSchedule,
|
|
31
|
+
listSchedules,
|
|
32
|
+
getScheduleStatus,
|
|
33
|
+
runSchedule,
|
|
34
|
+
enableSchedule,
|
|
35
|
+
disableSchedule,
|
|
36
|
+
removeSchedule,
|
|
37
|
+
validateCalendar
|
|
38
|
+
} from './schedule.js';
|
|
27
39
|
import {
|
|
28
40
|
getUserUnitDir,
|
|
29
41
|
getUnitPath,
|
|
42
|
+
getTimerPath,
|
|
30
43
|
unitFileExists,
|
|
31
|
-
|
|
44
|
+
timerFileExists,
|
|
45
|
+
parseUnitContent,
|
|
46
|
+
generateTimerContent,
|
|
47
|
+
generateScheduleServiceContent
|
|
32
48
|
} from './unit.js';
|
|
33
49
|
import {
|
|
34
50
|
sanitizeServiceName,
|
|
35
51
|
getUnitFilename,
|
|
52
|
+
getTimerFilename,
|
|
36
53
|
getServiceNameFromUnit,
|
|
37
54
|
readAppMetadata,
|
|
38
55
|
getAppMetadataPath,
|
|
56
|
+
readScheduleMetadata,
|
|
57
|
+
getScheduleMetadataPath,
|
|
58
|
+
readGlobalConfig,
|
|
59
|
+
saveGlobalConfig,
|
|
60
|
+
getConfigFilepath,
|
|
61
|
+
resolveEffectiveMemoryLimits,
|
|
62
|
+
getSchedulesDir,
|
|
39
63
|
getAppsDir,
|
|
40
64
|
getUnitupDir,
|
|
41
65
|
validateMemorySize,
|
|
66
|
+
validateDuration,
|
|
42
67
|
formatMemoryBytes
|
|
43
68
|
} from './utils.js';
|
|
44
69
|
import { detectRuntime, resolveRuntimeConfig } from './runtimes/index.js';
|
|
@@ -58,20 +83,40 @@ export {
|
|
|
58
83
|
getServiceFailures,
|
|
59
84
|
getServicesByGroup,
|
|
60
85
|
setServiceLimits,
|
|
86
|
+
getServiceMemoryUsage,
|
|
87
|
+
getAllServicesMemoryUsage,
|
|
61
88
|
executeJournalctlMaintenance as executeJournalctlMaintenance,
|
|
62
89
|
runJournalctlLogs as getServiceLogs,
|
|
63
90
|
|
|
64
|
-
//
|
|
91
|
+
// Programmatic schedule management API
|
|
92
|
+
createSchedule,
|
|
93
|
+
listSchedules,
|
|
94
|
+
getScheduleStatus,
|
|
95
|
+
runSchedule,
|
|
96
|
+
enableSchedule,
|
|
97
|
+
disableSchedule,
|
|
98
|
+
removeSchedule,
|
|
99
|
+
validateCalendar,
|
|
100
|
+
|
|
101
|
+
// Duration & Validation helpers
|
|
65
102
|
validateMemorySize,
|
|
103
|
+
validateDuration,
|
|
66
104
|
formatMemoryBytes,
|
|
67
105
|
|
|
68
106
|
// Runtime detection & resolution
|
|
69
107
|
detectRuntime,
|
|
70
108
|
resolveRuntimeConfig,
|
|
71
109
|
|
|
72
|
-
// Metadata helpers
|
|
110
|
+
// Metadata & Config helpers
|
|
73
111
|
readAppMetadata,
|
|
74
112
|
getAppMetadataPath,
|
|
113
|
+
readScheduleMetadata,
|
|
114
|
+
getScheduleMetadataPath,
|
|
115
|
+
readGlobalConfig,
|
|
116
|
+
saveGlobalConfig,
|
|
117
|
+
getConfigFilepath,
|
|
118
|
+
resolveEffectiveMemoryLimits,
|
|
119
|
+
getSchedulesDir,
|
|
75
120
|
getAppsDir,
|
|
76
121
|
getUnitupDir,
|
|
77
122
|
|
|
@@ -89,10 +134,15 @@ export {
|
|
|
89
134
|
// Unit file & path helpers
|
|
90
135
|
getUserUnitDir,
|
|
91
136
|
getUnitPath,
|
|
137
|
+
getTimerPath,
|
|
92
138
|
unitFileExists,
|
|
139
|
+
timerFileExists,
|
|
93
140
|
parseUnitContent,
|
|
141
|
+
generateTimerContent,
|
|
142
|
+
generateScheduleServiceContent,
|
|
94
143
|
sanitizeServiceName,
|
|
95
144
|
getUnitFilename,
|
|
145
|
+
getTimerFilename,
|
|
96
146
|
getServiceNameFromUnit,
|
|
97
147
|
|
|
98
148
|
// Runner override for testing
|