jeopi-utils 16.2.13 → 16.2.15
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/CHANGELOG.md +3 -1
- package/README.md +2 -2
- package/dist/types/dirs.d.ts +96 -67
- package/package.json +2 -2
- package/src/dirs.ts +142 -94
- package/src/logger.ts +1 -1
- package/src/procmgr.ts +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,13 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [16.2.14] - 2026-07-02
|
|
6
|
+
|
|
5
7
|
### Added
|
|
6
8
|
|
|
7
9
|
- Added `wrapFetchForExtraCa` and `withExtraCaFetch` utility functions to apply `NODE_EXTRA_CA_CERTS` to Bun's `RequestInit.tls.ca` configuration.
|
|
8
10
|
|
|
9
11
|
### Changed
|
|
10
12
|
|
|
11
|
-
- Renamed `APP_NAME` from `omp` to `jeopi`. `CONFIG_DIR_NAME`
|
|
13
|
+
- Renamed `APP_NAME` from `omp` to `jeopi`. `CONFIG_DIR_NAME` is now `.jeopi` (was `.omp`); XDG app directories and log-file prefixes now use `jeopi`. Added `LEGACY_CONFIG_DIR_NAME` (`.omp`), `hasUnmigratedLegacyConfigDir()`, and `migrateLegacyConfigDir()` to detect and move an existing `~/.omp` to `~/.jeopi` (exposed as `jeopi config migrate-legacy` in coding-agent) — never run automatically, so a live process never renames a directory another process might have open.
|
|
12
14
|
- The rotating log transport and crash-log path now derive their filename from `APP_NAME` (`jeopi.<DATE>.log`, `jeopi-crash.log`) instead of hardcoding `omp.*`, matching `getLogPath()`/`getDebugLogPath()` so path helpers and the writer agree.
|
|
13
15
|
|
|
14
16
|
## [16.2.9] - 2026-06-30
|
package/README.md
CHANGED
|
@@ -6,9 +6,9 @@ Shared utilities for [oh-my-pi](https://github.com/can1357/oh-my-pi) packages. Z
|
|
|
6
6
|
|
|
7
7
|
| Module | Purpose |
|
|
8
8
|
| --- | --- |
|
|
9
|
-
| `logger` | Centralized logger writing to `~/.
|
|
9
|
+
| `logger` | Centralized logger writing to `~/.jeopi/logs/` with rotation (TUI-safe — never stdout) |
|
|
10
10
|
| `prompt` | Handlebars-based prompt templating and formatting helpers |
|
|
11
|
-
| `dirs` | Path helpers for omp config directories (`~/.
|
|
11
|
+
| `dirs` | Path helpers for omp config directories (`~/.jeopi`, XDG-aware on Linux) |
|
|
12
12
|
| `stream` | `readStream` / `readLines` helpers over `ReadableStream` |
|
|
13
13
|
| `ptree` / `procmgr` | Process trees, `ChildProcess` wrapper, process lifecycle management |
|
|
14
14
|
| `postmortem` | Cleanup callbacks on exit, signals, and fatal exceptions |
|
package/dist/types/dirs.d.ts
CHANGED
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Centralized path helpers for
|
|
2
|
+
* Centralized path helpers for jeopi config directories.
|
|
3
3
|
*
|
|
4
|
-
* Uses PI_CONFIG_DIR (default ".
|
|
4
|
+
* Uses PI_CONFIG_DIR (default ".jeopi") for the config root and
|
|
5
5
|
* PI_CODING_AGENT_DIR to override the agent directory.
|
|
6
6
|
*
|
|
7
7
|
* On Linux, if XDG_DATA_HOME / XDG_STATE_HOME / XDG_CACHE_HOME environment
|
|
8
8
|
* variables are set, paths are redirected to XDG-compliant locations under
|
|
9
|
-
* $XDG_*_HOME/
|
|
9
|
+
* $XDG_*_HOME/jeopi/. This requires running `jeopi config init-xdg` first to
|
|
10
10
|
* move data to the new locations. No filesystem existence checks are performed
|
|
11
|
-
* — if the env var is set,
|
|
11
|
+
* — if the env var is set, jeopi trusts that the migration has been done.
|
|
12
12
|
*/
|
|
13
13
|
/** App name (e.g. "jeopi") */
|
|
14
14
|
export declare const APP_NAME: string;
|
|
15
|
-
/** Config directory name (e.g. ".
|
|
15
|
+
/** Config directory name (e.g. ".jeopi") */
|
|
16
16
|
export declare const CONFIG_DIR_NAME: string;
|
|
17
|
+
/** Pre-rebrand config directory name, kept only to detect and migrate unmigrated installs. */
|
|
18
|
+
export declare const LEGACY_CONFIG_DIR_NAME: string;
|
|
17
19
|
/** Version (e.g. "1.0.0") */
|
|
18
20
|
export declare const VERSION: string;
|
|
19
21
|
/** Minimum Bun version */
|
|
@@ -28,14 +30,14 @@ export declare const MIN_BUN_VERSION: string;
|
|
|
28
30
|
*/
|
|
29
31
|
export declare function normalizeProfileName(profile: string | undefined): string | undefined;
|
|
30
32
|
/**
|
|
31
|
-
* Resolve the active profile from the two profile env vars. `
|
|
32
|
-
* canonical variable and takes precedence; `PI_PROFILE` is the legacy
|
|
33
|
-
* compatibility fallback, consulted only when `
|
|
34
|
-
* explicitly-empty `
|
|
33
|
+
* Resolve the active profile from the two profile env vars. `JEOPI_PROFILE` is
|
|
34
|
+
* the canonical variable and takes precedence; `PI_PROFILE` is the legacy
|
|
35
|
+
* compatibility fallback, consulted only when `JEOPI_PROFILE` is undefined. An
|
|
36
|
+
* explicitly-empty `JEOPI_PROFILE` therefore selects the default profile rather
|
|
35
37
|
* than silently inheriting `PI_PROFILE`. Delegates validation/normalization to
|
|
36
38
|
* {@link normalizeProfileName} (which throws on a syntactically invalid value).
|
|
37
39
|
*/
|
|
38
|
-
export declare function resolveProfileEnv(
|
|
40
|
+
export declare function resolveProfileEnv(jeopi: string | undefined, pi: string | undefined): string | undefined;
|
|
39
41
|
export declare function resolveEquivalentPath(inputPath: string): string;
|
|
40
42
|
export declare function normalizePathForComparison(inputPath: string): string;
|
|
41
43
|
export declare function pathIsWithin(root: string, candidate: string): boolean;
|
|
@@ -51,9 +53,9 @@ export declare function setProjectDir(dir: string): void;
|
|
|
51
53
|
* working directory before {@link setProjectDir} throws on it.
|
|
52
54
|
*/
|
|
53
55
|
export declare function directoryExists(dir: string): Promise<boolean>;
|
|
54
|
-
/** Get the config directory name relative to home (e.g. ".
|
|
56
|
+
/** Get the config directory name relative to home (e.g. ".jeopi" or PI_CONFIG_DIR override). */
|
|
55
57
|
export declare function getConfigDirName(): string;
|
|
56
|
-
/** Get the config agent directory name relative to home (e.g. ".
|
|
58
|
+
/** Get the config agent directory name relative to home (e.g. ".jeopi/agent" or PI_CONFIG_DIR + "/agent"). */
|
|
57
59
|
export declare function getConfigAgentDirName(): string;
|
|
58
60
|
/**
|
|
59
61
|
* Rebuild the dirs resolver from the current environment, reusing the profile
|
|
@@ -66,8 +68,35 @@ export declare function getConfigAgentDirName(): string;
|
|
|
66
68
|
* `preProfileAgentDirEnv` snapshot is intentionally left untouched.
|
|
67
69
|
*/
|
|
68
70
|
export declare function refreshDirsFromEnv(): void;
|
|
69
|
-
/** Get the config root directory (~/.
|
|
71
|
+
/** Get the config root directory (~/.jeopi). */
|
|
70
72
|
export declare function getConfigRootDir(): string;
|
|
73
|
+
/**
|
|
74
|
+
* Whether an unmigrated pre-rebrand config directory exists at `~/.omp` while
|
|
75
|
+
* the new `~/.jeopi` root does not. Read-only — never renames anything. Used
|
|
76
|
+
* by CLI startup to print a one-time migration hint and by `{APP_NAME} config
|
|
77
|
+
* migrate-legacy` to decide whether there is anything to do.
|
|
78
|
+
*
|
|
79
|
+
* Always `false` when `PI_CONFIG_DIR` is set: an explicit override means the
|
|
80
|
+
* user already manages their own directory name and the legacy default is
|
|
81
|
+
* irrelevant.
|
|
82
|
+
*/
|
|
83
|
+
export declare function hasUnmigratedLegacyConfigDir(): boolean;
|
|
84
|
+
/**
|
|
85
|
+
* Rename `~/.omp` to `~/.jeopi` in place, carrying over auth, sessions,
|
|
86
|
+
* settings, and every profile in one atomic move. Explicit and opt-in
|
|
87
|
+
* (`{APP_NAME} config migrate-legacy`) — never invoked automatically at
|
|
88
|
+
* module load or from {@link DirResolver}, so a running process never renames
|
|
89
|
+
* a directory another concurrent process might have open.
|
|
90
|
+
*
|
|
91
|
+
* Throws if `PI_CONFIG_DIR` is set, if `~/.omp` does not exist, or if
|
|
92
|
+
* `~/.jeopi` already exists (refuses to clobber). Callers own reporting;
|
|
93
|
+
* this performs the single `fs.renameSync` and nothing else — no cache
|
|
94
|
+
* invalidation, since the resolver reads `os.homedir()` fresh on next use.
|
|
95
|
+
*/
|
|
96
|
+
export declare function migrateLegacyConfigDir(): {
|
|
97
|
+
from: string;
|
|
98
|
+
to: string;
|
|
99
|
+
};
|
|
71
100
|
/** Set the coding agent directory. Creates a fresh resolver, invalidating all cached paths. */
|
|
72
101
|
export declare function setAgentDir(dir: string): void;
|
|
73
102
|
/**
|
|
@@ -92,18 +121,18 @@ export declare function setProfile(profile: string | undefined): void;
|
|
|
92
121
|
export declare function getActiveProfile(): string | undefined;
|
|
93
122
|
/** Resolve the config root that backs a profile without activating it. */
|
|
94
123
|
export declare function getProfileRootDir(profile: string | undefined): string;
|
|
95
|
-
/** Get the agent config directory (~/.
|
|
124
|
+
/** Get the agent config directory (~/.jeopi/agent). */
|
|
96
125
|
export declare function getAgentDir(): string;
|
|
97
|
-
/** Get the project-local config directory (.
|
|
126
|
+
/** Get the project-local config directory (.jeopi). */
|
|
98
127
|
export declare function getProjectAgentDir(cwd?: string): string;
|
|
99
|
-
/** Get the reports directory (~/.
|
|
128
|
+
/** Get the reports directory (~/.jeopi/reports). */
|
|
100
129
|
export declare function getReportsDir(): string;
|
|
101
|
-
/** Get the logs directory (~/.
|
|
130
|
+
/** Get the logs directory (~/.jeopi/logs). */
|
|
102
131
|
export declare function getLogsDir(): string;
|
|
103
|
-
/** Get the path to a dated log file (~/.
|
|
132
|
+
/** Get the path to a dated log file (~/.jeopi/logs/jeopi.YYYY-MM-DD.log). */
|
|
104
133
|
export declare function getLogPath(date?: Date): string;
|
|
105
134
|
/**
|
|
106
|
-
* Get the plugins directory (~/.
|
|
135
|
+
* Get the plugins directory (~/.jeopi/plugins or its XDG equivalent).
|
|
107
136
|
*
|
|
108
137
|
* No-arg form (production callers) goes through the XDG-aware DirResolver so
|
|
109
138
|
* reads and writes always agree. The optional `home` parameter is for test
|
|
@@ -113,19 +142,19 @@ export declare function getLogPath(date?: Date): string;
|
|
|
113
142
|
* no-arg form — XDG semantics are preserved.
|
|
114
143
|
*/
|
|
115
144
|
export declare function getPluginsDir(home?: string): string;
|
|
116
|
-
/** Where npm installs packages (~/.
|
|
145
|
+
/** Where npm installs packages (~/.jeopi/plugins/node_modules). */
|
|
117
146
|
export declare function getPluginsNodeModules(home?: string): string;
|
|
118
|
-
/** Plugin manifest (~/.
|
|
147
|
+
/** Plugin manifest (~/.jeopi/plugins/package.json). */
|
|
119
148
|
export declare function getPluginsPackageJson(home?: string): string;
|
|
120
|
-
/** Plugin lock file (~/.
|
|
149
|
+
/** Plugin lock file (~/.jeopi/plugins/omp-plugins.lock.json). */
|
|
121
150
|
export declare function getPluginsLockfile(home?: string): string;
|
|
122
|
-
/** Get the remote mount directory (~/.
|
|
151
|
+
/** Get the remote mount directory (~/.jeopi/remote). */
|
|
123
152
|
export declare function getRemoteDir(): string;
|
|
124
153
|
/**
|
|
125
154
|
* Relocate the base directory for agent-managed worktrees (PR checkouts, task
|
|
126
|
-
* isolation, and `
|
|
155
|
+
* isolation, and `jeopi worktree` cleanup all read the same base). Driven by the
|
|
127
156
|
* `worktree.base` setting in coding-agent; pass `undefined`/empty to clear and
|
|
128
|
-
* fall back to `
|
|
157
|
+
* fall back to `JEOPI_WORKTREE_DIR` or the `~/.jeopi/wt` default.
|
|
129
158
|
*
|
|
130
159
|
* `~` is expanded and a relative path is rejected (see {@link resolveWorktreeBase}).
|
|
131
160
|
* Returns the absolute path that took effect, or `undefined` if the input was
|
|
@@ -135,21 +164,21 @@ export declare function getRemoteDir(): string;
|
|
|
135
164
|
export declare function setWorktreesDir(dir: string | undefined): string | undefined;
|
|
136
165
|
/**
|
|
137
166
|
* Get the agent-managed worktrees directory. Resolution order: the
|
|
138
|
-
* `
|
|
139
|
-
* `worktree.base` setting), then the `~/.
|
|
167
|
+
* `JEOPI_WORKTREE_DIR` env var, then the {@link setWorktreesDir} override (the
|
|
168
|
+
* `worktree.base` setting), then the `~/.jeopi/wt` default. The env var and the
|
|
140
169
|
* override are both `~`-expanded and must be absolute; a relative value is
|
|
141
170
|
* ignored and resolution falls through.
|
|
142
171
|
*/
|
|
143
172
|
export declare function getWorktreesDir(): string;
|
|
144
|
-
/** Get the SSH control socket directory (~/.
|
|
173
|
+
/** Get the SSH control socket directory (~/.jeopi/ssh-control). */
|
|
145
174
|
export declare function getSshControlDir(): string;
|
|
146
|
-
/** Get the remote host info directory (~/.
|
|
175
|
+
/** Get the remote host info directory (~/.jeopi/remote-host). */
|
|
147
176
|
export declare function getRemoteHostDir(): string;
|
|
148
|
-
/** Get the managed Python venv directory (~/.
|
|
177
|
+
/** Get the managed Python venv directory (~/.jeopi/python-env). */
|
|
149
178
|
export declare function getPythonEnvDir(): string;
|
|
150
|
-
/** Get the shared Python gateway state directory (~/.
|
|
179
|
+
/** Get the shared Python gateway state directory (~/.jeopi/agent/python-gateway; XDG default: $XDG_STATE_HOME/jeopi/python-gateway). */
|
|
151
180
|
export declare function getPythonGatewayDir(): string;
|
|
152
|
-
/** Get the puppeteer sandbox directory (~/.
|
|
181
|
+
/** Get the puppeteer sandbox directory (~/.jeopi/puppeteer). */
|
|
153
182
|
export declare function getPuppeteerDir(): string;
|
|
154
183
|
/** Get DOCS_RS cache directory () */
|
|
155
184
|
export declare function getDocsRsCacheDir(): string;
|
|
@@ -159,98 +188,98 @@ export declare function getAutoQaDbDir(): string;
|
|
|
159
188
|
* Stable 7-character hex digest of an absolute filesystem path.
|
|
160
189
|
*
|
|
161
190
|
* Used to pack the project identity into a single short fs-safe segment
|
|
162
|
-
* (e.g. PR-checkout and task-isolation worktree dirs under `~/.
|
|
191
|
+
* (e.g. PR-checkout and task-isolation worktree dirs under `~/.jeopi/wt/`).
|
|
163
192
|
* Bun.hash is non-cryptographic — collision space is ~2^28, which is fine
|
|
164
193
|
* for naming a handful of repos on a single machine. Same input on the
|
|
165
194
|
* same Bun runtime yields the same output.
|
|
166
195
|
*/
|
|
167
196
|
export declare function hashPath(absPath: string): string;
|
|
168
|
-
/** Get the path to a single worktree directory (~/.
|
|
197
|
+
/** Get the path to a single worktree directory (~/.jeopi/wt/<segment>). */
|
|
169
198
|
export declare function getWorktreeDir(segment: string): string;
|
|
170
|
-
/** Get the GPU cache path (~/.
|
|
199
|
+
/** Get the GPU cache path (~/.jeopi/gpu_cache.json). */
|
|
171
200
|
export declare function getGpuCachePath(): string;
|
|
172
201
|
/**
|
|
173
|
-
* Get the GitHub view cache database path (~/.
|
|
174
|
-
* Honors the `
|
|
202
|
+
* Get the GitHub view cache database path (~/.jeopi/cache/github-cache.db).
|
|
203
|
+
* Honors the `JEOPI_GITHUB_CACHE_DB` env var when set so tests can isolate the
|
|
175
204
|
* cache file without touching the rest of the config root.
|
|
176
205
|
*/
|
|
177
206
|
export declare function getGithubCacheDbPath(): string;
|
|
178
207
|
/**
|
|
179
|
-
* Get the encrypted auth-broker snapshot cache path (~/.
|
|
180
|
-
* Honors the `
|
|
208
|
+
* Get the encrypted auth-broker snapshot cache path (~/.jeopi/cache/auth-broker-snapshot.enc).
|
|
209
|
+
* Honors the `JEOPI_AUTH_BROKER_SNAPSHOT_CACHE` env var when set so tests and
|
|
181
210
|
* operators can isolate or relocate the cache file.
|
|
182
211
|
*/
|
|
183
212
|
export declare function getAuthBrokerSnapshotCachePath(): string;
|
|
184
|
-
/** Get the local FastEmbed model cache directory (~/.
|
|
213
|
+
/** Get the local FastEmbed model cache directory (~/.jeopi/cache/fastembed). */
|
|
185
214
|
export declare function getFastembedCacheDir(): string;
|
|
186
|
-
/** Get the on-demand fastembed runtime install root (~/.
|
|
215
|
+
/** Get the on-demand fastembed runtime install root (~/.jeopi/cache/fastembed-runtime). */
|
|
187
216
|
export declare function getFastembedRuntimeDir(): string;
|
|
188
|
-
/** Get the natives directory (~/.
|
|
217
|
+
/** Get the natives directory (~/.jeopi/natives). */
|
|
189
218
|
export declare function getNativesDir(): string;
|
|
190
|
-
/** Get the stats database path (~/.
|
|
219
|
+
/** Get the stats database path (~/.jeopi/stats.db). */
|
|
191
220
|
export declare function getStatsDbPath(): string;
|
|
192
|
-
/** Get the autoresearch state directory (~/.
|
|
221
|
+
/** Get the autoresearch state directory (~/.jeopi/autoresearch). */
|
|
193
222
|
export declare function getAutoresearchDir(): string;
|
|
194
|
-
/** Get the per-project autoresearch state directory (~/.
|
|
223
|
+
/** Get the per-project autoresearch state directory (~/.jeopi/autoresearch/<encoded-project>). */
|
|
195
224
|
export declare function getAutoresearchProjectDir(encodedProject: string): string;
|
|
196
|
-
/** Get the per-project autoresearch SQLite database path (~/.
|
|
225
|
+
/** Get the per-project autoresearch SQLite database path (~/.jeopi/autoresearch/<encoded-project>.db). */
|
|
197
226
|
export declare function getAutoresearchDbPath(encodedProject: string): string;
|
|
198
|
-
/** Get the per-run artifact directory (~/.
|
|
227
|
+
/** Get the per-run artifact directory (~/.jeopi/autoresearch/<encoded-project>/runs/<runId>). */
|
|
199
228
|
export declare function getAutoresearchRunDir(encodedProject: string, runId: number): string;
|
|
200
229
|
/** Get the path to agent.db (SQLite database for settings and auth storage). */
|
|
201
230
|
export declare function getAgentDbPath(agentDir?: string): string;
|
|
202
|
-
/** Get the last-seen-changelog-version marker file (~/.
|
|
231
|
+
/** Get the last-seen-changelog-version marker file (~/.jeopi/agent/last-changelog-version). */
|
|
203
232
|
export declare function getLastChangelogVersionPath(agentDir?: string): string;
|
|
204
233
|
/** Get the path to history.db (SQLite database for session history). */
|
|
205
234
|
export declare function getHistoryDbPath(agentDir?: string): string;
|
|
206
235
|
/** Get the path to models.db (model cache database). */
|
|
207
236
|
export declare function getModelDbPath(agentDir?: string): string;
|
|
208
|
-
/** Get the tiny title model cache directory (~/.
|
|
237
|
+
/** Get the tiny title model cache directory (~/.jeopi/agent/cache/tiny-models). */
|
|
209
238
|
export declare function getTinyModelsCacheDir(agentDir?: string): string;
|
|
210
|
-
/** Get the document conversion cache directory (~/.
|
|
239
|
+
/** Get the document conversion cache directory (~/.jeopi/agent/cache/document-conversions; XDG default: $XDG_CACHE_HOME/jeopi/cache/document-conversions). */
|
|
211
240
|
export declare function getDocumentConversionCacheDir(agentDir?: string): string;
|
|
212
|
-
/** Get the sessions directory (~/.
|
|
241
|
+
/** Get the sessions directory (~/.jeopi/agent/sessions). */
|
|
213
242
|
export declare function getSessionsDir(agentDir?: string): string;
|
|
214
|
-
/** Get the content-addressed blob store directory (~/.
|
|
243
|
+
/** Get the content-addressed blob store directory (~/.jeopi/agent/blobs). */
|
|
215
244
|
export declare function getBlobsDir(agentDir?: string): string;
|
|
216
|
-
/** Get the custom themes directory (~/.
|
|
245
|
+
/** Get the custom themes directory (~/.jeopi/agent/themes). */
|
|
217
246
|
export declare function getCustomThemesDir(agentDir?: string): string;
|
|
218
|
-
/** Get the tools directory (~/.
|
|
247
|
+
/** Get the tools directory (~/.jeopi/agent/tools). */
|
|
219
248
|
export declare function getToolsDir(agentDir?: string): string;
|
|
220
|
-
/** Get the slash commands directory (~/.
|
|
249
|
+
/** Get the slash commands directory (~/.jeopi/agent/commands). */
|
|
221
250
|
export declare function getCommandsDir(agentDir?: string): string;
|
|
222
|
-
/** Get the prompts directory (~/.
|
|
251
|
+
/** Get the prompts directory (~/.jeopi/agent/prompts). */
|
|
223
252
|
export declare function getPromptsDir(agentDir?: string): string;
|
|
224
|
-
/** Get the user-level Python modules directory (~/.
|
|
253
|
+
/** Get the user-level Python modules directory (~/.jeopi/agent/modules). */
|
|
225
254
|
export declare function getAgentModulesDir(agentDir?: string): string;
|
|
226
|
-
/** Get the memories directory (~/.
|
|
255
|
+
/** Get the memories directory (~/.jeopi/agent/memories). */
|
|
227
256
|
export declare function getMemoriesDir(agentDir?: string): string;
|
|
228
|
-
/** Get the terminal sessions directory (~/.
|
|
257
|
+
/** Get the terminal sessions directory (~/.jeopi/agent/terminal-sessions). */
|
|
229
258
|
export declare function getTerminalSessionsDir(agentDir?: string): string;
|
|
230
|
-
/** Get the crash log path (~/.
|
|
259
|
+
/** Get the crash log path (~/.jeopi/agent/jeopi-crash.log). */
|
|
231
260
|
export declare function getCrashLogPath(agentDir?: string): string;
|
|
232
|
-
/** Get the debug log path (~/.
|
|
261
|
+
/** Get the debug log path (~/.jeopi/agent/jeopi-debug.log). */
|
|
233
262
|
export declare function getDebugLogPath(agentDir?: string): string;
|
|
234
|
-
/** Get the project-level Python modules directory (.
|
|
263
|
+
/** Get the project-level Python modules directory (.jeopi/modules). */
|
|
235
264
|
export declare function getProjectModulesDir(cwd?: string): string;
|
|
236
|
-
/** Get the project-level prompts directory (.
|
|
265
|
+
/** Get the project-level prompts directory (.jeopi/prompts). */
|
|
237
266
|
export declare function getProjectPromptsDir(cwd?: string): string;
|
|
238
|
-
/** Get the project-level plugin overrides path (.
|
|
267
|
+
/** Get the project-level plugin overrides path (.jeopi/plugin-overrides.json). */
|
|
239
268
|
export declare function getProjectPluginOverridesPath(cwd?: string): string;
|
|
240
269
|
/** Get the primary MCP config file path (first candidate). */
|
|
241
270
|
export declare function getMCPConfigPath(scope: "user" | "project", cwd?: string): string;
|
|
242
271
|
/** Get the SSH config file path. */
|
|
243
272
|
export declare function getSSHConfigPath(scope: "user" | "project", cwd?: string): string;
|
|
244
273
|
/**
|
|
245
|
-
* Persistent per-install UUID stored at `~/.
|
|
274
|
+
* Persistent per-install UUID stored at `~/.jeopi/install-id`.
|
|
246
275
|
*
|
|
247
276
|
* Generated lazily on first call and persisted with `O_CREAT|O_EXCL` so
|
|
248
277
|
* concurrent first-call races don't clobber each other (loser re-reads the
|
|
249
278
|
* winner's id). Survives independently of agent state: deleting
|
|
250
|
-
* `~/.
|
|
279
|
+
* `~/.jeopi/agent/` does not regenerate it. Server-side dedup for grievance
|
|
251
280
|
* pushes (and similar telemetry) keys on this id.
|
|
252
281
|
*
|
|
253
|
-
* Anchored to the base config root (`~/.
|
|
282
|
+
* Anchored to the base config root (`~/.jeopi/install-id`) regardless of the
|
|
254
283
|
* active profile: install identity is per-install, not per-profile, so every
|
|
255
284
|
* profile shares one id and the global cache stays correct no matter the
|
|
256
285
|
* profile / `getInstallId` call order.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "jeopi-utils",
|
|
4
|
-
"version": "16.2.
|
|
4
|
+
"version": "16.2.15",
|
|
5
5
|
"description": "Shared utilities for pi packages",
|
|
6
6
|
"homepage": "https://github.com/akillness/jeopi",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"fmt": "biome format --write ."
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"jeopi-natives": "16.2.
|
|
34
|
+
"jeopi-natives": "16.2.15",
|
|
35
35
|
"handlebars": "^4.7.9",
|
|
36
36
|
"winston": "^3.19.0",
|
|
37
37
|
"winston-daily-rotate-file": "^5.0.0"
|
package/src/dirs.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Centralized path helpers for
|
|
2
|
+
* Centralized path helpers for jeopi config directories.
|
|
3
3
|
*
|
|
4
|
-
* Uses PI_CONFIG_DIR (default ".
|
|
4
|
+
* Uses PI_CONFIG_DIR (default ".jeopi") for the config root and
|
|
5
5
|
* PI_CODING_AGENT_DIR to override the agent directory.
|
|
6
6
|
*
|
|
7
7
|
* On Linux, if XDG_DATA_HOME / XDG_STATE_HOME / XDG_CACHE_HOME environment
|
|
8
8
|
* variables are set, paths are redirected to XDG-compliant locations under
|
|
9
|
-
* $XDG_*_HOME/
|
|
9
|
+
* $XDG_*_HOME/jeopi/. This requires running `jeopi config init-xdg` first to
|
|
10
10
|
* move data to the new locations. No filesystem existence checks are performed
|
|
11
|
-
* — if the env var is set,
|
|
11
|
+
* — if the env var is set, jeopi trusts that the migration has been done.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import * as fs from "node:fs";
|
|
@@ -19,8 +19,11 @@ import { engines, version } from "../package.json" with { type: "json" };
|
|
|
19
19
|
/** App name (e.g. "jeopi") */
|
|
20
20
|
export const APP_NAME: string = "jeopi";
|
|
21
21
|
|
|
22
|
-
/** Config directory name (e.g. ".
|
|
23
|
-
export const CONFIG_DIR_NAME: string = ".
|
|
22
|
+
/** Config directory name (e.g. ".jeopi") */
|
|
23
|
+
export const CONFIG_DIR_NAME: string = ".jeopi";
|
|
24
|
+
|
|
25
|
+
/** Pre-rebrand config directory name, kept only to detect and migrate unmigrated installs. */
|
|
26
|
+
export const LEGACY_CONFIG_DIR_NAME: string = ".omp";
|
|
24
27
|
|
|
25
28
|
/** Version (e.g. "1.0.0") */
|
|
26
29
|
export const VERSION: string = version;
|
|
@@ -29,7 +32,7 @@ export const VERSION: string = version;
|
|
|
29
32
|
export const MIN_BUN_VERSION: string = engines.bun.replace(/[^0-9.]/g, "");
|
|
30
33
|
|
|
31
34
|
const PROFILE_NAME_RE = /^[a-z0-9][a-z0-9._-]{0,63}$/;
|
|
32
|
-
const PROFILE_ENV_KEYS = ["
|
|
35
|
+
const PROFILE_ENV_KEYS = ["JEOPI_PROFILE", "PI_PROFILE"] as const;
|
|
33
36
|
|
|
34
37
|
/**
|
|
35
38
|
* Names Windows treats as reserved device aliases. Matches the basename
|
|
@@ -60,7 +63,7 @@ export function normalizeProfileName(profile: string | undefined): string | unde
|
|
|
60
63
|
WINDOWS_RESERVED_BASENAME_RE.test(normalized)
|
|
61
64
|
) {
|
|
62
65
|
throw new Error(
|
|
63
|
-
`Invalid
|
|
66
|
+
`Invalid jeopi profile "${profile}". Profile names must match ${PROFILE_NAME_RE.source}, ` +
|
|
64
67
|
`cannot be "." or "..", cannot end with ".", and cannot be a Windows reserved device name ` +
|
|
65
68
|
`(CON, PRN, AUX, NUL, COM0-9, LPT0-9, or any of those with an extension).`,
|
|
66
69
|
);
|
|
@@ -69,28 +72,28 @@ export function normalizeProfileName(profile: string | undefined): string | unde
|
|
|
69
72
|
}
|
|
70
73
|
|
|
71
74
|
/**
|
|
72
|
-
* Resolve the active profile from the two profile env vars. `
|
|
73
|
-
* canonical variable and takes precedence; `PI_PROFILE` is the legacy
|
|
74
|
-
* compatibility fallback, consulted only when `
|
|
75
|
-
* explicitly-empty `
|
|
75
|
+
* Resolve the active profile from the two profile env vars. `JEOPI_PROFILE` is
|
|
76
|
+
* the canonical variable and takes precedence; `PI_PROFILE` is the legacy
|
|
77
|
+
* compatibility fallback, consulted only when `JEOPI_PROFILE` is undefined. An
|
|
78
|
+
* explicitly-empty `JEOPI_PROFILE` therefore selects the default profile rather
|
|
76
79
|
* than silently inheriting `PI_PROFILE`. Delegates validation/normalization to
|
|
77
80
|
* {@link normalizeProfileName} (which throws on a syntactically invalid value).
|
|
78
81
|
*/
|
|
79
|
-
export function resolveProfileEnv(
|
|
80
|
-
return normalizeProfileName(
|
|
82
|
+
export function resolveProfileEnv(jeopi: string | undefined, pi: string | undefined): string | undefined {
|
|
83
|
+
return normalizeProfileName(jeopi !== undefined ? jeopi : pi);
|
|
81
84
|
}
|
|
82
85
|
|
|
83
86
|
function getProfileFromEnv(): string | undefined {
|
|
84
|
-
return resolveProfileEnv(process.env.
|
|
87
|
+
return resolveProfileEnv(process.env.JEOPI_PROFILE, process.env.PI_PROFILE);
|
|
85
88
|
}
|
|
86
89
|
|
|
87
90
|
/**
|
|
88
91
|
* Module-load profile resolution. Unlike {@link getProfileFromEnv}, an invalid
|
|
89
|
-
*
|
|
92
|
+
* JEOPI_PROFILE/PI_PROFILE value does NOT throw here — a bad env var must not
|
|
90
93
|
* crash a bare `import` of this module with an uncaught stack trace before the
|
|
91
94
|
* CLI's error handling is in scope. The default profile is used instead; the
|
|
92
95
|
* CLI re-validates the env (see `runCli` in coding-agent/src/cli.ts) so the
|
|
93
|
-
* user still gets a clean "Invalid
|
|
96
|
+
* user still gets a clean "Invalid jeopi profile" message.
|
|
94
97
|
*/
|
|
95
98
|
function readProfileFromEnvSafe(): string | undefined {
|
|
96
99
|
try {
|
|
@@ -199,12 +202,12 @@ export async function directoryExists(dir: string): Promise<boolean> {
|
|
|
199
202
|
}
|
|
200
203
|
}
|
|
201
204
|
|
|
202
|
-
/** Get the config directory name relative to home (e.g. ".
|
|
205
|
+
/** Get the config directory name relative to home (e.g. ".jeopi" or PI_CONFIG_DIR override). */
|
|
203
206
|
export function getConfigDirName(): string {
|
|
204
207
|
return process.env.PI_CONFIG_DIR || CONFIG_DIR_NAME;
|
|
205
208
|
}
|
|
206
209
|
|
|
207
|
-
/** Get the config agent directory name relative to home (e.g. ".
|
|
210
|
+
/** Get the config agent directory name relative to home (e.g. ".jeopi/agent" or PI_CONFIG_DIR + "/agent"). */
|
|
208
211
|
export function getConfigAgentDirName(): string {
|
|
209
212
|
const profile = getActiveProfile();
|
|
210
213
|
return profile ? path.join(getConfigDirName(), "profiles", profile, "agent") : `${getConfigDirName()}/agent`;
|
|
@@ -217,8 +220,8 @@ export function getConfigAgentDirName(): string {
|
|
|
217
220
|
type XdgCategory = "data" | "state" | "cache";
|
|
218
221
|
|
|
219
222
|
/**
|
|
220
|
-
* Resolves and caches all
|
|
221
|
-
* variables are set, paths are redirected under $XDG_*_HOME/
|
|
223
|
+
* Resolves and caches all jeopi directory paths. On Linux, when XDG environment
|
|
224
|
+
* variables are set, paths are redirected under $XDG_*_HOME/jeopi/. A new
|
|
222
225
|
* instance is created whenever the agent directory changes, which naturally
|
|
223
226
|
* invalidates all cached paths.
|
|
224
227
|
*/
|
|
@@ -227,7 +230,7 @@ class DirResolver {
|
|
|
227
230
|
readonly agentDir: string;
|
|
228
231
|
|
|
229
232
|
// Per-category base dirs. Without XDG, all three equal configRoot / agentDir.
|
|
230
|
-
// With XDG on Linux, they point to $XDG_*_HOME/
|
|
233
|
+
// With XDG on Linux, they point to $XDG_*_HOME/jeopi/.
|
|
231
234
|
readonly #rootDirs: Record<XdgCategory, string>;
|
|
232
235
|
readonly #agentDirs: Record<XdgCategory, string>;
|
|
233
236
|
|
|
@@ -244,14 +247,14 @@ class DirResolver {
|
|
|
244
247
|
const isDefault = this.agentDir === defaultAgent;
|
|
245
248
|
|
|
246
249
|
// XDG is a Linux convention. On supported platforms, default profile state
|
|
247
|
-
// resolves under $XDG_*_HOME/
|
|
250
|
+
// resolves under $XDG_*_HOME/jeopi once `jeopi config init-xdg` has migrated
|
|
248
251
|
// the user's data. Named profiles follow a stricter rule: the XDG choice
|
|
249
252
|
// is keyed on the profile-specific XDG path, never the base app root.
|
|
250
253
|
//
|
|
251
254
|
// Why: if we consulted the base app root for named profiles too, the same
|
|
252
|
-
// profile could resolve to `~/.
|
|
253
|
-
// (when no $XDG_*_HOME/
|
|
254
|
-
// `$XDG_*_HOME/
|
|
255
|
+
// profile could resolve to `~/.jeopi/profiles/<name>` on first activation
|
|
256
|
+
// (when no $XDG_*_HOME/jeopi exists yet) and then silently move to
|
|
257
|
+
// `$XDG_*_HOME/jeopi/profiles/<name>` the moment the base appeared, orphaning
|
|
255
258
|
// the earlier state. Pinning on the profile path means a profile's location
|
|
256
259
|
// is decided at first activation and stays put until the user explicitly
|
|
257
260
|
// migrates it (e.g. by mkdir'ing the XDG profile dir).
|
|
@@ -287,7 +290,7 @@ class DirResolver {
|
|
|
287
290
|
state: xdgState ?? this.configRoot,
|
|
288
291
|
cache: xdgCache ?? this.configRoot,
|
|
289
292
|
};
|
|
290
|
-
// XDG flattens the agent/ prefix: ~/.
|
|
293
|
+
// XDG flattens the agent/ prefix: ~/.jeopi/agent/sessions → $XDG_DATA_HOME/jeopi/sessions
|
|
291
294
|
this.#agentDirs = {
|
|
292
295
|
data: xdgData ?? this.agentDir,
|
|
293
296
|
state: xdgState ?? this.agentDir,
|
|
@@ -325,9 +328,9 @@ class DirResolver {
|
|
|
325
328
|
* (propagated by a parent's `setProfile`), so it must NOT be snapshotted as the
|
|
326
329
|
* default-mode baseline — otherwise default mode would resolve to the profile's
|
|
327
330
|
* agent dir. The profile source can be the active profile or a lower-priority
|
|
328
|
-
* `PI_PROFILE` that was bypassed because `
|
|
331
|
+
* `PI_PROFILE` that was bypassed because `JEOPI_PROFILE` explicitly selected the
|
|
329
332
|
* default profile. Returns `undefined` in those cases so reset falls back to the
|
|
330
|
-
* standard `~/.
|
|
333
|
+
* standard `~/.jeopi/agent`.
|
|
331
334
|
*/
|
|
332
335
|
function resolvePreProfileAgentDir(
|
|
333
336
|
profile: string | undefined,
|
|
@@ -362,7 +365,7 @@ let dirs = new DirResolver({
|
|
|
362
365
|
* unconditionally deleting the env var. Without the snapshot, a process started
|
|
363
366
|
* with `PI_CODING_AGENT_DIR=/custom` then `setProfile("work")` then
|
|
364
367
|
* `setProfile(undefined)` would silently lose `/custom` and fall back to
|
|
365
|
-
* `~/.
|
|
368
|
+
* `~/.jeopi/agent`. Captured at module load — ignoring a profile-derived value
|
|
366
369
|
* inherited from a parent's `setProfile` (see {@link resolvePreProfileAgentDir})
|
|
367
370
|
* — and refreshed on `setAgentDir`, since that call is the user explicitly
|
|
368
371
|
* redefining the baseline.
|
|
@@ -399,11 +402,56 @@ export function refreshDirsFromEnv(): void {
|
|
|
399
402
|
// Root directories
|
|
400
403
|
// =============================================================================
|
|
401
404
|
|
|
402
|
-
/** Get the config root directory (~/.
|
|
405
|
+
/** Get the config root directory (~/.jeopi). */
|
|
403
406
|
export function getConfigRootDir(): string {
|
|
404
407
|
return dirs.configRoot;
|
|
405
408
|
}
|
|
406
409
|
|
|
410
|
+
/**
|
|
411
|
+
* Whether an unmigrated pre-rebrand config directory exists at `~/.omp` while
|
|
412
|
+
* the new `~/.jeopi` root does not. Read-only — never renames anything. Used
|
|
413
|
+
* by CLI startup to print a one-time migration hint and by `{APP_NAME} config
|
|
414
|
+
* migrate-legacy` to decide whether there is anything to do.
|
|
415
|
+
*
|
|
416
|
+
* Always `false` when `PI_CONFIG_DIR` is set: an explicit override means the
|
|
417
|
+
* user already manages their own directory name and the legacy default is
|
|
418
|
+
* irrelevant.
|
|
419
|
+
*/
|
|
420
|
+
export function hasUnmigratedLegacyConfigDir(): boolean {
|
|
421
|
+
if (process.env.PI_CONFIG_DIR) return false;
|
|
422
|
+
const legacy = path.join(os.homedir(), LEGACY_CONFIG_DIR_NAME);
|
|
423
|
+
const current = path.join(os.homedir(), CONFIG_DIR_NAME);
|
|
424
|
+
return fs.existsSync(legacy) && !fs.existsSync(current);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Rename `~/.omp` to `~/.jeopi` in place, carrying over auth, sessions,
|
|
429
|
+
* settings, and every profile in one atomic move. Explicit and opt-in
|
|
430
|
+
* (`{APP_NAME} config migrate-legacy`) — never invoked automatically at
|
|
431
|
+
* module load or from {@link DirResolver}, so a running process never renames
|
|
432
|
+
* a directory another concurrent process might have open.
|
|
433
|
+
*
|
|
434
|
+
* Throws if `PI_CONFIG_DIR` is set, if `~/.omp` does not exist, or if
|
|
435
|
+
* `~/.jeopi` already exists (refuses to clobber). Callers own reporting;
|
|
436
|
+
* this performs the single `fs.renameSync` and nothing else — no cache
|
|
437
|
+
* invalidation, since the resolver reads `os.homedir()` fresh on next use.
|
|
438
|
+
*/
|
|
439
|
+
export function migrateLegacyConfigDir(): { from: string; to: string } {
|
|
440
|
+
if (process.env.PI_CONFIG_DIR) {
|
|
441
|
+
throw new Error("PI_CONFIG_DIR is set; migrate-legacy only handles the default config directory.");
|
|
442
|
+
}
|
|
443
|
+
const from = path.join(os.homedir(), LEGACY_CONFIG_DIR_NAME);
|
|
444
|
+
const to = path.join(os.homedir(), CONFIG_DIR_NAME);
|
|
445
|
+
if (!fs.existsSync(from)) {
|
|
446
|
+
throw new Error(`No legacy config directory found at ${from}.`);
|
|
447
|
+
}
|
|
448
|
+
if (fs.existsSync(to)) {
|
|
449
|
+
throw new Error(`Refusing to overwrite existing ${to}.`);
|
|
450
|
+
}
|
|
451
|
+
fs.renameSync(from, to);
|
|
452
|
+
return { from, to };
|
|
453
|
+
}
|
|
454
|
+
|
|
407
455
|
/** Set the coding agent directory. Creates a fresh resolver, invalidating all cached paths. */
|
|
408
456
|
export function setAgentDir(dir: string): void {
|
|
409
457
|
activeProfile = undefined;
|
|
@@ -461,7 +509,7 @@ export function setProfile(profile: string | undefined): void {
|
|
|
461
509
|
activeProfile = next;
|
|
462
510
|
if (activeProfile) {
|
|
463
511
|
dirs = new DirResolver({ profile: activeProfile });
|
|
464
|
-
process.env.
|
|
512
|
+
process.env.JEOPI_PROFILE = activeProfile;
|
|
465
513
|
process.env.PI_PROFILE = activeProfile;
|
|
466
514
|
process.env.PI_CODING_AGENT_DIR = dirs.agentDir;
|
|
467
515
|
} else {
|
|
@@ -486,37 +534,37 @@ export function getActiveProfile(): string | undefined {
|
|
|
486
534
|
export function getProfileRootDir(profile: string | undefined): string {
|
|
487
535
|
return getProfileConfigRoot(normalizeProfileName(profile));
|
|
488
536
|
}
|
|
489
|
-
/** Get the agent config directory (~/.
|
|
537
|
+
/** Get the agent config directory (~/.jeopi/agent). */
|
|
490
538
|
export function getAgentDir(): string {
|
|
491
539
|
return dirs.agentDir;
|
|
492
540
|
}
|
|
493
541
|
|
|
494
|
-
/** Get the project-local config directory (.
|
|
542
|
+
/** Get the project-local config directory (.jeopi). */
|
|
495
543
|
export function getProjectAgentDir(cwd: string = getProjectDir()): string {
|
|
496
544
|
return path.join(cwd, CONFIG_DIR_NAME);
|
|
497
545
|
}
|
|
498
546
|
|
|
499
547
|
// =============================================================================
|
|
500
|
-
// Config-root subdirectories (~/.
|
|
548
|
+
// Config-root subdirectories (~/.jeopi/*)
|
|
501
549
|
// =============================================================================
|
|
502
550
|
|
|
503
|
-
/** Get the reports directory (~/.
|
|
551
|
+
/** Get the reports directory (~/.jeopi/reports). */
|
|
504
552
|
export function getReportsDir(): string {
|
|
505
553
|
return dirs.rootSubdir("reports", "state");
|
|
506
554
|
}
|
|
507
555
|
|
|
508
|
-
/** Get the logs directory (~/.
|
|
556
|
+
/** Get the logs directory (~/.jeopi/logs). */
|
|
509
557
|
export function getLogsDir(): string {
|
|
510
558
|
return dirs.rootSubdir("logs", "state");
|
|
511
559
|
}
|
|
512
560
|
|
|
513
|
-
/** Get the path to a dated log file (~/.
|
|
561
|
+
/** Get the path to a dated log file (~/.jeopi/logs/jeopi.YYYY-MM-DD.log). */
|
|
514
562
|
export function getLogPath(date = new Date()): string {
|
|
515
563
|
return path.join(getLogsDir(), `${APP_NAME}.${date.toISOString().slice(0, 10)}.log`);
|
|
516
564
|
}
|
|
517
565
|
|
|
518
566
|
/**
|
|
519
|
-
* Get the plugins directory (~/.
|
|
567
|
+
* Get the plugins directory (~/.jeopi/plugins or its XDG equivalent).
|
|
520
568
|
*
|
|
521
569
|
* No-arg form (production callers) goes through the XDG-aware DirResolver so
|
|
522
570
|
* reads and writes always agree. The optional `home` parameter is for test
|
|
@@ -532,22 +580,22 @@ export function getPluginsDir(home?: string): string {
|
|
|
532
580
|
return dirs.rootSubdir("plugins", "data");
|
|
533
581
|
}
|
|
534
582
|
|
|
535
|
-
/** Where npm installs packages (~/.
|
|
583
|
+
/** Where npm installs packages (~/.jeopi/plugins/node_modules). */
|
|
536
584
|
export function getPluginsNodeModules(home?: string): string {
|
|
537
585
|
return path.join(getPluginsDir(home), "node_modules");
|
|
538
586
|
}
|
|
539
587
|
|
|
540
|
-
/** Plugin manifest (~/.
|
|
588
|
+
/** Plugin manifest (~/.jeopi/plugins/package.json). */
|
|
541
589
|
export function getPluginsPackageJson(home?: string): string {
|
|
542
590
|
return path.join(getPluginsDir(home), "package.json");
|
|
543
591
|
}
|
|
544
592
|
|
|
545
|
-
/** Plugin lock file (~/.
|
|
593
|
+
/** Plugin lock file (~/.jeopi/plugins/omp-plugins.lock.json). */
|
|
546
594
|
export function getPluginsLockfile(home?: string): string {
|
|
547
595
|
return path.join(getPluginsDir(home), "omp-plugins.lock.json");
|
|
548
596
|
}
|
|
549
597
|
|
|
550
|
-
/** Get the remote mount directory (~/.
|
|
598
|
+
/** Get the remote mount directory (~/.jeopi/remote). */
|
|
551
599
|
export function getRemoteDir(): string {
|
|
552
600
|
return dirs.rootSubdir("remote", "data");
|
|
553
601
|
}
|
|
@@ -557,8 +605,8 @@ export function getRemoteDir(): string {
|
|
|
557
605
|
* empty/whitespace input or a path that is still relative after expansion.
|
|
558
606
|
*
|
|
559
607
|
* A worktree base is process-global and consumed by both creation
|
|
560
|
-
* (PR checkout, task isolation) and cleanup (`
|
|
561
|
-
* would resolve against whatever cwd happened to launch `
|
|
608
|
+
* (PR checkout, task isolation) and cleanup (`jeopi worktree`). A relative value
|
|
609
|
+
* would resolve against whatever cwd happened to launch `jeopi`, so checkout and
|
|
562
610
|
* cleanup could disagree — we refuse it rather than silently bind it to cwd.
|
|
563
611
|
*/
|
|
564
612
|
function resolveWorktreeBase(value: string | undefined): string | undefined {
|
|
@@ -574,9 +622,9 @@ let worktreesDirOverride: string | undefined;
|
|
|
574
622
|
|
|
575
623
|
/**
|
|
576
624
|
* Relocate the base directory for agent-managed worktrees (PR checkouts, task
|
|
577
|
-
* isolation, and `
|
|
625
|
+
* isolation, and `jeopi worktree` cleanup all read the same base). Driven by the
|
|
578
626
|
* `worktree.base` setting in coding-agent; pass `undefined`/empty to clear and
|
|
579
|
-
* fall back to `
|
|
627
|
+
* fall back to `JEOPI_WORKTREE_DIR` or the `~/.jeopi/wt` default.
|
|
580
628
|
*
|
|
581
629
|
* `~` is expanded and a relative path is rejected (see {@link resolveWorktreeBase}).
|
|
582
630
|
* Returns the absolute path that took effect, or `undefined` if the input was
|
|
@@ -590,36 +638,36 @@ export function setWorktreesDir(dir: string | undefined): string | undefined {
|
|
|
590
638
|
|
|
591
639
|
/**
|
|
592
640
|
* Get the agent-managed worktrees directory. Resolution order: the
|
|
593
|
-
* `
|
|
594
|
-
* `worktree.base` setting), then the `~/.
|
|
641
|
+
* `JEOPI_WORKTREE_DIR` env var, then the {@link setWorktreesDir} override (the
|
|
642
|
+
* `worktree.base` setting), then the `~/.jeopi/wt` default. The env var and the
|
|
595
643
|
* override are both `~`-expanded and must be absolute; a relative value is
|
|
596
644
|
* ignored and resolution falls through.
|
|
597
645
|
*/
|
|
598
646
|
export function getWorktreesDir(): string {
|
|
599
|
-
return resolveWorktreeBase(process.env.
|
|
647
|
+
return resolveWorktreeBase(process.env.JEOPI_WORKTREE_DIR) ?? worktreesDirOverride ?? dirs.rootSubdir("wt", "data");
|
|
600
648
|
}
|
|
601
649
|
|
|
602
|
-
/** Get the SSH control socket directory (~/.
|
|
650
|
+
/** Get the SSH control socket directory (~/.jeopi/ssh-control). */
|
|
603
651
|
export function getSshControlDir(): string {
|
|
604
652
|
return dirs.rootSubdir("ssh-control", "state");
|
|
605
653
|
}
|
|
606
654
|
|
|
607
|
-
/** Get the remote host info directory (~/.
|
|
655
|
+
/** Get the remote host info directory (~/.jeopi/remote-host). */
|
|
608
656
|
export function getRemoteHostDir(): string {
|
|
609
657
|
return dirs.rootSubdir("remote-host", "data");
|
|
610
658
|
}
|
|
611
659
|
|
|
612
|
-
/** Get the managed Python venv directory (~/.
|
|
660
|
+
/** Get the managed Python venv directory (~/.jeopi/python-env). */
|
|
613
661
|
export function getPythonEnvDir(): string {
|
|
614
662
|
return dirs.rootSubdir("python-env", "data");
|
|
615
663
|
}
|
|
616
664
|
|
|
617
|
-
/** Get the shared Python gateway state directory (~/.
|
|
665
|
+
/** Get the shared Python gateway state directory (~/.jeopi/agent/python-gateway; XDG default: $XDG_STATE_HOME/jeopi/python-gateway). */
|
|
618
666
|
export function getPythonGatewayDir(): string {
|
|
619
667
|
return dirs.agentSubdir(undefined, "python-gateway", "state");
|
|
620
668
|
}
|
|
621
669
|
|
|
622
|
-
/** Get the puppeteer sandbox directory (~/.
|
|
670
|
+
/** Get the puppeteer sandbox directory (~/.jeopi/puppeteer). */
|
|
623
671
|
export function getPuppeteerDir(): string {
|
|
624
672
|
return dirs.rootSubdir("puppeteer", "cache");
|
|
625
673
|
}
|
|
@@ -637,7 +685,7 @@ export function getAutoQaDbDir(): string {
|
|
|
637
685
|
* Stable 7-character hex digest of an absolute filesystem path.
|
|
638
686
|
*
|
|
639
687
|
* Used to pack the project identity into a single short fs-safe segment
|
|
640
|
-
* (e.g. PR-checkout and task-isolation worktree dirs under `~/.
|
|
688
|
+
* (e.g. PR-checkout and task-isolation worktree dirs under `~/.jeopi/wt/`).
|
|
641
689
|
* Bun.hash is non-cryptographic — collision space is ~2^28, which is fine
|
|
642
690
|
* for naming a handful of repos on a single machine. Same input on the
|
|
643
691
|
* same Bun runtime yields the same output.
|
|
@@ -646,80 +694,80 @@ export function hashPath(absPath: string): string {
|
|
|
646
694
|
return Bun.hash(path.resolve(absPath)).toString(16).padStart(16, "0").slice(-7);
|
|
647
695
|
}
|
|
648
696
|
|
|
649
|
-
/** Get the path to a single worktree directory (~/.
|
|
697
|
+
/** Get the path to a single worktree directory (~/.jeopi/wt/<segment>). */
|
|
650
698
|
export function getWorktreeDir(segment: string): string {
|
|
651
699
|
return path.join(getWorktreesDir(), segment);
|
|
652
700
|
}
|
|
653
701
|
|
|
654
|
-
/** Get the GPU cache path (~/.
|
|
702
|
+
/** Get the GPU cache path (~/.jeopi/gpu_cache.json). */
|
|
655
703
|
export function getGpuCachePath(): string {
|
|
656
704
|
return dirs.rootSubdir("gpu_cache.json", "cache");
|
|
657
705
|
}
|
|
658
706
|
|
|
659
707
|
/**
|
|
660
|
-
* Get the GitHub view cache database path (~/.
|
|
661
|
-
* Honors the `
|
|
708
|
+
* Get the GitHub view cache database path (~/.jeopi/cache/github-cache.db).
|
|
709
|
+
* Honors the `JEOPI_GITHUB_CACHE_DB` env var when set so tests can isolate the
|
|
662
710
|
* cache file without touching the rest of the config root.
|
|
663
711
|
*/
|
|
664
712
|
export function getGithubCacheDbPath(): string {
|
|
665
|
-
const override = process.env.
|
|
713
|
+
const override = process.env.JEOPI_GITHUB_CACHE_DB;
|
|
666
714
|
if (override) return override;
|
|
667
715
|
return dirs.rootSubdir(path.join("cache", "github-cache.db"), "cache");
|
|
668
716
|
}
|
|
669
717
|
|
|
670
718
|
/**
|
|
671
|
-
* Get the encrypted auth-broker snapshot cache path (~/.
|
|
672
|
-
* Honors the `
|
|
719
|
+
* Get the encrypted auth-broker snapshot cache path (~/.jeopi/cache/auth-broker-snapshot.enc).
|
|
720
|
+
* Honors the `JEOPI_AUTH_BROKER_SNAPSHOT_CACHE` env var when set so tests and
|
|
673
721
|
* operators can isolate or relocate the cache file.
|
|
674
722
|
*/
|
|
675
723
|
export function getAuthBrokerSnapshotCachePath(): string {
|
|
676
|
-
const override = process.env.
|
|
724
|
+
const override = process.env.JEOPI_AUTH_BROKER_SNAPSHOT_CACHE;
|
|
677
725
|
if (override) return override;
|
|
678
726
|
return dirs.rootSubdir(path.join("cache", "auth-broker-snapshot.enc"), "cache");
|
|
679
727
|
}
|
|
680
728
|
|
|
681
|
-
/** Get the local FastEmbed model cache directory (~/.
|
|
729
|
+
/** Get the local FastEmbed model cache directory (~/.jeopi/cache/fastembed). */
|
|
682
730
|
export function getFastembedCacheDir(): string {
|
|
683
731
|
return dirs.rootSubdir(path.join("cache", "fastembed"), "cache");
|
|
684
732
|
}
|
|
685
733
|
|
|
686
|
-
/** Get the on-demand fastembed runtime install root (~/.
|
|
734
|
+
/** Get the on-demand fastembed runtime install root (~/.jeopi/cache/fastembed-runtime). */
|
|
687
735
|
export function getFastembedRuntimeDir(): string {
|
|
688
736
|
return dirs.rootSubdir(path.join("cache", "fastembed-runtime"), "cache");
|
|
689
737
|
}
|
|
690
738
|
|
|
691
|
-
/** Get the natives directory (~/.
|
|
739
|
+
/** Get the natives directory (~/.jeopi/natives). */
|
|
692
740
|
export function getNativesDir(): string {
|
|
693
741
|
return dirs.rootSubdir("natives", "cache");
|
|
694
742
|
}
|
|
695
743
|
|
|
696
|
-
/** Get the stats database path (~/.
|
|
744
|
+
/** Get the stats database path (~/.jeopi/stats.db). */
|
|
697
745
|
export function getStatsDbPath(): string {
|
|
698
746
|
return dirs.rootSubdir("stats.db", "data");
|
|
699
747
|
}
|
|
700
748
|
|
|
701
|
-
/** Get the autoresearch state directory (~/.
|
|
749
|
+
/** Get the autoresearch state directory (~/.jeopi/autoresearch). */
|
|
702
750
|
export function getAutoresearchDir(): string {
|
|
703
751
|
return dirs.rootSubdir("autoresearch", "state");
|
|
704
752
|
}
|
|
705
753
|
|
|
706
|
-
/** Get the per-project autoresearch state directory (~/.
|
|
754
|
+
/** Get the per-project autoresearch state directory (~/.jeopi/autoresearch/<encoded-project>). */
|
|
707
755
|
export function getAutoresearchProjectDir(encodedProject: string): string {
|
|
708
756
|
return path.join(getAutoresearchDir(), encodedProject);
|
|
709
757
|
}
|
|
710
758
|
|
|
711
|
-
/** Get the per-project autoresearch SQLite database path (~/.
|
|
759
|
+
/** Get the per-project autoresearch SQLite database path (~/.jeopi/autoresearch/<encoded-project>.db). */
|
|
712
760
|
export function getAutoresearchDbPath(encodedProject: string): string {
|
|
713
761
|
return path.join(getAutoresearchDir(), `${encodedProject}.db`);
|
|
714
762
|
}
|
|
715
763
|
|
|
716
|
-
/** Get the per-run artifact directory (~/.
|
|
764
|
+
/** Get the per-run artifact directory (~/.jeopi/autoresearch/<encoded-project>/runs/<runId>). */
|
|
717
765
|
export function getAutoresearchRunDir(encodedProject: string, runId: number): string {
|
|
718
766
|
return path.join(getAutoresearchProjectDir(encodedProject), "runs", String(runId).padStart(4, "0"));
|
|
719
767
|
}
|
|
720
768
|
|
|
721
769
|
// =============================================================================
|
|
722
|
-
// Agent subdirectories (~/.
|
|
770
|
+
// Agent subdirectories (~/.jeopi/agent/*)
|
|
723
771
|
// =============================================================================
|
|
724
772
|
|
|
725
773
|
/** Get the path to agent.db (SQLite database for settings and auth storage). */
|
|
@@ -727,7 +775,7 @@ export function getAgentDbPath(agentDir?: string): string {
|
|
|
727
775
|
return dirs.agentSubdir(agentDir, "agent.db", "data");
|
|
728
776
|
}
|
|
729
777
|
|
|
730
|
-
/** Get the last-seen-changelog-version marker file (~/.
|
|
778
|
+
/** Get the last-seen-changelog-version marker file (~/.jeopi/agent/last-changelog-version). */
|
|
731
779
|
export function getLastChangelogVersionPath(agentDir?: string): string {
|
|
732
780
|
return dirs.agentSubdir(agentDir, "last-changelog-version", "state");
|
|
733
781
|
}
|
|
@@ -742,86 +790,86 @@ export function getModelDbPath(agentDir?: string): string {
|
|
|
742
790
|
return dirs.agentSubdir(agentDir, "models.db", "data");
|
|
743
791
|
}
|
|
744
792
|
|
|
745
|
-
/** Get the tiny title model cache directory (~/.
|
|
793
|
+
/** Get the tiny title model cache directory (~/.jeopi/agent/cache/tiny-models). */
|
|
746
794
|
export function getTinyModelsCacheDir(agentDir?: string): string {
|
|
747
795
|
return dirs.agentSubdir(agentDir, path.join("cache", "tiny-models"), "cache");
|
|
748
796
|
}
|
|
749
797
|
|
|
750
|
-
/** Get the document conversion cache directory (~/.
|
|
798
|
+
/** Get the document conversion cache directory (~/.jeopi/agent/cache/document-conversions; XDG default: $XDG_CACHE_HOME/jeopi/cache/document-conversions). */
|
|
751
799
|
export function getDocumentConversionCacheDir(agentDir?: string): string {
|
|
752
800
|
return dirs.agentSubdir(agentDir, path.join("cache", "document-conversions"), "cache");
|
|
753
801
|
}
|
|
754
802
|
|
|
755
|
-
/** Get the sessions directory (~/.
|
|
803
|
+
/** Get the sessions directory (~/.jeopi/agent/sessions). */
|
|
756
804
|
export function getSessionsDir(agentDir?: string): string {
|
|
757
805
|
return dirs.agentSubdir(agentDir, "sessions", "data");
|
|
758
806
|
}
|
|
759
807
|
|
|
760
|
-
/** Get the content-addressed blob store directory (~/.
|
|
808
|
+
/** Get the content-addressed blob store directory (~/.jeopi/agent/blobs). */
|
|
761
809
|
export function getBlobsDir(agentDir?: string): string {
|
|
762
810
|
return dirs.agentSubdir(agentDir, "blobs", "data");
|
|
763
811
|
}
|
|
764
812
|
|
|
765
|
-
/** Get the custom themes directory (~/.
|
|
813
|
+
/** Get the custom themes directory (~/.jeopi/agent/themes). */
|
|
766
814
|
export function getCustomThemesDir(agentDir?: string): string {
|
|
767
815
|
return dirs.agentSubdir(agentDir, "themes");
|
|
768
816
|
}
|
|
769
817
|
|
|
770
|
-
/** Get the tools directory (~/.
|
|
818
|
+
/** Get the tools directory (~/.jeopi/agent/tools). */
|
|
771
819
|
export function getToolsDir(agentDir?: string): string {
|
|
772
820
|
return dirs.agentSubdir(agentDir, "tools");
|
|
773
821
|
}
|
|
774
822
|
|
|
775
|
-
/** Get the slash commands directory (~/.
|
|
823
|
+
/** Get the slash commands directory (~/.jeopi/agent/commands). */
|
|
776
824
|
export function getCommandsDir(agentDir?: string): string {
|
|
777
825
|
return dirs.agentSubdir(agentDir, "commands");
|
|
778
826
|
}
|
|
779
827
|
|
|
780
|
-
/** Get the prompts directory (~/.
|
|
828
|
+
/** Get the prompts directory (~/.jeopi/agent/prompts). */
|
|
781
829
|
export function getPromptsDir(agentDir?: string): string {
|
|
782
830
|
return dirs.agentSubdir(agentDir, "prompts");
|
|
783
831
|
}
|
|
784
832
|
|
|
785
|
-
/** Get the user-level Python modules directory (~/.
|
|
833
|
+
/** Get the user-level Python modules directory (~/.jeopi/agent/modules). */
|
|
786
834
|
export function getAgentModulesDir(agentDir?: string): string {
|
|
787
835
|
return dirs.agentSubdir(agentDir, "modules");
|
|
788
836
|
}
|
|
789
837
|
|
|
790
|
-
/** Get the memories directory (~/.
|
|
838
|
+
/** Get the memories directory (~/.jeopi/agent/memories). */
|
|
791
839
|
export function getMemoriesDir(agentDir?: string): string {
|
|
792
840
|
return dirs.agentSubdir(agentDir, "memories", "state");
|
|
793
841
|
}
|
|
794
842
|
|
|
795
|
-
/** Get the terminal sessions directory (~/.
|
|
843
|
+
/** Get the terminal sessions directory (~/.jeopi/agent/terminal-sessions). */
|
|
796
844
|
export function getTerminalSessionsDir(agentDir?: string): string {
|
|
797
845
|
return dirs.agentSubdir(agentDir, "terminal-sessions", "state");
|
|
798
846
|
}
|
|
799
847
|
|
|
800
|
-
/** Get the crash log path (~/.
|
|
848
|
+
/** Get the crash log path (~/.jeopi/agent/jeopi-crash.log). */
|
|
801
849
|
export function getCrashLogPath(agentDir?: string): string {
|
|
802
850
|
return dirs.agentSubdir(agentDir, `${APP_NAME}-crash.log`, "state");
|
|
803
851
|
}
|
|
804
852
|
|
|
805
|
-
/** Get the debug log path (~/.
|
|
853
|
+
/** Get the debug log path (~/.jeopi/agent/jeopi-debug.log). */
|
|
806
854
|
export function getDebugLogPath(agentDir?: string): string {
|
|
807
855
|
return dirs.agentSubdir(agentDir, `${APP_NAME}-debug.log`, "state");
|
|
808
856
|
}
|
|
809
857
|
|
|
810
858
|
// =============================================================================
|
|
811
|
-
// Project subdirectories (.
|
|
859
|
+
// Project subdirectories (.jeopi/*)
|
|
812
860
|
// =============================================================================
|
|
813
861
|
|
|
814
|
-
/** Get the project-level Python modules directory (.
|
|
862
|
+
/** Get the project-level Python modules directory (.jeopi/modules). */
|
|
815
863
|
export function getProjectModulesDir(cwd: string = getProjectDir()): string {
|
|
816
864
|
return path.join(getProjectAgentDir(cwd), "modules");
|
|
817
865
|
}
|
|
818
866
|
|
|
819
|
-
/** Get the project-level prompts directory (.
|
|
867
|
+
/** Get the project-level prompts directory (.jeopi/prompts). */
|
|
820
868
|
export function getProjectPromptsDir(cwd: string = getProjectDir()): string {
|
|
821
869
|
return path.join(getProjectAgentDir(cwd), "prompts");
|
|
822
870
|
}
|
|
823
871
|
|
|
824
|
-
/** Get the project-level plugin overrides path (.
|
|
872
|
+
/** Get the project-level plugin overrides path (.jeopi/plugin-overrides.json). */
|
|
825
873
|
export function getProjectPluginOverridesPath(cwd: string = getProjectDir()): string {
|
|
826
874
|
return path.join(getProjectAgentDir(cwd), "plugin-overrides.json");
|
|
827
875
|
}
|
|
@@ -856,15 +904,15 @@ const INSTALL_ID_FILE = "install-id";
|
|
|
856
904
|
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
857
905
|
|
|
858
906
|
/**
|
|
859
|
-
* Persistent per-install UUID stored at `~/.
|
|
907
|
+
* Persistent per-install UUID stored at `~/.jeopi/install-id`.
|
|
860
908
|
*
|
|
861
909
|
* Generated lazily on first call and persisted with `O_CREAT|O_EXCL` so
|
|
862
910
|
* concurrent first-call races don't clobber each other (loser re-reads the
|
|
863
911
|
* winner's id). Survives independently of agent state: deleting
|
|
864
|
-
* `~/.
|
|
912
|
+
* `~/.jeopi/agent/` does not regenerate it. Server-side dedup for grievance
|
|
865
913
|
* pushes (and similar telemetry) keys on this id.
|
|
866
914
|
*
|
|
867
|
-
* Anchored to the base config root (`~/.
|
|
915
|
+
* Anchored to the base config root (`~/.jeopi/install-id`) regardless of the
|
|
868
916
|
* active profile: install identity is per-install, not per-profile, so every
|
|
869
917
|
* profile shares one id and the global cache stays correct no matter the
|
|
870
918
|
* profile / `getInstallId` call order.
|
package/src/logger.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Centralized logger for jeopi.
|
|
3
3
|
*
|
|
4
|
-
* Default: rotating `~/.
|
|
4
|
+
* Default: rotating `~/.jeopi/logs/jeopi.<DATE>.log`, no console output (writing
|
|
5
5
|
* to stdout/stderr would corrupt the TUI). Long-running headless services
|
|
6
6
|
* (the auth broker, etc.) call {@link setTransports} to swap in a console
|
|
7
7
|
* transport so a process supervisor (pm2, journald, k8s) captures the logs.
|
package/src/procmgr.ts
CHANGED
|
@@ -113,7 +113,7 @@ export function getShellConfig(customShellPath?: string): ShellConfig {
|
|
|
113
113
|
return cachedShellConfig;
|
|
114
114
|
}
|
|
115
115
|
throw new Error(
|
|
116
|
-
`Custom shell path not found: ${customShellPath}\nPlease update shellPath in ~/.
|
|
116
|
+
`Custom shell path not found: ${customShellPath}\nPlease update shellPath in ~/.jeopi/agent/settings.json`,
|
|
117
117
|
);
|
|
118
118
|
}
|
|
119
119
|
|
|
@@ -147,7 +147,7 @@ export function getShellConfig(customShellPath?: string): ShellConfig {
|
|
|
147
147
|
`No bash shell found. Options:\n` +
|
|
148
148
|
` 1. Install Git for Windows: https://git-scm.com/download/win\n` +
|
|
149
149
|
` 2. Add your bash to PATH (Cygwin, MSYS2, etc.)\n` +
|
|
150
|
-
` 3. Set shellPath in ~/.
|
|
150
|
+
` 3. Set shellPath in ~/.jeopi/agent/settings.json\n\n` +
|
|
151
151
|
`Searched Git Bash in:\n${paths.map(p => ` ${p}`).join("\n")}`,
|
|
152
152
|
);
|
|
153
153
|
}
|