openclaw-openagent 1.0.1 → 1.0.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/dist/index.d.ts +25 -0
- package/dist/index.js +105 -0
- package/dist/src/app/channel-tools.d.ts +28 -0
- package/dist/src/app/channel-tools.js +251 -0
- package/dist/src/app/discovery-tools.d.ts +35 -0
- package/dist/src/app/discovery-tools.js +243 -0
- package/dist/src/app/download-file-tool.d.ts +10 -0
- package/dist/src/app/download-file-tool.js +104 -0
- package/dist/src/app/hooks.d.ts +14 -0
- package/dist/src/app/hooks.js +118 -0
- package/dist/src/app/index.d.ts +13 -0
- package/dist/src/app/index.js +44 -0
- package/dist/src/app/messaging-tools.d.ts +11 -0
- package/dist/src/app/messaging-tools.js +79 -0
- package/dist/src/app/ops-tools.d.ts +21 -0
- package/dist/src/app/ops-tools.js +158 -0
- package/dist/src/app/remote-agent-tool.d.ts +27 -0
- package/dist/src/app/remote-agent-tool.js +461 -0
- package/dist/src/app/types.d.ts +61 -0
- package/dist/src/app/types.js +11 -0
- package/dist/src/app/upload-file-tool.d.ts +16 -0
- package/dist/src/app/upload-file-tool.js +353 -0
- package/dist/src/app/verbose-preflight.d.ts +2 -0
- package/dist/src/app/verbose-preflight.js +145 -0
- package/dist/src/auth/config.d.ts +79 -0
- package/dist/src/auth/config.js +133 -0
- package/dist/src/auth/credential-manager.d.ts +65 -0
- package/dist/src/auth/credential-manager.js +122 -0
- package/dist/src/auth/index.d.ts +6 -0
- package/dist/src/auth/index.js +6 -0
- package/dist/src/auth/verify.d.ts +42 -0
- package/dist/src/auth/verify.js +60 -0
- package/dist/src/channel.d.ts +269 -0
- package/dist/src/channel.js +488 -0
- package/dist/src/compat.d.ts +37 -0
- package/dist/src/compat.js +70 -0
- package/dist/src/config/config-schema.d.ts +56 -0
- package/dist/src/config/config-schema.js +34 -0
- package/dist/src/messaging/aggregator.d.ts +25 -0
- package/dist/src/messaging/aggregator.js +90 -0
- package/dist/src/messaging/collector.d.ts +27 -0
- package/dist/src/messaging/collector.js +76 -0
- package/dist/src/messaging/executor.d.ts +14 -0
- package/dist/src/messaging/executor.js +59 -0
- package/dist/src/messaging/inbound.d.ts +97 -0
- package/dist/src/messaging/inbound.js +63 -0
- package/dist/src/messaging/index.d.ts +10 -0
- package/dist/src/messaging/index.js +9 -0
- package/dist/src/messaging/mention-protocol.d.ts +42 -0
- package/dist/src/messaging/mention-protocol.js +74 -0
- package/dist/src/messaging/process-c2c-request.d.ts +55 -0
- package/dist/src/messaging/process-c2c-request.js +445 -0
- package/dist/src/messaging/process-message.d.ts +62 -0
- package/dist/src/messaging/process-message.js +282 -0
- package/dist/src/messaging/scheduler.d.ts +17 -0
- package/dist/src/messaging/scheduler.js +47 -0
- package/dist/src/messaging/types.d.ts +34 -0
- package/dist/src/messaging/types.js +4 -0
- package/dist/src/plugin-ui/assets/openagent-override.js +9278 -0
- package/dist/src/plugin-ui/index.d.ts +13 -0
- package/dist/src/plugin-ui/index.js +16 -0
- package/dist/src/plugin-ui/ui-extension-loader/backup.d.ts +23 -0
- package/dist/src/plugin-ui/ui-extension-loader/backup.js +82 -0
- package/dist/src/plugin-ui/ui-extension-loader/index.d.ts +28 -0
- package/dist/src/plugin-ui/ui-extension-loader/index.js +240 -0
- package/dist/src/plugin-ui/ui-extension-loader/locator.d.ts +35 -0
- package/dist/src/plugin-ui/ui-extension-loader/locator.js +129 -0
- package/dist/src/plugin-ui/ui-extension-loader/manifest.d.ts +26 -0
- package/dist/src/plugin-ui/ui-extension-loader/manifest.js +45 -0
- package/dist/src/plugin-ui/ui-extension-loader/registry-regex.d.ts +9 -0
- package/dist/src/plugin-ui/ui-extension-loader/registry-regex.js +724 -0
- package/dist/src/plugin-ui/ui-extension-loader/removed-extensions.d.ts +8 -0
- package/dist/src/plugin-ui/ui-extension-loader/removed-extensions.js +58 -0
- package/dist/src/plugin-ui/ui-extension-loader/types.d.ts +55 -0
- package/dist/src/plugin-ui/ui-extension-loader/types.js +23 -0
- package/dist/src/proxy/auth-proxy.d.ts +20 -0
- package/dist/src/proxy/auth-proxy.js +337 -0
- package/dist/src/runtime/account.d.ts +156 -0
- package/dist/src/runtime/account.js +491 -0
- package/dist/src/runtime/index.d.ts +5 -0
- package/dist/src/runtime/index.js +6 -0
- package/dist/src/runtime/plugin-runtime.d.ts +40 -0
- package/dist/src/runtime/plugin-runtime.js +72 -0
- package/dist/src/runtime/registry.d.ts +41 -0
- package/dist/src/runtime/registry.js +60 -0
- package/dist/src/sdk/CLASS_MAP.md +143 -0
- package/dist/src/sdk/index.d.ts +126 -0
- package/dist/src/sdk/index.js +23990 -0
- package/dist/src/sdk/modules/cloud-search-module.js +1117 -0
- package/dist/src/sdk/modules/follow-module.js +1069 -0
- package/dist/src/sdk/modules/group-module.js +7397 -0
- package/dist/src/sdk/modules/relationship-module.js +2269 -0
- package/dist/src/sdk/modules/signaling-module.js +1468 -0
- package/dist/src/sdk/modules/tim-upload-plugin.js +730 -0
- package/dist/src/sdk/node-env/http-request.js +90 -0
- package/dist/src/sdk/node-env/index.js +57 -0
- package/dist/src/sdk/node-env/storage.js +114 -0
- package/dist/src/sdk/package.json +10 -0
- package/dist/src/sdk/sdk/CLASS_MAP.md +143 -0
- package/dist/src/sdk/sdk/index.d.ts +126 -0
- package/dist/src/sdk/sdk/index.js +23990 -0
- package/dist/src/sdk/sdk/modules/cloud-search-module.js +1117 -0
- package/dist/src/sdk/sdk/modules/follow-module.js +1069 -0
- package/dist/src/sdk/sdk/modules/group-module.js +7397 -0
- package/dist/src/sdk/sdk/modules/relationship-module.js +2269 -0
- package/dist/src/sdk/sdk/modules/signaling-module.js +1468 -0
- package/dist/src/sdk/sdk/modules/tim-upload-plugin.js +730 -0
- package/dist/src/sdk/sdk/node-env/http-request.js +90 -0
- package/dist/src/sdk/sdk/node-env/index.js +57 -0
- package/dist/src/sdk/sdk/node-env/storage.js +114 -0
- package/dist/src/sdk/sdk/package.json +10 -0
- package/dist/src/sdk/sdk/tsconfig.json +16 -0
- package/dist/src/sdk/tsconfig.json +16 -0
- package/dist/src/state/pending-invocation-store.d.ts +26 -0
- package/dist/src/state/pending-invocation-store.js +39 -0
- package/dist/src/state/store.d.ts +165 -0
- package/dist/src/state/store.js +535 -0
- package/dist/src/tim/c2c.d.ts +83 -0
- package/dist/src/tim/c2c.js +318 -0
- package/dist/src/tim/channels.d.ts +95 -0
- package/dist/src/tim/channels.js +279 -0
- package/dist/src/tim/client.d.ts +54 -0
- package/dist/src/tim/client.js +268 -0
- package/dist/src/tim/index.d.ts +6 -0
- package/dist/src/tim/index.js +6 -0
- package/dist/src/tim/messages.d.ts +50 -0
- package/dist/src/tim/messages.js +104 -0
- package/dist/src/tim/sdk-logger-init.d.ts +13 -0
- package/dist/src/tim/sdk-logger-init.js +46 -0
- package/dist/src/tools.d.ts +9 -0
- package/dist/src/tools.js +8 -0
- package/dist/src/transport/factory.d.ts +63 -0
- package/dist/src/transport/factory.js +54 -0
- package/dist/src/transport/oasn/index.d.ts +12 -0
- package/dist/src/transport/oasn/index.js +9 -0
- package/dist/src/transport/oasn/oasn-agent-card.d.ts +38 -0
- package/dist/src/transport/oasn/oasn-agent-card.js +102 -0
- package/dist/src/transport/oasn/oasn-discovery.d.ts +33 -0
- package/dist/src/transport/oasn/oasn-discovery.js +97 -0
- package/dist/src/transport/oasn/oasn-files.d.ts +64 -0
- package/dist/src/transport/oasn/oasn-files.js +174 -0
- package/dist/src/transport/oasn/oasn-http.d.ts +98 -0
- package/dist/src/transport/oasn/oasn-http.js +362 -0
- package/dist/src/transport/oasn/oasn-invocation.d.ts +154 -0
- package/dist/src/transport/oasn/oasn-invocation.js +432 -0
- package/dist/src/transport/oasn/oasn-normalize.d.ts +6 -0
- package/dist/src/transport/oasn/oasn-normalize.js +112 -0
- package/dist/src/transport/oasn/oasn-register.d.ts +19 -0
- package/dist/src/transport/oasn/oasn-register.js +24 -0
- package/dist/src/transport/oasn/oasn-transport.d.ts +114 -0
- package/dist/src/transport/oasn/oasn-transport.js +230 -0
- package/dist/src/transport/oasn/oasn-types.d.ts +331 -0
- package/dist/src/transport/oasn/oasn-types.js +44 -0
- package/dist/src/transport/tim/index.d.ts +7 -0
- package/dist/src/transport/tim/index.js +6 -0
- package/dist/src/transport/tim/tim-transport.d.ts +122 -0
- package/dist/src/transport/tim/tim-transport.js +402 -0
- package/dist/src/transport/types.d.ts +450 -0
- package/dist/src/transport/types.js +38 -0
- package/dist/src/util/http.d.ts +21 -0
- package/dist/src/util/http.js +93 -0
- package/dist/src/util/logger.d.ts +20 -0
- package/dist/src/util/logger.js +100 -0
- package/dist/src/util/url-resolver.d.ts +7 -0
- package/dist/src/util/url-resolver.js +20 -0
- package/index.ts +11 -0
- package/openclaw.plugin.json +9 -0
- package/package.json +7 -4
- package/src/app/download-file-tool.ts +133 -0
- package/src/app/hooks.ts +89 -5
- package/src/app/index.ts +6 -0
- package/src/app/remote-agent-tool.ts +46 -0
- package/src/app/types.ts +1 -0
- package/src/app/upload-file-tool.ts +411 -0
- package/src/plugin-ui/assets/openagent-override.js +15 -66
- package/src/plugin-ui/modules/agent-book/panel/agent-book.js +0 -61
- package/src/plugin-ui/modules/agent-book/panel/agent-data.js +0 -1
- package/src/plugin-ui/modules/agent-book/panel/inject-ui.js +14 -3
- package/src/plugin-ui/ui-extension-loader/registry-regex.ts +102 -63
- package/src/runtime/account.ts +0 -23
- package/src/transport/oasn/oasn-files.ts +5 -5
- package/src/transport/oasn/oasn-invocation.ts +44 -2
- package/src/transport/oasn/oasn-normalize.ts +0 -26
- package/src/transport/oasn/oasn-register.ts +8 -81
- package/src/transport/oasn/oasn-transport.ts +9 -32
- package/src/transport/oasn/oasn-types.ts +53 -6
- package/src/transport/types.ts +33 -36
- package/src/util/url-resolver.ts +17 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OASN API 请求/响应类型定义
|
|
3
|
+
*
|
|
4
|
+
* 设计依据:
|
|
5
|
+
* - docs/specs/2026-06-16-oasn-api-module-mapping.md
|
|
6
|
+
* - oasn-01-onboarding/apis.md(agent / claim / auth / agent-card / discovery)
|
|
7
|
+
* - oasn-02-access-layer/apis.md(invocation / file / artifact)
|
|
8
|
+
*
|
|
9
|
+
* 这些类型仅在 OASN transport 内部使用,对外暴露的通用类型在 src/transport/types.ts。
|
|
10
|
+
* onboarding controller 的 Java record 当前使用 camelCase 请求/响应字段;
|
|
11
|
+
* transport 层兼容历史 snake_case 响应,并对外归一到 src/transport/types.ts 的稳定模型。
|
|
12
|
+
*/
|
|
13
|
+
/** 是否进入终态(停止轮询) */
|
|
14
|
+
export function isInvocationTerminal(status) {
|
|
15
|
+
return status === 'succeeded'
|
|
16
|
+
|| status === 'failed'
|
|
17
|
+
|| status === 'timed_out'
|
|
18
|
+
|| status === 'cancelled';
|
|
19
|
+
}
|
|
20
|
+
// ═══════════════════════════════════════════════════════════════
|
|
21
|
+
// 端点常量
|
|
22
|
+
// ═══════════════════════════════════════════════════════════════
|
|
23
|
+
/** OASN HTTP 端点路径(相对于 api_base) */
|
|
24
|
+
export const OASN_ENDPOINTS = {
|
|
25
|
+
// Agent
|
|
26
|
+
REGISTER: '/api/agents/register',
|
|
27
|
+
CLAIM: '/api/agents/claim',
|
|
28
|
+
AGENT_STATUS: (agentId) => `/api/agents/${encodeURIComponent(agentId)}`,
|
|
29
|
+
// AgentCard
|
|
30
|
+
AGENT_CARD: (agentId) => `/api/agent-cards/${encodeURIComponent(agentId)}`,
|
|
31
|
+
// Discovery
|
|
32
|
+
CATEGORIES: '/api/agent-card-categories',
|
|
33
|
+
SEARCH_BY_QUERY: '/api/agent-cards/search/by-query',
|
|
34
|
+
SEARCH_BY_CATEGORY: '/api/agent-cards/search/by-category',
|
|
35
|
+
SEARCH_BY_TAGS: '/api/agent-cards/search/by-tags',
|
|
36
|
+
// Invocation
|
|
37
|
+
INVOCATIONS: '/api/invocations',
|
|
38
|
+
INVOCATION: (id) => `/api/invocations/${encodeURIComponent(id)}`,
|
|
39
|
+
// File / Artifact
|
|
40
|
+
FILES: '/api/files',
|
|
41
|
+
ARTIFACT_CONTENT: (id) => `/api/artifacts/${encodeURIComponent(id)}/content`,
|
|
42
|
+
// Owner
|
|
43
|
+
USERS_ME: '/api/users/me',
|
|
44
|
+
};
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TimTransport —— 把现有 TIM SDK 代码封装到 AgentTransport 接口背后
|
|
3
|
+
*
|
|
4
|
+
* 设计依据:docs/specs/2026-06-16-oasn-transport-abstraction-design.md §2.3
|
|
5
|
+
*
|
|
6
|
+
* 实现策略:
|
|
7
|
+
* - 直接复用现有 `src/tim/{client,c2c,messages,channels}.ts` 模块(不复制粘贴)
|
|
8
|
+
* - 内部持有自己的 TIMClient 实例,不依赖 `runtime/registry`
|
|
9
|
+
* - `sendC2CCustomMessage` 在 transport 内部就地重新实现一份(不用 registry),
|
|
10
|
+
* 避免 c2c.ts 中的 `registry.getDefault()` 反向依赖运行时
|
|
11
|
+
* - waitForC2CReply / 进度回调走原版 pendingRequests Map(c2c.ts 模块级单例 OK,
|
|
12
|
+
* 因为 TimTransport 同进程只会有一个 active 实例)
|
|
13
|
+
*
|
|
14
|
+
* 不实现的方法:
|
|
15
|
+
* - registerAgent / claimAgent / updateAgentCard / uploadFile / downloadArtifact
|
|
16
|
+
* 这些是 OASN-only,TimTransport 不在能力接口里声明,调用方需先 narrow 类型。
|
|
17
|
+
*
|
|
18
|
+
* 注:当前是「在 openagent 基础上原地改造」,所以 TIM 代码仍存于 src/tim/ 而非 src/transport/tim/。
|
|
19
|
+
* 等独立 npm 包发布时再做物理迁移。
|
|
20
|
+
*/
|
|
21
|
+
import { EventEmitter } from 'node:events';
|
|
22
|
+
import { TIMClient } from '../../tim/client.js';
|
|
23
|
+
import type { AgentCard, CategoryTree, ChannelInfo, CoreTransportEvent, MemberInfo, ProgressCallback, SearchQuery, SearchResult, TaskHandle, TaskHandler, TaskRequest, TaskResult, TimOnlyTransportEvent, TimTransport, TransportCredentials, TransportMessage } from '../types.js';
|
|
24
|
+
export interface TimTransportInitOptions {
|
|
25
|
+
/** 实例 id(多账户场景区分用) */
|
|
26
|
+
accountId: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* TimTransport 具体实现。
|
|
30
|
+
*
|
|
31
|
+
* 事件路由:
|
|
32
|
+
* - TIMClient 的 'message' / 'c2c_request' / 'credential_expired' / 'reconnected'
|
|
33
|
+
* 在此处转发为 TransportEvent('message' / 'task_received' / 'credential_expired' / 'reconnected')
|
|
34
|
+
* - 'task_progress' 不通过 EventEmitter 触发 —— C2C 进度走 c2c.ts 的 pendingRequests
|
|
35
|
+
* 回调(waitForC2CReply 的 onProgress 参数),与 sendTask 调用方直接绑定
|
|
36
|
+
*/
|
|
37
|
+
export declare class TimTransportImpl extends EventEmitter implements TimTransport {
|
|
38
|
+
readonly transportType: "tim";
|
|
39
|
+
readonly id: string;
|
|
40
|
+
private _client;
|
|
41
|
+
/** 用户注册的 T3 任务处理器(onTaskReceived) */
|
|
42
|
+
private _taskHandler;
|
|
43
|
+
/** 当前是否就绪(= TIMClient SDK_READY) */
|
|
44
|
+
private _isReady;
|
|
45
|
+
constructor(options: TimTransportInitOptions);
|
|
46
|
+
get isReady(): boolean;
|
|
47
|
+
/**
|
|
48
|
+
* 验证凭证并登录 TIM SDK。
|
|
49
|
+
*
|
|
50
|
+
* TransportCredentials → TIMCredentials 字段映射:
|
|
51
|
+
* identity → userId
|
|
52
|
+
* token → userSig
|
|
53
|
+
* 注:sdkAppId 来自外部 verify 接口结果,但在 AccountRuntime 拼装时
|
|
54
|
+
* 会通过 TransportCredentials 之外的"已知字段"传入 —— 实际我们走旧逻辑:
|
|
55
|
+
* credentials 直接是 TIMCredentials 子集 + sdkAppId 通过 verify 返回的结构。
|
|
56
|
+
*
|
|
57
|
+
* 为了类型安全,本方法接收的 credentials **必须**额外带 sdkAppId(通过
|
|
58
|
+
* TIM-专属扩展类型 TimCredentialsExt 传入,详见 _resolveCredentials in account.ts)。
|
|
59
|
+
*/
|
|
60
|
+
ready(credentials: TransportCredentials): Promise<void>;
|
|
61
|
+
dispose(): Promise<void>;
|
|
62
|
+
on(event: CoreTransportEvent | TimOnlyTransportEvent, handler: (...args: any[]) => void): this;
|
|
63
|
+
off(event: CoreTransportEvent | TimOnlyTransportEvent, handler: (...args: any[]) => void): this;
|
|
64
|
+
/**
|
|
65
|
+
* 发送 C2C 任务请求。
|
|
66
|
+
*
|
|
67
|
+
* TIM C2C 无独立的 invocation 概念,所以 TaskHandle.invocationId = requestId。
|
|
68
|
+
* sessionId / effectiveTimeoutMs 字段 undefined。
|
|
69
|
+
*/
|
|
70
|
+
sendTask(targetAgentId: string, task: TaskRequest): Promise<TaskHandle>;
|
|
71
|
+
/**
|
|
72
|
+
* 等待 C2C 回复(task_progress + task_result)。
|
|
73
|
+
* 进度回调通过 c2c.ts 的 pendingRequests 注册。
|
|
74
|
+
*/
|
|
75
|
+
waitForTaskResult(handle: TaskHandle, options: {
|
|
76
|
+
timeoutMs: number;
|
|
77
|
+
signal?: AbortSignal;
|
|
78
|
+
onProgress?: ProgressCallback;
|
|
79
|
+
}): Promise<TaskResult>;
|
|
80
|
+
searchAgents(query: SearchQuery): Promise<SearchResult>;
|
|
81
|
+
getAgent(agentId: string): Promise<AgentCard | null>;
|
|
82
|
+
/** TIM 没有 category 概念,返回空树 */
|
|
83
|
+
getCategories(): Promise<CategoryTree>;
|
|
84
|
+
onTaskReceived(handler: TaskHandler): void;
|
|
85
|
+
sendGroupMessage(channelId: string, text: string, atUserList?: string[]): Promise<{
|
|
86
|
+
ok: boolean;
|
|
87
|
+
messageId: string;
|
|
88
|
+
}>;
|
|
89
|
+
fetchGroupHistory(channelId: string, count?: number): Promise<TransportMessage[]>;
|
|
90
|
+
listChannels(): Promise<ChannelInfo[]>;
|
|
91
|
+
searchChannels(keyword: string): Promise<ChannelInfo[]>;
|
|
92
|
+
joinChannel(channelId: string): Promise<{
|
|
93
|
+
success: boolean;
|
|
94
|
+
message: string;
|
|
95
|
+
}>;
|
|
96
|
+
leaveChannel(channelId: string): Promise<{
|
|
97
|
+
success: boolean;
|
|
98
|
+
message: string;
|
|
99
|
+
}>;
|
|
100
|
+
getMembers(channelId: string): Promise<MemberInfo[]>;
|
|
101
|
+
createChannel(name: string, desc?: string): Promise<{
|
|
102
|
+
success: boolean;
|
|
103
|
+
channelId: string;
|
|
104
|
+
}>;
|
|
105
|
+
getChannelSkill(channelId: string): Promise<string | null>;
|
|
106
|
+
setChannelSkill(channelId: string, skill: string): Promise<{
|
|
107
|
+
success: boolean;
|
|
108
|
+
}>;
|
|
109
|
+
/** 暴露底层 TIMClient 给老代码兼容(如 src/runtime/registry 等) */
|
|
110
|
+
get _internalClient(): TIMClient | null;
|
|
111
|
+
private _ensureReady;
|
|
112
|
+
/**
|
|
113
|
+
* sendC2CCustomMessage 的 transport-local 实现。
|
|
114
|
+
* 不走 runtime/registry —— 直接用 this._client 持有的 SDK 实例。
|
|
115
|
+
*
|
|
116
|
+
* 与 src/tim/c2c.ts 中的同名函数行为等价(含 10s SDK guard),
|
|
117
|
+
* 但避免了"transport 反向依赖 runtime"。
|
|
118
|
+
*/
|
|
119
|
+
private _sendC2CCustom;
|
|
120
|
+
/** 调用注册的 TaskHandler 并把结果通过 C2C 回写给请求方 */
|
|
121
|
+
private _invokeTaskHandler;
|
|
122
|
+
}
|
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TimTransport —— 把现有 TIM SDK 代码封装到 AgentTransport 接口背后
|
|
3
|
+
*
|
|
4
|
+
* 设计依据:docs/specs/2026-06-16-oasn-transport-abstraction-design.md §2.3
|
|
5
|
+
*
|
|
6
|
+
* 实现策略:
|
|
7
|
+
* - 直接复用现有 `src/tim/{client,c2c,messages,channels}.ts` 模块(不复制粘贴)
|
|
8
|
+
* - 内部持有自己的 TIMClient 实例,不依赖 `runtime/registry`
|
|
9
|
+
* - `sendC2CCustomMessage` 在 transport 内部就地重新实现一份(不用 registry),
|
|
10
|
+
* 避免 c2c.ts 中的 `registry.getDefault()` 反向依赖运行时
|
|
11
|
+
* - waitForC2CReply / 进度回调走原版 pendingRequests Map(c2c.ts 模块级单例 OK,
|
|
12
|
+
* 因为 TimTransport 同进程只会有一个 active 实例)
|
|
13
|
+
*
|
|
14
|
+
* 不实现的方法:
|
|
15
|
+
* - registerAgent / claimAgent / updateAgentCard / uploadFile / downloadArtifact
|
|
16
|
+
* 这些是 OASN-only,TimTransport 不在能力接口里声明,调用方需先 narrow 类型。
|
|
17
|
+
*
|
|
18
|
+
* 注:当前是「在 openagent 基础上原地改造」,所以 TIM 代码仍存于 src/tim/ 而非 src/transport/tim/。
|
|
19
|
+
* 等独立 npm 包发布时再做物理迁移。
|
|
20
|
+
*/
|
|
21
|
+
import { EventEmitter } from 'node:events';
|
|
22
|
+
import { logger } from '../../util/logger.js';
|
|
23
|
+
import { TIMClient } from '../../tim/client.js';
|
|
24
|
+
import { initC2CHandler, waitForC2CReply, } from '../../tim/c2c.js';
|
|
25
|
+
import { sendMessage as timSendMessage, fetchMessages as timFetchMessages, } from '../../tim/messages.js';
|
|
26
|
+
import { listChannels as timListChannels, searchChannels as timSearchChannels, joinChannel as timJoinChannel, leaveChannel as timLeaveChannel, getMembers as timGetMembers, createChannel as timCreateChannel, getChannelSkill as timGetChannelSkill, setChannelSkill as timSetChannelSkill, } from '../../tim/channels.js';
|
|
27
|
+
import { makeTransportError } from '../types.js';
|
|
28
|
+
// 内部 SDK 引用 —— 仅用于 sendC2CCustomMessage 重新实现
|
|
29
|
+
// (把 c2c.ts 中依赖 registry 的部分搬到 transport 内)
|
|
30
|
+
// @ts-ignore — vendor JS module, typed as unknown
|
|
31
|
+
import TencentCloudChat from '../../sdk/index.js';
|
|
32
|
+
// ───────────────────────────────────────────────────────────────
|
|
33
|
+
// 实现类
|
|
34
|
+
// ───────────────────────────────────────────────────────────────
|
|
35
|
+
/**
|
|
36
|
+
* TimTransport 具体实现。
|
|
37
|
+
*
|
|
38
|
+
* 事件路由:
|
|
39
|
+
* - TIMClient 的 'message' / 'c2c_request' / 'credential_expired' / 'reconnected'
|
|
40
|
+
* 在此处转发为 TransportEvent('message' / 'task_received' / 'credential_expired' / 'reconnected')
|
|
41
|
+
* - 'task_progress' 不通过 EventEmitter 触发 —— C2C 进度走 c2c.ts 的 pendingRequests
|
|
42
|
+
* 回调(waitForC2CReply 的 onProgress 参数),与 sendTask 调用方直接绑定
|
|
43
|
+
*/
|
|
44
|
+
export class TimTransportImpl extends EventEmitter {
|
|
45
|
+
transportType = 'tim';
|
|
46
|
+
id;
|
|
47
|
+
_client = null;
|
|
48
|
+
/** 用户注册的 T3 任务处理器(onTaskReceived) */
|
|
49
|
+
_taskHandler = null;
|
|
50
|
+
/** 当前是否就绪(= TIMClient SDK_READY) */
|
|
51
|
+
_isReady = false;
|
|
52
|
+
constructor(options) {
|
|
53
|
+
super();
|
|
54
|
+
this.id = options.accountId;
|
|
55
|
+
}
|
|
56
|
+
get isReady() {
|
|
57
|
+
return this._isReady && (this._client?.isReady ?? false);
|
|
58
|
+
}
|
|
59
|
+
// ─────────────────────────────────────────────────────────────
|
|
60
|
+
// CoreTransport
|
|
61
|
+
// ─────────────────────────────────────────────────────────────
|
|
62
|
+
/**
|
|
63
|
+
* 验证凭证并登录 TIM SDK。
|
|
64
|
+
*
|
|
65
|
+
* TransportCredentials → TIMCredentials 字段映射:
|
|
66
|
+
* identity → userId
|
|
67
|
+
* token → userSig
|
|
68
|
+
* 注:sdkAppId 来自外部 verify 接口结果,但在 AccountRuntime 拼装时
|
|
69
|
+
* 会通过 TransportCredentials 之外的"已知字段"传入 —— 实际我们走旧逻辑:
|
|
70
|
+
* credentials 直接是 TIMCredentials 子集 + sdkAppId 通过 verify 返回的结构。
|
|
71
|
+
*
|
|
72
|
+
* 为了类型安全,本方法接收的 credentials **必须**额外带 sdkAppId(通过
|
|
73
|
+
* TIM-专属扩展类型 TimCredentialsExt 传入,详见 _resolveCredentials in account.ts)。
|
|
74
|
+
*/
|
|
75
|
+
async ready(credentials) {
|
|
76
|
+
// 从扩展字段中读取 sdkAppId(仅 TIM 路径会传入)
|
|
77
|
+
const sdkAppId = credentials.sdkAppId;
|
|
78
|
+
if (!sdkAppId) {
|
|
79
|
+
throw makeTransportError('TIM_MISSING_SDK_APP_ID', 'TimTransport.ready: credentials missing sdkAppId (TIM-specific field)');
|
|
80
|
+
}
|
|
81
|
+
const timCreds = {
|
|
82
|
+
sdkAppId,
|
|
83
|
+
userSig: credentials.token,
|
|
84
|
+
userId: credentials.identity,
|
|
85
|
+
// expire 是相对秒数(TIM 约定),而 TransportCredentials.expiresAt 是绝对 epoch ms。
|
|
86
|
+
// 这里换算回相对秒数(最低 1 小时,避免 0)。
|
|
87
|
+
expire: credentials.expiresAt > 0
|
|
88
|
+
? Math.max(3600, Math.floor((credentials.expiresAt - Date.now()) / 1000))
|
|
89
|
+
: 604800,
|
|
90
|
+
};
|
|
91
|
+
// 创建并连接 TIMClient
|
|
92
|
+
this._client = new TIMClient();
|
|
93
|
+
// SDK 'credential_expired' → transport 'credential_expired'(核心事件)
|
|
94
|
+
this._client.on('credential_expired', () => {
|
|
95
|
+
logger.warn(`[transport/tim] credential_expired emitted (account=${this.id})`);
|
|
96
|
+
this._isReady = false;
|
|
97
|
+
this.emit('credential_expired');
|
|
98
|
+
});
|
|
99
|
+
// SDK 'reconnected' → transport 'reconnected'
|
|
100
|
+
this._client.on('reconnected', () => {
|
|
101
|
+
logger.info(`[transport/tim] reconnected (account=${this.id})`);
|
|
102
|
+
this._isReady = true;
|
|
103
|
+
this.emit('reconnected');
|
|
104
|
+
});
|
|
105
|
+
// 群聊消息 → transport 'message'(仅 TIM-only 订阅者收到)
|
|
106
|
+
this._client.on('message', (msg) => {
|
|
107
|
+
// 适配 RawPushMessage → TransportMessage(字段语义一致)
|
|
108
|
+
const transportMsg = {
|
|
109
|
+
id: msg.id,
|
|
110
|
+
channelId: msg.channelId,
|
|
111
|
+
from: msg.from,
|
|
112
|
+
nick: msg.nick,
|
|
113
|
+
text: msg.text,
|
|
114
|
+
time: msg.time,
|
|
115
|
+
mentionsMe: msg.mentionsMe,
|
|
116
|
+
};
|
|
117
|
+
this.emit('message', transportMsg);
|
|
118
|
+
});
|
|
119
|
+
// C2C 入站任务请求 → transport 'task_received'(T3 路径)
|
|
120
|
+
// initC2CHandler 注册了 c2c.ts 内部的 pendingRequests 监听 +
|
|
121
|
+
// 'c2c_request' 自定义事件(task_request 类型转发)
|
|
122
|
+
this._client.on('c2c_request', (raw) => {
|
|
123
|
+
const req = {
|
|
124
|
+
from: raw.from,
|
|
125
|
+
requestId: raw.payload.request_id,
|
|
126
|
+
task: raw.payload.task,
|
|
127
|
+
};
|
|
128
|
+
// 优先调用注册的 TaskHandler;同时 emit 事件给那些只想旁听的订阅者
|
|
129
|
+
this.emit('task_received', req);
|
|
130
|
+
if (this._taskHandler) {
|
|
131
|
+
// 异步处理,不阻塞 c2c handler
|
|
132
|
+
this._invokeTaskHandler(req).catch((err) => logger.error(`[transport/tim] task handler error: ${err.message}`));
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
// 实际登录
|
|
136
|
+
await this._client.connect(timCreds);
|
|
137
|
+
// 初始化 C2C 消息处理器(解析 task_request/progress/result 自定义消息)
|
|
138
|
+
initC2CHandler(this._client);
|
|
139
|
+
this._isReady = true;
|
|
140
|
+
logger.info(`[transport/tim] ready (account=${this.id} agentId=${credentials.identity})`);
|
|
141
|
+
}
|
|
142
|
+
async dispose() {
|
|
143
|
+
this._isReady = false;
|
|
144
|
+
if (this._client) {
|
|
145
|
+
try {
|
|
146
|
+
await this._client.disconnect();
|
|
147
|
+
}
|
|
148
|
+
catch (err) {
|
|
149
|
+
logger.warn(`[transport/tim] disconnect error: ${err.message}`);
|
|
150
|
+
}
|
|
151
|
+
this._client = null;
|
|
152
|
+
}
|
|
153
|
+
this.removeAllListeners();
|
|
154
|
+
}
|
|
155
|
+
// EventEmitter.on 已经支持 string;这里只是收窄类型,让上层只能订阅
|
|
156
|
+
// CoreTransportEvent | TimOnlyTransportEvent。
|
|
157
|
+
on(event, handler) {
|
|
158
|
+
return super.on(event, handler);
|
|
159
|
+
}
|
|
160
|
+
off(event, handler) {
|
|
161
|
+
return super.off(event, handler);
|
|
162
|
+
}
|
|
163
|
+
// ─────────────────────────────────────────────────────────────
|
|
164
|
+
// TaskTransport
|
|
165
|
+
// ─────────────────────────────────────────────────────────────
|
|
166
|
+
/**
|
|
167
|
+
* 发送 C2C 任务请求。
|
|
168
|
+
*
|
|
169
|
+
* TIM C2C 无独立的 invocation 概念,所以 TaskHandle.invocationId = requestId。
|
|
170
|
+
* sessionId / effectiveTimeoutMs 字段 undefined。
|
|
171
|
+
*/
|
|
172
|
+
async sendTask(targetAgentId, task) {
|
|
173
|
+
this._ensureReady();
|
|
174
|
+
const payload = {
|
|
175
|
+
type: 'openagent_task_request',
|
|
176
|
+
request_id: task.requestId,
|
|
177
|
+
task: task.task,
|
|
178
|
+
session_mode: 'default',
|
|
179
|
+
};
|
|
180
|
+
await this._sendC2CCustom(targetAgentId, payload);
|
|
181
|
+
return {
|
|
182
|
+
invocationId: task.requestId,
|
|
183
|
+
// sessionId / effectiveTimeoutMs 故意不返回
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* 等待 C2C 回复(task_progress + task_result)。
|
|
188
|
+
* 进度回调通过 c2c.ts 的 pendingRequests 注册。
|
|
189
|
+
*/
|
|
190
|
+
async waitForTaskResult(handle, options) {
|
|
191
|
+
const reply = await waitForC2CReply(handle.invocationId, {
|
|
192
|
+
timeoutMs: options.timeoutMs,
|
|
193
|
+
signal: options.signal,
|
|
194
|
+
onProgress: options.onProgress
|
|
195
|
+
? (content) => options.onProgress({
|
|
196
|
+
requestId: handle.invocationId,
|
|
197
|
+
content,
|
|
198
|
+
})
|
|
199
|
+
: undefined,
|
|
200
|
+
});
|
|
201
|
+
// mediaUrls (本地文件路径) → ArtifactRef[]
|
|
202
|
+
// 注:TIM 路径走的是本地磁盘路径,contentUrl 直接是 file:// 风格的本地路径,
|
|
203
|
+
// 上层 remote-agent-tool.ts 知道如何处理(fs.readFile 而非 fetch)。
|
|
204
|
+
const artifacts = reply.mediaUrls?.map((path, idx) => ({
|
|
205
|
+
id: `tim-file-${idx}`,
|
|
206
|
+
displayName: path.split('/').pop() ?? `file-${idx}`,
|
|
207
|
+
mimeType: 'application/octet-stream',
|
|
208
|
+
sizeBytes: 0,
|
|
209
|
+
contentAvailable: true,
|
|
210
|
+
contentUrl: path,
|
|
211
|
+
}));
|
|
212
|
+
return {
|
|
213
|
+
requestId: handle.invocationId,
|
|
214
|
+
status: reply.status,
|
|
215
|
+
content: reply.content,
|
|
216
|
+
...(artifacts && artifacts.length > 0 ? { artifacts } : {}),
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
// ─────────────────────────────────────────────────────────────
|
|
220
|
+
// DiscoveryTransport(TIM 把 channel 列表伪装成 AgentCard)
|
|
221
|
+
// ─────────────────────────────────────────────────────────────
|
|
222
|
+
async searchAgents(query) {
|
|
223
|
+
this._ensureReady();
|
|
224
|
+
const channels = query.query
|
|
225
|
+
? await timSearchChannels(this._client, query.query)
|
|
226
|
+
: await timListChannels(this._client);
|
|
227
|
+
const cards = channels.map((ch) => ({
|
|
228
|
+
agent_id: ch.id,
|
|
229
|
+
agent_type: 'service',
|
|
230
|
+
name: ch.name,
|
|
231
|
+
description: ch.desc,
|
|
232
|
+
MOM: null,
|
|
233
|
+
tags: [],
|
|
234
|
+
category: [],
|
|
235
|
+
avatar_url: null,
|
|
236
|
+
}));
|
|
237
|
+
return {
|
|
238
|
+
searchId: `tim-search-${Date.now()}`,
|
|
239
|
+
agentCards: cards,
|
|
240
|
+
pageNum: 1,
|
|
241
|
+
pageSize: cards.length,
|
|
242
|
+
hasNext: false,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
async getAgent(agentId) {
|
|
246
|
+
// TIM 没有 AgentCard 概念,按 channelId 反查(如果是 channel id)
|
|
247
|
+
this._ensureReady();
|
|
248
|
+
const all = await timListChannels(this._client);
|
|
249
|
+
const ch = all.find((c) => c.id === agentId);
|
|
250
|
+
if (!ch)
|
|
251
|
+
return null;
|
|
252
|
+
return {
|
|
253
|
+
agent_id: ch.id,
|
|
254
|
+
agent_type: 'service',
|
|
255
|
+
name: ch.name,
|
|
256
|
+
description: ch.desc,
|
|
257
|
+
MOM: null,
|
|
258
|
+
tags: [],
|
|
259
|
+
category: [],
|
|
260
|
+
avatar_url: null,
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
/** TIM 没有 category 概念,返回空树 */
|
|
264
|
+
async getCategories() {
|
|
265
|
+
return { categories: [], updated_at: new Date().toISOString() };
|
|
266
|
+
}
|
|
267
|
+
// ─────────────────────────────────────────────────────────────
|
|
268
|
+
// IMChannelTransport(TIM-only)
|
|
269
|
+
// ─────────────────────────────────────────────────────────────
|
|
270
|
+
onTaskReceived(handler) {
|
|
271
|
+
this._taskHandler = handler;
|
|
272
|
+
}
|
|
273
|
+
async sendGroupMessage(channelId, text, atUserList) {
|
|
274
|
+
this._ensureReady();
|
|
275
|
+
return timSendMessage(this._client, channelId, text, atUserList);
|
|
276
|
+
}
|
|
277
|
+
async fetchGroupHistory(channelId, count = 20) {
|
|
278
|
+
this._ensureReady();
|
|
279
|
+
const msgs = await timFetchMessages(this._client, channelId, count);
|
|
280
|
+
return msgs.map((m) => ({
|
|
281
|
+
id: m.id,
|
|
282
|
+
channelId,
|
|
283
|
+
from: m.from,
|
|
284
|
+
nick: m.from,
|
|
285
|
+
text: m.text,
|
|
286
|
+
time: m.time,
|
|
287
|
+
mentionsMe: false,
|
|
288
|
+
}));
|
|
289
|
+
}
|
|
290
|
+
// 频道管理(DORMANT,接口保留)
|
|
291
|
+
async listChannels() {
|
|
292
|
+
this._ensureReady();
|
|
293
|
+
return timListChannels(this._client);
|
|
294
|
+
}
|
|
295
|
+
async searchChannels(keyword) {
|
|
296
|
+
this._ensureReady();
|
|
297
|
+
return timSearchChannels(this._client, keyword);
|
|
298
|
+
}
|
|
299
|
+
async joinChannel(channelId) {
|
|
300
|
+
this._ensureReady();
|
|
301
|
+
return timJoinChannel(this._client, channelId);
|
|
302
|
+
}
|
|
303
|
+
async leaveChannel(channelId) {
|
|
304
|
+
this._ensureReady();
|
|
305
|
+
return timLeaveChannel(this._client, channelId);
|
|
306
|
+
}
|
|
307
|
+
async getMembers(channelId) {
|
|
308
|
+
this._ensureReady();
|
|
309
|
+
return timGetMembers(this._client, channelId);
|
|
310
|
+
}
|
|
311
|
+
async createChannel(name, desc) {
|
|
312
|
+
this._ensureReady();
|
|
313
|
+
return timCreateChannel(this._client, name, desc);
|
|
314
|
+
}
|
|
315
|
+
async getChannelSkill(channelId) {
|
|
316
|
+
this._ensureReady();
|
|
317
|
+
return timGetChannelSkill(this._client, channelId);
|
|
318
|
+
}
|
|
319
|
+
async setChannelSkill(channelId, skill) {
|
|
320
|
+
this._ensureReady();
|
|
321
|
+
return timSetChannelSkill(this._client, channelId, skill);
|
|
322
|
+
}
|
|
323
|
+
// ─────────────────────────────────────────────────────────────
|
|
324
|
+
// 内部工具
|
|
325
|
+
// ─────────────────────────────────────────────────────────────
|
|
326
|
+
/** 暴露底层 TIMClient 给老代码兼容(如 src/runtime/registry 等) */
|
|
327
|
+
get _internalClient() {
|
|
328
|
+
return this._client;
|
|
329
|
+
}
|
|
330
|
+
_ensureReady() {
|
|
331
|
+
if (!this.isReady || !this._client) {
|
|
332
|
+
throw makeTransportError('TRANSPORT_NOT_READY', 'TimTransport not ready');
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* sendC2CCustomMessage 的 transport-local 实现。
|
|
337
|
+
* 不走 runtime/registry —— 直接用 this._client 持有的 SDK 实例。
|
|
338
|
+
*
|
|
339
|
+
* 与 src/tim/c2c.ts 中的同名函数行为等价(含 10s SDK guard),
|
|
340
|
+
* 但避免了"transport 反向依赖 runtime"。
|
|
341
|
+
*/
|
|
342
|
+
async _sendC2CCustom(userId, payload) {
|
|
343
|
+
this._ensureReady();
|
|
344
|
+
const chat = this._client._chat;
|
|
345
|
+
const types = this._client._types;
|
|
346
|
+
if (!chat) {
|
|
347
|
+
throw makeTransportError('TIM_SDK_NULL', 'TIM SDK not initialized');
|
|
348
|
+
}
|
|
349
|
+
const message = chat.createCustomMessage({
|
|
350
|
+
to: userId,
|
|
351
|
+
conversationType: types.CONV_C2C,
|
|
352
|
+
payload: {
|
|
353
|
+
data: JSON.stringify(payload),
|
|
354
|
+
description: payload.type,
|
|
355
|
+
extension: '',
|
|
356
|
+
},
|
|
357
|
+
});
|
|
358
|
+
// 10s SDK-call guard:避免死 WebSocket 让 Tool 永久阻塞
|
|
359
|
+
let timeoutHandle;
|
|
360
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
361
|
+
timeoutHandle = setTimeout(() => reject(new Error(`sendC2CCustomMessage timeout (10s) to=${userId}`)), 10_000);
|
|
362
|
+
});
|
|
363
|
+
try {
|
|
364
|
+
await Promise.race([chat.sendMessage(message), timeoutPromise]);
|
|
365
|
+
logger.info(`[transport/tim] C2C sent: to=${userId} type=${payload.type}`);
|
|
366
|
+
}
|
|
367
|
+
finally {
|
|
368
|
+
clearTimeout(timeoutHandle);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
/** 调用注册的 TaskHandler 并把结果通过 C2C 回写给请求方 */
|
|
372
|
+
async _invokeTaskHandler(req) {
|
|
373
|
+
if (!this._taskHandler)
|
|
374
|
+
return;
|
|
375
|
+
try {
|
|
376
|
+
const result = await this._taskHandler(req);
|
|
377
|
+
await this._sendC2CCustom(req.from, {
|
|
378
|
+
type: 'openagent_task_result',
|
|
379
|
+
request_id: result.requestId,
|
|
380
|
+
status: result.status,
|
|
381
|
+
content: result.content,
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
catch (err) {
|
|
385
|
+
logger.error(`[transport/tim] _invokeTaskHandler failed: ${err.message}`);
|
|
386
|
+
// 即使 handler 失败也回写 error 结果,避免对方永久阻塞
|
|
387
|
+
try {
|
|
388
|
+
await this._sendC2CCustom(req.from, {
|
|
389
|
+
type: 'openagent_task_result',
|
|
390
|
+
request_id: req.requestId,
|
|
391
|
+
status: 'error',
|
|
392
|
+
content: err.message,
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
catch {
|
|
396
|
+
/* swallow,已经记日志了 */
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
// 引用未使用的导入做编译期检查,避免 lint 报 unused
|
|
402
|
+
void TencentCloudChat;
|