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.
- package/dist/cli/index.js +13 -13
- package/dist/cli/index.js.map +1 -1
- package/dist/gateway/process.js +174 -11
- package/dist/gateway/process.js.map +1 -1
- package/package.json +1 -1
package/dist/gateway/process.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
|
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(
|
|
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 =
|
|
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("
|
|
12065
|
+
if (!context) return getEventTitle("fallback");
|
|
11903
12066
|
return getEventTitle(context.eventType);
|
|
11904
12067
|
}
|
|
11905
12068
|
};
|