synabun 2.0.5 → 2.0.7
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/CHANGELOG.md +40 -0
- package/README.md +2 -1
- package/THIRD-PARTY-LICENSES.md +13 -0
- package/docs/whatsapp.md +151 -16
- package/hooks/claude-code/shared.mjs +6 -3
- package/lib/child-path.js +303 -0
- package/lib/claude-hooks.js +2 -1
- package/lib/data-home-migration.js +2 -0
- package/lib/desktop-app.js +265 -0
- package/lib/external-tools.js +7 -3
- package/lib/log-redact.js +102 -0
- package/lib/packaged-runtime.js +138 -11
- package/mcp-server/dist/http.d.ts +3 -1
- package/mcp-server/dist/http.d.ts.map +1 -1
- package/mcp-server/dist/http.js +15 -4
- package/mcp-server/dist/http.js.map +1 -1
- package/mcp-server/dist/index.d.ts.map +1 -1
- package/mcp-server/dist/index.js +9 -6
- package/mcp-server/dist/index.js.map +1 -1
- package/mcp-server/dist/services/budget-judgments.d.ts +108 -0
- package/mcp-server/dist/services/budget-judgments.d.ts.map +1 -0
- package/mcp-server/dist/services/budget-judgments.js +153 -0
- package/mcp-server/dist/services/budget-judgments.js.map +1 -0
- package/mcp-server/dist/services/codex-config-heal.d.ts +5 -1
- package/mcp-server/dist/services/codex-config-heal.d.ts.map +1 -1
- package/mcp-server/dist/services/codex-config-heal.js +41 -4
- package/mcp-server/dist/services/codex-config-heal.js.map +1 -1
- package/mcp-server/dist/services/identity.d.ts +11 -0
- package/mcp-server/dist/services/identity.d.ts.map +1 -1
- package/mcp-server/dist/services/identity.js +24 -0
- package/mcp-server/dist/services/identity.js.map +1 -1
- package/mcp-server/dist/services/neural-interface.d.ts +13 -0
- package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
- package/mcp-server/dist/services/neural-interface.js +34 -2
- package/mcp-server/dist/services/neural-interface.js.map +1 -1
- package/mcp-server/dist/services/typesafe-config.d.ts +1 -1
- package/mcp-server/dist/services/typesafe-config.d.ts.map +1 -1
- package/mcp-server/dist/services/typesafe-config.js +4 -1
- package/mcp-server/dist/services/typesafe-config.js.map +1 -1
- package/mcp-server/dist/tools/agents.d.ts +10 -7
- package/mcp-server/dist/tools/agents.d.ts.map +1 -1
- package/mcp-server/dist/tools/agents.js +34 -13
- package/mcp-server/dist/tools/agents.js.map +1 -1
- package/mcp-server/dist/tools/whatsapp-send-file.d.ts +17 -0
- package/mcp-server/dist/tools/whatsapp-send-file.d.ts.map +1 -0
- package/mcp-server/dist/tools/whatsapp-send-file.js +44 -0
- package/mcp-server/dist/tools/whatsapp-send-file.js.map +1 -0
- package/mcp-server/dist/tui.js +2 -1
- package/mcp-server/dist/tui.js.map +1 -1
- package/mcp-server/dist/version.d.ts +5 -0
- package/mcp-server/dist/version.d.ts.map +1 -0
- package/mcp-server/dist/version.js +21 -0
- package/mcp-server/dist/version.js.map +1 -0
- package/mcp-server/package-lock.json +2 -2
- package/mcp-server/package.json +1 -1
- package/mcp-server/run.mjs +15 -9
- package/mcp-server/src/http.ts +18 -3
- package/mcp-server/src/index.ts +9 -6
- package/mcp-server/src/services/budget-judgments.ts +183 -0
- package/mcp-server/src/services/codex-config-heal.ts +35 -4
- package/mcp-server/src/services/identity.ts +27 -0
- package/mcp-server/src/services/neural-interface.ts +34 -2
- package/mcp-server/src/services/typesafe-config.ts +4 -1
- package/mcp-server/src/tools/agents.ts +33 -14
- package/mcp-server/src/tools/whatsapp-send-file.ts +51 -0
- package/mcp-server/src/tui.ts +2 -1
- package/mcp-server/src/version.ts +22 -0
- package/neural-interface/i18n/en.json +627 -57
- package/neural-interface/i18n/pt-BR.json +626 -56
- package/neural-interface/lib/account-usage.js +163 -0
- package/neural-interface/lib/assistant-api.js +255 -52
- package/neural-interface/lib/assistant-brains/claude.js +15 -2
- package/neural-interface/lib/assistant-brains/codex.js +3 -0
- package/neural-interface/lib/assistant-brains/opencode.js +17 -4
- package/neural-interface/lib/assistant-budget-planner.js +390 -0
- package/neural-interface/lib/assistant-budget-split.js +408 -0
- package/neural-interface/lib/assistant-budget.js +94 -95
- package/neural-interface/lib/assistant-config.js +62 -14
- package/neural-interface/lib/assistant-dispatch.js +432 -125
- package/neural-interface/lib/assistant-jev.js +26 -3
- package/neural-interface/lib/assistant-persona.js +68 -11
- package/neural-interface/lib/assistant-plan-permissions.js +8 -1
- package/neural-interface/lib/assistant-route-accounts.js +14 -0
- package/neural-interface/lib/assistant-route-gate.js +19 -3
- package/neural-interface/lib/assistant-router.js +373 -42
- package/neural-interface/lib/assistant-runtime.js +307 -47
- package/neural-interface/lib/assistant-user-writer.js +4 -2
- package/neural-interface/lib/augmented-path.js +317 -53
- package/neural-interface/lib/claude-accounts.js +342 -31
- package/neural-interface/lib/claude-agent-bridge.js +90 -3
- package/neural-interface/lib/claude-executable.js +6 -1
- package/neural-interface/lib/claude-model-catalog.js +3 -3
- package/neural-interface/lib/claude-panel-auth.js +221 -0
- package/neural-interface/lib/claude-profile-api-key.js +62 -0
- package/neural-interface/lib/cli-install-api.js +137 -0
- package/neural-interface/lib/cli-install.js +797 -0
- package/neural-interface/lib/cli-path.js +130 -0
- package/neural-interface/lib/cli-resolver.js +744 -0
- package/neural-interface/lib/cli-update-planner.js +143 -7
- package/neural-interface/lib/codex-account-auth.js +178 -0
- package/neural-interface/lib/codex-accounts.js +489 -0
- package/neural-interface/lib/codex-capabilities.js +2 -1
- package/neural-interface/lib/codex-compat-rpc.js +8 -2
- package/neural-interface/lib/codex-mcp-approvals.js +5 -1
- package/neural-interface/lib/codex-panel-auth.js +62 -0
- package/neural-interface/lib/codex-session-settings.js +1 -1
- package/neural-interface/lib/codex-settings-schema.js +120 -0
- package/neural-interface/lib/desktop/config.js +3 -0
- package/neural-interface/lib/git-bash/bzip2.js +339 -0
- package/neural-interface/lib/git-bash/claude-settings.js +73 -0
- package/neural-interface/lib/git-bash/extract-worker.js +34 -0
- package/neural-interface/lib/git-bash/install.js +529 -0
- package/neural-interface/lib/git-bash/licenses/COPYING +360 -0
- package/neural-interface/lib/git-bash/licenses/LGPL-2.1 +511 -0
- package/neural-interface/lib/git-bash/manager.js +268 -0
- package/neural-interface/lib/git-bash/pins.js +206 -0
- package/neural-interface/lib/git-bash/pins.json +34 -0
- package/neural-interface/lib/git-bash/resolver.js +194 -0
- package/neural-interface/lib/git-read.js +34 -2
- package/neural-interface/lib/install-details.js +145 -0
- package/neural-interface/lib/mcp-installer.js +14 -2
- package/neural-interface/lib/native-loop-providers.js +16 -3
- package/neural-interface/lib/native-loop-runtime.js +9 -4
- package/neural-interface/lib/os-path.js +319 -0
- package/neural-interface/lib/provider-panel-api-key-auth.js +92 -0
- package/neural-interface/lib/provider-panel-terminal-auth.js +95 -0
- package/neural-interface/lib/pty-host/core.js +2 -1
- package/neural-interface/lib/pty-host/manager.js +27 -2
- package/neural-interface/lib/remote-policy.js +20 -11
- package/neural-interface/lib/session-title-generator.js +4 -3
- package/neural-interface/lib/system-about.js +340 -0
- package/neural-interface/lib/system-activity.js +66 -0
- package/neural-interface/lib/terminal-env.js +11 -3
- package/neural-interface/lib/terminal-path.js +55 -0
- package/neural-interface/lib/virtual-socket.js +12 -1
- package/neural-interface/lib/whatsapp/baileys-adapter.js +165 -45
- package/neural-interface/lib/whatsapp/bridge.js +78 -34
- package/neural-interface/lib/whatsapp/commands.js +13 -14
- package/neural-interface/lib/whatsapp/config.js +134 -20
- package/neural-interface/lib/whatsapp/fake-baileys.js +99 -14
- package/neural-interface/lib/whatsapp/host-core.js +168 -35
- package/neural-interface/lib/whatsapp/inbound.js +5 -3
- package/neural-interface/lib/whatsapp/local-file.js +524 -0
- package/neural-interface/lib/whatsapp/manager.js +92 -32
- package/neural-interface/lib/whatsapp/paths.js +11 -1
- package/neural-interface/lib/whatsapp/protocol.js +65 -5
- package/neural-interface/lib/whatsapp/redact.js +63 -0
- package/neural-interface/lib/whatsapp/service.js +343 -91
- package/neural-interface/lib/whatsapp/transcription.js +463 -0
- package/neural-interface/package-lock.json +2 -2
- package/neural-interface/package.json +1 -1
- package/neural-interface/public/index.html +2 -0
- package/neural-interface/public/index2d.html +2 -0
- package/neural-interface/public/offline.html +16 -1
- package/neural-interface/public/onboarding.html +96 -2
- package/neural-interface/public/shared/account-i18n.js +7 -0
- package/neural-interface/public/shared/account-menu.js +539 -0
- package/neural-interface/public/shared/account-usage.js +19 -0
- package/neural-interface/public/shared/account-view.js +335 -0
- package/neural-interface/public/shared/agent-runtime-options.js +28 -8
- package/neural-interface/public/shared/assistant/asst-accounts.js +182 -184
- package/neural-interface/public/shared/assistant/asst-api.js +32 -2
- package/neural-interface/public/shared/assistant/asst-brain-picker.js +124 -25
- package/neural-interface/public/shared/assistant/asst-budget.js +259 -303
- package/neural-interface/public/shared/assistant/asst-mascot.js +126 -7
- package/neural-interface/public/shared/assistant/asst-menu.js +15 -3
- package/neural-interface/public/shared/assistant/asst-panel.js +48 -14
- package/neural-interface/public/shared/assistant/asst-render.js +38 -7
- package/neural-interface/public/shared/assistant/asst-route.js +72 -14
- package/neural-interface/public/shared/assistant/asst-routes-editor.js +45 -6
- package/neural-interface/public/shared/assistant/asst-slash.js +1 -1
- package/neural-interface/public/shared/assistant/asst-state.js +1 -1
- package/neural-interface/public/shared/assistant/asst-styles.js +99 -17
- package/neural-interface/public/shared/cdx/cdx-commands.js +4 -1
- package/neural-interface/public/shared/cdx/cdx-features.js +22 -14
- package/neural-interface/public/shared/cdx/cdx-icons.js +2 -0
- package/neural-interface/public/shared/cdx/cdx-panel.js +48 -107
- package/neural-interface/public/shared/cdx/cdx-settings.js +231 -0
- package/neural-interface/public/shared/cdx/cdx-styles.js +128 -107
- package/neural-interface/public/shared/cdx/cdx-tabs.js +261 -664
- package/neural-interface/public/shared/cdx/cdx-ws.js +2 -0
- package/neural-interface/public/shared/claude-account-view.js +3 -0
- package/neural-interface/public/shared/cli-install-view.js +392 -0
- package/neural-interface/public/shared/cli-status.js +282 -22
- package/neural-interface/public/shared/cp/cp-events.js +2 -1
- package/neural-interface/public/shared/cp/cp-styles.js +46 -0
- package/neural-interface/public/shared/desktop-bridge.js +39 -0
- package/neural-interface/public/shared/desktop-titlebar-page.js +59 -0
- package/neural-interface/public/shared/desktop-titlebar-view.js +122 -0
- package/neural-interface/public/shared/desktop-titlebar.js +153 -0
- package/neural-interface/public/shared/desktop-update-view.js +462 -0
- package/neural-interface/public/shared/editor/editor-panel.js +1 -1
- package/neural-interface/public/shared/explorer/fx-main.js +1 -1
- package/neural-interface/public/shared/git-bash-view.js +145 -0
- package/neural-interface/public/shared/html-shell.js +1 -0
- package/neural-interface/public/shared/i18n.js +19 -1
- package/neural-interface/public/shared/install-details-view.js +86 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +32 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +17 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +118 -0
- package/neural-interface/public/shared/pwa-install-page.js +3 -1
- package/neural-interface/public/shared/settings/settings-about-pane.js +271 -0
- package/neural-interface/public/shared/settings/settings-about.js +254 -0
- package/neural-interface/public/shared/settings/settings-ia.js +15 -0
- package/neural-interface/public/shared/showcase-privacy.js +489 -0
- package/neural-interface/public/shared/storage.js +30 -3
- package/neural-interface/public/shared/styles.css +278 -31
- package/neural-interface/public/shared/synabun-mascot.js +48 -2
- package/neural-interface/public/shared/ui-claude-panel.js +212 -213
- package/neural-interface/public/shared/ui-cli-banner.js +355 -0
- package/neural-interface/public/shared/ui-git-bash.js +124 -0
- package/neural-interface/public/shared/ui-judgments.js +10 -1
- package/neural-interface/public/shared/ui-loading.js +30 -6
- package/neural-interface/public/shared/ui-menubar.js +13 -3
- package/neural-interface/public/shared/ui-navbar.js +3 -1
- package/neural-interface/public/shared/ui-provider-auth.js +104 -0
- package/neural-interface/public/shared/ui-provider-terminal-auth.js +223 -0
- package/neural-interface/public/shared/ui-sessions.js +9 -3
- package/neural-interface/public/shared/ui-settings.js +55 -3
- package/neural-interface/public/shared/ui-sidepanel-sessions.js +3 -1
- package/neural-interface/public/shared/ui-sync.js +6 -0
- package/neural-interface/public/shared/ui-terminal.js +17 -0
- package/neural-interface/public/shared/ui-tool-updates.js +5 -0
- package/neural-interface/public/shared/ui-update.js +594 -5
- package/neural-interface/public/shared/ui-whatsapp.js +21 -6
- package/neural-interface/public/shared/whatsapp/wa-view.js +51 -8
- package/neural-interface/public/shared/worker-end.js +2 -0
- package/neural-interface/public/sw.js +1 -1
- package/neural-interface/public/variant/2d/main.js +10 -1
- package/neural-interface/public/variant/3d/main.js +10 -1
- package/neural-interface/server.js +1219 -608
- package/package.json +4 -1
- package/setup.js +9 -8
|
@@ -71,12 +71,14 @@ export function writerUsage(event = {}, fallbackModel = null) {
|
|
|
71
71
|
* @param {() => object} [deps.accountEnv] the Claude account's environment (config dir)
|
|
72
72
|
* @param {string} [deps.cwd]
|
|
73
73
|
* @param {object} [deps.env]
|
|
74
|
+
* @param {(env: object) => object} [deps.childEnv] the host's last word on the environment (on Windows the Git Bash it names)
|
|
74
75
|
* @param {string} [deps.label] what timed out or was refused a tool, in the messages
|
|
75
76
|
* @param {object} [deps.queryOptions] more query options of the caller's own (the greeting writer turns
|
|
76
77
|
* thinking off); they never replace the isolation below
|
|
77
78
|
*/
|
|
78
|
-
export function createOneShot({ claudeQuery = null, claudeExecutable = null, model = () => WRITER.fallbackModel, accountEnv = () => ({}), cwd = process.cwd(), env = process.env, timeoutMs = WRITER.timeoutMs, log = () => {}, label = 'statement writer', logTag = 'user-context:writer-error', queryOptions = null } = {}) {
|
|
79
|
+
export function createOneShot({ claudeQuery = null, claudeExecutable = null, model = () => WRITER.fallbackModel, accountEnv = () => ({}), cwd = process.cwd(), env = process.env, childEnv = null, timeoutMs = WRITER.timeoutMs, log = () => {}, label = 'statement writer', logTag = 'user-context:writer-error', queryOptions = null } = {}) {
|
|
79
80
|
function available() { return typeof claudeQuery === 'function'; }
|
|
81
|
+
const oneShotEnv = (built) => { try { return typeof childEnv === 'function' ? childEnv(built) : built; } catch { return built; } };
|
|
80
82
|
|
|
81
83
|
/**
|
|
82
84
|
* → { text, model, tokens, costUsd, ms } | { text: '', error, model?, tokens?, costUsd?, ms? }
|
|
@@ -115,7 +117,7 @@ export function createOneShot({ claudeQuery = null, claudeExecutable = null, mod
|
|
|
115
117
|
includePartialMessages: false,
|
|
116
118
|
abortController,
|
|
117
119
|
...(executable?.path ? { pathToClaudeCodeExecutable: executable.path } : {}),
|
|
118
|
-
env: { ...cleanEnv(env), ...account, SYNABUN_TEMPORARY_CHAT: '1', SYNABUN_TYPESAFE: 'off' },
|
|
120
|
+
env: oneShotEnv({ ...cleanEnv(env), ...account, SYNABUN_TEMPORARY_CHAT: '1', SYNABUN_TYPESAFE: 'off' }),
|
|
119
121
|
canUseTool: async () => ({ behavior: 'deny', message: `The ${label} cannot use tools.` }),
|
|
120
122
|
},
|
|
121
123
|
});
|
|
@@ -1,70 +1,334 @@
|
|
|
1
1
|
// PATH augmented with the directories CLIs actually install into.
|
|
2
2
|
//
|
|
3
|
-
// SynaBun is frequently launched from Finder/launchd
|
|
4
|
-
// and that process inherits a minimal
|
|
5
|
-
//
|
|
6
|
-
//
|
|
3
|
+
// SynaBun is frequently launched from Finder/launchd or the Start menu rather
|
|
4
|
+
// than a login shell, and that process inherits a minimal or an old PATH: no
|
|
5
|
+
// ~/.local/bin, no Homebrew, nothing an installer added after it started. Any
|
|
6
|
+
// `which claude` / bare-command spawn under that PATH fails even though the
|
|
7
|
+
// CLI is installed, so every lookup and spawn that needs to find a
|
|
7
8
|
// user-installed binary goes through here first.
|
|
8
9
|
//
|
|
9
|
-
//
|
|
10
|
-
// the
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
10
|
+
// The PATH, in this order:
|
|
11
|
+
// 1. the PATH this process was given
|
|
12
|
+
// 2. the OS PATH as a new terminal would have it now (os-path.js reads it:
|
|
13
|
+
// the registry on Windows, the login shell on macOS and Linux) and hands
|
|
14
|
+
// it in through rememberOsPath()
|
|
15
|
+
// 3. the folders each install method uses on this system (knownInstallDirs),
|
|
16
|
+
// for a CLI that no PATH names at all
|
|
17
|
+
// Folders that do not exist are left out of 2 and 3.
|
|
14
18
|
//
|
|
15
|
-
//
|
|
19
|
+
// Shared by lib/cli-resolver.js (where a CLI is), the model catalog (discovery
|
|
20
|
+
// spawns the CLI by name too, and without this a daemon-started server
|
|
21
|
+
// silently degrades to the static fallback model list) and every spawn that
|
|
22
|
+
// passes PATH on to a child.
|
|
23
|
+
//
|
|
24
|
+
// SYNABUN_TOOL_DISCOVERY=path turns 2 and 3 off: the tools are then looked
|
|
16
25
|
// for on the PATH this process was given and nowhere else. It is what a check
|
|
17
26
|
// of "nothing is installed" needs on a machine where something is (the
|
|
18
27
|
// packaged application's own run), and what a user sets to make SynaBun see
|
|
19
28
|
// exactly their PATH.
|
|
29
|
+
//
|
|
30
|
+
// A packaged application (packaging/) also hands in its own folders through
|
|
31
|
+
// rememberPackagedPath(): the Node runtime that came with it and the global
|
|
32
|
+
// bin of the npm that came with it. They go LAST, after everything of the
|
|
33
|
+
// user's, in every PATH built here (and in withPackagedPathLast for an env
|
|
34
|
+
// built elsewhere): the user's own Node and npm stay first, SynaBun's are the
|
|
35
|
+
// fallback on a machine that has none. The order itself is lib/child-path.js
|
|
36
|
+
// packagedPathLast, the one the packaged entry points use too.
|
|
37
|
+
//
|
|
38
|
+
// Only node: modules and lib/child-path.js (itself node: only) here: tests
|
|
39
|
+
// copy these files into a scratch tree.
|
|
20
40
|
|
|
21
41
|
import os from 'node:os';
|
|
22
|
-
import
|
|
23
|
-
import { existsSync } from 'node:fs';
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
42
|
+
import path from 'node:path';
|
|
43
|
+
import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
44
|
+
import {
|
|
45
|
+
packagedPathLast as orderPackagedPath, parseNpmrc, pathEnvValue, resolveNpmPrefix, withPathEnv,
|
|
46
|
+
} from '../../lib/child-path.js';
|
|
47
|
+
|
|
48
|
+
export { pathEnvValue, withPathEnv };
|
|
49
|
+
|
|
50
|
+
const MEMO_MS = 5_000;
|
|
51
|
+
|
|
52
|
+
let _osPath = [];
|
|
53
|
+
let _memo = null; // { key, at, value }
|
|
54
|
+
let _packaged = { last: [], extra: [], npmBin: null };
|
|
55
|
+
|
|
56
|
+
/** The OS PATH a new terminal would get, as os-path.js last read it. */
|
|
57
|
+
export function rememberOsPath(entries) {
|
|
58
|
+
_osPath = Array.isArray(entries) ? entries.map(entry => String(entry || '').trim()).filter(Boolean) : [];
|
|
59
|
+
_memo = null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function rememberedOsPath() {
|
|
63
|
+
return _osPath.slice();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Drop the few seconds of memory getAugmentedPath keeps: a folder an installer just made counts at once. */
|
|
67
|
+
export function forgetAugmentedPath() {
|
|
68
|
+
_memo = null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* A packaged application's own folders: `last`, the runtime it runs on (moved
|
|
73
|
+
* to the end of every PATH, wherever it was), and `extra`, the global bin of
|
|
74
|
+
* the npm that came with it (added before them when the PATH lacks it; where
|
|
75
|
+
* the user already has it, it stays). `npmBin`, when given, is asked for that
|
|
76
|
+
* bin each time (lib/packaged-runtime.js packagedNpmGlobalBin: the prefix as
|
|
77
|
+
* npm settles it, cached for seconds), so a prefix the user sets later counts
|
|
78
|
+
* without a restart. Called once at start; nothing for an npm or Git install.
|
|
79
|
+
*/
|
|
80
|
+
export function rememberPackagedPath({ last = [], extra = [], npmBin = null } = {}) {
|
|
81
|
+
const clean = (list) => (Array.isArray(list) ? list : []).map(entry => String(entry || '').trim()).filter(Boolean);
|
|
82
|
+
_packaged = { last: clean(last), extra: clean(extra), npmBin: typeof npmBin === 'function' ? npmBin : null };
|
|
83
|
+
_memo = null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** The packaged folders as they are now: { last, extra } (extra with npmBin's answer after the fixed ones). */
|
|
87
|
+
export function rememberedPackagedPath() {
|
|
88
|
+
let asked = null;
|
|
89
|
+
if (_packaged.npmBin) { try { asked = _packaged.npmBin(); } catch { asked = null; } }
|
|
90
|
+
const extra = _packaged.extra.slice();
|
|
91
|
+
if (typeof asked === 'string' && asked.trim() && !extra.includes(asked.trim())) extra.push(asked.trim());
|
|
92
|
+
return { last: _packaged.last.slice(), extra };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* PATH `value` with a packaged application's folders after the user's
|
|
97
|
+
* (lib/child-path.js packagedPathLast; the remembered folders by default).
|
|
98
|
+
*/
|
|
99
|
+
export function packagedPathLast(value, { platform = process.platform, ...folders } = {}) {
|
|
100
|
+
const { last, extra } = 'last' in folders || 'extra' in folders ? { last: folders.last || [], extra: folders.extra || [] } : rememberedPackagedPath();
|
|
101
|
+
return orderPackagedPath(value, { platform, last, extra });
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** `env` with packagedPathLast applied to its PATH (one PATH key kept, as withPathEnv does). No packaged folders: `env` as it is. */
|
|
105
|
+
export function withPackagedPathLast(env = process.env, { platform = process.platform, ...folders } = {}) {
|
|
106
|
+
const { last, extra } = 'last' in folders || 'extra' in folders ? { last: folders.last || [], extra: folders.extra || [] } : rememberedPackagedPath();
|
|
107
|
+
if (!last.length && !extra.length) return env;
|
|
108
|
+
return withPathEnv(env, orderPackagedPath(pathEnvValue(env), { platform, last, extra }), platform);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function pathImplFor(platform) {
|
|
112
|
+
return platform === 'win32' ? path.win32 : path.posix;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function homeOf(env, platform) {
|
|
116
|
+
if (platform === 'win32') return env.USERPROFILE || env.HOME || os.homedir();
|
|
117
|
+
return env.HOME || os.homedir();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function expandHome(value, home, pathImpl) {
|
|
121
|
+
const text = String(value || '').trim();
|
|
122
|
+
if (text === '~') return home;
|
|
123
|
+
if (text.startsWith('~/') || text.startsWith('~\\')) return pathImpl.join(home, text.slice(2));
|
|
124
|
+
return text;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** The value of `prefix=` in an .npmrc as npm's ini reader gives it (lib/child-path.js parseNpmrc), or ''. */
|
|
128
|
+
export function npmrcPrefix(text) {
|
|
129
|
+
const value = parseNpmrc(text).prefix;
|
|
130
|
+
return typeof value === 'string' ? value : '';
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* npm's global prefix as the user set it (lib/child-path.js resolveNpmPrefix
|
|
135
|
+
* without a builtin npmrc: npm_config_prefix, the user's config file named by
|
|
136
|
+
* npm_config_userconfig or ~/.npmrc, the global config file it names), with
|
|
137
|
+
* npm's ${VAR} substitution and `~/`: an absolute folder, or '' when the user
|
|
138
|
+
* set none.
|
|
139
|
+
*/
|
|
140
|
+
export function npmGlobalPrefix({ platform = process.platform, env = process.env, home, readFile = readFileSync } = {}) {
|
|
141
|
+
return resolveNpmPrefix({ platform, env, readFile, ...(home ? { home } : {}) }).prefix || '';
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** Version folders newest first ("v22.11.0" before "v20.18.1"); names that are not versions last. */
|
|
145
|
+
export function newestVersionsFirst(names) {
|
|
146
|
+
const parse = (name) => {
|
|
147
|
+
const match = String(name).match(/^v?(\d+)\.(\d+)\.(\d+)/);
|
|
148
|
+
return match ? match.slice(1, 4).map(Number) : null;
|
|
149
|
+
};
|
|
150
|
+
return [...names].sort((a, b) => {
|
|
151
|
+
const va = parse(a);
|
|
152
|
+
const vb = parse(b);
|
|
153
|
+
if (!va || !vb) return va ? -1 : vb ? 1 : String(a).localeCompare(String(b));
|
|
154
|
+
for (let i = 0; i < 3; i++) if (va[i] !== vb[i]) return vb[i] - va[i];
|
|
155
|
+
return 0;
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function listDir(readdir, dir) {
|
|
160
|
+
try { return readdir(dir).map(String).slice(0, 200); } catch { return []; }
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Every folder an install method puts its commands in on this system, most
|
|
165
|
+
* specific first. Folders are listed whether or not they exist: the caller
|
|
166
|
+
* checks (getAugmentedPath, the resolver's candidates). `via` names the
|
|
167
|
+
* method, for diagnostics.
|
|
168
|
+
*
|
|
169
|
+
* native installers ~/.local/bin (Claude Code, Codex's standalone
|
|
170
|
+
* installer, pipx), $XDG_BIN_HOME / $XDG_BIN_DIR, ~/bin
|
|
171
|
+
* Homebrew /opt/homebrew, /usr/local, Linuxbrew
|
|
172
|
+
* npm the configured prefix, ~/.npm-global, ~/.npm-packages;
|
|
173
|
+
* on Windows %APPDATA%\npm and the Node.js folder
|
|
174
|
+
* Node managers nvm, nvm-windows, fnm, Volta
|
|
175
|
+
* package managers bun, pnpm, yarn, Scoop, Chocolatey, WinGet, snap
|
|
176
|
+
* version managers asdf and mise shims
|
|
177
|
+
* cargo, the system ~/.cargo/bin, /usr/local/bin, /usr/bin, MacPorts
|
|
178
|
+
*
|
|
179
|
+
* Tool-specific places (~/.opencode/bin, ~/.claude/local, the Codex installer's
|
|
180
|
+
* folders, WinGet's package folders) are the resolver's (cli-resolver.js).
|
|
181
|
+
*/
|
|
182
|
+
export function knownInstallDirs({
|
|
183
|
+
platform = process.platform,
|
|
184
|
+
env = process.env,
|
|
185
|
+
home = homeOf(env, platform),
|
|
186
|
+
readdir = readdirSync,
|
|
187
|
+
readFile = readFileSync,
|
|
188
|
+
} = {}) {
|
|
189
|
+
const pathImpl = pathImplFor(platform);
|
|
190
|
+
const join = (...parts) => pathImpl.join(...parts);
|
|
191
|
+
const out = [];
|
|
192
|
+
const add = (dir, via) => { if (dir) out.push({ dir: String(dir), via }); };
|
|
193
|
+
// npm's own home rule (HOME, else the account's) for its config files.
|
|
194
|
+
const npmPrefix = npmGlobalPrefix({ platform, env, home: env.HOME || home, readFile });
|
|
195
|
+
|
|
196
|
+
if (platform === 'win32') {
|
|
197
|
+
const appData = env.APPDATA || join(home, 'AppData', 'Roaming');
|
|
198
|
+
const localAppData = env.LOCALAPPDATA || join(home, 'AppData', 'Local');
|
|
199
|
+
const programData = env.ProgramData || env.PROGRAMDATA || 'C:\\ProgramData';
|
|
200
|
+
const programFiles = env.ProgramFiles || env.PROGRAMFILES || 'C:\\Program Files';
|
|
201
|
+
add(join(home, '.local', 'bin'), 'native');
|
|
202
|
+
// npm keeps global commands in the prefix itself on Windows (no bin\).
|
|
203
|
+
if (npmPrefix) add(npmPrefix, 'npm');
|
|
204
|
+
add(join(appData, 'npm'), 'npm');
|
|
205
|
+
if (env.NVM_SYMLINK) add(env.NVM_SYMLINK, 'nvm');
|
|
206
|
+
if (env.NVM_HOME) for (const name of newestVersionsFirst(listDir(readdir, env.NVM_HOME).filter(n => /^v\d/.test(n)))) add(join(env.NVM_HOME, name), 'nvm');
|
|
207
|
+
add(join(programFiles, 'nodejs'), 'npm');
|
|
208
|
+
const fnmDir = env.FNM_DIR || join(appData, 'fnm');
|
|
209
|
+
add(join(fnmDir, 'aliases', 'default'), 'fnm');
|
|
210
|
+
for (const name of newestVersionsFirst(listDir(readdir, join(fnmDir, 'node-versions')))) add(join(fnmDir, 'node-versions', name, 'installation'), 'fnm');
|
|
211
|
+
if (env.VOLTA_HOME) add(join(env.VOLTA_HOME, 'bin'), 'volta');
|
|
212
|
+
add(join(localAppData, 'Volta', 'bin'), 'volta');
|
|
213
|
+
if (env.PNPM_HOME) add(env.PNPM_HOME, 'pnpm');
|
|
214
|
+
add(join(localAppData, 'pnpm'), 'pnpm');
|
|
215
|
+
if (env.BUN_INSTALL) add(join(env.BUN_INSTALL, 'bin'), 'bun');
|
|
216
|
+
add(join(home, '.bun', 'bin'), 'bun');
|
|
217
|
+
add(join(localAppData, 'Yarn', 'bin'), 'yarn');
|
|
218
|
+
if (env.SCOOP) add(join(env.SCOOP, 'shims'), 'scoop');
|
|
219
|
+
add(join(home, 'scoop', 'shims'), 'scoop');
|
|
220
|
+
if (env.SCOOP_GLOBAL) add(join(env.SCOOP_GLOBAL, 'shims'), 'scoop');
|
|
221
|
+
add(join(programData, 'scoop', 'shims'), 'scoop');
|
|
222
|
+
if (env.ChocolateyInstall) add(join(env.ChocolateyInstall, 'bin'), 'choco');
|
|
223
|
+
add(join(programData, 'chocolatey', 'bin'), 'choco');
|
|
224
|
+
add(join(localAppData, 'Microsoft', 'WinGet', 'Links'), 'winget');
|
|
225
|
+
add(join(programFiles, 'WinGet', 'Links'), 'winget');
|
|
226
|
+
add(join(localAppData, 'mise', 'shims'), 'mise');
|
|
227
|
+
if (env.CARGO_HOME) add(join(env.CARGO_HOME, 'bin'), 'cargo');
|
|
228
|
+
add(join(home, '.cargo', 'bin'), 'cargo');
|
|
229
|
+
return dedupeDirs(out, platform);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const darwin = platform === 'darwin';
|
|
233
|
+
const xdgData = env.XDG_DATA_HOME || join(home, '.local', 'share');
|
|
234
|
+
add(join(home, '.local', 'bin'), 'native');
|
|
235
|
+
if (env.XDG_BIN_HOME) add(expandHome(env.XDG_BIN_HOME, home, pathImpl), 'native');
|
|
236
|
+
if (env.XDG_BIN_DIR) add(expandHome(env.XDG_BIN_DIR, home, pathImpl), 'native');
|
|
237
|
+
add(join(home, 'bin'), 'native');
|
|
238
|
+
if (env.HOMEBREW_PREFIX) add(join(env.HOMEBREW_PREFIX, 'bin'), 'brew');
|
|
239
|
+
if (darwin) add('/opt/homebrew/bin', 'brew');
|
|
240
|
+
add('/usr/local/bin', darwin ? 'brew' : 'system');
|
|
241
|
+
if (!darwin) {
|
|
242
|
+
add('/home/linuxbrew/.linuxbrew/bin', 'brew');
|
|
243
|
+
add(join(home, '.linuxbrew', 'bin'), 'brew');
|
|
244
|
+
}
|
|
245
|
+
if (npmPrefix) add(join(npmPrefix, 'bin'), 'npm');
|
|
246
|
+
add(join(home, '.npm-global', 'bin'), 'npm');
|
|
247
|
+
add(join(home, '.npm-packages', 'bin'), 'npm');
|
|
248
|
+
if (env.BUN_INSTALL) add(join(env.BUN_INSTALL, 'bin'), 'bun');
|
|
249
|
+
add(join(home, '.bun', 'bin'), 'bun');
|
|
250
|
+
if (env.PNPM_HOME) add(env.PNPM_HOME, 'pnpm');
|
|
251
|
+
add(darwin ? join(home, 'Library', 'pnpm') : join(xdgData, 'pnpm'), 'pnpm');
|
|
252
|
+
add(join(home, '.yarn', 'bin'), 'yarn');
|
|
253
|
+
if (env.VOLTA_HOME) add(join(env.VOLTA_HOME, 'bin'), 'volta');
|
|
254
|
+
add(join(home, '.volta', 'bin'), 'volta');
|
|
255
|
+
// nvm: the default alias first, then every installed Node, newest first.
|
|
256
|
+
const nvmDir = env.NVM_DIR || join(home, '.nvm');
|
|
257
|
+
const nvmVersions = join(nvmDir, 'versions', 'node');
|
|
258
|
+
let nvmDefault = '';
|
|
259
|
+
try { nvmDefault = String(readFile(join(nvmDir, 'alias', 'default'), 'utf8')).trim(); } catch {}
|
|
260
|
+
const nvmInstalled = newestVersionsFirst(listDir(readdir, nvmVersions));
|
|
261
|
+
const nvmPick = nvmDefault ? nvmInstalled.find(name => name === nvmDefault || name === `v${nvmDefault}` || name.startsWith(`v${nvmDefault.replace(/^v/, '')}.`)) : null;
|
|
262
|
+
for (const name of nvmPick ? [nvmPick, ...nvmInstalled.filter(n => n !== nvmPick)] : nvmInstalled) add(join(nvmVersions, name, 'bin'), 'nvm');
|
|
263
|
+
// fnm keeps its Node versions in its own data folder.
|
|
264
|
+
const fnmDirs = env.FNM_DIR ? [env.FNM_DIR] : [darwin ? join(home, 'Library', 'Application Support', 'fnm') : join(xdgData, 'fnm'), join(home, '.fnm')];
|
|
265
|
+
for (const fnmDir of fnmDirs) {
|
|
266
|
+
add(join(fnmDir, 'aliases', 'default', 'bin'), 'fnm');
|
|
267
|
+
for (const name of newestVersionsFirst(listDir(readdir, join(fnmDir, 'node-versions')))) add(join(fnmDir, 'node-versions', name, 'installation', 'bin'), 'fnm');
|
|
268
|
+
}
|
|
269
|
+
add(join(env.ASDF_DATA_DIR || join(home, '.asdf'), 'shims'), 'asdf');
|
|
270
|
+
add(join(env.MISE_DATA_DIR || join(xdgData, 'mise'), 'shims'), 'mise');
|
|
271
|
+
add(join(env.CARGO_HOME || join(home, '.cargo'), 'bin'), 'cargo');
|
|
272
|
+
if (!darwin) add('/snap/bin', 'snap');
|
|
273
|
+
if (darwin) add('/opt/local/bin', 'macports');
|
|
274
|
+
add('/usr/bin', 'system');
|
|
275
|
+
return dedupeDirs(out, platform);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function dedupeDirs(list, platform) {
|
|
279
|
+
const seen = new Set();
|
|
280
|
+
return list.filter(({ dir }) => {
|
|
281
|
+
const key = platform === 'win32' ? dir.toLowerCase().replace(/[\\/]+$/, '') : dir.replace(/\/+$/, '');
|
|
282
|
+
if (!key || seen.has(key)) return false;
|
|
283
|
+
seen.add(key);
|
|
284
|
+
return true;
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* The PATH every lookup and spawn of a user's CLI uses: this process's PATH,
|
|
290
|
+
* then the OS PATH a new terminal would have, then the known install folders
|
|
291
|
+
* (each existing folder once). Options are for tests; called bare it describes
|
|
292
|
+
* this process.
|
|
293
|
+
*/
|
|
294
|
+
export function getAugmentedPath({
|
|
295
|
+
platform = process.platform,
|
|
296
|
+
env = process.env,
|
|
297
|
+
osPath = _osPath,
|
|
298
|
+
exists = existsSync,
|
|
299
|
+
readdir = readdirSync,
|
|
300
|
+
readFile = readFileSync,
|
|
301
|
+
// On Windows the folder of the Node running SynaBun comes last: an npm
|
|
302
|
+
// command file (claude.cmd) starts `node` from PATH when no node.exe lies
|
|
303
|
+
// beside it.
|
|
304
|
+
nodeDir = platform === 'win32' && platform === process.platform ? path.win32.dirname(process.execPath) : '',
|
|
305
|
+
// A packaged application's own folders (rememberPackagedPath, as they are now): after everything of the user's.
|
|
306
|
+
packaged,
|
|
307
|
+
} = {}) {
|
|
308
|
+
const folders = packaged || rememberedPackagedPath();
|
|
309
|
+
const ordered = (value) => (folders.last.length || folders.extra.length ? orderPackagedPath(value, { platform, last: folders.last, extra: folders.extra }) : value);
|
|
310
|
+
if (env.SYNABUN_TOOL_DISCOVERY === 'path') return ordered(pathEnvValue(env));
|
|
311
|
+
const inherited = pathEnvValue(env);
|
|
312
|
+
const live = env === process.env && platform === process.platform && exists === existsSync && !packaged;
|
|
313
|
+
const memoKey = live ? `${inherited}\0${osPath.join('\0')}\0${folders.last.join('\0')}\0${folders.extra.join('\0')}` : null;
|
|
314
|
+
if (memoKey && _memo && _memo.key === memoKey && Date.now() - _memo.at < MEMO_MS) return _memo.value;
|
|
315
|
+
|
|
316
|
+
const delimiter = platform === 'win32' ? ';' : ':';
|
|
317
|
+
const known = knownInstallDirs({ platform, env, readdir, readFile }).map(({ dir }) => dir);
|
|
57
318
|
const entries = [
|
|
58
|
-
...
|
|
59
|
-
...(
|
|
60
|
-
...
|
|
319
|
+
...inherited.split(delimiter),
|
|
320
|
+
...osPath.filter(dir => dir && exists(dir)),
|
|
321
|
+
...known.filter(dir => exists(dir)),
|
|
322
|
+
nodeDir,
|
|
61
323
|
];
|
|
62
324
|
const seen = new Set();
|
|
63
|
-
|
|
325
|
+
const value = ordered(entries.filter((entry) => {
|
|
64
326
|
if (!entry) return false;
|
|
65
|
-
const
|
|
66
|
-
if (seen.has(
|
|
67
|
-
seen.add(
|
|
327
|
+
const id = platform === 'win32' ? entry.toLowerCase().replace(/[\\/]+$/, '') : entry;
|
|
328
|
+
if (seen.has(id)) return false;
|
|
329
|
+
seen.add(id);
|
|
68
330
|
return true;
|
|
69
|
-
}).join(delimiter);
|
|
331
|
+
}).join(delimiter));
|
|
332
|
+
if (memoKey) _memo = { key: memoKey, at: Date.now(), value };
|
|
333
|
+
return value;
|
|
70
334
|
}
|