@zswarm/core 0.2.3 → 0.2.5
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/assets/slot.py +35 -1
- package/dist/harness.d.ts +10 -0
- package/dist/harness.js +34 -3
- package/dist/host/cli.js +41 -6
- package/dist/host/crew-windows.d.ts +155 -0
- package/dist/host/crew-windows.js +556 -0
- package/dist/host/crew.d.ts +32 -2
- package/dist/host/crew.js +212 -23
- package/dist/host/lease.d.ts +68 -0
- package/dist/host/lease.js +138 -0
- package/dist/host/relay.d.ts +17 -0
- package/dist/host/relay.js +52 -7
- package/dist/host/services.d.ts +7 -4
- package/dist/host/services.js +18 -4
- package/dist/index.d.ts +4 -3
- package/dist/index.js +3 -2
- package/dist/ops/dispatch.js +69 -5
- package/dist/ops/util.d.ts +9 -0
- package/dist/ops/util.js +24 -0
- package/dist/schema.js +6 -0
- package/dist/state.d.ts +20 -0
- package/dist/state.js +20 -0
- package/dist/zellij/args.d.ts +1 -1
- package/dist/zellij/args.js +3 -1
- package/dist/zellij/bus.js +1 -0
- package/dist/zellij/client.d.ts +1 -0
- package/dist/zellij/client.js +1 -1
- package/dist/zellij/panes.d.ts +1 -0
- package/dist/zellij/panes.js +3 -0
- package/package.json +1 -1
package/assets/slot.py
CHANGED
|
@@ -35,6 +35,7 @@ from __future__ import annotations # macOS ships Python 3.9
|
|
|
35
35
|
|
|
36
36
|
import os
|
|
37
37
|
import re
|
|
38
|
+
import shutil
|
|
38
39
|
import signal
|
|
39
40
|
import subprocess
|
|
40
41
|
import sys
|
|
@@ -300,6 +301,39 @@ def main(argv: list[str]) -> int:
|
|
|
300
301
|
return run(argv, env)
|
|
301
302
|
|
|
302
303
|
|
|
304
|
+
def resolve_command(
|
|
305
|
+
argv: list[str],
|
|
306
|
+
env: dict[str, str],
|
|
307
|
+
os_name: str = os.name,
|
|
308
|
+
which=shutil.which,
|
|
309
|
+
) -> list[str] | str:
|
|
310
|
+
"""Rewrite argv so Windows can start argv[0], including .cmd/.bat shims.
|
|
311
|
+
|
|
312
|
+
CreateProcess does not search PATH or apply PATHEXT and cannot start a
|
|
313
|
+
batch file, so `slot -- pnpm ...` died 127 even though pnpm was on PATH.
|
|
314
|
+
On Windows, resolve argv[0] with shutil.which against the child's PATH;
|
|
315
|
+
shutil.which also applies PATHEXT from the environment this process
|
|
316
|
+
inherits (slot passes that environment straight through). A .cmd/.bat shim
|
|
317
|
+
runs through the command processor with `cmd.exe /d /s /c` and MS-quoted
|
|
318
|
+
arguments -- the documented way to run a batch file, and `cmd /c` returns
|
|
319
|
+
the batch's own exit code. A resolved .exe is spawned directly. POSIX and
|
|
320
|
+
anything that does not resolve are returned unchanged, so Popen still
|
|
321
|
+
reports the same FileNotFoundError and 127 as before.
|
|
322
|
+
"""
|
|
323
|
+
if os_name != "nt" or not argv:
|
|
324
|
+
return argv
|
|
325
|
+
exe = which(argv[0], path=env.get("PATH"))
|
|
326
|
+
if not exe:
|
|
327
|
+
return argv
|
|
328
|
+
if Path(exe).suffix.lower() in (".cmd", ".bat"):
|
|
329
|
+
comspec = env.get("ComSpec") or "cmd.exe"
|
|
330
|
+
# One command line, handed to CreateProcess as is: a list would be
|
|
331
|
+
# quoted again by subprocess, and cmd.exe does not understand \".
|
|
332
|
+
# With /s, cmd strips exactly the outer quote pair and runs the rest.
|
|
333
|
+
return f'"{comspec}" /d /s /c "{subprocess.list2cmdline([exe, *argv[1:]])}"'
|
|
334
|
+
return [exe, *argv[1:]]
|
|
335
|
+
|
|
336
|
+
|
|
303
337
|
def run(argv: list[str], env: dict[str, str], renice: bool = True) -> int:
|
|
304
338
|
"""Run the command as a child and wait. The lock descriptors stay in this
|
|
305
339
|
process (close-on-exec), so a daemon the command leaves behind never
|
|
@@ -310,7 +344,7 @@ def run(argv: list[str], env: dict[str, str], renice: bool = True) -> int:
|
|
|
310
344
|
except OSError:
|
|
311
345
|
pass
|
|
312
346
|
try:
|
|
313
|
-
child = subprocess.Popen(argv, env=env)
|
|
347
|
+
child = subprocess.Popen(resolve_command(argv, env), env=env)
|
|
314
348
|
except FileNotFoundError:
|
|
315
349
|
die(f"{argv[0]}: command not found", 127)
|
|
316
350
|
for sig in (signal.SIGINT, signal.SIGTERM, getattr(signal, "SIGHUP", None)):
|
package/dist/harness.d.ts
CHANGED
|
@@ -14,6 +14,11 @@ export type HarnessProfile = {
|
|
|
14
14
|
* turn. `send` matches these against the trailing lines after the paste.
|
|
15
15
|
*/
|
|
16
16
|
queued: readonly RegExp[];
|
|
17
|
+
/**
|
|
18
|
+
* A hint that the harness is actively working on a turn. `status` matches
|
|
19
|
+
* these against the trailing lines to avoid treating a busy pane as idle.
|
|
20
|
+
*/
|
|
21
|
+
busy: readonly RegExp[];
|
|
17
22
|
};
|
|
18
23
|
/**
|
|
19
24
|
* Identify the harness from a pane's command and title, case-insensitively.
|
|
@@ -29,3 +34,8 @@ export declare function resolveHarness(pane: {
|
|
|
29
34
|
* Only the last few lines count: a quoted marker in scrollback is not a queue.
|
|
30
35
|
*/
|
|
31
36
|
export declare function queuedPrompt(screen: string, profile: HarnessProfile): boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Does this screen's trailing lines show the harness actively working? Only
|
|
39
|
+
* the last few lines count: a quoted marker in scrollback is not a live turn.
|
|
40
|
+
*/
|
|
41
|
+
export declare function busyPrompt(screen: string, profile: HarnessProfile): boolean;
|
package/dist/harness.js
CHANGED
|
@@ -69,11 +69,24 @@ const QUEUED = {
|
|
|
69
69
|
gemini: GENERIC_QUEUED,
|
|
70
70
|
pi: GENERIC_QUEUED,
|
|
71
71
|
};
|
|
72
|
+
/**
|
|
73
|
+
* Busy (working) markers seen live. OpenCode draws a progress bar of filled
|
|
74
|
+
* and hollow squares while a turn runs; no other named harness has an equally
|
|
75
|
+
* reliable marker yet, so they rely on the lease only (`[]`).
|
|
76
|
+
*/
|
|
77
|
+
const BUSY = {
|
|
78
|
+
codex: [],
|
|
79
|
+
cursor: [],
|
|
80
|
+
opencode: [/[■⬝]{4,}/],
|
|
81
|
+
gemini: [],
|
|
82
|
+
pi: [],
|
|
83
|
+
};
|
|
72
84
|
const UNKNOWN = {
|
|
73
85
|
name: "unknown",
|
|
74
86
|
submit: "auto",
|
|
75
87
|
waiting: GENERIC_WAITING,
|
|
76
88
|
queued: GENERIC_QUEUED,
|
|
89
|
+
busy: [/esc to interrupt/i],
|
|
77
90
|
};
|
|
78
91
|
function escapeRegExp(text) {
|
|
79
92
|
return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -118,18 +131,36 @@ export function resolveHarness(pane) {
|
|
|
118
131
|
const name = matchCommand(pane.command) ?? matchTitle(pane.title) ?? "unknown";
|
|
119
132
|
if (name === "unknown")
|
|
120
133
|
return UNKNOWN;
|
|
121
|
-
return {
|
|
134
|
+
return {
|
|
135
|
+
name,
|
|
136
|
+
submit: SUBMIT[name],
|
|
137
|
+
waiting: WAITING[name],
|
|
138
|
+
queued: QUEUED[name],
|
|
139
|
+
busy: BUSY[name],
|
|
140
|
+
};
|
|
122
141
|
}
|
|
123
142
|
/**
|
|
124
143
|
* Does this screen's trailing lines show the harness's queued-message hint?
|
|
125
144
|
* Only the last few lines count: a quoted marker in scrollback is not a queue.
|
|
126
145
|
*/
|
|
127
146
|
export function queuedPrompt(screen, profile) {
|
|
128
|
-
const lines = screen
|
|
147
|
+
const lines = trailingLines(screen);
|
|
148
|
+
return profile.queued.some((re) => lines.some((line) => re.test(line)));
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Does this screen's trailing lines show the harness actively working? Only
|
|
152
|
+
* the last few lines count: a quoted marker in scrollback is not a live turn.
|
|
153
|
+
*/
|
|
154
|
+
export function busyPrompt(screen, profile) {
|
|
155
|
+
const lines = trailingLines(screen);
|
|
156
|
+
return profile.busy.some((re) => lines.some((line) => re.test(line)));
|
|
157
|
+
}
|
|
158
|
+
/** The six last non-empty, right-trimmed lines of a screen, CR stripped. */
|
|
159
|
+
function trailingLines(screen) {
|
|
160
|
+
return screen
|
|
129
161
|
.replace(/\r/g, "")
|
|
130
162
|
.split("\n")
|
|
131
163
|
.map((line) => line.trimEnd())
|
|
132
164
|
.filter((line) => line.length > 0)
|
|
133
165
|
.slice(-6);
|
|
134
|
-
return profile.queued.some((re) => lines.some((line) => re.test(line)));
|
|
135
166
|
}
|
package/dist/host/cli.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import { ZellijError } from "../errors.js";
|
|
3
|
-
import {
|
|
3
|
+
import { createStateStore } from "../state.js";
|
|
4
|
+
import { crewCheck, crewClient, crewDown, crewStart, crewStatus, crewUp } from "./crew.js";
|
|
4
5
|
import { hostDoctor, hostInstall, hostInstallArgs, hostScriptPath } from "./host.js";
|
|
6
|
+
import { listLeases } from "./lease.js";
|
|
5
7
|
import { cancelAllTo, cancelRelay, listRelays, pruneRelays, runRelayDir, startRelay, waitForRelay } from "./relay.js";
|
|
6
8
|
import { runSlot } from "./slot.js";
|
|
7
9
|
/**
|
|
@@ -73,6 +75,10 @@ async function crew(argv) {
|
|
|
73
75
|
return crewStart(session);
|
|
74
76
|
case "check":
|
|
75
77
|
return crewCheck(session);
|
|
78
|
+
case "client":
|
|
79
|
+
// What the minimized console window on Windows runs: attach (and create)
|
|
80
|
+
// the session so its panes render and take input.
|
|
81
|
+
return crewClient(session);
|
|
76
82
|
default:
|
|
77
83
|
process.stderr.write(`${COMMANDS.crew.usage}\n`);
|
|
78
84
|
return 2;
|
|
@@ -80,7 +86,7 @@ async function crew(argv) {
|
|
|
80
86
|
}
|
|
81
87
|
/** A plain-text table for `relay --list`, one row per relay. */
|
|
82
88
|
function relayTable(rows) {
|
|
83
|
-
const header = ["ID", "STATE", "TO", "SESSION", "AGE", "LAST CHECK", "JOB"];
|
|
89
|
+
const header = ["ID", "STATE", "TO", "SESSION", "AGE", "LAST CHECK", "JOB", "LEASE"];
|
|
84
90
|
const body = rows.map((r) => [
|
|
85
91
|
r.id,
|
|
86
92
|
r.state,
|
|
@@ -89,12 +95,26 @@ function relayTable(rows) {
|
|
|
89
95
|
`${r.age}s`,
|
|
90
96
|
r.lastCheck === undefined ? "-" : String(r.lastCheck),
|
|
91
97
|
r.job ?? "-",
|
|
98
|
+
r.lease ? `${r.lease.relayId} (${r.lease.from})` : "-",
|
|
99
|
+
]);
|
|
100
|
+
const widths = header.map((h, i) => Math.max(h.length, ...body.map((row) => row[i].length)));
|
|
101
|
+
return [header, ...body].map((row) => row.map((cell, i) => cell.padEnd(widths[i])).join(" ").trimEnd()).join("\n");
|
|
102
|
+
}
|
|
103
|
+
/** A plain-text table for `relay --leases`, one row per live lease. */
|
|
104
|
+
function leaseTable(leases) {
|
|
105
|
+
const header = ["KEY", "RELAY", "FROM", "PANE", "SINCE"];
|
|
106
|
+
const body = leases.map((l) => [
|
|
107
|
+
l.key,
|
|
108
|
+
l.relayId,
|
|
109
|
+
l.from,
|
|
110
|
+
l.pane,
|
|
111
|
+
new Date(l.at).toISOString(),
|
|
92
112
|
]);
|
|
93
113
|
const widths = header.map((h, i) => Math.max(h.length, ...body.map((row) => row[i].length)));
|
|
94
114
|
return [header, ...body].map((row) => row.map((cell, i) => cell.padEnd(widths[i])).join(" ").trimEnd()).join("\n");
|
|
95
115
|
}
|
|
96
116
|
async function relay(argv) {
|
|
97
|
-
const { flags } = parseHostArgs(argv, ["--foreground", "--prune", "--list", "--cancel-all", "--json"]);
|
|
117
|
+
const { flags } = parseHostArgs(argv, ["--foreground", "--prune", "--list", "--leases", "--cancel-all", "--json", "--queue", "--force"]);
|
|
98
118
|
const run = str(flags["--run"]);
|
|
99
119
|
if (run)
|
|
100
120
|
return runRelayDir(run);
|
|
@@ -115,6 +135,13 @@ async function relay(argv) {
|
|
|
115
135
|
process.stdout.write(`${relayTable(rows)}\n`);
|
|
116
136
|
return 0;
|
|
117
137
|
}
|
|
138
|
+
if (flags["--leases"] === true) {
|
|
139
|
+
const leases = listLeases(createStateStore());
|
|
140
|
+
if (flags["--json"] === true)
|
|
141
|
+
return json(() => ({ leases }));
|
|
142
|
+
process.stdout.write(`${leaseTable(leases)}\n`);
|
|
143
|
+
return 0;
|
|
144
|
+
}
|
|
118
145
|
const cancelId = str(flags["--cancel"]);
|
|
119
146
|
if (cancelId) {
|
|
120
147
|
const res = cancelRelay(cancelId);
|
|
@@ -153,6 +180,8 @@ async function relay(argv) {
|
|
|
153
180
|
timeoutMin: flags["--timeout-min"] ? Number(str(flags["--timeout-min"])) : undefined,
|
|
154
181
|
maxAgeMin: flags["--max-age"] ? Number(str(flags["--max-age"])) : undefined,
|
|
155
182
|
foreground: flags["--foreground"] === true,
|
|
183
|
+
queue: flags["--queue"] === true,
|
|
184
|
+
force: flags["--force"] === true,
|
|
156
185
|
}));
|
|
157
186
|
}
|
|
158
187
|
async function host(argv) {
|
|
@@ -202,10 +231,13 @@ const COMMANDS = {
|
|
|
202
231
|
crew: {
|
|
203
232
|
usage: [
|
|
204
233
|
"zswarm crew up <session> --layout L.kdl [--config C.kdl] [--env-file F] [--first-pane NAME] [--run-as USER]",
|
|
205
|
-
" keep the session running from its layout (service + 2-min watchdog
|
|
234
|
+
" keep the session running from its layout (service + 2-min watchdog;",
|
|
235
|
+
" on Windows: Scheduled Tasks under \\zswarm\\ + an attached client window)",
|
|
206
236
|
"zswarm crew status <session> [--run-as USER] services, session, panes",
|
|
207
237
|
"zswarm crew down <session> [--run-as USER] remove the services and kill the session",
|
|
208
238
|
"zswarm crew start|check <session> what the service and the watchdog run",
|
|
239
|
+
"zswarm crew client <session> what the minimized console window runs (Windows)",
|
|
240
|
+
"--run-as installs for another account and is Linux/macOS only; a Windows crew runs as the signed-in user",
|
|
209
241
|
].join("\n"),
|
|
210
242
|
run: crew,
|
|
211
243
|
},
|
|
@@ -213,11 +245,14 @@ const COMMANDS = {
|
|
|
213
245
|
usage: [
|
|
214
246
|
"zswarm relay --to PANE --done REGEX --reply-to PANE [--serve TARGET] [--session S] [--body-file F]",
|
|
215
247
|
" [--reply-session S] [--from NAME] [--check SCRIPT] [--message TEMPLATE] [--id ID]",
|
|
216
|
-
" [--timeout-min 240] [--max-age MIN] [--foreground]",
|
|
248
|
+
" [--timeout-min 240] [--max-age MIN] [--queue] [--force] [--foreground]",
|
|
217
249
|
" send the body to PANE over serve (ZSWARM_SERVE / ZSWARM_SERVE_TOKEN), then a detached relay",
|
|
218
250
|
" waits for REGEX (and SCRIPT exit 0) and pastes a message into the local --reply-to pane;",
|
|
219
251
|
" --max-age (default --timeout-min + 60) is a hard end that always yields a final status",
|
|
220
|
-
"
|
|
252
|
+
" it leases its target pane until it ends; a second relay to a leased pane fails pane_leased",
|
|
253
|
+
" unless --queue waits for the lease to free or --force shares it",
|
|
254
|
+
"zswarm relay --list [--json] id, state, target, session, age, last check, job and lease per relay dir",
|
|
255
|
+
"zswarm relay --leases [--json] live pane leases and the relay that holds each",
|
|
221
256
|
"zswarm relay --cancel ID | --cancel-all --to PANE stop the job, mark it CANCELLED, paste nothing",
|
|
222
257
|
"zswarm relay --wait ID [--timeout-min 240] block until relay ID has its message, print it (pull path)",
|
|
223
258
|
"zswarm relay --prune [--days 14] remove old relay dirs and undelivered messages",
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Windows half of `zswarm crew`.
|
|
3
|
+
*
|
|
4
|
+
* A background Zellij session started with `zellij attach --create-background`
|
|
5
|
+
* on Windows renders empty pane grids and drops pasted input, because nothing
|
|
6
|
+
* is attached in a real console. The fix is two Scheduled Tasks under
|
|
7
|
+
* `\zswarm\` plus a minimized attached-client console window:
|
|
8
|
+
*
|
|
9
|
+
* zswarm-crew-<session> starts the session (interactive, highest)
|
|
10
|
+
* zswarm-crew-<session>-watch `crew check` every two minutes, so the crew
|
|
11
|
+
* restarts only when the session or its first
|
|
12
|
+
* layout pane is gone, and the client window is
|
|
13
|
+
* reopened when it is gone but the session lives
|
|
14
|
+
*
|
|
15
|
+
* This module is pure PowerShell text (unit-tested by text) plus thin async
|
|
16
|
+
* wrappers. It deliberately mirrors `buildServeTaskScript` in
|
|
17
|
+
* `ops/serve-install.ts`: same preamble helpers, same base64 UTF-8 round-trip
|
|
18
|
+
* for the cmd.exe action body, same `-EncodedCommand` invocation. It must not
|
|
19
|
+
* import `./crew.js`; that module imports this one.
|
|
20
|
+
*/
|
|
21
|
+
/** Scheduled-task folder every crew task lives in. */
|
|
22
|
+
export declare const CREW_TASK_FOLDER = "\\zswarm\\";
|
|
23
|
+
/** Watchdog cadence, matching the Linux/macOS `crew-<s>-watch` timer. */
|
|
24
|
+
export declare const CREW_WATCH_MINUTES = 2;
|
|
25
|
+
export type PowerShellResult = {
|
|
26
|
+
code: number;
|
|
27
|
+
stdout: string;
|
|
28
|
+
stderr: string;
|
|
29
|
+
};
|
|
30
|
+
export type PowerShellExec = (script: string, options: {
|
|
31
|
+
timeoutMs: number;
|
|
32
|
+
signal?: AbortSignal;
|
|
33
|
+
}) => Promise<PowerShellResult>;
|
|
34
|
+
export type CrewTaskNames = {
|
|
35
|
+
folder: string;
|
|
36
|
+
start: string;
|
|
37
|
+
watch: string;
|
|
38
|
+
};
|
|
39
|
+
export type CrewTaskState = {
|
|
40
|
+
/** Which of the two crew tasks this is. */
|
|
41
|
+
name: "start" | "watch";
|
|
42
|
+
/** Task name only, e.g. `zswarm-crew-crew`. */
|
|
43
|
+
task: string;
|
|
44
|
+
/** `\zswarm\zswarm-crew-crew`. */
|
|
45
|
+
path: string;
|
|
46
|
+
/** `Ready` | `Running` | `Disabled` | `missing` ... */
|
|
47
|
+
state: string;
|
|
48
|
+
/** Registered at all: Ready/Running/Disabled count; `missing` does not. */
|
|
49
|
+
active: boolean;
|
|
50
|
+
lastResult: number | null;
|
|
51
|
+
lastRun: string | null;
|
|
52
|
+
};
|
|
53
|
+
export type CrewClientWindow = {
|
|
54
|
+
present: boolean;
|
|
55
|
+
pid: number | null;
|
|
56
|
+
count: number;
|
|
57
|
+
};
|
|
58
|
+
/** `zswarm-crew-<session>`: task names, and the client window's title marker. */
|
|
59
|
+
export declare function crewTaskNames(session: string): CrewTaskNames;
|
|
60
|
+
/** The token that proves a console window is this crew's client: its title and
|
|
61
|
+
* the string in its launcher's command line. */
|
|
62
|
+
export declare function crewClientMarker(session: string): string;
|
|
63
|
+
/** The cmd.exe action body for a task: `set "K=V"&& ...` then the CLI argv. */
|
|
64
|
+
export declare function crewTaskCommand(input: {
|
|
65
|
+
launch: {
|
|
66
|
+
execPath: string;
|
|
67
|
+
scriptPath: string;
|
|
68
|
+
};
|
|
69
|
+
mode: "start" | "check";
|
|
70
|
+
session: string;
|
|
71
|
+
env: NodeJS.ProcessEnv;
|
|
72
|
+
}): string;
|
|
73
|
+
/** Pure PowerShell text, unit-tested by text. */
|
|
74
|
+
export declare function buildCrewTaskScript(action: "register" | "inspect" | "start" | "unregister", params: {
|
|
75
|
+
session: string;
|
|
76
|
+
startCommand?: string;
|
|
77
|
+
watchCommand?: string;
|
|
78
|
+
intervalMinutes?: number;
|
|
79
|
+
}): string;
|
|
80
|
+
/**
|
|
81
|
+
* The command line the client window runs. The marker is both the window title
|
|
82
|
+
* and the token `ensure`/`close` look for in a process command line; the crew
|
|
83
|
+
* CLI does the actual `zellij attach --create`, so the window can read the crew
|
|
84
|
+
* dir (and its env file) itself instead of taking secrets on a command line.
|
|
85
|
+
*/
|
|
86
|
+
export declare function crewClientCommand(input: {
|
|
87
|
+
session: string;
|
|
88
|
+
launch: {
|
|
89
|
+
execPath: string;
|
|
90
|
+
scriptPath: string;
|
|
91
|
+
};
|
|
92
|
+
}): string;
|
|
93
|
+
/** The minimized attached-client window that makes a Windows Zellij session render. */
|
|
94
|
+
export declare function buildCrewClientScript(action: "ensure" | "close", params: {
|
|
95
|
+
session: string;
|
|
96
|
+
clientCommand?: string;
|
|
97
|
+
}): string;
|
|
98
|
+
/** Last line that parses as a JSON object, else the first; null when none does. */
|
|
99
|
+
export declare function parseCrewJson(stdout: string): Record<string, unknown> | null;
|
|
100
|
+
/** Powershell runner used in production; injected in tests. Never throws. */
|
|
101
|
+
export declare const defaultPowerShellExec: PowerShellExec;
|
|
102
|
+
export declare function inspectCrewTasks(input: {
|
|
103
|
+
session: string;
|
|
104
|
+
runPowerShell: PowerShellExec;
|
|
105
|
+
timeoutMs?: number;
|
|
106
|
+
signal?: AbortSignal;
|
|
107
|
+
}): Promise<{
|
|
108
|
+
tasks: CrewTaskState[];
|
|
109
|
+
client: CrewClientWindow;
|
|
110
|
+
}>;
|
|
111
|
+
export declare function installCrewTasks(input: {
|
|
112
|
+
session: string;
|
|
113
|
+
startCommand: string;
|
|
114
|
+
watchCommand: string;
|
|
115
|
+
intervalMinutes?: number;
|
|
116
|
+
runPowerShell: PowerShellExec;
|
|
117
|
+
timeoutMs?: number;
|
|
118
|
+
signal?: AbortSignal;
|
|
119
|
+
}): Promise<{
|
|
120
|
+
lines: string[];
|
|
121
|
+
notes: string[];
|
|
122
|
+
tasks: CrewTaskState[];
|
|
123
|
+
client: CrewClientWindow;
|
|
124
|
+
}>;
|
|
125
|
+
export declare function removeCrewTasks(input: {
|
|
126
|
+
session: string;
|
|
127
|
+
runPowerShell: PowerShellExec;
|
|
128
|
+
timeoutMs?: number;
|
|
129
|
+
signal?: AbortSignal;
|
|
130
|
+
}): Promise<{
|
|
131
|
+
lines: string[];
|
|
132
|
+
tasks: CrewTaskState[];
|
|
133
|
+
}>;
|
|
134
|
+
export declare function ensureCrewClient(input: {
|
|
135
|
+
session: string;
|
|
136
|
+
clientCommand: string;
|
|
137
|
+
runPowerShell: PowerShellExec;
|
|
138
|
+
timeoutMs?: number;
|
|
139
|
+
signal?: AbortSignal;
|
|
140
|
+
}): Promise<{
|
|
141
|
+
opened: boolean;
|
|
142
|
+
pid: number | null;
|
|
143
|
+
title: string;
|
|
144
|
+
present: boolean;
|
|
145
|
+
count: number;
|
|
146
|
+
}>;
|
|
147
|
+
export declare function closeCrewClient(input: {
|
|
148
|
+
session: string;
|
|
149
|
+
runPowerShell: PowerShellExec;
|
|
150
|
+
timeoutMs?: number;
|
|
151
|
+
signal?: AbortSignal;
|
|
152
|
+
}): Promise<{
|
|
153
|
+
closed: number[];
|
|
154
|
+
count: number;
|
|
155
|
+
}>;
|