feat(project): add project trust and restricted execution mode (#517)

Projects now open with a user-owned trust decision. The prompt offers Preview in Restricted Mode, Don't Open and Trust Project, with an optional trusted parent folder; preview is the default focus so Enter never grants trust.

Restricted projects keep browsing, editing, saving, search and highlighting. Project-controlled execution is denied by default in the backend: run, debug, build, tools, language servers, plugins and Git commands that could honour repository configuration. Trusting a project enables those paths without reopening it, and decisions can be revoked in Settings.

Terminals stay available when the user opens one, without project shell settings, environment or virtualenv activation. A shell prompt that runs git in the folder can still honour the repository's core.fsmonitor.

Fixes #462
Цей коміт міститься в:
l0ng-ai authored and GitHub committed 2026-10-31 20:58:45 +08:00
1 джерело cf51b57924
коміт 2c9a6470ff
103 files changed
+1996 -168

No files matched your search

+3 -1
Переглянути файл
@@ -5,6 +5,7 @@
* which applies them to every session (adapter defaults for filters without a choice).
*/
import { create } from "zustand";
import { useProject } from "@/platform/project";
import { call, commands, isTauri, type ExceptionFilter } from "@/platform/ipc";
interface ExceptionState {
@@ -128,7 +129,8 @@ export function mergeKnown(
}
async function push() {
if (!isTauri()) return;
if (!isTauri() || !useExceptionBreakpoints.getState().root || useProject.getState().project?.trusted === false)
return;
try {
await call(commands.debugSetExceptionBreakpoints(useExceptionBreakpoints.getState().choices));
} catch (e) {
+2 -1
Переглянути файл
@@ -5,6 +5,7 @@
* them. Entries remember the source line they were created on for the gutter icon.
*/
import { create } from "zustand";
import { useProject } from "@/platform/project";
import { call, commands, isTauri, type DataBreakpoint, type FunctionBreakpoint } from "@/platform/ipc";
import { newBreakpointId, type BreakpointStatus } from "./model";
import { useBreakpoints } from "./store";
@@ -121,7 +122,7 @@ function scheduleSync() {
}
export async function syncSpecial() {
if (!isTauri()) return;
if (!isTauri() || !useSpecialBreakpoints.getState().root || useProject.getState().project?.trusted === false) return;
const { methods, fields } = useSpecialBreakpoints.getState();
const muted = useBreakpoints.getState().muted;
try {
+2 -1
Переглянути файл
@@ -4,6 +4,7 @@
* The backend keeps them across sessions and returns verification states.
*/
import { create } from "zustand";
import { useProject } from "@/platform/project";
import { call, commands, isTauri } from "@/platform/ipc";
import {
makeBreakpoint,
@@ -132,7 +133,7 @@ export function syncFile(path: string): Promise<void> {
}
async function sendFile(path: string) {
if (!isTauri()) return;
if (!isTauri() || useProject.getState().project?.trusted === false) return;
const { byFile, muted } = useBreakpoints.getState();
const list = byFile[path] ?? [];
sent.set(