@idl3/claude-control 1.0.1 → 1.3.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/lib/codex.js +597 -0
- package/lib/config.js +36 -3
- package/lib/pane-registry.js +23 -10
- package/lib/sessions.js +90 -14
- package/lib/transcript.js +5 -4
- package/package.json +1 -1
- package/server.js +127 -13
- package/web/dist/assets/{core-CEtbx-dx.js → core-C29-1O9j.js} +1 -1
- package/web/dist/assets/index-CT-y6LU4.css +1 -0
- package/web/dist/assets/index-DzIDTXLS.js +103 -0
- package/web/dist/index.html +2 -2
- package/web/dist/assets/index-CjJtW-Kv.css +0 -1
- package/web/dist/assets/index-DFru8Gzx.js +0 -103
package/lib/pane-registry.js
CHANGED
|
@@ -55,31 +55,44 @@ export async function readPaneRegistry(dir = PANES_DIR) {
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
/**
|
|
58
|
-
* Remove registry files
|
|
59
|
-
*
|
|
60
|
-
*
|
|
58
|
+
* Remove registry files that have gone stale. A pin is stale iff its transcript
|
|
59
|
+
* file no longer exists — the SAME rule readPaneRegistry uses to drop an entry
|
|
60
|
+
* in memory. This is the only safe deletion trigger.
|
|
61
61
|
*
|
|
62
|
-
*
|
|
62
|
+
* It deliberately does NOT use the live tmux pane set. That scan flickers
|
|
63
|
+
* (transient `list-panes` hiccups, a session momentarily not enumerated on a
|
|
64
|
+
* busy socket), and a flaky "pane absent" reading looks identical to a genuine
|
|
65
|
+
* pane close — so keying deletion off it wrongly nukes pins for panes that are
|
|
66
|
+
* very much alive (the long-lived window-1 binding kept vanishing this way).
|
|
67
|
+
*
|
|
68
|
+
* A pin for a closed pane whose transcript still lingers is harmless: there is
|
|
69
|
+
* no live pane to bind it to, and if the pane id is later reused the hook
|
|
70
|
+
* overwrites the file. It self-expires here once its transcript is removed.
|
|
71
|
+
*
|
|
72
|
+
* @param {string} [dir] Override registry dir (tests).
|
|
63
73
|
* @returns {Promise<void>}
|
|
64
74
|
*/
|
|
65
|
-
export async function gcPaneRegistry(
|
|
75
|
+
export async function gcPaneRegistry(dir = PANES_DIR) {
|
|
66
76
|
let entries;
|
|
67
77
|
try {
|
|
68
|
-
entries = await fsp.readdir(
|
|
78
|
+
entries = await fsp.readdir(dir);
|
|
69
79
|
} catch {
|
|
70
80
|
return;
|
|
71
81
|
}
|
|
82
|
+
|
|
72
83
|
await Promise.all(
|
|
73
84
|
entries
|
|
74
85
|
.filter((f) => f.endsWith('.json'))
|
|
75
86
|
.map(async (f) => {
|
|
76
87
|
try {
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
88
|
+
const filePath = path.join(dir, f);
|
|
89
|
+
const rec = JSON.parse(await fsp.readFile(filePath, 'utf8'));
|
|
90
|
+
if (!rec || typeof rec.transcriptPath !== 'string') return;
|
|
91
|
+
if (!fs.existsSync(rec.transcriptPath)) {
|
|
92
|
+
await fsp.rm(filePath, { force: true }); // transcript gone → stale
|
|
80
93
|
}
|
|
81
94
|
} catch {
|
|
82
|
-
// ignore
|
|
95
|
+
// ignore unreadable/partial files
|
|
83
96
|
}
|
|
84
97
|
}),
|
|
85
98
|
);
|
package/lib/sessions.js
CHANGED
|
@@ -18,11 +18,18 @@ import { parsePanePrompt } from './prompt.js';
|
|
|
18
18
|
import { assignTranscripts, parseEtime, fingerprintScore, shouldRebind } from './match.js';
|
|
19
19
|
import { pinKey } from './pins.js';
|
|
20
20
|
import { readPaneRegistry, gcPaneRegistry } from './pane-registry.js';
|
|
21
|
+
import {
|
|
22
|
+
matchesProcess as codexMatchesProcess,
|
|
23
|
+
buildTranscriptIndex as buildCodexIndex,
|
|
24
|
+
parseTuiStatus as parseCodexTuiStatus,
|
|
25
|
+
} from './codex.js';
|
|
21
26
|
|
|
22
27
|
const execFile = promisify(_execFile);
|
|
23
28
|
|
|
24
29
|
// Matches Claude Code's executable basename (e.g. /Users/x/.local/bin/claude).
|
|
25
30
|
const CLAUDE_COMM_RE = /(^|\/)claude$/;
|
|
31
|
+
// Matches Codex CLI executable basename.
|
|
32
|
+
const CODEX_COMM_RE = /(^|\/)codex$/;
|
|
26
33
|
|
|
27
34
|
// A pane is a Claude Code session when its process title is the Claude version
|
|
28
35
|
// (e.g. "2.1.162") — shells report zsh/bash/etc. A linked transcript also counts.
|
|
@@ -337,11 +344,12 @@ export async function listRecentTranscripts({ projectsRoot, limit = 60 }) {
|
|
|
337
344
|
|
|
338
345
|
export class SessionRegistry extends EventEmitter {
|
|
339
346
|
/**
|
|
340
|
-
* @param {{ projectsRoot: string, tmux: object, debounceMs?: number }} opts
|
|
347
|
+
* @param {{ projectsRoot: string, codexSessionsRoot?: string, tmux: object, debounceMs?: number }} opts
|
|
341
348
|
*/
|
|
342
|
-
constructor({ projectsRoot, tmux, debounceMs = 1000 } = {}) {
|
|
349
|
+
constructor({ projectsRoot, codexSessionsRoot, tmux, debounceMs = 1000 } = {}) {
|
|
343
350
|
super();
|
|
344
351
|
this._projectsRoot = projectsRoot;
|
|
352
|
+
this._codexSessionsRoot = codexSessionsRoot;
|
|
345
353
|
this._tmux = tmux;
|
|
346
354
|
this._debounceMs = debounceMs;
|
|
347
355
|
|
|
@@ -451,21 +459,28 @@ export class SessionRegistry extends EventEmitter {
|
|
|
451
459
|
return true;
|
|
452
460
|
});
|
|
453
461
|
|
|
454
|
-
// Classify every pane by its process subtree (a `claude` descendant)
|
|
455
|
-
// its
|
|
462
|
+
// Classify every pane by its process subtree (a `claude` or `codex` descendant)
|
|
463
|
+
// and get its start time in one ps snapshot. Falls back to the cmd heuristic
|
|
456
464
|
// only when ps is unavailable.
|
|
457
465
|
const paneProc = await this._buildPaneProc(panes);
|
|
458
466
|
const isClaudePane = (p) => {
|
|
459
467
|
const info = paneProc.get(p.target);
|
|
460
468
|
return info ? info.isClaude : isClaudeCmd(p.cmd);
|
|
461
469
|
};
|
|
470
|
+
const paneKind = (p) => {
|
|
471
|
+
const info = paneProc.get(p.target);
|
|
472
|
+
if (info?.kind) return info.kind;
|
|
473
|
+
if (isClaudeCmd(p.cmd)) return 'claude';
|
|
474
|
+
if (codexMatchesProcess(p.cmd)) return 'codex';
|
|
475
|
+
return 'terminal';
|
|
476
|
+
};
|
|
462
477
|
const claudePanes = panes.filter(isClaudePane);
|
|
463
478
|
|
|
464
479
|
// The exact pane→transcript map authored by the SessionStart hook. This is
|
|
465
480
|
// the deterministic binding; everything below is fallback for panes with no
|
|
466
481
|
// hook record (sessions started before the hook was installed).
|
|
467
482
|
const paneReg = await readPaneRegistry();
|
|
468
|
-
gcPaneRegistry(
|
|
483
|
+
gcPaneRegistry().catch(() => {}); // prunes only pins whose transcript is gone
|
|
469
484
|
|
|
470
485
|
// Manual pins win first: a pinned pane is force-bound to its transcript and
|
|
471
486
|
// that transcript is removed from the auto-matcher pool. Pins are keyed by
|
|
@@ -564,9 +579,56 @@ export class SessionRegistry extends EventEmitter {
|
|
|
564
579
|
}
|
|
565
580
|
// ── End self-heal ─────────────────────────────────────────────────────────
|
|
566
581
|
|
|
582
|
+
// ── Codex pane → transcript matching ────────────────────────────────────
|
|
583
|
+
// Discover Codex session transcripts and match them to Codex panes.
|
|
584
|
+
// The Claude assignment above is computed first and left untouched;
|
|
585
|
+
// codex results are merged in after.
|
|
586
|
+
const codexPanes = panes.filter((p) => paneKind(p) === 'codex');
|
|
587
|
+
if (codexPanes.length > 0) {
|
|
588
|
+
const codexIndex = await buildCodexIndex({ codexSessionsRoot: this._codexSessionsRoot });
|
|
589
|
+
const codexCandidates = [];
|
|
590
|
+
for (const rec of codexIndex.byCwd.values()) {
|
|
591
|
+
codexCandidates.push({
|
|
592
|
+
transcriptPath: rec.transcriptPath,
|
|
593
|
+
cwd: rec.cwd,
|
|
594
|
+
projectDir: null, // triggers isCwdConsistent scope fallback in match.js
|
|
595
|
+
birthtimeMs: rec.mtime,
|
|
596
|
+
mtimeMs: rec.mtime,
|
|
597
|
+
lastActivityMs: rec.lastActivityMs ?? rec.mtime,
|
|
598
|
+
customTitle: rec.customTitle,
|
|
599
|
+
aiTitle: rec.aiTitle,
|
|
600
|
+
recentText: null,
|
|
601
|
+
// Pass through for later session assembly
|
|
602
|
+
sessionId: rec.sessionId,
|
|
603
|
+
lastActivity: rec.lastActivity,
|
|
604
|
+
model: rec.model,
|
|
605
|
+
transcriptPending: rec.transcriptPending,
|
|
606
|
+
pendingToolUseId: rec.pendingToolUseId,
|
|
607
|
+
pendingQuestion: rec.pendingQuestion,
|
|
608
|
+
agentType: rec.agentType,
|
|
609
|
+
usagePct: rec.usagePct ?? null,
|
|
610
|
+
usageWindowMin: rec.usageWindowMin ?? null,
|
|
611
|
+
mtime: rec.mtime,
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
const codexPaneInputs = codexPanes.map((p) => ({
|
|
615
|
+
target: p.target,
|
|
616
|
+
windowName: p.windowName,
|
|
617
|
+
cwd: p.cwd,
|
|
618
|
+
projectDir: null,
|
|
619
|
+
procStartMs: paneProc.get(p.target)?.startMs ?? null,
|
|
620
|
+
capturedText: this._paneTextCache.get(p.target) ?? null,
|
|
621
|
+
}));
|
|
622
|
+
const codexAssignment = assignTranscripts(codexPaneInputs, codexCandidates);
|
|
623
|
+
for (const [t, rec] of codexAssignment) assignment.set(t, rec);
|
|
624
|
+
}
|
|
625
|
+
// ── End Codex matching ───────────────────────────────────────────────────
|
|
626
|
+
|
|
567
627
|
const sessions = panes.map((win) => {
|
|
568
628
|
const isClaude = isClaudePane(win);
|
|
569
|
-
const
|
|
629
|
+
const kind = paneKind(win);
|
|
630
|
+
const hasTranscript = kind === 'claude' || kind === 'codex';
|
|
631
|
+
const transcript = hasTranscript ? assignment.get(win.target) ?? null : null;
|
|
570
632
|
const isPinned = pinnedByTarget.has(win.target);
|
|
571
633
|
const id = win.target;
|
|
572
634
|
// Pending = subscribed-tailer pending (live modal) OR transcript-derived
|
|
@@ -578,7 +640,11 @@ export class SessionRegistry extends EventEmitter {
|
|
|
578
640
|
!!transcript?.transcriptPending ||
|
|
579
641
|
!!panePrompt?.pending;
|
|
580
642
|
const title = transcript?.customTitle || transcript?.aiTitle || null;
|
|
581
|
-
|
|
643
|
+
// Read the polled TUI status (model/ctx) for Claude AND Codex. Codex's
|
|
644
|
+
// _pollCtx populates _ctxMap with its model (ctxPct stays null — Codex's
|
|
645
|
+
// TUI has no context %). Without this, the assembly would discard the
|
|
646
|
+
// polled codex model and the rail would show no model for codex rows.
|
|
647
|
+
const ctx = isClaude || kind === 'codex' ? this._ctxMap.get(win.target) || {} : {};
|
|
582
648
|
|
|
583
649
|
return {
|
|
584
650
|
id,
|
|
@@ -603,12 +669,14 @@ export class SessionRegistry extends EventEmitter {
|
|
|
603
669
|
pendingQuestion: transcript?.pendingQuestion ?? panePrompt?.question ?? null,
|
|
604
670
|
cmd: win.cmd,
|
|
605
671
|
isClaude,
|
|
606
|
-
kind
|
|
672
|
+
kind,
|
|
607
673
|
ccShell: !!win.ccShell, // a composer >_ sister shell pane
|
|
608
674
|
|
|
609
675
|
model: ctx.model || prettyModel(transcript?.model) || null,
|
|
610
676
|
ctxPct: ctx.ctxPct ?? null,
|
|
611
|
-
thinking: isClaude ? this._thinkingMap.get(win.target) ?? false : false,
|
|
677
|
+
thinking: (isClaude || kind === 'codex') ? this._thinkingMap.get(win.target) ?? false : false,
|
|
678
|
+
usagePct: transcript?.usagePct ?? null,
|
|
679
|
+
usageWindowMin: transcript?.usageWindowMin ?? null,
|
|
612
680
|
};
|
|
613
681
|
});
|
|
614
682
|
|
|
@@ -634,7 +702,11 @@ export class SessionRegistry extends EventEmitter {
|
|
|
634
702
|
if (!this._tmux.isValidTarget(s.target)) return;
|
|
635
703
|
try {
|
|
636
704
|
const cap = await this._tmux.capturePane(s.target, 8);
|
|
637
|
-
|
|
705
|
+
// Codex panes use the codex header/footer parser (the Claude tui.js
|
|
706
|
+
// parser doesn't match codex's "model:"/footer formats). Codex has no
|
|
707
|
+
// ctx% in its TUI, so ctxPct stays null for codex (no faked value).
|
|
708
|
+
const { ctxPct, model } =
|
|
709
|
+
s.kind === 'codex' ? parseCodexTuiStatus(cap) : parseTuiStatus(cap);
|
|
638
710
|
this._ctxMap.set(s.target, { ctxPct, model });
|
|
639
711
|
// Merge into the live session object without a full rebuild.
|
|
640
712
|
if (ctxPct !== null) s.ctxPct = ctxPct;
|
|
@@ -854,7 +926,7 @@ export class SessionRegistry extends EventEmitter {
|
|
|
854
926
|
}
|
|
855
927
|
|
|
856
928
|
const now = Date.now();
|
|
857
|
-
// BFS from the pane shell pid for a `claude` descendant; return its start.
|
|
929
|
+
// BFS from the pane shell pid for a `claude` or `codex` descendant; return its start.
|
|
858
930
|
const findClaude = (rootPid) => {
|
|
859
931
|
const queue = [rootPid];
|
|
860
932
|
const seen = new Set();
|
|
@@ -865,15 +937,19 @@ export class SessionRegistry extends EventEmitter {
|
|
|
865
937
|
const meta = info.get(pid);
|
|
866
938
|
if (meta && CLAUDE_COMM_RE.test(meta.comm)) {
|
|
867
939
|
const sec = parseEtime(meta.etime);
|
|
868
|
-
return { isClaude: true, startMs: sec == null ? null : now - sec * 1000 };
|
|
940
|
+
return { isClaude: true, isCodex: false, kind: 'claude', startMs: sec == null ? null : now - sec * 1000 };
|
|
941
|
+
}
|
|
942
|
+
if (meta && CODEX_COMM_RE.test(meta.comm)) {
|
|
943
|
+
const sec = parseEtime(meta.etime);
|
|
944
|
+
return { isClaude: false, isCodex: true, kind: 'codex', startMs: sec == null ? null : now - sec * 1000 };
|
|
869
945
|
}
|
|
870
946
|
for (const c of children.get(pid) ?? []) queue.push(c);
|
|
871
947
|
}
|
|
872
|
-
return { isClaude: false, startMs: null };
|
|
948
|
+
return { isClaude: false, isCodex: false, kind: null, startMs: null };
|
|
873
949
|
};
|
|
874
950
|
|
|
875
951
|
for (const p of allPanes) {
|
|
876
|
-
out.set(p.target, p.panePid ? findClaude(p.panePid) : { isClaude: false, startMs: null });
|
|
952
|
+
out.set(p.target, p.panePid ? findClaude(p.panePid) : { isClaude: false, isCodex: false, kind: null, startMs: null });
|
|
877
953
|
}
|
|
878
954
|
return out;
|
|
879
955
|
}
|
package/lib/transcript.js
CHANGED
|
@@ -194,13 +194,14 @@ export function parseRecord(line) {
|
|
|
194
194
|
export class TranscriptTailer extends EventEmitter {
|
|
195
195
|
/**
|
|
196
196
|
* @param {string} filePath
|
|
197
|
-
* @param {{ maxBuffer?: number, debounceMs?: number }} options
|
|
197
|
+
* @param {{ maxBuffer?: number, debounceMs?: number, parser?: Function }} options
|
|
198
198
|
*/
|
|
199
|
-
constructor(filePath, { maxBuffer = DEFAULT_MAX_BUFFER, debounceMs = 150 } = {}) {
|
|
199
|
+
constructor(filePath, { maxBuffer = DEFAULT_MAX_BUFFER, debounceMs = 150, parser = parseRecord } = {}) {
|
|
200
200
|
super();
|
|
201
201
|
this._filePath = filePath;
|
|
202
202
|
this._maxBuffer = maxBuffer;
|
|
203
203
|
this._debounceMs = debounceMs;
|
|
204
|
+
this._parse = parser;
|
|
204
205
|
|
|
205
206
|
/** @type {import('./transcript.js').NormalizedMessage[]} */
|
|
206
207
|
this._messages = [];
|
|
@@ -340,7 +341,7 @@ export class TranscriptTailer extends EventEmitter {
|
|
|
340
341
|
|
|
341
342
|
const parsed = [];
|
|
342
343
|
for (const line of lines) {
|
|
343
|
-
const msg =
|
|
344
|
+
const msg = this._parse(line);
|
|
344
345
|
if (msg) {
|
|
345
346
|
parsed.push(msg);
|
|
346
347
|
this._trackPending(msg);
|
|
@@ -413,7 +414,7 @@ export class TranscriptTailer extends EventEmitter {
|
|
|
413
414
|
|
|
414
415
|
const newMsgs = [];
|
|
415
416
|
for (const line of complete) {
|
|
416
|
-
const msg =
|
|
417
|
+
const msg = this._parse(line);
|
|
417
418
|
if (msg) {
|
|
418
419
|
newMsgs.push(msg);
|
|
419
420
|
this._trackPending(msg);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@idl3/claude-control",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Local web UI to watch and drive your Claude Code sessions running in tmux — live transcripts, reply, answer AskUserQuestion, attach files, from a browser or phone.",
|
|
6
6
|
"keywords": [
|
package/server.js
CHANGED
|
@@ -10,7 +10,11 @@ import fs from 'node:fs';
|
|
|
10
10
|
import path from 'node:path';
|
|
11
11
|
import { fileURLToPath } from 'node:url';
|
|
12
12
|
import os from 'node:os';
|
|
13
|
-
import { spawn } from 'node:child_process';
|
|
13
|
+
import { spawn, execFile as _execFileRaw } from 'node:child_process';
|
|
14
|
+
import { promisify } from 'node:util';
|
|
15
|
+
import fsp from 'node:fs/promises';
|
|
16
|
+
|
|
17
|
+
const _execFile = promisify(_execFileRaw);
|
|
14
18
|
import { WebSocketServer } from 'ws';
|
|
15
19
|
|
|
16
20
|
import * as tmux from './lib/tmux.js';
|
|
@@ -27,6 +31,7 @@ import { sweepUploads, resolveUploadPath } from './lib/uploads.js';
|
|
|
27
31
|
import { getVersionInfo, currentVersion } from './lib/version.js';
|
|
28
32
|
import * as push from './lib/push.js';
|
|
29
33
|
import { readConfig, writeConfig } from './lib/config.js';
|
|
34
|
+
import { parseCodexRecord, parseCodexPrompt, buildSpawnCommand } from './lib/codex.js';
|
|
30
35
|
import { optimizePrompt, rulesOptimize } from './lib/optimize.js';
|
|
31
36
|
import * as mlx from './lib/mlx.js';
|
|
32
37
|
import {
|
|
@@ -76,6 +81,8 @@ const CONFIG = {
|
|
|
76
81
|
host: env('HOST') || '127.0.0.1',
|
|
77
82
|
projectsRoot:
|
|
78
83
|
env('PROJECTS') || path.join(os.homedir(), '.claude', 'projects'),
|
|
84
|
+
codexSessionsRoot:
|
|
85
|
+
env('CODEX_SESSIONS') || path.join(os.homedir(), '.codex', 'sessions'),
|
|
79
86
|
// 768MB: a long-running Node server (WS + transcript tailing + the bundled
|
|
80
87
|
// web app) baselines ~300-450MB of V8 heap + RSS, so the old 350MB budget
|
|
81
88
|
// tripped "over limit" permanently. Override with CLAUDE_CONTROL_RSS_LIMIT_MB.
|
|
@@ -123,7 +130,7 @@ const IMAGE_MIME = {
|
|
|
123
130
|
};
|
|
124
131
|
|
|
125
132
|
// --- shared state -----------------------------------------------------------
|
|
126
|
-
const registry = new SessionRegistry({ projectsRoot: CONFIG.projectsRoot, tmux });
|
|
133
|
+
const registry = new SessionRegistry({ projectsRoot: CONFIG.projectsRoot, codexSessionsRoot: CONFIG.codexSessionsRoot, tmux });
|
|
127
134
|
const resources = new ResourceMonitor({ rssLimitMB: CONFIG.rssLimitMB });
|
|
128
135
|
|
|
129
136
|
// Manual transcript pins (windowId.paneIndex -> transcript path). Loaded at boot,
|
|
@@ -347,6 +354,33 @@ const _handler = (req, res) => {
|
|
|
347
354
|
if (!checkToken(req)) return endJson(res, 401, { error: 'unauthorized' });
|
|
348
355
|
return handleTranscribe(req, res, u);
|
|
349
356
|
}
|
|
357
|
+
// GET /api/spawn-agents — agent-type availability (claude vs codex).
|
|
358
|
+
// Returns which agent binaries are resolvable on this machine so the UI can
|
|
359
|
+
// disable an unavailable agent picker option and show a reason.
|
|
360
|
+
// Token-gated + localhost, same as other GET endpoints.
|
|
361
|
+
if (u.pathname === '/api/spawn-agents') {
|
|
362
|
+
if (!checkToken(req)) return endJson(res, 401, { error: 'unauthorized' });
|
|
363
|
+
const cfg = readConfig();
|
|
364
|
+
return Promise.all([
|
|
365
|
+
resolveBin(cfg.claudeBin || cfg.launchCommand),
|
|
366
|
+
resolveBin(cfg.codexBin || cfg.codexLaunchCommand),
|
|
367
|
+
]).then(([claudeResult, codexResult]) => {
|
|
368
|
+
return endJson(res, 200, {
|
|
369
|
+
agents: [
|
|
370
|
+
{
|
|
371
|
+
id: 'claude',
|
|
372
|
+
available: claudeResult.available,
|
|
373
|
+
...(claudeResult.available ? {} : { reason: claudeResult.reason }),
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
id: 'codex',
|
|
377
|
+
available: codexResult.available,
|
|
378
|
+
...(codexResult.available ? {} : { reason: codexResult.reason }),
|
|
379
|
+
},
|
|
380
|
+
],
|
|
381
|
+
});
|
|
382
|
+
}).catch((err) => endJson(res, 500, { error: String(err?.message || err) }));
|
|
383
|
+
}
|
|
350
384
|
if (u.pathname === '/api/session/new') {
|
|
351
385
|
if (req.method !== 'POST') return endJson(res, 405, { error: 'method not allowed' });
|
|
352
386
|
if (!checkToken(req)) return endJson(res, 401, { error: 'unauthorized' });
|
|
@@ -672,6 +706,40 @@ function handleTranscribe(req, res, u) {
|
|
|
672
706
|
});
|
|
673
707
|
}
|
|
674
708
|
|
|
709
|
+
// ---------------------------------------------------------------------------
|
|
710
|
+
// resolveBin — async PATH lookup for a binary name or absolute path.
|
|
711
|
+
//
|
|
712
|
+
// If `bin` is an absolute path, checks it is executable directly.
|
|
713
|
+
// Otherwise runs `which <bin>` on PATH.
|
|
714
|
+
//
|
|
715
|
+
// Returns { available: true, path } on success, { available: false, reason }
|
|
716
|
+
// on failure. Never throws.
|
|
717
|
+
// ---------------------------------------------------------------------------
|
|
718
|
+
async function resolveBin(bin) {
|
|
719
|
+
if (!bin || typeof bin !== 'string' || !bin.trim()) {
|
|
720
|
+
return { available: false, reason: 'no binary configured' };
|
|
721
|
+
}
|
|
722
|
+
const b = bin.trim();
|
|
723
|
+
// Absolute path: check existence + execute permission directly.
|
|
724
|
+
if (b.startsWith('/')) {
|
|
725
|
+
try {
|
|
726
|
+
await fsp.access(b, fsp.constants?.X_OK ?? 1);
|
|
727
|
+
return { available: true, path: b };
|
|
728
|
+
} catch {
|
|
729
|
+
return { available: false, reason: `binary not found or not executable: ${b}` };
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
// Relative / bare name: resolve via `which`.
|
|
733
|
+
try {
|
|
734
|
+
const { stdout } = await _execFile('which', [b], { timeout: 5000 });
|
|
735
|
+
const resolved = stdout.trim();
|
|
736
|
+
if (resolved) return { available: true, path: resolved };
|
|
737
|
+
return { available: false, reason: `${b} not found on PATH` };
|
|
738
|
+
} catch {
|
|
739
|
+
return { available: false, reason: `${b} not found on PATH` };
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
|
|
675
743
|
// POST /api/session/new — create a new tmux window in the configured (or
|
|
676
744
|
// body-overridden) cwd, then type the launch command into it via send-keys so
|
|
677
745
|
// the interactive shell resolves aliases. Security: the command is operator
|
|
@@ -687,23 +755,69 @@ async function handleSessionNew(req, res) {
|
|
|
687
755
|
const config = readConfig();
|
|
688
756
|
const cwd =
|
|
689
757
|
typeof body.cwd === 'string' && body.cwd.trim() ? body.cwd : config.defaultCwd;
|
|
758
|
+
|
|
759
|
+
// agent ∈ {'claude','codex'}, default 'claude'.
|
|
760
|
+
const agent = body.agent === 'codex' ? 'codex' : 'claude';
|
|
761
|
+
|
|
690
762
|
// Name is required-with-default: sanitize the requested name, falling back to
|
|
691
763
|
// `session-<short-ts>` so a session is ALWAYS named (the rail reads the tmux
|
|
692
764
|
// window name until a transcript title exists).
|
|
693
765
|
const name = tmux.sanitizeName(body.name) || tmux.defaultSessionName();
|
|
766
|
+
|
|
767
|
+
// --- Pre-validation: binary resolution + cwd check BEFORE creating any window ---
|
|
768
|
+
|
|
769
|
+
// (i) Resolve the agent binary and return 400 if unavailable.
|
|
770
|
+
const agentBin = agent === 'codex'
|
|
771
|
+
? (config.codexBin || config.codexLaunchCommand)
|
|
772
|
+
: (config.claudeBin || config.launchCommand);
|
|
773
|
+
const binCheck = await resolveBin(agentBin);
|
|
774
|
+
if (!binCheck.available) {
|
|
775
|
+
return endJson(res, 400, { error: `agent binary unavailable: ${binCheck.reason}` });
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
// (ii) For codex: pre-validate cwd exists and is a directory BEFORE createWindow,
|
|
779
|
+
// so a bad request creates NO window (400 not 500, window-leak prevention).
|
|
780
|
+
if (agent === 'codex') {
|
|
781
|
+
try {
|
|
782
|
+
const st = await fsp.stat(cwd);
|
|
783
|
+
if (!st.isDirectory()) {
|
|
784
|
+
return endJson(res, 400, { error: `cwd is not a directory: ${cwd}` });
|
|
785
|
+
}
|
|
786
|
+
} catch {
|
|
787
|
+
return endJson(res, 400, { error: `cwd does not exist: ${cwd}` });
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
|
|
694
791
|
try {
|
|
695
792
|
// (1) Reliable named path: the tmux window name. createWindow sets it via
|
|
696
|
-
// `new-window -n
|
|
793
|
+
// `new-window -n` and the `-c cwd` flag — cwd flows through tmux's own
|
|
794
|
+
// working-directory flag, never a shell `cd`.
|
|
697
795
|
const target = await tmux.createWindow({ cwd, name });
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
796
|
+
|
|
797
|
+
let launch;
|
|
798
|
+
if (agent === 'codex') {
|
|
799
|
+
// Codex path: uses -C <cwd> (its own cwd flag). No --name flag — Codex
|
|
800
|
+
// has none. The tmux window is still named (above) so the rail shows it.
|
|
801
|
+
// buildSpawnCommand is the single source of truth for Codex's launch
|
|
802
|
+
// shape; the cwd arg is shell-quoted since the command is typed into an
|
|
803
|
+
// interactive shell via sendText. The executed command is
|
|
804
|
+
// config.codexLaunchCommand (may be a shell alias), validated above via
|
|
805
|
+
// codexBin||codexLaunchCommand — same pattern as the Claude branch.
|
|
806
|
+
const { bin, args } = buildSpawnCommand({ cwd, bin: config.codexLaunchCommand });
|
|
807
|
+
launch = `${bin} ${args.map((a) => (a === cwd ? tmux.shellQuoteName(cwd) : a)).join(' ')}`;
|
|
808
|
+
} else {
|
|
809
|
+
// Claude path: BYTE-IDENTICAL to the pre-Phase-D implementation.
|
|
810
|
+
// (2) Claude's own session title: `claude --help` exposes `-n/--name`
|
|
811
|
+
// (display name in the prompt box, /resume picker, terminal title), so
|
|
812
|
+
// we append it to the launch command rather than relying on a delayed
|
|
813
|
+
// `/rename`. The name is shell-quoted (sanitizeName already stripped
|
|
814
|
+
// control chars/newlines) since the command is typed into an interactive
|
|
815
|
+
// shell so aliases like `yolo` resolve. sendText appends Enter → runs it.
|
|
816
|
+
launch = `${config.launchCommand} --name ${tmux.shellQuoteName(name)}`;
|
|
817
|
+
}
|
|
818
|
+
|
|
705
819
|
await tmux.sendText(target, launch);
|
|
706
|
-
return endJson(res, 200, { ok: true, target, name });
|
|
820
|
+
return endJson(res, 200, { ok: true, target, name, agent });
|
|
707
821
|
} catch (err) {
|
|
708
822
|
return endJson(res, 500, { error: String(err?.message || err) });
|
|
709
823
|
}
|
|
@@ -1135,7 +1249,7 @@ function ensureSubscription(id) {
|
|
|
1135
1249
|
return sub;
|
|
1136
1250
|
}
|
|
1137
1251
|
|
|
1138
|
-
const tailer = new TranscriptTailer(session.transcriptPath, { maxBuffer: CONFIG.maxBuffer });
|
|
1252
|
+
const tailer = new TranscriptTailer(session.transcriptPath, { maxBuffer: CONFIG.maxBuffer, parser: session.kind === 'codex' ? parseCodexRecord : undefined });
|
|
1139
1253
|
// Watch this session's sub-agent transcripts (Task/Agent). Discovery is polled
|
|
1140
1254
|
// when the parent transcript grows (when sub-agents spawn) + once at subscribe.
|
|
1141
1255
|
const subagents = new SubAgentsWatcher(session.transcriptPath);
|
|
@@ -1201,7 +1315,7 @@ function startPromptPoller(id, sub) {
|
|
|
1201
1315
|
let prompt = null;
|
|
1202
1316
|
try {
|
|
1203
1317
|
const cap = await tmux.capturePane(session.target, 40);
|
|
1204
|
-
prompt = parsePanePrompt(cap);
|
|
1318
|
+
prompt = session.kind === 'codex' ? parseCodexPrompt(cap) : parsePanePrompt(cap);
|
|
1205
1319
|
} catch {
|
|
1206
1320
|
return;
|
|
1207
1321
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{g as Ve}from"./index-DFru8Gzx.js";function xe(e){return e instanceof Map?e.clear=e.delete=e.set=function(){throw new Error("map is read-only")}:e instanceof Set&&(e.add=e.clear=e.delete=function(){throw new Error("set is read-only")}),Object.freeze(e),Object.getOwnPropertyNames(e).forEach(t=>{const i=e[t],u=typeof i;(u==="object"||u==="function")&&!Object.isFrozen(i)&&xe(i)}),e}class he{constructor(t){t.data===void 0&&(t.data={}),this.data=t.data,this.isMatchIgnored=!1}ignoreMatch(){this.isMatchIgnored=!0}}function we(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}function B(e,...t){const i=Object.create(null);for(const u in e)i[u]=e[u];return t.forEach(function(u){for(const b in u)i[b]=u[b]}),i}const qe="</span>",pe=e=>!!e.scope,Qe=(e,{prefix:t})=>{if(e.startsWith("language:"))return e.replace("language:","language-");if(e.includes(".")){const i=e.split(".");return[`${t}${i.shift()}`,...i.map((u,b)=>`${u}${"_".repeat(b+1)}`)].join(" ")}return`${t}${e}`};class me{constructor(t,i){this.buffer="",this.classPrefix=i.classPrefix,t.walk(this)}addText(t){this.buffer+=we(t)}openNode(t){if(!pe(t))return;const i=Qe(t.scope,{prefix:this.classPrefix});this.span(i)}closeNode(t){pe(t)&&(this.buffer+=qe)}value(){return this.buffer}span(t){this.buffer+=`<span class="${t}">`}}const de=(e={})=>{const t={children:[]};return Object.assign(t,e),t};class te{constructor(){this.rootNode=de(),this.stack=[this.rootNode]}get top(){return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(t){this.top.children.push(t)}openNode(t){const i=de({scope:t});this.add(i),this.stack.push(i)}closeNode(){if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)}walk(t){return this.constructor._walk(t,this.rootNode)}static _walk(t,i){return typeof i=="string"?t.addText(i):i.children&&(t.openNode(i),i.children.forEach(u=>this._walk(t,u)),t.closeNode(i)),t}static _collapse(t){typeof t!="string"&&t.children&&(t.children.every(i=>typeof i=="string")?t.children=[t.children.join("")]:t.children.forEach(i=>{te._collapse(i)}))}}class et extends te{constructor(t){super(),this.options=t}addText(t){t!==""&&this.add(t)}startScope(t){this.openNode(t)}endScope(){this.closeNode()}__addSublanguage(t,i){const u=t.root;i&&(u.scope=`language:${i}`),this.add(u)}toHTML(){return new me(this,this.options).value()}finalize(){return this.closeAllNodes(),!0}}function P(e){return e?typeof e=="string"?e:e.source:null}function Oe(e){return C("(?=",e,")")}function tt(e){return C("(?:",e,")*")}function nt(e){return C("(?:",e,")?")}function C(...e){return e.map(i=>P(i)).join("")}function it(e){const t=e[e.length-1];return typeof t=="object"&&t.constructor===Object?(e.splice(e.length-1,1),t):{}}function ne(...e){return"("+(it(e).capture?"":"?:")+e.map(u=>P(u)).join("|")+")"}function Re(e){return new RegExp(e.toString()+"|").exec("").length-1}function st(e,t){const i=e&&e.exec(t);return i&&i.index===0}const rt=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./;function ie(e,{joinWith:t}){let i=0;return e.map(u=>{i+=1;const b=i;let _=P(u),c="";for(;_.length>0;){const r=rt.exec(_);if(!r){c+=_;break}c+=_.substring(0,r.index),_=_.substring(r.index+r[0].length),r[0][0]==="\\"&&r[1]?c+="\\"+String(Number(r[1])+b):(c+=r[0],r[0]==="("&&i++)}return c}).map(u=>`(${u})`).join(t)}const ct=/\b\B/,ye="[a-zA-Z]\\w*",se="[a-zA-Z_]\\w*",Se="\\b\\d+(\\.\\d+)?",Ne="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",Ae="\\b(0b[01]+)",ot="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",at=(e={})=>{const t=/^#![ ]*\//;return e.binary&&(e.begin=C(t,/.*\b/,e.binary,/\b.*/)),B({scope:"meta",begin:t,end:/$/,relevance:0,"on:begin":(i,u)=>{i.index!==0&&u.ignoreMatch()}},e)},U={begin:"\\\\[\\s\\S]",relevance:0},lt={scope:"string",begin:"'",end:"'",illegal:"\\n",contains:[U]},ut={scope:"string",begin:'"',end:'"',illegal:"\\n",contains:[U]},ft={begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},Y=function(e,t,i={}){const u=B({scope:"comment",begin:e,end:t,contains:[]},i);u.contains.push({scope:"doctag",begin:"[ ]*(?=(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):)",end:/(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):/,excludeBegin:!0,relevance:0});const b=ne("I","a","is","so","us","to","at","if","in","it","on",/[A-Za-z]+['](d|ve|re|ll|t|s|n)/,/[A-Za-z]+[-][a-z]+/,/[A-Za-z][a-z]{2,}/);return u.contains.push({begin:C(/[ ]+/,"(",b,/[.]?[:]?([.][ ]|[ ])/,"){3}")}),u},gt=Y("//","$"),ht=Y("/\\*","\\*/"),pt=Y("#","$"),dt={scope:"number",begin:Se,relevance:0},Et={scope:"number",begin:Ne,relevance:0},bt={scope:"number",begin:Ae,relevance:0},_t={scope:"regexp",begin:/\/(?=[^/\n]*\/)/,end:/\/[gimuy]*/,contains:[U,{begin:/\[/,end:/\]/,relevance:0,contains:[U]}]},Mt={scope:"title",begin:ye,relevance:0},xt={scope:"title",begin:se,relevance:0},wt={begin:"\\.\\s*"+se,relevance:0},Ot=function(e){return Object.assign(e,{"on:begin":(t,i)=>{i.data._beginMatch=t[1]},"on:end":(t,i)=>{i.data._beginMatch!==t[1]&&i.ignoreMatch()}})};var z=Object.freeze({__proto__:null,APOS_STRING_MODE:lt,BACKSLASH_ESCAPE:U,BINARY_NUMBER_MODE:bt,BINARY_NUMBER_RE:Ae,COMMENT:Y,C_BLOCK_COMMENT_MODE:ht,C_LINE_COMMENT_MODE:gt,C_NUMBER_MODE:Et,C_NUMBER_RE:Ne,END_SAME_AS_BEGIN:Ot,HASH_COMMENT_MODE:pt,IDENT_RE:ye,MATCH_NOTHING_RE:ct,METHOD_GUARD:wt,NUMBER_MODE:dt,NUMBER_RE:Se,PHRASAL_WORDS_MODE:ft,QUOTE_STRING_MODE:ut,REGEXP_MODE:_t,RE_STARTERS_RE:ot,SHEBANG:at,TITLE_MODE:Mt,UNDERSCORE_IDENT_RE:se,UNDERSCORE_TITLE_MODE:xt});function Rt(e,t){e.input[e.index-1]==="."&&t.ignoreMatch()}function yt(e,t){e.className!==void 0&&(e.scope=e.className,delete e.className)}function St(e,t){t&&e.beginKeywords&&(e.begin="\\b("+e.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)",e.__beforeBegin=Rt,e.keywords=e.keywords||e.beginKeywords,delete e.beginKeywords,e.relevance===void 0&&(e.relevance=0))}function Nt(e,t){Array.isArray(e.illegal)&&(e.illegal=ne(...e.illegal))}function At(e,t){if(e.match){if(e.begin||e.end)throw new Error("begin & end are not supported with match");e.begin=e.match,delete e.match}}function kt(e,t){e.relevance===void 0&&(e.relevance=1)}const Tt=(e,t)=>{if(!e.beforeMatch)return;if(e.starts)throw new Error("beforeMatch cannot be used with starts");const i=Object.assign({},e);Object.keys(e).forEach(u=>{delete e[u]}),e.keywords=i.keywords,e.begin=C(i.beforeMatch,Oe(i.begin)),e.starts={relevance:0,contains:[Object.assign(i,{endsParent:!0})]},e.relevance=0,delete i.beforeMatch},It=["of","and","for","in","not","or","if","then","parent","list","value"],Bt="keyword";function ke(e,t,i=Bt){const u=Object.create(null);return typeof e=="string"?b(i,e.split(" ")):Array.isArray(e)?b(i,e):Object.keys(e).forEach(function(_){Object.assign(u,ke(e[_],t,_))}),u;function b(_,c){t&&(c=c.map(r=>r.toLowerCase())),c.forEach(function(r){const l=r.split("|");u[l[0]]=[_,Dt(l[0],l[1])]})}}function Dt(e,t){return t?Number(t):vt(e)?0:1}function vt(e){return It.includes(e.toLowerCase())}const Ee={},v=e=>{console.error(e)},be=(e,...t)=>{console.log(`WARN: ${e}`,...t)},L=(e,t)=>{Ee[`${e}/${t}`]||(console.log(`Deprecated as of ${e}. ${t}`),Ee[`${e}/${t}`]=!0)},X=new Error;function Te(e,t,{key:i}){let u=0;const b=e[i],_={},c={};for(let r=1;r<=t.length;r++)c[r+u]=b[r],_[r+u]=!0,u+=Re(t[r-1]);e[i]=c,e[i]._emit=_,e[i]._multi=!0}function Ct(e){if(Array.isArray(e.begin)){if(e.skip||e.excludeBegin||e.returnBegin)throw v("skip, excludeBegin, returnBegin not compatible with beginScope: {}"),X;if(typeof e.beginScope!="object"||e.beginScope===null)throw v("beginScope must be object"),X;Te(e,e.begin,{key:"beginScope"}),e.begin=ie(e.begin,{joinWith:""})}}function Lt(e){if(Array.isArray(e.end)){if(e.skip||e.excludeEnd||e.returnEnd)throw v("skip, excludeEnd, returnEnd not compatible with endScope: {}"),X;if(typeof e.endScope!="object"||e.endScope===null)throw v("endScope must be object"),X;Te(e,e.end,{key:"endScope"}),e.end=ie(e.end,{joinWith:""})}}function Ht(e){e.scope&&typeof e.scope=="object"&&e.scope!==null&&(e.beginScope=e.scope,delete e.scope)}function jt(e){Ht(e),typeof e.beginScope=="string"&&(e.beginScope={_wrap:e.beginScope}),typeof e.endScope=="string"&&(e.endScope={_wrap:e.endScope}),Ct(e),Lt(e)}function Pt(e){function t(c,r){return new RegExp(P(c),"m"+(e.case_insensitive?"i":"")+(e.unicodeRegex?"u":"")+(r?"g":""))}class i{constructor(){this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0}addRule(r,l){l.position=this.position++,this.matchIndexes[this.matchAt]=l,this.regexes.push([l,r]),this.matchAt+=Re(r)+1}compile(){this.regexes.length===0&&(this.exec=()=>null);const r=this.regexes.map(l=>l[1]);this.matcherRe=t(ie(r,{joinWith:"|"}),!0),this.lastIndex=0}exec(r){this.matcherRe.lastIndex=this.lastIndex;const l=this.matcherRe.exec(r);if(!l)return null;const w=l.findIndex((j,Z)=>Z>0&&j!==void 0),M=this.matchIndexes[w];return l.splice(0,w),Object.assign(l,M)}}class u{constructor(){this.rules=[],this.multiRegexes=[],this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(r){if(this.multiRegexes[r])return this.multiRegexes[r];const l=new i;return this.rules.slice(r).forEach(([w,M])=>l.addRule(w,M)),l.compile(),this.multiRegexes[r]=l,l}resumingScanAtSamePosition(){return this.regexIndex!==0}considerAll(){this.regexIndex=0}addRule(r,l){this.rules.push([r,l]),l.type==="begin"&&this.count++}exec(r){const l=this.getMatcher(this.regexIndex);l.lastIndex=this.lastIndex;let w=l.exec(r);if(this.resumingScanAtSamePosition()&&!(w&&w.index===this.lastIndex)){const M=this.getMatcher(0);M.lastIndex=this.lastIndex+1,w=M.exec(r)}return w&&(this.regexIndex+=w.position+1,this.regexIndex===this.count&&this.considerAll()),w}}function b(c){const r=new u;return c.contains.forEach(l=>r.addRule(l.begin,{rule:l,type:"begin"})),c.terminatorEnd&&r.addRule(c.terminatorEnd,{type:"end"}),c.illegal&&r.addRule(c.illegal,{type:"illegal"}),r}function _(c,r){const l=c;if(c.isCompiled)return l;[yt,At,jt,Tt].forEach(M=>M(c,r)),e.compilerExtensions.forEach(M=>M(c,r)),c.__beforeBegin=null,[St,Nt,kt].forEach(M=>M(c,r)),c.isCompiled=!0;let w=null;return typeof c.keywords=="object"&&c.keywords.$pattern&&(c.keywords=Object.assign({},c.keywords),w=c.keywords.$pattern,delete c.keywords.$pattern),w=w||/\w+/,c.keywords&&(c.keywords=ke(c.keywords,e.case_insensitive)),l.keywordPatternRe=t(w,!0),r&&(c.begin||(c.begin=/\B|\b/),l.beginRe=t(l.begin),!c.end&&!c.endsWithParent&&(c.end=/\B|\b/),c.end&&(l.endRe=t(l.end)),l.terminatorEnd=P(l.end)||"",c.endsWithParent&&r.terminatorEnd&&(l.terminatorEnd+=(c.end?"|":"")+r.terminatorEnd)),c.illegal&&(l.illegalRe=t(c.illegal)),c.contains||(c.contains=[]),c.contains=[].concat(...c.contains.map(function(M){return Ut(M==="self"?c:M)})),c.contains.forEach(function(M){_(M,l)}),c.starts&&_(c.starts,r),l.matcher=b(l),l}if(e.compilerExtensions||(e.compilerExtensions=[]),e.contains&&e.contains.includes("self"))throw new Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.");return e.classNameAliases=B(e.classNameAliases||{}),_(e)}function Ie(e){return e?e.endsWithParent||Ie(e.starts):!1}function Ut(e){return e.variants&&!e.cachedVariants&&(e.cachedVariants=e.variants.map(function(t){return B(e,{variants:null},t)})),e.cachedVariants?e.cachedVariants:Ie(e)?B(e,{starts:e.starts?B(e.starts):null}):Object.isFrozen(e)?B(e):e}var $t="11.11.1";class Gt extends Error{constructor(t,i){super(t),this.name="HTMLInjectionError",this.html=i}}const ee=we,_e=B,Me=Symbol("nomatch"),Wt=7,Be=function(e){const t=Object.create(null),i=Object.create(null),u=[];let b=!0;const _="Could not find the language '{}', did you forget to load/include a language module?",c={disableAutodetect:!0,name:"Plain text",contains:[]};let r={ignoreUnescapedHTML:!1,throwUnescapedHTML:!1,noHighlightRe:/^(no-?highlight)$/i,languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-",cssSelector:"pre code",languages:null,__emitter:et};function l(n){return r.noHighlightRe.test(n)}function w(n){let a=n.className+" ";a+=n.parentNode?n.parentNode.className:"";const h=r.languageDetectRe.exec(a);if(h){const d=T(h[1]);return d||(be(_.replace("{}",h[1])),be("Falling back to no-highlight mode for this block.",n)),d?h[1]:"no-highlight"}return a.split(/\s+/).find(d=>l(d)||T(d))}function M(n,a,h){let d="",x="";typeof a=="object"?(d=n,h=a.ignoreIllegals,x=a.language):(L("10.7.0","highlight(lang, code, ...args) has been deprecated."),L("10.7.0",`Please use highlight(code, options) instead.
|
|
1
|
+
import{g as Ve}from"./index-DzIDTXLS.js";function xe(e){return e instanceof Map?e.clear=e.delete=e.set=function(){throw new Error("map is read-only")}:e instanceof Set&&(e.add=e.clear=e.delete=function(){throw new Error("set is read-only")}),Object.freeze(e),Object.getOwnPropertyNames(e).forEach(t=>{const i=e[t],u=typeof i;(u==="object"||u==="function")&&!Object.isFrozen(i)&&xe(i)}),e}class he{constructor(t){t.data===void 0&&(t.data={}),this.data=t.data,this.isMatchIgnored=!1}ignoreMatch(){this.isMatchIgnored=!0}}function we(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}function B(e,...t){const i=Object.create(null);for(const u in e)i[u]=e[u];return t.forEach(function(u){for(const b in u)i[b]=u[b]}),i}const qe="</span>",pe=e=>!!e.scope,Qe=(e,{prefix:t})=>{if(e.startsWith("language:"))return e.replace("language:","language-");if(e.includes(".")){const i=e.split(".");return[`${t}${i.shift()}`,...i.map((u,b)=>`${u}${"_".repeat(b+1)}`)].join(" ")}return`${t}${e}`};class me{constructor(t,i){this.buffer="",this.classPrefix=i.classPrefix,t.walk(this)}addText(t){this.buffer+=we(t)}openNode(t){if(!pe(t))return;const i=Qe(t.scope,{prefix:this.classPrefix});this.span(i)}closeNode(t){pe(t)&&(this.buffer+=qe)}value(){return this.buffer}span(t){this.buffer+=`<span class="${t}">`}}const de=(e={})=>{const t={children:[]};return Object.assign(t,e),t};class te{constructor(){this.rootNode=de(),this.stack=[this.rootNode]}get top(){return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(t){this.top.children.push(t)}openNode(t){const i=de({scope:t});this.add(i),this.stack.push(i)}closeNode(){if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)}walk(t){return this.constructor._walk(t,this.rootNode)}static _walk(t,i){return typeof i=="string"?t.addText(i):i.children&&(t.openNode(i),i.children.forEach(u=>this._walk(t,u)),t.closeNode(i)),t}static _collapse(t){typeof t!="string"&&t.children&&(t.children.every(i=>typeof i=="string")?t.children=[t.children.join("")]:t.children.forEach(i=>{te._collapse(i)}))}}class et extends te{constructor(t){super(),this.options=t}addText(t){t!==""&&this.add(t)}startScope(t){this.openNode(t)}endScope(){this.closeNode()}__addSublanguage(t,i){const u=t.root;i&&(u.scope=`language:${i}`),this.add(u)}toHTML(){return new me(this,this.options).value()}finalize(){return this.closeAllNodes(),!0}}function P(e){return e?typeof e=="string"?e:e.source:null}function Oe(e){return C("(?=",e,")")}function tt(e){return C("(?:",e,")*")}function nt(e){return C("(?:",e,")?")}function C(...e){return e.map(i=>P(i)).join("")}function it(e){const t=e[e.length-1];return typeof t=="object"&&t.constructor===Object?(e.splice(e.length-1,1),t):{}}function ne(...e){return"("+(it(e).capture?"":"?:")+e.map(u=>P(u)).join("|")+")"}function Re(e){return new RegExp(e.toString()+"|").exec("").length-1}function st(e,t){const i=e&&e.exec(t);return i&&i.index===0}const rt=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./;function ie(e,{joinWith:t}){let i=0;return e.map(u=>{i+=1;const b=i;let _=P(u),c="";for(;_.length>0;){const r=rt.exec(_);if(!r){c+=_;break}c+=_.substring(0,r.index),_=_.substring(r.index+r[0].length),r[0][0]==="\\"&&r[1]?c+="\\"+String(Number(r[1])+b):(c+=r[0],r[0]==="("&&i++)}return c}).map(u=>`(${u})`).join(t)}const ct=/\b\B/,ye="[a-zA-Z]\\w*",se="[a-zA-Z_]\\w*",Se="\\b\\d+(\\.\\d+)?",Ne="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",Ae="\\b(0b[01]+)",ot="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",at=(e={})=>{const t=/^#![ ]*\//;return e.binary&&(e.begin=C(t,/.*\b/,e.binary,/\b.*/)),B({scope:"meta",begin:t,end:/$/,relevance:0,"on:begin":(i,u)=>{i.index!==0&&u.ignoreMatch()}},e)},U={begin:"\\\\[\\s\\S]",relevance:0},lt={scope:"string",begin:"'",end:"'",illegal:"\\n",contains:[U]},ut={scope:"string",begin:'"',end:'"',illegal:"\\n",contains:[U]},ft={begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},Y=function(e,t,i={}){const u=B({scope:"comment",begin:e,end:t,contains:[]},i);u.contains.push({scope:"doctag",begin:"[ ]*(?=(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):)",end:/(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):/,excludeBegin:!0,relevance:0});const b=ne("I","a","is","so","us","to","at","if","in","it","on",/[A-Za-z]+['](d|ve|re|ll|t|s|n)/,/[A-Za-z]+[-][a-z]+/,/[A-Za-z][a-z]{2,}/);return u.contains.push({begin:C(/[ ]+/,"(",b,/[.]?[:]?([.][ ]|[ ])/,"){3}")}),u},gt=Y("//","$"),ht=Y("/\\*","\\*/"),pt=Y("#","$"),dt={scope:"number",begin:Se,relevance:0},Et={scope:"number",begin:Ne,relevance:0},bt={scope:"number",begin:Ae,relevance:0},_t={scope:"regexp",begin:/\/(?=[^/\n]*\/)/,end:/\/[gimuy]*/,contains:[U,{begin:/\[/,end:/\]/,relevance:0,contains:[U]}]},Mt={scope:"title",begin:ye,relevance:0},xt={scope:"title",begin:se,relevance:0},wt={begin:"\\.\\s*"+se,relevance:0},Ot=function(e){return Object.assign(e,{"on:begin":(t,i)=>{i.data._beginMatch=t[1]},"on:end":(t,i)=>{i.data._beginMatch!==t[1]&&i.ignoreMatch()}})};var z=Object.freeze({__proto__:null,APOS_STRING_MODE:lt,BACKSLASH_ESCAPE:U,BINARY_NUMBER_MODE:bt,BINARY_NUMBER_RE:Ae,COMMENT:Y,C_BLOCK_COMMENT_MODE:ht,C_LINE_COMMENT_MODE:gt,C_NUMBER_MODE:Et,C_NUMBER_RE:Ne,END_SAME_AS_BEGIN:Ot,HASH_COMMENT_MODE:pt,IDENT_RE:ye,MATCH_NOTHING_RE:ct,METHOD_GUARD:wt,NUMBER_MODE:dt,NUMBER_RE:Se,PHRASAL_WORDS_MODE:ft,QUOTE_STRING_MODE:ut,REGEXP_MODE:_t,RE_STARTERS_RE:ot,SHEBANG:at,TITLE_MODE:Mt,UNDERSCORE_IDENT_RE:se,UNDERSCORE_TITLE_MODE:xt});function Rt(e,t){e.input[e.index-1]==="."&&t.ignoreMatch()}function yt(e,t){e.className!==void 0&&(e.scope=e.className,delete e.className)}function St(e,t){t&&e.beginKeywords&&(e.begin="\\b("+e.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)",e.__beforeBegin=Rt,e.keywords=e.keywords||e.beginKeywords,delete e.beginKeywords,e.relevance===void 0&&(e.relevance=0))}function Nt(e,t){Array.isArray(e.illegal)&&(e.illegal=ne(...e.illegal))}function At(e,t){if(e.match){if(e.begin||e.end)throw new Error("begin & end are not supported with match");e.begin=e.match,delete e.match}}function kt(e,t){e.relevance===void 0&&(e.relevance=1)}const Tt=(e,t)=>{if(!e.beforeMatch)return;if(e.starts)throw new Error("beforeMatch cannot be used with starts");const i=Object.assign({},e);Object.keys(e).forEach(u=>{delete e[u]}),e.keywords=i.keywords,e.begin=C(i.beforeMatch,Oe(i.begin)),e.starts={relevance:0,contains:[Object.assign(i,{endsParent:!0})]},e.relevance=0,delete i.beforeMatch},It=["of","and","for","in","not","or","if","then","parent","list","value"],Bt="keyword";function ke(e,t,i=Bt){const u=Object.create(null);return typeof e=="string"?b(i,e.split(" ")):Array.isArray(e)?b(i,e):Object.keys(e).forEach(function(_){Object.assign(u,ke(e[_],t,_))}),u;function b(_,c){t&&(c=c.map(r=>r.toLowerCase())),c.forEach(function(r){const l=r.split("|");u[l[0]]=[_,Dt(l[0],l[1])]})}}function Dt(e,t){return t?Number(t):vt(e)?0:1}function vt(e){return It.includes(e.toLowerCase())}const Ee={},v=e=>{console.error(e)},be=(e,...t)=>{console.log(`WARN: ${e}`,...t)},L=(e,t)=>{Ee[`${e}/${t}`]||(console.log(`Deprecated as of ${e}. ${t}`),Ee[`${e}/${t}`]=!0)},X=new Error;function Te(e,t,{key:i}){let u=0;const b=e[i],_={},c={};for(let r=1;r<=t.length;r++)c[r+u]=b[r],_[r+u]=!0,u+=Re(t[r-1]);e[i]=c,e[i]._emit=_,e[i]._multi=!0}function Ct(e){if(Array.isArray(e.begin)){if(e.skip||e.excludeBegin||e.returnBegin)throw v("skip, excludeBegin, returnBegin not compatible with beginScope: {}"),X;if(typeof e.beginScope!="object"||e.beginScope===null)throw v("beginScope must be object"),X;Te(e,e.begin,{key:"beginScope"}),e.begin=ie(e.begin,{joinWith:""})}}function Lt(e){if(Array.isArray(e.end)){if(e.skip||e.excludeEnd||e.returnEnd)throw v("skip, excludeEnd, returnEnd not compatible with endScope: {}"),X;if(typeof e.endScope!="object"||e.endScope===null)throw v("endScope must be object"),X;Te(e,e.end,{key:"endScope"}),e.end=ie(e.end,{joinWith:""})}}function Ht(e){e.scope&&typeof e.scope=="object"&&e.scope!==null&&(e.beginScope=e.scope,delete e.scope)}function jt(e){Ht(e),typeof e.beginScope=="string"&&(e.beginScope={_wrap:e.beginScope}),typeof e.endScope=="string"&&(e.endScope={_wrap:e.endScope}),Ct(e),Lt(e)}function Pt(e){function t(c,r){return new RegExp(P(c),"m"+(e.case_insensitive?"i":"")+(e.unicodeRegex?"u":"")+(r?"g":""))}class i{constructor(){this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0}addRule(r,l){l.position=this.position++,this.matchIndexes[this.matchAt]=l,this.regexes.push([l,r]),this.matchAt+=Re(r)+1}compile(){this.regexes.length===0&&(this.exec=()=>null);const r=this.regexes.map(l=>l[1]);this.matcherRe=t(ie(r,{joinWith:"|"}),!0),this.lastIndex=0}exec(r){this.matcherRe.lastIndex=this.lastIndex;const l=this.matcherRe.exec(r);if(!l)return null;const w=l.findIndex((j,Z)=>Z>0&&j!==void 0),M=this.matchIndexes[w];return l.splice(0,w),Object.assign(l,M)}}class u{constructor(){this.rules=[],this.multiRegexes=[],this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(r){if(this.multiRegexes[r])return this.multiRegexes[r];const l=new i;return this.rules.slice(r).forEach(([w,M])=>l.addRule(w,M)),l.compile(),this.multiRegexes[r]=l,l}resumingScanAtSamePosition(){return this.regexIndex!==0}considerAll(){this.regexIndex=0}addRule(r,l){this.rules.push([r,l]),l.type==="begin"&&this.count++}exec(r){const l=this.getMatcher(this.regexIndex);l.lastIndex=this.lastIndex;let w=l.exec(r);if(this.resumingScanAtSamePosition()&&!(w&&w.index===this.lastIndex)){const M=this.getMatcher(0);M.lastIndex=this.lastIndex+1,w=M.exec(r)}return w&&(this.regexIndex+=w.position+1,this.regexIndex===this.count&&this.considerAll()),w}}function b(c){const r=new u;return c.contains.forEach(l=>r.addRule(l.begin,{rule:l,type:"begin"})),c.terminatorEnd&&r.addRule(c.terminatorEnd,{type:"end"}),c.illegal&&r.addRule(c.illegal,{type:"illegal"}),r}function _(c,r){const l=c;if(c.isCompiled)return l;[yt,At,jt,Tt].forEach(M=>M(c,r)),e.compilerExtensions.forEach(M=>M(c,r)),c.__beforeBegin=null,[St,Nt,kt].forEach(M=>M(c,r)),c.isCompiled=!0;let w=null;return typeof c.keywords=="object"&&c.keywords.$pattern&&(c.keywords=Object.assign({},c.keywords),w=c.keywords.$pattern,delete c.keywords.$pattern),w=w||/\w+/,c.keywords&&(c.keywords=ke(c.keywords,e.case_insensitive)),l.keywordPatternRe=t(w,!0),r&&(c.begin||(c.begin=/\B|\b/),l.beginRe=t(l.begin),!c.end&&!c.endsWithParent&&(c.end=/\B|\b/),c.end&&(l.endRe=t(l.end)),l.terminatorEnd=P(l.end)||"",c.endsWithParent&&r.terminatorEnd&&(l.terminatorEnd+=(c.end?"|":"")+r.terminatorEnd)),c.illegal&&(l.illegalRe=t(c.illegal)),c.contains||(c.contains=[]),c.contains=[].concat(...c.contains.map(function(M){return Ut(M==="self"?c:M)})),c.contains.forEach(function(M){_(M,l)}),c.starts&&_(c.starts,r),l.matcher=b(l),l}if(e.compilerExtensions||(e.compilerExtensions=[]),e.contains&&e.contains.includes("self"))throw new Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.");return e.classNameAliases=B(e.classNameAliases||{}),_(e)}function Ie(e){return e?e.endsWithParent||Ie(e.starts):!1}function Ut(e){return e.variants&&!e.cachedVariants&&(e.cachedVariants=e.variants.map(function(t){return B(e,{variants:null},t)})),e.cachedVariants?e.cachedVariants:Ie(e)?B(e,{starts:e.starts?B(e.starts):null}):Object.isFrozen(e)?B(e):e}var $t="11.11.1";class Gt extends Error{constructor(t,i){super(t),this.name="HTMLInjectionError",this.html=i}}const ee=we,_e=B,Me=Symbol("nomatch"),Wt=7,Be=function(e){const t=Object.create(null),i=Object.create(null),u=[];let b=!0;const _="Could not find the language '{}', did you forget to load/include a language module?",c={disableAutodetect:!0,name:"Plain text",contains:[]};let r={ignoreUnescapedHTML:!1,throwUnescapedHTML:!1,noHighlightRe:/^(no-?highlight)$/i,languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-",cssSelector:"pre code",languages:null,__emitter:et};function l(n){return r.noHighlightRe.test(n)}function w(n){let a=n.className+" ";a+=n.parentNode?n.parentNode.className:"";const h=r.languageDetectRe.exec(a);if(h){const d=T(h[1]);return d||(be(_.replace("{}",h[1])),be("Falling back to no-highlight mode for this block.",n)),d?h[1]:"no-highlight"}return a.split(/\s+/).find(d=>l(d)||T(d))}function M(n,a,h){let d="",x="";typeof a=="object"?(d=n,h=a.ignoreIllegals,x=a.language):(L("10.7.0","highlight(lang, code, ...args) has been deprecated."),L("10.7.0",`Please use highlight(code, options) instead.
|
|
2
2
|
https://github.com/highlightjs/highlight.js/issues/2277`),x=n,d=a),h===void 0&&(h=!0);const S={code:d,language:x};G("before:highlight",S);const I=S.result?S.result:j(S.language,S.code,h);return I.code=S.code,G("after:highlight",I),I}function j(n,a,h,d){const x=Object.create(null);function S(s,o){return s.keywords[o]}function I(){if(!f.keywords){O.addText(E);return}let s=0;f.keywordPatternRe.lastIndex=0;let o=f.keywordPatternRe.exec(E),g="";for(;o;){g+=E.substring(s,o.index);const p=A.case_insensitive?o[0].toLowerCase():o[0],R=S(f,p);if(R){const[k,Ze]=R;if(O.addText(g),g="",x[p]=(x[p]||0)+1,x[p]<=Wt&&(F+=Ze),k.startsWith("_"))g+=o[0];else{const Je=A.classNameAliases[k]||k;N(o[0],Je)}}else g+=o[0];s=f.keywordPatternRe.lastIndex,o=f.keywordPatternRe.exec(E)}g+=E.substring(s),O.addText(g)}function W(){if(E==="")return;let s=null;if(typeof f.subLanguage=="string"){if(!t[f.subLanguage]){O.addText(E);return}s=j(f.subLanguage,E,!0,ge[f.subLanguage]),ge[f.subLanguage]=s._top}else s=J(E,f.subLanguage.length?f.subLanguage:null);f.relevance>0&&(F+=s.relevance),O.__addSublanguage(s._emitter,s.language)}function y(){f.subLanguage!=null?W():I(),E=""}function N(s,o){s!==""&&(O.startScope(o),O.addText(s),O.endScope())}function ae(s,o){let g=1;const p=o.length-1;for(;g<=p;){if(!s._emit[g]){g++;continue}const R=A.classNameAliases[s[g]]||s[g],k=o[g];R?N(k,R):(E=k,I(),E=""),g++}}function le(s,o){return s.scope&&typeof s.scope=="string"&&O.openNode(A.classNameAliases[s.scope]||s.scope),s.beginScope&&(s.beginScope._wrap?(N(E,A.classNameAliases[s.beginScope._wrap]||s.beginScope._wrap),E=""):s.beginScope._multi&&(ae(s.beginScope,o),E="")),f=Object.create(s,{parent:{value:f}}),f}function ue(s,o,g){let p=st(s.endRe,g);if(p){if(s["on:end"]){const R=new he(s);s["on:end"](o,R),R.isMatchIgnored&&(p=!1)}if(p){for(;s.endsParent&&s.parent;)s=s.parent;return s}}if(s.endsWithParent)return ue(s.parent,o,g)}function Ke(s){return f.matcher.regexIndex===0?(E+=s[0],1):(m=!0,0)}function Fe(s){const o=s[0],g=s.rule,p=new he(g),R=[g.__beforeBegin,g["on:begin"]];for(const k of R)if(k&&(k(s,p),p.isMatchIgnored))return Ke(o);return g.skip?E+=o:(g.excludeBegin&&(E+=o),y(),!g.returnBegin&&!g.excludeBegin&&(E=o)),le(g,s),g.returnBegin?0:o.length}function ze(s){const o=s[0],g=a.substring(s.index),p=ue(f,s,g);if(!p)return Me;const R=f;f.endScope&&f.endScope._wrap?(y(),N(o,f.endScope._wrap)):f.endScope&&f.endScope._multi?(y(),ae(f.endScope,s)):R.skip?E+=o:(R.returnEnd||R.excludeEnd||(E+=o),y(),R.excludeEnd&&(E=o));do f.scope&&O.closeNode(),!f.skip&&!f.subLanguage&&(F+=f.relevance),f=f.parent;while(f!==p.parent);return p.starts&&le(p.starts,s),R.returnEnd?0:o.length}function Xe(){const s=[];for(let o=f;o!==A;o=o.parent)o.scope&&s.unshift(o.scope);s.forEach(o=>O.openNode(o))}let K={};function fe(s,o){const g=o&&o[0];if(E+=s,g==null)return y(),0;if(K.type==="begin"&&o.type==="end"&&K.index===o.index&&g===""){if(E+=a.slice(o.index,o.index+1),!b){const p=new Error(`0 width match regex (${n})`);throw p.languageName=n,p.badRule=K.rule,p}return 1}if(K=o,o.type==="begin")return Fe(o);if(o.type==="illegal"&&!h){const p=new Error('Illegal lexeme "'+g+'" for mode "'+(f.scope||"<unnamed>")+'"');throw p.mode=f,p}else if(o.type==="end"){const p=ze(o);if(p!==Me)return p}if(o.type==="illegal"&&g==="")return E+=`
|
|
3
3
|
`,1;if(Q>1e5&&Q>o.index*3)throw new Error("potential infinite loop, way more iterations than matches");return E+=g,g.length}const A=T(n);if(!A)throw v(_.replace("{}",n)),new Error('Unknown language: "'+n+'"');const Ye=Pt(A);let q="",f=d||Ye;const ge={},O=new r.__emitter(r);Xe();let E="",F=0,D=0,Q=0,m=!1;try{if(A.__emitTokens)A.__emitTokens(a,O);else{for(f.matcher.considerAll();;){Q++,m?m=!1:f.matcher.considerAll(),f.matcher.lastIndex=D;const s=f.matcher.exec(a);if(!s)break;const o=a.substring(D,s.index),g=fe(o,s);D=s.index+g}fe(a.substring(D))}return O.finalize(),q=O.toHTML(),{language:n,value:q,relevance:F,illegal:!1,_emitter:O,_top:f}}catch(s){if(s.message&&s.message.includes("Illegal"))return{language:n,value:ee(a),illegal:!0,relevance:0,_illegalBy:{message:s.message,index:D,context:a.slice(D-100,D+100),mode:s.mode,resultSoFar:q},_emitter:O};if(b)return{language:n,value:ee(a),illegal:!1,relevance:0,errorRaised:s,_emitter:O,_top:f};throw s}}function Z(n){const a={value:ee(n),illegal:!1,relevance:0,_top:c,_emitter:new r.__emitter(r)};return a._emitter.addText(n),a}function J(n,a){a=a||r.languages||Object.keys(t);const h=Z(n),d=a.filter(T).filter(oe).map(y=>j(y,n,!1));d.unshift(h);const x=d.sort((y,N)=>{if(y.relevance!==N.relevance)return N.relevance-y.relevance;if(y.language&&N.language){if(T(y.language).supersetOf===N.language)return 1;if(T(N.language).supersetOf===y.language)return-1}return 0}),[S,I]=x,W=S;return W.secondBest=I,W}function De(n,a,h){const d=a&&i[a]||h;n.classList.add("hljs"),n.classList.add(`language-${d}`)}function V(n){let a=null;const h=w(n);if(l(h))return;if(G("before:highlightElement",{el:n,language:h}),n.dataset.highlighted){console.log("Element previously highlighted. To highlight again, first unset `dataset.highlighted`.",n);return}if(n.children.length>0&&(r.ignoreUnescapedHTML||(console.warn("One of your code blocks includes unescaped HTML. This is a potentially serious security risk."),console.warn("https://github.com/highlightjs/highlight.js/wiki/security"),console.warn("The element with unescaped HTML:"),console.warn(n)),r.throwUnescapedHTML))throw new Gt("One of your code blocks includes unescaped HTML.",n.innerHTML);a=n;const d=a.textContent,x=h?M(d,{language:h,ignoreIllegals:!0}):J(d);n.innerHTML=x.value,n.dataset.highlighted="yes",De(n,h,x.language),n.result={language:x.language,re:x.relevance,relevance:x.relevance},x.secondBest&&(n.secondBest={language:x.secondBest.language,relevance:x.secondBest.relevance}),G("after:highlightElement",{el:n,result:x,text:d})}function ve(n){r=_e(r,n)}const Ce=()=>{$(),L("10.6.0","initHighlighting() deprecated. Use highlightAll() now.")};function Le(){$(),L("10.6.0","initHighlightingOnLoad() deprecated. Use highlightAll() now.")}let re=!1;function $(){function n(){$()}if(document.readyState==="loading"){re||window.addEventListener("DOMContentLoaded",n,!1),re=!0;return}document.querySelectorAll(r.cssSelector).forEach(V)}function He(n,a){let h=null;try{h=a(e)}catch(d){if(v("Language definition for '{}' could not be registered.".replace("{}",n)),b)v(d);else throw d;h=c}h.name||(h.name=n),t[n]=h,h.rawDefinition=a.bind(null,e),h.aliases&&ce(h.aliases,{languageName:n})}function je(n){delete t[n];for(const a of Object.keys(i))i[a]===n&&delete i[a]}function Pe(){return Object.keys(t)}function T(n){return n=(n||"").toLowerCase(),t[n]||t[i[n]]}function ce(n,{languageName:a}){typeof n=="string"&&(n=[n]),n.forEach(h=>{i[h.toLowerCase()]=a})}function oe(n){const a=T(n);return a&&!a.disableAutodetect}function Ue(n){n["before:highlightBlock"]&&!n["before:highlightElement"]&&(n["before:highlightElement"]=a=>{n["before:highlightBlock"](Object.assign({block:a.el},a))}),n["after:highlightBlock"]&&!n["after:highlightElement"]&&(n["after:highlightElement"]=a=>{n["after:highlightBlock"](Object.assign({block:a.el},a))})}function $e(n){Ue(n),u.push(n)}function Ge(n){const a=u.indexOf(n);a!==-1&&u.splice(a,1)}function G(n,a){const h=n;u.forEach(function(d){d[h]&&d[h](a)})}function We(n){return L("10.7.0","highlightBlock will be removed entirely in v12.0"),L("10.7.0","Please use highlightElement now."),V(n)}Object.assign(e,{highlight:M,highlightAuto:J,highlightAll:$,highlightElement:V,highlightBlock:We,configure:ve,initHighlighting:Ce,initHighlightingOnLoad:Le,registerLanguage:He,unregisterLanguage:je,listLanguages:Pe,getLanguage:T,registerAliases:ce,autoDetection:oe,inherit:_e,addPlugin:$e,removePlugin:Ge}),e.debugMode=function(){b=!1},e.safeMode=function(){b=!0},e.versionString=$t,e.regex={concat:C,lookahead:Oe,either:ne,optional:nt,anyNumberOfTimes:tt};for(const n in z)typeof z[n]=="object"&&xe(z[n]);return Object.assign(e,z),e},H=Be({});H.newInstance=()=>Be({});var Kt=H;H.HighlightJS=H;H.default=H;const zt=Ve(Kt);export{zt as HighlightJS,zt as default};
|