@within-7/minto 0.3.6 → 0.3.10
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/{cli.js → cli.cjs} +25 -23
- package/dist/commands/agents/AgentsCommand.js +459 -655
- package/dist/commands/agents/AgentsCommand.js.map +2 -2
- package/dist/commands/agents/types.js +1 -0
- package/dist/commands/agents/types.js.map +2 -2
- package/dist/commands/agents/utils/fileOperations.js +96 -36
- package/dist/commands/agents/utils/fileOperations.js.map +3 -3
- package/dist/commands/agents/utils/index.js +3 -1
- package/dist/commands/agents/utils/index.js.map +2 -2
- package/dist/commands/context.js +54 -23
- package/dist/commands/context.js.map +2 -2
- package/dist/commands/export.js +673 -93
- package/dist/commands/export.js.map +2 -2
- package/dist/commands/language.js +110 -0
- package/dist/commands/language.js.map +7 -0
- package/dist/commands/mcp-interactive.js +419 -217
- package/dist/commands/mcp-interactive.js.map +2 -2
- package/dist/commands/model.js +415 -66
- package/dist/commands/model.js.map +2 -2
- package/dist/commands/new.js +56 -0
- package/dist/commands/new.js.map +7 -0
- package/dist/commands/permissions.js +75 -49
- package/dist/commands/permissions.js.map +2 -2
- package/dist/commands/plugin.js +882 -185
- package/dist/commands/plugin.js.map +3 -3
- package/dist/commands/resume.js +251 -16
- package/dist/commands/resume.js.map +2 -2
- package/dist/commands/sandbox.js +168 -70
- package/dist/commands/sandbox.js.map +2 -2
- package/dist/commands/sessions.js +224 -0
- package/dist/commands/sessions.js.map +7 -0
- package/dist/commands/setup.js +596 -109
- package/dist/commands/setup.js.map +2 -2
- package/dist/commands/stats.js +292 -0
- package/dist/commands/stats.js.map +7 -0
- package/dist/commands/status.js +75 -7
- package/dist/commands/status.js.map +2 -2
- package/dist/commands/undo.js +154 -180
- package/dist/commands/undo.js.map +2 -2
- package/dist/commands.js +6 -0
- package/dist/commands.js.map +2 -2
- package/dist/components/AskUserQuestionDialog/AskUserQuestionDialog.js +3 -2
- package/dist/components/AskUserQuestionDialog/AskUserQuestionDialog.js.map +2 -2
- package/dist/components/Config.js +9 -8
- package/dist/components/Config.js.map +2 -2
- package/dist/components/HeaderBar.js +2 -1
- package/dist/components/HeaderBar.js.map +2 -2
- package/dist/components/Help.js +166 -32
- package/dist/components/Help.js.map +2 -2
- package/dist/components/HotkeyHelpPanel.js +46 -44
- package/dist/components/HotkeyHelpPanel.js.map +2 -2
- package/dist/components/InfoPanel/InfoPanel.js +123 -0
- package/dist/components/InfoPanel/InfoPanel.js.map +7 -0
- package/dist/components/InfoPanel/index.js +5 -0
- package/dist/components/InfoPanel/index.js.map +7 -0
- package/dist/components/InfoPanel/types.js +1 -0
- package/dist/components/InfoPanel/types.js.map +7 -0
- package/dist/components/Logo.js +5 -2
- package/dist/components/Logo.js.map +2 -2
- package/dist/components/MCPServerApprovalDialog.js +6 -5
- package/dist/components/MCPServerApprovalDialog.js.map +2 -2
- package/dist/components/MCPServerMultiselectDialog.js +5 -4
- package/dist/components/MCPServerMultiselectDialog.js.map +2 -2
- package/dist/components/MessageSelector.js +4 -3
- package/dist/components/MessageSelector.js.map +2 -2
- package/dist/components/ModelConfig.js +13 -12
- package/dist/components/ModelConfig.js.map +2 -2
- package/dist/components/ModelListManager.js +4 -3
- package/dist/components/ModelListManager.js.map +2 -2
- package/dist/components/ModelSelector/BrandTextInput.js +43 -0
- package/dist/components/ModelSelector/BrandTextInput.js.map +7 -0
- package/dist/components/ModelSelector/ModelSelector.js +419 -501
- package/dist/components/ModelSelector/ModelSelector.js.map +2 -2
- package/dist/components/ModelSelector/WizardContainer.js +45 -0
- package/dist/components/ModelSelector/WizardContainer.js.map +7 -0
- package/dist/components/ModelSelector/index.js +1 -3
- package/dist/components/ModelSelector/index.js.map +2 -2
- package/dist/components/PromptInput.js +77 -44
- package/dist/components/PromptInput.js.map +2 -2
- package/dist/components/SensitiveFileWarning.js +12 -8
- package/dist/components/SensitiveFileWarning.js.map +2 -2
- package/dist/components/SimpleSelector/SimpleSelector.js +154 -0
- package/dist/components/SimpleSelector/SimpleSelector.js.map +7 -0
- package/dist/components/SimpleSelector/index.js +5 -0
- package/dist/components/SimpleSelector/index.js.map +7 -0
- package/dist/components/SimpleSelector/types.js +1 -0
- package/dist/components/SimpleSelector/types.js.map +7 -0
- package/dist/components/StatusOverlayContent.js +21 -0
- package/dist/components/StatusOverlayContent.js.map +7 -0
- package/dist/components/TabbedListView/ScrollableList.js +117 -0
- package/dist/components/TabbedListView/ScrollableList.js.map +7 -0
- package/dist/components/TabbedListView/SearchInput.js +23 -0
- package/dist/components/TabbedListView/SearchInput.js.map +7 -0
- package/dist/components/TabbedListView/TabBar.js +20 -0
- package/dist/components/TabbedListView/TabBar.js.map +7 -0
- package/dist/components/TabbedListView/TabbedListView.js +246 -0
- package/dist/components/TabbedListView/TabbedListView.js.map +7 -0
- package/dist/components/TabbedListView/index.js +11 -0
- package/dist/components/TabbedListView/index.js.map +7 -0
- package/dist/components/TabbedListView/types.js +1 -0
- package/dist/components/TabbedListView/types.js.map +7 -0
- package/dist/components/TodoChangeBlock.js +6 -5
- package/dist/components/TodoChangeBlock.js.map +3 -3
- package/dist/components/TodoPanel.js +6 -3
- package/dist/components/TodoPanel.js.map +3 -3
- package/dist/components/TrustDialog.js +6 -5
- package/dist/components/TrustDialog.js.map +2 -2
- package/dist/components/messages/UserToolResultMessage/UserToolCanceledMessage.js +2 -1
- package/dist/components/messages/UserToolResultMessage/UserToolCanceledMessage.js.map +2 -2
- package/dist/constants/macros.js +1 -1
- package/dist/constants/macros.js.map +1 -1
- package/dist/constants/product.js +2 -2
- package/dist/constants/product.js.map +1 -1
- package/dist/constants/prompts.js +17 -0
- package/dist/constants/prompts.js.map +2 -2
- package/dist/constants/toolInputExamples.js +5 -1
- package/dist/constants/toolInputExamples.js.map +2 -2
- package/dist/core/backupHook.js +29 -0
- package/dist/core/backupHook.js.map +7 -0
- package/dist/core/config/defaults.js +8 -2
- package/dist/core/config/defaults.js.map +2 -2
- package/dist/core/config/schema.js +14 -2
- package/dist/core/config/schema.js.map +2 -2
- package/dist/core/costTracker.js +0 -16
- package/dist/core/costTracker.js.map +2 -2
- package/dist/core/tokenStatsManager.js +5 -0
- package/dist/core/tokenStatsManager.js.map +2 -2
- package/dist/cost-tracker.js +0 -16
- package/dist/cost-tracker.js.map +2 -2
- package/dist/entrypoints/bootstrap.js +56 -0
- package/dist/entrypoints/bootstrap.js.map +7 -0
- package/dist/entrypoints/cli.js +164 -23
- package/dist/entrypoints/cli.js.map +3 -3
- package/dist/history.js +75 -15
- package/dist/history.js.map +2 -2
- package/dist/i18n/index.js +2 -2
- package/dist/i18n/index.js.map +2 -2
- package/dist/i18n/locales/en.js +582 -1
- package/dist/i18n/locales/en.js.map +2 -2
- package/dist/i18n/locales/zh-CN.js +582 -1
- package/dist/i18n/locales/zh-CN.js.map +2 -2
- package/dist/i18n/types.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +2 -2
- package/dist/messages.js +11 -0
- package/dist/messages.js.map +2 -2
- package/dist/permissions.js.map +2 -2
- package/dist/query.js +9 -0
- package/dist/query.js.map +2 -2
- package/dist/screens/REPL.js +45 -7
- package/dist/screens/REPL.js.map +2 -2
- package/dist/services/customCommands.js +44 -16
- package/dist/services/customCommands.js.map +2 -2
- package/dist/services/plugins/lspServers.js +1 -1
- package/dist/services/plugins/lspServers.js.map +2 -2
- package/dist/services/plugins/pluginRuntime.js +2 -1
- package/dist/services/plugins/pluginRuntime.js.map +2 -2
- package/dist/services/plugins/pluginValidation.js +10 -3
- package/dist/services/plugins/pluginValidation.js.map +2 -2
- package/dist/services/plugins/skillMarketplace.js +16 -8
- package/dist/services/plugins/skillMarketplace.js.map +2 -2
- package/dist/services/systemReminder.js +17 -6
- package/dist/services/systemReminder.js.map +2 -2
- package/dist/tools/FileEditTool/FileEditTool.js +7 -0
- package/dist/tools/FileEditTool/FileEditTool.js.map +2 -2
- package/dist/tools/FileWriteTool/FileWriteTool.js +7 -0
- package/dist/tools/FileWriteTool/FileWriteTool.js.map +2 -2
- package/dist/tools/MultiEditTool/MultiEditTool.js +7 -0
- package/dist/tools/MultiEditTool/MultiEditTool.js.map +2 -2
- package/dist/tools/NotebookEditTool/NotebookEditTool.js +2 -0
- package/dist/tools/NotebookEditTool/NotebookEditTool.js.map +2 -2
- package/dist/tools/TaskTool/TaskTool.js +179 -1
- package/dist/tools/TaskTool/TaskTool.js.map +2 -2
- package/dist/tools/TodoWriteTool/prompt.js +21 -0
- package/dist/tools/TodoWriteTool/prompt.js.map +2 -2
- package/dist/tools/URLFetcherTool/prompt.js +14 -9
- package/dist/tools/URLFetcherTool/prompt.js.map +2 -2
- package/dist/tools/WebSearchTool/prompt.js +12 -6
- package/dist/tools/WebSearchTool/prompt.js.map +2 -2
- package/dist/types/PermissionMode.js +30 -1
- package/dist/types/PermissionMode.js.map +2 -2
- package/dist/types/plugin.js +2 -4
- package/dist/types/plugin.js.map +2 -2
- package/dist/utils/agentHookExecutor.js +103 -0
- package/dist/utils/agentHookExecutor.js.map +7 -0
- package/dist/utils/agentLoader.js +272 -32
- package/dist/utils/agentLoader.js.map +2 -2
- package/dist/utils/agentMemory.js +134 -0
- package/dist/utils/agentMemory.js.map +7 -0
- package/dist/utils/claudeCodeSync.js +439 -0
- package/dist/utils/claudeCodeSync.js.map +7 -0
- package/dist/utils/config.js +52 -24
- package/dist/utils/config.js.map +2 -2
- package/dist/utils/configPaths.js +199 -0
- package/dist/utils/configPaths.js.map +7 -0
- package/dist/utils/execFileNoThrow.js +2 -1
- package/dist/utils/execFileNoThrow.js.map +2 -2
- package/dist/utils/historyManager.js +234 -0
- package/dist/utils/historyManager.js.map +7 -0
- package/dist/utils/marketplaceManager.js +80 -43
- package/dist/utils/marketplaceManager.js.map +2 -2
- package/dist/utils/messages.js +13 -8
- package/dist/utils/messages.js.map +2 -2
- package/dist/utils/migration/index.js +37 -0
- package/dist/utils/migration/index.js.map +7 -0
- package/dist/utils/migration/migrateHistory.js +273 -0
- package/dist/utils/migration/migrateHistory.js.map +7 -0
- package/dist/utils/migration/migrateTodos.js +323 -0
- package/dist/utils/migration/migrateTodos.js.map +7 -0
- package/dist/utils/pasteCache.js +309 -0
- package/dist/utils/pasteCache.js.map +7 -0
- package/dist/utils/pluginInstaller.js +34 -24
- package/dist/utils/pluginInstaller.js.map +2 -2
- package/dist/utils/pluginLoader.js +54 -28
- package/dist/utils/pluginLoader.js.map +2 -2
- package/dist/utils/repoFetcher.js +110 -0
- package/dist/utils/repoFetcher.js.map +7 -0
- package/dist/utils/sessionIndex.js +192 -0
- package/dist/utils/sessionIndex.js.map +7 -0
- package/dist/utils/sessionTracker.js +170 -0
- package/dist/utils/sessionTracker.js.map +7 -0
- package/dist/utils/skillLoader.js +103 -5
- package/dist/utils/skillLoader.js.map +2 -2
- package/dist/utils/stats.js +417 -0
- package/dist/utils/stats.js.map +7 -0
- package/dist/utils/stringSubstitution.js +106 -0
- package/dist/utils/stringSubstitution.js.map +7 -0
- package/dist/utils/teamConfig.js +156 -14
- package/dist/utils/teamConfig.js.map +2 -2
- package/dist/utils/terminal.js +1 -1
- package/dist/utils/terminal.js.map +2 -2
- package/dist/utils/todoStorage.js +51 -19
- package/dist/utils/todoStorage.js.map +2 -2
- package/dist/utils/tooling/safeRender.js.map +2 -2
- package/dist/version.js +2 -2
- package/dist/version.js.map +1 -1
- package/package.json +71 -28
- package/scripts/{postinstall.js → postinstall.cjs} +1 -1
package/dist/commands/resume.js
CHANGED
|
@@ -1,33 +1,268 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { ResumeConversation } from "../screens/ResumeConversation.js";
|
|
1
|
+
import React, { useState, useMemo } from "react";
|
|
3
2
|
import { render } from "ink";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { t } from "../i18n/index.js";
|
|
5
|
+
import { getCwd } from "../utils/state.js";
|
|
4
6
|
import { CACHE_PATHS, loadLogList } from "../utils/log.js";
|
|
7
|
+
import { deserializeMessages } from "../utils/conversationRecovery.js";
|
|
8
|
+
import { getNextAvailableLogForkNumber, logError } from "../utils/log.js";
|
|
9
|
+
import { isDefaultSlowAndCapableModel } from "../utils/model.js";
|
|
10
|
+
import { prepareTerminalForREPL } from "../utils/terminal.js";
|
|
11
|
+
import { REPL } from "../screens/REPL.js";
|
|
12
|
+
import {
|
|
13
|
+
TabbedListView
|
|
14
|
+
} from "../components/TabbedListView/index.js";
|
|
15
|
+
function formatDate(date) {
|
|
16
|
+
const now = /* @__PURE__ */ new Date();
|
|
17
|
+
const hours = date.getHours().toString().padStart(2, "0");
|
|
18
|
+
const minutes = date.getMinutes().toString().padStart(2, "0");
|
|
19
|
+
const timeStr = `${hours}:${minutes}`;
|
|
20
|
+
const isToday = date.getDate() === now.getDate() && date.getMonth() === now.getMonth() && date.getFullYear() === now.getFullYear();
|
|
21
|
+
if (isToday) {
|
|
22
|
+
return `${t("commands.sessions.today")} ${timeStr}`;
|
|
23
|
+
}
|
|
24
|
+
const yesterday = new Date(now);
|
|
25
|
+
yesterday.setDate(yesterday.getDate() - 1);
|
|
26
|
+
const isYesterday = date.getDate() === yesterday.getDate() && date.getMonth() === yesterday.getMonth() && date.getFullYear() === yesterday.getFullYear();
|
|
27
|
+
if (isYesterday) {
|
|
28
|
+
return `${t("commands.sessions.yesterday")} ${timeStr}`;
|
|
29
|
+
}
|
|
30
|
+
const month = (date.getMonth() + 1).toString().padStart(2, "0");
|
|
31
|
+
const day = date.getDate().toString().padStart(2, "0");
|
|
32
|
+
return `${month}-${day} ${timeStr}`;
|
|
33
|
+
}
|
|
34
|
+
function truncate(str, maxLength) {
|
|
35
|
+
if (str.length <= maxLength) return str;
|
|
36
|
+
return str.slice(0, maxLength - 3) + "...";
|
|
37
|
+
}
|
|
38
|
+
function wasCleared(log) {
|
|
39
|
+
for (let i = log.messages.length - 1; i >= 0; i--) {
|
|
40
|
+
const msg = log.messages[i];
|
|
41
|
+
if (msg.type === "user") {
|
|
42
|
+
const content = typeof msg.message?.content === "string" ? msg.message.content : Array.isArray(msg.message?.content) ? msg.message.content.filter((c) => c.type === "text").map((c) => c.text).join("") : "";
|
|
43
|
+
return content.includes("<command-name>clear</command-name>");
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
function deduplicateLogForks(logs) {
|
|
49
|
+
const groupedByDate = /* @__PURE__ */ new Map();
|
|
50
|
+
for (const log of logs) {
|
|
51
|
+
const existing = groupedByDate.get(log.date) || [];
|
|
52
|
+
existing.push(log);
|
|
53
|
+
groupedByDate.set(log.date, existing);
|
|
54
|
+
}
|
|
55
|
+
const deduped = [];
|
|
56
|
+
for (const [, group] of groupedByDate) {
|
|
57
|
+
const sorted = group.sort((a, b) => {
|
|
58
|
+
const forkDiff = (b.forkNumber ?? 0) - (a.forkNumber ?? 0);
|
|
59
|
+
if (forkDiff !== 0) return forkDiff;
|
|
60
|
+
return b.modified.getTime() - a.modified.getTime();
|
|
61
|
+
});
|
|
62
|
+
if (sorted[0]) {
|
|
63
|
+
deduped.push(sorted[0]);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return deduped.sort((a, b) => b.modified.getTime() - a.modified.getTime());
|
|
67
|
+
}
|
|
68
|
+
function getLogProjectPath(log) {
|
|
69
|
+
const firstMessage = log.messages[0];
|
|
70
|
+
return firstMessage?.cwd || null;
|
|
71
|
+
}
|
|
72
|
+
function getProjectName(projectPath) {
|
|
73
|
+
return path.basename(projectPath) || projectPath;
|
|
74
|
+
}
|
|
75
|
+
function extractFirstPrompt(log) {
|
|
76
|
+
for (const msg of log.messages) {
|
|
77
|
+
if (msg.type === "user") {
|
|
78
|
+
let content = "";
|
|
79
|
+
if (typeof msg.message?.content === "string") {
|
|
80
|
+
content = msg.message.content;
|
|
81
|
+
} else if (Array.isArray(msg.message?.content)) {
|
|
82
|
+
content = msg.message.content.filter((c) => c.type === "text").map((c) => c.text).join("");
|
|
83
|
+
}
|
|
84
|
+
const trimmed = content.trim();
|
|
85
|
+
if (trimmed.startsWith("<system-reminder>") || trimmed.startsWith("<command-name>") || trimmed.startsWith("<") && trimmed.includes("</")) {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
const firstLine = content.split("\n")[0]?.trim() || "";
|
|
89
|
+
if (firstLine) {
|
|
90
|
+
return firstLine;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return log.firstPrompt || "No prompt";
|
|
95
|
+
}
|
|
96
|
+
function countToolCalls(log) {
|
|
97
|
+
let count = 0;
|
|
98
|
+
for (const msg of log.messages) {
|
|
99
|
+
if (msg.type === "assistant" && Array.isArray(msg.message?.content)) {
|
|
100
|
+
for (const block of msg.message.content) {
|
|
101
|
+
if (block.type === "tool_use") {
|
|
102
|
+
count++;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return count;
|
|
108
|
+
}
|
|
109
|
+
function logToListItem(log) {
|
|
110
|
+
const dateStr = formatDate(log.modified);
|
|
111
|
+
const projectPath = getLogProjectPath(log) || "";
|
|
112
|
+
const projectName = projectPath ? truncate(getProjectName(projectPath), 15) : "";
|
|
113
|
+
const msgCount = log.messageCount;
|
|
114
|
+
const toolCount = countToolCalls(log);
|
|
115
|
+
const firstPrompt = extractFirstPrompt(log);
|
|
116
|
+
return {
|
|
117
|
+
id: log.fullPath,
|
|
118
|
+
// Use fullPath as unique id (date can be duplicated for forks)
|
|
119
|
+
label: truncate(firstPrompt, 45),
|
|
120
|
+
description: projectName || void 0,
|
|
121
|
+
metadata: `${dateStr} \xB7 ${msgCount} msgs \xB7 ${toolCount} tools`,
|
|
122
|
+
category: projectName || "Unknown",
|
|
123
|
+
data: log
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
function ResumeDisplay({
|
|
127
|
+
recentLogs,
|
|
128
|
+
projectLogs,
|
|
129
|
+
allLogs,
|
|
130
|
+
tools,
|
|
131
|
+
commands,
|
|
132
|
+
verbose,
|
|
133
|
+
onClose,
|
|
134
|
+
unmount
|
|
135
|
+
}) {
|
|
136
|
+
const [activeTab, setActiveTab] = useState("recent");
|
|
137
|
+
const [searchQuery, setSearchQuery] = useState("");
|
|
138
|
+
const tabs = useMemo(
|
|
139
|
+
() => [
|
|
140
|
+
{
|
|
141
|
+
id: "recent",
|
|
142
|
+
label: t("commands.sessions.title"),
|
|
143
|
+
badge: recentLogs.length
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
id: "project",
|
|
147
|
+
label: t("commands.sessions.headerProject"),
|
|
148
|
+
badge: projectLogs.length
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
id: "all",
|
|
152
|
+
label: t("common.all"),
|
|
153
|
+
badge: allLogs.length
|
|
154
|
+
}
|
|
155
|
+
],
|
|
156
|
+
[recentLogs.length, projectLogs.length, allLogs.length]
|
|
157
|
+
);
|
|
158
|
+
const items = useMemo(() => {
|
|
159
|
+
let logs;
|
|
160
|
+
switch (activeTab) {
|
|
161
|
+
case "project":
|
|
162
|
+
logs = projectLogs;
|
|
163
|
+
break;
|
|
164
|
+
case "all":
|
|
165
|
+
logs = allLogs;
|
|
166
|
+
break;
|
|
167
|
+
case "recent":
|
|
168
|
+
default:
|
|
169
|
+
logs = recentLogs;
|
|
170
|
+
}
|
|
171
|
+
return logs.map(logToListItem);
|
|
172
|
+
}, [activeTab, recentLogs, projectLogs, allLogs]);
|
|
173
|
+
const handleSelect = async (item) => {
|
|
174
|
+
const log = item.data;
|
|
175
|
+
try {
|
|
176
|
+
onClose();
|
|
177
|
+
const isDefaultModel = await isDefaultSlowAndCapableModel();
|
|
178
|
+
unmount();
|
|
179
|
+
await prepareTerminalForREPL();
|
|
180
|
+
render(
|
|
181
|
+
/* @__PURE__ */ React.createElement(
|
|
182
|
+
REPL,
|
|
183
|
+
{
|
|
184
|
+
messageLogName: log.date,
|
|
185
|
+
initialPrompt: "",
|
|
186
|
+
shouldShowPromptInput: true,
|
|
187
|
+
verbose,
|
|
188
|
+
commands,
|
|
189
|
+
tools,
|
|
190
|
+
initialMessages: deserializeMessages(log.messages, tools),
|
|
191
|
+
initialForkNumber: getNextAvailableLogForkNumber(
|
|
192
|
+
log.date,
|
|
193
|
+
log.forkNumber ?? 1,
|
|
194
|
+
0
|
|
195
|
+
),
|
|
196
|
+
isDefaultModel,
|
|
197
|
+
isResumedConversation: true
|
|
198
|
+
}
|
|
199
|
+
),
|
|
200
|
+
{
|
|
201
|
+
exitOnCtrlC: false
|
|
202
|
+
}
|
|
203
|
+
);
|
|
204
|
+
} catch (e) {
|
|
205
|
+
logError(`Failed to load conversation: ${e}`);
|
|
206
|
+
throw e;
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
return /* @__PURE__ */ React.createElement(
|
|
210
|
+
TabbedListView,
|
|
211
|
+
{
|
|
212
|
+
title: t("commands.resume.title"),
|
|
213
|
+
tabs,
|
|
214
|
+
activeTab,
|
|
215
|
+
onTabChange: setActiveTab,
|
|
216
|
+
items,
|
|
217
|
+
searchEnabled: true,
|
|
218
|
+
searchPlaceholder: t("ui.tabbedList.search"),
|
|
219
|
+
searchQuery,
|
|
220
|
+
onSearchChange: setSearchQuery,
|
|
221
|
+
onSelect: handleSelect,
|
|
222
|
+
onClose,
|
|
223
|
+
emptyText: t("commands.resume.noSessions"),
|
|
224
|
+
footerHint: t("commands.resume.navigationHint"),
|
|
225
|
+
groupByCategory: activeTab === "all"
|
|
226
|
+
}
|
|
227
|
+
);
|
|
228
|
+
}
|
|
5
229
|
var resume_default = {
|
|
6
230
|
type: "local-jsx",
|
|
7
231
|
name: "resume",
|
|
8
|
-
description: "
|
|
232
|
+
description: t("commands.resume.description"),
|
|
9
233
|
isEnabled: true,
|
|
10
234
|
isHidden: false,
|
|
235
|
+
hidePromptInput: true,
|
|
11
236
|
aliases: ["r"],
|
|
12
237
|
userFacingName() {
|
|
13
238
|
return "resume";
|
|
14
239
|
},
|
|
15
240
|
async call(onDone, context) {
|
|
16
241
|
const { commands = [], tools = [], verbose = false } = context.options || {};
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
242
|
+
const cwd = getCwd();
|
|
243
|
+
const allRawLogs = await loadLogList(CACHE_PATHS.messages());
|
|
244
|
+
const validLogs = deduplicateLogForks(
|
|
245
|
+
allRawLogs.filter((log) => !wasCleared(log))
|
|
246
|
+
);
|
|
247
|
+
const recentLogs = validLogs.slice(0, 20);
|
|
248
|
+
const projectLogs = validLogs.filter((log) => {
|
|
249
|
+
const logPath = getLogProjectPath(log);
|
|
250
|
+
return logPath === cwd;
|
|
251
|
+
});
|
|
252
|
+
const allLogs = validLogs.slice(0, 100);
|
|
253
|
+
return /* @__PURE__ */ React.createElement(
|
|
254
|
+
ResumeDisplay,
|
|
255
|
+
{
|
|
256
|
+
recentLogs,
|
|
257
|
+
projectLogs,
|
|
258
|
+
allLogs,
|
|
259
|
+
tools,
|
|
260
|
+
commands,
|
|
261
|
+
verbose,
|
|
262
|
+
onClose: onDone,
|
|
263
|
+
unmount: context.unmount
|
|
264
|
+
}
|
|
29
265
|
);
|
|
30
|
-
return null;
|
|
31
266
|
}
|
|
32
267
|
};
|
|
33
268
|
export {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/commands/resume.tsx"],
|
|
4
|
-
"sourcesContent": ["import * as React from 'react'\nimport type { Command } from '@commands'\nimport { ResumeConversation } from '@screens/ResumeConversation'\nimport { render } from 'ink'\nimport { CACHE_PATHS, loadLogList } from '@utils/log'\n\nexport default {\n type: 'local-jsx',\n name: 'resume',\n description: 'Resume a previous conversation from this project',\n isEnabled: true,\n isHidden: false,\n aliases: ['r'],\n userFacingName() {\n return 'resume'\n },\n async call(onDone, context) {\n const { commands = [], tools = [], verbose = false } = context.options || {}\n const logs = await loadLogList(CACHE_PATHS.messages())\n render(\n <ResumeConversation\n commands={commands}\n context={{ unmount: onDone }}\n logs={logs}\n tools={tools}\n verbose={verbose}\n />,\n )\n // This return is here for type only\n return null\n },\n} satisfies Command\n"],
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["/**\n * Resume Command\n *\n * Resume a previous conversation.\n * Uses TabbedListView with 3 tabs: Recent, Project, All.\n *\n * Usage:\n * /resume # Show sessions with tabs\n * /r # Alias\n */\n\nimport React, { useState, useMemo } from 'react'\nimport { render } from 'ink'\nimport path from 'path'\nimport type { Command } from '@commands'\nimport { t } from '@i18n'\nimport { getCwd } from '@utils/state'\nimport { CACHE_PATHS, loadLogList } from '@utils/log'\nimport type { LogOption } from '@minto-types/logs'\nimport { deserializeMessages } from '@utils/conversationRecovery'\nimport { getNextAvailableLogForkNumber, logError } from '@utils/log'\nimport { isDefaultSlowAndCapableModel } from '@utils/model'\nimport { prepareTerminalForREPL } from '@utils/terminal'\nimport { REPL } from '@screens/REPL'\nimport type { Tool } from '@tool'\nimport {\n TabbedListView,\n type ListItem,\n type TabDefinition,\n} from '@components/TabbedListView'\n\n/**\n * Format date for display\n * - Today: \"Today HH:mm\"\n * - Yesterday: \"Yesterday HH:mm\"\n * - Other: \"MM-DD HH:mm\"\n */\nfunction formatDate(date: Date): string {\n const now = new Date()\n\n const hours = date.getHours().toString().padStart(2, '0')\n const minutes = date.getMinutes().toString().padStart(2, '0')\n const timeStr = `${hours}:${minutes}`\n\n // Check if it's today\n const isToday =\n date.getDate() === now.getDate() &&\n date.getMonth() === now.getMonth() &&\n date.getFullYear() === now.getFullYear()\n\n if (isToday) {\n return `${t('commands.sessions.today')} ${timeStr}`\n }\n\n // Check if it's yesterday\n const yesterday = new Date(now)\n yesterday.setDate(yesterday.getDate() - 1)\n const isYesterday =\n date.getDate() === yesterday.getDate() &&\n date.getMonth() === yesterday.getMonth() &&\n date.getFullYear() === yesterday.getFullYear()\n\n if (isYesterday) {\n return `${t('commands.sessions.yesterday')} ${timeStr}`\n }\n\n // Default format: MM-DD HH:mm\n const month = (date.getMonth() + 1).toString().padStart(2, '0')\n const day = date.getDate().toString().padStart(2, '0')\n return `${month}-${day} ${timeStr}`\n}\n\n/**\n * Truncate string with ellipsis\n */\nfunction truncate(str: string, maxLength: number): string {\n if (str.length <= maxLength) return str\n return str.slice(0, maxLength - 3) + '...'\n}\n\n/**\n * Check if the conversation was discarded (last user message is /clear)\n */\nfunction wasCleared(log: LogOption): boolean {\n // Find the last user message by iterating backwards\n for (let i = log.messages.length - 1; i >= 0; i--) {\n const msg = log.messages[i]\n if (msg.type === 'user') {\n const content =\n typeof msg.message?.content === 'string'\n ? msg.message.content\n : Array.isArray(msg.message?.content)\n ? msg.message.content\n .filter((c: any) => c.type === 'text')\n .map((c: any) => c.text)\n .join('')\n : ''\n // Check if the last user message is a /clear command\n return content.includes('<command-name>clear</command-name>')\n }\n }\n return false\n}\n\n/**\n * Deduplicate logs by keeping only the latest fork for each base conversation.\n * Groups logs by their base date (timestamp without fork number) and keeps\n * the one with the highest fork number or most recent modification.\n */\nfunction deduplicateLogForks(logs: LogOption[]): LogOption[] {\n // Group by base date (the original conversation timestamp)\n const groupedByDate = new Map<string, LogOption[]>()\n\n for (const log of logs) {\n const existing = groupedByDate.get(log.date) || []\n existing.push(log)\n groupedByDate.set(log.date, existing)\n }\n\n // For each group, keep the one with highest fork number (most recent state)\n const deduped: LogOption[] = []\n for (const [, group] of groupedByDate) {\n // Sort by forkNumber desc (higher = more recent), then by modified desc\n const sorted = group.sort((a, b) => {\n const forkDiff = (b.forkNumber ?? 0) - (a.forkNumber ?? 0)\n if (forkDiff !== 0) return forkDiff\n return b.modified.getTime() - a.modified.getTime()\n })\n // Keep only the first one (highest fork / most recently modified)\n if (sorted[0]) {\n deduped.push(sorted[0])\n }\n }\n\n // Re-sort by modified time descending\n return deduped.sort((a, b) => b.modified.getTime() - a.modified.getTime())\n}\n\n/**\n * Get project path from log's first message\n */\nfunction getLogProjectPath(log: LogOption): string | null {\n const firstMessage = log.messages[0]\n return firstMessage?.cwd || null\n}\n\n/**\n * Get project name from path\n */\nfunction getProjectName(projectPath: string): string {\n return path.basename(projectPath) || projectPath\n}\n\n/**\n * Extract clean first prompt from log (skip system messages and XML tags)\n */\nfunction extractFirstPrompt(log: LogOption): string {\n for (const msg of log.messages) {\n if (msg.type === 'user') {\n let content = ''\n if (typeof msg.message?.content === 'string') {\n content = msg.message.content\n } else if (Array.isArray(msg.message?.content)) {\n content = msg.message.content\n .filter((c: any) => c.type === 'text')\n .map((c: any) => c.text)\n .join('')\n }\n\n // Skip system messages (XML tags like <system-reminder>, <command-name>, etc.)\n const trimmed = content.trim()\n if (\n trimmed.startsWith('<system-reminder>') ||\n trimmed.startsWith('<command-name>') ||\n (trimmed.startsWith('<') && trimmed.includes('</'))\n ) {\n continue\n }\n\n // Return first line, cleaned up\n const firstLine = content.split('\\n')[0]?.trim() || ''\n if (firstLine) {\n return firstLine\n }\n }\n }\n return log.firstPrompt || 'No prompt'\n}\n\n/**\n * Count tool calls in a log\n */\nfunction countToolCalls(log: LogOption): number {\n let count = 0\n for (const msg of log.messages) {\n if (msg.type === 'assistant' && Array.isArray(msg.message?.content)) {\n for (const block of msg.message.content) {\n if (block.type === 'tool_use') {\n count++\n }\n }\n }\n }\n return count\n}\n\n/**\n * Convert LogOption to ListItem for TabbedListView\n */\nfunction logToListItem(log: LogOption): ListItem {\n const dateStr = formatDate(log.modified)\n const projectPath = getLogProjectPath(log) || ''\n const projectName = projectPath\n ? truncate(getProjectName(projectPath), 15)\n : ''\n const msgCount = log.messageCount\n const toolCount = countToolCalls(log)\n const firstPrompt = extractFirstPrompt(log)\n\n return {\n id: log.fullPath, // Use fullPath as unique id (date can be duplicated for forks)\n label: truncate(firstPrompt, 45),\n description: projectName || undefined,\n metadata: `${dateStr} \u00B7 ${msgCount} msgs \u00B7 ${toolCount} tools`,\n category: projectName || 'Unknown',\n data: log,\n }\n}\n\ninterface ResumeDisplayProps {\n recentLogs: LogOption[]\n projectLogs: LogOption[]\n allLogs: LogOption[]\n tools: Tool[]\n commands: Command[]\n verbose: boolean | undefined\n onClose: () => void\n unmount: () => void\n}\n\n/**\n * Resume display component with TabbedListView\n */\nfunction ResumeDisplay({\n recentLogs,\n projectLogs,\n allLogs,\n tools,\n commands,\n verbose,\n onClose,\n unmount,\n}: ResumeDisplayProps) {\n const [activeTab, setActiveTab] = useState('recent')\n const [searchQuery, setSearchQuery] = useState('')\n\n // Tab definitions\n const tabs: TabDefinition[] = useMemo(\n () => [\n {\n id: 'recent',\n label: t('commands.sessions.title'),\n badge: recentLogs.length,\n },\n {\n id: 'project',\n label: t('commands.sessions.headerProject'),\n badge: projectLogs.length,\n },\n {\n id: 'all',\n label: t('common.all'),\n badge: allLogs.length,\n },\n ],\n [recentLogs.length, projectLogs.length, allLogs.length],\n )\n\n // Get items based on active tab\n const items: ListItem[] = useMemo(() => {\n let logs: LogOption[]\n\n switch (activeTab) {\n case 'project':\n logs = projectLogs\n break\n case 'all':\n logs = allLogs\n break\n case 'recent':\n default:\n logs = recentLogs\n }\n\n return logs.map(logToListItem)\n }, [activeTab, recentLogs, projectLogs, allLogs])\n\n // Handle item selection - load and resume conversation\n const handleSelect = async (item: ListItem) => {\n const log = item.data as LogOption\n\n try {\n // First close the selection UI\n onClose()\n\n // Check if using default model before unmounting\n const isDefaultModel = await isDefaultSlowAndCapableModel()\n\n // Unmount current REPL before creating new one\n // This prevents two Ink instances from conflicting\n unmount()\n\n // Clear screen and prepare terminal for REPL\n await prepareTerminalForREPL()\n\n render(\n <REPL\n messageLogName={log.date}\n initialPrompt=\"\"\n shouldShowPromptInput={true}\n verbose={verbose}\n commands={commands}\n tools={tools}\n initialMessages={deserializeMessages(log.messages, tools)}\n initialForkNumber={getNextAvailableLogForkNumber(\n log.date,\n log.forkNumber ?? 1,\n 0,\n )}\n isDefaultModel={isDefaultModel}\n isResumedConversation={true}\n />,\n {\n exitOnCtrlC: false,\n },\n )\n } catch (e) {\n logError(`Failed to load conversation: ${e}`)\n throw e\n }\n }\n\n return (\n <TabbedListView\n title={t('commands.resume.title')}\n tabs={tabs}\n activeTab={activeTab}\n onTabChange={setActiveTab}\n items={items}\n searchEnabled={true}\n searchPlaceholder={t('ui.tabbedList.search')}\n searchQuery={searchQuery}\n onSearchChange={setSearchQuery}\n onSelect={handleSelect}\n onClose={onClose}\n emptyText={t('commands.resume.noSessions')}\n footerHint={t('commands.resume.navigationHint')}\n groupByCategory={activeTab === 'all'}\n />\n )\n}\n\nexport default {\n type: 'local-jsx',\n name: 'resume',\n description: t('commands.resume.description'),\n isEnabled: true,\n isHidden: false,\n hidePromptInput: true,\n aliases: ['r'],\n userFacingName() {\n return 'resume'\n },\n async call(onDone, context) {\n const { commands = [], tools = [], verbose = false } = context.options || {}\n const cwd = getCwd()\n\n // Load all logs\n const allRawLogs = await loadLogList(CACHE_PATHS.messages())\n\n // Filter out cleared conversations, then deduplicate forks\n // (keep only the latest fork for each base conversation)\n const validLogs = deduplicateLogForks(\n allRawLogs.filter(log => !wasCleared(log)),\n )\n\n // Recent: first 20 logs (already sorted by modified time)\n const recentLogs = validLogs.slice(0, 20)\n\n // Project: logs from current project\n const projectLogs = validLogs.filter(log => {\n const logPath = getLogProjectPath(log)\n return logPath === cwd\n })\n\n // All: all valid logs (up to 100)\n const allLogs = validLogs.slice(0, 100)\n\n return (\n <ResumeDisplay\n recentLogs={recentLogs}\n projectLogs={projectLogs}\n allLogs={allLogs}\n tools={tools}\n commands={commands}\n verbose={verbose}\n onClose={onDone}\n unmount={context.unmount}\n />\n )\n },\n} satisfies Command\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/commands/sandbox.js
CHANGED
|
@@ -1,68 +1,180 @@
|
|
|
1
1
|
import React, { useState } from "react";
|
|
2
|
-
import { Box, Text, useInput } from "ink";
|
|
3
2
|
import {
|
|
4
3
|
getSandboxController
|
|
5
4
|
} from "../services/sandbox/index.js";
|
|
6
5
|
import { getCwd } from "../utils/state.js";
|
|
7
6
|
import { SEMANTIC_COLORS } from "../constants/colors.js";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
7
|
+
import { InfoPanel } from "../components/InfoPanel/index.js";
|
|
8
|
+
import { t } from "../i18n/index.js";
|
|
9
|
+
function getImplColor(impl) {
|
|
10
|
+
switch (impl) {
|
|
11
|
+
case "seatbelt":
|
|
12
|
+
return SEMANTIC_COLORS.success;
|
|
13
|
+
case "bubblewrap":
|
|
14
|
+
return SEMANTIC_COLORS.info;
|
|
15
|
+
case "docker":
|
|
16
|
+
return "#FFB86C";
|
|
17
|
+
case "none":
|
|
18
|
+
return SEMANTIC_COLORS.dim;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function getImplDescription(impl) {
|
|
22
|
+
switch (impl) {
|
|
23
|
+
case "seatbelt":
|
|
24
|
+
return "macOS sandbox-exec (built-in)";
|
|
25
|
+
case "bubblewrap":
|
|
26
|
+
return "Linux bubblewrap container";
|
|
27
|
+
case "docker":
|
|
28
|
+
return "Docker container isolation";
|
|
29
|
+
case "none":
|
|
30
|
+
return "No sandbox available";
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function buildSections(config, implementation, isAvailable, showHelp) {
|
|
34
|
+
const sections = [];
|
|
35
|
+
const statusValue = config.enabled && isAvailable ? `\u25CF ${t("commands.sandbox.enabled")}` : `\u25CB ${t("commands.sandbox.disabled")}`;
|
|
36
|
+
const statusColor = config.enabled && isAvailable ? SEMANTIC_COLORS.success : SEMANTIC_COLORS.error;
|
|
37
|
+
const statusItems = [
|
|
38
|
+
{ label: "Status", value: statusValue, valueColor: statusColor },
|
|
39
|
+
{
|
|
40
|
+
label: t("commands.sandbox.implementation"),
|
|
41
|
+
value: `${implementation.toUpperCase()} - ${getImplDescription(implementation)}`,
|
|
42
|
+
valueColor: getImplColor(implementation)
|
|
23
43
|
}
|
|
44
|
+
];
|
|
45
|
+
if (!isAvailable) {
|
|
46
|
+
statusItems.push({
|
|
47
|
+
label: "\u26A0",
|
|
48
|
+
value: t("commands.sandbox.notAvailable"),
|
|
49
|
+
valueColor: "#FFB86C"
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
sections.push({
|
|
53
|
+
title: t("commands.sandbox.statusSection"),
|
|
54
|
+
items: statusItems
|
|
24
55
|
});
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
56
|
+
const fsItems = [
|
|
57
|
+
{
|
|
58
|
+
label: "Write Allowed",
|
|
59
|
+
value: config.filesystem.writeAllowed.length > 0 ? config.filesystem.writeAllowed.slice(0, 3).join(", ") + (config.filesystem.writeAllowed.length > 3 ? " ..." : "") : "None",
|
|
60
|
+
valueColor: config.filesystem.writeAllowed.length > 0 ? SEMANTIC_COLORS.success : SEMANTIC_COLORS.error
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
label: "Denied Paths",
|
|
64
|
+
value: config.filesystem.denied.length > 0 ? `${config.filesystem.denied.length} paths blocked` : "None blocked",
|
|
65
|
+
valueColor: config.filesystem.denied.length > 0 ? SEMANTIC_COLORS.error : SEMANTIC_COLORS.success
|
|
35
66
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
return "No sandbox available";
|
|
67
|
+
];
|
|
68
|
+
sections.push({
|
|
69
|
+
title: t("commands.sandbox.filesystemPolicy"),
|
|
70
|
+
items: fsItems
|
|
71
|
+
});
|
|
72
|
+
const netItems = [
|
|
73
|
+
{
|
|
74
|
+
label: "Network Access",
|
|
75
|
+
value: config.network.blockAll ? "Blocked" : "Allowed",
|
|
76
|
+
valueColor: config.network.blockAll ? SEMANTIC_COLORS.error : SEMANTIC_COLORS.success
|
|
47
77
|
}
|
|
78
|
+
];
|
|
79
|
+
if (!config.network.blockAll && config.network.allowedDomains.length > 0) {
|
|
80
|
+
netItems.push({
|
|
81
|
+
label: "Allowed Domains",
|
|
82
|
+
value: config.network.allowedDomains.slice(0, 3).join(", ") + (config.network.allowedDomains.length > 3 ? " ..." : "")
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
sections.push({ title: t("commands.sandbox.networkPolicy"), items: netItems });
|
|
86
|
+
sections.push({
|
|
87
|
+
title: t("commands.sandbox.processPolicy"),
|
|
88
|
+
items: [
|
|
89
|
+
{
|
|
90
|
+
label: "Max Exec Time",
|
|
91
|
+
value: `${config.process.maxExecutionTime / 1e3}s`
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
label: "Excluded Cmds",
|
|
95
|
+
value: config.process.excludedCommands.length > 0 ? config.process.excludedCommands.slice(0, 3).join(", ") + (config.process.excludedCommands.length > 3 ? " ..." : "") : "None"
|
|
96
|
+
}
|
|
97
|
+
]
|
|
98
|
+
});
|
|
99
|
+
if (showHelp) {
|
|
100
|
+
sections.push({
|
|
101
|
+
title: t("commands.sandbox.helpSection"),
|
|
102
|
+
items: [
|
|
103
|
+
{
|
|
104
|
+
label: "\u2022 seatbelt",
|
|
105
|
+
value: "macOS built-in sandbox-exec",
|
|
106
|
+
valueColor: SEMANTIC_COLORS.success
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
label: "\u2022 bubblewrap",
|
|
110
|
+
value: "Linux container isolation (bwrap)",
|
|
111
|
+
valueColor: SEMANTIC_COLORS.info
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
label: "\u2022 docker",
|
|
115
|
+
value: "Docker container isolation",
|
|
116
|
+
valueColor: "#FFB86C"
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
label: "\u2022 none",
|
|
120
|
+
value: "No sandboxing (not recommended)",
|
|
121
|
+
valueColor: SEMANTIC_COLORS.dim
|
|
122
|
+
}
|
|
123
|
+
]
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
return sections;
|
|
127
|
+
}
|
|
128
|
+
const SandboxView = ({
|
|
129
|
+
initialConfig,
|
|
130
|
+
implementation,
|
|
131
|
+
initialAvailable,
|
|
132
|
+
onDone
|
|
133
|
+
}) => {
|
|
134
|
+
const [config, setConfig] = useState(initialConfig);
|
|
135
|
+
const [isAvailable, setIsAvailable] = useState(initialAvailable);
|
|
136
|
+
const [showHelp, setShowHelp] = useState(false);
|
|
137
|
+
const cwd = getCwd();
|
|
138
|
+
const controller = getSandboxController(cwd);
|
|
139
|
+
const handleToggle = async () => {
|
|
140
|
+
const newEnabled = !config.enabled;
|
|
141
|
+
controller.updateConfig({ enabled: newEnabled });
|
|
142
|
+
const newConfig = controller.getConfig();
|
|
143
|
+
setConfig(newConfig);
|
|
144
|
+
const available = await controller.isAvailable();
|
|
145
|
+
setIsAvailable(available);
|
|
48
146
|
};
|
|
49
|
-
|
|
50
|
-
|
|
147
|
+
const sections = buildSections(config, implementation, isAvailable, showHelp);
|
|
148
|
+
const actions = [
|
|
51
149
|
{
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
150
|
+
key: "t",
|
|
151
|
+
keyLabel: "T",
|
|
152
|
+
description: t("commands.sandbox.toggle"),
|
|
153
|
+
onPress: handleToggle
|
|
56
154
|
},
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
155
|
+
{
|
|
156
|
+
key: "h",
|
|
157
|
+
keyLabel: "H",
|
|
158
|
+
description: t("commands.sandbox.toggleHelp"),
|
|
159
|
+
onPress: () => setShowHelp(!showHelp)
|
|
160
|
+
}
|
|
161
|
+
];
|
|
162
|
+
return /* @__PURE__ */ React.createElement(
|
|
163
|
+
InfoPanel,
|
|
164
|
+
{
|
|
165
|
+
title: t("commands.sandbox.title"),
|
|
166
|
+
sections,
|
|
167
|
+
actions,
|
|
168
|
+
onClose: onDone
|
|
169
|
+
}
|
|
170
|
+
);
|
|
60
171
|
};
|
|
61
172
|
const command = {
|
|
62
173
|
name: "sandbox",
|
|
63
|
-
description: "
|
|
174
|
+
description: t("commands.sandbox.description"),
|
|
64
175
|
isEnabled: true,
|
|
65
176
|
isHidden: false,
|
|
177
|
+
hidePromptInput: true,
|
|
66
178
|
type: "local-jsx",
|
|
67
179
|
userFacingName() {
|
|
68
180
|
return this.name;
|
|
@@ -73,29 +185,15 @@ const command = {
|
|
|
73
185
|
const initialConfig = controller.getConfig();
|
|
74
186
|
const implementation = controller.getImplementationType();
|
|
75
187
|
const initialAvailable = await controller.isAvailable();
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
setIsAvailable(available);
|
|
86
|
-
};
|
|
87
|
-
return /* @__PURE__ */ React.createElement(
|
|
88
|
-
SandboxStatus,
|
|
89
|
-
{
|
|
90
|
-
config,
|
|
91
|
-
implementation,
|
|
92
|
-
isAvailable,
|
|
93
|
-
onToggle: handleToggle,
|
|
94
|
-
onDone
|
|
95
|
-
}
|
|
96
|
-
);
|
|
97
|
-
};
|
|
98
|
-
return /* @__PURE__ */ React.createElement(SandboxWrapper, null);
|
|
188
|
+
return /* @__PURE__ */ React.createElement(
|
|
189
|
+
SandboxView,
|
|
190
|
+
{
|
|
191
|
+
initialConfig,
|
|
192
|
+
implementation,
|
|
193
|
+
initialAvailable,
|
|
194
|
+
onDone
|
|
195
|
+
}
|
|
196
|
+
);
|
|
99
197
|
}
|
|
100
198
|
};
|
|
101
199
|
var sandbox_default = command;
|