@aixle/insights 0.2.1-staging → 0.2.1

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.
Files changed (44) hide show
  1. package/README.md +63 -15
  2. package/dist/auth/credentials.js +24 -8
  3. package/dist/auth/exchange.d.ts +1 -1
  4. package/dist/auth/exchange.js +1 -1
  5. package/dist/auth/flow.d.ts +1 -8
  6. package/dist/auth/flow.js +5 -26
  7. package/dist/auth/keycloak.d.ts +1 -1
  8. package/dist/auth/keycloak.js +1 -20
  9. package/dist/cli.d.ts +3 -5
  10. package/dist/cli.js +20 -68
  11. package/dist/collect-cursor-payloads.d.ts +3 -3
  12. package/dist/cursor-checkpoints.d.ts +2 -2
  13. package/dist/cursor-payload-contract.d.ts +5 -5
  14. package/dist/health.d.ts +0 -2
  15. package/dist/health.js +0 -12
  16. package/dist/hooks/cursor-hooks-mapper.d.ts +3 -3
  17. package/dist/hooks/cursor-hooks-mapper.js +1 -1
  18. package/dist/hooks/cursor-hooks-reader.js +8 -1
  19. package/dist/install/index.d.ts +4 -6
  20. package/dist/install/index.js +1 -6
  21. package/dist/lib/config.d.ts +2 -2
  22. package/dist/lib/config.js +28 -20
  23. package/dist/lib/parse-error.d.ts +21 -0
  24. package/dist/lib/parse-error.js +25 -0
  25. package/dist/lib/project-resolver.js +37 -3
  26. package/dist/lib/repo-path-safety.d.ts +35 -0
  27. package/dist/lib/repo-path-safety.js +102 -0
  28. package/dist/lib/spawn-arg-safety.d.ts +25 -0
  29. package/dist/lib/spawn-arg-safety.js +49 -0
  30. package/dist/lib/transport-security.d.ts +0 -1
  31. package/dist/lib/transport-security.js +1 -1
  32. package/dist/readers/claude.d.ts +7 -2
  33. package/dist/readers/claude.js +44 -15
  34. package/dist/readers/cursor.d.ts +7 -7
  35. package/dist/readers/cursor.js +27 -6
  36. package/dist/risk-scanner.js +7 -0
  37. package/dist/server.d.ts +0 -17
  38. package/dist/server.js +2 -25
  39. package/dist/state.js +38 -35
  40. package/dist/sync.d.ts +11 -2
  41. package/dist/sync.js +32 -14
  42. package/package.json +8 -4
  43. package/dist/install/cursor.d.ts +0 -34
  44. package/dist/install/cursor.js +0 -193
@@ -1,193 +0,0 @@
1
- import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync, copyFileSync, unlinkSync } from "node:fs";
2
- import { dirname, join } from "node:path";
3
- import { homedir } from "node:os";
4
- import { randomBytes } from "node:crypto";
5
- import { desiredAixleInsightsEntry, aixleInsightsEntryMatchesDesired } from "./claude.js";
6
- export function defaultCursorUserConfigPath() {
7
- return join(homedir(), ".cursor", "mcp.json");
8
- }
9
- /**
10
- * Legacy keys to clean up on install: "db90" (pre-rebrand) and "insights" (an
11
- * earlier local-dev-only convention documented in scripts/reset-local-env.mjs,
12
- * never the production installer's key — see DB90DV-560 orientation.md).
13
- */
14
- const LEGACY_MCP_KEYS = ["db90", "insights"];
15
- const AIXLE_INSIGHTS_MCP_KEY = "aixle-insights";
16
- const BACKUP_SUFFIX = ".aixle-insights-backup";
17
- function readRootObject(path) {
18
- if (!existsSync(path)) {
19
- return {};
20
- }
21
- try {
22
- const raw = readFileSync(path, "utf-8");
23
- if (!raw.trim()) {
24
- return {};
25
- }
26
- const parsed = JSON.parse(raw);
27
- if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
28
- return {
29
- kind: "error",
30
- message: `${path}: top-level JSON value must be an object.`,
31
- };
32
- }
33
- return parsed;
34
- }
35
- catch (e) {
36
- const msg = e instanceof Error ? e.message : String(e);
37
- return { kind: "error", message: `Cannot read ${path}: ${msg}` };
38
- }
39
- }
40
- function atomicWriteJson(path, data) {
41
- try {
42
- const dir = dirname(path);
43
- mkdirSync(dir, { recursive: true });
44
- const serialized = `${JSON.stringify(data, null, 2)}\n`;
45
- const tmpPath = join(dir, `.aixle-insights-cursor-json-${randomBytes(8).toString("hex")}.tmp`);
46
- writeFileSync(tmpPath, serialized, "utf-8");
47
- renameSync(tmpPath, path);
48
- return { kind: "installed" };
49
- }
50
- catch (e) {
51
- const msg = e instanceof Error ? e.message : String(e);
52
- return { kind: "error", message: `Failed to write ${path}: ${msg}` };
53
- }
54
- }
55
- /** Copy the current file to a backup path, unless a backup already exists (never clobber the user's original). */
56
- function backupIfNeeded(path) {
57
- if (!existsSync(path))
58
- return;
59
- const backupPath = path + BACKUP_SUFFIX;
60
- if (existsSync(backupPath))
61
- return;
62
- copyFileSync(path, backupPath);
63
- }
64
- /**
65
- * Merges top-level `mcpServers.aixle-insights` into ~/.cursor/mcp.json (or overridden path).
66
- * Preserves all other keys and MCP server entries. Backs up the existing file
67
- * (once) before the first write, and removes legacy "db90"/"insights" keys so
68
- * Cursor does not spawn duplicate aixle-insights servers.
69
- */
70
- export function installCursorUserMcp(options = {}) {
71
- const path = options.cursorConfigPath ?? defaultCursorUserConfigPath();
72
- const force = options.force === true;
73
- const desired = desiredAixleInsightsEntry();
74
- const rootRead = readRootObject(path);
75
- if ("kind" in rootRead && typeof rootRead.kind === "string" && rootRead.kind === "error") {
76
- return rootRead;
77
- }
78
- const root = {
79
- ...rootRead,
80
- };
81
- const rawServers = root["mcpServers"];
82
- let mcpServers;
83
- if (rawServers === undefined) {
84
- mcpServers = {};
85
- }
86
- else if (typeof rawServers === "object" &&
87
- rawServers !== null &&
88
- !Array.isArray(rawServers)) {
89
- mcpServers = { ...rawServers };
90
- }
91
- else {
92
- return {
93
- kind: "error",
94
- message: `Invalid Cursor MCP config at ${path}: "mcpServers" must be an object when present.`,
95
- };
96
- }
97
- const existing = mcpServers[AIXLE_INSIGHTS_MCP_KEY];
98
- const hasLegacyKey = LEGACY_MCP_KEYS.some((k) => mcpServers[k] !== undefined);
99
- if (existing !== undefined && aixleInsightsEntryMatchesDesired(existing, desired)) {
100
- if (!hasLegacyKey) {
101
- return { kind: "already-configured" };
102
- }
103
- backupIfNeeded(path);
104
- for (const key of LEGACY_MCP_KEYS)
105
- delete mcpServers[key];
106
- root["mcpServers"] = mcpServers;
107
- return atomicWriteJson(path, root);
108
- }
109
- if (existing !== undefined && !force) {
110
- return {
111
- kind: "requires-force",
112
- detail: 'A different "aixle-insights" MCP server entry already exists in ~/.cursor/mcp.json. Re-run with `init --force` to replace only that entry.',
113
- };
114
- }
115
- backupIfNeeded(path);
116
- for (const key of LEGACY_MCP_KEYS)
117
- delete mcpServers[key];
118
- mcpServers[AIXLE_INSIGHTS_MCP_KEY] = { command: desired.command, args: desired.args };
119
- root["mcpServers"] = mcpServers;
120
- return atomicWriteJson(path, root);
121
- }
122
- /**
123
- * Removes the aixle-insights entry from ~/.cursor/mcp.json without disturbing any
124
- * other server entry — including ones the user or Cursor added after install.
125
- * Works on the CURRENT file (never a full-file revert to the backup). If a
126
- * pre-install backup exists, the user's ORIGINAL aixle-insights entry (if any)
127
- * is restored from it; otherwise our key is simply removed. The backup file is
128
- * cleaned up either way.
129
- */
130
- export function uninstallCursorUserMcp(options = {}) {
131
- const path = options.cursorConfigPath ?? defaultCursorUserConfigPath();
132
- const backupPath = path + BACKUP_SUFFIX;
133
- const hasBackup = existsSync(backupPath);
134
- const cleanupBackup = () => {
135
- if (hasBackup) {
136
- try {
137
- unlinkSync(backupPath);
138
- }
139
- catch {
140
- /* non-fatal */
141
- }
142
- }
143
- };
144
- if (!existsSync(path)) {
145
- cleanupBackup();
146
- return { kind: "noop" };
147
- }
148
- const rootRead = readRootObject(path);
149
- if ("kind" in rootRead && typeof rootRead.kind === "string" && rootRead.kind === "error") {
150
- return rootRead;
151
- }
152
- const root = { ...rootRead };
153
- const rawServers = root["mcpServers"];
154
- if (typeof rawServers !== "object" || rawServers === null || Array.isArray(rawServers)) {
155
- cleanupBackup();
156
- return { kind: "noop" };
157
- }
158
- const mcpServers = { ...rawServers };
159
- // If a backup exists, recover the user's ORIGINAL aixle-insights entry (if they
160
- // had one we overwrote) rather than just deleting ours — but only that one key,
161
- // leaving every current sibling entry intact.
162
- let restoredPrior = false;
163
- if (hasBackup) {
164
- const backupRead = readRootObject(backupPath);
165
- const backupIsObject = !("kind" in backupRead &&
166
- typeof backupRead.kind === "string" &&
167
- backupRead.kind === "error");
168
- if (backupIsObject) {
169
- const backupServers = backupRead["mcpServers"];
170
- if (typeof backupServers === "object" && backupServers !== null && !Array.isArray(backupServers)) {
171
- const priorEntry = backupServers[AIXLE_INSIGHTS_MCP_KEY];
172
- if (priorEntry !== undefined) {
173
- mcpServers[AIXLE_INSIGHTS_MCP_KEY] = priorEntry;
174
- restoredPrior = true;
175
- }
176
- }
177
- }
178
- }
179
- if (!restoredPrior) {
180
- if (mcpServers[AIXLE_INSIGHTS_MCP_KEY] === undefined) {
181
- cleanupBackup();
182
- return { kind: "noop" };
183
- }
184
- delete mcpServers[AIXLE_INSIGHTS_MCP_KEY];
185
- }
186
- root["mcpServers"] = mcpServers;
187
- const writeResult = atomicWriteJson(path, root);
188
- if (writeResult.kind === "error") {
189
- return writeResult;
190
- }
191
- cleanupBackup();
192
- return restoredPrior ? { kind: "restored", backupPath } : { kind: "removed" };
193
- }