@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.
- package/README.md +20 -7
- package/dist/src/agent/event-forwarder.js +185 -16
- package/dist/src/cli/commands/cron.js +39 -35
- package/dist/src/cli/program.js +0 -6
- 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/package.json +9 -3
- package/.env.example +0 -8
- package/AGENTS.md +0 -267
- package/ROADMAP.md +0 -34
- package/config/cron.example.json +0 -9
- package/config/gateway.example.json +0 -132
- package/dist/plugins/connector-discord/src/mapper.js +0 -75
- 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/discord-mapper.test.js +0 -90
- package/dist/src/cli/tests/doctor.test.js +0 -252
- package/dist/src/cli/tests/init-catalog.test.js +0 -134
- package/dist/src/cli/tests/program-options.test.js +0 -78
- package/dist/src/cli/tests/routing-config.test.js +0 -254
- package/dist/src/core/tests/control-command.test.js +0 -17
- 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 -243
- 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 -345
- package/plugins/connector-discord/src/contribution.ts +0 -21
- package/plugins/connector-discord/src/mapper.ts +0 -101
- 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 -606
- 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 -331
- package/src/cli/commands/extension.ts +0 -207
- package/src/cli/commands/init.ts +0 -211
- package/src/cli/commands/start.ts +0 -223
- package/src/cli/commands/topology.ts +0 -415
- package/src/cli/index.ts +0 -9
- package/src/cli/program.ts +0 -314
- package/src/cli/shared/config-io.ts +0 -245
- package/src/cli/shared/config-mutators.ts +0 -470
- package/src/cli/shared/config-schema.ts +0 -228
- package/src/cli/shared/config-types.ts +0 -129
- package/src/cli/shared/configure-sections.ts +0 -595
- package/src/cli/shared/discord-config.ts +0 -14
- package/src/cli/shared/init-catalog.ts +0 -249
- package/src/cli/shared/local-extension-specs.ts +0 -108
- package/src/cli/shared/runtime.ts +0 -33
- package/src/cli/shared/schema-prompts.ts +0 -443
- 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/discord-mapper.test.ts +0 -128
- package/src/cli/tests/doctor.test.ts +0 -269
- package/src/cli/tests/init-catalog.test.ts +0 -144
- package/src/cli/tests/program-options.test.ts +0 -95
- package/src/cli/tests/routing-config.test.ts +0 -281
- package/src/core/control-command.ts +0 -12
- package/src/core/dedup-store.ts +0 -103
- package/src/core/gateway.ts +0 -609
- package/src/core/routing.ts +0 -404
- 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 -324
- 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/src/shared/dobby-repo.ts +0 -48
- package/tsconfig.json +0 -18
|
@@ -1,415 +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: string;
|
|
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 effectiveRouteProjectRoot(
|
|
39
|
-
normalized: ReturnType<typeof ensureGatewayConfigShape>,
|
|
40
|
-
routeId: string,
|
|
41
|
-
): string | undefined {
|
|
42
|
-
const route = normalized.routes.items[routeId];
|
|
43
|
-
if (!route) {
|
|
44
|
-
return undefined;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return route.projectRoot ?? normalized.routes.default.projectRoot;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function listDiscordConnectors(rawConfig: unknown): DiscordConnectorView[] {
|
|
51
|
-
const normalized = ensureGatewayConfigShape(rawConfig as RawGatewayConfig);
|
|
52
|
-
const items: DiscordConnectorView[] = [];
|
|
53
|
-
|
|
54
|
-
for (const [connectorId, connector] of Object.entries(normalized.connectors.items)) {
|
|
55
|
-
if (connector.type !== DISCORD_CONNECTOR_CONTRIBUTION_ID) {
|
|
56
|
-
continue;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const botName = typeof connector.botName === "string" ? connector.botName : "";
|
|
60
|
-
const botToken = typeof connector.botToken === "string" ? connector.botToken.trim() : "";
|
|
61
|
-
items.push({
|
|
62
|
-
connectorId,
|
|
63
|
-
type: connector.type,
|
|
64
|
-
botName,
|
|
65
|
-
hasToken: botToken.length > 0,
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return items.sort((a, b) => a.connectorId.localeCompare(b.connectorId));
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function getDiscordConnectorOrThrow(
|
|
73
|
-
rawConfig: unknown,
|
|
74
|
-
connectorId: string,
|
|
75
|
-
): {
|
|
76
|
-
normalized: ReturnType<typeof ensureGatewayConfigShape>;
|
|
77
|
-
connector: Record<string, unknown> & { type: string };
|
|
78
|
-
} {
|
|
79
|
-
const normalized = ensureGatewayConfigShape(rawConfig as RawGatewayConfig);
|
|
80
|
-
const connector = normalized.connectors.items[connectorId];
|
|
81
|
-
if (!connector) {
|
|
82
|
-
throw new Error(`Connector instance '${connectorId}' not found`);
|
|
83
|
-
}
|
|
84
|
-
if (connector.type !== DISCORD_CONNECTOR_CONTRIBUTION_ID) {
|
|
85
|
-
throw new Error(
|
|
86
|
-
`Connector '${connectorId}' uses contribution '${connector.type}'. This command currently supports only '${DISCORD_CONNECTOR_CONTRIBUTION_ID}'.`,
|
|
87
|
-
);
|
|
88
|
-
}
|
|
89
|
-
return {
|
|
90
|
-
normalized,
|
|
91
|
-
connector,
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
function listBindings(rawConfig: unknown, connectorFilter?: string): BindingView[] {
|
|
96
|
-
const normalized = ensureGatewayConfigShape(rawConfig as RawGatewayConfig);
|
|
97
|
-
const bindings: BindingView[] = Object.entries(normalized.bindings.items)
|
|
98
|
-
.filter(([, binding]) => !connectorFilter || binding.connector === connectorFilter)
|
|
99
|
-
.map(([bindingId, binding]) => {
|
|
100
|
-
const route = normalized.routes.items[binding.route];
|
|
101
|
-
const projectRoot = route ? effectiveRouteProjectRoot(normalized, binding.route) : undefined;
|
|
102
|
-
return {
|
|
103
|
-
bindingId,
|
|
104
|
-
connectorId: binding.connector,
|
|
105
|
-
sourceType: binding.source.type,
|
|
106
|
-
sourceId: binding.source.id,
|
|
107
|
-
routeId: binding.route,
|
|
108
|
-
routeExists: Boolean(route),
|
|
109
|
-
...(projectRoot ? { projectRoot } : {}),
|
|
110
|
-
};
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
if (!connectorFilter && normalized.bindings.default) {
|
|
114
|
-
const projectRoot = effectiveRouteProjectRoot(normalized, normalized.bindings.default.route);
|
|
115
|
-
bindings.push({
|
|
116
|
-
bindingId: "bindings.default",
|
|
117
|
-
connectorId: "*",
|
|
118
|
-
sourceType: "direct_message",
|
|
119
|
-
sourceId: "*",
|
|
120
|
-
routeId: normalized.bindings.default.route,
|
|
121
|
-
routeExists: Boolean(normalized.routes.items[normalized.bindings.default.route]),
|
|
122
|
-
...(projectRoot ? { projectRoot } : {}),
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
return bindings.sort((a, b) => a.bindingId.localeCompare(b.bindingId));
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
function buildRouteBindingCounts(rawConfig: unknown): Map<string, number> {
|
|
130
|
-
const normalized = ensureGatewayConfigShape(rawConfig as RawGatewayConfig);
|
|
131
|
-
const counts = new Map<string, number>();
|
|
132
|
-
for (const binding of listBindings(normalized)) {
|
|
133
|
-
counts.set(binding.routeId, (counts.get(binding.routeId) ?? 0) + 1);
|
|
134
|
-
}
|
|
135
|
-
return counts;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function listRoutes(rawConfig: unknown): RouteView[] {
|
|
139
|
-
const normalized = ensureGatewayConfigShape(rawConfig as RawGatewayConfig);
|
|
140
|
-
const counts = buildRouteBindingCounts(normalized);
|
|
141
|
-
|
|
142
|
-
return Object.entries(normalized.routes.items)
|
|
143
|
-
.map(([routeId, route]): RouteView => ({
|
|
144
|
-
routeId,
|
|
145
|
-
projectRoot: effectiveRouteProjectRoot(normalized, routeId) ?? "(unset)",
|
|
146
|
-
tools: route.tools === "readonly" ? "readonly" : "full",
|
|
147
|
-
mentions: route.mentions === "optional" ? "optional" : "required",
|
|
148
|
-
...(route.provider ? { provider: route.provider } : {}),
|
|
149
|
-
...(route.sandbox ? { sandbox: route.sandbox } : {}),
|
|
150
|
-
bindings: counts.get(routeId) ?? 0,
|
|
151
|
-
}))
|
|
152
|
-
.sort((a, b) => a.routeId.localeCompare(b.routeId));
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
async function saveConfig(configPath: string, normalized: ReturnType<typeof ensureGatewayConfigShape>): Promise<void> {
|
|
156
|
-
await writeConfigWithValidation(configPath, normalized, {
|
|
157
|
-
validate: true,
|
|
158
|
-
createBackup: true,
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
export async function runBotListCommand(options: {
|
|
163
|
-
json?: boolean;
|
|
164
|
-
}): Promise<void> {
|
|
165
|
-
const configPath = resolveConfigPath();
|
|
166
|
-
const rawConfig = await requireRawConfig(configPath);
|
|
167
|
-
const bots = listDiscordConnectors(rawConfig);
|
|
168
|
-
|
|
169
|
-
if (options.json) {
|
|
170
|
-
console.log(JSON.stringify({ configPath, bots }, null, 2));
|
|
171
|
-
return;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
if (bots.length === 0) {
|
|
175
|
-
console.log("No Discord connector instances configured.");
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
console.log(`Bots (${configPath}):`);
|
|
180
|
-
for (const bot of bots) {
|
|
181
|
-
console.log(
|
|
182
|
-
`- ${bot.connectorId}: botName='${bot.botName || "(empty)"}', token=${bot.hasToken ? "set" : "missing"}`,
|
|
183
|
-
);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
export async function runBotSetCommand(options: {
|
|
188
|
-
connectorId: string;
|
|
189
|
-
name?: string;
|
|
190
|
-
token?: string;
|
|
191
|
-
}): Promise<void> {
|
|
192
|
-
const configPath = resolveConfigPath();
|
|
193
|
-
const rawConfig = await requireRawConfig(configPath);
|
|
194
|
-
const { normalized, connector } = getDiscordConnectorOrThrow(rawConfig, options.connectorId);
|
|
195
|
-
|
|
196
|
-
const nextName = typeof options.name === "string" ? options.name.trim() : undefined;
|
|
197
|
-
const nextToken = typeof options.token === "string" ? options.token.trim() : undefined;
|
|
198
|
-
if (!nextName && !nextToken) {
|
|
199
|
-
throw new Error("At least one of --name or --token must be provided");
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
normalized.connectors.items[options.connectorId] = {
|
|
203
|
-
...connector,
|
|
204
|
-
...(nextName !== undefined ? { botName: nextName } : {}),
|
|
205
|
-
...(nextToken !== undefined ? { botToken: nextToken } : {}),
|
|
206
|
-
};
|
|
207
|
-
|
|
208
|
-
await saveConfig(configPath, normalized);
|
|
209
|
-
console.log(`Updated bot settings for connector '${options.connectorId}'`);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
export async function runBindingListCommand(options: {
|
|
213
|
-
connectorId?: string;
|
|
214
|
-
json?: boolean;
|
|
215
|
-
}): Promise<void> {
|
|
216
|
-
const configPath = resolveConfigPath();
|
|
217
|
-
const rawConfig = await requireRawConfig(configPath);
|
|
218
|
-
const bindings = listBindings(rawConfig, options.connectorId);
|
|
219
|
-
|
|
220
|
-
if (options.connectorId) {
|
|
221
|
-
const normalized = ensureGatewayConfigShape(rawConfig);
|
|
222
|
-
if (!normalized.connectors.items[options.connectorId]) {
|
|
223
|
-
throw new Error(`Connector '${options.connectorId}' not found`);
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
if (options.json) {
|
|
228
|
-
console.log(JSON.stringify({ configPath, bindings }, null, 2));
|
|
229
|
-
return;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
if (bindings.length === 0) {
|
|
233
|
-
console.log("No bindings configured.");
|
|
234
|
-
return;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
console.log(`Bindings (${configPath}):`);
|
|
238
|
-
for (const binding of bindings) {
|
|
239
|
-
const routeSuffix = binding.routeExists ? "" : " [missing route]";
|
|
240
|
-
const projectSuffix = binding.projectRoot ? ` (${binding.projectRoot})` : "";
|
|
241
|
-
console.log(
|
|
242
|
-
`- ${binding.bindingId}: ${binding.connectorId}/${binding.sourceType}:${binding.sourceId} -> ${binding.routeId}${routeSuffix}${projectSuffix}`,
|
|
243
|
-
);
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
export async function runBindingSetCommand(options: {
|
|
248
|
-
bindingId: string;
|
|
249
|
-
connectorId: string;
|
|
250
|
-
routeId: string;
|
|
251
|
-
sourceType: "channel" | "chat";
|
|
252
|
-
sourceId: string;
|
|
253
|
-
}): Promise<void> {
|
|
254
|
-
const configPath = resolveConfigPath();
|
|
255
|
-
const rawConfig = await requireRawConfig(configPath);
|
|
256
|
-
const normalized = ensureGatewayConfigShape(structuredClone(rawConfig));
|
|
257
|
-
|
|
258
|
-
if (!normalized.connectors.items[options.connectorId]) {
|
|
259
|
-
throw new Error(`Connector '${options.connectorId}' does not exist`);
|
|
260
|
-
}
|
|
261
|
-
if (!normalized.routes.items[options.routeId]) {
|
|
262
|
-
throw new Error(`Route '${options.routeId}' does not exist`);
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
const duplicate = Object.entries(normalized.bindings.items).find(([bindingId, binding]) =>
|
|
266
|
-
bindingId !== options.bindingId
|
|
267
|
-
&& binding.connector === options.connectorId
|
|
268
|
-
&& binding.source.type === options.sourceType
|
|
269
|
-
&& binding.source.id === options.sourceId
|
|
270
|
-
);
|
|
271
|
-
if (duplicate) {
|
|
272
|
-
throw new Error(
|
|
273
|
-
`Binding source '${options.connectorId}/${options.sourceType}:${options.sourceId}' is already used by '${duplicate[0]}'`,
|
|
274
|
-
);
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
const binding: RawBindingConfig = {
|
|
278
|
-
connector: options.connectorId,
|
|
279
|
-
source: {
|
|
280
|
-
type: options.sourceType,
|
|
281
|
-
id: options.sourceId,
|
|
282
|
-
},
|
|
283
|
-
route: options.routeId,
|
|
284
|
-
};
|
|
285
|
-
upsertBinding(normalized, options.bindingId, binding);
|
|
286
|
-
|
|
287
|
-
await saveConfig(configPath, normalized);
|
|
288
|
-
console.log(`Upserted binding '${options.bindingId}'`);
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
export async function runBindingRemoveCommand(options: {
|
|
292
|
-
bindingId: string;
|
|
293
|
-
}): Promise<void> {
|
|
294
|
-
const configPath = resolveConfigPath();
|
|
295
|
-
const rawConfig = await requireRawConfig(configPath);
|
|
296
|
-
const normalized = ensureGatewayConfigShape(structuredClone(rawConfig));
|
|
297
|
-
|
|
298
|
-
if (!normalized.bindings.items[options.bindingId]) {
|
|
299
|
-
throw new Error(`Binding '${options.bindingId}' not found`);
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
delete normalized.bindings.items[options.bindingId];
|
|
303
|
-
await saveConfig(configPath, normalized);
|
|
304
|
-
console.log(`Removed binding '${options.bindingId}'`);
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
export async function runRouteListCommand(options: {
|
|
308
|
-
json?: boolean;
|
|
309
|
-
}): Promise<void> {
|
|
310
|
-
const configPath = resolveConfigPath();
|
|
311
|
-
const rawConfig = await requireRawConfig(configPath);
|
|
312
|
-
const routes = listRoutes(rawConfig);
|
|
313
|
-
|
|
314
|
-
if (options.json) {
|
|
315
|
-
console.log(JSON.stringify({ configPath, routes }, null, 2));
|
|
316
|
-
return;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
if (routes.length === 0) {
|
|
320
|
-
console.log("No routes configured.");
|
|
321
|
-
return;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
console.log(`Routes (${configPath}):`);
|
|
325
|
-
for (const route of routes) {
|
|
326
|
-
const providerInfo = route.provider ? route.provider : "(route default)";
|
|
327
|
-
const sandboxInfo = route.sandbox ? route.sandbox : BUILTIN_HOST_SANDBOX_ID;
|
|
328
|
-
console.log(
|
|
329
|
-
`- ${route.routeId}: ${route.projectRoot}, tools=${route.tools}, mentions=${route.mentions}, provider=${providerInfo}, sandbox=${sandboxInfo}, bindings=${route.bindings}`,
|
|
330
|
-
);
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
export async function runRouteSetCommand(options: {
|
|
335
|
-
routeId: string;
|
|
336
|
-
projectRoot?: string;
|
|
337
|
-
tools?: string;
|
|
338
|
-
providerId?: string;
|
|
339
|
-
sandboxId?: string;
|
|
340
|
-
mentions?: "required" | "optional";
|
|
341
|
-
}): Promise<void> {
|
|
342
|
-
const configPath = resolveConfigPath();
|
|
343
|
-
const rawConfig = await requireRawConfig(configPath);
|
|
344
|
-
const normalized = ensureGatewayConfigShape(structuredClone(rawConfig));
|
|
345
|
-
const existing = normalized.routes.items[options.routeId];
|
|
346
|
-
|
|
347
|
-
const projectRoot = options.projectRoot?.trim() || existing?.projectRoot;
|
|
348
|
-
if (!projectRoot && !normalized.routes.default.projectRoot) {
|
|
349
|
-
throw new Error("--project-root is required when creating a new route");
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
const toolsRaw = options.tools ?? existing?.tools;
|
|
353
|
-
if (toolsRaw !== undefined && toolsRaw !== "full" && toolsRaw !== "readonly") {
|
|
354
|
-
throw new Error(`Invalid --tools '${toolsRaw}'. Allowed: full, readonly`);
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
const provider = options.providerId ?? existing?.provider;
|
|
358
|
-
if (provider && !normalized.providers.items[provider]) {
|
|
359
|
-
throw new Error(`Provider '${provider}' does not exist`);
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
const sandbox = options.sandboxId ?? existing?.sandbox;
|
|
363
|
-
if (sandbox && sandbox !== BUILTIN_HOST_SANDBOX_ID && !normalized.sandboxes.items[sandbox]) {
|
|
364
|
-
throw new Error(`Sandbox '${sandbox}' does not exist`);
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
upsertRoute(normalized, options.routeId, {
|
|
368
|
-
...(projectRoot ? { projectRoot } : {}),
|
|
369
|
-
...(toolsRaw ? { tools: toolsRaw } : {}),
|
|
370
|
-
...((options.mentions ?? existing?.mentions) ? { mentions: (options.mentions ?? existing?.mentions)! } : {}),
|
|
371
|
-
...(provider ? { provider } : {}),
|
|
372
|
-
...(sandbox ? { sandbox } : {}),
|
|
373
|
-
...(typeof existing?.systemPromptFile === "string" ? { systemPromptFile: existing.systemPromptFile } : {}),
|
|
374
|
-
});
|
|
375
|
-
|
|
376
|
-
await saveConfig(configPath, normalized);
|
|
377
|
-
console.log(`Upserted route '${options.routeId}'`);
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
export async function runRouteRemoveCommand(options: {
|
|
381
|
-
routeId: string;
|
|
382
|
-
cascadeBindings?: boolean;
|
|
383
|
-
}): Promise<void> {
|
|
384
|
-
const configPath = resolveConfigPath();
|
|
385
|
-
const rawConfig = await requireRawConfig(configPath);
|
|
386
|
-
const normalized = ensureGatewayConfigShape(structuredClone(rawConfig));
|
|
387
|
-
|
|
388
|
-
if (!normalized.routes.items[options.routeId]) {
|
|
389
|
-
throw new Error(`Route '${options.routeId}' not found`);
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
const bindingRefs = listBindings(normalized).filter(
|
|
393
|
-
(binding) => binding.routeId === options.routeId && binding.bindingId !== "bindings.default",
|
|
394
|
-
);
|
|
395
|
-
const hasDefaultBindingRef = normalized.bindings.default?.route === options.routeId;
|
|
396
|
-
if ((bindingRefs.length > 0 || hasDefaultBindingRef) && !options.cascadeBindings) {
|
|
397
|
-
const refList = bindingRefs.map((binding) => binding.bindingId).join(", ");
|
|
398
|
-
throw new Error(
|
|
399
|
-
`Route '${options.routeId}' is referenced by bindings (${[refList, hasDefaultBindingRef ? "bindings.default" : ""].filter(Boolean).join(", ")}). Re-run with --cascade-bindings to remove these bindings automatically.`,
|
|
400
|
-
);
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
if (options.cascadeBindings) {
|
|
404
|
-
for (const binding of bindingRefs) {
|
|
405
|
-
delete normalized.bindings.items[binding.bindingId];
|
|
406
|
-
}
|
|
407
|
-
if (hasDefaultBindingRef) {
|
|
408
|
-
delete normalized.bindings.default;
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
delete normalized.routes.items[options.routeId];
|
|
413
|
-
await saveConfig(configPath, normalized);
|
|
414
|
-
console.log(`Removed route '${options.routeId}'`);
|
|
415
|
-
}
|
package/src/cli/index.ts
DELETED