botmux 2.52.0 → 2.54.0
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.en.md +22 -269
- package/README.md +21 -300
- package/dist/adapters/backend/session-backend-selector.d.ts +5 -1
- package/dist/adapters/backend/session-backend-selector.d.ts.map +1 -1
- package/dist/adapters/backend/session-backend-selector.js +15 -1
- package/dist/adapters/backend/session-backend-selector.js.map +1 -1
- package/dist/adapters/backend/tmux-backend.d.ts.map +1 -1
- package/dist/adapters/backend/tmux-backend.js +3 -0
- package/dist/adapters/backend/tmux-backend.js.map +1 -1
- package/dist/adapters/backend/types.d.ts +22 -0
- package/dist/adapters/backend/types.d.ts.map +1 -1
- package/dist/adapters/backend/types.js +7 -1
- package/dist/adapters/backend/types.js.map +1 -1
- package/dist/adapters/backend/zellij-backend.d.ts +132 -0
- package/dist/adapters/backend/zellij-backend.d.ts.map +1 -0
- package/dist/adapters/backend/zellij-backend.js +375 -0
- package/dist/adapters/backend/zellij-backend.js.map +1 -0
- package/dist/adapters/backend/zellij-observe-backend.d.ts +62 -0
- package/dist/adapters/backend/zellij-observe-backend.d.ts.map +1 -0
- package/dist/adapters/backend/zellij-observe-backend.js +218 -0
- package/dist/adapters/backend/zellij-observe-backend.js.map +1 -0
- package/dist/adapters/cli/claude-code.d.ts +39 -5
- package/dist/adapters/cli/claude-code.d.ts.map +1 -1
- package/dist/adapters/cli/claude-code.js +53 -31
- package/dist/adapters/cli/claude-code.js.map +1 -1
- package/dist/adapters/cli/registry.d.ts +2 -1
- package/dist/adapters/cli/registry.d.ts.map +1 -1
- package/dist/adapters/cli/registry.js +3 -1
- package/dist/adapters/cli/registry.js.map +1 -1
- package/dist/adapters/cli/seed.d.ts +29 -0
- package/dist/adapters/cli/seed.d.ts.map +1 -0
- package/dist/adapters/cli/seed.js +63 -0
- package/dist/adapters/cli/seed.js.map +1 -0
- package/dist/adapters/cli/types.d.ts +17 -1
- package/dist/adapters/cli/types.d.ts.map +1 -1
- package/dist/bot-registry.d.ts +1 -1
- package/dist/bot-registry.d.ts.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +79 -49
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +7 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +8 -0
- package/dist/config.js.map +1 -1
- package/dist/core/ask-hook/registry.d.ts.map +1 -1
- package/dist/core/ask-hook/registry.js +4 -0
- package/dist/core/ask-hook/registry.js.map +1 -1
- package/dist/core/command-handler.d.ts +4 -1
- package/dist/core/command-handler.d.ts.map +1 -1
- package/dist/core/command-handler.js +100 -8
- package/dist/core/command-handler.js.map +1 -1
- package/dist/core/dispatch.d.ts +33 -0
- package/dist/core/dispatch.d.ts.map +1 -1
- package/dist/core/dispatch.js +26 -0
- package/dist/core/dispatch.js.map +1 -1
- package/dist/core/pending-response.d.ts +31 -0
- package/dist/core/pending-response.d.ts.map +1 -0
- package/dist/core/pending-response.js +87 -0
- package/dist/core/pending-response.js.map +1 -0
- package/dist/core/session-discovery.d.ts +13 -4
- package/dist/core/session-discovery.d.ts.map +1 -1
- package/dist/core/session-discovery.js +5 -5
- package/dist/core/session-discovery.js.map +1 -1
- package/dist/core/session-manager.d.ts +10 -0
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +59 -19
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/types.d.ts +8 -2
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/core/worker-pool.d.ts +2 -2
- package/dist/core/worker-pool.d.ts.map +1 -1
- package/dist/core/worker-pool.js +89 -15
- package/dist/core/worker-pool.js.map +1 -1
- package/dist/core/zellij-adopt-discovery.d.ts +28 -0
- package/dist/core/zellij-adopt-discovery.d.ts.map +1 -0
- package/dist/core/zellij-adopt-discovery.js +276 -0
- package/dist/core/zellij-adopt-discovery.js.map +1 -0
- package/dist/core/zellij-session-discovery.d.ts +73 -0
- package/dist/core/zellij-session-discovery.d.ts.map +1 -0
- package/dist/core/zellij-session-discovery.js +259 -0
- package/dist/core/zellij-session-discovery.js.map +1 -0
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +70 -4
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard/web/i18n.js +1 -1
- package/dist/dashboard/web/i18n.js.map +1 -1
- package/dist/dashboard/web/sessions.d.ts.map +1 -1
- package/dist/dashboard/web/sessions.js +1 -0
- package/dist/dashboard/web/sessions.js.map +1 -1
- package/dist/dashboard/web/workflows.js +1 -1
- package/dist/dashboard/web/workflows.js.map +1 -1
- package/dist/dashboard-web/app.js +3 -3
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +12 -0
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/zh.d.ts.map +1 -1
- package/dist/i18n/zh.js +13 -1
- package/dist/i18n/zh.js.map +1 -1
- package/dist/im/lark/card-builder.d.ts +7 -1
- package/dist/im/lark/card-builder.d.ts.map +1 -1
- package/dist/im/lark/card-builder.js +92 -2
- package/dist/im/lark/card-builder.js.map +1 -1
- package/dist/im/lark/card-handler.d.ts.map +1 -1
- package/dist/im/lark/card-handler.js +78 -6
- package/dist/im/lark/card-handler.js.map +1 -1
- package/dist/im/lark/event-dispatcher.d.ts +8 -1
- package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
- package/dist/im/lark/event-dispatcher.js +28 -0
- package/dist/im/lark/event-dispatcher.js.map +1 -1
- package/dist/im/lark/grant-command.d.ts +13 -0
- package/dist/im/lark/grant-command.d.ts.map +1 -1
- package/dist/im/lark/grant-command.js +93 -0
- package/dist/im/lark/grant-command.js.map +1 -1
- package/dist/services/codex-app-threads.d.ts +20 -0
- package/dist/services/codex-app-threads.d.ts.map +1 -0
- package/dist/services/codex-app-threads.js +165 -0
- package/dist/services/codex-app-threads.js.map +1 -0
- package/dist/services/pending-response-transaction-store.d.ts +12 -0
- package/dist/services/pending-response-transaction-store.d.ts.map +1 -0
- package/dist/services/pending-response-transaction-store.js +52 -0
- package/dist/services/pending-response-transaction-store.js.map +1 -0
- package/dist/services/session-store.d.ts.map +1 -1
- package/dist/services/session-store.js +15 -1
- package/dist/services/session-store.js.map +1 -1
- package/dist/setup/bot-config-editor.d.ts +1 -1
- package/dist/setup/bot-config-editor.d.ts.map +1 -1
- package/dist/setup/bot-config-editor.js +5 -4
- package/dist/setup/bot-config-editor.js.map +1 -1
- package/dist/setup/ensure-zellij.d.ts +48 -0
- package/dist/setup/ensure-zellij.d.ts.map +1 -0
- package/dist/setup/ensure-zellij.js +93 -0
- package/dist/setup/ensure-zellij.js.map +1 -0
- package/dist/types.d.ts +14 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/anchor-serializer.d.ts +3 -2
- package/dist/utils/anchor-serializer.d.ts.map +1 -1
- package/dist/utils/anchor-serializer.js +20 -5
- package/dist/utils/anchor-serializer.js.map +1 -1
- package/dist/utils/transient-snapshot.js +2 -2
- package/dist/utils/transient-snapshot.js.map +1 -1
- package/dist/worker.js +235 -32
- package/dist/worker.js.map +1 -1
- package/dist/workflows/attempt-resume.d.ts +1 -1
- package/dist/workflows/attempt-resume.d.ts.map +1 -1
- package/dist/workflows/attempt-resume.js +1 -1
- package/dist/workflows/attempt-resume.js.map +1 -1
- package/dist/workflows/events/payloads.d.ts +20 -20
- package/dist/workflows/events/schema.d.ts +48 -48
- package/package.json +1 -1
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
export interface LayoutPane {
|
|
2
|
+
/** Foreground command (argv0) zellij introspected for this pane, e.g. "claude". */
|
|
3
|
+
command: string;
|
|
4
|
+
/** Explicit pane name if set (zellij `name=`), else undefined. */
|
|
5
|
+
name?: string;
|
|
6
|
+
/** Absolute cwd of the pane (layout base cwd joined with the pane's relative cwd). */
|
|
7
|
+
cwd?: string;
|
|
8
|
+
/** Command args, when present in the dump. */
|
|
9
|
+
args: string[];
|
|
10
|
+
}
|
|
11
|
+
export interface ListedPane {
|
|
12
|
+
/** "terminal_<n>" — the id used to target zellij `action` commands. */
|
|
13
|
+
paneId: string;
|
|
14
|
+
isPlugin: boolean;
|
|
15
|
+
isFloating: boolean;
|
|
16
|
+
title?: string;
|
|
17
|
+
terminalCommand?: string | null;
|
|
18
|
+
}
|
|
19
|
+
export interface DiscoveredCli {
|
|
20
|
+
session: string;
|
|
21
|
+
paneId: string;
|
|
22
|
+
command: string;
|
|
23
|
+
cwd?: string;
|
|
24
|
+
args: string[];
|
|
25
|
+
title?: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Parse `zellij action dump-layout` output, returning only the panes that have
|
|
29
|
+
* a foreground `command=` (i.e. real terminal panes running something) in
|
|
30
|
+
* document order. Template sections (new_tab_template / swap_*_layout) are cut
|
|
31
|
+
* off first — their bare `pane` nodes have no command and would otherwise add
|
|
32
|
+
* noise. Plugin panes (tab-bar / status-bar / about) have no command= and are
|
|
33
|
+
* naturally excluded.
|
|
34
|
+
*/
|
|
35
|
+
export declare function parseDumpLayoutPanes(kdl: string): LayoutPane[];
|
|
36
|
+
export interface LeafPane {
|
|
37
|
+
/** Foreground command (argv0) if the pane is running one; undefined for an
|
|
38
|
+
* idle shell pane (zellij emits a bare `pane` with no command=). */
|
|
39
|
+
command?: string;
|
|
40
|
+
name?: string;
|
|
41
|
+
cwd?: string;
|
|
42
|
+
args: string[];
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Parse `zellij action dump-layout` into the ordered list of LEAF terminal
|
|
46
|
+
* panes — both command-bearing panes AND idle bare shell panes — skipping
|
|
47
|
+
* plugin panes (tab-bar/status-bar/about), container panes (splits), and the
|
|
48
|
+
* floating subtree. Preserving bare panes is what makes a positional join to
|
|
49
|
+
* `list-panes` correct: a bare shell pane that sorts before the CLI pane would
|
|
50
|
+
* otherwise shift every command pane onto the wrong pane id (the bug Codex
|
|
51
|
+
* found). Templates (new_tab_template / swap_*) are cut off first.
|
|
52
|
+
*
|
|
53
|
+
* zellij always pretty-prints dump-layout one node per line, so a brace-stack
|
|
54
|
+
* line walk is reliable here.
|
|
55
|
+
*/
|
|
56
|
+
export declare function parseDumpLayoutLeafPanes(kdl: string): LeafPane[];
|
|
57
|
+
/** Parse `zellij action list-panes --json` into a flat list (document order). */
|
|
58
|
+
export declare function parseListPanesJson(json: string): ListedPane[];
|
|
59
|
+
/**
|
|
60
|
+
* Join dump-layout command panes with list-panes terminal panes by document
|
|
61
|
+
* order: the i-th command pane ↔ the i-th non-plugin terminal pane (sorted by
|
|
62
|
+
* id). zellij assigns pane ids in creation order and walks the tree in a stable
|
|
63
|
+
* order, so this aligns for normal layouts. Returns one DiscoveredCli per
|
|
64
|
+
* command pane that could be bound to an id.
|
|
65
|
+
*/
|
|
66
|
+
export declare function joinPanes(session: string, layoutPanes: LayoutPane[], listed: ListedPane[]): DiscoveredCli[];
|
|
67
|
+
/** Names of live (non-exited) zellij sessions. */
|
|
68
|
+
export declare function listLiveSessions(): string[];
|
|
69
|
+
/** Discover CLIs running in one session. */
|
|
70
|
+
export declare function discoverSessionClis(session: string): DiscoveredCli[];
|
|
71
|
+
/** Discover CLIs across every live zellij session. */
|
|
72
|
+
export declare function discoverAllClis(): DiscoveredCli[];
|
|
73
|
+
//# sourceMappingURL=zellij-session-discovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zellij-session-discovery.d.ts","sourceRoot":"","sources":["../../src/core/zellij-session-discovery.ts"],"names":[],"mappings":"AA4BA,MAAM,WAAW,UAAU;IACzB,mFAAmF;IACnF,OAAO,EAAE,MAAM,CAAC;IAChB,kEAAkE;IAClE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sFAAsF;IACtF,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,8CAA8C;IAC9C,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED,MAAM,WAAW,UAAU;IACzB,uEAAuE;IACvE,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,OAAO,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAID;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,EAAE,CA6C9D;AAED,MAAM,WAAW,QAAQ;IACvB;yEACqE;IACrE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,EAAE,CAuDhE;AAED,iFAAiF;AACjF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,EAAE,CAW7D;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,aAAa,EAAE,CAmB3G;AAID,kDAAkD;AAClD,wBAAgB,gBAAgB,IAAI,MAAM,EAAE,CAS3C;AAUD,4CAA4C;AAC5C,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,EAAE,CAKpE;AAED,sDAAsD;AACtD,wBAAgB,eAAe,IAAI,aAAa,EAAE,CAEjD"}
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zellij session discovery for /adopt — find CLIs already running inside a
|
|
3
|
+
* user's zellij sessions and the pane needed to drive them.
|
|
4
|
+
*
|
|
5
|
+
* Why this exists (and why it's not just `list-panes`): zellij's
|
|
6
|
+
* `list-panes --json` exposes pane ids + geometry but NOT the running command,
|
|
7
|
+
* cwd, or pid, and its `terminal_command` is null for anything the user started
|
|
8
|
+
* interactively (typed `claude` into a shell — the common case). The data we
|
|
9
|
+
* need is instead surfaced by zellij's **session-resurrection** machinery: to be
|
|
10
|
+
* able to restore a session after a reboot, zellij continuously introspects each
|
|
11
|
+
* pane's *foreground process command + cwd* and exposes it via
|
|
12
|
+
* `zellij action dump-layout`. That's where we read "what CLI is in this pane".
|
|
13
|
+
*
|
|
14
|
+
* Discovery pipeline:
|
|
15
|
+
* 1. dump-layout → per-pane { command, args, cwd } (detection)
|
|
16
|
+
* 2. list-panes --json → per-pane { id: terminal_<n> } (drive target)
|
|
17
|
+
* 3. order/geometry join → bind command ↔ pane id
|
|
18
|
+
* 4. (caller) /proc descent under the pane shell → pid → ~/.claude/sessions/<pid>.json
|
|
19
|
+
*
|
|
20
|
+
* Parsers here are pure (string in, struct out) so they unit-test without a
|
|
21
|
+
* live zellij. The order-join (step 3) is robust for normal single/few-pane
|
|
22
|
+
* layouts; exotic multi-tab/floating arrangements may need the geometry/proc
|
|
23
|
+
* cross-check the caller layers on top.
|
|
24
|
+
*/
|
|
25
|
+
import { execFileSync } from 'node:child_process';
|
|
26
|
+
import { isAbsolute, join as pathJoin } from 'node:path';
|
|
27
|
+
import { zellijEnv } from '../setup/ensure-zellij.js';
|
|
28
|
+
const TEMPLATE_MARKERS = /\b(new_tab_template|swap_tiled_layout|swap_floating_layout)\b/;
|
|
29
|
+
/**
|
|
30
|
+
* Parse `zellij action dump-layout` output, returning only the panes that have
|
|
31
|
+
* a foreground `command=` (i.e. real terminal panes running something) in
|
|
32
|
+
* document order. Template sections (new_tab_template / swap_*_layout) are cut
|
|
33
|
+
* off first — their bare `pane` nodes have no command and would otherwise add
|
|
34
|
+
* noise. Plugin panes (tab-bar / status-bar / about) have no command= and are
|
|
35
|
+
* naturally excluded.
|
|
36
|
+
*/
|
|
37
|
+
export function parseDumpLayoutPanes(kdl) {
|
|
38
|
+
// Drop the template tail so we only see live tab content.
|
|
39
|
+
const tmplIdx = kdl.search(TEMPLATE_MARKERS);
|
|
40
|
+
const body = tmplIdx >= 0 ? kdl.slice(0, tmplIdx) : kdl;
|
|
41
|
+
const lines = body.split('\n');
|
|
42
|
+
const panes = [];
|
|
43
|
+
// Layout base cwd is a NODE: `cwd "..."` (space). Pane cwd is an ATTRIBUTE:
|
|
44
|
+
// `cwd="..."` (equals). The first node-form cwd is the layout base.
|
|
45
|
+
let layoutCwd;
|
|
46
|
+
const baseCwdMatch = body.match(/^\s*cwd\s+"([^"]*)"/m);
|
|
47
|
+
if (baseCwdMatch)
|
|
48
|
+
layoutCwd = baseCwdMatch[1];
|
|
49
|
+
let pending = null;
|
|
50
|
+
const flush = () => { if (pending) {
|
|
51
|
+
panes.push(pending);
|
|
52
|
+
pending = null;
|
|
53
|
+
} };
|
|
54
|
+
for (const line of lines) {
|
|
55
|
+
const trimmed = line.trim();
|
|
56
|
+
// A pane that runs a command (attributes can appear in any order).
|
|
57
|
+
if (/^pane\b/.test(trimmed) && /\bcommand=/.test(trimmed)) {
|
|
58
|
+
flush();
|
|
59
|
+
const command = attr(trimmed, 'command');
|
|
60
|
+
const name = attr(trimmed, 'name');
|
|
61
|
+
const cwdAttr = attr(trimmed, 'cwd');
|
|
62
|
+
pending = {
|
|
63
|
+
command: command ?? '',
|
|
64
|
+
name: name ?? undefined,
|
|
65
|
+
cwd: resolveCwd(layoutCwd, cwdAttr),
|
|
66
|
+
args: [],
|
|
67
|
+
};
|
|
68
|
+
// Single-line pane (closed on same line, no block) — flush immediately.
|
|
69
|
+
if (trimmed.includes('}') && !trimmed.endsWith('{'))
|
|
70
|
+
flush();
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
// args node inside the current pane block: `args "a" "b" …`
|
|
74
|
+
if (pending && /^args\b/.test(trimmed)) {
|
|
75
|
+
pending.args = [...trimmed.matchAll(/"((?:[^"\\]|\\.)*)"/g)].map(m => unescapeKdl(m[1]));
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
// Next pane / tab / closing — a new `pane` (without command) ends the block.
|
|
79
|
+
if (pending && /^pane\b/.test(trimmed))
|
|
80
|
+
flush();
|
|
81
|
+
}
|
|
82
|
+
flush();
|
|
83
|
+
return panes;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Parse `zellij action dump-layout` into the ordered list of LEAF terminal
|
|
87
|
+
* panes — both command-bearing panes AND idle bare shell panes — skipping
|
|
88
|
+
* plugin panes (tab-bar/status-bar/about), container panes (splits), and the
|
|
89
|
+
* floating subtree. Preserving bare panes is what makes a positional join to
|
|
90
|
+
* `list-panes` correct: a bare shell pane that sorts before the CLI pane would
|
|
91
|
+
* otherwise shift every command pane onto the wrong pane id (the bug Codex
|
|
92
|
+
* found). Templates (new_tab_template / swap_*) are cut off first.
|
|
93
|
+
*
|
|
94
|
+
* zellij always pretty-prints dump-layout one node per line, so a brace-stack
|
|
95
|
+
* line walk is reliable here.
|
|
96
|
+
*/
|
|
97
|
+
export function parseDumpLayoutLeafPanes(kdl) {
|
|
98
|
+
const tmplIdx = kdl.search(TEMPLATE_MARKERS);
|
|
99
|
+
const body = tmplIdx >= 0 ? kdl.slice(0, tmplIdx) : kdl;
|
|
100
|
+
const baseCwdMatch = body.match(/^\s*cwd\s+"([^"]*)"/m);
|
|
101
|
+
const layoutCwd = baseCwdMatch ? baseCwdMatch[1] : undefined;
|
|
102
|
+
const stack = [];
|
|
103
|
+
const leaves = [];
|
|
104
|
+
const inFloating = () => stack.some(f => f.isFloating);
|
|
105
|
+
const emit = (command, name, cwdAttr, args) => {
|
|
106
|
+
if (inFloating())
|
|
107
|
+
return;
|
|
108
|
+
leaves.push({ command, name, cwd: resolveCwd(layoutCwd, cwdAttr), args });
|
|
109
|
+
};
|
|
110
|
+
for (const raw of body.split('\n')) {
|
|
111
|
+
const line = raw.trim();
|
|
112
|
+
if (!line)
|
|
113
|
+
continue;
|
|
114
|
+
if (line === '}') {
|
|
115
|
+
const f = stack.pop();
|
|
116
|
+
// A pane frame that contained neither a plugin nor child panes is a leaf
|
|
117
|
+
// terminal pane (its block held only props like args/start_suspended).
|
|
118
|
+
if (f?.isPane && !f.hasPlugin && !f.hasChildPane && !inFloating()) {
|
|
119
|
+
leaves.push({ command: f.command, name: f.name, cwd: resolveCwd(layoutCwd, f.cwdAttr), args: f.args });
|
|
120
|
+
}
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
const opensBlock = line.endsWith('{');
|
|
124
|
+
if (line.startsWith('plugin')) {
|
|
125
|
+
if (stack.length && stack[stack.length - 1].isPane)
|
|
126
|
+
stack[stack.length - 1].hasPlugin = true;
|
|
127
|
+
if (opensBlock)
|
|
128
|
+
stack.push({ isPane: false, isFloating: false, args: [], hasPlugin: false, hasChildPane: false });
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
if (line.startsWith('pane')) {
|
|
132
|
+
if (stack.length && stack[stack.length - 1].isPane)
|
|
133
|
+
stack[stack.length - 1].hasChildPane = true;
|
|
134
|
+
const command = attr(line, 'command');
|
|
135
|
+
const name = attr(line, 'name');
|
|
136
|
+
const cwdAttr = attr(line, 'cwd');
|
|
137
|
+
if (opensBlock) {
|
|
138
|
+
stack.push({ isPane: true, isFloating: false, command, name, cwdAttr, args: [], hasPlugin: false, hasChildPane: false });
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
emit(command, name, cwdAttr, []); // bare leaf, no block
|
|
142
|
+
}
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
if (opensBlock) {
|
|
146
|
+
// tab / floating_panes / swap_* / other container
|
|
147
|
+
stack.push({ isPane: false, isFloating: line.startsWith('floating_panes'), args: [], hasPlugin: false, hasChildPane: false });
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
if (line.startsWith('args') && stack.length && stack[stack.length - 1].isPane) {
|
|
151
|
+
stack[stack.length - 1].args = [...line.matchAll(/"((?:[^"\\]|\\.)*)"/g)].map(m => unescapeKdl(m[1]));
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return leaves;
|
|
155
|
+
}
|
|
156
|
+
/** Parse `zellij action list-panes --json` into a flat list (document order). */
|
|
157
|
+
export function parseListPanesJson(json) {
|
|
158
|
+
let arr;
|
|
159
|
+
try {
|
|
160
|
+
arr = JSON.parse(json);
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
return [];
|
|
164
|
+
}
|
|
165
|
+
if (!Array.isArray(arr))
|
|
166
|
+
return [];
|
|
167
|
+
return arr.map((p) => ({
|
|
168
|
+
paneId: `terminal_${p.id}`,
|
|
169
|
+
isPlugin: !!p.is_plugin,
|
|
170
|
+
isFloating: !!p.is_floating,
|
|
171
|
+
title: typeof p.title === 'string' ? p.title : undefined,
|
|
172
|
+
terminalCommand: p.terminal_command ?? null,
|
|
173
|
+
}));
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Join dump-layout command panes with list-panes terminal panes by document
|
|
177
|
+
* order: the i-th command pane ↔ the i-th non-plugin terminal pane (sorted by
|
|
178
|
+
* id). zellij assigns pane ids in creation order and walks the tree in a stable
|
|
179
|
+
* order, so this aligns for normal layouts. Returns one DiscoveredCli per
|
|
180
|
+
* command pane that could be bound to an id.
|
|
181
|
+
*/
|
|
182
|
+
export function joinPanes(session, layoutPanes, listed) {
|
|
183
|
+
const terminals = listed
|
|
184
|
+
.filter(p => !p.isPlugin)
|
|
185
|
+
.sort((a, b) => paneNum(a.paneId) - paneNum(b.paneId));
|
|
186
|
+
const out = [];
|
|
187
|
+
for (let i = 0; i < layoutPanes.length; i++) {
|
|
188
|
+
const lp = layoutPanes[i];
|
|
189
|
+
const tp = terminals[i];
|
|
190
|
+
if (!tp)
|
|
191
|
+
break;
|
|
192
|
+
out.push({
|
|
193
|
+
session,
|
|
194
|
+
paneId: tp.paneId,
|
|
195
|
+
command: lp.command,
|
|
196
|
+
cwd: lp.cwd,
|
|
197
|
+
args: lp.args,
|
|
198
|
+
title: tp.title,
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
return out;
|
|
202
|
+
}
|
|
203
|
+
// ─── Runtime (shells out to zellij) ─────────────────────────────────────────
|
|
204
|
+
/** Names of live (non-exited) zellij sessions. */
|
|
205
|
+
export function listLiveSessions() {
|
|
206
|
+
try {
|
|
207
|
+
const out = execFileSync('zellij', ['list-sessions', '--no-formatting'], {
|
|
208
|
+
encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'], timeout: 3000, env: zellijEnv(),
|
|
209
|
+
});
|
|
210
|
+
return out.split('\n').map(l => l.trim())
|
|
211
|
+
.filter(l => l.length > 0 && !/EXITED/i.test(l))
|
|
212
|
+
.map(l => l.split(/\s+/)[0]).filter(Boolean);
|
|
213
|
+
}
|
|
214
|
+
catch {
|
|
215
|
+
return [];
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
function zellijAction(session, args) {
|
|
219
|
+
try {
|
|
220
|
+
return execFileSync('zellij', ['--session', session, 'action', ...args], {
|
|
221
|
+
encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'], timeout: 4000, env: zellijEnv(),
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
catch {
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
/** Discover CLIs running in one session. */
|
|
229
|
+
export function discoverSessionClis(session) {
|
|
230
|
+
const layout = zellijAction(session, ['dump-layout']);
|
|
231
|
+
const panesJson = zellijAction(session, ['list-panes', '--json']);
|
|
232
|
+
if (!layout || !panesJson)
|
|
233
|
+
return [];
|
|
234
|
+
return joinPanes(session, parseDumpLayoutPanes(layout), parseListPanesJson(panesJson));
|
|
235
|
+
}
|
|
236
|
+
/** Discover CLIs across every live zellij session. */
|
|
237
|
+
export function discoverAllClis() {
|
|
238
|
+
return listLiveSessions().flatMap(discoverSessionClis);
|
|
239
|
+
}
|
|
240
|
+
// ─── helpers ────────────────────────────────────────────────────────────────
|
|
241
|
+
function attr(line, key) {
|
|
242
|
+
const m = line.match(new RegExp(`\\b${key}="((?:[^"\\\\]|\\\\.)*)"`));
|
|
243
|
+
return m ? unescapeKdl(m[1]) : undefined;
|
|
244
|
+
}
|
|
245
|
+
function resolveCwd(base, paneCwd) {
|
|
246
|
+
if (!paneCwd)
|
|
247
|
+
return base;
|
|
248
|
+
if (isAbsolute(paneCwd))
|
|
249
|
+
return paneCwd;
|
|
250
|
+
return base ? pathJoin(base, paneCwd) : paneCwd;
|
|
251
|
+
}
|
|
252
|
+
function paneNum(paneId) {
|
|
253
|
+
const m = paneId.match(/(\d+)$/);
|
|
254
|
+
return m ? Number(m[1]) : 0;
|
|
255
|
+
}
|
|
256
|
+
function unescapeKdl(s) {
|
|
257
|
+
return s.replace(/\\(.)/g, '$1');
|
|
258
|
+
}
|
|
259
|
+
//# sourceMappingURL=zellij-session-discovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zellij-session-discovery.js","sourceRoot":"","sources":["../../src/core/zellij-session-discovery.ts"],"names":[],"mappings":"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"}
|
package/dist/daemon.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"AA0BA,YAAY,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"AA0BA,YAAY,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAwErD,OAAO,EAAE,oBAAoB,EAA6B,MAAM,uBAAuB,CAAC;AACxF,OAAO,KAAK,EAAE,sBAAsB,EAAiB,MAAM,wBAAwB,CAAC;AAiTpF,wBAAsB,8BAA8B,CAClD,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,OAAO,CAAC,CA4ClB;AAED,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,GAAG,EAAE,MAAM,GACV,MAAM,GAAG,SAAS,CAIpB;AAED,wBAAgB,+BAA+B,CAC7C,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,GAAG,EAAE,MAAM,GACV,MAAM,GAAG,SAAS,CAGpB;AAyJD,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,sBAAsB,GAAG,oBAAoB,CA0C5G;AA4pED,wBAAsB,WAAW,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA6TlE"}
|
package/dist/daemon.js
CHANGED
|
@@ -27,7 +27,9 @@ import { buildTerminalUrl, setTerminalProxyPort } from './core/terminal-url.js';
|
|
|
27
27
|
import { startTerminalProxy } from './core/terminal-proxy.js';
|
|
28
28
|
import * as scheduler from './core/scheduler.js';
|
|
29
29
|
import { scanMultipleProjects } from './services/project-scanner.js';
|
|
30
|
-
import { buildQuotaExhaustedCard, buildRepoSelectCard, buildStreamingCard, getCliDisplayName } from './im/lark/card-builder.js';
|
|
30
|
+
import { buildPendingResponseCard, buildQuotaExhaustedCard, buildRepoSelectCard, buildStreamingCard, getCliDisplayName } from './im/lark/card-builder.js';
|
|
31
|
+
import { createPendingResponseQueue, markPendingResponseCardPatched, shouldTreatPendingCardAsPatchedByMarker, startPendingResponseTurn, syncPendingResponseState } from './core/pending-response.js';
|
|
32
|
+
import { readPendingResponsePatchMarker } from './services/pending-response-transaction-store.js';
|
|
31
33
|
import { t as tr, botLocale, localeForBot } from './i18n/index.js';
|
|
32
34
|
import { createCliAdapterSync } from './adapters/cli/registry.js';
|
|
33
35
|
import { initWorkerPool, setActiveSessionsRegistry, forkWorker, killWorker, scheduleCardPatch, setCurrentCliVersion, CARD_POSTING_SENTINEL, parkStreamCard, closeSession as closeSessionHelper, ensureCliEnv, writableTerminalLinkFor, } from './core/worker-pool.js';
|
|
@@ -180,6 +182,56 @@ function startMemoryDiagnostics() {
|
|
|
180
182
|
* Lark message ids start with `om_` and chat ids with `oc_`, so the two
|
|
181
183
|
* address spaces never collide; the lookup just tries both.
|
|
182
184
|
*/
|
|
185
|
+
const pendingResponseQueue = createPendingResponseQueue();
|
|
186
|
+
function streamingCardDisabledFor(ds) {
|
|
187
|
+
if (ds.streamingCardForced)
|
|
188
|
+
return false;
|
|
189
|
+
try {
|
|
190
|
+
return getBot(ds.larkAppId).config.disableStreamingCard === true;
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
function readSessionFreshFromDisk(sessionId, larkAppId) {
|
|
197
|
+
const paths = [
|
|
198
|
+
join(config.session.dataDir, `sessions-${larkAppId}.json`),
|
|
199
|
+
join(config.session.dataDir, 'sessions.json'),
|
|
200
|
+
];
|
|
201
|
+
for (const fp of paths) {
|
|
202
|
+
if (!existsSync(fp))
|
|
203
|
+
continue;
|
|
204
|
+
try {
|
|
205
|
+
const data = JSON.parse(readFileSync(fp, 'utf-8'));
|
|
206
|
+
if (data[sessionId])
|
|
207
|
+
return data[sessionId];
|
|
208
|
+
}
|
|
209
|
+
catch { /* ignore corrupt/racing session file */ }
|
|
210
|
+
}
|
|
211
|
+
return undefined;
|
|
212
|
+
}
|
|
213
|
+
async function postPendingResponseCard(ds, replyToMessageId, prompt, sender) {
|
|
214
|
+
if (!streamingCardDisabledFor(ds))
|
|
215
|
+
return;
|
|
216
|
+
await pendingResponseQueue.run(ds.session.sessionId, async () => {
|
|
217
|
+
syncPendingResponseState(ds, readSessionFreshFromDisk(ds.session.sessionId, ds.larkAppId));
|
|
218
|
+
const marker = readPendingResponsePatchMarker(ds.session.sessionId);
|
|
219
|
+
if (shouldTreatPendingCardAsPatchedByMarker(ds.pendingResponseCardId, marker)) {
|
|
220
|
+
markPendingResponseCardPatched(ds);
|
|
221
|
+
}
|
|
222
|
+
syncPendingResponseState(ds.session, ds);
|
|
223
|
+
const card = buildPendingResponseCard(localeForBot(ds.larkAppId));
|
|
224
|
+
try {
|
|
225
|
+
const messageId = await replyMessage(ds.larkAppId, replyToMessageId, card, 'interactive', false);
|
|
226
|
+
startPendingResponseTurn(ds, messageId);
|
|
227
|
+
startPendingResponseTurn(ds.session, messageId);
|
|
228
|
+
sessionStore.updateSession(ds.session);
|
|
229
|
+
}
|
|
230
|
+
catch (err) {
|
|
231
|
+
logger.warn(`[${tag(ds)}] failed to post pending response card: ${err instanceof Error ? err.message : String(err)}`);
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
}
|
|
183
235
|
async function sessionReply(anchor, content, msgType = 'text', larkAppId) {
|
|
184
236
|
let ds;
|
|
185
237
|
if (larkAppId) {
|
|
@@ -1673,6 +1725,7 @@ async function handleNewTopic(data, ctx) {
|
|
|
1673
1725
|
const selfBot = getBot(larkAppId);
|
|
1674
1726
|
const prompt = buildNewTopicPrompt(promptContent, session.sessionId, botCfg.cliId, botCfg.cliPathOverride, attachments, parsed.mentions, await getAvailableBots(larkAppId, chatId), undefined, { name: selfBot.botName, openId: selfBot.botOpenId }, localeForBot(larkAppId), newTopicSender, { larkAppId, chatId });
|
|
1675
1727
|
rememberLastCliInput(ds, promptContent, prompt);
|
|
1728
|
+
await postPendingResponseCard(ds, messageId, content, newTopicSender);
|
|
1676
1729
|
forkWorker(ds, prompt);
|
|
1677
1730
|
const reason = oncallEntry
|
|
1678
1731
|
? `oncall-bound chat ${chatId}`
|
|
@@ -1703,6 +1756,7 @@ async function handleNewTopic(data, ctx) {
|
|
|
1703
1756
|
const selfBot = getBot(larkAppId);
|
|
1704
1757
|
const prompt = buildNewTopicPrompt(promptContent, session.sessionId, botCfg.cliId, botCfg.cliPathOverride, attachments, parsed.mentions, await getAvailableBots(larkAppId, chatId), undefined, { name: selfBot.botName, openId: selfBot.botOpenId }, localeForBot(larkAppId), newTopicSender, { larkAppId, chatId });
|
|
1705
1758
|
rememberLastCliInput(ds, promptContent, prompt);
|
|
1759
|
+
await postPendingResponseCard(ds, messageId, content, newTopicSender);
|
|
1706
1760
|
forkWorker(ds, prompt);
|
|
1707
1761
|
logger.info(`Session ${session.sessionId} ready (no projects to select), total active: ${getActiveCount()}`);
|
|
1708
1762
|
}
|
|
@@ -1863,6 +1917,7 @@ async function handleBotAdded(chatId, operatorOpenId, larkAppId) {
|
|
|
1863
1917
|
return;
|
|
1864
1918
|
const prompt = await buildPrompt();
|
|
1865
1919
|
rememberLastCliInput(ds, promptBody, prompt);
|
|
1920
|
+
await postPendingResponseCard(ds, anchor, promptBody);
|
|
1866
1921
|
forkWorker(ds, prompt);
|
|
1867
1922
|
logger.info(`[auto-start:入群] ${chatId.substring(0, 12)} 自动开工(${mode}/${scope}),workingDir=${pinnedWorkingDir}`);
|
|
1868
1923
|
return;
|
|
@@ -1882,6 +1937,7 @@ async function handleBotAdded(chatId, operatorOpenId, larkAppId) {
|
|
|
1882
1937
|
ds.pendingRepo = false;
|
|
1883
1938
|
const prompt = await buildPrompt();
|
|
1884
1939
|
rememberLastCliInput(ds, promptBody, prompt);
|
|
1940
|
+
await postPendingResponseCard(ds, anchor, promptBody);
|
|
1885
1941
|
forkWorker(ds, prompt);
|
|
1886
1942
|
logger.info(`[auto-start:入群] ${chatId.substring(0, 12)} 无默认目录且无可选项目,直接开工`);
|
|
1887
1943
|
}
|
|
@@ -2133,6 +2189,8 @@ async function handleThreadReply(data, ctx) {
|
|
|
2133
2189
|
// reply cards to whoever triggered this turn — matters in oncall groups
|
|
2134
2190
|
// where the caller is often not the session owner).
|
|
2135
2191
|
if (ds) {
|
|
2192
|
+
syncPendingResponseState(ds, readSessionFreshFromDisk(ds.session.sessionId, ds.larkAppId));
|
|
2193
|
+
syncPendingResponseState(ds.session, ds);
|
|
2136
2194
|
markSessionActivity(ds);
|
|
2137
2195
|
const callerOpenId = parsed.senderId || data?.sender?.sender_id?.open_id;
|
|
2138
2196
|
// quoteTargetId changes every inbound message (always a new message_id), so
|
|
@@ -2285,6 +2343,7 @@ async function handleThreadReply(data, ctx) {
|
|
|
2285
2343
|
const selfBot = getBot(larkAppId);
|
|
2286
2344
|
const prompt = buildNewTopicPrompt(promptContent, session.sessionId, botCfg.cliId, botCfg.cliPathOverride, attachments, parsed.mentions, await getAvailableBots(larkAppId, autoCreateChatId), undefined, { name: selfBot.botName, openId: selfBot.botOpenId }, localeForBot(larkAppId), autoCreateSender, { larkAppId, chatId: autoCreateChatId });
|
|
2287
2345
|
rememberLastCliInput(newDs, promptContent, prompt);
|
|
2346
|
+
await postPendingResponseCard(newDs, parsed.messageId, parsed.content, autoCreateSender);
|
|
2288
2347
|
forkWorker(newDs, prompt);
|
|
2289
2348
|
const reason = oncallEntry
|
|
2290
2349
|
? `oncall-bound chat ${autoCreateChatId}`
|
|
@@ -2315,6 +2374,7 @@ async function handleThreadReply(data, ctx) {
|
|
|
2315
2374
|
const selfBot = getBot(larkAppId);
|
|
2316
2375
|
const prompt = buildNewTopicPrompt(promptContent, session.sessionId, botCfg.cliId, botCfg.cliPathOverride, attachments, parsed.mentions, await getAvailableBots(larkAppId, autoCreateChatId), undefined, { name: selfBot.botName, openId: selfBot.botOpenId }, localeForBot(larkAppId), autoCreateSender, { larkAppId, chatId: autoCreateChatId });
|
|
2317
2376
|
rememberLastCliInput(newDs, promptContent, prompt);
|
|
2377
|
+
await postPendingResponseCard(newDs, parsed.messageId, parsed.content, autoCreateSender);
|
|
2318
2378
|
forkWorker(newDs, prompt);
|
|
2319
2379
|
}
|
|
2320
2380
|
return;
|
|
@@ -2350,6 +2410,7 @@ async function handleThreadReply(data, ctx) {
|
|
|
2350
2410
|
});
|
|
2351
2411
|
beginNewTurn(ds, parsed.content);
|
|
2352
2412
|
rememberLastCliInput(ds, promptContent, msgContent);
|
|
2413
|
+
await postPendingResponseCard(ds, parsed.messageId, parsed.content, await getThreadSender());
|
|
2353
2414
|
ds.worker.send({ type: 'message', content: msgContent });
|
|
2354
2415
|
}
|
|
2355
2416
|
else {
|
|
@@ -2398,6 +2459,7 @@ async function handleThreadReply(data, ctx) {
|
|
|
2398
2459
|
sender: await getThreadSender(),
|
|
2399
2460
|
});
|
|
2400
2461
|
rememberLastCliInput(ds, promptContent, wrappedPrompt);
|
|
2462
|
+
await postPendingResponseCard(ds, parsed.messageId, parsed.content, await getThreadSender());
|
|
2401
2463
|
forkWorker(ds, wrappedPrompt, ds.hasHistory);
|
|
2402
2464
|
}
|
|
2403
2465
|
}
|
|
@@ -2662,9 +2724,13 @@ export async function startDaemon(botIndex) {
|
|
|
2662
2724
|
const backendType = ds.larkAppId
|
|
2663
2725
|
? (getBot(ds.larkAppId).config.backendType ?? config.daemon.backendType)
|
|
2664
2726
|
: config.daemon.backendType;
|
|
2665
|
-
if (backendType === 'tmux') {
|
|
2666
|
-
//
|
|
2667
|
-
//
|
|
2727
|
+
if (backendType === 'tmux' || backendType === 'zellij') {
|
|
2728
|
+
// Persistent backends (tmux / zellij): just kill the worker process —
|
|
2729
|
+
// the multiplexer session survives for re-attach. The worker's SIGTERM
|
|
2730
|
+
// handler calls backend.kill(), which only DETACHES. Going through
|
|
2731
|
+
// killWorker() instead would send {type:'close'} → destroySession() →
|
|
2732
|
+
// `zellij delete-session -f`, permanently erasing the session and
|
|
2733
|
+
// breaking daemon-restart reattach (the blocker Codex flagged).
|
|
2668
2734
|
try {
|
|
2669
2735
|
w.kill('SIGTERM');
|
|
2670
2736
|
}
|