@phenx-inc/ctlsurf 0.16.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/out/headless/index.mjs +549 -116
- package/out/headless/index.mjs.map +4 -4
- package/out/main/index.js +416 -38
- package/out/preload/index.js +3 -0
- package/out/renderer/assets/{cssMode-Xx1-SozN.js → cssMode-BtQxI2Eb.js} +3 -3
- package/out/renderer/assets/{freemarker2-DM49yPw1.js → freemarker2-DRW5dmY5.js} +1 -1
- package/out/renderer/assets/{handlebars-DJJBblpV.js → handlebars-CFLF3cP7.js} +1 -1
- package/out/renderer/assets/{html-DnMnJOxw.js → html-DtnBOkwp.js} +1 -1
- package/out/renderer/assets/{htmlMode-BG5u2CSH.js → htmlMode-DMVysDCg.js} +3 -3
- package/out/renderer/assets/{index-DID1LlMU.css → index-CEjwKjtd.css} +45 -0
- package/out/renderer/assets/{index-D65QIRev.js → index-Dm70hk5s.js} +292 -38
- package/out/renderer/assets/{javascript-Dci_phmN.js → javascript-ChlyA1sf.js} +2 -2
- package/out/renderer/assets/{jsonMode-Dmp3JJSm.js → jsonMode-D1_oHzeN.js} +3 -3
- package/out/renderer/assets/{liquid-C9KVysk1.js → liquid-D4ftdy1G.js} +1 -1
- package/out/renderer/assets/{lspLanguageFeatures-IDD0-KWa.js → lspLanguageFeatures-Ckqu5ubG.js} +1 -1
- package/out/renderer/assets/{mdx-ascmBFXX.js → mdx-B9wsVYSC.js} +1 -1
- package/out/renderer/assets/{python-DcbM4Hy5.js → python-e23tD6IS.js} +1 -1
- package/out/renderer/assets/{razor-XJROxpYK.js → razor-CrDyXj3d.js} +1 -1
- package/out/renderer/assets/{tsMode-EXbzul3n.js → tsMode-BwGC5o4O.js} +1 -1
- package/out/renderer/assets/{typescript-DhTtFBLt.js → typescript-Do-98d_O.js} +1 -1
- package/out/renderer/assets/{xml-BLfEkVl1.js → xml-Dpdu5R3m.js} +1 -1
- package/out/renderer/assets/{yaml-DpzsqvWa.js → yaml-B0Zt7LCJ.js} +1 -1
- package/out/renderer/index.html +2 -2
- package/package.json +1 -1
- package/scripts/test-terminal-scroll.cjs +64 -0
- package/src/main/headless.ts +12 -0
- package/src/main/index.ts +8 -0
- package/src/main/monitor.ts +540 -0
- package/src/main/orchestrator.ts +10 -0
- package/src/main/pty.ts +5 -0
- package/src/main/tui.ts +37 -3
- package/src/preload/index.ts +4 -0
- package/src/renderer/App.tsx +9 -1
- package/src/renderer/components/Resources.tsx +217 -0
- package/src/renderer/components/StatusBar.tsx +6 -1
- package/src/renderer/components/TerminalPanel.tsx +54 -20
- package/src/renderer/styles.css +45 -0
|
@@ -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-Dm70hk5s.js";
|
|
2
2
|
class WorkerManager {
|
|
3
3
|
constructor(_modeId, _defaults) {
|
|
4
4
|
this._modeId = _modeId;
|
package/out/renderer/index.html
CHANGED
|
@@ -106,8 +106,8 @@
|
|
|
106
106
|
:root[data-theme='catppuccin-latte'] .boot-splash-title { color: #2f3145; }
|
|
107
107
|
:root[data-theme='catppuccin-latte'] .boot-splash-label { color: #7c7f93; }
|
|
108
108
|
</style>
|
|
109
|
-
<script type="module" crossorigin src="./assets/index-
|
|
110
|
-
<link rel="stylesheet" crossorigin href="./assets/index-
|
|
109
|
+
<script type="module" crossorigin src="./assets/index-Dm70hk5s.js"></script>
|
|
110
|
+
<link rel="stylesheet" crossorigin href="./assets/index-CEjwKjtd.css">
|
|
111
111
|
</head>
|
|
112
112
|
<body>
|
|
113
113
|
<div id="boot-splash">
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phenx-inc/ctlsurf",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
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": {
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// Exercise the actual React terminal panel and xterm parser in Electron.
|
|
2
|
+
// Run: node_modules/.bin/electron scripts/test-terminal-scroll.cjs
|
|
3
|
+
const { app, BrowserWindow } = require('electron')
|
|
4
|
+
const { build } = require('esbuild')
|
|
5
|
+
const fs = require('node:fs/promises')
|
|
6
|
+
const os = require('node:os')
|
|
7
|
+
const path = require('node:path')
|
|
8
|
+
app.on('window-all-closed', () => {})
|
|
9
|
+
app.on('child-process-gone', (_event, details) => console.error('Child process exited:', details))
|
|
10
|
+
|
|
11
|
+
async function main() {
|
|
12
|
+
console.log('Starting terminal scroll regression checks...')
|
|
13
|
+
const temp = await fs.mkdtemp(path.join(os.tmpdir(), 'ctlsurf-scroll-'))
|
|
14
|
+
app.setPath('userData', path.join(temp, 'profile'))
|
|
15
|
+
let win
|
|
16
|
+
try {
|
|
17
|
+
await build({
|
|
18
|
+
entryPoints: [path.join(__dirname, '../repro-terminal-scroll.tsx')],
|
|
19
|
+
outfile: path.join(temp, 'test.js'),
|
|
20
|
+
bundle: true,
|
|
21
|
+
platform: 'browser',
|
|
22
|
+
jsx: 'automatic',
|
|
23
|
+
external: ['@huggingface/transformers'],
|
|
24
|
+
})
|
|
25
|
+
await fs.writeFile(path.join(temp, 'index.html'), `<!doctype html><meta charset="UTF-8">
|
|
26
|
+
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; style-src 'self' 'unsafe-inline'">
|
|
27
|
+
<link rel="stylesheet" href="test.css">
|
|
28
|
+
<style>
|
|
29
|
+
html, body, #root { margin: 0; width: 100%; height: 100%; }
|
|
30
|
+
.terminal-wrap, .terminal-container, .xterm { width: 100%; height: 100%; }
|
|
31
|
+
.terminal-wrap { position: relative; }
|
|
32
|
+
.term-jump-latest { position: absolute; bottom: 0; right: 0; }
|
|
33
|
+
</style>
|
|
34
|
+
<div id="root"></div><script src="test.js"></script>`)
|
|
35
|
+
await app.whenReady()
|
|
36
|
+
win = new BrowserWindow({
|
|
37
|
+
show: false, width: 900, height: 500,
|
|
38
|
+
webPreferences: { backgroundThrottling: false },
|
|
39
|
+
})
|
|
40
|
+
win.webContents.on('console-message', (event) => {
|
|
41
|
+
if (event.level === 'error') console.error(event.message)
|
|
42
|
+
})
|
|
43
|
+
win.webContents.on('render-process-gone', (_event, details) => console.error('Renderer exited:', details))
|
|
44
|
+
await win.loadFile(path.join(temp, 'index.html'))
|
|
45
|
+
let timeout
|
|
46
|
+
const result = await Promise.race([
|
|
47
|
+
win.webContents.executeJavaScript('window.scrollTest'),
|
|
48
|
+
new Promise((_, reject) => {
|
|
49
|
+
timeout = setTimeout(() => reject(new Error('Scroll test timed out')), 15_000)
|
|
50
|
+
}),
|
|
51
|
+
]).finally(() => clearTimeout(timeout))
|
|
52
|
+
for (const line of result.checks) console.log(line)
|
|
53
|
+
if (result.error) throw new Error(result.error)
|
|
54
|
+
console.log('Terminal scroll regression checks passed.')
|
|
55
|
+
} finally {
|
|
56
|
+
win?.destroy()
|
|
57
|
+
await fs.rm(temp, { recursive: true, force: true, maxRetries: 5 })
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
main().then(() => app.exit(0), (err) => {
|
|
62
|
+
console.error(err)
|
|
63
|
+
app.exit(1)
|
|
64
|
+
})
|
package/src/main/headless.ts
CHANGED
|
@@ -31,6 +31,7 @@ import { Orchestrator } from './orchestrator'
|
|
|
31
31
|
import { getSettingsDir } from './settingsDir'
|
|
32
32
|
import { getBuiltinAgents, getAvailableAgents, isCodingAgent, type AgentConfig } from './agents'
|
|
33
33
|
import { Tui } from './tui'
|
|
34
|
+
import { ResourceMonitor, summarizeSnapshot } from './monitor'
|
|
34
35
|
import { fetchLatestNpmVersion, compareSemver, NPM_PACKAGE } from './updateCheck'
|
|
35
36
|
|
|
36
37
|
function getCurrentVersion(): string {
|
|
@@ -177,6 +178,16 @@ async function main() {
|
|
|
177
178
|
await orchestrator.spawnAgent(HEADLESS_TAB, agent, args.cwd, { trackTime: trackTimeOverride })
|
|
178
179
|
orchestrator.resizePty(HEADLESS_TAB, ptySize.cols, ptySize.rows)
|
|
179
180
|
|
|
181
|
+
// CPU / RAM and the agent's plan quota, appended to the tab title.
|
|
182
|
+
const monitor = new ResourceMonitor(() => orchestrator.getTabProcesses())
|
|
183
|
+
const refreshResources = () => {
|
|
184
|
+
monitor.snapshot()
|
|
185
|
+
.then(snap => tui.update({ resources: summarizeSnapshot(snap) }))
|
|
186
|
+
.catch(() => { /* keep the last title */ })
|
|
187
|
+
}
|
|
188
|
+
refreshResources()
|
|
189
|
+
setInterval(refreshResources, 10_000).unref()
|
|
190
|
+
|
|
180
191
|
// For coding agents, send an initial prompt to kick-start them
|
|
181
192
|
if (isCodingAgent(agent)) {
|
|
182
193
|
setTimeout(() => {
|
|
@@ -221,6 +232,7 @@ async function main() {
|
|
|
221
232
|
if (process.stdin.isTTY) {
|
|
222
233
|
process.stdin.setRawMode(false)
|
|
223
234
|
}
|
|
235
|
+
monitor.stop()
|
|
224
236
|
tui.destroy()
|
|
225
237
|
await orchestrator.shutdown()
|
|
226
238
|
process.exit(0)
|
package/src/main/index.ts
CHANGED
|
@@ -27,6 +27,7 @@ import { getSettingsDir } from './settingsDir'
|
|
|
27
27
|
import { Orchestrator } from './orchestrator'
|
|
28
28
|
import { AttentionController } from './agentState'
|
|
29
29
|
import { listSshHosts, getRecentHosts, addRecentHost, probeRemote, buildRemoteAgent, type RemoteAction } from './ssh'
|
|
30
|
+
import { ResourceMonitor } from './monitor'
|
|
30
31
|
|
|
31
32
|
const SETTINGS_DIR = getSettingsDir(true)
|
|
32
33
|
|
|
@@ -498,6 +499,12 @@ async function checkForUpdate(): Promise<void> {
|
|
|
498
499
|
ipcMain.handle('app:getVersion', () => app.getVersion())
|
|
499
500
|
ipcMain.handle('app:getUpdateInfo', () => updateInfo)
|
|
500
501
|
|
|
502
|
+
// ─── Resources + plan quota (status bar) ──────────
|
|
503
|
+
|
|
504
|
+
// Samples only while the renderer keeps asking; `fast` = the panel is open.
|
|
505
|
+
const resourceMonitor = new ResourceMonitor(() => orchestrator.getTabProcesses())
|
|
506
|
+
ipcMain.handle('monitor:snapshot', (_event, fast: boolean) => resourceMonitor.snapshot(!!fast))
|
|
507
|
+
|
|
501
508
|
// ─── Filesystem IPC ───────────────────────────────
|
|
502
509
|
|
|
503
510
|
ipcMain.handle('fs:readDir', async (_event, dirPath: string) => {
|
|
@@ -831,6 +838,7 @@ let shutdownStarted = false
|
|
|
831
838
|
function shutdownThenQuit(): void {
|
|
832
839
|
if (shutdownStarted) return
|
|
833
840
|
shutdownStarted = true
|
|
841
|
+
resourceMonitor.stop()
|
|
834
842
|
void Promise.race([
|
|
835
843
|
orchestrator.shutdown(),
|
|
836
844
|
new Promise((resolve) => setTimeout(resolve, 10_000)),
|