@ours.network/fleet 0.15.7 → 0.16.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.
@@ -0,0 +1,168 @@
1
+ import { FleetError } from '../application/errors.js';
2
+ /**
3
+ * Turn sketches into configuration.
4
+ *
5
+ * Promotion is the middle step of sketch -> promote -> launch, and it writes
6
+ * configuration ONLY. It never provisions an identity, never registers a
7
+ * supervisor service and never starts a process: `Launch` is a separate, explicit
8
+ * action. That separation is what makes "visible but not launchable" true at the
9
+ * daemon level rather than as a UI rule.
10
+ *
11
+ * No YAML is written here. The mutation is expressed against the configuration
12
+ * *model* and handed to `FleetConfigService`, so the revision guard, the real
13
+ * loader validating a candidate file, the reviewed diff, the timestamped backup
14
+ * and the atomic 0600 replace are all impossible to bypass.
15
+ */
16
+ /** Loop interval when the sketch did not choose one (`resolveLoops` requires it). */
17
+ const DEFAULT_LOOP_INTERVAL = '10m';
18
+ /** Scalar draft fields that are valid keys of a role mapping. */
19
+ const ROLE_FIELDS = ['mission', 'bio', 'persona', 'coordinator', 'harness', 'session', 'model', 'identity', 'cwd'];
20
+ /** Scalar draft fields that are valid keys of a watchdog mapping. */
21
+ const WATCHDOG_FIELDS = ['coordinator', 'interval', 'enabled'];
22
+ /** Scalar draft fields that are valid keys of a loop mapping. */
23
+ const LOOP_FIELDS = ['prompt', 'interval', 'initial_delay', 'jitter'];
24
+ export class TopologyPromoteService {
25
+ options;
26
+ constructor(options) {
27
+ this.options = options;
28
+ }
29
+ async preview(request) {
30
+ const { model, promoted } = await this.build(request);
31
+ const preview = await this.options.configuration.preview(request.configRevision, model);
32
+ return { ...preview, promoted };
33
+ }
34
+ async promote(request) {
35
+ const { model, promoted } = await this.build(request);
36
+ const written = await this.options.configuration.write(request.configRevision, model);
37
+ // The configuration is real now. Clearing the sketches is best effort: if the
38
+ // sidecar moved under us the drafts simply resurface as shadowed-by-config,
39
+ // which the merged model already reports, rather than losing the write.
40
+ const cleared = await this.clearDrafts(promoted, request.draftRevision);
41
+ return { ...written, promoted, draftsCleared: cleared.ok, draftRevision: cleared.revision };
42
+ }
43
+ async clearDrafts(promoted, draftRevision) {
44
+ const current = this.options.drafts.read();
45
+ if (draftRevision !== undefined && draftRevision !== current.revision)
46
+ return { ok: false, revision: current.revision };
47
+ const promotedIds = new Set(promoted);
48
+ const next = {
49
+ ...current.draft,
50
+ drafts: {
51
+ nodes: current.draft.drafts.nodes.filter(node => !promotedIds.has(node.id)),
52
+ /*
53
+ * A drawn edge is owned by its SOURCE — it becomes the source's `watch:`
54
+ * or `roles:` list — so it is materialised, and therefore redundant, only
55
+ * once the source itself is written. Clearing it because the TARGET was
56
+ * promoted silently rewrites the survivor's meaning: a watchdog scoped to
57
+ * one agent loses its only scope edge, reads as standalone, and is then
58
+ * written with no `watch:` key at all — that is, watching everything.
59
+ */
60
+ edges: current.draft.drafts.edges.filter(edge => !promotedIds.has(edge.from)),
61
+ },
62
+ };
63
+ try {
64
+ const written = await this.options.drafts.write(current.revision, next);
65
+ return { ok: true, revision: written.revision };
66
+ }
67
+ catch {
68
+ return { ok: false, revision: current.revision };
69
+ }
70
+ }
71
+ /** Build the configuration model that adding `ids` produces. */
72
+ async build(request) {
73
+ if (!Array.isArray(request.ids) || request.ids.length === 0)
74
+ throw new FleetError('invalid_request', 'name at least one sketch to add to the fleet');
75
+ const merged = await this.options.topology();
76
+ const read = this.options.configuration.read();
77
+ if (read.revision !== request.configRevision)
78
+ throw new FleetError('stale_state', 'fleet.yaml changed since it was opened; reload before adding to the fleet');
79
+ const selected = request.ids.map(id => resolve(merged, id));
80
+ const model = structuredClone(read.model);
81
+ // Agents first: a watchdog's `watch:` list and a loop's `roles:` list may only
82
+ // name roles that exist, including ones being added in this same write.
83
+ for (const node of [...selected].sort(byKind))
84
+ addNode(model, node, merged);
85
+ return { model, promoted: selected.map(node => node.id) };
86
+ }
87
+ }
88
+ const KIND_ORDER = { agent: 0, watchdog: 1, loop: 2 };
89
+ const byKind = (a, b) => KIND_ORDER[a.kind] - KIND_ORDER[b.kind];
90
+ function resolve(merged, id) {
91
+ const node = merged.nodes.find(candidate => candidate.id === id);
92
+ if (!node)
93
+ throw new FleetError('invalid_request', `there is no sketch called ${id}`);
94
+ if (node.origin !== 'draft')
95
+ throw new FleetError('conflict', `${node.label} is already part of the fleet configuration`);
96
+ if (!node.valid || !node.complete) {
97
+ const why = node.missing.map(item => `${item.field}: ${item.fix}`).join(' ');
98
+ throw new FleetError('invalid_request', `${node.label} is not ready to add — ${why}`);
99
+ }
100
+ return node;
101
+ }
102
+ function addNode(model, node, merged) {
103
+ const block = section(model, node.kind === 'agent' ? 'roles' : node.kind === 'watchdog' ? 'watchdogs' : 'loops');
104
+ if (block[node.label] !== undefined)
105
+ throw new FleetError('conflict', `${node.label} already exists in the configuration`);
106
+ block[node.label] = node.kind === 'agent' ? agentEntry(node)
107
+ : node.kind === 'watchdog' ? watchdogEntry(node, merged)
108
+ : loopEntry(node, merged, model);
109
+ }
110
+ function agentEntry(node) {
111
+ return pick(node, ROLE_FIELDS);
112
+ }
113
+ /**
114
+ * D4, and the owner requirement it encodes: a watchdog drawn on its own carries
115
+ * NO `watch:` key, which the config layer reads as "every configured role" —
116
+ * so an agent added tomorrow is covered with no edit at all. A watchdog created
117
+ * from a specific agent carries that agent explicitly and stays scoped to it.
118
+ */
119
+ function watchdogEntry(node, merged) {
120
+ const scoped = linked(merged, node.id, 'watches');
121
+ return { ...pick(node, WATCHDOG_FIELDS), ...(scoped.length ? { watch: scoped } : {}) };
122
+ }
123
+ function loopEntry(node, merged, model) {
124
+ const roles = linked(merged, node.id, 'targets');
125
+ const entry = {
126
+ roles, ...pick(node, LOOP_FIELDS),
127
+ };
128
+ entry.interval ??= DEFAULT_LOOP_INTERVAL;
129
+ // An enabled loop hard-requires `session: acp` on every target. Adding one to a
130
+ // tmux agent would make the whole fleet unloadable, so it arrives switched off
131
+ // and badged instead — the owner turns it on after switching the agent to ACP.
132
+ if (node.enabled === false || !roles.every(role => sessionOf(model, role) === 'acp'))
133
+ entry.enabled = false;
134
+ return entry;
135
+ }
136
+ /** Names of the agents this node points at, from drawn edges and derived ones alike. */
137
+ function linked(merged, id, kind) {
138
+ return [...new Set(merged.edges
139
+ .filter(edge => edge.kind === kind && edge.from === id && !edge.implicit && !edge.dangling)
140
+ .map(edge => edge.to.slice('agent:'.length)))].sort();
141
+ }
142
+ function sessionOf(model, role) {
143
+ const roles = model.roles;
144
+ const defaults = model.defaults;
145
+ return String(roles?.[role]?.session ?? defaults?.session ?? 'tmux');
146
+ }
147
+ function section(model, key) {
148
+ const existing = model[key];
149
+ if (existing && typeof existing === 'object' && !Array.isArray(existing))
150
+ return existing;
151
+ const created = {};
152
+ model[key] = created;
153
+ return created;
154
+ }
155
+ /** Copy only the sketched fields that are real keys of the target mapping. */
156
+ function pick(node, keys) {
157
+ const fields = node.fields ?? {};
158
+ const out = {};
159
+ for (const key of keys) {
160
+ const value = fields[key];
161
+ if (value === undefined)
162
+ continue;
163
+ if (typeof value === 'string' && value.trim() === '')
164
+ continue;
165
+ out[key] = value;
166
+ }
167
+ return out;
168
+ }
@@ -0,0 +1,31 @@
1
+ export type YamlModel = Record<string, unknown>;
2
+ /** Formatting of the source document, so a rewrite does not reflow it. */
3
+ export interface DocumentFormatting {
4
+ indent: number;
5
+ indentSeq: boolean;
6
+ }
7
+ /**
8
+ * Apply `model` to `source`, returning the new document text.
9
+ *
10
+ * Guarantees:
11
+ * - an unchanged model returns `source` byte for byte;
12
+ * - regions outside a change keep their bytes whenever the change is spliceable;
13
+ * - the result parses back to exactly `model`, verified before returning, so a
14
+ * planning bug can never silently write something else to disk.
15
+ */
16
+ export declare function renderModelOntoSource(source: string, model: YamlModel): string;
17
+ /**
18
+ * Replace every `defaults.env` / `roles.*.env` value — and every `vars:` entry
19
+ * those values interpolate — with `marker`.
20
+ *
21
+ * Splices the scalars out of the original bytes rather than re-rendering, so the
22
+ * only difference from `source` is the secrets themselves. Redaction must not be
23
+ * able to introduce (or conceal) a formatting change in a review diff.
24
+ */
25
+ export declare function redactSourceSecrets(source: string, marker: string): string;
26
+ /**
27
+ * Best-effort detection of the document's block indentation, used only when a
28
+ * change is not spliceable and the document has to be re-rendered. Detection
29
+ * failure falls back to the library defaults; correctness never depends on it.
30
+ */
31
+ export declare function detectFormatting(source: string): DocumentFormatting;
@@ -0,0 +1,401 @@
1
+ import { Document, isMap, isPair, isScalar, isSeq, parseDocument, } from 'yaml';
2
+ /**
3
+ * Surgical fleet-YAML editing.
4
+ *
5
+ * The web console edits a JSON *model* of the configuration, but the file on
6
+ * disk belongs to the user. A `parse -> plain JS -> stringify` round-trip keeps
7
+ * the data and destroys everything around it. Re-rendering the parsed document
8
+ * is barely better: `yaml` keeps a comment's text but not its position, so
9
+ * `permissions: # note` comes back as `permissions:` with the note moved onto
10
+ * the next line, shifting everything below it. On `examples/fleet.yaml` a plain
11
+ * round-trip rewrites 80 of 147 lines.
12
+ *
13
+ * So edits are computed as *text splices* against the original bytes. Only the
14
+ * regions that genuinely changed are replaced; everything else is byte-identical
15
+ * by construction, which makes an unchanged save a true no-op and keeps a trailing
16
+ * inline comment attached to the scalar it annotates.
17
+ *
18
+ * The contract is deliberately narrower than "nothing else ever changes":
19
+ *
20
+ * - A *scalar* edit replaces only the value's source range, so the trailing
21
+ * comment beside it survives untouched.
22
+ * - Changing the *length* of a block sequence replaces that collection WHOLESALE,
23
+ * because an index-shifting edit cannot be attributed to particular items. Inline
24
+ * comments written on the individual items of that sequence are therefore LOST.
25
+ * The loss is bounded to the one collection being edited — every other line in
26
+ * the file keeps its bytes — and the caller diffs the real before/after text, so
27
+ * it is visible in review before anything is written and the save can be declined.
28
+ * - A model change that cannot be expressed as a splice at all falls back to
29
+ * re-rendering the whole document. That path does reflow, which is the other
30
+ * reason the caller diffs real bytes: reflow must be visible, never silent.
31
+ * - Deleting a mapping entry leaves any comment written above it in place. Removing
32
+ * a value must not quietly remove the operator's prose about it.
33
+ */
34
+ const PARSE_OPTIONS = { strict: true, uniqueKeys: true, prettyErrors: true };
35
+ const MIN_INDENT = 1;
36
+ const MAX_INDENT = 8;
37
+ const DEFAULT_INDENT = 2;
38
+ /**
39
+ * Apply `model` to `source`, returning the new document text.
40
+ *
41
+ * Guarantees:
42
+ * - an unchanged model returns `source` byte for byte;
43
+ * - regions outside a change keep their bytes whenever the change is spliceable;
44
+ * - the result parses back to exactly `model`, verified before returning, so a
45
+ * planning bug can never silently write something else to disk.
46
+ */
47
+ export function renderModelOntoSource(source, model) {
48
+ const document = parseSource(source);
49
+ const formatting = detectFormatting(source);
50
+ const plan = [];
51
+ const spliceable = planEdits(document, source, formatting, [], documentValue(document), model, plan);
52
+ const rendered = !spliceable ? reRender(document, source, formatting, model)
53
+ : plan.length === 0 ? source
54
+ : applySplices(source, plan);
55
+ const check = parseSource(rendered);
56
+ if (!deepEqual(documentValue(check), model))
57
+ throw new Error('surgical YAML edit did not reproduce the requested model');
58
+ return rendered;
59
+ }
60
+ /**
61
+ * Replace every `defaults.env` / `roles.*.env` value — and every `vars:` entry
62
+ * those values interpolate — with `marker`.
63
+ *
64
+ * Splices the scalars out of the original bytes rather than re-rendering, so the
65
+ * only difference from `source` is the secrets themselves. Redaction must not be
66
+ * able to introduce (or conceal) a formatting change in a review diff.
67
+ */
68
+ export function redactSourceSecrets(source, marker) {
69
+ const document = parseSource(source);
70
+ const secretVars = new Set();
71
+ const plan = [];
72
+ const redactEnvMap = (path) => {
73
+ const node = document.getIn(path, true);
74
+ if (!isMap(node))
75
+ return;
76
+ for (const item of node.items) {
77
+ if (!isPair(item) || !isScalar(item.value))
78
+ continue;
79
+ if (typeof item.value.value === 'string')
80
+ for (const match of item.value.value.matchAll(/\$\{(\w+)\}/g))
81
+ secretVars.add(match[1]);
82
+ pushValueSplice(plan, item.value, marker);
83
+ }
84
+ };
85
+ redactEnvMap(['defaults', 'env']);
86
+ for (const name of mapKeys(document, ['roles']))
87
+ redactEnvMap(['roles', name, 'env']);
88
+ const vars = document.getIn(['vars'], true);
89
+ if (isMap(vars))
90
+ for (const item of vars.items) {
91
+ if (isPair(item) && isScalar(item.key) && isScalar(item.value)
92
+ && secretVars.has(String(item.key.value)))
93
+ pushValueSplice(plan, item.value, marker);
94
+ }
95
+ return plan.length === 0 ? source : applySplices(source, plan);
96
+ }
97
+ function pushValueSplice(plan, node, marker) {
98
+ const range = node.range;
99
+ if (range)
100
+ plan.push({ start: range[0], end: range[1], text: marker });
101
+ }
102
+ /**
103
+ * Best-effort detection of the document's block indentation, used only when a
104
+ * change is not spliceable and the document has to be re-rendered. Detection
105
+ * failure falls back to the library defaults; correctness never depends on it.
106
+ */
107
+ export function detectFormatting(source) {
108
+ const lines = source.split('\n')
109
+ .filter(line => line.trim() !== '' && !line.trimStart().startsWith('#'));
110
+ let indent;
111
+ let indentSeq;
112
+ let previousWidth = 0;
113
+ let previousOpensBlock = false;
114
+ for (const line of lines) {
115
+ const width = line.length - line.trimStart().length;
116
+ const isItem = /^-(\s|$)/.test(line.trimStart());
117
+ if (previousOpensBlock && width >= previousWidth) {
118
+ if (isItem)
119
+ indentSeq ??= width > previousWidth;
120
+ if (width > previousWidth)
121
+ indent ??= width - previousWidth;
122
+ }
123
+ previousOpensBlock = !isItem && /:\s*(#.*)?$/.test(line);
124
+ previousWidth = width;
125
+ }
126
+ return {
127
+ indent: indent !== undefined && indent >= MIN_INDENT && indent <= MAX_INDENT
128
+ ? indent : DEFAULT_INDENT,
129
+ indentSeq: indentSeq ?? true,
130
+ };
131
+ }
132
+ /* ------------------------------------------------------------------ *
133
+ * Planning: turn a model diff into text splices
134
+ * ------------------------------------------------------------------ */
135
+ /**
136
+ * Walk `next` against `current` and record the splices that would realise it.
137
+ * Returns false as soon as a change cannot be expressed against the source text,
138
+ * in which case the caller re-renders instead.
139
+ */
140
+ function planEdits(document, source, formatting, path, current, next, plan) {
141
+ if (deepEqual(current, next))
142
+ return true;
143
+ if (isPlainObject(current) && isPlainObject(next)) {
144
+ const node = document.getIn(path, true);
145
+ if (!isMap(node))
146
+ return false;
147
+ // Removing every entry one by one would leave `watchdogs:` with no value,
148
+ // which reads back as null rather than the empty mapping that was asked for.
149
+ if (path.length > 0 && Object.keys(next).length === 0 && Object.keys(current).length > 0)
150
+ return planReplace(document, source, formatting, path, next, plan);
151
+ for (const key of Object.keys(current))
152
+ if (!hasOwn(next, key) && !planDelete(node, source, key, plan))
153
+ return false;
154
+ for (const key of Object.keys(next)) {
155
+ if (hasOwn(current, key)) {
156
+ if (!planEdits(document, source, formatting, [...path, key], own(current, key), own(next, key), plan))
157
+ return false;
158
+ }
159
+ else if (!planInsert(node, source, formatting, key, own(next, key), plan))
160
+ return false;
161
+ }
162
+ return true;
163
+ }
164
+ if (Array.isArray(current) && Array.isArray(next) && current.length === next.length)
165
+ return next.every((value, index) => planEdits(document, source, formatting, [...path, index], current[index], value, plan));
166
+ return planReplace(document, source, formatting, path, next, plan);
167
+ }
168
+ /** Replace one value in place, keeping the key, its layout and its comment. */
169
+ function planReplace(document, source, formatting, path, next, plan) {
170
+ if (path.length === 0)
171
+ return false;
172
+ const node = document.getIn(path, true);
173
+ if (!isNodeWithRange(node))
174
+ return false;
175
+ // A block scalar's body cannot be swapped for an inline one by splicing.
176
+ if (isScalar(node) && (node.type === 'BLOCK_LITERAL' || node.type === 'BLOCK_FOLDED'))
177
+ return false;
178
+ const start = node.range[0];
179
+ // A block collection's range runs past the newline that terminates it, while a
180
+ // scalar's stops at the value. Splicing over that newline would pull the next
181
+ // line up onto this one, so leave it where it is.
182
+ let end = node.range[1];
183
+ while (end > start && source[end - 1] === '\n')
184
+ end -= 1;
185
+ const ownLine = source.slice(lineStart(source, start), start).trim() === '';
186
+ // `roles: [Alice]` must not come back as a block list just because it grew.
187
+ const flow = (isSeq(node) || isMap(node)) && node.flow === true;
188
+ const text = renderValue(next, formatting, flow);
189
+ // An emptied collection belongs beside its key, not alone on the next line.
190
+ if (ownLine && (text === '{}' || text === '[]')
191
+ && planCollapseOntoKey(document, source, path, start, end, text, plan))
192
+ return true;
193
+ if (text.includes('\n') && !ownLine)
194
+ return false;
195
+ plan.push({ start, end, text: ownLine ? indentContinuation(text, columnOf(source, start)) : text });
196
+ return true;
197
+ }
198
+ /** Rewrite `key:\n <block>` as `key: {}` when the block became empty. */
199
+ function planCollapseOntoKey(document, source, path, start, end, text, plan) {
200
+ const key = path[path.length - 1];
201
+ if (typeof key !== 'string')
202
+ return false;
203
+ const parent = document.getIn(path.slice(0, -1), true);
204
+ const pair = parent === undefined ? undefined : findPair(parent, key);
205
+ if (!pair || !isNodeWithRange(pair.key))
206
+ return false;
207
+ const colon = source.indexOf(':', pair.key.range[1]);
208
+ // Anything but whitespace between the key and its value is a comment; keep it.
209
+ if (colon < 0 || colon >= start || source.slice(colon + 1, start).trim() !== '')
210
+ return false;
211
+ plan.push({ start: colon + 1, end, text: ` ${text}` });
212
+ return true;
213
+ }
214
+ /** Remove a whole `key: value` entry, including the line it sits on. */
215
+ function planDelete(node, source, key, plan) {
216
+ const pair = findPair(node, key);
217
+ if (!pair || !isNodeWithRange(pair.key))
218
+ return false;
219
+ const end = isNodeWithRange(pair.value) ? pair.value.range[2] : pair.key.range[2];
220
+ plan.push({ start: lineStart(source, pair.key.range[0]), end, text: '' });
221
+ return true;
222
+ }
223
+ /** Append a new `key: value` entry to an existing block mapping. */
224
+ function planInsert(node, source, formatting, key, value, plan) {
225
+ if (!isMap(node) || node.flow)
226
+ return false;
227
+ const last = [...node.items].reverse().find(isPair);
228
+ // An empty mapping has no sibling to copy an indent or a position from.
229
+ if (!last || !isNodeWithRange(last.key))
230
+ return false;
231
+ const column = columnOf(source, last.key.range[0]);
232
+ const at = isNodeWithRange(last.value) ? last.value.range[2] : last.key.range[2];
233
+ const entry = indentContinuation(renderValue({ [key]: value }, formatting), column);
234
+ const prefix = at > 0 && source[at - 1] !== '\n' ? '\n' : '';
235
+ plan.push({ start: at, end: at, text: `${prefix}${' '.repeat(column)}${entry}\n` });
236
+ return true;
237
+ }
238
+ function applySplices(source, plan) {
239
+ const ordered = [...plan].sort((a, b) => b.start - a.start || b.end - a.end);
240
+ let out = source;
241
+ for (const splice of ordered)
242
+ out = out.slice(0, splice.start) + splice.text + out.slice(splice.end);
243
+ return out;
244
+ }
245
+ /* ------------------------------------------------------------------ *
246
+ * Fallback: re-render the whole document
247
+ * ------------------------------------------------------------------ */
248
+ function reRender(document, source, formatting, model) {
249
+ applyToDocument(document, [], documentValue(document), model);
250
+ return restoreCommentAlignment(source, document.toString({ lineWidth: 0, ...formatting }));
251
+ }
252
+ function applyToDocument(document, path, current, next) {
253
+ if (deepEqual(current, next))
254
+ return;
255
+ if (isPlainObject(current) && isPlainObject(next)) {
256
+ for (const key of Object.keys(next))
257
+ applyToDocument(document, [...path, key], own(current, key), own(next, key));
258
+ for (const key of Object.keys(current))
259
+ if (!hasOwn(next, key))
260
+ document.deleteIn([...path, key]);
261
+ return;
262
+ }
263
+ if (Array.isArray(current) && Array.isArray(next) && current.length === next.length) {
264
+ for (const [index, value] of next.entries())
265
+ applyToDocument(document, [...path, index], current[index], value);
266
+ return;
267
+ }
268
+ if (next === undefined) {
269
+ document.deleteIn(path);
270
+ return;
271
+ }
272
+ // Mutating an existing scalar keeps its comment and quoting; replacing it loses both.
273
+ const existing = document.getIn(path, true);
274
+ if (isScalar(existing) && isScalarValue(next) && existing.type !== 'BLOCK_LITERAL' && existing.type !== 'BLOCK_FOLDED') {
275
+ existing.value = next;
276
+ return;
277
+ }
278
+ if (path.length === 0)
279
+ document.contents = document.createNode(next);
280
+ else
281
+ document.setIn(path, document.createNode(next));
282
+ }
283
+ /**
284
+ * `yaml` keeps a comment's text but not the padding in front of it. Put the
285
+ * original spacing back on lines that are otherwise byte-identical to the
286
+ * source; ambiguous or altered lines are left as rendered, and the caller
287
+ * re-parses the result, so this can never change what the file means.
288
+ */
289
+ function restoreCommentAlignment(source, rendered) {
290
+ const AMBIGUOUS = '';
291
+ const originals = new Map();
292
+ for (const line of source.split('\n')) {
293
+ const at = trailingCommentIndex(line);
294
+ if (at < 0)
295
+ continue;
296
+ const collapsed = `${line.slice(0, at).replace(/\s+$/, '')} ${line.slice(at)}`;
297
+ if (collapsed === line)
298
+ continue;
299
+ originals.set(collapsed, originals.has(collapsed) ? AMBIGUOUS : line);
300
+ }
301
+ if (originals.size === 0)
302
+ return rendered;
303
+ return rendered.split('\n').map(line => originals.get(line) || line).join('\n');
304
+ }
305
+ /** Index of a line's trailing `#` comment, ignoring `#` inside quoted scalars. */
306
+ function trailingCommentIndex(line) {
307
+ let quote;
308
+ for (let index = 0; index < line.length; index += 1) {
309
+ const character = line[index];
310
+ if (quote === "'") {
311
+ if (character !== "'")
312
+ continue;
313
+ if (line[index + 1] === "'")
314
+ index += 1;
315
+ else
316
+ quote = undefined;
317
+ }
318
+ else if (quote === '"') {
319
+ if (character === '\\')
320
+ index += 1;
321
+ else if (character === '"')
322
+ quote = undefined;
323
+ }
324
+ else if (character === '"' || character === "'")
325
+ quote = character;
326
+ else if (character === '#' && index > 0 && /\s/.test(line[index - 1]))
327
+ return index;
328
+ }
329
+ return -1;
330
+ }
331
+ /* ------------------------------------------------------------------ *
332
+ * Shared helpers
333
+ * ------------------------------------------------------------------ */
334
+ function parseSource(source) {
335
+ const document = parseDocument(source, PARSE_OPTIONS);
336
+ if (document.errors.length)
337
+ throw new Error(document.errors.map(error => error.message).join('; '));
338
+ return document;
339
+ }
340
+ function documentValue(document) {
341
+ return document.contents == null ? {} : document.toJS({ maxAliasCount: 100 });
342
+ }
343
+ function mapKeys(document, path) {
344
+ const node = document.getIn(path, true);
345
+ if (!isMap(node))
346
+ return [];
347
+ return node.items.flatMap(item => (isPair(item) && isScalar(item.key) ? [String(item.key.value)] : []));
348
+ }
349
+ function findPair(node, key) {
350
+ if (!isMap(node))
351
+ return undefined;
352
+ return node.items.find((item) => isPair(item) && isScalar(item.key) && String(item.key.value) === key);
353
+ }
354
+ function renderValue(value, formatting, flow = false) {
355
+ const document = new Document(value);
356
+ if (flow && (isSeq(document.contents) || isMap(document.contents)))
357
+ document.contents.flow = true;
358
+ return document
359
+ .toString({ lineWidth: 0, indent: formatting.indent, indentSeq: formatting.indentSeq })
360
+ .replace(/\n$/, '');
361
+ }
362
+ /** Indent every line but the first, which is spliced in after existing text. */
363
+ function indentContinuation(text, column) {
364
+ if (!text.includes('\n'))
365
+ return text;
366
+ const pad = ' '.repeat(column);
367
+ return text.split('\n').map((line, index) => (index === 0 || line === '' ? line : `${pad}${line}`)).join('\n');
368
+ }
369
+ const lineStart = (source, offset) => source.lastIndexOf('\n', offset - 1) + 1;
370
+ const columnOf = (source, offset) => offset - lineStart(source, offset);
371
+ function isNodeWithRange(node) {
372
+ return (isScalar(node) || isMap(node) || isSeq(node)) && Array.isArray(node.range);
373
+ }
374
+ function isScalarValue(value) {
375
+ return value === null || ['string', 'number', 'boolean'].includes(typeof value);
376
+ }
377
+ function isPlainObject(value) {
378
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
379
+ }
380
+ function hasOwn(value, key) {
381
+ return Object.prototype.hasOwnProperty.call(value, key);
382
+ }
383
+ function own(value, key) {
384
+ return hasOwn(value, key) ? value[key] : undefined;
385
+ }
386
+ function deepEqual(a, b) {
387
+ if (a === b)
388
+ return true;
389
+ if (typeof a === 'number' && typeof b === 'number')
390
+ return Number.isNaN(a) && Number.isNaN(b);
391
+ if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null)
392
+ return false;
393
+ if (Array.isArray(a) !== Array.isArray(b))
394
+ return false;
395
+ if (Array.isArray(a) && Array.isArray(b))
396
+ return a.length === b.length && a.every((item, index) => deepEqual(item, b[index]));
397
+ const [left, right] = [a, b];
398
+ const keys = Object.keys(left);
399
+ return keys.length === Object.keys(right).length
400
+ && keys.every(key => hasOwn(right, key) && deepEqual(left[key], right[key]));
401
+ }
@@ -1,4 +1,4 @@
1
- import{r as le,a as Ee,j as re}from"./index-BstfCP_F.js";var ge={exports:{}},Se;function ke(){return Se||(Se=1,(function(se,ne){(function(Q,X){se.exports=X()})(globalThis,(()=>(()=>{var Q={4567:function(B,r,o){var l=this&&this.__decorate||function(e,i,a,v){var f,g=arguments.length,c=g<3?i:v===null?v=Object.getOwnPropertyDescriptor(i,a):v;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")c=Reflect.decorate(e,i,a,v);else for(var m=e.length-1;m>=0;m--)(f=e[m])&&(c=(g<3?f(c):g>3?f(i,a,c):f(i,a))||c);return g>3&&c&&Object.defineProperty(i,a,c),c},_=this&&this.__param||function(e,i){return function(a,v){i(a,v,e)}};Object.defineProperty(r,"__esModule",{value:!0}),r.AccessibilityManager=void 0;const n=o(9042),d=o(9924),u=o(844),p=o(4725),h=o(2585),t=o(3656);let s=r.AccessibilityManager=class extends u.Disposable{constructor(e,i,a,v){super(),this._terminal=e,this._coreBrowserService=a,this._renderService=v,this._rowColumns=new WeakMap,this._liveRegionLineCount=0,this._charsToConsume=[],this._charsToAnnounce="",this._accessibilityContainer=this._coreBrowserService.mainDocument.createElement("div"),this._accessibilityContainer.classList.add("xterm-accessibility"),this._rowContainer=this._coreBrowserService.mainDocument.createElement("div"),this._rowContainer.setAttribute("role","list"),this._rowContainer.classList.add("xterm-accessibility-tree"),this._rowElements=[];for(let f=0;f<this._terminal.rows;f++)this._rowElements[f]=this._createAccessibilityTreeNode(),this._rowContainer.appendChild(this._rowElements[f]);if(this._topBoundaryFocusListener=f=>this._handleBoundaryFocus(f,0),this._bottomBoundaryFocusListener=f=>this._handleBoundaryFocus(f,1),this._rowElements[0].addEventListener("focus",this._topBoundaryFocusListener),this._rowElements[this._rowElements.length-1].addEventListener("focus",this._bottomBoundaryFocusListener),this._refreshRowsDimensions(),this._accessibilityContainer.appendChild(this._rowContainer),this._liveRegion=this._coreBrowserService.mainDocument.createElement("div"),this._liveRegion.classList.add("live-region"),this._liveRegion.setAttribute("aria-live","assertive"),this._accessibilityContainer.appendChild(this._liveRegion),this._liveRegionDebouncer=this.register(new d.TimeBasedDebouncer(this._renderRows.bind(this))),!this._terminal.element)throw new Error("Cannot enable accessibility before Terminal.open");this._terminal.element.insertAdjacentElement("afterbegin",this._accessibilityContainer),this.register(this._terminal.onResize((f=>this._handleResize(f.rows)))),this.register(this._terminal.onRender((f=>this._refreshRows(f.start,f.end)))),this.register(this._terminal.onScroll((()=>this._refreshRows()))),this.register(this._terminal.onA11yChar((f=>this._handleChar(f)))),this.register(this._terminal.onLineFeed((()=>this._handleChar(`
1
+ import{r as le,a as Ee,j as re}from"./index-CsHEL0f6.js";var ge={exports:{}},Se;function ke(){return Se||(Se=1,(function(se,ne){(function(Q,X){se.exports=X()})(globalThis,(()=>(()=>{var Q={4567:function(B,r,o){var l=this&&this.__decorate||function(e,i,a,v){var f,g=arguments.length,c=g<3?i:v===null?v=Object.getOwnPropertyDescriptor(i,a):v;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")c=Reflect.decorate(e,i,a,v);else for(var m=e.length-1;m>=0;m--)(f=e[m])&&(c=(g<3?f(c):g>3?f(i,a,c):f(i,a))||c);return g>3&&c&&Object.defineProperty(i,a,c),c},_=this&&this.__param||function(e,i){return function(a,v){i(a,v,e)}};Object.defineProperty(r,"__esModule",{value:!0}),r.AccessibilityManager=void 0;const n=o(9042),d=o(9924),u=o(844),p=o(4725),h=o(2585),t=o(3656);let s=r.AccessibilityManager=class extends u.Disposable{constructor(e,i,a,v){super(),this._terminal=e,this._coreBrowserService=a,this._renderService=v,this._rowColumns=new WeakMap,this._liveRegionLineCount=0,this._charsToConsume=[],this._charsToAnnounce="",this._accessibilityContainer=this._coreBrowserService.mainDocument.createElement("div"),this._accessibilityContainer.classList.add("xterm-accessibility"),this._rowContainer=this._coreBrowserService.mainDocument.createElement("div"),this._rowContainer.setAttribute("role","list"),this._rowContainer.classList.add("xterm-accessibility-tree"),this._rowElements=[];for(let f=0;f<this._terminal.rows;f++)this._rowElements[f]=this._createAccessibilityTreeNode(),this._rowContainer.appendChild(this._rowElements[f]);if(this._topBoundaryFocusListener=f=>this._handleBoundaryFocus(f,0),this._bottomBoundaryFocusListener=f=>this._handleBoundaryFocus(f,1),this._rowElements[0].addEventListener("focus",this._topBoundaryFocusListener),this._rowElements[this._rowElements.length-1].addEventListener("focus",this._bottomBoundaryFocusListener),this._refreshRowsDimensions(),this._accessibilityContainer.appendChild(this._rowContainer),this._liveRegion=this._coreBrowserService.mainDocument.createElement("div"),this._liveRegion.classList.add("live-region"),this._liveRegion.setAttribute("aria-live","assertive"),this._accessibilityContainer.appendChild(this._liveRegion),this._liveRegionDebouncer=this.register(new d.TimeBasedDebouncer(this._renderRows.bind(this))),!this._terminal.element)throw new Error("Cannot enable accessibility before Terminal.open");this._terminal.element.insertAdjacentElement("afterbegin",this._accessibilityContainer),this.register(this._terminal.onResize((f=>this._handleResize(f.rows)))),this.register(this._terminal.onRender((f=>this._refreshRows(f.start,f.end)))),this.register(this._terminal.onScroll((()=>this._refreshRows()))),this.register(this._terminal.onA11yChar((f=>this._handleChar(f)))),this.register(this._terminal.onLineFeed((()=>this._handleChar(`
2
2
  `)))),this.register(this._terminal.onA11yTab((f=>this._handleTab(f)))),this.register(this._terminal.onKey((f=>this._handleKey(f.key)))),this.register(this._terminal.onBlur((()=>this._clearLiveRegion()))),this.register(this._renderService.onDimensionsChange((()=>this._refreshRowsDimensions()))),this.register((0,t.addDisposableDomListener)(document,"selectionchange",(()=>this._handleSelectionChange()))),this.register(this._coreBrowserService.onDprChange((()=>this._refreshRowsDimensions()))),this._refreshRows(),this.register((0,u.toDisposable)((()=>{this._accessibilityContainer.remove(),this._rowElements.length=0})))}_handleTab(e){for(let i=0;i<e;i++)this._handleChar(" ")}_handleChar(e){this._liveRegionLineCount<21&&(this._charsToConsume.length>0?this._charsToConsume.shift()!==e&&(this._charsToAnnounce+=e):this._charsToAnnounce+=e,e===`
3
3
  `&&(this._liveRegionLineCount++,this._liveRegionLineCount===21&&(this._liveRegion.textContent+=n.tooMuchOutput)))}_clearLiveRegion(){this._liveRegion.textContent="",this._liveRegionLineCount=0}_handleKey(e){this._clearLiveRegion(),new RegExp("\\p{Control}","u").test(e)||this._charsToConsume.push(e)}_refreshRows(e,i){this._liveRegionDebouncer.refresh(e,i,this._terminal.rows)}_renderRows(e,i){const a=this._terminal.buffer,v=a.lines.length.toString();for(let f=e;f<=i;f++){const g=a.lines.get(a.ydisp+f),c=[],m=g?.translateToString(!0,void 0,void 0,c)||"",E=(a.ydisp+f+1).toString(),k=this._rowElements[f];k&&(m.length===0?(k.innerText=" ",this._rowColumns.set(k,[0,1])):(k.textContent=m,this._rowColumns.set(k,c)),k.setAttribute("aria-posinset",E),k.setAttribute("aria-setsize",v))}this._announceCharacters()}_announceCharacters(){this._charsToAnnounce.length!==0&&(this._liveRegion.textContent+=this._charsToAnnounce,this._charsToAnnounce="")}_handleBoundaryFocus(e,i){const a=e.target,v=this._rowElements[i===0?1:this._rowElements.length-2];if(a.getAttribute("aria-posinset")===(i===0?"1":`${this._terminal.buffer.lines.length}`)||e.relatedTarget!==v)return;let f,g;if(i===0?(f=a,g=this._rowElements.pop(),this._rowContainer.removeChild(g)):(f=this._rowElements.shift(),g=a,this._rowContainer.removeChild(f)),f.removeEventListener("focus",this._topBoundaryFocusListener),g.removeEventListener("focus",this._bottomBoundaryFocusListener),i===0){const c=this._createAccessibilityTreeNode();this._rowElements.unshift(c),this._rowContainer.insertAdjacentElement("afterbegin",c)}else{const c=this._createAccessibilityTreeNode();this._rowElements.push(c),this._rowContainer.appendChild(c)}this._rowElements[0].addEventListener("focus",this._topBoundaryFocusListener),this._rowElements[this._rowElements.length-1].addEventListener("focus",this._bottomBoundaryFocusListener),this._terminal.scrollLines(i===0?-1:1),this._rowElements[i===0?1:this._rowElements.length-2].focus(),e.preventDefault(),e.stopImmediatePropagation()}_handleSelectionChange(){if(this._rowElements.length===0)return;const e=document.getSelection();if(!e)return;if(e.isCollapsed)return void(this._rowContainer.contains(e.anchorNode)&&this._terminal.clearSelection());if(!e.anchorNode||!e.focusNode)return void console.error("anchorNode and/or focusNode are null");let i={node:e.anchorNode,offset:e.anchorOffset},a={node:e.focusNode,offset:e.focusOffset};if((i.node.compareDocumentPosition(a.node)&Node.DOCUMENT_POSITION_PRECEDING||i.node===a.node&&i.offset>a.offset)&&([i,a]=[a,i]),i.node.compareDocumentPosition(this._rowElements[0])&(Node.DOCUMENT_POSITION_CONTAINED_BY|Node.DOCUMENT_POSITION_FOLLOWING)&&(i={node:this._rowElements[0].childNodes[0],offset:0}),!this._rowContainer.contains(i.node))return;const v=this._rowElements.slice(-1)[0];if(a.node.compareDocumentPosition(v)&(Node.DOCUMENT_POSITION_CONTAINED_BY|Node.DOCUMENT_POSITION_PRECEDING)&&(a={node:v,offset:v.textContent?.length??0}),!this._rowContainer.contains(a.node))return;const f=({node:m,offset:E})=>{const k=m instanceof Text?m.parentNode:m;let D=parseInt(k?.getAttribute("aria-posinset"),10)-1;if(isNaN(D))return console.warn("row is invalid. Race condition?"),null;const b=this._rowColumns.get(k);if(!b)return console.warn("columns is null. Race condition?"),null;let x=E<b.length?b[E]:b.slice(-1)[0]+1;return x>=this._terminal.cols&&(++D,x=0),{row:D,column:x}},g=f(i),c=f(a);if(g&&c){if(g.row>c.row||g.row===c.row&&g.column>=c.column)throw new Error("invalid range");this._terminal.select(g.column,g.row,(c.row-g.row)*this._terminal.cols-g.column+c.column)}}_handleResize(e){this._rowElements[this._rowElements.length-1].removeEventListener("focus",this._bottomBoundaryFocusListener);for(let i=this._rowContainer.children.length;i<this._terminal.rows;i++)this._rowElements[i]=this._createAccessibilityTreeNode(),this._rowContainer.appendChild(this._rowElements[i]);for(;this._rowElements.length>e;)this._rowContainer.removeChild(this._rowElements.pop());this._rowElements[this._rowElements.length-1].addEventListener("focus",this._bottomBoundaryFocusListener),this._refreshRowsDimensions()}_createAccessibilityTreeNode(){const e=this._coreBrowserService.mainDocument.createElement("div");return e.setAttribute("role","listitem"),e.tabIndex=-1,this._refreshRowDimensions(e),e}_refreshRowsDimensions(){if(this._renderService.dimensions.css.cell.height){this._accessibilityContainer.style.width=`${this._renderService.dimensions.css.canvas.width}px`,this._rowElements.length!==this._terminal.rows&&this._handleResize(this._terminal.rows);for(let e=0;e<this._terminal.rows;e++)this._refreshRowDimensions(this._rowElements[e])}}_refreshRowDimensions(e){e.style.height=`${this._renderService.dimensions.css.cell.height}px`}};r.AccessibilityManager=s=l([_(1,h.IInstantiationService),_(2,p.ICoreBrowserService),_(3,p.IRenderService)],s)},3614:(B,r)=>{function o(d){return d.replace(/\r?\n/g,"\r")}function l(d,u){return u?"\x1B[200~"+d+"\x1B[201~":d}function _(d,u,p,h){d=l(d=o(d),p.decPrivateModes.bracketedPasteMode&&h.rawOptions.ignoreBracketedPasteMode!==!0),p.triggerDataEvent(d,!0),u.value=""}function n(d,u,p){const h=p.getBoundingClientRect(),t=d.clientX-h.left-10,s=d.clientY-h.top-10;u.style.width="20px",u.style.height="20px",u.style.left=`${t}px`,u.style.top=`${s}px`,u.style.zIndex="1000",u.focus()}Object.defineProperty(r,"__esModule",{value:!0}),r.rightClickHandler=r.moveTextAreaUnderMouseCursor=r.paste=r.handlePasteEvent=r.copyHandler=r.bracketTextForPaste=r.prepareTextForTerminal=void 0,r.prepareTextForTerminal=o,r.bracketTextForPaste=l,r.copyHandler=function(d,u){d.clipboardData&&d.clipboardData.setData("text/plain",u.selectionText),d.preventDefault()},r.handlePasteEvent=function(d,u,p,h){d.stopPropagation(),d.clipboardData&&_(d.clipboardData.getData("text/plain"),u,p,h)},r.paste=_,r.moveTextAreaUnderMouseCursor=n,r.rightClickHandler=function(d,u,p,h,t){n(d,u,p),t&&h.rightClickSelect(d),u.value=h.selectionText,u.select()}},7239:(B,r,o)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.ColorContrastCache=void 0;const l=o(1505);r.ColorContrastCache=class{constructor(){this._color=new l.TwoKeyMap,this._css=new l.TwoKeyMap}setCss(_,n,d){this._css.set(_,n,d)}getCss(_,n){return this._css.get(_,n)}setColor(_,n,d){this._color.set(_,n,d)}getColor(_,n){return this._color.get(_,n)}clear(){this._color.clear(),this._css.clear()}}},3656:(B,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.addDisposableDomListener=void 0,r.addDisposableDomListener=function(o,l,_,n){o.addEventListener(l,_,n);let d=!1;return{dispose:()=>{d||(d=!0,o.removeEventListener(l,_,n))}}}},3551:function(B,r,o){var l=this&&this.__decorate||function(s,e,i,a){var v,f=arguments.length,g=f<3?e:a===null?a=Object.getOwnPropertyDescriptor(e,i):a;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")g=Reflect.decorate(s,e,i,a);else for(var c=s.length-1;c>=0;c--)(v=s[c])&&(g=(f<3?v(g):f>3?v(e,i,g):v(e,i))||g);return f>3&&g&&Object.defineProperty(e,i,g),g},_=this&&this.__param||function(s,e){return function(i,a){e(i,a,s)}};Object.defineProperty(r,"__esModule",{value:!0}),r.Linkifier=void 0;const n=o(3656),d=o(8460),u=o(844),p=o(2585),h=o(4725);let t=r.Linkifier=class extends u.Disposable{get currentLink(){return this._currentLink}constructor(s,e,i,a,v){super(),this._element=s,this._mouseService=e,this._renderService=i,this._bufferService=a,this._linkProviderService=v,this._linkCacheDisposables=[],this._isMouseOut=!0,this._wasResized=!1,this._activeLine=-1,this._onShowLinkUnderline=this.register(new d.EventEmitter),this.onShowLinkUnderline=this._onShowLinkUnderline.event,this._onHideLinkUnderline=this.register(new d.EventEmitter),this.onHideLinkUnderline=this._onHideLinkUnderline.event,this.register((0,u.getDisposeArrayDisposable)(this._linkCacheDisposables)),this.register((0,u.toDisposable)((()=>{this._lastMouseEvent=void 0,this._activeProviderReplies?.clear()}))),this.register(this._bufferService.onResize((()=>{this._clearCurrentLink(),this._wasResized=!0}))),this.register((0,n.addDisposableDomListener)(this._element,"mouseleave",(()=>{this._isMouseOut=!0,this._clearCurrentLink()}))),this.register((0,n.addDisposableDomListener)(this._element,"mousemove",this._handleMouseMove.bind(this))),this.register((0,n.addDisposableDomListener)(this._element,"mousedown",this._handleMouseDown.bind(this))),this.register((0,n.addDisposableDomListener)(this._element,"mouseup",this._handleMouseUp.bind(this)))}_handleMouseMove(s){this._lastMouseEvent=s;const e=this._positionFromMouseEvent(s,this._element,this._mouseService);if(!e)return;this._isMouseOut=!1;const i=s.composedPath();for(let a=0;a<i.length;a++){const v=i[a];if(v.classList.contains("xterm"))break;if(v.classList.contains("xterm-hover"))return}this._lastBufferCell&&e.x===this._lastBufferCell.x&&e.y===this._lastBufferCell.y||(this._handleHover(e),this._lastBufferCell=e)}_handleHover(s){if(this._activeLine!==s.y||this._wasResized)return this._clearCurrentLink(),this._askForLink(s,!1),void(this._wasResized=!1);this._currentLink&&this._linkAtPosition(this._currentLink.link,s)||(this._clearCurrentLink(),this._askForLink(s,!0))}_askForLink(s,e){this._activeProviderReplies&&e||(this._activeProviderReplies?.forEach((a=>{a?.forEach((v=>{v.link.dispose&&v.link.dispose()}))})),this._activeProviderReplies=new Map,this._activeLine=s.y);let i=!1;for(const[a,v]of this._linkProviderService.linkProviders.entries())e?this._activeProviderReplies?.get(a)&&(i=this._checkLinkProviderResult(a,s,i)):v.provideLinks(s.y,(f=>{if(this._isMouseOut)return;const g=f?.map((c=>({link:c})));this._activeProviderReplies?.set(a,g),i=this._checkLinkProviderResult(a,s,i),this._activeProviderReplies?.size===this._linkProviderService.linkProviders.length&&this._removeIntersectingLinks(s.y,this._activeProviderReplies)}))}_removeIntersectingLinks(s,e){const i=new Set;for(let a=0;a<e.size;a++){const v=e.get(a);if(v)for(let f=0;f<v.length;f++){const g=v[f],c=g.link.range.start.y<s?0:g.link.range.start.x,m=g.link.range.end.y>s?this._bufferService.cols:g.link.range.end.x;for(let E=c;E<=m;E++){if(i.has(E)){v.splice(f--,1);break}i.add(E)}}}}_checkLinkProviderResult(s,e,i){if(!this._activeProviderReplies)return i;const a=this._activeProviderReplies.get(s);let v=!1;for(let f=0;f<s;f++)this._activeProviderReplies.has(f)&&!this._activeProviderReplies.get(f)||(v=!0);if(!v&&a){const f=a.find((g=>this._linkAtPosition(g.link,e)));f&&(i=!0,this._handleNewLink(f))}if(this._activeProviderReplies.size===this._linkProviderService.linkProviders.length&&!i)for(let f=0;f<this._activeProviderReplies.size;f++){const g=this._activeProviderReplies.get(f)?.find((c=>this._linkAtPosition(c.link,e)));if(g){i=!0,this._handleNewLink(g);break}}return i}_handleMouseDown(){this._mouseDownLink=this._currentLink}_handleMouseUp(s){if(!this._currentLink)return;const e=this._positionFromMouseEvent(s,this._element,this._mouseService);e&&this._mouseDownLink===this._currentLink&&this._linkAtPosition(this._currentLink.link,e)&&this._currentLink.link.activate(s,this._currentLink.link.text)}_clearCurrentLink(s,e){this._currentLink&&this._lastMouseEvent&&(!s||!e||this._currentLink.link.range.start.y>=s&&this._currentLink.link.range.end.y<=e)&&(this._linkLeave(this._element,this._currentLink.link,this._lastMouseEvent),this._currentLink=void 0,(0,u.disposeArray)(this._linkCacheDisposables))}_handleNewLink(s){if(!this._lastMouseEvent)return;const e=this._positionFromMouseEvent(this._lastMouseEvent,this._element,this._mouseService);e&&this._linkAtPosition(s.link,e)&&(this._currentLink=s,this._currentLink.state={decorations:{underline:s.link.decorations===void 0||s.link.decorations.underline,pointerCursor:s.link.decorations===void 0||s.link.decorations.pointerCursor},isHovered:!0},this._linkHover(this._element,s.link,this._lastMouseEvent),s.link.decorations={},Object.defineProperties(s.link.decorations,{pointerCursor:{get:()=>this._currentLink?.state?.decorations.pointerCursor,set:i=>{this._currentLink?.state&&this._currentLink.state.decorations.pointerCursor!==i&&(this._currentLink.state.decorations.pointerCursor=i,this._currentLink.state.isHovered&&this._element.classList.toggle("xterm-cursor-pointer",i))}},underline:{get:()=>this._currentLink?.state?.decorations.underline,set:i=>{this._currentLink?.state&&this._currentLink?.state?.decorations.underline!==i&&(this._currentLink.state.decorations.underline=i,this._currentLink.state.isHovered&&this._fireUnderlineEvent(s.link,i))}}}),this._linkCacheDisposables.push(this._renderService.onRenderedViewportChange((i=>{if(!this._currentLink)return;const a=i.start===0?0:i.start+1+this._bufferService.buffer.ydisp,v=this._bufferService.buffer.ydisp+1+i.end;if(this._currentLink.link.range.start.y>=a&&this._currentLink.link.range.end.y<=v&&(this._clearCurrentLink(a,v),this._lastMouseEvent)){const f=this._positionFromMouseEvent(this._lastMouseEvent,this._element,this._mouseService);f&&this._askForLink(f,!1)}}))))}_linkHover(s,e,i){this._currentLink?.state&&(this._currentLink.state.isHovered=!0,this._currentLink.state.decorations.underline&&this._fireUnderlineEvent(e,!0),this._currentLink.state.decorations.pointerCursor&&s.classList.add("xterm-cursor-pointer")),e.hover&&e.hover(i,e.text)}_fireUnderlineEvent(s,e){const i=s.range,a=this._bufferService.buffer.ydisp,v=this._createLinkUnderlineEvent(i.start.x-1,i.start.y-a-1,i.end.x,i.end.y-a-1,void 0);(e?this._onShowLinkUnderline:this._onHideLinkUnderline).fire(v)}_linkLeave(s,e,i){this._currentLink?.state&&(this._currentLink.state.isHovered=!1,this._currentLink.state.decorations.underline&&this._fireUnderlineEvent(e,!1),this._currentLink.state.decorations.pointerCursor&&s.classList.remove("xterm-cursor-pointer")),e.leave&&e.leave(i,e.text)}_linkAtPosition(s,e){const i=s.range.start.y*this._bufferService.cols+s.range.start.x,a=s.range.end.y*this._bufferService.cols+s.range.end.x,v=e.y*this._bufferService.cols+e.x;return i<=v&&v<=a}_positionFromMouseEvent(s,e,i){const a=i.getCoords(s,e,this._bufferService.cols,this._bufferService.rows);if(a)return{x:a[0],y:a[1]+this._bufferService.buffer.ydisp}}_createLinkUnderlineEvent(s,e,i,a,v){return{x1:s,y1:e,x2:i,y2:a,cols:this._bufferService.cols,fg:v}}};r.Linkifier=t=l([_(1,h.IMouseService),_(2,h.IRenderService),_(3,p.IBufferService),_(4,h.ILinkProviderService)],t)},9042:(B,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.tooMuchOutput=r.promptLabel=void 0,r.promptLabel="Terminal input",r.tooMuchOutput="Too much output to announce, navigate to rows manually to read"},3730:function(B,r,o){var l=this&&this.__decorate||function(h,t,s,e){var i,a=arguments.length,v=a<3?t:e===null?e=Object.getOwnPropertyDescriptor(t,s):e;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")v=Reflect.decorate(h,t,s,e);else for(var f=h.length-1;f>=0;f--)(i=h[f])&&(v=(a<3?i(v):a>3?i(t,s,v):i(t,s))||v);return a>3&&v&&Object.defineProperty(t,s,v),v},_=this&&this.__param||function(h,t){return function(s,e){t(s,e,h)}};Object.defineProperty(r,"__esModule",{value:!0}),r.OscLinkProvider=void 0;const n=o(511),d=o(2585);let u=r.OscLinkProvider=class{constructor(h,t,s){this._bufferService=h,this._optionsService=t,this._oscLinkService=s}provideLinks(h,t){const s=this._bufferService.buffer.lines.get(h-1);if(!s)return void t(void 0);const e=[],i=this._optionsService.rawOptions.linkHandler,a=new n.CellData,v=s.getTrimmedLength();let f=-1,g=-1,c=!1;for(let m=0;m<v;m++)if(g!==-1||s.hasContent(m)){if(s.loadCell(m,a),a.hasExtendedAttrs()&&a.extended.urlId){if(g===-1){g=m,f=a.extended.urlId;continue}c=a.extended.urlId!==f}else g!==-1&&(c=!0);if(c||g!==-1&&m===v-1){const E=this._oscLinkService.getLinkData(f)?.uri;if(E){const k={start:{x:g+1,y:h},end:{x:m+(c||m!==v-1?0:1),y:h}};let D=!1;if(!i?.allowNonHttpProtocols)try{const b=new URL(E);["http:","https:"].includes(b.protocol)||(D=!0)}catch{D=!0}D||e.push({text:E,range:k,activate:(b,x)=>i?i.activate(b,x,k):p(0,x),hover:(b,x)=>i?.hover?.(b,x,k),leave:(b,x)=>i?.leave?.(b,x,k)})}c=!1,a.hasExtendedAttrs()&&a.extended.urlId?(g=m,f=a.extended.urlId):(g=-1,f=-1)}}t(e)}};function p(h,t){if(confirm(`Do you want to navigate to ${t}?
4
4