@dobby.ai/dobby 0.1.0 → 0.1.2

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 (156) hide show
  1. package/README.md +84 -39
  2. package/dist/src/agent/event-forwarder.js +185 -16
  3. package/dist/src/cli/commands/cron.js +39 -35
  4. package/dist/src/cli/commands/doctor.js +81 -2
  5. package/dist/src/cli/commands/extension.js +3 -1
  6. package/dist/src/cli/commands/init.js +43 -173
  7. package/dist/src/cli/commands/topology.js +38 -14
  8. package/dist/src/cli/program.js +15 -137
  9. package/dist/src/cli/shared/config-io.js +3 -31
  10. package/dist/src/cli/shared/config-mutators.js +33 -9
  11. package/dist/src/cli/shared/configure-sections.js +52 -12
  12. package/dist/src/cli/shared/init-catalog.js +89 -46
  13. package/dist/src/cli/shared/local-extension-specs.js +85 -0
  14. package/dist/src/cli/shared/schema-prompts.js +26 -2
  15. package/dist/src/core/gateway.js +3 -1
  16. package/dist/src/core/routing.js +53 -38
  17. package/dist/src/core/types.js +2 -0
  18. package/dist/src/cron/config.js +2 -2
  19. package/dist/src/cron/service.js +87 -23
  20. package/dist/src/cron/store.js +1 -1
  21. package/dist/src/main.js +0 -0
  22. package/dist/src/shared/dobby-repo.js +40 -0
  23. package/package.json +11 -4
  24. package/.env.example +0 -9
  25. package/AGENTS.md +0 -267
  26. package/ROADMAP.md +0 -34
  27. package/config/cron.example.json +0 -9
  28. package/config/gateway.example.json +0 -128
  29. package/config/models.custom.example.json +0 -27
  30. package/dist/src/agent/tests/event-forwarder.test.js +0 -113
  31. package/dist/src/cli/shared/config-path.js +0 -207
  32. package/dist/src/cli/shared/init-models-file.js +0 -65
  33. package/dist/src/cli/shared/presets.js +0 -86
  34. package/dist/src/cli/tests/config-command.test.js +0 -42
  35. package/dist/src/cli/tests/config-io.test.js +0 -64
  36. package/dist/src/cli/tests/config-mutators.test.js +0 -47
  37. package/dist/src/cli/tests/config-path.test.js +0 -21
  38. package/dist/src/cli/tests/discord-config.test.js +0 -23
  39. package/dist/src/cli/tests/doctor.test.js +0 -107
  40. package/dist/src/cli/tests/init-catalog.test.js +0 -87
  41. package/dist/src/cli/tests/presets.test.js +0 -41
  42. package/dist/src/cli/tests/program-options.test.js +0 -92
  43. package/dist/src/cli/tests/routing-config.test.js +0 -199
  44. package/dist/src/cli/tests/routing-legacy.test.js +0 -191
  45. package/dist/src/core/tests/control-command.test.js +0 -17
  46. package/dist/src/core/tests/gateway-update-strategy.test.js +0 -167
  47. package/dist/src/core/tests/runtime-registry.test.js +0 -116
  48. package/dist/src/core/tests/typing-controller.test.js +0 -103
  49. package/docs/BOXLITE_SANDBOX_FEASIBILITY.md +0 -175
  50. package/docs/CRON_SCHEDULER_DESIGN.md +0 -374
  51. package/docs/DOCKER_SANDBOX_vs_BOXLITE.md +0 -77
  52. package/docs/EXTENSION_SYSTEM_ARCHITECTURE.md +0 -119
  53. package/docs/MVP.md +0 -135
  54. package/docs/RUNBOOK.md +0 -242
  55. package/docs/TEAMWORK_HANDOFF_DESIGN.md +0 -440
  56. package/plugins/connector-discord/dobby.manifest.json +0 -18
  57. package/plugins/connector-discord/index.js +0 -1
  58. package/plugins/connector-discord/package-lock.json +0 -360
  59. package/plugins/connector-discord/package.json +0 -38
  60. package/plugins/connector-discord/src/connector.ts +0 -350
  61. package/plugins/connector-discord/src/contribution.ts +0 -21
  62. package/plugins/connector-discord/src/mapper.ts +0 -102
  63. package/plugins/connector-discord/tsconfig.json +0 -19
  64. package/plugins/connector-feishu/dobby.manifest.json +0 -18
  65. package/plugins/connector-feishu/index.js +0 -1
  66. package/plugins/connector-feishu/package-lock.json +0 -618
  67. package/plugins/connector-feishu/package.json +0 -38
  68. package/plugins/connector-feishu/src/connector.ts +0 -343
  69. package/plugins/connector-feishu/src/contribution.ts +0 -26
  70. package/plugins/connector-feishu/src/mapper.ts +0 -401
  71. package/plugins/connector-feishu/tsconfig.json +0 -19
  72. package/plugins/plugin-sdk/index.d.ts +0 -261
  73. package/plugins/plugin-sdk/index.js +0 -1
  74. package/plugins/plugin-sdk/package-lock.json +0 -12
  75. package/plugins/plugin-sdk/package.json +0 -22
  76. package/plugins/provider-claude/dobby.manifest.json +0 -17
  77. package/plugins/provider-claude/index.js +0 -1
  78. package/plugins/provider-claude/package-lock.json +0 -3398
  79. package/plugins/provider-claude/package.json +0 -39
  80. package/plugins/provider-claude/src/contribution.ts +0 -1018
  81. package/plugins/provider-claude/tsconfig.json +0 -19
  82. package/plugins/provider-claude-cli/dobby.manifest.json +0 -17
  83. package/plugins/provider-claude-cli/index.js +0 -1
  84. package/plugins/provider-claude-cli/package-lock.json +0 -2898
  85. package/plugins/provider-claude-cli/package.json +0 -38
  86. package/plugins/provider-claude-cli/src/contribution.ts +0 -1673
  87. package/plugins/provider-claude-cli/tsconfig.json +0 -19
  88. package/plugins/provider-pi/dobby.manifest.json +0 -17
  89. package/plugins/provider-pi/index.js +0 -1
  90. package/plugins/provider-pi/package-lock.json +0 -3877
  91. package/plugins/provider-pi/package.json +0 -40
  92. package/plugins/provider-pi/src/contribution.ts +0 -476
  93. package/plugins/provider-pi/tsconfig.json +0 -19
  94. package/plugins/sandbox-core/boxlite.js +0 -1
  95. package/plugins/sandbox-core/dobby.manifest.json +0 -17
  96. package/plugins/sandbox-core/docker.js +0 -1
  97. package/plugins/sandbox-core/package-lock.json +0 -136
  98. package/plugins/sandbox-core/package.json +0 -39
  99. package/plugins/sandbox-core/src/boxlite-context.ts +0 -2
  100. package/plugins/sandbox-core/src/boxlite-contribution.ts +0 -53
  101. package/plugins/sandbox-core/src/boxlite-executor.ts +0 -911
  102. package/plugins/sandbox-core/src/docker-contribution.ts +0 -43
  103. package/plugins/sandbox-core/src/docker-executor.ts +0 -217
  104. package/plugins/sandbox-core/tsconfig.json +0 -19
  105. package/scripts/local-extensions.mjs +0 -168
  106. package/src/agent/event-forwarder.ts +0 -414
  107. package/src/cli/commands/config.ts +0 -328
  108. package/src/cli/commands/configure.ts +0 -92
  109. package/src/cli/commands/cron.ts +0 -410
  110. package/src/cli/commands/doctor.ts +0 -230
  111. package/src/cli/commands/extension.ts +0 -205
  112. package/src/cli/commands/init.ts +0 -396
  113. package/src/cli/commands/start.ts +0 -223
  114. package/src/cli/commands/topology.ts +0 -383
  115. package/src/cli/index.ts +0 -9
  116. package/src/cli/program.ts +0 -465
  117. package/src/cli/shared/config-io.ts +0 -277
  118. package/src/cli/shared/config-mutators.ts +0 -440
  119. package/src/cli/shared/config-schema.ts +0 -228
  120. package/src/cli/shared/config-types.ts +0 -121
  121. package/src/cli/shared/configure-sections.ts +0 -551
  122. package/src/cli/shared/discord-config.ts +0 -14
  123. package/src/cli/shared/init-catalog.ts +0 -189
  124. package/src/cli/shared/init-models-file.ts +0 -77
  125. package/src/cli/shared/runtime.ts +0 -33
  126. package/src/cli/shared/schema-prompts.ts +0 -414
  127. package/src/cli/tests/config-command.test.ts +0 -56
  128. package/src/cli/tests/config-io.test.ts +0 -92
  129. package/src/cli/tests/config-mutators.test.ts +0 -59
  130. package/src/cli/tests/doctor.test.ts +0 -120
  131. package/src/cli/tests/init-catalog.test.ts +0 -96
  132. package/src/cli/tests/program-options.test.ts +0 -113
  133. package/src/cli/tests/routing-config.test.ts +0 -209
  134. package/src/core/control-command.ts +0 -12
  135. package/src/core/dedup-store.ts +0 -103
  136. package/src/core/gateway.ts +0 -607
  137. package/src/core/routing.ts +0 -379
  138. package/src/core/runtime-registry.ts +0 -141
  139. package/src/core/tests/control-command.test.ts +0 -20
  140. package/src/core/tests/runtime-registry.test.ts +0 -140
  141. package/src/core/tests/typing-controller.test.ts +0 -129
  142. package/src/core/types.ts +0 -318
  143. package/src/core/typing-controller.ts +0 -119
  144. package/src/cron/config.ts +0 -154
  145. package/src/cron/schedule.ts +0 -61
  146. package/src/cron/service.ts +0 -249
  147. package/src/cron/store.ts +0 -155
  148. package/src/cron/types.ts +0 -60
  149. package/src/extension/loader.ts +0 -145
  150. package/src/extension/manager.ts +0 -355
  151. package/src/extension/manifest.ts +0 -26
  152. package/src/extension/registry.ts +0 -229
  153. package/src/main.ts +0 -8
  154. package/src/sandbox/executor.ts +0 -44
  155. package/src/sandbox/host-executor.ts +0 -118
  156. package/tsconfig.json +0 -18
@@ -4,6 +4,7 @@ export class CronService {
4
4
  timer = null;
5
5
  activeRuns = new Map();
6
6
  tickInFlight = false;
7
+ tickRequested = false;
7
8
  started = false;
8
9
  stopping = false;
9
10
  constructor(options) {
@@ -23,7 +24,7 @@ export class CronService {
23
24
  const intervalMs = Math.min(this.options.config.pollIntervalMs, 60_000);
24
25
  await this.tick();
25
26
  this.timer = setInterval(() => {
26
- void this.tick();
27
+ this.requestTick();
27
28
  }, intervalMs);
28
29
  this.started = true;
29
30
  this.options.logger.info({
@@ -86,50 +87,76 @@ export class CronService {
86
87
  return;
87
88
  }
88
89
  if (this.tickInFlight) {
90
+ this.tickRequested = true;
89
91
  return;
90
92
  }
91
93
  this.tickInFlight = true;
92
94
  try {
93
- // Reload from disk on every tick so CLI mutations (cron run/update/pause/resume)
94
- // made by a separate process become visible to the long-running scheduler.
95
- await this.options.store.load();
96
- const now = Date.now();
97
- const dueJobs = this.options.store.listJobs().filter((job) => job.enabled
98
- && job.state.runningAtMs === undefined
99
- && job.state.nextRunAtMs !== undefined
100
- && job.state.nextRunAtMs <= now);
101
- for (const job of dueJobs) {
102
- if (this.activeRuns.size >= this.options.config.maxConcurrentRuns) {
103
- break;
95
+ do {
96
+ this.tickRequested = false;
97
+ // Reload from disk on every tick so CLI mutations (cron run/update/pause/resume)
98
+ // made by a separate process become visible to the long-running scheduler.
99
+ await this.options.store.load();
100
+ const now = Date.now();
101
+ const dueJobs = this.options.store.listJobs()
102
+ .filter((job) => job.state.runningAtMs === undefined
103
+ && this.dueAtMs(job, now) !== undefined)
104
+ .sort((a, b) => {
105
+ const leftDueAt = this.dueAtMs(a, now) ?? Number.MAX_SAFE_INTEGER;
106
+ const rightDueAt = this.dueAtMs(b, now) ?? Number.MAX_SAFE_INTEGER;
107
+ return leftDueAt - rightDueAt || a.createdAtMs - b.createdAtMs || a.id.localeCompare(b.id);
108
+ });
109
+ for (const job of dueJobs) {
110
+ if (this.activeRuns.size >= this.options.config.maxConcurrentRuns) {
111
+ break;
112
+ }
113
+ await this.enqueueJob(job, now);
104
114
  }
105
- await this.enqueueJob(job, now);
106
- }
115
+ } while (this.tickRequested && !this.stopping && this.options.config.enabled);
107
116
  }
108
117
  finally {
109
118
  this.tickInFlight = false;
110
119
  }
111
120
  }
112
121
  async enqueueJob(job, now) {
113
- const scheduledAtMs = job.state.nextRunAtMs ?? now;
122
+ const scheduledAtMs = job.enabled
123
+ && job.state.nextRunAtMs !== undefined
124
+ && job.state.nextRunAtMs <= now
125
+ ? job.state.nextRunAtMs
126
+ : undefined;
127
+ const manualRequestedAtMs = job.state.manualRunRequestedAtMs !== undefined
128
+ && job.state.manualRunRequestedAtMs <= now
129
+ ? job.state.manualRunRequestedAtMs
130
+ : undefined;
131
+ const trigger = scheduledAtMs !== undefined ? "schedule" : "manual";
132
+ const triggerAtMs = scheduledAtMs ?? manualRequestedAtMs ?? now;
133
+ const consumeManualRequest = manualRequestedAtMs !== undefined;
114
134
  const runContext = {
115
- runId: `${job.id}:${scheduledAtMs}`,
135
+ runId: `${job.id}:${trigger}:${triggerAtMs}`,
116
136
  jobId: job.id,
117
- scheduledAtMs,
137
+ trigger,
118
138
  };
119
- await this.options.store.updateJob(job.id, (current) => ({
120
- ...current,
121
- updatedAtMs: now,
122
- state: {
139
+ await this.options.store.updateJob(job.id, (current) => {
140
+ const nextState = {
123
141
  ...current.state,
124
142
  runningAtMs: now,
125
- },
126
- }));
143
+ };
144
+ if (consumeManualRequest) {
145
+ delete nextState.manualRunRequestedAtMs;
146
+ }
147
+ return {
148
+ ...current,
149
+ updatedAtMs: now,
150
+ state: nextState,
151
+ };
152
+ });
127
153
  const runPromise = this.executeJobRun(runContext)
128
154
  .catch((error) => {
129
155
  this.options.logger.warn({ err: error, jobId: runContext.jobId, runId: runContext.runId }, "Cron run failed");
130
156
  })
131
157
  .finally(() => {
132
158
  this.activeRuns.delete(runContext.runId);
159
+ this.requestTick();
133
160
  });
134
161
  this.activeRuns.set(runContext.runId, runPromise);
135
162
  }
@@ -160,6 +187,19 @@ export class CronService {
160
187
  const endedAtMs = Date.now();
161
188
  await this.options.store.updateJob(job.id, (current) => {
162
189
  const isSuccess = status === "ok";
190
+ if (run.trigger === "manual") {
191
+ return {
192
+ ...current,
193
+ updatedAtMs: endedAtMs,
194
+ state: {
195
+ ...current.state,
196
+ runningAtMs: undefined,
197
+ lastRunAtMs: endedAtMs,
198
+ lastStatus: isSuccess ? "ok" : "error",
199
+ lastError: isSuccess ? undefined : errorMessage,
200
+ },
201
+ };
202
+ }
163
203
  const previousErrors = current.state.consecutiveErrors ?? 0;
164
204
  const nextErrorCount = isSuccess ? 0 : previousErrors + 1;
165
205
  const nextRunAtMs = isSuccess
@@ -193,4 +233,28 @@ export class CronService {
193
233
  };
194
234
  await this.options.store.appendRunLog(runRecord);
195
235
  }
236
+ dueAtMs(job, now) {
237
+ const scheduledAtMs = job.enabled
238
+ && job.state.nextRunAtMs !== undefined
239
+ && job.state.nextRunAtMs <= now
240
+ ? job.state.nextRunAtMs
241
+ : undefined;
242
+ if (scheduledAtMs !== undefined) {
243
+ return scheduledAtMs;
244
+ }
245
+ if (job.state.manualRunRequestedAtMs !== undefined && job.state.manualRunRequestedAtMs <= now) {
246
+ return job.state.manualRunRequestedAtMs;
247
+ }
248
+ return undefined;
249
+ }
250
+ requestTick() {
251
+ if (this.stopping || !this.options.config.enabled) {
252
+ return;
253
+ }
254
+ if (this.tickInFlight) {
255
+ this.tickRequested = true;
256
+ return;
257
+ }
258
+ void this.tick();
259
+ }
196
260
  }
@@ -21,7 +21,6 @@ const scheduledJobSchema = z.object({
21
21
  name: z.string().min(1),
22
22
  enabled: z.boolean(),
23
23
  schedule: jobScheduleSchema,
24
- sessionPolicy: z.enum(["stateless", "shared-session"]).optional(),
25
24
  prompt: z.string(),
26
25
  delivery: z.object({
27
26
  connectorId: z.string().min(1),
@@ -33,6 +32,7 @@ const scheduledJobSchema = z.object({
33
32
  updatedAtMs: z.number().int().nonnegative(),
34
33
  state: z.object({
35
34
  nextRunAtMs: z.number().int().nonnegative().optional(),
35
+ manualRunRequestedAtMs: z.number().int().nonnegative().optional(),
36
36
  runningAtMs: z.number().int().nonnegative().optional(),
37
37
  lastRunAtMs: z.number().int().nonnegative().optional(),
38
38
  lastStatus: z.enum(["ok", "error", "skipped"]).optional(),
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/package.json CHANGED
@@ -1,19 +1,24 @@
1
1
  {
2
2
  "name": "@dobby.ai/dobby",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Discord-first local agent gateway built on pi packages",
7
7
  "bin": {
8
8
  "dobby": "dist/src/main.js"
9
9
  },
10
+ "files": [
11
+ "dist/src/"
12
+ ],
10
13
  "engines": {
11
14
  "node": ">=20.0.0"
12
15
  },
13
16
  "scripts": {
14
17
  "dev": "tsx watch src/main.ts",
15
18
  "dev:local": "node --env-file-if-exists=.env --import tsx --watch src/main.ts",
16
- "build": "tsc -p tsconfig.json",
19
+ "prebuild": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
20
+ "build": "tsc -p tsconfig.build.json",
21
+ "prepack": "npm run build",
17
22
  "start": "node dist/src/main.js",
18
23
  "start:local": "node --env-file-if-exists=.env dist/src/main.js",
19
24
  "check": "tsc --noEmit",
@@ -23,7 +28,9 @@
23
28
  "plugins:build": "node scripts/local-extensions.mjs build",
24
29
  "extensions:install:local": "node scripts/local-extensions.mjs install-store",
25
30
  "extensions:list:local": "node scripts/local-extensions.mjs list-store",
26
- "plugins:setup:local": "node scripts/local-extensions.mjs setup"
31
+ "plugins:setup:local": "node scripts/local-extensions.mjs setup",
32
+ "publish:release": "node scripts/publish-packages.mjs",
33
+ "publish:release:dry-run": "node scripts/publish-packages.mjs --dry-run"
27
34
  },
28
35
  "dependencies": {
29
36
  "@clack/prompts": "^0.10.1",
@@ -40,4 +47,4 @@
40
47
  "tsx": "^4.20.3",
41
48
  "typescript": "^5.9.2"
42
49
  }
43
- }
50
+ }
package/.env.example DELETED
@@ -1,9 +0,0 @@
1
- DISCORD_BOT_TOKEN=replace_me
2
- ANTHROPIC_AUTH_TOKEN=replace_me
3
- ANTHROPIC_API_KEY=replace_me
4
- ANTHROPIC_BASE_URL=
5
- ANTHROPIC_MODEL=
6
- API_TIMEOUT_MS=600000
7
- CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1
8
- CUSTOM_PROVIDER_AUTH_TOKEN=replace_me
9
- LOG_LEVEL=info
package/AGENTS.md DELETED
@@ -1,267 +0,0 @@
1
- # AGENTS Guide (dobby)
2
-
3
- 本文件给在本仓库内工作的 AI / 自动化代理使用。目标是让文档、代码和配置改动都以当前实现为准,而不是沿用已经过时的假设。
4
-
5
- ## 1. 项目定位
6
-
7
- - 项目名:`dobby`
8
- - 形态:Discord-first 本地 Agent Gateway,宿主负责 CLI、网关主流程、扩展加载、计划任务调度
9
- - 扩展模型:Provider / Connector / Sandbox 全部通过扩展 contribution 接入
10
- - 运行时扩展目录固定:`<data.rootDir>/extensions`
11
- - 启用模型:`extensions.allowList` 只声明“允许加载”,安装与卸载由 `dobby extension *` 管理
12
- - 当前仓库内维护的扩展包 scope 是 `@dobby.ai/*`
13
- - 代码事实优先级:
14
- - 第一优先级:`src/*`
15
- - 第二优先级:`config/gateway.example.json`、`config/cron.example.json`
16
- - 第三优先级:`docs/*`
17
- - 不要把仓库中的 `config/gateway.json` 当作通用示例,它可能包含本地机器路径、私有包名或敏感 token
18
-
19
- ## 2. 技术栈与常用命令
20
-
21
- - Node.js `>=20`
22
- - TypeScript + ESM(`module: NodeNext`,`strict: true`)
23
- - 宿主核心依赖:`commander`、`pino`、`zod`、`ajv`、`@mariozechner/pi-ai`、`cron-parser`
24
-
25
- 仓库根目录常用命令:
26
-
27
- ```bash
28
- npm install
29
- npm run check
30
- npm run build
31
- npm run test:cli
32
- npm run start --
33
- npm run start -- doctor
34
- DOBBY_CONFIG_PATH=./config/gateway.json npm run start --
35
- ```
36
-
37
- 本地开发辅助命令:
38
-
39
- ```bash
40
- npm run dev
41
- npm run dev:local
42
- npm run start:local
43
- ```
44
-
45
- 插件开发命令(由 `scripts/local-extensions.mjs` 驱动):
46
-
47
- ```bash
48
- npm run plugins:install
49
- npm run plugins:check
50
- npm run plugins:build
51
- npm run extensions:install:local
52
- npm run extensions:list:local
53
- npm run plugins:setup:local
54
- ```
55
-
56
- 注意:
57
-
58
- - 普通运行路径不会自动读取 `.env`
59
- - 只有 `npm run dev:local` 和 `npm run start:local` 使用 `--env-file-if-exists=.env`
60
- - Discord connector 仍以显式 `botToken` 配置为主
61
-
62
- ## 3. 代码结构与职责
63
-
64
- - `src/main.ts`
65
- - CLI 入口,仅调用 `runCli`
66
- - `src/cli/program.ts`
67
- - 注册全部顶层命令:`start`、`init`、`configure`、`config`、`bot`、`channel`、`route`、`extension`、`doctor`、`cron`
68
- - `src/cli/commands/start.ts`
69
- - 启动网关、创建数据目录、加载扩展、实例化 provider / connector / sandbox、启动 cron 服务、接管优雅退出
70
- - `src/cli/commands/init.ts`
71
- - 首次初始化向导;会先安装所需扩展,再根据扩展 `configSchema` 交互生成配置
72
- - `src/cli/commands/config*.ts`
73
- - `config show|list|edit|schema *` 与 `configure`
74
- - `src/cli/commands/topology.ts`
75
- - `bot/channel/route` 管理命令
76
- - `src/cli/commands/extension.ts`
77
- - 运行时扩展 store 的安装、卸载、列举,以及 `--enable` 自动写入 allowList / 模板实例
78
- - `src/cli/commands/cron.ts`
79
- - 计划任务 CRUD、状态查看、手动触发
80
- - `src/core/gateway.ts`
81
- - 入站消息与计划任务的统一执行入口:去重、控制命令、路由解析、mention 策略、runtime 获取、事件转发、错误回写
82
- - `src/core/runtime-registry.ts`
83
- - conversation 级 runtime 复用、串行队列、取消、reset、closeAll
84
- - `src/core/control-command.ts`
85
- - 控制命令解析:`stop` / `/stop` / `/cancel` / `/new` / `/reset`
86
- - `src/core/routing.ts`
87
- - `gateway.json` 的 zod 校验、相对路径归一化、legacy 字段拒绝、引用关系校验
88
- - `src/core/dedup-store.ts`
89
- - 去重 TTL 持久化,文件位置:`data/state/dedup.json`
90
- - `src/agent/event-forwarder.ts`
91
- - 统一处理流式增量、tool 事件、不同 connector `updateStrategy`
92
- - `src/extension/manager.ts`
93
- - 初始化扩展 store、调用 npm 安装 / 卸载、列出已安装扩展
94
- - `src/extension/loader.ts`
95
- - 只从 `<data.rootDir>/extensions/node_modules` 解析 allowList 包并加载 contribution
96
- - `src/extension/registry.ts`
97
- - 注册 contribution,暴露实例创建与 `configSchema` catalog
98
- - `src/cron/*`
99
- - cron 配置解析、状态存储、调度与失败退避
100
- - `src/sandbox/*`
101
- - 宿主执行器接口和内置 `HostExecutor`
102
- - `plugins/*`
103
- - 本地维护的扩展源码:`connector-discord`、`provider-pi`、`provider-claude-cli`、`provider-claude`、`sandbox-core`、`plugin-sdk`
104
- - 运行时不会从这里 fallback 加载
105
-
106
- ## 4. 配置模型不变量
107
-
108
- 配置入口是 `gateway.json`,结构以 `src/core/routing.ts` 为准。
109
-
110
- 必须保持以下事实成立:
111
-
112
- - `providers.default` 必须存在于 `providers.items`
113
- - `sandboxes.default` 若存在且不是 `host.builtin`,必须存在于 `sandboxes.items`
114
- - `routes.defaults.provider` 若未设置,运行时回落到 `providers.default`
115
- - `routes.defaults.sandbox` 若未设置,运行时回落到 `sandboxes.default ?? host.builtin`
116
- - 每条 route 在加载后都会补全 `provider`、`sandbox`、`tools`、`mentions`
117
- - `bindings.items[*].connector` 必须指向存在的 `connectors.items`
118
- - `bindings.items[*].route` 必须指向存在的 `routes.items`
119
- - 同一个 `(connector, source.type, source.id)` 只能出现一次
120
- - `providers/connectors/sandboxes.items[*].type` 必须能在“已安装且已启用的扩展 contribution”里找到
121
- - `data.rootDir`、`projectRoot`、`systemPromptFile` 都会在加载时转成绝对路径
122
- - `data.rootDir` 的相对路径有一条特殊规则:
123
- - 如果配置文件位于 `.../config/gateway.json` 且父目录是 dobby 仓库根目录,则相对仓库根目录解析
124
- - 否则相对配置文件所在目录解析
125
- - `projectRoot`、`systemPromptFile` 等其他相对路径统一相对配置文件所在目录解析
126
- - connector 私有 config 不能再承载入口映射;入口绑定统一写在 `bindings.items`
127
-
128
- ## 5. 运行时行为不变量
129
-
130
- - 会话串行粒度:
131
- - `connectorId + platform + accountId + chatId + threadId(root)`
132
- - 去重键:
133
- - `connectorId + platform + accountId + chatId + messageId`
134
- - 线程路由规则:
135
- - Discord 线程消息使用父频道 ID 查 `bindings.items`
136
- - Discord connector 当前只处理已绑定的 guild channel
137
- - DM 在 connector 侧被直接忽略,虽然核心类型保留了 `isDirectMessage`
138
- - mention 策略:
139
- - `mentions="required"` 时,群聊消息必须 @bot 才会进入 runtime
140
- - 控制命令:
141
- - `stop`、`/stop`、`/cancel` 会取消当前与排队中的该会话任务
142
- - `/new`、`/reset` 会 reset runtime,并在 provider 支持时归档历史 session
143
- - 附件处理:
144
- - Discord / Feishu 附件优先下载到 `data/attachments/<connectorId>/<source.id>/<messageId>/...`
145
- - 图片转为 `session.prompt(..., { images })`
146
- - 非图片附件路径或远程 URL 以 `<attachments>...</attachments>` 注入 prompt
147
- - 流式输出:
148
- - 行为受 connector `updateStrategy` 控制,当前 Discord 是 `edit`
149
- - 错误策略:
150
- - 主流程异常会向 connector 写回 `Error: ...`
151
- - 流式更新、typing、tool 状态发送失败只记 warning,不应打崩进程
152
-
153
- ## 6. 扩展系统约束
154
-
155
- - 扩展包必须包含 `dobby.manifest.json`
156
- - `manifest.contributions[*].entry` 必须指向包内已构建的 `.js/.mjs/.cjs`
157
- - entry 必须位于包根目录内部,禁止路径越界
158
- - 模块导出必须提供有效 contribution,对应 `kind` 必须和 manifest 一致
159
- - 运行时加载来源只有 `<data.rootDir>/extensions/node_modules`
160
- - 宿主不会从自身依赖树、`plugins/*` 源码目录或 `dist` 外路径 fallback
161
- - `configSchema` 是可选的
162
- - `init`、`configure`、`config edit` 会优先按 schema 交互提问
163
- - `applyAndValidateContributionSchemas` 会用 Ajv 套默认值并验证实例配置
164
-
165
- 当前仓库内的扩展源码与 contribution:
166
-
167
- - `@dobby.ai/connector-discord` -> `connector.discord`
168
- - `@dobby.ai/provider-pi` -> `provider.pi`
169
- - `@dobby.ai/provider-claude-cli` -> `provider.claude-cli`
170
- - `@dobby.ai/provider-claude` -> `provider.claude`
171
- - `@dobby.ai/sandbox-core` -> `sandbox.boxlite`、`sandbox.docker`
172
-
173
- 注意:
174
-
175
- - `dobby init` 当前只内建选择 `provider.pi`、`provider.claude-cli` 和 `connector.discord`
176
- - `provider.claude` 与 sandbox 扩展需要手工安装 / 启用 / 配置
177
-
178
- ## 7. Cron / 计划任务约束
179
-
180
- - 启动时总会加载 cron 配置,并在缺失时自动创建默认文件
181
- - cron 配置路径优先级:
182
- - `--cron-config`
183
- - `DOBBY_CRON_CONFIG_PATH`
184
- - 与 gateway 配置同目录下的 `cron.json`
185
- - fallback 到 `<data.rootDir>/state/cron.config.json`
186
- - job 支持三种 schedule:
187
- - `at`
188
- - `every`
189
- - `cron`
190
- - 状态存储:
191
- - job store:`cron-jobs.json`
192
- - run log:`cron-runs.jsonl`
193
- - 调度器支持:
194
- - `maxConcurrentRuns`
195
- - 启动时补跑 `runMissedOnStartup`
196
- - 连续失败退避重试
197
- - 当前真实运行语义:
198
- - 所有 scheduled run 都走 `Gateway.handleScheduled`
199
- - conversation key 固定为 `cron:<runId>`
200
- - runtime 始终按 `stateless + ephemeral` 执行
201
- - `cron` CLI / store 虽然有 `sessionPolicy` 字段,但当前调度路径没有把它传到运行时
202
-
203
- ## 8. 文档和代码改动建议
204
-
205
- - 改配置模型时,同步检查:
206
- - `src/core/types.ts`
207
- - `src/core/routing.ts`
208
- - `src/cli/shared/config-types.ts`
209
- - `src/cli/shared/config-mutators.ts`
210
- - `src/cli/shared/configure-sections.ts`
211
- - `config/gateway.example.json`
212
- - `README.md`
213
- - 改 CLI 时,同步检查:
214
- - `src/cli/program.ts`
215
- - 对应 `src/cli/commands/*.ts`
216
- - `README.md`
217
- - 改扩展加载链路时,同步检查:
218
- - `src/extension/manager.ts`
219
- - `src/extension/loader.ts`
220
- - `src/extension/registry.ts`
221
- - `docs/EXTENSION_SYSTEM_ARCHITECTURE.md`
222
- - 改计划任务时,同步检查:
223
- - `src/cron/config.ts`
224
- - `src/cron/store.ts`
225
- - `src/cron/service.ts`
226
- - `src/cli/commands/cron.ts`
227
- - `config/cron.example.json`
228
- - `docs/CRON_SCHEDULER_DESIGN.md`
229
- - 涉及执行器和路径边界时优先保守,避免放宽 `projectRoot` / `workspaceRoot` 约束
230
-
231
- ## 9. 提交前最小校验
232
-
233
- 默认最小校验:
234
-
235
- ```bash
236
- npm run check
237
- npm run build
238
- npm run test:cli
239
- ```
240
-
241
- 如果改了插件实现,额外执行:
242
-
243
- ```bash
244
- npm run plugins:check
245
- npm run plugins:build
246
- ```
247
-
248
- 如果改了扩展安装 / 加载链路,建议再执行:
249
-
250
- ```bash
251
- npm run extensions:list:local
252
- ```
253
-
254
- 手工冒烟建议:
255
-
256
- 1. 启动后确认日志包含 `Extension packages loaded`、`Discord connector ready`、`Cron scheduler started`(若启用)以及 `Gateway started`
257
- 2. 在映射频道 @bot 发消息,确认流式更新与 typing
258
- 3. 发送 `stop` 或 `/cancel`,确认能取消当前会话
259
- 4. 发送 `/new` 或 `/reset`,确认会话被重置且 provider 归档成功
260
- 5. 若启用 cron,执行 `dobby cron run <jobId>` 并确认正在运行的 gateway 会在下一次 scheduler tick 执行任务
261
-
262
- ## 10. 当前已知边界
263
-
264
- - 有一批 focused tests,但仍缺少端到端自动化测试
265
- - cron job 的 `sessionPolicy` 目前是 schema / CLI 字段,调度执行时未生效
266
- - Discord connector 仍不处理 DM
267
- - `extension uninstall` 不会自动清理 `gateway.json` 中的 allowList 和实例引用
package/ROADMAP.md DELETED
@@ -1,34 +0,0 @@
1
- # ROADMAP
2
-
3
- Work In Progress
4
-
5
- 下面列出一些个人觉得比较有意思的功能
6
-
7
- ## session 管理
8
-
9
- 如何手动结束当前会话,场景:会话轮数太多,用户想要手动结束会话,开启新话题
10
-
11
- “用户打断”时怎么处理当前运行,非常贴近真实聊天场景
12
-
13
- ## 配置功能
14
-
15
- 如何让用户配置的更加简便
16
-
17
- ## skills
18
-
19
- 这个好像直接用 claude code 和 pi-mono 就可以实现,需要找到一些比较好用的 skills
20
-
21
- - X 上的热点追踪 social listening 功能
22
- - 机票价格助手 trip Google Flights
23
-
24
- ## agents 协同
25
-
26
- 如何让多个 agent 协同工作,handoff 等等
27
-
28
- ## 调度
29
-
30
- 如何让 agent 定时工作
31
-
32
- ## 插件
33
-
34
- - codex cli
@@ -1,9 +0,0 @@
1
- {
2
- "enabled": true,
3
- "storeFile": "./data/state/cron-jobs.json",
4
- "runLogFile": "./data/state/cron-runs.jsonl",
5
- "pollIntervalMs": 10000,
6
- "maxConcurrentRuns": 1,
7
- "runMissedOnStartup": true,
8
- "jobTimeoutMs": 600000
9
- }