@agentapprove/openclaw 0.1.7 → 0.1.8
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/README.md +12 -0
- package/dist/index.js +46 -20
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,6 +10,13 @@
|
|
|
10
10
|
|
|
11
11
|
You decide the rules. Agent Approve enforces them.
|
|
12
12
|
|
|
13
|
+
## Important
|
|
14
|
+
|
|
15
|
+
- A valid Agent Approve subscription is required to use this plugin.
|
|
16
|
+
- You must download the iOS app, run `npx agentapprove`, and pair your device.
|
|
17
|
+
- This plugin is currently pre-launch software. Public App Store availability is not open yet.
|
|
18
|
+
- If a valid token is not configured, tool calls are blocked with setup guidance.
|
|
19
|
+
|
|
13
20
|
## How it works
|
|
14
21
|
|
|
15
22
|
This plugin hooks into OpenClaw's agent loop to enforce your approval policy:
|
|
@@ -67,6 +74,11 @@ Plugin settings in `~/.openclaw/openclaw.json`:
|
|
|
67
74
|
| `privacyTier` | `full` | What tool data is stored in event logs: `minimal`, `summary`, or `full` |
|
|
68
75
|
| `debug` | `false` | Write debug logs to `~/.agentapprove/hook-debug.log` |
|
|
69
76
|
|
|
77
|
+
## Requirements
|
|
78
|
+
|
|
79
|
+
- OpenClaw runtime
|
|
80
|
+
- Agent Approve iOS app and active subscription ($9.99/month or $99/year, 7-day free trial)
|
|
81
|
+
|
|
70
82
|
## Supported agents
|
|
71
83
|
|
|
72
84
|
Agent Approve works with OpenClaw, Claude Code, Cursor, Gemini CLI, VS Code Agent, Copilot CLI, OpenAI Codex (coming soon), and more. Run `npx agentapprove` to configure multiple agents at once.
|
package/dist/index.js
CHANGED
|
@@ -339,7 +339,7 @@ function loadConfig(openclawConfig, logger) {
|
|
|
339
339
|
}
|
|
340
340
|
let token = process.env.AGENTAPPROVE_TOKEN || getKeychainToken() || parseConfigValue(fileContent, "AGENTAPPROVE_TOKEN") || "";
|
|
341
341
|
if (!token) {
|
|
342
|
-
logger?.warn("
|
|
342
|
+
logger?.warn("Missing token. Download the iOS app, run npx agentapprove, and pair your device. Subscription required.");
|
|
343
343
|
}
|
|
344
344
|
const rawApiUrl = openclawConfig?.apiUrl || process.env.AGENTAPPROVE_API || parseConfigValue(fileContent, "AGENTAPPROVE_API") || "https://api.agentapprove.com";
|
|
345
345
|
const rawApiVersion = process.env.AGENTAPPROVE_API_VERSION || parseConfigValue(fileContent, "AGENTAPPROVE_API_VERSION") || "v001";
|
|
@@ -387,6 +387,7 @@ function loadConfig(openclawConfig, logger) {
|
|
|
387
387
|
privacyTier,
|
|
388
388
|
debug,
|
|
389
389
|
hookVersion: "1.1.4",
|
|
390
|
+
agentType: "openclaw",
|
|
390
391
|
agentName,
|
|
391
392
|
e2eEnabled,
|
|
392
393
|
e2eUserKey,
|
|
@@ -473,6 +474,7 @@ var CONTENT_FIELDS = [
|
|
|
473
474
|
"command",
|
|
474
475
|
"toolInput",
|
|
475
476
|
"response",
|
|
477
|
+
"responsePreview",
|
|
476
478
|
"text",
|
|
477
479
|
"textPreview",
|
|
478
480
|
"prompt",
|
|
@@ -688,7 +690,7 @@ function httpPost(url, body, headers, timeoutMs) {
|
|
|
688
690
|
}
|
|
689
691
|
async function sendApprovalRequest(request, config, pluginPath, hookName = "openclaw-plugin") {
|
|
690
692
|
if (!config.token) {
|
|
691
|
-
throw new Error("
|
|
693
|
+
throw new Error("Missing token. Download the iOS app, run npx agentapprove, and pair your device. Subscription required.");
|
|
692
694
|
}
|
|
693
695
|
let payload;
|
|
694
696
|
if (config.e2eEnabled && config.e2eUserKey) {
|
|
@@ -915,13 +917,13 @@ function handleFailBehavior(config, error, toolName, logger, hookName = HOOK_PLU
|
|
|
915
917
|
}
|
|
916
918
|
function register(api) {
|
|
917
919
|
const config = loadConfig(api.pluginConfig, api.logger);
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
);
|
|
922
|
-
|
|
920
|
+
const missingTokenMessage = "Missing token. Download the iOS app, run npx agentapprove, and pair your device. Subscription required.";
|
|
921
|
+
const noTokenConfigured = !config.token;
|
|
922
|
+
if (noTokenConfigured) {
|
|
923
|
+
api.logger.warn(`Agent Approve: ${missingTokenMessage}`);
|
|
924
|
+
} else {
|
|
925
|
+
api.logger.info(`Agent Approve: Plugin loaded (privacy: ${config.privacyTier}, fail: ${config.failBehavior})`);
|
|
923
926
|
}
|
|
924
|
-
api.logger.info(`Agent Approve: Plugin loaded (privacy: ${config.privacyTier}, fail: ${config.failBehavior})`);
|
|
925
927
|
if (config.debug) {
|
|
926
928
|
const e2eStatus = !config.e2eEnabled ? "disabled" : !config.e2eUserKey ? "enabled (key missing)" : "enabled";
|
|
927
929
|
debugLog(
|
|
@@ -941,16 +943,24 @@ function register(api) {
|
|
|
941
943
|
if (config.debug) {
|
|
942
944
|
debugLog(`Tool: ${displayName} (${toolType}) [agent: ${config.agentName}]`, HOOK_BEFORE_TOOL);
|
|
943
945
|
}
|
|
946
|
+
if (noTokenConfigured) {
|
|
947
|
+
return {
|
|
948
|
+
block: true,
|
|
949
|
+
blockReason: missingTokenMessage
|
|
950
|
+
};
|
|
951
|
+
}
|
|
944
952
|
const request = {
|
|
945
953
|
toolName: displayName,
|
|
946
954
|
toolType,
|
|
947
955
|
command,
|
|
948
956
|
toolInput: event.params,
|
|
949
|
-
agent: config.
|
|
957
|
+
agent: config.agentType,
|
|
958
|
+
agentName: config.agentName,
|
|
950
959
|
hookType: "before_tool_call",
|
|
951
960
|
sessionId: conversationId,
|
|
952
961
|
conversationId,
|
|
953
962
|
cwd: event.params.workdir || void 0,
|
|
963
|
+
projectPath: process.cwd(),
|
|
954
964
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
955
965
|
};
|
|
956
966
|
try {
|
|
@@ -994,18 +1004,24 @@ function register(api) {
|
|
|
994
1004
|
}
|
|
995
1005
|
const { toolType } = classifyTool(event.toolName, event.params);
|
|
996
1006
|
const resultPreview = extractResultPreview(event.toolName, event.params, event.result);
|
|
1007
|
+
const resultStr = event.result != null ? typeof event.result === "string" ? event.result : JSON.stringify(event.result) : void 0;
|
|
1008
|
+
const longPreview = resultStr && resultStr.length > 1e3 ? resultStr.slice(0, 1e3) + "..." : resultStr;
|
|
997
1009
|
void sendEvent({
|
|
998
1010
|
toolName: event.toolName,
|
|
999
1011
|
toolType,
|
|
1000
1012
|
eventType: "tool_complete",
|
|
1001
|
-
agent: config.
|
|
1013
|
+
agent: config.agentType,
|
|
1014
|
+
agentName: config.agentName,
|
|
1002
1015
|
hookType: "after_tool_call",
|
|
1003
1016
|
sessionId: conversationId,
|
|
1004
1017
|
conversationId,
|
|
1005
1018
|
command: extractCommand(event.toolName, event.params),
|
|
1019
|
+
toolInput: event.params,
|
|
1006
1020
|
status: event.error ? "error" : "success",
|
|
1007
1021
|
response: event.error || resultPreview || void 0,
|
|
1022
|
+
responsePreview: longPreview,
|
|
1008
1023
|
durationMs: event.durationMs,
|
|
1024
|
+
cwd: event.params?.workdir || void 0,
|
|
1009
1025
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1010
1026
|
}, config, pluginFilePath, HOOK_AFTER_TOOL);
|
|
1011
1027
|
});
|
|
@@ -1017,7 +1033,8 @@ function register(api) {
|
|
|
1017
1033
|
const conversationId = resolveConversationId();
|
|
1018
1034
|
void sendEvent({
|
|
1019
1035
|
eventType: "session_start",
|
|
1020
|
-
agent: config.
|
|
1036
|
+
agent: config.agentType,
|
|
1037
|
+
agentName: config.agentName,
|
|
1021
1038
|
hookType: "session_start",
|
|
1022
1039
|
sessionId: conversationId,
|
|
1023
1040
|
conversationId,
|
|
@@ -1032,7 +1049,8 @@ function register(api) {
|
|
|
1032
1049
|
const conversationId = resolveConversationId();
|
|
1033
1050
|
void sendEvent({
|
|
1034
1051
|
eventType: "session_end",
|
|
1035
|
-
agent: config.
|
|
1052
|
+
agent: config.agentType,
|
|
1053
|
+
agentName: config.agentName,
|
|
1036
1054
|
hookType: "session_end",
|
|
1037
1055
|
sessionId: conversationId,
|
|
1038
1056
|
conversationId,
|
|
@@ -1049,7 +1067,8 @@ function register(api) {
|
|
|
1049
1067
|
const conversationId = resolveConversationId();
|
|
1050
1068
|
void sendEvent({
|
|
1051
1069
|
eventType: "user_prompt",
|
|
1052
|
-
agent: config.
|
|
1070
|
+
agent: config.agentType,
|
|
1071
|
+
agentName: config.agentName,
|
|
1053
1072
|
hookType: "llm_input",
|
|
1054
1073
|
sessionId: conversationId,
|
|
1055
1074
|
conversationId,
|
|
@@ -1069,7 +1088,8 @@ function register(api) {
|
|
|
1069
1088
|
}
|
|
1070
1089
|
void sendEvent({
|
|
1071
1090
|
eventType: "response",
|
|
1072
|
-
agent: config.
|
|
1091
|
+
agent: config.agentType,
|
|
1092
|
+
agentName: config.agentName,
|
|
1073
1093
|
hookType: "llm_output",
|
|
1074
1094
|
sessionId: conversationId,
|
|
1075
1095
|
conversationId,
|
|
@@ -1088,7 +1108,8 @@ function register(api) {
|
|
|
1088
1108
|
const conversationId = resolveConversationId();
|
|
1089
1109
|
void sendEvent({
|
|
1090
1110
|
eventType: "stop",
|
|
1091
|
-
agent: config.
|
|
1111
|
+
agent: config.agentType,
|
|
1112
|
+
agentName: config.agentName,
|
|
1092
1113
|
hookType: "agent_end",
|
|
1093
1114
|
sessionId: conversationId,
|
|
1094
1115
|
conversationId,
|
|
@@ -1106,7 +1127,8 @@ function register(api) {
|
|
|
1106
1127
|
const conversationId = resolveConversationId();
|
|
1107
1128
|
void sendEvent({
|
|
1108
1129
|
eventType: "context_compact",
|
|
1109
|
-
agent: config.
|
|
1130
|
+
agent: config.agentType,
|
|
1131
|
+
agentName: config.agentName,
|
|
1110
1132
|
hookType: "before_compaction",
|
|
1111
1133
|
sessionId: conversationId,
|
|
1112
1134
|
conversationId,
|
|
@@ -1122,7 +1144,8 @@ function register(api) {
|
|
|
1122
1144
|
const conversationId = resolveConversationId();
|
|
1123
1145
|
void sendEvent({
|
|
1124
1146
|
eventType: "subagent_start",
|
|
1125
|
-
agent: config.
|
|
1147
|
+
agent: config.agentType,
|
|
1148
|
+
agentName: config.agentName,
|
|
1126
1149
|
hookType: "subagent_spawned",
|
|
1127
1150
|
sessionId: conversationId,
|
|
1128
1151
|
conversationId,
|
|
@@ -1140,7 +1163,8 @@ function register(api) {
|
|
|
1140
1163
|
const conversationId = resolveConversationId();
|
|
1141
1164
|
void sendEvent({
|
|
1142
1165
|
eventType: "subagent_stop",
|
|
1143
|
-
agent: config.
|
|
1166
|
+
agent: config.agentType,
|
|
1167
|
+
agentName: config.agentName,
|
|
1144
1168
|
hookType: "subagent_ended",
|
|
1145
1169
|
sessionId: conversationId,
|
|
1146
1170
|
conversationId,
|
|
@@ -1166,7 +1190,8 @@ function register(api) {
|
|
|
1166
1190
|
toolName: `command:${event.action}`,
|
|
1167
1191
|
toolType: "command",
|
|
1168
1192
|
eventType: eventTypeMap[event.action] || "command_event",
|
|
1169
|
-
agent: config.
|
|
1193
|
+
agent: config.agentType,
|
|
1194
|
+
agentName: config.agentName,
|
|
1170
1195
|
hookType: "command_event",
|
|
1171
1196
|
sessionId: resolveConversationId(),
|
|
1172
1197
|
conversationId: resolveConversationId(),
|
|
@@ -1190,7 +1215,8 @@ function register(api) {
|
|
|
1190
1215
|
toolName: `message:${event.action}`,
|
|
1191
1216
|
toolType: "message_event",
|
|
1192
1217
|
eventType: isInbound ? "user_prompt" : "response",
|
|
1193
|
-
agent: config.
|
|
1218
|
+
agent: config.agentType,
|
|
1219
|
+
agentName: config.agentName,
|
|
1194
1220
|
hookType: "session_event",
|
|
1195
1221
|
sessionId: resolveConversationId(),
|
|
1196
1222
|
conversationId: resolveConversationId(),
|
package/package.json
CHANGED