@blackbelt-technology/pi-agent-dashboard 0.5.2 → 0.5.4
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/AGENTS.md +19 -30
- package/README.md +69 -1
- package/docs/architecture.md +89 -165
- package/package.json +11 -7
- package/packages/extension/package.json +2 -2
- package/packages/extension/src/__tests__/bridge-default-model-gate.test.ts +47 -0
- package/packages/extension/src/__tests__/bridge-followup-chat-order.test.ts +215 -0
- package/packages/extension/src/__tests__/bridge-followup-multi-entry.test.ts +202 -0
- package/packages/extension/src/__tests__/bridge-queue-update-forward.test.ts +77 -0
- package/packages/extension/src/__tests__/bridge-retry-ordering.test.ts +148 -0
- package/packages/extension/src/__tests__/bridge-shadow-queue-drain.test.ts +221 -0
- package/packages/extension/src/__tests__/bridge-shadow-queue-gate.test.ts +299 -0
- package/packages/extension/src/__tests__/bridge-shutdown-reset.test.ts +238 -0
- package/packages/extension/src/__tests__/bridge-slash-command-routing.test.ts +127 -31
- package/packages/extension/src/__tests__/command-handler.test.ts +105 -3
- package/packages/extension/src/__tests__/fixtures/usage-limit-error-strings.ts +127 -0
- package/packages/extension/src/__tests__/source-detector.test.ts +15 -0
- package/packages/extension/src/__tests__/usage-limit-orderer.test.ts +12 -0
- package/packages/extension/src/bridge-default-model-gate.ts +32 -0
- package/packages/extension/src/bridge.ts +299 -20
- package/packages/extension/src/command-handler.ts +53 -7
- package/packages/extension/src/dashboard-default-adapter.ts +5 -0
- package/packages/extension/src/prompt-bus.ts +15 -0
- package/packages/extension/src/slash-dispatch.ts +30 -15
- package/packages/extension/src/source-detector.ts +13 -5
- package/packages/extension/src/usage-limit-orderer.ts +18 -1
- package/packages/server/bin/pi-dashboard.mjs +62 -14
- package/packages/server/package.json +9 -5
- package/packages/server/src/__tests__/browser-gateway-register-handler.test.ts +69 -0
- package/packages/server/src/__tests__/cli-env-no-clobber.test.ts +46 -0
- package/packages/server/src/__tests__/cli-no-bootstrap-references.test.ts +69 -0
- package/packages/server/src/__tests__/cli-parse.test.ts +9 -10
- package/packages/server/src/__tests__/cli-version.test.ts +151 -0
- package/packages/server/src/__tests__/directory-service-openspec-enabled.test.ts +9 -0
- package/packages/server/src/__tests__/directory-service-refresh-force.test.ts +9 -0
- package/packages/server/src/__tests__/directory-service-specs-mtime.test.ts +9 -0
- package/packages/server/src/__tests__/directory-service-toctou.test.ts +9 -0
- package/packages/server/src/__tests__/directory-service.test.ts +9 -0
- package/packages/server/src/__tests__/doctor-route.test.ts +53 -0
- package/packages/server/src/__tests__/event-wiring-queue-state.test.ts +156 -0
- package/packages/server/src/__tests__/event-wiring-resume-clear.test.ts +105 -0
- package/packages/server/src/__tests__/health-shape.test.ts +35 -12
- package/packages/server/src/__tests__/installed-package-enricher.test.ts +12 -12
- package/packages/server/src/__tests__/is-activity-event.test.ts +4 -7
- package/packages/server/src/__tests__/package-routes.test.ts +6 -2
- package/packages/server/src/__tests__/pi-changelog-routes.test.ts +10 -13
- package/packages/server/src/__tests__/pi-core-checker.test.ts +2 -2
- package/packages/server/src/__tests__/pi-version-skew.test.ts +3 -2
- package/packages/server/src/__tests__/plugin-activation-routes.test.ts +267 -0
- package/packages/server/src/__tests__/plugin-intent-cache.test.ts +75 -0
- package/packages/server/src/__tests__/preferences-store.test.ts +196 -0
- package/packages/server/src/__tests__/reattach-placement.test.ts +9 -0
- package/packages/server/src/__tests__/recommended-routes.test.ts +2 -2
- package/packages/server/src/__tests__/recovery-server.test.ts +203 -0
- package/packages/server/src/__tests__/session-action-handler-clear-queue.test.ts +153 -0
- package/packages/server/src/__tests__/session-action-handler-headless-reload.test.ts +43 -0
- package/packages/server/src/__tests__/session-order-manager.test.ts +9 -0
- package/packages/server/src/__tests__/session-order-reboot.test.ts +9 -0
- package/packages/server/src/__tests__/session-ordering-integration.test.ts +9 -0
- package/packages/server/src/browser-gateway.ts +83 -5
- package/packages/server/src/browser-handlers/directory-handler.ts +69 -0
- package/packages/server/src/browser-handlers/session-action-handler.ts +89 -0
- package/packages/server/src/browser-handlers/subscription-handler.ts +23 -0
- package/packages/server/src/changelog-parser.ts +1 -1
- package/packages/server/src/cli.ts +68 -250
- package/packages/server/src/event-status-extraction.ts +14 -62
- package/packages/server/src/event-wiring.ts +23 -10
- package/packages/server/src/memory-session-manager.ts +4 -0
- package/packages/server/src/pi-core-checker.ts +1 -1
- package/packages/server/src/pi-dev-version-check.ts +1 -1
- package/packages/server/src/pi-version-skew.ts +24 -46
- package/packages/server/src/plugin-intent-cache.ts +67 -0
- package/packages/server/src/preferences-store.ts +199 -13
- package/packages/server/src/recovery-server.ts +366 -0
- package/packages/server/src/routes/__tests__/manifest-route.test.ts +138 -0
- package/packages/server/src/routes/doctor-routes.ts +26 -21
- package/packages/server/src/routes/manifest-route.ts +162 -0
- package/packages/server/src/routes/openspec-routes.ts +4 -25
- package/packages/server/src/routes/pi-changelog-routes.ts +5 -24
- package/packages/server/src/routes/pi-core-routes.ts +3 -23
- package/packages/server/src/routes/plugin-activation-routes.ts +193 -0
- package/packages/server/src/routes/recommended-routes.ts +21 -0
- package/packages/server/src/routes/system-routes.ts +73 -11
- package/packages/server/src/server.ts +105 -307
- package/packages/server/src/session-api.ts +5 -63
- package/packages/shared/package.json +1 -1
- package/packages/shared/src/__tests__/binary-lookup-resolveJiti.test.ts +28 -0
- package/packages/shared/src/__tests__/binary-lookup-spawn-env.test.ts +61 -0
- package/packages/shared/src/__tests__/binary-lookup.test.ts +16 -0
- package/packages/shared/src/__tests__/bridge-register.test.ts +67 -0
- package/packages/shared/src/__tests__/ci-electron-no-side-effects.test.ts +129 -0
- package/packages/shared/src/__tests__/config.test.ts +40 -0
- package/packages/shared/src/__tests__/dashboard-paths.test.ts +81 -0
- package/packages/shared/src/__tests__/ensure-windows-path.test.ts +112 -0
- package/packages/shared/src/__tests__/intent-types.test.ts +120 -0
- package/packages/shared/src/__tests__/jiti-packages-parity.test.ts +85 -0
- package/packages/shared/src/__tests__/legacy-managed-dir.test.ts +59 -0
- package/packages/shared/src/__tests__/no-direct-child-process.test.ts +12 -0
- package/packages/shared/src/__tests__/no-electron-execpath-spawn.test.ts +149 -0
- package/packages/shared/src/__tests__/no-flow-command-route-claims.test.ts +71 -0
- package/packages/shared/src/__tests__/no-flow-references-in-shell.test.ts +221 -0
- package/packages/shared/src/__tests__/no-managed-dir-reference.test.ts +134 -0
- package/packages/shared/src/__tests__/no-pi-dashboard-version-jiti-gate.test.ts +41 -0
- package/packages/shared/src/__tests__/no-primitive-direct-import.test.ts +235 -0
- package/packages/shared/src/__tests__/no-server-imports-in-resolver.test.ts +53 -0
- package/packages/shared/src/__tests__/node-spawn-jiti-contract.test.ts +54 -101
- package/packages/shared/src/__tests__/node-spawn.test.ts +29 -13
- package/packages/shared/src/__tests__/pi-package-resolver.test.ts +300 -0
- package/packages/shared/src/__tests__/plugin-activation-contracts.test.ts +74 -0
- package/packages/shared/src/__tests__/plugin-bridge-classify-source.test.ts +73 -0
- package/packages/shared/src/__tests__/plugin-bridge-register-extended.test.ts +17 -5
- package/packages/shared/src/__tests__/plugin-bridge-register-packages.test.ts +233 -0
- package/packages/shared/src/__tests__/plugin-bridge-register.test.ts +19 -9
- package/packages/shared/src/__tests__/publish-workflow-contract.test.ts +154 -15
- package/packages/shared/src/__tests__/recommended-extensions.test.ts +28 -10
- package/packages/shared/src/__tests__/resolver-parity-with-scanner.test.ts +76 -0
- package/packages/shared/src/__tests__/server-identity.test.ts +127 -0
- package/packages/shared/src/__tests__/server-launcher.test.ts +35 -0
- package/packages/shared/src/__tests__/source-matching.test.ts +5 -5
- package/packages/shared/src/__tests__/sync-versions-spec.test.ts +76 -0
- package/packages/shared/src/__tests__/tool-registry-definitions.test.ts +50 -2
- package/packages/shared/src/bridge-register.ts +35 -2
- package/packages/shared/src/browser-protocol.ts +176 -2
- package/packages/shared/src/config.ts +12 -0
- package/packages/shared/src/dashboard-paths.ts +69 -0
- package/packages/shared/src/dashboard-plugin/index.ts +2 -0
- package/packages/shared/src/dashboard-plugin/intent-types.ts +93 -0
- package/packages/shared/src/dashboard-plugin/manifest-types.ts +55 -1
- package/packages/shared/src/dashboard-plugin/plugin-status.ts +82 -0
- package/packages/shared/src/dashboard-plugin/slot-props.ts +11 -0
- package/packages/shared/src/dashboard-plugin/slot-types.ts +16 -2
- package/packages/shared/src/dashboard-plugin/ui-primitives.ts +287 -0
- package/packages/shared/src/dashboard-starter.ts +22 -0
- package/packages/shared/src/doctor-core.ts +49 -27
- package/packages/shared/src/launch-source-types.ts +9 -9
- package/packages/shared/src/legacy-managed-dir.ts +97 -0
- package/packages/shared/src/mdns-discovery.ts +4 -1
- package/packages/shared/src/pi-package-resolver.ts +388 -0
- package/packages/shared/src/platform/binary-lookup.ts +27 -3
- package/packages/shared/src/platform/ensure-windows-path.ts +95 -0
- package/packages/shared/src/platform/exec.ts +22 -0
- package/packages/shared/src/platform/node-spawn.ts +42 -41
- package/packages/shared/src/plugin-bridge-register.ts +275 -18
- package/packages/shared/src/protocol.ts +94 -2
- package/packages/shared/src/recommended-extensions.ts +34 -10
- package/packages/shared/src/server-identity.ts +74 -5
- package/packages/shared/src/server-launcher.ts +20 -0
- package/packages/shared/src/source-matching.ts +1 -1
- package/packages/shared/src/tool-registry/__tests__/node-script-toargv-fallback.test.ts +84 -0
- package/packages/shared/src/tool-registry/definitions.ts +91 -7
- package/packages/shared/src/types.ts +12 -8
- package/scripts/maybe-patch-package.cjs +44 -0
- package/packages/server/src/__tests__/bootstrap-install-from-list.test.ts +0 -263
- package/packages/server/src/__tests__/bootstrap-queue.test.ts +0 -120
- package/packages/server/src/__tests__/bootstrap-routes.test.ts +0 -125
- package/packages/server/src/__tests__/bootstrap-state.test.ts +0 -119
- package/packages/server/src/__tests__/cli-bootstrap.test.ts +0 -36
- package/packages/server/src/__tests__/event-status-extraction-flow.test.ts +0 -55
- package/packages/server/src/__tests__/legacy-pi-cleanup.test.ts +0 -149
- package/packages/server/src/__tests__/post-install-openspec-refresh.test.ts +0 -180
- package/packages/server/src/__tests__/post-install-rescan.test.ts +0 -134
- package/packages/server/src/__tests__/system-routes-reextract.test.ts +0 -91
- package/packages/server/src/bootstrap-install-from-list.ts +0 -232
- package/packages/server/src/bootstrap-queue.ts +0 -130
- package/packages/server/src/bootstrap-state.ts +0 -159
- package/packages/server/src/legacy-pi-cleanup.ts +0 -151
- package/packages/server/src/routes/bootstrap-routes.ts +0 -125
- package/packages/shared/src/__tests__/bootstrap/README.md +0 -133
- package/packages/shared/src/__tests__/bootstrap/__snapshots__/cube.test.ts.snap +0 -378
- package/packages/shared/src/__tests__/bootstrap/assertions.ts +0 -136
- package/packages/shared/src/__tests__/bootstrap/cube.test.ts +0 -47
- package/packages/shared/src/__tests__/bootstrap/cube.ts +0 -66
- package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/a-electron.test.ts.snap +0 -84
- package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/b-npm-global.test.ts.snap +0 -90
- package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/c-dev-monorepo.test.ts.snap +0 -34
- package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/d-overrides.test.ts.snap +0 -20
- package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/e-stale-partial.test.ts.snap +0 -62
- package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/f-cwd-variants.test.ts.snap +0 -34
- package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/g-windows-specifics.test.ts.snap +0 -49
- package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/j-path-gui-minimal.test.ts.snap +0 -12
- package/packages/shared/src/__tests__/bootstrap/families/a-electron.test.ts +0 -156
- package/packages/shared/src/__tests__/bootstrap/families/b-npm-global.test.ts +0 -157
- package/packages/shared/src/__tests__/bootstrap/families/c-dev-monorepo.test.ts +0 -102
- package/packages/shared/src/__tests__/bootstrap/families/d-overrides.test.ts +0 -76
- package/packages/shared/src/__tests__/bootstrap/families/e-stale-partial.test.ts +0 -94
- package/packages/shared/src/__tests__/bootstrap/families/f-cwd-variants.test.ts +0 -87
- package/packages/shared/src/__tests__/bootstrap/families/g-windows-specifics.test.ts +0 -143
- package/packages/shared/src/__tests__/bootstrap/families/h-home-drift.test.ts +0 -64
- package/packages/shared/src/__tests__/bootstrap/families/i-malformed-settings.test.ts +0 -77
- package/packages/shared/src/__tests__/bootstrap/families/index.ts +0 -19
- package/packages/shared/src/__tests__/bootstrap/families/j-path-gui-minimal.test.ts +0 -61
- package/packages/shared/src/__tests__/bootstrap/families/k-dashboard-absent.test.ts +0 -50
- package/packages/shared/src/__tests__/bootstrap/families/l-instance-coordination.test.ts +0 -272
- package/packages/shared/src/__tests__/bootstrap/fixtures/dev-monorepo.ts +0 -58
- package/packages/shared/src/__tests__/bootstrap/fixtures/electron-layout.ts +0 -84
- package/packages/shared/src/__tests__/bootstrap/fixtures/index.ts +0 -9
- package/packages/shared/src/__tests__/bootstrap/fixtures/managed-install.ts +0 -85
- package/packages/shared/src/__tests__/bootstrap/fixtures/npm-global-layout.ts +0 -122
- package/packages/shared/src/__tests__/bootstrap/fixtures/pi-versions.ts +0 -36
- package/packages/shared/src/__tests__/bootstrap/fixtures/settings-json.ts +0 -39
- package/packages/shared/src/__tests__/bootstrap/harness.smoke.test.ts +0 -220
- package/packages/shared/src/__tests__/bootstrap/harness.ts +0 -413
- package/packages/shared/src/__tests__/bootstrap/scenarios-skipped.ts +0 -125
- package/packages/shared/src/__tests__/bootstrap/scenarios.ts +0 -132
- package/packages/shared/src/__tests__/bootstrap-install-resolve-npm.test.ts +0 -72
- package/packages/shared/src/__tests__/install-managed-node-bootstrap-order.test.ts +0 -68
- package/packages/shared/src/__tests__/install-managed-node.test.ts +0 -192
- package/packages/shared/src/__tests__/installable-list.test.ts +0 -130
- package/packages/shared/src/__tests__/no-installable-list-in-bridge.test.ts +0 -52
- package/packages/shared/src/bootstrap-install.ts +0 -406
- package/packages/shared/src/installable-list.ts +0 -152
- package/packages/shared/src/launch-source-flag.ts +0 -14
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pi-package-resolver — shared helper that resolves a pi peer package
|
|
3
|
+
* name to its install directory and importable entry path by walking
|
|
4
|
+
* pi's own settings.json files.
|
|
5
|
+
*
|
|
6
|
+
* Pi-coding-agent installs packages into three different filesystem
|
|
7
|
+
* layouts depending on how the user added them to `settings.json#packages[]`:
|
|
8
|
+
*
|
|
9
|
+
* npm:<name>[@version] → ~/.pi/agent/node_modules/<name>/ (user scope)
|
|
10
|
+
* <cwd>/.pi/npm/node_modules/<name>/ (project)
|
|
11
|
+
* git+https://… / git@…/ → ~/.pi/agent/git/<host>/<owner>/<repo>/ (user)
|
|
12
|
+
* <cwd>/.pi/git/<host>/<owner>/<repo>/ (project)
|
|
13
|
+
* /abs/path → the path itself
|
|
14
|
+
* ./rel/path → resolved against settings dir
|
|
15
|
+
*
|
|
16
|
+
* None of these locations are on Node's `node_modules` walk from
|
|
17
|
+
* `process.cwd()`, so `createRequire(cwd).resolve(spec)` fails for every
|
|
18
|
+
* pi-installed peer. This module walks pi's own settings + applies the
|
|
19
|
+
* same path arithmetic pi-coding-agent uses internally, then matches by
|
|
20
|
+
* `package.json#name` to expose an `await import(absPath)`-ready result.
|
|
21
|
+
*
|
|
22
|
+
* Read-on-call contract — performs no installs, mutates nothing, holds
|
|
23
|
+
* no module-level cache. Two settings reads + N package.json reads per
|
|
24
|
+
* resolution; each settings file is ~1 KB. Designed to be called from
|
|
25
|
+
* plugin bridges (`packages/shared/`-only imports allowed) on every
|
|
26
|
+
* probe without amortization.
|
|
27
|
+
*
|
|
28
|
+
* Scope only: walks `packages[]` from `~/.pi/agent/settings.json` and
|
|
29
|
+
* `<cwd>/.pi/settings.json`. Does NOT walk `extensions[]`/`skills[]`/
|
|
30
|
+
* `prompts[]` top-level arrays — those hold file paths to individual
|
|
31
|
+
* extension entry files, not package roots, so `package.json#name`
|
|
32
|
+
* lookup doesn't apply.
|
|
33
|
+
*
|
|
34
|
+
* See change: add-shared-pi-package-resolver.
|
|
35
|
+
*/
|
|
36
|
+
import * as fs from "node:fs";
|
|
37
|
+
import * as os from "node:os";
|
|
38
|
+
import * as path from "node:path";
|
|
39
|
+
|
|
40
|
+
import { rootGlobalOr } from "./platform/npm.js";
|
|
41
|
+
|
|
42
|
+
// ── Public surface ──────────────────────────────────────────────────
|
|
43
|
+
|
|
44
|
+
export interface ResolvePiPackageOptions {
|
|
45
|
+
/** Override `~/.pi/agent`. Default: `path.join(os.homedir(), ".pi", "agent")`. */
|
|
46
|
+
agentDir?: string;
|
|
47
|
+
/** Project-scope cwd (enables reading `<cwd>/.pi/settings.json`). */
|
|
48
|
+
cwd?: string;
|
|
49
|
+
/** "user" | "project" | "any" (default). "any" reads project first, then user. */
|
|
50
|
+
scope?: "user" | "project" | "any";
|
|
51
|
+
/**
|
|
52
|
+
* Override `npm root -g`. Defaults to `npm.rootGlobalOr("")`. Tests pass
|
|
53
|
+
* a tmp path to avoid shelling out and to make `npm:` resolution
|
|
54
|
+
* hermetic.
|
|
55
|
+
*/
|
|
56
|
+
npmRoot?: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface ResolvedPiPackage {
|
|
60
|
+
/** Absolute path to the package root. */
|
|
61
|
+
packageDir: string;
|
|
62
|
+
/**
|
|
63
|
+
* Absolute path to the importable entry file (from `exports["."]` →
|
|
64
|
+
* `main` → `pi.extensions[0]` → `index.js` → `index.ts`), or `null`
|
|
65
|
+
* when no candidate exists on disk.
|
|
66
|
+
*/
|
|
67
|
+
entryPath: string | null;
|
|
68
|
+
/** Which scope matched. */
|
|
69
|
+
scope: "user" | "project";
|
|
70
|
+
/** Original `settings.json#packages[]` entry that produced the match. */
|
|
71
|
+
source: string;
|
|
72
|
+
/** Parsed `package.json#name`, or `null` when no readable package.json. */
|
|
73
|
+
packageJsonName: string | null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function resolvePiPackage(
|
|
77
|
+
spec: string,
|
|
78
|
+
opts: ResolvePiPackageOptions = {},
|
|
79
|
+
): ResolvedPiPackage | null {
|
|
80
|
+
const agentDir = opts.agentDir ?? path.join(os.homedir(), ".pi", "agent");
|
|
81
|
+
const scope = opts.scope ?? "any";
|
|
82
|
+
const cwd = opts.cwd;
|
|
83
|
+
const npmRoot = opts.npmRoot ?? rootGlobalOr("");
|
|
84
|
+
|
|
85
|
+
// Project scope first (matches deepMergeSettings precedence).
|
|
86
|
+
if ((scope === "project" || scope === "any") && cwd) {
|
|
87
|
+
const hit = findInScope(spec, "project", { agentDir, cwd, npmRoot });
|
|
88
|
+
if (hit) return hit;
|
|
89
|
+
}
|
|
90
|
+
if (scope === "user" || scope === "any") {
|
|
91
|
+
const hit = findInScope(spec, "user", { agentDir, cwd, npmRoot });
|
|
92
|
+
if (hit) return hit;
|
|
93
|
+
}
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function resolvePiPackageEntry(
|
|
98
|
+
spec: string,
|
|
99
|
+
opts: ResolvePiPackageOptions = {},
|
|
100
|
+
): string | null {
|
|
101
|
+
return resolvePiPackage(spec, opts)?.entryPath ?? null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* List every resolved pi package across the requested scopes. Iterates
|
|
106
|
+
* `settings.packages[]` in project then user scope (matching
|
|
107
|
+
* `resolvePiPackage`'s precedence) and yields a `ResolvedPiPackage` for
|
|
108
|
+
* every entry whose computed install path exists on disk.
|
|
109
|
+
*
|
|
110
|
+
* Unlike `resolvePiPackage`, this performs no name match — callers that
|
|
111
|
+
* need to find any package satisfying a per-directory predicate (e.g.
|
|
112
|
+
* "contains `bridge.ts`") consume the list directly.
|
|
113
|
+
*
|
|
114
|
+
* Order: project-scope hits first, then user-scope. Duplicates (same
|
|
115
|
+
* absolute packageDir) are de-duplicated keeping the first occurrence.
|
|
116
|
+
*
|
|
117
|
+
* Added by change: fix-electron-cold-launch-probe-cascade (Bug A).
|
|
118
|
+
* Used by `launch-source.ts::probePiExtension` to iterate the actual
|
|
119
|
+
* `packages[]` schema instead of the non-existent `extensions[]`.
|
|
120
|
+
*/
|
|
121
|
+
export function listPiPackages(opts: ResolvePiPackageOptions = {}): ResolvedPiPackage[] {
|
|
122
|
+
const agentDir = opts.agentDir ?? path.join(os.homedir(), ".pi", "agent");
|
|
123
|
+
const scope = opts.scope ?? "any";
|
|
124
|
+
const cwd = opts.cwd;
|
|
125
|
+
const npmRoot = opts.npmRoot ?? rootGlobalOr("");
|
|
126
|
+
|
|
127
|
+
const out: ResolvedPiPackage[] = [];
|
|
128
|
+
const seen = new Set<string>();
|
|
129
|
+
const collect = (s: "user" | "project") => {
|
|
130
|
+
for (const r of iterateInScope(s, { agentDir, cwd, npmRoot })) {
|
|
131
|
+
if (seen.has(r.packageDir)) continue;
|
|
132
|
+
seen.add(r.packageDir);
|
|
133
|
+
out.push(r);
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
if ((scope === "project" || scope === "any") && cwd) collect("project");
|
|
137
|
+
if (scope === "user" || scope === "any") collect("user");
|
|
138
|
+
return out;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// ── Internals ───────────────────────────────────────────────────────
|
|
142
|
+
|
|
143
|
+
interface ScopeContext {
|
|
144
|
+
agentDir: string;
|
|
145
|
+
cwd?: string;
|
|
146
|
+
npmRoot: string;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
interface ParsedSource {
|
|
150
|
+
kind: "npm" | "git" | "local-abs" | "local-rel";
|
|
151
|
+
/** For npm: package name (without version). For git: cleaned URL path. For local: raw path. */
|
|
152
|
+
value: string;
|
|
153
|
+
/** Original entry as it appeared in settings.json. */
|
|
154
|
+
original: string;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function findInScope(
|
|
158
|
+
spec: string,
|
|
159
|
+
scope: "user" | "project",
|
|
160
|
+
ctx: ScopeContext,
|
|
161
|
+
): ResolvedPiPackage | null {
|
|
162
|
+
for (const r of iterateInScope(scope, ctx)) {
|
|
163
|
+
if (r.packageJsonName === spec) return r;
|
|
164
|
+
}
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Shared iteration core used by both `findInScope` (name-matched lookup)
|
|
170
|
+
* and `listPiPackages` (no name filter). Yields every entry whose computed
|
|
171
|
+
* install path exists on disk; reads `package.json` lazily.
|
|
172
|
+
*/
|
|
173
|
+
function* iterateInScope(
|
|
174
|
+
scope: "user" | "project",
|
|
175
|
+
ctx: ScopeContext,
|
|
176
|
+
): Generator<ResolvedPiPackage> {
|
|
177
|
+
const settingsPath =
|
|
178
|
+
scope === "user"
|
|
179
|
+
? path.join(ctx.agentDir, "settings.json")
|
|
180
|
+
: ctx.cwd
|
|
181
|
+
? path.join(ctx.cwd, ".pi", "settings.json")
|
|
182
|
+
: null;
|
|
183
|
+
if (!settingsPath) return;
|
|
184
|
+
|
|
185
|
+
const packages = readSettingsPackages(settingsPath);
|
|
186
|
+
if (packages.length === 0) return;
|
|
187
|
+
|
|
188
|
+
const settingsDir = path.dirname(settingsPath);
|
|
189
|
+
for (const entry of packages) {
|
|
190
|
+
const parsed = parseSource(entry);
|
|
191
|
+
if (!parsed) continue;
|
|
192
|
+
const packageDir = computeInstallPath(parsed, scope, ctx, settingsDir);
|
|
193
|
+
if (!packageDir || !fs.existsSync(packageDir)) continue;
|
|
194
|
+
|
|
195
|
+
const pkgJson = readPackageJson(packageDir);
|
|
196
|
+
const name = pkgJson?.name ?? null;
|
|
197
|
+
const entryPath = resolveEntryPath(packageDir, pkgJson ?? {});
|
|
198
|
+
yield {
|
|
199
|
+
packageDir,
|
|
200
|
+
entryPath,
|
|
201
|
+
scope,
|
|
202
|
+
source: parsed.original,
|
|
203
|
+
packageJsonName: name,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Parse a `settings.json#packages[]` entry. Mirrors
|
|
210
|
+
* `packages/server/src/pi-resource-scanner.ts::resolvePackagePath`
|
|
211
|
+
* parsing arms and pi-coding-agent `package-manager.js::parseSource`.
|
|
212
|
+
*/
|
|
213
|
+
function parseSource(entry: unknown): ParsedSource | null {
|
|
214
|
+
// Pi accepts both string entries and `{source: "..."}` objects.
|
|
215
|
+
const original =
|
|
216
|
+
typeof entry === "string"
|
|
217
|
+
? entry
|
|
218
|
+
: typeof entry === "object" && entry !== null && typeof (entry as { source?: unknown }).source === "string"
|
|
219
|
+
? (entry as { source: string }).source
|
|
220
|
+
: "";
|
|
221
|
+
if (!original) return null;
|
|
222
|
+
|
|
223
|
+
if (original.startsWith("npm:")) {
|
|
224
|
+
const pkgName = original.slice(4).replace(/@[^/]*$/, "");
|
|
225
|
+
return { kind: "npm", value: pkgName, original };
|
|
226
|
+
}
|
|
227
|
+
if (
|
|
228
|
+
original.startsWith("git:") ||
|
|
229
|
+
original.startsWith("git@") ||
|
|
230
|
+
original.startsWith("https://") ||
|
|
231
|
+
original.startsWith("http://") ||
|
|
232
|
+
original.startsWith("ssh://") ||
|
|
233
|
+
original.startsWith("github:")
|
|
234
|
+
) {
|
|
235
|
+
let url = original.replace(/^git:/, "");
|
|
236
|
+
url = url.replace(/^github:/, "github.com/");
|
|
237
|
+
url = url.replace(/^git@([^:]+):/, "$1/");
|
|
238
|
+
url = url.replace(/^(https?|ssh|git):\/\//, "");
|
|
239
|
+
url = url.replace(/^[^@]+@/, "");
|
|
240
|
+
url = url.replace(/\.git$/, "").replace(/@[^/]*$/, "");
|
|
241
|
+
return { kind: "git", value: url, original };
|
|
242
|
+
}
|
|
243
|
+
if (path.isAbsolute(original)) {
|
|
244
|
+
return { kind: "local-abs", value: original, original };
|
|
245
|
+
}
|
|
246
|
+
return { kind: "local-rel", value: original, original };
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Compute the absolute install directory for a parsed entry. Mirrors
|
|
251
|
+
* pi-coding-agent's own arithmetic in `dist/core/package-manager.js`
|
|
252
|
+
* `getNpmInstallPath` / `getGitInstallPath` / `resolvePathFromBase`.
|
|
253
|
+
*/
|
|
254
|
+
function computeInstallPath(
|
|
255
|
+
parsed: ParsedSource,
|
|
256
|
+
scope: "user" | "project",
|
|
257
|
+
ctx: ScopeContext,
|
|
258
|
+
settingsDir: string,
|
|
259
|
+
): string | null {
|
|
260
|
+
switch (parsed.kind) {
|
|
261
|
+
case "npm": {
|
|
262
|
+
if (scope === "project") {
|
|
263
|
+
if (!ctx.cwd) return null;
|
|
264
|
+
return path.join(ctx.cwd, ".pi", "npm", "node_modules", parsed.value);
|
|
265
|
+
}
|
|
266
|
+
if (!ctx.npmRoot) return null;
|
|
267
|
+
return path.join(ctx.npmRoot, parsed.value);
|
|
268
|
+
}
|
|
269
|
+
case "git": {
|
|
270
|
+
if (scope === "project") {
|
|
271
|
+
if (!ctx.cwd) return null;
|
|
272
|
+
return path.join(ctx.cwd, ".pi", "git", parsed.value);
|
|
273
|
+
}
|
|
274
|
+
return path.join(ctx.agentDir, "git", parsed.value);
|
|
275
|
+
}
|
|
276
|
+
case "local-abs":
|
|
277
|
+
return parsed.value;
|
|
278
|
+
case "local-rel":
|
|
279
|
+
return path.resolve(settingsDir, parsed.value);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Read `<settingsPath>`'s `packages[]` array. Returns `[]` on missing
|
|
285
|
+
* file, parse error, or missing `packages` field. Never throws.
|
|
286
|
+
*/
|
|
287
|
+
function readSettingsPackages(settingsPath: string): unknown[] {
|
|
288
|
+
try {
|
|
289
|
+
if (!fs.existsSync(settingsPath)) return [];
|
|
290
|
+
const raw = fs.readFileSync(settingsPath, "utf-8");
|
|
291
|
+
const parsed = JSON.parse(raw);
|
|
292
|
+
const pkgs = (parsed && typeof parsed === "object" ? (parsed as { packages?: unknown }).packages : undefined);
|
|
293
|
+
return Array.isArray(pkgs) ? pkgs : [];
|
|
294
|
+
} catch (err) {
|
|
295
|
+
console.warn(
|
|
296
|
+
`[pi-package-resolver] failed to read ${settingsPath}: ${(err as Error).message}`,
|
|
297
|
+
);
|
|
298
|
+
return [];
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
interface PackageJsonShape {
|
|
303
|
+
name?: string;
|
|
304
|
+
main?: string;
|
|
305
|
+
exports?: unknown;
|
|
306
|
+
pi?: { extensions?: unknown };
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function readPackageJson(packageDir: string): PackageJsonShape | null {
|
|
310
|
+
const pkgJsonPath = path.join(packageDir, "package.json");
|
|
311
|
+
try {
|
|
312
|
+
if (!fs.existsSync(pkgJsonPath)) return null;
|
|
313
|
+
const raw = fs.readFileSync(pkgJsonPath, "utf-8");
|
|
314
|
+
return JSON.parse(raw) as PackageJsonShape;
|
|
315
|
+
} catch (err) {
|
|
316
|
+
console.warn(
|
|
317
|
+
`[pi-package-resolver] failed to parse ${pkgJsonPath}: ${(err as Error).message}`,
|
|
318
|
+
);
|
|
319
|
+
return null;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Entry-point resolution priority:
|
|
325
|
+
* 1. package.json#exports["."] (string, or `{import|default|node}`)
|
|
326
|
+
* 2. package.json#main
|
|
327
|
+
* 3. package.json#pi.extensions[0]
|
|
328
|
+
* 4. <packageDir>/index.js
|
|
329
|
+
* 5. <packageDir>/index.ts
|
|
330
|
+
*
|
|
331
|
+
* Each candidate is existence-checked before being returned. Returns
|
|
332
|
+
* `null` when no candidate resolves to an existing file.
|
|
333
|
+
*/
|
|
334
|
+
function resolveEntryPath(packageDir: string, pkg: PackageJsonShape): string | null {
|
|
335
|
+
// 1. exports["."]
|
|
336
|
+
const exportsDot = extractExportsDot(pkg.exports);
|
|
337
|
+
if (typeof exportsDot === "string") {
|
|
338
|
+
const candidate = path.resolve(packageDir, exportsDot);
|
|
339
|
+
if (fs.existsSync(candidate)) return candidate;
|
|
340
|
+
}
|
|
341
|
+
// 2. main
|
|
342
|
+
if (typeof pkg.main === "string" && pkg.main.length > 0) {
|
|
343
|
+
const candidate = path.resolve(packageDir, pkg.main);
|
|
344
|
+
if (fs.existsSync(candidate)) return candidate;
|
|
345
|
+
}
|
|
346
|
+
// 3. pi.extensions[0]
|
|
347
|
+
if (pkg.pi && Array.isArray(pkg.pi.extensions) && pkg.pi.extensions.length > 0) {
|
|
348
|
+
const first = pkg.pi.extensions[0];
|
|
349
|
+
if (typeof first === "string") {
|
|
350
|
+
const candidate = path.resolve(packageDir, first);
|
|
351
|
+
if (fs.existsSync(candidate)) return candidate;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
// 4. index.js
|
|
355
|
+
const idxJs = path.join(packageDir, "index.js");
|
|
356
|
+
if (fs.existsSync(idxJs)) return idxJs;
|
|
357
|
+
// 5. index.ts
|
|
358
|
+
const idxTs = path.join(packageDir, "index.ts");
|
|
359
|
+
if (fs.existsSync(idxTs)) return idxTs;
|
|
360
|
+
|
|
361
|
+
return null;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Extract the "." subpath from `package.json#exports`. Supports:
|
|
366
|
+
* - "./entry.js" (string form)
|
|
367
|
+
* - { ".": "./entry.js" } (path-only object)
|
|
368
|
+
* - { ".": { "import": "...", "default": "..." } } (conditional)
|
|
369
|
+
* Returns the first matching string from `import` → `default` → `node`.
|
|
370
|
+
* Returns `null` for unsupported shapes.
|
|
371
|
+
*/
|
|
372
|
+
function extractExportsDot(exportsField: unknown): string | null {
|
|
373
|
+
if (typeof exportsField === "string") {
|
|
374
|
+
return exportsField; // bare exports applies to "." implicitly
|
|
375
|
+
}
|
|
376
|
+
if (!exportsField || typeof exportsField !== "object") return null;
|
|
377
|
+
const root = exportsField as Record<string, unknown>;
|
|
378
|
+
const dot = root["."];
|
|
379
|
+
if (typeof dot === "string") return dot;
|
|
380
|
+
if (dot && typeof dot === "object") {
|
|
381
|
+
const cond = dot as Record<string, unknown>;
|
|
382
|
+
for (const key of ["import", "default", "node"]) {
|
|
383
|
+
const v = cond[key];
|
|
384
|
+
if (typeof v === "string") return v;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
return null;
|
|
388
|
+
}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* with a single configurable resolver.
|
|
5
5
|
*/
|
|
6
6
|
import { execSync, spawnSync, buildSafeArgv } from "./exec.js";
|
|
7
|
+
import { ensureWindowsSystemPath } from "./ensure-windows-path.js";
|
|
7
8
|
import { existsSync, realpathSync } from "node:fs";
|
|
8
9
|
import { createRequire } from "node:module";
|
|
9
10
|
import { pathToFileURL } from "node:url";
|
|
@@ -442,8 +443,29 @@ export class ToolResolver {
|
|
|
442
443
|
/**
|
|
443
444
|
* Build a spawn environment with managed bin, node bin, extra dirs,
|
|
444
445
|
* and common user bin dirs prepended to PATH.
|
|
446
|
+
*
|
|
447
|
+
* On Windows, additionally guarantees canonical system directories
|
|
448
|
+
* (System32, Wbem, PowerShell, WindowsApps) are present via
|
|
449
|
+
* `ensureWindowsSystemPath`. See change:
|
|
450
|
+
* fix-windows-path-system32-missing.
|
|
445
451
|
*/
|
|
446
|
-
buildSpawnEnv(
|
|
452
|
+
buildSpawnEnv(
|
|
453
|
+
base: NodeJS.ProcessEnv = process.env,
|
|
454
|
+
opts: { platform?: NodeJS.Platform; exists?: (p: string) => boolean } = {},
|
|
455
|
+
): NodeJS.ProcessEnv {
|
|
456
|
+
// Strip Electron-specific vars so spawned child processes (pi sessions,
|
|
457
|
+
// npm installs) don't accidentally run as Electron-node mode.
|
|
458
|
+
const ELECTRON_VARS_TO_STRIP = new Set([
|
|
459
|
+
"ELECTRON_RUN_AS_NODE",
|
|
460
|
+
"ELECTRON_DEFAULT_ERROR_MODE",
|
|
461
|
+
"ELECTRON_ENABLE_STACK_DUMPING",
|
|
462
|
+
]);
|
|
463
|
+
const strippedBase: NodeJS.ProcessEnv = {};
|
|
464
|
+
for (const [k, v] of Object.entries(base)) {
|
|
465
|
+
if (!ELECTRON_VARS_TO_STRIP.has(k)) strippedBase[k] = v;
|
|
466
|
+
}
|
|
467
|
+
base = strippedBase;
|
|
468
|
+
|
|
447
469
|
const currentPath = base.PATH || "";
|
|
448
470
|
const parts: string[] = [];
|
|
449
471
|
|
|
@@ -474,8 +496,10 @@ export class ToolResolver {
|
|
|
474
496
|
}
|
|
475
497
|
}
|
|
476
498
|
|
|
477
|
-
|
|
478
|
-
|
|
499
|
+
const out = parts.length === 0
|
|
500
|
+
? base
|
|
501
|
+
: { ...base, PATH: `${parts.join(path.delimiter)}${path.delimiter}${currentPath}` };
|
|
502
|
+
return ensureWindowsSystemPath(out, opts);
|
|
479
503
|
}
|
|
480
504
|
}
|
|
481
505
|
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ensureWindowsSystemPath — restore canonical Windows system directories
|
|
3
|
+
* on `env.PATH` when they're absent.
|
|
4
|
+
*
|
|
5
|
+
* Electron / GUI-launched processes can inherit a stripped PATH that
|
|
6
|
+
* lacks `C:\Windows\System32`. Without System32 on PATH, every spawn
|
|
7
|
+
* of `where.exe`, `powershell.exe`, `tasklist.exe`, `taskkill.exe`,
|
|
8
|
+
* `wmic.exe` fails with ENOENT and cascades into Tools-panel red rows,
|
|
9
|
+
* empty process-scanner output, and broken bridge spawns.
|
|
10
|
+
*
|
|
11
|
+
* This helper is idempotent: calling it twice on the same env returns
|
|
12
|
+
* an env identical to a single call. On non-Windows hosts it is a
|
|
13
|
+
* no-op.
|
|
14
|
+
*
|
|
15
|
+
* See change: fix-windows-path-system32-missing.
|
|
16
|
+
*/
|
|
17
|
+
import { existsSync } from "node:fs";
|
|
18
|
+
import path from "node:path";
|
|
19
|
+
|
|
20
|
+
export interface EnsureWindowsSystemPathOpts {
|
|
21
|
+
/** Override `process.platform` for tests. */
|
|
22
|
+
platform?: NodeJS.Platform;
|
|
23
|
+
/** Override `fs.existsSync` for tests. */
|
|
24
|
+
exists?: (p: string) => boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Prepend canonical Windows system directories to `env.PATH` when:
|
|
29
|
+
* - the host is Windows (or `opts.platform === "win32"`),
|
|
30
|
+
* - the directory physically exists on disk (via `opts.exists`),
|
|
31
|
+
* - the directory is not already substring-present in PATH
|
|
32
|
+
* (case-insensitive — Windows PATH semantics).
|
|
33
|
+
*
|
|
34
|
+
* Returns the input env unchanged on non-Windows hosts (no-op).
|
|
35
|
+
*
|
|
36
|
+
* Idempotence invariant:
|
|
37
|
+
* ensureWindowsSystemPath(ensureWindowsSystemPath(env)) deep-equals
|
|
38
|
+
* ensureWindowsSystemPath(env).
|
|
39
|
+
*/
|
|
40
|
+
export function ensureWindowsSystemPath(
|
|
41
|
+
env: NodeJS.ProcessEnv,
|
|
42
|
+
opts: EnsureWindowsSystemPathOpts = {},
|
|
43
|
+
): NodeJS.ProcessEnv {
|
|
44
|
+
const platform = opts.platform ?? process.platform;
|
|
45
|
+
if (platform !== "win32") return env;
|
|
46
|
+
|
|
47
|
+
const exists = opts.exists ?? existsSync;
|
|
48
|
+
|
|
49
|
+
const systemRoot = env.SYSTEMROOT || env.SystemRoot || env.systemroot || "C:\\Windows";
|
|
50
|
+
const localAppData = env.LOCALAPPDATA || env.LocalAppData || env.localappdata || "";
|
|
51
|
+
|
|
52
|
+
// Use win32 path semantics regardless of host OS — these paths only
|
|
53
|
+
// matter on Windows, and tests on POSIX hosts must still produce
|
|
54
|
+
// backslash-separated candidates.
|
|
55
|
+
const join = path.win32.join;
|
|
56
|
+
|
|
57
|
+
const candidates: string[] = [
|
|
58
|
+
join(systemRoot, "System32"),
|
|
59
|
+
systemRoot,
|
|
60
|
+
join(systemRoot, "System32", "Wbem"),
|
|
61
|
+
join(systemRoot, "System32", "WindowsPowerShell", "v1.0"),
|
|
62
|
+
join(systemRoot, "System32", "OpenSSH"),
|
|
63
|
+
];
|
|
64
|
+
if (localAppData) {
|
|
65
|
+
candidates.push(join(localAppData, "Microsoft", "WindowsApps"));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const currentPath = env.PATH ?? "";
|
|
69
|
+
const currentLower = currentPath.toLowerCase();
|
|
70
|
+
|
|
71
|
+
// Track lower-cased additions so duplicates within the candidate
|
|
72
|
+
// list itself don't slip through (defensive — current list has none).
|
|
73
|
+
const addedLower = new Set<string>();
|
|
74
|
+
const toAdd: string[] = [];
|
|
75
|
+
for (const dir of candidates) {
|
|
76
|
+
if (!exists(dir)) continue;
|
|
77
|
+
const dirLower = dir.toLowerCase();
|
|
78
|
+
if (currentLower.includes(dirLower)) continue;
|
|
79
|
+
if (addedLower.has(dirLower)) continue;
|
|
80
|
+
addedLower.add(dirLower);
|
|
81
|
+
toAdd.push(dir);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (toAdd.length === 0) return env;
|
|
85
|
+
|
|
86
|
+
// Windows PATH delimiter is `;`. Hard-code it: host may be POSIX
|
|
87
|
+
// during tests (`path.delimiter === ":"`) but we're building a
|
|
88
|
+
// Windows-targeted PATH.
|
|
89
|
+
const DELIM = ";";
|
|
90
|
+
const newPath = currentPath
|
|
91
|
+
? `${toAdd.join(DELIM)}${DELIM}${currentPath}`
|
|
92
|
+
: toAdd.join(DELIM);
|
|
93
|
+
|
|
94
|
+
return { ...env, PATH: newPath };
|
|
95
|
+
}
|
|
@@ -21,11 +21,13 @@
|
|
|
21
21
|
*/
|
|
22
22
|
import {
|
|
23
23
|
execSync as nodeExecSync,
|
|
24
|
+
execFileSync as nodeExecFileSync,
|
|
24
25
|
exec as nodeExec,
|
|
25
26
|
execFile as nodeExecFile,
|
|
26
27
|
spawnSync as nodeSpawnSync,
|
|
27
28
|
spawn as nodeSpawn,
|
|
28
29
|
type ExecSyncOptions,
|
|
30
|
+
type ExecFileSyncOptions,
|
|
29
31
|
type ExecOptions,
|
|
30
32
|
type ExecFileOptions,
|
|
31
33
|
type SpawnSyncOptions,
|
|
@@ -127,6 +129,25 @@ export function execSync(
|
|
|
127
129
|
return nodeExecSync(command, withHide(options));
|
|
128
130
|
}
|
|
129
131
|
|
|
132
|
+
/** Wrapped `execFileSync`. Always `windowsHide: true` unless overridden. */
|
|
133
|
+
export function execFileSync(
|
|
134
|
+
file: string,
|
|
135
|
+
args: readonly string[] | undefined,
|
|
136
|
+
options: ExecFileSyncOptions & { encoding: BufferEncoding },
|
|
137
|
+
): string;
|
|
138
|
+
export function execFileSync(
|
|
139
|
+
file: string,
|
|
140
|
+
args?: readonly string[],
|
|
141
|
+
options?: ExecFileSyncOptions,
|
|
142
|
+
): Buffer | string;
|
|
143
|
+
export function execFileSync(
|
|
144
|
+
file: string,
|
|
145
|
+
args?: readonly string[],
|
|
146
|
+
options?: ExecFileSyncOptions,
|
|
147
|
+
): Buffer | string {
|
|
148
|
+
return nodeExecFileSync(file, args ?? [], withHide(options));
|
|
149
|
+
}
|
|
150
|
+
|
|
130
151
|
/** Wrapped `spawnSync`. Always `windowsHide: true` unless overridden. */
|
|
131
152
|
export function spawnSync<T extends string | Buffer = Buffer>(
|
|
132
153
|
command: string,
|
|
@@ -211,6 +232,7 @@ export const execFileAsync = promisify(execFile) as unknown as (
|
|
|
211
232
|
|
|
212
233
|
export type {
|
|
213
234
|
ExecSyncOptions,
|
|
235
|
+
ExecFileSyncOptions,
|
|
214
236
|
ExecOptions,
|
|
215
237
|
ExecFileOptions,
|
|
216
238
|
SpawnSyncOptions,
|