@peac/adapter-openclaw 0.10.11 → 0.10.13

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 CHANGED
@@ -1,82 +1,122 @@
1
1
  # @peac/adapter-openclaw
2
2
 
3
- OpenClaw adapter for PEAC interaction evidence capture.
3
+ Offline-verifiable activity records for OpenClaw sessions.
4
4
 
5
- ## Overview
5
+ ## Quick Start
6
+
7
+ ```typescript
8
+ import { activate, generateSigningKey } from '@peac/adapter-openclaw';
9
+
10
+ // Generate a signing key (one-time setup)
11
+ const key = await generateSigningKey({ outputDir: '.peac' });
12
+
13
+ // Activate the plugin
14
+ const plugin = await activate({
15
+ config: {
16
+ signing: {
17
+ key_ref: `file:${key.keyPath}`,
18
+ issuer: 'https://my-org.example.com',
19
+ },
20
+ },
21
+ });
22
+
23
+ plugin.instance.start();
24
+ // Records are now captured automatically via hooks.
25
+ ```
26
+
27
+ > Never log or print the contents of `key_ref`. Use `env:` key references in CI, `file:` for local development. Signing key files are written with 0600 permissions (owner read/write only).
28
+
29
+ ## What It Records
6
30
 
7
- `@peac/adapter-openclaw` provides a complete capture pipeline for recording OpenClaw tool calls as PEAC interaction evidence. It includes:
31
+ Every tool call your agent makes is captured as a tamper-evident record:
8
32
 
9
- - **Mapper**: OpenClaw tool call events -> PEAC `CapturedAction`
10
- - **Hooks**: Sync capture bindings (< 10ms target)
11
- - **Emitter**: Background receipt signing and persistence
12
- - **Tailer**: Session history fallback for resilience
33
+ - **Tool name, timing, status** -- what ran, when, whether it succeeded
34
+ - **Input/output digests** -- SHA-256 hashes by default (payloads are never stored in plaintext unless you explicitly allowlist a tool)
35
+ - **Chain linkage** -- each record links to the previous one, so gaps or reordering are detectable
13
36
 
14
- ## Installation
37
+ Records are signed with your key and written to disk as individual `.peac.json` files.
38
+
39
+ ## Commands
40
+
41
+ ### Key Generation
15
42
 
16
43
  ```bash
17
- pnpm add @peac/adapter-openclaw
44
+ npx peac-keygen --output-dir .peac
18
45
  ```
19
46
 
20
- ## Quick Start
47
+ Generates a signing keypair. The private key is written with 0600 permissions. Print the `kid` and public key for registration with your infrastructure.
21
48
 
22
- ```typescript
23
- import { createCaptureSession, createHasher } from '@peac/capture-core';
24
- import { createInMemorySpoolStore, createInMemoryDedupeIndex } from '@peac/capture-core/testkit';
25
- import { createHookHandler, mapToolCallEvent } from '@peac/adapter-openclaw';
26
-
27
- // Create a capture session
28
- const session = createCaptureSession({
29
- store: createInMemorySpoolStore(),
30
- dedupe: createInMemoryDedupeIndex(),
31
- hasher: createHasher(),
32
- });
49
+ ### Plugin Tools
50
+
51
+ The plugin exposes 4 tools to the agent:
33
52
 
34
- // Create a hook handler
35
- const handler = createHookHandler({
36
- session,
53
+ | Tool | Description |
54
+ | ----------------------------- | ----------------------------------------------------------------- |
55
+ | `peac_receipts.status` | Spool size, last receipt time, configuration summary |
56
+ | `peac_receipts.export_bundle` | Export receipts as an evidence bundle (manifest.json + receipts/) |
57
+ | `peac_receipts.verify` | Verify a receipt or bundle offline |
58
+ | `peac_receipts.query` | Query receipts by workflow ID, tool name, or time range |
59
+
60
+ ## Configuration
61
+
62
+ ### `activate()` Options
63
+
64
+ ```typescript
65
+ const plugin = await activate({
37
66
  config: {
38
- platform: 'openclaw',
39
- platform_version: '0.2.0',
67
+ signing: {
68
+ key_ref: 'env:PEAC_SIGNING_KEY', // or 'file:/path/to/key.jwk'
69
+ issuer: 'https://my-org.example.com',
70
+ audience: 'https://api.example.com', // optional
71
+ },
72
+ output_dir: '.peac/receipts', // optional (default: {dataDir}/receipts/)
73
+ background: {
74
+ drain_interval_ms: 1000, // optional (default: 1000)
75
+ batch_size: 100, // optional (default: 100)
76
+ },
77
+ },
78
+ dataDir: '.peac', // optional (default: ~/.openclaw/peac/)
79
+ spoolOptions: {
80
+ maxEntries: 100_000, // optional (default: 100,000)
81
+ maxFileBytes: 104_857_600, // optional (default: 100MB)
82
+ autoCommitIntervalMs: 5000, // optional (default: 5000, 0 to disable)
40
83
  },
41
84
  });
85
+ ```
42
86
 
43
- // Handle OpenClaw tool call events
44
- const result = await handler.afterToolCall({
45
- tool_call_id: 'call_123',
46
- run_id: 'run_abc',
47
- tool_name: 'web_search',
48
- started_at: '2024-02-01T10:00:00Z',
49
- completed_at: '2024-02-01T10:00:01Z',
50
- status: 'ok',
51
- input: { query: 'hello world' },
52
- output: { results: ['result1'] },
53
- });
87
+ ### Key Reference Schemes
54
88
 
55
- if (result.success) {
56
- console.log('Captured:', result.entry.entry_digest);
57
- }
89
+ | Scheme | Format | Use Case |
90
+ | ------- | ----------------------- | ---------------------------------------------- |
91
+ | `env:` | `env:PEAC_SIGNING_KEY` | CI/CD, containers (key in env var as JWK JSON) |
92
+ | `file:` | `file:/path/to/key.jwk` | Local development (key file on disk) |
58
93
 
59
- await handler.close();
60
- ```
94
+ ## Compatibility
95
+
96
+ | Requirement | Value |
97
+ | ------------- | ---------------------------------------------------------- |
98
+ | Node.js | >= 22.0.0 |
99
+ | Module format | ESM (`.mjs`) and CJS (`.cjs`) dual-published |
100
+ | Runtime | Node.js only (uses `fs`, `path`, `crypto`) |
101
+ | Workers/Edge | Not supported (requires file system access) |
102
+ | OpenClaw | v2026.2.7+ (before_tool_call or tool_result_persist hooks) |
61
103
 
62
104
  ## Architecture
63
105
 
64
106
  ### Two-Stage Pipeline
65
107
 
66
- 1. **Capture Stage** (sync, < 10ms target)
67
- - Map OpenClaw event to `CapturedAction`
68
- - Hash payloads inline (truncate large payloads)
69
- - Write to tamper-evident spool
108
+ ```text
109
+ Tool Call Spool Receipts
110
+ (sync hook) --> (append-only) --> (signed receipt)
111
+ < 10ms spool.jsonl *.peac.json
112
+ ```
70
113
 
71
- 2. **Emit Stage** (async background)
72
- - Drain spool periodically
73
- - Convert to `InteractionEvidenceV01`
74
- - Sign with configured key
75
- - Write receipt to output
114
+ 1. **Capture stage** (sync, < 10ms target): Map OpenClaw event to a captured action, hash payloads inline, write to tamper-evident spool
115
+ 2. **Emit stage** (async background): Drain spool periodically, convert to interaction evidence, sign with configured key, write receipt to output directory
76
116
 
77
- ### OpenClaw to PEAC Mapping
117
+ ### OpenClaw-to-Receipt Mapping
78
118
 
79
- | OpenClaw Concept | PEAC Location |
119
+ | OpenClaw Concept | Receipt Field |
80
120
  | --------------------- | --------------------------- |
81
121
  | Session key | `workflow_id` (correlation) |
82
122
  | Run ID + tool_call_id | `interaction_id` (dedupe) |
@@ -88,85 +128,47 @@ await handler.close();
88
128
 
89
129
  ## API Reference
90
130
 
91
- ### Mapper
92
-
93
- ```typescript
94
- import { mapToolCallEvent, mapToolCallEventBatch } from '@peac/adapter-openclaw';
95
-
96
- // Map single event
97
- const result = mapToolCallEvent(event, config);
98
- if (result.success) {
99
- console.log(result.action);
100
- }
101
-
102
- // Map batch
103
- const results = mapToolCallEventBatch(events, config);
104
- ```
105
-
106
- ### Hook Handler
131
+ ### High-Level (Recommended)
107
132
 
108
133
  ```typescript
109
- import { createHookHandler, captureBatch, captureParallel } from '@peac/adapter-openclaw';
134
+ import { activate, generateSigningKey } from '@peac/adapter-openclaw';
110
135
 
111
- const handler = createHookHandler({
112
- session,
113
- config: { platform: 'openclaw' },
114
- onCapture: (result, event) => {
115
- console.log('Captured:', result);
116
- },
117
- });
136
+ // activate() wires all components from config
137
+ const plugin = await activate(options);
138
+ plugin.instance.start();
118
139
 
119
- // Single event
120
- await handler.afterToolCall(event);
140
+ // Capture events via the hook handler
141
+ await plugin.hookHandler.afterToolCall(event);
121
142
 
122
- // Batch (sequential)
123
- await captureBatch(handler, events);
143
+ // Flush pending records into signed receipts
144
+ await plugin.flush();
124
145
 
125
- // Batch (parallel - non-deterministic order)
126
- await captureParallel(handler, events);
146
+ // Clean shutdown (flushes + closes stores)
147
+ await plugin.shutdown();
127
148
  ```
128
149
 
129
- ### Background Emitter
150
+ ### Lower-Level (Advanced)
130
151
 
131
- ```typescript
132
- import { createReceiptEmitter, createBackgroundService } from '@peac/adapter-openclaw';
133
-
134
- const emitter = createReceiptEmitter({
135
- signer,
136
- writer,
137
- config: { platform: 'openclaw' },
138
- });
152
+ For custom wiring or when you need direct access to individual components:
139
153
 
140
- const service = createBackgroundService({
141
- emitter,
142
- getPendingEntries: () => spoolStore.getPending(),
143
- markEmitted: (digest) => dedupeIndex.markEmitted(digest),
144
- drainIntervalMs: 1000,
145
- });
146
-
147
- service.start();
148
- // ... later
149
- service.stop();
154
+ ```typescript
155
+ import {
156
+ createHookHandler,
157
+ createReceiptEmitter,
158
+ createBackgroundService,
159
+ mapToolCallEvent,
160
+ createSessionHistoryTailer,
161
+ } from '@peac/adapter-openclaw';
150
162
  ```
151
163
 
152
- ### Session History Tailer (Fallback)
164
+ See the source for `createHookHandler()`, `createReceiptEmitter()`, `createBackgroundService()`, and `createSessionHistoryTailer()`.
153
165
 
154
- ```typescript
155
- import { createSessionHistoryTailer } from '@peac/adapter-openclaw';
166
+ ## Security
156
167
 
157
- const tailer = createSessionHistoryTailer({
158
- handler,
159
- sessionId: 'session_123',
160
- fetchHistory: async (sessionId, afterEventId) => {
161
- return openclaw.sessions.getHistory(sessionId, { after: afterEventId });
162
- },
163
- pollIntervalMs: 1000,
164
- });
165
-
166
- tailer.start();
167
- // ... later
168
- tailer.stop();
169
- ```
168
+ - **Privacy by default**: All inputs and outputs are hashed (SHA-256). Plaintext is only captured for explicitly allowlisted tools.
169
+ - **Key protection**: Signing key files are written with 0600 permissions. Use `env:` references in CI to avoid keys on disk.
170
+ - **Tamper evidence**: Each spool entry links to the previous via digest. Gaps or reordering are detectable.
171
+ - **No secret logging**: The adapter never logs private key material. Only the key ID (`kid`) and public key component appear in logs.
170
172
 
171
173
  ## Error Codes
172
174
 
@@ -0,0 +1,87 @@
1
+ /**
2
+ * @peac/adapter-openclaw - Plugin Activation
3
+ *
4
+ * High-level entry point that wires together all adapter components
5
+ * from config. Calls existing functions (resolveSigner, createPluginInstance,
6
+ * createFileReceiptWriter, etc.) -- no logic duplication.
7
+ *
8
+ * Usage:
9
+ * const result = await activate({ config, logger });
10
+ * result.instance.start();
11
+ *
12
+ * When OpenClaw's plugin loader gains a stable activation shape,
13
+ * add a thin validated wrapper at that time.
14
+ */
15
+ import type { OpenClawAdapterConfig } from './types.js';
16
+ import type { PluginLogger, PluginInstance, PluginTool } from './plugin.js';
17
+ import type { OpenClawHookHandler } from './hooks.js';
18
+ import type { FsSpoolStoreOptions } from '@peac/capture-node';
19
+ /**
20
+ * Options for activating the plugin.
21
+ */
22
+ export interface ActivateOptions {
23
+ /** Adapter configuration (signing, capture, correlation, background). */
24
+ config: ActivateConfig;
25
+ /** Logger instance. */
26
+ logger?: PluginLogger;
27
+ /** Data directory for spool, dedupe, and signing key. Default: ~/.openclaw/peac/ */
28
+ dataDir?: string;
29
+ /** Spool store options override. */
30
+ spoolOptions?: Partial<Pick<FsSpoolStoreOptions, 'maxEntries' | 'maxFileBytes' | 'autoCommitIntervalMs' | 'lockOptions'>>;
31
+ }
32
+ /**
33
+ * Configuration for activate(). Extends OpenClawAdapterConfig with
34
+ * required signing fields.
35
+ */
36
+ export interface ActivateConfig extends OpenClawAdapterConfig {
37
+ /** Signing configuration (required for activation). */
38
+ signing: {
39
+ /** Key reference: env:VAR_NAME or file:/path */
40
+ key_ref: string;
41
+ /** Issuer URI for receipts */
42
+ issuer: string;
43
+ /** Optional audience URI */
44
+ audience?: string;
45
+ };
46
+ /** Output directory for receipt files. Default: {dataDir}/receipts/ */
47
+ output_dir?: string;
48
+ /** Background emitter settings. */
49
+ background?: {
50
+ drain_interval_ms?: number;
51
+ batch_size?: number;
52
+ };
53
+ }
54
+ /**
55
+ * Result of activating the plugin.
56
+ */
57
+ export interface ActivateResult {
58
+ /** The running plugin instance. */
59
+ instance: PluginInstance;
60
+ /** The 4 plugin tools (status, export, verify, query). */
61
+ tools: PluginTool[];
62
+ /** Hook handler for tool call events. */
63
+ hookHandler: OpenClawHookHandler;
64
+ /** Data directory used. */
65
+ dataDir: string;
66
+ /** Flush pending spool entries into signed receipts. */
67
+ flush: () => Promise<void>;
68
+ /** Shut down the plugin cleanly. */
69
+ shutdown: () => Promise<void>;
70
+ }
71
+ /**
72
+ * Activate the PEAC evidence export plugin.
73
+ *
74
+ * This is the single canonical entry point. It:
75
+ * 1. Resolves the signing key from config (env: or file:)
76
+ * 2. Creates durable stores (FsSpoolStore + FsDedupeIndex)
77
+ * 3. Creates the receipt writer
78
+ * 4. Creates the plugin instance (session + hooks + emitter)
79
+ * 5. Creates the 4 plugin tools
80
+ * 6. Logs startup diagnostics
81
+ *
82
+ * @throws If signing key cannot be resolved
83
+ * @throws If data directory is not writable
84
+ * @throws If spool lockfile cannot be acquired
85
+ */
86
+ export declare function activate(options: ActivateOptions): Promise<ActivateResult>;
87
+ //# sourceMappingURL=activate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"activate.d.ts","sourceRoot":"","sources":["../src/activate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC5E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAc9D;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,yEAAyE;IACzE,MAAM,EAAE,cAAc,CAAC;IACvB,uBAAuB;IACvB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,oFAAoF;IACpF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,oCAAoC;IACpC,YAAY,CAAC,EAAE,OAAO,CACpB,IAAI,CACF,mBAAmB,EACnB,YAAY,GAAG,cAAc,GAAG,sBAAsB,GAAG,aAAa,CACvE,CACF,CAAC;CACH;AAED;;;GAGG;AACH,MAAM,WAAW,cAAe,SAAQ,qBAAqB;IAC3D,uDAAuD;IACvD,OAAO,EAAE;QACP,gDAAgD;QAChD,OAAO,EAAE,MAAM,CAAC;QAChB,8BAA8B;QAC9B,MAAM,EAAE,MAAM,CAAC;QACf,4BAA4B;QAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,uEAAuE;IACvE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mCAAmC;IACnC,UAAU,CAAC,EAAE;QACX,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,mCAAmC;IACnC,QAAQ,EAAE,cAAc,CAAC;IACzB,0DAA0D;IAC1D,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,yCAAyC;IACzC,WAAW,EAAE,mBAAmB,CAAC;IACjC,2BAA2B;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,wDAAwD;IACxD,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,oCAAoC;IACpC,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/B;AA4BD;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC,CA8FhF"}