chorus-codes 0.7.0 → 0.7.1
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/.next/BUILD_ID +1 -1
- package/.next/build-manifest.json +3 -3
- package/.next/cache/.previewinfo +1 -1
- package/.next/cache/.rscinfo +1 -1
- package/.next/fallback-build-manifest.json +3 -3
- package/.next/prerender-manifest.json +3 -3
- package/.next/server/app/_global-error.html +1 -1
- package/.next/server/app/_global-error.rsc +1 -1
- package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/_not-found.html +1 -1
- package/.next/server/app/_not-found.rsc +1 -1
- package/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/new.html +1 -1
- package/.next/server/app/new.rsc +1 -1
- package/.next/server/app/new.segments/_full.segment.rsc +1 -1
- package/.next/server/app/new.segments/_head.segment.rsc +1 -1
- package/.next/server/app/new.segments/_index.segment.rsc +1 -1
- package/.next/server/app/new.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/new.segments/new/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/new.segments/new.segment.rsc +1 -1
- package/.next/server/app/onboarding.html +1 -1
- package/.next/server/app/onboarding.rsc +1 -1
- package/.next/server/app/onboarding.segments/_full.segment.rsc +1 -1
- package/.next/server/app/onboarding.segments/_head.segment.rsc +1 -1
- package/.next/server/app/onboarding.segments/_index.segment.rsc +1 -1
- package/.next/server/app/onboarding.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/onboarding.segments/onboarding/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/onboarding.segments/onboarding.segment.rsc +1 -1
- package/.next/server/app/personas.html +1 -1
- package/.next/server/app/personas.rsc +1 -1
- package/.next/server/app/personas.segments/_full.segment.rsc +1 -1
- package/.next/server/app/personas.segments/_head.segment.rsc +1 -1
- package/.next/server/app/personas.segments/_index.segment.rsc +1 -1
- package/.next/server/app/personas.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/personas.segments/personas/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/personas.segments/personas.segment.rsc +1 -1
- package/.next/server/app/settings.html +1 -1
- package/.next/server/app/settings.rsc +1 -1
- package/.next/server/app/settings.segments/_full.segment.rsc +1 -1
- package/.next/server/app/settings.segments/_head.segment.rsc +1 -1
- package/.next/server/app/settings.segments/_index.segment.rsc +1 -1
- package/.next/server/app/settings.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/settings.segments/settings.segment.rsc +1 -1
- package/.next/server/app/templates.html +1 -1
- package/.next/server/app/templates.rsc +1 -1
- package/.next/server/app/templates.segments/_full.segment.rsc +1 -1
- package/.next/server/app/templates.segments/_head.segment.rsc +1 -1
- package/.next/server/app/templates.segments/_index.segment.rsc +1 -1
- package/.next/server/app/templates.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/templates.segments/templates/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/templates.segments/templates.segment.rsc +1 -1
- package/.next/server/middleware-build-manifest.js +3 -3
- package/.next/server/pages/404.html +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/server/server-reference-manifest.js +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/trace +1 -1
- package/.next/trace-build +1 -1
- package/dist/cli/commands/doctor.js +116 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/init.js +211 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/start.js +240 -0
- package/dist/cli/commands/start.js.map +1 -0
- package/dist/cli/commands/status.js +54 -0
- package/dist/cli/commands/status.js.map +1 -0
- package/dist/cli/commands/stop.js +97 -0
- package/dist/cli/commands/stop.js.map +1 -0
- package/dist/cli/connect.js +108 -0
- package/dist/cli/connect.js.map +1 -0
- package/dist/cli/index.js +99 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/port-utils.js +260 -0
- package/dist/cli/port-utils.js.map +1 -0
- package/dist/cli/runtime-env.js +60 -0
- package/dist/cli/runtime-env.js.map +1 -0
- package/dist/cli/shared.js +54 -0
- package/dist/cli/shared.js.map +1 -0
- package/dist/cli/ui.js +60 -0
- package/dist/cli/ui.js.map +1 -0
- package/dist/daemon/agents/claude.js +98 -0
- package/dist/daemon/agents/claude.js.map +1 -0
- package/dist/daemon/agents/codex.js +160 -0
- package/dist/daemon/agents/codex.js.map +1 -0
- package/dist/daemon/agents/gemini.js +111 -0
- package/dist/daemon/agents/gemini.js.map +1 -0
- package/dist/daemon/agents/index.js +59 -0
- package/dist/daemon/agents/index.js.map +1 -0
- package/dist/daemon/agents/kimi.js +206 -0
- package/dist/daemon/agents/kimi.js.map +1 -0
- package/dist/daemon/agents/opencode.js +228 -0
- package/dist/daemon/agents/opencode.js.map +1 -0
- package/dist/daemon/agents/openrouter.js +274 -0
- package/dist/daemon/agents/openrouter.js.map +1 -0
- package/dist/daemon/agents/parsers/claude.js +63 -0
- package/dist/daemon/agents/parsers/claude.js.map +1 -0
- package/dist/daemon/agents/parsers/codex.js +51 -0
- package/dist/daemon/agents/parsers/codex.js.map +1 -0
- package/dist/daemon/agents/parsers/gemini.js +144 -0
- package/dist/daemon/agents/parsers/gemini.js.map +1 -0
- package/dist/daemon/agents/parsers/index.js +31 -0
- package/dist/daemon/agents/parsers/index.js.map +1 -0
- package/dist/daemon/agents/parsers/kimi.js +8 -0
- package/dist/daemon/agents/parsers/kimi.js.map +1 -0
- package/dist/daemon/agents/parsers/opencode.js +105 -0
- package/dist/daemon/agents/parsers/opencode.js.map +1 -0
- package/dist/daemon/agents/parsers/openrouter.js +69 -0
- package/dist/daemon/agents/parsers/openrouter.js.map +1 -0
- package/dist/daemon/agents/parsers/shared.js +17 -0
- package/dist/daemon/agents/parsers/shared.js.map +1 -0
- package/dist/daemon/agents/preflight.js +83 -0
- package/dist/daemon/agents/preflight.js.map +1 -0
- package/dist/daemon/agents/quote.js +45 -0
- package/dist/daemon/agents/quote.js.map +1 -0
- package/dist/daemon/agents/sandbox-guard.js +69 -0
- package/dist/daemon/agents/sandbox-guard.js.map +1 -0
- package/dist/daemon/agents/types.js +6 -0
- package/dist/daemon/agents/types.js.map +1 -0
- package/dist/daemon/api-response.js +65 -0
- package/dist/daemon/api-response.js.map +1 -0
- package/dist/daemon/error-detector.js +329 -0
- package/dist/daemon/error-detector.js.map +1 -0
- package/dist/daemon/headless.js +533 -0
- package/dist/daemon/headless.js.map +1 -0
- package/dist/daemon/index.js +333 -0
- package/dist/daemon/index.js.map +1 -0
- package/dist/daemon/openrouter.js +192 -0
- package/dist/daemon/openrouter.js.map +1 -0
- package/dist/daemon/orchestrators/claude.js +163 -0
- package/dist/daemon/orchestrators/claude.js.map +1 -0
- package/dist/daemon/orchestrators/codex.js +101 -0
- package/dist/daemon/orchestrators/codex.js.map +1 -0
- package/dist/daemon/orchestrators/cursor-windsurf.js +118 -0
- package/dist/daemon/orchestrators/cursor-windsurf.js.map +1 -0
- package/dist/daemon/orchestrators/gemini.js +108 -0
- package/dist/daemon/orchestrators/gemini.js.map +1 -0
- package/dist/daemon/orchestrators/index.js +90 -0
- package/dist/daemon/orchestrators/index.js.map +1 -0
- package/dist/daemon/orchestrators/kimi.js +108 -0
- package/dist/daemon/orchestrators/kimi.js.map +1 -0
- package/dist/daemon/orchestrators/opencode.js +152 -0
- package/dist/daemon/orchestrators/opencode.js.map +1 -0
- package/dist/daemon/orchestrators/shared.js +60 -0
- package/dist/daemon/orchestrators/shared.js.map +1 -0
- package/dist/daemon/output-watcher.js +131 -0
- package/dist/daemon/output-watcher.js.map +1 -0
- package/dist/daemon/participant-aborts.js +123 -0
- package/dist/daemon/participant-aborts.js.map +1 -0
- package/dist/daemon/reaper.js +46 -0
- package/dist/daemon/reaper.js.map +1 -0
- package/dist/daemon/routes/chats-events.js +62 -0
- package/dist/daemon/routes/chats-events.js.map +1 -0
- package/dist/daemon/routes/chats-stream.js +241 -0
- package/dist/daemon/routes/chats-stream.js.map +1 -0
- package/dist/daemon/routes/chats-validation.js +13 -0
- package/dist/daemon/routes/chats-validation.js.map +1 -0
- package/dist/daemon/routes/chats.js +545 -0
- package/dist/daemon/routes/chats.js.map +1 -0
- package/dist/daemon/routes/openrouter.js +103 -0
- package/dist/daemon/routes/openrouter.js.map +1 -0
- package/dist/daemon/routes/settings.js +199 -0
- package/dist/daemon/routes/settings.js.map +1 -0
- package/dist/daemon/routes/stats.js +155 -0
- package/dist/daemon/routes/stats.js.map +1 -0
- package/dist/daemon/routes/system.js +208 -0
- package/dist/daemon/routes/system.js.map +1 -0
- package/dist/daemon/routes/templates-personas.js +254 -0
- package/dist/daemon/routes/templates-personas.js.map +1 -0
- package/dist/daemon/routes/voices.js +139 -0
- package/dist/daemon/routes/voices.js.map +1 -0
- package/dist/daemon/runner/doer-driver.js +346 -0
- package/dist/daemon/runner/doer-driver.js.map +1 -0
- package/dist/daemon/runner/doer.js +336 -0
- package/dist/daemon/runner/doer.js.map +1 -0
- package/dist/daemon/runner/prior-round.js +140 -0
- package/dist/daemon/runner/prior-round.js.map +1 -0
- package/dist/daemon/runner/prompt-builder.js +292 -0
- package/dist/daemon/runner/prompt-builder.js.map +1 -0
- package/dist/daemon/runner/review-only-phase.js +103 -0
- package/dist/daemon/runner/review-only-phase.js.map +1 -0
- package/dist/daemon/runner/reviewer-driver.js +410 -0
- package/dist/daemon/runner/reviewer-driver.js.map +1 -0
- package/dist/daemon/runner/reviewer.js +384 -0
- package/dist/daemon/runner/reviewer.js.map +1 -0
- package/dist/daemon/runner/run-with-fallback.js +56 -0
- package/dist/daemon/runner/run-with-fallback.js.map +1 -0
- package/dist/daemon/runner/sanitize-name.js +8 -0
- package/dist/daemon/runner/sanitize-name.js.map +1 -0
- package/dist/daemon/runner/stream-file-writer.js +116 -0
- package/dist/daemon/runner/stream-file-writer.js.map +1 -0
- package/dist/daemon/runner/swap-sidecar.js +102 -0
- package/dist/daemon/runner/swap-sidecar.js.map +1 -0
- package/dist/daemon/runner/template-fallback.js +119 -0
- package/dist/daemon/runner/template-fallback.js.map +1 -0
- package/dist/daemon/runner/types.js +3 -0
- package/dist/daemon/runner/types.js.map +1 -0
- package/dist/daemon/runner/verdict.js +51 -0
- package/dist/daemon/runner/verdict.js.map +1 -0
- package/dist/daemon/runner-multiplex.js +364 -0
- package/dist/daemon/runner-multiplex.js.map +1 -0
- package/dist/daemon/runner.js +427 -0
- package/dist/daemon/runner.js.map +1 -0
- package/dist/daemon/ship.js +340 -0
- package/dist/daemon/ship.js.map +1 -0
- package/dist/daemon/template-cache.js +37 -0
- package/dist/daemon/template-cache.js.map +1 -0
- package/dist/daemon/tmux-types.js +9 -0
- package/dist/daemon/tmux-types.js.map +1 -0
- package/dist/daemon/tmux.js +341 -0
- package/dist/daemon/tmux.js.map +1 -0
- package/dist/lib/atomic-write.js +55 -0
- package/dist/lib/atomic-write.js.map +1 -0
- package/dist/lib/chat-events-bus.js +27 -0
- package/dist/lib/chat-events-bus.js.map +1 -0
- package/dist/lib/chat-slug.js +105 -0
- package/dist/lib/chat-slug.js.map +1 -0
- package/dist/lib/cli-detect.js +388 -0
- package/dist/lib/cli-detect.js.map +1 -0
- package/dist/lib/cli-health.js +156 -0
- package/dist/lib/cli-health.js.map +1 -0
- package/dist/lib/cli-paths.js +113 -0
- package/dist/lib/cli-paths.js.map +1 -0
- package/dist/lib/cli-precheck.js +141 -0
- package/dist/lib/cli-precheck.js.map +1 -0
- package/dist/lib/db/chats.js +244 -0
- package/dist/lib/db/chats.js.map +1 -0
- package/dist/lib/db/connection.js +254 -0
- package/dist/lib/db/connection.js.map +1 -0
- package/dist/lib/db/index.js +34 -0
- package/dist/lib/db/index.js.map +1 -0
- package/dist/lib/db/personas.js +65 -0
- package/dist/lib/db/personas.js.map +1 -0
- package/dist/lib/db/phase-events.js +172 -0
- package/dist/lib/db/phase-events.js.map +1 -0
- package/dist/lib/db/secrets.js +53 -0
- package/dist/lib/db/secrets.js.map +1 -0
- package/dist/lib/db/settings.js +47 -0
- package/dist/lib/db/settings.js.map +1 -0
- package/dist/lib/db/templates.js +75 -0
- package/dist/lib/db/templates.js.map +1 -0
- package/dist/lib/db/voices.js +184 -0
- package/dist/lib/db/voices.js.map +1 -0
- package/dist/lib/lineage-maps.js +200 -0
- package/dist/lib/lineage-maps.js.map +1 -0
- package/dist/lib/logger.js +186 -0
- package/dist/lib/logger.js.map +1 -0
- package/dist/lib/personas.js +117 -0
- package/dist/lib/personas.js.map +1 -0
- package/dist/lib/runtime-path.js +222 -0
- package/dist/lib/runtime-path.js.map +1 -0
- package/dist/lib/settings/billing.js +58 -0
- package/dist/lib/settings/billing.js.map +1 -0
- package/dist/lib/settings/permissions.js +81 -0
- package/dist/lib/settings/permissions.js.map +1 -0
- package/dist/lib/settings/transport.js +113 -0
- package/dist/lib/settings/transport.js.map +1 -0
- package/dist/lib/telemetry.js +290 -0
- package/dist/lib/telemetry.js.map +1 -0
- package/dist/lib/template-schema.js +319 -0
- package/dist/lib/template-schema.js.map +1 -0
- package/dist/lib/template-validation.js +82 -0
- package/dist/lib/template-validation.js.map +1 -0
- package/dist/lib/voices.js +533 -0
- package/dist/lib/voices.js.map +1 -0
- package/dist/mcp/client.js +138 -0
- package/dist/mcp/client.js.map +1 -0
- package/dist/mcp/index.js +178 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/tools.js +355 -0
- package/dist/mcp/tools.js.map +1 -0
- package/package.json +2 -1
- package/.next/dev/static/chunks/05w9_next_dist_shared_lib_0beh7rg._.js +0 -6077
- package/.next/dev/static/chunks/05w9_next_dist_shared_lib_0beh7rg._.js.map +0 -69
- package/.next/dev/static/chunks/05w9_next_dist_shared_lib_0pjsj.j._.js +0 -6318
- package/.next/dev/static/chunks/05w9_next_dist_shared_lib_0pjsj.j._.js.map +0 -71
- package/.next/dev/types/cache-life.d.ts +0 -145
- package/.next/dev/types/routes.d.ts +0 -84
- package/.next/dev/types/validator.ts +0 -178
- /package/.next/static/{dJlbRLlhISA0JGtHKVqgQ → 8H2I8TTPlrKuWCV-SxY5f}/_buildManifest.js +0 -0
- /package/.next/static/{dJlbRLlhISA0JGtHKVqgQ → 8H2I8TTPlrKuWCV-SxY5f}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{dJlbRLlhISA0JGtHKVqgQ → 8H2I8TTPlrKuWCV-SxY5f}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TmuxManagerImpl = void 0;
|
|
4
|
+
const child_process_1 = require("child_process");
|
|
5
|
+
/**
|
|
6
|
+
* TmuxManagerImpl: Session manager for Chorus CLI integrations.
|
|
7
|
+
*
|
|
8
|
+
* Responsibilities:
|
|
9
|
+
* - Acquire sessions respecting share-session policy (across rounds, rarely across phases)
|
|
10
|
+
* - Spawn fresh tmux sessions via the agent shim's buildLaunchCommand
|
|
11
|
+
* - Provide tmux operations (sendKeys, pasteBuffer, capturePane)
|
|
12
|
+
* - Track sessions in memory + reconcile from `tmux ls` on startup
|
|
13
|
+
* - Reap orphans via reapOnce() called by index.ts every 5 min
|
|
14
|
+
*
|
|
15
|
+
* Hard rules:
|
|
16
|
+
* 1. %q-quote all user/template values at substitution time
|
|
17
|
+
* 2. Never reuse across chats (hard rule)
|
|
18
|
+
* 3. Tag every session with chorus-<chatId>-...
|
|
19
|
+
* 4. Per-session CODEX_HOME for codex (caller/shim handles env-var passing)
|
|
20
|
+
*/
|
|
21
|
+
class TmuxManagerImpl {
|
|
22
|
+
/** In-memory registry: "chatId:phaseId:role:agentName" → SessionHandle */
|
|
23
|
+
sessions = new Map();
|
|
24
|
+
constructor() {
|
|
25
|
+
// Reconcile existing chorus sessions on startup
|
|
26
|
+
this.reconcileExisting();
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Scan `tmux ls` for existing chorus-* sessions and rebuild the registry.
|
|
30
|
+
* Recovers state after daemon restart.
|
|
31
|
+
*/
|
|
32
|
+
reconcileExisting() {
|
|
33
|
+
try {
|
|
34
|
+
const result = (0, child_process_1.spawnSync)('tmux', ['list-sessions', '-F', '#{session_name}'], {
|
|
35
|
+
encoding: 'utf-8',
|
|
36
|
+
});
|
|
37
|
+
if (result.status === 0 && result.stdout) {
|
|
38
|
+
const lines = result.stdout.trim().split('\n').filter((line) => line.length > 0);
|
|
39
|
+
for (const sessionName of lines) {
|
|
40
|
+
if (!sessionName.startsWith('chorus-'))
|
|
41
|
+
continue;
|
|
42
|
+
// Parse session name: chorus-<chatId>-<phaseId>-<role>-<agentName>
|
|
43
|
+
const parts = sessionName.split('-');
|
|
44
|
+
if (parts.length < 6)
|
|
45
|
+
continue; // chorus + chatId + phaseId + role + agentName = 5 parts after split
|
|
46
|
+
// Rebuild a minimal handle; agent lineage/name come from the parts
|
|
47
|
+
const chatId = parts[1];
|
|
48
|
+
const phaseId = parts[2];
|
|
49
|
+
const role = parts[3] || 'doer';
|
|
50
|
+
const agentName = parts.slice(4).join('-'); // Re-join in case agent name has hyphens
|
|
51
|
+
const key = `${chatId}:${phaseId}:${role}:${agentName}`;
|
|
52
|
+
// Avoid overwriting if we already have this session tracked
|
|
53
|
+
if (this.sessions.has(key))
|
|
54
|
+
continue;
|
|
55
|
+
const handle = {
|
|
56
|
+
name: sessionName,
|
|
57
|
+
chatId,
|
|
58
|
+
phaseId,
|
|
59
|
+
role,
|
|
60
|
+
lineage: 'anthropic', // Safe default; actual lineage is unknown from tmux metadata
|
|
61
|
+
agentName,
|
|
62
|
+
spawnedAt: Date.now(),
|
|
63
|
+
lastActivityAt: Date.now(),
|
|
64
|
+
state: 'active',
|
|
65
|
+
};
|
|
66
|
+
this.sessions.set(key, handle);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
// Silent fail; tmux may not be available yet
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Validate a string against the allowed charset for tmux session names.
|
|
76
|
+
* Tmux allows [a-zA-Z0-9_-].
|
|
77
|
+
*/
|
|
78
|
+
validateNameComponent(value, field) {
|
|
79
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(value)) {
|
|
80
|
+
throw new Error(`Invalid ${field}: ${value} contains forbidden characters`);
|
|
81
|
+
}
|
|
82
|
+
return value;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Build the session key used in our in-memory registry.
|
|
86
|
+
*/
|
|
87
|
+
makeSessionKey(chatId, phaseId, role, agentName) {
|
|
88
|
+
return `${chatId}:${phaseId}:${role}:${agentName}`;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Build the tmux session name from components.
|
|
92
|
+
* Format: chorus-<chatId>-<phaseId>-<role>-<agentName>
|
|
93
|
+
*/
|
|
94
|
+
makeSessionName(chatId, phaseId, role, agentName) {
|
|
95
|
+
return `chorus-${chatId}-${phaseId}-${role}-${agentName}`;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Acquire a session for a phase round, respecting share-session policy.
|
|
99
|
+
*
|
|
100
|
+
* Decision tree:
|
|
101
|
+
* 1. If shareSessionAcrossRounds && session exists for this chat+phase+role+agent → reuse
|
|
102
|
+
* 2. Else if shareSessionAcrossPhases && session exists for this chat+role+agent on ANY phase → reuse + rename
|
|
103
|
+
* 3. Else spawn fresh
|
|
104
|
+
*/
|
|
105
|
+
async acquire(opts) {
|
|
106
|
+
const { chatId, phaseId, role, shareSessionAcrossRounds, shareSessionAcrossPhases, agentName, } = opts;
|
|
107
|
+
// Validate input charset
|
|
108
|
+
this.validateNameComponent(chatId, 'chatId');
|
|
109
|
+
this.validateNameComponent(phaseId, 'phaseId');
|
|
110
|
+
this.validateNameComponent(agentName, 'agentName');
|
|
111
|
+
const key = this.makeSessionKey(chatId, phaseId, role, agentName);
|
|
112
|
+
// Rule 1: Across rounds (round N → N+1) within THIS phase
|
|
113
|
+
if (shareSessionAcrossRounds) {
|
|
114
|
+
const existing = this.sessions.get(key);
|
|
115
|
+
if (existing) {
|
|
116
|
+
existing.lastActivityAt = Date.now();
|
|
117
|
+
return existing;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
// Rule 2: Across phases (rare) — reuse from a previous phase
|
|
121
|
+
if (shareSessionAcrossPhases) {
|
|
122
|
+
for (const [registryKey, handle] of this.sessions) {
|
|
123
|
+
// Match on chat+role+agent, different phase
|
|
124
|
+
if (handle.chatId === chatId &&
|
|
125
|
+
handle.role === role &&
|
|
126
|
+
handle.agentName === agentName &&
|
|
127
|
+
handle.phaseId !== phaseId) {
|
|
128
|
+
// Found one on a previous phase — rename and reuse
|
|
129
|
+
const newSessionName = this.makeSessionName(chatId, phaseId, role, agentName);
|
|
130
|
+
try {
|
|
131
|
+
(0, child_process_1.spawnSync)('tmux', ['rename-session', '-t', handle.name, newSessionName]);
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
// If rename fails, fall through to spawn fresh
|
|
135
|
+
this.sessions.delete(registryKey);
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
// Update the handle
|
|
139
|
+
handle.phaseId = phaseId;
|
|
140
|
+
handle.lastActivityAt = Date.now();
|
|
141
|
+
this.sessions.delete(registryKey);
|
|
142
|
+
this.sessions.set(key, handle);
|
|
143
|
+
return handle;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// Rule 3: Spawn fresh
|
|
148
|
+
return this.spawnFresh(opts, key);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Spawn a fresh tmux session for the given phase round.
|
|
152
|
+
*/
|
|
153
|
+
async spawnFresh(opts, key) {
|
|
154
|
+
const { chatId, phaseId, role, agentName, shim, spawnOpts } = opts;
|
|
155
|
+
const sessionName = this.makeSessionName(chatId, phaseId, role, agentName);
|
|
156
|
+
// Build launch command via the shim
|
|
157
|
+
const launchCommand = shim.buildLaunchCommand(spawnOpts);
|
|
158
|
+
// Spawn tmux session
|
|
159
|
+
try {
|
|
160
|
+
// Use child_process.spawnSync with args array for safety
|
|
161
|
+
const result = (0, child_process_1.spawnSync)('tmux', ['new-session', '-d', '-s', sessionName, launchCommand], {
|
|
162
|
+
encoding: 'utf-8',
|
|
163
|
+
});
|
|
164
|
+
if (result.status !== 0) {
|
|
165
|
+
throw new Error(`Tmux spawn failed: status=${result.status}, stderr=${result.stderr || 'unknown'}`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
throw new Error(`TmuxSpawnError(code=tmux_unavailable): ${error instanceof Error ? error.message : String(error)}`);
|
|
170
|
+
}
|
|
171
|
+
// Cold-start poll: wait up to 8s for the session to be ready
|
|
172
|
+
const startTime = Date.now();
|
|
173
|
+
const timeout = 8000;
|
|
174
|
+
while (Date.now() - startTime < timeout) {
|
|
175
|
+
try {
|
|
176
|
+
const hasResult = (0, child_process_1.spawnSync)('tmux', ['has-session', '-t', sessionName], {
|
|
177
|
+
encoding: 'utf-8',
|
|
178
|
+
});
|
|
179
|
+
if (hasResult.status === 0) {
|
|
180
|
+
// Session is live
|
|
181
|
+
const handle = {
|
|
182
|
+
name: sessionName,
|
|
183
|
+
chatId,
|
|
184
|
+
phaseId,
|
|
185
|
+
role,
|
|
186
|
+
lineage: shim.lineage,
|
|
187
|
+
agentName,
|
|
188
|
+
spawnedAt: Date.now(),
|
|
189
|
+
lastActivityAt: Date.now(),
|
|
190
|
+
state: 'active',
|
|
191
|
+
};
|
|
192
|
+
this.sessions.set(key, handle);
|
|
193
|
+
return handle;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
// Still not ready
|
|
198
|
+
}
|
|
199
|
+
// Poll every 200ms
|
|
200
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
201
|
+
}
|
|
202
|
+
// Cold-start timeout
|
|
203
|
+
throw new Error(`TmuxSpawnError(code=cold_start_timeout): Session ${sessionName} did not become ready within 8s`);
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Send raw keys to a session (pre-nudge cleanup: Escape, Ctrl-C, /clear, etc.)
|
|
207
|
+
* Errors swallowed per spec.
|
|
208
|
+
*/
|
|
209
|
+
sendKeys(sessionName, keys) {
|
|
210
|
+
try {
|
|
211
|
+
const args = ['send-keys', '-t', sessionName, ...keys];
|
|
212
|
+
(0, child_process_1.spawnSync)('tmux', args, { stdio: 'ignore' });
|
|
213
|
+
}
|
|
214
|
+
catch {
|
|
215
|
+
// Swallow errors — session may be dead or unresponsive
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Send a prompt to a CLI's TUI input box.
|
|
220
|
+
*
|
|
221
|
+
* Originally implemented via `tmux load-buffer + paste-buffer`. That works
|
|
222
|
+
* for Claude/Codex/OpenCode/Kimi but Gemini's TUI silently drops paste-
|
|
223
|
+
* buffer events (likely due to its custom terminal-escape handling). The
|
|
224
|
+
* `send-keys -l` approach types each character literally and works
|
|
225
|
+
* universally across all the CLIs we ship — at the cost of slightly more
|
|
226
|
+
* shell argv pressure for very long prompts.
|
|
227
|
+
*
|
|
228
|
+
* Method name kept as `pasteBuffer` for caller compatibility, but the
|
|
229
|
+
* implementation no longer uses tmux's paste mechanism.
|
|
230
|
+
*/
|
|
231
|
+
pasteBuffer(sessionName, content) {
|
|
232
|
+
try {
|
|
233
|
+
(0, child_process_1.spawnSync)('tmux', ['send-keys', '-l', '-t', sessionName, content], {
|
|
234
|
+
stdio: 'ignore',
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
catch {
|
|
238
|
+
// Silent fail — session may be dead or unresponsive
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Capture the current pane content (last 200 lines).
|
|
243
|
+
* Used by the failure detector to match error patterns.
|
|
244
|
+
*/
|
|
245
|
+
capturePane(sessionName) {
|
|
246
|
+
try {
|
|
247
|
+
const result = (0, child_process_1.spawnSync)('tmux', ['capture-pane', '-t', sessionName, '-p', '-S', '-200'], {
|
|
248
|
+
encoding: 'utf-8',
|
|
249
|
+
});
|
|
250
|
+
if (result.status === 0) {
|
|
251
|
+
return result.stdout || '';
|
|
252
|
+
}
|
|
253
|
+
return '';
|
|
254
|
+
}
|
|
255
|
+
catch {
|
|
256
|
+
return '';
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* List all chorus-* sessions currently tracked in memory.
|
|
261
|
+
*/
|
|
262
|
+
list() {
|
|
263
|
+
return Array.from(this.sessions.values());
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Force-kill a session by name. Idempotent.
|
|
267
|
+
*/
|
|
268
|
+
kill(sessionName) {
|
|
269
|
+
try {
|
|
270
|
+
(0, child_process_1.spawnSync)('tmux', ['kill-session', '-t', sessionName], { stdio: 'ignore' });
|
|
271
|
+
}
|
|
272
|
+
catch {
|
|
273
|
+
// Session may not exist or be already dead
|
|
274
|
+
}
|
|
275
|
+
// Remove from registry
|
|
276
|
+
for (const [key, handle] of this.sessions) {
|
|
277
|
+
if (handle.name === sessionName) {
|
|
278
|
+
this.sessions.delete(key);
|
|
279
|
+
break;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Mark a session as terminal (eligible for reaping).
|
|
285
|
+
* Useful when a phase finishes but the tmux session hasn't been killed yet.
|
|
286
|
+
*/
|
|
287
|
+
markTerminal(sessionName) {
|
|
288
|
+
for (const handle of this.sessions.values()) {
|
|
289
|
+
if (handle.name === sessionName) {
|
|
290
|
+
handle.state = 'terminal';
|
|
291
|
+
break;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Reaper sweep: identify and kill orphan / idle sessions.
|
|
297
|
+
*
|
|
298
|
+
* Kill criteria:
|
|
299
|
+
* 1. Session's chatId is NOT in activeChats
|
|
300
|
+
* 2. Chat status is terminal (merged, cancelled, errored, timed-out)
|
|
301
|
+
* 3. Session state is awaiting_user AND idle > idleDestroyMinutes
|
|
302
|
+
*/
|
|
303
|
+
reapOnce(opts) {
|
|
304
|
+
const { activeChats, idleDestroyMinutes } = opts;
|
|
305
|
+
const killed = [];
|
|
306
|
+
const now = Date.now();
|
|
307
|
+
const idleThresholdMs = idleDestroyMinutes * 60 * 1000;
|
|
308
|
+
for (const handle of this.sessions.values()) {
|
|
309
|
+
let shouldKill = false;
|
|
310
|
+
let killReason = '';
|
|
311
|
+
// Criterion 1: Chat is not in activeChats map
|
|
312
|
+
if (!activeChats.has(handle.chatId)) {
|
|
313
|
+
shouldKill = true;
|
|
314
|
+
killReason = 'chat_not_active';
|
|
315
|
+
}
|
|
316
|
+
// Criterion 2: Chat status is terminal
|
|
317
|
+
const chatStatus = activeChats.get(handle.chatId);
|
|
318
|
+
if (chatStatus && ['merged', 'cancelled', 'errored', 'timed_out'].includes(chatStatus)) {
|
|
319
|
+
shouldKill = true;
|
|
320
|
+
killReason = `chat_${chatStatus}`;
|
|
321
|
+
}
|
|
322
|
+
// Criterion 3: Session state is terminal
|
|
323
|
+
if (handle.state === 'terminal') {
|
|
324
|
+
shouldKill = true;
|
|
325
|
+
killReason = 'session_terminal';
|
|
326
|
+
}
|
|
327
|
+
// Criterion 4: Session is awaiting_user and idle too long
|
|
328
|
+
if (handle.state === 'awaiting_user' && now - handle.lastActivityAt > idleThresholdMs) {
|
|
329
|
+
shouldKill = true;
|
|
330
|
+
killReason = `idle_${idleDestroyMinutes}m`;
|
|
331
|
+
}
|
|
332
|
+
if (shouldKill) {
|
|
333
|
+
this.kill(handle.name);
|
|
334
|
+
killed.push(`${handle.name}(${killReason})`);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
return { killed };
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
exports.TmuxManagerImpl = TmuxManagerImpl;
|
|
341
|
+
//# sourceMappingURL=tmux.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tmux.js","sourceRoot":"","sources":["../../src/daemon/tmux.ts"],"names":[],"mappings":";;;AAAA,iDAA0C;AAG1C;;;;;;;;;;;;;;;GAeG;AACH,MAAa,eAAe;IAC1B,0EAA0E;IAClE,QAAQ,GAAG,IAAI,GAAG,EAAyB,CAAC;IAEpD;QACE,gDAAgD;QAChD,IAAI,CAAC,iBAAiB,EAAE,CAAC;IAC3B,CAAC;IAED;;;OAGG;IACK,iBAAiB;QACvB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAA,yBAAS,EAAC,MAAM,EAAE,CAAC,eAAe,EAAE,IAAI,EAAE,iBAAiB,CAAC,EAAE;gBAC3E,QAAQ,EAAE,OAAO;aAClB,CAAC,CAAC;YAEH,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;gBACzC,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;gBAEjF,KAAK,MAAM,WAAW,IAAI,KAAK,EAAE,CAAC;oBAChC,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,SAAS,CAAC;wBAAE,SAAS;oBAEjD,mEAAmE;oBACnE,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;oBACrC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;wBAAE,SAAS,CAAC,qEAAqE;oBAErG,mEAAmE;oBACnE,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;oBACxB,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;oBACzB,MAAM,IAAI,GAAI,KAAK,CAAC,CAAC,CAAyB,IAAI,MAAM,CAAC;oBACzD,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,yCAAyC;oBAErF,MAAM,GAAG,GAAG,GAAG,MAAM,IAAI,OAAO,IAAI,IAAI,IAAI,SAAS,EAAE,CAAC;oBAExD,4DAA4D;oBAC5D,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC;wBAAE,SAAS;oBAErC,MAAM,MAAM,GAAkB;wBAC5B,IAAI,EAAE,WAAW;wBACjB,MAAM;wBACN,OAAO;wBACP,IAAI;wBACJ,OAAO,EAAE,WAAW,EAAE,6DAA6D;wBACnF,SAAS;wBACT,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;wBACrB,cAAc,EAAE,IAAI,CAAC,GAAG,EAAE;wBAC1B,KAAK,EAAE,QAAQ;qBAChB,CAAC;oBAEF,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;gBACjC,CAAC;YACH,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,6CAA6C;QAC/C,CAAC;IACH,CAAC;IAED;;;OAGG;IACK,qBAAqB,CAAC,KAAa,EAAE,KAAa;QACxD,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YACpC,MAAM,IAAI,KAAK,CAAC,WAAW,KAAK,KAAK,KAAK,gCAAgC,CAAC,CAAC;QAC9E,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;OAEG;IACK,cAAc,CACpB,MAAc,EACd,OAAe,EACf,IAAyB,EACzB,SAAiB;QAEjB,OAAO,GAAG,MAAM,IAAI,OAAO,IAAI,IAAI,IAAI,SAAS,EAAE,CAAC;IACrD,CAAC;IAED;;;OAGG;IACK,eAAe,CACrB,MAAc,EACd,OAAe,EACf,IAAyB,EACzB,SAAiB;QAEjB,OAAO,UAAU,MAAM,IAAI,OAAO,IAAI,IAAI,IAAI,SAAS,EAAE,CAAC;IAC5D,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,OAAO,CAAC,IAA2B;QACvC,MAAM,EACJ,MAAM,EACN,OAAO,EACP,IAAI,EACJ,wBAAwB,EACxB,wBAAwB,EACxB,SAAS,GACV,GAAG,IAAI,CAAC;QAET,yBAAyB;QACzB,IAAI,CAAC,qBAAqB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAC7C,IAAI,CAAC,qBAAqB,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;QAC/C,IAAI,CAAC,qBAAqB,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;QAEnD,MAAM,GAAG,GAAG,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;QAElE,0DAA0D;QAC1D,IAAI,wBAAwB,EAAE,CAAC;YAC7B,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACxC,IAAI,QAAQ,EAAE,CAAC;gBACb,QAAQ,CAAC,cAAc,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;gBACrC,OAAO,QAAQ,CAAC;YAClB,CAAC;QACH,CAAC;QAED,6DAA6D;QAC7D,IAAI,wBAAwB,EAAE,CAAC;YAC7B,KAAK,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;gBAClD,4CAA4C;gBAC5C,IACE,MAAM,CAAC,MAAM,KAAK,MAAM;oBACxB,MAAM,CAAC,IAAI,KAAK,IAAI;oBACpB,MAAM,CAAC,SAAS,KAAK,SAAS;oBAC9B,MAAM,CAAC,OAAO,KAAK,OAAO,EAC1B,CAAC;oBACD,mDAAmD;oBACnD,MAAM,cAAc,GAAG,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;oBAE9E,IAAI,CAAC;wBACH,IAAA,yBAAS,EAAC,MAAM,EAAE,CAAC,gBAAgB,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC;oBAC3E,CAAC;oBAAC,MAAM,CAAC;wBACP,+CAA+C;wBAC/C,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;wBAClC,MAAM;oBACR,CAAC;oBAED,oBAAoB;oBACpB,MAAM,CAAC,OAAO,GAAG,OAAO,CAAC;oBACzB,MAAM,CAAC,cAAc,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;oBACnC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;oBAClC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;oBAC/B,OAAO,MAAM,CAAC;gBAChB,CAAC;YACH,CAAC;QACH,CAAC;QAED,sBAAsB;QACtB,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IACpC,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,UAAU,CAAC,IAA2B,EAAE,GAAW;QAC/D,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC;QACnE,MAAM,WAAW,GAAG,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;QAE3E,oCAAoC;QACpC,MAAM,aAAa,GAAG,IAAI,CAAC,kBAAkB,CAAC,SAAS,CAAC,CAAC;QAEzD,qBAAqB;QACrB,IAAI,CAAC;YACH,yDAAyD;YACzD,MAAM,MAAM,GAAG,IAAA,yBAAS,EAAC,MAAM,EAAE,CAAC,aAAa,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,aAAa,CAAC,EAAE;gBACxF,QAAQ,EAAE,OAAO;aAClB,CAAC,CAAC;YAEH,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACxB,MAAM,IAAI,KAAK,CACb,6BAA6B,MAAM,CAAC,MAAM,YAAY,MAAM,CAAC,MAAM,IAAI,SAAS,EAAE,CACnF,CAAC;YACJ,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CACb,0CAA0C,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CACnG,CAAC;QACJ,CAAC;QAED,6DAA6D;QAC7D,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC7B,MAAM,OAAO,GAAG,IAAI,CAAC;QAErB,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,GAAG,OAAO,EAAE,CAAC;YACxC,IAAI,CAAC;gBACH,MAAM,SAAS,GAAG,IAAA,yBAAS,EAAC,MAAM,EAAE,CAAC,aAAa,EAAE,IAAI,EAAE,WAAW,CAAC,EAAE;oBACtE,QAAQ,EAAE,OAAO;iBAClB,CAAC,CAAC;gBAEH,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;oBAC3B,kBAAkB;oBAClB,MAAM,MAAM,GAAkB;wBAC5B,IAAI,EAAE,WAAW;wBACjB,MAAM;wBACN,OAAO;wBACP,IAAI;wBACJ,OAAO,EAAE,IAAI,CAAC,OAAO;wBACrB,SAAS;wBACT,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;wBACrB,cAAc,EAAE,IAAI,CAAC,GAAG,EAAE;wBAC1B,KAAK,EAAE,QAAQ;qBAChB,CAAC;oBAEF,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;oBAC/B,OAAO,MAAM,CAAC;gBAChB,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,kBAAkB;YACpB,CAAC;YAED,mBAAmB;YACnB,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC;QAC3D,CAAC;QAED,qBAAqB;QACrB,MAAM,IAAI,KAAK,CACb,oDAAoD,WAAW,iCAAiC,CACjG,CAAC;IACJ,CAAC;IAED;;;OAGG;IACH,QAAQ,CAAC,WAAmB,EAAE,IAAc;QAC1C,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,CAAC,WAAW,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,IAAI,CAAC,CAAC;YACvD,IAAA,yBAAS,EAAC,MAAM,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC/C,CAAC;QAAC,MAAM,CAAC;YACP,uDAAuD;QACzD,CAAC;IACH,CAAC;IAED;;;;;;;;;;;;OAYG;IACH,WAAW,CAAC,WAAmB,EAAE,OAAe;QAC9C,IAAI,CAAC;YACH,IAAA,yBAAS,EAAC,MAAM,EAAE,CAAC,WAAW,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE;gBACjE,KAAK,EAAE,QAAQ;aAChB,CAAC,CAAC;QACL,CAAC;QAAC,MAAM,CAAC;YACP,oDAAoD;QACtD,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,WAAW,CAAC,WAAmB;QAC7B,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAA,yBAAS,EAAC,MAAM,EAAE,CAAC,cAAc,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,EAAE;gBACxF,QAAQ,EAAE,OAAO;aAClB,CAAC,CAAC;YAEH,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACxB,OAAO,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC;YAC7B,CAAC;YAED,OAAO,EAAE,CAAC;QACZ,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;IAED;;OAEG;IACH,IAAI;QACF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IAC5C,CAAC;IAED;;OAEG;IACH,IAAI,CAAC,WAAmB;QACtB,IAAI,CAAC;YACH,IAAA,yBAAS,EAAC,MAAM,EAAE,CAAC,cAAc,EAAE,IAAI,EAAE,WAAW,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC9E,CAAC;QAAC,MAAM,CAAC;YACP,2CAA2C;QAC7C,CAAC;QAED,uBAAuB;QACvB,KAAK,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC1C,IAAI,MAAM,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;gBAChC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBAC1B,MAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,YAAY,CAAC,WAAmB;QAC9B,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;YAC5C,IAAI,MAAM,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;gBAChC,MAAM,CAAC,KAAK,GAAG,UAAU,CAAC;gBAC1B,MAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC;IAED;;;;;;;OAOG;IACH,QAAQ,CAAC,IAGR;QACC,MAAM,EAAE,WAAW,EAAE,kBAAkB,EAAE,GAAG,IAAI,CAAC;QACjD,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,eAAe,GAAG,kBAAkB,GAAG,EAAE,GAAG,IAAI,CAAC;QAEvD,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;YAC5C,IAAI,UAAU,GAAG,KAAK,CAAC;YACvB,IAAI,UAAU,GAAG,EAAE,CAAC;YAEpB,8CAA8C;YAC9C,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;gBACpC,UAAU,GAAG,IAAI,CAAC;gBAClB,UAAU,GAAG,iBAAiB,CAAC;YACjC,CAAC;YAED,uCAAuC;YACvC,MAAM,UAAU,GAAG,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAClD,IAAI,UAAU,IAAI,CAAC,QAAQ,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;gBACvF,UAAU,GAAG,IAAI,CAAC;gBAClB,UAAU,GAAG,QAAQ,UAAU,EAAE,CAAC;YACpC,CAAC;YAED,yCAAyC;YACzC,IAAI,MAAM,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;gBAChC,UAAU,GAAG,IAAI,CAAC;gBAClB,UAAU,GAAG,kBAAkB,CAAC;YAClC,CAAC;YAED,0DAA0D;YAC1D,IAAI,MAAM,CAAC,KAAK,KAAK,eAAe,IAAI,GAAG,GAAG,MAAM,CAAC,cAAc,GAAG,eAAe,EAAE,CAAC;gBACtF,UAAU,GAAG,IAAI,CAAC;gBAClB,UAAU,GAAG,QAAQ,kBAAkB,GAAG,CAAC;YAC7C,CAAC;YAED,IAAI,UAAU,EAAE,CAAC;gBACf,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;gBACvB,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,IAAI,IAAI,UAAU,GAAG,CAAC,CAAC;YAC/C,CAAC;QACH,CAAC;QAED,OAAO,EAAE,MAAM,EAAE,CAAC;IACpB,CAAC;CACF;AA/XD,0CA+XC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.atomicWriteJsonSync = atomicWriteJsonSync;
|
|
7
|
+
/**
|
|
8
|
+
* Atomic JSON write — writes to a temp file in the same dir, then renames
|
|
9
|
+
* over the destination. fs.renameSync is atomic on POSIX (single inode swap),
|
|
10
|
+
* so a reader concurrently opening the destination either sees the previous
|
|
11
|
+
* complete file or the new complete file — never a half-written one.
|
|
12
|
+
*
|
|
13
|
+
* Why this matters: the cockpit polls _meta.json sidecars and chat-level
|
|
14
|
+
* meta.json to render run cards. A daemon crash mid-write (or even a slow
|
|
15
|
+
* write under load) could leave a 0-byte or truncated file that JSON.parse
|
|
16
|
+
* rejects, and the cockpit silently swallows the error and the card never
|
|
17
|
+
* renders. Atomic-rename eliminates that window.
|
|
18
|
+
*
|
|
19
|
+
* The temp file uses a `.tmp.<pid>.<rand>` suffix so concurrent writers in
|
|
20
|
+
* the same dir (multiple participants in a phase) don't collide on the
|
|
21
|
+
* temp path. Rename is the synchronisation point; the OS guarantees only
|
|
22
|
+
* one of N concurrent renames "wins" the destination at any instant.
|
|
23
|
+
*
|
|
24
|
+
* Caller note: we deliberately use sync APIs because the existing call
|
|
25
|
+
* sites are sync (writeTransportMeta in kimi.ts, runner setup). An async
|
|
26
|
+
* variant can be added later if a hot path needs it.
|
|
27
|
+
*/
|
|
28
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
29
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
30
|
+
function atomicWriteJsonSync(targetPath, value) {
|
|
31
|
+
const dir = node_path_1.default.dirname(targetPath);
|
|
32
|
+
const base = node_path_1.default.basename(targetPath);
|
|
33
|
+
// pid + 8 hex chars from Date.now() is sufficient; same-process collision
|
|
34
|
+
// is impossible (sequential calls), cross-process needs the pid + nonce.
|
|
35
|
+
const nonce = Math.floor(Math.random() * 0xffffffff).toString(16).padStart(8, '0');
|
|
36
|
+
const tmpPath = node_path_1.default.join(dir, `.${base}.tmp.${process.pid}.${nonce}`);
|
|
37
|
+
// Stringify FIRST so a JSON serialization error doesn't leave an empty
|
|
38
|
+
// tmp file on disk (otherwise the catch-and-cleanup below has more to do).
|
|
39
|
+
const body = JSON.stringify(value, null, 2);
|
|
40
|
+
try {
|
|
41
|
+
node_fs_1.default.writeFileSync(tmpPath, body, 'utf-8');
|
|
42
|
+
node_fs_1.default.renameSync(tmpPath, targetPath);
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
// Best-effort cleanup of the temp file if rename failed (e.g. EXDEV
|
|
46
|
+
// on a cross-device tmpdir, EACCES on perms). Re-throw so callers can
|
|
47
|
+
// decide whether to swallow (informational sidecars) or escalate.
|
|
48
|
+
try {
|
|
49
|
+
node_fs_1.default.unlinkSync(tmpPath);
|
|
50
|
+
}
|
|
51
|
+
catch { /* tmp may not exist */ }
|
|
52
|
+
throw err;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=atomic-write.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"atomic-write.js","sourceRoot":"","sources":["../../src/lib/atomic-write.ts"],"names":[],"mappings":";;;;;AAwBA,kDAsBC;AA9CD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,sDAAyB;AACzB,0DAA6B;AAE7B,SAAgB,mBAAmB,CAAC,UAAkB,EAAE,KAAc;IACpE,MAAM,GAAG,GAAG,mBAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IACrC,MAAM,IAAI,GAAG,mBAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;IACvC,0EAA0E;IAC1E,yEAAyE;IACzE,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,UAAU,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACnF,MAAM,OAAO,GAAG,mBAAI,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,QAAQ,OAAO,CAAC,GAAG,IAAI,KAAK,EAAE,CAAC,CAAC;IAEvE,uEAAuE;IACvE,2EAA2E;IAC3E,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAE5C,IAAI,CAAC;QACH,iBAAE,CAAC,aAAa,CAAC,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;QACzC,iBAAE,CAAC,UAAU,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;IACrC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,oEAAoE;QACpE,sEAAsE;QACtE,kEAAkE;QAClE,IAAI,CAAC;YAAC,iBAAE,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAC,uBAAuB,CAAC,CAAC;QACjE,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Process-local event bus for chat list mutations.
|
|
4
|
+
*
|
|
5
|
+
* The cockpit sidebar (and any other surface that renders a list of
|
|
6
|
+
* chats) needs to react when a chat is created/updated/deleted from
|
|
7
|
+
* ANY source — REST POST, MCP tool call, runner status change. The
|
|
8
|
+
* daemon polled every 12s before, which is fine for status drift but
|
|
9
|
+
* looks broken when a freshly-fired MCP chat takes 12s to appear.
|
|
10
|
+
*
|
|
11
|
+
* We chose a process-local EventEmitter over a DB-NOTIFY broadcast
|
|
12
|
+
* because chorus is single-daemon today; if we ever shard the daemon,
|
|
13
|
+
* this becomes a libsql LISTEN. Subscribers are SSE handlers in
|
|
14
|
+
* `routes/chats-events.ts`.
|
|
15
|
+
*/
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.chatEventsBus = void 0;
|
|
18
|
+
const node_events_1 = require("node:events");
|
|
19
|
+
class ChatEventsBus extends node_events_1.EventEmitter {
|
|
20
|
+
emitChange(chatId, kind) {
|
|
21
|
+
const ev = { chatId, kind, ts: Date.now() };
|
|
22
|
+
this.emit('change', ev);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
exports.chatEventsBus = new ChatEventsBus();
|
|
26
|
+
exports.chatEventsBus.setMaxListeners(50);
|
|
27
|
+
//# sourceMappingURL=chat-events-bus.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat-events-bus.js","sourceRoot":"","sources":["../../src/lib/chat-events-bus.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;GAaG;;;AAEH,6CAA2C;AAU3C,MAAM,aAAc,SAAQ,0BAAY;IACtC,UAAU,CAAC,MAAc,EAAE,IAAoB;QAC7C,MAAM,EAAE,GAAoB,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;QAC7D,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IAC1B,CAAC;CACF;AAEY,QAAA,aAAa,GAAG,IAAI,aAAa,EAAE,CAAC;AACjD,qBAAa,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Chat slug generation.
|
|
4
|
+
*
|
|
5
|
+
* The cockpit URL for a chat is `/runs/<slug>` instead of `/runs/<ULID>`.
|
|
6
|
+
* Bookmarks and shared links should be human-readable: `/runs/review-pr-9`
|
|
7
|
+
* beats `/runs/019DE881853DC50566D8DFCE083F75F0`.
|
|
8
|
+
*
|
|
9
|
+
* Strategy:
|
|
10
|
+
* 1. Slugify the `work` field (truncated, ASCII-folded, lowercase,
|
|
11
|
+
* hyphen-separated).
|
|
12
|
+
* 2. Fall back to the template_id when work is empty/non-Latin/all
|
|
13
|
+
* whitespace.
|
|
14
|
+
* 3. On collision, append `-2`, `-3`, ... until unique.
|
|
15
|
+
*
|
|
16
|
+
* Pure module: takes an `existsFn(slug)` callback so the DB layer (or a
|
|
17
|
+
* test) decides what "exists" means. No I/O here, fully unit-testable.
|
|
18
|
+
*
|
|
19
|
+
* Slug grammar (regex): `[a-z0-9]+(-[a-z0-9]+)*` — same shape GitHub
|
|
20
|
+
* uses for issue/repo URLs. Leading/trailing/duplicate hyphens are
|
|
21
|
+
* collapsed. Max length 60 characters; collision suffix always fits in
|
|
22
|
+
* the trailing 8 chars even after truncation.
|
|
23
|
+
*/
|
|
24
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
+
exports.slugifyText = slugifyText;
|
|
26
|
+
exports.generateChatSlug = generateChatSlug;
|
|
27
|
+
exports.looksLikeSlug = looksLikeSlug;
|
|
28
|
+
const MAX_BASE_LEN = 60;
|
|
29
|
+
/** Generic stand-in when slugify produces empty string (all-emoji input, etc.). */
|
|
30
|
+
const FALLBACK_BASE = 'chat';
|
|
31
|
+
/** Defensive ceiling on the dedup loop — way above realistic collision counts. */
|
|
32
|
+
const MAX_DEDUP_ATTEMPTS = 10_000;
|
|
33
|
+
/**
|
|
34
|
+
* Convert arbitrary text to a URL-safe slug component. Pure, deterministic.
|
|
35
|
+
* - Folds Unicode → ASCII via NFKD (so "café" → "cafe").
|
|
36
|
+
* - Replaces every non-[a-z0-9] run with a single hyphen.
|
|
37
|
+
* - Strips leading/trailing hyphens.
|
|
38
|
+
* - Truncates to MAX_BASE_LEN.
|
|
39
|
+
* - Returns "" when input has no extractable slug characters; callers
|
|
40
|
+
* are expected to substitute a fallback.
|
|
41
|
+
*/
|
|
42
|
+
function slugifyText(input) {
|
|
43
|
+
if (!input)
|
|
44
|
+
return '';
|
|
45
|
+
// NFKD splits accented chars into base + combining mark, then strip
|
|
46
|
+
// the marks. Falls back to the original if the runtime doesn't
|
|
47
|
+
// implement Intl normalisation.
|
|
48
|
+
let s;
|
|
49
|
+
try {
|
|
50
|
+
s = input.normalize('NFKD').replace(/[̀-ͯ]/g, '');
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
s = input;
|
|
54
|
+
}
|
|
55
|
+
s = s.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
|
|
56
|
+
if (s.length === 0)
|
|
57
|
+
return '';
|
|
58
|
+
if (s.length > MAX_BASE_LEN) {
|
|
59
|
+
s = s.slice(0, MAX_BASE_LEN).replace(/-+$/g, '');
|
|
60
|
+
// Edge: truncating mid-token can leave us with a stray trailing
|
|
61
|
+
// hyphen even after the regex trim above (when the truncated tail
|
|
62
|
+
// ends with `-something`). The replace above handles it.
|
|
63
|
+
}
|
|
64
|
+
return s;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Build a slug for a chat with the given `work` text and `template_id`,
|
|
68
|
+
* unique among `existsFn(slug) === true` rows. Pure function — caller
|
|
69
|
+
* supplies the existence check.
|
|
70
|
+
*
|
|
71
|
+
* Collision suffix uses simple `-2`, `-3`, … rather than ULID prefixes
|
|
72
|
+
* because the URL stays short and predictable. Real-world collision rate
|
|
73
|
+
* is near zero (every chat starts with a different brief), so the loop
|
|
74
|
+
* almost always exits on the first iteration.
|
|
75
|
+
*/
|
|
76
|
+
async function generateChatSlug(args) {
|
|
77
|
+
let base = slugifyText(args.work);
|
|
78
|
+
if (!base)
|
|
79
|
+
base = slugifyText(args.templateId);
|
|
80
|
+
if (!base)
|
|
81
|
+
base = FALLBACK_BASE;
|
|
82
|
+
// Try the base first, then -2, -3, … until we find a free one.
|
|
83
|
+
for (let i = 0; i < MAX_DEDUP_ATTEMPTS; i++) {
|
|
84
|
+
const candidate = i === 0 ? base : `${base}-${i + 1}`;
|
|
85
|
+
if (!(await args.existsFn(candidate)))
|
|
86
|
+
return candidate;
|
|
87
|
+
}
|
|
88
|
+
// Vanishingly unlikely — every realistic workload exits in <5 iterations.
|
|
89
|
+
// Fall through to a timestamp-suffixed slug rather than throw.
|
|
90
|
+
return `${base}-${Date.now()}`;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Lightweight check: does this string look like a slug rather than a
|
|
94
|
+
* ULID? Used by routes that accept either.
|
|
95
|
+
*
|
|
96
|
+
* ULIDs are 26 uppercase Crockford-base32 chars; the codebase emits
|
|
97
|
+
* 32-char uppercase hex, but either way they're [0-9A-Z]+ with no
|
|
98
|
+
* hyphens. Slugs are lowercase + hyphens by construction. Test for the
|
|
99
|
+
* presence of a lowercase letter or hyphen — both are absent in ULIDs
|
|
100
|
+
* but always present in our slugs.
|
|
101
|
+
*/
|
|
102
|
+
function looksLikeSlug(s) {
|
|
103
|
+
return /[a-z]/.test(s) || s.includes('-');
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=chat-slug.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat-slug.js","sourceRoot":"","sources":["../../src/lib/chat-slug.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;;AAiBH,kCAoBC;AAYD,4CAkBC;AAYD,sCAEC;AA/ED,MAAM,YAAY,GAAG,EAAE,CAAC;AACxB,mFAAmF;AACnF,MAAM,aAAa,GAAG,MAAM,CAAC;AAC7B,kFAAkF;AAClF,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAElC;;;;;;;;GAQG;AACH,SAAgB,WAAW,CAAC,KAAa;IACvC,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAC;IACtB,oEAAoE;IACpE,+DAA+D;IAC/D,gCAAgC;IAChC,IAAI,CAAS,CAAC;IACd,IAAI,CAAC;QACH,CAAC,GAAG,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IACpD,CAAC;IAAC,MAAM,CAAC;QACP,CAAC,GAAG,KAAK,CAAC;IACZ,CAAC;IACD,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IACxE,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAC9B,IAAI,CAAC,CAAC,MAAM,GAAG,YAAY,EAAE,CAAC;QAC5B,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QACjD,gEAAgE;QAChE,kEAAkE;QAClE,yDAAyD;IAC3D,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED;;;;;;;;;GASG;AACI,KAAK,UAAU,gBAAgB,CAAC,IAKtC;IACC,IAAI,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClC,IAAI,CAAC,IAAI;QAAE,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC/C,IAAI,CAAC,IAAI;QAAE,IAAI,GAAG,aAAa,CAAC;IAEhC,+DAA+D;IAC/D,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,kBAAkB,EAAE,CAAC,EAAE,EAAE,CAAC;QAC5C,MAAM,SAAS,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACtD,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;YAAE,OAAO,SAAS,CAAC;IAC1D,CAAC;IACD,0EAA0E;IAC1E,+DAA+D;IAC/D,OAAO,GAAG,IAAI,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;AACjC,CAAC;AAED;;;;;;;;;GASG;AACH,SAAgB,aAAa,CAAC,CAAS;IACrC,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;AAC5C,CAAC"}
|