@adecore/agents 0.38.0 → 0.40.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/dist/async.d.ts +5 -0
- package/dist/async.js +13 -5
- package/dist/chat/acp-protocol.js +3 -5
- package/dist/chat/chat-core.d.ts +1 -0
- package/dist/chat/chat-core.js +9 -2
- package/dist/chat/chat-process.d.ts +1 -0
- package/dist/chat/chat-process.js +1 -1
- package/dist/chat/chat-session.d.ts +2 -0
- package/dist/chat/chat-session.js +9 -10
- package/dist/chat/claude-protocol.js +1 -6
- package/dist/chat/claude-transcript.js +1 -3
- package/dist/chat/codex-protocol.js +1 -6
- package/dist/chat/projector.js +1 -3
- package/dist/chat/request-summary.js +1 -3
- package/dist/coded-error.js +1 -5
- package/dist/context/argv.js +1 -2
- package/dist/context/verb.js +1 -3
- package/dist/error-text.d.ts +1 -0
- package/dist/error-text.js +4 -0
- package/dist/fields.d.ts +3 -0
- package/dist/fields.js +12 -0
- package/dist/fs.d.ts +1 -0
- package/dist/fs.js +8 -4
- package/dist/lineage.js +11 -5
- package/dist/messages/notice-store.js +3 -4
- package/dist/providers/accounts/acp-account.js +3 -20
- package/dist/providers/accounts/service.d.ts +1 -2
- package/dist/providers/accounts/service.js +5 -9
- package/dist/providers/accounts/status.js +1 -3
- package/dist/providers/acp-agent-state.js +1 -3
- package/dist/providers/claude.js +0 -1
- package/dist/providers/codex.js +0 -1
- package/dist/providers/detect.js +1 -1
- package/dist/providers/shell-commands.js +1 -2
- package/dist/run-process.js +1 -3
- package/dist/skills.d.ts +1 -1
- package/dist/skills.js +5 -8
- package/dist/tasks/background-limit.js +3 -2
- package/dist/tasks/task-coordinator.js +11 -17
- package/dist/testing/watch.js +0 -1
- package/dist/usage/limits/monitor.js +1 -2
- package/dist/usage/readers/claude.js +3 -10
- package/dist/usage/readers/codex.js +2 -9
- package/dist/usage/readers/grok.js +3 -10
- package/dist/usage/readers/host-log.js +3 -10
- package/dist/usage/record.d.ts +2 -0
- package/dist/usage/record.js +13 -0
- package/package.json +6 -6
- package/src/async.ts +23 -5
- package/src/chat/acp-backend.ts +121 -0
- package/src/chat/acp-protocol.ts +172 -6
- package/src/chat/attachment-store.ts +21 -0
- package/src/chat/backend.ts +0 -2
- package/src/chat/bookmark-store.ts +23 -0
- package/src/chat/chat-core.ts +218 -7
- package/src/chat/chat-log.ts +19 -0
- package/src/chat/chat-process.ts +52 -1
- package/src/chat/chat-session.ts +414 -13
- package/src/chat/chat-store.ts +50 -0
- package/src/chat/chat-title.ts +9 -0
- package/src/chat/claude-backend.ts +50 -0
- package/src/chat/claude-protocol.ts +155 -8
- package/src/chat/claude-title.ts +10 -0
- package/src/chat/claude-transcript.ts +47 -4
- package/src/chat/codex-backend.ts +131 -0
- package/src/chat/codex-protocol.ts +180 -8
- package/src/chat/codex-thread.ts +23 -0
- package/src/chat/composer-preferences.ts +6 -0
- package/src/chat/context-breakdown.ts +11 -0
- package/src/chat/delta-coalescer.ts +8 -0
- package/src/chat/external-sessions.ts +28 -0
- package/src/chat/generated-image.ts +25 -0
- package/src/chat/index.ts +5 -0
- package/src/chat/input.ts +13 -0
- package/src/chat/json-rpc-transport.ts +28 -0
- package/src/chat/limit-resume.ts +13 -0
- package/src/chat/projector.ts +160 -4
- package/src/chat/readable-text.ts +3 -0
- package/src/chat/request-summary.ts +18 -4
- package/src/chat/subagent-projection.ts +3 -0
- package/src/chat/subagent-reader.ts +76 -0
- package/src/chat/subagent-settlement.ts +24 -0
- package/src/chat/thread-search.ts +13 -0
- package/src/chat/thread.ts +31 -0
- package/src/chat/ui-queries.ts +122 -0
- package/src/chat/ui-replies.ts +34 -0
- package/src/chat/visual-store.ts +49 -0
- package/src/chat/wake-chat.ts +3 -0
- package/src/client-sinks.ts +2 -0
- package/src/coded-error.ts +1 -5
- package/src/context/argv.ts +18 -2
- package/src/context/index.ts +1 -0
- package/src/context/refusal.ts +3 -0
- package/src/context/verb.ts +32 -4
- package/src/coordination/index.ts +10 -0
- package/src/error-text.ts +7 -0
- package/src/fields.ts +14 -0
- package/src/fs.ts +20 -4
- package/src/host/agent-host.ts +20 -0
- package/src/host/environment.ts +6 -0
- package/src/host/handlers.ts +18 -0
- package/src/host/memory-port.ts +4 -0
- package/src/host/wiring.ts +10 -0
- package/src/index.ts +1 -0
- package/src/lineage.ts +24 -5
- package/src/messages/deliver-message.ts +6 -0
- package/src/messages/deliver-notice.ts +13 -0
- package/src/messages/notice-store.ts +16 -4
- package/src/modes.ts +1 -0
- package/src/outbox/outbox-worker.ts +31 -0
- package/src/outbox/outbox.ts +5 -0
- package/src/providers/accounts/accounts.ts +36 -0
- package/src/providers/accounts/acp-account.ts +22 -27
- package/src/providers/accounts/launch.ts +4 -0
- package/src/providers/accounts/service.ts +110 -12
- package/src/providers/accounts/shadow-home.ts +10 -0
- package/src/providers/accounts/status.ts +13 -4
- package/src/providers/accounts/store.ts +25 -0
- package/src/providers/accounts/variables.ts +11 -1
- package/src/providers/acp-agent-state.ts +24 -3
- package/src/providers/acp-provider.ts +5 -0
- package/src/providers/acp-usage-log.ts +2 -0
- package/src/providers/catalog.ts +17 -0
- package/src/providers/claude.ts +10 -1
- package/src/providers/codex-rules.ts +3 -0
- package/src/providers/codex.ts +2 -1
- package/src/providers/detect.ts +6 -1
- package/src/providers/provider.ts +1 -1
- package/src/providers/registry.ts +18 -2
- package/src/providers/shell-commands.ts +37 -2
- package/src/record-directory.ts +19 -0
- package/src/run-process.ts +24 -3
- package/src/serializer.ts +3 -0
- package/src/skills.ts +73 -10
- package/src/tasks/background-limit.ts +9 -2
- package/src/tasks/end-children.ts +9 -0
- package/src/tasks/give-task.ts +8 -0
- package/src/tasks/task-coordinator.ts +81 -17
- package/src/tasks/task-store.ts +28 -0
- package/src/tasks/waiting-child.ts +21 -0
- package/src/tasks/wake-parent.ts +26 -0
- package/src/tasks/wiring.ts +22 -0
- package/src/testing/accounts.ts +2 -0
- package/src/testing/fake-acp-capture.ts +16 -0
- package/src/testing/fake-acp.ts +66 -0
- package/src/testing/fake-claude.ts +177 -0
- package/src/testing/fake-cli.ts +29 -0
- package/src/testing/fake-codex.ts +122 -0
- package/src/testing/manual-clock.ts +2 -0
- package/src/testing/watch.ts +7 -1
- package/src/title-file.ts +12 -0
- package/src/usage/accounts.ts +6 -0
- package/src/usage/aggregate.ts +19 -0
- package/src/usage/exchange.ts +12 -0
- package/src/usage/index-file.ts +27 -0
- package/src/usage/limits/monitor.ts +31 -3
- package/src/usage/limits/normalize.ts +30 -0
- package/src/usage/limits/probe.ts +17 -0
- package/src/usage/pricing.ts +35 -0
- package/src/usage/projects.ts +8 -0
- package/src/usage/readers/claude.ts +12 -9
- package/src/usage/readers/codex.ts +40 -8
- package/src/usage/readers/grok.ts +17 -9
- package/src/usage/readers/host-log.ts +9 -9
- package/src/usage/record.ts +22 -0
- package/src/usage/roots.ts +6 -0
- package/src/usage/scanner.ts +61 -0
- package/src/usage/usage-service.ts +14 -0
- package/src/watch-seam.ts +8 -0
- package/src/windows-command.ts +12 -0
|
@@ -5,8 +5,7 @@ const REFUSED = new Set(['|', '>', '<', '$', '`', '(', ')', '{', '}', '*', '?',
|
|
|
5
5
|
/*
|
|
6
6
|
* Whether a shell command line is only simple commands, joined by `&&`, `||`, `;` or newlines, each of
|
|
7
7
|
* which starts with one of `allowed`. Anything the tokenizer cannot prove simple answers false, so a
|
|
8
|
-
* true is safe to act on without a person
|
|
9
|
-
* `&`, an unquoted glob or an unbalanced quote all refuse the whole line.
|
|
8
|
+
* true is safe to act on without a person.
|
|
10
9
|
*/
|
|
11
10
|
export function isOnlyCommands(command: string, allowed: readonly string[]): boolean {
|
|
12
11
|
let word = '';
|
|
@@ -20,103 +19,139 @@ export function isOnlyCommands(command: string, allowed: readonly string[]): boo
|
|
|
20
19
|
if (inWord) {
|
|
21
20
|
words.push(word);
|
|
22
21
|
}
|
|
22
|
+
|
|
23
23
|
word = '';
|
|
24
24
|
inWord = false;
|
|
25
25
|
};
|
|
26
26
|
|
|
27
27
|
const endCommand = (separator: string): boolean => {
|
|
28
28
|
endWord();
|
|
29
|
+
|
|
29
30
|
if (words.length === 0) {
|
|
30
31
|
// Blank lines are fine, and so is a newline after `&&` or `||`; an empty command before any other separator is not.
|
|
31
32
|
return separator === '\n';
|
|
32
33
|
}
|
|
34
|
+
|
|
33
35
|
if (!allowed.includes(words[0]!)) {
|
|
34
36
|
return false;
|
|
35
37
|
}
|
|
38
|
+
|
|
36
39
|
commands++;
|
|
37
40
|
words = [];
|
|
38
41
|
dangling = separator === '&&' || separator === '||';
|
|
42
|
+
|
|
39
43
|
return true;
|
|
40
44
|
};
|
|
41
45
|
|
|
42
46
|
for (let i = 0; i < command.length; i++) {
|
|
43
47
|
const char = command[i]!;
|
|
44
48
|
const next = command[i + 1];
|
|
49
|
+
|
|
45
50
|
if (char === ' ' || char === '\t') {
|
|
46
51
|
endWord();
|
|
52
|
+
|
|
47
53
|
continue;
|
|
48
54
|
}
|
|
55
|
+
|
|
49
56
|
if (char === '\n' || char === ';') {
|
|
50
57
|
if (!endCommand(char)) {
|
|
51
58
|
return false;
|
|
52
59
|
}
|
|
60
|
+
|
|
53
61
|
continue;
|
|
54
62
|
}
|
|
63
|
+
|
|
55
64
|
if ((char === '&' || char === '|') && next === char) {
|
|
56
65
|
if (!endCommand(char + next)) {
|
|
57
66
|
return false;
|
|
58
67
|
}
|
|
68
|
+
|
|
59
69
|
i++;
|
|
70
|
+
|
|
60
71
|
continue;
|
|
61
72
|
}
|
|
73
|
+
|
|
62
74
|
if (char === '&' || REFUSED.has(char)) {
|
|
63
75
|
return false;
|
|
64
76
|
}
|
|
77
|
+
|
|
65
78
|
if (char === '\\') {
|
|
66
79
|
if (next === undefined) {
|
|
67
80
|
return false;
|
|
68
81
|
}
|
|
82
|
+
|
|
69
83
|
i++;
|
|
84
|
+
|
|
70
85
|
if (next !== '\n') {
|
|
71
86
|
word += next;
|
|
72
87
|
inWord = true;
|
|
73
88
|
}
|
|
89
|
+
|
|
74
90
|
continue;
|
|
75
91
|
}
|
|
92
|
+
|
|
76
93
|
if (char === "'") {
|
|
77
94
|
const close = command.indexOf("'", i + 1);
|
|
95
|
+
|
|
78
96
|
if (close === -1) {
|
|
79
97
|
return false;
|
|
80
98
|
}
|
|
99
|
+
|
|
81
100
|
word += command.slice(i + 1, close);
|
|
82
101
|
inWord = true;
|
|
83
102
|
i = close;
|
|
103
|
+
|
|
84
104
|
continue;
|
|
85
105
|
}
|
|
106
|
+
|
|
86
107
|
if (char === '"') {
|
|
87
108
|
inWord = true;
|
|
88
109
|
let closed = false;
|
|
110
|
+
|
|
89
111
|
for (i++; i < command.length; i++) {
|
|
90
112
|
const quoted = command[i]!;
|
|
113
|
+
|
|
91
114
|
if (quoted === '"') {
|
|
92
115
|
closed = true;
|
|
116
|
+
|
|
93
117
|
break;
|
|
94
118
|
}
|
|
119
|
+
|
|
95
120
|
if (quoted === '$' || quoted === '`') {
|
|
96
121
|
return false;
|
|
97
122
|
}
|
|
123
|
+
|
|
98
124
|
if (quoted === '\\') {
|
|
99
125
|
const escaped = command[i + 1];
|
|
126
|
+
|
|
100
127
|
if (escaped === undefined) {
|
|
101
128
|
return false;
|
|
102
129
|
}
|
|
130
|
+
|
|
103
131
|
i++;
|
|
132
|
+
|
|
104
133
|
if (escaped === '"' || escaped === '\\' || escaped === '$' || escaped === '`') {
|
|
105
134
|
word += escaped;
|
|
106
135
|
} else if (escaped !== '\n') {
|
|
107
136
|
word += quoted + escaped;
|
|
108
137
|
}
|
|
138
|
+
|
|
109
139
|
continue;
|
|
110
140
|
}
|
|
141
|
+
|
|
111
142
|
word += quoted;
|
|
112
143
|
}
|
|
144
|
+
|
|
113
145
|
if (!closed) {
|
|
114
146
|
return false;
|
|
115
147
|
}
|
|
148
|
+
|
|
116
149
|
continue;
|
|
117
150
|
}
|
|
151
|
+
|
|
118
152
|
word += char;
|
|
119
153
|
inWord = true;
|
|
120
154
|
}
|
|
155
|
+
|
|
121
156
|
return endCommand('\n') && commands > 0 && !dangling;
|
|
122
157
|
}
|
package/src/record-directory.ts
CHANGED
|
@@ -54,36 +54,48 @@ export class RecordDirectory<T> {
|
|
|
54
54
|
/* Reads what an earlier run of the host wrote down. Call before anything can ask. */
|
|
55
55
|
async load(): Promise<void> {
|
|
56
56
|
let names: string[];
|
|
57
|
+
|
|
57
58
|
try {
|
|
58
59
|
names = await readdir(this.dir);
|
|
59
60
|
} catch (e) {
|
|
60
61
|
if (isNotFound(e)) {
|
|
61
62
|
return;
|
|
62
63
|
}
|
|
64
|
+
|
|
63
65
|
throw e;
|
|
64
66
|
}
|
|
67
|
+
|
|
65
68
|
for (const name of names) {
|
|
66
69
|
if (!name.endsWith('.json')) {
|
|
67
70
|
continue;
|
|
68
71
|
}
|
|
72
|
+
|
|
69
73
|
const raw = await readFile(join(this.dir, name), 'utf8').catch(() => null);
|
|
74
|
+
|
|
70
75
|
if (raw === null) {
|
|
71
76
|
continue;
|
|
72
77
|
}
|
|
78
|
+
|
|
73
79
|
let parsed: ReturnType<typeof this.schema.safeParse>;
|
|
80
|
+
|
|
74
81
|
try {
|
|
75
82
|
parsed = this.schema.safeParse(JSON.parse(raw));
|
|
76
83
|
} catch {
|
|
77
84
|
continue;
|
|
78
85
|
}
|
|
86
|
+
|
|
79
87
|
if (!parsed.success) {
|
|
80
88
|
continue;
|
|
81
89
|
}
|
|
90
|
+
|
|
82
91
|
const record = this.keep === null ? parsed.data : this.keep(parsed.data);
|
|
92
|
+
|
|
83
93
|
if (record === null) {
|
|
84
94
|
await this.removeFile(join(this.dir, name));
|
|
95
|
+
|
|
85
96
|
continue;
|
|
86
97
|
}
|
|
98
|
+
|
|
87
99
|
this.records.set(this.idOf(record), record);
|
|
88
100
|
}
|
|
89
101
|
}
|
|
@@ -109,18 +121,23 @@ export class RecordDirectory<T> {
|
|
|
109
121
|
write(record: T): Promise<boolean> {
|
|
110
122
|
const id = this.idOf(record);
|
|
111
123
|
this.records.set(id, record);
|
|
124
|
+
|
|
112
125
|
return this.files.run(id, async () => {
|
|
113
126
|
if (this.records.get(id) !== record) {
|
|
114
127
|
return false;
|
|
115
128
|
}
|
|
129
|
+
|
|
116
130
|
await mkdir(this.dir, { recursive: true, mode: 0o700 });
|
|
117
131
|
await this.writeFile(this.pathOf(id), JSON.stringify(record));
|
|
132
|
+
|
|
118
133
|
if (this.records.get(id) === record) {
|
|
119
134
|
return true;
|
|
120
135
|
}
|
|
136
|
+
|
|
121
137
|
if (!this.records.has(id)) {
|
|
122
138
|
await this.removeFile(this.pathOf(id));
|
|
123
139
|
}
|
|
140
|
+
|
|
124
141
|
return false;
|
|
125
142
|
});
|
|
126
143
|
}
|
|
@@ -128,6 +145,7 @@ export class RecordDirectory<T> {
|
|
|
128
145
|
/* Forgets a record here and now; its file follows, after what is queued for it. */
|
|
129
146
|
remove(id: string): Promise<void> {
|
|
130
147
|
this.records.delete(id);
|
|
148
|
+
|
|
131
149
|
return this.files.run(id, () => this.removeFile(this.pathOf(id)));
|
|
132
150
|
}
|
|
133
151
|
|
|
@@ -136,6 +154,7 @@ export class RecordDirectory<T> {
|
|
|
136
154
|
const id = this.idOf(listed);
|
|
137
155
|
// Read again after each await: a record written meanwhile may no longer be one to drop.
|
|
138
156
|
const current = this.records.get(id);
|
|
157
|
+
|
|
139
158
|
if (current !== undefined && drop(current)) {
|
|
140
159
|
await this.remove(id);
|
|
141
160
|
}
|
package/src/run-process.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
|
-
import { killProcessTree } from './chat/chat-process.ts';
|
|
2
|
+
import { KILL_GRACE_MS, killProcessTree } from './chat/chat-process.ts';
|
|
3
3
|
import { spawnTarget } from './windows-command.ts';
|
|
4
4
|
|
|
5
5
|
export interface ProcessResult {
|
|
@@ -19,8 +19,6 @@ export interface RunProcessOptions {
|
|
|
19
19
|
timeoutMessage?: string;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
// What a command and everything it started get between the SIGTERM and the SIGKILL.
|
|
23
|
-
const KILL_GRACE_MS = 2000;
|
|
24
22
|
// Stdout and stderr together; a short command that prints more is ended and fails instead of filling memory.
|
|
25
23
|
const MAX_OUTPUT_BYTES = 16 * 1024 * 1024;
|
|
26
24
|
|
|
@@ -28,11 +26,15 @@ const MAX_OUTPUT_BYTES = 16 * 1024 * 1024;
|
|
|
28
26
|
export function runProcess(command: readonly string[], options: RunProcessOptions = {}): Promise<ProcessResult> {
|
|
29
27
|
return new Promise((resolve, reject) => {
|
|
30
28
|
const [executable, ...args] = command;
|
|
29
|
+
|
|
31
30
|
if (executable === undefined) {
|
|
32
31
|
reject(new Error('No command to run'));
|
|
32
|
+
|
|
33
33
|
return;
|
|
34
34
|
}
|
|
35
|
+
|
|
35
36
|
const target = spawnTarget(executable, args, options.env === undefined ? {} : { env: options.env });
|
|
37
|
+
|
|
36
38
|
const child = spawn(target.file, target.args, {
|
|
37
39
|
...(options.cwd === undefined ? {} : { cwd: options.cwd }),
|
|
38
40
|
...(options.env === undefined ? {} : { env: options.env }),
|
|
@@ -42,60 +44,79 @@ export function runProcess(command: readonly string[], options: RunProcessOption
|
|
|
42
44
|
detached: process.platform !== 'win32',
|
|
43
45
|
windowsHide: true
|
|
44
46
|
});
|
|
47
|
+
|
|
45
48
|
const stdout: Buffer[] = [];
|
|
46
49
|
const stderr: Buffer[] = [];
|
|
47
50
|
let printed = 0;
|
|
48
51
|
let settled = false;
|
|
49
52
|
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
53
|
+
|
|
50
54
|
const settle = (): boolean => {
|
|
51
55
|
if (settled) {
|
|
52
56
|
return false;
|
|
53
57
|
}
|
|
58
|
+
|
|
54
59
|
settled = true;
|
|
60
|
+
|
|
55
61
|
if (timer !== null) {
|
|
56
62
|
clearTimeout(timer);
|
|
57
63
|
}
|
|
64
|
+
|
|
58
65
|
return true;
|
|
59
66
|
};
|
|
67
|
+
|
|
60
68
|
const end = (message: string): void => {
|
|
61
69
|
if (!settle()) {
|
|
62
70
|
return;
|
|
63
71
|
}
|
|
72
|
+
|
|
64
73
|
const pid = child.pid;
|
|
74
|
+
|
|
65
75
|
if (pid !== undefined) {
|
|
66
76
|
killProcessTree(pid, 'SIGTERM');
|
|
67
77
|
// Sent even when the command left in time: what it started may still ignore the SIGTERM.
|
|
68
78
|
setTimeout(() => killProcessTree(pid, 'SIGKILL'), KILL_GRACE_MS).unref?.();
|
|
69
79
|
}
|
|
80
|
+
|
|
70
81
|
reject(new Error(message));
|
|
71
82
|
};
|
|
83
|
+
|
|
72
84
|
const collect = (into: Buffer[]) => (chunk: Buffer) => {
|
|
73
85
|
if (settled) {
|
|
74
86
|
return;
|
|
75
87
|
}
|
|
88
|
+
|
|
76
89
|
printed += chunk.length;
|
|
90
|
+
|
|
77
91
|
if (printed > MAX_OUTPUT_BYTES) {
|
|
78
92
|
end(`${executable} printed more than ${MAX_OUTPUT_BYTES / 1024 / 1024} MB`);
|
|
93
|
+
|
|
79
94
|
return;
|
|
80
95
|
}
|
|
96
|
+
|
|
81
97
|
into.push(chunk);
|
|
82
98
|
};
|
|
99
|
+
|
|
83
100
|
child.stdout?.on('data', collect(stdout));
|
|
84
101
|
child.stderr?.on('data', collect(stderr));
|
|
102
|
+
|
|
85
103
|
if (options.timeoutMs !== undefined) {
|
|
86
104
|
const message = options.timeoutMessage ?? `${executable} did not finish in time`;
|
|
87
105
|
timer = setTimeout(() => end(message), options.timeoutMs);
|
|
88
106
|
}
|
|
107
|
+
|
|
89
108
|
child.on('error', (error) => {
|
|
90
109
|
if (settle()) {
|
|
91
110
|
reject(error);
|
|
92
111
|
}
|
|
93
112
|
});
|
|
113
|
+
|
|
94
114
|
child.on('close', (exitCode) => {
|
|
95
115
|
if (settle()) {
|
|
96
116
|
resolve({ exitCode, stdout: Buffer.concat(stdout).toString('utf8'), stderr: Buffer.concat(stderr).toString('utf8') });
|
|
97
117
|
}
|
|
98
118
|
});
|
|
119
|
+
|
|
99
120
|
if (options.stdin !== undefined && child.stdin) {
|
|
100
121
|
child.stdin.on('error', () => undefined);
|
|
101
122
|
child.stdin.end(options.stdin);
|
package/src/serializer.ts
CHANGED
|
@@ -9,6 +9,7 @@ export class Serializer {
|
|
|
9
9
|
run<T>(work: () => Promise<T>): Promise<T> {
|
|
10
10
|
const next = this.chain.then(work, work);
|
|
11
11
|
this.chain = next.catch(() => undefined);
|
|
12
|
+
|
|
12
13
|
return next;
|
|
13
14
|
}
|
|
14
15
|
|
|
@@ -30,12 +31,14 @@ export class KeyedSerializer {
|
|
|
30
31
|
const next = previous.then(work, work);
|
|
31
32
|
const settled = next.catch(() => undefined);
|
|
32
33
|
this.chains.set(key, settled);
|
|
34
|
+
|
|
33
35
|
// A key whose chain ran out leaves nothing behind, or the map grows with every id ever written.
|
|
34
36
|
void settled.then(() => {
|
|
35
37
|
if (this.chains.get(key) === settled) {
|
|
36
38
|
this.chains.delete(key);
|
|
37
39
|
}
|
|
38
40
|
});
|
|
41
|
+
|
|
39
42
|
return next;
|
|
40
43
|
}
|
|
41
44
|
}
|