dsh-aris-panel 0.1.5 → 0.1.6
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/dsh/client.js +40 -11
- package/dsh/workbench.mjs +115 -27
- package/package.json +1 -1
package/dsh/client.js
CHANGED
|
@@ -441,7 +441,7 @@ function WbTrigger(props) {
|
|
|
441
441
|
const doLaunch = function () {
|
|
442
442
|
if (!skill) { setStatus('请先选择技能'); return }
|
|
443
443
|
setBusy(true)
|
|
444
|
-
connection.rpc.call(WB_CHANNEL, 'launch', { sessionId: sessionId, skill: skill, args: args, autoProceed: autoProceed, effort: effort, assurance: assurance, difficulty: difficulty, venue: venue, gpu: gpu, reviewer: reviewer, humanCheckpoint: humanCheckpoint, extra: extra })
|
|
444
|
+
connection.rpc.call(WB_CHANNEL, 'launch', { sessionId: sessionId, skill: skill, args: args, autoProceed: autoProceed, effort: effort, assurance: assurance, difficulty: difficulty, venue: venue, gpu: gpu, reviewer: reviewer, humanCheckpoint: humanCheckpoint, targetSessionId: props.targetSessionId && props.targetSessionId !== '__new__' ? props.targetSessionId : undefined, newSession: props.targetSessionId === '__new__', extra: extra })
|
|
445
445
|
.then(function (res) {
|
|
446
446
|
if (res.ok) {
|
|
447
447
|
setStatus(res.value.injected ? '已注入会话(' + res.value.method + '),执行已唤醒,请到会话窗口查看进度' : '未能注入,请复制提示词手动发送:' + res.value.prompt)
|
|
@@ -534,6 +534,15 @@ function WbTrigger(props) {
|
|
|
534
534
|
status ? h('div', { className: 'wb-status' }, status) : null)
|
|
535
535
|
}
|
|
536
536
|
function wbNote(text) { return h('p', { className: 'wb-note' }, text) }
|
|
537
|
+
function wbWhen(ms) {
|
|
538
|
+
const seconds = Math.max(0, Math.round((Date.now() - ms) / 1000))
|
|
539
|
+
if (seconds < 60) return seconds + 's前'
|
|
540
|
+
const minutes = Math.round(seconds / 60)
|
|
541
|
+
if (minutes < 60) return minutes + '分钟前'
|
|
542
|
+
const hours = Math.round(minutes / 60)
|
|
543
|
+
if (hours < 24) return hours + '小时前'
|
|
544
|
+
return Math.round(hours / 24) + '天前'
|
|
545
|
+
}
|
|
537
546
|
function wbKey(label, value) {
|
|
538
547
|
return h('div', { className: 'wb-row' }, h('span', { className: 'wb-key' }, label), h('span', { className: 'wb-val' }, value))
|
|
539
548
|
}
|
|
@@ -683,19 +692,23 @@ function ArisWorkbenchView(props) {
|
|
|
683
692
|
const [args, setArgs] = React.useState('')
|
|
684
693
|
const [permPreset, setPermPreset] = React.useState('read-only')
|
|
685
694
|
const [permStatus, setPermStatus] = React.useState(undefined)
|
|
695
|
+
const [targetSessionId, setTargetSessionId] = React.useState(undefined)
|
|
696
|
+
const [workspaceId, setWorkspaceId] = React.useState(undefined)
|
|
686
697
|
|
|
687
698
|
React.useEffect(function () {
|
|
688
699
|
let live = true
|
|
689
|
-
connection.rpc.call(WB_CHANNEL, '
|
|
700
|
+
connection.rpc.call(WB_CHANNEL, 'workspaces', {})
|
|
690
701
|
.then(function (res) {
|
|
691
702
|
if (!live) return
|
|
692
|
-
const
|
|
693
|
-
if (Array.isArray(
|
|
694
|
-
setWorkspaces(
|
|
695
|
-
|
|
703
|
+
const list = res.ok && res.value ? res.value.workspaces : undefined
|
|
704
|
+
if (Array.isArray(list) && list.length > 0) {
|
|
705
|
+
setWorkspaces(list)
|
|
706
|
+
const first = list[0]
|
|
707
|
+
setWorkspaceId(first.id)
|
|
708
|
+
if (sessionId === undefined && first.sessions && first.sessions.length > 0) setSessionId(first.sessions[0].id)
|
|
696
709
|
} else {
|
|
697
710
|
setWorkspaces([])
|
|
698
|
-
setError(res.error ? res.error.message : '
|
|
711
|
+
setError(res.error ? res.error.message : 'workspaces RPC failed')
|
|
699
712
|
}
|
|
700
713
|
})
|
|
701
714
|
.catch(function (cause) { if (live) { setError(String(cause)); setWorkspaces([]) } })
|
|
@@ -765,6 +778,8 @@ function ArisWorkbenchView(props) {
|
|
|
765
778
|
const ideas = data && data.ideas
|
|
766
779
|
const exps = data && data.experiments
|
|
767
780
|
const stage = ov ? inferStage(ov) : 1
|
|
781
|
+
const curWs = workspaces && workspaceId ? workspaces.find(function (w) { return w.id === workspaceId }) : (workspaces && workspaces.length > 0 ? workspaces[0] : undefined)
|
|
782
|
+
const wsSessions = curWs && Array.isArray(curWs.sessions) ? curWs.sessions.filter(function (s) { return s.title || s.msgCount > 0 }) : []
|
|
768
783
|
|
|
769
784
|
const children = []
|
|
770
785
|
children.push(h('div', { className: 'wb-header', key: 'head' },
|
|
@@ -775,17 +790,31 @@ function ArisWorkbenchView(props) {
|
|
|
775
790
|
if (workspaces && workspaces.length > 1) {
|
|
776
791
|
children.push(h('div', { className: 'wb-picker', key: 'picker' },
|
|
777
792
|
h('label', null, '工作区: '),
|
|
778
|
-
h('select', { value:
|
|
779
|
-
workspaces.
|
|
793
|
+
h('select', { value: workspaceId, onChange: function (e) {
|
|
794
|
+
const w = workspaces.find(function (x) { return x.id === e.target.value })
|
|
795
|
+
setWorkspaceId(e.target.value)
|
|
796
|
+
setTargetSessionId(undefined)
|
|
797
|
+
if (w && w.sessions && w.sessions.length > 0) setSessionId(w.sessions[0].id)
|
|
798
|
+
} },
|
|
799
|
+
workspaces.map(function (w) {
|
|
800
|
+
const base = w.path ? String(w.path).split(/[\\/]/).filter(Boolean).pop() : ''
|
|
801
|
+
return h('option', { key: w.id, value: w.id }, w.title + (base && base !== w.title ? ' (' + base + ')' : ''))
|
|
802
|
+
}))))
|
|
780
803
|
}
|
|
781
804
|
if (workspaces && workspaces.length === 0) {
|
|
782
|
-
children.push(wbNote('
|
|
805
|
+
children.push(wbNote('无工作区数据 — 请先在 DSH 中打开一个工作区,然后点「刷新」'))
|
|
783
806
|
}
|
|
784
807
|
if (error) children.push(h('div', { className: 'wb-error', key: 'err' }, error))
|
|
785
808
|
if (sessionId === undefined && workspaces === undefined) children.push(wbNote('正在连接工作区…'))
|
|
786
809
|
if (sessionId !== undefined) {
|
|
787
810
|
|
|
788
811
|
children.push(h('div', { className: 'wb-picker', key: 'pctl' },
|
|
812
|
+
h('label', null, '会话: '),
|
|
813
|
+
h('select', { className: 'wb-perm-select', value: targetSessionId || (wsSessions.length > 0 ? wsSessions[0].id : ''), onChange: function (e) { setTargetSessionId(e.target.value) } },
|
|
814
|
+
wsSessions.map(function (s) {
|
|
815
|
+
const label = (s.title || '未命名会话') + ' [' + s.id.slice(0, 8) + ']' + (s.updatedAt ? ' · ' + wbWhen(s.updatedAt) : '') + (s.msgCount ? ' (' + s.msgCount + '条)' : '')
|
|
816
|
+
return h('option', { key: s.id, value: s.id, title: s.id }, label)
|
|
817
|
+
})),
|
|
789
818
|
h('label', null, '权限: '),
|
|
790
819
|
h('select', { className: 'wb-perm-select', value: permPreset, onChange: function (e) {
|
|
791
820
|
const v = e.target.value
|
|
@@ -800,7 +829,7 @@ function ArisWorkbenchView(props) {
|
|
|
800
829
|
permStatus ? h('span', { className: 'wb-perm-status' }, permStatus) : null))
|
|
801
830
|
children.push(h(WbPanorama, { key: 'pano', onPick: function (name) { setSkill(name) } }))
|
|
802
831
|
children.push(h(WbProgress, { key: 'progress', overview: ov, stage: stage }))
|
|
803
|
-
children.push(h(WbTrigger, { key: 'trigger', connection: connection, sessionId: sessionId, skills: skills, skill: skill, args: args, skillError: skillError, onSkill: setSkill, onArgs: setArgs }))
|
|
832
|
+
children.push(h(WbTrigger, { key: 'trigger', connection: connection, sessionId: sessionId, skills: skills, skill: skill, args: args, targetSessionId: targetSessionId, skillError: skillError, onSkill: setSkill, onArgs: setArgs }))
|
|
804
833
|
}
|
|
805
834
|
if (sessionId !== undefined && data === undefined && !error) children.push(wbNote('加载中…'))
|
|
806
835
|
if (data) {
|
package/dsh/workbench.mjs
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import { readdir, readFile, stat } from 'node:fs/promises'
|
|
11
11
|
import { isAbsolute, join } from 'node:path'
|
|
12
|
+
import { homedir } from 'node:os'
|
|
12
13
|
|
|
13
14
|
export const WORKBENCH_RPC_CHANNEL = '/aris-wb'
|
|
14
15
|
|
|
@@ -178,17 +179,78 @@ async function readSkills() {
|
|
|
178
179
|
return skills
|
|
179
180
|
}
|
|
180
181
|
|
|
182
|
+
const PROJCACHE_PATH = join(process.env.DSH_HOME || join(homedir(), '.dsh'), 'storages', 'session_projcache.json')
|
|
183
|
+
|
|
184
|
+
/** Read the DSH session projection cache (lightweight title index). */
|
|
185
|
+
async function readProjCache() {
|
|
186
|
+
try {
|
|
187
|
+
const text = await readFile(PROJCACHE_PATH, 'utf8')
|
|
188
|
+
const parsed = JSON.parse(text)
|
|
189
|
+
return parsed && parsed.tables && parsed.tables.sessions ? parsed.tables.sessions : undefined
|
|
190
|
+
} catch {
|
|
191
|
+
return undefined
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
|
|
181
196
|
export function registerWorkbench(ctx) {
|
|
182
|
-
ctx.inject(['connection', 'sessions', 'agents'], (scoped) => {
|
|
197
|
+
ctx.inject(['connection', 'sessions', 'agents', 'workspaceRegistry', 'sessionPersistence'], (scoped) => {
|
|
183
198
|
scoped.effect(() => scoped.connection.rpc.handle(
|
|
184
199
|
WORKBENCH_RPC_CHANNEL,
|
|
185
200
|
async (endpoint, payload) => {
|
|
186
|
-
if (endpoint === '
|
|
201
|
+
if (endpoint === 'workspaces') {
|
|
187
202
|
try {
|
|
188
|
-
const
|
|
189
|
-
|
|
203
|
+
const proj = await readProjCache()
|
|
204
|
+
const sessionMetaOf = function (sid) {
|
|
205
|
+
let evs = undefined
|
|
206
|
+
try {
|
|
207
|
+
const s = scoped.sessions.get(sid)
|
|
208
|
+
if (s && Array.isArray(s.events)) evs = s.events
|
|
209
|
+
} catch (e) { /* ignore */ }
|
|
210
|
+
const out = { id: sid, title: undefined, updatedAt: undefined, msgCount: 0 }
|
|
211
|
+
if (Array.isArray(evs)) {
|
|
212
|
+
out.msgCount = evs.filter(function (ev) { return ev && ev.type === 'user/message' }).length
|
|
213
|
+
for (let i = evs.length - 1; i >= 0; i--) {
|
|
214
|
+
const ev = evs[i]
|
|
215
|
+
if (!ev) continue
|
|
216
|
+
if (out.updatedAt === undefined && typeof ev.time === 'number') out.updatedAt = ev.time
|
|
217
|
+
if (ev.type === 'session/title' && ev.data && typeof ev.data.title === 'string' && ev.data.title.trim() !== '' && out.title === undefined) out.title = ev.data.title
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
if (out.title === undefined && proj && proj[sid] && proj[sid].rows && proj[sid].rows.title) {
|
|
221
|
+
const v = proj[sid].rows.title.val
|
|
222
|
+
if (typeof v === 'string' && v.trim() !== '') out.title = v
|
|
223
|
+
}
|
|
224
|
+
if (out.title === undefined && Array.isArray(evs)) {
|
|
225
|
+
for (const ev of evs) {
|
|
226
|
+
if (ev && ev.type === 'user/message' && ev.data && ev.data.source && ev.data.source.kind === 'user') {
|
|
227
|
+
const blocks = ev.data.content
|
|
228
|
+
const text = Array.isArray(blocks) ? blocks.filter(function (b) { return b && b.type === 'text' }).map(function (b) { return b.text }).join(' ') : ''
|
|
229
|
+
const cleaned = String(text).replace(/\s+/g, ' ').trim()
|
|
230
|
+
if (cleaned !== '') {
|
|
231
|
+
const words = cleaned.split(' ').filter(Boolean).slice(0, 10).join(' ')
|
|
232
|
+
out.title = words.length > 32 ? words.slice(0, 32) : words
|
|
233
|
+
break
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return out
|
|
239
|
+
}
|
|
240
|
+
const reg = scoped.workspaceRegistry
|
|
241
|
+
let archived = []
|
|
242
|
+
try {
|
|
243
|
+
const a = reg.archivedSessionIds
|
|
244
|
+
archived = Array.isArray(a) ? a : (typeof a === 'function' ? a() : [])
|
|
245
|
+
} catch (e) { /* ignore */ }
|
|
246
|
+
const list = typeof reg.list === 'function' ? reg.list().map(function (w) {
|
|
247
|
+
const sids = (Array.isArray(w.sessionIds) ? w.sessionIds : []).filter(function (sid) { return archived.indexOf(sid) === -1 })
|
|
248
|
+
const sessions = sids.map(sessionMetaOf)
|
|
249
|
+
return { id: w.id, path: w.path, title: w.title, sessions: sessions }
|
|
250
|
+
}) : []
|
|
251
|
+
return { ok: true, value: { workspaces: list } }
|
|
190
252
|
} catch (error) {
|
|
191
|
-
return { ok: false, error: { code: 'internal', message: String(error && error.message || error), details: {} } }
|
|
253
|
+
return { ok: false, error: { code: 'internal', message: String(error && error.message || error), details: { issues: [] } } }
|
|
192
254
|
}
|
|
193
255
|
}
|
|
194
256
|
if (endpoint === 'skills') {
|
|
@@ -197,28 +259,28 @@ export function registerWorkbench(ctx) {
|
|
|
197
259
|
return { ok: true, value: { skills: skills } }
|
|
198
260
|
} catch (error) {
|
|
199
261
|
console.error('[aris-wb] skills failed:', error)
|
|
200
|
-
return { ok: false, error: { code: 'internal', message: String(error && error.message || error), details: {} } }
|
|
262
|
+
return { ok: false, error: { code: 'internal', message: String(error && error.message || error), details: { issues: [] } } }
|
|
201
263
|
}
|
|
202
264
|
}
|
|
203
265
|
const sessionId = typeof payload === 'object' && payload !== null ? payload.sessionId : undefined
|
|
204
266
|
if (typeof sessionId !== 'string') {
|
|
205
|
-
return { ok: false, error: { code: 'bad-request', message: 'sessionId is required', details: {} } }
|
|
267
|
+
return { ok: false, error: { code: 'bad-request', message: 'sessionId is required', details: { issues: [] } } }
|
|
206
268
|
}
|
|
207
269
|
if (endpoint === 'permission') {
|
|
208
270
|
const preset = typeof payload === 'object' && payload !== null ? payload.preset : undefined
|
|
209
271
|
const allowed = ['read-only', 'workspace-write', 'danger-full-access']
|
|
210
272
|
if (typeof preset !== 'string' || allowed.indexOf(preset) === -1) {
|
|
211
|
-
return { ok: false, error: { code: 'bad-request', message: 'preset must be one of ' + allowed.join(', '), details: {} } }
|
|
273
|
+
return { ok: false, error: { code: 'bad-request', message: 'preset must be one of ' + allowed.join(', '), details: { issues: [] } } }
|
|
212
274
|
}
|
|
213
275
|
try {
|
|
214
276
|
const session = scoped.sessions.get(sessionId)
|
|
215
277
|
if (!session || typeof session.append !== 'function') {
|
|
216
|
-
return { ok: false, error: { code: 'internal', message: 'session unavailable', details: {} } }
|
|
278
|
+
return { ok: false, error: { code: 'internal', message: 'session unavailable', details: { issues: [] } } }
|
|
217
279
|
}
|
|
218
280
|
session.append('permission/preset', { preset: preset })
|
|
219
281
|
return { ok: true, value: { preset: preset } }
|
|
220
282
|
} catch (error) {
|
|
221
|
-
return { ok: false, error: { code: 'internal', message: String(error && error.message || error), details: {} } }
|
|
283
|
+
return { ok: false, error: { code: 'internal', message: String(error && error.message || error), details: { issues: [] } } }
|
|
222
284
|
}
|
|
223
285
|
}
|
|
224
286
|
const workspace = scoped.sessions.get(sessionId)?.header?.cwd
|
|
@@ -264,7 +326,7 @@ export function registerWorkbench(ctx) {
|
|
|
264
326
|
const skill = typeof payload === 'object' && payload !== null ? payload.skill : undefined
|
|
265
327
|
const args = typeof payload === 'object' && payload !== null ? payload.args : undefined
|
|
266
328
|
if (typeof skill !== 'string' || skill === '') {
|
|
267
|
-
return { ok: false, error: { code: 'bad-request', message: 'skill is required', details: {} } }
|
|
329
|
+
return { ok: false, error: { code: 'bad-request', message: 'skill is required', details: { issues: [] } } }
|
|
268
330
|
}
|
|
269
331
|
const argText = typeof args === 'string' ? args.trim() : ''
|
|
270
332
|
const autoProceed = typeof payload === 'object' && payload !== null ? payload.autoProceed : undefined
|
|
@@ -298,27 +360,53 @@ export function registerWorkbench(ctx) {
|
|
|
298
360
|
}
|
|
299
361
|
let injected = false
|
|
300
362
|
let method = 'none'
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
}
|
|
308
|
-
} catch (e) { /* fall through to session.append */ }
|
|
309
|
-
if (!injected) {
|
|
363
|
+
let targetId = sessionId
|
|
364
|
+
const newSession = typeof payload === 'object' && payload !== null ? payload.newSession === true : false
|
|
365
|
+
const targetSessionId = typeof payload === 'object' && payload !== null ? payload.targetSessionId : undefined
|
|
366
|
+
const deliver = function (sid) {
|
|
367
|
+
let logged = false
|
|
368
|
+
let woken = false
|
|
310
369
|
try {
|
|
311
|
-
const session = scoped.sessions.get(
|
|
370
|
+
const session = scoped.sessions.get(sid)
|
|
312
371
|
if (session && typeof session.append === 'function') {
|
|
313
|
-
session.append('user/message', message)
|
|
314
|
-
|
|
315
|
-
|
|
372
|
+
session.append('user/message', message, { surfaceOp: 'append' })
|
|
373
|
+
logged = true
|
|
374
|
+
}
|
|
375
|
+
} catch (e) { /* ignore */ }
|
|
376
|
+
try {
|
|
377
|
+
const agent = scoped.agents && typeof scoped.agents.get === 'function' ? scoped.agents.get(sid) : undefined
|
|
378
|
+
if (agent && typeof agent.send === 'function') {
|
|
379
|
+
agent.send(message, 'next-turn', true)
|
|
380
|
+
woken = true
|
|
381
|
+
}
|
|
382
|
+
} catch (e) { /* ignore */ }
|
|
383
|
+
return logged || woken
|
|
384
|
+
}
|
|
385
|
+
if (newSession) {
|
|
386
|
+
try {
|
|
387
|
+
const newId = 'session-aris-' + Date.now() + '-' + Math.random().toString(36).slice(2, 6)
|
|
388
|
+
const handle = await scoped.agents.create({ sessionId: newId, meta: { cwd: workspace } })
|
|
389
|
+
if (handle) {
|
|
390
|
+
if (deliver(newId)) {
|
|
391
|
+
targetId = newId
|
|
392
|
+
injected = true
|
|
393
|
+
method = 'agent.create+deliver'
|
|
394
|
+
}
|
|
316
395
|
}
|
|
317
396
|
} catch (e) {
|
|
318
|
-
return { ok: false, error: { code: 'internal', message: String(e && e.message || e), details: {} } }
|
|
397
|
+
return { ok: false, error: { code: 'internal', message: '新会话创建失败: ' + String(e && e.message || e), details: { issues: [] } } }
|
|
319
398
|
}
|
|
399
|
+
} else {
|
|
400
|
+
if (typeof targetSessionId === 'string' && targetSessionId !== '') targetId = targetSessionId
|
|
401
|
+
if (deliver(targetId)) {
|
|
402
|
+
injected = true
|
|
403
|
+
method = 'append+send'
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
if (!injected) {
|
|
407
|
+
return { ok: false, error: { code: 'internal', message: '注入失败:目标会话不可用', details: { issues: [] } } }
|
|
320
408
|
}
|
|
321
|
-
return { ok: true, value: { injected, method, prompt, sessionId, messageId } }
|
|
409
|
+
return { ok: true, value: { injected, method, prompt, sessionId: targetId, messageId } }
|
|
322
410
|
}
|
|
323
411
|
|
|
324
412
|
case 'audits': {
|
|
@@ -336,7 +424,7 @@ export function registerWorkbench(ctx) {
|
|
|
336
424
|
return { ok: false, error: { code: 'internal', message: 'unknown ARIS workbench endpoint \"' + endpoint + '\"' } }
|
|
337
425
|
}
|
|
338
426
|
} catch (error) {
|
|
339
|
-
return { ok: false, error: { code: 'internal', message: String(error && error.message || error), details: {} } }
|
|
427
|
+
return { ok: false, error: { code: 'internal', message: String(error && error.message || error), details: { issues: [] } } }
|
|
340
428
|
}
|
|
341
429
|
},
|
|
342
430
|
{ authority: 'loopback' },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-aris-panel",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "ARIS research-workflow skills + interactive workbench panel for DeepSeek Harness (user fork of dsh-aris: 82 skills, cross-model Codex review, idea/workflow launcher, research status board).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|