sidekick-shared 0.24.3 → 0.24.5
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 +436 -0
- package/LICENSE +21 -0
- package/README.md +172 -60
- package/dist/accountManager.d.ts +13 -0
- package/dist/accountManager.js +60 -32
- package/dist/accountRegistry.js +59 -41
- package/dist/accounts.js +40 -19
- package/dist/autoSwitch.d.ts +2 -1
- package/dist/autoSwitch.js +32 -7
- package/dist/browser.d.ts +2 -0
- package/dist/browser.js +3 -1
- package/dist/codexProfiles.d.ts +14 -0
- package/dist/codexProfiles.js +268 -116
- package/dist/context/sessionContext.js +2 -4
- package/dist/ensureDefaultAccounts.js +4 -4
- package/dist/index.d.ts +14 -7
- package/dist/index.js +33 -7
- package/dist/modelInfo.js +11 -2
- package/dist/node.d.ts +6 -0
- package/dist/node.js +8 -1
- package/dist/observedContextWindows.d.ts +20 -4
- package/dist/observedContextWindows.js +47 -7
- package/dist/parsers/codexHistory.d.ts +30 -0
- package/dist/parsers/codexHistory.js +118 -0
- package/dist/parsers/mcpToolName.d.ts +26 -0
- package/dist/parsers/mcpToolName.js +31 -0
- package/dist/parsers/sessionPathResolver.d.ts +11 -4
- package/dist/parsers/sessionPathResolver.js +11 -4
- package/dist/paths.d.ts +43 -4
- package/dist/paths.js +66 -6
- package/dist/peakHours.d.ts +16 -2
- package/dist/peakHours.js +18 -3
- package/dist/pricingCatalog.d.ts +7 -3
- package/dist/pricingCatalog.js +3 -2
- package/dist/providers/claudeCode.d.ts +2 -1
- package/dist/providers/claudeCode.js +18 -0
- package/dist/providers/codex.d.ts +22 -1
- package/dist/providers/codex.js +58 -0
- package/dist/providers/types.d.ts +5 -0
- package/dist/quotaHistory.d.ts +1 -1
- package/dist/quotaHistory.js +2 -2
- package/dist/quotaSnapshots.js +8 -77
- package/dist/sessionPreviews.d.ts +60 -0
- package/dist/sessionPreviews.js +234 -0
- package/dist/sessionTranscripts.js +10 -4
- package/dist/writers/atomic.d.ts +20 -0
- package/dist/writers/atomic.js +190 -19
- package/dist/zaiQuota.d.ts +87 -0
- package/dist/zaiQuota.js +49 -0
- package/dist/zaiQuotaWatcher.d.ts +7 -0
- package/dist/zaiQuotaWatcher.js +3 -0
- package/package.json +60 -2
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Claude Code exposes MCP tools under composite identifiers of the form
|
|
4
|
+
* `mcp__<server>__<tool>`. Splitting them correctly is subtle enough that
|
|
5
|
+
* consumers keep re-implementing it by hand (usually with a plain
|
|
6
|
+
* `split('__')`, which mangles server names containing single underscores).
|
|
7
|
+
* This module is the canonical splitter. Browser-safe: no Node imports.
|
|
8
|
+
*
|
|
9
|
+
* @module mcpToolName
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.parseMcpToolName = parseMcpToolName;
|
|
13
|
+
const MCP_TOOL_NAME_PATTERN = /^mcp__([^_]+(?:_[^_]+)*)__(.+)$/;
|
|
14
|
+
/**
|
|
15
|
+
* Splits a Claude Code MCP tool identifier `mcp__<server>__<tool>` into parts.
|
|
16
|
+
*
|
|
17
|
+
* The server name ends at the first double underscore after the prefix, so
|
|
18
|
+
* single underscores stay inside it (`mcp__plugin_context7_context7__query-docs`
|
|
19
|
+
* → server `plugin_context7_context7`) and the tool keeps any further double
|
|
20
|
+
* underscores (`mcp__a__b__c` → tool `b__c`).
|
|
21
|
+
*
|
|
22
|
+
* Returns `null` for anything that is not a well-formed MCP tool id: a
|
|
23
|
+
* non-`mcp__` name, a missing or empty server or tool part, or a
|
|
24
|
+
* case-mismatched prefix. No trimming is applied.
|
|
25
|
+
*/
|
|
26
|
+
function parseMcpToolName(name) {
|
|
27
|
+
const match = MCP_TOOL_NAME_PATTERN.exec(name);
|
|
28
|
+
if (!match)
|
|
29
|
+
return null;
|
|
30
|
+
return { serverName: match[1], toolName: match[2] };
|
|
31
|
+
}
|
|
@@ -11,10 +11,17 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import type { ProjectFolderInfo } from '../providers/types';
|
|
13
13
|
/**
|
|
14
|
-
* Encodes a workspace path to Claude Code's directory naming scheme
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
14
|
+
* Encodes a workspace path to Claude Code's on-disk directory naming scheme
|
|
15
|
+
* under `~/.claude/projects/` — every non-ASCII-alphanumeric character
|
|
16
|
+
* (separators, colons, underscores, dots, spaces) becomes a hyphen, and case
|
|
17
|
+
* is preserved. Exported from the package root as `encodeClaudeWorkspacePath`.
|
|
18
|
+
*
|
|
19
|
+
* Not to be confused with the top-level `encodeWorkspacePath` export from
|
|
20
|
+
* `paths.ts`: that one is Sidekick's own config-store slug (it keeps dots) and
|
|
21
|
+
* does NOT match Claude Code's scheme — using it to build a
|
|
22
|
+
* `~/.claude/projects/` path silently misses for any workspace containing a
|
|
23
|
+
* dot or space. For lookups, prefer `getClaudeSessionDirectory` or the
|
|
24
|
+
* mismatch-tolerant `discoverSessionDirectory` over raw string encoding.
|
|
18
25
|
*
|
|
19
26
|
* @param workspacePath - Absolute path to workspace directory
|
|
20
27
|
* @returns Encoded path string (e.g., "-home-user-code-project")
|
|
@@ -61,10 +61,17 @@ const fs = __importStar(require("fs"));
|
|
|
61
61
|
const path = __importStar(require("path"));
|
|
62
62
|
const os = __importStar(require("os"));
|
|
63
63
|
/**
|
|
64
|
-
* Encodes a workspace path to Claude Code's directory naming scheme
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
64
|
+
* Encodes a workspace path to Claude Code's on-disk directory naming scheme
|
|
65
|
+
* under `~/.claude/projects/` — every non-ASCII-alphanumeric character
|
|
66
|
+
* (separators, colons, underscores, dots, spaces) becomes a hyphen, and case
|
|
67
|
+
* is preserved. Exported from the package root as `encodeClaudeWorkspacePath`.
|
|
68
|
+
*
|
|
69
|
+
* Not to be confused with the top-level `encodeWorkspacePath` export from
|
|
70
|
+
* `paths.ts`: that one is Sidekick's own config-store slug (it keeps dots) and
|
|
71
|
+
* does NOT match Claude Code's scheme — using it to build a
|
|
72
|
+
* `~/.claude/projects/` path silently misses for any workspace containing a
|
|
73
|
+
* dot or space. For lookups, prefer `getClaudeSessionDirectory` or the
|
|
74
|
+
* mismatch-tolerant `discoverSessionDirectory` over raw string encoding.
|
|
68
75
|
*
|
|
69
76
|
* @param workspacePath - Absolute path to workspace directory
|
|
70
77
|
* @returns Encoded path string (e.g., "-home-user-code-project")
|
package/dist/paths.d.ts
CHANGED
|
@@ -3,9 +3,44 @@
|
|
|
3
3
|
* Mirrors patterns from sidekick-vscode/src/services/SessionPathResolver.ts
|
|
4
4
|
* and TaskPersistenceService.ts
|
|
5
5
|
*/
|
|
6
|
+
/** Environment variable that redirects the Sidekick config root. */
|
|
7
|
+
export declare const CONFIG_DIR_ENV_VAR = "SIDEKICK_CONFIG_DIR";
|
|
8
|
+
/**
|
|
9
|
+
* The platform default config directory, ignoring both {@link setConfigDir}
|
|
10
|
+
* and `SIDEKICK_CONFIG_DIR`.
|
|
11
|
+
*
|
|
12
|
+
* `~/.config/sidekick` on Unix, `%APPDATA%/sidekick` on Windows.
|
|
13
|
+
*/
|
|
14
|
+
export declare function getDefaultConfigDir(): string;
|
|
15
|
+
/**
|
|
16
|
+
* Redirect the Sidekick config root for this process.
|
|
17
|
+
*
|
|
18
|
+
* Takes precedence over `SIDEKICK_CONFIG_DIR`. Pass `null` (or an empty
|
|
19
|
+
* string) to clear it. Every reader and writer in the package resolves the
|
|
20
|
+
* root lazily through {@link getConfigDir}, so this takes effect immediately
|
|
21
|
+
* for all of them — including transitive callers of `getProjectDataPath`,
|
|
22
|
+
* `getGlobalDataPath`, and the account registry.
|
|
23
|
+
*
|
|
24
|
+
* Prefer `SIDEKICK_CONFIG_DIR` when the value is known before the process
|
|
25
|
+
* starts: it is read fresh on every call, so it stays correct even if a
|
|
26
|
+
* bundler ends up with more than one copy of this module.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* setConfigDir(path.join(os.tmpdir(), 'sidekick-fixture'));
|
|
30
|
+
* try {
|
|
31
|
+
* await addTask(project, { subject: 'isolated' });
|
|
32
|
+
* } finally {
|
|
33
|
+
* setConfigDir(null);
|
|
34
|
+
* }
|
|
35
|
+
*/
|
|
36
|
+
export declare function setConfigDir(dir: string | null | undefined): void;
|
|
37
|
+
/** The active {@link setConfigDir} override, or `null` when unset. */
|
|
38
|
+
export declare function getConfigDirOverride(): string | null;
|
|
6
39
|
/**
|
|
7
40
|
* Gets the Sidekick config directory.
|
|
8
|
-
*
|
|
41
|
+
*
|
|
42
|
+
* Resolution order: {@link setConfigDir} override → `SIDEKICK_CONFIG_DIR`
|
|
43
|
+
* → {@link getDefaultConfigDir}.
|
|
9
44
|
*/
|
|
10
45
|
export declare function getConfigDir(): string;
|
|
11
46
|
/**
|
|
@@ -19,10 +54,14 @@ export declare function getProjectDataPath(slug: string, subdomain: string): str
|
|
|
19
54
|
*/
|
|
20
55
|
export declare function getGlobalDataPath(filename: string): string;
|
|
21
56
|
/**
|
|
22
|
-
* Encodes a workspace path
|
|
23
|
-
*
|
|
57
|
+
* Encodes a workspace path into Sidekick's own config-store slug — path
|
|
58
|
+
* separators, colons, and underscores become hyphens, dots are kept.
|
|
24
59
|
*
|
|
25
|
-
*
|
|
60
|
+
* This is NOT Claude Code's `~/.claude/projects/` naming scheme, despite the
|
|
61
|
+
* shared name: Claude Code also replaces dots, spaces, and every other
|
|
62
|
+
* non-alphanumeric character. To locate Claude Code session directories use
|
|
63
|
+
* `encodeClaudeWorkspacePath` / `getClaudeSessionDirectory` from
|
|
64
|
+
* `parsers/sessionPathResolver` instead.
|
|
26
65
|
*/
|
|
27
66
|
export declare function encodeWorkspacePath(workspacePath: string): string;
|
|
28
67
|
/**
|
package/dist/paths.js
CHANGED
|
@@ -38,6 +38,10 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
38
38
|
};
|
|
39
39
|
})();
|
|
40
40
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
|
+
exports.CONFIG_DIR_ENV_VAR = void 0;
|
|
42
|
+
exports.getDefaultConfigDir = getDefaultConfigDir;
|
|
43
|
+
exports.setConfigDir = setConfigDir;
|
|
44
|
+
exports.getConfigDirOverride = getConfigDirOverride;
|
|
41
45
|
exports.getConfigDir = getConfigDir;
|
|
42
46
|
exports.getProjectDataPath = getProjectDataPath;
|
|
43
47
|
exports.getGlobalDataPath = getGlobalDataPath;
|
|
@@ -48,16 +52,68 @@ exports.getProjectSlugRaw = getProjectSlugRaw;
|
|
|
48
52
|
const fs = __importStar(require("fs"));
|
|
49
53
|
const path = __importStar(require("path"));
|
|
50
54
|
const os = __importStar(require("os"));
|
|
55
|
+
/** Environment variable that redirects the Sidekick config root. */
|
|
56
|
+
exports.CONFIG_DIR_ENV_VAR = 'SIDEKICK_CONFIG_DIR';
|
|
57
|
+
let configDirOverride = null;
|
|
51
58
|
/**
|
|
52
|
-
*
|
|
53
|
-
*
|
|
59
|
+
* The platform default config directory, ignoring both {@link setConfigDir}
|
|
60
|
+
* and `SIDEKICK_CONFIG_DIR`.
|
|
61
|
+
*
|
|
62
|
+
* `~/.config/sidekick` on Unix, `%APPDATA%/sidekick` on Windows.
|
|
54
63
|
*/
|
|
55
|
-
function
|
|
64
|
+
function getDefaultConfigDir() {
|
|
56
65
|
if (process.platform === 'win32') {
|
|
57
66
|
return path.join(process.env.APPDATA || os.homedir(), 'sidekick');
|
|
58
67
|
}
|
|
59
68
|
return path.join(os.homedir(), '.config', 'sidekick');
|
|
60
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* Redirect the Sidekick config root for this process.
|
|
72
|
+
*
|
|
73
|
+
* Takes precedence over `SIDEKICK_CONFIG_DIR`. Pass `null` (or an empty
|
|
74
|
+
* string) to clear it. Every reader and writer in the package resolves the
|
|
75
|
+
* root lazily through {@link getConfigDir}, so this takes effect immediately
|
|
76
|
+
* for all of them — including transitive callers of `getProjectDataPath`,
|
|
77
|
+
* `getGlobalDataPath`, and the account registry.
|
|
78
|
+
*
|
|
79
|
+
* Prefer `SIDEKICK_CONFIG_DIR` when the value is known before the process
|
|
80
|
+
* starts: it is read fresh on every call, so it stays correct even if a
|
|
81
|
+
* bundler ends up with more than one copy of this module.
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* setConfigDir(path.join(os.tmpdir(), 'sidekick-fixture'));
|
|
85
|
+
* try {
|
|
86
|
+
* await addTask(project, { subject: 'isolated' });
|
|
87
|
+
* } finally {
|
|
88
|
+
* setConfigDir(null);
|
|
89
|
+
* }
|
|
90
|
+
*/
|
|
91
|
+
function setConfigDir(dir) {
|
|
92
|
+
// Resolve the trimmed value, not the original: `path.resolve(' /tmp/x ')`
|
|
93
|
+
// treats the leading spaces as a relative segment and lands under cwd. The
|
|
94
|
+
// `SIDEKICK_CONFIG_DIR` branch in getConfigDir() already trims, and the two
|
|
95
|
+
// routes are documented as interchangeable.
|
|
96
|
+
const trimmed = dir?.trim();
|
|
97
|
+
configDirOverride = trimmed ? path.resolve(trimmed) : null;
|
|
98
|
+
}
|
|
99
|
+
/** The active {@link setConfigDir} override, or `null` when unset. */
|
|
100
|
+
function getConfigDirOverride() {
|
|
101
|
+
return configDirOverride;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Gets the Sidekick config directory.
|
|
105
|
+
*
|
|
106
|
+
* Resolution order: {@link setConfigDir} override → `SIDEKICK_CONFIG_DIR`
|
|
107
|
+
* → {@link getDefaultConfigDir}.
|
|
108
|
+
*/
|
|
109
|
+
function getConfigDir() {
|
|
110
|
+
if (configDirOverride)
|
|
111
|
+
return configDirOverride;
|
|
112
|
+
const fromEnv = process.env[exports.CONFIG_DIR_ENV_VAR]?.trim();
|
|
113
|
+
if (fromEnv)
|
|
114
|
+
return path.resolve(fromEnv);
|
|
115
|
+
return getDefaultConfigDir();
|
|
116
|
+
}
|
|
61
117
|
/**
|
|
62
118
|
* Gets the path to a project-specific data file.
|
|
63
119
|
* e.g., ~/.config/sidekick/tasks/my-project.json
|
|
@@ -73,10 +129,14 @@ function getGlobalDataPath(filename) {
|
|
|
73
129
|
return path.join(getConfigDir(), filename);
|
|
74
130
|
}
|
|
75
131
|
/**
|
|
76
|
-
* Encodes a workspace path
|
|
77
|
-
*
|
|
132
|
+
* Encodes a workspace path into Sidekick's own config-store slug — path
|
|
133
|
+
* separators, colons, and underscores become hyphens, dots are kept.
|
|
78
134
|
*
|
|
79
|
-
*
|
|
135
|
+
* This is NOT Claude Code's `~/.claude/projects/` naming scheme, despite the
|
|
136
|
+
* shared name: Claude Code also replaces dots, spaces, and every other
|
|
137
|
+
* non-alphanumeric character. To locate Claude Code session directories use
|
|
138
|
+
* `encodeClaudeWorkspacePath` / `getClaudeSessionDirectory` from
|
|
139
|
+
* `parsers/sessionPathResolver` instead.
|
|
80
140
|
*/
|
|
81
141
|
function encodeWorkspacePath(workspacePath) {
|
|
82
142
|
const normalized = workspacePath.replace(/\\/g, '/');
|
package/dist/peakHours.d.ts
CHANGED
|
@@ -23,6 +23,16 @@ export interface PeakHoursState {
|
|
|
23
23
|
unavailable: boolean;
|
|
24
24
|
notApplicable?: boolean;
|
|
25
25
|
}
|
|
26
|
+
/** Default budget for the promoclock.co request, in milliseconds. */
|
|
27
|
+
export declare const DEFAULT_PEAK_HOURS_TIMEOUT_MS = 10000;
|
|
28
|
+
/** Options for {@link fetchPeakHoursStatus}. */
|
|
29
|
+
export interface FetchPeakHoursOptions {
|
|
30
|
+
/**
|
|
31
|
+
* Abort the request after this many milliseconds.
|
|
32
|
+
* Defaults to {@link DEFAULT_PEAK_HOURS_TIMEOUT_MS}.
|
|
33
|
+
*/
|
|
34
|
+
timeoutMs?: number;
|
|
35
|
+
}
|
|
26
36
|
export declare function isClaudeCodeSessionProvider(providerId: ProviderId): boolean;
|
|
27
37
|
export declare function createPeakHoursNotApplicableState(providerId: ProviderId): PeakHoursState;
|
|
28
38
|
export declare function scopePeakHoursToSessionProvider(providerId: ProviderId, status: PeakHoursState | null | undefined): PeakHoursState | null;
|
|
@@ -30,6 +40,10 @@ export declare function scopePeakHoursToSessionProvider(providerId: ProviderId,
|
|
|
30
40
|
* Fetch current Claude peak-hours state from promoclock.co.
|
|
31
41
|
*
|
|
32
42
|
* Single-shot — caller wraps in polling loop, EventEmitter, or interval.
|
|
33
|
-
* Returns `unavailable: true` on any network or parse error.
|
|
43
|
+
* Returns `unavailable: true` on any network, timeout, or parse error.
|
|
44
|
+
*
|
|
45
|
+
* promoclock.co is a third-party host, so the request is always bounded by
|
|
46
|
+
* an abort timeout. Without one a hung host would stall the caller — and
|
|
47
|
+
* this is polled, so the stall would repeat.
|
|
34
48
|
*/
|
|
35
|
-
export declare function fetchPeakHoursStatus(): Promise<PeakHoursState>;
|
|
49
|
+
export declare function fetchPeakHoursStatus(options?: FetchPeakHoursOptions): Promise<PeakHoursState>;
|
package/dist/peakHours.js
CHANGED
|
@@ -11,11 +11,14 @@
|
|
|
11
11
|
* Polling / eventing is the caller's responsibility.
|
|
12
12
|
*/
|
|
13
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.DEFAULT_PEAK_HOURS_TIMEOUT_MS = void 0;
|
|
14
15
|
exports.isClaudeCodeSessionProvider = isClaudeCodeSessionProvider;
|
|
15
16
|
exports.createPeakHoursNotApplicableState = createPeakHoursNotApplicableState;
|
|
16
17
|
exports.scopePeakHoursToSessionProvider = scopePeakHoursToSessionProvider;
|
|
17
18
|
exports.fetchPeakHoursStatus = fetchPeakHoursStatus;
|
|
18
19
|
const PROMOCLOCK_ENDPOINT = 'https://promoclock.co/api/status';
|
|
20
|
+
/** Default budget for the promoclock.co request, in milliseconds. */
|
|
21
|
+
exports.DEFAULT_PEAK_HOURS_TIMEOUT_MS = 10_000;
|
|
19
22
|
function unavailableState() {
|
|
20
23
|
return {
|
|
21
24
|
status: 'unknown',
|
|
@@ -73,11 +76,19 @@ function normalizeSpeed(raw) {
|
|
|
73
76
|
* Fetch current Claude peak-hours state from promoclock.co.
|
|
74
77
|
*
|
|
75
78
|
* Single-shot — caller wraps in polling loop, EventEmitter, or interval.
|
|
76
|
-
* Returns `unavailable: true` on any network or parse error.
|
|
79
|
+
* Returns `unavailable: true` on any network, timeout, or parse error.
|
|
80
|
+
*
|
|
81
|
+
* promoclock.co is a third-party host, so the request is always bounded by
|
|
82
|
+
* an abort timeout. Without one a hung host would stall the caller — and
|
|
83
|
+
* this is polled, so the stall would repeat.
|
|
77
84
|
*/
|
|
78
|
-
async function fetchPeakHoursStatus() {
|
|
85
|
+
async function fetchPeakHoursStatus(options = {}) {
|
|
86
|
+
const timeoutMs = options.timeoutMs ?? exports.DEFAULT_PEAK_HOURS_TIMEOUT_MS;
|
|
87
|
+
const controller = new AbortController();
|
|
88
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
89
|
+
timeout.unref();
|
|
79
90
|
try {
|
|
80
|
-
const res = await fetch(PROMOCLOCK_ENDPOINT);
|
|
91
|
+
const res = await fetch(PROMOCLOCK_ENDPOINT, { signal: controller.signal });
|
|
81
92
|
if (!res.ok)
|
|
82
93
|
return unavailableState();
|
|
83
94
|
const data = await res.json();
|
|
@@ -98,6 +109,10 @@ async function fetchPeakHoursStatus() {
|
|
|
98
109
|
};
|
|
99
110
|
}
|
|
100
111
|
catch {
|
|
112
|
+
// Covers AbortError from the timeout above alongside network/parse failures.
|
|
101
113
|
return unavailableState();
|
|
102
114
|
}
|
|
115
|
+
finally {
|
|
116
|
+
clearTimeout(timeout);
|
|
117
|
+
}
|
|
103
118
|
}
|
package/dist/pricingCatalog.d.ts
CHANGED
|
@@ -18,8 +18,12 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import { type ModelPricing } from './modelInfo';
|
|
20
20
|
export interface HydrateOptions {
|
|
21
|
-
/**
|
|
22
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Directory for the on-disk cache. Defaults to the Sidekick config dir,
|
|
23
|
+
* which honors `SIDEKICK_CONFIG_DIR` and `setConfigDir()` — pass this only
|
|
24
|
+
* to place the cache somewhere outside the config root.
|
|
25
|
+
*/
|
|
26
|
+
cacheDir?: string;
|
|
23
27
|
/** Inject a fetch implementation (tests, custom agents). Defaults to `globalThis.fetch`. */
|
|
24
28
|
fetchImpl?: typeof fetch;
|
|
25
29
|
/** Cache TTL in ms. Default 24h. */
|
|
@@ -52,7 +56,7 @@ export declare const LITELLM_CATALOG_URL = "https://raw.githubusercontent.com/Be
|
|
|
52
56
|
*
|
|
53
57
|
* Always resolves — never throws. Offline-safe.
|
|
54
58
|
*/
|
|
55
|
-
export declare function hydratePricingCatalog(options
|
|
59
|
+
export declare function hydratePricingCatalog(options?: HydrateOptions): Promise<HydrateResult>;
|
|
56
60
|
/**
|
|
57
61
|
* Convert a LiteLLM catalog payload to our `ModelPricing` override map.
|
|
58
62
|
* Exported for tests; rarely called directly.
|
package/dist/pricingCatalog.js
CHANGED
|
@@ -59,6 +59,7 @@ const node_fs_1 = require("node:fs");
|
|
|
59
59
|
const path = __importStar(require("node:path"));
|
|
60
60
|
const modelContext_1 = require("./modelContext");
|
|
61
61
|
const modelInfo_1 = require("./modelInfo");
|
|
62
|
+
const paths_1 = require("./paths");
|
|
62
63
|
// ── Constants ──
|
|
63
64
|
exports.LITELLM_CATALOG_URL = 'https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json';
|
|
64
65
|
const CACHE_FILE_NAME = 'pricing-catalog.json';
|
|
@@ -75,8 +76,8 @@ const DEFAULT_TIMEOUT_MS = 3_000;
|
|
|
75
76
|
*
|
|
76
77
|
* Always resolves — never throws. Offline-safe.
|
|
77
78
|
*/
|
|
78
|
-
async function hydratePricingCatalog(options) {
|
|
79
|
-
const { cacheDir, fetchImpl = globalThis.fetch, ttlMs = DEFAULT_TTL_MS, timeoutMs = DEFAULT_TIMEOUT_MS, url = exports.LITELLM_CATALOG_URL, logger, } = options;
|
|
79
|
+
async function hydratePricingCatalog(options = {}) {
|
|
80
|
+
const { cacheDir = (0, paths_1.getConfigDir)(), fetchImpl = globalThis.fetch, ttlMs = DEFAULT_TTL_MS, timeoutMs = DEFAULT_TIMEOUT_MS, url = exports.LITELLM_CATALOG_URL, logger, } = options;
|
|
80
81
|
const cachePath = path.join(cacheDir, CACHE_FILE_NAME);
|
|
81
82
|
const now = Date.now();
|
|
82
83
|
// 1. Fresh cache?
|
|
@@ -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, SessionFileStats, SearchHit, ProjectFolderInfo, ProviderId } from './types';
|
|
12
|
+
import type { SessionProviderBase, SessionReader, SessionFileInfo, SessionFileStats, SearchHit, ProjectFolderInfo, ProviderId } from './types';
|
|
13
13
|
/**
|
|
14
14
|
* Session provider for Claude Code CLI.
|
|
15
15
|
*
|
|
@@ -30,6 +30,7 @@ export declare class ClaudeCodeProvider implements SessionProviderBase {
|
|
|
30
30
|
findSessionFiles(workspacePath: string): string[];
|
|
31
31
|
findSessionsInDirectory(dir: string): string[];
|
|
32
32
|
getAllProjectFolders(workspacePath?: string): ProjectFolderInfo[];
|
|
33
|
+
listAllSessionFiles(): SessionFileInfo[];
|
|
33
34
|
isSessionFile(filename: string): boolean;
|
|
34
35
|
getSessionId(sessionPath: string): string;
|
|
35
36
|
encodeWorkspacePath(workspacePath: string): string;
|
|
@@ -260,6 +260,24 @@ class ClaudeCodeProvider {
|
|
|
260
260
|
getAllProjectFolders(workspacePath) {
|
|
261
261
|
return (0, sessionPathResolver_1.getAllProjectFolders)(workspacePath);
|
|
262
262
|
}
|
|
263
|
+
listAllSessionFiles() {
|
|
264
|
+
const results = [];
|
|
265
|
+
const seen = new Set();
|
|
266
|
+
for (const folder of this.getAllProjectFolders()) {
|
|
267
|
+
for (const sessionPath of this.findSessionsInDirectory(folder.dir)) {
|
|
268
|
+
if (seen.has(sessionPath))
|
|
269
|
+
continue;
|
|
270
|
+
seen.add(sessionPath);
|
|
271
|
+
try {
|
|
272
|
+
results.push({ path: sessionPath, mtime: fs.statSync(sessionPath).mtime });
|
|
273
|
+
}
|
|
274
|
+
catch {
|
|
275
|
+
// Skip files that vanish between listing and stat.
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return results;
|
|
280
|
+
}
|
|
263
281
|
// --- File identification ---
|
|
264
282
|
isSessionFile(filename) {
|
|
265
283
|
return filename.endsWith('.jsonl');
|
|
@@ -9,9 +9,29 @@
|
|
|
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, SessionFileStats, ProviderId } from './types';
|
|
12
|
+
import type { SessionProviderBase, SessionReader, ProjectFolderInfo, SearchHit, SessionFileInfo, SessionFileStats, ProviderId } from './types';
|
|
13
13
|
import type { SubagentStats, TokenUsage } from '../types/sessionEvent';
|
|
14
14
|
import type { CodexRateLimits } from '../types/codex';
|
|
15
|
+
export interface FindCodexRolloutFileOptions {
|
|
16
|
+
/**
|
|
17
|
+
* Codex home whose `sessions/` subtree to search. Defaults to every
|
|
18
|
+
* monitored home: `$CODEX_HOME` if set, else `~/.codex`, plus managed
|
|
19
|
+
* profile homes that recorded sessions.
|
|
20
|
+
*/
|
|
21
|
+
codexHome?: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Locates the rollout file for a Codex session id by walking
|
|
25
|
+
* `<home>/sessions/YYYY/MM/DD/rollout-<timestamp>-<id>.jsonl`.
|
|
26
|
+
*
|
|
27
|
+
* Filename-only match, case-insensitive on the id — no file contents are read
|
|
28
|
+
* and files are only stat'ed once their name matches, so the walk stays cheap
|
|
29
|
+
* even over a large history. When the same id exists in more than one home,
|
|
30
|
+
* the most recently modified copy wins. Returns `null` for an unknown, empty,
|
|
31
|
+
* or whitespace id. Consumers who want the database's answer instead can query
|
|
32
|
+
* the already-public `CodexDatabase` (requires the external `sqlite3` binary).
|
|
33
|
+
*/
|
|
34
|
+
export declare function findCodexRolloutFile(sessionId: string, options?: FindCodexRolloutFileOptions): string | null;
|
|
15
35
|
/**
|
|
16
36
|
* Session provider for Codex CLI.
|
|
17
37
|
*
|
|
@@ -35,6 +55,7 @@ export declare class CodexProvider implements SessionProviderBase {
|
|
|
35
55
|
/** Backward-compatible alias for findAllSessions. */
|
|
36
56
|
findSessionFiles(workspacePath: string): string[];
|
|
37
57
|
findSessionsInDirectory(dir: string): string[];
|
|
58
|
+
listAllSessionFiles(): SessionFileInfo[];
|
|
38
59
|
getAllProjectFolders(workspacePath?: string): ProjectFolderInfo[];
|
|
39
60
|
isSessionFile(filename: string): boolean;
|
|
40
61
|
getSessionId(sessionPath: string): string;
|
package/dist/providers/codex.js
CHANGED
|
@@ -44,6 +44,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
44
44
|
})();
|
|
45
45
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
46
|
exports.CodexProvider = void 0;
|
|
47
|
+
exports.findCodexRolloutFile = findCodexRolloutFile;
|
|
47
48
|
const fs = __importStar(require("fs"));
|
|
48
49
|
const path = __importStar(require("path"));
|
|
49
50
|
const string_decoder_1 = require("string_decoder");
|
|
@@ -164,6 +165,60 @@ function findRolloutFilesInConfiguredHomes() {
|
|
|
164
165
|
}
|
|
165
166
|
return results;
|
|
166
167
|
}
|
|
168
|
+
/**
|
|
169
|
+
* Locates the rollout file for a Codex session id by walking
|
|
170
|
+
* `<home>/sessions/YYYY/MM/DD/rollout-<timestamp>-<id>.jsonl`.
|
|
171
|
+
*
|
|
172
|
+
* Filename-only match, case-insensitive on the id — no file contents are read
|
|
173
|
+
* and files are only stat'ed once their name matches, so the walk stays cheap
|
|
174
|
+
* even over a large history. When the same id exists in more than one home,
|
|
175
|
+
* the most recently modified copy wins. Returns `null` for an unknown, empty,
|
|
176
|
+
* or whitespace id. Consumers who want the database's answer instead can query
|
|
177
|
+
* the already-public `CodexDatabase` (requires the external `sqlite3` binary).
|
|
178
|
+
*/
|
|
179
|
+
function findCodexRolloutFile(sessionId, options = {}) {
|
|
180
|
+
const normalizedId = sessionId.trim().toLowerCase();
|
|
181
|
+
if (!normalizedId)
|
|
182
|
+
return null;
|
|
183
|
+
const sessionsDirs = options.codexHome
|
|
184
|
+
? [path.join(options.codexHome, 'sessions')]
|
|
185
|
+
: getSessionsDirs();
|
|
186
|
+
let best = null;
|
|
187
|
+
for (const dir of sessionsDirs) {
|
|
188
|
+
for (const candidate of findRolloutFilesById(dir, normalizedId)) {
|
|
189
|
+
if (!best || candidate.mtimeMs > best.mtimeMs)
|
|
190
|
+
best = candidate;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return best?.path ?? null;
|
|
194
|
+
}
|
|
195
|
+
function findRolloutFilesById(dir, normalizedId) {
|
|
196
|
+
const results = [];
|
|
197
|
+
try {
|
|
198
|
+
if (!fs.existsSync(dir))
|
|
199
|
+
return results;
|
|
200
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
201
|
+
const fullPath = path.join(dir, entry.name);
|
|
202
|
+
if (entry.isDirectory()) {
|
|
203
|
+
results.push(...findRolloutFilesById(fullPath, normalizedId));
|
|
204
|
+
}
|
|
205
|
+
else if (entry.isFile() &&
|
|
206
|
+
isRolloutFile(entry.name) &&
|
|
207
|
+
extractSessionId(entry.name).toLowerCase() === normalizedId) {
|
|
208
|
+
try {
|
|
209
|
+
results.push({ path: fullPath, mtimeMs: fs.statSync(fullPath).mtimeMs });
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
// Skip files that vanish between readdir and stat.
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
catch {
|
|
218
|
+
// Skip inaccessible directories.
|
|
219
|
+
}
|
|
220
|
+
return results;
|
|
221
|
+
}
|
|
167
222
|
/** Pick the best sessions directory for UI/search entrypoints. */
|
|
168
223
|
function getSessionsDir() {
|
|
169
224
|
const dirs = getSessionsDirs();
|
|
@@ -711,6 +766,9 @@ class CodexProvider {
|
|
|
711
766
|
files.sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
|
|
712
767
|
return files.map((f) => f.path);
|
|
713
768
|
}
|
|
769
|
+
listAllSessionFiles() {
|
|
770
|
+
return findRolloutFilesInConfiguredHomes();
|
|
771
|
+
}
|
|
714
772
|
getAllProjectFolders(workspacePath) {
|
|
715
773
|
const folders = [];
|
|
716
774
|
const seenCwds = new Map();
|
|
@@ -143,6 +143,11 @@ export interface SessionProviderBase {
|
|
|
143
143
|
readSessionContextSnapshot?(sessionPath: string, options?: ReadSessionContextSnapshotOptions): SessionContextSnapshot;
|
|
144
144
|
/** Reports provider runtime readiness for DB-backed providers. */
|
|
145
145
|
getRuntimeStatus?(): ProviderRuntimeStatus;
|
|
146
|
+
/**
|
|
147
|
+
* Lists every session file this provider knows about, across all workspaces.
|
|
148
|
+
* Stat-only — implementations must not read file contents.
|
|
149
|
+
*/
|
|
150
|
+
listAllSessionFiles?(): SessionFileInfo[];
|
|
146
151
|
/** Tests whether a provider can monitor a directory path, including synthetic DB-backed paths. */
|
|
147
152
|
canMonitorDirectory?(dir: string): boolean;
|
|
148
153
|
/** Release resources. */
|
package/dist/quotaHistory.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Sibling to `quotaSnapshots.ts`. Where snapshots persist a single most-recent sample per
|
|
5
5
|
* (provider, account), this module accumulates time-series samples so consumers (the VS Code
|
|
6
|
-
* dashboard, the `sidekick quota history` CLI,
|
|
6
|
+
* dashboard, the `sidekick quota history` CLI, external consumers) can render heatmaps and
|
|
7
7
|
* trend visualisations over a 13-week window.
|
|
8
8
|
*/
|
|
9
9
|
export type QuotaHistoryRuntimeProvider = 'claude' | 'codex' | 'zai';
|
package/dist/quotaHistory.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Sibling to `quotaSnapshots.ts`. Where snapshots persist a single most-recent sample per
|
|
6
6
|
* (provider, account), this module accumulates time-series samples so consumers (the VS Code
|
|
7
|
-
* dashboard, the `sidekick quota history` CLI,
|
|
7
|
+
* dashboard, the `sidekick quota history` CLI, external consumers) can render heatmaps and
|
|
8
8
|
* trend visualisations over a 13-week window.
|
|
9
9
|
*/
|
|
10
10
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
@@ -258,7 +258,7 @@ async function runAppend(sample, filePath, options) {
|
|
|
258
258
|
// ignore
|
|
259
259
|
}
|
|
260
260
|
// Backwards-compat: keep the latest-snapshot store hot so existing callers (DashboardViewProvider,
|
|
261
|
-
// codex session provider,
|
|
261
|
+
// codex session provider, external consumers) don't have to query history for "latest".
|
|
262
262
|
try {
|
|
263
263
|
const providerId = runtimeProviderToSnapshotProvider(sample.runtimeProvider);
|
|
264
264
|
(0, quotaSnapshots_1.writeQuotaSnapshot)(providerId, sample.providerId, sampleToQuotaState(sample));
|