monomind 1.15.6 → 1.16.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/.claude/agents/github/repo-architect.md +1 -1
- package/.claude/agents/specialists/integration-architect.md +6 -6
- package/.claude/commands/hive-mind/hive-mind-init.md +1 -1
- package/.claude/commands/hive-mind/hive-mind-memory.md +1 -1
- package/.claude/commands/mastermind/brain.md +11 -11
- package/.claude/commands/mastermind/master.md +4 -4
- package/.claude/commands/mastermind/memory.md +6 -6
- package/.claude/commands/memory/README.md +4 -4
- package/.claude/commands/truth/start.md +3 -3
- package/.claude/helpers/extras-registry.json +2 -2
- package/.claude/helpers/skill-registry.json +26 -26
- package/.claude/helpers/statusline.cjs +8 -8
- package/.claude/skills/agentic-jujutsu/SKILL.md +3 -3
- package/.claude/skills/mastermind/_protocol.md +8 -8
- package/README.md +6 -6
- package/package.json +2 -2
- package/packages/@monomind/cli/README.md +6 -6
- package/packages/@monomind/cli/dist/src/__tests__/browse-analyzer.test.js +18 -1
- package/packages/@monomind/cli/dist/src/commands/agent.js +2 -2
- package/packages/@monomind/cli/dist/src/commands/autopilot.js +1 -1
- package/packages/@monomind/cli/dist/src/commands/completions.js +2 -21
- package/packages/@monomind/cli/dist/src/commands/config.js +1 -1
- package/packages/@monomind/cli/dist/src/commands/hive-mind.js +1 -1
- package/packages/@monomind/cli/dist/src/commands/hooks-coverage-commands.js +31 -31
- package/packages/@monomind/cli/dist/src/commands/hooks-routing-commands.js +1 -1
- package/packages/@monomind/cli/dist/src/commands/hooks.js +1 -1
- package/packages/@monomind/cli/dist/src/commands/index.d.ts +0 -1
- package/packages/@monomind/cli/dist/src/commands/index.js +0 -4
- package/packages/@monomind/cli/dist/src/commands/init.js +8 -8
- package/packages/@monomind/cli/dist/src/commands/memory.d.ts +1 -1
- package/packages/@monomind/cli/dist/src/commands/memory.js +138 -28
- package/packages/@monomind/cli/dist/src/commands/migrate.js +2 -2
- package/packages/@monomind/cli/dist/src/commands/neural.js +1 -1
- package/packages/@monomind/cli/dist/src/commands/swarm.js +1 -1
- package/packages/@monomind/cli/dist/src/config-adapter.js +8 -8
- package/packages/@monomind/cli/dist/src/index.js +1 -1
- package/packages/@monomind/cli/dist/src/init/claudemd-generator.js +2 -2
- package/packages/@monomind/cli/dist/src/init/executor.js +16 -16
- package/packages/@monomind/cli/dist/src/init/shared-instructions-generator.d.ts +1 -1
- package/packages/@monomind/cli/dist/src/init/shared-instructions-generator.js +1 -1
- package/packages/@monomind/cli/dist/src/init/statusline-generator.d.ts +1 -1
- package/packages/@monomind/cli/dist/src/init/statusline-generator.js +8 -8
- package/packages/@monomind/cli/dist/src/init/types.d.ts +3 -3
- package/packages/@monomind/cli/dist/src/init/types.js +3 -3
- package/packages/@monomind/cli/dist/src/mcp-client.js +1 -8
- package/packages/@monomind/cli/dist/src/mcp-tools/autopilot-tools.js +3 -3
- package/packages/@monomind/cli/dist/src/mcp-tools/daa-tools.js +13 -13
- package/packages/@monomind/cli/dist/src/mcp-tools/guidance-tools.js +4 -4
- package/packages/@monomind/cli/dist/src/mcp-tools/hive-mind-tools.js +4 -4
- package/packages/@monomind/cli/dist/src/mcp-tools/hooks-intelligence.js +1 -0
- package/packages/@monomind/cli/dist/src/mcp-tools/hooks-routing.js +23 -23
- package/packages/@monomind/cli/dist/src/mcp-tools/index.d.ts +0 -1
- package/packages/@monomind/cli/dist/src/mcp-tools/index.js +0 -2
- package/packages/@monomind/cli/dist/src/mcp-tools/memory-tools.d.ts +22 -6
- package/packages/@monomind/cli/dist/src/mcp-tools/memory-tools.js +553 -505
- package/packages/@monomind/cli/dist/src/mcp-tools/progress-tools.js +1 -1
- package/packages/@monomind/cli/dist/src/mcp-tools/system-tools.js +5 -5
- package/packages/@monomind/cli/dist/src/mcp-tools/transfer-tools.d.ts +1 -1
- package/packages/@monomind/cli/dist/src/mcp-tools/transfer-tools.js +1 -156
- package/packages/@monomind/cli/dist/src/memory/embedding-operations.js +3 -3
- package/packages/@monomind/cli/dist/src/memory/hnsw-operations.js +5 -5
- package/packages/@monomind/cli/dist/src/memory/intelligence.js +2 -2
- package/packages/@monomind/cli/dist/src/memory/memory-bridge.d.ts +86 -234
- package/packages/@monomind/cli/dist/src/memory/memory-bridge.js +455 -1702
- package/packages/@monomind/cli/dist/src/memory/memory-crud.js +3 -3
- package/packages/@monomind/cli/dist/src/memory/memory-initializer.d.ts +1 -1
- package/packages/@monomind/cli/dist/src/memory/memory-initializer.js +5 -5
- package/packages/@monomind/cli/dist/src/memory/memory-read.js +4 -4
- package/packages/@monomind/cli/dist/src/suggest.js +0 -1
- package/packages/@monomind/cli/dist/src/types.d.ts +1 -1
- package/packages/@monomind/cli/dist/src/ui/dashboard.html +41 -5
- package/packages/@monomind/cli/dist/src/ui/orgs.html +91 -5
- package/packages/@monomind/cli/dist/src/ui/server.mjs +44 -0
- package/packages/@monomind/cli/dist/src/update/validator.js +1 -3
- package/packages/@monomind/cli/package.json +4 -4
- package/scripts/verify-appliance.sh +1 -1
- package/packages/@monomind/cli/dist/src/commands/plugins.d.ts +0 -11
- package/packages/@monomind/cli/dist/src/commands/plugins.js +0 -799
- package/packages/@monomind/cli/dist/src/plugins/manager.d.ts +0 -133
- package/packages/@monomind/cli/dist/src/plugins/manager.js +0 -498
- package/packages/@monomind/cli/dist/src/plugins/store/discovery.d.ts +0 -88
- package/packages/@monomind/cli/dist/src/plugins/store/discovery.js +0 -650
- package/packages/@monomind/cli/dist/src/plugins/store/index.d.ts +0 -76
- package/packages/@monomind/cli/dist/src/plugins/store/index.js +0 -141
- package/packages/@monomind/cli/dist/src/plugins/store/search.d.ts +0 -46
- package/packages/@monomind/cli/dist/src/plugins/store/search.js +0 -231
- package/packages/@monomind/cli/dist/src/plugins/store/types.d.ts +0 -274
- package/packages/@monomind/cli/dist/src/plugins/store/types.js +0 -7
- package/packages/@monomind/cli/dist/src/plugins/tests/demo-plugin-store.d.ts +0 -7
- package/packages/@monomind/cli/dist/src/plugins/tests/demo-plugin-store.js +0 -126
- package/packages/@monomind/cli/dist/src/plugins/tests/standalone-test.d.ts +0 -12
- package/packages/@monomind/cli/dist/src/plugins/tests/standalone-test.js +0 -188
- package/packages/@monomind/cli/dist/src/plugins/tests/test-plugin-store.d.ts +0 -7
- package/packages/@monomind/cli/dist/src/plugins/tests/test-plugin-store.js +0 -206
- package/packages/@monomind/cli/dist/src/services/registry-api.d.ts +0 -58
- package/packages/@monomind/cli/dist/src/services/registry-api.js +0 -199
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Plugin Manager
|
|
3
|
-
* Handles actual plugin installation, persistence, and lifecycle
|
|
4
|
-
* Bridges discovery service with file system persistence
|
|
5
|
-
*/
|
|
6
|
-
export interface InstalledPlugin {
|
|
7
|
-
name: string;
|
|
8
|
-
version: string;
|
|
9
|
-
installedAt: string;
|
|
10
|
-
enabled: boolean;
|
|
11
|
-
source: 'npm' | 'local' | 'ipfs';
|
|
12
|
-
path?: string;
|
|
13
|
-
commands?: string[];
|
|
14
|
-
hooks?: string[];
|
|
15
|
-
config?: Record<string, unknown>;
|
|
16
|
-
}
|
|
17
|
-
export interface InstalledPluginsManifest {
|
|
18
|
-
version: '1.0.0';
|
|
19
|
-
lastUpdated: string;
|
|
20
|
-
plugins: Record<string, InstalledPlugin>;
|
|
21
|
-
}
|
|
22
|
-
export interface PluginManagerConfig {
|
|
23
|
-
pluginsDir: string;
|
|
24
|
-
manifestPath: string;
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Manages plugin installation, persistence, and lifecycle.
|
|
28
|
-
*
|
|
29
|
-
* Unlike the simulated version, this actually:
|
|
30
|
-
* - Persists plugins to disk
|
|
31
|
-
* - Downloads from npm
|
|
32
|
-
* - Tracks enabled/disabled state
|
|
33
|
-
* - Loads plugin modules
|
|
34
|
-
*/
|
|
35
|
-
export declare class PluginManager {
|
|
36
|
-
private config;
|
|
37
|
-
private manifest;
|
|
38
|
-
constructor(baseDir?: string);
|
|
39
|
-
/**
|
|
40
|
-
* Initialize the plugin manager, creating directories and loading manifest
|
|
41
|
-
*/
|
|
42
|
-
initialize(): Promise<void>;
|
|
43
|
-
private ensureDirectory;
|
|
44
|
-
private loadManifest;
|
|
45
|
-
private saveManifest;
|
|
46
|
-
/**
|
|
47
|
-
* Install a plugin from npm
|
|
48
|
-
*/
|
|
49
|
-
installFromNpm(packageName: string, version?: string): Promise<{
|
|
50
|
-
success: boolean;
|
|
51
|
-
error?: string;
|
|
52
|
-
plugin?: InstalledPlugin;
|
|
53
|
-
}>;
|
|
54
|
-
/**
|
|
55
|
-
* Install a plugin from a local path
|
|
56
|
-
*/
|
|
57
|
-
installFromLocal(sourcePath: string): Promise<{
|
|
58
|
-
success: boolean;
|
|
59
|
-
error?: string;
|
|
60
|
-
plugin?: InstalledPlugin;
|
|
61
|
-
}>;
|
|
62
|
-
/**
|
|
63
|
-
* Uninstall a plugin
|
|
64
|
-
*/
|
|
65
|
-
uninstall(packageName: string): Promise<{
|
|
66
|
-
success: boolean;
|
|
67
|
-
error?: string;
|
|
68
|
-
}>;
|
|
69
|
-
/**
|
|
70
|
-
* Enable a plugin
|
|
71
|
-
*/
|
|
72
|
-
enable(packageName: string): Promise<{
|
|
73
|
-
success: boolean;
|
|
74
|
-
error?: string;
|
|
75
|
-
}>;
|
|
76
|
-
/**
|
|
77
|
-
* Disable a plugin
|
|
78
|
-
*/
|
|
79
|
-
disable(packageName: string): Promise<{
|
|
80
|
-
success: boolean;
|
|
81
|
-
error?: string;
|
|
82
|
-
}>;
|
|
83
|
-
/**
|
|
84
|
-
* Toggle a plugin's enabled state
|
|
85
|
-
*/
|
|
86
|
-
toggle(packageName: string): Promise<{
|
|
87
|
-
success: boolean;
|
|
88
|
-
enabled?: boolean;
|
|
89
|
-
error?: string;
|
|
90
|
-
}>;
|
|
91
|
-
/**
|
|
92
|
-
* Get all installed plugins
|
|
93
|
-
*/
|
|
94
|
-
getInstalled(): Promise<InstalledPlugin[]>;
|
|
95
|
-
/**
|
|
96
|
-
* Get enabled plugins
|
|
97
|
-
*/
|
|
98
|
-
getEnabled(): Promise<InstalledPlugin[]>;
|
|
99
|
-
/**
|
|
100
|
-
* Check if a plugin is installed
|
|
101
|
-
*/
|
|
102
|
-
isInstalled(packageName: string): Promise<boolean>;
|
|
103
|
-
/**
|
|
104
|
-
* Get a specific installed plugin
|
|
105
|
-
*/
|
|
106
|
-
getPlugin(packageName: string): Promise<InstalledPlugin | undefined>;
|
|
107
|
-
/**
|
|
108
|
-
* Upgrade a plugin to a new version
|
|
109
|
-
*/
|
|
110
|
-
upgrade(packageName: string, version?: string): Promise<{
|
|
111
|
-
success: boolean;
|
|
112
|
-
error?: string;
|
|
113
|
-
plugin?: InstalledPlugin;
|
|
114
|
-
}>;
|
|
115
|
-
/**
|
|
116
|
-
* Update plugin config
|
|
117
|
-
*/
|
|
118
|
-
setConfig(packageName: string, config: Record<string, unknown>): Promise<{
|
|
119
|
-
success: boolean;
|
|
120
|
-
error?: string;
|
|
121
|
-
}>;
|
|
122
|
-
/**
|
|
123
|
-
* Get plugins directory path
|
|
124
|
-
*/
|
|
125
|
-
getPluginsDir(): string;
|
|
126
|
-
/**
|
|
127
|
-
* Get manifest path
|
|
128
|
-
*/
|
|
129
|
-
getManifestPath(): string;
|
|
130
|
-
}
|
|
131
|
-
export declare function getPluginManager(baseDir?: string): PluginManager;
|
|
132
|
-
export declare function resetPluginManager(): void;
|
|
133
|
-
//# sourceMappingURL=manager.d.ts.map
|
|
@@ -1,498 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Plugin Manager
|
|
3
|
-
* Handles actual plugin installation, persistence, and lifecycle
|
|
4
|
-
* Bridges discovery service with file system persistence
|
|
5
|
-
*/
|
|
6
|
-
import * as fs from 'fs';
|
|
7
|
-
import * as path from 'path';
|
|
8
|
-
import { execFile } from 'child_process';
|
|
9
|
-
import { promisify } from 'util';
|
|
10
|
-
const execFileAsync = promisify(execFile);
|
|
11
|
-
/**
|
|
12
|
-
* Validate npm package name to prevent shell injection (S-3)
|
|
13
|
-
*/
|
|
14
|
-
const VALID_PACKAGE_RE = /^(@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*(@[a-z0-9._\-^~>=<]+)?$/;
|
|
15
|
-
function validatePackageName(spec) {
|
|
16
|
-
if (!VALID_PACKAGE_RE.test(spec)) {
|
|
17
|
-
throw new Error(`Invalid package name: ${spec}`);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
const VALID_VERSION_RE = /^[a-zA-Z0-9._\-^~>=<*]+$/;
|
|
21
|
-
function validateVersion(version) {
|
|
22
|
-
if (!VALID_VERSION_RE.test(version) || version.length > 50) {
|
|
23
|
-
throw new Error(`Invalid version specifier: ${version}`);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
/** Forbidden manifest keys (prototype pollution defense) */
|
|
27
|
-
const FORBIDDEN_PLUGIN_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
|
|
28
|
-
function isValidPluginKey(name) {
|
|
29
|
-
return typeof name === 'string'
|
|
30
|
-
&& name.length > 0
|
|
31
|
-
&& name.length <= 214
|
|
32
|
-
&& !FORBIDDEN_PLUGIN_KEYS.has(name)
|
|
33
|
-
&& VALID_PACKAGE_RE.test(name);
|
|
34
|
-
}
|
|
35
|
-
// ============================================================================
|
|
36
|
-
// Plugin Manager
|
|
37
|
-
// ============================================================================
|
|
38
|
-
/**
|
|
39
|
-
* Manages plugin installation, persistence, and lifecycle.
|
|
40
|
-
*
|
|
41
|
-
* Unlike the simulated version, this actually:
|
|
42
|
-
* - Persists plugins to disk
|
|
43
|
-
* - Downloads from npm
|
|
44
|
-
* - Tracks enabled/disabled state
|
|
45
|
-
* - Loads plugin modules
|
|
46
|
-
*/
|
|
47
|
-
export class PluginManager {
|
|
48
|
-
config;
|
|
49
|
-
manifest = null;
|
|
50
|
-
constructor(baseDir = process.cwd()) {
|
|
51
|
-
const pluginsDir = path.join(baseDir, '.monomind', 'plugins');
|
|
52
|
-
this.config = {
|
|
53
|
-
pluginsDir,
|
|
54
|
-
manifestPath: path.join(pluginsDir, 'installed.json'),
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
// =========================================================================
|
|
58
|
-
// Initialization
|
|
59
|
-
// =========================================================================
|
|
60
|
-
/**
|
|
61
|
-
* Initialize the plugin manager, creating directories and loading manifest
|
|
62
|
-
*/
|
|
63
|
-
async initialize() {
|
|
64
|
-
// Ensure plugins directory exists
|
|
65
|
-
await this.ensureDirectory(this.config.pluginsDir);
|
|
66
|
-
// Load or create manifest
|
|
67
|
-
this.manifest = await this.loadManifest();
|
|
68
|
-
}
|
|
69
|
-
async ensureDirectory(dir) {
|
|
70
|
-
if (!fs.existsSync(dir)) {
|
|
71
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
async loadManifest() {
|
|
75
|
-
try {
|
|
76
|
-
const MAX_MANIFEST_BYTES = 10 * 1024 * 1024; // 10 MB
|
|
77
|
-
if (fs.existsSync(this.config.manifestPath) && fs.statSync(this.config.manifestPath).size <= MAX_MANIFEST_BYTES) {
|
|
78
|
-
const content = fs.readFileSync(this.config.manifestPath, 'utf-8');
|
|
79
|
-
const parsed = JSON.parse(content);
|
|
80
|
-
if (Object.prototype.hasOwnProperty.call(parsed, '__proto__') ||
|
|
81
|
-
Object.prototype.hasOwnProperty.call(parsed, 'constructor') ||
|
|
82
|
-
Object.prototype.hasOwnProperty.call(parsed, 'prototype')) {
|
|
83
|
-
throw new Error('Manifest contains forbidden keys');
|
|
84
|
-
}
|
|
85
|
-
return parsed;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
catch (error) {
|
|
89
|
-
console.warn('[PluginManager] Failed to load manifest, creating new one');
|
|
90
|
-
}
|
|
91
|
-
return {
|
|
92
|
-
version: '1.0.0',
|
|
93
|
-
lastUpdated: new Date().toISOString(),
|
|
94
|
-
plugins: {},
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
async saveManifest() {
|
|
98
|
-
if (!this.manifest)
|
|
99
|
-
return;
|
|
100
|
-
this.manifest.lastUpdated = new Date().toISOString();
|
|
101
|
-
await this.ensureDirectory(path.dirname(this.config.manifestPath));
|
|
102
|
-
// Atomic write to prevent corruption on crash
|
|
103
|
-
const tmp = this.config.manifestPath + '.tmp';
|
|
104
|
-
fs.writeFileSync(tmp, JSON.stringify(this.manifest, null, 2), 'utf-8');
|
|
105
|
-
fs.renameSync(tmp, this.config.manifestPath);
|
|
106
|
-
}
|
|
107
|
-
// =========================================================================
|
|
108
|
-
// Installation
|
|
109
|
-
// =========================================================================
|
|
110
|
-
/**
|
|
111
|
-
* Install a plugin from npm
|
|
112
|
-
*/
|
|
113
|
-
async installFromNpm(packageName, version) {
|
|
114
|
-
if (!this.manifest) {
|
|
115
|
-
await this.initialize();
|
|
116
|
-
}
|
|
117
|
-
if (!isValidPluginKey(packageName)) {
|
|
118
|
-
return { success: false, error: `Invalid package name: ${packageName}` };
|
|
119
|
-
}
|
|
120
|
-
if (version) {
|
|
121
|
-
try {
|
|
122
|
-
validateVersion(version);
|
|
123
|
-
}
|
|
124
|
-
catch {
|
|
125
|
-
return { success: false, error: `Invalid version specifier: ${version}` };
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
const versionSpec = version ? `${packageName}@${version}` : packageName;
|
|
129
|
-
try {
|
|
130
|
-
// Check if already installed
|
|
131
|
-
if (Object.hasOwn(this.manifest.plugins, packageName)) {
|
|
132
|
-
return {
|
|
133
|
-
success: false,
|
|
134
|
-
error: `Plugin ${packageName} is already installed. Use upgrade to update.`,
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
// Install to local plugins directory
|
|
138
|
-
const installDir = path.join(this.config.pluginsDir, 'node_modules');
|
|
139
|
-
await this.ensureDirectory(installDir);
|
|
140
|
-
// Validate package name to prevent injection (S-3)
|
|
141
|
-
validatePackageName(versionSpec);
|
|
142
|
-
// Use npm to install. --ignore-scripts blocks pre/post-install lifecycle hooks
|
|
143
|
-
// from the plugin package, which would otherwise execute arbitrary code at
|
|
144
|
-
// install time (the canonical npm supply-chain attack vector).
|
|
145
|
-
console.log(`[PluginManager] Installing ${versionSpec}...`);
|
|
146
|
-
await execFileAsync('npm', ['install', '--ignore-scripts', '--prefix', this.config.pluginsDir, versionSpec], { timeout: 120000 });
|
|
147
|
-
// Get installed version
|
|
148
|
-
const packageJsonPath = path.join(installDir, packageName, 'package.json');
|
|
149
|
-
let installedVersion = version || 'latest';
|
|
150
|
-
let commands = [];
|
|
151
|
-
let hooks = [];
|
|
152
|
-
const MAX_PKG_JSON_BYTES = 1024 * 1024; // 1 MB
|
|
153
|
-
if (fs.existsSync(packageJsonPath) && fs.statSync(packageJsonPath).size <= MAX_PKG_JSON_BYTES) {
|
|
154
|
-
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
155
|
-
installedVersion = pkg.version;
|
|
156
|
-
// Check for monomind plugin metadata
|
|
157
|
-
if (pkg['monomind']) {
|
|
158
|
-
commands = Array.isArray(pkg['monomind'].commands) ? pkg['monomind'].commands : [];
|
|
159
|
-
hooks = Array.isArray(pkg['monomind'].hooks) ? pkg['monomind'].hooks : [];
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
// Create plugin entry
|
|
163
|
-
const plugin = {
|
|
164
|
-
name: packageName,
|
|
165
|
-
version: installedVersion,
|
|
166
|
-
installedAt: new Date().toISOString(),
|
|
167
|
-
enabled: true,
|
|
168
|
-
source: 'npm',
|
|
169
|
-
path: path.join(installDir, packageName),
|
|
170
|
-
commands,
|
|
171
|
-
hooks,
|
|
172
|
-
};
|
|
173
|
-
// Save to manifest
|
|
174
|
-
this.manifest.plugins[packageName] = plugin;
|
|
175
|
-
await this.saveManifest();
|
|
176
|
-
console.log(`[PluginManager] Installed ${packageName}@${installedVersion}`);
|
|
177
|
-
return { success: true, plugin };
|
|
178
|
-
}
|
|
179
|
-
catch (error) {
|
|
180
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
181
|
-
console.error(`[PluginManager] Failed to install ${packageName}:`, errorMsg);
|
|
182
|
-
return { success: false, error: errorMsg };
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
/**
|
|
186
|
-
* Install a plugin from a local path
|
|
187
|
-
*/
|
|
188
|
-
async installFromLocal(sourcePath) {
|
|
189
|
-
if (!this.manifest) {
|
|
190
|
-
await this.initialize();
|
|
191
|
-
}
|
|
192
|
-
try {
|
|
193
|
-
const absolutePath = path.resolve(sourcePath);
|
|
194
|
-
// Restrict local installs to paths under cwd or $HOME to prevent path traversal
|
|
195
|
-
const cwd = process.cwd();
|
|
196
|
-
const home = process.env.HOME ?? process.env.USERPROFILE ?? '';
|
|
197
|
-
const underCwd = absolutePath.startsWith(cwd + path.sep) || absolutePath === cwd;
|
|
198
|
-
const underHome = home && (absolutePath.startsWith(home + path.sep) || absolutePath === home);
|
|
199
|
-
if (!underCwd && !underHome) {
|
|
200
|
-
return { success: false, error: `Local path must be within the current directory or home: ${absolutePath}` };
|
|
201
|
-
}
|
|
202
|
-
if (!fs.existsSync(absolutePath)) {
|
|
203
|
-
return { success: false, error: `Path does not exist: ${absolutePath}` };
|
|
204
|
-
}
|
|
205
|
-
// Read package.json
|
|
206
|
-
const packageJsonPath = path.join(absolutePath, 'package.json');
|
|
207
|
-
if (!fs.existsSync(packageJsonPath)) {
|
|
208
|
-
return { success: false, error: 'No package.json found at path' };
|
|
209
|
-
}
|
|
210
|
-
if (fs.statSync(packageJsonPath).size > 1024 * 1024) {
|
|
211
|
-
return { success: false, error: 'package.json exceeds size limit' };
|
|
212
|
-
}
|
|
213
|
-
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
214
|
-
const packageName = pkg.name;
|
|
215
|
-
if (!isValidPluginKey(packageName)) {
|
|
216
|
-
return { success: false, error: `Invalid package.json: name is missing or invalid` };
|
|
217
|
-
}
|
|
218
|
-
// Check if already installed
|
|
219
|
-
if (Object.hasOwn(this.manifest.plugins, packageName)) {
|
|
220
|
-
return {
|
|
221
|
-
success: false,
|
|
222
|
-
error: `Plugin ${packageName} is already installed`,
|
|
223
|
-
};
|
|
224
|
-
}
|
|
225
|
-
// Create plugin entry (link to local path, don't copy)
|
|
226
|
-
const plugin = {
|
|
227
|
-
name: packageName,
|
|
228
|
-
version: pkg.version,
|
|
229
|
-
installedAt: new Date().toISOString(),
|
|
230
|
-
enabled: true,
|
|
231
|
-
source: 'local',
|
|
232
|
-
path: absolutePath,
|
|
233
|
-
commands: pkg['monomind']?.commands || [],
|
|
234
|
-
hooks: pkg['monomind']?.hooks || [],
|
|
235
|
-
};
|
|
236
|
-
// Save to manifest
|
|
237
|
-
this.manifest.plugins[packageName] = plugin;
|
|
238
|
-
await this.saveManifest();
|
|
239
|
-
console.log(`[PluginManager] Installed local plugin ${packageName}@${pkg.version}`);
|
|
240
|
-
return { success: true, plugin };
|
|
241
|
-
}
|
|
242
|
-
catch (error) {
|
|
243
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
244
|
-
console.error(`[PluginManager] Failed to install from local:`, errorMsg);
|
|
245
|
-
return { success: false, error: errorMsg };
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
// =========================================================================
|
|
249
|
-
// Uninstallation
|
|
250
|
-
// =========================================================================
|
|
251
|
-
/**
|
|
252
|
-
* Uninstall a plugin
|
|
253
|
-
*/
|
|
254
|
-
async uninstall(packageName) {
|
|
255
|
-
if (!this.manifest) {
|
|
256
|
-
await this.initialize();
|
|
257
|
-
}
|
|
258
|
-
if (!isValidPluginKey(packageName)) {
|
|
259
|
-
return { success: false, error: `Invalid package name` };
|
|
260
|
-
}
|
|
261
|
-
const plugin = Object.hasOwn(this.manifest.plugins, packageName)
|
|
262
|
-
? this.manifest.plugins[packageName]
|
|
263
|
-
: undefined;
|
|
264
|
-
if (!plugin) {
|
|
265
|
-
return { success: false, error: `Plugin ${packageName} is not installed` };
|
|
266
|
-
}
|
|
267
|
-
try {
|
|
268
|
-
// For npm-installed plugins, remove from node_modules
|
|
269
|
-
if (plugin.source === 'npm') {
|
|
270
|
-
validatePackageName(packageName);
|
|
271
|
-
await execFileAsync('npm', ['uninstall', '--ignore-scripts', '--prefix', this.config.pluginsDir, packageName], { timeout: 60000 });
|
|
272
|
-
}
|
|
273
|
-
// Remove from manifest
|
|
274
|
-
delete this.manifest.plugins[packageName];
|
|
275
|
-
await this.saveManifest();
|
|
276
|
-
console.log(`[PluginManager] Uninstalled ${packageName}`);
|
|
277
|
-
return { success: true };
|
|
278
|
-
}
|
|
279
|
-
catch (error) {
|
|
280
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
281
|
-
console.error(`[PluginManager] Failed to uninstall ${packageName}:`, errorMsg);
|
|
282
|
-
return { success: false, error: errorMsg };
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
// =========================================================================
|
|
286
|
-
// Enable/Disable
|
|
287
|
-
// =========================================================================
|
|
288
|
-
/**
|
|
289
|
-
* Enable a plugin
|
|
290
|
-
*/
|
|
291
|
-
async enable(packageName) {
|
|
292
|
-
if (!this.manifest) {
|
|
293
|
-
await this.initialize();
|
|
294
|
-
}
|
|
295
|
-
if (!isValidPluginKey(packageName)) {
|
|
296
|
-
return { success: false, error: `Invalid package name` };
|
|
297
|
-
}
|
|
298
|
-
const plugin = Object.hasOwn(this.manifest.plugins, packageName)
|
|
299
|
-
? this.manifest.plugins[packageName]
|
|
300
|
-
: undefined;
|
|
301
|
-
if (!plugin) {
|
|
302
|
-
return { success: false, error: `Plugin ${packageName} is not installed` };
|
|
303
|
-
}
|
|
304
|
-
plugin.enabled = true;
|
|
305
|
-
await this.saveManifest();
|
|
306
|
-
return { success: true };
|
|
307
|
-
}
|
|
308
|
-
/**
|
|
309
|
-
* Disable a plugin
|
|
310
|
-
*/
|
|
311
|
-
async disable(packageName) {
|
|
312
|
-
if (!this.manifest) {
|
|
313
|
-
await this.initialize();
|
|
314
|
-
}
|
|
315
|
-
if (!isValidPluginKey(packageName)) {
|
|
316
|
-
return { success: false, error: `Invalid package name` };
|
|
317
|
-
}
|
|
318
|
-
const plugin = Object.hasOwn(this.manifest.plugins, packageName)
|
|
319
|
-
? this.manifest.plugins[packageName]
|
|
320
|
-
: undefined;
|
|
321
|
-
if (!plugin) {
|
|
322
|
-
return { success: false, error: `Plugin ${packageName} is not installed` };
|
|
323
|
-
}
|
|
324
|
-
plugin.enabled = false;
|
|
325
|
-
await this.saveManifest();
|
|
326
|
-
return { success: true };
|
|
327
|
-
}
|
|
328
|
-
/**
|
|
329
|
-
* Toggle a plugin's enabled state
|
|
330
|
-
*/
|
|
331
|
-
async toggle(packageName) {
|
|
332
|
-
if (!this.manifest) {
|
|
333
|
-
await this.initialize();
|
|
334
|
-
}
|
|
335
|
-
if (!isValidPluginKey(packageName)) {
|
|
336
|
-
return { success: false, error: `Invalid package name` };
|
|
337
|
-
}
|
|
338
|
-
const plugin = Object.hasOwn(this.manifest.plugins, packageName)
|
|
339
|
-
? this.manifest.plugins[packageName]
|
|
340
|
-
: undefined;
|
|
341
|
-
if (!plugin) {
|
|
342
|
-
return { success: false, error: `Plugin ${packageName} is not installed` };
|
|
343
|
-
}
|
|
344
|
-
plugin.enabled = !plugin.enabled;
|
|
345
|
-
await this.saveManifest();
|
|
346
|
-
return { success: true, enabled: plugin.enabled };
|
|
347
|
-
}
|
|
348
|
-
// =========================================================================
|
|
349
|
-
// Query
|
|
350
|
-
// =========================================================================
|
|
351
|
-
/**
|
|
352
|
-
* Get all installed plugins
|
|
353
|
-
*/
|
|
354
|
-
async getInstalled() {
|
|
355
|
-
if (!this.manifest) {
|
|
356
|
-
await this.initialize();
|
|
357
|
-
}
|
|
358
|
-
return Object.values(this.manifest.plugins);
|
|
359
|
-
}
|
|
360
|
-
/**
|
|
361
|
-
* Get enabled plugins
|
|
362
|
-
*/
|
|
363
|
-
async getEnabled() {
|
|
364
|
-
const all = await this.getInstalled();
|
|
365
|
-
return all.filter(p => p.enabled);
|
|
366
|
-
}
|
|
367
|
-
/**
|
|
368
|
-
* Check if a plugin is installed
|
|
369
|
-
*/
|
|
370
|
-
async isInstalled(packageName) {
|
|
371
|
-
if (!this.manifest) {
|
|
372
|
-
await this.initialize();
|
|
373
|
-
}
|
|
374
|
-
if (!isValidPluginKey(packageName))
|
|
375
|
-
return false;
|
|
376
|
-
return Object.hasOwn(this.manifest.plugins, packageName);
|
|
377
|
-
}
|
|
378
|
-
/**
|
|
379
|
-
* Get a specific installed plugin
|
|
380
|
-
*/
|
|
381
|
-
async getPlugin(packageName) {
|
|
382
|
-
if (!this.manifest) {
|
|
383
|
-
await this.initialize();
|
|
384
|
-
}
|
|
385
|
-
if (!isValidPluginKey(packageName))
|
|
386
|
-
return undefined;
|
|
387
|
-
return Object.hasOwn(this.manifest.plugins, packageName)
|
|
388
|
-
? this.manifest.plugins[packageName]
|
|
389
|
-
: undefined;
|
|
390
|
-
}
|
|
391
|
-
// =========================================================================
|
|
392
|
-
// Upgrade
|
|
393
|
-
// =========================================================================
|
|
394
|
-
/**
|
|
395
|
-
* Upgrade a plugin to a new version
|
|
396
|
-
*/
|
|
397
|
-
async upgrade(packageName, version) {
|
|
398
|
-
if (!this.manifest) {
|
|
399
|
-
await this.initialize();
|
|
400
|
-
}
|
|
401
|
-
if (!isValidPluginKey(packageName)) {
|
|
402
|
-
return { success: false, error: `Invalid package name` };
|
|
403
|
-
}
|
|
404
|
-
if (version) {
|
|
405
|
-
try {
|
|
406
|
-
validateVersion(version);
|
|
407
|
-
}
|
|
408
|
-
catch {
|
|
409
|
-
return { success: false, error: `Invalid version specifier: ${version}` };
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
const existing = Object.hasOwn(this.manifest.plugins, packageName)
|
|
413
|
-
? this.manifest.plugins[packageName]
|
|
414
|
-
: undefined;
|
|
415
|
-
if (!existing) {
|
|
416
|
-
return { success: false, error: `Plugin ${packageName} is not installed` };
|
|
417
|
-
}
|
|
418
|
-
if (existing.source !== 'npm') {
|
|
419
|
-
return { success: false, error: 'Can only upgrade npm-installed plugins' };
|
|
420
|
-
}
|
|
421
|
-
try {
|
|
422
|
-
const versionSpec = version ? `${packageName}@${version}` : `${packageName}@latest`;
|
|
423
|
-
// Validate package name to prevent injection (S-3)
|
|
424
|
-
validatePackageName(versionSpec);
|
|
425
|
-
// Reinstall with new version. --ignore-scripts blocks pre/post-install
|
|
426
|
-
// lifecycle hooks from the plugin package.
|
|
427
|
-
await execFileAsync('npm', ['install', '--ignore-scripts', '--prefix', this.config.pluginsDir, versionSpec], { timeout: 120000 });
|
|
428
|
-
// Update manifest
|
|
429
|
-
const installDir = path.join(this.config.pluginsDir, 'node_modules');
|
|
430
|
-
const packageJsonPath = path.join(installDir, packageName, 'package.json');
|
|
431
|
-
if (fs.existsSync(packageJsonPath) && fs.statSync(packageJsonPath).size <= 1024 * 1024) {
|
|
432
|
-
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
433
|
-
existing.version = pkg.version;
|
|
434
|
-
existing.commands = pkg['monomind']?.commands || existing.commands;
|
|
435
|
-
existing.hooks = pkg['monomind']?.hooks || existing.hooks;
|
|
436
|
-
}
|
|
437
|
-
await this.saveManifest();
|
|
438
|
-
console.log(`[PluginManager] Upgraded ${packageName} to ${existing.version}`);
|
|
439
|
-
return { success: true, plugin: existing };
|
|
440
|
-
}
|
|
441
|
-
catch (error) {
|
|
442
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
443
|
-
return { success: false, error: errorMsg };
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
// =========================================================================
|
|
447
|
-
// Config
|
|
448
|
-
// =========================================================================
|
|
449
|
-
/**
|
|
450
|
-
* Update plugin config
|
|
451
|
-
*/
|
|
452
|
-
async setConfig(packageName, config) {
|
|
453
|
-
if (!this.manifest) {
|
|
454
|
-
await this.initialize();
|
|
455
|
-
}
|
|
456
|
-
if (!isValidPluginKey(packageName)) {
|
|
457
|
-
return { success: false, error: `Invalid package name` };
|
|
458
|
-
}
|
|
459
|
-
const plugin = Object.hasOwn(this.manifest.plugins, packageName)
|
|
460
|
-
? this.manifest.plugins[packageName]
|
|
461
|
-
: undefined;
|
|
462
|
-
if (!plugin) {
|
|
463
|
-
return { success: false, error: `Plugin ${packageName} is not installed` };
|
|
464
|
-
}
|
|
465
|
-
plugin.config = { ...plugin.config, ...config };
|
|
466
|
-
await this.saveManifest();
|
|
467
|
-
return { success: true };
|
|
468
|
-
}
|
|
469
|
-
/**
|
|
470
|
-
* Get plugins directory path
|
|
471
|
-
*/
|
|
472
|
-
getPluginsDir() {
|
|
473
|
-
return this.config.pluginsDir;
|
|
474
|
-
}
|
|
475
|
-
/**
|
|
476
|
-
* Get manifest path
|
|
477
|
-
*/
|
|
478
|
-
getManifestPath() {
|
|
479
|
-
return this.config.manifestPath;
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
// ============================================================================
|
|
483
|
-
// Singleton Instance
|
|
484
|
-
// ============================================================================
|
|
485
|
-
let defaultManager = null;
|
|
486
|
-
export function getPluginManager(baseDir) {
|
|
487
|
-
if (!defaultManager) {
|
|
488
|
-
defaultManager = new PluginManager(baseDir);
|
|
489
|
-
}
|
|
490
|
-
else if (baseDir && defaultManager.getPluginsDir() !== path.join(baseDir, '.monomind', 'plugins')) {
|
|
491
|
-
console.warn(`[PluginManager] Warning: getPluginManager called with different baseDir. Using existing instance. Call resetPluginManager() first to change.`);
|
|
492
|
-
}
|
|
493
|
-
return defaultManager;
|
|
494
|
-
}
|
|
495
|
-
export function resetPluginManager() {
|
|
496
|
-
defaultManager = null;
|
|
497
|
-
}
|
|
498
|
-
//# sourceMappingURL=manager.js.map
|