opencode-zellij 0.0.16 → 0.0.18
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/README.md +13 -2
- package/README.zh.md +13 -2
- package/dist/index.d.mts +9 -107
- package/dist/index.mjs +220 -709
- package/dist/index.mjs.map +1 -1
- package/dist/pane-watchdog-runner.mjs +1 -1
- package/package.json +5 -2
package/dist/index.mjs
CHANGED
|
@@ -1,262 +1,26 @@
|
|
|
1
1
|
import process from "node:process";
|
|
2
|
-
import { execFile, spawn, spawnSync } from "node:child_process";
|
|
3
2
|
import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
4
|
-
import { readFile
|
|
5
|
-
import path, { basename, dirname, join } from "node:path";
|
|
6
|
-
import { fileURLToPath } from "node:url";
|
|
7
|
-
import { promisify } from "node:util";
|
|
3
|
+
import { readFile } from "node:fs/promises";
|
|
8
4
|
import { homedir, tmpdir } from "node:os";
|
|
5
|
+
import path, { dirname, join } from "node:path";
|
|
9
6
|
import { parseJSON, parseJSONC } from "confbox";
|
|
10
7
|
import { z } from "zod";
|
|
11
8
|
import { randomUUID } from "node:crypto";
|
|
12
9
|
import { setTimeout as setTimeout$1 } from "node:timers/promises";
|
|
13
10
|
import { tool } from "@opencode-ai/plugin";
|
|
11
|
+
import { execFile, spawn, spawnSync } from "node:child_process";
|
|
12
|
+
import { promisify } from "node:util";
|
|
13
|
+
import { fileURLToPath } from "node:url";
|
|
14
14
|
import { Buffer } from "node:buffer";
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
console.warn(`[opencode-zellij] ${message}`, ...details);
|
|
19
|
-
}
|
|
20
|
-
//#endregion
|
|
21
|
-
//#region src/utils/errors.ts
|
|
22
|
-
function errorMessage(error) {
|
|
23
|
-
return error instanceof Error ? error.message : String(error);
|
|
24
|
-
}
|
|
25
|
-
//#endregion
|
|
26
|
-
//#region src/auto-update.ts
|
|
27
|
-
const PACKAGE_NAME = "opencode-zellij";
|
|
28
|
-
const NPM_REGISTRY_URL = "https://registry.npmjs.org/-/package/opencode-zellij/dist-tags";
|
|
29
|
-
const FETCH_TIMEOUT_MS = 5e3;
|
|
30
|
-
const INSTALL_TIMEOUT_MS = 6e4;
|
|
31
|
-
const defaultExecFile = promisify(execFile);
|
|
32
|
-
function packageDir(installRoot) {
|
|
33
|
-
return join(installRoot, "node_modules", PACKAGE_NAME);
|
|
34
|
-
}
|
|
35
|
-
function backupDir(installRoot) {
|
|
36
|
-
return join(installRoot, "node_modules", `${PACKAGE_NAME}.update-backup`);
|
|
37
|
-
}
|
|
38
|
-
async function installedPackageMetadata(installRoot) {
|
|
39
|
-
try {
|
|
40
|
-
const content = await readFile(join(packageDir(installRoot), "package.json"), "utf8");
|
|
41
|
-
const pkg = JSON.parse(content);
|
|
42
|
-
if (isRecord$2(pkg)) return {
|
|
43
|
-
name: typeof pkg.name === "string" ? pkg.name : void 0,
|
|
44
|
-
version: typeof pkg.version === "string" ? pkg.version : void 0,
|
|
45
|
-
main: typeof pkg.main === "string" ? pkg.main : void 0
|
|
46
|
-
};
|
|
47
|
-
} catch (error) {
|
|
48
|
-
debug("installedPackageMetadata failed", errorMessage(error));
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
function isExpectedPackage(metadata, version) {
|
|
52
|
-
return metadata?.name === "opencode-zellij" && metadata.version === version;
|
|
53
|
-
}
|
|
54
|
-
function hasRunnableEntry(installRoot, metadata) {
|
|
55
|
-
if (!metadata) return false;
|
|
56
|
-
const dir = packageDir(installRoot);
|
|
57
|
-
if (metadata.main && existsSync(join(dir, metadata.main))) return true;
|
|
58
|
-
return existsSync(join(dir, "dist", "index.mjs"));
|
|
59
|
-
}
|
|
60
|
-
async function isVerifiedInstall(installRoot, version) {
|
|
61
|
-
const metadata = await installedPackageMetadata(installRoot);
|
|
62
|
-
return isExpectedPackage(metadata, version) && hasRunnableEntry(installRoot, metadata);
|
|
63
|
-
}
|
|
64
|
-
async function removeInstalledPackage(installRoot) {
|
|
65
|
-
await rm(packageDir(installRoot), {
|
|
66
|
-
force: true,
|
|
67
|
-
recursive: true
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
async function backupInstalledPackage(installRoot) {
|
|
71
|
-
const source = packageDir(installRoot);
|
|
72
|
-
if (!existsSync(source)) return void 0;
|
|
73
|
-
const backup = backupDir(installRoot);
|
|
74
|
-
await rm(backup, {
|
|
75
|
-
force: true,
|
|
76
|
-
recursive: true
|
|
77
|
-
});
|
|
78
|
-
await rename(source, backup);
|
|
79
|
-
return backup;
|
|
80
|
-
}
|
|
81
|
-
async function restoreInstalledPackage(installRoot, backup) {
|
|
82
|
-
if (!backup || !existsSync(backup)) return;
|
|
83
|
-
await rm(packageDir(installRoot), {
|
|
84
|
-
force: true,
|
|
85
|
-
recursive: true
|
|
86
|
-
});
|
|
87
|
-
await rename(backup, packageDir(installRoot));
|
|
88
|
-
}
|
|
89
|
-
async function discardBackup(backup) {
|
|
90
|
-
if (backup) await rm(backup, {
|
|
91
|
-
force: true,
|
|
92
|
-
recursive: true
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
async function findInstallContext(importMetaUrl) {
|
|
96
|
-
let startPath;
|
|
97
|
-
try {
|
|
98
|
-
startPath = fileURLToPath(importMetaUrl);
|
|
99
|
-
} catch (cause) {
|
|
100
|
-
debug("invalid import.meta.url", cause instanceof Error ? cause.message : String(cause));
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
let dir = dirname(startPath);
|
|
104
|
-
while (true) {
|
|
105
|
-
if (dir.endsWith(`/node_modules/opencode-zellij`) || dir.endsWith(`\\node_modules\\opencode-zellij`)) {
|
|
106
|
-
const packageJsonPath = join(dir, "package.json");
|
|
107
|
-
try {
|
|
108
|
-
const content = await readFile(packageJsonPath, "utf8");
|
|
109
|
-
const pkg = JSON.parse(content);
|
|
110
|
-
if (isRecord$2(pkg) && pkg.name === "opencode-zellij" && typeof pkg.version === "string" && pkg.version.length > 0) {
|
|
111
|
-
const installRoot = dirname(dirname(dir));
|
|
112
|
-
if (existsSync(join(installRoot, "package.json"))) return {
|
|
113
|
-
installRoot,
|
|
114
|
-
cacheSpec: basename(installRoot),
|
|
115
|
-
currentVersion: pkg.version
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
} catch (error) {
|
|
119
|
-
debug("findInstallContext package.json read failed", errorMessage(error));
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
const parent = dirname(dir);
|
|
123
|
-
if (parent === dir) break;
|
|
124
|
-
dir = parent;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
function isRecord$2(value) {
|
|
128
|
-
return typeof value === "object" && value !== null;
|
|
129
|
-
}
|
|
130
|
-
function isAutoUpdatableSpec(spec) {
|
|
131
|
-
if (spec === "opencode-zellij") return true;
|
|
132
|
-
if (spec === `opencode-zellij@latest`) return true;
|
|
133
|
-
return false;
|
|
134
|
-
}
|
|
135
|
-
async function fetchLatestVersion(fetchImpl = globalThis.fetch) {
|
|
136
|
-
const controller = new AbortController();
|
|
137
|
-
const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
138
|
-
try {
|
|
139
|
-
const response = await fetchImpl(NPM_REGISTRY_URL, { signal: controller.signal });
|
|
140
|
-
clearTimeout(timeout);
|
|
141
|
-
if (!response.ok) {
|
|
142
|
-
debug(`npm registry returned ${response.status}`);
|
|
143
|
-
return;
|
|
144
|
-
}
|
|
145
|
-
const data = await response.json();
|
|
146
|
-
if (isRecord$2(data) && typeof data.latest === "string") return data.latest;
|
|
147
|
-
debug("npm registry response missing latest tag");
|
|
148
|
-
return;
|
|
149
|
-
} catch (cause) {
|
|
150
|
-
clearTimeout(timeout);
|
|
151
|
-
debug("failed to fetch latest version", cause instanceof Error ? cause.message : String(cause));
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
async function runNpmInstall(installRoot, version, execImpl = defaultExecFile) {
|
|
156
|
-
debug(`updating ${PACKAGE_NAME} to ${version} in ${installRoot}`);
|
|
157
|
-
try {
|
|
158
|
-
const install = () => execImpl("npm", [
|
|
159
|
-
"install",
|
|
160
|
-
`${PACKAGE_NAME}@${version}`,
|
|
161
|
-
"--save-exact",
|
|
162
|
-
"--ignore-scripts",
|
|
163
|
-
"--no-audit",
|
|
164
|
-
"--no-fund",
|
|
165
|
-
"--prefer-online"
|
|
166
|
-
], {
|
|
167
|
-
cwd: installRoot,
|
|
168
|
-
timeout: INSTALL_TIMEOUT_MS
|
|
169
|
-
});
|
|
170
|
-
await install();
|
|
171
|
-
if (await isVerifiedInstall(installRoot, version)) {
|
|
172
|
-
debug(`updated ${PACKAGE_NAME} to ${version}`);
|
|
173
|
-
return true;
|
|
174
|
-
}
|
|
175
|
-
const installedPackage = await installedPackageMetadata(installRoot);
|
|
176
|
-
debug(`npm install left stale or invalid ${PACKAGE_NAME} (${installedPackage?.name ?? "<missing>"}@${installedPackage?.version ?? "<missing>"}); reinstalling ${version}`);
|
|
177
|
-
const backup = await backupInstalledPackage(installRoot);
|
|
178
|
-
try {
|
|
179
|
-
await removeInstalledPackage(installRoot);
|
|
180
|
-
await install();
|
|
181
|
-
if (await isVerifiedInstall(installRoot, version)) {
|
|
182
|
-
await discardBackup(backup);
|
|
183
|
-
debug(`updated ${PACKAGE_NAME} to ${version}`);
|
|
184
|
-
return true;
|
|
185
|
-
}
|
|
186
|
-
const reinstalledPackage = await installedPackageMetadata(installRoot);
|
|
187
|
-
debug(`npm install verification failed: expected ${PACKAGE_NAME}@${version}, found ${reinstalledPackage?.name ?? "<missing>"}@${reinstalledPackage?.version ?? "<missing>"}`);
|
|
188
|
-
await restoreInstalledPackage(installRoot, backup);
|
|
189
|
-
return false;
|
|
190
|
-
} catch (cause) {
|
|
191
|
-
await restoreInstalledPackage(installRoot, backup);
|
|
192
|
-
throw cause;
|
|
193
|
-
}
|
|
194
|
-
} catch (cause) {
|
|
195
|
-
debug("npm install failed", cause instanceof Error ? cause.message : String(cause));
|
|
196
|
-
return false;
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
async function checkAndUpdate(options) {
|
|
200
|
-
const context = await findInstallContext(options.importMetaUrl);
|
|
201
|
-
if (!context) {
|
|
202
|
-
debug("skipping auto-update: not installed from npm");
|
|
203
|
-
return {
|
|
204
|
-
type: "skipped",
|
|
205
|
-
reason: "not installed from npm"
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
|
-
if (!isAutoUpdatableSpec(context.cacheSpec)) {
|
|
209
|
-
debug(`skipping auto-update: cache spec is pinned or unknown (${context.cacheSpec})`);
|
|
210
|
-
return {
|
|
211
|
-
type: "skipped",
|
|
212
|
-
reason: `cache spec is pinned or unknown (${context.cacheSpec})`
|
|
213
|
-
};
|
|
214
|
-
}
|
|
215
|
-
const latest = await fetchLatestVersion(options.fetchImpl);
|
|
216
|
-
if (!latest) {
|
|
217
|
-
debug("skipping auto-update: could not determine latest version");
|
|
218
|
-
return {
|
|
219
|
-
type: "skipped",
|
|
220
|
-
reason: "could not determine latest version"
|
|
221
|
-
};
|
|
222
|
-
}
|
|
223
|
-
const installedVersion = (await installedPackageMetadata(context.installRoot))?.version ?? context.currentVersion;
|
|
224
|
-
if (latest === installedVersion) {
|
|
225
|
-
debug(`auto-update: already on latest ${latest}`);
|
|
226
|
-
return {
|
|
227
|
-
type: "up-to-date",
|
|
228
|
-
currentVersion: installedVersion
|
|
229
|
-
};
|
|
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
|
-
};
|
|
245
|
-
}
|
|
246
|
-
//#endregion
|
|
15
|
+
import { LogFileRotationTransport } from "@loglayer/transport-log-file-rotation";
|
|
16
|
+
import { LogLayer } from "loglayer";
|
|
17
|
+
import { serializeError } from "serialize-error";
|
|
247
18
|
//#region src/config.ts
|
|
248
19
|
const sudoPaneSchema = z.enum([
|
|
249
20
|
"allow",
|
|
250
21
|
"deny",
|
|
251
22
|
"hide"
|
|
252
23
|
]);
|
|
253
|
-
const completionNotificationModeSchema = z.enum([
|
|
254
|
-
"off",
|
|
255
|
-
"queue",
|
|
256
|
-
"toast",
|
|
257
|
-
"queue+toast",
|
|
258
|
-
"prompt"
|
|
259
|
-
]);
|
|
260
24
|
const configFilenames = ["opencode-zellij.config.jsonc", "opencode-zellij.config.json"];
|
|
261
25
|
const tabTitleLayerSchema = z.object({
|
|
262
26
|
enabled: z.boolean().optional().describe("Enable dynamic Zellij tab title updates."),
|
|
@@ -269,22 +33,12 @@ const tabTitleLayerSchema = z.object({
|
|
|
269
33
|
const ptyLayerSchema = z.object({
|
|
270
34
|
enabled: z.boolean().optional().describe("Enable Zellij-backed PTY tools."),
|
|
271
35
|
cleanupExitedPaneOnRead: z.boolean().optional().describe("Remove exited PTY panes after they are read."),
|
|
272
|
-
sudoPane: sudoPaneSchema.optional().describe("Controls whether the sudo pane tool is available, denied, or hidden.")
|
|
273
|
-
completionNotification: z.object({
|
|
274
|
-
mode: completionNotificationModeSchema.optional().describe("Controls how completion notifications are delivered."),
|
|
275
|
-
prompt: z.object({
|
|
276
|
-
requireIdle: z.boolean().optional().describe("Require the plugin to be idle before prompting."),
|
|
277
|
-
cooldownMs: z.number().finite().min(0).optional().describe("Cooldown time before prompting again in milliseconds."),
|
|
278
|
-
maxAttempts: z.number().finite().int().min(0).optional().describe("Maximum prompt attempts before backing off.")
|
|
279
|
-
}).strict().optional().describe("Prompt-specific completion notification settings.")
|
|
280
|
-
}).strict().optional().describe("Completion notification delivery settings.")
|
|
36
|
+
sudoPane: sudoPaneSchema.optional().describe("Controls whether the sudo pane tool is available, denied, or hidden.")
|
|
281
37
|
}).strict();
|
|
282
|
-
const autoUpdateLayerSchema = z.boolean().optional().describe("Enable automatic update checks for the opencode-zellij plugin.");
|
|
283
38
|
const sidecarConfigSchema = z.object({
|
|
284
39
|
$schema: z.string().optional().describe("JSON Schema URI for editor completion."),
|
|
285
40
|
tabTitle: tabTitleLayerSchema.optional(),
|
|
286
|
-
pty: ptyLayerSchema.optional()
|
|
287
|
-
autoUpdate: autoUpdateLayerSchema.optional()
|
|
41
|
+
pty: ptyLayerSchema.optional()
|
|
288
42
|
}).strict();
|
|
289
43
|
const defaultConfig = {
|
|
290
44
|
tabTitle: {
|
|
@@ -298,25 +52,15 @@ const defaultConfig = {
|
|
|
298
52
|
pty: {
|
|
299
53
|
enabled: true,
|
|
300
54
|
cleanupExitedPaneOnRead: true,
|
|
301
|
-
sudoPane: "allow"
|
|
302
|
-
|
|
303
|
-
mode: "queue+toast",
|
|
304
|
-
prompt: {
|
|
305
|
-
requireIdle: true,
|
|
306
|
-
cooldownMs: 3e4,
|
|
307
|
-
maxAttempts: 1
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
},
|
|
311
|
-
autoUpdate: true
|
|
55
|
+
sudoPane: "allow"
|
|
56
|
+
}
|
|
312
57
|
};
|
|
313
58
|
function validConfigLayer(value) {
|
|
314
59
|
const result = sidecarConfigSchema.safeParse(value);
|
|
315
60
|
if (!result.success) return void 0;
|
|
316
61
|
return {
|
|
317
62
|
tabTitle: result.data.tabTitle,
|
|
318
|
-
pty: result.data.pty
|
|
319
|
-
autoUpdate: result.data.autoUpdate
|
|
63
|
+
pty: result.data.pty
|
|
320
64
|
};
|
|
321
65
|
}
|
|
322
66
|
function mergeConfig(user, project) {
|
|
@@ -332,17 +76,8 @@ function mergeConfig(user, project) {
|
|
|
332
76
|
pty: {
|
|
333
77
|
enabled: project?.pty?.enabled ?? user?.pty?.enabled ?? defaultConfig.pty.enabled,
|
|
334
78
|
cleanupExitedPaneOnRead: project?.pty?.cleanupExitedPaneOnRead ?? user?.pty?.cleanupExitedPaneOnRead ?? defaultConfig.pty.cleanupExitedPaneOnRead,
|
|
335
|
-
sudoPane: project?.pty?.sudoPane ?? user?.pty?.sudoPane ?? defaultConfig.pty.sudoPane
|
|
336
|
-
|
|
337
|
-
mode: project?.pty?.completionNotification?.mode ?? user?.pty?.completionNotification?.mode ?? defaultConfig.pty.completionNotification.mode,
|
|
338
|
-
prompt: {
|
|
339
|
-
requireIdle: project?.pty?.completionNotification?.prompt?.requireIdle ?? user?.pty?.completionNotification?.prompt?.requireIdle ?? defaultConfig.pty.completionNotification.prompt.requireIdle,
|
|
340
|
-
cooldownMs: project?.pty?.completionNotification?.prompt?.cooldownMs ?? user?.pty?.completionNotification?.prompt?.cooldownMs ?? defaultConfig.pty.completionNotification.prompt.cooldownMs,
|
|
341
|
-
maxAttempts: project?.pty?.completionNotification?.prompt?.maxAttempts ?? user?.pty?.completionNotification?.prompt?.maxAttempts ?? defaultConfig.pty.completionNotification.prompt.maxAttempts
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
},
|
|
345
|
-
autoUpdate: project?.autoUpdate ?? user?.autoUpdate ?? defaultConfig.autoUpdate
|
|
79
|
+
sudoPane: project?.pty?.sudoPane ?? user?.pty?.sudoPane ?? defaultConfig.pty.sudoPane
|
|
80
|
+
}
|
|
346
81
|
};
|
|
347
82
|
}
|
|
348
83
|
async function loadConfigLayer(directory, warnings) {
|
|
@@ -490,8 +225,7 @@ var SessionManager = class {
|
|
|
490
225
|
reason: input.reason,
|
|
491
226
|
terminalAt: now,
|
|
492
227
|
tail: (input.tail ?? []).slice(-tombstoneTailLimit),
|
|
493
|
-
paneClosedAt: null
|
|
494
|
-
notificationSentAt: null
|
|
228
|
+
paneClosedAt: null
|
|
495
229
|
};
|
|
496
230
|
session.status = "terminal";
|
|
497
231
|
session.tombstone = tombstone;
|
|
@@ -521,16 +255,6 @@ var SessionManager = class {
|
|
|
521
255
|
}
|
|
522
256
|
return session;
|
|
523
257
|
}
|
|
524
|
-
markTerminalNotificationSent(id) {
|
|
525
|
-
const session = this.get(id);
|
|
526
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
527
|
-
if (!session.tombstone) return session;
|
|
528
|
-
if (!session.tombstone.notificationSentAt) {
|
|
529
|
-
session.tombstone.notificationSentAt = now;
|
|
530
|
-
session.updatedAt = now;
|
|
531
|
-
}
|
|
532
|
-
return session;
|
|
533
|
-
}
|
|
534
258
|
listByOpenCodeSession(openCodeSessionId) {
|
|
535
259
|
return this.list().filter((session) => session.openCodeSessionId === openCodeSessionId);
|
|
536
260
|
}
|
|
@@ -573,6 +297,17 @@ function buildCommandArgv(input, options = {}) {
|
|
|
573
297
|
];
|
|
574
298
|
}
|
|
575
299
|
//#endregion
|
|
300
|
+
//#region src/utils/debug.ts
|
|
301
|
+
function debug(message, ...details) {
|
|
302
|
+
if (!process.env.ZELLIJ_PTY_DEBUG) return;
|
|
303
|
+
console.warn(`[opencode-zellij] ${message}`, ...details);
|
|
304
|
+
}
|
|
305
|
+
//#endregion
|
|
306
|
+
//#region src/utils/errors.ts
|
|
307
|
+
function errorMessage(error) {
|
|
308
|
+
return error instanceof Error ? error.message : String(error);
|
|
309
|
+
}
|
|
310
|
+
//#endregion
|
|
576
311
|
//#region src/zellij/parse.ts
|
|
577
312
|
function numericProperty(object, keys) {
|
|
578
313
|
for (const key of keys) {
|
|
@@ -1393,48 +1128,51 @@ var SubscriberManager = class {
|
|
|
1393
1128
|
const subscriberManager = new SubscriberManager(sessionManager);
|
|
1394
1129
|
//#endregion
|
|
1395
1130
|
//#region src/tools/format.ts
|
|
1396
|
-
function publicSession(session) {
|
|
1397
|
-
|
|
1131
|
+
function publicSession(session, options = {}) {
|
|
1132
|
+
const status = session.status === "terminal" ? "exited" : session.status;
|
|
1133
|
+
const summary = {
|
|
1398
1134
|
id: session.id,
|
|
1399
1135
|
paneId: session.paneId,
|
|
1400
1136
|
title: session.title,
|
|
1401
1137
|
command: session.command,
|
|
1402
|
-
|
|
1403
|
-
cwd: session.cwd,
|
|
1404
|
-
status: session.status === "terminal" ? "exited" : session.status,
|
|
1405
|
-
lineCount: session.lineCount,
|
|
1406
|
-
createdAt: session.createdAt,
|
|
1407
|
-
updatedAt: session.updatedAt,
|
|
1408
|
-
agentWritable: session.allowAgentInput,
|
|
1409
|
-
humanInputOnly: session.humanInputOnly,
|
|
1410
|
-
exitCode: session.exitCode,
|
|
1411
|
-
exitedAt: session.exitedAt,
|
|
1412
|
-
tombstone: session.tombstone ? {
|
|
1413
|
-
reason: session.tombstone.reason,
|
|
1414
|
-
terminalAt: session.tombstone.terminalAt,
|
|
1415
|
-
tailLines: session.tombstone.tail.length,
|
|
1416
|
-
paneClosedAt: session.tombstone.paneClosedAt,
|
|
1417
|
-
notificationSentAt: session.tombstone.notificationSentAt
|
|
1418
|
-
} : null
|
|
1138
|
+
status
|
|
1419
1139
|
};
|
|
1140
|
+
if (options.agentWritable === false) summary.humanInputOnly = true;
|
|
1141
|
+
if (options.includeTombstone) summary.tombstone = session.tombstone ? {
|
|
1142
|
+
reason: session.tombstone.reason,
|
|
1143
|
+
terminalAt: session.tombstone.terminalAt,
|
|
1144
|
+
tailLines: session.tombstone.tail.length,
|
|
1145
|
+
paneClosedAt: session.tombstone.paneClosedAt
|
|
1146
|
+
} : null;
|
|
1147
|
+
return summary;
|
|
1420
1148
|
}
|
|
1421
|
-
function
|
|
1149
|
+
function jsonResponse(value) {
|
|
1150
|
+
return JSON.stringify(value, null, 2);
|
|
1151
|
+
}
|
|
1152
|
+
function publicCompletedPane(session) {
|
|
1153
|
+
const status = session.status === "terminal" ? "exited" : session.status;
|
|
1154
|
+
const safeStatus = status === "exited" || status === "killed" || status === "unknown" ? status : "unknown";
|
|
1422
1155
|
return {
|
|
1423
|
-
|
|
1424
|
-
|
|
1156
|
+
id: session.id,
|
|
1157
|
+
paneId: session.paneId,
|
|
1158
|
+
status: safeStatus,
|
|
1159
|
+
exitCode: session.exitCode,
|
|
1160
|
+
reason: session.tombstone?.reason ?? null
|
|
1425
1161
|
};
|
|
1426
1162
|
}
|
|
1427
|
-
function
|
|
1428
|
-
|
|
1163
|
+
function completedPanesFromSessions(sessions) {
|
|
1164
|
+
const completedPanes = sessions.filter((session) => session.status === "terminal").map(publicCompletedPane);
|
|
1165
|
+
return {
|
|
1166
|
+
completedPaneIds: completedPanes.map((pane) => pane.id),
|
|
1167
|
+
completedPanes
|
|
1168
|
+
};
|
|
1429
1169
|
}
|
|
1430
1170
|
//#endregion
|
|
1431
1171
|
//#region src/tools/output.ts
|
|
1432
1172
|
function emptyOutputSnapshot(lineCount = 0) {
|
|
1433
1173
|
return {
|
|
1434
1174
|
text: "",
|
|
1435
|
-
lines: [],
|
|
1436
1175
|
lineCount,
|
|
1437
|
-
returned: 0,
|
|
1438
1176
|
truncated: false
|
|
1439
1177
|
};
|
|
1440
1178
|
}
|
|
@@ -1456,20 +1194,20 @@ function readOutputSnapshot(sessionId, options = {}) {
|
|
|
1456
1194
|
ignoreCase: options.ignoreCase
|
|
1457
1195
|
});
|
|
1458
1196
|
sessionManager.updateLineCount(sessionId, buffered.lineCount);
|
|
1459
|
-
|
|
1197
|
+
const snapshot = {
|
|
1460
1198
|
text: buffered.lines.join("\n"),
|
|
1461
|
-
lines: buffered.lines,
|
|
1462
1199
|
lineCount: buffered.lineCount,
|
|
1463
|
-
returned: buffered.returned,
|
|
1464
1200
|
truncated: buffered.offset > 0
|
|
1465
1201
|
};
|
|
1202
|
+
if (options.grep !== void 0) snapshot.matched = buffered.returned;
|
|
1203
|
+
return snapshot;
|
|
1466
1204
|
}
|
|
1467
1205
|
function outputMatches(sessionId, grep, ignoreCase) {
|
|
1468
|
-
return readOutputSnapshot(sessionId, {
|
|
1206
|
+
return (readOutputSnapshot(sessionId, {
|
|
1469
1207
|
maxLines: 5e3,
|
|
1470
1208
|
grep,
|
|
1471
1209
|
ignoreCase
|
|
1472
|
-
}).
|
|
1210
|
+
}).matched ?? 0) > 0;
|
|
1473
1211
|
}
|
|
1474
1212
|
//#endregion
|
|
1475
1213
|
//#region src/tools/pane-cleanup.ts
|
|
@@ -1528,7 +1266,6 @@ async function executeZellijPtyKill(args, dependencies = {}) {
|
|
|
1528
1266
|
cleanedUp: false,
|
|
1529
1267
|
session: publicSession(sessionManager.updateStatus(session.id, "unknown")),
|
|
1530
1268
|
output,
|
|
1531
|
-
next: nextAdvice(true, "close-pane failed and the pane may still be running; the session was kept so kill can be retried."),
|
|
1532
1269
|
warnings
|
|
1533
1270
|
};
|
|
1534
1271
|
}
|
|
@@ -1552,7 +1289,6 @@ async function finalizeKilledSession(sessionId, paneId, output, warnings) {
|
|
|
1552
1289
|
id: sessionId,
|
|
1553
1290
|
paneId,
|
|
1554
1291
|
output,
|
|
1555
|
-
next: nextAdvice(false, "Session was closed and removed from the in-memory registry."),
|
|
1556
1292
|
warnings
|
|
1557
1293
|
};
|
|
1558
1294
|
}
|
|
@@ -1562,10 +1298,14 @@ const zellijPtyListTool = tool({
|
|
|
1562
1298
|
description: "List known Zellij pane-backed PTY sessions created by this plugin process for the current OpenCode session.",
|
|
1563
1299
|
args: {},
|
|
1564
1300
|
async execute(_args, context) {
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1301
|
+
const sessionList = sessionManager.listByOpenCodeSession(context.sessionID);
|
|
1302
|
+
return jsonResponse({
|
|
1303
|
+
sessions: sessionList.map((session) => ({
|
|
1304
|
+
...publicSession(session, { includeTombstone: true }),
|
|
1305
|
+
subscriber: subscriberManager.status(session.id)
|
|
1306
|
+
})),
|
|
1307
|
+
...completedPanesFromSessions(sessionList)
|
|
1308
|
+
});
|
|
1569
1309
|
}
|
|
1570
1310
|
});
|
|
1571
1311
|
//#endregion
|
|
@@ -1575,26 +1315,22 @@ async function executeZellijPtyRead(args, dependencies = {}) {
|
|
|
1575
1315
|
const sessionManagerApi = dependencies.sessionManager ?? sessionManager;
|
|
1576
1316
|
const subscriberManagerApi = dependencies.subscriberManager ?? subscriberManager;
|
|
1577
1317
|
const publicSessionApi = dependencies.publicSession ?? publicSession;
|
|
1578
|
-
const nextAdviceApi = dependencies.nextAdvice ?? nextAdvice;
|
|
1579
1318
|
const readOutputSnapshotApi = dependencies.readOutputSnapshot ?? readOutputSnapshot;
|
|
1580
1319
|
const validateGrepApi = dependencies.validateGrep ?? validateGrep;
|
|
1581
1320
|
const paneExistsApi = dependencies.paneExists ?? ((paneId) => zellijCli.paneExists(paneId));
|
|
1582
1321
|
const session = sessionManagerApi.get(args.id);
|
|
1583
1322
|
const grepError = validateGrepApi(args.grep);
|
|
1584
1323
|
if (grepError) return {
|
|
1585
|
-
session: publicSessionApi(session),
|
|
1324
|
+
session: publicSessionApi(session, { includeTombstone: true }),
|
|
1586
1325
|
output: {
|
|
1587
1326
|
text: "",
|
|
1588
|
-
lines: [],
|
|
1589
1327
|
lineCount: session.lineCount,
|
|
1590
|
-
returned: 0,
|
|
1591
1328
|
truncated: false
|
|
1592
1329
|
},
|
|
1593
|
-
next: nextAdviceApi(false, `Invalid grep regex: ${grepError}`),
|
|
1594
1330
|
subscriberActive: false,
|
|
1595
1331
|
subscriberLastExitedAt: null,
|
|
1596
1332
|
subscriberErrors: [],
|
|
1597
|
-
warnings: [],
|
|
1333
|
+
warnings: [`Invalid grep regex: ${grepError}`],
|
|
1598
1334
|
cleanup: {
|
|
1599
1335
|
requested: false,
|
|
1600
1336
|
performed: false,
|
|
@@ -1622,9 +1358,8 @@ async function executeZellijPtyRead(args, dependencies = {}) {
|
|
|
1622
1358
|
});
|
|
1623
1359
|
if (cleanup.warning) warnings.push(cleanup.warning);
|
|
1624
1360
|
return {
|
|
1625
|
-
session: publicSessionApi(session),
|
|
1361
|
+
session: publicSessionApi(session, { includeTombstone: true }),
|
|
1626
1362
|
output,
|
|
1627
|
-
next: nextAdviceApi(!isCompletedSession(session.status), nextReadReason(session.status)),
|
|
1628
1363
|
subscriberActive: statusAfterStart.active,
|
|
1629
1364
|
subscriberLastExitedAt: statusAfterStart.lastExitedAt,
|
|
1630
1365
|
subscriberErrors: subscriberManagerApi.stderr(session.id),
|
|
@@ -1686,12 +1421,6 @@ createZellijPtyReadTool();
|
|
|
1686
1421
|
function isCompletedSession(status) {
|
|
1687
1422
|
return status === "terminal" || status === "exited" || status === "killed";
|
|
1688
1423
|
}
|
|
1689
|
-
function nextReadReason(status) {
|
|
1690
|
-
if (status === "terminal") return "Session has finished; the final output is retained until the completed pane is read and cleaned up.";
|
|
1691
|
-
if (status === "running") return "Session is still running; read again later if more output is expected.";
|
|
1692
|
-
if (status === "unknown") return "Session state is unknown because the subscriber is inactive; output may be stale, but retrying read may restart observation.";
|
|
1693
|
-
return "Session is no longer running.";
|
|
1694
|
-
}
|
|
1695
1424
|
//#endregion
|
|
1696
1425
|
//#region src/utils/pane-title.ts
|
|
1697
1426
|
const generatedInstanceId = randomUUID().replaceAll("-", "").slice(0, 8);
|
|
@@ -1768,10 +1497,8 @@ const requestSudoTool = tool({
|
|
|
1768
1497
|
registerPaneForWatchdog(session);
|
|
1769
1498
|
await subscriberManager.start(session);
|
|
1770
1499
|
return jsonResponse({
|
|
1771
|
-
session: publicSession(session),
|
|
1772
|
-
output: readOutputSnapshot(session.id)
|
|
1773
|
-
next: nextAdvice(false, "The user must review the summary and commands in Zellij, then type YES and any required credentials directly in the pane."),
|
|
1774
|
-
warnings: []
|
|
1500
|
+
session: publicSession(session, { agentWritable: false }),
|
|
1501
|
+
output: readOutputSnapshot(session.id)
|
|
1775
1502
|
});
|
|
1776
1503
|
}
|
|
1777
1504
|
});
|
|
@@ -1781,7 +1508,6 @@ const defaultSleepSeconds = 1;
|
|
|
1781
1508
|
const defaultProbeTimeoutSeconds = 20;
|
|
1782
1509
|
const pollIntervalMs = 250;
|
|
1783
1510
|
async function runProbe(probe, outputReader) {
|
|
1784
|
-
const startedAt = Date.now();
|
|
1785
1511
|
const effectiveProbe = probe ?? {
|
|
1786
1512
|
type: "sleep",
|
|
1787
1513
|
seconds: defaultSleepSeconds
|
|
@@ -1789,16 +1515,16 @@ async function runProbe(probe, outputReader) {
|
|
|
1789
1515
|
if (effectiveProbe.type === "sleep") {
|
|
1790
1516
|
const seconds = effectiveProbe.seconds ?? defaultSleepSeconds;
|
|
1791
1517
|
await setTimeout$1(seconds * 1e3);
|
|
1792
|
-
return result(effectiveProbe.type, true, `Slept for ${seconds}s
|
|
1518
|
+
return result(effectiveProbe.type, true, `Slept for ${seconds}s.`);
|
|
1793
1519
|
}
|
|
1794
1520
|
if (effectiveProbe.type === "output") {
|
|
1795
1521
|
const timeoutSeconds = effectiveProbe.timeoutSeconds ?? defaultProbeTimeoutSeconds;
|
|
1796
1522
|
const deadline = Date.now() + timeoutSeconds * 1e3;
|
|
1797
1523
|
while (Date.now() <= deadline) {
|
|
1798
|
-
if (outputReader(effectiveProbe.grep, effectiveProbe.ignoreCase)) return result(effectiveProbe.type, true, `Observed output matching /${effectiveProbe.grep}
|
|
1524
|
+
if (outputReader(effectiveProbe.grep, effectiveProbe.ignoreCase)) return result(effectiveProbe.type, true, `Observed output matching /${effectiveProbe.grep}/.`);
|
|
1799
1525
|
await setTimeout$1(pollIntervalMs);
|
|
1800
1526
|
}
|
|
1801
|
-
return result(effectiveProbe.type, false, `Timed out after ${timeoutSeconds}s waiting for output matching /${effectiveProbe.grep}
|
|
1527
|
+
return result(effectiveProbe.type, false, `Timed out after ${timeoutSeconds}s waiting for output matching /${effectiveProbe.grep}/.`);
|
|
1802
1528
|
}
|
|
1803
1529
|
const timeoutSeconds = effectiveProbe.timeoutSeconds ?? defaultProbeTimeoutSeconds;
|
|
1804
1530
|
const deadline = Date.now() + timeoutSeconds * 1e3;
|
|
@@ -1810,7 +1536,7 @@ async function runProbe(probe, outputReader) {
|
|
|
1810
1536
|
const response = await fetch(effectiveProbe.url, { signal: AbortSignal.timeout(Math.min(remainingMs, 3e3)) });
|
|
1811
1537
|
if (expectStatus === void 0 ? response.status >= 200 && response.status < 400 : response.status === expectStatus) {
|
|
1812
1538
|
const expected = expectStatus === void 0 ? "2xx/3xx" : String(expectStatus);
|
|
1813
|
-
return result(effectiveProbe.type, true, `HTTP probe ${effectiveProbe.url} returned expected status ${expected}
|
|
1539
|
+
return result(effectiveProbe.type, true, `HTTP probe ${effectiveProbe.url} returned expected status ${expected}.`);
|
|
1814
1540
|
}
|
|
1815
1541
|
lastError = `HTTP ${response.status}`;
|
|
1816
1542
|
} catch (error) {
|
|
@@ -1818,14 +1544,13 @@ async function runProbe(probe, outputReader) {
|
|
|
1818
1544
|
}
|
|
1819
1545
|
await setTimeout$1(pollIntervalMs);
|
|
1820
1546
|
}
|
|
1821
|
-
return result(effectiveProbe.type, false, `Timed out after ${timeoutSeconds}s probing ${effectiveProbe.url}: ${lastError}
|
|
1547
|
+
return result(effectiveProbe.type, false, `Timed out after ${timeoutSeconds}s probing ${effectiveProbe.url}: ${lastError}.`);
|
|
1822
1548
|
}
|
|
1823
|
-
function result(type, ok, message
|
|
1549
|
+
function result(type, ok, message) {
|
|
1824
1550
|
return {
|
|
1825
1551
|
type,
|
|
1826
1552
|
ok,
|
|
1827
|
-
message
|
|
1828
|
-
elapsedMs: Date.now() - startedAt
|
|
1553
|
+
message
|
|
1829
1554
|
};
|
|
1830
1555
|
}
|
|
1831
1556
|
//#endregion
|
|
@@ -1888,12 +1613,12 @@ const zellijPtySpawnTool = tool({
|
|
|
1888
1613
|
await subscriberManager.start(session);
|
|
1889
1614
|
const probe = await runProbe(args.probe, (grep, ignoreCase) => outputMatches(session.id, grep, ignoreCase));
|
|
1890
1615
|
const output = readOutputSnapshot(session.id, { maxLines: args.maxLines });
|
|
1616
|
+
const completedPanes = completedPanesFromSessions(sessionManager.listByOpenCodeSession(context.sessionID));
|
|
1891
1617
|
return jsonResponse({
|
|
1892
1618
|
session: publicSession(session),
|
|
1893
1619
|
output,
|
|
1894
1620
|
probe,
|
|
1895
|
-
|
|
1896
|
-
warnings: ["Registry remains in-memory; restarting OpenCode loses plugin session records."]
|
|
1621
|
+
...completedPanes
|
|
1897
1622
|
});
|
|
1898
1623
|
}
|
|
1899
1624
|
});
|
|
@@ -1942,8 +1667,7 @@ const zellijPtyWriteTool = tool({
|
|
|
1942
1667
|
const session = sessionManager.get(args.id);
|
|
1943
1668
|
if (session.humanInputOnly || !session.allowAgentInput) return jsonResponse({
|
|
1944
1669
|
session: publicSession(session),
|
|
1945
|
-
output: subscriberManager.has(session.id) ? readOutputSnapshot(session.id, { maxLines: args.maxLines }) : emptyOutputSnapshot(
|
|
1946
|
-
next: nextAdvice(false, "This session is human-input-only; the user must type directly in the Zellij pane."),
|
|
1670
|
+
output: subscriberManager.has(session.id) ? readOutputSnapshot(session.id, { maxLines: args.maxLines }) : emptyOutputSnapshot(),
|
|
1947
1671
|
warnings: ["Agent writes to human-input-only sessions are forbidden."]
|
|
1948
1672
|
});
|
|
1949
1673
|
if (args.data === "" || args.data === "") await zellijCli.sendCtrlC(session.paneId);
|
|
@@ -1969,296 +1693,155 @@ const zellijPtyWriteTool = tool({
|
|
|
1969
1693
|
return jsonResponse({
|
|
1970
1694
|
session: publicSession(session),
|
|
1971
1695
|
output,
|
|
1972
|
-
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."),
|
|
1973
1696
|
warnings
|
|
1974
1697
|
});
|
|
1975
1698
|
}
|
|
1976
1699
|
});
|
|
1977
1700
|
//#endregion
|
|
1978
|
-
//#region src/
|
|
1979
|
-
/**
|
|
1980
|
-
|
|
1701
|
+
//#region src/utils/runtime.ts
|
|
1702
|
+
/**
|
|
1703
|
+
* Detect whether the plugin process is running inside an OpenCode TUI
|
|
1704
|
+
* session, as opposed to a headless `opencode run` invocation.
|
|
1705
|
+
*
|
|
1706
|
+
* OpenCode spawns the TUI's renderer as a worker child process and
|
|
1707
|
+
* explicitly sets `OPENCODE_PROCESS_ROLE=worker`
|
|
1708
|
+
* (see `packages/opencode/src/cli/cmd/tui/thread.ts` in opencode). The
|
|
1709
|
+
* headless `opencode run` command keeps the default `main` role set by
|
|
1710
|
+
* the CLI entry point, so this is the most reliable signal to tell TUI
|
|
1711
|
+
* from headless.
|
|
1712
|
+
*
|
|
1713
|
+
* Outside the TUI there is no surface for toasts, prompts, or Zellij
|
|
1714
|
+
* panes, and the plugin's lifecycle hooks (watchdogs, tab title actor,
|
|
1715
|
+
* completion notifications) misbehave. The plugin short-circuits to a
|
|
1716
|
+
* no-op in headless mode to avoid leaking side effects.
|
|
1717
|
+
*/
|
|
1718
|
+
function isOpencodeTuiMode() {
|
|
1719
|
+
return process.env.OPENCODE_PROCESS_ROLE === "worker";
|
|
1720
|
+
}
|
|
1721
|
+
//#endregion
|
|
1722
|
+
//#region src/utils/logger.ts
|
|
1723
|
+
const defaultDebugLogPath = `${process.env.XDG_CACHE_HOME?.trim() || `${homedir()}/.cache`}/opencode-zellij/debug.log`;
|
|
1724
|
+
function buildRootLogger() {
|
|
1725
|
+
const filename = process.env.OPENCODE_ZELLIJ_DEBUG_LOG?.trim() || defaultDebugLogPath;
|
|
1726
|
+
if (!filename) return null;
|
|
1981
1727
|
try {
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
snapshot[sessionID] = parsed;
|
|
2002
|
-
}
|
|
2003
|
-
return snapshot;
|
|
2004
|
-
} catch (err) {
|
|
2005
|
-
debug("fetchPromptIdleStatusSnapshot failed", errorMessage(err));
|
|
2006
|
-
return;
|
|
1728
|
+
mkdirSync(dirname(filename), { recursive: true });
|
|
1729
|
+
} catch {
|
|
1730
|
+
return null;
|
|
1731
|
+
}
|
|
1732
|
+
try {
|
|
1733
|
+
return new LogLayer({
|
|
1734
|
+
errorSerializer: serializeError,
|
|
1735
|
+
transport: new LogFileRotationTransport({
|
|
1736
|
+
filename: filename.includes("%DATE%") ? filename : `${filename}-%DATE%`,
|
|
1737
|
+
dateFormat: "YMD",
|
|
1738
|
+
size: "1M",
|
|
1739
|
+
maxLogs: "7d",
|
|
1740
|
+
frequency: "daily",
|
|
1741
|
+
compressOnRotate: true,
|
|
1742
|
+
auditFile: join(dirname(filename), "audit.json")
|
|
1743
|
+
})
|
|
1744
|
+
});
|
|
1745
|
+
} catch {
|
|
1746
|
+
return null;
|
|
2007
1747
|
}
|
|
2008
1748
|
}
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
message: typeof status.message === "string" ? status.message : "",
|
|
2017
|
-
next: typeof status.next === "number" ? status.next : 0
|
|
2018
|
-
};
|
|
1749
|
+
const rootLogger = buildRootLogger();
|
|
1750
|
+
/**
|
|
1751
|
+
* Returns a child logger that prefixes every message with `[name]`, or
|
|
1752
|
+
* null if the root logger failed to initialize.
|
|
1753
|
+
*/
|
|
1754
|
+
function getChildLogger(name) {
|
|
1755
|
+
return rootLogger?.withPrefix(`[${name}]`) ?? null;
|
|
2019
1756
|
}
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
const
|
|
2023
|
-
function
|
|
2024
|
-
|
|
1757
|
+
//#endregion
|
|
1758
|
+
//#region src/zellij/completion-notifications.ts
|
|
1759
|
+
const logger = getChildLogger("completion-notifications");
|
|
1760
|
+
async function postPromptAsync(serverUrl, sessionID, body) {
|
|
1761
|
+
const url = new URL(`/session/${encodeURIComponent(sessionID)}/prompt_async`, serverUrl);
|
|
1762
|
+
const headers = { "Content-Type": "application/json" };
|
|
1763
|
+
const directory = process.env.OPENCODE_DIRECTORY?.trim();
|
|
1764
|
+
if (directory) headers["x-opencode-directory"] = encodeURIComponent(directory);
|
|
1765
|
+
return fetch(url, {
|
|
1766
|
+
method: "POST",
|
|
1767
|
+
headers,
|
|
1768
|
+
body: JSON.stringify(body)
|
|
1769
|
+
});
|
|
1770
|
+
}
|
|
1771
|
+
function formatExitCode(exitCode) {
|
|
1772
|
+
return exitCode === null ? "?" : String(exitCode);
|
|
2025
1773
|
}
|
|
2026
|
-
function
|
|
2027
|
-
|
|
1774
|
+
function buildCompletionPromptText(event) {
|
|
1775
|
+
const { paneId, exitCode } = event.session;
|
|
1776
|
+
return `[zellij_pty] pane ${paneId} exit=${formatExitCode(exitCode)} — call zellij_pty_read to read, then zellij_pty_kill to close.`;
|
|
2028
1777
|
}
|
|
2029
1778
|
function buildCompletionPromptRequest(event) {
|
|
2030
1779
|
return {
|
|
2031
|
-
|
|
2032
|
-
body: { parts: [{
|
|
2033
|
-
type: "text",
|
|
2034
|
-
text: completionMessage
|
|
2035
|
-
}] }
|
|
2036
|
-
};
|
|
2037
|
-
}
|
|
2038
|
-
function injectQueuedCompletionNotice(input, notice) {
|
|
2039
|
-
if (typeof input === "string") return `${notice}\n\n${input}`;
|
|
2040
|
-
if (!input || typeof input !== "object") return input;
|
|
2041
|
-
const record = input;
|
|
2042
|
-
if (Array.isArray(record.parts)) return {
|
|
2043
|
-
...record,
|
|
1780
|
+
sessionID: event.session.openCodeSessionId,
|
|
2044
1781
|
parts: [{
|
|
2045
1782
|
type: "text",
|
|
2046
|
-
text:
|
|
2047
|
-
}
|
|
2048
|
-
};
|
|
2049
|
-
if (typeof record.message === "string") return {
|
|
2050
|
-
...record,
|
|
2051
|
-
message: `${notice}\n\n${record.message}`
|
|
2052
|
-
};
|
|
2053
|
-
if (typeof record.content === "string") return {
|
|
2054
|
-
...record,
|
|
2055
|
-
content: `${notice}\n\n${record.content}`
|
|
2056
|
-
};
|
|
2057
|
-
if (typeof record.text === "string") return {
|
|
2058
|
-
...record,
|
|
2059
|
-
text: `${notice}\n\n${record.text}`
|
|
2060
|
-
};
|
|
2061
|
-
return {
|
|
2062
|
-
...record,
|
|
2063
|
-
message: notice
|
|
2064
|
-
};
|
|
2065
|
-
}
|
|
2066
|
-
function evaluateCompletionPromptDecision(input) {
|
|
2067
|
-
if (!supportsActivePrompt(input.config.mode)) return {
|
|
2068
|
-
shouldPrompt: false,
|
|
2069
|
-
shouldQueue: false,
|
|
2070
|
-
reason: "prompt mode disabled"
|
|
2071
|
-
};
|
|
2072
|
-
if (input.event.session.humanInputOnly || !input.event.session.allowAgentInput) return {
|
|
2073
|
-
shouldPrompt: false,
|
|
2074
|
-
shouldQueue: true,
|
|
2075
|
-
reason: "human-input-only session"
|
|
2076
|
-
};
|
|
2077
|
-
if (!input.promptClientAvailable) return {
|
|
2078
|
-
shouldPrompt: false,
|
|
2079
|
-
shouldQueue: true,
|
|
2080
|
-
reason: "prompt client unavailable"
|
|
2081
|
-
};
|
|
2082
|
-
if (!input.event.session.openCodeSessionId) return {
|
|
2083
|
-
shouldPrompt: false,
|
|
2084
|
-
shouldQueue: true,
|
|
2085
|
-
reason: "session id unavailable"
|
|
2086
|
-
};
|
|
2087
|
-
if (!input.snapshotAvailable) return {
|
|
2088
|
-
shouldPrompt: false,
|
|
2089
|
-
shouldQueue: true,
|
|
2090
|
-
reason: "session status snapshot unavailable"
|
|
2091
|
-
};
|
|
2092
|
-
if (input.config.prompt.maxAttempts <= 0 || input.promptAttemptCount >= input.config.prompt.maxAttempts) return {
|
|
2093
|
-
shouldPrompt: false,
|
|
2094
|
-
shouldQueue: true,
|
|
2095
|
-
reason: "prompt max attempts reached"
|
|
2096
|
-
};
|
|
2097
|
-
if (input.config.prompt.cooldownMs > 0 && input.lastPromptAttemptAt !== null && input.now - input.lastPromptAttemptAt < input.config.prompt.cooldownMs) return {
|
|
2098
|
-
shouldPrompt: false,
|
|
2099
|
-
shouldQueue: true,
|
|
2100
|
-
reason: "prompt cooldown active"
|
|
2101
|
-
};
|
|
2102
|
-
if (input.config.prompt.requireIdle) {
|
|
2103
|
-
const sessionId = input.event.session.openCodeSessionId;
|
|
2104
|
-
if (!sessionId) return {
|
|
2105
|
-
shouldPrompt: false,
|
|
2106
|
-
shouldQueue: true,
|
|
2107
|
-
reason: "session status unavailable"
|
|
2108
|
-
};
|
|
2109
|
-
const status = sessionId ? input.snapshot?.[sessionId] : void 0;
|
|
2110
|
-
if (!status) return {
|
|
2111
|
-
shouldPrompt: false,
|
|
2112
|
-
shouldQueue: true,
|
|
2113
|
-
reason: "session status unavailable"
|
|
2114
|
-
};
|
|
2115
|
-
if (status && status.type !== "idle") return {
|
|
2116
|
-
shouldPrompt: false,
|
|
2117
|
-
shouldQueue: true,
|
|
2118
|
-
reason: "session not idle"
|
|
2119
|
-
};
|
|
2120
|
-
}
|
|
2121
|
-
return {
|
|
2122
|
-
shouldPrompt: true,
|
|
2123
|
-
shouldQueue: false,
|
|
2124
|
-
reason: "prompt allowed"
|
|
1783
|
+
text: buildCompletionPromptText(event)
|
|
1784
|
+
}]
|
|
2125
1785
|
};
|
|
2126
1786
|
}
|
|
2127
|
-
var
|
|
2128
|
-
|
|
2129
|
-
constructor(context
|
|
1787
|
+
var SessionCompletionNotificationManager = class {
|
|
1788
|
+
seen = /* @__PURE__ */ new Set();
|
|
1789
|
+
constructor(context) {
|
|
2130
1790
|
this.context = context;
|
|
2131
|
-
this.hooks = hooks;
|
|
2132
|
-
this.clock = clock;
|
|
2133
|
-
}
|
|
2134
|
-
hasPending(sessionId) {
|
|
2135
|
-
return this.states.get(sessionId)?.queued ?? false;
|
|
2136
|
-
}
|
|
2137
|
-
clearSession(sessionId) {
|
|
2138
|
-
this.states.delete(sessionId);
|
|
2139
|
-
}
|
|
2140
|
-
clearAll() {
|
|
2141
|
-
this.states.clear();
|
|
2142
1791
|
}
|
|
2143
1792
|
dispose() {
|
|
2144
|
-
this.
|
|
1793
|
+
this.seen.clear();
|
|
2145
1794
|
}
|
|
2146
1795
|
async handleSessionTerminal(event) {
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
event,
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
this.states.set(event.sessionId, state);
|
|
2158
|
-
switch (this.context.config.mode) {
|
|
2159
|
-
case "queue":
|
|
2160
|
-
state.queued = true;
|
|
2161
|
-
return;
|
|
2162
|
-
case "toast":
|
|
2163
|
-
await this.sendToast(state);
|
|
2164
|
-
this.finalize(state);
|
|
2165
|
-
return;
|
|
2166
|
-
case "queue+toast":
|
|
2167
|
-
await this.tryPromptOrQueue(state);
|
|
2168
|
-
await this.sendToast(state);
|
|
2169
|
-
return;
|
|
2170
|
-
case "prompt":
|
|
2171
|
-
await this.tryPromptOrQueue(state);
|
|
2172
|
-
break;
|
|
2173
|
-
default:
|
|
2174
|
-
}
|
|
2175
|
-
}
|
|
2176
|
-
injectQueuedChatMessage(input) {
|
|
2177
|
-
const pending = [...this.states.values()].filter((state) => state.queued);
|
|
2178
|
-
if (pending.length === 0) return input;
|
|
2179
|
-
const notice = buildQueuedCompletionNotice(pending.map((state) => state.event));
|
|
2180
|
-
for (const state of pending) {
|
|
2181
|
-
this.markStateSent(state);
|
|
2182
|
-
this.finalize(state);
|
|
2183
|
-
}
|
|
2184
|
-
return injectQueuedCompletionNotice(input, notice);
|
|
2185
|
-
}
|
|
2186
|
-
async tryPromptOrQueue(state) {
|
|
2187
|
-
if (!state.event.session.openCodeSessionId) {
|
|
2188
|
-
state.queued = true;
|
|
1796
|
+
logger?.withMetadata({
|
|
1797
|
+
session: event.sessionId,
|
|
1798
|
+
reason: event.reason,
|
|
1799
|
+
paneId: event.session.paneId,
|
|
1800
|
+
openCodeSessionId: event.session.openCodeSessionId ?? "null"
|
|
1801
|
+
}).info("handleSessionTerminal");
|
|
1802
|
+
if (this.seen.has(event.sessionId)) return;
|
|
1803
|
+
this.seen.add(event.sessionId);
|
|
1804
|
+
if (!event.session.openCodeSessionId) {
|
|
1805
|
+
logger?.withMetadata({ session: event.sessionId }).info("skipped: no openCodeSessionId");
|
|
2189
1806
|
return;
|
|
2190
1807
|
}
|
|
2191
1808
|
const session = this.context.client.session;
|
|
2192
|
-
const
|
|
2193
|
-
const
|
|
2194
|
-
const
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
now: this.clock(),
|
|
2200
|
-
lastPromptAttemptAt: state.promptAttemptedAt,
|
|
2201
|
-
promptAttemptCount: state.promptAttempts,
|
|
2202
|
-
promptClientAvailable: Boolean(prompt)
|
|
2203
|
-
});
|
|
2204
|
-
if (!decision.shouldPrompt) {
|
|
2205
|
-
state.queued = decision.shouldQueue;
|
|
2206
|
-
return;
|
|
2207
|
-
}
|
|
2208
|
-
if (this.hooks.prompt) try {
|
|
2209
|
-
const maybePromise = this.hooks.prompt(state.event);
|
|
2210
|
-
if (maybePromise && typeof maybePromise.then === "function") await maybePromise;
|
|
2211
|
-
} catch (error) {
|
|
2212
|
-
debug("completion notification prompt hook failed", errorMessage(error));
|
|
2213
|
-
}
|
|
2214
|
-
if (!session || !prompt) {
|
|
2215
|
-
state.queued = true;
|
|
1809
|
+
const sdkPrompt = session?.promptAsync ?? session?.prompt;
|
|
1810
|
+
const request = buildCompletionPromptRequest(event);
|
|
1811
|
+
const sessionID = request.sessionID;
|
|
1812
|
+
if (sdkPrompt) try {
|
|
1813
|
+
logger?.withMetadata({ sessionID }).info("SDK prompt attempt");
|
|
1814
|
+
await sdkPrompt(request);
|
|
1815
|
+
logger?.withMetadata({ sessionID }).info("SDK prompt ok");
|
|
2216
1816
|
return;
|
|
2217
|
-
}
|
|
2218
|
-
state.promptAttempts += 1;
|
|
2219
|
-
state.promptAttemptedAt = this.clock();
|
|
2220
|
-
try {
|
|
2221
|
-
if (session.prompt) await session.prompt(buildCompletionPromptRequest(state.event));
|
|
2222
|
-
else if (session.promptAsync) await session.promptAsync(buildCompletionPromptRequest(state.event));
|
|
2223
|
-
else {
|
|
2224
|
-
state.queued = true;
|
|
2225
|
-
return;
|
|
2226
|
-
}
|
|
2227
|
-
this.markStateSent(state);
|
|
2228
|
-
this.finalize(state);
|
|
2229
1817
|
} catch (error) {
|
|
2230
|
-
|
|
2231
|
-
|
|
1818
|
+
logger?.withMetadata({
|
|
1819
|
+
sessionID,
|
|
1820
|
+
error: errorMessage(error)
|
|
1821
|
+
}).warn("SDK prompt failed, falling back to HTTP");
|
|
2232
1822
|
}
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
1823
|
+
else logger?.withMetadata({
|
|
1824
|
+
sessionID,
|
|
1825
|
+
sessionKeys: session ? Object.keys(session).join(",") : "null"
|
|
1826
|
+
}).info("no SDK prompt on client, using HTTP fallback");
|
|
1827
|
+
if (!this.context.serverUrl) {
|
|
1828
|
+
logger?.withMetadata({ sessionID }).error("no serverUrl for HTTP fallback");
|
|
2238
1829
|
return;
|
|
2239
1830
|
}
|
|
2240
1831
|
try {
|
|
2241
|
-
await
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
}
|
|
2247
|
-
state.toastSent = true;
|
|
2248
|
-
this.markStateSent(state);
|
|
2249
|
-
if (!state.queued) this.finalize(state);
|
|
1832
|
+
const response = await postPromptAsync(this.context.serverUrl, sessionID, { parts: request.parts });
|
|
1833
|
+
logger?.withMetadata({
|
|
1834
|
+
sessionID,
|
|
1835
|
+
status: response.status,
|
|
1836
|
+
ok: response.ok
|
|
1837
|
+
}).info("HTTP fallback response");
|
|
2250
1838
|
} catch (error) {
|
|
2251
|
-
|
|
1839
|
+
logger?.withMetadata({
|
|
1840
|
+
sessionID,
|
|
1841
|
+
error: errorMessage(error)
|
|
1842
|
+
}).error("HTTP fallback threw");
|
|
2252
1843
|
}
|
|
2253
1844
|
}
|
|
2254
|
-
markStateSent(state) {
|
|
2255
|
-
if (state.sent) return;
|
|
2256
|
-
this.context.markSent(state.event.sessionId);
|
|
2257
|
-
state.sent = true;
|
|
2258
|
-
}
|
|
2259
|
-
finalize(state) {
|
|
2260
|
-
this.states.delete(state.event.sessionId);
|
|
2261
|
-
}
|
|
2262
1845
|
};
|
|
2263
1846
|
//#endregion
|
|
2264
1847
|
//#region src/zellij/shutdown-cleanup.ts
|
|
@@ -2600,9 +2183,6 @@ var TabTitleManager = class {
|
|
|
2600
2183
|
emojis;
|
|
2601
2184
|
enabled;
|
|
2602
2185
|
destroyed = false;
|
|
2603
|
-
originalTabTitle;
|
|
2604
|
-
originalTabTitleLoaded = false;
|
|
2605
|
-
originalTabTitlePromise;
|
|
2606
2186
|
destroyPromise;
|
|
2607
2187
|
actor;
|
|
2608
2188
|
constructor(options) {
|
|
@@ -2628,8 +2208,6 @@ var TabTitleManager = class {
|
|
|
2628
2208
|
}
|
|
2629
2209
|
async renderImmediate() {
|
|
2630
2210
|
if (!this.enabled || this.destroyed) return;
|
|
2631
|
-
await this.ensureOriginalTabTitle();
|
|
2632
|
-
if (this.destroyed) return;
|
|
2633
2211
|
this.desiredTitle = this.buildTitle();
|
|
2634
2212
|
this.clearDebounceTimer();
|
|
2635
2213
|
await this.syncDesiredTitle();
|
|
@@ -2651,7 +2229,6 @@ var TabTitleManager = class {
|
|
|
2651
2229
|
async syncDesiredTitle() {
|
|
2652
2230
|
if (!this.enabled || this.destroyed) return;
|
|
2653
2231
|
const generation = this.syncGeneration;
|
|
2654
|
-
await this.ensureOriginalTabTitle();
|
|
2655
2232
|
if (this.destroyed || generation !== this.syncGeneration) return;
|
|
2656
2233
|
if (this.syncInFlight) return this.syncPromise;
|
|
2657
2234
|
this.syncInFlight = true;
|
|
@@ -2701,23 +2278,6 @@ var TabTitleManager = class {
|
|
|
2701
2278
|
if (this.debounceTimer) clearTimeout(this.debounceTimer);
|
|
2702
2279
|
this.debounceTimer = void 0;
|
|
2703
2280
|
}
|
|
2704
|
-
async ensureOriginalTabTitle() {
|
|
2705
|
-
if (!this.enabled || this.originalTabTitleLoaded) return;
|
|
2706
|
-
if (this.originalTabTitlePromise) return this.originalTabTitlePromise;
|
|
2707
|
-
this.originalTabTitlePromise = this.saveOriginalTabTitle();
|
|
2708
|
-
return this.originalTabTitlePromise;
|
|
2709
|
-
}
|
|
2710
|
-
async saveOriginalTabTitle() {
|
|
2711
|
-
try {
|
|
2712
|
-
const title = await this.cli.currentTabTitle();
|
|
2713
|
-
if (title !== void 0) this.originalTabTitle = title;
|
|
2714
|
-
} catch (error) {
|
|
2715
|
-
debug("TabTitleManager failed to save original tab title", errorMessage(error));
|
|
2716
|
-
} finally {
|
|
2717
|
-
this.originalTabTitleLoaded = true;
|
|
2718
|
-
this.originalTabTitlePromise = void 0;
|
|
2719
|
-
}
|
|
2720
|
-
}
|
|
2721
2281
|
destroy() {
|
|
2722
2282
|
if (this.destroyed) return this.destroyPromise ?? Promise.resolve();
|
|
2723
2283
|
this.destroyed = true;
|
|
@@ -2725,21 +2285,12 @@ var TabTitleManager = class {
|
|
|
2725
2285
|
this.desiredTitle = void 0;
|
|
2726
2286
|
this.clearDebounceTimer();
|
|
2727
2287
|
this.clearRetryTimer();
|
|
2728
|
-
|
|
2729
|
-
this.destroyPromise = this.restoreOriginalTabTitle().catch((error) => debug("TabTitleManager failed to restore original tab title", errorMessage(error)));
|
|
2730
|
-
return this.destroyPromise;
|
|
2731
|
-
}
|
|
2732
|
-
async restoreOriginalTabTitle() {
|
|
2733
|
-
await this.originalTabTitlePromise;
|
|
2734
|
-
await this.syncPromise;
|
|
2735
|
-
const originalTitle = this.originalTabTitle;
|
|
2736
|
-
this.originalTabTitle = void 0;
|
|
2737
|
-
if (originalTitle === void 0) return;
|
|
2738
|
-
await this.cli.renameTab(originalTitle);
|
|
2288
|
+
return Promise.resolve();
|
|
2739
2289
|
}
|
|
2740
2290
|
};
|
|
2741
2291
|
//#endregion
|
|
2742
2292
|
//#region src/plugin.ts
|
|
2293
|
+
const PLUGIN_ID = "opencode-zellij";
|
|
2743
2294
|
function createPtyTools(defaultCleanupExitedPaneOnRead) {
|
|
2744
2295
|
return {
|
|
2745
2296
|
zellij_pty_spawn: zellijPtySpawnTool,
|
|
@@ -2755,29 +2306,6 @@ function getProjectName(path) {
|
|
|
2755
2306
|
function getWorkspaceRoot(input) {
|
|
2756
2307
|
return input.worktree || input.directory || process.cwd();
|
|
2757
2308
|
}
|
|
2758
|
-
function showUpdateToast(client, result) {
|
|
2759
|
-
if (result.type === "updated") client.tui.showToast({ body: {
|
|
2760
|
-
title: "opencode-zellij updated",
|
|
2761
|
-
message: `Updated to ${result.toVersion}. Restart OpenCode to apply the changes.`,
|
|
2762
|
-
variant: "success",
|
|
2763
|
-
duration: 1e4
|
|
2764
|
-
} }).catch((error) => debug("show update toast for successful update failed", errorMessage(error)));
|
|
2765
|
-
else if (result.type === "failed") client.tui.showToast({ body: {
|
|
2766
|
-
title: "opencode-zellij update failed",
|
|
2767
|
-
message: `Failed to update to ${result.latestVersion}.`,
|
|
2768
|
-
variant: "error",
|
|
2769
|
-
duration: 8e3
|
|
2770
|
-
} }).catch((error) => debug("show update toast for failed update failed", errorMessage(error)));
|
|
2771
|
-
}
|
|
2772
|
-
function startAutoUpdateCheck(client, importMetaUrl, check = checkAndUpdate) {
|
|
2773
|
-
(async () => {
|
|
2774
|
-
try {
|
|
2775
|
-
showUpdateToast(client, await check({ importMetaUrl }));
|
|
2776
|
-
} catch (cause) {
|
|
2777
|
-
debug("auto-update check failed", errorMessage(cause));
|
|
2778
|
-
}
|
|
2779
|
-
})();
|
|
2780
|
-
}
|
|
2781
2309
|
async function cleanupStep(stepName, sessionId, step) {
|
|
2782
2310
|
try {
|
|
2783
2311
|
await step();
|
|
@@ -2793,6 +2321,10 @@ async function cleanupDeletedSession(sessionId) {
|
|
|
2793
2321
|
}
|
|
2794
2322
|
function createZellijPtyPlugin(dependencies = {}) {
|
|
2795
2323
|
return async (input) => {
|
|
2324
|
+
if (!isOpencodeTuiMode()) {
|
|
2325
|
+
debug("opencode-zellij disabled: not running inside an OpenCode TUI session");
|
|
2326
|
+
return {};
|
|
2327
|
+
}
|
|
2796
2328
|
const { config, warnings } = await loadConfig(input);
|
|
2797
2329
|
for (const warning of warnings) debug(warning);
|
|
2798
2330
|
configureSudoPane(config.pty.sudoPane === "allow");
|
|
@@ -2820,60 +2352,35 @@ function createZellijPtyPlugin(dependencies = {}) {
|
|
|
2820
2352
|
branch: config.tabTitle.emojiBranch
|
|
2821
2353
|
}
|
|
2822
2354
|
}) : void 0;
|
|
2823
|
-
const
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
try {
|
|
2830
|
-
sessionManager.markTerminalNotificationSent(sessionId);
|
|
2831
|
-
} catch (error) {
|
|
2832
|
-
debug("mark terminal notification sent failed", errorMessage(error));
|
|
2833
|
-
}
|
|
2834
|
-
}
|
|
2835
|
-
}) ?? new SessionCompletionNotificationQueue({
|
|
2836
|
-
client,
|
|
2837
|
-
workspaceRoot,
|
|
2838
|
-
config: config.pty.completionNotification,
|
|
2839
|
-
markSent(sessionId) {
|
|
2840
|
-
try {
|
|
2841
|
-
sessionManager.markTerminalNotificationSent(sessionId);
|
|
2842
|
-
} catch (error) {
|
|
2843
|
-
debug("mark terminal notification sent failed", errorMessage(error));
|
|
2844
|
-
}
|
|
2845
|
-
}
|
|
2355
|
+
const completionNotifications = dependencies.createCompletionNotifications?.({
|
|
2356
|
+
client: { session: input.client?.session },
|
|
2357
|
+
serverUrl: input.serverUrl
|
|
2358
|
+
}) ?? new SessionCompletionNotificationManager({
|
|
2359
|
+
client: { session: input.client?.session },
|
|
2360
|
+
serverUrl: input.serverUrl
|
|
2846
2361
|
});
|
|
2847
|
-
subscriberManager.setLifecycleHooks(
|
|
2362
|
+
subscriberManager.setLifecycleHooks({ onSessionTerminal: (event) => void completionNotifications.handleSessionTerminal(event).catch((error) => debug("completion notification lifecycle hook failed", errorMessage(error))) });
|
|
2848
2363
|
if (actor) await actor.ready;
|
|
2849
2364
|
tabTitleManager?.renderImmediate().catch((error) => debug("initial tab title render failed", errorMessage(error)));
|
|
2850
|
-
if (config.autoUpdate) (dependencies.startAutoUpdateCheck ?? startAutoUpdateCheck)(client, dependencies.importMetaUrl ?? import.meta.url);
|
|
2851
2365
|
return {
|
|
2852
2366
|
async event(input) {
|
|
2853
2367
|
const event = input.event;
|
|
2854
2368
|
if (actor && tabTitleManager) {
|
|
2855
2369
|
await actor.handleEvent(event);
|
|
2856
|
-
|
|
2857
|
-
else tabTitleManager.scheduleUpdate();
|
|
2370
|
+
tabTitleManager.scheduleUpdate();
|
|
2858
2371
|
}
|
|
2859
2372
|
if (event.type === "server.instance.disposed" || event.type === "global.disposed") {
|
|
2860
|
-
completionNotifications
|
|
2861
|
-
completionNotifications?.dispose();
|
|
2373
|
+
completionNotifications.dispose();
|
|
2862
2374
|
subscriberManager.setLifecycleHooks(void 0);
|
|
2863
2375
|
}
|
|
2864
2376
|
if (event.type === "session.deleted") {
|
|
2865
2377
|
const sessionID = deletedSessionID$1(event);
|
|
2866
2378
|
if (!sessionID) return;
|
|
2867
2379
|
const sessions = sessionManager.listByOpenCodeSession(sessionID);
|
|
2868
|
-
for (const session of sessions) completionNotifications?.clearSession(session.id);
|
|
2869
2380
|
await Promise.all(sessions.map((session) => cleanupDeletedSession(session.id)));
|
|
2870
2381
|
}
|
|
2871
2382
|
},
|
|
2872
|
-
|
|
2873
|
-
const injected = completionNotifications?.injectQueuedChatMessage(output) ?? output;
|
|
2874
|
-
if (injected !== output && injected && typeof injected === "object" && Array.isArray(injected.parts)) output.parts = injected.parts;
|
|
2875
|
-
},
|
|
2876
|
-
"tool": config.pty.enabled ? {
|
|
2383
|
+
tool: config.pty.enabled ? {
|
|
2877
2384
|
...createPtyTools(config.pty.cleanupExitedPaneOnRead),
|
|
2878
2385
|
...config.pty.sudoPane === "hide" ? {} : { zellij_pty_request_sudo: requestSudoTool }
|
|
2879
2386
|
} : {}
|
|
@@ -2881,7 +2388,11 @@ function createZellijPtyPlugin(dependencies = {}) {
|
|
|
2881
2388
|
};
|
|
2882
2389
|
}
|
|
2883
2390
|
const ZellijPtyPlugin = createZellijPtyPlugin();
|
|
2391
|
+
var plugin_default = {
|
|
2392
|
+
id: PLUGIN_ID,
|
|
2393
|
+
server: ZellijPtyPlugin
|
|
2394
|
+
};
|
|
2884
2395
|
//#endregion
|
|
2885
|
-
export { ZellijPtyPlugin,
|
|
2396
|
+
export { ZellijPtyPlugin, createZellijPtyPlugin, plugin_default as default };
|
|
2886
2397
|
|
|
2887
2398
|
//# sourceMappingURL=index.mjs.map
|