@memohai/cloud-runtime 0.1.0 → 2026.9.12-1

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/cli.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- // @memohai/cloud-runtime 0.1.0 — bundles @memohai/runtime 0.17.0 (memoh 9bcf9aaa-dirty)
2
+ // @memohai/cloud-runtime 2026.9.12-1 — bundles @memohai/runtime 0.19.0 (memoh cc440cea)
3
3
  import { createRequire as __memohCreateRequire } from 'node:module'
4
4
  const require = __memohCreateRequire(import.meta.url)
5
5
  var __create = Object.create;
@@ -1382,14 +1382,14 @@ var require_receiver = __commonJS({
1382
1382
  * @return {(Error|RangeError)} The error
1383
1383
  * @private
1384
1384
  */
1385
- createError(ErrorCtor, message, prefix, statusCode, errorCode) {
1385
+ createError(ErrorCtor, message, prefix, statusCode, errorCode2) {
1386
1386
  this._loop = false;
1387
1387
  this._errored = true;
1388
1388
  const err = new ErrorCtor(
1389
1389
  prefix ? `Invalid WebSocket frame: ${message}` : message
1390
1390
  );
1391
1391
  Error.captureStackTrace(err, this.createError);
1392
- err.code = errorCode;
1392
+ err.code = errorCode2;
1393
1393
  err[kStatusCode] = statusCode;
1394
1394
  return err;
1395
1395
  }
@@ -2129,7 +2129,7 @@ var require_extension = __commonJS({
2129
2129
  if (dest[name] === void 0) dest[name] = [elem];
2130
2130
  else dest[name].push(elem);
2131
2131
  }
2132
- function parse(header) {
2132
+ function parse2(header) {
2133
2133
  const offers = /* @__PURE__ */ Object.create(null);
2134
2134
  let params = /* @__PURE__ */ Object.create(null);
2135
2135
  let mustUnescape = false;
@@ -2269,7 +2269,7 @@ var require_extension = __commonJS({
2269
2269
  }).join(", ");
2270
2270
  }).join(", ");
2271
2271
  }
2272
- module.exports = { format, parse };
2272
+ module.exports = { format, parse: parse2 };
2273
2273
  }
2274
2274
  });
2275
2275
 
@@ -2303,7 +2303,7 @@ var require_websocket = __commonJS({
2303
2303
  var {
2304
2304
  EventTarget: { addEventListener, removeEventListener }
2305
2305
  } = require_event_target();
2306
- var { format, parse } = require_extension();
2306
+ var { format, parse: parse2 } = require_extension();
2307
2307
  var { toBuffer } = require_buffer_util();
2308
2308
  var kAborted = /* @__PURE__ */ Symbol("kAborted");
2309
2309
  var protocolVersions = [8, 13];
@@ -2980,7 +2980,7 @@ var require_websocket = __commonJS({
2980
2980
  }
2981
2981
  let extensions;
2982
2982
  try {
2983
- extensions = parse(secWebSocketExtensions);
2983
+ extensions = parse2(secWebSocketExtensions);
2984
2984
  } catch (err) {
2985
2985
  const message = "Invalid Sec-WebSocket-Extensions header";
2986
2986
  abortHandshake(websocket, socket, message);
@@ -3231,7 +3231,7 @@ var require_stream = __commonJS({
3231
3231
  };
3232
3232
  duplex._final = function(callback) {
3233
3233
  if (ws.readyState === ws.CONNECTING) {
3234
- ws.once("open", function open2() {
3234
+ ws.once("open", function open3() {
3235
3235
  duplex._final(callback);
3236
3236
  });
3237
3237
  return;
@@ -3252,7 +3252,7 @@ var require_stream = __commonJS({
3252
3252
  };
3253
3253
  duplex._write = function(chunk, encoding, callback) {
3254
3254
  if (ws.readyState === ws.CONNECTING) {
3255
- ws.once("open", function open2() {
3255
+ ws.once("open", function open3() {
3256
3256
  duplex._write(chunk, encoding, callback);
3257
3257
  });
3258
3258
  return;
@@ -3272,7 +3272,7 @@ var require_subprotocol = __commonJS({
3272
3272
  "../memoh/node_modules/.pnpm/ws@8.21.0/node_modules/ws/lib/subprotocol.js"(exports, module) {
3273
3273
  "use strict";
3274
3274
  var { tokenChars } = require_validation();
3275
- function parse(header) {
3275
+ function parse2(header) {
3276
3276
  const protocols = /* @__PURE__ */ new Set();
3277
3277
  let start = -1;
3278
3278
  let end = -1;
@@ -3308,7 +3308,7 @@ var require_subprotocol = __commonJS({
3308
3308
  protocols.add(protocol);
3309
3309
  return protocols;
3310
3310
  }
3311
- module.exports = { parse };
3311
+ module.exports = { parse: parse2 };
3312
3312
  }
3313
3313
  });
3314
3314
 
@@ -4255,7 +4255,7 @@ var require_call_credentials = __commonJS({
4255
4255
  if (isCurrentOauth2Client(googleCredentials)) {
4256
4256
  getHeaders = googleCredentials.getRequestHeaders(options.service_url);
4257
4257
  } else {
4258
- getHeaders = new Promise((resolve2, reject) => {
4258
+ getHeaders = new Promise((resolve5, reject) => {
4259
4259
  googleCredentials.getRequestMetadata(options.service_url, (err, headers) => {
4260
4260
  if (err) {
4261
4261
  reject(err);
@@ -4265,7 +4265,7 @@ var require_call_credentials = __commonJS({
4265
4265
  reject(new Error("Headers not set by metadata plugin"));
4266
4266
  return;
4267
4267
  }
4268
- resolve2(headers);
4268
+ resolve5(headers);
4269
4269
  });
4270
4270
  });
4271
4271
  }
@@ -4318,10 +4318,10 @@ var require_call_credentials = __commonJS({
4318
4318
  this.metadataGenerator = metadataGenerator;
4319
4319
  }
4320
4320
  generateMetadata(options) {
4321
- return new Promise((resolve2, reject) => {
4321
+ return new Promise((resolve5, reject) => {
4322
4322
  this.metadataGenerator(options, (err, metadata) => {
4323
4323
  if (metadata !== void 0) {
4324
- resolve2(metadata);
4324
+ resolve5(metadata);
4325
4325
  } else {
4326
4326
  reject(err);
4327
4327
  }
@@ -4637,7 +4637,7 @@ var require_channel_credentials = __commonJS({
4637
4637
  };
4638
4638
  function getConnectionOptions(secureContext, verifyOptions, channelTarget, options) {
4639
4639
  var _a, _b;
4640
- const connectionOptions = {
4640
+ const connectionOptions2 = {
4641
4641
  secureContext
4642
4642
  };
4643
4643
  let realTarget = channelTarget;
@@ -4650,44 +4650,44 @@ var require_channel_credentials = __commonJS({
4650
4650
  const targetPath = (0, resolver_1.getDefaultAuthority)(realTarget);
4651
4651
  const hostPort = (0, uri_parser_1.splitHostPort)(targetPath);
4652
4652
  const remoteHost = (_a = hostPort === null || hostPort === void 0 ? void 0 : hostPort.host) !== null && _a !== void 0 ? _a : targetPath;
4653
- connectionOptions.host = remoteHost;
4653
+ connectionOptions2.host = remoteHost;
4654
4654
  if (verifyOptions.checkServerIdentity) {
4655
- connectionOptions.checkServerIdentity = verifyOptions.checkServerIdentity;
4655
+ connectionOptions2.checkServerIdentity = verifyOptions.checkServerIdentity;
4656
4656
  }
4657
4657
  if (verifyOptions.rejectUnauthorized !== void 0) {
4658
- connectionOptions.rejectUnauthorized = verifyOptions.rejectUnauthorized;
4658
+ connectionOptions2.rejectUnauthorized = verifyOptions.rejectUnauthorized;
4659
4659
  }
4660
- connectionOptions.ALPNProtocols = ["h2"];
4660
+ connectionOptions2.ALPNProtocols = ["h2"];
4661
4661
  if (options["grpc.ssl_target_name_override"]) {
4662
4662
  const sslTargetNameOverride = options["grpc.ssl_target_name_override"];
4663
- const originalCheckServerIdentity = (_b = connectionOptions.checkServerIdentity) !== null && _b !== void 0 ? _b : tls_1.checkServerIdentity;
4664
- connectionOptions.checkServerIdentity = (host, cert) => {
4663
+ const originalCheckServerIdentity = (_b = connectionOptions2.checkServerIdentity) !== null && _b !== void 0 ? _b : tls_1.checkServerIdentity;
4664
+ connectionOptions2.checkServerIdentity = (host, cert) => {
4665
4665
  return originalCheckServerIdentity(sslTargetNameOverride, cert);
4666
4666
  };
4667
- connectionOptions.servername = sslTargetNameOverride;
4667
+ connectionOptions2.servername = sslTargetNameOverride;
4668
4668
  } else {
4669
- connectionOptions.servername = remoteHost;
4669
+ connectionOptions2.servername = remoteHost;
4670
4670
  }
4671
4671
  if (options["grpc-node.tls_enable_trace"]) {
4672
- connectionOptions.enableTrace = true;
4672
+ connectionOptions2.enableTrace = true;
4673
4673
  }
4674
- return connectionOptions;
4674
+ return connectionOptions2;
4675
4675
  }
4676
4676
  var SecureConnectorImpl = class {
4677
- constructor(connectionOptions, callCredentials) {
4678
- this.connectionOptions = connectionOptions;
4677
+ constructor(connectionOptions2, callCredentials) {
4678
+ this.connectionOptions = connectionOptions2;
4679
4679
  this.callCredentials = callCredentials;
4680
4680
  }
4681
4681
  connect(socket) {
4682
4682
  const tlsConnectOptions = Object.assign({ socket }, this.connectionOptions);
4683
- return new Promise((resolve2, reject) => {
4683
+ return new Promise((resolve5, reject) => {
4684
4684
  const tlsSocket = (0, tls_1.connect)(tlsConnectOptions, () => {
4685
4685
  var _a;
4686
4686
  if (((_a = this.connectionOptions.rejectUnauthorized) !== null && _a !== void 0 ? _a : true) && !tlsSocket.authorized) {
4687
4687
  reject(tlsSocket.authorizationError);
4688
4688
  return;
4689
4689
  }
4690
- resolve2({
4690
+ resolve5({
4691
4691
  socket: tlsSocket,
4692
4692
  secure: true
4693
4693
  });
@@ -4726,8 +4726,8 @@ var require_channel_credentials = __commonJS({
4726
4726
  }
4727
4727
  }
4728
4728
  _createSecureConnector(channelTarget, options, callCredentials) {
4729
- const connectionOptions = getConnectionOptions(this.secureContext, this.verifyOptions, channelTarget, options);
4730
- return new SecureConnectorImpl(connectionOptions, callCredentials !== null && callCredentials !== void 0 ? callCredentials : call_credentials_1.CallCredentials.createEmpty());
4729
+ const connectionOptions2 = getConnectionOptions(this.secureContext, this.verifyOptions, channelTarget, options);
4730
+ return new SecureConnectorImpl(connectionOptions2, callCredentials !== null && callCredentials !== void 0 ? callCredentials : call_credentials_1.CallCredentials.createEmpty());
4731
4731
  }
4732
4732
  };
4733
4733
  var CertificateProviderChannelCredentialsImpl = class _CertificateProviderChannelCredentialsImpl extends ChannelCredentials {
@@ -4806,8 +4806,8 @@ var require_channel_credentials = __commonJS({
4806
4806
  if (this.hasReceivedUpdates()) {
4807
4807
  return Promise.resolve(this.getLatestSecureContext());
4808
4808
  } else {
4809
- return new Promise((resolve2) => {
4810
- this.secureContextWatchers.push(resolve2);
4809
+ return new Promise((resolve5) => {
4810
+ this.secureContextWatchers.push(resolve5);
4811
4811
  });
4812
4812
  }
4813
4813
  }
@@ -4840,7 +4840,7 @@ var require_channel_credentials = __commonJS({
4840
4840
  this.callCredentials = callCredentials;
4841
4841
  }
4842
4842
  connect(socket) {
4843
- return new Promise((resolve2, reject) => {
4843
+ return new Promise((resolve5, reject) => {
4844
4844
  const secureContext = this.parent.getLatestSecureContext();
4845
4845
  if (!secureContext) {
4846
4846
  reject(new Error("Failed to load credentials"));
@@ -4865,7 +4865,7 @@ var require_channel_credentials = __commonJS({
4865
4865
  reject(tlsSocket.authorizationError);
4866
4866
  return;
4867
4867
  }
4868
- resolve2({
4868
+ resolve5({
4869
4869
  socket: tlsSocket,
4870
4870
  secure: true
4871
4871
  });
@@ -5736,7 +5736,7 @@ var require_resolving_load_balancer = __commonJS({
5736
5736
  "SERVICE",
5737
5737
  "EMPTY"
5738
5738
  ];
5739
- function hasMatchingName(service, method, methodConfig, matchLevel) {
5739
+ function hasMatchingName(service2, method, methodConfig, matchLevel) {
5740
5740
  for (const name of methodConfig.name) {
5741
5741
  switch (matchLevel) {
5742
5742
  case "EMPTY":
@@ -5745,21 +5745,21 @@ var require_resolving_load_balancer = __commonJS({
5745
5745
  }
5746
5746
  break;
5747
5747
  case "SERVICE":
5748
- if (name.service === service && !name.method) {
5748
+ if (name.service === service2 && !name.method) {
5749
5749
  return true;
5750
5750
  }
5751
5751
  break;
5752
5752
  case "SERVICE_AND_METHOD":
5753
- if (name.service === service && name.method === method) {
5753
+ if (name.service === service2 && name.method === method) {
5754
5754
  return true;
5755
5755
  }
5756
5756
  }
5757
5757
  }
5758
5758
  return false;
5759
5759
  }
5760
- function findMatchingConfig(service, method, methodConfigs, matchLevel) {
5760
+ function findMatchingConfig(service2, method, methodConfigs, matchLevel) {
5761
5761
  for (const config of methodConfigs) {
5762
- if (hasMatchingName(service, method, config, matchLevel)) {
5762
+ if (hasMatchingName(service2, method, config, matchLevel)) {
5763
5763
  return config;
5764
5764
  }
5765
5765
  }
@@ -5770,11 +5770,11 @@ var require_resolving_load_balancer = __commonJS({
5770
5770
  invoke(methodName, metadata) {
5771
5771
  var _a, _b;
5772
5772
  const splitName = methodName.split("/").filter((x) => x.length > 0);
5773
- const service = (_a = splitName[0]) !== null && _a !== void 0 ? _a : "";
5773
+ const service2 = (_a = splitName[0]) !== null && _a !== void 0 ? _a : "";
5774
5774
  const method = (_b = splitName[1]) !== null && _b !== void 0 ? _b : "";
5775
5775
  if (serviceConfig && serviceConfig.methodConfig) {
5776
5776
  for (const matchLevel of NAME_MATCH_LEVEL_ORDER) {
5777
- const matchingConfig = findMatchingConfig(service, method, serviceConfig.methodConfig, matchLevel);
5777
+ const matchingConfig = findMatchingConfig(service2, method, serviceConfig.methodConfig, matchLevel);
5778
5778
  if (matchingConfig) {
5779
5779
  return {
5780
5780
  methodConfig: matchingConfig,
@@ -8032,7 +8032,7 @@ var require_make_client = __commonJS({
8032
8032
  const result = {};
8033
8033
  for (const serviceFqn in packageDef) {
8034
8034
  if (Object.prototype.hasOwnProperty.call(packageDef, serviceFqn)) {
8035
- const service = packageDef[serviceFqn];
8035
+ const service2 = packageDef[serviceFqn];
8036
8036
  const nameComponents = serviceFqn.split(".");
8037
8037
  if (nameComponents.some((comp) => isPrototypePolluted(comp))) {
8038
8038
  continue;
@@ -8045,10 +8045,10 @@ var require_make_client = __commonJS({
8045
8045
  }
8046
8046
  current = current[packageName];
8047
8047
  }
8048
- if (isProtobufTypeDefinition(service)) {
8049
- current[serviceName] = service;
8048
+ if (isProtobufTypeDefinition(service2)) {
8049
+ current[serviceName] = service2;
8050
8050
  } else {
8051
- current[serviceName] = makeClientConstructor(service, serviceName, {});
8051
+ current[serviceName] = makeClientConstructor(service2, serviceName, {});
8052
8052
  }
8053
8053
  }
8054
8054
  }
@@ -8441,7 +8441,7 @@ var require_aspromise = __commonJS({
8441
8441
  var params = new Array(arguments.length - 1), offset = 0, index = 2, pending = true;
8442
8442
  while (index < arguments.length)
8443
8443
  params[offset++] = arguments[index++];
8444
- return new Promise(function executor(resolve2, reject) {
8444
+ return new Promise(function executor(resolve5, reject) {
8445
8445
  params[offset] = function callback(err) {
8446
8446
  if (pending) {
8447
8447
  pending = false;
@@ -8451,7 +8451,7 @@ var require_aspromise = __commonJS({
8451
8451
  var params2 = new Array(arguments.length - 1), offset2 = 0;
8452
8452
  while (offset2 < params2.length)
8453
8453
  params2[offset2++] = arguments[offset2];
8454
- resolve2.apply(null, params2);
8454
+ resolve5.apply(null, params2);
8455
8455
  }
8456
8456
  }
8457
8457
  };
@@ -10956,13 +10956,13 @@ var require_path = __commonJS({
10956
10956
  "../memoh/node_modules/.pnpm/@protobufjs+path@1.1.2/node_modules/@protobufjs/path/index.js"(exports) {
10957
10957
  "use strict";
10958
10958
  var path = exports;
10959
- var isAbsolute3 = (
10959
+ var isAbsolute4 = (
10960
10960
  /**
10961
10961
  * Tests if the specified path is absolute.
10962
10962
  * @param {string} path Path to test
10963
10963
  * @returns {boolean} `true` if path is absolute
10964
10964
  */
10965
- path.isAbsolute = function isAbsolute4(path2) {
10965
+ path.isAbsolute = function isAbsolute5(path2) {
10966
10966
  return /^(?:\/|\w+:)/.test(path2);
10967
10967
  }
10968
10968
  );
@@ -10974,7 +10974,7 @@ var require_path = __commonJS({
10974
10974
  */
10975
10975
  path.normalize = function normalize2(path2) {
10976
10976
  path2 = path2.replace(/\\/g, "/").replace(/\/{2,}/g, "/");
10977
- var parts = path2.split("/"), absolute = isAbsolute3(path2), prefix = "";
10977
+ var parts = path2.split("/"), absolute = isAbsolute4(path2), prefix = "";
10978
10978
  if (absolute)
10979
10979
  prefix = parts.shift() + "/";
10980
10980
  for (var i = 0; i < parts.length; ) {
@@ -10993,10 +10993,10 @@ var require_path = __commonJS({
10993
10993
  return prefix + parts.join("/");
10994
10994
  }
10995
10995
  );
10996
- path.resolve = function resolve2(originPath, includePath, alreadyNormalized) {
10996
+ path.resolve = function resolve5(originPath, includePath, alreadyNormalized) {
10997
10997
  if (!alreadyNormalized)
10998
10998
  includePath = normalize(includePath);
10999
- if (isAbsolute3(includePath))
10999
+ if (isAbsolute4(includePath))
11000
11000
  return includePath;
11001
11001
  if (!alreadyNormalized)
11002
11002
  originPath = normalize(originPath);
@@ -11350,7 +11350,7 @@ var require_mapfield = __commonJS({
11350
11350
  keepComments ? this.comment : void 0
11351
11351
  ]);
11352
11352
  };
11353
- MapField.prototype.resolve = function resolve2() {
11353
+ MapField.prototype.resolve = function resolve5() {
11354
11354
  if (this.resolved)
11355
11355
  return this;
11356
11356
  if (types.mapKey[this.keyType] === void 0)
@@ -11427,7 +11427,7 @@ var require_method = __commonJS({
11427
11427
  this.parsedOptions
11428
11428
  ]);
11429
11429
  };
11430
- Method.prototype.resolve = function resolve2() {
11430
+ Method.prototype.resolve = function resolve5() {
11431
11431
  if (this.resolved)
11432
11432
  return this;
11433
11433
  this.resolvedRequestType = this.parent.lookupType(this.requestType);
@@ -11454,17 +11454,17 @@ var require_service2 = __commonJS({
11454
11454
  }
11455
11455
  Service.fromJSON = function fromJSON(name, json, depth) {
11456
11456
  depth = util.checkDepth(depth);
11457
- var service = new Service(name, json.options);
11457
+ var service2 = new Service(name, json.options);
11458
11458
  if (json.methods)
11459
11459
  for (var names = Object.keys(json.methods), i = 0; i < names.length; ++i)
11460
- service.add(Method.fromJSON(names[i], json.methods[names[i]]));
11460
+ service2.add(Method.fromJSON(names[i], json.methods[names[i]]));
11461
11461
  if (json.nested)
11462
- service.addJSON(json.nested, depth);
11462
+ service2.addJSON(json.nested, depth);
11463
11463
  if (json.edition)
11464
- service._edition = json.edition;
11465
- service.comment = json.comment;
11466
- service._defaultEdition = "proto3";
11467
- return service;
11464
+ service2._edition = json.edition;
11465
+ service2.comment = json.comment;
11466
+ service2._defaultEdition = "proto3";
11467
+ return service2;
11468
11468
  };
11469
11469
  Service.prototype.toJSON = function toJSON(toJSONOptions) {
11470
11470
  var inherited = Namespace.prototype.toJSON.call(this, toJSONOptions);
@@ -11488,9 +11488,9 @@ var require_service2 = __commonJS({
11488
11488
  return this._methodsArray || (this._methodsArray = util.toArray(this.methods));
11489
11489
  }
11490
11490
  });
11491
- function clearCache(service) {
11492
- service._methodsArray = null;
11493
- return service;
11491
+ function clearCache(service2) {
11492
+ service2._methodsArray = null;
11493
+ return service2;
11494
11494
  }
11495
11495
  Service.prototype.get = function get(name) {
11496
11496
  return Object.prototype.hasOwnProperty.call(this.methods, name) ? this.methods[name] : Namespace.prototype.get.call(this, name);
@@ -12382,7 +12382,7 @@ var require_root = __commonJS({
12382
12382
  var OneOf = require_oneof();
12383
12383
  var util = require_util();
12384
12384
  var Type;
12385
- var parse;
12385
+ var parse2;
12386
12386
  var common;
12387
12387
  function Root(options) {
12388
12388
  Namespace.call(this, "", options);
@@ -12446,8 +12446,8 @@ var require_root = __commonJS({
12446
12446
  if (!util.isString(source))
12447
12447
  self2.setOptions(source.options).addJSON(source.nested);
12448
12448
  else {
12449
- parse.filename = filename2;
12450
- var parsed = parse(source, self2, options), resolved2, i2 = 0;
12449
+ parse2.filename = filename2;
12450
+ var parsed = parse2(source, self2, options), resolved2, i2 = 0;
12451
12451
  if (parsed.imports) {
12452
12452
  for (; i2 < parsed.imports.length; ++i2)
12453
12453
  if (resolved2 = getBundledFileName(parsed.imports[i2]) || self2.resolvePath(filename2, parsed.imports[i2]))
@@ -12620,7 +12620,7 @@ var require_root = __commonJS({
12620
12620
  };
12621
12621
  Root._configure = function(Type_, parse_, common_) {
12622
12622
  Type = Type_;
12623
- parse = parse_;
12623
+ parse2 = parse_;
12624
12624
  common = common_;
12625
12625
  };
12626
12626
  }
@@ -13040,7 +13040,7 @@ var require_field = __commonJS({
13040
13040
  keepComments ? this.comment : void 0
13041
13041
  ]);
13042
13042
  };
13043
- Field.prototype.resolve = function resolve2() {
13043
+ Field.prototype.resolve = function resolve5() {
13044
13044
  if (this.resolved)
13045
13045
  return this;
13046
13046
  if ((this.typeDefault = types.defaults[this.type]) === void 0) {
@@ -13313,7 +13313,7 @@ var require_object = __commonJS({
13313
13313
  this.parent = null;
13314
13314
  this.resolved = false;
13315
13315
  };
13316
- ReflectionObject.prototype.resolve = function resolve2() {
13316
+ ReflectionObject.prototype.resolve = function resolve5() {
13317
13317
  if (this.resolved)
13318
13318
  return this;
13319
13319
  if (this.root instanceof Root)
@@ -13893,9 +13893,9 @@ var require_tokenize = __commonJS({
13893
13893
  var require_parse = __commonJS({
13894
13894
  "../memoh/node_modules/.pnpm/protobufjs@7.6.5/node_modules/protobufjs/src/parse.js"(exports, module) {
13895
13895
  "use strict";
13896
- module.exports = parse;
13897
- parse.filename = null;
13898
- parse.defaults = { keepCase: false };
13896
+ module.exports = parse2;
13897
+ parse2.filename = null;
13898
+ parse2.defaults = { keepCase: false };
13899
13899
  var tokenize = require_tokenize();
13900
13900
  var Root = require_root();
13901
13901
  var Type = require_type();
@@ -13917,13 +13917,13 @@ var require_parse = __commonJS({
13917
13917
  var numberRe = util.patterns.numberRe;
13918
13918
  var nameRe = /^[a-zA-Z_][a-zA-Z_0-9]*$/;
13919
13919
  var typeRefRe = util.patterns.typeRefRe;
13920
- function parse(source, root, options) {
13920
+ function parse2(source, root, options) {
13921
13921
  if (!(root instanceof Root)) {
13922
13922
  options = root;
13923
13923
  root = new Root();
13924
13924
  }
13925
13925
  if (!options)
13926
- options = parse.defaults;
13926
+ options = parse2.defaults;
13927
13927
  var preferTrailingComment = options.preferTrailingComment || false;
13928
13928
  var tn = tokenize(source, options.alternateCommentMode || false), next = tn.next, push = tn.push, peek = tn.peek, skip = tn.skip, cmnt = tn.cmnt;
13929
13929
  var head = true, pkg, imports, weakImports, edition = "proto2";
@@ -13943,9 +13943,9 @@ var require_parse = __commonJS({
13943
13943
  });
13944
13944
  }
13945
13945
  function illegal(token2, name, insideTryCatch) {
13946
- var filename = parse.filename;
13946
+ var filename = parse2.filename;
13947
13947
  if (!insideTryCatch)
13948
- parse.filename = null;
13948
+ parse2.filename = null;
13949
13949
  return Error("illegal " + (name || "token") + " '" + token2 + "' (" + (filename ? filename + ", " : "") + "line " + tn.line + ")");
13950
13950
  }
13951
13951
  function readString() {
@@ -14145,7 +14145,7 @@ var require_parse = __commonJS({
14145
14145
  if (typeof obj.comment !== "string") {
14146
14146
  obj.comment = cmnt();
14147
14147
  }
14148
- obj.filename = parse.filename;
14148
+ obj.filename = parse2.filename;
14149
14149
  }
14150
14150
  if (skip("{", true)) {
14151
14151
  var token2;
@@ -14271,7 +14271,7 @@ var require_parse = __commonJS({
14271
14271
  var type = new Type(name);
14272
14272
  type.group = true;
14273
14273
  var field = new Field(fieldName, id, name, rule);
14274
- field.filename = parse.filename;
14274
+ field.filename = parse2.filename;
14275
14275
  ifBlock(type, function parseGroup_block(token2) {
14276
14276
  switch (token2) {
14277
14277
  case "option":
@@ -14510,19 +14510,19 @@ var require_parse = __commonJS({
14510
14510
  throw Error("max depth exceeded");
14511
14511
  if (!nameRe.test(token2 = next()))
14512
14512
  throw illegal(token2, "service name");
14513
- var service = new Service(token2);
14514
- ifBlock(service, function parseService_block(token3) {
14515
- if (parseCommon(service, token3, depth)) {
14513
+ var service2 = new Service(token2);
14514
+ ifBlock(service2, function parseService_block(token3) {
14515
+ if (parseCommon(service2, token3, depth)) {
14516
14516
  return;
14517
14517
  }
14518
14518
  if (token3 === "rpc")
14519
- parseMethod(service, token3);
14519
+ parseMethod(service2, token3);
14520
14520
  else
14521
14521
  throw illegal(token3);
14522
14522
  });
14523
- parent.add(service);
14523
+ parent.add(service2);
14524
14524
  if (parent === ptr) {
14525
- topLevelObjects.push(service);
14525
+ topLevelObjects.push(service2);
14526
14526
  }
14527
14527
  }
14528
14528
  function parseMethod(parent, token2) {
@@ -14619,7 +14619,7 @@ var require_parse = __commonJS({
14619
14619
  }
14620
14620
  }
14621
14621
  resolveFileFeatures();
14622
- parse.filename = null;
14622
+ parse2.filename = null;
14623
14623
  return {
14624
14624
  "package": pkg,
14625
14625
  "imports": imports,
@@ -16706,13 +16706,13 @@ var require_descriptor2 = __commonJS({
16706
16706
  Service.fromDescriptor = function fromDescriptor(descriptor, edition, nested) {
16707
16707
  if (typeof descriptor.length === "number")
16708
16708
  descriptor = exports.ServiceDescriptorProto.decode(descriptor);
16709
- var service = new Service(descriptor.name && descriptor.name.length ? descriptor.name : "Service" + unnamedServiceIndex++, fromDescriptorOptions(descriptor.options, exports.ServiceOptions));
16709
+ var service2 = new Service(descriptor.name && descriptor.name.length ? descriptor.name : "Service" + unnamedServiceIndex++, fromDescriptorOptions(descriptor.options, exports.ServiceOptions));
16710
16710
  if (!nested)
16711
- service._edition = edition;
16711
+ service2._edition = edition;
16712
16712
  if (descriptor.method)
16713
16713
  for (var i = 0; i < descriptor.method.length; ++i)
16714
- service.add(Method.fromDescriptor(descriptor.method[i]));
16715
- return service;
16714
+ service2.add(Method.fromDescriptor(descriptor.method[i]));
16715
+ return service2;
16716
16716
  };
16717
16717
  Service.prototype.toDescriptor = function toDescriptor() {
16718
16718
  var methods = [];
@@ -17496,9 +17496,9 @@ var require_src2 = __commonJS({
17496
17496
  options: mapMethodOptions(method.parsedOptions)
17497
17497
  };
17498
17498
  }
17499
- function createServiceDefinition(service, name, options, fileDescriptors) {
17499
+ function createServiceDefinition(service2, name, options, fileDescriptors) {
17500
17500
  const def = {};
17501
- for (const method of service.methodsArray) {
17501
+ for (const method of service2.methodsArray) {
17502
17502
  def[method.name] = createMethodDefinition(method, name, options, fileDescriptors);
17503
17503
  }
17504
17504
  return def;
@@ -18284,18 +18284,18 @@ var require_compression_filter = __commonJS({
18284
18284
  this.maxRecvMessageLength = maxRecvMessageLength;
18285
18285
  }
18286
18286
  compressMessage(message) {
18287
- return new Promise((resolve2, reject) => {
18287
+ return new Promise((resolve5, reject) => {
18288
18288
  zlib.deflate(message, (err, output) => {
18289
18289
  if (err) {
18290
18290
  reject(err);
18291
18291
  } else {
18292
- resolve2(output);
18292
+ resolve5(output);
18293
18293
  }
18294
18294
  });
18295
18295
  });
18296
18296
  }
18297
18297
  decompressMessage(message) {
18298
- return new Promise((resolve2, reject) => {
18298
+ return new Promise((resolve5, reject) => {
18299
18299
  let totalLength = 0;
18300
18300
  const messageParts = [];
18301
18301
  const decompresser = zlib.createInflate();
@@ -18317,7 +18317,7 @@ var require_compression_filter = __commonJS({
18317
18317
  }
18318
18318
  });
18319
18319
  decompresser.on("end", () => {
18320
- resolve2(Buffer.concat(messageParts));
18320
+ resolve5(Buffer.concat(messageParts));
18321
18321
  });
18322
18322
  decompresser.write(message);
18323
18323
  decompresser.end();
@@ -18330,18 +18330,18 @@ var require_compression_filter = __commonJS({
18330
18330
  this.maxRecvMessageLength = maxRecvMessageLength;
18331
18331
  }
18332
18332
  compressMessage(message) {
18333
- return new Promise((resolve2, reject) => {
18333
+ return new Promise((resolve5, reject) => {
18334
18334
  zlib.gzip(message, (err, output) => {
18335
18335
  if (err) {
18336
18336
  reject(err);
18337
18337
  } else {
18338
- resolve2(output);
18338
+ resolve5(output);
18339
18339
  }
18340
18340
  });
18341
18341
  });
18342
18342
  }
18343
18343
  decompressMessage(message) {
18344
- return new Promise((resolve2, reject) => {
18344
+ return new Promise((resolve5, reject) => {
18345
18345
  let totalLength = 0;
18346
18346
  const messageParts = [];
18347
18347
  const decompresser = zlib.createGunzip();
@@ -18363,7 +18363,7 @@ var require_compression_filter = __commonJS({
18363
18363
  }
18364
18364
  });
18365
18365
  decompresser.on("end", () => {
18366
- resolve2(Buffer.concat(messageParts));
18366
+ resolve5(Buffer.concat(messageParts));
18367
18367
  });
18368
18368
  decompresser.write(message);
18369
18369
  decompresser.end();
@@ -19698,7 +19698,7 @@ var require_http_proxy = __commonJS({
19698
19698
  options.headers = headers;
19699
19699
  const proxyAddressString = (0, subchannel_address_1.subchannelAddressToString)(address);
19700
19700
  trace("Using proxy " + proxyAddressString + " to connect to " + options.path);
19701
- return new Promise((resolve2, reject) => {
19701
+ return new Promise((resolve5, reject) => {
19702
19702
  const request = http.request(options);
19703
19703
  request.once("connect", (res, socket, head) => {
19704
19704
  request.removeAllListeners();
@@ -19709,7 +19709,7 @@ var require_http_proxy = __commonJS({
19709
19709
  socket.unshift(head);
19710
19710
  }
19711
19711
  trace("Successfully established a plaintext connection to " + options.path + " through proxy " + proxyAddressString);
19712
- resolve2(socket);
19712
+ resolve5(socket);
19713
19713
  } else {
19714
19714
  (0, logging_1.log)(constants_1.LogVerbosity.ERROR, "Failed to connect to " + options.path + " through proxy " + proxyAddressString + " with status " + res.statusCode);
19715
19715
  reject();
@@ -20289,12 +20289,12 @@ var require_transport = __commonJS({
20289
20289
  this.trace("session closed");
20290
20290
  this.handleDisconnect();
20291
20291
  });
20292
- session.once("goaway", (errorCode, lastStreamID, opaqueData) => {
20292
+ session.once("goaway", (errorCode2, lastStreamID, opaqueData) => {
20293
20293
  let tooManyPings = false;
20294
- if (errorCode === http2.constants.NGHTTP2_ENHANCE_YOUR_CALM && opaqueData && opaqueData.equals(tooManyPingsData)) {
20294
+ if (errorCode2 === http2.constants.NGHTTP2_ENHANCE_YOUR_CALM && opaqueData && opaqueData.equals(tooManyPingsData)) {
20295
20295
  tooManyPings = true;
20296
20296
  }
20297
- this.trace("connection closed by GOAWAY with code " + errorCode + " and data " + (opaqueData === null || opaqueData === void 0 ? void 0 : opaqueData.toString()));
20297
+ this.trace("connection closed by GOAWAY with code " + errorCode2 + " and data " + (opaqueData === null || opaqueData === void 0 ? void 0 : opaqueData.toString()));
20298
20298
  this.reportDisconnectToOwner(tooManyPings);
20299
20299
  });
20300
20300
  session.once("error", (error) => {
@@ -20609,7 +20609,7 @@ var require_transport = __commonJS({
20609
20609
  if (secureConnectResult.socket.closed) {
20610
20610
  return Promise.reject("Connection closed before starting HTTP/2 handshake");
20611
20611
  }
20612
- return new Promise((resolve2, reject) => {
20612
+ return new Promise((resolve5, reject) => {
20613
20613
  var _a, _b, _c, _d, _e, _f, _g, _h;
20614
20614
  let remoteName = null;
20615
20615
  let realTarget = this.channelTarget;
@@ -20678,7 +20678,7 @@ var require_transport = __commonJS({
20678
20678
  session.removeAllListeners();
20679
20679
  secureConnectResult.socket.removeListener("close", closeHandler);
20680
20680
  secureConnectResult.socket.removeListener("error", errorHandler);
20681
- resolve2(new Http2Transport(session, address, options, remoteName));
20681
+ resolve5(new Http2Transport(session, address, options, remoteName));
20682
20682
  this.session = null;
20683
20683
  });
20684
20684
  session.once("close", closeHandler);
@@ -20692,7 +20692,7 @@ var require_transport = __commonJS({
20692
20692
  if (proxiedSocket) {
20693
20693
  return proxiedSocket;
20694
20694
  } else {
20695
- return new Promise((resolve2, reject) => {
20695
+ return new Promise((resolve5, reject) => {
20696
20696
  const closeCallback = () => {
20697
20697
  reject(new Error("Socket closed"));
20698
20698
  };
@@ -20702,7 +20702,7 @@ var require_transport = __commonJS({
20702
20702
  const socket = net.connect(address, () => {
20703
20703
  socket.removeListener("close", closeCallback);
20704
20704
  socket.removeListener("error", errorCallback);
20705
- resolve2(socket);
20705
+ resolve5(socket);
20706
20706
  });
20707
20707
  socket.once("close", closeCallback);
20708
20708
  socket.once("error", errorCallback);
@@ -24004,7 +24004,7 @@ var require_server_interceptors = __commonJS({
24004
24004
  } else {
24005
24005
  decompresser = zlib.createGunzip();
24006
24006
  }
24007
- return new Promise((resolve2, reject) => {
24007
+ return new Promise((resolve5, reject) => {
24008
24008
  let totalLength = 0;
24009
24009
  const messageParts = [];
24010
24010
  decompresser.on("error", (error) => {
@@ -24025,7 +24025,7 @@ var require_server_interceptors = __commonJS({
24025
24025
  }
24026
24026
  });
24027
24027
  decompresser.on("end", () => {
24028
- resolve2(Buffer.concat(messageParts));
24028
+ resolve5(Buffer.concat(messageParts));
24029
24029
  });
24030
24030
  decompresser.write(messageContents);
24031
24031
  decompresser.end();
@@ -24485,16 +24485,16 @@ var require_server = __commonJS({
24485
24485
  addProtoService() {
24486
24486
  throw new Error("Not implemented. Use addService() instead");
24487
24487
  }
24488
- addService(service, implementation) {
24489
- if (service === null || typeof service !== "object" || implementation === null || typeof implementation !== "object") {
24488
+ addService(service2, implementation) {
24489
+ if (service2 === null || typeof service2 !== "object" || implementation === null || typeof implementation !== "object") {
24490
24490
  throw new Error("addService() requires two objects as arguments");
24491
24491
  }
24492
- const serviceKeys = Object.keys(service);
24492
+ const serviceKeys = Object.keys(service2);
24493
24493
  if (serviceKeys.length === 0) {
24494
24494
  throw new Error("Cannot add an empty service to a server");
24495
24495
  }
24496
24496
  serviceKeys.forEach((name) => {
24497
- const attrs = service[name];
24497
+ const attrs = service2[name];
24498
24498
  let methodType;
24499
24499
  if (attrs.requestStream) {
24500
24500
  if (attrs.responseStream) {
@@ -24525,13 +24525,13 @@ var require_server = __commonJS({
24525
24525
  }
24526
24526
  });
24527
24527
  }
24528
- removeService(service) {
24529
- if (service === null || typeof service !== "object") {
24528
+ removeService(service2) {
24529
+ if (service2 === null || typeof service2 !== "object") {
24530
24530
  throw new Error("removeService() requires object as argument");
24531
24531
  }
24532
- const serviceKeys = Object.keys(service);
24532
+ const serviceKeys = Object.keys(service2);
24533
24533
  serviceKeys.forEach((name) => {
24534
- const attrs = service[name];
24534
+ const attrs = service2[name];
24535
24535
  this.unregister(attrs.path);
24536
24536
  });
24537
24537
  }
@@ -24615,10 +24615,10 @@ var require_server = __commonJS({
24615
24615
  bindOneAddress(address, boundPortObject) {
24616
24616
  this.trace("Attempting to bind " + (0, subchannel_address_1.subchannelAddressToString)(address));
24617
24617
  const http2Server = this.createHttp2Server(boundPortObject.credentials);
24618
- return new Promise((resolve2, reject) => {
24618
+ return new Promise((resolve5, reject) => {
24619
24619
  const onError = (err) => {
24620
24620
  this.trace("Failed to bind " + (0, subchannel_address_1.subchannelAddressToString)(address) + " with error " + err.message);
24621
- resolve2({
24621
+ resolve5({
24622
24622
  port: "port" in address ? address.port : 1,
24623
24623
  error: err.message
24624
24624
  });
@@ -24646,7 +24646,7 @@ var require_server = __commonJS({
24646
24646
  });
24647
24647
  boundPortObject.listeningServers.add(http2Server);
24648
24648
  this.trace("Successfully bound " + (0, subchannel_address_1.subchannelAddressToString)(boundSubchannelAddress));
24649
- resolve2({
24649
+ resolve5({
24650
24650
  port: "port" in boundSubchannelAddress ? boundSubchannelAddress.port : 1
24651
24651
  });
24652
24652
  http2Server.removeListener("error", onError);
@@ -24699,7 +24699,7 @@ var require_server = __commonJS({
24699
24699
  }
24700
24700
  }
24701
24701
  resolvePort(port) {
24702
- return new Promise((resolve2, reject) => {
24702
+ return new Promise((resolve5, reject) => {
24703
24703
  let seenResolution = false;
24704
24704
  const resolverListener = (endpointList, attributes, serviceConfig, resolutionNote) => {
24705
24705
  if (seenResolution) {
@@ -24715,7 +24715,7 @@ var require_server = __commonJS({
24715
24715
  reject(new Error(`No addresses resolved for port ${port}`));
24716
24716
  return true;
24717
24717
  }
24718
- resolve2(addressList);
24718
+ resolve5(addressList);
24719
24719
  return true;
24720
24720
  };
24721
24721
  const resolver = (0, resolver_1.createResolver)(port, resolverListener, this.options);
@@ -27894,9 +27894,9 @@ var require_src3 = __commonJS({
27894
27894
  });
27895
27895
 
27896
27896
  // src/build-info.ts
27897
- var cloudRuntimeVersion = true ? "0.1.0" : unknownStamp;
27898
- var upstreamRuntimeVersion = true ? "0.17.0" : unknownStamp;
27899
- var upstreamRuntimeCommit = true ? "9bcf9aaa-dirty" : unknownStamp;
27897
+ var cloudRuntimeVersion = true ? "2026.9.12-1" : unknownStamp;
27898
+ var upstreamRuntimeVersion = true ? "0.19.0" : unknownStamp;
27899
+ var upstreamRuntimeCommit = true ? "cc440cea" : unknownStamp;
27900
27900
  function versionReport() {
27901
27901
  return [
27902
27902
  `@memohai/cloud-runtime ${cloudRuntimeVersion}`,
@@ -27908,6 +27908,9 @@ function versionReport() {
27908
27908
  var cloudDefaultServerUrl = "https://app.memoh.net/api/memoh";
27909
27909
  var serverUrlEnvVar = "MEMOH_RUNTIME_SERVER";
27910
27910
  function applyCloudDefaults(args2, env) {
27911
+ if (!args2.some((arg) => arg === "--key" || arg.startsWith("--key=")) && !env.MEMOH_RUNTIME_KEY?.trim()) {
27912
+ return;
27913
+ }
27911
27914
  if (args2.some((arg) => arg === "--server" || arg.startsWith("--server="))) {
27912
27915
  return;
27913
27916
  }
@@ -27916,11 +27919,28 @@ function applyCloudDefaults(args2, env) {
27916
27919
  }
27917
27920
  env[serverUrlEnvVar] = cloudDefaultServerUrl;
27918
27921
  }
27919
- function cloudUsage() {
27922
+ function cloudUsage(args2 = []) {
27923
+ const command = args2[0];
27924
+ if (command === "service") {
27925
+ return "Usage: memoh-cloud-runtime service <install|start|stop|restart|status|uninstall>\nInstall registers a stopped service. Use enroll first; status supports --json.";
27926
+ }
27927
+ if (command === "enroll") {
27928
+ return `Usage: memoh-cloud-runtime enroll --key <key> [--server <url>] [--team-id <uuid>] [--replace]
27929
+ Or use enroll --config <file>. Saves enrollment for future runs.
27930
+ --server defaults to ${cloudDefaultServerUrl} (or $MEMOH_RUNTIME_SERVER).`;
27931
+ }
27932
+ if (command === "run") {
27933
+ return `Usage: memoh-cloud-runtime run [--key <key> [--server <url>] | --config <file>] [--log <file>]
27934
+ Reads saved enrollment when no new connection is supplied. Never saves or changes a service.
27935
+ --server defaults to ${cloudDefaultServerUrl} when a new key is supplied.`;
27936
+ }
27920
27937
  return [
27921
- "Usage: memoh-cloud-runtime --key <key> [--team-id <uuid>] [--server <url>] [--insecure-localhost]",
27938
+ "Usage: memoh-cloud-runtime <enroll|run|service|version>",
27939
+ " memoh-cloud-runtime --key <key> [--team-id <uuid>] [--server <url>] [--insecure-localhost]",
27922
27940
  "",
27923
27941
  "Connects this machine to Memoh Cloud as a remote runtime.",
27942
+ "Use <command> --help for enrollment and user-service commands.",
27943
+ "Running without connection flags reads the saved enrollment.",
27924
27944
  "",
27925
27945
  ` --server defaults to ${cloudDefaultServerUrl}`,
27926
27946
  " (or $MEMOH_RUNTIME_SERVER when exported)",
@@ -27932,33 +27952,227 @@ function cloudUsage() {
27932
27952
 
27933
27953
  // src/bootstrap.ts
27934
27954
  var args = process.argv.slice(2);
27935
- if (args.includes("--help") || args.includes("-h")) {
27936
- console.error(cloudUsage());
27955
+ if (args[0] === "help" || args.includes("--help") || args.includes("-h")) {
27956
+ console.error(cloudUsage(args));
27937
27957
  process.exit(0);
27938
27958
  }
27939
- if (args.includes("--version") || args.includes("-v")) {
27959
+ if (args[0] === "version" || args.includes("--version") || args.includes("-v")) {
27940
27960
  console.log(versionReport());
27941
27961
  process.exit(0);
27942
27962
  }
27943
27963
  applyCloudDefaults(args, process.env);
27944
27964
 
27945
- // ../memoh/packages/runtime/src/cli.ts
27965
+ // ../memoh/packages/runtime/src/cli-main.ts
27966
+ import { access as access5, appendFile, mkdir as mkdir4 } from "node:fs/promises";
27967
+ import { homedir as homedir2 } from "node:os";
27968
+ import { dirname as dirname6, join as join6, resolve as resolve4 } from "node:path";
27969
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
27970
+ import { parseArgs } from "node:util";
27971
+
27972
+ // ../memoh/packages/runtime/src/daemon/index.ts
27973
+ import { access as access2, stat } from "node:fs/promises";
27974
+ import { constants as constants2 } from "node:fs";
27975
+ import { isAbsolute as isAbsolute2, join as join3 } from "node:path";
27976
+ import { setTimeout as sleep2 } from "node:timers/promises";
27977
+
27978
+ // ../memoh/packages/runtime/src/daemon/launchd.ts
27979
+ import { access, rm as rm2 } from "node:fs/promises";
27980
+ import { setTimeout as sleep } from "node:timers/promises";
27981
+
27982
+ // ../memoh/packages/runtime/src/runtime-config.ts
27983
+ import { lstat as lstat2 } from "node:fs/promises";
27946
27984
  import { homedir } from "node:os";
27985
+ import { dirname as dirname2, join as join2, resolve as resolve2 } from "node:path";
27947
27986
 
27948
- // ../memoh/packages/runtime/src/session.ts
27949
- import { arch, hostname, platform } from "node:os";
27950
- import { realpath as realpath2 } from "node:fs/promises";
27987
+ // ../memoh/packages/runtime/src/secure-files.ts
27988
+ import { execFile as execFile2 } from "node:child_process";
27989
+ import { promisify as promisify2 } from "node:util";
27990
+ import { randomUUID } from "node:crypto";
27991
+ import { constants } from "node:fs";
27992
+ import { chmod, lstat, mkdir, open, realpath, rename, rm } from "node:fs/promises";
27993
+ import { userInfo } from "node:os";
27994
+ import { dirname, join, parse, resolve } from "node:path";
27951
27995
 
27952
- // ../memoh/node_modules/.pnpm/ws@8.21.0/node_modules/ws/wrapper.mjs
27953
- var import_stream = __toESM(require_stream(), 1);
27954
- var import_extension = __toESM(require_extension(), 1);
27955
- var import_permessage_deflate = __toESM(require_permessage_deflate(), 1);
27956
- var import_receiver = __toESM(require_receiver(), 1);
27957
- var import_sender = __toESM(require_sender(), 1);
27958
- var import_subprotocol = __toESM(require_subprotocol(), 1);
27959
- var import_websocket = __toESM(require_websocket(), 1);
27960
- var import_websocket_server = __toESM(require_websocket_server(), 1);
27961
- var wrapper_default = import_websocket.default;
27996
+ // ../memoh/packages/runtime/src/windows-file-security.ts
27997
+ import { execFile } from "node:child_process";
27998
+ import { promisify } from "node:util";
27999
+
28000
+ // ../memoh/packages/runtime/src/windows-powershell.ts
28001
+ function windowsPowerShellEnv(env = process.env) {
28002
+ return Object.fromEntries(Object.entries(env).filter(([key]) => key.toUpperCase() !== "PSMODULEPATH"));
28003
+ }
28004
+
28005
+ // ../memoh/packages/runtime/src/windows-file-security.ts
28006
+ async function powershell(script) {
28007
+ await promisify(execFile)("powershell.exe", ["-NoProfile", "-NonInteractive", "-EncodedCommand", Buffer.from(script, "utf16le").toString("base64")], { timeout: 3e4, windowsHide: true, env: windowsPowerShellEnv() });
28008
+ }
28009
+ async function checkWindowsAccess(path, privateFile) {
28010
+ await powershell(`
28011
+ $ErrorActionPreference = 'Stop'
28012
+ $path = '${path.replaceAll("'", "''")}'
28013
+ $sid = [Security.Principal.WindowsIdentity]::GetCurrent().User.Value
28014
+ $trusted = @($sid, 'S-1-5-18', 'S-1-5-32-544', 'S-1-5-80-956008885-3418522649-1831038044-1853292631-2271478464')
28015
+ $item = Get-Item -LiteralPath $path -Force
28016
+ $leaf = $true
28017
+ while ($null -ne $item) {
28018
+ if ($item.Attributes -band [IO.FileAttributes]::ReparsePoint) { throw 'Runtime path cannot be a reparse point' }
28019
+ $acl = Get-Acl -LiteralPath $item.FullName
28020
+ if ($trusted -notcontains $acl.GetOwner([Security.Principal.SecurityIdentifier]).Value) { throw 'Runtime path has an untrusted owner' }
28021
+ foreach ($rule in $acl.GetAccessRules($true, $true, [Security.Principal.SecurityIdentifier])) {
28022
+ if ($rule.AccessControlType -ne 'Allow' -or $trusted -contains $rule.IdentityReference.Value -or ($rule.PropagationFlags -band [Security.AccessControl.PropagationFlags]::InheritOnly)) { continue }
28023
+ $unsafe = [Security.AccessControl.FileSystemRights]::Delete -bor [Security.AccessControl.FileSystemRights]::DeleteSubdirectoriesAndFiles -bor [Security.AccessControl.FileSystemRights]::ChangePermissions -bor [Security.AccessControl.FileSystemRights]::TakeOwnership
28024
+ if ($leaf) {
28025
+ $unsafe = $unsafe -bor [Security.AccessControl.FileSystemRights]::Write -bor [Security.AccessControl.FileSystemRights]::Delete
28026
+ if (${privateFile ? "$true" : "$false"}) { $unsafe = $unsafe -bor [Security.AccessControl.FileSystemRights]::ReadData }
28027
+ }
28028
+ if ($rule.FileSystemRights -band $unsafe) { throw 'Runtime path grants unsafe access to another account' }
28029
+ }
28030
+ $leaf = $false
28031
+ if ($item -is [IO.FileInfo]) { $item = $item.Directory } else { $item = $item.Parent }
28032
+ }
28033
+ `);
28034
+ }
28035
+ async function protectWindowsFile(path) {
28036
+ await protectWindowsPath(path, false);
28037
+ }
28038
+ async function protectWindowsDirectory(path) {
28039
+ await protectWindowsPath(path, true);
28040
+ }
28041
+ async function protectWindowsPath(path, directory) {
28042
+ const inheritance = directory ? "'ContainerInherit,ObjectInherit', 'None', " : "";
28043
+ await powershell(`
28044
+ $ErrorActionPreference = 'Stop'
28045
+ $sid = [Security.Principal.WindowsIdentity]::GetCurrent().User
28046
+ $acl = New-Object Security.AccessControl.${directory ? "DirectorySecurity" : "FileSecurity"}
28047
+ $acl.SetOwner($sid)
28048
+ $acl.SetAccessRuleProtection($true, $false)
28049
+ foreach ($identity in @($sid.Value, 'S-1-5-18', 'S-1-5-32-544')) {
28050
+ $principal = New-Object Security.Principal.SecurityIdentifier($identity)
28051
+ $rule = New-Object Security.AccessControl.FileSystemAccessRule($principal, 'FullControl', ${inheritance}'Allow')
28052
+ $acl.AddAccessRule($rule)
28053
+ }
28054
+ # The .NET API persists the modified owner/DACL without requesting SACL access.
28055
+ [IO.${directory ? "Directory" : "File"}]::SetAccessControl('${path.replaceAll("'", "''")}', $acl)
28056
+ `);
28057
+ }
28058
+
28059
+ // ../memoh/packages/runtime/src/secure-files.ts
28060
+ var directoryWriteRights = ["write", "append", "delete", "add_file", "add_subdirectory", "writeattr", "writeextattr", "writesecurity", "chown", "delete_child"];
28061
+ var fileWriteRights = ["write", "append", "delete", "writeattr", "writeextattr", "writesecurity", "chown"];
28062
+ var privateFileRights = ["read", ...fileWriteRights];
28063
+ async function checkDirectory(path) {
28064
+ const absolute = resolve(path);
28065
+ if (process.platform === "win32") {
28066
+ const info = await lstat(absolute);
28067
+ if (!info.isDirectory() || info.isSymbolicLink()) throw new Error(`unsafe runtime directory: ${absolute}`);
28068
+ return checkWindowsAccess(absolute, false);
28069
+ }
28070
+ const checked = /* @__PURE__ */ new Set();
28071
+ await inspectDirectory(absolute, checked);
28072
+ if (process.platform === "darwin" && await grantsOthers([...checked], directoryWriteRights)) {
28073
+ throw new Error(`runtime directory chain has a writable extended ACL: ${absolute}`);
28074
+ }
28075
+ }
28076
+ async function grantsOthers(paths, rights) {
28077
+ const { stdout } = await promisify2(execFile2)("/bin/ls", ["-lde", ...paths]);
28078
+ const self2 = userInfo().username;
28079
+ for (const line of stdout.split("\n").filter((line2) => /^\s*\d+:/.test(line2))) {
28080
+ const entry = line.match(/^\s*\d+:\s+(user|group):(.+?)\s+(?:inherited\s+)?(allow|deny)\s+(\S+)\s*$/);
28081
+ if (!entry) throw new Error(`could not verify macOS ACL entry for runtime path: ${paths.join(", ")}`);
28082
+ const [, kind, principal, access6, granted] = entry;
28083
+ if (access6 === "deny") continue;
28084
+ if (kind === "user" && principal === self2) continue;
28085
+ if (granted.split(",").some((right) => rights.includes(right))) return true;
28086
+ }
28087
+ return false;
28088
+ }
28089
+ async function inspectDirectory(absolute, checked) {
28090
+ if (checked.has(absolute)) return;
28091
+ checked.add(absolute);
28092
+ const parent = dirname(absolute);
28093
+ if (parent !== absolute) await inspectDirectory(parent, checked);
28094
+ const info = await lstat(absolute);
28095
+ if (info.isSymbolicLink() && info.uid === 0) {
28096
+ return inspectDirectory(await realpath(absolute), checked);
28097
+ }
28098
+ if (!info.isDirectory() || info.isSymbolicLink()) throw new Error(`unsafe runtime directory: ${absolute}`);
28099
+ const uid = process.getuid();
28100
+ const trustedOwner = info.uid === uid || info.uid === 0;
28101
+ const stickyRoot = info.uid === 0 && (info.mode & 512) !== 0;
28102
+ if (!trustedOwner || (info.mode & 18) !== 0 && !stickyRoot) {
28103
+ throw new Error(`runtime directory must be owned by you or root and not writable by other users: ${absolute}`);
28104
+ }
28105
+ }
28106
+ async function ensureDirectory(path) {
28107
+ const absolute = resolve(path);
28108
+ try {
28109
+ await checkDirectory(absolute);
28110
+ } catch (error) {
28111
+ if (errorCode(error) !== "ENOENT") throw error;
28112
+ const parent = dirname(absolute);
28113
+ if (parent === absolute || absolute === parse(absolute).root) throw error;
28114
+ await ensureDirectory(parent);
28115
+ try {
28116
+ await mkdir(absolute, { mode: 448 });
28117
+ } catch (error2) {
28118
+ if (errorCode(error2) !== "EEXIST") throw error2;
28119
+ }
28120
+ await checkDirectory(absolute);
28121
+ }
28122
+ }
28123
+ async function readPrivateFile(path) {
28124
+ await checkDirectory(dirname(path));
28125
+ const info = await lstat(path);
28126
+ if (!info.isFile() || info.isSymbolicLink() || process.platform !== "win32" && (info.uid !== process.getuid() || (info.mode & 63) !== 0)) {
28127
+ throw new Error(`runtime file must be a private regular file owned by you: ${path}`);
28128
+ }
28129
+ if (process.platform === "darwin" && await grantsOthers([path], privateFileRights)) {
28130
+ throw new Error(`runtime file has an unsafe extended ACL: ${path}`);
28131
+ }
28132
+ if (process.platform === "win32") await checkWindowsAccess(path, true);
28133
+ const handle = await open(path, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
28134
+ try {
28135
+ const opened = await handle.stat();
28136
+ if (opened.dev !== info.dev || opened.ino !== info.ino) throw new Error(`runtime file changed while opening: ${path}`);
28137
+ return await handle.readFile("utf8");
28138
+ } finally {
28139
+ await handle.close();
28140
+ }
28141
+ }
28142
+ async function writeFileAtomic(path, content, mode) {
28143
+ await ensureDirectory(dirname(path));
28144
+ const staging = join(dirname(path), `.${randomUUID()}.tmp`);
28145
+ await mkdir(staging, { mode: 448 });
28146
+ const temporary = join(staging, "content");
28147
+ try {
28148
+ if (process.platform === "darwin") await promisify2(execFile2)("/bin/chmod", ["-N", staging]);
28149
+ if (process.platform === "win32") await protectWindowsDirectory(staging);
28150
+ const handle = await open(temporary, "wx", mode);
28151
+ try {
28152
+ if (process.platform === "darwin") await promisify2(execFile2)("/bin/chmod", ["-N", temporary]);
28153
+ if (process.platform === "win32") await protectWindowsFile(temporary);
28154
+ await handle.writeFile(content);
28155
+ await handle.sync();
28156
+ } finally {
28157
+ await handle.close();
28158
+ }
28159
+ await chmod(temporary, mode);
28160
+ await rename(temporary, path);
28161
+ if (process.platform !== "win32") {
28162
+ const directory = await open(dirname(path), constants.O_RDONLY);
28163
+ try {
28164
+ await directory.sync();
28165
+ } finally {
28166
+ await directory.close();
28167
+ }
28168
+ }
28169
+ } finally {
28170
+ await rm(staging, { recursive: true, force: true });
28171
+ }
28172
+ }
28173
+ function errorCode(error) {
28174
+ return error && typeof error === "object" && "code" in error ? String(error.code) : void 0;
28175
+ }
27962
28176
 
27963
28177
  // ../memoh/packages/runtime/src/config.ts
27964
28178
  import { isAbsolute } from "node:path";
@@ -28017,10 +28231,620 @@ function validateConfig(config) {
28017
28231
  }
28018
28232
  }
28019
28233
 
28234
+ // ../memoh/packages/runtime/src/server-url.ts
28235
+ function normalizeRuntimeServerUrl(serverUrl) {
28236
+ const url = new URL(serverUrl.trim());
28237
+ if (url.protocol === "ws:") {
28238
+ url.protocol = "http:";
28239
+ } else if (url.protocol === "wss:") {
28240
+ url.protocol = "https:";
28241
+ }
28242
+ url.pathname = url.pathname.replace(/\/+$/, "") || "/";
28243
+ url.search = "";
28244
+ url.hash = "";
28245
+ return url.href;
28246
+ }
28247
+ function runtimeConnectUrl(serverUrl) {
28248
+ const url = new URL(serverUrl);
28249
+ if (url.protocol === "http:") {
28250
+ url.protocol = "ws:";
28251
+ } else if (url.protocol === "https:") {
28252
+ url.protocol = "wss:";
28253
+ }
28254
+ const basePath = url.pathname.replace(/\/+$/, "");
28255
+ url.pathname = `${basePath}/runtimes/connect`;
28256
+ url.search = "";
28257
+ url.hash = "";
28258
+ return url;
28259
+ }
28260
+ function assertSecureRuntimeUrl(url, insecureLocalhost = false) {
28261
+ if (url.username || url.password) {
28262
+ throw new Error("runtime connection URL must not contain credentials");
28263
+ }
28264
+ if (url.protocol === "wss:") {
28265
+ return;
28266
+ }
28267
+ const hostname2 = url.hostname.replace(/^\[|\]$/g, "");
28268
+ const local = hostname2 === "127.0.0.1" || hostname2 === "localhost" || hostname2 === "::1";
28269
+ if (url.protocol === "ws:" && insecureLocalhost && local) {
28270
+ return;
28271
+ }
28272
+ throw new Error("runtime connections require wss://; use --insecure-localhost only for localhost development");
28273
+ }
28274
+
28275
+ // ../memoh/packages/runtime/src/runtime-config.ts
28276
+ var runtimeEnrollmentSchemaVersion = 1;
28277
+ function resolveRuntimePaths(options = {}) {
28278
+ const home = resolve2(options.home ?? homedir());
28279
+ const runtimeHome = join2(home, ".memoh", "runtime");
28280
+ const configPath = join2(home, ".memoh", "runtime.json");
28281
+ const serviceDir = join2(runtimeHome, "service");
28282
+ return {
28283
+ home,
28284
+ runtimeHome,
28285
+ configPath,
28286
+ logsDir: join2(runtimeHome, "logs"),
28287
+ serviceDir,
28288
+ launchdPlistPath: join2(home, "Library", "LaunchAgents", "ai.memoh.runtime.plist"),
28289
+ systemdUnitPath: join2(serviceDir, "memoh-runtime.service"),
28290
+ windowsTaskXMLPath: join2(serviceDir, "memoh-runtime-task.xml")
28291
+ };
28292
+ }
28293
+ function normalizeRuntimeEnrollment(input, workspaceBase = homedir()) {
28294
+ const rawServerUrl = input.serverUrl.trim();
28295
+ const key = input.key.trim();
28296
+ const teamId = input.teamId === void 0 ? void 0 : normalizeRuntimeTeamId(input.teamId);
28297
+ const insecureLocalhost = input.insecureLocalhost === true;
28298
+ validateConfig({
28299
+ serverUrl: rawServerUrl,
28300
+ key,
28301
+ teamId,
28302
+ workspaceBase,
28303
+ insecureLocalhost
28304
+ });
28305
+ assertSecureRuntimeUrl(runtimeConnectUrl(rawServerUrl), insecureLocalhost);
28306
+ const serverUrl = normalizeRuntimeServerUrl(rawServerUrl);
28307
+ return {
28308
+ schemaVersion: runtimeEnrollmentSchemaVersion,
28309
+ serverUrl,
28310
+ key,
28311
+ teamId,
28312
+ insecureLocalhost
28313
+ };
28314
+ }
28315
+ async function readRuntimeEnrollment(path, workspaceBase = homedir()) {
28316
+ let raw;
28317
+ try {
28318
+ raw = await readPrivateFile(path);
28319
+ } catch (error) {
28320
+ if (nodeErrorCode(error) === "ENOENT") {
28321
+ throw new Error(`runtime configuration was not found at ${path}`);
28322
+ }
28323
+ throw new Error(`runtime configuration could not be read safely at ${path}`);
28324
+ }
28325
+ let parsed;
28326
+ try {
28327
+ parsed = JSON.parse(raw);
28328
+ } catch {
28329
+ throw new Error(`runtime configuration at ${path} is not valid JSON`);
28330
+ }
28331
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
28332
+ throw new Error(`runtime configuration at ${path} must be an object`);
28333
+ }
28334
+ const record = parsed;
28335
+ if (record.schemaVersion !== runtimeEnrollmentSchemaVersion) {
28336
+ throw new Error(`runtime configuration at ${path} has an unsupported schema version`);
28337
+ }
28338
+ if (typeof record.serverUrl !== "string" || typeof record.key !== "string") {
28339
+ throw new Error(`runtime configuration at ${path} is missing serverUrl or key`);
28340
+ }
28341
+ if (record.teamId !== void 0 && typeof record.teamId !== "string") {
28342
+ throw new Error(`runtime configuration at ${path} has an invalid teamId`);
28343
+ }
28344
+ if (record.insecureLocalhost !== void 0 && typeof record.insecureLocalhost !== "boolean") {
28345
+ throw new Error(`runtime configuration at ${path} has an invalid insecureLocalhost value`);
28346
+ }
28347
+ try {
28348
+ return normalizeRuntimeEnrollment({
28349
+ serverUrl: record.serverUrl,
28350
+ key: record.key,
28351
+ teamId: record.teamId,
28352
+ insecureLocalhost: record.insecureLocalhost
28353
+ }, workspaceBase);
28354
+ } catch (error) {
28355
+ const message = error instanceof Error ? error.message : String(error);
28356
+ throw new Error(`runtime configuration at ${path} is invalid: ${message}`);
28357
+ }
28358
+ }
28359
+ async function readRuntimeEnrollmentIfExists(path, workspaceBase = homedir()) {
28360
+ try {
28361
+ await lstat2(path);
28362
+ } catch (error) {
28363
+ if (nodeErrorCode(error) === "ENOENT") return void 0;
28364
+ throw error;
28365
+ }
28366
+ return readRuntimeEnrollment(path, workspaceBase);
28367
+ }
28368
+ async function writeRuntimeEnrollment(path, enrollment) {
28369
+ const normalized = normalizeRuntimeEnrollment(enrollment);
28370
+ await ensureDirectory(dirname2(path));
28371
+ await writeFileAtomic(path, `${JSON.stringify(normalized, null, 2)}
28372
+ `, 384);
28373
+ }
28374
+ function sameEnrollment(left, right) {
28375
+ return left.serverUrl === right.serverUrl && left.key === right.key && left.teamId === right.teamId && left.insecureLocalhost === right.insecureLocalhost;
28376
+ }
28377
+ function parseBooleanEnvironment(value, name) {
28378
+ const normalized = value?.trim().toLowerCase();
28379
+ if (!normalized) return void 0;
28380
+ if (["1", "true", "yes", "on"].includes(normalized)) return true;
28381
+ if (["0", "false", "no", "off"].includes(normalized)) return false;
28382
+ throw new Error(`${name} must be true or false`);
28383
+ }
28384
+ function nodeErrorCode(error) {
28385
+ return error && typeof error === "object" && "code" in error ? String(error.code) : void 0;
28386
+ }
28387
+
28388
+ // ../memoh/packages/runtime/src/daemon/types.ts
28389
+ import { spawn } from "node:child_process";
28390
+ var maxCommandOutputBytes = 64 * 1024;
28391
+ var spawnCommand = (command, args2, options = {}) => new Promise((resolve5, reject) => {
28392
+ const child = spawn(command, args2, {
28393
+ env: options.env,
28394
+ shell: false,
28395
+ stdio: ["ignore", "pipe", "pipe"],
28396
+ windowsHide: true
28397
+ });
28398
+ let stdout = "";
28399
+ let stderr = "";
28400
+ child.stdout.setEncoding("utf8");
28401
+ child.stderr.setEncoding("utf8");
28402
+ child.stdout.on("data", (chunk) => {
28403
+ if (stdout.length < maxCommandOutputBytes) stdout += chunk.slice(0, maxCommandOutputBytes - stdout.length);
28404
+ });
28405
+ child.stderr.on("data", (chunk) => {
28406
+ if (stderr.length < maxCommandOutputBytes) stderr += chunk.slice(0, maxCommandOutputBytes - stderr.length);
28407
+ });
28408
+ const timer = setTimeout(() => {
28409
+ child.kill();
28410
+ reject(new Error(`${command} timed out`));
28411
+ }, 3e4);
28412
+ timer.unref();
28413
+ child.once("error", (error) => {
28414
+ clearTimeout(timer);
28415
+ reject(error);
28416
+ });
28417
+ child.once("close", (code) => {
28418
+ clearTimeout(timer);
28419
+ resolve5({ code: code ?? 1, stdout, stderr });
28420
+ });
28421
+ });
28422
+ async function requireCommand(runner, command, args2, options = {}) {
28423
+ const result = await runner(command, args2, { env: options.env });
28424
+ const allowed = options.allowedExitCodes ?? [0];
28425
+ if (!allowed.includes(result.code)) {
28426
+ const detail = result.stderr.trim() || result.stdout.trim() || `exit code ${result.code}`;
28427
+ throw new Error(`${command} ${args2.join(" ")} failed: ${detail}`);
28428
+ }
28429
+ return result;
28430
+ }
28431
+ function serviceExecutablePath(nodePath, environmentPath, platform2 = process.platform) {
28432
+ const separator = platform2 === "win32" ? ";" : ":";
28433
+ const nodeDirectory = nodePath.replace(/[\\/][^\\/]+$/, "");
28434
+ const defaults = platform2 === "darwin" ? ["/opt/homebrew/bin", "/usr/local/bin", "/usr/bin", "/bin", "/usr/sbin", "/sbin"] : ["/usr/local/bin", "/usr/bin", "/bin"];
28435
+ const values = [nodeDirectory, ...environmentPath?.split(separator) ?? [], ...defaults].map((value) => value.trim()).filter((value) => value && !value.includes("\0") && !value.includes("\n") && !value.includes("\r"));
28436
+ return [...new Set(values)].join(separator);
28437
+ }
28438
+
28439
+ // ../memoh/packages/runtime/src/daemon/launchd.ts
28440
+ var launchdLabel = "ai.memoh.runtime";
28441
+ var bootstrapAttempts = 5;
28442
+ var defaultBootstrapRetryDelayMs = 300;
28443
+ var bootoutWaitAttempts = 50;
28444
+ function renderLaunchdPlist(spec) {
28445
+ return `<?xml version="1.0" encoding="UTF-8"?>
28446
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
28447
+ <plist version="1.0">
28448
+ <dict>
28449
+ <key>Label</key>
28450
+ <string>${xmlEscape(launchdLabel)}</string>
28451
+ <key>ProgramArguments</key>
28452
+ <array>
28453
+ <string>${xmlEscape(spec.nodePath)}</string>
28454
+ <string>${xmlEscape(spec.entryPath)}</string>
28455
+ <string>run</string>
28456
+ <string>--config</string>
28457
+ <string>${xmlEscape(spec.configPath)}</string>
28458
+ </array>
28459
+ <key>EnvironmentVariables</key>
28460
+ <dict>
28461
+ <key>PATH</key>
28462
+ <string>${xmlEscape(spec.servicePath)}</string>
28463
+ </dict>
28464
+ <key>WorkingDirectory</key>
28465
+ <string>${xmlEscape(spec.workingDirectory)}</string>
28466
+ <key>RunAtLoad</key>
28467
+ <true/>
28468
+ <key>KeepAlive</key>
28469
+ <true/>
28470
+ <key>ThrottleInterval</key>
28471
+ <integer>5</integer>
28472
+ <key>StandardOutPath</key>
28473
+ <string>${xmlEscape(`${spec.logsDir}/runtime.log`)}</string>
28474
+ <key>StandardErrorPath</key>
28475
+ <string>${xmlEscape(`${spec.logsDir}/runtime-error.log`)}</string>
28476
+ </dict>
28477
+ </plist>
28478
+ `;
28479
+ }
28480
+ function createLaunchdServiceManager(paths, runner, uid, options = {}) {
28481
+ const launchctl = "/bin/launchctl";
28482
+ const domain = `gui/${uid}`;
28483
+ const target = `${domain}/${launchdLabel}`;
28484
+ const retryDelayMs = options.bootstrapRetryDelayMs ?? defaultBootstrapRetryDelayMs;
28485
+ const loaded = async () => (await runner(launchctl, ["print", target])).code === 0;
28486
+ const bootstrap = async () => {
28487
+ for (let attempt = 1; ; attempt++) {
28488
+ try {
28489
+ await requireCommand(runner, launchctl, ["bootstrap", domain, paths.launchdPlistPath]);
28490
+ return;
28491
+ } catch (error) {
28492
+ if (attempt >= bootstrapAttempts) throw error;
28493
+ await sleep(retryDelayMs);
28494
+ }
28495
+ }
28496
+ };
28497
+ const bootstrapIfUnloaded = async () => {
28498
+ if (await loaded()) return;
28499
+ await bootstrap();
28500
+ };
28501
+ const bootout = async () => {
28502
+ await requireCommand(runner, launchctl, ["bootout", target], { allowedExitCodes: [0, 3] });
28503
+ for (let attempt = 0; attempt < bootoutWaitAttempts && await loaded(); attempt++) {
28504
+ await sleep(retryDelayMs / 3);
28505
+ }
28506
+ if (await loaded()) throw new Error("launchd job did not stop before the deadline");
28507
+ };
28508
+ return {
28509
+ backend: "launchd-user",
28510
+ async register(spec) {
28511
+ await ensureDirectory(spec.logsDir);
28512
+ await writeFileAtomic(paths.launchdPlistPath, renderLaunchdPlist(spec), 384);
28513
+ await requireCommand(runner, "/usr/bin/plutil", ["-lint", paths.launchdPlistPath]);
28514
+ },
28515
+ async start() {
28516
+ await bootstrapIfUnloaded();
28517
+ },
28518
+ async stop() {
28519
+ await bootout();
28520
+ },
28521
+ async status() {
28522
+ const result = await runner(launchctl, ["print", target]);
28523
+ if (result.code === 0) {
28524
+ return {
28525
+ backend: "launchd-user",
28526
+ state: /\bstate\s*=\s*running\b/.test(result.stdout) ? "running" : "stopped"
28527
+ };
28528
+ }
28529
+ try {
28530
+ await access(paths.launchdPlistPath);
28531
+ return { backend: "launchd-user", state: "stopped" };
28532
+ } catch {
28533
+ return { backend: "launchd-user", state: "not-installed" };
28534
+ }
28535
+ },
28536
+ async uninstall() {
28537
+ await bootout();
28538
+ await rm2(paths.launchdPlistPath, { force: true });
28539
+ }
28540
+ };
28541
+ }
28542
+ function xmlEscape(value) {
28543
+ if (value.includes("\0")) throw new Error("launchd service value contains a null byte");
28544
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&apos;");
28545
+ }
28546
+
28547
+ // ../memoh/packages/runtime/src/daemon/systemd.ts
28548
+ import { rm as rm3 } from "node:fs/promises";
28549
+ var unitName = "memoh-runtime.service";
28550
+ function renderSystemdUnit(spec) {
28551
+ return `[Unit]
28552
+ Description=Memoh Runtime
28553
+
28554
+ [Service]
28555
+ Type=simple
28556
+ Environment=${systemdQuote(`PATH=${spec.servicePath}`, false)}
28557
+ ExecStart=${systemdQuote(spec.nodePath, false)} ${systemdQuote(spec.entryPath)} run --config ${systemdQuote(spec.configPath)}
28558
+ WorkingDirectory=${systemdPath(spec.workingDirectory)}
28559
+ UMask=0077
28560
+ Restart=always
28561
+ RestartSec=5
28562
+ TimeoutStopSec=30
28563
+
28564
+ [Install]
28565
+ WantedBy=default.target
28566
+ `;
28567
+ }
28568
+ function createSystemdServiceManager(paths, runner) {
28569
+ const systemctl = "systemctl";
28570
+ const baseArgs = ["--user"];
28571
+ const run2 = (args2, allowedExitCodes) => requireCommand(runner, systemctl, [...baseArgs, ...args2], { allowedExitCodes });
28572
+ return {
28573
+ backend: "systemd-user",
28574
+ async register(spec) {
28575
+ await writeFileAtomic(paths.systemdUnitPath, renderSystemdUnit(spec), 384);
28576
+ await requireCommand(runner, "systemd-analyze", ["--user", "verify", paths.systemdUnitPath]);
28577
+ await run2(["daemon-reload"]);
28578
+ await run2(["enable", paths.systemdUnitPath]);
28579
+ },
28580
+ async start() {
28581
+ await run2(["start", unitName]);
28582
+ },
28583
+ async stop() {
28584
+ await run2(["stop", unitName], [0, 5]);
28585
+ },
28586
+ async status() {
28587
+ const result = await runner(systemctl, [...baseArgs, "show", unitName, "--property=LoadState,ActiveState,SubState,MainPID"]);
28588
+ const fields = Object.fromEntries(result.stdout.trim().split("\n").map((line) => line.split("=")));
28589
+ if (fields.LoadState === "not-found") return { backend: "systemd-user", state: "not-installed" };
28590
+ if (result.code !== 0 || fields.LoadState !== "loaded") {
28591
+ return { backend: "systemd-user", state: "unknown", detail: result.stderr.trim() || fields.LoadState };
28592
+ }
28593
+ const pid = Number(fields.MainPID);
28594
+ return {
28595
+ backend: "systemd-user",
28596
+ state: fields.ActiveState === "active" && fields.SubState === "running" && pid > 0 ? "running" : ["inactive", "failed"].includes(fields.ActiveState) ? "stopped" : "unknown",
28597
+ pid: pid > 0 ? pid : void 0,
28598
+ detail: fields.SubState
28599
+ };
28600
+ },
28601
+ async uninstall() {
28602
+ await run2(["disable", "--now", unitName], [0, 1, 5]);
28603
+ try {
28604
+ await rm3(paths.systemdUnitPath, { force: true });
28605
+ } catch (error) {
28606
+ if (nodeErrorCode(error) !== "ENOENT") throw error;
28607
+ }
28608
+ await run2(["daemon-reload"]);
28609
+ await run2(["reset-failed", unitName], [0, 1, 5]);
28610
+ }
28611
+ };
28612
+ }
28613
+ function systemdQuote(value, expandDollar = true) {
28614
+ if (expandDollar) value = value.replaceAll("$", () => "$$");
28615
+ if (value.includes("\0") || value.includes("\n") || value.includes("\r")) {
28616
+ throw new Error("systemd service value contains unsupported control characters");
28617
+ }
28618
+ return `"${value.replaceAll("\\", "\\\\").replaceAll('"', '\\"').replaceAll("%", "%%")}"`;
28619
+ }
28620
+ function systemdPath(value) {
28621
+ if (!value.startsWith("/") || value.trim() !== value || /[\\\0\n\r]/.test(value)) throw new Error("invalid systemd working directory");
28622
+ return value.replaceAll("%", "%%");
28623
+ }
28624
+
28625
+ // ../memoh/packages/runtime/src/daemon/windows.ts
28626
+ import { rm as rm4 } from "node:fs/promises";
28627
+ function renderWindowsTaskXML(spec, userId) {
28628
+ const argumentsValue = [
28629
+ windowsQuoteArgument(spec.entryPath),
28630
+ "run",
28631
+ "--config",
28632
+ windowsQuoteArgument(spec.configPath),
28633
+ "--log",
28634
+ windowsQuoteArgument(`${spec.logsDir}\\runtime.log`)
28635
+ ].join(" ");
28636
+ return `<?xml version="1.0" encoding="UTF-16"?>
28637
+ <Task version="1.4" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
28638
+ <RegistrationInfo>
28639
+ <Description>Memoh Runtime</Description>
28640
+ </RegistrationInfo>
28641
+ <Triggers>
28642
+ <LogonTrigger>
28643
+ <Enabled>true</Enabled>
28644
+ <UserId>${xmlEscape2(userId)}</UserId>
28645
+ </LogonTrigger>
28646
+ </Triggers>
28647
+ <Principals>
28648
+ <Principal id="Author">
28649
+ <UserId>${xmlEscape2(userId)}</UserId>
28650
+ <LogonType>InteractiveToken</LogonType>
28651
+ <RunLevel>LeastPrivilege</RunLevel>
28652
+ </Principal>
28653
+ </Principals>
28654
+ <Settings>
28655
+ <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
28656
+ <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
28657
+ <StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
28658
+ <AllowHardTerminate>true</AllowHardTerminate>
28659
+ <StartWhenAvailable>true</StartWhenAvailable>
28660
+ <RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
28661
+ <AllowStartOnDemand>true</AllowStartOnDemand>
28662
+ <Enabled>true</Enabled>
28663
+ <Hidden>false</Hidden>
28664
+ <RunOnlyIfIdle>false</RunOnlyIfIdle>
28665
+ <WakeToRun>false</WakeToRun>
28666
+ <ExecutionTimeLimit>PT0S</ExecutionTimeLimit>
28667
+ <RestartOnFailure>
28668
+ <Interval>PT1M</Interval>
28669
+ <Count>255</Count>
28670
+ </RestartOnFailure>
28671
+ </Settings>
28672
+ <Actions Context="Author">
28673
+ <Exec>
28674
+ <Command>${xmlEscape2(spec.nodePath)}</Command>
28675
+ <Arguments>${xmlEscape2(argumentsValue)}</Arguments>
28676
+ <WorkingDirectory>${xmlEscape2(spec.workingDirectory)}</WorkingDirectory>
28677
+ </Exec>
28678
+ </Actions>
28679
+ </Task>
28680
+ `;
28681
+ }
28682
+ function createWindowsTaskServiceManager(paths, runner) {
28683
+ const schtasks = "schtasks.exe";
28684
+ let identity;
28685
+ const userID = () => identity ??= currentWindowsUserID(runner);
28686
+ const taskName = async () => `\\Memoh\\Runtime-${await userID()}`;
28687
+ const inspect = async () => {
28688
+ const sid = await userID();
28689
+ const result = await requireCommand(runner, "powershell.exe", [
28690
+ "-NoProfile",
28691
+ "-NonInteractive",
28692
+ "-Command",
28693
+ `$ErrorActionPreference = 'Stop'; $task = Get-ScheduledTask | Where-Object { $_.TaskPath -eq '\\Memoh\\' -and $_.TaskName -eq 'Runtime-${sid}' }; if ($null -eq $task) { 'not-installed' } else { $owner = $task.Principal.UserId; if ($owner -notmatch '^S-1-') { $owner = ([System.Security.Principal.NTAccount]::new($owner)).Translate([System.Security.Principal.SecurityIdentifier]).Value }; if ($owner -ne '${sid}') { throw 'Runtime task belongs to another account' }; $task.State.ToString() }`
28694
+ ], { env: windowsPowerShellEnv() });
28695
+ const state = result.stdout.trim().toLowerCase();
28696
+ if (!["not-installed", "running", "ready", "disabled", "queued", "unknown"].includes(state)) {
28697
+ throw new Error("could not determine the Windows runtime task state");
28698
+ }
28699
+ return state;
28700
+ };
28701
+ const run2 = async (args2, allowedExitCodes) => requireCommand(runner, schtasks, [...args2, "/tn", await taskName()], { allowedExitCodes });
28702
+ const stop = async () => {
28703
+ if (["running", "queued", "unknown"].includes(await inspect())) await run2(["/end"]);
28704
+ };
28705
+ const start = async () => {
28706
+ if (await inspect() === "not-installed") throw new Error("Windows runtime task is not installed");
28707
+ await run2(["/run"]);
28708
+ };
28709
+ return {
28710
+ backend: "windows-task-scheduler",
28711
+ async register(spec) {
28712
+ const userId = await userID();
28713
+ await inspect();
28714
+ await ensureDirectory(spec.logsDir);
28715
+ const xml = renderWindowsTaskXML(spec, userId);
28716
+ const utf16 = Buffer.concat([Buffer.from([255, 254]), Buffer.from(xml, "utf16le")]);
28717
+ await writeFileAtomic(paths.windowsTaskXMLPath, utf16, 384);
28718
+ await run2(["/create", "/xml", paths.windowsTaskXMLPath, "/f"]);
28719
+ },
28720
+ start,
28721
+ stop,
28722
+ async status() {
28723
+ try {
28724
+ const state = await inspect();
28725
+ return {
28726
+ backend: "windows-task-scheduler",
28727
+ state: state === "running" ? "running" : state === "not-installed" ? "not-installed" : ["ready", "disabled"].includes(state) ? "stopped" : "unknown"
28728
+ };
28729
+ } catch {
28730
+ return { backend: "windows-task-scheduler", state: "unknown" };
28731
+ }
28732
+ },
28733
+ async uninstall() {
28734
+ if (await inspect() !== "not-installed") {
28735
+ await stop();
28736
+ await run2(["/delete", "/f"]);
28737
+ }
28738
+ await rm4(paths.windowsTaskXMLPath, { force: true });
28739
+ }
28740
+ };
28741
+ }
28742
+ async function currentWindowsUserID(runner) {
28743
+ const result = await requireCommand(runner, "whoami.exe", ["/user", "/fo", "csv", "/nh"]);
28744
+ const fields = [...result.stdout.matchAll(/"([^"]*)"/g)].map((match) => match[1]);
28745
+ const sid = fields.find((field) => /^S-1-(?:[0-9]+-)*[0-9]+$/i.test(field));
28746
+ if (sid) return sid;
28747
+ throw new Error("could not determine the current Windows account");
28748
+ }
28749
+ function windowsQuoteArgument(value) {
28750
+ if (value.includes("\0") || value.includes('"')) {
28751
+ throw new Error("Windows service path contains unsupported characters");
28752
+ }
28753
+ return `"${value}"`;
28754
+ }
28755
+ function xmlEscape2(value) {
28756
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&apos;");
28757
+ }
28758
+
28759
+ // ../memoh/packages/runtime/src/daemon/index.ts
28760
+ function createRuntimeServiceManager(options) {
28761
+ switch (options.platform) {
28762
+ case "darwin":
28763
+ if (options.uid === void 0) throw new Error("launchd service installation requires a user ID");
28764
+ return createLaunchdServiceManager(options.paths, options.runner, options.uid);
28765
+ case "linux":
28766
+ return createSystemdServiceManager(options.paths, options.runner);
28767
+ case "win32":
28768
+ return createWindowsTaskServiceManager(options.paths, options.runner);
28769
+ default:
28770
+ throw new Error(`background services are not supported on ${options.platform}`);
28771
+ }
28772
+ }
28773
+ async function findNodeExecutable(envPath, platform2) {
28774
+ for (const directory of envPath?.split(platform2 === "win32" ? ";" : ":") ?? []) {
28775
+ if (!isAbsolute2(directory)) continue;
28776
+ const path = join3(directory, platform2 === "win32" ? "node.exe" : "node");
28777
+ try {
28778
+ if (!(await stat(path)).isFile()) continue;
28779
+ await access2(path, platform2 === "win32" ? constants2.R_OK : constants2.X_OK);
28780
+ return path;
28781
+ } catch {
28782
+ }
28783
+ }
28784
+ throw new Error("Node was not found in PATH; install Node and rerun service install");
28785
+ }
28786
+ async function waitForService(manager, target) {
28787
+ const deadline = Date.now() + 15e3;
28788
+ let observations = 0;
28789
+ do {
28790
+ const { state } = await manager.status();
28791
+ const matched = state === target || target === "stopped" && state === "not-installed";
28792
+ observations = matched ? observations + 1 : 0;
28793
+ if (observations >= (target === "running" ? 2 : 1)) return;
28794
+ await sleep2(250);
28795
+ } while (Date.now() < deadline);
28796
+ throw new Error(`runtime service did not reach ${target}; inspect the service logs`);
28797
+ }
28798
+
28799
+ // ../memoh/packages/runtime/src/enrollment-lock.ts
28800
+ import { mkdir as mkdir2, rm as rm5 } from "node:fs/promises";
28801
+ import { dirname as dirname3, join as join4 } from "node:path";
28802
+ import { setTimeout as sleep3 } from "node:timers/promises";
28803
+ async function withEnrollmentLock(configPath, action, timeoutMs = 3e4) {
28804
+ await ensureDirectory(dirname3(configPath));
28805
+ const lockPath = `${configPath}.lock`;
28806
+ const deadline = Date.now() + timeoutMs;
28807
+ for (; ; ) {
28808
+ try {
28809
+ await mkdir2(lockPath, { mode: 448 });
28810
+ break;
28811
+ } catch (error) {
28812
+ if (errorCode(error) !== "EEXIST") throw error;
28813
+ if (Date.now() >= deadline) {
28814
+ throw new Error(`another enrollment operation holds ${lockPath}; inspect owner.json and remove the lock only after confirming that process has exited`);
28815
+ }
28816
+ await sleep3(100);
28817
+ }
28818
+ }
28819
+ try {
28820
+ if (process.platform === "win32") await protectWindowsDirectory(lockPath);
28821
+ await writeFileAtomic(join4(lockPath, "owner.json"), `${JSON.stringify({ pid: process.pid })}
28822
+ `, 384);
28823
+ return await action();
28824
+ } finally {
28825
+ await rm5(lockPath, { recursive: true, force: true });
28826
+ }
28827
+ }
28828
+
28829
+ // ../memoh/packages/runtime/src/session.ts
28830
+ import { arch, hostname, platform } from "node:os";
28831
+ import { realpath as realpath3 } from "node:fs/promises";
28832
+
28833
+ // ../memoh/node_modules/.pnpm/ws@8.21.0/node_modules/ws/wrapper.mjs
28834
+ var import_stream = __toESM(require_stream(), 1);
28835
+ var import_extension = __toESM(require_extension(), 1);
28836
+ var import_permessage_deflate = __toESM(require_permessage_deflate(), 1);
28837
+ var import_receiver = __toESM(require_receiver(), 1);
28838
+ var import_sender = __toESM(require_sender(), 1);
28839
+ var import_subprotocol = __toESM(require_subprotocol(), 1);
28840
+ var import_websocket = __toESM(require_websocket(), 1);
28841
+ var import_websocket_server = __toESM(require_websocket_server(), 1);
28842
+ var wrapper_default = import_websocket.default;
28843
+
28020
28844
  // ../memoh/packages/runtime/src/pipe/grpc-websocket.ts
28021
28845
  function bridgeWebSocketToGrpc(websocket, grpc) {
28022
28846
  const stream = (0, import_stream.default)(websocket, { encoding: void 0 });
28023
- return new Promise((resolve2, reject) => {
28847
+ return new Promise((resolve5, reject) => {
28024
28848
  let settled = false;
28025
28849
  const finish = (error) => {
28026
28850
  if (settled) {
@@ -28035,7 +28859,7 @@ function bridgeWebSocketToGrpc(websocket, grpc) {
28035
28859
  if (error) {
28036
28860
  reject(error);
28037
28861
  } else {
28038
- resolve2();
28862
+ resolve5();
28039
28863
  }
28040
28864
  };
28041
28865
  websocket.once("close", () => finish());
@@ -28062,11 +28886,11 @@ function bridgeWebSocketToGrpc(websocket, grpc) {
28062
28886
  // ../memoh/packages/runtime/src/service.ts
28063
28887
  var import_grpc_js6 = __toESM(require_src3(), 1);
28064
28888
  var import_proto_loader = __toESM(require_src2(), 1);
28065
- import { access as access2 } from "node:fs/promises";
28889
+ import { access as access4 } from "node:fs/promises";
28066
28890
  import { fileURLToPath } from "node:url";
28067
28891
 
28068
28892
  // ../memoh/packages/runtime/src/children.ts
28069
- import { execFile } from "node:child_process";
28893
+ import { execFile as execFile3 } from "node:child_process";
28070
28894
 
28071
28895
  // ../memoh/packages/runtime/src/core/shell.ts
28072
28896
  import { win32 } from "node:path";
@@ -28225,10 +29049,10 @@ async function terminateWindowsProcessTree(pid) {
28225
29049
  return;
28226
29050
  }
28227
29051
  const executable = windowsSystemExecutable("taskkill.exe");
28228
- await new Promise((resolve2, reject) => {
28229
- execFile(executable, ["/pid", String(pid), "/t", "/f"], { windowsHide: true }, (error) => {
29052
+ await new Promise((resolve5, reject) => {
29053
+ execFile3(executable, ["/pid", String(pid), "/t", "/f"], { windowsHide: true }, (error) => {
28230
29054
  if (!error || !processExists(pid)) {
28231
- resolve2();
29055
+ resolve5();
28232
29056
  } else {
28233
29057
  reject(error);
28234
29058
  }
@@ -28247,7 +29071,7 @@ async function waitForChildExit(child, timeout) {
28247
29071
  if (child.exitCode !== null || child.signalCode !== null) {
28248
29072
  return true;
28249
29073
  }
28250
- return new Promise((resolve2) => {
29074
+ return new Promise((resolve5) => {
28251
29075
  let settled = false;
28252
29076
  const finish = (exited) => {
28253
29077
  if (settled) {
@@ -28256,7 +29080,7 @@ async function waitForChildExit(child, timeout) {
28256
29080
  settled = true;
28257
29081
  clearTimeout(timer);
28258
29082
  child.off("exit", onExit);
28259
- resolve2(exited);
29083
+ resolve5(exited);
28260
29084
  };
28261
29085
  const onExit = () => finish(true);
28262
29086
  const timer = setTimeout(() => finish(child.exitCode !== null || child.signalCode !== null), timeout);
@@ -28287,7 +29111,7 @@ async function waitForProcessGroupExit(pgid, timeout) {
28287
29111
  if (!processGroupExists(pgid)) {
28288
29112
  return true;
28289
29113
  }
28290
- await new Promise((resolve2) => setTimeout(resolve2, 25));
29114
+ await new Promise((resolve5) => setTimeout(resolve5, 25));
28291
29115
  }
28292
29116
  return !processGroupExists(pgid);
28293
29117
  }
@@ -28303,7 +29127,7 @@ function safeError(error) {
28303
29127
 
28304
29128
  // ../memoh/packages/runtime/src/core/exec.ts
28305
29129
  var import_grpc_js3 = __toESM(require_src3(), 1);
28306
- import { spawn } from "node:child_process";
29130
+ import { spawn as spawn2 } from "node:child_process";
28307
29131
  import { constants as osConstants } from "node:os";
28308
29132
 
28309
29133
  // ../memoh/packages/runtime/src/rpc.ts
@@ -28319,7 +29143,7 @@ function mapNodeError(error, operation) {
28319
29143
  if (isServiceError(error)) {
28320
29144
  return error;
28321
29145
  }
28322
- const code = nodeErrorCode(error);
29146
+ const code = nodeErrorCode2(error);
28323
29147
  switch (code) {
28324
29148
  case "ENOENT":
28325
29149
  return rpcError(import_grpc_js.status.NOT_FOUND, `${operation}: not found`);
@@ -28335,7 +29159,7 @@ function mapNodeError(error, operation) {
28335
29159
  return rpcError(import_grpc_js.status.INTERNAL, `${operation} failed`);
28336
29160
  }
28337
29161
  }
28338
- function nodeErrorCode(error) {
29162
+ function nodeErrorCode2(error) {
28339
29163
  return typeof error === "object" && error !== null && "code" in error ? String(error.code) : void 0;
28340
29164
  }
28341
29165
  function isServiceError(error) {
@@ -28577,7 +29401,7 @@ var WorkspaceExecService = class {
28577
29401
  });
28578
29402
  assertExecAdmissionActive(call, admissionActive);
28579
29403
  const shell = shellSpawnSpec(command);
28580
- const child = spawn(shell.command, shell.args, {
29404
+ const child = spawn2(shell.command, shell.args, {
28581
29405
  cwd: workDirectory,
28582
29406
  env: environment,
28583
29407
  detached: shell.detached,
@@ -28589,8 +29413,8 @@ var WorkspaceExecService = class {
28589
29413
  const registration = this.children.register(child);
28590
29414
  void registration.catch(() => void 0);
28591
29415
  try {
28592
- await new Promise((resolve2, reject) => {
28593
- child.once("spawn", resolve2);
29416
+ await new Promise((resolve5, reject) => {
29417
+ child.once("spawn", resolve5);
28594
29418
  child.once("error", reject);
28595
29419
  });
28596
29420
  await registration;
@@ -28603,8 +29427,8 @@ var WorkspaceExecService = class {
28603
29427
  throw rpcError(import_grpc_js3.status.CANCELLED, "exec was cancelled before process admission completed");
28604
29428
  }
28605
29429
  child.stdin.on("error", () => void 0);
28606
- const closed = new Promise((resolve2) => {
28607
- child.once("close", (code, signal) => resolve2({ code, signal }));
29430
+ const closed = new Promise((resolve5) => {
29431
+ child.once("close", (code, signal) => resolve5({ code, signal }));
28608
29432
  });
28609
29433
  const stdoutDone = pipeOutput(child.stdout, call, stdoutStream, () => void this.children.terminate(child));
28610
29434
  const stderrDone = pipeOutput(child.stderr, call, stderrStream, () => void this.children.terminate(child));
@@ -28643,12 +29467,12 @@ function assertExecAdmissionActive(call, admissionActive) {
28643
29467
  }
28644
29468
  }
28645
29469
  function pipeOutput(source, call, stream, onFailure) {
28646
- return new Promise((resolve2) => {
29470
+ return new Promise((resolve5) => {
28647
29471
  let finished = false;
28648
29472
  const finish = () => {
28649
29473
  if (!finished) {
28650
29474
  finished = true;
28651
- resolve2();
29475
+ resolve5();
28652
29476
  }
28653
29477
  };
28654
29478
  source.on("data", (chunk) => {
@@ -28718,19 +29542,19 @@ function scheduleLongTimeout(milliseconds, callback) {
28718
29542
 
28719
29543
  // ../memoh/packages/runtime/src/core/fs.ts
28720
29544
  var import_grpc_js4 = __toESM(require_src3(), 1);
28721
- import { constants } from "node:fs";
29545
+ import { constants as constants3 } from "node:fs";
28722
29546
  import { randomBytes } from "node:crypto";
28723
29547
  import {
28724
- lstat,
28725
- open,
29548
+ lstat as lstat3,
29549
+ open as open2,
28726
29550
  readdir,
28727
29551
  rename as renamePath,
28728
- rm,
29552
+ rm as rm6,
28729
29553
  rmdir,
28730
- stat,
29554
+ stat as stat2,
28731
29555
  unlink
28732
29556
  } from "node:fs/promises";
28733
- import { basename, dirname, join, relative, sep } from "node:path";
29557
+ import { basename, dirname as dirname4, join as join5, relative, sep } from "node:path";
28734
29558
  var binaryProbeBytes = 8 * 1024;
28735
29559
  var readMaxLines = 2e3;
28736
29560
  var listMaxEntries = 200;
@@ -28744,7 +29568,7 @@ var WorkspaceFileService = class {
28744
29568
  const requested = requiredPath(request.path);
28745
29569
  const target = await this.paths.resolve(requested);
28746
29570
  await this.paths.revalidate(target);
28747
- const handle = await openNoFollow(target, constants.O_RDONLY, 0).catch((error) => {
29571
+ const handle = await openNoFollow(target, constants3.O_RDONLY, 0).catch((error) => {
28748
29572
  throw mapNodeError(error, "open");
28749
29573
  });
28750
29574
  try {
@@ -28789,13 +29613,13 @@ var WorkspaceFileService = class {
28789
29613
  async writeFile(request) {
28790
29614
  const requested = requiredPath(request.path);
28791
29615
  const target = await this.paths.prepareWrite(requested);
28792
- const temporary = join(
28793
- dirname(target),
29616
+ const temporary = join5(
29617
+ dirname4(target),
28794
29618
  `.${basename(target)}.tmp-${randomBytes(8).toString("hex")}`
28795
29619
  );
28796
29620
  const handle = await openNoFollow(
28797
29621
  temporary,
28798
- constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL,
29622
+ constants3.O_WRONLY | constants3.O_CREAT | constants3.O_EXCL,
28799
29623
  384
28800
29624
  ).catch((error) => {
28801
29625
  throw mapNodeError(error, "open");
@@ -28809,7 +29633,7 @@ var WorkspaceFileService = class {
28809
29633
  throw mapNodeError(error, "write");
28810
29634
  } finally {
28811
29635
  await handle.close().catch(() => void 0);
28812
- await rm(temporary, { force: true }).catch(() => void 0);
29636
+ await rm6(temporary, { force: true }).catch(() => void 0);
28813
29637
  }
28814
29638
  return {};
28815
29639
  }
@@ -28840,7 +29664,7 @@ var WorkspaceFileService = class {
28840
29664
  const target = await this.paths.resolve(requiredPath(request.path));
28841
29665
  await this.paths.revalidate(target);
28842
29666
  try {
28843
- const info = await stat(target);
29667
+ const info = await stat2(target);
28844
29668
  return { entry: fileEntry(basename(target), info) };
28845
29669
  } catch (error) {
28846
29670
  throw mapNodeError(error, "stat");
@@ -28870,7 +29694,7 @@ var WorkspaceFileService = class {
28870
29694
  followFinalSymlink: false
28871
29695
  });
28872
29696
  try {
28873
- const info = await lstat(target);
29697
+ const info = await lstat3(target);
28874
29698
  if (info.isSymbolicLink()) {
28875
29699
  await unlink(target);
28876
29700
  return {};
@@ -28879,12 +29703,12 @@ var WorkspaceFileService = class {
28879
29703
  if (info.isDirectory() && !request.recursive) {
28880
29704
  await rmdir(target);
28881
29705
  } else if (info.isDirectory()) {
28882
- await rm(target, { recursive: true, force: true });
29706
+ await rm6(target, { recursive: true, force: true });
28883
29707
  } else {
28884
29708
  await unlink(target);
28885
29709
  }
28886
29710
  } catch (error) {
28887
- if (nodeErrorCode(error) !== "ENOENT" && !(isServiceError(error) && error.code === import_grpc_js4.status.NOT_FOUND)) {
29711
+ if (nodeErrorCode2(error) !== "ENOENT" && !(isServiceError(error) && error.code === import_grpc_js4.status.NOT_FOUND)) {
28888
29712
  throw mapNodeError(error, "delete");
28889
29713
  }
28890
29714
  }
@@ -28893,7 +29717,7 @@ var WorkspaceFileService = class {
28893
29717
  async openReadRaw(requestedPath) {
28894
29718
  const target = await this.paths.resolve(requiredPath(requestedPath));
28895
29719
  await this.paths.revalidate(target);
28896
- return openNoFollow(target, constants.O_RDONLY, 0).catch((error) => {
29720
+ return openNoFollow(target, constants3.O_RDONLY, 0).catch((error) => {
28897
29721
  throw mapNodeError(error, "open");
28898
29722
  });
28899
29723
  }
@@ -28902,7 +29726,7 @@ var WorkspaceFileService = class {
28902
29726
  await this.paths.revalidate(target, { allowMissing: true });
28903
29727
  return openNoFollow(
28904
29728
  target,
28905
- constants.O_WRONLY | constants.O_CREAT | constants.O_TRUNC,
29729
+ constants3.O_WRONLY | constants3.O_CREAT | constants3.O_TRUNC,
28906
29730
  384
28907
29731
  ).catch((error) => {
28908
29732
  throw mapNodeError(error, "open");
@@ -28918,9 +29742,9 @@ var WorkspaceFileService = class {
28918
29742
  children.sort((left, right) => compareGoFilenames(left.name, right.name));
28919
29743
  const entries = [];
28920
29744
  for (const child of children) {
28921
- const target = join(directory, child.name);
29745
+ const target = join5(directory, child.name);
28922
29746
  try {
28923
- const info = await lstat(target);
29747
+ const info = await lstat3(target);
28924
29748
  entries.push(fileEntry(child.name, info));
28925
29749
  } catch {
28926
29750
  }
@@ -28938,9 +29762,9 @@ var WorkspaceFileService = class {
28938
29762
  }
28939
29763
  children.sort((left, right) => compareGoFilenames(left.name, right.name));
28940
29764
  for (const child of children) {
28941
- const target = join(current, child.name);
29765
+ const target = join5(current, child.name);
28942
29766
  try {
28943
- const info = await lstat(target);
29767
+ const info = await lstat3(target);
28944
29768
  const name = relative(directory, target).split(sep).join("/");
28945
29769
  entries.push(fileEntry(name, info));
28946
29770
  if (info.isDirectory() && !info.isSymbolicLink()) {
@@ -28962,8 +29786,8 @@ function requiredPath(value, field = "path") {
28962
29786
  return value;
28963
29787
  }
28964
29788
  function openNoFollow(path, flags, mode) {
28965
- const noFollow = process.platform === "win32" ? 0 : constants.O_NOFOLLOW;
28966
- return open(path, flags | noFollow, mode);
29789
+ const noFollow = process.platform === "win32" ? 0 : constants3.O_NOFOLLOW;
29790
+ return open2(path, flags | noFollow, mode);
28967
29791
  }
28968
29792
  function fileEntry(path, info) {
28969
29793
  return {
@@ -29033,15 +29857,15 @@ function formatMode(mode, directory = false, symbolicLink = false) {
29033
29857
  prefix = "-";
29034
29858
  }
29035
29859
  const permissions = [
29036
- [constants.S_IRUSR, "r"],
29037
- [constants.S_IWUSR, "w"],
29038
- [constants.S_IXUSR, "x"],
29039
- [constants.S_IRGRP, "r"],
29040
- [constants.S_IWGRP, "w"],
29041
- [constants.S_IXGRP, "x"],
29042
- [constants.S_IROTH, "r"],
29043
- [constants.S_IWOTH, "w"],
29044
- [constants.S_IXOTH, "x"]
29860
+ [constants3.S_IRUSR, "r"],
29861
+ [constants3.S_IWUSR, "w"],
29862
+ [constants3.S_IXUSR, "x"],
29863
+ [constants3.S_IRGRP, "r"],
29864
+ [constants3.S_IWGRP, "w"],
29865
+ [constants3.S_IXGRP, "x"],
29866
+ [constants3.S_IROTH, "r"],
29867
+ [constants3.S_IWOTH, "w"],
29868
+ [constants3.S_IXOTH, "x"]
29045
29869
  ];
29046
29870
  return prefix + permissions.map(([bit, value]) => mode & bit ? value : "-").join("");
29047
29871
  }
@@ -29119,20 +29943,20 @@ function decodeLine(line) {
29119
29943
 
29120
29944
  // ../memoh/packages/runtime/src/core/paths.ts
29121
29945
  var import_grpc_js5 = __toESM(require_src3(), 1);
29122
- import { constants as constants2 } from "node:fs";
29123
- import { access, lstat as lstat2, mkdir, realpath, stat as stat2 } from "node:fs/promises";
29124
- import { dirname as dirname2, isAbsolute as isAbsolute2, resolve, sep as sep2 } from "node:path";
29946
+ import { constants as constants4 } from "node:fs";
29947
+ import { access as access3, lstat as lstat4, mkdir as mkdir3, realpath as realpath2, stat as stat3 } from "node:fs/promises";
29948
+ import { dirname as dirname5, isAbsolute as isAbsolute3, resolve as resolve3, sep as sep2 } from "node:path";
29125
29949
  var HostPathResolver = class _HostPathResolver {
29126
29950
  defaultDirectory;
29127
29951
  constructor(defaultDirectory) {
29128
29952
  this.defaultDirectory = defaultDirectory;
29129
29953
  }
29130
29954
  static async create(defaultDirectory) {
29131
- if (!defaultDirectory.trim() || !isAbsolute2(defaultDirectory)) {
29955
+ if (!defaultDirectory.trim() || !isAbsolute3(defaultDirectory)) {
29132
29956
  throw new Error("default directory must be an absolute path");
29133
29957
  }
29134
- const canonical = await realpath(resolve(defaultDirectory));
29135
- const info = await stat2(canonical);
29958
+ const canonical = await realpath2(resolve3(defaultDirectory));
29959
+ const info = await stat3(canonical);
29136
29960
  if (!info.isDirectory()) {
29137
29961
  throw new Error("default directory must be a directory");
29138
29962
  }
@@ -29151,20 +29975,20 @@ var HostPathResolver = class _HostPathResolver {
29151
29975
  if (missingSegments.length > 0 && !options.allowMissing) {
29152
29976
  throw rpcError(import_grpc_js5.status.NOT_FOUND, "path does not exist");
29153
29977
  }
29154
- let canonicalAncestor = await realpath(ancestor).catch((error) => {
29978
+ let canonicalAncestor = await realpath2(ancestor).catch((error) => {
29155
29979
  throw mapNodeError(error, "realpath");
29156
29980
  });
29157
29981
  if (missingSegments.length === 0 && options.followFinalSymlink === false) {
29158
- const info = await lstat2(candidate).catch((error) => {
29982
+ const info = await lstat4(candidate).catch((error) => {
29159
29983
  throw mapNodeError(error, "lstat");
29160
29984
  });
29161
29985
  if (info.isSymbolicLink()) {
29162
29986
  canonicalAncestor = candidate;
29163
29987
  }
29164
29988
  }
29165
- const resolvedPath = resolve(canonicalAncestor, ...missingSegments);
29989
+ const resolvedPath = resolve3(canonicalAncestor, ...missingSegments);
29166
29990
  if (missingSegments.length === 0) {
29167
- const target = await stat2(resolvedPath).catch((error) => {
29991
+ const target = await stat3(resolvedPath).catch((error) => {
29168
29992
  throw mapNodeError(error, "stat");
29169
29993
  });
29170
29994
  if (options.requireDirectory && !target.isDirectory()) {
@@ -29179,7 +30003,7 @@ var HostPathResolver = class _HostPathResolver {
29179
30003
  }
29180
30004
  async prepareWrite(requestedPath) {
29181
30005
  const initial = await this.resolve(requestedPath, { allowMissing: true });
29182
- await this.ensureDirectory(dirname2(initial));
30006
+ await this.ensureDirectory(dirname5(initial));
29183
30007
  const checked = await this.resolve(initial, { allowMissing: true });
29184
30008
  if (checked !== initial) {
29185
30009
  throw rpcError(import_grpc_js5.status.PERMISSION_DENIED, "path changed while it was being checked");
@@ -29196,9 +30020,9 @@ var HostPathResolver = class _HostPathResolver {
29196
30020
  async ensureDirectory(requestedPath) {
29197
30021
  const target = await this.resolve(requestedPath, { allowMissing: true });
29198
30022
  try {
29199
- await mkdir(target, { recursive: true, mode: 488 });
30023
+ await mkdir3(target, { recursive: true, mode: 488 });
29200
30024
  } catch (error) {
29201
- if (nodeErrorCode(error) !== "EEXIST") {
30025
+ if (nodeErrorCode2(error) !== "EEXIST") {
29202
30026
  throw mapNodeError(error, "mkdir");
29203
30027
  }
29204
30028
  }
@@ -29210,15 +30034,15 @@ var HostPathResolver = class _HostPathResolver {
29210
30034
  return this.defaultDirectory;
29211
30035
  }
29212
30036
  if (bridgePath.startsWith("~/")) {
29213
- return resolve(this.defaultDirectory, bridgePath.slice(2));
30037
+ return resolve3(this.defaultDirectory, bridgePath.slice(2));
29214
30038
  }
29215
30039
  if (bridgePath.startsWith("/data/")) {
29216
- return resolve(this.defaultDirectory, bridgePath.slice("/data/".length));
30040
+ return resolve3(this.defaultDirectory, bridgePath.slice("/data/".length));
29217
30041
  }
29218
- if (isAbsolute2(value)) {
29219
- return resolve(value);
30042
+ if (isAbsolute3(value)) {
30043
+ return resolve3(value);
29220
30044
  }
29221
- return resolve(this.defaultDirectory, value);
30045
+ return resolve3(this.defaultDirectory, value);
29222
30046
  }
29223
30047
  };
29224
30048
  async function nearestExistingAncestor(candidate) {
@@ -29226,13 +30050,13 @@ async function nearestExistingAncestor(candidate) {
29226
30050
  let current = candidate;
29227
30051
  for (; ; ) {
29228
30052
  try {
29229
- await access(current, constants2.F_OK);
30053
+ await access3(current, constants4.F_OK);
29230
30054
  return { ancestor: current, missingSegments };
29231
30055
  } catch (error) {
29232
- if (nodeErrorCode(error) !== "ENOENT" && nodeErrorCode(error) !== "ENOTDIR") {
30056
+ if (nodeErrorCode2(error) !== "ENOENT" && nodeErrorCode2(error) !== "ENOTDIR") {
29233
30057
  throw mapNodeError(error, "access");
29234
30058
  }
29235
- const parent = dirname2(current);
30059
+ const parent = dirname5(current);
29236
30060
  if (parent === current) {
29237
30061
  throw mapNodeError(error, "access");
29238
30062
  }
@@ -29284,15 +30108,15 @@ async function startRuntimeGrpcServer(options) {
29284
30108
  }
29285
30109
  connections.clear();
29286
30110
  await children.close();
29287
- await new Promise((resolve2) => {
30111
+ await new Promise((resolve5) => {
29288
30112
  const timer = setTimeout(() => {
29289
30113
  server.forceShutdown();
29290
- resolve2();
30114
+ resolve5();
29291
30115
  }, 2e3);
29292
30116
  timer.unref();
29293
30117
  server.tryShutdown(() => {
29294
30118
  clearTimeout(timer);
29295
- resolve2();
30119
+ resolve5();
29296
30120
  });
29297
30121
  });
29298
30122
  })();
@@ -29470,14 +30294,14 @@ function waitForDrainOrCancellation(call) {
29470
30294
  if (call.cancelled) {
29471
30295
  return Promise.resolve();
29472
30296
  }
29473
- return new Promise((resolve2) => {
30297
+ return new Promise((resolve5) => {
29474
30298
  let settled = false;
29475
30299
  const finish = () => {
29476
30300
  if (!settled) {
29477
30301
  settled = true;
29478
30302
  call.off("drain", onDrain);
29479
30303
  call.off("cancelled", onCancelled);
29480
- resolve2();
30304
+ resolve5();
29481
30305
  }
29482
30306
  };
29483
30307
  const onDrain = () => finish();
@@ -29503,7 +30327,7 @@ async function locateBridgeProto() {
29503
30327
  ];
29504
30328
  for (const candidate of candidates) {
29505
30329
  try {
29506
- await access2(candidate);
30330
+ await access4(candidate);
29507
30331
  return candidate;
29508
30332
  } catch {
29509
30333
  }
@@ -29514,10 +30338,10 @@ async function locateBridgeProto() {
29514
30338
  // ../memoh/packages/runtime/package.json
29515
30339
  var package_default = {
29516
30340
  name: "@memohai/runtime",
29517
- version: "0.17.0",
30341
+ version: "0.19.0",
29518
30342
  repository: {
29519
30343
  type: "git",
29520
- url: "git+https://github.com/memohai/Memoh.git",
30344
+ url: "git+https://github.com/felinics/Memoh.git",
29521
30345
  directory: "packages/runtime"
29522
30346
  },
29523
30347
  description: "Embeddable Memoh Remote Runtime SDK and CLI",
@@ -29565,33 +30389,6 @@ var runtimeTeamIDHeader = "X-Team-ID";
29565
30389
  var runtimeMetadataMaxBytes = 8 * 1024;
29566
30390
  var runtimeHandshakeErrorMaxBytes = 8 * 1024;
29567
30391
  var runtimeHandshakeErrorReadTimeoutMs = 2e3;
29568
- function runtimeConnectUrl(serverUrl) {
29569
- const url = new URL(serverUrl);
29570
- if (url.protocol === "http:") {
29571
- url.protocol = "ws:";
29572
- } else if (url.protocol === "https:") {
29573
- url.protocol = "wss:";
29574
- }
29575
- const basePath = url.pathname.replace(/\/+$/, "");
29576
- url.pathname = `${basePath}/runtimes/connect`;
29577
- url.search = "";
29578
- url.hash = "";
29579
- return url;
29580
- }
29581
- function assertSecureRuntimeUrl(url, insecureLocalhost = false) {
29582
- if (url.username || url.password) {
29583
- throw new Error("runtime connection URL must not contain credentials");
29584
- }
29585
- if (url.protocol === "wss:") {
29586
- return;
29587
- }
29588
- const hostname2 = url.hostname.replace(/^\[|\]$/g, "");
29589
- const local = hostname2 === "127.0.0.1" || hostname2 === "localhost" || hostname2 === "::1";
29590
- if (url.protocol === "ws:" && insecureLocalhost && local) {
29591
- return;
29592
- }
29593
- throw new Error("runtime connections require wss://; use --insecure-localhost only for localhost development");
29594
- }
29595
30392
  function createHandshakeMetadata(workspaceBase, version = runtimeClientVersion, machine = {
29596
30393
  hostname: hostname(),
29597
30394
  os: platform(),
@@ -29665,7 +30462,7 @@ var RuntimeSession = class {
29665
30462
  try {
29666
30463
  const url = runtimeConnectUrl(this.config.serverUrl);
29667
30464
  assertSecureRuntimeUrl(url, this.config.insecureLocalhost);
29668
- const workspaceBase = await realpath2(this.config.workspaceBase);
30465
+ const workspaceBase = await realpath3(this.config.workspaceBase);
29669
30466
  let retry = 1e3;
29670
30467
  let lastError;
29671
30468
  while (!this.stopped && !controller.signal.aborted) {
@@ -29750,7 +30547,7 @@ var RuntimeSession = class {
29750
30547
  }
29751
30548
  };
29752
30549
  function waitForOpen(websocket) {
29753
- return new Promise((resolve2, reject) => {
30550
+ return new Promise((resolve5, reject) => {
29754
30551
  const cleanup = () => {
29755
30552
  websocket.off("open", onOpen);
29756
30553
  websocket.off("error", onError);
@@ -29759,7 +30556,7 @@ function waitForOpen(websocket) {
29759
30556
  };
29760
30557
  const onOpen = () => {
29761
30558
  cleanup();
29762
- resolve2();
30559
+ resolve5();
29763
30560
  };
29764
30561
  const onError = (error) => {
29765
30562
  cleanup();
@@ -29783,7 +30580,7 @@ function waitForOpen(websocket) {
29783
30580
  });
29784
30581
  }
29785
30582
  function readHandshakeError(response) {
29786
- return new Promise((resolve2) => {
30583
+ return new Promise((resolve5) => {
29787
30584
  const chunks = [];
29788
30585
  let size = 0;
29789
30586
  let settled = false;
@@ -29800,7 +30597,7 @@ function readHandshakeError(response) {
29800
30597
  settled = true;
29801
30598
  cleanup();
29802
30599
  response.resume();
29803
- resolve2(
30600
+ resolve5(
29804
30601
  Buffer.concat(chunks).toString("utf8").replace(/\s+/g, " ").trim()
29805
30602
  );
29806
30603
  };
@@ -29853,10 +30650,10 @@ function abortableDelay(milliseconds, signal) {
29853
30650
  if (signal?.aborted) {
29854
30651
  return Promise.resolve();
29855
30652
  }
29856
- return new Promise((resolve2) => {
30653
+ return new Promise((resolve5) => {
29857
30654
  const finish = () => {
29858
30655
  signal?.removeEventListener("abort", onAbort);
29859
- resolve2();
30656
+ resolve5();
29860
30657
  };
29861
30658
  const timer = setTimeout(finish, milliseconds);
29862
30659
  const onAbort = () => {
@@ -29878,60 +30675,223 @@ function sanitizeError(error, key) {
29878
30675
  return message.replaceAll(key, "<redacted>").replace(/mrk_[0-9a-f]{64}/g, "<redacted>").slice(0, 1024);
29879
30676
  }
29880
30677
 
29881
- // ../memoh/packages/runtime/src/cli.ts
29882
- async function main(args2) {
29883
- if (args2.includes("--help") || args2.includes("-h")) {
29884
- usage(0);
30678
+ // ../memoh/packages/runtime/src/cli-main.ts
30679
+ var connectionOptions = {
30680
+ server: { type: "string" },
30681
+ key: { type: "string" },
30682
+ "team-id": { type: "string" },
30683
+ config: { type: "string" },
30684
+ "insecure-localhost": { type: "boolean" },
30685
+ help: { type: "boolean", short: "h" }
30686
+ };
30687
+ async function runCLI(args2, overrides = {}) {
30688
+ const entryPath = overrides.entryPath ?? fileURLToPath2(import.meta.url);
30689
+ const context = {
30690
+ platform: process.platform,
30691
+ env: process.env,
30692
+ home: homedir2(),
30693
+ entryPath,
30694
+ uid: process.getuid?.(),
30695
+ runner: spawnCommand,
30696
+ createSession: (config, options) => new RuntimeSession(config, options),
30697
+ stdout: (message) => console.log(message),
30698
+ stderr: (message) => console.error(message),
30699
+ ...overrides
30700
+ };
30701
+ const [command, ...rest] = args2;
30702
+ if (["help", "--help", "-h"].includes(command)) {
30703
+ context.stdout("Usage: memoh-runtime <enroll|run|service|version>\nUse <command> --help for details.");
30704
+ return 0;
29885
30705
  }
29886
- if (args2.includes("--workspace-base")) {
29887
- throw new Error("--workspace-base is no longer supported; Remote Runtime uses the current user home directory");
30706
+ if (command === "version" || command === "--version") {
30707
+ context.stdout(runtimeClientVersion);
30708
+ return 0;
29888
30709
  }
29889
- const serverUrl = valueAfter(args2, "--server") ?? process.env.MEMOH_RUNTIME_SERVER;
29890
- const key = valueAfter(args2, "--key") ?? process.env.MEMOH_RUNTIME_KEY;
29891
- const teamId = (valueAfter(args2, "--team-id") ?? process.env.MEMOH_RUNTIME_TEAM_ID)?.trim() || void 0;
29892
- if (!serverUrl || !key) {
29893
- throw new Error("--server and --key are required (or set MEMOH_RUNTIME_SERVER and MEMOH_RUNTIME_KEY)");
30710
+ if (command === "enroll") return enroll(rest, context);
30711
+ if (command === "service") return service(rest, context);
30712
+ if (command === "run") return run(rest, context);
30713
+ if (!command || command.startsWith("-")) return run(args2, context);
30714
+ throw new Error(`unknown command: ${command}`);
30715
+ }
30716
+ async function run(args2, context) {
30717
+ const { values } = parseArgs({ args: args2, options: { ...connectionOptions, log: { type: "string" } }, strict: true });
30718
+ if (values.help) {
30719
+ context.stdout("Usage: memoh-runtime run [--server <url> --key <key> | --config <file>] [--log <file>]\nReads saved enrollment when no connection is supplied. Never saves or changes a service.");
30720
+ return 0;
29894
30721
  }
29895
- const workspaceBase = homedir();
30722
+ const log = createLogFile(stringValue(values.log), context.stderr);
29896
30723
  const controller = new AbortController();
29897
30724
  const stop = () => controller.abort();
29898
30725
  process.once("SIGINT", stop);
29899
30726
  process.once("SIGTERM", stop);
29900
30727
  try {
29901
- const session = new RuntimeSession({
29902
- serverUrl,
29903
- key,
29904
- teamId,
29905
- workspaceBase,
29906
- insecureLocalhost: args2.includes("--insecure-localhost")
29907
- }, {
29908
- onStatus: (status7, error) => console.log(error ? `${status7}: ${error}` : status7),
29909
- warn: (message) => console.warn(message)
29910
- });
29911
- await session.start(controller.signal);
30728
+ const enrollment = await resolveEnrollment(values, context);
30729
+ await context.createSession({ ...enrollment, workspaceBase: context.home }, {
30730
+ onStatus: (status7, error) => {
30731
+ const line = error ? `${status7}: ${error}` : status7;
30732
+ context.stdout(line);
30733
+ log.write(line);
30734
+ },
30735
+ warn: (message) => {
30736
+ context.stderr(message);
30737
+ log.write(message);
30738
+ }
30739
+ }).start(controller.signal);
30740
+ } catch (error) {
30741
+ log.write(`error: ${formatCLIError(error)}`);
30742
+ throw error;
29912
30743
  } finally {
29913
30744
  process.off("SIGINT", stop);
29914
30745
  process.off("SIGTERM", stop);
30746
+ await log.flush();
30747
+ }
30748
+ return 0;
30749
+ }
30750
+ function createLogFile(path, warn) {
30751
+ let queue = Promise.resolve();
30752
+ let reported = false;
30753
+ return {
30754
+ write(line) {
30755
+ if (!path) return;
30756
+ queue = queue.then(async () => {
30757
+ try {
30758
+ await mkdir4(dirname6(path), { recursive: true, mode: 448 });
30759
+ await appendFile(path, `${(/* @__PURE__ */ new Date()).toISOString()} ${line}
30760
+ `, { mode: 384 });
30761
+ } catch (error) {
30762
+ if (!reported) {
30763
+ reported = true;
30764
+ warn(`could not write log file ${path}: ${formatCLIError(error)}`);
30765
+ }
30766
+ }
30767
+ });
30768
+ },
30769
+ flush: () => queue
30770
+ };
30771
+ }
30772
+ async function enroll(args2, context) {
30773
+ const { values } = parseArgs({ args: args2, strict: true, options: { ...connectionOptions, replace: { type: "boolean" } } });
30774
+ if (values.help) {
30775
+ context.stdout("Usage: memoh-runtime enroll [--server <url> --key <key> | --config <file>] [--replace]\nSaves the enrollment used by future runs. A running service changes only after an explicit restart.");
30776
+ return 0;
29915
30777
  }
30778
+ assertManagedPaths(context);
30779
+ const paths = resolveRuntimePaths({ home: context.home });
30780
+ const enrollment = await resolveEnrollment(values, context);
30781
+ await withEnrollmentLock(paths.configPath, async () => {
30782
+ if (!values.replace) {
30783
+ let current;
30784
+ try {
30785
+ current = await readRuntimeEnrollmentIfExists(paths.configPath, context.home);
30786
+ } catch (error) {
30787
+ throw new Error(`${formatCLIError(error)}; pass --replace to overwrite it`);
30788
+ }
30789
+ if (current && !sameEnrollment(current, enrollment)) throw new Error("saved enrollment differs; pass --replace to replace it");
30790
+ }
30791
+ await writeRuntimeEnrollment(paths.configPath, enrollment);
30792
+ });
30793
+ context.stdout(`saved enrollment to ${paths.configPath}; restart a running service to apply it`);
30794
+ return 0;
29916
30795
  }
29917
- function valueAfter(args2, name) {
29918
- const index = args2.indexOf(name);
29919
- if (index < 0) {
29920
- return void 0;
30796
+ async function service(args2, context) {
30797
+ const [action, ...rest] = args2;
30798
+ if (!action || ["help", "--help", "-h"].includes(action)) {
30799
+ context.stdout("Usage: memoh-runtime service <install|start|stop|restart|status|uninstall>\nInstall registers a stopped service. Enrollment is managed separately with enroll.");
30800
+ return 0;
29921
30801
  }
29922
- const value = args2[index + 1];
29923
- if (!value || value.startsWith("--")) {
29924
- throw new Error(`${name} requires a value`);
30802
+ if (!["install", "start", "stop", "restart", "status", "uninstall"].includes(action)) throw new Error(`unknown service command: ${action}`);
30803
+ const { values } = parseArgs({ args: rest, strict: true, options: {
30804
+ help: { type: "boolean", short: "h" },
30805
+ ...action === "status" ? { json: { type: "boolean" } } : {}
30806
+ } });
30807
+ if (values.help) {
30808
+ context.stdout(`Usage: memoh-runtime service ${action}${action === "status" ? " [--json]" : ""}`);
30809
+ return 0;
29925
30810
  }
29926
- return value;
30811
+ assertManagedPaths(context);
30812
+ const paths = resolveRuntimePaths({ home: context.home });
30813
+ const manager = createRuntimeServiceManager({ platform: context.platform, paths, runner: context.runner, uid: context.uid });
30814
+ if (action === "status") {
30815
+ const status7 = await manager.status();
30816
+ context.stdout(values.json ? JSON.stringify(status7) : `Memoh Runtime service: ${status7.state} (${status7.backend})${status7.detail ? `: ${status7.detail}` : ""}`);
30817
+ return status7.state === "running" ? 0 : 1;
30818
+ }
30819
+ switch (action) {
30820
+ case "install": {
30821
+ const nodePath = await findNodeExecutable(context.env.PATH, context.platform);
30822
+ await access5(context.entryPath);
30823
+ await access5(join6(dirname6(context.entryPath), "bridge.proto"));
30824
+ await manager.stop();
30825
+ await waitForService(manager, "stopped");
30826
+ await manager.register({
30827
+ nodePath,
30828
+ entryPath: context.entryPath,
30829
+ configPath: paths.configPath,
30830
+ logsDir: paths.logsDir,
30831
+ workingDirectory: context.home,
30832
+ servicePath: serviceExecutablePath(nodePath, context.env.PATH, context.platform)
30833
+ });
30834
+ break;
30835
+ }
30836
+ case "stop":
30837
+ await manager.stop();
30838
+ await waitForService(manager, "stopped");
30839
+ break;
30840
+ case "uninstall":
30841
+ await manager.uninstall();
30842
+ await waitForService(manager, "not-installed");
30843
+ break;
30844
+ case "start":
30845
+ case "restart":
30846
+ await readRuntimeEnrollment(paths.configPath, context.home);
30847
+ if (action === "restart") {
30848
+ await manager.stop();
30849
+ await waitForService(manager, "stopped");
30850
+ }
30851
+ await manager.start();
30852
+ await waitForService(manager, "running");
30853
+ break;
30854
+ }
30855
+ context.stdout(action === "install" ? "installed Memoh Runtime service (stopped); run service start to connect" : action === "uninstall" ? "uninstalled Memoh Runtime service; saved enrollment was retained" : action === "stop" ? "stopped Memoh Runtime service; it starts again at the next login unless you run service uninstall" : `${action === "restart" ? "restarted" : "started"} Memoh Runtime service`);
30856
+ return 0;
30857
+ }
30858
+ async function resolveEnrollment(values, context) {
30859
+ const explicitConfig = stringValue(values.config);
30860
+ if (explicitConfig) {
30861
+ if (["server", "key", "team-id", "insecure-localhost"].some((key2) => values[key2] !== void 0)) throw new Error("--config cannot be combined with connection flags");
30862
+ return readRuntimeEnrollment(resolve4(explicitConfig), context.home);
30863
+ }
30864
+ const serverUrl = stringValue(values.server) ?? context.env.MEMOH_RUNTIME_SERVER;
30865
+ const key = stringValue(values.key) ?? context.env.MEMOH_RUNTIME_KEY;
30866
+ if (serverUrl || key) {
30867
+ if (!serverUrl || !key) throw new Error("--server and --key must be provided together");
30868
+ return normalizeRuntimeEnrollment({
30869
+ serverUrl,
30870
+ key,
30871
+ teamId: stringValue(values["team-id"]) ?? context.env.MEMOH_RUNTIME_TEAM_ID,
30872
+ insecureLocalhost: values["insecure-localhost"] === true || parseBooleanEnvironment(context.env.MEMOH_RUNTIME_INSECURE_LOCALHOST, "MEMOH_RUNTIME_INSECURE_LOCALHOST") === true
30873
+ }, context.home);
30874
+ }
30875
+ if (["team-id", "insecure-localhost"].some((key2) => values[key2] !== void 0)) throw new Error("connection options require --server and --key");
30876
+ const input = context.env.MEMOH_RUNTIME_CONFIG ?? resolveRuntimePaths({ home: context.home }).configPath;
30877
+ return readRuntimeEnrollment(resolve4(input), context.home);
30878
+ }
30879
+ function assertManagedPaths(context) {
30880
+ if (context.env.MEMOH_RUNTIME_HOME) throw new Error("MEMOH_RUNTIME_HOME is not supported for managed enrollment or services; use the fixed user runtime directory");
30881
+ }
30882
+ function stringValue(value) {
30883
+ return typeof value === "string" ? value : void 0;
29927
30884
  }
29928
- function usage(exitCode) {
29929
- console.error("Usage: memoh-runtime --server <url> --key <key> [--team-id <uuid>] [--insecure-localhost]");
29930
- process.exit(exitCode);
30885
+ function formatCLIError(error) {
30886
+ return error instanceof Error ? error.message : String(error);
29931
30887
  }
29932
- main(process.argv.slice(2)).catch((error) => {
29933
- console.error(error instanceof Error ? error.message : String(error));
29934
- process.exit(1);
30888
+
30889
+ // ../memoh/packages/runtime/src/cli.ts
30890
+ runCLI(process.argv.slice(2)).then((code) => {
30891
+ process.exitCode = code;
30892
+ }).catch((error) => {
30893
+ console.error(formatCLIError(error));
30894
+ process.exitCode = 1;
29935
30895
  });
29936
30896
  /*! Bundled license information:
29937
30897