@zhin.js/client 1.1.4 → 1.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/README.md +94 -432
- package/client/agent-stream.ts +121 -0
- package/client/app.test.ts +37 -0
- package/client/app.ts +130 -138
- package/client/bootstrap/loadConsoleEntries.test.ts +23 -12
- package/client/bootstrap/loadConsoleEntries.ts +40 -27
- package/client/console-client-context.ts +24 -0
- package/client/console-client.test.ts +46 -0
- package/client/console-client.ts +70 -0
- package/client/console-events.ts +38 -0
- package/client/console-utils/remoteApi.test.ts +91 -0
- package/client/console-utils/remoteApi.ts +47 -9
- package/client/index.ts +92 -28
- package/client/mediaSrc.test.ts +49 -0
- package/client/mediaSrc.ts +84 -11
- package/client/message-content/MarkdownContent.tsx +128 -0
- package/client/message-content/markdown.test.ts +48 -0
- package/client/message-content/markdown.ts +247 -0
- package/client/persistence/idb-store.test.ts +121 -0
- package/client/persistence/idb-store.ts +40 -21
- package/client/segments.test.ts +16 -0
- package/client/segments.ts +39 -0
- package/client/transport/console-transport.test.ts +394 -0
- package/client/transport/console-transport.ts +676 -0
- package/client/transport/hooks.test.ts +21 -0
- package/client/transport/hooks.ts +535 -0
- package/client/transport/index.ts +23 -0
- package/client/{websocket → transport}/types.ts +16 -17
- package/client/types.ts +5 -8
- package/client/workroom/WorkroomRunsPage.tsx +355 -0
- package/client/workroom/registerWorkroomConsole.test.ts +25 -0
- package/client/workroom/registerWorkroomConsole.ts +17 -0
- package/dist/agent-stream.d.ts +17 -0
- package/dist/agent-stream.d.ts.map +1 -0
- package/dist/agent-stream.js +73 -0
- package/dist/agent-stream.js.map +1 -0
- package/dist/app.d.ts +17 -52
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +110 -95
- package/dist/app.js.map +1 -1
- package/dist/bootstrap/loadConsoleEntries.d.ts +10 -7
- package/dist/bootstrap/loadConsoleEntries.d.ts.map +1 -1
- package/dist/bootstrap/loadConsoleEntries.js +37 -11
- package/dist/bootstrap/loadConsoleEntries.js.map +1 -1
- package/dist/console-client-context.d.ts +9 -0
- package/dist/console-client-context.d.ts.map +1 -0
- package/dist/console-client-context.js +13 -0
- package/dist/console-client-context.js.map +1 -0
- package/dist/console-client.d.ts +25 -0
- package/dist/console-client.d.ts.map +1 -0
- package/dist/console-client.js +45 -0
- package/dist/console-client.js.map +1 -0
- package/dist/console-events.d.ts +8 -0
- package/dist/console-events.d.ts.map +1 -0
- package/dist/console-events.js +26 -0
- package/dist/console-events.js.map +1 -0
- package/dist/console-utils/remoteApi.d.ts +2 -1
- package/dist/console-utils/remoteApi.d.ts.map +1 -1
- package/dist/console-utils/remoteApi.js +46 -8
- package/dist/console-utils/remoteApi.js.map +1 -1
- package/dist/index.d.ts +14 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -8
- package/dist/index.js.map +1 -1
- package/dist/mediaSrc.d.ts +1 -1
- package/dist/mediaSrc.d.ts.map +1 -1
- package/dist/mediaSrc.js +91 -11
- package/dist/mediaSrc.js.map +1 -1
- package/dist/message-content/MarkdownContent.d.ts +11 -0
- package/dist/message-content/MarkdownContent.d.ts.map +1 -0
- package/dist/message-content/MarkdownContent.js +68 -0
- package/dist/message-content/MarkdownContent.js.map +1 -0
- package/dist/message-content/markdown.d.ts +47 -0
- package/dist/message-content/markdown.d.ts.map +1 -0
- package/dist/message-content/markdown.js +219 -0
- package/dist/message-content/markdown.js.map +1 -0
- package/dist/persistence/idb-store.d.ts +5 -2
- package/dist/persistence/idb-store.d.ts.map +1 -1
- package/dist/persistence/idb-store.js +29 -20
- package/dist/persistence/idb-store.js.map +1 -1
- package/dist/segments.d.ts +9 -0
- package/dist/segments.d.ts.map +1 -0
- package/dist/segments.js +35 -0
- package/dist/segments.js.map +1 -0
- package/dist/transport/console-transport.d.ts +217 -0
- package/dist/transport/console-transport.d.ts.map +1 -0
- package/dist/transport/console-transport.js +563 -0
- package/dist/transport/console-transport.js.map +1 -0
- package/dist/transport/hooks.d.ts +253 -0
- package/dist/transport/hooks.d.ts.map +1 -0
- package/dist/transport/hooks.js +483 -0
- package/dist/transport/hooks.js.map +1 -0
- package/dist/transport/index.d.ts +4 -0
- package/dist/transport/index.d.ts.map +1 -0
- package/dist/transport/index.js +4 -0
- package/dist/transport/index.js.map +1 -0
- package/dist/{websocket → transport}/types.d.ts +14 -13
- package/dist/transport/types.d.ts.map +1 -0
- package/dist/{websocket → transport}/types.js +4 -9
- package/dist/transport/types.js.map +1 -0
- package/dist/types.d.ts +4 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/workroom/WorkroomRunsPage.d.ts +2 -0
- package/dist/workroom/WorkroomRunsPage.d.ts.map +1 -0
- package/dist/workroom/WorkroomRunsPage.js +111 -0
- package/dist/workroom/WorkroomRunsPage.js.map +1 -0
- package/dist/workroom/registerWorkroomConsole.d.ts +4 -0
- package/dist/workroom/registerWorkroomConsole.d.ts.map +1 -0
- package/dist/workroom/registerWorkroomConsole.js +16 -0
- package/dist/workroom/registerWorkroomConsole.js.map +1 -0
- package/package.json +18 -8
- package/client/runtime/index.ts +0 -12
- package/client/websocket/hooks.ts +0 -289
- package/client/websocket/index.ts +0 -6
- package/client/websocket/instance.ts +0 -24
- package/client/websocket/manager.ts +0 -426
- package/client/websocket/remote-settings.ts +0 -25
- package/dist/bootstrap/loadConsoleEntries.test.d.ts +0 -2
- package/dist/bootstrap/loadConsoleEntries.test.d.ts.map +0 -1
- package/dist/bootstrap/loadConsoleEntries.test.js +0 -27
- package/dist/bootstrap/loadConsoleEntries.test.js.map +0 -1
- package/dist/runtime/index.d.ts +0 -6
- package/dist/runtime/index.d.ts.map +0 -1
- package/dist/runtime/index.js +0 -10
- package/dist/runtime/index.js.map +0 -1
- package/dist/websocket/hooks.d.ts +0 -105
- package/dist/websocket/hooks.d.ts.map +0 -1
- package/dist/websocket/hooks.js +0 -247
- package/dist/websocket/hooks.js.map +0 -1
- package/dist/websocket/index.d.ts +0 -7
- package/dist/websocket/index.d.ts.map +0 -1
- package/dist/websocket/index.js +0 -7
- package/dist/websocket/index.js.map +0 -1
- package/dist/websocket/instance.d.ts +0 -5
- package/dist/websocket/instance.d.ts.map +0 -1
- package/dist/websocket/instance.js +0 -21
- package/dist/websocket/instance.js.map +0 -1
- package/dist/websocket/manager.d.ts +0 -113
- package/dist/websocket/manager.d.ts.map +0 -1
- package/dist/websocket/manager.js +0 -403
- package/dist/websocket/manager.js.map +0 -1
- package/dist/websocket/remote-settings.d.ts +0 -5
- package/dist/websocket/remote-settings.d.ts.map +0 -1
- package/dist/websocket/remote-settings.js +0 -23
- package/dist/websocket/remote-settings.js.map +0 -1
- package/dist/websocket/types.d.ts.map +0 -1
- package/dist/websocket/types.js.map +0 -1
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
import { useCallback, useState } from 'react';
|
|
2
|
+
import { apiFetch, consoleRpc } from '../console-utils/remoteApi.js';
|
|
3
|
+
|
|
4
|
+
type PlanningSetupStatus = {
|
|
5
|
+
projectId: string;
|
|
6
|
+
ready: boolean;
|
|
7
|
+
principalId?: string;
|
|
8
|
+
trustedPackPublisher: boolean;
|
|
9
|
+
projectSponsor: boolean;
|
|
10
|
+
catalogReady: boolean;
|
|
11
|
+
registryRevision: number;
|
|
12
|
+
activeProfile?: { revisionId: string; digest: string };
|
|
13
|
+
planningPolicyReady: boolean;
|
|
14
|
+
disclosureReady: boolean;
|
|
15
|
+
disclosureConfigReady: boolean;
|
|
16
|
+
modelProviderAlias?: string;
|
|
17
|
+
availableAgents: string[];
|
|
18
|
+
availableTools: string[];
|
|
19
|
+
availableSkills: string[];
|
|
20
|
+
diagnostics: string[];
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
type WorkroomTask = {
|
|
24
|
+
key: string;
|
|
25
|
+
title: string;
|
|
26
|
+
status: string;
|
|
27
|
+
revision: number;
|
|
28
|
+
attempt: number;
|
|
29
|
+
maxAttempts: number;
|
|
30
|
+
reportRef?: string;
|
|
31
|
+
terminalReason?: string;
|
|
32
|
+
currentAssignmentId?: string;
|
|
33
|
+
blockers?: Array<{ id: string; kind: string; owner: string; reason: string; deadline: number }>;
|
|
34
|
+
latestProgress?: { summary: string; completedUnits?: number; totalUnits?: number };
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
type WorkroomAssignment = {
|
|
38
|
+
id: string;
|
|
39
|
+
taskKey: string;
|
|
40
|
+
role: string;
|
|
41
|
+
status: string;
|
|
42
|
+
owner: string;
|
|
43
|
+
attempt: number;
|
|
44
|
+
leaseExpiresAt: number;
|
|
45
|
+
latestProgress?: { summary: string; completedUnits?: number; totalUnits?: number };
|
|
46
|
+
outcome?: string;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
type WorkroomWait = {
|
|
50
|
+
id: string;
|
|
51
|
+
taskKey: string;
|
|
52
|
+
status: string;
|
|
53
|
+
owner: string;
|
|
54
|
+
route: string;
|
|
55
|
+
riskTier: string;
|
|
56
|
+
deadline: number;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
type RunListItem = {
|
|
60
|
+
runId: string;
|
|
61
|
+
projectId: string;
|
|
62
|
+
status: string;
|
|
63
|
+
title: string;
|
|
64
|
+
sequence: number;
|
|
65
|
+
now: number;
|
|
66
|
+
cancelRequested: boolean;
|
|
67
|
+
tasks: Record<string, WorkroomTask>;
|
|
68
|
+
assignments: Record<string, WorkroomAssignment>;
|
|
69
|
+
reviewerAssignments: Record<string, WorkroomWait>;
|
|
70
|
+
sponsorGates: Record<string, WorkroomWait>;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
type RunDetail = RunListItem;
|
|
74
|
+
|
|
75
|
+
export default function WorkroomRunsPage() {
|
|
76
|
+
const [projectId, setProjectId] = useState('');
|
|
77
|
+
const [runs, setRuns] = useState<RunListItem[]>([]);
|
|
78
|
+
const [selected, setSelected] = useState<RunDetail | null>(null);
|
|
79
|
+
const [loading, setLoading] = useState(false);
|
|
80
|
+
const [error, setError] = useState('');
|
|
81
|
+
const [detailView, setDetailView] = useState<'tasks' | 'assignments' | 'acceptance'>('tasks');
|
|
82
|
+
const [planning, setPlanning] = useState<PlanningSetupStatus | null>(null);
|
|
83
|
+
const [planningBusy, setPlanningBusy] = useState(false);
|
|
84
|
+
|
|
85
|
+
const loadPlanning = useCallback(async () => {
|
|
86
|
+
if (!projectId.trim()) {
|
|
87
|
+
setError('请输入 projectId');
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
setPlanningBusy(true);
|
|
91
|
+
setError('');
|
|
92
|
+
try {
|
|
93
|
+
setPlanning(await consoleRpc<PlanningSetupStatus>('workroom.profile.status', {
|
|
94
|
+
projectId: projectId.trim(),
|
|
95
|
+
}));
|
|
96
|
+
} catch (err) {
|
|
97
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
98
|
+
} finally {
|
|
99
|
+
setPlanningBusy(false);
|
|
100
|
+
}
|
|
101
|
+
}, [projectId]);
|
|
102
|
+
|
|
103
|
+
const bootstrapPlanning = useCallback(async () => {
|
|
104
|
+
if (!planning) return;
|
|
105
|
+
setPlanningBusy(true);
|
|
106
|
+
setError('');
|
|
107
|
+
try {
|
|
108
|
+
const next = await consoleRpc<PlanningSetupStatus>('workroom.profile.bootstrap', {
|
|
109
|
+
operationId: `console:planning-bootstrap:${planning.projectId}:${Date.now()}`,
|
|
110
|
+
projectId: planning.projectId,
|
|
111
|
+
expectedRegistryRevision: planning.registryRevision,
|
|
112
|
+
includeTools: planning.availableTools,
|
|
113
|
+
includeSkills: planning.availableSkills,
|
|
114
|
+
});
|
|
115
|
+
setPlanning(next);
|
|
116
|
+
} catch (err) {
|
|
117
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
118
|
+
} finally {
|
|
119
|
+
setPlanningBusy(false);
|
|
120
|
+
}
|
|
121
|
+
}, [planning]);
|
|
122
|
+
|
|
123
|
+
const loadRuns = useCallback(async () => {
|
|
124
|
+
if (!projectId.trim()) {
|
|
125
|
+
setError('请输入 projectId');
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
setLoading(true);
|
|
129
|
+
setError('');
|
|
130
|
+
setSelected(null);
|
|
131
|
+
try {
|
|
132
|
+
const res = await apiFetch(
|
|
133
|
+
`/api/agent/workroom/runs?projectId=${encodeURIComponent(projectId.trim())}`,
|
|
134
|
+
);
|
|
135
|
+
const body = await res.json() as { success?: boolean; error?: string; data?: { runs?: RunListItem[] } };
|
|
136
|
+
if (!res.ok || !body.success) {
|
|
137
|
+
throw new Error(body.error ?? `HTTP ${res.status}`);
|
|
138
|
+
}
|
|
139
|
+
setRuns(body.data?.runs ?? []);
|
|
140
|
+
} catch (err) {
|
|
141
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
142
|
+
setRuns([]);
|
|
143
|
+
} finally {
|
|
144
|
+
setLoading(false);
|
|
145
|
+
}
|
|
146
|
+
}, [projectId]);
|
|
147
|
+
|
|
148
|
+
const loadRunDetail = useCallback(async (runId: string, runProjectId: string) => {
|
|
149
|
+
setLoading(true);
|
|
150
|
+
setError('');
|
|
151
|
+
try {
|
|
152
|
+
const res = await apiFetch(`/api/agent/workroom/runs/${encodeURIComponent(runId)}?projectId=${encodeURIComponent(runProjectId)}`);
|
|
153
|
+
const body = await res.json() as { success?: boolean; error?: string; data?: RunDetail };
|
|
154
|
+
if (!res.ok || !body.success || !body.data) {
|
|
155
|
+
throw new Error(body.error ?? `HTTP ${res.status}`);
|
|
156
|
+
}
|
|
157
|
+
setSelected(body.data);
|
|
158
|
+
} catch (err) {
|
|
159
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
160
|
+
} finally {
|
|
161
|
+
setLoading(false);
|
|
162
|
+
}
|
|
163
|
+
}, []);
|
|
164
|
+
|
|
165
|
+
return (
|
|
166
|
+
<div className="p-4 md:p-6 space-y-5 max-w-7xl mx-auto">
|
|
167
|
+
<header className="flex flex-wrap items-end justify-between gap-3 border-b pb-4">
|
|
168
|
+
<div>
|
|
169
|
+
<span className="text-[11px] font-semibold uppercase tracking-[0.14em] text-muted-foreground">Project control plane</span>
|
|
170
|
+
<h1 className="mt-1 text-xl font-semibold tracking-tight">Workroom 控制台</h1>
|
|
171
|
+
<p className="mt-1 max-w-3xl text-sm text-muted-foreground">查看 Journal + CAS Kernel 投影出的 Run、Task、Assignment 与验收门;普通聊天不会隐式创建 Workroom。</p>
|
|
172
|
+
</div>
|
|
173
|
+
<span className="rounded-full border px-2.5 py-1 text-xs text-muted-foreground">只读事实投影</span>
|
|
174
|
+
</header>
|
|
175
|
+
|
|
176
|
+
<details className="rounded-xl border bg-muted/20 open:bg-muted/10">
|
|
177
|
+
<summary className="cursor-pointer list-none px-4 py-3 text-sm font-medium">运行与持久化配置 <span className="ml-2 text-xs font-normal text-muted-foreground">ai.sessions.useDatabase · 修改后需重启</span></summary>
|
|
178
|
+
<div className="grid gap-4 border-t px-4 py-4 md:grid-cols-[1fr_1fr]">
|
|
179
|
+
<div className="space-y-2 text-sm">
|
|
180
|
+
<p className="font-medium">数据库 Journal(默认)</p>
|
|
181
|
+
<p className="text-muted-foreground">`ai.sessions.useDatabase` 不为 false 时写入 `workroom_events`;Database Root Host 未就绪会阻止 candidate generation 发布。</p>
|
|
182
|
+
<pre className="overflow-auto rounded-lg border bg-background p-3 text-xs">{'ai:\n sessions:\n useDatabase: true'}</pre>
|
|
183
|
+
</div>
|
|
184
|
+
<div className="space-y-2 text-sm">
|
|
185
|
+
<p className="font-medium">原子文件 Journal</p>
|
|
186
|
+
<p className="text-muted-foreground">显式设为 false 后使用 `.zhin/workroom-journal`。后端在进程启动时固定,热重载不会切换事实源。</p>
|
|
187
|
+
<pre className="overflow-auto rounded-lg border bg-background p-3 text-xs">{'ai:\n sessions:\n useDatabase: false'}</pre>
|
|
188
|
+
</div>
|
|
189
|
+
</div>
|
|
190
|
+
<div className="flex items-center justify-between gap-3 border-t px-4 py-3 text-xs text-muted-foreground"><span>配置项属于 Agent session storage;切换 Journal 后端必须重启 Host,不能只做热重载。</span><a className="rounded-md border bg-background px-2.5 py-1.5 font-medium text-foreground hover:bg-muted" href="/config">打开配置编辑器</a></div>
|
|
191
|
+
</details>
|
|
192
|
+
|
|
193
|
+
<div className="flex flex-wrap gap-2 items-end">
|
|
194
|
+
<label className="flex flex-col gap-1 text-sm flex-1 min-w-[240px]">
|
|
195
|
+
projectId
|
|
196
|
+
<input
|
|
197
|
+
className="border rounded px-2 py-1 text-sm bg-background"
|
|
198
|
+
value={projectId}
|
|
199
|
+
onChange={(e) => setProjectId(e.target.value)}
|
|
200
|
+
placeholder="project-alpha"
|
|
201
|
+
/>
|
|
202
|
+
</label>
|
|
203
|
+
<button
|
|
204
|
+
type="button"
|
|
205
|
+
className="border rounded px-3 py-1 text-sm hover:bg-muted"
|
|
206
|
+
disabled={loading}
|
|
207
|
+
onClick={() => void loadRuns()}
|
|
208
|
+
>
|
|
209
|
+
查询 Runs
|
|
210
|
+
</button>
|
|
211
|
+
</div>
|
|
212
|
+
|
|
213
|
+
<section className="rounded-xl border p-4 space-y-3">
|
|
214
|
+
<div className="flex flex-wrap items-start justify-between gap-3">
|
|
215
|
+
<div>
|
|
216
|
+
<h2 className="text-sm font-semibold">规划与披露配置</h2>
|
|
217
|
+
<p className="mt-1 text-xs text-muted-foreground">诊断 Catalog、Sponsor、Profile、Planning Policy 与 P12 模型披露 authority,并以当前 generation 的真实能力完成初始化。</p>
|
|
218
|
+
</div>
|
|
219
|
+
<button type="button" className="rounded-md border px-3 py-1.5 text-sm hover:bg-muted" disabled={planningBusy || !projectId.trim()} onClick={() => void loadPlanning()}>
|
|
220
|
+
{planningBusy ? '检查中…' : '检查规划能力'}
|
|
221
|
+
</button>
|
|
222
|
+
</div>
|
|
223
|
+
{planning ? (
|
|
224
|
+
<div className="space-y-3 text-sm">
|
|
225
|
+
<div className={`rounded-lg border p-3 ${planning.ready ? 'border-green-500/40 bg-green-500/5' : 'border-amber-500/40 bg-amber-500/5'}`}>
|
|
226
|
+
<p className="font-medium">{planning.ready ? 'Workroom 编排已就绪,可以提交 /work' : 'Workroom 编排尚未就绪'}</p>
|
|
227
|
+
<p className="mt-1 text-xs text-muted-foreground">principal: {planning.principalId ?? '未绑定'} · Profile: {planning.activeProfile?.revisionId ?? '未激活'} · Policy: {planning.planningPolicyReady ? '已发布' : '未发布'} · Disclosure: {planning.disclosureReady ? '已发布' : '未发布'}</p>
|
|
228
|
+
</div>
|
|
229
|
+
{planning.diagnostics.length > 0 ? <ul className="list-disc space-y-1 pl-5 text-xs text-amber-700 dark:text-amber-300">{planning.diagnostics.map(item => <li key={item}>{item}</li>)}</ul> : null}
|
|
230
|
+
{!planning.principalId || !planning.trustedPackPublisher ? (
|
|
231
|
+
<details className="rounded-lg border bg-muted/20">
|
|
232
|
+
<summary className="cursor-pointer px-3 py-2 text-xs font-medium">首次配置所需 YAML(保存后重启)</summary>
|
|
233
|
+
<pre className="overflow-auto border-t p-3 text-xs">{'http:\n tokens:\n - token: ${WORKROOM_SPONSOR_TOKEN}\n scope: full\n principalId: workroom-admin\nai:\n workroom:\n trustedPackPublishers:\n - workroom-admin'}</pre>
|
|
234
|
+
<p className="border-t px-3 py-2 text-xs text-muted-foreground">重启后用 WORKROOM_SPONSOR_TOKEN 登录 Console,并在 Project sponsors 中加入 workroom-admin。</p>
|
|
235
|
+
</details>
|
|
236
|
+
) : null}
|
|
237
|
+
{!planning.disclosureReady && planning.modelProviderAlias && !planning.disclosureConfigReady ? (
|
|
238
|
+
<details className="rounded-lg border bg-muted/20" open>
|
|
239
|
+
<summary className="cursor-pointer px-3 py-2 text-xs font-medium">模型处理方披露契约(保存后重启)</summary>
|
|
240
|
+
<p className="border-t px-3 py-2 text-xs text-muted-foreground">下面的占位符不能原样保存;必须按模型提供商合同与账号级设置填写。外部 Provider 只有在已禁止训练时才能初始化。</p>
|
|
241
|
+
<pre className="overflow-auto border-t p-3 text-xs">{`ai:\n workroom:\n disclosure:\n modelProviders:\n ${planning.modelProviderAlias}:\n endpoint: REPLACE_WITH_CONTRACTED_ENDPOINT\n processingRegions: [REPLACE_WITH_CONTRACTED_REGION]\n maxConfidentiality: project_internal\n external: true\n noTraining: REPLACE_WITH_PROVIDER_GUARANTEE\n loggingMode: REPLACE_WITH_LOGGING_MODE\n maximumRetentionSeconds: REPLACE_WITH_MAX_RETENTION_SECONDS\n allowsRedisclosure: REPLACE_WITH_PROVIDER_GUARANTEE\n supportsDeletion: REPLACE_WITH_PROVIDER_GUARANTEE`}</pre>
|
|
242
|
+
<p className="border-t px-3 py-2 text-xs text-muted-foreground">OpenRouter 还需在其 Privacy/Guardrail 中启用 ZDR,并禁止 data collection;YAML 声明不会替你修改第三方账号策略。</p>
|
|
243
|
+
</details>
|
|
244
|
+
) : null}
|
|
245
|
+
{!planning.ready && planning.catalogReady && planning.trustedPackPublisher && planning.projectSponsor ? (
|
|
246
|
+
<div className="flex flex-wrap items-center justify-between gap-3 rounded-lg border p-3">
|
|
247
|
+
<p className="text-xs text-muted-foreground">{planning.activeProfile
|
|
248
|
+
? planning.planningPolicyReady
|
|
249
|
+
? '为当前 Project 发布加密的 P12 模型披露 authority。'
|
|
250
|
+
: '为当前 active Profile 发布默认 Planning Policy 与 P12 模型披露 authority。'
|
|
251
|
+
: `将当前 ${planning.availableAgents.length} 个 Agent、${planning.availableTools.length} 个 Tool、${planning.availableSkills.length} 个 Skill 固定进首个 Profile,并发布 Planning Policy 与 P12 披露 authority。`}</p>
|
|
252
|
+
<button type="button" className="rounded-md border bg-primary px-3 py-1.5 text-sm text-primary-foreground" disabled={planningBusy} onClick={() => void bootstrapPlanning()}>
|
|
253
|
+
{planning.planningPolicyReady ? '初始化披露能力' : planning.activeProfile ? '补齐编排能力' : '初始化编排能力'}
|
|
254
|
+
</button>
|
|
255
|
+
</div>
|
|
256
|
+
) : null}
|
|
257
|
+
</div>
|
|
258
|
+
) : null}
|
|
259
|
+
</section>
|
|
260
|
+
|
|
261
|
+
{error ? <p className="text-sm text-red-600">{error}</p> : null}
|
|
262
|
+
|
|
263
|
+
<div className="grid gap-4 lg:grid-cols-[minmax(16rem,0.75fr)_minmax(0,1.5fr)]">
|
|
264
|
+
<section className="rounded-xl border p-3 space-y-2">
|
|
265
|
+
<h2 className="text-sm font-medium">Runs ({runs.length})</h2>
|
|
266
|
+
{runs.length === 0 ? (
|
|
267
|
+
<p className="text-sm text-muted-foreground">无数据</p>
|
|
268
|
+
) : (
|
|
269
|
+
<ul className="space-y-2 text-sm">
|
|
270
|
+
{runs.map((item) => (
|
|
271
|
+
<li key={item.runId} className={`rounded-lg border p-2 transition-colors ${selected?.runId === item.runId ? 'border-primary/40 bg-primary/5' : 'hover:bg-muted/30'}`}>
|
|
272
|
+
<button
|
|
273
|
+
type="button"
|
|
274
|
+
className="text-left w-full"
|
|
275
|
+
onClick={() => void loadRunDetail(item.runId, item.projectId)}
|
|
276
|
+
>
|
|
277
|
+
<div className="font-mono text-xs">{item.runId}</div>
|
|
278
|
+
<div className="mt-1 font-medium">{item.title || '(无标题)'}</div>
|
|
279
|
+
<div className="text-muted-foreground">
|
|
280
|
+
{item.status}
|
|
281
|
+
{' · '}
|
|
282
|
+
{Object.keys(item.tasks).length}
|
|
283
|
+
{' tasks'}
|
|
284
|
+
</div>
|
|
285
|
+
</button>
|
|
286
|
+
</li>
|
|
287
|
+
))}
|
|
288
|
+
</ul>
|
|
289
|
+
)}
|
|
290
|
+
</section>
|
|
291
|
+
|
|
292
|
+
<section className="rounded-xl border p-3 space-y-3">
|
|
293
|
+
<h2 className="text-sm font-medium">Run 详情</h2>
|
|
294
|
+
{!selected ? (
|
|
295
|
+
<p className="text-sm text-muted-foreground">选择左侧 Run</p>
|
|
296
|
+
) : (
|
|
297
|
+
<>
|
|
298
|
+
<div className="flex flex-wrap items-start justify-between gap-3 rounded-lg bg-muted/25 p-3 text-sm">
|
|
299
|
+
<div><div className="font-medium">{selected.title}</div><code className="text-xs text-muted-foreground">{selected.runId}</code></div>
|
|
300
|
+
<span className={`rounded-full border px-2 py-0.5 text-xs ${statusTone(selected.status)}`}>{selected.status}</span>
|
|
301
|
+
</div>
|
|
302
|
+
|
|
303
|
+
<dl className="grid grid-cols-2 overflow-hidden rounded-lg border text-sm sm:grid-cols-4">
|
|
304
|
+
<div className="p-2.5"><dt className="text-xs text-muted-foreground">Sequence</dt><dd className="mt-0.5 font-medium tabular-nums">{selected.sequence}</dd></div>
|
|
305
|
+
<div className="border-l p-2.5"><dt className="text-xs text-muted-foreground">Tasks</dt><dd className="mt-0.5 font-medium tabular-nums">{Object.keys(selected.tasks).length}</dd></div>
|
|
306
|
+
<div className="border-t p-2.5 sm:border-l sm:border-t-0"><dt className="text-xs text-muted-foreground">Assignments</dt><dd className="mt-0.5 font-medium tabular-nums">{Object.keys(selected.assignments ?? {}).length}</dd></div>
|
|
307
|
+
<div className="border-l border-t p-2.5 sm:border-t-0"><dt className="text-xs text-muted-foreground">Acceptance waits</dt><dd className="mt-0.5 font-medium tabular-nums">{Object.keys(selected.reviewerAssignments ?? {}).length + Object.keys(selected.sponsorGates ?? {}).length}</dd></div>
|
|
308
|
+
</dl>
|
|
309
|
+
|
|
310
|
+
<div className="flex gap-1 rounded-lg bg-muted/35 p-1" role="tablist" aria-label="Workroom 详情视图">
|
|
311
|
+
{(['tasks', 'assignments', 'acceptance'] as const).map((view) => <button key={view} type="button" role="tab" aria-selected={detailView === view} className={`flex-1 rounded-md px-2 py-1.5 text-xs font-medium ${detailView === view ? 'bg-background shadow-sm' : 'text-muted-foreground hover:text-foreground'}`} onClick={() => setDetailView(view)}>{view === 'tasks' ? '任务' : view === 'assignments' ? '执行分配' : '验收门'}</button>)}
|
|
312
|
+
</div>
|
|
313
|
+
|
|
314
|
+
{detailView === 'tasks' && <ul className="space-y-2 text-sm">
|
|
315
|
+
{Object.values(selected.tasks).map((task) => (
|
|
316
|
+
<li key={task.key} className="rounded-lg border p-3">
|
|
317
|
+
<div className="flex items-start justify-between gap-2"><div><code className="text-xs text-muted-foreground">{task.key}</code><div className="font-medium">{task.title}</div></div><span className={`rounded-full border px-2 py-0.5 text-[11px] ${statusTone(task.status)}`}>{task.status}</span></div>
|
|
318
|
+
<div className="mt-2 flex flex-wrap gap-x-4 gap-y-1 text-xs text-muted-foreground"><span>rev {task.revision}</span><span>attempt {task.attempt}/{task.maxAttempts}</span>{task.currentAssignmentId ? <span>assignment {task.currentAssignmentId}</span> : null}</div>
|
|
319
|
+
{task.blockers?.map((blocker) => <div key={blocker.id} className="mt-2 rounded-md border border-amber-500/25 bg-amber-500/5 p-2 text-xs"><strong>{blocker.kind}</strong> · {blocker.reason}<div className="text-muted-foreground">owner {blocker.owner}</div></div>)}
|
|
320
|
+
{task.reportRef ? <div className="mt-2 break-words rounded bg-muted/35 p-2 font-mono text-xs">report: {task.reportRef}</div> : null}
|
|
321
|
+
{task.terminalReason ? <div className="mt-2 whitespace-pre-wrap text-xs text-red-600">{task.terminalReason}</div> : null}
|
|
322
|
+
</li>
|
|
323
|
+
))}
|
|
324
|
+
</ul>}
|
|
325
|
+
|
|
326
|
+
{detailView === 'assignments' && <ul className="space-y-2 text-sm">
|
|
327
|
+
{Object.values(selected.assignments ?? {}).length === 0 ? <li className="py-8 text-center text-muted-foreground">暂无 Assignment</li> : Object.values(selected.assignments ?? {}).map((assignment) => (
|
|
328
|
+
<li key={assignment.id} className="rounded-lg border p-3">
|
|
329
|
+
<div className="flex items-start justify-between gap-2"><div><code className="text-xs text-muted-foreground">{assignment.id}</code><div className="font-medium">{assignment.taskKey} · {assignment.role}</div></div><span className={`rounded-full border px-2 py-0.5 text-[11px] ${statusTone(assignment.status)}`}>{assignment.status}</span></div>
|
|
330
|
+
<div className="mt-2 grid gap-1 text-xs text-muted-foreground sm:grid-cols-3"><span>owner {assignment.owner}</span><span>attempt {assignment.attempt}</span><span>lease {new Date(assignment.leaseExpiresAt).toLocaleString()}</span></div>
|
|
331
|
+
{assignment.latestProgress ? <div className="mt-2 rounded-md bg-muted/35 p-2 text-xs"><strong>Progress</strong> · {assignment.latestProgress.summary}{assignment.latestProgress.totalUnits ? <div className="mt-1 h-1.5 overflow-hidden rounded-full bg-muted"><i className="block h-full bg-primary" style={{ width: `${Math.min(100, Math.max(0, ((assignment.latestProgress.completedUnits ?? 0) / assignment.latestProgress.totalUnits) * 100))}%` }} /></div> : null}</div> : null}
|
|
332
|
+
</li>
|
|
333
|
+
))}
|
|
334
|
+
</ul>}
|
|
335
|
+
|
|
336
|
+
{detailView === 'acceptance' && <ul className="space-y-2 text-sm">
|
|
337
|
+
{[...Object.values(selected.reviewerAssignments ?? {}), ...Object.values(selected.sponsorGates ?? {})].length === 0 ? <li className="py-8 text-center text-muted-foreground">当前没有 Reviewer / Sponsor 等待项</li> : [...Object.values(selected.reviewerAssignments ?? {}), ...Object.values(selected.sponsorGates ?? {})].map((wait) => (
|
|
338
|
+
<li key={wait.id} className="rounded-lg border p-3"><div className="flex items-center justify-between gap-2"><div><div className="font-medium">{wait.taskKey} · {wait.route}</div><code className="text-xs text-muted-foreground">{wait.id}</code></div><span className={`rounded-full border px-2 py-0.5 text-[11px] ${statusTone(wait.status)}`}>{wait.status}</span></div><div className="mt-2 flex flex-wrap gap-3 text-xs text-muted-foreground"><span>risk {wait.riskTier}</span><span>owner {wait.owner}</span><span>deadline {new Date(wait.deadline).toLocaleString()}</span></div></li>
|
|
339
|
+
))}
|
|
340
|
+
</ul>}
|
|
341
|
+
|
|
342
|
+
</>
|
|
343
|
+
)}
|
|
344
|
+
</section>
|
|
345
|
+
</div>
|
|
346
|
+
</div>
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function statusTone(status: string): string {
|
|
351
|
+
if (/completed|accepted|approved|passed|execution_completed/u.test(status)) return 'border-emerald-500/25 bg-emerald-500/5 text-emerald-700 dark:text-emerald-300';
|
|
352
|
+
if (/failed|lost|rejected|expired/u.test(status)) return 'border-red-500/25 bg-red-500/5 text-red-700 dark:text-red-300';
|
|
353
|
+
if (/blocked|replan|rework|cancell/u.test(status)) return 'border-amber-500/25 bg-amber-500/5 text-amber-700 dark:text-amber-300';
|
|
354
|
+
return 'border-sky-500/25 bg-sky-500/5 text-sky-700 dark:text-sky-300';
|
|
355
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { registerWorkroomConsole } from './registerWorkroomConsole.js';
|
|
3
|
+
|
|
4
|
+
describe('registerWorkroomConsole', () => {
|
|
5
|
+
it('registers Workroom route and tool', () => {
|
|
6
|
+
const addRoute = vi.fn();
|
|
7
|
+
const addTool = vi.fn();
|
|
8
|
+
const React = { createElement: vi.fn((type) => ({ type })) };
|
|
9
|
+
|
|
10
|
+
registerWorkroomConsole({
|
|
11
|
+
React: React as never,
|
|
12
|
+
addRoute,
|
|
13
|
+
addTool,
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
expect(addRoute).toHaveBeenCalledWith(expect.objectContaining({
|
|
17
|
+
path: '/console/workroom',
|
|
18
|
+
name: 'Workroom',
|
|
19
|
+
}));
|
|
20
|
+
expect(addTool).toHaveBeenCalledWith(expect.objectContaining({
|
|
21
|
+
id: 'workroom',
|
|
22
|
+
path: '/console/workroom',
|
|
23
|
+
}));
|
|
24
|
+
});
|
|
25
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { PluginRegisterHostApi } from '@zhin.js/contract';
|
|
2
|
+
import WorkroomRunsPage from './WorkroomRunsPage.js';
|
|
3
|
+
|
|
4
|
+
/** Read-only Workroom Run/Task/Assignment projection. */
|
|
5
|
+
export function registerWorkroomConsole(api: PluginRegisterHostApi): void {
|
|
6
|
+
api.addRoute({
|
|
7
|
+
path: '/console/workroom',
|
|
8
|
+
name: 'Workroom',
|
|
9
|
+
element: api.React.createElement(WorkroomRunsPage),
|
|
10
|
+
meta: { order: 40, group: 'Agent' },
|
|
11
|
+
});
|
|
12
|
+
api.addTool({
|
|
13
|
+
id: 'workroom',
|
|
14
|
+
name: 'Workroom',
|
|
15
|
+
path: '/console/workroom',
|
|
16
|
+
});
|
|
17
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { foldAgentStreamNdjson, iterateAgentStreamNdjson, type AgentStreamEvent, type AgentStreamReduceState, type ContinueAgentSessionResponse, type FoldAgentStreamOptions, type StartAgentSessionResponse } from "@zhin.js/contract";
|
|
2
|
+
export type AgentStreamClientOptions = {
|
|
3
|
+
fetch?: typeof fetch;
|
|
4
|
+
};
|
|
5
|
+
/** POST /zhin/v1/session — start a Host agent session turn. */
|
|
6
|
+
export declare function startAgentSession(message: string, options?: AgentStreamClientOptions): Promise<StartAgentSessionResponse>;
|
|
7
|
+
/** POST /zhin/v1/session/:sessionId — continue with continuationToken + message. */
|
|
8
|
+
export declare function continueAgentSession(sessionId: string, continuationToken: string, message: string, options?: AgentStreamClientOptions): Promise<ContinueAgentSessionResponse>;
|
|
9
|
+
export type SubscribeAgentStreamOptions = FoldAgentStreamOptions & {
|
|
10
|
+
startIndex?: number;
|
|
11
|
+
fetch?: typeof fetch;
|
|
12
|
+
onEvent?: (event: AgentStreamEvent, state: AgentStreamReduceState) => void;
|
|
13
|
+
};
|
|
14
|
+
/** GET /zhin/v1/session/:sessionId/stream — consume NDJSON until the stream closes. */
|
|
15
|
+
export declare function subscribeAgentStream(sessionId: string, options?: SubscribeAgentStreamOptions): Promise<AgentStreamReduceState>;
|
|
16
|
+
export { iterateAgentStreamNdjson, foldAgentStreamNdjson };
|
|
17
|
+
//# sourceMappingURL=agent-stream.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-stream.d.ts","sourceRoot":"","sources":["../client/agent-stream.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,qBAAqB,EACrB,wBAAwB,EACxB,KAAK,gBAAgB,EACrB,KAAK,sBAAsB,EAC3B,KAAK,4BAA4B,EACjC,KAAK,sBAAsB,EAC3B,KAAK,yBAAyB,EAC/B,MAAM,mBAAmB,CAAC;AAG3B,MAAM,MAAM,wBAAwB,GAAG;IACrC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACtB,CAAC;AAkCF,+DAA+D;AAC/D,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,wBAAwB,GACjC,OAAO,CAAC,yBAAyB,CAAC,CAQpC;AAED,oFAAoF;AACpF,wBAAsB,oBAAoB,CACxC,SAAS,EAAE,MAAM,EACjB,iBAAiB,EAAE,MAAM,EACzB,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,wBAAwB,GACjC,OAAO,CAAC,4BAA4B,CAAC,CAYvC;AAED,MAAM,MAAM,2BAA2B,GAAG,sBAAsB,GAAG;IACjE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,EAAE,KAAK,EAAE,sBAAsB,KAAK,IAAI,CAAC;CAC5E,CAAC;AAEF,uFAAuF;AACvF,wBAAsB,oBAAoB,CACxC,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,2BAA2B,GACpC,OAAO,CAAC,sBAAsB,CAAC,CAwBjC;AAED,OAAO,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,CAAC"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { AGENT_STREAM_MEDIA_TYPE, ZHIN_AGENT_SESSION_API_PREFIX, ZHIN_SESSION_ID_HEADER, foldAgentStreamNdjson, iterateAgentStreamNdjson, } from "@zhin.js/contract";
|
|
2
|
+
import { getToken, resolveApiUrl } from "./console-utils/remoteApi.js";
|
|
3
|
+
function resolveFetch(options) {
|
|
4
|
+
const impl = options?.fetch ?? globalThis.fetch;
|
|
5
|
+
if (!impl)
|
|
6
|
+
throw new Error("fetch is not available");
|
|
7
|
+
return impl;
|
|
8
|
+
}
|
|
9
|
+
async function authedFetch(path, init, fetchImpl) {
|
|
10
|
+
const token = getToken();
|
|
11
|
+
const headers = new Headers(init.headers);
|
|
12
|
+
if (token)
|
|
13
|
+
headers.set("Authorization", `Bearer ${token}`);
|
|
14
|
+
const url = path.startsWith("/") ? resolveApiUrl(path) : path;
|
|
15
|
+
const res = await fetchImpl(url, { ...init, headers });
|
|
16
|
+
if (res.status === 401 && typeof localStorage !== "undefined") {
|
|
17
|
+
sessionStorage.removeItem("zhin_api_token");
|
|
18
|
+
localStorage.removeItem("zhin_api_token");
|
|
19
|
+
window.dispatchEvent(new CustomEvent("zhin:auth-required"));
|
|
20
|
+
}
|
|
21
|
+
return res;
|
|
22
|
+
}
|
|
23
|
+
async function readJson(res) {
|
|
24
|
+
if (!res.ok) {
|
|
25
|
+
const err = await res.json().catch(() => ({}));
|
|
26
|
+
throw new Error(err.error ?? `HTTP ${res.status}`);
|
|
27
|
+
}
|
|
28
|
+
return res.json();
|
|
29
|
+
}
|
|
30
|
+
/** POST /zhin/v1/session — start a Host agent session turn. */
|
|
31
|
+
export async function startAgentSession(message, options) {
|
|
32
|
+
const fetchImpl = resolveFetch(options);
|
|
33
|
+
const res = await authedFetch(`${ZHIN_AGENT_SESSION_API_PREFIX}/session`, {
|
|
34
|
+
method: "POST",
|
|
35
|
+
headers: { "Content-Type": "application/json" },
|
|
36
|
+
body: JSON.stringify({ message }),
|
|
37
|
+
}, fetchImpl);
|
|
38
|
+
return readJson(res);
|
|
39
|
+
}
|
|
40
|
+
/** POST /zhin/v1/session/:sessionId — continue with continuationToken + message. */
|
|
41
|
+
export async function continueAgentSession(sessionId, continuationToken, message, options) {
|
|
42
|
+
const fetchImpl = resolveFetch(options);
|
|
43
|
+
const res = await authedFetch(`${ZHIN_AGENT_SESSION_API_PREFIX}/session/${encodeURIComponent(sessionId)}`, {
|
|
44
|
+
method: "POST",
|
|
45
|
+
headers: { "Content-Type": "application/json" },
|
|
46
|
+
body: JSON.stringify({ continuationToken, message }),
|
|
47
|
+
}, fetchImpl);
|
|
48
|
+
return readJson(res);
|
|
49
|
+
}
|
|
50
|
+
/** GET /zhin/v1/session/:sessionId/stream — consume NDJSON until the stream closes. */
|
|
51
|
+
export async function subscribeAgentStream(sessionId, options) {
|
|
52
|
+
const fetchImpl = resolveFetch(options);
|
|
53
|
+
const startIndex = options?.startIndex ?? 0;
|
|
54
|
+
const query = startIndex > 0 ? `?startIndex=${startIndex}` : "";
|
|
55
|
+
const res = await authedFetch(`${ZHIN_AGENT_SESSION_API_PREFIX}/session/${encodeURIComponent(sessionId)}/stream${query}`, {
|
|
56
|
+
method: "GET",
|
|
57
|
+
headers: { Accept: AGENT_STREAM_MEDIA_TYPE },
|
|
58
|
+
}, fetchImpl);
|
|
59
|
+
if (!res.ok) {
|
|
60
|
+
const err = await res.json().catch(() => ({}));
|
|
61
|
+
throw new Error(err.error ?? `HTTP ${res.status}`);
|
|
62
|
+
}
|
|
63
|
+
const headerSessionId = res.headers.get(ZHIN_SESSION_ID_HEADER);
|
|
64
|
+
if (headerSessionId && headerSessionId !== sessionId) {
|
|
65
|
+
throw new Error(`stream session id mismatch: ${headerSessionId}`);
|
|
66
|
+
}
|
|
67
|
+
if (!res.body) {
|
|
68
|
+
throw new Error("stream response has no body");
|
|
69
|
+
}
|
|
70
|
+
return foldAgentStreamNdjson(res.body, options);
|
|
71
|
+
}
|
|
72
|
+
export { iterateAgentStreamNdjson, foldAgentStreamNdjson };
|
|
73
|
+
//# sourceMappingURL=agent-stream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-stream.js","sourceRoot":"","sources":["../client/agent-stream.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,EACvB,6BAA6B,EAC7B,sBAAsB,EACtB,qBAAqB,EACrB,wBAAwB,GAMzB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAMvE,SAAS,YAAY,CAAC,OAAkC;IACtD,MAAM,IAAI,GAAG,OAAO,EAAE,KAAK,IAAI,UAAU,CAAC,KAAK,CAAC;IAChD,IAAI,CAAC,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;IACrD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,KAAK,UAAU,WAAW,CACxB,IAAY,EACZ,IAAiB,EACjB,SAAuB;IAEvB,MAAM,KAAK,GAAG,QAAQ,EAAE,CAAC;IACzB,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC1C,IAAI,KAAK;QAAE,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,UAAU,KAAK,EAAE,CAAC,CAAC;IAC3D,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC9D,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,GAAG,EAAE,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;IACvD,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,IAAI,OAAO,YAAY,KAAK,WAAW,EAAE,CAAC;QAC9D,cAAc,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC;QAC5C,YAAY,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC;QAC1C,MAAM,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,oBAAoB,CAAC,CAAC,CAAC;IAC9D,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,KAAK,UAAU,QAAQ,CAAI,GAAa;IACtC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAuB,CAAC;QACrE,MAAM,IAAI,KAAK,CAAC,GAAG,CAAC,KAAK,IAAI,QAAQ,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;IACrD,CAAC;IACD,OAAO,GAAG,CAAC,IAAI,EAAgB,CAAC;AAClC,CAAC;AAED,+DAA+D;AAC/D,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,OAAe,EACf,OAAkC;IAElC,MAAM,SAAS,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;IACxC,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC,GAAG,6BAA6B,UAAU,EAAE;QACxE,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,CAAC;KAClC,EAAE,SAAS,CAAC,CAAC;IACd,OAAO,QAAQ,CAA4B,GAAG,CAAC,CAAC;AAClD,CAAC;AAED,oFAAoF;AACpF,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,SAAiB,EACjB,iBAAyB,EACzB,OAAe,EACf,OAAkC;IAElC,MAAM,SAAS,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;IACxC,MAAM,GAAG,GAAG,MAAM,WAAW,CAC3B,GAAG,6BAA6B,YAAY,kBAAkB,CAAC,SAAS,CAAC,EAAE,EAC3E;QACE,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,iBAAiB,EAAE,OAAO,EAAE,CAAC;KACrD,EACD,SAAS,CACV,CAAC;IACF,OAAO,QAAQ,CAA+B,GAAG,CAAC,CAAC;AACrD,CAAC;AAQD,uFAAuF;AACvF,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,SAAiB,EACjB,OAAqC;IAErC,MAAM,SAAS,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;IACxC,MAAM,UAAU,GAAG,OAAO,EAAE,UAAU,IAAI,CAAC,CAAC;IAC5C,MAAM,KAAK,GAAG,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,eAAe,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAChE,MAAM,GAAG,GAAG,MAAM,WAAW,CAC3B,GAAG,6BAA6B,YAAY,kBAAkB,CAAC,SAAS,CAAC,UAAU,KAAK,EAAE,EAC1F;QACE,MAAM,EAAE,KAAK;QACb,OAAO,EAAE,EAAE,MAAM,EAAE,uBAAuB,EAAE;KAC7C,EACD,SAAS,CACV,CAAC;IACF,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAuB,CAAC;QACrE,MAAM,IAAI,KAAK,CAAC,GAAG,CAAC,KAAK,IAAI,QAAQ,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;IACrD,CAAC;IACD,MAAM,eAAe,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC;IAChE,IAAI,eAAe,IAAI,eAAe,KAAK,SAAS,EAAE,CAAC;QACrD,MAAM,IAAI,KAAK,CAAC,+BAA+B,eAAe,EAAE,CAAC,CAAC;IACpE,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;IACjD,CAAC;IACD,OAAO,qBAAqB,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AAClD,CAAC;AAED,OAAO,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,CAAC"}
|
package/dist/app.d.ts
CHANGED
|
@@ -1,26 +1,7 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
element: React.ReactNode;
|
|
6
|
-
parent?: string | null;
|
|
7
|
-
icon?: React.ReactNode | string;
|
|
8
|
-
requiredPermissions?: string[];
|
|
9
|
-
requiredRoles?: string[];
|
|
10
|
-
meta?: {
|
|
11
|
-
hideInMenu?: boolean;
|
|
12
|
-
order?: number;
|
|
13
|
-
group?: string;
|
|
14
|
-
fullWidth?: boolean;
|
|
15
|
-
};
|
|
16
|
-
};
|
|
17
|
-
export type AddToolInput = {
|
|
18
|
-
id?: string;
|
|
19
|
-
name: string;
|
|
20
|
-
icon?: React.ReactNode;
|
|
21
|
-
parent?: string | null;
|
|
22
|
-
path?: string;
|
|
23
|
-
};
|
|
2
|
+
import type { PluginAddRouteInput, PluginAddToolInput } from '@zhin.js/contract';
|
|
3
|
+
export type AddRouteInput = PluginAddRouteInput;
|
|
4
|
+
export type AddToolInput = PluginAddToolInput;
|
|
24
5
|
export type ConsoleRouteRecord = {
|
|
25
6
|
path: string;
|
|
26
7
|
name: string;
|
|
@@ -49,18 +30,18 @@ export type RouteTreeNode = ConsoleRouteRecord & {
|
|
|
49
30
|
export type ToolTreeNode = InternalToolRecord & {
|
|
50
31
|
children: ToolTreeNode[];
|
|
51
32
|
};
|
|
52
|
-
type SidebarRenderer = (options: {
|
|
33
|
+
export type SidebarRenderer = (options: {
|
|
53
34
|
routes: RouteTreeNode[];
|
|
54
35
|
}) => React.ReactNode;
|
|
55
|
-
type ToolbarRenderer = (options: {
|
|
36
|
+
export type ToolbarRenderer = (options: {
|
|
56
37
|
tools: ToolTreeNode[];
|
|
57
38
|
}) => React.ReactNode;
|
|
58
39
|
export type ConsoleRouteRenderer = (route: ConsoleRouteRecord) => React.ReactNode;
|
|
59
|
-
|
|
60
|
-
declare
|
|
61
|
-
|
|
62
|
-
subscribe:
|
|
63
|
-
getVersion:
|
|
40
|
+
/** Owner-scoped registry for one mounted Remote Console application. */
|
|
41
|
+
export declare class ConsoleApp {
|
|
42
|
+
#private;
|
|
43
|
+
readonly subscribe: (listener: () => void) => (() => void);
|
|
44
|
+
readonly getVersion: () => number;
|
|
64
45
|
defineSidebar(render: SidebarRenderer): void;
|
|
65
46
|
defineToolbar(render: ToolbarRenderer): void;
|
|
66
47
|
defineRouter(render: ConsoleRouteRenderer): void;
|
|
@@ -69,27 +50,11 @@ declare function createConsoleApp(): {
|
|
|
69
50
|
addTool(input: AddToolInput): string;
|
|
70
51
|
getRouteTree(): RouteTreeNode[];
|
|
71
52
|
getToolTree(): ToolTreeNode[];
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
export declare
|
|
79
|
-
subscribe: typeof subscribe;
|
|
80
|
-
getVersion: typeof getVersion;
|
|
81
|
-
defineSidebar(render: SidebarRenderer): void;
|
|
82
|
-
defineToolbar(render: ToolbarRenderer): void;
|
|
83
|
-
defineRouter(render: ConsoleRouteRenderer): void;
|
|
84
|
-
addRoute(input: AddRouteInput): void;
|
|
85
|
-
removeRoute(path: string): void;
|
|
86
|
-
addTool(input: AddToolInput): string;
|
|
87
|
-
getRouteTree(): RouteTreeNode[];
|
|
88
|
-
getToolTree(): ToolTreeNode[];
|
|
89
|
-
_getRoutes(): readonly ConsoleRouteRecord[];
|
|
90
|
-
_getSidebarRenderer(): SidebarRenderer | null;
|
|
91
|
-
_getToolbarRenderer(): ToolbarRenderer | null;
|
|
92
|
-
_renderRouteElement(route: ConsoleRouteRecord): React.ReactNode;
|
|
93
|
-
};
|
|
94
|
-
export {};
|
|
53
|
+
getRoutes(): readonly ConsoleRouteRecord[];
|
|
54
|
+
getSidebarRenderer(): SidebarRenderer | null;
|
|
55
|
+
getToolbarRenderer(): ToolbarRenderer | null;
|
|
56
|
+
renderRouteElement(route: ConsoleRouteRecord): React.ReactNode;
|
|
57
|
+
dispose(): void;
|
|
58
|
+
}
|
|
59
|
+
export declare function createConsoleApp(): ConsoleApp;
|
|
95
60
|
//# sourceMappingURL=app.d.ts.map
|
package/dist/app.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../client/app.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../client/app.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,EACV,mBAAmB,EACnB,kBAAkB,EACnB,MAAM,mBAAmB,CAAC;AAE3B,MAAM,MAAM,aAAa,GAAG,mBAAmB,CAAC;AAChD,MAAM,MAAM,YAAY,GAAG,kBAAkB,CAAC;AAE9C,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,GAAG,MAAM,CAAC;IAChC,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC/B,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,IAAI,CAAC,EAAE;QACL,UAAU,CAAC,EAAE,OAAO,CAAC;QACrB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,SAAS,CAAC,EAAE,OAAO,CAAC;KACrB,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG,kBAAkB,GAAG;IAAE,QAAQ,EAAE,aAAa,EAAE,CAAA;CAAE,CAAC;AAC/E,MAAM,MAAM,YAAY,GAAG,kBAAkB,GAAG;IAAE,QAAQ,EAAE,YAAY,EAAE,CAAA;CAAE,CAAC;AAE7E,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,EAAE;IAAE,MAAM,EAAE,aAAa,EAAE,CAAA;CAAE,KAAK,KAAK,CAAC,SAAS,CAAC;AACxF,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,EAAE;IAAE,KAAK,EAAE,YAAY,EAAE,CAAA;CAAE,KAAK,KAAK,CAAC,SAAS,CAAC;AACtF,MAAM,MAAM,oBAAoB,GAAG,CAAC,KAAK,EAAE,kBAAkB,KAAK,KAAK,CAAC,SAAS,CAAC;AAsDlF,wEAAwE;AACxE,qBAAa,UAAU;;IAUrB,QAAQ,CAAC,SAAS,GAAI,UAAU,MAAM,IAAI,KAAG,CAAC,MAAM,IAAI,CAAC,CAIvD;IAEF,QAAQ,CAAC,UAAU,QAAO,MAAM,CAAkB;IAElD,aAAa,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI;IAM5C,aAAa,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI;IAM5C,YAAY,CAAC,MAAM,EAAE,oBAAoB,GAAG,IAAI;IAMhD,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI;IAiBpC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAM/B,OAAO,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM;IAoBpC,YAAY,IAAI,aAAa,EAAE;IAI/B,WAAW,IAAI,YAAY,EAAE;IAI7B,SAAS,IAAI,SAAS,kBAAkB,EAAE;IAI1C,kBAAkB,IAAI,eAAe,GAAG,IAAI;IAI5C,kBAAkB,IAAI,eAAe,GAAG,IAAI;IAI5C,kBAAkB,CAAC,KAAK,EAAE,kBAAkB,GAAG,KAAK,CAAC,SAAS;IAI9D,OAAO,IAAI,IAAI;CAmBhB;AAED,wBAAgB,gBAAgB,IAAI,UAAU,CAE7C"}
|