@zq-silk/yui 2.3.0 → 2.5.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 +18 -9
- 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 +65 -14
- 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 +18 -3
- 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 +39 -8
- package/dist/controller/globalInputDelivery.js +2 -4
- package/dist/controller/jobControl.js +2 -0
- package/dist/controller/nativeHostInput.js +37 -0
- package/dist/controller/providerRetryDelivery.js +4 -2
- 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/agentAdapter.js +3 -5
- package/dist/executor/codexConfigConflict.js +6 -4
- package/dist/executor/effectiveLaunch.js +24 -1
- package/dist/executor/fileRoleLaunchPlanner.js +15 -7
- package/dist/integration/gitIntegrationService.js +7 -12
- package/dist/integration/integrationSourceApplication.js +4 -2
- package/dist/interaction/notificationPresentation.js +85 -0
- package/dist/interaction/operatorPresentation.js +1 -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/leaderWakeupProcessor.js +1 -9
- 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/cli-information-contract.md +1 -1
- package/docs/cli-information-contract.zh-CN.md +1 -1
- package/docs/managed-turn-and-session-runtime.md +31 -0
- package/docs/managed-turn-and-session-runtime.zh-CN.md +22 -0
- package/docs/release-workflow.md +54 -0
- package/docs/release-workflow.zh-CN.md +39 -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/skills/yui-leader/SKILL.md +5 -4
- package/skills/yui-leader/references/execution.md +2 -1
- package/skills/yui-operator/SKILL.md +3 -0
- package/skills/yui-reviewer/SKILL.md +4 -2
- package/skills/yui-runtime/SKILL.md +43 -2
- package/skills/yui-worker/SKILL.md +3 -2
- 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
|
-
`;
|