@phenx-inc/ctlsurf 0.15.0 → 0.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/out/headless/index.mjs +89 -71
- package/out/headless/index.mjs.map +3 -3
- package/out/main/index.js +90 -72
- package/out/preload/index.js +1 -1
- package/out/renderer/assets/{cssMode-CJR4iD-G.js → cssMode-B77M9HlN.js} +3 -3
- package/out/renderer/assets/{freemarker2-BRI9ISq9.js → freemarker2-oUT2zvyQ.js} +1 -1
- package/out/renderer/assets/{handlebars-3hL6XIZI.js → handlebars-jCckoi5j.js} +1 -1
- package/out/renderer/assets/{html-Dd5_vBoP.js → html-kAgrAaKW.js} +1 -1
- package/out/renderer/assets/{htmlMode-DgNtlKcG.js → htmlMode-DIWkIawy.js} +3 -3
- package/out/renderer/assets/{index-5azC4yNn.js → index-CN3UN7Tm.js} +32 -27
- package/out/renderer/assets/{javascript-D0vPLEJg.js → javascript-B9GYUE-I.js} +2 -2
- package/out/renderer/assets/{jsonMode-DBvR0zMu.js → jsonMode-BzFKTZjJ.js} +3 -3
- package/out/renderer/assets/{liquid-CLoc6Tx7.js → liquid-Ccw-S_Y6.js} +1 -1
- package/out/renderer/assets/{lspLanguageFeatures-CiaMaQoY.js → lspLanguageFeatures-BE0g7ubH.js} +1 -1
- package/out/renderer/assets/{mdx-DTsq6tlv.js → mdx-oXeAA0qU.js} +1 -1
- package/out/renderer/assets/{python-D04DpZDr.js → python-DlSX3TvA.js} +1 -1
- package/out/renderer/assets/{razor-Di6CJ6s8.js → razor-ButO9S7r.js} +1 -1
- package/out/renderer/assets/{tsMode-mfv6mHOv.js → tsMode-CTCpE-67.js} +1 -1
- package/out/renderer/assets/{typescript-CDA0Hhef.js → typescript-9o62WOnS.js} +1 -1
- package/out/renderer/assets/{xml-BU0voRuq.js → xml-C4nOH2_D.js} +1 -1
- package/out/renderer/assets/{yaml-qzFWnaZJ.js → yaml-mXYgKoWW.js} +1 -1
- package/out/renderer/index.html +1 -1
- package/package.json +1 -1
- package/src/main/index.ts +2 -2
- package/src/main/orchestrator.ts +2 -2
- package/src/main/ticketStore.ts +100 -66
- package/src/preload/index.ts +3 -3
- package/src/renderer/App.tsx +2 -2
- package/src/renderer/components/TicketPanel.tsx +16 -5
package/out/headless/index.mjs
CHANGED
|
@@ -17314,7 +17314,7 @@ var require_package = __commonJS({
|
|
|
17314
17314
|
"package.json"(exports, module) {
|
|
17315
17315
|
module.exports = {
|
|
17316
17316
|
name: "@phenx-inc/ctlsurf",
|
|
17317
|
-
version: "0.15.
|
|
17317
|
+
version: "0.15.1",
|
|
17318
17318
|
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",
|
|
17319
17319
|
main: "out/main/index.js",
|
|
17320
17320
|
bin: {
|
|
@@ -19105,21 +19105,19 @@ var COLUMNS2 = [
|
|
|
19105
19105
|
function log3(...args) {
|
|
19106
19106
|
log("[ticket-store]", ...args);
|
|
19107
19107
|
}
|
|
19108
|
-
function
|
|
19108
|
+
function findPagesByTitle(pages, title) {
|
|
19109
|
+
const found = [];
|
|
19109
19110
|
for (const p of pages) {
|
|
19110
|
-
if (p?.title === title)
|
|
19111
|
-
if (p?.children?.length)
|
|
19112
|
-
const c = findPageByTitle2(p.children, title);
|
|
19113
|
-
if (c) return c;
|
|
19114
|
-
}
|
|
19111
|
+
if (p?.title === title) found.push(p);
|
|
19112
|
+
if (p?.children?.length) found.push(...findPagesByTitle(p.children, title));
|
|
19115
19113
|
}
|
|
19116
|
-
return
|
|
19114
|
+
return found;
|
|
19117
19115
|
}
|
|
19118
19116
|
var TicketStore = class {
|
|
19119
19117
|
api;
|
|
19120
|
-
// Per-cwd cache of
|
|
19121
|
-
//
|
|
19122
|
-
//
|
|
19118
|
+
// Per-cwd cache of every resolved Tickets datastore. `primary` is where new
|
|
19119
|
+
// tickets are written and what gets handed to the coding agent; `all` covers
|
|
19120
|
+
// duplicate Agent Datastore pages so listing misses nothing.
|
|
19123
19121
|
dsCache = /* @__PURE__ */ new Map();
|
|
19124
19122
|
constructor(api) {
|
|
19125
19123
|
this.api = api;
|
|
@@ -19129,11 +19127,11 @@ var TicketStore = class {
|
|
|
19129
19127
|
const title = input.title?.trim();
|
|
19130
19128
|
if (!title) return { ok: false, error: "Title is required" };
|
|
19131
19129
|
try {
|
|
19132
|
-
const
|
|
19133
|
-
if (!
|
|
19130
|
+
const stores = await this.resolveStores(cwd, true);
|
|
19131
|
+
if (!stores) {
|
|
19134
19132
|
return { ok: false, error: "No ctlsurf project found for this folder" };
|
|
19135
19133
|
}
|
|
19136
|
-
await this.api.addRow(
|
|
19134
|
+
await this.api.addRow(stores.primary.blockId, {
|
|
19137
19135
|
Title: title,
|
|
19138
19136
|
Description: input.description?.trim() || "",
|
|
19139
19137
|
Status: input.status || "Open",
|
|
@@ -19147,14 +19145,15 @@ var TicketStore = class {
|
|
|
19147
19145
|
return { ok: false, error: err?.message || String(err) };
|
|
19148
19146
|
}
|
|
19149
19147
|
}
|
|
19150
|
-
/** Updates an existing ticket row
|
|
19151
|
-
|
|
19148
|
+
/** Updates an existing ticket row. `blockId` names the store the row lives
|
|
19149
|
+
* in (from listTickets); without it the primary store is assumed. */
|
|
19150
|
+
async updateTicket(cwd, rowId, input, blockId) {
|
|
19152
19151
|
const title = input.title?.trim();
|
|
19153
19152
|
if (!title) return { ok: false, error: "Title is required" };
|
|
19154
19153
|
try {
|
|
19155
|
-
const
|
|
19156
|
-
if (!
|
|
19157
|
-
await this.api.updateRow(
|
|
19154
|
+
const stores = await this.resolveStores(cwd, false);
|
|
19155
|
+
if (!stores) return { ok: false, error: "No Tickets datastore for this project" };
|
|
19156
|
+
await this.api.updateRow(blockId || stores.primary.blockId, rowId, {
|
|
19158
19157
|
Title: title,
|
|
19159
19158
|
Description: input.description?.trim() || "",
|
|
19160
19159
|
Status: input.status || "Open",
|
|
@@ -19167,28 +19166,39 @@ var TicketStore = class {
|
|
|
19167
19166
|
return { ok: false, error: err?.message || String(err) };
|
|
19168
19167
|
}
|
|
19169
19168
|
}
|
|
19170
|
-
/** Lists existing tickets
|
|
19171
|
-
*
|
|
19169
|
+
/** Lists existing tickets across every Tickets store in the project's
|
|
19170
|
+
* folder, newest first. Does not create the datastore — an unconfigured
|
|
19171
|
+
* project simply has no tickets yet. */
|
|
19172
19172
|
async listTickets(cwd) {
|
|
19173
19173
|
try {
|
|
19174
|
-
const
|
|
19175
|
-
if (!
|
|
19176
|
-
const
|
|
19177
|
-
const
|
|
19178
|
-
|
|
19179
|
-
|
|
19180
|
-
|
|
19181
|
-
|
|
19182
|
-
|
|
19183
|
-
|
|
19184
|
-
|
|
19185
|
-
|
|
19174
|
+
const stores = await this.resolveStores(cwd, false);
|
|
19175
|
+
if (!stores) return { ok: true, tickets: [] };
|
|
19176
|
+
const tickets = [];
|
|
19177
|
+
for (const ds of stores.all) {
|
|
19178
|
+
const res = await this.api.queryRows(ds.blockId, { orderBy: "Created", order: "desc", limit: 200 });
|
|
19179
|
+
for (const r of res?.rows || []) {
|
|
19180
|
+
tickets.push({
|
|
19181
|
+
id: r.id,
|
|
19182
|
+
title: String(r.data?.Title ?? ""),
|
|
19183
|
+
description: String(r.data?.Description ?? ""),
|
|
19184
|
+
status: String(r.data?.Status ?? "Open"),
|
|
19185
|
+
priority: String(r.data?.Priority ?? "Med"),
|
|
19186
|
+
created: r.data?.Created ?? r.created_at ?? null,
|
|
19187
|
+
blockId: ds.blockId,
|
|
19188
|
+
pageId: ds.pageId
|
|
19189
|
+
});
|
|
19190
|
+
}
|
|
19191
|
+
}
|
|
19192
|
+
if (stores.all.length > 1) {
|
|
19193
|
+
tickets.sort((a, b) => (b.created || "").localeCompare(a.created || ""));
|
|
19194
|
+
}
|
|
19195
|
+
return { ok: true, tickets, blockId: stores.primary.blockId, pageId: stores.primary.pageId };
|
|
19186
19196
|
} catch (err) {
|
|
19187
19197
|
log3(`listTickets failed for ${cwd}: ${err?.message || err}`);
|
|
19188
19198
|
return { ok: false, tickets: [], error: err?.message || String(err) };
|
|
19189
19199
|
}
|
|
19190
19200
|
}
|
|
19191
|
-
async
|
|
19201
|
+
async resolveStores(cwd, create) {
|
|
19192
19202
|
const cached = this.dsCache.get(cwd);
|
|
19193
19203
|
if (cached) return cached;
|
|
19194
19204
|
let folder = null;
|
|
@@ -19199,17 +19209,27 @@ var TicketStore = class {
|
|
|
19199
19209
|
}
|
|
19200
19210
|
if (!folder?.id) return null;
|
|
19201
19211
|
const folderDetail = await this.api.getFolder(folder.id);
|
|
19202
|
-
const
|
|
19203
|
-
if (
|
|
19204
|
-
const
|
|
19205
|
-
|
|
19206
|
-
if (
|
|
19207
|
-
|
|
19208
|
-
|
|
19209
|
-
|
|
19210
|
-
|
|
19212
|
+
const agentPages = findPagesByTitle(folderDetail?.pages || [], AGENT_DATASTORE_PAGE_TITLE2);
|
|
19213
|
+
if (agentPages.length === 0) return null;
|
|
19214
|
+
const rootPath = folder.root_path || cwd;
|
|
19215
|
+
agentPages.sort((a, b) => Number(b?.root_path === rootPath) - Number(a?.root_path === rootPath));
|
|
19216
|
+
if (agentPages.length > 1) {
|
|
19217
|
+
log3(`Found ${agentPages.length} "Agent Datastore" pages for ${cwd}; aggregating tickets across all of them`);
|
|
19218
|
+
}
|
|
19219
|
+
const all = [];
|
|
19220
|
+
for (const page of agentPages) {
|
|
19221
|
+
for (const blockId of await this.findTicketBlocks(page.id)) {
|
|
19222
|
+
all.push({ blockId, pageId: page.id });
|
|
19223
|
+
}
|
|
19224
|
+
}
|
|
19225
|
+
if (all.length > 0) {
|
|
19226
|
+
for (const ds of all) await this.ensureColumns(ds.blockId);
|
|
19227
|
+
const stores = { primary: all[0], all };
|
|
19228
|
+
this.dsCache.set(cwd, stores);
|
|
19229
|
+
return stores;
|
|
19211
19230
|
}
|
|
19212
19231
|
if (!create) return null;
|
|
19232
|
+
const pageId = agentPages[0].id;
|
|
19213
19233
|
const columns = COLUMNS2.map((c, i) => ({
|
|
19214
19234
|
id: `col_${i}`,
|
|
19215
19235
|
name: c.name,
|
|
@@ -19224,47 +19244,45 @@ var TicketStore = class {
|
|
|
19224
19244
|
if (created?.id) {
|
|
19225
19245
|
log3(`Created "${DATASTORE_TITLE2}" datastore on Agent Datastore page for ${cwd}`);
|
|
19226
19246
|
const ds = { blockId: created.id, pageId };
|
|
19227
|
-
|
|
19228
|
-
|
|
19247
|
+
const stores = { primary: ds, all: [ds] };
|
|
19248
|
+
this.dsCache.set(cwd, stores);
|
|
19249
|
+
return stores;
|
|
19229
19250
|
}
|
|
19230
19251
|
return null;
|
|
19231
19252
|
}
|
|
19232
|
-
/** Finds
|
|
19233
|
-
*
|
|
19234
|
-
*
|
|
19235
|
-
|
|
19253
|
+
/** Finds every Tickets block on the page: system_key matches first, then
|
|
19254
|
+
* title matches for legacy blocks. When no system_key block exists, the
|
|
19255
|
+
* first legacy block is adopted (system_key backfilled so it survives a
|
|
19256
|
+
* later rename); extra title matches are still listed but left unstamped so
|
|
19257
|
+
* the primary can't flip between sessions. */
|
|
19258
|
+
async findTicketBlocks(pageId) {
|
|
19236
19259
|
const summaries = await this.api.getPageBlockSummaries(pageId) || [];
|
|
19237
19260
|
const datastoreSummaries = summaries.filter((b) => b?.type === "datastore" && b?.id);
|
|
19238
|
-
if (datastoreSummaries.length === 0) return
|
|
19239
|
-
|
|
19240
|
-
|
|
19261
|
+
if (datastoreSummaries.length === 0) return [];
|
|
19262
|
+
const keyed = [];
|
|
19263
|
+
const legacy = [];
|
|
19241
19264
|
for (const s of datastoreSummaries) {
|
|
19242
19265
|
try {
|
|
19243
19266
|
const block = await this.api.getBlock(s.id);
|
|
19244
19267
|
const props = block?.props || {};
|
|
19245
|
-
if (props.system_key === SYSTEM_KEY2)
|
|
19246
|
-
|
|
19247
|
-
}
|
|
19248
|
-
if (s.title === DATASTORE_TITLE2 && titleFallbackId === null) {
|
|
19249
|
-
titleFallbackId = s.id;
|
|
19250
|
-
titleFallbackProps = props;
|
|
19251
|
-
}
|
|
19268
|
+
if (props.system_key === SYSTEM_KEY2) keyed.push(s.id);
|
|
19269
|
+
else if (s.title === DATASTORE_TITLE2) legacy.push({ id: s.id, props });
|
|
19252
19270
|
} catch (err) {
|
|
19253
19271
|
log3(`getBlock(${s.id}) failed during lookup: ${err?.message || err}`);
|
|
19254
19272
|
}
|
|
19255
19273
|
}
|
|
19256
|
-
if (
|
|
19274
|
+
if (keyed.length === 0 && legacy.length > 0) {
|
|
19275
|
+
const adopt = legacy[0];
|
|
19257
19276
|
try {
|
|
19258
|
-
await this.api.updateBlock(
|
|
19259
|
-
props: { ...
|
|
19277
|
+
await this.api.updateBlock(adopt.id, {
|
|
19278
|
+
props: { ...adopt.props, system_key: SYSTEM_KEY2 }
|
|
19260
19279
|
});
|
|
19261
|
-
log3(`Backfilled system_key on legacy Tickets block ${
|
|
19280
|
+
log3(`Backfilled system_key on legacy Tickets block ${adopt.id}`);
|
|
19262
19281
|
} catch (err) {
|
|
19263
|
-
log3(`backfill system_key failed on ${
|
|
19282
|
+
log3(`backfill system_key failed on ${adopt.id}: ${err?.message || err}`);
|
|
19264
19283
|
}
|
|
19265
|
-
return titleFallbackId;
|
|
19266
19284
|
}
|
|
19267
|
-
return
|
|
19285
|
+
return [...keyed, ...legacy.map((l) => l.id)];
|
|
19268
19286
|
}
|
|
19269
19287
|
async ensureColumns(blockId) {
|
|
19270
19288
|
try {
|
|
@@ -20085,11 +20103,11 @@ var COLUMNS3 = [
|
|
|
20085
20103
|
function log4(...args) {
|
|
20086
20104
|
log("[observer-store]", ...args);
|
|
20087
20105
|
}
|
|
20088
|
-
function
|
|
20106
|
+
function findPageByTitle2(pages, title) {
|
|
20089
20107
|
for (const p of pages) {
|
|
20090
20108
|
if (p?.title === title) return p;
|
|
20091
20109
|
if (p?.children?.length) {
|
|
20092
|
-
const c =
|
|
20110
|
+
const c = findPageByTitle2(p.children, title);
|
|
20093
20111
|
if (c) return c;
|
|
20094
20112
|
}
|
|
20095
20113
|
}
|
|
@@ -20158,7 +20176,7 @@ var ObserverStore = class {
|
|
|
20158
20176
|
}
|
|
20159
20177
|
if (!folder?.id) return null;
|
|
20160
20178
|
const folderDetail = await this.api.getFolder(folder.id);
|
|
20161
|
-
const agentPage =
|
|
20179
|
+
const agentPage = findPageByTitle2(folderDetail?.pages || [], AGENT_DATASTORE_PAGE_TITLE3);
|
|
20162
20180
|
if (!agentPage?.id) return null;
|
|
20163
20181
|
const summaries = await this.api.getPageBlockSummaries(agentPage.id) || [];
|
|
20164
20182
|
for (const s of summaries.filter((b) => b?.type === spec.type && b?.id)) {
|
|
@@ -21255,13 +21273,13 @@ var Orchestrator = class {
|
|
|
21255
21273
|
}
|
|
21256
21274
|
return this.ticketStore.addTicket(cwd, input);
|
|
21257
21275
|
}
|
|
21258
|
-
async updateTicketForActiveTab(rowId, input) {
|
|
21276
|
+
async updateTicketForActiveTab(rowId, input, blockId) {
|
|
21259
21277
|
const cwd = this.getActiveTabProjectPath();
|
|
21260
21278
|
if (!cwd) return { ok: false, error: "No active terminal tab" };
|
|
21261
21279
|
if (!this.ctlsurfApi.getApiKey()) {
|
|
21262
21280
|
return { ok: false, error: "ctlsurf API key not configured" };
|
|
21263
21281
|
}
|
|
21264
|
-
return this.ticketStore.updateTicket(cwd, rowId, input);
|
|
21282
|
+
return this.ticketStore.updateTicket(cwd, rowId, input, blockId);
|
|
21265
21283
|
}
|
|
21266
21284
|
async listTicketsForActiveTab() {
|
|
21267
21285
|
const cwd = this.getActiveTabProjectPath();
|