openclaw-channel-claw-task-ops 0.1.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.
- package/README.md +141 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/setup-entry.d.ts +5 -0
- package/dist/setup-entry.d.ts.map +1 -0
- package/dist/setup-entry.js +3 -0
- package/dist/src/channel.d.ts +3 -0
- package/dist/src/channel.d.ts.map +1 -0
- package/dist/src/channel.js +72 -0
- package/dist/src/config.d.ts +15 -0
- package/dist/src/config.d.ts.map +1 -0
- package/dist/src/config.js +51 -0
- package/dist/src/constants.d.ts +17 -0
- package/dist/src/constants.d.ts.map +1 -0
- package/dist/src/constants.js +16 -0
- package/dist/src/consumer.d.ts +9 -0
- package/dist/src/consumer.d.ts.map +1 -0
- package/dist/src/consumer.js +76 -0
- package/dist/src/dispatch-inbound.d.ts +13 -0
- package/dist/src/dispatch-inbound.d.ts.map +1 -0
- package/dist/src/dispatch-inbound.js +126 -0
- package/dist/src/hsf-client.d.ts +21 -0
- package/dist/src/hsf-client.d.ts.map +1 -0
- package/dist/src/hsf-client.js +91 -0
- package/dist/src/logging.d.ts +11 -0
- package/dist/src/logging.d.ts.map +1 -0
- package/dist/src/logging.js +24 -0
- package/dist/src/types.d.ts +62 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +7 -0
- package/openclaw.plugin.json +38 -0
- package/package.json +53 -0
package/README.md
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# openclaw-channel-claw-task-ops
|
|
2
|
+
|
|
3
|
+
OpenClaw 渠道插件:从 Claw Task Ops 拉取任务,交由 OpenClaw Agent 处理,并将处理结果和日志反馈回去。
|
|
4
|
+
|
|
5
|
+
## 接入方式
|
|
6
|
+
|
|
7
|
+
### 1. 安装插件
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install openclaw-channel-claw-task-ops
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### 2. 配置 OpenClaw
|
|
14
|
+
|
|
15
|
+
在 OpenClaw 配置文件中添加渠道配置:
|
|
16
|
+
|
|
17
|
+
```yaml
|
|
18
|
+
channels:
|
|
19
|
+
claw-task-ops:
|
|
20
|
+
enabled: true
|
|
21
|
+
gatewayBaseUrl: "https://pre-lantu.alibaba-inc.com"
|
|
22
|
+
instanceId: "your_claw_instance_id"
|
|
23
|
+
pollIntervalSec: 10
|
|
24
|
+
pullLimit: 5
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
配置说明:
|
|
28
|
+
- `gatewayBaseUrl`: HSF HTTP 网关地址
|
|
29
|
+
- `instanceId`: Claw 实例 ID(在 Claw Task Ops 管理界面创建)
|
|
30
|
+
- `pollIntervalSec`: 轮询间隔(秒),默认 10
|
|
31
|
+
- `pullLimit`: 每次拉取的最大任务数,默认 5
|
|
32
|
+
|
|
33
|
+
### 3. 在 openclaw.config.yaml 中注册插件
|
|
34
|
+
|
|
35
|
+
```yaml
|
|
36
|
+
plugins:
|
|
37
|
+
entries:
|
|
38
|
+
claw-task-ops:
|
|
39
|
+
package: openclaw-channel-claw-task-ops
|
|
40
|
+
config:
|
|
41
|
+
gatewayBaseUrl: "https://pre-lantu.alibaba-inc.com"
|
|
42
|
+
instanceId: "your_claw_instance_id"
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## 投递任务
|
|
46
|
+
|
|
47
|
+
### 方式一:通过管理界面
|
|
48
|
+
|
|
49
|
+
1. 访问 Claw Task Ops 管理界面
|
|
50
|
+
2. 进入目标实例
|
|
51
|
+
3. 点击「新建任务」
|
|
52
|
+
4. 填写任务信息(名称、类型、优先级、载荷)
|
|
53
|
+
5. 提交后任务进入 pending 状态,等待 OpenClaw 拉取处理
|
|
54
|
+
|
|
55
|
+
### 方式二:通过 API 直接投递
|
|
56
|
+
|
|
57
|
+
使用通用仓储 API 直接写入任务数据:
|
|
58
|
+
|
|
59
|
+
```javascript
|
|
60
|
+
// 投递任务
|
|
61
|
+
const taskId = `claw_task_${Date.now()}`;
|
|
62
|
+
await fetch('/luyou/lp/data/save/claw_task_info', {
|
|
63
|
+
method: 'POST',
|
|
64
|
+
headers: { 'Content-Type': 'application/json' },
|
|
65
|
+
body: JSON.stringify({
|
|
66
|
+
dataKey: taskId,
|
|
67
|
+
dataValue: {
|
|
68
|
+
taskId,
|
|
69
|
+
instanceId: 'your_claw_instance_id',
|
|
70
|
+
taskName: '数据同步任务',
|
|
71
|
+
taskType: 'data_sync',
|
|
72
|
+
priority: 'high', // high | medium | low
|
|
73
|
+
status: 'pending', // 必须为 pending,插件才会拉取
|
|
74
|
+
payload: { key: 'value' },
|
|
75
|
+
operator: '张三',
|
|
76
|
+
operatorId: '12345',
|
|
77
|
+
createdTime: new Date().toISOString(),
|
|
78
|
+
updatedTime: new Date().toISOString(),
|
|
79
|
+
deleted: 0,
|
|
80
|
+
},
|
|
81
|
+
searchBy: {
|
|
82
|
+
taskId,
|
|
83
|
+
instanceId: 'your_claw_instance_id',
|
|
84
|
+
status: 'pending',
|
|
85
|
+
priority: 'high',
|
|
86
|
+
deleted: '0',
|
|
87
|
+
}
|
|
88
|
+
})
|
|
89
|
+
});
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### 方式三:通过 HSF 接口投递(后端调用)
|
|
93
|
+
|
|
94
|
+
```java
|
|
95
|
+
// 直接使用 CompassDataWarehouseRepository 写入
|
|
96
|
+
ClawTaskDTO task = new ClawTaskDTO();
|
|
97
|
+
task.setTaskId("claw_task_" + System.currentTimeMillis());
|
|
98
|
+
task.setInstanceId("your_claw_instance_id");
|
|
99
|
+
task.setTaskName("报表生成任务");
|
|
100
|
+
task.setTaskType("report");
|
|
101
|
+
task.setPriority("medium");
|
|
102
|
+
task.setStatus("pending");
|
|
103
|
+
task.setPayload(Map.of("reportType", "daily"));
|
|
104
|
+
task.setOperator("系统");
|
|
105
|
+
task.setOperatorId("system");
|
|
106
|
+
task.setCreatedTime(Instant.now().toString());
|
|
107
|
+
task.setDeleted(0);
|
|
108
|
+
task.setDataKey(task.getTaskId());
|
|
109
|
+
task.setWarehouseName(ClawTaskDTO.WAREHOUSE_NAME);
|
|
110
|
+
task.setSearchBy(Map.of(
|
|
111
|
+
"taskId", task.getTaskId(),
|
|
112
|
+
"instanceId", task.getInstanceId(),
|
|
113
|
+
"status", "pending",
|
|
114
|
+
"priority", "medium",
|
|
115
|
+
"deleted", "0"
|
|
116
|
+
));
|
|
117
|
+
|
|
118
|
+
compassDataWarehouseRepository.saveData(task);
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## 处理流程
|
|
122
|
+
|
|
123
|
+
```
|
|
124
|
+
投递任务 (status=pending)
|
|
125
|
+
↓
|
|
126
|
+
插件轮询拉取 → 状态变更为 processing
|
|
127
|
+
↓
|
|
128
|
+
OpenClaw Agent 处理任务
|
|
129
|
+
↓
|
|
130
|
+
处理日志写入 claw_task_log
|
|
131
|
+
↓
|
|
132
|
+
反馈结果 → 状态变更为 completed/failed
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## 推包脚本
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
cd packages/openclaw-channel-claw-task-ops
|
|
139
|
+
npm run build
|
|
140
|
+
npm publish
|
|
141
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { clawTaskOpsPlugin } from "./src/channel.js";
|
|
2
|
+
import { registerClawTaskOpsConsumer } from "./src/consumer.js";
|
|
3
|
+
declare const _default: {
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
description: string;
|
|
7
|
+
configSchema: import("openclaw/plugin-sdk/core").OpenClawPluginConfigSchema;
|
|
8
|
+
register: (api: import("openclaw/plugin-sdk/core").OpenClawPluginApi) => void;
|
|
9
|
+
channelPlugin: import("openclaw/plugin-sdk/core").ChannelPlugin<import("./src/types.js").ResolvedClawTaskOpsAccount, unknown, unknown>;
|
|
10
|
+
setChannelRuntime?: (runtime: import("openclaw/plugin-sdk/core").PluginRuntime) => void;
|
|
11
|
+
};
|
|
12
|
+
export default _default;
|
|
13
|
+
export { clawTaskOpsPlugin, registerClawTaskOpsConsumer };
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,2BAA2B,EAAE,MAAM,mBAAmB,CAAC;;;;;;;;;;AAEhE,wBAeG;AAEH,OAAO,EAAE,iBAAiB,EAAE,2BAA2B,EAAE,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
|
|
2
|
+
import { clawTaskOpsPlugin } from "./src/channel.js";
|
|
3
|
+
import { registerClawTaskOpsConsumer } from "./src/consumer.js";
|
|
4
|
+
export default defineChannelPluginEntry({
|
|
5
|
+
id: "claw-task-ops",
|
|
6
|
+
name: "Claw Task Ops",
|
|
7
|
+
description: "Polls pending tasks from Claw Task Ops via HSF HTTP gateway, dispatches to OpenClaw agent, feeds back results and processing logs.",
|
|
8
|
+
plugin: clawTaskOpsPlugin,
|
|
9
|
+
registerFull(api) {
|
|
10
|
+
try {
|
|
11
|
+
registerClawTaskOpsConsumer(api);
|
|
12
|
+
}
|
|
13
|
+
catch (e) {
|
|
14
|
+
api.logger?.error?.(`[claw-task-ops] registerFull failed; gateway continues without task consumer: ${String(e)}`);
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
export { clawTaskOpsPlugin, registerClawTaskOpsConsumer };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup-entry.d.ts","sourceRoot":"","sources":["../setup-entry.ts"],"names":[],"mappings":";;;AAGA,wBAAyD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel.d.ts","sourceRoot":"","sources":["../../src/channel.ts"],"names":[],"mappings":"AASA,OAAO,EAAc,KAAK,0BAA0B,EAAE,MAAM,YAAY,CAAC;AAkBzE,eAAO,MAAM,iBAAiB,gGAiD5B,CAAC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { createChannelPluginBase, createChatChannelPlugin, DEFAULT_ACCOUNT_ID, } from "openclaw/plugin-sdk/core";
|
|
2
|
+
import { chunkTextForOutbound } from "openclaw/plugin-sdk/irc";
|
|
3
|
+
import { inspectAccount, resolveAccount } from "./config.js";
|
|
4
|
+
import { addTaskLog } from "./hsf-client.js";
|
|
5
|
+
import { logWarn } from "./logging.js";
|
|
6
|
+
import { CHANNEL_ID } from "./types.js";
|
|
7
|
+
const CAPS = { chatTypes: ["direct", "group"] };
|
|
8
|
+
const pluginBase = createChannelPluginBase({
|
|
9
|
+
id: CHANNEL_ID,
|
|
10
|
+
capabilities: CAPS,
|
|
11
|
+
config: {
|
|
12
|
+
listAccountIds: () => [DEFAULT_ACCOUNT_ID],
|
|
13
|
+
resolveAccount: (cfg, aid) => resolveAccount(cfg, aid),
|
|
14
|
+
inspectAccount: (cfg, aid) => inspectAccount(cfg, aid),
|
|
15
|
+
defaultAccountId: () => DEFAULT_ACCOUNT_ID,
|
|
16
|
+
},
|
|
17
|
+
setup: {
|
|
18
|
+
applyAccountConfig: ({ cfg }) => cfg,
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
export const clawTaskOpsPlugin = createChatChannelPlugin({
|
|
22
|
+
base: {
|
|
23
|
+
...pluginBase,
|
|
24
|
+
capabilities: CAPS,
|
|
25
|
+
config: pluginBase.config,
|
|
26
|
+
},
|
|
27
|
+
security: {
|
|
28
|
+
dm: {
|
|
29
|
+
channelKey: CHANNEL_ID,
|
|
30
|
+
resolvePolicy: () => "open",
|
|
31
|
+
resolveAllowFrom: () => ["*"],
|
|
32
|
+
defaultPolicy: "open",
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
pairing: {
|
|
36
|
+
text: {
|
|
37
|
+
idLabel: "Claw Task conversation id",
|
|
38
|
+
message: "Claw Task Ops 会话已建立。",
|
|
39
|
+
notify: async () => { },
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
threading: { topLevelReplyToMode: CHANNEL_ID },
|
|
43
|
+
outbound: {
|
|
44
|
+
base: {
|
|
45
|
+
deliveryMode: "direct",
|
|
46
|
+
chunker: chunkTextForOutbound,
|
|
47
|
+
chunkerMode: "markdown",
|
|
48
|
+
textChunkLimit: 3500,
|
|
49
|
+
resolveTarget: ({ to }) => {
|
|
50
|
+
const t = to?.trim() ?? "";
|
|
51
|
+
if (!t)
|
|
52
|
+
return { ok: false, error: new Error("missing target peer id") };
|
|
53
|
+
return { ok: true, to: t };
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
attachedResults: {
|
|
57
|
+
channel: CHANNEL_ID,
|
|
58
|
+
sendText: async ({ cfg, to, text }) => {
|
|
59
|
+
try {
|
|
60
|
+
const acc = resolveAccount(cfg, DEFAULT_ACCOUNT_ID);
|
|
61
|
+
// 将出站消息作为任务日志写回
|
|
62
|
+
await addTaskLog(acc.gatewayBaseUrl, to, acc.instanceId, "info", text);
|
|
63
|
+
return { messageId: `${Date.now()}`, chatId: to };
|
|
64
|
+
}
|
|
65
|
+
catch (e) {
|
|
66
|
+
logWarn(`sendText_fail chatId=${to} err=${String(e)}`);
|
|
67
|
+
return { messageId: "error", chatId: to };
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type OpenClawConfig } from "openclaw/plugin-sdk/core";
|
|
2
|
+
import { type ResolvedClawTaskOpsAccount } from "./types.js";
|
|
3
|
+
export declare function resolveAccount(cfg: OpenClawConfig, accountId?: string | null): ResolvedClawTaskOpsAccount;
|
|
4
|
+
export declare function inspectAccount(cfg: OpenClawConfig, accountId?: string | null): {
|
|
5
|
+
enabled: boolean;
|
|
6
|
+
configured: boolean;
|
|
7
|
+
baseUrlStatus: "available";
|
|
8
|
+
tokenStatus: "available";
|
|
9
|
+
} | {
|
|
10
|
+
enabled: boolean;
|
|
11
|
+
configured: boolean;
|
|
12
|
+
baseUrlStatus: "missing";
|
|
13
|
+
tokenStatus: "available";
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0C,KAAK,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAEvG,OAAO,EAAiC,KAAK,0BAA0B,EAAE,MAAM,YAAY,CAAC;AAM5F,wBAAgB,cAAc,CAC5B,GAAG,EAAE,cAAc,EACnB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GACxB,0BAA0B,CA4B5B;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,cAAc,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI;;;;;;;;;;EAiB5E"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk/core";
|
|
2
|
+
import { DEFAULT_POLL_INTERVAL_SEC, DEFAULT_PULL_LIMIT } from "./constants.js";
|
|
3
|
+
import { CHANNEL_ID, getChannelSection } from "./types.js";
|
|
4
|
+
function str(v) {
|
|
5
|
+
return typeof v === "string" && v.trim() !== "" ? v.trim() : undefined;
|
|
6
|
+
}
|
|
7
|
+
export function resolveAccount(cfg, accountId) {
|
|
8
|
+
const id = normalizeAccountId(accountId ?? DEFAULT_ACCOUNT_ID);
|
|
9
|
+
if (id !== DEFAULT_ACCOUNT_ID) {
|
|
10
|
+
throw new Error(`[${CHANNEL_ID}] multi-account is not supported`);
|
|
11
|
+
}
|
|
12
|
+
const section = getChannelSection(cfg);
|
|
13
|
+
const gatewayBaseUrl = str(section.gatewayBaseUrl);
|
|
14
|
+
const instanceId = str(section.instanceId);
|
|
15
|
+
if (!gatewayBaseUrl) {
|
|
16
|
+
throw new Error(`[${CHANNEL_ID}] missing gatewayBaseUrl`);
|
|
17
|
+
}
|
|
18
|
+
if (!instanceId) {
|
|
19
|
+
throw new Error(`[${CHANNEL_ID}] missing instanceId`);
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
accountId: DEFAULT_ACCOUNT_ID,
|
|
23
|
+
gatewayBaseUrl: gatewayBaseUrl.replace(/\/+$/, ""),
|
|
24
|
+
instanceId,
|
|
25
|
+
instanceName: str(section.instanceName) ?? instanceId,
|
|
26
|
+
instanceDescription: str(section.instanceDescription) ?? "",
|
|
27
|
+
authorizedUsers: Array.isArray(section.authorizedUsers) ? section.authorizedUsers : [],
|
|
28
|
+
accessToken: str(section.accessToken) ?? "",
|
|
29
|
+
pollIntervalSec: section.pollIntervalSec ?? DEFAULT_POLL_INTERVAL_SEC,
|
|
30
|
+
pullLimit: section.pullLimit ?? DEFAULT_PULL_LIMIT,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export function inspectAccount(cfg, accountId) {
|
|
34
|
+
try {
|
|
35
|
+
resolveAccount(cfg, accountId);
|
|
36
|
+
return {
|
|
37
|
+
enabled: true,
|
|
38
|
+
configured: true,
|
|
39
|
+
baseUrlStatus: "available",
|
|
40
|
+
tokenStatus: "available",
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return {
|
|
45
|
+
enabled: true,
|
|
46
|
+
configured: false,
|
|
47
|
+
baseUrlStatus: "missing",
|
|
48
|
+
tokenStatus: "available",
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** 默认轮询间隔(秒) */
|
|
2
|
+
export declare const DEFAULT_POLL_INTERVAL_SEC = 10;
|
|
3
|
+
/** 默认每次拉取的最大任务数 */
|
|
4
|
+
export declare const DEFAULT_PULL_LIMIT = 5;
|
|
5
|
+
/** HSF 网关路径 */
|
|
6
|
+
export declare const HSF_GATEWAY_PATH = "/luyou/public/gateway/executeHsf.json";
|
|
7
|
+
/** HSF 服务配置 */
|
|
8
|
+
export declare const HSF_APP_NAME = "onetouch-logistics-operation";
|
|
9
|
+
export declare const HSF_SERVICE_INTERFACE = "com.alibaba.onetouch.logistics.compass.service.ClawTaskOpsRemoteService";
|
|
10
|
+
export declare const HSF_RESULT_TYPE = "com.alibaba.onetouch.basedata.api.Result";
|
|
11
|
+
/** HSF 方法名 */
|
|
12
|
+
export declare const HSF_REGISTER_INSTANCE = "com.alibaba.onetouch.logistics.compass.service.ClawTaskOpsRemoteService#registerInstance(java.lang.String,java.lang.String,java.lang.String,java.util.Map)~com.alibaba.onetouch.basedata.api.Result";
|
|
13
|
+
export declare const HSF_PULL_TASKS = "com.alibaba.onetouch.logistics.compass.service.ClawTaskOpsRemoteService#pullTasks(java.lang.String,int)~com.alibaba.onetouch.basedata.api.Result";
|
|
14
|
+
export declare const HSF_FEEDBACK_TASK = "com.alibaba.onetouch.logistics.compass.service.ClawTaskOpsRemoteService#feedbackTask(java.lang.String,java.lang.String,java.lang.String)~com.alibaba.onetouch.basedata.api.Result";
|
|
15
|
+
export declare const HSF_ADD_TASK_LOG = "com.alibaba.onetouch.logistics.compass.service.ClawTaskOpsRemoteService#addTaskLog(com.alibaba.onetouch.logistics.compass.dto.claw.ClawTaskLogDTO)~com.alibaba.onetouch.basedata.api.Result";
|
|
16
|
+
export declare const HSF_SUBMIT_TASK = "com.alibaba.onetouch.logistics.compass.service.ClawTaskOpsRemoteService#submitTask(com.alibaba.onetouch.logistics.compass.dto.claw.ClawTaskDTO,java.lang.String)~com.alibaba.onetouch.basedata.api.Result";
|
|
17
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":"AAAA,gBAAgB;AAChB,eAAO,MAAM,yBAAyB,KAAK,CAAC;AAE5C,mBAAmB;AACnB,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAEpC,eAAe;AACf,eAAO,MAAM,gBAAgB,0CAA0C,CAAC;AAExE,eAAe;AACf,eAAO,MAAM,YAAY,iCAAiC,CAAC;AAC3D,eAAO,MAAM,qBAAqB,4EAA4E,CAAC;AAC/G,eAAO,MAAM,eAAe,6CAA6C,CAAC;AAE1E,cAAc;AACd,eAAO,MAAM,qBAAqB,wMAAmI,CAAC;AACtK,eAAO,MAAM,cAAc,qJAAgF,CAAC;AAC5G,eAAO,MAAM,iBAAiB,sLAAiH,CAAC;AAChJ,eAAO,MAAM,gBAAgB,gMAA2H,CAAC;AACzJ,eAAO,MAAM,eAAe,8MAAyI,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** 默认轮询间隔(秒) */
|
|
2
|
+
export const DEFAULT_POLL_INTERVAL_SEC = 10;
|
|
3
|
+
/** 默认每次拉取的最大任务数 */
|
|
4
|
+
export const DEFAULT_PULL_LIMIT = 5;
|
|
5
|
+
/** HSF 网关路径 */
|
|
6
|
+
export const HSF_GATEWAY_PATH = "/luyou/public/gateway/executeHsf.json";
|
|
7
|
+
/** HSF 服务配置 */
|
|
8
|
+
export const HSF_APP_NAME = "onetouch-logistics-operation";
|
|
9
|
+
export const HSF_SERVICE_INTERFACE = "com.alibaba.onetouch.logistics.compass.service.ClawTaskOpsRemoteService";
|
|
10
|
+
export const HSF_RESULT_TYPE = "com.alibaba.onetouch.basedata.api.Result";
|
|
11
|
+
/** HSF 方法名 */
|
|
12
|
+
export const HSF_REGISTER_INSTANCE = `${HSF_SERVICE_INTERFACE}#registerInstance(java.lang.String,java.lang.String,java.lang.String,java.util.Map)~${HSF_RESULT_TYPE}`;
|
|
13
|
+
export const HSF_PULL_TASKS = `${HSF_SERVICE_INTERFACE}#pullTasks(java.lang.String,int)~${HSF_RESULT_TYPE}`;
|
|
14
|
+
export const HSF_FEEDBACK_TASK = `${HSF_SERVICE_INTERFACE}#feedbackTask(java.lang.String,java.lang.String,java.lang.String)~${HSF_RESULT_TYPE}`;
|
|
15
|
+
export const HSF_ADD_TASK_LOG = `${HSF_SERVICE_INTERFACE}#addTaskLog(com.alibaba.onetouch.logistics.compass.dto.claw.ClawTaskLogDTO)~${HSF_RESULT_TYPE}`;
|
|
16
|
+
export const HSF_SUBMIT_TASK = `${HSF_SERVICE_INTERFACE}#submitTask(com.alibaba.onetouch.logistics.compass.dto.claw.ClawTaskDTO,java.lang.String)~${HSF_RESULT_TYPE}`;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type OpenClawPluginApi } from "openclaw/plugin-sdk/irc";
|
|
2
|
+
/**
|
|
3
|
+
* 注册任务消费者:
|
|
4
|
+
* 1. 启动时自动注册实例到后端(不存在则创建,已存在则更新为 running)
|
|
5
|
+
* 2. 每次轮询时通过 registerInstance 心跳更新 lastOnlineTime
|
|
6
|
+
* 3. 拉取 pending 任务分发给 OpenClaw 处理
|
|
7
|
+
*/
|
|
8
|
+
export declare function registerClawTaskOpsConsumer(api: OpenClawPluginApi): void;
|
|
9
|
+
//# sourceMappingURL=consumer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consumer.d.ts","sourceRoot":"","sources":["../../src/consumer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAajE;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,iBAAiB,GAAG,IAAI,CAyExE"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/core";
|
|
2
|
+
import { resolveAccount } from "./config.js";
|
|
3
|
+
import { dispatchTaskInbound } from "./dispatch-inbound.js";
|
|
4
|
+
import { pullTasks, registerInstance } from "./hsf-client.js";
|
|
5
|
+
import { attachLogger, logError, logInfo, logWarn } from "./logging.js";
|
|
6
|
+
const RECONNECT_MS_MIN = 2000;
|
|
7
|
+
const RECONNECT_MS_MAX = 60_000;
|
|
8
|
+
function sleep(ms) {
|
|
9
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* 注册任务消费者:
|
|
13
|
+
* 1. 启动时自动注册实例到后端(不存在则创建,已存在则更新为 running)
|
|
14
|
+
* 2. 每次轮询时通过 registerInstance 心跳更新 lastOnlineTime
|
|
15
|
+
* 3. 拉取 pending 任务分发给 OpenClaw 处理
|
|
16
|
+
*/
|
|
17
|
+
export function registerClawTaskOpsConsumer(api) {
|
|
18
|
+
attachLogger(api);
|
|
19
|
+
logInfo("consumer_register_ok (polling loop starting)");
|
|
20
|
+
let stopped = false;
|
|
21
|
+
let reconnectDelay = RECONNECT_MS_MIN;
|
|
22
|
+
const loop = async () => {
|
|
23
|
+
while (!stopped) {
|
|
24
|
+
const cfg = api.runtime.config.loadConfig();
|
|
25
|
+
let account;
|
|
26
|
+
try {
|
|
27
|
+
account = resolveAccount(cfg, DEFAULT_ACCOUNT_ID);
|
|
28
|
+
}
|
|
29
|
+
catch (e) {
|
|
30
|
+
logWarn(`consumer_idle not_configured err=${String(e)}`);
|
|
31
|
+
await sleep(reconnectDelay);
|
|
32
|
+
reconnectDelay = Math.min(reconnectDelay * 2, RECONNECT_MS_MAX);
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
reconnectDelay = RECONNECT_MS_MIN;
|
|
36
|
+
// 每次轮询都调用 registerInstance 作为心跳,更新 lastOnlineTime + 自动注册
|
|
37
|
+
// 同时传入 authorizedUsers 和 accessToken
|
|
38
|
+
await registerInstance(account.gatewayBaseUrl, account.instanceId, account.instanceName, account.instanceDescription, {
|
|
39
|
+
authorizedUsers: account.authorizedUsers,
|
|
40
|
+
accessToken: account.accessToken,
|
|
41
|
+
});
|
|
42
|
+
try {
|
|
43
|
+
const tasks = await pullTasks(account.gatewayBaseUrl, account.instanceId, account.pullLimit);
|
|
44
|
+
if (tasks.length > 0) {
|
|
45
|
+
logInfo(`consumer_pulled count=${tasks.length} instanceId=${account.instanceId}`);
|
|
46
|
+
for (const task of tasks) {
|
|
47
|
+
try {
|
|
48
|
+
await dispatchTaskInbound({ api, cfg, account, task });
|
|
49
|
+
}
|
|
50
|
+
catch (e) {
|
|
51
|
+
logError(`dispatch_task_error taskId=${task.taskId} err=${String(e)}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch (e) {
|
|
57
|
+
logError(`consumer_poll_error err=${String(e)}`);
|
|
58
|
+
}
|
|
59
|
+
await sleep(account.pollIntervalSec * 1000);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
setImmediate(() => {
|
|
63
|
+
void (async () => {
|
|
64
|
+
for (;;) {
|
|
65
|
+
try {
|
|
66
|
+
await loop();
|
|
67
|
+
logWarn("consumer_loop_exited unexpectedly; restarting");
|
|
68
|
+
}
|
|
69
|
+
catch (e) {
|
|
70
|
+
logError(`consumer_loop_fatal restarting err=${String(e)}`);
|
|
71
|
+
}
|
|
72
|
+
await sleep(RECONNECT_MS_MIN);
|
|
73
|
+
}
|
|
74
|
+
})();
|
|
75
|
+
});
|
|
76
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type OpenClawConfig } from "openclaw/plugin-sdk/core";
|
|
2
|
+
import { type OpenClawPluginApi } from "openclaw/plugin-sdk/irc";
|
|
3
|
+
import { type ClawTaskPayload, type ResolvedClawTaskOpsAccount } from "./types.js";
|
|
4
|
+
/**
|
|
5
|
+
* 将拉取到的任务分发到 OpenClaw 处理
|
|
6
|
+
*/
|
|
7
|
+
export declare function dispatchTaskInbound(params: {
|
|
8
|
+
api: OpenClawPluginApi;
|
|
9
|
+
cfg: OpenClawConfig;
|
|
10
|
+
account: ResolvedClawTaskOpsAccount;
|
|
11
|
+
task: ClawTaskPayload;
|
|
12
|
+
}): Promise<"processed">;
|
|
13
|
+
//# sourceMappingURL=dispatch-inbound.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dispatch-inbound.d.ts","sourceRoot":"","sources":["../../src/dispatch-inbound.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0C,KAAK,cAAc,EAAE,MAAM,0BAA0B,CAAC;AACvG,OAAO,EAIL,KAAK,iBAAiB,EACvB,MAAM,yBAAyB,CAAC;AAIjC,OAAO,EAAc,KAAK,eAAe,EAAE,KAAK,0BAA0B,EAAE,MAAM,YAAY,CAAC;AAuB/F;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,MAAM,EAAE;IAChD,GAAG,EAAE,iBAAiB,CAAC;IACvB,GAAG,EAAE,cAAc,CAAC;IACpB,OAAO,EAAE,0BAA0B,CAAC;IACpC,IAAI,EAAE,eAAe,CAAC;CACvB,GAAG,OAAO,CAAC,WAAW,CAAC,CAsIvB"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk/core";
|
|
2
|
+
import { chunkTextForOutbound, deliverFormattedTextWithAttachments, dispatchInboundReplyWithBase, } from "openclaw/plugin-sdk/irc";
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
import { addTaskLog, feedbackTask } from "./hsf-client.js";
|
|
5
|
+
import { logError, logInfo } from "./logging.js";
|
|
6
|
+
import { CHANNEL_ID } from "./types.js";
|
|
7
|
+
const PEER_ID_HEX_LENGTH = 16;
|
|
8
|
+
function peerIdFromTaskId(taskId) {
|
|
9
|
+
return createHash("sha256").update(taskId, "utf8").digest("hex").slice(0, PEER_ID_HEX_LENGTH);
|
|
10
|
+
}
|
|
11
|
+
function formatTaskAsMessage(task) {
|
|
12
|
+
const parts = [
|
|
13
|
+
`**任务**: ${task.taskName}`,
|
|
14
|
+
`**类型**: ${task.taskType}`,
|
|
15
|
+
`**优先级**: ${task.priority}`,
|
|
16
|
+
`**任务ID**: ${task.taskId}`,
|
|
17
|
+
];
|
|
18
|
+
if (task.payload && Object.keys(task.payload).length > 0) {
|
|
19
|
+
parts.push(`**载荷**:\n\`\`\`json\n${JSON.stringify(task.payload, null, 2)}\n\`\`\``);
|
|
20
|
+
}
|
|
21
|
+
return parts.join("\n");
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* 将拉取到的任务分发到 OpenClaw 处理
|
|
25
|
+
*/
|
|
26
|
+
export async function dispatchTaskInbound(params) {
|
|
27
|
+
const { api, cfg, account, task } = params;
|
|
28
|
+
const t0 = Date.now();
|
|
29
|
+
const accountId = DEFAULT_ACCOUNT_ID;
|
|
30
|
+
const conversationName = `task:${task.taskId}`;
|
|
31
|
+
const peerId = peerIdFromTaskId(task.taskId);
|
|
32
|
+
const messageBody = formatTaskAsMessage(task);
|
|
33
|
+
logInfo(`inbound_dispatch_begin taskId=${task.taskId} taskName=${task.taskName} peerId=${peerId}`);
|
|
34
|
+
// 写入一条开始处理日志
|
|
35
|
+
await addTaskLog(account.gatewayBaseUrl, task.taskId, task.instanceId, "progress", "OpenClaw 开始处理任务");
|
|
36
|
+
const route = api.runtime.channel.routing.resolveAgentRoute({
|
|
37
|
+
cfg,
|
|
38
|
+
channel: CHANNEL_ID,
|
|
39
|
+
accountId: normalizeAccountId(accountId),
|
|
40
|
+
peer: { kind: "group", id: peerId },
|
|
41
|
+
});
|
|
42
|
+
const storePath = api.runtime.channel.session.resolveStorePath(cfg.session?.store, {
|
|
43
|
+
agentId: route.agentId,
|
|
44
|
+
});
|
|
45
|
+
const envelopeOptions = api.runtime.channel.reply.resolveEnvelopeFormatOptions(cfg);
|
|
46
|
+
const createdAt = new Date().toISOString();
|
|
47
|
+
const previousTimestamp = api.runtime.channel.session.readSessionUpdatedAt({
|
|
48
|
+
storePath,
|
|
49
|
+
sessionKey: route.sessionKey,
|
|
50
|
+
});
|
|
51
|
+
const body = api.runtime.channel.reply.formatAgentEnvelope({
|
|
52
|
+
channel: "ClawTaskOps",
|
|
53
|
+
from: conversationName,
|
|
54
|
+
timestamp: Date.parse(createdAt) || undefined,
|
|
55
|
+
previousTimestamp,
|
|
56
|
+
envelope: envelopeOptions,
|
|
57
|
+
body: messageBody,
|
|
58
|
+
});
|
|
59
|
+
const ctxPayload = api.runtime.channel.reply.finalizeInboundContext({
|
|
60
|
+
Body: body,
|
|
61
|
+
BodyForAgent: messageBody,
|
|
62
|
+
RawBody: messageBody,
|
|
63
|
+
CommandBody: messageBody,
|
|
64
|
+
From: `${CHANNEL_ID}:group:${peerId}`,
|
|
65
|
+
To: `${CHANNEL_ID}:${peerId}`,
|
|
66
|
+
SessionKey: route.sessionKey,
|
|
67
|
+
AccountId: route.accountId,
|
|
68
|
+
ChatType: "group",
|
|
69
|
+
ConversationLabel: conversationName,
|
|
70
|
+
SenderName: task.operator || undefined,
|
|
71
|
+
SenderId: task.operatorId || "system",
|
|
72
|
+
GroupSubject: conversationName,
|
|
73
|
+
Provider: CHANNEL_ID,
|
|
74
|
+
Surface: CHANNEL_ID,
|
|
75
|
+
WasMentioned: true,
|
|
76
|
+
MessageSid: `${Date.now()}`,
|
|
77
|
+
Timestamp: Date.parse(createdAt) || undefined,
|
|
78
|
+
OriginatingChannel: CHANNEL_ID,
|
|
79
|
+
OriginatingTo: `${CHANNEL_ID}:${peerId}`,
|
|
80
|
+
CommandAuthorized: true,
|
|
81
|
+
});
|
|
82
|
+
const core = {
|
|
83
|
+
channel: {
|
|
84
|
+
session: {
|
|
85
|
+
recordInboundSession: api.runtime.channel.session.recordInboundSession,
|
|
86
|
+
},
|
|
87
|
+
reply: {
|
|
88
|
+
dispatchReplyWithBufferedBlockDispatcher: api.runtime.channel.reply.dispatchReplyWithBufferedBlockDispatcher,
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
let agentReplyText = "";
|
|
93
|
+
await dispatchInboundReplyWithBase({
|
|
94
|
+
cfg,
|
|
95
|
+
channel: CHANNEL_ID,
|
|
96
|
+
accountId: normalizeAccountId(accountId),
|
|
97
|
+
route,
|
|
98
|
+
storePath,
|
|
99
|
+
ctxPayload,
|
|
100
|
+
core,
|
|
101
|
+
deliver: async (out) => {
|
|
102
|
+
await deliverFormattedTextWithAttachments({
|
|
103
|
+
payload: out,
|
|
104
|
+
send: async ({ text }) => {
|
|
105
|
+
agentReplyText += text + "\n";
|
|
106
|
+
// 将 agent 回复作为日志写回
|
|
107
|
+
const chunks = chunkTextForOutbound(text, 3500);
|
|
108
|
+
const parts = chunks.length > 0 ? chunks : [text];
|
|
109
|
+
for (const part of parts) {
|
|
110
|
+
await addTaskLog(account.gatewayBaseUrl, task.taskId, task.instanceId, "info", `OpenClaw 回复: ${part}`);
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
},
|
|
115
|
+
onRecordError: (err) => {
|
|
116
|
+
logError(`record_session_fail ${String(err)}`);
|
|
117
|
+
},
|
|
118
|
+
onDispatchError: (err, info) => {
|
|
119
|
+
logError(`dispatch_reply_fail kind=${info.kind} err=${String(err)}`);
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
// 处理完成后反馈状态
|
|
123
|
+
await feedbackTask(account.gatewayBaseUrl, task.taskId, "completed", agentReplyText.trim() || null);
|
|
124
|
+
logInfo(`inbound_dispatch_ok durationMs=${Date.now() - t0} taskId=${task.taskId}`);
|
|
125
|
+
return "processed";
|
|
126
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ClawTaskPayload } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* 注册实例(自动注册 + 心跳更新 lastOnlineTime),同时传入 authorizedUsers 和 accessToken
|
|
4
|
+
*/
|
|
5
|
+
export declare function registerInstance(gatewayBaseUrl: string, instanceId: string, name: string, description: string, extConfig: {
|
|
6
|
+
authorizedUsers?: string[];
|
|
7
|
+
accessToken?: string;
|
|
8
|
+
}): Promise<boolean>;
|
|
9
|
+
/**
|
|
10
|
+
* 拉取待处理任务
|
|
11
|
+
*/
|
|
12
|
+
export declare function pullTasks(gatewayBaseUrl: string, instanceId: string, limit: number): Promise<ClawTaskPayload[]>;
|
|
13
|
+
/**
|
|
14
|
+
* 反馈任务处理结果
|
|
15
|
+
*/
|
|
16
|
+
export declare function feedbackTask(gatewayBaseUrl: string, taskId: string, status: string, result: string | null): Promise<boolean>;
|
|
17
|
+
/**
|
|
18
|
+
* 添加任务日志
|
|
19
|
+
*/
|
|
20
|
+
export declare function addTaskLog(gatewayBaseUrl: string, taskId: string, instanceId: string, logType: string, content: string): Promise<boolean>;
|
|
21
|
+
//# sourceMappingURL=hsf-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hsf-client.d.ts","sourceRoot":"","sources":["../../src/hsf-client.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAoDlD;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,cAAc,EAAE,MAAM,EACtB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE;IAAE,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GAC9D,OAAO,CAAC,OAAO,CAAC,CASlB;AAED;;GAEG;AACH,wBAAsB,SAAS,CAC7B,cAAc,EAAE,MAAM,EACtB,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,eAAe,EAAE,CAAC,CAU5B;AAED;;GAEG;AACH,wBAAsB,YAAY,CAChC,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,GAAG,IAAI,GACpB,OAAO,CAAC,OAAO,CAAC,CASlB;AAED;;GAEG;AACH,wBAAsB,UAAU,CAC9B,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,OAAO,CAAC,CAWlB"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { HSF_ADD_TASK_LOG, HSF_APP_NAME, HSF_FEEDBACK_TASK, HSF_GATEWAY_PATH, HSF_PULL_TASKS, HSF_REGISTER_INSTANCE, } from "./constants.js";
|
|
2
|
+
import { logError, logInfo, logWarn } from "./logging.js";
|
|
3
|
+
/**
|
|
4
|
+
* 通用 HSF HTTP 网关调用
|
|
5
|
+
*/
|
|
6
|
+
async function callHsf(gatewayBaseUrl, hsfName, args) {
|
|
7
|
+
const url = `${gatewayBaseUrl}${HSF_GATEWAY_PATH}`;
|
|
8
|
+
const response = await fetch(url, {
|
|
9
|
+
method: "POST",
|
|
10
|
+
headers: { "Content-Type": "application/json" },
|
|
11
|
+
body: JSON.stringify({
|
|
12
|
+
appName: HSF_APP_NAME,
|
|
13
|
+
hsfName,
|
|
14
|
+
args,
|
|
15
|
+
}),
|
|
16
|
+
});
|
|
17
|
+
if (!response.ok) {
|
|
18
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
19
|
+
}
|
|
20
|
+
const result = (await response.json());
|
|
21
|
+
if (result.code !== 0) {
|
|
22
|
+
throw new Error(result.message || result.data?.result?.message || "HSF 调用失败");
|
|
23
|
+
}
|
|
24
|
+
if (result.data?.result) {
|
|
25
|
+
const biz = result.data.result;
|
|
26
|
+
if (!biz.success && biz.code !== "SUCCESS" && biz.code !== 0) {
|
|
27
|
+
throw new Error(biz.message || "HSF 业务调用失败");
|
|
28
|
+
}
|
|
29
|
+
return biz.data;
|
|
30
|
+
}
|
|
31
|
+
return result.data;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* 注册实例(自动注册 + 心跳更新 lastOnlineTime),同时传入 authorizedUsers 和 accessToken
|
|
35
|
+
*/
|
|
36
|
+
export async function registerInstance(gatewayBaseUrl, instanceId, name, description, extConfig) {
|
|
37
|
+
try {
|
|
38
|
+
await callHsf(gatewayBaseUrl, HSF_REGISTER_INSTANCE, [instanceId, name, description, extConfig]);
|
|
39
|
+
logInfo(`registerInstance ok instanceId=${instanceId}`);
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
catch (e) {
|
|
43
|
+
logWarn(`registerInstance failed instanceId=${instanceId} err=${String(e)}`);
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* 拉取待处理任务
|
|
49
|
+
*/
|
|
50
|
+
export async function pullTasks(gatewayBaseUrl, instanceId, limit) {
|
|
51
|
+
try {
|
|
52
|
+
const data = await callHsf(gatewayBaseUrl, HSF_PULL_TASKS, [instanceId, limit]);
|
|
53
|
+
const tasks = data ?? [];
|
|
54
|
+
logInfo(`pullTasks ok instanceId=${instanceId} count=${tasks.length}`);
|
|
55
|
+
return tasks;
|
|
56
|
+
}
|
|
57
|
+
catch (e) {
|
|
58
|
+
logWarn(`pullTasks failed instanceId=${instanceId} err=${String(e)}`);
|
|
59
|
+
return [];
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* 反馈任务处理结果
|
|
64
|
+
*/
|
|
65
|
+
export async function feedbackTask(gatewayBaseUrl, taskId, status, result) {
|
|
66
|
+
try {
|
|
67
|
+
await callHsf(gatewayBaseUrl, HSF_FEEDBACK_TASK, [taskId, status, result]);
|
|
68
|
+
logInfo(`feedbackTask ok taskId=${taskId} status=${status}`);
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
catch (e) {
|
|
72
|
+
logError(`feedbackTask failed taskId=${taskId} err=${String(e)}`);
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* 添加任务日志
|
|
78
|
+
*/
|
|
79
|
+
export async function addTaskLog(gatewayBaseUrl, taskId, instanceId, logType, content) {
|
|
80
|
+
try {
|
|
81
|
+
await callHsf(gatewayBaseUrl, HSF_ADD_TASK_LOG, [
|
|
82
|
+
{ taskId, instanceId, logType, content, operator: "openclaw", operatorId: "openclaw" },
|
|
83
|
+
]);
|
|
84
|
+
logInfo(`addTaskLog ok taskId=${taskId} logType=${logType}`);
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
catch (e) {
|
|
88
|
+
logWarn(`addTaskLog failed taskId=${taskId} err=${String(e)}`);
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare function attachLogger(api: {
|
|
2
|
+
logger?: {
|
|
3
|
+
info?: (m: string) => void;
|
|
4
|
+
warn?: (m: string) => void;
|
|
5
|
+
error?: (m: string) => void;
|
|
6
|
+
};
|
|
7
|
+
}): void;
|
|
8
|
+
export declare function logInfo(msg: string): void;
|
|
9
|
+
export declare function logWarn(msg: string): void;
|
|
10
|
+
export declare function logError(msg: string): void;
|
|
11
|
+
//# sourceMappingURL=logging.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logging.d.ts","sourceRoot":"","sources":["../../src/logging.ts"],"names":[],"mappings":"AAgBA,wBAAgB,YAAY,CAAC,GAAG,EAAE;IAChC,MAAM,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;QAAC,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;QAAC,KAAK,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,CAAC;CAClG,GAAG,IAAI,CAOP;AAED,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAEzC;AAED,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAEzC;AAED,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAE1C"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { CHANNEL_ID } from "./types.js";
|
|
2
|
+
const fallback = {
|
|
3
|
+
info: (m) => console.log(m),
|
|
4
|
+
warn: (m) => console.warn(m),
|
|
5
|
+
error: (m) => console.error(m),
|
|
6
|
+
};
|
|
7
|
+
let sink = {};
|
|
8
|
+
export function attachLogger(api) {
|
|
9
|
+
const L = api.logger;
|
|
10
|
+
sink = {
|
|
11
|
+
info: (m) => (L?.info ? L.info(m) : fallback.info(m)),
|
|
12
|
+
warn: (m) => (L?.warn ? L.warn(m) : fallback.warn(m)),
|
|
13
|
+
error: (m) => (L?.error ? L.error(m) : fallback.error(m)),
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export function logInfo(msg) {
|
|
17
|
+
(sink.info ?? fallback.info)(`[${CHANNEL_ID}] ${msg}`);
|
|
18
|
+
}
|
|
19
|
+
export function logWarn(msg) {
|
|
20
|
+
(sink.warn ?? fallback.warn)(`[${CHANNEL_ID}] ${msg}`);
|
|
21
|
+
}
|
|
22
|
+
export function logError(msg) {
|
|
23
|
+
(sink.error ?? fallback.error)(`[${CHANNEL_ID}] ${msg}`);
|
|
24
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { OpenClawConfig } from "openclaw/plugin-sdk/core";
|
|
2
|
+
export declare const CHANNEL_ID = "claw-task-ops";
|
|
3
|
+
/**
|
|
4
|
+
* 插件配置
|
|
5
|
+
*/
|
|
6
|
+
export type ClawTaskOpsChannelSection = {
|
|
7
|
+
enabled?: boolean;
|
|
8
|
+
/** HSF HTTP 网关地址 */
|
|
9
|
+
gatewayBaseUrl?: string;
|
|
10
|
+
/** 目标实例 ID */
|
|
11
|
+
instanceId?: string;
|
|
12
|
+
/** 实例显示名称(自动注册用) */
|
|
13
|
+
instanceName?: string;
|
|
14
|
+
/** 实例描述(自动注册用) */
|
|
15
|
+
instanceDescription?: string;
|
|
16
|
+
/** 授权用户工号列表,仅列表中的用户可在管理界面查看和操作该实例 */
|
|
17
|
+
authorizedUsers?: string[];
|
|
18
|
+
/** 投递令牌,通过 HTTP/HSF 投递任务时需携带此 token 校验 */
|
|
19
|
+
accessToken?: string;
|
|
20
|
+
/** 轮询间隔(秒),默认 10 */
|
|
21
|
+
pollIntervalSec?: number;
|
|
22
|
+
/** 每次拉取的最大任务数,默认 5 */
|
|
23
|
+
pullLimit?: number;
|
|
24
|
+
};
|
|
25
|
+
export type ResolvedClawTaskOpsAccount = {
|
|
26
|
+
accountId: string;
|
|
27
|
+
gatewayBaseUrl: string;
|
|
28
|
+
instanceId: string;
|
|
29
|
+
instanceName: string;
|
|
30
|
+
instanceDescription: string;
|
|
31
|
+
authorizedUsers: string[];
|
|
32
|
+
accessToken: string;
|
|
33
|
+
pollIntervalSec: number;
|
|
34
|
+
pullLimit: number;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* 拉取到的任务载荷
|
|
38
|
+
*/
|
|
39
|
+
export type ClawTaskPayload = {
|
|
40
|
+
taskId: string;
|
|
41
|
+
instanceId: string;
|
|
42
|
+
taskName: string;
|
|
43
|
+
taskType: string;
|
|
44
|
+
priority: string;
|
|
45
|
+
status: string;
|
|
46
|
+
payload: Record<string, unknown>;
|
|
47
|
+
operator: string;
|
|
48
|
+
operatorId: string;
|
|
49
|
+
createdTime: string;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* HSF 调用的入站消息(映射到 OpenClaw 消息)
|
|
53
|
+
*/
|
|
54
|
+
export type ClawInboundMessage = {
|
|
55
|
+
conversationName: string;
|
|
56
|
+
senderId: string;
|
|
57
|
+
text: string;
|
|
58
|
+
taskId: string;
|
|
59
|
+
instanceId: string;
|
|
60
|
+
};
|
|
61
|
+
export declare function getChannelSection(cfg: OpenClawConfig): ClawTaskOpsChannelSection;
|
|
62
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE/D,eAAO,MAAM,UAAU,kBAAkB,CAAC;AAE1C;;GAEG;AACH,MAAM,MAAM,yBAAyB,GAAG;IACtC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,oBAAoB;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,oBAAoB;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kBAAkB;IAClB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,qCAAqC;IACrC,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,0CAA0C;IAC1C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oBAAoB;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,sBAAsB;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAMF,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,cAAc,GAAG,yBAAyB,CAQhF"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export const CHANNEL_ID = "claw-task-ops";
|
|
2
|
+
export function getChannelSection(cfg) {
|
|
3
|
+
const fromChannels = cfg.channels?.[CHANNEL_ID] ?? {};
|
|
4
|
+
const raw = cfg.plugins?.entries?.[CHANNEL_ID]?.config;
|
|
5
|
+
const fromPlugin = (raw && typeof raw === "object" ? raw : {});
|
|
6
|
+
return { ...fromPlugin, ...fromChannels };
|
|
7
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "claw-task-ops",
|
|
3
|
+
"kind": "channel",
|
|
4
|
+
"channels": ["claw-task-ops"],
|
|
5
|
+
"name": "Claw Task Ops",
|
|
6
|
+
"description": "Polls pending tasks from Claw Task Ops via HSF HTTP gateway, dispatches to OpenClaw agent, feeds back results and processing logs. Open access; no multi-account.",
|
|
7
|
+
"configSchema": {
|
|
8
|
+
"type": "object",
|
|
9
|
+
"additionalProperties": true,
|
|
10
|
+
"description": "生效字段:gatewayBaseUrl、instanceId、instanceName、instanceDescription、enabled。其余键忽略。",
|
|
11
|
+
"properties": {
|
|
12
|
+
"enabled": { "type": "boolean" },
|
|
13
|
+
"gatewayBaseUrl": { "type": "string" },
|
|
14
|
+
"instanceId": { "type": "string" },
|
|
15
|
+
"instanceName": { "type": "string" },
|
|
16
|
+
"instanceDescription": { "type": "string" },
|
|
17
|
+
"authorizedUsers": { "type": "array", "items": { "type": "string" } },
|
|
18
|
+
"accessToken": { "type": "string" },
|
|
19
|
+
"pollIntervalSec": { "type": "number" },
|
|
20
|
+
"pullLimit": { "type": "number" },
|
|
21
|
+
"claw-task-ops": {
|
|
22
|
+
"type": "object",
|
|
23
|
+
"additionalProperties": true,
|
|
24
|
+
"properties": {
|
|
25
|
+
"enabled": { "type": "boolean" },
|
|
26
|
+
"gatewayBaseUrl": { "type": "string" },
|
|
27
|
+
"instanceId": { "type": "string" },
|
|
28
|
+
"instanceName": { "type": "string" },
|
|
29
|
+
"instanceDescription": { "type": "string" },
|
|
30
|
+
"authorizedUsers": { "type": "array", "items": { "type": "string" } },
|
|
31
|
+
"accessToken": { "type": "string" },
|
|
32
|
+
"pollIntervalSec": { "type": "number" },
|
|
33
|
+
"pullLimit": { "type": "number" }
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "openclaw-channel-claw-task-ops",
|
|
3
|
+
"version": "0.1.3",
|
|
4
|
+
"description": "OpenClaw channel plugin for Claw Task Ops: pulls tasks via HSF HTTP gateway, processes them, and feeds back logs",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist",
|
|
10
|
+
"openclaw.plugin.json",
|
|
11
|
+
"README.md"
|
|
12
|
+
],
|
|
13
|
+
"publishConfig": {
|
|
14
|
+
"access": "public",
|
|
15
|
+
"registry": "https://registry.npmjs.org/"
|
|
16
|
+
},
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsc -p tsconfig.json",
|
|
19
|
+
"prepublishOnly": "npm run build",
|
|
20
|
+
"publish:npm": "npm run build && npm publish"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"openclaw",
|
|
24
|
+
"openclaw-plugin",
|
|
25
|
+
"claw-task-ops",
|
|
26
|
+
"task-manager"
|
|
27
|
+
],
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"openclaw": ">=2026.3.24"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/node": "^22.10.1",
|
|
34
|
+
"openclaw": "2026.4.2",
|
|
35
|
+
"typescript": "^5.7.2"
|
|
36
|
+
},
|
|
37
|
+
"openclaw": {
|
|
38
|
+
"hooks": [],
|
|
39
|
+
"extensions": [
|
|
40
|
+
"./dist/index.js"
|
|
41
|
+
],
|
|
42
|
+
"setupEntry": "./dist/setup-entry.js",
|
|
43
|
+
"compat": {
|
|
44
|
+
"pluginApi": ">=2026.3.24-beta.2",
|
|
45
|
+
"minGatewayVersion": "2026.3.24-beta.2"
|
|
46
|
+
},
|
|
47
|
+
"channel": {
|
|
48
|
+
"id": "claw-task-ops",
|
|
49
|
+
"label": "Claw Task Ops",
|
|
50
|
+
"blurb": "Pulls tasks from Claw Task Ops via HSF HTTP gateway, dispatches to OpenClaw for processing, and feeds back results/logs."
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|