@jkwd/inbase 0.1.3 → 0.1.5

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/README.md CHANGED
@@ -1,4 +1,6 @@
1
- # Inbase
1
+ <p align="center">
2
+ <img src="docs/inbase-logo.png" alt="InBase — Dive into your codebase" width="520" />
3
+ </p>
2
4
 
3
5
  A first-person 3D map of a JavaScript or TypeScript codebase. Files become blocks, folders become walkable areas, and imports become lines in the air.
4
6
 
@@ -0,0 +1,6 @@
1
+ export function editorFileUri(filePath: string): string
2
+ export function defaultCursorUserDataDir(): string
3
+ export function discoverCursorUserDataDirs(): string[]
4
+ export function openFoldersFromStorage(storage: unknown): string[]
5
+ export function cursorUserDataDirForFile(filePath: string): string | null
6
+ export function openInEditor(filePath: string): boolean
@@ -0,0 +1,219 @@
1
+ import { spawnSync } from 'node:child_process'
2
+ import fs from 'node:fs'
3
+ import os from 'node:os'
4
+ import path from 'node:path'
5
+ import { fileURLToPath } from 'node:url'
6
+
7
+ export function editorFileUri(filePath) {
8
+ return `vscode://file${encodeURI(filePath)}`
9
+ }
10
+
11
+ function which(command) {
12
+ const result = spawnSync('which', [command], { encoding: 'utf8' })
13
+ if (result.error || result.status !== 0) return null
14
+ return result.stdout.trim().split('\n')[0] || null
15
+ }
16
+
17
+ export function defaultCursorUserDataDir() {
18
+ if (process.platform === 'darwin') {
19
+ return path.join(os.homedir(), 'Library/Application Support/Cursor')
20
+ }
21
+ if (process.platform === 'win32') {
22
+ return path.join(os.homedir(), 'AppData/Roaming/Cursor')
23
+ }
24
+ return path.join(os.homedir(), '.config/Cursor')
25
+ }
26
+
27
+ function expandUser(value) {
28
+ if (!value) return null
29
+ const trimmed = String(value).trim().replace(/^['"]|['"]$/g, '')
30
+ if (!trimmed) return null
31
+ if (trimmed.startsWith('~')) {
32
+ return path.resolve(os.homedir() + trimmed.slice(1))
33
+ }
34
+ return path.resolve(trimmed)
35
+ }
36
+
37
+ function isCursorUserDataDir(dir) {
38
+ try {
39
+ if (fs.existsSync(path.join(dir, 'User/globalStorage/storage.json'))) return true
40
+ return fs.readdirSync(dir).some((name) => name.endsWith('-main.sock'))
41
+ } catch {
42
+ return false
43
+ }
44
+ }
45
+
46
+ function userDataDirFromHook(hook = process.env.VSCODE_IPC_HOOK) {
47
+ if (!hook || !hook.endsWith('.sock')) return null
48
+ const dir = path.dirname(hook)
49
+ return isCursorUserDataDir(dir) ? dir : null
50
+ }
51
+
52
+ function userDataDirsFromProcessList() {
53
+ const result = spawnSync('ps', ['-ax', '-o', 'command='], { encoding: 'utf8' })
54
+ if (result.error || result.status !== 0) return []
55
+ const dirs = []
56
+ for (const line of result.stdout.split('\n')) {
57
+ const match = line.match(/--user-data-dir(?:=|\s+)(\S+)/)
58
+ if (!match) continue
59
+ const dir = expandUser(match[1])
60
+ if (dir && isCursorUserDataDir(dir)) dirs.push(dir)
61
+ }
62
+ return dirs
63
+ }
64
+
65
+ function profileDirsInHome() {
66
+ let names = []
67
+ try {
68
+ names = fs.readdirSync(os.homedir())
69
+ } catch {
70
+ return []
71
+ }
72
+ return names
73
+ .filter((name) => name.startsWith('cursor-profile'))
74
+ .map((name) => path.join(os.homedir(), name))
75
+ .filter(isCursorUserDataDir)
76
+ }
77
+
78
+ export function discoverCursorUserDataDirs() {
79
+ const dirs = new Set()
80
+ const explicit = expandUser(
81
+ process.env.INBASE_CURSOR_USER_DATA_DIR || process.env.CURSOR_USER_DATA_DIR,
82
+ )
83
+ if (explicit && isCursorUserDataDir(explicit)) dirs.add(explicit)
84
+ const hookDir = userDataDirFromHook()
85
+ if (hookDir) dirs.add(hookDir)
86
+ const def = defaultCursorUserDataDir()
87
+ if (isCursorUserDataDir(def)) dirs.add(def)
88
+ for (const dir of profileDirsInHome()) dirs.add(dir)
89
+ for (const dir of userDataDirsFromProcessList()) dirs.add(dir)
90
+ return [...dirs]
91
+ }
92
+
93
+ function folderPathFromUri(value) {
94
+ if (typeof value !== 'string' || !value) return null
95
+ try {
96
+ return value.startsWith('file:') ? fileURLToPath(value) : path.resolve(value)
97
+ } catch {
98
+ return null
99
+ }
100
+ }
101
+
102
+ function fileIsInside(folder, filePath) {
103
+ const root = path.resolve(folder)
104
+ const file = path.resolve(filePath)
105
+ return file === root || file.startsWith(`${root}${path.sep}`)
106
+ }
107
+
108
+ export function openFoldersFromStorage(storage) {
109
+ const state = storage?.windowsState
110
+ if (!state || typeof state !== 'object') return []
111
+ const windows = [
112
+ state.lastActiveWindow,
113
+ ...(Array.isArray(state.openedWindows) ? state.openedWindows : []),
114
+ ]
115
+ const folders = []
116
+ for (const window of windows) {
117
+ const folder = folderPathFromUri(window?.folder)
118
+ if (folder) folders.push(folder)
119
+ }
120
+ return folders
121
+ }
122
+
123
+ function readOpenFolders(userDataDir) {
124
+ const file = path.join(userDataDir, 'User/globalStorage/storage.json')
125
+ try {
126
+ return openFoldersFromStorage(JSON.parse(fs.readFileSync(file, 'utf8')))
127
+ } catch {
128
+ return []
129
+ }
130
+ }
131
+
132
+ function lastActiveFolder(userDataDir) {
133
+ const file = path.join(userDataDir, 'User/globalStorage/storage.json')
134
+ try {
135
+ const parsed = JSON.parse(fs.readFileSync(file, 'utf8'))
136
+ return folderPathFromUri(parsed?.windowsState?.lastActiveWindow?.folder)
137
+ } catch {
138
+ return null
139
+ }
140
+ }
141
+
142
+ export function cursorUserDataDirForFile(filePath) {
143
+ const explicit = expandUser(
144
+ process.env.INBASE_CURSOR_USER_DATA_DIR || process.env.CURSOR_USER_DATA_DIR,
145
+ )
146
+ if (explicit && isCursorUserDataDir(explicit)) return explicit
147
+
148
+ const hookDir = userDataDirFromHook()
149
+ let best = null
150
+ let bestScore = 0
151
+ for (const dir of discoverCursorUserDataDirs()) {
152
+ const folders = readOpenFolders(dir)
153
+ if (!folders.some((folder) => fileIsInside(folder, filePath))) continue
154
+ let score = 1
155
+ if (hookDir && path.resolve(hookDir) === path.resolve(dir)) score += 2
156
+ const last = lastActiveFolder(dir)
157
+ if (last && fileIsInside(last, filePath)) score += 2
158
+ if (score > bestScore) {
159
+ best = dir
160
+ bestScore = score
161
+ }
162
+ }
163
+ return best ?? hookDir
164
+ }
165
+
166
+ function cursorCliPaths() {
167
+ const home = os.homedir()
168
+ const fromEnv =
169
+ process.env.CURSOR_CLI && fs.existsSync(process.env.CURSOR_CLI)
170
+ ? process.env.CURSOR_CLI
171
+ : null
172
+ return [
173
+ fromEnv,
174
+ which('cursor'),
175
+ '/Applications/Cursor.app/Contents/Resources/app/bin/cursor',
176
+ path.join(home, '.local/bin/cursor'),
177
+ '/usr/local/bin/cursor',
178
+ '/opt/homebrew/bin/cursor',
179
+ ].filter((candidate, index, list) => {
180
+ if (!candidate || typeof candidate !== 'string') return false
181
+ return list.indexOf(candidate) === index
182
+ })
183
+ }
184
+
185
+ function editorEnv() {
186
+ const env = { ...process.env }
187
+ delete env.ELECTRON_RUN_AS_NODE
188
+ delete env.ELECTRON_NO_ASAR
189
+ delete env.CURSOR_AGENT
190
+ // A second Cursor instance uses its own sockets. Inherited hooks from
191
+ // another profile would send --goto to the wrong window.
192
+ delete env.VSCODE_IPC_HOOK
193
+ delete env.VSCODE_IPC_HOOK_CLI
194
+ return env
195
+ }
196
+
197
+ function runEditor(command, args) {
198
+ if (command.includes(path.sep) && !fs.existsSync(command)) return false
199
+ const result = spawnSync(command, args, {
200
+ stdio: 'ignore',
201
+ env: editorEnv(),
202
+ })
203
+ return !result.error && result.status === 0
204
+ }
205
+
206
+ export function openInEditor(filePath) {
207
+ const userDataDir = cursorUserDataDirForFile(filePath)
208
+ const goto = ['--goto', `${filePath}:1`]
209
+ if (userDataDir) {
210
+ goto.unshift('--user-data-dir', userDataDir)
211
+ }
212
+ for (const command of cursorCliPaths()) {
213
+ if (runEditor(command, goto)) return true
214
+ }
215
+
216
+ const code = which('code')
217
+ if (code && runEditor(code, goto)) return true
218
+ return false
219
+ }
@@ -45,10 +45,30 @@ export type DiffManifest = {
45
45
  export function assertSessionId(value: unknown): string
46
46
  export function readActiveSession(dataDir: string): string | null
47
47
  export function writeActiveSession(dataDir: string, sessionId: string | null): void
48
+ export function touchSessionConnection(dataDir: string, sessionId: string): void
49
+ export function isSessionConnected(
50
+ dataDir: string,
51
+ sessionId: string,
52
+ waiterIds?: Set<string>,
53
+ ): boolean
54
+ export function listStoredSessionIds(dataDir: string): string[]
55
+ export function listOpenSessionIds(dataDir: string): string[]
56
+ export function discardInactiveDiffSessions(
57
+ dataDir: string,
58
+ targetRoot?: string | null,
59
+ waiterIds?: Iterable<string>,
60
+ ): string[]
61
+ export function listSessionIntents(
62
+ dataDir: string,
63
+ knownFileIds?: string[],
64
+ ): Array<Record<string, unknown>>
48
65
  export function readBlueprintSession(dataDir: string): string | null
49
66
  export function writeBlueprintSession(dataDir: string, sessionId: string | null): void
50
67
  export function readManifest(dataDir: string, sessionId: string): DiffManifest | null
51
68
  export function writeManifest(dataDir: string, manifest: DiffManifest): void
69
+ export function isSessionStopped(dataDir: string, sessionId: string): boolean
70
+ export function isWorkflowStopped(dataDir: string, sessionId: string): boolean
71
+ export function sessionStoppedError(sessionId: string): Error
52
72
  export function startSession(
53
73
  dataDir: string,
54
74
  input: {
@@ -119,6 +139,36 @@ export function sessionIntent(
119
139
  knownFileIds?: string[],
120
140
  selectedDiffId?: string,
121
141
  ): Record<string, unknown> | null
142
+ export function resolveTargetFile(
143
+ targetRoot: string,
144
+ fileId: string,
145
+ ): { id: string; absolute: string }
146
+ export function captureBaseline(
147
+ dataDir: string,
148
+ sessionId: string,
149
+ targetRoot: string,
150
+ fileIds?: string[],
151
+ ): { files: Record<string, { existed: boolean }> }
152
+ export function restoreBaseline(
153
+ dataDir: string,
154
+ sessionId: string,
155
+ targetRoot: string,
156
+ ): void
157
+ export function materializeDiff(
158
+ dataDir: string,
159
+ targetRoot: string,
160
+ sessionId: string,
161
+ diffId?: string | null,
162
+ ): DiffManifest
163
+ export function inspectTargetFile(
164
+ dataDir: string,
165
+ targetRoot: string,
166
+ input?: {
167
+ sessionId?: string | null
168
+ diffId?: string | null
169
+ fileId?: string | null
170
+ },
171
+ ): string | null
122
172
  export function appendDiff(
123
173
  dataDir: string,
124
174
  targetRoot: string,
@@ -142,7 +192,7 @@ export function requestReplan(
142
192
  export function stopSession(
143
193
  dataDir: string,
144
194
  sessionId: string,
145
- diffId?: string,
195
+ targetRoot?: string | null,
146
196
  ): null
147
197
  export function decideDiff(
148
198
  dataDir: string,