@clinebot/core 0.0.11 → 0.0.13
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 +1 -1
- package/dist/agents/agent-config-loader.d.ts +1 -1
- package/dist/agents/agent-config-parser.d.ts +5 -2
- package/dist/agents/index.d.ts +1 -1
- package/dist/agents/plugin-config-loader.d.ts +4 -0
- package/dist/agents/plugin-loader.d.ts +1 -0
- package/dist/agents/plugin-sandbox-bootstrap.js +446 -0
- package/dist/agents/plugin-sandbox.d.ts +4 -0
- package/dist/index.node.d.ts +5 -0
- package/dist/index.node.js +685 -413
- package/dist/runtime/commands.d.ts +11 -0
- package/dist/runtime/sandbox/subprocess-sandbox.d.ts +8 -1
- package/dist/runtime/skills.d.ts +13 -0
- package/dist/session/default-session-manager.d.ts +5 -0
- package/dist/session/session-config-builder.d.ts +4 -1
- package/dist/session/session-manager.d.ts +1 -0
- package/dist/session/session-service.d.ts +22 -22
- package/dist/session/unified-session-persistence-service.d.ts +12 -6
- package/dist/session/utils/helpers.d.ts +2 -2
- package/dist/session/utils/types.d.ts +9 -0
- package/dist/tools/definitions.d.ts +2 -2
- package/dist/tools/presets.d.ts +3 -3
- package/dist/tools/schemas.d.ts +15 -14
- package/dist/types/config.d.ts +5 -0
- package/dist/types/events.d.ts +22 -0
- package/package.json +5 -4
- package/src/agents/agent-config-loader.test.ts +2 -0
- package/src/agents/agent-config-loader.ts +1 -0
- package/src/agents/agent-config-parser.ts +12 -5
- package/src/agents/index.ts +1 -0
- package/src/agents/plugin-config-loader.test.ts +49 -0
- package/src/agents/plugin-config-loader.ts +10 -73
- package/src/agents/plugin-loader.test.ts +127 -1
- package/src/agents/plugin-loader.ts +72 -5
- package/src/agents/plugin-sandbox-bootstrap.ts +445 -0
- package/src/agents/plugin-sandbox.test.ts +198 -1
- package/src/agents/plugin-sandbox.ts +223 -353
- package/src/index.node.ts +14 -0
- package/src/runtime/commands.test.ts +98 -0
- package/src/runtime/commands.ts +83 -0
- package/src/runtime/hook-file-hooks.test.ts +1 -1
- package/src/runtime/hook-file-hooks.ts +16 -6
- package/src/runtime/index.ts +10 -0
- package/src/runtime/runtime-builder.test.ts +67 -0
- package/src/runtime/runtime-builder.ts +70 -16
- package/src/runtime/sandbox/subprocess-sandbox.ts +35 -11
- package/src/runtime/skills.ts +44 -0
- package/src/runtime/workflows.ts +20 -29
- package/src/session/default-session-manager.e2e.test.ts +52 -33
- package/src/session/default-session-manager.test.ts +453 -1
- package/src/session/default-session-manager.ts +210 -12
- package/src/session/rpc-session-service.ts +14 -96
- package/src/session/session-config-builder.ts +2 -0
- package/src/session/session-manager.ts +1 -0
- package/src/session/session-service.ts +127 -64
- package/src/session/session-team-coordination.ts +30 -0
- package/src/session/unified-session-persistence-service.test.ts +3 -3
- package/src/session/unified-session-persistence-service.ts +159 -141
- package/src/session/utils/helpers.ts +22 -41
- package/src/session/utils/types.ts +10 -0
- package/src/storage/sqlite-team-store.ts +16 -5
- package/src/tools/definitions.test.ts +137 -8
- package/src/tools/definitions.ts +115 -70
- package/src/tools/presets.test.ts +2 -3
- package/src/tools/presets.ts +3 -3
- package/src/tools/schemas.ts +28 -28
- package/src/types/config.ts +5 -0
- package/src/types/events.ts +23 -0
|
@@ -1,19 +1,13 @@
|
|
|
1
|
-
import { existsSync
|
|
2
|
-
import { join, resolve } from "node:path";
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
3
2
|
import type { AgentConfig } from "@clinebot/agents";
|
|
4
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
discoverPluginModulePaths as discoverPluginModulePathsFromShared,
|
|
5
|
+
resolveConfiguredPluginModulePaths,
|
|
6
|
+
resolvePluginConfigSearchPaths as resolvePluginConfigSearchPathsFromShared,
|
|
7
|
+
} from "@clinebot/shared/storage";
|
|
5
8
|
import { loadAgentPluginsFromPaths } from "./plugin-loader";
|
|
6
9
|
import { loadSandboxedPlugins } from "./plugin-sandbox";
|
|
7
10
|
|
|
8
|
-
const PLUGIN_MODULE_EXTENSIONS = new Set([
|
|
9
|
-
".js",
|
|
10
|
-
".mjs",
|
|
11
|
-
".cjs",
|
|
12
|
-
".ts",
|
|
13
|
-
".mts",
|
|
14
|
-
".cts",
|
|
15
|
-
]);
|
|
16
|
-
|
|
17
11
|
type AgentPlugin = NonNullable<AgentConfig["extensions"]>[number];
|
|
18
12
|
|
|
19
13
|
export function resolvePluginConfigSearchPaths(
|
|
@@ -22,67 +16,8 @@ export function resolvePluginConfigSearchPaths(
|
|
|
22
16
|
return resolvePluginConfigSearchPathsFromShared(workspacePath);
|
|
23
17
|
}
|
|
24
18
|
|
|
25
|
-
function hasPluginModuleExtension(path: string): boolean {
|
|
26
|
-
const dot = path.lastIndexOf(".");
|
|
27
|
-
if (dot === -1) {
|
|
28
|
-
return false;
|
|
29
|
-
}
|
|
30
|
-
return PLUGIN_MODULE_EXTENSIONS.has(path.slice(dot));
|
|
31
|
-
}
|
|
32
|
-
|
|
33
19
|
export function discoverPluginModulePaths(directoryPath: string): string[] {
|
|
34
|
-
|
|
35
|
-
if (!existsSync(root)) {
|
|
36
|
-
return [];
|
|
37
|
-
}
|
|
38
|
-
const discovered: string[] = [];
|
|
39
|
-
const stack = [root];
|
|
40
|
-
while (stack.length > 0) {
|
|
41
|
-
const current = stack.pop();
|
|
42
|
-
if (!current) {
|
|
43
|
-
continue;
|
|
44
|
-
}
|
|
45
|
-
for (const entry of readdirSync(current, { withFileTypes: true })) {
|
|
46
|
-
const candidate = join(current, entry.name);
|
|
47
|
-
if (entry.isDirectory()) {
|
|
48
|
-
stack.push(candidate);
|
|
49
|
-
continue;
|
|
50
|
-
}
|
|
51
|
-
if (entry.isFile() && hasPluginModuleExtension(candidate)) {
|
|
52
|
-
discovered.push(candidate);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
return discovered.sort((a, b) => a.localeCompare(b));
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function resolveConfiguredPluginPaths(
|
|
60
|
-
pluginPaths: ReadonlyArray<string>,
|
|
61
|
-
cwd: string,
|
|
62
|
-
): string[] {
|
|
63
|
-
const resolvedPaths: string[] = [];
|
|
64
|
-
for (const pluginPath of pluginPaths) {
|
|
65
|
-
const trimmed = pluginPath.trim();
|
|
66
|
-
if (!trimmed) {
|
|
67
|
-
continue;
|
|
68
|
-
}
|
|
69
|
-
const absolutePath = resolve(cwd, trimmed);
|
|
70
|
-
if (!existsSync(absolutePath)) {
|
|
71
|
-
throw new Error(`Plugin path does not exist: ${absolutePath}`);
|
|
72
|
-
}
|
|
73
|
-
const stats = statSync(absolutePath);
|
|
74
|
-
if (stats.isDirectory()) {
|
|
75
|
-
resolvedPaths.push(...discoverPluginModulePaths(absolutePath));
|
|
76
|
-
continue;
|
|
77
|
-
}
|
|
78
|
-
if (!hasPluginModuleExtension(absolutePath)) {
|
|
79
|
-
throw new Error(
|
|
80
|
-
`Plugin file must use a supported extension (${[...PLUGIN_MODULE_EXTENSIONS].join(", ")}): ${absolutePath}`,
|
|
81
|
-
);
|
|
82
|
-
}
|
|
83
|
-
resolvedPaths.push(absolutePath);
|
|
84
|
-
}
|
|
85
|
-
return resolvedPaths;
|
|
20
|
+
return discoverPluginModulePathsFromShared(directoryPath);
|
|
86
21
|
}
|
|
87
22
|
|
|
88
23
|
export interface ResolveAgentPluginPathsOptions {
|
|
@@ -100,7 +35,7 @@ export function resolveAgentPluginPaths(
|
|
|
100
35
|
)
|
|
101
36
|
.flatMap((directoryPath) => discoverPluginModulePaths(directoryPath))
|
|
102
37
|
.filter((path) => existsSync(path));
|
|
103
|
-
const configuredPaths =
|
|
38
|
+
const configuredPaths = resolveConfiguredPluginModulePaths(
|
|
104
39
|
options.pluginPaths ?? [],
|
|
105
40
|
cwd,
|
|
106
41
|
);
|
|
@@ -124,6 +59,7 @@ export interface ResolveAndLoadAgentPluginsOptions
|
|
|
124
59
|
importTimeoutMs?: number;
|
|
125
60
|
hookTimeoutMs?: number;
|
|
126
61
|
contributionTimeoutMs?: number;
|
|
62
|
+
onEvent?: (event: { name: string; payload?: unknown }) => void;
|
|
127
63
|
}
|
|
128
64
|
|
|
129
65
|
export async function resolveAndLoadAgentPlugins(
|
|
@@ -152,6 +88,7 @@ export async function resolveAndLoadAgentPlugins(
|
|
|
152
88
|
importTimeoutMs: options.importTimeoutMs,
|
|
153
89
|
hookTimeoutMs: options.hookTimeoutMs,
|
|
154
90
|
contributionTimeoutMs: options.contributionTimeoutMs,
|
|
91
|
+
onEvent: options.onEvent,
|
|
155
92
|
});
|
|
156
93
|
return {
|
|
157
94
|
extensions: sandboxed.extensions ?? [],
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
1
|
+
import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
2
2
|
import { tmpdir } from "node:os";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { describe, expect, it } from "vitest";
|
|
@@ -82,6 +82,132 @@ describe("plugin-loader", () => {
|
|
|
82
82
|
}
|
|
83
83
|
});
|
|
84
84
|
|
|
85
|
+
it("loads TypeScript plugins from file paths", async () => {
|
|
86
|
+
const dir = await mkdtemp(join(tmpdir(), "core-plugin-loader-"));
|
|
87
|
+
try {
|
|
88
|
+
const pluginPath = join(dir, "plugin-ts.ts");
|
|
89
|
+
await writeFile(
|
|
90
|
+
pluginPath,
|
|
91
|
+
[
|
|
92
|
+
"const name: string = 'plugin-ts';",
|
|
93
|
+
"export default {",
|
|
94
|
+
" name,",
|
|
95
|
+
" manifest: { capabilities: ['tools'] },",
|
|
96
|
+
"};",
|
|
97
|
+
].join("\n"),
|
|
98
|
+
"utf8",
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
const plugin = await loadAgentPluginFromPath(pluginPath);
|
|
102
|
+
expect(plugin.name).toBe("plugin-ts");
|
|
103
|
+
} finally {
|
|
104
|
+
await rm(dir, { recursive: true, force: true });
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("resolves plugin-local dependencies from the plugin path", async () => {
|
|
109
|
+
const dir = await mkdtemp(join(tmpdir(), "core-plugin-loader-"));
|
|
110
|
+
try {
|
|
111
|
+
const depDir = join(dir, "node_modules", "plugin-local-dep");
|
|
112
|
+
await mkdir(depDir, { recursive: true });
|
|
113
|
+
await writeFile(
|
|
114
|
+
join(depDir, "package.json"),
|
|
115
|
+
JSON.stringify({
|
|
116
|
+
name: "plugin-local-dep",
|
|
117
|
+
type: "module",
|
|
118
|
+
exports: "./index.js",
|
|
119
|
+
}),
|
|
120
|
+
"utf8",
|
|
121
|
+
);
|
|
122
|
+
await writeFile(
|
|
123
|
+
join(depDir, "index.js"),
|
|
124
|
+
"export const depName = 'plugin-local-dep';\n",
|
|
125
|
+
"utf8",
|
|
126
|
+
);
|
|
127
|
+
const pluginPath = join(dir, "plugin-with-dep.ts");
|
|
128
|
+
await writeFile(
|
|
129
|
+
pluginPath,
|
|
130
|
+
[
|
|
131
|
+
"import { depName } from 'plugin-local-dep';",
|
|
132
|
+
"export default {",
|
|
133
|
+
" name: depName,",
|
|
134
|
+
" manifest: { capabilities: ['tools'] },",
|
|
135
|
+
"};",
|
|
136
|
+
].join("\n"),
|
|
137
|
+
"utf8",
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
const plugin = await loadAgentPluginFromPath(pluginPath, { cwd: dir });
|
|
141
|
+
expect(plugin.name).toBe("plugin-local-dep");
|
|
142
|
+
} finally {
|
|
143
|
+
await rm(dir, { recursive: true, force: true });
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it("prefers plugin-installed SDK packages over workspace aliases", async () => {
|
|
148
|
+
const dir = await mkdtemp(join(tmpdir(), "core-plugin-loader-"));
|
|
149
|
+
try {
|
|
150
|
+
const depDir = join(dir, "node_modules", "@clinebot", "shared");
|
|
151
|
+
await mkdir(depDir, { recursive: true });
|
|
152
|
+
await writeFile(
|
|
153
|
+
join(depDir, "package.json"),
|
|
154
|
+
JSON.stringify({
|
|
155
|
+
name: "@clinebot/shared",
|
|
156
|
+
type: "module",
|
|
157
|
+
exports: "./index.js",
|
|
158
|
+
}),
|
|
159
|
+
"utf8",
|
|
160
|
+
);
|
|
161
|
+
await writeFile(
|
|
162
|
+
join(depDir, "index.js"),
|
|
163
|
+
"export const sdkMarker = 'plugin-installed-sdk';\n",
|
|
164
|
+
"utf8",
|
|
165
|
+
);
|
|
166
|
+
const pluginPath = join(dir, "plugin-with-sdk-dep.ts");
|
|
167
|
+
await writeFile(
|
|
168
|
+
pluginPath,
|
|
169
|
+
[
|
|
170
|
+
"import { sdkMarker } from '@clinebot/shared';",
|
|
171
|
+
"export default {",
|
|
172
|
+
" name: sdkMarker,",
|
|
173
|
+
" manifest: { capabilities: ['tools'] },",
|
|
174
|
+
"};",
|
|
175
|
+
].join("\n"),
|
|
176
|
+
"utf8",
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
const plugin = await loadAgentPluginFromPath(pluginPath, { cwd: dir });
|
|
180
|
+
expect(plugin.name).toBe("plugin-installed-sdk");
|
|
181
|
+
} finally {
|
|
182
|
+
await rm(dir, { recursive: true, force: true });
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it("requires copied plugins to provide their own non-SDK dependencies", async () => {
|
|
187
|
+
const dir = await mkdtemp(join(tmpdir(), "core-plugin-loader-copy-"));
|
|
188
|
+
try {
|
|
189
|
+
const pluginPath = join(dir, "portable-subagents.ts");
|
|
190
|
+
await writeFile(
|
|
191
|
+
pluginPath,
|
|
192
|
+
[
|
|
193
|
+
"import { resolveClineDataDir } from '@clinebot/shared/storage';",
|
|
194
|
+
"import YAML from 'yaml';",
|
|
195
|
+
"export default {",
|
|
196
|
+
" name: typeof resolveClineDataDir === 'function' ? YAML.stringify({ ok: true }) : 'invalid',",
|
|
197
|
+
" manifest: { capabilities: ['tools'] },",
|
|
198
|
+
"};",
|
|
199
|
+
].join("\n"),
|
|
200
|
+
"utf8",
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
await expect(
|
|
204
|
+
loadAgentPluginFromPath(pluginPath, { cwd: dir, useCache: true }),
|
|
205
|
+
).rejects.toThrow(/Cannot find (package|module) 'yaml'/i);
|
|
206
|
+
} finally {
|
|
207
|
+
await rm(dir, { recursive: true, force: true });
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
|
|
85
211
|
it("rejects invalid plugin export missing manifest", async () => {
|
|
86
212
|
const dir = await mkdtemp(join(tmpdir(), "core-plugin-loader-"));
|
|
87
213
|
try {
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { builtinModules, createRequire } from "node:module";
|
|
3
|
+
import { dirname, resolve } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
3
5
|
import type { AgentConfig } from "@clinebot/agents";
|
|
6
|
+
import createJiti from "jiti";
|
|
4
7
|
|
|
5
8
|
type AgentPlugin = NonNullable<AgentConfig["extensions"]>[number];
|
|
6
9
|
type PluginLike = {
|
|
@@ -14,8 +17,15 @@ type PluginLike = {
|
|
|
14
17
|
export interface LoadAgentPluginFromPathOptions {
|
|
15
18
|
exportName?: string;
|
|
16
19
|
cwd?: string;
|
|
20
|
+
useCache?: boolean;
|
|
17
21
|
}
|
|
18
22
|
|
|
23
|
+
const MODULE_DIR = dirname(fileURLToPath(import.meta.url));
|
|
24
|
+
const WORKSPACE_ALIASES = collectWorkspaceAliases(MODULE_DIR);
|
|
25
|
+
const BUILTIN_MODULES = new Set(
|
|
26
|
+
builtinModules.flatMap((id) => [id, id.replace(/^node:/, "")]),
|
|
27
|
+
);
|
|
28
|
+
|
|
19
29
|
function isObject(value: unknown): value is Record<string, unknown> {
|
|
20
30
|
return typeof value === "object" && value !== null;
|
|
21
31
|
}
|
|
@@ -77,14 +87,71 @@ function validatePluginExport(
|
|
|
77
87
|
validatePluginManifest(plugin as PluginLike, absolutePath);
|
|
78
88
|
}
|
|
79
89
|
|
|
90
|
+
function collectWorkspaceAliases(startDir: string): Record<string, string> {
|
|
91
|
+
const root = resolve(startDir, "..", "..", "..", "..");
|
|
92
|
+
const aliases: Record<string, string> = {};
|
|
93
|
+
const candidates: Record<string, string> = {
|
|
94
|
+
"@clinebot/agents": resolve(root, "packages/agents/src/index.ts"),
|
|
95
|
+
"@clinebot/core": resolve(root, "packages/core/src/index.node.ts"),
|
|
96
|
+
"@clinebot/llms": resolve(root, "packages/llms/src/index.ts"),
|
|
97
|
+
"@clinebot/rpc": resolve(root, "packages/rpc/src/index.ts"),
|
|
98
|
+
"@clinebot/scheduler": resolve(root, "packages/scheduler/src/index.ts"),
|
|
99
|
+
"@clinebot/shared": resolve(root, "packages/shared/src/index.ts"),
|
|
100
|
+
"@clinebot/shared/storage": resolve(
|
|
101
|
+
root,
|
|
102
|
+
"packages/shared/src/storage/index.ts",
|
|
103
|
+
),
|
|
104
|
+
"@clinebot/shared/db": resolve(root, "packages/shared/src/db/index.ts"),
|
|
105
|
+
};
|
|
106
|
+
for (const [key, value] of Object.entries(candidates)) {
|
|
107
|
+
if (existsSync(value)) {
|
|
108
|
+
aliases[key] = value;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return aliases;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function collectPluginImportAliases(
|
|
115
|
+
pluginPath: string,
|
|
116
|
+
): Record<string, string> {
|
|
117
|
+
const pluginRequire = createRequire(pluginPath);
|
|
118
|
+
const aliases: Record<string, string> = {};
|
|
119
|
+
for (const [specifier, sourcePath] of Object.entries(WORKSPACE_ALIASES)) {
|
|
120
|
+
try {
|
|
121
|
+
pluginRequire.resolve(specifier);
|
|
122
|
+
continue;
|
|
123
|
+
} catch {
|
|
124
|
+
// Use the workspace source only when the plugin package does not provide
|
|
125
|
+
// its own installed SDK dependency.
|
|
126
|
+
}
|
|
127
|
+
aliases[specifier] = sourcePath;
|
|
128
|
+
}
|
|
129
|
+
return aliases;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async function importPluginModule(
|
|
133
|
+
absolutePath: string,
|
|
134
|
+
options: LoadAgentPluginFromPathOptions = {},
|
|
135
|
+
): Promise<Record<string, unknown>> {
|
|
136
|
+
const aliases = collectPluginImportAliases(absolutePath);
|
|
137
|
+
const jiti = createJiti(absolutePath, {
|
|
138
|
+
alias: aliases,
|
|
139
|
+
cache: options.useCache,
|
|
140
|
+
requireCache: options.useCache,
|
|
141
|
+
esmResolve: true,
|
|
142
|
+
interopDefault: false,
|
|
143
|
+
nativeModules: [...BUILTIN_MODULES],
|
|
144
|
+
transformModules: Object.keys(aliases),
|
|
145
|
+
});
|
|
146
|
+
return (await jiti.import(absolutePath, {})) as Record<string, unknown>;
|
|
147
|
+
}
|
|
148
|
+
|
|
80
149
|
export async function loadAgentPluginFromPath(
|
|
81
150
|
pluginPath: string,
|
|
82
151
|
options: LoadAgentPluginFromPathOptions = {},
|
|
83
152
|
): Promise<AgentPlugin> {
|
|
84
153
|
const absolutePath = resolve(options.cwd ?? process.cwd(), pluginPath);
|
|
85
|
-
const moduleExports =
|
|
86
|
-
pathToFileURL(absolutePath).href
|
|
87
|
-
)) as Record<string, unknown>;
|
|
154
|
+
const moduleExports = await importPluginModule(absolutePath, options);
|
|
88
155
|
const exportName = options.exportName ?? "plugin";
|
|
89
156
|
const plugin = (moduleExports.default ??
|
|
90
157
|
moduleExports[exportName]) as unknown;
|