nx-kv 0.3.0 → 0.3.2
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/CHANGELOG.md +27 -0
- package/package.json +1 -1
- package/src/modules/sessiontask/index.js +53 -34
- package/src/modules/sessiontask/popup.js +36 -12
- package/src/modules/sessiontask/service.js +259 -137
- package/src/modules/sessiontask/view.jsx +89 -34
- package/src/web/public/assets/{CliHints-CDKMmZbP.js → CliHints-uQDPykkl.js} +1 -1
- package/src/web/public/assets/{index-DW8t26MP.js → index-C3LDUTKW.js} +2 -2
- package/src/web/public/assets/{view-CqGxh_0X.js → view-1sDSMusO.js} +1 -1
- package/src/web/public/assets/view-BgQvKmWZ.js +1 -0
- package/src/web/public/assets/{view-CdtYjY9W.js → view-CZkcHw_6.js} +1 -1
- package/src/web/public/assets/{view-C_qjdm2x.js → view-UqnZYH9n.js} +1 -1
- package/src/web/public/index.html +1 -1
- package/src/web/public/assets/view-CLDx6X3W.js +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
//
|
|
2
|
-
// 本质是「agent 提交结果 → 阻塞 → 用户在 web
|
|
1
|
+
// 实时任务页:主题(按工作目录划分)总览 + 选中某个主题回填 + 该主题的任务队列。
|
|
2
|
+
// 本质是「agent 提交结果 → 阻塞 → 用户在 web 针对主题回填 → agent 接收」。
|
|
3
3
|
import { useCallback, useEffect, useState } from 'react';
|
|
4
4
|
import { api } from '../../web/frontend/api/client.js';
|
|
5
5
|
import { useToast, useGuard } from '../../web/frontend/components/ui.jsx';
|
|
@@ -29,6 +29,8 @@ export default function SessionTaskView() {
|
|
|
29
29
|
const [activeKey, setActiveKey] = useState(null);
|
|
30
30
|
const [text, setText] = useState('');
|
|
31
31
|
const [saving, setSaving] = useState(false);
|
|
32
|
+
const [timeoutSec, setTimeoutSec] = useState(180);
|
|
33
|
+
const [cfgSaving, setCfgSaving] = useState(false);
|
|
32
34
|
|
|
33
35
|
const load = useCallback(async () => {
|
|
34
36
|
try {
|
|
@@ -42,26 +44,47 @@ export default function SessionTaskView() {
|
|
|
42
44
|
load();
|
|
43
45
|
}, [load]);
|
|
44
46
|
|
|
45
|
-
//
|
|
47
|
+
// 实时刷新:主题状态/倒计时、任务是否被取走,每 1.5s 轮询
|
|
46
48
|
useEffect(() => {
|
|
47
49
|
const t = setInterval(load, 1500);
|
|
48
50
|
return () => clearInterval(t);
|
|
49
51
|
}, [load]);
|
|
50
52
|
|
|
51
|
-
const
|
|
53
|
+
const topics = overview ? overview.topics : [];
|
|
54
|
+
|
|
55
|
+
// 同步页面上的超时配置(来源:后端 settings)
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
if (overview && overview.settings) setTimeoutSec(overview.settings.timeoutSec);
|
|
58
|
+
}, [overview]);
|
|
59
|
+
|
|
60
|
+
const saveTimeout = (sec) =>
|
|
61
|
+
guard(async () => {
|
|
62
|
+
setTimeoutSec(Number(sec));
|
|
63
|
+
setCfgSaving(true);
|
|
64
|
+
try {
|
|
65
|
+
const s = await api('/api/sessiontasks/setting', {
|
|
66
|
+
method: 'PATCH',
|
|
67
|
+
body: { timeout: Number(sec) },
|
|
68
|
+
});
|
|
69
|
+
setTimeoutSec(s.timeoutSec);
|
|
70
|
+
toast(`等待超时已配置为 ${s.timeoutSec} 秒`);
|
|
71
|
+
} finally {
|
|
72
|
+
setCfgSaving(false);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
52
75
|
|
|
53
76
|
// 默认选中:优先等待中的,否则第一个;用户已选且仍存在则保留
|
|
54
77
|
useEffect(() => {
|
|
55
|
-
if (!
|
|
78
|
+
if (!topics.length) {
|
|
56
79
|
setActiveKey(null);
|
|
57
80
|
return;
|
|
58
81
|
}
|
|
59
|
-
if (activeKey &&
|
|
60
|
-
const w =
|
|
82
|
+
if (activeKey && topics.some((t) => t.key === activeKey)) return;
|
|
83
|
+
const w = topics.find((t) => t.waiting) || topics[0];
|
|
61
84
|
setActiveKey(w.key);
|
|
62
|
-
}, [
|
|
85
|
+
}, [topics, activeKey]);
|
|
63
86
|
|
|
64
|
-
const active =
|
|
87
|
+
const active = topics.find((t) => t.key === activeKey) || null;
|
|
65
88
|
|
|
66
89
|
const submit = () =>
|
|
67
90
|
guard(async () => {
|
|
@@ -71,17 +94,17 @@ export default function SessionTaskView() {
|
|
|
71
94
|
return;
|
|
72
95
|
}
|
|
73
96
|
if (!active) {
|
|
74
|
-
toast('
|
|
97
|
+
toast('请先选择一个主题(工作目录)');
|
|
75
98
|
return;
|
|
76
99
|
}
|
|
77
100
|
setSaving(true);
|
|
78
101
|
try {
|
|
79
102
|
const r = await api('/api/sessiontasks', {
|
|
80
103
|
method: 'POST',
|
|
81
|
-
body: {
|
|
104
|
+
body: { cwd: active.cwd, text: body },
|
|
82
105
|
});
|
|
83
106
|
setText('');
|
|
84
|
-
toast(r.delivered ? '已返回给等待中的 agent' : '
|
|
107
|
+
toast(r.delivered ? '已返回给等待中的 agent' : '已排入该主题队列,等待 agent 下一次 wait');
|
|
85
108
|
await load();
|
|
86
109
|
} finally {
|
|
87
110
|
setSaving(false);
|
|
@@ -92,7 +115,7 @@ export default function SessionTaskView() {
|
|
|
92
115
|
guard(async () => {
|
|
93
116
|
await api('/api/sessiontasks/item', {
|
|
94
117
|
method: 'DELETE',
|
|
95
|
-
body: {
|
|
118
|
+
body: { cwd: active.cwd, id },
|
|
96
119
|
});
|
|
97
120
|
toast('已删除');
|
|
98
121
|
await load();
|
|
@@ -105,30 +128,50 @@ export default function SessionTaskView() {
|
|
|
105
128
|
<div className="card" style={{ padding: '10px 14px' }}>
|
|
106
129
|
<div className="muted" style={{ fontSize: 12 }}>
|
|
107
130
|
agent 用法:完成当前任务后,把
|
|
108
|
-
<code> nx-kv sessiontask wait --summary "<
|
|
109
|
-
作为本轮<strong>最后一个动作</strong
|
|
110
|
-
|
|
111
|
-
|
|
131
|
+
<code> nx-kv sessiontask wait --cwd "<工作目录>" --summary "<当前现状>" --json </code>
|
|
132
|
+
作为本轮<strong>最后一个动作</strong>调用——工作目录即<strong>主题</strong>、主题拥有任务队列,
|
|
133
|
+
同目录共用队列、不同目录互不串,任务可在队列里堆积;命令阻塞并自动打开浏览器弹窗,
|
|
134
|
+
用户回填后解除阻塞、返回新任务;超时未回填则返回 <code>status:"timeout"</code>,agent 收尾结束本轮。
|
|
112
135
|
</div>
|
|
113
136
|
</div>
|
|
114
137
|
|
|
138
|
+
<div className="card" style={{ padding: '10px 14px', display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap' }}>
|
|
139
|
+
<span style={{ fontWeight: 700 }}>等待超时</span>
|
|
140
|
+
<select
|
|
141
|
+
value={timeoutSec}
|
|
142
|
+
disabled={cfgSaving}
|
|
143
|
+
onChange={(e) => saveTimeout(e.target.value)}
|
|
144
|
+
style={{ font: 'inherit', padding: '5px 8px' }}
|
|
145
|
+
>
|
|
146
|
+
<option value={60}>1 分钟</option>
|
|
147
|
+
<option value={120}>2 分钟</option>
|
|
148
|
+
<option value={180}>3 分钟(推荐)</option>
|
|
149
|
+
<option value={240}>4 分钟</option>
|
|
150
|
+
<option value={300}>5 分钟</option>
|
|
151
|
+
<option value={600}>10 分钟(最大)</option>
|
|
152
|
+
</select>
|
|
153
|
+
<span className="muted" style={{ fontSize: 12 }}>
|
|
154
|
+
agent wait 未带 --timeout 时使用此值;推荐 3 分钟、最大 10 分钟。
|
|
155
|
+
</span>
|
|
156
|
+
</div>
|
|
157
|
+
|
|
115
158
|
<div className="colhead">
|
|
116
|
-
<h3
|
|
117
|
-
<span className="muted">{overview ? `${
|
|
159
|
+
<h3>主题总览(按工作目录)</h3>
|
|
160
|
+
<span className="muted">{overview ? `${topics.length} 个 · 等待中 ${overview.waiting}` : ''}</span>
|
|
118
161
|
</div>
|
|
119
162
|
|
|
120
163
|
<div style={{ display: 'flex', gap: 10, flexWrap: 'wrap', marginBottom: 14 }}>
|
|
121
|
-
{
|
|
122
|
-
|
|
123
|
-
const isActive =
|
|
164
|
+
{topics.length ? (
|
|
165
|
+
topics.map((t) => {
|
|
166
|
+
const isActive = t.key === activeKey;
|
|
124
167
|
return (
|
|
125
168
|
<button
|
|
126
|
-
key={
|
|
127
|
-
onClick={() => setActiveKey(
|
|
169
|
+
key={t.key}
|
|
170
|
+
onClick={() => setActiveKey(t.key)}
|
|
128
171
|
className="card"
|
|
129
172
|
style={{
|
|
130
173
|
textAlign: 'left',
|
|
131
|
-
width:
|
|
174
|
+
width: 270,
|
|
132
175
|
padding: '10px 12px',
|
|
133
176
|
cursor: 'pointer',
|
|
134
177
|
font: 'inherit',
|
|
@@ -139,15 +182,27 @@ export default function SessionTaskView() {
|
|
|
139
182
|
: '0 1px 2px rgba(20,22,26,.06), 0 6px 16px rgba(20,22,26,.05)',
|
|
140
183
|
}}
|
|
141
184
|
>
|
|
142
|
-
<div style={{ fontWeight: 700,
|
|
185
|
+
<div style={{ fontWeight: 700, marginBottom: 2 }}>{t.name || '(未命名)'}</div>
|
|
186
|
+
<div
|
|
187
|
+
className="mono muted"
|
|
188
|
+
title={t.cwd}
|
|
189
|
+
style={{ fontSize: 11, wordBreak: 'break-all', marginBottom: 6, minHeight: 15 }}
|
|
190
|
+
>
|
|
191
|
+
{t.cwd || '(旧数据,无目录)'}
|
|
192
|
+
</div>
|
|
193
|
+
{t.summary && (
|
|
194
|
+
<div className="muted" style={{ fontSize: 11, whiteSpace: 'pre-wrap', marginBottom: 6 }}>
|
|
195
|
+
现状:{t.summary}
|
|
196
|
+
</div>
|
|
197
|
+
)}
|
|
143
198
|
<div style={{ display: 'flex', gap: 6, alignItems: 'center' }}>
|
|
144
|
-
{
|
|
145
|
-
<span className="tag strong">{countdown(
|
|
199
|
+
{t.waiting ? (
|
|
200
|
+
<span className="tag strong">{countdown(t.waitingUntil)}</span>
|
|
146
201
|
) : (
|
|
147
202
|
<span className="tag">已结束</span>
|
|
148
203
|
)}
|
|
149
204
|
<span className="muted" style={{ fontSize: 11 }}>
|
|
150
|
-
待领取 {
|
|
205
|
+
待领取 {t.pending}/{t.total}
|
|
151
206
|
</span>
|
|
152
207
|
</div>
|
|
153
208
|
</button>
|
|
@@ -155,7 +210,7 @@ export default function SessionTaskView() {
|
|
|
155
210
|
})
|
|
156
211
|
) : (
|
|
157
212
|
<div className="card muted" style={{ padding: '20px 16px', width: '100%', textAlign: 'center' }}>
|
|
158
|
-
|
|
213
|
+
(暂无主题;agent 调用 sessiontask wait 后会出现在这里)
|
|
159
214
|
</div>
|
|
160
215
|
)}
|
|
161
216
|
</div>
|
|
@@ -163,8 +218,8 @@ export default function SessionTaskView() {
|
|
|
163
218
|
{active && (
|
|
164
219
|
<>
|
|
165
220
|
<div className="toolbar">
|
|
166
|
-
<span style={{ fontWeight: 700, maxWidth:
|
|
167
|
-
{active.
|
|
221
|
+
<span style={{ fontWeight: 700, maxWidth: 240 }} title={active.cwd}>
|
|
222
|
+
{active.name}
|
|
168
223
|
</span>
|
|
169
224
|
<span className="sep"></span>
|
|
170
225
|
<textarea
|
|
@@ -178,7 +233,7 @@ export default function SessionTaskView() {
|
|
|
178
233
|
submit();
|
|
179
234
|
}
|
|
180
235
|
}}
|
|
181
|
-
placeholder=
|
|
236
|
+
placeholder={`向主题「${active.name}」的队列回填下一条任务(Enter 提交,Shift+Enter 换行)`}
|
|
182
237
|
/>
|
|
183
238
|
<button className="btn" onClick={submit} disabled={saving}>
|
|
184
239
|
{saving ? '提交中…' : '回填新任务'}
|
|
@@ -186,7 +241,7 @@ export default function SessionTaskView() {
|
|
|
186
241
|
</div>
|
|
187
242
|
|
|
188
243
|
<div className="colhead">
|
|
189
|
-
<h3
|
|
244
|
+
<h3>该主题的任务队列</h3>
|
|
190
245
|
<span className="muted">待领取 {active.pending} · 共 {active.total} 条</span>
|
|
191
246
|
</div>
|
|
192
247
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{o as e,s as t,t as n}from"./index-
|
|
1
|
+
import{o as e,s as t,t as n}from"./index-C3LDUTKW.js";var r=t();function i({module:t}){let{boot:i}=e(),a=(i?.commands||[]).filter(e=>e.module===t);return a.length?(0,r.jsxs)(`div`,{className:`cli-hint`,children:[(0,r.jsx)(`span`,{className:`cli-hint-label`,children:`CLI 等价(同构命令,加 --json 得机器可读输出):`}),a.map(e=>(0,r.jsx)(n,{className:`cli-cmd`,text:e.command,title:`点击复制:${e.usage}`,children:e.command},e.id))]}):null}export{i as t};
|