@coze-arch/cli 0.0.19-beta.1 → 0.0.20
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/lib/__templates__/nuxt-vue/app/pages/index.vue +6 -0
- package/lib/__templates__/nuxt-vue/nuxt.config.ts +2 -2
- package/lib/__templates__/pi-agent/AGENTS.md +7 -2
- package/lib/__templates__/pi-agent/README.md +2 -0
- package/lib/__templates__/pi-agent/docs/project-overview.md +9 -15
- package/lib/__templates__/pi-agent/docs/user/getting-started.md +3 -4
- package/lib/__templates__/pi-agent/pi-resources/skills/coze-asr/SKILL.md +4 -10
- package/lib/__templates__/pi-agent/pi-resources/skills/coze-image-gen/SKILL.md +6 -18
- package/lib/__templates__/pi-agent/pi-resources/skills/coze-tts/SKILL.md +9 -37
- package/lib/__templates__/pi-agent/pi-resources/skills/coze-video-gen/SKILL.md +17 -30
- package/lib/__templates__/pi-agent/src/config.ts +60 -19
- package/lib/__templates__/pi-agent/src/core.ts +1 -0
- package/lib/__templates__/pi-agent/src/dashboard/index.ts +39 -4
- package/lib/__templates__/pi-agent/src/dashboard/server.ts +0 -12
- package/lib/__templates__/pi-agent/src/dashboard/web/src/components/app-layout.tsx +1 -15
- package/lib/__templates__/pi-agent/src/dashboard/web/src/main.tsx +0 -6
- package/lib/__templates__/pi-agent/src/dashboard/web/src/pages/chat-page.tsx +0 -11
- package/lib/__templates__/pi-agent/src/dashboard/web/src/pages/overview-page.tsx +268 -72
- package/lib/__templates__/pi-agent/src/dashboard/web/src/styles.css +0 -91
- package/lib/__templates__/pi-agent/tests/config.test.ts +63 -1
- package/lib/__templates__/templates.json +24 -24
- package/lib/cli.js +1 -1
- package/package.json +1 -1
- package/lib/__templates__/pi-agent/pi-resources/skills/coze-asr/scripts/asr.mjs +0 -9
- package/lib/__templates__/pi-agent/pi-resources/skills/coze-image-gen/scripts/gen.mjs +0 -9
- package/lib/__templates__/pi-agent/pi-resources/skills/coze-tts/scripts/tts.mjs +0 -9
- package/lib/__templates__/pi-agent/pi-resources/skills/coze-video-gen/scripts/gen.mjs +0 -9
- package/lib/__templates__/pi-agent/src/dashboard/api/docs.ts +0 -204
- package/lib/__templates__/pi-agent/src/dashboard/web/src/pages/channels-page.tsx +0 -188
- package/lib/__templates__/pi-agent/src/dashboard/web/src/pages/docs-page.tsx +0 -65
- package/lib/__templates__/pi-agent/src/dashboard/web/src/pages/models-page.tsx +0 -122
- package/lib/__templates__/pi-agent/tests/dashboard-docs-api.test.ts +0 -125
|
@@ -8,7 +8,6 @@ import { useLocalStorageState } from "../hooks/use-local-storage-state";
|
|
|
8
8
|
import { Streamdown } from "streamdown";
|
|
9
9
|
import { code } from "@streamdown/code";
|
|
10
10
|
import { cn } from "../utils";
|
|
11
|
-
import { Link } from "react-router-dom";
|
|
12
11
|
|
|
13
12
|
type ChatUiMessage = {
|
|
14
13
|
role: "user" | "assistant";
|
|
@@ -424,16 +423,6 @@ export function ChatPage() {
|
|
|
424
423
|
</Button>
|
|
425
424
|
</div>
|
|
426
425
|
</div>
|
|
427
|
-
<div className="px-1 text-xs text-muted-foreground">
|
|
428
|
-
如需接入飞书机器人,见
|
|
429
|
-
<Link
|
|
430
|
-
to="/docs"
|
|
431
|
-
className="ml-1 underline decoration-border underline-offset-4 transition-colors hover:text-primary"
|
|
432
|
-
>
|
|
433
|
-
飞书接入文档
|
|
434
|
-
</Link>
|
|
435
|
-
。
|
|
436
|
-
</div>
|
|
437
426
|
</div>
|
|
438
427
|
</section>
|
|
439
428
|
);
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { Alert, AlertDescription, AlertTitle } from "../components/ui/alert";
|
|
3
|
-
import {
|
|
3
|
+
import { Button } from "../components/ui/button";
|
|
4
4
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../components/ui/card";
|
|
5
|
+
import { Input } from "../components/ui/input";
|
|
6
|
+
import { Label } from "../components/ui/label";
|
|
7
|
+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../components/ui/select";
|
|
5
8
|
import { useFetch } from "../hooks/use-fetch";
|
|
9
|
+
import { toast } from "sonner";
|
|
6
10
|
|
|
7
11
|
type Overview = {
|
|
8
12
|
appName: string;
|
|
@@ -14,6 +18,41 @@ type Overview = {
|
|
|
14
18
|
enabledChannels: Array<{ id: string; enabled: boolean }>;
|
|
15
19
|
};
|
|
16
20
|
|
|
21
|
+
type Channels = {
|
|
22
|
+
routing: {
|
|
23
|
+
feishuGroupRequireMention: boolean;
|
|
24
|
+
wechatGroupRequireMention: boolean;
|
|
25
|
+
};
|
|
26
|
+
feishu: {
|
|
27
|
+
enabled: boolean;
|
|
28
|
+
requireMention?: boolean;
|
|
29
|
+
appId?: string;
|
|
30
|
+
domain?: string;
|
|
31
|
+
encryptKey?: string;
|
|
32
|
+
verificationToken?: string;
|
|
33
|
+
appSecret?: string;
|
|
34
|
+
thinkingReaction?: {
|
|
35
|
+
enabled?: boolean;
|
|
36
|
+
emojiType?: string;
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
wechat: {
|
|
40
|
+
enabled: boolean;
|
|
41
|
+
requireMention?: boolean;
|
|
42
|
+
implementation?: string;
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
type ModelOption = {
|
|
47
|
+
value: string;
|
|
48
|
+
label: string;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
type ModelsConfig = {
|
|
52
|
+
defaultModel: string;
|
|
53
|
+
options: ModelOption[];
|
|
54
|
+
};
|
|
55
|
+
|
|
17
56
|
export function OverviewPage() {
|
|
18
57
|
const { data, error, loading } = useFetch<Overview>("/api/overview");
|
|
19
58
|
const enabledChannels = data?.enabledChannels ?? [];
|
|
@@ -28,7 +67,7 @@ export function OverviewPage() {
|
|
|
28
67
|
<CardTitle className="text-base">系统摘要</CardTitle>
|
|
29
68
|
<CardDescription>当前 bot、渠道与工作区的核心状态。</CardDescription>
|
|
30
69
|
</CardHeader>
|
|
31
|
-
<CardContent className="grid gap-3 sm:grid-cols-
|
|
70
|
+
<CardContent className="grid gap-3 sm:grid-cols-2">
|
|
32
71
|
<SummaryStat
|
|
33
72
|
label="运行状态"
|
|
34
73
|
value={botStatusText}
|
|
@@ -39,79 +78,199 @@ export function OverviewPage() {
|
|
|
39
78
|
value={String(activeChannelCount)}
|
|
40
79
|
hint={enabledChannels.length ? `${enabledChannels.length} 个渠道已接入` : "暂无渠道信息"}
|
|
41
80
|
/>
|
|
42
|
-
<SummaryStat
|
|
43
|
-
label="Agent 模式"
|
|
44
|
-
value={data?.agentMode ?? "--"}
|
|
45
|
-
hint="当前启动模式"
|
|
46
|
-
/>
|
|
47
81
|
</CardContent>
|
|
48
82
|
</Card>
|
|
49
83
|
|
|
50
|
-
<
|
|
51
|
-
<CardHeader className="pb-3">
|
|
52
|
-
<CardTitle className="text-base">渠道状态</CardTitle>
|
|
53
|
-
<CardDescription>快速查看各渠道是否已启用。</CardDescription>
|
|
54
|
-
</CardHeader>
|
|
55
|
-
<CardContent className="flex flex-wrap gap-2">
|
|
56
|
-
{enabledChannels.length === 0 ? (
|
|
57
|
-
<Badge variant="outline">暂无数据</Badge>
|
|
58
|
-
) : (
|
|
59
|
-
enabledChannels.map((channel) => (
|
|
60
|
-
<Badge
|
|
61
|
-
key={channel.id}
|
|
62
|
-
variant={channel.enabled ? "default" : "outline"}
|
|
63
|
-
className="rounded-full px-3 py-1"
|
|
64
|
-
>
|
|
65
|
-
{channel.id} · {channel.enabled ? "已开启" : "已关闭"}
|
|
66
|
-
</Badge>
|
|
67
|
-
))
|
|
68
|
-
)}
|
|
69
|
-
</CardContent>
|
|
70
|
-
</Card>
|
|
84
|
+
<DefaultModelSection />
|
|
71
85
|
</div>
|
|
72
86
|
|
|
73
|
-
<
|
|
74
|
-
<CardHeader>
|
|
75
|
-
<CardTitle>概览</CardTitle>
|
|
76
|
-
<CardDescription>关键运行信息</CardDescription>
|
|
77
|
-
</CardHeader>
|
|
78
|
-
<CardContent>
|
|
79
|
-
{loading ? (
|
|
80
|
-
<Alert>
|
|
81
|
-
<AlertTitle>加载中</AlertTitle>
|
|
82
|
-
<AlertDescription>正在获取 Dashboard 运行信息。</AlertDescription>
|
|
83
|
-
</Alert>
|
|
84
|
-
) : error ? (
|
|
85
|
-
<Alert className="border-destructive/20 bg-destructive/5 text-destructive">
|
|
86
|
-
<AlertTitle>请求失败</AlertTitle>
|
|
87
|
-
<AlertDescription>错误:{error}</AlertDescription>
|
|
88
|
-
</Alert>
|
|
89
|
-
) : (
|
|
90
|
-
<div className="grid gap-3">
|
|
91
|
-
<InfoRow label="面板地址">
|
|
92
|
-
<a
|
|
93
|
-
className="underline decoration-border underline-offset-4 transition-colors hover:text-primary"
|
|
94
|
-
href={data?.dashboardUrl}
|
|
95
|
-
target="_blank"
|
|
96
|
-
rel="noreferrer"
|
|
97
|
-
>
|
|
98
|
-
{data?.dashboardUrl}
|
|
99
|
-
</a>
|
|
100
|
-
</InfoRow>
|
|
101
|
-
<InfoRow label="工作区目录" mono>
|
|
102
|
-
{data?.workspaceDir}
|
|
103
|
-
</InfoRow>
|
|
104
|
-
<InfoRow label="Agent 目录" mono>
|
|
105
|
-
{data?.agentDir}
|
|
106
|
-
</InfoRow>
|
|
107
|
-
</div>
|
|
108
|
-
)}
|
|
109
|
-
</CardContent>
|
|
110
|
-
</Card>
|
|
87
|
+
<ChannelsSection />
|
|
111
88
|
</section>
|
|
112
89
|
);
|
|
113
90
|
}
|
|
114
91
|
|
|
92
|
+
function DefaultModelSection() {
|
|
93
|
+
const { data, error, loading } = useFetch<ModelsConfig>("/api/models");
|
|
94
|
+
const [saving, setSaving] = React.useState(false);
|
|
95
|
+
const [form, setForm] = React.useState<ModelsConfig | null>(null);
|
|
96
|
+
|
|
97
|
+
React.useEffect(() => {
|
|
98
|
+
if (!data) return;
|
|
99
|
+
setForm({
|
|
100
|
+
defaultModel: typeof data.defaultModel === "string" ? data.defaultModel : "",
|
|
101
|
+
options: Array.isArray((data as unknown as { options?: unknown }).options)
|
|
102
|
+
? (data as unknown as { options: ModelOption[] }).options
|
|
103
|
+
: [],
|
|
104
|
+
});
|
|
105
|
+
}, [data]);
|
|
106
|
+
|
|
107
|
+
const saveDefaultModel = async (defaultModel: string) => {
|
|
108
|
+
setSaving(true);
|
|
109
|
+
try {
|
|
110
|
+
const res = await fetch("/api/models", {
|
|
111
|
+
method: "POST",
|
|
112
|
+
headers: { "Content-Type": "application/json" },
|
|
113
|
+
body: JSON.stringify({ models: { defaultModel } }),
|
|
114
|
+
});
|
|
115
|
+
const json = (await res.json()) as { ok?: boolean; error?: string };
|
|
116
|
+
if (!res.ok || json.ok === false) {
|
|
117
|
+
throw new Error(json.error || `${res.status} ${res.statusText}`);
|
|
118
|
+
}
|
|
119
|
+
toast.success("已保存(需要重启进程生效)");
|
|
120
|
+
} catch (e) {
|
|
121
|
+
toast.error(`保存失败:${String(e)}`, { duration: 4500 });
|
|
122
|
+
throw e;
|
|
123
|
+
} finally {
|
|
124
|
+
setSaving(false);
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const options = form?.options ?? [];
|
|
129
|
+
const onDefaultModelChange = async (nextValue: string) => {
|
|
130
|
+
if (!form || nextValue === form.defaultModel) return;
|
|
131
|
+
const previousValue = form.defaultModel;
|
|
132
|
+
setForm({ ...form, defaultModel: nextValue });
|
|
133
|
+
try {
|
|
134
|
+
await saveDefaultModel(nextValue);
|
|
135
|
+
} catch {
|
|
136
|
+
setForm((current) => (current ? { ...current, defaultModel: previousValue } : current));
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
return (
|
|
141
|
+
<Card className="border-border/60 bg-background/70 shadow-none">
|
|
142
|
+
<CardHeader className="pb-3">
|
|
143
|
+
<CardTitle className="text-base">默认模型</CardTitle>
|
|
144
|
+
<CardDescription>切换当前使用的默认模型。</CardDescription>
|
|
145
|
+
</CardHeader>
|
|
146
|
+
<CardContent>
|
|
147
|
+
{loading ? (
|
|
148
|
+
<Alert>
|
|
149
|
+
<AlertTitle>加载中</AlertTitle>
|
|
150
|
+
<AlertDescription>正在读取模型配置。</AlertDescription>
|
|
151
|
+
</Alert>
|
|
152
|
+
) : error ? (
|
|
153
|
+
<Alert className="border-destructive/20 bg-destructive/5 text-destructive">
|
|
154
|
+
<AlertTitle>读取失败</AlertTitle>
|
|
155
|
+
<AlertDescription>错误:{error}</AlertDescription>
|
|
156
|
+
</Alert>
|
|
157
|
+
) : form && options.length > 0 ? (
|
|
158
|
+
<Select
|
|
159
|
+
value={form.defaultModel}
|
|
160
|
+
onValueChange={onDefaultModelChange}
|
|
161
|
+
disabled={saving}
|
|
162
|
+
>
|
|
163
|
+
<SelectTrigger aria-label="选择默认模型">
|
|
164
|
+
<SelectValue placeholder="选择默认模型" />
|
|
165
|
+
</SelectTrigger>
|
|
166
|
+
<SelectContent>
|
|
167
|
+
{options.map((option) => (
|
|
168
|
+
<SelectItem key={option.value} value={option.value}>
|
|
169
|
+
{option.label}
|
|
170
|
+
</SelectItem>
|
|
171
|
+
))}
|
|
172
|
+
</SelectContent>
|
|
173
|
+
</Select>
|
|
174
|
+
) : form ? (
|
|
175
|
+
<Alert>
|
|
176
|
+
<AlertTitle>没有可选模型</AlertTitle>
|
|
177
|
+
<AlertDescription>当前还没有可用模型,请先补充模型定义。</AlertDescription>
|
|
178
|
+
</Alert>
|
|
179
|
+
) : null}
|
|
180
|
+
</CardContent>
|
|
181
|
+
</Card>
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function ChannelsSection() {
|
|
186
|
+
const { data, error, loading } = useFetch<Channels>("/api/channels");
|
|
187
|
+
const [saving, setSaving] = React.useState(false);
|
|
188
|
+
const [form, setForm] = React.useState<Channels | null>(null);
|
|
189
|
+
|
|
190
|
+
React.useEffect(() => {
|
|
191
|
+
if (data) setForm(data);
|
|
192
|
+
}, [data]);
|
|
193
|
+
|
|
194
|
+
const patch = (fn: (draft: Channels) => void) => {
|
|
195
|
+
if (!form) return;
|
|
196
|
+
const draft = { ...form, feishu: { ...form.feishu }, wechat: { ...form.wechat } };
|
|
197
|
+
fn(draft);
|
|
198
|
+
setForm(draft);
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
const onSave = async () => {
|
|
202
|
+
if (!form) return;
|
|
203
|
+
setSaving(true);
|
|
204
|
+
try {
|
|
205
|
+
const res = await fetch("/api/channels", {
|
|
206
|
+
method: "POST",
|
|
207
|
+
headers: { "Content-Type": "application/json" },
|
|
208
|
+
body: JSON.stringify({ channels: form }),
|
|
209
|
+
});
|
|
210
|
+
const json = (await res.json()) as { ok?: boolean; error?: string };
|
|
211
|
+
if (!res.ok || json.ok === false) {
|
|
212
|
+
throw new Error(json.error || `${res.status} ${res.statusText}`);
|
|
213
|
+
}
|
|
214
|
+
toast.success("已保存(需要重启进程生效)");
|
|
215
|
+
} catch (e) {
|
|
216
|
+
toast.error(`保存失败:${String(e)}`, { duration: 4500 });
|
|
217
|
+
} finally {
|
|
218
|
+
setSaving(false);
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
return (
|
|
223
|
+
<Card className="border-border/60 bg-background/70 shadow-none">
|
|
224
|
+
<CardHeader>
|
|
225
|
+
<CardTitle className="text-base">飞书配置</CardTitle>
|
|
226
|
+
<CardDescription>管理飞书渠道的启用状态与接入凭证。</CardDescription>
|
|
227
|
+
</CardHeader>
|
|
228
|
+
<CardContent>
|
|
229
|
+
{loading ? (
|
|
230
|
+
<Alert>
|
|
231
|
+
<AlertTitle>加载中</AlertTitle>
|
|
232
|
+
<AlertDescription>正在读取渠道配置。</AlertDescription>
|
|
233
|
+
</Alert>
|
|
234
|
+
) : error ? (
|
|
235
|
+
<Alert className="border-destructive/20 bg-destructive/5 text-destructive">
|
|
236
|
+
<AlertTitle>读取失败</AlertTitle>
|
|
237
|
+
<AlertDescription>错误:{error}</AlertDescription>
|
|
238
|
+
</Alert>
|
|
239
|
+
) : form ? (
|
|
240
|
+
<div className="space-y-4">
|
|
241
|
+
<div className="grid gap-4">
|
|
242
|
+
<CheckboxField
|
|
243
|
+
label="启用状态"
|
|
244
|
+
checked={!!form.feishu.enabled}
|
|
245
|
+
onChange={(checked) => patch((d) => (d.feishu.enabled = checked))}
|
|
246
|
+
/>
|
|
247
|
+
<TextField
|
|
248
|
+
label="App ID"
|
|
249
|
+
value={form.feishu.appId ?? ""}
|
|
250
|
+
onChange={(value) => patch((d) => (d.feishu.appId = value))}
|
|
251
|
+
/>
|
|
252
|
+
<TextField
|
|
253
|
+
label="App Secret"
|
|
254
|
+
value={form.feishu.appSecret ?? ""}
|
|
255
|
+
onChange={(value) => patch((d) => (d.feishu.appSecret = value))}
|
|
256
|
+
/>
|
|
257
|
+
</div>
|
|
258
|
+
<div className="flex justify-start">
|
|
259
|
+
<Button
|
|
260
|
+
onClick={onSave}
|
|
261
|
+
disabled={!form || saving || loading || Boolean(error)}
|
|
262
|
+
className="shrink-0"
|
|
263
|
+
>
|
|
264
|
+
{saving ? "保存中…" : "保存"}
|
|
265
|
+
</Button>
|
|
266
|
+
</div>
|
|
267
|
+
</div>
|
|
268
|
+
) : null}
|
|
269
|
+
</CardContent>
|
|
270
|
+
</Card>
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
|
|
115
274
|
function SummaryStat(props: { label: string; value: string; hint: string }) {
|
|
116
275
|
return (
|
|
117
276
|
<div className="rounded-2xl border border-border/70 bg-muted/30 p-4">
|
|
@@ -122,13 +281,50 @@ function SummaryStat(props: { label: string; value: string; hint: string }) {
|
|
|
122
281
|
);
|
|
123
282
|
}
|
|
124
283
|
|
|
125
|
-
function
|
|
284
|
+
function FieldShell(props: { label: string; children: React.ReactNode; description?: string }) {
|
|
126
285
|
return (
|
|
127
|
-
<div className="
|
|
128
|
-
<
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
</div>
|
|
286
|
+
<div className="space-y-2">
|
|
287
|
+
<Label className="text-xs uppercase tracking-[0.16em] text-muted-foreground">{props.label}</Label>
|
|
288
|
+
{props.children}
|
|
289
|
+
{props.description ? <p className="text-xs text-muted-foreground">{props.description}</p> : null}
|
|
132
290
|
</div>
|
|
133
291
|
);
|
|
134
292
|
}
|
|
293
|
+
|
|
294
|
+
function TextField(props: {
|
|
295
|
+
label: string;
|
|
296
|
+
value: string;
|
|
297
|
+
placeholder?: string;
|
|
298
|
+
onChange: (value: string) => void;
|
|
299
|
+
}) {
|
|
300
|
+
return (
|
|
301
|
+
<FieldShell label={props.label}>
|
|
302
|
+
<Input
|
|
303
|
+
className="font-mono text-xs sm:text-sm"
|
|
304
|
+
value={props.value}
|
|
305
|
+
placeholder={props.placeholder}
|
|
306
|
+
onChange={(e) => props.onChange(e.target.value)}
|
|
307
|
+
/>
|
|
308
|
+
</FieldShell>
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function CheckboxField(props: {
|
|
313
|
+
label: string;
|
|
314
|
+
checked: boolean;
|
|
315
|
+
onChange: (checked: boolean) => void;
|
|
316
|
+
}) {
|
|
317
|
+
return (
|
|
318
|
+
<FieldShell label={props.label}>
|
|
319
|
+
<label className="flex h-10 items-center gap-3 rounded-md border border-input bg-background/80 px-3 text-sm">
|
|
320
|
+
<input
|
|
321
|
+
type="checkbox"
|
|
322
|
+
className="h-4 w-4 rounded border-border accent-primary"
|
|
323
|
+
checked={props.checked}
|
|
324
|
+
onChange={(e) => props.onChange(e.target.checked)}
|
|
325
|
+
/>
|
|
326
|
+
<span className="text-sm text-foreground">{props.checked ? "已启用" : "未启用"}</span>
|
|
327
|
+
</label>
|
|
328
|
+
</FieldShell>
|
|
329
|
+
);
|
|
330
|
+
}
|
|
@@ -201,94 +201,3 @@
|
|
|
201
201
|
max-width: 100%;
|
|
202
202
|
height: auto;
|
|
203
203
|
}
|
|
204
|
-
|
|
205
|
-
.docs-content [data-streamdown] {
|
|
206
|
-
overflow-wrap: anywhere;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
.docs-content h1,
|
|
210
|
-
.docs-content h2,
|
|
211
|
-
.docs-content h3,
|
|
212
|
-
.docs-content h4 {
|
|
213
|
-
margin: 1.5rem 0 0.75rem;
|
|
214
|
-
font-weight: 600;
|
|
215
|
-
letter-spacing: -0.02em;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
.docs-content h1:first-child,
|
|
219
|
-
.docs-content h2:first-child,
|
|
220
|
-
.docs-content h3:first-child {
|
|
221
|
-
margin-top: 0;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
.docs-content h1 {
|
|
225
|
-
font-size: 1.5rem;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
.docs-content h2 {
|
|
229
|
-
font-size: 1.2rem;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
.docs-content h3 {
|
|
233
|
-
font-size: 1rem;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
.docs-content p,
|
|
237
|
-
.docs-content ul,
|
|
238
|
-
.docs-content ol,
|
|
239
|
-
.docs-content table,
|
|
240
|
-
.docs-content blockquote {
|
|
241
|
-
margin: 0.75rem 0;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
.docs-content ul,
|
|
245
|
-
.docs-content ol {
|
|
246
|
-
padding-left: 1.25rem;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
.docs-content li + li {
|
|
250
|
-
margin-top: 0.35rem;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
.docs-content a {
|
|
254
|
-
color: var(--primary);
|
|
255
|
-
text-decoration: underline;
|
|
256
|
-
text-underline-offset: 0.2em;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
.docs-content hr {
|
|
260
|
-
margin: 1.25rem 0;
|
|
261
|
-
border: 0;
|
|
262
|
-
border-top: 1px solid color-mix(in oklab, var(--border) 100%, transparent);
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
.docs-content blockquote {
|
|
266
|
-
border-left: 3px solid color-mix(in oklab, var(--border) 100%, transparent);
|
|
267
|
-
padding-left: 1rem;
|
|
268
|
-
color: var(--muted-foreground);
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
.docs-content table {
|
|
272
|
-
width: 100%;
|
|
273
|
-
border-collapse: collapse;
|
|
274
|
-
overflow: hidden;
|
|
275
|
-
border-radius: 0.875rem;
|
|
276
|
-
border: 1px solid color-mix(in oklab, var(--border) 100%, transparent);
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
.docs-content th,
|
|
280
|
-
.docs-content td {
|
|
281
|
-
border-bottom: 1px solid color-mix(in oklab, var(--border) 100%, transparent);
|
|
282
|
-
padding: 0.75rem;
|
|
283
|
-
text-align: left;
|
|
284
|
-
vertical-align: top;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
.docs-content th {
|
|
288
|
-
background: color-mix(in oklab, var(--muted) 80%, var(--background));
|
|
289
|
-
font-weight: 600;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
.docs-content tbody tr:last-child td {
|
|
293
|
-
border-bottom: 0;
|
|
294
|
-
}
|
|
@@ -300,16 +300,78 @@ test("loadBotAppConfig builds the app config from config.json plus env-backed ch
|
|
|
300
300
|
|
|
301
301
|
rmSync(tempRoot, { recursive: true, force: true });
|
|
302
302
|
|
|
303
|
-
assert.equal(getDefaultConfigPath(), resolve(process.cwd(), "
|
|
303
|
+
assert.equal(getDefaultConfigPath(), resolve(process.cwd(), "<%= workspaceDir %>/config.json"));
|
|
304
304
|
assert.equal(loaded.agent.mode, "pi");
|
|
305
305
|
assert.equal(loaded.agent.provider, "coze");
|
|
306
306
|
assert.equal(loaded.agent.model, "auto");
|
|
307
307
|
assert.equal(loaded.agent.configPath, configPath);
|
|
308
308
|
assert.equal(loaded.agent.thinkingLevel, "high");
|
|
309
309
|
assert.equal(loaded.agent.cwd, dirname(configPath));
|
|
310
|
+
assert.equal(
|
|
311
|
+
(((loaded.configRoot?.agents as { defaults?: { model?: { primary?: string } } })?.defaults?.model?.primary) ?? ""),
|
|
312
|
+
"coze/auto"
|
|
313
|
+
);
|
|
310
314
|
assert.equal(loaded.channels.feishu?.appId, "app-id");
|
|
311
315
|
assert.equal(loaded.channels.feishu?.verificationToken, "verify-token");
|
|
312
316
|
assert.equal(loaded.routing.feishuGroupRequireMention, false);
|
|
313
317
|
assert.equal(loaded.channels.feishu?.thinkingReaction?.enabled, false);
|
|
314
318
|
assert.equal(loaded.channels.feishu?.thinkingReaction?.emojiType, "OneSecond");
|
|
315
319
|
});
|
|
320
|
+
|
|
321
|
+
test("loadConfig falls back to a minimal mock config when config.json is missing", () => {
|
|
322
|
+
const tempDir = mkdtempSync(join(tmpdir(), "pi-bot-missing-config-"));
|
|
323
|
+
const configPath = join(tempDir, "workspace", "config.json");
|
|
324
|
+
|
|
325
|
+
const loaded = loadConfig(configPath);
|
|
326
|
+
|
|
327
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
328
|
+
|
|
329
|
+
assert.equal(loaded.source, "fallback-mock");
|
|
330
|
+
assert.equal(loaded.path, configPath);
|
|
331
|
+
assert.equal(loaded.workspaceDir, dirname(configPath));
|
|
332
|
+
assert.deepEqual(loaded.defaultModel, {
|
|
333
|
+
provider: "openai",
|
|
334
|
+
modelId: "gpt-5-mini"
|
|
335
|
+
});
|
|
336
|
+
assert.equal(loaded.config.channels?.feishu?.enabled, false);
|
|
337
|
+
assert.equal(loaded.config.channels?.wechat?.enabled, false);
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
test("loadBotAppConfig falls back to mock mode and skips file config path when config.json is missing", () => {
|
|
341
|
+
const tempDir = mkdtempSync(join(tmpdir(), "pi-bot-missing-config-"));
|
|
342
|
+
const configPath = join(tempDir, "workspace", "config.json");
|
|
343
|
+
|
|
344
|
+
const loaded = loadBotAppConfig({
|
|
345
|
+
configPath,
|
|
346
|
+
env: {}
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
350
|
+
|
|
351
|
+
assert.equal(loaded.agent.mode, "mock");
|
|
352
|
+
assert.equal(loaded.agent.provider, "openai");
|
|
353
|
+
assert.equal(loaded.agent.model, "gpt-5-mini");
|
|
354
|
+
assert.equal(loaded.agent.configPath, undefined);
|
|
355
|
+
assert.equal(loaded.agent.cwd, dirname(configPath));
|
|
356
|
+
assert.equal(
|
|
357
|
+
(((loaded.configRoot?.agents as { defaults?: { model?: { primary?: string } } })?.defaults?.model?.primary) ?? ""),
|
|
358
|
+
"openai/gpt-5-mini"
|
|
359
|
+
);
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
test("loadBotAppConfig still allows env to override mode when config.json is missing", () => {
|
|
363
|
+
const tempDir = mkdtempSync(join(tmpdir(), "pi-bot-missing-config-"));
|
|
364
|
+
const configPath = join(tempDir, "workspace", "config.json");
|
|
365
|
+
|
|
366
|
+
const loaded = loadBotAppConfig({
|
|
367
|
+
configPath,
|
|
368
|
+
env: {
|
|
369
|
+
PI_BOT_AGENT_MODE: "pi"
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
374
|
+
|
|
375
|
+
assert.equal(loaded.agent.mode, "pi");
|
|
376
|
+
assert.equal(loaded.agent.configPath, undefined);
|
|
377
|
+
});
|
|
@@ -94,30 +94,6 @@
|
|
|
94
94
|
"additionalProperties": false
|
|
95
95
|
}
|
|
96
96
|
},
|
|
97
|
-
{
|
|
98
|
-
"name": "pi-agent",
|
|
99
|
-
"description": "Pi Agent:`coze init ${COZE_WORKSPACE_PATH} --template pi-agent`\n- 适用:基于 pi-agent-core 的 AI Agent 应用\n- 支持飞书、微信等多渠道接入\n- 内置 Dashboard 管理面板\n- 使用 TypeScript + Express + Vite",
|
|
100
|
-
"location": "./pi-agent",
|
|
101
|
-
"paramsSchema": {
|
|
102
|
-
"type": "object",
|
|
103
|
-
"properties": {
|
|
104
|
-
"port": {
|
|
105
|
-
"type": "number",
|
|
106
|
-
"default": 5000,
|
|
107
|
-
"minimum": 1024,
|
|
108
|
-
"maximum": 65535,
|
|
109
|
-
"description": "Dashboard server port"
|
|
110
|
-
},
|
|
111
|
-
"workspaceDir": {
|
|
112
|
-
"type": "string",
|
|
113
|
-
"default": "/workspace/workspace",
|
|
114
|
-
"description": "Workspace directory path"
|
|
115
|
-
}
|
|
116
|
-
},
|
|
117
|
-
"required": [],
|
|
118
|
-
"additionalProperties": false
|
|
119
|
-
}
|
|
120
|
-
},
|
|
121
97
|
{
|
|
122
98
|
"name": "taro",
|
|
123
99
|
"description": "Taro(小程序 + H5):`coze init ${COZE_WORKSPACE_PATH} --template taro`\n- 适用:微信小程序、H5 跨端应用\n- 前后端分离架构:Taro 4 + NestJS\n- 支持微信小程序和 H5 双端构建\n- 使用 TailwindCSS + weapp-tailwindcss 实现跨端样式",
|
|
@@ -181,6 +157,30 @@
|
|
|
181
157
|
"required": [],
|
|
182
158
|
"additionalProperties": false
|
|
183
159
|
}
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
"name": "pi-agent",
|
|
163
|
+
"description": "Pi Agent:`coze init ${COZE_WORKSPACE_PATH} --template pi-agent`\n- 适用:基于 pi-agent-core 的 AI Agent 应用\n- 支持飞书、微信等多渠道接入\n- 内置 Dashboard 管理面板\n- 使用 TypeScript + Express + Vite",
|
|
164
|
+
"location": "./pi-agent",
|
|
165
|
+
"paramsSchema": {
|
|
166
|
+
"type": "object",
|
|
167
|
+
"properties": {
|
|
168
|
+
"port": {
|
|
169
|
+
"type": "number",
|
|
170
|
+
"default": 5000,
|
|
171
|
+
"minimum": 1024,
|
|
172
|
+
"maximum": 65535,
|
|
173
|
+
"description": "Dashboard server port"
|
|
174
|
+
},
|
|
175
|
+
"workspaceDir": {
|
|
176
|
+
"type": "string",
|
|
177
|
+
"default": "/workspace/workspace",
|
|
178
|
+
"description": "Workspace directory path"
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
"required": [],
|
|
182
|
+
"additionalProperties": false
|
|
183
|
+
}
|
|
184
184
|
}
|
|
185
185
|
]
|
|
186
186
|
}
|
package/lib/cli.js
CHANGED
|
@@ -2107,7 +2107,7 @@ const EventBuilder = {
|
|
|
2107
2107
|
};
|
|
2108
2108
|
|
|
2109
2109
|
var name = "@coze-arch/cli";
|
|
2110
|
-
var version = "0.0.
|
|
2110
|
+
var version = "0.0.20";
|
|
2111
2111
|
var description = "coze coding devtools cli";
|
|
2112
2112
|
var license = "MIT";
|
|
2113
2113
|
var author = "fanwenjie.fe@bytedance.com";
|
package/package.json
CHANGED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import createJiti from "jiti";
|
|
4
|
-
|
|
5
|
-
const jiti = createJiti(import.meta.url);
|
|
6
|
-
const { runAsrCli } = await jiti.import("../../../src/skill-cli.ts");
|
|
7
|
-
|
|
8
|
-
const code = await runAsrCli(process.argv.slice(2), process.env);
|
|
9
|
-
process.exit(code);
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import createJiti from "jiti";
|
|
4
|
-
|
|
5
|
-
const jiti = createJiti(import.meta.url);
|
|
6
|
-
const { runImageCli } = await jiti.import("../../../src/skill-cli.ts");
|
|
7
|
-
|
|
8
|
-
const code = await runImageCli(process.argv.slice(2), process.env);
|
|
9
|
-
process.exit(code);
|