slacklocalvibe 0.1.2 → 0.1.4
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/package.json +1 -1
- package/src/commands/notify.js +15 -1
- package/src/commands/wizard.js +199 -26
- package/src/lib/notify-input.js +214 -10
package/package.json
CHANGED
package/src/commands/notify.js
CHANGED
|
@@ -69,12 +69,26 @@ async function runNotify({ tool }) {
|
|
|
69
69
|
throw new Error("通知対象のイベントではありません。");
|
|
70
70
|
}
|
|
71
71
|
if (input.skip) {
|
|
72
|
-
|
|
72
|
+
const reason = input.skip_reason || "unknown";
|
|
73
|
+
log(LEVELS.WARNING, `notify.skip.${reason}`, {
|
|
73
74
|
meta: input.meta || {},
|
|
74
75
|
duration_ms: Date.now() - startedAt,
|
|
75
76
|
});
|
|
76
77
|
return;
|
|
77
78
|
}
|
|
79
|
+
if (input.tool === "codex") {
|
|
80
|
+
const meta = input.meta || {};
|
|
81
|
+
log(LEVELS.INFO, "notify.codex_prompt_source", {
|
|
82
|
+
rollout_found: meta.codex_rollout_found,
|
|
83
|
+
rollout_source: meta.codex_rollout_source,
|
|
84
|
+
rollout_path: meta.codex_rollout_path,
|
|
85
|
+
rollout_user_messages: meta.codex_rollout_user_message_count,
|
|
86
|
+
rollout_line_count: meta.codex_rollout_line_count,
|
|
87
|
+
input_messages_len: meta.input_messages_len,
|
|
88
|
+
input_messages_has_content: meta.input_messages_has_content,
|
|
89
|
+
rollout_error: meta.codex_rollout_error,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
78
92
|
|
|
79
93
|
if (!input.session_id) {
|
|
80
94
|
log(LEVELS.ERROR, "notify.session_missing");
|
package/src/commands/wizard.js
CHANGED
|
@@ -102,21 +102,76 @@ async function runWizard() {
|
|
|
102
102
|
console.log("SlackLocalVibe セットアップを開始します。");
|
|
103
103
|
if (normalized) {
|
|
104
104
|
console.log("既存設定が見つかりました。開始方法を選んでください。");
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
105
|
+
while (true) {
|
|
106
|
+
const choice = await promptSelect({
|
|
107
|
+
message: "どこから始めますか?",
|
|
108
|
+
choices: [
|
|
109
|
+
{ title: "アップデートを確認する", value: "update" },
|
|
110
|
+
{ title: "テストから始める", value: "test" },
|
|
111
|
+
{ title: "リセットして最初からセットアップ", value: "reset" },
|
|
112
|
+
{ title: "終了", value: "exit" },
|
|
113
|
+
],
|
|
114
|
+
initial: 0,
|
|
115
|
+
});
|
|
116
|
+
if (choice === "update") {
|
|
117
|
+
log(LEVELS.INFO, "wizard.update_check_start");
|
|
118
|
+
const versionInfo = await fetchUpdateVersions({ log });
|
|
119
|
+
log(LEVELS.INFO, "wizard.update_versions", {
|
|
120
|
+
installed: versionInfo.installedVersion || "unknown",
|
|
121
|
+
latest: versionInfo.latestVersion || "unknown",
|
|
122
|
+
});
|
|
123
|
+
console.log(formatInfo(`現在: ${formatVersionLabel(versionInfo.installedVersion)}`));
|
|
124
|
+
console.log(formatInfo(`最新: ${formatVersionLabel(versionInfo.latestVersion)}`));
|
|
125
|
+
if (
|
|
126
|
+
versionInfo.installedVersion &&
|
|
127
|
+
versionInfo.latestVersion &&
|
|
128
|
+
versionInfo.installedVersion === versionInfo.latestVersion
|
|
129
|
+
) {
|
|
130
|
+
log(LEVELS.SUCCRSS, "wizard.update_latest_confirmed");
|
|
131
|
+
console.log(formatSuccess("最新バージョンです。"));
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const updateChoice = await promptSelect({
|
|
136
|
+
message: "アップデートしますか?",
|
|
137
|
+
choices: [
|
|
138
|
+
{ title: "アップデートする", value: "update" },
|
|
139
|
+
{ title: "戻る", value: "back" },
|
|
140
|
+
{ title: "終了", value: "exit" },
|
|
141
|
+
],
|
|
142
|
+
initial: 0,
|
|
143
|
+
});
|
|
144
|
+
if (updateChoice === "back") {
|
|
145
|
+
log(LEVELS.INFO, "wizard.update_skipped");
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
if (updateChoice === "exit") {
|
|
149
|
+
throw new UserExit();
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
await ensureGlobalInstall({ log });
|
|
153
|
+
const postUpdate = await fetchUpdateVersions({ log });
|
|
154
|
+
log(LEVELS.SUCCRSS, "wizard.update_applied", {
|
|
155
|
+
installed: postUpdate.installedVersion || "unknown",
|
|
156
|
+
latest: postUpdate.latestVersion || "unknown",
|
|
157
|
+
});
|
|
158
|
+
console.log(formatSuccess("アップデートが完了しました。"));
|
|
159
|
+
console.log(formatInfo(`現在: ${formatVersionLabel(postUpdate.installedVersion)}`));
|
|
160
|
+
console.log(formatInfo(`最新: ${formatVersionLabel(postUpdate.latestVersion)}`));
|
|
161
|
+
|
|
162
|
+
const launchdRecommended = Boolean(normalized?.features?.launchd_enabled);
|
|
163
|
+
await promptLaunchdReinstall({ log, recommended: launchdRecommended });
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
if (choice === "reset") {
|
|
167
|
+
resetStoredConfig({ log });
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
if (choice === "test") {
|
|
171
|
+
startFromTest = true;
|
|
172
|
+
console.log("既存設定を使って通知テストから開始します。");
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
120
175
|
throw new UserExit();
|
|
121
176
|
}
|
|
122
177
|
}
|
|
@@ -147,7 +202,7 @@ async function runWizard() {
|
|
|
147
202
|
message: "次の操作を選んでください",
|
|
148
203
|
choices: [
|
|
149
204
|
{ title: "リセットして最初から", value: "reset" },
|
|
150
|
-
{ title: "
|
|
205
|
+
{ title: "終了", value: "exit" },
|
|
151
206
|
],
|
|
152
207
|
});
|
|
153
208
|
if (next === "reset") {
|
|
@@ -349,7 +404,7 @@ async function stepReplySetup({ log }) {
|
|
|
349
404
|
message: "次の操作を選んでください",
|
|
350
405
|
choices: [
|
|
351
406
|
{ title: "npm i -g slacklocalvibe で登録する(必須)", value: "install" },
|
|
352
|
-
{ title: "
|
|
407
|
+
{ title: "終了", value: "exit" },
|
|
353
408
|
],
|
|
354
409
|
initial: 0,
|
|
355
410
|
});
|
|
@@ -412,7 +467,7 @@ async function stepNotifyTest({ log, botToken, dmConfig }) {
|
|
|
412
467
|
choices: [
|
|
413
468
|
{ title: "Bot Token を再入力する", value: "retry_token" },
|
|
414
469
|
{ title: "送信先(DM)を見直す", value: "retry_dm" },
|
|
415
|
-
{ title: "
|
|
470
|
+
{ title: "終了", value: "exit" },
|
|
416
471
|
],
|
|
417
472
|
});
|
|
418
473
|
if (choice === "retry_token") {
|
|
@@ -472,7 +527,7 @@ async function stepCodexConfig({ log }) {
|
|
|
472
527
|
message: "Codex設定の更新に失敗しました。次の操作を選んでください",
|
|
473
528
|
choices: [
|
|
474
529
|
{ title: "再試行", value: "retry" },
|
|
475
|
-
{ title: "
|
|
530
|
+
{ title: "終了", value: "exit" },
|
|
476
531
|
],
|
|
477
532
|
});
|
|
478
533
|
if (next === "retry") continue;
|
|
@@ -506,7 +561,7 @@ async function stepClaudeConfig({ log }) {
|
|
|
506
561
|
message: "Claude設定の更新に失敗しました。次の操作を選んでください",
|
|
507
562
|
choices: [
|
|
508
563
|
{ title: "再試行", value: "retry" },
|
|
509
|
-
{ title: "
|
|
564
|
+
{ title: "終了", value: "exit" },
|
|
510
565
|
],
|
|
511
566
|
});
|
|
512
567
|
if (next === "retry") continue;
|
|
@@ -541,7 +596,7 @@ async function stepDeliveryConfirmation({ log, stage }) {
|
|
|
541
596
|
choices: [
|
|
542
597
|
{ title: "届いたので次へ進む", value: "ok" },
|
|
543
598
|
{ title: "届いていないのでログを表示する", value: "logs" },
|
|
544
|
-
{ title: "
|
|
599
|
+
{ title: "終了", value: "exit" },
|
|
545
600
|
],
|
|
546
601
|
});
|
|
547
602
|
if (choice === "ok") {
|
|
@@ -654,7 +709,7 @@ async function runTestCommand({ log, tool }) {
|
|
|
654
709
|
message: "次の操作を選んでください",
|
|
655
710
|
choices: [
|
|
656
711
|
{ title: "再試行", value: "retry" },
|
|
657
|
-
{ title: "
|
|
712
|
+
{ title: "終了", value: "exit" },
|
|
658
713
|
],
|
|
659
714
|
});
|
|
660
715
|
if (next === "retry") continue;
|
|
@@ -672,7 +727,7 @@ async function stepLaunchd({ log }) {
|
|
|
672
727
|
choices: [
|
|
673
728
|
{ title: "launchd に登録する(推奨)", value: "install" },
|
|
674
729
|
{ title: "スキップして次へ進む", value: "skip" },
|
|
675
|
-
{ title: "
|
|
730
|
+
{ title: "終了", value: "exit" },
|
|
676
731
|
],
|
|
677
732
|
initial: 0,
|
|
678
733
|
});
|
|
@@ -708,7 +763,7 @@ async function stepLaunchd({ log }) {
|
|
|
708
763
|
choices: [
|
|
709
764
|
{ title: "再試行", value: "retry" },
|
|
710
765
|
{ title: "スキップして次へ進む", value: "skip" },
|
|
711
|
-
{ title: "
|
|
766
|
+
{ title: "終了", value: "exit" },
|
|
712
767
|
],
|
|
713
768
|
});
|
|
714
769
|
if (next === "retry") continue;
|
|
@@ -919,7 +974,7 @@ async function copyManifestToClipboard({ log }) {
|
|
|
919
974
|
message: "次の操作を選んでください",
|
|
920
975
|
choices: [
|
|
921
976
|
{ title: "再試行", value: "retry" },
|
|
922
|
-
{ title: "
|
|
977
|
+
{ title: "終了", value: "exit" },
|
|
923
978
|
],
|
|
924
979
|
});
|
|
925
980
|
if (next === "retry") continue;
|
|
@@ -1061,7 +1116,7 @@ async function ensureGlobalInstall({ log }) {
|
|
|
1061
1116
|
message: "次の操作を選んでください",
|
|
1062
1117
|
choices: [
|
|
1063
1118
|
{ title: "再試行", value: "retry" },
|
|
1064
|
-
{ title: "
|
|
1119
|
+
{ title: "終了", value: "exit" },
|
|
1065
1120
|
],
|
|
1066
1121
|
});
|
|
1067
1122
|
if (next === "retry") continue;
|
|
@@ -1070,6 +1125,124 @@ async function ensureGlobalInstall({ log }) {
|
|
|
1070
1125
|
}
|
|
1071
1126
|
}
|
|
1072
1127
|
|
|
1128
|
+
async function fetchUpdateVersions({ log }) {
|
|
1129
|
+
const [installedVersion, latestVersion] = await Promise.all([
|
|
1130
|
+
fetchInstalledVersion({ log }),
|
|
1131
|
+
fetchRegistryVersion({ log }),
|
|
1132
|
+
]);
|
|
1133
|
+
return { installedVersion, latestVersion };
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
async function fetchInstalledVersion({ log }) {
|
|
1137
|
+
try {
|
|
1138
|
+
const binaryPath = resolveBinaryPath();
|
|
1139
|
+
const result = await spawnCommand({
|
|
1140
|
+
command: binaryPath,
|
|
1141
|
+
args: ["--version"],
|
|
1142
|
+
cwd: process.cwd(),
|
|
1143
|
+
});
|
|
1144
|
+
if (result.code === 0) {
|
|
1145
|
+
const version = (result.stdoutText || "").trim().split(/\s+/)[0];
|
|
1146
|
+
if (version) return version;
|
|
1147
|
+
}
|
|
1148
|
+
log(LEVELS.WARNING, "wizard.update_installed_version_empty", {
|
|
1149
|
+
stdout_len: result.stdoutLen,
|
|
1150
|
+
stderr_len: result.stderrLen,
|
|
1151
|
+
});
|
|
1152
|
+
} catch (error) {
|
|
1153
|
+
log(LEVELS.WARNING, "wizard.update_installed_version_failed", {
|
|
1154
|
+
error: safeError(error),
|
|
1155
|
+
});
|
|
1156
|
+
}
|
|
1157
|
+
return "";
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
async function fetchRegistryVersion({ log }) {
|
|
1161
|
+
try {
|
|
1162
|
+
const npmPath = resolveCommandPathStrict("npm");
|
|
1163
|
+
const result = await spawnCommand({
|
|
1164
|
+
command: npmPath,
|
|
1165
|
+
args: ["view", "slacklocalvibe", "version"],
|
|
1166
|
+
cwd: process.cwd(),
|
|
1167
|
+
});
|
|
1168
|
+
if (result.code === 0) {
|
|
1169
|
+
const version = (result.stdoutText || "").trim().split(/\s+/)[0];
|
|
1170
|
+
if (version) return version;
|
|
1171
|
+
}
|
|
1172
|
+
log(LEVELS.WARNING, "wizard.update_registry_version_empty", {
|
|
1173
|
+
stdout_len: result.stdoutLen,
|
|
1174
|
+
stderr_len: result.stderrLen,
|
|
1175
|
+
});
|
|
1176
|
+
} catch (error) {
|
|
1177
|
+
log(LEVELS.WARNING, "wizard.update_registry_version_failed", {
|
|
1178
|
+
error: safeError(error),
|
|
1179
|
+
});
|
|
1180
|
+
}
|
|
1181
|
+
return "";
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
function formatVersionLabel(version) {
|
|
1185
|
+
return version ? version : "不明";
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
async function promptLaunchdReinstall({ log, recommended }) {
|
|
1189
|
+
const title = recommended
|
|
1190
|
+
? "launchd を再登録する(推奨)"
|
|
1191
|
+
: "launchd を再登録する";
|
|
1192
|
+
while (true) {
|
|
1193
|
+
const choice = await promptSelect({
|
|
1194
|
+
message: "更新後に launchd を再登録します",
|
|
1195
|
+
choices: [
|
|
1196
|
+
{ title, value: "install" },
|
|
1197
|
+
{ title: "あとでやる", value: "skip" },
|
|
1198
|
+
{ title: "終了", value: "exit" },
|
|
1199
|
+
],
|
|
1200
|
+
initial: 0,
|
|
1201
|
+
});
|
|
1202
|
+
if (choice === "skip") {
|
|
1203
|
+
log(LEVELS.INFO, "wizard.launchd_reinstall_skipped");
|
|
1204
|
+
return;
|
|
1205
|
+
}
|
|
1206
|
+
if (choice === "exit") {
|
|
1207
|
+
throw new UserExit();
|
|
1208
|
+
}
|
|
1209
|
+
try {
|
|
1210
|
+
installLaunchd();
|
|
1211
|
+
log(LEVELS.SUCCRSS, "wizard.launchd_reinstalled");
|
|
1212
|
+
console.log(formatSuccess("launchd を再登録しました。"));
|
|
1213
|
+
return;
|
|
1214
|
+
} catch (error) {
|
|
1215
|
+
log(LEVELS.ERROR, "wizard.launchd_reinstall_failed", {
|
|
1216
|
+
error: safeError(error),
|
|
1217
|
+
});
|
|
1218
|
+
console.log(formatError("launchd の再登録に失敗しました。"));
|
|
1219
|
+
const detail =
|
|
1220
|
+
error?.detail ||
|
|
1221
|
+
error?.stderrText ||
|
|
1222
|
+
error?.stdoutText ||
|
|
1223
|
+
error?.message ||
|
|
1224
|
+
"unknown";
|
|
1225
|
+
if (detail) {
|
|
1226
|
+
console.log(formatError(detail));
|
|
1227
|
+
}
|
|
1228
|
+
const next = await promptSelect({
|
|
1229
|
+
message: "次の操作を選んでください",
|
|
1230
|
+
choices: [
|
|
1231
|
+
{ title: "再試行", value: "retry" },
|
|
1232
|
+
{ title: "あとでやる", value: "skip" },
|
|
1233
|
+
{ title: "終了", value: "exit" },
|
|
1234
|
+
],
|
|
1235
|
+
});
|
|
1236
|
+
if (next === "retry") continue;
|
|
1237
|
+
if (next === "skip") {
|
|
1238
|
+
log(LEVELS.WARNING, "wizard.launchd_reinstall_skipped_after_error");
|
|
1239
|
+
return;
|
|
1240
|
+
}
|
|
1241
|
+
throw new UserExit();
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1073
1246
|
async function promptPassword({ message, validate }) {
|
|
1074
1247
|
while (true) {
|
|
1075
1248
|
const value = await promptVisible({ message });
|
package/src/lib/notify-input.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
const fs = require("fs");
|
|
2
|
+
const os = require("os");
|
|
3
|
+
const path = require("path");
|
|
2
4
|
|
|
3
5
|
function parseCodexNotify(rawJson) {
|
|
4
6
|
const payload = JSON.parse(rawJson);
|
|
@@ -9,16 +11,19 @@ function parseCodexNotify(rawJson) {
|
|
|
9
11
|
const turnId = payload["turn-id"] ? String(payload["turn-id"]) : undefined;
|
|
10
12
|
const inputMessages = payload["input-messages"];
|
|
11
13
|
const meta = buildCodexInputMeta(inputMessages);
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
+
const codexHomeInfo = resolveCodexHomeInfo();
|
|
15
|
+
Object.assign(meta, codexHomeInfo.meta || {});
|
|
16
|
+
if (codexHomeInfo.isDefault) {
|
|
14
17
|
return {
|
|
15
18
|
tool: "codex",
|
|
16
19
|
skip: true,
|
|
17
|
-
skip_reason: "
|
|
20
|
+
skip_reason: "codex_home_default",
|
|
18
21
|
meta,
|
|
19
22
|
};
|
|
20
23
|
}
|
|
21
|
-
const
|
|
24
|
+
const rolloutResult = readCodexUserMessageFromRollout(sessionId, codexHomeInfo.home);
|
|
25
|
+
Object.assign(meta, rolloutResult.meta || {});
|
|
26
|
+
const userText = rolloutResult.userText || "";
|
|
22
27
|
const assistantText = extractAssistantText(payload["last-assistant-message"]);
|
|
23
28
|
const cwd = payload?.cwd ? String(payload.cwd) : "";
|
|
24
29
|
|
|
@@ -33,16 +38,11 @@ function parseCodexNotify(rawJson) {
|
|
|
33
38
|
};
|
|
34
39
|
}
|
|
35
40
|
|
|
36
|
-
function extractUserTextFromCodex(inputMessages) {
|
|
37
|
-
if (!Array.isArray(inputMessages) || inputMessages.length === 0) return "";
|
|
38
|
-
const lastMessage = inputMessages[inputMessages.length - 1];
|
|
39
|
-
return normalizeContent(lastMessage);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
41
|
function buildCodexInputMeta(inputMessages) {
|
|
43
42
|
const meta = {
|
|
44
43
|
input_messages_type: Array.isArray(inputMessages) ? "array" : typeof inputMessages,
|
|
45
44
|
input_messages_len: Array.isArray(inputMessages) ? inputMessages.length : 0,
|
|
45
|
+
input_messages_has_content: hasNonEmptyInputMessages(inputMessages),
|
|
46
46
|
input_messages_roles: [],
|
|
47
47
|
input_messages_last_role: "",
|
|
48
48
|
input_messages_last_type: "",
|
|
@@ -78,6 +78,210 @@ function extractAssistantText(content) {
|
|
|
78
78
|
return normalizeContent(content);
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
+
function readCodexUserMessageFromRollout(sessionId, codexHome) {
|
|
82
|
+
const meta = {
|
|
83
|
+
codex_rollout_found: false,
|
|
84
|
+
codex_rollout_source: "",
|
|
85
|
+
codex_rollout_path: "",
|
|
86
|
+
codex_rollout_mtime_ms: 0,
|
|
87
|
+
codex_rollout_total: 0,
|
|
88
|
+
codex_rollout_user_message_count: 0,
|
|
89
|
+
codex_rollout_line_count: 0,
|
|
90
|
+
codex_rollout_error: "",
|
|
91
|
+
};
|
|
92
|
+
const rolloutPath = findCodexRolloutPath(sessionId, meta, codexHome);
|
|
93
|
+
if (!rolloutPath) {
|
|
94
|
+
return { userText: "", meta };
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
const content = fs.readFileSync(rolloutPath, "utf8");
|
|
98
|
+
const lines = content.split("\n").filter(Boolean);
|
|
99
|
+
meta.codex_rollout_line_count = lines.length;
|
|
100
|
+
let lastUser = "";
|
|
101
|
+
let lastTs = "";
|
|
102
|
+
let count = 0;
|
|
103
|
+
for (const line of lines) {
|
|
104
|
+
let record;
|
|
105
|
+
try {
|
|
106
|
+
record = JSON.parse(line);
|
|
107
|
+
} catch {
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (record?.type !== "event_msg") continue;
|
|
111
|
+
const payload = record?.payload;
|
|
112
|
+
if (payload?.type !== "user_message") continue;
|
|
113
|
+
const text = extractCodexUserMessage(payload);
|
|
114
|
+
if (text) {
|
|
115
|
+
lastUser = text;
|
|
116
|
+
lastTs = record?.timestamp || "";
|
|
117
|
+
}
|
|
118
|
+
count += 1;
|
|
119
|
+
}
|
|
120
|
+
meta.codex_rollout_user_message_count = count;
|
|
121
|
+
if (lastTs) {
|
|
122
|
+
meta.codex_rollout_last_user_ts = lastTs;
|
|
123
|
+
}
|
|
124
|
+
return { userText: lastUser, meta };
|
|
125
|
+
} catch (error) {
|
|
126
|
+
meta.codex_rollout_error = error?.message || "rollout_read_failed";
|
|
127
|
+
return { userText: "", meta };
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function extractCodexUserMessage(payload) {
|
|
132
|
+
if (!payload || typeof payload !== "object") return "";
|
|
133
|
+
const content =
|
|
134
|
+
payload.message ||
|
|
135
|
+
payload.text ||
|
|
136
|
+
payload.prompt ||
|
|
137
|
+
payload.input ||
|
|
138
|
+
payload.content;
|
|
139
|
+
return normalizeContent(content);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function findCodexRolloutPath(sessionId, meta, codexHome) {
|
|
143
|
+
if (!sessionId) {
|
|
144
|
+
meta.codex_rollout_error = "session_id_missing";
|
|
145
|
+
return "";
|
|
146
|
+
}
|
|
147
|
+
const baseHome = codexHome || path.join(os.homedir(), ".codex");
|
|
148
|
+
const sessionsDir = path.join(baseHome, "sessions");
|
|
149
|
+
meta.codex_sessions_dir = sessionsDir;
|
|
150
|
+
if (!fs.existsSync(sessionsDir)) {
|
|
151
|
+
meta.codex_rollout_error = "sessions_dir_missing";
|
|
152
|
+
return "";
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const files = collectRolloutFiles(sessionsDir);
|
|
156
|
+
meta.codex_rollout_total = files.length;
|
|
157
|
+
const byName = files.filter((item) => item.name.includes(sessionId));
|
|
158
|
+
if (byName.length > 0) {
|
|
159
|
+
const best = pickLatestFile(byName);
|
|
160
|
+
if (best) {
|
|
161
|
+
meta.codex_rollout_found = true;
|
|
162
|
+
meta.codex_rollout_source = "filename";
|
|
163
|
+
meta.codex_rollout_path = best.path;
|
|
164
|
+
meta.codex_rollout_mtime_ms = best.mtimeMs;
|
|
165
|
+
return best.path;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const byContent = findRolloutByContent(files, sessionId, meta);
|
|
170
|
+
if (byContent) {
|
|
171
|
+
meta.codex_rollout_found = true;
|
|
172
|
+
meta.codex_rollout_source = "content";
|
|
173
|
+
meta.codex_rollout_path = byContent.path;
|
|
174
|
+
meta.codex_rollout_mtime_ms = byContent.mtimeMs;
|
|
175
|
+
return byContent.path;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
meta.codex_rollout_error = meta.codex_rollout_error || "rollout_not_found";
|
|
179
|
+
return "";
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function collectRolloutFiles(rootDir) {
|
|
183
|
+
const results = [];
|
|
184
|
+
const stack = [rootDir];
|
|
185
|
+
while (stack.length > 0) {
|
|
186
|
+
const dir = stack.pop();
|
|
187
|
+
let entries;
|
|
188
|
+
try {
|
|
189
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
190
|
+
} catch {
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
for (const entry of entries) {
|
|
194
|
+
const fullPath = path.join(dir, entry.name);
|
|
195
|
+
if (entry.isDirectory()) {
|
|
196
|
+
stack.push(fullPath);
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
if (!entry.isFile()) continue;
|
|
200
|
+
if (!entry.name.startsWith("rollout-") || !entry.name.endsWith(".jsonl")) {
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
let stat;
|
|
204
|
+
try {
|
|
205
|
+
stat = fs.statSync(fullPath);
|
|
206
|
+
} catch {
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
results.push({
|
|
210
|
+
path: fullPath,
|
|
211
|
+
name: entry.name,
|
|
212
|
+
mtimeMs: stat.mtimeMs || 0,
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return results;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function pickLatestFile(files) {
|
|
220
|
+
if (!files || files.length === 0) return null;
|
|
221
|
+
let best = files[0];
|
|
222
|
+
for (const item of files) {
|
|
223
|
+
if (item.mtimeMs > best.mtimeMs) {
|
|
224
|
+
best = item;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return best;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function findRolloutByContent(files, sessionId, meta) {
|
|
231
|
+
if (!files || files.length === 0) {
|
|
232
|
+
meta.codex_rollout_error = "rollout_files_empty";
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
const sorted = [...files].sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
236
|
+
const limit = 30;
|
|
237
|
+
for (let i = 0; i < sorted.length && i < limit; i += 1) {
|
|
238
|
+
const file = sorted[i];
|
|
239
|
+
let content = "";
|
|
240
|
+
try {
|
|
241
|
+
content = fs.readFileSync(file.path, "utf8");
|
|
242
|
+
} catch {
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
if (content.includes(sessionId)) {
|
|
246
|
+
return file;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
meta.codex_rollout_error = "rollout_not_matched_in_recent_files";
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function resolveCodexHomeInfo() {
|
|
254
|
+
const defaultHome = path.join(os.homedir(), ".codex");
|
|
255
|
+
const defaultResolved = path.resolve(defaultHome);
|
|
256
|
+
const envHome = process.env.CODEX_HOME;
|
|
257
|
+
if (!envHome) {
|
|
258
|
+
return {
|
|
259
|
+
home: defaultResolved,
|
|
260
|
+
isDefault: true,
|
|
261
|
+
isSet: false,
|
|
262
|
+
meta: {
|
|
263
|
+
codex_home: defaultResolved,
|
|
264
|
+
codex_home_default: defaultResolved,
|
|
265
|
+
codex_home_is_default: true,
|
|
266
|
+
codex_home_set: false,
|
|
267
|
+
},
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
const resolved = path.resolve(envHome);
|
|
271
|
+
const isDefault = resolved === defaultResolved;
|
|
272
|
+
return {
|
|
273
|
+
home: resolved,
|
|
274
|
+
isDefault,
|
|
275
|
+
isSet: true,
|
|
276
|
+
meta: {
|
|
277
|
+
codex_home: resolved,
|
|
278
|
+
codex_home_default: defaultResolved,
|
|
279
|
+
codex_home_is_default: isDefault,
|
|
280
|
+
codex_home_set: true,
|
|
281
|
+
},
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
81
285
|
function extractTextDeep(value, depth = 0) {
|
|
82
286
|
if (depth > 6) return "";
|
|
83
287
|
if (value === null || value === undefined) return "";
|