@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.
- package/README.md +84 -39
- package/dist/src/agent/event-forwarder.js +185 -16
- package/dist/src/cli/commands/cron.js +39 -35
- 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 -137
- 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/core/gateway.js +3 -1
- package/dist/src/core/routing.js +53 -38
- package/dist/src/core/types.js +2 -0
- package/dist/src/cron/config.js +2 -2
- package/dist/src/cron/service.js +87 -23
- package/dist/src/cron/store.js +1 -1
- package/dist/src/main.js +0 -0
- package/dist/src/shared/dobby-repo.js +40 -0
- package/package.json +11 -4
- package/.env.example +0 -9
- package/AGENTS.md +0 -267
- package/ROADMAP.md +0 -34
- package/config/cron.example.json +0 -9
- package/config/gateway.example.json +0 -128
- 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-command.test.js +0 -42
- package/dist/src/cli/tests/config-io.test.js +0 -64
- package/dist/src/cli/tests/config-mutators.test.js +0 -47
- 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/doctor.test.js +0 -107
- package/dist/src/cli/tests/init-catalog.test.js +0 -87
- package/dist/src/cli/tests/presets.test.js +0 -41
- package/dist/src/cli/tests/program-options.test.js +0 -92
- package/dist/src/cli/tests/routing-config.test.js +0 -199
- package/dist/src/cli/tests/routing-legacy.test.js +0 -191
- package/dist/src/core/tests/control-command.test.js +0 -17
- package/dist/src/core/tests/gateway-update-strategy.test.js +0 -167
- package/dist/src/core/tests/runtime-registry.test.js +0 -116
- package/dist/src/core/tests/typing-controller.test.js +0 -103
- package/docs/BOXLITE_SANDBOX_FEASIBILITY.md +0 -175
- package/docs/CRON_SCHEDULER_DESIGN.md +0 -374
- package/docs/DOCKER_SANDBOX_vs_BOXLITE.md +0 -77
- package/docs/EXTENSION_SYSTEM_ARCHITECTURE.md +0 -119
- package/docs/MVP.md +0 -135
- package/docs/RUNBOOK.md +0 -242
- package/docs/TEAMWORK_HANDOFF_DESIGN.md +0 -440
- package/plugins/connector-discord/dobby.manifest.json +0 -18
- package/plugins/connector-discord/index.js +0 -1
- package/plugins/connector-discord/package-lock.json +0 -360
- package/plugins/connector-discord/package.json +0 -38
- package/plugins/connector-discord/src/connector.ts +0 -350
- package/plugins/connector-discord/src/contribution.ts +0 -21
- package/plugins/connector-discord/src/mapper.ts +0 -102
- package/plugins/connector-discord/tsconfig.json +0 -19
- package/plugins/connector-feishu/dobby.manifest.json +0 -18
- package/plugins/connector-feishu/index.js +0 -1
- package/plugins/connector-feishu/package-lock.json +0 -618
- package/plugins/connector-feishu/package.json +0 -38
- package/plugins/connector-feishu/src/connector.ts +0 -343
- package/plugins/connector-feishu/src/contribution.ts +0 -26
- package/plugins/connector-feishu/src/mapper.ts +0 -401
- package/plugins/connector-feishu/tsconfig.json +0 -19
- package/plugins/plugin-sdk/index.d.ts +0 -261
- package/plugins/plugin-sdk/index.js +0 -1
- package/plugins/plugin-sdk/package-lock.json +0 -12
- package/plugins/plugin-sdk/package.json +0 -22
- package/plugins/provider-claude/dobby.manifest.json +0 -17
- package/plugins/provider-claude/index.js +0 -1
- package/plugins/provider-claude/package-lock.json +0 -3398
- package/plugins/provider-claude/package.json +0 -39
- package/plugins/provider-claude/src/contribution.ts +0 -1018
- package/plugins/provider-claude/tsconfig.json +0 -19
- package/plugins/provider-claude-cli/dobby.manifest.json +0 -17
- package/plugins/provider-claude-cli/index.js +0 -1
- package/plugins/provider-claude-cli/package-lock.json +0 -2898
- package/plugins/provider-claude-cli/package.json +0 -38
- package/plugins/provider-claude-cli/src/contribution.ts +0 -1673
- package/plugins/provider-claude-cli/tsconfig.json +0 -19
- package/plugins/provider-pi/dobby.manifest.json +0 -17
- package/plugins/provider-pi/index.js +0 -1
- package/plugins/provider-pi/package-lock.json +0 -3877
- package/plugins/provider-pi/package.json +0 -40
- package/plugins/provider-pi/src/contribution.ts +0 -476
- package/plugins/provider-pi/tsconfig.json +0 -19
- package/plugins/sandbox-core/boxlite.js +0 -1
- package/plugins/sandbox-core/dobby.manifest.json +0 -17
- package/plugins/sandbox-core/docker.js +0 -1
- package/plugins/sandbox-core/package-lock.json +0 -136
- package/plugins/sandbox-core/package.json +0 -39
- package/plugins/sandbox-core/src/boxlite-context.ts +0 -2
- package/plugins/sandbox-core/src/boxlite-contribution.ts +0 -53
- package/plugins/sandbox-core/src/boxlite-executor.ts +0 -911
- package/plugins/sandbox-core/src/docker-contribution.ts +0 -43
- package/plugins/sandbox-core/src/docker-executor.ts +0 -217
- package/plugins/sandbox-core/tsconfig.json +0 -19
- package/scripts/local-extensions.mjs +0 -168
- package/src/agent/event-forwarder.ts +0 -414
- package/src/cli/commands/config.ts +0 -328
- package/src/cli/commands/configure.ts +0 -92
- package/src/cli/commands/cron.ts +0 -410
- package/src/cli/commands/doctor.ts +0 -230
- package/src/cli/commands/extension.ts +0 -205
- package/src/cli/commands/init.ts +0 -396
- package/src/cli/commands/start.ts +0 -223
- package/src/cli/commands/topology.ts +0 -383
- package/src/cli/index.ts +0 -9
- package/src/cli/program.ts +0 -465
- package/src/cli/shared/config-io.ts +0 -277
- package/src/cli/shared/config-mutators.ts +0 -440
- package/src/cli/shared/config-schema.ts +0 -228
- package/src/cli/shared/config-types.ts +0 -121
- package/src/cli/shared/configure-sections.ts +0 -551
- package/src/cli/shared/discord-config.ts +0 -14
- package/src/cli/shared/init-catalog.ts +0 -189
- package/src/cli/shared/init-models-file.ts +0 -77
- package/src/cli/shared/runtime.ts +0 -33
- package/src/cli/shared/schema-prompts.ts +0 -414
- package/src/cli/tests/config-command.test.ts +0 -56
- package/src/cli/tests/config-io.test.ts +0 -92
- package/src/cli/tests/config-mutators.test.ts +0 -59
- package/src/cli/tests/doctor.test.ts +0 -120
- package/src/cli/tests/init-catalog.test.ts +0 -96
- package/src/cli/tests/program-options.test.ts +0 -113
- package/src/cli/tests/routing-config.test.ts +0 -209
- package/src/core/control-command.ts +0 -12
- package/src/core/dedup-store.ts +0 -103
- package/src/core/gateway.ts +0 -607
- package/src/core/routing.ts +0 -379
- package/src/core/runtime-registry.ts +0 -141
- package/src/core/tests/control-command.test.ts +0 -20
- package/src/core/tests/runtime-registry.test.ts +0 -140
- package/src/core/tests/typing-controller.test.ts +0 -129
- package/src/core/types.ts +0 -318
- package/src/core/typing-controller.ts +0 -119
- package/src/cron/config.ts +0 -154
- package/src/cron/schedule.ts +0 -61
- package/src/cron/service.ts +0 -249
- package/src/cron/store.ts +0 -155
- package/src/cron/types.ts +0 -60
- package/src/extension/loader.ts +0 -145
- package/src/extension/manager.ts +0 -355
- package/src/extension/manifest.ts +0 -26
- package/src/extension/registry.ts +0 -229
- package/src/main.ts +0 -8
- package/src/sandbox/executor.ts +0 -44
- package/src/sandbox/host-executor.ts +0 -118
- package/tsconfig.json +0 -18
package/dist/src/cli/program.js
CHANGED
|
@@ -1,12 +1,22 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
1
3
|
import { Command } from "commander";
|
|
2
|
-
import {
|
|
3
|
-
import { runConfigureCommand } from "./commands/configure.js";
|
|
4
|
+
import { runConfigListCommand, runConfigSchemaListCommand, runConfigSchemaShowCommand, runConfigShowCommand, } from "./commands/config.js";
|
|
4
5
|
import { runCronAddCommand, runCronListCommand, runCronPauseCommand, runCronRemoveCommand, runCronResumeCommand, runCronRunCommand, runCronStatusCommand, runCronUpdateCommand, } from "./commands/cron.js";
|
|
5
6
|
import { runDoctorCommand } from "./commands/doctor.js";
|
|
6
7
|
import { runExtensionInstallCommand, runExtensionListCommand, runExtensionUninstallCommand, } from "./commands/extension.js";
|
|
7
8
|
import { runInitCommand } from "./commands/init.js";
|
|
8
9
|
import { runStartCommand } from "./commands/start.js";
|
|
9
|
-
|
|
10
|
+
function loadCliVersion() {
|
|
11
|
+
const candidates = [
|
|
12
|
+
fileURLToPath(new URL("../../package.json", import.meta.url)),
|
|
13
|
+
fileURLToPath(new URL("../../../package.json", import.meta.url)),
|
|
14
|
+
];
|
|
15
|
+
const fallbackCandidate = fileURLToPath(new URL("../../package.json", import.meta.url));
|
|
16
|
+
const packageJsonPath = candidates.find((candidate) => existsSync(candidate)) ?? fallbackCandidate;
|
|
17
|
+
return JSON.parse(readFileSync(packageJsonPath, "utf-8")).version;
|
|
18
|
+
}
|
|
19
|
+
const CLI_VERSION = loadCliVersion();
|
|
10
20
|
/**
|
|
11
21
|
* Builds the top-level dobby CLI program and registers all subcommands.
|
|
12
22
|
*/
|
|
@@ -14,6 +24,7 @@ export function buildProgram() {
|
|
|
14
24
|
const program = new Command();
|
|
15
25
|
program
|
|
16
26
|
.name("dobby")
|
|
27
|
+
.version(CLI_VERSION)
|
|
17
28
|
.description("Discord-first local agent gateway")
|
|
18
29
|
.showHelpAfterError()
|
|
19
30
|
.action(async () => {
|
|
@@ -31,125 +42,7 @@ export function buildProgram() {
|
|
|
31
42
|
.action(async () => {
|
|
32
43
|
await runInitCommand();
|
|
33
44
|
});
|
|
34
|
-
program
|
|
35
|
-
.command("configure")
|
|
36
|
-
.description("Interactive configuration wizard")
|
|
37
|
-
.option("--section <section>", "Config section (repeatable): provider|connector|route|binding|sandbox|data", (value, previous) => [...previous, value], [])
|
|
38
|
-
.action(async (opts) => {
|
|
39
|
-
await runConfigureCommand({
|
|
40
|
-
sections: opts.section,
|
|
41
|
-
});
|
|
42
|
-
});
|
|
43
|
-
const botCommand = program.command("bot").description("Manage bot connector settings");
|
|
44
|
-
botCommand
|
|
45
|
-
.command("list")
|
|
46
|
-
.description("List configured bot connectors")
|
|
47
|
-
.option("--json", "Output JSON", false)
|
|
48
|
-
.action(async (opts) => {
|
|
49
|
-
await runBotListCommand({
|
|
50
|
-
json: Boolean(opts.json),
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
botCommand
|
|
54
|
-
.command("set")
|
|
55
|
-
.description("Update one bot connector")
|
|
56
|
-
.argument("<connectorId>", "Connector instance ID")
|
|
57
|
-
.option("--name <name>", "Discord botName")
|
|
58
|
-
.option("--token <token>", "Discord botToken")
|
|
59
|
-
.action(async (connectorId, opts) => {
|
|
60
|
-
await runBotSetCommand({
|
|
61
|
-
connectorId,
|
|
62
|
-
...(typeof opts.name === "string" ? { name: opts.name } : {}),
|
|
63
|
-
...(typeof opts.token === "string" ? { token: opts.token } : {}),
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
const bindingCommand = program.command("binding").description("Manage connector source-route bindings");
|
|
67
|
-
bindingCommand
|
|
68
|
-
.command("list")
|
|
69
|
-
.description("List bindings")
|
|
70
|
-
.option("--connector <id>", "Filter by connector instance ID")
|
|
71
|
-
.option("--json", "Output JSON", false)
|
|
72
|
-
.action(async (opts) => {
|
|
73
|
-
await runBindingListCommand({
|
|
74
|
-
...(typeof opts.connector === "string" ? { connectorId: opts.connector } : {}),
|
|
75
|
-
json: Boolean(opts.json),
|
|
76
|
-
});
|
|
77
|
-
});
|
|
78
|
-
bindingCommand
|
|
79
|
-
.command("set")
|
|
80
|
-
.description("Create or update one binding")
|
|
81
|
-
.argument("<bindingId>", "Binding ID")
|
|
82
|
-
.requiredOption("--connector <id>", "Connector instance ID")
|
|
83
|
-
.requiredOption("--source-type <type>", "Source type: channel|chat")
|
|
84
|
-
.requiredOption("--source-id <id>", "Source ID")
|
|
85
|
-
.requiredOption("--route <id>", "Route ID")
|
|
86
|
-
.action(async (bindingId, opts) => {
|
|
87
|
-
if (opts.sourceType !== "channel" && opts.sourceType !== "chat") {
|
|
88
|
-
throw new Error("--source-type must be channel or chat");
|
|
89
|
-
}
|
|
90
|
-
await runBindingSetCommand({
|
|
91
|
-
bindingId,
|
|
92
|
-
connectorId: opts.connector,
|
|
93
|
-
sourceType: opts.sourceType,
|
|
94
|
-
sourceId: opts.sourceId,
|
|
95
|
-
routeId: opts.route,
|
|
96
|
-
});
|
|
97
|
-
});
|
|
98
|
-
bindingCommand
|
|
99
|
-
.command("remove")
|
|
100
|
-
.description("Remove one binding")
|
|
101
|
-
.argument("<bindingId>", "Binding ID")
|
|
102
|
-
.action(async (bindingId) => {
|
|
103
|
-
await runBindingRemoveCommand({
|
|
104
|
-
bindingId,
|
|
105
|
-
});
|
|
106
|
-
});
|
|
107
|
-
const routeCommand = program.command("route").description("Manage route profiles");
|
|
108
|
-
routeCommand
|
|
109
|
-
.command("list")
|
|
110
|
-
.description("List route profiles")
|
|
111
|
-
.option("--json", "Output JSON", false)
|
|
112
|
-
.action(async (opts) => {
|
|
113
|
-
await runRouteListCommand({
|
|
114
|
-
json: Boolean(opts.json),
|
|
115
|
-
});
|
|
116
|
-
});
|
|
117
|
-
routeCommand
|
|
118
|
-
.command("set")
|
|
119
|
-
.description("Create or update one route")
|
|
120
|
-
.argument("<routeId>", "Route ID")
|
|
121
|
-
.option("--project-root <path>", "Route project root")
|
|
122
|
-
.option("--tools <profile>", "Route tools profile: full|readonly")
|
|
123
|
-
.option("--provider <id>", "Provider instance ID")
|
|
124
|
-
.option("--sandbox <id>", "Sandbox instance ID")
|
|
125
|
-
.option("--mentions <policy>", "Mention policy: required|optional")
|
|
126
|
-
.action(async (routeId, opts) => {
|
|
127
|
-
if (typeof opts.mentions === "string"
|
|
128
|
-
&& opts.mentions !== "required"
|
|
129
|
-
&& opts.mentions !== "optional") {
|
|
130
|
-
throw new Error("--mentions must be required or optional");
|
|
131
|
-
}
|
|
132
|
-
await runRouteSetCommand({
|
|
133
|
-
routeId,
|
|
134
|
-
...(typeof opts.projectRoot === "string" ? { projectRoot: opts.projectRoot } : {}),
|
|
135
|
-
...(typeof opts.tools === "string" ? { tools: opts.tools } : {}),
|
|
136
|
-
...(typeof opts.provider === "string" ? { providerId: opts.provider } : {}),
|
|
137
|
-
...(typeof opts.sandbox === "string" ? { sandboxId: opts.sandbox } : {}),
|
|
138
|
-
...(typeof opts.mentions === "string" ? { mentions: opts.mentions } : {}),
|
|
139
|
-
});
|
|
140
|
-
});
|
|
141
|
-
routeCommand
|
|
142
|
-
.command("remove")
|
|
143
|
-
.description("Remove one route")
|
|
144
|
-
.argument("<routeId>", "Route ID")
|
|
145
|
-
.option("--cascade-bindings", "Remove bindings that reference this route", false)
|
|
146
|
-
.action(async (routeId, opts) => {
|
|
147
|
-
await runRouteRemoveCommand({
|
|
148
|
-
routeId,
|
|
149
|
-
cascadeBindings: Boolean(opts.cascadeBindings),
|
|
150
|
-
});
|
|
151
|
-
});
|
|
152
|
-
const configCommand = program.command("config").description("Inspect and edit config");
|
|
45
|
+
const configCommand = program.command("config").description("Inspect config");
|
|
153
46
|
configCommand
|
|
154
47
|
.command("show")
|
|
155
48
|
.description("Show full config or one section")
|
|
@@ -172,15 +65,6 @@ export function buildProgram() {
|
|
|
172
65
|
json: Boolean(opts.json),
|
|
173
66
|
});
|
|
174
67
|
});
|
|
175
|
-
configCommand
|
|
176
|
-
.command("edit")
|
|
177
|
-
.description("Interactive edit for high-frequency sections")
|
|
178
|
-
.option("--section <section>", "Edit section (repeatable): provider|connector|route|binding", (value, previous) => [...previous, value], [])
|
|
179
|
-
.action(async (opts) => {
|
|
180
|
-
await runConfigEditCommand({
|
|
181
|
-
sections: opts.section,
|
|
182
|
-
});
|
|
183
|
-
});
|
|
184
68
|
const configSchemaCommand = configCommand.command("schema").description("Inspect extension config schemas");
|
|
185
69
|
configSchemaCommand
|
|
186
70
|
.command("list")
|
|
@@ -253,7 +137,6 @@ export function buildProgram() {
|
|
|
253
137
|
.requiredOption("--route <id>", "Route ID")
|
|
254
138
|
.requiredOption("--channel <id>", "Delivery channel/chat ID")
|
|
255
139
|
.option("--thread <id>", "Delivery thread ID")
|
|
256
|
-
.option("--session-policy <policy>", "Session policy: stateless|shared-session", "stateless")
|
|
257
140
|
.option("--at <iso>", "Run once at ISO timestamp")
|
|
258
141
|
.option("--every-ms <ms>", "Run at fixed interval in milliseconds")
|
|
259
142
|
.option("--cron <expr>", "Cron expression")
|
|
@@ -268,7 +151,6 @@ export function buildProgram() {
|
|
|
268
151
|
routeId: opts.route,
|
|
269
152
|
channelId: opts.channel,
|
|
270
153
|
...(typeof opts.thread === "string" ? { threadId: opts.thread } : {}),
|
|
271
|
-
sessionPolicy: opts.sessionPolicy,
|
|
272
154
|
...(typeof opts.at === "string" ? { at: opts.at } : {}),
|
|
273
155
|
...(parsedEveryMs !== null && Number.isFinite(parsedEveryMs) ? { everyMs: parsedEveryMs } : {}),
|
|
274
156
|
...(typeof opts.cron === "string" ? { cronExpr: opts.cron } : {}),
|
|
@@ -322,7 +204,6 @@ export function buildProgram() {
|
|
|
322
204
|
.option("--channel <id>", "Delivery channel/chat ID")
|
|
323
205
|
.option("--thread <id>", "Delivery thread ID")
|
|
324
206
|
.option("--clear-thread", "Unset delivery thread", false)
|
|
325
|
-
.option("--session-policy <policy>", "Session policy: stateless|shared-session")
|
|
326
207
|
.option("--at <iso>", "Run once at ISO timestamp")
|
|
327
208
|
.option("--every-ms <ms>", "Run at fixed interval in milliseconds")
|
|
328
209
|
.option("--cron <expr>", "Cron expression")
|
|
@@ -339,9 +220,6 @@ export function buildProgram() {
|
|
|
339
220
|
...(typeof opts.channel === "string" ? { channelId: opts.channel } : {}),
|
|
340
221
|
...(typeof opts.thread === "string" ? { threadId: opts.thread } : {}),
|
|
341
222
|
clearThread: Boolean(opts.clearThread),
|
|
342
|
-
...(typeof opts.sessionPolicy === "string"
|
|
343
|
-
? { sessionPolicy: opts.sessionPolicy }
|
|
344
|
-
: {}),
|
|
345
223
|
...(typeof opts.at === "string" ? { at: opts.at } : {}),
|
|
346
224
|
...(parsedEveryMs !== null && Number.isFinite(parsedEveryMs) ? { everyMs: parsedEveryMs } : {}),
|
|
347
225
|
...(typeof opts.cron === "string" ? { cronExpr: opts.cron } : {}),
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
2
1
|
import { access, mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
3
2
|
import { dirname, isAbsolute, resolve } from "node:path";
|
|
4
3
|
import { homedir } from "node:os";
|
|
5
4
|
import { loadGatewayConfig } from "../../core/routing.js";
|
|
5
|
+
import { findDobbyRepoRoot, isDobbyRepoRoot } from "../../shared/dobby-repo.js";
|
|
6
6
|
/**
|
|
7
7
|
* Default config file path used by all CLI commands.
|
|
8
8
|
*/
|
|
@@ -19,25 +19,6 @@ function expandHome(value) {
|
|
|
19
19
|
}
|
|
20
20
|
return value;
|
|
21
21
|
}
|
|
22
|
-
/**
|
|
23
|
-
* Returns true when a directory looks like the dobby repository root.
|
|
24
|
-
*/
|
|
25
|
-
function isDobbyRepoRoot(candidateDir) {
|
|
26
|
-
const packageJsonPath = resolve(candidateDir, "package.json");
|
|
27
|
-
const repoConfigPath = resolve(candidateDir, "config", "gateway.json");
|
|
28
|
-
const localExtensionsScriptPath = resolve(candidateDir, "scripts", "local-extensions.mjs");
|
|
29
|
-
if (!existsSync(packageJsonPath) || !existsSync(repoConfigPath) || !existsSync(localExtensionsScriptPath)) {
|
|
30
|
-
return false;
|
|
31
|
-
}
|
|
32
|
-
try {
|
|
33
|
-
const packageJsonRaw = readFileSync(packageJsonPath, "utf-8");
|
|
34
|
-
const parsed = JSON.parse(packageJsonRaw);
|
|
35
|
-
return parsed.name === "dobby";
|
|
36
|
-
}
|
|
37
|
-
catch {
|
|
38
|
-
return false;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
22
|
function resolveConfigBaseDir(configPath) {
|
|
42
23
|
const absoluteConfigPath = resolve(configPath);
|
|
43
24
|
const configDir = dirname(absoluteConfigPath);
|
|
@@ -51,17 +32,8 @@ function resolveConfigBaseDir(configPath) {
|
|
|
51
32
|
* Scans current directory and ancestors to find a local dobby repo config path.
|
|
52
33
|
*/
|
|
53
34
|
function findDobbyRepoConfigPath(startDir) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
if (isDobbyRepoRoot(currentDir)) {
|
|
57
|
-
return resolve(currentDir, "config", "gateway.json");
|
|
58
|
-
}
|
|
59
|
-
const parentDir = dirname(currentDir);
|
|
60
|
-
if (parentDir === currentDir) {
|
|
61
|
-
return null;
|
|
62
|
-
}
|
|
63
|
-
currentDir = parentDir;
|
|
64
|
-
}
|
|
35
|
+
const repoRoot = findDobbyRepoRoot(startDir);
|
|
36
|
+
return repoRoot ? resolve(repoRoot, "config", "gateway.json") : null;
|
|
65
37
|
}
|
|
66
38
|
/**
|
|
67
39
|
* Resolves config path source by priority: env override -> local repo -> default home path.
|
|
@@ -42,6 +42,7 @@ function asRouteDefaults(value) {
|
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
44
|
return {
|
|
45
|
+
...(typeof value.projectRoot === "string" && value.projectRoot.trim().length > 0 ? { projectRoot: value.projectRoot } : {}),
|
|
45
46
|
...(typeof value.provider === "string" && value.provider.trim().length > 0 ? { provider: value.provider } : {}),
|
|
46
47
|
...(typeof value.sandbox === "string" && value.sandbox.trim().length > 0 ? { sandbox: value.sandbox } : {}),
|
|
47
48
|
tools: value.tools === "readonly" ? "readonly" : "full",
|
|
@@ -54,12 +55,12 @@ function asRoutes(value) {
|
|
|
54
55
|
}
|
|
55
56
|
const normalized = {};
|
|
56
57
|
for (const [routeId, route] of Object.entries(value)) {
|
|
57
|
-
if (!isRecord(route)
|
|
58
|
+
if (!isRecord(route)) {
|
|
58
59
|
continue;
|
|
59
60
|
}
|
|
60
61
|
normalized[routeId] = {
|
|
61
62
|
...route,
|
|
62
|
-
projectRoot: route.projectRoot,
|
|
63
|
+
...(typeof route.projectRoot === "string" && route.projectRoot.trim().length > 0 ? { projectRoot: route.projectRoot } : {}),
|
|
63
64
|
...(route.tools === "readonly" ? { tools: "readonly" } : {}),
|
|
64
65
|
...(route.mentions === "optional" ? { mentions: "optional" } : {}),
|
|
65
66
|
...(typeof route.provider === "string" && route.provider.trim().length > 0 ? { provider: route.provider } : {}),
|
|
@@ -69,6 +70,15 @@ function asRoutes(value) {
|
|
|
69
70
|
}
|
|
70
71
|
return normalized;
|
|
71
72
|
}
|
|
73
|
+
function asDefaultBinding(value) {
|
|
74
|
+
if (!isRecord(value) || typeof value.route !== "string" || value.route.trim().length === 0) {
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
...value,
|
|
79
|
+
route: value.route,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
72
82
|
function asBindings(value) {
|
|
73
83
|
if (!isRecord(value)) {
|
|
74
84
|
return {};
|
|
@@ -109,13 +119,14 @@ export function ensureGatewayConfigShape(config) {
|
|
|
109
119
|
const normalizedSandboxesDefault = typeof config.sandboxes?.default === "string" && config.sandboxes.default.trim().length > 0
|
|
110
120
|
? config.sandboxes.default
|
|
111
121
|
: "host.builtin";
|
|
112
|
-
const routeDefaults = asRouteDefaults(config.routes?.
|
|
122
|
+
const routeDefaults = asRouteDefaults(config.routes?.default);
|
|
113
123
|
if (!routeDefaults.provider && normalizedProvidersDefault) {
|
|
114
124
|
routeDefaults.provider = normalizedProvidersDefault;
|
|
115
125
|
}
|
|
116
126
|
if (!routeDefaults.sandbox && normalizedSandboxesDefault) {
|
|
117
127
|
routeDefaults.sandbox = normalizedSandboxesDefault;
|
|
118
128
|
}
|
|
129
|
+
const defaultBinding = asDefaultBinding(config.bindings?.default);
|
|
119
130
|
return {
|
|
120
131
|
...config,
|
|
121
132
|
extensions: {
|
|
@@ -138,11 +149,12 @@ export function ensureGatewayConfigShape(config) {
|
|
|
138
149
|
},
|
|
139
150
|
routes: {
|
|
140
151
|
...(isRecord(config.routes) ? config.routes : {}),
|
|
141
|
-
|
|
152
|
+
default: routeDefaults,
|
|
142
153
|
items: asRoutes(config.routes?.items),
|
|
143
154
|
},
|
|
144
155
|
bindings: {
|
|
145
156
|
...(isRecord(config.bindings) ? config.bindings : {}),
|
|
157
|
+
...(defaultBinding ? { default: defaultBinding } : {}),
|
|
146
158
|
items: asBindings(config.bindings?.items),
|
|
147
159
|
},
|
|
148
160
|
data: {
|
|
@@ -283,11 +295,11 @@ export function setDefaultProviderIfMissingOrInvalid(config) {
|
|
|
283
295
|
const defaultProvider = next.providers.default;
|
|
284
296
|
if (defaultProvider && items[defaultProvider]) {
|
|
285
297
|
config.providers = next.providers;
|
|
286
|
-
if (!next.routes.
|
|
298
|
+
if (!next.routes.default.provider) {
|
|
287
299
|
config.routes = {
|
|
288
300
|
...next.routes,
|
|
289
301
|
defaults: {
|
|
290
|
-
...next.routes.
|
|
302
|
+
...next.routes.default,
|
|
291
303
|
provider: defaultProvider,
|
|
292
304
|
},
|
|
293
305
|
};
|
|
@@ -306,8 +318,8 @@ export function setDefaultProviderIfMissingOrInvalid(config) {
|
|
|
306
318
|
};
|
|
307
319
|
config.routes = {
|
|
308
320
|
...next.routes,
|
|
309
|
-
|
|
310
|
-
...next.routes.
|
|
321
|
+
default: {
|
|
322
|
+
...next.routes.default,
|
|
311
323
|
provider: candidates[0],
|
|
312
324
|
},
|
|
313
325
|
};
|
|
@@ -315,7 +327,7 @@ export function setDefaultProviderIfMissingOrInvalid(config) {
|
|
|
315
327
|
export function upsertRoute(config, routeId, profile) {
|
|
316
328
|
const next = ensureGatewayConfigShape(config);
|
|
317
329
|
next.routes.items[routeId] = {
|
|
318
|
-
projectRoot: profile.projectRoot,
|
|
330
|
+
...(typeof profile.projectRoot === "string" && profile.projectRoot.trim().length > 0 ? { projectRoot: profile.projectRoot } : {}),
|
|
319
331
|
...(profile.tools ? { tools: profile.tools } : {}),
|
|
320
332
|
...(profile.mentions ? { mentions: profile.mentions } : {}),
|
|
321
333
|
...(profile.provider ? { provider: profile.provider } : {}),
|
|
@@ -335,6 +347,18 @@ export function upsertBinding(config, bindingId, binding) {
|
|
|
335
347
|
items: next.bindings.items,
|
|
336
348
|
};
|
|
337
349
|
}
|
|
350
|
+
export function setDefaultBinding(config, binding) {
|
|
351
|
+
const next = ensureGatewayConfigShape(config);
|
|
352
|
+
const normalizedBinding = binding ? structuredClone(binding) : undefined;
|
|
353
|
+
config.bindings = {
|
|
354
|
+
...next.bindings,
|
|
355
|
+
...(normalizedBinding ? { default: normalizedBinding } : {}),
|
|
356
|
+
items: next.bindings.items,
|
|
357
|
+
};
|
|
358
|
+
if (!normalizedBinding) {
|
|
359
|
+
delete config.bindings.default;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
338
362
|
export function listContributionIds(config) {
|
|
339
363
|
const next = ensureGatewayConfigShape(config);
|
|
340
364
|
return {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { cancel, confirm, isCancel, note, password, select, text, } from "@clack/prompts";
|
|
2
2
|
import JSON5 from "json5";
|
|
3
|
-
import { ensureGatewayConfigShape, setDefaultProviderIfMissingOrInvalid, upsertBinding, upsertConnectorInstance, upsertProviderInstance, upsertRoute, } from "./config-mutators.js";
|
|
3
|
+
import { ensureGatewayConfigShape, setDefaultBinding, setDefaultProviderIfMissingOrInvalid, upsertBinding, upsertConnectorInstance, upsertProviderInstance, upsertRoute, } from "./config-mutators.js";
|
|
4
4
|
import { DEFAULT_DISCORD_BOT_NAME, DISCORD_CONNECTOR_CONTRIBUTION_ID, } from "./discord-config.js";
|
|
5
5
|
import { promptConfigFromSchema } from "./schema-prompts.js";
|
|
6
6
|
export const CONFIGURE_SECTION_VALUES = ["provider", "connector", "route", "binding", "sandbox", "data"];
|
|
@@ -147,7 +147,7 @@ async function configureProviderSection(config, context) {
|
|
|
147
147
|
throw new Error("Configure cancelled.");
|
|
148
148
|
}
|
|
149
149
|
next.providers.default = String(defaultProvider);
|
|
150
|
-
next.routes.
|
|
150
|
+
next.routes.default.provider = String(defaultProvider);
|
|
151
151
|
}
|
|
152
152
|
Object.assign(config, next);
|
|
153
153
|
}
|
|
@@ -229,11 +229,32 @@ async function configureRouteSection(config) {
|
|
|
229
229
|
}
|
|
230
230
|
const routeId = String(targetRoute) === "__new" ? await requiredText("New route ID", "main") : String(targetRoute);
|
|
231
231
|
const existing = routeItems[routeId];
|
|
232
|
-
const
|
|
232
|
+
const defaultProjectRoot = next.routes.default.projectRoot;
|
|
233
|
+
let projectRoot = existing?.projectRoot;
|
|
234
|
+
if (defaultProjectRoot) {
|
|
235
|
+
const projectRootMode = await select({
|
|
236
|
+
message: "projectRoot",
|
|
237
|
+
options: [
|
|
238
|
+
{ value: "__default", label: `Use route default (${defaultProjectRoot})` },
|
|
239
|
+
{ value: "__custom", label: "Set explicit projectRoot" },
|
|
240
|
+
],
|
|
241
|
+
initialValue: existing?.projectRoot ? "__custom" : "__default",
|
|
242
|
+
});
|
|
243
|
+
if (isCancel(projectRootMode)) {
|
|
244
|
+
cancel("Configure cancelled.");
|
|
245
|
+
throw new Error("Configure cancelled.");
|
|
246
|
+
}
|
|
247
|
+
projectRoot = projectRootMode === "__custom"
|
|
248
|
+
? await requiredText("projectRoot", existing?.projectRoot ?? defaultProjectRoot)
|
|
249
|
+
: undefined;
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
projectRoot = await requiredText("projectRoot", existing?.projectRoot ?? process.cwd());
|
|
253
|
+
}
|
|
233
254
|
const tools = await select({
|
|
234
255
|
message: "tools",
|
|
235
256
|
options: [
|
|
236
|
-
{ value: "__default", label: `Use route default (${next.routes.
|
|
257
|
+
{ value: "__default", label: `Use route default (${next.routes.default.tools ?? "full"})` },
|
|
237
258
|
{ value: "full", label: "full" },
|
|
238
259
|
{ value: "readonly", label: "readonly" },
|
|
239
260
|
],
|
|
@@ -246,7 +267,7 @@ async function configureRouteSection(config) {
|
|
|
246
267
|
const mentions = await select({
|
|
247
268
|
message: "mentions",
|
|
248
269
|
options: [
|
|
249
|
-
{ value: "__default", label: `Use route default (${next.routes.
|
|
270
|
+
{ value: "__default", label: `Use route default (${next.routes.default.mentions ?? "required"})` },
|
|
250
271
|
{ value: "required", label: "required" },
|
|
251
272
|
{ value: "optional", label: "optional" },
|
|
252
273
|
],
|
|
@@ -261,7 +282,7 @@ async function configureRouteSection(config) {
|
|
|
261
282
|
? await select({
|
|
262
283
|
message: "provider",
|
|
263
284
|
options: [
|
|
264
|
-
{ value: "__default", label: `Use route default (${(next.routes.
|
|
285
|
+
{ value: "__default", label: `Use route default (${(next.routes.default.provider ?? next.providers.default) || "(unset)"})` },
|
|
265
286
|
...providerIds.map((id) => ({ value: id, label: id })),
|
|
266
287
|
],
|
|
267
288
|
initialValue: existing?.provider ?? "__default",
|
|
@@ -275,7 +296,7 @@ async function configureRouteSection(config) {
|
|
|
275
296
|
const sandboxValue = await select({
|
|
276
297
|
message: "sandbox",
|
|
277
298
|
options: [
|
|
278
|
-
{ value: "__default", label: `Use route default (${next.routes.
|
|
299
|
+
{ value: "__default", label: `Use route default (${next.routes.default.sandbox ?? next.sandboxes.default})` },
|
|
279
300
|
...sandboxIds.map((id) => ({ value: id, label: id })),
|
|
280
301
|
],
|
|
281
302
|
initialValue: existing?.sandbox ?? "__default",
|
|
@@ -286,7 +307,7 @@ async function configureRouteSection(config) {
|
|
|
286
307
|
}
|
|
287
308
|
const systemPromptFile = await optionalText("systemPromptFile (optional)", existing?.systemPromptFile ?? "");
|
|
288
309
|
upsertRoute(next, routeId, {
|
|
289
|
-
projectRoot,
|
|
310
|
+
...(projectRoot ? { projectRoot } : {}),
|
|
290
311
|
...(tools !== "__default" ? { tools: String(tools) } : {}),
|
|
291
312
|
...(mentions !== "__default" ? { mentions: String(mentions) } : {}),
|
|
292
313
|
...(providerValue !== "__default" ? { provider: String(providerValue) } : {}),
|
|
@@ -306,19 +327,39 @@ async function configureBindingSection(config) {
|
|
|
306
327
|
throw new Error("No routes found. Configure routes first.");
|
|
307
328
|
}
|
|
308
329
|
const targetBinding = bindingChoices.length === 0
|
|
309
|
-
? "__new"
|
|
330
|
+
? (next.bindings.default ? "__default" : "__new")
|
|
310
331
|
: await select({
|
|
311
332
|
message: "Select binding",
|
|
312
333
|
options: [
|
|
334
|
+
{ value: "__default", label: next.bindings.default ? "Edit default direct-message binding" : "Create default direct-message binding" },
|
|
313
335
|
...bindingChoices.map((id) => ({ value: id, label: id })),
|
|
314
336
|
{ value: "__new", label: "Create new binding" },
|
|
315
337
|
],
|
|
316
|
-
initialValue: bindingChoices[0],
|
|
338
|
+
initialValue: next.bindings.default ? "__default" : bindingChoices[0],
|
|
317
339
|
});
|
|
318
340
|
if (isCancel(targetBinding)) {
|
|
319
341
|
cancel("Configure cancelled.");
|
|
320
342
|
throw new Error("Configure cancelled.");
|
|
321
343
|
}
|
|
344
|
+
const routeIds = Object.keys(next.routes.items).sort((a, b) => a.localeCompare(b));
|
|
345
|
+
if (String(targetBinding) === "__default") {
|
|
346
|
+
const defaultRouteId = await select({
|
|
347
|
+
message: "Default direct-message route",
|
|
348
|
+
options: routeIds.map((id) => ({ value: id, label: id })),
|
|
349
|
+
initialValue: next.bindings.default?.route && routeIds.includes(next.bindings.default.route)
|
|
350
|
+
? next.bindings.default.route
|
|
351
|
+
: routeIds[0],
|
|
352
|
+
});
|
|
353
|
+
if (isCancel(defaultRouteId)) {
|
|
354
|
+
cancel("Configure cancelled.");
|
|
355
|
+
throw new Error("Configure cancelled.");
|
|
356
|
+
}
|
|
357
|
+
setDefaultBinding(next, {
|
|
358
|
+
route: String(defaultRouteId),
|
|
359
|
+
});
|
|
360
|
+
Object.assign(config, next);
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
322
363
|
const bindingId = String(targetBinding) === "__new"
|
|
323
364
|
? await requiredText("New binding ID", "discord.main.main")
|
|
324
365
|
: String(targetBinding);
|
|
@@ -346,7 +387,6 @@ async function configureBindingSection(config) {
|
|
|
346
387
|
throw new Error("Configure cancelled.");
|
|
347
388
|
}
|
|
348
389
|
const sourceId = await requiredText("source.id", existing?.source.id);
|
|
349
|
-
const routeIds = Object.keys(next.routes.items).sort((a, b) => a.localeCompare(b));
|
|
350
390
|
const routeId = await select({
|
|
351
391
|
message: "route",
|
|
352
392
|
options: routeIds.map((id) => ({ value: id, label: id })),
|
|
@@ -386,7 +426,7 @@ async function configureSandboxSection(config) {
|
|
|
386
426
|
throw new Error("Configure cancelled.");
|
|
387
427
|
}
|
|
388
428
|
next.sandboxes.default = String(defaultSandbox);
|
|
389
|
-
next.routes.
|
|
429
|
+
next.routes.default.sandbox = String(defaultSandbox);
|
|
390
430
|
Object.assign(config, next);
|
|
391
431
|
}
|
|
392
432
|
async function configureDataSection(config) {
|