@clinebot/core 0.0.0
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 +88 -0
- package/dist/account/cline-account-service.d.ts +34 -0
- package/dist/account/index.d.ts +3 -0
- package/dist/account/rpc.d.ts +38 -0
- package/dist/account/types.d.ts +74 -0
- package/dist/agents/agent-config-loader.d.ts +18 -0
- package/dist/agents/agent-config-parser.d.ts +25 -0
- package/dist/agents/hooks-config-loader.d.ts +23 -0
- package/dist/agents/index.d.ts +11 -0
- package/dist/agents/plugin-config-loader.d.ts +22 -0
- package/dist/agents/plugin-loader.d.ts +9 -0
- package/dist/agents/plugin-sandbox.d.ts +12 -0
- package/dist/agents/unified-config-file-watcher.d.ts +77 -0
- package/dist/agents/user-instruction-config-loader.d.ts +63 -0
- package/dist/auth/client.d.ts +11 -0
- package/dist/auth/cline.d.ts +41 -0
- package/dist/auth/codex.d.ts +39 -0
- package/dist/auth/oca.d.ts +22 -0
- package/dist/auth/server.d.ts +22 -0
- package/dist/auth/types.d.ts +72 -0
- package/dist/auth/utils.d.ts +32 -0
- package/dist/chat/chat-schema.d.ts +145 -0
- package/dist/default-tools/constants.d.ts +23 -0
- package/dist/default-tools/definitions.d.ts +96 -0
- package/dist/default-tools/executors/apply-patch-parser.d.ts +68 -0
- package/dist/default-tools/executors/apply-patch.d.ts +26 -0
- package/dist/default-tools/executors/bash.d.ts +49 -0
- package/dist/default-tools/executors/editor.d.ts +31 -0
- package/dist/default-tools/executors/file-read.d.ts +40 -0
- package/dist/default-tools/executors/index.d.ts +44 -0
- package/dist/default-tools/executors/search.d.ts +50 -0
- package/dist/default-tools/executors/web-fetch.d.ts +58 -0
- package/dist/default-tools/index.d.ts +57 -0
- package/dist/default-tools/presets.d.ts +124 -0
- package/dist/default-tools/schemas.d.ts +121 -0
- package/dist/default-tools/types.d.ts +237 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +220 -0
- package/dist/input/file-indexer.d.ts +5 -0
- package/dist/input/index.d.ts +4 -0
- package/dist/input/mention-enricher.d.ts +12 -0
- package/dist/mcp/config-loader.d.ts +15 -0
- package/dist/mcp/index.d.ts +4 -0
- package/dist/mcp/manager.d.ts +24 -0
- package/dist/mcp/types.d.ts +66 -0
- package/dist/runtime/hook-file-hooks.d.ts +18 -0
- package/dist/runtime/rules.d.ts +5 -0
- package/dist/runtime/runtime-builder.d.ts +5 -0
- package/dist/runtime/sandbox/subprocess-sandbox.d.ts +19 -0
- package/dist/runtime/session-runtime.d.ts +36 -0
- package/dist/runtime/tool-approval.d.ts +9 -0
- package/dist/runtime/workflows.d.ts +13 -0
- package/dist/server/index.d.ts +47 -0
- package/dist/server/index.js +641 -0
- package/dist/session/default-session-manager.d.ts +77 -0
- package/dist/session/rpc-session-service.d.ts +12 -0
- package/dist/session/runtime-oauth-token-manager.d.ts +28 -0
- package/dist/session/session-artifacts.d.ts +19 -0
- package/dist/session/session-graph.d.ts +15 -0
- package/dist/session/session-host.d.ts +21 -0
- package/dist/session/session-manager.d.ts +50 -0
- package/dist/session/session-manifest.d.ts +30 -0
- package/dist/session/session-service.d.ts +113 -0
- package/dist/session/sqlite-rpc-session-backend.d.ts +30 -0
- package/dist/session/unified-session-persistence-service.d.ts +93 -0
- package/dist/session/workspace-manager.d.ts +28 -0
- package/dist/session/workspace-manifest.d.ts +25 -0
- package/dist/storage/provider-settings-legacy-migration.d.ts +13 -0
- package/dist/storage/provider-settings-manager.d.ts +20 -0
- package/dist/storage/sqlite-session-store.d.ts +29 -0
- package/dist/storage/sqlite-team-store.d.ts +31 -0
- package/dist/storage/team-store.d.ts +2 -0
- package/dist/team/index.d.ts +1 -0
- package/dist/team/projections.d.ts +8 -0
- package/dist/types/common.d.ts +10 -0
- package/dist/types/config.d.ts +37 -0
- package/dist/types/events.d.ts +54 -0
- package/dist/types/provider-settings.d.ts +20 -0
- package/dist/types/sessions.d.ts +9 -0
- package/dist/types/storage.d.ts +37 -0
- package/dist/types/workspace.d.ts +7 -0
- package/dist/types.d.ts +26 -0
- package/package.json +63 -0
- package/src/account/cline-account-service.test.ts +101 -0
- package/src/account/cline-account-service.ts +267 -0
- package/src/account/index.ts +20 -0
- package/src/account/rpc.test.ts +62 -0
- package/src/account/rpc.ts +172 -0
- package/src/account/types.ts +80 -0
- package/src/agents/agent-config-loader.test.ts +234 -0
- package/src/agents/agent-config-loader.ts +107 -0
- package/src/agents/agent-config-parser.ts +191 -0
- package/src/agents/hooks-config-loader.ts +97 -0
- package/src/agents/index.ts +84 -0
- package/src/agents/plugin-config-loader.test.ts +91 -0
- package/src/agents/plugin-config-loader.ts +160 -0
- package/src/agents/plugin-loader.test.ts +102 -0
- package/src/agents/plugin-loader.ts +105 -0
- package/src/agents/plugin-sandbox.test.ts +120 -0
- package/src/agents/plugin-sandbox.ts +471 -0
- package/src/agents/unified-config-file-watcher.test.ts +196 -0
- package/src/agents/unified-config-file-watcher.ts +483 -0
- package/src/agents/user-instruction-config-loader.test.ts +158 -0
- package/src/agents/user-instruction-config-loader.ts +438 -0
- package/src/auth/client.test.ts +40 -0
- package/src/auth/client.ts +25 -0
- package/src/auth/cline.test.ts +130 -0
- package/src/auth/cline.ts +414 -0
- package/src/auth/codex.test.ts +170 -0
- package/src/auth/codex.ts +466 -0
- package/src/auth/oca.test.ts +215 -0
- package/src/auth/oca.ts +546 -0
- package/src/auth/server.ts +216 -0
- package/src/auth/types.ts +78 -0
- package/src/auth/utils.test.ts +128 -0
- package/src/auth/utils.ts +247 -0
- package/src/chat/chat-schema.ts +82 -0
- package/src/default-tools/constants.ts +35 -0
- package/src/default-tools/definitions.test.ts +233 -0
- package/src/default-tools/definitions.ts +632 -0
- package/src/default-tools/executors/apply-patch-parser.ts +520 -0
- package/src/default-tools/executors/apply-patch.ts +359 -0
- package/src/default-tools/executors/bash.ts +205 -0
- package/src/default-tools/executors/editor.ts +231 -0
- package/src/default-tools/executors/file-read.test.ts +25 -0
- package/src/default-tools/executors/file-read.ts +94 -0
- package/src/default-tools/executors/index.ts +75 -0
- package/src/default-tools/executors/search.ts +278 -0
- package/src/default-tools/executors/web-fetch.ts +259 -0
- package/src/default-tools/index.ts +161 -0
- package/src/default-tools/presets.test.ts +63 -0
- package/src/default-tools/presets.ts +168 -0
- package/src/default-tools/schemas.ts +228 -0
- package/src/default-tools/types.ts +324 -0
- package/src/index.ts +119 -0
- package/src/input/file-indexer.d.ts +11 -0
- package/src/input/file-indexer.test.ts +87 -0
- package/src/input/file-indexer.ts +280 -0
- package/src/input/index.ts +7 -0
- package/src/input/mention-enricher.test.ts +82 -0
- package/src/input/mention-enricher.ts +119 -0
- package/src/mcp/config-loader.test.ts +238 -0
- package/src/mcp/config-loader.ts +219 -0
- package/src/mcp/index.ts +26 -0
- package/src/mcp/manager.test.ts +106 -0
- package/src/mcp/manager.ts +262 -0
- package/src/mcp/types.ts +88 -0
- package/src/runtime/hook-file-hooks.test.ts +106 -0
- package/src/runtime/hook-file-hooks.ts +736 -0
- package/src/runtime/index.ts +27 -0
- package/src/runtime/rules.ts +34 -0
- package/src/runtime/runtime-builder.team-persistence.test.ts +203 -0
- package/src/runtime/runtime-builder.test.ts +215 -0
- package/src/runtime/runtime-builder.ts +515 -0
- package/src/runtime/runtime-parity.test.ts +132 -0
- package/src/runtime/sandbox/subprocess-sandbox.ts +207 -0
- package/src/runtime/session-runtime.ts +44 -0
- package/src/runtime/tool-approval.ts +104 -0
- package/src/runtime/workflows.test.ts +119 -0
- package/src/runtime/workflows.ts +54 -0
- package/src/server/index.ts +282 -0
- package/src/session/default-session-manager.e2e.test.ts +354 -0
- package/src/session/default-session-manager.test.ts +816 -0
- package/src/session/default-session-manager.ts +1286 -0
- package/src/session/index.ts +37 -0
- package/src/session/rpc-session-service.ts +189 -0
- package/src/session/runtime-oauth-token-manager.test.ts +137 -0
- package/src/session/runtime-oauth-token-manager.ts +265 -0
- package/src/session/session-artifacts.ts +106 -0
- package/src/session/session-graph.ts +90 -0
- package/src/session/session-host.ts +190 -0
- package/src/session/session-manager.ts +56 -0
- package/src/session/session-manifest.ts +29 -0
- package/src/session/session-service.team-persistence.test.ts +48 -0
- package/src/session/session-service.ts +610 -0
- package/src/session/sqlite-rpc-session-backend.ts +303 -0
- package/src/session/unified-session-persistence-service.ts +781 -0
- package/src/session/workspace-manager.ts +98 -0
- package/src/session/workspace-manifest.ts +100 -0
- package/src/storage/artifact-store.ts +1 -0
- package/src/storage/index.ts +11 -0
- package/src/storage/provider-settings-legacy-migration.test.ts +175 -0
- package/src/storage/provider-settings-legacy-migration.ts +637 -0
- package/src/storage/provider-settings-manager.test.ts +111 -0
- package/src/storage/provider-settings-manager.ts +129 -0
- package/src/storage/session-store.ts +1 -0
- package/src/storage/sqlite-session-store.ts +270 -0
- package/src/storage/sqlite-team-store.ts +443 -0
- package/src/storage/team-store.ts +5 -0
- package/src/team/index.ts +4 -0
- package/src/team/projections.ts +285 -0
- package/src/types/common.ts +14 -0
- package/src/types/config.ts +64 -0
- package/src/types/events.ts +46 -0
- package/src/types/index.ts +24 -0
- package/src/types/provider-settings.ts +43 -0
- package/src/types/sessions.ts +16 -0
- package/src/types/storage.ts +64 -0
- package/src/types/workspace.ts +7 -0
- package/src/types.ts +127 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { describe, expect, it } from "vitest";
|
|
5
|
+
import {
|
|
6
|
+
loadAgentPluginFromPath,
|
|
7
|
+
loadAgentPluginsFromPaths,
|
|
8
|
+
} from "./plugin-loader";
|
|
9
|
+
|
|
10
|
+
describe("plugin-loader", () => {
|
|
11
|
+
it("loads default-exported plugin from path", async () => {
|
|
12
|
+
const dir = await mkdtemp(join(tmpdir(), "core-plugin-loader-"));
|
|
13
|
+
try {
|
|
14
|
+
const pluginPath = join(dir, "plugin-default.mjs");
|
|
15
|
+
await writeFile(
|
|
16
|
+
pluginPath,
|
|
17
|
+
[
|
|
18
|
+
"export default {",
|
|
19
|
+
" name: 'from-default',",
|
|
20
|
+
" manifest: { capabilities: ['hooks'], hookStages: ['input'] },",
|
|
21
|
+
" onInput: ({ input }) => ({ overrideInput: input })",
|
|
22
|
+
"};",
|
|
23
|
+
].join("\n"),
|
|
24
|
+
"utf8",
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
const plugin = await loadAgentPluginFromPath(pluginPath);
|
|
28
|
+
expect(plugin.name).toBe("from-default");
|
|
29
|
+
expect(plugin.manifest.capabilities).toContain("hooks");
|
|
30
|
+
} finally {
|
|
31
|
+
await rm(dir, { recursive: true, force: true });
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("loads named plugin export from path", async () => {
|
|
36
|
+
const dir = await mkdtemp(join(tmpdir(), "core-plugin-loader-"));
|
|
37
|
+
try {
|
|
38
|
+
const pluginPath = join(dir, "plugin-named.mjs");
|
|
39
|
+
await writeFile(
|
|
40
|
+
pluginPath,
|
|
41
|
+
[
|
|
42
|
+
"export const plugin = {",
|
|
43
|
+
" name: 'from-named',",
|
|
44
|
+
" manifest: { capabilities: ['tools'] },",
|
|
45
|
+
"};",
|
|
46
|
+
].join("\n"),
|
|
47
|
+
"utf8",
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
const plugin = await loadAgentPluginFromPath(pluginPath, {
|
|
51
|
+
exportName: "plugin",
|
|
52
|
+
});
|
|
53
|
+
expect(plugin.name).toBe("from-named");
|
|
54
|
+
} finally {
|
|
55
|
+
await rm(dir, { recursive: true, force: true });
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("loads multiple plugins from file paths", async () => {
|
|
60
|
+
const dir = await mkdtemp(join(tmpdir(), "core-plugin-loader-"));
|
|
61
|
+
try {
|
|
62
|
+
const firstPath = join(dir, "plugin-a.mjs");
|
|
63
|
+
const secondPath = join(dir, "plugin-b.mjs");
|
|
64
|
+
await writeFile(
|
|
65
|
+
firstPath,
|
|
66
|
+
"export default { name: 'plugin-a', manifest: { capabilities: ['tools'] } };",
|
|
67
|
+
"utf8",
|
|
68
|
+
);
|
|
69
|
+
await writeFile(
|
|
70
|
+
secondPath,
|
|
71
|
+
"export default { name: 'plugin-b', manifest: { capabilities: ['commands'] } };",
|
|
72
|
+
"utf8",
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
const plugins = await loadAgentPluginsFromPaths([firstPath, secondPath]);
|
|
76
|
+
expect(plugins.map((plugin) => plugin.name)).toEqual([
|
|
77
|
+
"plugin-a",
|
|
78
|
+
"plugin-b",
|
|
79
|
+
]);
|
|
80
|
+
} finally {
|
|
81
|
+
await rm(dir, { recursive: true, force: true });
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("rejects invalid plugin export missing manifest", async () => {
|
|
86
|
+
const dir = await mkdtemp(join(tmpdir(), "core-plugin-loader-"));
|
|
87
|
+
try {
|
|
88
|
+
const pluginPath = join(dir, "invalid-plugin.mjs");
|
|
89
|
+
await writeFile(
|
|
90
|
+
pluginPath,
|
|
91
|
+
"export default { name: 'invalid-plugin' };",
|
|
92
|
+
"utf8",
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
await expect(loadAgentPluginFromPath(pluginPath)).rejects.toThrow(
|
|
96
|
+
/missing required "manifest"/i,
|
|
97
|
+
);
|
|
98
|
+
} finally {
|
|
99
|
+
await rm(dir, { recursive: true, force: true });
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
});
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import { pathToFileURL } from "node:url";
|
|
3
|
+
import type { AgentConfig } from "@clinebot/agents";
|
|
4
|
+
|
|
5
|
+
type AgentPlugin = NonNullable<AgentConfig["extensions"]>[number];
|
|
6
|
+
type PluginLike = {
|
|
7
|
+
name: string;
|
|
8
|
+
manifest: {
|
|
9
|
+
capabilities: string[];
|
|
10
|
+
hookStages?: string[];
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export interface LoadAgentPluginFromPathOptions {
|
|
15
|
+
exportName?: string;
|
|
16
|
+
cwd?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function isObject(value: unknown): value is Record<string, unknown> {
|
|
20
|
+
return typeof value === "object" && value !== null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function hasValidStringArray(value: unknown): value is string[] {
|
|
24
|
+
return (
|
|
25
|
+
Array.isArray(value) && value.every((entry) => typeof entry === "string")
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function validatePluginManifest(
|
|
30
|
+
plugin: PluginLike,
|
|
31
|
+
absolutePath: string,
|
|
32
|
+
): void {
|
|
33
|
+
if (!isObject(plugin.manifest)) {
|
|
34
|
+
throw new Error(
|
|
35
|
+
`Invalid plugin module at ${absolutePath}: missing required "manifest"`,
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
if (!hasValidStringArray(plugin.manifest.capabilities)) {
|
|
39
|
+
throw new Error(
|
|
40
|
+
`Invalid plugin module at ${absolutePath}: manifest.capabilities must be a string array`,
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
if (plugin.manifest.capabilities.length === 0) {
|
|
44
|
+
throw new Error(
|
|
45
|
+
`Invalid plugin module at ${absolutePath}: manifest.capabilities cannot be empty`,
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
if (
|
|
49
|
+
Object.hasOwn(plugin.manifest, "hookStages") &&
|
|
50
|
+
!hasValidStringArray(plugin.manifest.hookStages)
|
|
51
|
+
) {
|
|
52
|
+
throw new Error(
|
|
53
|
+
`Invalid plugin module at ${absolutePath}: manifest.hookStages must be a string array when provided`,
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function validatePluginExport(
|
|
59
|
+
plugin: unknown,
|
|
60
|
+
absolutePath: string,
|
|
61
|
+
): asserts plugin is PluginLike {
|
|
62
|
+
if (!isObject(plugin)) {
|
|
63
|
+
throw new Error(
|
|
64
|
+
`Invalid plugin module at ${absolutePath}: expected object export`,
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
if (typeof plugin.name !== "string" || plugin.name.length === 0) {
|
|
68
|
+
throw new Error(
|
|
69
|
+
`Invalid plugin module at ${absolutePath}: expected non-empty "name"`,
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
if (!Object.hasOwn(plugin, "manifest")) {
|
|
73
|
+
throw new Error(
|
|
74
|
+
`Invalid plugin module at ${absolutePath}: missing required "manifest"`,
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
validatePluginManifest(plugin as PluginLike, absolutePath);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export async function loadAgentPluginFromPath(
|
|
81
|
+
pluginPath: string,
|
|
82
|
+
options: LoadAgentPluginFromPathOptions = {},
|
|
83
|
+
): Promise<AgentPlugin> {
|
|
84
|
+
const absolutePath = resolve(options.cwd ?? process.cwd(), pluginPath);
|
|
85
|
+
const moduleExports = (await import(
|
|
86
|
+
pathToFileURL(absolutePath).href
|
|
87
|
+
)) as Record<string, unknown>;
|
|
88
|
+
const exportName = options.exportName ?? "plugin";
|
|
89
|
+
const plugin = (moduleExports.default ??
|
|
90
|
+
moduleExports[exportName]) as unknown;
|
|
91
|
+
|
|
92
|
+
validatePluginExport(plugin, absolutePath);
|
|
93
|
+
return plugin as AgentPlugin;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export async function loadAgentPluginsFromPaths(
|
|
97
|
+
pluginPaths: string[],
|
|
98
|
+
options: LoadAgentPluginFromPathOptions = {},
|
|
99
|
+
): Promise<AgentPlugin[]> {
|
|
100
|
+
const loaded: AgentPlugin[] = [];
|
|
101
|
+
for (const pluginPath of pluginPaths) {
|
|
102
|
+
loaded.push(await loadAgentPluginFromPath(pluginPath, options));
|
|
103
|
+
}
|
|
104
|
+
return loaded;
|
|
105
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import type { AgentConfig, Tool, ToolContext } from "@clinebot/agents";
|
|
5
|
+
import { describe, expect, it } from "vitest";
|
|
6
|
+
import { loadSandboxedPlugins } from "./plugin-sandbox";
|
|
7
|
+
|
|
8
|
+
function createApiCapture() {
|
|
9
|
+
const tools: Tool[] = [];
|
|
10
|
+
const api = {
|
|
11
|
+
registerTool: (tool: Tool) => tools.push(tool),
|
|
12
|
+
registerCommand: () => {},
|
|
13
|
+
registerShortcut: () => {},
|
|
14
|
+
registerFlag: () => {},
|
|
15
|
+
registerMessageRenderer: () => {},
|
|
16
|
+
registerProvider: () => {},
|
|
17
|
+
};
|
|
18
|
+
return { tools, api };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
describe("plugin-sandbox", () => {
|
|
22
|
+
it("runs plugin hooks and tool contributions in sandbox process", async () => {
|
|
23
|
+
const dir = await mkdtemp(join(tmpdir(), "core-plugin-sandbox-"));
|
|
24
|
+
try {
|
|
25
|
+
const pluginPath = join(dir, "plugin.mjs");
|
|
26
|
+
await writeFile(
|
|
27
|
+
pluginPath,
|
|
28
|
+
[
|
|
29
|
+
"export default {",
|
|
30
|
+
" name: 'sandbox-test',",
|
|
31
|
+
" manifest: { capabilities: ['hooks','tools'], hookStages: ['input'] },",
|
|
32
|
+
" setup(api) {",
|
|
33
|
+
" api.registerTool({",
|
|
34
|
+
" name: 'sandbox_echo',",
|
|
35
|
+
" description: 'echo',",
|
|
36
|
+
" inputSchema: { type: 'object', properties: { value: { type: 'string' } }, required: ['value'] },",
|
|
37
|
+
" execute: async (input) => ({ echoed: input.value }),",
|
|
38
|
+
" });",
|
|
39
|
+
" },",
|
|
40
|
+
" onInput(ctx) { return { overrideInput: String(ctx.input || '').toUpperCase() }; }",
|
|
41
|
+
"};",
|
|
42
|
+
].join("\n"),
|
|
43
|
+
"utf8",
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
const sandboxed = await loadSandboxedPlugins({
|
|
47
|
+
pluginPaths: [pluginPath],
|
|
48
|
+
});
|
|
49
|
+
try {
|
|
50
|
+
expect(sandboxed.extensions).toBeDefined();
|
|
51
|
+
const extension = sandboxed.extensions?.[0];
|
|
52
|
+
expect(extension?.name).toBe("sandbox-test");
|
|
53
|
+
type AgentExtensionInputContext = Parameters<
|
|
54
|
+
NonNullable<NonNullable<AgentConfig["extensions"]>[number]["onInput"]>
|
|
55
|
+
>[0];
|
|
56
|
+
const inputContext: AgentExtensionInputContext = {
|
|
57
|
+
agentId: "agent-1",
|
|
58
|
+
conversationId: "conv-1",
|
|
59
|
+
parentAgentId: null,
|
|
60
|
+
mode: "run",
|
|
61
|
+
input: "hello",
|
|
62
|
+
};
|
|
63
|
+
const control = await extension?.onInput?.(inputContext);
|
|
64
|
+
expect(control?.overrideInput).toBe("HELLO");
|
|
65
|
+
|
|
66
|
+
const { tools, api } = createApiCapture();
|
|
67
|
+
await extension?.setup?.(api);
|
|
68
|
+
expect(tools.map((tool) => tool.name)).toContain("sandbox_echo");
|
|
69
|
+
const echoTool = tools.find((tool) => tool.name === "sandbox_echo");
|
|
70
|
+
expect(echoTool).toBeDefined();
|
|
71
|
+
const result = await echoTool?.execute({ value: "ok" }, {
|
|
72
|
+
agentId: "agent-1",
|
|
73
|
+
conversationId: "conv-1",
|
|
74
|
+
iteration: 1,
|
|
75
|
+
} as ToolContext);
|
|
76
|
+
expect(result).toEqual({ echoed: "ok" });
|
|
77
|
+
} finally {
|
|
78
|
+
await sandboxed.shutdown();
|
|
79
|
+
}
|
|
80
|
+
} finally {
|
|
81
|
+
await rm(dir, { recursive: true, force: true });
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("enforces hook timeout and cancels sandbox process", async () => {
|
|
86
|
+
const dir = await mkdtemp(join(tmpdir(), "core-plugin-sandbox-timeout-"));
|
|
87
|
+
try {
|
|
88
|
+
const pluginPath = join(dir, "plugin-timeout.mjs");
|
|
89
|
+
await writeFile(
|
|
90
|
+
pluginPath,
|
|
91
|
+
[
|
|
92
|
+
"export default {",
|
|
93
|
+
" name: 'sandbox-timeout',",
|
|
94
|
+
" manifest: { capabilities: ['hooks'], hookStages: ['input'] },",
|
|
95
|
+
" onInput() { return new Promise(() => {}); }",
|
|
96
|
+
"};",
|
|
97
|
+
].join("\n"),
|
|
98
|
+
"utf8",
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
const sandboxed = await loadSandboxedPlugins({
|
|
102
|
+
pluginPaths: [pluginPath],
|
|
103
|
+
hookTimeoutMs: 50,
|
|
104
|
+
});
|
|
105
|
+
const extension = sandboxed.extensions?.[0];
|
|
106
|
+
await expect(
|
|
107
|
+
extension?.onInput?.({
|
|
108
|
+
agentId: "agent-1",
|
|
109
|
+
conversationId: "conv-1",
|
|
110
|
+
parentAgentId: null,
|
|
111
|
+
mode: "run",
|
|
112
|
+
input: "hello",
|
|
113
|
+
}),
|
|
114
|
+
).rejects.toThrow(/timed out/i);
|
|
115
|
+
await sandboxed.shutdown();
|
|
116
|
+
} finally {
|
|
117
|
+
await rm(dir, { recursive: true, force: true });
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
});
|