@kb-labs/core-workspace 2.119.0 → 2.119.1-canary.a6aa9b388
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/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../home/runner/work/kb-labs/kb-labs/core/workspace/src/workspace/root-resolver.ts"],"names":["fsp"],"mappings":"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","file":"index.js","sourcesContent":["import path from 'node:path'\nimport { promises as fsp } from 'node:fs'\nimport { fileURLToPath } from 'node:url'\n\nimport { findRepoRoot } from '@kb-labs/core-sys'\n\nimport type {\n PlatformRootResolution,\n ProjectRootResolution,\n ResolvePlatformRootOptions,\n ResolveProjectRootOptions,\n ResolveRootsOptions,\n ResolveWorkspaceRootOptions,\n RootsResolution,\n WorkspaceFs,\n WorkspaceRootResolution,\n} from './types'\n\n/**\n * Filenames probed when looking for a project config. Both variants are\n * accepted because `loadPlatformConfig` reads either one.\n */\nconst PROJECT_CONFIG_FILENAMES = ['kb.config.jsonc', 'kb.config.json'] as const\n\n/**\n * Package markers used to recognise an installed KB Labs platform. Any one of\n * these, when present as `<root>/node_modules/<marker>`, indicates that\n * `<root>` is a platform installation.\n */\nconst PLATFORM_PACKAGE_MARKERS = [\n '@kb-labs/cli-bin',\n '@kb-labs/core-runtime',\n] as const\n\nconst defaultFs: WorkspaceFs = {\n async exists(target: string): Promise<boolean> {\n try {\n await fsp.access(target)\n return true\n } catch {\n return false\n }\n },\n}\n\n// ──────────────────────────────────────────────────────────────────────────\n// Project root (previously \"workspace root\")\n// ──────────────────────────────────────────────────────────────────────────\n\nfunction resolveProjectEnvRoot(\n env: Record<string, string | undefined> | undefined,\n): string | undefined {\n if (!env) {\n return undefined\n }\n return (\n env.KB_PROJECT_ROOT ??\n env.KB_LABS_WORKSPACE_ROOT ??\n env.KB_LABS_REPO_ROOT\n )\n}\n\n/**\n * Walk up from `startDir` looking for a directory that contains\n * `.kb/kb.config.jsonc` or `.kb/kb.config.json`. Returns the first match, or\n * `undefined` if none is found before the filesystem root.\n *\n * This is the canonical \"is this a project?\" probe — exported so CLI\n * commands, discovery, and scope-aware code can share the same rules the\n * config loader uses.\n */\nexport async function findProjectConfigRoot(\n startDir: string,\n fs: WorkspaceFs = defaultFs,\n): Promise<string | undefined> {\n let current = path.resolve(startDir)\n\n while (true) {\n for (const name of PROJECT_CONFIG_FILENAMES) {\n const configPath = path.join(current, '.kb', name)\n // eslint-disable-next-line no-await-in-loop -- Sequential walk is intentional\n if (await fs.exists(configPath)) {\n return current\n }\n }\n\n const parent = path.dirname(current)\n if (parent === current) {\n return undefined\n }\n\n current = parent\n }\n}\n\n/**\n * Resolve the project root — the directory that contains the user's\n * `.kb/kb.config.json` and plugin state.\n *\n * Priority order:\n *\n * 1. Explicit `cwd` option (e.g. CLI flag).\n * 2. Environment variables: `KB_PROJECT_ROOT` (preferred), then the legacy\n * `KB_LABS_WORKSPACE_ROOT` / `KB_LABS_REPO_ROOT`.\n * 3. Nearest `.kb/kb.config.json` ancestor walking up from `startDir`.\n * 4. Repository root discovered via `findRepoRoot` (pnpm workspace, `.git`,\n * `package.json`).\n * 5. Fallback to `startDir` / `process.cwd()`.\n *\n * In monorepo dev mode this typically returns the workspace root. In installed\n * mode it returns the user's project directory.\n */\nexport async function resolveProjectRoot(\n options: ResolveProjectRootOptions = {},\n): Promise<ProjectRootResolution> {\n const {\n cwd,\n env = process.env,\n fs = defaultFs,\n startDir = process.cwd(),\n } = options\n\n if (cwd) {\n return {\n rootDir: path.resolve(cwd),\n source: 'explicit',\n }\n }\n\n const envRoot = resolveProjectEnvRoot(env)\n if (envRoot) {\n return {\n rootDir: path.resolve(envRoot),\n source: 'env',\n }\n }\n\n const configRoot = await findProjectConfigRoot(startDir, fs)\n if (configRoot) {\n return {\n rootDir: configRoot,\n source: 'config',\n }\n }\n\n try {\n const repoRoot = await findRepoRoot(startDir)\n const resolvedRepo = path.resolve(repoRoot)\n const fsRoot = path.parse(resolvedRepo).root\n\n if (resolvedRepo !== fsRoot) {\n return {\n rootDir: resolvedRepo,\n source: 'repo',\n }\n }\n } catch {\n // swallow and fall through to fallback\n }\n\n return {\n rootDir: path.resolve(startDir),\n source: 'fallback',\n }\n}\n\n/**\n * @deprecated Use {@link resolveProjectRoot}. This alias is kept for\n * backwards compatibility; semantics are identical.\n *\n * The name \"workspace root\" is ambiguous — in KB Labs terminology, what it\n * actually refers to is the project root (where `.kb/kb.config.json` lives),\n * not the KB Labs platform installation. Prefer the explicit name.\n */\nexport async function resolveWorkspaceRoot(\n options: ResolveWorkspaceRootOptions = {},\n): Promise<WorkspaceRootResolution> {\n return resolveProjectRoot(options)\n}\n\n// ──────────────────────────────────────────────────────────────────────────\n// Platform root\n// ──────────────────────────────────────────────────────────────────────────\n\n/**\n * Check whether `<candidate>/node_modules/<marker>` exists for any of the\n * known platform package markers.\n *\n * Candidates inside a pnpm virtual store (paths containing `/.pnpm/`) are\n * deliberately ignored: pnpm's virtual store nests `node_modules` trees\n * that look identical to a real platform install, and walking up from a\n * bin.js that pnpm resolved through the store would otherwise stop inside\n * the store at e.g. `.pnpm/@kb-labs+cli-bin@2.5.0_.../node_modules/@kb-labs/cli-bin`\n * instead of the real platform root that owns the hoisted symlinks.\n */\nasync function hasPlatformMarker(\n candidate: string,\n fs: WorkspaceFs,\n): Promise<boolean> {\n if (isInsidePnpmStore(candidate)) {\n return false\n }\n for (const marker of PLATFORM_PACKAGE_MARKERS) {\n const markerPath = path.join(candidate, 'node_modules', marker)\n // eslint-disable-next-line no-await-in-loop -- Sequential probe is intentional\n if (await fs.exists(markerPath)) {\n return true\n }\n }\n return false\n}\n\n/**\n * Returns true if `candidate` is located inside a pnpm virtual store\n * (i.e. its path contains a `/.pnpm/` segment). Such candidates should\n * never be treated as a platform root during walk-up because they live\n * inside the store of whichever real platform root hoists them.\n */\nfunction isInsidePnpmStore(candidate: string): boolean {\n // Use path separators so we don't match literal `.pnpm` directory names\n // at the beginning or end of the path (unlikely but cheap to be strict).\n const sep = path.sep\n return candidate.includes(`${sep}.pnpm${sep}`)\n}\n\n/**\n * Walk up from `startDir` looking for either a platform marker\n * (`node_modules/@kb-labs/*`) or a pnpm workspace marker\n * (`pnpm-workspace.yaml`). Returns the first match.\n *\n * Exported so CLI/diagnostics code can identify the platform root without\n * paying for the full composite `resolvePlatformRoot` fallback chain.\n */\nexport async function findPlatformMarkerRoot(\n startDir: string,\n fs: WorkspaceFs = defaultFs,\n): Promise<string | undefined> {\n let current = path.resolve(startDir)\n\n while (true) {\n // eslint-disable-next-line no-await-in-loop -- Sequential walk is intentional\n if (await hasPlatformMarker(current, fs)) {\n return current\n }\n\n // eslint-disable-next-line no-await-in-loop -- Sequential walk is intentional\n if (await fs.exists(path.join(current, 'pnpm-workspace.yaml'))) {\n return current\n }\n\n const parent = path.dirname(current)\n if (parent === current) {\n return undefined\n }\n current = parent\n }\n}\n\n/**\n * Given a `file://` URL (typically `import.meta.url` from a CLI entry or\n * service bootstrap), walk up through its directory ancestors to locate the\n * platform installation root.\n *\n * Two signals are collected on the way up:\n *\n * 1. **First platform-package marker hit** — a directory whose `node_modules`\n * contains `@kb-labs/cli-bin` (or another known platform marker). In\n * installed mode this is the platform installation directory.\n *\n * 2. **Top-most `pnpm-workspace.yaml`** — we keep walking past inner workspace\n * markers and record the *outermost* one. This is important in the\n * KB Labs \"workspace of workspaces\" dev layout, where individual sub-repos\n * (`platform/kb-labs-cli`, `plugins/kb-labs-*`, etc.) each have their own\n * `pnpm-workspace.yaml` while the true monorepo root lives at the very top.\n *\n * Precedence after walking: the outermost `pnpm-workspace.yaml` wins if one\n * exists (dev mode); otherwise we return the first marker hit (installed\n * mode). This way a naive walk-up can never stop prematurely at a nested\n * workspace or at a package with its own hoisting symlink.\n */\nasync function resolvePlatformRootFromModuleUrl(\n moduleUrl: string,\n fs: WorkspaceFs,\n): Promise<string | undefined> {\n let modulePath: string\n try {\n modulePath = fileURLToPath(moduleUrl)\n } catch {\n return undefined\n }\n\n let current = path.dirname(modulePath)\n let firstMarkerHit: string | undefined\n let topMostWorkspace: string | undefined\n\n while (true) {\n // eslint-disable-next-line no-await-in-loop -- Sequential walk is intentional\n if (!firstMarkerHit && (await hasPlatformMarker(current, fs))) {\n firstMarkerHit = current\n }\n\n // Keep overwriting: we want the *outermost* (highest-up) workspace root,\n // not the innermost. This handles the KB Labs nested-workspaces layout.\n // eslint-disable-next-line no-await-in-loop -- Sequential walk is intentional\n if (await fs.exists(path.join(current, 'pnpm-workspace.yaml'))) {\n topMostWorkspace = current\n }\n\n const parent = path.dirname(current)\n if (parent === current) {\n // Reached filesystem root. Prefer top-most workspace (dev mode);\n // otherwise return first marker hit (installed mode).\n return topMostWorkspace ?? firstMarkerHit\n }\n current = parent\n }\n}\n\n/**\n * Resolve the platform root — the directory that contains the installed\n * KB Labs platform code (i.e. the parent of `node_modules/@kb-labs/*`).\n *\n * Priority order:\n *\n * 1. Explicit `cwd` option (e.g. CLI flag).\n * 2. Environment variable `KB_PLATFORM_ROOT` (typically set by the installer\n * wrapper script in installed mode).\n * 3. Walk up from `moduleUrl` (e.g. `import.meta.url` of the CLI `bin.ts`),\n * looking for a directory whose `node_modules` contains a known platform\n * package. This is the most reliable signal in installed mode because it\n * does not depend on `process.cwd()`.\n * 4. Walk up from `startDir` looking for either a platform marker or\n * `pnpm-workspace.yaml` (the latter covers monorepo dev mode).\n * 5. Repository root discovered via `findRepoRoot`.\n * 6. Fallback to `startDir` / `process.cwd()`.\n *\n * In monorepo dev mode steps 4 or 5 will typically match the workspace root,\n * so `platformRoot` and `projectRoot` resolve to the same directory.\n */\nexport async function resolvePlatformRoot(\n options: ResolvePlatformRootOptions = {},\n): Promise<PlatformRootResolution> {\n const {\n cwd,\n moduleUrl,\n env = process.env,\n fs = defaultFs,\n startDir = process.cwd(),\n } = options\n\n if (cwd) {\n return {\n rootDir: path.resolve(cwd),\n source: 'explicit',\n }\n }\n\n const envRoot = env?.KB_PLATFORM_ROOT\n if (envRoot) {\n return {\n rootDir: path.resolve(envRoot),\n source: 'env',\n }\n }\n\n if (moduleUrl) {\n const fromModule = await resolvePlatformRootFromModuleUrl(moduleUrl, fs)\n if (fromModule) {\n return {\n rootDir: fromModule,\n source: 'module',\n }\n }\n }\n\n const fromMarker = await findPlatformMarkerRoot(startDir, fs)\n if (fromMarker) {\n return {\n rootDir: fromMarker,\n source: 'marker',\n }\n }\n\n try {\n const repoRoot = await findRepoRoot(startDir)\n const resolvedRepo = path.resolve(repoRoot)\n const fsRoot = path.parse(resolvedRepo).root\n\n if (resolvedRepo !== fsRoot) {\n return {\n rootDir: resolvedRepo,\n source: 'repo',\n }\n }\n } catch {\n // swallow and fall through to fallback\n }\n\n return {\n rootDir: path.resolve(startDir),\n source: 'fallback',\n }\n}\n\n// ──────────────────────────────────────────────────────────────────────────\n// Composite resolver\n// ──────────────────────────────────────────────────────────────────────────\n\n/**\n * Resolve both the platform root and the project root in a single call. This\n * is the main entry point for the CLI, service bootstrap, and `kb-dev`.\n *\n * @example\n * ```ts\n * // In bin.ts (the CLI entrypoint):\n * const { platformRoot, projectRoot } = await resolveRoots({\n * moduleUrl: import.meta.url,\n * startDir: process.cwd(),\n * })\n * ```\n *\n * In dev mode `platformRoot === projectRoot` and `sameLocation` is `true`.\n * In installed mode they differ: `platformRoot` points at the platform\n * installation while `projectRoot` points at the user's project directory.\n */\nexport async function resolveRoots(\n options: ResolveRootsOptions = {},\n): Promise<RootsResolution> {\n const [platform, project] = await Promise.all([\n resolvePlatformRoot(options),\n resolveProjectRoot(options),\n ])\n\n return {\n platformRoot: platform.rootDir,\n projectRoot: project.rootDir,\n sameLocation:\n path.resolve(platform.rootDir) === path.resolve(project.rootDir),\n sources: {\n platform: platform.source,\n project: project.source,\n },\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/workspace/root-resolver.ts"],"names":["fsp"],"mappings":"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","file":"index.js","sourcesContent":["import path from 'node:path'\nimport { promises as fsp } from 'node:fs'\nimport { fileURLToPath } from 'node:url'\n\nimport { findRepoRoot } from '@kb-labs/core-sys'\n\nimport type {\n PlatformRootResolution,\n ProjectRootResolution,\n ResolvePlatformRootOptions,\n ResolveProjectRootOptions,\n ResolveRootsOptions,\n ResolveWorkspaceRootOptions,\n RootsResolution,\n WorkspaceFs,\n WorkspaceRootResolution,\n} from './types'\n\n/**\n * Filenames probed when looking for a project config. Both variants are\n * accepted because `loadPlatformConfig` reads either one.\n */\nconst PROJECT_CONFIG_FILENAMES = ['kb.config.jsonc', 'kb.config.json'] as const\n\n/**\n * Package markers used to recognise an installed KB Labs platform. Any one of\n * these, when present as `<root>/node_modules/<marker>`, indicates that\n * `<root>` is a platform installation.\n */\nconst PLATFORM_PACKAGE_MARKERS = [\n '@kb-labs/cli-bin',\n '@kb-labs/core-runtime',\n] as const\n\nconst defaultFs: WorkspaceFs = {\n async exists(target: string): Promise<boolean> {\n try {\n await fsp.access(target)\n return true\n } catch {\n return false\n }\n },\n}\n\n// ──────────────────────────────────────────────────────────────────────────\n// Project root (previously \"workspace root\")\n// ──────────────────────────────────────────────────────────────────────────\n\nfunction resolveProjectEnvRoot(\n env: Record<string, string | undefined> | undefined,\n): string | undefined {\n if (!env) {\n return undefined\n }\n return (\n env.KB_PROJECT_ROOT ??\n env.KB_LABS_WORKSPACE_ROOT ??\n env.KB_LABS_REPO_ROOT\n )\n}\n\n/**\n * Walk up from `startDir` looking for a directory that contains\n * `.kb/kb.config.jsonc` or `.kb/kb.config.json`. Returns the first match, or\n * `undefined` if none is found before the filesystem root.\n *\n * This is the canonical \"is this a project?\" probe — exported so CLI\n * commands, discovery, and scope-aware code can share the same rules the\n * config loader uses.\n */\nexport async function findProjectConfigRoot(\n startDir: string,\n fs: WorkspaceFs = defaultFs,\n): Promise<string | undefined> {\n let current = path.resolve(startDir)\n\n while (true) {\n for (const name of PROJECT_CONFIG_FILENAMES) {\n const configPath = path.join(current, '.kb', name)\n // eslint-disable-next-line no-await-in-loop -- Sequential walk is intentional\n if (await fs.exists(configPath)) {\n return current\n }\n }\n\n const parent = path.dirname(current)\n if (parent === current) {\n return undefined\n }\n\n current = parent\n }\n}\n\n/**\n * Resolve the project root — the directory that contains the user's\n * `.kb/kb.config.json` and plugin state.\n *\n * Priority order:\n *\n * 1. Explicit `cwd` option (e.g. CLI flag).\n * 2. Environment variables: `KB_PROJECT_ROOT` (preferred), then the legacy\n * `KB_LABS_WORKSPACE_ROOT` / `KB_LABS_REPO_ROOT`.\n * 3. Nearest `.kb/kb.config.json` ancestor walking up from `startDir`.\n * 4. Repository root discovered via `findRepoRoot` (pnpm workspace, `.git`,\n * `package.json`).\n * 5. Fallback to `startDir` / `process.cwd()`.\n *\n * In monorepo dev mode this typically returns the workspace root. In installed\n * mode it returns the user's project directory.\n */\nexport async function resolveProjectRoot(\n options: ResolveProjectRootOptions = {},\n): Promise<ProjectRootResolution> {\n const {\n cwd,\n env = process.env,\n fs = defaultFs,\n startDir = process.cwd(),\n } = options\n\n if (cwd) {\n return {\n rootDir: path.resolve(cwd),\n source: 'explicit',\n }\n }\n\n const envRoot = resolveProjectEnvRoot(env)\n if (envRoot) {\n return {\n rootDir: path.resolve(envRoot),\n source: 'env',\n }\n }\n\n const configRoot = await findProjectConfigRoot(startDir, fs)\n if (configRoot) {\n return {\n rootDir: configRoot,\n source: 'config',\n }\n }\n\n try {\n const repoRoot = await findRepoRoot(startDir)\n const resolvedRepo = path.resolve(repoRoot)\n const fsRoot = path.parse(resolvedRepo).root\n\n if (resolvedRepo !== fsRoot) {\n return {\n rootDir: resolvedRepo,\n source: 'repo',\n }\n }\n } catch {\n // swallow and fall through to fallback\n }\n\n return {\n rootDir: path.resolve(startDir),\n source: 'fallback',\n }\n}\n\n/**\n * @deprecated Use {@link resolveProjectRoot}. This alias is kept for\n * backwards compatibility; semantics are identical.\n *\n * The name \"workspace root\" is ambiguous — in KB Labs terminology, what it\n * actually refers to is the project root (where `.kb/kb.config.json` lives),\n * not the KB Labs platform installation. Prefer the explicit name.\n */\nexport async function resolveWorkspaceRoot(\n options: ResolveWorkspaceRootOptions = {},\n): Promise<WorkspaceRootResolution> {\n return resolveProjectRoot(options)\n}\n\n// ──────────────────────────────────────────────────────────────────────────\n// Platform root\n// ──────────────────────────────────────────────────────────────────────────\n\n/**\n * Check whether `<candidate>/node_modules/<marker>` exists for any of the\n * known platform package markers.\n *\n * Candidates inside a pnpm virtual store (paths containing `/.pnpm/`) are\n * deliberately ignored: pnpm's virtual store nests `node_modules` trees\n * that look identical to a real platform install, and walking up from a\n * bin.js that pnpm resolved through the store would otherwise stop inside\n * the store at e.g. `.pnpm/@kb-labs+cli-bin@2.5.0_.../node_modules/@kb-labs/cli-bin`\n * instead of the real platform root that owns the hoisted symlinks.\n */\nasync function hasPlatformMarker(\n candidate: string,\n fs: WorkspaceFs,\n): Promise<boolean> {\n if (isInsidePnpmStore(candidate)) {\n return false\n }\n for (const marker of PLATFORM_PACKAGE_MARKERS) {\n const markerPath = path.join(candidate, 'node_modules', marker)\n // eslint-disable-next-line no-await-in-loop -- Sequential probe is intentional\n if (await fs.exists(markerPath)) {\n return true\n }\n }\n return false\n}\n\n/**\n * Returns true if `candidate` is located inside a pnpm virtual store\n * (i.e. its path contains a `/.pnpm/` segment). Such candidates should\n * never be treated as a platform root during walk-up because they live\n * inside the store of whichever real platform root hoists them.\n */\nfunction isInsidePnpmStore(candidate: string): boolean {\n // Use path separators so we don't match literal `.pnpm` directory names\n // at the beginning or end of the path (unlikely but cheap to be strict).\n const sep = path.sep\n return candidate.includes(`${sep}.pnpm${sep}`)\n}\n\n/**\n * Walk up from `startDir` looking for either a platform marker\n * (`node_modules/@kb-labs/*`) or a pnpm workspace marker\n * (`pnpm-workspace.yaml`). Returns the first match.\n *\n * Exported so CLI/diagnostics code can identify the platform root without\n * paying for the full composite `resolvePlatformRoot` fallback chain.\n */\nexport async function findPlatformMarkerRoot(\n startDir: string,\n fs: WorkspaceFs = defaultFs,\n): Promise<string | undefined> {\n let current = path.resolve(startDir)\n\n while (true) {\n // eslint-disable-next-line no-await-in-loop -- Sequential walk is intentional\n if (await hasPlatformMarker(current, fs)) {\n return current\n }\n\n // eslint-disable-next-line no-await-in-loop -- Sequential walk is intentional\n if (await fs.exists(path.join(current, 'pnpm-workspace.yaml'))) {\n return current\n }\n\n const parent = path.dirname(current)\n if (parent === current) {\n return undefined\n }\n current = parent\n }\n}\n\n/**\n * Given a `file://` URL (typically `import.meta.url` from a CLI entry or\n * service bootstrap), walk up through its directory ancestors to locate the\n * platform installation root.\n *\n * Two signals are collected on the way up:\n *\n * 1. **First platform-package marker hit** — a directory whose `node_modules`\n * contains `@kb-labs/cli-bin` (or another known platform marker). In\n * installed mode this is the platform installation directory.\n *\n * 2. **Top-most `pnpm-workspace.yaml`** — we keep walking past inner workspace\n * markers and record the *outermost* one. This is important in the\n * KB Labs \"workspace of workspaces\" dev layout, where individual sub-repos\n * (`platform/kb-labs-cli`, `plugins/kb-labs-*`, etc.) each have their own\n * `pnpm-workspace.yaml` while the true monorepo root lives at the very top.\n *\n * Precedence after walking: the outermost `pnpm-workspace.yaml` wins if one\n * exists (dev mode); otherwise we return the first marker hit (installed\n * mode). This way a naive walk-up can never stop prematurely at a nested\n * workspace or at a package with its own hoisting symlink.\n */\nasync function resolvePlatformRootFromModuleUrl(\n moduleUrl: string,\n fs: WorkspaceFs,\n): Promise<string | undefined> {\n let modulePath: string\n try {\n modulePath = fileURLToPath(moduleUrl)\n } catch {\n return undefined\n }\n\n let current = path.dirname(modulePath)\n let firstMarkerHit: string | undefined\n let topMostWorkspace: string | undefined\n\n while (true) {\n // eslint-disable-next-line no-await-in-loop -- Sequential walk is intentional\n if (!firstMarkerHit && (await hasPlatformMarker(current, fs))) {\n firstMarkerHit = current\n }\n\n // Keep overwriting: we want the *outermost* (highest-up) workspace root,\n // not the innermost. This handles the KB Labs nested-workspaces layout.\n // eslint-disable-next-line no-await-in-loop -- Sequential walk is intentional\n if (await fs.exists(path.join(current, 'pnpm-workspace.yaml'))) {\n topMostWorkspace = current\n }\n\n const parent = path.dirname(current)\n if (parent === current) {\n // Reached filesystem root. Prefer top-most workspace (dev mode);\n // otherwise return first marker hit (installed mode).\n return topMostWorkspace ?? firstMarkerHit\n }\n current = parent\n }\n}\n\n/**\n * Resolve the platform root — the directory that contains the installed\n * KB Labs platform code (i.e. the parent of `node_modules/@kb-labs/*`).\n *\n * Priority order:\n *\n * 1. Explicit `cwd` option (e.g. CLI flag).\n * 2. Environment variable `KB_PLATFORM_ROOT` (typically set by the installer\n * wrapper script in installed mode).\n * 3. Walk up from `moduleUrl` (e.g. `import.meta.url` of the CLI `bin.ts`),\n * looking for a directory whose `node_modules` contains a known platform\n * package. This is the most reliable signal in installed mode because it\n * does not depend on `process.cwd()`.\n * 4. Walk up from `startDir` looking for either a platform marker or\n * `pnpm-workspace.yaml` (the latter covers monorepo dev mode).\n * 5. Repository root discovered via `findRepoRoot`.\n * 6. Fallback to `startDir` / `process.cwd()`.\n *\n * In monorepo dev mode steps 4 or 5 will typically match the workspace root,\n * so `platformRoot` and `projectRoot` resolve to the same directory.\n */\nexport async function resolvePlatformRoot(\n options: ResolvePlatformRootOptions = {},\n): Promise<PlatformRootResolution> {\n const {\n cwd,\n moduleUrl,\n env = process.env,\n fs = defaultFs,\n startDir = process.cwd(),\n } = options\n\n if (cwd) {\n return {\n rootDir: path.resolve(cwd),\n source: 'explicit',\n }\n }\n\n const envRoot = env?.KB_PLATFORM_ROOT\n if (envRoot) {\n return {\n rootDir: path.resolve(envRoot),\n source: 'env',\n }\n }\n\n if (moduleUrl) {\n const fromModule = await resolvePlatformRootFromModuleUrl(moduleUrl, fs)\n if (fromModule) {\n return {\n rootDir: fromModule,\n source: 'module',\n }\n }\n }\n\n const fromMarker = await findPlatformMarkerRoot(startDir, fs)\n if (fromMarker) {\n return {\n rootDir: fromMarker,\n source: 'marker',\n }\n }\n\n try {\n const repoRoot = await findRepoRoot(startDir)\n const resolvedRepo = path.resolve(repoRoot)\n const fsRoot = path.parse(resolvedRepo).root\n\n if (resolvedRepo !== fsRoot) {\n return {\n rootDir: resolvedRepo,\n source: 'repo',\n }\n }\n } catch {\n // swallow and fall through to fallback\n }\n\n return {\n rootDir: path.resolve(startDir),\n source: 'fallback',\n }\n}\n\n// ──────────────────────────────────────────────────────────────────────────\n// Composite resolver\n// ──────────────────────────────────────────────────────────────────────────\n\n/**\n * Resolve both the platform root and the project root in a single call. This\n * is the main entry point for the CLI, service bootstrap, and `kb-dev`.\n *\n * @example\n * ```ts\n * // In bin.ts (the CLI entrypoint):\n * const { platformRoot, projectRoot } = await resolveRoots({\n * moduleUrl: import.meta.url,\n * startDir: process.cwd(),\n * })\n * ```\n *\n * In dev mode `platformRoot === projectRoot` and `sameLocation` is `true`.\n * In installed mode they differ: `platformRoot` points at the platform\n * installation while `projectRoot` points at the user's project directory.\n */\nexport async function resolveRoots(\n options: ResolveRootsOptions = {},\n): Promise<RootsResolution> {\n const [platform, project] = await Promise.all([\n resolvePlatformRoot(options),\n resolveProjectRoot(options),\n ])\n\n return {\n platformRoot: platform.rootDir,\n projectRoot: project.rootDir,\n sameLocation:\n path.resolve(platform.rootDir) === path.resolve(project.rootDir),\n sources: {\n platform: platform.source,\n project: project.source,\n },\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kb-labs/core-workspace",
|
|
3
3
|
"description": "Core workspace utilities for KB Labs, including root directory resolution and workspace detection",
|
|
4
|
-
"version": "2.119.
|
|
4
|
+
"version": "2.119.1-canary.a6aa9b388",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"LICENSE"
|
|
19
19
|
],
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@kb-labs/core-sys": "2.119.
|
|
21
|
+
"@kb-labs/core-sys": "2.119.1-canary.a6aa9b388"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@types/node": "^24.3.3",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"tsup": "^8.5.0",
|
|
30
30
|
"typescript": "^5.6.3",
|
|
31
31
|
"vitest": "^3.2.6",
|
|
32
|
-
"@kb-labs/devkit": "2.119.
|
|
32
|
+
"@kb-labs/devkit": "2.119.1-canary.a6aa9b388"
|
|
33
33
|
},
|
|
34
34
|
"engines": {
|
|
35
35
|
"node": ">=24.0.0",
|