@zq-silk/yui 2.17.0 → 2.19.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 +14 -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 +45 -22
- package/dist/controller/diagnosticJournal.js +214 -0
- package/dist/controller/fileSchedulerStoreAdapter.js +143 -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/messageHealth.js +354 -0
- package/dist/controller/runtime.js +75 -5
- package/dist/controller/taskHostTerminalRecovery.js +11 -0
- 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/contextRecords.js +16 -0
- package/dist/storage/persistenceWorker.js +41 -19
- package/dist/storage/sqliteSchema.js +35 -11
- package/dist/storage/sqliteStore.js +102 -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 +170 -0
- package/docs/managed-turn-and-session-runtime.zh-CN.md +116 -0
- package/docs/release-workflow.md +63 -0
- package/docs/release-workflow.zh-CN.md +44 -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
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
export const JOBS_APP_SCRIPT = String.raw `
|
|
2
|
+
import { h, icon } from "/assets/js/lib/dom.js";
|
|
3
|
+
import { formatClock } from "/assets/js/lib/format.js";
|
|
4
|
+
import { requestJson } from "/assets/js/lib/api.js";
|
|
5
|
+
import { i18n, registerManagePage, navigateManage, replaceManageRoute, pushManageRoute } from "/assets/js/layout/manageShell.js";
|
|
6
|
+
import { mountJobs } from "/assets/js/views/jobs.js";
|
|
7
|
+
|
|
8
|
+
// The Jobs entry: one shared Job module (the same one the Task page uses)
|
|
9
|
+
// over exactly one scope at a time, global or one owning Task. The scope,
|
|
10
|
+
// the selected Job and Run live in the URL, so deep links, refresh and
|
|
11
|
+
// Back/Forward all reopen the same view. One view is live at a time: a
|
|
12
|
+
// replaced view is retired, so its late reads cannot write this page.
|
|
13
|
+
const t = i18n.t;
|
|
14
|
+
const root = document.querySelector("#jobs-root");
|
|
15
|
+
const index = document.querySelector("#jobs-index");
|
|
16
|
+
const scopeSelect = h("select", { id: "jobs-scope", "aria-describedby": "jobs-scope-help" });
|
|
17
|
+
const scopeLabel = h("label", { for: "jobs-scope" });
|
|
18
|
+
const scopeHelp = h("p.jobs-scope-help", { id: "jobs-scope-help" });
|
|
19
|
+
const listMount = h("div.jobs-index-body");
|
|
20
|
+
// The list head works as on the workbench: one primary "new" action at the
|
|
21
|
+
// top of the list (creating in the selected scope), the last read time and
|
|
22
|
+
// a refresh control in the head. The page itself carries no second title.
|
|
23
|
+
const createLabel = h("span");
|
|
24
|
+
const create = h("button.btn.btn-primary.side-create", { id: "jobs-create", type: "button",
|
|
25
|
+
onclick: () => { if (view) view.create(); } }, icon("plus"), createLabel);
|
|
26
|
+
const sync = document.querySelector("#jobs-sync");
|
|
27
|
+
const lastSync = document.querySelector("#jobs-last-sync");
|
|
28
|
+
const refreshButton = document.querySelector("#jobs-refresh");
|
|
29
|
+
if (refreshButton) refreshButton.addEventListener("click", () => { if (view && !view.isBusy()) void view.refresh(); });
|
|
30
|
+
index.replaceChildren(create, h("div.jobs-scope", null, scopeLabel, scopeSelect, scopeHelp), listMount);
|
|
31
|
+
let route = null, view = null, mountedScope, scopes = null, scopesError = null;
|
|
32
|
+
|
|
33
|
+
scopeSelect.addEventListener("change", () => {
|
|
34
|
+
const task = scopeSelect.value || null;
|
|
35
|
+
if ((route && route.task) === task) return;
|
|
36
|
+
if (!navigateManage({ page: "jobs", section: "jobs", task })) drawScopes();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
function drawScopes() {
|
|
40
|
+
createLabel.textContent = t("jobs.newJob");
|
|
41
|
+
create.title = t("jobs.newJobIn") + " · " + (route && route.task ? route.task : t("jobs.ownerGlobal"));
|
|
42
|
+
scopeLabel.textContent = t("jobs.scopeLabel");
|
|
43
|
+
scopeHelp.textContent = scopesError ? t("jobs.scopesError") + " " + scopesError : t("jobs.scopeHelp");
|
|
44
|
+
scopeHelp.classList.toggle("is-error", !!scopesError);
|
|
45
|
+
const current = route ? route.task : null;
|
|
46
|
+
const tasks = scopes ? scopes.tasks.slice() : [];
|
|
47
|
+
// A deep-linked Task stays selectable even when it has no Job yet.
|
|
48
|
+
if (current && !tasks.some(item => item.taskId === current)) tasks.unshift({ taskId: current, title: "" });
|
|
49
|
+
scopeSelect.replaceChildren(h("option", { value: "" }, t("jobs.ownerGlobal")),
|
|
50
|
+
...tasks.map(item => h("option", { value: item.taskId }, item.taskId + (item.title ? " · " + item.title : ""))));
|
|
51
|
+
scopeSelect.value = current || "";
|
|
52
|
+
}
|
|
53
|
+
async function loadScopes() {
|
|
54
|
+
try { scopes = await requestJson("/api/jobs/scopes"); scopesError = null; }
|
|
55
|
+
catch (error) { scopesError = error.message; }
|
|
56
|
+
drawScopes();
|
|
57
|
+
}
|
|
58
|
+
function mount(next) {
|
|
59
|
+
if (view) view.dispose();
|
|
60
|
+
mountedScope = next.task || null;
|
|
61
|
+
let self = null;
|
|
62
|
+
self = view = mountJobs(root, t, mountedScope || undefined, {
|
|
63
|
+
index: listMount,
|
|
64
|
+
navigate: selection => navigateManage({ ...route, job: selection.job, jobRun: null, artifact: null, moved: undefined }),
|
|
65
|
+
pin: (selection, step) => pin(self, selection, step),
|
|
66
|
+
sync: observation => observe(self, observation),
|
|
67
|
+
notice: next.moved ? t("jobs.moved") : "",
|
|
68
|
+
locale: i18n.getLocale
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
// The last read of the live view, in the list head (as the workbench's sync).
|
|
72
|
+
function observe(owner, observation) {
|
|
73
|
+
if (owner !== view || !sync) return;
|
|
74
|
+
sync.dataset.state = observation.state;
|
|
75
|
+
if (observation.at) lastSync.textContent = formatClock(observation.at, i18n.getLocale());
|
|
76
|
+
sync.title = observation.text;
|
|
77
|
+
}
|
|
78
|
+
// Only the live view, while Jobs is the shown page, records its selection.
|
|
79
|
+
// A Run the reader opened or closed in place is a history step; anything else
|
|
80
|
+
// the view settles on refines the current entry.
|
|
81
|
+
function pin(owner, selection, step) {
|
|
82
|
+
if (owner !== view || root.hidden || !route || route.page !== "jobs") return;
|
|
83
|
+
route = { ...route, ...selection, moved: undefined };
|
|
84
|
+
if (step) pushManageRoute(route); else replaceManageRoute(route);
|
|
85
|
+
}
|
|
86
|
+
function show(next) {
|
|
87
|
+
route = next;
|
|
88
|
+
drawScopes();
|
|
89
|
+
if (!view || mountedScope !== (next.task || null)) mount(next);
|
|
90
|
+
else { view.resume(); view.select(next.job, next.jobRun, next.artifact); }
|
|
91
|
+
}
|
|
92
|
+
// Leaving Jobs sets a modal editor aside (its draft is kept for the return).
|
|
93
|
+
function hide() { if (view) view.suspend(); }
|
|
94
|
+
// Asked before any route change that leaves or enters Jobs, whatever its
|
|
95
|
+
// source. Leaving Jobs keeps its view (hidden) as it is; a change of scope or
|
|
96
|
+
// Job replaces the editor, so a draft needs confirmation; while a write is in
|
|
97
|
+
// flight the selection stays put so its receipt remains observable.
|
|
98
|
+
function canNavigate(next) {
|
|
99
|
+
if (!view || next.page !== "jobs" || !route) return true;
|
|
100
|
+
const same = key => (next[key] || null) === (route[key] || null);
|
|
101
|
+
if ((next.task || null) === mountedScope && same("job") && same("jobRun") && same("artifact")) return true;
|
|
102
|
+
if (view.isBusy() && root.hidden) { window.alert(t("jobs.leaveBusy")); return false; }
|
|
103
|
+
return view.leave((next.task || null) !== mountedScope || !same("job"));
|
|
104
|
+
}
|
|
105
|
+
registerManagePage("jobs", { show, hide, canNavigate, crumb: () => route && route.task ? route.task : t("jobs.ownerGlobal") });
|
|
106
|
+
void loadScopes();
|
|
107
|
+
i18n.subscribe(() => {
|
|
108
|
+
drawScopes();
|
|
109
|
+
if (route && route.page === "jobs" && view && !view.isDirty() && !view.isBusy()) mount({ ...route, moved: false });
|
|
110
|
+
});
|
|
111
|
+
window.addEventListener("beforeunload", event => {
|
|
112
|
+
if (!view || !view.isDirty()) return;
|
|
113
|
+
event.preventDefault(); event.returnValue = "";
|
|
114
|
+
});
|
|
115
|
+
`;
|
|
@@ -18,7 +18,7 @@ export const APP_SCRIPT = String.raw `
|
|
|
18
18
|
// refresh catalog.refresh → sidebar, then the overview (nothing selected)
|
|
19
19
|
// or selection.reload → taskView.render
|
|
20
20
|
// select sidebar row / overview row / URL → selection.selectTask
|
|
21
|
-
// leave rail
|
|
21
|
+
// leave rail Tasks or Escape → selection.clearSelection
|
|
22
22
|
// write Task page or discussion afterWrite → quiet catalog refresh
|
|
23
23
|
// URL load and back/forward → catalog.applyFilters + selection.followUrl
|
|
24
24
|
import { createI18n } from "/assets/js/lib/i18n.js";
|
|
@@ -30,7 +30,7 @@ import { createPreviewController } from "/assets/js/views/dock/preview.js";
|
|
|
30
30
|
import { bindGlobalInput } from "/assets/js/views/globalInput.js";
|
|
31
31
|
import { bindCreateTask } from "/assets/js/views/createTask.js";
|
|
32
32
|
import { bindBodySearch } from "/assets/js/views/bodySearch.js";
|
|
33
|
-
import { readQuery } from "/assets/js/layout/router.js";
|
|
33
|
+
import { readQuery, applyUrl } from "/assets/js/layout/router.js";
|
|
34
34
|
import { rememberPlace, linkPlaces, readSessionAccessMode } from "/assets/js/lib/prefs.js";
|
|
35
35
|
import { createWorkspace } from "/assets/js/layout/workspace.js";
|
|
36
36
|
import { createSidebarNav } from "/assets/js/layout/sidebarNav.js";
|
|
@@ -43,7 +43,7 @@ const $ = function (selector) { return document.querySelector(selector); };
|
|
|
43
43
|
const el = {
|
|
44
44
|
sidebar: $(".sidebar"), rail: $(".app-rail"), railWorkbench: $("#rail-workbench"),
|
|
45
45
|
sidebarScrim: $("#sidebar-scrim"), sidebarCollapse: $("#sidebar-collapse"), sidebarDivider: $("#sidebar-divider"), center: $("#center"), detail: $("#detail"),
|
|
46
|
-
search: $("#search"),
|
|
46
|
+
search: $("#search"), filters: $("#status-filters"), attention: $("#attention-bar"), tasks: $("#task-list"),
|
|
47
47
|
catalogNext: $("#catalog-next"), catalogReset: $("#catalog-reset"), catalogCount: $("#catalog-count"),
|
|
48
48
|
catalogAttentionReset: $("#catalog-attention-reset"),
|
|
49
49
|
refresh: $("#refresh"), sync: $("#sync-state"), lastSync: $("#last-sync"), toast: $("#toast"),
|
|
@@ -119,11 +119,11 @@ bindShortcuts({
|
|
|
119
119
|
el: el, state: state, globalInput: globalInput, workspace: workspace, sidebarNav: sidebarNav,
|
|
120
120
|
selection: selection, catalog: catalog, taskView: taskView
|
|
121
121
|
});
|
|
122
|
-
//
|
|
122
|
+
// Tasks <-> Jobs/Models/Settings: each side remembers where the user was, so a
|
|
123
123
|
// round trip returns to the same filters, Task and management section.
|
|
124
124
|
function rememberWorkbench() { rememberPlace("workbench", window.location.pathname + window.location.search); }
|
|
125
125
|
el.rail.querySelectorAll("a[data-nav-area]").forEach(function (link) { link.addEventListener("click", rememberWorkbench); });
|
|
126
|
-
// The rail's
|
|
126
|
+
// The rail's Tasks entry on the Task workbench itself returns to the overview.
|
|
127
127
|
el.railWorkbench.addEventListener("click", function (event) {
|
|
128
128
|
if (event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
|
|
129
129
|
event.preventDefault();
|
|
@@ -135,9 +135,20 @@ function linkAll() { linkPlaces(el.rail); linkPlaces(el.sidebar); }
|
|
|
135
135
|
window.addEventListener("pageshow", linkAll);
|
|
136
136
|
linkAll();
|
|
137
137
|
|
|
138
|
+
// The retired Task category filter (?source=ordinary|job|all): the Task
|
|
139
|
+
// list now always shows every real Task, so drop the parameter in place
|
|
140
|
+
// (no extra history step) and say why once; Jobs have their own entry.
|
|
141
|
+
let sourceHinted = false;
|
|
142
|
+
function stripRetiredSource(params) {
|
|
143
|
+
if (!params.has("source")) return params;
|
|
144
|
+
params.delete("source");
|
|
145
|
+
applyUrl(params, true);
|
|
146
|
+
if (!sourceHinted) { sourceHinted = true; toast(t("catalog.sourceRemoved")); }
|
|
147
|
+
return params;
|
|
148
|
+
}
|
|
138
149
|
// The URL names the catalog filters and the selected Task (with its section).
|
|
139
150
|
function applyStateFromUrl() {
|
|
140
|
-
const params = readQuery();
|
|
151
|
+
const params = stripRetiredSource(readQuery());
|
|
141
152
|
catalog.applyFilters(params);
|
|
142
153
|
selection.followUrl(params);
|
|
143
154
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export const MANAGE_SCRIPT = String.raw `
|
|
2
2
|
import "/assets/js/app/settings.js";
|
|
3
3
|
import "/assets/js/app/models.js";
|
|
4
|
+
import "/assets/js/app/jobs.js";
|
|
4
5
|
import { startManagement } from "/assets/js/layout/manageShell.js";
|
|
5
6
|
import { createSidebarNav } from "/assets/js/layout/sidebarNav.js";
|
|
6
7
|
import { isEditing } from "/assets/js/lib/dom.js";
|
|
@@ -8,7 +8,6 @@ import { createThemeController } from "/assets/js/lib/theme.js";
|
|
|
8
8
|
import { clearPreference, readSessionAccessMode, writeSessionAccessMode } from "/assets/js/lib/prefs.js";
|
|
9
9
|
import { i18n, createManageShell, pageSections, registerManagePage, navigateManage } from "/assets/js/layout/manageShell.js";
|
|
10
10
|
import { mountSkills } from "/assets/js/views/skills.js";
|
|
11
|
-
import { mountJobs } from "/assets/js/views/jobs.js";
|
|
12
11
|
import { mountPermissions } from "/assets/js/views/permissions.js";
|
|
13
12
|
|
|
14
13
|
const $ = selector => document.querySelector(selector);
|
|
@@ -20,14 +19,12 @@ function readSection() {
|
|
|
20
19
|
return SECTIONS.includes(section) ? section : SECTIONS[0];
|
|
21
20
|
}
|
|
22
21
|
let section = readSection();
|
|
23
|
-
let jobs;
|
|
24
22
|
let permissions;
|
|
25
23
|
const shell = createManageShell({ page: "settings", t, onNavigate: navigate });
|
|
26
24
|
function showSection(next) {
|
|
27
25
|
section = next;
|
|
28
26
|
document.querySelectorAll("#settings-root > .manage-section[data-section]").forEach(panel => { panel.hidden = panel.dataset.section !== next; });
|
|
29
27
|
shell.set(next);
|
|
30
|
-
if (next === "jobs" && !jobs) jobs = mountJobs($("#global-jobs"), t);
|
|
31
28
|
if (next === "permissions" && !permissions) permissions = mountPermissions($("#permission-browser"), t);
|
|
32
29
|
}
|
|
33
30
|
function navigate(next) {
|
|
@@ -41,7 +38,6 @@ function markDirty(name, value) {
|
|
|
41
38
|
registerManagePage("settings", { show: next => showSection(next.section) });
|
|
42
39
|
i18n.subscribe(() => shell.render());
|
|
43
40
|
i18n.subscribe(() => {
|
|
44
|
-
if (jobs && !jobs.isDirty() && !jobs.isBusy()) jobs = mountJobs($("#global-jobs"), t);
|
|
45
41
|
permissions?.translate();
|
|
46
42
|
});
|
|
47
43
|
showSection(section);
|
|
@@ -69,7 +65,7 @@ let searchTimer;
|
|
|
69
65
|
const more = h("button.btn", { type: "button", hidden: true }, t("settings.more"));
|
|
70
66
|
container.after(more);
|
|
71
67
|
window.addEventListener("beforeunload", event => {
|
|
72
|
-
if (!groups.some(g => g.dirty)
|
|
68
|
+
if (!groups.some(g => g.dirty)) return;
|
|
73
69
|
event.preventDefault(); event.returnValue = "";
|
|
74
70
|
});
|
|
75
71
|
|
|
@@ -50,7 +50,6 @@ export const EN = {
|
|
|
50
50
|
"permissions.empty": "No matching templates.",
|
|
51
51
|
"permissions.retry": "Retry read",
|
|
52
52
|
"tabs.jobs": "Jobs",
|
|
53
|
-
"settings.section.jobs": "Jobs & schedules",
|
|
54
53
|
"jobs.builtinSync": "Built-in Project sync · safe fast-forward only",
|
|
55
54
|
"jobs.automatic": "Automatic sync preference",
|
|
56
55
|
"jobs.syncEvery": "Sync interval (milliseconds, minimum 60000)",
|
|
@@ -62,14 +61,52 @@ export const EN = {
|
|
|
62
61
|
"jobs.executor": "Execution method",
|
|
63
62
|
"jobs.executionTask": "Open execution Task",
|
|
64
63
|
"jobs.sourceJob": "Created by Job / Run",
|
|
65
|
-
"jobs
|
|
66
|
-
"jobs.
|
|
67
|
-
"jobs.
|
|
68
|
-
"jobs.
|
|
69
|
-
"jobs.
|
|
70
|
-
"jobs.
|
|
71
|
-
"jobs.
|
|
72
|
-
"jobs.
|
|
64
|
+
"nav.jobs": "Jobs",
|
|
65
|
+
"jobs.scopeLabel": "Owner",
|
|
66
|
+
"jobs.scopeHelp": "Global, or one Task that owns Jobs. Counts, search and the list describe only this scope.",
|
|
67
|
+
"jobs.scopesError": "Owner list unavailable:",
|
|
68
|
+
"jobs.search": "Search Jobs",
|
|
69
|
+
"jobs.count": "{total} Jobs",
|
|
70
|
+
"jobs.countFiltered": "{shown} of {total} Jobs",
|
|
71
|
+
"jobs.noMatch": "No Jobs in this scope match the search.",
|
|
72
|
+
"jobs.noSchedule": "No plan",
|
|
73
|
+
"jobs.noRunsShort": "No Runs",
|
|
74
|
+
"jobs.nextShort": "next",
|
|
75
|
+
"jobs.plan": "Plan",
|
|
76
|
+
"jobs.definition": "Definition",
|
|
77
|
+
"jobs.execution": "Execution",
|
|
78
|
+
"jobs.runVsPlan": "A Run's status describes one execution; it does not turn the plan on or off.",
|
|
79
|
+
"jobs.type": "Type",
|
|
80
|
+
"jobs.type.projectSync": "Project sync",
|
|
81
|
+
"jobs.kind.shell": "Shell command",
|
|
82
|
+
"jobs.form.scripts": "Script files",
|
|
83
|
+
"jobs.form.scriptPath": "Relative path",
|
|
84
|
+
"jobs.form.scriptContent": "Script content",
|
|
85
|
+
"jobs.form.addScript": "Add script",
|
|
86
|
+
"jobs.form.stepOptions": "Timeout and environment",
|
|
87
|
+
"jobs.form.shellHome": "This Job owns its commands and scripts. Each Run uses a version snapshot in a separate directory. Invoke scripts with sh scripts/run.sh. Commands run as the local user, not in a security sandbox; saving does not execute them.",
|
|
88
|
+
"jobs.kind.task": "Task delegation",
|
|
89
|
+
"jobs.kind.agent": "Agent run",
|
|
90
|
+
"jobs.kind.builtin": "Built-in action",
|
|
91
|
+
"jobs.kind.process": "Process",
|
|
92
|
+
"jobs.ownerGlobal": "Global",
|
|
93
|
+
"jobs.ownerTask": "Task",
|
|
94
|
+
"jobs.openInJobs": "Open in Jobs",
|
|
95
|
+
"jobs.moved": "Jobs moved out of Settings into their own entry. This link now opens here.",
|
|
96
|
+
"catalog.sourceJob": "Job",
|
|
97
|
+
"catalog.sourceRemoved": "The Task category filter was removed: the list shows every Task, and Jobs have their own entry.",
|
|
98
|
+
"schedule.enabled": "On",
|
|
99
|
+
"schedule.disabled": "Off",
|
|
100
|
+
"schedule.completed": "Completed",
|
|
101
|
+
"schedule.needs-attention": "Needs attention",
|
|
102
|
+
"schedule.retired": "Retired",
|
|
103
|
+
"admission.admitted": "Admitted",
|
|
104
|
+
"admission.overlap": "Skipped (overlap)",
|
|
105
|
+
"admission.blocked": "Blocked",
|
|
106
|
+
"trigger.manual": "Manual",
|
|
107
|
+
"trigger.schedule": "Scheduled",
|
|
108
|
+
"trigger.retry": "Retry",
|
|
109
|
+
"jobs.recentSuccess": "Last success",
|
|
73
110
|
"jobs.diagnostics": "Execution diagnostics",
|
|
74
111
|
"jobs.artifacts": "Registered fixed artifacts",
|
|
75
112
|
"jobs.registeredOnly": "Only the exact registered owner, Run, commit, path and digest are readable. Linked images must also be registered.",
|
|
@@ -88,7 +125,9 @@ export const EN = {
|
|
|
88
125
|
"jobs.project": "Target Project",
|
|
89
126
|
"jobs.name": "Title",
|
|
90
127
|
"jobs.action": "Controlled action",
|
|
91
|
-
"jobs.newJob": "
|
|
128
|
+
"jobs.newJob": "New Job",
|
|
129
|
+
"jobs.newJobIn": "Creates the Job in",
|
|
130
|
+
"jobs.ownerShort": "Owner",
|
|
92
131
|
"jobs.editJob": "Edit Job",
|
|
93
132
|
"jobs.runNow": "Run now",
|
|
94
133
|
"jobs.newSchedule": "Create schedule",
|
|
@@ -96,10 +135,11 @@ export const EN = {
|
|
|
96
135
|
"jobs.cancelRun": "Cancel this Run",
|
|
97
136
|
"jobs.disable": "Disable future runs",
|
|
98
137
|
"jobs.enable": "Enable schedule",
|
|
99
|
-
"jobs.refresh": "Refresh
|
|
138
|
+
"jobs.refresh": "Refresh",
|
|
100
139
|
"jobs.save": "Save",
|
|
101
140
|
"jobs.close": "Close editor",
|
|
102
141
|
"jobs.discard": "Discard this unsaved draft?",
|
|
142
|
+
"jobs.leaveBusy": "A submission is still in progress; this view stays open until its receipt arrives.",
|
|
103
143
|
"jobs.loading": "Reading authoritative state…",
|
|
104
144
|
"jobs.submitting": "Submitting…",
|
|
105
145
|
"jobs.saved": "Saved. This is not proof of execution or success.",
|
|
@@ -109,17 +149,85 @@ export const EN = {
|
|
|
109
149
|
"jobs.observed": "Facts refreshed",
|
|
110
150
|
"jobs.unknown": "Submission outcome is unknown; it will not be resent. Inspect current facts and the original operation before taking another action.",
|
|
111
151
|
"jobs.empty": "No Jobs in this scope. Creating a Job does not run it.",
|
|
112
|
-
"jobs.configuration": "
|
|
113
|
-
"jobs.taskConfig": "
|
|
114
|
-
"jobs.
|
|
152
|
+
"jobs.configuration": "Configuration",
|
|
153
|
+
"jobs.taskConfig": "Saving changes configuration only; it does not run the Job. Do not include passwords or keys. Execution still requires the relevant permissions and workspace.",
|
|
154
|
+
"jobs.saveAsNew": "Save as new Job",
|
|
155
|
+
"jobs.kindCopyHelp": "You selected a different execution method. Saving creates a new Job. The original Job, history and schedules stay unchanged; schedules are not copied or started.",
|
|
156
|
+
"jobs.rebind": "Saving a Project refresh Job binds its current catalog target. It does not immediately refresh the Project.",
|
|
115
157
|
"jobs.rule": "Time rule",
|
|
116
158
|
"jobs.instant": "Instant / interval anchor (ISO time with UTC offset)",
|
|
117
159
|
"jobs.every": "Interval (milliseconds, minimum 1000)",
|
|
118
160
|
"jobs.cron": "Cron expression (five fields)",
|
|
119
161
|
"jobs.timezone": "Timezone",
|
|
120
|
-
"jobs.scheduleHelp": "
|
|
162
|
+
"jobs.scheduleHelp": "New schedules are enabled on save. Editing preserves their current on/off state.",
|
|
163
|
+
"jobs.form.customCron": "Custom Cron",
|
|
164
|
+
"jobs.form.cron": "Recurring",
|
|
165
|
+
"jobs.form.interval": "Fixed interval",
|
|
166
|
+
"jobs.form.once": "Run once",
|
|
167
|
+
"jobs.form.daily": "Daily",
|
|
168
|
+
"jobs.form.weekly": "Weekly",
|
|
169
|
+
"jobs.form.monthly": "Monthly",
|
|
170
|
+
"jobs.form.advanced": "Advanced details",
|
|
171
|
+
"jobs.form.frequency": "Repeat",
|
|
172
|
+
"jobs.form.time": "Time",
|
|
173
|
+
"jobs.form.time24": "Time · 24-hour clock",
|
|
174
|
+
"jobs.form.quickTime": "Common times",
|
|
175
|
+
"jobs.form.cronRequired": "Enter a Cron expression, or return to a repeat frequency.",
|
|
176
|
+
"jobs.form.cronSymbols": "* means any value, , separates values, - defines a range, and / sets a step. Example: 0 9 * * 1-5 runs at 09:00 Monday–Friday. The server validates on save.",
|
|
177
|
+
"jobs.form.cron.minute": "Minute",
|
|
178
|
+
"jobs.form.cron.hour": "Hour",
|
|
179
|
+
"jobs.form.cron.date": "Day",
|
|
180
|
+
"jobs.form.cron.month": "Month",
|
|
181
|
+
"jobs.form.cron.weekday": "Weekday",
|
|
182
|
+
"jobs.form.cron.minuteRange": "0–59",
|
|
183
|
+
"jobs.form.cron.hourRange": "0–23",
|
|
184
|
+
"jobs.form.cron.dateRange": "1–31",
|
|
185
|
+
"jobs.form.cron.monthRange": "1–12",
|
|
186
|
+
"jobs.form.cron.weekdayRange": "0–7; 0/7 = Sun",
|
|
187
|
+
"jobs.form.day": "Day of month",
|
|
188
|
+
"jobs.form.weekdays": "Days of week",
|
|
189
|
+
"jobs.form.monthHelp": "For days 29–31, months without that date are skipped.",
|
|
190
|
+
"jobs.form.complexHelp": "Custom five-field Cron. The server validates on save; complex expressions stay unchanged.",
|
|
191
|
+
"jobs.form.replaceComplex": "This expression cannot be converted losslessly. Switching to the visual form will replace this rule. Continue?",
|
|
192
|
+
"jobs.form.zonePreserved": "Times use this schedule’s saved timezone. Editing does not change it.",
|
|
193
|
+
"jobs.form.forJob": "Run Job",
|
|
194
|
+
"jobs.form.internalId": "Internal identifier",
|
|
195
|
+
"jobs.form.idHelp": "Used for links and record associations. Generated automatically; usually no need to change.",
|
|
196
|
+
"jobs.form.paused": "Future runs are paused. Existing run history is unchanged.",
|
|
197
|
+
"jobs.form.cronTime": "Choose a valid time.",
|
|
198
|
+
"jobs.form.cronDays": "Select at least one day.",
|
|
199
|
+
"jobs.form.cronDay": "Day must be an integer from 1 to 31.",
|
|
200
|
+
"jobs.form.monthRule": "Monthly on day {day}, {time}",
|
|
201
|
+
"jobs.form.command": "Command",
|
|
202
|
+
"jobs.form.remove": "Remove",
|
|
203
|
+
"jobs.form.addStep": "Add step",
|
|
204
|
+
"jobs.form.addEnv": "Add environment variable",
|
|
205
|
+
"jobs.form.envName": "Variable name",
|
|
206
|
+
"jobs.form.envValue": "Value",
|
|
207
|
+
"jobs.form.timeout": "Timeout (ms, optional)",
|
|
208
|
+
"jobs.form.target": "Execution target",
|
|
209
|
+
"jobs.form.goal": "Completion goal",
|
|
210
|
+
"jobs.form.delivery": "Deliver the task",
|
|
211
|
+
"jobs.form.task-created": "Create the task",
|
|
212
|
+
"jobs.form.work-item": "Existing work item",
|
|
213
|
+
"jobs.form.new": "New task",
|
|
214
|
+
"jobs.form.operator": "Global Operator",
|
|
215
|
+
"jobs.form.task": "Task role",
|
|
216
|
+
"jobs.form.job": "Dedicated Agent",
|
|
217
|
+
"jobs.form.serial": "Serial",
|
|
218
|
+
"jobs.form.isolated": "Isolated",
|
|
219
|
+
"jobs.form.concurrency": "Execution policy",
|
|
220
|
+
"jobs.form.addBinding": "Add Project",
|
|
221
|
+
"jobs.form.directory": "Directory",
|
|
222
|
+
"jobs.form.baseRef": "Base branch",
|
|
223
|
+
"jobs.form.unchanged": "Fields not shown here are preserved unchanged.",
|
|
224
|
+
"jobs.form.duplicateEnv": "Environment names must be nonempty and unique.",
|
|
225
|
+
"jobs.form.positiveInteger": "Enter a positive integer.",
|
|
226
|
+
"jobs.form.needStep": "Add at least one execution step.",
|
|
227
|
+
"jobs.form.argvHelp": "This step executes these arguments directly, without a shell. Command is a display description.",
|
|
228
|
+
"jobs.form.addArgument": "Add argument",
|
|
121
229
|
"jobs.state": "State",
|
|
122
|
-
"jobs.recentRun": "
|
|
230
|
+
"jobs.recentRun": "Latest Run",
|
|
123
231
|
"jobs.refreshAttempt": "Project: last attempt",
|
|
124
232
|
"jobs.refreshSuccess": "Project: last successful refresh",
|
|
125
233
|
"jobs.onceCompleted": "The once plan was admitted. Completed does not mean its Run succeeded.",
|
|
@@ -130,7 +238,7 @@ export const EN = {
|
|
|
130
238
|
"jobs.lastAttempt": "Last admission attempt",
|
|
131
239
|
"jobs.lastSuccess": "Last successful scheduled Run",
|
|
132
240
|
"jobs.refreshFacts": "Project refresh: last attempt / last success",
|
|
133
|
-
"jobs.history": "
|
|
241
|
+
"jobs.history": "Run history",
|
|
134
242
|
"jobs.noRuns": "No executions yet.",
|
|
135
243
|
"jobs.inspectRun": "Inspect attempt Run",
|
|
136
244
|
"jobs.trigger": "Trigger",
|
|
@@ -140,6 +248,83 @@ export const EN = {
|
|
|
140
248
|
"jobs.logs": "Step logs",
|
|
141
249
|
"jobs.truncated": "last 64 KiB",
|
|
142
250
|
"jobs.noLogs": "No checkpointed step logs yet. This does not establish success.",
|
|
251
|
+
"jobs.copy": "Copy",
|
|
252
|
+
"jobs.copied": "Copied",
|
|
253
|
+
"jobs.copyManual": "Copy manually:",
|
|
254
|
+
"jobs.copyJson": "Copy JSON",
|
|
255
|
+
"jobs.copyCommand": "Copy command",
|
|
256
|
+
"jobs.jsonInvalid": "Configuration is not valid JSON:",
|
|
257
|
+
"jobs.preview": "Reads as",
|
|
258
|
+
"jobs.basics": "Basics",
|
|
259
|
+
"jobs.runDetail": "Run",
|
|
260
|
+
"jobs.closeRun": "Close Run details",
|
|
261
|
+
"jobs.started": "Started",
|
|
262
|
+
"jobs.updated": "Updated",
|
|
263
|
+
"jobs.duration": "Duration",
|
|
264
|
+
"jobs.exit": "Exit",
|
|
265
|
+
"jobs.failedStep": "Failed step",
|
|
266
|
+
"jobs.retryOf": "Retry of",
|
|
267
|
+
"jobs.steps": "Steps",
|
|
268
|
+
"jobs.overview": "Overview",
|
|
269
|
+
"jobs.successRate": "Success rate",
|
|
270
|
+
"jobs.avgDuration": "Avg. duration",
|
|
271
|
+
"jobs.sparkLabel": "Last {count} Runs, {ok} succeeded",
|
|
272
|
+
"jobs.runCount": "{count} Runs",
|
|
273
|
+
"jobs.showMore": "Show {count} more",
|
|
274
|
+
"jobs.filterRuns": "Filter Runs by status",
|
|
275
|
+
"jobs.filter.all": "All",
|
|
276
|
+
"jobs.filter.active": "Active",
|
|
277
|
+
"jobs.col.status": "Status",
|
|
278
|
+
"jobs.col.run": "Run",
|
|
279
|
+
"jobs.col.started": "Started",
|
|
280
|
+
"jobs.lastAttemptShort": "Last attempt",
|
|
281
|
+
"jobs.lastSuccessShort": "Last success",
|
|
282
|
+
"jobs.ruleDaily": "Daily at {time}",
|
|
283
|
+
"jobs.ruleWeekly": "{days} at {time}",
|
|
284
|
+
"jobs.ruleEvery": "Every {period}",
|
|
285
|
+
"jobs.ruleOnce": "Once at {at}",
|
|
286
|
+
"jobs.ruleAnchor": "from",
|
|
287
|
+
"jobs.cfg.input": "Input",
|
|
288
|
+
"jobs.cfg.workspace": "Workspace",
|
|
289
|
+
"jobs.cfg.head": "HEAD",
|
|
290
|
+
"jobs.cfg.owner": "Owner",
|
|
291
|
+
"jobs.cfg.ownerTask": "This Task",
|
|
292
|
+
"jobs.cfg.env": "Environment",
|
|
293
|
+
"jobs.cfg.noEnv": "None",
|
|
294
|
+
"jobs.cfg.goal": "Goal",
|
|
295
|
+
"jobs.cfg.target": "Target",
|
|
296
|
+
"jobs.cfg.concurrency": "Concurrency",
|
|
297
|
+
"jobs.cfg.agentInput": "Instructions",
|
|
298
|
+
"jobs.cfg.other": "Other fields",
|
|
299
|
+
"jobs.cfg.raw": "Exact configuration",
|
|
300
|
+
"jobs.cfg.noParams": "No parameters",
|
|
301
|
+
"jobs.cfg.emptyList": "Empty list",
|
|
302
|
+
"jobs.cfg.emptyText": "Empty",
|
|
303
|
+
"jobs.cfg.fields": "{count} fields",
|
|
304
|
+
"jobs.cfg.value": "Value",
|
|
305
|
+
"jobs.cfg.timeout": "Timeout",
|
|
306
|
+
"jobs.cfg.cwd": "cwd",
|
|
307
|
+
"jobs.cfg.unnamed": "unnamed",
|
|
308
|
+
"jobs.cfg.description": "Description",
|
|
309
|
+
"jobs.cfg.bindings": "Project bindings",
|
|
310
|
+
"jobs.cfg.task": "Task",
|
|
311
|
+
"jobs.cfg.workItem": "WorkItem",
|
|
312
|
+
"jobs.cfg.role": "Role",
|
|
313
|
+
"jobs.cfg.agent": "Agent",
|
|
314
|
+
"jobs.cfg.ownerRun": "Owner Run",
|
|
315
|
+
"jobs.cfg.attempt": "Integration attempt",
|
|
316
|
+
"jobs.action.system.noop": "No-op check",
|
|
317
|
+
"jobs.action.project.refresh": "Project refresh",
|
|
318
|
+
"jobs.goal.delivery": "Deliver in a new Task",
|
|
319
|
+
"jobs.goal.task-created": "Create a Task",
|
|
320
|
+
"jobs.goal.work-item": "Run a WorkItem",
|
|
321
|
+
"jobs.target.new": "New Task",
|
|
322
|
+
"jobs.target.work-item": "Existing WorkItem",
|
|
323
|
+
"jobs.target.operator": "Operator",
|
|
324
|
+
"jobs.target.task": "Task role",
|
|
325
|
+
"jobs.target.job": "Job-owned Agent",
|
|
326
|
+
"jobs.concurrency.serial": "Serial",
|
|
327
|
+
"jobs.concurrency.isolated": "Isolated",
|
|
143
328
|
"models.subscriptionAuth": "Subscription",
|
|
144
329
|
"models.account": "Account",
|
|
145
330
|
"models.providerReadOnlyHelp": "Managed by its provider. Edit the configuration in the owning service.",
|
|
@@ -148,7 +333,7 @@ export const EN = {
|
|
|
148
333
|
"models.window.weekly": "Weekly",
|
|
149
334
|
"models.window.monthly": "Monthly",
|
|
150
335
|
"manage.nav": "Main navigation",
|
|
151
|
-
"manage.workbench": "
|
|
336
|
+
"manage.workbench": "Tasks",
|
|
152
337
|
"manage.sections": "Sections",
|
|
153
338
|
"manage.collapseSections": "Collapse sections",
|
|
154
339
|
"manage.showSections": "Show sections",
|