min-agent 0.2.1 → 0.4.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/README.md +242 -31
- package/dist/agent.js +1233 -485
- package/dist/assistant-stream.js +11 -7
- package/dist/cli/commands/chat.js +10 -0
- package/dist/cli/commands/exec.js +32 -0
- package/dist/cli/commands/history.js +58 -0
- package/dist/cli/commands/index.js +224 -0
- package/dist/cli/commands/init.js +18 -0
- package/dist/cli/commands/mcp.js +173 -0
- package/dist/cli/commands/memory.js +69 -0
- package/dist/cli/commands/models.js +21 -0
- package/dist/cli/commands/permission.js +12 -0
- package/dist/cli/commands/rules.js +33 -0
- package/dist/cli/commands/sandbox.js +13 -0
- package/dist/cli/commands/serve.js +9 -0
- package/dist/cli/commands/setup.js +4 -0
- package/dist/cli/commands/shared.js +16 -0
- package/dist/cli/commands/skills.js +119 -0
- package/dist/cli/commands/update.js +7 -0
- package/dist/cli/commands/write-config.js +30 -0
- package/dist/cli/errors.js +36 -0
- package/dist/cli/exec-prompt.js +26 -0
- package/dist/cli/option-helpers.js +53 -0
- package/dist/cli/program.js +180 -0
- package/dist/cli.js +7 -632
- package/dist/clipboard.js +59 -23
- package/dist/code-mode.js +35 -17
- package/dist/compaction.js +457 -169
- package/dist/config.js +298 -38
- package/dist/confirm.js +105 -9
- package/dist/context-window.js +156 -75
- package/dist/doom-loop.js +268 -26
- package/dist/fetch-timeout.js +152 -0
- package/dist/http-approvals.js +60 -0
- package/dist/http.js +119 -0
- package/dist/instructions.js +72 -33
- package/dist/logger.js +95 -0
- package/dist/markdown.js +35 -50
- package/dist/mcp.js +847 -102
- package/dist/memory.js +128 -45
- package/dist/output.js +42 -31
- package/dist/paste-handler.js +3 -3
- package/dist/permission-cli.js +43 -0
- package/dist/plugins.js +76 -11
- package/dist/pricing.js +119 -0
- package/dist/provider.js +34 -15
- package/dist/question-format.js +60 -0
- package/dist/sandbox-cli.js +82 -0
- package/dist/sandbox.js +403 -0
- package/dist/save-throttle.js +45 -0
- package/dist/serve/common.js +404 -0
- package/dist/serve/routes-chat.js +347 -0
- package/dist/serve/routes-mcp.js +212 -0
- package/dist/serve/routes-memory.js +66 -0
- package/dist/serve/routes-meta.js +205 -0
- package/dist/serve/routes-sessions.js +61 -0
- package/dist/serve/routes-skills.js +70 -0
- package/dist/serve.js +74 -635
- package/dist/sessions.js +197 -15
- package/dist/skills.js +531 -77
- package/dist/synthetic.js +7 -0
- package/dist/title-gen.js +9 -2
- package/dist/token-display.js +36 -0
- package/dist/tool-display.js +178 -0
- package/dist/tool-output.js +53 -46
- package/dist/tools/apply_patch.js +265 -0
- package/dist/tools/atomic-file.js +35 -0
- package/dist/tools/backend.js +61 -0
- package/dist/tools/bash.js +186 -71
- package/dist/tools/code_search.js +13 -6
- package/dist/tools/edit.js +26 -9
- package/dist/tools/explore.js +144 -16
- package/dist/tools/glob.js +7 -3
- package/dist/tools/grep.js +153 -14
- package/dist/tools/index.js +9 -24
- package/dist/tools/question.js +31 -30
- package/dist/tools/read.js +77 -15
- package/dist/tools/search-searxng.js +223 -0
- package/dist/tools/search-serper.js +189 -0
- package/dist/tools/task.js +100 -33
- package/dist/tools/todo.js +178 -67
- package/dist/tools/web_fetch.js +158 -46
- package/dist/tools/web_search.js +217 -29
- package/dist/tools/write.js +34 -11
- package/dist/tui/App.js +89 -6
- package/dist/tui/ConfirmBar.js +57 -4
- package/dist/tui/InputBar.js +504 -44
- package/dist/tui/MessageList.js +674 -20
- package/dist/tui/ModelPicker.js +113 -0
- package/dist/tui/QuestionBar.js +136 -0
- package/dist/tui/SessionPicker.js +79 -0
- package/dist/tui/StatusBar.js +14 -12
- package/dist/tui/agent-runner.js +223 -0
- package/dist/tui/caret-pos.js +177 -0
- package/dist/tui/caret.js +69 -0
- package/dist/tui/click-count.js +13 -0
- package/dist/tui/diff-view.js +61 -0
- package/dist/tui/drag-state.js +49 -0
- package/dist/tui/hydrate.js +129 -0
- package/dist/tui/index.js +189 -31
- package/dist/tui/input-history.js +125 -0
- package/dist/tui/layout.js +88 -0
- package/dist/tui/mouse.js +46 -0
- package/dist/tui/prompt-queue.js +24 -0
- package/dist/tui/selection.js +226 -0
- package/dist/tui/session-switch.js +28 -0
- package/dist/tui/slash-commands.js +106 -0
- package/dist/tui/slash-handler.js +545 -0
- package/dist/tui/text-width.js +113 -0
- package/dist/tui/theme.js +12 -0
- package/dist/tui/token-info.js +7 -0
- package/dist/tui/tool-children.js +19 -0
- package/dist/tui/undo-stack.js +14 -0
- package/dist/tui/use-sgr-mouse.js +29 -0
- package/dist/tui-chat.js +346 -330
- package/dist/updater.js +116 -0
- package/dist/xml-search.js +194 -0
- package/docs/API.md +410 -32
- package/docs/superpowers/plans/2026-08-16-batch1-tui-improvements.md +1510 -0
- package/docs/superpowers/plans/2026-08-16-batch2-cli-tools-api.md +2105 -0
- package/docs/superpowers/plans/2026-08-16-batch3-config-engineering.md +1595 -0
- package/docs/superpowers/plans/2026-08-16-input-caret.md +782 -0
- package/docs/superpowers/plans/2026-08-20-tui-completeness.md +873 -0
- package/docs/superpowers/plans/2026-08-20-unified-tui-default.md +631 -0
- package/docs/superpowers/specs/2026-08-16-batch1-tui-improvements-design.md +183 -0
- package/docs/superpowers/specs/2026-08-16-batch2-cli-tools-api-design.md +220 -0
- package/docs/superpowers/specs/2026-08-16-batch3-config-engineering-design.md +196 -0
- package/docs/superpowers/specs/2026-08-16-input-caret-design.md +63 -0
- package/docs/superpowers/specs/2026-08-17-mouse-selection-design.md +116 -0
- package/docs/superpowers/specs/2026-08-20-config-http-alignment-design.md +47 -0
- package/docs/superpowers/specs/2026-08-20-mcp-plugins-alignment-design.md +37 -0
- package/docs/superpowers/specs/2026-08-20-sandbox-permissions-design.md +68 -0
- package/docs/superpowers/specs/2026-08-20-tui-completeness-design.md +273 -0
- package/docs/superpowers/specs/2026-08-20-unified-tui-default-design.md +165 -0
- package/package.json +12 -8
- package/skills/self-config/SKILL.md +90 -0
- package/skills/self-config/reference.md +149 -0
package/dist/pricing.js
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { readFileSync, mkdirSync, existsSync } from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { atomicWriteFileSync } from "./tools/atomic-file.js";
|
|
4
|
+
import { getConfigDir, loadConfig } from "./config.js";
|
|
5
|
+
const CACHE_TTL = 7 * 24 * 60 * 60 * 1000;
|
|
6
|
+
const memoryCache = new Map();
|
|
7
|
+
const inFlight = new Map();
|
|
8
|
+
const NEGATIVE_TTL = 60 * 60 * 1000;
|
|
9
|
+
const negativeCache = new Map();
|
|
10
|
+
function cacheFile() {
|
|
11
|
+
return path.join(getConfigDir(), "pricing-cache.json");
|
|
12
|
+
}
|
|
13
|
+
function loadDiskCache() {
|
|
14
|
+
const file = cacheFile();
|
|
15
|
+
if (!existsSync(file))
|
|
16
|
+
return {};
|
|
17
|
+
try {
|
|
18
|
+
return JSON.parse(readFileSync(file, "utf-8"));
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return {};
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function saveCache(cache) {
|
|
25
|
+
const file = cacheFile();
|
|
26
|
+
mkdirSync(path.dirname(file), { recursive: true });
|
|
27
|
+
atomicWriteFileSync(file, JSON.stringify(cache));
|
|
28
|
+
}
|
|
29
|
+
function getCached(modelId) {
|
|
30
|
+
const memory = memoryCache.get(modelId);
|
|
31
|
+
if (memory && Date.now() - memory.timestamp <= CACHE_TTL)
|
|
32
|
+
return memory;
|
|
33
|
+
const disk = loadDiskCache()[modelId];
|
|
34
|
+
if (disk && Date.now() - disk.timestamp <= CACHE_TTL) {
|
|
35
|
+
memoryCache.set(modelId, disk);
|
|
36
|
+
return disk;
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
function setCache(modelId, price) {
|
|
41
|
+
const entry = { price, timestamp: Date.now() };
|
|
42
|
+
memoryCache.set(modelId, entry);
|
|
43
|
+
const cache = loadDiskCache();
|
|
44
|
+
cache[modelId] = entry;
|
|
45
|
+
saveCache(cache);
|
|
46
|
+
}
|
|
47
|
+
function priceFromConfig(modelId) {
|
|
48
|
+
const override = loadConfig().pricing?.[modelId];
|
|
49
|
+
if (!override)
|
|
50
|
+
return null;
|
|
51
|
+
if (typeof override.inputPerMillion !== "number" || typeof override.outputPerMillion !== "number")
|
|
52
|
+
return null;
|
|
53
|
+
return { inputPerMillion: override.inputPerMillion, outputPerMillion: override.outputPerMillion };
|
|
54
|
+
}
|
|
55
|
+
async function fetchFromModelsDev(modelId) {
|
|
56
|
+
try {
|
|
57
|
+
const response = await fetch("https://models.dev/api.json", { signal: AbortSignal.timeout(10000) });
|
|
58
|
+
if (!response.ok)
|
|
59
|
+
return null;
|
|
60
|
+
const providers = (await response.json());
|
|
61
|
+
for (const provider of Object.values(providers)) {
|
|
62
|
+
if (!provider.models)
|
|
63
|
+
continue;
|
|
64
|
+
for (const [id, model] of Object.entries(provider.models)) {
|
|
65
|
+
if (id === modelId || id.endsWith(`/${modelId}`) || modelId.endsWith(`/${id}`)) {
|
|
66
|
+
const p = model?.pricing;
|
|
67
|
+
if (p && typeof p.input === "number" && typeof p.output === "number") {
|
|
68
|
+
return { inputPerMillion: p.input, outputPerMillion: p.output };
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
export async function getModelPrice(modelId) {
|
|
80
|
+
const override = priceFromConfig(modelId);
|
|
81
|
+
if (override)
|
|
82
|
+
return override;
|
|
83
|
+
const cached = getCached(modelId);
|
|
84
|
+
if (cached)
|
|
85
|
+
return cached.price;
|
|
86
|
+
const negAt = negativeCache.get(modelId);
|
|
87
|
+
if (negAt !== undefined && Date.now() - negAt <= NEGATIVE_TTL)
|
|
88
|
+
return null;
|
|
89
|
+
const pending = inFlight.get(modelId);
|
|
90
|
+
if (pending)
|
|
91
|
+
return pending;
|
|
92
|
+
const probing = fetchFromModelsDev(modelId).then((price) => {
|
|
93
|
+
if (price) {
|
|
94
|
+
setCache(modelId, price);
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
negativeCache.set(modelId, Date.now());
|
|
98
|
+
}
|
|
99
|
+
return price;
|
|
100
|
+
});
|
|
101
|
+
inFlight.set(modelId, probing);
|
|
102
|
+
try {
|
|
103
|
+
return await probing;
|
|
104
|
+
}
|
|
105
|
+
finally {
|
|
106
|
+
inFlight.delete(modelId);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
export function estimateCost(usage, price) {
|
|
110
|
+
if (!price)
|
|
111
|
+
return null;
|
|
112
|
+
const cost = (usage.inputTokens / 1_000_000) * price.inputPerMillion + (usage.outputTokens / 1_000_000) * price.outputPerMillion;
|
|
113
|
+
return cost > 0 ? cost : null;
|
|
114
|
+
}
|
|
115
|
+
export function formatCost(cost) {
|
|
116
|
+
if (cost === null)
|
|
117
|
+
return "--";
|
|
118
|
+
return cost >= 0.1 ? `$${cost.toFixed(3)}` : `$${cost.toFixed(4)}`;
|
|
119
|
+
}
|
package/dist/provider.js
CHANGED
|
@@ -1,41 +1,60 @@
|
|
|
1
1
|
import { createOpenAI } from "@ai-sdk/openai";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
import { getEffectiveConfig, getActiveProvider, normalizeOllamaBaseURL } from "./config.js";
|
|
3
|
+
import { createTimeoutFetch, DEFAULT_FIRST_BYTE_TIMEOUT_MS, DEFAULT_STREAM_IDLE_TIMEOUT_MS, } from "./fetch-timeout.js";
|
|
4
|
+
function positiveMs(raw, fallback) {
|
|
5
|
+
const value = typeof raw === "string" ? Number(raw) : raw;
|
|
6
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0)
|
|
7
|
+
return fallback;
|
|
8
|
+
return value;
|
|
6
9
|
}
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
|
|
10
|
+
/** Timeout/trace-aware fetch shared by every provider client. */
|
|
11
|
+
export function modelFetch() {
|
|
12
|
+
const cfg = getEffectiveConfig();
|
|
13
|
+
return createTimeoutFetch({
|
|
14
|
+
firstByteTimeoutMs: positiveMs(process.env.MIN_AGENT_REQUEST_TIMEOUT_MS ?? cfg.agent?.requestTimeoutMs, DEFAULT_FIRST_BYTE_TIMEOUT_MS),
|
|
15
|
+
idleTimeoutMs: positiveMs(process.env.MIN_AGENT_STREAM_IDLE_TIMEOUT_MS ?? cfg.agent?.streamIdleTimeoutMs, DEFAULT_STREAM_IDLE_TIMEOUT_MS),
|
|
16
|
+
trace: process.env.MIN_AGENT_TRACE === "1" || process.env.MIN_AGENT_TRACE === "true",
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
export function resolveModelForProvider(provider, modelId) {
|
|
20
|
+
if (!provider.baseURL || !provider.apiKey) {
|
|
11
21
|
throw new Error("Not configured. Run: min-agent setup");
|
|
12
22
|
}
|
|
13
23
|
const id = modelId ?? provider.defaultModel;
|
|
14
24
|
if (!id) {
|
|
15
25
|
throw new Error("No model specified. Run: min-agent setup");
|
|
16
26
|
}
|
|
17
|
-
const
|
|
18
|
-
switch (type) {
|
|
27
|
+
const fetchImpl = modelFetch();
|
|
28
|
+
switch (provider.type ?? "openai-compatible") {
|
|
19
29
|
case "openai": {
|
|
20
|
-
const client = createOpenAI({ apiKey: provider.apiKey });
|
|
30
|
+
const client = createOpenAI({ apiKey: provider.apiKey, fetch: fetchImpl });
|
|
21
31
|
return client.chat(id);
|
|
22
32
|
}
|
|
23
33
|
case "ollama": {
|
|
24
|
-
// Ollama exposes an OpenAI-compatible API at /v1.
|
|
25
|
-
// Accept both "...:11434" and "...:11434/v1" in user config.
|
|
26
34
|
const client = createOpenAI({
|
|
27
35
|
baseURL: normalizeOllamaBaseURL(provider.baseURL),
|
|
28
36
|
apiKey: provider.apiKey || "ollama",
|
|
37
|
+
fetch: fetchImpl,
|
|
29
38
|
});
|
|
30
39
|
return client.chat(id);
|
|
31
40
|
}
|
|
32
|
-
case "openai-compatible":
|
|
33
|
-
default: {
|
|
41
|
+
case "openai-compatible": {
|
|
34
42
|
const client = createOpenAI({
|
|
35
43
|
baseURL: provider.baseURL,
|
|
36
44
|
apiKey: provider.apiKey,
|
|
45
|
+
fetch: fetchImpl,
|
|
37
46
|
});
|
|
38
47
|
return client.chat(id);
|
|
39
48
|
}
|
|
49
|
+
default:
|
|
50
|
+
throw new Error(`Unknown provider type: ${provider.type}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
export function resolveModel(modelId, providerName) {
|
|
54
|
+
const config = getEffectiveConfig();
|
|
55
|
+
const provider = providerName ? config.providers?.find((p) => p.name === providerName) : getActiveProvider(config);
|
|
56
|
+
if (!provider) {
|
|
57
|
+
throw new Error(providerName ? `Provider "${providerName}" not found` : "Not configured. Run: min-agent setup");
|
|
40
58
|
}
|
|
59
|
+
return resolveModelForProvider(provider, modelId);
|
|
41
60
|
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
const OPTION_TEXT_KEYS = ["label", "description", "title", "text", "value", "content", "prompt"];
|
|
2
|
+
function firstText(rec, keys) {
|
|
3
|
+
const parts = [];
|
|
4
|
+
const seen = new Set();
|
|
5
|
+
for (const key of keys) {
|
|
6
|
+
const v = rec[key];
|
|
7
|
+
if (typeof v !== "string")
|
|
8
|
+
continue;
|
|
9
|
+
const text = v.trim();
|
|
10
|
+
if (!text || seen.has(text))
|
|
11
|
+
continue;
|
|
12
|
+
seen.add(text);
|
|
13
|
+
parts.push(text);
|
|
14
|
+
}
|
|
15
|
+
return parts;
|
|
16
|
+
}
|
|
17
|
+
/** Pull a user-visible label out of a model-supplied question option. */
|
|
18
|
+
export function optionText(item) {
|
|
19
|
+
if (item == null)
|
|
20
|
+
return null;
|
|
21
|
+
if (typeof item === "string") {
|
|
22
|
+
const text = item.trim();
|
|
23
|
+
return text.length > 0 ? text : null;
|
|
24
|
+
}
|
|
25
|
+
if (typeof item === "number" || typeof item === "boolean")
|
|
26
|
+
return String(item);
|
|
27
|
+
if (typeof item !== "object" || Array.isArray(item))
|
|
28
|
+
return null;
|
|
29
|
+
const rec = item;
|
|
30
|
+
const parts = firstText(rec, OPTION_TEXT_KEYS);
|
|
31
|
+
if (parts.length > 0)
|
|
32
|
+
return parts.join(" — ");
|
|
33
|
+
const key = rec.key;
|
|
34
|
+
return typeof key === "string" && key.trim() ? key.trim() : null;
|
|
35
|
+
}
|
|
36
|
+
export function normalizeQuestionPrompt(question) {
|
|
37
|
+
if (typeof question === "string")
|
|
38
|
+
return question;
|
|
39
|
+
return optionText(question) ?? "";
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Models often send `{label, description, key}` (Claude-style) instead of
|
|
43
|
+
* `string[]`. Normalize to display strings so the TUI never calls string
|
|
44
|
+
* methods on objects.
|
|
45
|
+
*/
|
|
46
|
+
export function normalizeQuestionOptions(options) {
|
|
47
|
+
if (options == null)
|
|
48
|
+
return [];
|
|
49
|
+
const list = Array.isArray(options) ? options : [options];
|
|
50
|
+
const out = [];
|
|
51
|
+
const seen = new Set();
|
|
52
|
+
for (const item of list) {
|
|
53
|
+
const text = optionText(item);
|
|
54
|
+
if (!text || seen.has(text))
|
|
55
|
+
continue;
|
|
56
|
+
seen.add(text);
|
|
57
|
+
out.push(text);
|
|
58
|
+
}
|
|
59
|
+
return out;
|
|
60
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { takeScopeFlags } from "./memory.js";
|
|
2
|
+
import { getSandboxSnapshot, setSandboxConfig } from "./config.js";
|
|
3
|
+
import { parseNetworkPolicy, parseSandboxMode, getEffectiveSandboxPolicy, sandboxEnforcementCaveat, sandboxModeLabel, sandboxStatusLabel, setSandboxOverride, } from "./sandbox.js";
|
|
4
|
+
export const SANDBOX_CLI_USAGE = [
|
|
5
|
+
"Usage: min-agent sandbox [off|workspace|strict] [--project|--global]",
|
|
6
|
+
" min-agent sandbox network allow|deny [--project|--global]",
|
|
7
|
+
" min-agent --sandbox off|workspace|strict [--project|--global]",
|
|
8
|
+
].join("\n");
|
|
9
|
+
function sourceLabel(source) {
|
|
10
|
+
return source === "cli"
|
|
11
|
+
? "this run"
|
|
12
|
+
: source === "env"
|
|
13
|
+
? "environment variable"
|
|
14
|
+
: source === "project"
|
|
15
|
+
? "project"
|
|
16
|
+
: source === "global"
|
|
17
|
+
? "global"
|
|
18
|
+
: "default";
|
|
19
|
+
}
|
|
20
|
+
function scopeLabel(scope) {
|
|
21
|
+
return scope === "project" ? "project" : "global";
|
|
22
|
+
}
|
|
23
|
+
export function runSandboxCli(input) {
|
|
24
|
+
const { scope: posScope, rest } = takeScopeFlags(input.positionals);
|
|
25
|
+
const scope = posScope ?? input.scope ?? "global";
|
|
26
|
+
if (rest[0] === "--help" || rest[0] === "-h") {
|
|
27
|
+
return { ok: true, lines: [SANDBOX_CLI_USAGE] };
|
|
28
|
+
}
|
|
29
|
+
if (rest[0] === "network") {
|
|
30
|
+
const parsed = parseNetworkPolicy(rest[1]);
|
|
31
|
+
if (!parsed || rest.length > 2)
|
|
32
|
+
return { ok: false, lines: [SANDBOX_CLI_USAGE] };
|
|
33
|
+
setSandboxConfig({ network: parsed }, scope);
|
|
34
|
+
setSandboxOverride({ network: parsed });
|
|
35
|
+
return { ok: true, lines: [`✓ Network set to ${parsed === "deny" ? "denied" : "allowed"} (${scopeLabel(scope)})`] };
|
|
36
|
+
}
|
|
37
|
+
if (rest.length === 1) {
|
|
38
|
+
const parsed = parseSandboxMode(rest[0]);
|
|
39
|
+
if (!parsed)
|
|
40
|
+
return { ok: false, lines: [SANDBOX_CLI_USAGE] };
|
|
41
|
+
setSandboxConfig({ mode: parsed }, scope);
|
|
42
|
+
setSandboxOverride({ mode: parsed });
|
|
43
|
+
const caveat = sandboxEnforcementCaveat(parsed);
|
|
44
|
+
return {
|
|
45
|
+
ok: true,
|
|
46
|
+
lines: [`✓ Sandbox set to ${sandboxModeLabel(parsed)} (${scopeLabel(scope)})`, ...(caveat ? [caveat] : [])],
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
if (rest.length > 1)
|
|
50
|
+
return { ok: false, lines: [SANDBOX_CLI_USAGE] };
|
|
51
|
+
if (input.flagMode || input.flagNetwork) {
|
|
52
|
+
setSandboxConfig({
|
|
53
|
+
...(input.flagMode ? { mode: input.flagMode } : {}),
|
|
54
|
+
...(input.flagNetwork ? { network: input.flagNetwork } : {}),
|
|
55
|
+
}, scope);
|
|
56
|
+
setSandboxOverride({
|
|
57
|
+
...(input.flagMode ? { mode: input.flagMode } : {}),
|
|
58
|
+
...(input.flagNetwork ? { network: input.flagNetwork } : {}),
|
|
59
|
+
});
|
|
60
|
+
const parts = [
|
|
61
|
+
...(input.flagMode ? [`sandbox set to ${sandboxModeLabel(input.flagMode)}`] : []),
|
|
62
|
+
...(input.flagNetwork ? [`network set to ${input.flagNetwork === "deny" ? "denied" : "allowed"}`] : []),
|
|
63
|
+
];
|
|
64
|
+
const caveat = input.flagMode ? sandboxEnforcementCaveat(input.flagMode) : null;
|
|
65
|
+
return { ok: true, lines: [`✓ ${parts.join(", ")} (${scopeLabel(scope)})`, ...(caveat ? [caveat] : [])] };
|
|
66
|
+
}
|
|
67
|
+
const snap = getSandboxSnapshot();
|
|
68
|
+
const extras = [
|
|
69
|
+
...(snap.extraWriteRoots.length > 0 ? [`Extra writable dirs: ${snap.extraWriteRoots.join(", ")}`] : []),
|
|
70
|
+
...(snap.extraReadRoots.length > 0 ? [`Extra readable dirs: ${snap.extraReadRoots.join(", ")}`] : []),
|
|
71
|
+
];
|
|
72
|
+
const caveat = sandboxEnforcementCaveat(getEffectiveSandboxPolicy().mode);
|
|
73
|
+
return {
|
|
74
|
+
ok: true,
|
|
75
|
+
lines: [
|
|
76
|
+
`Current sandbox: ${sandboxStatusLabel()} (${sourceLabel(snap.source)})`,
|
|
77
|
+
...extras,
|
|
78
|
+
...(caveat ? [caveat] : []),
|
|
79
|
+
SANDBOX_CLI_USAGE,
|
|
80
|
+
],
|
|
81
|
+
};
|
|
82
|
+
}
|