@phenx-inc/ctlsurf 0.3.0 → 0.3.2
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/out/headless/index.mjs +12 -3
- package/out/headless/index.mjs.map +2 -2
- package/out/main/index.js +12 -3
- package/out/renderer/assets/{cssMode-DiOmyihM.js → cssMode-BW-SuYuP.js} +3 -3
- package/out/renderer/assets/{freemarker2-BAfv60yb.js → freemarker2-2YWYzawi.js} +1 -1
- package/out/renderer/assets/{handlebars-Ult17NzQ.js → handlebars-EwtUQRsf.js} +1 -1
- package/out/renderer/assets/{html-DCxh4J-1.js → html-BNZkIDb9.js} +1 -1
- package/out/renderer/assets/{htmlMode-CQ5Xenrg.js → htmlMode-C2dZKrOy.js} +3 -3
- package/out/renderer/assets/{index-BnCJ1IaZ.js → index-Bm_rbVP-.js} +30 -28
- package/out/renderer/assets/{javascript-U5dsRcHx.js → javascript-busdVZMv.js} +2 -2
- package/out/renderer/assets/{jsonMode-DshPNyVy.js → jsonMode-BaVI6jAw.js} +3 -3
- package/out/renderer/assets/{liquid-jHHLYTlB.js → liquid-DG08un1Q.js} +1 -1
- package/out/renderer/assets/{lspLanguageFeatures-CUafmPGy.js → lspLanguageFeatures-peGVtLxi.js} +1 -1
- package/out/renderer/assets/{mdx-Ct-tiY6g.js → mdx-DogBhUxZ.js} +1 -1
- package/out/renderer/assets/{python-wD3UwKPV.js → python-Bf-INYXh.js} +1 -1
- package/out/renderer/assets/{razor-11ECS4oH.js → razor-DLrZ2hsF.js} +1 -1
- package/out/renderer/assets/{tsMode-D-7JexQ_.js → tsMode-B4oEmliC.js} +1 -1
- package/out/renderer/assets/{typescript-Cvna1mak.js → typescript-CjkgfhVK.js} +1 -1
- package/out/renderer/assets/{xml-JsEaImjA.js → xml-0FAXmuVg.js} +1 -1
- package/out/renderer/assets/{yaml-B8pCNDb_.js → yaml-DWxnPuy8.js} +1 -1
- package/out/renderer/index.html +1 -1
- package/package.json +1 -1
- package/src/main/timeTracker.ts +13 -3
- package/src/renderer/components/TerminalPanel.tsx +11 -7
package/out/headless/index.mjs
CHANGED
|
@@ -594,13 +594,23 @@ var AGENT_DATASTORE_PAGE_TITLE = "Agent Datastore";
|
|
|
594
594
|
var FIRST_CHECKPOINT_DELAY_MS = 30 * 1e3;
|
|
595
595
|
var CHECKPOINT_INTERVAL_MS = 5 * 60 * 1e3;
|
|
596
596
|
var COLUMNS = [
|
|
597
|
-
{ name: "Started", type: "
|
|
597
|
+
{ name: "Started", type: "text" },
|
|
598
598
|
{ name: "Active Time", type: "number" },
|
|
599
599
|
{ name: "Agent", type: "text" },
|
|
600
600
|
{ name: "Worker", type: "text" },
|
|
601
601
|
{ name: "Session", type: "text" },
|
|
602
602
|
{ name: "Notes", type: "text" }
|
|
603
603
|
];
|
|
604
|
+
function formatStarted(ms) {
|
|
605
|
+
return new Date(ms).toLocaleString("en-US", {
|
|
606
|
+
year: "numeric",
|
|
607
|
+
month: "short",
|
|
608
|
+
day: "numeric",
|
|
609
|
+
hour: "numeric",
|
|
610
|
+
minute: "2-digit",
|
|
611
|
+
hour12: true
|
|
612
|
+
});
|
|
613
|
+
}
|
|
604
614
|
function log2(...args) {
|
|
605
615
|
try {
|
|
606
616
|
console.log("[time-tracker]", ...args);
|
|
@@ -635,10 +645,9 @@ var TimeTracker = class {
|
|
|
635
645
|
return;
|
|
636
646
|
}
|
|
637
647
|
const startedAt = Date.now();
|
|
638
|
-
const startedIso = new Date(startedAt).toISOString();
|
|
639
648
|
const sessionUuid = randomUUID();
|
|
640
649
|
const row = await this.api.addRow(blockId, {
|
|
641
|
-
Started:
|
|
650
|
+
Started: formatStarted(startedAt),
|
|
642
651
|
"Active Time": 0,
|
|
643
652
|
Agent: agentName,
|
|
644
653
|
Worker: os2.hostname(),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../node_modules/electron/index.js", "../../src/main/orchestrator.ts", "../../src/main/pty.ts", "../../src/main/agents.ts", "../../src/main/ctlsurfApi.ts", "../../src/main/bridge.ts", "../../src/main/workerWs.ts", "../../src/main/timeTracker.ts", "../../src/main/settingsDir.ts", "../../src/main/tui.ts", "../../src/main/headless.ts"],
|
|
4
|
-
"sourcesContent": ["const fs = require('fs');\nconst path = require('path');\n\nconst pathFile = path.join(__dirname, 'path.txt');\n\nfunction getElectronPath () {\n let executablePath;\n if (fs.existsSync(pathFile)) {\n executablePath = fs.readFileSync(pathFile, 'utf-8');\n }\n if (process.env.ELECTRON_OVERRIDE_DIST_PATH) {\n return path.join(process.env.ELECTRON_OVERRIDE_DIST_PATH, executablePath || 'electron');\n }\n if (executablePath) {\n return path.join(__dirname, 'dist', executablePath);\n } else {\n throw new Error('Electron failed to install correctly, please delete node_modules/electron and try installing again');\n }\n}\n\nmodule.exports = getElectronPath();\n", "import path from 'path'\nimport fs from 'fs'\nimport os from 'os'\n\nimport { PtyManager } from './pty'\nimport { AgentConfig, isCodingAgent } from './agents'\nimport { CtlsurfApi } from './ctlsurfApi'\nimport { ConversationBridge } from './bridge'\nimport { WorkerWsClient, type WorkerWsStatus, type IncomingMessage } from './workerWs'\nimport { TimeTracker } from './timeTracker'\n\nfunction log(...args: unknown[]): void {\n try { console.log(...args) } catch { /* EPIPE safe */ }\n}\n\n// \u2500\u2500\u2500 Types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface Profile {\n name: string\n apiKey: string\n baseUrl: string\n dataspacePageId: string\n trackTime?: boolean\n idleTimeoutMin?: number\n}\n\nconst DEFAULT_IDLE_TIMEOUT_MIN = 15\n\nexport interface SettingsData {\n activeProfile: string\n profiles: Record<string, Profile>\n ctlsurfApiKey?: string\n ctlsurfBaseUrl?: string\n ctlsurfDataspacePageId?: string\n}\n\nexport interface OrchestratorEvents {\n onPtyData: (tabId: string, data: string) => void\n onPtyExit: (tabId: string, code: number) => void\n onWorkerStatus: (status: string) => void\n onWorkerMessage: (message: IncomingMessage) => void\n onWorkerRegistered: (data: { worker_id: string; folder_id: string | null; status: string }) => void\n onCwdChanged: () => void\n}\n\ninterface TabState {\n ptyManager: PtyManager\n agent: AgentConfig\n cwd: string\n termStreamBuffer: string\n termStreamTimer: ReturnType<typeof setTimeout> | null\n}\n\n// \u2500\u2500\u2500 Orchestrator \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst DEFAULT_PROFILES: Record<string, Profile> = {\n production: {\n name: 'Production',\n apiKey: '',\n baseUrl: 'https://app.ctlsurf.com',\n dataspacePageId: '',\n trackTime: true,\n idleTimeoutMin: 15,\n },\n}\n\nconst TERM_STREAM_INTERVAL_MS = 50\n\nexport class Orchestrator {\n private settingsDir: string\n private events: OrchestratorEvents\n\n // Core services\n readonly ctlsurfApi = new CtlsurfApi()\n readonly bridge = new ConversationBridge()\n readonly workerWs: WorkerWsClient\n readonly timeTracker = new TimeTracker(this.ctlsurfApi)\n\n // State\n private tabs = new Map<string, TabState>()\n private activeTabId: string | null = null\n private currentAgent: AgentConfig | null = null\n private currentCwd: string | null = null\n private settings: SettingsData = {\n activeProfile: 'production',\n profiles: { ...DEFAULT_PROFILES },\n }\n\n constructor(settingsDir: string, events: OrchestratorEvents) {\n this.settingsDir = settingsDir\n this.events = events\n\n this.workerWs = new WorkerWsClient({\n onStatusChange: (status: WorkerWsStatus) => {\n log(`[worker-ws] Status: ${status}`)\n events.onWorkerStatus(status)\n },\n onMessage: (message: IncomingMessage) => {\n log(`[worker-ws] Incoming message: ${message.id} (${message.type})`)\n events.onWorkerMessage(message)\n this.workerWs.sendAck(message.id)\n\n if (message.type === 'prompt' || message.type === 'task_dispatch') {\n const activeTab = this.activeTabId ? this.tabs.get(this.activeTabId) : null\n if (activeTab) {\n activeTab.ptyManager.write(message.content + '\\r')\n this.bridge.feedInput(message.content)\n }\n }\n },\n onRegistered: (data) => {\n log(`[worker-ws] Registered: worker_id=${data.worker_id}, folder_id=${data.folder_id}, status=${data.status}`)\n events.onWorkerRegistered(data)\n if (!data.folder_id) {\n events.onWorkerStatus('no_project')\n }\n },\n onTerminalInput: (data: string) => {\n const activeTab = this.activeTabId ? this.tabs.get(this.activeTabId) : null\n activeTab?.ptyManager.write(data)\n },\n })\n\n this.bridge.setWsClient(this.workerWs)\n }\n\n // \u2500\u2500\u2500 Settings \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n getActiveProfile(): Profile {\n return this.settings.profiles[this.settings.activeProfile] || this.settings.profiles.production || DEFAULT_PROFILES.production\n }\n\n get settingsData(): SettingsData {\n return this.settings\n }\n\n get cwd(): string | null {\n return this.currentCwd\n }\n\n get agent(): AgentConfig | null {\n return this.currentAgent\n }\n\n applyProfile(profile: Profile): void {\n const apiKey = profile.apiKey || process.env.CTLSURF_API_KEY || ''\n if (apiKey) {\n this.ctlsurfApi.setApiKey(apiKey)\n this.workerWs.setApiKey(apiKey)\n } else {\n this.ctlsurfApi.setApiKey('')\n this.workerWs.setApiKey(null)\n }\n\n const baseUrl = profile.baseUrl || process.env.CTLSURF_BASE_URL || 'https://app.ctlsurf.com'\n this.ctlsurfApi.setBaseUrl(baseUrl)\n this.workerWs.setBaseUrl(baseUrl)\n\n log(`[settings] Profile applied: ${profile.name} (${baseUrl})`)\n }\n\n loadSettings(): void {\n // Ensure settings dir exists\n try { fs.mkdirSync(this.settingsDir, { recursive: true }) } catch { /* ignore */ }\n\n const settingsPath = path.join(this.settingsDir, 'settings.json')\n try {\n if (fs.existsSync(settingsPath)) {\n const raw = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'))\n\n if (!raw.profiles) {\n this.settings = {\n activeProfile: 'production',\n profiles: {\n production: {\n name: 'Production',\n apiKey: raw.ctlsurfApiKey || '',\n baseUrl: raw.ctlsurfBaseUrl || 'https://app.ctlsurf.com',\n dataspacePageId: raw.ctlsurfDataspacePageId || '',\n },\n },\n }\n this.saveSettings()\n log('[settings] Migrated legacy settings to profiles')\n } else {\n this.settings = raw as SettingsData\n if (!this.settings.profiles.production) {\n this.settings.profiles.production = { ...DEFAULT_PROFILES.production }\n }\n }\n }\n } catch {\n this.settings = {\n activeProfile: 'production',\n profiles: { ...DEFAULT_PROFILES },\n }\n }\n\n this.applyProfile(this.getActiveProfile())\n }\n\n saveSettings(): void {\n const settingsPath = path.join(this.settingsDir, 'settings.json')\n try {\n fs.mkdirSync(this.settingsDir, { recursive: true })\n fs.writeFileSync(settingsPath, JSON.stringify(this.settings, null, 2))\n } catch (err: any) {\n log('[settings] Failed to save:', err.message)\n }\n }\n\n overrideApiKey(key: string): void {\n this.ctlsurfApi.setApiKey(key)\n this.workerWs.setApiKey(key)\n }\n\n overrideBaseUrl(url: string): void {\n this.ctlsurfApi.setBaseUrl(url)\n this.workerWs.setBaseUrl(url)\n }\n\n // \u2500\u2500\u2500 Profile CRUD \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n listProfiles() {\n return {\n activeProfile: this.settings.activeProfile,\n profiles: Object.entries(this.settings.profiles).map(([id, p]) => ({\n id,\n name: p.name,\n baseUrl: p.baseUrl,\n hasApiKey: !!p.apiKey,\n dataspacePageId: p.dataspacePageId || null,\n trackTime: p.trackTime !== false,\n idleTimeoutMin: p.idleTimeoutMin ?? DEFAULT_IDLE_TIMEOUT_MIN,\n })),\n }\n }\n\n getProfile(profileId: string) {\n const p = this.settings.profiles[profileId]\n if (!p) return null\n return {\n id: profileId,\n name: p.name,\n baseUrl: p.baseUrl,\n hasApiKey: !!p.apiKey,\n dataspacePageId: p.dataspacePageId || '',\n trackTime: p.trackTime !== false,\n idleTimeoutMin: p.idleTimeoutMin ?? DEFAULT_IDLE_TIMEOUT_MIN,\n }\n }\n\n saveProfile(profileId: string, data: {\n name: string\n apiKey?: string\n baseUrl: string\n dataspacePageId: string\n trackTime?: boolean\n idleTimeoutMin?: number\n }) {\n const existing = this.settings.profiles[profileId]\n this.settings.profiles[profileId] = {\n name: data.name,\n apiKey: data.apiKey !== undefined ? data.apiKey : (existing?.apiKey || ''),\n baseUrl: data.baseUrl || 'https://app.ctlsurf.com',\n dataspacePageId: data.dataspacePageId || '',\n trackTime: data.trackTime !== undefined ? data.trackTime : (existing?.trackTime !== false),\n idleTimeoutMin: data.idleTimeoutMin !== undefined ? data.idleTimeoutMin : (existing?.idleTimeoutMin ?? DEFAULT_IDLE_TIMEOUT_MIN),\n }\n this.saveSettings()\n\n if (profileId === this.settings.activeProfile) {\n this.applyProfile(this.settings.profiles[profileId])\n if (this.currentAgent && this.currentCwd) {\n this.workerWs.disconnect()\n this.connectWorkerWs(this.currentAgent, this.currentCwd)\n }\n }\n }\n\n switchProfile(profileId: string): { ok: boolean; error?: string } {\n if (!this.settings.profiles[profileId]) return { ok: false, error: 'Profile not found' }\n this.workerWs.disconnect()\n this.settings.activeProfile = profileId\n this.saveSettings()\n this.applyProfile(this.getActiveProfile())\n if (this.currentAgent && this.currentCwd) {\n this.connectWorkerWs(this.currentAgent, this.currentCwd)\n }\n return { ok: true }\n }\n\n deleteProfile(profileId: string): { ok: boolean; error?: string } {\n if (profileId === 'production') return { ok: false, error: 'Cannot delete Production profile' }\n if (!this.settings.profiles[profileId]) return { ok: false, error: 'Profile not found' }\n\n if (this.settings.activeProfile === profileId) {\n this.workerWs.disconnect()\n this.settings.activeProfile = 'production'\n this.applyProfile(this.getActiveProfile())\n if (this.currentAgent && this.currentCwd) {\n this.connectWorkerWs(this.currentAgent, this.currentCwd)\n }\n }\n\n delete this.settings.profiles[profileId]\n this.saveSettings()\n return { ok: true }\n }\n\n // \u2500\u2500\u2500 PTY & Agent (multi-tab) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async spawnAgent(tabId: string, agent: AgentConfig, cwd: string, opts?: { trackTime?: boolean }): Promise<void> {\n // Kill existing PTY on this tab if any\n const existing = this.tabs.get(tabId)\n if (existing) {\n if (existing.termStreamTimer) clearTimeout(existing.termStreamTimer)\n existing.ptyManager.kill()\n this.tabs.delete(tabId)\n }\n\n this.currentAgent = agent\n const prevCwd = this.currentCwd\n this.currentCwd = cwd\n this.activeTabId = tabId\n if (prevCwd !== cwd) {\n this.events.onCwdChanged()\n }\n\n const ptyManager = new PtyManager(agent, cwd)\n const tab: TabState = { ptyManager, agent, cwd, termStreamBuffer: '', termStreamTimer: null }\n this.tabs.set(tabId, tab)\n\n ptyManager.onData((data: string) => {\n this.events.onPtyData(tabId, data)\n this.timeTracker.recordActivity(tabId)\n if (tabId === this.activeTabId) {\n this.bridge.feedOutput(data)\n this.streamTerminalData(tabId, data)\n }\n })\n\n ptyManager.onExit(async (exitCode: number) => {\n this.events.onPtyExit(tabId, exitCode)\n await this.timeTracker.endSession(tabId)\n if (tabId === this.activeTabId) {\n this.bridge.endSession()\n if (this.currentAgent && isCodingAgent(this.currentAgent)) {\n this.workerWs.disconnect()\n }\n }\n // Clean up tab state\n const t = this.tabs.get(tabId)\n if (t?.termStreamTimer) clearTimeout(t.termStreamTimer)\n })\n\n this.bridge.startSession()\n\n const profile = this.getActiveProfile()\n const shouldTrack = opts?.trackTime !== undefined ? opts.trackTime : (profile.trackTime !== false)\n if (shouldTrack) {\n void this.timeTracker.startSession(\n tabId,\n cwd,\n agent.name,\n profile.idleTimeoutMin ?? DEFAULT_IDLE_TIMEOUT_MIN,\n )\n }\n\n if (isCodingAgent(agent)) {\n this.connectWorkerWs(agent, cwd)\n } else {\n this.workerWs.disconnect()\n this.checkProjectStatus(cwd)\n }\n }\n\n writePty(tabId: string, data: string): void {\n this.tabs.get(tabId)?.ptyManager.write(data)\n if (tabId === this.activeTabId) {\n this.bridge.feedInput(data)\n }\n }\n\n resizePty(tabId: string, cols: number, rows: number): void {\n this.tabs.get(tabId)?.ptyManager.resize(cols, rows)\n if (tabId === this.activeTabId) {\n this.bridge.resize(cols, rows)\n this.workerWs.sendTerminalResize(cols, rows)\n }\n }\n\n async killTab(tabId: string): Promise<void> {\n const tab = this.tabs.get(tabId)\n if (!tab) return\n if (tab.termStreamTimer) clearTimeout(tab.termStreamTimer)\n await this.timeTracker.endSession(tabId)\n tab.ptyManager.kill()\n this.tabs.delete(tabId)\n if (tabId === this.activeTabId) {\n this.bridge.endSession()\n if (isCodingAgent(tab.agent)) {\n this.workerWs.disconnect()\n }\n // Switch active to another tab if available\n const remaining = [...this.tabs.keys()]\n this.activeTabId = remaining.length > 0 ? remaining[remaining.length - 1] : null\n }\n }\n\n setActiveTab(tabId: string): void {\n this.activeTabId = tabId\n const tab = this.tabs.get(tabId)\n if (tab) {\n this.currentAgent = tab.agent\n this.currentCwd = tab.cwd\n }\n }\n\n getTabIds(): string[] {\n return [...this.tabs.keys()]\n }\n\n // \u2500\u2500\u2500 Tracking control (active tab) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n isActiveTabTracking(): boolean {\n if (!this.activeTabId) return false\n return this.timeTracker.isTracking(this.activeTabId)\n }\n\n async setActiveTabTracking(enabled: boolean): Promise<void> {\n if (!this.activeTabId) return\n const tab = this.tabs.get(this.activeTabId)\n if (!tab) return\n if (enabled) {\n if (this.timeTracker.isTracking(this.activeTabId)) return\n const profile = this.getActiveProfile()\n await this.timeTracker.startSession(\n this.activeTabId,\n tab.cwd,\n tab.agent.name,\n profile.idleTimeoutMin ?? DEFAULT_IDLE_TIMEOUT_MIN,\n )\n } else {\n await this.timeTracker.endSession(this.activeTabId)\n }\n }\n\n // \u2500\u2500\u2500 Worker WebSocket \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n connectWorkerWs(agent: AgentConfig, cwd: string): void {\n const profile = this.getActiveProfile()\n const apiKey = profile.apiKey || process.env.CTLSURF_API_KEY\n if (!apiKey) {\n log('[worker-ws] No API key, skipping WS connect')\n return\n }\n\n this.workerWs.connect({\n machine: os.hostname(),\n cwd,\n agent: agent.name,\n })\n }\n\n private async checkProjectStatus(cwd: string): Promise<void> {\n if (!this.ctlsurfApi.getApiKey()) {\n this.events.onWorkerStatus('no_project')\n return\n }\n try {\n const folder = await this.ctlsurfApi.findFolderByPath(cwd)\n if (!folder?.id) {\n this.events.onWorkerStatus('no_project')\n }\n } catch {\n this.events.onWorkerStatus('no_project')\n }\n }\n\n private streamTerminalData(tabId: string, data: string): void {\n const tab = this.tabs.get(tabId)\n if (!tab) return\n tab.termStreamBuffer += data\n if (!tab.termStreamTimer) {\n tab.termStreamTimer = setTimeout(() => {\n if (tab.termStreamBuffer) {\n this.workerWs.sendTerminalData(tab.termStreamBuffer)\n tab.termStreamBuffer = ''\n }\n tab.termStreamTimer = null\n }, TERM_STREAM_INTERVAL_MS)\n }\n }\n\n // \u2500\u2500\u2500 Shutdown \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async shutdown(): Promise<void> {\n this.bridge.endSession()\n await this.timeTracker.endAll()\n for (const [, tab] of this.tabs) {\n if (tab.termStreamTimer) clearTimeout(tab.termStreamTimer)\n tab.ptyManager.kill()\n }\n this.tabs.clear()\n this.workerWs.disconnect()\n }\n}\n", "import { createRequire } from 'module'\nimport { AgentConfig } from './agents'\n\n// Use createRequire to load native module at runtime, bypassing bundler\nconst require = createRequire(import.meta.url)\nconst pty = require('node-pty')\n\nexport class PtyManager {\n private process: any | null = null\n private dataCallbacks: ((data: string) => void)[] = []\n private exitCallbacks: ((code: number) => void)[] = []\n\n constructor(agent: AgentConfig, cwd: string) {\n const shell = agent.command\n const args = agent.args || []\n\n try {\n console.log(`[pty] Spawning: ${shell} ${args.join(' ')} in ${cwd}`)\n } catch {\n // Ignore EPIPE errors when stdout is closed\n }\n\n this.process = pty.spawn(shell, args, {\n name: 'xterm-256color',\n cwd,\n env: process.env as Record<string, string>,\n cols: 80,\n rows: 24\n })\n\n this.process.onData((data: string) => {\n for (const cb of this.dataCallbacks) {\n cb(data)\n }\n })\n\n this.process.onExit(({ exitCode }: { exitCode: number }) => {\n for (const cb of this.exitCallbacks) {\n cb(exitCode)\n }\n this.process = null\n })\n }\n\n write(data: string): void {\n this.process?.write(data)\n }\n\n resize(cols: number, rows: number): void {\n this.process?.resize(cols, rows)\n }\n\n kill(): void {\n this.process?.kill()\n this.process = null\n }\n\n onData(cb: (data: string) => void): void {\n this.dataCallbacks.push(cb)\n }\n\n onExit(cb: (code: number) => void): void {\n this.exitCallbacks.push(cb)\n }\n}\n", "export interface AgentConfig {\n id: string\n name: string\n command: string\n args: string[]\n description: string\n}\n\nfunction getShellCommand(): string {\n if (process.platform === 'win32') return 'powershell.exe'\n return process.env.SHELL || '/bin/zsh'\n}\n\nexport function getBuiltinAgents(): AgentConfig[] {\n return [\n {\n id: 'shell',\n name: 'Shell',\n command: getShellCommand(),\n args: ['-l'], // login shell to load PATH\n description: 'Default system shell'\n },\n {\n id: 'claude',\n name: 'Claude Code',\n command: 'claude',\n args: [],\n description: 'Anthropic Claude Code CLI'\n },\n {\n id: 'codex',\n name: 'Codex CLI',\n command: 'codex',\n args: [],\n description: 'OpenAI Codex CLI'\n }\n ]\n}\n\nexport function getDefaultAgent(): AgentConfig {\n return getBuiltinAgents()[0]\n}\n\nexport function isCodingAgent(agent: AgentConfig): boolean {\n return agent.id !== 'shell'\n}\n", "const CTLSURF_BASE_URL = 'https://app.ctlsurf.com/api'\n\nexport class CtlsurfApi {\n private baseUrl: string\n private apiKey: string | null = null\n\n constructor(baseUrl?: string) {\n this.baseUrl = baseUrl || CTLSURF_BASE_URL\n }\n\n setApiKey(key: string): void {\n this.apiKey = key\n }\n\n setBaseUrl(url: string): void {\n this.baseUrl = url.endsWith('/api') ? url : `${url}/api`\n }\n\n getApiKey(): string | null {\n return this.apiKey\n }\n\n private headers(): Record<string, string> {\n const h: Record<string, string> = { 'Content-Type': 'application/json' }\n if (this.apiKey) {\n h['Authorization'] = `Bearer ${this.apiKey}`\n }\n return h\n }\n\n private async request(method: string, path: string, body?: unknown): Promise<any> {\n const url = `${this.baseUrl}${path}`\n const opts: RequestInit = {\n method,\n headers: this.headers()\n }\n if (body) {\n opts.body = JSON.stringify(body)\n }\n\n const res = await fetch(url, opts)\n if (!res.ok) {\n const text = await res.text()\n throw new Error(`ctlsurf API ${method} ${path}: ${res.status} ${text}`)\n }\n return res.json()\n }\n\n // \u2500\u2500\u2500 Pages \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async createPage(params: {\n title: string\n type?: string\n parent_id?: string\n folder_id?: string\n cwd?: string\n tags?: string[]\n }): Promise<any> {\n return this.request('POST', '/pages', params)\n }\n\n async findPageByRootPath(rootPath: string): Promise<any> {\n return this.request('POST', '/pages/find-by-root-path', { root_path: rootPath })\n }\n\n // \u2500\u2500\u2500 Blocks \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async createBlock(pageId: string, params: {\n type: string\n title?: string\n props?: Record<string, unknown>\n }): Promise<any> {\n return this.request('POST', `/blocks/page/${pageId}`, params)\n }\n\n async getBlock(blockId: string): Promise<any> {\n return this.request('GET', `/blocks/${blockId}`)\n }\n\n async updateBlock(blockId: string, params: {\n props?: Record<string, unknown>\n title?: string\n }): Promise<any> {\n return this.request('PUT', `/blocks/${blockId}`, params)\n }\n\n // \u2500\u2500\u2500 Folders \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async createFolder(params: { name: string; root_path: string }): Promise<any> {\n return this.request('POST', '/folders', params)\n }\n\n // \u2500\u2500\u2500 Workers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async getAuthCode(): Promise<{ code: string }> {\n return this.request('POST', '/workers/token-exchange')\n }\n\n async findFolderByPath(rootPath: string): Promise<any> {\n return this.request('POST', '/folders/find-by-path', { root_path: rootPath })\n }\n\n async getFolderPages(folderId: string): Promise<any[]> {\n const folder = await this.request('GET', `/folders/${folderId}`)\n return folder?.pages || []\n }\n\n async getFolder(folderId: string): Promise<any> {\n return this.request('GET', `/folders/${folderId}`)\n }\n\n // \u2500\u2500\u2500 Datastore \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async getPageBlockSummaries(pageId: string): Promise<any[]> {\n return this.request('GET', `/blocks/page/${pageId}/summary`)\n }\n\n async addRow(blockId: string, data: Record<string, unknown>): Promise<any> {\n return this.request('POST', `/datastore/${blockId}/rows`, { data })\n }\n\n async updateRow(blockId: string, rowId: string, data: Record<string, unknown>): Promise<any> {\n return this.request('PUT', `/datastore/${blockId}/rows/${rowId}`, { data })\n }\n\n async getDatastoreSchema(blockId: string): Promise<{ block_id: string; columns: Array<{ id: string; name: string; type: string }> }> {\n return this.request('GET', `/datastore/${blockId}/schema`)\n }\n\n async updateDatastoreSchema(blockId: string, columns: Array<{ id: string; name: string; type: string }>): Promise<any> {\n return this.request('PUT', `/datastore/${blockId}/schema`, { columns })\n }\n\n async findFolderByGitRemote(gitRemote: string): Promise<any> {\n // Search folders by listing all and matching git_remote\n const folders = await this.request('GET', '/folders')\n return folders?.find((f: any) => f.git_remote === gitRemote || f.root_path === gitRemote) || null\n }\n\n // \u2500\u2500\u2500 Log convenience \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async appendLog(blockId: string, action: string, message: string, data?: Record<string, unknown>): Promise<any> {\n // Read-modify-write: get current entries, append, put back\n const block = await this.getBlock(blockId)\n const props = block.props || {}\n const entries = Array.isArray(props.entries) ? [...props.entries] : []\n const maxEntries = props.max_entries || 1000\n\n const entry: Record<string, unknown> = {\n _id: `log_${entries.length}`,\n _timestamp: new Date().toISOString(),\n action,\n message\n }\n if (data) {\n entry.data = data\n }\n\n entries.push(entry)\n\n // Trim oldest if over max\n const trimmed = entries.length > maxEntries ? entries.slice(-maxEntries) : entries\n\n return this.updateBlock(blockId, {\n props: { ...props, entries: trimmed }\n })\n }\n}\n", "import { WorkerWsClient } from './workerWs'\n\n/**\n * Conversation Bridge\n *\n * Captures PTY output, strips ANSI codes and terminal artifacts,\n * and sends cleaned text to the backend via WebSocket for chat logging.\n * Uses a simple buffer + byte threshold approach (no timers, since\n * setTimeout/setInterval don't reliably fire under TUI raw mode).\n */\nexport class ConversationBridge {\n private wsClient: WorkerWsClient | null = null\n private sessionActive: boolean = false\n private inputBuffer: string = ''\n private outputBuffer: string = ''\n\n private readonly FLUSH_BYTES = 500\n\n setWsClient(ws: WorkerWsClient): void {\n this.wsClient = ws\n }\n\n startSession(): void {\n this.outputBuffer = ''\n this.inputBuffer = ''\n this.sessionActive = true\n console.log('[bridge] Session started')\n }\n\n feedOutput(data: string): void {\n if (!this.sessionActive) return\n\n this.outputBuffer += data\n\n if (this.outputBuffer.length >= this.FLUSH_BYTES) {\n this.flush()\n }\n }\n\n feedInput(data: string): void {\n if (!this.sessionActive) return\n this.inputBuffer += data\n\n if (data.includes('\\r') || data.includes('\\n')) {\n const cleaned = cleanInput(this.inputBuffer)\n if (cleaned.length > 0) {\n this.sendEntry('user_input', cleaned)\n }\n this.inputBuffer = ''\n }\n }\n\n resize(_cols: number, _rows: number): void {\n // no-op now (was used for xterm)\n }\n\n private flush(): void {\n if (this.outputBuffer.length === 0) return\n\n const raw = this.outputBuffer\n this.outputBuffer = ''\n\n const cleaned = cleanOutput(raw)\n if (cleaned.length === 0) return\n\n this.sendEntry('terminal_output', cleaned)\n }\n\n private sendEntry(type: string, content: string): void {\n if (!this.wsClient) return\n this.wsClient.sendChatLog({\n ts: new Date().toISOString(),\n type,\n content,\n })\n }\n\n endSession(): void {\n if (!this.sessionActive) return\n\n this.flush()\n\n this.sessionActive = false\n this.inputBuffer = ''\n this.outputBuffer = ''\n console.log('[bridge] Session ended')\n }\n}\n\n/**\n * Strip ANSI escape codes from a string.\n */\nfunction stripAnsi(str: string): string {\n return str\n // CSI sequences (e.g. \\x1b[0m, \\x1b[?2004h, \\x1b[1;32m, \\x1b[38;2;r;g;bm)\n .replace(/\\x1b\\[[\\x30-\\x3f]*[\\x20-\\x2f]*[\\x40-\\x7e]/g, '')\n // OSC sequences (e.g. \\x1b]0;title\\x07, \\x1b]...\\x1b\\\\)\n .replace(/\\x1b\\][^\\x07\\x1b]*(?:\\x07|\\x1b\\\\)/g, '')\n // DCS/PM/APC sequences\n .replace(/\\x1b[PX^_][^\\x1b]*\\x1b\\\\/g, '')\n // Other escape sequences (charset, keypad mode, etc.)\n .replace(/\\x1b[^[\\]PX^_](.|$)/g, '')\n // Remaining single ESC\n .replace(/\\x1b/g, '')\n}\n\n/**\n * Process backspace characters: each \\x7f or \\b deletes the preceding char.\n */\nfunction processBackspaces(str: string): string {\n const result: string[] = []\n for (const ch of str) {\n if (ch === '\\x7f' || ch === '\\b') {\n result.pop()\n } else {\n result.push(ch)\n }\n }\n return result.join('')\n}\n\n/**\n * Clean user input: strip ANSI, process backspaces, remove control chars.\n */\nfunction cleanInput(str: string): string {\n let cleaned = stripAnsi(str)\n cleaned = processBackspaces(cleaned)\n // eslint-disable-next-line no-control-regex\n cleaned = cleaned.replace(/[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]/g, '')\n return cleaned.trim()\n}\n\n/**\n * Clean terminal output: strip ANSI, remove control chars, collapse noise.\n */\nfunction cleanOutput(str: string): string {\n let cleaned = stripAnsi(str)\n // Remove carriage returns\n cleaned = cleaned.replace(/\\r/g, '')\n // Remove control characters except newline/tab\n // eslint-disable-next-line no-control-regex\n cleaned = cleaned.replace(/[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]/g, '')\n // Collapse 3+ consecutive newlines into 2\n cleaned = cleaned.replace(/\\n{3,}/g, '\\n\\n')\n // Remove lines that are only whitespace\n cleaned = cleaned.split('\\n').filter(line => line.trim().length > 0).join('\\n')\n return cleaned.trim()\n}\n", "import os from 'os'\nimport crypto from 'crypto'\nimport WsModule from 'ws'\n\n// Use native WebSocket if available (Node 22+), otherwise fall back to ws package\nconst WS: typeof WebSocket = typeof WebSocket !== 'undefined' ? WebSocket : WsModule as any\n\nfunction log(...args: unknown[]): void {\n try { console.log(...args) } catch { /* EPIPE safe */ }\n}\n\nconst HEARTBEAT_INTERVAL_MS = 30_000\nconst RECONNECT_DELAY_MS = 5_000\nconst MAX_RECONNECT_DELAY_MS = 60_000\n\nexport interface WorkerRegistration {\n machine: string\n cwd: string\n repo_url?: string\n agent: string\n fingerprint: string\n}\n\nexport interface WorkerWsEvents {\n onStatusChange: (status: WorkerWsStatus) => void\n onMessage: (message: IncomingMessage) => void\n onRegistered: (data: { worker_id: string; folder_id: string | null; status: string; pending_messages?: IncomingMessage[] }) => void\n onTerminalInput?: (data: string) => void\n}\n\nexport interface IncomingMessage {\n id: string\n type: string\n content: string\n metadata?: Record<string, unknown> | null\n parent_id?: string | null\n}\n\nexport type WorkerWsStatus = 'disconnected' | 'connecting' | 'connected' | 'pending_approval'\n\nexport class WorkerWsClient {\n private ws: WebSocket | null = null\n private apiKey: string | null = null\n private baseUrl: string\n private events: WorkerWsEvents\n private heartbeatTimer: ReturnType<typeof setInterval> | null = null\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null\n private reconnectDelay = RECONNECT_DELAY_MS\n private registration: WorkerRegistration | null = null\n private workerId: string | null = null\n private _status: WorkerWsStatus = 'disconnected'\n private shouldReconnect = false\n private fingerprint: string\n\n constructor(events: WorkerWsEvents, baseUrl?: string) {\n this.events = events\n this.baseUrl = baseUrl || 'wss://app.ctlsurf.com'\n // Generate a stable machine fingerprint\n this.fingerprint = this.generateFingerprint()\n }\n\n get status(): WorkerWsStatus {\n return this._status\n }\n\n get currentWorkerId(): string | null {\n return this.workerId\n }\n\n setApiKey(key: string | null): void {\n this.apiKey = key\n }\n\n setBaseUrl(url: string): void {\n this.baseUrl = url\n }\n\n private generateFingerprint(): string {\n const data = `${os.hostname()}:${os.userInfo().username}:${os.platform()}:${os.arch()}`\n return crypto.createHash('sha256').update(data).digest('hex').slice(0, 32)\n }\n\n private setStatus(status: WorkerWsStatus): void {\n if (this._status !== status) {\n this._status = status\n this.events.onStatusChange(status)\n }\n }\n\n connect(registration: WorkerRegistration): void {\n this.registration = { ...registration, fingerprint: this.fingerprint }\n this.shouldReconnect = true\n this.doConnect()\n }\n\n disconnect(): void {\n this.shouldReconnect = false\n this.clearTimers()\n if (this.ws) {\n const oldWs = this.ws\n this.ws = null\n // Remove handlers before closing to prevent stale onclose from firing\n oldWs.onopen = null\n oldWs.onmessage = null\n oldWs.onclose = null\n oldWs.onerror = null\n try { oldWs.close(1000, 'client disconnect') } catch { /* ignore */ }\n }\n this.setStatus('disconnected')\n }\n\n sendResponse(parentId: string, content: string, metadata?: Record<string, unknown>): void {\n this.send({\n type: 'response',\n parent_id: parentId,\n content,\n metadata,\n })\n }\n\n sendStatusUpdate(status: string): void {\n this.send({ type: 'status_update', status })\n }\n\n sendAck(messageId: string): void {\n this.send({ type: 'ack', message_id: messageId })\n }\n\n sendTerminalData(data: string): void {\n this.send({ type: 'terminal_stream', data })\n }\n\n sendTerminalResize(cols: number, rows: number): void {\n this.send({ type: 'terminal_resize', cols, rows })\n }\n\n sendChatLog(entry: { type: string; content: string; ts?: string }): void {\n this.send({ type: 'chat_log', entry })\n }\n\n private doConnect(): void {\n if (!this.apiKey || !this.registration) {\n log('[worker-ws] No API key or registration, skipping connect')\n return\n }\n\n this.clearTimers()\n if (this.ws) {\n const oldWs = this.ws\n this.ws = null\n oldWs.onopen = null\n oldWs.onmessage = null\n oldWs.onclose = null\n oldWs.onerror = null\n try { oldWs.close() } catch { /* ignore */ }\n // Let the old connection fully close before opening a new one\n setTimeout(() => this.doConnectNow(), 500)\n return\n }\n\n this.doConnectNow()\n }\n\n private doConnectNow(): void {\n if (!this.apiKey || !this.registration) return\n if (!this.shouldReconnect) {\n log('[worker-ws] shouldReconnect is false, aborting connect')\n return\n }\n\n this.setStatus('connecting')\n\n // Use ws:// for localhost, wss:// for remote\n const wsBase = this.baseUrl.replace(/^http/, 'ws')\n const url = `${wsBase}/api/ws/worker?token=${encodeURIComponent(this.apiKey)}`\n\n log(`[worker-ws] Connecting to ${url.replace(/token=.*/, 'token=***')}...`)\n\n try {\n this.ws = new WS(url) as unknown as WebSocket\n } catch (err) {\n log('[worker-ws] Failed to create WebSocket:', err)\n this.scheduleReconnect()\n return\n }\n\n this.ws.onopen = () => {\n log('[worker-ws] Connected, sending register')\n this.reconnectDelay = RECONNECT_DELAY_MS // Reset backoff\n this.send({\n type: 'register',\n ...this.registration,\n })\n this.startHeartbeat()\n }\n\n this.ws.onmessage = (event) => {\n try {\n const data = JSON.parse(String(event.data))\n this.handleMessage(data)\n } catch (err) {\n log('[worker-ws] Failed to parse message:', err)\n }\n }\n\n this.ws.onclose = (event) => {\n log(`[worker-ws] Disconnected: ${event.code} ${event.reason}`)\n this.ws = null\n this.clearHeartbeat()\n this.setStatus('disconnected')\n if (this.shouldReconnect) {\n this.scheduleReconnect()\n }\n }\n\n this.ws.onerror = () => {\n log('[worker-ws] WebSocket error')\n }\n }\n\n private handleMessage(data: Record<string, unknown>): void {\n const msgType = data.type as string\n\n switch (msgType) {\n case 'registered': {\n this.workerId = data.worker_id as string\n const workerStatus = data.status as string\n console.log(`[worker-ws] Registered as ${this.workerId}, status: ${workerStatus}`)\n\n if (workerStatus === 'pending_approval') {\n this.setStatus('pending_approval')\n } else {\n this.setStatus('connected')\n }\n\n const pendingMessages = (data.pending_messages || []) as IncomingMessage[]\n this.events.onRegistered({\n worker_id: this.workerId,\n folder_id: data.folder_id as string | null,\n status: workerStatus,\n pending_messages: pendingMessages,\n })\n\n // Deliver pending messages\n for (const msg of pendingMessages) {\n this.events.onMessage(msg)\n }\n break\n }\n\n case 'approved': {\n log('[worker-ws] Worker approved!')\n this.setStatus('connected')\n break\n }\n\n case 'message': {\n const msg = data.message as IncomingMessage\n if (msg) {\n console.log(`[worker-ws] Received message: ${msg.id}`)\n this.events.onMessage(msg)\n }\n break\n }\n\n case 'terminal_input': {\n const inputData = data.data as string\n if (inputData && this.events.onTerminalInput) {\n this.events.onTerminalInput(inputData)\n }\n break\n }\n\n case 'heartbeat_ack':\n break\n\n default:\n console.log(`[worker-ws] Unknown message type: ${msgType}`)\n }\n }\n\n private send(data: Record<string, unknown>): void {\n if (this.ws && this.ws.readyState === WS.OPEN) {\n this.ws.send(JSON.stringify(data))\n }\n }\n\n\n private startHeartbeat(): void {\n this.clearHeartbeat()\n this.heartbeatTimer = setInterval(() => {\n this.send({ type: 'heartbeat' })\n }, HEARTBEAT_INTERVAL_MS)\n }\n\n private clearHeartbeat(): void {\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer)\n this.heartbeatTimer = null\n }\n }\n\n private scheduleReconnect(): void {\n if (!this.shouldReconnect) return\n console.log(`[worker-ws] Reconnecting in ${this.reconnectDelay / 1000}s...`)\n this.reconnectTimer = setTimeout(() => {\n this.doConnect()\n }, this.reconnectDelay)\n // Exponential backoff\n this.reconnectDelay = Math.min(this.reconnectDelay * 2, MAX_RECONNECT_DELAY_MS)\n }\n\n private clearTimers(): void {\n this.clearHeartbeat()\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer)\n this.reconnectTimer = null\n }\n }\n}\n", "import os from 'os'\nimport { randomUUID } from 'crypto'\nimport type { CtlsurfApi } from './ctlsurfApi'\n\nconst DATASTORE_TITLE = 'Time Tracking'\nconst AGENT_DATASTORE_PAGE_TITLE = 'Agent Datastore'\nconst FIRST_CHECKPOINT_DELAY_MS = 30 * 1000\nconst CHECKPOINT_INTERVAL_MS = 5 * 60 * 1000\n\nconst COLUMNS: Array<{ name: string; type: string }> = [\n { name: 'Started', type: 'date' },\n { name: 'Active Time', type: 'number' },\n { name: 'Agent', type: 'text' },\n { name: 'Worker', type: 'text' },\n { name: 'Session', type: 'text' },\n { name: 'Notes', type: 'text' },\n]\n\ninterface SessionState {\n blockId: string\n rowId: string\n cwd: string\n startedAt: number\n lastActivity: number\n activeMs: number\n idleTimeoutMs: number\n firstCheckpointTimer: ReturnType<typeof setTimeout> | null\n checkpointTimer: ReturnType<typeof setInterval> | null\n ended: boolean\n}\n\nfunction log(...args: unknown[]): void {\n try { console.log('[time-tracker]', ...args) } catch { /* EPIPE safe */ }\n}\n\nfunction findPageByTitle(pages: any[], title: string): any | null {\n for (const p of pages) {\n if (p?.title === title) return p\n if (p?.children?.length) {\n const c = findPageByTitle(p.children, title)\n if (c) return c\n }\n }\n return null\n}\n\nexport class TimeTracker {\n private api: CtlsurfApi\n private sessions = new Map<string, SessionState>()\n private blockCache = new Map<string, string>()\n\n constructor(api: CtlsurfApi) {\n this.api = api\n }\n\n async startSession(tabId: string, cwd: string, agentName: string, idleTimeoutMin: number): Promise<void> {\n if (this.sessions.has(tabId)) {\n await this.endSession(tabId)\n }\n try {\n const blockId = await this.ensureDatastore(cwd)\n if (!blockId) {\n log(`No \"${AGENT_DATASTORE_PAGE_TITLE}\" page found for ${cwd} \u2014 tracking disabled for this session`)\n return\n }\n const startedAt = Date.now()\n const startedIso = new Date(startedAt).toISOString()\n const sessionUuid = randomUUID()\n const row = await this.api.addRow(blockId, {\n Started: startedIso,\n 'Active Time': 0,\n Agent: agentName,\n Worker: os.hostname(),\n Session: sessionUuid,\n Notes: '',\n })\n const rowId = row?.id\n if (!rowId) {\n log('addRow returned no id; aborting tracking', row)\n return\n }\n const state: SessionState = {\n blockId,\n rowId,\n cwd,\n startedAt,\n lastActivity: startedAt,\n activeMs: 0,\n idleTimeoutMs: Math.max(1, idleTimeoutMin) * 60 * 1000,\n firstCheckpointTimer: null,\n checkpointTimer: null,\n ended: false,\n }\n state.firstCheckpointTimer = setTimeout(() => {\n void this.checkpoint(tabId)\n const live = this.sessions.get(tabId)\n if (live && !live.ended) {\n live.checkpointTimer = setInterval(() => {\n void this.checkpoint(tabId)\n }, CHECKPOINT_INTERVAL_MS)\n }\n }, FIRST_CHECKPOINT_DELAY_MS)\n this.sessions.set(tabId, state)\n log(`Started tracking tab=${tabId} agent=\"${agentName}\" cwd=${cwd}`)\n } catch (err: any) {\n log(`startSession failed: ${err?.message || err}`)\n }\n }\n\n isTracking(tabId: string): boolean {\n const s = this.sessions.get(tabId)\n return !!s && !s.ended\n }\n\n recordActivity(tabId: string): void {\n const s = this.sessions.get(tabId)\n if (!s || s.ended) return\n const now = Date.now()\n const delta = now - s.lastActivity\n if (delta < s.idleTimeoutMs) {\n s.activeMs += delta\n }\n s.lastActivity = now\n }\n\n async endSession(tabId: string): Promise<void> {\n const s = this.sessions.get(tabId)\n if (!s || s.ended) return\n s.ended = true\n if (s.firstCheckpointTimer) clearTimeout(s.firstCheckpointTimer)\n if (s.checkpointTimer) clearInterval(s.checkpointTimer)\n try {\n await this.writeRow(s, Date.now())\n } catch (err: any) {\n log(`endSession write failed: ${err?.message || err}`)\n }\n this.sessions.delete(tabId)\n }\n\n async endAll(): Promise<void> {\n const ids = [...this.sessions.keys()]\n await Promise.all(ids.map(id => this.endSession(id)))\n }\n\n private async checkpoint(tabId: string): Promise<void> {\n const s = this.sessions.get(tabId)\n if (!s || s.ended) return\n try {\n await this.writeRow(s, Date.now())\n } catch (err: any) {\n log(`checkpoint failed: ${err?.message || err}`)\n }\n }\n\n private async writeRow(s: SessionState, _endTimeMs: number): Promise<void> {\n const activeMin = Math.round(s.activeMs / 60000)\n await this.api.updateRow(s.blockId, s.rowId, {\n 'Active Time': activeMin,\n })\n }\n\n private async ensureDatastore(cwd: string): Promise<string | null> {\n const cached = this.blockCache.get(cwd)\n if (cached) return cached\n\n let folder: any = null\n try {\n folder = await this.api.findFolderByPath(cwd)\n } catch {\n return null\n }\n if (!folder?.id) return null\n\n const folderDetail = await this.api.getFolder(folder.id)\n const agentPage = findPageByTitle(folderDetail?.pages || [], AGENT_DATASTORE_PAGE_TITLE)\n if (!agentPage?.id) return null\n\n const summaries = await this.api.getPageBlockSummaries(agentPage.id)\n const existing = (summaries || []).find((b: any) => b?.type === 'datastore' && b?.title === DATASTORE_TITLE)\n if (existing?.id) {\n await this.ensureColumns(existing.id)\n this.blockCache.set(cwd, existing.id)\n return existing.id\n }\n\n const columns = COLUMNS.map((c, i) => ({ id: `col_${i}`, name: c.name, type: c.type }))\n const created = await this.api.createBlock(agentPage.id, {\n type: 'datastore',\n title: DATASTORE_TITLE,\n props: { columns },\n })\n if (created?.id) {\n log(`Created \"${DATASTORE_TITLE}\" datastore on Agent Datastore page for ${cwd}`)\n this.blockCache.set(cwd, created.id)\n return created.id\n }\n return null\n }\n\n private async ensureColumns(blockId: string): Promise<void> {\n try {\n const schema = await this.api.getDatastoreSchema(blockId)\n const existingCols = schema.columns || []\n const existingNames = new Set(existingCols.map(c => c.name))\n const missing = COLUMNS.filter(c => !existingNames.has(c.name))\n if (missing.length === 0) return\n\n const usedIds = new Set(existingCols.map(c => c.id))\n let nextIdx = existingCols.length\n const appended = missing.map(c => {\n let id = `col_${nextIdx++}`\n while (usedIds.has(id)) id = `col_${nextIdx++}`\n usedIds.add(id)\n return { id, name: c.name, type: c.type }\n })\n const merged = [...existingCols, ...appended]\n await this.api.updateDatastoreSchema(blockId, merged)\n log(`Added ${missing.length} missing column(s) to existing Time Tracking datastore: ${missing.map(c => c.name).join(', ')}`)\n } catch (err: any) {\n log(`ensureColumns failed: ${err?.message || err}`)\n }\n }\n}\n", "import path from 'path'\nimport os from 'os'\n\nexport function getSettingsDir(useElectron: boolean): string {\n if (useElectron) {\n const { app } = require('electron')\n return app.getPath('userData')\n }\n\n if (process.platform === 'darwin') {\n return path.join(os.homedir(), 'Library', 'Application Support', 'ctlsurf-worker')\n }\n return path.join(\n process.env.XDG_CONFIG_HOME || path.join(os.homedir(), '.config'),\n 'ctlsurf-worker'\n )\n}\n", "/**\n * Terminal UI (TUI) renderer\n *\n * Minimal status bar at the bottom, full PTY passthrough above.\n * No alternate screen \u2014 the PTY (Claude Code) owns the screen and\n * handles its own scrolling, mouse events, and display management.\n * Status info also goes to the terminal tab title via OSC.\n */\n\nconst ESC = '\\x1b'\nconst CSI = `${ESC}[`\n\n// Colors (Tokyo Night palette)\nconst BG_BAR = `${CSI}48;2;22;22;30m` // #16161e\nconst FG_DIM = `${CSI}38;2;86;95;137m` // #565f89\nconst FG_ACCENT = `${CSI}38;2;122;162;247m` // #7aa2f7\nconst FG_GREEN = `${CSI}38;2;158;206;106m` // #9ece6a\nconst FG_RED = `${CSI}38;2;247;118;142m` // #f7768e\nconst FG_YELLOW = `${CSI}38;2;224;175;104m` // #e0af68\nconst FG_WHITE = `${CSI}38;2;169;177;214m` // #a9b1d6\nconst FG_TITLE = `${CSI}38;2;192;202;245m` // #c0caf5\nconst BG_MODAL = `${CSI}48;2;31;35;53m` // #1f2335\nconst BG_SELECTED = `${CSI}48;2;42;43;61m` // #2a2b3d\nconst RESET = `${CSI}0m`\n\nexport interface TuiState {\n agentName: string\n cwd: string\n wsStatus: string\n workerId: string | null\n mode: string\n}\n\nexport class Tui {\n private rows: number = 0\n private cols: number = 0\n private state: TuiState = {\n agentName: '',\n cwd: '',\n wsStatus: 'disconnected',\n workerId: null,\n mode: 'terminal',\n }\n\n constructor() {\n this.rows = process.stdout.rows || 24\n this.cols = process.stdout.columns || 80\n }\n\n /**\n * Initialize the TUI: scroll region leaving last row for status bar.\n * No alternate screen \u2014 PTY fully owns the display.\n */\n init(): void {\n // Just set tab title \u2014 the PTY agent owns the full screen\n this.updateTerminalTitle()\n }\n\n /**\n * Restore terminal to normal state\n */\n destroy(): void {\n // Show cursor\n this.write(`${CSI}?25h`)\n }\n\n /**\n * Handle terminal resize\n */\n resize(cols: number, rows: number): void {\n this.cols = cols\n this.rows = rows\n }\n\n /**\n * Get the PTY dimensions (all rows minus status bar)\n */\n getPtySize(): { cols: number; rows: number } {\n return {\n cols: this.cols,\n rows: this.rows,\n }\n }\n\n /**\n * Update state and redraw status bar\n */\n update(partial: Partial<TuiState>): void {\n Object.assign(this.state, partial)\n this.updateTerminalTitle()\n }\n\n /**\n * Write PTY output \u2014 full passthrough.\n */\n writePtyData(data: string): void {\n this.write(data)\n }\n\n /**\n * Update the terminal window/tab title via OSC escape sequence.\n */\n setTerminalTitle(title: string): void {\n this.write(`${ESC}]0;${title}\\x07`)\n }\n\n /**\n * Build a title string from current state for the terminal tab.\n */\n updateTerminalTitle(): void {\n const { agentName, wsStatus, cwd } = this.state\n const displayCwd = this.shortenPath(cwd)\n const statusIcon = {\n connected: '\\u25CF',\n connecting: '\\u25CB',\n disconnected: '\\u25CB',\n pending_approval: '\\u25CB',\n no_project: '\\u25CB',\n }[wsStatus] || '\\u25CB'\n const statusLabel = {\n connected: 'Connected',\n connecting: 'Connecting...',\n disconnected: 'Disconnected',\n pending_approval: 'Pending',\n no_project: 'No Project',\n }[wsStatus] || wsStatus\n\n this.setTerminalTitle(`ctlsurf \\u00B7 ${agentName} \\u00B7 ${statusIcon} ${statusLabel} \\u00B7 ${displayCwd}`)\n }\n\n /**\n * Show an interactive agent picker modal.\n * Uses alternate screen just for the picker, then exits back to normal.\n */\n showAgentPicker(\n agents: { name: string; description: string }[],\n options: { initialTrackTime: boolean },\n ): Promise<{ agentIdx: number; trackTime: boolean }> {\n return new Promise((resolve) => {\n let selected = 0\n let trackTime = options.initialTrackTime\n const modalWidth = 44\n // +4 for borders/title/sep, +2 for track-time separator + row\n const modalHeight = agents.length + 4 + 2\n const startCol = Math.max(1, Math.floor((this.cols - modalWidth) / 2))\n const startRow = Math.max(1, Math.floor((this.rows - modalHeight) / 2))\n\n // Enter alternate screen just for the picker\n this.write(`${CSI}?1049h`)\n this.write(`${CSI}?25l`)\n\n const drawModal = () => {\n const topBorder = '\\u250c' + '\\u2500'.repeat(modalWidth - 2) + '\\u2510'\n const botBorder = '\\u2514' + '\\u2500'.repeat(modalWidth - 2) + '\\u2518'\n\n for (let r = 0; r < this.rows; r++) {\n this.write(`${CSI}${r + 1};1H${BG_BAR}${CSI}2K${RESET}`)\n }\n\n const brand = 'ctlsurf'\n const brandCol = Math.max(1, Math.floor((this.cols - brand.length) / 2))\n this.write(`${CSI}${startRow - 2};${brandCol}H${FG_ACCENT}${brand}${RESET}`)\n\n this.write(`${CSI}${startRow};${startCol}H${BG_MODAL}${FG_DIM}${topBorder}${RESET}`)\n\n const title = ' Select Agent'\n const titlePad = ' '.repeat(Math.max(0, modalWidth - 2 - title.length))\n this.write(`${CSI}${startRow + 1};${startCol}H${BG_MODAL}${FG_DIM}\\u2502${RESET}${BG_MODAL}${FG_TITLE}${title}${titlePad}${FG_DIM}\\u2502${RESET}`)\n\n const sep = '\\u251c' + '\\u2500'.repeat(modalWidth - 2) + '\\u2524'\n this.write(`${CSI}${startRow + 2};${startCol}H${BG_MODAL}${FG_DIM}${sep}${RESET}`)\n\n for (let i = 0; i < agents.length; i++) {\n const agent = agents[i]\n const row = startRow + 3 + i\n const isSelected = i === selected\n const bg = isSelected ? BG_SELECTED : BG_MODAL\n const pointer = isSelected ? `${FG_ACCENT}\\u25B8 ` : ' '\n const nameFg = isSelected ? FG_ACCENT : FG_WHITE\n const nameStr = agent.name\n const descStr = agent.description ? ` ${FG_DIM}\\u2014 ${agent.description.slice(0, 20)}` : ''\n const content = `${pointer}${nameFg}${nameStr}${descStr}`\n const contentLen = 2 + nameStr.length + (agent.description ? 3 + Math.min(20, agent.description.length) : 0)\n const pad = ' '.repeat(Math.max(0, modalWidth - 2 - contentLen))\n this.write(`${CSI}${row};${startCol}H${bg}${FG_DIM}\\u2502${RESET}${bg}${content}${pad}${RESET}${BG_MODAL}${FG_DIM}\\u2502${RESET}`)\n }\n\n const innerSep = '\\u251c' + '\\u2500'.repeat(modalWidth - 2) + '\u2524'\n const sepRow = startRow + 3 + agents.length\n this.write(`${CSI}${sepRow};${startCol}H${BG_MODAL}${FG_DIM}${innerSep}${RESET}`)\n\n const trackRow = sepRow + 1\n const checkbox = trackTime ? `${FG_GREEN}[\u2713]${RESET}${BG_MODAL}` : `${FG_DIM}[ ]${RESET}${BG_MODAL}`\n const trackLabelFg = trackTime ? FG_WHITE : FG_DIM\n const trackContent = ` ${checkbox} ${trackLabelFg}Track time${RESET}${BG_MODAL}`\n const trackContentLen = 2 + 3 + 1 + 'Track time'.length\n const trackPad = ' '.repeat(Math.max(0, modalWidth - 2 - trackContentLen))\n this.write(`${CSI}${trackRow};${startCol}H${BG_MODAL}${FG_DIM}\u2502${RESET}${BG_MODAL}${trackContent}${trackPad}${FG_DIM}\u2502${RESET}`)\n\n const botRow = trackRow + 1\n this.write(`${CSI}${botRow};${startCol}H${BG_MODAL}${FG_DIM}${botBorder}${RESET}`)\n\n const hint = '\\u2191\\u2193 navigate \\u00B7 Enter select \\u00B7 t track \\u00B7 q quit'\n const hintCol = Math.max(1, Math.floor((this.cols - hint.length) / 2))\n this.write(`${CSI}${botRow + 2};${hintCol}H${FG_DIM}${hint}${RESET}`)\n }\n\n drawModal()\n\n if (process.stdin.isTTY) {\n process.stdin.setRawMode(true)\n }\n process.stdin.resume()\n\n const onKey = (data: Buffer) => {\n const key = data.toString()\n\n if (key === '\\x1b[A' || key === 'k') {\n selected = (selected - 1 + agents.length) % agents.length\n drawModal()\n } else if (key === '\\x1b[B' || key === 'j') {\n selected = (selected + 1) % agents.length\n drawModal()\n } else if (key === 't' || key === 'T' || key === ' ') {\n trackTime = !trackTime\n drawModal()\n } else if (key === '\\r' || key === '\\n') {\n cleanup()\n resolve({ agentIdx: selected, trackTime })\n } else if (key === 'q' || key === '\\x1b' || key === '\\x03') {\n cleanup()\n this.write(`${CSI}?25h`)\n this.write(`${CSI}?1049l`)\n process.exit(0)\n }\n }\n\n const cleanup = () => {\n process.stdin.removeListener('data', onKey)\n this.write(`${CSI}?25h`)\n // Leave alternate screen \u2014 back to normal mode for the agent\n this.write(`${CSI}?1049l`)\n }\n\n process.stdin.on('data', onKey)\n })\n }\n\n // \u2500\u2500\u2500 Internal \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private write(data: string): void {\n try {\n process.stdout.write(data)\n } catch { /* EPIPE safe */ }\n }\n\n private setScrollRegion(): void {\n // PTY gets rows 1 through (rows - 1), status bar on last row\n this.write(`${CSI}1;${this.rows - 1}r`)\n }\n\n private drawStatusBar(): void {\n const { agentName, wsStatus, cwd } = this.state\n\n // Move to last line (outside scroll region)\n this.write(`${CSI}${this.rows};1H`)\n this.write(`${BG_BAR}${CSI}2K`)\n\n const displayCwd = this.shortenPath(cwd)\n\n const statusColor = {\n connected: FG_GREEN,\n connecting: FG_YELLOW,\n disconnected: FG_RED,\n pending_approval: FG_YELLOW,\n no_project: FG_DIM,\n }[wsStatus] || FG_DIM\n\n const statusDot = `${statusColor}\\u25CF${RESET}${BG_BAR}`\n const statusLabel = {\n connected: 'Connected',\n connecting: 'Connecting...',\n disconnected: 'Disconnected',\n pending_approval: 'Pending Approval',\n no_project: 'No Project',\n }[wsStatus] || wsStatus\n\n const left = ` ${FG_ACCENT}ctlsurf${RESET}${BG_BAR} ${statusDot} ${FG_DIM}${statusLabel}${RESET}${BG_BAR} ${FG_DIM}\\u2502${RESET}${BG_BAR} ${FG_DIM}${agentName || '...'}${RESET}${BG_BAR}`\n const right = `${FG_DIM}Ctrl+\\\\ exit${RESET}${BG_BAR} ${FG_DIM}${displayCwd} ${RESET}${BG_BAR}`\n\n this.write(left)\n const pad = Math.max(0, this.cols - this.visibleLen(left) - this.visibleLen(right))\n this.write(' '.repeat(pad))\n this.write(right)\n this.write(RESET)\n }\n\n private shortenPath(p: string): string {\n if (!p) return ''\n const home = process.env.HOME || ''\n if (home && p.startsWith(home)) {\n return '~' + p.slice(home.length)\n }\n return p\n }\n\n private visibleLen(s: string): number {\n return s.replace(/\\x1b\\[[^m]*m/g, '').length\n }\n}\n", "#!/usr/bin/env node\n\n/**\n * ctlsurf terminal mode (TUI)\n *\n * Runs the agent in a PTY with a terminal UI: title bar, status bar,\n * conversation logging, and WebSocket control. No Electron required.\n *\n * Usage:\n * ctlsurf --terminal [--agent claude] [--cwd /path] [--api-key KEY] [--base-url URL] [--profile NAME]\n *\n * If no --agent is given, shows an interactive agent picker.\n * Press Ctrl+\\ to exit at any time.\n */\n\n// Prevent EPIPE crashes\nprocess.stdout?.on?.('error', () => {})\nprocess.stderr?.on?.('error', () => {})\nprocess.on('uncaughtException', (err) => {\n if (err.message === 'write EPIPE') return\n try { console.error('[uncaught]', err) } catch { /* ignore */ }\n})\n\nimport { Orchestrator } from './orchestrator'\nimport { getSettingsDir } from './settingsDir'\nimport { getBuiltinAgents, getDefaultAgent, isCodingAgent, type AgentConfig } from './agents'\nimport { Tui } from './tui'\n\n// \u2500\u2500\u2500 CLI arg parsing \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ninterface CliArgs {\n agent: string | null\n cwd: string\n apiKey: string | null\n baseUrl: string | null\n profile: string | null\n}\n\nfunction parseArgs(argv: string[]): CliArgs {\n const args: CliArgs = {\n agent: null,\n cwd: process.env.CTLSURF_WORKER_CWD || process.cwd(),\n apiKey: null,\n baseUrl: null,\n profile: null,\n }\n\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i]\n const next = argv[i + 1]\n switch (arg) {\n case '--agent': args.agent = next; i++; break\n case '--cwd': args.cwd = next; i++; break\n case '--api-key': args.apiKey = next; i++; break\n case '--base-url': args.baseUrl = next; i++; break\n case '--profile': args.profile = next; i++; break\n case '--terminal': break\n case '--desktop': break\n }\n }\n return args\n}\n\n// \u2500\u2500\u2500 Main \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nasync function main() {\n const args = parseArgs(process.argv.slice(2))\n const settingsDir = getSettingsDir(false)\n\n const tui = new Tui()\n const agents = getBuiltinAgents()\n\n // \u2500\u2500\u2500 Orchestrator (loaded early so picker can read profile defaults) \u2500\u2500\n\n const orchestrator = new Orchestrator(settingsDir, {\n onPtyData: (_tabId, data) => {\n tui.writePtyData(data)\n },\n onPtyExit: (_tabId, code) => {\n tui.destroy()\n console.log(`Agent exited with code ${code}`)\n orchestrator.shutdown().then(() => process.exit(code))\n },\n onWorkerStatus: (status) => {\n tui.update({ wsStatus: status })\n },\n onWorkerMessage: () => {},\n onWorkerRegistered: () => {\n tui.update({ wsStatus: 'connected' })\n },\n onCwdChanged: () => {\n tui.update({ cwd: orchestrator.cwd || '' })\n },\n })\n\n orchestrator.loadSettings()\n\n if (args.profile) orchestrator.switchProfile(args.profile)\n if (args.apiKey) orchestrator.overrideApiKey(args.apiKey)\n if (args.baseUrl) orchestrator.overrideBaseUrl(args.baseUrl)\n\n // \u2500\u2500\u2500 Agent selection \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n let agent: AgentConfig\n let trackTimeOverride: boolean | undefined\n\n if (args.agent) {\n const found = agents.find(a => a.id === args.agent)\n agent = found || {\n id: args.agent,\n name: args.agent,\n command: args.agent,\n args: [],\n description: `Custom agent: ${args.agent}`,\n }\n } else {\n const initialTrackTime = orchestrator.getActiveProfile().trackTime !== false\n const picked = await tui.showAgentPicker(agents, { initialTrackTime })\n agent = agents[picked.agentIdx]\n trackTimeOverride = picked.trackTime\n }\n\n // \u2500\u2500\u2500 Start TUI + agent \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n tui.update({\n agentName: agent.name,\n cwd: args.cwd,\n mode: 'terminal',\n })\n\n tui.init()\n\n // Spawn agent with PTY sized to fit the TUI content area\n const HEADLESS_TAB = 'headless'\n const ptySize = tui.getPtySize()\n await orchestrator.spawnAgent(HEADLESS_TAB, agent, args.cwd, { trackTime: trackTimeOverride })\n orchestrator.resizePty(HEADLESS_TAB, ptySize.cols, ptySize.rows)\n\n // For coding agents, send an initial prompt to kick-start them\n if (isCodingAgent(agent)) {\n setTimeout(() => {\n orchestrator.writePty(HEADLESS_TAB, 'hello\\r')\n }, 1000)\n }\n\n // Pipe stdin to PTY, with Ctrl+\\ as the exit key\n // SGR mouse wheel: \\x1b[<64;col;rowM = scroll up, \\x1b[<65;col;rowM = scroll down\n const SCROLL_UP_RE = /\\x1b\\[<64;\\d+;\\d+M/\n const SCROLL_DOWN_RE = /\\x1b\\[<65;\\d+;\\d+M/\n // Use mouse wheel events directly \u2014 forward as SGR mouse to the PTY\n // so the inner app can handle them natively\n\n if (process.stdin.isTTY) {\n process.stdin.setRawMode(true)\n process.stdin.resume()\n process.stdin.on('data', (data) => {\n const str = data.toString()\n // Ctrl+\\ (0x1c) = exit\n if (str === '\\x1c') {\n shutdown()\n return\n }\n // Drop mouse wheel events \u2014 Claude Code doesn't support mouse scrolling.\n // Scrolling is handled by the terminal emulator natively when not in\n // alternate screen, or not at all in alternate screen (Claude Code's TUI).\n if (SCROLL_UP_RE.test(str) || SCROLL_DOWN_RE.test(str)) {\n return\n }\n orchestrator.writePty(HEADLESS_TAB, str)\n })\n }\n\n // Handle terminal resize\n process.stdout.on('resize', () => {\n const cols = process.stdout.columns || 80\n const rows = process.stdout.rows || 24\n tui.resize(cols, rows)\n const size = tui.getPtySize()\n orchestrator.resizePty(HEADLESS_TAB, size.cols, size.rows)\n })\n\n // Graceful shutdown\n const shutdown = async () => {\n if (process.stdin.isTTY) {\n process.stdin.setRawMode(false)\n }\n tui.destroy()\n await orchestrator.shutdown()\n process.exit(0)\n }\n\n process.on('SIGINT', shutdown)\n process.on('SIGTERM', shutdown)\n}\n\nmain().catch((err) => {\n process.stdout.write('\\x1b[?1049l')\n console.error('Fatal error:', err)\n process.exit(1)\n})\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["const fs = require('fs');\nconst path = require('path');\n\nconst pathFile = path.join(__dirname, 'path.txt');\n\nfunction getElectronPath () {\n let executablePath;\n if (fs.existsSync(pathFile)) {\n executablePath = fs.readFileSync(pathFile, 'utf-8');\n }\n if (process.env.ELECTRON_OVERRIDE_DIST_PATH) {\n return path.join(process.env.ELECTRON_OVERRIDE_DIST_PATH, executablePath || 'electron');\n }\n if (executablePath) {\n return path.join(__dirname, 'dist', executablePath);\n } else {\n throw new Error('Electron failed to install correctly, please delete node_modules/electron and try installing again');\n }\n}\n\nmodule.exports = getElectronPath();\n", "import path from 'path'\nimport fs from 'fs'\nimport os from 'os'\n\nimport { PtyManager } from './pty'\nimport { AgentConfig, isCodingAgent } from './agents'\nimport { CtlsurfApi } from './ctlsurfApi'\nimport { ConversationBridge } from './bridge'\nimport { WorkerWsClient, type WorkerWsStatus, type IncomingMessage } from './workerWs'\nimport { TimeTracker } from './timeTracker'\n\nfunction log(...args: unknown[]): void {\n try { console.log(...args) } catch { /* EPIPE safe */ }\n}\n\n// \u2500\u2500\u2500 Types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface Profile {\n name: string\n apiKey: string\n baseUrl: string\n dataspacePageId: string\n trackTime?: boolean\n idleTimeoutMin?: number\n}\n\nconst DEFAULT_IDLE_TIMEOUT_MIN = 15\n\nexport interface SettingsData {\n activeProfile: string\n profiles: Record<string, Profile>\n ctlsurfApiKey?: string\n ctlsurfBaseUrl?: string\n ctlsurfDataspacePageId?: string\n}\n\nexport interface OrchestratorEvents {\n onPtyData: (tabId: string, data: string) => void\n onPtyExit: (tabId: string, code: number) => void\n onWorkerStatus: (status: string) => void\n onWorkerMessage: (message: IncomingMessage) => void\n onWorkerRegistered: (data: { worker_id: string; folder_id: string | null; status: string }) => void\n onCwdChanged: () => void\n}\n\ninterface TabState {\n ptyManager: PtyManager\n agent: AgentConfig\n cwd: string\n termStreamBuffer: string\n termStreamTimer: ReturnType<typeof setTimeout> | null\n}\n\n// \u2500\u2500\u2500 Orchestrator \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst DEFAULT_PROFILES: Record<string, Profile> = {\n production: {\n name: 'Production',\n apiKey: '',\n baseUrl: 'https://app.ctlsurf.com',\n dataspacePageId: '',\n trackTime: true,\n idleTimeoutMin: 15,\n },\n}\n\nconst TERM_STREAM_INTERVAL_MS = 50\n\nexport class Orchestrator {\n private settingsDir: string\n private events: OrchestratorEvents\n\n // Core services\n readonly ctlsurfApi = new CtlsurfApi()\n readonly bridge = new ConversationBridge()\n readonly workerWs: WorkerWsClient\n readonly timeTracker = new TimeTracker(this.ctlsurfApi)\n\n // State\n private tabs = new Map<string, TabState>()\n private activeTabId: string | null = null\n private currentAgent: AgentConfig | null = null\n private currentCwd: string | null = null\n private settings: SettingsData = {\n activeProfile: 'production',\n profiles: { ...DEFAULT_PROFILES },\n }\n\n constructor(settingsDir: string, events: OrchestratorEvents) {\n this.settingsDir = settingsDir\n this.events = events\n\n this.workerWs = new WorkerWsClient({\n onStatusChange: (status: WorkerWsStatus) => {\n log(`[worker-ws] Status: ${status}`)\n events.onWorkerStatus(status)\n },\n onMessage: (message: IncomingMessage) => {\n log(`[worker-ws] Incoming message: ${message.id} (${message.type})`)\n events.onWorkerMessage(message)\n this.workerWs.sendAck(message.id)\n\n if (message.type === 'prompt' || message.type === 'task_dispatch') {\n const activeTab = this.activeTabId ? this.tabs.get(this.activeTabId) : null\n if (activeTab) {\n activeTab.ptyManager.write(message.content + '\\r')\n this.bridge.feedInput(message.content)\n }\n }\n },\n onRegistered: (data) => {\n log(`[worker-ws] Registered: worker_id=${data.worker_id}, folder_id=${data.folder_id}, status=${data.status}`)\n events.onWorkerRegistered(data)\n if (!data.folder_id) {\n events.onWorkerStatus('no_project')\n }\n },\n onTerminalInput: (data: string) => {\n const activeTab = this.activeTabId ? this.tabs.get(this.activeTabId) : null\n activeTab?.ptyManager.write(data)\n },\n })\n\n this.bridge.setWsClient(this.workerWs)\n }\n\n // \u2500\u2500\u2500 Settings \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n getActiveProfile(): Profile {\n return this.settings.profiles[this.settings.activeProfile] || this.settings.profiles.production || DEFAULT_PROFILES.production\n }\n\n get settingsData(): SettingsData {\n return this.settings\n }\n\n get cwd(): string | null {\n return this.currentCwd\n }\n\n get agent(): AgentConfig | null {\n return this.currentAgent\n }\n\n applyProfile(profile: Profile): void {\n const apiKey = profile.apiKey || process.env.CTLSURF_API_KEY || ''\n if (apiKey) {\n this.ctlsurfApi.setApiKey(apiKey)\n this.workerWs.setApiKey(apiKey)\n } else {\n this.ctlsurfApi.setApiKey('')\n this.workerWs.setApiKey(null)\n }\n\n const baseUrl = profile.baseUrl || process.env.CTLSURF_BASE_URL || 'https://app.ctlsurf.com'\n this.ctlsurfApi.setBaseUrl(baseUrl)\n this.workerWs.setBaseUrl(baseUrl)\n\n log(`[settings] Profile applied: ${profile.name} (${baseUrl})`)\n }\n\n loadSettings(): void {\n // Ensure settings dir exists\n try { fs.mkdirSync(this.settingsDir, { recursive: true }) } catch { /* ignore */ }\n\n const settingsPath = path.join(this.settingsDir, 'settings.json')\n try {\n if (fs.existsSync(settingsPath)) {\n const raw = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'))\n\n if (!raw.profiles) {\n this.settings = {\n activeProfile: 'production',\n profiles: {\n production: {\n name: 'Production',\n apiKey: raw.ctlsurfApiKey || '',\n baseUrl: raw.ctlsurfBaseUrl || 'https://app.ctlsurf.com',\n dataspacePageId: raw.ctlsurfDataspacePageId || '',\n },\n },\n }\n this.saveSettings()\n log('[settings] Migrated legacy settings to profiles')\n } else {\n this.settings = raw as SettingsData\n if (!this.settings.profiles.production) {\n this.settings.profiles.production = { ...DEFAULT_PROFILES.production }\n }\n }\n }\n } catch {\n this.settings = {\n activeProfile: 'production',\n profiles: { ...DEFAULT_PROFILES },\n }\n }\n\n this.applyProfile(this.getActiveProfile())\n }\n\n saveSettings(): void {\n const settingsPath = path.join(this.settingsDir, 'settings.json')\n try {\n fs.mkdirSync(this.settingsDir, { recursive: true })\n fs.writeFileSync(settingsPath, JSON.stringify(this.settings, null, 2))\n } catch (err: any) {\n log('[settings] Failed to save:', err.message)\n }\n }\n\n overrideApiKey(key: string): void {\n this.ctlsurfApi.setApiKey(key)\n this.workerWs.setApiKey(key)\n }\n\n overrideBaseUrl(url: string): void {\n this.ctlsurfApi.setBaseUrl(url)\n this.workerWs.setBaseUrl(url)\n }\n\n // \u2500\u2500\u2500 Profile CRUD \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n listProfiles() {\n return {\n activeProfile: this.settings.activeProfile,\n profiles: Object.entries(this.settings.profiles).map(([id, p]) => ({\n id,\n name: p.name,\n baseUrl: p.baseUrl,\n hasApiKey: !!p.apiKey,\n dataspacePageId: p.dataspacePageId || null,\n trackTime: p.trackTime !== false,\n idleTimeoutMin: p.idleTimeoutMin ?? DEFAULT_IDLE_TIMEOUT_MIN,\n })),\n }\n }\n\n getProfile(profileId: string) {\n const p = this.settings.profiles[profileId]\n if (!p) return null\n return {\n id: profileId,\n name: p.name,\n baseUrl: p.baseUrl,\n hasApiKey: !!p.apiKey,\n dataspacePageId: p.dataspacePageId || '',\n trackTime: p.trackTime !== false,\n idleTimeoutMin: p.idleTimeoutMin ?? DEFAULT_IDLE_TIMEOUT_MIN,\n }\n }\n\n saveProfile(profileId: string, data: {\n name: string\n apiKey?: string\n baseUrl: string\n dataspacePageId: string\n trackTime?: boolean\n idleTimeoutMin?: number\n }) {\n const existing = this.settings.profiles[profileId]\n this.settings.profiles[profileId] = {\n name: data.name,\n apiKey: data.apiKey !== undefined ? data.apiKey : (existing?.apiKey || ''),\n baseUrl: data.baseUrl || 'https://app.ctlsurf.com',\n dataspacePageId: data.dataspacePageId || '',\n trackTime: data.trackTime !== undefined ? data.trackTime : (existing?.trackTime !== false),\n idleTimeoutMin: data.idleTimeoutMin !== undefined ? data.idleTimeoutMin : (existing?.idleTimeoutMin ?? DEFAULT_IDLE_TIMEOUT_MIN),\n }\n this.saveSettings()\n\n if (profileId === this.settings.activeProfile) {\n this.applyProfile(this.settings.profiles[profileId])\n if (this.currentAgent && this.currentCwd) {\n this.workerWs.disconnect()\n this.connectWorkerWs(this.currentAgent, this.currentCwd)\n }\n }\n }\n\n switchProfile(profileId: string): { ok: boolean; error?: string } {\n if (!this.settings.profiles[profileId]) return { ok: false, error: 'Profile not found' }\n this.workerWs.disconnect()\n this.settings.activeProfile = profileId\n this.saveSettings()\n this.applyProfile(this.getActiveProfile())\n if (this.currentAgent && this.currentCwd) {\n this.connectWorkerWs(this.currentAgent, this.currentCwd)\n }\n return { ok: true }\n }\n\n deleteProfile(profileId: string): { ok: boolean; error?: string } {\n if (profileId === 'production') return { ok: false, error: 'Cannot delete Production profile' }\n if (!this.settings.profiles[profileId]) return { ok: false, error: 'Profile not found' }\n\n if (this.settings.activeProfile === profileId) {\n this.workerWs.disconnect()\n this.settings.activeProfile = 'production'\n this.applyProfile(this.getActiveProfile())\n if (this.currentAgent && this.currentCwd) {\n this.connectWorkerWs(this.currentAgent, this.currentCwd)\n }\n }\n\n delete this.settings.profiles[profileId]\n this.saveSettings()\n return { ok: true }\n }\n\n // \u2500\u2500\u2500 PTY & Agent (multi-tab) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async spawnAgent(tabId: string, agent: AgentConfig, cwd: string, opts?: { trackTime?: boolean }): Promise<void> {\n // Kill existing PTY on this tab if any\n const existing = this.tabs.get(tabId)\n if (existing) {\n if (existing.termStreamTimer) clearTimeout(existing.termStreamTimer)\n existing.ptyManager.kill()\n this.tabs.delete(tabId)\n }\n\n this.currentAgent = agent\n const prevCwd = this.currentCwd\n this.currentCwd = cwd\n this.activeTabId = tabId\n if (prevCwd !== cwd) {\n this.events.onCwdChanged()\n }\n\n const ptyManager = new PtyManager(agent, cwd)\n const tab: TabState = { ptyManager, agent, cwd, termStreamBuffer: '', termStreamTimer: null }\n this.tabs.set(tabId, tab)\n\n ptyManager.onData((data: string) => {\n this.events.onPtyData(tabId, data)\n this.timeTracker.recordActivity(tabId)\n if (tabId === this.activeTabId) {\n this.bridge.feedOutput(data)\n this.streamTerminalData(tabId, data)\n }\n })\n\n ptyManager.onExit(async (exitCode: number) => {\n this.events.onPtyExit(tabId, exitCode)\n await this.timeTracker.endSession(tabId)\n if (tabId === this.activeTabId) {\n this.bridge.endSession()\n if (this.currentAgent && isCodingAgent(this.currentAgent)) {\n this.workerWs.disconnect()\n }\n }\n // Clean up tab state\n const t = this.tabs.get(tabId)\n if (t?.termStreamTimer) clearTimeout(t.termStreamTimer)\n })\n\n this.bridge.startSession()\n\n const profile = this.getActiveProfile()\n const shouldTrack = opts?.trackTime !== undefined ? opts.trackTime : (profile.trackTime !== false)\n if (shouldTrack) {\n void this.timeTracker.startSession(\n tabId,\n cwd,\n agent.name,\n profile.idleTimeoutMin ?? DEFAULT_IDLE_TIMEOUT_MIN,\n )\n }\n\n if (isCodingAgent(agent)) {\n this.connectWorkerWs(agent, cwd)\n } else {\n this.workerWs.disconnect()\n this.checkProjectStatus(cwd)\n }\n }\n\n writePty(tabId: string, data: string): void {\n this.tabs.get(tabId)?.ptyManager.write(data)\n if (tabId === this.activeTabId) {\n this.bridge.feedInput(data)\n }\n }\n\n resizePty(tabId: string, cols: number, rows: number): void {\n this.tabs.get(tabId)?.ptyManager.resize(cols, rows)\n if (tabId === this.activeTabId) {\n this.bridge.resize(cols, rows)\n this.workerWs.sendTerminalResize(cols, rows)\n }\n }\n\n async killTab(tabId: string): Promise<void> {\n const tab = this.tabs.get(tabId)\n if (!tab) return\n if (tab.termStreamTimer) clearTimeout(tab.termStreamTimer)\n await this.timeTracker.endSession(tabId)\n tab.ptyManager.kill()\n this.tabs.delete(tabId)\n if (tabId === this.activeTabId) {\n this.bridge.endSession()\n if (isCodingAgent(tab.agent)) {\n this.workerWs.disconnect()\n }\n // Switch active to another tab if available\n const remaining = [...this.tabs.keys()]\n this.activeTabId = remaining.length > 0 ? remaining[remaining.length - 1] : null\n }\n }\n\n setActiveTab(tabId: string): void {\n this.activeTabId = tabId\n const tab = this.tabs.get(tabId)\n if (tab) {\n this.currentAgent = tab.agent\n this.currentCwd = tab.cwd\n }\n }\n\n getTabIds(): string[] {\n return [...this.tabs.keys()]\n }\n\n // \u2500\u2500\u2500 Tracking control (active tab) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n isActiveTabTracking(): boolean {\n if (!this.activeTabId) return false\n return this.timeTracker.isTracking(this.activeTabId)\n }\n\n async setActiveTabTracking(enabled: boolean): Promise<void> {\n if (!this.activeTabId) return\n const tab = this.tabs.get(this.activeTabId)\n if (!tab) return\n if (enabled) {\n if (this.timeTracker.isTracking(this.activeTabId)) return\n const profile = this.getActiveProfile()\n await this.timeTracker.startSession(\n this.activeTabId,\n tab.cwd,\n tab.agent.name,\n profile.idleTimeoutMin ?? DEFAULT_IDLE_TIMEOUT_MIN,\n )\n } else {\n await this.timeTracker.endSession(this.activeTabId)\n }\n }\n\n // \u2500\u2500\u2500 Worker WebSocket \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n connectWorkerWs(agent: AgentConfig, cwd: string): void {\n const profile = this.getActiveProfile()\n const apiKey = profile.apiKey || process.env.CTLSURF_API_KEY\n if (!apiKey) {\n log('[worker-ws] No API key, skipping WS connect')\n return\n }\n\n this.workerWs.connect({\n machine: os.hostname(),\n cwd,\n agent: agent.name,\n })\n }\n\n private async checkProjectStatus(cwd: string): Promise<void> {\n if (!this.ctlsurfApi.getApiKey()) {\n this.events.onWorkerStatus('no_project')\n return\n }\n try {\n const folder = await this.ctlsurfApi.findFolderByPath(cwd)\n if (!folder?.id) {\n this.events.onWorkerStatus('no_project')\n }\n } catch {\n this.events.onWorkerStatus('no_project')\n }\n }\n\n private streamTerminalData(tabId: string, data: string): void {\n const tab = this.tabs.get(tabId)\n if (!tab) return\n tab.termStreamBuffer += data\n if (!tab.termStreamTimer) {\n tab.termStreamTimer = setTimeout(() => {\n if (tab.termStreamBuffer) {\n this.workerWs.sendTerminalData(tab.termStreamBuffer)\n tab.termStreamBuffer = ''\n }\n tab.termStreamTimer = null\n }, TERM_STREAM_INTERVAL_MS)\n }\n }\n\n // \u2500\u2500\u2500 Shutdown \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async shutdown(): Promise<void> {\n this.bridge.endSession()\n await this.timeTracker.endAll()\n for (const [, tab] of this.tabs) {\n if (tab.termStreamTimer) clearTimeout(tab.termStreamTimer)\n tab.ptyManager.kill()\n }\n this.tabs.clear()\n this.workerWs.disconnect()\n }\n}\n", "import { createRequire } from 'module'\nimport { AgentConfig } from './agents'\n\n// Use createRequire to load native module at runtime, bypassing bundler\nconst require = createRequire(import.meta.url)\nconst pty = require('node-pty')\n\nexport class PtyManager {\n private process: any | null = null\n private dataCallbacks: ((data: string) => void)[] = []\n private exitCallbacks: ((code: number) => void)[] = []\n\n constructor(agent: AgentConfig, cwd: string) {\n const shell = agent.command\n const args = agent.args || []\n\n try {\n console.log(`[pty] Spawning: ${shell} ${args.join(' ')} in ${cwd}`)\n } catch {\n // Ignore EPIPE errors when stdout is closed\n }\n\n this.process = pty.spawn(shell, args, {\n name: 'xterm-256color',\n cwd,\n env: process.env as Record<string, string>,\n cols: 80,\n rows: 24\n })\n\n this.process.onData((data: string) => {\n for (const cb of this.dataCallbacks) {\n cb(data)\n }\n })\n\n this.process.onExit(({ exitCode }: { exitCode: number }) => {\n for (const cb of this.exitCallbacks) {\n cb(exitCode)\n }\n this.process = null\n })\n }\n\n write(data: string): void {\n this.process?.write(data)\n }\n\n resize(cols: number, rows: number): void {\n this.process?.resize(cols, rows)\n }\n\n kill(): void {\n this.process?.kill()\n this.process = null\n }\n\n onData(cb: (data: string) => void): void {\n this.dataCallbacks.push(cb)\n }\n\n onExit(cb: (code: number) => void): void {\n this.exitCallbacks.push(cb)\n }\n}\n", "export interface AgentConfig {\n id: string\n name: string\n command: string\n args: string[]\n description: string\n}\n\nfunction getShellCommand(): string {\n if (process.platform === 'win32') return 'powershell.exe'\n return process.env.SHELL || '/bin/zsh'\n}\n\nexport function getBuiltinAgents(): AgentConfig[] {\n return [\n {\n id: 'shell',\n name: 'Shell',\n command: getShellCommand(),\n args: ['-l'], // login shell to load PATH\n description: 'Default system shell'\n },\n {\n id: 'claude',\n name: 'Claude Code',\n command: 'claude',\n args: [],\n description: 'Anthropic Claude Code CLI'\n },\n {\n id: 'codex',\n name: 'Codex CLI',\n command: 'codex',\n args: [],\n description: 'OpenAI Codex CLI'\n }\n ]\n}\n\nexport function getDefaultAgent(): AgentConfig {\n return getBuiltinAgents()[0]\n}\n\nexport function isCodingAgent(agent: AgentConfig): boolean {\n return agent.id !== 'shell'\n}\n", "const CTLSURF_BASE_URL = 'https://app.ctlsurf.com/api'\n\nexport class CtlsurfApi {\n private baseUrl: string\n private apiKey: string | null = null\n\n constructor(baseUrl?: string) {\n this.baseUrl = baseUrl || CTLSURF_BASE_URL\n }\n\n setApiKey(key: string): void {\n this.apiKey = key\n }\n\n setBaseUrl(url: string): void {\n this.baseUrl = url.endsWith('/api') ? url : `${url}/api`\n }\n\n getApiKey(): string | null {\n return this.apiKey\n }\n\n private headers(): Record<string, string> {\n const h: Record<string, string> = { 'Content-Type': 'application/json' }\n if (this.apiKey) {\n h['Authorization'] = `Bearer ${this.apiKey}`\n }\n return h\n }\n\n private async request(method: string, path: string, body?: unknown): Promise<any> {\n const url = `${this.baseUrl}${path}`\n const opts: RequestInit = {\n method,\n headers: this.headers()\n }\n if (body) {\n opts.body = JSON.stringify(body)\n }\n\n const res = await fetch(url, opts)\n if (!res.ok) {\n const text = await res.text()\n throw new Error(`ctlsurf API ${method} ${path}: ${res.status} ${text}`)\n }\n return res.json()\n }\n\n // \u2500\u2500\u2500 Pages \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async createPage(params: {\n title: string\n type?: string\n parent_id?: string\n folder_id?: string\n cwd?: string\n tags?: string[]\n }): Promise<any> {\n return this.request('POST', '/pages', params)\n }\n\n async findPageByRootPath(rootPath: string): Promise<any> {\n return this.request('POST', '/pages/find-by-root-path', { root_path: rootPath })\n }\n\n // \u2500\u2500\u2500 Blocks \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async createBlock(pageId: string, params: {\n type: string\n title?: string\n props?: Record<string, unknown>\n }): Promise<any> {\n return this.request('POST', `/blocks/page/${pageId}`, params)\n }\n\n async getBlock(blockId: string): Promise<any> {\n return this.request('GET', `/blocks/${blockId}`)\n }\n\n async updateBlock(blockId: string, params: {\n props?: Record<string, unknown>\n title?: string\n }): Promise<any> {\n return this.request('PUT', `/blocks/${blockId}`, params)\n }\n\n // \u2500\u2500\u2500 Folders \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async createFolder(params: { name: string; root_path: string }): Promise<any> {\n return this.request('POST', '/folders', params)\n }\n\n // \u2500\u2500\u2500 Workers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async getAuthCode(): Promise<{ code: string }> {\n return this.request('POST', '/workers/token-exchange')\n }\n\n async findFolderByPath(rootPath: string): Promise<any> {\n return this.request('POST', '/folders/find-by-path', { root_path: rootPath })\n }\n\n async getFolderPages(folderId: string): Promise<any[]> {\n const folder = await this.request('GET', `/folders/${folderId}`)\n return folder?.pages || []\n }\n\n async getFolder(folderId: string): Promise<any> {\n return this.request('GET', `/folders/${folderId}`)\n }\n\n // \u2500\u2500\u2500 Datastore \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async getPageBlockSummaries(pageId: string): Promise<any[]> {\n return this.request('GET', `/blocks/page/${pageId}/summary`)\n }\n\n async addRow(blockId: string, data: Record<string, unknown>): Promise<any> {\n return this.request('POST', `/datastore/${blockId}/rows`, { data })\n }\n\n async updateRow(blockId: string, rowId: string, data: Record<string, unknown>): Promise<any> {\n return this.request('PUT', `/datastore/${blockId}/rows/${rowId}`, { data })\n }\n\n async getDatastoreSchema(blockId: string): Promise<{ block_id: string; columns: Array<{ id: string; name: string; type: string }> }> {\n return this.request('GET', `/datastore/${blockId}/schema`)\n }\n\n async updateDatastoreSchema(blockId: string, columns: Array<{ id: string; name: string; type: string }>): Promise<any> {\n return this.request('PUT', `/datastore/${blockId}/schema`, { columns })\n }\n\n async findFolderByGitRemote(gitRemote: string): Promise<any> {\n // Search folders by listing all and matching git_remote\n const folders = await this.request('GET', '/folders')\n return folders?.find((f: any) => f.git_remote === gitRemote || f.root_path === gitRemote) || null\n }\n\n // \u2500\u2500\u2500 Log convenience \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async appendLog(blockId: string, action: string, message: string, data?: Record<string, unknown>): Promise<any> {\n // Read-modify-write: get current entries, append, put back\n const block = await this.getBlock(blockId)\n const props = block.props || {}\n const entries = Array.isArray(props.entries) ? [...props.entries] : []\n const maxEntries = props.max_entries || 1000\n\n const entry: Record<string, unknown> = {\n _id: `log_${entries.length}`,\n _timestamp: new Date().toISOString(),\n action,\n message\n }\n if (data) {\n entry.data = data\n }\n\n entries.push(entry)\n\n // Trim oldest if over max\n const trimmed = entries.length > maxEntries ? entries.slice(-maxEntries) : entries\n\n return this.updateBlock(blockId, {\n props: { ...props, entries: trimmed }\n })\n }\n}\n", "import { WorkerWsClient } from './workerWs'\n\n/**\n * Conversation Bridge\n *\n * Captures PTY output, strips ANSI codes and terminal artifacts,\n * and sends cleaned text to the backend via WebSocket for chat logging.\n * Uses a simple buffer + byte threshold approach (no timers, since\n * setTimeout/setInterval don't reliably fire under TUI raw mode).\n */\nexport class ConversationBridge {\n private wsClient: WorkerWsClient | null = null\n private sessionActive: boolean = false\n private inputBuffer: string = ''\n private outputBuffer: string = ''\n\n private readonly FLUSH_BYTES = 500\n\n setWsClient(ws: WorkerWsClient): void {\n this.wsClient = ws\n }\n\n startSession(): void {\n this.outputBuffer = ''\n this.inputBuffer = ''\n this.sessionActive = true\n console.log('[bridge] Session started')\n }\n\n feedOutput(data: string): void {\n if (!this.sessionActive) return\n\n this.outputBuffer += data\n\n if (this.outputBuffer.length >= this.FLUSH_BYTES) {\n this.flush()\n }\n }\n\n feedInput(data: string): void {\n if (!this.sessionActive) return\n this.inputBuffer += data\n\n if (data.includes('\\r') || data.includes('\\n')) {\n const cleaned = cleanInput(this.inputBuffer)\n if (cleaned.length > 0) {\n this.sendEntry('user_input', cleaned)\n }\n this.inputBuffer = ''\n }\n }\n\n resize(_cols: number, _rows: number): void {\n // no-op now (was used for xterm)\n }\n\n private flush(): void {\n if (this.outputBuffer.length === 0) return\n\n const raw = this.outputBuffer\n this.outputBuffer = ''\n\n const cleaned = cleanOutput(raw)\n if (cleaned.length === 0) return\n\n this.sendEntry('terminal_output', cleaned)\n }\n\n private sendEntry(type: string, content: string): void {\n if (!this.wsClient) return\n this.wsClient.sendChatLog({\n ts: new Date().toISOString(),\n type,\n content,\n })\n }\n\n endSession(): void {\n if (!this.sessionActive) return\n\n this.flush()\n\n this.sessionActive = false\n this.inputBuffer = ''\n this.outputBuffer = ''\n console.log('[bridge] Session ended')\n }\n}\n\n/**\n * Strip ANSI escape codes from a string.\n */\nfunction stripAnsi(str: string): string {\n return str\n // CSI sequences (e.g. \\x1b[0m, \\x1b[?2004h, \\x1b[1;32m, \\x1b[38;2;r;g;bm)\n .replace(/\\x1b\\[[\\x30-\\x3f]*[\\x20-\\x2f]*[\\x40-\\x7e]/g, '')\n // OSC sequences (e.g. \\x1b]0;title\\x07, \\x1b]...\\x1b\\\\)\n .replace(/\\x1b\\][^\\x07\\x1b]*(?:\\x07|\\x1b\\\\)/g, '')\n // DCS/PM/APC sequences\n .replace(/\\x1b[PX^_][^\\x1b]*\\x1b\\\\/g, '')\n // Other escape sequences (charset, keypad mode, etc.)\n .replace(/\\x1b[^[\\]PX^_](.|$)/g, '')\n // Remaining single ESC\n .replace(/\\x1b/g, '')\n}\n\n/**\n * Process backspace characters: each \\x7f or \\b deletes the preceding char.\n */\nfunction processBackspaces(str: string): string {\n const result: string[] = []\n for (const ch of str) {\n if (ch === '\\x7f' || ch === '\\b') {\n result.pop()\n } else {\n result.push(ch)\n }\n }\n return result.join('')\n}\n\n/**\n * Clean user input: strip ANSI, process backspaces, remove control chars.\n */\nfunction cleanInput(str: string): string {\n let cleaned = stripAnsi(str)\n cleaned = processBackspaces(cleaned)\n // eslint-disable-next-line no-control-regex\n cleaned = cleaned.replace(/[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]/g, '')\n return cleaned.trim()\n}\n\n/**\n * Clean terminal output: strip ANSI, remove control chars, collapse noise.\n */\nfunction cleanOutput(str: string): string {\n let cleaned = stripAnsi(str)\n // Remove carriage returns\n cleaned = cleaned.replace(/\\r/g, '')\n // Remove control characters except newline/tab\n // eslint-disable-next-line no-control-regex\n cleaned = cleaned.replace(/[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]/g, '')\n // Collapse 3+ consecutive newlines into 2\n cleaned = cleaned.replace(/\\n{3,}/g, '\\n\\n')\n // Remove lines that are only whitespace\n cleaned = cleaned.split('\\n').filter(line => line.trim().length > 0).join('\\n')\n return cleaned.trim()\n}\n", "import os from 'os'\nimport crypto from 'crypto'\nimport WsModule from 'ws'\n\n// Use native WebSocket if available (Node 22+), otherwise fall back to ws package\nconst WS: typeof WebSocket = typeof WebSocket !== 'undefined' ? WebSocket : WsModule as any\n\nfunction log(...args: unknown[]): void {\n try { console.log(...args) } catch { /* EPIPE safe */ }\n}\n\nconst HEARTBEAT_INTERVAL_MS = 30_000\nconst RECONNECT_DELAY_MS = 5_000\nconst MAX_RECONNECT_DELAY_MS = 60_000\n\nexport interface WorkerRegistration {\n machine: string\n cwd: string\n repo_url?: string\n agent: string\n fingerprint: string\n}\n\nexport interface WorkerWsEvents {\n onStatusChange: (status: WorkerWsStatus) => void\n onMessage: (message: IncomingMessage) => void\n onRegistered: (data: { worker_id: string; folder_id: string | null; status: string; pending_messages?: IncomingMessage[] }) => void\n onTerminalInput?: (data: string) => void\n}\n\nexport interface IncomingMessage {\n id: string\n type: string\n content: string\n metadata?: Record<string, unknown> | null\n parent_id?: string | null\n}\n\nexport type WorkerWsStatus = 'disconnected' | 'connecting' | 'connected' | 'pending_approval'\n\nexport class WorkerWsClient {\n private ws: WebSocket | null = null\n private apiKey: string | null = null\n private baseUrl: string\n private events: WorkerWsEvents\n private heartbeatTimer: ReturnType<typeof setInterval> | null = null\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null\n private reconnectDelay = RECONNECT_DELAY_MS\n private registration: WorkerRegistration | null = null\n private workerId: string | null = null\n private _status: WorkerWsStatus = 'disconnected'\n private shouldReconnect = false\n private fingerprint: string\n\n constructor(events: WorkerWsEvents, baseUrl?: string) {\n this.events = events\n this.baseUrl = baseUrl || 'wss://app.ctlsurf.com'\n // Generate a stable machine fingerprint\n this.fingerprint = this.generateFingerprint()\n }\n\n get status(): WorkerWsStatus {\n return this._status\n }\n\n get currentWorkerId(): string | null {\n return this.workerId\n }\n\n setApiKey(key: string | null): void {\n this.apiKey = key\n }\n\n setBaseUrl(url: string): void {\n this.baseUrl = url\n }\n\n private generateFingerprint(): string {\n const data = `${os.hostname()}:${os.userInfo().username}:${os.platform()}:${os.arch()}`\n return crypto.createHash('sha256').update(data).digest('hex').slice(0, 32)\n }\n\n private setStatus(status: WorkerWsStatus): void {\n if (this._status !== status) {\n this._status = status\n this.events.onStatusChange(status)\n }\n }\n\n connect(registration: WorkerRegistration): void {\n this.registration = { ...registration, fingerprint: this.fingerprint }\n this.shouldReconnect = true\n this.doConnect()\n }\n\n disconnect(): void {\n this.shouldReconnect = false\n this.clearTimers()\n if (this.ws) {\n const oldWs = this.ws\n this.ws = null\n // Remove handlers before closing to prevent stale onclose from firing\n oldWs.onopen = null\n oldWs.onmessage = null\n oldWs.onclose = null\n oldWs.onerror = null\n try { oldWs.close(1000, 'client disconnect') } catch { /* ignore */ }\n }\n this.setStatus('disconnected')\n }\n\n sendResponse(parentId: string, content: string, metadata?: Record<string, unknown>): void {\n this.send({\n type: 'response',\n parent_id: parentId,\n content,\n metadata,\n })\n }\n\n sendStatusUpdate(status: string): void {\n this.send({ type: 'status_update', status })\n }\n\n sendAck(messageId: string): void {\n this.send({ type: 'ack', message_id: messageId })\n }\n\n sendTerminalData(data: string): void {\n this.send({ type: 'terminal_stream', data })\n }\n\n sendTerminalResize(cols: number, rows: number): void {\n this.send({ type: 'terminal_resize', cols, rows })\n }\n\n sendChatLog(entry: { type: string; content: string; ts?: string }): void {\n this.send({ type: 'chat_log', entry })\n }\n\n private doConnect(): void {\n if (!this.apiKey || !this.registration) {\n log('[worker-ws] No API key or registration, skipping connect')\n return\n }\n\n this.clearTimers()\n if (this.ws) {\n const oldWs = this.ws\n this.ws = null\n oldWs.onopen = null\n oldWs.onmessage = null\n oldWs.onclose = null\n oldWs.onerror = null\n try { oldWs.close() } catch { /* ignore */ }\n // Let the old connection fully close before opening a new one\n setTimeout(() => this.doConnectNow(), 500)\n return\n }\n\n this.doConnectNow()\n }\n\n private doConnectNow(): void {\n if (!this.apiKey || !this.registration) return\n if (!this.shouldReconnect) {\n log('[worker-ws] shouldReconnect is false, aborting connect')\n return\n }\n\n this.setStatus('connecting')\n\n // Use ws:// for localhost, wss:// for remote\n const wsBase = this.baseUrl.replace(/^http/, 'ws')\n const url = `${wsBase}/api/ws/worker?token=${encodeURIComponent(this.apiKey)}`\n\n log(`[worker-ws] Connecting to ${url.replace(/token=.*/, 'token=***')}...`)\n\n try {\n this.ws = new WS(url) as unknown as WebSocket\n } catch (err) {\n log('[worker-ws] Failed to create WebSocket:', err)\n this.scheduleReconnect()\n return\n }\n\n this.ws.onopen = () => {\n log('[worker-ws] Connected, sending register')\n this.reconnectDelay = RECONNECT_DELAY_MS // Reset backoff\n this.send({\n type: 'register',\n ...this.registration,\n })\n this.startHeartbeat()\n }\n\n this.ws.onmessage = (event) => {\n try {\n const data = JSON.parse(String(event.data))\n this.handleMessage(data)\n } catch (err) {\n log('[worker-ws] Failed to parse message:', err)\n }\n }\n\n this.ws.onclose = (event) => {\n log(`[worker-ws] Disconnected: ${event.code} ${event.reason}`)\n this.ws = null\n this.clearHeartbeat()\n this.setStatus('disconnected')\n if (this.shouldReconnect) {\n this.scheduleReconnect()\n }\n }\n\n this.ws.onerror = () => {\n log('[worker-ws] WebSocket error')\n }\n }\n\n private handleMessage(data: Record<string, unknown>): void {\n const msgType = data.type as string\n\n switch (msgType) {\n case 'registered': {\n this.workerId = data.worker_id as string\n const workerStatus = data.status as string\n console.log(`[worker-ws] Registered as ${this.workerId}, status: ${workerStatus}`)\n\n if (workerStatus === 'pending_approval') {\n this.setStatus('pending_approval')\n } else {\n this.setStatus('connected')\n }\n\n const pendingMessages = (data.pending_messages || []) as IncomingMessage[]\n this.events.onRegistered({\n worker_id: this.workerId,\n folder_id: data.folder_id as string | null,\n status: workerStatus,\n pending_messages: pendingMessages,\n })\n\n // Deliver pending messages\n for (const msg of pendingMessages) {\n this.events.onMessage(msg)\n }\n break\n }\n\n case 'approved': {\n log('[worker-ws] Worker approved!')\n this.setStatus('connected')\n break\n }\n\n case 'message': {\n const msg = data.message as IncomingMessage\n if (msg) {\n console.log(`[worker-ws] Received message: ${msg.id}`)\n this.events.onMessage(msg)\n }\n break\n }\n\n case 'terminal_input': {\n const inputData = data.data as string\n if (inputData && this.events.onTerminalInput) {\n this.events.onTerminalInput(inputData)\n }\n break\n }\n\n case 'heartbeat_ack':\n break\n\n default:\n console.log(`[worker-ws] Unknown message type: ${msgType}`)\n }\n }\n\n private send(data: Record<string, unknown>): void {\n if (this.ws && this.ws.readyState === WS.OPEN) {\n this.ws.send(JSON.stringify(data))\n }\n }\n\n\n private startHeartbeat(): void {\n this.clearHeartbeat()\n this.heartbeatTimer = setInterval(() => {\n this.send({ type: 'heartbeat' })\n }, HEARTBEAT_INTERVAL_MS)\n }\n\n private clearHeartbeat(): void {\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer)\n this.heartbeatTimer = null\n }\n }\n\n private scheduleReconnect(): void {\n if (!this.shouldReconnect) return\n console.log(`[worker-ws] Reconnecting in ${this.reconnectDelay / 1000}s...`)\n this.reconnectTimer = setTimeout(() => {\n this.doConnect()\n }, this.reconnectDelay)\n // Exponential backoff\n this.reconnectDelay = Math.min(this.reconnectDelay * 2, MAX_RECONNECT_DELAY_MS)\n }\n\n private clearTimers(): void {\n this.clearHeartbeat()\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer)\n this.reconnectTimer = null\n }\n }\n}\n", "import os from 'os'\nimport { randomUUID } from 'crypto'\nimport type { CtlsurfApi } from './ctlsurfApi'\n\nconst DATASTORE_TITLE = 'Time Tracking'\nconst AGENT_DATASTORE_PAGE_TITLE = 'Agent Datastore'\nconst FIRST_CHECKPOINT_DELAY_MS = 30 * 1000\nconst CHECKPOINT_INTERVAL_MS = 5 * 60 * 1000\n\nconst COLUMNS: Array<{ name: string; type: string }> = [\n { name: 'Started', type: 'text' },\n { name: 'Active Time', type: 'number' },\n { name: 'Agent', type: 'text' },\n { name: 'Worker', type: 'text' },\n { name: 'Session', type: 'text' },\n { name: 'Notes', type: 'text' },\n]\n\nfunction formatStarted(ms: number): string {\n return new Date(ms).toLocaleString('en-US', {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n hour: 'numeric',\n minute: '2-digit',\n hour12: true,\n })\n}\n\ninterface SessionState {\n blockId: string\n rowId: string\n cwd: string\n startedAt: number\n lastActivity: number\n activeMs: number\n idleTimeoutMs: number\n firstCheckpointTimer: ReturnType<typeof setTimeout> | null\n checkpointTimer: ReturnType<typeof setInterval> | null\n ended: boolean\n}\n\nfunction log(...args: unknown[]): void {\n try { console.log('[time-tracker]', ...args) } catch { /* EPIPE safe */ }\n}\n\nfunction findPageByTitle(pages: any[], title: string): any | null {\n for (const p of pages) {\n if (p?.title === title) return p\n if (p?.children?.length) {\n const c = findPageByTitle(p.children, title)\n if (c) return c\n }\n }\n return null\n}\n\nexport class TimeTracker {\n private api: CtlsurfApi\n private sessions = new Map<string, SessionState>()\n private blockCache = new Map<string, string>()\n\n constructor(api: CtlsurfApi) {\n this.api = api\n }\n\n async startSession(tabId: string, cwd: string, agentName: string, idleTimeoutMin: number): Promise<void> {\n if (this.sessions.has(tabId)) {\n await this.endSession(tabId)\n }\n try {\n const blockId = await this.ensureDatastore(cwd)\n if (!blockId) {\n log(`No \"${AGENT_DATASTORE_PAGE_TITLE}\" page found for ${cwd} \u2014 tracking disabled for this session`)\n return\n }\n const startedAt = Date.now()\n const sessionUuid = randomUUID()\n const row = await this.api.addRow(blockId, {\n Started: formatStarted(startedAt),\n 'Active Time': 0,\n Agent: agentName,\n Worker: os.hostname(),\n Session: sessionUuid,\n Notes: '',\n })\n const rowId = row?.id\n if (!rowId) {\n log('addRow returned no id; aborting tracking', row)\n return\n }\n const state: SessionState = {\n blockId,\n rowId,\n cwd,\n startedAt,\n lastActivity: startedAt,\n activeMs: 0,\n idleTimeoutMs: Math.max(1, idleTimeoutMin) * 60 * 1000,\n firstCheckpointTimer: null,\n checkpointTimer: null,\n ended: false,\n }\n state.firstCheckpointTimer = setTimeout(() => {\n void this.checkpoint(tabId)\n const live = this.sessions.get(tabId)\n if (live && !live.ended) {\n live.checkpointTimer = setInterval(() => {\n void this.checkpoint(tabId)\n }, CHECKPOINT_INTERVAL_MS)\n }\n }, FIRST_CHECKPOINT_DELAY_MS)\n this.sessions.set(tabId, state)\n log(`Started tracking tab=${tabId} agent=\"${agentName}\" cwd=${cwd}`)\n } catch (err: any) {\n log(`startSession failed: ${err?.message || err}`)\n }\n }\n\n isTracking(tabId: string): boolean {\n const s = this.sessions.get(tabId)\n return !!s && !s.ended\n }\n\n recordActivity(tabId: string): void {\n const s = this.sessions.get(tabId)\n if (!s || s.ended) return\n const now = Date.now()\n const delta = now - s.lastActivity\n if (delta < s.idleTimeoutMs) {\n s.activeMs += delta\n }\n s.lastActivity = now\n }\n\n async endSession(tabId: string): Promise<void> {\n const s = this.sessions.get(tabId)\n if (!s || s.ended) return\n s.ended = true\n if (s.firstCheckpointTimer) clearTimeout(s.firstCheckpointTimer)\n if (s.checkpointTimer) clearInterval(s.checkpointTimer)\n try {\n await this.writeRow(s, Date.now())\n } catch (err: any) {\n log(`endSession write failed: ${err?.message || err}`)\n }\n this.sessions.delete(tabId)\n }\n\n async endAll(): Promise<void> {\n const ids = [...this.sessions.keys()]\n await Promise.all(ids.map(id => this.endSession(id)))\n }\n\n private async checkpoint(tabId: string): Promise<void> {\n const s = this.sessions.get(tabId)\n if (!s || s.ended) return\n try {\n await this.writeRow(s, Date.now())\n } catch (err: any) {\n log(`checkpoint failed: ${err?.message || err}`)\n }\n }\n\n private async writeRow(s: SessionState, _endTimeMs: number): Promise<void> {\n const activeMin = Math.round(s.activeMs / 60000)\n await this.api.updateRow(s.blockId, s.rowId, {\n 'Active Time': activeMin,\n })\n }\n\n private async ensureDatastore(cwd: string): Promise<string | null> {\n const cached = this.blockCache.get(cwd)\n if (cached) return cached\n\n let folder: any = null\n try {\n folder = await this.api.findFolderByPath(cwd)\n } catch {\n return null\n }\n if (!folder?.id) return null\n\n const folderDetail = await this.api.getFolder(folder.id)\n const agentPage = findPageByTitle(folderDetail?.pages || [], AGENT_DATASTORE_PAGE_TITLE)\n if (!agentPage?.id) return null\n\n const summaries = await this.api.getPageBlockSummaries(agentPage.id)\n const existing = (summaries || []).find((b: any) => b?.type === 'datastore' && b?.title === DATASTORE_TITLE)\n if (existing?.id) {\n await this.ensureColumns(existing.id)\n this.blockCache.set(cwd, existing.id)\n return existing.id\n }\n\n const columns = COLUMNS.map((c, i) => ({ id: `col_${i}`, name: c.name, type: c.type }))\n const created = await this.api.createBlock(agentPage.id, {\n type: 'datastore',\n title: DATASTORE_TITLE,\n props: { columns },\n })\n if (created?.id) {\n log(`Created \"${DATASTORE_TITLE}\" datastore on Agent Datastore page for ${cwd}`)\n this.blockCache.set(cwd, created.id)\n return created.id\n }\n return null\n }\n\n private async ensureColumns(blockId: string): Promise<void> {\n try {\n const schema = await this.api.getDatastoreSchema(blockId)\n const existingCols = schema.columns || []\n const existingNames = new Set(existingCols.map(c => c.name))\n const missing = COLUMNS.filter(c => !existingNames.has(c.name))\n if (missing.length === 0) return\n\n const usedIds = new Set(existingCols.map(c => c.id))\n let nextIdx = existingCols.length\n const appended = missing.map(c => {\n let id = `col_${nextIdx++}`\n while (usedIds.has(id)) id = `col_${nextIdx++}`\n usedIds.add(id)\n return { id, name: c.name, type: c.type }\n })\n const merged = [...existingCols, ...appended]\n await this.api.updateDatastoreSchema(blockId, merged)\n log(`Added ${missing.length} missing column(s) to existing Time Tracking datastore: ${missing.map(c => c.name).join(', ')}`)\n } catch (err: any) {\n log(`ensureColumns failed: ${err?.message || err}`)\n }\n }\n}\n", "import path from 'path'\nimport os from 'os'\n\nexport function getSettingsDir(useElectron: boolean): string {\n if (useElectron) {\n const { app } = require('electron')\n return app.getPath('userData')\n }\n\n if (process.platform === 'darwin') {\n return path.join(os.homedir(), 'Library', 'Application Support', 'ctlsurf-worker')\n }\n return path.join(\n process.env.XDG_CONFIG_HOME || path.join(os.homedir(), '.config'),\n 'ctlsurf-worker'\n )\n}\n", "/**\n * Terminal UI (TUI) renderer\n *\n * Minimal status bar at the bottom, full PTY passthrough above.\n * No alternate screen \u2014 the PTY (Claude Code) owns the screen and\n * handles its own scrolling, mouse events, and display management.\n * Status info also goes to the terminal tab title via OSC.\n */\n\nconst ESC = '\\x1b'\nconst CSI = `${ESC}[`\n\n// Colors (Tokyo Night palette)\nconst BG_BAR = `${CSI}48;2;22;22;30m` // #16161e\nconst FG_DIM = `${CSI}38;2;86;95;137m` // #565f89\nconst FG_ACCENT = `${CSI}38;2;122;162;247m` // #7aa2f7\nconst FG_GREEN = `${CSI}38;2;158;206;106m` // #9ece6a\nconst FG_RED = `${CSI}38;2;247;118;142m` // #f7768e\nconst FG_YELLOW = `${CSI}38;2;224;175;104m` // #e0af68\nconst FG_WHITE = `${CSI}38;2;169;177;214m` // #a9b1d6\nconst FG_TITLE = `${CSI}38;2;192;202;245m` // #c0caf5\nconst BG_MODAL = `${CSI}48;2;31;35;53m` // #1f2335\nconst BG_SELECTED = `${CSI}48;2;42;43;61m` // #2a2b3d\nconst RESET = `${CSI}0m`\n\nexport interface TuiState {\n agentName: string\n cwd: string\n wsStatus: string\n workerId: string | null\n mode: string\n}\n\nexport class Tui {\n private rows: number = 0\n private cols: number = 0\n private state: TuiState = {\n agentName: '',\n cwd: '',\n wsStatus: 'disconnected',\n workerId: null,\n mode: 'terminal',\n }\n\n constructor() {\n this.rows = process.stdout.rows || 24\n this.cols = process.stdout.columns || 80\n }\n\n /**\n * Initialize the TUI: scroll region leaving last row for status bar.\n * No alternate screen \u2014 PTY fully owns the display.\n */\n init(): void {\n // Just set tab title \u2014 the PTY agent owns the full screen\n this.updateTerminalTitle()\n }\n\n /**\n * Restore terminal to normal state\n */\n destroy(): void {\n // Show cursor\n this.write(`${CSI}?25h`)\n }\n\n /**\n * Handle terminal resize\n */\n resize(cols: number, rows: number): void {\n this.cols = cols\n this.rows = rows\n }\n\n /**\n * Get the PTY dimensions (all rows minus status bar)\n */\n getPtySize(): { cols: number; rows: number } {\n return {\n cols: this.cols,\n rows: this.rows,\n }\n }\n\n /**\n * Update state and redraw status bar\n */\n update(partial: Partial<TuiState>): void {\n Object.assign(this.state, partial)\n this.updateTerminalTitle()\n }\n\n /**\n * Write PTY output \u2014 full passthrough.\n */\n writePtyData(data: string): void {\n this.write(data)\n }\n\n /**\n * Update the terminal window/tab title via OSC escape sequence.\n */\n setTerminalTitle(title: string): void {\n this.write(`${ESC}]0;${title}\\x07`)\n }\n\n /**\n * Build a title string from current state for the terminal tab.\n */\n updateTerminalTitle(): void {\n const { agentName, wsStatus, cwd } = this.state\n const displayCwd = this.shortenPath(cwd)\n const statusIcon = {\n connected: '\\u25CF',\n connecting: '\\u25CB',\n disconnected: '\\u25CB',\n pending_approval: '\\u25CB',\n no_project: '\\u25CB',\n }[wsStatus] || '\\u25CB'\n const statusLabel = {\n connected: 'Connected',\n connecting: 'Connecting...',\n disconnected: 'Disconnected',\n pending_approval: 'Pending',\n no_project: 'No Project',\n }[wsStatus] || wsStatus\n\n this.setTerminalTitle(`ctlsurf \\u00B7 ${agentName} \\u00B7 ${statusIcon} ${statusLabel} \\u00B7 ${displayCwd}`)\n }\n\n /**\n * Show an interactive agent picker modal.\n * Uses alternate screen just for the picker, then exits back to normal.\n */\n showAgentPicker(\n agents: { name: string; description: string }[],\n options: { initialTrackTime: boolean },\n ): Promise<{ agentIdx: number; trackTime: boolean }> {\n return new Promise((resolve) => {\n let selected = 0\n let trackTime = options.initialTrackTime\n const modalWidth = 44\n // +4 for borders/title/sep, +2 for track-time separator + row\n const modalHeight = agents.length + 4 + 2\n const startCol = Math.max(1, Math.floor((this.cols - modalWidth) / 2))\n const startRow = Math.max(1, Math.floor((this.rows - modalHeight) / 2))\n\n // Enter alternate screen just for the picker\n this.write(`${CSI}?1049h`)\n this.write(`${CSI}?25l`)\n\n const drawModal = () => {\n const topBorder = '\\u250c' + '\\u2500'.repeat(modalWidth - 2) + '\\u2510'\n const botBorder = '\\u2514' + '\\u2500'.repeat(modalWidth - 2) + '\\u2518'\n\n for (let r = 0; r < this.rows; r++) {\n this.write(`${CSI}${r + 1};1H${BG_BAR}${CSI}2K${RESET}`)\n }\n\n const brand = 'ctlsurf'\n const brandCol = Math.max(1, Math.floor((this.cols - brand.length) / 2))\n this.write(`${CSI}${startRow - 2};${brandCol}H${FG_ACCENT}${brand}${RESET}`)\n\n this.write(`${CSI}${startRow};${startCol}H${BG_MODAL}${FG_DIM}${topBorder}${RESET}`)\n\n const title = ' Select Agent'\n const titlePad = ' '.repeat(Math.max(0, modalWidth - 2 - title.length))\n this.write(`${CSI}${startRow + 1};${startCol}H${BG_MODAL}${FG_DIM}\\u2502${RESET}${BG_MODAL}${FG_TITLE}${title}${titlePad}${FG_DIM}\\u2502${RESET}`)\n\n const sep = '\\u251c' + '\\u2500'.repeat(modalWidth - 2) + '\\u2524'\n this.write(`${CSI}${startRow + 2};${startCol}H${BG_MODAL}${FG_DIM}${sep}${RESET}`)\n\n for (let i = 0; i < agents.length; i++) {\n const agent = agents[i]\n const row = startRow + 3 + i\n const isSelected = i === selected\n const bg = isSelected ? BG_SELECTED : BG_MODAL\n const pointer = isSelected ? `${FG_ACCENT}\\u25B8 ` : ' '\n const nameFg = isSelected ? FG_ACCENT : FG_WHITE\n const nameStr = agent.name\n const descStr = agent.description ? ` ${FG_DIM}\\u2014 ${agent.description.slice(0, 20)}` : ''\n const content = `${pointer}${nameFg}${nameStr}${descStr}`\n const contentLen = 2 + nameStr.length + (agent.description ? 3 + Math.min(20, agent.description.length) : 0)\n const pad = ' '.repeat(Math.max(0, modalWidth - 2 - contentLen))\n this.write(`${CSI}${row};${startCol}H${bg}${FG_DIM}\\u2502${RESET}${bg}${content}${pad}${RESET}${BG_MODAL}${FG_DIM}\\u2502${RESET}`)\n }\n\n const innerSep = '\\u251c' + '\\u2500'.repeat(modalWidth - 2) + '\u2524'\n const sepRow = startRow + 3 + agents.length\n this.write(`${CSI}${sepRow};${startCol}H${BG_MODAL}${FG_DIM}${innerSep}${RESET}`)\n\n const trackRow = sepRow + 1\n const checkbox = trackTime ? `${FG_GREEN}[\u2713]${RESET}${BG_MODAL}` : `${FG_DIM}[ ]${RESET}${BG_MODAL}`\n const trackLabelFg = trackTime ? FG_WHITE : FG_DIM\n const trackContent = ` ${checkbox} ${trackLabelFg}Track time${RESET}${BG_MODAL}`\n const trackContentLen = 2 + 3 + 1 + 'Track time'.length\n const trackPad = ' '.repeat(Math.max(0, modalWidth - 2 - trackContentLen))\n this.write(`${CSI}${trackRow};${startCol}H${BG_MODAL}${FG_DIM}\u2502${RESET}${BG_MODAL}${trackContent}${trackPad}${FG_DIM}\u2502${RESET}`)\n\n const botRow = trackRow + 1\n this.write(`${CSI}${botRow};${startCol}H${BG_MODAL}${FG_DIM}${botBorder}${RESET}`)\n\n const hint = '\\u2191\\u2193 navigate \\u00B7 Enter select \\u00B7 t track \\u00B7 q quit'\n const hintCol = Math.max(1, Math.floor((this.cols - hint.length) / 2))\n this.write(`${CSI}${botRow + 2};${hintCol}H${FG_DIM}${hint}${RESET}`)\n }\n\n drawModal()\n\n if (process.stdin.isTTY) {\n process.stdin.setRawMode(true)\n }\n process.stdin.resume()\n\n const onKey = (data: Buffer) => {\n const key = data.toString()\n\n if (key === '\\x1b[A' || key === 'k') {\n selected = (selected - 1 + agents.length) % agents.length\n drawModal()\n } else if (key === '\\x1b[B' || key === 'j') {\n selected = (selected + 1) % agents.length\n drawModal()\n } else if (key === 't' || key === 'T' || key === ' ') {\n trackTime = !trackTime\n drawModal()\n } else if (key === '\\r' || key === '\\n') {\n cleanup()\n resolve({ agentIdx: selected, trackTime })\n } else if (key === 'q' || key === '\\x1b' || key === '\\x03') {\n cleanup()\n this.write(`${CSI}?25h`)\n this.write(`${CSI}?1049l`)\n process.exit(0)\n }\n }\n\n const cleanup = () => {\n process.stdin.removeListener('data', onKey)\n this.write(`${CSI}?25h`)\n // Leave alternate screen \u2014 back to normal mode for the agent\n this.write(`${CSI}?1049l`)\n }\n\n process.stdin.on('data', onKey)\n })\n }\n\n // \u2500\u2500\u2500 Internal \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private write(data: string): void {\n try {\n process.stdout.write(data)\n } catch { /* EPIPE safe */ }\n }\n\n private setScrollRegion(): void {\n // PTY gets rows 1 through (rows - 1), status bar on last row\n this.write(`${CSI}1;${this.rows - 1}r`)\n }\n\n private drawStatusBar(): void {\n const { agentName, wsStatus, cwd } = this.state\n\n // Move to last line (outside scroll region)\n this.write(`${CSI}${this.rows};1H`)\n this.write(`${BG_BAR}${CSI}2K`)\n\n const displayCwd = this.shortenPath(cwd)\n\n const statusColor = {\n connected: FG_GREEN,\n connecting: FG_YELLOW,\n disconnected: FG_RED,\n pending_approval: FG_YELLOW,\n no_project: FG_DIM,\n }[wsStatus] || FG_DIM\n\n const statusDot = `${statusColor}\\u25CF${RESET}${BG_BAR}`\n const statusLabel = {\n connected: 'Connected',\n connecting: 'Connecting...',\n disconnected: 'Disconnected',\n pending_approval: 'Pending Approval',\n no_project: 'No Project',\n }[wsStatus] || wsStatus\n\n const left = ` ${FG_ACCENT}ctlsurf${RESET}${BG_BAR} ${statusDot} ${FG_DIM}${statusLabel}${RESET}${BG_BAR} ${FG_DIM}\\u2502${RESET}${BG_BAR} ${FG_DIM}${agentName || '...'}${RESET}${BG_BAR}`\n const right = `${FG_DIM}Ctrl+\\\\ exit${RESET}${BG_BAR} ${FG_DIM}${displayCwd} ${RESET}${BG_BAR}`\n\n this.write(left)\n const pad = Math.max(0, this.cols - this.visibleLen(left) - this.visibleLen(right))\n this.write(' '.repeat(pad))\n this.write(right)\n this.write(RESET)\n }\n\n private shortenPath(p: string): string {\n if (!p) return ''\n const home = process.env.HOME || ''\n if (home && p.startsWith(home)) {\n return '~' + p.slice(home.length)\n }\n return p\n }\n\n private visibleLen(s: string): number {\n return s.replace(/\\x1b\\[[^m]*m/g, '').length\n }\n}\n", "#!/usr/bin/env node\n\n/**\n * ctlsurf terminal mode (TUI)\n *\n * Runs the agent in a PTY with a terminal UI: title bar, status bar,\n * conversation logging, and WebSocket control. No Electron required.\n *\n * Usage:\n * ctlsurf --terminal [--agent claude] [--cwd /path] [--api-key KEY] [--base-url URL] [--profile NAME]\n *\n * If no --agent is given, shows an interactive agent picker.\n * Press Ctrl+\\ to exit at any time.\n */\n\n// Prevent EPIPE crashes\nprocess.stdout?.on?.('error', () => {})\nprocess.stderr?.on?.('error', () => {})\nprocess.on('uncaughtException', (err) => {\n if (err.message === 'write EPIPE') return\n try { console.error('[uncaught]', err) } catch { /* ignore */ }\n})\n\nimport { Orchestrator } from './orchestrator'\nimport { getSettingsDir } from './settingsDir'\nimport { getBuiltinAgents, getDefaultAgent, isCodingAgent, type AgentConfig } from './agents'\nimport { Tui } from './tui'\n\n// \u2500\u2500\u2500 CLI arg parsing \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ninterface CliArgs {\n agent: string | null\n cwd: string\n apiKey: string | null\n baseUrl: string | null\n profile: string | null\n}\n\nfunction parseArgs(argv: string[]): CliArgs {\n const args: CliArgs = {\n agent: null,\n cwd: process.env.CTLSURF_WORKER_CWD || process.cwd(),\n apiKey: null,\n baseUrl: null,\n profile: null,\n }\n\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i]\n const next = argv[i + 1]\n switch (arg) {\n case '--agent': args.agent = next; i++; break\n case '--cwd': args.cwd = next; i++; break\n case '--api-key': args.apiKey = next; i++; break\n case '--base-url': args.baseUrl = next; i++; break\n case '--profile': args.profile = next; i++; break\n case '--terminal': break\n case '--desktop': break\n }\n }\n return args\n}\n\n// \u2500\u2500\u2500 Main \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nasync function main() {\n const args = parseArgs(process.argv.slice(2))\n const settingsDir = getSettingsDir(false)\n\n const tui = new Tui()\n const agents = getBuiltinAgents()\n\n // \u2500\u2500\u2500 Orchestrator (loaded early so picker can read profile defaults) \u2500\u2500\n\n const orchestrator = new Orchestrator(settingsDir, {\n onPtyData: (_tabId, data) => {\n tui.writePtyData(data)\n },\n onPtyExit: (_tabId, code) => {\n tui.destroy()\n console.log(`Agent exited with code ${code}`)\n orchestrator.shutdown().then(() => process.exit(code))\n },\n onWorkerStatus: (status) => {\n tui.update({ wsStatus: status })\n },\n onWorkerMessage: () => {},\n onWorkerRegistered: () => {\n tui.update({ wsStatus: 'connected' })\n },\n onCwdChanged: () => {\n tui.update({ cwd: orchestrator.cwd || '' })\n },\n })\n\n orchestrator.loadSettings()\n\n if (args.profile) orchestrator.switchProfile(args.profile)\n if (args.apiKey) orchestrator.overrideApiKey(args.apiKey)\n if (args.baseUrl) orchestrator.overrideBaseUrl(args.baseUrl)\n\n // \u2500\u2500\u2500 Agent selection \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n let agent: AgentConfig\n let trackTimeOverride: boolean | undefined\n\n if (args.agent) {\n const found = agents.find(a => a.id === args.agent)\n agent = found || {\n id: args.agent,\n name: args.agent,\n command: args.agent,\n args: [],\n description: `Custom agent: ${args.agent}`,\n }\n } else {\n const initialTrackTime = orchestrator.getActiveProfile().trackTime !== false\n const picked = await tui.showAgentPicker(agents, { initialTrackTime })\n agent = agents[picked.agentIdx]\n trackTimeOverride = picked.trackTime\n }\n\n // \u2500\u2500\u2500 Start TUI + agent \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n tui.update({\n agentName: agent.name,\n cwd: args.cwd,\n mode: 'terminal',\n })\n\n tui.init()\n\n // Spawn agent with PTY sized to fit the TUI content area\n const HEADLESS_TAB = 'headless'\n const ptySize = tui.getPtySize()\n await orchestrator.spawnAgent(HEADLESS_TAB, agent, args.cwd, { trackTime: trackTimeOverride })\n orchestrator.resizePty(HEADLESS_TAB, ptySize.cols, ptySize.rows)\n\n // For coding agents, send an initial prompt to kick-start them\n if (isCodingAgent(agent)) {\n setTimeout(() => {\n orchestrator.writePty(HEADLESS_TAB, 'hello\\r')\n }, 1000)\n }\n\n // Pipe stdin to PTY, with Ctrl+\\ as the exit key\n // SGR mouse wheel: \\x1b[<64;col;rowM = scroll up, \\x1b[<65;col;rowM = scroll down\n const SCROLL_UP_RE = /\\x1b\\[<64;\\d+;\\d+M/\n const SCROLL_DOWN_RE = /\\x1b\\[<65;\\d+;\\d+M/\n // Use mouse wheel events directly \u2014 forward as SGR mouse to the PTY\n // so the inner app can handle them natively\n\n if (process.stdin.isTTY) {\n process.stdin.setRawMode(true)\n process.stdin.resume()\n process.stdin.on('data', (data) => {\n const str = data.toString()\n // Ctrl+\\ (0x1c) = exit\n if (str === '\\x1c') {\n shutdown()\n return\n }\n // Drop mouse wheel events \u2014 Claude Code doesn't support mouse scrolling.\n // Scrolling is handled by the terminal emulator natively when not in\n // alternate screen, or not at all in alternate screen (Claude Code's TUI).\n if (SCROLL_UP_RE.test(str) || SCROLL_DOWN_RE.test(str)) {\n return\n }\n orchestrator.writePty(HEADLESS_TAB, str)\n })\n }\n\n // Handle terminal resize\n process.stdout.on('resize', () => {\n const cols = process.stdout.columns || 80\n const rows = process.stdout.rows || 24\n tui.resize(cols, rows)\n const size = tui.getPtySize()\n orchestrator.resizePty(HEADLESS_TAB, size.cols, size.rows)\n })\n\n // Graceful shutdown\n const shutdown = async () => {\n if (process.stdin.isTTY) {\n process.stdin.setRawMode(false)\n }\n tui.destroy()\n await orchestrator.shutdown()\n process.exit(0)\n }\n\n process.on('SIGINT', shutdown)\n process.on('SIGTERM', shutdown)\n}\n\nmain().catch((err) => {\n process.stdout.write('\\x1b[?1049l')\n console.error('Fatal error:', err)\n process.exit(1)\n})\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["fs", "path", "os", "require", "path", "os", "log", "log", "os", "path", "os"]
|
|
7
7
|
}
|
package/out/main/index.js
CHANGED
|
@@ -4455,13 +4455,23 @@ const AGENT_DATASTORE_PAGE_TITLE = "Agent Datastore";
|
|
|
4455
4455
|
const FIRST_CHECKPOINT_DELAY_MS = 30 * 1e3;
|
|
4456
4456
|
const CHECKPOINT_INTERVAL_MS = 5 * 60 * 1e3;
|
|
4457
4457
|
const COLUMNS = [
|
|
4458
|
-
{ name: "Started", type: "
|
|
4458
|
+
{ name: "Started", type: "text" },
|
|
4459
4459
|
{ name: "Active Time", type: "number" },
|
|
4460
4460
|
{ name: "Agent", type: "text" },
|
|
4461
4461
|
{ name: "Worker", type: "text" },
|
|
4462
4462
|
{ name: "Session", type: "text" },
|
|
4463
4463
|
{ name: "Notes", type: "text" }
|
|
4464
4464
|
];
|
|
4465
|
+
function formatStarted(ms) {
|
|
4466
|
+
return new Date(ms).toLocaleString("en-US", {
|
|
4467
|
+
year: "numeric",
|
|
4468
|
+
month: "short",
|
|
4469
|
+
day: "numeric",
|
|
4470
|
+
hour: "numeric",
|
|
4471
|
+
minute: "2-digit",
|
|
4472
|
+
hour12: true
|
|
4473
|
+
});
|
|
4474
|
+
}
|
|
4465
4475
|
function log$2(...args) {
|
|
4466
4476
|
try {
|
|
4467
4477
|
console.log("[time-tracker]", ...args);
|
|
@@ -4496,10 +4506,9 @@ class TimeTracker {
|
|
|
4496
4506
|
return;
|
|
4497
4507
|
}
|
|
4498
4508
|
const startedAt = Date.now();
|
|
4499
|
-
const startedIso = new Date(startedAt).toISOString();
|
|
4500
4509
|
const sessionUuid = require$$1.randomUUID();
|
|
4501
4510
|
const row = await this.api.addRow(blockId, {
|
|
4502
|
-
Started:
|
|
4511
|
+
Started: formatStarted(startedAt),
|
|
4503
4512
|
"Active Time": 0,
|
|
4504
4513
|
Agent: agentName,
|
|
4505
4514
|
Worker: os.hostname(),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { c as createWebWorker, l as languages } from "./index-
|
|
2
|
-
import { C as CompletionAdapter, H as HoverAdapter, D as DocumentHighlightAdapter, a as DefinitionAdapter, R as ReferenceAdapter, b as DocumentSymbolAdapter, c as RenameAdapter, d as DocumentColorAdapter, F as FoldingRangeAdapter, e as DiagnosticsAdapter, S as SelectionRangeAdapter, f as DocumentFormattingEditProvider, g as DocumentRangeFormattingEditProvider } from "./lspLanguageFeatures-
|
|
3
|
-
import { h, i, j, t, k } from "./lspLanguageFeatures-
|
|
1
|
+
import { c as createWebWorker, l as languages } from "./index-Bm_rbVP-.js";
|
|
2
|
+
import { C as CompletionAdapter, H as HoverAdapter, D as DocumentHighlightAdapter, a as DefinitionAdapter, R as ReferenceAdapter, b as DocumentSymbolAdapter, c as RenameAdapter, d as DocumentColorAdapter, F as FoldingRangeAdapter, e as DiagnosticsAdapter, S as SelectionRangeAdapter, f as DocumentFormattingEditProvider, g as DocumentRangeFormattingEditProvider } from "./lspLanguageFeatures-peGVtLxi.js";
|
|
3
|
+
import { h, i, j, t, k } from "./lspLanguageFeatures-peGVtLxi.js";
|
|
4
4
|
const STOP_WHEN_IDLE_FOR = 2 * 60 * 1e3;
|
|
5
5
|
class WorkerManager {
|
|
6
6
|
constructor(defaults) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { c as createWebWorker, l as languages } from "./index-
|
|
2
|
-
import { H as HoverAdapter, D as DocumentHighlightAdapter, h as DocumentLinkAdapter, F as FoldingRangeAdapter, b as DocumentSymbolAdapter, S as SelectionRangeAdapter, c as RenameAdapter, f as DocumentFormattingEditProvider, g as DocumentRangeFormattingEditProvider, C as CompletionAdapter } from "./lspLanguageFeatures-
|
|
3
|
-
import { a, e, d, R, i, j, t, k } from "./lspLanguageFeatures-
|
|
1
|
+
import { c as createWebWorker, l as languages } from "./index-Bm_rbVP-.js";
|
|
2
|
+
import { H as HoverAdapter, D as DocumentHighlightAdapter, h as DocumentLinkAdapter, F as FoldingRangeAdapter, b as DocumentSymbolAdapter, S as SelectionRangeAdapter, c as RenameAdapter, f as DocumentFormattingEditProvider, g as DocumentRangeFormattingEditProvider, C as CompletionAdapter } from "./lspLanguageFeatures-peGVtLxi.js";
|
|
3
|
+
import { a, e, d, R, i, j, t, k } from "./lspLanguageFeatures-peGVtLxi.js";
|
|
4
4
|
const STOP_WHEN_IDLE_FOR = 2 * 60 * 1e3;
|
|
5
5
|
class WorkerManager {
|
|
6
6
|
constructor(defaults) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./cssMode-
|
|
1
|
+
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./cssMode-BW-SuYuP.js","./lspLanguageFeatures-peGVtLxi.js","./htmlMode-C2dZKrOy.js","./jsonMode-BaVI6jAw.js","./javascript-busdVZMv.js","./typescript-CjkgfhVK.js"])))=>i.map(i=>d[i]);
|
|
2
2
|
function getDefaultExportFromCjs(x) {
|
|
3
3
|
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
|
|
4
4
|
}
|
|
@@ -18827,13 +18827,15 @@ function TerminalPanel({ tabId, agent, onSpawn, onExit, isActive }) {
|
|
|
18827
18827
|
const handleResize = () => {
|
|
18828
18828
|
clearTimeout(resizeTimeout);
|
|
18829
18829
|
resizeTimeout = setTimeout(() => {
|
|
18830
|
+
const el = containerRef.current;
|
|
18831
|
+
if (!el || el.offsetWidth === 0 || el.offsetHeight === 0) return;
|
|
18830
18832
|
const state = _terminals.get(tabId);
|
|
18831
|
-
if (state)
|
|
18832
|
-
|
|
18833
|
-
|
|
18834
|
-
|
|
18835
|
-
|
|
18836
|
-
|
|
18833
|
+
if (!state) return;
|
|
18834
|
+
state.fitAddon.fit();
|
|
18835
|
+
const { cols, rows } = state.terminal;
|
|
18836
|
+
if (cols < 10 || rows < 5) return;
|
|
18837
|
+
scrollIfPinned(tabId, state.terminal);
|
|
18838
|
+
window.worker.resizePty(tabId, cols, rows);
|
|
18837
18839
|
}, 150);
|
|
18838
18840
|
};
|
|
18839
18841
|
window.addEventListener("resize", handleResize);
|
|
@@ -206556,7 +206558,7 @@ const lessDefaults = new LanguageServiceDefaultsImpl$3(
|
|
|
206556
206558
|
modeConfigurationDefault$2
|
|
206557
206559
|
);
|
|
206558
206560
|
function getMode$3() {
|
|
206559
|
-
return __vitePreload(() => import("./cssMode-
|
|
206561
|
+
return __vitePreload(() => import("./cssMode-BW-SuYuP.js"), true ? __vite__mapDeps([0,1]) : void 0, import.meta.url);
|
|
206560
206562
|
}
|
|
206561
206563
|
languages.onLanguage("less", () => {
|
|
206562
206564
|
getMode$3().then((mode2) => mode2.setupMode(lessDefaults));
|
|
@@ -206661,7 +206663,7 @@ const razorLanguageService = registerHTMLLanguageService(
|
|
|
206661
206663
|
);
|
|
206662
206664
|
const razorDefaults = razorLanguageService.defaults;
|
|
206663
206665
|
function getMode$2() {
|
|
206664
|
-
return __vitePreload(() => import("./htmlMode-
|
|
206666
|
+
return __vitePreload(() => import("./htmlMode-C2dZKrOy.js"), true ? __vite__mapDeps([2,1]) : void 0, import.meta.url);
|
|
206665
206667
|
}
|
|
206666
206668
|
function registerHTMLLanguageService(languageId, options = optionsDefault, modeConfiguration = getConfigurationDefault(languageId)) {
|
|
206667
206669
|
const defaults = new LanguageServiceDefaultsImpl$2(languageId, options, modeConfiguration);
|
|
@@ -206745,7 +206747,7 @@ const jsonDefaults = new LanguageServiceDefaultsImpl$1(
|
|
|
206745
206747
|
);
|
|
206746
206748
|
const getWorker$1 = () => getMode$1().then((mode2) => mode2.getWorker());
|
|
206747
206749
|
function getMode$1() {
|
|
206748
|
-
return __vitePreload(() => import("./jsonMode-
|
|
206750
|
+
return __vitePreload(() => import("./jsonMode-BaVI6jAw.js"), true ? __vite__mapDeps([3,1]) : void 0, import.meta.url);
|
|
206749
206751
|
}
|
|
206750
206752
|
languages.register({
|
|
206751
206753
|
id: "json",
|
|
@@ -206991,7 +206993,7 @@ const getJavaScriptWorker = () => {
|
|
|
206991
206993
|
return getMode().then((mode) => mode.getJavaScriptWorker());
|
|
206992
206994
|
};
|
|
206993
206995
|
function getMode() {
|
|
206994
|
-
return __vitePreload(() => import("./tsMode-
|
|
206996
|
+
return __vitePreload(() => import("./tsMode-B4oEmliC.js"), true ? [] : void 0, import.meta.url);
|
|
206995
206997
|
}
|
|
206996
206998
|
languages.onLanguage("typescript", () => {
|
|
206997
206999
|
return getMode().then((mode) => mode.setupTypeScript(typescriptDefaults));
|
|
@@ -207186,49 +207188,49 @@ registerLanguage({
|
|
|
207186
207188
|
extensions: [".ftl", ".ftlh", ".ftlx"],
|
|
207187
207189
|
aliases: ["FreeMarker2", "Apache FreeMarker2"],
|
|
207188
207190
|
loader: () => {
|
|
207189
|
-
return __vitePreload(() => import("./freemarker2-
|
|
207191
|
+
return __vitePreload(() => import("./freemarker2-2YWYzawi.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationDollar);
|
|
207190
207192
|
}
|
|
207191
207193
|
});
|
|
207192
207194
|
registerLanguage({
|
|
207193
207195
|
id: "freemarker2.tag-angle.interpolation-dollar",
|
|
207194
207196
|
aliases: ["FreeMarker2 (Angle/Dollar)", "Apache FreeMarker2 (Angle/Dollar)"],
|
|
207195
207197
|
loader: () => {
|
|
207196
|
-
return __vitePreload(() => import("./freemarker2-
|
|
207198
|
+
return __vitePreload(() => import("./freemarker2-2YWYzawi.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAngleInterpolationDollar);
|
|
207197
207199
|
}
|
|
207198
207200
|
});
|
|
207199
207201
|
registerLanguage({
|
|
207200
207202
|
id: "freemarker2.tag-bracket.interpolation-dollar",
|
|
207201
207203
|
aliases: ["FreeMarker2 (Bracket/Dollar)", "Apache FreeMarker2 (Bracket/Dollar)"],
|
|
207202
207204
|
loader: () => {
|
|
207203
|
-
return __vitePreload(() => import("./freemarker2-
|
|
207205
|
+
return __vitePreload(() => import("./freemarker2-2YWYzawi.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagBracketInterpolationDollar);
|
|
207204
207206
|
}
|
|
207205
207207
|
});
|
|
207206
207208
|
registerLanguage({
|
|
207207
207209
|
id: "freemarker2.tag-angle.interpolation-bracket",
|
|
207208
207210
|
aliases: ["FreeMarker2 (Angle/Bracket)", "Apache FreeMarker2 (Angle/Bracket)"],
|
|
207209
207211
|
loader: () => {
|
|
207210
|
-
return __vitePreload(() => import("./freemarker2-
|
|
207212
|
+
return __vitePreload(() => import("./freemarker2-2YWYzawi.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAngleInterpolationBracket);
|
|
207211
207213
|
}
|
|
207212
207214
|
});
|
|
207213
207215
|
registerLanguage({
|
|
207214
207216
|
id: "freemarker2.tag-bracket.interpolation-bracket",
|
|
207215
207217
|
aliases: ["FreeMarker2 (Bracket/Bracket)", "Apache FreeMarker2 (Bracket/Bracket)"],
|
|
207216
207218
|
loader: () => {
|
|
207217
|
-
return __vitePreload(() => import("./freemarker2-
|
|
207219
|
+
return __vitePreload(() => import("./freemarker2-2YWYzawi.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagBracketInterpolationBracket);
|
|
207218
207220
|
}
|
|
207219
207221
|
});
|
|
207220
207222
|
registerLanguage({
|
|
207221
207223
|
id: "freemarker2.tag-auto.interpolation-dollar",
|
|
207222
207224
|
aliases: ["FreeMarker2 (Auto/Dollar)", "Apache FreeMarker2 (Auto/Dollar)"],
|
|
207223
207225
|
loader: () => {
|
|
207224
|
-
return __vitePreload(() => import("./freemarker2-
|
|
207226
|
+
return __vitePreload(() => import("./freemarker2-2YWYzawi.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationDollar);
|
|
207225
207227
|
}
|
|
207226
207228
|
});
|
|
207227
207229
|
registerLanguage({
|
|
207228
207230
|
id: "freemarker2.tag-auto.interpolation-bracket",
|
|
207229
207231
|
aliases: ["FreeMarker2 (Auto/Bracket)", "Apache FreeMarker2 (Auto/Bracket)"],
|
|
207230
207232
|
loader: () => {
|
|
207231
|
-
return __vitePreload(() => import("./freemarker2-
|
|
207233
|
+
return __vitePreload(() => import("./freemarker2-2YWYzawi.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationBracket);
|
|
207232
207234
|
}
|
|
207233
207235
|
});
|
|
207234
207236
|
registerLanguage({
|
|
@@ -207249,7 +207251,7 @@ registerLanguage({
|
|
|
207249
207251
|
extensions: [".handlebars", ".hbs"],
|
|
207250
207252
|
aliases: ["Handlebars", "handlebars", "hbs"],
|
|
207251
207253
|
mimetypes: ["text/x-handlebars-template"],
|
|
207252
|
-
loader: () => __vitePreload(() => import("./handlebars-
|
|
207254
|
+
loader: () => __vitePreload(() => import("./handlebars-EwtUQRsf.js"), true ? [] : void 0, import.meta.url)
|
|
207253
207255
|
});
|
|
207254
207256
|
registerLanguage({
|
|
207255
207257
|
id: "hcl",
|
|
@@ -207262,7 +207264,7 @@ registerLanguage({
|
|
|
207262
207264
|
extensions: [".html", ".htm", ".shtml", ".xhtml", ".mdoc", ".jsp", ".asp", ".aspx", ".jshtm"],
|
|
207263
207265
|
aliases: ["HTML", "htm", "html", "xhtml"],
|
|
207264
207266
|
mimetypes: ["text/html", "text/x-jshtm", "text/template", "text/ng-template"],
|
|
207265
|
-
loader: () => __vitePreload(() => import("./html-
|
|
207267
|
+
loader: () => __vitePreload(() => import("./html-BNZkIDb9.js"), true ? [] : void 0, import.meta.url)
|
|
207266
207268
|
});
|
|
207267
207269
|
registerLanguage({
|
|
207268
207270
|
id: "ini",
|
|
@@ -207285,7 +207287,7 @@ registerLanguage({
|
|
|
207285
207287
|
filenames: ["jakefile"],
|
|
207286
207288
|
aliases: ["JavaScript", "javascript", "js"],
|
|
207287
207289
|
mimetypes: ["text/javascript"],
|
|
207288
|
-
loader: () => __vitePreload(() => import("./javascript-
|
|
207290
|
+
loader: () => __vitePreload(() => import("./javascript-busdVZMv.js"), true ? __vite__mapDeps([4,5]) : void 0, import.meta.url)
|
|
207289
207291
|
});
|
|
207290
207292
|
registerLanguage({
|
|
207291
207293
|
id: "julia",
|
|
@@ -207324,7 +207326,7 @@ registerLanguage({
|
|
|
207324
207326
|
extensions: [".liquid", ".html.liquid"],
|
|
207325
207327
|
aliases: ["Liquid", "liquid"],
|
|
207326
207328
|
mimetypes: ["application/liquid"],
|
|
207327
|
-
loader: () => __vitePreload(() => import("./liquid-
|
|
207329
|
+
loader: () => __vitePreload(() => import("./liquid-DG08un1Q.js"), true ? [] : void 0, import.meta.url)
|
|
207328
207330
|
});
|
|
207329
207331
|
registerLanguage({
|
|
207330
207332
|
id: "m3",
|
|
@@ -207342,7 +207344,7 @@ registerLanguage({
|
|
|
207342
207344
|
id: "mdx",
|
|
207343
207345
|
extensions: [".mdx"],
|
|
207344
207346
|
aliases: ["MDX", "mdx"],
|
|
207345
|
-
loader: () => __vitePreload(() => import("./mdx-
|
|
207347
|
+
loader: () => __vitePreload(() => import("./mdx-DogBhUxZ.js"), true ? [] : void 0, import.meta.url)
|
|
207346
207348
|
});
|
|
207347
207349
|
registerLanguage({
|
|
207348
207350
|
id: "mips",
|
|
@@ -207441,7 +207443,7 @@ registerLanguage({
|
|
|
207441
207443
|
extensions: [".py", ".rpy", ".pyw", ".cpy", ".gyp", ".gypi"],
|
|
207442
207444
|
aliases: ["Python", "py"],
|
|
207443
207445
|
firstLine: "^#!/.*\\bpython[0-9.-]*\\b",
|
|
207444
|
-
loader: () => __vitePreload(() => import("./python-
|
|
207446
|
+
loader: () => __vitePreload(() => import("./python-Bf-INYXh.js"), true ? [] : void 0, import.meta.url)
|
|
207445
207447
|
});
|
|
207446
207448
|
registerLanguage({
|
|
207447
207449
|
id: "qsharp",
|
|
@@ -207460,7 +207462,7 @@ registerLanguage({
|
|
|
207460
207462
|
extensions: [".cshtml"],
|
|
207461
207463
|
aliases: ["Razor", "razor"],
|
|
207462
207464
|
mimetypes: ["text/x-cshtml"],
|
|
207463
|
-
loader: () => __vitePreload(() => import("./razor-
|
|
207465
|
+
loader: () => __vitePreload(() => import("./razor-DLrZ2hsF.js"), true ? [] : void 0, import.meta.url)
|
|
207464
207466
|
});
|
|
207465
207467
|
registerLanguage({
|
|
207466
207468
|
id: "redis",
|
|
@@ -207593,7 +207595,7 @@ registerLanguage({
|
|
|
207593
207595
|
aliases: ["TypeScript", "ts", "typescript"],
|
|
207594
207596
|
mimetypes: ["text/typescript"],
|
|
207595
207597
|
loader: () => {
|
|
207596
|
-
return __vitePreload(() => import("./typescript-
|
|
207598
|
+
return __vitePreload(() => import("./typescript-CjkgfhVK.js"), true ? [] : void 0, import.meta.url);
|
|
207597
207599
|
}
|
|
207598
207600
|
});
|
|
207599
207601
|
registerLanguage({
|
|
@@ -207638,14 +207640,14 @@ registerLanguage({
|
|
|
207638
207640
|
firstLine: "(\\<\\?xml.*)|(\\<svg)|(\\<\\!doctype\\s+svg)",
|
|
207639
207641
|
aliases: ["XML", "xml"],
|
|
207640
207642
|
mimetypes: ["text/xml", "application/xml", "application/xaml+xml", "application/xml-dtd"],
|
|
207641
|
-
loader: () => __vitePreload(() => import("./xml-
|
|
207643
|
+
loader: () => __vitePreload(() => import("./xml-0FAXmuVg.js"), true ? [] : void 0, import.meta.url)
|
|
207642
207644
|
});
|
|
207643
207645
|
registerLanguage({
|
|
207644
207646
|
id: "yaml",
|
|
207645
207647
|
extensions: [".yaml", ".yml"],
|
|
207646
207648
|
aliases: ["YAML", "yaml", "YML", "yml"],
|
|
207647
207649
|
mimetypes: ["application/x-yaml", "text/x-yaml"],
|
|
207648
|
-
loader: () => __vitePreload(() => import("./yaml-
|
|
207650
|
+
loader: () => __vitePreload(() => import("./yaml-DWxnPuy8.js"), true ? [] : void 0, import.meta.url)
|
|
207649
207651
|
});
|
|
207650
207652
|
var __defProp = Object.defineProperty;
|
|
207651
207653
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { conf as conf$1, language as language$1 } from "./typescript-
|
|
2
|
-
import "./index-
|
|
1
|
+
import { conf as conf$1, language as language$1 } from "./typescript-CjkgfhVK.js";
|
|
2
|
+
import "./index-Bm_rbVP-.js";
|
|
3
3
|
const conf = conf$1;
|
|
4
4
|
const language = {
|
|
5
5
|
// Set defaultToken to invalid to see what you do not tokenize yet
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { c as createWebWorker, l as languages, e as editor } from "./index-
|
|
2
|
-
import { f as DocumentFormattingEditProvider, g as DocumentRangeFormattingEditProvider, C as CompletionAdapter, H as HoverAdapter, b as DocumentSymbolAdapter, d as DocumentColorAdapter, F as FoldingRangeAdapter, S as SelectionRangeAdapter, e as DiagnosticsAdapter } from "./lspLanguageFeatures-
|
|
3
|
-
import { a, D, h, R, c, i, j, t, k } from "./lspLanguageFeatures-
|
|
1
|
+
import { c as createWebWorker, l as languages, e as editor } from "./index-Bm_rbVP-.js";
|
|
2
|
+
import { f as DocumentFormattingEditProvider, g as DocumentRangeFormattingEditProvider, C as CompletionAdapter, H as HoverAdapter, b as DocumentSymbolAdapter, d as DocumentColorAdapter, F as FoldingRangeAdapter, S as SelectionRangeAdapter, e as DiagnosticsAdapter } from "./lspLanguageFeatures-peGVtLxi.js";
|
|
3
|
+
import { a, D, h, R, c, i, j, t, k } from "./lspLanguageFeatures-peGVtLxi.js";
|
|
4
4
|
const STOP_WHEN_IDLE_FOR = 2 * 60 * 1e3;
|
|
5
5
|
class WorkerManager {
|
|
6
6
|
constructor(defaults) {
|
package/out/renderer/assets/{lspLanguageFeatures-CUafmPGy.js → lspLanguageFeatures-peGVtLxi.js}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { R as Range$1, l as languages, e as editor, U as Uri, M as MarkerSeverity } from "./index-
|
|
1
|
+
import { R as Range$1, l as languages, e as editor, U as Uri, M as MarkerSeverity } from "./index-Bm_rbVP-.js";
|
|
2
2
|
var DocumentUri;
|
|
3
3
|
(function(DocumentUri2) {
|
|
4
4
|
function is(value) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { c as createWebWorker, e as editor, U as Uri, a as MarkerTag, M as MarkerSeverity, l as languages, t as typescriptDefaults, R as Range } from "./index-
|
|
1
|
+
import { c as createWebWorker, e as editor, U as Uri, a as MarkerTag, M as MarkerSeverity, l as languages, t as typescriptDefaults, R as Range } from "./index-Bm_rbVP-.js";
|
|
2
2
|
class WorkerManager {
|
|
3
3
|
constructor(_modeId, _defaults) {
|
|
4
4
|
this._modeId = _modeId;
|
package/out/renderer/index.html
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>ctlsurf-worker</title>
|
|
7
|
-
<script type="module" crossorigin src="./assets/index-
|
|
7
|
+
<script type="module" crossorigin src="./assets/index-Bm_rbVP-.js"></script>
|
|
8
8
|
<link rel="stylesheet" crossorigin href="./assets/index-CrTu3Z4M.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phenx-inc/ctlsurf",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "Agent-agnostic terminal and desktop app for ctlsurf — run Claude Code, Codex, or any coding agent with live session logging and remote control",
|
|
5
5
|
"main": "out/main/index.js",
|
|
6
6
|
"bin": {
|
package/src/main/timeTracker.ts
CHANGED
|
@@ -8,7 +8,7 @@ const FIRST_CHECKPOINT_DELAY_MS = 30 * 1000
|
|
|
8
8
|
const CHECKPOINT_INTERVAL_MS = 5 * 60 * 1000
|
|
9
9
|
|
|
10
10
|
const COLUMNS: Array<{ name: string; type: string }> = [
|
|
11
|
-
{ name: 'Started', type: '
|
|
11
|
+
{ name: 'Started', type: 'text' },
|
|
12
12
|
{ name: 'Active Time', type: 'number' },
|
|
13
13
|
{ name: 'Agent', type: 'text' },
|
|
14
14
|
{ name: 'Worker', type: 'text' },
|
|
@@ -16,6 +16,17 @@ const COLUMNS: Array<{ name: string; type: string }> = [
|
|
|
16
16
|
{ name: 'Notes', type: 'text' },
|
|
17
17
|
]
|
|
18
18
|
|
|
19
|
+
function formatStarted(ms: number): string {
|
|
20
|
+
return new Date(ms).toLocaleString('en-US', {
|
|
21
|
+
year: 'numeric',
|
|
22
|
+
month: 'short',
|
|
23
|
+
day: 'numeric',
|
|
24
|
+
hour: 'numeric',
|
|
25
|
+
minute: '2-digit',
|
|
26
|
+
hour12: true,
|
|
27
|
+
})
|
|
28
|
+
}
|
|
29
|
+
|
|
19
30
|
interface SessionState {
|
|
20
31
|
blockId: string
|
|
21
32
|
rowId: string
|
|
@@ -64,10 +75,9 @@ export class TimeTracker {
|
|
|
64
75
|
return
|
|
65
76
|
}
|
|
66
77
|
const startedAt = Date.now()
|
|
67
|
-
const startedIso = new Date(startedAt).toISOString()
|
|
68
78
|
const sessionUuid = randomUUID()
|
|
69
79
|
const row = await this.api.addRow(blockId, {
|
|
70
|
-
Started:
|
|
80
|
+
Started: formatStarted(startedAt),
|
|
71
81
|
'Active Time': 0,
|
|
72
82
|
Agent: agentName,
|
|
73
83
|
Worker: os.hostname(),
|
|
@@ -158,18 +158,22 @@ export function TerminalPanel({ tabId, agent, onSpawn, onExit, isActive }: Termi
|
|
|
158
158
|
fitAddon.fit()
|
|
159
159
|
scrollIfPinned(tabId, terminal)
|
|
160
160
|
|
|
161
|
-
// Resize handling
|
|
161
|
+
// Resize handling — skip when the container is hidden (display:none → 0x0),
|
|
162
|
+
// otherwise fit() reports a tiny size and the PTY gets stuck at ~4 cols
|
|
163
|
+
// until the user switches back.
|
|
162
164
|
let resizeTimeout: ReturnType<typeof setTimeout>
|
|
163
165
|
const handleResize = () => {
|
|
164
166
|
clearTimeout(resizeTimeout)
|
|
165
167
|
resizeTimeout = setTimeout(() => {
|
|
168
|
+
const el = containerRef.current
|
|
169
|
+
if (!el || el.offsetWidth === 0 || el.offsetHeight === 0) return
|
|
166
170
|
const state = _terminals.get(tabId)
|
|
167
|
-
if (state)
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
171
|
+
if (!state) return
|
|
172
|
+
state.fitAddon.fit()
|
|
173
|
+
const { cols, rows } = state.terminal
|
|
174
|
+
if (cols < 10 || rows < 5) return
|
|
175
|
+
scrollIfPinned(tabId, state.terminal)
|
|
176
|
+
window.worker.resizePty(tabId, cols, rows)
|
|
173
177
|
}, 150)
|
|
174
178
|
}
|
|
175
179
|
|