@kikimoradev/kcloud 1.1.0 → 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 +531 -174
- 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",
|
|
@@ -805,7 +826,6 @@ const errorCodes = Object.keys({
|
|
|
805
826
|
"node.size_unavailable": "rule_violation",
|
|
806
827
|
"node.size_not_larger": "rule_violation",
|
|
807
828
|
"node.region_unavailable": "rule_violation",
|
|
808
|
-
"node.import_refused": "rule_violation",
|
|
809
829
|
"provider.not_found": "not_found",
|
|
810
830
|
"provider.name_taken": "conflict",
|
|
811
831
|
"provider.in_use": "conflict",
|
|
@@ -819,20 +839,18 @@ const errorCodes = Object.keys({
|
|
|
819
839
|
"infrastructure.provider_error": "internal",
|
|
820
840
|
"infrastructure.operation_in_progress": "conflict",
|
|
821
841
|
"infrastructure.host_ops_disabled": "rule_violation",
|
|
822
|
-
"infrastructure.backups_unavailable": "rule_violation",
|
|
823
|
-
"infrastructure.maintenance_in_progress": "conflict",
|
|
824
|
-
"infrastructure.maintenance_not_in_progress": "conflict",
|
|
825
842
|
"backup.not_found": "not_found",
|
|
826
843
|
"backup.in_progress": "conflict",
|
|
827
844
|
"backup.unavailable": "rule_violation",
|
|
828
845
|
"backup.corrupt": "rule_violation",
|
|
829
846
|
"backup.undecryptable": "rule_violation",
|
|
830
|
-
"platform.version_unknown": "rule_violation",
|
|
831
|
-
"platform.same_version": "rule_violation",
|
|
832
|
-
"platform.image_unresolved": "rule_violation",
|
|
833
|
-
"platform.scripts_outdated": "rule_violation",
|
|
834
847
|
"volume.invalid_size": "rule_violation",
|
|
835
|
-
"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"
|
|
836
854
|
});
|
|
837
855
|
const errorCodeSchema = z.enum(errorCodes);
|
|
838
856
|
const errorDetailsSchema = z.record(z.string(), z.union([
|
|
@@ -1215,11 +1233,6 @@ z.object({
|
|
|
1215
1233
|
sidecarsRunning: z.int().nonnegative(),
|
|
1216
1234
|
volumeBytes: z.int().nonnegative().nullable()
|
|
1217
1235
|
}),
|
|
1218
|
-
sessionLogs: z.object({
|
|
1219
|
-
prunedDirectories: z.int().nonnegative(),
|
|
1220
|
-
lastPrunedAt: z.iso.datetime().nullable(),
|
|
1221
|
-
failures: z.int().nonnegative()
|
|
1222
|
-
}),
|
|
1223
1236
|
metrics: runtimeMetricsSchema,
|
|
1224
1237
|
deadLetters: z.array(deadLetterQueueSchema),
|
|
1225
1238
|
degradedAgents: z.array(degradedAgentSchema)
|
|
@@ -1442,17 +1455,21 @@ const memorySummarySchema = z.object({
|
|
|
1442
1455
|
sessionId: z.string().nullable(),
|
|
1443
1456
|
createdAt: z.iso.datetime()
|
|
1444
1457
|
}).meta({ id: "MemorySummary" });
|
|
1445
|
-
|
|
1446
|
-
|
|
1458
|
+
const memoryOutcomes = ["succeeded", "failed"];
|
|
1459
|
+
const memoryOutcomeSchema = z.enum(memoryOutcomes);
|
|
1460
|
+
pageSchema(memorySummarySchema).extend({
|
|
1447
1461
|
source: z.enum(["worker", "unavailable"]),
|
|
1448
1462
|
staleReason: z.enum([
|
|
1449
1463
|
"unreachable",
|
|
1450
1464
|
"timeout",
|
|
1451
1465
|
"stopped"
|
|
1452
|
-
]).nullable()
|
|
1453
|
-
|
|
1466
|
+
]).nullable(),
|
|
1467
|
+
truncated: z.boolean()
|
|
1468
|
+
}).meta({ id: "AgentMemoryPage" });
|
|
1454
1469
|
z.object({
|
|
1455
1470
|
q: z.string().max(200).optional(),
|
|
1471
|
+
outcome: memoryOutcomeSchema.optional(),
|
|
1472
|
+
cursor: cursorSchema.optional(),
|
|
1456
1473
|
limit: z.coerce.number().int().min(1).max(100).default(20)
|
|
1457
1474
|
});
|
|
1458
1475
|
const costPointSchema = z.object({
|
|
@@ -1565,7 +1582,7 @@ z.object({
|
|
|
1565
1582
|
organizationId: idSchema,
|
|
1566
1583
|
accountId: idSchema
|
|
1567
1584
|
});
|
|
1568
|
-
z.enum(["monitor", "executor"]);
|
|
1585
|
+
const sessionRoleSchema = z.enum(["monitor", "executor"]);
|
|
1569
1586
|
const sessionSlotCountSchema = z.int().min(1).max(64);
|
|
1570
1587
|
const sessionQueueEntrySchema = z.object({
|
|
1571
1588
|
agentId: idSchema,
|
|
@@ -1574,8 +1591,21 @@ const sessionQueueEntrySchema = z.object({
|
|
|
1574
1591
|
projectName: z.string().nullable(),
|
|
1575
1592
|
position: z.int().positive(),
|
|
1576
1593
|
since: z.iso.datetime(),
|
|
1577
|
-
taskId: z.string().nullable()
|
|
1594
|
+
taskId: z.string().nullable(),
|
|
1595
|
+
taskTitle: z.string().nullable(),
|
|
1596
|
+
readable: z.boolean()
|
|
1578
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" });
|
|
1579
1609
|
const sessionPoolSchema = z.object({
|
|
1580
1610
|
slots: sessionSlotCountSchema.nullable(),
|
|
1581
1611
|
effective: z.int().nonnegative(),
|
|
@@ -1602,6 +1632,12 @@ const agentAdmissionSchema = z.object({
|
|
|
1602
1632
|
monitor: agentAdmissionStateSchema.nullable(),
|
|
1603
1633
|
executor: agentAdmissionStateSchema.nullable()
|
|
1604
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" });
|
|
1605
1641
|
const nodeStateSchema = z.enum([
|
|
1606
1642
|
"pending",
|
|
1607
1643
|
"online",
|
|
@@ -1676,6 +1712,7 @@ const nodePoolSchema = z.object({
|
|
|
1676
1712
|
slots: z.int().nonnegative(),
|
|
1677
1713
|
inUse: z.int().nonnegative(),
|
|
1678
1714
|
waiting: z.array(sessionQueueEntrySchema),
|
|
1715
|
+
holders: z.array(sessionHolderSchema).nullable(),
|
|
1679
1716
|
usage: poolUsageSchema.nullable()
|
|
1680
1717
|
}).meta({ id: "NodePool" });
|
|
1681
1718
|
const nodePoolsSchema = z.object({
|
|
@@ -1761,6 +1798,7 @@ const nodeUpdateSchema = z.object({
|
|
|
1761
1798
|
target: z.string().nullable(),
|
|
1762
1799
|
sessions: z.int().nonnegative(),
|
|
1763
1800
|
since: z.iso.datetime().nullable(),
|
|
1801
|
+
held: z.array(sessionRoleSchema),
|
|
1764
1802
|
last: z.object({
|
|
1765
1803
|
outcome: z.enum([
|
|
1766
1804
|
"updated",
|
|
@@ -1895,16 +1933,26 @@ const splitFits = (split, host) => {
|
|
|
1895
1933
|
return overflow.cpus <= 0 && overflow.memoryMb <= 0;
|
|
1896
1934
|
};
|
|
1897
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
|
|
1898
1944
|
//#region ../../packages/contracts/src/identity.ts
|
|
1899
1945
|
const emailSchema = z.email().max(254);
|
|
1900
1946
|
const passwordSchema = z.string().min(12).max(128);
|
|
1901
1947
|
const userStatusSchema = z.enum(["active", "disabled"]);
|
|
1902
1948
|
const organizationNameSchema = z.string().trim().min(2).max(80);
|
|
1903
|
-
const organizationSlugSchema = z.string().regex(slugPattern);
|
|
1949
|
+
const organizationSlugSchema = z.string().regex(slugPattern).refine((slug) => !uuidShapePattern.test(slug));
|
|
1904
1950
|
const organizationSchema = z.object({
|
|
1905
1951
|
id: idSchema,
|
|
1906
1952
|
name: z.string(),
|
|
1907
1953
|
slug: organizationSlugSchema,
|
|
1954
|
+
color: colorSchema,
|
|
1955
|
+
logoHash: logoHashSchema.nullable(),
|
|
1908
1956
|
createdAt: z.iso.datetime()
|
|
1909
1957
|
}).meta({ id: "Organization" });
|
|
1910
1958
|
z.object({ items: z.array(organizationSchema) }).meta({ id: "OrganizationList" });
|
|
@@ -1962,6 +2010,11 @@ z.object({
|
|
|
1962
2010
|
initialPassword: passwordSchema.optional()
|
|
1963
2011
|
}).optional()
|
|
1964
2012
|
}).meta({ id: "CreateOrganizationBody" });
|
|
2013
|
+
const updateOrganizationBodySchema = z.object({
|
|
2014
|
+
name: organizationNameSchema,
|
|
2015
|
+
slug: organizationSlugSchema,
|
|
2016
|
+
color: colorSchema
|
|
2017
|
+
}).meta({ id: "UpdateOrganizationBody" });
|
|
1965
2018
|
z.object({ temporaryPassword: z.string() }).meta({ id: "TemporaryPassword" });
|
|
1966
2019
|
z.object({ items: z.array(userSchema) }).meta({ id: "UserList" });
|
|
1967
2020
|
z.object({ id: idSchema });
|
|
@@ -2030,6 +2083,9 @@ const auditActionSchema = z.enum([
|
|
|
2030
2083
|
"platform.bootstrapped",
|
|
2031
2084
|
"organization.created",
|
|
2032
2085
|
"organization.renamed",
|
|
2086
|
+
"organization.updated",
|
|
2087
|
+
"organization.logo_set",
|
|
2088
|
+
"organization.logo_removed",
|
|
2033
2089
|
"user.created",
|
|
2034
2090
|
"user.role_changed",
|
|
2035
2091
|
"user.disabled",
|
|
@@ -2043,6 +2099,8 @@ const auditActionSchema = z.enum([
|
|
|
2043
2099
|
"token.revoked",
|
|
2044
2100
|
"project.created",
|
|
2045
2101
|
"project.updated",
|
|
2102
|
+
"project.logo_set",
|
|
2103
|
+
"project.logo_removed",
|
|
2046
2104
|
"project.member_added",
|
|
2047
2105
|
"project.member_role_changed",
|
|
2048
2106
|
"project.member_removed",
|
|
@@ -2151,7 +2209,6 @@ pageSchema(auditEntrySchema).meta({ id: "AuditPage" });
|
|
|
2151
2209
|
//#endregion
|
|
2152
2210
|
//#region ../../packages/contracts/src/projects.ts
|
|
2153
2211
|
const slugSchema = z.string().regex(slugPattern);
|
|
2154
|
-
const colorSchema = z.string().regex(hexColorPattern);
|
|
2155
2212
|
const projectSchema = z.object({
|
|
2156
2213
|
id: idSchema,
|
|
2157
2214
|
organizationId: idSchema,
|
|
@@ -2159,6 +2216,7 @@ const projectSchema = z.object({
|
|
|
2159
2216
|
slug: slugSchema,
|
|
2160
2217
|
description: z.string(),
|
|
2161
2218
|
color: colorSchema,
|
|
2219
|
+
logoHash: logoHashSchema.nullable(),
|
|
2162
2220
|
dockerAllowed: z.boolean(),
|
|
2163
2221
|
memberCount: z.int().nonnegative(),
|
|
2164
2222
|
createdAt: z.iso.datetime(),
|
|
@@ -2587,6 +2645,7 @@ const agentRuntimeSchema = z.object({
|
|
|
2587
2645
|
stopMode: agentStopModeSchema.nullable(),
|
|
2588
2646
|
stopDeadlineAt: z.iso.datetime().nullable(),
|
|
2589
2647
|
admission: agentAdmissionSchema.nullable(),
|
|
2648
|
+
admissionHold: admissionHoldSchema.nullable(),
|
|
2590
2649
|
poolLimit: poolLimitSchema.nullable()
|
|
2591
2650
|
}).meta({ id: "AgentRuntime" });
|
|
2592
2651
|
const agentListItemSchema = z.object({
|
|
@@ -2935,19 +2994,6 @@ const nodeVolumesSchema = z.discriminatedUnion("source", [z.object({
|
|
|
2935
2994
|
source: z.literal("ok"),
|
|
2936
2995
|
items: z.array(nodeVolumeSchema)
|
|
2937
2996
|
}), unavailableBecause(["not_configured"])]).meta({ id: "NodeVolumes" });
|
|
2938
|
-
const dropletSizeSchema = z.object({
|
|
2939
|
-
slug: z.string(),
|
|
2940
|
-
vcpus: z.int().positive(),
|
|
2941
|
-
memoryMb: z.int().positive(),
|
|
2942
|
-
diskGb: z.int().positive(),
|
|
2943
|
-
priceMonthly: z.number().nonnegative(),
|
|
2944
|
-
command: z.string()
|
|
2945
|
-
}).meta({ id: "DropletSize" });
|
|
2946
|
-
const nodeSizesSchema = z.discriminatedUnion("source", [z.object({
|
|
2947
|
-
source: z.literal("ok"),
|
|
2948
|
-
items: z.array(dropletSizeSchema)
|
|
2949
|
-
}), unavailableBecause(providerUnavailableReasons)]).meta({ id: "NodeSizes" });
|
|
2950
|
-
const capacityForecastSchema = z.object({ byDisk: z.int().nonnegative().nullable() }).meta({ id: "CapacityForecast" });
|
|
2951
2997
|
z.object({
|
|
2952
2998
|
droplet: nodeDropletSchema,
|
|
2953
2999
|
volumes: nodeVolumesSchema,
|
|
@@ -2956,23 +3002,16 @@ z.object({
|
|
|
2956
3002
|
data: storageReadingSchema,
|
|
2957
3003
|
agents: storageReadingSchema
|
|
2958
3004
|
}),
|
|
2959
|
-
sizes: nodeSizesSchema,
|
|
2960
3005
|
platform: z.object({
|
|
2961
3006
|
version: z.string(),
|
|
2962
3007
|
instance: z.string(),
|
|
2963
3008
|
scriptsVersion: z.string(),
|
|
2964
3009
|
dockerVersion: z.string().nullable(),
|
|
2965
3010
|
dockerRootDir: z.string().nullable()
|
|
2966
|
-
})
|
|
2967
|
-
maintenance: z.object({
|
|
2968
|
-
quiescedAt: instantSchema$1.nullable(),
|
|
2969
|
-
pending: z.array(idSchema)
|
|
2970
|
-
}),
|
|
2971
|
-
capacity: capacityForecastSchema
|
|
3011
|
+
})
|
|
2972
3012
|
}).meta({ id: "PlatformNode" });
|
|
2973
3013
|
z.object({ role: volumeRoleSchema });
|
|
2974
3014
|
z.object({ sizeGb: z.int().positive() }).meta({ id: "ResizeVolumeBody" });
|
|
2975
|
-
z.object({ agents: z.int().nonnegative() }).meta({ id: "QuiesceResult" });
|
|
2976
3015
|
const requesterSchema = z.object({
|
|
2977
3016
|
userId: idSchema,
|
|
2978
3017
|
label: z.string().nullable()
|
|
@@ -3076,22 +3115,17 @@ const registryUnavailableSchema = z.object({
|
|
|
3076
3115
|
detail: z.string().nullable()
|
|
3077
3116
|
}).meta({ id: "RegistryUnavailable" });
|
|
3078
3117
|
const hostOperationsAvailabilities = ["enabled", "disabled"];
|
|
3079
|
-
const backupsAvailabilities = ["enabled", "unavailable"];
|
|
3080
3118
|
const installedImageSchema = z.string().regex(pinnedPlatformImagePattern).nullable();
|
|
3081
3119
|
z.object({
|
|
3082
3120
|
version: z.string(),
|
|
3121
|
+
environment: z.string().nullable(),
|
|
3083
3122
|
apiImage: installedImageSchema,
|
|
3084
3123
|
webImage: installedImageSchema,
|
|
3085
3124
|
nodeImage: installedImageSchema,
|
|
3086
3125
|
latest: z.string().nullable(),
|
|
3087
3126
|
latestError: registryUnavailableSchema.nullable(),
|
|
3088
|
-
versions: z.array(z.string()),
|
|
3089
|
-
upgradeable: z.boolean(),
|
|
3090
|
-
scriptsVersion: z.string(),
|
|
3091
|
-
expectedScriptsVersion: z.string(),
|
|
3092
3127
|
imagePrefix: z.string(),
|
|
3093
3128
|
hostOperations: z.enum(hostOperationsAvailabilities),
|
|
3094
|
-
backups: z.enum(backupsAvailabilities),
|
|
3095
3129
|
operations: z.array(hostOperationSchema)
|
|
3096
3130
|
}).meta({ id: "Platform" });
|
|
3097
3131
|
const keyVersionSchema = z.int().positive();
|
|
@@ -3114,7 +3148,6 @@ z.object({
|
|
|
3114
3148
|
versions: z.array(masterKeyVersionSchema),
|
|
3115
3149
|
rotationPending: z.boolean()
|
|
3116
3150
|
}).meta({ id: "MasterKey" });
|
|
3117
|
-
z.object({ version: z.string().regex(versionTagPattern) }).meta({ id: "UpgradePlatformBody" });
|
|
3118
3151
|
const metricsRangeSchema = z.enum([
|
|
3119
3152
|
"1h",
|
|
3120
3153
|
"24h",
|
|
@@ -3195,24 +3228,6 @@ z.object({
|
|
|
3195
3228
|
}).meta({ id: "AgentMetrics" });
|
|
3196
3229
|
z.object({ range: metricsRangeSchema });
|
|
3197
3230
|
z.object({ range: agentMetricsRangeSchema });
|
|
3198
|
-
z.object({
|
|
3199
|
-
nodeId: idSchema,
|
|
3200
|
-
agentId: idSchema
|
|
3201
|
-
});
|
|
3202
|
-
z.object({ agentIds: z.array(idSchema).min(1).max(500) });
|
|
3203
|
-
z.object({ occupied: z.array(idSchema) });
|
|
3204
|
-
z.object({ offset: z.coerce.number().int().nonnegative() });
|
|
3205
|
-
z.object({ bytes: z.int().nonnegative() });
|
|
3206
|
-
z.object({
|
|
3207
|
-
bytes: z.int().nonnegative(),
|
|
3208
|
-
sha256: z.string().regex(/^[0-9a-f]{64}$/u)
|
|
3209
|
-
});
|
|
3210
|
-
z.object({
|
|
3211
|
-
bytes: z.int().nonnegative(),
|
|
3212
|
-
files: z.int().nonnegative(),
|
|
3213
|
-
memoryCheck: z.string()
|
|
3214
|
-
});
|
|
3215
|
-
z.object({ removed: z.boolean() });
|
|
3216
3231
|
const notificationKindSchema = z.enum([
|
|
3217
3232
|
"credential.verification_failed",
|
|
3218
3233
|
"credential.expiring",
|
|
@@ -3368,6 +3383,7 @@ const detailsOf$1 = (body) => {
|
|
|
3368
3383
|
const parsed = errorBodySchema.safeParse(body);
|
|
3369
3384
|
return parsed.success ? parsed.data.details ?? {} : {};
|
|
3370
3385
|
};
|
|
3386
|
+
const hintText = (hint, details) => typeof hint === "string" ? hint : hint(details);
|
|
3371
3387
|
//#endregion
|
|
3372
3388
|
//#region src/client/api.ts
|
|
3373
3389
|
const defaultTimeoutMs = 3e4;
|
|
@@ -3424,7 +3440,7 @@ const createApi = ({ origin, token, fetch = globalThis.fetch, timeoutMs = defaul
|
|
|
3424
3440
|
const refused = (action, { error, response }, hints) => {
|
|
3425
3441
|
const failure = new ApiError(response.status, codeOf(error), action, detailsOf$1(error));
|
|
3426
3442
|
const hint = failure.code === null ? void 0 : hints[failure.code];
|
|
3427
|
-
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 });
|
|
3428
3444
|
};
|
|
3429
3445
|
const dataOf = (action, answer, hints = {}) => {
|
|
3430
3446
|
if (answer.data === void 0) throw refused(action, answer, hints);
|
|
@@ -3796,7 +3812,7 @@ const createLoginCommand = (adaptersOf) => defineCommand({
|
|
|
3796
3812
|
}
|
|
3797
3813
|
});
|
|
3798
3814
|
const authorizeInBrowser = async ({ adapters, api, origin, prompt }) => {
|
|
3799
|
-
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.` });
|
|
3800
3816
|
prompt.note([
|
|
3801
3817
|
`Code ${started.userCode}`,
|
|
3802
3818
|
`Address ${started.verificationUri}`,
|
|
@@ -4243,10 +4259,15 @@ const runOf = async (adaptersOf, { settings, context }) => {
|
|
|
4243
4259
|
//#region src/commands/agents/shared.ts
|
|
4244
4260
|
const agentOption = { project: { type: "string" } };
|
|
4245
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
|
+
]);
|
|
4246
4267
|
const agentHints = {
|
|
4247
4268
|
"agent.not_found": "kcloud agents list --project <name> shows the agents.",
|
|
4248
4269
|
"project.not_found": "kcloud projects list shows the projects you are a member of.",
|
|
4249
|
-
"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.",
|
|
4250
4271
|
"agent.busy": "The agent is working on another action; try again in a moment.",
|
|
4251
4272
|
"concurrency.conflict": "Someone changed the agent meanwhile; read it again and retry.",
|
|
4252
4273
|
"agent.node_unsuitable": "The node lacks a capability the agent requires; kcloud nodes show <node> lists what it offers.",
|
|
@@ -4474,12 +4495,39 @@ const enforcementText = ({ state, reason }) => {
|
|
|
4474
4495
|
case "unsupported": return `The node holds no pool to a limit: ${reason === null ? "it cannot" : unsupportedReasons[reason] ?? reason}.`;
|
|
4475
4496
|
}
|
|
4476
4497
|
};
|
|
4477
|
-
const memoryOf = (pool) => {
|
|
4498
|
+
const memoryOf$1 = (pool) => {
|
|
4478
4499
|
if (pool.usage === null) return "-";
|
|
4479
4500
|
const { memoryBytes, memoryMaxBytes } = pool.usage;
|
|
4480
4501
|
return memoryMaxBytes === null ? bytesOf(memoryBytes) : `${bytesOf(memoryBytes)} of ${bytesOf(memoryMaxBytes)}`;
|
|
4481
4502
|
};
|
|
4482
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
|
+
};
|
|
4483
4531
|
const queueOf = (pools) => {
|
|
4484
4532
|
const waiting = roles.flatMap((role) => pools[role].waiting.map((entry) => [
|
|
4485
4533
|
role,
|
|
@@ -4487,7 +4535,7 @@ const queueOf = (pools) => {
|
|
|
4487
4535
|
entry.projectName ?? "-",
|
|
4488
4536
|
entry.agentName ?? entry.agentId,
|
|
4489
4537
|
entry.since,
|
|
4490
|
-
entry
|
|
4538
|
+
workOf(role, entry)
|
|
4491
4539
|
]));
|
|
4492
4540
|
return waiting.length === 0 ? "No session waits for a slot." : renderTable([
|
|
4493
4541
|
{ header: "WAITS FOR" },
|
|
@@ -4498,37 +4546,41 @@ const queueOf = (pools) => {
|
|
|
4498
4546
|
{ header: "PROJECT" },
|
|
4499
4547
|
{ header: "AGENT" },
|
|
4500
4548
|
{ header: "SINCE" },
|
|
4501
|
-
{ header: "
|
|
4549
|
+
{ header: "WORK" }
|
|
4502
4550
|
], waiting);
|
|
4503
4551
|
};
|
|
4504
|
-
const poolsText = (pools) =>
|
|
4505
|
-
|
|
4506
|
-
|
|
4507
|
-
|
|
4508
|
-
|
|
4509
|
-
|
|
4510
|
-
header: "
|
|
4511
|
-
|
|
4512
|
-
|
|
4513
|
-
|
|
4514
|
-
|
|
4515
|
-
header: "MEMORY
|
|
4516
|
-
|
|
4517
|
-
|
|
4518
|
-
|
|
4519
|
-
|
|
4520
|
-
|
|
4521
|
-
|
|
4522
|
-
|
|
4523
|
-
|
|
4524
|
-
|
|
4525
|
-
|
|
4526
|
-
|
|
4527
|
-
|
|
4528
|
-
|
|
4529
|
-
|
|
4530
|
-
|
|
4531
|
-
]
|
|
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
|
+
};
|
|
4532
4584
|
//#endregion
|
|
4533
4585
|
//#region src/commands/agents/plan.ts
|
|
4534
4586
|
const agentPlanOf = (agent) => ({
|
|
@@ -4708,6 +4760,15 @@ const admissionRowsOf = (agent) => {
|
|
|
4708
4760
|
const { admission } = agent.runtime;
|
|
4709
4761
|
return admission === null ? [] : [["monitor slot", slotOf(admission.monitor)], ["executor slot", slotOf(admission.executor)]];
|
|
4710
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
|
+
};
|
|
4711
4772
|
const poolLimitRowsOf = ({ runtime }) => runtime.poolLimit === null ? [] : [["pool limit", poolLimitText(runtime.poolLimit)]];
|
|
4712
4773
|
const detailsOf = (agent) => [
|
|
4713
4774
|
renderTable([{ header: "FIELD" }, { header: "VALUE" }], [
|
|
@@ -4726,6 +4787,7 @@ const detailsOf = (agent) => [
|
|
|
4726
4787
|
["container", agent.runtime.containerId ?? "none"],
|
|
4727
4788
|
["health", agent.runtime.health],
|
|
4728
4789
|
...admissionRowsOf(agent),
|
|
4790
|
+
...admissionHoldRowsOf(agent),
|
|
4729
4791
|
...poolLimitRowsOf(agent),
|
|
4730
4792
|
["pending", agent.restartRequired ? agent.restartReasons.join(", ") : "-"],
|
|
4731
4793
|
["actions", agent.availableActions.join(", ")],
|
|
@@ -5061,30 +5123,29 @@ const lifecycleCommands = (adaptersOf) => agentActions.map((action) => defineCom
|
|
|
5061
5123
|
}));
|
|
5062
5124
|
//#endregion
|
|
5063
5125
|
//#region src/client/pages.ts
|
|
5064
|
-
const windowOf = (limit, all, fallback = 50) => {
|
|
5126
|
+
const windowOf = (limit, all, fallback = 50, largest = 200) => {
|
|
5065
5127
|
if (limit === void 0) return {
|
|
5066
5128
|
limit: fallback,
|
|
5067
5129
|
all
|
|
5068
5130
|
};
|
|
5069
5131
|
const size = Number(limit);
|
|
5070
|
-
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}.`);
|
|
5071
5133
|
return {
|
|
5072
5134
|
limit: size,
|
|
5073
5135
|
all
|
|
5074
5136
|
};
|
|
5075
5137
|
};
|
|
5076
|
-
const collect = async (page, { limit, all }) => {
|
|
5077
|
-
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));
|
|
5078
5140
|
if (!all) return first;
|
|
5079
5141
|
const items = [...first.items];
|
|
5080
|
-
let
|
|
5081
|
-
while (
|
|
5082
|
-
|
|
5083
|
-
items.push(...
|
|
5084
|
-
cursor = next.nextCursor;
|
|
5142
|
+
let last = first;
|
|
5143
|
+
while (last.nextCursor !== null) {
|
|
5144
|
+
last = await page(last.nextCursor, largest);
|
|
5145
|
+
items.push(...last.items);
|
|
5085
5146
|
}
|
|
5086
5147
|
return {
|
|
5087
|
-
...
|
|
5148
|
+
...last,
|
|
5088
5149
|
items,
|
|
5089
5150
|
nextCursor: null
|
|
5090
5151
|
};
|
|
@@ -5131,6 +5192,43 @@ const transcriptOf = async (api, agentId, sessionId, all) => collect(async (curs
|
|
|
5131
5192
|
limit: 200,
|
|
5132
5193
|
all
|
|
5133
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
|
+
};
|
|
5134
5232
|
const observabilityCommands = (adaptersOf) => [
|
|
5135
5233
|
defineCommand({
|
|
5136
5234
|
name: "sessions",
|
|
@@ -5206,7 +5304,9 @@ const observabilityCommands = (adaptersOf) => [
|
|
|
5206
5304
|
const run = await runOf(adaptersOf, invocation);
|
|
5207
5305
|
const agentId = await agentFrom(run, invocation.positionals[0], invocation.values.project);
|
|
5208
5306
|
const page = await transcriptOf(run.api, agentId, invocation.positionals[1] ?? "", invocation.values.all);
|
|
5209
|
-
|
|
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;
|
|
5210
5310
|
}
|
|
5211
5311
|
}),
|
|
5212
5312
|
defineCommand({
|
|
@@ -5342,44 +5442,35 @@ const observabilityCommands = (adaptersOf) => [
|
|
|
5342
5442
|
defineCommand({
|
|
5343
5443
|
name: "memory",
|
|
5344
5444
|
summary: "Search what an agent remembers of its tasks.",
|
|
5345
|
-
usage: "agents memory <agent> [--project <name>] [--query <text>] [--limit <n>]",
|
|
5445
|
+
usage: "agents memory <agent> [--project <name>] [--query <text>] [--outcome <outcome>] [--limit <n>] [--all]",
|
|
5346
5446
|
optionsUsage: [
|
|
5347
5447
|
...agentUsage,
|
|
5348
5448
|
["--query <text>", "only the summaries that match this text"],
|
|
5349
|
-
["--
|
|
5449
|
+
["--outcome <outcome>", `only the summaries of one outcome: ${memoryOutcomes.join(", ")}`],
|
|
5450
|
+
["--limit <n>", `how many summaries (default: 20)`],
|
|
5451
|
+
allUsage
|
|
5350
5452
|
],
|
|
5351
5453
|
options: {
|
|
5352
5454
|
...agentOption,
|
|
5455
|
+
...pageOptions,
|
|
5353
5456
|
query: { type: "string" },
|
|
5354
|
-
|
|
5457
|
+
outcome: { type: "string" }
|
|
5355
5458
|
},
|
|
5356
5459
|
positionals: {
|
|
5357
5460
|
min: 1,
|
|
5358
5461
|
max: 1
|
|
5359
5462
|
},
|
|
5360
5463
|
run: async (invocation) => {
|
|
5464
|
+
const { values } = invocation;
|
|
5361
5465
|
const run = await runOf(adaptersOf, invocation);
|
|
5362
|
-
const agentId = await agentFrom(run, invocation.positionals[0],
|
|
5363
|
-
const
|
|
5364
|
-
|
|
5365
|
-
const
|
|
5366
|
-
|
|
5367
|
-
|
|
5368
|
-
|
|
5369
|
-
|
|
5370
|
-
}
|
|
5371
|
-
} }), agentHints);
|
|
5372
|
-
return printed(run, listed, (shown) => shown.items.length === 0 ? "The agent remembers nothing that matches." : renderTable([
|
|
5373
|
-
{ header: "WHEN" },
|
|
5374
|
-
{ header: "OUTCOME" },
|
|
5375
|
-
{ header: "TITLE" },
|
|
5376
|
-
{ header: "HEADLINE" }
|
|
5377
|
-
], shown.items.map((summary) => [
|
|
5378
|
-
summary.createdAt,
|
|
5379
|
-
summary.ok ? "ok" : "failed",
|
|
5380
|
-
summary.title,
|
|
5381
|
-
summary.headline
|
|
5382
|
-
])));
|
|
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);
|
|
5383
5474
|
}
|
|
5384
5475
|
})
|
|
5385
5476
|
];
|
|
@@ -5953,25 +6044,59 @@ const secretUsage = [
|
|
|
5953
6044
|
//#region src/commands/credentials.ts
|
|
5954
6045
|
const projectOption$2 = { project: { type: "string" } };
|
|
5955
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.";
|
|
5956
6048
|
const credentialHints = {
|
|
5957
6049
|
"project.not_found": "kcloud projects list shows the projects you are a member of.",
|
|
5958
6050
|
"credential.not_found": "kcloud credentials list --project <name> shows them.",
|
|
5959
6051
|
"credential.label_taken": "Another credential of the project already has that label.",
|
|
5960
|
-
"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;
|
|
5961
6085
|
};
|
|
5962
6086
|
const projectFrom$2 = async (run, given) => {
|
|
5963
6087
|
if (given === void 0) throw new UsageError("This command needs a project.", "Pass --project <name>; kcloud projects list shows them.");
|
|
5964
6088
|
return resolveProjectId(run, given);
|
|
5965
6089
|
};
|
|
5966
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;
|
|
5967
|
-
const
|
|
6091
|
+
const resolveCredential = async (api, projectId, reference) => {
|
|
5968
6092
|
const credentials = await credentialsOf(api, projectId);
|
|
5969
6093
|
const found = credentials.filter((credential) => credential.id === reference || credential.label === reference);
|
|
5970
6094
|
const [first] = found;
|
|
5971
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(", ")}.`);
|
|
5972
6096
|
if (found.length > 1) throw new CliError(`${reference} names ${found.length} credentials of the project.`, "Name it by its id instead.");
|
|
5973
|
-
return first
|
|
6097
|
+
return first;
|
|
5974
6098
|
};
|
|
6099
|
+
const resolveCredentialId = async (api, projectId, reference) => (await resolveCredential(api, projectId, reference)).id;
|
|
5975
6100
|
const createCredentialsCommand = (adaptersOf) => defineGroup({
|
|
5976
6101
|
name: "credentials",
|
|
5977
6102
|
summary: "The Claude and GitHub credentials of a project.",
|
|
@@ -5997,13 +6122,15 @@ const createCredentialsCommand = (adaptersOf) => defineGroup({
|
|
|
5997
6122
|
{ header: "FINGERPRINT" },
|
|
5998
6123
|
{ header: "VERIFIED" },
|
|
5999
6124
|
{ header: "EXPIRES" },
|
|
6125
|
+
{ header: "USAGE" },
|
|
6000
6126
|
{ header: "ID" }
|
|
6001
6127
|
], credentials.map((credential) => [
|
|
6002
6128
|
credential.label,
|
|
6003
6129
|
credential.kind,
|
|
6004
6130
|
credential.fingerprint,
|
|
6005
6131
|
credential.verification,
|
|
6006
|
-
credential
|
|
6132
|
+
expiryOf(credential),
|
|
6133
|
+
usageSummaryOf(credential),
|
|
6007
6134
|
credential.id
|
|
6008
6135
|
])));
|
|
6009
6136
|
}
|
|
@@ -6149,6 +6276,34 @@ const createCredentialsCommand = (adaptersOf) => defineGroup({
|
|
|
6149
6276
|
return said(run, `The instance is verifying ${credentialId}; kcloud credentials list shows the answer.`);
|
|
6150
6277
|
}
|
|
6151
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
|
+
}),
|
|
6152
6307
|
defineCommand({
|
|
6153
6308
|
name: "delete",
|
|
6154
6309
|
summary: "Delete a credential no agent uses.",
|
|
@@ -7010,19 +7165,31 @@ const credentialTools = [
|
|
|
7010
7165
|
}),
|
|
7011
7166
|
defineTool({
|
|
7012
7167
|
name: "credential_update",
|
|
7013
|
-
title: "Rename or
|
|
7014
|
-
description: "rename gives a credential a new label; verify asks the instance to check it against its service, and credentials_list shows the answer.",
|
|
7015
|
-
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
|
+
],
|
|
7016
7175
|
safety: "write",
|
|
7017
7176
|
input: z.object({
|
|
7018
7177
|
project: projectField,
|
|
7019
7178
|
credential: credentialField$1,
|
|
7020
|
-
action: z.enum([
|
|
7179
|
+
action: z.enum([
|
|
7180
|
+
"rename",
|
|
7181
|
+
"verify",
|
|
7182
|
+
"refresh_usage"
|
|
7183
|
+
]),
|
|
7021
7184
|
label: z.string().trim().min(1).optional().describe("The new label, for rename.")
|
|
7022
7185
|
}),
|
|
7023
7186
|
run: async ({ project, credential, action, label }, run) => {
|
|
7024
7187
|
const projectId = await projectIdOf(run, project);
|
|
7025
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);
|
|
7026
7193
|
if (action === "verify") {
|
|
7027
7194
|
doneWith("verify the credential", await run.api.POST("/projects/{id}/credentials/{credentialId}/verify", { params: { path: {
|
|
7028
7195
|
id: projectId,
|
|
@@ -7363,21 +7530,23 @@ const observabilityTools = [
|
|
|
7363
7530
|
defineTool({
|
|
7364
7531
|
name: "agent_memory_search",
|
|
7365
7532
|
title: "Search the memory of an agent",
|
|
7366
|
-
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.",
|
|
7367
7534
|
operations: ["searchAgentMemory"],
|
|
7368
7535
|
safety: "read",
|
|
7369
7536
|
input: z.object({
|
|
7370
7537
|
...agentFields,
|
|
7371
7538
|
query: z.string().max(200).optional(),
|
|
7372
|
-
|
|
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
|
|
7373
7542
|
}),
|
|
7374
|
-
run: async ({ query, limit, ...reference }, run) =>
|
|
7375
|
-
|
|
7376
|
-
|
|
7377
|
-
|
|
7378
|
-
|
|
7379
|
-
|
|
7380
|
-
}
|
|
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
|
+
})
|
|
7381
7550
|
}),
|
|
7382
7551
|
defineTool({
|
|
7383
7552
|
name: "agent_earlier_output",
|
|
@@ -7442,6 +7611,70 @@ const observabilityTools = [
|
|
|
7442
7611
|
})
|
|
7443
7612
|
];
|
|
7444
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
|
|
7445
7678
|
//#region src/commands/roles.ts
|
|
7446
7679
|
const roleOption = { role: { type: "string" } };
|
|
7447
7680
|
const roleUsage = ["--role <role>", "admin or member"];
|
|
@@ -7751,6 +7984,48 @@ const costsText = (costs, { from, to }, slugs) => [
|
|
|
7751
7984
|
"",
|
|
7752
7985
|
`${costs.totalCostUsd.toFixed(2)} over ${costs.totalRuns} runs and ${costs.sessions} sessions.`
|
|
7753
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
|
+
};
|
|
7754
8029
|
const createOrgsCommand = (adaptersOf) => defineGroup({
|
|
7755
8030
|
name: "orgs",
|
|
7756
8031
|
summary: "The organizations you can see, and the one kcloud acts in.",
|
|
@@ -7835,11 +8110,52 @@ const createOrgsCommand = (adaptersOf) => defineGroup({
|
|
|
7835
8110
|
return printed(run, organization, (shown) => renderTable([{ header: "FIELD" }, { header: "VALUE" }], [
|
|
7836
8111
|
["name", shown.name],
|
|
7837
8112
|
["slug", shown.slug],
|
|
8113
|
+
["color", shown.color],
|
|
8114
|
+
["logo", shown.logoHash ?? "-"],
|
|
7838
8115
|
["created", shown.createdAt],
|
|
7839
8116
|
["id", shown.id]
|
|
7840
8117
|
]));
|
|
7841
8118
|
}
|
|
7842
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
|
+
}),
|
|
7843
8159
|
defineCommand({
|
|
7844
8160
|
name: "costs",
|
|
7845
8161
|
summary: "Show what the organization spent, by day and by project.",
|
|
@@ -7904,6 +8220,20 @@ const organizationTools = [
|
|
|
7904
8220
|
return dataOf("describe the organization", await run.api.GET("/organizations/{organizationId}", { params: { path: { organizationId: id } } }), organizationHints);
|
|
7905
8221
|
}
|
|
7906
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
|
+
}),
|
|
7907
8237
|
defineTool({
|
|
7908
8238
|
name: "organization_costs",
|
|
7909
8239
|
title: "Show what the organization spent",
|
|
@@ -8433,19 +8763,37 @@ const createdProject = async (scope, name, { slug, description, color }) => {
|
|
|
8433
8763
|
organization
|
|
8434
8764
|
};
|
|
8435
8765
|
};
|
|
8436
|
-
const
|
|
8437
|
-
|
|
8438
|
-
|
|
8439
|
-
|
|
8440
|
-
|
|
8441
|
-
|
|
8442
|
-
|
|
8443
|
-
|
|
8444
|
-
|
|
8445
|
-
|
|
8446
|
-
|
|
8447
|
-
|
|
8448
|
-
|
|
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;
|
|
8449
8797
|
};
|
|
8450
8798
|
const deleteProject = async (api, id) => {
|
|
8451
8799
|
doneWith("delete the project", await api.DELETE("/projects/{id}", { params: { path: { id } } }), projectHint);
|
|
@@ -8504,6 +8852,7 @@ const createProjectsCommand = (adaptersOf) => defineGroup({
|
|
|
8504
8852
|
["slug", shown.slug],
|
|
8505
8853
|
["description", shown.description === "" ? "-" : shown.description],
|
|
8506
8854
|
["color", shown.color],
|
|
8855
|
+
["logo", shown.logoHash ?? "-"],
|
|
8507
8856
|
["docker", shown.dockerAllowed ? "allowed" : "not allowed"],
|
|
8508
8857
|
["members", String(shown.memberCount)],
|
|
8509
8858
|
["created", shown.createdAt],
|
|
@@ -8529,12 +8878,17 @@ const createProjectsCommand = (adaptersOf) => defineGroup({
|
|
|
8529
8878
|
}),
|
|
8530
8879
|
defineCommand({
|
|
8531
8880
|
name: "update",
|
|
8532
|
-
summary: "Change the name, slug, description or
|
|
8533
|
-
usage: "projects update <project> [--name <name>] [--slug <slug>] [--description <text>] [--color <#rrggbb>]",
|
|
8534
|
-
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
|
+
],
|
|
8535
8888
|
options: {
|
|
8536
8889
|
name: { type: "string" },
|
|
8537
|
-
...fieldOptions
|
|
8890
|
+
...fieldOptions,
|
|
8891
|
+
...logoOptions
|
|
8538
8892
|
},
|
|
8539
8893
|
positionals: {
|
|
8540
8894
|
min: 1,
|
|
@@ -8547,10 +8901,11 @@ const createProjectsCommand = (adaptersOf) => defineGroup({
|
|
|
8547
8901
|
values.slug,
|
|
8548
8902
|
values.description,
|
|
8549
8903
|
values.color
|
|
8550
|
-
].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);
|
|
8551
8906
|
const run = await runOf(adaptersOf, invocation);
|
|
8552
8907
|
const id = await resolveProjectId(run, invocation.positionals[0] ?? "");
|
|
8553
|
-
const project = await changedProject(run.api, id, values);
|
|
8908
|
+
const project = await changedProject(run.api, id, values, logo);
|
|
8554
8909
|
return printed(run, project, (shown) => `Changed ${shown.name} (${shown.slug}).`);
|
|
8555
8910
|
}
|
|
8556
8911
|
}),
|
|
@@ -9338,11 +9693,13 @@ const runtimeRowsOf = (runtime) => [
|
|
|
9338
9693
|
["agents", `${runtime.reconcile.agentsActive} active, ${runtime.reconcile.agentsQueued} queued`],
|
|
9339
9694
|
["log readers", String(runtime.ingest.readers)]
|
|
9340
9695
|
];
|
|
9696
|
+
const sessionsText = (count, noun) => `${String(count)} ${noun}${count === 1 ? "" : "s"}`;
|
|
9697
|
+
const heldPoolsOf = ({ held = [] }) => held;
|
|
9341
9698
|
const updateOf = ({ update }) => {
|
|
9342
9699
|
if (update === null) return "not reported";
|
|
9343
9700
|
switch (update.state) {
|
|
9344
9701
|
case "current": return "current";
|
|
9345
|
-
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`;
|
|
9346
9703
|
case "updating": return `updating to ${update.target ?? "-"}`;
|
|
9347
9704
|
case "failed": return `failed to reach ${update.target ?? "-"}, runs its previous version`;
|
|
9348
9705
|
case "unmanaged": return "updated with its platform";
|