@myagentroam/node 0.10.35 → 0.10.36
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/connector.js +113 -18
- package/dist/database.js +27 -12
- package/dist/main.js +18 -2
- package/dist/migrations/v019.js +9 -0
- package/dist/runner/mar-agent/conversation-normalizer.js +28 -0
- package/dist/runner/mar-agent/marctl-skill.js +3 -2
- package/dist/runner/mar-agent-runner.js +32 -0
- package/dist/runtime-state.js +15 -10
- package/dist/service/continuous-run-coordinator.js +31 -0
- package/dist/service/direct-transfer-service.js +58 -11
- package/dist/service/external-session-resume-service.js +2 -1
- package/dist/service/goal-runtime.js +13 -0
- package/dist/service/native-session-projection-service.js +77 -11
- package/dist/service/planned-restart-handoff.js +279 -0
- package/dist/service/restart-session-restorer.js +141 -0
- package/dist/service/{scheduled-run-authorization-control.js → run-authorization-control.js} +17 -10
- package/dist/service/run-event-service.js +2 -0
- package/dist/service/session-catalog-service.js +57 -4
- package/dist/service/session-command-service.js +1 -0
- package/dist/service/session-lifecycle-service.js +25 -8
- package/dist/service/session-message-service.js +60 -14
- package/dist/service/session-presentation-service.js +2 -0
- package/dist/service/session-query-service.js +14 -9
- package/dist/service/workspace-queue-workbench-service.js +8 -3
- package/dist/service/workspace-upload-service.js +128 -29
- package/dist/supervisor.js +7 -3
- package/package.json +4 -4
|
@@ -11,7 +11,7 @@ export class SessionQueryService {
|
|
|
11
11
|
'session.cli.context': (data) => this.cliContext(record(data)),
|
|
12
12
|
'session.goal.get': async (data) => {
|
|
13
13
|
const input = record(data);
|
|
14
|
-
const session = await this.requireSession(input.sessionId, input.workspaceId);
|
|
14
|
+
const session = await this.requireSession(input.sessionId, input.workspaceId, input.registeredOnly === true);
|
|
15
15
|
return { goal: this.options.goal?.(session.id) ?? null };
|
|
16
16
|
},
|
|
17
17
|
'session.runs': (data) => this.runs(record(data)),
|
|
@@ -24,7 +24,7 @@ export class SessionQueryService {
|
|
|
24
24
|
}
|
|
25
25
|
async get(input) {
|
|
26
26
|
return {
|
|
27
|
-
session: this.options.present(await this.requireSession(input.sessionId, input.workspaceId))
|
|
27
|
+
session: this.options.present(await this.requireSession(input.sessionId, input.workspaceId, input.registeredOnly === true))
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
30
|
async cliContext(input) {
|
|
@@ -35,7 +35,7 @@ export class SessionQueryService {
|
|
|
35
35
|
throw new Error('WORKSPACE_NOT_FOUND');
|
|
36
36
|
const session = input.sessionId === undefined
|
|
37
37
|
? undefined
|
|
38
|
-
: await this.requireSession(input.sessionId, workspace.id);
|
|
38
|
+
: await this.requireSession(input.sessionId, workspace.id, input.registeredOnly === true);
|
|
39
39
|
return {
|
|
40
40
|
workspace: { id: workspace.id, path: workspace.path },
|
|
41
41
|
session: session === undefined ? null : this.options.present(session),
|
|
@@ -43,12 +43,12 @@ export class SessionQueryService {
|
|
|
43
43
|
};
|
|
44
44
|
}
|
|
45
45
|
async runs(input) {
|
|
46
|
-
const session = await this.requireSession(input.sessionId, input.workspaceId);
|
|
46
|
+
const session = await this.requireSession(input.sessionId, input.workspaceId, input.registeredOnly === true);
|
|
47
47
|
return { runs: this.options.runtime.listRunsForSession(session.id, { includeTerminal: true }) };
|
|
48
48
|
}
|
|
49
49
|
async turns(input) {
|
|
50
50
|
const page = pageInput(input);
|
|
51
|
-
const session = await this.requireSession(input.sessionId, input.workspaceId);
|
|
51
|
+
const session = await this.requireSession(input.sessionId, input.workspaceId, input.registeredOnly === true);
|
|
52
52
|
if (input.unit === 'SEGMENT')
|
|
53
53
|
return this.options.segments(session, page);
|
|
54
54
|
if (input.unit !== undefined && input.unit !== 'TURN')
|
|
@@ -66,7 +66,7 @@ export class SessionQueryService {
|
|
|
66
66
|
return this.options.runtime.listConversationItems({ sessionId: session.id, ...page });
|
|
67
67
|
}
|
|
68
68
|
async readToolResource(input) {
|
|
69
|
-
const session = await this.requireSession(input.sessionId, input.workspaceId);
|
|
69
|
+
const session = await this.requireSession(input.sessionId, input.workspaceId, input.registeredOnly === true);
|
|
70
70
|
if (typeof input.workspaceId !== 'string' || input.workspaceId !== session.workspaceId)
|
|
71
71
|
throw new Error('SESSION_NOT_FOUND');
|
|
72
72
|
const resource = toolResultResourceInputSchema.parse({
|
|
@@ -80,7 +80,7 @@ export class SessionQueryService {
|
|
|
80
80
|
return toolResultResourcePageSchema.parse(await runner.readToolResultResource(session, resource));
|
|
81
81
|
}
|
|
82
82
|
async readContext(input) {
|
|
83
|
-
return this.options.context.read(await this.requireSession(input.sessionId, input.workspaceId));
|
|
83
|
+
return this.options.context.read(await this.requireSession(input.sessionId, input.workspaceId, input.registeredOnly === true));
|
|
84
84
|
}
|
|
85
85
|
async discover(input) {
|
|
86
86
|
const runner = this.options.runners.product(input.runner);
|
|
@@ -96,10 +96,15 @@ export class SessionQueryService {
|
|
|
96
96
|
const sessions = await this.options.discover(runner.name, workspaceId);
|
|
97
97
|
return { sessions: sessions.map((session) => this.options.present(session)) };
|
|
98
98
|
}
|
|
99
|
-
async requireSession(sessionId, workspaceId) {
|
|
99
|
+
async requireSession(sessionId, workspaceId, registeredOnly = false) {
|
|
100
100
|
if (typeof sessionId !== 'string')
|
|
101
101
|
throw new Error('SESSION_INVALID');
|
|
102
|
-
const session = this.options.runtime.getAgentSession(sessionId) ??
|
|
102
|
+
const session = this.options.runtime.getAgentSession(sessionId) ??
|
|
103
|
+
(await this.options.resolve(sessionId, registeredOnly));
|
|
104
|
+
if (session === undefined && registeredOnly && typeof workspaceId === 'string')
|
|
105
|
+
throw Object.assign(new Error('SESSION_NOT_FOUND'), {
|
|
106
|
+
publicDetails: this.options.similar(sessionId, workspaceId)
|
|
107
|
+
});
|
|
103
108
|
if (session === undefined || (workspaceId !== undefined && session.workspaceId !== workspaceId))
|
|
104
109
|
throw new Error('SESSION_NOT_FOUND');
|
|
105
110
|
return session;
|
|
@@ -63,10 +63,15 @@ export class WorkspaceQueueWorkbenchService {
|
|
|
63
63
|
})
|
|
64
64
|
};
|
|
65
65
|
}
|
|
66
|
-
get(input) {
|
|
67
|
-
|
|
66
|
+
async get(input) {
|
|
67
|
+
if (typeof input.workspaceId !== 'string' || typeof input.sessionId !== 'string')
|
|
68
|
+
throw new Error('WORKSPACE_NOT_FOUND');
|
|
69
|
+
const session = this.options.runtime.getAgentSession(input.sessionId) ??
|
|
70
|
+
(await this.options.resolve(input.sessionId, input.registeredOnly === true));
|
|
71
|
+
if (session?.workspaceId !== input.workspaceId)
|
|
72
|
+
throw new Error('SESSION_NOT_FOUND');
|
|
68
73
|
return {
|
|
69
|
-
queue: this.
|
|
74
|
+
queue: this.snapshot(session.id)
|
|
70
75
|
};
|
|
71
76
|
}
|
|
72
77
|
pauseOrResume(mode, input) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { randomUUID } from 'node:crypto';
|
|
2
|
-
import { lstat, mkdir, open, readFile, rename, rm } from 'node:fs/promises';
|
|
1
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
2
|
+
import { link, lstat, mkdir, open, readFile, rename, rm, stat } from 'node:fs/promises';
|
|
3
3
|
import { dirname, join, posix, resolve } from 'node:path';
|
|
4
4
|
import { parseComposerAttachmentUpload, validComposerAttachmentName } from '../util/node-operation-parsers.js';
|
|
5
5
|
import { preflightWorkspaceUpload, workspaceUploadTemporaryName } from '../workspace.js';
|
|
@@ -38,6 +38,8 @@ export class WorkspaceUploadService {
|
|
|
38
38
|
!Number.isSafeInteger(input.size) ||
|
|
39
39
|
input.size < 0 ||
|
|
40
40
|
typeof input.overwrite !== 'boolean' ||
|
|
41
|
+
(input.sha256 !== undefined &&
|
|
42
|
+
(typeof input.sha256 !== 'string' || !/^[a-f0-9]{64}$/u.test(input.sha256))) ||
|
|
41
43
|
(attachment !== undefined && input.directory !== undefined))
|
|
42
44
|
throw new Error('UPLOAD_INVALID');
|
|
43
45
|
if (attachment !== undefined &&
|
|
@@ -59,20 +61,26 @@ export class WorkspaceUploadService {
|
|
|
59
61
|
throw new Error('UPLOAD_TARGET_BUSY');
|
|
60
62
|
const id = randomUUID();
|
|
61
63
|
const temporaryPath = resolve(dirname(target.absolutePath), workspaceUploadTemporaryName(id));
|
|
62
|
-
const
|
|
64
|
+
const parentIdentity = await stat(dirname(target.absolutePath));
|
|
65
|
+
const handle = await open(temporaryPath, 'wx+');
|
|
66
|
+
const stagingIdentity = await handle.stat();
|
|
63
67
|
const timer = setTimeout(() => {
|
|
64
68
|
const upload = this.state.uploads.get(id);
|
|
65
69
|
if (upload !== undefined)
|
|
66
|
-
void this.remove(upload);
|
|
70
|
+
void this.remove(upload).catch(() => undefined);
|
|
67
71
|
}, TTL_MS);
|
|
68
72
|
const upload = {
|
|
69
73
|
id,
|
|
70
74
|
workspaceId: workspace.id,
|
|
75
|
+
workspacePath: workspace.path,
|
|
76
|
+
parentIdentity,
|
|
77
|
+
stagingIdentity,
|
|
71
78
|
path: target.path,
|
|
72
79
|
targetPath: target.absolutePath,
|
|
73
80
|
temporaryPath,
|
|
74
81
|
size: input.size,
|
|
75
82
|
overwrite: input.overwrite,
|
|
83
|
+
...(typeof input.sha256 === 'string' ? { directSha256: input.sha256 } : {}),
|
|
76
84
|
...(attachment === undefined ? {} : { composerAttachment: attachment }),
|
|
77
85
|
handle,
|
|
78
86
|
receivedBytes: 0,
|
|
@@ -88,6 +96,8 @@ export class WorkspaceUploadService {
|
|
|
88
96
|
typeof input.dataBase64 !== 'string')
|
|
89
97
|
throw new Error('UPLOAD_CHUNK_INVALID');
|
|
90
98
|
const upload = this.require(input.uploadId);
|
|
99
|
+
if (upload.writing)
|
|
100
|
+
throw new Error('UPLOAD_OFFSET_INVALID');
|
|
91
101
|
if (input.offset !== upload.receivedBytes)
|
|
92
102
|
throw new Error('UPLOAD_OFFSET_INVALID');
|
|
93
103
|
const bytes = Buffer.from(input.dataBase64, 'base64');
|
|
@@ -95,18 +105,42 @@ export class WorkspaceUploadService {
|
|
|
95
105
|
throw new Error('UPLOAD_CHUNK_INVALID');
|
|
96
106
|
if (upload.receivedBytes + bytes.length > upload.size)
|
|
97
107
|
throw new Error('UPLOAD_SIZE_INVALID');
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
108
|
+
upload.writing = true;
|
|
109
|
+
try {
|
|
110
|
+
const result = await upload.handle.write(bytes, 0, bytes.length, upload.receivedBytes);
|
|
111
|
+
if (result.bytesWritten !== bytes.length)
|
|
112
|
+
throw new Error('UPLOAD_WRITE_FAILED');
|
|
113
|
+
upload.receivedBytes += bytes.length;
|
|
114
|
+
return this.status(upload.id);
|
|
115
|
+
}
|
|
116
|
+
finally {
|
|
117
|
+
upload.writing = false;
|
|
118
|
+
}
|
|
103
119
|
}
|
|
104
120
|
async createDirect(workspace, input) {
|
|
105
121
|
const normalized = input.path.replaceAll('\\', '/');
|
|
122
|
+
const existing = [...this.state.uploads.values()].find((upload) => input.composerAttachment === undefined &&
|
|
123
|
+
upload.composerAttachment === undefined &&
|
|
124
|
+
upload.workspaceId === workspace.id &&
|
|
125
|
+
upload.path === normalized &&
|
|
126
|
+
upload.size === input.size &&
|
|
127
|
+
upload.overwrite === input.overwrite &&
|
|
128
|
+
upload.directSha256 === input.sha256);
|
|
129
|
+
if (existing !== undefined) {
|
|
130
|
+
const target = await preflightWorkspaceUpload(workspace.path, posix.dirname(normalized), posix.basename(normalized));
|
|
131
|
+
if (target.absolutePath !== existing.targetPath ||
|
|
132
|
+
target.path !== existing.path ||
|
|
133
|
+
target.status === 'INVALID_NAME' ||
|
|
134
|
+
target.status === 'EXISTS_DIRECTORY' ||
|
|
135
|
+
(target.status === 'EXISTS_FILE' && !existing.overwrite))
|
|
136
|
+
throw new Error('UPLOAD_TARGET_INVALID');
|
|
137
|
+
return this.status(existing.id);
|
|
138
|
+
}
|
|
106
139
|
const status = await this.create(workspace, {
|
|
107
140
|
name: posix.basename(normalized),
|
|
108
141
|
size: input.size,
|
|
109
142
|
overwrite: input.overwrite,
|
|
143
|
+
sha256: input.sha256,
|
|
110
144
|
...(input.composerAttachment === undefined
|
|
111
145
|
? { directory: posix.dirname(normalized) }
|
|
112
146
|
: {
|
|
@@ -124,11 +158,21 @@ export class WorkspaceUploadService {
|
|
|
124
158
|
});
|
|
125
159
|
return status.receivedBytes;
|
|
126
160
|
}
|
|
127
|
-
async completeDirect(uploadId, size) {
|
|
161
|
+
async completeDirect(uploadId, size, signal) {
|
|
128
162
|
const upload = this.require(uploadId);
|
|
129
|
-
if (size !== upload.size)
|
|
163
|
+
if (size !== upload.size || upload.receivedBytes !== upload.size)
|
|
130
164
|
throw new Error('UPLOAD_SIZE_INVALID');
|
|
131
|
-
|
|
165
|
+
const hash = createHash('sha256');
|
|
166
|
+
for await (const bytes of upload.handle.createReadStream({ start: 0, autoClose: false })) {
|
|
167
|
+
this.assertActive(upload, signal);
|
|
168
|
+
hash.update(bytes);
|
|
169
|
+
}
|
|
170
|
+
this.assertActive(upload, signal);
|
|
171
|
+
if (hash.digest('hex') !== upload.directSha256) {
|
|
172
|
+
await this.remove(upload);
|
|
173
|
+
throw new Error('DIRECT_INTEGRITY_FAILED');
|
|
174
|
+
}
|
|
175
|
+
await this.complete(uploadId, signal);
|
|
132
176
|
}
|
|
133
177
|
status(uploadId) {
|
|
134
178
|
const upload = this.require(uploadId);
|
|
@@ -139,27 +183,28 @@ export class WorkspaceUploadService {
|
|
|
139
183
|
size: upload.size
|
|
140
184
|
};
|
|
141
185
|
}
|
|
142
|
-
async complete(uploadId) {
|
|
186
|
+
async complete(uploadId, signal) {
|
|
143
187
|
const upload = this.require(uploadId);
|
|
144
188
|
if (upload.receivedBytes !== upload.size)
|
|
145
189
|
throw new Error('UPLOAD_INCOMPLETE');
|
|
146
190
|
await upload.handle.sync();
|
|
191
|
+
this.assertActive(upload, signal);
|
|
147
192
|
await upload.handle.close();
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
if (existing !== undefined && (!existing.isFile() || !upload.overwrite))
|
|
152
|
-
throw new Error('UPLOAD_TARGET_EXISTS');
|
|
153
|
-
try {
|
|
193
|
+
await this.validateCommit(upload);
|
|
194
|
+
this.assertActive(upload, signal);
|
|
195
|
+
if (upload.overwrite) {
|
|
154
196
|
await rename(upload.temporaryPath, upload.targetPath);
|
|
155
197
|
}
|
|
156
|
-
|
|
157
|
-
|
|
198
|
+
else {
|
|
199
|
+
try {
|
|
200
|
+
await link(upload.temporaryPath, upload.targetPath);
|
|
201
|
+
}
|
|
202
|
+
catch (error) {
|
|
203
|
+
if (error.code === 'EEXIST')
|
|
204
|
+
throw new Error('UPLOAD_TARGET_EXISTS');
|
|
158
205
|
throw error;
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
await rm(upload.targetPath, { force: false });
|
|
162
|
-
await rename(upload.temporaryPath, upload.targetPath);
|
|
206
|
+
}
|
|
207
|
+
await rm(upload.temporaryPath);
|
|
163
208
|
}
|
|
164
209
|
clearTimeout(upload.timer);
|
|
165
210
|
this.state.uploads.delete(upload.id);
|
|
@@ -284,17 +329,68 @@ export class WorkspaceUploadService {
|
|
|
284
329
|
if (typeof id !== 'string')
|
|
285
330
|
throw new Error('UPLOAD_NOT_FOUND');
|
|
286
331
|
const upload = this.state.uploads.get(id);
|
|
287
|
-
if (upload === undefined)
|
|
332
|
+
if (upload === undefined || upload.cancelled)
|
|
288
333
|
throw new Error('UPLOAD_NOT_FOUND');
|
|
289
334
|
return upload;
|
|
290
335
|
}
|
|
336
|
+
assertActive(upload, signal) {
|
|
337
|
+
if (this.state.uploads.get(upload.id) !== upload || upload.cancelled || signal?.aborted)
|
|
338
|
+
throw new Error('UPLOAD_CANCELLED');
|
|
339
|
+
}
|
|
340
|
+
async validateCommit(upload) {
|
|
341
|
+
if (upload.composerAttachment === undefined) {
|
|
342
|
+
const target = await preflightWorkspaceUpload(upload.workspacePath, posix.dirname(upload.path), posix.basename(upload.path));
|
|
343
|
+
if (target.absolutePath !== upload.targetPath || target.path !== upload.path)
|
|
344
|
+
throw new Error('UPLOAD_TARGET_INVALID');
|
|
345
|
+
if (target.status === 'EXISTS_DIRECTORY')
|
|
346
|
+
throw new Error('UPLOAD_TARGET_DIRECTORY');
|
|
347
|
+
if (target.status === 'INVALID_NAME')
|
|
348
|
+
throw new Error('UPLOAD_NAME_INVALID');
|
|
349
|
+
if (target.status === 'EXISTS_FILE' && !upload.overwrite)
|
|
350
|
+
throw new Error('UPLOAD_TARGET_EXISTS');
|
|
351
|
+
}
|
|
352
|
+
else {
|
|
353
|
+
const target = await lstat(upload.targetPath).catch((error) => {
|
|
354
|
+
if (error.code === 'ENOENT')
|
|
355
|
+
return undefined;
|
|
356
|
+
throw error;
|
|
357
|
+
});
|
|
358
|
+
if (target?.isSymbolicLink())
|
|
359
|
+
throw new Error('FILE_PATH_ESCAPE');
|
|
360
|
+
if (target !== undefined && (!target.isFile() || !upload.overwrite))
|
|
361
|
+
throw new Error('UPLOAD_TARGET_EXISTS');
|
|
362
|
+
}
|
|
363
|
+
const parent = await lstat(dirname(upload.targetPath));
|
|
364
|
+
if (!parent.isDirectory() ||
|
|
365
|
+
parent.isSymbolicLink() ||
|
|
366
|
+
!sameIdentity(parent, upload.parentIdentity))
|
|
367
|
+
throw new Error('UPLOAD_TARGET_INVALID');
|
|
368
|
+
const staging = await lstat(upload.temporaryPath);
|
|
369
|
+
if (!staging.isFile() ||
|
|
370
|
+
staging.isSymbolicLink() ||
|
|
371
|
+
!sameIdentity(staging, upload.stagingIdentity) ||
|
|
372
|
+
staging.size !== upload.size)
|
|
373
|
+
throw new Error('UPLOAD_STAGING_INVALID');
|
|
374
|
+
}
|
|
291
375
|
async remove(upload) {
|
|
292
|
-
|
|
376
|
+
upload.cancelled = true;
|
|
293
377
|
clearTimeout(upload.timer);
|
|
294
378
|
await upload.handle.close().catch(() => undefined);
|
|
295
|
-
await
|
|
379
|
+
const parent = await lstat(dirname(upload.temporaryPath));
|
|
380
|
+
const staging = await lstat(upload.temporaryPath).catch((error) => {
|
|
381
|
+
if (error.code === 'ENOENT')
|
|
382
|
+
return undefined;
|
|
383
|
+
throw error;
|
|
384
|
+
});
|
|
385
|
+
if (!sameIdentity(parent, upload.parentIdentity) ||
|
|
386
|
+
parent.isSymbolicLink() ||
|
|
387
|
+
(staging !== undefined && !sameIdentity(staging, upload.stagingIdentity)))
|
|
388
|
+
throw new Error('UPLOAD_CLEANUP_FAILED');
|
|
389
|
+
if (staging !== undefined)
|
|
390
|
+
await rm(upload.temporaryPath, { force: true });
|
|
296
391
|
if (upload.composerAttachment !== undefined)
|
|
297
|
-
await rm(dirname(upload.targetPath), { recursive: true, force: true })
|
|
392
|
+
await rm(dirname(upload.targetPath), { recursive: true, force: true });
|
|
393
|
+
this.state.uploads.delete(upload.id);
|
|
298
394
|
}
|
|
299
395
|
async removeCompleted(attachment) {
|
|
300
396
|
if (this.state.composerAttachments.get(attachment.id) !== attachment)
|
|
@@ -331,6 +427,9 @@ export class WorkspaceUploadService {
|
|
|
331
427
|
};
|
|
332
428
|
}
|
|
333
429
|
}
|
|
430
|
+
function sameIdentity(actual, expected) {
|
|
431
|
+
return actual.dev === expected.dev && actual.ino === expected.ino;
|
|
432
|
+
}
|
|
334
433
|
async function safeDirectory(path) {
|
|
335
434
|
const value = await lstat(path);
|
|
336
435
|
if (!value.isDirectory() || value.isSymbolicLink())
|
package/dist/supervisor.js
CHANGED
|
@@ -14,8 +14,10 @@ export async function superviseNode(configPath, bootstrapEntrypoint) {
|
|
|
14
14
|
let entrypoint = await readActiveEntrypoint(activePath, bootstrapEntrypoint);
|
|
15
15
|
for (;;) {
|
|
16
16
|
const childResult = await runProcess(process.execPath, [entrypoint, 'run', '--config', configPath], true, { ...process.env, MAR_NODE_SUPERVISED: '1' });
|
|
17
|
-
if (childResult.signalled)
|
|
17
|
+
if (childResult.signalled) {
|
|
18
|
+
process.exitCode = childResult.code ?? 1;
|
|
18
19
|
return;
|
|
20
|
+
}
|
|
19
21
|
const code = childResult.code;
|
|
20
22
|
if (code !== UPGRADE_EXIT_CODE) {
|
|
21
23
|
if (code === 0)
|
|
@@ -102,6 +104,8 @@ async function runProcess(command, args, inherit, env = process.env) {
|
|
|
102
104
|
let settled = false;
|
|
103
105
|
let signalled = false;
|
|
104
106
|
const forward = (signal) => {
|
|
107
|
+
if (signalled)
|
|
108
|
+
return;
|
|
105
109
|
signalled = true;
|
|
106
110
|
child.kill(signal);
|
|
107
111
|
};
|
|
@@ -115,8 +119,8 @@ async function runProcess(command, args, inherit, env = process.env) {
|
|
|
115
119
|
process.off('SIGTERM', onSigterm);
|
|
116
120
|
resolveProcess({ code, signalled });
|
|
117
121
|
};
|
|
118
|
-
process.
|
|
119
|
-
process.
|
|
122
|
+
process.on('SIGINT', onSigint);
|
|
123
|
+
process.on('SIGTERM', onSigterm);
|
|
120
124
|
child.once('error', () => finish(null));
|
|
121
125
|
child.once('close', finish);
|
|
122
126
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@myagentroam/node",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.36",
|
|
4
4
|
"description": "MyAgentRoam Node runtime CLI.",
|
|
5
5
|
"license": "Apache-2.0 AND GPL-2.0-or-later",
|
|
6
6
|
"type": "module",
|
|
@@ -37,10 +37,10 @@
|
|
|
37
37
|
"playwright": "1.62.1",
|
|
38
38
|
"ws": "^8.21.3",
|
|
39
39
|
"zod": "4.4.3",
|
|
40
|
+
"@myagentroam/agent": "0.10.36",
|
|
40
41
|
"@myagentroam/code-navigation": "0.10.1",
|
|
41
|
-
"@myagentroam/marctl": "0.10.
|
|
42
|
-
"@myagentroam/
|
|
43
|
-
"@myagentroam/protocol": "0.10.35"
|
|
42
|
+
"@myagentroam/marctl": "0.10.6",
|
|
43
|
+
"@myagentroam/protocol": "0.10.36"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@types/ws": "^8.18.1"
|