@arnilo/prism-coding-agent 0.2.4 → 0.2.6
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 +16 -0
- package/README.md +10 -0
- package/dist/coding-checkpoint.js +4 -0
- package/dist/diagnostics.d.ts +83 -0
- package/dist/diagnostics.js +179 -0
- package/dist/git.d.ts +27 -6
- package/dist/git.js +58 -1
- package/dist/index.d.ts +13 -4
- package/dist/index.js +13 -2
- package/dist/language/client.d.ts +25 -0
- package/dist/language/client.js +54 -0
- package/dist/language/framing.d.ts +9 -1
- package/dist/language/framing.js +89 -17
- package/dist/language/index.d.ts +1 -1
- package/dist/language/intelligence.js +58 -0
- package/dist/language/types.d.ts +32 -0
- package/dist/limits.d.ts +59 -0
- package/dist/limits.js +59 -0
- package/dist/process/index.d.ts +4 -1
- package/dist/process/index.js +1 -0
- package/dist/process/recovery.d.ts +174 -0
- package/dist/process/recovery.js +320 -0
- package/dist/process/sessions.js +714 -25
- package/dist/process/types.d.ts +128 -4
- package/dist/process/types.js +7 -1
- package/dist/repository/glob.d.ts +4 -0
- package/dist/repository/glob.js +143 -0
- package/dist/repository/indexed-search.d.ts +121 -0
- package/dist/repository/indexed-search.js +313 -0
- package/dist/repository/list.d.ts +3 -0
- package/dist/repository/list.js +119 -0
- package/dist/repository/operations.d.ts +5 -0
- package/dist/repository/operations.js +14 -0
- package/dist/repository/path.d.ts +18 -0
- package/dist/repository/path.js +91 -0
- package/dist/repository/search.d.ts +9 -0
- package/dist/repository/search.js +284 -0
- package/dist/repository/types.d.ts +138 -0
- package/dist/repository/types.js +31 -0
- package/dist/repository/walk.d.ts +22 -0
- package/dist/repository/walk.js +99 -0
- package/dist/repository.d.ts +11 -172
- package/dist/repository.js +11 -748
- package/dist/review.d.ts +150 -0
- package/dist/review.js +222 -0
- package/dist/search.d.ts +3 -1
- package/dist/search.js +42 -7
- package/dist/workspace-lifecycle.d.ts +153 -0
- package/dist/workspace-lifecycle.js +629 -0
- package/package.json +3 -3
package/dist/process/types.d.ts
CHANGED
|
@@ -11,6 +11,8 @@ export interface ProcessSandboxHandle {
|
|
|
11
11
|
}): Promise<{
|
|
12
12
|
exitCode: number | null;
|
|
13
13
|
}>;
|
|
14
|
+
/** Opaque non-secret reattachment ref for durable process recovery (plan 026 Task 5). */
|
|
15
|
+
readonly ref?: string;
|
|
14
16
|
}
|
|
15
17
|
export interface ProcessSandboxStartRequest {
|
|
16
18
|
readonly file: string;
|
|
@@ -21,8 +23,7 @@ export interface ProcessSandboxStartRequest {
|
|
|
21
23
|
readonly signal?: AbortSignal;
|
|
22
24
|
readonly timeout?: number;
|
|
23
25
|
}
|
|
24
|
-
/**
|
|
25
|
-
* Optional sandbox backend for ProcessSessions.
|
|
26
|
+
/** Optional sandbox backend for ProcessSessions.
|
|
26
27
|
* Presence of `startProcess` = long-running capable; absence fails closed.
|
|
27
28
|
*/
|
|
28
29
|
export interface ProcessSandboxBackend {
|
|
@@ -31,8 +32,67 @@ export interface ProcessSandboxBackend {
|
|
|
31
32
|
readonly state: string;
|
|
32
33
|
}>;
|
|
33
34
|
}
|
|
35
|
+
/** Bounded terminal geometry + TERM for interactive PTY sessions (frozen caps in limits.ts). */
|
|
36
|
+
export interface ProcessTerminalRequest {
|
|
37
|
+
/** 1..maxTerminalColumns (default 120; host-configured max wins when smaller). */
|
|
38
|
+
readonly columns?: number;
|
|
39
|
+
/** 1..maxTerminalRows (default 40; host-configured max wins when smaller). */
|
|
40
|
+
readonly rows?: number;
|
|
41
|
+
/** TERM string, UTF-8 bytes <= maxTerminalTermBytes (default "xterm-256color"). */
|
|
42
|
+
readonly term?: string;
|
|
43
|
+
}
|
|
44
|
+
/** Bounded resize dimensions for an interactive PTY session. */
|
|
45
|
+
export interface ProcessTerminalResize {
|
|
46
|
+
readonly columns: number;
|
|
47
|
+
readonly rows: number;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Host PTY handle (mirrors ProcessSandboxHandle) plus optional bounded resize
|
|
51
|
+
* and backend metadata. Capability is explicit: the session handle exposes
|
|
52
|
+
* `resize` only when the backend declared `capabilities.resize`.
|
|
53
|
+
*/
|
|
54
|
+
export interface ProcessPtyHandle {
|
|
55
|
+
write(data: Uint8Array): Promise<void>;
|
|
56
|
+
signal(name: string): Promise<void>;
|
|
57
|
+
kill(): Promise<void>;
|
|
58
|
+
release(): Promise<void>;
|
|
59
|
+
resize?(dimensions: ProcessTerminalResize): Promise<void>;
|
|
60
|
+
/** Bounded host metadata (UTF-8 JSON bytes <= maxPtyBackendMetadataBytes), surfaced via session metadata(). */
|
|
61
|
+
readonly metadata?: Readonly<Record<string, string>>;
|
|
62
|
+
/** Opaque non-secret reattachment ref for durable process recovery (plan 026 Task 5). */
|
|
63
|
+
readonly ref?: string;
|
|
64
|
+
wait(options?: {
|
|
65
|
+
timeoutMs?: number;
|
|
66
|
+
signal?: AbortSignal;
|
|
67
|
+
}): Promise<{
|
|
68
|
+
exitCode: number | null;
|
|
69
|
+
}>;
|
|
70
|
+
}
|
|
71
|
+
export interface ProcessPtyStartRequest {
|
|
72
|
+
readonly file: string;
|
|
73
|
+
readonly args: readonly string[];
|
|
74
|
+
readonly cwd: string;
|
|
75
|
+
readonly env?: Readonly<Record<string, string>>;
|
|
76
|
+
readonly columns: number;
|
|
77
|
+
readonly rows: number;
|
|
78
|
+
readonly term: string;
|
|
79
|
+
readonly onData?: (data: Buffer) => void;
|
|
80
|
+
readonly signal?: AbortSignal;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Host-selected PTY backend. Capability and metadata are explicit, never
|
|
84
|
+
* duck-typed: `pty: true` requires a backend whose `startPty` is present
|
|
85
|
+
* (otherwise `ERR_PRISM_PROCESS_PTY_UNSUPPORTED` before process creation) and
|
|
86
|
+
* `capabilities.resize` must be true for the session handle to expose resize.
|
|
87
|
+
*/
|
|
88
|
+
export interface ProcessPtyBackend {
|
|
89
|
+
startPty?(request: ProcessPtyStartRequest): Promise<ProcessPtyHandle>;
|
|
90
|
+
readonly capabilities?: {
|
|
91
|
+
readonly resize: boolean;
|
|
92
|
+
};
|
|
93
|
+
}
|
|
34
94
|
export type ProcessSessionState = "starting" | "running" | "exited" | "killed" | "released" | "expired" | "unknown";
|
|
35
|
-
export type ProcessSessionErrorCode = "ERR_PRISM_PROCESS_POLICY" | "ERR_PRISM_PROCESS_OWNERSHIP" | "ERR_PRISM_PROCESS_STATE" | "ERR_PRISM_PROCESS_LIMIT" | "ERR_PRISM_PROCESS_PTY_UNSUPPORTED" | "ERR_PRISM_PROCESS_UNSUPPORTED";
|
|
95
|
+
export type ProcessSessionErrorCode = "ERR_PRISM_PROCESS_POLICY" | "ERR_PRISM_PROCESS_OWNERSHIP" | "ERR_PRISM_PROCESS_STATE" | "ERR_PRISM_PROCESS_LIMIT" | "ERR_PRISM_PROCESS_PTY_UNSUPPORTED" | "ERR_PRISM_PROCESS_PTY_BACKEND" | "ERR_PRISM_PROCESS_PTY_LIMIT" | "ERR_PRISM_PROCESS_UNSUPPORTED";
|
|
36
96
|
export declare class ProcessSessionError extends Error {
|
|
37
97
|
readonly code: ProcessSessionErrorCode;
|
|
38
98
|
constructor(code: ProcessSessionErrorCode, message: string);
|
|
@@ -43,6 +103,13 @@ export interface ProcessSessionLimits {
|
|
|
43
103
|
readonly maxLifetimeMs?: number;
|
|
44
104
|
readonly maxOutputChunkBytes?: number;
|
|
45
105
|
readonly maxTotalOutputBytes?: number;
|
|
106
|
+
/** Phase 26: PTY terminal bounds (defaults/hard caps frozen in limits.ts). */
|
|
107
|
+
readonly maxTerminalColumns?: number;
|
|
108
|
+
readonly maxTerminalRows?: number;
|
|
109
|
+
readonly maxTerminalTermBytes?: number;
|
|
110
|
+
readonly maxTerminalResizesPerMinute?: number;
|
|
111
|
+
readonly maxPtyAttachTimeoutMs?: number;
|
|
112
|
+
readonly maxPtyBackendMetadataBytes?: number;
|
|
46
113
|
}
|
|
47
114
|
export interface ResolvedProcessSessionLimits {
|
|
48
115
|
readonly maxSessions: number;
|
|
@@ -50,6 +117,12 @@ export interface ResolvedProcessSessionLimits {
|
|
|
50
117
|
readonly maxLifetimeMs: number;
|
|
51
118
|
readonly maxOutputChunkBytes: number;
|
|
52
119
|
readonly maxTotalOutputBytes: number;
|
|
120
|
+
readonly maxTerminalColumns: number;
|
|
121
|
+
readonly maxTerminalRows: number;
|
|
122
|
+
readonly maxTerminalTermBytes: number;
|
|
123
|
+
readonly maxTerminalResizesPerMinute: number;
|
|
124
|
+
readonly maxPtyAttachTimeoutMs: number;
|
|
125
|
+
readonly maxPtyBackendMetadataBytes: number;
|
|
53
126
|
}
|
|
54
127
|
export declare function resolveProcessSessionLimits(limits?: ProcessSessionLimits): ResolvedProcessSessionLimits;
|
|
55
128
|
export interface ProcessStartRequest {
|
|
@@ -57,13 +130,22 @@ export interface ProcessStartRequest {
|
|
|
57
130
|
readonly args?: readonly string[];
|
|
58
131
|
readonly cwd?: string;
|
|
59
132
|
readonly env?: Readonly<Record<string, string>>;
|
|
60
|
-
/**
|
|
133
|
+
/**
|
|
134
|
+
* Default false. true without a host ptyBackend.startPty →
|
|
135
|
+
* ERR_PRISM_PROCESS_PTY_UNSUPPORTED before process creation; backend
|
|
136
|
+
* failures → ERR_PRISM_PROCESS_PTY_BACKEND; terminal/resize/attach bounds →
|
|
137
|
+
* ERR_PRISM_PROCESS_PTY_LIMIT.
|
|
138
|
+
*/
|
|
61
139
|
readonly pty?: boolean;
|
|
140
|
+
/** Bounded terminal geometry + TERM for pty: true sessions. */
|
|
141
|
+
readonly terminal?: ProcessTerminalRequest;
|
|
62
142
|
readonly lifetimeMs?: number;
|
|
63
143
|
/** Owner string for this session; defaults to registry ownership key. */
|
|
64
144
|
readonly owner?: string;
|
|
65
145
|
/** When true, run cancel releases instead of killing (default kill). */
|
|
66
146
|
readonly releaseOnCancel?: boolean;
|
|
147
|
+
/** Abortable start: an aborted signal fails the start before spawn persists. */
|
|
148
|
+
readonly signal?: AbortSignal;
|
|
67
149
|
}
|
|
68
150
|
export interface ProcessOutputChunk {
|
|
69
151
|
readonly data: string;
|
|
@@ -85,6 +167,16 @@ export interface ProcessSessionMetadata {
|
|
|
85
167
|
readonly exitedAt?: string;
|
|
86
168
|
readonly state: ProcessSessionState;
|
|
87
169
|
readonly releaseOnCancel: boolean;
|
|
170
|
+
/** True when the session runs through the host ptyBackend. */
|
|
171
|
+
readonly pty: boolean;
|
|
172
|
+
/** Resolved terminal geometry + TERM for PTY sessions (bounded at start). */
|
|
173
|
+
readonly terminal?: {
|
|
174
|
+
readonly columns: number;
|
|
175
|
+
readonly rows: number;
|
|
176
|
+
readonly term: string;
|
|
177
|
+
};
|
|
178
|
+
/** Bounded backend metadata (validated <= maxPtyBackendMetadataBytes at start). */
|
|
179
|
+
readonly ptyBackendMetadata?: Readonly<Record<string, string>>;
|
|
88
180
|
}
|
|
89
181
|
export interface ProcessSession {
|
|
90
182
|
readonly id: string;
|
|
@@ -103,6 +195,8 @@ export interface ProcessSession {
|
|
|
103
195
|
signal(name: "SIGTERM" | "SIGINT" | "SIGHUP"): Promise<void>;
|
|
104
196
|
kill(): Promise<void>;
|
|
105
197
|
release(): Promise<void>;
|
|
198
|
+
/** Present only when the host backend declared `capabilities.resize` (PTY sessions). */
|
|
199
|
+
resize?(dimensions: ProcessTerminalResize): Promise<void>;
|
|
106
200
|
}
|
|
107
201
|
export type CodingProcessEvent = {
|
|
108
202
|
readonly type: "process_started" | "process_exited" | "process_killed" | "process_released" | "process_expired" | "process_unknown";
|
|
@@ -128,6 +222,17 @@ export interface ProcessSessions {
|
|
|
128
222
|
reconcile(): Promise<{
|
|
129
223
|
readonly markedUnknown: number;
|
|
130
224
|
}>;
|
|
225
|
+
/**
|
|
226
|
+
* Durable process recovery (plan 026 Task 5): reconcile durable recovery
|
|
227
|
+
* records against the live registry — attach-if-attested via the host
|
|
228
|
+
* recovery backend, otherwise `starting|running` records atomically become
|
|
229
|
+
* `unknown`. Never fabricates an exit code and never probes PIDs. Requires
|
|
230
|
+
* `checkpoints` + `leases` + `ownerId` at construction, else
|
|
231
|
+
* ERR_PRISM_RECOVERY_UNSUPPORTED. O(owned records).
|
|
232
|
+
*/
|
|
233
|
+
recover(options?: {
|
|
234
|
+
signal?: AbortSignal;
|
|
235
|
+
}): Promise<import("./recovery.js").ProcessRecoveryReport>;
|
|
131
236
|
dispose(): Promise<void>;
|
|
132
237
|
}
|
|
133
238
|
export interface CreateProcessSessionsOptions {
|
|
@@ -143,4 +248,23 @@ export interface CreateProcessSessionsOptions {
|
|
|
143
248
|
* (`ERR_PRISM_PROCESS_UNSUPPORTED`). Native spawn only when sandbox omitted.
|
|
144
249
|
*/
|
|
145
250
|
readonly sandbox?: ProcessSandboxBackend;
|
|
251
|
+
/**
|
|
252
|
+
* Phase 26: optional host-selected PTY backend. `pty: true` delegates only
|
|
253
|
+
* to this backend; absent backend or missing startPty fails closed with
|
|
254
|
+
* `ERR_PRISM_PROCESS_PTY_UNSUPPORTED` before process creation.
|
|
255
|
+
*/
|
|
256
|
+
readonly ptyBackend?: ProcessPtyBackend;
|
|
257
|
+
/**
|
|
258
|
+
* Phase 26 durable process recovery: intent is persisted before spawn and
|
|
259
|
+
* lifecycle transitions are CAS-written under LeaseStore fencing. All three
|
|
260
|
+
* of `checkpoints`, `leases`, and `ownerId` must be present together — a
|
|
261
|
+
* partial recovery configuration fails closed at construction. `recover()`
|
|
262
|
+
* is attach-if-attested via `recoveryBackend`; otherwise starting/running
|
|
263
|
+
* records atomically become unknown (no fabricated exit, no PID probing).
|
|
264
|
+
*/
|
|
265
|
+
readonly checkpoints?: import("@arnilo/prism").CheckpointStore;
|
|
266
|
+
readonly leases?: import("@arnilo/prism").LeaseStore;
|
|
267
|
+
readonly ownerId?: string;
|
|
268
|
+
readonly recoveryBackend?: import("./recovery.js").ProcessRecoveryBackend;
|
|
269
|
+
readonly recoveryLimits?: import("./recovery.js").ProcessRecoveryLimits;
|
|
146
270
|
}
|
package/dist/process/types.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DEFAULT_MAX_PROCESS_INPUT_BYTES, DEFAULT_MAX_PROCESS_LIFETIME_MS, DEFAULT_MAX_PROCESS_OUTPUT_CHUNK_BYTES, DEFAULT_MAX_PROCESS_SESSIONS, DEFAULT_MAX_PROCESS_TOTAL_OUTPUT_BYTES, HARD_MAX_PROCESS_INPUT_BYTES, HARD_MAX_PROCESS_LIFETIME_MS, HARD_MAX_PROCESS_OUTPUT_CHUNK_BYTES, HARD_MAX_PROCESS_SESSIONS, HARD_MAX_PROCESS_TOTAL_OUTPUT_BYTES, validateCodingLimit, } from "../limits.js";
|
|
1
|
+
import { DEFAULT_MAX_PROCESS_INPUT_BYTES, DEFAULT_MAX_PROCESS_LIFETIME_MS, DEFAULT_MAX_PROCESS_OUTPUT_CHUNK_BYTES, DEFAULT_MAX_PROCESS_SESSIONS, DEFAULT_MAX_PROCESS_TOTAL_OUTPUT_BYTES, DEFAULT_MAX_PTY_ATTACH_TIMEOUT_MS, DEFAULT_MAX_PTY_BACKEND_METADATA_BYTES, DEFAULT_MAX_TERMINAL_COLUMNS, DEFAULT_MAX_TERMINAL_RESIZES_PER_MINUTE, DEFAULT_MAX_TERMINAL_ROWS, DEFAULT_MAX_TERMINAL_TERM_BYTES, HARD_MAX_PROCESS_INPUT_BYTES, HARD_MAX_PROCESS_LIFETIME_MS, HARD_MAX_PROCESS_OUTPUT_CHUNK_BYTES, HARD_MAX_PROCESS_SESSIONS, HARD_MAX_PROCESS_TOTAL_OUTPUT_BYTES, HARD_MAX_PTY_ATTACH_TIMEOUT_MS, HARD_MAX_PTY_BACKEND_METADATA_BYTES, HARD_MAX_TERMINAL_COLUMNS, HARD_MAX_TERMINAL_RESIZES_PER_MINUTE, HARD_MAX_TERMINAL_ROWS, HARD_MAX_TERMINAL_TERM_BYTES, validateCodingLimit, } from "../limits.js";
|
|
2
2
|
export class ProcessSessionError extends Error {
|
|
3
3
|
code;
|
|
4
4
|
constructor(code, message) {
|
|
@@ -14,6 +14,12 @@ export function resolveProcessSessionLimits(limits) {
|
|
|
14
14
|
maxLifetimeMs: validateCodingLimit("maxLifetimeMs", limits?.maxLifetimeMs ?? DEFAULT_MAX_PROCESS_LIFETIME_MS, HARD_MAX_PROCESS_LIFETIME_MS),
|
|
15
15
|
maxOutputChunkBytes: validateCodingLimit("maxOutputChunkBytes", limits?.maxOutputChunkBytes ?? DEFAULT_MAX_PROCESS_OUTPUT_CHUNK_BYTES, HARD_MAX_PROCESS_OUTPUT_CHUNK_BYTES),
|
|
16
16
|
maxTotalOutputBytes: validateCodingLimit("maxTotalOutputBytes", limits?.maxTotalOutputBytes ?? DEFAULT_MAX_PROCESS_TOTAL_OUTPUT_BYTES, HARD_MAX_PROCESS_TOTAL_OUTPUT_BYTES),
|
|
17
|
+
maxTerminalColumns: validateCodingLimit("maxTerminalColumns", limits?.maxTerminalColumns ?? DEFAULT_MAX_TERMINAL_COLUMNS, HARD_MAX_TERMINAL_COLUMNS),
|
|
18
|
+
maxTerminalRows: validateCodingLimit("maxTerminalRows", limits?.maxTerminalRows ?? DEFAULT_MAX_TERMINAL_ROWS, HARD_MAX_TERMINAL_ROWS),
|
|
19
|
+
maxTerminalTermBytes: validateCodingLimit("maxTerminalTermBytes", limits?.maxTerminalTermBytes ?? DEFAULT_MAX_TERMINAL_TERM_BYTES, HARD_MAX_TERMINAL_TERM_BYTES),
|
|
20
|
+
maxTerminalResizesPerMinute: validateCodingLimit("maxTerminalResizesPerMinute", limits?.maxTerminalResizesPerMinute ?? DEFAULT_MAX_TERMINAL_RESIZES_PER_MINUTE, HARD_MAX_TERMINAL_RESIZES_PER_MINUTE),
|
|
21
|
+
maxPtyAttachTimeoutMs: validateCodingLimit("maxPtyAttachTimeoutMs", limits?.maxPtyAttachTimeoutMs ?? DEFAULT_MAX_PTY_ATTACH_TIMEOUT_MS, HARD_MAX_PTY_ATTACH_TIMEOUT_MS),
|
|
22
|
+
maxPtyBackendMetadataBytes: validateCodingLimit("maxPtyBackendMetadataBytes", limits?.maxPtyBackendMetadataBytes ?? DEFAULT_MAX_PTY_BACKEND_METADATA_BYTES, HARD_MAX_PTY_BACKEND_METADATA_BYTES),
|
|
17
23
|
};
|
|
18
24
|
}
|
|
19
25
|
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { RepositoryGlobRequest, RepositoryGlobResult, ResolvedRepositoryLimits } from "./types.js";
|
|
2
|
+
import type { RepositoryWalk } from "./walk.js";
|
|
3
|
+
export declare function globLocal(request: RepositoryGlobRequest, defaults: ResolvedRepositoryLimits, walk: RepositoryWalk): Promise<RepositoryGlobResult>;
|
|
4
|
+
/** Local filesystem repository operations (default backend). */
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/** Repository glob family (0.2.5 plan 025 Task 1 split).
|
|
2
|
+
* Moved verbatim from repository.ts; public surface unchanged behind the barrel. */
|
|
3
|
+
import { HARD_MAX_REPO_DEPTH, HARD_MAX_REPO_ENTRIES, HARD_MAX_REPO_RESULTS, validateCodingLimit, validateCodingLimitAllowZero, } from "../limits.js";
|
|
4
|
+
import { expandGlobBraces, matchGlobPattern, validateGlobPattern } from "../glob-match.js";
|
|
5
|
+
import { lstat } from "node:fs/promises";
|
|
6
|
+
import { RepositoryError } from "./types.js";
|
|
7
|
+
import { resolveRepoPath } from "./path.js";
|
|
8
|
+
export async function globLocal(request, defaults, walk) {
|
|
9
|
+
try {
|
|
10
|
+
validateGlobPattern(request.pattern, defaults.maxPatternBytes, { braceExpansion: request.braceExpansion === true });
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
throw new RepositoryError(error instanceof Error ? error.message : String(error));
|
|
14
|
+
}
|
|
15
|
+
// Opt-in bounded brace expansion: textual alternatives only (never touches the
|
|
16
|
+
// filesystem); bounds enforced by expandGlobBraces (max alternatives / bytes).
|
|
17
|
+
const patterns = request.braceExpansion === true ? expandGlobBraces(request.pattern) : [request.pattern];
|
|
18
|
+
const resolved = await resolveRepoPath(request.root, request.path);
|
|
19
|
+
const maxResults = validateCodingLimit("maxResults", request.maxResults ?? defaults.maxResults, HARD_MAX_REPO_RESULTS);
|
|
20
|
+
const offset = validateCodingLimitAllowZero("offset", request.offset ?? 0, HARD_MAX_REPO_ENTRIES);
|
|
21
|
+
const maxDepth = validateCodingLimit("maxDepth", request.maxDepth ?? defaults.maxDepth, HARD_MAX_REPO_DEPTH);
|
|
22
|
+
const exclude = new Set(request.exclude ?? defaults.exclude);
|
|
23
|
+
const deadlineAt = request.deadlineMs !== undefined ? Date.now() + request.deadlineMs : Date.now() + defaults.maxTimeMs;
|
|
24
|
+
const collected = [];
|
|
25
|
+
let scannedEntries = 0;
|
|
26
|
+
let scannedFiles = 0;
|
|
27
|
+
let seen = 0;
|
|
28
|
+
let truncated = false;
|
|
29
|
+
let truncatedBy = null;
|
|
30
|
+
const matchesAnyPattern = (relativePath) => {
|
|
31
|
+
for (const p of patterns) {
|
|
32
|
+
if (matchGlobPattern(p, relativePath))
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
return false;
|
|
36
|
+
};
|
|
37
|
+
const maybeCollect = (relativePath) => {
|
|
38
|
+
if (!matchesAnyPattern(relativePath))
|
|
39
|
+
return false;
|
|
40
|
+
if (seen < offset) {
|
|
41
|
+
seen++;
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
if (collected.length >= maxResults) {
|
|
45
|
+
truncated = true;
|
|
46
|
+
truncatedBy = "results";
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
collected.push(relativePath);
|
|
50
|
+
seen++;
|
|
51
|
+
return truncated;
|
|
52
|
+
};
|
|
53
|
+
try {
|
|
54
|
+
const startStat = await lstat(resolved.absolute);
|
|
55
|
+
if (!startStat.isDirectory()) {
|
|
56
|
+
scannedEntries = 1;
|
|
57
|
+
if (startStat.isFile()) {
|
|
58
|
+
scannedFiles = 1;
|
|
59
|
+
if (matchesAnyPattern(resolved.relative)) {
|
|
60
|
+
if (offset === 0 && maxResults > 0)
|
|
61
|
+
collected.push(resolved.relative);
|
|
62
|
+
else if (offset === 0 && maxResults === 0) {
|
|
63
|
+
truncated = true;
|
|
64
|
+
truncatedBy = "results";
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
paths: collected,
|
|
70
|
+
truncated,
|
|
71
|
+
truncatedBy,
|
|
72
|
+
scannedEntries,
|
|
73
|
+
scannedFiles,
|
|
74
|
+
offset,
|
|
75
|
+
nextOffset: undefined,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
81
|
+
throw new RepositoryError(`cannot open path: ${message}`);
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
for await (const event of walk(resolved.rootReal, resolved.absolute, {
|
|
85
|
+
maxDepth,
|
|
86
|
+
maxEntries: defaults.maxEntries,
|
|
87
|
+
maxFiles: defaults.maxFiles,
|
|
88
|
+
exclude,
|
|
89
|
+
includeHidden: request.includeHidden === true,
|
|
90
|
+
signal: request.signal,
|
|
91
|
+
deadlineAt,
|
|
92
|
+
})) {
|
|
93
|
+
if (event.type === "limit") {
|
|
94
|
+
truncated = true;
|
|
95
|
+
truncatedBy = event.truncatedBy;
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
scannedEntries++;
|
|
99
|
+
if (event.entry.kind === "file")
|
|
100
|
+
scannedFiles++;
|
|
101
|
+
if (event.entry.kind !== "file")
|
|
102
|
+
continue;
|
|
103
|
+
if (maybeCollect(event.entry.path))
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
if (error instanceof RepositoryError && error.message === "Operation aborted") {
|
|
109
|
+
return {
|
|
110
|
+
paths: collected,
|
|
111
|
+
truncated: true,
|
|
112
|
+
truncatedBy: "abort",
|
|
113
|
+
scannedEntries,
|
|
114
|
+
scannedFiles,
|
|
115
|
+
offset,
|
|
116
|
+
nextOffset: collected.length > 0 || offset > 0 ? offset + collected.length : undefined,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
if (error instanceof RepositoryError && error.message === "Repository operation exceeded time limit") {
|
|
120
|
+
return {
|
|
121
|
+
paths: collected,
|
|
122
|
+
truncated: true,
|
|
123
|
+
truncatedBy: "time",
|
|
124
|
+
scannedEntries,
|
|
125
|
+
scannedFiles,
|
|
126
|
+
offset,
|
|
127
|
+
nextOffset: offset + collected.length,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
throw error;
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
paths: collected,
|
|
134
|
+
truncated,
|
|
135
|
+
truncatedBy,
|
|
136
|
+
scannedEntries,
|
|
137
|
+
scannedFiles,
|
|
138
|
+
offset,
|
|
139
|
+
nextOffset: truncated ? offset + collected.length : undefined,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
/** Local filesystem repository operations (default backend). */
|
|
143
|
+
//# sourceMappingURL=glob.js.map
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import type { RepositoryOperations } from "./types.js";
|
|
2
|
+
/** Frozen index state machine: empty | building | ready | stale | failed. */
|
|
3
|
+
export type IndexState = "empty" | "building" | "ready" | "stale" | "failed";
|
|
4
|
+
export declare const INDEX_STATES: readonly IndexState[];
|
|
5
|
+
export interface IndexResourceDiagnostics {
|
|
6
|
+
readonly entries: number;
|
|
7
|
+
readonly bytes: number;
|
|
8
|
+
readonly lastUpdatedAt?: number;
|
|
9
|
+
}
|
|
10
|
+
export interface RepositoryIndexStatus {
|
|
11
|
+
readonly state: IndexState;
|
|
12
|
+
/** Source revision the index was built from; required when `requireSourceRevision`. */
|
|
13
|
+
readonly sourceRevision?: string;
|
|
14
|
+
/** Indexed-at timestamp (epoch ms); used for the staleness age check. */
|
|
15
|
+
readonly updatedAt?: number;
|
|
16
|
+
readonly diagnostics?: IndexResourceDiagnostics;
|
|
17
|
+
}
|
|
18
|
+
export type IndexFileChangeKind = "add" | "edit" | "delete" | "rename";
|
|
19
|
+
export interface IndexFileChange {
|
|
20
|
+
/** Repository-relative path (forward slashes, no leading slash, no `..`). */
|
|
21
|
+
readonly path: string;
|
|
22
|
+
readonly kind: IndexFileChangeKind;
|
|
23
|
+
/** Rename source path (repository-relative). */
|
|
24
|
+
readonly oldPath?: string;
|
|
25
|
+
readonly bytes?: number;
|
|
26
|
+
}
|
|
27
|
+
export interface RepositoryIndexUpdateRequest {
|
|
28
|
+
/** Optional host identity scoping (bounded; never credential-bearing). */
|
|
29
|
+
readonly repositoryId?: string;
|
|
30
|
+
readonly worktreeId?: string;
|
|
31
|
+
/** Revision the changes apply on top of. */
|
|
32
|
+
readonly sourceRevision: string;
|
|
33
|
+
readonly changes: readonly IndexFileChange[];
|
|
34
|
+
}
|
|
35
|
+
export interface RepositoryIndexRemoveRequest {
|
|
36
|
+
/** Repository-relative paths to drop from the index. */
|
|
37
|
+
readonly paths: readonly string[];
|
|
38
|
+
}
|
|
39
|
+
export interface IndexSearchHit {
|
|
40
|
+
/** Repository-relative path (validated for containment). */
|
|
41
|
+
readonly path: string;
|
|
42
|
+
/** Relevance in [0,1]; non-finite or out-of-range scores fail closed. */
|
|
43
|
+
readonly score: number;
|
|
44
|
+
/** Snippet text (bounded; treated as untrusted prompt-injection surface). */
|
|
45
|
+
readonly snippet: string;
|
|
46
|
+
}
|
|
47
|
+
export interface RepositoryIndexQueryRequest {
|
|
48
|
+
readonly query: string;
|
|
49
|
+
readonly mode: "indexed_literal" | "semantic";
|
|
50
|
+
/** Optional repository-relative scope filter. */
|
|
51
|
+
readonly path?: string;
|
|
52
|
+
readonly maxResults: number;
|
|
53
|
+
readonly signal?: AbortSignal;
|
|
54
|
+
readonly deadlineMs?: number;
|
|
55
|
+
}
|
|
56
|
+
export interface RepositoryIndexQueryResult {
|
|
57
|
+
readonly hits: readonly IndexSearchHit[];
|
|
58
|
+
readonly truncated: boolean;
|
|
59
|
+
}
|
|
60
|
+
export interface RepositoryIndexBackend {
|
|
61
|
+
/** Explicit capability declaration; semantic mode is never duck-typed. */
|
|
62
|
+
readonly capabilities: {
|
|
63
|
+
readonly semantic: boolean;
|
|
64
|
+
};
|
|
65
|
+
update(request: RepositoryIndexUpdateRequest): Promise<void>;
|
|
66
|
+
remove(request: RepositoryIndexRemoveRequest): Promise<void>;
|
|
67
|
+
search(request: RepositoryIndexQueryRequest): Promise<RepositoryIndexQueryResult>;
|
|
68
|
+
status(): Promise<RepositoryIndexStatus>;
|
|
69
|
+
dispose(): Promise<void>;
|
|
70
|
+
}
|
|
71
|
+
export interface ResolvedIndexLimits {
|
|
72
|
+
readonly maxUpdateFiles: number;
|
|
73
|
+
readonly maxUpdateBytes: number;
|
|
74
|
+
readonly maxResults: number;
|
|
75
|
+
readonly maxSnippetBytes: number;
|
|
76
|
+
readonly staleMaxAgeMs: number;
|
|
77
|
+
readonly queryTimeoutMs: number;
|
|
78
|
+
}
|
|
79
|
+
export interface IndexLimitOptions {
|
|
80
|
+
readonly maxUpdateFiles?: number;
|
|
81
|
+
readonly maxUpdateBytes?: number;
|
|
82
|
+
readonly maxResults?: number;
|
|
83
|
+
readonly maxSnippetBytes?: number;
|
|
84
|
+
readonly staleMaxAgeMs?: number;
|
|
85
|
+
readonly queryTimeoutMs?: number;
|
|
86
|
+
}
|
|
87
|
+
export declare function resolveIndexLimits(options?: IndexLimitOptions): ResolvedIndexLimits;
|
|
88
|
+
export declare class IndexError extends Error {
|
|
89
|
+
readonly code: "ERR_PRISM_INDEX_UNSUPPORTED" | "ERR_PRISM_INDEX_STALE" | "ERR_PRISM_INDEX_FAILED" | "ERR_PRISM_INDEX_LIMIT" | "ERR_PRISM_INDEX_TIMEOUT" | "ERR_PRISM_INDEX_UNTRUSTED";
|
|
90
|
+
constructor(code: "ERR_PRISM_INDEX_UNSUPPORTED" | "ERR_PRISM_INDEX_STALE" | "ERR_PRISM_INDEX_FAILED" | "ERR_PRISM_INDEX_LIMIT" | "ERR_PRISM_INDEX_TIMEOUT" | "ERR_PRISM_INDEX_UNTRUSTED", message: string);
|
|
91
|
+
}
|
|
92
|
+
export interface IndexedRepositoryOptions {
|
|
93
|
+
/** Host-owned incremental index backend (never started or built implicitly). */
|
|
94
|
+
readonly index: RepositoryIndexBackend;
|
|
95
|
+
/** Literal fallback (e.g. createGitAwareRepositoryOperations) for mode "literal". */
|
|
96
|
+
readonly fallback: RepositoryOperations;
|
|
97
|
+
/** Modes this composite accepts; default literal only. `indexed_literal`/`semantic` must be listed explicitly. */
|
|
98
|
+
readonly allowedModes?: readonly ("literal" | "indexed_literal" | "semantic")[];
|
|
99
|
+
readonly stale?: {
|
|
100
|
+
readonly maxAgeMs?: number;
|
|
101
|
+
/** Require the index to attest a sourceRevision before serving queries. */
|
|
102
|
+
readonly requireSourceRevision?: boolean;
|
|
103
|
+
};
|
|
104
|
+
readonly limits?: IndexLimitOptions;
|
|
105
|
+
}
|
|
106
|
+
export interface IndexFacade {
|
|
107
|
+
update(request: RepositoryIndexUpdateRequest): Promise<void>;
|
|
108
|
+
remove(request: RepositoryIndexRemoveRequest): Promise<void>;
|
|
109
|
+
status(): Promise<RepositoryIndexStatus>;
|
|
110
|
+
dispose(): Promise<void>;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Compose a host index with a literal fallback. Mode "literal" is routed to
|
|
114
|
+
* `fallback` unchanged; indexed modes are served only by the host backend with
|
|
115
|
+
* freshness checks, result validation, and no silent fallback.
|
|
116
|
+
*/
|
|
117
|
+
export declare function createIndexedRepositoryOperations(cwd: string, options: IndexedRepositoryOptions): RepositoryOperations & {
|
|
118
|
+
readonly index: IndexFacade;
|
|
119
|
+
};
|
|
120
|
+
/** Stable repo-relative label used in errors; kept for tests and docs. */
|
|
121
|
+
export declare function indexErrorCode(error: unknown): string | undefined;
|