@blackbelt-technology/pi-agent-dashboard 0.2.9 → 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 +64 -8
- package/README.md +308 -101
- package/docs/architecture.md +515 -16
- package/package.json +14 -7
- package/packages/extension/package.json +11 -3
- package/packages/extension/src/__tests__/ask-user-tool.test.ts +300 -3
- package/packages/extension/src/__tests__/enrich-model-metadata.test.ts +201 -0
- package/packages/extension/src/__tests__/fork-entryid-timing.test.ts +100 -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/ask-user-tool.ts +289 -20
- package/packages/extension/src/bridge.ts +107 -6
- package/packages/extension/src/command-handler.ts +34 -39
- 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/prompt-expander.ts +25 -4
- 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 +17 -2
- package/packages/server/src/__tests__/auto-attach.test.ts +10 -1
- package/packages/server/src/__tests__/auto-shutdown.test.ts +8 -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 +246 -10
- package/packages/server/src/__tests__/browser-gateway-handler-errors.test.ts +129 -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__/cors.test.ts +34 -2
- 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-manager-pid-registry.test.ts +168 -0
- package/packages/server/src/__tests__/editor-manager.test.ts +33 -0
- package/packages/server/src/__tests__/editor-pid-registry.test.ts +191 -0
- package/packages/server/src/__tests__/editor-registry.test.ts +29 -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__/fix-pty-permissions.test.ts +59 -0
- package/packages/server/src/__tests__/force-kill-handler.test.ts +57 -8
- package/packages/server/src/__tests__/git-operations.test.ts +9 -7
- package/packages/server/src/__tests__/health-endpoint.test.ts +11 -13
- 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__/openspec-tasks-parser.test.ts +178 -0
- package/packages/server/src/__tests__/openspec-tasks-routes.test.ts +180 -0
- package/packages/server/src/__tests__/package-manager-wrapper-resolve.test.ts +126 -0
- package/packages/server/src/__tests__/package-manager-wrapper.test.ts +45 -10
- package/packages/server/src/__tests__/pi-core-checker.test.ts +195 -0
- package/packages/server/src/__tests__/pi-core-routes.test.ts +184 -0
- package/packages/server/src/__tests__/pi-core-updater.test.ts +214 -0
- 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-auth-routes.test.ts +13 -3
- 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__/recommended-routes.test.ts +389 -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__/session-file-dedup.test.ts +10 -10
- package/packages/server/src/__tests__/session-lifecycle-logging.test.ts +8 -2
- package/packages/server/src/__tests__/sleep-aware-heartbeat.test.ts +3 -1
- package/packages/server/src/__tests__/smoke-integration.test.ts +10 -10
- package/packages/server/src/__tests__/terminal-manager.test.ts +41 -1
- package/packages/server/src/__tests__/test-server-canary.test.ts +31 -0
- 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 +103 -6
- package/packages/server/src/__tests__/ws-ping-pong.test.ts +10 -2
- 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 +108 -9
- package/packages/server/src/browser-gateway.ts +16 -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 +39 -5
- package/packages/server/src/editor-pid-registry.ts +199 -0
- package/packages/server/src/editor-registry.ts +22 -25
- package/packages/server/src/fix-pty-permissions.ts +44 -0
- package/packages/server/src/git-operations.ts +1 -1
- package/packages/server/src/headless-pid-registry.ts +16 -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/npm-search-proxy.ts +71 -0
- package/packages/server/src/openspec-tasks.ts +158 -0
- package/packages/server/src/package-manager-wrapper.ts +225 -34
- package/packages/server/src/pi-core-checker.ts +290 -0
- package/packages/server/src/pi-core-updater.ts +172 -0
- package/packages/server/src/pi-gateway.ts +7 -0
- 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/file-routes.ts +30 -3
- package/packages/server/src/routes/openspec-routes.ts +107 -1
- package/packages/server/src/routes/pi-core-routes.ts +140 -0
- package/packages/server/src/routes/provider-auth-routes.ts +12 -10
- package/packages/server/src/routes/provider-routes.ts +55 -2
- package/packages/server/src/routes/recommended-routes.ts +225 -0
- package/packages/server/src/routes/system-routes.ts +30 -34
- 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 +363 -26
- 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 +65 -20
- package/packages/server/src/test-env-guard.ts +26 -0
- package/packages/server/src/test-support/test-server.ts +63 -0
- package/packages/server/src/tunnel.ts +172 -34
- 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 +59 -3
- 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__/openspec-poller.test.ts +44 -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 +156 -0
- 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__/source-matching.test.ts +143 -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 +93 -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 +71 -49
- 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 +196 -0
- package/packages/shared/src/resolve-jiti.ts +62 -3
- package/packages/shared/src/rest-api.ts +97 -0
- package/packages/shared/src/semaphore.ts +83 -0
- package/packages/shared/src/source-matching.ts +126 -0
- package/packages/shared/src/test-support/setup-home.ts +74 -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
- package/packages/shared/src/types.ts +7 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Family A — Electron-packaged dashboard scenarios.
|
|
3
|
+
*
|
|
4
|
+
* A1: electron-fresh — bundled dashboard, no pi, no settings.
|
|
5
|
+
* A2: electron-prewarmed — bundled dashboard, managed pi+openspec, valid settings.
|
|
6
|
+
*
|
|
7
|
+
* Full family (A3, A4) lands as cells are activated in tasks.md §6.
|
|
8
|
+
*/
|
|
9
|
+
import { describe, expect, it } from "vitest";
|
|
10
|
+
import { withFakeEnv, layer } from "../harness.js";
|
|
11
|
+
import { registerDefaultTools } from "../../../tool-registry/definitions.js";
|
|
12
|
+
import * as fixtures from "../fixtures/index.js";
|
|
13
|
+
import { snapshotTrail } from "../assertions.js";
|
|
14
|
+
import { register, SKIPPED_SCENARIOS, cellKey } from "../scenarios.js";
|
|
15
|
+
|
|
16
|
+
// Register the cells this file covers. Overrides the bulk-skip from
|
|
17
|
+
// scenarios-skipped.ts. Runs at module import.
|
|
18
|
+
const A1: Parameters<typeof register>[0][] = [
|
|
19
|
+
{ platform: "linux", dash: "electron", pi: "absent", settings: "missing", env: "normal" },
|
|
20
|
+
{ platform: "darwin", dash: "electron", pi: "absent", settings: "missing", env: "normal" },
|
|
21
|
+
{ platform: "win32", dash: "electron", pi: "absent", settings: "missing", env: "normal" },
|
|
22
|
+
];
|
|
23
|
+
const A2: Parameters<typeof register>[0][] = [
|
|
24
|
+
{ platform: "linux", dash: "electron", pi: "present-valid", settings: "valid", env: "normal" },
|
|
25
|
+
{ platform: "darwin", dash: "electron", pi: "present-valid", settings: "valid", env: "normal" },
|
|
26
|
+
{ platform: "win32", dash: "electron", pi: "present-valid", settings: "valid", env: "normal" },
|
|
27
|
+
];
|
|
28
|
+
// A3: electron bundled + global pi (user had pi before installing dashboard).
|
|
29
|
+
const A3: Parameters<typeof register>[0][] = [
|
|
30
|
+
{ platform: "linux", dash: "electron", pi: "present-no-ext", settings: "valid", env: "normal" },
|
|
31
|
+
];
|
|
32
|
+
// A4: linux AppImage first run (temp mount; bridge registration skipped).
|
|
33
|
+
const A4: Parameters<typeof register>[0][] = [
|
|
34
|
+
{ platform: "linux", dash: "electron", pi: "appimage-tmp", settings: "missing", env: "normal" },
|
|
35
|
+
];
|
|
36
|
+
for (const cell of [...A1, ...A2, ...A3, ...A4]) {
|
|
37
|
+
register(cell, "families/a-electron.test.ts");
|
|
38
|
+
SKIPPED_SCENARIOS.delete(cellKey(cell));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
describe("Family A — electron-packaged", () => {
|
|
42
|
+
describe("A1 — electron-fresh (bundled dashboard, no pi)", () => {
|
|
43
|
+
it.each(["linux", "darwin", "win32"] as const)(
|
|
44
|
+
"resolves nothing for pi (%s)",
|
|
45
|
+
async (platform) => {
|
|
46
|
+
const homedir = platform === "win32" ? "C:\\Users\\R" : "/home/r";
|
|
47
|
+
await withFakeEnv(
|
|
48
|
+
{
|
|
49
|
+
platform,
|
|
50
|
+
homedir,
|
|
51
|
+
env: { PATH: platform === "win32" ? "C:\\Windows\\System32" : "/usr/bin" },
|
|
52
|
+
fs: layer(fixtures.electronPackaged({ platform })),
|
|
53
|
+
},
|
|
54
|
+
(ctx) => {
|
|
55
|
+
const registry = ctx.createRegistry();
|
|
56
|
+
registerDefaultTools(registry, ctx.createStrategyDeps());
|
|
57
|
+
const res = registry.resolve("pi");
|
|
58
|
+
expect(res.ok).toBe(false);
|
|
59
|
+
expect(snapshotTrail(res, ctx)).toMatchSnapshot();
|
|
60
|
+
},
|
|
61
|
+
);
|
|
62
|
+
},
|
|
63
|
+
);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
describe("A2 — electron-prewarmed (bundled + managed pi)", () => {
|
|
67
|
+
it.each(["linux", "darwin", "win32"] as const)(
|
|
68
|
+
"resolves pi via managed (%s)",
|
|
69
|
+
async (platform) => {
|
|
70
|
+
const homedir = platform === "win32" ? "C:\\Users\\R" : "/home/r";
|
|
71
|
+
await withFakeEnv(
|
|
72
|
+
{
|
|
73
|
+
platform,
|
|
74
|
+
homedir,
|
|
75
|
+
env: { PATH: platform === "win32" ? "C:\\Windows\\System32" : "/usr/bin" },
|
|
76
|
+
fs: layer(
|
|
77
|
+
fixtures.electronPackaged({ platform }),
|
|
78
|
+
fixtures.managedInstall({ homedir, platform }),
|
|
79
|
+
fixtures.settingsJson({
|
|
80
|
+
homedir,
|
|
81
|
+
platform,
|
|
82
|
+
packages: [
|
|
83
|
+
platform === "win32"
|
|
84
|
+
? "C:\\Program Files\\PI Dashboard\\resources\\server\\packages\\extension"
|
|
85
|
+
: "/usr/lib/pi-dashboard/resources/server/packages/extension",
|
|
86
|
+
],
|
|
87
|
+
}),
|
|
88
|
+
),
|
|
89
|
+
},
|
|
90
|
+
(ctx) => {
|
|
91
|
+
const registry = ctx.createRegistry();
|
|
92
|
+
registerDefaultTools(registry, ctx.createStrategyDeps());
|
|
93
|
+
const res = registry.resolve("pi");
|
|
94
|
+
expect(res.ok).toBe(true);
|
|
95
|
+
expect(res.source).toBe("managed");
|
|
96
|
+
expect(snapshotTrail(res, ctx)).toMatchSnapshot();
|
|
97
|
+
},
|
|
98
|
+
);
|
|
99
|
+
},
|
|
100
|
+
);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe("A3 — electron + pre-existing global pi", () => {
|
|
104
|
+
it("global npm pi takes precedence over managed-bin fallback (linux)", async () => {
|
|
105
|
+
const homedir = "/home/r";
|
|
106
|
+
await withFakeEnv(
|
|
107
|
+
{
|
|
108
|
+
platform: "linux",
|
|
109
|
+
homedir,
|
|
110
|
+
env: { PATH: "/usr/local/bin" },
|
|
111
|
+
fs: layer(
|
|
112
|
+
fixtures.electronPackaged({ platform: "linux" }),
|
|
113
|
+
fixtures.npmGlobalUnix({}),
|
|
114
|
+
fixtures.settingsJson({ homedir, platform: "linux", packages: [] }),
|
|
115
|
+
),
|
|
116
|
+
},
|
|
117
|
+
(ctx) => {
|
|
118
|
+
const registry = ctx.createRegistry();
|
|
119
|
+
registerDefaultTools(registry, ctx.createStrategyDeps());
|
|
120
|
+
const res = registry.resolve("pi");
|
|
121
|
+
// pi chain on posix: override → managed-bin → where.
|
|
122
|
+
// No managed install present; `where` finds /usr/local/bin/pi.
|
|
123
|
+
expect(res.ok).toBe(true);
|
|
124
|
+
expect(res.source).toBe("system");
|
|
125
|
+
expect(snapshotTrail(res, ctx)).toMatchSnapshot();
|
|
126
|
+
},
|
|
127
|
+
);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
describe("A4 — AppImage first run (temp mount)", () => {
|
|
132
|
+
it("findBundledExtension rejects /tmp/.mount_* paths", async () => {
|
|
133
|
+
const homedir = "/home/r";
|
|
134
|
+
await withFakeEnv(
|
|
135
|
+
{
|
|
136
|
+
platform: "linux",
|
|
137
|
+
homedir,
|
|
138
|
+
fs: fixtures.electronPackaged({ platform: "linux", appimage: true }),
|
|
139
|
+
},
|
|
140
|
+
(ctx) => {
|
|
141
|
+
// The bundled extension resolved by findBundledExtension when
|
|
142
|
+
// running from an AppImage temp mount would be under
|
|
143
|
+
// `/tmp/.mount_*/...`. The real implementation rejects those
|
|
144
|
+
// paths explicitly (bridge-register.ts). Here we just verify
|
|
145
|
+
// the fixture places files where the temp-mount detection
|
|
146
|
+
// would fire.
|
|
147
|
+
const paths = fixtures.electronPaths({ platform: "linux", appimage: true });
|
|
148
|
+
expect(paths.extensionDir).toContain("/tmp/.mount_");
|
|
149
|
+
expect(ctx.fs.existsSync(paths.extensionDir + "/package.json")).toBe(true);
|
|
150
|
+
// Full round-trip rejection lives in bridge-register unit
|
|
151
|
+
// tests; this family cell confirms only the fixture shape.
|
|
152
|
+
},
|
|
153
|
+
);
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
});
|
|
@@ -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
|
+
});
|