@alibaba-group/opensandbox 0.1.4 → 0.1.5

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.
@@ -31,6 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
33
  ConnectionConfig: () => ConnectionConfig,
34
+ DEFAULT_EGRESS_PORT: () => DEFAULT_EGRESS_PORT,
34
35
  DEFAULT_ENTRYPOINT: () => DEFAULT_ENTRYPOINT,
35
36
  DEFAULT_EXECD_PORT: () => DEFAULT_EXECD_PORT,
36
37
  DEFAULT_HEALTH_CHECK_POLLING_INTERVAL_MILLIS: () => DEFAULT_HEALTH_CHECK_POLLING_INTERVAL_MILLIS,
@@ -69,25 +70,26 @@ var SandboxException = class extends Error {
69
70
  name = "SandboxException";
70
71
  error;
71
72
  cause;
73
+ requestId;
72
74
  constructor(opts = {}) {
73
75
  super(opts.message);
74
76
  this.cause = opts.cause;
75
77
  this.error = opts.error ?? new SandboxError(SandboxError.INTERNAL_UNKNOWN_ERROR);
78
+ this.requestId = opts.requestId;
76
79
  }
77
80
  };
78
81
  var SandboxApiException = class extends SandboxException {
79
82
  name = "SandboxApiException";
80
83
  statusCode;
81
- requestId;
82
84
  rawBody;
83
85
  constructor(opts) {
84
86
  super({
85
87
  message: opts.message,
86
88
  cause: opts.cause,
87
- error: opts.error ?? new SandboxError(SandboxError.UNEXPECTED_RESPONSE, opts.message)
89
+ error: opts.error ?? new SandboxError(SandboxError.UNEXPECTED_RESPONSE, opts.message),
90
+ requestId: opts.requestId
88
91
  });
89
92
  this.statusCode = opts.statusCode;
90
- this.requestId = opts.requestId;
91
93
  this.rawBody = opts.rawBody;
92
94
  }
93
95
  };
@@ -143,8 +145,19 @@ function createExecdClient(opts) {
143
145
  });
144
146
  }
145
147
 
146
- // src/openapi/lifecycleClient.ts
148
+ // src/openapi/egressClient.ts
147
149
  var import_openapi_fetch2 = __toESM(require("openapi-fetch"), 1);
150
+ function createEgressClient(opts) {
151
+ const createClientFn = import_openapi_fetch2.default.default ?? import_openapi_fetch2.default;
152
+ return createClientFn({
153
+ baseUrl: opts.baseUrl,
154
+ headers: opts.headers,
155
+ fetch: opts.fetch
156
+ });
157
+ }
158
+
159
+ // src/openapi/lifecycleClient.ts
160
+ var import_openapi_fetch3 = __toESM(require("openapi-fetch"), 1);
148
161
  function readEnvApiKey() {
149
162
  const env = globalThis?.process?.env;
150
163
  const v = env?.OPEN_SANDBOX_API_KEY;
@@ -158,7 +171,7 @@ function createLifecycleClient(opts = {}) {
158
171
  if (apiKey && !headers["OPEN-SANDBOX-API-KEY"]) {
159
172
  headers["OPEN-SANDBOX-API-KEY"] = apiKey;
160
173
  }
161
- const createClientFn = import_openapi_fetch2.default.default ?? import_openapi_fetch2.default;
174
+ const createClientFn = import_openapi_fetch3.default.default ?? import_openapi_fetch3.default;
162
175
  return createClientFn({
163
176
  baseUrl: opts.baseUrl ?? "http://localhost:8080/v1",
164
177
  headers,
@@ -322,6 +335,9 @@ function joinUrl(baseUrl, pathname) {
322
335
  return `${base}${path}`;
323
336
  }
324
337
  function toRunCommandRequest(command, opts) {
338
+ if (opts?.gid != null && opts.uid == null) {
339
+ throw new Error("uid is required when gid is provided");
340
+ }
325
341
  const body = {
326
342
  command,
327
343
  cwd: opts?.workingDirectory,
@@ -330,8 +346,39 @@ function toRunCommandRequest(command, opts) {
330
346
  if (opts?.timeoutSeconds != null) {
331
347
  body.timeout = Math.round(opts.timeoutSeconds * 1e3);
332
348
  }
349
+ if (opts?.uid != null) {
350
+ body.uid = opts.uid;
351
+ }
352
+ if (opts?.gid != null) {
353
+ body.gid = opts.gid;
354
+ }
355
+ if (opts?.envs != null) {
356
+ body.envs = opts.envs;
357
+ }
358
+ return body;
359
+ }
360
+ function toRunInSessionRequest(command, opts) {
361
+ const body = {
362
+ command
363
+ };
364
+ if (opts?.workingDirectory != null) {
365
+ body.cwd = opts.workingDirectory;
366
+ }
367
+ if (opts?.timeout != null) {
368
+ body.timeout = opts.timeout;
369
+ }
333
370
  return body;
334
371
  }
372
+ function inferForegroundExitCode(execution) {
373
+ const errorValue = execution.error?.value?.trim();
374
+ const parsedExitCode = errorValue && /^-?\d+$/.test(errorValue) ? Number(errorValue) : Number.NaN;
375
+ return execution.error != null ? Number.isFinite(parsedExitCode) ? parsedExitCode : null : execution.complete ? 0 : null;
376
+ }
377
+ function assertNonBlank(value, field) {
378
+ if (!value.trim()) {
379
+ throw new Error(`${field} cannot be empty`);
380
+ }
381
+ }
335
382
  function parseOptionalDate(value, field) {
336
383
  if (value == null) return void 0;
337
384
  if (value instanceof Date) return value;
@@ -351,6 +398,58 @@ var CommandsAdapter = class {
351
398
  this.fetch = opts.fetch ?? fetch;
352
399
  }
353
400
  fetch;
401
+ buildRunStreamSpec(command, opts) {
402
+ assertNonBlank(command, "command");
403
+ return {
404
+ pathname: "/command",
405
+ body: toRunCommandRequest(command, opts),
406
+ fallbackErrorMessage: "Run command failed"
407
+ };
408
+ }
409
+ buildRunInSessionStreamSpec(sessionId, command, opts) {
410
+ assertNonBlank(sessionId, "sessionId");
411
+ assertNonBlank(command, "command");
412
+ return {
413
+ pathname: `/session/${encodeURIComponent(sessionId)}/run`,
414
+ body: toRunInSessionRequest(command, opts),
415
+ fallbackErrorMessage: "Run in session failed"
416
+ };
417
+ }
418
+ async *streamExecution(spec, signal) {
419
+ const url = joinUrl(this.opts.baseUrl, spec.pathname);
420
+ const res = await this.fetch(url, {
421
+ method: "POST",
422
+ headers: {
423
+ accept: "text/event-stream",
424
+ "content-type": "application/json",
425
+ ...this.opts.headers ?? {}
426
+ },
427
+ body: JSON.stringify(spec.body),
428
+ signal
429
+ });
430
+ for await (const ev of parseJsonEventStream(res, {
431
+ fallbackErrorMessage: spec.fallbackErrorMessage
432
+ })) {
433
+ yield ev;
434
+ }
435
+ }
436
+ async consumeExecutionStream(stream, handlers, inferExitCode = false) {
437
+ const execution = {
438
+ logs: { stdout: [], stderr: [] },
439
+ result: []
440
+ };
441
+ const dispatcher = new ExecutionEventDispatcher(execution, handlers);
442
+ for await (const ev of stream) {
443
+ if (ev.type === "init" && (ev.text ?? "") === "" && execution.id) {
444
+ ev.text = execution.id;
445
+ }
446
+ await dispatcher.dispatch(ev);
447
+ }
448
+ if (inferExitCode) {
449
+ execution.exitCode = inferForegroundExitCode(execution);
450
+ }
451
+ return execution;
452
+ }
354
453
  async interrupt(sessionId) {
355
454
  const { error, response } = await this.client.DELETE("/command", {
356
455
  params: { query: { id: sessionId } }
@@ -394,35 +493,76 @@ var CommandsAdapter = class {
394
493
  };
395
494
  }
396
495
  async *runStream(command, opts, signal) {
397
- const url = joinUrl(this.opts.baseUrl, "/command");
398
- const body = JSON.stringify(toRunCommandRequest(command, opts));
399
- const res = await this.fetch(url, {
400
- method: "POST",
401
- headers: {
402
- "accept": "text/event-stream",
403
- "content-type": "application/json",
404
- ...this.opts.headers ?? {}
405
- },
406
- body,
496
+ for await (const ev of this.streamExecution(
497
+ this.buildRunStreamSpec(command, opts),
407
498
  signal
408
- });
409
- for await (const ev of parseJsonEventStream(res, { fallbackErrorMessage: "Run command failed" })) {
499
+ )) {
410
500
  yield ev;
411
501
  }
412
502
  }
413
503
  async run(command, opts, handlers, signal) {
414
- const execution = {
415
- logs: { stdout: [], stderr: [] },
416
- result: []
417
- };
418
- const dispatcher = new ExecutionEventDispatcher(execution, handlers);
419
- for await (const ev of this.runStream(command, opts, signal)) {
420
- if (ev.type === "init" && (ev.text ?? "") === "" && execution.id) {
421
- ev.text = execution.id;
422
- }
423
- await dispatcher.dispatch(ev);
504
+ return this.consumeExecutionStream(
505
+ this.runStream(command, opts, signal),
506
+ handlers,
507
+ !opts?.background
508
+ );
509
+ }
510
+ async createSession(options) {
511
+ const body = options?.workingDirectory != null ? { cwd: options.workingDirectory } : {};
512
+ const { data, error, response } = await this.client.POST("/session", {
513
+ body
514
+ });
515
+ throwOnOpenApiFetchError({ error, response }, "Create session failed");
516
+ const ok = data;
517
+ if (!ok || typeof ok.session_id !== "string") {
518
+ throw new Error("Create session failed: unexpected response shape");
424
519
  }
425
- return execution;
520
+ return ok.session_id;
521
+ }
522
+ async *runInSessionStream(sessionId, command, opts, signal) {
523
+ for await (const ev of this.streamExecution(
524
+ this.buildRunInSessionStreamSpec(sessionId, command, opts),
525
+ signal
526
+ )) {
527
+ yield ev;
528
+ }
529
+ }
530
+ async runInSession(sessionId, command, options, handlers, signal) {
531
+ return this.consumeExecutionStream(
532
+ this.runInSessionStream(sessionId, command, options, signal),
533
+ handlers,
534
+ true
535
+ );
536
+ }
537
+ async deleteSession(sessionId) {
538
+ const { error, response } = await this.client.DELETE(
539
+ "/session/{sessionId}",
540
+ { params: { path: { sessionId } } }
541
+ );
542
+ throwOnOpenApiFetchError({ error, response }, "Delete session failed");
543
+ }
544
+ };
545
+
546
+ // src/adapters/egressAdapter.ts
547
+ var EgressAdapter = class {
548
+ constructor(client) {
549
+ this.client = client;
550
+ }
551
+ async getPolicy() {
552
+ const { data, error, response } = await this.client.GET("/policy");
553
+ throwOnOpenApiFetchError({ error, response }, "Get sandbox egress policy failed");
554
+ const raw = data;
555
+ if (!raw || typeof raw !== "object" || !raw.policy || typeof raw.policy !== "object") {
556
+ throw new Error("Get sandbox egress policy failed: unexpected response shape");
557
+ }
558
+ return raw.policy;
559
+ }
560
+ async patchRules(rules) {
561
+ const body = rules;
562
+ const { error, response } = await this.client.PATCH("/policy", {
563
+ body
564
+ });
565
+ throwOnOpenApiFetchError({ error, response }, "Patch sandbox egress rules failed");
426
566
  }
427
567
  };
428
568
 
@@ -911,11 +1051,15 @@ var SandboxesAdapter = class {
911
1051
  }
912
1052
  return d;
913
1053
  }
1054
+ parseOptionalIsoDate(field, v) {
1055
+ if (v == null) return null;
1056
+ return this.parseIsoDate(field, v);
1057
+ }
914
1058
  mapSandboxInfo(raw) {
915
1059
  return {
916
1060
  ...raw ?? {},
917
1061
  createdAt: this.parseIsoDate("createdAt", raw?.createdAt),
918
- expiresAt: this.parseIsoDate("expiresAt", raw?.expiresAt)
1062
+ expiresAt: this.parseOptionalIsoDate("expiresAt", raw?.expiresAt)
919
1063
  };
920
1064
  }
921
1065
  async createSandbox(req) {
@@ -931,7 +1075,7 @@ var SandboxesAdapter = class {
931
1075
  return {
932
1076
  ...raw ?? {},
933
1077
  createdAt: this.parseIsoDate("createdAt", raw?.createdAt),
934
- expiresAt: this.parseIsoDate("expiresAt", raw?.expiresAt)
1078
+ expiresAt: this.parseOptionalIsoDate("expiresAt", raw?.expiresAt)
935
1079
  };
936
1080
  }
937
1081
  async getSandbox(sandboxId) {
@@ -1056,6 +1200,20 @@ var DefaultAdapterFactory = class {
1056
1200
  metrics
1057
1201
  };
1058
1202
  }
1203
+ createEgressStack(opts) {
1204
+ const headers = {
1205
+ ...opts.connectionConfig.headers ?? {},
1206
+ ...opts.endpointHeaders ?? {}
1207
+ };
1208
+ const egressClient = createEgressClient({
1209
+ baseUrl: opts.egressBaseUrl,
1210
+ headers,
1211
+ fetch: opts.connectionConfig.fetch
1212
+ });
1213
+ return {
1214
+ egress: new EgressAdapter(egressClient)
1215
+ };
1216
+ }
1059
1217
  };
1060
1218
  function createDefaultAdapterFactory() {
1061
1219
  return new DefaultAdapterFactory();
@@ -1063,6 +1221,7 @@ function createDefaultAdapterFactory() {
1063
1221
 
1064
1222
  // src/core/constants.ts
1065
1223
  var DEFAULT_EXECD_PORT = 44772;
1224
+ var DEFAULT_EGRESS_PORT = 18080;
1066
1225
  var DEFAULT_ENTRYPOINT = ["tail", "-f", "/dev/null"];
1067
1226
  var DEFAULT_RESOURCE_LIMITS = {
1068
1227
  cpu: "1",
@@ -1072,7 +1231,7 @@ var DEFAULT_TIMEOUT_SECONDS = 600;
1072
1231
  var DEFAULT_READY_TIMEOUT_SECONDS = 30;
1073
1232
  var DEFAULT_HEALTH_CHECK_POLLING_INTERVAL_MILLIS = 200;
1074
1233
  var DEFAULT_REQUEST_TIMEOUT_SECONDS = 30;
1075
- var DEFAULT_USER_AGENT = "OpenSandbox-JS-SDK/0.1.4";
1234
+ var DEFAULT_USER_AGENT = "OpenSandbox-JS-SDK/0.1.5";
1076
1235
 
1077
1236
  // src/config/connection.ts
1078
1237
  function isNodeRuntime2() {
@@ -1429,7 +1588,8 @@ var Sandbox = class _Sandbox {
1429
1588
  _Sandbox._priv.set(this, {
1430
1589
  adapterFactory: opts.adapterFactory,
1431
1590
  lifecycleBaseUrl: opts.lifecycleBaseUrl,
1432
- execdBaseUrl: opts.execdBaseUrl
1591
+ execdBaseUrl: opts.execdBaseUrl,
1592
+ egress: opts.egress
1433
1593
  });
1434
1594
  this.sandboxes = opts.sandboxes;
1435
1595
  this.commands = opts.commands;
@@ -1454,23 +1614,29 @@ var Sandbox = class _Sandbox {
1454
1614
  }
1455
1615
  if (opts.volumes) {
1456
1616
  for (const vol of opts.volumes) {
1457
- const backendsSpecified = [vol.host, vol.pvc].filter((b) => b !== void 0).length;
1617
+ const backendsSpecified = [vol.host, vol.pvc, vol.ossfs].filter((b) => b != null).length;
1458
1618
  if (backendsSpecified === 0) {
1459
1619
  throw new Error(
1460
- `Volume '${vol.name}' must specify exactly one backend (host, pvc), but none was provided.`
1620
+ `Volume '${vol.name}' must specify exactly one backend (host, pvc, ossfs), but none was provided.`
1461
1621
  );
1462
1622
  }
1463
1623
  if (backendsSpecified > 1) {
1464
1624
  throw new Error(
1465
- `Volume '${vol.name}' must specify exactly one backend (host, pvc), but multiple were provided.`
1625
+ `Volume '${vol.name}' must specify exactly one backend (host, pvc, ossfs), but multiple were provided.`
1466
1626
  );
1467
1627
  }
1468
1628
  }
1469
1629
  }
1630
+ const rawTimeout = opts.timeoutSeconds ?? DEFAULT_TIMEOUT_SECONDS;
1631
+ const timeoutSeconds = opts.timeoutSeconds === null ? null : Math.floor(rawTimeout);
1632
+ if (timeoutSeconds !== null && !Number.isFinite(timeoutSeconds)) {
1633
+ throw new Error(
1634
+ `timeoutSeconds must be a finite number, got ${opts.timeoutSeconds}`
1635
+ );
1636
+ }
1470
1637
  const req = {
1471
1638
  image: toImageSpec(opts.image),
1472
1639
  entrypoint: opts.entrypoint ?? DEFAULT_ENTRYPOINT,
1473
- timeout: Math.floor(opts.timeoutSeconds ?? DEFAULT_TIMEOUT_SECONDS),
1474
1640
  resourceLimits: opts.resource ?? DEFAULT_RESOURCE_LIMITS,
1475
1641
  env: opts.env ?? {},
1476
1642
  metadata: opts.metadata ?? {},
@@ -1481,6 +1647,9 @@ var Sandbox = class _Sandbox {
1481
1647
  volumes: opts.volumes,
1482
1648
  extensions: opts.extensions ?? {}
1483
1649
  };
1650
+ if (timeoutSeconds !== null) {
1651
+ req.timeout = timeoutSeconds;
1652
+ }
1484
1653
  let sandboxId;
1485
1654
  try {
1486
1655
  const created = await sandboxes.createSandbox(req);
@@ -1490,12 +1659,23 @@ var Sandbox = class _Sandbox {
1490
1659
  DEFAULT_EXECD_PORT,
1491
1660
  connectionConfig.useServerProxy
1492
1661
  );
1662
+ const egressEndpoint = await sandboxes.getSandboxEndpoint(
1663
+ sandboxId,
1664
+ DEFAULT_EGRESS_PORT,
1665
+ connectionConfig.useServerProxy
1666
+ );
1493
1667
  const execdBaseUrl = `${connectionConfig.protocol}://${endpoint.endpoint}`;
1668
+ const egressBaseUrl = `${connectionConfig.protocol}://${egressEndpoint.endpoint}`;
1494
1669
  const { commands, files, health, metrics } = adapterFactory.createExecdStack({
1495
1670
  connectionConfig,
1496
1671
  execdBaseUrl,
1497
1672
  endpointHeaders: endpoint.headers
1498
1673
  });
1674
+ const { egress } = adapterFactory.createEgressStack({
1675
+ connectionConfig,
1676
+ egressBaseUrl,
1677
+ endpointHeaders: egressEndpoint.headers
1678
+ });
1499
1679
  const sbx = new _Sandbox({
1500
1680
  id: sandboxId,
1501
1681
  connectionConfig,
@@ -1506,7 +1686,8 @@ var Sandbox = class _Sandbox {
1506
1686
  commands,
1507
1687
  files,
1508
1688
  health,
1509
- metrics
1689
+ metrics,
1690
+ egress
1510
1691
  });
1511
1692
  if (!(opts.skipHealthCheck ?? false)) {
1512
1693
  await sbx.waitUntilReady({
@@ -1548,12 +1729,23 @@ var Sandbox = class _Sandbox {
1548
1729
  DEFAULT_EXECD_PORT,
1549
1730
  connectionConfig.useServerProxy
1550
1731
  );
1732
+ const egressEndpoint = await sandboxes.getSandboxEndpoint(
1733
+ opts.sandboxId,
1734
+ DEFAULT_EGRESS_PORT,
1735
+ connectionConfig.useServerProxy
1736
+ );
1551
1737
  const execdBaseUrl = `${connectionConfig.protocol}://${endpoint.endpoint}`;
1738
+ const egressBaseUrl = `${connectionConfig.protocol}://${egressEndpoint.endpoint}`;
1552
1739
  const { commands, files, health, metrics } = adapterFactory.createExecdStack({
1553
1740
  connectionConfig,
1554
1741
  execdBaseUrl,
1555
1742
  endpointHeaders: endpoint.headers
1556
1743
  });
1744
+ const { egress } = adapterFactory.createEgressStack({
1745
+ connectionConfig,
1746
+ egressBaseUrl,
1747
+ endpointHeaders: egressEndpoint.headers
1748
+ });
1557
1749
  const sbx = new _Sandbox({
1558
1750
  id: opts.sandboxId,
1559
1751
  connectionConfig,
@@ -1564,7 +1756,8 @@ var Sandbox = class _Sandbox {
1564
1756
  commands,
1565
1757
  files,
1566
1758
  health,
1567
- metrics
1759
+ metrics,
1760
+ egress
1568
1761
  });
1569
1762
  if (!(opts.skipHealthCheck ?? false)) {
1570
1763
  await sbx.waitUntilReady({
@@ -1652,6 +1845,12 @@ var Sandbox = class _Sandbox {
1652
1845
  ).toISOString();
1653
1846
  return await this.sandboxes.renewSandboxExpiration(this.id, { expiresAt });
1654
1847
  }
1848
+ async getEgressPolicy() {
1849
+ return await _Sandbox._priv.get(this).egress.getPolicy();
1850
+ }
1851
+ async patchEgressRules(rules) {
1852
+ await _Sandbox._priv.get(this).egress.patchRules(rules);
1853
+ }
1655
1854
  /**
1656
1855
  * Get sandbox endpoint for a port (STRICT: no scheme), e.g. "localhost:44772" or "domain/route/.../44772".
1657
1856
  */
@@ -1671,21 +1870,39 @@ var Sandbox = class _Sandbox {
1671
1870
  }
1672
1871
  async waitUntilReady(opts) {
1673
1872
  const deadline = Date.now() + opts.readyTimeoutSeconds * 1e3;
1873
+ let attempt = 0;
1874
+ let errorDetail = "Health check returned false continuously.";
1875
+ const buildTimeoutMessage = () => {
1876
+ const context = `domain=${this.connectionConfig.domain}, useServerProxy=${this.connectionConfig.useServerProxy}`;
1877
+ let suggestion = "If this sandbox runs in Docker bridge or remote-network mode, consider enabling useServerProxy=true.";
1878
+ if (!this.connectionConfig.useServerProxy) {
1879
+ suggestion += " You can also configure server-side [docker].host_ip for direct endpoint access.";
1880
+ }
1881
+ return `Sandbox health check timed out after ${opts.readyTimeoutSeconds}s (${attempt} attempts). ${errorDetail} Connection context: ${context}. ${suggestion}`;
1882
+ };
1674
1883
  while (true) {
1675
1884
  if (Date.now() > deadline) {
1676
1885
  throw new SandboxReadyTimeoutException({
1677
- message: `Sandbox not ready: timed out waiting for health check (timeoutSeconds=${opts.readyTimeoutSeconds})`
1886
+ message: buildTimeoutMessage()
1678
1887
  });
1679
1888
  }
1889
+ attempt++;
1680
1890
  try {
1681
1891
  if (opts.healthCheck) {
1682
1892
  const ok = await opts.healthCheck(this);
1683
- if (ok) return;
1893
+ if (ok) {
1894
+ return;
1895
+ }
1684
1896
  } else {
1685
1897
  const ok = await this.health.ping();
1686
- if (ok) return;
1898
+ if (ok) {
1899
+ return;
1900
+ }
1687
1901
  }
1688
- } catch {
1902
+ errorDetail = "Health check returned false continuously.";
1903
+ } catch (err) {
1904
+ const message = err instanceof Error ? err.message : String(err);
1905
+ errorDetail = `Last health check error: ${message}`;
1689
1906
  }
1690
1907
  await sleep(opts.pollingIntervalMillis);
1691
1908
  }
@@ -1694,6 +1911,7 @@ var Sandbox = class _Sandbox {
1694
1911
  // Annotate the CommonJS export names for ESM import in node:
1695
1912
  0 && (module.exports = {
1696
1913
  ConnectionConfig,
1914
+ DEFAULT_EGRESS_PORT,
1697
1915
  DEFAULT_ENTRYPOINT,
1698
1916
  DEFAULT_EXECD_PORT,
1699
1917
  DEFAULT_HEALTH_CHECK_POLLING_INTERVAL_MILLIS,