opencode-zellij 0.0.7 → 0.0.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/LICENSE +21 -0
- package/README.md +18 -0
- package/README.zh.md +18 -0
- package/dist/index.mjs +217 -235
- package/dist/index.mjs.map +1 -1
- package/dist/pane-watchdog-runner.mjs +13 -4
- package/dist/pane-watchdog-runner.mjs.map +1 -1
- package/package.json +2 -1
package/dist/index.mjs
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import process from "node:process";
|
|
2
2
|
import { execFile, spawn, spawnSync } from "node:child_process";
|
|
3
|
-
import { randomUUID } from "node:crypto";
|
|
4
3
|
import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
5
|
-
import {
|
|
4
|
+
import { readFile, rename, rm } from "node:fs/promises";
|
|
6
5
|
import path, { basename, dirname, join } from "node:path";
|
|
7
6
|
import { fileURLToPath } from "node:url";
|
|
8
7
|
import { promisify } from "node:util";
|
|
9
8
|
import { homedir, tmpdir } from "node:os";
|
|
10
9
|
import { parseJSON, parseJSONC } from "confbox";
|
|
11
10
|
import { z } from "zod";
|
|
11
|
+
import { randomUUID } from "node:crypto";
|
|
12
12
|
import { setTimeout as setTimeout$1 } from "node:timers/promises";
|
|
13
13
|
import { tool } from "@opencode-ai/plugin";
|
|
14
14
|
import { Buffer } from "node:buffer";
|
|
@@ -18,19 +18,20 @@ function debug(message, ...details) {
|
|
|
18
18
|
console.warn(`[opencode-zellij] ${message}`, ...details);
|
|
19
19
|
}
|
|
20
20
|
//#endregion
|
|
21
|
+
//#region src/utils/errors.ts
|
|
22
|
+
function errorMessage(error) {
|
|
23
|
+
return error instanceof Error ? error.message : String(error);
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
21
26
|
//#region src/auto-update.ts
|
|
22
27
|
const PACKAGE_NAME = "opencode-zellij";
|
|
23
28
|
const NPM_REGISTRY_URL = "https://registry.npmjs.org/-/package/opencode-zellij/dist-tags";
|
|
24
29
|
const FETCH_TIMEOUT_MS = 5e3;
|
|
25
30
|
const INSTALL_TIMEOUT_MS = 6e4;
|
|
26
|
-
const LOCK_STALE_MS = INSTALL_TIMEOUT_MS * 2 + FETCH_TIMEOUT_MS + 3e4;
|
|
27
31
|
const defaultExecFile = promisify(execFile);
|
|
28
32
|
function packageDir(installRoot) {
|
|
29
33
|
return join(installRoot, "node_modules", PACKAGE_NAME);
|
|
30
34
|
}
|
|
31
|
-
function lockDir(installRoot) {
|
|
32
|
-
return join(installRoot, ".opencode-zellij-update.lock");
|
|
33
|
-
}
|
|
34
35
|
function backupDir(installRoot) {
|
|
35
36
|
return join(installRoot, "node_modules", `${PACKAGE_NAME}.update-backup`);
|
|
36
37
|
}
|
|
@@ -43,7 +44,9 @@ async function installedPackageMetadata(installRoot) {
|
|
|
43
44
|
version: typeof pkg.version === "string" ? pkg.version : void 0,
|
|
44
45
|
main: typeof pkg.main === "string" ? pkg.main : void 0
|
|
45
46
|
};
|
|
46
|
-
} catch {
|
|
47
|
+
} catch (error) {
|
|
48
|
+
debug("installedPackageMetadata failed", errorMessage(error));
|
|
49
|
+
}
|
|
47
50
|
}
|
|
48
51
|
function isExpectedPackage(metadata, version) {
|
|
49
52
|
return metadata?.name === "opencode-zellij" && metadata.version === version;
|
|
@@ -64,52 +67,6 @@ async function removeInstalledPackage(installRoot) {
|
|
|
64
67
|
recursive: true
|
|
65
68
|
});
|
|
66
69
|
}
|
|
67
|
-
async function installRootLockIsStale(installRoot) {
|
|
68
|
-
try {
|
|
69
|
-
const content = await readFile(join(lockDir(installRoot), "owner.json"), "utf8");
|
|
70
|
-
const owner = JSON.parse(content);
|
|
71
|
-
if (isRecord$1(owner) && typeof owner.createdAt === "number") return Date.now() - owner.createdAt > LOCK_STALE_MS;
|
|
72
|
-
} catch {
|
|
73
|
-
return true;
|
|
74
|
-
}
|
|
75
|
-
return false;
|
|
76
|
-
}
|
|
77
|
-
async function acquireInstallLock(installRoot) {
|
|
78
|
-
const dir = lockDir(installRoot);
|
|
79
|
-
const token = randomUUID();
|
|
80
|
-
try {
|
|
81
|
-
await mkdir(dir);
|
|
82
|
-
} catch {
|
|
83
|
-
if (!await installRootLockIsStale(installRoot)) return void 0;
|
|
84
|
-
await rm(dir, {
|
|
85
|
-
force: true,
|
|
86
|
-
recursive: true
|
|
87
|
-
});
|
|
88
|
-
try {
|
|
89
|
-
await mkdir(dir);
|
|
90
|
-
} catch {
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
await writeFile(join(dir, "owner.json"), JSON.stringify({
|
|
95
|
-
pid: process.pid,
|
|
96
|
-
token,
|
|
97
|
-
createdAt: Date.now()
|
|
98
|
-
}));
|
|
99
|
-
return async () => {
|
|
100
|
-
try {
|
|
101
|
-
const content = await readFile(join(dir, "owner.json"), "utf8");
|
|
102
|
-
const owner = JSON.parse(content);
|
|
103
|
-
if (isRecord$1(owner) && owner.token !== token) return;
|
|
104
|
-
} catch {
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
await rm(dir, {
|
|
108
|
-
force: true,
|
|
109
|
-
recursive: true
|
|
110
|
-
});
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
70
|
async function backupInstalledPackage(installRoot) {
|
|
114
71
|
const source = packageDir(installRoot);
|
|
115
72
|
if (!existsSync(source)) return void 0;
|
|
@@ -158,7 +115,9 @@ async function findInstallContext(importMetaUrl) {
|
|
|
158
115
|
currentVersion: pkg.version
|
|
159
116
|
};
|
|
160
117
|
}
|
|
161
|
-
} catch {
|
|
118
|
+
} catch (error) {
|
|
119
|
+
debug("findInstallContext package.json read failed", errorMessage(error));
|
|
120
|
+
}
|
|
162
121
|
}
|
|
163
122
|
const parent = dirname(dir);
|
|
164
123
|
if (parent === dir) break;
|
|
@@ -253,48 +212,36 @@ async function checkAndUpdate(options) {
|
|
|
253
212
|
reason: `cache spec is pinned or unknown (${context.cacheSpec})`
|
|
254
213
|
};
|
|
255
214
|
}
|
|
256
|
-
const
|
|
257
|
-
if (!
|
|
258
|
-
debug(
|
|
215
|
+
const latest = await fetchLatestVersion(options.fetchImpl);
|
|
216
|
+
if (!latest) {
|
|
217
|
+
debug("skipping auto-update: could not determine latest version");
|
|
259
218
|
return {
|
|
260
219
|
type: "skipped",
|
|
261
|
-
reason: "
|
|
220
|
+
reason: "could not determine latest version"
|
|
262
221
|
};
|
|
263
222
|
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
debug("skipping auto-update: could not determine latest version");
|
|
268
|
-
return {
|
|
269
|
-
type: "skipped",
|
|
270
|
-
reason: "could not determine latest version"
|
|
271
|
-
};
|
|
272
|
-
}
|
|
273
|
-
const installedVersion = (await installedPackageMetadata(context.installRoot))?.version ?? context.currentVersion;
|
|
274
|
-
if (latest === installedVersion) {
|
|
275
|
-
debug(`auto-update: already on latest ${latest}`);
|
|
276
|
-
return {
|
|
277
|
-
type: "up-to-date",
|
|
278
|
-
currentVersion: installedVersion
|
|
279
|
-
};
|
|
280
|
-
}
|
|
281
|
-
if (await runNpmInstall(context.installRoot, latest, options.execImpl)) {
|
|
282
|
-
debug(`updated ${PACKAGE_NAME} from ${installedVersion} to ${latest}`);
|
|
283
|
-
return {
|
|
284
|
-
type: "updated",
|
|
285
|
-
fromVersion: installedVersion,
|
|
286
|
-
toVersion: latest
|
|
287
|
-
};
|
|
288
|
-
}
|
|
223
|
+
const installedVersion = (await installedPackageMetadata(context.installRoot))?.version ?? context.currentVersion;
|
|
224
|
+
if (latest === installedVersion) {
|
|
225
|
+
debug(`auto-update: already on latest ${latest}`);
|
|
289
226
|
return {
|
|
290
|
-
type: "
|
|
291
|
-
currentVersion: installedVersion
|
|
292
|
-
latestVersion: latest,
|
|
293
|
-
reason: "npm install failed"
|
|
227
|
+
type: "up-to-date",
|
|
228
|
+
currentVersion: installedVersion
|
|
294
229
|
};
|
|
295
|
-
} finally {
|
|
296
|
-
await releaseLock();
|
|
297
230
|
}
|
|
231
|
+
if (await runNpmInstall(context.installRoot, latest, options.execImpl)) {
|
|
232
|
+
debug(`updated ${PACKAGE_NAME} from ${installedVersion} to ${latest}`);
|
|
233
|
+
return {
|
|
234
|
+
type: "updated",
|
|
235
|
+
fromVersion: installedVersion,
|
|
236
|
+
toVersion: latest
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
return {
|
|
240
|
+
type: "failed",
|
|
241
|
+
currentVersion: installedVersion,
|
|
242
|
+
latestVersion: latest,
|
|
243
|
+
reason: "npm install failed"
|
|
244
|
+
};
|
|
298
245
|
}
|
|
299
246
|
//#endregion
|
|
300
247
|
//#region src/config.ts
|
|
@@ -316,7 +263,7 @@ const ptyLayerSchema = z.object({
|
|
|
316
263
|
enabled: z.boolean().optional().describe("Enable Zellij-backed PTY tools."),
|
|
317
264
|
sudoPane: sudoPaneSchema.optional().describe("Controls whether the sudo pane tool is available, denied, or hidden.")
|
|
318
265
|
}).strict();
|
|
319
|
-
const autoUpdateLayerSchema = z.
|
|
266
|
+
const autoUpdateLayerSchema = z.boolean().optional().describe("Enable automatic update checks for the opencode-zellij plugin.");
|
|
320
267
|
const sidecarConfigSchema = z.object({
|
|
321
268
|
$schema: z.string().optional().describe("JSON Schema URI for editor completion."),
|
|
322
269
|
tabTitle: tabTitleLayerSchema.optional(),
|
|
@@ -336,7 +283,7 @@ const defaultConfig = {
|
|
|
336
283
|
enabled: true,
|
|
337
284
|
sudoPane: "allow"
|
|
338
285
|
},
|
|
339
|
-
autoUpdate:
|
|
286
|
+
autoUpdate: true
|
|
340
287
|
};
|
|
341
288
|
function validConfigLayer(value) {
|
|
342
289
|
const result = sidecarConfigSchema.safeParse(value);
|
|
@@ -361,7 +308,7 @@ function mergeConfig(user, project) {
|
|
|
361
308
|
enabled: project?.pty?.enabled ?? user?.pty?.enabled ?? defaultConfig.pty.enabled,
|
|
362
309
|
sudoPane: project?.pty?.sudoPane ?? user?.pty?.sudoPane ?? defaultConfig.pty.sudoPane
|
|
363
310
|
},
|
|
364
|
-
autoUpdate:
|
|
311
|
+
autoUpdate: project?.autoUpdate ?? user?.autoUpdate ?? defaultConfig.autoUpdate
|
|
365
312
|
};
|
|
366
313
|
}
|
|
367
314
|
async function loadConfigLayer(directory, warnings) {
|
|
@@ -416,80 +363,13 @@ async function loadConfig(input) {
|
|
|
416
363
|
};
|
|
417
364
|
}
|
|
418
365
|
//#endregion
|
|
419
|
-
//#region src/
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
const command = input.command.trim();
|
|
424
|
-
if (!command) throw new Error("command is required");
|
|
425
|
-
if (options.exitCodeToken) {
|
|
426
|
-
if (input.args && input.args.length > 0) return [
|
|
427
|
-
"bash",
|
|
428
|
-
"-lc",
|
|
429
|
-
directCommandExitWrapper,
|
|
430
|
-
"zellij-pty",
|
|
431
|
-
options.exitCodeToken,
|
|
432
|
-
command,
|
|
433
|
-
...input.args
|
|
434
|
-
];
|
|
435
|
-
return [
|
|
436
|
-
"bash",
|
|
437
|
-
"-lc",
|
|
438
|
-
shellCommandExitWrapper,
|
|
439
|
-
"zellij-pty",
|
|
440
|
-
options.exitCodeToken,
|
|
441
|
-
command
|
|
442
|
-
];
|
|
443
|
-
}
|
|
444
|
-
if (input.args && input.args.length > 0) return [command, ...input.args];
|
|
445
|
-
return [
|
|
446
|
-
"bash",
|
|
447
|
-
"-lc",
|
|
448
|
-
command
|
|
449
|
-
];
|
|
366
|
+
//#region src/permissions/sudo-pane.ts
|
|
367
|
+
let sudoPaneAllowed = true;
|
|
368
|
+
function configureSudoPane(allowed) {
|
|
369
|
+
sudoPaneAllowed = allowed;
|
|
450
370
|
}
|
|
451
|
-
function
|
|
452
|
-
if (!
|
|
453
|
-
return [input.command, ...input.args].join(" ").trim();
|
|
454
|
-
}
|
|
455
|
-
//#endregion
|
|
456
|
-
//#region src/permissions/policy.ts
|
|
457
|
-
const denyPatterns = [
|
|
458
|
-
/(^|\s)rm\s+-[^\n&;r|]*r[^\n&;|]*f\s+\//,
|
|
459
|
-
/(^|\s)mkfs(?:\s|$)/,
|
|
460
|
-
/(^|\s)dd\s+(?:[^\s&;|][^\n;|&]*)?\bof=\/dev\//,
|
|
461
|
-
/:\(\)\s*\{\s*:\|:\s*&\s*\}\s*;/
|
|
462
|
-
];
|
|
463
|
-
const sudoPattern = /(?:^|[\s;&|])sudo(?:[\s;&|]|$)/;
|
|
464
|
-
let configuredDenyCommands = [];
|
|
465
|
-
let configuredAllowCommands = [];
|
|
466
|
-
let allowSudoPane = true;
|
|
467
|
-
function isStringArray(value) {
|
|
468
|
-
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
469
|
-
}
|
|
470
|
-
function escapeRegex(value) {
|
|
471
|
-
return value.replace(/[|\\{}()[\]^$+?.]/g, "\\$&");
|
|
472
|
-
}
|
|
473
|
-
function wildcardMatches(pattern, commandLine) {
|
|
474
|
-
return new RegExp(`^${pattern.split("*").map(escapeRegex).join(".*")}$`).test(commandLine);
|
|
475
|
-
}
|
|
476
|
-
function configurePolicy(config) {
|
|
477
|
-
configuredDenyCommands = [];
|
|
478
|
-
configuredAllowCommands = [];
|
|
479
|
-
allowSudoPane = true;
|
|
480
|
-
if (!config || typeof config !== "object") return;
|
|
481
|
-
const object = config;
|
|
482
|
-
if (isStringArray(object.denyCommands)) configuredDenyCommands = object.denyCommands;
|
|
483
|
-
if (isStringArray(object.allowCommands)) configuredAllowCommands = object.allowCommands;
|
|
484
|
-
if (typeof object.allowSudoPane === "boolean") allowSudoPane = object.allowSudoPane;
|
|
485
|
-
}
|
|
486
|
-
function assertCommandAllowed(input) {
|
|
487
|
-
const commandLine = commandLineForPolicy(input);
|
|
488
|
-
for (const pattern of denyPatterns) if (pattern.test(commandLine)) throw new Error(`Command denied by zellij-pty policy: ${commandLine}`);
|
|
489
|
-
for (const pattern of configuredDenyCommands) if (wildcardMatches(pattern, commandLine)) throw new Error(`Command denied by zellij-pty configured deny rule: ${commandLine}`);
|
|
490
|
-
if (configuredAllowCommands.length > 0 && !configuredAllowCommands.some((pattern) => wildcardMatches(pattern, commandLine))) throw new Error(`Command denied by zellij-pty allow list: ${commandLine}`);
|
|
491
|
-
if (!input.humanInputOnly && sudoPattern.test(commandLine)) throw new Error("sudo commands must use zellij_pty_request_sudo so credentials stay human-input-only and never pass through agent tool input.");
|
|
492
|
-
if (input.humanInputOnly && !allowSudoPane) throw new Error("sudo pane is disabled by zellij-pty policy.");
|
|
371
|
+
function assertSudoPaneAllowed() {
|
|
372
|
+
if (!sudoPaneAllowed) throw new Error("sudo pane is disabled by zellij-pty config.");
|
|
493
373
|
}
|
|
494
374
|
//#endregion
|
|
495
375
|
//#region src/utils/ids.ts
|
|
@@ -540,6 +420,9 @@ var SessionManager = class {
|
|
|
540
420
|
if (!session) throw new Error(`Unknown zellij PTY session: ${id}`);
|
|
541
421
|
return session;
|
|
542
422
|
}
|
|
423
|
+
find(id) {
|
|
424
|
+
return this.sessions.get(id);
|
|
425
|
+
}
|
|
543
426
|
list() {
|
|
544
427
|
return Array.from(this.sessions.values()).sort((a, b) => a.createdAt.localeCompare(b.createdAt));
|
|
545
428
|
}
|
|
@@ -572,6 +455,39 @@ var SessionManager = class {
|
|
|
572
455
|
};
|
|
573
456
|
const sessionManager = new SessionManager();
|
|
574
457
|
//#endregion
|
|
458
|
+
//#region src/utils/shell-args.ts
|
|
459
|
+
const directCommandExitWrapper = "token=\"$1\"; shift; set +e; \"$@\"; code=$?; printf \"\\n[zellij-pty:%s] exit-code=%s\\n\" \"$token\" \"$code\"; exit \"$code\"";
|
|
460
|
+
const shellCommandExitWrapper = "token=\"$1\"; command=\"$2\"; set +e; bash -lc \"$command\"; code=$?; printf \"\\n[zellij-pty:%s] exit-code=%s\\n\" \"$token\" \"$code\"; exit \"$code\"";
|
|
461
|
+
function buildCommandArgv(input, options = {}) {
|
|
462
|
+
const command = input.command.trim();
|
|
463
|
+
if (!command) throw new Error("command is required");
|
|
464
|
+
if (options.exitCodeToken) {
|
|
465
|
+
if (input.args && input.args.length > 0) return [
|
|
466
|
+
"bash",
|
|
467
|
+
"-lc",
|
|
468
|
+
directCommandExitWrapper,
|
|
469
|
+
"zellij-pty",
|
|
470
|
+
options.exitCodeToken,
|
|
471
|
+
command,
|
|
472
|
+
...input.args
|
|
473
|
+
];
|
|
474
|
+
return [
|
|
475
|
+
"bash",
|
|
476
|
+
"-lc",
|
|
477
|
+
shellCommandExitWrapper,
|
|
478
|
+
"zellij-pty",
|
|
479
|
+
options.exitCodeToken,
|
|
480
|
+
command
|
|
481
|
+
];
|
|
482
|
+
}
|
|
483
|
+
if (input.args && input.args.length > 0) return [command, ...input.args];
|
|
484
|
+
return [
|
|
485
|
+
"bash",
|
|
486
|
+
"-lc",
|
|
487
|
+
command
|
|
488
|
+
];
|
|
489
|
+
}
|
|
490
|
+
//#endregion
|
|
575
491
|
//#region src/zellij/cli.ts
|
|
576
492
|
const execFileAsync$1 = promisify(execFile);
|
|
577
493
|
function zellijCommandArgs(actionArgs) {
|
|
@@ -652,7 +568,8 @@ function parseCurrentPaneTabId(listPanesJson, paneId) {
|
|
|
652
568
|
if (!Number.isInteger(parsedPaneId)) return void 0;
|
|
653
569
|
try {
|
|
654
570
|
return findPaneTabId(JSON.parse(listPanesJson), parsedPaneId);
|
|
655
|
-
} catch {
|
|
571
|
+
} catch (error) {
|
|
572
|
+
debug("parseCurrentPaneTabId failed", errorMessage(error));
|
|
656
573
|
return;
|
|
657
574
|
}
|
|
658
575
|
}
|
|
@@ -735,6 +652,7 @@ const zellijCli = new ZellijCli();
|
|
|
735
652
|
//#region src/zellij/pane-watchdog.ts
|
|
736
653
|
const instanceId = randomUUID();
|
|
737
654
|
let watchdogStarted = false;
|
|
655
|
+
let watchdogChild = null;
|
|
738
656
|
function registryDirectory() {
|
|
739
657
|
const base = process.env.XDG_RUNTIME_DIR || tmpdir();
|
|
740
658
|
return path.join(base, `opencode-zellij-${process.getuid?.() ?? "user"}`);
|
|
@@ -748,7 +666,8 @@ function parseLinuxProcessStartTime(stat) {
|
|
|
748
666
|
function linuxProcessStartTime(pid) {
|
|
749
667
|
try {
|
|
750
668
|
return parseLinuxProcessStartTime(readFileSync(`/proc/${pid}/stat`, "utf8"));
|
|
751
|
-
} catch {
|
|
669
|
+
} catch (error) {
|
|
670
|
+
debug("linuxProcessStartTime failed", errorMessage(error));
|
|
752
671
|
return null;
|
|
753
672
|
}
|
|
754
673
|
}
|
|
@@ -769,7 +688,8 @@ function readRegistry() {
|
|
|
769
688
|
const parsed = JSON.parse(readFileSync(file, "utf8"));
|
|
770
689
|
if (parsed.version !== 1 || parsed.instanceId !== instanceId || parsed.ownerPid !== process.pid || !Array.isArray(parsed.panes)) return emptyRegistry();
|
|
771
690
|
return parsed;
|
|
772
|
-
} catch {
|
|
691
|
+
} catch (error) {
|
|
692
|
+
debug("readRegistry failed", errorMessage(error));
|
|
773
693
|
return emptyRegistry();
|
|
774
694
|
}
|
|
775
695
|
}
|
|
@@ -784,13 +704,24 @@ function writeRegistry(registry) {
|
|
|
784
704
|
renameSync(tempFile, file);
|
|
785
705
|
}
|
|
786
706
|
function ensureWatchdog() {
|
|
787
|
-
if (watchdogStarted) return;
|
|
707
|
+
if (watchdogStarted && watchdogChild) return;
|
|
788
708
|
watchdogStarted = true;
|
|
789
|
-
spawn(
|
|
709
|
+
const child = spawn(process.execPath, [watchdogRunnerPath(), watchdogRegistryPath()], {
|
|
790
710
|
detached: true,
|
|
791
711
|
stdio: "ignore",
|
|
792
712
|
env: process.env
|
|
793
|
-
})
|
|
713
|
+
});
|
|
714
|
+
watchdogChild = child;
|
|
715
|
+
child.unref();
|
|
716
|
+
child.on("error", () => {
|
|
717
|
+
watchdogStarted = false;
|
|
718
|
+
if (watchdogChild === child) watchdogChild = null;
|
|
719
|
+
});
|
|
720
|
+
child.on("exit", () => {
|
|
721
|
+
watchdogStarted = false;
|
|
722
|
+
if (watchdogChild === child) watchdogChild = null;
|
|
723
|
+
if (existsSync(watchdogRegistryPath())) ensureWatchdog();
|
|
724
|
+
});
|
|
794
725
|
}
|
|
795
726
|
function watchdogRunnerPath() {
|
|
796
727
|
return fileURLToPath(new URL("./pane-watchdog-runner.mjs", import.meta.url));
|
|
@@ -806,7 +737,8 @@ function cleanupStaleWatchdogRegistries() {
|
|
|
806
737
|
if (registry.version !== 1 || ownerStillMatches(registry)) continue;
|
|
807
738
|
closeRegistryPanes(registry);
|
|
808
739
|
rmSync(file, { force: true });
|
|
809
|
-
} catch {
|
|
740
|
+
} catch (error) {
|
|
741
|
+
debug("cleanupStaleWatchdogRegistries failed", errorMessage(error));
|
|
810
742
|
rmSync(file, { force: true });
|
|
811
743
|
}
|
|
812
744
|
}
|
|
@@ -814,7 +746,8 @@ function cleanupStaleWatchdogRegistries() {
|
|
|
814
746
|
function ownerStillMatches(registry) {
|
|
815
747
|
try {
|
|
816
748
|
process.kill(registry.ownerPid, 0);
|
|
817
|
-
} catch {
|
|
749
|
+
} catch (error) {
|
|
750
|
+
debug("ownerStillMatches kill check failed", errorMessage(error));
|
|
818
751
|
return false;
|
|
819
752
|
}
|
|
820
753
|
return !registry.ownerStartTime || linuxProcessStartTime(registry.ownerPid) === registry.ownerStartTime;
|
|
@@ -866,7 +799,10 @@ function unregisterPaneFromWatchdog(sessionId) {
|
|
|
866
799
|
function removeWatchdogRegistry() {
|
|
867
800
|
try {
|
|
868
801
|
rmSync(watchdogRegistryPath(), { force: true });
|
|
869
|
-
} catch {
|
|
802
|
+
} catch (error) {
|
|
803
|
+
debug("removeWatchdogRegistry failed", errorMessage(error));
|
|
804
|
+
}
|
|
805
|
+
if (!watchdogChild) watchdogStarted = false;
|
|
870
806
|
}
|
|
871
807
|
//#endregion
|
|
872
808
|
//#region src/pty/ring-buffer.ts
|
|
@@ -1011,14 +947,26 @@ function extractRenderedLines(event) {
|
|
|
1011
947
|
}
|
|
1012
948
|
var SubscriberManager = class {
|
|
1013
949
|
subscribers = /* @__PURE__ */ new Map();
|
|
950
|
+
startingSessions = /* @__PURE__ */ new Map();
|
|
1014
951
|
constructor(sessions, maxBufferLines = Number(process.env.PTY_MAX_BUFFER_LINES ?? 5e4)) {
|
|
1015
952
|
this.sessions = sessions;
|
|
1016
953
|
this.maxBufferLines = maxBufferLines;
|
|
1017
954
|
}
|
|
1018
955
|
async start(session) {
|
|
1019
|
-
|
|
1020
|
-
|
|
956
|
+
if (this.subscribers.get(session.id)?.child) return;
|
|
957
|
+
const inProgress = this.startingSessions.get(session.id);
|
|
958
|
+
if (inProgress) return inProgress;
|
|
1021
959
|
ensureZellijTarget();
|
|
960
|
+
const startPromise = this.doStart(session);
|
|
961
|
+
this.startingSessions.set(session.id, startPromise);
|
|
962
|
+
try {
|
|
963
|
+
await startPromise;
|
|
964
|
+
} finally {
|
|
965
|
+
this.startingSessions.delete(session.id);
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
async doStart(session) {
|
|
969
|
+
const existing = this.subscribers.get(session.id);
|
|
1022
970
|
const state = existing ?? {
|
|
1023
971
|
child: null,
|
|
1024
972
|
buffer: new RingBuffer(this.maxBufferLines),
|
|
@@ -1027,13 +975,7 @@ var SubscriberManager = class {
|
|
|
1027
975
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1028
976
|
lastExitedAt: null
|
|
1029
977
|
};
|
|
1030
|
-
if (!existing)
|
|
1031
|
-
try {
|
|
1032
|
-
state.buffer.appendSnapshot(await zellijCli.dumpScreen(session.paneId));
|
|
1033
|
-
this.sessions.updateLineCount(session.id, state.buffer.lineCount);
|
|
1034
|
-
} catch {}
|
|
1035
|
-
this.subscribers.set(session.id, state);
|
|
1036
|
-
}
|
|
978
|
+
if (!existing) this.subscribers.set(session.id, state);
|
|
1037
979
|
const child = spawn("zellij", zellijCommandArgs([
|
|
1038
980
|
"subscribe",
|
|
1039
981
|
"--pane-id",
|
|
@@ -1048,14 +990,26 @@ var SubscriberManager = class {
|
|
|
1048
990
|
"pipe"
|
|
1049
991
|
] });
|
|
1050
992
|
child.stdin.end();
|
|
993
|
+
if (this.subscribers.get(session.id) !== state) {
|
|
994
|
+
child.kill("SIGTERM");
|
|
995
|
+
return;
|
|
996
|
+
}
|
|
1051
997
|
state.child = child;
|
|
1052
998
|
state.lastExitedAt = null;
|
|
1053
999
|
child.stdout.setEncoding("utf8");
|
|
1054
|
-
child.stdout.on("data", (chunk) => this.handleStdout(session.id, chunk));
|
|
1000
|
+
child.stdout.on("data", (chunk) => this.handleStdout(session.id, child, chunk));
|
|
1055
1001
|
child.stderr.setEncoding("utf8");
|
|
1056
|
-
child.stderr.on("data", (chunk) => this.handleStderr(session.id, chunk));
|
|
1057
|
-
child.on("exit", () => this.handleSubscriberExit(session.id));
|
|
1058
|
-
child.on("error", (error) => this.handleSubscriberError(session.id, error));
|
|
1002
|
+
child.stderr.on("data", (chunk) => this.handleStderr(session.id, child, chunk));
|
|
1003
|
+
child.on("exit", () => this.handleSubscriberExit(session.id, child));
|
|
1004
|
+
child.on("error", (error) => this.handleSubscriberError(session.id, child, error));
|
|
1005
|
+
if (!existing) try {
|
|
1006
|
+
const snapshot = await zellijCli.dumpScreen(session.paneId);
|
|
1007
|
+
if (this.subscribers.get(session.id) !== state || state.child !== child) return;
|
|
1008
|
+
state.buffer.appendSnapshot(snapshot);
|
|
1009
|
+
this.sessions.updateLineCount(session.id, state.buffer.lineCount);
|
|
1010
|
+
} catch (error) {
|
|
1011
|
+
debug("dumpScreen failed", errorMessage(error));
|
|
1012
|
+
}
|
|
1059
1013
|
}
|
|
1060
1014
|
read(sessionId, input) {
|
|
1061
1015
|
const state = this.subscribers.get(sessionId);
|
|
@@ -1095,18 +1049,20 @@ var SubscriberManager = class {
|
|
|
1095
1049
|
this.stop(sessionId);
|
|
1096
1050
|
try {
|
|
1097
1051
|
await zellijCli.closePane(session.paneId);
|
|
1098
|
-
} catch {
|
|
1052
|
+
} catch (error) {
|
|
1053
|
+
debug("closePane failed", errorMessage(error));
|
|
1054
|
+
}
|
|
1099
1055
|
}
|
|
1100
|
-
handleStdout(sessionId, chunk) {
|
|
1056
|
+
handleStdout(sessionId, child, chunk) {
|
|
1101
1057
|
const state = this.subscribers.get(sessionId);
|
|
1102
|
-
if (!state) return;
|
|
1058
|
+
if (!state || state.child !== child) return;
|
|
1103
1059
|
const parts = `${state.stdoutRemainder}${chunk}`.split("\n");
|
|
1104
1060
|
state.stdoutRemainder = parts.pop() ?? "";
|
|
1105
|
-
for (const part of parts) this.handleJsonLine(sessionId, part);
|
|
1061
|
+
for (const part of parts) this.handleJsonLine(sessionId, child, part);
|
|
1106
1062
|
}
|
|
1107
|
-
handleJsonLine(sessionId, line) {
|
|
1063
|
+
handleJsonLine(sessionId, child, line) {
|
|
1108
1064
|
const state = this.subscribers.get(sessionId);
|
|
1109
|
-
if (!state) return;
|
|
1065
|
+
if (!state || state.child !== child) return;
|
|
1110
1066
|
const trimmed = line.trim();
|
|
1111
1067
|
if (!trimmed) return;
|
|
1112
1068
|
let event;
|
|
@@ -1114,12 +1070,20 @@ var SubscriberManager = class {
|
|
|
1114
1070
|
const parsed = JSON.parse(trimmed);
|
|
1115
1071
|
if (!parsed || typeof parsed !== "object") return;
|
|
1116
1072
|
event = parsed;
|
|
1117
|
-
} catch {
|
|
1073
|
+
} catch (error) {
|
|
1118
1074
|
state.buffer.append(trimmed);
|
|
1119
1075
|
this.sessions.updateLineCount(sessionId, state.buffer.lineCount);
|
|
1076
|
+
debug("JSON parse of subscriber event failed, treating as raw text", errorMessage(error));
|
|
1077
|
+
return;
|
|
1078
|
+
}
|
|
1079
|
+
let session;
|
|
1080
|
+
try {
|
|
1081
|
+
session = this.sessions.get(sessionId);
|
|
1082
|
+
} catch (error) {
|
|
1083
|
+
this.forget(sessionId);
|
|
1084
|
+
debug("session lookup by id failed", errorMessage(error));
|
|
1120
1085
|
return;
|
|
1121
1086
|
}
|
|
1122
|
-
const session = this.sessions.get(sessionId);
|
|
1123
1087
|
const paneId = eventPaneId(event);
|
|
1124
1088
|
if (paneId && paneId !== session.paneId) return;
|
|
1125
1089
|
const type = eventType(event);
|
|
@@ -1147,24 +1111,29 @@ var SubscriberManager = class {
|
|
|
1147
1111
|
return;
|
|
1148
1112
|
}
|
|
1149
1113
|
}
|
|
1150
|
-
handleStderr(sessionId, chunk) {
|
|
1114
|
+
handleStderr(sessionId, child, chunk) {
|
|
1151
1115
|
const state = this.subscribers.get(sessionId);
|
|
1152
|
-
if (!state) return;
|
|
1116
|
+
if (!state || state.child !== child) return;
|
|
1153
1117
|
state.stderr.push(...splitLines(chunk));
|
|
1154
1118
|
if (state.stderr.length > maxStderrLines) state.stderr = state.stderr.slice(state.stderr.length - maxStderrLines);
|
|
1155
1119
|
}
|
|
1156
|
-
handleSubscriberExit(sessionId) {
|
|
1120
|
+
handleSubscriberExit(sessionId, child) {
|
|
1157
1121
|
const state = this.subscribers.get(sessionId);
|
|
1158
1122
|
if (!state) return;
|
|
1123
|
+
if (state.child !== child) return;
|
|
1159
1124
|
state.child = null;
|
|
1160
1125
|
state.lastExitedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1161
1126
|
state.stderr.push(`[zellij-pty] subscriber exited at ${state.lastExitedAt}; last buffered output is retained.`);
|
|
1162
1127
|
if (state.stderr.length > maxStderrLines) state.stderr = state.stderr.slice(state.stderr.length - maxStderrLines);
|
|
1163
1128
|
}
|
|
1164
|
-
handleSubscriberError(sessionId, error) {
|
|
1129
|
+
handleSubscriberError(sessionId, child, error) {
|
|
1165
1130
|
const state = this.subscribers.get(sessionId);
|
|
1166
|
-
if (state)
|
|
1167
|
-
|
|
1131
|
+
if (state?.child === child) {
|
|
1132
|
+
state.stderr.push(error.message);
|
|
1133
|
+
state.child = null;
|
|
1134
|
+
state.lastExitedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1135
|
+
this.sessions.updateStatus(sessionId, "unknown");
|
|
1136
|
+
}
|
|
1168
1137
|
}
|
|
1169
1138
|
};
|
|
1170
1139
|
const subscriberManager = new SubscriberManager(sessionManager);
|
|
@@ -1214,7 +1183,7 @@ function validateGrep(grep) {
|
|
|
1214
1183
|
new RegExp(grep).test("");
|
|
1215
1184
|
return null;
|
|
1216
1185
|
} catch (error) {
|
|
1217
|
-
return
|
|
1186
|
+
return errorMessage(error);
|
|
1218
1187
|
}
|
|
1219
1188
|
}
|
|
1220
1189
|
function readOutputSnapshot(sessionId, options = {}) {
|
|
@@ -1327,11 +1296,12 @@ const zellijPtyReadTool = tool({
|
|
|
1327
1296
|
next: nextAdvice(false, `Invalid grep regex: ${grepError}`),
|
|
1328
1297
|
warnings: []
|
|
1329
1298
|
});
|
|
1330
|
-
if (!subscriberManager.has(session.id)) await subscriberManager.start(session);
|
|
1331
1299
|
const subscriberStatus = subscriberManager.status(session.id);
|
|
1300
|
+
if (!subscriberStatus.hasBuffer || !subscriberStatus.active && (session.status === "running" || session.status === "unknown")) await subscriberManager.start(session);
|
|
1301
|
+
const statusAfterStart = subscriberManager.status(session.id);
|
|
1332
1302
|
const warnings = [];
|
|
1333
1303
|
if (session.humanInputOnly) warnings.push("This pane is human-input-only: agent writes are forbidden, but rendered output is visible to the agent.");
|
|
1334
|
-
if (!
|
|
1304
|
+
if (!statusAfterStart.active) {
|
|
1335
1305
|
warnings.push("Subscriber is inactive; returned output may be stale.");
|
|
1336
1306
|
if (session.status === "running") sessionManager.updateStatus(session.id, "unknown");
|
|
1337
1307
|
}
|
|
@@ -1344,8 +1314,8 @@ const zellijPtyReadTool = tool({
|
|
|
1344
1314
|
session: publicSession(session),
|
|
1345
1315
|
output,
|
|
1346
1316
|
next: nextAdvice(session.status !== "exited" && session.status !== "killed", nextReadReason(session.status)),
|
|
1347
|
-
subscriberActive:
|
|
1348
|
-
subscriberLastExitedAt:
|
|
1317
|
+
subscriberActive: statusAfterStart.active,
|
|
1318
|
+
subscriberLastExitedAt: statusAfterStart.lastExitedAt,
|
|
1349
1319
|
subscriberErrors: subscriberManager.stderr(session.id),
|
|
1350
1320
|
warnings
|
|
1351
1321
|
});
|
|
@@ -1407,10 +1377,7 @@ const requestSudoTool = tool({
|
|
|
1407
1377
|
async execute(args, context) {
|
|
1408
1378
|
const cwd = context.directory;
|
|
1409
1379
|
const exitCodeToken = createExitCodeToken();
|
|
1410
|
-
|
|
1411
|
-
command: script.command,
|
|
1412
|
-
humanInputOnly: true
|
|
1413
|
-
});
|
|
1380
|
+
assertSudoPaneAllowed();
|
|
1414
1381
|
const command = buildReviewScript(args.summary, args.scripts);
|
|
1415
1382
|
const title = createOpenCodePaneTitle("zellij_pty_request_sudo");
|
|
1416
1383
|
const paneId = await zellijCli.newPane({
|
|
@@ -1536,11 +1503,6 @@ const zellijPtySpawnTool = tool({
|
|
|
1536
1503
|
async execute(args, context) {
|
|
1537
1504
|
const cwd = args.cwd ?? context.directory;
|
|
1538
1505
|
const exitCodeToken = createExitCodeToken();
|
|
1539
|
-
assertCommandAllowed({
|
|
1540
|
-
command: args.command,
|
|
1541
|
-
args: args.args,
|
|
1542
|
-
humanInputOnly: false
|
|
1543
|
-
});
|
|
1544
1506
|
const grepError = args.probe?.type === "output" ? validateGrep(args.probe.grep) : null;
|
|
1545
1507
|
if (grepError) throw new Error(`Invalid probe.grep regex: ${grepError}`);
|
|
1546
1508
|
const title = createOpenCodePaneTitle(args.title ?? args.command);
|
|
@@ -1633,16 +1595,23 @@ const zellijPtyWriteTool = tool({
|
|
|
1633
1595
|
session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1634
1596
|
if (args.interruptAfterSeconds) {
|
|
1635
1597
|
await setTimeout$1(args.interruptAfterSeconds * 1e3);
|
|
1636
|
-
if (sessionManager.
|
|
1598
|
+
if (sessionManager.find(session.id)?.status === "running") {
|
|
1637
1599
|
await zellijCli.sendCtrlC(session.paneId);
|
|
1638
1600
|
await setTimeout$1(500);
|
|
1639
1601
|
}
|
|
1640
1602
|
} else await setTimeout$1(1e3);
|
|
1603
|
+
const warnings = [];
|
|
1604
|
+
let output = emptyOutputSnapshot(session.lineCount);
|
|
1605
|
+
try {
|
|
1606
|
+
output = readOutputSnapshot(session.id, { maxLines: args.maxLines });
|
|
1607
|
+
} catch (error) {
|
|
1608
|
+
warnings.push(`Session output was unavailable before the write response completed: ${errorMessage(error)}`);
|
|
1609
|
+
}
|
|
1641
1610
|
return jsonResponse({
|
|
1642
1611
|
session: publicSession(session),
|
|
1643
|
-
output
|
|
1612
|
+
output,
|
|
1644
1613
|
next: nextAdvice(true, args.interruptAfterSeconds ? "Input was sent; Ctrl-C was sent after the requested interrupt timeout if the session was still running." : "Input was sent and recent output was observed."),
|
|
1645
|
-
warnings
|
|
1614
|
+
warnings
|
|
1646
1615
|
});
|
|
1647
1616
|
}
|
|
1648
1617
|
});
|
|
@@ -1656,11 +1625,15 @@ function cleanupPanesOnShutdown(sessions = sessionManager, subscribers = subscri
|
|
|
1656
1625
|
for (const session of sessions.list()) {
|
|
1657
1626
|
try {
|
|
1658
1627
|
zellijCli.closePaneSync(session.paneId);
|
|
1659
|
-
} catch {
|
|
1628
|
+
} catch (error) {
|
|
1629
|
+
debug("cleanupPanesOnShutdown closePane failed", errorMessage(error));
|
|
1630
|
+
}
|
|
1660
1631
|
subscribers.forget(session.id);
|
|
1661
1632
|
try {
|
|
1662
1633
|
sessions.remove(session.id);
|
|
1663
|
-
} catch {
|
|
1634
|
+
} catch (error) {
|
|
1635
|
+
debug("cleanupPanesOnShutdown sessions.remove failed", errorMessage(error));
|
|
1636
|
+
}
|
|
1664
1637
|
}
|
|
1665
1638
|
}
|
|
1666
1639
|
function registerShutdownCleanup() {
|
|
@@ -1730,7 +1703,8 @@ async function readGitBranch(worktree) {
|
|
|
1730
1703
|
async function getInitialBranch(worktree, readBranch = readGitBranch) {
|
|
1731
1704
|
try {
|
|
1732
1705
|
return (await readBranch(worktree)).trim() || void 0;
|
|
1733
|
-
} catch {
|
|
1706
|
+
} catch (error) {
|
|
1707
|
+
debug("getInitialBranch failed", errorMessage(error));
|
|
1734
1708
|
return;
|
|
1735
1709
|
}
|
|
1736
1710
|
}
|
|
@@ -1915,7 +1889,7 @@ var TabTitleManager = class {
|
|
|
1915
1889
|
this.clearDebounceTimer();
|
|
1916
1890
|
this.debounceTimer = setTimeout(() => {
|
|
1917
1891
|
this.debounceTimer = void 0;
|
|
1918
|
-
this.syncDesiredTitle().catch(() =>
|
|
1892
|
+
this.syncDesiredTitle().catch((error) => debug("debounced tab title sync failed", errorMessage(error)));
|
|
1919
1893
|
}, this.debounceMs);
|
|
1920
1894
|
this.unrefTimer(this.debounceTimer);
|
|
1921
1895
|
}
|
|
@@ -1947,7 +1921,7 @@ var TabTitleManager = class {
|
|
|
1947
1921
|
this.retryAttempt += 1;
|
|
1948
1922
|
this.retryTimer = setTimeout(() => {
|
|
1949
1923
|
this.retryTimer = void 0;
|
|
1950
|
-
this.syncDesiredTitle().catch(() =>
|
|
1924
|
+
this.syncDesiredTitle().catch((error) => debug("retry tab title sync failed", errorMessage(error)));
|
|
1951
1925
|
}, delay);
|
|
1952
1926
|
this.unrefTimer(this.retryTimer);
|
|
1953
1927
|
}
|
|
@@ -1989,28 +1963,41 @@ function showUpdateToast(client, result) {
|
|
|
1989
1963
|
message: `Updated to ${result.toVersion}. Restart OpenCode to apply the changes.`,
|
|
1990
1964
|
variant: "success",
|
|
1991
1965
|
duration: 1e4
|
|
1992
|
-
} }).catch(() =>
|
|
1966
|
+
} }).catch((error) => debug("show update toast for successful update failed", errorMessage(error)));
|
|
1993
1967
|
else if (result.type === "failed") client.tui.showToast({ body: {
|
|
1994
1968
|
title: "opencode-zellij update failed",
|
|
1995
1969
|
message: `Failed to update to ${result.latestVersion}.`,
|
|
1996
1970
|
variant: "error",
|
|
1997
1971
|
duration: 8e3
|
|
1998
|
-
} }).catch(() =>
|
|
1972
|
+
} }).catch((error) => debug("show update toast for failed update failed", errorMessage(error)));
|
|
1999
1973
|
}
|
|
2000
1974
|
function startAutoUpdateCheck(client, importMetaUrl, check = checkAndUpdate) {
|
|
2001
1975
|
(async () => {
|
|
2002
1976
|
try {
|
|
2003
1977
|
showUpdateToast(client, await check({ importMetaUrl }));
|
|
2004
1978
|
} catch (cause) {
|
|
2005
|
-
debug("auto-update check failed",
|
|
1979
|
+
debug("auto-update check failed", errorMessage(cause));
|
|
2006
1980
|
}
|
|
2007
1981
|
})();
|
|
2008
1982
|
}
|
|
1983
|
+
async function cleanupStep(stepName, sessionId, step) {
|
|
1984
|
+
try {
|
|
1985
|
+
await step();
|
|
1986
|
+
} catch (error) {
|
|
1987
|
+
debug(`session.deleted cleanup failed: ${stepName} for ${sessionId}`, errorMessage(error));
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1990
|
+
async function cleanupDeletedSession(sessionId) {
|
|
1991
|
+
await cleanupStep("close pane", sessionId, () => subscriberManager.closeSessionPane(sessionId));
|
|
1992
|
+
await cleanupStep("forget subscriber", sessionId, () => subscriberManager.forget(sessionId));
|
|
1993
|
+
await cleanupStep("unregister watchdog", sessionId, () => unregisterPaneFromWatchdog(sessionId));
|
|
1994
|
+
await cleanupStep("remove session", sessionId, () => sessionManager.remove(sessionId));
|
|
1995
|
+
}
|
|
2009
1996
|
function createZellijPtyPlugin(dependencies = {}) {
|
|
2010
1997
|
return async (input) => {
|
|
2011
1998
|
const { config, warnings } = await loadConfig(input);
|
|
2012
1999
|
for (const warning of warnings) debug(warning);
|
|
2013
|
-
|
|
2000
|
+
configureSudoPane(config.pty.sudoPane === "allow");
|
|
2014
2001
|
cleanupStaleWatchdogRegistries();
|
|
2015
2002
|
registerShutdownCleanup();
|
|
2016
2003
|
const workspaceRoot = getWorkspaceRoot(input);
|
|
@@ -2027,9 +2014,9 @@ function createZellijPtyPlugin(dependencies = {}) {
|
|
|
2027
2014
|
branch: config.tabTitle.emojiBranch
|
|
2028
2015
|
}
|
|
2029
2016
|
}) : void 0;
|
|
2030
|
-
tabTitleManager?.renderImmediate().catch(() =>
|
|
2017
|
+
tabTitleManager?.renderImmediate().catch((error) => debug("initial tab title render failed", errorMessage(error)));
|
|
2031
2018
|
const client = input.client;
|
|
2032
|
-
if (config.autoUpdate
|
|
2019
|
+
if (config.autoUpdate) (dependencies.startAutoUpdateCheck ?? startAutoUpdateCheck)(client, dependencies.importMetaUrl ?? import.meta.url);
|
|
2033
2020
|
return {
|
|
2034
2021
|
async event(input) {
|
|
2035
2022
|
const event = input.event;
|
|
@@ -2038,12 +2025,7 @@ function createZellijPtyPlugin(dependencies = {}) {
|
|
|
2038
2025
|
const sessionID = deletedSessionID(event);
|
|
2039
2026
|
if (!sessionID) return;
|
|
2040
2027
|
const sessions = sessionManager.listByOpenCodeSession(sessionID);
|
|
2041
|
-
await Promise.all(sessions.map(
|
|
2042
|
-
await subscriberManager.closeSessionPane(session.id);
|
|
2043
|
-
subscriberManager.forget(session.id);
|
|
2044
|
-
unregisterPaneFromWatchdog(session.id);
|
|
2045
|
-
sessionManager.remove(session.id);
|
|
2046
|
-
}));
|
|
2028
|
+
await Promise.all(sessions.map((session) => cleanupDeletedSession(session.id)));
|
|
2047
2029
|
}
|
|
2048
2030
|
},
|
|
2049
2031
|
tool: config.pty.enabled ? {
|