@chrysb/alphaclaw 0.6.2-beta.1 → 0.6.2-beta.3
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/lib/public/css/cron.css +9 -1
- package/lib/public/js/components/cron-tab/cron-calendar.js +239 -35
- package/lib/public/js/components/cron-tab/cron-job-detail.js +161 -127
- package/lib/public/js/components/cron-tab/cron-job-usage.js +11 -18
- package/lib/public/js/components/cron-tab/cron-overview.js +13 -274
- package/lib/public/js/components/cron-tab/cron-run-history-panel.js +296 -0
- package/lib/public/js/components/cron-tab/cron-runs-trend-card.js +155 -96
- package/lib/public/js/components/cron-tab/index.js +9 -4
- package/lib/public/js/components/cron-tab/use-cron-tab.js +118 -24
- package/lib/public/js/lib/api.js +25 -0
- package/lib/server/cron-service.js +57 -4
- package/lib/server/onboarding/import/import-applier.js +15 -3
- package/lib/server/routes/cron.js +21 -0
- package/package.json +1 -1
package/lib/public/css/cron.css
CHANGED
|
@@ -210,6 +210,9 @@
|
|
|
210
210
|
flex-direction: column;
|
|
211
211
|
gap: 12px;
|
|
212
212
|
min-height: 100%;
|
|
213
|
+
width: min(100%, 1024px);
|
|
214
|
+
margin-left: auto;
|
|
215
|
+
margin-right: auto;
|
|
213
216
|
}
|
|
214
217
|
|
|
215
218
|
.cron-prompt-editor-shell {
|
|
@@ -222,6 +225,11 @@
|
|
|
222
225
|
background: rgba(255, 255, 255, 0.01);
|
|
223
226
|
}
|
|
224
227
|
|
|
228
|
+
.cron-prompt-editor-shell .file-viewer-editor-line-num-col {
|
|
229
|
+
width: 44px;
|
|
230
|
+
padding: 16px 8px 112px 0;
|
|
231
|
+
}
|
|
232
|
+
|
|
225
233
|
.cron-calendar-repeating-strip {
|
|
226
234
|
border: 1px solid var(--border);
|
|
227
235
|
border-radius: 10px;
|
|
@@ -284,7 +292,7 @@
|
|
|
284
292
|
.cron-calendar-grid-header,
|
|
285
293
|
.cron-calendar-grid-row {
|
|
286
294
|
display: grid;
|
|
287
|
-
grid-template-columns:
|
|
295
|
+
grid-template-columns: 80px repeat(7, minmax(80px, 1fr));
|
|
288
296
|
}
|
|
289
297
|
|
|
290
298
|
.cron-calendar-day-header {
|
|
@@ -7,7 +7,6 @@ import { formatCost, formatTokenCount } from "./cron-helpers.js";
|
|
|
7
7
|
import { formatCronScheduleLabel } from "./cron-helpers.js";
|
|
8
8
|
import { readUiSettings, updateUiSettings } from "../../lib/ui-settings.js";
|
|
9
9
|
import {
|
|
10
|
-
buildTokenTierByJobId,
|
|
11
10
|
classifyRepeatingJobs,
|
|
12
11
|
expandJobsToRollingSlots,
|
|
13
12
|
getUpcomingSlots,
|
|
@@ -68,47 +67,195 @@ const renderLegend = () => html`
|
|
|
68
67
|
|
|
69
68
|
const kNowRefreshMs = 60 * 1000;
|
|
70
69
|
const kCalendarExpandedUiSettingKey = "cronCalendarExpanded";
|
|
70
|
+
const kRunWindow7dMs = 7 * 24 * 60 * 60 * 1000;
|
|
71
|
+
const kSlotRunToleranceMs = 45 * 60 * 1000;
|
|
72
|
+
const kUnknownTier = "unknown";
|
|
71
73
|
|
|
72
74
|
const formatUpcomingTime = (timestampMs) => {
|
|
73
75
|
const dateValue = new Date(timestampMs);
|
|
74
76
|
return dateValue.toLocaleTimeString([], { hour: "numeric", minute: "2-digit" });
|
|
75
77
|
};
|
|
76
78
|
|
|
79
|
+
const getRunTotalTokens = (entry = {}) => {
|
|
80
|
+
const usage = entry?.usage || {};
|
|
81
|
+
const candidates = [
|
|
82
|
+
usage?.total_tokens,
|
|
83
|
+
usage?.totalTokens,
|
|
84
|
+
entry?.total_tokens,
|
|
85
|
+
entry?.totalTokens,
|
|
86
|
+
];
|
|
87
|
+
for (const candidate of candidates) {
|
|
88
|
+
const numericValue = Number(candidate);
|
|
89
|
+
if (Number.isFinite(numericValue) && numericValue >= 0) return numericValue;
|
|
90
|
+
}
|
|
91
|
+
return 0;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const getRunEstimatedCost = (entry = {}) => {
|
|
95
|
+
const usage = entry?.usage || {};
|
|
96
|
+
const candidates = [
|
|
97
|
+
entry?.estimatedCost,
|
|
98
|
+
entry?.estimated_cost,
|
|
99
|
+
usage?.estimatedCost,
|
|
100
|
+
usage?.estimated_cost,
|
|
101
|
+
usage?.totalCost,
|
|
102
|
+
usage?.total_cost,
|
|
103
|
+
usage?.costUsd,
|
|
104
|
+
usage?.cost,
|
|
105
|
+
];
|
|
106
|
+
for (const candidate of candidates) {
|
|
107
|
+
const numericValue = Number(candidate);
|
|
108
|
+
if (Number.isFinite(numericValue) && numericValue >= 0) return numericValue;
|
|
109
|
+
}
|
|
110
|
+
return null;
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const buildRunSummaryByJobId = ({ runsByJobId = {}, nowMs = Date.now() } = {}) => {
|
|
114
|
+
const cutoffMs = Number(nowMs || Date.now()) - kRunWindow7dMs;
|
|
115
|
+
return Object.entries(runsByJobId || {}).reduce((accumulator, [jobId, runResult]) => {
|
|
116
|
+
const entries = Array.isArray(runResult?.entries) ? runResult.entries : [];
|
|
117
|
+
const recentEntries = entries.filter((entry) => {
|
|
118
|
+
const timestampMs = Number(entry?.ts || 0);
|
|
119
|
+
return Number.isFinite(timestampMs) && timestampMs >= cutoffMs && timestampMs <= nowMs;
|
|
120
|
+
});
|
|
121
|
+
const runCount = recentEntries.length;
|
|
122
|
+
const totalTokens = recentEntries.reduce(
|
|
123
|
+
(sum, entry) => sum + Number(getRunTotalTokens(entry) || 0),
|
|
124
|
+
0,
|
|
125
|
+
);
|
|
126
|
+
const totalCost = recentEntries.reduce((sum, entry) => {
|
|
127
|
+
const cost = getRunEstimatedCost(entry);
|
|
128
|
+
return sum + Number(cost == null ? 0 : cost);
|
|
129
|
+
}, 0);
|
|
130
|
+
accumulator[String(jobId || "")] = {
|
|
131
|
+
runCount,
|
|
132
|
+
totalTokens,
|
|
133
|
+
totalCost,
|
|
134
|
+
avgTokensPerRun: runCount > 0 ? Math.round(totalTokens / runCount) : 0,
|
|
135
|
+
avgCostPerRun: runCount > 0 ? totalCost / runCount : 0,
|
|
136
|
+
};
|
|
137
|
+
return accumulator;
|
|
138
|
+
}, {});
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const mapRunsToSlots = ({ slots = [], runsByJobId = {}, nowMs = Date.now() } = {}) => {
|
|
142
|
+
const runsBySlotKey = {};
|
|
143
|
+
const consumedRunTimestampsByJobId = {};
|
|
144
|
+
const runEntriesByJobId = Object.entries(runsByJobId || {}).reduce(
|
|
145
|
+
(accumulator, [jobId, runResult]) => {
|
|
146
|
+
const entries = Array.isArray(runResult?.entries) ? runResult.entries : [];
|
|
147
|
+
const normalizedEntries = entries
|
|
148
|
+
.map((entry) => ({ ...entry, ts: Number(entry?.ts || 0) }))
|
|
149
|
+
.filter((entry) => Number.isFinite(entry.ts) && entry.ts > 0)
|
|
150
|
+
.sort((left, right) => left.ts - right.ts);
|
|
151
|
+
accumulator[String(jobId || "")] = normalizedEntries;
|
|
152
|
+
return accumulator;
|
|
153
|
+
},
|
|
154
|
+
{},
|
|
155
|
+
);
|
|
156
|
+
slots.forEach((slot) => {
|
|
157
|
+
if (Number(slot?.scheduledAtMs || 0) > nowMs) return;
|
|
158
|
+
const jobId = String(slot?.jobId || "");
|
|
159
|
+
const runEntries = runEntriesByJobId[jobId] || [];
|
|
160
|
+
if (runEntries.length === 0) return;
|
|
161
|
+
const consumedSet = consumedRunTimestampsByJobId[jobId] || new Set();
|
|
162
|
+
consumedRunTimestampsByJobId[jobId] = consumedSet;
|
|
163
|
+
let nearestEntry = null;
|
|
164
|
+
let nearestDeltaMs = Number.MAX_SAFE_INTEGER;
|
|
165
|
+
runEntries.forEach((entry) => {
|
|
166
|
+
if (consumedSet.has(entry.ts)) return;
|
|
167
|
+
const deltaMs = Math.abs(entry.ts - Number(slot?.scheduledAtMs || 0));
|
|
168
|
+
if (deltaMs > kSlotRunToleranceMs) return;
|
|
169
|
+
if (deltaMs < nearestDeltaMs) {
|
|
170
|
+
nearestDeltaMs = deltaMs;
|
|
171
|
+
nearestEntry = entry;
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
if (!nearestEntry) return;
|
|
175
|
+
consumedSet.add(nearestEntry.ts);
|
|
176
|
+
runsBySlotKey[String(slot?.key || "")] = nearestEntry;
|
|
177
|
+
});
|
|
178
|
+
return runsBySlotKey;
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
const buildTierThresholds = (values = []) => {
|
|
182
|
+
const sortedValues = values
|
|
183
|
+
.map((value) => Number(value || 0))
|
|
184
|
+
.filter((value) => Number.isFinite(value) && value > 0)
|
|
185
|
+
.sort((left, right) => left - right);
|
|
186
|
+
if (sortedValues.length === 0) return null;
|
|
187
|
+
const percentileAt = (indexRatio = 0) => {
|
|
188
|
+
const index = Math.min(
|
|
189
|
+
sortedValues.length - 1,
|
|
190
|
+
Math.floor((sortedValues.length - 1) * indexRatio),
|
|
191
|
+
);
|
|
192
|
+
return sortedValues[Math.max(0, index)];
|
|
193
|
+
};
|
|
194
|
+
return {
|
|
195
|
+
q1: percentileAt(0.25),
|
|
196
|
+
q2: percentileAt(0.5),
|
|
197
|
+
p90: percentileAt(0.9),
|
|
198
|
+
};
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
const classifyTokenTier = ({
|
|
202
|
+
enabled = true,
|
|
203
|
+
tokenValue = 0,
|
|
204
|
+
thresholds = null,
|
|
205
|
+
} = {}) => {
|
|
206
|
+
if (!enabled) return "disabled";
|
|
207
|
+
const safeValue = Number(tokenValue || 0);
|
|
208
|
+
if (!Number.isFinite(safeValue) || safeValue <= 0 || !thresholds) return kUnknownTier;
|
|
209
|
+
if (safeValue <= thresholds.q1) return "low";
|
|
210
|
+
if (safeValue <= thresholds.q2) return "medium";
|
|
211
|
+
if (safeValue <= thresholds.p90) return "high";
|
|
212
|
+
return "very-high";
|
|
213
|
+
};
|
|
214
|
+
|
|
77
215
|
|
|
78
216
|
const buildJobTooltipText = ({
|
|
79
217
|
jobName = "",
|
|
80
218
|
job = null,
|
|
81
|
-
|
|
219
|
+
runSummary7d = {},
|
|
220
|
+
slotRun = null,
|
|
82
221
|
latestRun = null,
|
|
83
222
|
scheduledAtMs = 0,
|
|
84
223
|
scheduledStatus = "",
|
|
224
|
+
nowMs = Date.now(),
|
|
85
225
|
} = {}) => {
|
|
86
|
-
const
|
|
87
|
-
const
|
|
88
|
-
const
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const
|
|
226
|
+
const isPastSlot = Number(scheduledAtMs || 0) > 0 && Number(scheduledAtMs || 0) <= nowMs;
|
|
227
|
+
const runCount7d = Number(runSummary7d?.runCount || 0);
|
|
228
|
+
const avgTokensPerRun7d = Number(runSummary7d?.avgTokensPerRun || 0);
|
|
229
|
+
const avgCostPerRun7d = Number(runSummary7d?.avgCostPerRun || 0);
|
|
230
|
+
const slotRunTokens = getRunTotalTokens(slotRun || {});
|
|
231
|
+
const slotRunCost = getRunEstimatedCost(slotRun || {});
|
|
232
|
+
const slotRunStatus = String(slotRun?.status || "").trim().toLowerCase();
|
|
93
233
|
|
|
94
|
-
const lines = [
|
|
95
|
-
|
|
96
|
-
`
|
|
97
|
-
`
|
|
98
|
-
`
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
234
|
+
const lines = [String(jobName || "Job")];
|
|
235
|
+
if (isPastSlot) {
|
|
236
|
+
lines.push(`Run tokens: ${slotRun ? formatTokenCount(slotRunTokens) : "—"}`);
|
|
237
|
+
lines.push(`Run cost: ${slotRunCost == null ? "—" : formatCost(slotRunCost)}`);
|
|
238
|
+
lines.push(`Run status: ${slotRunStatus || scheduledStatus || "unknown"}`);
|
|
239
|
+
if (slotRun?.ts) {
|
|
240
|
+
lines.push(
|
|
241
|
+
`Run time: ${new Date(Number(slotRun.ts || 0)).toLocaleString()}`,
|
|
242
|
+
);
|
|
243
|
+
}
|
|
103
244
|
} else {
|
|
104
|
-
lines.push(
|
|
245
|
+
lines.push(
|
|
246
|
+
`Avg tokens/run (last 7d): ${runCount7d > 0 ? formatTokenCount(avgTokensPerRun7d) : "—"}`,
|
|
247
|
+
);
|
|
248
|
+
lines.push(
|
|
249
|
+
`Avg cost/run (last 7d): ${runCount7d > 0 ? formatCost(avgCostPerRun7d) : "—"}`,
|
|
250
|
+
);
|
|
251
|
+
lines.push(`Runs (last 7d): ${runCount7d > 0 ? formatTokenCount(runCount7d) : "none"}`);
|
|
105
252
|
}
|
|
106
253
|
|
|
107
|
-
if (latestRun?.status) {
|
|
254
|
+
if (!isPastSlot && latestRun?.status) {
|
|
108
255
|
lines.push(
|
|
109
256
|
`Latest run: ${latestRun.status} (${new Date(Number(latestRun.ts || 0)).toLocaleString()})`,
|
|
110
257
|
);
|
|
111
|
-
} else {
|
|
258
|
+
} else if (!isPastSlot) {
|
|
112
259
|
lines.push("Latest run: none");
|
|
113
260
|
}
|
|
114
261
|
if (Number(job?.state?.runningAtMs || 0) > 0) {
|
|
@@ -125,7 +272,6 @@ const buildJobTooltipText = ({
|
|
|
125
272
|
|
|
126
273
|
export const CronCalendar = ({
|
|
127
274
|
jobs = [],
|
|
128
|
-
usageByJobId = {},
|
|
129
275
|
runsByJobId = {},
|
|
130
276
|
onSelectJob = () => {},
|
|
131
277
|
}) => {
|
|
@@ -163,10 +309,6 @@ export const CronCalendar = ({
|
|
|
163
309
|
() => mapRunStatusesToSlots({ slots: timeline.slots, bulkRunsByJobId: runsByJobId, nowMs }),
|
|
164
310
|
[timeline.slots, runsByJobId, nowMs],
|
|
165
311
|
);
|
|
166
|
-
const tokenTierByJobId = useMemo(
|
|
167
|
-
() => buildTokenTierByJobId({ jobs, usageByJobId }),
|
|
168
|
-
[jobs, usageByJobId],
|
|
169
|
-
);
|
|
170
312
|
const jobById = useMemo(
|
|
171
313
|
() =>
|
|
172
314
|
jobs.reduce((accumulator, job) => {
|
|
@@ -188,6 +330,63 @@ export const CronCalendar = ({
|
|
|
188
330
|
}, {}),
|
|
189
331
|
[runsByJobId],
|
|
190
332
|
);
|
|
333
|
+
const runSummary7dByJobId = useMemo(
|
|
334
|
+
() => buildRunSummaryByJobId({ runsByJobId, nowMs }),
|
|
335
|
+
[runsByJobId, nowMs],
|
|
336
|
+
);
|
|
337
|
+
const runBySlotKey = useMemo(
|
|
338
|
+
() => mapRunsToSlots({ slots: timeline.slots, runsByJobId, nowMs }),
|
|
339
|
+
[timeline.slots, runsByJobId, nowMs],
|
|
340
|
+
);
|
|
341
|
+
const slotTierThresholds = useMemo(() => {
|
|
342
|
+
const values = [];
|
|
343
|
+
timeline.slots.forEach((slot) => {
|
|
344
|
+
const job = jobById[slot.jobId] || null;
|
|
345
|
+
if (!job || job.enabled === false) return;
|
|
346
|
+
const isPastSlot = Number(slot?.scheduledAtMs || 0) <= nowMs;
|
|
347
|
+
if (isPastSlot) {
|
|
348
|
+
const slotRunTokens = getRunTotalTokens(runBySlotKey[slot.key] || {});
|
|
349
|
+
if (slotRunTokens > 0) values.push(slotRunTokens);
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
const projectedAvgTokens = Number(runSummary7dByJobId[slot.jobId]?.avgTokensPerRun || 0);
|
|
353
|
+
if (projectedAvgTokens > 0) values.push(projectedAvgTokens);
|
|
354
|
+
});
|
|
355
|
+
repeatingJobs.forEach((job) => {
|
|
356
|
+
const jobId = String(job?.id || "");
|
|
357
|
+
const projectedAvgTokens = Number(runSummary7dByJobId[jobId]?.avgTokensPerRun || 0);
|
|
358
|
+
if (projectedAvgTokens > 0) values.push(projectedAvgTokens);
|
|
359
|
+
});
|
|
360
|
+
return buildTierThresholds(values);
|
|
361
|
+
}, [jobById, nowMs, repeatingJobs, runBySlotKey, runSummary7dByJobId, timeline.slots]);
|
|
362
|
+
const getSlotTokenTier = useCallback((slot = null) => {
|
|
363
|
+
const jobId = String(slot?.jobId || "");
|
|
364
|
+
const job = jobById[jobId] || null;
|
|
365
|
+
const enabled = job?.enabled !== false;
|
|
366
|
+
const isPastSlot = Number(slot?.scheduledAtMs || 0) <= nowMs;
|
|
367
|
+
if (isPastSlot) {
|
|
368
|
+
const slotRunTokens = getRunTotalTokens(runBySlotKey[String(slot?.key || "")] || {});
|
|
369
|
+
return classifyTokenTier({
|
|
370
|
+
enabled,
|
|
371
|
+
tokenValue: slotRunTokens,
|
|
372
|
+
thresholds: slotTierThresholds,
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
const projectedAvgTokens = Number(runSummary7dByJobId[jobId]?.avgTokensPerRun || 0);
|
|
376
|
+
return classifyTokenTier({
|
|
377
|
+
enabled,
|
|
378
|
+
tokenValue: projectedAvgTokens,
|
|
379
|
+
thresholds: slotTierThresholds,
|
|
380
|
+
});
|
|
381
|
+
}, [jobById, nowMs, runBySlotKey, runSummary7dByJobId, slotTierThresholds]);
|
|
382
|
+
const getJobProjectedTier = useCallback((jobId = "") => {
|
|
383
|
+
const job = jobById[jobId] || null;
|
|
384
|
+
return classifyTokenTier({
|
|
385
|
+
enabled: job?.enabled !== false,
|
|
386
|
+
tokenValue: Number(runSummary7dByJobId[jobId]?.avgTokensPerRun || 0),
|
|
387
|
+
thresholds: slotTierThresholds,
|
|
388
|
+
});
|
|
389
|
+
}, [jobById, runSummary7dByJobId, slotTierThresholds]);
|
|
191
390
|
|
|
192
391
|
const upcomingSlots = useMemo(
|
|
193
392
|
() => getUpcomingSlots({ slots: timeline.slots, nowMs }),
|
|
@@ -223,13 +422,14 @@ export const CronCalendar = ({
|
|
|
223
422
|
: html`
|
|
224
423
|
<div class="cron-calendar-compact-strip">
|
|
225
424
|
${upcomingSlots.map((slot) => {
|
|
226
|
-
const usage = usageByJobId[slot.jobId] || {};
|
|
227
425
|
const tooltipText = buildJobTooltipText({
|
|
228
426
|
jobName: slot.jobName,
|
|
229
427
|
job: jobById[slot.jobId] || null,
|
|
230
|
-
|
|
428
|
+
runSummary7d: runSummary7dByJobId[slot.jobId] || {},
|
|
429
|
+
slotRun: runBySlotKey[slot.key] || null,
|
|
231
430
|
latestRun: latestRunByJobId[slot.jobId],
|
|
232
431
|
scheduledAtMs: slot.scheduledAtMs,
|
|
432
|
+
nowMs,
|
|
233
433
|
});
|
|
234
434
|
return html`
|
|
235
435
|
<${Tooltip}
|
|
@@ -309,15 +509,16 @@ export const CronCalendar = ({
|
|
|
309
509
|
${visibleSlots.map((slot) => {
|
|
310
510
|
const status = statusBySlotKey[slot.key] || "";
|
|
311
511
|
const isPast = slot.scheduledAtMs <= nowMs;
|
|
312
|
-
const tokenTier =
|
|
313
|
-
const usage = usageByJobId[slot.jobId] || {};
|
|
512
|
+
const tokenTier = getSlotTokenTier(slot);
|
|
314
513
|
const tooltipText = buildJobTooltipText({
|
|
315
514
|
jobName: slot.jobName,
|
|
316
515
|
job: jobById[slot.jobId] || null,
|
|
317
|
-
|
|
516
|
+
runSummary7d: runSummary7dByJobId[slot.jobId] || {},
|
|
517
|
+
slotRun: runBySlotKey[slot.key] || null,
|
|
318
518
|
latestRun: latestRunByJobId[slot.jobId],
|
|
319
519
|
scheduledAtMs: slot.scheduledAtMs,
|
|
320
520
|
scheduledStatus: status,
|
|
521
|
+
nowMs,
|
|
321
522
|
});
|
|
322
523
|
return html`
|
|
323
524
|
<${Tooltip}
|
|
@@ -366,13 +567,16 @@ export const CronCalendar = ({
|
|
|
366
567
|
<div class="cron-calendar-repeating-list">
|
|
367
568
|
${repeatingJobs.map((job) => {
|
|
368
569
|
const jobId = String(job?.id || "");
|
|
369
|
-
const
|
|
370
|
-
|
|
570
|
+
const avgTokensPerRun = Number(
|
|
571
|
+
runSummary7dByJobId[jobId]?.avgTokensPerRun || 0,
|
|
572
|
+
);
|
|
371
573
|
const tooltipText = buildJobTooltipText({
|
|
372
574
|
jobName: job.name || job.id,
|
|
373
575
|
job,
|
|
374
|
-
|
|
576
|
+
runSummary7d: runSummary7dByJobId[jobId] || {},
|
|
577
|
+
slotRun: null,
|
|
375
578
|
latestRun: latestRunByJobId[jobId],
|
|
579
|
+
nowMs,
|
|
376
580
|
});
|
|
377
581
|
return html`
|
|
378
582
|
<${Tooltip}
|
|
@@ -385,7 +589,7 @@ export const CronCalendar = ({
|
|
|
385
589
|
class=${`cron-calendar-repeating-pill ${slotStateClassName({
|
|
386
590
|
isPast: false,
|
|
387
591
|
mappedStatus: "",
|
|
388
|
-
tokenTier:
|
|
592
|
+
tokenTier: getJobProjectedTier(jobId),
|
|
389
593
|
})}`}
|
|
390
594
|
role="button"
|
|
391
595
|
tabindex="0"
|