@zq-silk/yui 2.3.0 → 2.4.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/README.md +15 -1
- package/dist/artifacts/artifactCapability.js +39 -10
- package/dist/artifacts/imageMetadata.js +120 -0
- package/dist/artifacts/managedGit.js +7 -4
- package/dist/artifacts/taskArtifactRepository.js +6 -0
- package/dist/cli/commandCatalog.js +17 -8
- package/dist/cli/invocationAuthority.js +1 -1
- package/dist/commands/executionAuditCommands.js +5 -0
- package/dist/commands/globalRoleCommands.js +41 -3
- package/dist/commands/projectCommands.js +89 -94
- package/dist/commands/roleSkillValidation.js +4 -8
- package/dist/commands/taskCommands.js +25 -2
- package/dist/commands/taskUsageCommand.js +38 -0
- package/dist/context/roleSessionContext.js +18 -2
- package/dist/context/runContextPack.js +5 -1
- package/dist/context/sessionBootstrapManifest.js +10 -1
- package/dist/context/skillCatalog.js +108 -0
- package/dist/context/skillPackage.js +161 -0
- package/dist/context/taskContext.js +3 -2
- package/dist/context/taskSearch.js +127 -0
- package/dist/controlPlaneCli.js +25 -20
- package/dist/controller/fileSchedulerStoreAdapter.js +32 -6
- package/dist/controller/jobControl.js +2 -0
- package/dist/controller/nativeHostInput.js +37 -0
- package/dist/controller/runtime.js +93 -2
- package/dist/controller/runtimeEventInbox.js +2 -2
- package/dist/controller/runtimeEventProcessor.js +3 -3
- package/dist/doctor/doctor.js +10 -0
- package/dist/execution/reviewMainRun.js +1 -0
- package/dist/execution/workItemMainRun.js +1 -0
- package/dist/executor/codexConfigConflict.js +6 -4
- package/dist/executor/effectiveLaunch.js +24 -1
- package/dist/executor/fileRoleLaunchPlanner.js +13 -5
- package/dist/integration/gitIntegrationService.js +7 -12
- package/dist/integration/integrationSourceApplication.js +4 -2
- package/dist/job/httpPreview.js +61 -0
- package/dist/job/jobRunner.js +12 -2
- package/dist/kernel/builtinCapabilities.js +3 -1
- package/dist/message/message.js +15 -1
- package/dist/output/rolePresentation.js +4 -1
- package/dist/repository/executionLaneGitSnapshot.js +2 -1
- package/dist/repository/gitCompatibility.js +62 -0
- package/dist/repository/gitWorkspace.js +48 -71
- package/dist/repository/gitWorktreeInventory.js +121 -0
- package/dist/repository/project.js +74 -25
- package/dist/resources/resourceDiscovery.js +2 -2
- package/dist/resources/resourceGc.js +7 -4
- package/dist/resources/resourceRegistrar.js +2 -1
- package/dist/runtime/agentEndpoint.js +8 -0
- package/dist/runtime/agentHost.js +170 -3
- package/dist/runtime/codexAppServerRuntime.js +1 -1
- package/dist/runtime/codexNativeAccess.js +175 -0
- package/dist/runtime/controlledCodexTui.js +116 -0
- package/dist/runtime/publicReply.js +23 -0
- package/dist/runtime/runtimeObservation.js +48 -0
- package/dist/runtime/structuredProviderHost.js +16 -7
- package/dist/runtime/taskCostMetrics.js +133 -0
- package/dist/runtime/taskUsageMetrics.js +11 -2
- package/dist/runtime/taskUsageQuery.js +38 -0
- package/dist/scheduler/taskExecutionProjection.js +2 -0
- package/dist/scheduler/taskObservabilityProjection.js +9 -1
- package/dist/storage/homeLayout.js +13 -0
- package/dist/storage/sqliteSchema.js +44 -0
- package/dist/storage/sqliteStore.js +53 -0
- package/dist/storage/storageVersions.js +1 -1
- package/dist/storage/storeRpc.js +2 -0
- package/dist/storage/taskSearch.js +35 -0
- package/dist/storage/upgrade/projectKnowledgeMigration.js +52 -0
- package/dist/task/ordinaryArchive.js +42 -0
- package/dist/tmux/tmuxManager.js +8 -8
- package/dist/web/assets/assetManifest.js +11 -51
- package/dist/web/assets/client/app/catalog.js +215 -0
- package/dist/web/assets/client/app/main.js +148 -0
- package/dist/web/assets/client/app/selection.js +139 -0
- package/dist/web/assets/client/app/settings.js +326 -0
- package/dist/web/assets/client/app/taskView.js +280 -0
- package/dist/web/assets/client/domain/context.js +96 -0
- package/dist/web/assets/client/domain/records.js +126 -0
- package/dist/web/assets/client/domain/runs.js +119 -0
- package/dist/web/assets/client/domain/taskForms.js +197 -0
- package/dist/web/assets/client/domain/vocab.js +69 -0
- package/dist/web/assets/client/domain/work.js +116 -0
- package/dist/web/assets/client/i18n/en.js +900 -0
- package/dist/web/assets/client/i18n/zh-CN.js +898 -0
- package/dist/web/assets/client/index.js +114 -0
- package/dist/web/assets/client/layout/resizer.js +37 -0
- package/dist/web/assets/client/layout/router.js +35 -0
- package/dist/web/assets/client/layout/shortcuts.js +45 -0
- package/dist/web/assets/client/layout/sizing.js +95 -0
- package/dist/web/assets/client/layout/workspace.js +247 -0
- package/dist/web/assets/client/{api.js → lib/api.js} +20 -4
- package/dist/web/assets/client/{dom.js → lib/dom.js} +24 -12
- package/dist/web/assets/client/{format.js → lib/format.js} +7 -0
- package/dist/web/assets/client/lib/i18n.js +54 -0
- package/dist/web/assets/client/lib/markdown.js +167 -0
- package/dist/web/assets/client/lib/prefs.js +28 -0
- package/dist/web/assets/client/lib/theme.js +68 -0
- package/dist/web/assets/client/ui/containers.js +101 -0
- package/dist/web/assets/client/ui/controls.js +51 -0
- package/dist/web/assets/client/ui/forms.js +48 -0
- package/dist/web/assets/client/ui/metrics.js +137 -0
- package/dist/web/assets/client/ui/primitives.js +92 -0
- package/dist/web/assets/client/ui/text.js +33 -0
- package/dist/web/assets/client/ui/toast.js +12 -0
- package/dist/web/assets/client/views/bodySearch.js +172 -0
- package/dist/web/assets/client/views/createTask.js +85 -0
- package/dist/web/assets/client/views/dock/conversation.js +612 -0
- package/dist/web/assets/client/views/dock/discussion.js +173 -0
- package/dist/web/assets/client/views/dock/preview.js +129 -0
- package/dist/web/assets/client/views/dock/terminal.js +199 -0
- package/dist/web/assets/client/views/globalInput.js +67 -0
- package/dist/web/assets/client/{overview.js → views/overview.js} +61 -51
- package/dist/web/assets/client/{sidebar.js → views/sidebar.js} +56 -62
- package/dist/web/assets/client/views/skills.js +152 -0
- package/dist/web/assets/client/views/task/actions.js +36 -0
- package/dist/web/assets/client/views/task/delivery.js +185 -0
- package/dist/web/assets/client/views/task/deliveryResults.js +159 -0
- package/dist/web/assets/client/views/task/files.js +272 -0
- package/dist/web/assets/client/views/task/observation.js +153 -0
- package/dist/web/assets/client/views/task/overview.js +161 -0
- package/dist/web/assets/client/views/task/page.js +131 -0
- package/dist/web/assets/client/views/task/records.js +163 -0
- package/dist/web/assets/client/views/task/runtime.js +130 -0
- package/dist/web/assets/client/views/task/timeline.js +190 -0
- package/dist/web/assets/shared/geometry.js +19 -0
- package/dist/web/assets/shared/taskTabs.js +8 -0
- package/dist/web/assets/shared/themes.js +93 -0
- package/dist/web/assets/shell/dialogs.js +63 -0
- package/dist/web/assets/shell/dock.js +40 -0
- package/dist/web/assets/shell/index.js +40 -0
- package/dist/web/assets/shell/settings.js +34 -0
- package/dist/web/assets/shell/sidebar.js +45 -0
- package/dist/web/assets/styles/index.js +41 -0
- package/dist/web/assets/styles/{responsive.js → layout/responsive.js} +10 -10
- package/dist/web/assets/styles/{layout.js → layout/workspace.js} +17 -24
- package/dist/web/assets/styles/markdown.js +22 -3
- package/dist/web/assets/styles/tokens.js +10 -63
- package/dist/web/assets/styles/ui/controls.js +76 -0
- package/dist/web/assets/styles/ui/forms.js +46 -0
- package/dist/web/assets/styles/ui/surfaces.js +122 -0
- package/dist/web/assets/styles/views/delivery.js +29 -0
- package/dist/web/assets/styles/views/dock.js +71 -0
- package/dist/web/assets/styles/views/page.js +32 -0
- package/dist/web/assets/styles/views/records.js +70 -0
- package/dist/web/assets/styles/views/settings.js +26 -0
- package/dist/web/assets/styles/views/sidebar.js +74 -0
- package/dist/web/assets/styles/views/task.js +80 -0
- package/dist/web/assets/styles/views/terminal.js +38 -0
- package/dist/web/taskPreviews.js +205 -0
- package/dist/web/tmuxWebTerminal.js +16 -9
- package/dist/web/webConversation.js +373 -0
- package/dist/web/webMaterials.js +155 -0
- package/dist/web/webServer.js +295 -18
- package/dist/web/webSettings.js +331 -0
- package/dist/web/webSkills.js +86 -0
- package/dist/web/webSnapshot.js +40 -8
- package/dist/web/webTaskSurface.js +106 -8
- package/dist/workspace/gitChangeSetCapture.js +3 -1
- package/docs/managed-turn-and-session-runtime.md +9 -0
- package/docs/managed-turn-and-session-runtime.zh-CN.md +7 -0
- package/docs/release-workflow.md +28 -0
- package/docs/release-workflow.zh-CN.md +21 -0
- package/docs/storage-baseline.md +38 -1
- package/docs/storage-baseline.zh-CN.md +22 -0
- package/docs/task-discovery.md +28 -0
- package/docs/testing/verification-levels.md +2 -2
- package/i18n/README.zh-CN.md +1 -1
- package/package.json +1 -1
- package/dist/web/assets/client/app.js +0 -808
- package/dist/web/assets/client/components.js +0 -477
- package/dist/web/assets/client/detail.js +0 -379
- package/dist/web/assets/client/dock.js +0 -309
- package/dist/web/assets/client/evidence.js +0 -364
- package/dist/web/assets/client/forms.js +0 -230
- package/dist/web/assets/client/i18n.js +0 -1277
- package/dist/web/assets/client/markdown.js +0 -114
- package/dist/web/assets/client/records.js +0 -198
- package/dist/web/assets/client/sections.js +0 -349
- package/dist/web/assets/client/theme.js +0 -71
- package/dist/web/assets/shell.js +0 -161
- package/dist/web/assets/styles/components.js +0 -216
- package/dist/web/assets/styles/views.js +0 -309
- /package/dist/web/assets/{icons.js → shared/icons.js} +0 -0
|
@@ -1,309 +0,0 @@
|
|
|
1
|
-
export const DOCK_SCRIPT = String.raw `
|
|
2
|
-
// The session dock: the Task discussion (recorded Task messages plus the
|
|
3
|
-
// composer) and the live native Session terminal. The feed is a read-only
|
|
4
|
-
// projection and redraws on every refresh; the composer is kept per Task so a
|
|
5
|
-
// refresh can never discard an unsent draft or a pending receipt.
|
|
6
|
-
import { Terminal } from "/assets/vendor/xterm.mjs";
|
|
7
|
-
import { FitAddon } from "/assets/vendor/addon-fit.mjs";
|
|
8
|
-
import { h, icon, clear } from "/assets/js/dom.js";
|
|
9
|
-
import { formatShort } from "/assets/js/format.js";
|
|
10
|
-
import { label, chip, emptyState, richText, dot } from "/assets/js/components.js";
|
|
11
|
-
import { entriesOf, totalOf, readExact, recordCard } from "/assets/js/records.js";
|
|
12
|
-
import { messageComposer } from "/assets/js/forms.js";
|
|
13
|
-
import { pageToken } from "/assets/js/api.js";
|
|
14
|
-
|
|
15
|
-
export function renderDiscussion(host, data, t, locale, actions) {
|
|
16
|
-
const task = data.task;
|
|
17
|
-
const composerKey = task.id + "|" + task.status + "|" + locale;
|
|
18
|
-
let feed = host.querySelector(".feed");
|
|
19
|
-
const composerWrap = host.querySelector(".composer-wrap");
|
|
20
|
-
const composer = composerWrap && composerWrap.firstChild;
|
|
21
|
-
const keepComposer = host.dataset.taskId === task.id && composer
|
|
22
|
-
&& (composer.dataset.key === composerKey || composer.dataset.unsent === "true" || composer.contains(document.activeElement));
|
|
23
|
-
const previousScroll = feed ? feed.scrollTop : 0;
|
|
24
|
-
const atEnd = !feed || host.dataset.taskId !== task.id || feed.scrollHeight - feed.clientHeight - feed.scrollTop < 40;
|
|
25
|
-
if (host.dataset.taskId !== task.id || !feed) {
|
|
26
|
-
clear(host);
|
|
27
|
-
host.append(
|
|
28
|
-
h("div.dock-sub", null, h("span.dock-sub-title", null, t("discussion.title")), h("span.dock-sub-note", null, t("discussion.source"))),
|
|
29
|
-
h("div.feed", { "aria-label": t("discussion.feed"), tabIndex: 0 }),
|
|
30
|
-
h("div.composer-wrap"));
|
|
31
|
-
feed = host.querySelector(".feed");
|
|
32
|
-
}
|
|
33
|
-
host.dataset.taskId = task.id;
|
|
34
|
-
latest.set(task.id, { data: data, t: t, locale: locale, actions: actions });
|
|
35
|
-
if (!feed.querySelector('[data-reading="true"]')) drawFeed(feed, data, t, locale, actions);
|
|
36
|
-
feed.scrollTop = atEnd ? feed.scrollHeight : previousScroll;
|
|
37
|
-
if (!keepComposer) {
|
|
38
|
-
const next = messageComposer(task, t, actions);
|
|
39
|
-
next.dataset.key = composerKey;
|
|
40
|
-
host.querySelector(".composer-wrap").replaceChildren(next);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export function discussionHasUnsent(host) {
|
|
45
|
-
return !!host.querySelector('[data-unsent="true"]');
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// The Context read carries the newest messages as summaries; each is read
|
|
49
|
-
// exactly once per digest (kept in the Task's view state) and drawn in full,
|
|
50
|
-
// so a message needs no separate raw-record reader.
|
|
51
|
-
// Older messages are listed on request, newest first, and read the same way.
|
|
52
|
-
const older = new Map();
|
|
53
|
-
const failedReads = new Set();
|
|
54
|
-
const latest = new Map();
|
|
55
|
-
|
|
56
|
-
function readKey(entry) { return entry.ref.refId + "@" + entry.ref.digest; }
|
|
57
|
-
|
|
58
|
-
function exactValue(entry, cache) {
|
|
59
|
-
if (!entry.omitted) return entry.value;
|
|
60
|
-
const hit = cache[entry.ref.store + "/" + entry.ref.refId];
|
|
61
|
-
return hit && hit.digest === entry.ref.digest && hit.result ? hit.result.value : null;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function loadOlder(data, actions, state, shownIds) {
|
|
65
|
-
state.pending = true;
|
|
66
|
-
state.error = null;
|
|
67
|
-
let rounds = 0;
|
|
68
|
-
function next() {
|
|
69
|
-
return actions.list(data.task.id, "task-message", { limit: 20, cursor: state.cursor }).then(function (page) {
|
|
70
|
-
const known = new Set(state.items.map(function (item) { return item.ref.refId; }));
|
|
71
|
-
const fresh = page.items.filter(function (item) { return !known.has(item.ref.refId) && !shownIds.has(item.ref.refId); });
|
|
72
|
-
state.items = state.items.concat(fresh);
|
|
73
|
-
state.cursor = page.nextCursor;
|
|
74
|
-
state.done = !page.nextCursor;
|
|
75
|
-
rounds += 1;
|
|
76
|
-
if (!fresh.length && page.nextCursor && rounds < 10) return next();
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
// A listing that moved invalidates its continuation; the next request
|
|
80
|
-
// starts again from the newest page and skips what is already shown.
|
|
81
|
-
return next().catch(function (error) { state.error = error.message; state.cursor = null; })
|
|
82
|
-
.then(function () { state.pending = false; });
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function drawFeed(feed, data, t, locale, actions) {
|
|
86
|
-
clear(feed);
|
|
87
|
-
const task = data.task;
|
|
88
|
-
const cache = data.viewState.exact = data.viewState.exact || {};
|
|
89
|
-
const contextEntries = entriesOf(data.core, "task-message");
|
|
90
|
-
const shownIds = new Set(contextEntries.map(function (entry) { return entry.ref.refId; }));
|
|
91
|
-
if (!older.has(task.id)) older.set(task.id, { items: [], cursor: null, done: false, pending: false, error: null });
|
|
92
|
-
const state = older.get(task.id);
|
|
93
|
-
const olderItems = state.items.filter(function (item) { return !shownIds.has(item.ref.refId); })
|
|
94
|
-
.sort(function (a, b) { return Date.parse(a.createdAt || 0) - Date.parse(b.createdAt || 0); });
|
|
95
|
-
// Context lists messages newest first; the feed reads oldest first.
|
|
96
|
-
const rows = olderItems.map(function (item) { return { entry: { ref: item.ref, summary: item.summary, omitted: true }, at: item.createdAt }; })
|
|
97
|
-
.concat(contextEntries.slice().reverse().map(function (entry) { return { entry: entry, at: null }; }));
|
|
98
|
-
const reads = [];
|
|
99
|
-
rows.forEach(function (row) {
|
|
100
|
-
row.value = exactValue(row.entry, cache);
|
|
101
|
-
if (!row.value && !failedReads.has(readKey(row.entry))) {
|
|
102
|
-
row.reading = true;
|
|
103
|
-
reads.push(readExact(row.entry, task.id, actions, cache).catch(function () { failedReads.add(readKey(row.entry)); }));
|
|
104
|
-
}
|
|
105
|
-
row.at = (row.value && row.value.createdAt) || row.at;
|
|
106
|
-
});
|
|
107
|
-
if (rows.every(function (row) { return row.at; })) rows.sort(function (a, b) { return Date.parse(a.at) - Date.parse(b.at); });
|
|
108
|
-
|
|
109
|
-
const remaining = totalOf(data.core, "task-message") - shownIds.size - olderItems.length;
|
|
110
|
-
if (actions.list && remaining > 0 && !state.done) {
|
|
111
|
-
const more = h("button.show-more.feed-older", { type: "button", disabled: state.pending },
|
|
112
|
-
state.pending ? t("discussion.olderLoading") : t("discussion.older").replace("{count}", String(remaining)));
|
|
113
|
-
more.addEventListener("click", function () {
|
|
114
|
-
more.disabled = true;
|
|
115
|
-
more.textContent = t("discussion.olderLoading");
|
|
116
|
-
loadOlder(data, actions, state, shownIds).then(function () { redraw(feed, task.id, true); });
|
|
117
|
-
});
|
|
118
|
-
feed.append(more);
|
|
119
|
-
}
|
|
120
|
-
if (state.error) feed.append(h("p.feed-note", null, t("records.unavailable") + " " + state.error));
|
|
121
|
-
if (!rows.length) {
|
|
122
|
-
feed.append(emptyState(t("discussion.empty"), "chat"));
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
let lastDay = "";
|
|
126
|
-
rows.forEach(function (row) {
|
|
127
|
-
if (!row.value) {
|
|
128
|
-
const placeholder = recordCard(row.entry, task.id, t, actions, { compact: true });
|
|
129
|
-
if (row.reading) placeholder.dataset.reading = "true";
|
|
130
|
-
feed.append(placeholder);
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
const value = row.value;
|
|
134
|
-
const day = value.createdAt ? new Intl.DateTimeFormat(locale, { dateStyle: "medium" }).format(new Date(value.createdAt)) : "";
|
|
135
|
-
if (day && day !== lastDay) { feed.append(h("div.feed-day", null, h("span", null, day))); lastDay = day; }
|
|
136
|
-
const type = (value.author && value.author.type) || "system";
|
|
137
|
-
const author = (value.author && value.author.roleName) || label(t, "author", type);
|
|
138
|
-
const message = h("article.msg.from-" + (type === "user" || type === "operator" ? "user" : type === "role" ? "role" : "system"));
|
|
139
|
-
message.append(h("span.msg-avatar", { "aria-hidden": "true" }, String(author).slice(0, 1).toUpperCase()));
|
|
140
|
-
const main = h("div.msg-main", null,
|
|
141
|
-
h("header.msg-head", null, h("strong", null, author),
|
|
142
|
-
value.intent ? chip(label(t, "intent", value.intent)) : null,
|
|
143
|
-
value.kind === "role-result" ? chip(label(t, "messageKind", value.kind)) : null,
|
|
144
|
-
h("time", { dateTime: value.createdAt || "", title: row.entry.ref.refId }, formatShort(value.createdAt, locale))),
|
|
145
|
-
h("div.msg-bubble", null, richText(null, value.body || "", t, { threshold: 900 })));
|
|
146
|
-
message.append(main);
|
|
147
|
-
feed.append(message);
|
|
148
|
-
});
|
|
149
|
-
if (reads.length) Promise.all(reads).then(function () { redraw(feed, task.id, false); });
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// Redraw after a read without losing the reader's place: the bottom stays
|
|
153
|
-
// pinned, and prepended older messages keep the current message in view.
|
|
154
|
-
function redraw(feed, taskId, prepend) {
|
|
155
|
-
const host = feed.closest("[data-task-id]");
|
|
156
|
-
const current = latest.get(taskId);
|
|
157
|
-
if (!feed.isConnected || !host || host.dataset.taskId !== taskId || !current) return;
|
|
158
|
-
const atEnd = feed.scrollHeight - feed.clientHeight - feed.scrollTop < 40;
|
|
159
|
-
const fromBottom = feed.scrollHeight - feed.scrollTop;
|
|
160
|
-
drawFeed(feed, current.data, current.t, current.locale, current.actions);
|
|
161
|
-
feed.scrollTop = atEnd && !prepend ? feed.scrollHeight : feed.scrollHeight - fromBottom;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// --- Live native Session ----------------------------------------------------------
|
|
165
|
-
function cssVar(name, fallback) {
|
|
166
|
-
const value = getComputedStyle(document.documentElement).getPropertyValue(name).trim();
|
|
167
|
-
return value || fallback;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
function terminalTheme() {
|
|
171
|
-
return {
|
|
172
|
-
background: cssVar("--term-bg", "#0b0c0d"),
|
|
173
|
-
foreground: cssVar("--term-fg", "#e6e6e8"),
|
|
174
|
-
cursor: cssVar("--term-cursor", "#ff7a52"),
|
|
175
|
-
selectionBackground: cssVar("--term-sel", "#3a2a24")
|
|
176
|
-
};
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
function cliFor(target) {
|
|
180
|
-
if (target.scope === "task") return "yui task role session inspect " + target.taskId + " " + target.roleName;
|
|
181
|
-
return target.roleName === "operator" ? "yui operator enter" : "yui session enter " + target.roleName;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
export function sameTarget(left, right) {
|
|
185
|
-
return !!left && !!right && left.scope === right.scope && left.roleName === right.roleName
|
|
186
|
-
&& (left.scope !== "task" || left.taskId === right.taskId);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
export function createTerminalController(elements, t, locale, notify) {
|
|
190
|
-
let session = null;
|
|
191
|
-
let current = null;
|
|
192
|
-
let stateKey = "terminal.idle";
|
|
193
|
-
let targets = [];
|
|
194
|
-
|
|
195
|
-
function setState(key, state) {
|
|
196
|
-
stateKey = key;
|
|
197
|
-
elements.state.dataset.state = state;
|
|
198
|
-
elements.state.lastChild.textContent = t(key);
|
|
199
|
-
}
|
|
200
|
-
function drawTargets() {
|
|
201
|
-
clear(elements.targets);
|
|
202
|
-
targets.forEach(function (target) {
|
|
203
|
-
elements.targets.append(h("button.target-chip", {
|
|
204
|
-
type: "button",
|
|
205
|
-
"aria-pressed": String(sameTarget(target, current)),
|
|
206
|
-
title: target.scope === "task" ? target.taskId + " / " + target.roleName : t("terminal.global") + " / " + target.roleName,
|
|
207
|
-
onclick: function () { open(target); }
|
|
208
|
-
}, target.scope === "task" ? dot(target.live ? "ok" : "idle") : icon("broadcast", "icon-sm"), h("span", null, target.roleName)));
|
|
209
|
-
});
|
|
210
|
-
if (!targets.length) elements.targets.append(h("span.faint.small", null, t("terminal.noTargets")));
|
|
211
|
-
}
|
|
212
|
-
function dispose() {
|
|
213
|
-
if (!session) return;
|
|
214
|
-
const owned = session;
|
|
215
|
-
session = null;
|
|
216
|
-
owned.resizeObserver.disconnect();
|
|
217
|
-
owned.input.dispose();
|
|
218
|
-
owned.socket.close();
|
|
219
|
-
owned.terminal.dispose();
|
|
220
|
-
clear(elements.host);
|
|
221
|
-
}
|
|
222
|
-
function close() {
|
|
223
|
-
dispose();
|
|
224
|
-
current = null;
|
|
225
|
-
elements.empty.hidden = false;
|
|
226
|
-
elements.cli.textContent = "yui operator enter";
|
|
227
|
-
setState("terminal.idle", "idle");
|
|
228
|
-
drawTargets();
|
|
229
|
-
}
|
|
230
|
-
function open(target) {
|
|
231
|
-
dispose();
|
|
232
|
-
current = target;
|
|
233
|
-
elements.empty.hidden = true;
|
|
234
|
-
elements.cli.textContent = cliFor(target);
|
|
235
|
-
setState("terminal.connecting", "connecting");
|
|
236
|
-
drawTargets();
|
|
237
|
-
const terminal = new Terminal({
|
|
238
|
-
cursorBlink: true,
|
|
239
|
-
scrollback: 0,
|
|
240
|
-
convertEol: false,
|
|
241
|
-
fontFamily: '"JetBrains Mono","SFMono-Regular",Menlo,Consolas,monospace',
|
|
242
|
-
fontSize: 12.5,
|
|
243
|
-
lineHeight: 1.15,
|
|
244
|
-
theme: terminalTheme()
|
|
245
|
-
});
|
|
246
|
-
const fit = new FitAddon();
|
|
247
|
-
terminal.loadAddon(fit);
|
|
248
|
-
terminal.open(elements.host);
|
|
249
|
-
try { fit.fit(); } catch {}
|
|
250
|
-
let writable = false;
|
|
251
|
-
const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
|
|
252
|
-
const parameters = new URLSearchParams({
|
|
253
|
-
scope: target.scope, role: target.roleName, cols: String(terminal.cols), rows: String(terminal.rows), token: pageToken()
|
|
254
|
-
});
|
|
255
|
-
if (target.scope === "task") parameters.set("task", target.taskId);
|
|
256
|
-
const socket = new WebSocket(protocol + "//" + window.location.host + "/api/terminal?" + parameters.toString());
|
|
257
|
-
const input = terminal.onData(function (data) {
|
|
258
|
-
if (writable && socket.readyState === WebSocket.OPEN) socket.send(JSON.stringify({ type: "input", data: data }));
|
|
259
|
-
});
|
|
260
|
-
const resizeObserver = new ResizeObserver(function () {
|
|
261
|
-
if (!session || session.terminal !== terminal || !elements.host.offsetWidth) return;
|
|
262
|
-
try { fit.fit(); } catch { return; }
|
|
263
|
-
if (socket.readyState === WebSocket.OPEN) {
|
|
264
|
-
socket.send(JSON.stringify({ type: "resize", columns: terminal.cols, rows: terminal.rows }));
|
|
265
|
-
}
|
|
266
|
-
});
|
|
267
|
-
resizeObserver.observe(elements.host);
|
|
268
|
-
socket.addEventListener("message", function (event) {
|
|
269
|
-
let message;
|
|
270
|
-
try { message = JSON.parse(event.data); } catch { return; }
|
|
271
|
-
if (message.type === "ready") {
|
|
272
|
-
writable = !message.readOnly;
|
|
273
|
-
setState(writable ? "terminal.writable" : "terminal.readOnly", writable ? "writable" : "readonly");
|
|
274
|
-
if (message.history && message.history.limit < message.history.target) {
|
|
275
|
-
const number = new Intl.NumberFormat(locale());
|
|
276
|
-
notify(t("terminal.historyLimited").replace("{current}", number.format(message.history.limit))
|
|
277
|
-
.replace("{target}", number.format(message.history.target)));
|
|
278
|
-
}
|
|
279
|
-
terminal.focus();
|
|
280
|
-
} else if (message.type === "data") terminal.write(message.data);
|
|
281
|
-
else if (message.type === "exit") setState("terminal.closed", "closed");
|
|
282
|
-
else if (message.type === "error") {
|
|
283
|
-
setState("terminal.error", "error");
|
|
284
|
-
terminal.writeln("\r\n" + message.message);
|
|
285
|
-
}
|
|
286
|
-
});
|
|
287
|
-
socket.addEventListener("close", function () {
|
|
288
|
-
writable = false;
|
|
289
|
-
if (stateKey !== "terminal.error") setState("terminal.closed", "closed");
|
|
290
|
-
});
|
|
291
|
-
socket.addEventListener("error", function () {
|
|
292
|
-
writable = false;
|
|
293
|
-
setState("terminal.error", "error");
|
|
294
|
-
terminal.writeln(t("terminal.errorDetail"));
|
|
295
|
-
});
|
|
296
|
-
session = { terminal, socket, input, resizeObserver };
|
|
297
|
-
}
|
|
298
|
-
drawTargets();
|
|
299
|
-
return {
|
|
300
|
-
open: open,
|
|
301
|
-
close: close,
|
|
302
|
-
current: function () { return current; },
|
|
303
|
-
connected: function () { return !!session; },
|
|
304
|
-
setTargets: function (next) { targets = next; drawTargets(); },
|
|
305
|
-
relabel: function () { setState(stateKey, elements.state.dataset.state); drawTargets(); },
|
|
306
|
-
retheme: function () { if (session) session.terminal.options.theme = terminalTheme(); }
|
|
307
|
-
};
|
|
308
|
-
}
|
|
309
|
-
`;
|