@prometheus-ai/utils 0.5.4 → 0.5.8

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 ADDED
@@ -0,0 +1,14 @@
1
+ # Changelog
2
+
3
+ ## [Unreleased]
4
+
5
+ ## [0.5.8] - 2026-06-14
6
+
7
+ ### Fixed
8
+
9
+ - Fixed runtime module resolution so bare imports made by runtime-cache parents
10
+ stay inside the runtime cache before falling back to bundled or host modules.
11
+
12
+ ## [0.5.0] - 2026-06-11
13
+
14
+ - Establish Prometheus public baseline for the package.
package/README.md ADDED
@@ -0,0 +1,36 @@
1
+ # @prometheus-ai/utils
2
+
3
+ Shared utilities for Prometheus packages. Bun-first, small, and used across the agent runtime.
4
+
5
+ ## Notable modules
6
+
7
+ | Module | Purpose |
8
+ | --- | --- |
9
+ | `logger` | Centralized logger writing to `~/.prometheus/logs/` with rotation and TUI-safe output |
10
+ | `prompt` | Handlebars-based prompt templating and formatting helpers |
11
+ | `dirs` | Path helpers for Prometheus config directories (`~/.prometheus`, XDG-aware on Linux) |
12
+ | `stream` | `readStream` and `readLines` helpers over `ReadableStream` |
13
+ | `ptree` / `procmgr` | Process trees, `ChildProcess` wrapper, and process lifecycle management |
14
+ | `postmortem` | Cleanup callbacks on exit, signals, and fatal exceptions |
15
+ | `which` | `$which()` binary lookup with caching |
16
+ | `fetch-retry` | `fetch` with retry/backoff policies |
17
+ | `fs-error` | Errno guards such as `isEnoent` |
18
+ | `env` | Environment plumbing and worker-host entry contract (`workerHostEntry`) |
19
+ | `abortable` / `async` | AbortSignal-aware stream and promise helpers |
20
+ | `peek-file` | Read the first N bytes of a file with pooled buffers |
21
+ | `frontmatter`, `glob`, `mime`, `temp`, `format`, `color`, `snowflake`, `tab-spacing`, `path-tree`, `sanitize-text` | Smaller single-purpose helpers |
22
+
23
+ Import from the root barrel or per-module subpaths (`@prometheus-ai/utils/<module>`).
24
+
25
+ ## Install
26
+
27
+ ```sh
28
+ bun add @prometheus-ai/utils
29
+ ```
30
+
31
+ Ships TypeScript source directly and requires Bun 1.3.14 or newer.
32
+
33
+ ## References
34
+
35
+ - [Prometheus README](https://github.com/uttamtrivedi/Prometheus#readme)
36
+ - [CHANGELOG](./CHANGELOG.md)
@@ -4,6 +4,11 @@ export declare class AbortError extends Error {
4
4
  /**
5
5
  * Creates an abortable stream from a given stream and signal.
6
6
  *
7
+ * Unlike `stream.pipeThrough(..., { signal })`, this explicitly cancels the
8
+ * source reader when the signal aborts. That propagates HTTP-client disconnects
9
+ * and stream watchdog timeouts all the way to the backend request instead of
10
+ * only stopping the local consumer.
11
+ *
7
12
  * @param stream - The stream to make abortable
8
13
  * @param signal - The signal to abort the stream
9
14
  * @returns The abortable stream
@@ -105,6 +105,8 @@ export declare function getGithubCacheDbPath(): string;
105
105
  export declare function getAuthBrokerSnapshotCachePath(): string;
106
106
  /** Get the local FastEmbed model cache directory (~/.prometheus/cache/fastembed). */
107
107
  export declare function getFastembedCacheDir(): string;
108
+ /** Get the on-demand FastEmbed runtime install root (~/.prometheus/cache/fastembed-runtime). */
109
+ export declare function getFastembedRuntimeDir(): string;
108
110
  /** Get the natives directory (~/.prometheus/natives). */
109
111
  export declare function getNativesDir(): string;
110
112
  /** Get the stats database path (~/.prometheus/stats.db). */
@@ -119,6 +121,8 @@ export declare function getAutoresearchDbPath(encodedProject: string): string;
119
121
  export declare function getAutoresearchRunDir(encodedProject: string, runId: number): string;
120
122
  /** Get the path to agent.db (SQLite database for settings and auth storage). */
121
123
  export declare function getAgentDbPath(agentDir?: string): string;
124
+ /** Get the last-seen-changelog-version marker file (~/.prometheus/agent/last-changelog-version). */
125
+ export declare function getLastChangelogVersionPath(agentDir?: string): string;
122
126
  /** Get the path to history.db (SQLite database for session history). */
123
127
  export declare function getHistoryDbPath(agentDir?: string): string;
124
128
  /** Get the path to models.db (model cache database). */
@@ -52,4 +52,8 @@ export declare function isBunTestRuntime(): boolean;
52
52
  * first for cheap fast-path detection.
53
53
  */
54
54
  export declare function isCompiledBinary(): boolean;
55
+ /** Called by CLI entrypoints whose main module dispatches worker argv selectors. */
56
+ export declare function declareWorkerHostEntry(): void;
57
+ /** Main-module path of the self-dispatching CLI host, or null outside it. */
58
+ export declare function workerHostEntry(): string | null;
55
59
  export declare function $flag(name: string, def?: boolean): boolean;
@@ -43,6 +43,14 @@ export interface FetchWithRetryOptions extends RequestInit {
43
43
  * mock during tests.
44
44
  */
45
45
  fetch?: (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
46
+ /**
47
+ * Bun extension forwarded verbatim to the underlying `fetch` call. `false`
48
+ * disables Bun's native ~300s pre-response timeout (callers that own a
49
+ * configurable first-event/idle watchdog or an external `AbortSignal`
50
+ * supply this so the runtime ceiling cannot pre-empt them); a positive
51
+ * number sets a custom ceiling in ms. Bare browser/Node fetch ignores it.
52
+ */
53
+ timeout?: number | false;
46
54
  }
47
55
  /**
48
56
  * Fetch with bounded retries and sensible defaults. Retries on any
@@ -11,14 +11,17 @@ export * from "./glob";
11
11
  export * from "./hook-fetch";
12
12
  export * from "./json";
13
13
  export * as logger from "./logger";
14
+ export * from "./loop-phase";
14
15
  export * from "./mermaid-ascii";
15
16
  export * from "./mime";
17
+ export * from "./path-tree";
16
18
  export * from "./peek-file";
17
19
  export * as postmortem from "./postmortem";
18
20
  export * as procmgr from "./procmgr";
19
21
  export * as prompt from "./prompt";
20
22
  export * as ptree from "./ptree";
21
23
  export { AbortError, ChildProcess, Exception, NonZeroExitError } from "./ptree";
24
+ export * from "./runtime-install";
22
25
  export * from "./sanitize-text";
23
26
  export * from "./snowflake";
24
27
  export * from "./stream";
@@ -31,6 +31,17 @@ export declare function info(message: string, context?: Record<string, unknown>)
31
31
  * @param context - The context to log.
32
32
  */
33
33
  export declare function debug(message: string, context?: Record<string, unknown>): void;
34
+ /**
35
+ * Streaming startup markers, enabled by `PROMETHEUS_DEBUG_STARTUP`. Unlike the
36
+ * PROMETHEUS_TIMING tree (printed only after startup completes), these write one
37
+ * synchronous stderr line as each phase begins/ends, so a hard hang still
38
+ * shows the last phase that started. `fs.writeSync(2)` is used deliberately:
39
+ * it cannot be reordered or buffered past a synchronous block of the event
40
+ * loop (dlopen, sync fs on a dead mount, spawnSync).
41
+ */
42
+ export declare function startupMarker(text: string): void;
43
+ export declare function timingModeIncludes(option: "full" | "x"): boolean;
44
+ export declare function shouldExitAfterTimings(): boolean;
34
45
  /**
35
46
  * Print collected timings as an indented tree.
36
47
  * Each span shows wall duration; parents with children also show "(self)" for unattributed time.
@@ -39,20 +50,26 @@ export declare function debug(message: string, context?: Record<string, unknown>
39
50
  export declare function printTimings(): void;
40
51
  /**
41
52
  * Begin recording startup timings under a new root span.
42
- * Idempotent: a second call while already recording is a no-op so that side-effect
43
- * starters (see module-timer.ts) and explicit starters (main.ts) can coexist.
53
+ * Idempotent: a second call while already recording is a no-op, so an explicit
54
+ * starter (main.ts) and any future early starter can coexist.
44
55
  */
45
56
  export declare function startTiming(): void;
46
57
  /**
47
58
  * Record an externally-measured span as a leaf child of the active span (or root
48
- * when no span is active). Used by the module-load timing plugin to splice load
49
- * events into the tree retroactively.
59
+ * when no span is active). Used by {@link spliceModuleLoadBuffer} to fold
60
+ * preload-captured module windows into the tree.
50
61
  */
51
- export declare function recordModuleLoadSpan(path: string, start: number, durationMs: number): void;
62
+ export declare function recordModuleLoadSpan(path: string, start: number, durationMs: number, bodyMs?: number, imports?: string[]): void;
52
63
  /**
53
64
  * End timing window and clear buffers.
54
65
  */
55
66
  export declare function endTiming(): void;
67
+ /**
68
+ * Ops of the currently-open span chain (root → deepest), following the most
69
+ * recently started unfinished child at each level. Lets a startup watchdog
70
+ * name the phase a stalled startup is stuck in.
71
+ */
72
+ export declare function openSpanPath(): string[];
56
73
  /**
57
74
  * Time a span. Three forms:
58
75
  * time(op) — point event (zero-duration breadcrumb)
@@ -0,0 +1,10 @@
1
+ export declare function pushLoopPhase(label: string): void;
2
+ export declare function popLoopPhase(): void;
3
+ export declare function currentLoopPhase(): string | undefined;
4
+ /**
5
+ * Phase to blame for a just-detected loop block: the live top phase if one is
6
+ * still held, else the most recent phase pushed since the last call. Clears the
7
+ * recent slot so a block in a later, phase-less interval is not misattributed
8
+ * to a phase that already finished.
9
+ */
10
+ export declare function takeRecentLoopPhase(): string | undefined;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Multi-level path tree shared by grouped file listings (find / grep / ast tools)
3
+ * and compaction file-operation lists.
4
+ *
5
+ * Flat path lists used to group by the *immediate* parent directory and print the
6
+ * full directory path in every header. For results spread across a deep tree — or
7
+ * rooted outside cwd, where paths stay absolute — that repeated the shared prefix
8
+ * on every line. The tree below folds single-child directory chains (so the common
9
+ * prefix collapses into one header) and nests the rest, charging the model one
10
+ * token per path segment instead of one per file.
11
+ */
12
+ /** True for `scheme://…` entries that have no meaningful directory structure. */
13
+ export declare function isUrlLikePath(filePath: string): boolean;
14
+ export interface PathTreeNode {
15
+ /** Direct file leaves, in first-seen order. */
16
+ files: Array<{
17
+ name: string;
18
+ key: string;
19
+ }>;
20
+ /** Dedup set for `files` (a glob can surface the same path twice on retry). */
21
+ fileNames: Set<string>;
22
+ /** Child directories, in first-seen order. */
23
+ subdirs: Array<{
24
+ name: string;
25
+ node: PathTreeNode;
26
+ }>;
27
+ /** Dedup index for `subdirs`. */
28
+ dirIndex: Map<string, PathTreeNode>;
29
+ }
30
+ export interface PathTreeInput {
31
+ /** Path string; absolute, cwd-relative, or url-like. Backslashes are normalized. */
32
+ path: string;
33
+ /** Whether the leaf itself is a directory (trailing-slash match from find). */
34
+ isDir: boolean;
35
+ /** Opaque key carried onto file events for section lookup. Defaults to `path`. */
36
+ key?: string;
37
+ }
38
+ /** One node emitted while walking the tree: a folded directory or a file leaf. */
39
+ export interface GroupedTreeEvent {
40
+ kind: "dir" | "file";
41
+ /** 0-based nesting depth (root children are depth 0). */
42
+ depth: number;
43
+ /** Folded chain for dirs (e.g. `a/b/c`, no trailing slash); basename for files. */
44
+ name: string;
45
+ /** File key for `kind === "file"`; empty string for directories. */
46
+ key: string;
47
+ }
48
+ /**
49
+ * Build a directory tree from a flat list of paths. URL-like entries are kept
50
+ * whole as root-level file leaves (they have no meaningful directory structure).
51
+ * Absolute paths carry a leading empty segment so they share a common `/` root
52
+ * and fold like any other prefix.
53
+ */
54
+ export declare function buildPathTree(entries: Iterable<PathTreeInput>): PathTreeNode;
55
+ /**
56
+ * Depth-first walk yielding directory and file events. Directories collapse their
57
+ * single-child chains (`a` → `a/b` → `a/b/c`) so a shared prefix becomes one
58
+ * header. Each node's direct files are emitted before its subdirectories, keeping
59
+ * a file unambiguously attached to the header above it.
60
+ */
61
+ export declare function walkPathTree(node: PathTreeNode, depth?: number): Generator<GroupedTreeEvent>;
62
+ /**
63
+ * Render a flat path list as a grouped, prefix-folded directory tree without
64
+ * per-file bodies (find-tool output shape, also used by compaction `<files>`
65
+ * lists). Single-child directory chains fold into one header (`# a/b/c/`),
66
+ * each level adds one `#`, and files are listed bare under the deepest
67
+ * directory header that owns them. Trailing-slash entries are directory
68
+ * leaves and keep their slash in the header.
69
+ *
70
+ * `annotate` receives each file's full original path and its return value is
71
+ * appended verbatim to the file line (e.g. ` (RW)`).
72
+ *
73
+ * Order follows the input: a directory appears when its first member is
74
+ * emitted, and a node's own files precede its subdirectories.
75
+ */
76
+ export declare function formatGroupedPaths(paths: readonly string[], annotate?: (path: string) => string): string;
@@ -5,6 +5,10 @@ export interface ShellConfig {
5
5
  env: Record<string, string>;
6
6
  prefix: string | undefined;
7
7
  }
8
+ /**
9
+ * Check if a shell binary is executable.
10
+ */
11
+ export declare function isExecutable(path: string): boolean;
8
12
  /**
9
13
  * Resolve a basic shell (bash or sh) as fallback.
10
14
  */
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Walk a conditional `exports` target (string, array of fallbacks, or a
3
+ * condition object) and return the first relative path that matches a runtime
4
+ * condition in declaration order. Returns `null` when nothing applies (e.g.
5
+ * an `import`-only entry).
6
+ */
7
+ export declare function selectConditionalTarget(target: unknown): string | null;
8
+ /**
9
+ * Split a bare specifier into its package name and optional subpath, handling
10
+ * scoped packages (`@scope/name/sub` → `@scope/name` + `sub`).
11
+ */
12
+ export declare function splitBareSpecifier(specifier: string): {
13
+ packageName: string;
14
+ subpath: string | undefined;
15
+ };
16
+ /**
17
+ * Resolve a bare specifier against an installed `node_modules` directory,
18
+ * honoring `exports` (CommonJS conditions), then `main`, then `index.js`.
19
+ * Returns an absolute file path, or `null` when the package/entry is absent.
20
+ */
21
+ export declare function resolveRuntimeModule(runtimeNodeModules: string, specifier: string): string | null;
22
+ export interface RuntimeResolverOptions {
23
+ /** Absolute path to the runtime cache's `node_modules`. */
24
+ runtimeNodeModules: string;
25
+ /** Bare specifier → absolute file path overrides (e.g. `sharp` → no-op stub). */
26
+ stubs?: Record<string, string>;
27
+ }
28
+ /**
29
+ * Patch `node:module`'s resolver (idempotently) so bare specifiers that the
30
+ * stock compiled-binary resolver cannot find fall back to the registered
31
+ * runtime caches. Stock resolution is tried first and kept for anything
32
+ * outside the registered roots (bundled imports, node builtins, host or
33
+ * extension trees). Multiple runtime roots may register; they are consulted
34
+ * in registration order.
35
+ *
36
+ * One stock "success" is distrusted: the compiled-binary resolver ignores
37
+ * `main`/`exports` for real-FS packages (Bun #1763), so a package shipping
38
+ * its TS source next to `dist/` (e.g. `@huggingface/hub`'s root `index.ts`)
39
+ * resolves to the wrong file. When the stock hit lands inside a registered
40
+ * runtime root, the manifest-aware resolution wins.
41
+ */
42
+ export declare function installRuntimeModuleResolver({ runtimeNodeModules, stubs }: RuntimeResolverOptions): void;
43
+ /** Pinned dependency set materialized into a runtime cache directory. */
44
+ export interface RuntimeInstallSpec {
45
+ dependencies: Record<string, string>;
46
+ /** Version pins forced across the whole runtime tree (bun `overrides`), e.g. dislodging a transitive dep. */
47
+ overrides?: Record<string, string>;
48
+ /** Packages whose lifecycle scripts bun may run during the install. */
49
+ trustedDependencies?: string[];
50
+ }
51
+ export type RuntimeInstallPhase = "initiate" | "download" | "done";
52
+ export interface EnsureRuntimeInstalledOptions {
53
+ /** Directory owning the runtime `package.json` + `node_modules`. */
54
+ runtimeDir: string;
55
+ install: RuntimeInstallSpec;
56
+ /** Package whose installed manifest marks the runtime complete; defaults to the first dependency. */
57
+ probePackage?: string;
58
+ /** Phase notifications (progress UI); not emitted when already installed. */
59
+ onPhase?: (phase: RuntimeInstallPhase) => void;
60
+ lockAttempts?: number;
61
+ lockSleepMs?: number;
62
+ }
63
+ export declare function writeRuntimeManifest(runtimeDir: string, install: RuntimeInstallSpec): Promise<void>;
64
+ /**
65
+ * Materialize a pinned dependency set into `runtimeDir` (idempotent,
66
+ * cross-process safe via a lock directory). Returns `runtimeDir`.
67
+ */
68
+ export declare function ensureRuntimeInstalled(options: EnsureRuntimeInstalledOptions): Promise<string>;
@@ -7,3 +7,19 @@ export declare function setDefaultTabWidth(width: number): void;
7
7
  * Visible tab width in columns for `file` (from `.editorconfig` + default), or the default when `file` is omitted.
8
8
  */
9
9
  export declare function getIndentation(file?: string | null, projectDir?: string | null): number;
10
+ /**
11
+ * `.editorconfig`-derived formatting options for an LSP `textDocument/formatting` request.
12
+ *
13
+ * Both fields are absent when the resolved `.editorconfig` chain does not pin them, so callers
14
+ * can layer their own fallbacks underneath.
15
+ */
16
+ export interface EditorConfigFormatting {
17
+ /** Effective indent width in columns, from `indent_size` or `tab_width`. */
18
+ tabSize?: number;
19
+ /** `true` for `indent_style = space`, `false` for `indent_style = tab` or `indent_size = tab`. */
20
+ insertSpaces?: boolean;
21
+ }
22
+ /**
23
+ * Resolve `.editorconfig` formatting hints for `file` without falling back to any default.
24
+ */
25
+ export declare function getEditorConfigFormatting(file?: string | null, projectDir?: string | null): EditorConfigFormatting;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Shared contract between the {@link module-timer} preload and {@link logger}'s
3
+ * timing tree. Kept in its own dependency-free module so the preload can import
4
+ * it without pulling in winston (via logger) and the logger can drain the buffer
5
+ * without importing the Bun-plugin preload.
6
+ */
7
+ export interface ModuleLoadEvent {
8
+ /** Absolute or Bun-resolved module path. */
9
+ path: string;
10
+ /** `performance.now()` timestamp captured at Bun `onLoad` entry. */
11
+ start: number;
12
+ /** Inclusive module window: `onLoad` entry → appended final marker. */
13
+ durationMs: number;
14
+ /** Own top-level body / TLA time: prepended body marker → appended final marker. */
15
+ bodyMs?: number;
16
+ /** Resolved static children imported by this module. */
17
+ imports: string[];
18
+ }
19
+ /** The append-only buffer the preload pushes into (created on first access). */
20
+ export declare function moduleLoadBuffer(): ModuleLoadEvent[];
21
+ /** Drain and return all buffered events, leaving the buffer empty. */
22
+ export declare function drainModuleLoadEvents(): ModuleLoadEvent[];
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@prometheus-ai/utils",
4
- "version": "0.5.4",
4
+ "version": "0.5.8",
5
5
  "description": "Shared utilities for Prometheus packages",
6
6
  "homepage": "https://prometheus.trivlab.com",
7
7
  "author": "Uttam Trivedi",
@@ -31,7 +31,7 @@
31
31
  "fmt": "biome format --write ."
32
32
  },
33
33
  "dependencies": {
34
- "@prometheus-ai/natives": "0.5.4",
34
+ "@prometheus-ai/natives": "0.5.8",
35
35
  "beautiful-mermaid": "^1.1.3",
36
36
  "handlebars": "^4.7.9",
37
37
  "winston": "^3.19.0",
@@ -45,6 +45,8 @@
45
45
  },
46
46
  "files": [
47
47
  "src",
48
+ "README.md",
49
+ "CHANGELOG.md",
48
50
  "dist/types"
49
51
  ],
50
52
  "exports": {
package/src/abortable.ts CHANGED
@@ -10,16 +10,101 @@ export class AbortError extends Error {
10
10
  }
11
11
  }
12
12
 
13
+ type AbortableStreamReadResult<T> = { done: true; value?: T } | { done: false; value: T };
14
+
15
+ interface AbortableStreamReader<T> {
16
+ read(): Promise<AbortableStreamReadResult<T>>;
17
+ cancel(reason?: unknown): Promise<void>;
18
+ releaseLock(): void;
19
+ }
20
+
13
21
  /**
14
22
  * Creates an abortable stream from a given stream and signal.
15
23
  *
24
+ * Unlike `stream.pipeThrough(..., { signal })`, this explicitly cancels the
25
+ * source reader when the signal aborts. That propagates HTTP-client disconnects
26
+ * and stream watchdog timeouts all the way to the backend request instead of
27
+ * only stopping the local consumer.
28
+ *
16
29
  * @param stream - The stream to make abortable
17
30
  * @param signal - The signal to abort the stream
18
31
  * @returns The abortable stream
19
32
  */
20
33
  export function createAbortableStream<T>(stream: ReadableStream<T>, signal?: AbortSignal): ReadableStream<T> {
21
34
  if (!signal) return stream;
22
- return stream.pipeThrough(new TransformStream<T, T>(), { signal });
35
+ let reader: AbortableStreamReader<T> | undefined;
36
+ let closed = false;
37
+ let onAbort: (() => void) | undefined;
38
+
39
+ const cleanup = () => {
40
+ if (onAbort) signal.removeEventListener("abort", onAbort);
41
+ onAbort = undefined;
42
+ const currentReader = reader;
43
+ reader = undefined;
44
+ try {
45
+ currentReader?.releaseLock();
46
+ } catch {}
47
+ };
48
+
49
+ const cancelReader = (reason: unknown): Promise<void> => {
50
+ if (closed) return Promise.resolve();
51
+ closed = true;
52
+ const currentReader = reader;
53
+ reader = undefined;
54
+ if (onAbort) signal.removeEventListener("abort", onAbort);
55
+ onAbort = undefined;
56
+ if (!currentReader) return Promise.resolve();
57
+ return currentReader
58
+ .cancel(reason)
59
+ .catch(() => {})
60
+ .finally(() => {
61
+ try {
62
+ currentReader.releaseLock();
63
+ } catch {}
64
+ });
65
+ };
66
+
67
+ return new ReadableStream<T>({
68
+ start(controller) {
69
+ reader = stream.getReader();
70
+ onAbort = () => {
71
+ void cancelReader(signal.reason);
72
+ try {
73
+ controller.error(new AbortError(signal));
74
+ } catch {}
75
+ };
76
+ if (signal.aborted) {
77
+ onAbort();
78
+ return;
79
+ }
80
+ signal.addEventListener("abort", onAbort, { once: true });
81
+ void (async () => {
82
+ try {
83
+ for (;;) {
84
+ const currentReader = reader;
85
+ if (!currentReader) return;
86
+ const { value, done } = await currentReader.read();
87
+ if (closed) return;
88
+ if (done) {
89
+ closed = true;
90
+ cleanup();
91
+ controller.close();
92
+ return;
93
+ }
94
+ controller.enqueue(value);
95
+ }
96
+ } catch (error) {
97
+ if (closed) return;
98
+ closed = true;
99
+ cleanup();
100
+ controller.error(signal.aborted ? new AbortError(signal) : error);
101
+ }
102
+ })();
103
+ },
104
+ cancel(reason) {
105
+ return cancelReader(reason);
106
+ },
107
+ });
23
108
  }
24
109
 
25
110
  /**
package/src/dirs.ts CHANGED
@@ -362,6 +362,11 @@ export function getFastembedCacheDir(): string {
362
362
  return dirs.rootSubdir(path.join("cache", "fastembed"), "cache");
363
363
  }
364
364
 
365
+ /** Get the on-demand FastEmbed runtime install root (~/.prometheus/cache/fastembed-runtime). */
366
+ export function getFastembedRuntimeDir(): string {
367
+ return dirs.rootSubdir(path.join("cache", "fastembed-runtime"), "cache");
368
+ }
369
+
365
370
  /** Get the natives directory (~/.prometheus/natives). */
366
371
  export function getNativesDir(): string {
367
372
  return dirs.rootSubdir("natives", "cache");
@@ -401,6 +406,11 @@ export function getAgentDbPath(agentDir?: string): string {
401
406
  return dirs.agentSubdir(agentDir, "agent.db", "data");
402
407
  }
403
408
 
409
+ /** Get the last-seen-changelog-version marker file (~/.prometheus/agent/last-changelog-version). */
410
+ export function getLastChangelogVersionPath(agentDir?: string): string {
411
+ return dirs.agentSubdir(agentDir, "last-changelog-version", "state");
412
+ }
413
+
404
414
  /** Get the path to history.db (SQLite database for session history). */
405
415
  export function getHistoryDbPath(agentDir?: string): string {
406
416
  return dirs.agentSubdir(agentDir, "history.db", "data");
package/src/env.ts CHANGED
@@ -154,6 +154,23 @@ export function isCompiledBinary(): boolean {
154
154
  return url.includes("$bunfs") || url.includes("~BUN") || url.includes("%7EBUN");
155
155
  }
156
156
 
157
+ /**
158
+ * Main-module path declared by self-dispatching CLI entrypoints. Worker spawn
159
+ * sites re-enter this module via `new Worker(entry, { argv })`, so source,
160
+ * npm bundle, and compiled binary installs all share one host entrypoint.
161
+ */
162
+ let workerHostMain: string | null = null;
163
+
164
+ /** Called by CLI entrypoints whose main module dispatches worker argv selectors. */
165
+ export function declareWorkerHostEntry(): void {
166
+ workerHostMain = Bun.main;
167
+ }
168
+
169
+ /** Main-module path of the self-dispatching CLI host, or null outside it. */
170
+ export function workerHostEntry(): string | null {
171
+ return workerHostMain;
172
+ }
173
+
157
174
  const TRUTHY: Dict<boolean> = {
158
175
  "1": true,
159
176
  Y: true,
@@ -129,6 +129,14 @@ export interface FetchWithRetryOptions extends RequestInit {
129
129
  * mock during tests.
130
130
  */
131
131
  fetch?: (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
132
+ /**
133
+ * Bun extension forwarded verbatim to the underlying `fetch` call. `false`
134
+ * disables Bun's native ~300s pre-response timeout (callers that own a
135
+ * configurable first-event/idle watchdog or an external `AbortSignal`
136
+ * supply this so the runtime ceiling cannot pre-empt them); a positive
137
+ * number sets a custom ceiling in ms. Bare browser/Node fetch ignores it.
138
+ */
139
+ timeout?: number | false;
132
140
  }
133
141
 
134
142
  const DEFAULT_MAX_DELAY_MS = 60_000;
package/src/index.ts CHANGED
@@ -11,14 +11,17 @@ export * from "./glob";
11
11
  export * from "./hook-fetch";
12
12
  export * from "./json";
13
13
  export * as logger from "./logger";
14
+ export * from "./loop-phase";
14
15
  export * from "./mermaid-ascii";
15
16
  export * from "./mime";
17
+ export * from "./path-tree";
16
18
  export * from "./peek-file";
17
19
  export * as postmortem from "./postmortem";
18
20
  export * as procmgr from "./procmgr";
19
21
  export * as prompt from "./prompt";
20
22
  export * as ptree from "./ptree";
21
23
  export { AbortError, ChildProcess, Exception, NonZeroExitError } from "./ptree";
24
+ export * from "./runtime-install";
22
25
  export * from "./sanitize-text";
23
26
  export * from "./snowflake";
24
27
  export * from "./stream";