@hermespilot/link 0.7.5 → 0.7.6

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.
@@ -2314,6 +2314,9 @@ function resolveHermesProfilesDir() {
2314
2314
  function resolveHermesConfigPath(profileName = "default") {
2315
2315
  return path3.join(resolveHermesProfileDir(profileName), "config.yaml");
2316
2316
  }
2317
+ function resolveHermesEnvPath(profileName = "default") {
2318
+ return path3.join(resolveHermesProfileDir(profileName), ".env");
2319
+ }
2317
2320
  async function readHermesSessionsDir(profileName = "default", configPath = resolveHermesConfigPath(profileName)) {
2318
2321
  const profileDir = resolveHermesProfileDir(profileName);
2319
2322
  const { config } = await readHermesConfigDocument(configPath);
@@ -2946,7 +2949,9 @@ async function readHermesProfilePermissions(profileName = "default", configPath
2946
2949
  }
2947
2950
  async function saveHermesProfilePermissions(profileName, input, configPath = resolveHermesConfigPath(profileName)) {
2948
2951
  const { document, config, existingRaw } = await readHermesConfigDocument(configPath);
2952
+ let configTouched = false;
2949
2953
  if (input.approvals) {
2954
+ configTouched = true;
2950
2955
  const approvals = ensureRecord(config, "approvals");
2951
2956
  if (input.approvals.mode !== void 0) {
2952
2957
  approvals.mode = normalizeApprovalMode(input.approvals.mode);
@@ -2962,6 +2967,7 @@ async function saveHermesProfilePermissions(profileName, input, configPath = res
2962
2967
  }
2963
2968
  }
2964
2969
  if (input.terminal) {
2970
+ configTouched = true;
2965
2971
  const terminal = ensureRecord(config, "terminal");
2966
2972
  if (input.terminal.backend !== void 0) {
2967
2973
  terminal.backend = normalizeTerminalBackend(input.terminal.backend);
@@ -2991,7 +2997,24 @@ async function saveHermesProfilePermissions(profileName, input, configPath = res
2991
2997
  terminal.container_persistent = input.terminal.containerPersistent;
2992
2998
  }
2993
2999
  }
3000
+ let envChanged = false;
3001
+ if (input.sudo) {
3002
+ if (input.sudo.clear) {
3003
+ envChanged = await deleteHermesEnvValue(profileName, "SUDO_PASSWORD") || envChanged;
3004
+ const terminal = toRecord(config.terminal);
3005
+ if (terminal.sudo_password !== void 0) {
3006
+ delete terminal.sudo_password;
3007
+ configTouched = true;
3008
+ }
3009
+ }
3010
+ if (input.sudo.password !== void 0) {
3011
+ const password = normalizeSudoPassword(input.sudo.password);
3012
+ await writeHermesEnvValue(profileName, "SUDO_PASSWORD", password);
3013
+ envChanged = true;
3014
+ }
3015
+ }
2994
3016
  if (input.toolsets) {
3017
+ configTouched = true;
2995
3018
  const env = await readHermesEnvFile(profileName);
2996
3019
  const currentPermissions = profilePermissionsFromConfig(
2997
3020
  profileName,
@@ -3019,16 +3042,16 @@ async function saveHermesProfilePermissions(profileName, input, configPath = res
3019
3042
  }
3020
3043
  platformToolsets.api_server = next;
3021
3044
  }
3022
- const backupPath = await writeHermesConfigDocument({
3045
+ const backupPath = configTouched ? await writeHermesConfigDocument({
3023
3046
  configPath,
3024
3047
  document,
3025
3048
  config,
3026
3049
  existingRaw
3027
- });
3050
+ }) : null;
3028
3051
  return {
3029
3052
  ...await readHermesProfilePermissions(profileName, configPath),
3030
3053
  backupPath,
3031
- requiresGatewayReload: true,
3054
+ requiresGatewayReload: configTouched || envChanged,
3032
3055
  restartHint: PROFILE_PERMISSIONS_RESTART_HINT
3033
3056
  };
3034
3057
  }
@@ -4947,6 +4970,7 @@ function readPositiveInteger(value) {
4947
4970
  function profilePermissionsFromConfig(profileName, configPath, config, env) {
4948
4971
  const approvals = toRecord(config.approvals);
4949
4972
  const terminal = toRecord(config.terminal);
4973
+ const envPath = resolveHermesEnvPath(profileName);
4950
4974
  const platformToolsets = toRecord(config.platform_toolsets);
4951
4975
  const apiServerToolsets = readStringList(platformToolsets.api_server);
4952
4976
  const hasExplicitToolsets = apiServerToolsets.some(
@@ -4960,6 +4984,7 @@ function profilePermissionsFromConfig(profileName, configPath, config, env) {
4960
4984
  return {
4961
4985
  profileName,
4962
4986
  configPath,
4987
+ envPath,
4963
4988
  approvals: {
4964
4989
  mode: readApprovalMode(approvals.mode),
4965
4990
  timeout: readPositiveInteger(approvals.timeout) ?? 60,
@@ -4973,6 +4998,11 @@ function profilePermissionsFromConfig(profileName, configPath, config, env) {
4973
4998
  containerDisk: readPositiveInteger(terminal.container_disk) ?? null,
4974
4999
  containerPersistent: terminal.container_persistent !== false
4975
5000
  },
5001
+ sudo: {
5002
+ configured: isEnvValueConfigured(env.SUDO_PASSWORD) || isEnvValueConfigured(readString2(terminal.sudo_password)),
5003
+ envKey: "SUDO_PASSWORD",
5004
+ envPath
5005
+ },
4976
5006
  toolsets: {
4977
5007
  items: PROFILE_PERMISSION_TOOLSETS.map((toolset) => {
4978
5008
  const configState = readToolsetConfigState(toolset.key, config, env);
@@ -5175,6 +5205,15 @@ function normalizeCronApprovalMode(value) {
5175
5205
  }
5176
5206
  throw new Error("approvals.cron_mode must be deny or approve");
5177
5207
  }
5208
+ function normalizeSudoPassword(value) {
5209
+ if (!value) {
5210
+ throw new Error("sudo.password must be non-empty");
5211
+ }
5212
+ if (value.includes("\n") || value.includes("\r") || value.includes("\0")) {
5213
+ throw new Error("sudo.password must not contain line breaks");
5214
+ }
5215
+ return value;
5216
+ }
5178
5217
  function normalizeTerminalBackend(value) {
5179
5218
  const backend = value.trim().toLowerCase();
5180
5219
  if (TERMINAL_BACKENDS.has(backend)) {
@@ -5932,7 +5971,7 @@ async function readHermesApiServerEnvOverrides(profileName) {
5932
5971
  };
5933
5972
  }
5934
5973
  async function readHermesEnvFile(profileName) {
5935
- const envPath = path3.join(resolveHermesProfileDir(profileName), ".env");
5974
+ const envPath = resolveHermesEnvPath(profileName);
5936
5975
  const raw = await readFile2(envPath, "utf8").catch((error) => {
5937
5976
  if (isNodeError3(error, "ENOENT")) {
5938
5977
  return "";
@@ -5956,7 +5995,7 @@ async function readHermesEnvFile(profileName) {
5956
5995
  return values;
5957
5996
  }
5958
5997
  async function writeHermesEnvValue(profileName, key, value) {
5959
- const envPath = path3.join(resolveHermesProfileDir(profileName), ".env");
5998
+ const envPath = resolveHermesEnvPath(profileName);
5960
5999
  const existingRaw = await readFile2(envPath, "utf8").catch(
5961
6000
  (error) => {
5962
6001
  if (isNodeError3(error, "ENOENT")) {
@@ -5991,8 +6030,32 @@ async function writeHermesEnvValue(profileName, key, value) {
5991
6030
  }
5992
6031
  await atomicWriteFilePreservingMetadata(envPath, nextRaw);
5993
6032
  }
6033
+ async function deleteHermesEnvValue(profileName, key) {
6034
+ const envPath = resolveHermesEnvPath(profileName);
6035
+ const raw = await readFile2(envPath, "utf8").catch((error) => {
6036
+ if (isNodeError3(error, "ENOENT")) {
6037
+ return "";
6038
+ }
6039
+ throw error;
6040
+ });
6041
+ if (!raw) {
6042
+ return false;
6043
+ }
6044
+ const keyPattern = new RegExp(`^(?:export\\s+)?${escapeRegExp(key)}=`, "u");
6045
+ const lines = raw.split(/\r?\n/u);
6046
+ const nextLines = lines.filter((line) => !keyPattern.test(line.trim()));
6047
+ const nextRaw = nextLines.join("\n").replace(/\n*$/u, "\n");
6048
+ if (nextRaw === raw) {
6049
+ return false;
6050
+ }
6051
+ await atomicWriteFilePreservingMetadata(`${envPath}.bak.${Date.now()}`, raw, {
6052
+ metadataSourcePath: envPath
6053
+ });
6054
+ await atomicWriteFilePreservingMetadata(envPath, nextRaw);
6055
+ return true;
6056
+ }
5994
6057
  async function writeHermesApiServerEnv(profileName, config, options = {}) {
5995
- const envPath = path3.join(resolveHermesProfileDir(profileName), ".env");
6058
+ const envPath = resolveHermesEnvPath(profileName);
5996
6059
  const existingRaw = await readFile2(envPath, "utf8").catch(
5997
6060
  (error) => {
5998
6061
  if (isNodeError3(error, "ENOENT")) {
@@ -6016,7 +6079,7 @@ async function writeHermesApiServerEnv(profileName, config, options = {}) {
6016
6079
  );
6017
6080
  }
6018
6081
  async function writeHermesEnvValues(profileName, values, existingRaw) {
6019
- const envPath = path3.join(resolveHermesProfileDir(profileName), ".env");
6082
+ const envPath = resolveHermesEnvPath(profileName);
6020
6083
  const raw = existingRaw ?? await readFile2(envPath, "utf8").catch((error) => {
6021
6084
  if (isNodeError3(error, "ENOENT")) {
6022
6085
  return "";
@@ -6510,7 +6573,7 @@ function isConversationMissingError(error) {
6510
6573
  }
6511
6574
 
6512
6575
  // src/constants.ts
6513
- var LINK_VERSION = "0.7.5";
6576
+ var LINK_VERSION = "0.7.6";
6514
6577
  var LINK_COMMAND = "hermeslink";
6515
6578
  var LINK_DEFAULT_PORT = 52379;
6516
6579
  var LINK_RUNTIME_DIR_NAME = ".hermeslink";
@@ -27375,6 +27438,17 @@ function readProfilePermissionsInput(body) {
27375
27438
  )
27376
27439
  };
27377
27440
  }
27441
+ const sudo = readOptionalObject(body, "sudo");
27442
+ if (sudo) {
27443
+ const clear = readBoolean3(sudo.clear ?? sudo.remove ?? sudo.delete);
27444
+ const sudoInput = {
27445
+ password: readRawString(sudo, "password") ?? readRawString(sudo, "sudo_password") ?? readRawString(sudo, "sudoPassword") ?? void 0,
27446
+ clear: clear === true ? true : void 0
27447
+ };
27448
+ if (sudoInput.password !== void 0 || sudoInput.clear !== void 0) {
27449
+ input.sudo = sudoInput;
27450
+ }
27451
+ }
27378
27452
  const toolsets = readOptionalObject(body, "toolsets");
27379
27453
  if (toolsets) {
27380
27454
  input.toolsets = {
@@ -27408,6 +27482,10 @@ function readOptionalObject(body, key) {
27408
27482
  }
27409
27483
  return value;
27410
27484
  }
27485
+ function readRawString(body, key) {
27486
+ const value = body[key];
27487
+ return typeof value === "string" ? value : null;
27488
+ }
27411
27489
  function readStringListValue(value, field) {
27412
27490
  if (value === void 0) {
27413
27491
  return null;
package/dist/cli/index.js CHANGED
@@ -53,7 +53,7 @@ import {
53
53
  stopDaemonProcess,
54
54
  summarizeUsageProbeEnsure,
55
55
  translate
56
- } from "../chunk-MVKOUAVA.js";
56
+ } from "../chunk-P54DBGLE.js";
57
57
 
58
58
  // src/cli/index.ts
59
59
  import { Command } from "commander";
package/dist/http/app.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createApp
3
- } from "../chunk-MVKOUAVA.js";
3
+ } from "../chunk-P54DBGLE.js";
4
4
  export {
5
5
  createApp
6
6
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hermespilot/link",
3
- "version": "0.7.5",
3
+ "version": "0.7.6",
4
4
  "private": false,
5
5
  "description": "Hermes Link companion service and CLI for connecting hermes-agent through HermesPilot",
6
6
  "license": "MIT",