@zq-silk/yui 2.17.0 → 2.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/commandCatalog.js +2 -1
- package/dist/commands/controllerCommands.js +13 -4
- package/dist/commands/durableJobCommands.js +50 -3
- package/dist/commands/globalRoleCommands.js +18 -12
- package/dist/commands/inboxCommands.js +1 -0
- package/dist/commands/operatorCommands.js +10 -2
- package/dist/controlPlaneCli.js +21 -4
- package/dist/controller/clientRuntime.js +34 -13
- package/dist/controller/controller.js +35 -22
- package/dist/controller/diagnosticJournal.js +214 -0
- package/dist/controller/fileSchedulerStoreAdapter.js +139 -101
- package/dist/controller/globalInputDelivery.js +6 -2
- package/dist/controller/globalRuntimeAttention.js +11 -3
- package/dist/controller/jobControl.js +1 -1
- package/dist/controller/jobDefinitionControl.js +24 -13
- package/dist/controller/jobSupervisor.js +4 -0
- package/dist/controller/runtime.js +66 -4
- package/dist/controller/workerSchedulerStore.js +24 -0
- package/dist/core/boundedRpc.js +4 -0
- package/dist/core/controllerServer.js +1 -1
- package/dist/core/controllerTelemetry.js +92 -19
- package/dist/job/globalShell.js +51 -0
- package/dist/job/jobBusinessResult.js +4 -3
- package/dist/job/jobDefinition.js +6 -1
- package/dist/job/jobRunner.js +9 -0
- package/dist/job/scheduleService.js +2 -0
- package/dist/message/globalInboxRecovery.js +30 -19
- package/dist/message/globalProviderRetry.js +7 -1
- package/dist/runtime/builtinAgentErrorMappers.js +4 -0
- package/dist/runtime/globalSessionHealth.js +39 -0
- package/dist/scheduler/roleRunStall.js +10 -10
- package/dist/storage/persistenceWorker.js +41 -19
- package/dist/storage/sqliteSchema.js +35 -11
- package/dist/storage/sqliteStore.js +86 -8
- package/dist/storage/storageVersions.js +1 -1
- package/dist/storage/storeRpc.js +25 -11
- package/dist/storage/taskReadRevisionSchema.js +39 -0
- package/dist/web/assets/client/app/catalog.js +0 -29
- package/dist/web/assets/client/app/jobs.js +115 -0
- package/dist/web/assets/client/app/main.js +17 -6
- package/dist/web/assets/client/app/manage.js +1 -0
- package/dist/web/assets/client/app/settings.js +1 -5
- package/dist/web/assets/client/i18n/en.js +203 -18
- package/dist/web/assets/client/i18n/zh-CN.js +202 -17
- package/dist/web/assets/client/index.js +2 -0
- package/dist/web/assets/client/layout/manageShell.js +61 -15
- package/dist/web/assets/client/layout/router.js +2 -1
- package/dist/web/assets/client/lib/prefs.js +1 -1
- package/dist/web/assets/client/views/jobs.js +1042 -160
- package/dist/web/assets/client/views/sidebar.js +3 -0
- package/dist/web/assets/shared/manageNav.js +3 -1
- package/dist/web/assets/shell/appRail.js +2 -1
- package/dist/web/assets/shell/manage.js +7 -2
- package/dist/web/assets/shell/models.js +4 -3
- package/dist/web/assets/shell/settingsContent.js +0 -3
- package/dist/web/assets/shell/sidebar.js +0 -1
- package/dist/web/assets/styles/views/jobs.js +301 -12
- package/dist/web/assets/styles/views/sidebar.js +1 -2
- package/dist/web/webServer.js +28 -1
- package/docs/global-shell-jobs.md +78 -0
- package/docs/job-runs.md +6 -3
- package/docs/managed-turn-and-session-runtime.md +74 -0
- package/docs/managed-turn-and-session-runtime.zh-CN.md +51 -0
- package/docs/release-workflow.md +43 -0
- package/docs/release-workflow.zh-CN.md +29 -0
- package/docs/storage-baseline.md +14 -1
- package/docs/storage-baseline.zh-CN.md +6 -1
- package/docs/testing/verification-levels.md +2 -2
- package/docs/testing/verification-levels.zh-CN.md +2 -2
- package/package.json +1 -1
|
@@ -16,22 +16,30 @@ export function manageArea(page, section) {
|
|
|
16
16
|
export function pageSections(page) {
|
|
17
17
|
return Object.values(SECTIONS).flat().filter(item => item.page === page).map(item => item.section);
|
|
18
18
|
}
|
|
19
|
+
const TASK_ID = /^task-\d+$/;
|
|
19
20
|
export function readManageRoute(url) {
|
|
20
21
|
const parts = url.pathname.split("/").filter(Boolean);
|
|
22
|
+
// Jobs has its own entry. The retired Settings section keeps its links
|
|
23
|
+
// working: it opens the same Job (canonicalized without a Back step).
|
|
24
|
+
const legacyJobs = parts[0] === "settings" && (parts[1] || url.searchParams.get("section")) === "jobs";
|
|
25
|
+
if (parts[0] === "jobs" || legacyJobs) {
|
|
26
|
+
const task = url.searchParams.get("task");
|
|
27
|
+
return { page: "jobs", section: "jobs", task: task && TASK_ID.test(task) ? task : null,
|
|
28
|
+
job: url.searchParams.get("job"), jobRun: url.searchParams.get("jobRun"),
|
|
29
|
+
artifact: url.searchParams.get("artifact"), ...(legacyJobs ? { moved: true } : {}) };
|
|
30
|
+
}
|
|
21
31
|
const area = parts[0] === "settings" ? "settings" : "models";
|
|
22
32
|
const requested = parts[1] || url.searchParams.get("section");
|
|
23
33
|
const section = (SECTIONS[area].find(item => item.section === requested) ||
|
|
24
34
|
(area === "models" && requested === "connection" ? SECTIONS.settings.at(-1) : SECTIONS[area][0])).section;
|
|
25
35
|
return { page: section === "connection" ? "models" : area, section,
|
|
26
|
-
...(section === "jobs" ? { job: url.searchParams.get("job"), jobRun: url.searchParams.get("jobRun"),
|
|
27
|
-
artifact: url.searchParams.get("artifact") } : {}),
|
|
28
36
|
source: url.searchParams.get("source") === "command-code" ? "command-code" : "factory" };
|
|
29
37
|
}
|
|
30
38
|
export function manageHref(next) {
|
|
31
|
-
if (next.
|
|
39
|
+
if (next.page === "jobs") {
|
|
32
40
|
const query = new URLSearchParams();
|
|
33
|
-
for (const key of ["job", "jobRun", "artifact"]) if (next[key]) query.set(key, next[key]);
|
|
34
|
-
return "/
|
|
41
|
+
for (const key of ["task", "job", "jobRun", "artifact"]) if (next[key]) query.set(key, next[key]);
|
|
42
|
+
return "/jobs" + (query.size ? "?" + query : "");
|
|
35
43
|
}
|
|
36
44
|
return "/" + manageArea(next.page, next.section) + "/" + next.section +
|
|
37
45
|
(next.section === "quota" && next.source === "command-code" ? "?source=command-code" : "");
|
|
@@ -53,11 +61,15 @@ function render() {
|
|
|
53
61
|
if (link.dataset.area === area) link.setAttribute("aria-current", "page"); else link.removeAttribute("aria-current");
|
|
54
62
|
});
|
|
55
63
|
// The list head and the top bar name where the reader is.
|
|
56
|
-
const areaTitle = i18n.t(area + ".title");
|
|
57
|
-
const
|
|
64
|
+
const areaTitle = i18n.t(area === "jobs" ? "nav.jobs" : area + ".title");
|
|
65
|
+
const page = pages.get(route.page);
|
|
66
|
+
const sectionTitle = page && page.crumb ? page.crumb()
|
|
67
|
+
: i18n.t((route.page === "models" ? "models" : "settings") + ".section." + route.section);
|
|
58
68
|
document.querySelector("#manage-area-title").textContent = areaTitle;
|
|
59
69
|
document.querySelector("#manage-crumb-area").textContent = areaTitle;
|
|
60
70
|
document.querySelector("#manage-crumb-section").textContent = sectionTitle;
|
|
71
|
+
// Area-only controls in the shared list head (the Jobs sync and refresh).
|
|
72
|
+
rail.querySelectorAll("[data-area-only]").forEach(node => { node.hidden = node.dataset.areaOnly !== area; });
|
|
61
73
|
rail.querySelectorAll("[data-area-group]").forEach(group => {
|
|
62
74
|
if (group.dataset.areaGroup === area) group.dataset.active = "true"; else delete group.dataset.active;
|
|
63
75
|
});
|
|
@@ -65,27 +77,60 @@ function render() {
|
|
|
65
77
|
document.title = sectionTitle + " · " + areaTitle + " · Yui";
|
|
66
78
|
}
|
|
67
79
|
function apply(next) {
|
|
80
|
+
// The page being left is told first, so it can set aside what must not
|
|
81
|
+
// outlive its visibility (a modal editor) without discarding it.
|
|
82
|
+
const previous = route && route.page !== next.page ? pages.get(route.page) : null;
|
|
83
|
+
if (previous && previous.hide) previous.hide();
|
|
68
84
|
route = next;
|
|
69
85
|
rememberPlace(manageArea(next.page, next.section), manageHref(next));
|
|
70
86
|
rememberPlace("manage", manageHref(next));
|
|
71
|
-
document.querySelector("#
|
|
72
|
-
document.querySelector("#settings-root").hidden = next.page !== "settings";
|
|
87
|
+
for (const name of ["models", "settings", "jobs"]) document.querySelector("#" + name + "-root").hidden = next.page !== name;
|
|
73
88
|
pages.get(next.page).show(next);
|
|
74
89
|
render();
|
|
75
90
|
}
|
|
76
91
|
// A section the reader switched to rises in (never on the first paint).
|
|
77
92
|
function revealSection() {
|
|
78
93
|
if (typeof enter !== "function") return;
|
|
79
|
-
enter(document.querySelector("#models-root:not([hidden]), #settings-root:not([hidden])"), "rise");
|
|
94
|
+
enter(document.querySelector("#models-root:not([hidden]), #settings-root:not([hidden]), #jobs-root:not([hidden])"), "rise");
|
|
95
|
+
}
|
|
96
|
+
// Every route change (link, control, Back/Forward) first asks the page it
|
|
97
|
+
// leaves and the page it enters; either may keep the current view.
|
|
98
|
+
function mayNavigate(next) {
|
|
99
|
+
for (const name of new Set([route.page, next.page])) {
|
|
100
|
+
const page = pages.get(name);
|
|
101
|
+
if (page && page.canNavigate && !page.canNavigate(next)) return false;
|
|
102
|
+
}
|
|
103
|
+
return true;
|
|
80
104
|
}
|
|
105
|
+
// Returns false when a page kept the current view (nothing changed).
|
|
81
106
|
export function navigateManage(next) {
|
|
82
107
|
const normalized = readManageRoute(new URL(manageHref(next), location.origin));
|
|
83
|
-
if (manageHref(normalized) === location.pathname + location.search) return;
|
|
108
|
+
if (manageHref(normalized) === location.pathname + location.search) return true;
|
|
109
|
+
if (!mayNavigate(normalized)) return false;
|
|
84
110
|
history.replaceState({ ...history.state, scroll: scroller().scrollTop }, "");
|
|
85
111
|
history.pushState({ scroll: 0 }, "", manageHref(normalized));
|
|
86
112
|
apply(normalized);
|
|
87
113
|
scroller().scrollTop = 0;
|
|
88
114
|
revealSection();
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
// A page settled on a more exact address for the current view (for example
|
|
118
|
+
// the Job it opened): record it in place, without a history step.
|
|
119
|
+
export function replaceManageRoute(next) {
|
|
120
|
+
route = next;
|
|
121
|
+
rememberPlace(manageArea(next.page, next.section), manageHref(next));
|
|
122
|
+
rememberPlace("manage", manageHref(next));
|
|
123
|
+
history.replaceState(history.state, "", manageHref(next));
|
|
124
|
+
render();
|
|
125
|
+
}
|
|
126
|
+
// A page already shows a selection the reader chose in place (for example a
|
|
127
|
+
// Run opened under its row): record it as a history step without applying it
|
|
128
|
+
// again, keeping the reading position for Back.
|
|
129
|
+
export function pushManageRoute(next) {
|
|
130
|
+
if (manageHref(next) === location.pathname + location.search) return;
|
|
131
|
+
history.replaceState({ ...history.state, scroll: scroller().scrollTop }, "");
|
|
132
|
+
history.pushState({ scroll: scroller().scrollTop }, "", manageHref(next));
|
|
133
|
+
replaceManageRoute(next);
|
|
89
134
|
}
|
|
90
135
|
export function createManageShell(options) {
|
|
91
136
|
return {
|
|
@@ -108,14 +153,15 @@ export function startManagement() {
|
|
|
108
153
|
if (!link || link.target || link.hasAttribute("download") || event.defaultPrevented ||
|
|
109
154
|
event.button > 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
|
|
110
155
|
const url = new URL(link.href);
|
|
111
|
-
|
|
112
|
-
if (url.searchParams.has("job")) return;
|
|
113
|
-
if (url.origin !== location.origin || !/^\/(models|settings)(\/|$)/.test(url.pathname)) return;
|
|
156
|
+
if (url.origin !== location.origin || !/^\/(models|settings|jobs)(\/|$)/.test(url.pathname)) return;
|
|
114
157
|
event.preventDefault();
|
|
115
158
|
navigateManage(readManageRoute(url));
|
|
116
159
|
});
|
|
117
160
|
window.addEventListener("popstate", () => {
|
|
118
|
-
|
|
161
|
+
const next = readManageRoute(new URL(location.href));
|
|
162
|
+
// A kept view restores its own address as the newest entry.
|
|
163
|
+
if (!mayNavigate(next)) { history.pushState({ scroll: scroller().scrollTop }, "", manageHref(route)); return; }
|
|
164
|
+
apply(next);
|
|
119
165
|
scroller().scrollTop = history.state?.scroll || 0;
|
|
120
166
|
revealSection();
|
|
121
167
|
});
|
|
@@ -23,7 +23,8 @@ export function syncUrl(state, replace) {
|
|
|
23
23
|
if (state.query) params.set("q", state.query); else params.delete("q");
|
|
24
24
|
if (state.attentionFilter) params.set("attention", state.attentionFilter); else params.delete("attention");
|
|
25
25
|
if (state.catalogAll) params.set("all", "true"); else params.delete("all");
|
|
26
|
-
|
|
26
|
+
// Tasks have no category filter: every real Task, Job-created ones marked.
|
|
27
|
+
params.delete("source");
|
|
27
28
|
applyUrl(params, replace);
|
|
28
29
|
}
|
|
29
30
|
|
|
@@ -18,7 +18,7 @@ export function clearPreference(key) {
|
|
|
18
18
|
// switching back restores the filters, Task or section the user left. Only
|
|
19
19
|
// same-origin paths of the expected area are accepted; anything else falls back.
|
|
20
20
|
const PLACES = { workbench: /^\/(?:\?|$)/, models: /^\/models(?:[/?]|$)/, settings: /^\/settings(?:[/?]|$)/,
|
|
21
|
-
manage: /^\/(?:models|settings)(?:[/?]|$)/ };
|
|
21
|
+
jobs: /^\/jobs(?:[/?]|$)/, manage: /^\/(?:jobs|models|settings)(?:[/?]|$)/ };
|
|
22
22
|
|
|
23
23
|
export function rememberPlace(area, href) {
|
|
24
24
|
if (!PLACES[area] || !PLACES[area].test(href)) return;
|