cvc-tui 0.4.4 → 0.4.7
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/NOTICES.md +13 -0
- package/dist/app/completion.js +102 -0
- package/dist/app/createGatewayEventHandler.js +508 -0
- package/dist/app/createSlashHandler.js +101 -0
- package/dist/app/delegationStore.js +51 -0
- package/dist/app/gatewayContext.js +17 -0
- package/dist/app/historyStore.js +123 -0
- package/dist/app/inputBuffer.js +120 -0
- package/dist/app/inputSelectionStore.js +8 -0
- package/dist/app/inputStore.js +28 -0
- package/dist/app/interfaces.js +6 -0
- package/dist/app/overlayStore.js +40 -0
- package/dist/app/promptStore.js +44 -0
- package/dist/app/queueStore.js +25 -0
- package/dist/app/scroll.js +44 -0
- package/dist/app/setupHandoff.js +28 -0
- package/dist/app/slash/commands/core.js +479 -0
- package/dist/app/slash/commands/debug.js +44 -0
- package/dist/app/slash/commands/ops.js +512 -0
- package/dist/app/slash/commands/session.js +431 -0
- package/dist/app/slash/commands/setup.js +20 -0
- package/dist/app/slash/commands/toggles.js +40 -0
- package/dist/app/slash/registry.js +18 -0
- package/dist/app/slash/types.js +1 -0
- package/dist/app/spawnHistoryStore.js +105 -0
- package/dist/app/turnController.js +650 -0
- package/dist/app/turnStore.js +48 -0
- package/dist/app/uiStore.js +36 -0
- package/dist/app/useComposerState.js +265 -0
- package/dist/app/useConfigSync.js +144 -0
- package/dist/app/useInputHandlers.js +403 -0
- package/dist/app/useLongRunToolCharms.js +50 -0
- package/dist/app/useMainApp.js +638 -0
- package/dist/app/useSessionLifecycle.js +175 -0
- package/dist/app/useSubmission.js +287 -0
- package/dist/app.js +15 -0
- package/dist/banner.js +63 -0
- package/dist/components/agentsOverlay.js +474 -0
- package/dist/components/appChrome.js +252 -0
- package/dist/components/appLayout.js +122 -0
- package/dist/components/appOverlays.js +65 -0
- package/dist/components/branding.js +97 -0
- package/dist/components/fpsOverlay.js +22 -0
- package/dist/components/helpHint.js +21 -0
- package/dist/components/markdown.js +501 -0
- package/dist/components/maskedPrompt.js +12 -0
- package/dist/components/messageLine.js +82 -0
- package/dist/components/modelPicker.js +254 -0
- package/dist/components/overlayControls.js +30 -0
- package/dist/components/overlays/confirmPrompt.js +25 -0
- package/dist/components/overlays/helpOverlay.js +76 -0
- package/dist/components/overlays/historySearch.js +49 -0
- package/dist/components/overlays/modelPicker.js +60 -0
- package/dist/components/overlays/overlayUtils.js +19 -0
- package/dist/components/overlays/secretPrompt.js +36 -0
- package/dist/components/overlays/sessionPicker.js +93 -0
- package/dist/components/overlays/skillsHub.js +71 -0
- package/dist/components/prompts.js +95 -0
- package/dist/components/queuedMessages.js +24 -0
- package/dist/components/sessionPicker.js +130 -0
- package/dist/components/skillsHub.js +165 -0
- package/dist/components/streamingAssistant.js +35 -0
- package/dist/components/streamingMarkdown.js +144 -0
- package/dist/components/textInput.js +794 -0
- package/dist/components/themed.js +12 -0
- package/dist/components/thinking.js +496 -0
- package/dist/components/todoPanel.js +40 -0
- package/dist/components/transcript.js +22 -0
- package/dist/config/env.js +18 -0
- package/dist/config/limits.js +22 -0
- package/dist/config/timing.js +25 -0
- package/dist/content/charms.js +5 -0
- package/dist/content/faces.js +21 -0
- package/dist/content/fortunes.js +29 -0
- package/dist/content/hotkeys.js +38 -0
- package/dist/content/placeholders.js +15 -0
- package/dist/content/setup.js +14 -0
- package/dist/content/verbs.js +41 -0
- package/dist/domain/details.js +53 -0
- package/dist/domain/messages.js +63 -0
- package/dist/domain/paths.js +16 -0
- package/dist/domain/providers.js +11 -0
- package/dist/domain/roles.js +6 -0
- package/dist/domain/slash.js +11 -0
- package/dist/domain/usage.js +1 -0
- package/dist/domain/viewport.js +33 -0
- package/dist/entry.js +64 -70236
- package/dist/gateway/client.js +312 -0
- package/dist/gatewayClient.js +574 -0
- package/dist/gatewayTypes.js +1 -0
- package/dist/hooks/useCompletion.js +86 -0
- package/dist/hooks/useGitBranch.js +58 -0
- package/dist/hooks/useInputHistory.js +12 -0
- package/dist/hooks/useQueue.js +57 -0
- package/dist/hooks/useVirtualHistory.js +401 -0
- package/dist/lib/circularBuffer.js +43 -0
- package/dist/lib/clipboard.js +126 -0
- package/dist/lib/editor.js +41 -0
- package/dist/lib/editor.test.js +58 -0
- package/dist/lib/emoji.js +49 -0
- package/dist/lib/externalCli.js +11 -0
- package/dist/lib/forceTruecolor.js +26 -0
- package/dist/lib/fpsStore.js +36 -0
- package/dist/lib/gracefulExit.js +29 -0
- package/dist/lib/history.js +69 -0
- package/dist/lib/inputMetrics.js +143 -0
- package/dist/lib/liveProgress.js +51 -0
- package/dist/lib/liveProgress.test.js +89 -0
- package/dist/lib/localSessionInfo.js +116 -0
- package/dist/lib/mathUnicode.js +685 -0
- package/dist/lib/memory.js +123 -0
- package/dist/lib/memoryMonitor.js +76 -0
- package/dist/lib/messages.js +3 -0
- package/dist/lib/messages.test.js +25 -0
- package/dist/lib/osc52.js +53 -0
- package/dist/lib/perfPane.js +94 -0
- package/dist/lib/platform.js +312 -0
- package/dist/lib/precisionWheel.js +25 -0
- package/dist/lib/react-devtools-stub.js +12 -0
- package/dist/lib/reasoning.js +39 -0
- package/dist/lib/rpc.js +26 -0
- package/dist/lib/subagentTree.js +287 -0
- package/dist/lib/syntax.js +89 -0
- package/dist/lib/terminalModes.js +46 -0
- package/dist/lib/terminalParity.js +48 -0
- package/dist/lib/terminalSetup.js +321 -0
- package/dist/lib/text.js +203 -0
- package/dist/lib/text.test.js +18 -0
- package/dist/lib/todo.js +2 -0
- package/dist/lib/todo.test.js +22 -0
- package/dist/lib/viewportStore.js +82 -0
- package/dist/lib/virtualHeights.js +61 -0
- package/dist/lib/wheelAccel.js +143 -0
- package/dist/protocol/interpolation.js +4 -0
- package/dist/protocol/paste.js +3 -0
- package/dist/theme.js +398 -0
- package/dist/types.js +1 -0
- package/dist/vendor/cvc-ink/dist/entry-exports.js +52737 -0
- package/dist/vendor/cvc-ink/index.js +1 -0
- package/dist/vendor/cvc-ink/package.json +9 -0
- package/dist/vendor/cvc-ink/text-input.js +1 -0
- package/package.json +9 -9
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
// Real subprocess GatewayClient.
|
|
2
|
+
//
|
|
3
|
+
// Spawns `python -m cvc.tui_gateway.entry` (or attaches to a configured
|
|
4
|
+
// `cvc-tui-gateway` binary), speaks newline-delimited JSON-RPC 2.0 over
|
|
5
|
+
// stdio, and surfaces:
|
|
6
|
+
// * request/response RPCs — `send(method, params)` → Promise<result>
|
|
7
|
+
// * server-push events — `on('event', ev => …)` (e.g. chat.delta,
|
|
8
|
+
// chat.done, chat.start)
|
|
9
|
+
// * stderr lines — `on('stderr', line => …)`
|
|
10
|
+
// * lifecycle — `on('exit', code => …)`,
|
|
11
|
+
// `on('connected', info => …)`
|
|
12
|
+
//
|
|
13
|
+
// The protocol contract matches the Python gateway in cvc/tui_gateway/:
|
|
14
|
+
// request : {jsonrpc:'2.0', id, method, params}
|
|
15
|
+
// response: {jsonrpc:'2.0', id, result|error}
|
|
16
|
+
// event : {jsonrpc:'2.0', method, params} (no id)
|
|
17
|
+
//
|
|
18
|
+
// During `connect()` we issue a `session.info` handshake; the resolved
|
|
19
|
+
// session payload (version, model, provider, session_id, cwd) is emitted
|
|
20
|
+
// as `connected` and also returned from connect().
|
|
21
|
+
//
|
|
22
|
+
// Tests inject a fake transport via the `transport` constructor option.
|
|
23
|
+
// In production the transport is a child_process spawned with
|
|
24
|
+
// `stdio: ['pipe','pipe','pipe']`.
|
|
25
|
+
import { spawn } from 'node:child_process';
|
|
26
|
+
import { EventEmitter } from 'node:events';
|
|
27
|
+
import { existsSync } from 'node:fs';
|
|
28
|
+
import { delimiter, resolve } from 'node:path';
|
|
29
|
+
import { createInterface } from 'node:readline';
|
|
30
|
+
const DEFAULT_TIMEOUT_MS = Math.max(10000, parseInt(process.env.CVC_TUI_RPC_TIMEOUT_MS ?? '120000', 10) || 120000);
|
|
31
|
+
const STARTUP_TIMEOUT_MS = Math.max(3000, parseInt(process.env.CVC_TUI_STARTUP_TIMEOUT_MS ?? '15000', 10) || 15000);
|
|
32
|
+
const resolvePython = (root) => {
|
|
33
|
+
const configured = process.env.CVC_PYTHON?.trim() || process.env.PYTHON?.trim();
|
|
34
|
+
if (configured)
|
|
35
|
+
return configured;
|
|
36
|
+
const venv = process.env.VIRTUAL_ENV?.trim();
|
|
37
|
+
const candidates = [
|
|
38
|
+
venv && resolve(venv, 'bin/python'),
|
|
39
|
+
venv && resolve(venv, 'bin/python3'),
|
|
40
|
+
venv && resolve(venv, 'Scripts/python.exe'),
|
|
41
|
+
resolve(root, '.venv/bin/python'),
|
|
42
|
+
resolve(root, '.venv/bin/python3'),
|
|
43
|
+
resolve(root, 'venv/bin/python'),
|
|
44
|
+
resolve(root, 'venv/bin/python3'),
|
|
45
|
+
];
|
|
46
|
+
for (const c of candidates) {
|
|
47
|
+
if (c && existsSync(c))
|
|
48
|
+
return c;
|
|
49
|
+
}
|
|
50
|
+
return process.platform === 'win32' ? 'python' : 'python3';
|
|
51
|
+
};
|
|
52
|
+
const findCvcRoot = () => {
|
|
53
|
+
// entry.tsx is typically at <root>/cvc-tui/src/entry.tsx
|
|
54
|
+
// dist/entry.js at <root>/cvc-tui/dist/entry.js
|
|
55
|
+
// Walk up looking for a "cvc/tui_gateway" sibling.
|
|
56
|
+
if (process.env.CVC_PYTHON_SRC_ROOT)
|
|
57
|
+
return process.env.CVC_PYTHON_SRC_ROOT;
|
|
58
|
+
let dir = process.cwd();
|
|
59
|
+
for (let i = 0; i < 6; i++) {
|
|
60
|
+
if (existsSync(resolve(dir, 'cvc', 'tui_gateway', 'entry.py')))
|
|
61
|
+
return dir;
|
|
62
|
+
const parent = resolve(dir, '..');
|
|
63
|
+
if (parent === dir)
|
|
64
|
+
break;
|
|
65
|
+
dir = parent;
|
|
66
|
+
}
|
|
67
|
+
return process.cwd();
|
|
68
|
+
};
|
|
69
|
+
export class GatewayClient extends EventEmitter {
|
|
70
|
+
opts;
|
|
71
|
+
proc = null;
|
|
72
|
+
transport = null;
|
|
73
|
+
stdoutRl = null;
|
|
74
|
+
stderrRl = null;
|
|
75
|
+
pending = new Map();
|
|
76
|
+
reqId = 0;
|
|
77
|
+
session = null;
|
|
78
|
+
connected = false;
|
|
79
|
+
exited = false;
|
|
80
|
+
constructor(opts = {}) {
|
|
81
|
+
super();
|
|
82
|
+
this.setMaxListeners(0);
|
|
83
|
+
this.opts = opts;
|
|
84
|
+
}
|
|
85
|
+
/** Spawn (or attach) the gateway and run the session.info handshake. */
|
|
86
|
+
async connect() {
|
|
87
|
+
if (this.connected) {
|
|
88
|
+
return this.session;
|
|
89
|
+
}
|
|
90
|
+
if (this.opts.transport) {
|
|
91
|
+
this.transport = this.opts.transport;
|
|
92
|
+
this.wireTransport();
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
this.spawnGateway();
|
|
96
|
+
}
|
|
97
|
+
// Handshake — single session.info round-trip with bounded timeout.
|
|
98
|
+
const info = (await this.send('session.info', {}, { timeoutMs: STARTUP_TIMEOUT_MS }));
|
|
99
|
+
this.session = info;
|
|
100
|
+
this.connected = true;
|
|
101
|
+
this.emit('connected', info);
|
|
102
|
+
return info;
|
|
103
|
+
}
|
|
104
|
+
spawnGateway() {
|
|
105
|
+
const root = this.opts.cwd ?? findCvcRoot();
|
|
106
|
+
const python = this.opts.python ?? resolvePython(root);
|
|
107
|
+
const env = { ...process.env };
|
|
108
|
+
const pyPath = env.PYTHONPATH?.trim();
|
|
109
|
+
env.PYTHONPATH = pyPath ? `${root}${delimiter}${pyPath}` : root;
|
|
110
|
+
const proc = spawn(python, ['-m', 'cvc.tui_gateway.entry'], {
|
|
111
|
+
cwd: root,
|
|
112
|
+
env,
|
|
113
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
114
|
+
});
|
|
115
|
+
this.proc = proc;
|
|
116
|
+
this.transport = {
|
|
117
|
+
stdin: proc.stdin,
|
|
118
|
+
stdout: proc.stdout,
|
|
119
|
+
stderr: proc.stderr,
|
|
120
|
+
kill: () => proc.kill(),
|
|
121
|
+
on: (event, cb) => {
|
|
122
|
+
proc.on(event, cb);
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
proc.on('error', (err) => {
|
|
126
|
+
this.emit('stderr', `[spawn] ${err.message}`);
|
|
127
|
+
this.handleExit(1);
|
|
128
|
+
});
|
|
129
|
+
proc.on('exit', (code) => this.handleExit(code));
|
|
130
|
+
this.wireTransport();
|
|
131
|
+
}
|
|
132
|
+
wireTransport() {
|
|
133
|
+
const t = this.transport;
|
|
134
|
+
this.stdoutRl = createInterface({ input: t.stdout });
|
|
135
|
+
this.stdoutRl.on('line', (line) => this.handleLine(line));
|
|
136
|
+
if (t.stderr) {
|
|
137
|
+
this.stderrRl = createInterface({ input: t.stderr });
|
|
138
|
+
this.stderrRl.on('line', (line) => {
|
|
139
|
+
const trimmed = line.trim();
|
|
140
|
+
if (!trimmed)
|
|
141
|
+
return;
|
|
142
|
+
this.emit('stderr', trimmed);
|
|
143
|
+
if (this.opts.forwardStderr) {
|
|
144
|
+
process.stderr.write(`[cvc-gateway] ${trimmed}\n`);
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
// Lifecycle: injected transports surface exit through their own `on()`.
|
|
149
|
+
// The real subprocess path also calls handleExit via proc.on('exit')
|
|
150
|
+
// registered in spawnGateway, so guard with `exited` to dedupe.
|
|
151
|
+
t.on?.('exit', (code) => this.handleExit(code));
|
|
152
|
+
}
|
|
153
|
+
handleLine(raw) {
|
|
154
|
+
const line = raw.trim();
|
|
155
|
+
if (!line)
|
|
156
|
+
return;
|
|
157
|
+
let frame;
|
|
158
|
+
try {
|
|
159
|
+
frame = JSON.parse(line);
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
this.emit('stderr', `[protocol] malformed stdout: ${line.slice(0, 200)}`);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
this.dispatch(frame);
|
|
166
|
+
}
|
|
167
|
+
dispatch(frame) {
|
|
168
|
+
// Response — has `id` and (`result` xor `error`).
|
|
169
|
+
if ('id' in frame && (('result' in frame) || ('error' in frame))) {
|
|
170
|
+
const id = String(frame.id);
|
|
171
|
+
const p = this.pending.get(id);
|
|
172
|
+
if (!p)
|
|
173
|
+
return;
|
|
174
|
+
this.pending.delete(id);
|
|
175
|
+
clearTimeout(p.timer);
|
|
176
|
+
if ('error' in frame && frame.error) {
|
|
177
|
+
const err = frame.error;
|
|
178
|
+
p.reject(new Error(`${p.method}: ${err.message ?? 'rpc error'} (code=${err.code ?? '?'})`));
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
p.resolve(frame.result);
|
|
182
|
+
}
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
// Event — has `method` but no `id`.
|
|
186
|
+
if (typeof frame.method === 'string' && !('id' in frame)) {
|
|
187
|
+
const ev = {
|
|
188
|
+
method: frame.method,
|
|
189
|
+
params: frame.params ?? {},
|
|
190
|
+
};
|
|
191
|
+
this.emit('event', ev);
|
|
192
|
+
this.emit(`event:${ev.method}`, ev.params);
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
// Anything else is malformed — log but don't crash.
|
|
196
|
+
this.emit('stderr', `[protocol] unexpected frame: ${JSON.stringify(frame).slice(0, 200)}`);
|
|
197
|
+
}
|
|
198
|
+
handleExit(code) {
|
|
199
|
+
if (this.exited)
|
|
200
|
+
return;
|
|
201
|
+
this.exited = true;
|
|
202
|
+
for (const p of Array.from(this.pending.values())) {
|
|
203
|
+
clearTimeout(p.timer);
|
|
204
|
+
p.reject(new Error(`gateway exited (code=${code ?? 'null'})`));
|
|
205
|
+
}
|
|
206
|
+
this.pending.clear();
|
|
207
|
+
this.stdoutRl?.close();
|
|
208
|
+
this.stderrRl?.close();
|
|
209
|
+
this.connected = false;
|
|
210
|
+
this.emit('exit', code);
|
|
211
|
+
}
|
|
212
|
+
/** Issue a JSON-RPC request and await its response. */
|
|
213
|
+
send(method, params = {}, opts = {}) {
|
|
214
|
+
if (!this.transport) {
|
|
215
|
+
return Promise.reject(new Error('GatewayClient: not connected'));
|
|
216
|
+
}
|
|
217
|
+
if (this.exited) {
|
|
218
|
+
return Promise.reject(new Error('GatewayClient: gateway has exited'));
|
|
219
|
+
}
|
|
220
|
+
const id = String(++this.reqId);
|
|
221
|
+
const frame = JSON.stringify({ jsonrpc: '2.0', id, method, params }) + '\n';
|
|
222
|
+
const timeoutMs = opts.timeoutMs ?? this.opts.defaultTimeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
223
|
+
return new Promise((resolve, reject) => {
|
|
224
|
+
const timer = setTimeout(() => {
|
|
225
|
+
this.pending.delete(id);
|
|
226
|
+
reject(new Error(`${method}: timeout after ${timeoutMs}ms`));
|
|
227
|
+
}, timeoutMs);
|
|
228
|
+
this.pending.set(id, { id, method, resolve, reject, timer });
|
|
229
|
+
try {
|
|
230
|
+
this.transport.stdin.write(frame);
|
|
231
|
+
}
|
|
232
|
+
catch (err) {
|
|
233
|
+
clearTimeout(timer);
|
|
234
|
+
this.pending.delete(id);
|
|
235
|
+
reject(err);
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Streaming chat helper. Wires `chat.start` / `chat.delta` / `chat.done`
|
|
241
|
+
* to the supplied handlers, then issues `chat.send`. Resolves with the
|
|
242
|
+
* `chat.done` summary; rejects on RPC error.
|
|
243
|
+
*/
|
|
244
|
+
async sendChat(params, handlers = {}) {
|
|
245
|
+
const onStart = (p) => {
|
|
246
|
+
handlers.onStart?.({ provider: String(p.provider ?? ''), model: String(p.model ?? '') });
|
|
247
|
+
};
|
|
248
|
+
const onDelta = (p) => {
|
|
249
|
+
const t = typeof p.text === 'string' ? p.text : '';
|
|
250
|
+
if (t)
|
|
251
|
+
handlers.onDelta?.(t);
|
|
252
|
+
};
|
|
253
|
+
const onDone = (p) => {
|
|
254
|
+
handlers.onDone?.(p);
|
|
255
|
+
};
|
|
256
|
+
this.on('event:chat.start', onStart);
|
|
257
|
+
this.on('event:chat.delta', onDelta);
|
|
258
|
+
this.on('event:chat.done', onDone);
|
|
259
|
+
try {
|
|
260
|
+
const summary = (await this.send('chat.send', params));
|
|
261
|
+
return summary;
|
|
262
|
+
}
|
|
263
|
+
catch (err) {
|
|
264
|
+
handlers.onError?.(err);
|
|
265
|
+
throw err;
|
|
266
|
+
}
|
|
267
|
+
finally {
|
|
268
|
+
this.off('event:chat.start', onStart);
|
|
269
|
+
this.off('event:chat.delta', onDelta);
|
|
270
|
+
this.off('event:chat.done', onDone);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
/** Resolved session info from the handshake. */
|
|
274
|
+
getSession() {
|
|
275
|
+
return this.session;
|
|
276
|
+
}
|
|
277
|
+
isConnected() {
|
|
278
|
+
return this.connected && !this.exited;
|
|
279
|
+
}
|
|
280
|
+
/** Best-effort orderly shutdown — issues system.shutdown and kills proc. */
|
|
281
|
+
async close() {
|
|
282
|
+
if (!this.transport || this.exited)
|
|
283
|
+
return;
|
|
284
|
+
try {
|
|
285
|
+
await this.send('system.shutdown', {}, { timeoutMs: 1000 });
|
|
286
|
+
}
|
|
287
|
+
catch {
|
|
288
|
+
// ignore — the gateway may already be tearing down.
|
|
289
|
+
}
|
|
290
|
+
try {
|
|
291
|
+
this.transport.kill?.();
|
|
292
|
+
}
|
|
293
|
+
catch {
|
|
294
|
+
// ignore
|
|
295
|
+
}
|
|
296
|
+
if (this.proc && !this.proc.killed) {
|
|
297
|
+
try {
|
|
298
|
+
this.proc.kill();
|
|
299
|
+
}
|
|
300
|
+
catch {
|
|
301
|
+
// ignore
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
this.handleExit(0);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Render an OSC 8 hyperlink escape sequence: `\x1b]8;;URL\x1b\\TEXT\x1b]8;;\x1b\\`.
|
|
309
|
+
* Re-exported here for tests + downstream consumers that don't want a hard
|
|
310
|
+
* dependency on the markdown component.
|
|
311
|
+
*/
|
|
312
|
+
export const osc8Link = (url, label) => `\x1b]8;;${url}\x1b\\${label}\x1b]8;;\x1b\\`;
|