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.
- package/README.md +406 -0
- package/dist/cli/register.d.ts +22 -0
- package/dist/cli/register.d.ts.map +1 -0
- package/dist/cli/register.js +229 -0
- package/dist/cli/register.js.map +1 -0
- package/dist/client.d.ts +136 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +474 -0
- package/dist/client.js.map +1 -0
- package/dist/commands/airlockStatus.d.ts +20 -0
- package/dist/commands/airlockStatus.d.ts.map +1 -0
- package/dist/commands/airlockStatus.js +48 -0
- package/dist/commands/airlockStatus.js.map +1 -0
- package/dist/config.d.ts +48 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +104 -0
- package/dist/config.js.map +1 -0
- package/dist/crypto.d.ts +41 -0
- package/dist/crypto.d.ts.map +1 -0
- package/dist/crypto.js +112 -0
- package/dist/crypto.js.map +1 -0
- package/dist/hooks/beforeTool.d.ts +46 -0
- package/dist/hooks/beforeTool.d.ts.map +1 -0
- package/dist/hooks/beforeTool.js +112 -0
- package/dist/hooks/beforeTool.js.map +1 -0
- package/dist/index.d.ts +50 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +88 -0
- package/dist/index.js.map +1 -0
- package/dist/state.d.ts +44 -0
- package/dist/state.d.ts.map +1 -0
- package/dist/state.js +79 -0
- package/dist/state.js.map +1 -0
- package/dist/tools/checkStatus.d.ts +42 -0
- package/dist/tools/checkStatus.d.ts.map +1 -0
- package/dist/tools/checkStatus.js +67 -0
- package/dist/tools/checkStatus.js.map +1 -0
- package/dist/tools/requestApproval.d.ts +63 -0
- package/dist/tools/requestApproval.d.ts.map +1 -0
- package/dist/tools/requestApproval.js +85 -0
- package/dist/tools/requestApproval.js.map +1 -0
- package/openclaw.plugin.json +69 -0
- package/package.json +61 -0
package/dist/client.d.ts
ADDED
|
@@ -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"}
|