aquaman-plugin 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.ts +38 -3
- package/package.json +1 -1
- package/src/http-interceptor.ts +38 -1
- package/src/proxy-manager.ts +8 -0
package/index.ts
CHANGED
|
@@ -22,6 +22,7 @@ import { HttpInterceptor, createHttpInterceptor } from "./src/http-interceptor.j
|
|
|
22
22
|
|
|
23
23
|
let proxyProcess: ChildProcess | null = null;
|
|
24
24
|
let httpInterceptor: HttpInterceptor | null = null;
|
|
25
|
+
let clientToken: string | null = null;
|
|
25
26
|
const proxyPort = 8081;
|
|
26
27
|
const services = ["anthropic", "openai"];
|
|
27
28
|
|
|
@@ -33,6 +34,13 @@ function getExternalProxyUrl(): string | null {
|
|
|
33
34
|
return process.env.AQUAMAN_PROXY_URL || null;
|
|
34
35
|
}
|
|
35
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Get external client token from environment (for Docker two-container mode).
|
|
39
|
+
*/
|
|
40
|
+
function getExternalClientToken(): string | null {
|
|
41
|
+
return process.env.AQUAMAN_CLIENT_TOKEN || null;
|
|
42
|
+
}
|
|
43
|
+
|
|
36
44
|
/**
|
|
37
45
|
* Check if aquaman CLI is installed
|
|
38
46
|
*/
|
|
@@ -62,11 +70,33 @@ async function startProxy(port: number, log: OpenClawPluginApi["logger"]): Promi
|
|
|
62
70
|
}
|
|
63
71
|
|
|
64
72
|
let started = false;
|
|
73
|
+
let stdoutBuffer = "";
|
|
65
74
|
|
|
66
75
|
proxyProcess.stdout?.on("data", (data: Buffer) => {
|
|
67
|
-
|
|
68
|
-
log.debug(`[aquaman] ${
|
|
69
|
-
if (
|
|
76
|
+
stdoutBuffer += data.toString();
|
|
77
|
+
log.debug(`[aquaman] ${data.toString().trim()}`);
|
|
78
|
+
if (started) return;
|
|
79
|
+
|
|
80
|
+
// Try to parse JSON connection info from stdout
|
|
81
|
+
const lines = stdoutBuffer.split("\n");
|
|
82
|
+
for (const line of lines) {
|
|
83
|
+
const trimmed = line.trim();
|
|
84
|
+
if (!trimmed.startsWith("{")) continue;
|
|
85
|
+
try {
|
|
86
|
+
const info = JSON.parse(trimmed);
|
|
87
|
+
if (info.ready === true) {
|
|
88
|
+
started = true;
|
|
89
|
+
clientToken = info.token || null;
|
|
90
|
+
resolve(true);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
} catch {
|
|
94
|
+
// Not valid JSON yet, keep buffering
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Fall back to string matching for backward compat
|
|
99
|
+
if (stdoutBuffer.includes("listening") || stdoutBuffer.includes("started")) {
|
|
70
100
|
started = true;
|
|
71
101
|
resolve(true);
|
|
72
102
|
}
|
|
@@ -111,6 +141,7 @@ function stopProxy(): void {
|
|
|
111
141
|
proxyProcess.kill("SIGTERM");
|
|
112
142
|
proxyProcess = null;
|
|
113
143
|
}
|
|
144
|
+
clientToken = null;
|
|
114
145
|
}
|
|
115
146
|
|
|
116
147
|
/**
|
|
@@ -124,6 +155,8 @@ function activateHttpInterceptor(log: OpenClawPluginApi["logger"]): void {
|
|
|
124
155
|
['api.anthropic.com', 'anthropic'],
|
|
125
156
|
['api.openai.com', 'openai'],
|
|
126
157
|
['api.github.com', 'github'],
|
|
158
|
+
['api.x.ai', 'xai'],
|
|
159
|
+
['gateway.ai.cloudflare.com', 'cloudflare-ai'],
|
|
127
160
|
// Channel APIs
|
|
128
161
|
['slack.com', 'slack'],
|
|
129
162
|
['*.slack.com', 'slack'],
|
|
@@ -152,6 +185,7 @@ function activateHttpInterceptor(log: OpenClawPluginApi["logger"]): void {
|
|
|
152
185
|
httpInterceptor = createHttpInterceptor({
|
|
153
186
|
proxyBaseUrl: baseUrl,
|
|
154
187
|
hostMap,
|
|
188
|
+
clientToken: clientToken || undefined,
|
|
155
189
|
log: (msg) => log.info(msg),
|
|
156
190
|
});
|
|
157
191
|
|
|
@@ -243,6 +277,7 @@ export default function register(api: OpenClawPluginApi): void {
|
|
|
243
277
|
// External proxy mode (Docker two-container architecture)
|
|
244
278
|
if (externalUrl) {
|
|
245
279
|
api.logger.info(`External proxy mode: ${externalUrl}`);
|
|
280
|
+
clientToken = getExternalClientToken();
|
|
246
281
|
configureEnvironment(api.logger);
|
|
247
282
|
|
|
248
283
|
if (api.registerLifecycle) {
|
package/package.json
CHANGED
package/src/http-interceptor.ts
CHANGED
|
@@ -15,6 +15,8 @@ export interface HttpInterceptorOptions {
|
|
|
15
15
|
proxyBaseUrl: string;
|
|
16
16
|
/** Map of hostname (or *.domain wildcard) → service name */
|
|
17
17
|
hostMap: Map<string, string>;
|
|
18
|
+
/** Client authentication token for the proxy */
|
|
19
|
+
clientToken?: string;
|
|
18
20
|
/** Optional logger */
|
|
19
21
|
log?: (msg: string) => void;
|
|
20
22
|
}
|
|
@@ -23,6 +25,7 @@ export class HttpInterceptor {
|
|
|
23
25
|
private proxyBaseUrl: string;
|
|
24
26
|
private proxyHost: string;
|
|
25
27
|
private hostMap: Map<string, string>;
|
|
28
|
+
private clientToken: string | null;
|
|
26
29
|
private originalFetch: typeof globalThis.fetch | null = null;
|
|
27
30
|
private active = false;
|
|
28
31
|
private log: (msg: string) => void;
|
|
@@ -30,6 +33,7 @@ export class HttpInterceptor {
|
|
|
30
33
|
constructor(options: HttpInterceptorOptions) {
|
|
31
34
|
this.proxyBaseUrl = options.proxyBaseUrl.replace(/\/$/, '');
|
|
32
35
|
this.hostMap = options.hostMap;
|
|
36
|
+
this.clientToken = options.clientToken || null;
|
|
33
37
|
this.log = options.log || (() => {});
|
|
34
38
|
|
|
35
39
|
// Extract proxy hostname to avoid intercepting requests to the proxy itself
|
|
@@ -51,9 +55,11 @@ export class HttpInterceptor {
|
|
|
51
55
|
const origFetch = this.originalFetch;
|
|
52
56
|
const proxyBase = this.proxyBaseUrl;
|
|
53
57
|
const proxyHostname = this.proxyHost;
|
|
58
|
+
const token = this.clientToken;
|
|
54
59
|
const matchHost = this.matchHost.bind(this);
|
|
55
60
|
const extractUrl = this.extractUrl.bind(this);
|
|
56
61
|
const stripAuthHeaders = this.stripAuthHeaders.bind(this);
|
|
62
|
+
const injectToken = this.injectTokenHeader.bind(this);
|
|
57
63
|
const logFn = this.log;
|
|
58
64
|
|
|
59
65
|
globalThis.fetch = (
|
|
@@ -65,8 +71,12 @@ export class HttpInterceptor {
|
|
|
65
71
|
return origFetch.call(globalThis, input, init);
|
|
66
72
|
}
|
|
67
73
|
|
|
68
|
-
//
|
|
74
|
+
// Requests to the proxy itself (SDK traffic via env vars) — inject token, pass through
|
|
69
75
|
if (url.hostname === proxyHostname || url.hostname === 'localhost') {
|
|
76
|
+
if (token) {
|
|
77
|
+
const tokenInit = injectToken(init, token);
|
|
78
|
+
return origFetch.call(globalThis, input, tokenInit);
|
|
79
|
+
}
|
|
70
80
|
return origFetch.call(globalThis, input, init);
|
|
71
81
|
}
|
|
72
82
|
|
|
@@ -86,6 +96,11 @@ export class HttpInterceptor {
|
|
|
86
96
|
newInit = { ...init, headers: stripped };
|
|
87
97
|
}
|
|
88
98
|
|
|
99
|
+
// Inject client token for proxy authentication
|
|
100
|
+
if (token) {
|
|
101
|
+
newInit = injectToken(newInit, token);
|
|
102
|
+
}
|
|
103
|
+
|
|
89
104
|
return origFetch.call(globalThis, proxyUrl, newInit);
|
|
90
105
|
};
|
|
91
106
|
|
|
@@ -138,6 +153,28 @@ export class HttpInterceptor {
|
|
|
138
153
|
return null;
|
|
139
154
|
}
|
|
140
155
|
|
|
156
|
+
private injectTokenHeader(init: RequestInit | undefined, token: string): RequestInit {
|
|
157
|
+
const base = init || {};
|
|
158
|
+
const headers = base.headers;
|
|
159
|
+
|
|
160
|
+
if (!headers) {
|
|
161
|
+
return { ...base, headers: { 'x-aquaman-token': token } };
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (headers instanceof Headers) {
|
|
165
|
+
const h = new Headers(headers);
|
|
166
|
+
h.set('x-aquaman-token', token);
|
|
167
|
+
return { ...base, headers: h };
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (Array.isArray(headers)) {
|
|
171
|
+
return { ...base, headers: [...headers, ['x-aquaman-token', token]] };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Plain object
|
|
175
|
+
return { ...base, headers: { ...headers, 'x-aquaman-token': token } };
|
|
176
|
+
}
|
|
177
|
+
|
|
141
178
|
private stripAuthHeaders(headers: HeadersInit): HeadersInit {
|
|
142
179
|
if (headers instanceof Headers) {
|
|
143
180
|
const h = new Headers(headers);
|
package/src/proxy-manager.ts
CHANGED
|
@@ -17,6 +17,7 @@ export interface ProxyConnectionInfo {
|
|
|
17
17
|
baseUrl: string;
|
|
18
18
|
services: string[];
|
|
19
19
|
backend: string;
|
|
20
|
+
token?: string;
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
export interface ProxyManagerOptions {
|
|
@@ -193,6 +194,13 @@ export class ProxyManager {
|
|
|
193
194
|
return this.connectionInfo;
|
|
194
195
|
}
|
|
195
196
|
|
|
197
|
+
/**
|
|
198
|
+
* Get client authentication token
|
|
199
|
+
*/
|
|
200
|
+
getClientToken(): string | null {
|
|
201
|
+
return this.connectionInfo?.token || null;
|
|
202
|
+
}
|
|
203
|
+
|
|
196
204
|
/**
|
|
197
205
|
* Get base URL for a service
|
|
198
206
|
*/
|