@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.
Files changed (76) hide show
  1. package/.env.example +0 -1
  2. package/AGENTS.md +7 -7
  3. package/README.md +64 -32
  4. package/config/gateway.example.json +10 -6
  5. package/dist/plugins/connector-discord/src/mapper.js +75 -0
  6. package/dist/src/cli/commands/doctor.js +81 -2
  7. package/dist/src/cli/commands/extension.js +3 -1
  8. package/dist/src/cli/commands/init.js +43 -173
  9. package/dist/src/cli/commands/topology.js +38 -14
  10. package/dist/src/cli/program.js +15 -131
  11. package/dist/src/cli/shared/config-io.js +3 -31
  12. package/dist/src/cli/shared/config-mutators.js +33 -9
  13. package/dist/src/cli/shared/configure-sections.js +52 -12
  14. package/dist/src/cli/shared/init-catalog.js +89 -46
  15. package/dist/src/cli/shared/local-extension-specs.js +85 -0
  16. package/dist/src/cli/shared/schema-prompts.js +26 -2
  17. package/dist/src/cli/tests/config-io.test.js +5 -5
  18. package/dist/src/cli/tests/discord-mapper.test.js +90 -0
  19. package/dist/src/cli/tests/doctor.test.js +145 -0
  20. package/dist/src/cli/tests/init-catalog.test.js +108 -61
  21. package/dist/src/cli/tests/program-options.test.js +14 -28
  22. package/dist/src/cli/tests/routing-config.test.js +59 -4
  23. package/dist/src/core/gateway.js +3 -1
  24. package/dist/src/core/routing.js +53 -38
  25. package/dist/src/main.js +0 -0
  26. package/dist/src/shared/dobby-repo.js +40 -0
  27. package/docs/RUNBOOK.md +28 -27
  28. package/package.json +3 -2
  29. package/plugins/connector-discord/package-lock.json +2 -2
  30. package/plugins/connector-discord/package.json +1 -1
  31. package/plugins/connector-discord/src/connector.ts +0 -5
  32. package/plugins/connector-discord/src/mapper.ts +3 -4
  33. package/plugins/connector-feishu/package-lock.json +2 -2
  34. package/plugins/connector-feishu/package.json +1 -1
  35. package/plugins/plugin-sdk/package-lock.json +2 -2
  36. package/plugins/plugin-sdk/package.json +1 -1
  37. package/plugins/provider-claude/package-lock.json +2 -2
  38. package/plugins/provider-claude/package.json +1 -1
  39. package/plugins/provider-claude-cli/package-lock.json +2 -2
  40. package/plugins/provider-claude-cli/package.json +1 -1
  41. package/plugins/provider-pi/package-lock.json +2 -2
  42. package/plugins/provider-pi/package.json +1 -1
  43. package/plugins/provider-pi/src/contribution.ts +139 -9
  44. package/src/cli/commands/doctor.ts +103 -2
  45. package/src/cli/commands/extension.ts +3 -1
  46. package/src/cli/commands/init.ts +45 -230
  47. package/src/cli/commands/topology.ts +48 -16
  48. package/src/cli/program.ts +16 -167
  49. package/src/cli/shared/config-io.ts +3 -35
  50. package/src/cli/shared/config-mutators.ts +39 -9
  51. package/src/cli/shared/config-types.ts +10 -2
  52. package/src/cli/shared/configure-sections.ts +55 -11
  53. package/src/cli/shared/init-catalog.ts +126 -66
  54. package/src/cli/shared/local-extension-specs.ts +108 -0
  55. package/src/cli/shared/schema-prompts.ts +30 -1
  56. package/src/cli/tests/config-io.test.ts +5 -5
  57. package/src/cli/tests/discord-mapper.test.ts +128 -0
  58. package/src/cli/tests/doctor.test.ts +149 -0
  59. package/src/cli/tests/init-catalog.test.ts +112 -64
  60. package/src/cli/tests/program-options.test.ts +14 -32
  61. package/src/cli/tests/routing-config.test.ts +76 -4
  62. package/src/core/gateway.ts +3 -1
  63. package/src/core/routing.ts +70 -45
  64. package/src/core/types.ts +8 -2
  65. package/src/shared/dobby-repo.ts +48 -0
  66. package/config/models.custom.example.json +0 -27
  67. package/dist/src/agent/tests/event-forwarder.test.js +0 -113
  68. package/dist/src/cli/shared/config-path.js +0 -207
  69. package/dist/src/cli/shared/init-models-file.js +0 -65
  70. package/dist/src/cli/shared/presets.js +0 -86
  71. package/dist/src/cli/tests/config-path.test.js +0 -21
  72. package/dist/src/cli/tests/discord-config.test.js +0 -23
  73. package/dist/src/cli/tests/presets.test.js +0 -41
  74. package/dist/src/cli/tests/routing-legacy.test.js +0 -191
  75. package/dist/src/core/tests/gateway-update-strategy.test.js +0 -167
  76. package/src/cli/shared/init-models-file.ts +0 -77
@@ -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 routeDefaultsSchema = z.object({
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
- defaults: routeDefaultsSchema.default({}),
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, profile.projectRoot),
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 { items };
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.defaults.provider ?? parsed.providers.default,
206
- sandbox: parsed.routes.defaults.sandbox ?? parsed.sandboxes.default ?? BUILTIN_HOST_SANDBOX_ID,
207
- tools: parsed.routes.defaults.tools ?? "full",
208
- mentions: parsed.routes.defaults.mentions ?? "required",
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.defaults.provider references unknown provider '${resolvedDefaults.provider}'`);
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.defaults.sandbox references unknown sandbox '${resolvedDefaults.sandbox}'`);
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
- provider: parsed.routes.defaults.provider ?? parsed.providers.default,
248
- sandbox: parsed.routes.defaults.sandbox ?? parsed.sandboxes.default ?? BUILTIN_HOST_SANDBOX_ID,
249
- tools: parsed.routes.defaults.tools ?? "full",
250
- mentions: parsed.routes.defaults.mentions ?? "required",
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)) ?? null;
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
- DISCORD_BOT_TOKEN=你的真实Token
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. 若你使用 `dobby init` 并选择了 `provider.pi`,`models.custom.json` 会在缺失时自动生成。
71
- 2. 手动维护配置时,仍可参考 `config/models.custom.example.json`。
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` 已切换为交互与结构化查看,不再支持路径式 `get/set/unset`。
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. `config edit` `configure` 在编辑 provider/connector 实例时,会优先读取扩展暴露的 `configSchema` 动态生成字段输入。
89
- 2. 默认仅询问关键字段;带默认值的高级选项可在提示时按需展开。
90
- 3. 若某个 contribution 没有可用 `configSchema`,CLI 会先提示原因(例如扩展 disabled/未安装);未加载 schema 时会要求你确认是否继续使用 JSON 文本输入。
91
- 4. `dobby init` 也会在安装扩展后按 `configSchema` 动态询问 provider/connector 的配置字段(Discord connector 保留专用引导配置)。
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` 会直接报错;请改用 `dobby config edit` 或 `dobby configure`。
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 实例(Discord)。
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.defaults.provider`。
113
- 7. `routes.items.*.sandbox`:可省略;省略时走 `routes.defaults.sandbox`。
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. `Discord bot token env 'DISCORD_BOT_TOKEN' is not set`
228
- - 未导出环境变量,先执行 `set -a; source .env; set +a`,或使用 `npm run start:local`。
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
- 2. `Configured model 'provider/model' not found`
231
- - 检查 provider 实例中的 `provider/model/modelsFile` 是否和 `config/models.custom.json` 一致。
231
+ 3. `Configured model 'provider/model' not found`
232
+ - 检查 provider 实例中的 `provider` / `model` 配置是否有效;对 `provider.pi`,还要确认 `models` 内联列表里包含该 model。
232
233
 
233
- 3. `Extension package 'xxx' is not installed in '.../data/extensions'`
234
+ 4. `Extension package 'xxx' is not installed in '.../data/extensions'`
234
235
  - 先执行 `npm run start -- extension install <package>`。
235
236
 
236
- 4. Docker 沙箱报 `container is not running` 或越界错误
237
+ 5. Docker 沙箱报 `container is not running` 或越界错误
237
238
  - 检查 docker container 状态,以及 `hostWorkspaceRoot` 是否覆盖 route 的 `projectRoot`。
238
239
 
239
- 5. 机器人在群里没反应
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.0",
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.2",
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.2",
9
+ "version": "0.1.3",
10
10
  "dependencies": {
11
11
  "discord.js": "^14.22.1",
12
12
  "zod": "^4.3.6"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dobby.ai/connector-discord",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Built-in Discord connector extension package",
@@ -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.0",
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.0",
9
+ "version": "0.1.1",
10
10
  "dependencies": {
11
11
  "@larksuiteoapi/node-sdk": "^1.59.0",
12
12
  "zod": "^4.3.6"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dobby.ai/connector-feishu",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Built-in Feishu connector extension package",
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@dobby.ai/plugin-sdk",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@dobby.ai/plugin-sdk",
9
- "version": "0.1.2"
9
+ "version": "0.1.3"
10
10
  }
11
11
  }
12
12
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dobby.ai/plugin-sdk",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Public plugin contracts for dobby extensions",
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@dobby.ai/provider-claude",
3
- "version": "0.1.2",
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.2",
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,6 +1,6 @@
1
1
  {
2
2
  "name": "@dobby.ai/provider-claude",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Claude Agent SDK provider extension package",
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@dobby.ai/provider-claude-cli",
3
- "version": "0.1.2",
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.2",
9
+ "version": "0.1.3",
10
10
  "dependencies": {
11
11
  "@mariozechner/pi-ai": "^0.53.0",
12
12
  "zod": "^4.3.6"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dobby.ai/provider-claude-cli",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Claude CLI provider extension package",
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@dobby.ai/provider-pi",
3
- "version": "0.1.2",
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.2",
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",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dobby.ai/provider-pi",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Built-in pi provider extension package",