flint-agent 1.14.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/.env.example +108 -0
- package/CHANGELOG.md +55 -0
- package/FEATURES.md +298 -0
- package/LICENSE +21 -0
- package/README.md +435 -0
- package/bin/flint.js +47 -0
- package/config/classifier-prompt.md +218 -0
- package/config/models-curated.json +4 -0
- package/config/providers.json +74 -0
- package/package.json +92 -0
- package/patches/ink+6.8.0.patch +78 -0
- package/profiles/desktop.md +65 -0
- package/profiles/generic.md +20 -0
- package/profiles/marketer.md +20 -0
- package/profiles/profiles.json +34 -0
- package/profiles/ux-reviewer.md +25 -0
- package/src/agent/agent.js +1743 -0
- package/src/agent/auto.js +346 -0
- package/src/agent/backoff.js +143 -0
- package/src/agent/compression.js +310 -0
- package/src/agent/content-resolver.js +180 -0
- package/src/agent/flow-controller.js +309 -0
- package/src/agent/intent-manifest.js +231 -0
- package/src/agent/intent-timeout.js +46 -0
- package/src/agent/intent.js +633 -0
- package/src/agent/knowledge.js +114 -0
- package/src/agent/learning.js +180 -0
- package/src/agent/modes.js +187 -0
- package/src/agent/outcome-ask.js +91 -0
- package/src/agent/project-context.js +76 -0
- package/src/agent/prompt-budget.js +117 -0
- package/src/agent/reflection-extractor.js +140 -0
- package/src/agent/steering.js +86 -0
- package/src/agent/supervisor.js +430 -0
- package/src/agent/swap.js +443 -0
- package/src/agent/system-prompt.js +446 -0
- package/src/agent/time-stamp.js +48 -0
- package/src/agent/tool-guard.js +201 -0
- package/src/agent/toolcall-text.js +162 -0
- package/src/agent/usage.js +297 -0
- package/src/agent/vision.js +94 -0
- package/src/agent/watchdog.js +139 -0
- package/src/agent/workspace-changes.js +177 -0
- package/src/api/address.js +14 -0
- package/src/api/client.js +280 -0
- package/src/api/server.js +535 -0
- package/src/api/stream-pipe.js +113 -0
- package/src/app-state.js +39 -0
- package/src/bootstrap.js +501 -0
- package/src/bus/drain-loop.js +497 -0
- package/src/bus/index.js +270 -0
- package/src/bus/plugins.js +65 -0
- package/src/child-idle.js +14 -0
- package/src/cli.js +118 -0
- package/src/commands/commands.js +1297 -0
- package/src/commands/registry.js +132 -0
- package/src/components/App.js +491 -0
- package/src/components/CarefulMenu.js +145 -0
- package/src/components/HistoryWriter.js +86 -0
- package/src/components/LineInput.js +69 -0
- package/src/components/LiveZone.js +294 -0
- package/src/components/OverlayMenu.js +179 -0
- package/src/components/SystemPanel.js +156 -0
- package/src/components/Table.js +54 -0
- package/src/config.js +249 -0
- package/src/free-models.js +230 -0
- package/src/index.js +1111 -0
- package/src/input-handler.js +13 -0
- package/src/input-text.js +123 -0
- package/src/launcher.js +129 -0
- package/src/logging/api-log.js +95 -0
- package/src/logging/chat-log-follower.js +113 -0
- package/src/logging/chat-log.js +15 -0
- package/src/logging/log-collector.js +182 -0
- package/src/logging/logger.js +112 -0
- package/src/logging/tool-log.js +20 -0
- package/src/mcp-client.js +314 -0
- package/src/memory/conversation-digest.js +113 -0
- package/src/memory/extract-facts.js +98 -0
- package/src/memory/facts.js +181 -0
- package/src/memory/inbox.js +63 -0
- package/src/memory/markdown.js +38 -0
- package/src/memory/patterns.js +185 -0
- package/src/memory/project.js +66 -0
- package/src/memory/reflections.js +74 -0
- package/src/memory/retrieval.js +84 -0
- package/src/memory/rules.js +105 -0
- package/src/memory/session-facts.js +125 -0
- package/src/memory/skills.js +191 -0
- package/src/memory/sqlite-store.js +653 -0
- package/src/memory/store.js +208 -0
- package/src/memory/tools.js +196 -0
- package/src/memory/user-model.js +86 -0
- package/src/message-handler.js +775 -0
- package/src/model-check.js +218 -0
- package/src/plugins/loader.js +120 -0
- package/src/plugins/manager.js +88 -0
- package/src/production-env.js +22 -0
- package/src/profiles.js +42 -0
- package/src/providers/adapters/anthropic.js +270 -0
- package/src/providers/adapters/openai.js +120 -0
- package/src/providers/keys-dpapi.js +41 -0
- package/src/providers/keys-fallback.js +31 -0
- package/src/providers/keys.js +132 -0
- package/src/providers/models.js +154 -0
- package/src/providers/registry.js +56 -0
- package/src/providers/state.js +56 -0
- package/src/registry.js +96 -0
- package/src/restart.js +29 -0
- package/src/sandbox/backend.js +130 -0
- package/src/security/api-auth.js +132 -0
- package/src/security/audit.js +98 -0
- package/src/security/child-policy.js +41 -0
- package/src/security/command-guard.js +173 -0
- package/src/security/content-fence.js +250 -0
- package/src/security/content-validator.js +132 -0
- package/src/security/index.js +143 -0
- package/src/security/network-guard.js +126 -0
- package/src/security/pairing.js +180 -0
- package/src/security/path-guard.js +140 -0
- package/src/security/persona-guard.js +67 -0
- package/src/security/policies.js +452 -0
- package/src/security/safety-constants.js +34 -0
- package/src/security/watchdog.js +107 -0
- package/src/sessions.js +130 -0
- package/src/spend.js +97 -0
- package/src/startup-watchdog.js +59 -0
- package/src/stdio/args.js +71 -0
- package/src/stdio/guard.js +59 -0
- package/src/stdio/protocol.js +167 -0
- package/src/stdio/run.js +106 -0
- package/src/stdio/session.js +180 -0
- package/src/store/agent-slice.js +306 -0
- package/src/store/dataset-slice.js +73 -0
- package/src/store/index.js +22 -0
- package/src/store/process-slice.js +135 -0
- package/src/store/session-slice.js +191 -0
- package/src/store/ui-slice.js +119 -0
- package/src/tasks/db.js +184 -0
- package/src/tasks/queries.js +589 -0
- package/src/tools/agent-tools.js +473 -0
- package/src/tools/checkpoint.js +152 -0
- package/src/tools/command-approvals.js +180 -0
- package/src/tools/dataset.js +50 -0
- package/src/tools/filesystem.js +682 -0
- package/src/tools/inbox-tools.js +48 -0
- package/src/tools/mesh.js +135 -0
- package/src/tools/own-env.js +136 -0
- package/src/tools/permissions.js +681 -0
- package/src/tools/plugin-tools.js +123 -0
- package/src/tools/process-tools.js +595 -0
- package/src/tools/registry.js +307 -0
- package/src/tools/swap-tools.js +72 -0
- package/src/tools/system.js +662 -0
- package/src/tools/tasks.js +532 -0
- package/src/tools/tool-search.js +171 -0
- package/src/ui/header.js +140 -0
- package/src/ui/input-cursor.js +23 -0
- package/src/ui/last-line.js +25 -0
- package/src/ui/line-edit.js +135 -0
- package/src/ui/output.js +399 -0
- package/src/ui/paste-tokens.js +131 -0
- package/src/ui/prompt-attention.js +134 -0
- package/src/ui/render-options.js +13 -0
- package/src/ui/replay.js +94 -0
- package/src/ui/splash.js +49 -0
- package/src/ui/status-level.js +36 -0
- package/src/ui/tool-ledger.js +203 -0
- package/src/ui/window-title.js +150 -0
- package/src/update.js +205 -0
- package/system.md +63 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// Inbox tools — check_inbox, dismiss_notifications
|
|
2
|
+
import { readInbox, allInbox, unreadCount } from "../memory/inbox.js";
|
|
3
|
+
|
|
4
|
+
export function createInboxTools() {
|
|
5
|
+
return [
|
|
6
|
+
{
|
|
7
|
+
type: "function",
|
|
8
|
+
function: {
|
|
9
|
+
name: "check_inbox",
|
|
10
|
+
description: "Read unread notifications (reminders, messages, events). Returns all unread items and marks them as read.",
|
|
11
|
+
parameters: {
|
|
12
|
+
type: "object",
|
|
13
|
+
properties: {
|
|
14
|
+
all: {
|
|
15
|
+
type: "boolean",
|
|
16
|
+
description: "If true, show all recent notifications (including already read). Default: false (unread only).",
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function handleInboxTool(name, args) {
|
|
26
|
+
if (name === "check_inbox") {
|
|
27
|
+
const showAll = args?.all || false;
|
|
28
|
+
const items = showAll ? allInbox(30) : readInbox();
|
|
29
|
+
|
|
30
|
+
if (items.length === 0) {
|
|
31
|
+
return showAll ? "No recent notifications." : "No unread notifications.";
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const lines = items.map((item) => {
|
|
35
|
+
const status = item.read ? "(read)" : "(new)";
|
|
36
|
+
const time = item.ts.slice(11, 16); // HH:MM
|
|
37
|
+
const from = item.from ? ` from ${item.from}` : "";
|
|
38
|
+
return `[${time}] ${item.type}${from} ${status}: ${item.content}`;
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const remaining = unreadCount();
|
|
42
|
+
const footer = remaining > 0 ? `\n${remaining} more unread.` : "";
|
|
43
|
+
|
|
44
|
+
return lines.join("\n") + footer;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { config } from "../config.js";
|
|
2
|
+
|
|
3
|
+
const MEMORY_URL = config.memoryUrl;
|
|
4
|
+
|
|
5
|
+
async function meshFetch(path, opts = {}) {
|
|
6
|
+
const url = `${MEMORY_URL}${path}`;
|
|
7
|
+
const res = await fetch(url, {
|
|
8
|
+
headers: { "Content-Type": "application/json", ...opts.headers },
|
|
9
|
+
...opts,
|
|
10
|
+
});
|
|
11
|
+
if (!res.ok) {
|
|
12
|
+
const text = await res.text();
|
|
13
|
+
throw new Error(`Mesh API ${res.status}: ${text}`);
|
|
14
|
+
}
|
|
15
|
+
return res.json();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const tools = [
|
|
19
|
+
{
|
|
20
|
+
type: "function",
|
|
21
|
+
function: {
|
|
22
|
+
name: "mesh_search",
|
|
23
|
+
description:
|
|
24
|
+
"Semantic search across all documents in Mesh memory. " +
|
|
25
|
+
"Finds documents by meaning, not exact words. " +
|
|
26
|
+
"Use this to recall past decisions, worklogs, notes, and any saved knowledge.",
|
|
27
|
+
parameters: {
|
|
28
|
+
type: "object",
|
|
29
|
+
properties: {
|
|
30
|
+
query: { type: "string", description: "What to search for (natural language)" },
|
|
31
|
+
limit: { type: "number", description: "Max results (default 5)" },
|
|
32
|
+
tags: {
|
|
33
|
+
type: "array",
|
|
34
|
+
items: { type: "string" },
|
|
35
|
+
description: "Optional tag filter, e.g. ['type:worklog', 'guid:mesh-api']",
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
required: ["query"],
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
type: "function",
|
|
44
|
+
function: {
|
|
45
|
+
name: "mesh_add",
|
|
46
|
+
description:
|
|
47
|
+
"Save a document to Mesh memory. Auto-tags (date, source) are added automatically. " +
|
|
48
|
+
"Type and project tags are inferred from similar documents. " +
|
|
49
|
+
"Use this to remember important information, decisions, worklogs, research results.",
|
|
50
|
+
parameters: {
|
|
51
|
+
type: "object",
|
|
52
|
+
properties: {
|
|
53
|
+
content: { type: "string", description: "Document text (min 10 chars)" },
|
|
54
|
+
tags: {
|
|
55
|
+
type: "array",
|
|
56
|
+
items: { type: "string" },
|
|
57
|
+
description: "Optional tags, e.g. ['type:worklog', 'guid:my-project']",
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
required: ["content"],
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
type: "function",
|
|
66
|
+
function: {
|
|
67
|
+
name: "mesh_recent",
|
|
68
|
+
description: "Get recent documents from Mesh memory.",
|
|
69
|
+
parameters: {
|
|
70
|
+
type: "object",
|
|
71
|
+
properties: {
|
|
72
|
+
limit: { type: "number", description: "Max documents (default 5)" },
|
|
73
|
+
type: { type: "string", description: "Filter by type: worklog, note, decision, research, etc." },
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
export const handlers = {
|
|
81
|
+
async mesh_search({ query, limit = 5, tags }) {
|
|
82
|
+
const body = { query, limit };
|
|
83
|
+
if (tags?.length) body.tags = tags;
|
|
84
|
+
const data = await meshFetch("/search", {
|
|
85
|
+
method: "POST",
|
|
86
|
+
body: JSON.stringify(body),
|
|
87
|
+
});
|
|
88
|
+
const results = data.results || [];
|
|
89
|
+
if (!results.length) return `No results found for: ${query}`;
|
|
90
|
+
const lines = [`Found ${results.length} results for "${query}":\n`];
|
|
91
|
+
for (const r of results) {
|
|
92
|
+
const score = (r.similarity_score || 0).toFixed(3);
|
|
93
|
+
const date = (r.created_at || "").slice(0, 10);
|
|
94
|
+
const tags = (r.tags || []).join(", ");
|
|
95
|
+
const preview = (r.content || "").slice(0, 200);
|
|
96
|
+
lines.push(`[${r.guid}] score=${score} date=${date}`);
|
|
97
|
+
lines.push(` tags: ${tags}`);
|
|
98
|
+
lines.push(` ${preview}`);
|
|
99
|
+
lines.push("");
|
|
100
|
+
}
|
|
101
|
+
return lines.join("\n");
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
async mesh_add({ content, tags }) {
|
|
105
|
+
const body = { content, source: "flint" };
|
|
106
|
+
if (tags?.length) body.tags = tags;
|
|
107
|
+
const data = await meshFetch("/", {
|
|
108
|
+
method: "PUT",
|
|
109
|
+
body: JSON.stringify(body),
|
|
110
|
+
});
|
|
111
|
+
const finalTags = (data.tags || []).join(", ");
|
|
112
|
+
return (
|
|
113
|
+
`Document saved: ${data.guid}\n` +
|
|
114
|
+
`Tags: ${finalTags}\n` +
|
|
115
|
+
`Auto-tagging will infer additional tags from similar documents.`
|
|
116
|
+
);
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
async mesh_recent({ limit = 5, type } = {}) {
|
|
120
|
+
const params = new URLSearchParams({ limit: String(limit) });
|
|
121
|
+
if (type) params.set("tag", `type:${type}`);
|
|
122
|
+
const data = await meshFetch(`/?${params}`);
|
|
123
|
+
if (!Array.isArray(data) || !data.length) return "No documents found.";
|
|
124
|
+
const lines = [`Recent ${data.length} documents:\n`];
|
|
125
|
+
for (const doc of data) {
|
|
126
|
+
const date = (doc.created_at || "").slice(0, 10);
|
|
127
|
+
const tags = (doc.tags || []).join(", ");
|
|
128
|
+
const preview = (doc.content || "").slice(0, 120);
|
|
129
|
+
lines.push(`[${doc.guid}] ${date} [${tags}]`);
|
|
130
|
+
lines.push(` ${preview}`);
|
|
131
|
+
lines.push("");
|
|
132
|
+
}
|
|
133
|
+
return lines.join("\n");
|
|
134
|
+
},
|
|
135
|
+
};
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
// Flint's own writable environment: a Python venv and an npm prefix that the
|
|
2
|
+
// shell tools put first on PATH.
|
|
3
|
+
//
|
|
4
|
+
// WHY: when a task needs a library Flint does not have (read a PDF, speak a
|
|
5
|
+
// line), the model does the right thing and installs one. On 2026-09-27 in the
|
|
6
|
+
// bench box every such install failed: python3 and pip3 on PATH were another
|
|
7
|
+
// program's venv owned by root, the system python had no pip, npm -g pointed at
|
|
8
|
+
// /usr/local. E-docs-003 took 24 calls, most of them pip, npm and apt attempts
|
|
9
|
+
// refused one after another. A user's machine can be the same: a managed
|
|
10
|
+
// python, a read-only global prefix. So Flint brings a place it may write to,
|
|
11
|
+
// and pip / npm -g land there without the model having to know it exists.
|
|
12
|
+
//
|
|
13
|
+
// It lives next to ~/.flint/plugins, not in FLINT_DATA_DIR: what was installed
|
|
14
|
+
// is the user's tooling, shared by every session, like the plugins.
|
|
15
|
+
//
|
|
16
|
+
// If no python can make a venv, the shell tools run with the PATH they had and
|
|
17
|
+
// the reason is logged: nothing is pretended.
|
|
18
|
+
|
|
19
|
+
import { existsSync, mkdirSync } from "node:fs";
|
|
20
|
+
import { join, delimiter } from "node:path";
|
|
21
|
+
import { homedir } from "node:os";
|
|
22
|
+
import { spawn } from "node:child_process";
|
|
23
|
+
import { createLogger } from "../logging/logger.js";
|
|
24
|
+
|
|
25
|
+
const log = createLogger("own-env");
|
|
26
|
+
const isWin = process.platform === "win32";
|
|
27
|
+
|
|
28
|
+
export function ownEnvPaths(dir = process.env.FLINT_ENV_DIR || join(homedir(), ".flint", "env")) {
|
|
29
|
+
const venv = join(dir, "py");
|
|
30
|
+
const npmPrefix = join(dir, "npm");
|
|
31
|
+
return {
|
|
32
|
+
dir,
|
|
33
|
+
venv,
|
|
34
|
+
venvBin: join(venv, isWin ? "Scripts" : "bin"),
|
|
35
|
+
venvPython: join(venv, isWin ? "Scripts" : "bin", isWin ? "python.exe" : "python"),
|
|
36
|
+
npmPrefix,
|
|
37
|
+
// npm puts global bins in <prefix> on Windows and <prefix>/bin elsewhere,
|
|
38
|
+
// and global modules in <prefix>/node_modules vs <prefix>/lib/node_modules.
|
|
39
|
+
npmBin: isWin ? npmPrefix : join(npmPrefix, "bin"),
|
|
40
|
+
npmModules: isWin ? join(npmPrefix, "node_modules") : join(npmPrefix, "lib", "node_modules"),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function run(cmd, args, timeoutMs) {
|
|
45
|
+
return new Promise((resolve) => {
|
|
46
|
+
let err = "";
|
|
47
|
+
let child;
|
|
48
|
+
try {
|
|
49
|
+
child = spawn(cmd, args, { stdio: ["ignore", "ignore", "pipe"], windowsHide: true });
|
|
50
|
+
} catch (e) {
|
|
51
|
+
resolve({ ok: false, err: e.message });
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const timer = setTimeout(() => { try { child.kill(); } catch {} }, timeoutMs);
|
|
55
|
+
child.stderr.on("data", (d) => { err += d; });
|
|
56
|
+
child.on("error", (e) => { clearTimeout(timer); resolve({ ok: false, err: e.message }); });
|
|
57
|
+
child.on("close", (code) => { clearTimeout(timer); resolve({ ok: code === 0, err: err.trim().slice(-300) }); });
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// On Windows, the first real python of that name on PATH, skipping the Store's
|
|
62
|
+
// App Execution Alias stubs in ...\Microsoft\WindowsApps. Spawned from a process
|
|
63
|
+
// that sits in a job object (seen under the vitest pool on 2026-09-28) the stub
|
|
64
|
+
// printed "AssignProcessToJobObject: (87)" and took the PARENT down with it,
|
|
65
|
+
// not just its own call: a Flint under a supervisor would die on its first
|
|
66
|
+
// shell command. Elsewhere the bare name is fine.
|
|
67
|
+
function resolvePython(name) {
|
|
68
|
+
if (!isWin) return name;
|
|
69
|
+
for (const d of (process.env.PATH || "").split(delimiter)) {
|
|
70
|
+
if (!d || /[\\/]Microsoft[\\/]WindowsApps[\\/]?$/i.test(d)) continue;
|
|
71
|
+
const exe = join(d, `${name}.exe`);
|
|
72
|
+
if (existsSync(exe)) return exe;
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// FLINT_OWN_ENV=0 turns it off. The test configs set it: otherwise every
|
|
78
|
+
// test that runs a shell command made a venv in the developer's real
|
|
79
|
+
// ~/.flint/env and waited seconds for it, and the suite went flaky on
|
|
80
|
+
// timeouts (2026-09-28). The own-env test turns it back on in a temp dir.
|
|
81
|
+
function ownEnvOff() {
|
|
82
|
+
return process.env.FLINT_OWN_ENV === "0";
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// One promise per env dir: the first shell command and the boot warm-up share it.
|
|
86
|
+
const pending = new Map();
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Make the venv and the npm prefix if they are not there yet. Never throws.
|
|
90
|
+
* @returns {Promise<{python: boolean, reason?: string}>}
|
|
91
|
+
*/
|
|
92
|
+
export function ensureOwnEnv(paths = ownEnvPaths()) {
|
|
93
|
+
if (ownEnvOff()) return Promise.resolve({ python: false, reason: "FLINT_OWN_ENV=0" });
|
|
94
|
+
if (!pending.has(paths.dir)) pending.set(paths.dir, create(paths));
|
|
95
|
+
return pending.get(paths.dir);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async function create(paths) {
|
|
99
|
+
try { mkdirSync(paths.npmModules, { recursive: true }); } catch {}
|
|
100
|
+
if (!isWin) { try { mkdirSync(paths.npmBin, { recursive: true }); } catch {} }
|
|
101
|
+
if (existsSync(paths.venvPython)) return { python: true };
|
|
102
|
+
|
|
103
|
+
// "python3" first: on Linux "python" may be absent or Python 2.
|
|
104
|
+
const candidates = [process.env.FLINT_PYTHON, ...["python3", "python"].map(resolvePython)].filter(Boolean);
|
|
105
|
+
const reasons = [];
|
|
106
|
+
for (const py of candidates) {
|
|
107
|
+
const r = await run(py, ["-m", "venv", paths.venv], 120000);
|
|
108
|
+
if (r.ok && existsSync(paths.venvPython)) {
|
|
109
|
+
log.info("own venv created", { python: py, venv: paths.venv });
|
|
110
|
+
return { python: true };
|
|
111
|
+
}
|
|
112
|
+
reasons.push(`${py}: ${r.err || "no venv made"}`);
|
|
113
|
+
}
|
|
114
|
+
const reason = reasons.join("; ");
|
|
115
|
+
log.warn("own venv not created; pip installs go wherever the system python points", { reason });
|
|
116
|
+
return { python: false, reason };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* The environment a shell command runs with, with Flint's own env first.
|
|
121
|
+
* Pure: reads the disk for what exists, changes nothing.
|
|
122
|
+
*/
|
|
123
|
+
export function withOwnEnv(env, paths = ownEnvPaths()) {
|
|
124
|
+
if (ownEnvOff()) return { ...env };
|
|
125
|
+
const out = { ...env };
|
|
126
|
+
const front = [];
|
|
127
|
+
if (existsSync(paths.venvPython)) {
|
|
128
|
+
front.push(paths.venvBin);
|
|
129
|
+
out.VIRTUAL_ENV = paths.venv;
|
|
130
|
+
}
|
|
131
|
+
front.push(paths.npmBin);
|
|
132
|
+
out.NPM_CONFIG_PREFIX = paths.npmPrefix;
|
|
133
|
+
out.NODE_PATH = [paths.npmModules, env.NODE_PATH].filter(Boolean).join(delimiter);
|
|
134
|
+
out.PATH = [...front, env.PATH].filter(Boolean).join(delimiter);
|
|
135
|
+
return out;
|
|
136
|
+
}
|