@4onstudios/iris-agent 0.1.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/LICENSE +21 -0
- package/README.md +690 -0
- package/dist/api/acp/acpServer.d.ts +23 -0
- package/dist/api/acp/acpServer.js +814 -0
- package/dist/api/acp/index.d.ts +2 -0
- package/dist/api/acp/index.js +2 -0
- package/dist/api/acp/irisClient.d.ts +58 -0
- package/dist/api/acp/irisClient.js +214 -0
- package/dist/api/agent.d.ts +14 -0
- package/dist/api/agent.js +4171 -0
- package/dist/api/backendService.d.ts +4 -0
- package/dist/api/backendService.js +14 -0
- package/dist/api/core/agent/host/AgentContract.d.ts +82 -0
- package/dist/api/core/agent/host/AgentContract.js +1 -0
- package/dist/api/core/agent/host/AgentRegistry.d.ts +24 -0
- package/dist/api/core/agent/host/AgentRegistry.js +125 -0
- package/dist/api/core/agent/host/defaultRegistry.d.ts +15 -0
- package/dist/api/core/agent/host/defaultRegistry.js +46 -0
- package/dist/api/core/agent/host/externalAgentLifecycle.d.ts +65 -0
- package/dist/api/core/agent/host/externalAgentLifecycle.js +117 -0
- package/dist/api/core/agent/host/hostSessionManager.d.ts +111 -0
- package/dist/api/core/agent/host/hostSessionManager.js +352 -0
- package/dist/api/core/agent/host/index.d.ts +5 -0
- package/dist/api/core/agent/host/index.js +5 -0
- package/dist/api/core/agent/index.d.ts +87 -0
- package/dist/api/core/agent/index.js +1024 -0
- package/dist/api/core/agent/publicContracts.d.ts +35 -0
- package/dist/api/core/agent/publicContracts.js +103 -0
- package/dist/api/core/agent/tools/applyDiff.d.ts +51 -0
- package/dist/api/core/agent/tools/applyDiff.js +131 -0
- package/dist/api/core/agent/tools/backgroundTasks.d.ts +240 -0
- package/dist/api/core/agent/tools/backgroundTasks.js +313 -0
- package/dist/api/core/agent/tools/createDirectory.d.ts +43 -0
- package/dist/api/core/agent/tools/createDirectory.js +77 -0
- package/dist/api/core/agent/tools/deleteFile.d.ts +37 -0
- package/dist/api/core/agent/tools/deleteFile.js +66 -0
- package/dist/api/core/agent/tools/editFile.d.ts +73 -0
- package/dist/api/core/agent/tools/editFile.js +173 -0
- package/dist/api/core/agent/tools/executeCommand.d.ts +59 -0
- package/dist/api/core/agent/tools/executeCommand.js +250 -0
- package/dist/api/core/agent/tools/fileContent.d.ts +54 -0
- package/dist/api/core/agent/tools/fileContent.js +227 -0
- package/dist/api/core/agent/tools/findDefinition.d.ts +72 -0
- package/dist/api/core/agent/tools/findDefinition.js +109 -0
- package/dist/api/core/agent/tools/findReferences.d.ts +86 -0
- package/dist/api/core/agent/tools/findReferences.js +135 -0
- package/dist/api/core/agent/tools/formatDocument.d.ts +67 -0
- package/dist/api/core/agent/tools/formatDocument.js +90 -0
- package/dist/api/core/agent/tools/getCodeActions.d.ts +77 -0
- package/dist/api/core/agent/tools/getCodeActions.js +97 -0
- package/dist/api/core/agent/tools/getCodeCompletion.d.ts +77 -0
- package/dist/api/core/agent/tools/getCodeCompletion.js +174 -0
- package/dist/api/core/agent/tools/getCodeContext.d.ts +14 -0
- package/dist/api/core/agent/tools/getCodeContext.js +282 -0
- package/dist/api/core/agent/tools/getSignatureHelp.d.ts +61 -0
- package/dist/api/core/agent/tools/getSignatureHelp.js +85 -0
- package/dist/api/core/agent/tools/getSymbols.d.ts +50 -0
- package/dist/api/core/agent/tools/getSymbols.js +164 -0
- package/dist/api/core/agent/tools/getSymbolsLSP.d.ts +58 -0
- package/dist/api/core/agent/tools/getSymbolsLSP.js +147 -0
- package/dist/api/core/agent/tools/getTypeInfo.d.ts +70 -0
- package/dist/api/core/agent/tools/getTypeInfo.js +84 -0
- package/dist/api/core/agent/tools/getWorkspaceInfo.d.ts +80 -0
- package/dist/api/core/agent/tools/getWorkspaceInfo.js +281 -0
- package/dist/api/core/agent/tools/getWorkspaceSymbols.d.ts +53 -0
- package/dist/api/core/agent/tools/getWorkspaceSymbols.js +97 -0
- package/dist/api/core/agent/tools/grepSearch.d.ts +100 -0
- package/dist/api/core/agent/tools/grepSearch.js +211 -0
- package/dist/api/core/agent/tools/languageModelToolsIntegration.d.ts +13 -0
- package/dist/api/core/agent/tools/languageModelToolsIntegration.js +22 -0
- package/dist/api/core/agent/tools/listDirectory.d.ts +66 -0
- package/dist/api/core/agent/tools/listDirectory.js +161 -0
- package/dist/api/core/agent/tools/mcpTools.d.ts +48 -0
- package/dist/api/core/agent/tools/mcpTools.js +442 -0
- package/dist/api/core/agent/tools/queryKnowledgeGraph.d.ts +120 -0
- package/dist/api/core/agent/tools/queryKnowledgeGraph.js +306 -0
- package/dist/api/core/agent/tools/readFile.d.ts +92 -0
- package/dist/api/core/agent/tools/readFile.js +337 -0
- package/dist/api/core/agent/tools/renameFile.d.ts +49 -0
- package/dist/api/core/agent/tools/renameFile.js +86 -0
- package/dist/api/core/agent/tools/renameSymbol.d.ts +70 -0
- package/dist/api/core/agent/tools/renameSymbol.js +106 -0
- package/dist/api/core/agent/tools/runTerminalCommand.d.ts +72 -0
- package/dist/api/core/agent/tools/runTerminalCommand.js +69 -0
- package/dist/api/core/agent/tools/searchFiles.d.ts +82 -0
- package/dist/api/core/agent/tools/searchFiles.js +280 -0
- package/dist/api/core/agent/tools/terminalAutoApprove.d.ts +3 -0
- package/dist/api/core/agent/tools/terminalAutoApprove.js +676 -0
- package/dist/api/core/agent/tools/treeTraversal.d.ts +60 -0
- package/dist/api/core/agent/tools/treeTraversal.js +217 -0
- package/dist/api/core/agent/tools/webSearch.d.ts +37 -0
- package/dist/api/core/agent/tools/webSearch.js +80 -0
- package/dist/api/core/agent/tools/writeFile.d.ts +59 -0
- package/dist/api/core/agent/tools/writeFile.js +97 -0
- package/dist/api/core/agent/utils/capturedWorkspaceMutationBridge.d.ts +12 -0
- package/dist/api/core/agent/utils/capturedWorkspaceMutationBridge.js +67 -0
- package/dist/api/core/agent/utils/diffUtils.d.ts +14 -0
- package/dist/api/core/agent/utils/diffUtils.js +39 -0
- package/dist/api/core/agent/utils/environmentSnapshot.d.ts +42 -0
- package/dist/api/core/agent/utils/environmentSnapshot.js +213 -0
- package/dist/api/core/agent/utils/errorRecovery.d.ts +49 -0
- package/dist/api/core/agent/utils/errorRecovery.js +229 -0
- package/dist/api/core/agent/utils/multimodalTokenLimiter.d.ts +8 -0
- package/dist/api/core/agent/utils/multimodalTokenLimiter.js +238 -0
- package/dist/api/core/agent/utils/openRouterModelSettings.d.ts +12 -0
- package/dist/api/core/agent/utils/openRouterModelSettings.js +34 -0
- package/dist/api/core/agent/utils/pathRecovery.d.ts +1 -0
- package/dist/api/core/agent/utils/pathRecovery.js +29 -0
- package/dist/api/core/agent/utils/repoMapIndex.d.ts +3 -0
- package/dist/api/core/agent/utils/repoMapIndex.js +197 -0
- package/dist/api/core/agent/utils/skillsDiscovery.d.ts +10 -0
- package/dist/api/core/agent/utils/skillsDiscovery.js +50 -0
- package/dist/api/core/agent/utils/toolCallBudget.d.ts +9 -0
- package/dist/api/core/agent/utils/toolCallBudget.js +55 -0
- package/dist/api/core/agent/utils/toolLifecycle.d.ts +30 -0
- package/dist/api/core/agent/utils/toolLifecycle.js +361 -0
- package/dist/api/core/agent/utils/toolResultSafetyProcessor.d.ts +12 -0
- package/dist/api/core/agent/utils/toolResultSafetyProcessor.js +57 -0
- package/dist/api/core/agent/utils/workspaceMutationBridge.d.ts +13 -0
- package/dist/api/core/agent/utils/workspaceMutationBridge.js +149 -0
- package/dist/api/core/agent/utils/workspacePathGuard.d.ts +7 -0
- package/dist/api/core/agent/utils/workspacePathGuard.js +79 -0
- package/dist/api/core/containers/chat/toolResultSerialization.d.ts +8 -0
- package/dist/api/core/containers/chat/toolResultSerialization.js +71 -0
- package/dist/api/core/library/BrowserManager.d.ts +11 -0
- package/dist/api/core/library/BrowserManager.js +66 -0
- package/dist/api/core/library/desktopWorkspace.d.ts +1 -0
- package/dist/api/core/library/desktopWorkspace.js +3 -0
- package/dist/api/core/library/extensionManager.d.ts +10 -0
- package/dist/api/core/library/extensionManager.js +27 -0
- package/dist/api/core/library/knowledgeGraph.d.ts +268 -0
- package/dist/api/core/library/knowledgeGraph.js +989 -0
- package/dist/api/core/library/languageModelTools.d.ts +141 -0
- package/dist/api/core/library/languageModelTools.js +207 -0
- package/dist/api/core/library/localRuntime.d.ts +23 -0
- package/dist/api/core/library/localRuntime.js +144 -0
- package/dist/api/core/library/lsp/coreLsp.d.ts +367 -0
- package/dist/api/core/library/lsp/coreLsp.js +2076 -0
- package/dist/api/core/library/lsp/protocol.d.ts +25 -0
- package/dist/api/core/library/lsp/protocol.js +1 -0
- package/dist/api/core/library/lsp/serverManager.d.ts +37 -0
- package/dist/api/core/library/lsp/serverManager.js +427 -0
- package/dist/api/core/library/mcpServerProvider.d.ts +69 -0
- package/dist/api/core/library/mcpServerProvider.js +152 -0
- package/dist/api/core/library/mcpSettings.d.ts +20 -0
- package/dist/api/core/library/mcpSettings.js +131 -0
- package/dist/api/core/library/patternMatcher.d.ts +66 -0
- package/dist/api/core/library/patternMatcher.js +284 -0
- package/dist/api/core/library/regexEscape.d.ts +4 -0
- package/dist/api/core/library/regexEscape.js +4 -0
- package/dist/api/core/library/runtimeEventBus.d.ts +27 -0
- package/dist/api/core/library/runtimeEventBus.js +78 -0
- package/dist/api/core/library/safetyMiddleware.d.ts +98 -0
- package/dist/api/core/library/safetyMiddleware.js +215 -0
- package/dist/api/core/library/tauri.d.ts +71 -0
- package/dist/api/core/library/tauri.js +222 -0
- package/dist/api/core/library/tauriImport.d.ts +5 -0
- package/dist/api/core/library/tauriImport.js +7 -0
- package/dist/api/core/library/terminalAutoApproveSettings.d.ts +32 -0
- package/dist/api/core/library/terminalAutoApproveSettings.js +529 -0
- package/dist/api/core/library/workspaceIdentity.d.ts +10 -0
- package/dist/api/core/library/workspaceIdentity.js +49 -0
- package/dist/api/core/library/workspaceSummary.d.ts +19 -0
- package/dist/api/core/library/workspaceSummary.js +35 -0
- package/dist/api/core/skills/agent-customization/SKILL.md +27 -0
- package/dist/api/core/skills/bug-fix/SKILL.md +21 -0
- package/dist/api/core/skills/create-pr/SKILL.md +32 -0
- package/dist/api/core/skills/dev-server/SKILL.md +20 -0
- package/dist/api/core/skills/fix-suggestions/SKILL.md +21 -0
- package/dist/api/core/skills/github-search/SKILL.md +21 -0
- package/dist/api/core/skills/github-summary/SKILL.md +25 -0
- package/dist/api/core/skills/integration-tests/SKILL.md +29 -0
- package/dist/api/core/skills/pr-comments/SKILL.md +23 -0
- package/dist/api/core/skills/project-setup/SKILL.md +20 -0
- package/dist/api/core/skills/search-integration/SKILL.md +19 -0
- package/dist/api/core/skills/search-results/SKILL.md +19 -0
- package/dist/api/core/skills/typescript-upgrade/SKILL.md +21 -0
- package/dist/api/data/runStore.d.ts +46 -0
- package/dist/api/data/runStore.js +244 -0
- package/dist/api/helpers/agentUtils.d.ts +11 -0
- package/dist/api/helpers/agentUtils.js +23 -0
- package/dist/api/helpers/modelTokenLimits.d.ts +2 -0
- package/dist/api/helpers/modelTokenLimits.js +158 -0
- package/dist/api/helpers/observationalMemory.d.ts +10 -0
- package/dist/api/helpers/observationalMemory.js +36 -0
- package/dist/api/helpers/promptBudget.d.ts +30 -0
- package/dist/api/helpers/promptBudget.js +216 -0
- package/dist/api/helpers/resolveImageMessageParts.d.ts +9 -0
- package/dist/api/helpers/resolveImageMessageParts.js +140 -0
- package/dist/api/helpers/slashCommands.d.ts +31 -0
- package/dist/api/helpers/slashCommands.js +129 -0
- package/dist/api/helpers/tokenUsage.d.ts +19 -0
- package/dist/api/helpers/tokenUsage.js +81 -0
- package/dist/api/routes/fileRoutes.d.ts +2 -0
- package/dist/api/routes/fileRoutes.js +108 -0
- package/dist/api/routes/lspDocumentRoutes.d.ts +4 -0
- package/dist/api/routes/lspDocumentRoutes.js +84 -0
- package/dist/api/routes/lspHierarchyRoutes.d.ts +4 -0
- package/dist/api/routes/lspHierarchyRoutes.js +122 -0
- package/dist/api/routes/lspPositionRoutes.d.ts +4 -0
- package/dist/api/routes/lspPositionRoutes.js +263 -0
- package/dist/api/routes/lspQueryRoutes.d.ts +4 -0
- package/dist/api/routes/lspQueryRoutes.js +679 -0
- package/dist/api/routes/lspResolveRoutes.d.ts +4 -0
- package/dist/api/routes/lspResolveRoutes.js +143 -0
- package/dist/api/routes/lspSemanticDocumentRoutes.d.ts +4 -0
- package/dist/api/routes/lspSemanticDocumentRoutes.js +98 -0
- package/dist/api/routes/semanticRoutes.d.ts +2 -0
- package/dist/api/routes/semanticRoutes.js +126 -0
- package/dist/cli.d.ts +8 -0
- package/dist/cli.js +160 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +38 -0
- package/package.json +100 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export type TerminalAutoApproveRuleValue = boolean | {
|
|
2
|
+
approve: boolean;
|
|
3
|
+
matchCommandLine?: boolean;
|
|
4
|
+
};
|
|
5
|
+
export type TerminalAutoApproveRules = Record<string, TerminalAutoApproveRuleValue>;
|
|
6
|
+
export declare const TERMINAL_AUTO_APPROVE_RULES_STORAGE_KEY = "settings.terminalAutoApproveRulesJson";
|
|
7
|
+
export declare const TERMINAL_AUTO_APPROVE_RULES_SETTING_ID = "terminalAutoApproveRulesJson";
|
|
8
|
+
export declare function sanitizeTerminalAutoApproveRules(input: unknown): TerminalAutoApproveRules | undefined;
|
|
9
|
+
export declare function parseTerminalAutoApproveRulesJson(raw: string | undefined | null): {
|
|
10
|
+
rules?: TerminalAutoApproveRules;
|
|
11
|
+
error?: string;
|
|
12
|
+
};
|
|
13
|
+
export declare function getStoredTerminalAutoApproveRules(storage?: Pick<Storage, "getItem"> | null | undefined): TerminalAutoApproveRules | undefined;
|
|
14
|
+
export declare function buildExactCommandLineAutoApproveRuleKey(commandLine: string): string;
|
|
15
|
+
export declare function suggestTerminalAutoApproveRule(commandLine: string): {
|
|
16
|
+
ruleKey: string;
|
|
17
|
+
ruleValue: TerminalAutoApproveRuleValue;
|
|
18
|
+
strategy: "exact" | "narrow";
|
|
19
|
+
};
|
|
20
|
+
export declare function saveExactTerminalAutoApproveRule(commandLine: string, storage?: Pick<Storage, "getItem" | "setItem"> | null | undefined): {
|
|
21
|
+
ruleKey: string;
|
|
22
|
+
rules: TerminalAutoApproveRules;
|
|
23
|
+
rawValue: string;
|
|
24
|
+
} | undefined;
|
|
25
|
+
export declare function saveTerminalAutoApproveRule(commandLine: string, storage?: Pick<Storage, "getItem" | "setItem"> | null | undefined): {
|
|
26
|
+
ruleKey: string;
|
|
27
|
+
ruleValue: TerminalAutoApproveRuleValue;
|
|
28
|
+
rules: TerminalAutoApproveRules;
|
|
29
|
+
rawValue: string;
|
|
30
|
+
strategy: "exact" | "narrow";
|
|
31
|
+
} | undefined;
|
|
32
|
+
export declare function getTerminalAutoApproveRulesFingerprint(rules: TerminalAutoApproveRules | undefined): string;
|
|
@@ -0,0 +1,529 @@
|
|
|
1
|
+
import { escapeRegExp } from "./regexEscape.js";
|
|
2
|
+
export const TERMINAL_AUTO_APPROVE_RULES_STORAGE_KEY = "settings.terminalAutoApproveRulesJson";
|
|
3
|
+
export const TERMINAL_AUTO_APPROVE_RULES_SETTING_ID = "terminalAutoApproveRulesJson";
|
|
4
|
+
const WRAPPER_PROGRAMS = new Set([
|
|
5
|
+
"sudo",
|
|
6
|
+
"doas",
|
|
7
|
+
"time",
|
|
8
|
+
"command",
|
|
9
|
+
"builtin",
|
|
10
|
+
"exec",
|
|
11
|
+
"nice",
|
|
12
|
+
"ionice",
|
|
13
|
+
"nohup",
|
|
14
|
+
"env",
|
|
15
|
+
"xargs",
|
|
16
|
+
"stdbuf",
|
|
17
|
+
"unbuffer",
|
|
18
|
+
"script",
|
|
19
|
+
"timeout",
|
|
20
|
+
]);
|
|
21
|
+
const SHELL_PROGRAMS = new Set([
|
|
22
|
+
"bash",
|
|
23
|
+
"sh",
|
|
24
|
+
"zsh",
|
|
25
|
+
"fish",
|
|
26
|
+
"ksh",
|
|
27
|
+
"csh",
|
|
28
|
+
"tcsh",
|
|
29
|
+
"dash",
|
|
30
|
+
"pwsh",
|
|
31
|
+
"powershell",
|
|
32
|
+
"powershell.exe",
|
|
33
|
+
"cmd",
|
|
34
|
+
"cmd.exe",
|
|
35
|
+
]);
|
|
36
|
+
const NEVER_AUTO_APPROVE_COMMANDS = new Set([
|
|
37
|
+
"bash",
|
|
38
|
+
"sh",
|
|
39
|
+
"zsh",
|
|
40
|
+
"fish",
|
|
41
|
+
"ksh",
|
|
42
|
+
"csh",
|
|
43
|
+
"tcsh",
|
|
44
|
+
"dash",
|
|
45
|
+
"pwsh",
|
|
46
|
+
"powershell",
|
|
47
|
+
"powershell.exe",
|
|
48
|
+
"cmd",
|
|
49
|
+
"cmd.exe",
|
|
50
|
+
"python",
|
|
51
|
+
"python3",
|
|
52
|
+
"node",
|
|
53
|
+
"ruby",
|
|
54
|
+
"perl",
|
|
55
|
+
"php",
|
|
56
|
+
"lua",
|
|
57
|
+
"eval",
|
|
58
|
+
"exec",
|
|
59
|
+
"source",
|
|
60
|
+
"sudo",
|
|
61
|
+
"su",
|
|
62
|
+
"doas",
|
|
63
|
+
"curl",
|
|
64
|
+
"wget",
|
|
65
|
+
"invoke-restmethod",
|
|
66
|
+
"invoke-webrequest",
|
|
67
|
+
"irm",
|
|
68
|
+
"iwr",
|
|
69
|
+
]);
|
|
70
|
+
const COMMANDS_WITH_SUBCOMMANDS = new Set([
|
|
71
|
+
"git",
|
|
72
|
+
"npm",
|
|
73
|
+
"npx",
|
|
74
|
+
"yarn",
|
|
75
|
+
"docker",
|
|
76
|
+
"kubectl",
|
|
77
|
+
"cargo",
|
|
78
|
+
"dotnet",
|
|
79
|
+
"mvn",
|
|
80
|
+
"gradle",
|
|
81
|
+
]);
|
|
82
|
+
const COMMANDS_WITH_SUB_SUB_COMMANDS = new Set(["npm run", "yarn run"]);
|
|
83
|
+
const ENV_ASSIGN_RE = /^[A-Za-z_][A-Za-z0-9_]*=.*$/;
|
|
84
|
+
function getPreviousNonWhitespaceChar(input, index) {
|
|
85
|
+
for (let cursor = index; cursor >= 0; cursor--) {
|
|
86
|
+
const char = input[cursor];
|
|
87
|
+
if (!/\s/.test(char)) {
|
|
88
|
+
return char;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
function isPlainObject(value) {
|
|
94
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
95
|
+
}
|
|
96
|
+
function normalizeCommandKeyword(token) {
|
|
97
|
+
if (!token) {
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
const unquoted = token.replace(/^['"]|['"]$/g, "");
|
|
101
|
+
if (!unquoted) {
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
const parts = unquoted.split(/[\\/]/);
|
|
105
|
+
const base = parts[parts.length - 1]?.toLowerCase() ?? "";
|
|
106
|
+
const normalized = base.replace(/\.(?:exe|cmd|bat|ps1)$/i, "");
|
|
107
|
+
return normalized || undefined;
|
|
108
|
+
}
|
|
109
|
+
function splitSegments(command) {
|
|
110
|
+
const segments = [];
|
|
111
|
+
let current = "";
|
|
112
|
+
let inSingle = false;
|
|
113
|
+
let inDouble = false;
|
|
114
|
+
const push = () => {
|
|
115
|
+
const trimmed = current.trim();
|
|
116
|
+
if (trimmed) {
|
|
117
|
+
segments.push(trimmed);
|
|
118
|
+
}
|
|
119
|
+
current = "";
|
|
120
|
+
};
|
|
121
|
+
for (let index = 0; index < command.length; index++) {
|
|
122
|
+
const char = command[index];
|
|
123
|
+
if (inSingle) {
|
|
124
|
+
current += char;
|
|
125
|
+
if (char === "'") {
|
|
126
|
+
inSingle = false;
|
|
127
|
+
}
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (inDouble) {
|
|
131
|
+
if (char === "\\" && index + 1 < command.length) {
|
|
132
|
+
current += char + command[index + 1];
|
|
133
|
+
index++;
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
current += char;
|
|
137
|
+
if (char === '"') {
|
|
138
|
+
inDouble = false;
|
|
139
|
+
}
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (char === "\\" && index + 1 < command.length) {
|
|
143
|
+
current += char + command[index + 1];
|
|
144
|
+
index++;
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
if (char === "'") {
|
|
148
|
+
inSingle = true;
|
|
149
|
+
current += char;
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
if (char === '"') {
|
|
153
|
+
inDouble = true;
|
|
154
|
+
current += char;
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
if (char === "|" || char === ";") {
|
|
158
|
+
if (command[index + 1] === "|" || command[index + 1] === "&") {
|
|
159
|
+
push();
|
|
160
|
+
index++;
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
push();
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
if (char === "&" && command[index + 1] === "&") {
|
|
167
|
+
push();
|
|
168
|
+
index++;
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
if (char === "&") {
|
|
172
|
+
const prev = getPreviousNonWhitespaceChar(command, index - 1);
|
|
173
|
+
const next = command[index + 1];
|
|
174
|
+
const isRedirectAmpersand = prev === ">" || prev === "<" || next === ">";
|
|
175
|
+
if (!isRedirectAmpersand) {
|
|
176
|
+
push();
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
current += char;
|
|
181
|
+
}
|
|
182
|
+
push();
|
|
183
|
+
return segments;
|
|
184
|
+
}
|
|
185
|
+
function tokenize(segment) {
|
|
186
|
+
const tokens = [];
|
|
187
|
+
let current = "";
|
|
188
|
+
let inSingle = false;
|
|
189
|
+
let inDouble = false;
|
|
190
|
+
let hasContent = false;
|
|
191
|
+
for (let index = 0; index < segment.length; index++) {
|
|
192
|
+
const char = segment[index];
|
|
193
|
+
if (inSingle) {
|
|
194
|
+
if (char === "'") {
|
|
195
|
+
inSingle = false;
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
current += char;
|
|
199
|
+
}
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
if (inDouble) {
|
|
203
|
+
if (char === "\\" && index + 1 < segment.length) {
|
|
204
|
+
const next = segment[index + 1];
|
|
205
|
+
if (next === "\\" || next === '"' || next === "$" || next === "`") {
|
|
206
|
+
current += next;
|
|
207
|
+
index++;
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
if (char === '"') {
|
|
212
|
+
inDouble = false;
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
current += char;
|
|
216
|
+
}
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
if (char === "\\" && index + 1 < segment.length) {
|
|
220
|
+
current += segment[index + 1];
|
|
221
|
+
index++;
|
|
222
|
+
hasContent = true;
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
if (char === "'") {
|
|
226
|
+
inSingle = true;
|
|
227
|
+
hasContent = true;
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
if (char === '"') {
|
|
231
|
+
inDouble = true;
|
|
232
|
+
hasContent = true;
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
if (/\s/.test(char)) {
|
|
236
|
+
if (current.length > 0 || hasContent) {
|
|
237
|
+
tokens.push(current);
|
|
238
|
+
current = "";
|
|
239
|
+
hasContent = false;
|
|
240
|
+
}
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
current += char;
|
|
244
|
+
hasContent = true;
|
|
245
|
+
}
|
|
246
|
+
if (current.length > 0 || hasContent) {
|
|
247
|
+
tokens.push(current);
|
|
248
|
+
}
|
|
249
|
+
return tokens;
|
|
250
|
+
}
|
|
251
|
+
function stripPrefixesAndWrappers(rawTokens) {
|
|
252
|
+
let index = 0;
|
|
253
|
+
while (index < rawTokens.length && ENV_ASSIGN_RE.test(rawTokens[index])) {
|
|
254
|
+
index++;
|
|
255
|
+
}
|
|
256
|
+
while (index < rawTokens.length) {
|
|
257
|
+
const token = normalizeCommandKeyword(rawTokens[index]);
|
|
258
|
+
if (!token || !WRAPPER_PROGRAMS.has(token)) {
|
|
259
|
+
break;
|
|
260
|
+
}
|
|
261
|
+
index++;
|
|
262
|
+
while (index < rawTokens.length) {
|
|
263
|
+
const next = rawTokens[index];
|
|
264
|
+
if (next === "--") {
|
|
265
|
+
index++;
|
|
266
|
+
break;
|
|
267
|
+
}
|
|
268
|
+
if (next.startsWith("-")) {
|
|
269
|
+
index++;
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
if (ENV_ASSIGN_RE.test(next)) {
|
|
273
|
+
index++;
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
if ((token === "timeout" || token === "nice" || token === "ionice") && /^\d/.test(next)) {
|
|
277
|
+
index++;
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
break;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
return rawTokens.slice(index);
|
|
284
|
+
}
|
|
285
|
+
function extractShellCommand(tokens) {
|
|
286
|
+
const head = normalizeCommandKeyword(tokens[0]);
|
|
287
|
+
if (!head || !SHELL_PROGRAMS.has(head)) {
|
|
288
|
+
return undefined;
|
|
289
|
+
}
|
|
290
|
+
for (let index = 1; index < tokens.length; index++) {
|
|
291
|
+
const token = tokens[index];
|
|
292
|
+
if (!token) {
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
if (/^--(?:command)$/i.test(token) || /^-(?:c|Command)$/i.test(token)) {
|
|
296
|
+
return tokens[index + 1];
|
|
297
|
+
}
|
|
298
|
+
if (/^-[A-Za-z]+$/.test(token) && token.toLowerCase().includes("c")) {
|
|
299
|
+
return tokens[index + 1];
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
return undefined;
|
|
303
|
+
}
|
|
304
|
+
function extractEffectiveCommands(commandLine, depth = 0) {
|
|
305
|
+
if (depth > 8) {
|
|
306
|
+
return [];
|
|
307
|
+
}
|
|
308
|
+
const commands = [];
|
|
309
|
+
for (const segment of splitSegments(commandLine)) {
|
|
310
|
+
const rawTokens = tokenize(segment);
|
|
311
|
+
const tokens = stripPrefixesAndWrappers(rawTokens);
|
|
312
|
+
if (tokens.length === 0) {
|
|
313
|
+
continue;
|
|
314
|
+
}
|
|
315
|
+
const nested = extractShellCommand(tokens);
|
|
316
|
+
if (nested) {
|
|
317
|
+
commands.push(...extractEffectiveCommands(nested, depth + 1));
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
commands.push(tokens.join(" "));
|
|
321
|
+
}
|
|
322
|
+
return commands;
|
|
323
|
+
}
|
|
324
|
+
function isNavigationOnlyCommand(command) {
|
|
325
|
+
return /^(cd|set-location)\b/i.test(command.trim());
|
|
326
|
+
}
|
|
327
|
+
function findNextNonFlagArg(parts, startIndex) {
|
|
328
|
+
for (let index = startIndex; index < parts.length; index++) {
|
|
329
|
+
if (!parts[index].startsWith("-")) {
|
|
330
|
+
return index;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
return undefined;
|
|
334
|
+
}
|
|
335
|
+
export function sanitizeTerminalAutoApproveRules(input) {
|
|
336
|
+
if (!isPlainObject(input)) {
|
|
337
|
+
return undefined;
|
|
338
|
+
}
|
|
339
|
+
const normalized = {};
|
|
340
|
+
for (const [key, value] of Object.entries(input)) {
|
|
341
|
+
const trimmedKey = key.trim();
|
|
342
|
+
if (!trimmedKey) {
|
|
343
|
+
continue;
|
|
344
|
+
}
|
|
345
|
+
if (typeof value === "boolean") {
|
|
346
|
+
normalized[trimmedKey] = value;
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
if (isPlainObject(value) &&
|
|
350
|
+
typeof value.approve === "boolean" &&
|
|
351
|
+
(value.matchCommandLine === undefined ||
|
|
352
|
+
typeof value.matchCommandLine === "boolean")) {
|
|
353
|
+
normalized[trimmedKey] = value.matchCommandLine === true
|
|
354
|
+
? { approve: value.approve, matchCommandLine: true }
|
|
355
|
+
: { approve: value.approve };
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return Object.keys(normalized).length > 0 ? normalized : undefined;
|
|
359
|
+
}
|
|
360
|
+
export function parseTerminalAutoApproveRulesJson(raw) {
|
|
361
|
+
const trimmed = raw?.trim() || "";
|
|
362
|
+
if (!trimmed) {
|
|
363
|
+
return {};
|
|
364
|
+
}
|
|
365
|
+
try {
|
|
366
|
+
const parsed = JSON.parse(trimmed);
|
|
367
|
+
const rules = sanitizeTerminalAutoApproveRules(parsed);
|
|
368
|
+
if (!rules) {
|
|
369
|
+
return {
|
|
370
|
+
error: "Rules must be a JSON object whose values are booleans or { approve, matchCommandLine } objects.",
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
return { rules };
|
|
374
|
+
}
|
|
375
|
+
catch (error) {
|
|
376
|
+
return {
|
|
377
|
+
error: error.message || "Invalid JSON",
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
export function getStoredTerminalAutoApproveRules(storage = typeof window !== "undefined" ? window.localStorage : null) {
|
|
382
|
+
if (!storage) {
|
|
383
|
+
return undefined;
|
|
384
|
+
}
|
|
385
|
+
try {
|
|
386
|
+
const raw = storage.getItem(TERMINAL_AUTO_APPROVE_RULES_STORAGE_KEY);
|
|
387
|
+
return parseTerminalAutoApproveRulesJson(raw).rules;
|
|
388
|
+
}
|
|
389
|
+
catch {
|
|
390
|
+
return undefined;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
export function buildExactCommandLineAutoApproveRuleKey(commandLine) {
|
|
394
|
+
const trimmed = commandLine.trim();
|
|
395
|
+
return `/^${escapeRegExp(trimmed)}$/`;
|
|
396
|
+
}
|
|
397
|
+
export function suggestTerminalAutoApproveRule(commandLine) {
|
|
398
|
+
const trimmed = commandLine.trim();
|
|
399
|
+
if (!trimmed) {
|
|
400
|
+
return {
|
|
401
|
+
ruleKey: buildExactCommandLineAutoApproveRuleKey(commandLine),
|
|
402
|
+
ruleValue: { approve: true, matchCommandLine: true },
|
|
403
|
+
strategy: "exact",
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
const effectiveCommands = extractEffectiveCommands(trimmed).filter((command) => !isNavigationOnlyCommand(command));
|
|
407
|
+
if (effectiveCommands.length !== 1) {
|
|
408
|
+
return {
|
|
409
|
+
ruleKey: buildExactCommandLineAutoApproveRuleKey(trimmed),
|
|
410
|
+
ruleValue: { approve: true, matchCommandLine: true },
|
|
411
|
+
strategy: "exact",
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
const parts = effectiveCommands[0].trim().split(/\s+/);
|
|
415
|
+
const baseCommand = parts[0]?.toLowerCase();
|
|
416
|
+
if (!baseCommand || NEVER_AUTO_APPROVE_COMMANDS.has(baseCommand)) {
|
|
417
|
+
return {
|
|
418
|
+
ruleKey: buildExactCommandLineAutoApproveRuleKey(trimmed),
|
|
419
|
+
ruleValue: { approve: true, matchCommandLine: true },
|
|
420
|
+
strategy: "exact",
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
if (COMMANDS_WITH_SUBCOMMANDS.has(baseCommand)) {
|
|
424
|
+
const subCommandIndex = findNextNonFlagArg(parts, 1);
|
|
425
|
+
if (subCommandIndex !== undefined) {
|
|
426
|
+
const baseSubCommand = `${parts[0]} ${parts[subCommandIndex]}`.toLowerCase();
|
|
427
|
+
if (COMMANDS_WITH_SUB_SUB_COMMANDS.has(baseSubCommand)) {
|
|
428
|
+
const subSubCommandIndex = findNextNonFlagArg(parts, subCommandIndex + 1);
|
|
429
|
+
if (subSubCommandIndex !== undefined) {
|
|
430
|
+
return {
|
|
431
|
+
ruleKey: parts.slice(0, subSubCommandIndex + 1).join(" "),
|
|
432
|
+
ruleValue: true,
|
|
433
|
+
strategy: "narrow",
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
return {
|
|
438
|
+
ruleKey: parts.slice(0, subCommandIndex + 1).join(" "),
|
|
439
|
+
ruleValue: true,
|
|
440
|
+
strategy: "narrow",
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
return {
|
|
445
|
+
ruleKey: parts[0],
|
|
446
|
+
ruleValue: true,
|
|
447
|
+
strategy: "narrow",
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
export function saveExactTerminalAutoApproveRule(commandLine, storage = typeof window !== "undefined" ? window.localStorage : null) {
|
|
451
|
+
const trimmed = commandLine.trim();
|
|
452
|
+
if (!trimmed || !storage) {
|
|
453
|
+
return undefined;
|
|
454
|
+
}
|
|
455
|
+
const existing = getStoredTerminalAutoApproveRules(storage) || {};
|
|
456
|
+
const ruleKey = buildExactCommandLineAutoApproveRuleKey(trimmed);
|
|
457
|
+
const rules = {
|
|
458
|
+
...existing,
|
|
459
|
+
[ruleKey]: {
|
|
460
|
+
approve: true,
|
|
461
|
+
matchCommandLine: true,
|
|
462
|
+
},
|
|
463
|
+
};
|
|
464
|
+
const rawValue = JSON.stringify(rules, null, 2);
|
|
465
|
+
storage.setItem(TERMINAL_AUTO_APPROVE_RULES_STORAGE_KEY, rawValue);
|
|
466
|
+
if (typeof window !== "undefined") {
|
|
467
|
+
window.dispatchEvent(new CustomEvent("settingsChanged", {
|
|
468
|
+
detail: {
|
|
469
|
+
key: TERMINAL_AUTO_APPROVE_RULES_SETTING_ID,
|
|
470
|
+
value: rawValue,
|
|
471
|
+
settings: {
|
|
472
|
+
[TERMINAL_AUTO_APPROVE_RULES_SETTING_ID]: rawValue,
|
|
473
|
+
},
|
|
474
|
+
},
|
|
475
|
+
}));
|
|
476
|
+
}
|
|
477
|
+
return { ruleKey, rules, rawValue };
|
|
478
|
+
}
|
|
479
|
+
export function saveTerminalAutoApproveRule(commandLine, storage = typeof window !== "undefined" ? window.localStorage : null) {
|
|
480
|
+
const trimmed = commandLine.trim();
|
|
481
|
+
if (!trimmed || !storage) {
|
|
482
|
+
return undefined;
|
|
483
|
+
}
|
|
484
|
+
const existing = getStoredTerminalAutoApproveRules(storage) || {};
|
|
485
|
+
const suggestion = suggestTerminalAutoApproveRule(trimmed);
|
|
486
|
+
const rules = {
|
|
487
|
+
...existing,
|
|
488
|
+
[suggestion.ruleKey]: suggestion.ruleValue,
|
|
489
|
+
};
|
|
490
|
+
const rawValue = JSON.stringify(rules, null, 2);
|
|
491
|
+
storage.setItem(TERMINAL_AUTO_APPROVE_RULES_STORAGE_KEY, rawValue);
|
|
492
|
+
if (typeof window !== "undefined") {
|
|
493
|
+
window.dispatchEvent(new CustomEvent("settingsChanged", {
|
|
494
|
+
detail: {
|
|
495
|
+
key: TERMINAL_AUTO_APPROVE_RULES_SETTING_ID,
|
|
496
|
+
value: rawValue,
|
|
497
|
+
settings: {
|
|
498
|
+
[TERMINAL_AUTO_APPROVE_RULES_SETTING_ID]: rawValue,
|
|
499
|
+
},
|
|
500
|
+
},
|
|
501
|
+
}));
|
|
502
|
+
}
|
|
503
|
+
return {
|
|
504
|
+
ruleKey: suggestion.ruleKey,
|
|
505
|
+
ruleValue: suggestion.ruleValue,
|
|
506
|
+
rules,
|
|
507
|
+
rawValue,
|
|
508
|
+
strategy: suggestion.strategy,
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
export function getTerminalAutoApproveRulesFingerprint(rules) {
|
|
512
|
+
if (!rules) {
|
|
513
|
+
return "";
|
|
514
|
+
}
|
|
515
|
+
const entries = Object.entries(rules)
|
|
516
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
517
|
+
.map(([key, value]) => [
|
|
518
|
+
key,
|
|
519
|
+
typeof value === "boolean"
|
|
520
|
+
? value
|
|
521
|
+
: {
|
|
522
|
+
approve: value.approve,
|
|
523
|
+
...(value.matchCommandLine === true
|
|
524
|
+
? { matchCommandLine: true }
|
|
525
|
+
: {}),
|
|
526
|
+
},
|
|
527
|
+
]);
|
|
528
|
+
return JSON.stringify(entries);
|
|
529
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { WorkspaceNode } from "./workspaceSummary.js";
|
|
2
|
+
type WorkspaceRootOptions = {
|
|
3
|
+
preferDesktopRoot?: boolean;
|
|
4
|
+
allowVirtualRoot?: boolean;
|
|
5
|
+
fallbackToTreePath?: boolean;
|
|
6
|
+
};
|
|
7
|
+
export declare const getWorkspaceTree: () => WorkspaceNode | null;
|
|
8
|
+
export declare const getWorkspaceRoot: (options?: WorkspaceRootOptions) => string | undefined;
|
|
9
|
+
export declare const getWorkspaceDisplayName: () => string;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { getDesktopWorkspacePath } from "./desktopWorkspace.js";
|
|
2
|
+
const isNonEmptyString = (value) => typeof value === "string" && value.trim().length > 0;
|
|
3
|
+
const isVirtualWorkspaceRoot = (root) => root.startsWith("/workspace/");
|
|
4
|
+
export const getWorkspaceTree = () => {
|
|
5
|
+
if (typeof window === "undefined") {
|
|
6
|
+
return null;
|
|
7
|
+
}
|
|
8
|
+
return (window.workspaceStructure ||
|
|
9
|
+
window.tauriWorkspaceStructure ||
|
|
10
|
+
null);
|
|
11
|
+
};
|
|
12
|
+
export const getWorkspaceRoot = (options = {}) => {
|
|
13
|
+
if (typeof window === "undefined") {
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
const { preferDesktopRoot = true, allowVirtualRoot = true, fallbackToTreePath = true, } = options;
|
|
17
|
+
const candidates = [];
|
|
18
|
+
if (preferDesktopRoot) {
|
|
19
|
+
candidates.push(getDesktopWorkspacePath());
|
|
20
|
+
}
|
|
21
|
+
candidates.push(window.workspaceRoot);
|
|
22
|
+
if (fallbackToTreePath) {
|
|
23
|
+
candidates.push(getWorkspaceTree()?.path);
|
|
24
|
+
}
|
|
25
|
+
if (!preferDesktopRoot) {
|
|
26
|
+
candidates.push(getDesktopWorkspacePath());
|
|
27
|
+
}
|
|
28
|
+
for (const candidate of candidates) {
|
|
29
|
+
if (!isNonEmptyString(candidate)) {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
if (!allowVirtualRoot && isVirtualWorkspaceRoot(candidate)) {
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
return candidate;
|
|
36
|
+
}
|
|
37
|
+
return undefined;
|
|
38
|
+
};
|
|
39
|
+
export const getWorkspaceDisplayName = () => {
|
|
40
|
+
const root = getWorkspaceTree();
|
|
41
|
+
const pathCandidate = getWorkspaceRoot({ preferDesktopRoot: false }) ||
|
|
42
|
+
(typeof root?.name === "string" ? root.name : "");
|
|
43
|
+
const normalized = String(pathCandidate).trim();
|
|
44
|
+
if (!normalized) {
|
|
45
|
+
return "Workspace";
|
|
46
|
+
}
|
|
47
|
+
const segments = normalized.split(/[/\\]/).filter(Boolean);
|
|
48
|
+
return segments[segments.length - 1] || "Workspace";
|
|
49
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type WorkspaceNode = {
|
|
2
|
+
type?: string;
|
|
3
|
+
name?: string;
|
|
4
|
+
path?: string;
|
|
5
|
+
children?: WorkspaceNode[];
|
|
6
|
+
};
|
|
7
|
+
export type WorkspaceSummary = {
|
|
8
|
+
hasRoot: boolean;
|
|
9
|
+
fileCount: number;
|
|
10
|
+
isDefinitelyEmpty: boolean;
|
|
11
|
+
};
|
|
12
|
+
export declare const countWorkspaceFiles: (node?: WorkspaceNode | null) => number;
|
|
13
|
+
export declare const getWorkspaceSummaryFromRoot: (root?: WorkspaceNode | null) => WorkspaceSummary;
|
|
14
|
+
export declare const collectWorkspaceFilePaths: (root?: WorkspaceNode | null) => Set<string>;
|
|
15
|
+
export declare const shouldShowEmptyWorkspaceState: ({ summary, currentFile, activeTab, }: {
|
|
16
|
+
summary: WorkspaceSummary;
|
|
17
|
+
currentFile?: unknown;
|
|
18
|
+
activeTab?: string | null;
|
|
19
|
+
}) => boolean;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export const countWorkspaceFiles = (node) => {
|
|
2
|
+
if (!node)
|
|
3
|
+
return 0;
|
|
4
|
+
if (node.type === "file")
|
|
5
|
+
return 1;
|
|
6
|
+
if (!Array.isArray(node.children) || node.children.length === 0)
|
|
7
|
+
return 0;
|
|
8
|
+
return node.children.reduce((total, child) => total + countWorkspaceFiles(child), 0);
|
|
9
|
+
};
|
|
10
|
+
export const getWorkspaceSummaryFromRoot = (root) => {
|
|
11
|
+
const hasRoot = root != null;
|
|
12
|
+
const children = root?.children;
|
|
13
|
+
return {
|
|
14
|
+
hasRoot,
|
|
15
|
+
fileCount: countWorkspaceFiles(root),
|
|
16
|
+
isDefinitelyEmpty: hasRoot && Array.isArray(children) && children.length === 0,
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
export const collectWorkspaceFilePaths = (root) => {
|
|
20
|
+
const paths = new Set();
|
|
21
|
+
const walk = (node) => {
|
|
22
|
+
if (!node)
|
|
23
|
+
return;
|
|
24
|
+
if (node.type === "file" && typeof node.path === "string" && node.path) {
|
|
25
|
+
paths.add(node.path);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (Array.isArray(node.children)) {
|
|
29
|
+
node.children.forEach(walk);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
walk(root);
|
|
33
|
+
return paths;
|
|
34
|
+
};
|
|
35
|
+
export const shouldShowEmptyWorkspaceState = ({ summary, currentFile, activeTab, }) => summary.hasRoot && !currentFile && !activeTab;
|