@ryanfw/prompt-orchestration-pipeline 1.2.7 → 1.2.9
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/package.json +1 -1
- package/src/config/__tests__/models.test.ts +31 -1
- package/src/config/models.ts +81 -35
- package/src/config/paths.ts +13 -8
- package/src/core/__tests__/config.test.ts +121 -0
- package/src/core/__tests__/job-concurrency.test.ts +554 -0
- package/src/core/__tests__/orchestrator.test.ts +353 -0
- package/src/core/__tests__/pipeline-runner.test.ts +430 -2
- package/src/core/__tests__/task-runner.test.ts +1 -2
- package/src/core/config.ts +48 -1
- package/src/core/job-concurrency.ts +462 -0
- package/src/core/orchestrator.ts +370 -57
- package/src/core/pipeline-runner.ts +79 -15
- package/src/core/status-writer.ts +4 -0
- package/src/core/task-runner.ts +1 -1
- package/src/providers/__tests__/base.test.ts +1 -1
- package/src/ui/client/__tests__/api.test.ts +101 -1
- package/src/ui/client/__tests__/job-adapter.test.ts +12 -0
- package/src/ui/client/__tests__/useConcurrencyStatus.test.ts +126 -0
- package/src/ui/client/adapters/job-adapter.ts +1 -0
- package/src/ui/client/api.ts +77 -7
- package/src/ui/client/hooks/useConcurrencyStatus.ts +102 -0
- package/src/ui/client/types.ts +34 -1
- package/src/ui/components/DAGGrid.tsx +11 -1
- package/src/ui/components/JobDetail.tsx +2 -1
- package/src/ui/components/__tests__/DAGGrid.test.tsx +92 -0
- package/src/ui/components/__tests__/JobDetail.test.tsx +62 -0
- package/src/ui/components/types.ts +2 -0
- package/src/ui/dist/assets/{index-SKy2shWc.js → index-BnAqY4_n.js} +336 -52
- package/src/ui/dist/assets/index-BnAqY4_n.js.map +1 -0
- package/src/ui/dist/assets/style-BKG0bHu-.css +2 -0
- package/src/ui/dist/index.html +2 -2
- package/src/ui/embedded-assets.js +6 -6
- package/src/ui/pages/PromptPipelineDashboard.tsx +186 -4
- package/src/ui/pages/__tests__/PromptPipelineDashboard.test.tsx +272 -1
- package/src/ui/server/__tests__/concurrency-endpoint.test.ts +190 -0
- package/src/ui/server/__tests__/index.test.ts +92 -3
- package/src/ui/server/__tests__/job-control-endpoints.test.ts +660 -3
- package/src/ui/server/endpoints/concurrency-endpoint.ts +72 -0
- package/src/ui/server/endpoints/job-control-endpoints.ts +248 -37
- package/src/ui/server/index.ts +21 -2
- package/src/ui/server/router.ts +2 -0
- package/src/ui/state/__tests__/watcher.test.ts +31 -0
- package/src/ui/state/transformers/__tests__/status-transformer.test.ts +15 -0
- package/src/ui/state/transformers/status-transformer.ts +1 -0
- package/src/ui/state/types.ts +3 -0
- package/src/ui/state/watcher.ts +9 -1
- package/src/utils/__tests__/dag.test.ts +35 -0
- package/src/utils/dag.ts +1 -0
- package/src/ui/dist/assets/index-SKy2shWc.js.map +0 -1
- package/src/ui/dist/assets/style-DA1Ma4YS.css +0 -2
|
@@ -20453,11 +20453,11 @@ const MODEL_CONFIG_RAW = {
|
|
|
20453
20453
|
tokenCostOutPerMillion: 4.5
|
|
20454
20454
|
},
|
|
20455
20455
|
// Alibaba (Qwen via DashScope, international/Singapore deployment, base tier)
|
|
20456
|
-
"alibaba:qwen3-max": {
|
|
20456
|
+
"alibaba:qwen3.6-max-preview": {
|
|
20457
20457
|
provider: "alibaba",
|
|
20458
|
-
model: "qwen3-max",
|
|
20459
|
-
tokenCostInPerMillion: 1.
|
|
20460
|
-
tokenCostOutPerMillion:
|
|
20458
|
+
model: "qwen3.6-max-preview",
|
|
20459
|
+
tokenCostInPerMillion: 1.3,
|
|
20460
|
+
tokenCostOutPerMillion: 7.8
|
|
20461
20461
|
},
|
|
20462
20462
|
"alibaba:qwen3.6-plus": {
|
|
20463
20463
|
provider: "alibaba",
|
|
@@ -20465,6 +20465,18 @@ const MODEL_CONFIG_RAW = {
|
|
|
20465
20465
|
tokenCostInPerMillion: 0.276,
|
|
20466
20466
|
tokenCostOutPerMillion: 1.651
|
|
20467
20467
|
},
|
|
20468
|
+
"alibaba:qwen3.6-flash": {
|
|
20469
|
+
provider: "alibaba",
|
|
20470
|
+
model: "qwen3.6-flash",
|
|
20471
|
+
tokenCostInPerMillion: 0.025,
|
|
20472
|
+
tokenCostOutPerMillion: 1.5
|
|
20473
|
+
},
|
|
20474
|
+
"alibaba:qwen3-max": {
|
|
20475
|
+
provider: "alibaba",
|
|
20476
|
+
model: "qwen3-max",
|
|
20477
|
+
tokenCostInPerMillion: 1.2,
|
|
20478
|
+
tokenCostOutPerMillion: 6
|
|
20479
|
+
},
|
|
20468
20480
|
"alibaba:qwen3.5-plus": {
|
|
20469
20481
|
provider: "alibaba",
|
|
20470
20482
|
model: "qwen3.5-plus",
|
|
@@ -20514,13 +20526,18 @@ const VALID_MODEL_ALIASES = new Set(
|
|
|
20514
20526
|
);
|
|
20515
20527
|
function aliasToFunctionName(alias) {
|
|
20516
20528
|
if (typeof alias !== "string") {
|
|
20517
|
-
throw new Error(
|
|
20529
|
+
throw new Error(
|
|
20530
|
+
`Invalid model alias: expected string, got ${typeof alias}`
|
|
20531
|
+
);
|
|
20518
20532
|
}
|
|
20519
20533
|
if (!alias.includes(":")) {
|
|
20520
20534
|
throw new Error(`Invalid model alias: "${alias}" does not contain a colon`);
|
|
20521
20535
|
}
|
|
20522
20536
|
const model = alias.split(":").slice(1).join(":");
|
|
20523
|
-
return model.replace(
|
|
20537
|
+
return model.replace(
|
|
20538
|
+
/[-.]([a-z0-9])/gi,
|
|
20539
|
+
(_, char) => char.toUpperCase()
|
|
20540
|
+
);
|
|
20524
20541
|
}
|
|
20525
20542
|
Object.freeze(
|
|
20526
20543
|
Object.fromEntries(
|
|
@@ -20542,7 +20559,13 @@ function buildProviderFunctionsIndex() {
|
|
|
20542
20559
|
index2[provider] = [];
|
|
20543
20560
|
}
|
|
20544
20561
|
index2[provider].push(
|
|
20545
|
-
Object.freeze({
|
|
20562
|
+
Object.freeze({
|
|
20563
|
+
alias,
|
|
20564
|
+
provider,
|
|
20565
|
+
model,
|
|
20566
|
+
functionName,
|
|
20567
|
+
fullPath
|
|
20568
|
+
})
|
|
20546
20569
|
);
|
|
20547
20570
|
}
|
|
20548
20571
|
for (const provider of Object.keys(index2)) {
|
|
@@ -21634,15 +21657,16 @@ function Code() {
|
|
|
21634
21657
|
}
|
|
21635
21658
|
);
|
|
21636
21659
|
}
|
|
21637
|
-
|
|
21660
|
+
const CONCURRENCY_LIMIT_MESSAGE = "Capacity reached. Wait for a job to finish, then try again.";
|
|
21661
|
+
function isRecord$5(value) {
|
|
21638
21662
|
return typeof value === "object" && value !== null;
|
|
21639
21663
|
}
|
|
21640
21664
|
function normalizeBackendErrorCode(errorData, status) {
|
|
21641
|
-
if (!isRecord$
|
|
21665
|
+
if (!isRecord$5(errorData) || typeof errorData["code"] !== "string") {
|
|
21642
21666
|
return getErrorCodeFromStatus(status);
|
|
21643
21667
|
}
|
|
21644
21668
|
const code2 = errorData["code"];
|
|
21645
|
-
if (code2 === "job_running" || code2 === "job_not_found" || code2 === "conflict" || code2 === "spawn_failed" || code2 === "unknown_error" || code2 === "network_error" || code2 === "dependencies_not_satisfied" || code2 === "unsupported_lifecycle" || code2 === "task_not_found" || code2 === "task_not_pending") {
|
|
21669
|
+
if (code2 === "job_running" || code2 === "job_not_found" || code2 === "conflict" || code2 === "spawn_failed" || code2 === "unknown_error" || code2 === "network_error" || code2 === "malformed_response" || code2 === "dependencies_not_satisfied" || code2 === "unsupported_lifecycle" || code2 === "task_not_found" || code2 === "task_not_pending" || code2 === "concurrency_limit_reached") {
|
|
21646
21670
|
return code2;
|
|
21647
21671
|
}
|
|
21648
21672
|
if (code2 === "JOB_NOT_FOUND" || code2 === "NOT_FOUND") return "job_not_found";
|
|
@@ -21656,9 +21680,12 @@ function normalizeBackendErrorCode(errorData, status) {
|
|
|
21656
21680
|
return getErrorCodeFromStatus(status);
|
|
21657
21681
|
}
|
|
21658
21682
|
function getMessage(value) {
|
|
21659
|
-
if (!isRecord$
|
|
21683
|
+
if (!isRecord$5(value)) return null;
|
|
21660
21684
|
return typeof value["message"] === "string" ? value["message"] : null;
|
|
21661
21685
|
}
|
|
21686
|
+
function hasBackendCode(errorData, code2) {
|
|
21687
|
+
return isRecord$5(errorData) && errorData["code"] === code2;
|
|
21688
|
+
}
|
|
21662
21689
|
function getErrorCodeFromStatus(status) {
|
|
21663
21690
|
if (status === 404) return "job_not_found";
|
|
21664
21691
|
if (status === 409) return "conflict";
|
|
@@ -21678,31 +21705,37 @@ function getErrorMessageFromStatus(status) {
|
|
|
21678
21705
|
return "Request failed";
|
|
21679
21706
|
}
|
|
21680
21707
|
function getRestartErrorMessage(errorData, status) {
|
|
21681
|
-
if (
|
|
21708
|
+
if (hasBackendCode(errorData, "job_running")) {
|
|
21682
21709
|
return "Cannot restart a job while it is still running";
|
|
21683
21710
|
}
|
|
21684
|
-
if (
|
|
21711
|
+
if (hasBackendCode(errorData, "spawn_failed")) {
|
|
21685
21712
|
return "Failed to spawn the restarted job";
|
|
21686
21713
|
}
|
|
21714
|
+
if (hasBackendCode(errorData, "concurrency_limit_reached")) {
|
|
21715
|
+
return CONCURRENCY_LIMIT_MESSAGE;
|
|
21716
|
+
}
|
|
21687
21717
|
return getMessage(errorData) ?? getErrorMessageFromStatus(status);
|
|
21688
21718
|
}
|
|
21689
21719
|
function getStartTaskErrorMessage(errorData, status) {
|
|
21690
|
-
if (
|
|
21720
|
+
if (hasBackendCode(errorData, "dependencies_not_satisfied")) {
|
|
21691
21721
|
return "Cannot start task before its dependencies are complete";
|
|
21692
21722
|
}
|
|
21693
|
-
if (
|
|
21723
|
+
if (hasBackendCode(errorData, "task_not_found")) {
|
|
21694
21724
|
return "Task not found";
|
|
21695
21725
|
}
|
|
21696
|
-
if (
|
|
21726
|
+
if (hasBackendCode(errorData, "task_not_pending")) {
|
|
21697
21727
|
return "Only pending tasks can be started";
|
|
21698
21728
|
}
|
|
21729
|
+
if (hasBackendCode(errorData, "concurrency_limit_reached")) {
|
|
21730
|
+
return CONCURRENCY_LIMIT_MESSAGE;
|
|
21731
|
+
}
|
|
21699
21732
|
return getMessage(errorData) ?? getErrorMessageFromStatus(status);
|
|
21700
21733
|
}
|
|
21701
21734
|
function getStopErrorMessage(errorData, status) {
|
|
21702
|
-
if (isRecord$
|
|
21735
|
+
if (isRecord$5(errorData) && errorData["code"] === "job_not_found") {
|
|
21703
21736
|
return "Job not found";
|
|
21704
21737
|
}
|
|
21705
|
-
if (isRecord$
|
|
21738
|
+
if (isRecord$5(errorData) && errorData["code"] === "unsupported_lifecycle") {
|
|
21706
21739
|
return "This job cannot be stopped";
|
|
21707
21740
|
}
|
|
21708
21741
|
return getMessage(errorData) ?? getErrorMessageFromStatus(status);
|
|
@@ -21710,11 +21743,12 @@ function getStopErrorMessage(errorData, status) {
|
|
|
21710
21743
|
async function parseJson(response) {
|
|
21711
21744
|
try {
|
|
21712
21745
|
return await response.json();
|
|
21713
|
-
} catch {
|
|
21746
|
+
} catch (error) {
|
|
21747
|
+
if (error instanceof DOMException && error.name === "AbortError") throw error;
|
|
21714
21748
|
return null;
|
|
21715
21749
|
}
|
|
21716
21750
|
}
|
|
21717
|
-
function toApiError$
|
|
21751
|
+
function toApiError$2(status, message, errorData) {
|
|
21718
21752
|
return {
|
|
21719
21753
|
code: normalizeBackendErrorCode(errorData, status),
|
|
21720
21754
|
message,
|
|
@@ -21737,7 +21771,7 @@ async function postJson(url, body, getFailureMessage) {
|
|
|
21737
21771
|
}
|
|
21738
21772
|
const payload = await parseJson(response);
|
|
21739
21773
|
if (response.ok) {
|
|
21740
|
-
if (isRecord$
|
|
21774
|
+
if (isRecord$5(payload) && payload["ok"] === true) {
|
|
21741
21775
|
return {
|
|
21742
21776
|
ok: true,
|
|
21743
21777
|
message: typeof payload["message"] === "string" ? payload["message"] : void 0
|
|
@@ -21745,7 +21779,7 @@ async function postJson(url, body, getFailureMessage) {
|
|
|
21745
21779
|
}
|
|
21746
21780
|
return { ok: true };
|
|
21747
21781
|
}
|
|
21748
|
-
throw toApiError$
|
|
21782
|
+
throw toApiError$2(response.status, getFailureMessage(payload, response.status), payload);
|
|
21749
21783
|
}
|
|
21750
21784
|
async function restartJob(jobId, opts = {}) {
|
|
21751
21785
|
const clearTokenUsage = opts.options?.clearTokenUsage ?? true;
|
|
@@ -21766,6 +21800,40 @@ async function startTask(jobId, taskId) {
|
|
|
21766
21800
|
async function stopJob(jobId) {
|
|
21767
21801
|
return postJson(`/api/jobs/${jobId}/stop`, {}, getStopErrorMessage);
|
|
21768
21802
|
}
|
|
21803
|
+
async function fetchConcurrencyStatus(signal) {
|
|
21804
|
+
let response;
|
|
21805
|
+
let payload;
|
|
21806
|
+
try {
|
|
21807
|
+
response = await fetch("/api/concurrency", { signal });
|
|
21808
|
+
if (signal?.aborted) throw new DOMException("Aborted", "AbortError");
|
|
21809
|
+
payload = await parseJson(response);
|
|
21810
|
+
} catch (error) {
|
|
21811
|
+
if (error instanceof DOMException && error.name === "AbortError") throw error;
|
|
21812
|
+
throw {
|
|
21813
|
+
code: "network_error",
|
|
21814
|
+
message: error instanceof Error ? error.message : "Network request failed"
|
|
21815
|
+
};
|
|
21816
|
+
}
|
|
21817
|
+
if (!response.ok) {
|
|
21818
|
+
throw toApiError$2(
|
|
21819
|
+
response.status,
|
|
21820
|
+
getMessage(payload) ?? getErrorMessageFromStatus(response.status),
|
|
21821
|
+
payload
|
|
21822
|
+
);
|
|
21823
|
+
}
|
|
21824
|
+
if (isRecord$5(payload) && payload["ok"] === true && isJobConcurrencyApiStatus(payload["data"])) {
|
|
21825
|
+
return payload["data"];
|
|
21826
|
+
}
|
|
21827
|
+
throw {
|
|
21828
|
+
code: "malformed_response",
|
|
21829
|
+
message: "Malformed concurrency status response",
|
|
21830
|
+
status: response.status
|
|
21831
|
+
};
|
|
21832
|
+
}
|
|
21833
|
+
function isJobConcurrencyApiStatus(value) {
|
|
21834
|
+
if (!isRecord$5(value)) return false;
|
|
21835
|
+
return typeof value["limit"] === "number" && typeof value["runningCount"] === "number" && typeof value["availableSlots"] === "number" && typeof value["queuedCount"] === "number" && Array.isArray(value["activeJobs"]) && Array.isArray(value["queuedJobs"]) && Array.isArray(value["staleSlots"]);
|
|
21836
|
+
}
|
|
21769
21837
|
const TaskState = Object.freeze({
|
|
21770
21838
|
PENDING: "pending",
|
|
21771
21839
|
RUNNING: "running",
|
|
@@ -21849,7 +21917,7 @@ const EMPTY_COSTS = {
|
|
|
21849
21917
|
totalInputCost: 0,
|
|
21850
21918
|
totalOutputCost: 0
|
|
21851
21919
|
};
|
|
21852
|
-
function isRecord$
|
|
21920
|
+
function isRecord$4(value) {
|
|
21853
21921
|
return typeof value === "object" && value !== null;
|
|
21854
21922
|
}
|
|
21855
21923
|
function toStringOrNull(value) {
|
|
@@ -21859,7 +21927,7 @@ function toNumber(value, fallback = 0) {
|
|
|
21859
21927
|
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
21860
21928
|
}
|
|
21861
21929
|
function normalizeFiles(files) {
|
|
21862
|
-
if (!isRecord$
|
|
21930
|
+
if (!isRecord$4(files)) return EMPTY_FILES$1;
|
|
21863
21931
|
return {
|
|
21864
21932
|
artifacts: Array.isArray(files["artifacts"]) ? files["artifacts"].filter((x) => typeof x === "string") : [],
|
|
21865
21933
|
logs: Array.isArray(files["logs"]) ? files["logs"].filter((x) => typeof x === "string") : [],
|
|
@@ -21867,25 +21935,26 @@ function normalizeFiles(files) {
|
|
|
21867
21935
|
};
|
|
21868
21936
|
}
|
|
21869
21937
|
function normalizeTask(name2, rawTask) {
|
|
21870
|
-
const task = isRecord$
|
|
21938
|
+
const task = isRecord$4(rawTask) ? rawTask : {};
|
|
21871
21939
|
return {
|
|
21872
21940
|
name: name2,
|
|
21873
21941
|
state: normalizeTaskState(task["state"]),
|
|
21874
21942
|
startedAt: toStringOrNull(task["startedAt"]),
|
|
21875
21943
|
endedAt: toStringOrNull(task["endedAt"]),
|
|
21876
21944
|
attempts: typeof task["attempts"] === "number" ? task["attempts"] : void 0,
|
|
21945
|
+
restartCount: typeof task["restartCount"] === "number" ? task["restartCount"] : void 0,
|
|
21877
21946
|
executionTimeMs: typeof task["executionTimeMs"] === "number" ? task["executionTimeMs"] : void 0,
|
|
21878
21947
|
currentStage: typeof task["currentStage"] === "string" ? task["currentStage"] : void 0,
|
|
21879
21948
|
failedStage: typeof task["failedStage"] === "string" ? task["failedStage"] : void 0,
|
|
21880
21949
|
files: normalizeFiles(task["files"]),
|
|
21881
21950
|
artifacts: Array.isArray(task["artifacts"]) ? task["artifacts"].filter((x) => typeof x === "string") : void 0,
|
|
21882
|
-
tokenUsage: isRecord$
|
|
21883
|
-
error: isRecord$
|
|
21951
|
+
tokenUsage: isRecord$4(task["tokenUsage"]) ? task["tokenUsage"] : void 0,
|
|
21952
|
+
error: isRecord$4(task["error"]) ? task["error"] : void 0
|
|
21884
21953
|
};
|
|
21885
21954
|
}
|
|
21886
21955
|
function normalizeCurrent(current) {
|
|
21887
21956
|
if (typeof current === "string") return { taskName: current };
|
|
21888
|
-
if (!isRecord$
|
|
21957
|
+
if (!isRecord$4(current) || typeof current["taskName"] !== "string") return null;
|
|
21889
21958
|
return {
|
|
21890
21959
|
taskName: current["taskName"],
|
|
21891
21960
|
stage: typeof current["stage"] === "string" ? current["stage"] : void 0
|
|
@@ -21898,11 +21967,11 @@ function getDisplayCategory(status) {
|
|
|
21898
21967
|
}
|
|
21899
21968
|
function getWarnings(rawTasks) {
|
|
21900
21969
|
if (rawTasks == null) return [];
|
|
21901
|
-
if (Array.isArray(rawTasks) || isRecord$
|
|
21970
|
+
if (Array.isArray(rawTasks) || isRecord$4(rawTasks)) return [];
|
|
21902
21971
|
return ["Unsupported task collection shape"];
|
|
21903
21972
|
}
|
|
21904
21973
|
function normalizeCostsSummary(costs) {
|
|
21905
|
-
if (!isRecord$
|
|
21974
|
+
if (!isRecord$4(costs)) return EMPTY_COSTS;
|
|
21906
21975
|
return {
|
|
21907
21976
|
totalTokens: toNumber(costs["totalTokens"]),
|
|
21908
21977
|
totalInputTokens: toNumber(costs["totalInputTokens"]),
|
|
@@ -21913,9 +21982,9 @@ function normalizeCostsSummary(costs) {
|
|
|
21913
21982
|
};
|
|
21914
21983
|
}
|
|
21915
21984
|
function normalizeCostBreakdown(costs) {
|
|
21916
|
-
if (!isRecord$
|
|
21985
|
+
if (!isRecord$4(costs)) return void 0;
|
|
21917
21986
|
const entries = Object.entries(costs).flatMap(([name2, value]) => {
|
|
21918
|
-
if (!isRecord$
|
|
21987
|
+
if (!isRecord$4(value)) return [];
|
|
21919
21988
|
return [[name2, {
|
|
21920
21989
|
inputTokens: toNumber(value["inputTokens"]),
|
|
21921
21990
|
outputTokens: toNumber(value["outputTokens"]),
|
|
@@ -21930,12 +21999,12 @@ function normalizeTasks$1(rawTasks) {
|
|
|
21930
21999
|
if (rawTasks == null) return {};
|
|
21931
22000
|
if (Array.isArray(rawTasks)) {
|
|
21932
22001
|
return Object.fromEntries(rawTasks.map((task, index2) => {
|
|
21933
|
-
const record = isRecord$
|
|
22002
|
+
const record = isRecord$4(task) ? task : {};
|
|
21934
22003
|
const name2 = typeof record["name"] === "string" ? record["name"] : `task-${index2}`;
|
|
21935
22004
|
return [name2, normalizeTask(name2, record)];
|
|
21936
22005
|
}));
|
|
21937
22006
|
}
|
|
21938
|
-
if (isRecord$
|
|
22007
|
+
if (isRecord$4(rawTasks)) {
|
|
21939
22008
|
return Object.fromEntries(Object.entries(rawTasks).map(([name2, task]) => {
|
|
21940
22009
|
return [name2, normalizeTask(name2, task)];
|
|
21941
22010
|
}));
|
|
@@ -21947,7 +22016,7 @@ function adaptBaseJob(apiJob) {
|
|
|
21947
22016
|
const tasks = normalizeTasks$1(rawTasks);
|
|
21948
22017
|
const taskList = Object.values(tasks);
|
|
21949
22018
|
const doneCount = taskList.filter((task) => task.state === "done").length;
|
|
21950
|
-
const rawPipelineConfig = isRecord$
|
|
22019
|
+
const rawPipelineConfig = isRecord$4(apiJob["pipelineConfig"]) ? apiJob["pipelineConfig"] : null;
|
|
21951
22020
|
const pipelineTaskArray = rawPipelineConfig && Array.isArray(rawPipelineConfig["tasks"]) ? rawPipelineConfig["tasks"] : null;
|
|
21952
22021
|
const taskCount = pipelineTaskArray ? pipelineTaskArray.length : taskList.length;
|
|
21953
22022
|
const inferredStatus = deriveJobStatusFromTasks(taskList);
|
|
@@ -22002,12 +22071,12 @@ function deriveAllowedActions(adaptedJob, pipelineTasks) {
|
|
|
22002
22071
|
}
|
|
22003
22072
|
const REFRESH_DEBOUNCE_MS = 200;
|
|
22004
22073
|
const POLL_INTERVAL_MS = 3e3;
|
|
22005
|
-
function isRecord$
|
|
22074
|
+
function isRecord$3(value) {
|
|
22006
22075
|
return typeof value === "object" && value !== null;
|
|
22007
22076
|
}
|
|
22008
22077
|
function extractJobDetail(payload) {
|
|
22009
|
-
if (isRecord$
|
|
22010
|
-
if (isRecord$
|
|
22078
|
+
if (isRecord$3(payload) && isRecord$3(payload["data"])) return payload["data"];
|
|
22079
|
+
if (isRecord$3(payload)) return payload;
|
|
22011
22080
|
return null;
|
|
22012
22081
|
}
|
|
22013
22082
|
function getPipelineTaskCount(detail) {
|
|
@@ -22047,7 +22116,7 @@ function applyDetailEvent(detail, event) {
|
|
|
22047
22116
|
if (!currentTask) return detail;
|
|
22048
22117
|
const nextTask = {
|
|
22049
22118
|
...currentTask,
|
|
22050
|
-
...isRecord$
|
|
22119
|
+
...isRecord$3(event.data["task"]) ? adaptJobDetail({
|
|
22051
22120
|
...detail,
|
|
22052
22121
|
tasks: {
|
|
22053
22122
|
[taskName]: event.data["task"]
|
|
@@ -22283,7 +22352,8 @@ function computeDagItems(job, pipeline) {
|
|
|
22283
22352
|
subtitle: null,
|
|
22284
22353
|
body: task?.error?.message ?? null,
|
|
22285
22354
|
startedAt: task?.startedAt ?? 0,
|
|
22286
|
-
endedAt: task?.endedAt ?? null
|
|
22355
|
+
endedAt: task?.endedAt ?? null,
|
|
22356
|
+
restartCount: task?.restartCount ?? 0
|
|
22287
22357
|
};
|
|
22288
22358
|
});
|
|
22289
22359
|
}
|
|
@@ -51031,7 +51101,20 @@ function DAGGrid({
|
|
|
51031
51101
|
"data-role": "card-header",
|
|
51032
51102
|
className: `flex items-center justify-between gap-3 rounded-t-lg border-b px-4 py-2 ${reducedMotion ? "" : "transition-opacity duration-300 ease-in-out"} ${getHeaderClasses(getItemStatus(items[index2], index2, activeIndex))}`,
|
|
51033
51103
|
children: [
|
|
51034
|
-
|
|
51104
|
+
(items[index2]?.restartCount ?? 0) > 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
51105
|
+
"span",
|
|
51106
|
+
{
|
|
51107
|
+
"data-role": "restart-badge",
|
|
51108
|
+
className: "inline-flex items-center justify-center min-w-[20px] h-5 px-1.5 rounded-sm border border-red-300 bg-red-50 text-red-700 text-[11px] font-medium tabular-nums",
|
|
51109
|
+
title: `Restarted ${items[index2].restartCount} time${items[index2].restartCount === 1 ? "" : "s"}`,
|
|
51110
|
+
"aria-label": `Restarted ${items[index2].restartCount} time${items[index2].restartCount === 1 ? "" : "s"}`,
|
|
51111
|
+
children: [
|
|
51112
|
+
"↻ ",
|
|
51113
|
+
items[index2].restartCount
|
|
51114
|
+
]
|
|
51115
|
+
}
|
|
51116
|
+
) : null,
|
|
51117
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "min-w-0 flex-1 truncate text-left font-medium", children: items[index2]?.title ?? formatStepName(items[index2]?.id ?? "") }),
|
|
51035
51118
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center gap-2", children: getItemStatus(items[index2], index2, activeIndex) === "running" ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
51036
51119
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative h-4 w-4", "aria-label": "Active", children: [
|
|
51037
51120
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sr-only", children: "Active" }),
|
|
@@ -51185,7 +51268,7 @@ function JobDetail({
|
|
|
51185
51268
|
const previous2 = prevDagItemsRef.current;
|
|
51186
51269
|
const reused = enriched.map((item, index2) => {
|
|
51187
51270
|
const prior = previous2[index2];
|
|
51188
|
-
if (prior && prior.id === item.id && prior.status === item.status && prior.stage === item.stage && prior.title === item.title && prior.subtitle === item.subtitle && prior.body === item.body) {
|
|
51271
|
+
if (prior && prior.id === item.id && prior.status === item.status && prior.stage === item.stage && prior.title === item.title && prior.subtitle === item.subtitle && prior.body === item.body && prior.restartCount === item.restartCount) {
|
|
51189
51272
|
return prior;
|
|
51190
51273
|
}
|
|
51191
51274
|
return item;
|
|
@@ -77633,11 +77716,11 @@ function createInitialAnalysisState() {
|
|
|
77633
77716
|
error: null
|
|
77634
77717
|
};
|
|
77635
77718
|
}
|
|
77636
|
-
function isRecord$
|
|
77719
|
+
function isRecord$2(value) {
|
|
77637
77720
|
return typeof value === "object" && value !== null;
|
|
77638
77721
|
}
|
|
77639
77722
|
function reduceAnalysisEvent(state, eventType, payload) {
|
|
77640
|
-
const data = isRecord$
|
|
77723
|
+
const data = isRecord$2(payload) ? payload : {};
|
|
77641
77724
|
if (eventType === "started") {
|
|
77642
77725
|
return {
|
|
77643
77726
|
...state,
|
|
@@ -77778,6 +77861,86 @@ function PipelineTypeDetail() {
|
|
|
77778
77861
|
}
|
|
77779
77862
|
);
|
|
77780
77863
|
}
|
|
77864
|
+
const REFETCH_EVENTS$1 = ["state:summary", "state:change"];
|
|
77865
|
+
function isRecord$1(value) {
|
|
77866
|
+
return typeof value === "object" && value !== null;
|
|
77867
|
+
}
|
|
77868
|
+
function toApiError$1(error) {
|
|
77869
|
+
if (isRecord$1(error) && typeof error["code"] === "string" && typeof error["message"] === "string") {
|
|
77870
|
+
return {
|
|
77871
|
+
code: error["code"],
|
|
77872
|
+
message: error["message"],
|
|
77873
|
+
status: typeof error["status"] === "number" ? error["status"] : void 0
|
|
77874
|
+
};
|
|
77875
|
+
}
|
|
77876
|
+
return {
|
|
77877
|
+
code: "unknown_error",
|
|
77878
|
+
message: error instanceof Error ? error.message : "Failed to load concurrency status"
|
|
77879
|
+
};
|
|
77880
|
+
}
|
|
77881
|
+
function useConcurrencyStatus() {
|
|
77882
|
+
const [loading, setLoading] = reactExports.useState(true);
|
|
77883
|
+
const [data, setData] = reactExports.useState(null);
|
|
77884
|
+
const [error, setError] = reactExports.useState(null);
|
|
77885
|
+
const abortRef = reactExports.useRef(null);
|
|
77886
|
+
const dataRef = reactExports.useRef(null);
|
|
77887
|
+
const mountedRef = reactExports.useRef(true);
|
|
77888
|
+
const refetchTimerRef = reactExports.useRef(null);
|
|
77889
|
+
const load = reactExports.useCallback((showLoading = false) => {
|
|
77890
|
+
abortRef.current?.abort();
|
|
77891
|
+
const controller = new AbortController();
|
|
77892
|
+
abortRef.current = controller;
|
|
77893
|
+
if (showLoading || dataRef.current === null) setLoading(true);
|
|
77894
|
+
void fetchConcurrencyStatus(controller.signal).then((status) => {
|
|
77895
|
+
if (!mountedRef.current || abortRef.current !== controller) return;
|
|
77896
|
+
dataRef.current = status;
|
|
77897
|
+
setData(status);
|
|
77898
|
+
setError(null);
|
|
77899
|
+
}).catch((fetchError) => {
|
|
77900
|
+
if (fetchError instanceof DOMException && fetchError.name === "AbortError") return;
|
|
77901
|
+
if (!mountedRef.current || abortRef.current !== controller) return;
|
|
77902
|
+
setError(toApiError$1(fetchError));
|
|
77903
|
+
}).finally(() => {
|
|
77904
|
+
if (!mountedRef.current || abortRef.current !== controller) return;
|
|
77905
|
+
setLoading(false);
|
|
77906
|
+
});
|
|
77907
|
+
}, []);
|
|
77908
|
+
reactExports.useEffect(() => {
|
|
77909
|
+
mountedRef.current = true;
|
|
77910
|
+
load(true);
|
|
77911
|
+
const source2 = new EventSource("/api/events");
|
|
77912
|
+
const onMessage = () => {
|
|
77913
|
+
if (refetchTimerRef.current) return;
|
|
77914
|
+
refetchTimerRef.current = setTimeout(() => {
|
|
77915
|
+
refetchTimerRef.current = null;
|
|
77916
|
+
load(false);
|
|
77917
|
+
}, 0);
|
|
77918
|
+
};
|
|
77919
|
+
for (const eventName of REFETCH_EVENTS$1) {
|
|
77920
|
+
source2.addEventListener(eventName, onMessage);
|
|
77921
|
+
}
|
|
77922
|
+
source2.onerror = () => {
|
|
77923
|
+
if (!mountedRef.current) return;
|
|
77924
|
+
setError({
|
|
77925
|
+
code: "network_error",
|
|
77926
|
+
message: "Live concurrency updates disconnected"
|
|
77927
|
+
});
|
|
77928
|
+
};
|
|
77929
|
+
return () => {
|
|
77930
|
+
mountedRef.current = false;
|
|
77931
|
+
if (refetchTimerRef.current) {
|
|
77932
|
+
clearTimeout(refetchTimerRef.current);
|
|
77933
|
+
refetchTimerRef.current = null;
|
|
77934
|
+
}
|
|
77935
|
+
abortRef.current?.abort();
|
|
77936
|
+
source2.close();
|
|
77937
|
+
};
|
|
77938
|
+
}, [load]);
|
|
77939
|
+
const refetch = reactExports.useCallback(() => {
|
|
77940
|
+
load(true);
|
|
77941
|
+
}, [load]);
|
|
77942
|
+
return { loading, data, error, refetch };
|
|
77943
|
+
}
|
|
77781
77944
|
function isRecord(value) {
|
|
77782
77945
|
return typeof value === "object" && value !== null;
|
|
77783
77946
|
}
|
|
@@ -78081,6 +78244,119 @@ function JobTable({
|
|
|
78081
78244
|
}) })
|
|
78082
78245
|
] }) });
|
|
78083
78246
|
}
|
|
78247
|
+
const STALE_REASON_LABELS = {
|
|
78248
|
+
missing_current_job: "Missing current job directory",
|
|
78249
|
+
missing_pid: "Lease missing PID past timeout",
|
|
78250
|
+
dead_pid: "Process no longer running",
|
|
78251
|
+
invalid_json: "Lease file is not valid JSON"
|
|
78252
|
+
};
|
|
78253
|
+
function formatTimestamp(value) {
|
|
78254
|
+
if (!value) return "—";
|
|
78255
|
+
const ms = Date.parse(value);
|
|
78256
|
+
if (Number.isNaN(ms)) return value;
|
|
78257
|
+
return new Date(ms).toLocaleString();
|
|
78258
|
+
}
|
|
78259
|
+
function CapacityMetrics({ status }) {
|
|
78260
|
+
const cells = [
|
|
78261
|
+
{ label: "Limit", value: status.limit },
|
|
78262
|
+
{ label: "Running", value: status.runningCount },
|
|
78263
|
+
{ label: "Available", value: status.availableSlots },
|
|
78264
|
+
{ label: "Queued", value: status.queuedCount }
|
|
78265
|
+
];
|
|
78266
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx("dl", { className: "mb-6 grid grid-cols-2 gap-px overflow-hidden rounded-md border border-gray-200 bg-gray-200 sm:grid-cols-4", children: cells.map((cell2) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-white px-4 py-3", children: [
|
|
78267
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("dt", { className: "text-xs text-gray-500", children: cell2.label }),
|
|
78268
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("dd", { className: "text-2xl tabular-nums text-gray-900", children: cell2.value })
|
|
78269
|
+
] }, cell2.label)) });
|
|
78270
|
+
}
|
|
78271
|
+
function ActiveJobsTable({ jobs }) {
|
|
78272
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("section", { className: "mb-6", children: [
|
|
78273
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("h2", { className: "mb-2 text-sm font-medium text-gray-700", children: [
|
|
78274
|
+
"Active jobs (",
|
|
78275
|
+
jobs.length,
|
|
78276
|
+
")"
|
|
78277
|
+
] }),
|
|
78278
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "overflow-hidden rounded-md border border-gray-200 bg-white", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("table", { className: "min-w-full divide-y divide-gray-200", children: [
|
|
78279
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("thead", { className: "bg-gray-50 text-left text-sm font-medium text-gray-500", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("tr", { children: [
|
|
78280
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("th", { scope: "col", className: "px-4 py-2", children: "Job ID" }),
|
|
78281
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("th", { scope: "col", className: "px-4 py-2", children: "Source" }),
|
|
78282
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("th", { scope: "col", className: "px-4 py-2 text-right", children: "PID" }),
|
|
78283
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("th", { scope: "col", className: "px-4 py-2", children: "Acquired" })
|
|
78284
|
+
] }) }),
|
|
78285
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("tbody", { className: "divide-y divide-gray-200 text-sm text-gray-700", children: jobs.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("tr", { children: /* @__PURE__ */ jsxRuntimeExports.jsx("td", { colSpan: 4, className: "px-4 py-3 text-gray-500", children: "No active jobs." }) }) : jobs.map((job) => /* @__PURE__ */ jsxRuntimeExports.jsxs("tr", { children: [
|
|
78286
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-4 py-2 font-mono text-xs text-gray-900", children: job.jobId }),
|
|
78287
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-4 py-2", children: job.source }),
|
|
78288
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-4 py-2 text-right tabular-nums", children: job.pid ?? "—" }),
|
|
78289
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-4 py-2 tabular-nums text-gray-600", children: formatTimestamp(job.acquiredAt) })
|
|
78290
|
+
] }, job.jobId)) })
|
|
78291
|
+
] }) })
|
|
78292
|
+
] });
|
|
78293
|
+
}
|
|
78294
|
+
function QueuedJobsTable({ jobs }) {
|
|
78295
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("section", { className: "mb-6", children: [
|
|
78296
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("h2", { className: "mb-2 text-sm font-medium text-gray-700", children: [
|
|
78297
|
+
"Queued jobs (",
|
|
78298
|
+
jobs.length,
|
|
78299
|
+
")"
|
|
78300
|
+
] }),
|
|
78301
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "overflow-hidden rounded-md border border-gray-200 bg-white", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("table", { className: "min-w-full divide-y divide-gray-200", children: [
|
|
78302
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("thead", { className: "bg-gray-50 text-left text-sm font-medium text-gray-500", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("tr", { children: [
|
|
78303
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("th", { scope: "col", className: "px-4 py-2", children: "Job ID" }),
|
|
78304
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("th", { scope: "col", className: "px-4 py-2", children: "Name" }),
|
|
78305
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("th", { scope: "col", className: "px-4 py-2", children: "Pipeline" }),
|
|
78306
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("th", { scope: "col", className: "px-4 py-2", children: "Queued" })
|
|
78307
|
+
] }) }),
|
|
78308
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("tbody", { className: "divide-y divide-gray-200 text-sm text-gray-700", children: jobs.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("tr", { children: /* @__PURE__ */ jsxRuntimeExports.jsx("td", { colSpan: 4, className: "px-4 py-3 text-gray-500", children: "No queued jobs." }) }) : jobs.map((job) => /* @__PURE__ */ jsxRuntimeExports.jsxs("tr", { children: [
|
|
78309
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-4 py-2 font-mono text-xs text-gray-900", children: job.jobId }),
|
|
78310
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-4 py-2", children: job.name ?? "—" }),
|
|
78311
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-4 py-2", children: job.pipeline ?? "—" }),
|
|
78312
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-4 py-2 tabular-nums text-gray-600", children: formatTimestamp(job.queuedAt) })
|
|
78313
|
+
] }, job.jobId)) })
|
|
78314
|
+
] }) })
|
|
78315
|
+
] });
|
|
78316
|
+
}
|
|
78317
|
+
function StaleSlotWarnings({ slots }) {
|
|
78318
|
+
if (slots.length === 0) return null;
|
|
78319
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("section", { className: "mb-6 rounded-sm border-l-[3px] border-l-yellow-600 bg-yellow-50", children: [
|
|
78320
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("h2", { className: "px-4 pt-3 text-sm font-medium text-yellow-800", children: [
|
|
78321
|
+
"Stale slots (",
|
|
78322
|
+
slots.length,
|
|
78323
|
+
")"
|
|
78324
|
+
] }),
|
|
78325
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("table", { className: "min-w-full text-sm text-yellow-900", children: [
|
|
78326
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("thead", { className: "text-left text-xs font-medium text-yellow-700", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("tr", { children: [
|
|
78327
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("th", { scope: "col", className: "px-4 py-2", children: "Job ID" }),
|
|
78328
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("th", { scope: "col", className: "px-4 py-2", children: "Reason" })
|
|
78329
|
+
] }) }),
|
|
78330
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("tbody", { children: slots.map((slot) => /* @__PURE__ */ jsxRuntimeExports.jsxs("tr", { children: [
|
|
78331
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-4 py-2 font-mono text-xs", children: slot.jobId }),
|
|
78332
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-4 py-2", children: STALE_REASON_LABELS[slot.reason] })
|
|
78333
|
+
] }, slot.jobId)) })
|
|
78334
|
+
] })
|
|
78335
|
+
] });
|
|
78336
|
+
}
|
|
78337
|
+
function ConcurrencyPanel() {
|
|
78338
|
+
const { data, error, loading } = useConcurrencyStatus();
|
|
78339
|
+
if (error && !data) {
|
|
78340
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "rounded-sm border-l-[3px] border-l-yellow-600 bg-yellow-100 p-3 text-sm text-yellow-700", children: [
|
|
78341
|
+
"Unable to load concurrency status: ",
|
|
78342
|
+
error.message
|
|
78343
|
+
] });
|
|
78344
|
+
}
|
|
78345
|
+
if (loading && !data) {
|
|
78346
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-sm text-gray-500", children: "Loading concurrency status…" });
|
|
78347
|
+
}
|
|
78348
|
+
if (!data) return null;
|
|
78349
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
|
|
78350
|
+
error ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-4 rounded-sm border-l-[3px] border-l-yellow-600 bg-yellow-50 p-3 text-sm text-yellow-700", children: [
|
|
78351
|
+
"Showing last known concurrency status: ",
|
|
78352
|
+
error.message
|
|
78353
|
+
] }) : null,
|
|
78354
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(CapacityMetrics, { status: data }),
|
|
78355
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(StaleSlotWarnings, { slots: data.staleSlots }),
|
|
78356
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(ActiveJobsTable, { jobs: data.activeJobs }),
|
|
78357
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(QueuedJobsTable, { jobs: data.queuedJobs })
|
|
78358
|
+
] });
|
|
78359
|
+
}
|
|
78084
78360
|
function PromptPipelineDashboard() {
|
|
78085
78361
|
const navigate = useNavigate();
|
|
78086
78362
|
const hookResult = useJobListWithUpdates();
|
|
@@ -78101,6 +78377,19 @@ function PromptPipelineDashboard() {
|
|
|
78101
78377
|
);
|
|
78102
78378
|
const runningJobs = grouped.current;
|
|
78103
78379
|
const aggregateProgress = runningJobs.length === 0 ? 0 : Math.round(runningJobs.reduce((sum, job) => sum + job.progress, 0) / runningJobs.length);
|
|
78380
|
+
const tabLabels = {
|
|
78381
|
+
current: "Current",
|
|
78382
|
+
errors: "Errors",
|
|
78383
|
+
complete: "Complete",
|
|
78384
|
+
concurrency: "Concurrency"
|
|
78385
|
+
};
|
|
78386
|
+
const tabCounts = {
|
|
78387
|
+
current: grouped.current.length,
|
|
78388
|
+
errors: grouped.errors.length,
|
|
78389
|
+
complete: grouped.complete.length,
|
|
78390
|
+
concurrency: null
|
|
78391
|
+
};
|
|
78392
|
+
const tabText = (tab2) => `${tabLabels[tab2]}${tabCounts[tab2] === null ? "" : ` (${tabCounts[tab2]})`}`;
|
|
78104
78393
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
78105
78394
|
Layout,
|
|
78106
78395
|
{
|
|
@@ -78119,22 +78408,17 @@ function PromptPipelineDashboard() {
|
|
|
78119
78408
|
children: [
|
|
78120
78409
|
error ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mb-4 rounded-sm border-l-[3px] border-l-yellow-600 bg-yellow-100 p-3 text-sm text-yellow-700", children: "Unable to load jobs from the server" }) : null,
|
|
78121
78410
|
/* @__PURE__ */ jsxRuntimeExports.jsx(HintBanner, { storageKey: "dashboard-hint", title: "Upload a seed file to get started.", variant: "action", children: "Your first pipeline takes about 2 minutes to complete." }),
|
|
78122
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mb-4 flex flex-wrap gap-2", children: ["current", "errors", "complete"].map((tab2) => /* @__PURE__ */ jsxRuntimeExports.
|
|
78411
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mb-4 flex flex-wrap gap-2", children: ["current", "errors", "complete", "concurrency"].map((tab2) => /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
78123
78412
|
"button",
|
|
78124
78413
|
{
|
|
78125
78414
|
type: "button",
|
|
78126
78415
|
className: `px-4 py-2 text-sm ${activeTab === tab2 ? "text-[#6d28d9] font-medium border-b-2 border-[#6d28d9]" : "text-gray-500 hover:text-gray-900"}`,
|
|
78127
78416
|
onClick: () => setActiveTab(tab2),
|
|
78128
|
-
children:
|
|
78129
|
-
tab2 === "complete" ? "Complete" : `${tab2.charAt(0).toUpperCase()}${tab2.slice(1)}`,
|
|
78130
|
-
" (",
|
|
78131
|
-
grouped[tab2].length,
|
|
78132
|
-
")"
|
|
78133
|
-
]
|
|
78417
|
+
children: tabText(tab2)
|
|
78134
78418
|
},
|
|
78135
78419
|
tab2
|
|
78136
78420
|
)) }),
|
|
78137
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(JobTable, { jobs: grouped[activeTab], pipeline: null, onOpenJob: (jobId) => navigate(`/pipeline/${jobId}`) })
|
|
78421
|
+
activeTab === "concurrency" ? /* @__PURE__ */ jsxRuntimeExports.jsx(ConcurrencyPanel, {}) : /* @__PURE__ */ jsxRuntimeExports.jsx(JobTable, { jobs: grouped[activeTab], pipeline: null, onOpenJob: (jobId) => navigate(`/pipeline/${jobId}`) })
|
|
78138
78422
|
]
|
|
78139
78423
|
}
|
|
78140
78424
|
);
|