mirror of
https://github.com/affaan-m/ECC.git
synced 2026-11-13 19:42:10 +00:00
169 lines
5.4 KiB
JavaScript
169 lines
5.4 KiB
JavaScript
'use strict';
|
|
|
|
const fs = require('fs');
|
|
const os = require('os');
|
|
const path = require('path');
|
|
|
|
const grokHomeTarget = require('./install-targets/grok-home');
|
|
const { SHA_PATTERN } = require('./grok-source-identity');
|
|
|
|
const GROK_PLUGIN_ROOT_ENV = 'GROK_PLUGIN_ROOT';
|
|
const GROK_HOME_DIRNAME = '.grok';
|
|
const SOURCE_IDENTITY_FILE = '.ecc-source.json';
|
|
const CURRENT_PLUGIN_SLUG = 'ecc';
|
|
const DEFAULT_SCRIPT_PROBE = path.join('scripts', 'lib', 'utils.js');
|
|
const DEFAULT_SKILL_PROBE = path.join('skills', 'continuous-learning-v2');
|
|
const SHARED_PLUGIN_ROOT_KEYS = Object.freeze([
|
|
'PLUGIN_ROOT',
|
|
'CLAUDE_PLUGIN_ROOT',
|
|
'ECC_PLUGIN_ROOT',
|
|
]);
|
|
|
|
function trimEnv(value) {
|
|
return value && String(value).trim() ? String(value).trim() : '';
|
|
}
|
|
|
|
function grokPluginRootFromEnv(env = {}) {
|
|
return trimEnv(env[GROK_PLUGIN_ROOT_ENV]);
|
|
}
|
|
|
|
function toSharedPluginEnv(env = {}) {
|
|
const next = { ...env };
|
|
const grokRoot = grokPluginRootFromEnv(env);
|
|
delete next[GROK_PLUGIN_ROOT_ENV];
|
|
if (grokRoot && !SHARED_PLUGIN_ROOT_KEYS.some((key) => trimEnv(next[key]))) {
|
|
next.PLUGIN_ROOT = grokRoot;
|
|
}
|
|
return next;
|
|
}
|
|
|
|
function resolveHomeDir(env = process.env) {
|
|
return trimEnv(env.ECC_GROK_HOME) || os.homedir();
|
|
}
|
|
|
|
function grokHomeDir(homeDir, pathModule = path) {
|
|
return pathModule.join(homeDir, GROK_HOME_DIRNAME);
|
|
}
|
|
|
|
function grokInstallStatePath(homeDir, pathModule = path) {
|
|
if (pathModule === path) return grokHomeTarget.getInstallStatePath({ homeDir });
|
|
return pathModule.join(
|
|
homeDir,
|
|
...grokHomeTarget.rootSegments,
|
|
...grokHomeTarget.installStatePathSegments
|
|
);
|
|
}
|
|
|
|
function readJsonIfPresent(filePath) {
|
|
try {
|
|
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function isCompleteRoot(dir, probe, pathModule = path) {
|
|
if (probe) return fs.existsSync(pathModule.join(dir, probe));
|
|
return fs.existsSync(pathModule.join(dir, DEFAULT_SCRIPT_PROBE))
|
|
&& fs.existsSync(pathModule.join(dir, DEFAULT_SKILL_PROBE));
|
|
}
|
|
|
|
function listCachedGrokVersions(homeDir, pathModule = path) {
|
|
const versions = [];
|
|
const cacheBase = pathModule.join(grokHomeDir(homeDir, pathModule), 'plugins', 'cache', CURRENT_PLUGIN_SLUG);
|
|
try {
|
|
for (const orgEntry of fs.readdirSync(cacheBase, { withFileTypes: true })) {
|
|
if (!orgEntry.isDirectory()) continue;
|
|
const orgPath = pathModule.join(cacheBase, orgEntry.name);
|
|
for (const versionEntry of fs.readdirSync(orgPath, { withFileTypes: true })) {
|
|
if (!versionEntry.isDirectory()) continue;
|
|
const installedRoot = pathModule.join(orgPath, versionEntry.name);
|
|
const identity = readJsonIfPresent(pathModule.join(installedRoot, SOURCE_IDENTITY_FILE));
|
|
versions.push({
|
|
org: orgEntry.name,
|
|
version: versionEntry.name,
|
|
installedRoot,
|
|
sha: identity && identity.sha,
|
|
});
|
|
}
|
|
}
|
|
} catch {
|
|
return versions;
|
|
}
|
|
return versions;
|
|
}
|
|
|
|
function selectPinnedCachedVersion(versions, sha) {
|
|
if (!SHA_PATTERN.test(sha || '')) return null;
|
|
return versions.find((entry) => entry.sha === sha) || null;
|
|
}
|
|
|
|
function isPinnedRoot(root, sha, probe, pathModule = path) {
|
|
if (!SHA_PATTERN.test(sha || '') || !isCompleteRoot(root, probe, pathModule)) return false;
|
|
const identity = readJsonIfPresent(pathModule.join(root, SOURCE_IDENTITY_FILE));
|
|
return Boolean(identity && identity.sha === sha);
|
|
}
|
|
|
|
function findRootInGrokHome(homeDir, probe, pathModule = path) {
|
|
const grokRoot = grokHomeDir(homeDir, pathModule);
|
|
const candidates = [
|
|
pathModule.join(grokRoot, 'plugins', CURRENT_PLUGIN_SLUG),
|
|
pathModule.join(grokRoot, 'plugins', `${CURRENT_PLUGIN_SLUG}@${CURRENT_PLUGIN_SLUG}`),
|
|
pathModule.join(grokRoot, 'plugins', 'marketplaces', CURRENT_PLUGIN_SLUG),
|
|
];
|
|
for (const candidate of candidates) {
|
|
if (isCompleteRoot(candidate, probe, pathModule)) return candidate;
|
|
}
|
|
for (const dirname of ['installed-plugins', 'plugins']) {
|
|
const base = pathModule.join(grokRoot, dirname);
|
|
try {
|
|
for (const entry of fs.readdirSync(base, { withFileTypes: true })) {
|
|
if (!entry.isDirectory()) continue;
|
|
const candidate = pathModule.join(base, entry.name);
|
|
if (isCompleteRoot(candidate, probe, pathModule)) return candidate;
|
|
}
|
|
} catch {
|
|
// Optional native discovery root.
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function resolveGrokPluginRoot(options = {}) {
|
|
const pathModule = options.pathModule || path;
|
|
if (options.enabled === false) return null;
|
|
const pinnedSha = options.pinnedSha || (options.source && options.source.sha) || '';
|
|
const envRoot = options.envRoot !== undefined
|
|
? trimEnv(options.envRoot)
|
|
: grokPluginRootFromEnv(options.env || {});
|
|
if (envRoot) {
|
|
return pinnedSha && !isPinnedRoot(envRoot, pinnedSha, options.probe, pathModule)
|
|
? null
|
|
: envRoot;
|
|
}
|
|
|
|
const homeDir = options.homeDir || os.homedir();
|
|
if (pinnedSha) {
|
|
const pinned = selectPinnedCachedVersion(listCachedGrokVersions(homeDir, pathModule), pinnedSha);
|
|
if (pinned && isCompleteRoot(pinned.installedRoot, options.probe, pathModule)) {
|
|
return pinned.installedRoot;
|
|
}
|
|
return null;
|
|
}
|
|
return findRootInGrokHome(homeDir, options.probe, pathModule);
|
|
}
|
|
|
|
module.exports = {
|
|
GROK_PLUGIN_ROOT_ENV,
|
|
GROK_HOME_DIRNAME,
|
|
SOURCE_IDENTITY_FILE,
|
|
grokPluginRootFromEnv,
|
|
toSharedPluginEnv,
|
|
resolveHomeDir,
|
|
grokHomeDir,
|
|
grokInstallStatePath,
|
|
resolveGrokPluginRoot,
|
|
listCachedGrokVersions,
|
|
selectPinnedCachedVersion,
|
|
};
|