codexmate 0.1.4 → 0.1.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/README.md +2 -2
- package/README.vi.md +2 -2
- package/README.zh.md +2 -2
- package/cli/analytics-export-args.js +2 -2
- package/cli/session-usage.js +5 -2
- package/cli.js +424 -54
- package/lib/cli-sessions.js +19 -0
- package/package.json +1 -1
- package/web-ui/logic.sessions.mjs +5 -5
- package/web-ui/modules/app.computed.session.mjs +2 -1
- package/web-ui/modules/app.methods.session-actions.mjs +13 -5
- package/web-ui/modules/app.methods.session-browser.mjs +8 -4
- package/web-ui/modules/app.methods.session-trash.mjs +2 -2
- package/web-ui/modules/i18n/locales/en.mjs +1 -0
- package/web-ui/modules/i18n/locales/ja.mjs +1 -0
- package/web-ui/modules/i18n/locales/vi.mjs +1 -0
- package/web-ui/modules/i18n/locales/zh-tw.mjs +1 -0
- package/web-ui/modules/i18n/locales/zh.mjs +1 -0
- package/web-ui/partials/index/panel-sessions.html +8 -12
- package/web-ui/res/web-ui-render.precompiled.js +12 -29
- package/web-ui/styles/responsive.css +12 -0
- package/web-ui/styles/sessions-toolbar-trash.css +23 -1
package/cli.js
CHANGED
|
@@ -260,6 +260,8 @@ const CODEXMATE_DERIVED_CODEX_DIR = path.join(CODEXMATE_DERIVED_SESSIONS_DIR, 'c
|
|
|
260
260
|
const CODEXMATE_DERIVED_CLAUDE_DIR = path.join(CODEXMATE_DERIVED_SESSIONS_DIR, 'claude');
|
|
261
261
|
const GEMINI_DIR = path.join(os.homedir(), '.gemini');
|
|
262
262
|
const GEMINI_TMP_DIR = path.join(GEMINI_DIR, 'tmp');
|
|
263
|
+
const PI_DIR = path.join(os.homedir(), '.pi');
|
|
264
|
+
const PI_SESSIONS_DIR = path.join(PI_DIR, 'agent', 'sessions');
|
|
263
265
|
const RECENT_CONFIGS_FILE = path.join(CONFIG_DIR, 'recent-configs.json');
|
|
264
266
|
const WORKFLOW_DEFINITIONS_FILE = path.join(CONFIG_DIR, 'codexmate-workflows.json');
|
|
265
267
|
const WORKFLOW_RUNS_FILE = path.join(CONFIG_DIR, 'codexmate-workflow-runs.jsonl');
|
|
@@ -770,7 +772,8 @@ let g_sessionFileLookupCache = {
|
|
|
770
772
|
codex: new Map(),
|
|
771
773
|
claude: new Map(),
|
|
772
774
|
gemini: new Map(),
|
|
773
|
-
codebuddy: new Map()
|
|
775
|
+
codebuddy: new Map(),
|
|
776
|
+
pi: new Map()
|
|
774
777
|
};
|
|
775
778
|
let g_exactMessageCountCache = new Map();
|
|
776
779
|
let g_modelsCache = new Map();
|
|
@@ -1017,7 +1020,7 @@ function normalizeSidebarCollapsedPreference(value) {
|
|
|
1017
1020
|
|
|
1018
1021
|
function normalizeSessionFilterSourcePreference(value) {
|
|
1019
1022
|
const normalized = typeof value === 'string' ? value.trim().toLowerCase() : '';
|
|
1020
|
-
if (normalized === 'all' || normalized === 'claude' || normalized === 'gemini' || normalized === 'codebuddy') return normalized;
|
|
1023
|
+
if (normalized === 'all' || normalized === 'claude' || normalized === 'gemini' || normalized === 'codebuddy' || normalized === 'pi') return normalized;
|
|
1021
1024
|
return 'codex';
|
|
1022
1025
|
}
|
|
1023
1026
|
|
|
@@ -1823,6 +1826,20 @@ function getCodeBuddyProjectsDir() {
|
|
|
1823
1826
|
return resolveExistingDir(candidates, CODEBUDDY_PROJECTS_DIR);
|
|
1824
1827
|
}
|
|
1825
1828
|
|
|
1829
|
+
function getPiSessionsDir() {
|
|
1830
|
+
const candidates = [];
|
|
1831
|
+
const envPiHome = process.env.PI_HOME;
|
|
1832
|
+
if (envPiHome) {
|
|
1833
|
+
candidates.push(path.join(envPiHome, 'agent', 'sessions'));
|
|
1834
|
+
}
|
|
1835
|
+
const xdgConfig = process.env.XDG_CONFIG_HOME;
|
|
1836
|
+
if (xdgConfig) {
|
|
1837
|
+
candidates.push(path.join(xdgConfig, 'pi', 'agent', 'sessions'));
|
|
1838
|
+
}
|
|
1839
|
+
candidates.push(PI_SESSIONS_DIR);
|
|
1840
|
+
return resolveExistingDir(candidates, PI_SESSIONS_DIR);
|
|
1841
|
+
}
|
|
1842
|
+
|
|
1826
1843
|
function getCodexmateDerivedSessionsRoot(target) {
|
|
1827
1844
|
if (target === 'claude') {
|
|
1828
1845
|
return CODEXMATE_DERIVED_CLAUDE_DIR;
|
|
@@ -3842,6 +3859,15 @@ async function countConversationMessagesInFile(filePath, source) {
|
|
|
3842
3859
|
role = '';
|
|
3843
3860
|
}
|
|
3844
3861
|
}
|
|
3862
|
+
} else if (source === 'pi') {
|
|
3863
|
+
if (record && record.type === 'message' && record.message && typeof record.message === 'object') {
|
|
3864
|
+
role = normalizeRole(record.message.role);
|
|
3865
|
+
if (role === 'assistant' || role === 'user' || role === 'system') {
|
|
3866
|
+
text = extractMessageText(record.message.content);
|
|
3867
|
+
} else {
|
|
3868
|
+
role = '';
|
|
3869
|
+
}
|
|
3870
|
+
}
|
|
3845
3871
|
} else {
|
|
3846
3872
|
role = normalizeRole(record.type);
|
|
3847
3873
|
if (role === 'assistant' || role === 'user' || role === 'system') {
|
|
@@ -4010,7 +4036,9 @@ async function hydrateSessionTrashEntries(entries, options = {}) {
|
|
|
4010
4036
|
? 'codex'
|
|
4011
4037
|
: (options.source === 'gemini'
|
|
4012
4038
|
? 'gemini'
|
|
4013
|
-
: (options.source === 'codebuddy'
|
|
4039
|
+
: (options.source === 'codebuddy'
|
|
4040
|
+
? 'codebuddy'
|
|
4041
|
+
: (options.source === 'pi' ? 'pi' : 'all'))));
|
|
4014
4042
|
const hydratedEntries = await mapWithConcurrency(Array.isArray(entries) ? entries : [], 8, async (entry) => {
|
|
4015
4043
|
const normalizedEntry = normalizeSessionTrashEntry(entry);
|
|
4016
4044
|
if (!normalizedEntry) {
|
|
@@ -4019,7 +4047,7 @@ async function hydrateSessionTrashEntries(entries, options = {}) {
|
|
|
4019
4047
|
return await resolveSessionTrashEntryExactMessageCount(normalizedEntry);
|
|
4020
4048
|
});
|
|
4021
4049
|
|
|
4022
|
-
if (source === 'codex' || source === 'claude' || source === 'gemini' || source === 'codebuddy') {
|
|
4050
|
+
if (source === 'codex' || source === 'claude' || source === 'gemini' || source === 'codebuddy' || source === 'pi') {
|
|
4023
4051
|
return hydratedEntries.filter((entry) => entry.source === source);
|
|
4024
4052
|
}
|
|
4025
4053
|
return hydratedEntries;
|
|
@@ -4037,7 +4065,11 @@ async function hydrateSessionItemsExactMessageCount(items) {
|
|
|
4037
4065
|
? 'claude'
|
|
4038
4066
|
: (item.source === 'codex'
|
|
4039
4067
|
? 'codex'
|
|
4040
|
-
: (item.source === 'gemini'
|
|
4068
|
+
: (item.source === 'gemini'
|
|
4069
|
+
? 'gemini'
|
|
4070
|
+
: (item.source === 'codebuddy'
|
|
4071
|
+
? 'codebuddy'
|
|
4072
|
+
: (item.source === 'pi' ? 'pi' : ''))));
|
|
4041
4073
|
const filePath = typeof item.filePath === 'string' ? item.filePath : '';
|
|
4042
4074
|
if (!source || !filePath || !fs.existsSync(filePath)) {
|
|
4043
4075
|
return item;
|
|
@@ -4085,7 +4117,11 @@ async function readSessionMessageCounts(params = {}) {
|
|
|
4085
4117
|
? 'claude'
|
|
4086
4118
|
: (item.source === 'codex'
|
|
4087
4119
|
? 'codex'
|
|
4088
|
-
: (item.source === 'gemini'
|
|
4120
|
+
: (item.source === 'gemini'
|
|
4121
|
+
? 'gemini'
|
|
4122
|
+
: (item.source === 'codebuddy'
|
|
4123
|
+
? 'codebuddy'
|
|
4124
|
+
: (item.source === 'pi' ? 'pi' : ''))));
|
|
4089
4125
|
const filePath = typeof item.filePath === 'string' ? item.filePath : '';
|
|
4090
4126
|
if (!source || !filePath || !fs.existsSync(filePath)) {
|
|
4091
4127
|
return { key };
|
|
@@ -4599,7 +4635,13 @@ function setSessionListCache(cacheKey, value) {
|
|
|
4599
4635
|
}
|
|
4600
4636
|
|
|
4601
4637
|
function buildSessionInventoryCacheKey(source, limit, options = {}) {
|
|
4602
|
-
const normalizedSource = source === 'claude'
|
|
4638
|
+
const normalizedSource = source === 'claude'
|
|
4639
|
+
? 'claude'
|
|
4640
|
+
: (source === 'gemini'
|
|
4641
|
+
? 'gemini'
|
|
4642
|
+
: (source === 'codebuddy'
|
|
4643
|
+
? 'codebuddy'
|
|
4644
|
+
: (source === 'pi' ? 'pi' : 'codex')));
|
|
4603
4645
|
const normalizedLimit = Number.isFinite(Number(limit))
|
|
4604
4646
|
? Math.max(1, Math.floor(Number(limit)))
|
|
4605
4647
|
: 1;
|
|
@@ -4685,7 +4727,7 @@ function getSessionInventoryCache(cacheKey, forceRefresh = false) {
|
|
|
4685
4727
|
}
|
|
4686
4728
|
|
|
4687
4729
|
function registerSessionFileLookupEntries(source, sessions = []) {
|
|
4688
|
-
const normalizedSource = source === 'claude' || source === 'gemini' || source === 'codebuddy'
|
|
4730
|
+
const normalizedSource = source === 'claude' || source === 'gemini' || source === 'codebuddy' || source === 'pi'
|
|
4689
4731
|
? source
|
|
4690
4732
|
: 'codex';
|
|
4691
4733
|
const store = g_sessionFileLookupCache[normalizedSource];
|
|
@@ -4726,7 +4768,7 @@ function setSessionInventoryCache(cacheKey, source, value) {
|
|
|
4726
4768
|
}
|
|
4727
4769
|
|
|
4728
4770
|
function listSessionInventoryBySource(source, limit, scanOptions = {}, options = {}) {
|
|
4729
|
-
const normalizedSource = source === 'claude' || source === 'gemini' || source === 'codebuddy'
|
|
4771
|
+
const normalizedSource = source === 'claude' || source === 'gemini' || source === 'codebuddy' || source === 'pi'
|
|
4730
4772
|
? source
|
|
4731
4773
|
: 'codex';
|
|
4732
4774
|
const forceRefresh = !!options.forceRefresh;
|
|
@@ -4742,7 +4784,9 @@ function listSessionInventoryBySource(source, limit, scanOptions = {}, options =
|
|
|
4742
4784
|
? listGeminiSessions(limit, scanOptions)
|
|
4743
4785
|
: (normalizedSource === 'codebuddy'
|
|
4744
4786
|
? listCodeBuddySessions(limit, scanOptions)
|
|
4745
|
-
:
|
|
4787
|
+
: (normalizedSource === 'pi'
|
|
4788
|
+
? listPiSessions(limit, scanOptions)
|
|
4789
|
+
: listCodexSessions(limit, scanOptions))));
|
|
4746
4790
|
setSessionInventoryCache(cacheKey, normalizedSource, sessions);
|
|
4747
4791
|
return sessions;
|
|
4748
4792
|
}
|
|
@@ -4754,7 +4798,8 @@ function invalidateSessionListCache() {
|
|
|
4754
4798
|
codex: new Map(),
|
|
4755
4799
|
claude: new Map(),
|
|
4756
4800
|
gemini: new Map(),
|
|
4757
|
-
codebuddy: new Map()
|
|
4801
|
+
codebuddy: new Map(),
|
|
4802
|
+
pi: new Map()
|
|
4758
4803
|
};
|
|
4759
4804
|
}
|
|
4760
4805
|
|
|
@@ -4988,7 +5033,13 @@ function readSessionProviderFromRecord(record, source = '') {
|
|
|
4988
5033
|
if (provider) {
|
|
4989
5034
|
return provider;
|
|
4990
5035
|
}
|
|
4991
|
-
return source === 'claude'
|
|
5036
|
+
return source === 'claude'
|
|
5037
|
+
? 'claude'
|
|
5038
|
+
: (source === 'gemini'
|
|
5039
|
+
? 'gemini'
|
|
5040
|
+
: (source === 'codebuddy'
|
|
5041
|
+
? 'codebuddy'
|
|
5042
|
+
: (source === 'pi' ? 'pi' : 'codex')));
|
|
4992
5043
|
}
|
|
4993
5044
|
|
|
4994
5045
|
function applySessionUsageSummaryFromRecord(state, record, source) {
|
|
@@ -5687,6 +5738,177 @@ function parseGeminiSessionSummary(filePath, options = {}) {
|
|
|
5687
5738
|
};
|
|
5688
5739
|
}
|
|
5689
5740
|
|
|
5741
|
+
function extractPiUsageFromMessage(message) {
|
|
5742
|
+
if (!message || typeof message !== 'object' || Array.isArray(message)) {
|
|
5743
|
+
return null;
|
|
5744
|
+
}
|
|
5745
|
+
const usage = message.usage;
|
|
5746
|
+
if (!usage || typeof usage !== 'object' || Array.isArray(usage)) {
|
|
5747
|
+
return null;
|
|
5748
|
+
}
|
|
5749
|
+
const inputTokens = readNonNegativeInteger(usage.input);
|
|
5750
|
+
const outputTokens = readNonNegativeInteger(usage.output);
|
|
5751
|
+
const cachedInputTokens = readNonNegativeInteger(usage.cacheRead);
|
|
5752
|
+
const cacheCreationInputTokens = readNonNegativeInteger(usage.cacheWrite);
|
|
5753
|
+
const reasoningOutputTokens = readNonNegativeInteger(usage.reasoning);
|
|
5754
|
+
if (inputTokens === null && outputTokens === null && cachedInputTokens === null && cacheCreationInputTokens === null && reasoningOutputTokens === null) {
|
|
5755
|
+
return null;
|
|
5756
|
+
}
|
|
5757
|
+
return {
|
|
5758
|
+
inputTokens: inputTokens || 0,
|
|
5759
|
+
cachedInputTokens: cachedInputTokens || 0,
|
|
5760
|
+
cacheCreationInputTokens: cacheCreationInputTokens || 0,
|
|
5761
|
+
outputTokens: outputTokens || 0,
|
|
5762
|
+
reasoningOutputTokens: reasoningOutputTokens || 0
|
|
5763
|
+
};
|
|
5764
|
+
}
|
|
5765
|
+
|
|
5766
|
+
function parsePiSessionSummary(filePath, options = {}) {
|
|
5767
|
+
const summaryReadBytes = Number.isFinite(Number(options.summaryReadBytes))
|
|
5768
|
+
? Math.max(1024, Math.floor(Number(options.summaryReadBytes)))
|
|
5769
|
+
: SESSION_SUMMARY_READ_BYTES;
|
|
5770
|
+
const titleReadBytes = Number.isFinite(Number(options.titleReadBytes))
|
|
5771
|
+
? Math.max(1024, Math.floor(Number(options.titleReadBytes)))
|
|
5772
|
+
: SESSION_TITLE_READ_BYTES;
|
|
5773
|
+
const records = parseJsonlHeadRecords(filePath, summaryReadBytes);
|
|
5774
|
+
if (records.length === 0) {
|
|
5775
|
+
return null;
|
|
5776
|
+
}
|
|
5777
|
+
|
|
5778
|
+
let stat;
|
|
5779
|
+
try {
|
|
5780
|
+
stat = fs.statSync(filePath);
|
|
5781
|
+
} catch (_) {
|
|
5782
|
+
return null;
|
|
5783
|
+
}
|
|
5784
|
+
|
|
5785
|
+
let sessionId = path.basename(filePath, '.jsonl');
|
|
5786
|
+
let cwd = '';
|
|
5787
|
+
let createdAt = '';
|
|
5788
|
+
let updatedAt = stat.mtime.toISOString();
|
|
5789
|
+
let firstPrompt = '';
|
|
5790
|
+
let messageCount = 0;
|
|
5791
|
+
let totalTokens = 0;
|
|
5792
|
+
let contextWindow = 0;
|
|
5793
|
+
let inputTokens = 0;
|
|
5794
|
+
let cachedInputTokens = 0;
|
|
5795
|
+
let cacheCreationInputTokens = 0;
|
|
5796
|
+
let outputTokens = 0;
|
|
5797
|
+
let reasoningOutputTokens = 0;
|
|
5798
|
+
let provider = 'pi';
|
|
5799
|
+
let model = '';
|
|
5800
|
+
const models = [];
|
|
5801
|
+
const previewMessages = [];
|
|
5802
|
+
|
|
5803
|
+
for (const record of records) {
|
|
5804
|
+
if (!createdAt && record && record.type === 'session' && record.timestamp) {
|
|
5805
|
+
createdAt = toIsoTime(record.timestamp, createdAt);
|
|
5806
|
+
}
|
|
5807
|
+
if (record && record.timestamp) {
|
|
5808
|
+
updatedAt = updateLatestIso(updatedAt, record.timestamp);
|
|
5809
|
+
}
|
|
5810
|
+
if (record && record.type === 'session') {
|
|
5811
|
+
sessionId = record.id || sessionId;
|
|
5812
|
+
cwd = record.cwd || cwd;
|
|
5813
|
+
}
|
|
5814
|
+
if (record && record.type === 'model_change' && record.modelId) {
|
|
5815
|
+
if (!models.includes(record.modelId)) {
|
|
5816
|
+
models.push(record.modelId);
|
|
5817
|
+
}
|
|
5818
|
+
model = model || record.modelId;
|
|
5819
|
+
if (record.provider) {
|
|
5820
|
+
provider = record.provider;
|
|
5821
|
+
}
|
|
5822
|
+
}
|
|
5823
|
+
if (record && record.type === 'message' && record.message && typeof record.message === 'object') {
|
|
5824
|
+
const role = normalizeRole(record.message.role);
|
|
5825
|
+
if (role === 'user' || role === 'assistant' || role === 'system') {
|
|
5826
|
+
messageCount += 1;
|
|
5827
|
+
const text = extractMessageText(record.message.content);
|
|
5828
|
+
if (role === 'assistant' && record.message.model && !models.includes(record.message.model)) {
|
|
5829
|
+
models.push(record.message.model);
|
|
5830
|
+
model = model || record.message.model;
|
|
5831
|
+
}
|
|
5832
|
+
if (record.message.provider) {
|
|
5833
|
+
provider = record.message.provider;
|
|
5834
|
+
}
|
|
5835
|
+
const piUsage = extractPiUsageFromMessage(record.message);
|
|
5836
|
+
if (piUsage) {
|
|
5837
|
+
inputTokens += piUsage.inputTokens;
|
|
5838
|
+
cachedInputTokens += piUsage.cachedInputTokens;
|
|
5839
|
+
cacheCreationInputTokens += piUsage.cacheCreationInputTokens;
|
|
5840
|
+
outputTokens += piUsage.outputTokens;
|
|
5841
|
+
reasoningOutputTokens += piUsage.reasoningOutputTokens;
|
|
5842
|
+
}
|
|
5843
|
+
if (text) {
|
|
5844
|
+
previewMessages.push({ role, text });
|
|
5845
|
+
if (!firstPrompt && role === 'user') {
|
|
5846
|
+
firstPrompt = text.split('\n')[0].slice(0, 80);
|
|
5847
|
+
}
|
|
5848
|
+
}
|
|
5849
|
+
}
|
|
5850
|
+
}
|
|
5851
|
+
}
|
|
5852
|
+
|
|
5853
|
+
totalTokens = inputTokens + cachedInputTokens + cacheCreationInputTokens + outputTokens + reasoningOutputTokens;
|
|
5854
|
+
|
|
5855
|
+
const tailRecords = parseJsonlTailRecords(filePath, summaryReadBytes);
|
|
5856
|
+
let tailMessageCount = 0;
|
|
5857
|
+
let tailHasMore = false;
|
|
5858
|
+
for (const record of tailRecords) {
|
|
5859
|
+
if (record && record.timestamp) {
|
|
5860
|
+
updatedAt = updateLatestIso(updatedAt, record.timestamp);
|
|
5861
|
+
}
|
|
5862
|
+
if (record && record.type === 'message' && record.message && typeof record.message === 'object') {
|
|
5863
|
+
const role = normalizeRole(record.message.role);
|
|
5864
|
+
if (role === 'user' || role === 'assistant' || role === 'system') {
|
|
5865
|
+
tailMessageCount += 1;
|
|
5866
|
+
const piUsage = extractPiUsageFromMessage(record.message);
|
|
5867
|
+
if (piUsage) {
|
|
5868
|
+
inputTokens += piUsage.inputTokens;
|
|
5869
|
+
cachedInputTokens += piUsage.cachedInputTokens;
|
|
5870
|
+
cacheCreationInputTokens += piUsage.cacheCreationInputTokens;
|
|
5871
|
+
outputTokens += piUsage.outputTokens;
|
|
5872
|
+
reasoningOutputTokens += piUsage.reasoningOutputTokens;
|
|
5873
|
+
}
|
|
5874
|
+
if (role === 'assistant' && record.message.model && !models.includes(record.message.model)) {
|
|
5875
|
+
models.push(record.message.model);
|
|
5876
|
+
}
|
|
5877
|
+
if (record.message.provider) {
|
|
5878
|
+
provider = record.message.provider;
|
|
5879
|
+
}
|
|
5880
|
+
}
|
|
5881
|
+
}
|
|
5882
|
+
}
|
|
5883
|
+
totalTokens = inputTokens + cachedInputTokens + cacheCreationInputTokens + outputTokens + reasoningOutputTokens;
|
|
5884
|
+
|
|
5885
|
+
const fileName = path.basename(filePath, '.jsonl');
|
|
5886
|
+
return {
|
|
5887
|
+
source: 'pi',
|
|
5888
|
+
sourceLabel: 'Pi',
|
|
5889
|
+
provider,
|
|
5890
|
+
model,
|
|
5891
|
+
models,
|
|
5892
|
+
sessionId,
|
|
5893
|
+
title: firstPrompt || sessionId || fileName,
|
|
5894
|
+
cwd,
|
|
5895
|
+
createdAt,
|
|
5896
|
+
updatedAt,
|
|
5897
|
+
messageCount,
|
|
5898
|
+
totalTokens,
|
|
5899
|
+
contextWindow,
|
|
5900
|
+
inputTokens,
|
|
5901
|
+
cachedInputTokens,
|
|
5902
|
+
cacheCreationInputTokens,
|
|
5903
|
+
outputTokens,
|
|
5904
|
+
reasoningOutputTokens,
|
|
5905
|
+
__messageCountExact: false,
|
|
5906
|
+
filePath,
|
|
5907
|
+
keywords: [],
|
|
5908
|
+
capabilities: { code: true }
|
|
5909
|
+
};
|
|
5910
|
+
}
|
|
5911
|
+
|
|
5690
5912
|
function listCodexSessions(limit, options = {}) {
|
|
5691
5913
|
const codexSessionsDir = getCodexSessionsDir();
|
|
5692
5914
|
const scanFactor = Number.isFinite(Number(options.scanFactor))
|
|
@@ -6126,8 +6348,56 @@ function listCodeBuddySessions(limit, options = {}) {
|
|
|
6126
6348
|
return mergeAndLimitSessions(sessions, limit);
|
|
6127
6349
|
}
|
|
6128
6350
|
|
|
6351
|
+
function listPiSessions(limit, options = {}) {
|
|
6352
|
+
const sessionsDir = getPiSessionsDir();
|
|
6353
|
+
if (!fs.existsSync(sessionsDir)) {
|
|
6354
|
+
return [];
|
|
6355
|
+
}
|
|
6356
|
+
|
|
6357
|
+
const scanFactor = Number.isFinite(Number(options.scanFactor))
|
|
6358
|
+
? Math.max(1, Number(options.scanFactor))
|
|
6359
|
+
: SESSION_SCAN_FACTOR;
|
|
6360
|
+
const minFiles = Number.isFinite(Number(options.minFiles))
|
|
6361
|
+
? Math.max(1, Number(options.minFiles))
|
|
6362
|
+
: Math.min(SESSION_SCAN_MIN_FILES, MAX_SESSION_LIST_SIZE * SESSION_SCAN_FACTOR);
|
|
6363
|
+
const targetCount = Number.isFinite(Number(options.targetCount))
|
|
6364
|
+
? Math.max(1, Math.floor(Number(options.targetCount)))
|
|
6365
|
+
: Math.max(1, Math.floor(limit * scanFactor));
|
|
6366
|
+
const scanCount = Number.isFinite(Number(options.scanCount))
|
|
6367
|
+
? Math.max(targetCount, Math.floor(Number(options.scanCount)))
|
|
6368
|
+
: Math.max(targetCount, minFiles);
|
|
6369
|
+
const maxFilesScanned = Number.isFinite(Number(options.maxFilesScanned))
|
|
6370
|
+
? Math.max(scanCount, Math.floor(Number(options.maxFilesScanned)))
|
|
6371
|
+
: Math.max(scanCount * 2, minFiles);
|
|
6372
|
+
const summaryReadBytes = Number.isFinite(Number(options.summaryReadBytes))
|
|
6373
|
+
? Math.max(1024, Math.floor(Number(options.summaryReadBytes)))
|
|
6374
|
+
: SESSION_SUMMARY_READ_BYTES;
|
|
6375
|
+
const titleReadBytes = Number.isFinite(Number(options.titleReadBytes))
|
|
6376
|
+
? Math.max(1024, Math.floor(Number(options.titleReadBytes)))
|
|
6377
|
+
: SESSION_TITLE_READ_BYTES;
|
|
6378
|
+
|
|
6379
|
+
const files = collectRecentJsonlFiles(sessionsDir, {
|
|
6380
|
+
returnCount: scanCount,
|
|
6381
|
+
maxFilesScanned
|
|
6382
|
+
});
|
|
6383
|
+
const sessions = [];
|
|
6384
|
+
for (const filePath of files) {
|
|
6385
|
+
const summary = parsePiSessionSummary(filePath, {
|
|
6386
|
+
summaryReadBytes,
|
|
6387
|
+
titleReadBytes
|
|
6388
|
+
});
|
|
6389
|
+
if (summary) {
|
|
6390
|
+
sessions.push(summary);
|
|
6391
|
+
}
|
|
6392
|
+
if (sessions.length >= targetCount) {
|
|
6393
|
+
break;
|
|
6394
|
+
}
|
|
6395
|
+
}
|
|
6396
|
+
return mergeAndLimitSessions(sessions, limit);
|
|
6397
|
+
}
|
|
6398
|
+
|
|
6129
6399
|
async function listAllSessions(params = {}) {
|
|
6130
|
-
const source = params.source === 'codex' || params.source === 'claude' || params.source === 'gemini' || params.source === 'codebuddy'
|
|
6400
|
+
const source = params.source === 'codex' || params.source === 'claude' || params.source === 'gemini' || params.source === 'codebuddy' || params.source === 'pi'
|
|
6131
6401
|
? params.source
|
|
6132
6402
|
: 'all';
|
|
6133
6403
|
const rawLimit = Number(params.limit);
|
|
@@ -6177,6 +6447,9 @@ async function listAllSessions(params = {}) {
|
|
|
6177
6447
|
if (source === 'all' || source === 'codebuddy') {
|
|
6178
6448
|
sessions = sessions.concat(listSessionInventoryBySource('codebuddy', limit, scanOptions, { forceRefresh }));
|
|
6179
6449
|
}
|
|
6450
|
+
if (source === 'all' || source === 'pi') {
|
|
6451
|
+
sessions = sessions.concat(listSessionInventoryBySource('pi', limit, scanOptions, { forceRefresh }));
|
|
6452
|
+
}
|
|
6180
6453
|
|
|
6181
6454
|
if (hasPathFilter) {
|
|
6182
6455
|
sessions = sessions.filter(item => matchesSessionPathFilter(item, normalizedPathFilter));
|
|
@@ -6259,6 +6532,7 @@ async function listSessionUsage(params = {}) {
|
|
|
6259
6532
|
parseClaudeSessionSummary,
|
|
6260
6533
|
parseCodeBuddySessionSummary,
|
|
6261
6534
|
parseGeminiSessionSummary,
|
|
6535
|
+
parsePiSessionSummary,
|
|
6262
6536
|
MAX_SESSION_USAGE_LIST_SIZE,
|
|
6263
6537
|
SESSION_BROWSE_SUMMARY_READ_BYTES
|
|
6264
6538
|
});
|
|
@@ -6272,10 +6546,10 @@ async function exportSessionUsage(params = {}) {
|
|
|
6272
6546
|
|
|
6273
6547
|
function listSessionPaths(params = {}) {
|
|
6274
6548
|
const source = typeof params.source === 'string' ? params.source.trim().toLowerCase() : '';
|
|
6275
|
-
if (source && source !== 'codex' && source !== 'claude' && source !== 'gemini' && source !== 'codebuddy' && source !== 'all') {
|
|
6549
|
+
if (source && source !== 'codex' && source !== 'claude' && source !== 'gemini' && source !== 'codebuddy' && source !== 'pi' && source !== 'all') {
|
|
6276
6550
|
return [];
|
|
6277
6551
|
}
|
|
6278
|
-
const validSource = source === 'codex' || source === 'claude' || source === 'gemini' || source === 'codebuddy' ? source : 'all';
|
|
6552
|
+
const validSource = source === 'codex' || source === 'claude' || source === 'gemini' || source === 'codebuddy' || source === 'pi' ? source : 'all';
|
|
6279
6553
|
const rawLimit = Number(params.limit);
|
|
6280
6554
|
const limit = Number.isFinite(rawLimit)
|
|
6281
6555
|
? Math.max(1, Math.min(rawLimit, MAX_SESSION_PATH_LIST_SIZE))
|
|
@@ -6309,6 +6583,9 @@ function listSessionPaths(params = {}) {
|
|
|
6309
6583
|
if (validSource === 'all' || validSource === 'codebuddy') {
|
|
6310
6584
|
sessions = sessions.concat(listSessionInventoryBySource('codebuddy', gatherLimit, scanOptions, { forceRefresh }));
|
|
6311
6585
|
}
|
|
6586
|
+
if (validSource === 'all' || validSource === 'pi') {
|
|
6587
|
+
sessions = sessions.concat(listSessionInventoryBySource('pi', gatherLimit, scanOptions, { forceRefresh }));
|
|
6588
|
+
}
|
|
6312
6589
|
|
|
6313
6590
|
const dedupedPaths = [];
|
|
6314
6591
|
const seen = new Set();
|
|
@@ -6334,7 +6611,7 @@ function listSessionPaths(params = {}) {
|
|
|
6334
6611
|
}
|
|
6335
6612
|
|
|
6336
6613
|
function resolveSessionFilePath(source, filePath, sessionId) {
|
|
6337
|
-
const normalizedSource = source === 'claude' || source === 'gemini' || source === 'codebuddy'
|
|
6614
|
+
const normalizedSource = source === 'claude' || source === 'gemini' || source === 'codebuddy' || source === 'pi'
|
|
6338
6615
|
? source
|
|
6339
6616
|
: 'codex';
|
|
6340
6617
|
const homeDir = process && process.env && process.env.HOME ? process.env.HOME : '';
|
|
@@ -6346,7 +6623,9 @@ function resolveSessionFilePath(source, filePath, sessionId) {
|
|
|
6346
6623
|
? [getGeminiTmpDir()]
|
|
6347
6624
|
: (normalizedSource === 'codebuddy'
|
|
6348
6625
|
? [getCodeBuddyProjectsDir()]
|
|
6349
|
-
:
|
|
6626
|
+
: (normalizedSource === 'pi'
|
|
6627
|
+
? [getPiSessionsDir()]
|
|
6628
|
+
: [getCodexSessionsDir(), derivedCodexDir])));
|
|
6350
6629
|
const availableRoots = roots.filter((dirPath) => dirPath && fs.existsSync(dirPath));
|
|
6351
6630
|
if (availableRoots.length === 0) {
|
|
6352
6631
|
return '';
|
|
@@ -6894,13 +7173,21 @@ function buildSessionSummaryFallback(source, filePath, sessionId = '') {
|
|
|
6894
7173
|
const resolvedSessionId = sessionId || path.basename(filePath, '.jsonl');
|
|
6895
7174
|
const sourceLabel = source === 'claude'
|
|
6896
7175
|
? 'Claude Code'
|
|
6897
|
-
: (source === 'gemini'
|
|
7176
|
+
: (source === 'gemini'
|
|
7177
|
+
? 'Gemini CLI'
|
|
7178
|
+
: (source === 'codebuddy'
|
|
7179
|
+
? 'CodeBuddy Code'
|
|
7180
|
+
: (source === 'pi' ? 'Pi' : 'Codex')));
|
|
6898
7181
|
return {
|
|
6899
7182
|
source,
|
|
6900
7183
|
sourceLabel,
|
|
6901
7184
|
provider: source === 'claude'
|
|
6902
7185
|
? 'claude'
|
|
6903
|
-
: (source === 'gemini'
|
|
7186
|
+
: (source === 'gemini'
|
|
7187
|
+
? 'gemini'
|
|
7188
|
+
: (source === 'codebuddy'
|
|
7189
|
+
? 'codebuddy'
|
|
7190
|
+
: (source === 'pi' ? 'pi' : 'codex'))),
|
|
6904
7191
|
sessionId: resolvedSessionId,
|
|
6905
7192
|
title: resolvedSessionId,
|
|
6906
7193
|
cwd: '',
|
|
@@ -6953,7 +7240,9 @@ function normalizeSessionTrashEntry(entry) {
|
|
|
6953
7240
|
? 'codex'
|
|
6954
7241
|
: (entry.source === 'gemini'
|
|
6955
7242
|
? 'gemini'
|
|
6956
|
-
: (entry.source === 'codebuddy'
|
|
7243
|
+
: (entry.source === 'codebuddy'
|
|
7244
|
+
? 'codebuddy'
|
|
7245
|
+
: (entry.source === 'pi' ? 'pi' : ''))));
|
|
6957
7246
|
const trashId = typeof entry.trashId === 'string' ? entry.trashId.trim() : '';
|
|
6958
7247
|
if (!source || !trashId || trashId.includes('/') || trashId.includes('\\') || trashId.includes('\0')) {
|
|
6959
7248
|
return null;
|
|
@@ -6970,7 +7259,11 @@ function normalizeSessionTrashEntry(entry) {
|
|
|
6970
7259
|
source,
|
|
6971
7260
|
sourceLabel: source === 'claude'
|
|
6972
7261
|
? 'Claude Code'
|
|
6973
|
-
: (source === 'gemini'
|
|
7262
|
+
: (source === 'gemini'
|
|
7263
|
+
? 'Gemini CLI'
|
|
7264
|
+
: (source === 'codebuddy'
|
|
7265
|
+
? 'CodeBuddy Code'
|
|
7266
|
+
: (source === 'pi' ? 'Pi' : 'Codex'))),
|
|
6974
7267
|
sessionId: sessionId || trashId,
|
|
6975
7268
|
title: typeof entry.title === 'string' && entry.title.trim() ? entry.title.trim() : (sessionId || trashId),
|
|
6976
7269
|
cwd: typeof entry.cwd === 'string' ? entry.cwd : '',
|
|
@@ -6986,7 +7279,7 @@ function normalizeSessionTrashEntry(entry) {
|
|
|
6986
7279
|
originalFilePath: typeof entry.originalFilePath === 'string' ? entry.originalFilePath : '',
|
|
6987
7280
|
provider: typeof entry.provider === 'string' && entry.provider.trim()
|
|
6988
7281
|
? entry.provider.trim()
|
|
6989
|
-
: (source === 'claude' ? 'claude' : (source === 'gemini' ? 'gemini' : (source === 'codebuddy' ? 'codebuddy' : 'codex'))),
|
|
7282
|
+
: (source === 'claude' ? 'claude' : (source === 'gemini' ? 'gemini' : (source === 'codebuddy' ? 'codebuddy' : (source === 'pi' ? 'pi' : 'codex')))),
|
|
6990
7283
|
keywords: normalizeKeywords(entry.keywords),
|
|
6991
7284
|
capabilities: normalizeCapabilities(entry.capabilities),
|
|
6992
7285
|
claudeIndexPath: typeof entry.claudeIndexPath === 'string' ? entry.claudeIndexPath : '',
|
|
@@ -7073,7 +7366,13 @@ function purgeExpiredSessionTrashEntries(retentionDays) {
|
|
|
7073
7366
|
}
|
|
7074
7367
|
|
|
7075
7368
|
function buildSessionTrashEntry(summary, options = {}) {
|
|
7076
|
-
const source = options.source === 'claude'
|
|
7369
|
+
const source = options.source === 'claude'
|
|
7370
|
+
? 'claude'
|
|
7371
|
+
: (options.source === 'gemini'
|
|
7372
|
+
? 'gemini'
|
|
7373
|
+
: (options.source === 'codebuddy'
|
|
7374
|
+
? 'codebuddy'
|
|
7375
|
+
: (options.source === 'pi' ? 'pi' : 'codex')));
|
|
7077
7376
|
const sessionId = options.sessionId || summary.sessionId || path.basename(options.originalFilePath || summary.filePath || '', '.jsonl');
|
|
7078
7377
|
const claudeIndexEntry = options.claudeIndexEntry && typeof options.claudeIndexEntry === 'object' && !Array.isArray(options.claudeIndexEntry)
|
|
7079
7378
|
? options.claudeIndexEntry
|
|
@@ -7081,7 +7380,13 @@ function buildSessionTrashEntry(summary, options = {}) {
|
|
|
7081
7380
|
const deletedAt = typeof options.deletedAt === 'string' && options.deletedAt
|
|
7082
7381
|
? options.deletedAt
|
|
7083
7382
|
: new Date().toISOString();
|
|
7084
|
-
const sourceLabel = source === 'claude'
|
|
7383
|
+
const sourceLabel = source === 'claude'
|
|
7384
|
+
? 'Claude Code'
|
|
7385
|
+
: (source === 'gemini'
|
|
7386
|
+
? 'Gemini CLI'
|
|
7387
|
+
: (source === 'codebuddy'
|
|
7388
|
+
? 'CodeBuddy Code'
|
|
7389
|
+
: (source === 'pi' ? 'Pi' : 'Codex')));
|
|
7085
7390
|
const fallbackTitle = truncateText(
|
|
7086
7391
|
(claudeIndexEntry && (claudeIndexEntry.summary || claudeIndexEntry.firstPrompt)) || sessionId,
|
|
7087
7392
|
120
|
|
@@ -7118,7 +7423,7 @@ function buildSessionTrashEntry(summary, options = {}) {
|
|
|
7118
7423
|
originalFilePath: options.originalFilePath || summary.filePath || '',
|
|
7119
7424
|
provider: (claudeIndexEntry && typeof claudeIndexEntry.provider === 'string' && claudeIndexEntry.provider.trim())
|
|
7120
7425
|
? claudeIndexEntry.provider.trim()
|
|
7121
|
-
: (summary.provider || (source === 'claude' ? 'claude' : 'codex')),
|
|
7426
|
+
: (summary.provider || (source === 'claude' ? 'claude' : (source === 'gemini' ? 'gemini' : (source === 'codebuddy' ? 'codebuddy' : (source === 'pi' ? 'pi' : 'codex'))))),
|
|
7122
7427
|
keywords: normalizedClaudeKeywords.length > 0 ? normalizedClaudeKeywords : normalizedSummaryKeywords,
|
|
7123
7428
|
capabilities: Object.keys(normalizedClaudeCapabilities).length > 0
|
|
7124
7429
|
? normalizedClaudeCapabilities
|
|
@@ -7137,7 +7442,9 @@ function resolveSessionRestoreTarget(entry) {
|
|
|
7137
7442
|
? getClaudeProjectsDir()
|
|
7138
7443
|
: (normalized.source === 'gemini'
|
|
7139
7444
|
? getGeminiTmpDir()
|
|
7140
|
-
: (normalized.source === 'codebuddy'
|
|
7445
|
+
: (normalized.source === 'codebuddy'
|
|
7446
|
+
? getCodeBuddyProjectsDir()
|
|
7447
|
+
: (normalized.source === 'pi' ? getPiSessionsDir() : getCodexSessionsDir())));
|
|
7141
7448
|
const originalFilePath = typeof normalized.originalFilePath === 'string' ? normalized.originalFilePath.trim() : '';
|
|
7142
7449
|
if (!root || !originalFilePath) {
|
|
7143
7450
|
return '';
|
|
@@ -7277,7 +7584,9 @@ async function listSessionTrashItems(params = {}) {
|
|
|
7277
7584
|
? 'codex'
|
|
7278
7585
|
: (params.source === 'gemini'
|
|
7279
7586
|
? 'gemini'
|
|
7280
|
-
: (params.source === 'codebuddy'
|
|
7587
|
+
: (params.source === 'codebuddy'
|
|
7588
|
+
? 'codebuddy'
|
|
7589
|
+
: (params.source === 'pi' ? 'pi' : 'all'))));
|
|
7281
7590
|
const countOnly = params.countOnly === true;
|
|
7282
7591
|
const rawLimit = Number(params.limit);
|
|
7283
7592
|
const limit = Number.isFinite(rawLimit)
|
|
@@ -7287,7 +7596,7 @@ async function listSessionTrashItems(params = {}) {
|
|
|
7287
7596
|
purgeExpiredSessionTrashEntries(params.retentionDays);
|
|
7288
7597
|
}
|
|
7289
7598
|
const allEntries = readSessionTrashEntries();
|
|
7290
|
-
let items = source === 'codex' || source === 'claude' || source === 'gemini' || source === 'codebuddy'
|
|
7599
|
+
let items = source === 'codex' || source === 'claude' || source === 'gemini' || source === 'codebuddy' || source === 'pi'
|
|
7291
7600
|
? allEntries.filter((entry) => entry.source === source)
|
|
7292
7601
|
: allEntries.slice();
|
|
7293
7602
|
items.sort((a, b) => {
|
|
@@ -7473,7 +7782,9 @@ async function trashSessionData(params = {}) {
|
|
|
7473
7782
|
? 'codex'
|
|
7474
7783
|
: (params.source === 'gemini'
|
|
7475
7784
|
? 'gemini'
|
|
7476
|
-
: (params.source === 'codebuddy'
|
|
7785
|
+
: (params.source === 'codebuddy'
|
|
7786
|
+
? 'codebuddy'
|
|
7787
|
+
: (params.source === 'pi' ? 'pi' : ''))));
|
|
7477
7788
|
if (!source) {
|
|
7478
7789
|
return { error: 'Invalid source' };
|
|
7479
7790
|
}
|
|
@@ -7487,7 +7798,9 @@ async function trashSessionData(params = {}) {
|
|
|
7487
7798
|
? parseClaudeSessionSummary(filePath)
|
|
7488
7799
|
: (source === 'gemini'
|
|
7489
7800
|
? parseGeminiSessionSummary(filePath)
|
|
7490
|
-
: (source === 'codebuddy'
|
|
7801
|
+
: (source === 'codebuddy'
|
|
7802
|
+
? parseCodeBuddySessionSummary(filePath)
|
|
7803
|
+
: (source === 'pi' ? parsePiSessionSummary(filePath) : parseCodexSessionSummary(filePath)))))
|
|
7491
7804
|
|| buildSessionSummaryFallback(source, filePath, params.sessionId);
|
|
7492
7805
|
const exactMessageCount = await countConversationMessagesInFile(filePath, source);
|
|
7493
7806
|
if (Number.isFinite(Number(exactMessageCount))) {
|
|
@@ -7584,7 +7897,9 @@ async function deleteSessionData(params = {}) {
|
|
|
7584
7897
|
? 'codex'
|
|
7585
7898
|
: (params.source === 'gemini'
|
|
7586
7899
|
? 'gemini'
|
|
7587
|
-
: (params.source === 'codebuddy'
|
|
7900
|
+
: (params.source === 'codebuddy'
|
|
7901
|
+
? 'codebuddy'
|
|
7902
|
+
: (params.source === 'pi' ? 'pi' : ''))));
|
|
7588
7903
|
if (!source) {
|
|
7589
7904
|
return { error: 'Invalid source' };
|
|
7590
7905
|
}
|
|
@@ -8054,6 +8369,32 @@ function extractCodeBuddyMessageFromRecord(record, state, lineIndex = -1) {
|
|
|
8054
8369
|
}
|
|
8055
8370
|
}
|
|
8056
8371
|
|
|
8372
|
+
function extractPiMessageFromRecord(record, state, lineIndex = -1) {
|
|
8373
|
+
if (record && record.timestamp) {
|
|
8374
|
+
state.updatedAt = toIsoTime(record.timestamp, state.updatedAt);
|
|
8375
|
+
}
|
|
8376
|
+
if (record && record.type === 'session') {
|
|
8377
|
+
state.sessionId = record.id || state.sessionId;
|
|
8378
|
+
state.cwd = record.cwd || state.cwd;
|
|
8379
|
+
return;
|
|
8380
|
+
}
|
|
8381
|
+
if (!record || record.type !== 'message' || !record.message || typeof record.message !== 'object') {
|
|
8382
|
+
return;
|
|
8383
|
+
}
|
|
8384
|
+
const role = normalizeRole(record.message.role);
|
|
8385
|
+
if (role === 'user' || role === 'assistant' || role === 'system') {
|
|
8386
|
+
const text = extractMessageText(record.message.content);
|
|
8387
|
+
if (text && canAppendMessage(state)) {
|
|
8388
|
+
state.messages.push({
|
|
8389
|
+
role,
|
|
8390
|
+
text,
|
|
8391
|
+
timestamp: toIsoTime(record.timestamp, ''),
|
|
8392
|
+
recordLineIndex: Number.isInteger(lineIndex) ? lineIndex : -1
|
|
8393
|
+
});
|
|
8394
|
+
}
|
|
8395
|
+
}
|
|
8396
|
+
}
|
|
8397
|
+
|
|
8057
8398
|
function recordHasCodexMessage(record) {
|
|
8058
8399
|
if (!record || record.type !== 'response_item' || !record.payload) {
|
|
8059
8400
|
return false;
|
|
@@ -8095,9 +8436,22 @@ function recordHasCodeBuddyMessage(record) {
|
|
|
8095
8436
|
return !!text;
|
|
8096
8437
|
}
|
|
8097
8438
|
|
|
8439
|
+
function recordHasPiMessage(record) {
|
|
8440
|
+
if (!record || record.type !== 'message' || !record.message || typeof record.message !== 'object') {
|
|
8441
|
+
return false;
|
|
8442
|
+
}
|
|
8443
|
+
const role = normalizeRole(record.message.role);
|
|
8444
|
+
if (role !== 'user' && role !== 'assistant' && role !== 'system') {
|
|
8445
|
+
return false;
|
|
8446
|
+
}
|
|
8447
|
+
const text = extractMessageText(record.message.content);
|
|
8448
|
+
return !!text;
|
|
8449
|
+
}
|
|
8450
|
+
|
|
8098
8451
|
function recordHasMessage(record, source) {
|
|
8099
8452
|
if (source === 'codex') return recordHasCodexMessage(record);
|
|
8100
8453
|
if (source === 'codebuddy') return recordHasCodeBuddyMessage(record);
|
|
8454
|
+
if (source === 'pi') return recordHasPiMessage(record);
|
|
8101
8455
|
return recordHasClaudeMessage(record);
|
|
8102
8456
|
}
|
|
8103
8457
|
|
|
@@ -8118,6 +8472,8 @@ function extractMessagesFromRecords(records, source, options = {}) {
|
|
|
8118
8472
|
extractCodexMessageFromRecord(record, state, lineIndex);
|
|
8119
8473
|
} else if (source === 'codebuddy') {
|
|
8120
8474
|
extractCodeBuddyMessageFromRecord(record, state, lineIndex);
|
|
8475
|
+
} else if (source === 'pi') {
|
|
8476
|
+
extractPiMessageFromRecord(record, state, lineIndex);
|
|
8121
8477
|
} else {
|
|
8122
8478
|
extractClaudeMessageFromRecord(record, state, lineIndex);
|
|
8123
8479
|
}
|
|
@@ -8181,6 +8537,8 @@ async function extractMessagesFromFile(filePath, source, options = {}) {
|
|
|
8181
8537
|
extractCodexMessageFromRecord(record, state, currentLineIndex);
|
|
8182
8538
|
} else if (source === 'codebuddy') {
|
|
8183
8539
|
extractCodeBuddyMessageFromRecord(record, state, currentLineIndex);
|
|
8540
|
+
} else if (source === 'pi') {
|
|
8541
|
+
extractPiMessageFromRecord(record, state, currentLineIndex);
|
|
8184
8542
|
} else {
|
|
8185
8543
|
extractClaudeMessageFromRecord(record, state, currentLineIndex);
|
|
8186
8544
|
}
|
|
@@ -8211,7 +8569,9 @@ async function readSessionDetail(params = {}) {
|
|
|
8211
8569
|
? 'codex'
|
|
8212
8570
|
: (params.source === 'gemini'
|
|
8213
8571
|
? 'gemini'
|
|
8214
|
-
: (params.source === 'codebuddy'
|
|
8572
|
+
: (params.source === 'codebuddy'
|
|
8573
|
+
? 'codebuddy'
|
|
8574
|
+
: (params.source === 'pi' ? 'pi' : ''))));
|
|
8215
8575
|
if (!source) {
|
|
8216
8576
|
return { error: 'Invalid source' };
|
|
8217
8577
|
}
|
|
@@ -8273,7 +8633,9 @@ async function readSessionDetail(params = {}) {
|
|
|
8273
8633
|
? 'Codex'
|
|
8274
8634
|
: (source === 'claude'
|
|
8275
8635
|
? 'Claude Code'
|
|
8276
|
-
: (source === 'gemini'
|
|
8636
|
+
: (source === 'gemini'
|
|
8637
|
+
? 'Gemini CLI'
|
|
8638
|
+
: (source === 'pi' ? 'Pi' : 'CodeBuddy Code')));
|
|
8277
8639
|
const clippedMessages = Array.isArray(extracted.messages) ? extracted.messages : [];
|
|
8278
8640
|
const hasExactTotalMessages = Number.isFinite(extracted.totalMessages);
|
|
8279
8641
|
const startIndex = hasExactTotalMessages
|
|
@@ -8340,7 +8702,9 @@ async function readSessionPlain(params = {}) {
|
|
|
8340
8702
|
? 'codex'
|
|
8341
8703
|
: (params.source === 'gemini'
|
|
8342
8704
|
? 'gemini'
|
|
8343
|
-
: (params.source === 'codebuddy'
|
|
8705
|
+
: (params.source === 'codebuddy'
|
|
8706
|
+
? 'codebuddy'
|
|
8707
|
+
: (params.source === 'pi' ? 'pi' : ''))));
|
|
8344
8708
|
if (!source) {
|
|
8345
8709
|
return { error: 'Invalid source' };
|
|
8346
8710
|
}
|
|
@@ -8414,7 +8778,9 @@ async function readSessionPlain(params = {}) {
|
|
|
8414
8778
|
? 'Codex'
|
|
8415
8779
|
: (source === 'claude'
|
|
8416
8780
|
? 'Claude Code'
|
|
8417
|
-
: (source === 'gemini'
|
|
8781
|
+
: (source === 'gemini'
|
|
8782
|
+
? 'Gemini CLI'
|
|
8783
|
+
: (source === 'pi' ? 'Pi' : 'CodeBuddy Code')));
|
|
8418
8784
|
const messages = removeLeadingSystemMessage(Array.isArray(extracted.messages) ? extracted.messages : []);
|
|
8419
8785
|
const text = buildSessionPlainText(messages);
|
|
8420
8786
|
|
|
@@ -8436,7 +8802,9 @@ async function exportSessionData(params = {}) {
|
|
|
8436
8802
|
? 'codex'
|
|
8437
8803
|
: (params.source === 'gemini'
|
|
8438
8804
|
? 'gemini'
|
|
8439
|
-
: (params.source === 'codebuddy'
|
|
8805
|
+
: (params.source === 'codebuddy'
|
|
8806
|
+
? 'codebuddy'
|
|
8807
|
+
: (params.source === 'pi' ? 'pi' : ''))));
|
|
8440
8808
|
if (!source) {
|
|
8441
8809
|
return { error: 'Invalid source' };
|
|
8442
8810
|
}
|
|
@@ -8510,7 +8878,9 @@ async function exportSessionData(params = {}) {
|
|
|
8510
8878
|
? 'Codex'
|
|
8511
8879
|
: (source === 'claude'
|
|
8512
8880
|
? 'Claude Code'
|
|
8513
|
-
: (source === 'gemini'
|
|
8881
|
+
: (source === 'gemini'
|
|
8882
|
+
? 'Gemini CLI'
|
|
8883
|
+
: (source === 'pi' ? 'Pi' : 'CodeBuddy Code')));
|
|
8514
8884
|
const truncated = !!extracted.truncated;
|
|
8515
8885
|
const maxMessagesLabel = maxMessages === Infinity ? 'all' : maxMessages;
|
|
8516
8886
|
const markdown = buildSessionMarkdown({
|
|
@@ -11696,7 +12066,7 @@ function resolveExportOutputPath(outputPath, defaultFileName) {
|
|
|
11696
12066
|
}
|
|
11697
12067
|
|
|
11698
12068
|
function printExportSessionUsage() {
|
|
11699
|
-
console.log('\n用法: codexmate export-session --source <codex|claude|gemini|codebuddy> (--session-id <ID>|--file <PATH>) [--output <PATH>] [--max-messages <N|all|Infinity>]');
|
|
12069
|
+
console.log('\n用法: codexmate export-session --source <codex|claude|gemini|codebuddy|pi> (--session-id <ID>|--file <PATH>) [--output <PATH>] [--max-messages <N|all|Infinity>]');
|
|
11700
12070
|
console.log('\n示例:');
|
|
11701
12071
|
console.log(' codexmate export-session --source codex --session-id 123456');
|
|
11702
12072
|
console.log(' codexmate export-session --source claude --file "~/.claude/projects/demo/session.jsonl"');
|
|
@@ -11768,8 +12138,8 @@ function parseExportSessionArgs(args = []) {
|
|
|
11768
12138
|
}
|
|
11769
12139
|
|
|
11770
12140
|
const normalizedSource = options.source.trim().toLowerCase();
|
|
11771
|
-
if (normalizedSource && normalizedSource !== 'codex' && normalizedSource !== 'claude') {
|
|
11772
|
-
errors.push('参数 --source 仅支持 codex 或
|
|
12141
|
+
if (normalizedSource && normalizedSource !== 'codex' && normalizedSource !== 'claude' && normalizedSource !== 'pi') {
|
|
12142
|
+
errors.push('参数 --source 仅支持 codex、claude 或 pi');
|
|
11773
12143
|
}
|
|
11774
12144
|
options.source = normalizedSource;
|
|
11775
12145
|
|
|
@@ -11842,7 +12212,7 @@ async function cmdExportSession(args = []) {
|
|
|
11842
12212
|
|
|
11843
12213
|
function printAnalyticsUsage() {
|
|
11844
12214
|
console.log('\n用法:');
|
|
11845
|
-
console.log(' codexmate analytics export [--format csv|json] [--from YYYY-MM-DD] [--to YYYY-MM-DD] [--model <MODEL>] [--source <codex|claude|gemini|codebuddy|all>] [--output <PATH|->] [-o <PATH|->]');
|
|
12215
|
+
console.log(' codexmate analytics export [--format csv|json] [--from YYYY-MM-DD] [--to YYYY-MM-DD] [--model <MODEL>] [--source <codex|claude|gemini|codebuddy|pi|all>] [--output <PATH|->] [-o <PATH|->]');
|
|
11846
12216
|
console.log('');
|
|
11847
12217
|
}
|
|
11848
12218
|
|
|
@@ -13260,8 +13630,8 @@ function createWebServer({ htmlPath, assetsDir, webDir, host, port, openBrowser
|
|
|
13260
13630
|
case 'list-sessions':
|
|
13261
13631
|
{
|
|
13262
13632
|
const source = typeof params.source === 'string' ? params.source.trim().toLowerCase() : '';
|
|
13263
|
-
if (source && source !== 'codex' && source !== 'claude' && source !== 'gemini' && source !== 'codebuddy' && source !== 'all') {
|
|
13264
|
-
result = { error: 'Invalid source. Must be codex, claude, gemini, codebuddy, or all' };
|
|
13633
|
+
if (source && source !== 'codex' && source !== 'claude' && source !== 'gemini' && source !== 'codebuddy' && source !== 'pi' && source !== 'all') {
|
|
13634
|
+
result = { error: 'Invalid source. Must be codex, claude, gemini, codebuddy, pi, or all' };
|
|
13265
13635
|
} else {
|
|
13266
13636
|
result = {
|
|
13267
13637
|
sessions: await listSessionBrowse(params),
|
|
@@ -13274,8 +13644,8 @@ function createWebServer({ htmlPath, assetsDir, webDir, host, port, openBrowser
|
|
|
13274
13644
|
{
|
|
13275
13645
|
const usageParams = isPlainObject(params) ? params : {};
|
|
13276
13646
|
const source = typeof usageParams.source === 'string' ? usageParams.source.trim().toLowerCase() : '';
|
|
13277
|
-
if (source && source !== 'codex' && source !== 'claude' && source !== 'gemini' && source !== 'codebuddy' && source !== 'all') {
|
|
13278
|
-
result = { error: 'Invalid source. Must be codex, claude, gemini, codebuddy, or all' };
|
|
13647
|
+
if (source && source !== 'codex' && source !== 'claude' && source !== 'gemini' && source !== 'codebuddy' && source !== 'pi' && source !== 'all') {
|
|
13648
|
+
result = { error: 'Invalid source. Must be codex, claude, gemini, codebuddy, pi, or all' };
|
|
13279
13649
|
} else {
|
|
13280
13650
|
result = {
|
|
13281
13651
|
sessions: await listSessionUsage({
|
|
@@ -13291,8 +13661,8 @@ function createWebServer({ htmlPath, assetsDir, webDir, host, port, openBrowser
|
|
|
13291
13661
|
{
|
|
13292
13662
|
const usageParams = isPlainObject(params) ? params : {};
|
|
13293
13663
|
const source = typeof usageParams.source === 'string' ? usageParams.source.trim().toLowerCase() : '';
|
|
13294
|
-
if (source && source !== 'codex' && source !== 'claude' && source !== 'gemini' && source !== 'codebuddy' && source !== 'all') {
|
|
13295
|
-
result = { error: 'Invalid source. Must be codex, claude, gemini, codebuddy, or all' };
|
|
13664
|
+
if (source && source !== 'codex' && source !== 'claude' && source !== 'gemini' && source !== 'codebuddy' && source !== 'pi' && source !== 'all') {
|
|
13665
|
+
result = { error: 'Invalid source. Must be codex, claude, gemini, codebuddy, pi, or all' };
|
|
13296
13666
|
} else {
|
|
13297
13667
|
result = await exportSessionUsage({
|
|
13298
13668
|
...usageParams,
|
|
@@ -13304,8 +13674,8 @@ function createWebServer({ htmlPath, assetsDir, webDir, host, port, openBrowser
|
|
|
13304
13674
|
case 'list-session-paths':
|
|
13305
13675
|
{
|
|
13306
13676
|
const source = typeof params.source === 'string' ? params.source.trim().toLowerCase() : '';
|
|
13307
|
-
if (source && source !== 'codex' && source !== 'claude' && source !== 'gemini' && source !== 'codebuddy' && source !== 'all') {
|
|
13308
|
-
result = { error: 'Invalid source. Must be codex, claude, gemini, codebuddy, or all' };
|
|
13677
|
+
if (source && source !== 'codex' && source !== 'claude' && source !== 'gemini' && source !== 'codebuddy' && source !== 'pi' && source !== 'all') {
|
|
13678
|
+
result = { error: 'Invalid source. Must be codex, claude, gemini, codebuddy, pi, or all' };
|
|
13309
13679
|
} else {
|
|
13310
13680
|
result = {
|
|
13311
13681
|
paths: listSessionPaths(params)
|
|
@@ -15564,7 +15934,7 @@ function buildMcpClaudeSettingsPayload() {
|
|
|
15564
15934
|
function normalizeMcpSource(value) {
|
|
15565
15935
|
const source = typeof value === 'string' ? value.trim().toLowerCase() : '';
|
|
15566
15936
|
if (!source) return '';
|
|
15567
|
-
if (source === 'codex' || source === 'claude' || source === 'gemini' || source === 'codebuddy' || source === 'all') {
|
|
15937
|
+
if (source === 'codex' || source === 'claude' || source === 'gemini' || source === 'codebuddy' || source === 'pi' || source === 'all') {
|
|
15568
15938
|
return source;
|
|
15569
15939
|
}
|
|
15570
15940
|
return null;
|
|
@@ -15877,7 +16247,7 @@ function createWorkflowToolCatalog() {
|
|
|
15877
16247
|
handler: async (args = {}) => {
|
|
15878
16248
|
const source = normalizeMcpSource(args.source);
|
|
15879
16249
|
if (source === null) {
|
|
15880
|
-
return { error: 'Invalid source. Must be codex, claude, gemini, codebuddy, or all' };
|
|
16250
|
+
return { error: 'Invalid source. Must be codex, claude, gemini, codebuddy, pi, or all' };
|
|
15881
16251
|
}
|
|
15882
16252
|
return {
|
|
15883
16253
|
source: source || 'all',
|
|
@@ -17997,7 +18367,7 @@ function createMcpTools(options = {}) {
|
|
|
17997
18367
|
const input = args && typeof args === 'object' ? args : {};
|
|
17998
18368
|
const source = normalizeMcpSource(input.source);
|
|
17999
18369
|
if (source === null) {
|
|
18000
|
-
return { error: 'Invalid source. Must be codex, claude, gemini, codebuddy, or all' };
|
|
18370
|
+
return { error: 'Invalid source. Must be codex, claude, gemini, codebuddy, pi, or all' };
|
|
18001
18371
|
}
|
|
18002
18372
|
const normalizedInput = {
|
|
18003
18373
|
...input,
|
|
@@ -18457,7 +18827,7 @@ function createMcpResources() {
|
|
|
18457
18827
|
contents: [{
|
|
18458
18828
|
uri,
|
|
18459
18829
|
mimeType: 'application/json',
|
|
18460
|
-
text: JSON.stringify({ error: 'Invalid source. Must be codex, claude, gemini, codebuddy, or all' }, null, 2)
|
|
18830
|
+
text: JSON.stringify({ error: 'Invalid source. Must be codex, claude, gemini, codebuddy, pi, or all' }, null, 2)
|
|
18461
18831
|
}]
|
|
18462
18832
|
};
|
|
18463
18833
|
}
|
|
@@ -18707,7 +19077,7 @@ function printMainHelp() {
|
|
|
18707
19077
|
console.log(' 注: follow-up 自动排队仅支持 linux/android/netbsd/openbsd/darwin/freebsd 且 stdin 必须是 TTY,其他平台会报错');
|
|
18708
19078
|
console.log(' codexmate qwen [参数...] 等同于 qwen --yolo');
|
|
18709
19079
|
console.log(' codexmate mcp [serve] [--transport stdio] [--allow-write|--read-only]');
|
|
18710
|
-
console.log(' codexmate export-session --source <codex|claude|gemini|codebuddy> (--session-id <ID>|--file <PATH>) [--output <PATH>] [--max-messages <N|all|Infinity>]');
|
|
19080
|
+
console.log(' codexmate export-session --source <codex|claude|gemini|codebuddy|pi> (--session-id <ID>|--file <PATH>) [--output <PATH>] [--max-messages <N|all|Infinity>]');
|
|
18711
19081
|
console.log(' codexmate convert-session --from <codex|claude> --to <codex|claude> (--session-id <ID>|--file <PATH>) [--output <PATH>] [--max-messages <N|all|Infinity>]');
|
|
18712
19082
|
console.log(' codexmate zip <路径> [--max:级别] 压缩(系统 zip 优先,其次 zip-lib)');
|
|
18713
19083
|
console.log(' codexmate unzip <zip文件> [输出目录] 解压(zip-lib)');
|