proofscan 0.10.54 → 0.10.55

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.
Files changed (42) hide show
  1. package/dist/shell/find-command.d.ts +54 -0
  2. package/dist/shell/find-command.d.ts.map +1 -0
  3. package/dist/shell/find-command.js +330 -0
  4. package/dist/shell/find-command.js.map +1 -0
  5. package/dist/shell/pager/index.d.ts +9 -0
  6. package/dist/shell/pager/index.d.ts.map +1 -0
  7. package/dist/shell/pager/index.js +8 -0
  8. package/dist/shell/pager/index.js.map +1 -0
  9. package/dist/shell/pager/less-pager.d.ts +31 -0
  10. package/dist/shell/pager/less-pager.d.ts.map +1 -0
  11. package/dist/shell/pager/less-pager.js +196 -0
  12. package/dist/shell/pager/less-pager.js.map +1 -0
  13. package/dist/shell/pager/more-pager.d.ts +31 -0
  14. package/dist/shell/pager/more-pager.d.ts.map +1 -0
  15. package/dist/shell/pager/more-pager.js +108 -0
  16. package/dist/shell/pager/more-pager.js.map +1 -0
  17. package/dist/shell/pager/renderer.d.ts +17 -0
  18. package/dist/shell/pager/renderer.d.ts.map +1 -0
  19. package/dist/shell/pager/renderer.js +125 -0
  20. package/dist/shell/pager/renderer.js.map +1 -0
  21. package/dist/shell/pager/types.d.ts +19 -0
  22. package/dist/shell/pager/types.d.ts.map +1 -0
  23. package/dist/shell/pager/types.js +7 -0
  24. package/dist/shell/pager/types.js.map +1 -0
  25. package/dist/shell/pager/utils.d.ts +28 -0
  26. package/dist/shell/pager/utils.d.ts.map +1 -0
  27. package/dist/shell/pager/utils.js +53 -0
  28. package/dist/shell/pager/utils.js.map +1 -0
  29. package/dist/shell/pipeline-types.d.ts +1 -0
  30. package/dist/shell/pipeline-types.d.ts.map +1 -1
  31. package/dist/shell/repl.d.ts +13 -0
  32. package/dist/shell/repl.d.ts.map +1 -1
  33. package/dist/shell/repl.js +287 -38
  34. package/dist/shell/repl.js.map +1 -1
  35. package/dist/shell/types.d.ts +5 -0
  36. package/dist/shell/types.d.ts.map +1 -1
  37. package/dist/shell/types.js +7 -1
  38. package/dist/shell/types.js.map +1 -1
  39. package/dist/shell/where-command.d.ts.map +1 -1
  40. package/dist/shell/where-command.js +29 -0
  41. package/dist/shell/where-command.js.map +1 -1
  42. package/package.json +1 -1
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Find Command for psh Shell
3
+ *
4
+ * Cross-session search without changing `ls` semantics.
5
+ * Scope derived from cwd:
6
+ * / => all connectors
7
+ * /<cid>: => that connector's sessions
8
+ * /<cid>/<sid>: => that session only
9
+ */
10
+ import type { ShellContext } from './types.js';
11
+ import type { PipelineValue } from './pipeline-types.js';
12
+ /** Find command options */
13
+ export interface FindOptions {
14
+ kind: 'session' | 'rpc' | 'event';
15
+ limit: number;
16
+ sessions: number;
17
+ errorsOnly: boolean;
18
+ }
19
+ /** Find result type */
20
+ export type FindResult = {
21
+ ok: true;
22
+ result: PipelineValue;
23
+ stats: {
24
+ count: number;
25
+ sessions: number;
26
+ };
27
+ } | {
28
+ ok: false;
29
+ error: string;
30
+ } | {
31
+ ok: false;
32
+ error: string;
33
+ help: true;
34
+ };
35
+ /**
36
+ * Parse find command arguments
37
+ *
38
+ * Usage: find <kind> [--limit N] [--sessions N] [--errors-only]
39
+ */
40
+ export declare function parseFindArgs(args: string[]): {
41
+ ok: true;
42
+ options: FindOptions;
43
+ } | {
44
+ ok: false;
45
+ error: string;
46
+ help?: true;
47
+ };
48
+ /**
49
+ * Execute find command
50
+ *
51
+ * Loads rows from multiple sessions based on scope and options.
52
+ */
53
+ export declare function executeFind(context: ShellContext, configPath: string, options: FindOptions): FindResult;
54
+ //# sourceMappingURL=find-command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"find-command.d.ts","sourceRoot":"","sources":["../../src/shell/find-command.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,KAAK,EAAE,aAAa,EAAsB,MAAM,qBAAqB,CAAC;AAK7E,2BAA2B;AAC3B,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,SAAS,GAAG,KAAK,GAAG,OAAO,CAAC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,OAAO,CAAC;CACrB;AAUD,uBAAuB;AACvB,MAAM,MAAM,UAAU,GAClB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,aAAa,CAAC;IAAC,KAAK,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GAC/E;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC5B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,IAAI,CAAA;CAAE,CAAC;AA8B7C;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,WAAW,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,IAAI,CAAA;CAAE,CAmD5H;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,YAAY,EACrB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,WAAW,GACnB,UAAU,CAoBZ"}
@@ -0,0 +1,330 @@
1
+ /**
2
+ * Find Command for psh Shell
3
+ *
4
+ * Cross-session search without changing `ls` semantics.
5
+ * Scope derived from cwd:
6
+ * / => all connectors
7
+ * /<cid>: => that connector's sessions
8
+ * /<cid>/<sid>: => that session only
9
+ */
10
+ import { getContextLevel } from './router-commands.js';
11
+ import { EventLineStore } from '../eventline/store.js';
12
+ import { ConfigManager } from '../config/index.js';
13
+ /** Default options */
14
+ const DEFAULT_OPTIONS = {
15
+ kind: 'rpc',
16
+ limit: 200,
17
+ sessions: 50,
18
+ errorsOnly: false,
19
+ };
20
+ /** Help text for find command */
21
+ const FIND_HELP = `find - cross-session search
22
+
23
+ Usage: find <kind> [options]
24
+
25
+ Kinds:
26
+ session List sessions across scope
27
+ rpc List RPC calls across sessions
28
+ event List events (not yet implemented)
29
+
30
+ Options:
31
+ --limit N Max rows to return (default: 200)
32
+ --sessions N Max sessions to search (default: 50)
33
+ --errors-only Only return error RPCs
34
+ -h, --help Show this help
35
+
36
+ Scope (derived from cwd):
37
+ / All connectors
38
+ /<connector>: That connector's sessions
39
+ /<conn>/<sess>: That session only
40
+
41
+ Examples:
42
+ find rpc All RPCs in current scope
43
+ find rpc --errors-only Only errors
44
+ find rpc --limit 50 --sessions 10 Limit results
45
+ find rpc | where tools.name ~= "read" Chain with filter
46
+ find session --limit 10 Latest sessions`;
47
+ /**
48
+ * Parse find command arguments
49
+ *
50
+ * Usage: find <kind> [--limit N] [--sessions N] [--errors-only]
51
+ */
52
+ export function parseFindArgs(args) {
53
+ const options = { ...DEFAULT_OPTIONS };
54
+ // Handle help flags first
55
+ if (args.length === 0 || args.includes('-h') || args.includes('--help')) {
56
+ return { ok: false, error: FIND_HELP, help: true };
57
+ }
58
+ let i = 0;
59
+ const kindArg = args[0].toLowerCase();
60
+ if (!['session', 'rpc', 'event'].includes(kindArg)) {
61
+ return { ok: false, error: `Invalid kind: ${args[0]}. Must be one of: session, rpc, event` };
62
+ }
63
+ options.kind = kindArg;
64
+ i++;
65
+ // Parse optional flags
66
+ while (i < args.length) {
67
+ const arg = args[i];
68
+ if (arg === '--limit') {
69
+ const val = args[++i];
70
+ if (!val || isNaN(parseInt(val, 10))) {
71
+ return { ok: false, error: '--limit requires a number' };
72
+ }
73
+ const limitVal = parseInt(val, 10);
74
+ if (limitVal <= 0) {
75
+ return { ok: false, error: '--limit must be a positive number' };
76
+ }
77
+ options.limit = limitVal;
78
+ }
79
+ else if (arg === '--sessions') {
80
+ const val = args[++i];
81
+ if (!val || isNaN(parseInt(val, 10))) {
82
+ return { ok: false, error: '--sessions requires a number' };
83
+ }
84
+ const sessionsVal = parseInt(val, 10);
85
+ if (sessionsVal <= 0) {
86
+ return { ok: false, error: '--sessions must be a positive number' };
87
+ }
88
+ options.sessions = sessionsVal;
89
+ }
90
+ else if (arg === '--errors-only') {
91
+ options.errorsOnly = true;
92
+ }
93
+ else if (arg.startsWith('-')) {
94
+ return { ok: false, error: `Unknown option: ${arg}` };
95
+ }
96
+ i++;
97
+ }
98
+ return { ok: true, options };
99
+ }
100
+ /**
101
+ * Execute find command
102
+ *
103
+ * Loads rows from multiple sessions based on scope and options.
104
+ */
105
+ export function executeFind(context, configPath, options) {
106
+ const manager = new ConfigManager(configPath);
107
+ const store = new EventLineStore(manager.getConfigDir());
108
+ const level = getContextLevel(context);
109
+ // Determine scope
110
+ const scope = determineScope(level, context);
111
+ // Load rows based on kind
112
+ switch (options.kind) {
113
+ case 'session':
114
+ return findSessions(store, scope, options);
115
+ case 'rpc':
116
+ return findRpcs(store, scope, options);
117
+ case 'event':
118
+ // Event kind is reserved for future implementation
119
+ return { ok: false, error: 'Event kind is not yet implemented' };
120
+ default:
121
+ return { ok: false, error: `Unknown kind: ${options.kind}` };
122
+ }
123
+ }
124
+ /**
125
+ * Determine search scope from context
126
+ */
127
+ function determineScope(level, context) {
128
+ return {
129
+ level,
130
+ connectorId: context.connector,
131
+ sessionId: context.session,
132
+ };
133
+ }
134
+ /**
135
+ * Find sessions across scope
136
+ */
137
+ function findSessions(store, scope, options) {
138
+ const rows = [];
139
+ let sessionCount = 0;
140
+ if (scope.level === 'session' && scope.sessionId) {
141
+ // Session level - return just this session info
142
+ const sessions = store.getSessions(scope.connectorId, options.sessions);
143
+ const session = sessions.find(s => s.session_id === scope.sessionId);
144
+ if (session) {
145
+ rows.push({
146
+ session_id: session.session_id,
147
+ connector_id: scope.connectorId,
148
+ started_at: session.started_at ?? '',
149
+ ended_at: session.ended_at ?? null,
150
+ event_count: session.event_count ?? 0,
151
+ rpc_count: session.rpc_count ?? 0,
152
+ });
153
+ sessionCount = 1;
154
+ }
155
+ }
156
+ else if (scope.level === 'connector' && scope.connectorId) {
157
+ // Connector level - return sessions for this connector
158
+ const sessions = store.getSessions(scope.connectorId, options.sessions);
159
+ for (const session of sessions) {
160
+ if (rows.length >= options.limit)
161
+ break;
162
+ rows.push({
163
+ session_id: session.session_id,
164
+ connector_id: scope.connectorId,
165
+ started_at: session.started_at ?? '',
166
+ ended_at: session.ended_at ?? null,
167
+ event_count: session.event_count ?? 0,
168
+ rpc_count: session.rpc_count ?? 0,
169
+ });
170
+ }
171
+ sessionCount = sessions.length;
172
+ }
173
+ else {
174
+ // Root level - return sessions across all connectors
175
+ const connectors = store.getConnectors();
176
+ for (const connector of connectors) {
177
+ if (rows.length >= options.limit)
178
+ break;
179
+ const sessions = store.getSessions(connector.id, options.sessions);
180
+ for (const session of sessions) {
181
+ if (rows.length >= options.limit)
182
+ break;
183
+ rows.push({
184
+ session_id: session.session_id,
185
+ connector_id: connector.id,
186
+ started_at: session.started_at ?? '',
187
+ ended_at: session.ended_at ?? null,
188
+ event_count: session.event_count ?? 0,
189
+ rpc_count: session.rpc_count ?? 0,
190
+ });
191
+ }
192
+ sessionCount += sessions.length;
193
+ }
194
+ }
195
+ return {
196
+ ok: true,
197
+ result: { kind: 'rows', rows, rowType: 'session' },
198
+ stats: { count: rows.length, sessions: sessionCount },
199
+ };
200
+ }
201
+ /**
202
+ * Extract tool name from request JSON.
203
+ * Fallback chain: params.name ?? params.tool ?? params.toolName
204
+ */
205
+ function extractToolName(rawJson) {
206
+ if (!rawJson)
207
+ return undefined;
208
+ try {
209
+ const parsed = JSON.parse(rawJson);
210
+ const params = parsed?.params;
211
+ if (!params || typeof params !== 'object')
212
+ return undefined;
213
+ if (typeof params.name === 'string' && params.name.length > 0) {
214
+ return params.name;
215
+ }
216
+ if (typeof params.tool === 'string' && params.tool.length > 0) {
217
+ return params.tool;
218
+ }
219
+ if (typeof params.toolName === 'string' && params.toolName.length > 0) {
220
+ return params.toolName;
221
+ }
222
+ }
223
+ catch {
224
+ // JSON parse error
225
+ }
226
+ return undefined;
227
+ }
228
+ /**
229
+ * Find RPCs across scope
230
+ */
231
+ function findRpcs(store, scope, options) {
232
+ const rows = [];
233
+ let sessionCount = 0;
234
+ // Collect session IDs to search
235
+ const sessionIds = [];
236
+ if (scope.level === 'session' && scope.sessionId && scope.connectorId) {
237
+ // Session level - search only this session
238
+ sessionIds.push({ sessionId: scope.sessionId, connectorId: scope.connectorId });
239
+ }
240
+ else if (scope.level === 'connector' && scope.connectorId) {
241
+ // Connector level - search sessions for this connector
242
+ const sessions = store.getSessions(scope.connectorId, options.sessions);
243
+ for (const session of sessions) {
244
+ sessionIds.push({ sessionId: session.session_id, connectorId: scope.connectorId });
245
+ }
246
+ }
247
+ else {
248
+ // Root level - search across all connectors
249
+ const connectors = store.getConnectors();
250
+ for (const connector of connectors) {
251
+ const sessions = store.getSessions(connector.id, options.sessions);
252
+ for (const session of sessions) {
253
+ sessionIds.push({ sessionId: session.session_id, connectorId: connector.id });
254
+ }
255
+ }
256
+ }
257
+ sessionCount = sessionIds.length;
258
+ // Load RPCs from each session
259
+ for (const { sessionId, connectorId } of sessionIds) {
260
+ if (rows.length >= options.limit)
261
+ break;
262
+ const rpcs = store.getRpcCalls(sessionId);
263
+ // Build tool name map for tools/call RPCs in this session (batch lookup)
264
+ // This avoids N+1 query pattern by fetching raw events once per session
265
+ const toolsCallRpcIds = rpcs
266
+ .filter(rpc => rpc.method === 'tools/call')
267
+ .map(rpc => rpc.rpc_id);
268
+ const toolNameMap = new Map();
269
+ if (toolsCallRpcIds.length > 0) {
270
+ // Build tool name map for all tools/call RPCs in this session
271
+ // Note: getRawEvent internally fetches all session events, so subsequent
272
+ // calls for the same session benefit from SQLite's page cache
273
+ for (const rpcId of toolsCallRpcIds) {
274
+ const event = store.getRawEvent(sessionId, rpcId);
275
+ if (event?.request?.raw_json) {
276
+ const toolName = extractToolName(event.request.raw_json);
277
+ if (toolName) {
278
+ toolNameMap.set(rpcId, toolName);
279
+ }
280
+ }
281
+ }
282
+ }
283
+ for (const rpc of rpcs) {
284
+ if (rows.length >= options.limit)
285
+ break;
286
+ // Determine status
287
+ let status;
288
+ if (rpc.success === 1) {
289
+ status = 'OK';
290
+ }
291
+ else if (rpc.success === 0) {
292
+ status = 'ERR';
293
+ }
294
+ else {
295
+ status = 'pending';
296
+ }
297
+ // Filter errors only
298
+ if (options.errorsOnly && status !== 'ERR') {
299
+ continue;
300
+ }
301
+ // Calculate latency
302
+ let latency_ms = null;
303
+ if (rpc.request_ts && rpc.response_ts) {
304
+ const requestTime = new Date(rpc.request_ts).getTime();
305
+ const responseTime = new Date(rpc.response_ts).getTime();
306
+ latency_ms = responseTime - requestTime;
307
+ }
308
+ // Get tool_name from pre-built map (avoids N+1 query)
309
+ const tool_name = toolNameMap.get(rpc.rpc_id);
310
+ rows.push({
311
+ rpc_id: rpc.rpc_id,
312
+ session_id: rpc.session_id,
313
+ connector_id: connectorId,
314
+ method: rpc.method,
315
+ status,
316
+ latency_ms,
317
+ request_ts: rpc.request_ts,
318
+ response_ts: rpc.response_ts ?? null,
319
+ error_code: rpc.error_code ?? null,
320
+ tool_name,
321
+ });
322
+ }
323
+ }
324
+ return {
325
+ ok: true,
326
+ result: { kind: 'rows', rows, rowType: 'rpc' },
327
+ stats: { count: rows.length, sessions: sessionCount },
328
+ };
329
+ }
330
+ //# sourceMappingURL=find-command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"find-command.js","sourceRoot":"","sources":["../../src/shell/find-command.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Pager module exports
3
+ */
4
+ export { LessPager } from './less-pager.js';
5
+ export { MorePager } from './more-pager.js';
6
+ export { renderRowsToLines } from './renderer.js';
7
+ export { commandExists, parsePagerCommand, runPager, FOOTER_RESERVE_LINES } from './utils.js';
8
+ export type { Pager, PagerOptions } from './types.js';
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/shell/pager/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,QAAQ,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAC9F,YAAY,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Pager module exports
3
+ */
4
+ export { LessPager } from './less-pager.js';
5
+ export { MorePager } from './more-pager.js';
6
+ export { renderRowsToLines } from './renderer.js';
7
+ export { commandExists, parsePagerCommand, runPager, FOOTER_RESERVE_LINES } from './utils.js';
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/shell/pager/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,QAAQ,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Less Pager for psh Shell
3
+ *
4
+ * Delegates to external pager for reliable TTY handling.
5
+ * Pager selection priority:
6
+ * 1. $PAGER environment variable (user preference)
7
+ * 2. 'less' command
8
+ * 3. Built-in pager (fallback)
9
+ *
10
+ * Note: When using external pager, standard pager key bindings apply.
11
+ * The built-in fallback provides vim-style navigation (j/k/space/b/g/G/q).
12
+ */
13
+ import type { Pager, PagerOptions } from './types.js';
14
+ import type { PipelineValue } from '../pipeline-types.js';
15
+ export declare class LessPager implements Pager {
16
+ private options;
17
+ constructor(options?: PagerOptions);
18
+ run(input: PipelineValue): Promise<void>;
19
+ /**
20
+ * Try external pager in priority order
21
+ * Returns true if successful, false if all failed
22
+ */
23
+ private tryExternalPager;
24
+ /**
25
+ * Built-in pager fallback
26
+ * Provides vim-style navigation when no external pager is available
27
+ */
28
+ private runBuiltIn;
29
+ private runLoop;
30
+ }
31
+ //# sourceMappingURL=less-pager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"less-pager.d.ts","sourceRoot":"","sources":["../../../src/shell/pager/less-pager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAI1D,qBAAa,SAAU,YAAW,KAAK;IACrC,OAAO,CAAC,OAAO,CAAe;gBAElB,OAAO,CAAC,EAAE,YAAY;IAI5B,GAAG,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IA2B9C;;;OAGG;YACW,gBAAgB;IAmC9B;;;OAGG;YACW,UAAU;YAmBV,OAAO;CAyGtB"}
@@ -0,0 +1,196 @@
1
+ /**
2
+ * Less Pager for psh Shell
3
+ *
4
+ * Delegates to external pager for reliable TTY handling.
5
+ * Pager selection priority:
6
+ * 1. $PAGER environment variable (user preference)
7
+ * 2. 'less' command
8
+ * 3. Built-in pager (fallback)
9
+ *
10
+ * Note: When using external pager, standard pager key bindings apply.
11
+ * The built-in fallback provides vim-style navigation (j/k/space/b/g/G/q).
12
+ */
13
+ import { renderRowsToLines } from './renderer.js';
14
+ import { commandExists, parsePagerCommand, runPager, FOOTER_RESERVE_LINES } from './utils.js';
15
+ export class LessPager {
16
+ options;
17
+ constructor(options) {
18
+ this.options = options ?? {};
19
+ }
20
+ async run(input) {
21
+ // TTY check - non-TTY outputs all lines without paging
22
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
23
+ const lines = renderRowsToLines(input, { useColor: false });
24
+ lines.forEach(line => console.log(line));
25
+ return;
26
+ }
27
+ const lines = renderRowsToLines(input, { useColor: true });
28
+ // If content fits in one page, just print and return
29
+ const terminalHeight = this.options.height ?? (process.stdout.rows || 24);
30
+ const pageSize = Math.max(1, terminalHeight - FOOTER_RESERVE_LINES);
31
+ if (lines.length <= pageSize) {
32
+ lines.forEach(line => console.log(line));
33
+ return;
34
+ }
35
+ // Try external pager (PAGER env -> less -> built-in)
36
+ const pagerResult = await this.tryExternalPager(lines);
37
+ if (!pagerResult) {
38
+ // All external pagers failed, use built-in
39
+ await this.runBuiltIn(lines, pageSize);
40
+ }
41
+ }
42
+ /**
43
+ * Try external pager in priority order
44
+ * Returns true if successful, false if all failed
45
+ */
46
+ async tryExternalPager(lines) {
47
+ const content = lines.join('\n') + '\n';
48
+ // 1. Try $PAGER environment variable (supports arguments like "less -R")
49
+ const pagerEnv = process.env.PAGER;
50
+ if (pagerEnv) {
51
+ const { cmd, args } = parsePagerCommand(pagerEnv);
52
+ if (commandExists(cmd)) {
53
+ try {
54
+ await runPager(cmd, args, content);
55
+ return true;
56
+ }
57
+ catch {
58
+ // $PAGER failed, continue to next option
59
+ }
60
+ }
61
+ }
62
+ // 2. Try 'less' with recommended options
63
+ // -F: quit if content fits one screen
64
+ // -R: interpret ANSI color codes
65
+ // -S: don't wrap long lines (preserves table layout)
66
+ // -X: don't clear screen on exit
67
+ if (commandExists('less')) {
68
+ try {
69
+ await runPager('less', ['-FRSX'], content);
70
+ return true;
71
+ }
72
+ catch {
73
+ // less failed, continue
74
+ }
75
+ }
76
+ // All external pagers failed
77
+ return false;
78
+ }
79
+ /**
80
+ * Built-in pager fallback
81
+ * Provides vim-style navigation when no external pager is available
82
+ */
83
+ async runBuiltIn(lines, pageSize) {
84
+ // Enable raw mode for key input
85
+ process.stdin.setRawMode(true);
86
+ process.stdin.resume();
87
+ // Hide cursor while in pager mode
88
+ process.stdout.write('\x1B[?25l');
89
+ try {
90
+ await this.runLoop(lines, pageSize);
91
+ }
92
+ finally {
93
+ // Show cursor again
94
+ process.stdout.write('\x1B[?25h');
95
+ // Restore raw mode
96
+ process.stdin.setRawMode(false);
97
+ }
98
+ }
99
+ async runLoop(lines, pageSize) {
100
+ let offset = 0;
101
+ return new Promise((resolve) => {
102
+ const render = () => {
103
+ // Clear screen and move cursor to top
104
+ process.stdout.write('\x1B[2J\x1B[H');
105
+ // Show visible lines
106
+ const visible = lines.slice(offset, offset + pageSize);
107
+ visible.forEach(line => console.log(line));
108
+ // Footer with position info
109
+ const start = offset + 1;
110
+ const end = Math.min(offset + pageSize, lines.length);
111
+ const position = `${start}-${end}/${lines.length}`;
112
+ process.stdout.write(`\x1b[2m-- psh pager -- ${position} | j/k scroll | space page | q quit\x1b[0m`);
113
+ };
114
+ const cleanup = () => {
115
+ process.stdin.removeListener('data', onKey);
116
+ // Show cursor and clear screen before returning to shell
117
+ process.stdout.write('\x1B[?25h\x1B[2J\x1B[H');
118
+ };
119
+ const maxOffset = Math.max(0, lines.length - pageSize);
120
+ const onKey = (key) => {
121
+ const ch = key.toString();
122
+ // Handle escape sequences (arrow keys, page up/down)
123
+ if (ch.startsWith('\x1B[')) {
124
+ switch (ch) {
125
+ case '\x1B[B': // Down arrow
126
+ if (offset < maxOffset) {
127
+ offset++;
128
+ }
129
+ render(); // Always re-render to clear any stray characters
130
+ break;
131
+ case '\x1B[A': // Up arrow
132
+ if (offset > 0) {
133
+ offset--;
134
+ }
135
+ render();
136
+ break;
137
+ case '\x1B[6~': // Page Down
138
+ offset = Math.min(offset + pageSize, maxOffset);
139
+ render();
140
+ break;
141
+ case '\x1B[5~': // Page Up
142
+ offset = Math.max(0, offset - pageSize);
143
+ render();
144
+ break;
145
+ default:
146
+ // Unknown escape sequence - re-render to clear
147
+ render();
148
+ }
149
+ return;
150
+ }
151
+ // Single character commands - always re-render to prevent stray chars
152
+ switch (ch) {
153
+ case 'q':
154
+ case '\x03': // Ctrl+C
155
+ cleanup();
156
+ resolve();
157
+ return; // Don't render after cleanup
158
+ case 'j': // Down
159
+ if (offset < maxOffset) {
160
+ offset++;
161
+ }
162
+ break;
163
+ case 'k': // Up
164
+ if (offset > 0) {
165
+ offset--;
166
+ }
167
+ break;
168
+ case ' ': // Page down
169
+ case 'f': // Forward (like less)
170
+ offset = Math.min(offset + pageSize, maxOffset);
171
+ break;
172
+ case 'b': // Page up / backward
173
+ offset = Math.max(0, offset - pageSize);
174
+ break;
175
+ case 'g': // First line
176
+ offset = 0;
177
+ break;
178
+ case 'G': // Last line
179
+ offset = maxOffset;
180
+ break;
181
+ case 'd': // Half page down (like vim)
182
+ offset = Math.min(offset + Math.floor(pageSize / 2), maxOffset);
183
+ break;
184
+ case 'u': // Half page up (like vim)
185
+ offset = Math.max(0, offset - Math.floor(pageSize / 2));
186
+ break;
187
+ }
188
+ // Re-render after any key to clear potential stray characters
189
+ render();
190
+ };
191
+ process.stdin.on('data', onKey);
192
+ render();
193
+ });
194
+ }
195
+ }
196
+ //# sourceMappingURL=less-pager.js.map