@pi-harness/pi-harness 0.1.30 → 0.1.31
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/apps/web/dist/assets/index-D9ZcvGXi.js +25 -0
- package/apps/web/dist/assets/{index-xF647pQJ.css → index-DJsNIiIo.css} +1 -1
- package/apps/web/dist/index.html +2 -2
- package/apps/web/package.json +1 -1
- package/apps/web/profile/cordis.yml +0 -221
- package/apps/web/src/style.css +3 -0
- package/node_modules/@pi-harness/api-gateway/dist/index.d.ts.map +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/index.js +42 -11
- package/node_modules/@pi-harness/api-gateway/dist/index.js.map +1 -1
- package/node_modules/@pi-harness/api-gateway/package.json +1 -27
- package/node_modules/@pi-harness/cli/package.json +1 -1
- package/node_modules/@pi-harness/cli/profiles/default/cordis.yml +0 -87
- package/node_modules/@pi-harness/host-webserver/package.json +1 -1
- package/node_modules/@pi-harness/web-app/package.json +1 -1
- package/package.json +6 -79
- package/packages/api-gateway/dist/index.d.ts.map +1 -1
- package/packages/api-gateway/dist/index.js +42 -11
- package/packages/api-gateway/dist/index.js.map +1 -1
- package/packages/api-gateway/package.json +1 -27
- package/packages/api-gateway/src/index.ts +54 -16
- package/packages/api-gateway/test/index.test.ts +157 -2
- package/packages/api-gateway/test/packed-package.test.ts +18 -6
- package/packages/api-gateway/test/release-contract.test.ts +8 -4
- package/packages/bundle-web-app/package.json +1 -1
- package/packages/cli/package.json +1 -1
- package/packages/cli/profiles/default/cordis.yml +0 -87
- package/packages/cli/test/profiles/plugins/cordis.yml +122 -0
- package/packages/cli/test/profiles.integration.test.ts +40 -23
- package/packages/host-webserver/package.json +1 -1
- package/apps/web/dist/assets/index-B7pwZ76Z.js +0 -25
|
@@ -10,7 +10,7 @@ import timerPlugin from "@deepseek-ai/cordis-plugin-timer";
|
|
|
10
10
|
import { SessionManager } from "@earendil-works/pi-coding-agent";
|
|
11
11
|
import { afterEach, describe, expect, test, vi } from "vitest";
|
|
12
12
|
import webServerPlugin from "@pi-harness/host-webserver";
|
|
13
|
-
import { PiPluginUiRegistry, PiToolRegistry } from "@pi-harness/core";
|
|
13
|
+
import { PiPluginUiRegistry, PiToolRegistry, PiToolRegistryLeasedError } from "@pi-harness/core";
|
|
14
14
|
import type { MarketplacePlugin } from "../src/marketplace.js";
|
|
15
15
|
import apiPlugin from "../src/index.js";
|
|
16
16
|
import type * as FsPromises from "node:fs/promises";
|
|
@@ -2190,12 +2190,167 @@ describe("API gateway plugin", () => {
|
|
|
2190
2190
|
}
|
|
2191
2191
|
|
|
2192
2192
|
expect(response.status).toBe(200);
|
|
2193
|
-
await expect(response.json()).resolves.toMatchObject({ installed: true, plugin: { id: "skill-guard" } });
|
|
2193
|
+
await expect(response.json()).resolves.toMatchObject({ installed: true, restartRequired: false, plugin: { id: "skill-guard" } });
|
|
2194
2194
|
expect((await readFile(npmLog, "utf8")).trim()).toMatch(/^install --save-exact --package-lock=false @pi-harness\/plugin-skill-guard@\d/u);
|
|
2195
2195
|
expect(created).toEqual([expect.objectContaining({ name: "@pi-harness/plugin-skill-guard" })]);
|
|
2196
2196
|
await expect(readFile(configPath, "utf8")).resolves.toContain('name: "@pi-harness/plugin-skill-guard"');
|
|
2197
2197
|
});
|
|
2198
2198
|
|
|
2199
|
+
// A plugin registers its tools while it activates, and pi-runtime takes the tool registry when it activates, so an installed entry only works if it sits ahead of the runtime inside the runtime's own group.
|
|
2200
|
+
test("installs a marketplace plugin into the runtime's group ahead of the runtime", async () => {
|
|
2201
|
+
const directory = await mkdtemp(join(tmpdir(), "pi-harness-api-plugin-placement-"));
|
|
2202
|
+
temporaryDirectories.push(directory);
|
|
2203
|
+
const shimDirectory = join(directory, "bin");
|
|
2204
|
+
await mkdir(shimDirectory);
|
|
2205
|
+
await writeFile(join(directory, "package.json"), '{ "name": "harness" }\n', "utf8");
|
|
2206
|
+
const configPath = join(directory, "profile.yml");
|
|
2207
|
+
await writeFile(
|
|
2208
|
+
configPath,
|
|
2209
|
+
'- id: agent\n name: cordis:group\n group: true\n config:\n - id: tools\n name: "@pi-harness/core/plugins/tools"\n config: {}\n - id: runtime\n name: "@pi-harness/core/plugins/runtime"\n config: {}\n',
|
|
2210
|
+
"utf8",
|
|
2211
|
+
);
|
|
2212
|
+
await writeFile(join(shimDirectory, "npm"), "#!/bin/sh\nexit 0\n", { mode: 0o755 });
|
|
2213
|
+
const context = new Context();
|
|
2214
|
+
contexts.push(context);
|
|
2215
|
+
await context.plugin(webServerPlugin, { host: "127.0.0.1", port: 0 });
|
|
2216
|
+
const session = { sessionId: "marketplace-placement-session", sessionFile: undefined, messages: [], isStreaming: false, subscribe: () => () => {} };
|
|
2217
|
+
context.provide("piRuntime", { session, prompt: () => Promise.resolve(), abort: () => Promise.resolve(), dispose: () => Promise.resolve() } as never);
|
|
2218
|
+
context.provide("piModels", { model: { provider: "test", id: "model" } } as never);
|
|
2219
|
+
context.provide("piHarnessLaunch", { cwd: directory, agentDir: directory, configPath, args: [], requestExit() {} });
|
|
2220
|
+
const runtimeOptions = { id: "runtime", name: "@pi-harness/core/plugins/runtime", config: {} };
|
|
2221
|
+
const runtimeGroup = { data: [{ id: "tools", name: "@pi-harness/core/plugins/tools", config: {} }, runtimeOptions] };
|
|
2222
|
+
const loaderEntries = [
|
|
2223
|
+
// The loader addresses a nested group by its qualified id, while the profile file names the same entry by its own id.
|
|
2224
|
+
{ id: "profile:agent", options: { id: "agent", name: "cordis:group" }, subgroup: runtimeGroup },
|
|
2225
|
+
{ id: "profile:runtime", options: runtimeOptions, parent: runtimeGroup },
|
|
2226
|
+
];
|
|
2227
|
+
const created: Array<[unknown, unknown, unknown]> = [];
|
|
2228
|
+
context.reflect.provide("loader", {
|
|
2229
|
+
entries: () => loaderEntries,
|
|
2230
|
+
create: (options: unknown, parent: unknown, position: unknown) => {
|
|
2231
|
+
created.push([options, parent, position]);
|
|
2232
|
+
return Promise.resolve("marketplace-entry");
|
|
2233
|
+
},
|
|
2234
|
+
resolve: () => ({ fiber: { await: () => Promise.resolve() } }),
|
|
2235
|
+
remove: () => Promise.resolve(),
|
|
2236
|
+
});
|
|
2237
|
+
await context.plugin(apiPlugin);
|
|
2238
|
+
|
|
2239
|
+
const originalPath = process.env.PATH ?? "";
|
|
2240
|
+
process.env.PATH = `${shimDirectory}:${originalPath}`;
|
|
2241
|
+
let response: Response;
|
|
2242
|
+
try {
|
|
2243
|
+
response = await fetch(context.webServer.url + "/api/marketplace/install", {
|
|
2244
|
+
method: "POST",
|
|
2245
|
+
headers: { "content-type": "application/json" },
|
|
2246
|
+
body: JSON.stringify({ id: "skill-guard" }),
|
|
2247
|
+
});
|
|
2248
|
+
} finally {
|
|
2249
|
+
process.env.PATH = originalPath;
|
|
2250
|
+
}
|
|
2251
|
+
|
|
2252
|
+
expect(response.status).toBe(200);
|
|
2253
|
+
await expect(response.json()).resolves.toMatchObject({ installed: true, restartRequired: false });
|
|
2254
|
+
expect(created).toEqual([[expect.objectContaining({ id: "marketplace-skill-guard", name: "@pi-harness/plugin-skill-guard" }), "profile:agent", 1]]);
|
|
2255
|
+
await expect(readFile(configPath, "utf8")).resolves.toBe(
|
|
2256
|
+
'- id: agent\n name: cordis:group\n group: true\n config:\n - id: tools\n name: "@pi-harness/core/plugins/tools"\n config: {}\n - id: marketplace-skill-guard\n name: "@pi-harness/plugin-skill-guard"\n config: {}\n - id: runtime\n name: "@pi-harness/core/plugins/runtime"\n config: {}\n',
|
|
2257
|
+
);
|
|
2258
|
+
});
|
|
2259
|
+
|
|
2260
|
+
// The runtime snapshots the tool set when it takes the registry, so a plugin that contributes tools cannot join a harness that is already running. Rolling the install back would leave the user unable to install it at all, so the package and the profile row stay and the console asks for a restart.
|
|
2261
|
+
test("keeps an installed plugin in place when the runtime already leased the tool registry", async () => {
|
|
2262
|
+
const directory = await mkdtemp(join(tmpdir(), "pi-harness-api-plugin-leased-"));
|
|
2263
|
+
temporaryDirectories.push(directory);
|
|
2264
|
+
const shimDirectory = join(directory, "bin");
|
|
2265
|
+
await mkdir(shimDirectory);
|
|
2266
|
+
await writeFile(join(directory, "package.json"), '{ "name": "harness", "dependencies": {} }\n', "utf8");
|
|
2267
|
+
const configPath = join(directory, "profile.yml");
|
|
2268
|
+
await writeFile(configPath, '- id: runtime\n name: "@pi-harness/core/plugins/runtime"\n config: {}\n', "utf8");
|
|
2269
|
+
await writeFile(join(shimDirectory, "npm"), "#!/bin/sh\nexit 0\n", { mode: 0o755 });
|
|
2270
|
+
const context = new Context();
|
|
2271
|
+
contexts.push(context);
|
|
2272
|
+
await context.plugin(webServerPlugin, { host: "127.0.0.1", port: 0 });
|
|
2273
|
+
const session = { sessionId: "marketplace-leased-session", sessionFile: undefined, messages: [], isStreaming: false, subscribe: () => () => {} };
|
|
2274
|
+
context.provide("piRuntime", { session, prompt: () => Promise.resolve(), abort: () => Promise.resolve(), dispose: () => Promise.resolve() } as never);
|
|
2275
|
+
context.provide("piModels", { model: { provider: "test", id: "model" } } as never);
|
|
2276
|
+
context.provide("piHarnessLaunch", { cwd: directory, agentDir: directory, configPath, args: [], requestExit() {} });
|
|
2277
|
+
const removed: string[] = [];
|
|
2278
|
+
context.reflect.provide("loader", {
|
|
2279
|
+
entries: () => [],
|
|
2280
|
+
create: () => Promise.resolve("marketplace-entry"),
|
|
2281
|
+
// The loader wraps an activation failure in its own error chain, which is what the gateway has to see through.
|
|
2282
|
+
resolve: () => ({
|
|
2283
|
+
fiber: {
|
|
2284
|
+
await: () => Promise.reject(new Error("failed to apply loader entry", { cause: new PiToolRegistryLeasedError("skill_scan") })),
|
|
2285
|
+
},
|
|
2286
|
+
}),
|
|
2287
|
+
remove: (id: string) => {
|
|
2288
|
+
removed.push(id);
|
|
2289
|
+
return Promise.resolve();
|
|
2290
|
+
},
|
|
2291
|
+
});
|
|
2292
|
+
await context.plugin(apiPlugin);
|
|
2293
|
+
|
|
2294
|
+
const originalPath = process.env.PATH ?? "";
|
|
2295
|
+
process.env.PATH = `${shimDirectory}:${originalPath}`;
|
|
2296
|
+
let response: Response;
|
|
2297
|
+
try {
|
|
2298
|
+
response = await fetch(context.webServer.url + "/api/marketplace/install", {
|
|
2299
|
+
method: "POST",
|
|
2300
|
+
headers: { "content-type": "application/json" },
|
|
2301
|
+
body: JSON.stringify({ id: "skill-guard" }),
|
|
2302
|
+
});
|
|
2303
|
+
} finally {
|
|
2304
|
+
process.env.PATH = originalPath;
|
|
2305
|
+
}
|
|
2306
|
+
|
|
2307
|
+
expect(response.status).toBe(200);
|
|
2308
|
+
await expect(response.json()).resolves.toMatchObject({ installed: true, restartRequired: true, plugin: { id: "skill-guard" } });
|
|
2309
|
+
expect(removed).toEqual(["marketplace-entry"]);
|
|
2310
|
+
await expect(readFile(configPath, "utf8")).resolves.toContain('name: "@pi-harness/plugin-skill-guard"');
|
|
2311
|
+
await expect(readFile(join(directory, "package.json"), "utf8")).resolves.toBe('{ "name": "harness", "dependencies": {} }\n');
|
|
2312
|
+
});
|
|
2313
|
+
|
|
2314
|
+
// Reverting the profile here would leave a plugin that can be switched off but never on again, so the change stays and the plugin comes back on the next start.
|
|
2315
|
+
test("keeps a re-enabled plugin enabled in the profile when the runtime already leased the tool registry", async () => {
|
|
2316
|
+
const context = new Context();
|
|
2317
|
+
contexts.push(context);
|
|
2318
|
+
const directory = await mkdtemp(join(tmpdir(), "pi-harness-api-plugin-toggle-leased-"));
|
|
2319
|
+
temporaryDirectories.push(directory);
|
|
2320
|
+
const configPath = join(directory, "profile.yml");
|
|
2321
|
+
const loaderEntry = {
|
|
2322
|
+
id: "profile:marketplace-skill-guard",
|
|
2323
|
+
options: { id: "marketplace-skill-guard", name: "@pi-harness/plugin-skill-guard", config: {} },
|
|
2324
|
+
update: () => Promise.reject(new Error("failed to apply loader entry", { cause: new PiToolRegistryLeasedError("skill_scan") })),
|
|
2325
|
+
};
|
|
2326
|
+
await writeFile(
|
|
2327
|
+
configPath,
|
|
2328
|
+
'- id: marketplace-skill-guard\n name: "@pi-harness/plugin-skill-guard"\n disabled: true\n config: {}\n- id: runtime\n name: "@pi-harness/core/plugins/runtime"\n config: {}\n',
|
|
2329
|
+
"utf8",
|
|
2330
|
+
);
|
|
2331
|
+
await context.plugin(webServerPlugin, { host: "127.0.0.1", port: 0 });
|
|
2332
|
+
const session = { sessionId: "plugin-toggle-leased-session", sessionFile: undefined, messages: [], isStreaming: false, subscribe: () => () => {} };
|
|
2333
|
+
context.provide("piRuntime", { session, prompt: () => Promise.resolve(), abort: () => Promise.resolve(), dispose: () => Promise.resolve() } as never);
|
|
2334
|
+
context.provide("piModels", { model: { provider: "test", id: "model" }, runtime: { getModels: () => [], getModel: () => undefined } } as never);
|
|
2335
|
+
context.provide("piHarnessLaunch", { cwd: directory, agentDir: directory, configPath, args: [], requestExit() {} });
|
|
2336
|
+
context.reflect.provide("loader", {
|
|
2337
|
+
*entries() {
|
|
2338
|
+
yield loaderEntry;
|
|
2339
|
+
},
|
|
2340
|
+
});
|
|
2341
|
+
await context.plugin(apiPlugin);
|
|
2342
|
+
|
|
2343
|
+
const response = await fetch(context.webServer.url + "/api/plugins/toggle", {
|
|
2344
|
+
method: "POST",
|
|
2345
|
+
headers: { "content-type": "application/json" },
|
|
2346
|
+
body: JSON.stringify({ id: "marketplace-skill-guard", enabled: true }),
|
|
2347
|
+
});
|
|
2348
|
+
|
|
2349
|
+
expect(response.status).toBe(200);
|
|
2350
|
+
await expect(response.json()).resolves.toMatchObject({ restartRequired: true, plugin: { id: "marketplace-skill-guard" } });
|
|
2351
|
+
await expect(readFile(configPath, "utf8")).resolves.not.toContain("disabled: true");
|
|
2352
|
+
});
|
|
2353
|
+
|
|
2199
2354
|
test("locks marketplace installation before reading a request body", async () => {
|
|
2200
2355
|
const cwd = await mkdtemp(join(tmpdir(), "pi-harness-api-plugin-install-lock-"));
|
|
2201
2356
|
temporaryDirectories.push(cwd);
|
|
@@ -73,10 +73,13 @@ const bin = { "pi-harness": "./apps/web/server-dist/bin.js", pih: "./packages/cl
|
|
|
73
73
|
write(join(harnessRoot, "package.json"), JSON.stringify({ name: "@pi-harness/pi-harness", version: "9.9.9", bin, dependencies: { "@earendil-works/pi-coding-agent": "0.84.4" } }));
|
|
74
74
|
write(join(agentRoot, "package.json"), JSON.stringify({ name: "@earendil-works/pi-coding-agent", version: "0.84.4", dependencies: {} }));
|
|
75
75
|
write(join(agentRoot, "dist", "cli", "args.js"), "");
|
|
76
|
-
|
|
76
|
+
const profileEntries = ["- id: webserver\\n name: \\"@pi-harness/host-webserver\\"\\n"];
|
|
77
|
+
if (process.env.PACKED_PACKAGE_FIXTURE_BUNDLED_PLUGIN === "1") profileEntries.push("- id: modlens\\n name: \\"@pi-harness/plugin-modlens\\"\\n");
|
|
78
|
+
write(join(harnessRoot, "apps", "web", "profile", "cordis.yml"), profileEntries.join(""));
|
|
77
79
|
const shippedDependencies = process.env.PACKED_PACKAGE_FIXTURE_SHIPPED_DEP === "1" ? { "@pi-harness/client-web": "9.9.9" } : undefined;
|
|
78
80
|
write(join(harnessRoot, "apps", "web", "package.json"), JSON.stringify({ name: "@pi-harness/web", private: true, dependencies: shippedDependencies }));
|
|
79
|
-
if (process.env.
|
|
81
|
+
if (process.env.PACKED_PACKAGE_FIXTURE_PROFILE_ENTRY !== "0") write(join(harnessRoot, "node_modules", "@pi-harness", "host-webserver", "dist", "index.js"), "export default {};\\n");
|
|
82
|
+
if (process.env.PACKED_PACKAGE_FIXTURE_BUNDLED_PLUGIN === "1") write(join(harnessRoot, "node_modules", "@pi-harness", "plugin-modlens", "dist", "index.js"), "export default {};\\n");
|
|
80
83
|
write(join(harnessRoot, "node_modules", "@pi-harness", "core", "dist", "index.js"), process.env.PACKED_PACKAGE_FIXTURE_CORE);
|
|
81
84
|
write(join(harnessRoot, "node_modules", "@pi-harness", "core", "dist", "plugin-resolve.js"), process.env.PACKED_PACKAGE_FIXTURE_RESOLVE);
|
|
82
85
|
if (process.env.PACKED_PACKAGE_FIXTURE_BINS === "1") for (const target of Object.values(bin)) write(join(harnessRoot, target), "");
|
|
@@ -94,8 +97,9 @@ const runSmokeTest = async (
|
|
|
94
97
|
fakeNpm: string,
|
|
95
98
|
bins: boolean,
|
|
96
99
|
marketplace = true,
|
|
97
|
-
|
|
100
|
+
profileEntry = true,
|
|
98
101
|
shippedDependency = false,
|
|
102
|
+
bundledPlugin = false,
|
|
99
103
|
): ReturnType<typeof execFileAsync> =>
|
|
100
104
|
execFileAsync(process.execPath, [resolve(repositoryRoot, "scripts/test-packed-package.mjs")], {
|
|
101
105
|
env: {
|
|
@@ -105,8 +109,9 @@ const runSmokeTest = async (
|
|
|
105
109
|
PACKED_PACKAGE_FIXTURE_CORE: fakeCoreSource,
|
|
106
110
|
PACKED_PACKAGE_FIXTURE_RESOLVE: fakeResolveSource,
|
|
107
111
|
PACKED_PACKAGE_FIXTURE_MARKETPLACE: marketplace ? "1" : "0",
|
|
108
|
-
|
|
112
|
+
PACKED_PACKAGE_FIXTURE_PROFILE_ENTRY: profileEntry ? "1" : "0",
|
|
109
113
|
PACKED_PACKAGE_FIXTURE_SHIPPED_DEP: shippedDependency ? "1" : "0",
|
|
114
|
+
PACKED_PACKAGE_FIXTURE_BUNDLED_PLUGIN: bundledPlugin ? "1" : "0",
|
|
110
115
|
},
|
|
111
116
|
});
|
|
112
117
|
|
|
@@ -130,11 +135,18 @@ describe("packed package smoke test", () => {
|
|
|
130
135
|
await expect(runSmokeTest(fakeNpm, false)).rejects.toThrow(/missing the pi-harness entrypoint \.\/apps\/web\/server-dist\/bin\.js/u);
|
|
131
136
|
});
|
|
132
137
|
|
|
133
|
-
it("rejects an install whose shipped profile enables
|
|
138
|
+
it("rejects an install whose shipped profile enables an entry the tarball never brings in", async () => {
|
|
134
139
|
// The 0.1.29 launcher shipped a profile naming 45 packages it did not depend on, so a clean install failed to boot on the first entry the loader could not import.
|
|
135
140
|
const fakeNpm = await createFakeNpm();
|
|
136
141
|
|
|
137
|
-
await expect(runSmokeTest(fakeNpm, true, true, false)).rejects.toThrow(/enables 1
|
|
142
|
+
await expect(runSmokeTest(fakeNpm, true, true, false)).rejects.toThrow(/enables 1 entr\(ies\) the install does not contain: @pi-harness\/host-webserver/u);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it("rejects an install whose shipped profile bundles a pluggable plugin", async () => {
|
|
146
|
+
// Official plugins are installed from the plugin center like community ones, so a profile entry for one would hand every fresh install a plugin the user never asked for.
|
|
147
|
+
const fakeNpm = await createFakeNpm();
|
|
148
|
+
|
|
149
|
+
await expect(runSmokeTest(fakeNpm, true, true, true, false, true)).rejects.toThrow(/enables 1 pluggable plugin\(s\): @pi-harness\/plugin-modlens/u);
|
|
138
150
|
});
|
|
139
151
|
|
|
140
152
|
it("rejects an install whose shipped workspace depends on a package npm can never fetch", async () => {
|
|
@@ -98,8 +98,8 @@ describe("release contract", () => {
|
|
|
98
98
|
expect(core.files).not.toContain("profiles");
|
|
99
99
|
});
|
|
100
100
|
|
|
101
|
-
test("the published launcher declares every
|
|
102
|
-
//
|
|
101
|
+
test("the published launcher declares every package its shipped profiles load, and no plugin", () => {
|
|
102
|
+
// A profile entry is imported by its bare specifier, so the package a user installs has to pull the infrastructure each entry names. Pluggable plugins are the exception by design: they are ordinary npm packages installed from the plugin center, so bundling one would put it in every install whether the user wanted it or not.
|
|
103
103
|
const root = readJson("package.json") as { name: string; dependencies: Record<string, string> };
|
|
104
104
|
const required = new Map<string, string>();
|
|
105
105
|
for (const file of sourceFiles(resolve(repositoryRoot, "packages/cli/profiles"), ".yml")) {
|
|
@@ -107,8 +107,12 @@ describe("release contract", () => {
|
|
|
107
107
|
required.set(packageNameOf(match[1] ?? ""), relative(repositoryRoot, file));
|
|
108
108
|
}
|
|
109
109
|
const external = [...required].filter(([name]) => !name.includes(":"));
|
|
110
|
-
expect(external.
|
|
111
|
-
for (const [name, file] of external)
|
|
110
|
+
expect(external.length).toBeGreaterThan(0);
|
|
111
|
+
for (const [name, file] of external) {
|
|
112
|
+
expect(name.startsWith("@pi-harness/plugin-"), `${name} (loaded by ${file}) is a pluggable plugin, which a shipped profile must not enable`).toBe(false);
|
|
113
|
+
expect(root.dependencies[name], `${name} (loaded by ${file}) is not declared by ${root.name}`).toBeDefined();
|
|
114
|
+
}
|
|
115
|
+
expect(Object.keys(root.dependencies).filter((name) => name.startsWith("@pi-harness/plugin-"))).toEqual([]);
|
|
112
116
|
});
|
|
113
117
|
|
|
114
118
|
test("the plugin API declares every package its sources import as a peer", () => {
|
|
@@ -26,97 +26,10 @@
|
|
|
26
26
|
- bash
|
|
27
27
|
- edit
|
|
28
28
|
- write
|
|
29
|
-
- id: agent-teams
|
|
30
|
-
name: "@pi-harness/plugin-agent-teams"
|
|
31
|
-
config: {}
|
|
32
|
-
- id: modlens
|
|
33
|
-
name: "@pi-harness/plugin-modlens"
|
|
34
|
-
config: {}
|
|
35
|
-
- id: vision-toolkit
|
|
36
|
-
name: "@pi-harness/plugin-vision-toolkit"
|
|
37
|
-
config: {}
|
|
38
|
-
- id: git-time-capsule
|
|
39
|
-
name: "@pi-harness/plugin-git-time-capsule"
|
|
40
|
-
config: {}
|
|
41
|
-
- id: dependency-checker
|
|
42
|
-
name: "@pi-harness/plugin-dependency-checker"
|
|
43
|
-
config: {}
|
|
44
|
-
- id: at-file
|
|
45
|
-
name: "@pi-harness/plugin-at-file"
|
|
46
|
-
config: {}
|
|
47
|
-
- id: test-harness
|
|
48
|
-
name: "@pi-harness/plugin-test-harness"
|
|
49
|
-
config:
|
|
50
|
-
timeoutMs: 120000
|
|
51
|
-
- id: session-insights
|
|
52
|
-
name: "@pi-harness/plugin-session-insights"
|
|
53
|
-
config: {}
|
|
54
|
-
- id: readme-gen
|
|
55
|
-
name: "@pi-harness/plugin-readme-gen"
|
|
56
|
-
config: {}
|
|
57
|
-
- id: i18n-pair
|
|
58
|
-
name: "@pi-harness/plugin-i18n-pair"
|
|
59
|
-
config: {}
|
|
60
|
-
- id: cleaner
|
|
61
|
-
name: "@pi-harness/plugin-cleaner"
|
|
62
|
-
config: {}
|
|
63
|
-
- id: sql-lens
|
|
64
|
-
name: "@pi-harness/plugin-sql-lens"
|
|
65
|
-
config:
|
|
66
|
-
timeoutMs: 5000
|
|
67
|
-
- id: docker-sandbox
|
|
68
|
-
name: "@pi-harness/plugin-docker-sandbox"
|
|
69
|
-
config: {}
|
|
70
|
-
- id: mcp-client
|
|
71
|
-
name: "@pi-harness/plugin-mcp-client"
|
|
72
|
-
config: {}
|
|
73
|
-
- id: browser-fetch
|
|
74
|
-
name: "@pi-harness/plugin-browser-fetch"
|
|
75
|
-
config: {}
|
|
76
|
-
- id: browser-session
|
|
77
|
-
name: "@pi-harness/plugin-browser-session"
|
|
78
|
-
config: {}
|
|
79
|
-
- id: plugin-stars
|
|
80
|
-
name: "@pi-harness/plugin-plugin-stars"
|
|
81
|
-
config: {}
|
|
82
|
-
- id: yaml-validator
|
|
83
|
-
name: "@pi-harness/plugin-yaml-validator"
|
|
84
|
-
config: {}
|
|
85
|
-
- id: plugin-dev
|
|
86
|
-
name: "@pi-harness/plugin-plugin-dev"
|
|
87
|
-
config: {}
|
|
88
|
-
- id: openpets
|
|
89
|
-
name: "@pi-harness/plugin-openpets"
|
|
90
|
-
config: {}
|
|
91
|
-
- id: session-bridge
|
|
92
|
-
name: "@pi-harness/plugin-session-bridge"
|
|
93
|
-
config: {}
|
|
94
|
-
- id: skill-guard
|
|
95
|
-
name: "@pi-harness/plugin-skill-guard"
|
|
96
|
-
config: {}
|
|
97
|
-
- id: recall-unread
|
|
98
|
-
name: "@pi-harness/plugin-recall-unread"
|
|
99
|
-
config: {}
|
|
100
|
-
- id: turn-rewind
|
|
101
|
-
name: "@pi-harness/plugin-turn-rewind"
|
|
102
|
-
config: {}
|
|
103
|
-
- id: context-doctor
|
|
104
|
-
name: "@pi-harness/plugin-context-doctor"
|
|
105
|
-
config: {}
|
|
106
29
|
- id: runtime
|
|
107
30
|
name: "@pi-harness/core/plugins/runtime"
|
|
108
31
|
config:
|
|
109
32
|
thinkingLevel: medium
|
|
110
|
-
- id: context
|
|
111
|
-
name: "@pi-harness/plugin-context"
|
|
112
|
-
config: {}
|
|
113
|
-
- id: token-guard
|
|
114
|
-
name: "@pi-harness/plugin-token-guard"
|
|
115
|
-
config:
|
|
116
|
-
maxPercent: 90
|
|
117
|
-
- id: fail-logger
|
|
118
|
-
name: "@pi-harness/plugin-fail-logger"
|
|
119
|
-
config: {}
|
|
120
33
|
- id: stdio
|
|
121
34
|
name: "@pi-harness/core/plugins/stdio"
|
|
122
35
|
config: {}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
- id: agent
|
|
2
|
+
name: cordis:group
|
|
3
|
+
group: true
|
|
4
|
+
config:
|
|
5
|
+
- id: models
|
|
6
|
+
name: "@pi-harness/core/plugins/models"
|
|
7
|
+
config:
|
|
8
|
+
provider: deepseek
|
|
9
|
+
model: deepseek-v4-flash
|
|
10
|
+
refreshOnCreate: false
|
|
11
|
+
- id: resources
|
|
12
|
+
name: "@pi-harness/core/plugins/resources"
|
|
13
|
+
config: {}
|
|
14
|
+
- id: model
|
|
15
|
+
name: "@pi-harness/core/plugins/model"
|
|
16
|
+
config: {}
|
|
17
|
+
- id: session
|
|
18
|
+
name: "@pi-harness/core/plugins/session"
|
|
19
|
+
config:
|
|
20
|
+
storage: jsonl
|
|
21
|
+
- id: tools
|
|
22
|
+
name: "@pi-harness/core/plugins/tools"
|
|
23
|
+
config:
|
|
24
|
+
names:
|
|
25
|
+
- read
|
|
26
|
+
- bash
|
|
27
|
+
- edit
|
|
28
|
+
- write
|
|
29
|
+
- id: agent-teams
|
|
30
|
+
name: "@pi-harness/plugin-agent-teams"
|
|
31
|
+
config: {}
|
|
32
|
+
- id: modlens
|
|
33
|
+
name: "@pi-harness/plugin-modlens"
|
|
34
|
+
config: {}
|
|
35
|
+
- id: vision-toolkit
|
|
36
|
+
name: "@pi-harness/plugin-vision-toolkit"
|
|
37
|
+
config: {}
|
|
38
|
+
- id: git-time-capsule
|
|
39
|
+
name: "@pi-harness/plugin-git-time-capsule"
|
|
40
|
+
config: {}
|
|
41
|
+
- id: dependency-checker
|
|
42
|
+
name: "@pi-harness/plugin-dependency-checker"
|
|
43
|
+
config: {}
|
|
44
|
+
- id: at-file
|
|
45
|
+
name: "@pi-harness/plugin-at-file"
|
|
46
|
+
config: {}
|
|
47
|
+
- id: test-harness
|
|
48
|
+
name: "@pi-harness/plugin-test-harness"
|
|
49
|
+
config:
|
|
50
|
+
timeoutMs: 120000
|
|
51
|
+
- id: session-insights
|
|
52
|
+
name: "@pi-harness/plugin-session-insights"
|
|
53
|
+
config: {}
|
|
54
|
+
- id: readme-gen
|
|
55
|
+
name: "@pi-harness/plugin-readme-gen"
|
|
56
|
+
config: {}
|
|
57
|
+
- id: i18n-pair
|
|
58
|
+
name: "@pi-harness/plugin-i18n-pair"
|
|
59
|
+
config: {}
|
|
60
|
+
- id: cleaner
|
|
61
|
+
name: "@pi-harness/plugin-cleaner"
|
|
62
|
+
config: {}
|
|
63
|
+
- id: sql-lens
|
|
64
|
+
name: "@pi-harness/plugin-sql-lens"
|
|
65
|
+
config:
|
|
66
|
+
timeoutMs: 5000
|
|
67
|
+
- id: docker-sandbox
|
|
68
|
+
name: "@pi-harness/plugin-docker-sandbox"
|
|
69
|
+
config: {}
|
|
70
|
+
- id: mcp-client
|
|
71
|
+
name: "@pi-harness/plugin-mcp-client"
|
|
72
|
+
config: {}
|
|
73
|
+
- id: browser-fetch
|
|
74
|
+
name: "@pi-harness/plugin-browser-fetch"
|
|
75
|
+
config: {}
|
|
76
|
+
- id: browser-session
|
|
77
|
+
name: "@pi-harness/plugin-browser-session"
|
|
78
|
+
config: {}
|
|
79
|
+
- id: plugin-stars
|
|
80
|
+
name: "@pi-harness/plugin-plugin-stars"
|
|
81
|
+
config: {}
|
|
82
|
+
- id: yaml-validator
|
|
83
|
+
name: "@pi-harness/plugin-yaml-validator"
|
|
84
|
+
config: {}
|
|
85
|
+
- id: plugin-dev
|
|
86
|
+
name: "@pi-harness/plugin-plugin-dev"
|
|
87
|
+
config: {}
|
|
88
|
+
- id: openpets
|
|
89
|
+
name: "@pi-harness/plugin-openpets"
|
|
90
|
+
config: {}
|
|
91
|
+
- id: session-bridge
|
|
92
|
+
name: "@pi-harness/plugin-session-bridge"
|
|
93
|
+
config: {}
|
|
94
|
+
- id: skill-guard
|
|
95
|
+
name: "@pi-harness/plugin-skill-guard"
|
|
96
|
+
config: {}
|
|
97
|
+
- id: recall-unread
|
|
98
|
+
name: "@pi-harness/plugin-recall-unread"
|
|
99
|
+
config: {}
|
|
100
|
+
- id: turn-rewind
|
|
101
|
+
name: "@pi-harness/plugin-turn-rewind"
|
|
102
|
+
config: {}
|
|
103
|
+
- id: context-doctor
|
|
104
|
+
name: "@pi-harness/plugin-context-doctor"
|
|
105
|
+
config: {}
|
|
106
|
+
- id: runtime
|
|
107
|
+
name: "@pi-harness/core/plugins/runtime"
|
|
108
|
+
config:
|
|
109
|
+
thinkingLevel: medium
|
|
110
|
+
- id: context
|
|
111
|
+
name: "@pi-harness/plugin-context"
|
|
112
|
+
config: {}
|
|
113
|
+
- id: token-guard
|
|
114
|
+
name: "@pi-harness/plugin-token-guard"
|
|
115
|
+
config:
|
|
116
|
+
maxPercent: 90
|
|
117
|
+
- id: fail-logger
|
|
118
|
+
name: "@pi-harness/plugin-fail-logger"
|
|
119
|
+
config: {}
|
|
120
|
+
- id: stdio
|
|
121
|
+
name: "@pi-harness/core/plugins/stdio"
|
|
122
|
+
config: {}
|