handsoff 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6991,11 +6991,23 @@ var InteractionQueue = class {
6991
6991
  }
6992
6992
  return count;
6993
6993
  }
6994
+ getTimeoutMs() {
6995
+ return this.timeoutMs;
6996
+ }
6997
+ setTimeoutMs(ms) {
6998
+ this.timeoutMs = ms;
6999
+ }
7000
+ getDefaultOnTimeout() {
7001
+ return this.defaultOnTimeout;
7002
+ }
7003
+ setDefaultOnTimeout(value) {
7004
+ this.defaultOnTimeout = value;
7005
+ }
6994
7006
  };
6995
7007
 
6996
7008
  // src/config.ts
6997
7009
  init_esm_shims();
6998
- import { readFileSync as readFileSync4, existsSync as existsSync7 } from "fs";
7010
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync7 } from "fs";
6999
7011
  import { join as join5 } from "path";
7000
7012
  import { homedir as homedir2 } from "os";
7001
7013
  import TOML from "@iarna/toml";
@@ -7141,6 +7153,29 @@ function loadConfig() {
7141
7153
  return DEFAULT_CONFIG;
7142
7154
  }
7143
7155
  }
7156
+ function savePermissionConfig(timeoutMs, defaultOnTimeout) {
7157
+ const configPath = getConfigPath();
7158
+ try {
7159
+ let raw = {};
7160
+ if (existsSync7(configPath)) {
7161
+ const content = readFileSync4(configPath, "utf-8");
7162
+ raw = TOML.parse(content);
7163
+ }
7164
+ if (!raw.channel) {
7165
+ raw.channel = {};
7166
+ }
7167
+ if (!raw.channel.permission) {
7168
+ raw.channel.permission = {};
7169
+ }
7170
+ raw.channel.permission.timeout_ms = timeoutMs;
7171
+ raw.channel.permission.default_on_timeout = defaultOnTimeout;
7172
+ const tomlString = TOML.stringify(raw);
7173
+ writeFileSync3(configPath, tomlString, "utf-8");
7174
+ } catch (error2) {
7175
+ console.error(`Failed to save permission config to ${configPath}:`, error2);
7176
+ throw error2;
7177
+ }
7178
+ }
7144
7179
 
7145
7180
  // src/core/bus/index.ts
7146
7181
  init_esm_shims();
@@ -7885,7 +7920,7 @@ init_esm_shims();
7885
7920
 
7886
7921
  // src/core/binding/BindingService.ts
7887
7922
  init_esm_shims();
7888
- import { writeFileSync as writeFileSync3 } from "fs";
7923
+ import { writeFileSync as writeFileSync4 } from "fs";
7889
7924
  import { join as join6 } from "path";
7890
7925
  import { homedir as homedir3 } from "os";
7891
7926
  import TOML2 from "@iarna/toml";
@@ -7996,7 +8031,7 @@ var BindingService = class {
7996
8031
  bindings
7997
8032
  };
7998
8033
  const configPath = join6(homedir3(), ".handsoff", "config.toml");
7999
- writeFileSync3(configPath, TOML2.stringify(merged));
8034
+ writeFileSync4(configPath, TOML2.stringify(merged));
8000
8035
  this.invalidateCache();
8001
8036
  }
8002
8037
  };
@@ -8015,7 +8050,8 @@ var SYSTEM_COMMAND_ROOTS = /* @__PURE__ */ new Set([
8015
8050
  "clear",
8016
8051
  "stop",
8017
8052
  "session",
8018
- "system"
8053
+ "system",
8054
+ "permission"
8019
8055
  ]);
8020
8056
  function getSlashCommandRoot(text) {
8021
8057
  const trimmed = text.trim();
@@ -8643,6 +8679,126 @@ function registerSystemCommands(registerFn) {
8643
8679
  }
8644
8680
  }
8645
8681
 
8682
+ // src/core/command/builtin/permission.ts
8683
+ init_esm_shims();
8684
+ function parseDuration(input) {
8685
+ const trimmed = input.trim().toLowerCase();
8686
+ const match2 = trimmed.match(/^(\d+)([smhd]?)$/);
8687
+ if (!match2) {
8688
+ throw new Error(`Invalid duration: "${input}". Use format like 300, 300s, 5m, 1h, 1d (default unit: seconds)`);
8689
+ }
8690
+ const value = parseInt(match2[1], 10);
8691
+ const unit = match2[2];
8692
+ switch (unit) {
8693
+ case "s":
8694
+ return value * 1e3;
8695
+ case "m":
8696
+ return value * 60 * 1e3;
8697
+ case "h":
8698
+ return value * 60 * 60 * 1e3;
8699
+ case "d":
8700
+ return value * 24 * 60 * 60 * 1e3;
8701
+ default:
8702
+ return value * 1e3;
8703
+ }
8704
+ }
8705
+ function formatDuration3(ms) {
8706
+ if (ms >= 24 * 60 * 60 * 1e3) {
8707
+ return `${(ms / (24 * 60 * 60 * 1e3)).toFixed(1)}d`;
8708
+ }
8709
+ if (ms >= 60 * 60 * 1e3) {
8710
+ return `${(ms / (60 * 60 * 1e3)).toFixed(1)}h`;
8711
+ }
8712
+ if (ms >= 60 * 1e3) {
8713
+ return `${(ms / (60 * 1e3)).toFixed(1)}m`;
8714
+ }
8715
+ if (ms >= 1e3) {
8716
+ return `${(ms / 1e3).toFixed(1)}s`;
8717
+ }
8718
+ return `${ms}ms`;
8719
+ }
8720
+ var permissionTimeoutCommand = {
8721
+ description: "Set permission request timeout duration",
8722
+ usage: "/permission timeout <duration> (e.g. 300, 5m, 1h). Default unit: seconds.",
8723
+ handler: async (args, context) => {
8724
+ if (args.length === 0) {
8725
+ return "Usage: /permission timeout <duration>\nDefault unit is seconds. Examples: /permission timeout 300 (300s), /permission timeout 5m";
8726
+ }
8727
+ const accessor = context.permissionConfig;
8728
+ if (!accessor) {
8729
+ return "Permission config accessor is not available.";
8730
+ }
8731
+ const durationStr = args[0];
8732
+ try {
8733
+ const ms = parseDuration(durationStr);
8734
+ if (ms < 1e3) {
8735
+ return "Timeout must be at least 1000ms (1s).";
8736
+ }
8737
+ if (ms > 24 * 60 * 60 * 1e3) {
8738
+ return "Timeout must not exceed 1 day.";
8739
+ }
8740
+ accessor.setTimeoutMs(ms);
8741
+ const defaultOnTimeout = accessor.getDefaultOnTimeout();
8742
+ savePermissionConfig(ms, defaultOnTimeout);
8743
+ return `Permission timeout set to ${formatDuration3(ms)} (${ms}ms) and saved to config.`;
8744
+ } catch (error2) {
8745
+ const message = error2 instanceof Error ? error2.message : String(error2);
8746
+ return `Failed to set timeout: ${message}`;
8747
+ }
8748
+ }
8749
+ };
8750
+ var permissionDefaultCommand = {
8751
+ description: "Set default action when permission request times out",
8752
+ usage: "/permission default <allow|deny>",
8753
+ handler: async (args, context) => {
8754
+ if (args.length === 0) {
8755
+ return "Usage: /permission default <allow|deny>";
8756
+ }
8757
+ const accessor = context.permissionConfig;
8758
+ if (!accessor) {
8759
+ return "Permission config accessor is not available.";
8760
+ }
8761
+ const value = args[0].toLowerCase();
8762
+ if (value !== "allow" && value !== "deny") {
8763
+ return 'Invalid value. Use "allow" or "deny".';
8764
+ }
8765
+ accessor.setDefaultOnTimeout(value);
8766
+ const timeoutMs = accessor.getTimeoutMs();
8767
+ savePermissionConfig(timeoutMs, value);
8768
+ return `Default timeout action set to "${value}" and saved to config.`;
8769
+ }
8770
+ };
8771
+ var permissionRootCommand = {
8772
+ description: "Show current permission runtime configuration",
8773
+ usage: "/permission",
8774
+ handler: async (_args, context) => {
8775
+ const accessor = context.permissionConfig;
8776
+ if (!accessor) {
8777
+ return "Permission config accessor is not available.";
8778
+ }
8779
+ const timeoutMs = accessor.getTimeoutMs();
8780
+ const defaultOnTimeout = accessor.getDefaultOnTimeout();
8781
+ return [
8782
+ "**Permission Configuration**",
8783
+ "",
8784
+ `Timeout: ${formatDuration3(timeoutMs)} (${timeoutMs}ms)`,
8785
+ `Default on timeout: ${defaultOnTimeout}`,
8786
+ "",
8787
+ "Use `/permission timeout <duration>` to change timeout. Default unit: seconds.",
8788
+ "Use `/permission default <allow|deny>` to change default action."
8789
+ ].join("\n");
8790
+ }
8791
+ };
8792
+ var permissionCommands = {
8793
+ timeout: permissionTimeoutCommand,
8794
+ default: permissionDefaultCommand
8795
+ };
8796
+ function registerPermissionCommands(registerFn) {
8797
+ for (const [subcommand, handler] of Object.entries(permissionCommands)) {
8798
+ registerFn("permission", subcommand, handler);
8799
+ }
8800
+ }
8801
+
8646
8802
  // src/core/command/builtin/index.ts
8647
8803
  function registerBuiltinCommands(registry) {
8648
8804
  registry.register("start", COMMAND_ROOT_SUBCOMMAND, startCommand);
@@ -8655,12 +8811,16 @@ function registerBuiltinCommands(registry) {
8655
8811
  registry.register("clear", COMMAND_ROOT_SUBCOMMAND, clearCommand);
8656
8812
  registry.register("stop", COMMAND_ROOT_SUBCOMMAND, stopCommand);
8657
8813
  registry.register("session", COMMAND_ROOT_SUBCOMMAND, sessionRootCommand);
8814
+ registry.register("permission", COMMAND_ROOT_SUBCOMMAND, permissionRootCommand);
8658
8815
  registerSessionCommands((group, subcommand, handler) => {
8659
8816
  registry.register(group, subcommand, handler);
8660
8817
  });
8661
8818
  registerSystemCommands((group, subcommand, handler) => {
8662
8819
  registry.register(group, subcommand, handler);
8663
8820
  });
8821
+ registerPermissionCommands((group, subcommand, handler) => {
8822
+ registry.register(group, subcommand, handler);
8823
+ });
8664
8824
  }
8665
8825
 
8666
8826
  // src/core/command/index.ts
@@ -8815,7 +8975,7 @@ var CommandRegistry = class {
8815
8975
  var commandRegistry = new CommandRegistry();
8816
8976
 
8817
8977
  // src/gateway/handlers/command.ts
8818
- function setupCommandHandler(bus, sessionManager, logger5) {
8978
+ function setupCommandHandler(bus, sessionManager, logger5, permissionConfig) {
8819
8979
  registerBuiltinCommands(commandRegistry);
8820
8980
  logger5.debug("Builtin commands registered");
8821
8981
  const executor = new CommandExecutor(commandRegistry, logger5);
@@ -8826,7 +8986,8 @@ function setupCommandHandler(bus, sessionManager, logger5) {
8826
8986
  const result = await executor.execute(message.text, {
8827
8987
  source: message,
8828
8988
  bus,
8829
- sessionManager
8989
+ sessionManager,
8990
+ permissionConfig
8830
8991
  });
8831
8992
  if (result === null) {
8832
8993
  logger5.warn(
@@ -8888,7 +9049,7 @@ function setupPermissionHandler(bus, interactionQueue, logger5) {
8888
9049
  // src/gateway/handlers/index.ts
8889
9050
  function setupAllHandlers(bus, gateway, interactionQueue, logger5) {
8890
9051
  const cleanupFns = [];
8891
- cleanupFns.push(setupCommandHandler(bus, gateway.getSessionManager(), logger5));
9052
+ cleanupFns.push(setupCommandHandler(bus, gateway.getSessionManager(), logger5, interactionQueue));
8892
9053
  cleanupFns.push(setupPermissionHandler(bus, interactionQueue, logger5));
8893
9054
  return () => {
8894
9055
  for (const fn of cleanupFns) {
@@ -9897,6 +10058,8 @@ function getEventTitle(eventType) {
9897
10058
  return t("eventTitles.agentMessage");
9898
10059
  case "error":
9899
10060
  return t("eventTitles.error");
10061
+ case "fallback":
10062
+ return t("eventTitles.fallback");
9900
10063
  default:
9901
10064
  return t("eventTitles.fallback");
9902
10065
  }
@@ -9904,7 +10067,7 @@ function getEventTitle(eventType) {
9904
10067
 
9905
10068
  // src/shared/format.ts
9906
10069
  init_esm_shims();
9907
- function formatDuration3(ms) {
10070
+ function formatDuration4(ms) {
9908
10071
  if (ms === void 0 || ms < 0) return "-";
9909
10072
  if (ms < 1e3) return `${ms}ms`;
9910
10073
  if (ms < 6e4) return `${Math.round(ms / 1e3)}s`;
@@ -10089,7 +10252,7 @@ var TelegramFormatter = class {
10089
10252
  lines.push(`<b>Model:</b> ${this.escape(model)}`);
10090
10253
  }
10091
10254
  if (durationMs !== void 0) {
10092
- lines.push(`<b>Duration:</b> ${this.escape(formatDuration3(durationMs))}`);
10255
+ lines.push(`<b>Duration:</b> ${this.escape(formatDuration4(durationMs))}`);
10093
10256
  }
10094
10257
  if (inputTokens !== void 0 && outputTokens !== void 0) {
10095
10258
  lines.push(`<b>Tokens:</b> ${formatTokenCount(inputTokens)}\u2191 / ${formatTokenCount(outputTokens)}\u2193`);
@@ -11490,7 +11653,7 @@ var FeishuChannel = class extends (_a4 = BaseChannel) {
11490
11653
  if (message.context?.eventType === "turn:finished") {
11491
11654
  const finishedCtx = message.context;
11492
11655
  const model = finishedCtx.model || finishedCtx.agentType || "-";
11493
- const duration = formatDuration3(finishedCtx.durationMs);
11656
+ const duration = formatDuration4(finishedCtx.durationMs);
11494
11657
  const outTokens = finishedCtx.usage?.outputTokens;
11495
11658
  const inTokens = finishedCtx.usage?.inputTokens;
11496
11659
  const tokens = outTokens !== void 0 && inTokens !== void 0 ? `${formatTokenCount(inTokens)}\u2191 ${formatTokenCount(outTokens)}\u2193` : "-";
@@ -11899,7 +12062,7 @@ ${context.interaction.question.question}`;
11899
12062
  * 根据上下文获取标题
11900
12063
  */
11901
12064
  getTitleForContext(context) {
11902
- if (!context) return getEventTitle("error");
12065
+ if (!context) return getEventTitle("fallback");
11903
12066
  return getEventTitle(context.eventType);
11904
12067
  }
11905
12068
  };