@phenx-inc/ctlsurf 0.3.7 → 0.3.9
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 +254 -13
- package/out/headless/index.mjs.map +3 -3
- package/out/main/index.js +130 -51
- package/out/preload/index.js +3 -0
- package/out/renderer/assets/{cssMode-DQW-brNd.js → cssMode-CYoo4t9f.js} +3 -3
- package/out/renderer/assets/{freemarker2-DxgOckH2.js → freemarker2--UQnPZsn.js} +1 -1
- package/out/renderer/assets/{handlebars-BX1Wpk_3.js → handlebars-DVDrmX0C.js} +1 -1
- package/out/renderer/assets/{html-t-KXioI0.js → html-D1-cXoLy.js} +1 -1
- package/out/renderer/assets/{htmlMode-Dya7iUjr.js → htmlMode-f5nBuprq.js} +3 -3
- package/out/renderer/assets/{index-D6JBcQ20.css → index-65hyKM_8.css} +16 -0
- package/out/renderer/assets/{index-DNqZidnO.js → index-D23nru43.js} +64 -23
- package/out/renderer/assets/{javascript-DZzW2adn.js → javascript-CcarFzBL.js} +2 -2
- package/out/renderer/assets/{jsonMode-D_Wv7XH8.js → jsonMode-BvF-xK9U.js} +3 -3
- package/out/renderer/assets/{liquid-BJAHAm2T.js → liquid-CHLtUKl2.js} +1 -1
- package/out/renderer/assets/{lspLanguageFeatures-BgMd-KJk.js → lspLanguageFeatures-B9aNeatS.js} +1 -1
- package/out/renderer/assets/{mdx-B6Zod3ry.js → mdx-HGDrkifZ.js} +1 -1
- package/out/renderer/assets/{python-Cgt13-KH.js → python-B_dPzjJ6.js} +1 -1
- package/out/renderer/assets/{razor-BcwFJGYS.js → razor-CHheM4ot.js} +1 -1
- package/out/renderer/assets/{tsMode-BTjzM6fl.js → tsMode-CdC3i1gG.js} +1 -1
- package/out/renderer/assets/{typescript-DZYDQEUb.js → typescript-BX6guVRK.js} +1 -1
- package/out/renderer/assets/{xml-CloiUoIW.js → xml-CpS-pOPE.js} +1 -1
- package/out/renderer/assets/{yaml-CdKdpE-z.js → yaml-Du0AjOHW.js} +1 -1
- package/out/renderer/index.html +2 -2
- package/package.json +1 -1
- package/src/main/bridge.ts +9 -3
- package/src/main/headless.ts +35 -2
- package/src/main/index.ts +10 -39
- package/src/main/orchestrator.ts +74 -1
- package/src/main/tui.ts +20 -8
- package/src/main/updateCheck.ts +40 -0
- package/src/preload/index.ts +6 -0
- package/src/renderer/App.tsx +2 -0
- package/src/renderer/components/AgentPicker.tsx +38 -3
- package/src/renderer/styles.css +16 -0
package/out/headless/index.mjs
CHANGED
|
@@ -5466,6 +5466,88 @@ var require_electron = __commonJS({
|
|
|
5466
5466
|
}
|
|
5467
5467
|
});
|
|
5468
5468
|
|
|
5469
|
+
// package.json
|
|
5470
|
+
var require_package = __commonJS({
|
|
5471
|
+
"package.json"(exports, module) {
|
|
5472
|
+
module.exports = {
|
|
5473
|
+
name: "@phenx-inc/ctlsurf",
|
|
5474
|
+
version: "0.3.9",
|
|
5475
|
+
description: "Agent-agnostic terminal and desktop app for ctlsurf \u2014 run Claude Code, Codex, or any coding agent with live session logging and remote control",
|
|
5476
|
+
main: "out/main/index.js",
|
|
5477
|
+
bin: {
|
|
5478
|
+
ctlsurf: "./bin/ctlsurf-worker.js",
|
|
5479
|
+
"ctlsurf-worker": "./bin/ctlsurf-worker.js"
|
|
5480
|
+
},
|
|
5481
|
+
files: [
|
|
5482
|
+
"bin/",
|
|
5483
|
+
"out/",
|
|
5484
|
+
"resources/",
|
|
5485
|
+
"scripts/",
|
|
5486
|
+
"src/",
|
|
5487
|
+
"package.json",
|
|
5488
|
+
"electron-vite.config.ts",
|
|
5489
|
+
"tsconfig*.json"
|
|
5490
|
+
],
|
|
5491
|
+
scripts: {
|
|
5492
|
+
dev: "electron-vite dev",
|
|
5493
|
+
"dev:terminal": "esbuild src/main/headless.ts --bundle --platform=node --target=node18 --format=esm --outfile=out/headless/index.mjs --external:node-pty --external:ws --sourcemap && node out/headless/index.mjs",
|
|
5494
|
+
build: "electron-vite build && npm run build:headless",
|
|
5495
|
+
"build:headless": "esbuild src/main/headless.ts --bundle --platform=node --target=node18 --format=esm --outfile=out/headless/index.mjs --external:node-pty --external:ws --sourcemap",
|
|
5496
|
+
prepublishOnly: "npm run build",
|
|
5497
|
+
preview: "electron-vite preview",
|
|
5498
|
+
package: "electron-builder",
|
|
5499
|
+
postinstall: "node-gyp rebuild --directory=node_modules/node-pty 2>/dev/null || true"
|
|
5500
|
+
},
|
|
5501
|
+
keywords: [
|
|
5502
|
+
"ctlsurf",
|
|
5503
|
+
"terminal",
|
|
5504
|
+
"tui",
|
|
5505
|
+
"coding-agent",
|
|
5506
|
+
"claude-code",
|
|
5507
|
+
"codex",
|
|
5508
|
+
"electron",
|
|
5509
|
+
"node-pty",
|
|
5510
|
+
"xterm"
|
|
5511
|
+
],
|
|
5512
|
+
license: "MIT",
|
|
5513
|
+
engines: {
|
|
5514
|
+
node: ">=18"
|
|
5515
|
+
},
|
|
5516
|
+
dependencies: {
|
|
5517
|
+
"@monaco-editor/react": "^4.7.0",
|
|
5518
|
+
"@xterm/addon-fit": "^0.10.0",
|
|
5519
|
+
"@xterm/addon-serialize": "^0.14.0",
|
|
5520
|
+
"@xterm/addon-web-links": "^0.11.0",
|
|
5521
|
+
"@xterm/headless": "^6.0.0",
|
|
5522
|
+
"@xterm/xterm": "^5.5.0",
|
|
5523
|
+
"electron-store": "^10.0.0",
|
|
5524
|
+
esbuild: "^0.27.4",
|
|
5525
|
+
"monaco-editor": "^0.55.1",
|
|
5526
|
+
"node-pty": "^1.0.0",
|
|
5527
|
+
ws: "^8.20.0"
|
|
5528
|
+
},
|
|
5529
|
+
devDependencies: {
|
|
5530
|
+
"@electron/rebuild": "^4.0.3",
|
|
5531
|
+
"@types/node": "^22.15.0",
|
|
5532
|
+
"@types/react": "^19.1.0",
|
|
5533
|
+
"@types/react-dom": "^19.1.0",
|
|
5534
|
+
"@types/ws": "^8.18.1",
|
|
5535
|
+
"@vitejs/plugin-react": "^4.5.2",
|
|
5536
|
+
electron: "^35.0.0",
|
|
5537
|
+
"electron-builder": "^25.1.8",
|
|
5538
|
+
"electron-vite": "^3.1.0",
|
|
5539
|
+
react: "^19.1.0",
|
|
5540
|
+
"react-dom": "^19.1.0",
|
|
5541
|
+
typescript: "^5.8.3"
|
|
5542
|
+
},
|
|
5543
|
+
optionalDependencies: {
|
|
5544
|
+
bufferutil: "^4.1.0",
|
|
5545
|
+
"utf-8-validate": "^6.0.6"
|
|
5546
|
+
}
|
|
5547
|
+
};
|
|
5548
|
+
}
|
|
5549
|
+
});
|
|
5550
|
+
|
|
5469
5551
|
// src/main/orchestrator.ts
|
|
5470
5552
|
import path from "path";
|
|
5471
5553
|
import fs from "fs";
|
|
@@ -5682,6 +5764,7 @@ var import_addon_serialize = __toESM(require_addon_serialize());
|
|
|
5682
5764
|
var ConversationBridge = class {
|
|
5683
5765
|
wsClient = null;
|
|
5684
5766
|
sessionActive = false;
|
|
5767
|
+
loggingEnabled = false;
|
|
5685
5768
|
inputBuffer = "";
|
|
5686
5769
|
outputBuffer = "";
|
|
5687
5770
|
outputFlushTimer = null;
|
|
@@ -5691,7 +5774,11 @@ var ConversationBridge = class {
|
|
|
5691
5774
|
setWsClient(ws) {
|
|
5692
5775
|
this.wsClient = ws;
|
|
5693
5776
|
}
|
|
5777
|
+
setLoggingEnabled(enabled) {
|
|
5778
|
+
this.loggingEnabled = enabled;
|
|
5779
|
+
}
|
|
5694
5780
|
startSession() {
|
|
5781
|
+
if (!this.loggingEnabled) return;
|
|
5695
5782
|
this.clearOutputTimers();
|
|
5696
5783
|
this.outputBuffer = "";
|
|
5697
5784
|
this.inputBuffer = "";
|
|
@@ -5700,13 +5787,13 @@ var ConversationBridge = class {
|
|
|
5700
5787
|
console.log("[bridge] Session started");
|
|
5701
5788
|
}
|
|
5702
5789
|
feedOutput(data) {
|
|
5703
|
-
if (!this.sessionActive) return;
|
|
5790
|
+
if (!this.sessionActive || !this.loggingEnabled) return;
|
|
5704
5791
|
this.outputBuffer += data;
|
|
5705
5792
|
this.terminalCapture.write(data);
|
|
5706
5793
|
this.scheduleOutputFlush();
|
|
5707
5794
|
}
|
|
5708
5795
|
feedInput(data) {
|
|
5709
|
-
if (!this.sessionActive) return;
|
|
5796
|
+
if (!this.sessionActive || !this.loggingEnabled) return;
|
|
5710
5797
|
this.inputBuffer += data;
|
|
5711
5798
|
if (data.includes("\r") || data.includes("\n")) {
|
|
5712
5799
|
const cleaned = cleanInput(this.inputBuffer);
|
|
@@ -5728,7 +5815,7 @@ var ConversationBridge = class {
|
|
|
5728
5815
|
this.sendEntry("terminal_output", cleaned);
|
|
5729
5816
|
}
|
|
5730
5817
|
sendEntry(type, content) {
|
|
5731
|
-
if (!this.wsClient) return;
|
|
5818
|
+
if (!this.wsClient || !this.loggingEnabled) return;
|
|
5732
5819
|
this.wsClient.sendChatLog({
|
|
5733
5820
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5734
5821
|
type,
|
|
@@ -6532,6 +6619,7 @@ var DEFAULT_PROFILES = {
|
|
|
6532
6619
|
}
|
|
6533
6620
|
};
|
|
6534
6621
|
var TERM_STREAM_INTERVAL_MS = 50;
|
|
6622
|
+
var NO_PROJECT_POLL_MS = 5e3;
|
|
6535
6623
|
var Orchestrator = class {
|
|
6536
6624
|
settingsDir;
|
|
6537
6625
|
events;
|
|
@@ -6547,8 +6635,11 @@ var Orchestrator = class {
|
|
|
6547
6635
|
currentCwd = null;
|
|
6548
6636
|
settings = {
|
|
6549
6637
|
activeProfile: "production",
|
|
6550
|
-
profiles: { ...DEFAULT_PROFILES }
|
|
6638
|
+
profiles: { ...DEFAULT_PROFILES },
|
|
6639
|
+
logChat: false
|
|
6551
6640
|
};
|
|
6641
|
+
noProjectPollTimer = null;
|
|
6642
|
+
noProjectPollCwd = null;
|
|
6552
6643
|
constructor(settingsDir, events) {
|
|
6553
6644
|
this.settingsDir = settingsDir;
|
|
6554
6645
|
this.events = events;
|
|
@@ -6574,6 +6665,11 @@ var Orchestrator = class {
|
|
|
6574
6665
|
events.onWorkerRegistered(data);
|
|
6575
6666
|
if (!data.folder_id) {
|
|
6576
6667
|
events.onWorkerStatus("no_project");
|
|
6668
|
+
if (this.currentCwd && data.status !== "pending_approval") {
|
|
6669
|
+
this.startNoProjectPolling(this.currentCwd);
|
|
6670
|
+
}
|
|
6671
|
+
} else {
|
|
6672
|
+
this.stopNoProjectPolling();
|
|
6577
6673
|
}
|
|
6578
6674
|
},
|
|
6579
6675
|
onTerminalInput: (data) => {
|
|
@@ -6582,6 +6678,21 @@ var Orchestrator = class {
|
|
|
6582
6678
|
}
|
|
6583
6679
|
});
|
|
6584
6680
|
this.bridge.setWsClient(this.workerWs);
|
|
6681
|
+
this.bridge.setLoggingEnabled(!!this.settings.logChat);
|
|
6682
|
+
}
|
|
6683
|
+
// ─── Chat logging ───────────────────────────────
|
|
6684
|
+
get logChatEnabled() {
|
|
6685
|
+
return !!this.settings.logChat;
|
|
6686
|
+
}
|
|
6687
|
+
setLogChat(enabled) {
|
|
6688
|
+
this.settings.logChat = enabled;
|
|
6689
|
+
this.saveSettings();
|
|
6690
|
+
this.bridge.setLoggingEnabled(enabled);
|
|
6691
|
+
if (!enabled) {
|
|
6692
|
+
this.bridge.endSession();
|
|
6693
|
+
} else if (this.activeTabId) {
|
|
6694
|
+
this.bridge.startSession();
|
|
6695
|
+
}
|
|
6585
6696
|
}
|
|
6586
6697
|
// ─── Settings ───────────────────────────────────
|
|
6587
6698
|
getActiveProfile() {
|
|
@@ -6629,7 +6740,8 @@ var Orchestrator = class {
|
|
|
6629
6740
|
baseUrl: raw.ctlsurfBaseUrl || "https://app.ctlsurf.com",
|
|
6630
6741
|
dataspacePageId: raw.ctlsurfDataspacePageId || ""
|
|
6631
6742
|
}
|
|
6632
|
-
}
|
|
6743
|
+
},
|
|
6744
|
+
logChat: !!raw.logChat
|
|
6633
6745
|
};
|
|
6634
6746
|
this.saveSettings();
|
|
6635
6747
|
log3("[settings] Migrated legacy settings to profiles");
|
|
@@ -6638,15 +6750,20 @@ var Orchestrator = class {
|
|
|
6638
6750
|
if (!this.settings.profiles.production) {
|
|
6639
6751
|
this.settings.profiles.production = { ...DEFAULT_PROFILES.production };
|
|
6640
6752
|
}
|
|
6753
|
+
if (this.settings.logChat === void 0) {
|
|
6754
|
+
this.settings.logChat = false;
|
|
6755
|
+
}
|
|
6641
6756
|
}
|
|
6642
6757
|
}
|
|
6643
6758
|
} catch {
|
|
6644
6759
|
this.settings = {
|
|
6645
6760
|
activeProfile: "production",
|
|
6646
|
-
profiles: { ...DEFAULT_PROFILES }
|
|
6761
|
+
profiles: { ...DEFAULT_PROFILES },
|
|
6762
|
+
logChat: false
|
|
6647
6763
|
};
|
|
6648
6764
|
}
|
|
6649
6765
|
this.applyProfile(this.getActiveProfile());
|
|
6766
|
+
this.bridge.setLoggingEnabled(!!this.settings.logChat);
|
|
6650
6767
|
}
|
|
6651
6768
|
saveSettings() {
|
|
6652
6769
|
const settingsPath = path.join(this.settingsDir, "settings.json");
|
|
@@ -6776,7 +6893,9 @@ var Orchestrator = class {
|
|
|
6776
6893
|
const t = this.tabs.get(tabId);
|
|
6777
6894
|
if (t?.termStreamTimer) clearTimeout(t.termStreamTimer);
|
|
6778
6895
|
});
|
|
6779
|
-
this.
|
|
6896
|
+
if (this.settings.logChat) {
|
|
6897
|
+
this.bridge.startSession();
|
|
6898
|
+
}
|
|
6780
6899
|
const profile = this.getActiveProfile();
|
|
6781
6900
|
const shouldTrack = opts?.trackTime !== void 0 ? opts.trackTime : profile.trackTime !== false;
|
|
6782
6901
|
if (shouldTrack) {
|
|
@@ -6790,6 +6909,7 @@ var Orchestrator = class {
|
|
|
6790
6909
|
if (isCodingAgent(agent)) {
|
|
6791
6910
|
this.connectWorkerWs(agent, cwd);
|
|
6792
6911
|
} else {
|
|
6912
|
+
this.stopNoProjectPolling();
|
|
6793
6913
|
this.workerWs.disconnect();
|
|
6794
6914
|
this.checkProjectStatus(cwd);
|
|
6795
6915
|
}
|
|
@@ -6864,12 +6984,47 @@ var Orchestrator = class {
|
|
|
6864
6984
|
log3("[worker-ws] No API key, skipping WS connect");
|
|
6865
6985
|
return;
|
|
6866
6986
|
}
|
|
6987
|
+
this.stopNoProjectPolling();
|
|
6867
6988
|
this.workerWs.connect({
|
|
6868
6989
|
machine: os3.hostname(),
|
|
6869
6990
|
cwd,
|
|
6870
6991
|
agent: agent.name
|
|
6871
6992
|
});
|
|
6872
6993
|
}
|
|
6994
|
+
startNoProjectPolling(cwd) {
|
|
6995
|
+
if (this.noProjectPollTimer && this.noProjectPollCwd === cwd) return;
|
|
6996
|
+
this.stopNoProjectPolling();
|
|
6997
|
+
this.noProjectPollCwd = cwd;
|
|
6998
|
+
log3(`[worker-ws] Polling for project folder at ${cwd}`);
|
|
6999
|
+
this.noProjectPollTimer = setInterval(() => {
|
|
7000
|
+
void this.checkForProjectFolder(cwd);
|
|
7001
|
+
}, NO_PROJECT_POLL_MS);
|
|
7002
|
+
}
|
|
7003
|
+
stopNoProjectPolling() {
|
|
7004
|
+
if (this.noProjectPollTimer) {
|
|
7005
|
+
clearInterval(this.noProjectPollTimer);
|
|
7006
|
+
this.noProjectPollTimer = null;
|
|
7007
|
+
this.noProjectPollCwd = null;
|
|
7008
|
+
}
|
|
7009
|
+
}
|
|
7010
|
+
async checkForProjectFolder(cwd) {
|
|
7011
|
+
if (this.currentCwd !== cwd || !this.currentAgent) {
|
|
7012
|
+
this.stopNoProjectPolling();
|
|
7013
|
+
return;
|
|
7014
|
+
}
|
|
7015
|
+
if (!this.ctlsurfApi.getApiKey()) return;
|
|
7016
|
+
try {
|
|
7017
|
+
const folder = await this.ctlsurfApi.findFolderByPath(cwd);
|
|
7018
|
+
if (folder?.id && this.currentCwd === cwd && this.currentAgent) {
|
|
7019
|
+
log3(`[worker-ws] Project folder appeared (${folder.id}); reconnecting`);
|
|
7020
|
+
const agent = this.currentAgent;
|
|
7021
|
+
this.stopNoProjectPolling();
|
|
7022
|
+
this.workerWs.disconnect();
|
|
7023
|
+
this.connectWorkerWs(agent, cwd);
|
|
7024
|
+
}
|
|
7025
|
+
} catch {
|
|
7026
|
+
}
|
|
7027
|
+
}
|
|
6873
7028
|
async checkProjectStatus(cwd) {
|
|
6874
7029
|
if (!this.ctlsurfApi.getApiKey()) {
|
|
6875
7030
|
this.events.onWorkerStatus("no_project");
|
|
@@ -6900,6 +7055,7 @@ var Orchestrator = class {
|
|
|
6900
7055
|
}
|
|
6901
7056
|
// ─── Shutdown ───────────────────────────────────
|
|
6902
7057
|
async shutdown() {
|
|
7058
|
+
this.stopNoProjectPolling();
|
|
6903
7059
|
this.bridge.endSession();
|
|
6904
7060
|
await this.timeTracker.endAll();
|
|
6905
7061
|
for (const [, tab] of this.tabs) {
|
|
@@ -7034,8 +7190,9 @@ var Tui = class {
|
|
|
7034
7190
|
return new Promise((resolve) => {
|
|
7035
7191
|
let selected = 0;
|
|
7036
7192
|
let trackTime = options.initialTrackTime;
|
|
7193
|
+
let logChat = options.initialLogChat;
|
|
7037
7194
|
const modalWidth = 44;
|
|
7038
|
-
const modalHeight = agents.length + 4 +
|
|
7195
|
+
const modalHeight = agents.length + 4 + 3;
|
|
7039
7196
|
const startCol = Math.max(1, Math.floor((this.cols - modalWidth) / 2));
|
|
7040
7197
|
const startRow = Math.max(1, Math.floor((this.rows - modalHeight) / 2));
|
|
7041
7198
|
this.write(`${CSI}?1049h`);
|
|
@@ -7079,9 +7236,16 @@ var Tui = class {
|
|
|
7079
7236
|
const trackContentLen = 2 + 3 + 1 + "Track time".length;
|
|
7080
7237
|
const trackPad = " ".repeat(Math.max(0, modalWidth - 2 - trackContentLen));
|
|
7081
7238
|
this.write(`${CSI}${trackRow};${startCol}H${BG_MODAL}${FG_DIM}\u2502${RESET}${BG_MODAL}${trackContent}${trackPad}${FG_DIM}\u2502${RESET}`);
|
|
7082
|
-
const
|
|
7239
|
+
const logRow = trackRow + 1;
|
|
7240
|
+
const logCheckbox = logChat ? `${FG_GREEN}[\u2713]${RESET}${BG_MODAL}` : `${FG_DIM}[ ]${RESET}${BG_MODAL}`;
|
|
7241
|
+
const logLabelFg = logChat ? FG_WHITE : FG_DIM;
|
|
7242
|
+
const logContent = ` ${logCheckbox} ${logLabelFg}Log chat${RESET}${BG_MODAL}`;
|
|
7243
|
+
const logContentLen = 2 + 3 + 1 + "Log chat".length;
|
|
7244
|
+
const logPad = " ".repeat(Math.max(0, modalWidth - 2 - logContentLen));
|
|
7245
|
+
this.write(`${CSI}${logRow};${startCol}H${BG_MODAL}${FG_DIM}\u2502${RESET}${BG_MODAL}${logContent}${logPad}${FG_DIM}\u2502${RESET}`);
|
|
7246
|
+
const botRow = logRow + 1;
|
|
7083
7247
|
this.write(`${CSI}${botRow};${startCol}H${BG_MODAL}${FG_DIM}${botBorder}${RESET}`);
|
|
7084
|
-
const hint = "\u2191\u2193
|
|
7248
|
+
const hint = "\u2191\u2193 nav \xB7 Enter \xB7 t track \xB7 l log \xB7 q quit";
|
|
7085
7249
|
const hintCol = Math.max(1, Math.floor((this.cols - hint.length) / 2));
|
|
7086
7250
|
this.write(`${CSI}${botRow + 2};${hintCol}H${FG_DIM}${hint}${RESET}`);
|
|
7087
7251
|
};
|
|
@@ -7098,12 +7262,15 @@ var Tui = class {
|
|
|
7098
7262
|
} else if (key === "\x1B[B" || key === "j") {
|
|
7099
7263
|
selected = (selected + 1) % agents.length;
|
|
7100
7264
|
drawModal();
|
|
7101
|
-
} else if (key === "t" || key === "T"
|
|
7265
|
+
} else if (key === "t" || key === "T") {
|
|
7102
7266
|
trackTime = !trackTime;
|
|
7103
7267
|
drawModal();
|
|
7268
|
+
} else if (key === "l" || key === "L") {
|
|
7269
|
+
logChat = !logChat;
|
|
7270
|
+
drawModal();
|
|
7104
7271
|
} else if (key === "\r" || key === "\n") {
|
|
7105
7272
|
cleanup();
|
|
7106
|
-
resolve({ agentIdx: selected, trackTime });
|
|
7273
|
+
resolve({ agentIdx: selected, trackTime, logChat });
|
|
7107
7274
|
} else if (key === "q" || key === "\x1B" || key === "") {
|
|
7108
7275
|
cleanup();
|
|
7109
7276
|
this.write(`${CSI}?25h`);
|
|
@@ -7170,6 +7337,50 @@ var Tui = class {
|
|
|
7170
7337
|
}
|
|
7171
7338
|
};
|
|
7172
7339
|
|
|
7340
|
+
// src/main/updateCheck.ts
|
|
7341
|
+
import https from "https";
|
|
7342
|
+
var NPM_PACKAGE = "@phenx-inc/ctlsurf";
|
|
7343
|
+
function compareSemver(a, b) {
|
|
7344
|
+
const pa = a.split(".").map((n) => parseInt(n, 10) || 0);
|
|
7345
|
+
const pb = b.split(".").map((n) => parseInt(n, 10) || 0);
|
|
7346
|
+
for (let i = 0; i < 3; i++) {
|
|
7347
|
+
const ai = pa[i] || 0;
|
|
7348
|
+
const bi = pb[i] || 0;
|
|
7349
|
+
if (ai !== bi) return ai - bi;
|
|
7350
|
+
}
|
|
7351
|
+
return 0;
|
|
7352
|
+
}
|
|
7353
|
+
function fetchLatestNpmVersion(timeoutMs = 8e3) {
|
|
7354
|
+
return new Promise((resolve) => {
|
|
7355
|
+
const url = `https://registry.npmjs.org/${encodeURIComponent(NPM_PACKAGE)}/latest`;
|
|
7356
|
+
const req = https.get(url, { headers: { "Accept": "application/json" } }, (res) => {
|
|
7357
|
+
if (res.statusCode !== 200) {
|
|
7358
|
+
res.resume();
|
|
7359
|
+
resolve(null);
|
|
7360
|
+
return;
|
|
7361
|
+
}
|
|
7362
|
+
let body = "";
|
|
7363
|
+
res.setEncoding("utf8");
|
|
7364
|
+
res.on("data", (chunk) => {
|
|
7365
|
+
body += chunk;
|
|
7366
|
+
});
|
|
7367
|
+
res.on("end", () => {
|
|
7368
|
+
try {
|
|
7369
|
+
const json = JSON.parse(body);
|
|
7370
|
+
resolve(typeof json?.version === "string" ? json.version : null);
|
|
7371
|
+
} catch {
|
|
7372
|
+
resolve(null);
|
|
7373
|
+
}
|
|
7374
|
+
});
|
|
7375
|
+
});
|
|
7376
|
+
req.on("error", () => resolve(null));
|
|
7377
|
+
req.setTimeout(timeoutMs, () => {
|
|
7378
|
+
req.destroy();
|
|
7379
|
+
resolve(null);
|
|
7380
|
+
});
|
|
7381
|
+
});
|
|
7382
|
+
}
|
|
7383
|
+
|
|
7173
7384
|
// src/main/headless.ts
|
|
7174
7385
|
process.stdout?.on?.("error", () => {
|
|
7175
7386
|
});
|
|
@@ -7182,6 +7393,14 @@ process.on("uncaughtException", (err) => {
|
|
|
7182
7393
|
} catch {
|
|
7183
7394
|
}
|
|
7184
7395
|
});
|
|
7396
|
+
function getCurrentVersion() {
|
|
7397
|
+
try {
|
|
7398
|
+
const pkg = require_package();
|
|
7399
|
+
return typeof pkg?.version === "string" ? pkg.version : "0.0.0";
|
|
7400
|
+
} catch {
|
|
7401
|
+
return "0.0.0";
|
|
7402
|
+
}
|
|
7403
|
+
}
|
|
7185
7404
|
function parseArgs(argv) {
|
|
7186
7405
|
const args = {
|
|
7187
7406
|
agent: null,
|
|
@@ -7222,9 +7441,27 @@ function parseArgs(argv) {
|
|
|
7222
7441
|
}
|
|
7223
7442
|
return args;
|
|
7224
7443
|
}
|
|
7444
|
+
async function checkVersionAndNotify() {
|
|
7445
|
+
const current = getCurrentVersion();
|
|
7446
|
+
const latest = await fetchLatestNpmVersion(3e3);
|
|
7447
|
+
if (!latest) return;
|
|
7448
|
+
if (compareSemver(latest, current) <= 0) return;
|
|
7449
|
+
const Y = "\x1B[33m";
|
|
7450
|
+
const G = "\x1B[32m";
|
|
7451
|
+
const D = "\x1B[90m";
|
|
7452
|
+
const R = "\x1B[0m";
|
|
7453
|
+
process.stdout.write(
|
|
7454
|
+
`
|
|
7455
|
+
${Y}A new version of ctlsurf is available${R} ${D}(${current} \u2192 ${latest})${R}
|
|
7456
|
+
Update: ${G}npm i -g ${NPM_PACKAGE}${R}
|
|
7457
|
+
|
|
7458
|
+
`
|
|
7459
|
+
);
|
|
7460
|
+
}
|
|
7225
7461
|
async function main() {
|
|
7226
7462
|
const args = parseArgs(process.argv.slice(2));
|
|
7227
7463
|
const settingsDir = getSettingsDir(false);
|
|
7464
|
+
await checkVersionAndNotify();
|
|
7228
7465
|
const tui = new Tui();
|
|
7229
7466
|
const agents = getBuiltinAgents();
|
|
7230
7467
|
const orchestrator = new Orchestrator(settingsDir, {
|
|
@@ -7265,9 +7502,13 @@ async function main() {
|
|
|
7265
7502
|
};
|
|
7266
7503
|
} else {
|
|
7267
7504
|
const initialTrackTime = orchestrator.getActiveProfile().trackTime !== false;
|
|
7268
|
-
const
|
|
7505
|
+
const initialLogChat = orchestrator.logChatEnabled;
|
|
7506
|
+
const picked = await tui.showAgentPicker(agents, { initialTrackTime, initialLogChat });
|
|
7269
7507
|
agent = agents[picked.agentIdx];
|
|
7270
7508
|
trackTimeOverride = picked.trackTime;
|
|
7509
|
+
if (picked.logChat !== orchestrator.logChatEnabled) {
|
|
7510
|
+
orchestrator.setLogChat(picked.logChat);
|
|
7511
|
+
}
|
|
7271
7512
|
}
|
|
7272
7513
|
tui.update({
|
|
7273
7514
|
agentName: agent.name,
|