@kikimoradev/kcloud 1.11.0 → 1.11.2
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 +371 -41
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -929,6 +929,7 @@ const errorCodes = Object.keys({
|
|
|
929
929
|
"release.hidden": "rule_violation",
|
|
930
930
|
"release.unavailable": "rule_violation",
|
|
931
931
|
"release.unsupported": "rule_violation",
|
|
932
|
+
"release.none_stable": "rule_violation",
|
|
932
933
|
"agent.not_found": "not_found",
|
|
933
934
|
"agent.invalid_slug": "rule_violation",
|
|
934
935
|
"agent.slug_taken": "conflict",
|
|
@@ -1165,7 +1166,11 @@ const agentEventSchema = z.discriminatedUnion("event", [
|
|
|
1165
1166
|
z.looseObject({
|
|
1166
1167
|
...roleEnvelope("task.started"),
|
|
1167
1168
|
taskId: z.string(),
|
|
1168
|
-
title: z.string()
|
|
1169
|
+
title: z.string(),
|
|
1170
|
+
sourceLabel: z.string().optional(),
|
|
1171
|
+
sourceUrl: z.string().optional(),
|
|
1172
|
+
attempt: countSchema.optional(),
|
|
1173
|
+
maxAttempts: countSchema.optional()
|
|
1169
1174
|
}),
|
|
1170
1175
|
z.looseObject({
|
|
1171
1176
|
...roleEnvelope("task.finished"),
|
|
@@ -1179,7 +1184,11 @@ const agentEventSchema = z.discriminatedUnion("event", [
|
|
|
1179
1184
|
attempt: countSchema.optional(),
|
|
1180
1185
|
maxAttempts: countSchema.optional(),
|
|
1181
1186
|
sessionId: z.string().optional(),
|
|
1182
|
-
error: z.string().optional()
|
|
1187
|
+
error: z.string().optional(),
|
|
1188
|
+
sourceLabel: z.string().optional(),
|
|
1189
|
+
sourceUrl: z.string().optional(),
|
|
1190
|
+
handedOff: z.boolean().optional(),
|
|
1191
|
+
handoffReason: z.string().optional()
|
|
1183
1192
|
}),
|
|
1184
1193
|
z.looseObject({
|
|
1185
1194
|
...roleEnvelope("task.retryScheduled"),
|
|
@@ -1471,6 +1480,108 @@ z.object({
|
|
|
1471
1480
|
source: agentTaskSourceSchema,
|
|
1472
1481
|
staleReason: agentTaskStaleReasonSchema
|
|
1473
1482
|
}).meta({ id: "AgentTaskView" });
|
|
1483
|
+
const taskSourceSchema = z.object({
|
|
1484
|
+
label: z.string(),
|
|
1485
|
+
url: z.string().nullable()
|
|
1486
|
+
}).meta({ id: "TaskSource" });
|
|
1487
|
+
const overviewTaskOutcomeSchema = z.enum([
|
|
1488
|
+
"running",
|
|
1489
|
+
"done",
|
|
1490
|
+
"failed",
|
|
1491
|
+
"retrying",
|
|
1492
|
+
"handedOff"
|
|
1493
|
+
]);
|
|
1494
|
+
const overviewTaskSchema = z.object({
|
|
1495
|
+
taskId: z.string(),
|
|
1496
|
+
title: z.string(),
|
|
1497
|
+
source: taskSourceSchema.nullable(),
|
|
1498
|
+
attempts: z.int().nonnegative(),
|
|
1499
|
+
maxAttempts: z.int().nullable(),
|
|
1500
|
+
startedAt: z.iso.datetime(),
|
|
1501
|
+
finishedAt: z.iso.datetime().nullable(),
|
|
1502
|
+
outcome: overviewTaskOutcomeSchema,
|
|
1503
|
+
error: z.string().nullable(),
|
|
1504
|
+
costUsd: z.number(),
|
|
1505
|
+
durationMs: z.int().nonnegative(),
|
|
1506
|
+
headline: z.string().nullable(),
|
|
1507
|
+
handoffReason: z.string().nullable()
|
|
1508
|
+
}).meta({ id: "OverviewTask" });
|
|
1509
|
+
const overviewGroupSchema = z.object({
|
|
1510
|
+
source: taskSourceSchema.nullable(),
|
|
1511
|
+
title: z.string(),
|
|
1512
|
+
tasks: z.array(overviewTaskSchema),
|
|
1513
|
+
totals: z.object({
|
|
1514
|
+
durationMs: z.int().nonnegative(),
|
|
1515
|
+
costUsd: z.number()
|
|
1516
|
+
})
|
|
1517
|
+
}).meta({ id: "OverviewGroup" });
|
|
1518
|
+
const attentionBase = {
|
|
1519
|
+
taskId: z.string(),
|
|
1520
|
+
title: z.string(),
|
|
1521
|
+
source: taskSourceSchema.nullable(),
|
|
1522
|
+
finishedAt: z.iso.datetime()
|
|
1523
|
+
};
|
|
1524
|
+
const attentionItemSchema = z.discriminatedUnion("kind", [
|
|
1525
|
+
z.object({
|
|
1526
|
+
kind: z.literal("failed"),
|
|
1527
|
+
...attentionBase,
|
|
1528
|
+
attempts: z.int().nonnegative(),
|
|
1529
|
+
maxAttempts: z.int().nullable(),
|
|
1530
|
+
error: z.string().nullable()
|
|
1531
|
+
}),
|
|
1532
|
+
z.object({
|
|
1533
|
+
kind: z.literal("retried"),
|
|
1534
|
+
...attentionBase,
|
|
1535
|
+
attempts: z.int().nonnegative()
|
|
1536
|
+
}),
|
|
1537
|
+
z.object({
|
|
1538
|
+
kind: z.literal("handedOff"),
|
|
1539
|
+
...attentionBase,
|
|
1540
|
+
reason: z.string().nullable()
|
|
1541
|
+
})
|
|
1542
|
+
]).meta({ id: "AttentionItem" });
|
|
1543
|
+
const workloadSchema = z.object({
|
|
1544
|
+
tasksDone: z.int().nonnegative(),
|
|
1545
|
+
tasksFailed: z.int().nonnegative(),
|
|
1546
|
+
tasksHandedOff: z.int().nonnegative(),
|
|
1547
|
+
tasksCostUsd: z.number(),
|
|
1548
|
+
averageTaskMs: z.int().nonnegative().nullable(),
|
|
1549
|
+
cycles: z.int().nonnegative(),
|
|
1550
|
+
cyclesCostUsd: z.number()
|
|
1551
|
+
}).meta({ id: "Workload" });
|
|
1552
|
+
z.object({
|
|
1553
|
+
window: z.object({
|
|
1554
|
+
from: z.iso.datetime(),
|
|
1555
|
+
to: z.iso.datetime()
|
|
1556
|
+
}),
|
|
1557
|
+
source: agentTaskSourceSchema,
|
|
1558
|
+
staleReason: agentTaskStaleReasonSchema,
|
|
1559
|
+
lastEventAt: z.iso.datetime().nullable(),
|
|
1560
|
+
current: overviewTaskSchema.nullable(),
|
|
1561
|
+
last: overviewTaskSchema.nullable(),
|
|
1562
|
+
lastCycle: z.object({
|
|
1563
|
+
finishedAt: z.iso.datetime(),
|
|
1564
|
+
ok: z.boolean().nullable(),
|
|
1565
|
+
addedTasks: z.int().nonnegative().nullable()
|
|
1566
|
+
}).nullable(),
|
|
1567
|
+
queue: z.object({
|
|
1568
|
+
pending: z.int().nonnegative(),
|
|
1569
|
+
inProgress: z.int().nonnegative(),
|
|
1570
|
+
failed: z.int().nonnegative()
|
|
1571
|
+
}).nullable(),
|
|
1572
|
+
lastSession: z.object({
|
|
1573
|
+
role: agentRoleSchema.nullable(),
|
|
1574
|
+
startedAt: z.iso.datetime()
|
|
1575
|
+
}).nullable(),
|
|
1576
|
+
costTodayUsd: z.number(),
|
|
1577
|
+
attention: z.array(attentionItemSchema),
|
|
1578
|
+
groups: z.array(overviewGroupSchema),
|
|
1579
|
+
moreTasks: z.int().nonnegative(),
|
|
1580
|
+
health: z.object({
|
|
1581
|
+
day: workloadSchema,
|
|
1582
|
+
week: workloadSchema
|
|
1583
|
+
})
|
|
1584
|
+
}).meta({ id: "AgentOverview" });
|
|
1474
1585
|
z.object({
|
|
1475
1586
|
title: z.string().trim().min(1).max(200).optional(),
|
|
1476
1587
|
description: z.string().trim().min(1).max(maxTaskDescriptionLength)
|
|
@@ -2378,6 +2489,7 @@ const auditActionSchema = z.enum([
|
|
|
2378
2489
|
"tool.deleted",
|
|
2379
2490
|
"tool.copied",
|
|
2380
2491
|
"release.visibility_changed",
|
|
2492
|
+
"release.stability_changed",
|
|
2381
2493
|
"agent.created",
|
|
2382
2494
|
"agent.updated",
|
|
2383
2495
|
"agent.deleted",
|
|
@@ -2515,6 +2627,8 @@ z.object({
|
|
|
2515
2627
|
//#endregion
|
|
2516
2628
|
//#region ../../packages/contracts/src/releases.ts
|
|
2517
2629
|
const kikimoraVersionSchema = z.string().regex(versionTagPattern);
|
|
2630
|
+
const latestKikimoraVersion = "latest";
|
|
2631
|
+
const kikimoraVersionChoiceSchema = z.union([kikimoraVersionSchema, z.literal(latestKikimoraVersion)]);
|
|
2518
2632
|
const releaseSummarySchema = z.object({
|
|
2519
2633
|
version: kikimoraVersionSchema,
|
|
2520
2634
|
claudeCodeVersion: z.string().nullable(),
|
|
@@ -2522,14 +2636,20 @@ const releaseSummarySchema = z.object({
|
|
|
2522
2636
|
imageCreatedAt: z.iso.datetime().nullable(),
|
|
2523
2637
|
gitRevision: z.string().nullable(),
|
|
2524
2638
|
releaseNotesUrl: z.url().nullable(),
|
|
2525
|
-
supported: z.boolean()
|
|
2639
|
+
supported: z.boolean(),
|
|
2640
|
+
stable: z.boolean()
|
|
2526
2641
|
}).meta({ id: "ReleaseSummary" });
|
|
2527
|
-
z.object({
|
|
2642
|
+
z.object({
|
|
2643
|
+
items: z.array(releaseSummarySchema),
|
|
2644
|
+
latest: kikimoraVersionSchema.nullable()
|
|
2645
|
+
}).meta({ id: "ReleaseList" });
|
|
2528
2646
|
const releaseAdminSchema = releaseSummarySchema.extend({
|
|
2529
2647
|
available: z.boolean(),
|
|
2530
2648
|
hidden: z.boolean(),
|
|
2531
2649
|
hiddenAt: z.iso.datetime().nullable(),
|
|
2532
2650
|
hiddenBy: idSchema.nullable(),
|
|
2651
|
+
stableAt: z.iso.datetime().nullable(),
|
|
2652
|
+
stableBy: idSchema.nullable(),
|
|
2533
2653
|
syncedAt: z.iso.datetime(),
|
|
2534
2654
|
lastSyncError: z.string().nullable()
|
|
2535
2655
|
}).meta({ id: "ReleaseAdmin" });
|
|
@@ -2539,6 +2659,7 @@ z.object({
|
|
|
2539
2659
|
lastSyncError: z.string().nullable()
|
|
2540
2660
|
}).meta({ id: "ReleaseAdminList" });
|
|
2541
2661
|
z.object({ hidden: z.boolean() }).meta({ id: "SetReleaseVisibilityBody" });
|
|
2662
|
+
z.object({ stable: z.boolean() }).meta({ id: "SetReleaseStabilityBody" });
|
|
2542
2663
|
z.object({ version: kikimoraVersionSchema });
|
|
2543
2664
|
const repositoryStatusSchema = z.enum([
|
|
2544
2665
|
"ok",
|
|
@@ -2849,7 +2970,8 @@ const restartReasonSchema = z.enum([
|
|
|
2849
2970
|
"tool_changed",
|
|
2850
2971
|
"spec_drift",
|
|
2851
2972
|
"spec_version",
|
|
2852
|
-
"release_unsupported"
|
|
2973
|
+
"release_unsupported",
|
|
2974
|
+
"latest_release"
|
|
2853
2975
|
]);
|
|
2854
2976
|
const containerHealthSchema = z.enum([
|
|
2855
2977
|
"none",
|
|
@@ -2941,6 +3063,8 @@ const agentListItemSchema = z.object({
|
|
|
2941
3063
|
name: z.string(),
|
|
2942
3064
|
slug: slugSchema,
|
|
2943
3065
|
kikimoraVersion: z.string(),
|
|
3066
|
+
followsLatest: z.boolean(),
|
|
3067
|
+
pendingKikimoraVersion: z.string().nullable(),
|
|
2944
3068
|
state: agentStateSchema,
|
|
2945
3069
|
observedState: agentObservedStateSchema,
|
|
2946
3070
|
desiredState: agentDesiredStateSchema,
|
|
@@ -2950,6 +3074,7 @@ const agentListItemSchema = z.object({
|
|
|
2950
3074
|
retryAt: z.iso.datetime().nullable(),
|
|
2951
3075
|
progress: agentProgressSchema.nullable(),
|
|
2952
3076
|
availableActions: z.array(agentActionSchema),
|
|
3077
|
+
stateSince: z.iso.datetime().nullable(),
|
|
2953
3078
|
updatedAt: z.iso.datetime()
|
|
2954
3079
|
}).meta({ id: "AgentListItem" });
|
|
2955
3080
|
const agentNodeSchema = z.object({
|
|
@@ -3006,7 +3131,7 @@ const createAgentBodySchema = z.object({
|
|
|
3006
3131
|
name: agentNameSchema,
|
|
3007
3132
|
slug: slugSchema.optional(),
|
|
3008
3133
|
description: agentDescriptionSchema,
|
|
3009
|
-
kikimoraVersion:
|
|
3134
|
+
kikimoraVersion: kikimoraVersionChoiceSchema,
|
|
3010
3135
|
nodeId: idSchema,
|
|
3011
3136
|
requiredCapabilities: requiredCapabilitiesSchema.optional(),
|
|
3012
3137
|
claudeCredentialId: idSchema.optional(),
|
|
@@ -3025,7 +3150,7 @@ const updateAgentSectionsSchema = z.object({
|
|
|
3025
3150
|
slug: slugSchema.optional(),
|
|
3026
3151
|
description: agentDescriptionSchema
|
|
3027
3152
|
}).optional(),
|
|
3028
|
-
version:
|
|
3153
|
+
version: kikimoraVersionChoiceSchema.optional(),
|
|
3029
3154
|
credentials: z.object({
|
|
3030
3155
|
claudeCredentialId: idSchema.optional(),
|
|
3031
3156
|
githubCredentialId: idSchema.optional(),
|
|
@@ -4662,6 +4787,7 @@ const agentHints = {
|
|
|
4662
4787
|
"agent.node_unsuitable": "The node lacks a capability the agent requires; kcloud nodes show <node> lists what it offers.",
|
|
4663
4788
|
"node.offline": "The node of the agent is not connected; kcloud nodes show <node> tells when it was last seen.",
|
|
4664
4789
|
"account.queue_empty": "The queue of Claude accounts of the project is empty; kcloud projects accounts add <account> --project <name> puts one in it.",
|
|
4790
|
+
"release.none_stable": "No release is stable yet, so latest points at no release; kcloud releases list shows the releases to pick one of.",
|
|
4665
4791
|
"node.outdated": "The node of the agent runs an older release; kcloud nodes show <node> names the release it runs and its update."
|
|
4666
4792
|
};
|
|
4667
4793
|
const agentFrom = async (run, reference, project) => {
|
|
@@ -5011,7 +5137,7 @@ const agentPlanOf = (agent) => ({
|
|
|
5011
5137
|
name: agent.name,
|
|
5012
5138
|
slug: agent.slug,
|
|
5013
5139
|
description: agent.description,
|
|
5014
|
-
kikimoraVersion: agent.kikimoraVersion,
|
|
5140
|
+
kikimoraVersion: agent.followsLatest ? latestKikimoraVersion : agent.kikimoraVersion,
|
|
5015
5141
|
...agent.node === null ? {} : { nodeId: agent.node.id },
|
|
5016
5142
|
requiredCapabilities: agent.requiredCapabilities,
|
|
5017
5143
|
gitCredentialIds: agent.gitCredentials.map(({ id }) => id),
|
|
@@ -5084,6 +5210,9 @@ const acceptedAgentChange = (run, document) => {
|
|
|
5084
5210
|
};
|
|
5085
5211
|
//#endregion
|
|
5086
5212
|
//#region src/commands/agents/config.ts
|
|
5213
|
+
const releaseOf = ({ kikimoraVersion, followsLatest }) => followsLatest ? `latest (${kikimoraVersion})` : kikimoraVersion;
|
|
5214
|
+
const nodeAppliedReasons = /* @__PURE__ */ new Set(["latest_release"]);
|
|
5215
|
+
const awaitsRestart = ({ restartRequired, restartReasons }) => restartRequired && (restartReasons.length === 0 || restartReasons.some((reason) => !nodeAppliedReasons.has(reason)));
|
|
5087
5216
|
const agentsOf = async (scope, project) => {
|
|
5088
5217
|
const projects = project === void 0 ? (await projectsOf(scope)).map(({ id, slug }) => ({
|
|
5089
5218
|
id,
|
|
@@ -5144,8 +5273,8 @@ const configCommands = (adaptersOf) => [
|
|
|
5144
5273
|
agent.project,
|
|
5145
5274
|
agent.slug,
|
|
5146
5275
|
agent.state,
|
|
5147
|
-
agent
|
|
5148
|
-
agent
|
|
5276
|
+
releaseOf(agent),
|
|
5277
|
+
awaitsRestart(agent) ? "restart" : "-",
|
|
5149
5278
|
agent.id
|
|
5150
5279
|
])));
|
|
5151
5280
|
}
|
|
@@ -5198,7 +5327,7 @@ const configCommands = (adaptersOf) => [
|
|
|
5198
5327
|
const file = invocation.values.file;
|
|
5199
5328
|
if (file === void 0) throw new UsageError("agents set needs --file <path>.", "The file holds the sections to change; kcloud agents show <agent> --json shows the current ones.");
|
|
5200
5329
|
const agent = await changedAgent(run.api, agentId, acceptedAgentChange(run, await documentOf(run, file)));
|
|
5201
|
-
return printed(run, agent, (changed) => changed
|
|
5330
|
+
return printed(run, agent, (changed) => awaitsRestart(changed) ? `Changed ${changed.name}; it needs ${changeTakerOf(changed)} to take the change.` : `Changed ${changed.name}.`);
|
|
5202
5331
|
}
|
|
5203
5332
|
}),
|
|
5204
5333
|
defineCommand({
|
|
@@ -5302,6 +5431,7 @@ const gitCredentialLinesOf = ({ gitCredentials }) => gitCredentials.length === 0
|
|
|
5302
5431
|
serverUrl,
|
|
5303
5432
|
label
|
|
5304
5433
|
]))];
|
|
5434
|
+
const nextReleaseRowsOf = ({ pendingKikimoraVersion }) => pendingKikimoraVersion == null ? [] : [["next release", `${pendingKikimoraVersion} after the current session`]];
|
|
5305
5435
|
const detailsOf = (agent) => [
|
|
5306
5436
|
renderTable([{ header: "FIELD" }, { header: "VALUE" }], [
|
|
5307
5437
|
["name", agent.name],
|
|
@@ -5309,7 +5439,8 @@ const detailsOf = (agent) => [
|
|
|
5309
5439
|
["state", agent.state],
|
|
5310
5440
|
["wanted", agent.desiredState],
|
|
5311
5441
|
["observed", agent.observedState],
|
|
5312
|
-
["release", agent
|
|
5442
|
+
["release", releaseOf(agent)],
|
|
5443
|
+
...nextReleaseRowsOf(agent),
|
|
5313
5444
|
["claude code", claudeCodeOf(agent)],
|
|
5314
5445
|
["claude account", claudeAccountOf(agent)],
|
|
5315
5446
|
["node", agent.node === null ? "none" : `${agent.node.name} (${agent.node.state})`],
|
|
@@ -5324,7 +5455,7 @@ const detailsOf = (agent) => [
|
|
|
5324
5455
|
...admissionRowsOf(agent),
|
|
5325
5456
|
...admissionHoldRowsOf(agent),
|
|
5326
5457
|
...poolLimitRowsOf(agent),
|
|
5327
|
-
["pending", agent
|
|
5458
|
+
["pending", awaitsRestart(agent) ? agent.restartReasons.join(", ") : "-"],
|
|
5328
5459
|
["actions", agent.availableActions.join(", ")],
|
|
5329
5460
|
["version", String(agent.version)],
|
|
5330
5461
|
["id", agent.id]
|
|
@@ -5373,11 +5504,16 @@ const placedNodeId = async (run, { projectId, given, ask }) => {
|
|
|
5373
5504
|
//#region src/commands/agents/create.ts
|
|
5374
5505
|
const catalogueOf = async (api, projectId) => {
|
|
5375
5506
|
const path = { params: { path: { id: projectId } } };
|
|
5507
|
+
const credentials = dataOf("list the credentials of the project", await api.GET("/projects/{id}/credentials", path), agentHints).items;
|
|
5508
|
+
const repositories = dataOf("list the repositories of the project", await api.GET("/projects/{id}/repositories", path), agentHints).items;
|
|
5509
|
+
const tools = dataOf("list the tools of the project", await api.GET("/projects/{id}/tools", path), agentHints).items;
|
|
5510
|
+
const { items, latest } = dataOf("list the releases", await api.GET("/releases"));
|
|
5376
5511
|
return {
|
|
5377
|
-
credentials
|
|
5378
|
-
repositories
|
|
5379
|
-
tools
|
|
5380
|
-
releases:
|
|
5512
|
+
credentials,
|
|
5513
|
+
repositories,
|
|
5514
|
+
tools,
|
|
5515
|
+
releases: items.map(({ version }) => version),
|
|
5516
|
+
latest
|
|
5381
5517
|
};
|
|
5382
5518
|
};
|
|
5383
5519
|
const serversOf = (catalogue, chosen) => {
|
|
@@ -5497,6 +5633,21 @@ const requiredCapabilitiesOf = (given) => {
|
|
|
5497
5633
|
};
|
|
5498
5634
|
const requirementOf = (required) => required === void 0 ? {} : { requiredCapabilities: [...required] };
|
|
5499
5635
|
const localTimezone = () => Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
|
|
5636
|
+
const releaseChoicesOf = ({ releases, latest }) => [...latest === null ? [] : [{
|
|
5637
|
+
value: latestKikimoraVersion,
|
|
5638
|
+
label: `latest (now ${latest})`
|
|
5639
|
+
}], ...releases.map((version) => ({
|
|
5640
|
+
value: version,
|
|
5641
|
+
label: version
|
|
5642
|
+
}))];
|
|
5643
|
+
const checkedRelease = ({ releases, latest }, release) => {
|
|
5644
|
+
if (release === "latest") {
|
|
5645
|
+
if (latest !== null) return release;
|
|
5646
|
+
throw new CliError("No release is stable yet, so latest points at no release.", "kcloud releases list shows the releases to pick one of.");
|
|
5647
|
+
}
|
|
5648
|
+
if (releases.includes(release)) return release;
|
|
5649
|
+
throw new CliError(`${release} is not a release an agent can run.`, releases.length === 0 ? "The catalogue holds no release yet; an administrator syncs it under Releases in the panel." : `kcloud releases list shows ${releases.join(", ")}.`);
|
|
5650
|
+
};
|
|
5500
5651
|
const askedPlan = async (run, projectId, values, required) => {
|
|
5501
5652
|
const nodeId = await placedNodeId(run, {
|
|
5502
5653
|
projectId,
|
|
@@ -5508,14 +5659,10 @@ const askedPlan = async (run, projectId, values, required) => {
|
|
|
5508
5659
|
message: "The name of the agent",
|
|
5509
5660
|
validate: (value) => value.trim() === "" ? "cannot be empty" : void 0
|
|
5510
5661
|
});
|
|
5511
|
-
const release = values.release ?? await run.prompt.select({
|
|
5662
|
+
const release = checkedRelease(catalogue, values.release ?? await run.prompt.select({
|
|
5512
5663
|
message: "The Kikimora release it runs",
|
|
5513
|
-
choices: catalogue
|
|
5514
|
-
|
|
5515
|
-
label: version
|
|
5516
|
-
}))
|
|
5517
|
-
});
|
|
5518
|
-
if (!catalogue.releases.includes(release)) throw new CliError(`${release} is not a release an agent can run.`, catalogue.releases.length === 0 ? "The catalogue holds no release yet; an administrator syncs it under Releases in the panel." : `kcloud releases list shows ${catalogue.releases.join(", ")}.`);
|
|
5664
|
+
choices: releaseChoicesOf(catalogue)
|
|
5665
|
+
}));
|
|
5519
5666
|
const repositories = await chosenRepositories(run.prompt, catalogue);
|
|
5520
5667
|
const gitCredentialIds = await chosenGitCredentials(run.prompt, catalogue, repositories);
|
|
5521
5668
|
return {
|
|
@@ -5567,7 +5714,7 @@ const createCommand = (adaptersOf) => defineCommand({
|
|
|
5567
5714
|
["--require <capability,...>", `what its node has to offer: ${nodeCapabilityNames.join(", ")}`],
|
|
5568
5715
|
["--file <path>", "the whole agent as JSON, or - to read stdin"],
|
|
5569
5716
|
["--name <name>", "the name of the agent, instead of asking"],
|
|
5570
|
-
["--release <x.y.z>", "the Kikimora release it runs, instead of asking"],
|
|
5717
|
+
["--release <x.y.z|latest>", "the Kikimora release it runs, or latest to follow the newest stable one, instead of asking"],
|
|
5571
5718
|
["--monitor-prompt-file <path>", "the prompt of the monitor, instead of asking"],
|
|
5572
5719
|
["--executor-prompt-file <path>", "the prompt of the executor, instead of asking"],
|
|
5573
5720
|
["--timezone <zone>", "the timezone of the agent (default: this machine's)"],
|
|
@@ -6311,6 +6458,165 @@ const observabilityCommands = (adaptersOf) => [
|
|
|
6311
6458
|
})
|
|
6312
6459
|
];
|
|
6313
6460
|
//#endregion
|
|
6461
|
+
//#region src/commands/agents/overview.ts
|
|
6462
|
+
const none = "-";
|
|
6463
|
+
const agentOverviewOf = async (api, agentId) => dataOf("show the overview of the agent", await api.GET("/agents/{id}/overview", { params: { path: { id: agentId } } }), agentHints);
|
|
6464
|
+
const staleReasons = {
|
|
6465
|
+
stopped: "the agent is not running",
|
|
6466
|
+
unreachable: "the agent did not answer on its control socket",
|
|
6467
|
+
timeout: "the agent did not answer in time"
|
|
6468
|
+
};
|
|
6469
|
+
const nameOf = (task) => task.source === null ? task.taskId : `${task.source.label} (${task.taskId})`;
|
|
6470
|
+
const money = (amount) => `$${amount.toFixed(2)}`;
|
|
6471
|
+
const minutesOf = (ms) => `${Math.round(ms / 6e4)} min`;
|
|
6472
|
+
const counted = (count, one, many) => `${count} ${count === 1 ? one : many}`;
|
|
6473
|
+
const attemptsOf = (task) => task.maxAttempts === null ? `attempt ${task.attempts}` : `attempt ${task.attempts} of ${task.maxAttempts}`;
|
|
6474
|
+
const nowText = (overview) => {
|
|
6475
|
+
const lines = [];
|
|
6476
|
+
const { current, last, queue, lastCycle } = overview;
|
|
6477
|
+
if (current !== null && overview.source === "projection") {
|
|
6478
|
+
lines.push(`Last known: ${nameOf(current)} ${current.title}, started ${current.startedAt}, ${attemptsOf(current)}`);
|
|
6479
|
+
if (overview.staleReason !== null) lines.push(`Not current: ${staleReasons[overview.staleReason]}.`);
|
|
6480
|
+
} else if (current !== null) {
|
|
6481
|
+
lines.push(`Working on ${nameOf(current)} ${current.title}, since ${current.startedAt}, ${attemptsOf(current)}`);
|
|
6482
|
+
if (current.source?.url != null) lines.push(`Source: ${current.source.url}`);
|
|
6483
|
+
} else if (last !== null) {
|
|
6484
|
+
lines.push(`Last: ${nameOf(last)} ${last.title}, ${last.outcome}, finished ${last.finishedAt ?? none}`);
|
|
6485
|
+
if (last.headline !== null) lines.push(`Result: ${last.headline}`);
|
|
6486
|
+
} else lines.push("No task in the last 7 days.");
|
|
6487
|
+
if (queue !== null) lines.push(`Queue: ${queue.pending} pending, ${queue.failed} failed`);
|
|
6488
|
+
if (lastCycle !== null) {
|
|
6489
|
+
const added = lastCycle.addedTasks === null ? "" : lastCycle.addedTasks === 0 ? ", found nothing new" : `, added ${counted(lastCycle.addedTasks, "task", "tasks")}`;
|
|
6490
|
+
lines.push(`Last review of new work: ${lastCycle.finishedAt}${added}`);
|
|
6491
|
+
}
|
|
6492
|
+
return lines.join("\n");
|
|
6493
|
+
};
|
|
6494
|
+
const attentionLine = (item) => {
|
|
6495
|
+
const name = `${nameOf(item)} ${item.title}`;
|
|
6496
|
+
switch (item.kind) {
|
|
6497
|
+
case "failed": return `failed after ${counted(item.attempts, "attempt", "attempts")}: ${name}${item.error === null ? "" : `: ${item.error}`}`;
|
|
6498
|
+
case "retried": return `succeeded on attempt ${item.attempts}: ${name}`;
|
|
6499
|
+
case "handedOff": return `handed off to a person: ${name}${item.reason === null ? "" : `: ${item.reason}`}`;
|
|
6500
|
+
}
|
|
6501
|
+
};
|
|
6502
|
+
const attentionText = (overview) => overview.attention.length === 0 ? "Nothing needs attention." : overview.attention.map(attentionLine).join("\n");
|
|
6503
|
+
const groupRows = (group) => {
|
|
6504
|
+
const [newest] = group.tasks;
|
|
6505
|
+
const head = [
|
|
6506
|
+
group.source?.label ?? newest?.taskId ?? none,
|
|
6507
|
+
group.title,
|
|
6508
|
+
String(group.tasks.length),
|
|
6509
|
+
newest?.outcome ?? none,
|
|
6510
|
+
minutesOf(group.totals.durationMs),
|
|
6511
|
+
money(group.totals.costUsd),
|
|
6512
|
+
newest?.finishedAt ?? none
|
|
6513
|
+
];
|
|
6514
|
+
if (group.tasks.length < 2) return [head];
|
|
6515
|
+
return [head, ...group.tasks.map((task) => [
|
|
6516
|
+
` ${task.taskId}`,
|
|
6517
|
+
task.title,
|
|
6518
|
+
"",
|
|
6519
|
+
task.attempts > 1 ? `${task.outcome}, ${counted(task.attempts, "attempt", "attempts")}` : task.outcome,
|
|
6520
|
+
minutesOf(task.durationMs),
|
|
6521
|
+
money(task.costUsd),
|
|
6522
|
+
task.finishedAt ?? none
|
|
6523
|
+
])];
|
|
6524
|
+
};
|
|
6525
|
+
const recentText = (overview, agent) => {
|
|
6526
|
+
if (overview.groups.length === 0) return "No work in the last 7 days.";
|
|
6527
|
+
const lines = [renderTable([
|
|
6528
|
+
{ header: "SOURCE" },
|
|
6529
|
+
{ header: "TITLE" },
|
|
6530
|
+
{
|
|
6531
|
+
header: "TASKS",
|
|
6532
|
+
align: "right"
|
|
6533
|
+
},
|
|
6534
|
+
{ header: "OUTCOME" },
|
|
6535
|
+
{
|
|
6536
|
+
header: "TIME",
|
|
6537
|
+
align: "right"
|
|
6538
|
+
},
|
|
6539
|
+
{
|
|
6540
|
+
header: "COST",
|
|
6541
|
+
align: "right"
|
|
6542
|
+
},
|
|
6543
|
+
{ header: "FINISHED" }
|
|
6544
|
+
], overview.groups.flatMap(groupRows))];
|
|
6545
|
+
if (overview.moreTasks > 0) lines.push(`${counted(overview.moreTasks, "more task", "more tasks")}: kcloud agents tasks list ${agent}`);
|
|
6546
|
+
if (overview.source === "projection" && overview.staleReason !== null) lines.push(`No results from memory: ${staleReasons[overview.staleReason]}.`);
|
|
6547
|
+
return lines.join("\n");
|
|
6548
|
+
};
|
|
6549
|
+
const healthRow = (label, workload) => [
|
|
6550
|
+
label,
|
|
6551
|
+
String(workload.tasksDone),
|
|
6552
|
+
String(workload.tasksFailed),
|
|
6553
|
+
String(workload.tasksHandedOff),
|
|
6554
|
+
money(workload.tasksCostUsd),
|
|
6555
|
+
workload.averageTaskMs === null ? none : minutesOf(workload.averageTaskMs),
|
|
6556
|
+
`${workload.cycles} (${money(workload.cyclesCostUsd)})`
|
|
6557
|
+
];
|
|
6558
|
+
const healthText = (overview) => {
|
|
6559
|
+
const lines = [renderTable([
|
|
6560
|
+
{ header: "WINDOW" },
|
|
6561
|
+
{
|
|
6562
|
+
header: "DONE",
|
|
6563
|
+
align: "right"
|
|
6564
|
+
},
|
|
6565
|
+
{
|
|
6566
|
+
header: "FAILED",
|
|
6567
|
+
align: "right"
|
|
6568
|
+
},
|
|
6569
|
+
{
|
|
6570
|
+
header: "HANDED OFF",
|
|
6571
|
+
align: "right"
|
|
6572
|
+
},
|
|
6573
|
+
{
|
|
6574
|
+
header: "TASK COST",
|
|
6575
|
+
align: "right"
|
|
6576
|
+
},
|
|
6577
|
+
{
|
|
6578
|
+
header: "AVERAGE",
|
|
6579
|
+
align: "right"
|
|
6580
|
+
},
|
|
6581
|
+
{
|
|
6582
|
+
header: "REVIEWS",
|
|
6583
|
+
align: "right"
|
|
6584
|
+
}
|
|
6585
|
+
], [healthRow("24 hours", overview.health.day), healthRow("7 days", overview.health.week)]), `Cost today: ${money(overview.costTodayUsd)}`];
|
|
6586
|
+
if (overview.lastSession !== null) lines.push(`Last session: ${overview.lastSession.role ?? "unknown"}, ${overview.lastSession.startedAt}`);
|
|
6587
|
+
return lines.join("\n");
|
|
6588
|
+
};
|
|
6589
|
+
const overviewText = (overview, agent = "<agent>") => [
|
|
6590
|
+
"NOW",
|
|
6591
|
+
nowText(overview),
|
|
6592
|
+
"",
|
|
6593
|
+
`NEEDS ATTENTION (${overview.attention.length})`,
|
|
6594
|
+
attentionText(overview),
|
|
6595
|
+
"",
|
|
6596
|
+
"RECENT WORK",
|
|
6597
|
+
recentText(overview, agent),
|
|
6598
|
+
"",
|
|
6599
|
+
"HOW IT GOES",
|
|
6600
|
+
healthText(overview)
|
|
6601
|
+
].join("\n");
|
|
6602
|
+
const overviewCommand = (adaptersOf) => defineCommand({
|
|
6603
|
+
name: "overview",
|
|
6604
|
+
summary: "Show what an agent does now, what needs attention and how its work goes.",
|
|
6605
|
+
usage: "agents overview <agent> [--project <name>]",
|
|
6606
|
+
optionsUsage: [...agentUsage],
|
|
6607
|
+
options: { ...agentOption },
|
|
6608
|
+
positionals: {
|
|
6609
|
+
min: 1,
|
|
6610
|
+
max: 1
|
|
6611
|
+
},
|
|
6612
|
+
run: async (invocation) => {
|
|
6613
|
+
const run = await runOf(adaptersOf, invocation);
|
|
6614
|
+
const agent = invocation.positionals[0];
|
|
6615
|
+
const agentId = await agentFrom(run, agent, invocation.values.project);
|
|
6616
|
+
return printed(run, await agentOverviewOf(run.api, agentId), (overview) => overviewText(overview, agent));
|
|
6617
|
+
}
|
|
6618
|
+
});
|
|
6619
|
+
//#endregion
|
|
6314
6620
|
//#region src/commands/agents/streams.ts
|
|
6315
6621
|
const streamOptions = {
|
|
6316
6622
|
...agentOption,
|
|
@@ -6675,6 +6981,7 @@ const createAgentsCommand = (adaptersOf) => defineGroup({
|
|
|
6675
6981
|
tasksCommand(adaptersOf),
|
|
6676
6982
|
backupsCommand(adaptersOf),
|
|
6677
6983
|
restoreCommand(adaptersOf),
|
|
6984
|
+
overviewCommand(adaptersOf),
|
|
6678
6985
|
...observabilityCommands(adaptersOf),
|
|
6679
6986
|
...streamCommands(adaptersOf),
|
|
6680
6987
|
dashboardCommand(adaptersOf)
|
|
@@ -7623,7 +7930,7 @@ const agentTools = [
|
|
|
7623
7930
|
defineTool({
|
|
7624
7931
|
name: "agent_create",
|
|
7625
7932
|
title: "Create an agent",
|
|
7626
|
-
description: "Create an agent in a project. The node it runs on, the git provider credentials, repositories and tools are named by the ids that nodes_list, credentials_list, repositories_list and tools_list return; gitCredentialIds takes one git provider credential for each server of its repositories; agent_show with asPlan shows a complete example. The agent works on the queue of Claude accounts of its project, which project_accounts_list shows. requiredCapabilities names what the node has to offer, from the offered list of nodes_list; the instance refuses a node that lacks one. The agent stays stopped until agent_action starts it.",
|
|
7933
|
+
description: "Create an agent in a project. The node it runs on, the git provider credentials, repositories and tools are named by the ids that nodes_list, credentials_list, repositories_list and tools_list return; gitCredentialIds takes one git provider credential for each server of its repositories; agent_show with asPlan shows a complete example. The agent works on the queue of Claude accounts of its project, which project_accounts_list shows. requiredCapabilities names what the node has to offer, from the offered list of nodes_list; the instance refuses a node that lacks one. kikimoraVersion takes a release of releases_list, or \"latest\" to follow the newest stable release, which the agent then moves to between sessions; the instance refuses \"latest\" with release.none_stable while no release is stable. The agent stays stopped until agent_action starts it.",
|
|
7627
7934
|
operations: ["createAgent"],
|
|
7628
7935
|
safety: "write",
|
|
7629
7936
|
input: z.object({
|
|
@@ -7638,7 +7945,7 @@ const agentTools = [
|
|
|
7638
7945
|
defineTool({
|
|
7639
7946
|
name: "agent_update",
|
|
7640
7947
|
title: "Change an agent",
|
|
7641
|
-
description: "Change sections of an agent; the credentials section names its git provider credentials as gitCredentialIds, and takes the repositories section in the same change. Without version the change applies to the version read just before it. When restartRequired comes back true, agent_action start or update applies the change, whichever availableActions offers.",
|
|
7948
|
+
description: "Change sections of an agent; the credentials section names its git provider credentials as gitCredentialIds, and takes the repositories section in the same change. sections.version names the Kikimora release, one of releases_list, or \"latest\" to follow the newest stable release. Without version the change applies to the version read just before it. When restartRequired comes back true with a reason other than latest_release, agent_action start or update applies the change, whichever availableActions offers. latest_release alone needs no action: the node moves the agent onto the pending release after its current session, and a restart would replace the container in the middle of one.",
|
|
7642
7949
|
operations: ["updateAgent"],
|
|
7643
7950
|
safety: "write",
|
|
7644
7951
|
input: z.object({
|
|
@@ -8927,6 +9234,15 @@ const sources = {
|
|
|
8927
9234
|
}
|
|
8928
9235
|
};
|
|
8929
9236
|
const observabilityTools = [
|
|
9237
|
+
defineTool({
|
|
9238
|
+
name: "agent_overview",
|
|
9239
|
+
title: "Show the overview of an agent",
|
|
9240
|
+
description: "Show what an agent works on, what needs attention, its recent work grouped by the source of each task, and its counts and costs over 24 hours and 7 days.",
|
|
9241
|
+
operations: ["getAgentOverview"],
|
|
9242
|
+
safety: "read",
|
|
9243
|
+
input: z.object({ ...agentFields }),
|
|
9244
|
+
run: async (reference, run) => agentOverviewOf(run.api, await agentIdOf(run, reference))
|
|
9245
|
+
}),
|
|
8930
9246
|
defineTool({
|
|
8931
9247
|
name: "agent_sessions",
|
|
8932
9248
|
title: "List the sessions of an agent",
|
|
@@ -10639,11 +10955,17 @@ const providerTools = [
|
|
|
10639
10955
|
const releaseTools = [defineTool({
|
|
10640
10956
|
name: "releases_list",
|
|
10641
10957
|
title: "List releases",
|
|
10642
|
-
description: "List the Kikimora releases an agent can run, with the Claude Code and Playwright MCP versions each one ships.",
|
|
10958
|
+
description: "List the Kikimora releases an agent can run, with the Claude Code and Playwright MCP versions each one ships and whether it is stable, and the version \"latest\" points at now, null when no release is stable.",
|
|
10643
10959
|
operations: ["listReleases"],
|
|
10644
10960
|
safety: "read",
|
|
10645
10961
|
input: z.object({}),
|
|
10646
|
-
run: async (_, run) =>
|
|
10962
|
+
run: async (_, run) => {
|
|
10963
|
+
const { latest, items } = dataOf("list the releases", await run.api.GET("/releases"));
|
|
10964
|
+
return {
|
|
10965
|
+
latest,
|
|
10966
|
+
items
|
|
10967
|
+
};
|
|
10968
|
+
}
|
|
10647
10969
|
})];
|
|
10648
10970
|
//#endregion
|
|
10649
10971
|
//#region src/commands/repositories.ts
|
|
@@ -11844,13 +12166,31 @@ const createProvidersCommand = (adaptersOf) => defineGroup({
|
|
|
11844
12166
|
});
|
|
11845
12167
|
//#endregion
|
|
11846
12168
|
//#region src/commands/releases.ts
|
|
12169
|
+
const latestLineOf = ({ latest }) => latest === null ? "No release is stable yet." : `Latest points at ${latest}.`;
|
|
12170
|
+
const releasesOf = (listed) => listed.items.length === 0 ? "The catalogue holds no release an agent can run." : [
|
|
12171
|
+
renderTable([
|
|
12172
|
+
{ header: "VERSION" },
|
|
12173
|
+
{ header: "STABLE" },
|
|
12174
|
+
{ header: "CLAUDE CODE" },
|
|
12175
|
+
{ header: "PLAYWRIGHT MCP" },
|
|
12176
|
+
{ header: "PUBLISHED" }
|
|
12177
|
+
], listed.items.map((release) => [
|
|
12178
|
+
release.version,
|
|
12179
|
+
release.stable ? "yes" : "no",
|
|
12180
|
+
release.claudeCodeVersion ?? "-",
|
|
12181
|
+
release.playwrightMcpVersion ?? "-",
|
|
12182
|
+
release.imageCreatedAt ?? "-"
|
|
12183
|
+
])),
|
|
12184
|
+
"",
|
|
12185
|
+
latestLineOf(listed)
|
|
12186
|
+
].join("\n");
|
|
11847
12187
|
const createReleasesCommand = (adaptersOf) => defineGroup({
|
|
11848
12188
|
name: "releases",
|
|
11849
12189
|
summary: "The Kikimora releases an agent can run.",
|
|
11850
12190
|
usage: "releases <command>",
|
|
11851
12191
|
subcommands: [defineCommand({
|
|
11852
12192
|
name: "list",
|
|
11853
|
-
summary: "List the releases you can give an agent.",
|
|
12193
|
+
summary: "List the releases you can give an agent, and the one latest points at.",
|
|
11854
12194
|
usage: "releases list",
|
|
11855
12195
|
options: {},
|
|
11856
12196
|
positionals: {
|
|
@@ -11860,17 +12200,7 @@ const createReleasesCommand = (adaptersOf) => defineGroup({
|
|
|
11860
12200
|
run: async (invocation) => {
|
|
11861
12201
|
const run = await runOf(adaptersOf, invocation);
|
|
11862
12202
|
const releases = dataOf("list the releases", await run.api.GET("/releases"));
|
|
11863
|
-
return printed(run, releases,
|
|
11864
|
-
{ header: "VERSION" },
|
|
11865
|
-
{ header: "CLAUDE CODE" },
|
|
11866
|
-
{ header: "PLAYWRIGHT MCP" },
|
|
11867
|
-
{ header: "PUBLISHED" }
|
|
11868
|
-
], listed.items.map((release) => [
|
|
11869
|
-
release.version,
|
|
11870
|
-
release.claudeCodeVersion ?? "-",
|
|
11871
|
-
release.playwrightMcpVersion ?? "-",
|
|
11872
|
-
release.imageCreatedAt ?? "-"
|
|
11873
|
-
])));
|
|
12203
|
+
return printed(run, releases, releasesOf);
|
|
11874
12204
|
}
|
|
11875
12205
|
})]
|
|
11876
12206
|
});
|