@kikimoradev/kcloud 1.0.4 → 1.2.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 +787 -202
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -518,7 +518,9 @@ const runCli = dispatcherOf(spec);
|
|
|
518
518
|
//#endregion
|
|
519
519
|
//#region ../../packages/contracts/src/patterns.ts
|
|
520
520
|
const slugPattern = /^[a-z0-9](?:[a-z0-9-]{0,38}[a-z0-9])?$/;
|
|
521
|
+
const uuidShapePattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
521
522
|
const hexColorPattern = /^#[0-9a-f]{6}$/;
|
|
523
|
+
const logoHashPattern = /^[0-9a-f]{64}$/;
|
|
522
524
|
const userCodePattern = /^[23456789BCDFGHJKLMNPQRSTVWXZ]{4}-[23456789BCDFGHJKLMNPQRSTVWXZ]{4}$/;
|
|
523
525
|
const versionTagPattern = /^(?:0|[1-9]\d{0,8})\.(?:0|[1-9]\d{0,8})\.(?:0|[1-9]\d{0,8})$/;
|
|
524
526
|
const effortLevelSchema = z.enum([
|
|
@@ -661,6 +663,22 @@ const credentialMetadataSchema = z.object({
|
|
|
661
663
|
checkedAt: z.iso.datetime().optional(),
|
|
662
664
|
failureReason: z.string().optional()
|
|
663
665
|
}).meta({ id: "CredentialMetadata" });
|
|
666
|
+
const usageWindowStatusSchema = z.enum([
|
|
667
|
+
"allowed",
|
|
668
|
+
"allowed_warning",
|
|
669
|
+
"rejected"
|
|
670
|
+
]);
|
|
671
|
+
const usageWindowSchema = z.object({
|
|
672
|
+
utilization: z.number().nonnegative(),
|
|
673
|
+
resetsAt: z.iso.datetime().nullable(),
|
|
674
|
+
status: usageWindowStatusSchema.nullable()
|
|
675
|
+
}).meta({ id: "UsageWindow" });
|
|
676
|
+
const subscriptionUsageSchema = z.object({
|
|
677
|
+
fiveHour: usageWindowSchema.nullable(),
|
|
678
|
+
sevenDay: usageWindowSchema.nullable(),
|
|
679
|
+
readAt: z.iso.datetime(),
|
|
680
|
+
stale: z.boolean()
|
|
681
|
+
}).meta({ id: "SubscriptionUsage" });
|
|
664
682
|
const credentialSchema = z.object({
|
|
665
683
|
id: idSchema,
|
|
666
684
|
projectId: idSchema,
|
|
@@ -671,6 +689,8 @@ const credentialSchema = z.object({
|
|
|
671
689
|
verification: verificationStatusSchema,
|
|
672
690
|
metadata: credentialMetadataSchema,
|
|
673
691
|
expiresAt: z.iso.datetime().nullable(),
|
|
692
|
+
expiresAtComputed: z.boolean(),
|
|
693
|
+
usage: subscriptionUsageSchema.nullable(),
|
|
674
694
|
createdAt: z.iso.datetime(),
|
|
675
695
|
updatedAt: z.iso.datetime()
|
|
676
696
|
}).meta({ id: "Credential" });
|
|
@@ -739,6 +759,7 @@ const errorCodes = Object.keys({
|
|
|
739
759
|
"credential.in_use": "conflict",
|
|
740
760
|
"credential.kind_mismatch": "rule_violation",
|
|
741
761
|
"credential.rejected": "rule_violation",
|
|
762
|
+
"credential.usage_unavailable": "rule_violation",
|
|
742
763
|
"repository.not_found": "not_found",
|
|
743
764
|
"repository.already_registered": "conflict",
|
|
744
765
|
"repository.in_use": "conflict",
|
|
@@ -781,9 +802,13 @@ const errorCodes = Object.keys({
|
|
|
781
802
|
"runtime.monitor_draining": "conflict",
|
|
782
803
|
"runtime.monitor_limited": "conflict",
|
|
783
804
|
"runtime.monitor_check_unsupported": "rule_violation",
|
|
805
|
+
"runtime.task_deletion_unsupported": "rule_violation",
|
|
806
|
+
"task.not_found": "not_found",
|
|
807
|
+
"task.not_finished": "conflict",
|
|
784
808
|
"node.not_found": "not_found",
|
|
785
809
|
"node.name_taken": "conflict",
|
|
786
810
|
"node.offline": "rule_violation",
|
|
811
|
+
"node.outdated": "rule_violation",
|
|
787
812
|
"node.not_registered": "rule_violation",
|
|
788
813
|
"node.registration_invalid": "unauthenticated",
|
|
789
814
|
"node.registration_used": "conflict",
|
|
@@ -801,7 +826,6 @@ const errorCodes = Object.keys({
|
|
|
801
826
|
"node.size_unavailable": "rule_violation",
|
|
802
827
|
"node.size_not_larger": "rule_violation",
|
|
803
828
|
"node.region_unavailable": "rule_violation",
|
|
804
|
-
"node.import_refused": "rule_violation",
|
|
805
829
|
"provider.not_found": "not_found",
|
|
806
830
|
"provider.name_taken": "conflict",
|
|
807
831
|
"provider.in_use": "conflict",
|
|
@@ -815,20 +839,18 @@ const errorCodes = Object.keys({
|
|
|
815
839
|
"infrastructure.provider_error": "internal",
|
|
816
840
|
"infrastructure.operation_in_progress": "conflict",
|
|
817
841
|
"infrastructure.host_ops_disabled": "rule_violation",
|
|
818
|
-
"infrastructure.backups_unavailable": "rule_violation",
|
|
819
|
-
"infrastructure.maintenance_in_progress": "conflict",
|
|
820
|
-
"infrastructure.maintenance_not_in_progress": "conflict",
|
|
821
842
|
"backup.not_found": "not_found",
|
|
822
843
|
"backup.in_progress": "conflict",
|
|
823
844
|
"backup.unavailable": "rule_violation",
|
|
824
845
|
"backup.corrupt": "rule_violation",
|
|
825
846
|
"backup.undecryptable": "rule_violation",
|
|
826
|
-
"platform.version_unknown": "rule_violation",
|
|
827
|
-
"platform.same_version": "rule_violation",
|
|
828
|
-
"platform.image_unresolved": "rule_violation",
|
|
829
|
-
"platform.scripts_outdated": "rule_violation",
|
|
830
847
|
"volume.invalid_size": "rule_violation",
|
|
831
|
-
"notification.not_found": "not_found"
|
|
848
|
+
"notification.not_found": "not_found",
|
|
849
|
+
"logo.not_found": "not_found",
|
|
850
|
+
"logo.unreadable": "rule_violation",
|
|
851
|
+
"logo.too_small": "rule_violation",
|
|
852
|
+
"logo.too_large": "rule_violation",
|
|
853
|
+
"logo.type_mismatch": "rule_violation"
|
|
832
854
|
});
|
|
833
855
|
const errorCodeSchema = z.enum(errorCodes);
|
|
834
856
|
const errorDetailsSchema = z.record(z.string(), z.union([
|
|
@@ -1211,11 +1233,6 @@ z.object({
|
|
|
1211
1233
|
sidecarsRunning: z.int().nonnegative(),
|
|
1212
1234
|
volumeBytes: z.int().nonnegative().nullable()
|
|
1213
1235
|
}),
|
|
1214
|
-
sessionLogs: z.object({
|
|
1215
|
-
prunedDirectories: z.int().nonnegative(),
|
|
1216
|
-
lastPrunedAt: z.iso.datetime().nullable(),
|
|
1217
|
-
failures: z.int().nonnegative()
|
|
1218
|
-
}),
|
|
1219
1236
|
metrics: runtimeMetricsSchema,
|
|
1220
1237
|
deadLetters: z.array(deadLetterQueueSchema),
|
|
1221
1238
|
degradedAgents: z.array(degradedAgentSchema)
|
|
@@ -1235,10 +1252,16 @@ const agentEventSummarySchema = z.object({
|
|
|
1235
1252
|
durationMs: z.int().nullable(),
|
|
1236
1253
|
payload: z.record(z.string(), z.unknown())
|
|
1237
1254
|
}).meta({ id: "AgentEventSummary" });
|
|
1255
|
+
const agentEventLimitSchema = z.coerce.number().int().min(1).max(500).default(200);
|
|
1238
1256
|
z.object({
|
|
1239
1257
|
since: z.string().max(64).optional(),
|
|
1240
|
-
limit:
|
|
1258
|
+
limit: agentEventLimitSchema
|
|
1241
1259
|
});
|
|
1260
|
+
z.object({
|
|
1261
|
+
before: z.string().max(64).optional(),
|
|
1262
|
+
limit: agentEventLimitSchema
|
|
1263
|
+
});
|
|
1264
|
+
pageSchema(agentEventSummarySchema).meta({ id: "LiveHistoryPage" });
|
|
1242
1265
|
const taskStatuses = [
|
|
1243
1266
|
"pending",
|
|
1244
1267
|
"in_progress",
|
|
@@ -1247,6 +1270,13 @@ const taskStatuses = [
|
|
|
1247
1270
|
"cancelled"
|
|
1248
1271
|
];
|
|
1249
1272
|
const taskStatusSchema = z.enum(taskStatuses);
|
|
1273
|
+
const finalTaskStatuses = [
|
|
1274
|
+
"done",
|
|
1275
|
+
"failed",
|
|
1276
|
+
"cancelled"
|
|
1277
|
+
];
|
|
1278
|
+
const finalTaskStatusSchema = z.enum(finalTaskStatuses);
|
|
1279
|
+
const taskDeletionKikimoraVersion = "0.14.0";
|
|
1250
1280
|
const maxTaskDescriptionLength = 4e3;
|
|
1251
1281
|
const agentTaskSchema = z.object({
|
|
1252
1282
|
id: z.string(),
|
|
@@ -1262,15 +1292,31 @@ const agentTaskSchema = z.object({
|
|
|
1262
1292
|
startedAt: z.iso.datetime().nullable(),
|
|
1263
1293
|
finishedAt: z.iso.datetime().nullable()
|
|
1264
1294
|
}).meta({ id: "AgentTask" });
|
|
1295
|
+
const agentTaskSourceSchema = z.enum(["worker", "projection"]);
|
|
1296
|
+
const agentTaskStaleReasonSchema = z.enum([
|
|
1297
|
+
"unreachable",
|
|
1298
|
+
"timeout",
|
|
1299
|
+
"stopped"
|
|
1300
|
+
]).nullable();
|
|
1301
|
+
const taskCountsSchema = z.object({
|
|
1302
|
+
pending: z.int().nonnegative(),
|
|
1303
|
+
in_progress: z.int().nonnegative(),
|
|
1304
|
+
done: z.int().nonnegative(),
|
|
1305
|
+
failed: z.int().nonnegative(),
|
|
1306
|
+
cancelled: z.int().nonnegative()
|
|
1307
|
+
}).meta({ id: "TaskCounts" });
|
|
1265
1308
|
z.object({
|
|
1266
1309
|
items: z.array(agentTaskSchema),
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
"stopped"
|
|
1272
|
-
]).nullable()
|
|
1310
|
+
nextCursor: z.string().nullable(),
|
|
1311
|
+
counts: taskCountsSchema.nullable(),
|
|
1312
|
+
source: agentTaskSourceSchema,
|
|
1313
|
+
staleReason: agentTaskStaleReasonSchema
|
|
1273
1314
|
}).meta({ id: "AgentTaskList" });
|
|
1315
|
+
z.object({
|
|
1316
|
+
task: agentTaskSchema,
|
|
1317
|
+
source: agentTaskSourceSchema,
|
|
1318
|
+
staleReason: agentTaskStaleReasonSchema
|
|
1319
|
+
}).meta({ id: "AgentTaskView" });
|
|
1274
1320
|
z.object({
|
|
1275
1321
|
title: z.string().trim().min(1).max(200).optional(),
|
|
1276
1322
|
description: z.string().trim().min(1).max(maxTaskDescriptionLength)
|
|
@@ -1279,7 +1325,9 @@ z.object({
|
|
|
1279
1325
|
state: z.enum(["requested", "running"]),
|
|
1280
1326
|
cycle: z.int().nonnegative().nullable()
|
|
1281
1327
|
}).meta({ id: "MonitorCheck" });
|
|
1282
|
-
|
|
1328
|
+
cursorQuerySchema.extend({ status: taskStatusSchema.optional() });
|
|
1329
|
+
z.object({ status: finalTaskStatusSchema });
|
|
1330
|
+
z.object({ deleted: z.array(z.string()) }).meta({ id: "AgentTasksDeleted" });
|
|
1283
1331
|
z.object({
|
|
1284
1332
|
id: idSchema,
|
|
1285
1333
|
taskId: z.string().min(1).max(128)
|
|
@@ -1407,17 +1455,21 @@ const memorySummarySchema = z.object({
|
|
|
1407
1455
|
sessionId: z.string().nullable(),
|
|
1408
1456
|
createdAt: z.iso.datetime()
|
|
1409
1457
|
}).meta({ id: "MemorySummary" });
|
|
1410
|
-
|
|
1411
|
-
|
|
1458
|
+
const memoryOutcomes = ["succeeded", "failed"];
|
|
1459
|
+
const memoryOutcomeSchema = z.enum(memoryOutcomes);
|
|
1460
|
+
pageSchema(memorySummarySchema).extend({
|
|
1412
1461
|
source: z.enum(["worker", "unavailable"]),
|
|
1413
1462
|
staleReason: z.enum([
|
|
1414
1463
|
"unreachable",
|
|
1415
1464
|
"timeout",
|
|
1416
1465
|
"stopped"
|
|
1417
|
-
]).nullable()
|
|
1418
|
-
|
|
1466
|
+
]).nullable(),
|
|
1467
|
+
truncated: z.boolean()
|
|
1468
|
+
}).meta({ id: "AgentMemoryPage" });
|
|
1419
1469
|
z.object({
|
|
1420
1470
|
q: z.string().max(200).optional(),
|
|
1471
|
+
outcome: memoryOutcomeSchema.optional(),
|
|
1472
|
+
cursor: cursorSchema.optional(),
|
|
1421
1473
|
limit: z.coerce.number().int().min(1).max(100).default(20)
|
|
1422
1474
|
});
|
|
1423
1475
|
const costPointSchema = z.object({
|
|
@@ -1530,7 +1582,7 @@ z.object({
|
|
|
1530
1582
|
organizationId: idSchema,
|
|
1531
1583
|
accountId: idSchema
|
|
1532
1584
|
});
|
|
1533
|
-
z.enum(["monitor", "executor"]);
|
|
1585
|
+
const sessionRoleSchema = z.enum(["monitor", "executor"]);
|
|
1534
1586
|
const sessionSlotCountSchema = z.int().min(1).max(64);
|
|
1535
1587
|
const sessionQueueEntrySchema = z.object({
|
|
1536
1588
|
agentId: idSchema,
|
|
@@ -1539,8 +1591,21 @@ const sessionQueueEntrySchema = z.object({
|
|
|
1539
1591
|
projectName: z.string().nullable(),
|
|
1540
1592
|
position: z.int().positive(),
|
|
1541
1593
|
since: z.iso.datetime(),
|
|
1542
|
-
taskId: z.string().nullable()
|
|
1594
|
+
taskId: z.string().nullable(),
|
|
1595
|
+
taskTitle: z.string().nullable(),
|
|
1596
|
+
readable: z.boolean()
|
|
1543
1597
|
}).meta({ id: "SessionQueueEntry" });
|
|
1598
|
+
const sessionHolderSchema = z.object({
|
|
1599
|
+
agentId: idSchema,
|
|
1600
|
+
agentName: z.string().nullable(),
|
|
1601
|
+
projectId: idSchema.nullable(),
|
|
1602
|
+
projectName: z.string().nullable(),
|
|
1603
|
+
since: z.iso.datetime().nullable(),
|
|
1604
|
+
taskId: z.string().nullable(),
|
|
1605
|
+
taskTitle: z.string().nullable(),
|
|
1606
|
+
readable: z.boolean(),
|
|
1607
|
+
pid: z.int().positive().nullable()
|
|
1608
|
+
}).meta({ id: "SessionHolder" });
|
|
1544
1609
|
const sessionPoolSchema = z.object({
|
|
1545
1610
|
slots: sessionSlotCountSchema.nullable(),
|
|
1546
1611
|
effective: z.int().nonnegative(),
|
|
@@ -1567,6 +1632,12 @@ const agentAdmissionSchema = z.object({
|
|
|
1567
1632
|
monitor: agentAdmissionStateSchema.nullable(),
|
|
1568
1633
|
executor: agentAdmissionStateSchema.nullable()
|
|
1569
1634
|
}).meta({ id: "AgentAdmission" });
|
|
1635
|
+
const admissionHoldSchema = z.object({
|
|
1636
|
+
pools: z.array(sessionRoleSchema),
|
|
1637
|
+
sessions: z.int().nonnegative(),
|
|
1638
|
+
target: z.string().nullable(),
|
|
1639
|
+
updating: z.boolean()
|
|
1640
|
+
}).meta({ id: "AdmissionHold" });
|
|
1570
1641
|
const nodeStateSchema = z.enum([
|
|
1571
1642
|
"pending",
|
|
1572
1643
|
"online",
|
|
@@ -1641,6 +1712,7 @@ const nodePoolSchema = z.object({
|
|
|
1641
1712
|
slots: z.int().nonnegative(),
|
|
1642
1713
|
inUse: z.int().nonnegative(),
|
|
1643
1714
|
waiting: z.array(sessionQueueEntrySchema),
|
|
1715
|
+
holders: z.array(sessionHolderSchema).nullable(),
|
|
1644
1716
|
usage: poolUsageSchema.nullable()
|
|
1645
1717
|
}).meta({ id: "NodePool" });
|
|
1646
1718
|
const nodePoolsSchema = z.object({
|
|
@@ -1726,6 +1798,7 @@ const nodeUpdateSchema = z.object({
|
|
|
1726
1798
|
target: z.string().nullable(),
|
|
1727
1799
|
sessions: z.int().nonnegative(),
|
|
1728
1800
|
since: z.iso.datetime().nullable(),
|
|
1801
|
+
held: z.array(sessionRoleSchema),
|
|
1729
1802
|
last: z.object({
|
|
1730
1803
|
outcome: z.enum([
|
|
1731
1804
|
"updated",
|
|
@@ -1860,16 +1933,26 @@ const splitFits = (split, host) => {
|
|
|
1860
1933
|
return overflow.cpus <= 0 && overflow.memoryMb <= 0;
|
|
1861
1934
|
};
|
|
1862
1935
|
//#endregion
|
|
1936
|
+
//#region ../../packages/contracts/src/appearance.ts
|
|
1937
|
+
const colorSchema = z.string().regex(hexColorPattern);
|
|
1938
|
+
const logoMaxBytes = 524288;
|
|
1939
|
+
const logoHashSchema = z.string().regex(logoHashPattern);
|
|
1940
|
+
z.instanceof(Uint8Array).meta({ description: "The bytes of a PNG, JPEG or WebP image." });
|
|
1941
|
+
z.instanceof(Uint8Array).meta({ description: "The bytes of the WebP image." });
|
|
1942
|
+
z.object({ v: logoHashSchema.optional() });
|
|
1943
|
+
//#endregion
|
|
1863
1944
|
//#region ../../packages/contracts/src/identity.ts
|
|
1864
1945
|
const emailSchema = z.email().max(254);
|
|
1865
1946
|
const passwordSchema = z.string().min(12).max(128);
|
|
1866
1947
|
const userStatusSchema = z.enum(["active", "disabled"]);
|
|
1867
1948
|
const organizationNameSchema = z.string().trim().min(2).max(80);
|
|
1868
|
-
const organizationSlugSchema = z.string().regex(slugPattern);
|
|
1949
|
+
const organizationSlugSchema = z.string().regex(slugPattern).refine((slug) => !uuidShapePattern.test(slug));
|
|
1869
1950
|
const organizationSchema = z.object({
|
|
1870
1951
|
id: idSchema,
|
|
1871
1952
|
name: z.string(),
|
|
1872
1953
|
slug: organizationSlugSchema,
|
|
1954
|
+
color: colorSchema,
|
|
1955
|
+
logoHash: logoHashSchema.nullable(),
|
|
1873
1956
|
createdAt: z.iso.datetime()
|
|
1874
1957
|
}).meta({ id: "Organization" });
|
|
1875
1958
|
z.object({ items: z.array(organizationSchema) }).meta({ id: "OrganizationList" });
|
|
@@ -1927,6 +2010,11 @@ z.object({
|
|
|
1927
2010
|
initialPassword: passwordSchema.optional()
|
|
1928
2011
|
}).optional()
|
|
1929
2012
|
}).meta({ id: "CreateOrganizationBody" });
|
|
2013
|
+
const updateOrganizationBodySchema = z.object({
|
|
2014
|
+
name: organizationNameSchema,
|
|
2015
|
+
slug: organizationSlugSchema,
|
|
2016
|
+
color: colorSchema
|
|
2017
|
+
}).meta({ id: "UpdateOrganizationBody" });
|
|
1930
2018
|
z.object({ temporaryPassword: z.string() }).meta({ id: "TemporaryPassword" });
|
|
1931
2019
|
z.object({ items: z.array(userSchema) }).meta({ id: "UserList" });
|
|
1932
2020
|
z.object({ id: idSchema });
|
|
@@ -1995,6 +2083,9 @@ const auditActionSchema = z.enum([
|
|
|
1995
2083
|
"platform.bootstrapped",
|
|
1996
2084
|
"organization.created",
|
|
1997
2085
|
"organization.renamed",
|
|
2086
|
+
"organization.updated",
|
|
2087
|
+
"organization.logo_set",
|
|
2088
|
+
"organization.logo_removed",
|
|
1998
2089
|
"user.created",
|
|
1999
2090
|
"user.role_changed",
|
|
2000
2091
|
"user.disabled",
|
|
@@ -2008,6 +2099,8 @@ const auditActionSchema = z.enum([
|
|
|
2008
2099
|
"token.revoked",
|
|
2009
2100
|
"project.created",
|
|
2010
2101
|
"project.updated",
|
|
2102
|
+
"project.logo_set",
|
|
2103
|
+
"project.logo_removed",
|
|
2011
2104
|
"project.member_added",
|
|
2012
2105
|
"project.member_role_changed",
|
|
2013
2106
|
"project.member_removed",
|
|
@@ -2030,6 +2123,7 @@ const auditActionSchema = z.enum([
|
|
|
2030
2123
|
"agent.action",
|
|
2031
2124
|
"agent.backup_requested",
|
|
2032
2125
|
"agent.restored",
|
|
2126
|
+
"agent.tasks_deleted",
|
|
2033
2127
|
"platform.settings_updated",
|
|
2034
2128
|
"runtime.sessions_updated",
|
|
2035
2129
|
"platform.upgrade_requested",
|
|
@@ -2115,7 +2209,6 @@ pageSchema(auditEntrySchema).meta({ id: "AuditPage" });
|
|
|
2115
2209
|
//#endregion
|
|
2116
2210
|
//#region ../../packages/contracts/src/projects.ts
|
|
2117
2211
|
const slugSchema = z.string().regex(slugPattern);
|
|
2118
|
-
const colorSchema = z.string().regex(hexColorPattern);
|
|
2119
2212
|
const projectSchema = z.object({
|
|
2120
2213
|
id: idSchema,
|
|
2121
2214
|
organizationId: idSchema,
|
|
@@ -2123,6 +2216,7 @@ const projectSchema = z.object({
|
|
|
2123
2216
|
slug: slugSchema,
|
|
2124
2217
|
description: z.string(),
|
|
2125
2218
|
color: colorSchema,
|
|
2219
|
+
logoHash: logoHashSchema.nullable(),
|
|
2126
2220
|
dockerAllowed: z.boolean(),
|
|
2127
2221
|
memberCount: z.int().nonnegative(),
|
|
2128
2222
|
createdAt: z.iso.datetime(),
|
|
@@ -2551,6 +2645,7 @@ const agentRuntimeSchema = z.object({
|
|
|
2551
2645
|
stopMode: agentStopModeSchema.nullable(),
|
|
2552
2646
|
stopDeadlineAt: z.iso.datetime().nullable(),
|
|
2553
2647
|
admission: agentAdmissionSchema.nullable(),
|
|
2648
|
+
admissionHold: admissionHoldSchema.nullable(),
|
|
2554
2649
|
poolLimit: poolLimitSchema.nullable()
|
|
2555
2650
|
}).meta({ id: "AgentRuntime" });
|
|
2556
2651
|
const agentListItemSchema = z.object({
|
|
@@ -2899,19 +2994,6 @@ const nodeVolumesSchema = z.discriminatedUnion("source", [z.object({
|
|
|
2899
2994
|
source: z.literal("ok"),
|
|
2900
2995
|
items: z.array(nodeVolumeSchema)
|
|
2901
2996
|
}), unavailableBecause(["not_configured"])]).meta({ id: "NodeVolumes" });
|
|
2902
|
-
const dropletSizeSchema = z.object({
|
|
2903
|
-
slug: z.string(),
|
|
2904
|
-
vcpus: z.int().positive(),
|
|
2905
|
-
memoryMb: z.int().positive(),
|
|
2906
|
-
diskGb: z.int().positive(),
|
|
2907
|
-
priceMonthly: z.number().nonnegative(),
|
|
2908
|
-
command: z.string()
|
|
2909
|
-
}).meta({ id: "DropletSize" });
|
|
2910
|
-
const nodeSizesSchema = z.discriminatedUnion("source", [z.object({
|
|
2911
|
-
source: z.literal("ok"),
|
|
2912
|
-
items: z.array(dropletSizeSchema)
|
|
2913
|
-
}), unavailableBecause(providerUnavailableReasons)]).meta({ id: "NodeSizes" });
|
|
2914
|
-
const capacityForecastSchema = z.object({ byDisk: z.int().nonnegative().nullable() }).meta({ id: "CapacityForecast" });
|
|
2915
2997
|
z.object({
|
|
2916
2998
|
droplet: nodeDropletSchema,
|
|
2917
2999
|
volumes: nodeVolumesSchema,
|
|
@@ -2920,23 +3002,16 @@ z.object({
|
|
|
2920
3002
|
data: storageReadingSchema,
|
|
2921
3003
|
agents: storageReadingSchema
|
|
2922
3004
|
}),
|
|
2923
|
-
sizes: nodeSizesSchema,
|
|
2924
3005
|
platform: z.object({
|
|
2925
3006
|
version: z.string(),
|
|
2926
3007
|
instance: z.string(),
|
|
2927
3008
|
scriptsVersion: z.string(),
|
|
2928
3009
|
dockerVersion: z.string().nullable(),
|
|
2929
3010
|
dockerRootDir: z.string().nullable()
|
|
2930
|
-
})
|
|
2931
|
-
maintenance: z.object({
|
|
2932
|
-
quiescedAt: instantSchema$1.nullable(),
|
|
2933
|
-
pending: z.array(idSchema)
|
|
2934
|
-
}),
|
|
2935
|
-
capacity: capacityForecastSchema
|
|
3011
|
+
})
|
|
2936
3012
|
}).meta({ id: "PlatformNode" });
|
|
2937
3013
|
z.object({ role: volumeRoleSchema });
|
|
2938
3014
|
z.object({ sizeGb: z.int().positive() }).meta({ id: "ResizeVolumeBody" });
|
|
2939
|
-
z.object({ agents: z.int().nonnegative() }).meta({ id: "QuiesceResult" });
|
|
2940
3015
|
const requesterSchema = z.object({
|
|
2941
3016
|
userId: idSchema,
|
|
2942
3017
|
label: z.string().nullable()
|
|
@@ -3040,22 +3115,17 @@ const registryUnavailableSchema = z.object({
|
|
|
3040
3115
|
detail: z.string().nullable()
|
|
3041
3116
|
}).meta({ id: "RegistryUnavailable" });
|
|
3042
3117
|
const hostOperationsAvailabilities = ["enabled", "disabled"];
|
|
3043
|
-
const backupsAvailabilities = ["enabled", "unavailable"];
|
|
3044
3118
|
const installedImageSchema = z.string().regex(pinnedPlatformImagePattern).nullable();
|
|
3045
3119
|
z.object({
|
|
3046
3120
|
version: z.string(),
|
|
3121
|
+
environment: z.string().nullable(),
|
|
3047
3122
|
apiImage: installedImageSchema,
|
|
3048
3123
|
webImage: installedImageSchema,
|
|
3049
3124
|
nodeImage: installedImageSchema,
|
|
3050
3125
|
latest: z.string().nullable(),
|
|
3051
3126
|
latestError: registryUnavailableSchema.nullable(),
|
|
3052
|
-
versions: z.array(z.string()),
|
|
3053
|
-
upgradeable: z.boolean(),
|
|
3054
|
-
scriptsVersion: z.string(),
|
|
3055
|
-
expectedScriptsVersion: z.string(),
|
|
3056
3127
|
imagePrefix: z.string(),
|
|
3057
3128
|
hostOperations: z.enum(hostOperationsAvailabilities),
|
|
3058
|
-
backups: z.enum(backupsAvailabilities),
|
|
3059
3129
|
operations: z.array(hostOperationSchema)
|
|
3060
3130
|
}).meta({ id: "Platform" });
|
|
3061
3131
|
const keyVersionSchema = z.int().positive();
|
|
@@ -3078,7 +3148,6 @@ z.object({
|
|
|
3078
3148
|
versions: z.array(masterKeyVersionSchema),
|
|
3079
3149
|
rotationPending: z.boolean()
|
|
3080
3150
|
}).meta({ id: "MasterKey" });
|
|
3081
|
-
z.object({ version: z.string().regex(versionTagPattern) }).meta({ id: "UpgradePlatformBody" });
|
|
3082
3151
|
const metricsRangeSchema = z.enum([
|
|
3083
3152
|
"1h",
|
|
3084
3153
|
"24h",
|
|
@@ -3159,24 +3228,6 @@ z.object({
|
|
|
3159
3228
|
}).meta({ id: "AgentMetrics" });
|
|
3160
3229
|
z.object({ range: metricsRangeSchema });
|
|
3161
3230
|
z.object({ range: agentMetricsRangeSchema });
|
|
3162
|
-
z.object({
|
|
3163
|
-
nodeId: idSchema,
|
|
3164
|
-
agentId: idSchema
|
|
3165
|
-
});
|
|
3166
|
-
z.object({ agentIds: z.array(idSchema).min(1).max(500) });
|
|
3167
|
-
z.object({ occupied: z.array(idSchema) });
|
|
3168
|
-
z.object({ offset: z.coerce.number().int().nonnegative() });
|
|
3169
|
-
z.object({ bytes: z.int().nonnegative() });
|
|
3170
|
-
z.object({
|
|
3171
|
-
bytes: z.int().nonnegative(),
|
|
3172
|
-
sha256: z.string().regex(/^[0-9a-f]{64}$/u)
|
|
3173
|
-
});
|
|
3174
|
-
z.object({
|
|
3175
|
-
bytes: z.int().nonnegative(),
|
|
3176
|
-
files: z.int().nonnegative(),
|
|
3177
|
-
memoryCheck: z.string()
|
|
3178
|
-
});
|
|
3179
|
-
z.object({ removed: z.boolean() });
|
|
3180
3231
|
const notificationKindSchema = z.enum([
|
|
3181
3232
|
"credential.verification_failed",
|
|
3182
3233
|
"credential.expiring",
|
|
@@ -3332,6 +3383,7 @@ const detailsOf$1 = (body) => {
|
|
|
3332
3383
|
const parsed = errorBodySchema.safeParse(body);
|
|
3333
3384
|
return parsed.success ? parsed.data.details ?? {} : {};
|
|
3334
3385
|
};
|
|
3386
|
+
const hintText = (hint, details) => typeof hint === "string" ? hint : hint(details);
|
|
3335
3387
|
//#endregion
|
|
3336
3388
|
//#region src/client/api.ts
|
|
3337
3389
|
const defaultTimeoutMs = 3e4;
|
|
@@ -3388,7 +3440,7 @@ const createApi = ({ origin, token, fetch = globalThis.fetch, timeoutMs = defaul
|
|
|
3388
3440
|
const refused = (action, { error, response }, hints) => {
|
|
3389
3441
|
const failure = new ApiError(response.status, codeOf(error), action, detailsOf$1(error));
|
|
3390
3442
|
const hint = failure.code === null ? void 0 : hints[failure.code];
|
|
3391
|
-
return hint === void 0 ? failure : new CliError(failure.message, hint, { cause: failure });
|
|
3443
|
+
return hint === void 0 ? failure : new CliError(failure.message, hintText(hint, failure.details), { cause: failure });
|
|
3392
3444
|
};
|
|
3393
3445
|
const dataOf = (action, answer, hints = {}) => {
|
|
3394
3446
|
if (answer.data === void 0) throw refused(action, answer, hints);
|
|
@@ -3760,7 +3812,7 @@ const createLoginCommand = (adaptersOf) => defineCommand({
|
|
|
3760
3812
|
}
|
|
3761
3813
|
});
|
|
3762
3814
|
const authorizeInBrowser = async ({ adapters, api, origin, prompt }) => {
|
|
3763
|
-
const started = dataOf("start the sign-in", await api.POST("/auth/cli/authorize", { body: { clientName: adapters.clientName() } }), { "infrastructure.not_configured": `${origin} has no administrator yet; the
|
|
3815
|
+
const started = dataOf("start the sign-in", await api.POST("/auth/cli/authorize", { body: { clientName: adapters.clientName() } }), { "infrastructure.not_configured": `${origin} has no administrator yet; the platform is seeded during pnpm platform bootstrap.` });
|
|
3764
3816
|
prompt.note([
|
|
3765
3817
|
`Code ${started.userCode}`,
|
|
3766
3818
|
`Address ${started.verificationUri}`,
|
|
@@ -4207,10 +4259,15 @@ const runOf = async (adaptersOf, { settings, context }) => {
|
|
|
4207
4259
|
//#region src/commands/agents/shared.ts
|
|
4208
4260
|
const agentOption = { project: { type: "string" } };
|
|
4209
4261
|
const agentUsage = [["--project <name>", "the project of the agent, when the agent is named alone"]];
|
|
4262
|
+
const actionsAfterPause = /* @__PURE__ */ new Set([
|
|
4263
|
+
"update",
|
|
4264
|
+
"restart",
|
|
4265
|
+
"resume"
|
|
4266
|
+
]);
|
|
4210
4267
|
const agentHints = {
|
|
4211
4268
|
"agent.not_found": "kcloud agents list --project <name> shows the agents.",
|
|
4212
4269
|
"project.not_found": "kcloud projects list shows the projects you are a member of.",
|
|
4213
|
-
"agent.invalid_transition": "kcloud agents show <agent> lists the actions it takes now.",
|
|
4270
|
+
"agent.invalid_transition": ({ state, action }) => state === "pausing" && actionsAfterPause.has(String(action)) ? "The agent finishes its current session before it pauses; the action goes through once kcloud agents show <agent> says paused." : "kcloud agents show <agent> lists the actions it takes now.",
|
|
4214
4271
|
"agent.busy": "The agent is working on another action; try again in a moment.",
|
|
4215
4272
|
"concurrency.conflict": "Someone changed the agent meanwhile; read it again and retry.",
|
|
4216
4273
|
"agent.node_unsuitable": "The node lacks a capability the agent requires; kcloud nodes show <node> lists what it offers.",
|
|
@@ -4438,12 +4495,39 @@ const enforcementText = ({ state, reason }) => {
|
|
|
4438
4495
|
case "unsupported": return `The node holds no pool to a limit: ${reason === null ? "it cannot" : unsupportedReasons[reason] ?? reason}.`;
|
|
4439
4496
|
}
|
|
4440
4497
|
};
|
|
4441
|
-
const memoryOf = (pool) => {
|
|
4498
|
+
const memoryOf$1 = (pool) => {
|
|
4442
4499
|
if (pool.usage === null) return "-";
|
|
4443
4500
|
const { memoryBytes, memoryMaxBytes } = pool.usage;
|
|
4444
4501
|
return memoryMaxBytes === null ? bytesOf(memoryBytes) : `${bytesOf(memoryBytes)} of ${bytesOf(memoryMaxBytes)}`;
|
|
4445
4502
|
};
|
|
4446
4503
|
const roles = ["monitor", "executor"];
|
|
4504
|
+
const workOf = (role, { taskId, taskTitle, readable }) => {
|
|
4505
|
+
if (readable === false) return "another project";
|
|
4506
|
+
if (taskId === null) return role === "monitor" ? "monitor cycle" : "-";
|
|
4507
|
+
return taskTitle == null ? taskId : `${taskId} ${taskTitle}`;
|
|
4508
|
+
};
|
|
4509
|
+
const holdersOf = (pools) => {
|
|
4510
|
+
if (roles.every((role) => pools[role].holders == null)) return null;
|
|
4511
|
+
const held = roles.flatMap((role) => (pools[role].holders ?? []).map((holder) => [
|
|
4512
|
+
role,
|
|
4513
|
+
holder.projectName ?? "-",
|
|
4514
|
+
holder.agentName ?? holder.agentId,
|
|
4515
|
+
workOf(role, holder),
|
|
4516
|
+
holder.since ?? "-",
|
|
4517
|
+
holder.pid == null ? "-" : String(holder.pid)
|
|
4518
|
+
]));
|
|
4519
|
+
return held.length === 0 ? "No session holds a slot." : renderTable([
|
|
4520
|
+
{ header: "HOLDS" },
|
|
4521
|
+
{ header: "PROJECT" },
|
|
4522
|
+
{ header: "AGENT" },
|
|
4523
|
+
{ header: "WORK" },
|
|
4524
|
+
{ header: "SINCE" },
|
|
4525
|
+
{
|
|
4526
|
+
header: "PID",
|
|
4527
|
+
align: "right"
|
|
4528
|
+
}
|
|
4529
|
+
], held);
|
|
4530
|
+
};
|
|
4447
4531
|
const queueOf = (pools) => {
|
|
4448
4532
|
const waiting = roles.flatMap((role) => pools[role].waiting.map((entry) => [
|
|
4449
4533
|
role,
|
|
@@ -4451,7 +4535,7 @@ const queueOf = (pools) => {
|
|
|
4451
4535
|
entry.projectName ?? "-",
|
|
4452
4536
|
entry.agentName ?? entry.agentId,
|
|
4453
4537
|
entry.since,
|
|
4454
|
-
entry
|
|
4538
|
+
workOf(role, entry)
|
|
4455
4539
|
]));
|
|
4456
4540
|
return waiting.length === 0 ? "No session waits for a slot." : renderTable([
|
|
4457
4541
|
{ header: "WAITS FOR" },
|
|
@@ -4462,37 +4546,41 @@ const queueOf = (pools) => {
|
|
|
4462
4546
|
{ header: "PROJECT" },
|
|
4463
4547
|
{ header: "AGENT" },
|
|
4464
4548
|
{ header: "SINCE" },
|
|
4465
|
-
{ header: "
|
|
4549
|
+
{ header: "WORK" }
|
|
4466
4550
|
], waiting);
|
|
4467
4551
|
};
|
|
4468
|
-
const poolsText = (pools) =>
|
|
4469
|
-
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
header: "
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
|
|
4479
|
-
header: "MEMORY
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
]
|
|
4552
|
+
const poolsText = (pools) => {
|
|
4553
|
+
const holders = holdersOf(pools);
|
|
4554
|
+
return [
|
|
4555
|
+
enforcementText(pools.enforcement),
|
|
4556
|
+
renderTable([
|
|
4557
|
+
{ header: "POOL" },
|
|
4558
|
+
{ header: "SLOTS IN USE" },
|
|
4559
|
+
{
|
|
4560
|
+
header: "CPU IN USE",
|
|
4561
|
+
align: "right"
|
|
4562
|
+
},
|
|
4563
|
+
{ header: "MEMORY IN USE" },
|
|
4564
|
+
{
|
|
4565
|
+
header: "MEMORY KILLS",
|
|
4566
|
+
align: "right"
|
|
4567
|
+
}
|
|
4568
|
+
], roles.map((role) => {
|
|
4569
|
+
const pool = pools[role];
|
|
4570
|
+
const cpus = pool.usage?.cpus ?? null;
|
|
4571
|
+
return [
|
|
4572
|
+
role,
|
|
4573
|
+
`${pool.inUse} of ${pool.slots}`,
|
|
4574
|
+
cpus === null ? "-" : cpus.toFixed(2),
|
|
4575
|
+
memoryOf$1(pool),
|
|
4576
|
+
pool.usage === null ? "-" : String(pool.usage.memoryKills)
|
|
4577
|
+
];
|
|
4578
|
+
})),
|
|
4579
|
+
...holders === null ? [] : ["", holders],
|
|
4580
|
+
"",
|
|
4581
|
+
queueOf(pools)
|
|
4582
|
+
].join("\n");
|
|
4583
|
+
};
|
|
4496
4584
|
//#endregion
|
|
4497
4585
|
//#region src/commands/agents/plan.ts
|
|
4498
4586
|
const agentPlanOf = (agent) => ({
|
|
@@ -4672,6 +4760,15 @@ const admissionRowsOf = (agent) => {
|
|
|
4672
4760
|
const { admission } = agent.runtime;
|
|
4673
4761
|
return admission === null ? [] : [["monitor slot", slotOf(admission.monitor)], ["executor slot", slotOf(admission.executor)]];
|
|
4674
4762
|
};
|
|
4763
|
+
const admissionHoldRowsOf = ({ runtime }) => {
|
|
4764
|
+
const hold = runtime.admissionHold;
|
|
4765
|
+
if (hold == null) return [];
|
|
4766
|
+
if (hold.updating) return [["admissions", `held while the node updates to ${hold.target ?? "-"}`]];
|
|
4767
|
+
const all = hold.pools.includes("monitor");
|
|
4768
|
+
const noun = all ? "session" : "executor session";
|
|
4769
|
+
const waits = `${String(hold.sessions)} ${noun}${hold.sessions === 1 ? "" : "s"}`;
|
|
4770
|
+
return [["admissions", `${all ? "held" : "executor held"} for the node update to ${hold.target ?? "-"}, waiting for ${waits}`]];
|
|
4771
|
+
};
|
|
4675
4772
|
const poolLimitRowsOf = ({ runtime }) => runtime.poolLimit === null ? [] : [["pool limit", poolLimitText(runtime.poolLimit)]];
|
|
4676
4773
|
const detailsOf = (agent) => [
|
|
4677
4774
|
renderTable([{ header: "FIELD" }, { header: "VALUE" }], [
|
|
@@ -4690,6 +4787,7 @@ const detailsOf = (agent) => [
|
|
|
4690
4787
|
["container", agent.runtime.containerId ?? "none"],
|
|
4691
4788
|
["health", agent.runtime.health],
|
|
4692
4789
|
...admissionRowsOf(agent),
|
|
4790
|
+
...admissionHoldRowsOf(agent),
|
|
4693
4791
|
...poolLimitRowsOf(agent),
|
|
4694
4792
|
["pending", agent.restartRequired ? agent.restartReasons.join(", ") : "-"],
|
|
4695
4793
|
["actions", agent.availableActions.join(", ")],
|
|
@@ -5025,29 +5123,29 @@ const lifecycleCommands = (adaptersOf) => agentActions.map((action) => defineCom
|
|
|
5025
5123
|
}));
|
|
5026
5124
|
//#endregion
|
|
5027
5125
|
//#region src/client/pages.ts
|
|
5028
|
-
const windowOf = (limit, all, fallback = 50) => {
|
|
5126
|
+
const windowOf = (limit, all, fallback = 50, largest = 200) => {
|
|
5029
5127
|
if (limit === void 0) return {
|
|
5030
5128
|
limit: fallback,
|
|
5031
5129
|
all
|
|
5032
5130
|
};
|
|
5033
5131
|
const size = Number(limit);
|
|
5034
|
-
if (!/^[0-9]+$/u.test(limit) || size < 1 || size >
|
|
5132
|
+
if (!/^[0-9]+$/u.test(limit) || size < 1 || size > largest) throw new UsageError(`--limit ${limit} is not a whole number from 1 to ${largest}.`);
|
|
5035
5133
|
return {
|
|
5036
5134
|
limit: size,
|
|
5037
5135
|
all
|
|
5038
5136
|
};
|
|
5039
5137
|
};
|
|
5040
|
-
const collect = async (page, { limit, all }) => {
|
|
5041
|
-
const first = await page(null, Math.min(limit,
|
|
5138
|
+
const collect = async (page, { limit, all }, largest = 200) => {
|
|
5139
|
+
const first = await page(null, Math.min(limit, largest));
|
|
5042
5140
|
if (!all) return first;
|
|
5043
5141
|
const items = [...first.items];
|
|
5044
|
-
let
|
|
5045
|
-
while (
|
|
5046
|
-
|
|
5047
|
-
items.push(...
|
|
5048
|
-
cursor = next.nextCursor;
|
|
5142
|
+
let last = first;
|
|
5143
|
+
while (last.nextCursor !== null) {
|
|
5144
|
+
last = await page(last.nextCursor, largest);
|
|
5145
|
+
items.push(...last.items);
|
|
5049
5146
|
}
|
|
5050
5147
|
return {
|
|
5148
|
+
...last,
|
|
5051
5149
|
items,
|
|
5052
5150
|
nextCursor: null
|
|
5053
5151
|
};
|
|
@@ -5094,6 +5192,43 @@ const transcriptOf = async (api, agentId, sessionId, all) => collect(async (curs
|
|
|
5094
5192
|
limit: 200,
|
|
5095
5193
|
all
|
|
5096
5194
|
});
|
|
5195
|
+
const memoryOf = (api, agentId, { query, outcome }, window) => collect(async (cursor, limit) => dataOf("search what the agent remembers", await api.GET("/agents/{id}/memory", { params: {
|
|
5196
|
+
path: { id: agentId },
|
|
5197
|
+
query: {
|
|
5198
|
+
limit,
|
|
5199
|
+
...query === void 0 ? {} : { q: query },
|
|
5200
|
+
...outcome === void 0 ? {} : { outcome },
|
|
5201
|
+
...cursor === null ? {} : { cursor }
|
|
5202
|
+
}
|
|
5203
|
+
} }), agentHints), window, 100);
|
|
5204
|
+
const memoryReasons = {
|
|
5205
|
+
stopped: "the agent is not running",
|
|
5206
|
+
unreachable: "the agent did not answer on its control socket",
|
|
5207
|
+
timeout: "the agent did not answer in time"
|
|
5208
|
+
};
|
|
5209
|
+
const unreadReasonOf = (page) => page.staleReason === null ? "the agent did not answer" : memoryReasons[page.staleReason];
|
|
5210
|
+
const memoryText = (page) => {
|
|
5211
|
+
const unread = page.source === "unavailable";
|
|
5212
|
+
if (unread && page.items.length === 0) return `The memory cannot be read: ${unreadReasonOf(page)}.`;
|
|
5213
|
+
if (page.items.length === 0) return "The agent remembers nothing that matches.";
|
|
5214
|
+
return [
|
|
5215
|
+
renderTable([
|
|
5216
|
+
{ header: "WHEN" },
|
|
5217
|
+
{ header: "OUTCOME" },
|
|
5218
|
+
{ header: "TASK" },
|
|
5219
|
+
{ header: "TITLE" },
|
|
5220
|
+
{ header: "HEADLINE" }
|
|
5221
|
+
], page.items.map((summary) => [
|
|
5222
|
+
summary.createdAt,
|
|
5223
|
+
summary.ok ? "ok" : "failed",
|
|
5224
|
+
summary.taskId,
|
|
5225
|
+
summary.title,
|
|
5226
|
+
summary.headline
|
|
5227
|
+
])),
|
|
5228
|
+
...unread ? ["", `The rest of the memory cannot be read: ${unreadReasonOf(page)}.`] : [],
|
|
5229
|
+
...page.truncated ? ["", `The agent returns at most 100 summaries, so the list may miss older ones.`] : []
|
|
5230
|
+
].join("\n");
|
|
5231
|
+
};
|
|
5097
5232
|
const observabilityCommands = (adaptersOf) => [
|
|
5098
5233
|
defineCommand({
|
|
5099
5234
|
name: "sessions",
|
|
@@ -5169,7 +5304,9 @@ const observabilityCommands = (adaptersOf) => [
|
|
|
5169
5304
|
const run = await runOf(adaptersOf, invocation);
|
|
5170
5305
|
const agentId = await agentFrom(run, invocation.positionals[0], invocation.values.project);
|
|
5171
5306
|
const page = await transcriptOf(run.api, agentId, invocation.positionals[1] ?? "", invocation.values.all);
|
|
5172
|
-
|
|
5307
|
+
const code = printed(run, page.items, (entries) => entries.length === 0 ? "The session left no transcript." : entries.map((entry) => JSON.stringify(entry)).join("\n"));
|
|
5308
|
+
if (page.nextCursor !== null && !run.settings.json) run.context.terminal.error("More entries follow; --all prints the whole session.");
|
|
5309
|
+
return code;
|
|
5173
5310
|
}
|
|
5174
5311
|
}),
|
|
5175
5312
|
defineCommand({
|
|
@@ -5305,44 +5442,35 @@ const observabilityCommands = (adaptersOf) => [
|
|
|
5305
5442
|
defineCommand({
|
|
5306
5443
|
name: "memory",
|
|
5307
5444
|
summary: "Search what an agent remembers of its tasks.",
|
|
5308
|
-
usage: "agents memory <agent> [--project <name>] [--query <text>] [--limit <n>]",
|
|
5445
|
+
usage: "agents memory <agent> [--project <name>] [--query <text>] [--outcome <outcome>] [--limit <n>] [--all]",
|
|
5309
5446
|
optionsUsage: [
|
|
5310
5447
|
...agentUsage,
|
|
5311
5448
|
["--query <text>", "only the summaries that match this text"],
|
|
5312
|
-
["--
|
|
5449
|
+
["--outcome <outcome>", `only the summaries of one outcome: ${memoryOutcomes.join(", ")}`],
|
|
5450
|
+
["--limit <n>", `how many summaries (default: 20)`],
|
|
5451
|
+
allUsage
|
|
5313
5452
|
],
|
|
5314
5453
|
options: {
|
|
5315
5454
|
...agentOption,
|
|
5455
|
+
...pageOptions,
|
|
5316
5456
|
query: { type: "string" },
|
|
5317
|
-
|
|
5457
|
+
outcome: { type: "string" }
|
|
5318
5458
|
},
|
|
5319
5459
|
positionals: {
|
|
5320
5460
|
min: 1,
|
|
5321
5461
|
max: 1
|
|
5322
5462
|
},
|
|
5323
5463
|
run: async (invocation) => {
|
|
5464
|
+
const { values } = invocation;
|
|
5324
5465
|
const run = await runOf(adaptersOf, invocation);
|
|
5325
|
-
const agentId = await agentFrom(run, invocation.positionals[0],
|
|
5326
|
-
const
|
|
5327
|
-
|
|
5328
|
-
const
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
|
|
5333
|
-
}
|
|
5334
|
-
} }), agentHints);
|
|
5335
|
-
return printed(run, listed, (shown) => shown.items.length === 0 ? "The agent remembers nothing that matches." : renderTable([
|
|
5336
|
-
{ header: "WHEN" },
|
|
5337
|
-
{ header: "OUTCOME" },
|
|
5338
|
-
{ header: "TITLE" },
|
|
5339
|
-
{ header: "HEADLINE" }
|
|
5340
|
-
], shown.items.map((summary) => [
|
|
5341
|
-
summary.createdAt,
|
|
5342
|
-
summary.ok ? "ok" : "failed",
|
|
5343
|
-
summary.title,
|
|
5344
|
-
summary.headline
|
|
5345
|
-
])));
|
|
5466
|
+
const agentId = await agentFrom(run, invocation.positionals[0], values.project);
|
|
5467
|
+
const outcome = memoryOutcomes.find((candidate) => candidate === values.outcome);
|
|
5468
|
+
if (values.outcome !== void 0 && outcome === void 0) throw new UsageError(`--outcome ${values.outcome} is not an outcome of a summary.`, `The outcomes are ${memoryOutcomes.join(", ")}.`);
|
|
5469
|
+
const page = await memoryOf(run.api, agentId, {
|
|
5470
|
+
query: values.query,
|
|
5471
|
+
outcome
|
|
5472
|
+
}, windowOf(values.limit, values.all, 20, 100));
|
|
5473
|
+
return printed(run, page, memoryText);
|
|
5346
5474
|
}
|
|
5347
5475
|
})
|
|
5348
5476
|
];
|
|
@@ -5511,42 +5639,67 @@ const streamOptions = {
|
|
|
5511
5639
|
default: false
|
|
5512
5640
|
},
|
|
5513
5641
|
since: { type: "string" },
|
|
5642
|
+
before: { type: "string" },
|
|
5514
5643
|
limit: { type: "string" }
|
|
5515
5644
|
};
|
|
5516
5645
|
const streamUsage = [
|
|
5517
5646
|
...agentUsage,
|
|
5518
5647
|
["-f, --follow", "keep the stream open instead of leaving when the output stops"],
|
|
5519
5648
|
["--since <id>", "start after this event instead of at the start of the buffer"],
|
|
5520
|
-
["--limit <n>", "how many events to replay before the live ones"]
|
|
5649
|
+
["--limit <n>", "how many events to replay before the live ones, or the page size with --before"]
|
|
5521
5650
|
];
|
|
5522
|
-
const
|
|
5523
|
-
|
|
5524
|
-
if (
|
|
5651
|
+
const beforeUsage = ["--before <id>", "print the stored output before this event and leave, oldest first"];
|
|
5652
|
+
const linePrinter = (json) => (data) => {
|
|
5653
|
+
if (json) return JSON.stringify(data);
|
|
5654
|
+
const parsed = agentEventSummarySchema.safeParse(data);
|
|
5655
|
+
if (!parsed.success) return JSON.stringify(data);
|
|
5525
5656
|
const event = parsed.data;
|
|
5526
5657
|
const text = "text" in event.payload && typeof event.payload.text === "string" ? event.payload.text : JSON.stringify(event.payload);
|
|
5527
5658
|
return `${event.ts} ${event.level.padEnd(5)} ${event.agentRole ?? "-"} ${event.event} ${text}`;
|
|
5528
5659
|
};
|
|
5660
|
+
const earlierOutput = async (session, agentId, before, limit) => dataOf("read the earlier output of the agent", await session.api.GET("/agents/{id}/live/history", { params: {
|
|
5661
|
+
path: { id: agentId },
|
|
5662
|
+
query: {
|
|
5663
|
+
before,
|
|
5664
|
+
...limit === void 0 ? {} : { limit: Number(limit) }
|
|
5665
|
+
}
|
|
5666
|
+
} }), agentHints);
|
|
5667
|
+
const olderPageCommand = (reference, project, cursor, limit) => [
|
|
5668
|
+
"kcloud agents logs",
|
|
5669
|
+
reference,
|
|
5670
|
+
...project === void 0 ? [] : ["--project", project],
|
|
5671
|
+
"--before",
|
|
5672
|
+
cursor,
|
|
5673
|
+
...limit === void 0 ? [] : ["--limit", limit]
|
|
5674
|
+
].join(" ");
|
|
5529
5675
|
const streamCommands = (adaptersOf) => [{
|
|
5530
5676
|
name: "logs",
|
|
5531
5677
|
path: "/agents/{id}/live",
|
|
5532
5678
|
summary: "Print what the agent is writing, and keep printing with --follow.",
|
|
5533
|
-
resumable: false
|
|
5679
|
+
resumable: false,
|
|
5680
|
+
pagesBack: true
|
|
5534
5681
|
}, {
|
|
5535
5682
|
name: "events",
|
|
5536
5683
|
path: "/agents/{id}/events",
|
|
5537
5684
|
summary: "Print the stored events of the agent, and keep printing with --follow.",
|
|
5538
|
-
resumable: true
|
|
5539
|
-
|
|
5685
|
+
resumable: true,
|
|
5686
|
+
pagesBack: false
|
|
5687
|
+
}].map(({ name, path, summary, resumable, pagesBack }) => defineCommand({
|
|
5540
5688
|
name,
|
|
5541
5689
|
summary,
|
|
5542
|
-
usage: `agents ${name} <agent> [--project <name>] [--follow] [--since <id>] [--limit <n>]`,
|
|
5543
|
-
optionsUsage: streamUsage,
|
|
5690
|
+
usage: pagesBack ? `agents ${name} <agent> [--project <name>] [--follow] [--since <id>] [--before <id>] [--limit <n>]` : `agents ${name} <agent> [--project <name>] [--follow] [--since <id>] [--limit <n>]`,
|
|
5691
|
+
optionsUsage: pagesBack ? [...streamUsage, beforeUsage] : streamUsage,
|
|
5544
5692
|
options: streamOptions,
|
|
5545
5693
|
positionals: {
|
|
5546
5694
|
min: 1,
|
|
5547
5695
|
max: 1
|
|
5548
5696
|
},
|
|
5549
5697
|
run: async ({ values, positionals, settings, context }) => {
|
|
5698
|
+
const before = values.before;
|
|
5699
|
+
if (before !== void 0 && !pagesBack) throw new UsageError("--before reads the output of kcloud agents logs alone.");
|
|
5700
|
+
if (before !== void 0 && (values.follow || values.since !== void 0)) throw new UsageError("--before reads earlier output; leave out --follow and --since.");
|
|
5701
|
+
const limit = values.limit;
|
|
5702
|
+
if (limit !== void 0 && !/^[0-9]+$/u.test(limit)) throw new UsageError(`--limit ${limit} is not a whole number.`);
|
|
5550
5703
|
const adapters = adaptersOf(context);
|
|
5551
5704
|
const session = await openSession({
|
|
5552
5705
|
settings,
|
|
@@ -5555,8 +5708,17 @@ const streamCommands = (adaptersOf) => [{
|
|
|
5555
5708
|
});
|
|
5556
5709
|
const reference = positionals[0] ?? "";
|
|
5557
5710
|
const agentId = await resolveAgentId(session, reference, values.project);
|
|
5558
|
-
const
|
|
5559
|
-
if (
|
|
5711
|
+
const lineOf = linePrinter(settings.json);
|
|
5712
|
+
if (before !== void 0) {
|
|
5713
|
+
const page = await earlierOutput(session, agentId, before, limit);
|
|
5714
|
+
if (settings.json) {
|
|
5715
|
+
context.terminal.line(JSON.stringify(page));
|
|
5716
|
+
return exitCodes.ok;
|
|
5717
|
+
}
|
|
5718
|
+
for (const event of page.items) context.terminal.line(lineOf(event));
|
|
5719
|
+
context.terminal.error(context.terminal.errorStyles.dim(page.nextCursor === null ? "The stored output begins here." : `older: ${olderPageCommand(reference, values.project, page.nextCursor, limit)}`));
|
|
5720
|
+
return exitCodes.ok;
|
|
5721
|
+
}
|
|
5560
5722
|
await streamFrames({
|
|
5561
5723
|
origin: session.origin,
|
|
5562
5724
|
token: session.token,
|
|
@@ -5575,7 +5737,7 @@ const streamCommands = (adaptersOf) => [{
|
|
|
5575
5737
|
},
|
|
5576
5738
|
onFrame: (frame) => {
|
|
5577
5739
|
if (frame.event === "agent.state") return;
|
|
5578
|
-
context.terminal.line(
|
|
5740
|
+
context.terminal.line(lineOf(frame.data));
|
|
5579
5741
|
}
|
|
5580
5742
|
});
|
|
5581
5743
|
return exitCodes.ok;
|
|
@@ -5585,7 +5747,10 @@ const streamCommands = (adaptersOf) => [{
|
|
|
5585
5747
|
//#region src/commands/agents/tasks.ts
|
|
5586
5748
|
const taskHints = {
|
|
5587
5749
|
...agentHints,
|
|
5588
|
-
"runtime.control_unreachable": "The worker is not answering; kcloud agents show <agent> tells whether it runs."
|
|
5750
|
+
"runtime.control_unreachable": "The worker is not answering; kcloud agents show <agent> tells whether it runs.",
|
|
5751
|
+
"task.not_found": "kcloud agents tasks list <agent> shows the tasks of the agent.",
|
|
5752
|
+
"task.not_finished": "Only done, failed and cancelled tasks can be deleted; kcloud agents tasks cancel <agent> <task> cancels a pending one.",
|
|
5753
|
+
"runtime.task_deletion_unsupported": `Move the agent to Kikimora ${taskDeletionKikimoraVersion} or newer with kcloud agents set, then kcloud agents update.`
|
|
5589
5754
|
};
|
|
5590
5755
|
const tasksCommand = (adaptersOf) => defineGroup({
|
|
5591
5756
|
name: "tasks",
|
|
@@ -5595,11 +5760,21 @@ const tasksCommand = (adaptersOf) => defineGroup({
|
|
|
5595
5760
|
defineCommand({
|
|
5596
5761
|
name: "list",
|
|
5597
5762
|
summary: "List the tasks of an agent.",
|
|
5598
|
-
usage: "agents tasks list <agent> [--project <name>] [--status <status>]",
|
|
5599
|
-
optionsUsage: [
|
|
5763
|
+
usage: "agents tasks list <agent> [--project <name>] [--status <status>] [--limit <n>] [--all]",
|
|
5764
|
+
optionsUsage: [
|
|
5765
|
+
...agentUsage,
|
|
5766
|
+
["--status <status>", `one of ${taskStatuses.join(", ")}`],
|
|
5767
|
+
["--limit <n>", `how many tasks, newest first (default: 50)`],
|
|
5768
|
+
["--all", "follow the cursor to the end instead of one page"]
|
|
5769
|
+
],
|
|
5600
5770
|
options: {
|
|
5601
5771
|
...agentOption,
|
|
5602
|
-
status: { type: "string" }
|
|
5772
|
+
status: { type: "string" },
|
|
5773
|
+
limit: { type: "string" },
|
|
5774
|
+
all: {
|
|
5775
|
+
type: "boolean",
|
|
5776
|
+
default: false
|
|
5777
|
+
}
|
|
5603
5778
|
},
|
|
5604
5779
|
positionals: {
|
|
5605
5780
|
min: 1,
|
|
@@ -5611,10 +5786,14 @@ const tasksCommand = (adaptersOf) => defineGroup({
|
|
|
5611
5786
|
const given = invocation.values.status;
|
|
5612
5787
|
const status = taskStatuses.find((candidate) => candidate === given);
|
|
5613
5788
|
if (given !== void 0 && status === void 0) throw new UsageError(`--status ${given} is not a status of a task.`, `The statuses are ${taskStatuses.join(", ")}.`);
|
|
5614
|
-
const listed = dataOf("list the tasks of the agent", await run.api.GET("/agents/{id}/tasks", { params: {
|
|
5789
|
+
const listed = await collect(async (cursor, limit) => dataOf("list the tasks of the agent", await run.api.GET("/agents/{id}/tasks", { params: {
|
|
5615
5790
|
path: { id: agentId },
|
|
5616
|
-
|
|
5617
|
-
|
|
5791
|
+
query: {
|
|
5792
|
+
limit,
|
|
5793
|
+
...cursor === null ? {} : { cursor },
|
|
5794
|
+
...status === void 0 ? {} : { status }
|
|
5795
|
+
}
|
|
5796
|
+
} }), taskHints), windowOf(invocation.values.limit, invocation.values.all));
|
|
5618
5797
|
return printed(run, listed, (page) => page.items.length === 0 ? "The agent has no task in its queue." : renderTable([
|
|
5619
5798
|
{ header: "STATUS" },
|
|
5620
5799
|
{ header: "TITLE" },
|
|
@@ -5627,12 +5806,42 @@ const tasksCommand = (adaptersOf) => defineGroup({
|
|
|
5627
5806
|
], page.items.map((task) => [
|
|
5628
5807
|
task.status,
|
|
5629
5808
|
task.title,
|
|
5630
|
-
`${String(task.attempt)}/${String(task.maxAttempts)}`,
|
|
5809
|
+
task.attempt === null ? "-" : task.maxAttempts === null ? String(task.attempt) : `${String(task.attempt)}/${String(task.maxAttempts)}`,
|
|
5631
5810
|
task.costUsd === null ? "-" : task.costUsd.toFixed(2),
|
|
5632
5811
|
task.id
|
|
5633
5812
|
])));
|
|
5634
5813
|
}
|
|
5635
5814
|
}),
|
|
5815
|
+
defineCommand({
|
|
5816
|
+
name: "show",
|
|
5817
|
+
summary: "Show one task of an agent with its description and error.",
|
|
5818
|
+
usage: "agents tasks show <agent> <task> [--project <name>]",
|
|
5819
|
+
optionsUsage: agentUsage,
|
|
5820
|
+
options: agentOption,
|
|
5821
|
+
positionals: {
|
|
5822
|
+
min: 2,
|
|
5823
|
+
max: 2
|
|
5824
|
+
},
|
|
5825
|
+
run: async (invocation) => {
|
|
5826
|
+
const run = await runOf(adaptersOf, invocation);
|
|
5827
|
+
const agentId = await agentFrom(run, invocation.positionals[0], invocation.values.project);
|
|
5828
|
+
const taskId = invocation.positionals[1] ?? "";
|
|
5829
|
+
const view = dataOf("read the task", await run.api.GET("/agents/{id}/tasks/{taskId}", { params: { path: {
|
|
5830
|
+
id: agentId,
|
|
5831
|
+
taskId
|
|
5832
|
+
} } }), taskHints);
|
|
5833
|
+
return printed(run, view, ({ task }) => [
|
|
5834
|
+
`${task.title} (${task.id})`,
|
|
5835
|
+
`status ${task.status}`,
|
|
5836
|
+
`attempt ${task.attempt === null ? "-" : String(task.attempt)}${task.maxAttempts === null ? "" : `/${String(task.maxAttempts)}`}`,
|
|
5837
|
+
`created ${task.createdAt ?? "-"}`,
|
|
5838
|
+
`started ${task.startedAt ?? "-"}`,
|
|
5839
|
+
`finished ${task.finishedAt ?? "-"}`,
|
|
5840
|
+
...task.error === null ? [] : [`error ${task.error}`],
|
|
5841
|
+
...task.description === null ? [] : ["", task.description]
|
|
5842
|
+
].join("\n"));
|
|
5843
|
+
}
|
|
5844
|
+
}),
|
|
5636
5845
|
defineCommand({
|
|
5637
5846
|
name: "add",
|
|
5638
5847
|
summary: "Put a task in the queue of an agent.",
|
|
@@ -5712,9 +5921,50 @@ const tasksCommand = (adaptersOf) => defineGroup({
|
|
|
5712
5921
|
} } }), taskHints);
|
|
5713
5922
|
return said(run, `Cancelled the task ${taskId}.`);
|
|
5714
5923
|
}
|
|
5924
|
+
}),
|
|
5925
|
+
defineCommand({
|
|
5926
|
+
name: "delete",
|
|
5927
|
+
summary: "Delete a finished task, or every task of one final status.",
|
|
5928
|
+
usage: "agents tasks delete <agent> <task> [--project <name>] | agents tasks delete <agent> --status <status> [--project <name>]",
|
|
5929
|
+
optionsUsage: [...agentUsage, ["--status <status>", `one of ${finalTaskStatuses.join(", ")}`]],
|
|
5930
|
+
options: {
|
|
5931
|
+
...agentOption,
|
|
5932
|
+
status: { type: "string" }
|
|
5933
|
+
},
|
|
5934
|
+
positionals: {
|
|
5935
|
+
min: 1,
|
|
5936
|
+
max: 2
|
|
5937
|
+
},
|
|
5938
|
+
run: async (invocation) => {
|
|
5939
|
+
const taskId = invocation.positionals[1];
|
|
5940
|
+
const given = invocation.values.status;
|
|
5941
|
+
if (taskId === void 0 === (given === void 0)) throw new UsageError("agents tasks delete needs either a task or --status, not both.", "kcloud agents tasks delete <agent> <task> deletes one task.");
|
|
5942
|
+
const status = finalTaskStatuses.find((candidate) => candidate === given);
|
|
5943
|
+
if (given !== void 0 && status === void 0) throw new UsageError(`--status ${given} is not a final status of a task.`, `Only ${finalTaskStatuses.join(", ")} tasks can be deleted.`);
|
|
5944
|
+
const run = await runOf(adaptersOf, invocation);
|
|
5945
|
+
const agentId = await agentFrom(run, invocation.positionals[0], invocation.values.project);
|
|
5946
|
+
await confirmed({
|
|
5947
|
+
prompt: run.prompt,
|
|
5948
|
+
settings: run.settings,
|
|
5949
|
+
question: status === void 0 ? `Delete the task ${taskId ?? ""}?` : `Delete every ${status} task of the agent?${status === "done" ? " The monitor may report them again." : ""}`
|
|
5950
|
+
});
|
|
5951
|
+
const answer = dataOf("delete the tasks", status === void 0 ? await run.api.DELETE("/agents/{id}/tasks/{taskId}", { params: { path: {
|
|
5952
|
+
id: agentId,
|
|
5953
|
+
taskId: taskId ?? ""
|
|
5954
|
+
} } }) : await run.api.DELETE("/agents/{id}/tasks", { params: {
|
|
5955
|
+
path: { id: agentId },
|
|
5956
|
+
query: { status }
|
|
5957
|
+
} }), taskHints);
|
|
5958
|
+
return printed(run, answer, ({ deleted }) => deletedSummary(deleted.length, status ?? null, taskId ?? null));
|
|
5959
|
+
}
|
|
5715
5960
|
})
|
|
5716
5961
|
]
|
|
5717
5962
|
});
|
|
5963
|
+
const deletedSummary = (count, status, taskId) => {
|
|
5964
|
+
if (status === null) return `Deleted the task ${taskId ?? ""}.`;
|
|
5965
|
+
if (count === 0) return `The agent has no ${status} task.`;
|
|
5966
|
+
return `Deleted ${String(count)} ${status} ${count === 1 ? "task" : "tasks"}.`;
|
|
5967
|
+
};
|
|
5718
5968
|
const descriptionOf = async (run, given) => {
|
|
5719
5969
|
if (given === "-") {
|
|
5720
5970
|
const chunks = [];
|
|
@@ -5794,25 +6044,59 @@ const secretUsage = [
|
|
|
5794
6044
|
//#region src/commands/credentials.ts
|
|
5795
6045
|
const projectOption$2 = { project: { type: "string" } };
|
|
5796
6046
|
const projectUsage$2 = [["--project <name>", "the project the credential belongs to, by slug, name or id"]];
|
|
6047
|
+
const usageUnavailable = "Only a Claude Code OAuth token reports the usage of a subscription.";
|
|
5797
6048
|
const credentialHints = {
|
|
5798
6049
|
"project.not_found": "kcloud projects list shows the projects you are a member of.",
|
|
5799
6050
|
"credential.not_found": "kcloud credentials list --project <name> shows them.",
|
|
5800
6051
|
"credential.label_taken": "Another credential of the project already has that label.",
|
|
5801
|
-
"credential.in_use": "Point the agents at another credential first."
|
|
6052
|
+
"credential.in_use": "Point the agents at another credential first.",
|
|
6053
|
+
"credential.usage_unavailable": usageUnavailable
|
|
6054
|
+
};
|
|
6055
|
+
const percent = 100;
|
|
6056
|
+
const notApplicable = "-";
|
|
6057
|
+
const usedOf = (window) => window === null ? notApplicable : `${String(Math.round(window.utilization * percent))}%`;
|
|
6058
|
+
const shareOf = (window) => window?.status === "rejected" ? "limit" : usedOf(window);
|
|
6059
|
+
const usageSummaryOf = ({ kind, usage }) => {
|
|
6060
|
+
if (kind !== "claude_oauth_token") return notApplicable;
|
|
6061
|
+
if (usage === null) return "no data";
|
|
6062
|
+
const summary = `5h ${shareOf(usage.fiveHour)} 7d ${shareOf(usage.sevenDay)}`;
|
|
6063
|
+
return usage.stale ? `${summary} (stale)` : summary;
|
|
6064
|
+
};
|
|
6065
|
+
const usageReportOf = (credential) => {
|
|
6066
|
+
const { usage } = credential;
|
|
6067
|
+
if (usage === null) return `No usage of ${credential.label} was read yet.`;
|
|
6068
|
+
const windows = [["5h", usage.fiveHour], ["7d", usage.sevenDay]];
|
|
6069
|
+
return `${renderTable([
|
|
6070
|
+
{ header: "WINDOW" },
|
|
6071
|
+
{ header: "USED" },
|
|
6072
|
+
{ header: "STATUS" },
|
|
6073
|
+
{ header: "RESETS" }
|
|
6074
|
+
], windows.map(([name, window]) => [
|
|
6075
|
+
name,
|
|
6076
|
+
usedOf(window),
|
|
6077
|
+
window?.status ?? notApplicable,
|
|
6078
|
+
window?.resetsAt ?? notApplicable
|
|
6079
|
+
]))}\n${usage.stale ? `Read at ${usage.readAt}; the latest reading failed.` : `Read at ${usage.readAt}.`}`;
|
|
6080
|
+
};
|
|
6081
|
+
const isoDateLength = 10;
|
|
6082
|
+
const expiryOf = ({ expiresAt, expiresAtComputed }) => {
|
|
6083
|
+
if (expiresAt === null) return "never";
|
|
6084
|
+
return expiresAtComputed ? `about ${expiresAt.slice(0, isoDateLength)}` : expiresAt;
|
|
5802
6085
|
};
|
|
5803
6086
|
const projectFrom$2 = async (run, given) => {
|
|
5804
6087
|
if (given === void 0) throw new UsageError("This command needs a project.", "Pass --project <name>; kcloud projects list shows them.");
|
|
5805
6088
|
return resolveProjectId(run, given);
|
|
5806
6089
|
};
|
|
5807
6090
|
const credentialsOf = async (api, projectId) => dataOf("list the credentials of the project", await api.GET("/projects/{id}/credentials", { params: { path: { id: projectId } } }), credentialHints).items;
|
|
5808
|
-
const
|
|
6091
|
+
const resolveCredential = async (api, projectId, reference) => {
|
|
5809
6092
|
const credentials = await credentialsOf(api, projectId);
|
|
5810
6093
|
const found = credentials.filter((credential) => credential.id === reference || credential.label === reference);
|
|
5811
6094
|
const [first] = found;
|
|
5812
6095
|
if (first === void 0) throw new CliError(`The project has no credential ${reference}.`, credentials.length === 0 ? "kcloud credentials add makes one." : `Its credentials are ${credentials.map(({ label }) => label).join(", ")}.`);
|
|
5813
6096
|
if (found.length > 1) throw new CliError(`${reference} names ${found.length} credentials of the project.`, "Name it by its id instead.");
|
|
5814
|
-
return first
|
|
6097
|
+
return first;
|
|
5815
6098
|
};
|
|
6099
|
+
const resolveCredentialId = async (api, projectId, reference) => (await resolveCredential(api, projectId, reference)).id;
|
|
5816
6100
|
const createCredentialsCommand = (adaptersOf) => defineGroup({
|
|
5817
6101
|
name: "credentials",
|
|
5818
6102
|
summary: "The Claude and GitHub credentials of a project.",
|
|
@@ -5838,13 +6122,15 @@ const createCredentialsCommand = (adaptersOf) => defineGroup({
|
|
|
5838
6122
|
{ header: "FINGERPRINT" },
|
|
5839
6123
|
{ header: "VERIFIED" },
|
|
5840
6124
|
{ header: "EXPIRES" },
|
|
6125
|
+
{ header: "USAGE" },
|
|
5841
6126
|
{ header: "ID" }
|
|
5842
6127
|
], credentials.map((credential) => [
|
|
5843
6128
|
credential.label,
|
|
5844
6129
|
credential.kind,
|
|
5845
6130
|
credential.fingerprint,
|
|
5846
6131
|
credential.verification,
|
|
5847
|
-
credential
|
|
6132
|
+
expiryOf(credential),
|
|
6133
|
+
usageSummaryOf(credential),
|
|
5848
6134
|
credential.id
|
|
5849
6135
|
])));
|
|
5850
6136
|
}
|
|
@@ -5990,6 +6276,34 @@ const createCredentialsCommand = (adaptersOf) => defineGroup({
|
|
|
5990
6276
|
return said(run, `The instance is verifying ${credentialId}; kcloud credentials list shows the answer.`);
|
|
5991
6277
|
}
|
|
5992
6278
|
}),
|
|
6279
|
+
defineCommand({
|
|
6280
|
+
name: "usage",
|
|
6281
|
+
summary: "Show the usage of the Claude subscription behind a Claude Code OAuth token.",
|
|
6282
|
+
usage: "credentials usage <credential> --project <name> [--refresh]",
|
|
6283
|
+
optionsUsage: [...projectUsage$2, ["--refresh", "read it from Anthropic now; within 30 seconds the last reading"]],
|
|
6284
|
+
options: {
|
|
6285
|
+
...projectOption$2,
|
|
6286
|
+
refresh: {
|
|
6287
|
+
type: "boolean",
|
|
6288
|
+
default: false
|
|
6289
|
+
}
|
|
6290
|
+
},
|
|
6291
|
+
positionals: {
|
|
6292
|
+
min: 1,
|
|
6293
|
+
max: 1
|
|
6294
|
+
},
|
|
6295
|
+
run: async (invocation) => {
|
|
6296
|
+
const run = await runOf(adaptersOf, invocation);
|
|
6297
|
+
const projectId = await projectFrom$2(run, invocation.values.project);
|
|
6298
|
+
const stored = await resolveCredential(run.api, projectId, invocation.positionals[0] ?? "");
|
|
6299
|
+
if (stored.kind !== "claude_oauth_token") throw new CliError(`${stored.label} is a ${stored.kind}, which reports no usage.`, usageUnavailable);
|
|
6300
|
+
const credential = invocation.values.refresh ? dataOf("read the usage of the credential", await run.api.POST("/projects/{id}/credentials/{credentialId}/usage", { params: { path: {
|
|
6301
|
+
id: projectId,
|
|
6302
|
+
credentialId: stored.id
|
|
6303
|
+
} } }), credentialHints) : stored;
|
|
6304
|
+
return printed(run, credential, usageReportOf);
|
|
6305
|
+
}
|
|
6306
|
+
}),
|
|
5993
6307
|
defineCommand({
|
|
5994
6308
|
name: "delete",
|
|
5995
6309
|
summary: "Delete a credential no agent uses.",
|
|
@@ -6851,19 +7165,31 @@ const credentialTools = [
|
|
|
6851
7165
|
}),
|
|
6852
7166
|
defineTool({
|
|
6853
7167
|
name: "credential_update",
|
|
6854
|
-
title: "Rename or
|
|
6855
|
-
description: "rename gives a credential a new label; verify asks the instance to check it against its service, and credentials_list shows the answer.",
|
|
6856
|
-
operations: [
|
|
7168
|
+
title: "Rename, verify or read the usage of a credential",
|
|
7169
|
+
description: "rename gives a credential a new label; verify asks the instance to check it against its service, and credentials_list shows the answer; refresh_usage reads the usage of the Claude subscription behind a Claude Code OAuth token now, or answers the last reading within 30 seconds of the previous one.",
|
|
7170
|
+
operations: [
|
|
7171
|
+
"renameCredential",
|
|
7172
|
+
"verifyCredential",
|
|
7173
|
+
"refreshCredentialUsage"
|
|
7174
|
+
],
|
|
6857
7175
|
safety: "write",
|
|
6858
7176
|
input: z.object({
|
|
6859
7177
|
project: projectField,
|
|
6860
7178
|
credential: credentialField$1,
|
|
6861
|
-
action: z.enum([
|
|
7179
|
+
action: z.enum([
|
|
7180
|
+
"rename",
|
|
7181
|
+
"verify",
|
|
7182
|
+
"refresh_usage"
|
|
7183
|
+
]),
|
|
6862
7184
|
label: z.string().trim().min(1).optional().describe("The new label, for rename.")
|
|
6863
7185
|
}),
|
|
6864
7186
|
run: async ({ project, credential, action, label }, run) => {
|
|
6865
7187
|
const projectId = await projectIdOf(run, project);
|
|
6866
7188
|
const credentialId = await resolveCredentialId(run.api, projectId, credential);
|
|
7189
|
+
if (action === "refresh_usage") return dataOf("read the usage of the credential", await run.api.POST("/projects/{id}/credentials/{credentialId}/usage", { params: { path: {
|
|
7190
|
+
id: projectId,
|
|
7191
|
+
credentialId
|
|
7192
|
+
} } }), credentialHints);
|
|
6867
7193
|
if (action === "verify") {
|
|
6868
7194
|
doneWith("verify the credential", await run.api.POST("/projects/{id}/credentials/{credentialId}/verify", { params: { path: {
|
|
6869
7195
|
id: projectId,
|
|
@@ -7204,19 +7530,40 @@ const observabilityTools = [
|
|
|
7204
7530
|
defineTool({
|
|
7205
7531
|
name: "agent_memory_search",
|
|
7206
7532
|
title: "Search the memory of an agent",
|
|
7207
|
-
description: "Search what an agent remembers of the tasks it finished.",
|
|
7533
|
+
description: "Search what an agent remembers of the tasks it finished, newest first, one page or every summary with all.",
|
|
7208
7534
|
operations: ["searchAgentMemory"],
|
|
7209
7535
|
safety: "read",
|
|
7210
7536
|
input: z.object({
|
|
7211
7537
|
...agentFields,
|
|
7212
7538
|
query: z.string().max(200).optional(),
|
|
7213
|
-
|
|
7539
|
+
outcome: memoryOutcomeSchema.optional().describe("Only the summaries of tasks that succeeded or failed."),
|
|
7540
|
+
limit: z.int().min(1).max(100).default(20),
|
|
7541
|
+
all: pageFields.all
|
|
7542
|
+
}),
|
|
7543
|
+
run: async ({ query, outcome, limit, all, ...reference }, run) => memoryOf(run.api, await agentIdOf(run, reference), {
|
|
7544
|
+
query,
|
|
7545
|
+
outcome
|
|
7546
|
+
}, {
|
|
7547
|
+
limit,
|
|
7548
|
+
all
|
|
7549
|
+
})
|
|
7550
|
+
}),
|
|
7551
|
+
defineTool({
|
|
7552
|
+
name: "agent_earlier_output",
|
|
7553
|
+
title: "Read the earlier output of an agent",
|
|
7554
|
+
description: "Read what an agent wrote before an event, oldest first: the newest page without before. Pass nextCursor as before to read the page before that; null means the stored output begins here.",
|
|
7555
|
+
operations: ["listAgentLiveHistory"],
|
|
7556
|
+
safety: "read",
|
|
7557
|
+
input: z.object({
|
|
7558
|
+
...agentFields,
|
|
7559
|
+
before: z.string().trim().min(1).max(64).optional().describe("End the page before this event id."),
|
|
7560
|
+
limit: z.int().min(1).max(500).optional().describe("How many events to read.")
|
|
7214
7561
|
}),
|
|
7215
|
-
run: async ({
|
|
7562
|
+
run: async ({ before, limit, ...reference }, run) => dataOf("read the earlier output of the agent", await run.api.GET("/agents/{id}/live/history", { params: {
|
|
7216
7563
|
path: { id: await agentIdOf(run, reference) },
|
|
7217
7564
|
query: {
|
|
7218
|
-
|
|
7219
|
-
...
|
|
7565
|
+
...before === void 0 ? {} : { before },
|
|
7566
|
+
...limit === void 0 ? {} : { limit }
|
|
7220
7567
|
}
|
|
7221
7568
|
} }), agentHints)
|
|
7222
7569
|
}),
|
|
@@ -7264,6 +7611,70 @@ const observabilityTools = [
|
|
|
7264
7611
|
})
|
|
7265
7612
|
];
|
|
7266
7613
|
//#endregion
|
|
7614
|
+
//#region src/commands/logo.ts
|
|
7615
|
+
const logoOptions = {
|
|
7616
|
+
logo: { type: "string" },
|
|
7617
|
+
"no-logo": {
|
|
7618
|
+
type: "boolean",
|
|
7619
|
+
default: false
|
|
7620
|
+
}
|
|
7621
|
+
};
|
|
7622
|
+
const logoUsage = [["--logo <path>", "show a PNG, JPEG or WebP image instead of the color, at most 512 KB and 2048 px on a side"], ["--no-logo", "remove the logo and show the color again"]];
|
|
7623
|
+
const logoHints = {
|
|
7624
|
+
"logo.unreadable": "The file has to be a PNG, JPEG or WebP image.",
|
|
7625
|
+
"logo.too_small": "The image needs at least 64 pixels on each side.",
|
|
7626
|
+
"logo.too_large": "The image may have at most 2048 pixels on a side."
|
|
7627
|
+
};
|
|
7628
|
+
const startsWith = (bytes, prefix, at = 0) => prefix.every((byte, index) => bytes[at + index] === byte);
|
|
7629
|
+
const ascii = (text) => Array.from(text, (letter) => letter.charCodeAt(0));
|
|
7630
|
+
const signatures = [
|
|
7631
|
+
{
|
|
7632
|
+
contentType: "image/png",
|
|
7633
|
+
matches: (bytes) => startsWith(bytes, [
|
|
7634
|
+
137,
|
|
7635
|
+
...ascii("PNG\r\n"),
|
|
7636
|
+
26,
|
|
7637
|
+
10
|
|
7638
|
+
])
|
|
7639
|
+
},
|
|
7640
|
+
{
|
|
7641
|
+
contentType: "image/jpeg",
|
|
7642
|
+
matches: (bytes) => startsWith(bytes, [
|
|
7643
|
+
255,
|
|
7644
|
+
216,
|
|
7645
|
+
255
|
|
7646
|
+
])
|
|
7647
|
+
},
|
|
7648
|
+
{
|
|
7649
|
+
contentType: "image/webp",
|
|
7650
|
+
matches: (bytes) => startsWith(bytes, ascii("RIFF")) && startsWith(bytes, ascii("WEBP"), 8)
|
|
7651
|
+
}
|
|
7652
|
+
];
|
|
7653
|
+
const isLogoChange = (change) => change.logo !== void 0 || change["no-logo"] === true;
|
|
7654
|
+
const logoFileOf = async (workingDirectory, file) => {
|
|
7655
|
+
const path = resolve(workingDirectory, file);
|
|
7656
|
+
const bytes = await readFile(path).catch(() => {
|
|
7657
|
+
throw new CliError(`${path} could not be read.`);
|
|
7658
|
+
});
|
|
7659
|
+
if (bytes.byteLength > 524288) throw new UsageError(`${file} holds ${String(Math.ceil(bytes.byteLength / 1024))} KB, and a logo at most ${String(logoMaxBytes / 1024)} KB.`);
|
|
7660
|
+
const contentType = signatures.find(({ matches }) => matches(bytes))?.contentType;
|
|
7661
|
+
if (contentType === void 0) throw new UsageError(`${file} is not a PNG, JPEG or WebP image.`);
|
|
7662
|
+
return {
|
|
7663
|
+
bytes,
|
|
7664
|
+
contentType
|
|
7665
|
+
};
|
|
7666
|
+
};
|
|
7667
|
+
const logoActionOf = async (workingDirectory, change) => {
|
|
7668
|
+
if (change.logo !== void 0 && change["no-logo"] === true) throw new UsageError("--logo and --no-logo exclude each other.");
|
|
7669
|
+
if (change.logo !== void 0) return logoFileOf(workingDirectory, change.logo);
|
|
7670
|
+
return change["no-logo"] === true ? "remove" : null;
|
|
7671
|
+
};
|
|
7672
|
+
const logoUpload = ({ bytes, contentType }) => ({
|
|
7673
|
+
body: bytes,
|
|
7674
|
+
bodySerializer: (body) => body,
|
|
7675
|
+
headers: { "content-type": contentType }
|
|
7676
|
+
});
|
|
7677
|
+
//#endregion
|
|
7267
7678
|
//#region src/commands/roles.ts
|
|
7268
7679
|
const roleOption = { role: { type: "string" } };
|
|
7269
7680
|
const roleUsage = ["--role <role>", "admin or member"];
|
|
@@ -7573,6 +7984,48 @@ const costsText = (costs, { from, to }, slugs) => [
|
|
|
7573
7984
|
"",
|
|
7574
7985
|
`${costs.totalCostUsd.toFixed(2)} over ${costs.totalRuns} runs and ${costs.sessions} sessions.`
|
|
7575
7986
|
].join("\n");
|
|
7987
|
+
const organizationChangeHints = {
|
|
7988
|
+
...organizationHints,
|
|
7989
|
+
"organization.slug_taken": "Another organization already uses that slug.",
|
|
7990
|
+
"organization.invalid_slug": "A slug holds lowercase letters, digits and hyphens.",
|
|
7991
|
+
"auth.forbidden": "Only an admin of the organization changes it.",
|
|
7992
|
+
...logoHints
|
|
7993
|
+
};
|
|
7994
|
+
const followSlug = async (homeDirectory, origin, before, after) => {
|
|
7995
|
+
const store = await readInstanceStore(homeDirectory);
|
|
7996
|
+
const stored = store.instances[origin];
|
|
7997
|
+
if (stored?.organization !== before.slug) return;
|
|
7998
|
+
await writeInstanceStore(homeDirectory, withInstance(store, origin, {
|
|
7999
|
+
...stored,
|
|
8000
|
+
organization: after.slug
|
|
8001
|
+
}));
|
|
8002
|
+
};
|
|
8003
|
+
const updatedOrganization = async (api, current, fields) => {
|
|
8004
|
+
if ([
|
|
8005
|
+
fields.name,
|
|
8006
|
+
fields.slug,
|
|
8007
|
+
fields.color
|
|
8008
|
+
].every((field) => field === void 0)) return current;
|
|
8009
|
+
const body = {
|
|
8010
|
+
name: fields.name ?? current.name,
|
|
8011
|
+
slug: fields.slug ?? current.slug,
|
|
8012
|
+
color: fields.color ?? current.color
|
|
8013
|
+
};
|
|
8014
|
+
validated(updateOrganizationBodySchema, body, "a change to an organization");
|
|
8015
|
+
return dataOf("change the organization", await api.PATCH("/organizations/{organizationId}", {
|
|
8016
|
+
params: { path: { organizationId: current.id } },
|
|
8017
|
+
body
|
|
8018
|
+
}), organizationChangeHints);
|
|
8019
|
+
};
|
|
8020
|
+
const withLogo = async (api, organization, logo) => {
|
|
8021
|
+
const params = { path: { organizationId: organization.id } };
|
|
8022
|
+
if (logo === null) return organization;
|
|
8023
|
+
if (logo === "remove") return dataOf("remove the logo of the organization", await api.DELETE("/organizations/{organizationId}/logo", { params }), organizationChangeHints);
|
|
8024
|
+
return dataOf("set the logo of the organization", await api.PUT("/organizations/{organizationId}/logo", {
|
|
8025
|
+
params,
|
|
8026
|
+
...logoUpload(logo)
|
|
8027
|
+
}), organizationChangeHints);
|
|
8028
|
+
};
|
|
7576
8029
|
const createOrgsCommand = (adaptersOf) => defineGroup({
|
|
7577
8030
|
name: "orgs",
|
|
7578
8031
|
summary: "The organizations you can see, and the one kcloud acts in.",
|
|
@@ -7657,11 +8110,52 @@ const createOrgsCommand = (adaptersOf) => defineGroup({
|
|
|
7657
8110
|
return printed(run, organization, (shown) => renderTable([{ header: "FIELD" }, { header: "VALUE" }], [
|
|
7658
8111
|
["name", shown.name],
|
|
7659
8112
|
["slug", shown.slug],
|
|
8113
|
+
["color", shown.color],
|
|
8114
|
+
["logo", shown.logoHash ?? "-"],
|
|
7660
8115
|
["created", shown.createdAt],
|
|
7661
8116
|
["id", shown.id]
|
|
7662
8117
|
]));
|
|
7663
8118
|
}
|
|
7664
8119
|
}),
|
|
8120
|
+
defineCommand({
|
|
8121
|
+
name: "set",
|
|
8122
|
+
summary: "Change the name, slug, color or logo of an organization.",
|
|
8123
|
+
usage: "orgs set [<organization>] [--name <name>] [--slug <slug>] [--color <#rrggbb>] [--logo <path> | --no-logo]",
|
|
8124
|
+
optionsUsage: [
|
|
8125
|
+
["--name <name>", "the new name"],
|
|
8126
|
+
["--slug <slug>", "the new slug; scripts and --org that name the old one stop finding it"],
|
|
8127
|
+
["--color <#rrggbb>", "the color of the organization in the panel"],
|
|
8128
|
+
...logoUsage
|
|
8129
|
+
],
|
|
8130
|
+
options: {
|
|
8131
|
+
name: { type: "string" },
|
|
8132
|
+
slug: { type: "string" },
|
|
8133
|
+
color: { type: "string" },
|
|
8134
|
+
...logoOptions
|
|
8135
|
+
},
|
|
8136
|
+
positionals: {
|
|
8137
|
+
min: 0,
|
|
8138
|
+
max: 1
|
|
8139
|
+
},
|
|
8140
|
+
run: async (invocation) => {
|
|
8141
|
+
const { values } = invocation;
|
|
8142
|
+
if ([
|
|
8143
|
+
values.name,
|
|
8144
|
+
values.slug,
|
|
8145
|
+
values.color
|
|
8146
|
+
].every((value) => value === void 0) && !isLogoChange(values)) throw new UsageError("orgs set needs --name, --slug, --color, --logo or --no-logo.");
|
|
8147
|
+
const logo = await logoActionOf(invocation.context.workingDirectory, values);
|
|
8148
|
+
const run = await runOf(adaptersOf, invocation);
|
|
8149
|
+
const current = await organizationOrInUse(run, invocation.positionals[0]);
|
|
8150
|
+
const updated = await updatedOrganization(run.api, current, values);
|
|
8151
|
+
if (updated.slug !== current.slug) {
|
|
8152
|
+
await followSlug(run.context.homeDirectory, run.origin, current, updated);
|
|
8153
|
+
run.context.terminal.error(run.context.terminal.errorStyles.warning(`warning Scripts that pass --org ${current.slug} or set ${organizationVariable}=${current.slug} need ${updated.slug} now.`));
|
|
8154
|
+
}
|
|
8155
|
+
const organization = await withLogo(run.api, updated, logo);
|
|
8156
|
+
return printed(run, organization, (shown) => `Changed ${shown.name} (${shown.slug}).`);
|
|
8157
|
+
}
|
|
8158
|
+
}),
|
|
7665
8159
|
defineCommand({
|
|
7666
8160
|
name: "costs",
|
|
7667
8161
|
summary: "Show what the organization spent, by day and by project.",
|
|
@@ -7726,6 +8220,20 @@ const organizationTools = [
|
|
|
7726
8220
|
return dataOf("describe the organization", await run.api.GET("/organizations/{organizationId}", { params: { path: { organizationId: id } } }), organizationHints);
|
|
7727
8221
|
}
|
|
7728
8222
|
}),
|
|
8223
|
+
defineTool({
|
|
8224
|
+
name: "organization_update",
|
|
8225
|
+
title: "Change an organization",
|
|
8226
|
+
description: "Change the name or color of an organization, by default the one this server acts in. A field left out keeps its value. A new slug is set with kcloud orgs set or in the panel.",
|
|
8227
|
+
operations: ["listOrganizations", "updateOrganization"],
|
|
8228
|
+
safety: "write",
|
|
8229
|
+
idempotent: true,
|
|
8230
|
+
input: z.object({
|
|
8231
|
+
organization: z.string().trim().min(1).optional().describe("The organization, by its slug, its name or its id."),
|
|
8232
|
+
name: organizationNameSchema.optional(),
|
|
8233
|
+
color: colorSchema.optional().describe("The color in the panel, as #rrggbb.")
|
|
8234
|
+
}),
|
|
8235
|
+
run: async ({ organization, ...change }, run) => updatedOrganization(run.api, await organizationOrInUse(run, organization), change)
|
|
8236
|
+
}),
|
|
7729
8237
|
defineTool({
|
|
7730
8238
|
name: "organization_costs",
|
|
7731
8239
|
title: "Show what the organization spent",
|
|
@@ -8255,19 +8763,37 @@ const createdProject = async (scope, name, { slug, description, color }) => {
|
|
|
8255
8763
|
organization
|
|
8256
8764
|
};
|
|
8257
8765
|
};
|
|
8258
|
-
const
|
|
8259
|
-
|
|
8260
|
-
|
|
8261
|
-
|
|
8262
|
-
|
|
8263
|
-
|
|
8264
|
-
|
|
8265
|
-
|
|
8266
|
-
|
|
8267
|
-
|
|
8268
|
-
|
|
8269
|
-
|
|
8270
|
-
|
|
8766
|
+
const logoHint = {
|
|
8767
|
+
...projectHint,
|
|
8768
|
+
...logoHints
|
|
8769
|
+
};
|
|
8770
|
+
const changedProject = async (api, id, change, logo = null) => {
|
|
8771
|
+
const params = { path: { id } };
|
|
8772
|
+
let project = await projectOf(api, id);
|
|
8773
|
+
if ([
|
|
8774
|
+
change.name,
|
|
8775
|
+
change.slug,
|
|
8776
|
+
change.description,
|
|
8777
|
+
change.color
|
|
8778
|
+
].some((field) => field !== void 0)) {
|
|
8779
|
+
const body = {
|
|
8780
|
+
name: change.name ?? project.name,
|
|
8781
|
+
slug: change.slug ?? project.slug,
|
|
8782
|
+
description: change.description ?? project.description,
|
|
8783
|
+
color: change.color ?? project.color
|
|
8784
|
+
};
|
|
8785
|
+
validated(updateProjectBodySchema, body, "a change to a project");
|
|
8786
|
+
project = dataOf("change the project", await api.PATCH("/projects/{id}", {
|
|
8787
|
+
params,
|
|
8788
|
+
body
|
|
8789
|
+
}), projectHint);
|
|
8790
|
+
}
|
|
8791
|
+
if (logo !== null && logo !== "remove") project = dataOf("set the logo of the project", await api.PUT("/projects/{id}/logo", {
|
|
8792
|
+
params,
|
|
8793
|
+
...logoUpload(logo)
|
|
8794
|
+
}), logoHint);
|
|
8795
|
+
if (logo === "remove") project = dataOf("remove the logo of the project", await api.DELETE("/projects/{id}/logo", { params }), logoHint);
|
|
8796
|
+
return project;
|
|
8271
8797
|
};
|
|
8272
8798
|
const deleteProject = async (api, id) => {
|
|
8273
8799
|
doneWith("delete the project", await api.DELETE("/projects/{id}", { params: { path: { id } } }), projectHint);
|
|
@@ -8326,6 +8852,7 @@ const createProjectsCommand = (adaptersOf) => defineGroup({
|
|
|
8326
8852
|
["slug", shown.slug],
|
|
8327
8853
|
["description", shown.description === "" ? "-" : shown.description],
|
|
8328
8854
|
["color", shown.color],
|
|
8855
|
+
["logo", shown.logoHash ?? "-"],
|
|
8329
8856
|
["docker", shown.dockerAllowed ? "allowed" : "not allowed"],
|
|
8330
8857
|
["members", String(shown.memberCount)],
|
|
8331
8858
|
["created", shown.createdAt],
|
|
@@ -8351,12 +8878,17 @@ const createProjectsCommand = (adaptersOf) => defineGroup({
|
|
|
8351
8878
|
}),
|
|
8352
8879
|
defineCommand({
|
|
8353
8880
|
name: "update",
|
|
8354
|
-
summary: "Change the name, slug, description or
|
|
8355
|
-
usage: "projects update <project> [--name <name>] [--slug <slug>] [--description <text>] [--color <#rrggbb>]",
|
|
8356
|
-
optionsUsage: [
|
|
8881
|
+
summary: "Change the name, slug, description, color or logo of a project.",
|
|
8882
|
+
usage: "projects update <project> [--name <name>] [--slug <slug>] [--description <text>] [--color <#rrggbb>] [--logo <path> | --no-logo]",
|
|
8883
|
+
optionsUsage: [
|
|
8884
|
+
["--name <name>", "the new name"],
|
|
8885
|
+
...fieldUsage,
|
|
8886
|
+
...logoUsage
|
|
8887
|
+
],
|
|
8357
8888
|
options: {
|
|
8358
8889
|
name: { type: "string" },
|
|
8359
|
-
...fieldOptions
|
|
8890
|
+
...fieldOptions,
|
|
8891
|
+
...logoOptions
|
|
8360
8892
|
},
|
|
8361
8893
|
positionals: {
|
|
8362
8894
|
min: 1,
|
|
@@ -8369,10 +8901,11 @@ const createProjectsCommand = (adaptersOf) => defineGroup({
|
|
|
8369
8901
|
values.slug,
|
|
8370
8902
|
values.description,
|
|
8371
8903
|
values.color
|
|
8372
|
-
].every((value) => value === void 0)) throw new UsageError("projects update needs --name, --slug, --description or --
|
|
8904
|
+
].every((value) => value === void 0) && !isLogoChange(values)) throw new UsageError("projects update needs --name, --slug, --description, --color, --logo or --no-logo.");
|
|
8905
|
+
const logo = await logoActionOf(invocation.context.workingDirectory, values);
|
|
8373
8906
|
const run = await runOf(adaptersOf, invocation);
|
|
8374
8907
|
const id = await resolveProjectId(run, invocation.positionals[0] ?? "");
|
|
8375
|
-
const project = await changedProject(run.api, id, values);
|
|
8908
|
+
const project = await changedProject(run.api, id, values, logo);
|
|
8376
8909
|
return printed(run, project, (shown) => `Changed ${shown.name} (${shown.slug}).`);
|
|
8377
8910
|
}
|
|
8378
8911
|
}),
|
|
@@ -8919,17 +9452,40 @@ const taskTools = [
|
|
|
8919
9452
|
defineTool({
|
|
8920
9453
|
name: "agent_tasks_list",
|
|
8921
9454
|
title: "List the tasks of an agent",
|
|
8922
|
-
description: "List the task queue of an agent, with the status, attempts and cost of each task.",
|
|
9455
|
+
description: "List the task queue of an agent, newest first, with the status, attempts and cost of each task.",
|
|
8923
9456
|
operations: ["listAgentTasks"],
|
|
8924
9457
|
safety: "read",
|
|
8925
9458
|
input: z.object({
|
|
8926
9459
|
...agentFields,
|
|
9460
|
+
...pageFields,
|
|
8927
9461
|
status: z.enum(taskStatuses).optional()
|
|
8928
9462
|
}),
|
|
8929
|
-
run: async ({ status, ...reference }, run) =>
|
|
8930
|
-
|
|
8931
|
-
|
|
8932
|
-
|
|
9463
|
+
run: async ({ status, limit, all, ...reference }, run) => {
|
|
9464
|
+
const agentId = await agentIdOf(run, reference);
|
|
9465
|
+
return collect(async (cursor, size) => dataOf("list the tasks of the agent", await run.api.GET("/agents/{id}/tasks", { params: {
|
|
9466
|
+
path: { id: agentId },
|
|
9467
|
+
query: {
|
|
9468
|
+
limit: size,
|
|
9469
|
+
...cursor === null ? {} : { cursor },
|
|
9470
|
+
...status === void 0 ? {} : { status }
|
|
9471
|
+
}
|
|
9472
|
+
} }), taskHints), windowOfPage({
|
|
9473
|
+
limit,
|
|
9474
|
+
all
|
|
9475
|
+
}));
|
|
9476
|
+
}
|
|
9477
|
+
}),
|
|
9478
|
+
defineTool({
|
|
9479
|
+
name: "agent_task_show",
|
|
9480
|
+
title: "Show a task",
|
|
9481
|
+
description: "Read one task of an agent with its description, status, attempts, cost and error.",
|
|
9482
|
+
operations: ["getAgentTask"],
|
|
9483
|
+
safety: "read",
|
|
9484
|
+
input: z.object(taskFields),
|
|
9485
|
+
run: async ({ task, ...reference }, run) => dataOf("read the task", await run.api.GET("/agents/{id}/tasks/{taskId}", { params: { path: {
|
|
9486
|
+
id: await agentIdOf(run, reference),
|
|
9487
|
+
taskId: task
|
|
9488
|
+
} } }), taskHints)
|
|
8933
9489
|
}),
|
|
8934
9490
|
defineTool({
|
|
8935
9491
|
name: "agent_task_add",
|
|
@@ -8979,6 +9535,33 @@ const taskTools = [
|
|
|
8979
9535
|
} } }), taskHints);
|
|
8980
9536
|
return { cancelled: task };
|
|
8981
9537
|
}
|
|
9538
|
+
}),
|
|
9539
|
+
defineTool({
|
|
9540
|
+
name: "agent_task_delete",
|
|
9541
|
+
title: "Delete a task",
|
|
9542
|
+
description: "Delete a done, failed or cancelled task of an agent. A monitor may report a deleted done task again.",
|
|
9543
|
+
operations: ["deleteAgentTask"],
|
|
9544
|
+
safety: "destructive",
|
|
9545
|
+
input: z.object(taskFields),
|
|
9546
|
+
run: async ({ task, ...reference }, run) => dataOf("delete the task", await run.api.DELETE("/agents/{id}/tasks/{taskId}", { params: { path: {
|
|
9547
|
+
id: await agentIdOf(run, reference),
|
|
9548
|
+
taskId: task
|
|
9549
|
+
} } }), taskHints)
|
|
9550
|
+
}),
|
|
9551
|
+
defineTool({
|
|
9552
|
+
name: "agent_tasks_delete",
|
|
9553
|
+
title: "Delete the tasks of a status",
|
|
9554
|
+
description: "Delete every task of an agent with one final status: done, failed or cancelled. A monitor may report a deleted done task again.",
|
|
9555
|
+
operations: ["deleteAgentTasks"],
|
|
9556
|
+
safety: "destructive",
|
|
9557
|
+
input: z.object({
|
|
9558
|
+
...agentFields,
|
|
9559
|
+
status: z.enum(finalTaskStatuses)
|
|
9560
|
+
}),
|
|
9561
|
+
run: async ({ status, ...reference }, run) => dataOf("delete the tasks", await run.api.DELETE("/agents/{id}/tasks", { params: {
|
|
9562
|
+
path: { id: await agentIdOf(run, reference) },
|
|
9563
|
+
query: { status }
|
|
9564
|
+
} }), taskHints)
|
|
8982
9565
|
})
|
|
8983
9566
|
];
|
|
8984
9567
|
//#endregion
|
|
@@ -9110,11 +9693,13 @@ const runtimeRowsOf = (runtime) => [
|
|
|
9110
9693
|
["agents", `${runtime.reconcile.agentsActive} active, ${runtime.reconcile.agentsQueued} queued`],
|
|
9111
9694
|
["log readers", String(runtime.ingest.readers)]
|
|
9112
9695
|
];
|
|
9696
|
+
const sessionsText = (count, noun) => `${String(count)} ${noun}${count === 1 ? "" : "s"}`;
|
|
9697
|
+
const heldPoolsOf = ({ held = [] }) => held;
|
|
9113
9698
|
const updateOf = ({ update }) => {
|
|
9114
9699
|
if (update === null) return "not reported";
|
|
9115
9700
|
switch (update.state) {
|
|
9116
9701
|
case "current": return "current";
|
|
9117
|
-
case "waiting": return `
|
|
9702
|
+
case "waiting": return heldPoolsOf(update).includes("monitor") ? `admissions held for ${update.target ?? "-"}, waits for ${sessionsText(update.sessions, "session")}` : `executor admissions held for ${update.target ?? "-"}, waits for ${sessionsText(update.sessions, "executor session")}; monitors go on until the executor pool is empty`;
|
|
9118
9703
|
case "updating": return `updating to ${update.target ?? "-"}`;
|
|
9119
9704
|
case "failed": return `failed to reach ${update.target ?? "-"}, runs its previous version`;
|
|
9120
9705
|
case "unmanaged": return "updated with its platform";
|