openclaw-openagent 1.0.12 → 1.0.14

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.
Files changed (132) hide show
  1. package/dist/index.js +2 -0
  2. package/dist/src/config/config-schema.d.ts +9 -0
  3. package/dist/src/config/config-schema.js +6 -0
  4. package/dist/src/messaging/executor.d.ts +2 -3
  5. package/dist/src/messaging/executor.js +4 -31
  6. package/dist/src/plugin-ui/assets/openagent-override.js +124 -18
  7. package/dist/src/proxy/auth-proxy.js +5 -0
  8. package/dist/src/runtime/update-checker.d.ts +18 -0
  9. package/dist/src/runtime/update-checker.js +216 -0
  10. package/index.ts +3 -0
  11. package/package.json +1 -3
  12. package/src/app/channel-tools.ts +0 -249
  13. package/src/app/discovery-tools.ts +0 -273
  14. package/src/app/download-file-tool.ts +0 -133
  15. package/src/app/hooks.ts +0 -144
  16. package/src/app/index.ts +0 -84
  17. package/src/app/messaging-tools.ts +0 -79
  18. package/src/app/ops-tools.ts +0 -155
  19. package/src/app/remote-agent-tool.ts +0 -762
  20. package/src/app/types.ts +0 -68
  21. package/src/app/upload-file-tool.ts +0 -411
  22. package/src/app/verbose-preflight.ts +0 -190
  23. package/src/auth/config.ts +0 -266
  24. package/src/auth/credential-manager.ts +0 -146
  25. package/src/auth/index.ts +0 -24
  26. package/src/auth/verify.ts +0 -99
  27. package/src/channel.ts +0 -566
  28. package/src/compat.ts +0 -94
  29. package/src/config/config-schema.ts +0 -39
  30. package/src/messaging/aggregator.ts +0 -120
  31. package/src/messaging/collector.ts +0 -89
  32. package/src/messaging/executor.ts +0 -72
  33. package/src/messaging/inbound.ts +0 -150
  34. package/src/messaging/index.ts +0 -11
  35. package/src/messaging/mention-protocol.ts +0 -94
  36. package/src/messaging/process-c2c-request.ts +0 -564
  37. package/src/messaging/process-message.ts +0 -373
  38. package/src/messaging/scheduler.ts +0 -55
  39. package/src/messaging/types.ts +0 -38
  40. package/src/plugin-ui/adapters/oc-2026-04.js +0 -103
  41. package/src/plugin-ui/adapters/oc-2026-05.js +0 -125
  42. package/src/plugin-ui/adapters/oc-2026-06.js +0 -125
  43. package/src/plugin-ui/adapters/oc-unknown.js +0 -48
  44. package/src/plugin-ui/assets/agentbook-icon.svg +0 -5
  45. package/src/plugin-ui/assets/bg.png +0 -0
  46. package/src/plugin-ui/assets/icon.png +0 -0
  47. package/src/plugin-ui/assets/magic.svg +0 -5
  48. package/src/plugin-ui/assets/openagent-override.js +0 -11156
  49. package/src/plugin-ui/build.cjs +0 -463
  50. package/src/plugin-ui/index.ts +0 -18
  51. package/src/plugin-ui/modules/agent-book/panel/agent-book.js +0 -570
  52. package/src/plugin-ui/modules/agent-book/panel/agent-card.js +0 -170
  53. package/src/plugin-ui/modules/agent-book/panel/agent-data.js +0 -643
  54. package/src/plugin-ui/modules/agent-book/panel/inject-ui.js +0 -665
  55. package/src/plugin-ui/modules/agent-book/panel/mention-state.js +0 -206
  56. package/src/plugin-ui/modules/agent-book/panel/styles.js +0 -833
  57. package/src/plugin-ui/modules/agent-book/panel/theme-adapter.js +0 -86
  58. package/src/plugin-ui/modules/agent-book/remote-agent-tool/components-core.js +0 -295
  59. package/src/plugin-ui/modules/agent-book/remote-agent-tool/thought-chain-card.js +0 -211
  60. package/src/plugin-ui/modules/agent-book/scanner.js +0 -131
  61. package/src/plugin-ui/modules/agent-book/travelcard/travel-cards.js +0 -500
  62. package/src/plugin-ui/modules/agent-book/travelcard/travel-engine.js +0 -652
  63. package/src/plugin-ui/modules/agent-book/travelcard/travel-styles.js +0 -256
  64. package/src/plugin-ui/modules/loader/bootstrap.js +0 -38
  65. package/src/plugin-ui/modules/loader/shared-state.js +0 -838
  66. package/src/plugin-ui/modules/loader/ws-intercept.js +0 -199
  67. package/src/plugin-ui/modules/remote-agent/chunk-parser.js +0 -161
  68. package/src/plugin-ui/modules/remote-agent/execution-card.js +0 -530
  69. package/src/plugin-ui/modules/remote-agent/markdown-renderer.js +0 -256
  70. package/src/plugin-ui/modules/remote-agent/media-links.js +0 -151
  71. package/src/plugin-ui/modules/remote-agent/native-style-adapter.js +0 -134
  72. package/src/plugin-ui/modules/remote-agent/output-card.js +0 -342
  73. package/src/plugin-ui/modules/remote-agent/progress-store.js +0 -363
  74. package/src/plugin-ui/modules/remote-agent/render-hooks.js +0 -1700
  75. package/src/plugin-ui/modules/remote-agent/styles.js +0 -896
  76. package/src/plugin-ui/modules/remote-agent/tool-card-model.js +0 -130
  77. package/src/plugin-ui/postinstall-deploy.cjs +0 -52
  78. package/src/plugin-ui/ui-extension-loader/backup.ts +0 -92
  79. package/src/plugin-ui/ui-extension-loader/index.ts +0 -276
  80. package/src/plugin-ui/ui-extension-loader/locator.ts +0 -152
  81. package/src/plugin-ui/ui-extension-loader/manifest.ts +0 -57
  82. package/src/plugin-ui/ui-extension-loader/registry-regex.ts +0 -944
  83. package/src/plugin-ui/ui-extension-loader/removed-extensions.ts +0 -70
  84. package/src/plugin-ui/ui-extension-loader/types.ts +0 -72
  85. package/src/proxy/auth-proxy.ts +0 -409
  86. package/src/runtime/account.ts +0 -549
  87. package/src/runtime/index.ts +0 -7
  88. package/src/runtime/plugin-runtime.ts +0 -94
  89. package/src/runtime/registry.ts +0 -71
  90. package/src/sdk/CLASS_MAP.md +0 -143
  91. package/src/sdk/index.d.ts +0 -126
  92. package/src/sdk/index.js +0 -23990
  93. package/src/sdk/modules/cloud-search-module.js +0 -1117
  94. package/src/sdk/modules/follow-module.js +0 -1069
  95. package/src/sdk/modules/group-module.js +0 -7397
  96. package/src/sdk/modules/relationship-module.js +0 -2269
  97. package/src/sdk/modules/signaling-module.js +0 -1468
  98. package/src/sdk/modules/tim-upload-plugin.js +0 -730
  99. package/src/sdk/node-env/http-request.js +0 -90
  100. package/src/sdk/node-env/index.js +0 -57
  101. package/src/sdk/node-env/storage.js +0 -114
  102. package/src/sdk/package.json +0 -10
  103. package/src/sdk/tsconfig.json +0 -16
  104. package/src/state/pending-invocation-store.ts +0 -43
  105. package/src/state/store.ts +0 -756
  106. package/src/tim/c2c.ts +0 -451
  107. package/src/tim/channels.ts +0 -364
  108. package/src/tim/client.ts +0 -330
  109. package/src/tim/index.ts +0 -18
  110. package/src/tim/messages.ts +0 -166
  111. package/src/tim/sdk-logger-init.ts +0 -50
  112. package/src/tools.ts +0 -10
  113. package/src/transport/factory.ts +0 -95
  114. package/src/transport/oasn/index.ts +0 -17
  115. package/src/transport/oasn/oasn-agent-card.ts +0 -111
  116. package/src/transport/oasn/oasn-discovery.ts +0 -108
  117. package/src/transport/oasn/oasn-files.ts +0 -210
  118. package/src/transport/oasn/oasn-http.ts +0 -529
  119. package/src/transport/oasn/oasn-invocation.ts +0 -604
  120. package/src/transport/oasn/oasn-normalize.ts +0 -133
  121. package/src/transport/oasn/oasn-register.ts +0 -33
  122. package/src/transport/oasn/oasn-transport.ts +0 -318
  123. package/src/transport/oasn/oasn-types.ts +0 -404
  124. package/src/transport/tim/index.ts +0 -8
  125. package/src/transport/tim/tim-transport.ts +0 -515
  126. package/src/transport/types.ts +0 -538
  127. package/src/types/openclaw-plugin-sdk-media-store.d.ts +0 -9
  128. package/src/types/openclaw.d.ts +0 -97
  129. package/src/types/tencentcloud-chat.d.ts +0 -15
  130. package/src/util/http.ts +0 -113
  131. package/src/util/logger.ts +0 -131
  132. package/src/util/url-resolver.ts +0 -77
package/src/channel.ts DELETED
@@ -1,566 +0,0 @@
1
- /**
2
- * OpenAgent Channel Adapter — OpenClaw channel plugin interface
3
- *
4
- * Assembles the v3 modular architecture:
5
- * auth/ → runtime/ → tim/ → messaging/ → tools
6
- *
7
- * Gateway lifecycle uses AccountRuntime with transaction-style
8
- * start/stop and RuntimeRegistry enforcement.
9
- */
10
-
11
- import { DEFAULT_OASN_API_BASE, isUnconfiguredOasnApiBase, resolveOpenagentAccount, getOpenagentConfig } from './auth/config.js';
12
- import { registry } from './runtime/registry.js';
13
- import { AccountRuntime } from './runtime/account.js';
14
-
15
- import { logger } from './util/logger.js';
16
- import { resolveOasnRelativeUrl } from './util/url-resolver.js';
17
- import * as timMessages from './tim/messages.js';
18
-
19
- const API_BASE = 'https://api.clawlink.club';
20
- const OASN_TRANSPORT = 'oasn';
21
-
22
- // ── Channel adapter meta ──
23
-
24
- const meta = {
25
- id: 'openagent',
26
- label: 'OpenAgent',
27
- selectionLabel: 'OpenAgent (Agent Network)',
28
- detailLabel: 'OpenAgent Agent Network',
29
- docsPath: '/channels/openagent',
30
- docsLabel: 'openagent',
31
- blurb: 'Connect to OASN agent social network via Tencent IM.',
32
- aliases: ['openagent', 'agent-network'],
33
- order: 90,
34
- quickstartAllowFrom: true,
35
- };
36
-
37
- // ── Channel plugin object ──
38
-
39
- export const openagentPlugin = {
40
- id: 'openagent',
41
- meta,
42
-
43
- capabilities: {
44
- chatTypes: ['group'],
45
- media: false,
46
- reactions: true,
47
- threads: false,
48
- polls: false,
49
- nativeCommands: false,
50
- },
51
-
52
- reload: { configPrefixes: ['channels.openagent'] },
53
-
54
- config: {
55
- listAccountIds: (cfgOrCtx: Record<string, unknown>) => {
56
- const cfg = (cfgOrCtx as { cfg?: Record<string, unknown> })?.cfg
57
- || (cfgOrCtx?.['channels'] ? cfgOrCtx : {});
58
- const clCfg = getOpenagentConfig(cfg);
59
- const keys = Object.keys((clCfg['accounts'] || {}) as Record<string, unknown>);
60
- return keys.length > 0 ? keys : ['default'];
61
- },
62
- resolveAccount: (cfg: Record<string, unknown>, accountId: string) =>
63
- resolveOpenagentAccount(cfg, accountId),
64
- defaultAccountId: (cfgOrCtx: Record<string, unknown>) => {
65
- const cfg = (cfgOrCtx as { cfg?: Record<string, unknown> })?.cfg
66
- || (cfgOrCtx?.['channels'] ? cfgOrCtx : {});
67
- const clCfg = getOpenagentConfig(cfg);
68
- const keys = Object.keys((clCfg['accounts'] || {}) as Record<string, unknown>);
69
- return keys.length > 0 ? keys[0] : 'default';
70
- },
71
- setAccountEnabled: ({ cfg, enabled }: { cfg: Record<string, unknown>; enabled: boolean }) => ({
72
- ...cfg,
73
- channels: {
74
- ...(cfg['channels'] as Record<string, unknown>),
75
- openagent: { ...((cfg['channels'] as Record<string, unknown>)?.['openagent'] as Record<string, unknown>), enabled },
76
- },
77
- }),
78
- deleteAccount: ({ cfg }: { cfg: Record<string, unknown> }) => {
79
- const channels = { ...(cfg['channels'] as Record<string, unknown>) };
80
- delete channels['openagent'];
81
- return { ...cfg, channels };
82
- },
83
- isConfigured: (account: { configured: boolean }) => account.configured,
84
- describeAccount: (account: { accountId: string; name: string; enabled: boolean; configured: boolean; transport?: string; status?: string }) => ({
85
- accountId: account.accountId,
86
- name: account.name,
87
- enabled: account.enabled,
88
- configured: account.configured,
89
- tokenStatus: account.configured ? 'available' : 'missing',
90
- transport: account.transport ?? 'oasn',
91
- claimStatus: account.status ?? 'unknown',
92
- }),
93
- },
94
-
95
- security: {
96
- resolveDmPolicy: () => ({
97
- policy: 'open',
98
- allowFrom: ['*'],
99
- policyPath: 'channels.openagent.dm.policy',
100
- allowFromPath: 'channels.openagent.dm.allowFrom',
101
- normalizeEntry: (raw: string) => raw.trim(),
102
- }),
103
- },
104
-
105
- groups: {
106
- resolveRequireMention: () => false,
107
- },
108
-
109
- threading: {
110
- resolveReplyToMode: () => 'first',
111
- },
112
-
113
- messaging: {
114
- normalizeTarget: (raw: string) => {
115
- const trimmed = raw.trim();
116
- if (!trimmed) return undefined;
117
- return trimmed.replace(/^(openagent):/i, '').trim() || undefined;
118
- },
119
- targetResolver: {
120
- looksLikeId: (raw: string) => Boolean(raw.trim()),
121
- hint: '<channel-id> e.g. ch-001',
122
- },
123
- },
124
-
125
- outbound: {
126
- deliveryMode: 'direct',
127
- chunkerMode: 'text',
128
- textChunkLimit: 3000,
129
-
130
- sendText: async ({ to, text }: { cfg: unknown; to: string; text: string; accountId?: string }) => {
131
- logger.info(`[outbound] sendText to=${to} len=${text.length}`);
132
- const rt = registry.getDefault();
133
- if (!rt?.isRunning) {
134
- logger.warn('[outbound] sendText failed: not connected');
135
- return { channel: 'openagent', ok: false, messageId: '', error: new Error('OpenAgent not connected') };
136
- }
137
-
138
- // v3.9+ 双轨抽象:群聊外发仅在 TIM 路径有效(IMChannelTransport)。
139
- // OASN 路径下没有"主动发群消息"语义 —— ClientAgent 只通过 sendTask
140
- // 向远端 ServiceAgent 发起 Invocation,原则上不会触达此分支;
141
- // 但若上层误调用,直接告警并返回 ok=false,避免崩溃。
142
- if (rt.transportType === 'oasn') {
143
- logger.warn(`[outbound] sendText not supported on OASN transport (to=${to})`);
144
- return {
145
- channel: 'openagent',
146
- ok: false,
147
- messageId: '',
148
- error: new Error('sendText not supported on OASN transport'),
149
- };
150
- }
151
-
152
- try {
153
- const result = await rt.sendMessage(to, text);
154
- logger.info(`[outbound] sendText OK msgId=${result.messageId}`);
155
- return { channel: 'openagent', ok: true, messageId: result.messageId };
156
- } catch (err) {
157
- logger.error(`[outbound] sendText failed: ${(err as Error).message}`);
158
- return {
159
- channel: 'openagent', ok: false, messageId: '',
160
- error: err instanceof Error ? err : new Error(String(err)),
161
- };
162
- }
163
- },
164
- },
165
-
166
- status: {
167
- defaultRuntime: {
168
- accountId: 'default',
169
- running: false,
170
- lastStartAt: null as number | null,
171
- lastStopAt: null as number | null,
172
- lastError: null as string | null,
173
- },
174
- buildChannelSummary: ({ snapshot }: { snapshot: Record<string, unknown> }) => ({
175
- configured: snapshot['configured'] ?? false,
176
- running: snapshot['running'] ?? false,
177
- connected: snapshot['connected'] ?? false,
178
- transport: snapshot['transport'] ?? 'oasn',
179
- claimStatus: snapshot['claimStatus'] ?? 'unknown',
180
- claimUrl: snapshot['claimUrl'] ?? '',
181
- lastStartAt: snapshot['lastStartAt'] ?? null,
182
- lastStopAt: snapshot['lastStopAt'] ?? null,
183
- lastError: snapshot['lastError'] ?? null,
184
- lastInboundAt: snapshot['lastInboundAt'] ?? null,
185
- lastOutboundAt: snapshot['lastOutboundAt'] ?? null,
186
- }),
187
- probeAccount: async () => ({ ok: true }),
188
- buildAccountSnapshot: ({ account, runtime }: {
189
- account: { accountId: string; name: string; enabled: boolean; configured: boolean; transport?: string; status?: string; claimUrl?: string };
190
- runtime?: { running?: boolean; lastStartAt?: number; lastStopAt?: number; lastError?: string };
191
- }) => ({
192
- accountId: account.accountId,
193
- name: account.name,
194
- enabled: account.enabled,
195
- configured: account.configured,
196
- tokenStatus: account.configured ? 'available' : 'missing',
197
- transport: account.transport ?? 'oasn',
198
- claimStatus: runtime && 'claimStatus' in runtime ? (runtime as { claimStatus?: string }).claimStatus : account.status ?? 'unknown',
199
- claimUrl: account.claimUrl ?? '',
200
- running: runtime?.running ?? false,
201
- connected: runtime?.running ?? false,
202
- lastStartAt: runtime?.lastStartAt ?? null,
203
- lastStopAt: runtime?.lastStopAt ?? null,
204
- lastError: runtime?.lastError ?? null,
205
- }),
206
-
207
- // Outbound handlers: intentionally no-op (LLM text is intercepted here)
208
- sendText: async () => ({ ok: true }),
209
- sendMedia: async () => ({ ok: true }),
210
- },
211
-
212
- // ── Gateway lifecycle ──
213
- gateway: {
214
- startAccount: async (ctx: {
215
- account: {
216
- accountId: string;
217
- agentId: string;
218
- apiBase: string;
219
- accessApiBase?: string;
220
- apiKey: string;
221
- configured: boolean;
222
- config: Record<string, unknown>;
223
- };
224
- cfg: Record<string, unknown>;
225
- abortSignal?: AbortSignal;
226
- log?: { info?: (msg: string) => void; warn?: (msg: string) => void; error?: (msg: string) => void };
227
- setStatus: (status: Record<string, unknown>) => void;
228
- }) => {
229
- const { account, abortSignal } = ctx;
230
-
231
- // Guard is installed in register() (index.ts), not here.
232
-
233
- // OASN 模式以 CLI install/register/write-config 为首次注册入口。
234
- // runtime 只消费已经写入的 agent_id + api_key。
235
- const accountConfig = resolveOpenagentAccount(ctx.cfg);
236
- if (accountConfig.transport === 'oasn' && isUnconfiguredOasnApiBase(accountConfig.apiBase)) {
237
- logger.warn('[channel] OASN api_base not configured; skipping start');
238
- ctx.setStatus({
239
- accountId: account.accountId,
240
- running: false,
241
- configured: false,
242
- transport: 'oasn',
243
- claimStatus: accountConfig.status,
244
- lastError: 'OASN api_base is not configured',
245
- });
246
- return;
247
- }
248
- if (accountConfig.transport === 'oasn' && !accountConfig.configured) {
249
- const message = 'OASN account is not configured; run openclaw-plugin-openagent-cli install to register this device';
250
- logger.warn(`[channel] ${message}`);
251
- ctx.setStatus({
252
- accountId: account.accountId,
253
- running: false,
254
- configured: false,
255
- transport: 'oasn',
256
- claimStatus: accountConfig.status,
257
- claimUrl: accountConfig.claimUrl,
258
- lastError: message,
259
- });
260
- return;
261
- }
262
- if (!account.configured && accountConfig.transport !== 'oasn') {
263
- logger.warn('[channel] Not configured; skipping');
264
- ctx.setStatus({ accountId: account.accountId, running: false, configured: false });
265
- return;
266
- }
267
-
268
- logger.info(`[channel] Starting account '${account.accountId}' for agent ${accountConfig.agentId || account.agentId || '(unconfigured)'}`);
269
-
270
- const runtime = new AccountRuntime(account.accountId);
271
-
272
- try {
273
- // Register in registry (throws if another account is active)
274
- registry.register(runtime);
275
-
276
- // Transaction-style start
277
- await runtime.start(
278
- {
279
- accountId: account.accountId,
280
- name: 'OpenAgent',
281
- enabled: true,
282
- configured: accountConfig.configured,
283
- agentId: accountConfig.agentId,
284
- apiKey: accountConfig.apiKey,
285
- apiBase: accountConfig.apiBase,
286
- accessApiBase: accountConfig.accessApiBase,
287
- claimUrl: accountConfig.claimUrl,
288
- status: accountConfig.status,
289
- registeredAt: accountConfig.registeredAt,
290
- // v3.9+ 双轨抽象:用 resolveOpenagentAccount 从 raw cfg 解析 transport(不依赖 OpenClaw 框架透传 account.config 自定义字段)
291
- transport: accountConfig.transport,
292
- config: account.config || {},
293
- },
294
- {
295
- batchWindowMs: (account.config?.['batchWindowMs'] as number) || 20_000,
296
- maxBatchSize: (account.config?.['maxBatchSize'] as number) || 20,
297
- },
298
- ctx.cfg,
299
- );
300
-
301
- ctx.setStatus({
302
- accountId: account.accountId,
303
- running: true,
304
- configured: accountConfig.configured,
305
- transport: runtime.transportType,
306
- claimStatus: runtime.claimStatus,
307
- claimUrl: runtime.claimUrl,
308
- lastStartAt: Date.now(),
309
- });
310
-
311
- logger.info('[channel] Pipeline active: Push mode');
312
-
313
- // Block until aborted
314
- await new Promise<void>((_, reject) => {
315
- if (abortSignal) {
316
- abortSignal.addEventListener('abort', () => {
317
- void runtime.stop().then(() => {
318
- registry.unregister(runtime);
319
- ctx.setStatus({
320
- accountId: account.accountId,
321
- running: false,
322
- lastStopAt: Date.now(),
323
- });
324
- logger.info(`[channel] Account '${account.accountId}' stopped via abort`);
325
- reject(new Error('abort'));
326
- });
327
- }, { once: true });
328
- }
329
- });
330
- } catch (err) {
331
- // Controlled failure: catch registry error or start error
332
- const message = (err as Error).message;
333
- if (message !== 'abort') {
334
- logger.error(`[channel] Start failed: ${message}`);
335
- registry.unregister(runtime);
336
- ctx.setStatus({
337
- accountId: account.accountId,
338
- running: false,
339
- lastError: message,
340
- });
341
- }
342
- }
343
- },
344
-
345
- stopAccount: async (ctx: {
346
- account: { accountId: string };
347
- setStatus: (status: Record<string, unknown>) => void;
348
- }) => {
349
- const rt = registry.getDefault();
350
- if (rt) {
351
- await rt.stop();
352
- registry.unregister(rt);
353
- }
354
- ctx.setStatus({
355
- accountId: ctx.account.accountId,
356
- running: false,
357
- lastStopAt: Date.now(),
358
- });
359
- },
360
- },
361
-
362
- // ── Onboarding wizard ──
363
- onboarding: {
364
- channel: 'openagent',
365
- getStatus: async ({ cfg }: { cfg: Record<string, unknown> }) => {
366
- const account = resolveOpenagentAccount(cfg);
367
- const apiBaseMissing = account.transport === 'oasn' && isUnconfiguredOasnApiBase(account.apiBase);
368
- const statusLine = buildOnboardingStatusLine(account, apiBaseMissing);
369
- return {
370
- channel: 'openagent',
371
- configured: account.transport === 'oasn' ? account.configured && !apiBaseMissing : account.configured,
372
- statusLines: [statusLine],
373
- selectionHint: apiBaseMissing
374
- ? 'needs api_base'
375
- : account.transport === 'oasn' && !account.configured
376
- ? 'needs CLI install'
377
- : account.configured ? 'configured' : 'needs credentials',
378
- quickstartScore: apiBaseMissing ? 0 : account.configured ? 2 : 0,
379
- };
380
- },
381
- configure: async ({ cfg, prompter }: {
382
- cfg: Record<string, unknown>;
383
- prompter: { text: (opts: { message: string; validate: (v: string) => string | undefined }) => Promise<string> };
384
- }) => {
385
- const existing = ((cfg['channels'] as Record<string, unknown>)?.['openagent'] || {}) as Record<string, unknown>;
386
- const accounts = (existing['accounts'] || {}) as Record<string, Record<string, unknown>>;
387
- const firstAccKey = Object.keys(accounts)[0];
388
- const fallbackAcc = firstAccKey ? accounts[firstAccKey]! : {};
389
- const resolved = resolveOpenagentAccount(cfg);
390
-
391
- if (resolved.transport === OASN_TRANSPORT) {
392
- let apiBase = resolved.apiBase;
393
- if (isUnconfiguredOasnApiBase(apiBase)) {
394
- apiBase = String(await prompter.text({
395
- message: 'Enter OASN API Base URL',
396
- validate: (v: string) => {
397
- const value = v?.trim();
398
- if (!value || isUnconfiguredOasnApiBase(value)) return 'Required';
399
- try {
400
- // eslint-disable-next-line no-new
401
- new URL(value);
402
- return undefined;
403
- } catch {
404
- return 'Must be a valid URL';
405
- }
406
- },
407
- })).trim();
408
- }
409
- const accessApiBase = String(
410
- resolved.accessApiBase
411
- || fallbackAcc['access_api_base']
412
- || fallbackAcc['accessApiBase']
413
- || existing['access_api_base']
414
- || existing['accessApiBase']
415
- || apiBase,
416
- );
417
-
418
- let claimUpdates: Record<string, unknown> = {};
419
- if (resolved.agentId && resolved.apiKey && resolved.status !== 'claimed') {
420
- const ownerEmail = String(await prompter.text({
421
- message: 'Enter Owner email to claim this OASN ClientAgent (leave blank to skip)',
422
- validate: () => undefined,
423
- })).trim();
424
- if (ownerEmail) {
425
- claimUpdates = await claimOasnAgent(apiBase, resolved.apiKey, resolved.agentId, ownerEmail);
426
- }
427
- }
428
-
429
- return {
430
- cfg: {
431
- ...cfg,
432
- channels: {
433
- ...(cfg['channels'] as Record<string, unknown>),
434
- openagent: {
435
- ...existing,
436
- enabled: true,
437
- transport: 'oasn',
438
- api_base: apiBase,
439
- access_api_base: accessApiBase,
440
- accounts: {
441
- default: compactConfig({
442
- ...fallbackAcc,
443
- transport: 'oasn',
444
- api_base: apiBase,
445
- access_api_base: accessApiBase,
446
- agent_id: resolved.agentId || fallbackAcc['agent_id'],
447
- api_key: resolved.apiKey || fallbackAcc['api_key'],
448
- claim_url: resolved.claimUrl || fallbackAcc['claim_url'],
449
- status: claimUpdates.status || resolved.status,
450
- registered_at: resolved.registeredAt || fallbackAcc['registered_at'],
451
- ...claimUpdates,
452
- }),
453
- },
454
- },
455
- },
456
- },
457
- accountId: 'default',
458
- };
459
- }
460
-
461
- let agentId = (fallbackAcc['agentId'] || fallbackAcc['agent_id'] || firstAccKey || '') as string;
462
- let apiKey = (fallbackAcc['api_key'] || fallbackAcc['apiKey'] || '') as string;
463
- const apiBase = (fallbackAcc['apiBase'] || fallbackAcc['api_base'] || API_BASE) as string;
464
-
465
- if (!agentId) {
466
- agentId = String(await prompter.text({
467
- message: 'Enter your OpenAgent Agent ID',
468
- validate: (v: string) => v?.trim() ? undefined : 'Required',
469
- })).trim();
470
- }
471
-
472
- if (!apiKey) {
473
- apiKey = String(await prompter.text({
474
- message: 'Enter your OpenAgent API Key',
475
- validate: (v: string) => v?.trim() ? undefined : 'Required',
476
- })).trim();
477
- }
478
-
479
- const cleanedExisting = { ...existing };
480
- delete cleanedExisting['agentId'];
481
- delete cleanedExisting['apiKey'];
482
- delete cleanedExisting['agent_id'];
483
- delete cleanedExisting['api_key'];
484
-
485
- return {
486
- cfg: {
487
- ...cfg,
488
- channels: {
489
- ...(cfg['channels'] as Record<string, unknown>),
490
- openagent: {
491
- ...cleanedExisting,
492
- enabled: true,
493
- accounts: {
494
- default: {
495
- agent_id: agentId,
496
- api_key: apiKey,
497
- api_base: apiBase,
498
- },
499
- },
500
- },
501
- },
502
- },
503
- accountId: 'default',
504
- };
505
- },
506
- disable: (cfg: Record<string, unknown>) => ({
507
- ...cfg,
508
- channels: {
509
- ...(cfg['channels'] as Record<string, unknown>),
510
- openagent: {
511
- ...((cfg['channels'] as Record<string, unknown>)?.['openagent'] as Record<string, unknown>),
512
- enabled: false,
513
- },
514
- },
515
- }),
516
- },
517
- };
518
-
519
- function buildOnboardingStatusLine(
520
- account: ReturnType<typeof resolveOpenagentAccount>,
521
- apiBaseMissing: boolean,
522
- ): string {
523
- if (account.transport !== 'oasn') {
524
- return account.configured
525
- ? `OpenAgent: configured (agent=${account.agentId})`
526
- : 'OpenAgent: needs agentId + apiKey';
527
- }
528
- if (apiBaseMissing) return 'OASN: needs api_base';
529
- if (!account.configured) return `OASN: run openclaw-plugin-openagent-cli install to register (${account.apiBase})`;
530
- const claimHint = account.status === 'claimed'
531
- ? 'claimed'
532
- : `${account.status}${account.claimUrl ? ` claim=${account.claimUrl}` : ''}`;
533
- return `OASN: ${claimHint} (agent=${account.agentId})`;
534
- }
535
-
536
- function compactConfig(input: Record<string, unknown>): Record<string, unknown> {
537
- return Object.fromEntries(
538
- Object.entries(input).filter(([, value]) => value !== undefined && value !== ''),
539
- );
540
- }
541
-
542
- async function claimOasnAgent(
543
- apiBase: string,
544
- apiKey: string,
545
- agentId: string,
546
- ownerEmail: string,
547
- ): Promise<Record<string, unknown>> {
548
- const res = await fetch(resolveOasnRelativeUrl(apiBase, '/api/agents/claim'), {
549
- method: 'POST',
550
- headers: {
551
- 'Content-Type': 'application/json',
552
- Authorization: `Bearer ${apiKey}`,
553
- },
554
- body: JSON.stringify({ agentId, ownerEmail }),
555
- signal: AbortSignal.timeout(15_000),
556
- });
557
- const body = await res.json().catch(() => ({})) as Record<string, unknown>;
558
- if (!res.ok) {
559
- throw new Error(`claim failed: ${res.status} ${JSON.stringify(body).slice(0, 200)}`);
560
- }
561
- return {
562
- status: 'claim_pending',
563
- claim_expires_at: body['expiresAt'] || body['expires_at'],
564
- owner_email_masked: body['ownerEmailMasked'] || body['owner_email_masked'],
565
- };
566
- }
package/src/compat.ts DELETED
@@ -1,94 +0,0 @@
1
- /**
2
- * Runtime host-version compatibility check for openclaw-openagent.
3
- *
4
- * OpenClaw uses a date-based version format: YYYY.M.DD (e.g. 2026.3.22).
5
- * This module parses that format and validates the running host is within
6
- * the supported range for this plugin version.
7
- *
8
- * Ported from openclaw-weixin v2.1.1 src/compat.ts
9
- * Reference: docs/reference/plugins/openclaw-weixin-latest-v2.1.1/src/compat.ts
10
- */
11
-
12
- import { logger } from './util/logger.js';
13
-
14
- export const PLUGIN_VERSION = '1.0.0';
15
-
16
- export const SUPPORTED_HOST_MIN = '2026.3.22';
17
-
18
- /** Plugin has been verified up to this OpenClaw host version */
19
- export const MAX_TESTED_VERSION = '2026.6.30';
20
-
21
- export interface OpenClawVersion {
22
- year: number;
23
- month: number;
24
- day: number;
25
- }
26
-
27
- /**
28
- * Parse an OpenClaw date version string (e.g. "2026.3.22") into components.
29
- * Returns null for unparseable strings.
30
- */
31
- export function parseOpenClawVersion(version: string): OpenClawVersion | null {
32
- // Strip any pre-release suffix (e.g. "2026.3.22-beta.1" -> "2026.3.22")
33
- const base = version.trim().split('-')[0];
34
- const parts = base.split('.');
35
- if (parts.length !== 3) return null;
36
- const [year, month, day] = parts.map(Number);
37
- if (Number.isNaN(year) || Number.isNaN(month) || Number.isNaN(day)) return null;
38
- return { year, month, day };
39
- }
40
-
41
- /**
42
- * Compare two parsed versions. Returns -1 | 0 | 1.
43
- */
44
- export function compareVersions(a: OpenClawVersion, b: OpenClawVersion): -1 | 0 | 1 {
45
- for (const key of ['year', 'month', 'day'] as const) {
46
- if (a[key] < b[key]) return -1;
47
- if (a[key] > b[key]) return 1;
48
- }
49
- return 0;
50
- }
51
-
52
- /**
53
- * Check whether a host version string is >= SUPPORTED_HOST_MIN.
54
- */
55
- export function isHostVersionSupported(hostVersion: string): boolean {
56
- const host = parseOpenClawVersion(hostVersion);
57
- if (!host) return false;
58
- const min = parseOpenClawVersion(SUPPORTED_HOST_MIN)!;
59
- return compareVersions(host, min) >= 0;
60
- }
61
-
62
- /**
63
- * Fail-fast guard. Call at the very start of `register()` to prevent the
64
- * plugin from loading on an incompatible host.
65
- *
66
- * @throws {Error} with a human-readable message when the host is out of range.
67
- */
68
- export function assertHostCompatibility(hostVersion: string | undefined): void {
69
- if (!hostVersion || hostVersion === 'unknown') {
70
- logger.warn(
71
- '[compat] Could not determine host OpenClaw version; skipping compatibility check.',
72
- );
73
- return;
74
- }
75
- if (isHostVersionSupported(hostVersion)) {
76
- logger.info(`[compat] Host OpenClaw ${hostVersion} >= ${SUPPORTED_HOST_MIN}, OK.`);
77
- // Warn but don't block for versions beyond tested range
78
- const host = parseOpenClawVersion(hostVersion);
79
- const max = parseOpenClawVersion(MAX_TESTED_VERSION);
80
- if (host && max && compareVersions(host, max) > 0) {
81
- logger.warn(
82
- `[compat] Host OpenClaw ${hostVersion} is beyond max tested version ${MAX_TESTED_VERSION}. ` +
83
- `The plugin may encounter compatibility issues.`,
84
- );
85
- }
86
- return;
87
- }
88
- throw new Error(
89
- `openclaw-openagent@${PLUGIN_VERSION} requires OpenClaw >=${SUPPORTED_HOST_MIN}, ` +
90
- `but found ${hostVersion}. ` +
91
- `Please upgrade OpenClaw, or install the legacy version:\n` +
92
- ` npm install openclaw-openagent@legacy`,
93
- );
94
- }
@@ -1,39 +0,0 @@
1
- /**
2
- * OpenAgent config schema — Zod-based runtime validation.
3
- *
4
- * This mirrors the JSON schema in openclaw.plugin.json but provides
5
- * type-safe runtime parsing via Zod.
6
- *
7
- * Uses direct zod dependency (same pattern as openclaw-weixin v2.1.1)
8
- * rather than openclaw/plugin-sdk/zod, which is only available in
9
- * OpenClaw >= ~2026.3.28 and would break on earlier supported hosts.
10
- */
11
-
12
- import { z } from 'zod';
13
-
14
- // ---------------------------------------------------------------------------
15
- // Zod config schema
16
- // ---------------------------------------------------------------------------
17
-
18
- const transportSchema = z.enum(['oasn', 'tim']).default('oasn');
19
-
20
- const openagentAccountSchema = z.object({
21
- name: z.string().optional(),
22
- enabled: z.boolean().optional(),
23
- transport: transportSchema.optional(),
24
- agent_id: z.string().optional(),
25
- api_key: z.string().optional(),
26
- api_base: z.string().optional(),
27
- access_api_base: z.string().optional(),
28
- claim_url: z.string().optional(),
29
- status: z.enum(['unregistered', 'unclaimed', 'claim_pending', 'claimed', 'credential_expired', 'unknown']).optional(),
30
- registered_at: z.string().optional(),
31
- });
32
-
33
- /** Top-level openagent config schema. */
34
- export const OpenagentConfigSchema = openagentAccountSchema.extend({
35
- transport: transportSchema.optional(),
36
- api_base: z.string().optional(),
37
- access_api_base: z.string().optional(),
38
- accounts: z.record(z.string(), openagentAccountSchema).optional(),
39
- });