@clipboard-health/groundcrew 4.4.0 → 4.5.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.md +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +49 -16
- package/dist/lib/tmuxAdapter.d.ts.map +1 -1
- package/dist/lib/tmuxAdapter.js +10 -6
- package/dist/lib/workspaceAdapter.d.ts +4 -1
- package/dist/lib/workspaceAdapter.d.ts.map +1 -1
- package/dist/lib/workspaces.d.ts.map +1 -1
- package/dist/lib/workspaces.js +3 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -502,7 +502,7 @@ If a `models.definitions.<name>.cmd` already starts with `safehouse`, groundcrew
|
|
|
502
502
|
<details>
|
|
503
503
|
<summary>Dead tmux windows vanish by default</summary>
|
|
504
504
|
|
|
505
|
-
When a wrapped agent command fails (e.g. `safehouse-clearance` not found, `npm install` crash), the tmux window closes immediately and the error scrolls into the void. Set `GROUNDCREW_KEEP_DEAD_WINDOWS=1` in the env you launch `crew` from to flip the per-window `remain-on-exit` to `on`; the window stays open with the error visible.
|
|
505
|
+
When a wrapped agent command fails (e.g. `safehouse-clearance` not found, `npm install` crash), the tmux window closes immediately and the error scrolls into the void. Set `GROUNDCREW_KEEP_DEAD_WINDOWS=1` in the env you launch `crew` from to flip the per-window `remain-on-exit` to `on`; the window stays open with the error visible. `crew status` reports those kept windows as `exited` and keeps the tmux attach command visible so you can inspect scrollback before resuming or cleaning up. tmux backend only.
|
|
506
506
|
|
|
507
507
|
</details>
|
|
508
508
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAIA,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAanE,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAIA,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAanE,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAkjBD,wBAAsB,MAAM,CAAC,MAAM,EAAE,cAAc,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAU/F;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAI7D"}
|
package/dist/commands/status.js
CHANGED
|
@@ -68,8 +68,14 @@ function ticketWorkspaceText(probe, ticket) {
|
|
|
68
68
|
if (probe.kind === "unavailable") {
|
|
69
69
|
return workspaceProbeUnavailableLine(probe);
|
|
70
70
|
}
|
|
71
|
+
if (isWorkspaceExited(probe, ticket)) {
|
|
72
|
+
return "exited";
|
|
73
|
+
}
|
|
71
74
|
return probe.names.has(ticket) ? "live" : "not live";
|
|
72
75
|
}
|
|
76
|
+
function isWorkspaceExited(probe, ticket) {
|
|
77
|
+
return probe.kind === "ok" && probe.exitedNames?.has(ticket) === true;
|
|
78
|
+
}
|
|
73
79
|
function formatRunState(state) {
|
|
74
80
|
if (state === undefined) {
|
|
75
81
|
return "(none)";
|
|
@@ -116,6 +122,17 @@ function writeRecentLogs(config, ticket) {
|
|
|
116
122
|
writeSection("Recent logs");
|
|
117
123
|
writeOutput(logLines.join("\n"));
|
|
118
124
|
}
|
|
125
|
+
async function exitedWorkspaceAccessHint(config, probe, ticket) {
|
|
126
|
+
if (!isWorkspaceExited(probe, ticket)) {
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
try {
|
|
130
|
+
return await withLogOutputSuppressed(async () => await workspaces.accessHint(config, ticket));
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
return undefined;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
119
136
|
function formatTicketLine(ticket, runState, sourceStatus) {
|
|
120
137
|
const parts = [`ticket: ${ticket}`];
|
|
121
138
|
if (sourceStatus.kind === "found") {
|
|
@@ -154,10 +171,14 @@ async function writeTicketStatus(config, rawTicket) {
|
|
|
154
171
|
withLogOutputSuppressed(async () => await workspaces.probe(config)),
|
|
155
172
|
readTicketSourceStatus(config, ticket),
|
|
156
173
|
]);
|
|
174
|
+
const accessHint = await exitedWorkspaceAccessHint(config, workspaceProbe, ticket);
|
|
157
175
|
writeOutput(formatTicketLine(ticket, runState, sourceStatus));
|
|
158
176
|
writeTicketTitle(runState, sourceStatus);
|
|
159
177
|
writeOutput(`run: ${formatRunState(runState)}`);
|
|
160
178
|
writeOutput(`workspace: ${ticketWorkspaceText(workspaceProbe, ticket)}`);
|
|
179
|
+
if (accessHint !== undefined) {
|
|
180
|
+
writeOutput(`attach: ${accessHint.command}`);
|
|
181
|
+
}
|
|
161
182
|
await writeTicketWorktrees(config, ticket);
|
|
162
183
|
writeRecentLogs(config, ticket);
|
|
163
184
|
}
|
|
@@ -202,22 +223,26 @@ function formatDuration(ms) {
|
|
|
202
223
|
/**
|
|
203
224
|
* Combined human-readable state for the inventory row. Surfaces RunState
|
|
204
225
|
* lifecycle and flags the two interesting disagreements with the workspace
|
|
205
|
-
* probe
|
|
206
|
-
*
|
|
207
|
-
*
|
|
208
|
-
*
|
|
209
|
-
*
|
|
226
|
+
* probe. A recorded running dispatch can have a missing or exited session;
|
|
227
|
+
* an idle row can have a stray live or exited session. `probe.kind ===
|
|
228
|
+
* "unavailable"` is treated as "we don't know" and never produces a suffix.
|
|
229
|
+
* When the row is actively running, appends the elapsed wall-clock time since
|
|
230
|
+
* dispatch.
|
|
210
231
|
*/
|
|
211
232
|
function inventoryStateText(runState, probe, ticket, now) {
|
|
212
233
|
const lifecycle = runState?.state ?? "idle";
|
|
213
234
|
const duration = runStateDurationMs(runState, now);
|
|
214
235
|
const flags = [];
|
|
215
236
|
if (probe.kind === "ok") {
|
|
216
|
-
const
|
|
217
|
-
|
|
218
|
-
|
|
237
|
+
const sessionPresent = probe.names.has(ticket);
|
|
238
|
+
const sessionExited = isWorkspaceExited(probe, ticket);
|
|
239
|
+
if (lifecycle === "idle" && sessionPresent) {
|
|
240
|
+
flags.push(sessionExited ? "stray exited session" : "stray session");
|
|
241
|
+
}
|
|
242
|
+
if ((lifecycle === "running" || lifecycle === "resumed") && sessionExited) {
|
|
243
|
+
flags.push("session exited");
|
|
219
244
|
}
|
|
220
|
-
if ((lifecycle === "running" || lifecycle === "resumed") && !
|
|
245
|
+
else if ((lifecycle === "running" || lifecycle === "resumed") && !sessionPresent) {
|
|
221
246
|
flags.push("session dead");
|
|
222
247
|
}
|
|
223
248
|
}
|
|
@@ -231,9 +256,11 @@ function inventoryStateText(runState, probe, ticket, now) {
|
|
|
231
256
|
* probe disagree. Returned commands are safe defaults; the user is free to
|
|
232
257
|
* ignore them and use `attach:` + `pr:` to investigate first.
|
|
233
258
|
*
|
|
234
|
-
* - Stray session (
|
|
235
|
-
* tear down the orphaned worktree
|
|
236
|
-
* - Session
|
|
259
|
+
* - Stray session (session present, no run-state record) -> `crew cleanup`
|
|
260
|
+
* to tear down the orphaned worktree and close the session.
|
|
261
|
+
* - Session exited (run-state says running/resumed, kept dead tmux window)
|
|
262
|
+
* -> attach first so the failed command remains available for inspection.
|
|
263
|
+
* - Session dead (run-state says running/resumed, no session present) ->
|
|
237
264
|
* `crew resume` to bring the agent back; the worktree is preserved.
|
|
238
265
|
*
|
|
239
266
|
* No hint when the probe is unavailable (we genuinely don't know whether
|
|
@@ -244,11 +271,17 @@ function inventoryHint(runState, probe, ticket) {
|
|
|
244
271
|
return undefined;
|
|
245
272
|
}
|
|
246
273
|
const lifecycle = runState?.state ?? "idle";
|
|
247
|
-
const
|
|
248
|
-
|
|
249
|
-
|
|
274
|
+
const sessionPresent = probe.names.has(ticket);
|
|
275
|
+
const sessionExited = isWorkspaceExited(probe, ticket);
|
|
276
|
+
if (lifecycle === "idle" && sessionPresent) {
|
|
277
|
+
return sessionExited
|
|
278
|
+
? `run 'crew cleanup ${ticket}' to clear this stray exited session`
|
|
279
|
+
: `run 'crew cleanup ${ticket}' to clear this stray session`;
|
|
280
|
+
}
|
|
281
|
+
if ((lifecycle === "running" || lifecycle === "resumed") && sessionExited) {
|
|
282
|
+
return `attach to inspect scrollback, then run 'crew resume ${ticket}'`;
|
|
250
283
|
}
|
|
251
|
-
if ((lifecycle === "running" || lifecycle === "resumed") && !
|
|
284
|
+
if ((lifecycle === "running" || lifecycle === "resumed") && !sessionPresent) {
|
|
252
285
|
return `run 'crew resume ${ticket}' to bring the session back`;
|
|
253
286
|
}
|
|
254
287
|
return undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tmuxAdapter.d.ts","sourceRoot":"","sources":["../../src/lib/tmuxAdapter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,KAAK,OAAO,EAIb,MAAM,uBAAuB,CAAC;AAY/B,eAAO,MAAM,WAAW,EAAE,
|
|
1
|
+
{"version":3,"file":"tmuxAdapter.d.ts","sourceRoot":"","sources":["../../src/lib/tmuxAdapter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,KAAK,OAAO,EAIb,MAAM,uBAAuB,CAAC;AAY/B,eAAO,MAAM,WAAW,EAAE,OA+DzB,CAAC"}
|
package/dist/lib/tmuxAdapter.js
CHANGED
|
@@ -17,8 +17,7 @@ export const tmuxAdapter = {
|
|
|
17
17
|
async open(spec, signal) {
|
|
18
18
|
await ensureTmuxSession(signal);
|
|
19
19
|
const target = tmuxTarget(spec.name);
|
|
20
|
-
const
|
|
21
|
-
const keepDeadWindows = keepDeadWindowsEnv !== undefined && keepDeadWindowsEnv.length > 0;
|
|
20
|
+
const keepDeadWindows = shouldKeepDeadWindows();
|
|
22
21
|
await runWorkspaceCommand("tmux", [
|
|
23
22
|
"new-window",
|
|
24
23
|
"-d",
|
|
@@ -54,7 +53,7 @@ export const tmuxAdapter = {
|
|
|
54
53
|
// oxlint-disable-next-line unicorn/no-useless-undefined -- undefined marks the workspace backend as unavailable.
|
|
55
54
|
return undefined;
|
|
56
55
|
}
|
|
57
|
-
return parseTmuxWindows(probe.output);
|
|
56
|
+
return parseTmuxWindows(probe.output, { includeExited: shouldKeepDeadWindows() });
|
|
58
57
|
},
|
|
59
58
|
async close(name, signal) {
|
|
60
59
|
try {
|
|
@@ -78,6 +77,10 @@ export const tmuxAdapter = {
|
|
|
78
77
|
function tmuxTarget(name) {
|
|
79
78
|
return `${TMUX_SESSION}:${name}`;
|
|
80
79
|
}
|
|
80
|
+
function shouldKeepDeadWindows() {
|
|
81
|
+
const keepDeadWindowsEnv = readEnvironmentVariable("GROUNDCREW_KEEP_DEAD_WINDOWS");
|
|
82
|
+
return keepDeadWindowsEnv === "1";
|
|
83
|
+
}
|
|
81
84
|
function isTmuxNotFoundError(error) {
|
|
82
85
|
// runCommand surfaces the child's stderr in error.message, so the "no
|
|
83
86
|
// server" / "missing session" / "can't find window" signatures are visible
|
|
@@ -130,7 +133,7 @@ async function ensureTmuxSession(signal) {
|
|
|
130
133
|
}
|
|
131
134
|
}
|
|
132
135
|
}
|
|
133
|
-
function parseTmuxWindows(output) {
|
|
136
|
+
function parseTmuxWindows(output, options = {}) {
|
|
134
137
|
const items = [];
|
|
135
138
|
for (const line of output.split("\n")) {
|
|
136
139
|
if (line.length === 0) {
|
|
@@ -147,10 +150,11 @@ function parseTmuxWindows(output) {
|
|
|
147
150
|
// pane_dead != 0 means the command exited and the window is a zombie
|
|
148
151
|
// (only happens when remain-on-exit is on; defense in depth in case a
|
|
149
152
|
// user-globally-set value beats our per-window override).
|
|
150
|
-
|
|
153
|
+
const isExited = deadFlag !== undefined && deadFlag !== "0";
|
|
154
|
+
if (isExited && options.includeExited !== true) {
|
|
151
155
|
continue;
|
|
152
156
|
}
|
|
153
|
-
items.push({ name });
|
|
157
|
+
items.push(isExited ? { name, state: "exited" } : { name });
|
|
154
158
|
}
|
|
155
159
|
return items;
|
|
156
160
|
}
|
|
@@ -10,6 +10,8 @@ export type WorkspaceKind = "cmux" | "tmux";
|
|
|
10
10
|
export interface Workspace {
|
|
11
11
|
/** Ticket id; the join key callers use. */
|
|
12
12
|
name: string;
|
|
13
|
+
/** Omitted means live, for backends that do not expose an exited state. */
|
|
14
|
+
state?: "exited";
|
|
13
15
|
}
|
|
14
16
|
export interface WorkspaceStatus {
|
|
15
17
|
text: string;
|
|
@@ -37,6 +39,7 @@ export interface OpenSpec {
|
|
|
37
39
|
export type WorkspaceProbe = {
|
|
38
40
|
kind: "ok";
|
|
39
41
|
names: Set<string>;
|
|
42
|
+
exitedNames?: Set<string>;
|
|
40
43
|
} | {
|
|
41
44
|
kind: "unavailable";
|
|
42
45
|
error?: unknown;
|
|
@@ -60,7 +63,7 @@ export type WorkspaceCloseResult = {
|
|
|
60
63
|
export interface Adapter {
|
|
61
64
|
open(spec: OpenSpec, signal?: AbortSignal): Promise<void>;
|
|
62
65
|
/**
|
|
63
|
-
*
|
|
66
|
+
* Known workspaces. Returns:
|
|
64
67
|
* - `Workspace[]` when the adapter probe succeeded (may be empty).
|
|
65
68
|
* - `undefined` when the adapter binary failed in a way that doesn't
|
|
66
69
|
* distinguish "no live workspaces" from "couldn't ask".
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspaceAdapter.d.ts","sourceRoot":"","sources":["../../src/lib/workspaceAdapter.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,MAAM,CAAC;AAE5C,MAAM,WAAW,SAAS;IACxB,2CAA2C;IAC3C,IAAI,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"workspaceAdapter.d.ts","sourceRoot":"","sources":["../../src/lib/workspaceAdapter.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,MAAM,CAAC;AAE5C,MAAM,WAAW,SAAS;IACxB,2CAA2C;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,2EAA2E;IAC3E,KAAK,CAAC,EAAE,QAAQ,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,eAAe,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,QAAQ;IACvB,+CAA+C;IAC/C,IAAI,EAAE,MAAM,CAAC;IACb,+CAA+C;IAC/C,GAAG,EAAE,MAAM,CAAC;IACZ,qEAAqE;IACrE,OAAO,EAAE,MAAM,CAAC;IAChB,4EAA4E;IAC5E,MAAM,CAAC,EAAE,eAAe,CAAC;CAC1B;AAED;;;GAGG;AACH,MAAM,MAAM,cAAc,GACtB;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAAC,WAAW,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;CAAE,GAC7D;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAE7C,MAAM,MAAM,wBAAwB,GAChC;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,GACvB;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAE7C,MAAM,MAAM,oBAAoB,GAC5B;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAE7C,MAAM,WAAW,OAAO;IACtB,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D;;;;;OAKG;IACH,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,SAAS,EAAE,GAAG,SAAS,CAAC,CAAC;IAC7D,0DAA0D;IAC1D,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACzE;;;OAGG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,mBAAmB,GAAG,SAAS,CAAC;CAC3D;AAED,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,SAAS,MAAM,EAAE,EAC7B,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,MAAM,CAAC,CAIjB;AAED,wBAAgB,eAAe,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAE7D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspaces.d.ts","sourceRoot":"","sources":["../../src/lib/workspaces.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACxE,OAAO,EAA0B,KAAK,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAE1E,OAAO,EAGL,KAAK,QAAQ,EACb,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,wBAAwB,EAC7B,KAAK,aAAa,EAClB,KAAK,cAAc,EACpB,MAAM,uBAAuB,CAAC;AAE/B,YAAY,EACV,QAAQ,EACR,SAAS,EACT,mBAAmB,EACnB,oBAAoB,EACpB,wBAAwB,EACxB,aAAa,EACb,cAAc,EACd,eAAe,GAChB,MAAM,uBAAuB,CAAC;AAE/B,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,oBAAoB,CAAC;IAChC,QAAQ,EAAE,aAAa,CAAC;IACxB,yDAAyD;IACzD,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,gBAAgB;IACxB,MAAM,EAAE,cAAc,CAAC;IACvB,IAAI,EAAE,gBAAgB,CAAC;CACxB;AAED,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,gBAAgB,GAAG,mBAAmB,CAUtF;AAsDD,iBAAe,eAAe,CAC5B,MAAM,EAAE,cAAc,EACtB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,cAAc,CAAC,
|
|
1
|
+
{"version":3,"file":"workspaces.d.ts","sourceRoot":"","sources":["../../src/lib/workspaces.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACxE,OAAO,EAA0B,KAAK,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAE1E,OAAO,EAGL,KAAK,QAAQ,EACb,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,wBAAwB,EAC7B,KAAK,aAAa,EAClB,KAAK,cAAc,EACpB,MAAM,uBAAuB,CAAC;AAE/B,YAAY,EACV,QAAQ,EACR,SAAS,EACT,mBAAmB,EACnB,oBAAoB,EACpB,wBAAwB,EACxB,aAAa,EACb,cAAc,EACd,eAAe,GAChB,MAAM,uBAAuB,CAAC;AAE/B,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,oBAAoB,CAAC;IAChC,QAAQ,EAAE,aAAa,CAAC;IACxB,yDAAyD;IACzD,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,gBAAgB;IACxB,MAAM,EAAE,cAAc,CAAC;IACvB,IAAI,EAAE,gBAAgB,CAAC;CACxB;AAED,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,gBAAgB,GAAG,mBAAmB,CAUtF;AAsDD,iBAAe,eAAe,CAC5B,MAAM,EAAE,cAAc,EACtB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,cAAc,CAAC,CAiBzB;AAED,iBAAe,sBAAsB,CACnC,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC,CAG1C;AAED,iBAAe,kBAAkB,CAC/B,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,wBAAwB,CAAC,CAenC;AAED,eAAO,MAAM,UAAU;IACf,IAAI,SAAS,cAAc,QAAQ,QAAQ,WAAW,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvF,KAAK;IACC,KAAK,SACD,cAAc,QAChB,MAAM,WACH,WAAW,GACnB,OAAO,CAAC,oBAAoB,CAAC;IAIhC,SAAS;IACT,UAAU;CACX,CAAC"}
|
package/dist/lib/workspaces.js
CHANGED
|
@@ -74,7 +74,9 @@ async function probeWorkspaces(config, signal) {
|
|
|
74
74
|
if (raw === undefined) {
|
|
75
75
|
return { kind: "unavailable" };
|
|
76
76
|
}
|
|
77
|
-
|
|
77
|
+
const names = new Set(raw.map((ws) => ws.name));
|
|
78
|
+
const exitedNames = new Set(raw.filter((ws) => ws.state === "exited").map((ws) => ws.name));
|
|
79
|
+
return exitedNames.size === 0 ? { kind: "ok", names } : { kind: "ok", names, exitedNames };
|
|
78
80
|
}
|
|
79
81
|
async function accessHintForWorkspace(config, name, signal) {
|
|
80
82
|
const adapter = await adapterFor(config, signal);
|
package/package.json
CHANGED