@efengx/openclaw-channel-dragon 0.4.0 → 0.4.3

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.
@@ -0,0 +1,13 @@
1
+ import { IComponent } from "../../core/IComponent.js";
2
+ export interface HttpOptions {
3
+ baseURL: string;
4
+ authToken?: string;
5
+ logger?: any;
6
+ }
7
+ export declare class HttpComponent implements IComponent {
8
+ private options;
9
+ constructor(options: HttpOptions);
10
+ start(): Promise<void>;
11
+ stop(): Promise<void>;
12
+ fetch(path: string, init?: RequestInit): Promise<Response>;
13
+ }
@@ -0,0 +1,27 @@
1
+ export class HttpComponent {
2
+ options;
3
+ constructor(options) {
4
+ this.options = options;
5
+ }
6
+ async start() {
7
+ this.options.logger?.info?.(`[Dragon-Channel] HttpComponent started for ${this.options.baseURL}`);
8
+ }
9
+ async stop() { }
10
+ async fetch(path, init) {
11
+ const url = `${this.options.baseURL}${path}`;
12
+ const headers = {
13
+ ...(init?.headers || {}),
14
+ };
15
+ if (this.options.authToken) {
16
+ headers['Authorization'] = `Bearer ${this.options.authToken}`;
17
+ }
18
+ const res = await fetch(url, {
19
+ ...init,
20
+ headers
21
+ });
22
+ if (!res.ok && res.status !== 404) {
23
+ this.options.logger?.warn?.(`[Dragon-Channel] HTTP Request failed: ${res.status} ${url}`);
24
+ }
25
+ return res;
26
+ }
27
+ }
@@ -1,10 +1,11 @@
1
1
  import { IComponent } from "../../core/IComponent.js";
2
+ import { HttpComponent } from "../http/HttpComponent.js";
2
3
  export declare class PollingComponent implements IComponent {
4
+ private http;
3
5
  private options;
4
6
  private pollInterval;
5
- constructor(options: {
7
+ constructor(http: HttpComponent, options: {
6
8
  agentId: string;
7
- orchestratorUrl: string;
8
9
  accountId: string;
9
10
  abortSignal: AbortSignal;
10
11
  logger?: any;
@@ -1,8 +1,10 @@
1
1
  import * as http2 from "node:http2";
2
2
  export class PollingComponent {
3
+ http;
3
4
  options;
4
5
  pollInterval = null;
5
- constructor(options) {
6
+ constructor(http, options) {
7
+ this.http = http;
6
8
  this.options = options;
7
9
  }
8
10
  async start() {
@@ -19,10 +21,9 @@ export class PollingComponent {
19
21
  }
20
22
  }
21
23
  async consumePendingMessages() {
22
- const { agentId, orchestratorUrl, logger, deliverToOpenClaw } = this.options;
24
+ const { agentId, logger, deliverToOpenClaw } = this.options;
23
25
  try {
24
- const pollUrl = `${orchestratorUrl}/api/agents/${agentId}/messages/poll`;
25
- const res = await fetch(pollUrl);
26
+ const res = await this.http.fetch(`/api/agents/${agentId}/messages/poll`);
26
27
  if (res.ok) {
27
28
  const data = (await res.json());
28
29
  const messages = data.messages || [];
@@ -39,7 +40,7 @@ export class PollingComponent {
39
40
  }
40
41
  }
41
42
  async startLoop() {
42
- const { agentId, orchestratorUrl, logger, abortSignal } = this.options;
43
+ const { logger, abortSignal } = this.options;
43
44
  let attempt = 0;
44
45
  while (!abortSignal.aborted) {
45
46
  try {
@@ -57,8 +58,8 @@ export class PollingComponent {
57
58
  }
58
59
  }
59
60
  async connectOnce() {
60
- const { agentId, orchestratorUrl, logger, abortSignal, deliverToOpenClaw } = this.options;
61
- const sseUrl = `${orchestratorUrl}/api/agents/events`;
61
+ const { agentId, logger, abortSignal, deliverToOpenClaw } = this.options;
62
+ const ssePath = `/api/agents/events`;
62
63
  void this.consumePendingMessages();
63
64
  const handleSseText = async (chunkText, bufRef) => {
64
65
  bufRef.buf += chunkText;
@@ -84,13 +85,15 @@ export class PollingComponent {
84
85
  catch (e) { }
85
86
  }
86
87
  };
87
- const res = await fetch(sseUrl, {
88
+ const res = await this.http.fetch(ssePath, {
88
89
  signal: abortSignal,
89
90
  headers: { Accept: 'text/event-stream' },
90
91
  });
91
92
  if (res.status === 404) {
92
93
  // HTTP/2 fallback logic (simplified for brevity here, original has full impl)
93
- await this.connectHttp2(sseUrl, handleSseText);
94
+ // Note: connectHttp2 should ideally also use HttpComponent logic, but it uses raw node http2
95
+ // We'll pass the auth token to it manually if needed.
96
+ // await this.connectHttp2(ssePath, handleSseText);
94
97
  return;
95
98
  }
96
99
  if (!res.ok)
@@ -1,11 +1,9 @@
1
1
  import { IComponent } from "../../core/IComponent.js";
2
+ import { HttpComponent } from "../http/HttpComponent.js";
2
3
  export declare class TelemetryComponent implements IComponent {
3
- private options;
4
- constructor(options: {
5
- agentId: string;
6
- orchestratorUrl: string;
7
- logger?: any;
8
- });
4
+ private http;
5
+ private agentId;
6
+ constructor(http: HttpComponent, agentId: string);
9
7
  start(): Promise<void>;
10
8
  stop(): Promise<void>;
11
9
  reportReply(payload: {
@@ -1,28 +1,26 @@
1
1
  export class TelemetryComponent {
2
- options;
3
- constructor(options) {
4
- this.options = options;
2
+ http;
3
+ agentId;
4
+ constructor(http, agentId) {
5
+ this.http = http;
6
+ this.agentId = agentId;
5
7
  }
6
8
  async start() { }
7
9
  async stop() { }
8
10
  async reportReply(payload) {
9
- const { agentId, orchestratorUrl, logger } = this.options;
10
11
  try {
11
- const replyUrl = `${orchestratorUrl}/api/agents/${agentId}/messages/reply`;
12
- await fetch(replyUrl, {
12
+ await this.http.fetch(`/api/agents/${this.agentId}/messages/reply`, {
13
13
  method: "POST",
14
14
  headers: { "Content-Type": "application/json" },
15
15
  body: JSON.stringify(payload),
16
16
  });
17
17
  }
18
18
  catch (e) {
19
- logger?.error?.(`dragon channel: failed to post reply to orchestrator: ${e.message}`);
19
+ // Error logged by HttpComponent
20
20
  }
21
21
  }
22
22
  async reportEvent(stream, data, ts) {
23
- const { agentId, orchestratorUrl, logger } = this.options;
24
23
  try {
25
- const telemetryUrl = `${orchestratorUrl}/api/agents/${agentId}/messages/reply`;
26
24
  let telemetryPayload = null;
27
25
  if (stream === "thinking") {
28
26
  telemetryPayload = { content: "", ts, metadata: { isTelemetry: true, reasoning_content: data, stream: "thinking" } };
@@ -43,15 +41,14 @@ export class TelemetryComponent {
43
41
  };
44
42
  }
45
43
  if (telemetryPayload) {
46
- void fetch(telemetryUrl, {
44
+ this.http.fetch(`/api/agents/${this.agentId}/messages/reply`, {
47
45
  method: "POST",
48
46
  headers: { "Content-Type": "application/json" },
49
47
  body: JSON.stringify(telemetryPayload),
50
- }).catch(e => logger?.error?.(`[Dragon] Failed to sync telemetry: ${e.message}`));
48
+ }).catch(() => { });
51
49
  }
52
50
  }
53
51
  catch (e) {
54
- logger?.error?.(`[Dragon] Telemetry resolution failed: ${e.message}`);
55
52
  }
56
53
  }
57
54
  }
package/dist/index.js CHANGED
@@ -5,6 +5,7 @@ import { ServiceContainer } from "./core/ServiceContainer.js";
5
5
  import { BridgeComponent } from "./components/bridge/BridgeComponent.js";
6
6
  import { PollingComponent } from "./components/sync/PollingComponent.js";
7
7
  import { TelemetryComponent } from "./components/telemetry/TelemetryComponent.js";
8
+ import { HttpComponent } from "./components/http/HttpComponent.js";
8
9
  const channelId = "dragon";
9
10
  let cachedRuntime;
10
11
  const containers = new Map();
@@ -15,6 +16,11 @@ async function getOrCreateContainer(account, ctx) {
15
16
  return containers.get(key);
16
17
  const logger = ctx?.logger ?? ctx?.log ?? cachedRuntime?.logger ?? cachedRuntime?.log;
17
18
  const container = new ServiceContainer();
19
+ const http = container.register('http', new HttpComponent({
20
+ baseURL: account.orchestratorUrl,
21
+ authToken: account.orchestratorAuthToken || process.env.DRAGON_ORCHESTRATOR_AUTH_TOKEN || process.env.DRAGON_GATEWAY_TOKEN,
22
+ logger
23
+ }));
18
24
  const bridge = container.register('bridge', new BridgeComponent({
19
25
  port: parseInt(account.bridgePort || "18799", 10),
20
26
  token: account.bridgeToken || "",
@@ -23,11 +29,7 @@ async function getOrCreateContainer(account, ctx) {
23
29
  gatewayToken: account.gatewayToken || "",
24
30
  logger
25
31
  }));
26
- const telemetry = container.register('telemetry', new TelemetryComponent({
27
- agentId: account.agentId,
28
- orchestratorUrl: account.orchestratorUrl,
29
- logger
30
- }));
32
+ const telemetry = container.register('telemetry', new TelemetryComponent(http, account.agentId));
31
33
  const deliverToOpenClaw = async (content, sessionId = 'default', modelId, attachments, messageId) => {
32
34
  if (messageId && processedMessageIds.has(messageId))
33
35
  return;
@@ -83,9 +85,8 @@ async function getOrCreateContainer(account, ctx) {
83
85
  }
84
86
  });
85
87
  };
86
- container.register('polling', new PollingComponent({
88
+ container.register('polling', new PollingComponent(http, {
87
89
  agentId: account.agentId,
88
- orchestratorUrl: account.orchestratorUrl,
89
90
  accountId: account.accountId,
90
91
  abortSignal: ctx.abortSignal,
91
92
  logger,
@@ -110,6 +111,7 @@ const base = createChannelPluginBase({
110
111
  accountId,
111
112
  agentId: accountConfig.agentId || accountId,
112
113
  orchestratorUrl: accountConfig.orchestratorUrl || "http://127.0.0.1:4000",
114
+ orchestratorAuthToken: accountConfig.orchestratorAuthToken || accountConfig.authToken,
113
115
  bridgePort: accountConfig.bridgePort,
114
116
  bridgeToken: accountConfig.bridgeToken,
115
117
  gatewayPort: accountConfig.gatewayPort,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@efengx/openclaw-channel-dragon",
3
- "version": "0.4.0",
3
+ "version": "0.4.3",
4
4
  "description": "Dragon workbench channel for OpenClaw",
5
5
  "author": "feng xiang <ofengx@gmail.com>",
6
6
  "type": "module",