@namzu/sdk 14.0.2 → 14.0.4
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/CHANGELOG.md +45 -0
- package/dist/connector/mcp/__tests__/a-close-that-means-closed.test.d.ts +2 -0
- package/dist/connector/mcp/__tests__/a-close-that-means-closed.test.d.ts.map +1 -0
- package/dist/connector/mcp/__tests__/a-close-that-means-closed.test.js +106 -0
- package/dist/connector/mcp/__tests__/a-close-that-means-closed.test.js.map +1 -0
- package/dist/connector/mcp/stdio.d.ts.map +1 -1
- package/dist/connector/mcp/stdio.js +38 -1
- package/dist/connector/mcp/stdio.js.map +1 -1
- package/dist/manager/project/__tests__/a-closed-workspace-takes-no-work.test.js +40 -0
- package/dist/manager/project/__tests__/a-closed-workspace-takes-no-work.test.js.map +1 -1
- package/dist/manager/project/lifecycle.d.ts.map +1 -1
- package/dist/manager/project/lifecycle.js +17 -1
- package/dist/manager/project/lifecycle.js.map +1 -1
- package/package.json +1 -1
- package/src/connector/mcp/__tests__/a-close-that-means-closed.test.ts +119 -0
- package/src/connector/mcp/stdio.ts +38 -1
- package/src/manager/project/__tests__/a-closed-workspace-takes-no-work.test.ts +46 -0
- package/src/manager/project/lifecycle.ts +19 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,50 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 14.0.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- b4a3fa7: `StdioTransport.close()` resolves when the child is gone, not when the signal
|
|
8
|
+
was sent.
|
|
9
|
+
|
|
10
|
+
It called `kill('SIGTERM')` and returned. `kill()` returns as soon as the
|
|
11
|
+
signal is delivered, so an awaited `close()` meant "SIGTERM is on its way", and
|
|
12
|
+
a caller that closed a transport and then deleted the child's working directory
|
|
13
|
+
raced the exit — reported as `EBUSY` from a real integration, not inferred. A
|
|
14
|
+
close that does not mean closed makes every teardown built on it a guess, and
|
|
15
|
+
the guess is only wrong sometimes.
|
|
16
|
+
|
|
17
|
+
`close()` now waits for the child's `exit`. A child ignoring SIGTERM is sent
|
|
18
|
+
SIGKILL after two seconds, and a second timer gives up waiting, so `close()`
|
|
19
|
+
cannot hang; both timers are unreferenced, so neither holds the event loop
|
|
20
|
+
open. A spawn that never produced a process emits `error` and no `exit`, and
|
|
21
|
+
that path settles too rather than waiting for an event that is not coming.
|
|
22
|
+
|
|
23
|
+
## 14.0.3
|
|
24
|
+
|
|
25
|
+
### Patch Changes
|
|
26
|
+
|
|
27
|
+
- c50f9bf: `ProjectManager.archive` refuses when it cannot establish the precondition.
|
|
28
|
+
|
|
29
|
+
14.0.0 shipped archival that "refuses rather than cascading" — a workspace with
|
|
30
|
+
a live session throws `ProjectNotEmptyError` instead of closing over running
|
|
31
|
+
work. The check read the session list as
|
|
32
|
+
`(await store.listSessionsByProject?.(...)) ?? []`, and `listSessionsByProject`
|
|
33
|
+
is optional. So on a store that does not implement it, "this store cannot tell
|
|
34
|
+
me what is running here" became "nothing is running here": the workspace closed
|
|
35
|
+
over live sessions and the call returned success.
|
|
36
|
+
|
|
37
|
+
It now throws, naming the missing method and what to do instead. Both stores in
|
|
38
|
+
this package implement it, so nothing in-tree changes; a host with its own
|
|
39
|
+
`SessionStore` gets a refusal where it previously got a wrong answer.
|
|
40
|
+
|
|
41
|
+
The mistake is worth naming, because the two halves are each correct and only
|
|
42
|
+
the combination is not. Optional-on-the-interface protects implementors: a
|
|
43
|
+
host's own store should not stop compiling because the SDK grew a method. It
|
|
44
|
+
cannot also mean a **safety precondition silently passes**. Where a store
|
|
45
|
+
cannot answer the question the check exists to ask, the answer is a refusal,
|
|
46
|
+
not a default.
|
|
47
|
+
|
|
3
48
|
## 14.0.2
|
|
4
49
|
|
|
5
50
|
### Patch Changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"a-close-that-means-closed.test.d.ts","sourceRoot":"","sources":["../../../../src/connector/mcp/__tests__/a-close-that-means-closed.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { StdioTransport } from '../stdio.js';
|
|
3
|
+
/**
|
|
4
|
+
* `close()` sent SIGTERM and returned without waiting, so a resolved close
|
|
5
|
+
* meant "the signal is on its way", not "the child is gone". A caller that
|
|
6
|
+
* closed a transport and then deleted the child's working directory raced the
|
|
7
|
+
* exit and saw EBUSY — reported from a real integration, not inferred.
|
|
8
|
+
*
|
|
9
|
+
* A close that does not mean closed makes every teardown built on top of it a
|
|
10
|
+
* guess, and the guess is only wrong sometimes, which is the worst kind.
|
|
11
|
+
*
|
|
12
|
+
* These assert on the operating system rather than on the transport's own
|
|
13
|
+
* state: `process.kill(pid, 0)` throws ESRCH once the pid is gone, so the
|
|
14
|
+
* assertion cannot pass by the transport merely believing it closed.
|
|
15
|
+
*/
|
|
16
|
+
/** The child the transport spawned, read before `close()` clears the field. */
|
|
17
|
+
function childOf(transport) {
|
|
18
|
+
const child = transport.process;
|
|
19
|
+
if (!child)
|
|
20
|
+
throw new Error('transport spawned no process');
|
|
21
|
+
return child;
|
|
22
|
+
}
|
|
23
|
+
function pidOf(transport) {
|
|
24
|
+
const pid = childOf(transport).pid;
|
|
25
|
+
if (pid === undefined)
|
|
26
|
+
throw new Error('transport spawned no process');
|
|
27
|
+
return pid;
|
|
28
|
+
}
|
|
29
|
+
function isAlive(pid) {
|
|
30
|
+
try {
|
|
31
|
+
process.kill(pid, 0);
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
describe('a close that means closed', () => {
|
|
39
|
+
it('does not resolve until the child is actually gone', async () => {
|
|
40
|
+
const transport = new StdioTransport({
|
|
41
|
+
type: 'stdio',
|
|
42
|
+
command: process.execPath,
|
|
43
|
+
args: ['-e', 'setInterval(() => {}, 1000)'],
|
|
44
|
+
});
|
|
45
|
+
await transport.connect();
|
|
46
|
+
const child = childOf(transport);
|
|
47
|
+
const pid = pidOf(transport);
|
|
48
|
+
expect(isAlive(pid)).toBe(true);
|
|
49
|
+
await transport.close();
|
|
50
|
+
// The reaped-ness of the child, not the liveness of the pid.
|
|
51
|
+
//
|
|
52
|
+
// `isAlive(pid)` alone is sound about the wrong thing: on Windows
|
|
53
|
+
// `kill('SIGTERM')` terminates the process synchronously, so the pid is
|
|
54
|
+
// already gone by the next line whether or not `close()` waited — the
|
|
55
|
+
// assertion passed under a deliberately reintroduced fire-and-forget
|
|
56
|
+
// kill, which is how this was caught. Node fills `exitCode`/`signalCode`
|
|
57
|
+
// only when it reaps the child and emits `exit`, a later tick, so these
|
|
58
|
+
// are non-null here exactly when `close()` awaited that event.
|
|
59
|
+
expect(child.exitCode !== null || child.signalCode !== null).toBe(true);
|
|
60
|
+
expect(isAlive(pid)).toBe(false);
|
|
61
|
+
});
|
|
62
|
+
it('returns rather than hanging when the command does not exist', async () => {
|
|
63
|
+
// A spawn that fails emits `error` and never `exit`. Waiting on `exit`
|
|
64
|
+
// alone would turn a bad command into a shutdown that never completes.
|
|
65
|
+
const transport = new StdioTransport({
|
|
66
|
+
type: 'stdio',
|
|
67
|
+
command: 'namzu-no-such-command-exists-here',
|
|
68
|
+
args: [],
|
|
69
|
+
});
|
|
70
|
+
await transport.connect();
|
|
71
|
+
await expect(transport.close()).resolves.toBeUndefined();
|
|
72
|
+
});
|
|
73
|
+
it('is safe to call twice', async () => {
|
|
74
|
+
const transport = new StdioTransport({
|
|
75
|
+
type: 'stdio',
|
|
76
|
+
command: process.execPath,
|
|
77
|
+
args: ['-e', 'setInterval(() => {}, 1000)'],
|
|
78
|
+
});
|
|
79
|
+
await transport.connect();
|
|
80
|
+
const pid = pidOf(transport);
|
|
81
|
+
await transport.close();
|
|
82
|
+
await transport.close();
|
|
83
|
+
expect(isAlive(pid)).toBe(false);
|
|
84
|
+
});
|
|
85
|
+
it('reports the exit to the handler registered for that session', async () => {
|
|
86
|
+
// The waiting must not swallow the notification the client depends on
|
|
87
|
+
// to learn the session ended.
|
|
88
|
+
const transport = new StdioTransport({
|
|
89
|
+
type: 'stdio',
|
|
90
|
+
command: process.execPath,
|
|
91
|
+
args: ['-e', 'setInterval(() => {}, 1000)'],
|
|
92
|
+
});
|
|
93
|
+
await transport.connect();
|
|
94
|
+
let closes = 0;
|
|
95
|
+
transport.onClose(() => {
|
|
96
|
+
closes++;
|
|
97
|
+
});
|
|
98
|
+
await transport.close();
|
|
99
|
+
// `close` on a ChildProcess follows `exit` by a tick once the stdio
|
|
100
|
+
// streams drain; give the loop that tick rather than asserting on a
|
|
101
|
+
// race.
|
|
102
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
103
|
+
expect(closes).toBe(1);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
//# sourceMappingURL=a-close-that-means-closed.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"a-close-that-means-closed.test.js","sourceRoot":"","sources":["../../../../src/connector/mcp/__tests__/a-close-that-means-closed.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stdio.d.ts","sourceRoot":"","sources":["../../../src/connector/mcp/stdio.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACX,iBAAiB,EACjB,uBAAuB,EACvB,YAAY,EACZ,MAAM,gCAAgC,CAAA;
|
|
1
|
+
{"version":3,"file":"stdio.d.ts","sourceRoot":"","sources":["../../../src/connector/mcp/stdio.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACX,iBAAiB,EACjB,uBAAuB,EACvB,YAAY,EACZ,MAAM,gCAAgC,CAAA;AAUvC,qBAAa,cAAe,YAAW,YAAY;IAWtC,OAAO,CAAC,QAAQ,CAAC,MAAM;IAVnC,OAAO,CAAC,OAAO,CAA4B;IAC3C,OAAO,CAAC,eAAe,CAAkD;IACzE,OAAO,CAAC,aAAa,CAAwB;IAC7C,OAAO,CAAC,aAAa,CAAoC;IACzD,OAAO,CAAC,SAAS,CAAQ;IACzB,6DAA6D;IAC7D,OAAO,CAAC,WAAW,CAAQ;IAC3B,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,GAAG,CAAQ;gBAEU,MAAM,EAAE,uBAAuB;IAItD,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAuCxB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAkD5B;;;;;;;;OAQG;IACH,OAAO,CAAC,aAAa;IAMf,IAAI,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAQrD,SAAS,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,IAAI,GAAG,IAAI;IAI9D,OAAO,CAAC,OAAO,EAAE,MAAM,IAAI,GAAG,IAAI;IAIlC,OAAO,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,GAAG,IAAI;IAI9C,WAAW,IAAI,OAAO;IAItB,OAAO,CAAC,aAAa;CAerB"}
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
2
|
import { getRootLogger } from '../../utils/logger.js';
|
|
3
|
+
/**
|
|
4
|
+
* How long a child gets to honour SIGTERM before SIGKILL. Two seconds is
|
|
5
|
+
* long enough for a server flushing a response and short enough that a
|
|
6
|
+
* shutdown does not read as a hang.
|
|
7
|
+
*/
|
|
8
|
+
const TERMINATE_GRACE_MS = 2_000;
|
|
3
9
|
export class StdioTransport {
|
|
4
10
|
config;
|
|
5
11
|
process = null;
|
|
@@ -60,9 +66,40 @@ export class StdioTransport {
|
|
|
60
66
|
}
|
|
61
67
|
this.connected = false;
|
|
62
68
|
this.exitPending = true;
|
|
63
|
-
this.process
|
|
69
|
+
const child = this.process;
|
|
64
70
|
this.process = null;
|
|
65
71
|
this.buffer = '';
|
|
72
|
+
// Resolve when the child is actually gone, not when the signal was
|
|
73
|
+
// sent. `kill()` returns as soon as the signal is delivered, so an
|
|
74
|
+
// awaited `close()` meant only "SIGTERM is on its way" — a caller that
|
|
75
|
+
// closed and then deleted the child's working directory raced the exit
|
|
76
|
+
// and saw EBUSY. A close that does not mean closed makes every
|
|
77
|
+
// teardown after it a guess.
|
|
78
|
+
//
|
|
79
|
+
// A spawn that never produced a process emits `error` and no `exit`,
|
|
80
|
+
// so both settle this, and two timers make a hang impossible: the
|
|
81
|
+
// first escalates to SIGKILL for a child ignoring SIGTERM, the second
|
|
82
|
+
// gives up waiting. Neither holds the event loop open.
|
|
83
|
+
if (child.pid === undefined)
|
|
84
|
+
return;
|
|
85
|
+
await new Promise((resolve) => {
|
|
86
|
+
let settled = false;
|
|
87
|
+
const finish = () => {
|
|
88
|
+
if (settled)
|
|
89
|
+
return;
|
|
90
|
+
settled = true;
|
|
91
|
+
clearTimeout(escalate);
|
|
92
|
+
clearTimeout(giveUp);
|
|
93
|
+
resolve();
|
|
94
|
+
};
|
|
95
|
+
child.once('exit', finish);
|
|
96
|
+
child.once('error', finish);
|
|
97
|
+
child.kill('SIGTERM');
|
|
98
|
+
const escalate = setTimeout(() => child.kill('SIGKILL'), TERMINATE_GRACE_MS);
|
|
99
|
+
const giveUp = setTimeout(finish, TERMINATE_GRACE_MS * 2);
|
|
100
|
+
escalate.unref?.();
|
|
101
|
+
giveUp.unref?.();
|
|
102
|
+
});
|
|
66
103
|
// The handlers deliberately survive this call. `process.on('close')`
|
|
67
104
|
// fires them when the process actually exits, and dropping them here
|
|
68
105
|
// would leave `MCPClient` believing it is still connected — so its next
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stdio.js","sourceRoot":"","sources":["../../../src/connector/mcp/stdio.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"stdio.js","sourceRoot":"","sources":["../../../src/connector/mcp/stdio.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAM7D,OAAO,EAAe,aAAa,EAAE,MAAM,uBAAuB,CAAA;AAElE;;;;GAIG;AACH,MAAM,kBAAkB,GAAG,KAAK,CAAA;AAEhC,MAAM,OAAO,cAAc;IAWG;IAVrB,OAAO,GAAwB,IAAI,CAAA;IACnC,eAAe,GAAgD,EAAE,CAAA;IACjE,aAAa,GAAsB,EAAE,CAAA;IACrC,aAAa,GAAkC,EAAE,CAAA;IACjD,SAAS,GAAG,KAAK,CAAA;IACzB,6DAA6D;IACrD,WAAW,GAAG,KAAK,CAAA;IACnB,MAAM,GAAG,EAAE,CAAA;IACX,GAAG,CAAQ;IAEnB,YAA6B,MAA+B;QAA/B,WAAM,GAAN,MAAM,CAAyB;QAC3D,IAAI,CAAC,GAAG,GAAG,aAAa,EAAE,CAAC,KAAK,CAAC,EAAE,SAAS,EAAE,gBAAgB,EAAE,CAAC,CAAA;IAClE,CAAC;IAED,KAAK,CAAC,OAAO;QACZ,IAAI,IAAI,CAAC,SAAS;YAAE,OAAM;QAE1B,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,EAAE,EAAE;YACjE,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE;YAC3C,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,GAAG;YACpB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;SAC/B,CAAC,CAAA;QAEF,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YACjD,IAAI,CAAC,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAA;YACtC,IAAI,CAAC,aAAa,EAAE,CAAA;QACrB,CAAC,CAAC,CAAA;QAEF,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YACjD,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,sBAAsB,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAA;QACtE,CAAC,CAAC,CAAA;QAEF,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;YACjC,IAAI,CAAC,SAAS,GAAG,KAAK,CAAA;YACtB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,uCAAuC,IAAI,EAAE,CAAC,CAAA;YAC5D,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,aAAa;gBAAE,OAAO,EAAE,CAAA;YACnD,kEAAkE;YAClE,iCAAiC;YACjC,IAAI,CAAC,WAAW,GAAG,KAAK,CAAA;YACxB,IAAI,CAAC,aAAa,EAAE,CAAA;QACrB,CAAC,CAAC,CAAA;QAEF,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YAChC,IAAI,CAAC,SAAS,GAAG,KAAK,CAAA;YACtB,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,aAAa;gBAAE,OAAO,CAAC,GAAG,CAAC,CAAA;QACvD,CAAC,CAAC,CAAA;QAEF,IAAI,CAAC,SAAS,GAAG,IAAI,CAAA;QACrB,IAAI,CAAC,GAAG,CAAC,IAAI,CACZ,6BAA6B,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CACxF,CAAA;IACF,CAAC;IAED,KAAK,CAAC,KAAK;QACV,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;YACnB,oEAAoE;YACpE,8DAA8D;YAC9D,oEAAoE;YACpE,uCAAuC;YACvC,IAAI,CAAC,IAAI,CAAC,WAAW;gBAAE,IAAI,CAAC,aAAa,EAAE,CAAA;YAC3C,OAAM;QACP,CAAC;QACD,IAAI,CAAC,SAAS,GAAG,KAAK,CAAA;QACtB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAA;QACvB,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAA;QAC1B,IAAI,CAAC,OAAO,GAAG,IAAI,CAAA;QACnB,IAAI,CAAC,MAAM,GAAG,EAAE,CAAA;QAEhB,mEAAmE;QACnE,mEAAmE;QACnE,uEAAuE;QACvE,uEAAuE;QACvE,+DAA+D;QAC/D,6BAA6B;QAC7B,EAAE;QACF,qEAAqE;QACrE,kEAAkE;QAClE,sEAAsE;QACtE,uDAAuD;QACvD,IAAI,KAAK,CAAC,GAAG,KAAK,SAAS;YAAE,OAAM;QACnC,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;YACnC,IAAI,OAAO,GAAG,KAAK,CAAA;YACnB,MAAM,MAAM,GAAG,GAAS,EAAE;gBACzB,IAAI,OAAO;oBAAE,OAAM;gBACnB,OAAO,GAAG,IAAI,CAAA;gBACd,YAAY,CAAC,QAAQ,CAAC,CAAA;gBACtB,YAAY,CAAC,MAAM,CAAC,CAAA;gBACpB,OAAO,EAAE,CAAA;YACV,CAAC,CAAA;YACD,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;YAC1B,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;YAC3B,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;YACrB,MAAM,QAAQ,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,kBAAkB,CAAC,CAAA;YAC5E,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,EAAE,kBAAkB,GAAG,CAAC,CAAC,CAAA;YACzD,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAA;YAClB,MAAM,CAAC,KAAK,EAAE,EAAE,CAAA;QACjB,CAAC,CAAC,CAAA;QACF,qEAAqE;QACrE,qEAAqE;QACrE,wEAAwE;QACxE,yDAAyD;IAC1D,CAAC;IAED;;;;;;;;OAQG;IACK,aAAa;QACpB,IAAI,CAAC,eAAe,GAAG,EAAE,CAAA;QACzB,IAAI,CAAC,aAAa,GAAG,EAAE,CAAA;QACvB,IAAI,CAAC,aAAa,GAAG,EAAE,CAAA;IACxB,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,OAA0B;QACpC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;YACpC,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAA;QACvE,CAAC;QACD,MAAM,IAAI,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAA;QAC3C,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IAC/B,CAAC;IAED,SAAS,CAAC,OAA6C;QACtD,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IACnC,CAAC;IAED,OAAO,CAAC,OAAmB;QAC1B,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IACjC,CAAC;IAED,OAAO,CAAC,OAA+B;QACtC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IACjC,CAAC;IAED,WAAW;QACV,OAAO,IAAI,CAAC,SAAS,CAAA;IACtB,CAAC;IAEO,aAAa;QACpB,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QACrC,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,CAAA;QAE/B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YAC1B,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAA;YAC3B,IAAI,CAAC,OAAO;gBAAE,SAAQ;YACtB,IAAI,CAAC;gBACJ,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAsB,CAAA;gBACxD,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,eAAe;oBAAE,OAAO,CAAC,OAAO,CAAC,CAAA;YAC7D,CAAC;YAAC,MAAM,CAAC;gBACR,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,6CAA6C,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAA;YACpF,CAAC;QACF,CAAC;IACF,CAAC;CACD"}
|
|
@@ -205,4 +205,44 @@ describe('a workspace stored before it had a status', () => {
|
|
|
205
205
|
expect(archived.status).toBe('archived');
|
|
206
206
|
});
|
|
207
207
|
});
|
|
208
|
+
describe('a store that cannot answer the precondition', () => {
|
|
209
|
+
/**
|
|
210
|
+
* `listSessionsByProject` is optional on `SessionStore` so a host's own
|
|
211
|
+
* store keeps compiling. The archive path read it as
|
|
212
|
+
* `(await store.listSessionsByProject?.(...)) ?? []`, which turned "this
|
|
213
|
+
* store cannot tell me what is running here" into "nothing is running
|
|
214
|
+
* here" — so a host with such a store closed a workspace over live
|
|
215
|
+
* sessions and got a success back. The refusal that the whole feature is
|
|
216
|
+
* about was skippable by not implementing one method.
|
|
217
|
+
*/
|
|
218
|
+
function storeWithoutTheListing(inner) {
|
|
219
|
+
const partial = {};
|
|
220
|
+
for (const key of [
|
|
221
|
+
'getProject',
|
|
222
|
+
'createProject',
|
|
223
|
+
'createSession',
|
|
224
|
+
'getSession',
|
|
225
|
+
'updateSession',
|
|
226
|
+
'setProjectStatus',
|
|
227
|
+
]) {
|
|
228
|
+
const fn = inner[key];
|
|
229
|
+
if (typeof fn === 'function')
|
|
230
|
+
partial[key] = fn.bind(inner);
|
|
231
|
+
}
|
|
232
|
+
return partial;
|
|
233
|
+
}
|
|
234
|
+
it('refuses to archive rather than assuming the workspace is empty', async () => {
|
|
235
|
+
const h = await harness();
|
|
236
|
+
await h.store.updateSession({ ...h.parentSession, status: 'active' }, TENANT);
|
|
237
|
+
const blind = new ProjectManager({ store: storeWithoutTheListing(h.store) });
|
|
238
|
+
await expect(blind.archive(h.project.id, TENANT)).rejects.toThrow(/listSessionsByProject/);
|
|
239
|
+
});
|
|
240
|
+
it('leaves the workspace open after refusing', async () => {
|
|
241
|
+
// The refusal has to happen before the write, or it is only a message.
|
|
242
|
+
const h = await harness();
|
|
243
|
+
const blind = new ProjectManager({ store: storeWithoutTheListing(h.store) });
|
|
244
|
+
await blind.archive(h.project.id, TENANT).catch(() => undefined);
|
|
245
|
+
expect((await h.store.getProject(h.project.id, TENANT))?.status).toBe('open');
|
|
246
|
+
});
|
|
247
|
+
});
|
|
208
248
|
//# sourceMappingURL=a-closed-workspace-takes-no-work.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"a-closed-workspace-takes-no-work.test.js","sourceRoot":"","sources":["../../../../src/manager/project/__tests__/a-closed-workspace-takes-no-work.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AACnE,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AAExD,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAA;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,wCAAwC,CAAA;AACtE,OAAO,EACN,kBAAkB,EAClB,oBAAoB,EACpB,iBAAiB,GACjB,MAAM,4BAA4B,CAAA;AACnC,OAAO,EAAE,wBAAwB,EAAE,MAAM,sCAAsC,CAAA;AAC/E,OAAO,EAAE,0BAA0B,EAAE,MAAM,yCAAyC,CAAA;AACpF,OAAO,EAAE,wBAAwB,EAAE,MAAM,wCAAwC,CAAA;AACjF,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAA;AACjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAA;AACvE,OAAO,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAA;
|
|
1
|
+
{"version":3,"file":"a-closed-workspace-takes-no-work.test.js","sourceRoot":"","sources":["../../../../src/manager/project/__tests__/a-closed-workspace-takes-no-work.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AACnE,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AAExD,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAA;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,wCAAwC,CAAA;AACtE,OAAO,EACN,kBAAkB,EAClB,oBAAoB,EACpB,iBAAiB,GACjB,MAAM,4BAA4B,CAAA;AACnC,OAAO,EAAE,wBAAwB,EAAE,MAAM,sCAAsC,CAAA;AAC/E,OAAO,EAAE,0BAA0B,EAAE,MAAM,yCAAyC,CAAA;AACpF,OAAO,EAAE,wBAAwB,EAAE,MAAM,wCAAwC,CAAA;AACjF,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAA;AACjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAA;AACvE,OAAO,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAA;AAQrE,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAA;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAA;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEhD;;;;;;;;;;;;GAYG;AAEH,MAAM,MAAM,GAAG,WAAuB,CAAA;AAEtC,SAAS,WAAW;IACnB,OAAO;QACN,IAAI,EAAE,UAAU;QAChB,QAAQ,EAAE;YACT,EAAE,EAAE,QAAQ;YACZ,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,OAAO;YAChB,QAAQ,EAAE,SAAS;YACnB,WAAW,EAAE,cAAc;YAC3B,IAAI,EAAE,UAAU;YAChB,YAAY,EAAE,EAAE;SAChB;QACD,KAAK,CAAC,GAAG;YACR,OAAO;gBACN,KAAK,EAAE,WAAW;gBAClB,MAAM,EAAE,WAAW;gBACnB,MAAM,EAAE,IAAI;gBACZ,KAAK,EAAE,EAAE,GAAG,iBAAiB,EAAE;gBAC/B,IAAI,EAAE,EAAE,GAAG,SAAS,EAAE;gBACtB,UAAU,EAAE,CAAC;gBACb,UAAU,EAAE,CAAC;gBACb,QAAQ,EAAE,EAAE;aACO,CAAA;QACrB,CAAC;QACD,KAAK,CAAC,MAAM,KAAI,CAAC;QACjB,eAAe;YACd,OAAO,EAAW,CAAA;QACnB,CAAC;KACqD,CAAA;AACxD,CAAC;AAED,SAAS,UAAU;IAClB,OAAO;QACN,IAAI,EAAE;YACL,EAAE,EAAE,QAAQ;YACZ,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,OAAO;YAChB,QAAQ,EAAE,SAAS;YACnB,WAAW,EAAE,UAAU;YACvB,KAAK,EAAE,EAAE;YACT,QAAQ,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE;SAC/C;QACD,UAAU,EAAE,WAAW,EAAE;KACN,CAAA;AACrB,CAAC;AAED,kFAAkF;AAClF,KAAK,UAAU,OAAO;IACrB,MAAM,KAAK,GAAG,IAAI,oBAAoB,EAAE,CAAA;IACxC,MAAM,WAAW,GAAG,IAAI,mBAAmB,EAAE,CAAA;IAC7C,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,aAAa,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,MAAM,CAAC,CAAA;IAClF,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,YAAY,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,MAAM,CAAC,CAAA;IAC5F,MAAM,WAAW,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,KAAgB,EAAE,QAAQ,EAAE,MAAM,EAAW,CAAA;IAC3F,MAAM,aAAa,GAAG,MAAM,KAAK,CAAC,aAAa,CAC9C,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,EAAE,YAAY,EAAE,WAAW,EAAE,EACzE,MAAM,CACN,CAAA;IAED,MAAM,QAAQ,GAAG,IAAI,aAAa,EAAE,CAAA;IACpC,QAAQ,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAA;IAE/B,IAAI,CAAC,GAAG,CAAC,CAAA;IACT,MAAM,OAAO,GAAG,IAAI,YAAY,CAAC,QAAQ,EAAE,SAAS,EAAE;QACrD,YAAY,EAAE,KAAK;QACnB,aAAa,EAAE,IAAI,aAAa,CAAC,EAAE,WAAW,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC;QACtE,iBAAiB,EAAE,IAAI,wBAAwB,EAAE;QACjD,QAAQ,EAAE,IAAI,wBAAwB,CAAC,KAAK,CAAC;QAC7C,mBAAmB,EAAE,IAAI,0BAA0B,CAAC;YACnD,KAAK;YACL,iBAAiB,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,EAAe;SAClD,CAAC;KACF,CAAC,CAAA;IAEF,MAAM,OAAO,GAAG;QACf,WAAW,EAAE,YAAqB;QAClC,aAAa,EAAE,KAAK;QACpB,qBAAqB,EAAE,IAAI,eAAe,EAAE;QAC5C,KAAK,EAAE,CAAC;QACR,aAAa,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE;QACrD,QAAQ,EAAE,MAAM;QAChB,QAAQ,EAAE,MAAM,CAAC,EAAE;QACnB,SAAS,EAAE,aAAa,CAAC,EAAE;QAC3B,SAAS,EAAE,OAAO,CAAC,EAAE;QACrB,WAAW;KACS,CAAA;IAErB,MAAM,KAAK,GAAG,GAAG,EAAE,CAClB,OAAO,CAAC,WAAW,CAClB;QACC,OAAO,EAAE,QAAQ;QACjB,KAAK,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,gBAAgB,EAAE,MAAM,EAAW;QAC1D,eAAe,EAAE,aAAa,CAAC,EAAE;QACjC,QAAQ,EAAE,MAAM;KACP,EACV,OAAO,CACP,CAAA;IAEF,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,IAAI,cAAc,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAA;AACzF,CAAC;AAED,QAAQ,CAAC,sCAAsC,EAAE,GAAG,EAAE;IACrD,EAAE,CAAC,oCAAoC,EAAE,KAAK,IAAI,EAAE;QACnD,kEAAkE;QAClE,2CAA2C;QAC3C,MAAM,CAAC,GAAG,MAAM,OAAO,EAAE,CAAA;QAEzB,MAAM,MAAM,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAA;IAC/C,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,yEAAyE,EAAE,KAAK,IAAI,EAAE;QACxF,MAAM,CAAC,GAAG,MAAM,OAAO,EAAE,CAAA;QACzB,MAAM,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,EAAE,MAAM,CAAC,CAAA;QAE9C,MAAM,MAAM,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,cAAc,CAAC,kBAAkB,CAAC,CAAA;IACnE,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,gCAAgC,EAAE,KAAK,IAAI,EAAE;QAC/C,iEAAiE;QACjE,MAAM,CAAC,GAAG,MAAM,OAAO,EAAE,CAAA;QACzB,MAAM,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,EAAE,MAAM,CAAC,CAAA;QAE9C,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,KAAK,EAAE,CAAC,IAAI,CACjC,GAAG,EAAE,CAAC,IAAI,EACV,CAAC,CAAU,EAAE,EAAE,CAAC,CAAC,CACjB,CAAA;QAED,MAAM,CAAE,KAA4B,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC;YACrD,SAAS,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE;YACvB,EAAE,EAAE,OAAO;SACX,CAAC,CAAA;IACH,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,8CAA8C,EAAE,KAAK,IAAI,EAAE;QAC7D,oEAAoE;QACpE,MAAM,CAAC,GAAG,MAAM,OAAO,EAAE,CAAA;QACzB,MAAM,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,EAAE,MAAM,CAAC,CAAA;QAC9C,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,EAAE,MAAM,CAAC,CAAA;QAE7C,MAAM,MAAM,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAA;IAC/C,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,kEAAkE,EAAE,KAAK,IAAI,EAAE;QACjF,mEAAmE;QACnE,+CAA+C;QAC/C,MAAM,CAAC,GAAG,MAAM,OAAO,EAAE,CAAA;QACzB,MAAM,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,EAAE,GAAG,CAAC,CAAC,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,MAAM,CAAC,CAAA;QAE7E,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC,IAAI,CAChE,GAAG,EAAE,CAAC,IAAI,EACV,CAAC,CAAU,EAAE,EAAE,CAAC,CAAC,CACjB,CAAA;QAED,MAAM,CAAC,KAAK,CAAC,CAAC,cAAc,CAAC,oBAAoB,CAAC,CAAA;QAClD,MAAM,OAAO,GAAI,KAA8B,CAAC,OAAO,CAAA;QACvD,MAAM,CAAC,OAAO,CAAC,qBAAqB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QAC7C,MAAM,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAA;IAChG,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,gDAAgD,EAAE,KAAK,IAAI,EAAE;QAC/D,MAAM,CAAC,GAAG,MAAM,OAAO,EAAE,CAAA;QAEzB,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,EAAE,MAAM,CAAC,CAAA;QAE/D,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;QACxC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;IAClF,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,sDAAsD,EAAE,KAAK,IAAI,EAAE;QACrE,wEAAwE;QACxE,mEAAmE;QACnE,MAAM,CAAC,GAAG,MAAM,OAAO,EAAE,CAAA;QACzB,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,EAAE,MAAM,CAAC,CAAA;QAE5D,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,EAAE,MAAM,CAAC,CAAA;QAE7D,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAA;IACrD,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,2DAA2D,EAAE,KAAK,IAAI,EAAE;QAC1E,qEAAqE;QACrE,mEAAmE;QACnE,MAAM,CAAC,GAAG,MAAM,OAAO,EAAE,CAAA;QACzB,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,EAAE,MAAM,CAAC,CAAA;QAC3D,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAA;QAE9C,MAAM,CAAC,CAAC,KAAK,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC,YAAY,CAAC,CAAA;QAErF,MAAM,MAAM,CACX,CAAC,CAAC,KAAK,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,YAAY,CAAC,CAC3E,CAAC,OAAO,CAAC,cAAc,CAAC,iBAAiB,CAAC,CAAA;IAC5C,CAAC,CAAC,CAAA;AACH,CAAC,CAAC,CAAA;AAEF,QAAQ,CAAC,2CAA2C,EAAE,GAAG,EAAE;IAC1D,MAAM,IAAI,GAAa,EAAE,CAAA;IACzB,SAAS,CAAC,KAAK,IAAI,EAAE;QACpB,MAAM,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAA;QAC3E,IAAI,CAAC,MAAM,GAAG,CAAC,CAAA;IAChB,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,qDAAqD,EAAE,KAAK,IAAI,EAAE;QACpE,sEAAsE;QACtE,uEAAuE;QACvE,qEAAqE;QACrE,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,gBAAgB,CAAC,CAAC,CAAA;QAC5D,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACf,MAAM,KAAK,GAAG,IAAI,gBAAgB,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAA;QACrD,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,aAAa,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,MAAM,CAAC,CAAA;QAElF,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,EAAE,cAAc,CAAC,CAAA;QAC/D,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAA;QACrD,GAAG,CAAC,MAAM,GAAG,SAAS,CAAA;QACtB,GAAG,CAAC,YAAY,GAAG,SAAS,CAAA;QAC5B,MAAM,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,CAAA;QAEnD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,EAAE,MAAM,CAAC,CAAA;QAC3D,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QACrC,MAAM,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QAEtC,MAAM,QAAQ,GAAG,MAAM,IAAI,cAAc,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,EAAE,MAAM,CAAC,CAAA;QAChF,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;IACzC,CAAC,CAAC,CAAA;AACH,CAAC,CAAC,CAAA;AAEF,QAAQ,CAAC,6CAA6C,EAAE,GAAG,EAAE;IAC5D;;;;;;;;OAQG;IACH,SAAS,sBAAsB,CAAC,KAAmB;QAClD,MAAM,OAAO,GAA4B,EAAE,CAAA;QAC3C,KAAK,MAAM,GAAG,IAAI;YACjB,YAAY;YACZ,eAAe;YACf,eAAe;YACf,YAAY;YACZ,eAAe;YACf,kBAAkB;SAClB,EAAE,CAAC;YACH,MAAM,EAAE,GAAI,KAA4C,CAAC,GAAG,CAAC,CAAA;YAC7D,IAAI,OAAO,EAAE,KAAK,UAAU;gBAAE,OAAO,CAAC,GAAG,CAAC,GAAI,EAAmC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QAC9F,CAAC;QACD,OAAO,OAAkC,CAAA;IAC1C,CAAC;IAED,EAAE,CAAC,gEAAgE,EAAE,KAAK,IAAI,EAAE;QAC/E,MAAM,CAAC,GAAG,MAAM,OAAO,EAAE,CAAA;QACzB,MAAM,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,EAAE,GAAG,CAAC,CAAC,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,MAAM,CAAC,CAAA;QAC7E,MAAM,KAAK,GAAG,IAAI,cAAc,CAAC,EAAE,KAAK,EAAE,sBAAsB,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;QAE5E,MAAM,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,uBAAuB,CAAC,CAAA;IAC3F,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,0CAA0C,EAAE,KAAK,IAAI,EAAE;QACzD,uEAAuE;QACvE,MAAM,CAAC,GAAG,MAAM,OAAO,EAAE,CAAA;QACzB,MAAM,KAAK,GAAG,IAAI,cAAc,CAAC,EAAE,KAAK,EAAE,sBAAsB,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;QAE5E,MAAM,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;QAEhE,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;IAC9E,CAAC,CAAC,CAAA;AACH,CAAC,CAAC,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../../src/manager/project/lifecycle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAOH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAA;AACxD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAA;AAE5D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAA;AAC3D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAA;AAgBhE;;;;;;;;;;GAUG;AACH,wBAAsB,kBAAkB,CACvC,KAAK,EAAE,IAAI,CAAC,YAAY,EAAE,YAAY,CAAC,EACvC,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE,QAAQ,EAClB,EAAE,EAAE,MAAM,GACR,OAAO,CAAC,OAAO,CAAC,CASlB;AAED,MAAM,WAAW,kBAAkB;IAClC,KAAK,EAAE,YAAY,CAAA;CACnB;AAED,qBAAa,cAAc;IACd,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,kBAAkB;IAErD,sCAAsC;IACtC,WAAW,CAAC,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,SAAiB,GAAG,OAAO,CAAC,OAAO,CAAC;IAI5F;;;;;;;;;;;;;OAaG;IACG,OAAO,CAAC,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../../src/manager/project/lifecycle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAOH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAA;AACxD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAA;AAE5D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAA;AAC3D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAA;AAgBhE;;;;;;;;;;GAUG;AACH,wBAAsB,kBAAkB,CACvC,KAAK,EAAE,IAAI,CAAC,YAAY,EAAE,YAAY,CAAC,EACvC,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE,QAAQ,EAClB,EAAE,EAAE,MAAM,GACR,OAAO,CAAC,OAAO,CAAC,CASlB;AAED,MAAM,WAAW,kBAAkB;IAClC,KAAK,EAAE,YAAY,CAAA;CACnB;AAED,qBAAa,cAAc;IACd,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,kBAAkB;IAErD,sCAAsC;IACtC,WAAW,CAAC,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,SAAiB,GAAG,OAAO,CAAC,OAAO,CAAC;IAI5F;;;;;;;;;;;;;OAaG;IACG,OAAO,CAAC,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;IA0CzE;;;;OAIG;IACG,MAAM,CAAC,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;YAS1D,KAAK;CAwBnB"}
|
|
@@ -79,7 +79,23 @@ export class ProjectManager {
|
|
|
79
79
|
if (!project) {
|
|
80
80
|
throw new Error(`Project ${projectId} not found for tenant ${tenantId} — archive rejected`);
|
|
81
81
|
}
|
|
82
|
-
|
|
82
|
+
// An unanswerable precondition is a refusal, not an empty answer.
|
|
83
|
+
//
|
|
84
|
+
// This read used to be `?? []`, which turned "this store cannot tell me
|
|
85
|
+
// what is running here" into "nothing is running here": a store without
|
|
86
|
+
// the optional method archived the workspace over live sessions and
|
|
87
|
+
// reported success. The whole point of the check is that archival does
|
|
88
|
+
// not cascade over running work, and the check was skippable by any
|
|
89
|
+
// store that had not implemented one optional method.
|
|
90
|
+
//
|
|
91
|
+
// Optional on the interface protects implementors — a host's own store
|
|
92
|
+
// should not stop compiling because the SDK grew a method. It cannot
|
|
93
|
+
// also mean a safety precondition silently passes.
|
|
94
|
+
const listByProject = this.deps.store.listSessionsByProject;
|
|
95
|
+
if (!listByProject) {
|
|
96
|
+
throw new Error(`Project ${projectId} cannot be archived: this SessionStore does not implement listSessionsByProject, so whether a session is still running in the workspace cannot be established. Implement it, or settle and verify the sessions through your own path before closing the workspace.`);
|
|
97
|
+
}
|
|
98
|
+
const sessions = await listByProject.call(this.deps.store, projectId, tenantId);
|
|
83
99
|
const blocking = sessions.filter((s) => ARCHIVAL_BLOCKING_STATUSES.has(s.status));
|
|
84
100
|
if (blocking.length > 0) {
|
|
85
101
|
throw new ProjectNotEmptyError({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../../../src/manager/project/lifecycle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EACN,8BAA8B,EAC9B,kBAAkB,EAClB,oBAAoB,GACpB,MAAM,yBAAyB,CAAA;AAOhC;;;;;;GAMG;AACH,MAAM,0BAA0B,GAA+B,IAAI,GAAG,CAAC;IACtE,QAAQ;IACR,QAAQ;IACR,gBAAgB;IAChB,eAAe;CACf,CAAC,CAAA;AAEF;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACvC,KAAuC,EACvC,SAAoB,EACpB,QAAkB,EAClB,EAAU;IAEV,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,UAAU,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAA;IAC3D,IAAI,CAAC,OAAO,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,WAAW,SAAS,yBAAyB,QAAQ,MAAM,EAAE,WAAW,CAAC,CAAA;IAC1F,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;QACnC,MAAM,IAAI,kBAAkB,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,CAAA;IAChD,CAAC;IACD,OAAO,OAAO,CAAA;AACf,CAAC;AAMD,MAAM,OAAO,cAAc;IACG;IAA7B,YAA6B,IAAwB;QAAxB,SAAI,GAAJ,IAAI,CAAoB;IAAG,CAAC;IAEzD,sCAAsC;IACtC,WAAW,CAAC,SAAoB,EAAE,QAAkB,EAAE,EAAE,GAAG,cAAc;QACxE,OAAO,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAA;IACpE,CAAC;IAED;;;;;;;;;;;;;OAaG;IACH,KAAK,CAAC,OAAO,CAAC,SAAoB,EAAE,QAAkB;QACrD,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAA;QACrE,IAAI,CAAC,OAAO,EAAE,CAAC;YACd,MAAM,IAAI,KAAK,CAAC,WAAW,SAAS,yBAAyB,QAAQ,qBAAqB,CAAC,CAAA;QAC5F,CAAC;QAED,
|
|
1
|
+
{"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../../../src/manager/project/lifecycle.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import type { ChildProcess } from 'node:child_process'
|
|
2
|
+
import { describe, expect, it } from 'vitest'
|
|
3
|
+
|
|
4
|
+
import { StdioTransport } from '../stdio.js'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* `close()` sent SIGTERM and returned without waiting, so a resolved close
|
|
8
|
+
* meant "the signal is on its way", not "the child is gone". A caller that
|
|
9
|
+
* closed a transport and then deleted the child's working directory raced the
|
|
10
|
+
* exit and saw EBUSY — reported from a real integration, not inferred.
|
|
11
|
+
*
|
|
12
|
+
* A close that does not mean closed makes every teardown built on top of it a
|
|
13
|
+
* guess, and the guess is only wrong sometimes, which is the worst kind.
|
|
14
|
+
*
|
|
15
|
+
* These assert on the operating system rather than on the transport's own
|
|
16
|
+
* state: `process.kill(pid, 0)` throws ESRCH once the pid is gone, so the
|
|
17
|
+
* assertion cannot pass by the transport merely believing it closed.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/** The child the transport spawned, read before `close()` clears the field. */
|
|
21
|
+
function childOf(transport: StdioTransport): ChildProcess {
|
|
22
|
+
const child = (transport as unknown as { process: ChildProcess | null }).process
|
|
23
|
+
if (!child) throw new Error('transport spawned no process')
|
|
24
|
+
return child
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function pidOf(transport: StdioTransport): number {
|
|
28
|
+
const pid = childOf(transport).pid
|
|
29
|
+
if (pid === undefined) throw new Error('transport spawned no process')
|
|
30
|
+
return pid
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function isAlive(pid: number): boolean {
|
|
34
|
+
try {
|
|
35
|
+
process.kill(pid, 0)
|
|
36
|
+
return true
|
|
37
|
+
} catch {
|
|
38
|
+
return false
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
describe('a close that means closed', () => {
|
|
43
|
+
it('does not resolve until the child is actually gone', async () => {
|
|
44
|
+
const transport = new StdioTransport({
|
|
45
|
+
type: 'stdio',
|
|
46
|
+
command: process.execPath,
|
|
47
|
+
args: ['-e', 'setInterval(() => {}, 1000)'],
|
|
48
|
+
})
|
|
49
|
+
await transport.connect()
|
|
50
|
+
const child = childOf(transport)
|
|
51
|
+
const pid = pidOf(transport)
|
|
52
|
+
expect(isAlive(pid)).toBe(true)
|
|
53
|
+
|
|
54
|
+
await transport.close()
|
|
55
|
+
|
|
56
|
+
// The reaped-ness of the child, not the liveness of the pid.
|
|
57
|
+
//
|
|
58
|
+
// `isAlive(pid)` alone is sound about the wrong thing: on Windows
|
|
59
|
+
// `kill('SIGTERM')` terminates the process synchronously, so the pid is
|
|
60
|
+
// already gone by the next line whether or not `close()` waited — the
|
|
61
|
+
// assertion passed under a deliberately reintroduced fire-and-forget
|
|
62
|
+
// kill, which is how this was caught. Node fills `exitCode`/`signalCode`
|
|
63
|
+
// only when it reaps the child and emits `exit`, a later tick, so these
|
|
64
|
+
// are non-null here exactly when `close()` awaited that event.
|
|
65
|
+
expect(child.exitCode !== null || child.signalCode !== null).toBe(true)
|
|
66
|
+
expect(isAlive(pid)).toBe(false)
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
it('returns rather than hanging when the command does not exist', async () => {
|
|
70
|
+
// A spawn that fails emits `error` and never `exit`. Waiting on `exit`
|
|
71
|
+
// alone would turn a bad command into a shutdown that never completes.
|
|
72
|
+
const transport = new StdioTransport({
|
|
73
|
+
type: 'stdio',
|
|
74
|
+
command: 'namzu-no-such-command-exists-here',
|
|
75
|
+
args: [],
|
|
76
|
+
})
|
|
77
|
+
await transport.connect()
|
|
78
|
+
|
|
79
|
+
await expect(transport.close()).resolves.toBeUndefined()
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it('is safe to call twice', async () => {
|
|
83
|
+
const transport = new StdioTransport({
|
|
84
|
+
type: 'stdio',
|
|
85
|
+
command: process.execPath,
|
|
86
|
+
args: ['-e', 'setInterval(() => {}, 1000)'],
|
|
87
|
+
})
|
|
88
|
+
await transport.connect()
|
|
89
|
+
const pid = pidOf(transport)
|
|
90
|
+
|
|
91
|
+
await transport.close()
|
|
92
|
+
await transport.close()
|
|
93
|
+
|
|
94
|
+
expect(isAlive(pid)).toBe(false)
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
it('reports the exit to the handler registered for that session', async () => {
|
|
98
|
+
// The waiting must not swallow the notification the client depends on
|
|
99
|
+
// to learn the session ended.
|
|
100
|
+
const transport = new StdioTransport({
|
|
101
|
+
type: 'stdio',
|
|
102
|
+
command: process.execPath,
|
|
103
|
+
args: ['-e', 'setInterval(() => {}, 1000)'],
|
|
104
|
+
})
|
|
105
|
+
await transport.connect()
|
|
106
|
+
let closes = 0
|
|
107
|
+
transport.onClose(() => {
|
|
108
|
+
closes++
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
await transport.close()
|
|
112
|
+
// `close` on a ChildProcess follows `exit` by a tick once the stdio
|
|
113
|
+
// streams drain; give the loop that tick rather than asserting on a
|
|
114
|
+
// race.
|
|
115
|
+
await new Promise((resolve) => setTimeout(resolve, 50))
|
|
116
|
+
|
|
117
|
+
expect(closes).toBe(1)
|
|
118
|
+
})
|
|
119
|
+
})
|
|
@@ -6,6 +6,13 @@ import type {
|
|
|
6
6
|
} from '../../types/connector/index.js'
|
|
7
7
|
import { type Logger, getRootLogger } from '../../utils/logger.js'
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* How long a child gets to honour SIGTERM before SIGKILL. Two seconds is
|
|
11
|
+
* long enough for a server flushing a response and short enough that a
|
|
12
|
+
* shutdown does not read as a hang.
|
|
13
|
+
*/
|
|
14
|
+
const TERMINATE_GRACE_MS = 2_000
|
|
15
|
+
|
|
9
16
|
export class StdioTransport implements MCPTransport {
|
|
10
17
|
private process: ChildProcess | null = null
|
|
11
18
|
private messageHandlers: Array<(message: MCPJsonRpcMessage) => void> = []
|
|
@@ -71,9 +78,39 @@ export class StdioTransport implements MCPTransport {
|
|
|
71
78
|
}
|
|
72
79
|
this.connected = false
|
|
73
80
|
this.exitPending = true
|
|
74
|
-
this.process
|
|
81
|
+
const child = this.process
|
|
75
82
|
this.process = null
|
|
76
83
|
this.buffer = ''
|
|
84
|
+
|
|
85
|
+
// Resolve when the child is actually gone, not when the signal was
|
|
86
|
+
// sent. `kill()` returns as soon as the signal is delivered, so an
|
|
87
|
+
// awaited `close()` meant only "SIGTERM is on its way" — a caller that
|
|
88
|
+
// closed and then deleted the child's working directory raced the exit
|
|
89
|
+
// and saw EBUSY. A close that does not mean closed makes every
|
|
90
|
+
// teardown after it a guess.
|
|
91
|
+
//
|
|
92
|
+
// A spawn that never produced a process emits `error` and no `exit`,
|
|
93
|
+
// so both settle this, and two timers make a hang impossible: the
|
|
94
|
+
// first escalates to SIGKILL for a child ignoring SIGTERM, the second
|
|
95
|
+
// gives up waiting. Neither holds the event loop open.
|
|
96
|
+
if (child.pid === undefined) return
|
|
97
|
+
await new Promise<void>((resolve) => {
|
|
98
|
+
let settled = false
|
|
99
|
+
const finish = (): void => {
|
|
100
|
+
if (settled) return
|
|
101
|
+
settled = true
|
|
102
|
+
clearTimeout(escalate)
|
|
103
|
+
clearTimeout(giveUp)
|
|
104
|
+
resolve()
|
|
105
|
+
}
|
|
106
|
+
child.once('exit', finish)
|
|
107
|
+
child.once('error', finish)
|
|
108
|
+
child.kill('SIGTERM')
|
|
109
|
+
const escalate = setTimeout(() => child.kill('SIGKILL'), TERMINATE_GRACE_MS)
|
|
110
|
+
const giveUp = setTimeout(finish, TERMINATE_GRACE_MS * 2)
|
|
111
|
+
escalate.unref?.()
|
|
112
|
+
giveUp.unref?.()
|
|
113
|
+
})
|
|
77
114
|
// The handlers deliberately survive this call. `process.on('close')`
|
|
78
115
|
// fires them when the process actually exits, and dropping them here
|
|
79
116
|
// would leave `MCPClient` believing it is still connected — so its next
|
|
@@ -22,6 +22,7 @@ import type { AgentDefinition } from '../../../types/agent/factory.js'
|
|
|
22
22
|
import type { AgentTaskContext } from '../../../types/agent/task.js'
|
|
23
23
|
import type { AgentId, TenantId } from '../../../types/ids/index.js'
|
|
24
24
|
import type { SummaryId } from '../../../types/session/ids.js'
|
|
25
|
+
import type { SessionStore } from '../../../types/session/store.js'
|
|
25
26
|
import { ZERO_COST } from '../../../utils/cost.js'
|
|
26
27
|
import { AgentManager } from '../../agent/lifecycle.js'
|
|
27
28
|
import { ThreadManager } from '../../thread/lifecycle.js'
|
|
@@ -266,3 +267,48 @@ describe('a workspace stored before it had a status', () => {
|
|
|
266
267
|
expect(archived.status).toBe('archived')
|
|
267
268
|
})
|
|
268
269
|
})
|
|
270
|
+
|
|
271
|
+
describe('a store that cannot answer the precondition', () => {
|
|
272
|
+
/**
|
|
273
|
+
* `listSessionsByProject` is optional on `SessionStore` so a host's own
|
|
274
|
+
* store keeps compiling. The archive path read it as
|
|
275
|
+
* `(await store.listSessionsByProject?.(...)) ?? []`, which turned "this
|
|
276
|
+
* store cannot tell me what is running here" into "nothing is running
|
|
277
|
+
* here" — so a host with such a store closed a workspace over live
|
|
278
|
+
* sessions and got a success back. The refusal that the whole feature is
|
|
279
|
+
* about was skippable by not implementing one method.
|
|
280
|
+
*/
|
|
281
|
+
function storeWithoutTheListing(inner: SessionStore): SessionStore {
|
|
282
|
+
const partial: Record<string, unknown> = {}
|
|
283
|
+
for (const key of [
|
|
284
|
+
'getProject',
|
|
285
|
+
'createProject',
|
|
286
|
+
'createSession',
|
|
287
|
+
'getSession',
|
|
288
|
+
'updateSession',
|
|
289
|
+
'setProjectStatus',
|
|
290
|
+
]) {
|
|
291
|
+
const fn = (inner as unknown as Record<string, unknown>)[key]
|
|
292
|
+
if (typeof fn === 'function') partial[key] = (fn as (...a: unknown[]) => unknown).bind(inner)
|
|
293
|
+
}
|
|
294
|
+
return partial as unknown as SessionStore
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
it('refuses to archive rather than assuming the workspace is empty', async () => {
|
|
298
|
+
const h = await harness()
|
|
299
|
+
await h.store.updateSession({ ...h.parentSession, status: 'active' }, TENANT)
|
|
300
|
+
const blind = new ProjectManager({ store: storeWithoutTheListing(h.store) })
|
|
301
|
+
|
|
302
|
+
await expect(blind.archive(h.project.id, TENANT)).rejects.toThrow(/listSessionsByProject/)
|
|
303
|
+
})
|
|
304
|
+
|
|
305
|
+
it('leaves the workspace open after refusing', async () => {
|
|
306
|
+
// The refusal has to happen before the write, or it is only a message.
|
|
307
|
+
const h = await harness()
|
|
308
|
+
const blind = new ProjectManager({ store: storeWithoutTheListing(h.store) })
|
|
309
|
+
|
|
310
|
+
await blind.archive(h.project.id, TENANT).catch(() => undefined)
|
|
311
|
+
|
|
312
|
+
expect((await h.store.getProject(h.project.id, TENANT))?.status).toBe('open')
|
|
313
|
+
})
|
|
314
|
+
})
|
|
@@ -101,7 +101,25 @@ export class ProjectManager {
|
|
|
101
101
|
throw new Error(`Project ${projectId} not found for tenant ${tenantId} — archive rejected`)
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
|
|
104
|
+
// An unanswerable precondition is a refusal, not an empty answer.
|
|
105
|
+
//
|
|
106
|
+
// This read used to be `?? []`, which turned "this store cannot tell me
|
|
107
|
+
// what is running here" into "nothing is running here": a store without
|
|
108
|
+
// the optional method archived the workspace over live sessions and
|
|
109
|
+
// reported success. The whole point of the check is that archival does
|
|
110
|
+
// not cascade over running work, and the check was skippable by any
|
|
111
|
+
// store that had not implemented one optional method.
|
|
112
|
+
//
|
|
113
|
+
// Optional on the interface protects implementors — a host's own store
|
|
114
|
+
// should not stop compiling because the SDK grew a method. It cannot
|
|
115
|
+
// also mean a safety precondition silently passes.
|
|
116
|
+
const listByProject = this.deps.store.listSessionsByProject
|
|
117
|
+
if (!listByProject) {
|
|
118
|
+
throw new Error(
|
|
119
|
+
`Project ${projectId} cannot be archived: this SessionStore does not implement listSessionsByProject, so whether a session is still running in the workspace cannot be established. Implement it, or settle and verify the sessions through your own path before closing the workspace.`,
|
|
120
|
+
)
|
|
121
|
+
}
|
|
122
|
+
const sessions = await listByProject.call(this.deps.store, projectId, tenantId)
|
|
105
123
|
const blocking = sessions.filter((s) => ARCHIVAL_BLOCKING_STATUSES.has(s.status))
|
|
106
124
|
if (blocking.length > 0) {
|
|
107
125
|
throw new ProjectNotEmptyError({
|