agentworkshop 0.7.12 → 0.7.14
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/.output/nitro.json +1 -1
- package/.output/public/_nuxt/CD1SlCoI.js +14 -0
- package/.output/public/_nuxt/CYBq3kS7.js +1 -0
- package/.output/public/_nuxt/Cr9DkD1d.js +1 -0
- package/.output/public/_nuxt/DGVAuScD.js +1 -0
- package/.output/public/_nuxt/DMdVWIGz.js +19 -0
- package/.output/public/_nuxt/DVoWWlUW.js +1 -0
- package/.output/public/_nuxt/agents.CB9Ck7VQ.css +1 -0
- package/.output/public/_nuxt/builds/latest.json +1 -1
- package/.output/public/_nuxt/builds/meta/23c30854-f9e1-4651-bc3a-b695ef5faca4.json +1 -0
- package/.output/public/_nuxt/channel-templates.C-M5nKcC.css +1 -0
- package/.output/public/_nuxt/{Ds8JbqGB.js → ed5qX-x5.js} +1 -1
- package/.output/public/_nuxt/{entry.Bv_GCmPP.css → entry.BNApuuEW.css} +1 -1
- package/.output/public/_nuxt/logs.BarkrvUp.css +1 -0
- package/.output/public/_nuxt/pages.CG8UJ2TK.css +1 -0
- package/.output/public/_nuxt/teams.DN0pAGGu.css +1 -0
- package/.output/public/_nuxt/tokens.BVXU1jOK.css +1 -0
- package/.output/server/chunks/_/nitro.mjs +849 -752
- package/.output/server/chunks/_/nitro.mjs.map +1 -1
- package/.output/server/chunks/_/user.schema.mjs +1 -1
- package/.output/server/chunks/_/user.schema.mjs.map +1 -1
- package/.output/server/chunks/build/TownView-CunmEMzS.mjs +1 -1
- package/.output/server/chunks/build/_id_-CVnMdSeG.mjs +1 -1
- package/.output/server/chunks/build/_id_-D265Wh_k.mjs +1 -1
- package/.output/server/chunks/build/{agents-B10wmFri.mjs → agents--6bzGtES.mjs} +156 -80
- package/.output/server/chunks/build/agents--6bzGtES.mjs.map +1 -0
- package/.output/server/chunks/build/{channel-templates-CpWxVk5A.mjs → channel-templates-C1Bb-U75.mjs} +19 -20
- package/.output/server/chunks/build/channel-templates-C1Bb-U75.mjs.map +1 -0
- package/.output/server/chunks/build/daq-DxaXk_8N.mjs +1 -1
- package/.output/server/chunks/build/dcw-B0dtiHFG.mjs +1 -1
- package/.output/server/chunks/build/{logs-Crf6f1IC.mjs → logs-UWXeNKy_.mjs} +34 -34
- package/.output/server/chunks/build/{logs-Crf6f1IC.mjs.map → logs-UWXeNKy_.mjs.map} +1 -1
- package/.output/server/chunks/build/{pages-BmRUbGUi.mjs → pages-Aexs6LjD.mjs} +27 -15
- package/.output/server/chunks/build/pages-Aexs6LjD.mjs.map +1 -0
- package/.output/server/chunks/build/{teams-CNflUtPh.mjs → teams-DD8ZXTKL.mjs} +29 -32
- package/.output/server/chunks/build/teams-DD8ZXTKL.mjs.map +1 -0
- package/.output/server/chunks/build/{tokens-BKLzcdMg.mjs → tokens-Dxnuw96_.mjs} +14 -14
- package/.output/server/chunks/build/{tokens-BKLzcdMg.mjs.map → tokens-Dxnuw96_.mjs.map} +1 -1
- package/.output/server/chunks/build/useDaqStream-BkmyQT06.mjs +1 -1
- package/.output/server/chunks/build/useDcwStream-DWVoU96J.mjs +1 -1
- package/.output/server/chunks/build/useWorkshopApi-D2gq06N2.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/channel-templates/_id/instantiate.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/harnesses/_id/providers.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/harnesses.get.mjs +14 -2
- package/.output/server/chunks/routes/api/workshop/harnesses.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/hitl/pending.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/hitl/respond.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index.put.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/plugins/_name/disable.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/plugins/_name/enable.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/scene/layouts/_channelId_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/scene/layouts/_channelId_.put.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/workspaces/_id/channel-templates/_tplId_.post.mjs +1 -1
- package/.output/server/chunks/routes/renderer.mjs +2 -2
- package/.output/server/chunks/virtual/entry.mjs +25 -13
- package/.output/server/chunks/virtual/precomputed.mjs +1 -1
- package/.output/server/package.json +1 -1
- package/app/composables/workshop/useWorkshopApi.ts +11 -1
- package/app/pages/index.vue +72 -0
- package/app/pages/logs/index.vue +3 -1
- package/app/pages/tokens.vue +3 -1
- package/app/pages/workshop/agents.vue +71 -6
- package/app/pages/workshop/channel-templates.vue +2 -1
- package/app/pages/workshop/teams.vue +24 -2
- package/app/plugins/http.ts +4 -3
- package/i18n/locales/en.ts +8 -1
- package/i18n/locales/zh-CN.ts +8 -1
- package/package.json +148 -148
- package/scripts/_dbg-harness-e2e.mjs +92 -0
- package/scripts/_dbg-harness-ui-dbg.py +49 -0
- package/scripts/_dbg-harness-ui-e2e.py +87 -0
- package/scripts/_dbg-login-e2e.mjs +26 -0
- package/server/api/workshop/harnesses.get.ts +15 -2
- package/server/data/dcw-lines.json +14 -0
- package/server/data/dcw-rollback.json +6 -6
- package/server/data/dcws.json +198 -198
- package/server/repositories/user.repository.ts +4 -3
- package/server/schemas/user.schema.ts +2 -2
- package/server/services/workshop/agents/adapters/line-spawn.ts +3 -2
- package/server/services/workshop/agents/harness-availability.ts +127 -0
- package/server/services/workshop/agents/registry.ts +21 -0
- package/server/services/workshop/runtime/manager.ts +16 -1
- package/.output/public/_nuxt/CvYfzHYz.js +0 -1
- package/.output/public/_nuxt/D2JyEL28.js +0 -14
- package/.output/public/_nuxt/D8xY-Yjc.js +0 -19
- package/.output/public/_nuxt/QoCzfpo2.js +0 -1
- package/.output/public/_nuxt/agents.CEbox5-D.css +0 -1
- package/.output/public/_nuxt/builds/meta/54e22640-4fd0-4fee-9077-31d4d4bb7969.json +0 -1
- package/.output/public/_nuxt/channel-templates.De5ZvrGz.css +0 -1
- package/.output/public/_nuxt/eUGyJdiT.js +0 -1
- package/.output/public/_nuxt/hb_ceMJP.js +0 -1
- package/.output/public/_nuxt/logs.BS1sb41K.css +0 -1
- package/.output/public/_nuxt/pages.BkE-AfIb.css +0 -1
- package/.output/public/_nuxt/teams.CUMnbQGf.css +0 -1
- package/.output/public/_nuxt/tokens.rW1xHxRW.css +0 -1
- package/.output/server/chunks/build/agents-B10wmFri.mjs.map +0 -1
- package/.output/server/chunks/build/channel-templates-CpWxVk5A.mjs.map +0 -1
- package/.output/server/chunks/build/pages-BmRUbGUi.mjs.map +0 -1
- package/.output/server/chunks/build/teams-CNflUtPh.mjs.map +0 -1
|
@@ -33,7 +33,7 @@ export interface AgentInfoDto {
|
|
|
33
33
|
enabled?: number
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
/** harness 注册表元信息(GET /workshop/harnesses) */
|
|
36
|
+
/** harness 注册表元信息(GET /workshop/harnesses;含环境可用性探测结果) */
|
|
37
37
|
export interface HarnessMetaDto {
|
|
38
38
|
id: string
|
|
39
39
|
label: string
|
|
@@ -46,6 +46,16 @@ export interface HarnessMetaDto {
|
|
|
46
46
|
contextStats: boolean
|
|
47
47
|
compact: boolean
|
|
48
48
|
}
|
|
49
|
+
/** 环境探测:引擎可立即使用(进程内引擎恒 true;进程型引擎 = CLI 在 PATH 中) */
|
|
50
|
+
available?: boolean
|
|
51
|
+
/** 进程内引擎(无外部 CLI) */
|
|
52
|
+
inprocess?: boolean
|
|
53
|
+
/** 进程型引擎声明将拉起的命令 */
|
|
54
|
+
command?: string | null
|
|
55
|
+
/** PATH 解析出的可执行文件绝对路径 */
|
|
56
|
+
resolvedPath?: string | null
|
|
57
|
+
/** 不可用原因(available=false 时的人话提示) */
|
|
58
|
+
error?: string | null
|
|
49
59
|
}
|
|
50
60
|
|
|
51
61
|
export interface TaskDto {
|
package/app/pages/index.vue
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import { useDaqStream } from '@/app/composables/workshop/useDaqStream'
|
|
10
10
|
import { useDcwStream } from '@/app/composables/workshop/useDcwStream'
|
|
11
11
|
import { useVisibleInterval } from '@/app/composables/workshop/useVisibleInterval'
|
|
12
|
+
import { useWorkshopApi, type HarnessMetaDto } from '@/app/composables/workshop/useWorkshopApi'
|
|
12
13
|
import AwChart from '@/app/components/AwChart.vue'
|
|
13
14
|
import type { EChartsOption } from 'echarts'
|
|
14
15
|
|
|
@@ -30,13 +31,27 @@ onMounted(() => {
|
|
|
30
31
|
daq.ensureWsFeed()
|
|
31
32
|
dcw.ensureWsFeed()
|
|
32
33
|
void Promise.all([daq.load(), dcw.load()]).then(() => pushTrend())
|
|
34
|
+
void loadHarnesses()
|
|
33
35
|
useVisibleInterval(() => {
|
|
34
36
|
void daq.load()
|
|
35
37
|
void dcw.load()
|
|
38
|
+
void loadHarnesses()
|
|
36
39
|
pushTrend()
|
|
37
40
|
}, 5000, { bgMs: 30000 })
|
|
38
41
|
})
|
|
39
42
|
|
|
43
|
+
// ---------- Harness 可用性(引擎 CLI 环境探测;服务端 30s 探测缓存,随兜底节拍刷新) ----------
|
|
44
|
+
const api = useWorkshopApi()
|
|
45
|
+
const harnesses = ref<HarnessMetaDto[]>([])
|
|
46
|
+
const loadHarnesses = async (): Promise<void> => {
|
|
47
|
+
try {
|
|
48
|
+
const res = await api.listHarnesses()
|
|
49
|
+
harnesses.value = (res as unknown as { data?: { harnesses?: HarnessMetaDto[] } })?.data?.harnesses ?? []
|
|
50
|
+
}
|
|
51
|
+
catch { /* 探测不可得时面板留空,不阻塞大屏 */ }
|
|
52
|
+
}
|
|
53
|
+
const harnessOk = computed(() => harnesses.value.filter(h => h.available !== false).length)
|
|
54
|
+
|
|
40
55
|
// ---------- KPI ----------
|
|
41
56
|
const linesActive = computed(() => dcw.lines.filter(l => dcw.lineStateOf(l.id).active))
|
|
42
57
|
const daqOnline = computed(() => daq.nodes.filter(n => n.enabled && n.state !== 'offline').length)
|
|
@@ -351,6 +366,31 @@ const fleetOverflow = computed(() => Math.max(lineCards.value.length - FLEET_CAP
|
|
|
351
366
|
</div>
|
|
352
367
|
|
|
353
368
|
<!-- 大屏图阵 -->
|
|
369
|
+
<!-- 执行引擎可用性(Harness CLI 环境探测;未安装不可选,派工前强校验) -->
|
|
370
|
+
<section
|
|
371
|
+
v-if="harnesses.length > 0"
|
|
372
|
+
class="dpanel harness aw-stagger"
|
|
373
|
+
>
|
|
374
|
+
<header class="dp-hd">
|
|
375
|
+
<h3>{{ t('home.harness.title') }}</h3>
|
|
376
|
+
<small>{{ t('home.harness.sub') }}</small>
|
|
377
|
+
<small class="mono fleet-total">{{ harnessOk }}/{{ harnesses.length }}</small>
|
|
378
|
+
</header>
|
|
379
|
+
<div class="harness-row">
|
|
380
|
+
<div
|
|
381
|
+
v-for="h in harnesses"
|
|
382
|
+
:key="h.id"
|
|
383
|
+
class="h-item"
|
|
384
|
+
:class="{ off: h.available === false }"
|
|
385
|
+
:title="h.available === false ? (h.error ?? '') : (h.resolvedPath ?? h.command ?? '')"
|
|
386
|
+
>
|
|
387
|
+
<span class="h-dot" />
|
|
388
|
+
<span class="h-name">{{ h.label }}</span>
|
|
389
|
+
<span class="h-cmd mono">{{ h.available === false ? t('home.harness.missing') : (h.inprocess ? 'in-process' : h.command) }}</span>
|
|
390
|
+
</div>
|
|
391
|
+
</div>
|
|
392
|
+
</section>
|
|
393
|
+
|
|
354
394
|
<div class="grid aw-stagger">
|
|
355
395
|
<section class="dpanel span8">
|
|
356
396
|
<header class="dp-hd">
|
|
@@ -790,6 +830,38 @@ const fleetOverflow = computed(() => Math.max(lineCards.value.length - FLEET_CAP
|
|
|
790
830
|
font-size: 11px;
|
|
791
831
|
color: var(--ink-faint);
|
|
792
832
|
}
|
|
833
|
+
/* ---------- Harness 可用性条:墨色药丸式引擎清单,未安装灰化 ---------- */
|
|
834
|
+
.harness-row {
|
|
835
|
+
display: flex;
|
|
836
|
+
flex-wrap: wrap;
|
|
837
|
+
gap: 8px;
|
|
838
|
+
padding: 4px 0 6px;
|
|
839
|
+
}
|
|
840
|
+
.h-item {
|
|
841
|
+
display: flex;
|
|
842
|
+
gap: 7px;
|
|
843
|
+
align-items: center;
|
|
844
|
+
padding: 5px 12px;
|
|
845
|
+
font-size: 11.5px;
|
|
846
|
+
cursor: default;
|
|
847
|
+
background: color-mix(in srgb, var(--ink) 3%, transparent);
|
|
848
|
+
border: 1px solid var(--divider-hair);
|
|
849
|
+
border-radius: var(--radius-pill);
|
|
850
|
+
transition: border-color 0.15s, background 0.15s;
|
|
851
|
+
}
|
|
852
|
+
.h-item:hover { border-color: var(--line-strong); }
|
|
853
|
+
.h-dot {
|
|
854
|
+
flex: none;
|
|
855
|
+
width: 6px;
|
|
856
|
+
height: 6px;
|
|
857
|
+
border-radius: 50%;
|
|
858
|
+
background: var(--tone-success-dot, #4a6b57);
|
|
859
|
+
}
|
|
860
|
+
.h-item.off { opacity: 0.55; }
|
|
861
|
+
.h-item.off .h-dot { background: var(--tone-danger-dot, #c25a4e); }
|
|
862
|
+
.h-name { font-weight: 600; color: var(--ink); }
|
|
863
|
+
.h-item.off .h-name { color: var(--ink-soft); }
|
|
864
|
+
.h-cmd { font-size: 10px; color: var(--ink-faint); }
|
|
793
865
|
/* 全部产线入口:虚线幽灵卡,聚合清单外的余量 */
|
|
794
866
|
.fleet-all {
|
|
795
867
|
display: flex;
|
package/app/pages/logs/index.vue
CHANGED
package/app/pages/tokens.vue
CHANGED
|
@@ -272,7 +272,9 @@ useHead({ title: () => tt('titles.tokens') })
|
|
|
272
272
|
<template v-else>
|
|
273
273
|
<div class="head">
|
|
274
274
|
<div>
|
|
275
|
-
<p class="aw-kicker">
|
|
275
|
+
<p class="aw-kicker">
|
|
276
|
+
agentworkshop / api tokens
|
|
277
|
+
</p>
|
|
276
278
|
<h2>API Token</h2>
|
|
277
279
|
<p class="sub">
|
|
278
280
|
{{ userStore.user?.name }} · {{ $t('tokens.k1upppaw026') }}
|
|
@@ -26,7 +26,9 @@ const load = async (): Promise<void> => {
|
|
|
26
26
|
loading.value = false
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
|
-
|
|
29
|
+
// SSR 安全:setup 期 $http(axios)无法在服务端发相对地址请求,拒绝会变成未处理
|
|
30
|
+
// rejection 直杀渲染进程;页面数据一律客户端装载
|
|
31
|
+
if (import.meta.client) void load()
|
|
30
32
|
|
|
31
33
|
// ===== 过滤(全部/我的/公开/内置;admin 另有"他人私有") =====
|
|
32
34
|
type Filter = 'all' | 'mine' | 'public' | 'builtin' | 'others'
|
|
@@ -64,8 +66,10 @@ const visTag = (t: AgentTemplateDto): { text: string, color: string, icon?: stri
|
|
|
64
66
|
return { text: tt('agents.k447jj019'), color: 'default' }
|
|
65
67
|
}
|
|
66
68
|
|
|
67
|
-
// ===== harness 选项(
|
|
69
|
+
// ===== harness 选项(引擎注册表动态拉取,含环境可用性探测;失败回退静态表) =====
|
|
68
70
|
const harnesses = ref<HarnessMetaDto[]>([])
|
|
71
|
+
const harnessById = computed(() => new Map(harnesses.value.map(h => [h.id, h])))
|
|
72
|
+
const isUnavailable = (id: string): boolean => harnessById.value.get(id)?.available === false
|
|
69
73
|
const harnessOptions = computed(() =>
|
|
70
74
|
(harnesses.value.length > 0
|
|
71
75
|
? harnesses.value
|
|
@@ -73,7 +77,10 @@ const harnessOptions = computed(() =>
|
|
|
73
77
|
{ id: 'mock', label: 'mock(测试)', description: '', capabilities: { steer: true, supervise: false, hitl: false, terminal: false, contextStats: false, compact: false } },
|
|
74
78
|
{ id: 'omp', label: 'omp(真实 LLM)', description: '', capabilities: { steer: true, supervise: true, hitl: true, terminal: true, contextStats: true, compact: true } },
|
|
75
79
|
{ id: 'claude', label: 'claude', description: '', capabilities: { steer: false, supervise: false, hitl: false, terminal: false, contextStats: false, compact: false } },
|
|
76
|
-
] as HarnessMetaDto[]).map(h =>
|
|
80
|
+
] as HarnessMetaDto[]).map((h) => {
|
|
81
|
+
const unavailable = h.available === false
|
|
82
|
+
return { value: h.id, label: unavailable ? `${h.label}(${tt('agents.notInstalled')})` : h.label, disabled: unavailable }
|
|
83
|
+
}),
|
|
77
84
|
)
|
|
78
85
|
const capBadges = (id: string): string[] => {
|
|
79
86
|
const caps = harnesses.value.find(h => h.id === id)?.capabilities
|
|
@@ -93,7 +100,7 @@ const loadHarnesses = async (): Promise<void> => {
|
|
|
93
100
|
}
|
|
94
101
|
catch { /* 回退静态表 */ }
|
|
95
102
|
}
|
|
96
|
-
void loadHarnesses()
|
|
103
|
+
if (import.meta.client) void loadHarnesses()
|
|
97
104
|
|
|
98
105
|
const editOpen = ref(false)
|
|
99
106
|
const editing = ref<AgentTemplateDto | null>(null)
|
|
@@ -123,6 +130,11 @@ const save = async (): Promise<void> => {
|
|
|
123
130
|
message.error('config 不是合法 JSON')
|
|
124
131
|
return
|
|
125
132
|
}
|
|
133
|
+
// 前端兜底:引擎未安装禁止保存(与后端 assertHarnessUsable 同判据)
|
|
134
|
+
if (isUnavailable(form.harness)) {
|
|
135
|
+
message.error(harnessById.value.get(form.harness)?.error ?? tt('agents.notInstalled'))
|
|
136
|
+
return
|
|
137
|
+
}
|
|
126
138
|
try {
|
|
127
139
|
if (editing.value) {
|
|
128
140
|
await api.updateTemplate(editing.value.id, { name: form.name, harness: form.harness, config, visibility: form.visibility })
|
|
@@ -220,8 +232,21 @@ useHead({ title: () => tt('titles.agents') })
|
|
|
220
232
|
<a-table-column
|
|
221
233
|
title="harness"
|
|
222
234
|
data-index="harness"
|
|
223
|
-
:width="
|
|
224
|
-
|
|
235
|
+
:width="120"
|
|
236
|
+
>
|
|
237
|
+
<template #default="{ record }">
|
|
238
|
+
<span
|
|
239
|
+
class="h-cell"
|
|
240
|
+
:class="{ off: isUnavailable(record.harness) }"
|
|
241
|
+
:title="isUnavailable(record.harness) ? (harnessById.get(record.harness)?.error ?? '') : (harnessById.get(record.harness)?.resolvedPath ?? '')"
|
|
242
|
+
>
|
|
243
|
+
<span class="h-dot" />{{ record.harness }}<span
|
|
244
|
+
v-if="isUnavailable(record.harness)"
|
|
245
|
+
class="h-miss"
|
|
246
|
+
>({{ tt('agents.notInstalled') }})</span>
|
|
247
|
+
</span>
|
|
248
|
+
</template>
|
|
249
|
+
</a-table-column>
|
|
225
250
|
<a-table-column
|
|
226
251
|
:title="$t('agents.k3lrqn0002')"
|
|
227
252
|
:width="120"
|
|
@@ -346,6 +371,21 @@ useHead({ title: () => tt('titles.agents') })
|
|
|
346
371
|
v-model:value="form.harness"
|
|
347
372
|
:options="harnessOptions"
|
|
348
373
|
/>
|
|
374
|
+
<div
|
|
375
|
+
v-if="harnesses.length > 0"
|
|
376
|
+
class="harness-status"
|
|
377
|
+
:class="{ off: isUnavailable(form.harness) }"
|
|
378
|
+
>
|
|
379
|
+
<template v-if="isUnavailable(form.harness)">
|
|
380
|
+
<span class="i-tabler-plug-off" /> {{ harnessById.get(form.harness)?.error }}
|
|
381
|
+
</template>
|
|
382
|
+
<template v-else-if="harnessById.get(form.harness)?.inprocess">
|
|
383
|
+
<span class="i-tabler-plug-connected" /> {{ tt('agents.harnessInprocess') }}
|
|
384
|
+
</template>
|
|
385
|
+
<template v-else-if="harnessById.get(form.harness)?.resolvedPath">
|
|
386
|
+
<span class="i-tabler-plug-connected" /> <span class="mono">{{ harnessById.get(form.harness)?.resolvedPath }}</span>
|
|
387
|
+
</template>
|
|
388
|
+
</div>
|
|
349
389
|
<div
|
|
350
390
|
v-if="capBadges(form.harness).length"
|
|
351
391
|
class="harness-caps"
|
|
@@ -388,6 +428,31 @@ useHead({ title: () => tt('titles.agents') })
|
|
|
388
428
|
border-radius: 3px;
|
|
389
429
|
opacity: 0.75;
|
|
390
430
|
}
|
|
431
|
+
.harness-status {
|
|
432
|
+
display: flex;
|
|
433
|
+
gap: 6px;
|
|
434
|
+
align-items: center;
|
|
435
|
+
margin-top: 6px;
|
|
436
|
+
font-size: 11px;
|
|
437
|
+
color: var(--ink-faint);
|
|
438
|
+
}
|
|
439
|
+
.harness-status.off { color: var(--danger, #c25a4e); }
|
|
440
|
+
.h-cell {
|
|
441
|
+
display: inline-flex;
|
|
442
|
+
gap: 6px;
|
|
443
|
+
align-items: center;
|
|
444
|
+
font-family: var(--font-mono);
|
|
445
|
+
font-size: 12px;
|
|
446
|
+
}
|
|
447
|
+
.h-dot {
|
|
448
|
+
width: 6px;
|
|
449
|
+
height: 6px;
|
|
450
|
+
border-radius: 50%;
|
|
451
|
+
background: var(--ok, #4a6b57);
|
|
452
|
+
}
|
|
453
|
+
.h-cell.off .h-dot { background: var(--danger, #c25a4e); }
|
|
454
|
+
.h-cell.off { opacity: 0.75; }
|
|
455
|
+
.h-miss { color: var(--danger, #c25a4e); font-family: var(--font-sans); font-size: 11px; }
|
|
391
456
|
.page { padding: 4px; }
|
|
392
457
|
.head {
|
|
393
458
|
display: flex;
|
|
@@ -31,7 +31,8 @@ const load = async (): Promise<void> => {
|
|
|
31
31
|
loading.value = false
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
|
-
|
|
34
|
+
// SSR 安全:setup 期 $http(axios)无法在服务端发相对地址请求(同 agents 页注释)
|
|
35
|
+
if (import.meta.client) void load()
|
|
35
36
|
|
|
36
37
|
type Filter = 'all' | 'mine' | 'public' | 'builtin'
|
|
37
38
|
const filter = ref<Filter>('all')
|
|
@@ -31,7 +31,29 @@ const load = async (): Promise<void> => {
|
|
|
31
31
|
loading.value = false
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
|
-
|
|
34
|
+
// SSR 安全:setup 期 $http(axios)无法在服务端发相对地址请求(同 agents 页注释)
|
|
35
|
+
if (import.meta.client) void load()
|
|
36
|
+
|
|
37
|
+
// ===== harness 可用性(成员选择禁用引擎未安装的模板;deploy 由后端 assert 兜底) =====
|
|
38
|
+
const harnessAvail = ref<Record<string, boolean>>({})
|
|
39
|
+
const loadHarnessAvail = async (): Promise<void> => {
|
|
40
|
+
try {
|
|
41
|
+
const res = await api.listHarnesses()
|
|
42
|
+
const list = (res as unknown as { data?: { harnesses?: Array<{ id: string, available?: boolean }> } })?.data?.harnesses
|
|
43
|
+
if (Array.isArray(list)) {
|
|
44
|
+
const m: Record<string, boolean> = {}
|
|
45
|
+
for (const h of list) m[h.id] = h.available !== false
|
|
46
|
+
harnessAvail.value = m
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch { /* 探测不可得时不限制选项(后端仍有强校验) */ }
|
|
50
|
+
}
|
|
51
|
+
if (import.meta.client) void loadHarnessAvail()
|
|
52
|
+
const tplUnavailable = (tpl: AgentTemplateDto): boolean => harnessAvail.value[tpl.harness] === false
|
|
53
|
+
const memberOptions = computed(() => templates.value.map((t) => {
|
|
54
|
+
const un = tplUnavailable(t)
|
|
55
|
+
return { value: t.id, label: un ? `${t.name}(${t.harness} · ${t('agents.notInstalled')})` : `${t.name}(${t.harness})`, disabled: un }
|
|
56
|
+
}))
|
|
35
57
|
|
|
36
58
|
// ===== 过滤 =====
|
|
37
59
|
type Filter = 'all' | 'mine' | 'public' | 'builtin'
|
|
@@ -323,7 +345,7 @@ useHead({ title: () => t('titles.teams') })
|
|
|
323
345
|
<a-form-item :label="$t('teams.k41ds5006')">
|
|
324
346
|
<a-select
|
|
325
347
|
v-model:value="addTemplateId"
|
|
326
|
-
:options="
|
|
348
|
+
:options="memberOptions"
|
|
327
349
|
/>
|
|
328
350
|
</a-form-item>
|
|
329
351
|
<a-form-item :label="$t('teams.k479op007')">
|
package/app/plugins/http.ts
CHANGED
|
@@ -52,17 +52,18 @@ export default defineNuxtPlugin((nuxtApp) => {
|
|
|
52
52
|
catch { /* 无 pinia 上下文(极端时序) */ }
|
|
53
53
|
if (!g.__awShown401) {
|
|
54
54
|
g.__awShown401 = true
|
|
55
|
-
message
|
|
55
|
+
// SSR 安全:ant-design message 依赖 DOM,服务端调用会炸掉渲染进程
|
|
56
|
+
if (import.meta.client) message.error('登录已过期,请重新登录')
|
|
56
57
|
setTimeout(() => {
|
|
57
58
|
g.__awShown401 = false
|
|
58
59
|
}, 3000)
|
|
59
60
|
}
|
|
60
61
|
}
|
|
61
62
|
else if (status >= 500) {
|
|
62
|
-
message.error('服务器异常,请稍后重试')
|
|
63
|
+
if (import.meta.client) message.error('服务器异常,请稍后重试')
|
|
63
64
|
}
|
|
64
65
|
else {
|
|
65
|
-
message.error(msg)
|
|
66
|
+
if (import.meta.client) message.error(msg)
|
|
66
67
|
}
|
|
67
68
|
|
|
68
69
|
return Promise.reject(error)
|
package/i18n/locales/en.ts
CHANGED
|
@@ -81,6 +81,11 @@ export default {
|
|
|
81
81
|
fleetAll: 'View all lines',
|
|
82
82
|
fleetHint: 'Running first · top 8 shown',
|
|
83
83
|
writesFailed: 'failed',
|
|
84
|
+
harness: {
|
|
85
|
+
title: 'Agent Harnesses',
|
|
86
|
+
sub: 'CLI availability probe · ready to dispatch',
|
|
87
|
+
missing: 'not installed',
|
|
88
|
+
},
|
|
84
89
|
fields: {
|
|
85
90
|
name: 'App Name',
|
|
86
91
|
version: 'Version',
|
|
@@ -342,6 +347,8 @@ export default {
|
|
|
342
347
|
hitlSubmit: 'Submit',
|
|
343
348
|
},
|
|
344
349
|
agents: {
|
|
350
|
+
notInstalled: 'not installed',
|
|
351
|
+
harnessInprocess: 'In-process engine · always available',
|
|
345
352
|
k3pa5h4025: 'Templates',
|
|
346
353
|
k3xhia001: 'Name',
|
|
347
354
|
k3lrqn0002: 'Visibility',
|
|
@@ -1119,7 +1126,7 @@ export default {
|
|
|
1119
1126
|
ke16e53026: '\'s resources (user-isolated; persisted server-side)',
|
|
1120
1127
|
k41ds5027: 'Template',
|
|
1121
1128
|
k1ggoa45028: 'Members / Busy',
|
|
1122
|
-
k48h2c001: 'Email',
|
|
1129
|
+
k48h2c001: 'Email or username',
|
|
1123
1130
|
k3yvgs002: 'Password',
|
|
1124
1131
|
kwgbai9003: 'Username (unique)',
|
|
1125
1132
|
kizjkbo004: 'Email (unique)',
|
package/i18n/locales/zh-CN.ts
CHANGED
|
@@ -81,6 +81,11 @@ export default {
|
|
|
81
81
|
fleetAll: '查看全部产线',
|
|
82
82
|
fleetHint: '运行中优先 · 最多展示 8 条',
|
|
83
83
|
writesFailed: '失败',
|
|
84
|
+
harness: {
|
|
85
|
+
title: '执行引擎',
|
|
86
|
+
sub: 'Harness CLI 环境探测 · 就绪即可派工',
|
|
87
|
+
missing: '未安装',
|
|
88
|
+
},
|
|
84
89
|
fields: {
|
|
85
90
|
name: '应用名称',
|
|
86
91
|
version: '版本',
|
|
@@ -342,6 +347,8 @@ export default {
|
|
|
342
347
|
hitlSubmit: '提交',
|
|
343
348
|
},
|
|
344
349
|
agents: {
|
|
350
|
+
notInstalled: '未安装',
|
|
351
|
+
harnessInprocess: '进程内引擎 · 恒可用',
|
|
345
352
|
k3pa5h4025: '模板库',
|
|
346
353
|
k3xhia001: '名称',
|
|
347
354
|
k3lrqn0002: '可见性',
|
|
@@ -1118,7 +1125,7 @@ export default {
|
|
|
1118
1125
|
ke16e53026: '的资源(用户级隔离;服务端持久化)',
|
|
1119
1126
|
k41ds5027: '模板',
|
|
1120
1127
|
k1ggoa45028: '成员 / 忙',
|
|
1121
|
-
k48h2c001: '邮箱',
|
|
1128
|
+
k48h2c001: '邮箱 / 用户名',
|
|
1122
1129
|
k3yvgs002: '密码',
|
|
1123
1130
|
kwgbai9003: '用户名(唯一)',
|
|
1124
1131
|
kizjkbo004: '邮箱(唯一)',
|