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.
@@ -1,5 +1,5 @@
1
- // 实时任务页:等待点(按现状总结分散)总览 + 选中某个现状回填 + 该现状任务表。
2
- // 本质是「agent 提交结果 → 阻塞 → 用户在 web 针对现状回填 → agent 接收」。
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
- // 实时刷新:等待点状态/倒计时、任务是否被取走,每 1.5s 轮询
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 slots = overview ? overview.slots : [];
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 (!slots.length) {
78
+ if (!topics.length) {
56
79
  setActiveKey(null);
57
80
  return;
58
81
  }
59
- if (activeKey && slots.some((s) => s.key === activeKey)) return;
60
- const w = slots.find((s) => s.waiting) || slots[0];
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
- }, [slots, activeKey]);
85
+ }, [topics, activeKey]);
63
86
 
64
- const active = slots.find((s) => s.key === activeKey) || null;
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: { summary: active.summary, text: body },
104
+ body: { cwd: active.cwd, text: body },
82
105
  });
83
106
  setText('');
84
- toast(r.delivered ? '已返回给等待中的 agent' : '已排队,等待 agent 下一次 wait');
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: { summary: active.summary, id },
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 "&lt;当前现状总结&gt;" --json </code>
109
- 作为本轮<strong>最后一个动作</strong>调用——现状总结即等待点 key,命令阻塞并
110
- 自动打开浏览器弹窗;用户针对该现状回填后解除阻塞、返回新任务;120 秒未回填
111
- 则返回 <code>status:"timeout"</code>,agent 收尾结束本轮。
131
+ <code> nx-kv sessiontask wait --cwd "&lt;工作目录&gt;" --summary "&lt;当前现状&gt;" --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>等待点总览</h3>
117
- <span className="muted">{overview ? `${slots.length} 个 · 等待中 ${overview.waiting}` : ''}</span>
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
- {slots.length ? (
122
- slots.map((s) => {
123
- const isActive = s.key === activeKey;
164
+ {topics.length ? (
165
+ topics.map((t) => {
166
+ const isActive = t.key === activeKey;
124
167
  return (
125
168
  <button
126
- key={s.key}
127
- onClick={() => setActiveKey(s.key)}
169
+ key={t.key}
170
+ onClick={() => setActiveKey(t.key)}
128
171
  className="card"
129
172
  style={{
130
173
  textAlign: 'left',
131
- width: 250,
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, whiteSpace: 'pre-wrap', marginBottom: 6 }}>{s.summary}</div>
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
- {s.waiting ? (
145
- <span className="tag strong">{countdown(s.waitingUntil)}</span>
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
- 待领取 {s.pending}/{s.total}
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
- (暂无等待点;agent 调用 sessiontask wait 后会出现在这里)
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: 380, whiteSpace: 'pre-wrap' }} title={active.summary}>
167
- {active.summary}
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="针对该现状回填下一条任务(Enter 提交,Shift+Enter 换行)"
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>该现状的任务表</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-DW8t26MP.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};
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};