@blackbelt-technology/pi-agent-dashboard 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +67 -116
- package/README.md +93 -7
- package/docs/architecture.md +408 -9
- package/package.json +6 -4
- package/packages/extension/package.json +11 -3
- package/packages/extension/src/__tests__/enrich-model-metadata.test.ts +201 -0
- package/packages/extension/src/__tests__/git-info.test.ts +67 -55
- package/packages/extension/src/__tests__/openspec-poller.test.ts +101 -96
- package/packages/extension/src/__tests__/process-scanner-kill.test.ts +61 -0
- package/packages/extension/src/__tests__/provider-register-reload.test.ts +394 -0
- package/packages/extension/src/__tests__/server-auto-start.test.ts +95 -4
- package/packages/extension/src/__tests__/server-launcher.test.ts +16 -0
- package/packages/extension/src/bridge.ts +69 -2
- package/packages/extension/src/dev-build.ts +1 -1
- package/packages/extension/src/git-info.ts +9 -19
- package/packages/extension/src/pi-env.d.ts +1 -0
- package/packages/extension/src/process-scanner.ts +72 -38
- package/packages/extension/src/provider-register.ts +304 -16
- package/packages/extension/src/server-auto-start.ts +27 -1
- package/packages/extension/src/server-launcher.ts +71 -27
- package/packages/server/package.json +16 -2
- package/packages/server/src/__tests__/bootstrap-queue.test.ts +120 -0
- package/packages/server/src/__tests__/bootstrap-routes.test.ts +125 -0
- package/packages/server/src/__tests__/bootstrap-state.test.ts +119 -0
- package/packages/server/src/__tests__/browse-endpoint.test.ts +17 -0
- package/packages/server/src/__tests__/cli-parse.test.ts +11 -0
- package/packages/server/src/__tests__/concurrent-launch.test.ts +110 -0
- package/packages/server/src/__tests__/config-api.test.ts +68 -0
- package/packages/server/src/__tests__/crash-recovery.test.ts +88 -0
- package/packages/server/src/__tests__/directory-service.test.ts +234 -8
- package/packages/server/src/__tests__/editor-registry.test.ts +28 -15
- package/packages/server/src/__tests__/extension-register-appimage.test.ts +5 -1
- package/packages/server/src/__tests__/extension-register.test.ts +3 -1
- package/packages/server/src/__tests__/find-port-holders.test.ts +94 -0
- package/packages/server/src/__tests__/force-kill-handler.test.ts +57 -8
- package/packages/server/src/__tests__/home-lock-escape-hatch.test.ts +60 -0
- package/packages/server/src/__tests__/home-lock-release.test.ts +85 -0
- package/packages/server/src/__tests__/home-lock.test.ts +308 -0
- package/packages/server/src/__tests__/is-pi-process.test.ts +36 -0
- package/packages/server/src/__tests__/node-guard.test.ts +85 -0
- package/packages/server/src/__tests__/package-manager-wrapper-resolve.test.ts +5 -1
- package/packages/server/src/__tests__/package-manager-wrapper.test.ts +45 -10
- package/packages/server/src/__tests__/pi-version-skew.test.ts +165 -0
- package/packages/server/src/__tests__/preferences-store.test.ts +73 -4
- package/packages/server/src/__tests__/process-manager.test.ts +45 -18
- package/packages/server/src/__tests__/provider-probe.test.ts +287 -0
- package/packages/server/src/__tests__/provider-test-route.test.ts +149 -0
- package/packages/server/src/__tests__/restart-helper.test.ts +83 -0
- package/packages/server/src/__tests__/session-action-handler-headless-reload.test.ts +467 -0
- package/packages/server/src/__tests__/session-action-handler-reload-predicate.test.ts +73 -0
- package/packages/server/src/__tests__/session-action-handler-spawn-error.test.ts +74 -0
- package/packages/server/src/__tests__/terminal-manager.test.ts +41 -1
- package/packages/server/src/__tests__/tool-routes.test.ts +277 -0
- package/packages/server/src/__tests__/trusted-networks-config.test.ts +19 -0
- package/packages/server/src/__tests__/trusted-networks-no-oauth-roundtrip.test.ts +126 -0
- package/packages/server/src/__tests__/tunnel-cleanup.test.ts +90 -0
- package/packages/server/src/__tests__/tunnel.test.ts +13 -7
- package/packages/server/src/__tests__/wsl-tmux-probe-cache.test.ts +44 -0
- package/packages/server/src/bootstrap-queue.ts +130 -0
- package/packages/server/src/bootstrap-state.ts +131 -0
- package/packages/server/src/browse.ts +8 -3
- package/packages/server/src/browser-handlers/directory-handler.ts +23 -8
- package/packages/server/src/browser-handlers/session-action-handler.ts +213 -79
- package/packages/server/src/browser-handlers/session-action-helpers.ts +36 -0
- package/packages/server/src/cli.ts +256 -32
- package/packages/server/src/config-api.ts +16 -0
- package/packages/server/src/directory-service.ts +270 -39
- package/packages/server/src/editor-detection.ts +12 -9
- package/packages/server/src/editor-manager.ts +19 -4
- package/packages/server/src/editor-pid-registry.ts +9 -8
- package/packages/server/src/editor-registry.ts +22 -25
- package/packages/server/src/git-operations.ts +1 -1
- package/packages/server/src/headless-pid-registry.ts +7 -20
- package/packages/server/src/home-lock-release.ts +72 -0
- package/packages/server/src/home-lock.ts +389 -0
- package/packages/server/src/node-guard.ts +52 -0
- package/packages/server/src/package-manager-wrapper.ts +207 -47
- package/packages/server/src/pi-core-checker.ts +1 -1
- package/packages/server/src/pi-core-updater.ts +7 -1
- package/packages/server/src/pi-resource-scanner.ts +5 -8
- package/packages/server/src/pi-version-skew.ts +196 -0
- package/packages/server/src/preferences-store.ts +17 -3
- package/packages/server/src/process-manager.ts +403 -222
- package/packages/server/src/provider-probe.ts +234 -0
- package/packages/server/src/restart-helper.ts +130 -0
- package/packages/server/src/routes/bootstrap-routes.ts +88 -0
- package/packages/server/src/routes/openspec-routes.ts +25 -1
- package/packages/server/src/routes/pi-core-routes.ts +24 -1
- package/packages/server/src/routes/provider-auth-routes.ts +8 -8
- package/packages/server/src/routes/provider-routes.ts +43 -0
- package/packages/server/src/routes/recommended-routes.ts +10 -12
- package/packages/server/src/routes/system-routes.ts +20 -33
- package/packages/server/src/routes/tool-routes.ts +153 -0
- package/packages/server/src/server-pid.ts +5 -9
- package/packages/server/src/server.ts +211 -10
- package/packages/server/src/session-api.ts +77 -8
- package/packages/server/src/session-bootstrap.ts +17 -3
- package/packages/server/src/session-diff.ts +21 -21
- package/packages/server/src/terminal-manager.ts +61 -20
- package/packages/server/src/tunnel.ts +42 -28
- package/packages/shared/package.json +10 -3
- package/packages/shared/src/__tests__/{tool-resolver.test.ts → binary-lookup.test.ts} +32 -12
- package/packages/shared/src/__tests__/bootstrap/README.md +133 -0
- package/packages/shared/src/__tests__/bootstrap/__snapshots__/cube.test.ts.snap +370 -0
- package/packages/shared/src/__tests__/bootstrap/assertions.ts +136 -0
- package/packages/shared/src/__tests__/bootstrap/cube.test.ts +47 -0
- package/packages/shared/src/__tests__/bootstrap/cube.ts +66 -0
- package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/a-electron.test.ts.snap +83 -0
- package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/b-npm-global.test.ts.snap +89 -0
- package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/c-dev-monorepo.test.ts.snap +33 -0
- package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/d-overrides.test.ts.snap +20 -0
- package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/e-stale-partial.test.ts.snap +61 -0
- package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/f-cwd-variants.test.ts.snap +33 -0
- package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/g-windows-specifics.test.ts.snap +46 -0
- package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/j-path-gui-minimal.test.ts.snap +12 -0
- package/packages/shared/src/__tests__/bootstrap/families/a-electron.test.ts +156 -0
- package/packages/shared/src/__tests__/bootstrap/families/b-npm-global.test.ts +157 -0
- package/packages/shared/src/__tests__/bootstrap/families/c-dev-monorepo.test.ts +102 -0
- package/packages/shared/src/__tests__/bootstrap/families/d-overrides.test.ts +76 -0
- package/packages/shared/src/__tests__/bootstrap/families/e-stale-partial.test.ts +94 -0
- package/packages/shared/src/__tests__/bootstrap/families/f-cwd-variants.test.ts +87 -0
- package/packages/shared/src/__tests__/bootstrap/families/g-windows-specifics.test.ts +143 -0
- package/packages/shared/src/__tests__/bootstrap/families/h-home-drift.test.ts +64 -0
- package/packages/shared/src/__tests__/bootstrap/families/i-malformed-settings.test.ts +77 -0
- package/packages/shared/src/__tests__/bootstrap/families/index.ts +19 -0
- package/packages/shared/src/__tests__/bootstrap/families/j-path-gui-minimal.test.ts +61 -0
- package/packages/shared/src/__tests__/bootstrap/families/k-dashboard-absent.test.ts +50 -0
- package/packages/shared/src/__tests__/bootstrap/families/l-instance-coordination.test.ts +272 -0
- package/packages/shared/src/__tests__/bootstrap/fixtures/dev-monorepo.ts +58 -0
- package/packages/shared/src/__tests__/bootstrap/fixtures/electron-layout.ts +84 -0
- package/packages/shared/src/__tests__/bootstrap/fixtures/index.ts +9 -0
- package/packages/shared/src/__tests__/bootstrap/fixtures/managed-install.ts +85 -0
- package/packages/shared/src/__tests__/bootstrap/fixtures/npm-global-layout.ts +122 -0
- package/packages/shared/src/__tests__/bootstrap/fixtures/pi-versions.ts +36 -0
- package/packages/shared/src/__tests__/bootstrap/fixtures/settings-json.ts +39 -0
- package/packages/shared/src/__tests__/bootstrap/harness.smoke.test.ts +220 -0
- package/packages/shared/src/__tests__/bootstrap/harness.ts +413 -0
- package/packages/shared/src/__tests__/bootstrap/scenarios-skipped.ts +125 -0
- package/packages/shared/src/__tests__/bootstrap/scenarios.ts +132 -0
- package/packages/shared/src/__tests__/bridge-register.test.ts +29 -6
- package/packages/shared/src/__tests__/config-openspec.test.ts +106 -0
- package/packages/shared/src/__tests__/config.test.ts +56 -0
- package/packages/shared/src/__tests__/detached-spawn.test.ts +243 -0
- package/packages/shared/src/__tests__/managed-paths.test.ts +60 -0
- package/packages/shared/src/__tests__/no-direct-child-process.test.ts +112 -0
- package/packages/shared/src/__tests__/no-direct-platform-branch.test.ts +174 -0
- package/packages/shared/src/__tests__/no-direct-process-kill.test.ts +105 -0
- package/packages/shared/src/__tests__/platform-commands.test.ts +108 -0
- package/packages/shared/src/__tests__/platform-exec.test.ts +103 -0
- package/packages/shared/src/__tests__/platform-git.test.ts +194 -0
- package/packages/shared/src/__tests__/platform-npm.test.ts +137 -0
- package/packages/shared/src/__tests__/platform-openspec.test.ts +92 -0
- package/packages/shared/src/__tests__/platform-paths.test.ts +284 -0
- package/packages/shared/src/__tests__/platform-process-scan.test.ts +55 -0
- package/packages/shared/src/__tests__/platform-process.test.ts +160 -0
- package/packages/shared/src/__tests__/platform-runner.test.ts +173 -0
- package/packages/shared/src/__tests__/platform-shell.test.ts +74 -0
- package/packages/shared/src/__tests__/process-identify.test.ts +113 -0
- package/packages/shared/src/__tests__/recommended-extensions.test.ts +40 -7
- package/packages/shared/src/__tests__/resolve-jiti.test.ts +43 -7
- package/packages/shared/src/__tests__/semaphore.test.ts +119 -0
- package/packages/shared/src/__tests__/spawn-mechanism.test.ts +131 -0
- package/packages/shared/src/__tests__/tool-registry-definitions.test.ts +239 -0
- package/packages/shared/src/__tests__/tool-registry-overrides.test.ts +137 -0
- package/packages/shared/src/__tests__/tool-registry-registry.test.ts +343 -0
- package/packages/shared/src/bootstrap-install.ts +212 -0
- package/packages/shared/src/bridge-register.ts +87 -20
- package/packages/shared/src/browser-protocol.ts +71 -1
- package/packages/shared/src/config.ts +87 -15
- package/packages/shared/src/managed-paths.ts +31 -4
- package/packages/shared/src/openspec-poller.ts +63 -46
- package/packages/shared/src/{tool-resolver.ts → platform/binary-lookup.ts} +125 -25
- package/packages/shared/src/platform/commands.ts +100 -0
- package/packages/shared/src/platform/detached-spawn.ts +305 -0
- package/packages/shared/src/platform/exec.ts +220 -0
- package/packages/shared/src/platform/git.ts +155 -0
- package/packages/shared/src/platform/index.ts +15 -0
- package/packages/shared/src/platform/npm.ts +162 -0
- package/packages/shared/src/platform/openspec.ts +91 -0
- package/packages/shared/src/platform/paths.ts +276 -0
- package/packages/shared/src/platform/process-identify.ts +126 -0
- package/packages/shared/src/platform/process-scan.ts +94 -0
- package/packages/shared/src/platform/process.ts +168 -0
- package/packages/shared/src/platform/runner.ts +369 -0
- package/packages/shared/src/platform/shell.ts +44 -0
- package/packages/shared/src/platform/spawn-mechanism.ts +124 -0
- package/packages/shared/src/platform/subprocess-adapter.ts +124 -0
- package/packages/shared/src/recommended-extensions.ts +18 -2
- package/packages/shared/src/resolve-jiti.ts +62 -3
- package/packages/shared/src/rest-api.ts +26 -0
- package/packages/shared/src/semaphore.ts +83 -0
- package/packages/shared/src/tool-registry/definitions.ts +342 -0
- package/packages/shared/src/tool-registry/index.ts +56 -0
- package/packages/shared/src/tool-registry/overrides.ts +118 -0
- package/packages/shared/src/tool-registry/registry.ts +262 -0
- package/packages/shared/src/tool-registry/strategies.ts +198 -0
- package/packages/shared/src/tool-registry/types.ts +180 -0
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Family B — npm-global install scenarios.
|
|
3
|
+
*
|
|
4
|
+
* B1: npm-g dash only (⚠ Windows bug) — pi UNRESOLVED.
|
|
5
|
+
* B2: npm-g full — pi + openspec via global npm.
|
|
6
|
+
* B3: npm-g pi-installed-first — pi present, settings lacks bridge;
|
|
7
|
+
* dashboard registers on boot (delta assertion).
|
|
8
|
+
*
|
|
9
|
+
* B1 is the key scenario: current behavior when a Windows user runs
|
|
10
|
+
* `npm i -g pi-dashboard` without first installing pi. This snapshot
|
|
11
|
+
* captures the broken state; `unified-bootstrap-install` will flip it.
|
|
12
|
+
*/
|
|
13
|
+
import { describe, expect, it } from "vitest";
|
|
14
|
+
import { withFakeEnv, layer } from "../harness.js";
|
|
15
|
+
import { registerDefaultTools } from "../../../tool-registry/definitions.js";
|
|
16
|
+
import * as fixtures from "../fixtures/index.js";
|
|
17
|
+
import { snapshotTrail, snapshotSettingsDelta } from "../assertions.js";
|
|
18
|
+
import { register, SKIPPED_SCENARIOS, cellKey } from "../scenarios.js";
|
|
19
|
+
|
|
20
|
+
const B1 = [
|
|
21
|
+
{ platform: "linux", dash: "npm-g", pi: "absent", settings: "missing", env: "normal" },
|
|
22
|
+
{ platform: "darwin", dash: "npm-g", pi: "absent", settings: "missing", env: "normal" },
|
|
23
|
+
{ platform: "win32", dash: "npm-g", pi: "absent", settings: "missing", env: "normal" },
|
|
24
|
+
] as const;
|
|
25
|
+
const B2 = [
|
|
26
|
+
{ platform: "linux", dash: "npm-g", pi: "present-valid", settings: "valid", env: "normal" },
|
|
27
|
+
{ platform: "darwin", dash: "npm-g", pi: "present-valid", settings: "valid", env: "normal" },
|
|
28
|
+
{ platform: "win32", dash: "npm-g", pi: "present-valid", settings: "valid", env: "normal" },
|
|
29
|
+
] as const;
|
|
30
|
+
const B3 = [
|
|
31
|
+
{ platform: "linux", dash: "npm-g", pi: "present-no-ext", settings: "empty", env: "normal" },
|
|
32
|
+
] as const;
|
|
33
|
+
for (const cell of [...B1, ...B2, ...B3]) {
|
|
34
|
+
register(cell, "families/b-npm-global.test.ts");
|
|
35
|
+
SKIPPED_SCENARIOS.delete(cellKey(cell));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
describe("Family B — npm-global", () => {
|
|
39
|
+
describe("B1 — npm-g dash-only (⚠ captures current Windows bug)", () => {
|
|
40
|
+
it.each(["linux", "darwin", "win32"] as const)(
|
|
41
|
+
"pi unresolved on %s",
|
|
42
|
+
async (platform) => {
|
|
43
|
+
const homedir = platform === "win32" ? "C:\\Users\\R" : "/home/r";
|
|
44
|
+
await withFakeEnv(
|
|
45
|
+
{
|
|
46
|
+
platform,
|
|
47
|
+
homedir,
|
|
48
|
+
env: platform === "win32"
|
|
49
|
+
? { PATH: "C:\\Users\\R\\AppData\\Roaming\\npm" }
|
|
50
|
+
: { PATH: "/usr/local/bin" },
|
|
51
|
+
fs: platform === "win32"
|
|
52
|
+
// On Windows we ship dash only — no pi, no openspec.
|
|
53
|
+
? fixtures.npmGlobalWindowsAppData(homedir, {
|
|
54
|
+
dashboard: true,
|
|
55
|
+
pi: false,
|
|
56
|
+
openspec: false,
|
|
57
|
+
})
|
|
58
|
+
: fixtures.npmGlobalUnix({ pi: false, openspec: false }),
|
|
59
|
+
},
|
|
60
|
+
(ctx) => {
|
|
61
|
+
const registry = ctx.createRegistry();
|
|
62
|
+
registerDefaultTools(registry, ctx.createStrategyDeps());
|
|
63
|
+
const res = registry.resolve("pi");
|
|
64
|
+
expect(res.ok).toBe(false);
|
|
65
|
+
// FIXED-BY: unified-bootstrap-install.
|
|
66
|
+
// When that proposal lands, update the snapshot: pi should
|
|
67
|
+
// resolve via managed after first-run bootstrap.
|
|
68
|
+
expect(snapshotTrail(res, ctx)).toMatchSnapshot();
|
|
69
|
+
},
|
|
70
|
+
);
|
|
71
|
+
},
|
|
72
|
+
);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe("B2 — npm-g full (pi + openspec via global npm)", () => {
|
|
76
|
+
it.each(["linux", "darwin"] as const)(
|
|
77
|
+
"pi resolves via npm-global on %s",
|
|
78
|
+
async (platform) => {
|
|
79
|
+
const homedir = "/home/r";
|
|
80
|
+
await withFakeEnv(
|
|
81
|
+
{
|
|
82
|
+
platform,
|
|
83
|
+
homedir,
|
|
84
|
+
env: { PATH: "/usr/local/bin" },
|
|
85
|
+
fs: fixtures.npmGlobalUnix({}),
|
|
86
|
+
},
|
|
87
|
+
(ctx) => {
|
|
88
|
+
// On Unix, the pi strategy chain only has override → managed-bin
|
|
89
|
+
// → where. `where` finds /usr/local/bin/pi. So source === "system"
|
|
90
|
+
// not "npm-global" — the current strategy chain doesn't know
|
|
91
|
+
// about npm-g on Unix at the executor level.
|
|
92
|
+
const registry = ctx.createRegistry();
|
|
93
|
+
registerDefaultTools(registry, ctx.createStrategyDeps());
|
|
94
|
+
const res = registry.resolve("pi");
|
|
95
|
+
expect(res.ok).toBe(true);
|
|
96
|
+
expect(snapshotTrail(res, ctx)).toMatchSnapshot();
|
|
97
|
+
},
|
|
98
|
+
);
|
|
99
|
+
},
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
it("pi resolves via npm-global on win32 (with node.exe toArgv)", async () => {
|
|
103
|
+
const homedir = "C:\\Users\\R";
|
|
104
|
+
await withFakeEnv(
|
|
105
|
+
{
|
|
106
|
+
platform: "win32",
|
|
107
|
+
homedir,
|
|
108
|
+
env: {
|
|
109
|
+
PATH: "C:\\Users\\R\\AppData\\Roaming\\npm;C:\\Program Files\\nodejs",
|
|
110
|
+
},
|
|
111
|
+
fs: layer(
|
|
112
|
+
fixtures.npmGlobalWindowsAppData(homedir, { dashboard: true }),
|
|
113
|
+
{ "C:\\Program Files\\nodejs\\node.exe": "\x7fELF" },
|
|
114
|
+
),
|
|
115
|
+
},
|
|
116
|
+
(ctx) => {
|
|
117
|
+
const registry = ctx.createRegistry();
|
|
118
|
+
registerDefaultTools(registry, ctx.createStrategyDeps());
|
|
119
|
+
const executor = registry.resolveExecutor("pi");
|
|
120
|
+
expect(executor.ok).toBe(true);
|
|
121
|
+
expect(executor.source).toBe("npm-global");
|
|
122
|
+
// No-cmd-flash invariant for npm-g on Windows: argv[0] is
|
|
123
|
+
// node.exe, argv[1] is cli.js. The snapshot locks this in.
|
|
124
|
+
expect(executor.argv[0]).toBe("C:\\Program Files\\nodejs\\node.exe");
|
|
125
|
+
expect(snapshotTrail(executor, ctx)).toMatchSnapshot();
|
|
126
|
+
},
|
|
127
|
+
);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
describe("B3 — npm-g pi-installed-first (bridge needs registration)", () => {
|
|
132
|
+
it("settings.json present but lacks bridge entry (linux)", async () => {
|
|
133
|
+
const homedir = "/home/r";
|
|
134
|
+
await withFakeEnv(
|
|
135
|
+
{
|
|
136
|
+
platform: "linux",
|
|
137
|
+
homedir,
|
|
138
|
+
fs: layer(
|
|
139
|
+
fixtures.npmGlobalUnix({}),
|
|
140
|
+
fixtures.settingsJson({ homedir, platform: "linux", packages: [] }),
|
|
141
|
+
),
|
|
142
|
+
},
|
|
143
|
+
(ctx) => {
|
|
144
|
+
// Input-side assertion: the fixture correctly produces a
|
|
145
|
+
// settings.json without the bridge entry. Full round-trip
|
|
146
|
+
// (calling `registerBridgeExtension` and observing the
|
|
147
|
+
// mutation) requires `bridge-register` to accept an
|
|
148
|
+
// injectable fs — future task, cross-proposal.
|
|
149
|
+
const before = ctx.readSettings() as { packages?: string[] } | null;
|
|
150
|
+
expect(before).toEqual({ packages: [] });
|
|
151
|
+
const after = before; // no mutation via harness
|
|
152
|
+
expect(snapshotSettingsDelta(before, after, ctx)).toMatchSnapshot();
|
|
153
|
+
},
|
|
154
|
+
);
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
});
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Family C — dev monorepo scenarios.
|
|
3
|
+
*
|
|
4
|
+
* C1: mac/linux — bare-import resolves pi via workspace node_modules.
|
|
5
|
+
* C2: win32 — same, plus node.exe toArgv invariant.
|
|
6
|
+
*
|
|
7
|
+
* The bare-import strategy only runs for pi on win32 (see pi's
|
|
8
|
+
* winStrategies in definitions.ts). On Unix, pi on Unix still resolves
|
|
9
|
+
* via managed-bin → where. So the interesting "dev monorepo resolves
|
|
10
|
+
* via bare-import" path is Windows-only today. C1 asserts what Unix
|
|
11
|
+
* actually does (no bare-import in its chain).
|
|
12
|
+
*/
|
|
13
|
+
import { describe, expect, it } from "vitest";
|
|
14
|
+
import { withFakeEnv, layer } from "../harness.js";
|
|
15
|
+
import { registerDefaultTools } from "../../../tool-registry/definitions.js";
|
|
16
|
+
import * as fixtures from "../fixtures/index.js";
|
|
17
|
+
import { snapshotTrail } from "../assertions.js";
|
|
18
|
+
import { register, SKIPPED_SCENARIOS, cellKey } from "../scenarios.js";
|
|
19
|
+
|
|
20
|
+
// C1 posix — dev dash, pi present via workspace; settings typically empty
|
|
21
|
+
// at dev time.
|
|
22
|
+
const C1 = [
|
|
23
|
+
{ platform: "linux", dash: "dev", pi: "present-valid", settings: "empty", env: "normal" },
|
|
24
|
+
{ platform: "darwin", dash: "dev", pi: "present-valid", settings: "empty", env: "normal" },
|
|
25
|
+
] as const;
|
|
26
|
+
|
|
27
|
+
// C2 — Windows dev layout is already skipped in scenarios-skipped.ts
|
|
28
|
+
// with reason "rare; capture if reported". We still add a test case
|
|
29
|
+
// that registers the cell so the bare-import + toArgv invariant is
|
|
30
|
+
// locked in — developers on Windows do exist.
|
|
31
|
+
const C2 = [
|
|
32
|
+
{ platform: "win32", dash: "dev", pi: "present-valid", settings: "empty", env: "normal" },
|
|
33
|
+
] as const;
|
|
34
|
+
|
|
35
|
+
for (const cell of [...C1, ...C2]) {
|
|
36
|
+
register(cell, "families/c-dev-monorepo.test.ts");
|
|
37
|
+
SKIPPED_SCENARIOS.delete(cellKey(cell));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
describe("Family C — dev monorepo", () => {
|
|
41
|
+
describe("C1 — posix (managed/where chain, no bare-import for pi)", () => {
|
|
42
|
+
it.each(["linux", "darwin"] as const)(
|
|
43
|
+
"pi chain runs on %s",
|
|
44
|
+
async (platform) => {
|
|
45
|
+
const root = "/home/dev/pi-agent-dashboard";
|
|
46
|
+
const homedir = "/home/dev";
|
|
47
|
+
await withFakeEnv(
|
|
48
|
+
{
|
|
49
|
+
platform,
|
|
50
|
+
homedir,
|
|
51
|
+
cwd: root,
|
|
52
|
+
env: { PATH: "/usr/bin" },
|
|
53
|
+
fs: fixtures.devMonorepo({ root, platform }),
|
|
54
|
+
},
|
|
55
|
+
(ctx) => {
|
|
56
|
+
const registry = ctx.createRegistry();
|
|
57
|
+
registerDefaultTools(registry, ctx.createStrategyDeps());
|
|
58
|
+
const res = registry.resolve("pi");
|
|
59
|
+
// Nothing on PATH, nothing in ~/.pi-dashboard — unresolved.
|
|
60
|
+
// The workspace node_modules is only reachable via
|
|
61
|
+
// bare-import, which isn't in pi's Unix chain.
|
|
62
|
+
expect(res.ok).toBe(false);
|
|
63
|
+
expect(snapshotTrail(res, ctx)).toMatchSnapshot();
|
|
64
|
+
},
|
|
65
|
+
);
|
|
66
|
+
},
|
|
67
|
+
);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe("C2 — win32 (bare-import from workspace)", () => {
|
|
71
|
+
it("resolves pi via workspace bare-import", async () => {
|
|
72
|
+
const root = "C:\\dev\\pi-agent-dashboard";
|
|
73
|
+
const homedir = "C:\\Users\\Dev";
|
|
74
|
+
await withFakeEnv(
|
|
75
|
+
{
|
|
76
|
+
platform: "win32",
|
|
77
|
+
homedir,
|
|
78
|
+
cwd: root,
|
|
79
|
+
env: { PATH: "C:\\Windows\\System32" },
|
|
80
|
+
fs: layer(fixtures.devMonorepo({ root, platform: "win32" })),
|
|
81
|
+
},
|
|
82
|
+
(ctx) => {
|
|
83
|
+
// Build deps with a resolveModule anchor at the workspace
|
|
84
|
+
// root so bareImportCliStrategy finds pi-coding-agent.
|
|
85
|
+
const baseDeps = ctx.createStrategyDeps();
|
|
86
|
+
const rootAnchor = `${root}\\packages\\shared\\src\\index.ts`;
|
|
87
|
+
const deps = {
|
|
88
|
+
...baseDeps,
|
|
89
|
+
resolveModule: (id: string, _from: string) =>
|
|
90
|
+
baseDeps.resolveModule(id, rootAnchor),
|
|
91
|
+
};
|
|
92
|
+
const registry = ctx.createRegistry();
|
|
93
|
+
registerDefaultTools(registry, deps);
|
|
94
|
+
const res = registry.resolve("pi");
|
|
95
|
+
expect(res.ok).toBe(true);
|
|
96
|
+
expect(res.source).toBe("bare-import");
|
|
97
|
+
expect(snapshotTrail(res, ctx)).toMatchSnapshot();
|
|
98
|
+
},
|
|
99
|
+
);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
});
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Family D — overrides.
|
|
3
|
+
*
|
|
4
|
+
* D1: override-valid — pi resolves via override, source = "override".
|
|
5
|
+
* D2: override-invalid — broken path; falls through to next strategy.
|
|
6
|
+
*
|
|
7
|
+
* Overrides override platform. We test on linux; Windows is covered
|
|
8
|
+
* structurally because overrideStrategy is first in every chain.
|
|
9
|
+
*/
|
|
10
|
+
import { describe, expect, it } from "vitest";
|
|
11
|
+
import { withFakeEnv } from "../harness.js";
|
|
12
|
+
import { registerDefaultTools } from "../../../tool-registry/definitions.js";
|
|
13
|
+
import * as fixtures from "../fixtures/index.js";
|
|
14
|
+
import { snapshotTrail } from "../assertions.js";
|
|
15
|
+
import { register, SKIPPED_SCENARIOS, cellKey } from "../scenarios.js";
|
|
16
|
+
|
|
17
|
+
// Overrides are orthogonal to the cube's pi-state axis — treat as
|
|
18
|
+
// "present-valid" scenarios for taxonomy purposes.
|
|
19
|
+
const D = [
|
|
20
|
+
{ platform: "linux", dash: "managed", pi: "present-valid", settings: "empty", env: "normal" },
|
|
21
|
+
{ platform: "darwin", dash: "managed", pi: "present-valid", settings: "empty", env: "normal" },
|
|
22
|
+
] as const;
|
|
23
|
+
for (const cell of D) {
|
|
24
|
+
register(cell, "families/d-overrides.test.ts");
|
|
25
|
+
SKIPPED_SCENARIOS.delete(cellKey(cell));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
describe("Family D — overrides", () => {
|
|
29
|
+
it("D1 — override-valid: pi resolves via override", async () => {
|
|
30
|
+
const homedir = "/home/r";
|
|
31
|
+
const overridePath = "/opt/custom/bin/pi";
|
|
32
|
+
await withFakeEnv(
|
|
33
|
+
{
|
|
34
|
+
platform: "linux",
|
|
35
|
+
homedir,
|
|
36
|
+
fs: {
|
|
37
|
+
[overridePath]: "#!/bin/sh\nexec custom-pi",
|
|
38
|
+
// Also managed, to prove override wins.
|
|
39
|
+
...fixtures.managedInstall({ homedir, platform: "linux" }),
|
|
40
|
+
},
|
|
41
|
+
overrides: { pi: overridePath },
|
|
42
|
+
},
|
|
43
|
+
(ctx) => {
|
|
44
|
+
const registry = ctx.createRegistry();
|
|
45
|
+
registerDefaultTools(registry, ctx.createStrategyDeps());
|
|
46
|
+
const res = registry.resolve("pi");
|
|
47
|
+
expect(res.ok).toBe(true);
|
|
48
|
+
expect(res.source).toBe("override");
|
|
49
|
+
expect(res.path).toBe(overridePath);
|
|
50
|
+
expect(snapshotTrail(res, ctx)).toMatchSnapshot();
|
|
51
|
+
},
|
|
52
|
+
);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("D2 — override-invalid: path doesn't exist, chain falls through", async () => {
|
|
56
|
+
const homedir = "/home/r";
|
|
57
|
+
await withFakeEnv(
|
|
58
|
+
{
|
|
59
|
+
platform: "linux",
|
|
60
|
+
homedir,
|
|
61
|
+
fs: fixtures.managedInstall({ homedir, platform: "linux" }),
|
|
62
|
+
overrides: { pi: "/nonexistent/broken/pi" },
|
|
63
|
+
},
|
|
64
|
+
(ctx) => {
|
|
65
|
+
const registry = ctx.createRegistry();
|
|
66
|
+
registerDefaultTools(registry, ctx.createStrategyDeps());
|
|
67
|
+
const res = registry.resolve("pi");
|
|
68
|
+
// Override strategy returns `invalid: ...` as reason; next
|
|
69
|
+
// strategy (managed-bin) succeeds.
|
|
70
|
+
expect(res.ok).toBe(true);
|
|
71
|
+
expect(res.source).toBe("managed");
|
|
72
|
+
expect(snapshotTrail(res, ctx)).toMatchSnapshot();
|
|
73
|
+
},
|
|
74
|
+
);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Family E — stale / partial managed installs.
|
|
3
|
+
*
|
|
4
|
+
* E1: stale-managed — managed pi version 0.0.1. TODAY the strategies
|
|
5
|
+
* don't do version gating; resolution succeeds. Snapshot locks in
|
|
6
|
+
* current behavior so version-skew detection (from proposal 2)
|
|
7
|
+
* will force a snapshot update.
|
|
8
|
+
* E2: managed-partial — only package.json, no dist/cli.js. Strategy
|
|
9
|
+
* returns not-found; next strategy runs.
|
|
10
|
+
*/
|
|
11
|
+
import { describe, expect, it } from "vitest";
|
|
12
|
+
import { withFakeEnv } from "../harness.js";
|
|
13
|
+
import { registerDefaultTools } from "../../../tool-registry/definitions.js";
|
|
14
|
+
import * as fixtures from "../fixtures/index.js";
|
|
15
|
+
import { snapshotTrail } from "../assertions.js";
|
|
16
|
+
import { register, SKIPPED_SCENARIOS, cellKey } from "../scenarios.js";
|
|
17
|
+
|
|
18
|
+
const E1 = [
|
|
19
|
+
// present-stale-ext as cube axis for "installed but bridge registration is stale"
|
|
20
|
+
{ platform: "linux", dash: "electron", pi: "present-stale-ext", settings: "valid", env: "normal" },
|
|
21
|
+
{ platform: "darwin", dash: "electron", pi: "present-stale-ext", settings: "valid", env: "normal" },
|
|
22
|
+
{ platform: "win32", dash: "electron", pi: "present-stale-ext", settings: "valid", env: "normal" },
|
|
23
|
+
] as const;
|
|
24
|
+
// E2 maps to "malformed" pi-state — the managed install is present but
|
|
25
|
+
// broken (incomplete).
|
|
26
|
+
const E2 = [
|
|
27
|
+
{ platform: "linux", dash: "electron", pi: "malformed", settings: "empty", env: "normal" },
|
|
28
|
+
{ platform: "win32", dash: "electron", pi: "malformed", settings: "empty", env: "normal" },
|
|
29
|
+
] as const;
|
|
30
|
+
for (const cell of [...E1, ...E2]) {
|
|
31
|
+
register(cell, "families/e-stale-partial.test.ts");
|
|
32
|
+
SKIPPED_SCENARIOS.delete(cellKey(cell));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
describe("Family E — stale / partial", () => {
|
|
36
|
+
describe("E1 — stale managed pi (old version)", () => {
|
|
37
|
+
it.each(["linux", "darwin", "win32"] as const)(
|
|
38
|
+
"current strategies resolve without version gating (%s)",
|
|
39
|
+
async (platform) => {
|
|
40
|
+
const homedir = platform === "win32" ? "C:\\Users\\R" : "/home/r";
|
|
41
|
+
await withFakeEnv(
|
|
42
|
+
{
|
|
43
|
+
platform,
|
|
44
|
+
homedir,
|
|
45
|
+
fs: fixtures.managedInstall({
|
|
46
|
+
homedir,
|
|
47
|
+
platform,
|
|
48
|
+
pi: { version: "0.0.1" }, // very old
|
|
49
|
+
}),
|
|
50
|
+
},
|
|
51
|
+
(ctx) => {
|
|
52
|
+
const registry = ctx.createRegistry();
|
|
53
|
+
registerDefaultTools(registry, ctx.createStrategyDeps());
|
|
54
|
+
const res = registry.resolve("pi");
|
|
55
|
+
expect(res.ok).toBe(true);
|
|
56
|
+
// NOTE: version-skew detection lands in
|
|
57
|
+
// `unified-bootstrap-install` — resolution still
|
|
58
|
+
// succeeds; the detection happens downstream in
|
|
59
|
+
// dependency-detector. Snapshot will shift when that
|
|
60
|
+
// detection is added to trail.
|
|
61
|
+
expect(snapshotTrail(res, ctx)).toMatchSnapshot();
|
|
62
|
+
},
|
|
63
|
+
);
|
|
64
|
+
},
|
|
65
|
+
);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
describe("E2 — partial managed install (package.json, no dist)", () => {
|
|
69
|
+
it.each(["linux", "win32"] as const)(
|
|
70
|
+
"strategy skips when entry file absent (%s)",
|
|
71
|
+
async (platform) => {
|
|
72
|
+
const homedir = platform === "win32" ? "C:\\Users\\R" : "/home/r";
|
|
73
|
+
await withFakeEnv(
|
|
74
|
+
{
|
|
75
|
+
platform,
|
|
76
|
+
homedir,
|
|
77
|
+
fs: fixtures.managedInstall({
|
|
78
|
+
homedir,
|
|
79
|
+
platform,
|
|
80
|
+
piPartial: true, // package.json only, no dist/cli.js, no .bin shim
|
|
81
|
+
}),
|
|
82
|
+
},
|
|
83
|
+
(ctx) => {
|
|
84
|
+
const registry = ctx.createRegistry();
|
|
85
|
+
registerDefaultTools(registry, ctx.createStrategyDeps());
|
|
86
|
+
const res = registry.resolve("pi");
|
|
87
|
+
expect(res.ok).toBe(false);
|
|
88
|
+
expect(snapshotTrail(res, ctx)).toMatchSnapshot();
|
|
89
|
+
},
|
|
90
|
+
);
|
|
91
|
+
},
|
|
92
|
+
);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Family F — cwd variants.
|
|
3
|
+
*
|
|
4
|
+
* F1: cwd-with-spaces — cwd contains spaces. Resolution unaffected.
|
|
5
|
+
* F2: cwd-unicode — cwd contains non-ASCII. Resolution unaffected.
|
|
6
|
+
*
|
|
7
|
+
* Today, cwd is NOT used by any strategy in the chain. Asserting that
|
|
8
|
+
* invariant via snapshots means if someone later adds cwd-sensitive
|
|
9
|
+
* resolution (e.g. workspace-local binaries), the change surfaces
|
|
10
|
+
* loudly in these snapshots.
|
|
11
|
+
*/
|
|
12
|
+
import { describe, expect, it } from "vitest";
|
|
13
|
+
import { withFakeEnv } from "../harness.js";
|
|
14
|
+
import { registerDefaultTools } from "../../../tool-registry/definitions.js";
|
|
15
|
+
import * as fixtures from "../fixtures/index.js";
|
|
16
|
+
import { snapshotTrail } from "../assertions.js";
|
|
17
|
+
import { register, SKIPPED_SCENARIOS, cellKey } from "../scenarios.js";
|
|
18
|
+
|
|
19
|
+
const F = [
|
|
20
|
+
{ platform: "linux", dash: "managed", pi: "present-valid", settings: "empty", env: "spaces-unicode" },
|
|
21
|
+
{ platform: "win32", dash: "managed", pi: "present-valid", settings: "empty", env: "spaces-unicode" },
|
|
22
|
+
] as const;
|
|
23
|
+
for (const cell of F) {
|
|
24
|
+
register(cell, "families/f-cwd-variants.test.ts");
|
|
25
|
+
SKIPPED_SCENARIOS.delete(cellKey(cell));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
describe("Family F — cwd variants", () => {
|
|
29
|
+
it("F1 — resolves normally with spaces in cwd (linux)", async () => {
|
|
30
|
+
const homedir = "/home/r";
|
|
31
|
+
await withFakeEnv(
|
|
32
|
+
{
|
|
33
|
+
platform: "linux",
|
|
34
|
+
homedir,
|
|
35
|
+
cwd: "/home/r/My Project With Spaces",
|
|
36
|
+
fs: fixtures.managedInstall({ homedir, platform: "linux" }),
|
|
37
|
+
},
|
|
38
|
+
(ctx) => {
|
|
39
|
+
const registry = ctx.createRegistry();
|
|
40
|
+
registerDefaultTools(registry, ctx.createStrategyDeps());
|
|
41
|
+
const res = registry.resolve("pi");
|
|
42
|
+
expect(res.ok).toBe(true);
|
|
43
|
+
expect(res.source).toBe("managed");
|
|
44
|
+
expect(snapshotTrail(res, ctx)).toMatchSnapshot();
|
|
45
|
+
},
|
|
46
|
+
);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("F1 — resolves normally with Program Files (x86) cwd (win32)", async () => {
|
|
50
|
+
const homedir = "C:\\Users\\R";
|
|
51
|
+
await withFakeEnv(
|
|
52
|
+
{
|
|
53
|
+
platform: "win32",
|
|
54
|
+
homedir,
|
|
55
|
+
cwd: "C:\\Program Files (x86)\\Pi Dashboard",
|
|
56
|
+
fs: fixtures.managedInstall({ homedir, platform: "win32" }),
|
|
57
|
+
},
|
|
58
|
+
(ctx) => {
|
|
59
|
+
const registry = ctx.createRegistry();
|
|
60
|
+
registerDefaultTools(registry, ctx.createStrategyDeps());
|
|
61
|
+
const res = registry.resolve("pi");
|
|
62
|
+
expect(res.ok).toBe(true);
|
|
63
|
+
expect(res.source).toBe("managed");
|
|
64
|
+
expect(snapshotTrail(res, ctx)).toMatchSnapshot();
|
|
65
|
+
},
|
|
66
|
+
);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("F2 — resolves with Greek/Cyrillic/emoji in cwd", async () => {
|
|
70
|
+
const homedir = "/home/r";
|
|
71
|
+
await withFakeEnv(
|
|
72
|
+
{
|
|
73
|
+
platform: "linux",
|
|
74
|
+
homedir,
|
|
75
|
+
cwd: "/home/r/πρότζεκτ_тест_🚀",
|
|
76
|
+
fs: fixtures.managedInstall({ homedir, platform: "linux" }),
|
|
77
|
+
},
|
|
78
|
+
(ctx) => {
|
|
79
|
+
const registry = ctx.createRegistry();
|
|
80
|
+
registerDefaultTools(registry, ctx.createStrategyDeps());
|
|
81
|
+
const res = registry.resolve("pi");
|
|
82
|
+
expect(res.ok).toBe(true);
|
|
83
|
+
expect(snapshotTrail(res, ctx)).toMatchSnapshot();
|
|
84
|
+
},
|
|
85
|
+
);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Family G — Windows specifics.
|
|
3
|
+
*
|
|
4
|
+
* G1: win-cmd-shim — pi.cmd found; `toArgv` MUST prepend node.exe.
|
|
5
|
+
* G2: win-appdata-roaming — npm-g installed at %APPDATA%\Roaming\npm.
|
|
6
|
+
* G3: win-programfiles-cwd — cwd under "C:\Program Files (x86)\..."
|
|
7
|
+
* (covered in F1-win; add a G-variant with
|
|
8
|
+
* pi resolution via npm-g).
|
|
9
|
+
* G4: win-programfiles-node — node.exe at "C:\Program Files\nodejs".
|
|
10
|
+
*/
|
|
11
|
+
import { describe, expect, it } from "vitest";
|
|
12
|
+
import { withFakeEnv, layer } from "../harness.js";
|
|
13
|
+
import { registerDefaultTools } from "../../../tool-registry/definitions.js";
|
|
14
|
+
import * as fixtures from "../fixtures/index.js";
|
|
15
|
+
import { snapshotTrail } from "../assertions.js";
|
|
16
|
+
import { register, SKIPPED_SCENARIOS, cellKey } from "../scenarios.js";
|
|
17
|
+
|
|
18
|
+
// All Family G cells are win32-only.
|
|
19
|
+
const G = [
|
|
20
|
+
// G1 is already covered by B2 (npm-g on win32); this family focuses
|
|
21
|
+
// on specific layout variants.
|
|
22
|
+
{ platform: "win32", dash: "managed", pi: "present-valid", settings: "valid", env: "normal" },
|
|
23
|
+
{ platform: "win32", dash: "npm-g", pi: "present-valid", settings: "valid", env: "normal" },
|
|
24
|
+
] as const;
|
|
25
|
+
for (const cell of G) {
|
|
26
|
+
register(cell, "families/g-windows-specifics.test.ts");
|
|
27
|
+
SKIPPED_SCENARIOS.delete(cellKey(cell));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
describe("Family G — Windows specifics", () => {
|
|
31
|
+
it("G1 — pi.cmd resolved + toArgv prepends node.exe (no-cmd-flash)", async () => {
|
|
32
|
+
// Managed install with pi-coding-agent at a real module path, NOT
|
|
33
|
+
// the .bin/pi.cmd shim. This forces resolution through
|
|
34
|
+
// managedModuleStrategy (matches `@mariozechner/pi-coding-agent/
|
|
35
|
+
// dist/cli.js`), which is a Node script — `toArgv` prepends
|
|
36
|
+
// node.exe. That's the no-cmd-flash invariant.
|
|
37
|
+
const homedir = "C:\\Users\\R";
|
|
38
|
+
await withFakeEnv(
|
|
39
|
+
{
|
|
40
|
+
platform: "win32",
|
|
41
|
+
homedir,
|
|
42
|
+
env: { PATH: "C:\\Program Files\\nodejs" },
|
|
43
|
+
fs: layer(
|
|
44
|
+
fixtures.managedInstall({ homedir, platform: "win32" }),
|
|
45
|
+
{
|
|
46
|
+
// node.exe must be resolvable for toArgv to prepend it.
|
|
47
|
+
"C:\\Program Files\\nodejs\\node.exe": "\x7fELF",
|
|
48
|
+
},
|
|
49
|
+
),
|
|
50
|
+
},
|
|
51
|
+
(ctx) => {
|
|
52
|
+
const registry = ctx.createRegistry();
|
|
53
|
+
registerDefaultTools(registry, ctx.createStrategyDeps());
|
|
54
|
+
const executor = registry.resolveExecutor("pi");
|
|
55
|
+
expect(executor.ok).toBe(true);
|
|
56
|
+
expect(executor.path?.endsWith("cli.js")).toBe(true);
|
|
57
|
+
// No-cmd-flash invariant: argv[0] MUST be node.exe, NOT the
|
|
58
|
+
// .cmd shim or cmd.exe. The snapshot locks this in.
|
|
59
|
+
expect(executor.argv[0]).toBe("C:\\Program Files\\nodejs\\node.exe");
|
|
60
|
+
expect(executor.argv).toHaveLength(2);
|
|
61
|
+
expect(snapshotTrail(executor, ctx)).toMatchSnapshot();
|
|
62
|
+
},
|
|
63
|
+
);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("G2 — npm-g at %APPDATA%\\Roaming\\npm (argv prepends node.exe)", async () => {
|
|
67
|
+
const homedir = "C:\\Users\\R";
|
|
68
|
+
await withFakeEnv(
|
|
69
|
+
{
|
|
70
|
+
platform: "win32",
|
|
71
|
+
homedir,
|
|
72
|
+
env: {
|
|
73
|
+
PATH: "C:\\Users\\R\\AppData\\Roaming\\npm;C:\\Program Files\\nodejs",
|
|
74
|
+
APPDATA: "C:\\Users\\R\\AppData\\Roaming",
|
|
75
|
+
},
|
|
76
|
+
fs: layer(
|
|
77
|
+
fixtures.npmGlobalWindowsAppData(homedir, { dashboard: false }),
|
|
78
|
+
{ "C:\\Program Files\\nodejs\\node.exe": "\x7fELF" },
|
|
79
|
+
),
|
|
80
|
+
},
|
|
81
|
+
(ctx) => {
|
|
82
|
+
const registry = ctx.createRegistry();
|
|
83
|
+
registerDefaultTools(registry, ctx.createStrategyDeps());
|
|
84
|
+
const executor = registry.resolveExecutor("pi");
|
|
85
|
+
expect(executor.ok).toBe(true);
|
|
86
|
+
expect(executor.source).toBe("npm-global");
|
|
87
|
+
// Same no-cmd-flash invariant: even for npm-g, argv routes
|
|
88
|
+
// through node.exe.
|
|
89
|
+
expect(executor.argv[0]).toBe("C:\\Program Files\\nodejs\\node.exe");
|
|
90
|
+
expect(snapshotTrail(executor, ctx)).toMatchSnapshot();
|
|
91
|
+
},
|
|
92
|
+
);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("G3 — cwd under Program Files (x86) does not affect resolution", async () => {
|
|
96
|
+
// Spec requires this cell tested; covered structurally by F1-win,
|
|
97
|
+
// but a dedicated block documents the invariant alongside the
|
|
98
|
+
// other G cells.
|
|
99
|
+
const homedir = "C:\\Users\\R";
|
|
100
|
+
await withFakeEnv(
|
|
101
|
+
{
|
|
102
|
+
platform: "win32",
|
|
103
|
+
homedir,
|
|
104
|
+
cwd: "C:\\Program Files (x86)\\Pi Dashboard",
|
|
105
|
+
fs: fixtures.managedInstall({ homedir, platform: "win32" }),
|
|
106
|
+
},
|
|
107
|
+
(ctx) => {
|
|
108
|
+
const registry = ctx.createRegistry();
|
|
109
|
+
registerDefaultTools(registry, ctx.createStrategyDeps());
|
|
110
|
+
const res = registry.resolve("pi");
|
|
111
|
+
expect(res.ok).toBe(true);
|
|
112
|
+
expect(res.source).toBe("managed");
|
|
113
|
+
},
|
|
114
|
+
);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it("G4 — node.exe at C:\\Program Files\\nodejs\\node.exe", async () => {
|
|
118
|
+
const homedir = "C:\\Users\\R";
|
|
119
|
+
await withFakeEnv(
|
|
120
|
+
{
|
|
121
|
+
platform: "win32",
|
|
122
|
+
homedir,
|
|
123
|
+
env: { PATH: "C:\\Program Files\\nodejs" },
|
|
124
|
+
fs: layer(
|
|
125
|
+
fixtures.managedInstall({ homedir, platform: "win32" }),
|
|
126
|
+
{
|
|
127
|
+
"C:\\Program Files\\nodejs\\node.exe": "\x7fELF",
|
|
128
|
+
},
|
|
129
|
+
),
|
|
130
|
+
},
|
|
131
|
+
(ctx) => {
|
|
132
|
+
const registry = ctx.createRegistry();
|
|
133
|
+
registerDefaultTools(registry, ctx.createStrategyDeps());
|
|
134
|
+
const nodeRes = registry.resolveExecutor("node");
|
|
135
|
+
expect(nodeRes.ok).toBe(true);
|
|
136
|
+
expect(nodeRes.path).toBe("C:\\Program Files\\nodejs\\node.exe");
|
|
137
|
+
// Binary-kind tool: argv = [path] (no interpreter prepended).
|
|
138
|
+
expect(nodeRes.argv).toEqual(["C:\\Program Files\\nodejs\\node.exe"]);
|
|
139
|
+
expect(snapshotTrail(nodeRes, ctx)).toMatchSnapshot();
|
|
140
|
+
},
|
|
141
|
+
);
|
|
142
|
+
});
|
|
143
|
+
});
|