@phenx-inc/ctlsurf 0.12.1 → 0.13.4
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/LICENSE +22 -0
- package/out/headless/index.mjs +33 -36
- package/out/headless/index.mjs.map +3 -3
- package/out/main/index.js +47 -40
- package/out/preload/index.js +1 -0
- package/out/renderer/assets/{cssMode-D7r66wvO.js → cssMode-CAEObGNt.js} +3 -3
- package/out/renderer/assets/{freemarker2-jSWJIjPG.js → freemarker2-D63hnnDq.js} +1 -1
- package/out/renderer/assets/{handlebars-BnFyz5RH.js → handlebars-UYo_IYpi.js} +1 -1
- package/out/renderer/assets/{html-Bl5UFavp.js → html-2dvYDi3g.js} +1 -1
- package/out/renderer/assets/{htmlMode-D4VByBT6.js → htmlMode-B6fxDDPF.js} +3 -3
- package/out/renderer/assets/{index-Cc_TqOCL.css → index-BEekQe3-.css} +232 -8
- package/out/renderer/assets/{index-DxhW1emk.js → index-CMb4AqkZ.js} +786 -473
- package/out/renderer/assets/{javascript-CMN1OmwG.js → javascript-8RyFDDdk.js} +2 -2
- package/out/renderer/assets/{jsonMode-CJ5AYSPV.js → jsonMode-CqspkWCb.js} +3 -3
- package/out/renderer/assets/{liquid-BGMIDTEm.js → liquid-DaEnAsYY.js} +1 -1
- package/out/renderer/assets/{lspLanguageFeatures-DGifVYB5.js → lspLanguageFeatures-Czkp5sZb.js} +1 -1
- package/out/renderer/assets/{mdx-B2Z7R10Q.js → mdx-Q-qSZsFg.js} +1 -1
- package/out/renderer/assets/{python-LMDMSSdn.js → python-Sw0S0BJV.js} +1 -1
- package/out/renderer/assets/{razor-BZSrziPu.js → razor-C7zQmabz.js} +1 -1
- package/out/renderer/assets/{tsMode-DF9m6_3n.js → tsMode-BWdOxk6v.js} +1 -1
- package/out/renderer/assets/{typescript-rZeCMf0a.js → typescript-BrdIiKZf.js} +1 -1
- package/out/renderer/assets/{xml-fNSZ-7so.js → xml-DbKLNPOq.js} +1 -1
- package/out/renderer/assets/{yaml-1wxIuvzh.js → yaml-ehupJBsZ.js} +1 -1
- package/out/renderer/index.html +2 -2
- package/package.json +2 -2
- package/src/main/bridge.ts +3 -2
- package/src/main/index.ts +15 -0
- package/src/main/orchestrator.ts +1 -1
- package/src/main/pty.ts +2 -5
- package/src/main/ticketStore.ts +28 -21
- package/src/main/timeTracker.ts +4 -1
- package/src/main/transcripts.ts +3 -2
- package/src/preload/index.ts +2 -0
- package/src/renderer/App.tsx +15 -2
- package/src/renderer/components/CtlsurfPanel.tsx +169 -35
- package/src/renderer/components/SettingsDialog.tsx +13 -0
- package/src/renderer/components/SpeakControls.tsx +5 -0
- package/src/renderer/components/TerminalPanel.tsx +155 -48
- package/src/renderer/components/TicketPanel.tsx +80 -24
- package/src/renderer/lib/speech.ts +2 -1
- package/src/renderer/styles.css +232 -8
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright © 2026 Phenx, Inc. All rights reserved.
|
|
2
|
+
|
|
3
|
+
This software, including its source code, design, and documentation
|
|
4
|
+
(the "Software"), is the confidential and proprietary property of
|
|
5
|
+
Phenx, Inc. ("Phenx").
|
|
6
|
+
|
|
7
|
+
No license or right to use, copy, modify, merge, publish, distribute,
|
|
8
|
+
sublicense, sell, reverse engineer, decompile, or create derivative works
|
|
9
|
+
of the Software, in whole or in part, is granted except under a separate
|
|
10
|
+
written agreement signed by Phenx. Unauthorized use, reproduction, or
|
|
11
|
+
distribution is strictly prohibited and may result in civil and criminal
|
|
12
|
+
penalties.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
17
|
+
PHENX BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY ARISING FROM OR
|
|
18
|
+
IN CONNECTION WITH THE SOFTWARE OR ITS USE.
|
|
19
|
+
|
|
20
|
+
Third-party components distributed with the Software are licensed under
|
|
21
|
+
their own terms by their respective authors; those licenses continue to
|
|
22
|
+
apply to those components.
|
package/out/headless/index.mjs
CHANGED
|
@@ -5471,7 +5471,7 @@ var require_package = __commonJS({
|
|
|
5471
5471
|
"package.json"(exports, module) {
|
|
5472
5472
|
module.exports = {
|
|
5473
5473
|
name: "@phenx-inc/ctlsurf",
|
|
5474
|
-
version: "0.
|
|
5474
|
+
version: "0.13.4",
|
|
5475
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
5476
|
main: "out/main/index.js",
|
|
5477
5477
|
bin: {
|
|
@@ -5509,7 +5509,7 @@ var require_package = __commonJS({
|
|
|
5509
5509
|
"node-pty",
|
|
5510
5510
|
"xterm"
|
|
5511
5511
|
],
|
|
5512
|
-
license: "
|
|
5512
|
+
license: "SEE LICENSE IN LICENSE",
|
|
5513
5513
|
engines: {
|
|
5514
5514
|
node: ">=18"
|
|
5515
5515
|
},
|
|
@@ -5578,10 +5578,7 @@ var PtyManager = class {
|
|
|
5578
5578
|
constructor(agent, cwd) {
|
|
5579
5579
|
const shell = agent.command;
|
|
5580
5580
|
const args = agent.args || [];
|
|
5581
|
-
|
|
5582
|
-
console.log(`[pty] Spawning: ${shell} ${args.join(" ")} in ${cwd}`);
|
|
5583
|
-
} catch {
|
|
5584
|
-
}
|
|
5581
|
+
log(`[pty] Spawning: ${shell} ${args.join(" ")} in ${cwd}`);
|
|
5585
5582
|
this.process = pty.spawn(shell, args, {
|
|
5586
5583
|
name: "xterm-256color",
|
|
5587
5584
|
cwd,
|
|
@@ -5847,7 +5844,7 @@ var ConversationBridge = class {
|
|
|
5847
5844
|
this.inputBuffer = "";
|
|
5848
5845
|
this.terminalCapture.reset();
|
|
5849
5846
|
this.sessionActive = true;
|
|
5850
|
-
|
|
5847
|
+
log("[bridge] Session started");
|
|
5851
5848
|
}
|
|
5852
5849
|
feedOutput(data) {
|
|
5853
5850
|
if (!this.sessionActive || !this.loggingEnabled) return;
|
|
@@ -5927,7 +5924,7 @@ var ConversationBridge = class {
|
|
|
5927
5924
|
this.sessionActive = false;
|
|
5928
5925
|
this.inputBuffer = "";
|
|
5929
5926
|
this.outputBuffer = "";
|
|
5930
|
-
|
|
5927
|
+
log("[bridge] Session ended");
|
|
5931
5928
|
}
|
|
5932
5929
|
};
|
|
5933
5930
|
var DEFAULT_COLS = 80;
|
|
@@ -6141,7 +6138,7 @@ var TranscriptTailer = class {
|
|
|
6141
6138
|
this.sinceMs = Date.now();
|
|
6142
6139
|
this.files.clear();
|
|
6143
6140
|
this.pollTimer = setInterval(() => this.poll(), POLL_INTERVAL_MS);
|
|
6144
|
-
|
|
6141
|
+
log(`[transcripts] Tailing ${this.agentId} transcripts for ${this.cwd}`);
|
|
6145
6142
|
}
|
|
6146
6143
|
stop() {
|
|
6147
6144
|
if (!this.pollTimer) return;
|
|
@@ -6149,7 +6146,7 @@ var TranscriptTailer = class {
|
|
|
6149
6146
|
this.pollTimer = null;
|
|
6150
6147
|
this.poll();
|
|
6151
6148
|
this.files.clear();
|
|
6152
|
-
|
|
6149
|
+
log("[transcripts] Stopped");
|
|
6153
6150
|
}
|
|
6154
6151
|
poll() {
|
|
6155
6152
|
try {
|
|
@@ -6952,10 +6949,7 @@ function isSameLocalDay(a, b) {
|
|
|
6952
6949
|
return da.getFullYear() === db.getFullYear() && da.getMonth() === db.getMonth() && da.getDate() === db.getDate();
|
|
6953
6950
|
}
|
|
6954
6951
|
function log2(...args) {
|
|
6955
|
-
|
|
6956
|
-
console.log("[time-tracker]", ...args);
|
|
6957
|
-
} catch {
|
|
6958
|
-
}
|
|
6952
|
+
log("[time-tracker]", ...args);
|
|
6959
6953
|
}
|
|
6960
6954
|
function findPageByTitle(pages, title) {
|
|
6961
6955
|
for (const p of pages) {
|
|
@@ -7244,10 +7238,7 @@ var COLUMNS2 = [
|
|
|
7244
7238
|
{ name: "Created", type: "date" }
|
|
7245
7239
|
];
|
|
7246
7240
|
function log3(...args) {
|
|
7247
|
-
|
|
7248
|
-
console.log("[ticket-store]", ...args);
|
|
7249
|
-
} catch {
|
|
7250
|
-
}
|
|
7241
|
+
log("[ticket-store]", ...args);
|
|
7251
7242
|
}
|
|
7252
7243
|
function findPageByTitle2(pages, title) {
|
|
7253
7244
|
for (const p of pages) {
|
|
@@ -7261,7 +7252,10 @@ function findPageByTitle2(pages, title) {
|
|
|
7261
7252
|
}
|
|
7262
7253
|
var TicketStore = class {
|
|
7263
7254
|
api;
|
|
7264
|
-
|
|
7255
|
+
// Per-cwd cache of the resolved Tickets datastore: the block (the datastore
|
|
7256
|
+
// element) and the Agent Datastore page it lives on. Both ids are handed to
|
|
7257
|
+
// the coding agent so it can read/update tickets via the ctlsurf MCP tools.
|
|
7258
|
+
dsCache = /* @__PURE__ */ new Map();
|
|
7265
7259
|
constructor(api) {
|
|
7266
7260
|
this.api = api;
|
|
7267
7261
|
}
|
|
@@ -7270,11 +7264,11 @@ var TicketStore = class {
|
|
|
7270
7264
|
const title = input.title?.trim();
|
|
7271
7265
|
if (!title) return { ok: false, error: "Title is required" };
|
|
7272
7266
|
try {
|
|
7273
|
-
const
|
|
7274
|
-
if (!
|
|
7267
|
+
const ds = await this.ensureDatastore(cwd, true);
|
|
7268
|
+
if (!ds) {
|
|
7275
7269
|
return { ok: false, error: "No ctlsurf project found for this folder" };
|
|
7276
7270
|
}
|
|
7277
|
-
await this.api.addRow(blockId, {
|
|
7271
|
+
await this.api.addRow(ds.blockId, {
|
|
7278
7272
|
Title: title,
|
|
7279
7273
|
Description: input.description?.trim() || "",
|
|
7280
7274
|
Status: input.status || "Open",
|
|
@@ -7293,9 +7287,9 @@ var TicketStore = class {
|
|
|
7293
7287
|
const title = input.title?.trim();
|
|
7294
7288
|
if (!title) return { ok: false, error: "Title is required" };
|
|
7295
7289
|
try {
|
|
7296
|
-
const
|
|
7297
|
-
if (!
|
|
7298
|
-
await this.api.updateRow(blockId, rowId, {
|
|
7290
|
+
const ds = await this.ensureDatastore(cwd, false);
|
|
7291
|
+
if (!ds) return { ok: false, error: "No Tickets datastore for this project" };
|
|
7292
|
+
await this.api.updateRow(ds.blockId, rowId, {
|
|
7299
7293
|
Title: title,
|
|
7300
7294
|
Description: input.description?.trim() || "",
|
|
7301
7295
|
Status: input.status || "Open",
|
|
@@ -7312,9 +7306,9 @@ var TicketStore = class {
|
|
|
7312
7306
|
* datastore — an unconfigured project simply has no tickets yet. */
|
|
7313
7307
|
async listTickets(cwd) {
|
|
7314
7308
|
try {
|
|
7315
|
-
const
|
|
7316
|
-
if (!
|
|
7317
|
-
const res = await this.api.queryRows(blockId, { orderBy: "Created", order: "desc", limit: 200 });
|
|
7309
|
+
const ds = await this.ensureDatastore(cwd, false);
|
|
7310
|
+
if (!ds) return { ok: true, tickets: [] };
|
|
7311
|
+
const res = await this.api.queryRows(ds.blockId, { orderBy: "Created", order: "desc", limit: 200 });
|
|
7318
7312
|
const tickets = (res?.rows || []).map((r) => ({
|
|
7319
7313
|
id: r.id,
|
|
7320
7314
|
title: String(r.data?.Title ?? ""),
|
|
@@ -7323,14 +7317,14 @@ var TicketStore = class {
|
|
|
7323
7317
|
priority: String(r.data?.Priority ?? "Med"),
|
|
7324
7318
|
created: r.data?.Created ?? r.created_at ?? null
|
|
7325
7319
|
}));
|
|
7326
|
-
return { ok: true, tickets };
|
|
7320
|
+
return { ok: true, tickets, blockId: ds.blockId, pageId: ds.pageId };
|
|
7327
7321
|
} catch (err) {
|
|
7328
7322
|
log3(`listTickets failed for ${cwd}: ${err?.message || err}`);
|
|
7329
7323
|
return { ok: false, tickets: [], error: err?.message || String(err) };
|
|
7330
7324
|
}
|
|
7331
7325
|
}
|
|
7332
7326
|
async ensureDatastore(cwd, create) {
|
|
7333
|
-
const cached = this.
|
|
7327
|
+
const cached = this.dsCache.get(cwd);
|
|
7334
7328
|
if (cached) return cached;
|
|
7335
7329
|
let folder = null;
|
|
7336
7330
|
try {
|
|
@@ -7342,11 +7336,13 @@ var TicketStore = class {
|
|
|
7342
7336
|
const folderDetail = await this.api.getFolder(folder.id);
|
|
7343
7337
|
const agentPage = findPageByTitle2(folderDetail?.pages || [], AGENT_DATASTORE_PAGE_TITLE2);
|
|
7344
7338
|
if (!agentPage?.id) return null;
|
|
7345
|
-
const
|
|
7339
|
+
const pageId = agentPage.id;
|
|
7340
|
+
const blockId = await this.findOrAdoptBlock(pageId);
|
|
7346
7341
|
if (blockId) {
|
|
7347
7342
|
await this.ensureColumns(blockId);
|
|
7348
|
-
|
|
7349
|
-
|
|
7343
|
+
const ds = { blockId, pageId };
|
|
7344
|
+
this.dsCache.set(cwd, ds);
|
|
7345
|
+
return ds;
|
|
7350
7346
|
}
|
|
7351
7347
|
if (!create) return null;
|
|
7352
7348
|
const columns = COLUMNS2.map((c, i) => ({
|
|
@@ -7355,15 +7351,16 @@ var TicketStore = class {
|
|
|
7355
7351
|
type: c.type,
|
|
7356
7352
|
...c.options ? { options: c.options } : {}
|
|
7357
7353
|
}));
|
|
7358
|
-
const created = await this.api.createBlock(
|
|
7354
|
+
const created = await this.api.createBlock(pageId, {
|
|
7359
7355
|
type: "datastore",
|
|
7360
7356
|
title: DATASTORE_TITLE2,
|
|
7361
7357
|
props: { columns, system_key: SYSTEM_KEY2 }
|
|
7362
7358
|
});
|
|
7363
7359
|
if (created?.id) {
|
|
7364
7360
|
log3(`Created "${DATASTORE_TITLE2}" datastore on Agent Datastore page for ${cwd}`);
|
|
7365
|
-
|
|
7366
|
-
|
|
7361
|
+
const ds = { blockId: created.id, pageId };
|
|
7362
|
+
this.dsCache.set(cwd, ds);
|
|
7363
|
+
return ds;
|
|
7367
7364
|
}
|
|
7368
7365
|
return null;
|
|
7369
7366
|
}
|