@peac/adapter-openclaw 0.10.11 → 0.10.12
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 +120 -118
- package/dist/activate.d.ts +87 -0
- package/dist/activate.d.ts.map +1 -0
- package/dist/index.cjs +341 -65
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +319 -67
- package/dist/index.mjs.map +1 -1
- package/dist/keygen-cli.cjs +116 -0
- package/dist/keygen-cli.cjs.map +1 -0
- package/dist/keygen-cli.d.ts +7 -0
- package/dist/keygen-cli.d.ts.map +1 -0
- package/dist/keygen-cli.mjs +93 -0
- package/dist/keygen-cli.mjs.map +1 -0
- package/dist/keygen.d.ts +53 -0
- package/dist/keygen.d.ts.map +1 -0
- package/dist/plugin.d.ts.map +1 -1
- package/dist/tools.d.ts +5 -1
- package/dist/tools.d.ts.map +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +11 -7
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
var path = require('path');
|
|
5
|
+
var fs = require('fs/promises');
|
|
6
|
+
var crypto = require('@peac/crypto');
|
|
7
|
+
require('@peac/capture-core');
|
|
8
|
+
|
|
9
|
+
function _interopNamespace(e) {
|
|
10
|
+
if (e && e.__esModule) return e;
|
|
11
|
+
var n = Object.create(null);
|
|
12
|
+
if (e) {
|
|
13
|
+
Object.keys(e).forEach(function (k) {
|
|
14
|
+
if (k !== 'default') {
|
|
15
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
16
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
17
|
+
enumerable: true,
|
|
18
|
+
get: function () { return e[k]; }
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
n.default = e;
|
|
24
|
+
return Object.freeze(n);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
var path__namespace = /*#__PURE__*/_interopNamespace(path);
|
|
28
|
+
var fs__namespace = /*#__PURE__*/_interopNamespace(fs);
|
|
29
|
+
|
|
30
|
+
function generateKeyId(jwk) {
|
|
31
|
+
const publicPart = jwk.x || "";
|
|
32
|
+
let hash = 0;
|
|
33
|
+
for (let i = 0; i < publicPart.length; i++) {
|
|
34
|
+
const char = publicPart.charCodeAt(i);
|
|
35
|
+
hash = (hash << 5) - hash + char;
|
|
36
|
+
hash = hash & hash;
|
|
37
|
+
}
|
|
38
|
+
return `k_${Math.abs(hash).toString(16).padStart(8, "0")}`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// src/keygen.ts
|
|
42
|
+
var DEFAULT_KEY_FILENAME = "signing-key.jwk";
|
|
43
|
+
async function generateSigningKey(options) {
|
|
44
|
+
const outputDir = options?.outputDir ?? ".";
|
|
45
|
+
const filename = options?.filename ?? DEFAULT_KEY_FILENAME;
|
|
46
|
+
const { privateKey: privateKeyBytes, publicKey: publicKeyBytes } = await crypto.generateKeypair();
|
|
47
|
+
const d = crypto.base64urlEncode(privateKeyBytes);
|
|
48
|
+
const x = crypto.base64urlEncode(publicKeyBytes);
|
|
49
|
+
const privateJwk = {
|
|
50
|
+
kty: "OKP",
|
|
51
|
+
crv: "Ed25519",
|
|
52
|
+
x,
|
|
53
|
+
d,
|
|
54
|
+
alg: "EdDSA",
|
|
55
|
+
use: "sig"
|
|
56
|
+
};
|
|
57
|
+
const kid = generateKeyId(privateJwk);
|
|
58
|
+
privateJwk.kid = kid;
|
|
59
|
+
const publicJwk = {
|
|
60
|
+
kty: "OKP",
|
|
61
|
+
crv: "Ed25519",
|
|
62
|
+
x,
|
|
63
|
+
kid,
|
|
64
|
+
alg: "EdDSA",
|
|
65
|
+
use: "sig"
|
|
66
|
+
};
|
|
67
|
+
await fs__namespace.mkdir(outputDir, { recursive: true });
|
|
68
|
+
const keyPath = path__namespace.join(outputDir, filename);
|
|
69
|
+
await fs__namespace.writeFile(keyPath, JSON.stringify(privateJwk, null, 2) + "\n", "utf-8");
|
|
70
|
+
try {
|
|
71
|
+
await fs__namespace.chmod(keyPath, 384);
|
|
72
|
+
} catch {
|
|
73
|
+
process.stderr.write(
|
|
74
|
+
`Warning: Could not set file permissions on ${keyPath}. Ensure the file is not readable by other users.
|
|
75
|
+
`
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
return { kid, keyPath, publicJwk, privateJwk };
|
|
79
|
+
}
|
|
80
|
+
async function keygenCli(args) {
|
|
81
|
+
let outputDir;
|
|
82
|
+
for (let i = 0; i < args.length; i++) {
|
|
83
|
+
if (args[i] === "--output-dir" && i + 1 < args.length) {
|
|
84
|
+
outputDir = args[i + 1];
|
|
85
|
+
i++;
|
|
86
|
+
} else if (args[i] === "--help" || args[i] === "-h") {
|
|
87
|
+
process.stdout.write(
|
|
88
|
+
"Usage: peac-keygen [--output-dir <dir>]\n\nGenerate an Ed25519 signing key for PEAC receipt signing.\n\nOptions:\n --output-dir <dir> Directory for key file (default: current directory)\n --help, -h Show this help message\n"
|
|
89
|
+
);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const result = await generateSigningKey({ outputDir });
|
|
94
|
+
process.stdout.write(`Key generated successfully.
|
|
95
|
+
`);
|
|
96
|
+
process.stdout.write(` kid: ${result.kid}
|
|
97
|
+
`);
|
|
98
|
+
process.stdout.write(` key file: ${result.keyPath}
|
|
99
|
+
`);
|
|
100
|
+
process.stdout.write(` public x: ${result.publicJwk.x}
|
|
101
|
+
`);
|
|
102
|
+
process.stdout.write(`
|
|
103
|
+
To use with the OpenClaw adapter:
|
|
104
|
+
`);
|
|
105
|
+
process.stdout.write(` signing.key_ref: "file:${path__namespace.resolve(result.keyPath)}"
|
|
106
|
+
`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// src/keygen-cli.ts
|
|
110
|
+
keygenCli(process.argv.slice(2)).catch((err) => {
|
|
111
|
+
process.stderr.write(`Error: ${err.message}
|
|
112
|
+
`);
|
|
113
|
+
process.exitCode = 1;
|
|
114
|
+
});
|
|
115
|
+
//# sourceMappingURL=keygen-cli.cjs.map
|
|
116
|
+
//# sourceMappingURL=keygen-cli.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/plugin.ts","../src/keygen.ts","../src/keygen-cli.ts"],"names":["generateKeypair","base64urlEncode","fs","path"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqPO,SAAS,cAAc,GAAA,EAAkB;AAC9C,EAAA,MAAM,UAAA,GAAa,IAAI,CAAA,IAAK,EAAA;AAC5B,EAAA,IAAI,IAAA,GAAO,CAAA;AACX,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,UAAA,CAAW,QAAQ,CAAA,EAAA,EAAK;AAC1C,IAAA,MAAM,IAAA,GAAO,UAAA,CAAW,UAAA,CAAW,CAAC,CAAA;AACpC,IAAA,IAAA,GAAA,CAAQ,IAAA,IAAQ,KAAK,IAAA,GAAO,IAAA;AAC5B,IAAA,IAAA,GAAO,IAAA,GAAO,IAAA;AAAA,EAChB;AACA,EAAA,OAAO,CAAA,EAAA,EAAK,IAAA,CAAK,GAAA,CAAI,IAAI,CAAA,CAAE,QAAA,CAAS,EAAE,CAAA,CAAE,QAAA,CAAS,CAAA,EAAG,GAAG,CAAC,CAAA,CAAA;AAC1D;;;AC1MA,IAAM,oBAAA,GAAuB,iBAAA;AAe7B,eAAsB,mBAAmB,OAAA,EAAgD;AACvF,EAAA,MAAM,SAAA,GAAY,SAAS,SAAA,IAAa,GAAA;AACxC,EAAA,MAAM,QAAA,GAAW,SAAS,QAAA,IAAY,oBAAA;AAGtC,EAAA,MAAM,EAAE,UAAA,EAAY,eAAA,EAAiB,WAAW,cAAA,EAAe,GAAI,MAAMA,sBAAA,EAAgB;AAGzF,EAAA,MAAM,CAAA,GAAIC,uBAAgB,eAAe,CAAA;AACzC,EAAA,MAAM,CAAA,GAAIA,uBAAgB,cAAc,CAAA;AAGxC,EAAA,MAAM,UAAA,GAAkB;AAAA,IACtB,GAAA,EAAK,KAAA;AAAA,IACL,GAAA,EAAK,SAAA;AAAA,IACL,CAAA;AAAA,IACA,CAAA;AAAA,IACA,GAAA,EAAK,OAAA;AAAA,IACL,GAAA,EAAK;AAAA,GACP;AAGA,EAAA,MAAM,GAAA,GAAM,cAAc,UAAU,CAAA;AACpC,EAAA,UAAA,CAAW,GAAA,GAAM,GAAA;AAGjB,EAAA,MAAM,SAAA,GAAiB;AAAA,IACrB,GAAA,EAAK,KAAA;AAAA,IACL,GAAA,EAAK,SAAA;AAAA,IACL,CAAA;AAAA,IACA,GAAA;AAAA,IACA,GAAA,EAAK,OAAA;AAAA,IACL,GAAA,EAAK;AAAA,GACP;AAGA,EAAA,MAASC,aAAA,CAAA,KAAA,CAAM,SAAA,EAAW,EAAE,SAAA,EAAW,MAAM,CAAA;AAC7C,EAAA,MAAM,OAAA,GAAeC,eAAA,CAAA,IAAA,CAAK,SAAA,EAAW,QAAQ,CAAA;AAC7C,EAAA,MAASD,aAAA,CAAA,SAAA,CAAU,SAAS,IAAA,CAAK,SAAA,CAAU,YAAY,IAAA,EAAM,CAAC,CAAA,GAAI,IAAA,EAAM,OAAO,CAAA;AAG/E,EAAA,IAAI;AACF,IAAA,MAASA,aAAA,CAAA,KAAA,CAAM,SAAS,GAAK,CAAA;AAAA,EAC/B,CAAA,CAAA,MAAQ;AAEN,IAAA,OAAA,CAAQ,MAAA,CAAO,KAAA;AAAA,MACb,8CAA8C,OAAO,CAAA;AAAA;AAAA,KAEvD;AAAA,EACF;AAEA,EAAA,OAAO,EAAE,GAAA,EAAK,OAAA,EAAS,SAAA,EAAW,UAAA,EAAW;AAC/C;AAUA,eAAsB,UAAU,IAAA,EAA+B;AAC7D,EAAA,IAAI,SAAA;AAEJ,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,IAAA,CAAK,QAAQ,CAAA,EAAA,EAAK;AACpC,IAAA,IAAI,KAAK,CAAC,CAAA,KAAM,kBAAkB,CAAA,GAAI,CAAA,GAAI,KAAK,MAAA,EAAQ;AACrD,MAAA,SAAA,GAAY,IAAA,CAAK,IAAI,CAAC,CAAA;AACtB,MAAA,CAAA,EAAA;AAAA,IACF,CAAA,MAAA,IAAW,KAAK,CAAC,CAAA,KAAM,YAAY,IAAA,CAAK,CAAC,MAAM,IAAA,EAAM;AACnD,MAAA,OAAA,CAAQ,MAAA,CAAO,KAAA;AAAA,QACb;AAAA,OAKF;AACA,MAAA;AAAA,IACF;AAAA,EACF;AAEA,EAAA,MAAM,MAAA,GAAS,MAAM,kBAAA,CAAmB,EAAE,WAAW,CAAA;AAErD,EAAA,OAAA,CAAQ,OAAO,KAAA,CAAM,CAAA;AAAA,CAA+B,CAAA;AACpD,EAAA,OAAA,CAAQ,MAAA,CAAO,KAAA,CAAM,CAAA,YAAA,EAAe,MAAA,CAAO,GAAG;AAAA,CAAI,CAAA;AAClD,EAAA,OAAA,CAAQ,MAAA,CAAO,KAAA,CAAM,CAAA,YAAA,EAAe,MAAA,CAAO,OAAO;AAAA,CAAI,CAAA;AACtD,EAAA,OAAA,CAAQ,MAAA,CAAO,KAAA,CAAM,CAAA,YAAA,EAAe,MAAA,CAAO,UAAU,CAAC;AAAA,CAAI,CAAA;AAC1D,EAAA,OAAA,CAAQ,OAAO,KAAA,CAAM;AAAA;AAAA,CAAuC,CAAA;AAC5D,EAAA,OAAA,CAAQ,OAAO,KAAA,CAAM,CAAA,yBAAA,EAAiCC,eAAA,CAAA,OAAA,CAAQ,MAAA,CAAO,OAAO,CAAC,CAAA;AAAA,CAAK,CAAA;AACpF;;;ACpJA,SAAA,CAAU,OAAA,CAAQ,KAAK,KAAA,CAAM,CAAC,CAAC,CAAA,CAAE,KAAA,CAAM,CAAC,GAAA,KAAe;AACrD,EAAA,OAAA,CAAQ,MAAA,CAAO,KAAA,CAAM,CAAA,OAAA,EAAU,GAAA,CAAI,OAAO;AAAA,CAAI,CAAA;AAC9C,EAAA,OAAA,CAAQ,QAAA,GAAW,CAAA;AACrB,CAAC,CAAA","file":"keygen-cli.cjs","sourcesContent":["/**\n * @peac/adapter-openclaw - Plugin Entry Point\n *\n * OpenClaw plugin types and utilities for PEAC receipts.\n * This module provides types and factory functions for OpenClaw integration.\n *\n * Note: Full plugin activation requires OpenClaw's plugin runtime.\n * The types and utilities here allow building custom integrations.\n */\n\nimport type { CaptureSession, SpoolStore, DedupeIndex } from '@peac/capture-core';\nimport { createCaptureSession, createHasher } from '@peac/capture-core';\nimport { sign, base64urlDecode } from '@peac/crypto';\nimport type { OpenClawAdapterConfig } from './types.js';\nimport { createHookHandler, type OpenClawHookHandler } from './hooks.js';\nimport {\n createReceiptEmitter,\n createBackgroundService,\n type Signer,\n type ReceiptWriter,\n type BackgroundEmitterService,\n} from './emitter.js';\n\n// =============================================================================\n// JWK Type\n// =============================================================================\n\n/**\n * JSON Web Key structure for signing keys (Ed25519).\n */\nexport interface JWK {\n kty: string;\n crv?: string;\n x?: string;\n d?: string;\n kid?: string;\n alg?: string;\n use?: string;\n}\n\n// =============================================================================\n// Plugin Configuration Types\n// =============================================================================\n\n/**\n * Plugin configuration from openclaw.plugin.json configSchema.\n *\n * @experimental This interface is experimental and may change.\n */\nexport interface PluginConfig {\n enabled?: boolean;\n output_dir?: string;\n capture?: {\n mode?: 'hash_only' | 'allowlist';\n allowlist?: string[];\n max_payload_size?: number;\n };\n signing: {\n key_ref: string;\n issuer: string;\n audience?: string;\n };\n correlation?: {\n include_workflow?: boolean;\n // Note: include_identity and identity_attestation_ref are reserved for future use\n };\n background?: {\n drain_interval_ms?: number;\n batch_size?: number;\n };\n}\n\n/**\n * OpenClaw plugin context provided by the runtime.\n */\nexport interface PluginContext {\n /** Plugin configuration from gateway config */\n config: PluginConfig;\n /** Agent workspace directory */\n workspaceDir: string;\n /** Logger instance */\n logger: PluginLogger;\n /** Register a tool call hook */\n onToolCall: (handler: ToolCallHandler) => void;\n /** Register a tool result hook */\n onToolResult: (handler: ToolResultHandler) => void;\n /** Register tools */\n registerTools: (tools: PluginTool[]) => void;\n}\n\n/**\n * Plugin logger interface.\n */\nexport interface PluginLogger {\n debug: (message: string, ...args: unknown[]) => void;\n info: (message: string, ...args: unknown[]) => void;\n warn: (message: string, ...args: unknown[]) => void;\n error: (message: string, ...args: unknown[]) => void;\n}\n\n/**\n * Tool call event from OpenClaw.\n */\nexport interface ToolCallEvent {\n tool_call_id: string;\n run_id: string;\n session_key?: string;\n tool_name: string;\n tool_provider?: string;\n input: unknown;\n started_at: string;\n}\n\n/**\n * Tool result event from OpenClaw.\n */\nexport interface ToolResultEvent {\n tool_call_id: string;\n run_id: string;\n session_key?: string;\n tool_name: string;\n output: unknown;\n completed_at: string;\n status: 'ok' | 'error' | 'timeout' | 'canceled';\n error_code?: string;\n}\n\n/**\n * Tool call handler function.\n */\nexport type ToolCallHandler = (event: ToolCallEvent) => void | Promise<void>;\n\n/**\n * Tool result handler function.\n */\nexport type ToolResultHandler = (event: ToolResultEvent) => void | Promise<void>;\n\n/**\n * Plugin tool definition.\n */\nexport interface PluginTool {\n name: string;\n description: string;\n parameters?: Record<string, unknown>;\n execute: (params: Record<string, unknown>) => Promise<unknown>;\n}\n\n// =============================================================================\n// Signer Creation\n// =============================================================================\n\n/**\n * Create a signer from a JWK private key.\n *\n * @param jwk - Ed25519 private key in JWK format\n * @param issuer - Issuer URI for receipts\n * @param audience - Optional audience URI\n * @returns Signer instance\n */\nexport function createJwkSigner(jwk: JWK, issuer: string, audience?: string): Signer {\n const kid = jwk.kid || generateKeyId(jwk);\n\n // Extract the private key bytes from JWK\n if (!jwk.d) {\n throw new Error('JWK must include private key (d parameter)');\n }\n\n // Decode base64url to get raw key bytes\n const privateKeyBytes = base64urlDecode(jwk.d);\n\n return {\n async sign(payload: unknown): Promise<string> {\n return sign(payload, privateKeyBytes, kid);\n },\n\n getKeyId(): string {\n return kid;\n },\n\n getIssuer(): string {\n return issuer;\n },\n\n getAudience(): string | undefined {\n return audience;\n },\n };\n}\n\n/**\n * Resolve a signer from a key reference string.\n *\n * Supported formats:\n * - env:VAR_NAME - Load key from environment variable\n * - file:/path - Load from file (development only)\n *\n * @param keyRef - Key reference string\n * @param issuer - Issuer URI\n * @param audience - Optional audience URI\n * @returns Promise resolving to Signer\n */\nexport async function resolveSigner(\n keyRef: string,\n issuer: string,\n audience?: string\n): Promise<Signer> {\n const colonIndex = keyRef.indexOf(':');\n if (colonIndex === -1) {\n throw new Error(`Invalid key reference format: ${keyRef}. Expected scheme:value`);\n }\n\n const scheme = keyRef.slice(0, colonIndex);\n const value = keyRef.slice(colonIndex + 1);\n\n switch (scheme) {\n case 'env': {\n const keyData = process.env[value];\n if (!keyData) {\n throw new Error(`Environment variable ${value} not set for signing key`);\n }\n const jwk = JSON.parse(keyData) as JWK;\n return createJwkSigner(jwk, issuer, audience);\n }\n\n case 'file': {\n const fs = await import('fs');\n const keyData = await fs.promises.readFile(value, 'utf-8');\n const jwk = JSON.parse(keyData) as JWK;\n return createJwkSigner(jwk, issuer, audience);\n }\n\n case 'keychain':\n throw new Error('Keychain signing not yet implemented. Use env: for development.');\n\n case 'sidecar':\n throw new Error('Sidecar signing not yet implemented. Use env: for development.');\n\n default:\n throw new Error(`Unknown key reference scheme: ${scheme}. Use env: or file:`);\n }\n}\n\n/**\n * Generate a key ID from a JWK by hashing its public components.\n */\nexport function generateKeyId(jwk: JWK): string {\n const publicPart = jwk.x || '';\n let hash = 0;\n for (let i = 0; i < publicPart.length; i++) {\n const char = publicPart.charCodeAt(i);\n hash = (hash << 5) - hash + char;\n hash = hash & hash;\n }\n return `k_${Math.abs(hash).toString(16).padStart(8, '0')}`;\n}\n\n// =============================================================================\n// Receipt Writer\n// =============================================================================\n\n/**\n * Create a file system receipt writer with atomic writes.\n *\n * Writes are atomic: write to .tmp, fsync, rename. This ensures\n * partially written files are never observable.\n *\n * @param outputDir - Directory for receipt files\n * @returns Promise resolving to ReceiptWriter\n */\nexport async function createFileReceiptWriter(outputDir: string): Promise<ReceiptWriter> {\n const fs = await import('fs');\n const pathModule = await import('path');\n\n // Use path.resolve for portability\n const resolvedOutputDir = pathModule.resolve(outputDir);\n\n // Ensure directory exists\n await fs.promises.mkdir(resolvedOutputDir, { recursive: true });\n\n return {\n async write(receipt) {\n const filename = `${receipt.rid}.peac.json`;\n const filepath = pathModule.join(resolvedOutputDir, filename);\n const tempPath = `${filepath}.tmp`;\n\n // Decode the JWS payload to persist the full receipt structure.\n // The verify and export tools expect auth + evidence blocks at the top level.\n // JWS compact serialization: header.payload.signature (base64url)\n let content: string;\n const jwsParts = receipt.jws.split('.');\n if (jwsParts.length !== 3) {\n // Malformed JWS -- write _jws-only receipt (best effort, still recoverable)\n content = JSON.stringify({ _jws: receipt.jws }, null, 2);\n } else {\n try {\n const payloadJson = Buffer.from(jwsParts[1], 'base64url').toString('utf-8');\n const payload = JSON.parse(payloadJson);\n\n // Split payload into auth (identity fields) and evidence (data fields)\n const { evidence, ...auth } = payload;\n\n content = JSON.stringify({ auth, evidence, _jws: receipt.jws }, null, 2);\n } catch {\n // Payload decode failed -- write _jws-only receipt (best effort)\n content = JSON.stringify({ _jws: receipt.jws }, null, 2);\n }\n }\n\n // Atomic write: write to .tmp, fsync, rename, fsync dir\n await fs.promises.writeFile(tempPath, content, 'utf-8');\n\n // fsync file for durability (open, sync, close)\n const fd = await fs.promises.open(tempPath, 'r');\n await fd.sync();\n await fd.close();\n\n // Atomic rename (POSIX guarantees atomicity)\n await fs.promises.rename(tempPath, filepath);\n\n // fsync parent directory for full crash durability (POSIX best practice)\n // This ensures the rename is persisted even on power loss\n // Best-effort: may fail on some platforms (Windows, network FS)\n try {\n const dirFd = await fs.promises.open(resolvedOutputDir, 'r');\n await dirFd.sync();\n await dirFd.close();\n } catch {\n // Directory fsync not supported on this platform - continue anyway\n // Receipt is still atomic via rename, just not fully durable on power loss\n }\n\n return filepath;\n },\n\n async close() {\n // No-op for file writer\n },\n };\n}\n\n// =============================================================================\n// Plugin Instance\n// =============================================================================\n\n/**\n * Plugin instance state.\n */\nexport interface PluginInstance {\n /** Capture session */\n session: CaptureSession;\n /** Hook handler for tool calls */\n hookHandler: OpenClawHookHandler;\n /** Background emitter service */\n backgroundService: BackgroundEmitterService;\n /** Start the plugin */\n start(): void;\n /** Stop the plugin */\n stop(): Promise<void>;\n /** Get plugin stats */\n getStats(): PluginStats;\n}\n\n/**\n * Plugin statistics.\n */\nexport interface PluginStats {\n totalCaptured: number;\n duplicatesSkipped: number;\n pendingCount: number;\n totalEmitted: number;\n totalErrors: number;\n keyId: string;\n isRunning: boolean;\n}\n\n/**\n * Options for creating a plugin instance.\n */\nexport interface CreatePluginOptions {\n /** Signer for receipts */\n signer: Signer;\n /** Receipt writer */\n writer: ReceiptWriter;\n /** Adapter configuration */\n adapterConfig?: OpenClawAdapterConfig;\n /** Spool store implementation (required) */\n store: SpoolStore;\n /** Dedupe index implementation (required) */\n dedupe: DedupeIndex;\n /** Background drain interval in ms (default: 1000) */\n drainIntervalMs?: number;\n /** Batch size for draining (default: 100) */\n batchSize?: number;\n /** Error callback */\n onError?: (error: Error) => void;\n}\n\n/**\n * Create a plugin instance.\n *\n * @param options - Plugin options\n * @returns Plugin instance\n */\nexport async function createPluginInstance(options: CreatePluginOptions): Promise<PluginInstance> {\n const {\n signer,\n writer,\n adapterConfig,\n store,\n dedupe,\n drainIntervalMs = 1000,\n batchSize = 100,\n onError,\n } = options;\n\n // Create hasher\n const hasher = await createHasher();\n\n // Create capture session\n const session = createCaptureSession({\n store,\n dedupe,\n hasher,\n });\n\n // Track stats\n const stats: PluginStats = {\n totalCaptured: 0,\n duplicatesSkipped: 0,\n pendingCount: 0,\n totalEmitted: 0,\n totalErrors: 0,\n keyId: signer.getKeyId(),\n isRunning: false,\n };\n\n // Create hook handler\n const hookHandler = createHookHandler({\n session,\n config: adapterConfig,\n onCapture: (result) => {\n if (result.success) {\n stats.totalCaptured++;\n stats.pendingCount++;\n } else {\n stats.duplicatesSkipped++;\n }\n },\n });\n\n // Create receipt emitter\n const emitter = createReceiptEmitter({\n signer,\n writer,\n onEmit: (result) => {\n if (result.success) {\n stats.totalEmitted++;\n stats.pendingCount = Math.max(0, stats.pendingCount - 1);\n }\n },\n onError: (error) => {\n stats.totalErrors++;\n if (onError) onError(error);\n },\n });\n\n // Track last emitted sequence for cursor-based emission\n // This is the in-memory cursor; on restart, we rebuild from dedupe index\n let lastEmittedSequence = 0;\n\n // Mutex to prevent overlapping drain cycles\n let drainInProgress = false;\n\n // Create background service with proper cursor tracking\n // Note: For full restart safety, the dedupe index should be persistent.\n // The getPendingEntries filters out already-emitted entries using the dedupe index.\n const backgroundService = createBackgroundService({\n emitter,\n drainIntervalMs,\n getPendingEntries: async () => {\n // Prevent overlapping drain cycles\n if (drainInProgress) return [];\n drainInProgress = true;\n\n try {\n // Read entries after the last emitted sequence\n const currentSequence = await store.getSequence();\n if (currentSequence <= lastEmittedSequence) return [];\n\n // Read from cursor position\n const entries = await store.read(lastEmittedSequence, batchSize);\n\n // Filter out entries that are already emitted (restart safety)\n // This uses the persistent dedupe index as source of truth\n const pending = [];\n for (const entry of entries) {\n if (entry.sequence <= lastEmittedSequence) continue;\n // Check if already emitted in dedupe index\n const dedupeEntry = await dedupe.get(entry.action.id);\n if (dedupeEntry?.emitted) continue;\n pending.push(entry);\n }\n return pending;\n } finally {\n drainInProgress = false;\n }\n },\n markEmitted: async (digest) => {\n // Find the entry with this digest and update cursor\n const entries = await store.read(lastEmittedSequence, batchSize);\n const entry = entries.find((e) => e.entry_digest === digest);\n if (entry) {\n // Update in-memory cursor\n if (entry.sequence > lastEmittedSequence) {\n lastEmittedSequence = entry.sequence;\n }\n // Mark in persistent dedupe index (restart-safe)\n await dedupe.markEmitted(entry.action.id);\n }\n },\n onError,\n });\n\n return {\n session,\n hookHandler,\n backgroundService,\n\n start() {\n stats.isRunning = true;\n backgroundService.start();\n },\n\n async stop() {\n backgroundService.stop();\n await backgroundService.drain();\n await hookHandler.close();\n stats.isRunning = false;\n },\n\n getStats() {\n return { ...stats };\n },\n };\n}\n","/**\n * @peac/adapter-openclaw - Signing Key Generation\n *\n * Generates an Ed25519 keypair for PEAC receipt signing.\n * Reuses generateKeyId() from plugin.ts for kid derivation.\n *\n * Usage (programmatic):\n * const result = await generateSigningKey({ outputDir: '~/.openclaw/peac' });\n * console.log(result.kid);\n *\n * Usage (CLI):\n * npx @peac/adapter-openclaw keygen\n * npx @peac/adapter-openclaw keygen --output-dir /path/to/dir\n */\n\nimport * as path from 'node:path';\nimport * as fs from 'node:fs/promises';\nimport { generateKeypair, base64urlEncode } from '@peac/crypto';\nimport { generateKeyId, type JWK } from './plugin.js';\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Options for key generation.\n */\nexport interface KeygenOptions {\n /** Directory to write the key file to. Default: current directory. */\n outputDir?: string;\n /** Filename for the private key. Default: signing-key.jwk */\n filename?: string;\n}\n\n/**\n * Result of key generation.\n */\nexport interface KeygenResult {\n /** Key ID (derived from public key). */\n kid: string;\n /** Path where the private key was written. */\n keyPath: string;\n /** The public JWK (safe to share). */\n publicJwk: JWK;\n /** The private JWK (keep secret). */\n privateJwk: JWK;\n}\n\n// =============================================================================\n// Defaults\n// =============================================================================\n\nconst DEFAULT_KEY_FILENAME = 'signing-key.jwk';\n\n// =============================================================================\n// Key Generation\n// =============================================================================\n\n/**\n * Generate an Ed25519 signing keypair and write the private key to disk.\n *\n * - Private key file has 0o600 permissions (owner read/write only).\n * On platforms where chmod fails (Windows), a warning is logged to stderr.\n * - The key ID (kid) is derived from the public key via generateKeyId().\n *\n * @returns KeygenResult with paths and key material\n */\nexport async function generateSigningKey(options?: KeygenOptions): Promise<KeygenResult> {\n const outputDir = options?.outputDir ?? '.';\n const filename = options?.filename ?? DEFAULT_KEY_FILENAME;\n\n // Generate Ed25519 keypair via @peac/crypto\n const { privateKey: privateKeyBytes, publicKey: publicKeyBytes } = await generateKeypair();\n\n // Encode as base64url\n const d = base64urlEncode(privateKeyBytes);\n const x = base64urlEncode(publicKeyBytes);\n\n // Build JWK\n const privateJwk: JWK = {\n kty: 'OKP',\n crv: 'Ed25519',\n x,\n d,\n alg: 'EdDSA',\n use: 'sig',\n };\n\n // Derive key ID from public component\n const kid = generateKeyId(privateJwk);\n privateJwk.kid = kid;\n\n // Public JWK (no private component)\n const publicJwk: JWK = {\n kty: 'OKP',\n crv: 'Ed25519',\n x,\n kid,\n alg: 'EdDSA',\n use: 'sig',\n };\n\n // Write to disk\n await fs.mkdir(outputDir, { recursive: true });\n const keyPath = path.join(outputDir, filename);\n await fs.writeFile(keyPath, JSON.stringify(privateJwk, null, 2) + '\\n', 'utf-8');\n\n // Set restrictive permissions (0o600 = owner read/write only)\n try {\n await fs.chmod(keyPath, 0o600);\n } catch {\n // chmod may fail on Windows -- log warning but don't fail\n process.stderr.write(\n `Warning: Could not set file permissions on ${keyPath}. ` +\n `Ensure the file is not readable by other users.\\n`\n );\n }\n\n return { kid, keyPath, publicJwk, privateJwk };\n}\n\n// =============================================================================\n// CLI Entry Point\n// =============================================================================\n\n/**\n * CLI handler for `peac-keygen` command.\n * Parses args, generates key, prints results.\n */\nexport async function keygenCli(args: string[]): Promise<void> {\n let outputDir: string | undefined;\n\n for (let i = 0; i < args.length; i++) {\n if (args[i] === '--output-dir' && i + 1 < args.length) {\n outputDir = args[i + 1];\n i++;\n } else if (args[i] === '--help' || args[i] === '-h') {\n process.stdout.write(\n 'Usage: peac-keygen [--output-dir <dir>]\\n\\n' +\n 'Generate an Ed25519 signing key for PEAC receipt signing.\\n\\n' +\n 'Options:\\n' +\n ' --output-dir <dir> Directory for key file (default: current directory)\\n' +\n ' --help, -h Show this help message\\n'\n );\n return;\n }\n }\n\n const result = await generateSigningKey({ outputDir });\n\n process.stdout.write(`Key generated successfully.\\n`);\n process.stdout.write(` kid: ${result.kid}\\n`);\n process.stdout.write(` key file: ${result.keyPath}\\n`);\n process.stdout.write(` public x: ${result.publicJwk.x}\\n`);\n process.stdout.write(`\\nTo use with the OpenClaw adapter:\\n`);\n process.stdout.write(` signing.key_ref: \"file:${path.resolve(result.keyPath)}\"\\n`);\n}\n","#!/usr/bin/env node\n/**\n * CLI entry point for peac-keygen.\n * Invoked via: npx @peac/adapter-openclaw keygen\n */\n\nimport { keygenCli } from './keygen.js';\n\nkeygenCli(process.argv.slice(2)).catch((err: Error) => {\n process.stderr.write(`Error: ${err.message}\\n`);\n process.exitCode = 1;\n});\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keygen-cli.d.ts","sourceRoot":"","sources":["../src/keygen-cli.ts"],"names":[],"mappings":";AACA;;;GAGG"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as fs from 'fs/promises';
|
|
4
|
+
import { generateKeypair, base64urlEncode } from '@peac/crypto';
|
|
5
|
+
import '@peac/capture-core';
|
|
6
|
+
|
|
7
|
+
function generateKeyId(jwk) {
|
|
8
|
+
const publicPart = jwk.x || "";
|
|
9
|
+
let hash = 0;
|
|
10
|
+
for (let i = 0; i < publicPart.length; i++) {
|
|
11
|
+
const char = publicPart.charCodeAt(i);
|
|
12
|
+
hash = (hash << 5) - hash + char;
|
|
13
|
+
hash = hash & hash;
|
|
14
|
+
}
|
|
15
|
+
return `k_${Math.abs(hash).toString(16).padStart(8, "0")}`;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// src/keygen.ts
|
|
19
|
+
var DEFAULT_KEY_FILENAME = "signing-key.jwk";
|
|
20
|
+
async function generateSigningKey(options) {
|
|
21
|
+
const outputDir = options?.outputDir ?? ".";
|
|
22
|
+
const filename = options?.filename ?? DEFAULT_KEY_FILENAME;
|
|
23
|
+
const { privateKey: privateKeyBytes, publicKey: publicKeyBytes } = await generateKeypair();
|
|
24
|
+
const d = base64urlEncode(privateKeyBytes);
|
|
25
|
+
const x = base64urlEncode(publicKeyBytes);
|
|
26
|
+
const privateJwk = {
|
|
27
|
+
kty: "OKP",
|
|
28
|
+
crv: "Ed25519",
|
|
29
|
+
x,
|
|
30
|
+
d,
|
|
31
|
+
alg: "EdDSA",
|
|
32
|
+
use: "sig"
|
|
33
|
+
};
|
|
34
|
+
const kid = generateKeyId(privateJwk);
|
|
35
|
+
privateJwk.kid = kid;
|
|
36
|
+
const publicJwk = {
|
|
37
|
+
kty: "OKP",
|
|
38
|
+
crv: "Ed25519",
|
|
39
|
+
x,
|
|
40
|
+
kid,
|
|
41
|
+
alg: "EdDSA",
|
|
42
|
+
use: "sig"
|
|
43
|
+
};
|
|
44
|
+
await fs.mkdir(outputDir, { recursive: true });
|
|
45
|
+
const keyPath = path.join(outputDir, filename);
|
|
46
|
+
await fs.writeFile(keyPath, JSON.stringify(privateJwk, null, 2) + "\n", "utf-8");
|
|
47
|
+
try {
|
|
48
|
+
await fs.chmod(keyPath, 384);
|
|
49
|
+
} catch {
|
|
50
|
+
process.stderr.write(
|
|
51
|
+
`Warning: Could not set file permissions on ${keyPath}. Ensure the file is not readable by other users.
|
|
52
|
+
`
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
return { kid, keyPath, publicJwk, privateJwk };
|
|
56
|
+
}
|
|
57
|
+
async function keygenCli(args) {
|
|
58
|
+
let outputDir;
|
|
59
|
+
for (let i = 0; i < args.length; i++) {
|
|
60
|
+
if (args[i] === "--output-dir" && i + 1 < args.length) {
|
|
61
|
+
outputDir = args[i + 1];
|
|
62
|
+
i++;
|
|
63
|
+
} else if (args[i] === "--help" || args[i] === "-h") {
|
|
64
|
+
process.stdout.write(
|
|
65
|
+
"Usage: peac-keygen [--output-dir <dir>]\n\nGenerate an Ed25519 signing key for PEAC receipt signing.\n\nOptions:\n --output-dir <dir> Directory for key file (default: current directory)\n --help, -h Show this help message\n"
|
|
66
|
+
);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
const result = await generateSigningKey({ outputDir });
|
|
71
|
+
process.stdout.write(`Key generated successfully.
|
|
72
|
+
`);
|
|
73
|
+
process.stdout.write(` kid: ${result.kid}
|
|
74
|
+
`);
|
|
75
|
+
process.stdout.write(` key file: ${result.keyPath}
|
|
76
|
+
`);
|
|
77
|
+
process.stdout.write(` public x: ${result.publicJwk.x}
|
|
78
|
+
`);
|
|
79
|
+
process.stdout.write(`
|
|
80
|
+
To use with the OpenClaw adapter:
|
|
81
|
+
`);
|
|
82
|
+
process.stdout.write(` signing.key_ref: "file:${path.resolve(result.keyPath)}"
|
|
83
|
+
`);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// src/keygen-cli.ts
|
|
87
|
+
keygenCli(process.argv.slice(2)).catch((err) => {
|
|
88
|
+
process.stderr.write(`Error: ${err.message}
|
|
89
|
+
`);
|
|
90
|
+
process.exitCode = 1;
|
|
91
|
+
});
|
|
92
|
+
//# sourceMappingURL=keygen-cli.mjs.map
|
|
93
|
+
//# sourceMappingURL=keygen-cli.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/plugin.ts","../src/keygen.ts","../src/keygen-cli.ts"],"names":[],"mappings":"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","file":"keygen-cli.mjs","sourcesContent":["/**\n * @peac/adapter-openclaw - Plugin Entry Point\n *\n * OpenClaw plugin types and utilities for PEAC receipts.\n * This module provides types and factory functions for OpenClaw integration.\n *\n * Note: Full plugin activation requires OpenClaw's plugin runtime.\n * The types and utilities here allow building custom integrations.\n */\n\nimport type { CaptureSession, SpoolStore, DedupeIndex } from '@peac/capture-core';\nimport { createCaptureSession, createHasher } from '@peac/capture-core';\nimport { sign, base64urlDecode } from '@peac/crypto';\nimport type { OpenClawAdapterConfig } from './types.js';\nimport { createHookHandler, type OpenClawHookHandler } from './hooks.js';\nimport {\n createReceiptEmitter,\n createBackgroundService,\n type Signer,\n type ReceiptWriter,\n type BackgroundEmitterService,\n} from './emitter.js';\n\n// =============================================================================\n// JWK Type\n// =============================================================================\n\n/**\n * JSON Web Key structure for signing keys (Ed25519).\n */\nexport interface JWK {\n kty: string;\n crv?: string;\n x?: string;\n d?: string;\n kid?: string;\n alg?: string;\n use?: string;\n}\n\n// =============================================================================\n// Plugin Configuration Types\n// =============================================================================\n\n/**\n * Plugin configuration from openclaw.plugin.json configSchema.\n *\n * @experimental This interface is experimental and may change.\n */\nexport interface PluginConfig {\n enabled?: boolean;\n output_dir?: string;\n capture?: {\n mode?: 'hash_only' | 'allowlist';\n allowlist?: string[];\n max_payload_size?: number;\n };\n signing: {\n key_ref: string;\n issuer: string;\n audience?: string;\n };\n correlation?: {\n include_workflow?: boolean;\n // Note: include_identity and identity_attestation_ref are reserved for future use\n };\n background?: {\n drain_interval_ms?: number;\n batch_size?: number;\n };\n}\n\n/**\n * OpenClaw plugin context provided by the runtime.\n */\nexport interface PluginContext {\n /** Plugin configuration from gateway config */\n config: PluginConfig;\n /** Agent workspace directory */\n workspaceDir: string;\n /** Logger instance */\n logger: PluginLogger;\n /** Register a tool call hook */\n onToolCall: (handler: ToolCallHandler) => void;\n /** Register a tool result hook */\n onToolResult: (handler: ToolResultHandler) => void;\n /** Register tools */\n registerTools: (tools: PluginTool[]) => void;\n}\n\n/**\n * Plugin logger interface.\n */\nexport interface PluginLogger {\n debug: (message: string, ...args: unknown[]) => void;\n info: (message: string, ...args: unknown[]) => void;\n warn: (message: string, ...args: unknown[]) => void;\n error: (message: string, ...args: unknown[]) => void;\n}\n\n/**\n * Tool call event from OpenClaw.\n */\nexport interface ToolCallEvent {\n tool_call_id: string;\n run_id: string;\n session_key?: string;\n tool_name: string;\n tool_provider?: string;\n input: unknown;\n started_at: string;\n}\n\n/**\n * Tool result event from OpenClaw.\n */\nexport interface ToolResultEvent {\n tool_call_id: string;\n run_id: string;\n session_key?: string;\n tool_name: string;\n output: unknown;\n completed_at: string;\n status: 'ok' | 'error' | 'timeout' | 'canceled';\n error_code?: string;\n}\n\n/**\n * Tool call handler function.\n */\nexport type ToolCallHandler = (event: ToolCallEvent) => void | Promise<void>;\n\n/**\n * Tool result handler function.\n */\nexport type ToolResultHandler = (event: ToolResultEvent) => void | Promise<void>;\n\n/**\n * Plugin tool definition.\n */\nexport interface PluginTool {\n name: string;\n description: string;\n parameters?: Record<string, unknown>;\n execute: (params: Record<string, unknown>) => Promise<unknown>;\n}\n\n// =============================================================================\n// Signer Creation\n// =============================================================================\n\n/**\n * Create a signer from a JWK private key.\n *\n * @param jwk - Ed25519 private key in JWK format\n * @param issuer - Issuer URI for receipts\n * @param audience - Optional audience URI\n * @returns Signer instance\n */\nexport function createJwkSigner(jwk: JWK, issuer: string, audience?: string): Signer {\n const kid = jwk.kid || generateKeyId(jwk);\n\n // Extract the private key bytes from JWK\n if (!jwk.d) {\n throw new Error('JWK must include private key (d parameter)');\n }\n\n // Decode base64url to get raw key bytes\n const privateKeyBytes = base64urlDecode(jwk.d);\n\n return {\n async sign(payload: unknown): Promise<string> {\n return sign(payload, privateKeyBytes, kid);\n },\n\n getKeyId(): string {\n return kid;\n },\n\n getIssuer(): string {\n return issuer;\n },\n\n getAudience(): string | undefined {\n return audience;\n },\n };\n}\n\n/**\n * Resolve a signer from a key reference string.\n *\n * Supported formats:\n * - env:VAR_NAME - Load key from environment variable\n * - file:/path - Load from file (development only)\n *\n * @param keyRef - Key reference string\n * @param issuer - Issuer URI\n * @param audience - Optional audience URI\n * @returns Promise resolving to Signer\n */\nexport async function resolveSigner(\n keyRef: string,\n issuer: string,\n audience?: string\n): Promise<Signer> {\n const colonIndex = keyRef.indexOf(':');\n if (colonIndex === -1) {\n throw new Error(`Invalid key reference format: ${keyRef}. Expected scheme:value`);\n }\n\n const scheme = keyRef.slice(0, colonIndex);\n const value = keyRef.slice(colonIndex + 1);\n\n switch (scheme) {\n case 'env': {\n const keyData = process.env[value];\n if (!keyData) {\n throw new Error(`Environment variable ${value} not set for signing key`);\n }\n const jwk = JSON.parse(keyData) as JWK;\n return createJwkSigner(jwk, issuer, audience);\n }\n\n case 'file': {\n const fs = await import('fs');\n const keyData = await fs.promises.readFile(value, 'utf-8');\n const jwk = JSON.parse(keyData) as JWK;\n return createJwkSigner(jwk, issuer, audience);\n }\n\n case 'keychain':\n throw new Error('Keychain signing not yet implemented. Use env: for development.');\n\n case 'sidecar':\n throw new Error('Sidecar signing not yet implemented. Use env: for development.');\n\n default:\n throw new Error(`Unknown key reference scheme: ${scheme}. Use env: or file:`);\n }\n}\n\n/**\n * Generate a key ID from a JWK by hashing its public components.\n */\nexport function generateKeyId(jwk: JWK): string {\n const publicPart = jwk.x || '';\n let hash = 0;\n for (let i = 0; i < publicPart.length; i++) {\n const char = publicPart.charCodeAt(i);\n hash = (hash << 5) - hash + char;\n hash = hash & hash;\n }\n return `k_${Math.abs(hash).toString(16).padStart(8, '0')}`;\n}\n\n// =============================================================================\n// Receipt Writer\n// =============================================================================\n\n/**\n * Create a file system receipt writer with atomic writes.\n *\n * Writes are atomic: write to .tmp, fsync, rename. This ensures\n * partially written files are never observable.\n *\n * @param outputDir - Directory for receipt files\n * @returns Promise resolving to ReceiptWriter\n */\nexport async function createFileReceiptWriter(outputDir: string): Promise<ReceiptWriter> {\n const fs = await import('fs');\n const pathModule = await import('path');\n\n // Use path.resolve for portability\n const resolvedOutputDir = pathModule.resolve(outputDir);\n\n // Ensure directory exists\n await fs.promises.mkdir(resolvedOutputDir, { recursive: true });\n\n return {\n async write(receipt) {\n const filename = `${receipt.rid}.peac.json`;\n const filepath = pathModule.join(resolvedOutputDir, filename);\n const tempPath = `${filepath}.tmp`;\n\n // Decode the JWS payload to persist the full receipt structure.\n // The verify and export tools expect auth + evidence blocks at the top level.\n // JWS compact serialization: header.payload.signature (base64url)\n let content: string;\n const jwsParts = receipt.jws.split('.');\n if (jwsParts.length !== 3) {\n // Malformed JWS -- write _jws-only receipt (best effort, still recoverable)\n content = JSON.stringify({ _jws: receipt.jws }, null, 2);\n } else {\n try {\n const payloadJson = Buffer.from(jwsParts[1], 'base64url').toString('utf-8');\n const payload = JSON.parse(payloadJson);\n\n // Split payload into auth (identity fields) and evidence (data fields)\n const { evidence, ...auth } = payload;\n\n content = JSON.stringify({ auth, evidence, _jws: receipt.jws }, null, 2);\n } catch {\n // Payload decode failed -- write _jws-only receipt (best effort)\n content = JSON.stringify({ _jws: receipt.jws }, null, 2);\n }\n }\n\n // Atomic write: write to .tmp, fsync, rename, fsync dir\n await fs.promises.writeFile(tempPath, content, 'utf-8');\n\n // fsync file for durability (open, sync, close)\n const fd = await fs.promises.open(tempPath, 'r');\n await fd.sync();\n await fd.close();\n\n // Atomic rename (POSIX guarantees atomicity)\n await fs.promises.rename(tempPath, filepath);\n\n // fsync parent directory for full crash durability (POSIX best practice)\n // This ensures the rename is persisted even on power loss\n // Best-effort: may fail on some platforms (Windows, network FS)\n try {\n const dirFd = await fs.promises.open(resolvedOutputDir, 'r');\n await dirFd.sync();\n await dirFd.close();\n } catch {\n // Directory fsync not supported on this platform - continue anyway\n // Receipt is still atomic via rename, just not fully durable on power loss\n }\n\n return filepath;\n },\n\n async close() {\n // No-op for file writer\n },\n };\n}\n\n// =============================================================================\n// Plugin Instance\n// =============================================================================\n\n/**\n * Plugin instance state.\n */\nexport interface PluginInstance {\n /** Capture session */\n session: CaptureSession;\n /** Hook handler for tool calls */\n hookHandler: OpenClawHookHandler;\n /** Background emitter service */\n backgroundService: BackgroundEmitterService;\n /** Start the plugin */\n start(): void;\n /** Stop the plugin */\n stop(): Promise<void>;\n /** Get plugin stats */\n getStats(): PluginStats;\n}\n\n/**\n * Plugin statistics.\n */\nexport interface PluginStats {\n totalCaptured: number;\n duplicatesSkipped: number;\n pendingCount: number;\n totalEmitted: number;\n totalErrors: number;\n keyId: string;\n isRunning: boolean;\n}\n\n/**\n * Options for creating a plugin instance.\n */\nexport interface CreatePluginOptions {\n /** Signer for receipts */\n signer: Signer;\n /** Receipt writer */\n writer: ReceiptWriter;\n /** Adapter configuration */\n adapterConfig?: OpenClawAdapterConfig;\n /** Spool store implementation (required) */\n store: SpoolStore;\n /** Dedupe index implementation (required) */\n dedupe: DedupeIndex;\n /** Background drain interval in ms (default: 1000) */\n drainIntervalMs?: number;\n /** Batch size for draining (default: 100) */\n batchSize?: number;\n /** Error callback */\n onError?: (error: Error) => void;\n}\n\n/**\n * Create a plugin instance.\n *\n * @param options - Plugin options\n * @returns Plugin instance\n */\nexport async function createPluginInstance(options: CreatePluginOptions): Promise<PluginInstance> {\n const {\n signer,\n writer,\n adapterConfig,\n store,\n dedupe,\n drainIntervalMs = 1000,\n batchSize = 100,\n onError,\n } = options;\n\n // Create hasher\n const hasher = await createHasher();\n\n // Create capture session\n const session = createCaptureSession({\n store,\n dedupe,\n hasher,\n });\n\n // Track stats\n const stats: PluginStats = {\n totalCaptured: 0,\n duplicatesSkipped: 0,\n pendingCount: 0,\n totalEmitted: 0,\n totalErrors: 0,\n keyId: signer.getKeyId(),\n isRunning: false,\n };\n\n // Create hook handler\n const hookHandler = createHookHandler({\n session,\n config: adapterConfig,\n onCapture: (result) => {\n if (result.success) {\n stats.totalCaptured++;\n stats.pendingCount++;\n } else {\n stats.duplicatesSkipped++;\n }\n },\n });\n\n // Create receipt emitter\n const emitter = createReceiptEmitter({\n signer,\n writer,\n onEmit: (result) => {\n if (result.success) {\n stats.totalEmitted++;\n stats.pendingCount = Math.max(0, stats.pendingCount - 1);\n }\n },\n onError: (error) => {\n stats.totalErrors++;\n if (onError) onError(error);\n },\n });\n\n // Track last emitted sequence for cursor-based emission\n // This is the in-memory cursor; on restart, we rebuild from dedupe index\n let lastEmittedSequence = 0;\n\n // Mutex to prevent overlapping drain cycles\n let drainInProgress = false;\n\n // Create background service with proper cursor tracking\n // Note: For full restart safety, the dedupe index should be persistent.\n // The getPendingEntries filters out already-emitted entries using the dedupe index.\n const backgroundService = createBackgroundService({\n emitter,\n drainIntervalMs,\n getPendingEntries: async () => {\n // Prevent overlapping drain cycles\n if (drainInProgress) return [];\n drainInProgress = true;\n\n try {\n // Read entries after the last emitted sequence\n const currentSequence = await store.getSequence();\n if (currentSequence <= lastEmittedSequence) return [];\n\n // Read from cursor position\n const entries = await store.read(lastEmittedSequence, batchSize);\n\n // Filter out entries that are already emitted (restart safety)\n // This uses the persistent dedupe index as source of truth\n const pending = [];\n for (const entry of entries) {\n if (entry.sequence <= lastEmittedSequence) continue;\n // Check if already emitted in dedupe index\n const dedupeEntry = await dedupe.get(entry.action.id);\n if (dedupeEntry?.emitted) continue;\n pending.push(entry);\n }\n return pending;\n } finally {\n drainInProgress = false;\n }\n },\n markEmitted: async (digest) => {\n // Find the entry with this digest and update cursor\n const entries = await store.read(lastEmittedSequence, batchSize);\n const entry = entries.find((e) => e.entry_digest === digest);\n if (entry) {\n // Update in-memory cursor\n if (entry.sequence > lastEmittedSequence) {\n lastEmittedSequence = entry.sequence;\n }\n // Mark in persistent dedupe index (restart-safe)\n await dedupe.markEmitted(entry.action.id);\n }\n },\n onError,\n });\n\n return {\n session,\n hookHandler,\n backgroundService,\n\n start() {\n stats.isRunning = true;\n backgroundService.start();\n },\n\n async stop() {\n backgroundService.stop();\n await backgroundService.drain();\n await hookHandler.close();\n stats.isRunning = false;\n },\n\n getStats() {\n return { ...stats };\n },\n };\n}\n","/**\n * @peac/adapter-openclaw - Signing Key Generation\n *\n * Generates an Ed25519 keypair for PEAC receipt signing.\n * Reuses generateKeyId() from plugin.ts for kid derivation.\n *\n * Usage (programmatic):\n * const result = await generateSigningKey({ outputDir: '~/.openclaw/peac' });\n * console.log(result.kid);\n *\n * Usage (CLI):\n * npx @peac/adapter-openclaw keygen\n * npx @peac/adapter-openclaw keygen --output-dir /path/to/dir\n */\n\nimport * as path from 'node:path';\nimport * as fs from 'node:fs/promises';\nimport { generateKeypair, base64urlEncode } from '@peac/crypto';\nimport { generateKeyId, type JWK } from './plugin.js';\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Options for key generation.\n */\nexport interface KeygenOptions {\n /** Directory to write the key file to. Default: current directory. */\n outputDir?: string;\n /** Filename for the private key. Default: signing-key.jwk */\n filename?: string;\n}\n\n/**\n * Result of key generation.\n */\nexport interface KeygenResult {\n /** Key ID (derived from public key). */\n kid: string;\n /** Path where the private key was written. */\n keyPath: string;\n /** The public JWK (safe to share). */\n publicJwk: JWK;\n /** The private JWK (keep secret). */\n privateJwk: JWK;\n}\n\n// =============================================================================\n// Defaults\n// =============================================================================\n\nconst DEFAULT_KEY_FILENAME = 'signing-key.jwk';\n\n// =============================================================================\n// Key Generation\n// =============================================================================\n\n/**\n * Generate an Ed25519 signing keypair and write the private key to disk.\n *\n * - Private key file has 0o600 permissions (owner read/write only).\n * On platforms where chmod fails (Windows), a warning is logged to stderr.\n * - The key ID (kid) is derived from the public key via generateKeyId().\n *\n * @returns KeygenResult with paths and key material\n */\nexport async function generateSigningKey(options?: KeygenOptions): Promise<KeygenResult> {\n const outputDir = options?.outputDir ?? '.';\n const filename = options?.filename ?? DEFAULT_KEY_FILENAME;\n\n // Generate Ed25519 keypair via @peac/crypto\n const { privateKey: privateKeyBytes, publicKey: publicKeyBytes } = await generateKeypair();\n\n // Encode as base64url\n const d = base64urlEncode(privateKeyBytes);\n const x = base64urlEncode(publicKeyBytes);\n\n // Build JWK\n const privateJwk: JWK = {\n kty: 'OKP',\n crv: 'Ed25519',\n x,\n d,\n alg: 'EdDSA',\n use: 'sig',\n };\n\n // Derive key ID from public component\n const kid = generateKeyId(privateJwk);\n privateJwk.kid = kid;\n\n // Public JWK (no private component)\n const publicJwk: JWK = {\n kty: 'OKP',\n crv: 'Ed25519',\n x,\n kid,\n alg: 'EdDSA',\n use: 'sig',\n };\n\n // Write to disk\n await fs.mkdir(outputDir, { recursive: true });\n const keyPath = path.join(outputDir, filename);\n await fs.writeFile(keyPath, JSON.stringify(privateJwk, null, 2) + '\\n', 'utf-8');\n\n // Set restrictive permissions (0o600 = owner read/write only)\n try {\n await fs.chmod(keyPath, 0o600);\n } catch {\n // chmod may fail on Windows -- log warning but don't fail\n process.stderr.write(\n `Warning: Could not set file permissions on ${keyPath}. ` +\n `Ensure the file is not readable by other users.\\n`\n );\n }\n\n return { kid, keyPath, publicJwk, privateJwk };\n}\n\n// =============================================================================\n// CLI Entry Point\n// =============================================================================\n\n/**\n * CLI handler for `peac-keygen` command.\n * Parses args, generates key, prints results.\n */\nexport async function keygenCli(args: string[]): Promise<void> {\n let outputDir: string | undefined;\n\n for (let i = 0; i < args.length; i++) {\n if (args[i] === '--output-dir' && i + 1 < args.length) {\n outputDir = args[i + 1];\n i++;\n } else if (args[i] === '--help' || args[i] === '-h') {\n process.stdout.write(\n 'Usage: peac-keygen [--output-dir <dir>]\\n\\n' +\n 'Generate an Ed25519 signing key for PEAC receipt signing.\\n\\n' +\n 'Options:\\n' +\n ' --output-dir <dir> Directory for key file (default: current directory)\\n' +\n ' --help, -h Show this help message\\n'\n );\n return;\n }\n }\n\n const result = await generateSigningKey({ outputDir });\n\n process.stdout.write(`Key generated successfully.\\n`);\n process.stdout.write(` kid: ${result.kid}\\n`);\n process.stdout.write(` key file: ${result.keyPath}\\n`);\n process.stdout.write(` public x: ${result.publicJwk.x}\\n`);\n process.stdout.write(`\\nTo use with the OpenClaw adapter:\\n`);\n process.stdout.write(` signing.key_ref: \"file:${path.resolve(result.keyPath)}\"\\n`);\n}\n","#!/usr/bin/env node\n/**\n * CLI entry point for peac-keygen.\n * Invoked via: npx @peac/adapter-openclaw keygen\n */\n\nimport { keygenCli } from './keygen.js';\n\nkeygenCli(process.argv.slice(2)).catch((err: Error) => {\n process.stderr.write(`Error: ${err.message}\\n`);\n process.exitCode = 1;\n});\n"]}
|
package/dist/keygen.d.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @peac/adapter-openclaw - Signing Key Generation
|
|
3
|
+
*
|
|
4
|
+
* Generates an Ed25519 keypair for PEAC receipt signing.
|
|
5
|
+
* Reuses generateKeyId() from plugin.ts for kid derivation.
|
|
6
|
+
*
|
|
7
|
+
* Usage (programmatic):
|
|
8
|
+
* const result = await generateSigningKey({ outputDir: '~/.openclaw/peac' });
|
|
9
|
+
* console.log(result.kid);
|
|
10
|
+
*
|
|
11
|
+
* Usage (CLI):
|
|
12
|
+
* npx @peac/adapter-openclaw keygen
|
|
13
|
+
* npx @peac/adapter-openclaw keygen --output-dir /path/to/dir
|
|
14
|
+
*/
|
|
15
|
+
import { type JWK } from './plugin.js';
|
|
16
|
+
/**
|
|
17
|
+
* Options for key generation.
|
|
18
|
+
*/
|
|
19
|
+
export interface KeygenOptions {
|
|
20
|
+
/** Directory to write the key file to. Default: current directory. */
|
|
21
|
+
outputDir?: string;
|
|
22
|
+
/** Filename for the private key. Default: signing-key.jwk */
|
|
23
|
+
filename?: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Result of key generation.
|
|
27
|
+
*/
|
|
28
|
+
export interface KeygenResult {
|
|
29
|
+
/** Key ID (derived from public key). */
|
|
30
|
+
kid: string;
|
|
31
|
+
/** Path where the private key was written. */
|
|
32
|
+
keyPath: string;
|
|
33
|
+
/** The public JWK (safe to share). */
|
|
34
|
+
publicJwk: JWK;
|
|
35
|
+
/** The private JWK (keep secret). */
|
|
36
|
+
privateJwk: JWK;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Generate an Ed25519 signing keypair and write the private key to disk.
|
|
40
|
+
*
|
|
41
|
+
* - Private key file has 0o600 permissions (owner read/write only).
|
|
42
|
+
* On platforms where chmod fails (Windows), a warning is logged to stderr.
|
|
43
|
+
* - The key ID (kid) is derived from the public key via generateKeyId().
|
|
44
|
+
*
|
|
45
|
+
* @returns KeygenResult with paths and key material
|
|
46
|
+
*/
|
|
47
|
+
export declare function generateSigningKey(options?: KeygenOptions): Promise<KeygenResult>;
|
|
48
|
+
/**
|
|
49
|
+
* CLI handler for `peac-keygen` command.
|
|
50
|
+
* Parses args, generates key, prints results.
|
|
51
|
+
*/
|
|
52
|
+
export declare function keygenCli(args: string[]): Promise<void>;
|
|
53
|
+
//# sourceMappingURL=keygen.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keygen.d.ts","sourceRoot":"","sources":["../src/keygen.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAKH,OAAO,EAAiB,KAAK,GAAG,EAAE,MAAM,aAAa,CAAC;AAMtD;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,sEAAsE;IACtE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,wCAAwC;IACxC,GAAG,EAAE,MAAM,CAAC;IACZ,8CAA8C;IAC9C,OAAO,EAAE,MAAM,CAAC;IAChB,sCAAsC;IACtC,SAAS,EAAE,GAAG,CAAC;IACf,qCAAqC;IACrC,UAAU,EAAE,GAAG,CAAC;CACjB;AAYD;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CAoDvF;AAMD;;;GAGG;AACH,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA2B7D"}
|
package/dist/plugin.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"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"}
|
package/dist/tools.d.ts
CHANGED
|
@@ -21,8 +21,12 @@ interface PluginStats {
|
|
|
21
21
|
/**
|
|
22
22
|
* Create the peac_receipts.status tool.
|
|
23
23
|
* Shows spool size, last receipt time, and config summary.
|
|
24
|
+
*
|
|
25
|
+
* @param getStats - Getter function that returns live stats on each invocation.
|
|
26
|
+
* Accepts either a getter or a static PluginStats for backwards compat.
|
|
27
|
+
* @param outputDir - Directory where receipt files are stored.
|
|
24
28
|
*/
|
|
25
|
-
export declare function createStatusTool(
|
|
29
|
+
export declare function createStatusTool(getStats: (() => PluginStats) | PluginStats, outputDir: string): PluginTool;
|
|
26
30
|
/**
|
|
27
31
|
* Create the peac_receipts.export_bundle tool.
|
|
28
32
|
* Exports receipts as a bundle directory for audit.
|
package/dist/tools.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AA0D5D;;GAEG;AACH,UAAU,WAAW;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,OAAO,CAAC;CACpB;AAMD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,CAAC,MAAM,WAAW,CAAC,GAAG,WAAW,EAC3C,SAAS,EAAE,MAAM,GAChB,UAAU,CAqEZ;AA4BD;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,UAAU,CAmM1F;AA8BD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,YAAY,GAAG,UAAU,CAmDjE;AA8UD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,UAAU,CAiPnF"}
|
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@peac/adapter-openclaw",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.12",
|
|
4
4
|
"description": "OpenClaw adapter for PEAC interaction evidence capture",
|
|
5
5
|
"main": "dist/index.cjs",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -25,6 +25,9 @@
|
|
|
25
25
|
"url": "https://github.com/peacprotocol/peac/issues"
|
|
26
26
|
},
|
|
27
27
|
"homepage": "https://github.com/peacprotocol/peac#readme",
|
|
28
|
+
"bin": {
|
|
29
|
+
"peac-keygen": "./dist/keygen-cli.cjs"
|
|
30
|
+
},
|
|
28
31
|
"files": [
|
|
29
32
|
"dist",
|
|
30
33
|
"openclaw.plugin.json",
|
|
@@ -36,14 +39,15 @@
|
|
|
36
39
|
"provenance": true
|
|
37
40
|
},
|
|
38
41
|
"dependencies": {
|
|
39
|
-
"@peac/
|
|
40
|
-
"@peac/
|
|
41
|
-
"@peac/
|
|
42
|
-
"@peac/
|
|
43
|
-
"@peac/
|
|
42
|
+
"@peac/adapter-core": "0.10.12",
|
|
43
|
+
"@peac/capture-core": "0.10.12",
|
|
44
|
+
"@peac/capture-node": "0.10.12",
|
|
45
|
+
"@peac/crypto": "0.10.12",
|
|
46
|
+
"@peac/schema": "0.10.12",
|
|
47
|
+
"@peac/kernel": "0.10.12"
|
|
44
48
|
},
|
|
45
49
|
"devDependencies": {
|
|
46
|
-
"@types/node": "^
|
|
50
|
+
"@types/node": "^22.0.0",
|
|
47
51
|
"typescript": "^5.3.3",
|
|
48
52
|
"vitest": "^4.0.0",
|
|
49
53
|
"tsup": "^8.0.0"
|