@nexrall/code-core 1.4.45 → 1.4.47

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.
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Which trust-sensitive files actually exist in `dir`, so a confirmation
3
+ * prompt can tell the user what THIS folder will do rather than describing
4
+ * risks in the abstract. Ordered most- to least-dangerous.
5
+ */
6
+ export declare function detectTrustSignals(dir: string): string[];
7
+ //# sourceMappingURL=trust.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trust.d.ts","sourceRoot":"","sources":["../../src/agent/trust.ts"],"names":[],"mappings":"AAiDA;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CA0DxD"}
@@ -0,0 +1,151 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.detectTrustSignals = detectTrustSignals;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ // ─── Workspace Trust Signals (shared) ─────────────────────────────────────
40
+ //
41
+ // Opening a folder for an agent to work in is not a passive act. Before the
42
+ // user types anything, a session can already read and act on files found in
43
+ // that folder:
44
+ //
45
+ // • `.nexrall/mcp.json` declares MCP servers, and connecting to a stdio
46
+ // server runs `spawn(server.command, server.args)` — arbitrary command
47
+ // execution, straight from a file in the repo (see mcp/manager.ts /
48
+ // mcp/client.ts).
49
+ // • `.nexrall/plugins/` may contribute their OWN `mcp.json`/`hooks.json`
50
+ // (see plugins/index.ts's pluginMcpServers/pluginHooks) — same risk
51
+ // class as a bare mcp.json, just one hop further away.
52
+ // • `nexrall.md` is injected into the system prompt as trusted project
53
+ // instructions, so its contents steer the agent for the whole session.
54
+ // • `.nexrall/{skills,commands}` define playbooks the agent will follow,
55
+ // and `.nexrall/permissions.json` can pre-approve tool calls that would
56
+ // otherwise prompt.
57
+ //
58
+ // So `git clone` of a hostile repo followed by pointing an agent at it is
59
+ // enough to execute attacker-chosen commands before a human reviews anything.
60
+ //
61
+ // This module is the shared, pure "what would this folder do" detector. The
62
+ // CLI has its own longstanding copy (packages/cli/src/trust.ts) — kept
63
+ // independent there rather than migrated onto this one, so a change here
64
+ // can never silently alter the CLI's own trust-prompt wording/behaviour or
65
+ // its own dedicated test suite. Any NEW client (e.g. the desktop app) should
66
+ // use this shared copy instead of hand-rolling a third one.
67
+ //
68
+ // Deliberately stateless: nothing here reads or writes any trust STORE.
69
+ // Whether/how a client remembers "already confirmed this session" is the
70
+ // caller's job (see e.g. desktop's main/trustGate.ts) — this function only
71
+ // ever reports what is true of the folder RIGHT NOW.
72
+ /** Does the project settings file bind plugin aliases to remote repos? */
73
+ function declaresPluginSources(dir) {
74
+ try {
75
+ const raw = fs.readFileSync(path.join(dir, '.nexrall', 'settings.json'), 'utf-8');
76
+ const obj = JSON.parse(raw);
77
+ const s = obj.pluginSources;
78
+ return Boolean(s && typeof s === 'object' && !Array.isArray(s) && Object.keys(s).length > 0);
79
+ }
80
+ catch {
81
+ return false;
82
+ }
83
+ }
84
+ /**
85
+ * Which trust-sensitive files actually exist in `dir`, so a confirmation
86
+ * prompt can tell the user what THIS folder will do rather than describing
87
+ * risks in the abstract. Ordered most- to least-dangerous.
88
+ */
89
+ function detectTrustSignals(dir) {
90
+ const signals = [];
91
+ const exists = (...p) => {
92
+ try {
93
+ return fs.existsSync(path.join(dir, ...p));
94
+ }
95
+ catch {
96
+ return false;
97
+ }
98
+ };
99
+ // Code-executing first — these can run local commands before the user
100
+ // types anything, so they are what the decision really hinges on.
101
+ if (exists('.nexrall', 'mcp.json')) {
102
+ signals.push('.nexrall/mcp.json — can start MCP servers (runs local commands)');
103
+ }
104
+ if (exists('.nexrall', 'plugins')) {
105
+ // Plugins are scanned from the workspace and may contribute their own MCP
106
+ // servers/hooks (see plugins/index.ts → pluginMcpServers/pluginHooks), so
107
+ // this is in the same risk class as mcp.json rather than a cosmetic add-on.
108
+ signals.push('.nexrall/plugins/ — project plugins (may add MCP servers or hooks)');
109
+ }
110
+ // Then things that widen what the agent may do without asking.
111
+ if (exists('.nexrall', 'permissions.json')) {
112
+ signals.push('.nexrall/permissions.json — pre-approved tool permissions');
113
+ }
114
+ if (exists('.nexrall', 'settings.json')) {
115
+ // Called out separately when the file declares plugin sources: those bind
116
+ // a short alias to a remote repo, so installing that alias would fetch
117
+ // whatever the REPOSITORY chose. Installing still needs a human and its
118
+ // own confirmation — nothing is fetched from a declaration alone — but the
119
+ // user should know the repo is trying to name what they install. A
120
+ // settings.json can also carry a project's own `hooks` config, which runs
121
+ // shell commands automatically around tool calls — worth flagging here too.
122
+ let hasHooks = false;
123
+ try {
124
+ const raw = fs.readFileSync(path.join(dir, '.nexrall', 'settings.json'), 'utf-8');
125
+ const obj = JSON.parse(raw);
126
+ hasHooks = Boolean(obj.hooks && typeof obj.hooks === 'object' && Object.keys(obj.hooks).length > 0);
127
+ }
128
+ catch { /* absent or malformed — treated as no hooks declared */ }
129
+ const bits = ['.nexrall/settings.json — project settings and permission rules'];
130
+ if (declaresPluginSources(dir))
131
+ bits.push('declares plugin sources (aliases for remote plugin repos)');
132
+ if (hasHooks)
133
+ bits.push('runs hooks (shell commands) automatically around tool calls');
134
+ signals.push(bits.join(', '));
135
+ }
136
+ // Then things that steer the agent's behaviour.
137
+ if (exists('nexrall.md')) {
138
+ signals.push('nexrall.md — project instructions added to the agent\u2019s prompt');
139
+ }
140
+ if (exists('.nexrall', 'skills')) {
141
+ signals.push('.nexrall/skills/ — custom agent playbooks');
142
+ }
143
+ if (exists('.nexrall', 'commands')) {
144
+ signals.push('.nexrall/commands/ — custom slash commands');
145
+ }
146
+ if (exists('.nexrall', 'agents')) {
147
+ signals.push('.nexrall/agents/ — custom sub-agent definitions');
148
+ }
149
+ return signals;
150
+ }
151
+ //# sourceMappingURL=trust.js.map
@@ -131,6 +131,36 @@ export interface DescribeAttachmentResult {
131
131
  }
132
132
  export declare function describeAttachment(kind: 'image' | 'pdf', data: string, mediaType?: string, name?: string): Promise<DescribeAttachmentResult>;
133
133
  export declare function getBalance(): Promise<number>;
134
+ export interface UsageDailyDay {
135
+ date: string;
136
+ total: number;
137
+ byType: Record<string, number>;
138
+ }
139
+ export interface UsageDailyResult {
140
+ days: UsageDailyDay[];
141
+ totalSpent: number;
142
+ }
143
+ /**
144
+ * Day-by-day spend breakdown, same endpoint the web app's Settings > Usage
145
+ * chart reads (backend/routes/user.js's /usage-daily). Used by Nexrall Work's
146
+ * own Settings > Usage panel so both surfaces show the identical numbers.
147
+ */
148
+ export declare function getUsageDaily(days?: number): Promise<UsageDailyResult>;
134
149
  export declare function exchangeVscodeCode(code: string): Promise<AuthConfig>;
135
150
  export declare function login(email: string, password: string): Promise<AuthConfig>;
151
+ export interface UserProfile {
152
+ id: string | number;
153
+ email: string;
154
+ name?: string;
155
+ phone?: string;
156
+ avatar_url?: string;
157
+ username?: string;
158
+ bio?: string;
159
+ }
160
+ /**
161
+ * Same GET /api/user/profile the web app's Settings > Profile section reads.
162
+ * Used by Nexrall Work's own Settings > Profile panel so both surfaces show
163
+ * identical account data.
164
+ */
165
+ export declare function getUserProfile(): Promise<UserProfile>;
136
166
  //# sourceMappingURL=client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAA8B,UAAU,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAiErH,eAAO,MAAM,QAAQ,QAAmB,CAAC;AAIzC;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,EAC5D,OAAO,EAAE,CAAC,EAAE,EACZ,UAAU,EAAE,KAAK,CAAC;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,IAAI,GAAG,SAAS,GACtD,CAAC,EAAE,CAeL;AAiGD,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,UAAU,CAAC;IACjB,aAAa,CAAC,EAAE,aAAa,GAAG,IAAI,CAAC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;IACnC,oFAAoF;IACpF,UAAU,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC,CAAC;IACjG,6EAA6E;IAC7E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6IAA6I;IAC7I,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;;OAOG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,yFAAyF;IACzF,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;;;;;;;;;OASG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;;;;;;OASG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;;;;;;;;;;;;;OAeG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC;AA8ID,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,OAAO,EAAE,EACnB,OAAO,EAAE,iBAAiB,EAC1B,OAAO,EAAE,CAAC,CAAC,EAAE,QAAQ,KAAK,IAAI,GAC7B,OAAO,CAAC,OAAO,CAAC,CAkmClB;AAID;;;;;;;;;;;GAWG;AACH,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAU9D;AAID;;;;;;;;;;GAUG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC,CAYxD;AAeD,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,OAAO,GAAG,KAAK,EACrB,IAAI,EAAE,MAAM,EACZ,SAAS,CAAC,EAAE,MAAM,EAClB,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,wBAAwB,CAAC,CAmCnC;AAID,wBAAsB,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,CA0BlD;AAID,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CA+B1E;AAID,wBAAsB,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CA0BhF"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAA8B,UAAU,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAiErH,eAAO,MAAM,QAAQ,QAAmB,CAAC;AAIzC;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,EAC5D,OAAO,EAAE,CAAC,EAAE,EACZ,UAAU,EAAE,KAAK,CAAC;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,IAAI,GAAG,SAAS,GACtD,CAAC,EAAE,CAeL;AAiGD,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,UAAU,CAAC;IACjB,aAAa,CAAC,EAAE,aAAa,GAAG,IAAI,CAAC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;IACnC,oFAAoF;IACpF,UAAU,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC,CAAC;IACjG,6EAA6E;IAC7E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6IAA6I;IAC7I,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;;OAOG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,yFAAyF;IACzF,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;;;;;;;;;OASG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;;;;;;OASG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;;;;;;;;;;;;;OAeG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC;AA8ID,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,OAAO,EAAE,EACnB,OAAO,EAAE,iBAAiB,EAC1B,OAAO,EAAE,CAAC,CAAC,EAAE,QAAQ,KAAK,IAAI,GAC7B,OAAO,CAAC,OAAO,CAAC,CAkmClB;AAID;;;;;;;;;;;GAWG;AACH,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAU9D;AAID;;;;;;;;;;GAUG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC,CAYxD;AAeD,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,OAAO,GAAG,KAAK,EACrB,IAAI,EAAE,MAAM,EACZ,SAAS,CAAC,EAAE,MAAM,EAClB,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,wBAAwB,CAAC,CAmCnC;AAID,wBAAsB,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,CA0BlD;AAID,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,aAAa,EAAE,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,IAAI,SAAK,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAyBxE;AAID,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CA+B1E;AAID,wBAAsB,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CA0BhF;AAID,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;;;GAIG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,WAAW,CAAC,CAqB3D"}
@@ -10,8 +10,10 @@ exports.cancelTurn = cancelTurn;
10
10
  exports.revokeRefreshToken = revokeRefreshToken;
11
11
  exports.describeAttachment = describeAttachment;
12
12
  exports.getBalance = getBalance;
13
+ exports.getUsageDaily = getUsageDaily;
13
14
  exports.exchangeVscodeCode = exchangeVscodeCode;
14
15
  exports.login = login;
16
+ exports.getUserProfile = getUserProfile;
15
17
  const eventsource_parser_1 = require("eventsource-parser");
16
18
  const node_fetch_1 = __importDefault(require("node-fetch"));
17
19
  const crypto_1 = require("crypto");
@@ -1544,6 +1546,33 @@ async function getBalance() {
1544
1546
  const data = (await response.json());
1545
1547
  return typeof data.balance === 'number' ? data.balance : 0;
1546
1548
  }
1549
+ /**
1550
+ * Day-by-day spend breakdown, same endpoint the web app's Settings > Usage
1551
+ * chart reads (backend/routes/user.js's /usage-daily). Used by Nexrall Work's
1552
+ * own Settings > Usage panel so both surfaces show the identical numbers.
1553
+ */
1554
+ async function getUsageDaily(days = 30) {
1555
+ const fetchOnce = () => (0, node_fetch_1.default)(`${exports.API_BASE}/api/user/usage-daily?days=${days}`, { method: 'GET', headers: authHeaders() });
1556
+ let response = await fetchOnce();
1557
+ if (response.status === 401 || response.status === 403) {
1558
+ const body = await response.text().catch(() => '');
1559
+ if (isExpiredTokenResponse(response.status, body) && await refreshAccessToken()) {
1560
+ response = await fetchOnce();
1561
+ }
1562
+ else {
1563
+ throw new Error(`API error ${response.status}: ${body}`);
1564
+ }
1565
+ }
1566
+ if (!response.ok) {
1567
+ const errText = await response.text();
1568
+ throw new Error(`API error ${response.status}: ${errText}`);
1569
+ }
1570
+ const data = (await response.json());
1571
+ return {
1572
+ days: Array.isArray(data.days) ? data.days : [],
1573
+ totalSpent: typeof data.totalSpent === 'number' ? data.totalSpent : 0,
1574
+ };
1575
+ }
1547
1576
  // ─── VSCode OAuth ─────────────────────────────────────────────────────────────
1548
1577
  async function exchangeVscodeCode(code) {
1549
1578
  const response = await (0, node_fetch_1.default)(`${exports.API_BASE}/api/auth/vscode-exchange`, {
@@ -1596,4 +1625,27 @@ async function login(email, password) {
1596
1625
  email: data.email ?? email,
1597
1626
  };
1598
1627
  }
1628
+ /**
1629
+ * Same GET /api/user/profile the web app's Settings > Profile section reads.
1630
+ * Used by Nexrall Work's own Settings > Profile panel so both surfaces show
1631
+ * identical account data.
1632
+ */
1633
+ async function getUserProfile() {
1634
+ const fetchOnce = () => (0, node_fetch_1.default)(`${exports.API_BASE}/api/user/profile`, { method: 'GET', headers: authHeaders() });
1635
+ let response = await fetchOnce();
1636
+ if (response.status === 401 || response.status === 403) {
1637
+ const body = await response.text().catch(() => '');
1638
+ if (isExpiredTokenResponse(response.status, body) && await refreshAccessToken()) {
1639
+ response = await fetchOnce();
1640
+ }
1641
+ else {
1642
+ throw new Error(`API error ${response.status}: ${body}`);
1643
+ }
1644
+ }
1645
+ if (!response.ok) {
1646
+ const errText = await response.text();
1647
+ throw new Error(`API error ${response.status}: ${errText}`);
1648
+ }
1649
+ return (await response.json());
1650
+ }
1599
1651
  //# sourceMappingURL=client.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/checkpoint/manager.ts"],"names":[],"mappings":"AA4CA,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEpD;AAQD,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAMlE;AAyBD,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,0DAA0D;IAC1D,OAAO,EAAE,OAAO,CAAC;IACjB,8FAA8F;IAC9F,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,qEAAqE;IACrE,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,sFAAsF;IACtF,SAAS,EAAE,MAAM,CAAC;IAClB,sGAAsG;IACtG,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAMD,MAAM,WAAW,aAAa;IAC5B,4CAA4C;IAC5C,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,kFAAkF;IAClF,YAAY,EAAE,MAAM,CAAC;IACrB,6FAA6F;IAC7F,SAAS,EAAE,MAAM,CAAC;IAClB,6FAA6F;IAC7F,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B;AA6BD,qBAAa,iBAAiB;IAWhB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAVpC,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,OAAO,CAAqB;IACpC,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAElC;;;;OAIG;gBAC0B,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM;IAa5D,OAAO,CAAC,OAAO;IAIf;;;OAGG;IACH,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI;IAcpD;;;;;OAKG;IACH,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IA8BxE;;;;;;;OAOG;IACH,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAQxC,OAAO,CAAC,iBAAiB;IAiBzB,0FAA0F;IAC1F,UAAU,IAAI,IAAI;IAWlB,IAAI,IAAI,iBAAiB,EAAE;IAY3B,cAAc,IAAI,OAAO;IAIzB;;;;;OAKG;IACH,OAAO,CAAC,YAAY,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI;IA0BnD,OAAO,CAAC,aAAa;IAsBrB,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,WAAW;IAuBnB,OAAO,CAAC,YAAY;IAepB,OAAO,CAAC,cAAc;IAItB,OAAO,CAAC,KAAK;IAOb,OAAO,CAAC,YAAY;CAkCrB"}
1
+ {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/checkpoint/manager.ts"],"names":[],"mappings":"AA+CA,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEpD;AAQD,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAMlE;AA4BD,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,0DAA0D;IAC1D,OAAO,EAAE,OAAO,CAAC;IACjB,8FAA8F;IAC9F,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,qEAAqE;IACrE,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,sFAAsF;IACtF,SAAS,EAAE,MAAM,CAAC;IAClB,sGAAsG;IACtG,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAMD,MAAM,WAAW,aAAa;IAC5B,4CAA4C;IAC5C,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,kFAAkF;IAClF,YAAY,EAAE,MAAM,CAAC;IACrB,6FAA6F;IAC7F,SAAS,EAAE,MAAM,CAAC;IAClB,6FAA6F;IAC7F,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B;AA6BD,qBAAa,iBAAiB;IAWhB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAVpC,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,OAAO,CAAqB;IACpC,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAElC;;;;OAIG;gBAC0B,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM;IAa5D,OAAO,CAAC,OAAO;IAIf;;;OAGG;IACH,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI;IAcpD;;;;;OAKG;IACH,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IA8BxE;;;;;;;OAOG;IACH,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAQxC,OAAO,CAAC,iBAAiB;IAiBzB,0FAA0F;IAC1F,UAAU,IAAI,IAAI;IAWlB,IAAI,IAAI,iBAAiB,EAAE;IAY3B,cAAc,IAAI,OAAO;IAIzB;;;;;OAKG;IACH,OAAO,CAAC,YAAY,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI;IA0BnD,OAAO,CAAC,aAAa;IAsBrB,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,WAAW;IAuBnB,OAAO,CAAC,YAAY;IAepB,OAAO,CAAC,cAAc;IAItB,OAAO,CAAC,KAAK;IAOb,OAAO,CAAC,YAAY;CAkCrB"}
@@ -68,6 +68,9 @@ const MAX_SNAPSHOT_BYTES = 2 * 1024 * 1024; // 2 MB/file — larger files are no
68
68
  const MAX_PERSISTED_TURNS = 50; // pruned oldest-first on commit
69
69
  const MUTATING_TOOLS = new Set([
70
70
  'write_file',
71
+ 'write_docx',
72
+ 'write_xlsx',
73
+ 'write_pptx',
71
74
  'edit_file',
72
75
  'multi_edit',
73
76
  'delete_file',
@@ -99,6 +102,9 @@ function str(v) {
99
102
  function affectedPaths(name, input) {
100
103
  switch (name) {
101
104
  case 'write_file':
105
+ case 'write_docx':
106
+ case 'write_xlsx':
107
+ case 'write_pptx':
102
108
  case 'edit_file':
103
109
  case 'multi_edit':
104
110
  case 'delete_file':
@@ -1 +1 @@
1
- {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/commands/loader.ts"],"names":[],"mappings":"AA6BA,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;CACrD;AAiFD,uFAAuF;AACvF,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,EAAE,CASjE;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,YAAY,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAG7F;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAuCnF;AAED,4FAA4F;AAC5F,wBAAgB,aAAa,CAAC,GAAG,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAE3F"}
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/commands/loader.ts"],"names":[],"mappings":"AA8BA,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;CACrD;AAsED,uFAAuF;AACvF,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,EAAE,CASjE;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,YAAY,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAG7F;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAuCnF;AAED,4FAA4F;AAC5F,wBAAgB,aAAa,CAAC,GAAG,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAE3F"}
@@ -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
@@ -26,4 +26,5 @@ export * from './permissions/destructive';
26
26
  export * from './plugins/index';
27
27
  export * from './plugins/installer';
28
28
  export * from './plugins/sources';
29
+ export * from './agent/trust';
29
30
  //# sourceMappingURL=index.d.ts.map
@@ -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,aAE3B,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"}
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'],
@@ -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
- /** Discover installed plugins (project scope shadows global on name clash). */
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
- /** Subdirectories of every installed plugin that hold `kind` assets (existing only). */
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;CAC7B;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,+EAA+E;AAC/E,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,EAAE,CAKzD;AAED,wFAAwF;AACxF,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,EAAE,CAMjG;AAED,2FAA2F;AAC3F,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAiBtE;AAED,iGAAiG;AACjG,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAezE"}
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"}
@@ -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
- /** Discover installed plugins (project scope shadows global on name clash). */
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
- /** Subdirectories of every installed plugin that hold `kind` assets (existing only). */
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;