@kikimoradev/kcloud 0.16.0 → 0.17.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/main.js +738 -445
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -386,6 +386,12 @@ const dispatcherOf = (spec) => {
|
|
|
386
386
|
}
|
|
387
387
|
};
|
|
388
388
|
};
|
|
389
|
+
//#endregion
|
|
390
|
+
//#region ../../packages/cli-kit/src/list.ts
|
|
391
|
+
const listOf = (items) => {
|
|
392
|
+
const last = items.at(-1);
|
|
393
|
+
return items.length < 2 || last === void 0 ? items.join("") : `${items.slice(0, -1).join(", ")} and ${last}`;
|
|
394
|
+
};
|
|
389
395
|
const permissionsOutsideOwner = 63;
|
|
390
396
|
const isMissing = (error) => error instanceof Error && error.code === "ENOENT";
|
|
391
397
|
const ensurePrivateDirectory = async (path) => {
|
|
@@ -509,243 +515,6 @@ const spec = {
|
|
|
509
515
|
const defineCommand = commandFactoryOf(spec);
|
|
510
516
|
const defineGroup = (definition) => defineGroup$1(definition);
|
|
511
517
|
const runCli = dispatcherOf(spec);
|
|
512
|
-
const agentEventLevelSchema = z.enum([
|
|
513
|
-
"info",
|
|
514
|
-
"warn",
|
|
515
|
-
"error"
|
|
516
|
-
]);
|
|
517
|
-
const agentRoles = [
|
|
518
|
-
"monitor",
|
|
519
|
-
"executor",
|
|
520
|
-
"summarizer"
|
|
521
|
-
];
|
|
522
|
-
const agentRoleSchema = z.enum(agentRoles);
|
|
523
|
-
const agentLogStreams = ["stdout", "stderr"];
|
|
524
|
-
z.enum(agentLogStreams);
|
|
525
|
-
const platformStream = "platform";
|
|
526
|
-
const agentEventStreams = [...agentLogStreams, platformStream];
|
|
527
|
-
const agentEventStreamSchema = z.enum(agentEventStreams);
|
|
528
|
-
const authKindSchema = z.enum([
|
|
529
|
-
"apiKey",
|
|
530
|
-
"oauth",
|
|
531
|
-
"claude.ai",
|
|
532
|
-
"unknown"
|
|
533
|
-
]);
|
|
534
|
-
const agentControlStateSchema = z.enum([
|
|
535
|
-
"running",
|
|
536
|
-
"pausing",
|
|
537
|
-
"paused"
|
|
538
|
-
]);
|
|
539
|
-
z.enum([
|
|
540
|
-
"session",
|
|
541
|
-
"diagnostic",
|
|
542
|
-
"task"
|
|
543
|
-
]);
|
|
544
|
-
const maxCostUsd = 1e3;
|
|
545
|
-
const countSchema = z.int().min(0).max(2147483647);
|
|
546
|
-
const costUsdSchema = z.number().min(0).max(maxCostUsd);
|
|
547
|
-
const instantSchema$2 = z.iso.datetime();
|
|
548
|
-
const workerEnvelope = (event) => ({
|
|
549
|
-
ts: instantSchema$2,
|
|
550
|
-
level: agentEventLevelSchema,
|
|
551
|
-
event: z.literal(event)
|
|
552
|
-
});
|
|
553
|
-
const roleEnvelope = (event) => ({
|
|
554
|
-
...workerEnvelope(event),
|
|
555
|
-
agent: agentRoleSchema.optional()
|
|
556
|
-
});
|
|
557
|
-
const agentEventSchema = z.discriminatedUnion("event", [
|
|
558
|
-
z.looseObject({
|
|
559
|
-
...workerEnvelope("worker.started"),
|
|
560
|
-
version: z.string(),
|
|
561
|
-
claudeVersion: z.string().optional(),
|
|
562
|
-
nodeVersion: z.string(),
|
|
563
|
-
authKind: authKindSchema,
|
|
564
|
-
pid: countSchema,
|
|
565
|
-
projectDir: z.string(),
|
|
566
|
-
paused: z.boolean().optional()
|
|
567
|
-
}),
|
|
568
|
-
z.looseObject({
|
|
569
|
-
...workerEnvelope("worker.stopped"),
|
|
570
|
-
signal: z.string().optional(),
|
|
571
|
-
drained: z.boolean().optional(),
|
|
572
|
-
forced: z.boolean().optional()
|
|
573
|
-
}),
|
|
574
|
-
z.looseObject({
|
|
575
|
-
...workerEnvelope("worker.draining"),
|
|
576
|
-
reason: z.string(),
|
|
577
|
-
timeoutMs: countSchema.optional()
|
|
578
|
-
}),
|
|
579
|
-
z.looseObject({
|
|
580
|
-
...workerEnvelope("update.available"),
|
|
581
|
-
from: z.string(),
|
|
582
|
-
to: z.string(),
|
|
583
|
-
installError: z.string().optional()
|
|
584
|
-
}),
|
|
585
|
-
z.looseObject({
|
|
586
|
-
...workerEnvelope("update.installed"),
|
|
587
|
-
from: z.string(),
|
|
588
|
-
to: z.string()
|
|
589
|
-
}),
|
|
590
|
-
z.looseObject({
|
|
591
|
-
...roleEnvelope("control.changed"),
|
|
592
|
-
state: agentControlStateSchema
|
|
593
|
-
}),
|
|
594
|
-
z.looseObject({
|
|
595
|
-
...roleEnvelope("cycle.started"),
|
|
596
|
-
cycle: countSchema
|
|
597
|
-
}),
|
|
598
|
-
z.looseObject({
|
|
599
|
-
...roleEnvelope("cycle.finished"),
|
|
600
|
-
cycle: countSchema,
|
|
601
|
-
ok: z.boolean(),
|
|
602
|
-
durationMs: countSchema,
|
|
603
|
-
costUsd: costUsdSchema.optional(),
|
|
604
|
-
addedTasks: countSchema,
|
|
605
|
-
skippedDuplicates: countSchema,
|
|
606
|
-
sessionId: z.string().optional(),
|
|
607
|
-
error: z.string().optional()
|
|
608
|
-
}),
|
|
609
|
-
z.looseObject({
|
|
610
|
-
...roleEnvelope("monitor.offHours"),
|
|
611
|
-
resumeAt: instantSchema$2
|
|
612
|
-
}),
|
|
613
|
-
z.looseObject({
|
|
614
|
-
...roleEnvelope("monitor.limitWait"),
|
|
615
|
-
resumeAt: instantSchema$2
|
|
616
|
-
}),
|
|
617
|
-
z.looseObject({
|
|
618
|
-
...roleEnvelope("monitor.sleeping"),
|
|
619
|
-
nextCycleAt: instantSchema$2
|
|
620
|
-
}),
|
|
621
|
-
z.looseObject({
|
|
622
|
-
...roleEnvelope("monitor.authBlocked"),
|
|
623
|
-
reason: z.string()
|
|
624
|
-
}),
|
|
625
|
-
z.looseObject({
|
|
626
|
-
...roleEnvelope("monitor.admissionWait"),
|
|
627
|
-
ticket: z.string(),
|
|
628
|
-
since: instantSchema$2,
|
|
629
|
-
seq: countSchema
|
|
630
|
-
}),
|
|
631
|
-
z.looseObject({
|
|
632
|
-
...roleEnvelope("executor.admissionWait"),
|
|
633
|
-
ticket: z.string(),
|
|
634
|
-
since: instantSchema$2,
|
|
635
|
-
seq: countSchema,
|
|
636
|
-
taskId: z.string()
|
|
637
|
-
}),
|
|
638
|
-
z.looseObject({
|
|
639
|
-
...roleEnvelope("admission.granted"),
|
|
640
|
-
ticket: z.string(),
|
|
641
|
-
seq: countSchema
|
|
642
|
-
}),
|
|
643
|
-
z.looseObject({
|
|
644
|
-
...roleEnvelope("admission.released"),
|
|
645
|
-
ticket: z.string(),
|
|
646
|
-
seq: countSchema
|
|
647
|
-
}),
|
|
648
|
-
z.looseObject({
|
|
649
|
-
...roleEnvelope("admission.cancelled"),
|
|
650
|
-
ticket: z.string(),
|
|
651
|
-
seq: countSchema
|
|
652
|
-
}),
|
|
653
|
-
z.looseObject({
|
|
654
|
-
...roleEnvelope("task.started"),
|
|
655
|
-
taskId: z.string(),
|
|
656
|
-
title: z.string()
|
|
657
|
-
}),
|
|
658
|
-
z.looseObject({
|
|
659
|
-
...roleEnvelope("task.finished"),
|
|
660
|
-
taskId: z.string(),
|
|
661
|
-
title: z.string(),
|
|
662
|
-
ok: z.boolean(),
|
|
663
|
-
durationMs: countSchema,
|
|
664
|
-
costUsd: costUsdSchema.optional(),
|
|
665
|
-
numTurns: countSchema.optional(),
|
|
666
|
-
willRetry: z.boolean().optional(),
|
|
667
|
-
attempt: countSchema.optional(),
|
|
668
|
-
maxAttempts: countSchema.optional(),
|
|
669
|
-
sessionId: z.string().optional(),
|
|
670
|
-
error: z.string().optional()
|
|
671
|
-
}),
|
|
672
|
-
z.looseObject({
|
|
673
|
-
...roleEnvelope("task.retryScheduled"),
|
|
674
|
-
taskId: z.string(),
|
|
675
|
-
resumeAt: instantSchema$2
|
|
676
|
-
}),
|
|
677
|
-
z.looseObject(roleEnvelope("executor.waiting")),
|
|
678
|
-
z.looseObject({
|
|
679
|
-
...roleEnvelope("executor.limitWait"),
|
|
680
|
-
resumeAt: instantSchema$2
|
|
681
|
-
}),
|
|
682
|
-
z.looseObject({
|
|
683
|
-
...roleEnvelope("executor.authBlocked"),
|
|
684
|
-
reason: z.string()
|
|
685
|
-
}),
|
|
686
|
-
z.looseObject({
|
|
687
|
-
...roleEnvelope("summary.started"),
|
|
688
|
-
taskId: z.string()
|
|
689
|
-
}),
|
|
690
|
-
z.looseObject({
|
|
691
|
-
...roleEnvelope("summary.finished"),
|
|
692
|
-
taskId: z.string(),
|
|
693
|
-
ok: z.boolean(),
|
|
694
|
-
durationMs: countSchema,
|
|
695
|
-
costUsd: costUsdSchema.optional(),
|
|
696
|
-
sessionId: z.string().optional(),
|
|
697
|
-
error: z.string().optional()
|
|
698
|
-
}),
|
|
699
|
-
z.looseObject({
|
|
700
|
-
...roleEnvelope("session.init"),
|
|
701
|
-
model: z.string(),
|
|
702
|
-
sessionId: z.string(),
|
|
703
|
-
taskId: z.string().optional(),
|
|
704
|
-
cycle: countSchema.optional()
|
|
705
|
-
}),
|
|
706
|
-
z.looseObject({
|
|
707
|
-
...roleEnvelope("session.stderr"),
|
|
708
|
-
line: z.string()
|
|
709
|
-
}),
|
|
710
|
-
z.looseObject({
|
|
711
|
-
...roleEnvelope("session.procError"),
|
|
712
|
-
message: z.string()
|
|
713
|
-
}),
|
|
714
|
-
z.looseObject({
|
|
715
|
-
...roleEnvelope("session.killed"),
|
|
716
|
-
reason: z.string()
|
|
717
|
-
}),
|
|
718
|
-
z.looseObject({
|
|
719
|
-
...roleEnvelope("session.text"),
|
|
720
|
-
text: z.string()
|
|
721
|
-
}),
|
|
722
|
-
z.looseObject({
|
|
723
|
-
...roleEnvelope("session.tool"),
|
|
724
|
-
tool: z.string(),
|
|
725
|
-
detail: z.string()
|
|
726
|
-
}),
|
|
727
|
-
z.looseObject({
|
|
728
|
-
...roleEnvelope("session.toolError"),
|
|
729
|
-
output: z.string()
|
|
730
|
-
})
|
|
731
|
-
]);
|
|
732
|
-
z.looseObject({
|
|
733
|
-
ts: instantSchema$2,
|
|
734
|
-
level: agentEventLevelSchema,
|
|
735
|
-
agent: agentRoleSchema.optional(),
|
|
736
|
-
event: z.string().min(1).max(64)
|
|
737
|
-
});
|
|
738
|
-
agentEventSchema.options.map((option) => option.shape.event.value);
|
|
739
|
-
({
|
|
740
|
-
rawLog: "log.raw",
|
|
741
|
-
rejectedLog: "log.rejected",
|
|
742
|
-
throttledLog: "log.throttled",
|
|
743
|
-
stateChanged: "platform.stateChanged",
|
|
744
|
-
crashLooping: "platform.crashLooping",
|
|
745
|
-
provisioned: "platform.provisioned",
|
|
746
|
-
sandboxStarted: "platform.sandboxStarted",
|
|
747
|
-
sandboxStopped: "platform.sandboxStopped"
|
|
748
|
-
}).rawLog;
|
|
749
518
|
//#endregion
|
|
750
519
|
//#region ../../packages/contracts/src/patterns.ts
|
|
751
520
|
const slugPattern = /^[a-z0-9](?:[a-z0-9-]{0,38}[a-z0-9])?$/;
|
|
@@ -1021,6 +790,11 @@ const errorCodes = Object.keys({
|
|
|
1021
790
|
"node.in_use": "conflict",
|
|
1022
791
|
"node.host_unknown": "rule_violation",
|
|
1023
792
|
"node.split_exceeds_host": "rule_violation",
|
|
793
|
+
"node.package_unavailable": "not_found",
|
|
794
|
+
"agent.backup_not_found": "not_found",
|
|
795
|
+
"agent.backup_unavailable": "rule_violation",
|
|
796
|
+
"agent.backup_without_export": "rule_violation",
|
|
797
|
+
"agent.backup_in_progress": "conflict",
|
|
1024
798
|
"project.last_admin": "conflict",
|
|
1025
799
|
"agent.node_unassigned": "rule_violation",
|
|
1026
800
|
"stream.too_many": "rate_limited",
|
|
@@ -1043,20 +817,257 @@ const errorCodes = Object.keys({
|
|
|
1043
817
|
"volume.invalid_size": "rule_violation",
|
|
1044
818
|
"notification.not_found": "not_found"
|
|
1045
819
|
});
|
|
1046
|
-
const errorCodeSchema = z.enum(errorCodes);
|
|
1047
|
-
const errorDetailsSchema = z.record(z.string(), z.union([
|
|
1048
|
-
z.string(),
|
|
1049
|
-
z.number(),
|
|
1050
|
-
z.boolean()
|
|
1051
|
-
]));
|
|
1052
|
-
const errorBodySchema = z.object({
|
|
1053
|
-
code: errorCodeSchema,
|
|
1054
|
-
details: errorDetailsSchema.optional(),
|
|
1055
|
-
issues: z.array(z.object({
|
|
1056
|
-
path: z.string(),
|
|
1057
|
-
message: z.string()
|
|
1058
|
-
})).optional()
|
|
1059
|
-
}).meta({ id: "ErrorBody" });
|
|
820
|
+
const errorCodeSchema = z.enum(errorCodes);
|
|
821
|
+
const errorDetailsSchema = z.record(z.string(), z.union([
|
|
822
|
+
z.string(),
|
|
823
|
+
z.number(),
|
|
824
|
+
z.boolean()
|
|
825
|
+
]));
|
|
826
|
+
const errorBodySchema = z.object({
|
|
827
|
+
code: errorCodeSchema,
|
|
828
|
+
details: errorDetailsSchema.optional(),
|
|
829
|
+
issues: z.array(z.object({
|
|
830
|
+
path: z.string(),
|
|
831
|
+
message: z.string()
|
|
832
|
+
})).optional()
|
|
833
|
+
}).meta({ id: "ErrorBody" });
|
|
834
|
+
const agentEventLevelSchema = z.enum([
|
|
835
|
+
"info",
|
|
836
|
+
"warn",
|
|
837
|
+
"error"
|
|
838
|
+
]);
|
|
839
|
+
const agentRoles = [
|
|
840
|
+
"monitor",
|
|
841
|
+
"executor",
|
|
842
|
+
"summarizer"
|
|
843
|
+
];
|
|
844
|
+
const agentRoleSchema = z.enum(agentRoles);
|
|
845
|
+
const agentLogStreams = ["stdout", "stderr"];
|
|
846
|
+
z.enum(agentLogStreams);
|
|
847
|
+
const platformStream = "platform";
|
|
848
|
+
const agentEventStreams = [...agentLogStreams, platformStream];
|
|
849
|
+
const agentEventStreamSchema = z.enum(agentEventStreams);
|
|
850
|
+
const authKindSchema = z.enum([
|
|
851
|
+
"apiKey",
|
|
852
|
+
"oauth",
|
|
853
|
+
"claude.ai",
|
|
854
|
+
"unknown"
|
|
855
|
+
]);
|
|
856
|
+
const agentControlStateSchema = z.enum([
|
|
857
|
+
"running",
|
|
858
|
+
"pausing",
|
|
859
|
+
"paused"
|
|
860
|
+
]);
|
|
861
|
+
z.enum([
|
|
862
|
+
"session",
|
|
863
|
+
"diagnostic",
|
|
864
|
+
"task"
|
|
865
|
+
]);
|
|
866
|
+
const maxCostUsd = 1e3;
|
|
867
|
+
const countSchema = z.int().min(0).max(2147483647);
|
|
868
|
+
const costUsdSchema = z.number().min(0).max(maxCostUsd);
|
|
869
|
+
const instantSchema$2 = z.iso.datetime();
|
|
870
|
+
const workerEnvelope = (event) => ({
|
|
871
|
+
ts: instantSchema$2,
|
|
872
|
+
level: agentEventLevelSchema,
|
|
873
|
+
event: z.literal(event)
|
|
874
|
+
});
|
|
875
|
+
const roleEnvelope = (event) => ({
|
|
876
|
+
...workerEnvelope(event),
|
|
877
|
+
agent: agentRoleSchema.optional()
|
|
878
|
+
});
|
|
879
|
+
const agentEventSchema = z.discriminatedUnion("event", [
|
|
880
|
+
z.looseObject({
|
|
881
|
+
...workerEnvelope("worker.started"),
|
|
882
|
+
version: z.string(),
|
|
883
|
+
claudeVersion: z.string().optional(),
|
|
884
|
+
nodeVersion: z.string(),
|
|
885
|
+
authKind: authKindSchema,
|
|
886
|
+
pid: countSchema,
|
|
887
|
+
projectDir: z.string(),
|
|
888
|
+
paused: z.boolean().optional()
|
|
889
|
+
}),
|
|
890
|
+
z.looseObject({
|
|
891
|
+
...workerEnvelope("worker.stopped"),
|
|
892
|
+
signal: z.string().optional(),
|
|
893
|
+
drained: z.boolean().optional(),
|
|
894
|
+
forced: z.boolean().optional()
|
|
895
|
+
}),
|
|
896
|
+
z.looseObject({
|
|
897
|
+
...workerEnvelope("worker.draining"),
|
|
898
|
+
reason: z.string(),
|
|
899
|
+
timeoutMs: countSchema.optional()
|
|
900
|
+
}),
|
|
901
|
+
z.looseObject({
|
|
902
|
+
...workerEnvelope("update.available"),
|
|
903
|
+
from: z.string(),
|
|
904
|
+
to: z.string(),
|
|
905
|
+
installError: z.string().optional()
|
|
906
|
+
}),
|
|
907
|
+
z.looseObject({
|
|
908
|
+
...workerEnvelope("update.installed"),
|
|
909
|
+
from: z.string(),
|
|
910
|
+
to: z.string()
|
|
911
|
+
}),
|
|
912
|
+
z.looseObject({
|
|
913
|
+
...roleEnvelope("control.changed"),
|
|
914
|
+
state: agentControlStateSchema
|
|
915
|
+
}),
|
|
916
|
+
z.looseObject({
|
|
917
|
+
...roleEnvelope("cycle.started"),
|
|
918
|
+
cycle: countSchema
|
|
919
|
+
}),
|
|
920
|
+
z.looseObject({
|
|
921
|
+
...roleEnvelope("cycle.finished"),
|
|
922
|
+
cycle: countSchema,
|
|
923
|
+
ok: z.boolean(),
|
|
924
|
+
durationMs: countSchema,
|
|
925
|
+
costUsd: costUsdSchema.optional(),
|
|
926
|
+
addedTasks: countSchema,
|
|
927
|
+
skippedDuplicates: countSchema,
|
|
928
|
+
sessionId: z.string().optional(),
|
|
929
|
+
error: z.string().optional()
|
|
930
|
+
}),
|
|
931
|
+
z.looseObject({
|
|
932
|
+
...roleEnvelope("monitor.offHours"),
|
|
933
|
+
resumeAt: instantSchema$2
|
|
934
|
+
}),
|
|
935
|
+
z.looseObject({
|
|
936
|
+
...roleEnvelope("monitor.limitWait"),
|
|
937
|
+
resumeAt: instantSchema$2
|
|
938
|
+
}),
|
|
939
|
+
z.looseObject({
|
|
940
|
+
...roleEnvelope("monitor.sleeping"),
|
|
941
|
+
nextCycleAt: instantSchema$2
|
|
942
|
+
}),
|
|
943
|
+
z.looseObject({
|
|
944
|
+
...roleEnvelope("monitor.authBlocked"),
|
|
945
|
+
reason: z.string()
|
|
946
|
+
}),
|
|
947
|
+
z.looseObject({
|
|
948
|
+
...roleEnvelope("monitor.admissionWait"),
|
|
949
|
+
ticket: z.string(),
|
|
950
|
+
since: instantSchema$2,
|
|
951
|
+
seq: countSchema
|
|
952
|
+
}),
|
|
953
|
+
z.looseObject({
|
|
954
|
+
...roleEnvelope("executor.admissionWait"),
|
|
955
|
+
ticket: z.string(),
|
|
956
|
+
since: instantSchema$2,
|
|
957
|
+
seq: countSchema,
|
|
958
|
+
taskId: z.string()
|
|
959
|
+
}),
|
|
960
|
+
z.looseObject({
|
|
961
|
+
...roleEnvelope("admission.granted"),
|
|
962
|
+
ticket: z.string(),
|
|
963
|
+
seq: countSchema
|
|
964
|
+
}),
|
|
965
|
+
z.looseObject({
|
|
966
|
+
...roleEnvelope("admission.released"),
|
|
967
|
+
ticket: z.string(),
|
|
968
|
+
seq: countSchema
|
|
969
|
+
}),
|
|
970
|
+
z.looseObject({
|
|
971
|
+
...roleEnvelope("admission.cancelled"),
|
|
972
|
+
ticket: z.string(),
|
|
973
|
+
seq: countSchema
|
|
974
|
+
}),
|
|
975
|
+
z.looseObject({
|
|
976
|
+
...roleEnvelope("task.started"),
|
|
977
|
+
taskId: z.string(),
|
|
978
|
+
title: z.string()
|
|
979
|
+
}),
|
|
980
|
+
z.looseObject({
|
|
981
|
+
...roleEnvelope("task.finished"),
|
|
982
|
+
taskId: z.string(),
|
|
983
|
+
title: z.string(),
|
|
984
|
+
ok: z.boolean(),
|
|
985
|
+
durationMs: countSchema,
|
|
986
|
+
costUsd: costUsdSchema.optional(),
|
|
987
|
+
numTurns: countSchema.optional(),
|
|
988
|
+
willRetry: z.boolean().optional(),
|
|
989
|
+
attempt: countSchema.optional(),
|
|
990
|
+
maxAttempts: countSchema.optional(),
|
|
991
|
+
sessionId: z.string().optional(),
|
|
992
|
+
error: z.string().optional()
|
|
993
|
+
}),
|
|
994
|
+
z.looseObject({
|
|
995
|
+
...roleEnvelope("task.retryScheduled"),
|
|
996
|
+
taskId: z.string(),
|
|
997
|
+
resumeAt: instantSchema$2
|
|
998
|
+
}),
|
|
999
|
+
z.looseObject(roleEnvelope("executor.waiting")),
|
|
1000
|
+
z.looseObject({
|
|
1001
|
+
...roleEnvelope("executor.limitWait"),
|
|
1002
|
+
resumeAt: instantSchema$2
|
|
1003
|
+
}),
|
|
1004
|
+
z.looseObject({
|
|
1005
|
+
...roleEnvelope("executor.authBlocked"),
|
|
1006
|
+
reason: z.string()
|
|
1007
|
+
}),
|
|
1008
|
+
z.looseObject({
|
|
1009
|
+
...roleEnvelope("summary.started"),
|
|
1010
|
+
taskId: z.string()
|
|
1011
|
+
}),
|
|
1012
|
+
z.looseObject({
|
|
1013
|
+
...roleEnvelope("summary.finished"),
|
|
1014
|
+
taskId: z.string(),
|
|
1015
|
+
ok: z.boolean(),
|
|
1016
|
+
durationMs: countSchema,
|
|
1017
|
+
costUsd: costUsdSchema.optional(),
|
|
1018
|
+
sessionId: z.string().optional(),
|
|
1019
|
+
error: z.string().optional()
|
|
1020
|
+
}),
|
|
1021
|
+
z.looseObject({
|
|
1022
|
+
...roleEnvelope("session.init"),
|
|
1023
|
+
model: z.string(),
|
|
1024
|
+
sessionId: z.string(),
|
|
1025
|
+
taskId: z.string().optional(),
|
|
1026
|
+
cycle: countSchema.optional()
|
|
1027
|
+
}),
|
|
1028
|
+
z.looseObject({
|
|
1029
|
+
...roleEnvelope("session.stderr"),
|
|
1030
|
+
line: z.string()
|
|
1031
|
+
}),
|
|
1032
|
+
z.looseObject({
|
|
1033
|
+
...roleEnvelope("session.procError"),
|
|
1034
|
+
message: z.string()
|
|
1035
|
+
}),
|
|
1036
|
+
z.looseObject({
|
|
1037
|
+
...roleEnvelope("session.killed"),
|
|
1038
|
+
reason: z.string()
|
|
1039
|
+
}),
|
|
1040
|
+
z.looseObject({
|
|
1041
|
+
...roleEnvelope("session.text"),
|
|
1042
|
+
text: z.string()
|
|
1043
|
+
}),
|
|
1044
|
+
z.looseObject({
|
|
1045
|
+
...roleEnvelope("session.tool"),
|
|
1046
|
+
tool: z.string(),
|
|
1047
|
+
detail: z.string()
|
|
1048
|
+
}),
|
|
1049
|
+
z.looseObject({
|
|
1050
|
+
...roleEnvelope("session.toolError"),
|
|
1051
|
+
output: z.string()
|
|
1052
|
+
})
|
|
1053
|
+
]);
|
|
1054
|
+
z.looseObject({
|
|
1055
|
+
ts: instantSchema$2,
|
|
1056
|
+
level: agentEventLevelSchema,
|
|
1057
|
+
agent: agentRoleSchema.optional(),
|
|
1058
|
+
event: z.string().min(1).max(64)
|
|
1059
|
+
});
|
|
1060
|
+
agentEventSchema.options.map((option) => option.shape.event.value);
|
|
1061
|
+
({
|
|
1062
|
+
rawLog: "log.raw",
|
|
1063
|
+
rejectedLog: "log.rejected",
|
|
1064
|
+
throttledLog: "log.throttled",
|
|
1065
|
+
stateChanged: "platform.stateChanged",
|
|
1066
|
+
crashLooping: "platform.crashLooping",
|
|
1067
|
+
provisioned: "platform.provisioned",
|
|
1068
|
+
sandboxStarted: "platform.sandboxStarted",
|
|
1069
|
+
sandboxStopped: "platform.sandboxStopped"
|
|
1070
|
+
}).rawLog;
|
|
1060
1071
|
const agentStateSchema = z.enum([
|
|
1061
1072
|
"created",
|
|
1062
1073
|
"provisioning",
|
|
@@ -1561,9 +1572,32 @@ const nodeSchema = z.object({
|
|
|
1561
1572
|
agentCount: z.int().nonnegative(),
|
|
1562
1573
|
connectedAt: z.iso.datetime().nullable(),
|
|
1563
1574
|
lastSeenAt: z.iso.datetime().nullable(),
|
|
1575
|
+
offlineSince: z.iso.datetime().nullable(),
|
|
1564
1576
|
registrationExpiresAt: z.iso.datetime().nullable(),
|
|
1565
1577
|
createdAt: z.iso.datetime()
|
|
1566
1578
|
}).meta({ id: "Node" });
|
|
1579
|
+
const nodeUpdateSchema = z.object({
|
|
1580
|
+
state: z.enum([
|
|
1581
|
+
"current",
|
|
1582
|
+
"waiting",
|
|
1583
|
+
"updating",
|
|
1584
|
+
"failed",
|
|
1585
|
+
"unmanaged"
|
|
1586
|
+
]),
|
|
1587
|
+
target: z.string().nullable(),
|
|
1588
|
+
sessions: z.int().nonnegative(),
|
|
1589
|
+
since: z.iso.datetime().nullable(),
|
|
1590
|
+
last: z.object({
|
|
1591
|
+
outcome: z.enum([
|
|
1592
|
+
"updated",
|
|
1593
|
+
"rolledBack",
|
|
1594
|
+
"failed"
|
|
1595
|
+
]),
|
|
1596
|
+
version: z.string().nullable(),
|
|
1597
|
+
reason: z.string().nullable(),
|
|
1598
|
+
at: z.iso.datetime()
|
|
1599
|
+
}).nullable()
|
|
1600
|
+
}).meta({ id: "NodeUpdate" });
|
|
1567
1601
|
const nodeAgentSchema = z.object({
|
|
1568
1602
|
id: idSchema,
|
|
1569
1603
|
name: z.string(),
|
|
@@ -1609,7 +1643,8 @@ const nodeRuntimeSchema = z.object({
|
|
|
1609
1643
|
}).meta({ id: "NodeRuntime" });
|
|
1610
1644
|
nodeSchema.extend({
|
|
1611
1645
|
agents: z.array(nodeAgentSchema),
|
|
1612
|
-
runtime: nodeRuntimeSchema.nullable()
|
|
1646
|
+
runtime: nodeRuntimeSchema.nullable(),
|
|
1647
|
+
update: nodeUpdateSchema.nullable()
|
|
1613
1648
|
}).meta({ id: "NodeDetail" });
|
|
1614
1649
|
z.object({ items: z.array(nodeSchema) }).meta({ id: "NodeList" });
|
|
1615
1650
|
z.object({ nodeId: idSchema });
|
|
@@ -1825,6 +1860,8 @@ const auditActionSchema = z.enum([
|
|
|
1825
1860
|
"agent.updated",
|
|
1826
1861
|
"agent.deleted",
|
|
1827
1862
|
"agent.action",
|
|
1863
|
+
"agent.backup_requested",
|
|
1864
|
+
"agent.restored",
|
|
1828
1865
|
"platform.settings_updated",
|
|
1829
1866
|
"runtime.sessions_updated",
|
|
1830
1867
|
"platform.upgrade_requested",
|
|
@@ -2464,6 +2501,43 @@ z.object({
|
|
|
2464
2501
|
z.object({ code: errorCodeSchema })
|
|
2465
2502
|
])
|
|
2466
2503
|
}).meta({ id: "AgentEventFrame" });
|
|
2504
|
+
//#endregion
|
|
2505
|
+
//#region ../../packages/contracts/src/agent-backups.ts
|
|
2506
|
+
const agentBackupTriggers = ["nightly", "manual"];
|
|
2507
|
+
const agentBackupSchema = z.object({
|
|
2508
|
+
id: idSchema,
|
|
2509
|
+
agentId: idSchema,
|
|
2510
|
+
nodeId: idSchema,
|
|
2511
|
+
nodeName: z.string().nullable(),
|
|
2512
|
+
trigger: z.enum(agentBackupTriggers),
|
|
2513
|
+
kikimoraVersion: z.string(),
|
|
2514
|
+
entries: z.int().nonnegative(),
|
|
2515
|
+
sizeBytes: z.int().nonnegative(),
|
|
2516
|
+
createdAt: z.iso.datetime(),
|
|
2517
|
+
expiresAt: z.iso.datetime()
|
|
2518
|
+
}).meta({ id: "AgentBackup" });
|
|
2519
|
+
z.object({
|
|
2520
|
+
items: z.array(agentBackupSchema),
|
|
2521
|
+
retentionDays: z.int().positive()
|
|
2522
|
+
}).meta({ id: "AgentBackupList" });
|
|
2523
|
+
z.object({ state: z.literal("requested") }).meta({ id: "AgentBackupRequested" });
|
|
2524
|
+
z.object({
|
|
2525
|
+
id: idSchema,
|
|
2526
|
+
backupId: idSchema
|
|
2527
|
+
});
|
|
2528
|
+
z.object({
|
|
2529
|
+
name: agentNameSchema,
|
|
2530
|
+
slug: slugSchema.optional(),
|
|
2531
|
+
nodeId: idSchema
|
|
2532
|
+
}).meta({ id: "RestoreAgentBody" });
|
|
2533
|
+
z.object({ agentId: idSchema }).meta({ id: "RestoredAgent" });
|
|
2534
|
+
z.object({
|
|
2535
|
+
agentId: idSchema,
|
|
2536
|
+
trigger: z.enum(agentBackupTriggers),
|
|
2537
|
+
kikimoraVersion: z.string().min(1).max(64),
|
|
2538
|
+
entries: z.coerce.number().int().nonnegative()
|
|
2539
|
+
});
|
|
2540
|
+
z.object({ backupId: idSchema });
|
|
2467
2541
|
[
|
|
2468
2542
|
"application/vnd.oci.image.index.v1+json",
|
|
2469
2543
|
"application/vnd.oci.image.manifest.v1+json",
|
|
@@ -3644,6 +3718,116 @@ const createWhoamiCommand = (adaptersOf) => defineCommand({
|
|
|
3644
3718
|
}
|
|
3645
3719
|
});
|
|
3646
3720
|
//#endregion
|
|
3721
|
+
//#region src/render/body.ts
|
|
3722
|
+
const isTerminal$1 = (stream) => stream.isTTY === true;
|
|
3723
|
+
const pipedText$1 = async (stream) => {
|
|
3724
|
+
const chunks = [];
|
|
3725
|
+
for await (const chunk of stream) chunks.push(Buffer.from(chunk));
|
|
3726
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
3727
|
+
};
|
|
3728
|
+
const fileOptions = { file: { type: "string" } };
|
|
3729
|
+
const fileUsage = (what) => [["--file <path>", `read ${what} from a JSON file, or from stdin with -`]];
|
|
3730
|
+
const documentOf = async (run, file) => {
|
|
3731
|
+
const text = file === "-" ? isTerminal$1(run.context.io.stdin) ? "" : await pipedText$1(run.context.io.stdin) : await readFileOrRefuse(resolve(run.context.workingDirectory, file));
|
|
3732
|
+
if (text.trim() === "") throw new UsageError(file === "-" ? "Nothing is piped on stdin." : `${file} is empty.`);
|
|
3733
|
+
try {
|
|
3734
|
+
return JSON.parse(text);
|
|
3735
|
+
} catch {
|
|
3736
|
+
throw new CliError(`${file === "-" ? "stdin" : file} is not JSON.`);
|
|
3737
|
+
}
|
|
3738
|
+
};
|
|
3739
|
+
const readFileOrRefuse = async (path) => {
|
|
3740
|
+
try {
|
|
3741
|
+
return await readFile(path, "utf8");
|
|
3742
|
+
} catch {
|
|
3743
|
+
throw new CliError(`${path} could not be read.`);
|
|
3744
|
+
}
|
|
3745
|
+
};
|
|
3746
|
+
const validated = (schema, document, what) => {
|
|
3747
|
+
const parsed = schema.safeParse(document);
|
|
3748
|
+
if (!parsed.success) throw new CliError(`That is not ${what}.`, z.prettifyError(parsed.error));
|
|
3749
|
+
};
|
|
3750
|
+
//#endregion
|
|
3751
|
+
//#region src/commands/nodes/shared.ts
|
|
3752
|
+
const nodeHints = {
|
|
3753
|
+
...organizationHints,
|
|
3754
|
+
"node.not_found": "kcloud nodes list shows the nodes of the organization.",
|
|
3755
|
+
"node.name_taken": "Another node of the organization has that name.",
|
|
3756
|
+
"node.in_use": "Delete the agents placed on it first; kcloud nodes show <node> lists them.",
|
|
3757
|
+
"node.registration_used": "The node has joined and holds its own credential; it needs no token."
|
|
3758
|
+
};
|
|
3759
|
+
const splitHints = {
|
|
3760
|
+
...nodeHints,
|
|
3761
|
+
"auth.forbidden": "Splitting a node takes an admin of its organization.",
|
|
3762
|
+
"node.host_unknown": "The node reports its CPUs and memory once its service runs; kcloud nodes show <node> shows its state.",
|
|
3763
|
+
"node.split_exceeds_host": "The split asks for more than the host has; kcloud nodes split <node> shows the host.",
|
|
3764
|
+
"concurrency.conflict": "Someone changed the node meanwhile; run the command again."
|
|
3765
|
+
};
|
|
3766
|
+
const nodesOf = async (api, organizationId) => dataOf("list the nodes", await api.GET("/organizations/{organizationId}/nodes", { params: { path: { organizationId } } }), nodeHints).items;
|
|
3767
|
+
const namesOf = (nodes) => nodes.map(({ name }) => name).join(", ");
|
|
3768
|
+
const nodeIn = (nodes, reference, organization) => {
|
|
3769
|
+
const found = nodes.find((node) => node.id === reference.toLowerCase() || node.name === reference);
|
|
3770
|
+
if (found === void 0) throw new CliError(`There is no node ${reference} in ${organization}.`, nodes.length === 0 ? "kcloud nodes create <name> creates one." : `kcloud nodes list shows ${namesOf(nodes)}.`);
|
|
3771
|
+
return found;
|
|
3772
|
+
};
|
|
3773
|
+
const resolveNode = async (scope, reference) => {
|
|
3774
|
+
const organization = await scope.organization();
|
|
3775
|
+
if (isId(reference)) return {
|
|
3776
|
+
organizationId: organization.id,
|
|
3777
|
+
nodeId: reference
|
|
3778
|
+
};
|
|
3779
|
+
const node = nodeIn(await nodesOf(scope.api, organization.id), reference, organization.slug);
|
|
3780
|
+
return {
|
|
3781
|
+
organizationId: organization.id,
|
|
3782
|
+
nodeId: node.id
|
|
3783
|
+
};
|
|
3784
|
+
};
|
|
3785
|
+
const nodeDetailOf = async (api, { organizationId, nodeId }) => dataOf("describe the node", await api.GET("/organizations/{organizationId}/nodes/{nodeId}", { params: { path: {
|
|
3786
|
+
organizationId,
|
|
3787
|
+
nodeId
|
|
3788
|
+
} } }), nodeHints);
|
|
3789
|
+
const createdNode = async (api, organizationId, name) => {
|
|
3790
|
+
const body = { name };
|
|
3791
|
+
validated(createNodeBodySchema, body, "a node");
|
|
3792
|
+
return dataOf("create the node", await api.POST("/organizations/{organizationId}/nodes", {
|
|
3793
|
+
params: { path: { organizationId } },
|
|
3794
|
+
body
|
|
3795
|
+
}), nodeHints);
|
|
3796
|
+
};
|
|
3797
|
+
const renewedRegistration = async (api, { organizationId, nodeId }) => dataOf("issue a registration token for the node", await api.POST("/organizations/{organizationId}/nodes/{nodeId}/registration", { params: { path: {
|
|
3798
|
+
organizationId,
|
|
3799
|
+
nodeId
|
|
3800
|
+
} } }), nodeHints);
|
|
3801
|
+
const deleteNode = async (api, { organizationId, nodeId }) => {
|
|
3802
|
+
doneWith("delete the node", await api.DELETE("/organizations/{organizationId}/nodes/{nodeId}", { params: { path: {
|
|
3803
|
+
organizationId,
|
|
3804
|
+
nodeId
|
|
3805
|
+
} } }), nodeHints);
|
|
3806
|
+
};
|
|
3807
|
+
const splitNode = async (api, { organizationId, nodeId }, revision, split) => dataOf(split === null ? "clear the split of the node" : "split the node", await api.PUT("/organizations/{organizationId}/nodes/{nodeId}/split", {
|
|
3808
|
+
params: { path: {
|
|
3809
|
+
organizationId,
|
|
3810
|
+
nodeId
|
|
3811
|
+
} },
|
|
3812
|
+
body: {
|
|
3813
|
+
revision,
|
|
3814
|
+
split
|
|
3815
|
+
}
|
|
3816
|
+
}), splitHints);
|
|
3817
|
+
const nodeUsage = [["", "a node is named by its name or its id"]];
|
|
3818
|
+
//#endregion
|
|
3819
|
+
//#region src/commands/projects/shared.ts
|
|
3820
|
+
const defaultProjectColor = "#7c86ff";
|
|
3821
|
+
const projectHint = {
|
|
3822
|
+
"project.not_found": "kcloud projects list shows the projects you are a member of.",
|
|
3823
|
+
"project.slug_taken": "Another project of the organization has that slug.",
|
|
3824
|
+
"project.invalid_slug": "A slug holds lowercase letters, digits and hyphens.",
|
|
3825
|
+
"project.not_empty": "Delete its agents, credentials, repositories and tools first.",
|
|
3826
|
+
"project.member_not_found": "kcloud projects members list <project> shows them.",
|
|
3827
|
+
"project.member_must_be_active": "kcloud orgs members enable <member> enables the account first."
|
|
3828
|
+
};
|
|
3829
|
+
const projectOf = async (api, id) => dataOf("describe the project", await api.GET("/projects/{id}", { params: { path: { id } } }), projectHint);
|
|
3830
|
+
//#endregion
|
|
3647
3831
|
//#region src/commands/run.ts
|
|
3648
3832
|
const runOf = async (adaptersOf, { settings, context }) => {
|
|
3649
3833
|
const adapters = adaptersOf(context);
|
|
@@ -3682,62 +3866,187 @@ const agentFrom = async (run, reference, project) => {
|
|
|
3682
3866
|
return resolveAgentId(run, reference, project);
|
|
3683
3867
|
};
|
|
3684
3868
|
//#endregion
|
|
3685
|
-
//#region src/commands/agents/
|
|
3686
|
-
const
|
|
3869
|
+
//#region src/commands/agents/backups.ts
|
|
3870
|
+
const backupsListHint = "kcloud agents backups list <agent> shows the backups of the agent and their ids.";
|
|
3871
|
+
const backupHints = {
|
|
3687
3872
|
...agentHints,
|
|
3688
|
-
"
|
|
3689
|
-
"
|
|
3690
|
-
"
|
|
3691
|
-
"
|
|
3692
|
-
"runtime.monitor_check_unsupported": "Move the agent to Kikimora 0.10.0 or newer with kcloud agents set, then kcloud agents update."
|
|
3873
|
+
"agent.backup_unavailable": "Backups need the Spaces of the platform and a node that can make them. An administrator of the platform configures the Spaces.",
|
|
3874
|
+
"agent.backup_without_export": "The Kikimora release of the agent cannot export its memory. Move it to 0.12.0 or newer with kcloud agents set, then kcloud agents update.",
|
|
3875
|
+
"agent.backup_in_progress": "A backup of the agent is running already. kcloud agents backups list <agent> shows it once the node has sent it.",
|
|
3876
|
+
"node.offline": "The node of the agent is not connected, so it cannot make a backup. kcloud nodes show <node> tells when it was last seen."
|
|
3693
3877
|
};
|
|
3694
|
-
const
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3878
|
+
const restoreHints = {
|
|
3879
|
+
...agentHints,
|
|
3880
|
+
"node.not_found": "kcloud nodes list shows the nodes of the organization.",
|
|
3881
|
+
"node.offline": "The node is not connected. kcloud nodes show <node> tells when it was last seen.",
|
|
3882
|
+
"agent.backup_not_found": `The backup does not exist or has expired. ${backupsListHint}`,
|
|
3883
|
+
"agent.slug_taken": "Another agent of the project has that slug. Pass another with --slug.",
|
|
3884
|
+
"agent.invalid_slug": "A slug holds lowercase letters, digits and hyphens."
|
|
3885
|
+
};
|
|
3886
|
+
const backupsOf = async (api, agentId) => dataOf("list the backups of the agent", await api.GET("/agents/{id}/backups", { params: { path: { id: agentId } } }), backupHints);
|
|
3887
|
+
const requestedBackup = async (api, agentId) => dataOf("back the agent up", await api.POST("/agents/{id}/backups", { params: { path: { id: agentId } } }), backupHints);
|
|
3888
|
+
const nodeIdFor = async (api, agentId, node) => {
|
|
3889
|
+
if (isId(node)) return node;
|
|
3890
|
+
const agent = dataOf("describe the agent", await api.GET("/agents/{id}", { params: { path: { id: agentId } } }), agentHints);
|
|
3891
|
+
const project = await projectOf(api, agent.projectId);
|
|
3892
|
+
const nodes = await nodesOf(api, project.organizationId);
|
|
3893
|
+
return nodeIn(nodes, node, `the organization of ${project.slug}`).id;
|
|
3894
|
+
};
|
|
3895
|
+
const restoredAgent = async (api, agentId, { backupId, name, node, slug }) => {
|
|
3896
|
+
const nodeId = await nodeIdFor(api, agentId, node);
|
|
3897
|
+
return dataOf("restore the agent from the backup", await api.POST("/agents/{id}/backups/{backupId}/restore", {
|
|
3898
|
+
params: { path: {
|
|
3899
|
+
id: agentId,
|
|
3900
|
+
backupId
|
|
3901
|
+
} },
|
|
3902
|
+
body: {
|
|
3903
|
+
name,
|
|
3904
|
+
nodeId,
|
|
3905
|
+
...slug === void 0 ? {} : { slug }
|
|
3906
|
+
}
|
|
3907
|
+
}), restoreHints);
|
|
3908
|
+
};
|
|
3909
|
+
const keptFor = (days) => `A backup is kept for ${String(days)} ${days === 1 ? "day" : "days"}.`;
|
|
3910
|
+
const backupsTable = ({ items, retentionDays }) => items.length === 0 ? `The agent has no backup yet. ${keptFor(retentionDays)}` : [renderTable([
|
|
3911
|
+
{ header: "CREATED" },
|
|
3912
|
+
{ header: "TRIGGER" },
|
|
3913
|
+
{ header: "NODE" },
|
|
3914
|
+
{ header: "KIKIMORA" },
|
|
3915
|
+
{
|
|
3916
|
+
header: "ENTRIES",
|
|
3917
|
+
align: "right"
|
|
3918
|
+
},
|
|
3919
|
+
{
|
|
3920
|
+
header: "SIZE",
|
|
3921
|
+
align: "right"
|
|
3922
|
+
},
|
|
3923
|
+
{ header: "EXPIRES" },
|
|
3924
|
+
{ header: "ID" }
|
|
3925
|
+
], items.map((backup) => [
|
|
3926
|
+
backup.createdAt,
|
|
3927
|
+
backup.trigger,
|
|
3928
|
+
backup.nodeName ?? backup.nodeId,
|
|
3929
|
+
backup.kikimoraVersion,
|
|
3930
|
+
String(backup.entries),
|
|
3931
|
+
bytesOf(backup.sizeBytes),
|
|
3932
|
+
backup.expiresAt,
|
|
3933
|
+
backup.id
|
|
3934
|
+
])), keptFor(retentionDays)].join("\n");
|
|
3935
|
+
const restoreRequestOf = ({ backup, name, node, slug }) => {
|
|
3936
|
+
if (backup === void 0 || name === void 0 || node === void 0 || node.trim() === "") {
|
|
3937
|
+
const missing = [
|
|
3938
|
+
...backup === void 0 ? ["--backup <id>"] : [],
|
|
3939
|
+
...name === void 0 ? ["--name <name>"] : [],
|
|
3940
|
+
...node === void 0 || node.trim() === "" ? ["--node <node>"] : []
|
|
3941
|
+
];
|
|
3942
|
+
throw new UsageError(`agents restore needs ${listOf(missing)}.`, backupsListHint);
|
|
3943
|
+
}
|
|
3944
|
+
if (!isId(backup)) throw new UsageError(`--backup ${backup} is not the id of a backup.`, backupsListHint);
|
|
3945
|
+
const named = agentNameSchema.safeParse(name);
|
|
3946
|
+
if (!named.success) throw new UsageError(`--name needs from 1 to ${String(80)} characters.`);
|
|
3947
|
+
if (slug !== void 0 && !slugSchema.safeParse(slug).success) throw new UsageError(`--slug ${slug} is not a slug.`, "A slug holds lowercase letters, digits and hyphens, at most 40 of them.");
|
|
3948
|
+
return {
|
|
3949
|
+
backupId: backup,
|
|
3950
|
+
name: named.data,
|
|
3951
|
+
node: node.trim(),
|
|
3952
|
+
slug
|
|
3953
|
+
};
|
|
3954
|
+
};
|
|
3955
|
+
const backupsCommand = (adaptersOf) => defineGroup({
|
|
3956
|
+
name: "backups",
|
|
3957
|
+
summary: "The backups of the memory of an agent.",
|
|
3958
|
+
usage: "agents backups <command>",
|
|
3959
|
+
subcommands: [defineCommand({
|
|
3960
|
+
name: "list",
|
|
3961
|
+
summary: "List the backups of an agent, the newest first.",
|
|
3962
|
+
usage: "agents backups list <agent> [--project <name>]",
|
|
3963
|
+
optionsUsage: agentUsage,
|
|
3964
|
+
options: agentOption,
|
|
3965
|
+
positionals: {
|
|
3966
|
+
min: 1,
|
|
3967
|
+
max: 1
|
|
3968
|
+
},
|
|
3969
|
+
run: async (invocation) => {
|
|
3970
|
+
const run = await runOf(adaptersOf, invocation);
|
|
3971
|
+
const agentId = await agentFrom(run, invocation.positionals[0], invocation.values.project);
|
|
3972
|
+
return printed(run, await backupsOf(run.api, agentId), backupsTable);
|
|
3973
|
+
}
|
|
3974
|
+
}), defineCommand({
|
|
3975
|
+
name: "create",
|
|
3976
|
+
summary: "Ask the node of an agent for a backup now.",
|
|
3977
|
+
usage: "agents backups create <agent> [--project <name>]",
|
|
3978
|
+
optionsUsage: agentUsage,
|
|
3979
|
+
options: agentOption,
|
|
3980
|
+
positionals: {
|
|
3981
|
+
min: 1,
|
|
3982
|
+
max: 1
|
|
3983
|
+
},
|
|
3984
|
+
run: async (invocation) => {
|
|
3985
|
+
const run = await runOf(adaptersOf, invocation);
|
|
3986
|
+
const reference = invocation.positionals[0] ?? "";
|
|
3987
|
+
const agentId = await agentFrom(run, reference, invocation.values.project);
|
|
3988
|
+
return printed(run, await requestedBackup(run.api, agentId), () => `Backup of ${reference} requested. kcloud agents backups list ${reference} shows it once the node has sent it.`);
|
|
3989
|
+
}
|
|
3990
|
+
})]
|
|
3991
|
+
});
|
|
3992
|
+
const restoreCommand = (adaptersOf) => defineCommand({
|
|
3993
|
+
name: "restore",
|
|
3994
|
+
summary: "Create a new agent from a backup of an agent.",
|
|
3995
|
+
usage: "agents restore <agent> --backup <id> --name <name> --node <node> [--slug <slug>] [--project <name>]",
|
|
3996
|
+
optionsUsage: [
|
|
3997
|
+
...agentUsage,
|
|
3998
|
+
["--backup <id>", "the backup, by its id from kcloud agents backups list"],
|
|
3999
|
+
["--name <name>", "the name of the new agent"],
|
|
4000
|
+
["--node <node>", "the node the new agent runs on, by name or id"],
|
|
4001
|
+
["--slug <slug>", "the slug of the new agent (default: one made from its name)"],
|
|
4002
|
+
["", "the new agent joins the project of the agent with its configuration and the memory of the backup"]
|
|
4003
|
+
],
|
|
4004
|
+
options: {
|
|
4005
|
+
...agentOption,
|
|
4006
|
+
backup: { type: "string" },
|
|
4007
|
+
name: { type: "string" },
|
|
4008
|
+
node: { type: "string" },
|
|
4009
|
+
slug: { type: "string" }
|
|
4010
|
+
},
|
|
3700
4011
|
positionals: {
|
|
3701
4012
|
min: 1,
|
|
3702
4013
|
max: 1
|
|
3703
4014
|
},
|
|
3704
4015
|
run: async (invocation) => {
|
|
4016
|
+
const request = restoreRequestOf(invocation.values);
|
|
3705
4017
|
const run = await runOf(adaptersOf, invocation);
|
|
3706
|
-
const
|
|
3707
|
-
const
|
|
3708
|
-
return printed(run,
|
|
4018
|
+
const reference = invocation.positionals[0] ?? "";
|
|
4019
|
+
const agentId = await agentFrom(run, reference, invocation.values.project);
|
|
4020
|
+
return printed(run, await restoredAgent(run.api, agentId, request), (restored) => [`Restored ${request.name} as ${restored.agentId} from backup ${request.backupId} of ${reference}.`, `kcloud agents start ${restored.agentId} starts it once it is provisioned.`].join("\n"));
|
|
3709
4021
|
}
|
|
3710
4022
|
});
|
|
3711
4023
|
//#endregion
|
|
3712
|
-
//#region src/
|
|
3713
|
-
const
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
const fileUsage = (what) => [["--file <path>", `read ${what} from a JSON file, or from stdin with -`]];
|
|
3721
|
-
const documentOf = async (run, file) => {
|
|
3722
|
-
const text = file === "-" ? isTerminal$1(run.context.io.stdin) ? "" : await pipedText$1(run.context.io.stdin) : await readFileOrRefuse(resolve(run.context.workingDirectory, file));
|
|
3723
|
-
if (text.trim() === "") throw new UsageError(file === "-" ? "Nothing is piped on stdin." : `${file} is empty.`);
|
|
3724
|
-
try {
|
|
3725
|
-
return JSON.parse(text);
|
|
3726
|
-
} catch {
|
|
3727
|
-
throw new CliError(`${file === "-" ? "stdin" : file} is not JSON.`);
|
|
3728
|
-
}
|
|
3729
|
-
};
|
|
3730
|
-
const readFileOrRefuse = async (path) => {
|
|
3731
|
-
try {
|
|
3732
|
-
return await readFile(path, "utf8");
|
|
3733
|
-
} catch {
|
|
3734
|
-
throw new CliError(`${path} could not be read.`);
|
|
3735
|
-
}
|
|
3736
|
-
};
|
|
3737
|
-
const validated = (schema, document, what) => {
|
|
3738
|
-
const parsed = schema.safeParse(document);
|
|
3739
|
-
if (!parsed.success) throw new CliError(`That is not ${what}.`, z.prettifyError(parsed.error));
|
|
4024
|
+
//#region src/commands/agents/check.ts
|
|
4025
|
+
const checkHints = {
|
|
4026
|
+
...agentHints,
|
|
4027
|
+
"runtime.control_unreachable": "The worker is not answering; kcloud agents show <agent> tells whether it runs.",
|
|
4028
|
+
"runtime.monitor_paused": "kcloud agents resume <agent> lets the agent work again.",
|
|
4029
|
+
"runtime.monitor_draining": "The agent stops after its current session; start it again to run a cycle.",
|
|
4030
|
+
"runtime.monitor_limited": "The Claude usage limit holds; the monitor runs its next cycle once it resets.",
|
|
4031
|
+
"runtime.monitor_check_unsupported": "Move the agent to Kikimora 0.10.0 or newer with kcloud agents set, then kcloud agents update."
|
|
3740
4032
|
};
|
|
4033
|
+
const checkCommand = (adaptersOf) => defineCommand({
|
|
4034
|
+
name: "check",
|
|
4035
|
+
summary: "Run a monitor cycle now instead of waiting for the interval.",
|
|
4036
|
+
usage: "agents check <agent> [--project <name>]",
|
|
4037
|
+
optionsUsage: agentUsage,
|
|
4038
|
+
options: agentOption,
|
|
4039
|
+
positionals: {
|
|
4040
|
+
min: 1,
|
|
4041
|
+
max: 1
|
|
4042
|
+
},
|
|
4043
|
+
run: async (invocation) => {
|
|
4044
|
+
const run = await runOf(adaptersOf, invocation);
|
|
4045
|
+
const agentId = await agentFrom(run, invocation.positionals[0], invocation.values.project);
|
|
4046
|
+
const check = dataOf("check the monitor", await run.api.POST("/agents/{id}/monitor/check", { params: { path: { id: agentId } } }), checkHints);
|
|
4047
|
+
return printed(run, check, (answer) => answer.state === "running" ? `Monitor cycle ${String(answer.cycle)} is already running.` : "Monitor cycle requested; it starts now, also outside the active hours.");
|
|
4048
|
+
}
|
|
4049
|
+
});
|
|
3741
4050
|
//#endregion
|
|
3742
4051
|
//#region src/render/confirm.ts
|
|
3743
4052
|
const confirmed = async ({ prompt, settings, question }) => {
|
|
@@ -4042,86 +4351,6 @@ const detailsOf = (agent) => [
|
|
|
4042
4351
|
]))
|
|
4043
4352
|
].join("\n");
|
|
4044
4353
|
//#endregion
|
|
4045
|
-
//#region src/commands/nodes/shared.ts
|
|
4046
|
-
const nodeHints = {
|
|
4047
|
-
...organizationHints,
|
|
4048
|
-
"node.not_found": "kcloud nodes list shows the nodes of the organization.",
|
|
4049
|
-
"node.name_taken": "Another node of the organization has that name.",
|
|
4050
|
-
"node.in_use": "Delete the agents placed on it first; kcloud nodes show <node> lists them.",
|
|
4051
|
-
"node.registration_used": "The node has joined and holds its own credential; it needs no token."
|
|
4052
|
-
};
|
|
4053
|
-
const splitHints = {
|
|
4054
|
-
...nodeHints,
|
|
4055
|
-
"auth.forbidden": "Splitting a node takes an admin of its organization.",
|
|
4056
|
-
"node.host_unknown": "The node reports its CPUs and memory once its service runs; kcloud nodes show <node> shows its state.",
|
|
4057
|
-
"node.split_exceeds_host": "The split asks for more than the host has; kcloud nodes split <node> shows the host.",
|
|
4058
|
-
"concurrency.conflict": "Someone changed the node meanwhile; run the command again."
|
|
4059
|
-
};
|
|
4060
|
-
const nodesOf = async (api, organizationId) => dataOf("list the nodes", await api.GET("/organizations/{organizationId}/nodes", { params: { path: { organizationId } } }), nodeHints).items;
|
|
4061
|
-
const namesOf = (nodes) => nodes.map(({ name }) => name).join(", ");
|
|
4062
|
-
const nodeIn = (nodes, reference, organization) => {
|
|
4063
|
-
const found = nodes.find((node) => node.id === reference.toLowerCase() || node.name === reference);
|
|
4064
|
-
if (found === void 0) throw new CliError(`There is no node ${reference} in ${organization}.`, nodes.length === 0 ? "kcloud nodes create <name> creates one." : `kcloud nodes list shows ${namesOf(nodes)}.`);
|
|
4065
|
-
return found;
|
|
4066
|
-
};
|
|
4067
|
-
const resolveNode = async (scope, reference) => {
|
|
4068
|
-
const organization = await scope.organization();
|
|
4069
|
-
if (isId(reference)) return {
|
|
4070
|
-
organizationId: organization.id,
|
|
4071
|
-
nodeId: reference
|
|
4072
|
-
};
|
|
4073
|
-
const node = nodeIn(await nodesOf(scope.api, organization.id), reference, organization.slug);
|
|
4074
|
-
return {
|
|
4075
|
-
organizationId: organization.id,
|
|
4076
|
-
nodeId: node.id
|
|
4077
|
-
};
|
|
4078
|
-
};
|
|
4079
|
-
const nodeDetailOf = async (api, { organizationId, nodeId }) => dataOf("describe the node", await api.GET("/organizations/{organizationId}/nodes/{nodeId}", { params: { path: {
|
|
4080
|
-
organizationId,
|
|
4081
|
-
nodeId
|
|
4082
|
-
} } }), nodeHints);
|
|
4083
|
-
const createdNode = async (api, organizationId, name) => {
|
|
4084
|
-
const body = { name };
|
|
4085
|
-
validated(createNodeBodySchema, body, "a node");
|
|
4086
|
-
return dataOf("create the node", await api.POST("/organizations/{organizationId}/nodes", {
|
|
4087
|
-
params: { path: { organizationId } },
|
|
4088
|
-
body
|
|
4089
|
-
}), nodeHints);
|
|
4090
|
-
};
|
|
4091
|
-
const renewedRegistration = async (api, { organizationId, nodeId }) => dataOf("issue a registration token for the node", await api.POST("/organizations/{organizationId}/nodes/{nodeId}/registration", { params: { path: {
|
|
4092
|
-
organizationId,
|
|
4093
|
-
nodeId
|
|
4094
|
-
} } }), nodeHints);
|
|
4095
|
-
const deleteNode = async (api, { organizationId, nodeId }) => {
|
|
4096
|
-
doneWith("delete the node", await api.DELETE("/organizations/{organizationId}/nodes/{nodeId}", { params: { path: {
|
|
4097
|
-
organizationId,
|
|
4098
|
-
nodeId
|
|
4099
|
-
} } }), nodeHints);
|
|
4100
|
-
};
|
|
4101
|
-
const splitNode = async (api, { organizationId, nodeId }, revision, split) => dataOf(split === null ? "clear the split of the node" : "split the node", await api.PUT("/organizations/{organizationId}/nodes/{nodeId}/split", {
|
|
4102
|
-
params: { path: {
|
|
4103
|
-
organizationId,
|
|
4104
|
-
nodeId
|
|
4105
|
-
} },
|
|
4106
|
-
body: {
|
|
4107
|
-
revision,
|
|
4108
|
-
split
|
|
4109
|
-
}
|
|
4110
|
-
}), splitHints);
|
|
4111
|
-
const nodeUsage = [["", "a node is named by its name or its id"]];
|
|
4112
|
-
//#endregion
|
|
4113
|
-
//#region src/commands/projects/shared.ts
|
|
4114
|
-
const defaultProjectColor = "#7c86ff";
|
|
4115
|
-
const projectHint = {
|
|
4116
|
-
"project.not_found": "kcloud projects list shows the projects you are a member of.",
|
|
4117
|
-
"project.slug_taken": "Another project of the organization has that slug.",
|
|
4118
|
-
"project.invalid_slug": "A slug holds lowercase letters, digits and hyphens.",
|
|
4119
|
-
"project.not_empty": "Delete its agents, credentials, repositories and tools first.",
|
|
4120
|
-
"project.member_not_found": "kcloud projects members list <project> shows them.",
|
|
4121
|
-
"project.member_must_be_active": "kcloud orgs members enable <member> enables the account first."
|
|
4122
|
-
};
|
|
4123
|
-
const projectOf = async (api, id) => dataOf("describe the project", await api.GET("/projects/{id}", { params: { path: { id } } }), projectHint);
|
|
4124
|
-
//#endregion
|
|
4125
4354
|
//#region src/commands/agents/placement.ts
|
|
4126
4355
|
const placedNodeId = async (run, { projectId, given, ask }) => {
|
|
4127
4356
|
const project = await projectOf(run.api, projectId);
|
|
@@ -5156,6 +5385,8 @@ const createAgentsCommand = (adaptersOf) => defineGroup({
|
|
|
5156
5385
|
...lifecycleCommands(adaptersOf),
|
|
5157
5386
|
checkCommand(adaptersOf),
|
|
5158
5387
|
tasksCommand(adaptersOf),
|
|
5388
|
+
backupsCommand(adaptersOf),
|
|
5389
|
+
restoreCommand(adaptersOf),
|
|
5159
5390
|
...observabilityCommands(adaptersOf),
|
|
5160
5391
|
...streamCommands(adaptersOf)
|
|
5161
5392
|
]
|
|
@@ -5788,75 +6019,6 @@ const agentTools = [
|
|
|
5788
6019
|
})
|
|
5789
6020
|
];
|
|
5790
6021
|
//#endregion
|
|
5791
|
-
//#region src/mcp/tools/credentials.ts
|
|
5792
|
-
const credentialField$1 = z.string().trim().min(1).describe("The credential, by its label or its id.");
|
|
5793
|
-
const credentialTools = [
|
|
5794
|
-
defineTool({
|
|
5795
|
-
name: "credentials_list",
|
|
5796
|
-
title: "List credentials",
|
|
5797
|
-
description: "List the Claude and GitHub credentials of a project, with their fingerprints and verification, never their secrets.",
|
|
5798
|
-
operations: ["listCredentials"],
|
|
5799
|
-
safety: "read",
|
|
5800
|
-
input: z.object({ project: projectField }),
|
|
5801
|
-
run: async ({ project }, run) => credentialsOf(run.api, await projectIdOf(run, project))
|
|
5802
|
-
}),
|
|
5803
|
-
defineTool({
|
|
5804
|
-
name: "credential_update",
|
|
5805
|
-
title: "Rename or verify a credential",
|
|
5806
|
-
description: "rename gives a credential a new label; verify asks the instance to check it against its service, and credentials_list shows the answer.",
|
|
5807
|
-
operations: ["renameCredential", "verifyCredential"],
|
|
5808
|
-
safety: "write",
|
|
5809
|
-
input: z.object({
|
|
5810
|
-
project: projectField,
|
|
5811
|
-
credential: credentialField$1,
|
|
5812
|
-
action: z.enum(["rename", "verify"]),
|
|
5813
|
-
label: z.string().trim().min(1).optional().describe("The new label, for rename.")
|
|
5814
|
-
}),
|
|
5815
|
-
run: async ({ project, credential, action, label }, run) => {
|
|
5816
|
-
const projectId = await projectIdOf(run, project);
|
|
5817
|
-
const credentialId = await resolveCredentialId(run.api, projectId, credential);
|
|
5818
|
-
if (action === "verify") {
|
|
5819
|
-
doneWith("verify the credential", await run.api.POST("/projects/{id}/credentials/{credentialId}/verify", { params: { path: {
|
|
5820
|
-
id: projectId,
|
|
5821
|
-
credentialId
|
|
5822
|
-
} } }), credentialHints);
|
|
5823
|
-
return {
|
|
5824
|
-
id: credentialId,
|
|
5825
|
-
verifying: true
|
|
5826
|
-
};
|
|
5827
|
-
}
|
|
5828
|
-
if (label === void 0) throw new UsageError("rename needs a label.");
|
|
5829
|
-
return dataOf("rename the credential", await run.api.PATCH("/projects/{id}/credentials/{credentialId}", {
|
|
5830
|
-
params: { path: {
|
|
5831
|
-
id: projectId,
|
|
5832
|
-
credentialId
|
|
5833
|
-
} },
|
|
5834
|
-
body: { label }
|
|
5835
|
-
}), credentialHints);
|
|
5836
|
-
}
|
|
5837
|
-
}),
|
|
5838
|
-
defineTool({
|
|
5839
|
-
name: "credential_delete",
|
|
5840
|
-
title: "Delete a credential",
|
|
5841
|
-
description: "Delete a credential of a project that no agent and no repository uses.",
|
|
5842
|
-
operations: ["deleteCredential"],
|
|
5843
|
-
safety: "destructive",
|
|
5844
|
-
input: z.object({
|
|
5845
|
-
project: projectField,
|
|
5846
|
-
credential: credentialField$1
|
|
5847
|
-
}),
|
|
5848
|
-
run: async ({ project, credential }, run) => {
|
|
5849
|
-
const projectId = await projectIdOf(run, project);
|
|
5850
|
-
const credentialId = await resolveCredentialId(run.api, projectId, credential);
|
|
5851
|
-
doneWith("delete the credential", await run.api.DELETE("/projects/{id}/credentials/{credentialId}", { params: { path: {
|
|
5852
|
-
id: projectId,
|
|
5853
|
-
credentialId
|
|
5854
|
-
} } }), credentialHints);
|
|
5855
|
-
return { deleted: credentialId };
|
|
5856
|
-
}
|
|
5857
|
-
})
|
|
5858
|
-
];
|
|
5859
|
-
//#endregion
|
|
5860
6022
|
//#region src/commands/nodes/split.ts
|
|
5861
6023
|
const splitFields = [
|
|
5862
6024
|
"reserve.cpus",
|
|
@@ -6242,6 +6404,117 @@ const nodeTools = [
|
|
|
6242
6404
|
})
|
|
6243
6405
|
];
|
|
6244
6406
|
//#endregion
|
|
6407
|
+
//#region src/mcp/tools/backups.ts
|
|
6408
|
+
const agentBackupTools = [
|
|
6409
|
+
defineTool({
|
|
6410
|
+
name: "agent_backups_list",
|
|
6411
|
+
title: "List the backups of an agent",
|
|
6412
|
+
description: "List the backups of the memory of an agent, the newest first, with their trigger, node, Kikimora release, entries, size and expiry, and the days a backup is kept.",
|
|
6413
|
+
operations: ["listAgentBackups"],
|
|
6414
|
+
safety: "read",
|
|
6415
|
+
input: z.object(agentFields),
|
|
6416
|
+
run: async (reference, run) => backupsOf(run.api, await agentIdOf(run, reference))
|
|
6417
|
+
}),
|
|
6418
|
+
defineTool({
|
|
6419
|
+
name: "agent_backup_request",
|
|
6420
|
+
title: "Back an agent up now",
|
|
6421
|
+
description: "Ask the node of an agent for a backup of its memory now. state requested means the node makes it, and agent_backups_list shows it once the node has sent it.",
|
|
6422
|
+
operations: ["requestAgentBackup"],
|
|
6423
|
+
safety: "write",
|
|
6424
|
+
input: z.object(agentFields),
|
|
6425
|
+
run: async (reference, run) => requestedBackup(run.api, await agentIdOf(run, reference))
|
|
6426
|
+
}),
|
|
6427
|
+
defineTool({
|
|
6428
|
+
name: "agent_restore",
|
|
6429
|
+
title: "Restore an agent from a backup",
|
|
6430
|
+
description: "Create a new agent in the project of an agent, with its configuration and the memory of one of its backups, on the node named. Returns the id of the new agent, which stays stopped until agent_action starts it.",
|
|
6431
|
+
operations: ["restoreAgentFromBackup"],
|
|
6432
|
+
safety: "write",
|
|
6433
|
+
input: z.object({
|
|
6434
|
+
...agentFields,
|
|
6435
|
+
backup: idSchema.describe("The id of the backup, from agent_backups_list."),
|
|
6436
|
+
name: agentNameSchema.describe("The name of the new agent."),
|
|
6437
|
+
slug: slugSchema.optional().describe("The slug of the new agent, instead of one made from its name."),
|
|
6438
|
+
node: nodeField
|
|
6439
|
+
}),
|
|
6440
|
+
run: async ({ backup, name, slug, node, ...reference }, run) => restoredAgent(run.api, await agentIdOf(run, reference), {
|
|
6441
|
+
backupId: backup,
|
|
6442
|
+
name,
|
|
6443
|
+
node,
|
|
6444
|
+
slug
|
|
6445
|
+
})
|
|
6446
|
+
})
|
|
6447
|
+
];
|
|
6448
|
+
//#endregion
|
|
6449
|
+
//#region src/mcp/tools/credentials.ts
|
|
6450
|
+
const credentialField$1 = z.string().trim().min(1).describe("The credential, by its label or its id.");
|
|
6451
|
+
const credentialTools = [
|
|
6452
|
+
defineTool({
|
|
6453
|
+
name: "credentials_list",
|
|
6454
|
+
title: "List credentials",
|
|
6455
|
+
description: "List the Claude and GitHub credentials of a project, with their fingerprints and verification, never their secrets.",
|
|
6456
|
+
operations: ["listCredentials"],
|
|
6457
|
+
safety: "read",
|
|
6458
|
+
input: z.object({ project: projectField }),
|
|
6459
|
+
run: async ({ project }, run) => credentialsOf(run.api, await projectIdOf(run, project))
|
|
6460
|
+
}),
|
|
6461
|
+
defineTool({
|
|
6462
|
+
name: "credential_update",
|
|
6463
|
+
title: "Rename or verify a credential",
|
|
6464
|
+
description: "rename gives a credential a new label; verify asks the instance to check it against its service, and credentials_list shows the answer.",
|
|
6465
|
+
operations: ["renameCredential", "verifyCredential"],
|
|
6466
|
+
safety: "write",
|
|
6467
|
+
input: z.object({
|
|
6468
|
+
project: projectField,
|
|
6469
|
+
credential: credentialField$1,
|
|
6470
|
+
action: z.enum(["rename", "verify"]),
|
|
6471
|
+
label: z.string().trim().min(1).optional().describe("The new label, for rename.")
|
|
6472
|
+
}),
|
|
6473
|
+
run: async ({ project, credential, action, label }, run) => {
|
|
6474
|
+
const projectId = await projectIdOf(run, project);
|
|
6475
|
+
const credentialId = await resolveCredentialId(run.api, projectId, credential);
|
|
6476
|
+
if (action === "verify") {
|
|
6477
|
+
doneWith("verify the credential", await run.api.POST("/projects/{id}/credentials/{credentialId}/verify", { params: { path: {
|
|
6478
|
+
id: projectId,
|
|
6479
|
+
credentialId
|
|
6480
|
+
} } }), credentialHints);
|
|
6481
|
+
return {
|
|
6482
|
+
id: credentialId,
|
|
6483
|
+
verifying: true
|
|
6484
|
+
};
|
|
6485
|
+
}
|
|
6486
|
+
if (label === void 0) throw new UsageError("rename needs a label.");
|
|
6487
|
+
return dataOf("rename the credential", await run.api.PATCH("/projects/{id}/credentials/{credentialId}", {
|
|
6488
|
+
params: { path: {
|
|
6489
|
+
id: projectId,
|
|
6490
|
+
credentialId
|
|
6491
|
+
} },
|
|
6492
|
+
body: { label }
|
|
6493
|
+
}), credentialHints);
|
|
6494
|
+
}
|
|
6495
|
+
}),
|
|
6496
|
+
defineTool({
|
|
6497
|
+
name: "credential_delete",
|
|
6498
|
+
title: "Delete a credential",
|
|
6499
|
+
description: "Delete a credential of a project that no agent and no repository uses.",
|
|
6500
|
+
operations: ["deleteCredential"],
|
|
6501
|
+
safety: "destructive",
|
|
6502
|
+
input: z.object({
|
|
6503
|
+
project: projectField,
|
|
6504
|
+
credential: credentialField$1
|
|
6505
|
+
}),
|
|
6506
|
+
run: async ({ project, credential }, run) => {
|
|
6507
|
+
const projectId = await projectIdOf(run, project);
|
|
6508
|
+
const credentialId = await resolveCredentialId(run.api, projectId, credential);
|
|
6509
|
+
doneWith("delete the credential", await run.api.DELETE("/projects/{id}/credentials/{credentialId}", { params: { path: {
|
|
6510
|
+
id: projectId,
|
|
6511
|
+
credentialId
|
|
6512
|
+
} } }), credentialHints);
|
|
6513
|
+
return { deleted: credentialId };
|
|
6514
|
+
}
|
|
6515
|
+
})
|
|
6516
|
+
];
|
|
6517
|
+
//#endregion
|
|
6245
6518
|
//#region src/commands/notifications.ts
|
|
6246
6519
|
const notificationHints = { "notification.not_found": "kcloud notifications list shows them." };
|
|
6247
6520
|
const createNotificationsCommand = (adaptersOf) => defineGroup({
|
|
@@ -8263,6 +8536,7 @@ const kcloudTools = [
|
|
|
8263
8536
|
...projectToolTools,
|
|
8264
8537
|
...nodeTools,
|
|
8265
8538
|
...agentTools,
|
|
8539
|
+
...agentBackupTools,
|
|
8266
8540
|
...taskTools,
|
|
8267
8541
|
...observabilityTools,
|
|
8268
8542
|
...notificationTools,
|
|
@@ -8359,6 +8633,22 @@ const runtimeRowsOf = (runtime) => [
|
|
|
8359
8633
|
["agents", `${runtime.reconcile.agentsActive} active, ${runtime.reconcile.agentsQueued} queued`],
|
|
8360
8634
|
["log readers", String(runtime.ingest.readers)]
|
|
8361
8635
|
];
|
|
8636
|
+
const updateOf = ({ update }) => {
|
|
8637
|
+
if (update === null) return "not reported";
|
|
8638
|
+
switch (update.state) {
|
|
8639
|
+
case "current": return "current";
|
|
8640
|
+
case "waiting": return `waits for ${String(update.sessions)} ${update.sessions === 1 ? "session" : "sessions"} to end before ${update.target ?? "-"}`;
|
|
8641
|
+
case "updating": return `updating to ${update.target ?? "-"}`;
|
|
8642
|
+
case "failed": return `failed to reach ${update.target ?? "-"}, runs its previous version`;
|
|
8643
|
+
case "unmanaged": return "updated with its platform";
|
|
8644
|
+
}
|
|
8645
|
+
};
|
|
8646
|
+
const lastUpdateOf = ({ update }) => {
|
|
8647
|
+
const last = update?.last ?? null;
|
|
8648
|
+
if (last === null) return null;
|
|
8649
|
+
const outcome = `${last.outcome} ${last.version ?? "-"} at ${last.at}`;
|
|
8650
|
+
return last.reason === null ? outcome : `${outcome}: ${last.reason}`;
|
|
8651
|
+
};
|
|
8362
8652
|
const nodeDetailsText = (node) => [
|
|
8363
8653
|
renderTable([{ header: "FIELD" }, { header: "VALUE" }], [
|
|
8364
8654
|
["name", node.name],
|
|
@@ -8375,6 +8665,9 @@ const nodeDetailsText = (node) => [
|
|
|
8375
8665
|
["registration", registrationOf(node)],
|
|
8376
8666
|
["connected", node.connectedAt ?? "never"],
|
|
8377
8667
|
["last seen", node.lastSeenAt ?? "never"],
|
|
8668
|
+
...node.offlineSince === null ? [] : [["offline since", node.offlineSince]],
|
|
8669
|
+
["update", updateOf(node)],
|
|
8670
|
+
...lastUpdateOf(node) === null ? [] : [["last update", lastUpdateOf(node) ?? ""]],
|
|
8378
8671
|
["created", node.createdAt],
|
|
8379
8672
|
["id", node.id]
|
|
8380
8673
|
]),
|