feat(plugins): tidy the unreleased 1.3 API before it freezes (#2079)

- workspace.symbols becomes the project method project.symbols(query, limit), like find-files; the Rust SDK's Project::symbols is unchanged, components built against the previous main need a rebuild.
- The open-log-tab web message names its arguments args, as pty-spawn and the services process-log do; baseArgs is refused with log-error rather than ignored.
- Document the hover, semantic-tokens and diagnostics provider exports and their worlds.
Цей коміт міститься в:
l0ng-ai authored and GitHub committed 2026-11-08 09:43:29 +08:00
1 джерело 32a778d28d
коміт ec171c3188
86 files changed
+177 -111

No files matched your search

+7 -6
Переглянути файл
@@ -19,7 +19,7 @@ const plugin = { id: "probe", enabled: true, capabilities: [{ id: "process:bash"
const spec = {
program: "bash",
title: "Probe logs",
baseArgs: ["--noprofile", "--norc"],
args: ["--noprofile", "--norc"],
toggles: [{ id: "verbose", label: "Verbose", args: ["-v"] }],
};
let stopActions: () => void;
@@ -76,9 +76,10 @@ describe("plugin streaming log tabs", () => {
{ program: "sh" },
{ program: "/bin/bash" },
{ program: " bash" },
{ baseArgs: [123] },
{ baseArgs: ["\0"] },
{ baseArgs: ["x".repeat(4097)] },
{ args: [123] },
{ args: ["\0"] },
{ args: ["x".repeat(4097)] },
{ baseArgs: ["--verbose"] },
{ cwd: "/" },
{ env: { PATH: "/tmp" } },
{ title: "x".repeat(257) },
@@ -102,7 +103,7 @@ describe("plugin streaming log tabs", () => {
const f = setup();
expect(webPtyBackend).toHaveBeenCalledWith("probe", "logs");
expect(f.backend.spawn).toHaveBeenCalledWith(
expect.objectContaining({ program: "bash", env: {}, args: spec.baseArgs }),
expect.objectContaining({ program: "bash", env: {}, args: spec.args }),
expect.any(Function),
);
expect(f.view().state.facet(EditorState.readOnly)).toBe(true);
@@ -128,7 +129,7 @@ describe("plugin streaming log tabs", () => {
fireEvent.click(screen.getByRole("switch", { name: "Verbose" }));
await waitFor(() => expect(f.backend.kill).toHaveBeenCalledWith(42));
expect(f.backend.spawn).toHaveBeenLastCalledWith(
expect.objectContaining({ args: [...spec.baseArgs, "-v"] }),
expect.objectContaining({ args: [...spec.args, "-v"] }),
expect.any(Function),
);
vi.mocked(f.backend.kill).mockClear();
+2 -2
Переглянути файл
@@ -277,7 +277,7 @@ describe("WebView", () => {
title: "App Logs",
key: "shared",
program: "tail",
baseArgs: ["-f", "app.log"],
args: ["-f", "app.log"],
toggles: [{ id: "verbose", label: "Verbose", args: ["-v"] }],
};
@@ -308,7 +308,7 @@ describe("WebView", () => {
pluginId: "acme.draw",
title: "App Logs",
program: "tail",
baseArgs: ["-f", "app.log"],
args: ["-f", "app.log"],
toggles: [{ id: "verbose", label: "Verbose", args: ["-v"] }],
},
});
+1 -1
Переглянути файл
@@ -395,7 +395,7 @@ function PluginLogEditorBody({ input, data }: { input: EditorInput; data: Plugin
op: "pty-spawn",
sessionId: "log",
program: data.program,
args: [...data.baseArgs, ...data.toggles.filter((t) => on.has(t.id)).flatMap((t) => t.args)],
args: [...data.args, ...data.toggles.filter((t) => on.has(t.id)).flatMap((t) => t.args)],
cols: 240,
rows: 40,
}),
+7 -6
Переглянути файл
@@ -13,7 +13,7 @@ export interface PluginLogEditorData {
key?: string;
title: string;
program: string;
baseArgs: string[];
args: string[];
toggles: PluginLogToggleSpec[];
}
@@ -25,27 +25,28 @@ const args = (v: unknown): v is string[] =>
/** Reject malformed requests instead of silently running a different command. */
export function logTabData(pluginId: string, view: string, value: object, length: number): PluginLogEditorData {
const m = value as Record<string, unknown>;
const { program, baseArgs = [], toggles = [], key, title = program } = m;
const { program, args: command = [], toggles = [], key, title = program } = m;
if (
length > 2 << 20 ||
!text(program, 256) ||
program.trim() !== program ||
!text(title, 256) ||
(key !== undefined && !text(key, 128)) ||
!args(baseArgs) ||
!args(command) ||
!Array.isArray(toggles) ||
toggles.length > 32 ||
toggles.some((t) => !t || !text(t.id, 64) || !text(t.label, 128) || !args(t.args)) ||
new Set(toggles.map((t) => t.id)).size !== toggles.length ||
baseArgs.length + toggles.reduce((n, t) => n + t.args.length, 0) > 256 ||
command.length + toggles.reduce((n, t) => n + t.args.length, 0) > 256 ||
"cwd" in m ||
"env" in m
"env" in m ||
"baseArgs" in m
)
throw new Error(tx("Invalid log stream configuration"));
const plugin = usePlugins.getState().plugins.find((p) => p.id === pluginId);
if (!plugin?.enabled || !plugin.capabilities.some((c) => c.id === `process:${program}` && c.granted))
throw new Error(tx`Cannot start PTY: process:${program} capability not granted.`);
return { pluginId, view, key: key as string | undefined, title, program, baseArgs, toggles };
return { pluginId, view, key: key as string | undefined, title, program, args: command, toggles };
}
/** Bound both line count and text size, including output with no newlines. */