opencode-zellij 0.0.13 → 0.0.15
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/dist/index.mjs +310 -174
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -1
package/dist/index.mjs
CHANGED
|
@@ -39,7 +39,7 @@ async function installedPackageMetadata(installRoot) {
|
|
|
39
39
|
try {
|
|
40
40
|
const content = await readFile(join(packageDir(installRoot), "package.json"), "utf8");
|
|
41
41
|
const pkg = JSON.parse(content);
|
|
42
|
-
if (isRecord$
|
|
42
|
+
if (isRecord$2(pkg)) return {
|
|
43
43
|
name: typeof pkg.name === "string" ? pkg.name : void 0,
|
|
44
44
|
version: typeof pkg.version === "string" ? pkg.version : void 0,
|
|
45
45
|
main: typeof pkg.main === "string" ? pkg.main : void 0
|
|
@@ -107,7 +107,7 @@ async function findInstallContext(importMetaUrl) {
|
|
|
107
107
|
try {
|
|
108
108
|
const content = await readFile(packageJsonPath, "utf8");
|
|
109
109
|
const pkg = JSON.parse(content);
|
|
110
|
-
if (isRecord$
|
|
110
|
+
if (isRecord$2(pkg) && pkg.name === "opencode-zellij" && typeof pkg.version === "string" && pkg.version.length > 0) {
|
|
111
111
|
const installRoot = dirname(dirname(dir));
|
|
112
112
|
if (existsSync(join(installRoot, "package.json"))) return {
|
|
113
113
|
installRoot,
|
|
@@ -124,7 +124,7 @@ async function findInstallContext(importMetaUrl) {
|
|
|
124
124
|
dir = parent;
|
|
125
125
|
}
|
|
126
126
|
}
|
|
127
|
-
function isRecord$
|
|
127
|
+
function isRecord$2(value) {
|
|
128
128
|
return typeof value === "object" && value !== null;
|
|
129
129
|
}
|
|
130
130
|
function isAutoUpdatableSpec(spec) {
|
|
@@ -143,7 +143,7 @@ async function fetchLatestVersion(fetchImpl = globalThis.fetch) {
|
|
|
143
143
|
return;
|
|
144
144
|
}
|
|
145
145
|
const data = await response.json();
|
|
146
|
-
if (isRecord$
|
|
146
|
+
if (isRecord$2(data) && typeof data.latest === "string") return data.latest;
|
|
147
147
|
debug("npm registry response missing latest tag");
|
|
148
148
|
return;
|
|
149
149
|
} catch (cause) {
|
|
@@ -584,7 +584,7 @@ function numericProperty(object, keys) {
|
|
|
584
584
|
}
|
|
585
585
|
}
|
|
586
586
|
}
|
|
587
|
-
function stringProperty$
|
|
587
|
+
function stringProperty$2(object, keys) {
|
|
588
588
|
for (const key of keys) {
|
|
589
589
|
const value = object[key];
|
|
590
590
|
if (typeof value === "string") return value;
|
|
@@ -643,7 +643,7 @@ function parsePaneExists(listPanesJson, paneId) {
|
|
|
643
643
|
function tabNameProperty(object, tabId) {
|
|
644
644
|
if (tabId === void 0) return void 0;
|
|
645
645
|
if (numericProperty(object, ["tab_id", "tabId"]) !== tabId) return void 0;
|
|
646
|
-
const name = stringProperty$
|
|
646
|
+
const name = stringProperty$2(object, ["name", "title"]);
|
|
647
647
|
return typeof name === "string" ? name : void 0;
|
|
648
648
|
}
|
|
649
649
|
function findTabName(value, tabId) {
|
|
@@ -1099,6 +1099,7 @@ var SubscriberManager = class {
|
|
|
1099
1099
|
startingSessions = /* @__PURE__ */ new Map();
|
|
1100
1100
|
spawnProcess;
|
|
1101
1101
|
dumpScreen;
|
|
1102
|
+
paneExists;
|
|
1102
1103
|
closePane;
|
|
1103
1104
|
lifecycleHooks;
|
|
1104
1105
|
terminalTailLines;
|
|
@@ -1107,6 +1108,7 @@ var SubscriberManager = class {
|
|
|
1107
1108
|
this.maxBufferLines = maxBufferLines;
|
|
1108
1109
|
this.spawnProcess = dependencies.spawn ?? spawn;
|
|
1109
1110
|
this.dumpScreen = dependencies.dumpScreen ?? zellijCli.dumpScreen;
|
|
1111
|
+
this.paneExists = dependencies.paneExists ?? zellijCli.paneExists;
|
|
1110
1112
|
this.closePane = dependencies.closePane ?? zellijCli.closePane;
|
|
1111
1113
|
this.lifecycleHooks = dependencies.lifecycleHooks;
|
|
1112
1114
|
this.terminalTailLines = dependencies.terminalTailLines ?? 200;
|
|
@@ -1276,8 +1278,7 @@ var SubscriberManager = class {
|
|
|
1276
1278
|
handleStderr(sessionId, child, chunk) {
|
|
1277
1279
|
const state = this.subscribers.get(sessionId);
|
|
1278
1280
|
if (!state || state.child !== child) return;
|
|
1279
|
-
|
|
1280
|
-
if (state.stderr.length > maxStderrLines) state.stderr = state.stderr.slice(state.stderr.length - maxStderrLines);
|
|
1281
|
+
this.appendStderr(state, ...splitLines(chunk));
|
|
1281
1282
|
}
|
|
1282
1283
|
handleSubscriberExit(sessionId, child) {
|
|
1283
1284
|
const state = this.subscribers.get(sessionId);
|
|
@@ -1285,18 +1286,46 @@ var SubscriberManager = class {
|
|
|
1285
1286
|
if (state.child !== child) return;
|
|
1286
1287
|
state.child = null;
|
|
1287
1288
|
state.lastExitedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1288
|
-
if (this.sessions.find(sessionId)?.status !== "terminal")
|
|
1289
|
-
|
|
1289
|
+
if (this.sessions.find(sessionId)?.status !== "terminal") this.appendStderr(state, `[zellij-pty] subscriber exited at ${state.lastExitedAt}; last buffered output is retained.`);
|
|
1290
|
+
this.reconcileSubscriberTermination(sessionId, state, "subscriber_exit");
|
|
1290
1291
|
}
|
|
1291
1292
|
handleSubscriberError(sessionId, child, error) {
|
|
1292
1293
|
const state = this.subscribers.get(sessionId);
|
|
1293
1294
|
if (state?.child === child) {
|
|
1294
|
-
|
|
1295
|
+
this.appendStderr(state, error.message);
|
|
1295
1296
|
state.child = null;
|
|
1296
1297
|
state.lastExitedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1297
|
-
this.sessions.updateStatus(sessionId, "unknown");
|
|
1298
|
+
if (this.sessions.find(sessionId)?.status !== "terminal") this.sessions.updateStatus(sessionId, "unknown");
|
|
1299
|
+
this.reconcileSubscriberTermination(sessionId, state, "subscriber_error");
|
|
1298
1300
|
}
|
|
1299
1301
|
}
|
|
1302
|
+
appendStderr(state, ...lines) {
|
|
1303
|
+
state.stderr.push(...lines);
|
|
1304
|
+
if (state.stderr.length > maxStderrLines) state.stderr = state.stderr.slice(state.stderr.length - maxStderrLines);
|
|
1305
|
+
}
|
|
1306
|
+
async reconcileSubscriberTermination(sessionId, state, reason) {
|
|
1307
|
+
const session = this.sessions.find(sessionId);
|
|
1308
|
+
if (!session || session.status === "terminal" || this.subscribers.get(sessionId) !== state || state.child) return;
|
|
1309
|
+
let paneExists;
|
|
1310
|
+
try {
|
|
1311
|
+
paneExists = await this.paneExists(session.paneId);
|
|
1312
|
+
} catch (error) {
|
|
1313
|
+
const latestState = this.subscribers.get(sessionId);
|
|
1314
|
+
const latestSession = this.sessions.find(sessionId);
|
|
1315
|
+
if (!latestState || latestState !== state || latestState.child || !latestSession || latestSession.status === "terminal") return;
|
|
1316
|
+
this.appendStderr(latestState, `[zellij-pty] ${reason} reconciliation could not verify pane ${latestSession.paneId}: ${errorMessage(error)}`);
|
|
1317
|
+
return;
|
|
1318
|
+
}
|
|
1319
|
+
const latestState = this.subscribers.get(sessionId);
|
|
1320
|
+
const latestSession = this.sessions.find(sessionId);
|
|
1321
|
+
if (!latestState || latestState !== state || latestState.child || !latestSession || latestSession.status === "terminal") return;
|
|
1322
|
+
if (paneExists === false) {
|
|
1323
|
+
this.markSessionTerminal(sessionId, reason);
|
|
1324
|
+
unregisterPaneFromWatchdog(sessionId);
|
|
1325
|
+
return;
|
|
1326
|
+
}
|
|
1327
|
+
if (paneExists === void 0) this.appendStderr(latestState, `[zellij-pty] ${reason} reconciliation could not confirm whether pane ${latestSession.paneId} still exists; leaving session non-terminal.`);
|
|
1328
|
+
}
|
|
1300
1329
|
markSessionTerminal(sessionId, reason, input = {}) {
|
|
1301
1330
|
const state = this.subscribers.get(sessionId);
|
|
1302
1331
|
if (!state) return;
|
|
@@ -2226,41 +2255,21 @@ function exitAfterCleanup(signal, code) {
|
|
|
2226
2255
|
//#endregion
|
|
2227
2256
|
//#region src/zellij/tab-title-events.ts
|
|
2228
2257
|
const execFileAsync = promisify(execFile);
|
|
2229
|
-
function isRecord(value) {
|
|
2258
|
+
function isRecord$1(value) {
|
|
2230
2259
|
return typeof value === "object" && value !== null;
|
|
2231
2260
|
}
|
|
2232
|
-
function stringProperty(object, key) {
|
|
2261
|
+
function stringProperty$1(object, key) {
|
|
2233
2262
|
const value = object[key];
|
|
2234
2263
|
return typeof value === "string" ? value : void 0;
|
|
2235
2264
|
}
|
|
2236
|
-
function nestedStringProperty(object, key, nestedKey) {
|
|
2265
|
+
function nestedStringProperty$1(object, key, nestedKey) {
|
|
2237
2266
|
const nested = object[key];
|
|
2238
|
-
if (!isRecord(nested)) return void 0;
|
|
2239
|
-
return stringProperty(nested, nestedKey);
|
|
2267
|
+
if (!isRecord$1(nested)) return void 0;
|
|
2268
|
+
return stringProperty$1(nested, nestedKey);
|
|
2240
2269
|
}
|
|
2241
|
-
function
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
if (status.type === "idle" || status.type === "busy") return { type: status.type };
|
|
2245
|
-
if (status.type === "retry") return {
|
|
2246
|
-
type: "retry",
|
|
2247
|
-
attempt: typeof status.attempt === "number" ? status.attempt : 0,
|
|
2248
|
-
message: typeof status.message === "string" ? status.message : "",
|
|
2249
|
-
next: typeof status.next === "number" ? status.next : 0
|
|
2250
|
-
};
|
|
2251
|
-
}
|
|
2252
|
-
function inputRequestID(object) {
|
|
2253
|
-
return stringProperty(object, "id") ?? stringProperty(object, "requestID") ?? stringProperty(object, "permissionID");
|
|
2254
|
-
}
|
|
2255
|
-
function inputState(object) {
|
|
2256
|
-
return (stringProperty(object, "status") ?? stringProperty(object, "state") ?? stringProperty(object, "type"))?.toLowerCase();
|
|
2257
|
-
}
|
|
2258
|
-
function isResolvedInputState(state) {
|
|
2259
|
-
return state === "approved" || state === "denied" || state === "rejected" || state === "resolved" || state === "replied";
|
|
2260
|
-
}
|
|
2261
|
-
function deletedSessionID(event) {
|
|
2262
|
-
if (!isRecord(event.properties)) return void 0;
|
|
2263
|
-
return nestedStringProperty(event.properties, "info", "id") ?? stringProperty(event.properties, "sessionID");
|
|
2270
|
+
function deletedSessionID$1(event) {
|
|
2271
|
+
if (!isRecord$1(event.properties)) return void 0;
|
|
2272
|
+
return nestedStringProperty$1(event.properties, "info", "id") ?? stringProperty$1(event.properties, "sessionID");
|
|
2264
2273
|
}
|
|
2265
2274
|
async function readGitBranch(worktree) {
|
|
2266
2275
|
return (await execFileAsync("git", [
|
|
@@ -2285,70 +2294,6 @@ async function getInitialBranch(worktree, readBranch = readGitBranch) {
|
|
|
2285
2294
|
function shouldReadInitialBranch(zellij) {
|
|
2286
2295
|
return Boolean(zellij);
|
|
2287
2296
|
}
|
|
2288
|
-
function handleTabTitleEvent(tabTitleManager, event) {
|
|
2289
|
-
if (event.type === "server.instance.disposed" || event.type === "global.disposed") return tabTitleManager.destroy?.();
|
|
2290
|
-
if (!isRecord(event.properties)) return;
|
|
2291
|
-
const properties = event.properties;
|
|
2292
|
-
switch (event.type) {
|
|
2293
|
-
case "session.status": {
|
|
2294
|
-
const sessionID = stringProperty(properties, "sessionID");
|
|
2295
|
-
const status = sessionStatusProperty(properties);
|
|
2296
|
-
if (sessionID && status) if (status.type === "idle") tabTitleManager.markSessionIdle(sessionID);
|
|
2297
|
-
else tabTitleManager.updateSessionStatus(sessionID, status);
|
|
2298
|
-
break;
|
|
2299
|
-
}
|
|
2300
|
-
case "session.idle": {
|
|
2301
|
-
const sessionID = stringProperty(properties, "sessionID");
|
|
2302
|
-
if (sessionID) tabTitleManager.markSessionIdle(sessionID);
|
|
2303
|
-
break;
|
|
2304
|
-
}
|
|
2305
|
-
case "session.error": {
|
|
2306
|
-
const sessionID = stringProperty(properties, "sessionID");
|
|
2307
|
-
if (sessionID) tabTitleManager.markSessionIdle(sessionID);
|
|
2308
|
-
break;
|
|
2309
|
-
}
|
|
2310
|
-
case "vcs.branch.updated":
|
|
2311
|
-
tabTitleManager.setBranch(stringProperty(properties, "branch"));
|
|
2312
|
-
break;
|
|
2313
|
-
case "question.asked":
|
|
2314
|
-
case "permission.asked": {
|
|
2315
|
-
const id = inputRequestID(properties);
|
|
2316
|
-
const sessionID = stringProperty(properties, "sessionID");
|
|
2317
|
-
if (id && sessionID) {
|
|
2318
|
-
tabTitleManager.markNeedsInput(id, sessionID);
|
|
2319
|
-
tabTitleManager.updateSessionStatus(sessionID, { type: "busy" });
|
|
2320
|
-
}
|
|
2321
|
-
break;
|
|
2322
|
-
}
|
|
2323
|
-
case "permission.updated": {
|
|
2324
|
-
const id = inputRequestID(properties);
|
|
2325
|
-
const sessionID = stringProperty(properties, "sessionID");
|
|
2326
|
-
const state = inputState(properties);
|
|
2327
|
-
if (id && isResolvedInputState(state)) {
|
|
2328
|
-
tabTitleManager.clearNeedsInput(id);
|
|
2329
|
-
if (sessionID) tabTitleManager.updateSessionStatus(sessionID, { type: "busy" });
|
|
2330
|
-
} else if (id && sessionID) {
|
|
2331
|
-
tabTitleManager.markNeedsInput(id, sessionID);
|
|
2332
|
-
tabTitleManager.updateSessionStatus(sessionID, { type: "busy" });
|
|
2333
|
-
}
|
|
2334
|
-
break;
|
|
2335
|
-
}
|
|
2336
|
-
case "question.replied":
|
|
2337
|
-
case "question.rejected":
|
|
2338
|
-
case "permission.replied": {
|
|
2339
|
-
const id = inputRequestID(properties);
|
|
2340
|
-
const sessionID = stringProperty(properties, "sessionID");
|
|
2341
|
-
if (id) tabTitleManager.clearNeedsInput(id);
|
|
2342
|
-
if (sessionID) tabTitleManager.updateSessionStatus(sessionID, { type: "busy" });
|
|
2343
|
-
break;
|
|
2344
|
-
}
|
|
2345
|
-
case "session.deleted": {
|
|
2346
|
-
const sessionID = deletedSessionID(event);
|
|
2347
|
-
if (sessionID) tabTitleManager.removeSession(sessionID);
|
|
2348
|
-
break;
|
|
2349
|
-
}
|
|
2350
|
-
}
|
|
2351
|
-
}
|
|
2352
2297
|
//#endregion
|
|
2353
2298
|
//#region src/zellij/tab-title.ts
|
|
2354
2299
|
const defaultTabTitleEmojis = {
|
|
@@ -2367,12 +2312,240 @@ function sanitizeTitle(title, maxLength = 90) {
|
|
|
2367
2312
|
if (chars.length > maxLength) cleaned = `${chars.slice(0, maxLength - 1).join("")}…`;
|
|
2368
2313
|
return cleaned;
|
|
2369
2314
|
}
|
|
2370
|
-
|
|
2315
|
+
function isRecord(value) {
|
|
2316
|
+
return typeof value === "object" && value !== null;
|
|
2317
|
+
}
|
|
2318
|
+
function stringProperty(object, key) {
|
|
2319
|
+
const value = object[key];
|
|
2320
|
+
return typeof value === "string" ? value : void 0;
|
|
2321
|
+
}
|
|
2322
|
+
function nestedStringProperty(object, key, nestedKey) {
|
|
2323
|
+
const nested = object[key];
|
|
2324
|
+
if (!isRecord(nested)) return void 0;
|
|
2325
|
+
return stringProperty(nested, nestedKey);
|
|
2326
|
+
}
|
|
2327
|
+
function sessionStatusType(properties) {
|
|
2328
|
+
const status = properties.status;
|
|
2329
|
+
if (!isRecord(status)) return void 0;
|
|
2330
|
+
const type = status.type;
|
|
2331
|
+
if (type === "idle" || type === "busy") return type;
|
|
2332
|
+
if (type === "retry") return "retry";
|
|
2333
|
+
}
|
|
2334
|
+
function inputRequestID(properties) {
|
|
2335
|
+
return stringProperty(properties, "id") ?? stringProperty(properties, "requestID") ?? stringProperty(properties, "permissionID");
|
|
2336
|
+
}
|
|
2337
|
+
function inputState(properties) {
|
|
2338
|
+
return (stringProperty(properties, "status") ?? stringProperty(properties, "state") ?? stringProperty(properties, "type"))?.toLowerCase();
|
|
2339
|
+
}
|
|
2340
|
+
function isResolvedInputState(state) {
|
|
2341
|
+
return state === "approved" || state === "denied" || state === "rejected" || state === "resolved" || state === "replied";
|
|
2342
|
+
}
|
|
2343
|
+
function deletedSessionID(properties) {
|
|
2344
|
+
return nestedStringProperty(properties, "info", "id") ?? stringProperty(properties, "sessionID");
|
|
2345
|
+
}
|
|
2346
|
+
var TabTitleIdentityModel = class {
|
|
2347
|
+
ready;
|
|
2348
|
+
projectName;
|
|
2349
|
+
branchName;
|
|
2350
|
+
worktree;
|
|
2351
|
+
readBranch;
|
|
2352
|
+
refreshGeneration = 0;
|
|
2353
|
+
constructor(options) {
|
|
2354
|
+
this.projectName = options.projectName;
|
|
2355
|
+
this.worktree = options.worktree;
|
|
2356
|
+
this.readBranch = options.readBranch;
|
|
2357
|
+
this.ready = this.refreshBranch("initial");
|
|
2358
|
+
}
|
|
2359
|
+
async refreshBranch(_reason) {
|
|
2360
|
+
const generation = ++this.refreshGeneration;
|
|
2361
|
+
try {
|
|
2362
|
+
const result = await this.readBranch(this.worktree);
|
|
2363
|
+
if (generation !== this.refreshGeneration) return;
|
|
2364
|
+
const trimmed = result.trim() || void 0;
|
|
2365
|
+
this.branchName = trimmed;
|
|
2366
|
+
} catch (error) {
|
|
2367
|
+
if (generation !== this.refreshGeneration) return;
|
|
2368
|
+
debug("refreshBranch failed", errorMessage(error));
|
|
2369
|
+
}
|
|
2370
|
+
}
|
|
2371
|
+
handleEvent(event) {
|
|
2372
|
+
if (event.type === "vcs.branch.updated") return this.refreshBranch("vcs.branch.updated");
|
|
2373
|
+
}
|
|
2374
|
+
};
|
|
2375
|
+
var TabTitleActivityModel = class {
|
|
2376
|
+
status = "idle";
|
|
2377
|
+
worktreeDirectory;
|
|
2378
|
+
sessions = /* @__PURE__ */ new Map();
|
|
2379
|
+
scopedSessions = /* @__PURE__ */ new Set();
|
|
2371
2380
|
runningSessions = /* @__PURE__ */ new Set();
|
|
2372
2381
|
pendingInputs = /* @__PURE__ */ new Map();
|
|
2373
|
-
|
|
2382
|
+
constructor(options) {
|
|
2383
|
+
this.worktreeDirectory = options.worktreeDirectory;
|
|
2384
|
+
}
|
|
2385
|
+
getSession(sessionID) {
|
|
2386
|
+
return this.scopedSessions.has(sessionID) ? this.sessions.get(sessionID) : void 0;
|
|
2387
|
+
}
|
|
2388
|
+
hasPendingInput(sessionID, requestID) {
|
|
2389
|
+
return this.pendingInputs.has(`${sessionID}:${requestID}`);
|
|
2390
|
+
}
|
|
2391
|
+
handleEvent(event) {
|
|
2392
|
+
if (!isRecord(event.properties)) return;
|
|
2393
|
+
const properties = event.properties;
|
|
2394
|
+
switch (event.type) {
|
|
2395
|
+
case "session.created":
|
|
2396
|
+
case "session.updated": {
|
|
2397
|
+
const info = properties.info;
|
|
2398
|
+
if (isRecord(info)) {
|
|
2399
|
+
const id = stringProperty(info, "id");
|
|
2400
|
+
if (id) this.storeSession(id, info);
|
|
2401
|
+
}
|
|
2402
|
+
break;
|
|
2403
|
+
}
|
|
2404
|
+
case "session.status": {
|
|
2405
|
+
const sessionID = stringProperty(properties, "sessionID");
|
|
2406
|
+
const statusType = sessionStatusType(properties);
|
|
2407
|
+
if (sessionID && statusType) {
|
|
2408
|
+
if (statusType === "idle") {
|
|
2409
|
+
if (this.runningSessions.has(sessionID)) {
|
|
2410
|
+
this.runningSessions.delete(sessionID);
|
|
2411
|
+
this.updateStatus();
|
|
2412
|
+
}
|
|
2413
|
+
} else if (statusType === "busy" || statusType === "retry") {
|
|
2414
|
+
if (this.scopedSessions.has(sessionID)) {
|
|
2415
|
+
this.runningSessions.add(sessionID);
|
|
2416
|
+
this.updateStatus();
|
|
2417
|
+
}
|
|
2418
|
+
}
|
|
2419
|
+
}
|
|
2420
|
+
break;
|
|
2421
|
+
}
|
|
2422
|
+
case "session.idle":
|
|
2423
|
+
case "session.error": {
|
|
2424
|
+
const sessionID = stringProperty(properties, "sessionID");
|
|
2425
|
+
if (sessionID && this.runningSessions.has(sessionID)) {
|
|
2426
|
+
this.runningSessions.delete(sessionID);
|
|
2427
|
+
this.updateStatus();
|
|
2428
|
+
}
|
|
2429
|
+
break;
|
|
2430
|
+
}
|
|
2431
|
+
case "question.asked":
|
|
2432
|
+
case "permission.asked": {
|
|
2433
|
+
const id = inputRequestID(properties);
|
|
2434
|
+
const sessionID = stringProperty(properties, "sessionID");
|
|
2435
|
+
if (id && sessionID && this.scopedSessions.has(sessionID)) {
|
|
2436
|
+
this.pendingInputs.set(`${sessionID}:${id}`, sessionID);
|
|
2437
|
+
this.runningSessions.add(sessionID);
|
|
2438
|
+
this.updateStatus();
|
|
2439
|
+
}
|
|
2440
|
+
break;
|
|
2441
|
+
}
|
|
2442
|
+
case "permission.updated": {
|
|
2443
|
+
const id = inputRequestID(properties);
|
|
2444
|
+
const sessionID = stringProperty(properties, "sessionID");
|
|
2445
|
+
const state = inputState(properties);
|
|
2446
|
+
if (id && isResolvedInputState(state)) {
|
|
2447
|
+
this.pendingInputs.delete(`${sessionID}:${id}`);
|
|
2448
|
+
if (sessionID && this.runningSessions.has(sessionID)) this.runningSessions.add(sessionID);
|
|
2449
|
+
this.updateStatus();
|
|
2450
|
+
} else if (id && sessionID && this.scopedSessions.has(sessionID)) {
|
|
2451
|
+
this.pendingInputs.set(`${sessionID}:${id}`, sessionID);
|
|
2452
|
+
this.runningSessions.add(sessionID);
|
|
2453
|
+
this.updateStatus();
|
|
2454
|
+
}
|
|
2455
|
+
break;
|
|
2456
|
+
}
|
|
2457
|
+
case "question.replied":
|
|
2458
|
+
case "question.rejected":
|
|
2459
|
+
case "permission.replied": {
|
|
2460
|
+
const id = inputRequestID(properties);
|
|
2461
|
+
const sessionID = stringProperty(properties, "sessionID");
|
|
2462
|
+
if (id) this.pendingInputs.delete(`${sessionID}:${id}`);
|
|
2463
|
+
if (sessionID && this.runningSessions.has(sessionID)) this.runningSessions.add(sessionID);
|
|
2464
|
+
this.updateStatus();
|
|
2465
|
+
break;
|
|
2466
|
+
}
|
|
2467
|
+
case "session.deleted": {
|
|
2468
|
+
const sessionID = deletedSessionID(properties);
|
|
2469
|
+
if (sessionID) {
|
|
2470
|
+
this.removeSessionAndDescendants(sessionID);
|
|
2471
|
+
this.updateStatus();
|
|
2472
|
+
}
|
|
2473
|
+
break;
|
|
2474
|
+
}
|
|
2475
|
+
}
|
|
2476
|
+
}
|
|
2477
|
+
storeSession(id, info) {
|
|
2478
|
+
const directory = stringProperty(info, "directory");
|
|
2479
|
+
const parentID = stringProperty(info, "parentID");
|
|
2480
|
+
this.sessions.set(id, {
|
|
2481
|
+
directory,
|
|
2482
|
+
parentID
|
|
2483
|
+
});
|
|
2484
|
+
const isDirectlyScoped = directory === this.worktreeDirectory;
|
|
2485
|
+
const isDescendantScoped = parentID ? this.scopedSessions.has(parentID) : false;
|
|
2486
|
+
if (this.scopedSessions.has(id) || isDirectlyScoped || isDescendantScoped) this.scopedSessions.add(id);
|
|
2487
|
+
}
|
|
2488
|
+
removeSessionAndDescendants(rootID) {
|
|
2489
|
+
const toRemove = /* @__PURE__ */ new Set();
|
|
2490
|
+
toRemove.add(rootID);
|
|
2491
|
+
let changed = true;
|
|
2492
|
+
while (changed) {
|
|
2493
|
+
changed = false;
|
|
2494
|
+
for (const [id, session] of this.sessions) if (!toRemove.has(id) && session.parentID && toRemove.has(session.parentID)) {
|
|
2495
|
+
toRemove.add(id);
|
|
2496
|
+
changed = true;
|
|
2497
|
+
}
|
|
2498
|
+
}
|
|
2499
|
+
for (const id of toRemove) {
|
|
2500
|
+
this.sessions.delete(id);
|
|
2501
|
+
this.scopedSessions.delete(id);
|
|
2502
|
+
this.runningSessions.delete(id);
|
|
2503
|
+
}
|
|
2504
|
+
for (const [key, sessionID] of [...this.pendingInputs.entries()]) if (toRemove.has(sessionID)) this.pendingInputs.delete(key);
|
|
2505
|
+
}
|
|
2506
|
+
updateStatus() {
|
|
2507
|
+
if (this.pendingInputs.size > 0) this.status = "needs-input";
|
|
2508
|
+
else if (this.runningSessions.size > 0) this.status = "running";
|
|
2509
|
+
else this.status = "idle";
|
|
2510
|
+
}
|
|
2511
|
+
};
|
|
2512
|
+
var TabTitleActor = class {
|
|
2513
|
+
ready;
|
|
2514
|
+
identity;
|
|
2515
|
+
activity;
|
|
2516
|
+
emojis;
|
|
2517
|
+
constructor(options) {
|
|
2518
|
+
this.identity = options.identity;
|
|
2519
|
+
this.activity = options.activity;
|
|
2520
|
+
this.emojis = {
|
|
2521
|
+
...defaultTabTitleEmojis,
|
|
2522
|
+
...options.emojis
|
|
2523
|
+
};
|
|
2524
|
+
this.ready = this.identity.ready;
|
|
2525
|
+
}
|
|
2526
|
+
get context() {
|
|
2527
|
+
return {
|
|
2528
|
+
projectName: this.identity.projectName,
|
|
2529
|
+
branchName: this.identity.branchName,
|
|
2530
|
+
status: this.activity.status
|
|
2531
|
+
};
|
|
2532
|
+
}
|
|
2533
|
+
get title() {
|
|
2534
|
+
return formatTabTitle({
|
|
2535
|
+
...this.context,
|
|
2536
|
+
emojis: this.emojis
|
|
2537
|
+
});
|
|
2538
|
+
}
|
|
2539
|
+
async handleEvent(event) {
|
|
2540
|
+
this.activity.handleEvent(event);
|
|
2541
|
+
const identityResult = this.identity.handleEvent(event);
|
|
2542
|
+
if (identityResult instanceof Promise) await identityResult;
|
|
2543
|
+
}
|
|
2544
|
+
};
|
|
2545
|
+
var TabTitleManager = class {
|
|
2374
2546
|
desiredTitle;
|
|
2375
2547
|
lastSyncedTitle;
|
|
2548
|
+
syncGeneration = 0;
|
|
2376
2549
|
debounceTimer;
|
|
2377
2550
|
retryTimer;
|
|
2378
2551
|
retryAttempt = 0;
|
|
@@ -2381,7 +2554,6 @@ var TabTitleManager = class {
|
|
|
2381
2554
|
debounceMs;
|
|
2382
2555
|
retryInitialMs;
|
|
2383
2556
|
retryMaxMs;
|
|
2384
|
-
projectName;
|
|
2385
2557
|
cli;
|
|
2386
2558
|
emojis;
|
|
2387
2559
|
enabled;
|
|
@@ -2390,9 +2562,8 @@ var TabTitleManager = class {
|
|
|
2390
2562
|
originalTabTitleLoaded = false;
|
|
2391
2563
|
originalTabTitlePromise;
|
|
2392
2564
|
destroyPromise;
|
|
2565
|
+
actor;
|
|
2393
2566
|
constructor(options) {
|
|
2394
|
-
this.projectName = options.projectName;
|
|
2395
|
-
this.branchName = options.branchName?.trim() || void 0;
|
|
2396
2567
|
this.cli = options.cli ?? new ZellijCli();
|
|
2397
2568
|
this.emojis = {
|
|
2398
2569
|
...defaultTabTitleEmojis,
|
|
@@ -2401,65 +2572,12 @@ var TabTitleManager = class {
|
|
|
2401
2572
|
this.debounceMs = options.debounceMs ?? 300;
|
|
2402
2573
|
this.retryInitialMs = options.retryInitialMs ?? 250;
|
|
2403
2574
|
this.retryMaxMs = options.retryMaxMs ?? 5e3;
|
|
2404
|
-
this.enabled = Boolean(process.env.ZELLIJ);
|
|
2405
|
-
|
|
2406
|
-
setBranch(branch) {
|
|
2407
|
-
const trimmed = branch?.trim() || void 0;
|
|
2408
|
-
if (this.branchName === trimmed) return;
|
|
2409
|
-
this.branchName = trimmed;
|
|
2410
|
-
this.scheduleUpdate();
|
|
2411
|
-
}
|
|
2412
|
-
updateSessionStatus(sessionID, status) {
|
|
2413
|
-
const isRunning = status.type === "busy" || status.type === "retry";
|
|
2414
|
-
const wasRunning = this.runningSessions.has(sessionID);
|
|
2415
|
-
if (isRunning) {
|
|
2416
|
-
if (!wasRunning) {
|
|
2417
|
-
this.runningSessions.add(sessionID);
|
|
2418
|
-
this.scheduleUpdate();
|
|
2419
|
-
}
|
|
2420
|
-
} else if (wasRunning) {
|
|
2421
|
-
this.runningSessions.delete(sessionID);
|
|
2422
|
-
this.scheduleUpdate();
|
|
2423
|
-
}
|
|
2424
|
-
}
|
|
2425
|
-
markSessionIdle(sessionID) {
|
|
2426
|
-
if (this.runningSessions.delete(sessionID)) this.scheduleUpdate();
|
|
2427
|
-
}
|
|
2428
|
-
removeSession(sessionID) {
|
|
2429
|
-
const hadRunning = this.runningSessions.delete(sessionID);
|
|
2430
|
-
let hadPendingInput = false;
|
|
2431
|
-
for (const [id, pendingSessionID] of this.pendingInputs) if (pendingSessionID === sessionID) {
|
|
2432
|
-
this.pendingInputs.delete(id);
|
|
2433
|
-
hadPendingInput = true;
|
|
2434
|
-
}
|
|
2435
|
-
if (!hadRunning && !hadPendingInput) return;
|
|
2436
|
-
this.scheduleUpdate();
|
|
2437
|
-
}
|
|
2438
|
-
markNeedsInput(id, sessionID) {
|
|
2439
|
-
if (this.pendingInputs.get(id) === sessionID) return;
|
|
2440
|
-
this.pendingInputs.set(id, sessionID);
|
|
2441
|
-
this.scheduleUpdate();
|
|
2442
|
-
}
|
|
2443
|
-
clearNeedsInput(id) {
|
|
2444
|
-
if (!this.pendingInputs.delete(id)) return;
|
|
2445
|
-
this.scheduleUpdate();
|
|
2446
|
-
}
|
|
2447
|
-
get isBusy() {
|
|
2448
|
-
return this.runningSessions.size > 0;
|
|
2449
|
-
}
|
|
2450
|
-
get needsInput() {
|
|
2451
|
-
return this.pendingInputs.size > 0;
|
|
2452
|
-
}
|
|
2453
|
-
get status() {
|
|
2454
|
-
if (this.needsInput) return "needs-input";
|
|
2455
|
-
if (this.isBusy) return "running";
|
|
2456
|
-
return "idle";
|
|
2575
|
+
this.enabled = Boolean(process.env.ZELLIJ || process.env.ZELLIJ_SESSION_NAME);
|
|
2576
|
+
this.actor = options.actor;
|
|
2457
2577
|
}
|
|
2458
2578
|
buildTitle() {
|
|
2459
2579
|
return sanitizeTitle(formatTabTitle({
|
|
2460
|
-
|
|
2461
|
-
branchName: this.branchName,
|
|
2462
|
-
status: this.status,
|
|
2580
|
+
...this.actor.context,
|
|
2463
2581
|
emojis: this.emojis
|
|
2464
2582
|
}));
|
|
2465
2583
|
}
|
|
@@ -2490,24 +2608,27 @@ var TabTitleManager = class {
|
|
|
2490
2608
|
}
|
|
2491
2609
|
async syncDesiredTitle() {
|
|
2492
2610
|
if (!this.enabled || this.destroyed) return;
|
|
2611
|
+
const generation = this.syncGeneration;
|
|
2493
2612
|
await this.ensureOriginalTabTitle();
|
|
2494
|
-
if (this.destroyed) return;
|
|
2613
|
+
if (this.destroyed || generation !== this.syncGeneration) return;
|
|
2495
2614
|
if (this.syncInFlight) return this.syncPromise;
|
|
2496
2615
|
this.syncInFlight = true;
|
|
2497
|
-
this.syncPromise = this.runTitleSync();
|
|
2616
|
+
this.syncPromise = this.runTitleSync(generation);
|
|
2498
2617
|
return this.syncPromise;
|
|
2499
2618
|
}
|
|
2500
|
-
async runTitleSync() {
|
|
2619
|
+
async runTitleSync(generation) {
|
|
2501
2620
|
try {
|
|
2502
|
-
while (this.desiredTitle && this.desiredTitle !== this.lastSyncedTitle) {
|
|
2621
|
+
while (generation === this.syncGeneration && this.desiredTitle && this.desiredTitle !== this.lastSyncedTitle) {
|
|
2503
2622
|
const title = this.desiredTitle;
|
|
2504
2623
|
try {
|
|
2505
2624
|
await this.cli.renameTab(title);
|
|
2625
|
+
if (generation !== this.syncGeneration || this.destroyed) return;
|
|
2506
2626
|
this.lastSyncedTitle = title;
|
|
2507
2627
|
this.retryAttempt = 0;
|
|
2508
2628
|
this.clearRetryTimer();
|
|
2509
2629
|
} catch (cause) {
|
|
2510
2630
|
debug("Failed to rename Zellij tab.", cause);
|
|
2631
|
+
if (generation !== this.syncGeneration || this.destroyed) break;
|
|
2511
2632
|
this.scheduleRetry();
|
|
2512
2633
|
break;
|
|
2513
2634
|
}
|
|
@@ -2558,6 +2679,8 @@ var TabTitleManager = class {
|
|
|
2558
2679
|
destroy() {
|
|
2559
2680
|
if (this.destroyed) return this.destroyPromise ?? Promise.resolve();
|
|
2560
2681
|
this.destroyed = true;
|
|
2682
|
+
this.syncGeneration += 1;
|
|
2683
|
+
this.desiredTitle = void 0;
|
|
2561
2684
|
this.clearDebounceTimer();
|
|
2562
2685
|
this.clearRetryTimer();
|
|
2563
2686
|
if (!this.enabled) return Promise.resolve();
|
|
@@ -2635,10 +2758,18 @@ function createZellijPtyPlugin(dependencies = {}) {
|
|
|
2635
2758
|
registerShutdownCleanup();
|
|
2636
2759
|
const workspaceRoot = getWorkspaceRoot(input);
|
|
2637
2760
|
const projectName = getProjectName(workspaceRoot);
|
|
2638
|
-
const
|
|
2639
|
-
const tabTitleManager = config.tabTitle.enabled ? new TabTitleManager({
|
|
2761
|
+
const identityModel = config.tabTitle.enabled ? new TabTitleIdentityModel({
|
|
2640
2762
|
projectName,
|
|
2641
|
-
|
|
2763
|
+
worktree: workspaceRoot,
|
|
2764
|
+
readBranch: async (worktree) => shouldReadInitialBranch(process.env.ZELLIJ || process.env.ZELLIJ_SESSION_NAME) ? await getInitialBranch(worktree) ?? "" : ""
|
|
2765
|
+
}) : void 0;
|
|
2766
|
+
const activityModel = config.tabTitle.enabled ? new TabTitleActivityModel({ worktreeDirectory: workspaceRoot }) : void 0;
|
|
2767
|
+
const actor = identityModel && activityModel ? new TabTitleActor({
|
|
2768
|
+
identity: identityModel,
|
|
2769
|
+
activity: activityModel
|
|
2770
|
+
}) : void 0;
|
|
2771
|
+
const tabTitleManager = config.tabTitle.enabled && actor ? new TabTitleManager({
|
|
2772
|
+
actor,
|
|
2642
2773
|
debounceMs: config.tabTitle.debounceMs,
|
|
2643
2774
|
emojis: {
|
|
2644
2775
|
idle: config.tabTitle.emojiIdle,
|
|
@@ -2672,19 +2803,24 @@ function createZellijPtyPlugin(dependencies = {}) {
|
|
|
2672
2803
|
}
|
|
2673
2804
|
});
|
|
2674
2805
|
subscriberManager.setLifecycleHooks(completionNotifications ? { onSessionTerminal: (event) => void completionNotifications.handleSessionTerminal(event).catch((error) => debug("completion notification lifecycle hook failed", errorMessage(error))) } : void 0);
|
|
2806
|
+
if (actor) await actor.ready;
|
|
2675
2807
|
tabTitleManager?.renderImmediate().catch((error) => debug("initial tab title render failed", errorMessage(error)));
|
|
2676
2808
|
if (config.autoUpdate) (dependencies.startAutoUpdateCheck ?? startAutoUpdateCheck)(client, dependencies.importMetaUrl ?? import.meta.url);
|
|
2677
2809
|
return {
|
|
2678
2810
|
async event(input) {
|
|
2679
2811
|
const event = input.event;
|
|
2680
|
-
if (
|
|
2812
|
+
if (actor && tabTitleManager) {
|
|
2813
|
+
await actor.handleEvent(event);
|
|
2814
|
+
if (event.type === "server.instance.disposed" || event.type === "global.disposed") await tabTitleManager.destroy();
|
|
2815
|
+
else tabTitleManager.scheduleUpdate();
|
|
2816
|
+
}
|
|
2681
2817
|
if (event.type === "server.instance.disposed" || event.type === "global.disposed") {
|
|
2682
2818
|
completionNotifications?.clearAll();
|
|
2683
2819
|
completionNotifications?.dispose();
|
|
2684
2820
|
subscriberManager.setLifecycleHooks(void 0);
|
|
2685
2821
|
}
|
|
2686
2822
|
if (event.type === "session.deleted") {
|
|
2687
|
-
const sessionID = deletedSessionID(event);
|
|
2823
|
+
const sessionID = deletedSessionID$1(event);
|
|
2688
2824
|
if (!sessionID) return;
|
|
2689
2825
|
const sessions = sessionManager.listByOpenCodeSession(sessionID);
|
|
2690
2826
|
for (const session of sessions) completionNotifications?.clearSession(session.id);
|