openclaw-airlock 0.4.7

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.
Files changed (43) hide show
  1. package/README.md +406 -0
  2. package/dist/cli/register.d.ts +22 -0
  3. package/dist/cli/register.d.ts.map +1 -0
  4. package/dist/cli/register.js +229 -0
  5. package/dist/cli/register.js.map +1 -0
  6. package/dist/client.d.ts +136 -0
  7. package/dist/client.d.ts.map +1 -0
  8. package/dist/client.js +474 -0
  9. package/dist/client.js.map +1 -0
  10. package/dist/commands/airlockStatus.d.ts +20 -0
  11. package/dist/commands/airlockStatus.d.ts.map +1 -0
  12. package/dist/commands/airlockStatus.js +48 -0
  13. package/dist/commands/airlockStatus.js.map +1 -0
  14. package/dist/config.d.ts +48 -0
  15. package/dist/config.d.ts.map +1 -0
  16. package/dist/config.js +104 -0
  17. package/dist/config.js.map +1 -0
  18. package/dist/crypto.d.ts +41 -0
  19. package/dist/crypto.d.ts.map +1 -0
  20. package/dist/crypto.js +112 -0
  21. package/dist/crypto.js.map +1 -0
  22. package/dist/hooks/beforeTool.d.ts +46 -0
  23. package/dist/hooks/beforeTool.d.ts.map +1 -0
  24. package/dist/hooks/beforeTool.js +112 -0
  25. package/dist/hooks/beforeTool.js.map +1 -0
  26. package/dist/index.d.ts +50 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +88 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/state.d.ts +44 -0
  31. package/dist/state.d.ts.map +1 -0
  32. package/dist/state.js +79 -0
  33. package/dist/state.js.map +1 -0
  34. package/dist/tools/checkStatus.d.ts +42 -0
  35. package/dist/tools/checkStatus.d.ts.map +1 -0
  36. package/dist/tools/checkStatus.js +67 -0
  37. package/dist/tools/checkStatus.js.map +1 -0
  38. package/dist/tools/requestApproval.d.ts +63 -0
  39. package/dist/tools/requestApproval.d.ts.map +1 -0
  40. package/dist/tools/requestApproval.js +85 -0
  41. package/dist/tools/requestApproval.js.map +1 -0
  42. package/openclaw.plugin.json +69 -0
  43. package/package.json +61 -0
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Airlock Client — wraps @airlockapp/gateway-sdk with approval workflow logic.
3
+ *
4
+ * Responsibilities:
5
+ * - Submit encrypted artifacts for approval
6
+ * - Poll for decisions (long-poll with server-side 25s timeout)
7
+ * - Verify decision signatures (Ed25519)
8
+ * - Claim pre-generated pairing codes (X25519 ECDH key exchange)
9
+ * - Persist pairing state to disk
10
+ * - Health check via gateway echo
11
+ * - Presence heartbeat
12
+ * - DND (Do Not Disturb) policy check
13
+ * - Handle HTTP errors (401, 403, 422, 429) with appropriate responses
14
+ */
15
+ import type { AirlockConfig } from "./config.js";
16
+ /** Payload for an approval request. */
17
+ export interface ApprovalPayload {
18
+ /** Tool name or action type (e.g. "shell.exec", "deploy.run"). */
19
+ actionType: string;
20
+ /** The command or action text to display to the approver. */
21
+ commandText: string;
22
+ /** Human-readable description for the approval card. */
23
+ description: string;
24
+ /** Optional additional context. */
25
+ context?: string;
26
+ }
27
+ /** Approval decision returned by the client. */
28
+ export interface Decision {
29
+ /** The decision result. */
30
+ decision: "approved" | "rejected" | "timeout";
31
+ /** Optional reason from the approver. */
32
+ reason?: string;
33
+ /** The exchange request ID. */
34
+ requestId: string;
35
+ }
36
+ /** Gateway health check result. */
37
+ export interface HealthResult {
38
+ connected: boolean;
39
+ gatewayUrl: string;
40
+ serverTime?: string;
41
+ error?: string;
42
+ }
43
+ /** Consent status values. */
44
+ export type ConsentStatus = "approved" | "required" | "pending" | "denied" | "unknown";
45
+ /** Result of a consent check. */
46
+ export interface ConsentResult {
47
+ status: ConsentStatus;
48
+ message?: string;
49
+ consentUrl?: string;
50
+ }
51
+ export declare class AirlockClient {
52
+ private readonly gateway;
53
+ private readonly config;
54
+ private readonly log;
55
+ private heartbeatTimer;
56
+ private pairedKeys;
57
+ constructor(config: AirlockConfig, logger?: (msg: string) => void);
58
+ private initPromise;
59
+ /**
60
+ * Restore pairing state from disk and start presence heartbeat.
61
+ * Idempotent — safe to call multiple times; only runs once.
62
+ */
63
+ initialize(): Promise<void>;
64
+ /**
65
+ * Ensure initialization is complete before proceeding.
66
+ * CLI commands should await this before checking config.routingToken etc.
67
+ */
68
+ ensureInitialized(): Promise<void>;
69
+ private _doInitialize;
70
+ /** Stop background tasks (heartbeat timer). */
71
+ dispose(): void;
72
+ /**
73
+ * Submit an encrypted artifact for approval and poll for the decision.
74
+ * Returns when the approver decides or the timeout elapses.
75
+ */
76
+ requestApproval(payload: ApprovalPayload): Promise<Decision>;
77
+ /**
78
+ * Poll for a decision using the long-poll endpoint.
79
+ * Repeats until the decision is received or timeout elapses.
80
+ */
81
+ private pollDecision;
82
+ /**
83
+ * Check if a DND (Do Not Disturb) policy is active that would auto-approve
84
+ * actions for the current enforcer/workspace.
85
+ *
86
+ * @returns True if a matching DND policy covers this action (auto-approve).
87
+ */
88
+ isDndActive(): Promise<boolean>;
89
+ /** Result of a consent check. */
90
+ /** {@link checkConsent} */
91
+ /**
92
+ * Check the user consent status for this enforcer app.
93
+ *
94
+ * Calls GET /v1/consent/status. The gateway returns:
95
+ * - 200 + { status: "approved" } — consent granted
96
+ * - 403 + app_consent_required — first contact; push sent to mobile app
97
+ * - 403 + app_consent_pending — user hasn't responded yet
98
+ * - 403 + app_consent_denied — user denied
99
+ *
100
+ * @returns ConsentResult with status and optional message/consentUrl.
101
+ */
102
+ checkConsent(): Promise<ConsentResult>;
103
+ /** Check gateway connectivity via the echo endpoint. */
104
+ checkHealth(): Promise<HealthResult>;
105
+ /**
106
+ * Get the status of an exchange by request ID.
107
+ * Returns the exchange state (Pending, Decided, Expired, Withdrawn).
108
+ */
109
+ getExchangeStatus(requestId: string): Promise<{
110
+ state: string;
111
+ createdAt?: string;
112
+ expiresAt?: string;
113
+ }>;
114
+ /** Start periodic presence heartbeat so the mobile app shows enforcer online. */
115
+ private startHeartbeat;
116
+ /**
117
+ * Claim a pre-generated pairing code with proper X25519 ECDH key exchange.
118
+ * Polls until pairing is completed, derives shared encryption key,
119
+ * and persists the pairing state to disk.
120
+ *
121
+ * @param code The pre-generated pairing code.
122
+ * @returns Routing token and encryption key for artifact encryption.
123
+ */
124
+ claimPairing(code: string): Promise<{
125
+ routingToken: string;
126
+ encryptionKey: string;
127
+ pairingNonce: string;
128
+ }>;
129
+ private parseDecision;
130
+ private handleSubmitError;
131
+ /** Clear pairing state from memory and disk. */
132
+ private clearPairingState;
133
+ }
134
+ /** Factory function to create an AirlockClient from validated config. */
135
+ export declare function createAirlockClient(config: AirlockConfig, logger?: (msg: string) => void): AirlockClient;
136
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
package/dist/client.js ADDED
@@ -0,0 +1,474 @@
1
+ /**
2
+ * Airlock Client — wraps @airlockapp/gateway-sdk with approval workflow logic.
3
+ *
4
+ * Responsibilities:
5
+ * - Submit encrypted artifacts for approval
6
+ * - Poll for decisions (long-poll with server-side 25s timeout)
7
+ * - Verify decision signatures (Ed25519)
8
+ * - Claim pre-generated pairing codes (X25519 ECDH key exchange)
9
+ * - Persist pairing state to disk
10
+ * - Health check via gateway echo
11
+ * - Presence heartbeat
12
+ * - DND (Do Not Disturb) policy check
13
+ * - Handle HTTP errors (401, 403, 422, 429) with appropriate responses
14
+ */
15
+ import { AirlockGatewayClient, AirlockGatewayError, } from "@airlockapp/gateway-sdk";
16
+ import { generateX25519KeyPair, deriveSharedKey, verifyDecisionSignature, } from "./crypto.js";
17
+ import { loadPairingState, savePairingState, clearPairingState, } from "./state.js";
18
+ // ── Constants ───────────────────────────────────────────────────
19
+ /** Server-side long-poll timeout per request (seconds). */
20
+ const LONG_POLL_TIMEOUT_SEC = 25;
21
+ /** Source identifier sent in artifact metadata. */
22
+ const SOURCE_ID = "openclaw-airlock";
23
+ /** Presence heartbeat interval (ms). */
24
+ const HEARTBEAT_INTERVAL_MS = 45_000;
25
+ // ── Client ──────────────────────────────────────────────────────
26
+ export class AirlockClient {
27
+ gateway;
28
+ config;
29
+ log;
30
+ heartbeatTimer = null;
31
+ pairedKeys = {};
32
+ constructor(config, logger) {
33
+ this.config = config;
34
+ this.log = logger ?? ((msg) => console.info(`[Airlock] ${msg}`));
35
+ this.gateway = new AirlockGatewayClient({
36
+ baseUrl: config.gatewayUrl,
37
+ pat: config.pat,
38
+ clientId: config.clientId,
39
+ clientSecret: config.clientSecret,
40
+ });
41
+ }
42
+ initPromise = null;
43
+ // ── Initialization ────────────────────────────────────────────
44
+ /**
45
+ * Restore pairing state from disk and start presence heartbeat.
46
+ * Idempotent — safe to call multiple times; only runs once.
47
+ */
48
+ async initialize() {
49
+ if (!this.initPromise) {
50
+ this.initPromise = this._doInitialize();
51
+ }
52
+ return this.initPromise;
53
+ }
54
+ /**
55
+ * Ensure initialization is complete before proceeding.
56
+ * CLI commands should await this before checking config.routingToken etc.
57
+ */
58
+ async ensureInitialized() {
59
+ await this.initialize();
60
+ }
61
+ async _doInitialize() {
62
+ // Restore persisted pairing state (only if not already set from config)
63
+ if (!this.config.routingToken || !this.config.encryptionKey) {
64
+ const state = await loadPairingState();
65
+ if (state) {
66
+ this.config.routingToken = state.routingToken;
67
+ this.config.encryptionKey = state.encryptionKey;
68
+ this.pairedKeys = state.pairedKeys;
69
+ this.log("Pairing state restored from disk");
70
+ }
71
+ }
72
+ else {
73
+ this.log("Pairing state already in config");
74
+ }
75
+ // Start presence heartbeat
76
+ this.startHeartbeat();
77
+ }
78
+ /** Stop background tasks (heartbeat timer). */
79
+ dispose() {
80
+ if (this.heartbeatTimer) {
81
+ clearInterval(this.heartbeatTimer);
82
+ this.heartbeatTimer = null;
83
+ }
84
+ }
85
+ // ── Approval Flow ─────────────────────────────────────────────
86
+ /**
87
+ * Submit an encrypted artifact for approval and poll for the decision.
88
+ * Returns when the approver decides or the timeout elapses.
89
+ */
90
+ async requestApproval(payload) {
91
+ if (!this.config.encryptionKey) {
92
+ return {
93
+ decision: "rejected",
94
+ reason: "Not paired — run 'airlock pair' first",
95
+ requestId: "",
96
+ };
97
+ }
98
+ if (!this.config.routingToken) {
99
+ return {
100
+ decision: "rejected",
101
+ reason: "No routing token — run 'airlock pair' first",
102
+ requestId: "",
103
+ };
104
+ }
105
+ // Build plaintext payload with HARP requestedActions extension
106
+ const plaintextPayload = JSON.stringify({
107
+ actionType: payload.actionType,
108
+ commandText: payload.commandText,
109
+ description: payload.description,
110
+ context: payload.context ?? "",
111
+ workspace: this.config.workspaceName,
112
+ source: SOURCE_ID,
113
+ extensions: {
114
+ "org.harp.requestedActions": {
115
+ version: 1,
116
+ actions: [
117
+ { id: "approve", caption: "Approve", style: "primary", decision: "approve" },
118
+ { id: "reject", caption: "Reject", style: "danger", decision: "reject" },
119
+ ],
120
+ },
121
+ },
122
+ });
123
+ let requestId;
124
+ try {
125
+ requestId = await this.gateway.encryptAndSubmitArtifact({
126
+ enforcerId: this.config.enforcerId,
127
+ artifactType: "command.review",
128
+ plaintextPayload,
129
+ encryptionKeyBase64Url: this.config.encryptionKey,
130
+ metadata: {
131
+ workspaceName: this.config.workspaceName,
132
+ routingToken: this.config.routingToken,
133
+ requestLabel: payload.actionType === "terminal_command" ? "Terminal Command" : "Agent Action",
134
+ },
135
+ });
136
+ }
137
+ catch (err) {
138
+ return await this.handleSubmitError(err);
139
+ }
140
+ this.log(`Artifact submitted: ${requestId}`);
141
+ // Submit ack (fire-and-forget)
142
+ this.gateway.submitAck(`msg-${requestId}`, requestId).catch(() => { });
143
+ return this.pollDecision(requestId);
144
+ }
145
+ /**
146
+ * Poll for a decision using the long-poll endpoint.
147
+ * Repeats until the decision is received or timeout elapses.
148
+ */
149
+ async pollDecision(requestId) {
150
+ const deadline = Date.now() + this.config.timeoutMs;
151
+ let pollCount = 0;
152
+ while (Date.now() < deadline) {
153
+ pollCount++;
154
+ const remainingSec = Math.ceil((deadline - Date.now()) / 1000);
155
+ const serverTimeout = Math.min(LONG_POLL_TIMEOUT_SEC, remainingSec);
156
+ if (serverTimeout <= 0)
157
+ break;
158
+ try {
159
+ const envelope = await this.gateway.waitForDecision(requestId, serverTimeout);
160
+ if (!envelope) {
161
+ // 204 — no decision yet, continue polling
162
+ continue;
163
+ }
164
+ const decision = this.parseDecision(envelope, requestId);
165
+ if (decision)
166
+ return decision;
167
+ }
168
+ catch (err) {
169
+ if (err instanceof AirlockGatewayError) {
170
+ if (err.statusCode === 401) {
171
+ this.log("Received 401 during poll — retrying");
172
+ continue;
173
+ }
174
+ this.log(`Poll error (HTTP ${err.statusCode}): ${err.message}`);
175
+ }
176
+ else {
177
+ this.log(`Poll error: ${err instanceof Error ? err.message : String(err)}`);
178
+ }
179
+ }
180
+ }
181
+ // Timed out — withdraw the stale exchange
182
+ this.log(`Approval timed out after ${this.config.timeoutMs}ms (${pollCount} polls)`);
183
+ this.gateway.withdrawExchange(requestId).catch(() => { });
184
+ return { decision: "timeout", requestId };
185
+ }
186
+ // ── DND Policy Check ──────────────────────────────────────────
187
+ /**
188
+ * Check if a DND (Do Not Disturb) policy is active that would auto-approve
189
+ * actions for the current enforcer/workspace.
190
+ *
191
+ * @returns True if a matching DND policy covers this action (auto-approve).
192
+ */
193
+ async isDndActive() {
194
+ try {
195
+ const resp = await this.gateway.getEffectiveDndPolicies(this.config.enforcerId, this.config.enforcerId);
196
+ if (resp.body && resp.body.length > 0) {
197
+ this.log(`DND active: ${resp.body.length} policy/policies`);
198
+ return true;
199
+ }
200
+ return false;
201
+ }
202
+ catch {
203
+ // DND check is best-effort — if it fails, proceed to normal approval
204
+ return false;
205
+ }
206
+ }
207
+ // ── Consent ────────────────────────────────────────────────────
208
+ /** Result of a consent check. */
209
+ /** {@link checkConsent} */
210
+ /**
211
+ * Check the user consent status for this enforcer app.
212
+ *
213
+ * Calls GET /v1/consent/status. The gateway returns:
214
+ * - 200 + { status: "approved" } — consent granted
215
+ * - 403 + app_consent_required — first contact; push sent to mobile app
216
+ * - 403 + app_consent_pending — user hasn't responded yet
217
+ * - 403 + app_consent_denied — user denied
218
+ *
219
+ * @returns ConsentResult with status and optional message/consentUrl.
220
+ */
221
+ async checkConsent() {
222
+ try {
223
+ const status = await this.gateway.checkConsent();
224
+ return { status: status };
225
+ }
226
+ catch (err) {
227
+ if (err instanceof AirlockGatewayError) {
228
+ const code = err.errorCode ?? "";
229
+ if (code === "app_consent_required" ||
230
+ code === "app_consent_pending" ||
231
+ code === "app_consent_denied") {
232
+ // Parse consentUrl and message from the response body
233
+ let consentUrl;
234
+ let message;
235
+ try {
236
+ const body = JSON.parse(err.responseBody ?? "{}");
237
+ consentUrl = body.consentUrl;
238
+ message = body.message;
239
+ }
240
+ catch {
241
+ // ignore parse errors
242
+ }
243
+ return {
244
+ status: code === "app_consent_required"
245
+ ? "required"
246
+ : code === "app_consent_pending"
247
+ ? "pending"
248
+ : "denied",
249
+ message: message ?? err.message,
250
+ consentUrl,
251
+ };
252
+ }
253
+ }
254
+ // Non-consent errors — rethrow
255
+ throw err;
256
+ }
257
+ }
258
+ // ── Health Check ──────────────────────────────────────────────
259
+ /** Check gateway connectivity via the echo endpoint. */
260
+ async checkHealth() {
261
+ try {
262
+ const echo = await this.gateway.echo();
263
+ return {
264
+ connected: true,
265
+ gatewayUrl: this.config.gatewayUrl,
266
+ serverTime: echo.utc,
267
+ };
268
+ }
269
+ catch (err) {
270
+ return {
271
+ connected: false,
272
+ gatewayUrl: this.config.gatewayUrl,
273
+ error: err instanceof Error ? err.message : String(err),
274
+ };
275
+ }
276
+ }
277
+ // ── Exchange Status ───────────────────────────────────────────
278
+ /**
279
+ * Get the status of an exchange by request ID.
280
+ * Returns the exchange state (Pending, Decided, Expired, Withdrawn).
281
+ */
282
+ async getExchangeStatus(requestId) {
283
+ const resp = await this.gateway.getExchangeStatus(requestId);
284
+ return {
285
+ state: resp.body?.state ?? "unknown",
286
+ createdAt: resp.body?.createdAt,
287
+ expiresAt: resp.body?.expiresAt,
288
+ };
289
+ }
290
+ // ── Presence Heartbeat ────────────────────────────────────────
291
+ /** Start periodic presence heartbeat so the mobile app shows enforcer online. */
292
+ startHeartbeat() {
293
+ if (this.heartbeatTimer)
294
+ return;
295
+ const sendHeartbeat = () => {
296
+ this.gateway.sendHeartbeat({
297
+ enforcerId: this.config.enforcerId,
298
+ workspaceName: this.config.workspaceName,
299
+ enforcerLabel: "OpenClaw",
300
+ }).catch(() => {
301
+ // Heartbeat is best-effort
302
+ });
303
+ };
304
+ // Send immediately on start, then at intervals
305
+ sendHeartbeat();
306
+ this.heartbeatTimer = setInterval(sendHeartbeat, HEARTBEAT_INTERVAL_MS);
307
+ // Prevent the timer from blocking Node.js exit
308
+ if (this.heartbeatTimer && typeof this.heartbeatTimer === "object" && "unref" in this.heartbeatTimer) {
309
+ this.heartbeatTimer.unref();
310
+ }
311
+ }
312
+ // ── Pairing ───────────────────────────────────────────────────
313
+ /**
314
+ * Claim a pre-generated pairing code with proper X25519 ECDH key exchange.
315
+ * Polls until pairing is completed, derives shared encryption key,
316
+ * and persists the pairing state to disk.
317
+ *
318
+ * @param code The pre-generated pairing code.
319
+ * @returns Routing token and encryption key for artifact encryption.
320
+ */
321
+ async claimPairing(code) {
322
+ const { randomUUID } = await import("node:crypto");
323
+ const deviceId = `openclaw-${randomUUID().slice(0, 8)}`;
324
+ // Generate X25519 keypair for ECDH key exchange
325
+ const keyPair = generateX25519KeyPair();
326
+ this.log(`Claiming pairing code: ${code}`);
327
+ const claim = await this.gateway.claimPairing({
328
+ pairingCode: code,
329
+ deviceId,
330
+ enforcerId: this.config.enforcerId,
331
+ enforcerLabel: "OpenClaw",
332
+ workspaceName: this.config.workspaceName,
333
+ x25519PublicKey: keyPair.publicKey,
334
+ });
335
+ this.log(`Pairing initiated: nonce=${claim.pairingNonce}, expires=${claim.expiresAt}`);
336
+ // Poll for completion
337
+ const deadline = Date.now() + 10 * 60 * 1000; // 10 min
338
+ const pollInterval = 3000;
339
+ while (Date.now() < deadline) {
340
+ await new Promise((r) => setTimeout(r, pollInterval));
341
+ const status = await this.gateway.getPairingStatus(claim.pairingNonce);
342
+ if (status.state === "Expired") {
343
+ throw new Error("Pairing code expired. Generate a new one and try again.");
344
+ }
345
+ if (status.state !== "Completed") {
346
+ continue;
347
+ }
348
+ if (!status.routingToken || !status.responseJson) {
349
+ throw new Error("Invalid pairing completion response — missing routing token or response data");
350
+ }
351
+ // Parse the pairing response to get the approver's X25519 public key
352
+ const response = JSON.parse(status.responseJson);
353
+ // Derive shared encryption key via X25519 ECDH
354
+ if (!response.x25519PublicKey) {
355
+ throw new Error("Pairing response missing x25519PublicKey — cannot derive encryption key");
356
+ }
357
+ const encryptionKey = deriveSharedKey(keyPair.privateKey, response.x25519PublicKey);
358
+ // Store the approver's Ed25519 signing key for decision verification
359
+ const pairedKeys = {};
360
+ if (response.signerKeyId && response.publicKey) {
361
+ pairedKeys[response.signerKeyId] = {
362
+ publicKey: response.publicKey,
363
+ deviceId: response.deviceId ?? "mobile",
364
+ };
365
+ }
366
+ // Update runtime state
367
+ this.config.routingToken = status.routingToken;
368
+ this.config.encryptionKey = encryptionKey;
369
+ this.pairedKeys = pairedKeys;
370
+ // Persist state to disk
371
+ await savePairingState({
372
+ routingToken: status.routingToken,
373
+ encryptionKey,
374
+ pairedKeys,
375
+ pairedAt: new Date().toISOString(),
376
+ pairingNonce: claim.pairingNonce,
377
+ });
378
+ this.log("Pairing completed — keys derived and persisted");
379
+ return {
380
+ routingToken: status.routingToken,
381
+ encryptionKey,
382
+ pairingNonce: claim.pairingNonce,
383
+ };
384
+ }
385
+ throw new Error("Pairing timed out (10 minutes). Try again.");
386
+ }
387
+ // ── Helpers ───────────────────────────────────────────────────
388
+ parseDecision(envelope, requestId) {
389
+ const body = envelope.body;
390
+ if (!body)
391
+ return null;
392
+ const dec = String(body.decision ?? "").toLowerCase();
393
+ if (dec !== "approve" && dec !== "reject")
394
+ return null;
395
+ // Verify decision signature if signing material is present
396
+ const signerKeyId = body.signerKeyId;
397
+ const signature = body.signature;
398
+ const nonce = body.nonce;
399
+ const artifactHash = body.artifactHash;
400
+ if (signature && signerKeyId && nonce && artifactHash) {
401
+ const verified = verifyDecisionSignature(this.pairedKeys, artifactHash, dec, nonce, signature, signerKeyId);
402
+ if (!verified) {
403
+ this.log(`Decision signature verification failed for signerKeyId=${signerKeyId}`);
404
+ return null; // Reject unverifiable decisions
405
+ }
406
+ this.log("Decision signature verified ✓");
407
+ }
408
+ return {
409
+ decision: dec === "approve" ? "approved" : "rejected",
410
+ reason: body.reason,
411
+ requestId,
412
+ };
413
+ }
414
+ async handleSubmitError(err) {
415
+ if (err instanceof AirlockGatewayError) {
416
+ // 403 — pairing revoked or access denied
417
+ if (err.statusCode === 403) {
418
+ const code = err.errorCode ?? "";
419
+ if (code === "pairing_revoked" || code === "no_approver") {
420
+ await this.clearPairingState();
421
+ }
422
+ return {
423
+ decision: "rejected",
424
+ reason: `Access denied: ${code || "forbidden"} — run 'airlock pair' again`,
425
+ requestId: "",
426
+ };
427
+ }
428
+ // 422 — validation error (may include no_approver)
429
+ if (err.statusCode === 422) {
430
+ const code = err.errorCode ?? "";
431
+ if (code === "no_approver") {
432
+ await this.clearPairingState();
433
+ return {
434
+ decision: "rejected",
435
+ reason: "No approver available — pairing may be stale. Run 'airlock pair' again.",
436
+ requestId: "",
437
+ };
438
+ }
439
+ return {
440
+ decision: "rejected",
441
+ reason: `Validation error: ${err.message}`,
442
+ requestId: "",
443
+ };
444
+ }
445
+ // 429 — quota exceeded
446
+ if (err.statusCode === 429) {
447
+ return {
448
+ decision: "rejected",
449
+ reason: "Quota exceeded — try again later",
450
+ requestId: "",
451
+ };
452
+ }
453
+ }
454
+ const msg = err instanceof Error ? err.message : String(err);
455
+ return {
456
+ decision: "rejected",
457
+ reason: `Gateway error: ${msg}`,
458
+ requestId: "",
459
+ };
460
+ }
461
+ /** Clear pairing state from memory and disk. */
462
+ async clearPairingState() {
463
+ this.config.routingToken = undefined;
464
+ this.config.encryptionKey = undefined;
465
+ this.pairedKeys = {};
466
+ await clearPairingState().catch(() => { });
467
+ this.log("Pairing state cleared (revoked or stale)");
468
+ }
469
+ }
470
+ /** Factory function to create an AirlockClient from validated config. */
471
+ export function createAirlockClient(config, logger) {
472
+ return new AirlockClient(config, logger);
473
+ }
474
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Command: airlock-status — Diagnostic slash command.
3
+ *
4
+ * Shows the current Airlock configuration, gateway connectivity,
5
+ * pairing status, and enforcement settings.
6
+ */
7
+ import type { AirlockClient } from "../client.js";
8
+ import type { AirlockConfig } from "../config.js";
9
+ /** Command definition for OpenClaw registration. */
10
+ export declare const airlockStatusCommandDef: {
11
+ name: string;
12
+ description: string;
13
+ };
14
+ /**
15
+ * Register the /airlock-status command with the OpenClaw plugin API.
16
+ */
17
+ export declare function registerAirlockStatusCommand(api: {
18
+ registerCommand: (def: unknown, handler: () => Promise<string>) => void;
19
+ }, client: AirlockClient, config: AirlockConfig): void;
20
+ //# sourceMappingURL=airlockStatus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"airlockStatus.d.ts","sourceRoot":"","sources":["../../src/commands/airlockStatus.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAGlD,oDAAoD;AACpD,eAAO,MAAM,uBAAuB;;;CAGnC,CAAC;AAEF;;GAEG;AACH,wBAAgB,4BAA4B,CAC1C,GAAG,EAAE;IAAE,eAAe,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,KAAK,IAAI,CAAA;CAAE,EAChF,MAAM,EAAE,aAAa,EACrB,MAAM,EAAE,aAAa,GACpB,IAAI,CAiCN"}