@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,128 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import { access, mkdtemp, readFile } from "node:fs/promises";
|
|
3
|
-
import { tmpdir } from "node:os";
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
import test from "node:test";
|
|
6
|
-
import pino from "pino";
|
|
7
|
-
import { mapDiscordMessage } from "../../../plugins/connector-discord/src/mapper.js";
|
|
8
|
-
|
|
9
|
-
function createMessage(overrides?: {
|
|
10
|
-
id?: string;
|
|
11
|
-
content?: string;
|
|
12
|
-
attachments?: Map<string, unknown>;
|
|
13
|
-
}): unknown {
|
|
14
|
-
return {
|
|
15
|
-
id: overrides?.id ?? "msg-1",
|
|
16
|
-
content: overrides?.content ?? "hello",
|
|
17
|
-
author: {
|
|
18
|
-
id: "user-1",
|
|
19
|
-
username: "alice",
|
|
20
|
-
bot: false,
|
|
21
|
-
},
|
|
22
|
-
attachments: overrides?.attachments ?? new Map(),
|
|
23
|
-
mentions: {
|
|
24
|
-
users: {
|
|
25
|
-
has: () => false,
|
|
26
|
-
},
|
|
27
|
-
},
|
|
28
|
-
guildId: "guild-1",
|
|
29
|
-
channelId: "channel-1",
|
|
30
|
-
channel: {
|
|
31
|
-
isThread: () => false,
|
|
32
|
-
},
|
|
33
|
-
createdTimestamp: 1_700_000_000_000,
|
|
34
|
-
toJSON: () => ({ ok: true }),
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
async function pathExists(path: string): Promise<boolean> {
|
|
39
|
-
try {
|
|
40
|
-
await access(path);
|
|
41
|
-
return true;
|
|
42
|
-
} catch {
|
|
43
|
-
return false;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const logger = pino({ enabled: false });
|
|
48
|
-
|
|
49
|
-
test("mapDiscordMessage does not create attachment directory when message has no attachments", async () => {
|
|
50
|
-
const root = await mkdtemp(join(tmpdir(), "dobby-discord-mapper-empty-"));
|
|
51
|
-
const message = createMessage();
|
|
52
|
-
|
|
53
|
-
const envelope = await mapDiscordMessage(
|
|
54
|
-
message as never,
|
|
55
|
-
"discord.main",
|
|
56
|
-
"bot-1",
|
|
57
|
-
"source-1",
|
|
58
|
-
root,
|
|
59
|
-
logger,
|
|
60
|
-
);
|
|
61
|
-
|
|
62
|
-
assert.ok(envelope);
|
|
63
|
-
assert.deepEqual(envelope.attachments, []);
|
|
64
|
-
assert.equal(await pathExists(join(root, "source-1", "msg-1")), false);
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
test("mapDiscordMessage only creates attachment directory when a download succeeds", async (t) => {
|
|
68
|
-
t.mock.method(globalThis, "fetch", async () => new Response("file-body", { status: 200 }));
|
|
69
|
-
|
|
70
|
-
const root = await mkdtemp(join(tmpdir(), "dobby-discord-mapper-file-"));
|
|
71
|
-
const message = createMessage({
|
|
72
|
-
attachments: new Map([
|
|
73
|
-
["att-1", {
|
|
74
|
-
id: "att-1",
|
|
75
|
-
name: "hello.png",
|
|
76
|
-
contentType: "image/png",
|
|
77
|
-
size: 9,
|
|
78
|
-
url: "https://example.test/hello.png",
|
|
79
|
-
}],
|
|
80
|
-
]),
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
const envelope = await mapDiscordMessage(
|
|
84
|
-
message as never,
|
|
85
|
-
"discord.main",
|
|
86
|
-
"bot-1",
|
|
87
|
-
"source-1",
|
|
88
|
-
root,
|
|
89
|
-
logger,
|
|
90
|
-
);
|
|
91
|
-
|
|
92
|
-
assert.ok(envelope);
|
|
93
|
-
assert.equal(envelope.attachments.length, 1);
|
|
94
|
-
assert.equal(await pathExists(join(root, "source-1", "msg-1")), true);
|
|
95
|
-
assert.equal(envelope.attachments[0]?.localPath, join(root, "source-1", "msg-1", "hello.png"));
|
|
96
|
-
assert.equal(await readFile(join(root, "source-1", "msg-1", "hello.png"), "utf-8"), "file-body");
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
test("mapDiscordMessage does not leave an empty attachment directory when download fails", async (t) => {
|
|
100
|
-
t.mock.method(globalThis, "fetch", async () => new Response("nope", { status: 500 }));
|
|
101
|
-
|
|
102
|
-
const root = await mkdtemp(join(tmpdir(), "dobby-discord-mapper-fail-"));
|
|
103
|
-
const message = createMessage({
|
|
104
|
-
attachments: new Map([
|
|
105
|
-
["att-1", {
|
|
106
|
-
id: "att-1",
|
|
107
|
-
name: "broken.png",
|
|
108
|
-
contentType: "image/png",
|
|
109
|
-
size: 9,
|
|
110
|
-
url: "https://example.test/broken.png",
|
|
111
|
-
}],
|
|
112
|
-
]),
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
const envelope = await mapDiscordMessage(
|
|
116
|
-
message as never,
|
|
117
|
-
"discord.main",
|
|
118
|
-
"bot-1",
|
|
119
|
-
"source-1",
|
|
120
|
-
root,
|
|
121
|
-
logger,
|
|
122
|
-
);
|
|
123
|
-
|
|
124
|
-
assert.ok(envelope);
|
|
125
|
-
assert.equal(envelope.attachments.length, 1);
|
|
126
|
-
assert.equal(envelope.attachments[0]?.localPath, undefined);
|
|
127
|
-
assert.equal(await pathExists(join(root, "source-1", "msg-1")), false);
|
|
128
|
-
});
|
|
@@ -1,269 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import { spawn } from "node:child_process";
|
|
3
|
-
import { mkdtemp, mkdir, rm, writeFile } from "node:fs/promises";
|
|
4
|
-
import { tmpdir } from "node:os";
|
|
5
|
-
import { join } from "node:path";
|
|
6
|
-
import test from "node:test";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Writes a temporary default config under HOME/.dobby/gateway.json.
|
|
10
|
-
*/
|
|
11
|
-
async function writeTempHomeConfig(homeDir: string, payload: unknown): Promise<string> {
|
|
12
|
-
const dobbyDir = join(homeDir, ".dobby");
|
|
13
|
-
await mkdir(dobbyDir, { recursive: true });
|
|
14
|
-
const configPath = join(dobbyDir, "gateway.json");
|
|
15
|
-
await writeFile(configPath, `${JSON.stringify(payload, null, 2)}\n`, "utf-8");
|
|
16
|
-
return configPath;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Runs `dobby doctor` in a child process with an isolated HOME directory.
|
|
21
|
-
*/
|
|
22
|
-
async function runDoctorWithHome(homeDir: string, configPath: string): Promise<{ code: number | null; output: string }> {
|
|
23
|
-
return await new Promise((resolve, reject) => {
|
|
24
|
-
const child = spawn(
|
|
25
|
-
process.execPath,
|
|
26
|
-
["--import", "tsx", "src/main.ts", "doctor"],
|
|
27
|
-
{
|
|
28
|
-
cwd: process.cwd(),
|
|
29
|
-
env: {
|
|
30
|
-
...process.env,
|
|
31
|
-
HOME: homeDir,
|
|
32
|
-
DOBBY_CONFIG_PATH: configPath,
|
|
33
|
-
},
|
|
34
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
35
|
-
},
|
|
36
|
-
);
|
|
37
|
-
|
|
38
|
-
let output = "";
|
|
39
|
-
child.stdout.on("data", (chunk) => {
|
|
40
|
-
output += String(chunk);
|
|
41
|
-
});
|
|
42
|
-
child.stderr.on("data", (chunk) => {
|
|
43
|
-
output += String(chunk);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
child.once("error", (error) => reject(error));
|
|
47
|
-
child.once("close", (code) => {
|
|
48
|
-
resolve({ code, output });
|
|
49
|
-
});
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
test("doctor reports invalid binding route references", async () => {
|
|
54
|
-
const homeDir = await mkdtemp(join(tmpdir(), "dobby-doctor-home-"));
|
|
55
|
-
|
|
56
|
-
try {
|
|
57
|
-
const configPath = await writeTempHomeConfig(homeDir, {
|
|
58
|
-
extensions: { allowList: [] },
|
|
59
|
-
providers: {
|
|
60
|
-
default: "pi.main",
|
|
61
|
-
items: {
|
|
62
|
-
"pi.main": {
|
|
63
|
-
type: "provider.pi",
|
|
64
|
-
},
|
|
65
|
-
},
|
|
66
|
-
},
|
|
67
|
-
connectors: {
|
|
68
|
-
items: {
|
|
69
|
-
"discord.main": {
|
|
70
|
-
type: "connector.discord",
|
|
71
|
-
botName: "dobby-main",
|
|
72
|
-
botToken: "token",
|
|
73
|
-
},
|
|
74
|
-
},
|
|
75
|
-
},
|
|
76
|
-
sandboxes: {
|
|
77
|
-
default: "host.builtin",
|
|
78
|
-
items: {},
|
|
79
|
-
},
|
|
80
|
-
routes: {
|
|
81
|
-
defaults: {
|
|
82
|
-
provider: "pi.main",
|
|
83
|
-
sandbox: "host.builtin",
|
|
84
|
-
tools: "full",
|
|
85
|
-
mentions: "required",
|
|
86
|
-
},
|
|
87
|
-
items: {
|
|
88
|
-
main: {
|
|
89
|
-
projectRoot: process.cwd(),
|
|
90
|
-
},
|
|
91
|
-
},
|
|
92
|
-
},
|
|
93
|
-
bindings: {
|
|
94
|
-
items: {
|
|
95
|
-
"discord.main.123": {
|
|
96
|
-
connector: "discord.main",
|
|
97
|
-
source: {
|
|
98
|
-
type: "channel",
|
|
99
|
-
id: "123",
|
|
100
|
-
},
|
|
101
|
-
route: "missing-route",
|
|
102
|
-
},
|
|
103
|
-
},
|
|
104
|
-
},
|
|
105
|
-
data: {
|
|
106
|
-
rootDir: "./data",
|
|
107
|
-
dedupTtlMs: 604800000,
|
|
108
|
-
},
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
const result = await runDoctorWithHome(homeDir, configPath);
|
|
112
|
-
assert.equal(result.code, 1);
|
|
113
|
-
assert.equal(
|
|
114
|
-
result.output.includes("bindings.items['discord.main.123'].route") && result.output.includes("missing-route"),
|
|
115
|
-
true,
|
|
116
|
-
);
|
|
117
|
-
} finally {
|
|
118
|
-
await rm(homeDir, { recursive: true, force: true });
|
|
119
|
-
}
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
test("doctor reports invalid default binding route references", async () => {
|
|
123
|
-
const homeDir = await mkdtemp(join(tmpdir(), "dobby-doctor-default-binding-"));
|
|
124
|
-
|
|
125
|
-
try {
|
|
126
|
-
const configPath = await writeTempHomeConfig(homeDir, {
|
|
127
|
-
extensions: { allowList: [] },
|
|
128
|
-
providers: {
|
|
129
|
-
default: "pi.main",
|
|
130
|
-
items: {
|
|
131
|
-
"pi.main": {
|
|
132
|
-
type: "provider.pi",
|
|
133
|
-
},
|
|
134
|
-
},
|
|
135
|
-
},
|
|
136
|
-
connectors: {
|
|
137
|
-
items: {
|
|
138
|
-
"discord.main": {
|
|
139
|
-
type: "connector.discord",
|
|
140
|
-
botName: "dobby-main",
|
|
141
|
-
botToken: "token",
|
|
142
|
-
},
|
|
143
|
-
},
|
|
144
|
-
},
|
|
145
|
-
sandboxes: {
|
|
146
|
-
default: "host.builtin",
|
|
147
|
-
items: {},
|
|
148
|
-
},
|
|
149
|
-
routes: {
|
|
150
|
-
defaults: {
|
|
151
|
-
projectRoot: process.cwd(),
|
|
152
|
-
provider: "pi.main",
|
|
153
|
-
sandbox: "host.builtin",
|
|
154
|
-
tools: "full",
|
|
155
|
-
mentions: "required",
|
|
156
|
-
},
|
|
157
|
-
items: {
|
|
158
|
-
main: {},
|
|
159
|
-
},
|
|
160
|
-
},
|
|
161
|
-
bindings: {
|
|
162
|
-
default: {
|
|
163
|
-
route: "missing-route",
|
|
164
|
-
},
|
|
165
|
-
items: {},
|
|
166
|
-
},
|
|
167
|
-
data: {
|
|
168
|
-
rootDir: "./data",
|
|
169
|
-
dedupTtlMs: 604800000,
|
|
170
|
-
},
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
const result = await runDoctorWithHome(homeDir, configPath);
|
|
174
|
-
assert.equal(result.code, 1);
|
|
175
|
-
assert.equal(result.output.includes("bindings.default.route") && result.output.includes("missing-route"), true);
|
|
176
|
-
} finally {
|
|
177
|
-
await rm(homeDir, { recursive: true, force: true });
|
|
178
|
-
}
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
test("doctor reports init template placeholders as errors and warnings", async () => {
|
|
182
|
-
const homeDir = await mkdtemp(join(tmpdir(), "dobby-doctor-placeholders-"));
|
|
183
|
-
|
|
184
|
-
try {
|
|
185
|
-
const configPath = await writeTempHomeConfig(homeDir, {
|
|
186
|
-
extensions: { allowList: [] },
|
|
187
|
-
providers: {
|
|
188
|
-
default: "pi.main",
|
|
189
|
-
items: {
|
|
190
|
-
"pi.main": {
|
|
191
|
-
type: "provider.pi",
|
|
192
|
-
model: "REPLACE_WITH_PROVIDER_MODEL_ID",
|
|
193
|
-
baseUrl: "REPLACE_WITH_PROVIDER_BASE_URL",
|
|
194
|
-
apiKey: "REPLACE_WITH_PROVIDER_API_KEY_OR_ENV",
|
|
195
|
-
},
|
|
196
|
-
},
|
|
197
|
-
},
|
|
198
|
-
connectors: {
|
|
199
|
-
items: {
|
|
200
|
-
"discord.main": {
|
|
201
|
-
type: "connector.discord",
|
|
202
|
-
botName: "dobby-main",
|
|
203
|
-
botToken: "REPLACE_WITH_DISCORD_BOT_TOKEN",
|
|
204
|
-
},
|
|
205
|
-
"feishu.main": {
|
|
206
|
-
type: "connector.feishu",
|
|
207
|
-
appId: "REPLACE_WITH_FEISHU_APP_ID",
|
|
208
|
-
appSecret: "REPLACE_WITH_FEISHU_APP_SECRET",
|
|
209
|
-
},
|
|
210
|
-
},
|
|
211
|
-
},
|
|
212
|
-
sandboxes: {
|
|
213
|
-
default: "host.builtin",
|
|
214
|
-
items: {},
|
|
215
|
-
},
|
|
216
|
-
routes: {
|
|
217
|
-
defaults: {
|
|
218
|
-
provider: "pi.main",
|
|
219
|
-
sandbox: "host.builtin",
|
|
220
|
-
tools: "full",
|
|
221
|
-
mentions: "required",
|
|
222
|
-
},
|
|
223
|
-
items: {
|
|
224
|
-
main: {
|
|
225
|
-
projectRoot: "./REPLACE_WITH_PROJECT_ROOT",
|
|
226
|
-
},
|
|
227
|
-
},
|
|
228
|
-
},
|
|
229
|
-
bindings: {
|
|
230
|
-
items: {
|
|
231
|
-
"discord.main.main": {
|
|
232
|
-
connector: "discord.main",
|
|
233
|
-
source: {
|
|
234
|
-
type: "channel",
|
|
235
|
-
id: "YOUR_DISCORD_CHANNEL_ID",
|
|
236
|
-
},
|
|
237
|
-
route: "main",
|
|
238
|
-
},
|
|
239
|
-
"feishu.main.main": {
|
|
240
|
-
connector: "feishu.main",
|
|
241
|
-
source: {
|
|
242
|
-
type: "chat",
|
|
243
|
-
id: "YOUR_FEISHU_CHAT_ID",
|
|
244
|
-
},
|
|
245
|
-
route: "main",
|
|
246
|
-
},
|
|
247
|
-
},
|
|
248
|
-
},
|
|
249
|
-
data: {
|
|
250
|
-
rootDir: "./data",
|
|
251
|
-
dedupTtlMs: 604800000,
|
|
252
|
-
},
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
const result = await runDoctorWithHome(homeDir, configPath);
|
|
256
|
-
assert.equal(result.code, 1);
|
|
257
|
-
assert.equal(result.output.includes("providers.items['pi.main'].model still uses placeholder value"), true);
|
|
258
|
-
assert.equal(result.output.includes("providers.items['pi.main'].baseUrl still uses placeholder value"), true);
|
|
259
|
-
assert.equal(result.output.includes("providers.items['pi.main'].apiKey still uses placeholder value"), true);
|
|
260
|
-
assert.equal(result.output.includes("connectors.items['discord.main'].botToken still uses placeholder value"), true);
|
|
261
|
-
assert.equal(result.output.includes("connectors.items['feishu.main'].appId still uses placeholder value"), true);
|
|
262
|
-
assert.equal(result.output.includes("connectors.items['feishu.main'].appSecret still uses placeholder value"), true);
|
|
263
|
-
assert.equal(result.output.includes("routes.items['main'].projectRoot still uses placeholder value"), true);
|
|
264
|
-
assert.equal(result.output.includes("bindings.items['discord.main.main'].source.id still uses placeholder value"), true);
|
|
265
|
-
assert.equal(result.output.includes("bindings.items['feishu.main.main'].source.id still uses placeholder value"), true);
|
|
266
|
-
} finally {
|
|
267
|
-
await rm(homeDir, { recursive: true, force: true });
|
|
268
|
-
}
|
|
269
|
-
});
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import test from "node:test";
|
|
3
|
-
import { createInitSelectionConfig } from "../shared/init-catalog.js";
|
|
4
|
-
|
|
5
|
-
test("createInitSelectionConfig writes Discord starter template for provider.pi", () => {
|
|
6
|
-
const selected = createInitSelectionConfig(["provider.pi"], ["connector.discord"], {
|
|
7
|
-
routeProviderChoiceId: "provider.pi",
|
|
8
|
-
defaultProjectRoot: "/Users/oolong/workspace/dobby",
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
assert.deepEqual(selected.providerChoiceIds, ["provider.pi"]);
|
|
12
|
-
assert.deepEqual(selected.connectorChoiceIds, ["connector.discord"]);
|
|
13
|
-
assert.equal(selected.routeId, "main");
|
|
14
|
-
assert.equal(selected.providerInstanceId, "pi.main");
|
|
15
|
-
assert.deepEqual(selected.providerInstances, [{
|
|
16
|
-
choiceId: "provider.pi",
|
|
17
|
-
instanceId: "pi.main",
|
|
18
|
-
contributionId: "provider.pi",
|
|
19
|
-
config: {
|
|
20
|
-
model: "REPLACE_WITH_PROVIDER_MODEL_ID",
|
|
21
|
-
baseUrl: "REPLACE_WITH_PROVIDER_BASE_URL",
|
|
22
|
-
apiKey: "REPLACE_WITH_PROVIDER_API_KEY_OR_ENV",
|
|
23
|
-
},
|
|
24
|
-
}]);
|
|
25
|
-
assert.deepEqual(selected.connectorInstances, [{
|
|
26
|
-
choiceId: "connector.discord",
|
|
27
|
-
instanceId: "discord.main",
|
|
28
|
-
contributionId: "connector.discord",
|
|
29
|
-
config: {
|
|
30
|
-
botName: "dobby-main",
|
|
31
|
-
botToken: "REPLACE_WITH_DISCORD_BOT_TOKEN",
|
|
32
|
-
reconnectStaleMs: 60_000,
|
|
33
|
-
reconnectCheckIntervalMs: 10_000,
|
|
34
|
-
},
|
|
35
|
-
}]);
|
|
36
|
-
assert.deepEqual(selected.routeDefaults, {
|
|
37
|
-
projectRoot: "/Users/oolong/workspace/dobby",
|
|
38
|
-
tools: "full",
|
|
39
|
-
mentions: "required",
|
|
40
|
-
provider: "pi.main",
|
|
41
|
-
sandbox: "host.builtin",
|
|
42
|
-
});
|
|
43
|
-
assert.deepEqual(selected.routeProfile, {});
|
|
44
|
-
assert.deepEqual(selected.defaultBinding, {
|
|
45
|
-
route: "main",
|
|
46
|
-
});
|
|
47
|
-
assert.deepEqual(selected.bindings, [{
|
|
48
|
-
id: "discord.main.main",
|
|
49
|
-
config: {
|
|
50
|
-
connector: "discord.main",
|
|
51
|
-
source: {
|
|
52
|
-
type: "channel",
|
|
53
|
-
id: "YOUR_DISCORD_CHANNEL_ID",
|
|
54
|
-
},
|
|
55
|
-
route: "main",
|
|
56
|
-
},
|
|
57
|
-
}]);
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
test("createInitSelectionConfig writes Feishu starter template for provider.claude-cli", () => {
|
|
61
|
-
const selected = createInitSelectionConfig(["provider.claude-cli"], ["connector.feishu"], {
|
|
62
|
-
routeProviderChoiceId: "provider.claude-cli",
|
|
63
|
-
defaultProjectRoot: "/Users/oolong/workspace/dobby",
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
assert.deepEqual(selected.providerChoiceIds, ["provider.claude-cli"]);
|
|
67
|
-
assert.deepEqual(selected.connectorChoiceIds, ["connector.feishu"]);
|
|
68
|
-
assert.equal(selected.routeId, "main");
|
|
69
|
-
assert.equal(selected.providerInstanceId, "claude-cli.main");
|
|
70
|
-
assert.deepEqual(selected.connectorInstances, [{
|
|
71
|
-
choiceId: "connector.feishu",
|
|
72
|
-
instanceId: "feishu.main",
|
|
73
|
-
contributionId: "connector.feishu",
|
|
74
|
-
config: {
|
|
75
|
-
appId: "REPLACE_WITH_FEISHU_APP_ID",
|
|
76
|
-
appSecret: "REPLACE_WITH_FEISHU_APP_SECRET",
|
|
77
|
-
domain: "feishu",
|
|
78
|
-
messageFormat: "card_markdown",
|
|
79
|
-
replyMode: "direct",
|
|
80
|
-
downloadAttachments: true,
|
|
81
|
-
},
|
|
82
|
-
}]);
|
|
83
|
-
assert.deepEqual(selected.bindings, [{
|
|
84
|
-
id: "feishu.main.main",
|
|
85
|
-
config: {
|
|
86
|
-
connector: "feishu.main",
|
|
87
|
-
source: {
|
|
88
|
-
type: "chat",
|
|
89
|
-
id: "YOUR_FEISHU_CHAT_ID",
|
|
90
|
-
},
|
|
91
|
-
route: "main",
|
|
92
|
-
},
|
|
93
|
-
}]);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
test("createInitSelectionConfig supports multiple providers and connectors with one default provider", () => {
|
|
97
|
-
const selected = createInitSelectionConfig(
|
|
98
|
-
["provider.pi", "provider.claude-cli"],
|
|
99
|
-
["connector.discord", "connector.feishu"],
|
|
100
|
-
{
|
|
101
|
-
routeProviderChoiceId: "provider.claude-cli",
|
|
102
|
-
defaultProjectRoot: "/Users/oolong/workspace/dobby",
|
|
103
|
-
},
|
|
104
|
-
);
|
|
105
|
-
|
|
106
|
-
assert.deepEqual(selected.providerChoiceIds, ["provider.pi", "provider.claude-cli"]);
|
|
107
|
-
assert.deepEqual(selected.connectorChoiceIds, ["connector.discord", "connector.feishu"]);
|
|
108
|
-
assert.deepEqual(selected.extensionPackages, [
|
|
109
|
-
"@dobby.ai/provider-pi",
|
|
110
|
-
"@dobby.ai/provider-claude-cli",
|
|
111
|
-
"@dobby.ai/connector-discord",
|
|
112
|
-
"@dobby.ai/connector-feishu",
|
|
113
|
-
]);
|
|
114
|
-
assert.equal(selected.providerInstanceId, "claude-cli.main");
|
|
115
|
-
assert.equal(selected.routeDefaults.provider, "claude-cli.main");
|
|
116
|
-
assert.equal(selected.routeProviderChoiceId, "provider.claude-cli");
|
|
117
|
-
assert.deepEqual(selected.defaultBinding, {
|
|
118
|
-
route: "main",
|
|
119
|
-
});
|
|
120
|
-
assert.deepEqual(selected.bindings, [
|
|
121
|
-
{
|
|
122
|
-
id: "discord.main.main",
|
|
123
|
-
config: {
|
|
124
|
-
connector: "discord.main",
|
|
125
|
-
source: {
|
|
126
|
-
type: "channel",
|
|
127
|
-
id: "YOUR_DISCORD_CHANNEL_ID",
|
|
128
|
-
},
|
|
129
|
-
route: "main",
|
|
130
|
-
},
|
|
131
|
-
},
|
|
132
|
-
{
|
|
133
|
-
id: "feishu.main.main",
|
|
134
|
-
config: {
|
|
135
|
-
connector: "feishu.main",
|
|
136
|
-
source: {
|
|
137
|
-
type: "chat",
|
|
138
|
-
id: "YOUR_FEISHU_CHAT_ID",
|
|
139
|
-
},
|
|
140
|
-
route: "main",
|
|
141
|
-
},
|
|
142
|
-
},
|
|
143
|
-
]);
|
|
144
|
-
});
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import test from "node:test";
|
|
3
|
-
import { CommanderError } from "commander";
|
|
4
|
-
import { buildProgram } from "../program.js";
|
|
5
|
-
|
|
6
|
-
test("CLI rejects --config option", async () => {
|
|
7
|
-
const program = buildProgram();
|
|
8
|
-
program.configureOutput({
|
|
9
|
-
writeErr: () => {},
|
|
10
|
-
writeOut: () => {},
|
|
11
|
-
});
|
|
12
|
-
program.exitOverride();
|
|
13
|
-
|
|
14
|
-
await assert.rejects(
|
|
15
|
-
program.parseAsync(["node", "dobby", "--config", "./config/gateway.json"]),
|
|
16
|
-
(error) => {
|
|
17
|
-
assert.equal(error instanceof CommanderError, true);
|
|
18
|
-
assert.equal((error as CommanderError).code, "commander.unknownOption");
|
|
19
|
-
assert.match(String((error as CommanderError).message), /unknown option '--config'/i);
|
|
20
|
-
return true;
|
|
21
|
-
},
|
|
22
|
-
);
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
test("init help has no merge/overwrite flags", () => {
|
|
26
|
-
const program = buildProgram();
|
|
27
|
-
const initCommand = program.commands.find((command) => command.name() === "init");
|
|
28
|
-
assert.ok(initCommand);
|
|
29
|
-
|
|
30
|
-
const help = initCommand.helpInformation();
|
|
31
|
-
assert.equal(help.includes("--merge"), false);
|
|
32
|
-
assert.equal(help.includes("--merge-strategy"), false);
|
|
33
|
-
assert.equal(help.includes("--overwrite"), false);
|
|
34
|
-
|
|
35
|
-
assert.equal(help.includes("--preset"), false);
|
|
36
|
-
assert.equal(help.includes("--non-interactive"), false);
|
|
37
|
-
assert.equal(help.includes("--yes"), false);
|
|
38
|
-
assert.equal(help.includes("--config"), false);
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
test("config help shows read-only inspect commands and schema", () => {
|
|
42
|
-
const program = buildProgram();
|
|
43
|
-
const configCommand = program.commands.find((command) => command.name() === "config");
|
|
44
|
-
assert.ok(configCommand);
|
|
45
|
-
|
|
46
|
-
const help = configCommand.helpInformation();
|
|
47
|
-
assert.match(help, /show \[options\] \[section\]/);
|
|
48
|
-
assert.match(help, /list \[options\] \[section\]/);
|
|
49
|
-
assert.match(help, /schema/);
|
|
50
|
-
assert.equal(help.includes("edit"), false);
|
|
51
|
-
|
|
52
|
-
assert.equal(help.includes("get"), false);
|
|
53
|
-
assert.equal(help.includes("set"), false);
|
|
54
|
-
assert.equal(help.includes("unset"), false);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
test("config schema help shows list/show subcommands", () => {
|
|
58
|
-
const program = buildProgram();
|
|
59
|
-
const configCommand = program.commands.find((command) => command.name() === "config");
|
|
60
|
-
assert.ok(configCommand);
|
|
61
|
-
|
|
62
|
-
const schemaCommand = configCommand.commands.find((command) => command.name() === "schema");
|
|
63
|
-
assert.ok(schemaCommand);
|
|
64
|
-
|
|
65
|
-
const help = schemaCommand.helpInformation();
|
|
66
|
-
assert.match(help, /list \[options\]/);
|
|
67
|
-
assert.match(help, /show \[options\] <contributionId>/);
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
test("cron help shows core subcommands", () => {
|
|
71
|
-
const program = buildProgram();
|
|
72
|
-
const cronCommand = program.commands.find((command) => command.name() === "cron");
|
|
73
|
-
assert.ok(cronCommand);
|
|
74
|
-
|
|
75
|
-
const help = cronCommand.helpInformation();
|
|
76
|
-
assert.match(help, /add \[options\] <name>/);
|
|
77
|
-
assert.match(help, /list \[options\]/);
|
|
78
|
-
assert.match(help, /run \[options\] <jobId>/);
|
|
79
|
-
assert.match(help, /remove \[options\] <jobId>/);
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
test("top-level help keeps bootstrap, inspect, install, validate, and ops commands only", () => {
|
|
83
|
-
const program = buildProgram();
|
|
84
|
-
const help = program.helpInformation();
|
|
85
|
-
assert.match(help, /start/);
|
|
86
|
-
assert.match(help, /init/);
|
|
87
|
-
assert.match(help, /config/);
|
|
88
|
-
assert.match(help, /extension/);
|
|
89
|
-
assert.match(help, /doctor/);
|
|
90
|
-
assert.match(help, /cron/);
|
|
91
|
-
assert.equal(help.includes("configure"), false);
|
|
92
|
-
assert.equal(help.includes("bot"), false);
|
|
93
|
-
assert.equal(help.includes("binding"), false);
|
|
94
|
-
assert.equal(help.includes("route"), false);
|
|
95
|
-
});
|