termdock 1.4.70 → 1.4.72
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -0
- package/dist/client/assets/{ModelPreview-Cs14IQuQ.js → ModelPreview-i9dS1A3I.js} +1 -1
- package/dist/client/assets/{abnfDiagram-VRR7QNED-DuDfdv-6.js → abnfDiagram-VRR7QNED-Cs-ty-HA.js} +1 -1
- package/dist/client/assets/{arc-CSYzGV_2.js → arc-CIX5k_JD.js} +1 -1
- package/dist/client/assets/{architectureDiagram-ZJ3FMSHR-YiZ0HXNi.js → architectureDiagram-ZJ3FMSHR-DnPnQoJy.js} +1 -1
- package/dist/client/assets/{blockDiagram-677ZJIJ3-IaQMzmYv.js → blockDiagram-677ZJIJ3-Dhew-L5v.js} +1 -1
- package/dist/client/assets/{c4Diagram-LMCZKHZV-BwyuIPRL.js → c4Diagram-LMCZKHZV-BSsEMeWn.js} +1 -1
- package/dist/client/assets/channel-CASnt1pP.js +1 -0
- package/dist/client/assets/{chunk-2Q5K7J3B-BtHQYLBT.js → chunk-2Q5K7J3B-BWm--1Mo.js} +1 -1
- package/dist/client/assets/{chunk-32BRIVSS-CXrK07sW.js → chunk-32BRIVSS-D_WFmuvb.js} +1 -1
- package/dist/client/assets/{chunk-5VM5RSS4-BfBshn11.js → chunk-5VM5RSS4-CQdzqlQg.js} +1 -1
- package/dist/client/assets/{chunk-EX3LRPZG-BJbilNaf.js → chunk-EX3LRPZG-Cm3zLQpt.js} +1 -1
- package/dist/client/assets/{chunk-JWPE2WC7-wgcmSRlL.js → chunk-JWPE2WC7-Cmcv5sEN.js} +1 -1
- package/dist/client/assets/{chunk-MOJQB5TN-8VZVUeDI.js → chunk-MOJQB5TN-Bam44EwJ.js} +1 -1
- package/dist/client/assets/{chunk-RYQCIY6F-B900YBFa.js → chunk-RYQCIY6F-DIVOuE8y.js} +1 -1
- package/dist/client/assets/{chunk-V7JOEXUC-BK4_RLz-.js → chunk-V7JOEXUC-BfOTf7yg.js} +1 -1
- package/dist/client/assets/{chunk-VR4S4FIN-Kj5_ycjB.js → chunk-VR4S4FIN-DbiXn0dd.js} +1 -1
- package/dist/client/assets/{chunk-XXDRQBXY-BolvPLRz.js → chunk-XXDRQBXY-C4pOe4OC.js} +1 -1
- package/dist/client/assets/classDiagram-OUVF2IWQ-Da_SQWEq.js +1 -0
- package/dist/client/assets/classDiagram-v2-EOCWNBFH-Da_SQWEq.js +1 -0
- package/dist/client/assets/{cose-bilkent-JH36ORCC-9_9_SLqv.js → cose-bilkent-JH36ORCC-CCoSPmoc.js} +1 -1
- package/dist/client/assets/{cynefin-VYW2F7L2-yEpoi3vG.js → cynefin-VYW2F7L2-B2lYz7sl.js} +1 -1
- package/dist/client/assets/{cynefinDiagram-TSTJHNR4-Dy-gvup_.js → cynefinDiagram-TSTJHNR4-Cd7crN9s.js} +1 -1
- package/dist/client/assets/{dagre-VKFMJZFB-CNjJ5pRK.js → dagre-VKFMJZFB-q9Fj9HuQ.js} +1 -1
- package/dist/client/assets/{diagram-FQU43EPY-uZ73Rj3T.js → diagram-FQU43EPY-iqHy5YHW.js} +1 -1
- package/dist/client/assets/{diagram-G47NLZAW-Cxt7TYZT.js → diagram-G47NLZAW-BE-jlh1B.js} +1 -1
- package/dist/client/assets/{diagram-NH7WQ7WH-B4TKinuZ.js → diagram-NH7WQ7WH-DmP7_WOC.js} +1 -1
- package/dist/client/assets/{diagram-OA4YK3LP-CGSuenHU.js → diagram-OA4YK3LP-CHx_k8Ar.js} +1 -1
- package/dist/client/assets/{diagram-WEI45ONY-DqqzDaUZ.js → diagram-WEI45ONY-D0wImpAF.js} +1 -1
- package/dist/client/assets/{ebnfDiagram-CCIWWBDH-B2SgXL5w.js → ebnfDiagram-CCIWWBDH-DyCB0rF8.js} +1 -1
- package/dist/client/assets/{erDiagram-Q63AITRT-D-ibkr8y.js → erDiagram-Q63AITRT-SRcd1B2Z.js} +1 -1
- package/dist/client/assets/{flowDiagram-23GEKE2U-BPeyMqr-.js → flowDiagram-23GEKE2U-D0i9mFwt.js} +1 -1
- package/dist/client/assets/{ganttDiagram-NO4QXBWP-DcEqEzzu.js → ganttDiagram-NO4QXBWP-BfwGIYDn.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-IHSO6WYX-DTZ6UCQY.js → gitGraphDiagram-IHSO6WYX-BXkJQP7O.js} +1 -1
- package/dist/client/assets/index-BA-DqJM2.js +451 -0
- package/dist/client/assets/{infoDiagram-FWYZ7A6U-BHrzVXeX.js → infoDiagram-FWYZ7A6U-CM-Ebv9R.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-FXEZZL3T-CwuoQJku.js → ishikawaDiagram-FXEZZL3T-U-qnP7xi.js} +1 -1
- package/dist/client/assets/{journeyDiagram-5HDEW3XC-ZEcy0rDZ.js → journeyDiagram-5HDEW3XC-D4RxvsuL.js} +1 -1
- package/dist/client/assets/{kanban-definition-HUTT4EX6-BDBawNbr.js → kanban-definition-HUTT4EX6-Dm_asCYK.js} +1 -1
- package/dist/client/assets/{linear-smYQWzDV.js → linear-Zhe8d635.js} +1 -1
- package/dist/client/assets/{mermaid.core-eJs7Ouvw.js → mermaid.core-D_NWIuMY.js} +4 -4
- package/dist/client/assets/{mindmap-definition-LN4V7U3C-BK1IZ9zg.js → mindmap-definition-LN4V7U3C-DXpNidd5.js} +1 -1
- package/dist/client/assets/{pegDiagram-2B236MQR-B3-mVNgX.js → pegDiagram-2B236MQR-DdOwOEgu.js} +1 -1
- package/dist/client/assets/{pieDiagram-ENE6RG2P-Bkeeyx5l.js → pieDiagram-ENE6RG2P-D_A9HoY_.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-ABIIQ3AL-DouIklEO.js → quadrantDiagram-ABIIQ3AL-D-Y3cLEF.js} +1 -1
- package/dist/client/assets/{railroadDiagram-RFXS5EU6-BB_jXQd2.js → railroadDiagram-RFXS5EU6-DD6eFV82.js} +1 -1
- package/dist/client/assets/{requirementDiagram-TGXJPOKE-u56IHfiR.js → requirementDiagram-TGXJPOKE-B59HJgL2.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-HTMAVEWB-eDi2cs5E.js → sankeyDiagram-HTMAVEWB-C2BvHMH8.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-DBY2YBRQ-DnAA2xYa.js → sequenceDiagram-DBY2YBRQ-3004tCRm.js} +1 -1
- package/dist/client/assets/{sizeCapture-X5ZJPWSS-BogPui79.js → sizeCapture-X5ZJPWSS-DCBhg8QW.js} +1 -1
- package/dist/client/assets/{stateDiagram-2N3HPSRC--EeXDy2M.js → stateDiagram-2N3HPSRC-BVqiKVe0.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-6OUMAXLB-Z0GHWyoF.js +1 -0
- package/dist/client/assets/{swimlanes-5IMT3BWC-DZAw7-kY.js → swimlanes-5IMT3BWC-DS6-sdEW.js} +2 -2
- package/dist/client/assets/swimlanesDiagram-G3AALYLV-el4HDX0G.js +8 -0
- package/dist/client/assets/{timeline-definition-FHXFAJF6-DkcnpB-Y.js → timeline-definition-FHXFAJF6-wVOEmQwG.js} +1 -1
- package/dist/client/assets/{vennDiagram-L72KCM5P-BStcDGEf.js → vennDiagram-L72KCM5P-DMZiSJDk.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-EHGQE667-Cj4CQn1M.js → wardleyDiagram-EHGQE667-Dx5Aob4Y.js} +1 -1
- package/dist/client/assets/{xychartDiagram-FW5EYKEG-_uBCjai8.js → xychartDiagram-FW5EYKEG-BqMhkhmW.js} +1 -1
- package/dist/client/index.html +1 -1
- package/dist/client/sw.js +1 -1
- package/dist/server/agent/autoTitle.js +16 -36
- package/dist/server/agent/installers.js +98 -15
- package/dist/server/agent/pluginPackages.js +289 -0
- package/dist/server/agent/plugins.js +164 -15
- package/dist/server/agent/registry.js +1 -1
- package/dist/server/agent/session.js +10 -1
- package/dist/server/agent/titleNamerCatalog.js +131 -2
- package/dist/server/agentHook.js +47 -0
- package/dist/server/cli.js +224 -13
- package/dist/server/entry.js +11 -2
- package/dist/server/routes/terminal.js +267 -37
- package/dist/server/utils/csrfProtection.js +4 -1
- package/dist/server/utils/settings.js +18 -5
- package/package.json +1 -1
- package/runtime-manifest.json +2 -2
- package/dist/client/assets/channel-BotxtiNE.js +0 -1
- package/dist/client/assets/classDiagram-OUVF2IWQ-CLWlbTSC.js +0 -1
- package/dist/client/assets/classDiagram-v2-EOCWNBFH-CLWlbTSC.js +0 -1
- package/dist/client/assets/index-ivnGnbVg.js +0 -451
- package/dist/client/assets/stateDiagram-v2-6OUMAXLB-Dc-3HKTs.js +0 -1
- package/dist/client/assets/swimlanesDiagram-G3AALYLV-CI1Wlr0V.js +0 -8
|
@@ -14,22 +14,23 @@ import { getPtyHostManager } from '../ptyhost/manager.js';
|
|
|
14
14
|
import { pathValidator } from '../utils/pathValidator.js';
|
|
15
15
|
import { TERMINAL, TMUX } from '../config.js';
|
|
16
16
|
import { localAccessManager } from '../utils/localAccess.js';
|
|
17
|
-
import { normalizeLocalAccessName, getLocaleSetting, setLocaleSetting, getContextDraftHeightSetting, setContextDraftHeightSetting, getAutoRenameAgentsSetting, setAutoRenameAgentsSetting, getAutoRenameNamerSetting, setAutoRenameNamerSetting, getAutoRenameModelsSetting, setAutoRenameModelsSetting, } from '../utils/settings.js';
|
|
17
|
+
import { normalizeLocalAccessName, getLocaleSetting, setLocaleSetting, getContextDraftHeightSetting, setContextDraftHeightSetting, getAutoRenameAgentsSetting, setAutoRenameAgentsSetting, getAutoRenameNamerSetting, setAutoRenameNamerSetting, getAutoRenameModelsSetting, setAutoRenameModelsSetting, getAutoRenameIntervalMinutesSetting, setAutoRenameIntervalMinutesSetting, } from '../utils/settings.js';
|
|
18
18
|
import { loadContextDraft, saveContextDraft } from '../utils/contextDraft.js';
|
|
19
19
|
import { getOnboardingServerUrl } from '../onboardingServer.js';
|
|
20
20
|
import { getFocusSequence, removeClientFocusState, scanFocusTrackingMode, setClientFocusState, } from '../utils/tmuxFocus.js';
|
|
21
21
|
import { extractProgramLabelFromArgs, normalizeProgramName, normalizeTmuxMetadataProgram, selectTmuxForegroundProgram, tmuxMetadataChanged, } from '../utils/tmuxProgramDetection.js';
|
|
22
22
|
import { agentBySlug, buildResumeCommand, detectAgentFromCommand, listAgents, } from '../agent/registry.js';
|
|
23
|
-
import { applyAgentEvent, defaultAgentSessionState, parseAgentEvent, } from '../agent/session.js';
|
|
23
|
+
import { applyAgentEvent, agentEventMatchesCurrentAgent, defaultAgentSessionState, parseAgentEvent, } from '../agent/session.js';
|
|
24
24
|
import { listAllHookAgents, refreshStaleHooksAtLaunch, installHooksForSlug, uninstallHooksForSlug, } from '../agent/installers.js';
|
|
25
25
|
import { loadPlugins, savePlugin, removePlugin, readPluginIcon, validateManifest, } from '../agent/plugins.js';
|
|
26
|
+
import { checkPluginPackageUpdate, commitPreparedPlugin, preparePluginPackage, } from '../agent/pluginPackages.js';
|
|
26
27
|
import { clearPluginAgents, registerPluginAgents } from '../agent/registry.js';
|
|
27
28
|
import { notifyAgentTransition, notifyTerminalExit } from '../notifications/pushService.js';
|
|
28
29
|
import { setClientViewingSession } from '../notifications/pushViewers.js';
|
|
29
30
|
import { getQuotaStatus, refreshQuota, startQuotaManager, } from '../quota/QuotaManager.js';
|
|
30
31
|
import { TermdockAutoUpdateManager } from '../utils/autoUpdate.js';
|
|
31
|
-
import { cleanTerminalContext, generateAgentTitle, isNewAgentSessionId, shouldReplaceAutoTitle } from '../agent/autoTitle.js';
|
|
32
|
-
import { getTitleNamerCatalog } from '../agent/titleNamerCatalog.js';
|
|
32
|
+
import { AUTO_TITLE_MIN_CONTEXT_CHARS, cleanTerminalContext, generateAgentTitle, isNewAgentSessionId, isAutoTitleReevaluationDue, shouldReplaceAutoTitle, } from '../agent/autoTitle.js';
|
|
33
|
+
import { getTitleNamerCatalog, invalidateTitleNamerCatalog } from '../agent/titleNamerCatalog.js';
|
|
33
34
|
const router = express.Router();
|
|
34
35
|
const execFileAsync = promisify(execFile);
|
|
35
36
|
const TERMDOCK_DIR = `${os.homedir()}/.termdock`;
|
|
@@ -2128,7 +2129,6 @@ function detectSessionAgent(session) {
|
|
|
2128
2129
|
return null;
|
|
2129
2130
|
}
|
|
2130
2131
|
const AUTO_TITLE_CONTEXT_LIMIT = 24_000;
|
|
2131
|
-
const AUTO_TITLE_REEVALUATE_MS = 60 * 60_000;
|
|
2132
2132
|
const autoTitlePending = new Set();
|
|
2133
2133
|
function appendAutoTitleContext(session, data) {
|
|
2134
2134
|
session.autoTitleContext = (session.autoTitleContext + data).slice(-AUTO_TITLE_CONTEXT_LIMIT);
|
|
@@ -2155,10 +2155,14 @@ async function maybeAutoRenameSession(sessionId, agent, rawContext) {
|
|
|
2155
2155
|
const record = globalSessionState.sessions.find((item) => item.backendSessionId === sessionId);
|
|
2156
2156
|
if (!record || (record.customName === true && !record.autoTitle))
|
|
2157
2157
|
return;
|
|
2158
|
-
if (record.autoTitle &&
|
|
2158
|
+
if (record.autoTitle && !isAutoTitleReevaluationDue(record.autoTitle.updatedAt, getAutoRenameIntervalMinutesSetting()))
|
|
2159
2159
|
return;
|
|
2160
2160
|
const context = cleanTerminalContext(rawContext);
|
|
2161
|
-
|
|
2161
|
+
// A new session gets one cheap attempt after its first real exchange. Short
|
|
2162
|
+
// conversations ("hi" plus a brief reply) are still valid sessions; the
|
|
2163
|
+
// one-hour re-evaluation guard below keeps this from becoming noisy after a
|
|
2164
|
+
// title exists.
|
|
2165
|
+
if (context.length < AUTO_TITLE_MIN_CONTEXT_CHARS)
|
|
2162
2166
|
return;
|
|
2163
2167
|
const contentHash = createHash('sha256').update(context).digest('hex');
|
|
2164
2168
|
if (record.autoTitle?.contentHash === contentHash)
|
|
@@ -2389,6 +2393,8 @@ function applyAgentSignals(sessionId, session, events, notification) {
|
|
|
2389
2393
|
let identityChanged = false;
|
|
2390
2394
|
let completedTurnAgent = null;
|
|
2391
2395
|
for (const event of events) {
|
|
2396
|
+
if (!agentEventMatchesCurrentAgent(session.agent, event))
|
|
2397
|
+
continue;
|
|
2392
2398
|
// An event naming an agent brands the pane even when the process poll
|
|
2393
2399
|
// can't see through a wrapper: identity via protocol.
|
|
2394
2400
|
if (!session.agent && event.agent) {
|
|
@@ -2501,18 +2507,9 @@ function maybeRefreshGitStatusForAgent(sessionId, session) {
|
|
|
2501
2507
|
session.gitAgentActivitySeen = activity;
|
|
2502
2508
|
refreshGitStatus(sessionId, session);
|
|
2503
2509
|
}
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
*/
|
|
2508
|
-
function resolveAgentResumeCommand(sessionId, session) {
|
|
2509
|
-
const liveAgent = session.agent;
|
|
2510
|
-
const live = session.agentSession;
|
|
2511
|
-
if (liveAgent && live?.sessionId) {
|
|
2512
|
-
const cmd = buildResumeCommand(liveAgent, live.sessionId, live.launchArgv);
|
|
2513
|
-
if (cmd)
|
|
2514
|
-
return cmd;
|
|
2515
|
-
}
|
|
2510
|
+
function resolveAgentResumeTarget(sessionId, session) {
|
|
2511
|
+
// Resume always starts a new Agent process. Never offer it while the pane is
|
|
2512
|
+
// still inside an Agent TUI, even when that Agent reports a completed turn.
|
|
2516
2513
|
if (!isPaneAtShellPrompt(session))
|
|
2517
2514
|
return null;
|
|
2518
2515
|
const record = globalSessionState.sessions.find((s) => s.backendSessionId === sessionId);
|
|
@@ -2522,7 +2519,66 @@ function resolveAgentResumeCommand(sessionId, session) {
|
|
|
2522
2519
|
const agent = agentBySlug(persisted.slug);
|
|
2523
2520
|
if (!agent)
|
|
2524
2521
|
return null;
|
|
2525
|
-
|
|
2522
|
+
const command = buildResumeCommand(agent, persisted.sessionId, persisted.launchArgv);
|
|
2523
|
+
return command ? { slug: agent.slug, nativeSessionId: persisted.sessionId, command } : null;
|
|
2524
|
+
}
|
|
2525
|
+
function findActiveAgentResumeOwner(excludedBackendSessionId, target) {
|
|
2526
|
+
for (const [backendSessionId, candidate] of terminalSessions) {
|
|
2527
|
+
if (backendSessionId === excludedBackendSessionId || candidate.agent?.slug !== target.slug)
|
|
2528
|
+
continue;
|
|
2529
|
+
const record = globalSessionState.sessions.find((entry) => entry.backendSessionId === backendSessionId);
|
|
2530
|
+
const nativeSessionId = candidate.agentSession?.sessionId ?? record?.agentResume?.sessionId ?? null;
|
|
2531
|
+
if (nativeSessionId === target.nativeSessionId)
|
|
2532
|
+
return backendSessionId;
|
|
2533
|
+
}
|
|
2534
|
+
return null;
|
|
2535
|
+
}
|
|
2536
|
+
const codexSessionFileCache = new Map();
|
|
2537
|
+
async function findCodexSessionFile(nativeSessionId) {
|
|
2538
|
+
const cachedPath = codexSessionFileCache.get(nativeSessionId);
|
|
2539
|
+
if (cachedPath && fs.existsSync(cachedPath))
|
|
2540
|
+
return cachedPath;
|
|
2541
|
+
if (!/^[a-zA-Z0-9._-]{8,160}$/.test(nativeSessionId))
|
|
2542
|
+
return null;
|
|
2543
|
+
const root = path.join(os.homedir(), '.codex', 'sessions');
|
|
2544
|
+
const pending = [root];
|
|
2545
|
+
let visited = 0;
|
|
2546
|
+
while (pending.length > 0 && visited < 20_000) {
|
|
2547
|
+
const directory = pending.pop();
|
|
2548
|
+
let entries;
|
|
2549
|
+
try {
|
|
2550
|
+
entries = await fs.promises.readdir(directory, { withFileTypes: true });
|
|
2551
|
+
}
|
|
2552
|
+
catch {
|
|
2553
|
+
continue;
|
|
2554
|
+
}
|
|
2555
|
+
for (const entry of entries) {
|
|
2556
|
+
visited += 1;
|
|
2557
|
+
const candidate = path.join(directory, entry.name);
|
|
2558
|
+
if (entry.isDirectory())
|
|
2559
|
+
pending.push(candidate);
|
|
2560
|
+
else if (entry.isFile() && entry.name.endsWith(`-${nativeSessionId}.jsonl`)) {
|
|
2561
|
+
codexSessionFileCache.set(nativeSessionId, candidate);
|
|
2562
|
+
return candidate;
|
|
2563
|
+
}
|
|
2564
|
+
}
|
|
2565
|
+
}
|
|
2566
|
+
return null;
|
|
2567
|
+
}
|
|
2568
|
+
async function findExternalCodexWriter(target) {
|
|
2569
|
+
if (target.slug !== 'codex' || process.platform !== 'linux')
|
|
2570
|
+
return null;
|
|
2571
|
+
const sessionFile = await findCodexSessionFile(target.nativeSessionId);
|
|
2572
|
+
if (!sessionFile)
|
|
2573
|
+
return null;
|
|
2574
|
+
try {
|
|
2575
|
+
const { stdout } = await execFileAsync('lsof', ['-t', '--', sessionFile], { timeout: 2500, maxBuffer: 64 * 1024 });
|
|
2576
|
+
const pid = stdout.split(/\s+/).map(Number).find((value) => Number.isInteger(value) && value > 1);
|
|
2577
|
+
return pid ?? null;
|
|
2578
|
+
}
|
|
2579
|
+
catch {
|
|
2580
|
+
return null;
|
|
2581
|
+
}
|
|
2526
2582
|
}
|
|
2527
2583
|
/**
|
|
2528
2584
|
* Deliver a prompt into an agent's PTY: bracketed paste (so multi-line
|
|
@@ -4372,6 +4428,7 @@ async function getSettingsPayload() {
|
|
|
4372
4428
|
autoRenameAgents: getAutoRenameAgentsSetting(),
|
|
4373
4429
|
autoRenameNamer: getAutoRenameNamerSetting(),
|
|
4374
4430
|
autoRenameModels: getAutoRenameModelsSetting(),
|
|
4431
|
+
autoRenameIntervalMinutes: getAutoRenameIntervalMinutesSetting(),
|
|
4375
4432
|
localAccess: {
|
|
4376
4433
|
...localAccess,
|
|
4377
4434
|
interfaces,
|
|
@@ -4427,21 +4484,23 @@ router.put('/settings', async (req, res) => {
|
|
|
4427
4484
|
const requested = body.autoRenameAgents
|
|
4428
4485
|
.filter((slug) => typeof slug === 'string' && knownSlugs.has(slug));
|
|
4429
4486
|
setAutoRenameAgentsSetting(requested);
|
|
4487
|
+
invalidateTitleNamerCatalog();
|
|
4430
4488
|
}
|
|
4431
4489
|
if (typeof body.autoRenameNamer === 'string') {
|
|
4432
|
-
|
|
4490
|
+
const available = new Set((await getTitleNamerCatalog()).filter((entry) => entry.available).map((entry) => entry.slug));
|
|
4491
|
+
if (body.autoRenameNamer !== 'auto' && !available.has(body.autoRenameNamer)) {
|
|
4433
4492
|
res.status(400).json({ error: 'Unsupported automatic title agent', code: 'AUTO_RENAME_NAMER_INVALID' });
|
|
4434
4493
|
return;
|
|
4435
4494
|
}
|
|
4436
4495
|
setAutoRenameNamerSetting(body.autoRenameNamer);
|
|
4496
|
+
invalidateTitleNamerCatalog();
|
|
4437
4497
|
}
|
|
4438
4498
|
if (body.autoRenameModels && typeof body.autoRenameModels === 'object') {
|
|
4439
4499
|
const requested = body.autoRenameModels;
|
|
4440
4500
|
const catalog = await getTitleNamerCatalog();
|
|
4441
4501
|
const allowed = new Map(catalog.map((namer) => [namer.slug, new Set(namer.models.map((model) => model.id))]));
|
|
4442
4502
|
const models = {};
|
|
4443
|
-
for (const slug of
|
|
4444
|
-
const model = requested[slug];
|
|
4503
|
+
for (const [slug, model] of Object.entries(requested)) {
|
|
4445
4504
|
if (model === undefined || model === '')
|
|
4446
4505
|
continue;
|
|
4447
4506
|
if (typeof model !== 'string' || !allowed.get(slug)?.has(model)) {
|
|
@@ -4452,6 +4511,12 @@ router.put('/settings', async (req, res) => {
|
|
|
4452
4511
|
}
|
|
4453
4512
|
setAutoRenameModelsSetting(models);
|
|
4454
4513
|
}
|
|
4514
|
+
if (typeof body.autoRenameIntervalMinutes === 'number'
|
|
4515
|
+
&& Number.isInteger(body.autoRenameIntervalMinutes)
|
|
4516
|
+
&& body.autoRenameIntervalMinutes >= 5
|
|
4517
|
+
&& body.autoRenameIntervalMinutes <= 1440) {
|
|
4518
|
+
setAutoRenameIntervalMinutesSetting(body.autoRenameIntervalMinutes);
|
|
4519
|
+
}
|
|
4455
4520
|
if (body.localAccess && typeof body.localAccess === 'object') {
|
|
4456
4521
|
const localAccessBody = body.localAccess;
|
|
4457
4522
|
if (localAccessBody.reset === true) {
|
|
@@ -4494,8 +4559,8 @@ router.put('/context-draft', (req, res) => {
|
|
|
4494
4559
|
res.json(doc);
|
|
4495
4560
|
});
|
|
4496
4561
|
// ── Agent hooks API (rich status channel installers, built-in + plugin) ──
|
|
4497
|
-
router.get('/agent-hooks', (_req, res) => {
|
|
4498
|
-
res.json({ agents: listAllHookAgents() });
|
|
4562
|
+
router.get('/agent-hooks', async (_req, res) => {
|
|
4563
|
+
res.json({ agents: await listAllHookAgents() });
|
|
4499
4564
|
});
|
|
4500
4565
|
router.get('/agent-launchers', async (_req, res) => {
|
|
4501
4566
|
const pathEntries = buildAugmentedPath().split(path.delimiter).filter(Boolean);
|
|
@@ -4601,11 +4666,134 @@ router.get('/agent-plugins', (_req, res) => {
|
|
|
4601
4666
|
iconMode: p.manifest.iconMode ?? 'mask',
|
|
4602
4667
|
hasHooks: p.manifest.hooks !== undefined,
|
|
4603
4668
|
hasResume: p.manifest.resume !== undefined,
|
|
4669
|
+
hasTitleNamer: p.manifest.titleNamer !== undefined,
|
|
4604
4670
|
hasIcon: p.iconPath !== null,
|
|
4671
|
+
sourceType: p.source?.type ?? 'manifest',
|
|
4672
|
+
source: p.source?.source ?? null,
|
|
4673
|
+
revision: p.source?.revision ?? null,
|
|
4674
|
+
latestRevision: p.source?.latestRevision ?? null,
|
|
4675
|
+
checkedAt: p.source?.checkedAt ?? null,
|
|
4676
|
+
updatedAt: p.source?.updatedAt ?? null,
|
|
4677
|
+
updateSupported: Boolean(p.source?.source && p.source.type !== 'manifest'),
|
|
4678
|
+
updateAvailable: Boolean(p.source?.revision && p.source.latestRevision && p.source.revision !== p.source.latestRevision),
|
|
4605
4679
|
})),
|
|
4606
4680
|
errors: errors.map((e) => ({ slug: e.slug, errors: e.errors, code: e.code, migration: e.migration })),
|
|
4607
4681
|
});
|
|
4608
4682
|
});
|
|
4683
|
+
function reloadAgentPlugins() {
|
|
4684
|
+
invalidateTitleNamerCatalog();
|
|
4685
|
+
clearPluginAgents();
|
|
4686
|
+
registerPluginAgents(loadPlugins().plugins);
|
|
4687
|
+
}
|
|
4688
|
+
router.post('/agent-plugins/install', async (req, res) => {
|
|
4689
|
+
const source = typeof req.body?.source === 'string' ? req.body.source.trim() : '';
|
|
4690
|
+
if (!source) {
|
|
4691
|
+
res.status(400).json({ error: 'Plugin source is required', code: 'AGENT_PLUGIN_SOURCE_REQUIRED' });
|
|
4692
|
+
return;
|
|
4693
|
+
}
|
|
4694
|
+
try {
|
|
4695
|
+
const prepared = await preparePluginPackage(source);
|
|
4696
|
+
const dir = await commitPreparedPlugin(prepared, false);
|
|
4697
|
+
reloadAgentPlugins();
|
|
4698
|
+
res.json({ slug: prepared.manifest.slug, dir, state: 'installed', source: prepared.metadata });
|
|
4699
|
+
}
|
|
4700
|
+
catch (error) {
|
|
4701
|
+
const detail = error;
|
|
4702
|
+
res.status(400).json({
|
|
4703
|
+
error: detail.message,
|
|
4704
|
+
code: detail.code ?? 'AGENT_PLUGIN_INSTALL_FAILED',
|
|
4705
|
+
migration: detail.migration,
|
|
4706
|
+
});
|
|
4707
|
+
}
|
|
4708
|
+
});
|
|
4709
|
+
router.post('/agent-plugins/:slug/check-update', async (req, res) => {
|
|
4710
|
+
const plugin = loadPlugins().plugins.find((entry) => entry.manifest.slug === req.params.slug);
|
|
4711
|
+
if (!plugin) {
|
|
4712
|
+
res.status(404).json({ error: `Plugin "${req.params.slug}" not found` });
|
|
4713
|
+
return;
|
|
4714
|
+
}
|
|
4715
|
+
try {
|
|
4716
|
+
res.json(await checkPluginPackageUpdate(plugin));
|
|
4717
|
+
}
|
|
4718
|
+
catch (error) {
|
|
4719
|
+
res.status(400).json({ error: error.message, code: 'AGENT_PLUGIN_UPDATE_CHECK_FAILED' });
|
|
4720
|
+
}
|
|
4721
|
+
});
|
|
4722
|
+
router.post('/agent-plugins/:slug/update', async (req, res) => {
|
|
4723
|
+
const slug = req.params.slug;
|
|
4724
|
+
const plugin = loadPlugins().plugins.find((entry) => entry.manifest.slug === slug);
|
|
4725
|
+
if (!plugin) {
|
|
4726
|
+
res.status(404).json({ error: `Plugin "${slug}" not found` });
|
|
4727
|
+
return;
|
|
4728
|
+
}
|
|
4729
|
+
if (!plugin.source?.source || plugin.source.type === 'manifest') {
|
|
4730
|
+
res.status(400).json({ error: 'This plugin has no update source', code: 'AGENT_PLUGIN_UPDATE_UNSUPPORTED' });
|
|
4731
|
+
return;
|
|
4732
|
+
}
|
|
4733
|
+
let prepared = null;
|
|
4734
|
+
let oldHooksRemoved = false;
|
|
4735
|
+
let committed = false;
|
|
4736
|
+
try {
|
|
4737
|
+
// Download and validate the complete replacement before touching the live
|
|
4738
|
+
// package or its currently installed hooks.
|
|
4739
|
+
prepared = await preparePluginPackage(plugin.source.source, slug);
|
|
4740
|
+
const hooksChanged = JSON.stringify(plugin.manifest.hooks ?? null) !== JSON.stringify(prepared.manifest.hooks ?? null);
|
|
4741
|
+
const titleCapabilityChanged = JSON.stringify(plugin.manifest.titleNamer ?? null) !== JSON.stringify(prepared.manifest.titleNamer ?? null);
|
|
4742
|
+
const hookState = (await listAllHookAgents()).find((entry) => entry.slug === slug)?.state;
|
|
4743
|
+
const restoreHooks = hookState === 'installed' || hookState === 'outdated';
|
|
4744
|
+
if (restoreHooks && plugin.manifest.hooks) {
|
|
4745
|
+
await uninstallHooksForSlug(slug);
|
|
4746
|
+
oldHooksRemoved = true;
|
|
4747
|
+
}
|
|
4748
|
+
const dir = await commitPreparedPlugin(prepared, true);
|
|
4749
|
+
prepared = null;
|
|
4750
|
+
committed = true;
|
|
4751
|
+
reloadAgentPlugins();
|
|
4752
|
+
let hookWarning = null;
|
|
4753
|
+
if (restoreHooks && hooksChanged) {
|
|
4754
|
+
hookWarning = 'Hook declarations changed during update. Review them, then install hooks again.';
|
|
4755
|
+
}
|
|
4756
|
+
else if (restoreHooks) {
|
|
4757
|
+
try {
|
|
4758
|
+
await installHooksForSlug(slug);
|
|
4759
|
+
}
|
|
4760
|
+
catch (error) {
|
|
4761
|
+
hookWarning = error.message;
|
|
4762
|
+
}
|
|
4763
|
+
}
|
|
4764
|
+
let titleWarning = null;
|
|
4765
|
+
if (titleCapabilityChanged) {
|
|
4766
|
+
setAutoRenameAgentsSetting(getAutoRenameAgentsSetting().filter((entry) => entry !== slug));
|
|
4767
|
+
if (getAutoRenameNamerSetting() === slug)
|
|
4768
|
+
setAutoRenameNamerSetting('auto');
|
|
4769
|
+
const models = getAutoRenameModelsSetting();
|
|
4770
|
+
delete models[slug];
|
|
4771
|
+
setAutoRenameModelsSetting(models);
|
|
4772
|
+
invalidateTitleNamerCatalog();
|
|
4773
|
+
titleWarning = 'Title command declarations changed during update. Automatic titles for this plugin were disabled; review and enable them again.';
|
|
4774
|
+
}
|
|
4775
|
+
res.json({
|
|
4776
|
+
slug,
|
|
4777
|
+
dir,
|
|
4778
|
+
state: 'updated',
|
|
4779
|
+
source: loadPlugins().plugins.find((entry) => entry.manifest.slug === slug)?.source,
|
|
4780
|
+
hookWarning,
|
|
4781
|
+
titleWarning,
|
|
4782
|
+
});
|
|
4783
|
+
}
|
|
4784
|
+
catch (error) {
|
|
4785
|
+
if (prepared)
|
|
4786
|
+
await prepared.cleanup().catch(() => undefined);
|
|
4787
|
+
if (oldHooksRemoved && !committed)
|
|
4788
|
+
await installHooksForSlug(slug).catch(() => undefined);
|
|
4789
|
+
const detail = error;
|
|
4790
|
+
res.status(400).json({
|
|
4791
|
+
error: detail.message,
|
|
4792
|
+
code: detail.code ?? 'AGENT_PLUGIN_UPDATE_FAILED',
|
|
4793
|
+
migration: detail.migration,
|
|
4794
|
+
});
|
|
4795
|
+
}
|
|
4796
|
+
});
|
|
4609
4797
|
router.post('/agent-plugins', async (req, res) => {
|
|
4610
4798
|
const manifest = req.body;
|
|
4611
4799
|
if (!manifest || typeof manifest !== 'object') {
|
|
@@ -4631,9 +4819,7 @@ router.post('/agent-plugins', async (req, res) => {
|
|
|
4631
4819
|
return;
|
|
4632
4820
|
}
|
|
4633
4821
|
const dir = savePlugin(validation.manifest);
|
|
4634
|
-
|
|
4635
|
-
const loaded = loadPlugins();
|
|
4636
|
-
registerPluginAgents(loaded.plugins);
|
|
4822
|
+
reloadAgentPlugins();
|
|
4637
4823
|
res.json({ slug: validation.manifest.slug, dir, state: 'created' });
|
|
4638
4824
|
}
|
|
4639
4825
|
catch (error) {
|
|
@@ -4643,13 +4829,32 @@ router.post('/agent-plugins', async (req, res) => {
|
|
|
4643
4829
|
router.delete('/agent-plugins/:slug', async (req, res) => {
|
|
4644
4830
|
const { slug } = req.params;
|
|
4645
4831
|
try {
|
|
4832
|
+
const plugin = loadPlugins().plugins.find((entry) => entry.manifest.slug === slug);
|
|
4833
|
+
if (!plugin) {
|
|
4834
|
+
res.status(404).json({ error: `Plugin "${slug}" not found` });
|
|
4835
|
+
return;
|
|
4836
|
+
}
|
|
4837
|
+
// Uninstall while the manifest is still present: after deletion we no
|
|
4838
|
+
// longer know the target file or event shape and would orphan commands in
|
|
4839
|
+
// the Agent's config.
|
|
4840
|
+
if (plugin.manifest.hooks)
|
|
4841
|
+
await uninstallHooksForSlug(slug);
|
|
4646
4842
|
removePlugin(slug);
|
|
4843
|
+
invalidateTitleNamerCatalog();
|
|
4844
|
+
setAutoRenameAgentsSetting(getAutoRenameAgentsSetting().filter((entry) => entry !== slug));
|
|
4845
|
+
if (getAutoRenameNamerSetting() === slug)
|
|
4846
|
+
setAutoRenameNamerSetting('auto');
|
|
4847
|
+
const models = getAutoRenameModelsSetting();
|
|
4848
|
+
if (models[slug]) {
|
|
4849
|
+
delete models[slug];
|
|
4850
|
+
setAutoRenameModelsSetting(models);
|
|
4851
|
+
}
|
|
4647
4852
|
clearPluginAgents();
|
|
4648
4853
|
registerPluginAgents(loadPlugins().plugins);
|
|
4649
4854
|
res.json({ slug, state: 'removed' });
|
|
4650
4855
|
}
|
|
4651
4856
|
catch (error) {
|
|
4652
|
-
res.status(
|
|
4857
|
+
res.status(400).json({ error: error.message });
|
|
4653
4858
|
}
|
|
4654
4859
|
});
|
|
4655
4860
|
router.get('/agent-plugin-icon/:slug', (req, res) => {
|
|
@@ -5099,7 +5304,7 @@ router.post('/:sessionId/input', express.text({ type: '*/*' }), (req, res) => {
|
|
|
5099
5304
|
* bracketed paste. Refused while an agent turn is in flight — pasting a
|
|
5100
5305
|
* resume into a busy TUI would corrupt its input.
|
|
5101
5306
|
*/
|
|
5102
|
-
router.post('/:sessionId/agent-resume', (req, res) => {
|
|
5307
|
+
router.post('/:sessionId/agent-resume', async (req, res) => {
|
|
5103
5308
|
const { sessionId } = req.params;
|
|
5104
5309
|
const session = terminalSessions.get(sessionId);
|
|
5105
5310
|
if (!session) {
|
|
@@ -5109,14 +5314,30 @@ router.post('/:sessionId/agent-resume', (req, res) => {
|
|
|
5109
5314
|
if (session.agent && live && (live.status === 'working' || live.status === 'waiting')) {
|
|
5110
5315
|
return res.status(409).json({ error: 'agent is busy; wait for the turn to finish' });
|
|
5111
5316
|
}
|
|
5112
|
-
const
|
|
5113
|
-
if (!
|
|
5317
|
+
const target = resolveAgentResumeTarget(sessionId, session);
|
|
5318
|
+
if (!target) {
|
|
5114
5319
|
return res.status(404).json({ error: 'no resumable agent session for this pane' });
|
|
5115
5320
|
}
|
|
5321
|
+
const activeOwner = findActiveAgentResumeOwner(sessionId, target);
|
|
5322
|
+
if (activeOwner) {
|
|
5323
|
+
return res.status(409).json({
|
|
5324
|
+
error: 'this agent session is already open in another terminal',
|
|
5325
|
+
code: 'AGENT_SESSION_ACTIVE_ELSEWHERE',
|
|
5326
|
+
activeBackendSessionId: activeOwner,
|
|
5327
|
+
});
|
|
5328
|
+
}
|
|
5329
|
+
const activeWriterPid = await findExternalCodexWriter(target);
|
|
5330
|
+
if (activeWriterPid) {
|
|
5331
|
+
return res.status(409).json({
|
|
5332
|
+
error: 'this Codex thread already has an active writer',
|
|
5333
|
+
code: 'AGENT_SESSION_ACTIVE_ELSEWHERE',
|
|
5334
|
+
activeWriterPid,
|
|
5335
|
+
});
|
|
5336
|
+
}
|
|
5116
5337
|
try {
|
|
5117
|
-
session.ptyProcess.write(buildBracketedSubmitBytes(command));
|
|
5338
|
+
session.ptyProcess.write(buildBracketedSubmitBytes(target.command));
|
|
5118
5339
|
session.lastActivity = Date.now();
|
|
5119
|
-
res.json({ success: true, command });
|
|
5340
|
+
res.json({ success: true, command: target.command });
|
|
5120
5341
|
}
|
|
5121
5342
|
catch (error) {
|
|
5122
5343
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -5124,16 +5345,25 @@ router.post('/:sessionId/agent-resume', (req, res) => {
|
|
|
5124
5345
|
}
|
|
5125
5346
|
});
|
|
5126
5347
|
/** Whether this pane has a resumable agent conversation (for the UI affordance). */
|
|
5127
|
-
router.get('/:sessionId/agent-resume', (req, res) => {
|
|
5348
|
+
router.get('/:sessionId/agent-resume', async (req, res) => {
|
|
5128
5349
|
const { sessionId } = req.params;
|
|
5129
5350
|
const session = terminalSessions.get(sessionId);
|
|
5130
5351
|
if (!session) {
|
|
5131
5352
|
return res.status(404).json({ error: 'Terminal session not found' });
|
|
5132
5353
|
}
|
|
5133
|
-
const
|
|
5354
|
+
const target = resolveAgentResumeTarget(sessionId, session);
|
|
5134
5355
|
const busy = !!(session.agent && session.agentSession
|
|
5135
5356
|
&& (session.agentSession.status === 'working' || session.agentSession.status === 'waiting'));
|
|
5136
|
-
|
|
5357
|
+
const activeBackendSessionId = target ? findActiveAgentResumeOwner(sessionId, target) : null;
|
|
5358
|
+
const activeWriterPid = target && !activeBackendSessionId ? await findExternalCodexWriter(target) : null;
|
|
5359
|
+
res.json({
|
|
5360
|
+
available: target !== null && activeBackendSessionId === null && activeWriterPid === null,
|
|
5361
|
+
command: target?.command ?? null,
|
|
5362
|
+
busy,
|
|
5363
|
+
conflict: activeBackendSessionId || activeWriterPid ? 'active-elsewhere' : null,
|
|
5364
|
+
activeBackendSessionId,
|
|
5365
|
+
activeWriterPid,
|
|
5366
|
+
});
|
|
5137
5367
|
});
|
|
5138
5368
|
router.post('/:sessionId/resize', async (req, res) => {
|
|
5139
5369
|
const { sessionId } = req.params;
|
|
@@ -62,8 +62,11 @@ export class CsrfProtection {
|
|
|
62
62
|
/**
|
|
63
63
|
* Express中间件:验证CSRF令牌
|
|
64
64
|
*/
|
|
65
|
-
verifyMiddleware() {
|
|
65
|
+
verifyMiddleware(options) {
|
|
66
66
|
return (req, res, next) => {
|
|
67
|
+
if (options?.bypass?.(req)) {
|
|
68
|
+
return next();
|
|
69
|
+
}
|
|
67
70
|
// 跳过安全检查和GET/HEAD/OPTIONS请求
|
|
68
71
|
const safeMethods = ['GET', 'HEAD', 'OPTIONS'];
|
|
69
72
|
if (safeMethods.includes(req.method)) {
|
|
@@ -71,15 +71,20 @@ function normalizeSettings(value) {
|
|
|
71
71
|
locale: typeof raw.locale === 'string' && raw.locale === 'zh' ? 'zh' : 'en',
|
|
72
72
|
contextDraftHeight: normalizeContextDraftHeight(raw.contextDraftHeight),
|
|
73
73
|
autoRenameAgents,
|
|
74
|
-
autoRenameNamer: raw.autoRenameNamer === '
|
|
75
|
-
? raw.autoRenameNamer
|
|
76
|
-
: 'auto',
|
|
74
|
+
autoRenameNamer: typeof raw.autoRenameNamer === 'string' && /^(?:auto|[a-z][a-z0-9-]{0,39})$/.test(raw.autoRenameNamer)
|
|
75
|
+
? raw.autoRenameNamer : 'auto',
|
|
77
76
|
autoRenameModels: raw.autoRenameModels && typeof raw.autoRenameModels === 'object'
|
|
78
77
|
? Object.fromEntries(Object.entries(raw.autoRenameModels)
|
|
79
|
-
.filter((entry) => ([
|
|
78
|
+
.filter((entry) => (/^[a-z][a-z0-9-]{0,39}$/.test(entry[0])
|
|
80
79
|
&& typeof entry[1] === 'string'
|
|
81
80
|
&& /^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,119}$/.test(entry[1]))))
|
|
82
81
|
: {},
|
|
82
|
+
autoRenameIntervalMinutes: typeof raw.autoRenameIntervalMinutes === 'number'
|
|
83
|
+
&& Number.isInteger(raw.autoRenameIntervalMinutes)
|
|
84
|
+
&& raw.autoRenameIntervalMinutes >= 5
|
|
85
|
+
&& raw.autoRenameIntervalMinutes <= 1440
|
|
86
|
+
? raw.autoRenameIntervalMinutes
|
|
87
|
+
: 10,
|
|
83
88
|
updatedAt: typeof raw.updatedAt === 'number' ? raw.updatedAt : Date.now(),
|
|
84
89
|
};
|
|
85
90
|
}
|
|
@@ -219,10 +224,18 @@ export function setAutoRenameNamerSetting(namer) {
|
|
|
219
224
|
export function getAutoRenameModelsSetting() {
|
|
220
225
|
return { ...loadSettings().autoRenameModels };
|
|
221
226
|
}
|
|
227
|
+
export function getAutoRenameIntervalMinutesSetting() {
|
|
228
|
+
return loadSettings().autoRenameIntervalMinutes;
|
|
229
|
+
}
|
|
230
|
+
export function setAutoRenameIntervalMinutesSetting(minutes) {
|
|
231
|
+
return updateSettings((settings) => {
|
|
232
|
+
settings.autoRenameIntervalMinutes = Math.max(5, Math.min(1440, Math.round(minutes)));
|
|
233
|
+
});
|
|
234
|
+
}
|
|
222
235
|
export function setAutoRenameModelsSetting(models) {
|
|
223
236
|
return updateSettings((settings) => {
|
|
224
237
|
settings.autoRenameModels = Object.fromEntries(Object.entries(models)
|
|
225
|
-
.filter(([slug, model]) => ([
|
|
238
|
+
.filter(([slug, model]) => (/^[a-z][a-z0-9-]{0,39}$/.test(slug)
|
|
226
239
|
&& /^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,119}$/.test(model))));
|
|
227
240
|
});
|
|
228
241
|
}
|
package/package.json
CHANGED
package/runtime-manifest.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"packageName": "termdock",
|
|
4
|
-
"version": "1.4.
|
|
4
|
+
"version": "1.4.72",
|
|
5
5
|
"runtimeProtocolVersion": 1,
|
|
6
6
|
"minimumDesktopVersion": "1.4.46",
|
|
7
7
|
"nodeMajor": 22,
|
|
8
8
|
"dependencyHash": "sha256-HxC6p3b4kFesefw+vmNYJtPtdIKJaZR1jFNb7DRJqjU=",
|
|
9
|
-
"serverBundleHash": "sha256-
|
|
9
|
+
"serverBundleHash": "sha256-4Ti+r7BiQtaMfPn2SORjiaLhcxgBT7xkGbas+Dsh6Zo=",
|
|
10
10
|
"entrypoint": "dist/server/cli.js",
|
|
11
11
|
"clientEntrypoint": "dist/client/index.html"
|
|
12
12
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{U as a,C as n}from"./mermaid.core-eJs7Ouvw.js";const t=(r,o)=>a.lang.round(n.parse(r)[o]);export{t as c};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{s as a,a as s,c as t,C as e}from"./chunk-V7JOEXUC-BK4_RLz-.js";import{b as i}from"./mermaid.core-eJs7Ouvw.js";import"./chunk-5VM5RSS4-BfBshn11.js";import"./chunk-XXDRQBXY-BolvPLRz.js";import"./chunk-VR4S4FIN-Kj5_ycjB.js";import"./chunk-32BRIVSS-CXrK07sW.js";import"./purify.es-CC4Brkr_.js";import"./index-ivnGnbVg.js";import"./react-XDCjal2T.js";import"./dnd-BCFAPC0L.js";import"./terminal-C7X98Amf.js";var g={parser:t,get db(){return new e},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{g as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{s as a,a as s,c as t,C as e}from"./chunk-V7JOEXUC-BK4_RLz-.js";import{b as i}from"./mermaid.core-eJs7Ouvw.js";import"./chunk-5VM5RSS4-BfBshn11.js";import"./chunk-XXDRQBXY-BolvPLRz.js";import"./chunk-VR4S4FIN-Kj5_ycjB.js";import"./chunk-32BRIVSS-CXrK07sW.js";import"./purify.es-CC4Brkr_.js";import"./index-ivnGnbVg.js";import"./react-XDCjal2T.js";import"./dnd-BCFAPC0L.js";import"./terminal-C7X98Amf.js";var g={parser:t,get db(){return new e},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{g as diagram};
|