@workclaw/openclaw-workclaw 1.0.17 → 1.0.19

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 (147) hide show
  1. package/README.md +364 -325
  2. package/dist/api.d.ts +3 -0
  3. package/dist/api.js +3 -0
  4. package/dist/index.d.ts +11 -0
  5. package/dist/index.js +263 -0
  6. package/dist/setup-entry.d.ts +2 -0
  7. package/dist/setup-entry.js +12 -0
  8. package/dist/src/accounts.d.ts +66 -0
  9. package/dist/src/accounts.js +279 -0
  10. package/dist/src/api/accounts-api.d.ts +2 -0
  11. package/dist/src/api/accounts-api.js +130 -0
  12. package/dist/src/api/prompts-api.d.ts +2 -0
  13. package/dist/src/api/prompts-api.js +103 -0
  14. package/dist/src/api/session-api.d.ts +2 -0
  15. package/dist/src/api/session-api.js +207 -0
  16. package/dist/src/api/skills-api.d.ts +2 -0
  17. package/dist/src/api/skills-api.js +64 -0
  18. package/dist/src/api/workspace.d.ts +3 -0
  19. package/dist/src/api/workspace.js +28 -0
  20. package/dist/src/channel.d.ts +11 -0
  21. package/dist/src/channel.js +198 -0
  22. package/dist/src/config-schema.d.ts +93 -0
  23. package/dist/src/config-schema.js +56 -0
  24. package/dist/src/connection/workclaw-client.d.ts +147 -0
  25. package/dist/src/connection/workclaw-client.js +351 -0
  26. package/dist/src/gateway/agent-handlers.d.ts +12 -0
  27. package/dist/src/gateway/agent-handlers.js +433 -0
  28. package/dist/src/gateway/config-writer.d.ts +71 -0
  29. package/dist/src/gateway/config-writer.js +312 -0
  30. package/dist/src/gateway/message-context.d.ts +76 -0
  31. package/dist/src/gateway/message-context.js +499 -0
  32. package/dist/src/gateway/message-dispatcher.d.ts +19 -0
  33. package/dist/src/gateway/message-dispatcher.js +512 -0
  34. package/dist/src/gateway/reconnect.d.ts +27 -0
  35. package/dist/src/gateway/reconnect.js +206 -0
  36. package/dist/src/gateway/skills-handler.d.ts +29 -0
  37. package/dist/src/gateway/skills-handler.js +576 -0
  38. package/dist/src/gateway/skills-list-handler.d.ts +27 -0
  39. package/dist/src/gateway/skills-list-handler.js +233 -0
  40. package/dist/src/gateway/tools-list-handler.d.ts +30 -0
  41. package/dist/src/gateway/tools-list-handler.js +101 -0
  42. package/dist/src/gateway/workclaw-gateway.d.ts +14 -0
  43. package/dist/src/gateway/workclaw-gateway.js +223 -0
  44. package/dist/src/media/upload.d.ts +13 -0
  45. package/dist/src/media/upload.js +125 -0
  46. package/dist/src/outbound/index.d.ts +36 -0
  47. package/dist/src/outbound/index.js +123 -0
  48. package/dist/src/outbound/workclaw-sender.d.ts +36 -0
  49. package/dist/src/outbound/workclaw-sender.js +95 -0
  50. package/dist/src/runtime.d.ts +116 -0
  51. package/dist/src/runtime.js +374 -0
  52. package/dist/src/secret-contract-api.d.ts +4 -0
  53. package/dist/src/secret-contract-api.js +4 -0
  54. package/dist/src/send.d.ts +1 -0
  55. package/dist/src/send.js +1 -0
  56. package/dist/src/setup-api.d.ts +3 -0
  57. package/dist/src/setup-api.js +3 -0
  58. package/dist/src/setup-core.d.ts +3 -0
  59. package/dist/src/setup-core.js +13 -0
  60. package/dist/src/setup-surface.d.ts +7 -0
  61. package/dist/src/setup-surface.js +363 -0
  62. package/dist/src/tools/openclaw-workclaw-cron/api/index.d.ts +93 -0
  63. package/dist/src/tools/openclaw-workclaw-cron/api/index.js +209 -0
  64. package/dist/src/tools/openclaw-workclaw-cron/index.d.ts +10 -0
  65. package/{src/tools/openclaw-workclaw-cron/index.ts → dist/src/tools/openclaw-workclaw-cron/index.js} +33 -39
  66. package/dist/src/tools/openclaw-workclaw-cron/src/add/params.d.ts +16 -0
  67. package/dist/src/tools/openclaw-workclaw-cron/src/add/params.js +152 -0
  68. package/dist/src/tools/openclaw-workclaw-cron/src/add/sync.d.ts +18 -0
  69. package/dist/src/tools/openclaw-workclaw-cron/src/add/sync.js +162 -0
  70. package/dist/src/tools/openclaw-workclaw-cron/src/disable/params.d.ts +14 -0
  71. package/dist/src/tools/openclaw-workclaw-cron/src/disable/params.js +80 -0
  72. package/dist/src/tools/openclaw-workclaw-cron/src/disable/sync.d.ts +14 -0
  73. package/dist/src/tools/openclaw-workclaw-cron/src/disable/sync.js +107 -0
  74. package/dist/src/tools/openclaw-workclaw-cron/src/enable/params.d.ts +14 -0
  75. package/dist/src/tools/openclaw-workclaw-cron/src/enable/params.js +80 -0
  76. package/dist/src/tools/openclaw-workclaw-cron/src/enable/sync.d.ts +14 -0
  77. package/dist/src/tools/openclaw-workclaw-cron/src/enable/sync.js +107 -0
  78. package/dist/src/tools/openclaw-workclaw-cron/src/notify/sync.d.ts +17 -0
  79. package/dist/src/tools/openclaw-workclaw-cron/src/notify/sync.js +127 -0
  80. package/dist/src/tools/openclaw-workclaw-cron/src/remove/params.d.ts +14 -0
  81. package/dist/src/tools/openclaw-workclaw-cron/src/remove/params.js +87 -0
  82. package/dist/src/tools/openclaw-workclaw-cron/src/remove/sync.d.ts +14 -0
  83. package/dist/src/tools/openclaw-workclaw-cron/src/remove/sync.js +107 -0
  84. package/dist/src/tools/openclaw-workclaw-cron/src/update/params.d.ts +17 -0
  85. package/dist/src/tools/openclaw-workclaw-cron/src/update/params.js +164 -0
  86. package/dist/src/tools/openclaw-workclaw-cron/src/update/sync.d.ts +18 -0
  87. package/dist/src/tools/openclaw-workclaw-cron/src/update/sync.js +135 -0
  88. package/dist/src/tools/openclaw-workclaw-cron/types/index.d.ts +52 -0
  89. package/dist/src/tools/openclaw-workclaw-cron/types/index.js +4 -0
  90. package/dist/src/tools/openclaw-workclaw-cron/utils/index.d.ts +40 -0
  91. package/dist/src/tools/openclaw-workclaw-cron/utils/index.js +122 -0
  92. package/dist/src/tools/openclaw-workclaw-system/index.d.ts +10 -0
  93. package/{src/tools/openclaw-workclaw-system/index.ts → dist/src/tools/openclaw-workclaw-system/index.js} +15 -17
  94. package/dist/src/tools/openclaw-workclaw-system/src/get/index.d.ts +8 -0
  95. package/dist/src/tools/openclaw-workclaw-system/src/get/index.js +64 -0
  96. package/dist/src/tools/openclaw-workclaw-system/src/token/index.d.ts +8 -0
  97. package/dist/src/tools/openclaw-workclaw-system/src/token/index.js +78 -0
  98. package/dist/src/types.d.ts +39 -0
  99. package/dist/src/types.js +1 -0
  100. package/dist/src/utils/content.d.ts +15 -0
  101. package/dist/src/utils/content.js +38 -0
  102. package/openclaw.plugin.json +1 -0
  103. package/package.json +62 -42
  104. package/skills/openclaw-workclaw-cron/SKILL.md +45 -28
  105. package/index.ts +0 -302
  106. package/src/accounts.ts +0 -287
  107. package/src/api/accounts-api.ts +0 -157
  108. package/src/api/prompts-api.ts +0 -123
  109. package/src/api/session-api.ts +0 -247
  110. package/src/api/skills-api.ts +0 -74
  111. package/src/api/workspace.ts +0 -43
  112. package/src/channel.ts +0 -227
  113. package/src/config-schema.ts +0 -110
  114. package/src/connection/workclaw-client.ts +0 -656
  115. package/src/gateway/agent-handlers.ts +0 -557
  116. package/src/gateway/config-writer.ts +0 -311
  117. package/src/gateway/message-context.ts +0 -422
  118. package/src/gateway/message-dispatcher.ts +0 -601
  119. package/src/gateway/reconnect.ts +0 -149
  120. package/src/gateway/skills-handler.ts +0 -805
  121. package/src/gateway/skills-list-handler.ts +0 -332
  122. package/src/gateway/tools-list-handler.ts +0 -162
  123. package/src/gateway/workclaw-gateway.ts +0 -525
  124. package/src/media/upload.ts +0 -168
  125. package/src/outbound/index.ts +0 -183
  126. package/src/outbound/workclaw-sender.ts +0 -157
  127. package/src/runtime.ts +0 -400
  128. package/src/send.ts +0 -1
  129. package/src/tools/openclaw-workclaw-cron/api/index.ts +0 -326
  130. package/src/tools/openclaw-workclaw-cron/src/add/params.ts +0 -176
  131. package/src/tools/openclaw-workclaw-cron/src/add/sync.ts +0 -188
  132. package/src/tools/openclaw-workclaw-cron/src/disable/params.ts +0 -100
  133. package/src/tools/openclaw-workclaw-cron/src/disable/sync.ts +0 -127
  134. package/src/tools/openclaw-workclaw-cron/src/enable/params.ts +0 -100
  135. package/src/tools/openclaw-workclaw-cron/src/enable/sync.ts +0 -127
  136. package/src/tools/openclaw-workclaw-cron/src/notify/sync.ts +0 -148
  137. package/src/tools/openclaw-workclaw-cron/src/remove/params.ts +0 -109
  138. package/src/tools/openclaw-workclaw-cron/src/remove/sync.ts +0 -127
  139. package/src/tools/openclaw-workclaw-cron/src/update/params.ts +0 -197
  140. package/src/tools/openclaw-workclaw-cron/src/update/sync.ts +0 -161
  141. package/src/tools/openclaw-workclaw-cron/types/index.ts +0 -55
  142. package/src/tools/openclaw-workclaw-cron/utils/index.ts +0 -141
  143. package/src/tools/openclaw-workclaw-system/src/get/index.ts +0 -77
  144. package/src/tools/openclaw-workclaw-system/src/token/index.ts +0 -93
  145. package/src/types.ts +0 -60
  146. package/src/utils/content.ts +0 -40
  147. package/tsconfig.json +0 -11
@@ -1,157 +0,0 @@
1
- import type { OpenClawPluginApi } from 'openclaw/plugin-sdk'
2
- import { Buffer } from 'node:buffer'
3
- import { normalizeAccountId } from '../accounts.js'
4
-
5
- function sendJson(res: any, statusCode: number, payload: unknown): void {
6
- res.statusCode = statusCode
7
- res.setHeader('Content-Type', 'application/json')
8
- res.end(JSON.stringify(payload))
9
- }
10
-
11
- async function readRequestBody(req: any): Promise<string> {
12
- const chunks: Buffer[] = []
13
- await new Promise<void>((resolve, reject) => {
14
- req.on('data', (chunk: any) => {
15
- chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk))
16
- })
17
- req.on('end', () => resolve())
18
- req.on('error', (err: unknown) => reject(err))
19
- })
20
- return Buffer.concat(chunks).toString('utf-8')
21
- }
22
-
23
- async function loadRuntimeConfig(api: OpenClawPluginApi): Promise<any> {
24
- const runtimeConfig = (api.runtime as any)?.config
25
- if (runtimeConfig?.loadConfig) {
26
- return runtimeConfig.loadConfig()
27
- }
28
- return api.config
29
- }
30
-
31
- async function writeRuntimeConfig(api: OpenClawPluginApi, config: any): Promise<void> {
32
- const runtimeConfig = (api.runtime as any)?.config
33
- if (runtimeConfig?.writeConfigFile) {
34
- await runtimeConfig.writeConfigFile(config)
35
- return
36
- }
37
- throw new Error('Config write not supported')
38
- }
39
-
40
- function getAccountMap(config: any): Record<string, any> {
41
- const channels = config?.channels && typeof config.channels === 'object' ? config.channels : {}
42
- const openclawWorkclaw
43
- = channels['openclaw-workclaw'] && typeof channels['openclaw-workclaw'] === 'object' ? channels['openclaw-workclaw'] : {}
44
- const accounts
45
- = openclawWorkclaw.accounts && typeof openclawWorkclaw.accounts === 'object' ? openclawWorkclaw.accounts : {}
46
- return { ...accounts }
47
- }
48
-
49
- function withAccounts(config: any, accounts: Record<string, any>): any {
50
- const channels = config?.channels && typeof config.channels === 'object' ? config.channels : {}
51
- const openclawWorkclaw
52
- = channels['openclaw-workclaw'] && typeof channels['openclaw-workclaw'] === 'object' ? channels['openclaw-workclaw'] : {}
53
- return {
54
- ...config,
55
- channels: {
56
- ...channels,
57
- 'openclaw-workclaw': {
58
- ...openclawWorkclaw,
59
- accounts,
60
- },
61
- },
62
- }
63
- }
64
-
65
- export function createAccountsApiHandler(api: OpenClawPluginApi) {
66
- return async (req: any, res: any) => {
67
- const method = String(req.method ?? 'GET').toUpperCase()
68
- const url = new URL(req.url ?? '', 'http://localhost')
69
- const idRaw = url.searchParams.get('id') ?? url.searchParams.get('accountId')
70
- const accountId = idRaw ? normalizeAccountId(idRaw) : null
71
-
72
- if (method === 'GET') {
73
- const cfg = await loadRuntimeConfig(api)
74
- const accounts = getAccountMap(cfg)
75
-
76
- if (accountId) {
77
- const account = accounts[accountId]
78
- if (!account) {
79
- sendJson(res, 404, { ok: false, error: 'Not Found' })
80
- return
81
- }
82
- sendJson(res, 200, { ok: true, accountId, config: account })
83
- return
84
- }
85
-
86
- const entries = Object.entries(accounts).map(([id, config]) => ({
87
- accountId: id,
88
- config,
89
- }))
90
- sendJson(res, 200, { ok: true, accounts: entries })
91
- return
92
- }
93
-
94
- if (method === 'POST' || method === 'PUT') {
95
- const raw = await readRequestBody(req)
96
- let input: any = {}
97
- try {
98
- input = raw ? JSON.parse(raw) : {}
99
- }
100
- catch {
101
- sendJson(res, 400, { ok: false, error: 'Invalid JSON' })
102
- return
103
- }
104
-
105
- const inputId = input?.accountId ?? input?.id
106
- const normalizedId = inputId ? normalizeAccountId(String(inputId)) : ''
107
- const patch = input?.config
108
-
109
- if (!normalizedId || !patch || typeof patch !== 'object') {
110
- sendJson(res, 400, { ok: false, error: 'Missing accountId or config' })
111
- return
112
- }
113
-
114
- const cfg = await loadRuntimeConfig(api)
115
- const accounts = getAccountMap(cfg)
116
-
117
- if (method === 'POST' && accounts[normalizedId]) {
118
- sendJson(res, 409, { ok: false, error: 'Account already exists' })
119
- return
120
- }
121
-
122
- if (method === 'PUT' && !accounts[normalizedId]) {
123
- sendJson(res, 404, { ok: false, error: 'Not Found' })
124
- return
125
- }
126
-
127
- const next = method === 'PUT' ? { ...accounts[normalizedId], ...patch } : patch
128
- const updated = withAccounts(cfg, { ...accounts, [normalizedId]: next })
129
- await writeRuntimeConfig(api, updated)
130
- sendJson(res, 200, { ok: true, accountId: normalizedId })
131
- return
132
- }
133
-
134
- if (method === 'DELETE') {
135
- if (!accountId) {
136
- sendJson(res, 400, { ok: false, error: 'Missing accountId' })
137
- return
138
- }
139
-
140
- const cfg = await loadRuntimeConfig(api)
141
- const accounts = getAccountMap(cfg)
142
-
143
- if (!accounts[accountId]) {
144
- sendJson(res, 404, { ok: false, error: 'Not Found' })
145
- return
146
- }
147
-
148
- const { [accountId]: _ignored, ...rest } = accounts
149
- const updated = withAccounts(cfg, rest)
150
- await writeRuntimeConfig(api, updated)
151
- sendJson(res, 200, { ok: true, accountId, deleted: true })
152
- return
153
- }
154
-
155
- sendJson(res, 405, { ok: false, error: 'Method Not Allowed' })
156
- }
157
- }
@@ -1,123 +0,0 @@
1
- import type { OpenClawPluginApi } from 'openclaw/plugin-sdk'
2
- import { Buffer } from 'node:buffer'
3
- import { mkdir, readFile, stat, unlink, writeFile } from 'node:fs/promises'
4
- import path from 'node:path'
5
-
6
- import { resolveWorkspaceDir } from './workspace.js'
7
-
8
- const promptNames = ['SOUL.md', 'IDENTITY.md'] as const
9
-
10
- function normalizePromptName(raw: string | null | undefined): (typeof promptNames)[number] | null {
11
- const name = String(raw ?? '').trim().toUpperCase()
12
- if (name === 'SOUL' || name === 'SOUL.MD')
13
- return 'SOUL.md'
14
- if (name === 'IDENTITY' || name === 'IDENTITY.MD')
15
- return 'IDENTITY.md'
16
- return null
17
- }
18
-
19
- function resolvePromptPath(api: OpenClawPluginApi, name: (typeof promptNames)[number]): string {
20
- return path.join(resolveWorkspaceDir(api), name)
21
- }
22
-
23
- function sendJson(res: any, statusCode: number, payload: unknown): void {
24
- res.statusCode = statusCode
25
- res.setHeader('Content-Type', 'application/json')
26
- res.end(JSON.stringify(payload))
27
- }
28
-
29
- export function createPromptsApiHandler(api: OpenClawPluginApi) {
30
- return async (req: any, res: any) => {
31
- const method = String(req.method ?? 'GET').toUpperCase()
32
- const url = new URL(req.url ?? '', 'http://localhost')
33
- const name = normalizePromptName(url.searchParams.get('name'))
34
-
35
- if (method === 'GET') {
36
- if (name) {
37
- const filePath = resolvePromptPath(api, name)
38
- try {
39
- const content = await readFile(filePath, 'utf-8')
40
- sendJson(res, 200, { ok: true, name, content })
41
- return
42
- }
43
- catch {
44
- sendJson(res, 404, { ok: false, error: 'Not Found' })
45
- return
46
- }
47
- }
48
-
49
- const entries = await Promise.all(
50
- promptNames.map(async (promptName) => {
51
- const filePath = resolvePromptPath(api, promptName)
52
- try {
53
- const info = await stat(filePath)
54
- return { name: promptName, exists: true, bytes: info.size }
55
- }
56
- catch {
57
- return { name: promptName, exists: false, bytes: 0 }
58
- }
59
- }),
60
- )
61
- sendJson(res, 200, { ok: true, entries })
62
- return
63
- }
64
-
65
- if (method === 'PUT' || method === 'POST') {
66
- const raw = await readRequestBody(req)
67
- let input: any = {}
68
- try {
69
- input = raw ? JSON.parse(raw) : {}
70
- }
71
- catch {
72
- sendJson(res, 400, { ok: false, error: 'Invalid JSON' })
73
- return
74
- }
75
-
76
- const targetName = normalizePromptName(input?.name)
77
- const content = typeof input?.content === 'string' ? input.content : null
78
-
79
- if (!targetName || content === null) {
80
- sendJson(res, 400, { ok: false, error: 'Missing name or content' })
81
- return
82
- }
83
-
84
- const workspaceDir = resolveWorkspaceDir(api)
85
- await mkdir(workspaceDir, { recursive: true })
86
- await writeFile(resolvePromptPath(api, targetName), content, { encoding: 'utf-8' })
87
- sendJson(res, 200, { ok: true, name: targetName })
88
- return
89
- }
90
-
91
- if (method === 'DELETE') {
92
- if (!name) {
93
- sendJson(res, 400, { ok: false, error: 'Missing name' })
94
- return
95
- }
96
-
97
- const filePath = resolvePromptPath(api, name)
98
- try {
99
- await unlink(filePath)
100
- sendJson(res, 200, { ok: true, name, deleted: true })
101
- return
102
- }
103
- catch {
104
- sendJson(res, 200, { ok: true, name, deleted: false })
105
- return
106
- }
107
- }
108
-
109
- sendJson(res, 405, { ok: false, error: 'Method Not Allowed' })
110
- }
111
- }
112
-
113
- async function readRequestBody(req: any): Promise<string> {
114
- const chunks: Buffer[] = []
115
- await new Promise<void>((resolve, reject) => {
116
- req.on('data', (chunk: any) => {
117
- chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk))
118
- })
119
- req.on('end', () => resolve())
120
- req.on('error', (err: unknown) => reject(err))
121
- })
122
- return Buffer.concat(chunks).toString('utf-8')
123
- }
@@ -1,247 +0,0 @@
1
- import type { OpenClawPluginApi } from 'openclaw/plugin-sdk'
2
- import { Buffer } from 'node:buffer'
3
- import { getOpenclawWorkclawRuntime } from '../runtime.js'
4
-
5
- function sendJson(res: any, statusCode: number, payload: unknown): void {
6
- res.statusCode = statusCode
7
- res.setHeader('Content-Type', 'application/json')
8
- res.end(JSON.stringify(payload))
9
- }
10
-
11
- async function readRequestBody(req: any): Promise<string> {
12
- const chunks: Buffer[] = []
13
- await new Promise<void>((resolve, reject) => {
14
- req.on('data', (chunk: any) => {
15
- chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk))
16
- })
17
- req.on('end', () => resolve())
18
- req.on('error', (err: unknown) => reject(err))
19
- })
20
- return Buffer.concat(chunks).toString('utf-8')
21
- }
22
-
23
- /**
24
- * 构建会话 key
25
- * 格式: openclaw-workclaw:{accountId}:{userId}
26
- */
27
- function buildSessionKey(accountId: string, userId: string): string {
28
- return `openclaw-workclaw:${accountId}:${userId}`
29
- }
30
-
31
- /**
32
- * 解析会话 key
33
- */
34
- function parseSessionKey(sessionKey: string): {
35
- channel: string
36
- accountId: string
37
- userId: string
38
- } | null {
39
- const parts = sessionKey.split(':')
40
- if (parts.length !== 3 || parts[0] !== 'openclaw-workclaw') {
41
- return null
42
- }
43
- return {
44
- channel: parts[0],
45
- accountId: parts[1],
46
- userId: parts[2],
47
- }
48
- }
49
-
50
- /**
51
- * 重置/开启新会话
52
- * 通过发送系统事件 /new 到指定会话
53
- */
54
- async function resetSession(
55
- sessionKey: string,
56
- log?: { info?: (msg: string) => void, error?: (msg: string) => void },
57
- ): Promise<{ success: boolean, message: string }> {
58
- try {
59
- const runtime = getOpenclawWorkclawRuntime()
60
-
61
- // 使用 system.enqueueSystemEvent 发送 /new 命令
62
- runtime.system.enqueueSystemEvent('/new', {
63
- sessionKey,
64
- contextKey: null,
65
- })
66
-
67
- log?.info?.(`[SessionAPI] Sent /new command to session: ${sessionKey}`)
68
-
69
- return {
70
- success: true,
71
- message: `Session reset command sent to ${sessionKey}`,
72
- }
73
- }
74
- catch (err) {
75
- const errorMsg = `Failed to reset session: ${String(err)}`
76
- log?.error?.(`[SessionAPI] ${errorMsg}`)
77
- return { success: false, message: errorMsg }
78
- }
79
- }
80
-
81
- /**
82
- * 获取会话存储路径
83
- */
84
- function getSessionStorePath(
85
- agentId?: string,
86
- log?: { info?: (msg: string) => void, error?: (msg: string) => void },
87
- ): string | null {
88
- try {
89
- const runtime = getOpenclawWorkclawRuntime()
90
- const storePath = runtime.channel.session.resolveStorePath(undefined, {
91
- agentId,
92
- })
93
- return storePath
94
- }
95
- catch (err) {
96
- log?.error?.(`[SessionAPI] Failed to resolve store path: ${String(err)}`)
97
- return null
98
- }
99
- }
100
-
101
- /**
102
- * 读取会话最后更新时间
103
- */
104
- async function getSessionLastUpdated(
105
- sessionKey: string,
106
- log?: { info?: (msg: string) => void, error?: (msg: string) => void },
107
- ): Promise<number | null> {
108
- try {
109
- const runtime = getOpenclawWorkclawRuntime()
110
- const storePath = runtime.channel.session.resolveStorePath()
111
- const updatedAt = runtime.channel.session.readSessionUpdatedAt({
112
- storePath,
113
- sessionKey,
114
- })
115
- return updatedAt ?? null
116
- }
117
- catch (err) {
118
- log?.error?.(`[SessionAPI] Failed to read session updated at: ${String(err)}`)
119
- return null
120
- }
121
- }
122
-
123
- export function createSessionApiHandler(api: OpenClawPluginApi) {
124
- return async (req: any, res: any) => {
125
- const method = String(req.method ?? 'GET').toUpperCase()
126
- const url = new URL(req.url ?? '', 'http://localhost')
127
- // 移除尾部斜杠并获取路径部分
128
- const fullPath = url.pathname.replace(/\/+$/, '')
129
- // 提取子路径(去掉 /openclaw-workclaw/sessions 前缀)
130
- const subPath = fullPath.replace(/^\/openclaw-workclaw\/sessions/, '').replace(/^\//, '') || '/'
131
-
132
- const log = {
133
- info: (msg: string) => api.logger?.info?.(`[SessionAPI] ${msg}`),
134
- error: (msg: string) => api.logger?.error?.(`[SessionAPI] ${msg}`),
135
- }
136
-
137
- log?.info?.(`[SessionAPI] ${method} ${fullPath} (subPath: ${subPath})`)
138
-
139
- // GET /sessions 或 /sessions/ - 列出会话信息
140
- if (method === 'GET' && (subPath === '/' || subPath === '')) {
141
- const sessionKey = url.searchParams.get('sessionKey')
142
-
143
- if (sessionKey) {
144
- // 获取特定会话信息
145
- const parsed = parseSessionKey(sessionKey)
146
- const updatedAt = await getSessionLastUpdated(sessionKey, log)
147
-
148
- sendJson(res, 200, {
149
- ok: true,
150
- session: {
151
- sessionKey,
152
- parsed,
153
- lastUpdatedAt: updatedAt,
154
- lastUpdatedAtFormatted: updatedAt
155
- ? new Date(updatedAt).toISOString()
156
- : null,
157
- },
158
- })
159
- return
160
- }
161
-
162
- // 返回 API 信息
163
- sendJson(res, 200, {
164
- ok: true,
165
- message: 'Session Management API',
166
- endpoints: {
167
- 'GET /sessions': '获取会话信息 (可选参数: sessionKey)',
168
- 'POST /sessions/reset': '重置/开启新会话 (参数: accountId + userId 或直接提供 sessionKey)',
169
- 'GET /sessions/store-path': '获取会话存储路径 (可选参数: agentId)',
170
- },
171
- })
172
- return
173
- }
174
-
175
- // POST /sessions/reset - 重置会话
176
- if (method === 'POST' && subPath === 'reset') {
177
- const raw = await readRequestBody(req)
178
- let input: any = {}
179
- try {
180
- input = raw ? JSON.parse(raw) : {}
181
- }
182
- catch {
183
- sendJson(res, 400, { ok: false, error: 'Invalid JSON' })
184
- return
185
- }
186
-
187
- const { accountId, userId, sessionKey: directSessionKey } = input
188
-
189
- let sessionKey: string
190
- if (directSessionKey) {
191
- sessionKey = directSessionKey
192
- }
193
- else if (accountId && userId) {
194
- sessionKey = buildSessionKey(accountId, userId)
195
- }
196
- else {
197
- sendJson(res, 400, {
198
- ok: false,
199
- error: 'Missing required fields: either provide \'sessionKey\' or both \'accountId\' and \'userId\'',
200
- })
201
- return
202
- }
203
-
204
- const result = await resetSession(sessionKey, log)
205
-
206
- if (result.success) {
207
- sendJson(res, 200, {
208
- ok: true,
209
- message: result.message,
210
- sessionKey,
211
- })
212
- }
213
- else {
214
- sendJson(res, 500, {
215
- ok: false,
216
- error: result.message,
217
- sessionKey,
218
- })
219
- }
220
- return
221
- }
222
-
223
- // GET /sessions/store-path - 获取会话存储路径
224
- if (method === 'GET' && subPath === 'store-path') {
225
- const agentId = url.searchParams.get('agentId') ?? undefined
226
- const storePath = getSessionStorePath(agentId, log)
227
-
228
- if (storePath) {
229
- sendJson(res, 200, {
230
- ok: true,
231
- storePath,
232
- agentId: agentId ?? 'default',
233
- })
234
- }
235
- else {
236
- sendJson(res, 500, {
237
- ok: false,
238
- error: 'Failed to resolve session store path',
239
- })
240
- }
241
- return
242
- }
243
-
244
- // 404
245
- sendJson(res, 404, { ok: false, error: 'Not Found' })
246
- }
247
- }
@@ -1,74 +0,0 @@
1
- import type { OpenClawPluginApi } from 'openclaw/plugin-sdk'
2
- import { exec } from 'node:child_process'
3
- import { promisify } from 'node:util'
4
-
5
- const execAsync = promisify(exec)
6
-
7
- function sendJson(res: any, statusCode: number, payload: unknown): void {
8
- res.statusCode = statusCode
9
- res.setHeader('Content-Type', 'application/json')
10
- res.end(JSON.stringify(payload))
11
- }
12
-
13
- export function createSkillsApiHandler(api: OpenClawPluginApi) {
14
- return async (req: any, res: any) => {
15
- const method = String(req.method ?? 'GET').toUpperCase()
16
-
17
- if (method === 'GET') {
18
- try {
19
- // 调用 openclaw skills list 命令
20
- const { stdout, stderr } = await execAsync('openclaw skills list --json', {
21
- timeout: 10000, // 10秒超时
22
- })
23
-
24
- if (stderr && !stdout) {
25
- api.logger.error(`Skills list error: ${stderr}`)
26
- sendJson(res, 500, { ok: false, error: 'Failed to list skills' })
27
- return
28
- }
29
-
30
- // 尝试解析JSON输出
31
- let skillsData: any
32
- try {
33
- skillsData = JSON.parse(stdout)
34
- }
35
- catch {
36
- // 如果不支持--json输出,尝试解析文本输出
37
- const lines = stdout.split('\n')
38
- const skills = lines
39
- .filter((line: string) => line.trim() && !line.includes('Skills'))
40
- .map((line: string) => {
41
- const parts = line.split(/\s{2,}/).map((p: string) => p.trim())
42
- if (parts.length >= 3) {
43
- return {
44
- status: parts[0]?.includes('✓') ? 'ready' : 'missing',
45
- name: parts[1],
46
- description: parts[2],
47
- source: parts[3] || 'openclaw-bundled',
48
- }
49
- }
50
- return null
51
- })
52
- .filter((s: any) => s !== null)
53
-
54
- skillsData = {
55
- total: skills.length,
56
- ready: skills.filter((s: any) => s.status === 'ready').length,
57
- missing: skills.filter((s: any) => s.status === 'missing').length,
58
- skills,
59
- }
60
- }
61
-
62
- sendJson(res, 200, { ok: true, ...skillsData })
63
- return
64
- }
65
- catch (error: any) {
66
- api.logger.error(`Skills API error: ${error.message}`)
67
- sendJson(res, 500, { ok: false, error: error.message })
68
- return
69
- }
70
- }
71
-
72
- sendJson(res, 405, { ok: false, error: 'Method Not Allowed' })
73
- }
74
- }
@@ -1,43 +0,0 @@
1
- import type { OpenClawPluginApi } from 'openclaw/plugin-sdk'
2
- import os from 'node:os'
3
- import path from 'node:path'
4
- import process from 'node:process'
5
-
6
- export function resolveWorkspaceDir(api: OpenClawPluginApi): string {
7
- const agents = api.config.agents?.list ?? []
8
- const defaultAgent
9
- = agents.find(agent => agent?.default === true)
10
- ?? agents.find(agent => String(agent?.id ?? '').toLowerCase() === 'main')
11
-
12
- const workspaceRaw
13
- = (typeof defaultAgent?.workspace === 'string' && defaultAgent.workspace.trim())
14
- || (typeof api.config.agents?.defaults?.workspace === 'string'
15
- && api.config.agents.defaults.workspace.trim())
16
-
17
- if (workspaceRaw) {
18
- return api.resolvePath(workspaceRaw)
19
- }
20
-
21
- const profile = process.env.OPENCLAW_PROFILE?.trim()
22
- const suffix
23
- = profile && profile.toLowerCase() !== 'default' ? `workspace-${profile}` : 'workspace'
24
- return path.join(os.homedir(), '.openclaw', suffix)
25
- }
26
-
27
- export function isDefaultWorkspace(api: OpenClawPluginApi): boolean {
28
- const agents = api.config.agents?.list ?? []
29
- const defaultAgent
30
- = agents.find(agent => agent?.default === true)
31
- ?? agents.find(agent => String(agent?.id ?? '').toLowerCase() === 'main')
32
-
33
- const workspaceRaw
34
- = (typeof defaultAgent?.workspace === 'string' && defaultAgent.workspace.trim())
35
- || (typeof api.config.agents?.defaults?.workspace === 'string'
36
- && api.config.agents.defaults.workspace.trim())
37
-
38
- if (workspaceRaw)
39
- return false
40
-
41
- const profile = process.env.OPENCLAW_PROFILE?.trim()
42
- return !profile || profile.toLowerCase() === 'default'
43
- }