@whimmy-ai/whimmy 0.2.1 → 0.3.0
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/index.ts +18 -1
- package/package.json +1 -1
- package/src/channel.ts +40 -6
package/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { OpenClawPluginApi } from 'openclaw/plugin-sdk';
|
|
2
|
-
import { whimmyPlugin, registerWhimmyHooks } from './src/channel';
|
|
2
|
+
import { whimmyPlugin, registerWhimmyHooks, setApprovalManager, broadcastApprovalRequest } from './src/channel';
|
|
3
3
|
import { setWhimmyRuntime } from './src/runtime';
|
|
4
4
|
import { registerWhimmyCli } from './src/setup';
|
|
5
5
|
|
|
@@ -13,6 +13,23 @@ const plugin = {
|
|
|
13
13
|
api.registerChannel({ plugin: whimmyPlugin });
|
|
14
14
|
registerWhimmyCli(api);
|
|
15
15
|
registerWhimmyHooks(api);
|
|
16
|
+
|
|
17
|
+
// Register a gateway method that captures the ExecApprovalManager reference.
|
|
18
|
+
// The manager is only accessible via GatewayRequestHandlerOptions.context,
|
|
19
|
+
// so we use this method as the capture point.
|
|
20
|
+
api.registerGatewayMethod('whimmy.approval.init', (opts) => {
|
|
21
|
+
const manager = opts.context.execApprovalManager;
|
|
22
|
+
if (manager) {
|
|
23
|
+
setApprovalManager(manager);
|
|
24
|
+
}
|
|
25
|
+
opts.respond(true, { ok: true });
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// Once the gateway starts, the approval manager will be available
|
|
29
|
+
// after the first gateway method invocation.
|
|
30
|
+
api.on('gateway_start', () => {
|
|
31
|
+
// ExecApprovalManager is captured on the first whimmy.approval.init call.
|
|
32
|
+
});
|
|
16
33
|
},
|
|
17
34
|
};
|
|
18
35
|
|
package/package.json
CHANGED
package/src/channel.ts
CHANGED
|
@@ -31,6 +31,27 @@ import type {
|
|
|
31
31
|
AgentInfo,
|
|
32
32
|
} from './types';
|
|
33
33
|
|
|
34
|
+
// ============ Exec Approval Manager Singleton ============
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Minimal interface matching ExecApprovalManager.resolve().
|
|
38
|
+
* The full class isn't exported from openclaw/plugin-sdk's barrel,
|
|
39
|
+
* so we type just the method we need.
|
|
40
|
+
*/
|
|
41
|
+
interface ApprovalManagerLike {
|
|
42
|
+
resolve(recordId: string, decision: 'allow-once' | 'allow-always' | 'deny', resolvedBy?: string | null): boolean;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
let approvalManager: ApprovalManagerLike | null = null;
|
|
46
|
+
|
|
47
|
+
export function setApprovalManager(manager: ApprovalManagerLike): void {
|
|
48
|
+
approvalManager = manager;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function getApprovalManager(): ApprovalManagerLike | null {
|
|
52
|
+
return approvalManager;
|
|
53
|
+
}
|
|
54
|
+
|
|
34
55
|
// ============ Config Helpers ============
|
|
35
56
|
|
|
36
57
|
function getConfig(cfg: OpenClawConfig, accountId?: string): WhimmyConfig {
|
|
@@ -264,14 +285,22 @@ async function handleHookApproval(
|
|
|
264
285
|
request: HookApprovalRequest,
|
|
265
286
|
log?: Logger,
|
|
266
287
|
): Promise<void> {
|
|
267
|
-
const rt = getWhimmyRuntime();
|
|
268
|
-
|
|
269
288
|
log?.info?.(`[Whimmy] Approval: execution=${request.executionId} approved=${request.approved}`);
|
|
270
289
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
290
|
+
const manager = getApprovalManager();
|
|
291
|
+
if (!manager) {
|
|
292
|
+
log?.warn?.(`[Whimmy] ExecApprovalManager not yet captured — cannot resolve execution ${request.executionId}`);
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const decision = request.approved ? 'allow-once' as const : 'deny' as const;
|
|
297
|
+
const resolved = manager.resolve(request.executionId, decision, 'whimmy');
|
|
298
|
+
|
|
299
|
+
if (resolved) {
|
|
300
|
+
log?.info?.(`[Whimmy] Resolved execution ${request.executionId} → ${decision}`);
|
|
301
|
+
} else {
|
|
302
|
+
log?.warn?.(`[Whimmy] Failed to resolve execution ${request.executionId} (expired or unknown)`);
|
|
303
|
+
}
|
|
275
304
|
}
|
|
276
305
|
|
|
277
306
|
// ============ Inbound Event Handlers ============
|
|
@@ -300,6 +329,11 @@ function broadcastEvent(event: string, payload: unknown): void {
|
|
|
300
329
|
}
|
|
301
330
|
}
|
|
302
331
|
|
|
332
|
+
/** Forward an exec.approval.requested event to all connected Whimmy backends. */
|
|
333
|
+
export function broadcastApprovalRequest(payload: ExecApprovalRequestedPayload): void {
|
|
334
|
+
broadcastEvent('exec.approval.requested', payload);
|
|
335
|
+
}
|
|
336
|
+
|
|
303
337
|
// ============ Actions ============
|
|
304
338
|
|
|
305
339
|
function createWhimmyActions(ws: WebSocket, sessionKey: string, agentId: string) {
|