arkitek-relay-skill 1.0.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ArkiTek
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,236 @@
1
+ # arkitek-relay-skill
2
+
3
+ Connect your self-hosted [OpenClaw](https://github.com/openclaw) agent to [ArkiTek](https://arkitek.dev) — a modern web UI for interacting with AI agents. This skill opens a persistent, outbound-only SSE connection from your agent to ArkiTek's cloud relay, so users can chat with your agent through ArkiTek's interface. No tunnels, public URLs, or open ports required.
4
+
5
+ ## How It Works
6
+
7
+ ```
8
+ ArkiTek Web UI ←→ ArkiTek Cloud ←——SSE—— Your Agent (this skill)
9
+ (user) (relay) ——POST→
10
+ ```
11
+
12
+ 1. Your agent connects **outbound** to ArkiTek over HTTPS
13
+ 2. Messages from the ArkiTek UI are delivered to your agent via SSE (Server-Sent Events)
14
+ 3. Your agent processes each message and sends the response back via HTTPS POST
15
+ 4. ArkiTek delivers the response to the user's browser in real time
16
+
17
+ The agent initiates all connections. Nothing is exposed on your network.
18
+
19
+ ## Prerequisites
20
+
21
+ - **Node.js 18+** (uses native `fetch` and `ReadableStream`)
22
+ - An **OpenClaw agent** (or any agent framework — the handler interface is generic)
23
+ - An **ArkiTek account** at [arkitek.dev](https://arkitek.dev)
24
+
25
+ ## Installation
26
+
27
+ ```bash
28
+ npm install arkitek-relay-skill
29
+ ```
30
+
31
+ Or clone and build from source:
32
+
33
+ ```bash
34
+ git clone https://github.com/raleighgardner16-source/arkitek-relay-skill.git
35
+ cd arkitek-relay-skill
36
+ npm install
37
+ npm run build
38
+ ```
39
+
40
+ ## Getting Your API Key
41
+
42
+ 1. Log in to [arkitek.dev](https://arkitek.dev)
43
+ 2. Navigate to **Agents** → **Add Agent**
44
+ 3. Give your agent a name and description
45
+ 4. Click **Create** — you'll receive a private API key (starts with `ak_`)
46
+ 5. Copy the key immediately — it won't be shown again
47
+
48
+ > **Security**: Your API key is a secret. Never commit it to version control, share it publicly, or log it. Use environment variables.
49
+
50
+ ## Configuration
51
+
52
+ Set your API key as an environment variable:
53
+
54
+ ```bash
55
+ export ARKITEK_API_KEY=ak_your_api_key_here
56
+ ```
57
+
58
+ Or use a `.env` file (see `.env.example`):
59
+
60
+ ```
61
+ ARKITEK_API_KEY=ak_your_api_key_here
62
+ ARKITEK_AUTO_RECONNECT=true
63
+ ```
64
+
65
+ ### Configuration Options
66
+
67
+ | Variable | Required | Default | Description |
68
+ |----------|----------|---------|-------------|
69
+ | `ARKITEK_API_KEY` | Yes | — | Your agent's private API key from ArkiTek |
70
+ | `ARKITEK_AUTO_RECONNECT` | No | `true` | Auto-reconnect on network errors |
71
+
72
+ ## Usage
73
+
74
+ ### As an imported module
75
+
76
+ ```typescript
77
+ import { createArkitekRelay } from "arkitek-relay-skill";
78
+ import type { IncomingMessage } from "arkitek-relay-skill";
79
+
80
+ // Define your message handler
81
+ async function handleMessage(message: IncomingMessage): Promise<string> {
82
+ console.log(`User said: ${message.content}`);
83
+
84
+ // Pass to your OpenClaw agent, LLM, or any processing logic
85
+ const response = await yourAgent.process(message.content, message.images);
86
+
87
+ return response;
88
+ }
89
+
90
+ // Create and connect the relay
91
+ const relay = createArkitekRelay(
92
+ {
93
+ apiKey: process.env.ARKITEK_API_KEY!,
94
+ autoReconnect: true,
95
+ },
96
+ handleMessage,
97
+ {
98
+ onConnect: (agentId) => console.log(`Connected as agent ${agentId}`),
99
+ onDisconnect: (reason) => console.log(`Disconnected: ${reason}`),
100
+ onError: (err) => console.error(`Error: ${err.message}`),
101
+ }
102
+ );
103
+
104
+ await relay.connect();
105
+ ```
106
+
107
+ ### As a standalone script (echo mode)
108
+
109
+ ```bash
110
+ ARKITEK_API_KEY=ak_your_key node dist/index.js
111
+ ```
112
+
113
+ This starts the skill with a built-in echo handler — useful for testing that your connection works.
114
+
115
+ ### With the RelayClient class directly
116
+
117
+ ```typescript
118
+ import { RelayClient } from "arkitek-relay-skill";
119
+
120
+ const client = new RelayClient(
121
+ { apiKey: process.env.ARKITEK_API_KEY! },
122
+ async (message) => {
123
+ // Your logic here
124
+ return `Processed: ${message.content}`;
125
+ }
126
+ );
127
+
128
+ await client.connect();
129
+
130
+ // Later...
131
+ client.disconnect();
132
+ ```
133
+
134
+ ## Council of LLMs (Optional)
135
+
136
+ ArkiTek's Council feature lets you query multiple LLMs simultaneously and get aggregated responses. This requires an active ArkiTek subscription.
137
+
138
+ ```typescript
139
+ import { queryCouncil } from "arkitek-relay-skill";
140
+
141
+ const result = await queryCouncil(
142
+ { apiKey: process.env.ARKITEK_API_KEY! },
143
+ "What are the security implications of SSE vs WebSockets?",
144
+ ["gpt-4", "claude-3"] // optional — omit to use defaults
145
+ );
146
+
147
+ for (const r of result.responses) {
148
+ console.log(`${r.modelId}: ${r.response}`);
149
+ }
150
+ ```
151
+
152
+ **Limits**: 10 requests per minute, prompt max 100KB, max 8 models per request.
153
+
154
+ ## Message Format
155
+
156
+ ### Incoming messages (from ArkiTek)
157
+
158
+ ```typescript
159
+ interface IncomingMessage {
160
+ messageId: string; // Unique ID — must be included in your response
161
+ content: string; // The user's message text
162
+ images?: string[]; // Optional base64-encoded images
163
+ userId: string; // The ArkiTek user's ID
164
+ timestamp: string; // ISO 8601 timestamp
165
+ }
166
+ ```
167
+
168
+ ### Your handler's return value
169
+
170
+ Return a `string` — the agent's response text. The skill handles sending it back to ArkiTek with the correct `messageId`.
171
+
172
+ ## Security
173
+
174
+ This skill is designed with security as a top priority:
175
+
176
+ - **Outbound-only connections** — your agent never exposes any ports or URLs
177
+ - **TLS enforced** — the skill refuses to start if `NODE_TLS_REJECT_UNAUTHORIZED=0` is detected
178
+ - **API key validation** — keys are validated against the expected format before any network request
179
+ - **Key masking** — API keys are never logged; only `ak_****...last4` appears in output
180
+ - **No tunnels** — no ngrok, no Cloudflare tunnels, no port forwarding
181
+ - **Auth on every request** — both SSE and POST endpoints require the Bearer token
182
+ - **Message integrity** — responses must include the matching `messageId` or they are rejected
183
+
184
+ ## Troubleshooting
185
+
186
+ ### "API key invalid or revoked"
187
+
188
+ - Double-check your `ARKITEK_API_KEY` value
189
+ - Verify the key in ArkiTek's dashboard — it may have been revoked
190
+ - Keys start with `ak_` and are exactly 67 characters
191
+ - If you rotated your key, the old key has a 1-hour grace period
192
+
193
+ ### "NODE_TLS_REJECT_UNAUTHORIZED=0 detected"
194
+
195
+ - Remove `NODE_TLS_REJECT_UNAUTHORIZED=0` from your environment
196
+ - This setting disables TLS certificate verification, which would allow man-in-the-middle attacks
197
+ - If you're behind a corporate proxy, configure `NODE_EXTRA_CA_CERTS` instead
198
+
199
+ ### Connection keeps dropping
200
+
201
+ - Check your network connection and firewall rules
202
+ - Ensure outbound HTTPS (port 443) to `arkitek.dev` is allowed
203
+ - The skill auto-reconnects with exponential backoff (1s → 30s max)
204
+ - Messages are queued server-side for up to 5 minutes during disconnections
205
+
206
+ ### "Response not delivered"
207
+
208
+ - Ensure you're returning from your handler within 1 hour (server timeout)
209
+ - The `messageId` in your response must exactly match the incoming message
210
+ - Response content must be under 500KB
211
+
212
+ ### No messages arriving
213
+
214
+ - Confirm your agent shows as "Connected" in the ArkiTek dashboard
215
+ - Try sending a test message from the ArkiTek web UI
216
+ - Check that your handler isn't throwing errors (they're caught and logged)
217
+
218
+ ## Development
219
+
220
+ ```bash
221
+ # Install dependencies
222
+ npm install
223
+
224
+ # Build
225
+ npm run build
226
+
227
+ # Run tests
228
+ npm test
229
+
230
+ # Watch mode (rebuilds on changes)
231
+ npm run dev
232
+ ```
233
+
234
+ ## License
235
+
236
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,3 @@
1
+ import { type ArkitekConfig, type CouncilResponse } from "./types.js";
2
+ export declare function queryCouncil(config: ArkitekConfig, prompt: string, models?: string[]): Promise<CouncilResponse>;
3
+ //# sourceMappingURL=council.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"council.d.ts","sourceRoot":"","sources":["../src/council.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,aAAa,EAElB,KAAK,eAAe,EAMrB,MAAM,YAAY,CAAC;AAGpB,wBAAsB,YAAY,CAChC,MAAM,EAAE,aAAa,EACrB,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,MAAM,EAAE,GAChB,OAAO,CAAC,eAAe,CAAC,CA0E1B"}
@@ -0,0 +1,57 @@
1
+ import { DEFAULT_BASE_URL, LOG_PREFIX, MAX_COUNCIL_MODELS, MAX_COUNCIL_PROMPT_SIZE, COUNCIL_TIMEOUT_MS, } from "./types.js";
2
+ import { validateApiKey, checkTlsSafety, warnIfNotHttps } from "./validation.js";
3
+ export async function queryCouncil(config, prompt, models) {
4
+ validateApiKey(config.apiKey);
5
+ checkTlsSafety();
6
+ const rawBaseUrl = config.baseUrl ?? DEFAULT_BASE_URL;
7
+ try {
8
+ const parsed = new URL(rawBaseUrl);
9
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
10
+ throw new Error("Only http:// and https:// protocols are supported");
11
+ }
12
+ }
13
+ catch (err) {
14
+ if (err instanceof TypeError) {
15
+ throw new Error(`Invalid baseUrl "${rawBaseUrl}": not a valid URL`);
16
+ }
17
+ throw err;
18
+ }
19
+ const baseUrl = rawBaseUrl.replace(/\/+$/, "");
20
+ warnIfNotHttps(baseUrl);
21
+ const url = `${baseUrl}/council`;
22
+ const promptBytes = new TextEncoder().encode(prompt).length;
23
+ if (promptBytes > MAX_COUNCIL_PROMPT_SIZE) {
24
+ throw new Error(`Council prompt exceeds 100KB limit (${promptBytes} bytes)`);
25
+ }
26
+ if (models && models.length > MAX_COUNCIL_MODELS) {
27
+ throw new Error(`Council request exceeds ${MAX_COUNCIL_MODELS} model limit (got ${models.length})`);
28
+ }
29
+ const body = { prompt };
30
+ if (models && models.length > 0) {
31
+ body.models = models;
32
+ }
33
+ const response = await fetch(url, {
34
+ method: "POST",
35
+ headers: {
36
+ Authorization: `Bearer ${config.apiKey}`,
37
+ "Content-Type": "application/json",
38
+ },
39
+ body: JSON.stringify(body),
40
+ signal: AbortSignal.timeout(COUNCIL_TIMEOUT_MS),
41
+ });
42
+ if (response.status === 401 || response.status === 403) {
43
+ throw new Error(`${LOG_PREFIX} Council request authentication failed (HTTP ${response.status})`);
44
+ }
45
+ if (response.status === 429) {
46
+ throw new Error(`${LOG_PREFIX} Council rate limit exceeded (10 requests per minute). Try again later.`);
47
+ }
48
+ if (response.status === 402) {
49
+ throw new Error(`${LOG_PREFIX} Council requires an active ArkiTek subscription.`);
50
+ }
51
+ if (!response.ok) {
52
+ const errorText = await response.text().catch(() => "");
53
+ throw new Error(`${LOG_PREFIX} Council request failed: HTTP ${response.status} — ${errorText}`);
54
+ }
55
+ return (await response.json());
56
+ }
57
+ //# sourceMappingURL=council.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"council.js","sourceRoot":"","sources":["../src/council.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export { type ArkitekConfig, type ArkitekRelayEvents, type IncomingMessage, type MessageHandler, type ConnectionState, type ConnectedEvent, type PingEvent, type RespondPayload, type RespondResponse, type CouncilRequest, type CouncilResponse, type CouncilModelResponse, } from "./types.js";
2
+ export { RelayClient } from "./relay.js";
3
+ export { maskKey, validateApiKey, checkTlsSafety, warnIfNotHttps } from "./validation.js";
4
+ export { queryCouncil } from "./council.js";
5
+ import { type ArkitekConfig, type ArkitekRelayEvents, type MessageHandler } from "./types.js";
6
+ import { RelayClient } from "./relay.js";
7
+ export declare function createArkitekRelay(config: ArkitekConfig, handler: MessageHandler, events?: ArkitekRelayEvents): RelayClient;
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,SAAS,EACd,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,oBAAoB,GAC1B,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAC1F,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,kBAAkB,EAAE,KAAK,cAAc,EAAc,MAAM,YAAY,CAAC;AAC1G,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAIzC,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,cAAc,EACvB,MAAM,CAAC,EAAE,kBAAkB,GAC1B,WAAW,CAEb"}
package/dist/index.js ADDED
@@ -0,0 +1,35 @@
1
+ export { RelayClient } from "./relay.js";
2
+ export { maskKey, validateApiKey, checkTlsSafety, warnIfNotHttps } from "./validation.js";
3
+ export { queryCouncil } from "./council.js";
4
+ import { LOG_PREFIX } from "./types.js";
5
+ import { RelayClient } from "./relay.js";
6
+ import { fileURLToPath } from "node:url";
7
+ import { resolve } from "node:path";
8
+ export function createArkitekRelay(config, handler, events) {
9
+ return new RelayClient(config, handler, events);
10
+ }
11
+ const isMainModule = typeof process !== "undefined" &&
12
+ process.argv[1] &&
13
+ fileURLToPath(import.meta.url) === resolve(process.argv[1]);
14
+ if (isMainModule) {
15
+ const apiKey = process.env.ARKITEK_API_KEY;
16
+ if (!apiKey) {
17
+ console.error(`${LOG_PREFIX} ARKITEK_API_KEY environment variable is required`);
18
+ process.exit(1);
19
+ }
20
+ const autoReconnect = process.env.ARKITEK_AUTO_RECONNECT !== "false";
21
+ const echoHandler = async (message) => {
22
+ console.log(`${LOG_PREFIX} [Echo] Received: ${message.content.slice(0, 100)}`);
23
+ return `Echo: ${message.content}`;
24
+ };
25
+ const relay = createArkitekRelay({ apiKey, autoReconnect }, echoHandler, {
26
+ onConnect: (agentId) => console.log(`${LOG_PREFIX} Agent ${agentId} connected`),
27
+ onDisconnect: (reason) => console.log(`${LOG_PREFIX} Disconnected: ${reason}`),
28
+ onError: (err) => console.error(`${LOG_PREFIX} Error: ${err.message}`),
29
+ });
30
+ relay.connect().catch((err) => {
31
+ console.error(`${LOG_PREFIX} Fatal:`, err);
32
+ process.exit(1);
33
+ });
34
+ }
35
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAC1F,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,OAAO,EAAoE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC1G,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,MAAM,UAAU,kBAAkB,CAChC,MAAqB,EACrB,OAAuB,EACvB,MAA2B;IAE3B,OAAO,IAAI,WAAW,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;AAClD,CAAC;AAED,MAAM,YAAY,GAChB,OAAO,OAAO,KAAK,WAAW;IAC9B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACf,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AAE9D,IAAI,YAAY,EAAE,CAAC;IACjB,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;IAC3C,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,CAAC,KAAK,CAAC,GAAG,UAAU,mDAAmD,CAAC,CAAC;QAChF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,sBAAsB,KAAK,OAAO,CAAC;IAErE,MAAM,WAAW,GAAmB,KAAK,EAAE,OAAO,EAAE,EAAE;QACpD,OAAO,CAAC,GAAG,CAAC,GAAG,UAAU,qBAAqB,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;QAC/E,OAAO,SAAS,OAAO,CAAC,OAAO,EAAE,CAAC;IACpC,CAAC,CAAC;IAEF,MAAM,KAAK,GAAG,kBAAkB,CAC9B,EAAE,MAAM,EAAE,aAAa,EAAE,EACzB,WAAW,EACX;QACE,SAAS,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,UAAU,UAAU,OAAO,YAAY,CAAC;QAC/E,YAAY,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,UAAU,kBAAkB,MAAM,EAAE,CAAC;QAC9E,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,UAAU,WAAW,GAAG,CAAC,OAAO,EAAE,CAAC;KACvE,CACF,CAAC;IAEF,KAAK,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;QACrC,OAAO,CAAC,KAAK,CAAC,GAAG,UAAU,SAAS,EAAE,GAAG,CAAC,CAAC;QAC3C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,37 @@
1
+ import { type ArkitekConfig, type ArkitekRelayEvents, type ConnectionState, type MessageHandler } from "./types.js";
2
+ import { maskKey, validateApiKey, checkTlsSafety, warnIfNotHttps } from "./validation.js";
3
+ export { maskKey, validateApiKey, checkTlsSafety, warnIfNotHttps };
4
+ export declare class RelayClient {
5
+ private readonly config;
6
+ private readonly handler;
7
+ private readonly events;
8
+ private state;
9
+ private abortController;
10
+ private heartbeatTimer;
11
+ private reconnectTimer;
12
+ private reconnectAttempt;
13
+ private activeHandlers;
14
+ private agentId;
15
+ private shutdownRequested;
16
+ private connectResolve;
17
+ private connectReject;
18
+ private readonly boundShutdown;
19
+ constructor(config: ArkitekConfig, handler: MessageHandler, events?: ArkitekRelayEvents);
20
+ getState(): ConnectionState;
21
+ isConnected(): boolean;
22
+ connect(): Promise<void>;
23
+ disconnect(reason?: string): void;
24
+ private registerShutdownHandlers;
25
+ private unregisterShutdownHandlers;
26
+ private cleanup;
27
+ private openStream;
28
+ private consumeStream;
29
+ private handleSSEEvent;
30
+ private processMessage;
31
+ private sendResponse;
32
+ private startHeartbeatMonitor;
33
+ private resetHeartbeatMonitor;
34
+ private scheduleReconnect;
35
+ private emitSafe;
36
+ }
37
+ //# sourceMappingURL=relay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relay.d.ts","sourceRoot":"","sources":["../src/relay.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,kBAAkB,EAEvB,KAAK,eAAe,EAEpB,KAAK,cAAc,EAkBpB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAE1F,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,cAAc,EAAE,CAAC;AAoBnE,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM,CACgB;IACvC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiB;IACzC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAE5C,OAAO,CAAC,KAAK,CAAmC;IAChD,OAAO,CAAC,eAAe,CAAgC;IACvD,OAAO,CAAC,cAAc,CAA8C;IACpE,OAAO,CAAC,cAAc,CAA8C;IACpE,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,OAAO,CAAuB;IACtC,OAAO,CAAC,iBAAiB,CAAS;IAElC,OAAO,CAAC,cAAc,CAA6B;IACnD,OAAO,CAAC,aAAa,CAAuC;IAE5D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAa;gBAGzC,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,cAAc,EACvB,MAAM,GAAE,kBAAuB;IAgCjC,QAAQ,IAAI,eAAe;IAI3B,WAAW,IAAI,OAAO;IAIhB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAyB9B,UAAU,CAAC,MAAM,SAAW,GAAG,IAAI;IAenC,OAAO,CAAC,wBAAwB;IAKhC,OAAO,CAAC,0BAA0B;IAKlC,OAAO,CAAC,OAAO;YAcD,UAAU;YAgEV,aAAa;IAsD3B,OAAO,CAAC,cAAc;IA6CtB,OAAO,CAAC,cAAc;YAyCR,YAAY;IA4D1B,OAAO,CAAC,qBAAqB;IAI7B,OAAO,CAAC,qBAAqB;IAY7B,OAAO,CAAC,iBAAiB;IA4CzB,OAAO,CAAC,QAAQ;CAcjB"}
package/dist/relay.js ADDED
@@ -0,0 +1,429 @@
1
+ import { DEFAULT_BASE_URL, HEARTBEAT_TIMEOUT_MS, LOG_PREFIX, MAX_RESPONSE_SIZE, MAX_RECONNECT_ATTEMPTS, MAX_CONCURRENT_HANDLERS, RESPOND_TIMEOUT_MS, RESPOND_MAX_RETRIES, RESPOND_RETRY_DELAY_MS, RECONNECT_BASE_MS, RECONNECT_MAX_MS, MAX_SSE_BUFFER_SIZE, MAX_IMAGES_PER_MESSAGE, MAX_IMAGE_SIZE, } from "./types.js";
2
+ import { maskKey, validateApiKey, checkTlsSafety, warnIfNotHttps } from "./validation.js";
3
+ export { maskKey, validateApiKey, checkTlsSafety, warnIfNotHttps };
4
+ function log(...args) {
5
+ console.log(LOG_PREFIX, ...args);
6
+ }
7
+ function logError(...args) {
8
+ console.error(LOG_PREFIX, ...args);
9
+ }
10
+ function backoffDelay(attempt) {
11
+ const base = Math.min(RECONNECT_BASE_MS * Math.pow(2, attempt), RECONNECT_MAX_MS);
12
+ const jitter = Math.random() * base * 0.3;
13
+ return base + jitter;
14
+ }
15
+ function sleep(ms) {
16
+ return new Promise((resolve) => setTimeout(resolve, ms));
17
+ }
18
+ export class RelayClient {
19
+ config;
20
+ handler;
21
+ events;
22
+ state = "disconnected";
23
+ abortController = null;
24
+ heartbeatTimer = null;
25
+ reconnectTimer = null;
26
+ reconnectAttempt = 0;
27
+ activeHandlers = 0;
28
+ agentId = null;
29
+ shutdownRequested = false;
30
+ connectResolve = null;
31
+ connectReject = null;
32
+ boundShutdown;
33
+ constructor(config, handler, events = {}) {
34
+ validateApiKey(config.apiKey);
35
+ checkTlsSafety();
36
+ const resolvedUrl = config.baseUrl ?? DEFAULT_BASE_URL;
37
+ try {
38
+ const parsed = new URL(resolvedUrl);
39
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
40
+ throw new Error("Only http:// and https:// protocols are supported");
41
+ }
42
+ }
43
+ catch (err) {
44
+ if (err instanceof TypeError) {
45
+ throw new Error(`Invalid baseUrl "${resolvedUrl}": not a valid URL`);
46
+ }
47
+ throw err;
48
+ }
49
+ this.config = {
50
+ apiKey: config.apiKey,
51
+ baseUrl: resolvedUrl.replace(/\/+$/, ""),
52
+ autoReconnect: config.autoReconnect ?? true,
53
+ };
54
+ warnIfNotHttps(this.config.baseUrl);
55
+ this.handler = handler;
56
+ this.events = events;
57
+ this.boundShutdown = () => {
58
+ this.disconnect("process signal");
59
+ };
60
+ }
61
+ getState() {
62
+ return this.state;
63
+ }
64
+ isConnected() {
65
+ return this.state === "connected";
66
+ }
67
+ async connect() {
68
+ if (this.state === "connected" || this.state === "connecting") {
69
+ log("Already connected or connecting");
70
+ return;
71
+ }
72
+ if (this.state === "auth_failed") {
73
+ logError("Cannot connect — API key was rejected. Please check your key.");
74
+ return;
75
+ }
76
+ this.shutdownRequested = false;
77
+ this.registerShutdownHandlers();
78
+ return new Promise((resolve, reject) => {
79
+ this.connectResolve = resolve;
80
+ this.connectReject = reject;
81
+ this.openStream().catch((err) => {
82
+ const error = err instanceof Error ? err : new Error(String(err));
83
+ this.connectReject?.(error);
84
+ this.connectResolve = null;
85
+ this.connectReject = null;
86
+ });
87
+ });
88
+ }
89
+ disconnect(reason = "manual") {
90
+ this.shutdownRequested = true;
91
+ const hadPendingConnect = this.connectReject !== null;
92
+ this.cleanup();
93
+ this.state = "disconnected";
94
+ this.unregisterShutdownHandlers();
95
+ log(`Disconnected (reason: ${reason})`);
96
+ this.emitSafe("onDisconnect", reason);
97
+ if (hadPendingConnect) {
98
+ this.connectReject?.(new Error(`Disconnected before connection was established (reason: ${reason})`));
99
+ }
100
+ this.connectResolve = null;
101
+ this.connectReject = null;
102
+ }
103
+ registerShutdownHandlers() {
104
+ process.on("SIGINT", this.boundShutdown);
105
+ process.on("SIGTERM", this.boundShutdown);
106
+ }
107
+ unregisterShutdownHandlers() {
108
+ process.removeListener("SIGINT", this.boundShutdown);
109
+ process.removeListener("SIGTERM", this.boundShutdown);
110
+ }
111
+ cleanup() {
112
+ this.abortController?.abort();
113
+ this.abortController = null;
114
+ if (this.heartbeatTimer) {
115
+ clearTimeout(this.heartbeatTimer);
116
+ this.heartbeatTimer = null;
117
+ }
118
+ if (this.reconnectTimer) {
119
+ clearTimeout(this.reconnectTimer);
120
+ this.reconnectTimer = null;
121
+ }
122
+ }
123
+ async openStream() {
124
+ this.state = "connecting";
125
+ this.abortController = new AbortController();
126
+ const url = `${this.config.baseUrl}/stream`;
127
+ log(`Connecting to ${url}...`);
128
+ try {
129
+ const response = await fetch(url, {
130
+ method: "GET",
131
+ headers: {
132
+ Authorization: `Bearer ${this.config.apiKey}`,
133
+ Accept: "text/event-stream",
134
+ "Cache-Control": "no-cache",
135
+ },
136
+ signal: this.abortController.signal,
137
+ });
138
+ if (response.status === 401 || response.status === 403) {
139
+ this.state = "auth_failed";
140
+ const authErr = new Error(`API key invalid or revoked (HTTP ${response.status}). Key: ${maskKey(this.config.apiKey)}. ` +
141
+ "Will NOT retry. Please check your API key in ArkiTek.");
142
+ logError(authErr.message);
143
+ this.cleanup();
144
+ this.unregisterShutdownHandlers();
145
+ this.emitSafe("onError", authErr);
146
+ this.connectReject?.(authErr);
147
+ this.connectResolve = null;
148
+ this.connectReject = null;
149
+ return;
150
+ }
151
+ if (!response.ok) {
152
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
153
+ }
154
+ if (!response.body) {
155
+ throw new Error("Response body is null — SSE stream not available");
156
+ }
157
+ this.reconnectAttempt = 0;
158
+ this.startHeartbeatMonitor();
159
+ await this.consumeStream(response.body);
160
+ }
161
+ catch (err) {
162
+ if (this.shutdownRequested)
163
+ return;
164
+ if (err instanceof Error && err.name === "AbortError")
165
+ return;
166
+ const message = err instanceof Error ? err.message : String(err);
167
+ logError(`Connection error: ${message}`);
168
+ const error = err instanceof Error ? err : new Error(message);
169
+ this.emitSafe("onError", error);
170
+ if (this.connectReject) {
171
+ this.connectReject(error);
172
+ this.connectResolve = null;
173
+ this.connectReject = null;
174
+ }
175
+ this.scheduleReconnect();
176
+ }
177
+ }
178
+ async consumeStream(body) {
179
+ const reader = body.getReader();
180
+ const decoder = new TextDecoder();
181
+ let buffer = "";
182
+ let currentEvent = "";
183
+ let currentData = "";
184
+ try {
185
+ while (true) {
186
+ const { done, value } = await reader.read();
187
+ if (done)
188
+ break;
189
+ buffer += decoder.decode(value, { stream: true });
190
+ if (buffer.length > MAX_SSE_BUFFER_SIZE) {
191
+ logError("SSE buffer exceeded 1MB limit — disconnecting to prevent memory exhaustion");
192
+ reader.cancel();
193
+ this.scheduleReconnect();
194
+ return;
195
+ }
196
+ const lines = buffer.split("\n");
197
+ buffer = lines.pop() ?? "";
198
+ for (const rawLine of lines) {
199
+ const line = rawLine.replace(/\r$/, "");
200
+ if (line.startsWith("event:")) {
201
+ currentEvent = line.slice(6).trim();
202
+ }
203
+ else if (line.startsWith("data:")) {
204
+ if (currentData)
205
+ currentData += "\n";
206
+ currentData += line.slice(5).trim();
207
+ }
208
+ else if (line === "") {
209
+ if (currentEvent && currentData) {
210
+ this.handleSSEEvent(currentEvent, currentData);
211
+ }
212
+ currentEvent = "";
213
+ currentData = "";
214
+ }
215
+ }
216
+ }
217
+ }
218
+ catch (err) {
219
+ if (this.shutdownRequested)
220
+ return;
221
+ if (err instanceof Error && err.name === "AbortError")
222
+ return;
223
+ throw err;
224
+ }
225
+ finally {
226
+ reader.releaseLock();
227
+ }
228
+ if (!this.shutdownRequested) {
229
+ log("SSE stream ended");
230
+ this.scheduleReconnect();
231
+ }
232
+ }
233
+ handleSSEEvent(event, rawData) {
234
+ try {
235
+ switch (event) {
236
+ case "connected": {
237
+ const data = JSON.parse(rawData);
238
+ if (typeof data.agentId !== "string" || !data.agentId) {
239
+ logError("Received malformed connected event — missing or invalid agentId");
240
+ break;
241
+ }
242
+ this.state = "connected";
243
+ this.agentId = data.agentId;
244
+ log(`Connected — agent ID: ${data.agentId}`);
245
+ this.emitSafe("onConnect", data.agentId);
246
+ this.connectResolve?.();
247
+ this.connectResolve = null;
248
+ this.connectReject = null;
249
+ break;
250
+ }
251
+ case "ping": {
252
+ const data = JSON.parse(rawData);
253
+ if (typeof data.t !== "number" || !Number.isFinite(data.t)) {
254
+ logError("Received malformed ping event — missing or invalid timestamp");
255
+ break;
256
+ }
257
+ this.resetHeartbeatMonitor();
258
+ break;
259
+ }
260
+ case "new_message": {
261
+ const data = JSON.parse(rawData);
262
+ if (!data.messageId || !data.content) {
263
+ logError("Received malformed new_message — missing messageId or content");
264
+ break;
265
+ }
266
+ this.processMessage(data);
267
+ break;
268
+ }
269
+ default:
270
+ log(`Unknown event type: ${event}`);
271
+ }
272
+ }
273
+ catch (err) {
274
+ const message = err instanceof Error ? err.message : String(err);
275
+ logError(`Failed to parse SSE event "${event}": ${message}`);
276
+ }
277
+ }
278
+ processMessage(message) {
279
+ if (this.activeHandlers >= MAX_CONCURRENT_HANDLERS) {
280
+ logError(`Dropping message ${message.messageId} — concurrency limit reached (${MAX_CONCURRENT_HANDLERS} active handlers)`);
281
+ return;
282
+ }
283
+ if (message.images) {
284
+ if (!Array.isArray(message.images)) {
285
+ logError(`Dropping message ${message.messageId} — images field is not an array`);
286
+ return;
287
+ }
288
+ if (message.images.length > MAX_IMAGES_PER_MESSAGE) {
289
+ logError(`Dropping message ${message.messageId} — too many images (${message.images.length}, max ${MAX_IMAGES_PER_MESSAGE})`);
290
+ return;
291
+ }
292
+ for (const img of message.images) {
293
+ if (typeof img !== "string" || img.length > MAX_IMAGE_SIZE) {
294
+ logError(`Dropping message ${message.messageId} — invalid or oversized image data`);
295
+ return;
296
+ }
297
+ }
298
+ }
299
+ this.activeHandlers++;
300
+ log(`Received message ${message.messageId} from user ${message.userId}`);
301
+ this.handler(message)
302
+ .then((responseContent) => this.sendResponse(message.messageId, responseContent))
303
+ .catch((err) => {
304
+ const msg = err instanceof Error ? err.message : String(err);
305
+ logError(`Handler error for message ${message.messageId}: ${msg}`);
306
+ })
307
+ .finally(() => {
308
+ this.activeHandlers--;
309
+ });
310
+ }
311
+ async sendResponse(messageId, content) {
312
+ const encoder = new TextEncoder();
313
+ const encoded = encoder.encode(content);
314
+ if (encoded.length > MAX_RESPONSE_SIZE) {
315
+ logError(`Response for ${messageId} exceeds 500KB limit (${encoded.length} bytes). Truncating.`);
316
+ const truncatedBytes = encoded.slice(0, MAX_RESPONSE_SIZE);
317
+ content = new TextDecoder().decode(truncatedBytes);
318
+ if (content.endsWith("\uFFFD")) {
319
+ content = content.slice(0, -1);
320
+ }
321
+ }
322
+ const url = `${this.config.baseUrl}/respond`;
323
+ const payload = { messageId, content };
324
+ for (let attempt = 0; attempt <= RESPOND_MAX_RETRIES; attempt++) {
325
+ try {
326
+ const response = await fetch(url, {
327
+ method: "POST",
328
+ headers: {
329
+ Authorization: `Bearer ${this.config.apiKey}`,
330
+ "Content-Type": "application/json",
331
+ },
332
+ body: JSON.stringify(payload),
333
+ signal: AbortSignal.timeout(RESPOND_TIMEOUT_MS),
334
+ });
335
+ if (!response.ok) {
336
+ const body = await response.text().catch(() => "");
337
+ logError(`Failed to send response for ${messageId}: HTTP ${response.status} — ${body}`);
338
+ return;
339
+ }
340
+ const result = (await response.json());
341
+ if (result.success && result.delivered) {
342
+ log(`Response delivered for message ${messageId}`);
343
+ }
344
+ else {
345
+ logError(`Response sent but not confirmed for ${messageId}:`, result);
346
+ }
347
+ return;
348
+ }
349
+ catch (err) {
350
+ const msg = err instanceof Error ? err.message : String(err);
351
+ if (attempt < RESPOND_MAX_RETRIES) {
352
+ logError(`Network error sending response for ${messageId} (attempt ${attempt + 1}/${RESPOND_MAX_RETRIES + 1}): ${msg}. Retrying...`);
353
+ await sleep(RESPOND_RETRY_DELAY_MS * (attempt + 1));
354
+ }
355
+ else {
356
+ logError(`Network error sending response for ${messageId} after ${RESPOND_MAX_RETRIES + 1} attempts: ${msg}`);
357
+ }
358
+ }
359
+ }
360
+ }
361
+ startHeartbeatMonitor() {
362
+ this.resetHeartbeatMonitor();
363
+ }
364
+ resetHeartbeatMonitor() {
365
+ if (this.heartbeatTimer) {
366
+ clearTimeout(this.heartbeatTimer);
367
+ }
368
+ this.heartbeatTimer = setTimeout(() => {
369
+ if (this.state !== "connected")
370
+ return;
371
+ log("Heartbeat timeout — no ping received for 60s. Reconnecting...");
372
+ this.cleanup();
373
+ this.scheduleReconnect();
374
+ }, HEARTBEAT_TIMEOUT_MS);
375
+ }
376
+ scheduleReconnect() {
377
+ if (this.shutdownRequested)
378
+ return;
379
+ if (this.state === "auth_failed")
380
+ return;
381
+ if (!this.config.autoReconnect) {
382
+ log("Auto-reconnect disabled. Staying disconnected.");
383
+ this.state = "disconnected";
384
+ this.emitSafe("onDisconnect", "connection_lost");
385
+ if (this.connectReject) {
386
+ this.connectReject(new Error("Connection lost and auto-reconnect is disabled"));
387
+ this.connectResolve = null;
388
+ this.connectReject = null;
389
+ }
390
+ return;
391
+ }
392
+ if (this.reconnectAttempt >= MAX_RECONNECT_ATTEMPTS) {
393
+ const err = new Error(`Max reconnect attempts reached (${MAX_RECONNECT_ATTEMPTS}). Giving up.`);
394
+ logError(err.message);
395
+ this.state = "disconnected";
396
+ this.emitSafe("onError", err);
397
+ this.emitSafe("onDisconnect", "max_reconnect_attempts");
398
+ if (this.connectReject) {
399
+ this.connectReject(err);
400
+ this.connectResolve = null;
401
+ this.connectReject = null;
402
+ }
403
+ return;
404
+ }
405
+ this.state = "reconnecting";
406
+ const delay = backoffDelay(this.reconnectAttempt);
407
+ this.reconnectAttempt++;
408
+ log(`Reconnecting in ${Math.round(delay)}ms (attempt ${this.reconnectAttempt}/${MAX_RECONNECT_ATTEMPTS})...`);
409
+ this.reconnectTimer = setTimeout(() => {
410
+ this.openStream().catch((err) => {
411
+ const msg = err instanceof Error ? err.message : String(err);
412
+ logError(`Reconnect stream error: ${msg}`);
413
+ });
414
+ }, delay);
415
+ }
416
+ emitSafe(event, ...args) {
417
+ try {
418
+ const handler = this.events[event];
419
+ if (handler) {
420
+ handler(...args);
421
+ }
422
+ }
423
+ catch (err) {
424
+ const msg = err instanceof Error ? err.message : String(err);
425
+ logError(`User callback "${event}" threw: ${msg}`);
426
+ }
427
+ }
428
+ }
429
+ //# sourceMappingURL=relay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relay.js","sourceRoot":"","sources":["../src/relay.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,66 @@
1
+ export interface ArkitekConfig {
2
+ apiKey: string;
3
+ autoReconnect?: boolean;
4
+ baseUrl?: string;
5
+ }
6
+ export interface IncomingMessage {
7
+ messageId: string;
8
+ content: string;
9
+ images?: string[];
10
+ userId: string;
11
+ timestamp: string;
12
+ }
13
+ export interface ConnectedEvent {
14
+ agentId: string;
15
+ timestamp: string;
16
+ }
17
+ export interface PingEvent {
18
+ t: number;
19
+ }
20
+ export interface RespondPayload {
21
+ messageId: string;
22
+ content: string;
23
+ }
24
+ export interface RespondResponse {
25
+ success: boolean;
26
+ delivered: boolean;
27
+ }
28
+ export interface CouncilRequest {
29
+ prompt: string;
30
+ models?: string[];
31
+ }
32
+ export interface CouncilResponse {
33
+ success: boolean;
34
+ responses: CouncilModelResponse[];
35
+ }
36
+ export interface CouncilModelResponse {
37
+ modelId: string;
38
+ response: string;
39
+ error?: string;
40
+ }
41
+ export type MessageHandler = (message: IncomingMessage) => Promise<string>;
42
+ export interface ArkitekRelayEvents {
43
+ onConnect?: (agentId: string) => void;
44
+ onDisconnect?: (reason: string) => void;
45
+ onError?: (error: Error) => void;
46
+ }
47
+ export type ConnectionState = "disconnected" | "connecting" | "connected" | "auth_failed" | "reconnecting";
48
+ export declare const DEFAULT_BASE_URL = "https://arkitek.dev/v1/agents/relay";
49
+ export declare const API_KEY_PATTERN: RegExp;
50
+ export declare const MAX_RESPONSE_SIZE: number;
51
+ export declare const MAX_COUNCIL_PROMPT_SIZE: number;
52
+ export declare const MAX_COUNCIL_MODELS = 8;
53
+ export declare const HEARTBEAT_TIMEOUT_MS = 60000;
54
+ export declare const RECONNECT_BASE_MS = 1000;
55
+ export declare const RECONNECT_MAX_MS = 30000;
56
+ export declare const MAX_RECONNECT_ATTEMPTS = 50;
57
+ export declare const RESPOND_TIMEOUT_MS = 15000;
58
+ export declare const COUNCIL_TIMEOUT_MS = 60000;
59
+ export declare const MAX_CONCURRENT_HANDLERS = 10;
60
+ export declare const RESPOND_MAX_RETRIES = 2;
61
+ export declare const RESPOND_RETRY_DELAY_MS = 1000;
62
+ export declare const MAX_SSE_BUFFER_SIZE: number;
63
+ export declare const MAX_IMAGES_PER_MESSAGE = 20;
64
+ export declare const MAX_IMAGE_SIZE: number;
65
+ export declare const LOG_PREFIX = "[ArkiTek Relay]";
66
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,SAAS;IACxB,CAAC,EAAE,MAAM,CAAC;CACX;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,oBAAoB,EAAE,CAAC;CACnC;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,EAAE,eAAe,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;AAE3E,MAAM,WAAW,kBAAkB;IACjC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;AAED,MAAM,MAAM,eAAe,GACvB,cAAc,GACd,YAAY,GACZ,WAAW,GACX,aAAa,GACb,cAAc,CAAC;AAEnB,eAAO,MAAM,gBAAgB,wCAAwC,CAAC;AACtE,eAAO,MAAM,eAAe,QAAyB,CAAC;AACtD,eAAO,MAAM,iBAAiB,QAAa,CAAC;AAC5C,eAAO,MAAM,uBAAuB,QAAa,CAAC;AAClD,eAAO,MAAM,kBAAkB,IAAI,CAAC;AACpC,eAAO,MAAM,oBAAoB,QAAS,CAAC;AAC3C,eAAO,MAAM,iBAAiB,OAAQ,CAAC;AACvC,eAAO,MAAM,gBAAgB,QAAS,CAAC;AACvC,eAAO,MAAM,sBAAsB,KAAK,CAAC;AACzC,eAAO,MAAM,kBAAkB,QAAS,CAAC;AACzC,eAAO,MAAM,kBAAkB,QAAS,CAAC;AACzC,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAC1C,eAAO,MAAM,mBAAmB,IAAI,CAAC;AACrC,eAAO,MAAM,sBAAsB,OAAQ,CAAC;AAC5C,eAAO,MAAM,mBAAmB,QAAgB,CAAC;AACjD,eAAO,MAAM,sBAAsB,KAAK,CAAC;AACzC,eAAO,MAAM,cAAc,QAAqB,CAAC;AACjD,eAAO,MAAM,UAAU,oBAAoB,CAAC"}
package/dist/types.js ADDED
@@ -0,0 +1,19 @@
1
+ export const DEFAULT_BASE_URL = "https://arkitek.dev/v1/agents/relay";
2
+ export const API_KEY_PATTERN = /^ak_[a-zA-Z0-9]{64}$/;
3
+ export const MAX_RESPONSE_SIZE = 500 * 1024; // 500KB
4
+ export const MAX_COUNCIL_PROMPT_SIZE = 100 * 1024; // 100KB
5
+ export const MAX_COUNCIL_MODELS = 8;
6
+ export const HEARTBEAT_TIMEOUT_MS = 60_000;
7
+ export const RECONNECT_BASE_MS = 1_000;
8
+ export const RECONNECT_MAX_MS = 30_000;
9
+ export const MAX_RECONNECT_ATTEMPTS = 50;
10
+ export const RESPOND_TIMEOUT_MS = 15_000;
11
+ export const COUNCIL_TIMEOUT_MS = 60_000;
12
+ export const MAX_CONCURRENT_HANDLERS = 10;
13
+ export const RESPOND_MAX_RETRIES = 2;
14
+ export const RESPOND_RETRY_DELAY_MS = 1_000;
15
+ export const MAX_SSE_BUFFER_SIZE = 1_024 * 1_024; // 1MB
16
+ export const MAX_IMAGES_PER_MESSAGE = 20;
17
+ export const MAX_IMAGE_SIZE = 10 * 1_024 * 1_024; // 10MB per image string
18
+ export const LOG_PREFIX = "[ArkiTek Relay]";
19
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAgEA,MAAM,CAAC,MAAM,gBAAgB,GAAG,qCAAqC,CAAC;AACtE,MAAM,CAAC,MAAM,eAAe,GAAG,sBAAsB,CAAC;AACtD,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,GAAG,IAAI,CAAC,CAAC,QAAQ;AACrD,MAAM,CAAC,MAAM,uBAAuB,GAAG,GAAG,GAAG,IAAI,CAAC,CAAC,QAAQ;AAC3D,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC;AACpC,MAAM,CAAC,MAAM,oBAAoB,GAAG,MAAM,CAAC;AAC3C,MAAM,CAAC,MAAM,iBAAiB,GAAG,KAAK,CAAC;AACvC,MAAM,CAAC,MAAM,gBAAgB,GAAG,MAAM,CAAC;AACvC,MAAM,CAAC,MAAM,sBAAsB,GAAG,EAAE,CAAC;AACzC,MAAM,CAAC,MAAM,kBAAkB,GAAG,MAAM,CAAC;AACzC,MAAM,CAAC,MAAM,kBAAkB,GAAG,MAAM,CAAC;AACzC,MAAM,CAAC,MAAM,uBAAuB,GAAG,EAAE,CAAC;AAC1C,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC;AACrC,MAAM,CAAC,MAAM,sBAAsB,GAAG,KAAK,CAAC;AAC5C,MAAM,CAAC,MAAM,mBAAmB,GAAG,KAAK,GAAG,KAAK,CAAC,CAAC,MAAM;AACxD,MAAM,CAAC,MAAM,sBAAsB,GAAG,EAAE,CAAC;AACzC,MAAM,CAAC,MAAM,cAAc,GAAG,EAAE,GAAG,KAAK,GAAG,KAAK,CAAC,CAAC,wBAAwB;AAC1E,MAAM,CAAC,MAAM,UAAU,GAAG,iBAAiB,CAAC"}
@@ -0,0 +1,5 @@
1
+ export declare function maskKey(key: string): string;
2
+ export declare function validateApiKey(key: string): void;
3
+ export declare function checkTlsSafety(): void;
4
+ export declare function warnIfNotHttps(url: string): void;
5
+ //# sourceMappingURL=validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAEA,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAG3C;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAShD;AAED,wBAAgB,cAAc,IAAI,IAAI,CAMrC;AAKD,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAahD"}
@@ -0,0 +1,35 @@
1
+ import { API_KEY_PATTERN, LOG_PREFIX } from "./types.js";
2
+ export function maskKey(key) {
3
+ if (!key || key.length < 8)
4
+ return "ak_****";
5
+ return `ak_****...${key.slice(-4)}`;
6
+ }
7
+ export function validateApiKey(key) {
8
+ if (!key) {
9
+ throw new Error("ARKITEK_API_KEY is required");
10
+ }
11
+ if (!API_KEY_PATTERN.test(key)) {
12
+ throw new Error(`Invalid API key format. Expected ak_ prefix followed by 64 alphanumeric characters (67 total). Got ${key.length} characters.`);
13
+ }
14
+ }
15
+ export function checkTlsSafety() {
16
+ if (process.env.NODE_TLS_REJECT_UNAUTHORIZED === "0") {
17
+ throw new Error("NODE_TLS_REJECT_UNAUTHORIZED=0 detected. Refusing to connect — TLS verification must be enabled for secure communication with ArkiTek.");
18
+ }
19
+ }
20
+ const MAX_WARNED_URLS = 50;
21
+ const warnedUrls = new Set();
22
+ export function warnIfNotHttps(url) {
23
+ if (warnedUrls.has(url))
24
+ return;
25
+ if (!url.startsWith("https://") && !url.startsWith("http://localhost") && !url.startsWith("http://127.0.0.1")) {
26
+ console.warn(`${LOG_PREFIX} WARNING: baseUrl "${url}" is not using HTTPS. ` +
27
+ "Your API key may be transmitted in plain text. " +
28
+ "Use HTTPS in production to prevent credential exposure.");
29
+ if (warnedUrls.size >= MAX_WARNED_URLS) {
30
+ warnedUrls.clear();
31
+ }
32
+ warnedUrls.add(url);
33
+ }
34
+ }
35
+ //# sourceMappingURL=validation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.js","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEzD,MAAM,UAAU,OAAO,CAAC,GAAW;IACjC,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IAC7C,OAAO,aAAa,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACtC,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,GAAW;IACxC,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;IACjD,CAAC;IACD,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CACb,sGAAsG,GAAG,CAAC,MAAM,cAAc,CAC/H,CAAC;IACJ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,IAAI,OAAO,CAAC,GAAG,CAAC,4BAA4B,KAAK,GAAG,EAAE,CAAC;QACrD,MAAM,IAAI,KAAK,CACb,wIAAwI,CACzI,CAAC;IACJ,CAAC;AACH,CAAC;AAED,MAAM,eAAe,GAAG,EAAE,CAAC;AAC3B,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;AAErC,MAAM,UAAU,cAAc,CAAC,GAAW;IACxC,IAAI,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC;QAAE,OAAO;IAChC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,kBAAkB,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,kBAAkB,CAAC,EAAE,CAAC;QAC9G,OAAO,CAAC,IAAI,CACV,GAAG,UAAU,sBAAsB,GAAG,wBAAwB;YAC5D,iDAAiD;YACjD,yDAAyD,CAC5D,CAAC;QACF,IAAI,UAAU,CAAC,IAAI,IAAI,eAAe,EAAE,CAAC;YACvC,UAAU,CAAC,KAAK,EAAE,CAAC;QACrB,CAAC;QACD,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACtB,CAAC;AACH,CAAC"}
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "arkitek-relay-skill",
3
+ "version": "1.0.0",
4
+ "description": "Connect your OpenClaw agent to ArkiTek via secure outbound SSE relay",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "import": "./dist/index.js",
10
+ "types": "./dist/index.d.ts"
11
+ }
12
+ },
13
+ "type": "module",
14
+ "scripts": {
15
+ "build": "tsc",
16
+ "start": "node dist/index.js",
17
+ "dev": "tsc --watch",
18
+ "test": "vitest run",
19
+ "test:watch": "vitest",
20
+ "clean": "rm -rf dist"
21
+ },
22
+ "engines": {
23
+ "node": ">=18.0.0"
24
+ },
25
+ "keywords": [
26
+ "openclaw",
27
+ "arkitek",
28
+ "agent",
29
+ "relay",
30
+ "sse",
31
+ "skill"
32
+ ],
33
+ "author": "",
34
+ "files": [
35
+ "dist",
36
+ "README.md",
37
+ "LICENSE"
38
+ ],
39
+ "license": "MIT",
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "git+https://github.com/raleighgardner16-source/arkitek-relay-skill.git"
43
+ },
44
+ "devDependencies": {
45
+ "@types/node": "^20.11.0",
46
+ "typescript": "^5.3.0",
47
+ "vitest": "^1.2.0"
48
+ }
49
+ }