@dobby.ai/dobby 0.1.0 → 0.1.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.
- package/.env.example +0 -1
- package/AGENTS.md +7 -7
- package/README.md +64 -32
- package/config/gateway.example.json +10 -6
- package/dist/plugins/connector-discord/src/mapper.js +75 -0
- package/dist/src/cli/commands/doctor.js +81 -2
- package/dist/src/cli/commands/extension.js +3 -1
- package/dist/src/cli/commands/init.js +43 -173
- package/dist/src/cli/commands/topology.js +38 -14
- package/dist/src/cli/program.js +15 -131
- package/dist/src/cli/shared/config-io.js +3 -31
- package/dist/src/cli/shared/config-mutators.js +33 -9
- package/dist/src/cli/shared/configure-sections.js +52 -12
- package/dist/src/cli/shared/init-catalog.js +89 -46
- package/dist/src/cli/shared/local-extension-specs.js +85 -0
- package/dist/src/cli/shared/schema-prompts.js +26 -2
- package/dist/src/cli/tests/config-io.test.js +5 -5
- package/dist/src/cli/tests/discord-mapper.test.js +90 -0
- package/dist/src/cli/tests/doctor.test.js +145 -0
- package/dist/src/cli/tests/init-catalog.test.js +108 -61
- package/dist/src/cli/tests/program-options.test.js +14 -28
- package/dist/src/cli/tests/routing-config.test.js +59 -4
- package/dist/src/core/gateway.js +3 -1
- package/dist/src/core/routing.js +53 -38
- package/dist/src/main.js +0 -0
- package/dist/src/shared/dobby-repo.js +40 -0
- package/docs/RUNBOOK.md +28 -27
- package/package.json +3 -2
- package/plugins/connector-discord/package-lock.json +2 -2
- package/plugins/connector-discord/package.json +1 -1
- package/plugins/connector-discord/src/connector.ts +0 -5
- package/plugins/connector-discord/src/mapper.ts +3 -4
- package/plugins/connector-feishu/package-lock.json +2 -2
- package/plugins/connector-feishu/package.json +1 -1
- package/plugins/plugin-sdk/package-lock.json +2 -2
- package/plugins/plugin-sdk/package.json +1 -1
- package/plugins/provider-claude/package-lock.json +2 -2
- package/plugins/provider-claude/package.json +1 -1
- package/plugins/provider-claude-cli/package-lock.json +2 -2
- package/plugins/provider-claude-cli/package.json +1 -1
- package/plugins/provider-pi/package-lock.json +2 -2
- package/plugins/provider-pi/package.json +1 -1
- package/plugins/provider-pi/src/contribution.ts +139 -9
- package/src/cli/commands/doctor.ts +103 -2
- package/src/cli/commands/extension.ts +3 -1
- package/src/cli/commands/init.ts +45 -230
- package/src/cli/commands/topology.ts +48 -16
- package/src/cli/program.ts +16 -167
- package/src/cli/shared/config-io.ts +3 -35
- package/src/cli/shared/config-mutators.ts +39 -9
- package/src/cli/shared/config-types.ts +10 -2
- package/src/cli/shared/configure-sections.ts +55 -11
- package/src/cli/shared/init-catalog.ts +126 -66
- package/src/cli/shared/local-extension-specs.ts +108 -0
- package/src/cli/shared/schema-prompts.ts +30 -1
- package/src/cli/tests/config-io.test.ts +5 -5
- package/src/cli/tests/discord-mapper.test.ts +128 -0
- package/src/cli/tests/doctor.test.ts +149 -0
- package/src/cli/tests/init-catalog.test.ts +112 -64
- package/src/cli/tests/program-options.test.ts +14 -32
- package/src/cli/tests/routing-config.test.ts +76 -4
- package/src/core/gateway.ts +3 -1
- package/src/core/routing.ts +70 -45
- package/src/core/types.ts +8 -2
- package/src/shared/dobby-repo.ts +48 -0
- package/config/models.custom.example.json +0 -27
- package/dist/src/agent/tests/event-forwarder.test.js +0 -113
- package/dist/src/cli/shared/config-path.js +0 -207
- package/dist/src/cli/shared/init-models-file.js +0 -65
- package/dist/src/cli/shared/presets.js +0 -86
- package/dist/src/cli/tests/config-path.test.js +0 -21
- package/dist/src/cli/tests/discord-config.test.js +0 -23
- package/dist/src/cli/tests/presets.test.js +0 -41
- package/dist/src/cli/tests/routing-legacy.test.js +0 -191
- package/dist/src/core/tests/gateway-update-strategy.test.js +0 -167
- package/src/cli/shared/init-models-file.ts +0 -77
package/dist/src/core/routing.js
CHANGED
|
@@ -1,20 +1,21 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
2
1
|
import { readFile } from "node:fs/promises";
|
|
3
2
|
import { homedir } from "node:os";
|
|
4
3
|
import { dirname, isAbsolute, resolve } from "node:path";
|
|
5
4
|
import { z } from "zod";
|
|
5
|
+
import { isDobbyRepoRoot } from "../shared/dobby-repo.js";
|
|
6
6
|
import { BUILTIN_HOST_SANDBOX_ID } from "./types.js";
|
|
7
7
|
const extensionItemSchema = z.object({
|
|
8
8
|
type: z.string().trim().min(1),
|
|
9
9
|
}).catchall(z.unknown());
|
|
10
|
-
const
|
|
10
|
+
const routeDefaultSchema = z.object({
|
|
11
|
+
projectRoot: z.string().trim().min(1).optional(),
|
|
11
12
|
provider: z.string().trim().min(1).optional(),
|
|
12
13
|
sandbox: z.string().trim().min(1).optional(),
|
|
13
14
|
tools: z.enum(["full", "readonly"]).optional(),
|
|
14
15
|
mentions: z.enum(["required", "optional"]).optional(),
|
|
15
16
|
}).strict();
|
|
16
17
|
const routeItemSchema = z.object({
|
|
17
|
-
projectRoot: z.string().trim().min(1),
|
|
18
|
+
projectRoot: z.string().trim().min(1).optional(),
|
|
18
19
|
tools: z.enum(["full", "readonly"]).optional(),
|
|
19
20
|
mentions: z.enum(["required", "optional"]).optional(),
|
|
20
21
|
provider: z.string().trim().min(1).optional(),
|
|
@@ -33,6 +34,9 @@ const bindingItemSchema = z.object({
|
|
|
33
34
|
source: bindingSourceSchema,
|
|
34
35
|
route: z.string().trim().min(1),
|
|
35
36
|
}).strict();
|
|
37
|
+
const defaultBindingSchema = z.object({
|
|
38
|
+
route: z.string().trim().min(1),
|
|
39
|
+
}).strict();
|
|
36
40
|
const gatewayConfigSchema = z.object({
|
|
37
41
|
extensions: z.object({
|
|
38
42
|
allowList: z
|
|
@@ -54,10 +58,11 @@ const gatewayConfigSchema = z.object({
|
|
|
54
58
|
items: z.record(z.string(), extensionItemSchema).default({}),
|
|
55
59
|
}).strict(),
|
|
56
60
|
routes: z.object({
|
|
57
|
-
|
|
61
|
+
default: routeDefaultSchema.default({}),
|
|
58
62
|
items: z.record(z.string(), routeItemSchema),
|
|
59
63
|
}).strict(),
|
|
60
64
|
bindings: z.object({
|
|
65
|
+
default: defaultBindingSchema.optional(),
|
|
61
66
|
items: z.record(z.string(), bindingItemSchema).default({}),
|
|
62
67
|
}).strict(),
|
|
63
68
|
data: z.object({
|
|
@@ -70,22 +75,6 @@ const FORBIDDEN_CONNECTOR_CONFIG_KEYS = {
|
|
|
70
75
|
chatRouteMap: "Use bindings.items to map connector sources to routes.",
|
|
71
76
|
botTokenEnv: "Set botToken directly in connector config or inject it before the config is loaded.",
|
|
72
77
|
};
|
|
73
|
-
function isDobbyRepoRoot(candidateDir) {
|
|
74
|
-
const packageJsonPath = resolve(candidateDir, "package.json");
|
|
75
|
-
const repoConfigPath = resolve(candidateDir, "config", "gateway.json");
|
|
76
|
-
const localExtensionsScriptPath = resolve(candidateDir, "scripts", "local-extensions.mjs");
|
|
77
|
-
if (!existsSync(packageJsonPath) || !existsSync(repoConfigPath) || !existsSync(localExtensionsScriptPath)) {
|
|
78
|
-
return false;
|
|
79
|
-
}
|
|
80
|
-
try {
|
|
81
|
-
const packageJsonRaw = readFileSync(packageJsonPath, "utf-8");
|
|
82
|
-
const parsed = JSON.parse(packageJsonRaw);
|
|
83
|
-
return parsed.name === "dobby";
|
|
84
|
-
}
|
|
85
|
-
catch {
|
|
86
|
-
return false;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
78
|
function resolveConfigBaseDir(configPath) {
|
|
90
79
|
const absoluteConfigPath = resolve(configPath);
|
|
91
80
|
const configDir = dirname(absoluteConfigPath);
|
|
@@ -147,9 +136,13 @@ function normalizeSandboxes(parsed) {
|
|
|
147
136
|
items: normalizeInstances(parsed.items),
|
|
148
137
|
};
|
|
149
138
|
}
|
|
150
|
-
function normalizeRouteProfile(baseDir, profile, defaults) {
|
|
139
|
+
function normalizeRouteProfile(routeId, baseDir, profile, defaults) {
|
|
140
|
+
const resolvedProjectRoot = profile.projectRoot ?? defaults.projectRoot;
|
|
141
|
+
if (!resolvedProjectRoot) {
|
|
142
|
+
throw new Error(`routes.items['${routeId}'].projectRoot is required when routes.default.projectRoot is not set`);
|
|
143
|
+
}
|
|
151
144
|
const normalized = {
|
|
152
|
-
projectRoot: resolveMaybeAbsolute(baseDir,
|
|
145
|
+
projectRoot: resolveMaybeAbsolute(baseDir, resolvedProjectRoot),
|
|
153
146
|
tools: profile.tools ?? defaults.tools,
|
|
154
147
|
mentions: profile.mentions ?? defaults.mentions,
|
|
155
148
|
provider: profile.provider ?? defaults.provider,
|
|
@@ -163,10 +156,10 @@ function normalizeRouteProfile(baseDir, profile, defaults) {
|
|
|
163
156
|
function normalizeRoutes(parsed, baseDir, defaults) {
|
|
164
157
|
const items = {};
|
|
165
158
|
for (const [routeId, profile] of Object.entries(parsed.items)) {
|
|
166
|
-
items[routeId] = normalizeRouteProfile(baseDir, profile, defaults);
|
|
159
|
+
items[routeId] = normalizeRouteProfile(routeId, baseDir, profile, defaults);
|
|
167
160
|
}
|
|
168
161
|
return {
|
|
169
|
-
defaults,
|
|
162
|
+
default: defaults,
|
|
170
163
|
items,
|
|
171
164
|
};
|
|
172
165
|
}
|
|
@@ -182,7 +175,10 @@ function normalizeBindings(parsed) {
|
|
|
182
175
|
route: binding.route,
|
|
183
176
|
};
|
|
184
177
|
}
|
|
185
|
-
return {
|
|
178
|
+
return {
|
|
179
|
+
...(parsed.default ? { default: { route: parsed.default.route } } : {}),
|
|
180
|
+
items,
|
|
181
|
+
};
|
|
186
182
|
}
|
|
187
183
|
function validateConnectorConfigKeys(parsed) {
|
|
188
184
|
for (const [instanceId, item] of Object.entries(parsed.items)) {
|
|
@@ -202,16 +198,16 @@ function validateReferences(parsed, normalizedRoutes) {
|
|
|
202
198
|
throw new Error(`sandboxes.default '${defaultSandbox}' does not exist in sandboxes.items`);
|
|
203
199
|
}
|
|
204
200
|
const resolvedDefaults = {
|
|
205
|
-
provider: parsed.routes.
|
|
206
|
-
sandbox: parsed.routes.
|
|
207
|
-
tools: parsed.routes.
|
|
208
|
-
mentions: parsed.routes.
|
|
201
|
+
provider: parsed.routes.default.provider ?? parsed.providers.default,
|
|
202
|
+
sandbox: parsed.routes.default.sandbox ?? parsed.sandboxes.default ?? BUILTIN_HOST_SANDBOX_ID,
|
|
203
|
+
tools: parsed.routes.default.tools ?? "full",
|
|
204
|
+
mentions: parsed.routes.default.mentions ?? "required",
|
|
209
205
|
};
|
|
210
206
|
if (!parsed.providers.items[resolvedDefaults.provider]) {
|
|
211
|
-
throw new Error(`routes.
|
|
207
|
+
throw new Error(`routes.default.provider references unknown provider '${resolvedDefaults.provider}'`);
|
|
212
208
|
}
|
|
213
209
|
if (resolvedDefaults.sandbox !== BUILTIN_HOST_SANDBOX_ID && !parsed.sandboxes.items[resolvedDefaults.sandbox]) {
|
|
214
|
-
throw new Error(`routes.
|
|
210
|
+
throw new Error(`routes.default.sandbox references unknown sandbox '${resolvedDefaults.sandbox}'`);
|
|
215
211
|
}
|
|
216
212
|
for (const [routeId, profile] of Object.entries(normalizedRoutes.items)) {
|
|
217
213
|
if (!parsed.providers.items[profile.provider]) {
|
|
@@ -236,6 +232,9 @@ function validateReferences(parsed, normalizedRoutes) {
|
|
|
236
232
|
}
|
|
237
233
|
seenSources.set(bindingKey, bindingId);
|
|
238
234
|
}
|
|
235
|
+
if (parsed.bindings.default && !normalizedRoutes.items[parsed.bindings.default.route]) {
|
|
236
|
+
throw new Error(`bindings.default.route references unknown route '${parsed.bindings.default.route}'`);
|
|
237
|
+
}
|
|
239
238
|
}
|
|
240
239
|
export async function loadGatewayConfig(configPath) {
|
|
241
240
|
const absoluteConfigPath = resolve(configPath);
|
|
@@ -244,10 +243,11 @@ export async function loadGatewayConfig(configPath) {
|
|
|
244
243
|
const parsed = gatewayConfigSchema.parse(JSON.parse(raw));
|
|
245
244
|
validateConnectorConfigKeys(parsed.connectors);
|
|
246
245
|
const routeDefaults = {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
246
|
+
...(parsed.routes.default.projectRoot ? { projectRoot: resolveMaybeAbsolute(configBaseDir, parsed.routes.default.projectRoot) } : {}),
|
|
247
|
+
provider: parsed.routes.default.provider ?? parsed.providers.default,
|
|
248
|
+
sandbox: parsed.routes.default.sandbox ?? parsed.sandboxes.default ?? BUILTIN_HOST_SANDBOX_ID,
|
|
249
|
+
tools: parsed.routes.default.tools ?? "full",
|
|
250
|
+
mentions: parsed.routes.default.mentions ?? "required",
|
|
251
251
|
};
|
|
252
252
|
const normalizedRoutes = normalizeRoutes(parsed.routes, configBaseDir, routeDefaults);
|
|
253
253
|
validateReferences(parsed, normalizedRoutes);
|
|
@@ -286,6 +286,7 @@ export class RouteResolver {
|
|
|
286
286
|
}
|
|
287
287
|
export class BindingResolver {
|
|
288
288
|
bindingsBySource = new Map();
|
|
289
|
+
defaultBinding;
|
|
289
290
|
constructor(bindings) {
|
|
290
291
|
for (const [bindingId, binding] of Object.entries(bindings.items)) {
|
|
291
292
|
this.bindingsBySource.set(this.buildKey(binding.connector, binding.source), {
|
|
@@ -293,12 +294,26 @@ export class BindingResolver {
|
|
|
293
294
|
config: binding,
|
|
294
295
|
});
|
|
295
296
|
}
|
|
297
|
+
this.defaultBinding = bindings.default
|
|
298
|
+
? {
|
|
299
|
+
bindingId: "__default__",
|
|
300
|
+
config: {
|
|
301
|
+
connector: "__default__",
|
|
302
|
+
source: {
|
|
303
|
+
type: "chat",
|
|
304
|
+
id: "__direct_message__",
|
|
305
|
+
},
|
|
306
|
+
route: bindings.default.route,
|
|
307
|
+
},
|
|
308
|
+
}
|
|
309
|
+
: null;
|
|
296
310
|
}
|
|
297
|
-
resolve(connectorId, source) {
|
|
311
|
+
resolve(connectorId, source, options) {
|
|
298
312
|
if (!connectorId.trim() || !source.id.trim()) {
|
|
299
|
-
return null;
|
|
313
|
+
return options?.isDirectMessage ? this.defaultBinding : null;
|
|
300
314
|
}
|
|
301
|
-
return this.bindingsBySource.get(this.buildKey(connectorId, source))
|
|
315
|
+
return this.bindingsBySource.get(this.buildKey(connectorId, source))
|
|
316
|
+
?? (options?.isDirectMessage ? this.defaultBinding : null);
|
|
302
317
|
}
|
|
303
318
|
buildKey(connectorId, source) {
|
|
304
319
|
return `${connectorId.trim()}:${source.type}:${source.id.trim()}`;
|
package/dist/src/main.js
CHANGED
|
File without changes
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { dirname, resolve } from "node:path";
|
|
3
|
+
const DOBBY_REPO_PACKAGE_NAMES = new Set(["dobby", "@dobby.ai/dobby"]);
|
|
4
|
+
function readPackageName(candidateDir) {
|
|
5
|
+
const packageJsonPath = resolve(candidateDir, "package.json");
|
|
6
|
+
if (!existsSync(packageJsonPath)) {
|
|
7
|
+
return undefined;
|
|
8
|
+
}
|
|
9
|
+
try {
|
|
10
|
+
const packageJsonRaw = readFileSync(packageJsonPath, "utf-8");
|
|
11
|
+
const parsed = JSON.parse(packageJsonRaw);
|
|
12
|
+
return typeof parsed.name === "string" ? parsed.name : undefined;
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export function isDobbyRepoRoot(candidateDir) {
|
|
19
|
+
const repoConfigPath = resolve(candidateDir, "config", "gateway.json");
|
|
20
|
+
const repoConfigExamplePath = resolve(candidateDir, "config", "gateway.example.json");
|
|
21
|
+
const localExtensionsScriptPath = resolve(candidateDir, "scripts", "local-extensions.mjs");
|
|
22
|
+
if ((!existsSync(repoConfigPath) && !existsSync(repoConfigExamplePath)) || !existsSync(localExtensionsScriptPath)) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
const packageName = readPackageName(candidateDir);
|
|
26
|
+
return packageName !== undefined && DOBBY_REPO_PACKAGE_NAMES.has(packageName);
|
|
27
|
+
}
|
|
28
|
+
export function findDobbyRepoRoot(startDir) {
|
|
29
|
+
let currentDir = resolve(startDir);
|
|
30
|
+
while (true) {
|
|
31
|
+
if (isDobbyRepoRoot(currentDir)) {
|
|
32
|
+
return currentDir;
|
|
33
|
+
}
|
|
34
|
+
const parentDir = dirname(currentDir);
|
|
35
|
+
if (parentDir === currentDir) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
currentDir = parentDir;
|
|
39
|
+
}
|
|
40
|
+
}
|
package/docs/RUNBOOK.md
CHANGED
|
@@ -30,14 +30,18 @@ npm run check
|
|
|
30
30
|
cp .env.example .env
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
编辑 `.env
|
|
33
|
+
编辑 `.env`,按需设置:
|
|
34
34
|
|
|
35
35
|
```bash
|
|
36
|
-
|
|
36
|
+
ANTHROPIC_API_KEY=你的真实Key
|
|
37
37
|
LOG_LEVEL=info
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
说明:
|
|
41
|
+
1. Discord connector 当前读取 `gateway.json` 里的 `connectors.items[*].botToken`,不支持 `botTokenEnv`。
|
|
42
|
+
2. `.env` 主要用于 Claude 类 provider 的鉴权变量,以及 `LOG_LEVEL` 这类进程级配置。
|
|
43
|
+
|
|
44
|
+
加载行为:
|
|
41
45
|
1. `npm run start --` 不会自动加载 `.env`,请先导出变量。
|
|
42
46
|
2. `npm run start:local --` 会通过 `--env-file-if-exists=.env` 自动加载。
|
|
43
47
|
|
|
@@ -67,37 +71,31 @@ cp config/gateway.example.json config/gateway.json
|
|
|
67
71
|
```
|
|
68
72
|
|
|
69
73
|
说明:
|
|
70
|
-
1.
|
|
71
|
-
2. 手动维护配置时,仍可参考 `config/
|
|
74
|
+
1. `dobby init` 生成的是模板配置;写入后需要先替换 `gateway.json` 里的占位值。
|
|
75
|
+
2. 手动维护配置时,仍可参考 `config/gateway.example.json`。
|
|
72
76
|
|
|
73
|
-
### 4.1 CLI config
|
|
77
|
+
### 4.1 CLI config 命令
|
|
74
78
|
|
|
75
|
-
`config`
|
|
79
|
+
`config` 现在只保留查看与 schema inspect;配置变更建议直接编辑 `gateway.json`。
|
|
76
80
|
|
|
77
81
|
可用命令:
|
|
78
82
|
|
|
79
83
|
```bash
|
|
80
84
|
dobby config show [section] [--json]
|
|
81
85
|
dobby config list [section] [--json]
|
|
82
|
-
dobby config edit
|
|
83
86
|
dobby config schema list [--json]
|
|
84
87
|
dobby config schema show <contributionId> [--json]
|
|
85
88
|
```
|
|
86
89
|
|
|
87
90
|
说明:
|
|
88
|
-
1. `
|
|
89
|
-
2.
|
|
90
|
-
3.
|
|
91
|
-
4.
|
|
92
|
-
|
|
93
|
-
旧命令映射:
|
|
94
|
-
1. `config get ...` -> `config show` 或 `config list`
|
|
95
|
-
2. `config set ...` -> `config edit`
|
|
96
|
-
3. `config unset ...` -> 使用专用删除命令(`channel unset`、`route remove`、`extension uninstall`)
|
|
91
|
+
1. `dobby init` 支持多 provider / 多 connector 选择,并会为每个所选 connector 生成一条默认 binding。
|
|
92
|
+
2. `config schema show <contributionId>` 可用于查看扩展真实接受的字段。
|
|
93
|
+
3. `dobby doctor` 除了结构校验,还会识别 `REPLACE_WITH_*` / `YOUR_*` 这类 init 占位值。
|
|
94
|
+
4. 编辑完 `gateway.json` 后,建议执行 `dobby doctor` 或直接 `dobby start` 做校验。
|
|
97
95
|
|
|
98
96
|
`init` 语义说明:
|
|
99
97
|
1. `dobby init` 仅用于首次初始化。
|
|
100
|
-
2. 若配置文件已存在,`init`
|
|
98
|
+
2. 若配置文件已存在,`init` 会直接报错;请直接编辑现有配置文件。
|
|
101
99
|
|
|
102
100
|
## 5. 关键配置说明(v3)
|
|
103
101
|
|
|
@@ -106,11 +104,11 @@ dobby config schema show <contributionId> [--json]
|
|
|
106
104
|
必须检查:
|
|
107
105
|
1. `extensions.allowList`:声明启用的扩展包(仅声明启用,不等于已安装)。
|
|
108
106
|
2. `providers.items`:至少有一个 provider 实例,并与 `providers.default` 对应。
|
|
109
|
-
3. `connectors.items`:至少有一个 connector
|
|
107
|
+
3. `connectors.items`:至少有一个 connector 实例。
|
|
110
108
|
4. `routes.items.*.projectRoot`:改成你机器上的真实目录。
|
|
111
109
|
5. `bindings.items.*`:为每个入口声明 `(connector, source.type, source.id) -> route`。
|
|
112
|
-
6. `routes.items.*.provider`:可省略;省略时走 `routes.
|
|
113
|
-
7. `routes.items.*.sandbox`:可省略;省略时走 `routes.
|
|
110
|
+
6. `routes.items.*.provider`:可省略;省略时走 `routes.default.provider`。
|
|
111
|
+
7. `routes.items.*.sandbox`:可省略;省略时走 `routes.default.sandbox`。
|
|
114
112
|
|
|
115
113
|
默认 sandbox:
|
|
116
114
|
1. 全局默认是 `sandboxes.default = "host.builtin"`。
|
|
@@ -224,19 +222,22 @@ npm run start:local --
|
|
|
224
222
|
|
|
225
223
|
## 11. 常见问题
|
|
226
224
|
|
|
227
|
-
1.
|
|
228
|
-
-
|
|
225
|
+
1. Discord 无法登录 / 提示 token 为空
|
|
226
|
+
- 检查 `gateway.json` 中 `connectors.items[*].botToken` 是否已填写;当前配置模型不支持 `botTokenEnv`。
|
|
227
|
+
|
|
228
|
+
2. `doctor` 提示 placeholder value / `REPLACE_WITH_*` / `YOUR_*`
|
|
229
|
+
- 这是 `dobby init` 生成的模板占位值还没替换。直接编辑 `gateway.json`,改成你的真实 token / appId / appSecret / channelId / chatId / projectRoot。
|
|
229
230
|
|
|
230
|
-
|
|
231
|
-
- 检查 provider 实例中的 `provider/model
|
|
231
|
+
3. `Configured model 'provider/model' not found`
|
|
232
|
+
- 检查 provider 实例中的 `provider` / `model` 配置是否有效;对 `provider.pi`,还要确认 `models` 内联列表里包含该 model。
|
|
232
233
|
|
|
233
|
-
|
|
234
|
+
4. `Extension package 'xxx' is not installed in '.../data/extensions'`
|
|
234
235
|
- 先执行 `npm run start -- extension install <package>`。
|
|
235
236
|
|
|
236
|
-
|
|
237
|
+
5. Docker 沙箱报 `container is not running` 或越界错误
|
|
237
238
|
- 检查 docker container 状态,以及 `hostWorkspaceRoot` 是否覆盖 route 的 `projectRoot`。
|
|
238
239
|
|
|
239
|
-
|
|
240
|
+
6. 机器人在群里没反应
|
|
240
241
|
- 检查入口是否已经写进 `bindings.items`。
|
|
241
242
|
- 检查是否需要 @bot(`mentions="required"`)。
|
|
242
243
|
- 检查 bot 在频道内的读写权限与消息内容权限。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dobby.ai/dobby",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Discord-first local agent gateway built on pi packages",
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"scripts": {
|
|
14
14
|
"dev": "tsx watch src/main.ts",
|
|
15
15
|
"dev:local": "node --env-file-if-exists=.env --import tsx --watch src/main.ts",
|
|
16
|
+
"prebuild": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
|
|
16
17
|
"build": "tsc -p tsconfig.json",
|
|
17
18
|
"start": "node dist/src/main.js",
|
|
18
19
|
"start:local": "node --env-file-if-exists=.env dist/src/main.js",
|
|
@@ -40,4 +41,4 @@
|
|
|
40
41
|
"tsx": "^4.20.3",
|
|
41
42
|
"typescript": "^5.9.2"
|
|
42
43
|
}
|
|
43
|
-
}
|
|
44
|
+
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dobby.ai/connector-discord",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@dobby.ai/connector-discord",
|
|
9
|
-
"version": "0.1.
|
|
9
|
+
"version": "0.1.3",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"discord.js": "^14.22.1",
|
|
12
12
|
"zod": "^4.3.6"
|
|
@@ -243,11 +243,6 @@ export class DiscordConnector implements ConnectorPlugin {
|
|
|
243
243
|
client.on("messageCreate", async (message: Message) => {
|
|
244
244
|
if (client !== this.client || !client.user || !this.ctx || !this.botUserId) return;
|
|
245
245
|
|
|
246
|
-
// v1 explicitly disables DM handling; only bound guild channels are processed.
|
|
247
|
-
if (!message.guildId) {
|
|
248
|
-
return;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
246
|
if (message.author.bot) return;
|
|
252
247
|
|
|
253
248
|
const sourceId = message.channel.isThread() && message.channel.parentId ? message.channel.parentId : message.channelId;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
|
-
import { join } from "node:path";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
3
|
import type { Message } from "discord.js";
|
|
4
4
|
import type { GatewayLogger, InboundAttachment, InboundEnvelope } from "@dobby.ai/plugin-sdk";
|
|
5
5
|
|
|
@@ -19,6 +19,7 @@ async function downloadAttachment(url: string, targetPath: string): Promise<void
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
const data = await response.arrayBuffer();
|
|
22
|
+
await mkdir(dirname(targetPath), { recursive: true });
|
|
22
23
|
await writeFile(targetPath, Buffer.from(data));
|
|
23
24
|
}
|
|
24
25
|
|
|
@@ -56,13 +57,11 @@ export async function mapDiscordMessage(
|
|
|
56
57
|
|
|
57
58
|
const cleanedText = stripBotMention(message.content ?? "", botUserId);
|
|
58
59
|
|
|
59
|
-
const attachmentDir = join(attachmentsRoot, sourceId, message.id);
|
|
60
|
-
await mkdir(attachmentDir, { recursive: true });
|
|
61
|
-
|
|
62
60
|
const attachments: InboundAttachment[] = [];
|
|
63
61
|
|
|
64
62
|
for (const attachment of message.attachments.values()) {
|
|
65
63
|
const base = mapAttachmentBase(attachment);
|
|
64
|
+
const attachmentDir = join(attachmentsRoot, sourceId, message.id);
|
|
66
65
|
const fileName = sanitizeFileName(attachment.name ?? attachment.id);
|
|
67
66
|
const localPath = join(attachmentDir, fileName);
|
|
68
67
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dobby.ai/connector-feishu",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@dobby.ai/connector-feishu",
|
|
9
|
-
"version": "0.1.
|
|
9
|
+
"version": "0.1.1",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@larksuiteoapi/node-sdk": "^1.59.0",
|
|
12
12
|
"zod": "^4.3.6"
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dobby.ai/provider-claude",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@dobby.ai/provider-claude",
|
|
9
|
-
"version": "0.1.
|
|
9
|
+
"version": "0.1.3",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@anthropic-ai/claude-agent-sdk": "^0.2.0",
|
|
12
12
|
"@mariozechner/pi-ai": "^0.53.0",
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dobby.ai/provider-claude-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@dobby.ai/provider-claude-cli",
|
|
9
|
-
"version": "0.1.
|
|
9
|
+
"version": "0.1.3",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@mariozechner/pi-ai": "^0.53.0",
|
|
12
12
|
"zod": "^4.3.6"
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dobby.ai/provider-pi",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@dobby.ai/provider-pi",
|
|
9
|
-
"version": "0.1.
|
|
9
|
+
"version": "0.1.3",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@mariozechner/pi-agent-core": "^0.53.0",
|
|
12
12
|
"@mariozechner/pi-ai": "^0.53.0",
|