@auto-ai/agent 2.1.120 → 2.1.122
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/.env.example +2 -1
- package/dist/404/index.html +1 -1
- package/dist/404.html +1 -1
- package/dist/_next/static/chunks/19aa69c07b3642d5.js +1 -0
- package/dist/_next/static/chunks/24459c7365a2b28b.js +1 -0
- package/dist/_next/static/chunks/{d3e2070a86378cfb.js → 33eeef286c328da0.js} +1 -1
- package/dist/_next/static/chunks/3407244006d6a98a.js +1 -0
- package/dist/_next/static/chunks/566e152e480b267a.js +1 -0
- package/dist/_next/static/chunks/6ad4268160f361a6.js +1 -0
- package/dist/_next/static/chunks/{d9a278a2a26e8ee5.js → 6f9a48a7f83e669a.js} +1 -1
- package/dist/_next/static/chunks/7658b5b9c2865eb1.js +1 -0
- package/dist/_next/static/chunks/7a5fd448b280dd64.js +1 -0
- package/dist/_next/static/chunks/{fa677aa06f1c0539.js → 98f0c5604e839ba2.js} +1 -1
- package/dist/_next/static/chunks/a38eccf0bb5ca1e9.js +1 -0
- package/dist/_next/static/chunks/b29a354245bfc377.css +1 -0
- package/dist/_next/static/chunks/b76778c5811ef7a1.js +1 -0
- package/dist/_next/static/chunks/c2bed9d5fa7be4bd.js +1 -0
- package/dist/_next/static/chunks/c5a4977aae6a008c.js +1 -0
- package/dist/_next/static/chunks/e7bd145455a541af.css +4 -0
- package/dist/_next/static/chunks/f2bb685629307d4d.js +1 -0
- package/dist/_next/static/chunks/f7e71ce1c236f806.js +1 -0
- package/dist/_next/static/chunks/{67acc15b8a448e1a.js → fc4be3bcf72559a0.js} +1 -1
- package/dist/index.html +1 -1
- package/dist/index.txt +16 -16
- package/dist/manage/about/index.html +2 -2
- package/dist/manage/about/index.txt +19 -19
- package/dist/manage/add-account/basic/index.html +2 -2
- package/dist/manage/add-account/basic/index.txt +23 -22
- package/dist/manage/add-account/index.html +2 -2
- package/dist/manage/add-account/index.txt +22 -23
- package/dist/manage/agent-teams/index.html +2 -2
- package/dist/manage/agent-teams/index.txt +21 -21
- package/dist/manage/env/index.html +2 -2
- package/dist/manage/env/index.txt +21 -21
- package/dist/manage/general/index.html +2 -2
- package/dist/manage/general/index.txt +19 -19
- package/dist/manage/index.html +1 -1
- package/dist/manage/index.txt +16 -16
- package/dist/manage/mcp/index.html +2 -2
- package/dist/manage/mcp/index.txt +21 -21
- package/dist/manage/permissions/index.html +2 -2
- package/dist/manage/permissions/index.txt +19 -19
- package/dist/manage/skills/index.html +2 -2
- package/dist/manage/skills/index.txt +21 -21
- package/dist/manage/task/index.html +2 -2
- package/dist/manage/task/index.txt +19 -19
- package/dist/manage/teams/index.html +2 -2
- package/dist/manage/teams/index.txt +19 -19
- package/dist/manage/tools/index.html +2 -2
- package/dist/manage/tools/index.txt +21 -21
- package/dist/ws-test.html +129 -191
- package/mcps-runtime/claude-geelib-channel/.mcp.json +18 -0
- package/mcps-runtime/claude-geelib-channel/server/boot.mjs +32 -0
- package/mcps-runtime/claude-geelib-channel/server/geelib-api.mjs +192 -0
- package/mcps-runtime/claude-geelib-channel/server/geelib-auth.mjs +147 -0
- package/mcps-runtime/claude-geelib-channel/server/geelib-client.mjs +63 -0
- package/mcps-runtime/claude-geelib-channel/server/index.mjs +451 -0
- package/mcps-runtime/claude-geelib-channel/server/poll.mjs +457 -0
- package/mcps-runtime/claude-geelib-channel/server/state.mjs +216 -0
- package/package.json +6 -6
- package/tools-runtime/code-tool/index.cjs +1 -0
- package/tools-runtime/code-tool/src/index.ts +7 -1
- package/tools-runtime/git-tool/index.cjs +69 -5
- package/tools-runtime/git-tool/src/workspace.ts +101 -4
- package/tools-runtime/git-tool/src/wsInit.ts +26 -3
- package/dist/_next/static/chunks/249606605eed1993.js +0 -1
- package/dist/_next/static/chunks/424d3f3f176d43d7.js +0 -1
- package/dist/_next/static/chunks/4302af16b34bf750.js +0 -1
- package/dist/_next/static/chunks/4a073924e062c459.css +0 -1
- package/dist/_next/static/chunks/6209b42cbfcf288a.js +0 -1
- package/dist/_next/static/chunks/83bf6e4bc0aad48b.css +0 -4
- package/dist/_next/static/chunks/8b34d657c6bbb8df.js +0 -1
- package/dist/_next/static/chunks/904ad158fd721117.js +0 -1
- package/dist/_next/static/chunks/99063f8d79783308.js +0 -1
- package/dist/_next/static/chunks/9cd912a1967f10df.js +0 -1
- package/dist/_next/static/chunks/c52dcbe269bb02d0.js +0 -1
- package/dist/_next/static/chunks/d01957b1055b357a.js +0 -1
- package/dist/_next/static/chunks/dacc94cf10289334.js +0 -1
- package/dist/_next/static/chunks/fff9d250e482f00e.js +0 -1
- package/skills-runtime/geelib/SKILL.md +0 -229
- package/skills-runtime/geelib/geelib-poller-state.json +0 -17
- package/skills-runtime/geelib/geelib.mjs +0 -762
- /package/dist/_next/static/{fg4nA8liSSLEgWV4WTZDt → pVB1xZbEW09ksdJlV12Zn}/_buildManifest.js +0 -0
- /package/dist/_next/static/{fg4nA8liSSLEgWV4WTZDt → pVB1xZbEW09ksdJlV12Zn}/_clientMiddlewareManifest.json +0 -0
- /package/dist/_next/static/{fg4nA8liSSLEgWV4WTZDt → pVB1xZbEW09ksdJlV12Zn}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
import {
|
|
2
|
+
cmdGetUserInfo,
|
|
3
|
+
cmdListMatterStatuses,
|
|
4
|
+
cmdListMatterTypes,
|
|
5
|
+
cmdListMatters,
|
|
6
|
+
cmdListProjects,
|
|
7
|
+
cmdUpdateMatter,
|
|
8
|
+
extractMatterList,
|
|
9
|
+
} from './geelib-api.mjs'
|
|
10
|
+
|
|
11
|
+
/** 轮询仅拉取「未开始」任务。 */
|
|
12
|
+
const POLL_TARGET_STATUS_NAME = '未开始'
|
|
13
|
+
|
|
14
|
+
/** dispatch 前将任务流转到该状态,表示 agent 已接手。 */
|
|
15
|
+
const IN_PROGRESS_STATUS_NAME = '处理中'
|
|
16
|
+
|
|
17
|
+
/** agent 处理完成后流转到该状态。 */
|
|
18
|
+
const SELF_TEST_STATUS_NAME = '自测&联调中'
|
|
19
|
+
|
|
20
|
+
function extractMatterId(row) {
|
|
21
|
+
const id = row.id ?? row.matter_id
|
|
22
|
+
if (typeof id === 'number' && Number.isFinite(id)) {
|
|
23
|
+
return id
|
|
24
|
+
}
|
|
25
|
+
return null
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function extractStatusName(row) {
|
|
29
|
+
return (row.status_name ?? row.matterStatus?.status_name ?? '').trim()
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function extractStatusId(row) {
|
|
33
|
+
const sid = row.matter_status ?? row.status ?? row.matterStatus?.id
|
|
34
|
+
if (typeof sid === 'number' && Number.isFinite(sid)) {
|
|
35
|
+
return sid
|
|
36
|
+
}
|
|
37
|
+
return null
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** 判断状态名是否为轮询目标(未开始)。 */
|
|
41
|
+
function isUnstartedStatusName(name) {
|
|
42
|
+
return (name ?? '').trim() === POLL_TARGET_STATUS_NAME
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** 判断状态名是否为 dispatch 目标(处理中)。 */
|
|
46
|
+
function isInProgressStatusName(name) {
|
|
47
|
+
return (name ?? '').trim() === IN_PROGRESS_STATUS_NAME
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** 判断状态名是否为完成后目标(自测&联调中)。 */
|
|
51
|
+
function isSelfTestStatusName(name) {
|
|
52
|
+
return (name ?? '').trim() === SELF_TEST_STATUS_NAME
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 解析 GEELIB_SUB_ID:支持逗号、分号、空格分隔的多个项目 ID。
|
|
57
|
+
* 未配置时返回空数组,由调用方决定是否回退到首个可访问项目。
|
|
58
|
+
*/
|
|
59
|
+
export function parseGeelibSubIds(raw = process.env.GEELIB_SUB_ID) {
|
|
60
|
+
if (raw === undefined || raw === null) {
|
|
61
|
+
return []
|
|
62
|
+
}
|
|
63
|
+
const text = String(raw).trim()
|
|
64
|
+
if (text.length === 0) {
|
|
65
|
+
return []
|
|
66
|
+
}
|
|
67
|
+
const ids = []
|
|
68
|
+
const seen = new Set()
|
|
69
|
+
for (const part of text.split(/[,;\s]+/)) {
|
|
70
|
+
const n = Number(part.trim())
|
|
71
|
+
if (!Number.isFinite(n) || n <= 0 || seen.has(n)) {
|
|
72
|
+
continue
|
|
73
|
+
}
|
|
74
|
+
seen.add(n)
|
|
75
|
+
ids.push(n)
|
|
76
|
+
}
|
|
77
|
+
return ids
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** 两个 sub_id 列表是否一致(顺序无关)。 */
|
|
81
|
+
function sameSubIdList(a, b) {
|
|
82
|
+
if (a.length !== b.length) {
|
|
83
|
+
return false
|
|
84
|
+
}
|
|
85
|
+
const sa = [...a].sort((x, y) => x - y)
|
|
86
|
+
const sb = [...b].sort((x, y) => x - y)
|
|
87
|
+
return sa.every((v, i) => v === sb[i])
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* 为单个项目发现 type_id、未开始状态 ID、完成后状态 ID。
|
|
92
|
+
*/
|
|
93
|
+
async function discoverProjectConfig(subId) {
|
|
94
|
+
const types = await cmdListMatterTypes(subId)
|
|
95
|
+
if (types.errno !== 2000 || !types.data?.length) {
|
|
96
|
+
throw new Error(`list-matter-types(sub_id=${subId}) 无数据`)
|
|
97
|
+
}
|
|
98
|
+
const taskType =
|
|
99
|
+
types.data.find(t => (t.type_name ?? t.name ?? '').includes('任务')) ??
|
|
100
|
+
types.data[0]
|
|
101
|
+
const typeId = taskType.id ?? taskType.type_id ?? null
|
|
102
|
+
if (typeId === null) {
|
|
103
|
+
throw new Error(`无法从类型列表解析 type_id,sub_id=${subId}`)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const statuses = await cmdListMatterStatuses(subId, typeId)
|
|
107
|
+
if (statuses.errno !== 2000 || !statuses.data?.length) {
|
|
108
|
+
throw new Error(`list-matter-statuses 无数据,sub_id=${subId}`)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const pollStatusIds = []
|
|
112
|
+
let inProgressStatusId = null
|
|
113
|
+
let selfTestStatusId = null
|
|
114
|
+
for (const s of statuses.data) {
|
|
115
|
+
const sid = s.status_id ?? s.matterStatus?.id ?? s.id
|
|
116
|
+
const name = s.matterStatus?.status_name ?? s.name ?? ''
|
|
117
|
+
if (sid === undefined) {
|
|
118
|
+
continue
|
|
119
|
+
}
|
|
120
|
+
if (isUnstartedStatusName(name)) {
|
|
121
|
+
pollStatusIds.push(sid)
|
|
122
|
+
}
|
|
123
|
+
if (isInProgressStatusName(name) && inProgressStatusId === null) {
|
|
124
|
+
inProgressStatusId = sid
|
|
125
|
+
}
|
|
126
|
+
if (isSelfTestStatusName(name) && selfTestStatusId === null) {
|
|
127
|
+
selfTestStatusId = sid
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (pollStatusIds.length === 0) {
|
|
132
|
+
throw new Error(
|
|
133
|
+
`未找到轮询目标状态「${POLL_TARGET_STATUS_NAME}」,sub_id=${subId} type_id=${typeId}`,
|
|
134
|
+
)
|
|
135
|
+
}
|
|
136
|
+
if (inProgressStatusId === null) {
|
|
137
|
+
throw new Error(
|
|
138
|
+
`未找到 dispatch 目标状态「${IN_PROGRESS_STATUS_NAME}」,sub_id=${subId} type_id=${typeId}`,
|
|
139
|
+
)
|
|
140
|
+
}
|
|
141
|
+
if (selfTestStatusId === null) {
|
|
142
|
+
throw new Error(
|
|
143
|
+
`未找到完成后目标状态「${SELF_TEST_STATUS_NAME}」,sub_id=${subId} type_id=${typeId}`,
|
|
144
|
+
)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
sub_id: subId,
|
|
149
|
+
type_id: typeId,
|
|
150
|
+
poll_status_ids: pollStatusIds,
|
|
151
|
+
in_progress_status_id: inProgressStatusId,
|
|
152
|
+
self_test_status_id: selfTestStatusId,
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* 解析本轮 poll 要扫描的项目列表。
|
|
158
|
+
* 优先 GEELIB_SUB_ID;未配置时仅取第一个可访问项目。
|
|
159
|
+
*/
|
|
160
|
+
async function resolvePollSubIds(configuredSubIds) {
|
|
161
|
+
if (configuredSubIds.length > 0) {
|
|
162
|
+
return configuredSubIds
|
|
163
|
+
}
|
|
164
|
+
const projects = await cmdListProjects('')
|
|
165
|
+
const projectList = Array.isArray(projects.data)
|
|
166
|
+
? projects.data
|
|
167
|
+
: projects.data?.list
|
|
168
|
+
if (projects.errno !== 2000 || !projectList?.length) {
|
|
169
|
+
throw new Error('list-projects 未返回可用项目')
|
|
170
|
+
}
|
|
171
|
+
const first = projectList[0]
|
|
172
|
+
const subId = first.id ?? first.sub_id ?? null
|
|
173
|
+
if (subId === null) {
|
|
174
|
+
throw new Error('无法从项目列表解析 sub_id')
|
|
175
|
+
}
|
|
176
|
+
return [subId]
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* 发现/刷新全部轮询项目配置。
|
|
181
|
+
*/
|
|
182
|
+
async function discoverAllProjects(state) {
|
|
183
|
+
const configuredSubIds = parseGeelibSubIds()
|
|
184
|
+
const pollSubIds = await resolvePollSubIds(configuredSubIds)
|
|
185
|
+
const cachedProjects = Array.isArray(state.discovered.projects)
|
|
186
|
+
? state.discovered.projects
|
|
187
|
+
: []
|
|
188
|
+
const cacheValid =
|
|
189
|
+
sameSubIdList(
|
|
190
|
+
state.discovered.configured_sub_ids ?? [],
|
|
191
|
+
configuredSubIds,
|
|
192
|
+
) &&
|
|
193
|
+
pollSubIds.every(subId =>
|
|
194
|
+
cachedProjects.some(
|
|
195
|
+
p =>
|
|
196
|
+
p.sub_id === subId &&
|
|
197
|
+
p.type_id !== null &&
|
|
198
|
+
p.in_progress_status_id !== null &&
|
|
199
|
+
p.self_test_status_id !== null,
|
|
200
|
+
),
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
if (cacheValid && cachedProjects.length === pollSubIds.length) {
|
|
204
|
+
return {
|
|
205
|
+
configured_sub_ids: configuredSubIds,
|
|
206
|
+
projects: cachedProjects.filter(p => pollSubIds.includes(p.sub_id)),
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const projects = []
|
|
211
|
+
for (const subId of pollSubIds) {
|
|
212
|
+
projects.push(await discoverProjectConfig(subId))
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return {
|
|
216
|
+
configured_sub_ids: configuredSubIds,
|
|
217
|
+
projects,
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* 按 executor + 未开始状态拉取工作项。
|
|
223
|
+
*/
|
|
224
|
+
async function listUnstartedMatters(subId, typeId, assigneeId, pollStatusIds) {
|
|
225
|
+
const pollStatusSet = new Set(pollStatusIds)
|
|
226
|
+
const byId = new Map()
|
|
227
|
+
for (const statusId of pollStatusIds) {
|
|
228
|
+
const listResp = await cmdListMatters({
|
|
229
|
+
subId,
|
|
230
|
+
typeId,
|
|
231
|
+
page: 1,
|
|
232
|
+
pageSize: 50,
|
|
233
|
+
assigneeId,
|
|
234
|
+
statusId,
|
|
235
|
+
})
|
|
236
|
+
if (listResp.errno !== 2000) {
|
|
237
|
+
continue
|
|
238
|
+
}
|
|
239
|
+
for (const row of extractMatterList(listResp.data)) {
|
|
240
|
+
const mid = extractMatterId(row)
|
|
241
|
+
if (mid === null || byId.has(mid)) {
|
|
242
|
+
continue
|
|
243
|
+
}
|
|
244
|
+
const rowStatusId = extractStatusId(row)
|
|
245
|
+
const rowStatusName = extractStatusName(row)
|
|
246
|
+
if (rowStatusId !== null && !pollStatusSet.has(rowStatusId)) {
|
|
247
|
+
continue
|
|
248
|
+
}
|
|
249
|
+
if (rowStatusId === null && !isUnstartedStatusName(rowStatusName)) {
|
|
250
|
+
continue
|
|
251
|
+
}
|
|
252
|
+
byId.set(mid, row)
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return [...byId.values()]
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/** 任务是否已在队列、执行中或已完成,下次 poll 应跳过。 */
|
|
259
|
+
function isMatterTracked(state, matterId) {
|
|
260
|
+
const id = Number(matterId)
|
|
261
|
+
if (state.processedMatterIds.includes(id)) {
|
|
262
|
+
return true
|
|
263
|
+
}
|
|
264
|
+
if (state.notifiedMatterIds.includes(id)) {
|
|
265
|
+
return true
|
|
266
|
+
}
|
|
267
|
+
if (state.activeMatterId === id) {
|
|
268
|
+
return true
|
|
269
|
+
}
|
|
270
|
+
return (state.pendingQueue ?? []).some(q => Number(q.matterId) === id)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* 根据 sub_id 从 poller state 取该项目「处理中」状态 ID。
|
|
275
|
+
*/
|
|
276
|
+
export function resolveInProgressStatusId(state, subId) {
|
|
277
|
+
const projects = state.discovered?.projects ?? []
|
|
278
|
+
const project = projects.find(p => p.sub_id === Number(subId))
|
|
279
|
+
return project?.in_progress_status_id ?? null
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* 根据 sub_id 从 poller state 取该项目完成后状态 ID。
|
|
284
|
+
*/
|
|
285
|
+
export function resolveSelfTestStatusId(state, subId) {
|
|
286
|
+
const projects = state.discovered?.projects ?? []
|
|
287
|
+
const project = projects.find(p => p.sub_id === Number(subId))
|
|
288
|
+
return project?.self_test_status_id ?? null
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* dispatch 前将工作项状态改为「处理中」。
|
|
293
|
+
* 业务语义:GeeLib 侧先可见 agent 已接手,再推送 channel 通知启动执行。
|
|
294
|
+
*/
|
|
295
|
+
export async function markMatterInProgress(store, item) {
|
|
296
|
+
const matterId = Number(item.matterId)
|
|
297
|
+
const subId = Number(item.subId)
|
|
298
|
+
const state = store.loadPollerState()
|
|
299
|
+
const inProgressStatusId = resolveInProgressStatusId(state, subId)
|
|
300
|
+
if (inProgressStatusId === null) {
|
|
301
|
+
throw new Error(
|
|
302
|
+
`未找到 sub_id=${subId} 的「处理中」状态 ID,请确认 poll 已完成项目发现`,
|
|
303
|
+
)
|
|
304
|
+
}
|
|
305
|
+
const result = await cmdUpdateMatter({
|
|
306
|
+
subId,
|
|
307
|
+
matterId,
|
|
308
|
+
statusId: inProgressStatusId,
|
|
309
|
+
})
|
|
310
|
+
if (result?.errno !== 2000) {
|
|
311
|
+
throw new Error(
|
|
312
|
+
`matter_id=${matterId} 状态改为「处理中」失败: ${JSON.stringify(result)}`,
|
|
313
|
+
)
|
|
314
|
+
}
|
|
315
|
+
return { matterId, subId, inProgressStatusId, result }
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* dispatch 失败时回滚:释放 active 槽位,任务重新入队队首。
|
|
320
|
+
*/
|
|
321
|
+
export function rollbackDispatch(store, item) {
|
|
322
|
+
const state = store.loadPollerState()
|
|
323
|
+
const matterId = Number(item.matterId)
|
|
324
|
+
if (state.activeMatterId === matterId) {
|
|
325
|
+
state.activeMatterId = null
|
|
326
|
+
}
|
|
327
|
+
state.notifiedMatterIds = state.notifiedMatterIds.filter(x => x !== matterId)
|
|
328
|
+
const pendingQueue = Array.isArray(state.pendingQueue) ? state.pendingQueue : []
|
|
329
|
+
if (!pendingQueue.some(q => Number(q.matterId) === matterId)) {
|
|
330
|
+
pendingQueue.unshift(item)
|
|
331
|
+
}
|
|
332
|
+
state.pendingQueue = pendingQueue
|
|
333
|
+
store.savePollerState(state)
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* 一次拉取全部「未开始」任务并入队;已在执行/队列/已完成的任务会被忽略。
|
|
338
|
+
*/
|
|
339
|
+
export async function syncUnstartedTaskQueue(store) {
|
|
340
|
+
const state = store.loadPollerState()
|
|
341
|
+
const discovered = await discoverAllProjects(state)
|
|
342
|
+
state.discovered = discovered
|
|
343
|
+
|
|
344
|
+
const userInfo = await cmdGetUserInfo()
|
|
345
|
+
if (userInfo.errno !== 2000) {
|
|
346
|
+
throw new Error(`get-user-info 失败: ${JSON.stringify(userInfo)}`)
|
|
347
|
+
}
|
|
348
|
+
const assigneeId = userInfo.data?.id ?? userInfo.data?.user_id
|
|
349
|
+
if (assigneeId === undefined) {
|
|
350
|
+
throw new Error('无法从 get-user-info 解析当前用户 ID')
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const pendingQueue = Array.isArray(state.pendingQueue) ? [...state.pendingQueue] : []
|
|
354
|
+
const pendingIds = new Set(pendingQueue.map(q => Number(q.matterId)))
|
|
355
|
+
const newCandidates = []
|
|
356
|
+
|
|
357
|
+
for (const project of discovered.projects) {
|
|
358
|
+
const rows = await listUnstartedMatters(
|
|
359
|
+
project.sub_id,
|
|
360
|
+
project.type_id,
|
|
361
|
+
assigneeId,
|
|
362
|
+
project.poll_status_ids,
|
|
363
|
+
)
|
|
364
|
+
for (const row of rows) {
|
|
365
|
+
const mid = extractMatterId(row)
|
|
366
|
+
if (mid === null || isMatterTracked(state, mid) || pendingIds.has(mid)) {
|
|
367
|
+
continue
|
|
368
|
+
}
|
|
369
|
+
newCandidates.push({
|
|
370
|
+
row,
|
|
371
|
+
project,
|
|
372
|
+
})
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
newCandidates.sort((a, b) => {
|
|
377
|
+
const ta = a.row.create_time ?? a.row.updated_at ?? ''
|
|
378
|
+
const tb = b.row.create_time ?? b.row.updated_at ?? ''
|
|
379
|
+
return ta.localeCompare(tb)
|
|
380
|
+
})
|
|
381
|
+
|
|
382
|
+
for (const { row, project } of newCandidates) {
|
|
383
|
+
const mid = extractMatterId(row)
|
|
384
|
+
pendingQueue.push({
|
|
385
|
+
matterId: mid,
|
|
386
|
+
subId: project.sub_id,
|
|
387
|
+
typeId: project.type_id,
|
|
388
|
+
title: extractMatterTitle(row),
|
|
389
|
+
matter: row,
|
|
390
|
+
enqueuedAt: Date.now(),
|
|
391
|
+
})
|
|
392
|
+
pendingIds.add(mid)
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
state.pendingQueue = pendingQueue
|
|
396
|
+
state.lastPollAt = Date.now()
|
|
397
|
+
store.savePollerState(state)
|
|
398
|
+
|
|
399
|
+
return {
|
|
400
|
+
state,
|
|
401
|
+
discovered,
|
|
402
|
+
newlyEnqueued: newCandidates.length,
|
|
403
|
+
queueLength: pendingQueue.length,
|
|
404
|
+
activeMatterId: state.activeMatterId,
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* 当前无执行任务时,从队列头取一条准备 NOTIFY。
|
|
410
|
+
* 业务语义:同一时刻仅 dispatch 一条,保证按顺序串行执行。
|
|
411
|
+
*/
|
|
412
|
+
export function takeNextForDispatch(store) {
|
|
413
|
+
const state = store.loadPollerState()
|
|
414
|
+
if (state.activeMatterId !== null) {
|
|
415
|
+
return null
|
|
416
|
+
}
|
|
417
|
+
const pendingQueue = Array.isArray(state.pendingQueue) ? state.pendingQueue : []
|
|
418
|
+
if (pendingQueue.length === 0) {
|
|
419
|
+
return null
|
|
420
|
+
}
|
|
421
|
+
const item = pendingQueue.shift()
|
|
422
|
+
const matterId = Number(item.matterId)
|
|
423
|
+
state.pendingQueue = pendingQueue
|
|
424
|
+
state.activeMatterId = matterId
|
|
425
|
+
if (!state.notifiedMatterIds.includes(matterId)) {
|
|
426
|
+
state.notifiedMatterIds.push(matterId)
|
|
427
|
+
}
|
|
428
|
+
store.savePollerState(state)
|
|
429
|
+
return item
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/** 将 matter_id 记入 processedMatterIds,释放 active 槽位。 */
|
|
433
|
+
export function markMatterProcessed(store, matterId) {
|
|
434
|
+
const state = store.loadPollerState()
|
|
435
|
+
const id = Number(matterId)
|
|
436
|
+
if (!state.processedMatterIds.includes(id)) {
|
|
437
|
+
state.processedMatterIds.push(id)
|
|
438
|
+
}
|
|
439
|
+
state.notifiedMatterIds = state.notifiedMatterIds.filter(x => x !== id)
|
|
440
|
+
if (state.activeMatterId === id) {
|
|
441
|
+
state.activeMatterId = null
|
|
442
|
+
}
|
|
443
|
+
state.pendingQueue = (state.pendingQueue ?? []).filter(
|
|
444
|
+
q => Number(q.matterId) !== id,
|
|
445
|
+
)
|
|
446
|
+
store.savePollerState(state)
|
|
447
|
+
return state
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
export function extractMatterTitle(row) {
|
|
451
|
+
return (
|
|
452
|
+
row.title ??
|
|
453
|
+
row.matter_title ??
|
|
454
|
+
row.name ??
|
|
455
|
+
`matter-${extractMatterId(row) ?? '?'}`
|
|
456
|
+
)
|
|
457
|
+
}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import {
|
|
2
|
+
mkdirSync,
|
|
3
|
+
readFileSync,
|
|
4
|
+
writeFileSync,
|
|
5
|
+
appendFileSync,
|
|
6
|
+
existsSync,
|
|
7
|
+
} from 'node:fs'
|
|
8
|
+
import { dirname, join } from 'node:path'
|
|
9
|
+
|
|
10
|
+
const POLLER_STATE_FILE = 'geelib-poller-state.json'
|
|
11
|
+
const INBOX_FILE = 'inbox.jsonl'
|
|
12
|
+
const META_FILE = 'meta.json'
|
|
13
|
+
|
|
14
|
+
function ensureDir(path) {
|
|
15
|
+
mkdirSync(path, { recursive: true })
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function readJson(file, fallback) {
|
|
19
|
+
try {
|
|
20
|
+
return JSON.parse(readFileSync(file, 'utf8'))
|
|
21
|
+
} catch {
|
|
22
|
+
return fallback
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function writeJsonAtomic(file, data) {
|
|
27
|
+
ensureDir(dirname(file))
|
|
28
|
+
writeFileSync(file, `${JSON.stringify(data, null, 2)}\n`, { mode: 0o600 })
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function defaultMeta() {
|
|
32
|
+
return { cursor: 0, lastPollAt: 0 }
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function defaultDiscovered() {
|
|
36
|
+
return {
|
|
37
|
+
configured_sub_ids: [],
|
|
38
|
+
projects: [],
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function defaultPollerState() {
|
|
43
|
+
return {
|
|
44
|
+
lastPollAt: 0,
|
|
45
|
+
processedMatterIds: [],
|
|
46
|
+
notifiedMatterIds: [],
|
|
47
|
+
activeMatterId: null,
|
|
48
|
+
pendingQueue: [],
|
|
49
|
+
discovered: defaultDiscovered(),
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** 将旧版单项目 discovered 结构迁移为 projects 数组。 */
|
|
54
|
+
function normalizeDiscovered(raw) {
|
|
55
|
+
if (!raw || typeof raw !== 'object') {
|
|
56
|
+
return defaultDiscovered()
|
|
57
|
+
}
|
|
58
|
+
if (Array.isArray(raw.projects) && raw.projects.length > 0) {
|
|
59
|
+
return {
|
|
60
|
+
configured_sub_ids: Array.isArray(raw.configured_sub_ids)
|
|
61
|
+
? raw.configured_sub_ids.map(Number)
|
|
62
|
+
: [],
|
|
63
|
+
projects: raw.projects.map(p => ({
|
|
64
|
+
sub_id: p.sub_id ?? null,
|
|
65
|
+
type_id: p.type_id ?? null,
|
|
66
|
+
poll_status_ids: Array.isArray(p.poll_status_ids)
|
|
67
|
+
? p.poll_status_ids.map(Number)
|
|
68
|
+
: [],
|
|
69
|
+
in_progress_status_id: p.in_progress_status_id ?? null,
|
|
70
|
+
self_test_status_id: p.self_test_status_id ?? null,
|
|
71
|
+
})),
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (raw.sub_id !== null && raw.sub_id !== undefined) {
|
|
75
|
+
return {
|
|
76
|
+
configured_sub_ids: [],
|
|
77
|
+
projects: [
|
|
78
|
+
{
|
|
79
|
+
sub_id: raw.sub_id,
|
|
80
|
+
type_id: raw.type_id ?? null,
|
|
81
|
+
poll_status_ids: Array.isArray(raw.poll_status_ids)
|
|
82
|
+
? raw.poll_status_ids.map(Number)
|
|
83
|
+
: Array.isArray(raw.pending_status_ids)
|
|
84
|
+
? raw.pending_status_ids.map(Number)
|
|
85
|
+
: [],
|
|
86
|
+
in_progress_status_id: raw.in_progress_status_id ?? null,
|
|
87
|
+
self_test_status_id:
|
|
88
|
+
raw.self_test_status_id ?? raw.done_status_id ?? null,
|
|
89
|
+
},
|
|
90
|
+
],
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return defaultDiscovered()
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Connector 侧状态:轮询去重 + 入站 inbox */
|
|
97
|
+
export class GeelibStateStore {
|
|
98
|
+
constructor(stateDir) {
|
|
99
|
+
this.stateDir = stateDir
|
|
100
|
+
this.pollerFile = join(stateDir, POLLER_STATE_FILE)
|
|
101
|
+
this.inboxFile = join(stateDir, INBOX_FILE)
|
|
102
|
+
this.metaFile = join(stateDir, META_FILE)
|
|
103
|
+
ensureDir(stateDir)
|
|
104
|
+
if (!existsSync(this.pollerFile)) {
|
|
105
|
+
this.savePollerState(defaultPollerState())
|
|
106
|
+
}
|
|
107
|
+
if (!existsSync(this.metaFile)) {
|
|
108
|
+
this.saveMeta(defaultMeta())
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
loadPollerState() {
|
|
113
|
+
const parsed = readJson(this.pollerFile, defaultPollerState())
|
|
114
|
+
const activeRaw = parsed.activeMatterId
|
|
115
|
+
return {
|
|
116
|
+
lastPollAt: parsed.lastPollAt ?? 0,
|
|
117
|
+
processedMatterIds: Array.isArray(parsed.processedMatterIds)
|
|
118
|
+
? parsed.processedMatterIds.map(Number)
|
|
119
|
+
: [],
|
|
120
|
+
notifiedMatterIds: Array.isArray(parsed.notifiedMatterIds)
|
|
121
|
+
? parsed.notifiedMatterIds.map(Number)
|
|
122
|
+
: [],
|
|
123
|
+
activeMatterId:
|
|
124
|
+
activeRaw === null || activeRaw === undefined
|
|
125
|
+
? null
|
|
126
|
+
: Number(activeRaw),
|
|
127
|
+
pendingQueue: Array.isArray(parsed.pendingQueue)
|
|
128
|
+
? parsed.pendingQueue.map(q => ({
|
|
129
|
+
matterId: Number(q.matterId),
|
|
130
|
+
subId: Number(q.subId),
|
|
131
|
+
typeId: Number(q.typeId),
|
|
132
|
+
title: String(q.title ?? ''),
|
|
133
|
+
matter: q.matter ?? null,
|
|
134
|
+
enqueuedAt: Number(q.enqueuedAt ?? 0),
|
|
135
|
+
}))
|
|
136
|
+
: [],
|
|
137
|
+
discovered: normalizeDiscovered(parsed.discovered),
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
savePollerState(state) {
|
|
142
|
+
writeJsonAtomic(this.pollerFile, state)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
loadMeta() {
|
|
146
|
+
return { ...defaultMeta(), ...readJson(this.metaFile, defaultMeta()) }
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
saveMeta(data) {
|
|
150
|
+
writeJsonAtomic(this.metaFile, { ...defaultMeta(), ...(data || {}) })
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** 写入入站任务 inbox,返回 cursor */
|
|
154
|
+
appendInbox(entry) {
|
|
155
|
+
ensureDir(this.stateDir)
|
|
156
|
+
const meta = this.loadMeta()
|
|
157
|
+
meta.lastPollAt = Date.now()
|
|
158
|
+
meta.cursor = (meta.cursor || 0) + 1
|
|
159
|
+
this.saveMeta(meta)
|
|
160
|
+
const row = { ...entry, cursor: meta.cursor }
|
|
161
|
+
appendFileSync(this.inboxFile, `${JSON.stringify(row)}\n`, {
|
|
162
|
+
encoding: 'utf8',
|
|
163
|
+
mode: 0o600,
|
|
164
|
+
flag: 'a',
|
|
165
|
+
})
|
|
166
|
+
return meta.cursor
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
listInbox(limit = 20, unreadOnly = false, afterCursor = 0) {
|
|
170
|
+
let rows = []
|
|
171
|
+
try {
|
|
172
|
+
rows = readFileSync(this.inboxFile, 'utf8')
|
|
173
|
+
.trim()
|
|
174
|
+
.split(/\n+/)
|
|
175
|
+
.filter(Boolean)
|
|
176
|
+
.map(line => JSON.parse(line))
|
|
177
|
+
} catch {
|
|
178
|
+
return []
|
|
179
|
+
}
|
|
180
|
+
if (afterCursor) {
|
|
181
|
+
rows = rows.filter(x => Number(x.cursor || 0) > Number(afterCursor))
|
|
182
|
+
}
|
|
183
|
+
if (unreadOnly) {
|
|
184
|
+
rows = rows.filter(x => !x.readAt)
|
|
185
|
+
}
|
|
186
|
+
return rows.slice(-limit)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
markInboxRead(uptoCursor = Infinity) {
|
|
190
|
+
let rows = []
|
|
191
|
+
try {
|
|
192
|
+
rows = readFileSync(this.inboxFile, 'utf8')
|
|
193
|
+
.trim()
|
|
194
|
+
.split(/\n+/)
|
|
195
|
+
.filter(Boolean)
|
|
196
|
+
.map(line => JSON.parse(line))
|
|
197
|
+
} catch {
|
|
198
|
+
return 0
|
|
199
|
+
}
|
|
200
|
+
let changed = 0
|
|
201
|
+
const now = Date.now()
|
|
202
|
+
rows = rows.map(row => {
|
|
203
|
+
if (!row.readAt && Number(row.cursor || 0) <= uptoCursor) {
|
|
204
|
+
changed += 1
|
|
205
|
+
return { ...row, readAt: now }
|
|
206
|
+
}
|
|
207
|
+
return row
|
|
208
|
+
})
|
|
209
|
+
writeFileSync(
|
|
210
|
+
this.inboxFile,
|
|
211
|
+
rows.map(x => JSON.stringify(x)).join('\n') + (rows.length ? '\n' : ''),
|
|
212
|
+
'utf8',
|
|
213
|
+
)
|
|
214
|
+
return changed
|
|
215
|
+
}
|
|
216
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@auto-ai/agent",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.122",
|
|
4
4
|
"description": "Auto AI Agent 网关 CLI(WebSocket,独立二进制,无需 Bun)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"maintainers": [
|
|
@@ -32,11 +32,11 @@
|
|
|
32
32
|
"zod": "^4.3.6"
|
|
33
33
|
},
|
|
34
34
|
"optionalDependencies": {
|
|
35
|
-
"@auto-ai/agent-linux-x64": "2.1.
|
|
36
|
-
"@auto-ai/agent-linux-arm64": "2.1.
|
|
37
|
-
"@auto-ai/agent-darwin-x64": "2.1.
|
|
38
|
-
"@auto-ai/agent-darwin-arm64": "2.1.
|
|
39
|
-
"@auto-ai/agent-win-x64": "2.1.
|
|
35
|
+
"@auto-ai/agent-linux-x64": "2.1.122",
|
|
36
|
+
"@auto-ai/agent-linux-arm64": "2.1.122",
|
|
37
|
+
"@auto-ai/agent-darwin-x64": "2.1.122",
|
|
38
|
+
"@auto-ai/agent-darwin-arm64": "2.1.122",
|
|
39
|
+
"@auto-ai/agent-win-x64": "2.1.122"
|
|
40
40
|
},
|
|
41
41
|
"scripts": {
|
|
42
42
|
"prepare": "node ../../scripts/sync-launcher-env.js",
|
|
@@ -46,6 +46,7 @@ __export(exports_src, {
|
|
|
46
46
|
ensureConfigured: () => ensureConfigured,
|
|
47
47
|
default: () => src_default,
|
|
48
48
|
configure: () => configure,
|
|
49
|
+
buildSessionAppend: () => buildCodeToolSessionAppend,
|
|
49
50
|
buildCodeToolSessionAppend: () => buildCodeToolSessionAppend,
|
|
50
51
|
CodeTool: () => CodeTool
|
|
51
52
|
});
|