@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,43 +0,0 @@
1
- import { resolve } from "node:path";
2
- import { z } from "zod";
3
- import type { SandboxContributionModule } from "@dobby.ai/plugin-sdk";
4
- import { DockerExecutor } from "./docker-executor.js";
5
-
6
- const dockerSandboxConfigSchema = z.object({
7
- container: z.string().min(1),
8
- hostWorkspaceRoot: z.string().min(1),
9
- containerWorkspaceRoot: z.string().min(1).default("/workspace"),
10
- });
11
-
12
- function resolveMaybeAbsolute(baseDir: string, value: string): string {
13
- if (value === "~") {
14
- return resolve(process.env.HOME ?? "", ".");
15
- }
16
- if (value.startsWith("~/") || value.startsWith("~\\")) {
17
- return resolve(process.env.HOME ?? "", value.slice(2));
18
- }
19
- return resolve(baseDir, value);
20
- }
21
-
22
- export const sandboxDockerContribution: SandboxContributionModule = {
23
- kind: "sandbox",
24
- configSchema: z.toJSONSchema(dockerSandboxConfigSchema),
25
- async createInstance(options) {
26
- const parsed = dockerSandboxConfigSchema.parse(options.config);
27
- const executor = await DockerExecutor.create(
28
- {
29
- container: parsed.container,
30
- hostWorkspaceRoot: resolveMaybeAbsolute(options.host.configBaseDir, parsed.hostWorkspaceRoot),
31
- containerWorkspaceRoot: parsed.containerWorkspaceRoot,
32
- },
33
- options.host.logger,
34
- );
35
-
36
- return {
37
- id: options.instanceId,
38
- executor,
39
- };
40
- },
41
- };
42
-
43
- export default sandboxDockerContribution;
@@ -1,217 +0,0 @@
1
- import { spawn } from "node:child_process";
2
- import { resolve, sep } from "node:path";
3
- import type { GatewayLogger } from "@dobby.ai/plugin-sdk";
4
- import type { ExecOptions, ExecResult, Executor, SpawnOptions, SpawnedProcess } from "@dobby.ai/plugin-sdk";
5
-
6
- export interface DockerConfig {
7
- container: string;
8
- hostWorkspaceRoot: string;
9
- containerWorkspaceRoot: string;
10
- }
11
-
12
- function shellEscape(value: string): string {
13
- return `'${value.replace(/'/g, "'\\''")}'`;
14
- }
15
-
16
- function normalizePrefix(path: string): string {
17
- return path.endsWith(sep) ? path : `${path}${sep}`;
18
- }
19
-
20
- export class DockerExecutor implements Executor {
21
- private readonly normalizedHostRoot: string;
22
-
23
- private constructor(
24
- private readonly config: DockerConfig,
25
- private readonly logger: GatewayLogger,
26
- ) {
27
- this.normalizedHostRoot = normalizePrefix(resolve(config.hostWorkspaceRoot));
28
- }
29
-
30
- static async create(config: DockerConfig, logger: GatewayLogger): Promise<DockerExecutor> {
31
- const instance = new DockerExecutor(config, logger);
32
- await instance.validate();
33
- return instance;
34
- }
35
-
36
- async exec(command: string, cwd: string, options: ExecOptions = {}): Promise<ExecResult> {
37
- const containerCwd = this.toContainerPath(cwd);
38
- const wrapped = `cd ${shellEscape(containerCwd)} && ${command}`;
39
-
40
- const args = ["exec", "-i"];
41
- if (options.env) {
42
- for (const [key, value] of Object.entries(options.env)) {
43
- if (value !== undefined) {
44
- args.push("-e", `${key}=${value}`);
45
- }
46
- }
47
- }
48
- args.push(this.config.container, "sh", "-lc", wrapped);
49
-
50
- return new Promise<ExecResult>((resolveResult, reject) => {
51
- const child = spawn("docker", args, {
52
- stdio: ["ignore", "pipe", "pipe"],
53
- });
54
-
55
- let stdout = "";
56
- let stderr = "";
57
- let killed = false;
58
-
59
- const onAbort = () => {
60
- killed = true;
61
- child.kill("SIGKILL");
62
- };
63
-
64
- let timeoutHandle: NodeJS.Timeout | undefined;
65
- if (options.timeoutSeconds && options.timeoutSeconds > 0) {
66
- timeoutHandle = setTimeout(onAbort, options.timeoutSeconds * 1000);
67
- }
68
-
69
- if (options.signal) {
70
- if (options.signal.aborted) {
71
- onAbort();
72
- } else {
73
- options.signal.addEventListener("abort", onAbort, { once: true });
74
- }
75
- }
76
-
77
- child.stdout.on("data", (chunk) => {
78
- stdout += String(chunk);
79
- });
80
- child.stderr.on("data", (chunk) => {
81
- stderr += String(chunk);
82
- });
83
-
84
- child.on("error", (error) => {
85
- if (timeoutHandle) clearTimeout(timeoutHandle);
86
- if (options.signal) options.signal.removeEventListener("abort", onAbort);
87
- reject(error);
88
- });
89
-
90
- child.on("close", (code) => {
91
- if (timeoutHandle) clearTimeout(timeoutHandle);
92
- if (options.signal) options.signal.removeEventListener("abort", onAbort);
93
- resolveResult({ stdout, stderr, code: code ?? 0, killed });
94
- });
95
- });
96
- }
97
-
98
- spawn(options: SpawnOptions): SpawnedProcess {
99
- const cwd = options.cwd ?? this.config.hostWorkspaceRoot;
100
- const containerCwd = this.toContainerPath(cwd);
101
- const argv = [options.command, ...options.args].map(shellEscape).join(" ");
102
- const wrapped = `cd ${shellEscape(containerCwd)} && exec ${argv}`;
103
-
104
- const args = ["exec", "-i"];
105
- if (options.tty) {
106
- args.push("-t");
107
- }
108
- if (options.env) {
109
- for (const [key, value] of Object.entries(options.env)) {
110
- if (value !== undefined) {
111
- args.push("-e", `${key}=${value}`);
112
- }
113
- }
114
- }
115
- args.push(this.config.container, "sh", "-lc", wrapped);
116
-
117
- const child = spawn("docker", args, {
118
- stdio: ["pipe", "pipe", "pipe"],
119
- });
120
-
121
- const onAbort = () => {
122
- child.kill("SIGKILL");
123
- };
124
-
125
- if (options.signal) {
126
- if (options.signal.aborted) {
127
- onAbort();
128
- } else {
129
- options.signal.addEventListener("abort", onAbort, { once: true });
130
- child.once("exit", () => {
131
- options.signal?.removeEventListener("abort", onAbort);
132
- });
133
- }
134
- }
135
-
136
- if (!child.stdin || !child.stdout || !child.stderr) {
137
- child.kill("SIGKILL");
138
- throw new Error("Docker executor failed to create stdio pipes for spawned process");
139
- }
140
-
141
- const spawned: SpawnedProcess = {
142
- stdin: child.stdin,
143
- stdout: child.stdout,
144
- stderr: child.stderr,
145
- get killed() {
146
- return child.killed;
147
- },
148
- get exitCode() {
149
- return child.exitCode;
150
- },
151
- kill(signal = "SIGKILL") {
152
- return child.kill(signal);
153
- },
154
- on(event, listener) {
155
- child.on(event, listener as (...args: unknown[]) => void);
156
- },
157
- once(event, listener) {
158
- child.once(event, listener as (...args: unknown[]) => void);
159
- },
160
- off(event, listener) {
161
- child.off(event, listener as (...args: unknown[]) => void);
162
- },
163
- };
164
-
165
- return spawned;
166
- }
167
-
168
- async close(): Promise<void> {
169
- this.logger.debug({ container: this.config.container }, "DockerExecutor closed");
170
- }
171
-
172
- private async validate(): Promise<void> {
173
- await this.execSimple("docker", ["--version"]);
174
-
175
- const inspect = await this.execSimple("docker", ["inspect", "-f", "{{.State.Running}}", this.config.container]);
176
- if (inspect.trim() !== "true") {
177
- throw new Error(`Docker container '${this.config.container}' is not running`);
178
- }
179
- }
180
-
181
- private toContainerPath(hostPath: string): string {
182
- const resolved = resolve(hostPath);
183
- if (!resolved.startsWith(this.normalizedHostRoot) && resolved !== this.normalizedHostRoot.slice(0, -1)) {
184
- throw new Error(`Path '${resolved}' is outside docker hostWorkspaceRoot '${this.config.hostWorkspaceRoot}'`);
185
- }
186
-
187
- const relative = resolved.slice(this.normalizedHostRoot.length).replaceAll("\\", "/");
188
- const base = this.config.containerWorkspaceRoot.endsWith("/")
189
- ? this.config.containerWorkspaceRoot.slice(0, -1)
190
- : this.config.containerWorkspaceRoot;
191
-
192
- return relative.length > 0 ? `${base}/${relative}` : base;
193
- }
194
-
195
- private execSimple(command: string, args: string[]): Promise<string> {
196
- return new Promise((resolveOutput, reject) => {
197
- const child = spawn(command, args, { stdio: ["ignore", "pipe", "pipe"] });
198
- let stdout = "";
199
- let stderr = "";
200
-
201
- child.stdout.on("data", (chunk) => {
202
- stdout += String(chunk);
203
- });
204
- child.stderr.on("data", (chunk) => {
205
- stderr += String(chunk);
206
- });
207
- child.on("error", reject);
208
- child.on("close", (code) => {
209
- if (code === 0) {
210
- resolveOutput(stdout);
211
- } else {
212
- reject(new Error(stderr || `Command failed: ${command} ${args.join(" ")}`));
213
- }
214
- });
215
- });
216
- }
217
- }
@@ -1,19 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "NodeNext",
5
- "moduleResolution": "NodeNext",
6
- "outDir": "./dist",
7
- "rootDir": "./src",
8
- "types": ["node"],
9
- "strict": true,
10
- "esModuleInterop": true,
11
- "forceConsistentCasingInFileNames": true,
12
- "skipLibCheck": true,
13
- "noUncheckedIndexedAccess": true,
14
- "exactOptionalPropertyTypes": true,
15
- "resolveJsonModule": true
16
- },
17
- "include": ["src/**/*.ts"],
18
- "exclude": ["dist", "node_modules"]
19
- }
@@ -1,168 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { spawn } from "node:child_process";
4
- import { access, readdir, readFile } from "node:fs/promises";
5
- import { dirname, resolve } from "node:path";
6
- import { fileURLToPath } from "node:url";
7
-
8
- const scriptDir = dirname(fileURLToPath(import.meta.url));
9
- const projectRoot = resolve(scriptDir, "..");
10
-
11
- const npmCommand = process.platform === "win32" ? "npm.cmd" : "npm";
12
- const nodeCommand = process.execPath;
13
-
14
- const pluginsRootDir = resolve(projectRoot, "plugins");
15
- const pluginSdkDir = "plugins/plugin-sdk";
16
-
17
- async function discoverLocalExtensionPackages() {
18
- const entries = await readdir(pluginsRootDir, { withFileTypes: true });
19
- const discovered = [];
20
-
21
- for (const entry of entries) {
22
- if (!entry.isDirectory()) {
23
- continue;
24
- }
25
- if (entry.name === "plugin-sdk") {
26
- continue;
27
- }
28
-
29
- const relativeDir = `plugins/${entry.name}`;
30
- const packageJsonPath = resolve(projectRoot, relativeDir, "package.json");
31
- const manifestPath = resolve(projectRoot, relativeDir, "dobby.manifest.json");
32
-
33
- try {
34
- await access(packageJsonPath);
35
- await access(manifestPath);
36
- const rawPackageJson = await readFile(packageJsonPath, "utf-8");
37
- const parsedPackageJson = JSON.parse(rawPackageJson);
38
- if (typeof parsedPackageJson?.name !== "string" || parsedPackageJson.name.length === 0) {
39
- throw new Error(`Missing package name in ${packageJsonPath}`);
40
- }
41
-
42
- discovered.push({
43
- name: parsedPackageJson.name,
44
- dir: relativeDir,
45
- });
46
- } catch {
47
- continue;
48
- }
49
- }
50
-
51
- discovered.sort((a, b) => a.dir.localeCompare(b.dir));
52
- return discovered;
53
- }
54
-
55
- function printUsage() {
56
- console.log("Usage: node scripts/local-extensions.mjs <command>");
57
- console.log("");
58
- console.log("Commands:");
59
- console.log(" install Install local plugin development dependencies");
60
- console.log(" check Type-check local extension plugins");
61
- console.log(" build Build local extension plugins");
62
- console.log(" install-store Install local extension plugins into extension store");
63
- console.log(" list-store List installed extensions from extension store");
64
- console.log(" setup Run install + build + install-store");
65
- }
66
-
67
- async function run(command, args) {
68
- const pretty = [command, ...args].join(" ");
69
- console.log(`$ ${pretty}`);
70
-
71
- await new Promise((resolvePromise, rejectPromise) => {
72
- const child = spawn(command, args, {
73
- cwd: projectRoot,
74
- stdio: "inherit",
75
- env: process.env,
76
- });
77
-
78
- child.once("error", (error) => rejectPromise(error));
79
- child.once("exit", (code) => {
80
- if (code === 0) {
81
- resolvePromise();
82
- return;
83
- }
84
- rejectPromise(new Error(`Command failed (${code ?? "unknown"}): ${pretty}`));
85
- });
86
- });
87
- }
88
-
89
- async function installLocalPluginDeps(localExtensionPackages) {
90
- await run(npmCommand, ["install", "--prefix", pluginSdkDir]);
91
- for (const item of localExtensionPackages) {
92
- await run(npmCommand, ["install", "--prefix", item.dir]);
93
- }
94
- }
95
-
96
- async function checkLocalPlugins(localExtensionPackages) {
97
- for (const item of localExtensionPackages) {
98
- await run(npmCommand, ["run", "check", "--prefix", item.dir]);
99
- }
100
- }
101
-
102
- async function buildLocalPlugins(localExtensionPackages) {
103
- for (const item of localExtensionPackages) {
104
- await run(npmCommand, ["run", "build", "--prefix", item.dir]);
105
- }
106
- }
107
-
108
- async function installToExtensionStore(localExtensionPackages) {
109
- for (const item of localExtensionPackages) {
110
- await run(nodeCommand, [
111
- "--import",
112
- "tsx",
113
- "src/main.ts",
114
- "extension",
115
- "install",
116
- `file:./${item.dir}`,
117
- ]);
118
- }
119
- }
120
-
121
- async function listExtensionStore() {
122
- await run(nodeCommand, ["--import", "tsx", "src/main.ts", "extension", "list"]);
123
- }
124
-
125
- async function main() {
126
- const [command] = process.argv.slice(2);
127
-
128
- if (!command || command === "--help" || command === "-h") {
129
- printUsage();
130
- process.exit(command ? 0 : 1);
131
- }
132
-
133
- const needsLocalPackages = command !== "list-store";
134
- const localExtensionPackages = needsLocalPackages ? await discoverLocalExtensionPackages() : [];
135
- if (needsLocalPackages && localExtensionPackages.length === 0) {
136
- throw new Error("No local extension packages found under ./plugins");
137
- }
138
-
139
- switch (command) {
140
- case "install":
141
- await installLocalPluginDeps(localExtensionPackages);
142
- return;
143
- case "check":
144
- await checkLocalPlugins(localExtensionPackages);
145
- return;
146
- case "build":
147
- await buildLocalPlugins(localExtensionPackages);
148
- return;
149
- case "install-store":
150
- await installToExtensionStore(localExtensionPackages);
151
- return;
152
- case "list-store":
153
- await listExtensionStore();
154
- return;
155
- case "setup":
156
- await installLocalPluginDeps(localExtensionPackages);
157
- await buildLocalPlugins(localExtensionPackages);
158
- await installToExtensionStore(localExtensionPackages);
159
- return;
160
- default:
161
- throw new Error(`Unknown command '${command}'`);
162
- }
163
- }
164
-
165
- main().catch((error) => {
166
- console.error(error instanceof Error ? error.message : String(error));
167
- process.exit(1);
168
- });