@nexrall/code-core 1.4.46 → 1.4.48
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/agent/agentTypes.d.ts.map +1 -1
- package/dist/agent/agentTypes.js +12 -15
- package/dist/agent/loop.d.ts.map +1 -1
- package/dist/agent/loop.js +14 -1
- package/dist/agent/memory.d.ts +38 -10
- package/dist/agent/memory.d.ts.map +1 -1
- package/dist/agent/memory.js +223 -67
- package/dist/agent/skills.d.ts +34 -4
- package/dist/agent/skills.d.ts.map +1 -1
- package/dist/agent/skills.js +111 -38
- package/dist/agent/trust.d.ts +7 -0
- package/dist/agent/trust.d.ts.map +1 -0
- package/dist/agent/trust.js +163 -0
- package/dist/api/client.d.ts +30 -0
- package/dist/api/client.d.ts.map +1 -1
- package/dist/api/client.js +52 -0
- package/dist/checkpoint/manager.d.ts.map +1 -1
- package/dist/checkpoint/manager.js +6 -0
- package/dist/commands/loader.d.ts.map +1 -1
- package/dist/commands/loader.js +2 -13
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/permissions/modePolicy.d.ts.map +1 -1
- package/dist/permissions/modePolicy.js +1 -0
- package/dist/permissions/rules.js +1 -1
- package/dist/plugins/index.d.ts +31 -4
- package/dist/plugins/index.d.ts.map +1 -1
- package/dist/plugins/index.js +72 -5
- package/dist/plugins/installer.d.ts +47 -0
- package/dist/plugins/installer.d.ts.map +1 -1
- package/dist/plugins/installer.js +173 -0
- package/dist/tools/executor.d.ts.map +1 -1
- package/dist/tools/executor.js +196 -5
- package/dist/util/frontmatter.d.ts +79 -0
- package/dist/util/frontmatter.d.ts.map +1 -0
- package/dist/util/frontmatter.js +172 -0
- package/package.json +5 -2
package/dist/commands/loader.js
CHANGED
|
@@ -42,6 +42,7 @@ const path = __importStar(require("path"));
|
|
|
42
42
|
const os = __importStar(require("os"));
|
|
43
43
|
const child_process_1 = require("child_process");
|
|
44
44
|
const index_1 = require("../plugins/index");
|
|
45
|
+
const frontmatter_1 = require("../util/frontmatter");
|
|
45
46
|
// ── Built-in commands ─────────────────────────────────────────────────────────
|
|
46
47
|
// Shipped with the product; lowest precedence (project > global > builtin), so
|
|
47
48
|
// a user can override any of them by creating a file with the same name.
|
|
@@ -79,18 +80,6 @@ const BUILTIN_COMMANDS = [
|
|
|
79
80
|
].join('\n'),
|
|
80
81
|
},
|
|
81
82
|
];
|
|
82
|
-
function parseFrontmatter(raw) {
|
|
83
|
-
const m = /^\s*---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/.exec(raw);
|
|
84
|
-
if (!m)
|
|
85
|
-
return { meta: {}, body: raw.trim() };
|
|
86
|
-
const meta = {};
|
|
87
|
-
for (const line of m[1].split(/\r?\n/)) {
|
|
88
|
-
const kv = /^([A-Za-z0-9_-]+)\s*:\s*(.*)$/.exec(line.trim());
|
|
89
|
-
if (kv)
|
|
90
|
-
meta[kv[1].toLowerCase()] = kv[2].trim().replace(/^["']|["']$/g, '');
|
|
91
|
-
}
|
|
92
|
-
return { meta, body: (m[2] ?? '').trim() };
|
|
93
|
-
}
|
|
94
83
|
function loadDir(dir, source, into) {
|
|
95
84
|
let files;
|
|
96
85
|
try {
|
|
@@ -102,7 +91,7 @@ function loadDir(dir, source, into) {
|
|
|
102
91
|
for (const file of files) {
|
|
103
92
|
try {
|
|
104
93
|
const raw = fs.readFileSync(path.join(dir, file), 'utf-8');
|
|
105
|
-
const { meta, body } = parseFrontmatter(raw);
|
|
94
|
+
const { meta, body } = (0, frontmatter_1.parseFrontmatter)(raw);
|
|
106
95
|
const name = (meta.name || path.basename(file, '.md')).trim().toLowerCase();
|
|
107
96
|
if (!name)
|
|
108
97
|
continue;
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,uBAAuB,CAAC;AACtC,cAAc,0BAA0B,CAAC;AACzC,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,uBAAuB,CAAC;AACtC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,qBAAqB,CAAC;AACpC,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,iBAAiB,CAAC;AAChC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,uBAAuB,CAAC;AACtC,cAAc,0BAA0B,CAAC;AACzC,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,uBAAuB,CAAC;AACtC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,qBAAqB,CAAC;AACpC,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,iBAAiB,CAAC;AAChC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -42,4 +42,5 @@ __exportStar(require("./permissions/destructive"), exports);
|
|
|
42
42
|
__exportStar(require("./plugins/index"), exports);
|
|
43
43
|
__exportStar(require("./plugins/installer"), exports);
|
|
44
44
|
__exportStar(require("./plugins/sources"), exports);
|
|
45
|
+
__exportStar(require("./agent/trust"), exports);
|
|
45
46
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"modePolicy.d.ts","sourceRoot":"","sources":["../../src/permissions/modePolicy.ts"],"names":[],"mappings":"AAsBA,yDAAyD;AACzD,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;AAE9D,kDAAkD;AAClD,MAAM,MAAM,cAAc,GACtB,OAAO,GACP,KAAK,GACL,MAAM,GACN,SAAS,CAAC;AAEd,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,IAAI,EAAE,cAAc,CAAC;IACrB,oFAAoF;IACpF,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,2EAA2E;IAC3E,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,aAU1B,CAAC;AAEH,0CAA0C;AAC1C,eAAO,MAAM,gBAAgB,
|
|
1
|
+
{"version":3,"file":"modePolicy.d.ts","sourceRoot":"","sources":["../../src/permissions/modePolicy.ts"],"names":[],"mappings":"AAsBA,yDAAyD;AACzD,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;AAE9D,kDAAkD;AAClD,MAAM,MAAM,cAAc,GACtB,OAAO,GACP,KAAK,GACL,MAAM,GACN,SAAS,CAAC;AAEd,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,IAAI,EAAE,cAAc,CAAC;IACrB,oFAAoF;IACpF,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,2EAA2E;IAC3E,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,aAU1B,CAAC;AAEH,0CAA0C;AAC1C,eAAO,MAAM,gBAAgB,aAG3B,CAAC;AAEH,oEAAoE;AACpE,eAAO,MAAM,iBAAiB,aAE5B,CAAC;AAEH,sCAAsC;AACtC,eAAO,MAAM,UAAU,aAAsD,CAAC;AAE9E;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEvD;AAED;;;;;;;GAOG;AACH,wBAAgB,MAAM,CAAC,GAAG,EAAE,WAAW,GAAG,cAAc,CAwDvD;AAED,4DAA4D;AAC5D,wBAAgB,YAAY,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM,CAOzD;AAED,gFAAgF;AAChF,wBAAgB,SAAS,CAAC,GAAG,EAAE,OAAO,GAAG,cAAc,CAGtD"}
|
|
@@ -28,6 +28,7 @@ exports.READ_ONLY_TOOLS = new Set([
|
|
|
28
28
|
/** Tools that write to the filesystem. */
|
|
29
29
|
exports.FILE_WRITE_TOOLS = new Set([
|
|
30
30
|
'write_file', 'create_file', 'edit_file', 'multi_edit', 'notebook_edit',
|
|
31
|
+
'write_docx', 'write_xlsx', 'write_pptx',
|
|
31
32
|
]);
|
|
32
33
|
/** Tools that move/delete files — riskier than an in-place edit. */
|
|
33
34
|
exports.FILE_MUTATE_TOOLS = new Set([
|
|
@@ -200,7 +200,7 @@ function ruleMatches(rule, tool, input, workDir, opts) {
|
|
|
200
200
|
const aliases = {
|
|
201
201
|
Read: ['read_file', 'list_directory', 'search_files', 'glob', 'notebook_read'],
|
|
202
202
|
Edit: ['edit_file', 'multi_edit', 'write_file', 'notebook_edit'],
|
|
203
|
-
Write: ['write_file'],
|
|
203
|
+
Write: ['write_file', 'write_docx', 'write_xlsx', 'write_pptx'],
|
|
204
204
|
Bash: ['bash'],
|
|
205
205
|
WebFetch: ['fetch_url'],
|
|
206
206
|
Image: ['generate_image', 'stock_photo'],
|
package/dist/plugins/index.d.ts
CHANGED
|
@@ -6,7 +6,13 @@ export interface PluginInfo {
|
|
|
6
6
|
/** Absolute path to the plugin directory. */
|
|
7
7
|
dir: string;
|
|
8
8
|
scope: 'project' | 'global';
|
|
9
|
+
/** True when the user has toggled this plugin off (see isPluginDisabled). */
|
|
10
|
+
disabled: boolean;
|
|
9
11
|
}
|
|
12
|
+
/** True if this installed plugin directory has been toggled off. */
|
|
13
|
+
export declare function isPluginDisabled(dir: string): boolean;
|
|
14
|
+
/** Toggle a plugin on/off. Idempotent — enabling an already-enabled plugin is a no-op. */
|
|
15
|
+
export declare function setPluginDisabled(dir: string, disabled: boolean): void;
|
|
10
16
|
/**
|
|
11
17
|
* Where each single-file component may live, in priority order.
|
|
12
18
|
*
|
|
@@ -27,12 +33,33 @@ export declare const FILE_CANDIDATES: {
|
|
|
27
33
|
};
|
|
28
34
|
/** First existing candidate path for a component, or null. */
|
|
29
35
|
export declare function resolvePluginFile(dir: string, kind: keyof typeof FILE_CANDIDATES): string | null;
|
|
30
|
-
/**
|
|
36
|
+
/**
|
|
37
|
+
* Discover installed plugins (project scope shadows global on name clash).
|
|
38
|
+
*
|
|
39
|
+
* Includes disabled plugins — callers that manage the install (the desktop
|
|
40
|
+
* UI's list, `nex plugin list`) need to show and re-enable them. Everything
|
|
41
|
+
* that actually LOADS a plugin's contents at runtime (pluginAssetDirs,
|
|
42
|
+
* pluginHooks, pluginMcpServers below) filters `disabled` out itself, which is
|
|
43
|
+
* what makes toggling a plugin off actually take effect instead of only
|
|
44
|
+
* hiding it from a list.
|
|
45
|
+
*/
|
|
31
46
|
export declare function loadPlugins(workDir: string): PluginInfo[];
|
|
32
|
-
/**
|
|
47
|
+
/**
|
|
48
|
+
* Find one installed plugin by name AND scope. Looking this up through
|
|
49
|
+
* loadPlugins() (rather than re-deriving `<scopeRoot>/<name>` the way
|
|
50
|
+
* removePlugin/updatePlugin in installer.ts do) is deliberate: a plugin's
|
|
51
|
+
* effective name can come from plugin.json's "name" field rather than its
|
|
52
|
+
* directory name (see scanRoot/readMeta above), so a caller that needs the
|
|
53
|
+
* plugin's actual `dir` for a specific scope — not just "some plugin with
|
|
54
|
+
* this name, whichever scope loadPlugins()'s single merged map preferred" —
|
|
55
|
+
* has to re-scan that one scope directly to avoid resolving to the wrong
|
|
56
|
+
* directory or silently matching the other scope's plugin of the same name.
|
|
57
|
+
*/
|
|
58
|
+
export declare function findPlugin(workDir: string, name: string, scope: 'project' | 'global'): PluginInfo | undefined;
|
|
59
|
+
/** Subdirectories of every ENABLED installed plugin that hold `kind` assets (existing only). */
|
|
33
60
|
export declare function pluginAssetDirs(workDir: string, kind: 'commands' | 'agents' | 'skills'): string[];
|
|
34
|
-
/** Merge hooks.json from every plugin into a single hooks config (arrays concatenated). */
|
|
61
|
+
/** Merge hooks.json from every ENABLED plugin into a single hooks config (arrays concatenated). */
|
|
35
62
|
export declare function pluginHooks(workDir: string): Record<string, unknown[]>;
|
|
36
|
-
/** MCP server maps declared by plugins: { serverName → config } (first plugin wins on clash). */
|
|
63
|
+
/** MCP server maps declared by ENABLED plugins: { serverName → config } (first plugin wins on clash). */
|
|
37
64
|
export declare function pluginMcpServers(workDir: string): Record<string, unknown>;
|
|
38
65
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/plugins/index.ts"],"names":[],"mappings":"AAsCA,MAAM,WAAW,UAAU;IACzB,2DAA2D;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6CAA6C;IAC7C,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,SAAS,GAAG,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/plugins/index.ts"],"names":[],"mappings":"AAsCA,MAAM,WAAW,UAAU;IACzB,2DAA2D;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6CAA6C;IAC7C,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,SAAS,GAAG,QAAQ,CAAC;IAC5B,6EAA6E;IAC7E,QAAQ,EAAE,OAAO,CAAC;CACnB;AAYD,oEAAoE;AACpE,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAMrD;AAED,0FAA0F;AAC1F,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,IAAI,CAWtE;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,eAAe;;;;CAIlB,CAAC;AAEX,8DAA8D;AAC9D,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,OAAO,eAAe,GAAG,MAAM,GAAG,IAAI,CAQhG;AAwCD;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,EAAE,CAKzD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,CAK7G;AAED,gGAAgG;AAChG,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,EAAE,CAOjG;AAED,mGAAmG;AACnG,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAkBtE;AAED,yGAAyG;AACzG,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAgBzE"}
|
package/dist/plugins/index.js
CHANGED
|
@@ -34,14 +34,50 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.FILE_CANDIDATES = void 0;
|
|
37
|
+
exports.isPluginDisabled = isPluginDisabled;
|
|
38
|
+
exports.setPluginDisabled = setPluginDisabled;
|
|
37
39
|
exports.resolvePluginFile = resolvePluginFile;
|
|
38
40
|
exports.loadPlugins = loadPlugins;
|
|
41
|
+
exports.findPlugin = findPlugin;
|
|
39
42
|
exports.pluginAssetDirs = pluginAssetDirs;
|
|
40
43
|
exports.pluginHooks = pluginHooks;
|
|
41
44
|
exports.pluginMcpServers = pluginMcpServers;
|
|
42
45
|
const fs = __importStar(require("fs"));
|
|
43
46
|
const path = __importStar(require("path"));
|
|
44
47
|
const os = __importStar(require("os"));
|
|
48
|
+
// ─── Enable / disable ──────────────────────────────────────────────────────────
|
|
49
|
+
//
|
|
50
|
+
// A marker file rather than a registry list, so the on/off state travels WITH
|
|
51
|
+
// the plugin directory itself: it survives a rename of the parent folder, and
|
|
52
|
+
// (deliberately, see installPlugin's preservation logic in installer.ts) it
|
|
53
|
+
// must survive `nex plugin update` re-copying the directory's contents — a
|
|
54
|
+
// user who turned a plugin off should not have it silently turn back on the
|
|
55
|
+
// next time it updates.
|
|
56
|
+
const DISABLED_MARKER = '.disabled';
|
|
57
|
+
/** True if this installed plugin directory has been toggled off. */
|
|
58
|
+
function isPluginDisabled(dir) {
|
|
59
|
+
try {
|
|
60
|
+
return fs.statSync(path.join(dir, DISABLED_MARKER)).isFile();
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/** Toggle a plugin on/off. Idempotent — enabling an already-enabled plugin is a no-op. */
|
|
67
|
+
function setPluginDisabled(dir, disabled) {
|
|
68
|
+
const marker = path.join(dir, DISABLED_MARKER);
|
|
69
|
+
if (disabled) {
|
|
70
|
+
fs.writeFileSync(marker, '');
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
try {
|
|
74
|
+
fs.unlinkSync(marker);
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
/* already absent */
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
45
81
|
/**
|
|
46
82
|
* Where each single-file component may live, in priority order.
|
|
47
83
|
*
|
|
@@ -112,19 +148,46 @@ function scanRoot(root, scope, into) {
|
|
|
112
148
|
const name = meta.name || entry.name;
|
|
113
149
|
if (into.has(name))
|
|
114
150
|
continue; // project scope scanned first — it wins
|
|
115
|
-
into.set(name, { name, version: meta.version, description: meta.description, dir, scope });
|
|
151
|
+
into.set(name, { name, version: meta.version, description: meta.description, dir, scope, disabled: isPluginDisabled(dir) });
|
|
116
152
|
}
|
|
117
153
|
}
|
|
118
|
-
/**
|
|
154
|
+
/**
|
|
155
|
+
* Discover installed plugins (project scope shadows global on name clash).
|
|
156
|
+
*
|
|
157
|
+
* Includes disabled plugins — callers that manage the install (the desktop
|
|
158
|
+
* UI's list, `nex plugin list`) need to show and re-enable them. Everything
|
|
159
|
+
* that actually LOADS a plugin's contents at runtime (pluginAssetDirs,
|
|
160
|
+
* pluginHooks, pluginMcpServers below) filters `disabled` out itself, which is
|
|
161
|
+
* what makes toggling a plugin off actually take effect instead of only
|
|
162
|
+
* hiding it from a list.
|
|
163
|
+
*/
|
|
119
164
|
function loadPlugins(workDir) {
|
|
120
165
|
const out = new Map();
|
|
121
166
|
scanRoot(path.join(workDir, '.nexrall', 'plugins'), 'project', out);
|
|
122
167
|
scanRoot(path.join(os.homedir(), '.nexrall', 'plugins'), 'global', out);
|
|
123
168
|
return [...out.values()];
|
|
124
169
|
}
|
|
125
|
-
/**
|
|
170
|
+
/**
|
|
171
|
+
* Find one installed plugin by name AND scope. Looking this up through
|
|
172
|
+
* loadPlugins() (rather than re-deriving `<scopeRoot>/<name>` the way
|
|
173
|
+
* removePlugin/updatePlugin in installer.ts do) is deliberate: a plugin's
|
|
174
|
+
* effective name can come from plugin.json's "name" field rather than its
|
|
175
|
+
* directory name (see scanRoot/readMeta above), so a caller that needs the
|
|
176
|
+
* plugin's actual `dir` for a specific scope — not just "some plugin with
|
|
177
|
+
* this name, whichever scope loadPlugins()'s single merged map preferred" —
|
|
178
|
+
* has to re-scan that one scope directly to avoid resolving to the wrong
|
|
179
|
+
* directory or silently matching the other scope's plugin of the same name.
|
|
180
|
+
*/
|
|
181
|
+
function findPlugin(workDir, name, scope) {
|
|
182
|
+
const out = new Map();
|
|
183
|
+
const root = scope === 'project' ? path.join(workDir, '.nexrall', 'plugins') : path.join(os.homedir(), '.nexrall', 'plugins');
|
|
184
|
+
scanRoot(root, scope, out);
|
|
185
|
+
return out.get(name);
|
|
186
|
+
}
|
|
187
|
+
/** Subdirectories of every ENABLED installed plugin that hold `kind` assets (existing only). */
|
|
126
188
|
function pluginAssetDirs(workDir, kind) {
|
|
127
189
|
return loadPlugins(workDir)
|
|
190
|
+
.filter((p) => !p.disabled)
|
|
128
191
|
.map((p) => path.join(p.dir, kind))
|
|
129
192
|
.filter((d) => {
|
|
130
193
|
try {
|
|
@@ -135,10 +198,12 @@ function pluginAssetDirs(workDir, kind) {
|
|
|
135
198
|
}
|
|
136
199
|
});
|
|
137
200
|
}
|
|
138
|
-
/** Merge hooks.json from every plugin into a single hooks config (arrays concatenated). */
|
|
201
|
+
/** Merge hooks.json from every ENABLED plugin into a single hooks config (arrays concatenated). */
|
|
139
202
|
function pluginHooks(workDir) {
|
|
140
203
|
const merged = {};
|
|
141
204
|
for (const p of loadPlugins(workDir)) {
|
|
205
|
+
if (p.disabled)
|
|
206
|
+
continue;
|
|
142
207
|
const file = resolvePluginFile(p.dir, 'hooks');
|
|
143
208
|
if (!file)
|
|
144
209
|
continue;
|
|
@@ -157,10 +222,12 @@ function pluginHooks(workDir) {
|
|
|
157
222
|
}
|
|
158
223
|
return merged;
|
|
159
224
|
}
|
|
160
|
-
/** MCP server maps declared by plugins: { serverName → config } (first plugin wins on clash). */
|
|
225
|
+
/** MCP server maps declared by ENABLED plugins: { serverName → config } (first plugin wins on clash). */
|
|
161
226
|
function pluginMcpServers(workDir) {
|
|
162
227
|
const merged = {};
|
|
163
228
|
for (const p of loadPlugins(workDir)) {
|
|
229
|
+
if (p.disabled)
|
|
230
|
+
continue;
|
|
164
231
|
const file = resolvePluginFile(p.dir, 'mcp');
|
|
165
232
|
if (!file)
|
|
166
233
|
continue;
|
|
@@ -86,6 +86,53 @@ export declare function parsePluginSource(spec: string): PluginSource;
|
|
|
86
86
|
* containing no MCP servers.
|
|
87
87
|
*/
|
|
88
88
|
export declare function inspectPluginDir(dir: string): PluginInspection;
|
|
89
|
+
export interface PluginComponentSummary {
|
|
90
|
+
name: string;
|
|
91
|
+
description: string;
|
|
92
|
+
}
|
|
93
|
+
export interface PluginMcpServerSummary {
|
|
94
|
+
name: string;
|
|
95
|
+
/** Transport, best-effort from the raw config shape (mirrors McpServerConfig in mcp/client.ts). */
|
|
96
|
+
type: 'stdio' | 'http' | 'sse';
|
|
97
|
+
/** Short human-readable summary of how this server connects (command, or URL host). */
|
|
98
|
+
summary: string;
|
|
99
|
+
}
|
|
100
|
+
export interface PluginComponentDetail {
|
|
101
|
+
commands: PluginComponentSummary[];
|
|
102
|
+
skills: PluginComponentSummary[];
|
|
103
|
+
agents: PluginComponentSummary[];
|
|
104
|
+
mcpServers: PluginMcpServerSummary[];
|
|
105
|
+
hasHooks: boolean;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Full breakdown of what a plugin bundles — one skill/command/connector per
|
|
109
|
+
* row, each with the human-readable text a user needs to decide whether to
|
|
110
|
+
* use/connect it. Distinct from inspectPluginDir (which only reports COUNTS
|
|
111
|
+
* and booleans for the install-time safety warning): this is the read model
|
|
112
|
+
* for the "click a plugin to see what's inside" detail screen, most directly
|
|
113
|
+
* needed because a Connectors tab has to show each MCP server by NAME so a
|
|
114
|
+
* user can connect them one at a time, the same way Claude Cowork's plugin
|
|
115
|
+
* detail view does.
|
|
116
|
+
*/
|
|
117
|
+
export declare function pluginComponentDetail(dir: string): PluginComponentDetail;
|
|
118
|
+
export interface PluginItemDetail extends PluginComponentSummary {
|
|
119
|
+
/** Full raw file contents, including frontmatter — what a "Raw" toggle / Copy button should use. */
|
|
120
|
+
raw: string;
|
|
121
|
+
/** Frontmatter-stripped body — what a "Preview" toggle should render as markdown. */
|
|
122
|
+
body: string;
|
|
123
|
+
/** Absolute path to the source file, for "Edit" (open in the user's default editor/file manager). */
|
|
124
|
+
filePath: string;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Full content of ONE skill/command/agent inside a plugin — the read model
|
|
128
|
+
* for "click a skill row to see its content", mirroring Claude Cowork's own
|
|
129
|
+
* per-skill screen (rendered preview, raw markdown toggle, copy button).
|
|
130
|
+
* Deliberately separate from pluginComponentDetail's summaries (name +
|
|
131
|
+
* description only): loading every item's full body up front for a plugin
|
|
132
|
+
* with many skills would be wasteful, so this is fetched lazily, one row at
|
|
133
|
+
* a time, only when the user actually opens it.
|
|
134
|
+
*/
|
|
135
|
+
export declare function pluginItemDetail(dir: string, kind: 'commands' | 'agents' | 'skills', name: string): PluginItemDetail | null;
|
|
89
136
|
export interface InstallOptions {
|
|
90
137
|
/** 'global' (default) → ~/.nexrall/plugins; 'project' → <workDir>/.nexrall/plugins */
|
|
91
138
|
scope?: 'project' | 'global';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"installer.d.ts","sourceRoot":"","sources":["../../src/plugins/installer.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"installer.d.ts","sourceRoot":"","sources":["../../src/plugins/installer.ts"],"names":[],"mappings":"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"}
|
|
@@ -38,6 +38,8 @@ exports.getRegistryPlugin = getRegistryPlugin;
|
|
|
38
38
|
exports.reportInstall = reportInstall;
|
|
39
39
|
exports.parsePluginSource = parsePluginSource;
|
|
40
40
|
exports.inspectPluginDir = inspectPluginDir;
|
|
41
|
+
exports.pluginComponentDetail = pluginComponentDetail;
|
|
42
|
+
exports.pluginItemDetail = pluginItemDetail;
|
|
41
43
|
exports.installPlugin = installPlugin;
|
|
42
44
|
exports.readReceipt = readReceipt;
|
|
43
45
|
exports.removePlugin = removePlugin;
|
|
@@ -49,6 +51,7 @@ const child_process_1 = require("child_process");
|
|
|
49
51
|
const client_1 = require("../api/client");
|
|
50
52
|
const index_1 = require("./index");
|
|
51
53
|
const sources_1 = require("./sources");
|
|
54
|
+
const frontmatter_1 = require("../util/frontmatter");
|
|
52
55
|
const RECEIPT_FILE = '_install.json';
|
|
53
56
|
/** Search the registry (empty query = list all, official first). */
|
|
54
57
|
async function searchRegistry(query, limit = 50) {
|
|
@@ -169,6 +172,176 @@ function inspectPluginDir(dir) {
|
|
|
169
172
|
meta,
|
|
170
173
|
};
|
|
171
174
|
}
|
|
175
|
+
/** Read a flat `commands/*.md` or `agents/*.md` directory, returning name + frontmatter description. */
|
|
176
|
+
function listMdWithDescription(dir) {
|
|
177
|
+
let files;
|
|
178
|
+
try {
|
|
179
|
+
files = fs.readdirSync(dir).filter((f) => f.endsWith('.md'));
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
return [];
|
|
183
|
+
}
|
|
184
|
+
const out = [];
|
|
185
|
+
for (const file of files) {
|
|
186
|
+
const name = file.replace(/\.md$/, '');
|
|
187
|
+
try {
|
|
188
|
+
const raw = fs.readFileSync(path.join(dir, file), 'utf-8');
|
|
189
|
+
const { meta } = (0, frontmatter_1.parseFrontmatter)(raw);
|
|
190
|
+
out.push({ name, description: meta.description || '' });
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
out.push({ name, description: '' }); // malformed file — still list it, just without a description
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return out;
|
|
197
|
+
}
|
|
198
|
+
/** Read `skills/<name>/SKILL.md` subdirectories, returning name + frontmatter description. */
|
|
199
|
+
function listSkillsWithDescription(dir) {
|
|
200
|
+
let entries;
|
|
201
|
+
try {
|
|
202
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
return [];
|
|
206
|
+
}
|
|
207
|
+
const out = [];
|
|
208
|
+
for (const entry of entries) {
|
|
209
|
+
if (!entry.isDirectory() && !entry.isSymbolicLink())
|
|
210
|
+
continue;
|
|
211
|
+
const skillFile = path.join(dir, entry.name, 'SKILL.md');
|
|
212
|
+
try {
|
|
213
|
+
if (!fs.statSync(skillFile).isFile())
|
|
214
|
+
continue;
|
|
215
|
+
const raw = fs.readFileSync(skillFile, 'utf-8');
|
|
216
|
+
const { meta } = (0, frontmatter_1.parseFrontmatter)(raw);
|
|
217
|
+
out.push({ name: meta.name || entry.name, description: meta.description || '' });
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
/* not a valid skill directory — skip */
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return out;
|
|
224
|
+
}
|
|
225
|
+
/** Best-effort transport + one-line summary for an mcp.json server entry, for display only (never executed). */
|
|
226
|
+
function summariseMcpServer(name, cfg) {
|
|
227
|
+
const c = (cfg && typeof cfg === 'object' ? cfg : {});
|
|
228
|
+
const url = typeof c.url === 'string' ? c.url : undefined;
|
|
229
|
+
const command = typeof c.command === 'string' ? c.command : undefined;
|
|
230
|
+
const declaredType = c.type === 'http' || c.type === 'sse' || c.type === 'stdio' ? c.type : undefined;
|
|
231
|
+
const type = declaredType ?? (url ? 'http' : 'stdio');
|
|
232
|
+
let summary = 'Unknown connector configuration';
|
|
233
|
+
if (url) {
|
|
234
|
+
try {
|
|
235
|
+
summary = new URL(url).host;
|
|
236
|
+
}
|
|
237
|
+
catch {
|
|
238
|
+
summary = url;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
else if (command) {
|
|
242
|
+
const args = Array.isArray(c.args) ? c.args.filter((a) => typeof a === 'string') : [];
|
|
243
|
+
summary = [command, ...args].join(' ');
|
|
244
|
+
}
|
|
245
|
+
return { name, type, summary };
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Full breakdown of what a plugin bundles — one skill/command/connector per
|
|
249
|
+
* row, each with the human-readable text a user needs to decide whether to
|
|
250
|
+
* use/connect it. Distinct from inspectPluginDir (which only reports COUNTS
|
|
251
|
+
* and booleans for the install-time safety warning): this is the read model
|
|
252
|
+
* for the "click a plugin to see what's inside" detail screen, most directly
|
|
253
|
+
* needed because a Connectors tab has to show each MCP server by NAME so a
|
|
254
|
+
* user can connect them one at a time, the same way Claude Cowork's plugin
|
|
255
|
+
* detail view does.
|
|
256
|
+
*/
|
|
257
|
+
function pluginComponentDetail(dir) {
|
|
258
|
+
const mcpFile = (0, index_1.resolvePluginFile)(dir, 'mcp');
|
|
259
|
+
let mcpServers = [];
|
|
260
|
+
if (mcpFile) {
|
|
261
|
+
try {
|
|
262
|
+
const j = JSON.parse(fs.readFileSync(mcpFile, 'utf-8'));
|
|
263
|
+
const servers = (j.mcpServers ?? j);
|
|
264
|
+
mcpServers = Object.entries(servers).map(([name, cfg]) => summariseMcpServer(name, cfg));
|
|
265
|
+
}
|
|
266
|
+
catch {
|
|
267
|
+
/* malformed mcp.json — report no servers rather than throwing */
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return {
|
|
271
|
+
commands: listMdWithDescription(path.join(dir, 'commands')),
|
|
272
|
+
skills: listSkillsWithDescription(path.join(dir, 'skills')),
|
|
273
|
+
agents: listMdWithDescription(path.join(dir, 'agents')),
|
|
274
|
+
mcpServers,
|
|
275
|
+
hasHooks: (0, index_1.resolvePluginFile)(dir, 'hooks') !== null,
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
/** Match a flat `commands/*.md` or `agents/*.md` file by its display name (never builds a path from `name` — only compares against real directory entries, so a caller cannot path-traverse via the name argument). */
|
|
279
|
+
function readPluginItemFromFlatDir(dir, name) {
|
|
280
|
+
let files;
|
|
281
|
+
try {
|
|
282
|
+
files = fs.readdirSync(dir).filter((f) => f.endsWith('.md'));
|
|
283
|
+
}
|
|
284
|
+
catch {
|
|
285
|
+
return null;
|
|
286
|
+
}
|
|
287
|
+
for (const file of files) {
|
|
288
|
+
if (file.replace(/\.md$/, '') !== name)
|
|
289
|
+
continue;
|
|
290
|
+
const filePath = path.join(dir, file);
|
|
291
|
+
try {
|
|
292
|
+
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
293
|
+
const { meta, body } = (0, frontmatter_1.parseFrontmatter)(raw);
|
|
294
|
+
return { name, description: meta.description || '', raw, body, filePath };
|
|
295
|
+
}
|
|
296
|
+
catch {
|
|
297
|
+
return null;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
302
|
+
/** Match a `skills/<dir>/SKILL.md` by its effective name (meta.name override, or the directory name). */
|
|
303
|
+
function readPluginSkill(skillsDir, name) {
|
|
304
|
+
let entries;
|
|
305
|
+
try {
|
|
306
|
+
entries = fs.readdirSync(skillsDir, { withFileTypes: true });
|
|
307
|
+
}
|
|
308
|
+
catch {
|
|
309
|
+
return null;
|
|
310
|
+
}
|
|
311
|
+
for (const entry of entries) {
|
|
312
|
+
if (!entry.isDirectory() && !entry.isSymbolicLink())
|
|
313
|
+
continue;
|
|
314
|
+
const skillFile = path.join(skillsDir, entry.name, 'SKILL.md');
|
|
315
|
+
try {
|
|
316
|
+
if (!fs.statSync(skillFile).isFile())
|
|
317
|
+
continue;
|
|
318
|
+
const raw = fs.readFileSync(skillFile, 'utf-8');
|
|
319
|
+
const { meta, body } = (0, frontmatter_1.parseFrontmatter)(raw);
|
|
320
|
+
const skillName = meta.name || entry.name;
|
|
321
|
+
if (skillName !== name)
|
|
322
|
+
continue;
|
|
323
|
+
return { name: skillName, description: meta.description || '', raw, body, filePath: skillFile };
|
|
324
|
+
}
|
|
325
|
+
catch {
|
|
326
|
+
/* unreadable/malformed — keep looking, don't fail the whole scan */
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return null;
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Full content of ONE skill/command/agent inside a plugin — the read model
|
|
333
|
+
* for "click a skill row to see its content", mirroring Claude Cowork's own
|
|
334
|
+
* per-skill screen (rendered preview, raw markdown toggle, copy button).
|
|
335
|
+
* Deliberately separate from pluginComponentDetail's summaries (name +
|
|
336
|
+
* description only): loading every item's full body up front for a plugin
|
|
337
|
+
* with many skills would be wasteful, so this is fetched lazily, one row at
|
|
338
|
+
* a time, only when the user actually opens it.
|
|
339
|
+
*/
|
|
340
|
+
function pluginItemDetail(dir, kind, name) {
|
|
341
|
+
if (kind === 'skills')
|
|
342
|
+
return readPluginSkill(path.join(dir, 'skills'), name);
|
|
343
|
+
return readPluginItemFromFlatDir(path.join(dir, kind), name);
|
|
344
|
+
}
|
|
172
345
|
/** Is this directory a plausible plugin root? */
|
|
173
346
|
function looksLikePlugin(dir) {
|
|
174
347
|
const i = inspectPluginDir(dir);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../../src/tools/executor.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,EAAyB,KAAK,aAAa,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../../src/tools/executor.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,EAAyB,KAAK,aAAa,EAAE,MAAM,WAAW,CAAC;AA8kFtE,wBAAsB,WAAW,CAC/B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,WAAW,CAAC,EAAE;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,EAClC,OAAO,CAAC,EAAE,aAAa,EACvB,OAAO,CAAC,EAAE,MAAM,EAChB,UAAU,CAAC,EAAE,MAAM,EACnB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GACjC,OAAO,CAAC,UAAU,CAAC,CA6BrB"}
|