sidekick-shared 0.24.5 → 0.25.0
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 +35 -0
- package/README.md +159 -52
- package/dist/accountChangeSignal.d.ts +3 -0
- package/dist/accountChangeSignal.js +19 -0
- package/dist/accountChanges.d.ts +21 -0
- package/dist/accountChanges.js +151 -0
- package/dist/accountRegistry.d.ts +3 -1
- package/dist/accountRegistry.js +5 -0
- package/dist/browser.d.ts +8 -3
- package/dist/browser.js +15 -1
- package/dist/codexQuotaWatcher.d.ts +6 -0
- package/dist/codexQuotaWatcher.js +101 -47
- package/dist/index.d.ts +23 -13
- package/dist/index.js +34 -10
- package/dist/modelAliases.d.ts +13 -0
- package/dist/modelAliases.js +58 -0
- package/dist/modelCatalog.d.ts +32 -0
- package/dist/modelCatalog.js +145 -0
- package/dist/modelContext.d.ts +36 -3
- package/dist/modelContext.js +109 -34
- package/dist/modelInfo.d.ts +30 -1
- package/dist/modelInfo.js +116 -26
- package/dist/multiProviderQuotaService.d.ts +8 -0
- package/dist/multiProviderQuotaService.js +64 -3
- package/dist/node.d.ts +4 -4
- package/dist/node.js +4 -1
- package/dist/observedSessionCollector.d.ts +75 -4
- package/dist/observedSessionCollector.js +452 -42
- package/dist/parsers/codexHistory.d.ts +4 -2
- package/dist/parsers/codexHistory.js +9 -2
- package/dist/parsers/sessionActivityDetector.d.ts +6 -0
- package/dist/parsers/sessionActivityDetector.js +17 -0
- package/dist/providerIds.d.ts +11 -0
- package/dist/providerIds.js +9 -0
- package/dist/providerQuota.d.ts +3 -1
- package/dist/providerQuota.js +3 -0
- package/dist/providers/claudeCode.d.ts +10 -1
- package/dist/providers/claudeCode.js +204 -39
- package/dist/providers/codex.d.ts +18 -1
- package/dist/providers/codex.js +269 -40
- package/dist/providers/codexDatabase.d.ts +9 -0
- package/dist/providers/codexDatabase.js +107 -2
- package/dist/providers/diagnostics.d.ts +13 -0
- package/dist/providers/diagnostics.js +88 -0
- package/dist/providers/factory.d.ts +14 -0
- package/dist/providers/factory.js +57 -0
- package/dist/providers/openCode.d.ts +20 -1
- package/dist/providers/openCode.js +361 -11
- package/dist/providers/openCodeDatabase.d.ts +10 -0
- package/dist/providers/openCodeDatabase.js +69 -0
- package/dist/providers/types.d.ts +52 -2
- package/dist/providers/types.js +4 -0
- package/dist/quota.d.ts +4 -1
- package/dist/quota.js +3 -7
- package/dist/quotaHistory.d.ts +2 -1
- package/dist/quotaPoller.d.ts +13 -1
- package/dist/quotaPoller.js +65 -3
- package/dist/schemas/accountManager.d.ts +2 -2
- package/dist/schemas/accountManager.js +2 -4
- package/dist/schemas/accountStatus.d.ts +3 -0
- package/dist/schemas/accountStatus.js +1 -0
- package/dist/schemas/index.d.ts +1 -0
- package/dist/schemas/index.js +7 -2
- package/dist/schemas/observedSessionV1.d.ts +27 -26
- package/dist/schemas/observedSessionV1.js +6 -4
- package/dist/schemas/quota.d.ts +2 -2
- package/dist/schemas/quota.js +3 -6
- package/dist/schemas/quotaHistory.d.ts +2 -2
- package/dist/schemas/tokenEstimation.d.ts +1 -1
- package/dist/sessionMonitor.d.ts +15 -0
- package/dist/sessionMonitor.js +142 -0
- package/dist/sessionPreviews.d.ts +26 -1
- package/dist/sessionPreviews.js +319 -4
- package/dist/types/observedSessionV1.d.ts +6 -2
- package/dist/types/observedSessionV1.js +14 -8
- package/package.json +22 -1
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Browser-safe provider ID catalogs. Types are derived from these values. */
|
|
2
|
+
export declare const SESSION_PROVIDER_IDS: readonly ["claude-code", "opencode", "codex"];
|
|
3
|
+
export type SessionProviderId = (typeof SESSION_PROVIDER_IDS)[number];
|
|
4
|
+
export declare const ACCOUNT_PROVIDER_IDS: readonly ["claude-code", "codex"];
|
|
5
|
+
export type AccountProviderId = (typeof ACCOUNT_PROVIDER_IDS)[number];
|
|
6
|
+
export declare const QUOTA_PROVIDER_IDS: readonly ["claude-code", "codex", "zai"];
|
|
7
|
+
export type QuotaProviderId = (typeof QUOTA_PROVIDER_IDS)[number];
|
|
8
|
+
export declare const RUNTIME_QUOTA_PROVIDER_IDS: readonly ["claude", "codex", "zai"];
|
|
9
|
+
export type RuntimeQuotaProviderId = (typeof RUNTIME_QUOTA_PROVIDER_IDS)[number];
|
|
10
|
+
export declare const MODEL_PROVIDER_IDS: readonly ["anthropic", "openai", "google", "unknown"];
|
|
11
|
+
export type ModelProviderId = (typeof MODEL_PROVIDER_IDS)[number];
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MODEL_PROVIDER_IDS = exports.RUNTIME_QUOTA_PROVIDER_IDS = exports.QUOTA_PROVIDER_IDS = exports.ACCOUNT_PROVIDER_IDS = exports.SESSION_PROVIDER_IDS = void 0;
|
|
4
|
+
/** Browser-safe provider ID catalogs. Types are derived from these values. */
|
|
5
|
+
exports.SESSION_PROVIDER_IDS = ['claude-code', 'opencode', 'codex'];
|
|
6
|
+
exports.ACCOUNT_PROVIDER_IDS = ['claude-code', 'codex'];
|
|
7
|
+
exports.QUOTA_PROVIDER_IDS = ['claude-code', 'codex', 'zai'];
|
|
8
|
+
exports.RUNTIME_QUOTA_PROVIDER_IDS = ['claude', 'codex', 'zai'];
|
|
9
|
+
exports.MODEL_PROVIDER_IDS = ['anthropic', 'openai', 'google', 'unknown'];
|
package/dist/providerQuota.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { PeakHoursState } from './peakHours';
|
|
2
2
|
import type { QuotaState } from './quota';
|
|
3
3
|
import type { QuotaFailureDescriptor } from './quotaPresentation';
|
|
4
|
-
|
|
4
|
+
import type { RuntimeQuotaProviderId } from './providerIds';
|
|
5
|
+
export { RUNTIME_QUOTA_PROVIDER_IDS } from './providerIds';
|
|
6
|
+
export type RuntimeQuotaProvider = RuntimeQuotaProviderId;
|
|
5
7
|
export interface ProviderQuotaState<TProvider extends RuntimeQuotaProvider = RuntimeQuotaProvider> extends QuotaState {
|
|
6
8
|
runtimeProvider: TProvider;
|
|
7
9
|
accountLabel?: string;
|
package/dist/providerQuota.js
CHANGED
|
@@ -1,2 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RUNTIME_QUOTA_PROVIDER_IDS = void 0;
|
|
4
|
+
var providerIds_1 = require("./providerIds");
|
|
5
|
+
Object.defineProperty(exports, "RUNTIME_QUOTA_PROVIDER_IDS", { enumerable: true, get: function () { return providerIds_1.RUNTIME_QUOTA_PROVIDER_IDS; } });
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import type { ReadSessionContextSnapshotOptions, SessionContextSnapshot } from '../context/sessionContext';
|
|
11
11
|
import type { SubagentStats, TokenUsage } from '../types/sessionEvent';
|
|
12
|
-
import type { SessionProviderBase, SessionReader, SessionFileInfo, SessionFileStats, SearchHit, ProjectFolderInfo, ProviderId } from './types';
|
|
12
|
+
import type { SessionProviderBase, SessionReader, SessionFileInfo, SessionFileStats, SearchHit, ProjectFolderInfo, ProviderId, ProviderOperationStatus, ProviderRuntimeStatus, SessionProviderOptions } from './types';
|
|
13
13
|
/**
|
|
14
14
|
* Session provider for Claude Code CLI.
|
|
15
15
|
*
|
|
@@ -20,27 +20,35 @@ import type { SessionProviderBase, SessionReader, SessionFileInfo, SessionFileSt
|
|
|
20
20
|
export declare class ClaudeCodeProvider implements SessionProviderBase {
|
|
21
21
|
readonly id: ProviderId;
|
|
22
22
|
readonly displayName = "Claude Code";
|
|
23
|
+
private readonly diagnostics;
|
|
23
24
|
/** Runtime-reported context window limit (overrides static map when set). */
|
|
24
25
|
private dynamicContextWindowLimit;
|
|
26
|
+
constructor(options?: SessionProviderOptions);
|
|
25
27
|
getSessionDirectory(workspacePath: string): string;
|
|
26
28
|
discoverSessionDirectory(workspacePath: string): string | null;
|
|
27
29
|
findActiveSession(workspacePath: string): string | null;
|
|
28
30
|
findAllSessions(workspacePath: string): string[];
|
|
31
|
+
findSessionById(workspacePath: string, sessionId: string): string | null;
|
|
29
32
|
/** Backward-compatible alias for findAllSessions. */
|
|
30
33
|
findSessionFiles(workspacePath: string): string[];
|
|
31
34
|
findSessionsInDirectory(dir: string): string[];
|
|
32
35
|
getAllProjectFolders(workspacePath?: string): ProjectFolderInfo[];
|
|
33
36
|
listAllSessionFiles(): SessionFileInfo[];
|
|
37
|
+
listSessionFilesAsync(workspacePath?: string): Promise<SessionFileInfo[]>;
|
|
34
38
|
isSessionFile(filename: string): boolean;
|
|
35
39
|
getSessionId(sessionPath: string): string;
|
|
36
40
|
encodeWorkspacePath(workspacePath: string): string;
|
|
37
41
|
extractSessionLabel(sessionPath: string): string | null;
|
|
42
|
+
extractSessionLabelsAsync(sessionPaths: readonly string[]): Promise<Map<string, string | null>>;
|
|
38
43
|
createReader(sessionPath: string): SessionReader;
|
|
39
44
|
scanSubagents(sessionDir: string, sessionId: string): SubagentStats[];
|
|
40
45
|
searchInSession(sessionPath: string, query: string, maxResults: number): SearchHit[];
|
|
41
46
|
getProjectsBaseDir(): string;
|
|
47
|
+
getWatchRoots(): string[];
|
|
42
48
|
readSessionStats(sessionPath: string): SessionFileStats;
|
|
43
49
|
readSessionContextSnapshot(sessionPath: string, options?: ReadSessionContextSnapshotOptions): SessionContextSnapshot;
|
|
50
|
+
getRuntimeStatus(): ProviderRuntimeStatus;
|
|
51
|
+
getLastOperationStatus(): ProviderOperationStatus;
|
|
44
52
|
getContextWindowLimit(modelId?: string): number;
|
|
45
53
|
/** Set a runtime-reported context window limit (overrides static map). */
|
|
46
54
|
setDynamicContextWindowLimit(limit: number): void;
|
|
@@ -52,4 +60,5 @@ export declare class ClaudeCodeProvider implements SessionProviderBase {
|
|
|
52
60
|
*/
|
|
53
61
|
getCurrentUsageSnapshot(sessionPath: string): TokenUsage | null;
|
|
54
62
|
dispose(): void;
|
|
63
|
+
private recordHomeStatus;
|
|
55
64
|
}
|
|
@@ -49,6 +49,7 @@ const path = __importStar(require("path"));
|
|
|
49
49
|
const string_decoder_1 = require("string_decoder");
|
|
50
50
|
const sessionContext_1 = require("../context/sessionContext");
|
|
51
51
|
const jsonl_1 = require("../parsers/jsonl");
|
|
52
|
+
const diagnostics_1 = require("./diagnostics");
|
|
52
53
|
const sessionPathResolver_1 = require("../parsers/sessionPathResolver");
|
|
53
54
|
const subagentScanner_1 = require("../parsers/subagentScanner");
|
|
54
55
|
const modelContext_1 = require("../modelContext");
|
|
@@ -88,6 +89,42 @@ function extractSearchableText(event) {
|
|
|
88
89
|
}
|
|
89
90
|
return '';
|
|
90
91
|
}
|
|
92
|
+
function extractClaudeLabelFromPrefix(chunk) {
|
|
93
|
+
for (const line of chunk.split('\n')) {
|
|
94
|
+
const trimmed = line.trim();
|
|
95
|
+
if (!trimmed)
|
|
96
|
+
continue;
|
|
97
|
+
try {
|
|
98
|
+
const event = JSON.parse(trimmed);
|
|
99
|
+
if (event.type !== 'user')
|
|
100
|
+
continue;
|
|
101
|
+
const content = event.message?.content;
|
|
102
|
+
if (!content)
|
|
103
|
+
continue;
|
|
104
|
+
let text = null;
|
|
105
|
+
if (typeof content === 'string') {
|
|
106
|
+
text = content.trim();
|
|
107
|
+
}
|
|
108
|
+
else if (Array.isArray(content)) {
|
|
109
|
+
const textBlock = content.find((block) => isTypedBlock(block) &&
|
|
110
|
+
block.type === 'text' &&
|
|
111
|
+
typeof block.text === 'string' &&
|
|
112
|
+
block.text.trim().length > 0);
|
|
113
|
+
if (textBlock && isTypedBlock(textBlock) && typeof textBlock.text === 'string') {
|
|
114
|
+
text = textBlock.text.trim();
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if (text) {
|
|
118
|
+
const compact = text.replace(/\s+/g, ' ');
|
|
119
|
+
return compact.length > 60 ? compact.substring(0, 57) + '...' : compact;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
// Skip malformed lines.
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
91
128
|
/**
|
|
92
129
|
* Incremental JSONL reader for Claude Code session files.
|
|
93
130
|
*
|
|
@@ -234,8 +271,12 @@ function normalizeClaudeUsage(event) {
|
|
|
234
271
|
class ClaudeCodeProvider {
|
|
235
272
|
id = 'claude-code';
|
|
236
273
|
displayName = 'Claude Code';
|
|
274
|
+
diagnostics;
|
|
237
275
|
/** Runtime-reported context window limit (overrides static map when set). */
|
|
238
276
|
dynamicContextWindowLimit = null;
|
|
277
|
+
constructor(options = {}) {
|
|
278
|
+
this.diagnostics = new diagnostics_1.ProviderDiagnosticTracker(this.id, options);
|
|
279
|
+
}
|
|
239
280
|
// --- Path resolution ---
|
|
240
281
|
getSessionDirectory(workspacePath) {
|
|
241
282
|
return (0, sessionPathResolver_1.getSessionDirectory)(workspacePath);
|
|
@@ -250,6 +291,43 @@ class ClaudeCodeProvider {
|
|
|
250
291
|
findAllSessions(workspacePath) {
|
|
251
292
|
return (0, sessionPathResolver_1.findAllSessions)(workspacePath);
|
|
252
293
|
}
|
|
294
|
+
findSessionById(workspacePath, sessionId) {
|
|
295
|
+
const normalizedId = sessionId.trim();
|
|
296
|
+
if (!normalizedId || path.basename(normalizedId) !== normalizedId)
|
|
297
|
+
return null;
|
|
298
|
+
try {
|
|
299
|
+
const directories = new Set();
|
|
300
|
+
directories.add(this.getSessionDirectory(workspacePath));
|
|
301
|
+
const discovered = this.discoverSessionDirectory(workspacePath);
|
|
302
|
+
if (discovered)
|
|
303
|
+
directories.add(discovered);
|
|
304
|
+
for (const folder of this.getAllProjectFolders(workspacePath))
|
|
305
|
+
directories.add(folder.dir);
|
|
306
|
+
for (const directory of directories) {
|
|
307
|
+
const direct = path.join(directory, `${normalizedId}.jsonl`);
|
|
308
|
+
try {
|
|
309
|
+
if (fs.statSync(direct).isFile()) {
|
|
310
|
+
this.diagnostics.available('findSessionById');
|
|
311
|
+
return direct;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
catch {
|
|
315
|
+
// Continue with directory-shaped lookup for worktree layouts.
|
|
316
|
+
}
|
|
317
|
+
const match = this.findSessionsInDirectory(directory).find((candidate) => this.getSessionId(candidate) === normalizedId);
|
|
318
|
+
if (match) {
|
|
319
|
+
this.diagnostics.available('findSessionById');
|
|
320
|
+
return match;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
this.recordHomeStatus('findSessionById');
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
326
|
+
catch {
|
|
327
|
+
this.recordHomeStatus('findSessionById');
|
|
328
|
+
return null;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
253
331
|
/** Backward-compatible alias for findAllSessions. */
|
|
254
332
|
findSessionFiles(workspacePath) {
|
|
255
333
|
return this.findAllSessions(workspacePath);
|
|
@@ -276,8 +354,81 @@ class ClaudeCodeProvider {
|
|
|
276
354
|
}
|
|
277
355
|
}
|
|
278
356
|
}
|
|
357
|
+
this.recordHomeStatus('listAllSessionFiles');
|
|
279
358
|
return results;
|
|
280
359
|
}
|
|
360
|
+
async listSessionFilesAsync(workspacePath) {
|
|
361
|
+
const projectsRoot = this.getProjectsBaseDir();
|
|
362
|
+
let projectEntries = [];
|
|
363
|
+
try {
|
|
364
|
+
projectEntries = await fs.promises.readdir(projectsRoot, { withFileTypes: true });
|
|
365
|
+
}
|
|
366
|
+
catch {
|
|
367
|
+
this.recordHomeStatus('listSessionFilesAsync');
|
|
368
|
+
return [];
|
|
369
|
+
}
|
|
370
|
+
const encodedWorkspace = workspacePath ? (0, sessionPathResolver_1.encodeWorkspacePath)(workspacePath).toLowerCase() : null;
|
|
371
|
+
const workspaceBasename = workspacePath
|
|
372
|
+
? path
|
|
373
|
+
.basename(workspacePath)
|
|
374
|
+
.replace(/[^a-zA-Z0-9]/g, '-')
|
|
375
|
+
.toLowerCase()
|
|
376
|
+
: null;
|
|
377
|
+
const directories = [];
|
|
378
|
+
for (const entry of projectEntries) {
|
|
379
|
+
const directory = path.join(projectsRoot, entry.name);
|
|
380
|
+
try {
|
|
381
|
+
const stat = entry.isDirectory() ? null : await fs.promises.stat(directory);
|
|
382
|
+
if (!entry.isDirectory() && !stat?.isDirectory())
|
|
383
|
+
continue;
|
|
384
|
+
}
|
|
385
|
+
catch {
|
|
386
|
+
continue;
|
|
387
|
+
}
|
|
388
|
+
const name = entry.name.toLowerCase();
|
|
389
|
+
if (encodedWorkspace &&
|
|
390
|
+
name !== encodedWorkspace &&
|
|
391
|
+
!name.startsWith(`${encodedWorkspace}-`) &&
|
|
392
|
+
name !== workspaceBasename &&
|
|
393
|
+
!name.endsWith(`-${workspaceBasename}`)) {
|
|
394
|
+
continue;
|
|
395
|
+
}
|
|
396
|
+
directories.push(directory);
|
|
397
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
398
|
+
}
|
|
399
|
+
const results = [];
|
|
400
|
+
for (const directory of directories) {
|
|
401
|
+
let entries = [];
|
|
402
|
+
try {
|
|
403
|
+
entries = await fs.promises.readdir(directory, { withFileTypes: true });
|
|
404
|
+
}
|
|
405
|
+
catch {
|
|
406
|
+
continue;
|
|
407
|
+
}
|
|
408
|
+
for (const entry of entries) {
|
|
409
|
+
if (!entry.isFile() || !this.isSessionFile(entry.name))
|
|
410
|
+
continue;
|
|
411
|
+
const sessionPath = path.join(directory, entry.name);
|
|
412
|
+
try {
|
|
413
|
+
const stat = await fs.promises.stat(sessionPath);
|
|
414
|
+
if (stat.size > 0) {
|
|
415
|
+
results.push({
|
|
416
|
+
path: sessionPath,
|
|
417
|
+
mtime: stat.mtime,
|
|
418
|
+
sizeBytes: stat.size,
|
|
419
|
+
sessionId: this.getSessionId(sessionPath),
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
catch {
|
|
424
|
+
// Skip files that vanish between discovery and stat.
|
|
425
|
+
}
|
|
426
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
this.recordHomeStatus('listSessionFilesAsync');
|
|
430
|
+
return results.sort((left, right) => right.mtime.getTime() - left.mtime.getTime());
|
|
431
|
+
}
|
|
281
432
|
// --- File identification ---
|
|
282
433
|
isSessionFile(filename) {
|
|
283
434
|
return filename.endsWith('.jsonl');
|
|
@@ -296,50 +447,32 @@ class ClaudeCodeProvider {
|
|
|
296
447
|
fs.closeSync(fd);
|
|
297
448
|
if (bytesRead === 0)
|
|
298
449
|
return null;
|
|
299
|
-
|
|
300
|
-
const lines = chunk.split('\n');
|
|
301
|
-
for (const line of lines) {
|
|
302
|
-
const trimmed = line.trim();
|
|
303
|
-
if (!trimmed)
|
|
304
|
-
continue;
|
|
305
|
-
try {
|
|
306
|
-
const event = JSON.parse(trimmed);
|
|
307
|
-
if (event.type !== 'user')
|
|
308
|
-
continue;
|
|
309
|
-
const content = event.message?.content;
|
|
310
|
-
if (!content)
|
|
311
|
-
continue;
|
|
312
|
-
let text = null;
|
|
313
|
-
if (typeof content === 'string') {
|
|
314
|
-
text = content.trim();
|
|
315
|
-
}
|
|
316
|
-
else if (Array.isArray(content)) {
|
|
317
|
-
const textBlock = content.find((block) => isTypedBlock(block) &&
|
|
318
|
-
block.type === 'text' &&
|
|
319
|
-
typeof block.text === 'string' &&
|
|
320
|
-
block.text.trim().length > 0);
|
|
321
|
-
if (textBlock && isTypedBlock(textBlock) && typeof textBlock.text === 'string') {
|
|
322
|
-
text = textBlock.text.trim();
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
if (text && text.length > 0) {
|
|
326
|
-
text = text.replace(/\s+/g, ' ');
|
|
327
|
-
if (text.length > 60) {
|
|
328
|
-
text = text.substring(0, 57) + '...';
|
|
329
|
-
}
|
|
330
|
-
return text;
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
catch {
|
|
334
|
-
// Skip malformed lines
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
return null;
|
|
450
|
+
return extractClaudeLabelFromPrefix(buffer.toString('utf-8', 0, bytesRead));
|
|
338
451
|
}
|
|
339
452
|
catch {
|
|
340
453
|
return null;
|
|
341
454
|
}
|
|
342
455
|
}
|
|
456
|
+
async extractSessionLabelsAsync(sessionPaths) {
|
|
457
|
+
const results = new Map();
|
|
458
|
+
for (const sessionPath of sessionPaths) {
|
|
459
|
+
let handle = null;
|
|
460
|
+
try {
|
|
461
|
+
handle = await fs.promises.open(sessionPath, 'r');
|
|
462
|
+
const buffer = Buffer.alloc(8192);
|
|
463
|
+
const { bytesRead } = await handle.read(buffer, 0, buffer.length, 0);
|
|
464
|
+
results.set(sessionPath, extractClaudeLabelFromPrefix(buffer.toString('utf8', 0, bytesRead)));
|
|
465
|
+
}
|
|
466
|
+
catch {
|
|
467
|
+
results.set(sessionPath, null);
|
|
468
|
+
}
|
|
469
|
+
finally {
|
|
470
|
+
await handle?.close().catch(() => undefined);
|
|
471
|
+
}
|
|
472
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
473
|
+
}
|
|
474
|
+
return results;
|
|
475
|
+
}
|
|
343
476
|
// --- Data reading ---
|
|
344
477
|
createReader(sessionPath) {
|
|
345
478
|
return new ClaudeCodeReader(sessionPath);
|
|
@@ -397,6 +530,9 @@ class ClaudeCodeProvider {
|
|
|
397
530
|
getProjectsBaseDir() {
|
|
398
531
|
return path.join(os.homedir(), '.claude', 'projects');
|
|
399
532
|
}
|
|
533
|
+
getWatchRoots() {
|
|
534
|
+
return [this.getProjectsBaseDir()];
|
|
535
|
+
}
|
|
400
536
|
// --- Stats ---
|
|
401
537
|
readSessionStats(sessionPath) {
|
|
402
538
|
const sessionId = path.basename(sessionPath, '.jsonl');
|
|
@@ -491,6 +627,22 @@ class ClaudeCodeProvider {
|
|
|
491
627
|
return (0, sessionContext_1.readSessionContextSnapshot)(this, sessionPath, options);
|
|
492
628
|
}
|
|
493
629
|
// --- Optional methods ---
|
|
630
|
+
getRuntimeStatus() {
|
|
631
|
+
try {
|
|
632
|
+
fs.accessSync(this.getProjectsBaseDir(), fs.constants.R_OK);
|
|
633
|
+
return { available: true, kind: 'available' };
|
|
634
|
+
}
|
|
635
|
+
catch {
|
|
636
|
+
return {
|
|
637
|
+
available: false,
|
|
638
|
+
kind: 'home_unavailable',
|
|
639
|
+
message: 'Claude Code session home is unavailable.',
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
getLastOperationStatus() {
|
|
644
|
+
return this.diagnostics.getLastOperationStatus();
|
|
645
|
+
}
|
|
494
646
|
getContextWindowLimit(modelId) {
|
|
495
647
|
if (this.dynamicContextWindowLimit)
|
|
496
648
|
return this.dynamicContextWindowLimit;
|
|
@@ -552,5 +704,18 @@ class ClaudeCodeProvider {
|
|
|
552
704
|
dispose() {
|
|
553
705
|
this.dynamicContextWindowLimit = null;
|
|
554
706
|
}
|
|
707
|
+
recordHomeStatus(operation) {
|
|
708
|
+
const status = this.getRuntimeStatus();
|
|
709
|
+
if (status.available) {
|
|
710
|
+
this.diagnostics.available(operation);
|
|
711
|
+
return;
|
|
712
|
+
}
|
|
713
|
+
this.diagnostics.degraded(operation, status, {
|
|
714
|
+
kind: 'home_unavailable',
|
|
715
|
+
severity: 'info',
|
|
716
|
+
phase: 'enumerate',
|
|
717
|
+
message: status.message ?? 'Claude Code session home is unavailable.',
|
|
718
|
+
});
|
|
719
|
+
}
|
|
555
720
|
}
|
|
556
721
|
exports.ClaudeCodeProvider = ClaudeCodeProvider;
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* Ported from sidekick-vscode/src/services/providers/CodexSessionProvider.ts
|
|
10
10
|
*/
|
|
11
11
|
import type { ReadSessionContextSnapshotOptions, SessionContextSnapshot } from '../context/sessionContext';
|
|
12
|
-
import type { SessionProviderBase, SessionReader, ProjectFolderInfo, SearchHit, SessionFileInfo, SessionFileStats, ProviderId } from './types';
|
|
12
|
+
import type { SessionProviderBase, SessionReader, ProjectFolderInfo, SearchHit, SessionFileInfo, SessionFileStats, ProviderId, ProviderOperationStatus, ProviderRuntimeStatus, SessionProviderOptions } from './types';
|
|
13
13
|
import type { SubagentStats, TokenUsage } from '../types/sessionEvent';
|
|
14
14
|
import type { CodexRateLimits } from '../types/codex';
|
|
15
15
|
export interface FindCodexRolloutFileOptions {
|
|
@@ -43,24 +43,39 @@ export declare class CodexProvider implements SessionProviderBase {
|
|
|
43
43
|
readonly displayName = "Codex CLI";
|
|
44
44
|
private db;
|
|
45
45
|
private dbInitialized;
|
|
46
|
+
private dbStatus;
|
|
47
|
+
private readonly diagnostics;
|
|
46
48
|
private dynamicContextWindowLimit;
|
|
47
49
|
private lastRateLimitsData;
|
|
48
50
|
private lastTokenUsageData;
|
|
51
|
+
constructor(options?: SessionProviderOptions);
|
|
49
52
|
/** Lazy-initialize database connection. */
|
|
50
53
|
private ensureDb;
|
|
54
|
+
private ensureDbAsync;
|
|
55
|
+
/**
|
|
56
|
+
* Cache an async-created database only after an operation proved it usable.
|
|
57
|
+
* Sync paths treat a cached `db` as "open() succeeded", so adopting an
|
|
58
|
+
* unproven instance would silently disable their file fallbacks.
|
|
59
|
+
*/
|
|
60
|
+
private adoptDbIfAvailable;
|
|
61
|
+
getRuntimeStatus(): ProviderRuntimeStatus;
|
|
62
|
+
getLastOperationStatus(): ProviderOperationStatus;
|
|
51
63
|
getSessionDirectory(workspacePath: string): string;
|
|
52
64
|
discoverSessionDirectory(workspacePath: string): string | null;
|
|
53
65
|
findActiveSession(workspacePath: string): string | null;
|
|
54
66
|
findAllSessions(workspacePath: string): string[];
|
|
67
|
+
findSessionById(_workspacePath: string, sessionId: string): string | null;
|
|
55
68
|
/** Backward-compatible alias for findAllSessions. */
|
|
56
69
|
findSessionFiles(workspacePath: string): string[];
|
|
57
70
|
findSessionsInDirectory(dir: string): string[];
|
|
58
71
|
listAllSessionFiles(): SessionFileInfo[];
|
|
72
|
+
listSessionFilesAsync(workspacePath?: string): Promise<SessionFileInfo[]>;
|
|
59
73
|
getAllProjectFolders(workspacePath?: string): ProjectFolderInfo[];
|
|
60
74
|
isSessionFile(filename: string): boolean;
|
|
61
75
|
getSessionId(sessionPath: string): string;
|
|
62
76
|
encodeWorkspacePath(workspacePath: string): string;
|
|
63
77
|
extractSessionLabel(sessionPath: string): string | null;
|
|
78
|
+
extractSessionLabelsAsync(sessionPaths: readonly string[]): Promise<Map<string, string | null>>;
|
|
64
79
|
createReader(sessionPath: string): SessionReader;
|
|
65
80
|
scanSubagents(_sessionDir: string, sessionId: string): SubagentStats[];
|
|
66
81
|
/**
|
|
@@ -69,6 +84,7 @@ export declare class CodexProvider implements SessionProviderBase {
|
|
|
69
84
|
private buildSubagentStats;
|
|
70
85
|
searchInSession(sessionPath: string, query: string, maxResults: number): SearchHit[];
|
|
71
86
|
getProjectsBaseDir(): string;
|
|
87
|
+
getWatchRoots(): string[];
|
|
72
88
|
readSessionStats(sessionPath: string): SessionFileStats;
|
|
73
89
|
readSessionContextSnapshot(sessionPath: string, options?: ReadSessionContextSnapshotOptions): SessionContextSnapshot;
|
|
74
90
|
getContextWindowLimit(modelId?: string): number;
|
|
@@ -77,4 +93,5 @@ export declare class CodexProvider implements SessionProviderBase {
|
|
|
77
93
|
/** Get the last rate limits data received from token_count events. */
|
|
78
94
|
getLastRateLimits(): CodexRateLimits | null;
|
|
79
95
|
dispose(): void;
|
|
96
|
+
private recordDatabaseFallback;
|
|
80
97
|
}
|