taskplane 0.10.0 → 0.10.1
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.
|
@@ -12,43 +12,81 @@ import type { AllocatedLane, AllocatedTask, DependencyGraph, LaneExecutionResult
|
|
|
12
12
|
import { allocateLanes } from "./waves.ts";
|
|
13
13
|
import { runGit } from "./git.ts";
|
|
14
14
|
|
|
15
|
-
// ──
|
|
15
|
+
// ── Taskplane Package File Resolution ────────────────────────────────
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
|
-
*
|
|
18
|
+
* Cached result of `npm root -g` to avoid repeated child process spawns.
|
|
19
|
+
* null = not yet resolved, "" = resolution failed.
|
|
20
|
+
*/
|
|
21
|
+
let _npmGlobalRoot: string | null = null;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Get the global npm root directory via `npm root -g`.
|
|
25
|
+
* Result is cached for the process lifetime.
|
|
26
|
+
*/
|
|
27
|
+
function getNpmGlobalRoot(): string {
|
|
28
|
+
if (_npmGlobalRoot !== null) return _npmGlobalRoot;
|
|
29
|
+
try {
|
|
30
|
+
const result = spawnSync("npm", ["root", "-g"], {
|
|
31
|
+
encoding: "utf-8",
|
|
32
|
+
timeout: 5000,
|
|
33
|
+
shell: true,
|
|
34
|
+
});
|
|
35
|
+
_npmGlobalRoot = result.stdout?.trim() || "";
|
|
36
|
+
} catch {
|
|
37
|
+
_npmGlobalRoot = "";
|
|
38
|
+
}
|
|
39
|
+
return _npmGlobalRoot;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Resolve a file path within the taskplane package.
|
|
19
44
|
*
|
|
20
45
|
* Resolution order:
|
|
21
|
-
* 1. Local project: {repoRoot}/
|
|
22
|
-
* 2.
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
46
|
+
* 1. Local project: {repoRoot}/{relPath} (for taskplane development)
|
|
47
|
+
* 2. `npm root -g` based: {npmGlobalRoot}/taskplane/{relPath}
|
|
48
|
+
* (covers Homebrew, nvm, volta, pnpm, and any custom npm prefix)
|
|
49
|
+
* 3. Well-known global npm paths (Windows/macOS/Linux):
|
|
50
|
+
* - {APPDATA}/npm/node_modules/taskplane/{relPath}
|
|
51
|
+
* - {HOME}/.npm-global/lib/node_modules/taskplane/{relPath}
|
|
52
|
+
* - /usr/local/lib/node_modules/taskplane/{relPath}
|
|
53
|
+
* - /opt/homebrew/lib/node_modules/taskplane/{relPath}
|
|
54
|
+
* 4. Peer of pi's package: resolve from pi's binary location
|
|
55
|
+
*
|
|
56
|
+
* @param repoRoot - Absolute path to the project root
|
|
57
|
+
* @param relPath - Relative path within the taskplane package (e.g., "bin/rpc-wrapper.mjs")
|
|
58
|
+
* @returns Absolute path to the resolved file
|
|
27
59
|
*/
|
|
28
|
-
function
|
|
29
|
-
const extFile = join("extensions", "task-runner.ts");
|
|
30
|
-
|
|
60
|
+
function resolveTaskplanePackageFile(repoRoot: string, relPath: string): string {
|
|
31
61
|
// 1. Local project (taskplane development)
|
|
32
|
-
const localPath = join(resolve(repoRoot),
|
|
62
|
+
const localPath = join(resolve(repoRoot), relPath);
|
|
33
63
|
if (existsSync(localPath)) return localPath;
|
|
34
64
|
|
|
35
|
-
// 2. Global npm install paths
|
|
36
|
-
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
37
65
|
const candidates: string[] = [];
|
|
66
|
+
|
|
67
|
+
// 2. Dynamic: `npm root -g` (covers ALL npm setups: nvm, Homebrew, volta, etc.)
|
|
68
|
+
const npmRoot = getNpmGlobalRoot();
|
|
69
|
+
if (npmRoot) {
|
|
70
|
+
candidates.push(join(npmRoot, "taskplane", relPath));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// 3. Well-known static paths
|
|
74
|
+
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
38
75
|
if (process.env.APPDATA) {
|
|
39
|
-
candidates.push(join(process.env.APPDATA, "npm", "node_modules", "taskplane",
|
|
76
|
+
candidates.push(join(process.env.APPDATA, "npm", "node_modules", "taskplane", relPath));
|
|
40
77
|
}
|
|
41
78
|
if (home) {
|
|
42
|
-
candidates.push(join(home, "AppData", "Roaming", "npm", "node_modules", "taskplane",
|
|
43
|
-
candidates.push(join(home, ".npm-global", "lib", "node_modules", "taskplane",
|
|
79
|
+
candidates.push(join(home, "AppData", "Roaming", "npm", "node_modules", "taskplane", relPath));
|
|
80
|
+
candidates.push(join(home, ".npm-global", "lib", "node_modules", "taskplane", relPath));
|
|
44
81
|
}
|
|
45
|
-
candidates.push(join("/usr", "local", "lib", "node_modules", "taskplane",
|
|
82
|
+
candidates.push(join("/usr", "local", "lib", "node_modules", "taskplane", relPath));
|
|
83
|
+
candidates.push(join("/opt", "homebrew", "lib", "node_modules", "taskplane", relPath));
|
|
46
84
|
|
|
47
|
-
//
|
|
85
|
+
// 4. Peer of pi's package
|
|
48
86
|
try {
|
|
49
87
|
const piPath = process.argv[1] || "";
|
|
50
88
|
const piPkgDir = resolve(piPath, "..", "..");
|
|
51
|
-
candidates.push(join(piPkgDir, "..", "taskplane",
|
|
89
|
+
candidates.push(join(piPkgDir, "..", "taskplane", relPath));
|
|
52
90
|
} catch { /* ignore */ }
|
|
53
91
|
|
|
54
92
|
for (const candidate of candidates) {
|
|
@@ -59,51 +97,24 @@ function resolveTaskRunnerExtensionPath(repoRoot: string): string {
|
|
|
59
97
|
return localPath;
|
|
60
98
|
}
|
|
61
99
|
|
|
100
|
+
// ── Task Runner Extension Path Resolution ────────────────────────────
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Find the task-runner extension path for lane sessions.
|
|
104
|
+
* @see resolveTaskplanePackageFile for resolution order
|
|
105
|
+
*/
|
|
106
|
+
function resolveTaskRunnerExtensionPath(repoRoot: string): string {
|
|
107
|
+
return resolveTaskplanePackageFile(repoRoot, join("extensions", "task-runner.ts"));
|
|
108
|
+
}
|
|
109
|
+
|
|
62
110
|
// ── RPC Wrapper Path Resolution ──────────────────────────────────────
|
|
63
111
|
|
|
64
112
|
/**
|
|
65
113
|
* Find the rpc-wrapper.mjs path for lane sessions.
|
|
66
|
-
*
|
|
67
|
-
* Resolution order mirrors resolveTaskRunnerExtensionPath:
|
|
68
|
-
* 1. Local project: {repoRoot}/bin/rpc-wrapper.mjs (for taskplane dev)
|
|
69
|
-
* 2. Global npm (Windows): {APPDATA}/npm/node_modules/taskplane/bin/rpc-wrapper.mjs
|
|
70
|
-
* 3. Global npm (Unix): /usr/local/lib/node_modules/taskplane/bin/rpc-wrapper.mjs
|
|
71
|
-
* 4. npm peer: resolve from pi's location
|
|
72
|
-
*
|
|
73
|
-
* @throws ExecutionError if rpc-wrapper.mjs cannot be found anywhere
|
|
114
|
+
* @see resolveTaskplanePackageFile for resolution order
|
|
74
115
|
*/
|
|
75
116
|
export function resolveRpcWrapperPath(repoRoot: string): string {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
// 1. Local project (taskplane development)
|
|
79
|
-
const localPath = join(resolve(repoRoot), wrapperFile);
|
|
80
|
-
if (existsSync(localPath)) return localPath;
|
|
81
|
-
|
|
82
|
-
// 2. Global npm install paths
|
|
83
|
-
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
84
|
-
const candidates: string[] = [];
|
|
85
|
-
if (process.env.APPDATA) {
|
|
86
|
-
candidates.push(join(process.env.APPDATA, "npm", "node_modules", "taskplane", wrapperFile));
|
|
87
|
-
}
|
|
88
|
-
if (home) {
|
|
89
|
-
candidates.push(join(home, "AppData", "Roaming", "npm", "node_modules", "taskplane", wrapperFile));
|
|
90
|
-
candidates.push(join(home, ".npm-global", "lib", "node_modules", "taskplane", wrapperFile));
|
|
91
|
-
}
|
|
92
|
-
candidates.push(join("/usr", "local", "lib", "node_modules", "taskplane", wrapperFile));
|
|
93
|
-
|
|
94
|
-
// 3. Peer of pi's package
|
|
95
|
-
try {
|
|
96
|
-
const piPath = process.argv[1] || "";
|
|
97
|
-
const piPkgDir = resolve(piPath, "..", "..");
|
|
98
|
-
candidates.push(join(piPkgDir, "..", "taskplane", wrapperFile));
|
|
99
|
-
} catch { /* ignore */ }
|
|
100
|
-
|
|
101
|
-
for (const candidate of candidates) {
|
|
102
|
-
if (existsSync(candidate)) return candidate;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// Fallback: return the local path (will fail at spawn time with a clear error)
|
|
106
|
-
return localPath;
|
|
117
|
+
return resolveTaskplanePackageFile(repoRoot, join("bin", "rpc-wrapper.mjs"));
|
|
107
118
|
}
|
|
108
119
|
|
|
109
120
|
// ── Telemetry Helpers ────────────────────────────────────────────────
|