@dobby.ai/dobby 0.1.1 → 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 (136) hide show
  1. package/README.md +20 -7
  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/program.js +0 -6
  5. package/dist/src/core/types.js +2 -0
  6. package/dist/src/cron/config.js +2 -2
  7. package/dist/src/cron/service.js +87 -23
  8. package/dist/src/cron/store.js +1 -1
  9. package/package.json +9 -3
  10. package/.env.example +0 -8
  11. package/AGENTS.md +0 -267
  12. package/ROADMAP.md +0 -34
  13. package/config/cron.example.json +0 -9
  14. package/config/gateway.example.json +0 -132
  15. package/dist/plugins/connector-discord/src/mapper.js +0 -75
  16. package/dist/src/cli/tests/config-command.test.js +0 -42
  17. package/dist/src/cli/tests/config-io.test.js +0 -64
  18. package/dist/src/cli/tests/config-mutators.test.js +0 -47
  19. package/dist/src/cli/tests/discord-mapper.test.js +0 -90
  20. package/dist/src/cli/tests/doctor.test.js +0 -252
  21. package/dist/src/cli/tests/init-catalog.test.js +0 -134
  22. package/dist/src/cli/tests/program-options.test.js +0 -78
  23. package/dist/src/cli/tests/routing-config.test.js +0 -254
  24. package/dist/src/core/tests/control-command.test.js +0 -17
  25. package/dist/src/core/tests/runtime-registry.test.js +0 -116
  26. package/dist/src/core/tests/typing-controller.test.js +0 -103
  27. package/docs/BOXLITE_SANDBOX_FEASIBILITY.md +0 -175
  28. package/docs/CRON_SCHEDULER_DESIGN.md +0 -374
  29. package/docs/DOCKER_SANDBOX_vs_BOXLITE.md +0 -77
  30. package/docs/EXTENSION_SYSTEM_ARCHITECTURE.md +0 -119
  31. package/docs/MVP.md +0 -135
  32. package/docs/RUNBOOK.md +0 -243
  33. package/docs/TEAMWORK_HANDOFF_DESIGN.md +0 -440
  34. package/plugins/connector-discord/dobby.manifest.json +0 -18
  35. package/plugins/connector-discord/index.js +0 -1
  36. package/plugins/connector-discord/package-lock.json +0 -360
  37. package/plugins/connector-discord/package.json +0 -38
  38. package/plugins/connector-discord/src/connector.ts +0 -345
  39. package/plugins/connector-discord/src/contribution.ts +0 -21
  40. package/plugins/connector-discord/src/mapper.ts +0 -101
  41. package/plugins/connector-discord/tsconfig.json +0 -19
  42. package/plugins/connector-feishu/dobby.manifest.json +0 -18
  43. package/plugins/connector-feishu/index.js +0 -1
  44. package/plugins/connector-feishu/package-lock.json +0 -618
  45. package/plugins/connector-feishu/package.json +0 -38
  46. package/plugins/connector-feishu/src/connector.ts +0 -343
  47. package/plugins/connector-feishu/src/contribution.ts +0 -26
  48. package/plugins/connector-feishu/src/mapper.ts +0 -401
  49. package/plugins/connector-feishu/tsconfig.json +0 -19
  50. package/plugins/plugin-sdk/index.d.ts +0 -261
  51. package/plugins/plugin-sdk/index.js +0 -1
  52. package/plugins/plugin-sdk/package-lock.json +0 -12
  53. package/plugins/plugin-sdk/package.json +0 -22
  54. package/plugins/provider-claude/dobby.manifest.json +0 -17
  55. package/plugins/provider-claude/index.js +0 -1
  56. package/plugins/provider-claude/package-lock.json +0 -3398
  57. package/plugins/provider-claude/package.json +0 -39
  58. package/plugins/provider-claude/src/contribution.ts +0 -1018
  59. package/plugins/provider-claude/tsconfig.json +0 -19
  60. package/plugins/provider-claude-cli/dobby.manifest.json +0 -17
  61. package/plugins/provider-claude-cli/index.js +0 -1
  62. package/plugins/provider-claude-cli/package-lock.json +0 -2898
  63. package/plugins/provider-claude-cli/package.json +0 -38
  64. package/plugins/provider-claude-cli/src/contribution.ts +0 -1673
  65. package/plugins/provider-claude-cli/tsconfig.json +0 -19
  66. package/plugins/provider-pi/dobby.manifest.json +0 -17
  67. package/plugins/provider-pi/index.js +0 -1
  68. package/plugins/provider-pi/package-lock.json +0 -3877
  69. package/plugins/provider-pi/package.json +0 -40
  70. package/plugins/provider-pi/src/contribution.ts +0 -606
  71. package/plugins/provider-pi/tsconfig.json +0 -19
  72. package/plugins/sandbox-core/boxlite.js +0 -1
  73. package/plugins/sandbox-core/dobby.manifest.json +0 -17
  74. package/plugins/sandbox-core/docker.js +0 -1
  75. package/plugins/sandbox-core/package-lock.json +0 -136
  76. package/plugins/sandbox-core/package.json +0 -39
  77. package/plugins/sandbox-core/src/boxlite-context.ts +0 -2
  78. package/plugins/sandbox-core/src/boxlite-contribution.ts +0 -53
  79. package/plugins/sandbox-core/src/boxlite-executor.ts +0 -911
  80. package/plugins/sandbox-core/src/docker-contribution.ts +0 -43
  81. package/plugins/sandbox-core/src/docker-executor.ts +0 -217
  82. package/plugins/sandbox-core/tsconfig.json +0 -19
  83. package/scripts/local-extensions.mjs +0 -168
  84. package/src/agent/event-forwarder.ts +0 -414
  85. package/src/cli/commands/config.ts +0 -328
  86. package/src/cli/commands/configure.ts +0 -92
  87. package/src/cli/commands/cron.ts +0 -410
  88. package/src/cli/commands/doctor.ts +0 -331
  89. package/src/cli/commands/extension.ts +0 -207
  90. package/src/cli/commands/init.ts +0 -211
  91. package/src/cli/commands/start.ts +0 -223
  92. package/src/cli/commands/topology.ts +0 -415
  93. package/src/cli/index.ts +0 -9
  94. package/src/cli/program.ts +0 -314
  95. package/src/cli/shared/config-io.ts +0 -245
  96. package/src/cli/shared/config-mutators.ts +0 -470
  97. package/src/cli/shared/config-schema.ts +0 -228
  98. package/src/cli/shared/config-types.ts +0 -129
  99. package/src/cli/shared/configure-sections.ts +0 -595
  100. package/src/cli/shared/discord-config.ts +0 -14
  101. package/src/cli/shared/init-catalog.ts +0 -249
  102. package/src/cli/shared/local-extension-specs.ts +0 -108
  103. package/src/cli/shared/runtime.ts +0 -33
  104. package/src/cli/shared/schema-prompts.ts +0 -443
  105. package/src/cli/tests/config-command.test.ts +0 -56
  106. package/src/cli/tests/config-io.test.ts +0 -92
  107. package/src/cli/tests/config-mutators.test.ts +0 -59
  108. package/src/cli/tests/discord-mapper.test.ts +0 -128
  109. package/src/cli/tests/doctor.test.ts +0 -269
  110. package/src/cli/tests/init-catalog.test.ts +0 -144
  111. package/src/cli/tests/program-options.test.ts +0 -95
  112. package/src/cli/tests/routing-config.test.ts +0 -281
  113. package/src/core/control-command.ts +0 -12
  114. package/src/core/dedup-store.ts +0 -103
  115. package/src/core/gateway.ts +0 -609
  116. package/src/core/routing.ts +0 -404
  117. package/src/core/runtime-registry.ts +0 -141
  118. package/src/core/tests/control-command.test.ts +0 -20
  119. package/src/core/tests/runtime-registry.test.ts +0 -140
  120. package/src/core/tests/typing-controller.test.ts +0 -129
  121. package/src/core/types.ts +0 -324
  122. package/src/core/typing-controller.ts +0 -119
  123. package/src/cron/config.ts +0 -154
  124. package/src/cron/schedule.ts +0 -61
  125. package/src/cron/service.ts +0 -249
  126. package/src/cron/store.ts +0 -155
  127. package/src/cron/types.ts +0 -60
  128. package/src/extension/loader.ts +0 -145
  129. package/src/extension/manager.ts +0 -355
  130. package/src/extension/manifest.ts +0 -26
  131. package/src/extension/registry.ts +0 -229
  132. package/src/main.ts +0 -8
  133. package/src/sandbox/executor.ts +0 -44
  134. package/src/sandbox/host-executor.ts +0 -118
  135. package/src/shared/dobby-repo.ts +0 -48
  136. package/tsconfig.json +0 -18
@@ -1,595 +0,0 @@
1
- import {
2
- cancel,
3
- confirm,
4
- isCancel,
5
- note,
6
- password,
7
- select,
8
- text,
9
- } from "@clack/prompts";
10
- import JSON5 from "json5";
11
- import {
12
- ensureGatewayConfigShape,
13
- setDefaultBinding,
14
- setDefaultProviderIfMissingOrInvalid,
15
- upsertBinding,
16
- upsertConnectorInstance,
17
- upsertProviderInstance,
18
- upsertRoute,
19
- } from "./config-mutators.js";
20
- import {
21
- DEFAULT_DISCORD_BOT_NAME,
22
- DISCORD_CONNECTOR_CONTRIBUTION_ID,
23
- } from "./discord-config.js";
24
- import type { RawBindingConfig, RawGatewayConfig } from "./config-types.js";
25
- import { promptConfigFromSchema } from "./schema-prompts.js";
26
-
27
- export type ConfigureSection = "provider" | "connector" | "route" | "binding" | "sandbox" | "data";
28
-
29
- export const CONFIGURE_SECTION_VALUES: ConfigureSection[] = ["provider", "connector", "route", "binding", "sandbox", "data"];
30
-
31
- export interface ConfigureSectionContext {
32
- schemaByContributionId?: ReadonlyMap<string, Record<string, unknown>>;
33
- schemaStateByContributionId?: ReadonlyMap<string, "with_schema" | "without_schema">;
34
- }
35
-
36
- type SchemaFallbackState = "without_schema" | "not_loaded";
37
-
38
- const SECTION_ORDER: Record<ConfigureSection, number> = {
39
- provider: 1,
40
- connector: 2,
41
- sandbox: 3,
42
- route: 4,
43
- binding: 5,
44
- data: 6,
45
- };
46
-
47
- export function isConfigureSection(value: string): value is ConfigureSection {
48
- return CONFIGURE_SECTION_VALUES.includes(value as ConfigureSection);
49
- }
50
-
51
- export function normalizeConfigureSectionOrder(sections: ConfigureSection[]): ConfigureSection[] {
52
- const unique: ConfigureSection[] = [];
53
- for (const section of sections) {
54
- if (!unique.includes(section)) {
55
- unique.push(section);
56
- }
57
- }
58
-
59
- return unique.sort((a, b) => SECTION_ORDER[a] - SECTION_ORDER[b]);
60
- }
61
-
62
- async function requiredText(message: string, initialValue?: string, placeholder?: string): Promise<string> {
63
- while (true) {
64
- const result = await text({
65
- message,
66
- ...(initialValue !== undefined ? { initialValue } : {}),
67
- ...(placeholder !== undefined ? { placeholder } : {}),
68
- });
69
- if (isCancel(result)) {
70
- cancel("Configure cancelled.");
71
- throw new Error("Configure cancelled.");
72
- }
73
-
74
- const trimmed = String(result ?? "").trim();
75
- if (trimmed.length > 0) {
76
- return trimmed;
77
- }
78
-
79
- await note("This field is required.", "Validation");
80
- }
81
- }
82
-
83
- async function optionalText(message: string, initialValue?: string, placeholder?: string): Promise<string | undefined> {
84
- const result = await text({
85
- message,
86
- ...(initialValue !== undefined ? { initialValue } : {}),
87
- ...(placeholder !== undefined ? { placeholder } : {}),
88
- });
89
- if (isCancel(result)) {
90
- cancel("Configure cancelled.");
91
- throw new Error("Configure cancelled.");
92
- }
93
-
94
- const trimmed = String(result ?? "").trim();
95
- return trimmed.length > 0 ? trimmed : undefined;
96
- }
97
-
98
- async function noteSchemaFallback(
99
- contributionId: string,
100
- context?: ConfigureSectionContext,
101
- ): Promise<SchemaFallbackState> {
102
- const state = context?.schemaStateByContributionId?.get(contributionId);
103
- if (state === "without_schema") {
104
- await note(
105
- `Contribution '${contributionId}' is loaded but does not expose configSchema. Falling back to JSON input.`,
106
- "Schema",
107
- );
108
- return "without_schema";
109
- }
110
-
111
- await note(
112
- `No loaded schema for contribution '${contributionId}'. The extension may be disabled or not installed. Falling back to JSON input.`,
113
- "Schema",
114
- );
115
- return "not_loaded";
116
- }
117
-
118
- async function confirmUnloadedSchemaFallback(contributionId: string, state: SchemaFallbackState): Promise<void> {
119
- if (state !== "not_loaded") {
120
- return;
121
- }
122
-
123
- const proceed = await confirm({
124
- message: `Continue with raw JSON for '${contributionId}'? Defaults will not be auto-applied.`,
125
- initialValue: false,
126
- });
127
- if (isCancel(proceed)) {
128
- cancel("Configure cancelled.");
129
- throw new Error("Configure cancelled.");
130
- }
131
- if (!proceed) {
132
- throw new Error(
133
- `Cannot continue without schema for contribution '${contributionId}'. ` +
134
- `Enable/install the extension first (check 'extensions.allowList'), then retry.`,
135
- );
136
- }
137
- }
138
-
139
- async function promptJsonObject(
140
- message: string,
141
- initialValue: Record<string, unknown>,
142
- ): Promise<Record<string, unknown>> {
143
- const result = await text({
144
- message,
145
- initialValue: JSON.stringify(initialValue, null, 2),
146
- });
147
- if (isCancel(result)) {
148
- cancel("Configure cancelled.");
149
- throw new Error("Configure cancelled.");
150
- }
151
-
152
- const rawText = String(result ?? "{}").trim();
153
- if (rawText.length === 0) {
154
- return {};
155
- }
156
-
157
- const parsed = JSON5.parse(rawText);
158
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
159
- throw new Error("Config must be a JSON object");
160
- }
161
-
162
- return parsed as Record<string, unknown>;
163
- }
164
-
165
- async function configureProviderSection(config: RawGatewayConfig, context?: ConfigureSectionContext): Promise<void> {
166
- const next = ensureGatewayConfigShape(config);
167
- const providerItems = next.providers.items;
168
-
169
- const providerChoices = Object.keys(providerItems).sort((a, b) => a.localeCompare(b));
170
- const targetProvider = providerChoices.length === 0
171
- ? "__new"
172
- : await select({
173
- message: "Select provider instance",
174
- options: [
175
- ...providerChoices.map((id) => ({ value: id, label: id })),
176
- { value: "__new", label: "Create new provider instance" },
177
- ],
178
- initialValue: providerChoices[0],
179
- });
180
- if (isCancel(targetProvider)) {
181
- cancel("Configure cancelled.");
182
- throw new Error("Configure cancelled.");
183
- }
184
-
185
- const instanceId = String(targetProvider) === "__new"
186
- ? await requiredText("New provider instance ID", "pi.main")
187
- : String(targetProvider);
188
- const existing = providerItems[instanceId];
189
- const contributionId = await requiredText(
190
- "Provider contribution ID",
191
- existing?.type ?? "provider.pi",
192
- "provider.pi",
193
- );
194
-
195
- let providerConfig: Record<string, unknown> = {};
196
- const existingConfig = existing ? Object.fromEntries(Object.entries(existing).filter(([key]) => key !== "type")) : {};
197
- if (context?.schemaByContributionId?.has(contributionId)) {
198
- providerConfig = await promptConfigFromSchema(
199
- context.schemaByContributionId.get(contributionId)!,
200
- existingConfig,
201
- { title: `Provider '${instanceId}' (${contributionId})` },
202
- );
203
- } else {
204
- const fallbackState = await noteSchemaFallback(contributionId, context);
205
- await confirmUnloadedSchemaFallback(contributionId, fallbackState);
206
- providerConfig = await promptJsonObject("Provider config JSON", existingConfig);
207
- }
208
-
209
- upsertProviderInstance(next, instanceId, contributionId, providerConfig);
210
- setDefaultProviderIfMissingOrInvalid(next);
211
-
212
- const providerIds = Object.keys(next.providers.items).sort((a, b) => a.localeCompare(b));
213
- if (providerIds.length > 0) {
214
- const defaultProvider = await select({
215
- message: "Default provider",
216
- options: providerIds.map((id) => ({ value: id, label: id })),
217
- initialValue: next.providers.default && providerIds.includes(next.providers.default) ? next.providers.default : providerIds[0],
218
- });
219
- if (isCancel(defaultProvider)) {
220
- cancel("Configure cancelled.");
221
- throw new Error("Configure cancelled.");
222
- }
223
- next.providers.default = String(defaultProvider);
224
- next.routes.default.provider = String(defaultProvider);
225
- }
226
-
227
- Object.assign(config, next);
228
- }
229
-
230
- async function configureConnectorSection(config: RawGatewayConfig, context?: ConfigureSectionContext): Promise<void> {
231
- const next = ensureGatewayConfigShape(config);
232
- const connectorItems = next.connectors.items;
233
-
234
- const connectorChoices = Object.keys(connectorItems).sort((a, b) => a.localeCompare(b));
235
- const targetConnector = connectorChoices.length === 0
236
- ? "__new"
237
- : await select({
238
- message: "Select connector instance",
239
- options: [
240
- ...connectorChoices.map((id) => ({ value: id, label: id })),
241
- { value: "__new", label: "Create new connector instance" },
242
- ],
243
- initialValue: connectorChoices[0],
244
- });
245
- if (isCancel(targetConnector)) {
246
- cancel("Configure cancelled.");
247
- throw new Error("Configure cancelled.");
248
- }
249
-
250
- const instanceId = String(targetConnector) === "__new"
251
- ? await requiredText("New connector instance ID", "discord.main")
252
- : String(targetConnector);
253
- const existing = connectorItems[instanceId];
254
- const contributionId = await requiredText(
255
- "Connector contribution ID",
256
- existing?.type ?? DISCORD_CONNECTOR_CONTRIBUTION_ID,
257
- DISCORD_CONNECTOR_CONTRIBUTION_ID,
258
- );
259
-
260
- let connectorConfig: Record<string, unknown> = {};
261
- const existingConfig = existing ? Object.fromEntries(Object.entries(existing).filter(([key]) => key !== "type")) : {};
262
- if (contributionId === DISCORD_CONNECTOR_CONTRIBUTION_ID) {
263
- const botName = await requiredText(
264
- "Discord botName",
265
- typeof existing?.botName === "string" ? existing.botName : DEFAULT_DISCORD_BOT_NAME,
266
- );
267
- const botTokenResult = await password({
268
- message: "Discord botToken (leave blank to keep current value)",
269
- mask: "*",
270
- });
271
- if (isCancel(botTokenResult)) {
272
- cancel("Configure cancelled.");
273
- throw new Error("Configure cancelled.");
274
- }
275
- const providedBotToken = String(botTokenResult ?? "").trim();
276
- const existingBotToken = typeof existing?.botToken === "string" ? existing.botToken : "";
277
- const botToken = providedBotToken.length > 0 ? providedBotToken : existingBotToken;
278
- if (botToken.length === 0) {
279
- throw new Error("Discord botToken is required");
280
- }
281
- connectorConfig = {
282
- ...existingConfig,
283
- botName,
284
- botToken,
285
- };
286
- } else if (context?.schemaByContributionId?.has(contributionId)) {
287
- connectorConfig = await promptConfigFromSchema(
288
- context.schemaByContributionId.get(contributionId)!,
289
- existingConfig,
290
- { title: `Connector '${instanceId}' (${contributionId})` },
291
- );
292
- } else {
293
- const fallbackState = await noteSchemaFallback(contributionId, context);
294
- await confirmUnloadedSchemaFallback(contributionId, fallbackState);
295
- connectorConfig = await promptJsonObject("Connector config JSON", existingConfig);
296
- }
297
-
298
- upsertConnectorInstance(next, instanceId, contributionId, connectorConfig);
299
- Object.assign(config, next);
300
- }
301
-
302
- async function configureRouteSection(config: RawGatewayConfig): Promise<void> {
303
- const next = ensureGatewayConfigShape(config);
304
- const routeItems = next.routes.items;
305
- const routeChoices = Object.keys(routeItems).sort((a, b) => a.localeCompare(b));
306
-
307
- const targetRoute = routeChoices.length === 0
308
- ? "__new"
309
- : await select({
310
- message: "Select route",
311
- options: [
312
- ...routeChoices.map((id) => ({ value: id, label: id })),
313
- { value: "__new", label: "Create new route" },
314
- ],
315
- initialValue: routeChoices[0],
316
- });
317
- if (isCancel(targetRoute)) {
318
- cancel("Configure cancelled.");
319
- throw new Error("Configure cancelled.");
320
- }
321
-
322
- const routeId = String(targetRoute) === "__new" ? await requiredText("New route ID", "main") : String(targetRoute);
323
- const existing = routeItems[routeId];
324
-
325
- const defaultProjectRoot = next.routes.default.projectRoot;
326
- let projectRoot = existing?.projectRoot;
327
- if (defaultProjectRoot) {
328
- const projectRootMode = await select({
329
- message: "projectRoot",
330
- options: [
331
- { value: "__default", label: `Use route default (${defaultProjectRoot})` },
332
- { value: "__custom", label: "Set explicit projectRoot" },
333
- ],
334
- initialValue: existing?.projectRoot ? "__custom" : "__default",
335
- });
336
- if (isCancel(projectRootMode)) {
337
- cancel("Configure cancelled.");
338
- throw new Error("Configure cancelled.");
339
- }
340
-
341
- projectRoot = projectRootMode === "__custom"
342
- ? await requiredText("projectRoot", existing?.projectRoot ?? defaultProjectRoot)
343
- : undefined;
344
- } else {
345
- projectRoot = await requiredText("projectRoot", existing?.projectRoot ?? process.cwd());
346
- }
347
- const tools = await select({
348
- message: "tools",
349
- options: [
350
- { value: "__default", label: `Use route default (${next.routes.default.tools ?? "full"})` },
351
- { value: "full", label: "full" },
352
- { value: "readonly", label: "readonly" },
353
- ],
354
- initialValue: existing?.tools ?? "__default",
355
- });
356
- if (isCancel(tools)) {
357
- cancel("Configure cancelled.");
358
- throw new Error("Configure cancelled.");
359
- }
360
-
361
- const mentions = await select({
362
- message: "mentions",
363
- options: [
364
- { value: "__default", label: `Use route default (${next.routes.default.mentions ?? "required"})` },
365
- { value: "required", label: "required" },
366
- { value: "optional", label: "optional" },
367
- ],
368
- initialValue: existing?.mentions ?? "__default",
369
- });
370
- if (isCancel(mentions)) {
371
- cancel("Configure cancelled.");
372
- throw new Error("Configure cancelled.");
373
- }
374
-
375
- const providerIds = Object.keys(next.providers.items).sort((a, b) => a.localeCompare(b));
376
- const providerValue = providerIds.length > 0
377
- ? await select({
378
- message: "provider",
379
- options: [
380
- { value: "__default", label: `Use route default (${(next.routes.default.provider ?? next.providers.default) || "(unset)"})` },
381
- ...providerIds.map((id) => ({ value: id, label: id })),
382
- ],
383
- initialValue: existing?.provider ?? "__default",
384
- })
385
- : "__default";
386
- if (isCancel(providerValue)) {
387
- cancel("Configure cancelled.");
388
- throw new Error("Configure cancelled.");
389
- }
390
-
391
- const sandboxIds = ["host.builtin", ...Object.keys(next.sandboxes.items).sort((a, b) => a.localeCompare(b))];
392
- const sandboxValue = await select({
393
- message: "sandbox",
394
- options: [
395
- { value: "__default", label: `Use route default (${next.routes.default.sandbox ?? next.sandboxes.default})` },
396
- ...sandboxIds.map((id) => ({ value: id, label: id })),
397
- ],
398
- initialValue: existing?.sandbox ?? "__default",
399
- });
400
- if (isCancel(sandboxValue)) {
401
- cancel("Configure cancelled.");
402
- throw new Error("Configure cancelled.");
403
- }
404
-
405
- const systemPromptFile = await optionalText("systemPromptFile (optional)", existing?.systemPromptFile ?? "");
406
-
407
- upsertRoute(next, routeId, {
408
- ...(projectRoot ? { projectRoot } : {}),
409
- ...(tools !== "__default" ? { tools: String(tools) as "full" | "readonly" } : {}),
410
- ...(mentions !== "__default" ? { mentions: String(mentions) as "required" | "optional" } : {}),
411
- ...(providerValue !== "__default" ? { provider: String(providerValue) } : {}),
412
- ...(sandboxValue !== "__default" ? { sandbox: String(sandboxValue) } : {}),
413
- ...(systemPromptFile ? { systemPromptFile } : {}),
414
- });
415
-
416
- Object.assign(config, next);
417
- }
418
-
419
- async function configureBindingSection(config: RawGatewayConfig): Promise<void> {
420
- const next = ensureGatewayConfigShape(config);
421
- const bindingItems = next.bindings.items;
422
- const bindingChoices = Object.keys(bindingItems).sort((a, b) => a.localeCompare(b));
423
-
424
- if (Object.keys(next.connectors.items).length === 0) {
425
- throw new Error("No connectors found. Configure connectors first.");
426
- }
427
- if (Object.keys(next.routes.items).length === 0) {
428
- throw new Error("No routes found. Configure routes first.");
429
- }
430
-
431
- const targetBinding = bindingChoices.length === 0
432
- ? (next.bindings.default ? "__default" : "__new")
433
- : await select({
434
- message: "Select binding",
435
- options: [
436
- { value: "__default", label: next.bindings.default ? "Edit default direct-message binding" : "Create default direct-message binding" },
437
- ...bindingChoices.map((id) => ({ value: id, label: id })),
438
- { value: "__new", label: "Create new binding" },
439
- ],
440
- initialValue: next.bindings.default ? "__default" : bindingChoices[0],
441
- });
442
- if (isCancel(targetBinding)) {
443
- cancel("Configure cancelled.");
444
- throw new Error("Configure cancelled.");
445
- }
446
-
447
- const routeIds = Object.keys(next.routes.items).sort((a, b) => a.localeCompare(b));
448
- if (String(targetBinding) === "__default") {
449
- const defaultRouteId = await select({
450
- message: "Default direct-message route",
451
- options: routeIds.map((id) => ({ value: id, label: id })),
452
- initialValue:
453
- next.bindings.default?.route && routeIds.includes(next.bindings.default.route)
454
- ? next.bindings.default.route
455
- : routeIds[0],
456
- });
457
- if (isCancel(defaultRouteId)) {
458
- cancel("Configure cancelled.");
459
- throw new Error("Configure cancelled.");
460
- }
461
-
462
- setDefaultBinding(next, {
463
- route: String(defaultRouteId),
464
- });
465
- Object.assign(config, next);
466
- return;
467
- }
468
-
469
- const bindingId = String(targetBinding) === "__new"
470
- ? await requiredText("New binding ID", "discord.main.main")
471
- : String(targetBinding);
472
- const existing = bindingItems[bindingId];
473
-
474
- const connectorIds = Object.keys(next.connectors.items).sort((a, b) => a.localeCompare(b));
475
- const connectorId = await select({
476
- message: "connector",
477
- options: connectorIds.map((id) => ({ value: id, label: id })),
478
- initialValue: existing?.connector && connectorIds.includes(existing.connector) ? existing.connector : connectorIds[0],
479
- });
480
- if (isCancel(connectorId)) {
481
- cancel("Configure cancelled.");
482
- throw new Error("Configure cancelled.");
483
- }
484
-
485
- const sourceType = await select({
486
- message: "source.type",
487
- options: [
488
- { value: "channel", label: "channel" },
489
- { value: "chat", label: "chat" },
490
- ],
491
- initialValue: existing?.source.type ?? "channel",
492
- });
493
- if (isCancel(sourceType)) {
494
- cancel("Configure cancelled.");
495
- throw new Error("Configure cancelled.");
496
- }
497
-
498
- const sourceId = await requiredText("source.id", existing?.source.id);
499
- const routeId = await select({
500
- message: "route",
501
- options: routeIds.map((id) => ({ value: id, label: id })),
502
- initialValue: existing?.route && routeIds.includes(existing.route) ? existing.route : routeIds[0],
503
- });
504
- if (isCancel(routeId)) {
505
- cancel("Configure cancelled.");
506
- throw new Error("Configure cancelled.");
507
- }
508
-
509
- const duplicate = Object.entries(next.bindings.items).find(([existingBindingId, binding]) =>
510
- existingBindingId !== bindingId
511
- && binding.connector === connectorId
512
- && binding.source.type === sourceType
513
- && binding.source.id === sourceId
514
- );
515
- if (duplicate) {
516
- throw new Error(
517
- `Binding source '${String(connectorId)}/${String(sourceType)}:${sourceId}' is already used by '${duplicate[0]}'`,
518
- );
519
- }
520
-
521
- upsertBinding(next, bindingId, {
522
- connector: String(connectorId),
523
- source: {
524
- type: String(sourceType) as "channel" | "chat",
525
- id: sourceId,
526
- },
527
- route: String(routeId),
528
- });
529
-
530
- Object.assign(config, next);
531
- }
532
-
533
- async function configureSandboxSection(config: RawGatewayConfig): Promise<void> {
534
- const next = ensureGatewayConfigShape(config);
535
- const sandboxIds = ["host.builtin", ...Object.keys(next.sandboxes.items).sort((a, b) => a.localeCompare(b))];
536
-
537
- const defaultSandbox = await select({
538
- message: "Default sandbox",
539
- options: sandboxIds.map((id) => ({ value: id, label: id })),
540
- initialValue: next.sandboxes.default && sandboxIds.includes(next.sandboxes.default) ? next.sandboxes.default : "host.builtin",
541
- });
542
- if (isCancel(defaultSandbox)) {
543
- cancel("Configure cancelled.");
544
- throw new Error("Configure cancelled.");
545
- }
546
-
547
- next.sandboxes.default = String(defaultSandbox);
548
- next.routes.default.sandbox = String(defaultSandbox);
549
- Object.assign(config, next);
550
- }
551
-
552
- async function configureDataSection(config: RawGatewayConfig): Promise<void> {
553
- const next = ensureGatewayConfigShape(config);
554
- const rootDir = await requiredText("data.rootDir", next.data.rootDir);
555
- const dedupTtlMsRaw = await requiredText("data.dedupTtlMs", String(next.data.dedupTtlMs));
556
- const dedupTtlMs = Number.parseInt(dedupTtlMsRaw, 10);
557
- if (!Number.isFinite(dedupTtlMs) || dedupTtlMs <= 0) {
558
- throw new Error("data.dedupTtlMs must be a positive integer");
559
- }
560
-
561
- next.data = {
562
- ...next.data,
563
- rootDir,
564
- dedupTtlMs,
565
- };
566
- Object.assign(config, next);
567
- }
568
-
569
- export async function applyConfigureSection(
570
- section: ConfigureSection,
571
- config: RawGatewayConfig,
572
- context?: ConfigureSectionContext,
573
- ): Promise<void> {
574
- if (section === "provider") {
575
- await configureProviderSection(config, context);
576
- return;
577
- }
578
- if (section === "connector") {
579
- await configureConnectorSection(config, context);
580
- return;
581
- }
582
- if (section === "route") {
583
- await configureRouteSection(config);
584
- return;
585
- }
586
- if (section === "binding") {
587
- await configureBindingSection(config);
588
- return;
589
- }
590
- if (section === "sandbox") {
591
- await configureSandboxSection(config);
592
- return;
593
- }
594
- await configureDataSection(config);
595
- }
@@ -1,14 +0,0 @@
1
- /**
2
- * Contribution id used by the built-in Discord connector plugin.
3
- */
4
- export const DISCORD_CONNECTOR_CONTRIBUTION_ID = "connector.discord";
5
-
6
- /**
7
- * Default connector instance id used by starter init flows.
8
- */
9
- export const DEFAULT_DISCORD_CONNECTOR_INSTANCE_ID = "discord.main";
10
-
11
- /**
12
- * Default human-readable bot name for starter setups.
13
- */
14
- export const DEFAULT_DISCORD_BOT_NAME = "dobby-main";