@myagentroam/node 0.13.3 → 0.13.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.
@@ -26,7 +26,10 @@ export class DirectTransferService {
26
26
  disposed = false;
27
27
  static async create(options) {
28
28
  const range = options.udpPortRange?.() ?? { begin: 49_152, end: 49_215 };
29
- const createIceUdpMuxListener = options.createIceUdpMuxListener ?? ((port) => new NativeIceUdpMuxListener(port));
29
+ const createIceUdpMuxListener = options.createIceUdpMuxListener ??
30
+ ((port) => new NativeIceUdpMuxListener(port, process.platform === 'win32' && process.env['MAR_QUICK_START_OWNER'] === '1'
31
+ ? '0.0.0.0'
32
+ : undefined));
30
33
  // Windows can accept an IPv6 mux bind while another IPv4 endpoint owns the port.
31
34
  const probe = options.probeUdp4Port ?? (process.platform === 'win32' ? probeUdp4Port : undefined);
32
35
  const listener = await bindIceUdpMuxListener(range, createIceUdpMuxListener, probe);
@@ -874,13 +874,15 @@ export class HostedConversationNodeService {
874
874
  function hostedRequest(raw) {
875
875
  if (!record(raw))
876
876
  throw new Error('HOSTED_CONVERSATION_NOT_AUTHORIZED');
877
- const { __usageGrant, __requestUserId, __template, __marAgentModel, __marAgentModels, __marAgentCompact, __marAgentRuntimeScopeId, __workspaceWatchSessionHash, __templateDeleted, __sourceTemplateId, ...data } = raw;
877
+ const { __usageGrant, __requestUserId, __template, __marAgentModel, __marAgentModels, __marAgentCompact, __marAgentRuntimeScopeId, __searchBackendAvailable, __workspaceWatchSessionHash, __templateDeleted, __sourceTemplateId, ...data } = raw;
878
878
  if (typeof __requestUserId !== 'number' ||
879
879
  !Number.isSafeInteger(__requestUserId) ||
880
880
  __requestUserId <= 0)
881
881
  throw new Error('HOSTED_CONVERSATION_NOT_AUTHORIZED');
882
882
  if (__templateDeleted !== undefined && __templateDeleted !== true)
883
883
  throw new Error('HOSTED_CONVERSATION_NOT_AUTHORIZED');
884
+ if (__searchBackendAvailable !== undefined && typeof __searchBackendAvailable !== 'boolean')
885
+ throw new Error('HOSTED_CONVERSATION_NOT_AUTHORIZED');
884
886
  if (__sourceTemplateId !== undefined &&
885
887
  (typeof __sourceTemplateId !== 'string' || __sourceTemplateId.length === 0))
886
888
  throw new Error('HOSTED_CONVERSATION_NOT_AUTHORIZED');
@@ -889,6 +891,9 @@ function hostedRequest(raw) {
889
891
  throw new Error('HOSTED_CONVERSATION_NOT_AUTHORIZED');
890
892
  return {
891
893
  data,
894
+ ...(__searchBackendAvailable === undefined
895
+ ? {}
896
+ : { searchBackendAvailable: __searchBackendAvailable }),
892
897
  ...(__sourceTemplateId === undefined ? {} : { sourceTemplateId: __sourceTemplateId }),
893
898
  ...(__usageGrant === undefined
894
899
  ? {}
@@ -140,6 +140,9 @@ export class HostedConversationRuns {
140
140
  access: 'full-access',
141
141
  modelId: selected.id,
142
142
  models: networkModels(allowedModels(version, models), version.draft.networkMode),
143
+ ...(payload.searchBackendAvailable === undefined
144
+ ? {}
145
+ : { searchBackendAvailable: payload.searchBackendAvailable }),
143
146
  ...(payload.compact === undefined ? {} : { compact: payload.compact }),
144
147
  ...(payload.runtimeScopeId === undefined ? {} : { runtimeScopeId: payload.runtimeScopeId }),
145
148
  systemInstruction: systemInstruction(version, generatedImagesLogicalPath),
package/dist/main.js CHANGED
@@ -18,6 +18,23 @@ const browserPath = readOption(args, '--browser-cache');
18
18
  if (browserPath !== undefined)
19
19
  process.env['PLAYWRIGHT_BROWSERS_PATH'] = browserPath;
20
20
  if (command === 'run') {
21
+ let earlyQuickStop = false;
22
+ let quickStop;
23
+ process.on('message', (message) => {
24
+ if (typeof message !== 'object' ||
25
+ message === null ||
26
+ !('type' in message) ||
27
+ message.type !== 'node-quick-start-stop')
28
+ return;
29
+ earlyQuickStop = true;
30
+ quickStop?.();
31
+ });
32
+ if (process.env['MAR_QUICK_START_OWNER'] === '1') {
33
+ process.once('disconnect', () => {
34
+ earlyQuickStop = true;
35
+ quickStop?.();
36
+ });
37
+ }
21
38
  const nodeConfig = await loadNodeConfig(config);
22
39
  let releaseOwner;
23
40
  try {
@@ -59,7 +76,7 @@ if (command === 'run') {
59
76
  });
60
77
  };
61
78
  const supervisorHandoff = new SupervisorUpgradeHandoff();
62
- const supportsProgress = await supervisorHandoff.handshake();
79
+ const supportsProgress = process.env['MAR_QUICK_START_OWNER'] !== '1' && (await supervisorHandoff.handshake());
63
80
  if (!supportsProgress)
64
81
  supervisorHandoff.close();
65
82
  connector = new NodeConnector({
@@ -71,8 +88,12 @@ if (command === 'run') {
71
88
  for (const signal of ['SIGINT', 'SIGTERM']) {
72
89
  process.on(signal, () => shutdown());
73
90
  }
91
+ quickStop = () => shutdown();
74
92
  try {
75
- await connector.start();
93
+ if (earlyQuickStop)
94
+ shutdown();
95
+ else
96
+ await connector.start();
76
97
  }
77
98
  catch (error) {
78
99
  nodeLog('node.start.failed', { code: safeErrorCode(error, 'NODE_START_FAILED') });
@@ -1 +1 @@
1
- {"type":"module","version":"0.13.3"}
1
+ {"type":"module","version":"0.13.4"}
@@ -4,7 +4,8 @@ export function captureMarAgentExecutionInput(input) {
4
4
  model: input.marAgentModel,
5
5
  models: input.marAgentModels,
6
6
  compact: input.marAgentCompact,
7
- runtimeScopeId: input.marAgentRuntimeScopeId
7
+ runtimeScopeId: input.marAgentRuntimeScopeId,
8
+ searchBackendAvailable: input.searchBackendAvailable
8
9
  });
9
10
  }
10
11
  /** Only the MAR boundary understands either the wire input or its captured form. */
@@ -24,6 +25,7 @@ export function resolveMarAgentExecutionInput(input, modelId) {
24
25
  model: parsed.data,
25
26
  models: resolveRunModels(parsed.data, input?.models),
26
27
  compact: input?.compact,
28
+ searchBackendAvailable: input?.searchBackendAvailable !== false,
27
29
  ...(typeof input?.runtimeScopeId === 'string' ? { runtimeScopeId: input.runtimeScopeId } : {})
28
30
  };
29
31
  }
@@ -346,6 +346,9 @@ export class MarAgentHostedConversationRuntime {
346
346
  const webAccess = 'webAccess' in input && input.webAccess;
347
347
  const extensions = combineExecutionExtensions(imageExtensions, createWebToolsExtensions({
348
348
  enabled: webAccess,
349
+ ...('searchBackendAvailable' in input && input.searchBackendAvailable !== undefined
350
+ ? { searchBackendAvailable: input.searchBackendAvailable }
351
+ : {}),
349
352
  hostedSearchModelIds: models
350
353
  .filter((model) => model.hostedWebSearch)
351
354
  .map((model) => model.id),
@@ -323,6 +323,9 @@ export class MarAgentManagedRunController {
323
323
  });
324
324
  const extensions = combineExecutionExtensions(imageExtensions, createWebToolsExtensions({
325
325
  enabled: true,
326
+ ...(input.searchBackendAvailable === undefined
327
+ ? {}
328
+ : { searchBackendAvailable: input.searchBackendAvailable }),
326
329
  hostedSearchModelIds: executionModels
327
330
  .filter((model) => model.hostedWebSearch)
328
331
  .map((model) => model.id),
@@ -39,7 +39,9 @@ export const webFetchToolDefinition = {
39
39
  export function createWebToolsExtensions(input) {
40
40
  const tools = input.enabled
41
41
  ? [
42
- ...((input.hostedSearchModelIds !== undefined || !input.hostedWebSearch) && input.webSearch
42
+ ...(input.searchBackendAvailable !== false &&
43
+ (input.hostedSearchModelIds !== undefined || !input.hostedWebSearch) &&
44
+ input.webSearch
43
45
  ? [webSearchToolDefinition]
44
46
  : []),
45
47
  ...(input.webFetch ? [webFetchToolDefinition] : [])
@@ -47,7 +47,10 @@ export function executionAgentActor(input) {
47
47
  marAgentModel: captured.model,
48
48
  marAgentModels: captured.models,
49
49
  marAgentCompact: captured.compact,
50
- marAgentRuntimeScopeId: captured.runtimeScopeId
50
+ marAgentRuntimeScopeId: captured.runtimeScopeId,
51
+ ...(typeof captured.searchBackendAvailable === 'boolean'
52
+ ? { searchBackendAvailable: captured.searchBackendAvailable }
53
+ : {})
51
54
  }
52
55
  };
53
56
  }
@@ -120,7 +123,10 @@ export function agentNetworkExecutionCommand(input) {
120
123
  marAgentModel: data.marAgentModel,
121
124
  marAgentModels: data.marAgentModels,
122
125
  marAgentCompact: data.marAgentCompact,
123
- marAgentRuntimeScopeId: data.marAgentRuntimeScopeId
126
+ marAgentRuntimeScopeId: data.marAgentRuntimeScopeId,
127
+ ...(typeof data.searchBackendAvailable === 'boolean'
128
+ ? { searchBackendAvailable: data.searchBackendAvailable }
129
+ : {})
124
130
  },
125
131
  ...(usageGrant.success ? { usageGrant: usageGrant.data } : {}),
126
132
  ...(typeof data.personalInstructions === 'string'
@@ -159,7 +165,10 @@ export function agentNetworkExecutionCommand(input) {
159
165
  marAgentModel: resources.marAgentModel,
160
166
  marAgentModels: resources.marAgentModels,
161
167
  marAgentCompact: resources.marAgentCompact,
162
- marAgentRuntimeScopeId: resources.marAgentRuntimeScopeId
168
+ marAgentRuntimeScopeId: resources.marAgentRuntimeScopeId,
169
+ ...(typeof resources.searchBackendAvailable === 'boolean'
170
+ ? { searchBackendAvailable: resources.searchBackendAvailable }
171
+ : {})
163
172
  }
164
173
  };
165
174
  return invokeAgentNetwork(input.service, boundActor, data.action, data.input, undefined, data.__requestAbortSignal instanceof AbortSignal ? data.__requestAbortSignal : undefined);
@@ -37,6 +37,8 @@ export class NodeUpgradeCoordinator {
37
37
  }
38
38
  }
39
39
  request(data) {
40
+ if (process.env['MAR_QUICK_START_OWNER'] === '1')
41
+ throw new Error('NODE_UPGRADE_UNAVAILABLE');
40
42
  const input = record(data);
41
43
  const targetVersion = input.targetVersion;
42
44
  if (typeof targetVersion !== 'string' || !STABLE_VERSION.test(targetVersion))
@@ -1,18 +1,22 @@
1
1
  import { spawn } from 'node:child_process';
2
2
  import { terminateProcessTree } from '../process-tree.js';
3
3
  /** Wait for the child process itself, not inherited output handles held by descendants. */
4
- export function runSupervisedNodeChild(entrypoint, configPath, accept, accepted, upgradeAttemptId) {
4
+ export function runSupervisedNodeChild(entrypoint, configPath, accept, accepted, upgradeAttemptId, stopSignal) {
5
5
  return new Promise((resolve) => {
6
6
  const child = spawn(process.execPath, [entrypoint, 'run', '--config', configPath], {
7
7
  windowsHide: true,
8
8
  stdio: ['inherit', 'inherit', 'inherit', 'ipc'],
9
9
  env: {
10
10
  ...process.env,
11
- MAR_NODE_SUPERVISED: '1',
11
+ MAR_NODE_SUPERVISED: process.env['MAR_QUICK_START_OWNER'] === '1' ? '0' : '1',
12
12
  ...(upgradeAttemptId === undefined ? {} : { MAR_NODE_UPGRADE_ATTEMPT_ID: upgradeAttemptId })
13
13
  },
14
14
  detached: process.platform !== 'win32'
15
15
  });
16
+ if (process.env['MAR_QUICK_START_OWNER'] === '1' &&
17
+ child.pid !== undefined &&
18
+ process.connected)
19
+ process.send?.({ type: 'node-quick-start-child', pid: child.pid }, () => { });
16
20
  let done = false;
17
21
  let signalled = false;
18
22
  let channelId;
@@ -34,6 +38,7 @@ export function runSupervisedNodeChild(entrypoint, configPath, accept, accepted,
34
38
  clearTimeout(reapDeadline);
35
39
  process.off('SIGINT', onSigint);
36
40
  process.off('SIGTERM', onSigterm);
41
+ stopSignal?.removeEventListener('abort', onQuickStop);
37
42
  if (child.connected)
38
43
  child.disconnect();
39
44
  if (reclaimFailed)
@@ -54,21 +59,61 @@ export function runSupervisedNodeChild(entrypoint, configPath, accept, accepted,
54
59
  };
55
60
  const onSigint = () => forward('SIGINT');
56
61
  const onSigterm = () => forward('SIGTERM');
62
+ const onQuickStop = () => {
63
+ if (done || signalled)
64
+ return;
65
+ signalled = true;
66
+ if (deadline !== undefined)
67
+ clearTimeout(deadline);
68
+ send(child, { type: 'node-quick-start-stop' });
69
+ deadline = setTimeout(() => {
70
+ forced = true;
71
+ reclaiming = true;
72
+ reapDeadline = setTimeout(() => finish(null, true), 10_000);
73
+ void terminateProcessTree(child)
74
+ .then(() => {
75
+ reclaimed = true;
76
+ if (exited !== undefined)
77
+ finish(exited.code);
78
+ })
79
+ .catch(() => finish(null, true));
80
+ if (process.platform !== 'win32' && child.pid !== undefined) {
81
+ try {
82
+ process.kill(-child.pid, 'SIGKILL');
83
+ }
84
+ catch {
85
+ child.kill('SIGKILL');
86
+ }
87
+ }
88
+ }, 30_000);
89
+ };
57
90
  process.on('SIGINT', onSigint);
58
91
  process.on('SIGTERM', onSigterm);
92
+ stopSignal?.addEventListener('abort', onQuickStop, { once: true });
93
+ if (stopSignal?.aborted)
94
+ onQuickStop();
59
95
  child.on('error', () => {
60
96
  if (child.pid === undefined)
61
97
  finish(null);
62
98
  });
63
99
  child.once('exit', (code) => {
100
+ if (process.env['MAR_QUICK_START_OWNER'] === '1' &&
101
+ child.pid !== undefined &&
102
+ process.connected)
103
+ process.send?.({ type: 'node-quick-start-child-exited', pid: child.pid }, () => { });
64
104
  exited = { code };
65
105
  if (!reclaiming || reclaimed)
66
106
  finish(code);
67
107
  });
68
108
  child.on('message', (message) => {
69
- if (done || signalled || typeof message !== 'object' || message === null)
109
+ if (done || typeof message !== 'object' || message === null)
70
110
  return;
71
111
  const value = message;
112
+ if (signalled) {
113
+ if (stopSignal?.aborted && value['type'] === 'node-upgrade-hello')
114
+ send(child, { type: 'node-quick-start-stop' });
115
+ return;
116
+ }
72
117
  if (value['type'] === 'node-upgrade-hello' &&
73
118
  channelId === undefined &&
74
119
  typeof value['channelId'] === 'string') {
@@ -12,16 +12,50 @@ import { terminateProcessTree } from './process-tree.js';
12
12
  const UPGRADE_EXIT_CODE = 75;
13
13
  const STABLE_VERSION = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/u;
14
14
  export async function superviseNode(configPath, bootstrapEntrypoint) {
15
+ const stop = new AbortController();
16
+ let ownerDisconnected = false;
17
+ const onMessage = (message) => {
18
+ if (typeof message === 'object' &&
19
+ message !== null &&
20
+ 'type' in message &&
21
+ message.type === 'node-quick-start-stop')
22
+ stop.abort();
23
+ };
24
+ process.on('message', onMessage);
25
+ const onOwnerDisconnect = () => {
26
+ ownerDisconnected = true;
27
+ process.exitCode = 1;
28
+ stop.abort();
29
+ };
30
+ if (process.env['MAR_QUICK_START_OWNER'] === '1')
31
+ process.once('disconnect', onOwnerDisconnect);
32
+ try {
33
+ await superviseNodeUntilStopped(configPath, bootstrapEntrypoint, stop.signal);
34
+ }
35
+ finally {
36
+ if (ownerDisconnected)
37
+ process.exitCode = 1;
38
+ process.off('message', onMessage);
39
+ process.off('disconnect', onOwnerDisconnect);
40
+ }
41
+ }
42
+ async function superviseNodeUntilStopped(configPath, bootstrapEntrypoint, stopSignal) {
15
43
  const config = await loadNodeConfig(configPath);
16
44
  const dataDirectory = dirname(nodeDatabasePath(config, configPath));
17
45
  const activePath = resolve(dataDirectory, 'node-active-entrypoint');
18
- let entrypoint = await readActiveEntrypoint(activePath, bootstrapEntrypoint);
46
+ let entrypoint = process.env['MAR_QUICK_START_OWNER'] === '1'
47
+ ? bootstrapEntrypoint
48
+ : await readActiveEntrypoint(activePath, bootstrapEntrypoint);
19
49
  let reporter;
20
50
  let awaitingOnline;
21
51
  for (;;) {
52
+ if (stopSignal.aborted)
53
+ return;
22
54
  const requestPath = nodeUpgradeRequestPath(nodeDatabasePath(config, configPath));
23
55
  let reportingIdentity;
24
56
  const childResult = await runSupervisedNodeChild(entrypoint, configPath, async (handoff) => {
57
+ if (process.env['MAR_QUICK_START_OWNER'] === '1')
58
+ return false;
25
59
  const currentConfig = await loadNodeConfig(configPath);
26
60
  if (currentConfig.nodeId === undefined ||
27
61
  !STABLE_VERSION.test(handoff.targetVersion) ||
@@ -42,12 +76,15 @@ export async function superviseNode(configPath, bootstrapEntrypoint) {
42
76
  reporter?.stop();
43
77
  reporter = next;
44
78
  next.update({ attemptId: handoff.attemptId, step: 'CLOSING_NODE' });
45
- }, awaitingOnline);
79
+ }, awaitingOnline, stopSignal);
46
80
  if (childResult.signalled) {
47
81
  reporter?.stop();
48
- process.exitCode = childResult.code ?? 1;
82
+ process.exitCode =
83
+ childResult.forced || childResult.reclaimFailed ? 1 : (childResult.code ?? 1);
49
84
  return;
50
85
  }
86
+ if (stopSignal.aborted)
87
+ return;
51
88
  if (childResult.reclaimFailed) {
52
89
  if (childResult.handoff !== undefined)
53
90
  await reporter?.finish({
@@ -90,6 +127,11 @@ export async function superviseNode(configPath, bootstrapEntrypoint) {
90
127
  process.exitCode = code;
91
128
  return;
92
129
  }
130
+ if (process.env['MAR_QUICK_START_OWNER'] === '1' &&
131
+ (code === UPGRADE_EXIT_CODE || childResult.handoff !== undefined)) {
132
+ process.exitCode = 1;
133
+ return;
134
+ }
93
135
  if (code !== UPGRADE_EXIT_CODE && childResult.handoff === undefined) {
94
136
  if (code === 0)
95
137
  return;
@@ -110,7 +152,7 @@ export async function superviseNode(configPath, bootstrapEntrypoint) {
110
152
  !STABLE_VERSION.test(targetVersion))
111
153
  throw new Error('NODE_UPGRADE_REQUEST_INVALID');
112
154
  const prefix = resolve(dataDirectory, 'node-versions', targetVersion);
113
- const installDeadline = Date.now() + 15 * 60_000;
155
+ const installDeadline = Date.now() + 20 * 60_000;
114
156
  if (childResult.handoff !== undefined)
115
157
  reporter?.update({ attemptId: childResult.handoff.attemptId, step: 'INSTALLING' });
116
158
  failureStage = 'PREPARE_INSTALL_DIRECTORY';
@@ -126,7 +168,7 @@ export async function superviseNode(configPath, bootstrapEntrypoint) {
126
168
  `@myagentroam/node@${targetVersion}`
127
169
  ]);
128
170
  failureStage = 'NPM_INSTALL';
129
- const installResult = await runProcess(invocation.command, invocation.args, false, installDeadline);
171
+ const installResult = await runProcess(invocation.command, invocation.args, false, installDeadline, stopSignal);
130
172
  failureExitCode = installResult.code;
131
173
  if (installResult.reclaimFailed) {
132
174
  installReclaimFailed = true;
@@ -144,7 +186,7 @@ export async function superviseNode(configPath, bootstrapEntrypoint) {
144
186
  const rebuild = platformCliInvocation('npm', ['rebuild', '--prefix', prefix, 'node-pty']);
145
187
  failureStage = 'NATIVE_REBUILD';
146
188
  failureExitCode = undefined;
147
- const rebuildResult = await runProcess(rebuild.command, rebuild.args, false, installDeadline);
189
+ const rebuildResult = await runProcess(rebuild.command, rebuild.args, false, installDeadline, stopSignal);
148
190
  failureExitCode = rebuildResult.code;
149
191
  if (rebuildResult.reclaimFailed) {
150
192
  installReclaimFailed = true;
@@ -163,7 +205,7 @@ export async function superviseNode(configPath, bootstrapEntrypoint) {
163
205
  failureExitCode = undefined;
164
206
  const browserInstall = await runProcess(process.execPath, [
165
207
  resolve(prefix, 'node_modules', '@myagentroam', 'node', 'scripts', 'install-web-browser.mjs')
166
- ], true, installDeadline);
208
+ ], true, installDeadline, stopSignal);
167
209
  failureExitCode = browserInstall.code;
168
210
  if (browserInstall.reclaimFailed) {
169
211
  installReclaimFailed = true;
@@ -243,11 +285,16 @@ export async function superviseNode(configPath, bootstrapEntrypoint) {
243
285
  code: 'NODE_UPGRADE_REQUEST_CLEANUP_FAILED'
244
286
  });
245
287
  }
246
- if (stopping)
288
+ if (stopping) {
289
+ if (stopSignal.aborted)
290
+ process.exitCode = 1;
247
291
  return;
292
+ }
248
293
  }
249
294
  }
250
- async function runProcess(command, args, inherit, deadlineAt) {
295
+ async function runProcess(command, args, inherit, deadlineAt, stopSignal) {
296
+ if (stopSignal.aborted)
297
+ return { code: null, signalled: true, timedOut: false, reclaimFailed: false };
251
298
  return new Promise((resolveProcess) => {
252
299
  const child = spawn(command, args, {
253
300
  windowsHide: true,
@@ -293,6 +340,28 @@ async function runProcess(command, args, inherit, deadlineAt) {
293
340
  };
294
341
  const onSigint = () => forward('SIGINT');
295
342
  const onSigterm = () => forward('SIGTERM');
343
+ const onQuickStop = () => {
344
+ if (signalled || settled)
345
+ return;
346
+ signalled = true;
347
+ timedOut = true;
348
+ reapDeadline = setTimeout(() => finish(null, true), 10_000);
349
+ void terminateProcessTree(child)
350
+ .then(() => {
351
+ reclaimed = true;
352
+ if (exited !== undefined)
353
+ finish(exited.code);
354
+ })
355
+ .catch(() => finish(null, true));
356
+ if (process.platform !== 'win32' && child.pid !== undefined) {
357
+ try {
358
+ process.kill(-child.pid, 'SIGKILL');
359
+ }
360
+ catch {
361
+ child.kill('SIGKILL');
362
+ }
363
+ }
364
+ };
296
365
  const finish = (code, reclaimFailed = false) => {
297
366
  if (settled)
298
367
  return;
@@ -302,12 +371,16 @@ async function runProcess(command, args, inherit, deadlineAt) {
302
371
  clearTimeout(reapDeadline);
303
372
  process.off('SIGINT', onSigint);
304
373
  process.off('SIGTERM', onSigterm);
374
+ stopSignal.removeEventListener('abort', onQuickStop);
305
375
  if (reclaimFailed)
306
376
  child.unref();
307
377
  resolveProcess({ code, signalled, timedOut, reclaimFailed });
308
378
  };
309
379
  process.on('SIGINT', onSigint);
310
380
  process.on('SIGTERM', onSigterm);
381
+ stopSignal.addEventListener('abort', onQuickStop, { once: true });
382
+ if (stopSignal.aborted)
383
+ onQuickStop();
311
384
  child.on('error', () => {
312
385
  if (child.pid === undefined)
313
386
  finish(null);
package/package.json CHANGED
@@ -1,6 +1,11 @@
1
1
  {
2
2
  "name": "@myagentroam/node",
3
- "version": "0.13.3",
3
+ "version": "0.13.4",
4
+ "repository": {
5
+ "type": "git",
6
+ "url": "git+https://github.com/myagentroam/mar.git",
7
+ "directory": "mar-node"
8
+ },
4
9
  "description": "MyAgentRoam Node runtime CLI.",
5
10
  "license": "Apache-2.0 AND GPL-2.0-or-later",
6
11
  "type": "module",
@@ -32,7 +37,7 @@
32
37
  "dependencies": {
33
38
  "@anthropic-ai/claude-agent-sdk": "0.3.233",
34
39
  "@modelcontextprotocol/sdk": "1.32.0",
35
- "@myagentroam/binaries": "0.10.1",
40
+ "@myagentroam/binaries": "0.10.2",
36
41
  "@opencode-ai/sdk": "1.18.20",
37
42
  "minimatch": "^10.2.6",
38
43
  "node-datachannel": "0.33.4",
@@ -44,14 +49,14 @@
44
49
  "turndown-plugin-gfm": "^1.0.2",
45
50
  "ws": "^8.21.3",
46
51
  "zod": "4.4.3",
47
- "@myagentroam/agent": "0.13.3",
48
- "@myagentroam/protocol": "0.13.3"
52
+ "@myagentroam/agent": "0.13.4",
53
+ "@myagentroam/protocol": "0.13.4"
49
54
  },
50
55
  "devDependencies": {
51
56
  "@types/turndown": "^5.0.6",
52
57
  "@types/ws": "^8.18.1",
53
- "@myagentroam/code-navigation": "0.11.1",
54
- "@myagentroam/marctl": "0.13.3"
58
+ "@myagentroam/marctl": "0.13.4",
59
+ "@myagentroam/code-navigation": "0.11.1"
55
60
  },
56
61
  "exports": {
57
62
  "./config": {
@@ -25,15 +25,12 @@ export async function installWebBrowser(options = {}) {
25
25
  }
26
26
  const require = createRequire(import.meta.url);
27
27
  const cli = resolve(dirname(require.resolve('playwright/package.json')), 'cli.js');
28
- const run =
29
- options.run ??
30
- ((command, args) =>
31
- spawnSync(command, args, {
32
- stdio: 'inherit',
33
- env: environment,
34
- timeout: 300_000
35
- }));
36
- const result = run(process.execPath, [cli, 'install', 'chromium', '--no-shell']);
28
+ const run = options.run ?? spawnSync;
29
+ const result = run(process.execPath, [cli, 'install', 'chromium', '--no-shell'], {
30
+ stdio: 'inherit',
31
+ env: environment,
32
+ timeout: 12 * 60_000
33
+ });
37
34
  if (result.error || result.signal || result.status !== 0)
38
35
  throw new Error(
39
36
  'NODE_WEB_BROWSER_INSTALL_FAILED: Chromium download failed. Check network access or preinstall the matching Playwright Chromium runtime.'