@coze-arch/cli 0.0.18 → 0.0.19-beta.1

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.
Files changed (104) hide show
  1. package/lib/__templates__/expo/.coze +1 -0
  2. package/lib/__templates__/expo/.cozeproj/scripts/validate.sh +8 -0
  3. package/lib/__templates__/expo/package.json +2 -1
  4. package/lib/__templates__/nextjs/.coze +1 -0
  5. package/lib/__templates__/nextjs/package.json +3 -1
  6. package/lib/__templates__/nextjs/scripts/validate.sh +10 -0
  7. package/lib/__templates__/nuxt-vue/.coze +1 -0
  8. package/lib/__templates__/nuxt-vue/eslint.config.mjs +25 -0
  9. package/lib/__templates__/nuxt-vue/package.json +9 -2
  10. package/lib/__templates__/nuxt-vue/pnpm-lock.yaml +790 -10
  11. package/lib/__templates__/nuxt-vue/scripts/validate.sh +10 -0
  12. package/lib/__templates__/pi-agent/.coze +10 -0
  13. package/lib/__templates__/pi-agent/AGENTS.md +144 -0
  14. package/lib/__templates__/pi-agent/README.md +216 -0
  15. package/lib/__templates__/pi-agent/_gitignore +3 -0
  16. package/lib/__templates__/pi-agent/_npmrc +23 -0
  17. package/lib/__templates__/pi-agent/bin/pi-bot.ts +8 -0
  18. package/lib/__templates__/pi-agent/docs/project-overview.md +374 -0
  19. package/lib/__templates__/pi-agent/docs/user/getting-started.md +47 -0
  20. package/lib/__templates__/pi-agent/package.json +63 -0
  21. package/lib/__templates__/pi-agent/pi-resources/SYSTEM.md +15 -0
  22. package/lib/__templates__/pi-agent/pi-resources/extensions/preference-memory/index.ts +355 -0
  23. package/lib/__templates__/pi-agent/pi-resources/skills/coze-asr/SKILL.md +36 -0
  24. package/lib/__templates__/pi-agent/pi-resources/skills/coze-asr/scripts/asr.mjs +9 -0
  25. package/lib/__templates__/pi-agent/pi-resources/skills/coze-image-gen/SKILL.md +41 -0
  26. package/lib/__templates__/pi-agent/pi-resources/skills/coze-image-gen/scripts/gen.mjs +9 -0
  27. package/lib/__templates__/pi-agent/pi-resources/skills/coze-tts/SKILL.md +85 -0
  28. package/lib/__templates__/pi-agent/pi-resources/skills/coze-tts/scripts/tts.mjs +9 -0
  29. package/lib/__templates__/pi-agent/pi-resources/skills/coze-video-gen/SKILL.md +53 -0
  30. package/lib/__templates__/pi-agent/pi-resources/skills/coze-video-gen/scripts/gen.mjs +9 -0
  31. package/lib/__templates__/pi-agent/pnpm-lock.yaml +8282 -0
  32. package/lib/__templates__/pi-agent/scripts/dev.sh +14 -0
  33. package/lib/__templates__/pi-agent/scripts/prepare.sh +35 -0
  34. package/lib/__templates__/pi-agent/src/agent.ts +363 -0
  35. package/lib/__templates__/pi-agent/src/channels/feishu/index.ts +760 -0
  36. package/lib/__templates__/pi-agent/src/channels/feishu/streaming-card.ts +297 -0
  37. package/lib/__templates__/pi-agent/src/channels/wechat/index.ts +171 -0
  38. package/lib/__templates__/pi-agent/src/cli.ts +117 -0
  39. package/lib/__templates__/pi-agent/src/config.ts +708 -0
  40. package/lib/__templates__/pi-agent/src/core.ts +218 -0
  41. package/lib/__templates__/pi-agent/src/dashboard/api/channels.ts +104 -0
  42. package/lib/__templates__/pi-agent/src/dashboard/api/docs.ts +204 -0
  43. package/lib/__templates__/pi-agent/src/dashboard/api/models.ts +98 -0
  44. package/lib/__templates__/pi-agent/src/dashboard/api/overview.ts +33 -0
  45. package/lib/__templates__/pi-agent/src/dashboard/config-store.ts +64 -0
  46. package/lib/__templates__/pi-agent/src/dashboard/index.ts +39 -0
  47. package/lib/__templates__/pi-agent/src/dashboard/server.ts +622 -0
  48. package/lib/__templates__/pi-agent/src/dashboard/types.ts +25 -0
  49. package/lib/__templates__/pi-agent/src/dashboard/web/index.html +13 -0
  50. package/lib/__templates__/pi-agent/src/dashboard/web/postcss.config.cjs +7 -0
  51. package/lib/__templates__/pi-agent/src/dashboard/web/src/components/app-layout.tsx +186 -0
  52. package/lib/__templates__/pi-agent/src/dashboard/web/src/components/page-title.tsx +17 -0
  53. package/lib/__templates__/pi-agent/src/dashboard/web/src/components/ui/alert.tsx +22 -0
  54. package/lib/__templates__/pi-agent/src/dashboard/web/src/components/ui/badge.tsx +25 -0
  55. package/lib/__templates__/pi-agent/src/dashboard/web/src/components/ui/button.tsx +40 -0
  56. package/lib/__templates__/pi-agent/src/dashboard/web/src/components/ui/card.tsx +29 -0
  57. package/lib/__templates__/pi-agent/src/dashboard/web/src/components/ui/input.tsx +18 -0
  58. package/lib/__templates__/pi-agent/src/dashboard/web/src/components/ui/label.tsx +8 -0
  59. package/lib/__templates__/pi-agent/src/dashboard/web/src/components/ui/select.tsx +80 -0
  60. package/lib/__templates__/pi-agent/src/dashboard/web/src/components/ui/separator.tsx +23 -0
  61. package/lib/__templates__/pi-agent/src/dashboard/web/src/hooks/use-fetch.ts +32 -0
  62. package/lib/__templates__/pi-agent/src/dashboard/web/src/hooks/use-local-storage-state.ts +23 -0
  63. package/lib/__templates__/pi-agent/src/dashboard/web/src/main.tsx +30 -0
  64. package/lib/__templates__/pi-agent/src/dashboard/web/src/pages/channels-page.tsx +188 -0
  65. package/lib/__templates__/pi-agent/src/dashboard/web/src/pages/chat-page.tsx +451 -0
  66. package/lib/__templates__/pi-agent/src/dashboard/web/src/pages/docs-page.tsx +65 -0
  67. package/lib/__templates__/pi-agent/src/dashboard/web/src/pages/models-page.tsx +122 -0
  68. package/lib/__templates__/pi-agent/src/dashboard/web/src/pages/overview-page.tsx +134 -0
  69. package/lib/__templates__/pi-agent/src/dashboard/web/src/services/chat-ws-service.ts +167 -0
  70. package/lib/__templates__/pi-agent/src/dashboard/web/src/styles.css +294 -0
  71. package/lib/__templates__/pi-agent/src/dashboard/web/src/utils/index.ts +11 -0
  72. package/lib/__templates__/pi-agent/src/dashboard/web/tsconfig.json +13 -0
  73. package/lib/__templates__/pi-agent/src/dashboard/web/vite.config.ts +17 -0
  74. package/lib/__templates__/pi-agent/src/index.ts +123 -0
  75. package/lib/__templates__/pi-agent/src/pi-resources.ts +125 -0
  76. package/lib/__templates__/pi-agent/src/session-store.ts +223 -0
  77. package/lib/__templates__/pi-agent/src/tools/common/format-coze-error.ts +12 -0
  78. package/lib/__templates__/pi-agent/src/tools/index.ts +2 -0
  79. package/lib/__templates__/pi-agent/src/tools/web-fetch/index.ts +195 -0
  80. package/lib/__templates__/pi-agent/src/tools/web-search/index.ts +206 -0
  81. package/lib/__templates__/pi-agent/template.config.js +45 -0
  82. package/lib/__templates__/pi-agent/tests/cli.test.ts +136 -0
  83. package/lib/__templates__/pi-agent/tests/config.test.ts +315 -0
  84. package/lib/__templates__/pi-agent/tests/dashboard-docs-api.test.ts +125 -0
  85. package/lib/__templates__/pi-agent/tests/dashboard-models-api.test.ts +171 -0
  86. package/lib/__templates__/pi-agent/tests/feishu-channel.test.ts +149 -0
  87. package/lib/__templates__/pi-agent/tests/feishu-streaming-card.test.ts +15 -0
  88. package/lib/__templates__/pi-agent/tests/pi-resources.test.ts +73 -0
  89. package/lib/__templates__/pi-agent/tests/preference-memory.test.ts +43 -0
  90. package/lib/__templates__/pi-agent/tests/session-store.test.ts +61 -0
  91. package/lib/__templates__/pi-agent/tests/smoke/run-smoke.ts +275 -0
  92. package/lib/__templates__/pi-agent/tests/web-fetch.test.ts +157 -0
  93. package/lib/__templates__/pi-agent/tests/web-search.test.ts +208 -0
  94. package/lib/__templates__/pi-agent/tsconfig.json +21 -0
  95. package/lib/__templates__/pi-agent/types/larksuiteoapi-node-sdk.d.ts +113 -0
  96. package/lib/__templates__/taro/.coze +1 -0
  97. package/lib/__templates__/taro/.cozeproj/scripts/validate.sh +8 -0
  98. package/lib/__templates__/taro/package.json +1 -1
  99. package/lib/__templates__/templates.json +24 -0
  100. package/lib/__templates__/vite/.coze +1 -0
  101. package/lib/__templates__/vite/package.json +3 -1
  102. package/lib/__templates__/vite/scripts/validate.sh +10 -0
  103. package/lib/cli.js +13 -2
  104. package/package.json +1 -1
@@ -0,0 +1,294 @@
1
+ @import "tailwindcss";
2
+ @source "../../../../node_modules/streamdown/dist/*.js";
3
+ @source "../../../../node_modules/@streamdown/code/dist/*.js";
4
+
5
+ :root {
6
+ color-scheme: light;
7
+ --background: oklch(1 0 0);
8
+ --foreground: oklch(0.145 0 0);
9
+ --card: oklch(1 0 0);
10
+ --card-foreground: oklch(0.145 0 0);
11
+ --popover: oklch(1 0 0);
12
+ --popover-foreground: oklch(0.145 0 0);
13
+ --primary: oklch(0.205 0 0);
14
+ --primary-foreground: oklch(0.985 0 0);
15
+ --secondary: oklch(0.97 0 0);
16
+ --secondary-foreground: oklch(0.205 0 0);
17
+ --muted: oklch(0.97 0 0);
18
+ --muted-foreground: oklch(0.556 0 0);
19
+ --accent: oklch(0.97 0 0);
20
+ --accent-foreground: oklch(0.205 0 0);
21
+ --destructive: oklch(0.577 0.245 27.325);
22
+ --destructive-foreground: oklch(0.985 0 0);
23
+ --border: oklch(0.922 0 0);
24
+ --input: oklch(0.922 0 0);
25
+ --ring: oklch(0.708 0 0);
26
+ --radius: 0.625rem;
27
+ }
28
+
29
+ .dark {
30
+ color-scheme: dark;
31
+ --background: oklch(0.145 0 0);
32
+ --foreground: oklch(0.985 0 0);
33
+ --card: oklch(0.205 0 0);
34
+ --card-foreground: oklch(0.985 0 0);
35
+ --popover: oklch(0.205 0 0);
36
+ --popover-foreground: oklch(0.985 0 0);
37
+ --primary: oklch(0.922 0 0);
38
+ --primary-foreground: oklch(0.205 0 0);
39
+ --secondary: oklch(0.269 0 0);
40
+ --secondary-foreground: oklch(0.985 0 0);
41
+ --muted: oklch(0.269 0 0);
42
+ --muted-foreground: oklch(0.708 0 0);
43
+ --accent: oklch(0.269 0 0);
44
+ --accent-foreground: oklch(0.985 0 0);
45
+ --destructive: oklch(0.704 0.191 22.216);
46
+ --destructive-foreground: oklch(0.985 0 0);
47
+ --border: oklch(1 0 0 / 10%);
48
+ --input: oklch(1 0 0 / 15%);
49
+ --ring: oklch(0.556 0 0);
50
+ }
51
+
52
+ @theme inline {
53
+ --font-sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Arial, "Apple Color Emoji", "Segoe UI Emoji";
54
+ --color-background: var(--background);
55
+ --color-foreground: var(--foreground);
56
+ --color-card: var(--card);
57
+ --color-card-foreground: var(--card-foreground);
58
+ --color-popover: var(--popover);
59
+ --color-popover-foreground: var(--popover-foreground);
60
+ --color-primary: var(--primary);
61
+ --color-primary-foreground: var(--primary-foreground);
62
+ --color-secondary: var(--secondary);
63
+ --color-secondary-foreground: var(--secondary-foreground);
64
+ --color-muted: var(--muted);
65
+ --color-muted-foreground: var(--muted-foreground);
66
+ --color-accent: var(--accent);
67
+ --color-accent-foreground: var(--accent-foreground);
68
+ --color-destructive: var(--destructive);
69
+ --color-destructive-foreground: var(--destructive-foreground);
70
+ --color-border: var(--border);
71
+ --color-input: var(--input);
72
+ --color-ring: var(--ring);
73
+ --radius-sm: calc(var(--radius) - 6px);
74
+ --radius-md: calc(var(--radius) - 2px);
75
+ --radius-lg: var(--radius);
76
+ --radius-xl: calc(var(--radius) + 8px);
77
+ }
78
+
79
+ @layer base {
80
+ * {
81
+ box-sizing: border-box;
82
+ }
83
+
84
+ body {
85
+ @apply bg-background font-sans text-foreground antialiased transition-colors;
86
+ height: 100vh;
87
+ overflow: hidden;
88
+ }
89
+
90
+ #root {
91
+ height: 100%;
92
+ }
93
+
94
+ a {
95
+ @apply text-inherit;
96
+ }
97
+
98
+ h1,
99
+ h2,
100
+ h3,
101
+ p {
102
+ @apply m-0;
103
+ }
104
+ }
105
+
106
+ @supports (height: 100dvh) {
107
+ @layer base {
108
+ body {
109
+ height: 100dvh;
110
+ }
111
+ }
112
+ }
113
+
114
+ .streamdown-content [data-streamdown] {
115
+ overflow-wrap: anywhere;
116
+ }
117
+
118
+ .streamdown-content [data-streamdown="code-block"] {
119
+ position: relative;
120
+ margin: 1rem 0;
121
+ gap: 0.5rem;
122
+ border: 1px solid color-mix(in oklab, var(--border) 100%, transparent);
123
+ border-radius: 0.875rem;
124
+ background: color-mix(in oklab, var(--muted) 65%, var(--background));
125
+ padding: 0.75rem;
126
+ }
127
+
128
+ .streamdown-content [data-streamdown="code-block-header"] {
129
+ display: flex;
130
+ min-height: 1.75rem;
131
+ align-items: center;
132
+ height: auto;
133
+ padding: 0 4.5rem 0 0.125rem;
134
+ color: var(--muted-foreground);
135
+ font-size: 0.75rem;
136
+ }
137
+
138
+ .streamdown-content [data-streamdown="code-block-header"] + div {
139
+ position: absolute;
140
+ top: 0.75rem;
141
+ right: 0.75rem;
142
+ left: auto;
143
+ z-index: 20;
144
+ margin: 0;
145
+ padding: 0;
146
+ }
147
+
148
+ .streamdown-content [data-streamdown="code-block-actions"] {
149
+ gap: 0.375rem;
150
+ border: 1px solid color-mix(in oklab, var(--border) 100%, transparent);
151
+ border-radius: 0.625rem;
152
+ background: color-mix(in oklab, var(--background) 88%, transparent);
153
+ padding: 0.25rem;
154
+ box-shadow: none;
155
+ backdrop-filter: none;
156
+ }
157
+
158
+ .streamdown-content [data-streamdown="code-block-actions"] button {
159
+ display: inline-flex;
160
+ height: 1.75rem;
161
+ width: 1.75rem;
162
+ align-items: center;
163
+ justify-content: center;
164
+ border: 0;
165
+ border-radius: 0.5rem;
166
+ background: transparent;
167
+ color: var(--muted-foreground);
168
+ transition:
169
+ background-color 150ms ease,
170
+ color 150ms ease;
171
+ }
172
+
173
+ .streamdown-content [data-streamdown="code-block-actions"] button:hover {
174
+ background: color-mix(in oklab, var(--accent) 100%, transparent);
175
+ color: var(--foreground);
176
+ }
177
+
178
+ .streamdown-content [data-streamdown="code-block-actions"] button:focus-visible {
179
+ outline: 2px solid color-mix(in oklab, var(--ring) 65%, transparent);
180
+ outline-offset: 1px;
181
+ }
182
+
183
+ .streamdown-content [data-streamdown="code-block-body"] {
184
+ border: 1px solid color-mix(in oklab, var(--border) 100%, transparent);
185
+ border-radius: 0.75rem;
186
+ background: color-mix(in oklab, var(--background) 92%, var(--muted));
187
+ margin-top: 0.5rem;
188
+ padding: 0.875rem 1rem;
189
+ }
190
+
191
+ .streamdown-content [data-streamdown="code-block-body"] pre {
192
+ margin: 0;
193
+ background: transparent;
194
+ }
195
+
196
+ .streamdown-content pre {
197
+ overflow: auto;
198
+ }
199
+
200
+ .streamdown-content img {
201
+ max-width: 100%;
202
+ height: auto;
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
+ }
@@ -0,0 +1,11 @@
1
+ import { type ClassValue, clsx } from "clsx";
2
+ import { twMerge } from "tailwind-merge";
3
+ import { cloneDeep } from "lodash-es";
4
+
5
+ export function cn(...inputs: ClassValue[]) {
6
+ return twMerge(clsx(inputs));
7
+ }
8
+
9
+ export function deepClone<T>(value: T): T {
10
+ return cloneDeep(value);
11
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "extends": "../../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "jsx": "react-jsx",
5
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
6
+ "module": "ESNext",
7
+ "moduleResolution": "Bundler",
8
+ "types": ["vite/client"],
9
+ "noEmit": true
10
+ },
11
+ "include": ["vite.config.ts", "src/**/*.ts", "src/**/*.tsx"]
12
+ }
13
+
@@ -0,0 +1,17 @@
1
+ import { defineConfig } from "vite";
2
+ import react from "@vitejs/plugin-react";
3
+ import { fileURLToPath } from "node:url";
4
+ import { dirname, resolve } from "node:path";
5
+
6
+ const DIR = dirname(fileURLToPath(import.meta.url));
7
+
8
+ export default defineConfig({
9
+ root: DIR,
10
+ plugins: [react()],
11
+ base: "/",
12
+ build: {
13
+ outDir: resolve(DIR, "dist"),
14
+ emptyOutDir: true,
15
+ },
16
+ });
17
+
@@ -0,0 +1,123 @@
1
+ import { fileURLToPath } from "node:url";
2
+ import type { BotAppConfig } from "./core.js";
3
+ import type { FeishuChannel } from "./channels/feishu/index.js";
4
+ import { createFeishuChannel } from "./channels/feishu/index.js";
5
+ import type { WechatChannel } from "./channels/wechat/index.js";
6
+ import { createWechatChannel } from "./channels/wechat/index.js";
7
+ import { loadBotAppConfig } from "./config.js";
8
+ import { createAgentRuntime } from "./agent.js";
9
+ import type { DashboardServer } from "./dashboard/types.js";
10
+ import { createDashboard } from "./dashboard/index.js";
11
+ import type { ConfigStore } from "./dashboard/config-store.js";
12
+
13
+ export interface BotApp {
14
+ config: BotAppConfig;
15
+ channels: {
16
+ feishu?: FeishuChannel;
17
+ wechat?: WechatChannel;
18
+ };
19
+ dashboard: DashboardServer;
20
+ start(): Promise<void>;
21
+ stop(): Promise<void>;
22
+ }
23
+
24
+ export async function createBotApp(
25
+ config: BotAppConfig = loadBotAppConfig(),
26
+ options: { dashboardConfigStore?: ConfigStore } = {},
27
+ ): Promise<BotApp> {
28
+ const runtime = await createAgentRuntime(config.agent);
29
+ const channels: BotApp["channels"] = {};
30
+
31
+ if (config.channels.feishu?.enabled) {
32
+ channels.feishu = createFeishuChannel(
33
+ {
34
+ appId: config.channels.feishu.appId,
35
+ appSecret: config.channels.feishu.appSecret,
36
+ domain: config.channels.feishu.domain,
37
+ encryptKey: config.channels.feishu.encryptKey,
38
+ verificationToken: config.channels.feishu.verificationToken,
39
+ routing: {
40
+ groupRequireMention: config.routing.feishuGroupRequireMention
41
+ }
42
+ },
43
+ {
44
+ async onMessage(message) {
45
+ const text = await runtime.run(message);
46
+ return { text };
47
+ },
48
+ async onStreamMessage(message, handlers) {
49
+ const result = await runtime.stream(message, {
50
+ onMeta: handlers.onMeta,
51
+ onDelta: handlers.onDelta,
52
+ onError: handlers.onError
53
+ });
54
+ return { text: result.finalText };
55
+ }
56
+ }
57
+ );
58
+ }
59
+
60
+ if (config.channels.wechat?.enabled) {
61
+ channels.wechat = createWechatChannel(
62
+ {
63
+ routing: {
64
+ groupRequireMention: config.routing.wechatGroupRequireMention
65
+ }
66
+ },
67
+ {
68
+ async onMessage(message) {
69
+ const text = await runtime.run(message);
70
+ return { text };
71
+ }
72
+ }
73
+ );
74
+ }
75
+
76
+ const dashboard = createDashboard({
77
+ botConfig: config,
78
+ channels,
79
+ agentRuntime: runtime,
80
+ configStore: options.dashboardConfigStore,
81
+ });
82
+
83
+ return {
84
+ config,
85
+ channels,
86
+ dashboard,
87
+ async start() {
88
+ await Promise.all([
89
+ ...Object.values(channels).map((channel) => channel.start()),
90
+ dashboard.start()
91
+ ]);
92
+ },
93
+ async stop() {
94
+ await Promise.all([
95
+ ...Object.values(channels).map((channel) => channel.stop()),
96
+ dashboard.stop()
97
+ ]);
98
+ await runtime.dispose();
99
+ }
100
+ };
101
+ }
102
+
103
+ export async function main(): Promise<void> {
104
+ const config = loadBotAppConfig();
105
+ const app = await createBotApp(config);
106
+ await app.start();
107
+
108
+ console.log(`[${config.appName}] started`);
109
+ console.log(`Feishu enabled: ${Boolean(app.channels.feishu)}`);
110
+ console.log(`WeChat enabled: ${Boolean(app.channels.wechat)}`);
111
+ console.log(`Dashboard: ${app.dashboard.getUrl()}`);
112
+ console.log(`Agent mode: ${config.agent.mode}`);
113
+ console.log(`Workspace dir: ${config.agent.cwd}`);
114
+ console.log(`Agent dir: ${config.agent.agentDir}`);
115
+ }
116
+
117
+ const entrypoint = process.argv[1];
118
+ if (entrypoint && fileURLToPath(import.meta.url) === entrypoint) {
119
+ main().catch((error: unknown) => {
120
+ console.error(error);
121
+ process.exit(1);
122
+ });
123
+ }
@@ -0,0 +1,125 @@
1
+ import { existsSync, readFileSync, readdirSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { DefaultResourceLoader, type SettingsManager } from "@mariozechner/pi-coding-agent";
4
+ import { getProjectRoot } from "./config.js";
5
+
6
+ type PiResourceLoaderArgs = {
7
+ cwd: string;
8
+ agentDir?: string;
9
+ settingsManager: SettingsManager;
10
+ };
11
+
12
+ export function getPiResourcesDir(): string {
13
+ return join(getProjectRoot(), "pi-resources");
14
+ }
15
+
16
+ export function buildPiResourceLoaderOptions(
17
+ args: PiResourceLoaderArgs
18
+ ): ConstructorParameters<typeof DefaultResourceLoader>[0] {
19
+ const piResourcesDir = getPiResourcesDir();
20
+ const repoSystemPromptPath = join(piResourcesDir, "SYSTEM.md");
21
+ const repoAppendSystemPromptPath = join(piResourcesDir, "APPEND_SYSTEM.md");
22
+ const localSystemPromptPath = join(args.cwd, ".pi", "SYSTEM.md");
23
+ const localAppendSystemPromptPath = join(args.cwd, ".pi", "APPEND_SYSTEM.md");
24
+ const systemPromptSource = pickFirstExistingPath([localSystemPromptPath, repoSystemPromptPath]);
25
+ const appendSystemPromptSource = pickFirstExistingPath([
26
+ localAppendSystemPromptPath,
27
+ repoAppendSystemPromptPath
28
+ ]);
29
+
30
+ return {
31
+ cwd: args.cwd,
32
+ agentDir: args.agentDir,
33
+ settingsManager: args.settingsManager,
34
+ additionalExtensionPaths: discoverExtensionEntries(join(piResourcesDir, "extensions")),
35
+ additionalSkillPaths: existingPaths([join(piResourcesDir, "skills")]),
36
+ additionalPromptTemplatePaths: existingPaths([join(piResourcesDir, "prompts")]),
37
+ // Keep the bot runtime independent from repo/workspace AGENTS.md files.
38
+ agentsFilesOverride: () => ({ agentsFiles: [] }),
39
+ // Make source precedence explicit instead of expressing it indirectly through
40
+ // override callbacks: workspace/.pi wins, then repo-managed resources, then the
41
+ // loader's own defaults when neither exists.
42
+ systemPrompt: systemPromptSource,
43
+ appendSystemPrompt: appendSystemPromptSource
44
+ };
45
+ }
46
+
47
+ export function createPiResourceLoader(args: PiResourceLoaderArgs): DefaultResourceLoader {
48
+ return new DefaultResourceLoader(buildPiResourceLoaderOptions(args));
49
+ }
50
+
51
+ function existingPaths(paths: string[]): string[] {
52
+ return paths.filter((path) => existsSync(path));
53
+ }
54
+
55
+ function pickFirstExistingPath(paths: string[]): string | undefined {
56
+ return paths.find((path) => existsSync(path));
57
+ }
58
+
59
+ function discoverExtensionEntries(dir: string): string[] {
60
+ if (!existsSync(dir)) {
61
+ return [];
62
+ }
63
+
64
+ const discovered = new Set<string>();
65
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
66
+ if (entry.name.startsWith(".")) {
67
+ continue;
68
+ }
69
+
70
+ const entryPath = join(dir, entry.name);
71
+ if (entry.isFile() && isExtensionModule(entry.name)) {
72
+ discovered.add(entryPath);
73
+ continue;
74
+ }
75
+
76
+ if (!entry.isDirectory()) {
77
+ continue;
78
+ }
79
+
80
+ for (const extensionEntry of resolveExtensionEntries(entryPath)) {
81
+ discovered.add(extensionEntry);
82
+ }
83
+ }
84
+
85
+ return Array.from(discovered);
86
+ }
87
+
88
+ function resolveExtensionEntries(dir: string): string[] {
89
+ const packageJsonPath = join(dir, "package.json");
90
+ if (existsSync(packageJsonPath)) {
91
+ const manifest = readPiManifest(packageJsonPath);
92
+ if (manifest?.extensions?.length) {
93
+ return manifest.extensions
94
+ .map((extensionPath) => join(dir, extensionPath))
95
+ .filter((extensionPath) => existsSync(extensionPath));
96
+ }
97
+ }
98
+
99
+ const indexTs = join(dir, "index.ts");
100
+ if (existsSync(indexTs)) {
101
+ return [indexTs];
102
+ }
103
+
104
+ const indexJs = join(dir, "index.js");
105
+ if (existsSync(indexJs)) {
106
+ return [indexJs];
107
+ }
108
+
109
+ return [];
110
+ }
111
+
112
+ function readPiManifest(packageJsonPath: string): { extensions?: string[] } | undefined {
113
+ try {
114
+ const pkg = JSON.parse(readFileSync(packageJsonPath, "utf-8")) as {
115
+ pi?: { extensions?: string[] };
116
+ };
117
+ return pkg.pi;
118
+ } catch {
119
+ return undefined;
120
+ }
121
+ }
122
+
123
+ function isExtensionModule(fileName: string): boolean {
124
+ return fileName.endsWith(".ts") || fileName.endsWith(".js");
125
+ }