@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
@@ -1,223 +0,0 @@
1
- import { dirname, join } from "node:path";
2
- import { loadCronConfig } from "../../cron/config.js";
3
- import { CronService } from "../../cron/service.js";
4
- import { CronStore } from "../../cron/store.js";
5
- import { DedupStore } from "../../core/dedup-store.js";
6
- import { Gateway } from "../../core/gateway.js";
7
- import { BindingResolver, loadGatewayConfig, RouteResolver } from "../../core/routing.js";
8
- import { RuntimeRegistry } from "../../core/runtime-registry.js";
9
- import { BUILTIN_HOST_SANDBOX_ID } from "../../core/types.js";
10
- import type {
11
- GatewayLogger,
12
- ProviderInstance,
13
- ProvidersConfig,
14
- SandboxInstance,
15
- SandboxesConfig,
16
- } from "../../core/types.js";
17
- import { ExtensionLoader } from "../../extension/loader.js";
18
- import { ExtensionRegistry } from "../../extension/registry.js";
19
- import type { Executor } from "../../sandbox/executor.js";
20
- import { HostExecutor } from "../../sandbox/host-executor.js";
21
- import { resolveConfigPath } from "../shared/config-io.js";
22
- import { createLogger, ensureDataDirs, extensionStoreDir } from "../shared/runtime.js";
23
-
24
- /**
25
- * Closes provider instances best-effort so shutdown does not stop on one failed provider.
26
- */
27
- async function closeProviderInstances(providers: Map<string, ProviderInstance>, logger: GatewayLogger): Promise<void> {
28
- for (const [providerId, provider] of providers.entries()) {
29
- if (!provider.close) {
30
- continue;
31
- }
32
-
33
- try {
34
- await provider.close();
35
- } catch (error) {
36
- logger.warn({ err: error, providerId }, "Failed to close provider instance");
37
- }
38
- }
39
- }
40
-
41
- /**
42
- * Closes sandbox executors and optional sandbox lifecycle hooks during shutdown.
43
- */
44
- async function closeSandboxInstances(sandboxes: Map<string, SandboxInstance>, logger: GatewayLogger): Promise<void> {
45
- for (const [sandboxId, sandbox] of sandboxes.entries()) {
46
- try {
47
- await sandbox.executor.close();
48
- } catch (error) {
49
- logger.warn({ err: error, sandboxId }, "Failed to close sandbox executor");
50
- }
51
-
52
- if (!sandbox.close) {
53
- continue;
54
- }
55
-
56
- try {
57
- await sandbox.close();
58
- } catch (error) {
59
- logger.warn({ err: error, sandboxId }, "Failed to close sandbox instance");
60
- }
61
- }
62
- }
63
-
64
- /**
65
- * Narrows provider instances to only those referenced by default provider or any route override.
66
- */
67
- function selectProviderInstances(config: Awaited<ReturnType<typeof loadGatewayConfig>>): ProvidersConfig {
68
- const requiredProviderIds = new Set<string>([config.providers.default]);
69
- for (const route of Object.values(config.routes.items)) {
70
- requiredProviderIds.add(route.provider);
71
- }
72
-
73
- const instances = Object.fromEntries(
74
- Object.entries(config.providers.items).filter(([instanceId]) => requiredProviderIds.has(instanceId)),
75
- );
76
-
77
- return {
78
- default: config.providers.default,
79
- items: instances,
80
- };
81
- }
82
-
83
- /**
84
- * Narrows sandbox instances to only those referenced by default/route sandbox settings.
85
- */
86
- function selectSandboxInstances(config: Awaited<ReturnType<typeof loadGatewayConfig>>): SandboxesConfig {
87
- const defaultSandboxId = config.sandboxes.default ?? BUILTIN_HOST_SANDBOX_ID;
88
- const requiredSandboxIds = new Set<string>();
89
-
90
- if (defaultSandboxId !== BUILTIN_HOST_SANDBOX_ID) {
91
- requiredSandboxIds.add(defaultSandboxId);
92
- }
93
-
94
- for (const route of Object.values(config.routes.items)) {
95
- if (route.sandbox !== BUILTIN_HOST_SANDBOX_ID) {
96
- requiredSandboxIds.add(route.sandbox);
97
- }
98
- }
99
-
100
- const instances = Object.fromEntries(
101
- Object.entries(config.sandboxes.items).filter(([instanceId]) => requiredSandboxIds.has(instanceId)),
102
- );
103
-
104
- return {
105
- ...(config.sandboxes.default ? { default: config.sandboxes.default } : {}),
106
- items: instances,
107
- };
108
- }
109
-
110
- /**
111
- * Starts the gateway runtime from config and wires graceful shutdown handlers.
112
- */
113
- export async function runStartCommand(): Promise<void> {
114
- const configPath = resolveConfigPath();
115
- const config = await loadGatewayConfig(configPath);
116
-
117
- await ensureDataDirs(config.data.rootDir);
118
-
119
- const logger = createLogger();
120
- const loader = new ExtensionLoader(logger, {
121
- extensionsDir: extensionStoreDir(config),
122
- });
123
-
124
- const loadedPackages = await loader.loadAllowList(config.extensions.allowList);
125
- const registry = new ExtensionRegistry();
126
- registry.registerPackages(loadedPackages);
127
-
128
- logger.info(
129
- {
130
- extensionStoreDir: extensionStoreDir(config),
131
- packages: loadedPackages.map((item) => ({
132
- package: item.packageName,
133
- manifestName: item.manifest.name,
134
- version: item.manifest.version,
135
- contributions: item.manifest.contributions.map((contribution) => `${contribution.kind}:${contribution.id}`),
136
- })),
137
- },
138
- "Extension packages loaded",
139
- );
140
-
141
- const extensionHostContext = {
142
- logger,
143
- configBaseDir: dirname(configPath),
144
- };
145
-
146
- const activeProvidersConfig = selectProviderInstances(config);
147
- const activeSandboxesConfig = selectSandboxInstances(config);
148
-
149
- const providers = await registry.createProviderInstances(activeProvidersConfig, extensionHostContext, config.data);
150
- const connectors = await registry.createConnectorInstances(config.connectors, extensionHostContext, config.data.attachmentsDir);
151
- const sandboxes = await registry.createSandboxInstances(activeSandboxesConfig, extensionHostContext);
152
-
153
- const hostExecutor = new HostExecutor(logger);
154
- const executors = new Map<string, Executor>();
155
- executors.set(BUILTIN_HOST_SANDBOX_ID, hostExecutor);
156
-
157
- for (const [sandboxId, sandbox] of sandboxes.entries()) {
158
- executors.set(sandboxId, sandbox.executor);
159
- }
160
-
161
- if (connectors.length === 0) {
162
- throw new Error("No connectors are configured. Add connector instances in your dobby config.");
163
- }
164
-
165
- const dedupStore = new DedupStore(join(config.data.stateDir, "dedup.json"), config.data.dedupTtlMs, logger);
166
- const routeResolver = new RouteResolver(config.routes);
167
- const bindingResolver = new BindingResolver(config.bindings);
168
- const runtimeRegistry = new RuntimeRegistry(logger);
169
-
170
- const gateway = new Gateway({
171
- config,
172
- connectors,
173
- providers,
174
- executors,
175
- routeResolver,
176
- bindingResolver,
177
- dedupStore,
178
- runtimeRegistry,
179
- logger,
180
- });
181
-
182
- const loadedCronConfig = await loadCronConfig({
183
- gatewayConfigPath: configPath,
184
- gatewayConfig: config,
185
- });
186
- const cronStore = new CronStore(loadedCronConfig.config.storeFile, loadedCronConfig.config.runLogFile, logger);
187
- const cronService = new CronService({
188
- config: loadedCronConfig.config,
189
- store: cronStore,
190
- gateway,
191
- logger,
192
- });
193
-
194
- await gateway.start();
195
- await cronService.start();
196
- logger.info(
197
- {
198
- configPath,
199
- cronConfigPath: loadedCronConfig.configPath,
200
- cronConfigSource: loadedCronConfig.source,
201
- cronEnabled: loadedCronConfig.config.enabled,
202
- },
203
- "Gateway started",
204
- );
205
-
206
- const shutdown = async (signal: string) => {
207
- logger.info({ signal }, "Shutting down gateway");
208
- await cronService.stop();
209
- await gateway.stop();
210
- await hostExecutor.close();
211
- await closeProviderInstances(providers, logger);
212
- await closeSandboxInstances(sandboxes, logger);
213
- process.exit(0);
214
- };
215
-
216
- process.on("SIGINT", () => {
217
- void shutdown("SIGINT");
218
- });
219
-
220
- process.on("SIGTERM", () => {
221
- void shutdown("SIGTERM");
222
- });
223
- }
@@ -1,383 +0,0 @@
1
- import { BUILTIN_HOST_SANDBOX_ID } from "../../core/types.js";
2
- import {
3
- ensureGatewayConfigShape,
4
- upsertBinding,
5
- upsertRoute,
6
- } from "../shared/config-mutators.js";
7
- import { DISCORD_CONNECTOR_CONTRIBUTION_ID } from "../shared/discord-config.js";
8
- import { requireRawConfig, resolveConfigPath, writeConfigWithValidation } from "../shared/config-io.js";
9
- import type { RawBindingConfig, RawGatewayConfig } from "../shared/config-types.js";
10
-
11
- interface DiscordConnectorView {
12
- connectorId: string;
13
- type: string;
14
- botName: string;
15
- hasToken: boolean;
16
- }
17
-
18
- interface BindingView {
19
- bindingId: string;
20
- connectorId: string;
21
- sourceType: "channel" | "chat";
22
- sourceId: string;
23
- routeId: string;
24
- routeExists: boolean;
25
- projectRoot?: string;
26
- }
27
-
28
- interface RouteView {
29
- routeId: string;
30
- projectRoot: string;
31
- tools: "full" | "readonly";
32
- mentions: "required" | "optional";
33
- provider?: string;
34
- sandbox?: string;
35
- bindings: number;
36
- }
37
-
38
- function listDiscordConnectors(rawConfig: unknown): DiscordConnectorView[] {
39
- const normalized = ensureGatewayConfigShape(rawConfig as RawGatewayConfig);
40
- const items: DiscordConnectorView[] = [];
41
-
42
- for (const [connectorId, connector] of Object.entries(normalized.connectors.items)) {
43
- if (connector.type !== DISCORD_CONNECTOR_CONTRIBUTION_ID) {
44
- continue;
45
- }
46
-
47
- const botName = typeof connector.botName === "string" ? connector.botName : "";
48
- const botToken = typeof connector.botToken === "string" ? connector.botToken.trim() : "";
49
- items.push({
50
- connectorId,
51
- type: connector.type,
52
- botName,
53
- hasToken: botToken.length > 0,
54
- });
55
- }
56
-
57
- return items.sort((a, b) => a.connectorId.localeCompare(b.connectorId));
58
- }
59
-
60
- function getDiscordConnectorOrThrow(
61
- rawConfig: unknown,
62
- connectorId: string,
63
- ): {
64
- normalized: ReturnType<typeof ensureGatewayConfigShape>;
65
- connector: Record<string, unknown> & { type: string };
66
- } {
67
- const normalized = ensureGatewayConfigShape(rawConfig as RawGatewayConfig);
68
- const connector = normalized.connectors.items[connectorId];
69
- if (!connector) {
70
- throw new Error(`Connector instance '${connectorId}' not found`);
71
- }
72
- if (connector.type !== DISCORD_CONNECTOR_CONTRIBUTION_ID) {
73
- throw new Error(
74
- `Connector '${connectorId}' uses contribution '${connector.type}'. This command currently supports only '${DISCORD_CONNECTOR_CONTRIBUTION_ID}'.`,
75
- );
76
- }
77
- return {
78
- normalized,
79
- connector,
80
- };
81
- }
82
-
83
- function listBindings(rawConfig: unknown, connectorFilter?: string): BindingView[] {
84
- const normalized = ensureGatewayConfigShape(rawConfig as RawGatewayConfig);
85
- const routes = normalized.routes.items;
86
-
87
- return Object.entries(normalized.bindings.items)
88
- .filter(([, binding]) => !connectorFilter || binding.connector === connectorFilter)
89
- .map(([bindingId, binding]) => {
90
- const route = routes[binding.route];
91
- return {
92
- bindingId,
93
- connectorId: binding.connector,
94
- sourceType: binding.source.type,
95
- sourceId: binding.source.id,
96
- routeId: binding.route,
97
- routeExists: Boolean(route),
98
- ...(route ? { projectRoot: route.projectRoot } : {}),
99
- };
100
- })
101
- .sort((a, b) => a.bindingId.localeCompare(b.bindingId));
102
- }
103
-
104
- function buildRouteBindingCounts(rawConfig: unknown): Map<string, number> {
105
- const counts = new Map<string, number>();
106
- for (const binding of listBindings(rawConfig)) {
107
- counts.set(binding.routeId, (counts.get(binding.routeId) ?? 0) + 1);
108
- }
109
- return counts;
110
- }
111
-
112
- function listRoutes(rawConfig: unknown): RouteView[] {
113
- const normalized = ensureGatewayConfigShape(rawConfig as RawGatewayConfig);
114
- const counts = buildRouteBindingCounts(normalized);
115
-
116
- return Object.entries(normalized.routes.items)
117
- .map(([routeId, route]): RouteView => ({
118
- routeId,
119
- projectRoot: route.projectRoot,
120
- tools: route.tools === "readonly" ? "readonly" : "full",
121
- mentions: route.mentions === "optional" ? "optional" : "required",
122
- ...(route.provider ? { provider: route.provider } : {}),
123
- ...(route.sandbox ? { sandbox: route.sandbox } : {}),
124
- bindings: counts.get(routeId) ?? 0,
125
- }))
126
- .sort((a, b) => a.routeId.localeCompare(b.routeId));
127
- }
128
-
129
- async function saveConfig(configPath: string, normalized: ReturnType<typeof ensureGatewayConfigShape>): Promise<void> {
130
- await writeConfigWithValidation(configPath, normalized, {
131
- validate: true,
132
- createBackup: true,
133
- });
134
- }
135
-
136
- export async function runBotListCommand(options: {
137
- json?: boolean;
138
- }): Promise<void> {
139
- const configPath = resolveConfigPath();
140
- const rawConfig = await requireRawConfig(configPath);
141
- const bots = listDiscordConnectors(rawConfig);
142
-
143
- if (options.json) {
144
- console.log(JSON.stringify({ configPath, bots }, null, 2));
145
- return;
146
- }
147
-
148
- if (bots.length === 0) {
149
- console.log("No Discord connector instances configured.");
150
- return;
151
- }
152
-
153
- console.log(`Bots (${configPath}):`);
154
- for (const bot of bots) {
155
- console.log(
156
- `- ${bot.connectorId}: botName='${bot.botName || "(empty)"}', token=${bot.hasToken ? "set" : "missing"}`,
157
- );
158
- }
159
- }
160
-
161
- export async function runBotSetCommand(options: {
162
- connectorId: string;
163
- name?: string;
164
- token?: string;
165
- }): Promise<void> {
166
- const configPath = resolveConfigPath();
167
- const rawConfig = await requireRawConfig(configPath);
168
- const { normalized, connector } = getDiscordConnectorOrThrow(rawConfig, options.connectorId);
169
-
170
- const nextName = typeof options.name === "string" ? options.name.trim() : undefined;
171
- const nextToken = typeof options.token === "string" ? options.token.trim() : undefined;
172
- if (!nextName && !nextToken) {
173
- throw new Error("At least one of --name or --token must be provided");
174
- }
175
-
176
- normalized.connectors.items[options.connectorId] = {
177
- ...connector,
178
- ...(nextName !== undefined ? { botName: nextName } : {}),
179
- ...(nextToken !== undefined ? { botToken: nextToken } : {}),
180
- };
181
-
182
- await saveConfig(configPath, normalized);
183
- console.log(`Updated bot settings for connector '${options.connectorId}'`);
184
- }
185
-
186
- export async function runBindingListCommand(options: {
187
- connectorId?: string;
188
- json?: boolean;
189
- }): Promise<void> {
190
- const configPath = resolveConfigPath();
191
- const rawConfig = await requireRawConfig(configPath);
192
- const bindings = listBindings(rawConfig, options.connectorId);
193
-
194
- if (options.connectorId) {
195
- const normalized = ensureGatewayConfigShape(rawConfig);
196
- if (!normalized.connectors.items[options.connectorId]) {
197
- throw new Error(`Connector '${options.connectorId}' not found`);
198
- }
199
- }
200
-
201
- if (options.json) {
202
- console.log(JSON.stringify({ configPath, bindings }, null, 2));
203
- return;
204
- }
205
-
206
- if (bindings.length === 0) {
207
- console.log("No bindings configured.");
208
- return;
209
- }
210
-
211
- console.log(`Bindings (${configPath}):`);
212
- for (const binding of bindings) {
213
- const routeSuffix = binding.routeExists ? "" : " [missing route]";
214
- const projectSuffix = binding.projectRoot ? ` (${binding.projectRoot})` : "";
215
- console.log(
216
- `- ${binding.bindingId}: ${binding.connectorId}/${binding.sourceType}:${binding.sourceId} -> ${binding.routeId}${routeSuffix}${projectSuffix}`,
217
- );
218
- }
219
- }
220
-
221
- export async function runBindingSetCommand(options: {
222
- bindingId: string;
223
- connectorId: string;
224
- routeId: string;
225
- sourceType: "channel" | "chat";
226
- sourceId: string;
227
- }): Promise<void> {
228
- const configPath = resolveConfigPath();
229
- const rawConfig = await requireRawConfig(configPath);
230
- const normalized = ensureGatewayConfigShape(structuredClone(rawConfig));
231
-
232
- if (!normalized.connectors.items[options.connectorId]) {
233
- throw new Error(`Connector '${options.connectorId}' does not exist`);
234
- }
235
- if (!normalized.routes.items[options.routeId]) {
236
- throw new Error(`Route '${options.routeId}' does not exist`);
237
- }
238
-
239
- const duplicate = Object.entries(normalized.bindings.items).find(([bindingId, binding]) =>
240
- bindingId !== options.bindingId
241
- && binding.connector === options.connectorId
242
- && binding.source.type === options.sourceType
243
- && binding.source.id === options.sourceId
244
- );
245
- if (duplicate) {
246
- throw new Error(
247
- `Binding source '${options.connectorId}/${options.sourceType}:${options.sourceId}' is already used by '${duplicate[0]}'`,
248
- );
249
- }
250
-
251
- const binding: RawBindingConfig = {
252
- connector: options.connectorId,
253
- source: {
254
- type: options.sourceType,
255
- id: options.sourceId,
256
- },
257
- route: options.routeId,
258
- };
259
- upsertBinding(normalized, options.bindingId, binding);
260
-
261
- await saveConfig(configPath, normalized);
262
- console.log(`Upserted binding '${options.bindingId}'`);
263
- }
264
-
265
- export async function runBindingRemoveCommand(options: {
266
- bindingId: string;
267
- }): Promise<void> {
268
- const configPath = resolveConfigPath();
269
- const rawConfig = await requireRawConfig(configPath);
270
- const normalized = ensureGatewayConfigShape(structuredClone(rawConfig));
271
-
272
- if (!normalized.bindings.items[options.bindingId]) {
273
- throw new Error(`Binding '${options.bindingId}' not found`);
274
- }
275
-
276
- delete normalized.bindings.items[options.bindingId];
277
- await saveConfig(configPath, normalized);
278
- console.log(`Removed binding '${options.bindingId}'`);
279
- }
280
-
281
- export async function runRouteListCommand(options: {
282
- json?: boolean;
283
- }): Promise<void> {
284
- const configPath = resolveConfigPath();
285
- const rawConfig = await requireRawConfig(configPath);
286
- const routes = listRoutes(rawConfig);
287
-
288
- if (options.json) {
289
- console.log(JSON.stringify({ configPath, routes }, null, 2));
290
- return;
291
- }
292
-
293
- if (routes.length === 0) {
294
- console.log("No routes configured.");
295
- return;
296
- }
297
-
298
- console.log(`Routes (${configPath}):`);
299
- for (const route of routes) {
300
- const providerInfo = route.provider ? route.provider : "(route default)";
301
- const sandboxInfo = route.sandbox ? route.sandbox : BUILTIN_HOST_SANDBOX_ID;
302
- console.log(
303
- `- ${route.routeId}: ${route.projectRoot}, tools=${route.tools}, mentions=${route.mentions}, provider=${providerInfo}, sandbox=${sandboxInfo}, bindings=${route.bindings}`,
304
- );
305
- }
306
- }
307
-
308
- export async function runRouteSetCommand(options: {
309
- routeId: string;
310
- projectRoot?: string;
311
- tools?: string;
312
- providerId?: string;
313
- sandboxId?: string;
314
- mentions?: "required" | "optional";
315
- }): Promise<void> {
316
- const configPath = resolveConfigPath();
317
- const rawConfig = await requireRawConfig(configPath);
318
- const normalized = ensureGatewayConfigShape(structuredClone(rawConfig));
319
- const existing = normalized.routes.items[options.routeId];
320
-
321
- const projectRoot = options.projectRoot?.trim() || existing?.projectRoot;
322
- if (!projectRoot) {
323
- throw new Error("--project-root is required when creating a new route");
324
- }
325
-
326
- const toolsRaw = options.tools ?? existing?.tools;
327
- if (toolsRaw !== undefined && toolsRaw !== "full" && toolsRaw !== "readonly") {
328
- throw new Error(`Invalid --tools '${toolsRaw}'. Allowed: full, readonly`);
329
- }
330
-
331
- const provider = options.providerId ?? existing?.provider;
332
- if (provider && !normalized.providers.items[provider]) {
333
- throw new Error(`Provider '${provider}' does not exist`);
334
- }
335
-
336
- const sandbox = options.sandboxId ?? existing?.sandbox;
337
- if (sandbox && sandbox !== BUILTIN_HOST_SANDBOX_ID && !normalized.sandboxes.items[sandbox]) {
338
- throw new Error(`Sandbox '${sandbox}' does not exist`);
339
- }
340
-
341
- upsertRoute(normalized, options.routeId, {
342
- projectRoot,
343
- ...(toolsRaw ? { tools: toolsRaw } : {}),
344
- ...((options.mentions ?? existing?.mentions) ? { mentions: (options.mentions ?? existing?.mentions)! } : {}),
345
- ...(provider ? { provider } : {}),
346
- ...(sandbox ? { sandbox } : {}),
347
- ...(typeof existing?.systemPromptFile === "string" ? { systemPromptFile: existing.systemPromptFile } : {}),
348
- });
349
-
350
- await saveConfig(configPath, normalized);
351
- console.log(`Upserted route '${options.routeId}'`);
352
- }
353
-
354
- export async function runRouteRemoveCommand(options: {
355
- routeId: string;
356
- cascadeBindings?: boolean;
357
- }): Promise<void> {
358
- const configPath = resolveConfigPath();
359
- const rawConfig = await requireRawConfig(configPath);
360
- const normalized = ensureGatewayConfigShape(structuredClone(rawConfig));
361
-
362
- if (!normalized.routes.items[options.routeId]) {
363
- throw new Error(`Route '${options.routeId}' not found`);
364
- }
365
-
366
- const bindingRefs = listBindings(normalized).filter((binding) => binding.routeId === options.routeId);
367
- if (bindingRefs.length > 0 && !options.cascadeBindings) {
368
- const refList = bindingRefs.map((binding) => binding.bindingId).join(", ");
369
- throw new Error(
370
- `Route '${options.routeId}' is referenced by bindings (${refList}). Re-run with --cascade-bindings to remove these bindings automatically.`,
371
- );
372
- }
373
-
374
- if (bindingRefs.length > 0 && options.cascadeBindings) {
375
- for (const binding of bindingRefs) {
376
- delete normalized.bindings.items[binding.bindingId];
377
- }
378
- }
379
-
380
- delete normalized.routes.items[options.routeId];
381
- await saveConfig(configPath, normalized);
382
- console.log(`Removed route '${options.routeId}'`);
383
- }
package/src/cli/index.ts DELETED
@@ -1,9 +0,0 @@
1
- import { buildProgram } from "./program.js";
2
-
3
- /**
4
- * Runs the CLI entrypoint with the provided argv vector.
5
- */
6
- export async function runCli(argv = process.argv): Promise<void> {
7
- const program = buildProgram();
8
- await program.parseAsync(argv);
9
- }