@oh-my-pi/pi-utils 17.2.2 → 17.2.4
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 +15 -0
- package/dist/types/dirs.d.ts +8 -0
- package/dist/types/env.d.ts +15 -1
- package/package.json +2 -2
- package/src/dirs.ts +44 -0
- package/src/env.ts +23 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [17.2.4] - 2026-08-01
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added `getSecretPlaceholderKeyPath()`, `getDaemonRuntimeDir()`, `getProviderInFlightRoot()`, and `getMarketplacesRegistryPath()` to resolve secret key, daemon runtime, provider in-flight, and marketplace registry paths under their respective XDG categories (state, data) instead of the config root.
|
|
10
|
+
- Existing installs enabling XDG keep their data: a legacy `~/.omp/agent/secret-placeholder.key` or `~/.omp/marketplaces.json` is copied to its XDG location on first resolution, so persisted transcripts still deobfuscate and added marketplaces survive the move.
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- Headless hosts (print/RPC/ACP/eval/SDK) now use a 1s SQLite `busy_timeout` for the session-critical databases (agent.db, history.db, stats.db) via `getDbBusyTimeoutMs()`, so lock contention no longer freezes the protocol loop for the full interactive 5s timeout; interactive hosts keep the 5s timeout.
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- Fixed Bun test-runtime detection treating application-owned `NODE_ENV=test` and `BUN_ENV=test` values as test-runner signals ([#7261](https://github.com/can1357/oh-my-pi/issues/7261)).
|
|
19
|
+
|
|
5
20
|
## [17.2.1] - 2026-07-30
|
|
6
21
|
|
|
7
22
|
### Added
|
package/dist/types/dirs.d.ts
CHANGED
|
@@ -237,6 +237,14 @@ export declare function getTerminalSessionsDir(agentDir?: string): string;
|
|
|
237
237
|
export declare function getCrashLogPath(agentDir?: string): string;
|
|
238
238
|
/** Get the debug log path (~/.omp/agent/omp-debug.log). */
|
|
239
239
|
export declare function getDebugLogPath(agentDir?: string): string;
|
|
240
|
+
/** Get the secret placeholder key path (~/.omp/agent/secret-placeholder.key; XDG default: $XDG_STATE_HOME/omp/secret-placeholder.key). Adopts a legacy key on first XDG resolution. */
|
|
241
|
+
export declare function getSecretPlaceholderKeyPath(): string;
|
|
242
|
+
/** Get the daemon runtime directory for a project (~/.omp/run/daemons/<hash>; XDG default: $XDG_STATE_HOME/omp/run/daemons/<hash>). */
|
|
243
|
+
export declare function getDaemonRuntimeDir(projectDir: string): string;
|
|
244
|
+
/** Get the provider in-flight root directory (~/.omp/run/provider-inflight; XDG default: $XDG_STATE_HOME/omp/run/provider-inflight). */
|
|
245
|
+
export declare function getProviderInFlightRoot(): string;
|
|
246
|
+
/** Get the marketplaces registry path (~/.omp/marketplaces.json; XDG default: $XDG_DATA_HOME/omp/marketplaces.json). Adopts a legacy registry on first XDG resolution. */
|
|
247
|
+
export declare function getMarketplacesRegistryPath(): string;
|
|
240
248
|
/** Get the project-level Python modules directory (.omp/modules). */
|
|
241
249
|
export declare function getProjectModulesDir(cwd?: string): string;
|
|
242
250
|
/** Get the project-level prompts directory (.omp/prompts). */
|
package/dist/types/env.d.ts
CHANGED
|
@@ -44,7 +44,7 @@ export declare function $pickenv(...keys: string[]): string | undefined;
|
|
|
44
44
|
* Empty, invalid, NaN, zero, or negative values return `defaultValue`.
|
|
45
45
|
*/
|
|
46
46
|
export declare function $envpos(name: string, defaultValue: number): number;
|
|
47
|
-
/** True when
|
|
47
|
+
/** True when the process is an explicitly marked test child or Bun is running a test entrypoint. */
|
|
48
48
|
export declare function isBunTestRuntime(): boolean;
|
|
49
49
|
/**
|
|
50
50
|
* True when real-terminal side effects must be suppressed: stdout escape/frame
|
|
@@ -77,6 +77,20 @@ export declare function isInteractiveHost(): boolean;
|
|
|
77
77
|
* restore exact prior state. See {@link isInteractiveHost}.
|
|
78
78
|
*/
|
|
79
79
|
export declare function setInteractiveHost(interactive: boolean): boolean;
|
|
80
|
+
/**
|
|
81
|
+
* SQLite `busy_timeout` for the session-critical databases (agent.db,
|
|
82
|
+
* history.db, stats.db).
|
|
83
|
+
*
|
|
84
|
+
* Interactive hosts tolerate a longer synchronous wait on lock contention
|
|
85
|
+
* (SQLITE_BUSY during WAL recovery/checkpoint — see oh-my-pi#2421): the
|
|
86
|
+
* operator sees a brief freeze and the statement eventually completes.
|
|
87
|
+
* Headless hosts (print/RPC/ACP/eval/SDK) run a protocol on the same thread —
|
|
88
|
+
* a multi-second synchronous busy-wait freezes their event loop and stalls
|
|
89
|
+
* every in-flight frame with no liveness signal, so they use a short timeout
|
|
90
|
+
* and rely on the existing asynchronous open/retry paths to recover from
|
|
91
|
+
* contention instead of blocking.
|
|
92
|
+
*/
|
|
93
|
+
export declare function getDbBusyTimeoutMs(): number;
|
|
80
94
|
/**
|
|
81
95
|
* True when this code is running inside a `bun build --compile` standalone
|
|
82
96
|
* binary. Detects via the embedded virtual-filesystem path markers
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/pi-utils",
|
|
4
|
-
"version": "17.2.
|
|
4
|
+
"version": "17.2.4",
|
|
5
5
|
"description": "Shared utilities for pi packages",
|
|
6
6
|
"homepage": "https://omp.sh",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"fmt": "biome format --write ."
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@oh-my-pi/pi-natives": "17.2.
|
|
34
|
+
"@oh-my-pi/pi-natives": "17.2.4",
|
|
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
|
@@ -820,6 +820,50 @@ export function getDebugLogPath(agentDir?: string): string {
|
|
|
820
820
|
return dirs.agentSubdir(agentDir, `${APP_NAME}-debug.log`, "state");
|
|
821
821
|
}
|
|
822
822
|
|
|
823
|
+
/**
|
|
824
|
+
* Best-effort one-time copy of a legacy config-root file to its redirected XDG
|
|
825
|
+
* location. Existing installs that enable XDG after the file was created keep
|
|
826
|
+
* their data (e.g. a placeholder key whose loss would break deobfuscation of
|
|
827
|
+
* persisted transcripts). The legacy file is left in place for older omp
|
|
828
|
+
* versions sharing the profile.
|
|
829
|
+
*/
|
|
830
|
+
function adoptLegacyFile(legacyPath: string, targetPath: string): void {
|
|
831
|
+
if (targetPath === legacyPath) return;
|
|
832
|
+
try {
|
|
833
|
+
if (fs.existsSync(targetPath) || !fs.existsSync(legacyPath)) return;
|
|
834
|
+
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
835
|
+
fs.copyFileSync(legacyPath, targetPath, fs.constants.COPYFILE_EXCL);
|
|
836
|
+
} catch {
|
|
837
|
+
// Opportunistic: a copy race or unwritable XDG dir falls back to a fresh
|
|
838
|
+
// file at the new path — the pre-adoption behavior.
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
/** Get the secret placeholder key path (~/.omp/agent/secret-placeholder.key; XDG default: $XDG_STATE_HOME/omp/secret-placeholder.key). Adopts a legacy key on first XDG resolution. */
|
|
843
|
+
export function getSecretPlaceholderKeyPath(): string {
|
|
844
|
+
const keyPath = dirs.agentSubdir(undefined, "secret-placeholder.key", "state");
|
|
845
|
+
adoptLegacyFile(path.join(dirs.agentDir, "secret-placeholder.key"), keyPath);
|
|
846
|
+
return keyPath;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
/** Get the daemon runtime directory for a project (~/.omp/run/daemons/<hash>; XDG default: $XDG_STATE_HOME/omp/run/daemons/<hash>). */
|
|
850
|
+
export function getDaemonRuntimeDir(projectDir: string): string {
|
|
851
|
+
const key = Bun.hash.wyhash(path.resolve(projectDir)).toString(16).padStart(16, "0");
|
|
852
|
+
return dirs.rootSubdir(path.join("run", "daemons", key), "state");
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
/** Get the provider in-flight root directory (~/.omp/run/provider-inflight; XDG default: $XDG_STATE_HOME/omp/run/provider-inflight). */
|
|
856
|
+
export function getProviderInFlightRoot(): string {
|
|
857
|
+
return dirs.rootSubdir(path.join("run", "provider-inflight"), "state");
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
/** Get the marketplaces registry path (~/.omp/marketplaces.json; XDG default: $XDG_DATA_HOME/omp/marketplaces.json). Adopts a legacy registry on first XDG resolution. */
|
|
861
|
+
export function getMarketplacesRegistryPath(): string {
|
|
862
|
+
const registryPath = dirs.rootSubdir("marketplaces.json", "data");
|
|
863
|
+
adoptLegacyFile(path.join(dirs.configRoot, "marketplaces.json"), registryPath);
|
|
864
|
+
return registryPath;
|
|
865
|
+
}
|
|
866
|
+
|
|
823
867
|
// =============================================================================
|
|
824
868
|
// Project subdirectories (.omp/*)
|
|
825
869
|
// =============================================================================
|
package/src/env.ts
CHANGED
|
@@ -258,9 +258,13 @@ export function $envpos(name: string, defaultValue: number): number {
|
|
|
258
258
|
return parsed;
|
|
259
259
|
}
|
|
260
260
|
|
|
261
|
-
|
|
261
|
+
const BUN_TEST_ENTRY_PATTERN = /[._](?:test|spec)\.[cm]?[jt]sx?$/;
|
|
262
|
+
|
|
263
|
+
/** True when the process is an explicitly marked test child or Bun is running a test entrypoint. */
|
|
262
264
|
export function isBunTestRuntime(): boolean {
|
|
263
|
-
|
|
265
|
+
if (Bun.env.PI_TEST_RUNTIME === "1") return true;
|
|
266
|
+
const hasTestEnvironment = Bun.env.BUN_ENV === "test" || Bun.env.NODE_ENV === "test";
|
|
267
|
+
return hasTestEnvironment && BUN_TEST_ENTRY_PATTERN.test(Bun.main);
|
|
264
268
|
}
|
|
265
269
|
|
|
266
270
|
let terminalHeadless = isBunTestRuntime();
|
|
@@ -314,6 +318,23 @@ export function setInteractiveHost(interactive: boolean): boolean {
|
|
|
314
318
|
return previous;
|
|
315
319
|
}
|
|
316
320
|
|
|
321
|
+
/**
|
|
322
|
+
* SQLite `busy_timeout` for the session-critical databases (agent.db,
|
|
323
|
+
* history.db, stats.db).
|
|
324
|
+
*
|
|
325
|
+
* Interactive hosts tolerate a longer synchronous wait on lock contention
|
|
326
|
+
* (SQLITE_BUSY during WAL recovery/checkpoint — see oh-my-pi#2421): the
|
|
327
|
+
* operator sees a brief freeze and the statement eventually completes.
|
|
328
|
+
* Headless hosts (print/RPC/ACP/eval/SDK) run a protocol on the same thread —
|
|
329
|
+
* a multi-second synchronous busy-wait freezes their event loop and stalls
|
|
330
|
+
* every in-flight frame with no liveness signal, so they use a short timeout
|
|
331
|
+
* and rely on the existing asynchronous open/retry paths to recover from
|
|
332
|
+
* contention instead of blocking.
|
|
333
|
+
*/
|
|
334
|
+
export function getDbBusyTimeoutMs(): number {
|
|
335
|
+
return isInteractiveHost() ? 5000 : 1000;
|
|
336
|
+
}
|
|
337
|
+
|
|
317
338
|
/**
|
|
318
339
|
* True when this code is running inside a `bun build --compile` standalone
|
|
319
340
|
* binary. Detects via the embedded virtual-filesystem path markers
|