@usulpro/codex-bee 0.1.1 → 0.1.2
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 +22 -109
- package/dist/cli.js +119 -336
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -537,7 +537,6 @@ function renderContinuationPrompt(promptTemplate, capture) {
|
|
|
537
537
|
}
|
|
538
538
|
|
|
539
539
|
// src/auto-continue.ts
|
|
540
|
-
var DURATION_INPUT_PATTERN = /^(\d+(?:\.\d+)?)(ms|s|m|h)?$/i;
|
|
541
540
|
var PROMPT_PREVIEW_LENGTH = 96;
|
|
542
541
|
var SINGLE_LINE_WHITESPACE_PATTERN2 = /\s+/g;
|
|
543
542
|
var DEFAULT_CONTINUATION_PROMPT = "continue";
|
|
@@ -561,38 +560,6 @@ function describeContinuationSource(source) {
|
|
|
561
560
|
function toErrorMessage2(error) {
|
|
562
561
|
return error instanceof Error ? error.message : String(error);
|
|
563
562
|
}
|
|
564
|
-
function parseDurationInput(value, options) {
|
|
565
|
-
const trimmedValue = value.trim();
|
|
566
|
-
if (!trimmedValue || /^off$/i.test(trimmedValue)) {
|
|
567
|
-
if (options?.allowEmpty ?? true) {
|
|
568
|
-
return null;
|
|
569
|
-
}
|
|
570
|
-
throw new Error("Duration is required.");
|
|
571
|
-
}
|
|
572
|
-
const match = trimmedValue.match(DURATION_INPUT_PATTERN);
|
|
573
|
-
if (!match) {
|
|
574
|
-
throw new Error(`Unsupported duration value: ${value}`);
|
|
575
|
-
}
|
|
576
|
-
const amount = Number.parseFloat(match[1]);
|
|
577
|
-
const unit = (match[2] ?? "m").toLowerCase();
|
|
578
|
-
if (!Number.isFinite(amount) || amount <= 0) {
|
|
579
|
-
throw new Error(`Unsupported duration value: ${value}`);
|
|
580
|
-
}
|
|
581
|
-
const multiplier = {
|
|
582
|
-
h: 60 * 60 * 1e3,
|
|
583
|
-
m: 60 * 1e3,
|
|
584
|
-
ms: 1,
|
|
585
|
-
s: 1e3
|
|
586
|
-
}[unit];
|
|
587
|
-
if (multiplier === void 0) {
|
|
588
|
-
throw new Error(`Unsupported duration value: ${value}`);
|
|
589
|
-
}
|
|
590
|
-
const durationMs = Math.round(amount * multiplier);
|
|
591
|
-
if (durationMs <= 0) {
|
|
592
|
-
throw new Error(`Unsupported duration value: ${value}`);
|
|
593
|
-
}
|
|
594
|
-
return durationMs;
|
|
595
|
-
}
|
|
596
563
|
function formatDurationMs(value) {
|
|
597
564
|
if (value === null) {
|
|
598
565
|
return "off";
|
|
@@ -815,47 +782,17 @@ bee
|
|
|
815
782
|
Autonomous wrapper around Codex CLI.
|
|
816
783
|
|
|
817
784
|
Usage:
|
|
818
|
-
bee [options] <command> [commandArgs]
|
|
819
|
-
bee [options] -- <command> [commandArgs]
|
|
785
|
+
bee [bee options] <command> [commandArgs]
|
|
786
|
+
bee [bee options] -- <command> [commandArgs]
|
|
820
787
|
|
|
821
|
-
|
|
822
|
-
-h, --help
|
|
823
|
-
--version
|
|
824
|
-
--continue-once <prompt> Inject one follow-up prompt after the first Stop event
|
|
825
|
-
--continue-once-file <path> Load the one-shot follow-up prompt from a file
|
|
826
|
-
--continue-once-agent-file <path>
|
|
827
|
-
Load a one-shot bee-agent config from a file
|
|
828
|
-
--continue-loop <prompt> Inject the same follow-up prompt after multiple Stop events
|
|
829
|
-
--continue-loop-file <path> Load the loop follow-up prompt from a file
|
|
830
|
-
--continue-loop-agent-file <path>
|
|
831
|
-
Load a loop bee-agent config from a file
|
|
832
|
-
--max-continues <count> Guardrail for loop injections (default: 3)
|
|
833
|
-
--max-duration <duration> Session guardrail, for example 90m or 1.5h
|
|
834
|
-
--verify-command <command> Run a verification shell command after each Stop; continue only while it fails
|
|
835
|
-
--inject-delay-ms <milliseconds> Optional debug delay before prompt injection
|
|
788
|
+
Bee options:
|
|
789
|
+
-h, --help Show help
|
|
790
|
+
--version Show version
|
|
836
791
|
|
|
837
|
-
|
|
838
|
-
|
|
792
|
+
All remaining arguments are passed through to the wrapped command unchanged.
|
|
793
|
+
Continuation prompts, guardrails, and verification settings are configured from the in-session UI.
|
|
839
794
|
`;
|
|
840
|
-
|
|
841
|
-
if (currentSource) {
|
|
842
|
-
throw new Error(`Use only one ${modeLabel} continuation source.`);
|
|
843
|
-
}
|
|
844
|
-
return nextSource;
|
|
845
|
-
}
|
|
846
|
-
var WRAPPER_FLAGS_WITH_VALUE = /* @__PURE__ */ new Set([
|
|
847
|
-
"--continue-loop",
|
|
848
|
-
"--continue-loop-agent-file",
|
|
849
|
-
"--continue-loop-file",
|
|
850
|
-
"--continue-once",
|
|
851
|
-
"--continue-once-agent-file",
|
|
852
|
-
"--continue-once-file",
|
|
853
|
-
"--inject-delay-ms",
|
|
854
|
-
"--max-continues",
|
|
855
|
-
"--max-duration",
|
|
856
|
-
"--verify-command"
|
|
857
|
-
]);
|
|
858
|
-
var WRAPPER_FLAGS_WITHOUT_VALUE = /* @__PURE__ */ new Set([
|
|
795
|
+
var WRAPPER_FLAGS = /* @__PURE__ */ new Set([
|
|
859
796
|
"-h",
|
|
860
797
|
"--help",
|
|
861
798
|
"--version"
|
|
@@ -876,31 +813,18 @@ function parseCliArguments(rawArgs) {
|
|
|
876
813
|
);
|
|
877
814
|
}
|
|
878
815
|
const wrapperArgs = [];
|
|
879
|
-
let index = 0;
|
|
880
|
-
while (index < rawArgs.length) {
|
|
816
|
+
for (let index = 0; index < rawArgs.length; index += 1) {
|
|
881
817
|
const argument = rawArgs[index];
|
|
882
|
-
if (argument
|
|
883
|
-
return {
|
|
884
|
-
command: argument,
|
|
885
|
-
commandArgs: rawArgs.slice(index + 1),
|
|
886
|
-
wrapperArgs
|
|
887
|
-
};
|
|
888
|
-
}
|
|
889
|
-
if (WRAPPER_FLAGS_WITHOUT_VALUE.has(argument)) {
|
|
818
|
+
if (WRAPPER_FLAGS.has(argument)) {
|
|
890
819
|
wrapperArgs.push(argument);
|
|
891
|
-
index += 1;
|
|
892
820
|
continue;
|
|
893
821
|
}
|
|
894
|
-
if (
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
wrapperArgs
|
|
899
|
-
|
|
900
|
-
} else {
|
|
901
|
-
index += 1;
|
|
902
|
-
}
|
|
903
|
-
continue;
|
|
822
|
+
if (argument.startsWith("-")) {
|
|
823
|
+
return {
|
|
824
|
+
command: null,
|
|
825
|
+
commandArgs: [],
|
|
826
|
+
wrapperArgs: rawArgs
|
|
827
|
+
};
|
|
904
828
|
}
|
|
905
829
|
return {
|
|
906
830
|
command: argument,
|
|
@@ -916,16 +840,9 @@ function parseCliArguments(rawArgs) {
|
|
|
916
840
|
}
|
|
917
841
|
function parseWrapperOptions(rawArgs) {
|
|
918
842
|
const { command, commandArgs, wrapperArgs } = parseCliArguments(rawArgs);
|
|
919
|
-
let continueLoopSource = null;
|
|
920
|
-
let continueOnceSource = null;
|
|
921
|
-
let injectDelayMs = 0;
|
|
922
|
-
let maxContinues = 3;
|
|
923
|
-
let maxDurationMs = null;
|
|
924
843
|
let showHelp = false;
|
|
925
844
|
let showVersion = false;
|
|
926
|
-
|
|
927
|
-
for (let index = 0; index < wrapperArgs.length; index += 1) {
|
|
928
|
-
const argument = wrapperArgs[index];
|
|
845
|
+
for (const argument of wrapperArgs) {
|
|
929
846
|
if (argument === "-h" || argument === "--help") {
|
|
930
847
|
showHelp = true;
|
|
931
848
|
continue;
|
|
@@ -934,177 +851,18 @@ function parseWrapperOptions(rawArgs) {
|
|
|
934
851
|
showVersion = true;
|
|
935
852
|
continue;
|
|
936
853
|
}
|
|
937
|
-
if (argument
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
throw new Error("Missing value for --continue-once.");
|
|
941
|
-
}
|
|
942
|
-
continueOnceSource = setContinuationSource(
|
|
943
|
-
continueOnceSource,
|
|
944
|
-
{
|
|
945
|
-
kind: "inline",
|
|
946
|
-
template: prompt
|
|
947
|
-
},
|
|
948
|
-
"one-shot"
|
|
949
|
-
);
|
|
950
|
-
index += 1;
|
|
951
|
-
continue;
|
|
952
|
-
}
|
|
953
|
-
if (argument === "--continue-once-file") {
|
|
954
|
-
const path6 = wrapperArgs[index + 1];
|
|
955
|
-
if (!path6) {
|
|
956
|
-
throw new Error("Missing value for --continue-once-file.");
|
|
957
|
-
}
|
|
958
|
-
continueOnceSource = setContinuationSource(
|
|
959
|
-
continueOnceSource,
|
|
960
|
-
{
|
|
961
|
-
kind: "file",
|
|
962
|
-
path: path6
|
|
963
|
-
},
|
|
964
|
-
"one-shot"
|
|
965
|
-
);
|
|
966
|
-
index += 1;
|
|
967
|
-
continue;
|
|
968
|
-
}
|
|
969
|
-
if (argument === "--continue-once-agent-file") {
|
|
970
|
-
const path6 = wrapperArgs[index + 1];
|
|
971
|
-
if (!path6) {
|
|
972
|
-
throw new Error("Missing value for --continue-once-agent-file.");
|
|
973
|
-
}
|
|
974
|
-
continueOnceSource = setContinuationSource(
|
|
975
|
-
continueOnceSource,
|
|
976
|
-
{
|
|
977
|
-
kind: "bee-agent",
|
|
978
|
-
path: path6
|
|
979
|
-
},
|
|
980
|
-
"one-shot"
|
|
981
|
-
);
|
|
982
|
-
index += 1;
|
|
983
|
-
continue;
|
|
984
|
-
}
|
|
985
|
-
if (argument === "--continue-loop") {
|
|
986
|
-
const prompt = wrapperArgs[index + 1];
|
|
987
|
-
if (!prompt) {
|
|
988
|
-
throw new Error("Missing value for --continue-loop.");
|
|
989
|
-
}
|
|
990
|
-
continueLoopSource = setContinuationSource(
|
|
991
|
-
continueLoopSource,
|
|
992
|
-
{
|
|
993
|
-
kind: "inline",
|
|
994
|
-
template: prompt
|
|
995
|
-
},
|
|
996
|
-
"loop"
|
|
997
|
-
);
|
|
998
|
-
index += 1;
|
|
999
|
-
continue;
|
|
1000
|
-
}
|
|
1001
|
-
if (argument === "--continue-loop-file") {
|
|
1002
|
-
const path6 = wrapperArgs[index + 1];
|
|
1003
|
-
if (!path6) {
|
|
1004
|
-
throw new Error("Missing value for --continue-loop-file.");
|
|
1005
|
-
}
|
|
1006
|
-
continueLoopSource = setContinuationSource(
|
|
1007
|
-
continueLoopSource,
|
|
1008
|
-
{
|
|
1009
|
-
kind: "file",
|
|
1010
|
-
path: path6
|
|
1011
|
-
},
|
|
1012
|
-
"loop"
|
|
1013
|
-
);
|
|
1014
|
-
index += 1;
|
|
1015
|
-
continue;
|
|
1016
|
-
}
|
|
1017
|
-
if (argument === "--continue-loop-agent-file") {
|
|
1018
|
-
const path6 = wrapperArgs[index + 1];
|
|
1019
|
-
if (!path6) {
|
|
1020
|
-
throw new Error("Missing value for --continue-loop-agent-file.");
|
|
1021
|
-
}
|
|
1022
|
-
continueLoopSource = setContinuationSource(
|
|
1023
|
-
continueLoopSource,
|
|
1024
|
-
{
|
|
1025
|
-
kind: "bee-agent",
|
|
1026
|
-
path: path6
|
|
1027
|
-
},
|
|
1028
|
-
"loop"
|
|
854
|
+
if (argument.startsWith("-")) {
|
|
855
|
+
throw new Error(
|
|
856
|
+
`Unknown bee option: ${argument}. Continuation, guardrails, and verification are configured from the UI after launch.`
|
|
1029
857
|
);
|
|
1030
|
-
index += 1;
|
|
1031
|
-
continue;
|
|
1032
|
-
}
|
|
1033
|
-
if (argument === "--max-continues") {
|
|
1034
|
-
const value = wrapperArgs[index + 1];
|
|
1035
|
-
if (!value) {
|
|
1036
|
-
throw new Error("Missing value for --max-continues.");
|
|
1037
|
-
}
|
|
1038
|
-
const parsed = Number.parseInt(value, 10);
|
|
1039
|
-
if (!Number.isFinite(parsed) || parsed < 1) {
|
|
1040
|
-
throw new Error(`Invalid --max-continues value: ${value}`);
|
|
1041
|
-
}
|
|
1042
|
-
maxContinues = parsed;
|
|
1043
|
-
index += 1;
|
|
1044
|
-
continue;
|
|
1045
|
-
}
|
|
1046
|
-
if (argument === "--inject-delay-ms") {
|
|
1047
|
-
const value = wrapperArgs[index + 1];
|
|
1048
|
-
if (!value) {
|
|
1049
|
-
throw new Error("Missing value for --inject-delay-ms.");
|
|
1050
|
-
}
|
|
1051
|
-
const parsed = Number.parseInt(value, 10);
|
|
1052
|
-
if (!Number.isFinite(parsed) || parsed < 0) {
|
|
1053
|
-
throw new Error(`Invalid --inject-delay-ms value: ${value}`);
|
|
1054
|
-
}
|
|
1055
|
-
injectDelayMs = parsed;
|
|
1056
|
-
index += 1;
|
|
1057
|
-
continue;
|
|
1058
|
-
}
|
|
1059
|
-
if (argument === "--max-duration") {
|
|
1060
|
-
const value = wrapperArgs[index + 1];
|
|
1061
|
-
if (!value) {
|
|
1062
|
-
throw new Error("Missing value for --max-duration.");
|
|
1063
|
-
}
|
|
1064
|
-
maxDurationMs = parseDurationInput(value, {
|
|
1065
|
-
allowEmpty: false
|
|
1066
|
-
});
|
|
1067
|
-
index += 1;
|
|
1068
|
-
continue;
|
|
1069
|
-
}
|
|
1070
|
-
if (argument === "--verify-command") {
|
|
1071
|
-
const value = wrapperArgs[index + 1];
|
|
1072
|
-
if (!value) {
|
|
1073
|
-
throw new Error("Missing value for --verify-command.");
|
|
1074
|
-
}
|
|
1075
|
-
verificationCommand = value;
|
|
1076
|
-
index += 1;
|
|
1077
|
-
continue;
|
|
1078
858
|
}
|
|
1079
|
-
throw new Error(`
|
|
1080
|
-
}
|
|
1081
|
-
if (continueLoopSource && continueOnceSource) {
|
|
1082
|
-
throw new Error("Use either --continue-once or --continue-loop, not both.");
|
|
1083
|
-
}
|
|
1084
|
-
if (!continueLoopSource && maxContinues !== 3) {
|
|
1085
|
-
throw new Error("--max-continues requires --continue-loop.");
|
|
1086
|
-
}
|
|
1087
|
-
if (!continueLoopSource && !continueOnceSource && maxDurationMs !== null) {
|
|
1088
|
-
throw new Error("--max-duration requires --continue-once or --continue-loop.");
|
|
1089
|
-
}
|
|
1090
|
-
if (!continueLoopSource && !continueOnceSource && verificationCommand !== null) {
|
|
1091
|
-
throw new Error("--verify-command requires --continue-once or --continue-loop.");
|
|
1092
|
-
}
|
|
1093
|
-
const hasWrapperBehavior = continueLoopSource !== null || continueOnceSource !== null || injectDelayMs !== 0 || maxContinues !== 3 || maxDurationMs !== null || verificationCommand !== null;
|
|
1094
|
-
if (!showHelp && !showVersion && command === null && hasWrapperBehavior) {
|
|
1095
|
-
throw new Error("Missing command to wrap. Use `bee <command> [args]`.");
|
|
859
|
+
throw new Error(`Unexpected bee argument before the wrapped command: ${argument}`);
|
|
1096
860
|
}
|
|
1097
861
|
return {
|
|
1098
862
|
command,
|
|
1099
863
|
commandArgs,
|
|
1100
|
-
continueLoopSource,
|
|
1101
|
-
continueOnceSource,
|
|
1102
|
-
injectDelayMs,
|
|
1103
|
-
maxContinues,
|
|
1104
|
-
maxDurationMs,
|
|
1105
864
|
showHelp,
|
|
1106
|
-
showVersion
|
|
1107
|
-
verificationCommand
|
|
865
|
+
showVersion
|
|
1108
866
|
};
|
|
1109
867
|
}
|
|
1110
868
|
|
|
@@ -2133,6 +1891,11 @@ var PtyProxy = class {
|
|
|
2133
1891
|
this.#child = child;
|
|
2134
1892
|
child.stdout.on("data", (chunk) => {
|
|
2135
1893
|
const normalizedChunk = sanitizeTerminalOutput(chunk);
|
|
1894
|
+
this.#options.onOutputActivity?.({
|
|
1895
|
+
bytes: normalizedChunk.length,
|
|
1896
|
+
source: "stdout",
|
|
1897
|
+
timestamp: Date.now()
|
|
1898
|
+
});
|
|
2136
1899
|
if (this.#outputMuted) {
|
|
2137
1900
|
this.#bufferedStdoutChunks.push(Buffer.from(normalizedChunk));
|
|
2138
1901
|
return;
|
|
@@ -2141,6 +1904,11 @@ var PtyProxy = class {
|
|
|
2141
1904
|
});
|
|
2142
1905
|
child.stderr.on("data", (chunk) => {
|
|
2143
1906
|
const normalizedChunk = sanitizeTerminalOutput(chunk);
|
|
1907
|
+
this.#options.onOutputActivity?.({
|
|
1908
|
+
bytes: normalizedChunk.length,
|
|
1909
|
+
source: "stderr",
|
|
1910
|
+
timestamp: Date.now()
|
|
1911
|
+
});
|
|
2144
1912
|
if (this.#outputMuted) {
|
|
2145
1913
|
this.#bufferedStderrChunks.push(Buffer.from(normalizedChunk));
|
|
2146
1914
|
return;
|
|
@@ -2585,6 +2353,7 @@ function createDefaultRuntimeSnapshot() {
|
|
|
2585
2353
|
code: 0,
|
|
2586
2354
|
ok: true
|
|
2587
2355
|
},
|
|
2356
|
+
lastCodexOutputAt: null,
|
|
2588
2357
|
stopAt: null,
|
|
2589
2358
|
stopScript: "pnpm test",
|
|
2590
2359
|
totalTurns: null
|
|
@@ -3097,6 +2866,7 @@ import { Box as Box2, useApp, useInput, useStdin, useStdout } from "ink";
|
|
|
3097
2866
|
import { useEffect, useState } from "react";
|
|
3098
2867
|
|
|
3099
2868
|
// src/ui/core/view-model.ts
|
|
2869
|
+
var CODEX_ACTIVITY_WINDOW_MS = 1800;
|
|
3100
2870
|
var PROMPT_PREVIEW_LINE_LIMIT = 5;
|
|
3101
2871
|
var CODE_BLOCK_PATTERN = /```[\s\S]*?```/g;
|
|
3102
2872
|
function toSingleLine3(value) {
|
|
@@ -3141,6 +2911,24 @@ function formatHookStatus(snapshot) {
|
|
|
3141
2911
|
}
|
|
3142
2912
|
return `hook: ${snapshot.session.hookStatus.ok ? "\u2713" : "\u2717"} ${snapshot.session.hookStatus.code}`;
|
|
3143
2913
|
}
|
|
2914
|
+
function hasRecentCodexActivity(snapshot, now = Date.now()) {
|
|
2915
|
+
if (snapshot.connectionState === "disconnected") {
|
|
2916
|
+
return false;
|
|
2917
|
+
}
|
|
2918
|
+
if (snapshot.session.lastCodexOutputAt === null) {
|
|
2919
|
+
return false;
|
|
2920
|
+
}
|
|
2921
|
+
return now - snapshot.session.lastCodexOutputAt <= CODEX_ACTIVITY_WINDOW_MS;
|
|
2922
|
+
}
|
|
2923
|
+
function getDisplayedRunState(snapshot, now = Date.now()) {
|
|
2924
|
+
if (hasRecentCodexActivity(snapshot, now)) {
|
|
2925
|
+
return "running";
|
|
2926
|
+
}
|
|
2927
|
+
if (snapshot.runState === "running") {
|
|
2928
|
+
return "quiet";
|
|
2929
|
+
}
|
|
2930
|
+
return "idle";
|
|
2931
|
+
}
|
|
3144
2932
|
function formatSessionInfo(snapshot, now = Date.now()) {
|
|
3145
2933
|
const parts = [];
|
|
3146
2934
|
if (snapshot.session.currentTurn !== null && snapshot.session.totalTurns !== null) {
|
|
@@ -3390,13 +3178,15 @@ function renderLine(line, width) {
|
|
|
3390
3178
|
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
3391
3179
|
var MAX_CONTENT_WIDTH = 200;
|
|
3392
3180
|
var MIN_CONTENT_WIDTH = 80;
|
|
3181
|
+
var LOADER_TICK_MS = 160;
|
|
3393
3182
|
var PROMPT_BAR = "\u2503";
|
|
3394
3183
|
var TIMESTAMP_WIDTH = 8;
|
|
3395
|
-
var
|
|
3396
|
-
|
|
3397
|
-
["
|
|
3398
|
-
["
|
|
3399
|
-
["
|
|
3184
|
+
var LOADER_GLYPHS = ["\u25A0", "\u25A0", "\u25A0", "\u25A0"];
|
|
3185
|
+
var LOADER_COLOR_FRAMES = [
|
|
3186
|
+
["#4F3500", "#8A5B00", "#D89200", "#FFD35A"],
|
|
3187
|
+
["#8A5B00", "#D89200", "#FFD35A", "#D89200"],
|
|
3188
|
+
["#D89200", "#FFD35A", "#D89200", "#8A5B00"],
|
|
3189
|
+
["#FFD35A", "#D89200", "#8A5B00", "#4F3500"]
|
|
3400
3190
|
];
|
|
3401
3191
|
function createPalette(stylesEnabled) {
|
|
3402
3192
|
if (!stylesEnabled) {
|
|
@@ -3757,22 +3547,20 @@ function buildBodyLines(state, title, width, palette) {
|
|
|
3757
3547
|
...buildLogLines(state, width, palette)
|
|
3758
3548
|
];
|
|
3759
3549
|
}
|
|
3760
|
-
function buildLoaderSegments(palette, animationTick) {
|
|
3761
|
-
const
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
|
|
3550
|
+
function buildLoaderSegments(palette, animationTick, isActive) {
|
|
3551
|
+
const frame = LOADER_COLOR_FRAMES[animationTick % LOADER_COLOR_FRAMES.length] ?? LOADER_COLOR_FRAMES[0];
|
|
3552
|
+
return LOADER_GLYPHS.map((glyph, index) => ({
|
|
3553
|
+
color: isActive ? frame[index] ?? palette.primary : ["#A87400", "#BB8500", "#CF9600", "#E0A700"][index] ?? palette.primary,
|
|
3554
|
+
dimColor: !isActive,
|
|
3765
3555
|
text: glyph
|
|
3766
3556
|
}));
|
|
3767
3557
|
}
|
|
3768
3558
|
function measureSegmentsWidth(segments) {
|
|
3769
3559
|
return segments.reduce((total, segment) => total + measureText(segment.text), 0);
|
|
3770
3560
|
}
|
|
3771
|
-
function buildStatusTokens(state, palette, animationTick, width) {
|
|
3772
|
-
const
|
|
3773
|
-
|
|
3774
|
-
text: glyph
|
|
3775
|
-
}));
|
|
3561
|
+
function buildStatusTokens(state, palette, animationTick, now, width) {
|
|
3562
|
+
const displayedRunState = getDisplayedRunState(state.runtime, now);
|
|
3563
|
+
const loaderToken = buildLoaderSegments(palette, animationTick, displayedRunState === "running");
|
|
3776
3564
|
const connectionSpec = getConnectionTokenSpec(state.runtime.connectionState, palette);
|
|
3777
3565
|
const connectionToken = [
|
|
3778
3566
|
{
|
|
@@ -3802,8 +3590,8 @@ function buildStatusTokens(state, palette, animationTick, width) {
|
|
|
3802
3590
|
];
|
|
3803
3591
|
const runStateToken = [
|
|
3804
3592
|
{
|
|
3805
|
-
color:
|
|
3806
|
-
text:
|
|
3593
|
+
color: displayedRunState === "running" ? palette.primary : displayedRunState === "quiet" ? palette.warning : palette.muted,
|
|
3594
|
+
text: displayedRunState === "running" ? "Running" : displayedRunState === "quiet" ? "Quiet" : "Idle"
|
|
3807
3595
|
}
|
|
3808
3596
|
];
|
|
3809
3597
|
const hotkeyToken = [
|
|
@@ -3851,9 +3639,9 @@ function buildStatusTokens(state, palette, animationTick, width) {
|
|
|
3851
3639
|
}
|
|
3852
3640
|
return variants[variants.length - 1] ?? [];
|
|
3853
3641
|
}
|
|
3854
|
-
function buildStatusBarLine(state, palette, animationTick, width) {
|
|
3642
|
+
function buildStatusBarLine(state, palette, animationTick, now, width) {
|
|
3855
3643
|
const backgroundColor = palette.statusBottomBackground;
|
|
3856
|
-
const tokens = buildStatusTokens(state, palette, animationTick, width);
|
|
3644
|
+
const tokens = buildStatusTokens(state, palette, animationTick, now, width);
|
|
3857
3645
|
const segments = tokens.flatMap((token, index) => index === 0 ? token : [
|
|
3858
3646
|
{
|
|
3859
3647
|
text: " \u2022 "
|
|
@@ -4048,7 +3836,7 @@ function buildPromptFooterLines(state, width, palette, cursorVisible, animationT
|
|
|
4048
3836
|
color: palette.promptBorder ?? palette.muted
|
|
4049
3837
|
}));
|
|
4050
3838
|
lines.push(createSpacerLine("footer-gap", palette.footerBase));
|
|
4051
|
-
lines.push(buildStatusBarLine(state, palette, animationTick, width));
|
|
3839
|
+
lines.push(buildStatusBarLine(state, palette, animationTick, now, width));
|
|
4052
3840
|
lines.push(createSpacerLine("workspace-gap", palette.footerBase));
|
|
4053
3841
|
lines.push(createTextLine("workspace", formatWorkspaceLine(state.runtime.workspace), {
|
|
4054
3842
|
backgroundColor: palette.footerMutedBackground,
|
|
@@ -4115,7 +3903,7 @@ function InkControlApp({
|
|
|
4115
3903
|
const [isPinnedToBottom, setIsPinnedToBottom] = useState(true);
|
|
4116
3904
|
const [scrollOffset, setScrollOffset] = useState(0);
|
|
4117
3905
|
const [clockNow, setClockNow] = useState(() => Date.now());
|
|
4118
|
-
const animationTick =
|
|
3906
|
+
const animationTick = Math.floor(clockNow / LOADER_TICK_MS);
|
|
4119
3907
|
const cursorVisible = true;
|
|
4120
3908
|
const [viewport] = useState(() => ({
|
|
4121
3909
|
columns: stdout.columns ?? 100,
|
|
@@ -4141,17 +3929,14 @@ function InkControlApp({
|
|
|
4141
3929
|
});
|
|
4142
3930
|
}, [allowQuit, controller, exit, state.exitRequested]);
|
|
4143
3931
|
useEffect(() => {
|
|
4144
|
-
if (state.runtime.session.stopAt === null) {
|
|
4145
|
-
return;
|
|
4146
|
-
}
|
|
4147
3932
|
setClockNow(Date.now());
|
|
4148
3933
|
const intervalId = setInterval(() => {
|
|
4149
3934
|
setClockNow(Date.now());
|
|
4150
|
-
},
|
|
3935
|
+
}, LOADER_TICK_MS);
|
|
4151
3936
|
return () => {
|
|
4152
3937
|
clearInterval(intervalId);
|
|
4153
3938
|
};
|
|
4154
|
-
}, [
|
|
3939
|
+
}, []);
|
|
4155
3940
|
const palette = createPalette(stdout.isTTY !== false && !process.env.NO_COLOR && process.env.TERM !== "dumb");
|
|
4156
3941
|
const contentWidth = Math.min(Math.max(viewport.columns, MIN_CONTENT_WIDTH), MAX_CONTENT_WIDTH);
|
|
4157
3942
|
const renderWidth = viewport.columns < MIN_CONTENT_WIDTH ? viewport.columns : contentWidth;
|
|
@@ -4455,6 +4240,7 @@ import { execFile } from "child_process";
|
|
|
4455
4240
|
import process7 from "process";
|
|
4456
4241
|
import { promisify } from "util";
|
|
4457
4242
|
var execFileAsync = promisify(execFile);
|
|
4243
|
+
var OUTPUT_ACTIVITY_NOTIFY_INTERVAL_MS = 250;
|
|
4458
4244
|
var MAX_EVENT_LINES = 8;
|
|
4459
4245
|
function cloneLogEntry(entry) {
|
|
4460
4246
|
return {
|
|
@@ -4533,6 +4319,7 @@ function createInitialSnapshot(options) {
|
|
|
4533
4319
|
session: {
|
|
4534
4320
|
currentTurn: 0,
|
|
4535
4321
|
hookStatus: null,
|
|
4322
|
+
lastCodexOutputAt: null,
|
|
4536
4323
|
stopAt: getStopAt(continuationSnapshot),
|
|
4537
4324
|
stopScript: options.initialVerificationCommand ?? null,
|
|
4538
4325
|
totalTurns: toUiTotalTurns(continuationSnapshot.maxContinues)
|
|
@@ -4549,6 +4336,13 @@ function getNextSleepTimestamp(hours, minutes, now = Date.now()) {
|
|
|
4549
4336
|
}
|
|
4550
4337
|
return nextTarget.getTime();
|
|
4551
4338
|
}
|
|
4339
|
+
function formatTimeLeftSuffix(stopAt) {
|
|
4340
|
+
const label = formatTimeLeft(stopAt);
|
|
4341
|
+
if (!label) {
|
|
4342
|
+
return "";
|
|
4343
|
+
}
|
|
4344
|
+
return ` (${label.replace(/^time left:\s*/u, "")} left)`;
|
|
4345
|
+
}
|
|
4552
4346
|
function createLogEntry(id, kind, text, timestamp) {
|
|
4553
4347
|
return {
|
|
4554
4348
|
id,
|
|
@@ -4664,6 +4458,7 @@ var LiveUiRuntimeHost = class {
|
|
|
4664
4458
|
#listeners = /* @__PURE__ */ new Set();
|
|
4665
4459
|
#recentEvents = [];
|
|
4666
4460
|
#entryCounter = 0;
|
|
4461
|
+
#lastOutputActivityNotificationAt = 0;
|
|
4667
4462
|
#pendingPrompts = [];
|
|
4668
4463
|
#snapshot;
|
|
4669
4464
|
#verificationCommand;
|
|
@@ -4695,6 +4490,20 @@ var LiveUiRuntimeHost = class {
|
|
|
4695
4490
|
this.#pushEventLine("Codex session disconnected.");
|
|
4696
4491
|
this.#notify();
|
|
4697
4492
|
}
|
|
4493
|
+
recordPtyOutputActivity(timestamp = Date.now()) {
|
|
4494
|
+
this.#snapshot = {
|
|
4495
|
+
...this.#snapshot,
|
|
4496
|
+
session: {
|
|
4497
|
+
...this.#snapshot.session,
|
|
4498
|
+
lastCodexOutputAt: timestamp
|
|
4499
|
+
}
|
|
4500
|
+
};
|
|
4501
|
+
if (timestamp - this.#lastOutputActivityNotificationAt < OUTPUT_ACTIVITY_NOTIFY_INTERVAL_MS) {
|
|
4502
|
+
return;
|
|
4503
|
+
}
|
|
4504
|
+
this.#lastOutputActivityNotificationAt = timestamp;
|
|
4505
|
+
this.#notify();
|
|
4506
|
+
}
|
|
4698
4507
|
recordAuxiliaryHookEvent(capture) {
|
|
4699
4508
|
const eventName = capture.payload?.hook_event_name ?? null;
|
|
4700
4509
|
if (eventName === "SessionStart") {
|
|
@@ -4719,9 +4528,11 @@ var LiveUiRuntimeHost = class {
|
|
|
4719
4528
|
runState: "running",
|
|
4720
4529
|
session: {
|
|
4721
4530
|
...this.#snapshot.session,
|
|
4722
|
-
currentTurn: this.#snapshot.logs.length
|
|
4531
|
+
currentTurn: this.#snapshot.logs.length,
|
|
4532
|
+
lastCodexOutputAt: submittedAt
|
|
4723
4533
|
}
|
|
4724
4534
|
};
|
|
4535
|
+
this.#lastOutputActivityNotificationAt = submittedAt;
|
|
4725
4536
|
this.#pushEventLine(
|
|
4726
4537
|
promptText ? `UserPromptSubmit received. Codex turn is running: ${formatCodexEcho(promptText)}` : "UserPromptSubmit received. Codex turn is running."
|
|
4727
4538
|
);
|
|
@@ -4763,9 +4574,11 @@ var LiveUiRuntimeHost = class {
|
|
|
4763
4574
|
maxDurationMs: command.minutes * 60 * 1e3,
|
|
4764
4575
|
prompt: currentContinuation.promptText
|
|
4765
4576
|
});
|
|
4577
|
+
const stopAt = getStopAt(updatedSnapshot);
|
|
4578
|
+
const timeLeftSuffix = formatTimeLeftSuffix(stopAt);
|
|
4766
4579
|
this.#syncContinuationState(updatedSnapshot);
|
|
4767
|
-
this.#pushEventLine(`Updated the session duration to ${command.minutes} minute(s).`);
|
|
4768
|
-
return this.#createUpdate(`Set the live session duration to ${command.minutes} minute(s).`);
|
|
4580
|
+
this.#pushEventLine(`Updated the session duration to ${command.minutes} minute(s)${timeLeftSuffix}.`);
|
|
4581
|
+
return this.#createUpdate(`Set the live session duration to ${command.minutes} minute(s)${timeLeftSuffix}.`);
|
|
4769
4582
|
}
|
|
4770
4583
|
case "quit":
|
|
4771
4584
|
return this.#createUpdate("Exiting the current codex-bee session.", {
|
|
@@ -4780,9 +4593,10 @@ var LiveUiRuntimeHost = class {
|
|
|
4780
4593
|
maxDurationMs: Math.max(stopAt - Date.now(), 1e3),
|
|
4781
4594
|
prompt: currentContinuation.promptText
|
|
4782
4595
|
});
|
|
4596
|
+
const timeLeftSuffix = formatTimeLeftSuffix(getStopAt(updatedSnapshot));
|
|
4783
4597
|
this.#syncContinuationState(updatedSnapshot);
|
|
4784
|
-
this.#pushEventLine(`Set the absolute stop time to ${command.time}.`);
|
|
4785
|
-
return this.#createUpdate(`Set the live stop time to ${command.time}.`);
|
|
4598
|
+
this.#pushEventLine(`Set the absolute stop time to ${command.time}${timeLeftSuffix}.`);
|
|
4599
|
+
return this.#createUpdate(`Set the live stop time to ${command.time}${timeLeftSuffix}.`);
|
|
4786
4600
|
}
|
|
4787
4601
|
case "steps-count": {
|
|
4788
4602
|
const currentContinuation = this.#continuation.getSnapshot();
|
|
@@ -4924,6 +4738,7 @@ var LiveUiRuntimeHost = class {
|
|
|
4924
4738
|
...this.#snapshot.session,
|
|
4925
4739
|
currentTurn: nextLogs.length,
|
|
4926
4740
|
hookStatus: hookSummary.hookStatus,
|
|
4741
|
+
lastCodexOutputAt: completedAt,
|
|
4927
4742
|
stopAt: getStopAt(continuationSnapshot),
|
|
4928
4743
|
stopScript: this.#verificationCommand,
|
|
4929
4744
|
totalTurns: toUiTotalTurns(continuationSnapshot.maxContinues)
|
|
@@ -5346,9 +5161,8 @@ async function main() {
|
|
|
5346
5161
|
}
|
|
5347
5162
|
const runId = randomUUID();
|
|
5348
5163
|
const runStartedAt = Date.now();
|
|
5349
|
-
const hasContinuation = Boolean(options.continueLoopSource || options.continueOnceSource);
|
|
5350
5164
|
const executionMode = resolveExecutionMode({
|
|
5351
|
-
hasContinuation,
|
|
5165
|
+
hasContinuation: false,
|
|
5352
5166
|
stdinIsTTY: process8.stdin.isTTY,
|
|
5353
5167
|
stdoutIsTTY: process8.stdout.isTTY
|
|
5354
5168
|
});
|
|
@@ -5387,28 +5201,7 @@ async function main() {
|
|
|
5387
5201
|
configPath: hookSetup.activeConfig?.path ?? hookSetup.globalPath
|
|
5388
5202
|
});
|
|
5389
5203
|
}
|
|
5390
|
-
|
|
5391
|
-
enabled: true,
|
|
5392
|
-
maxContinues: options.maxContinues,
|
|
5393
|
-
maxDurationMs: options.maxDurationMs,
|
|
5394
|
-
promptSource: options.continueLoopSource
|
|
5395
|
-
} : options.continueOnceSource ? {
|
|
5396
|
-
enabled: true,
|
|
5397
|
-
maxContinues: 1,
|
|
5398
|
-
maxDurationMs: options.maxDurationMs,
|
|
5399
|
-
promptSource: options.continueOnceSource
|
|
5400
|
-
} : null;
|
|
5401
|
-
if (initialContinuation) {
|
|
5402
|
-
if (isBeeAgentSource(initialContinuation.promptSource)) {
|
|
5403
|
-
readBeeAgentConfig(initialContinuation.promptSource, {
|
|
5404
|
-
cwd: process8.cwd()
|
|
5405
|
-
});
|
|
5406
|
-
} else {
|
|
5407
|
-
readContinuationTemplate(initialContinuation.promptSource, {
|
|
5408
|
-
cwd: process8.cwd()
|
|
5409
|
-
});
|
|
5410
|
-
}
|
|
5411
|
-
}
|
|
5204
|
+
let runtimeHost = null;
|
|
5412
5205
|
const proxy = new PtyProxy({
|
|
5413
5206
|
command: wrappedCommand,
|
|
5414
5207
|
commandArgs: options.commandArgs,
|
|
@@ -5416,20 +5209,21 @@ async function main() {
|
|
|
5416
5209
|
...process8.env,
|
|
5417
5210
|
CODEX_BEE_CAPTURE_ROOT: process8.cwd(),
|
|
5418
5211
|
CODEX_BEE_RUN_ID: runId
|
|
5212
|
+
},
|
|
5213
|
+
onOutputActivity: ({ timestamp }) => {
|
|
5214
|
+
runtimeHost?.recordPtyOutputActivity(timestamp);
|
|
5419
5215
|
}
|
|
5420
5216
|
});
|
|
5421
5217
|
const continuation = new ContinuationController({
|
|
5422
|
-
cwd: process8.cwd()
|
|
5423
|
-
initial: initialContinuation ?? void 0
|
|
5218
|
+
cwd: process8.cwd()
|
|
5424
5219
|
});
|
|
5425
|
-
|
|
5220
|
+
runtimeHost = new LiveUiRuntimeHost({
|
|
5426
5221
|
beeVersion: CLI_VERSION,
|
|
5427
5222
|
codexCommand: wrappedCommand,
|
|
5428
5223
|
continuation,
|
|
5429
5224
|
cwd: process8.cwd(),
|
|
5430
5225
|
initialAnnouncementMessage: startupAnnouncement,
|
|
5431
5226
|
initialConnectionState,
|
|
5432
|
-
initialVerificationCommand: options.verificationCommand,
|
|
5433
5227
|
promptProxy: proxy
|
|
5434
5228
|
});
|
|
5435
5229
|
const controller = new UiController(runtimeHost);
|
|
@@ -5454,17 +5248,6 @@ async function main() {
|
|
|
5454
5248
|
});
|
|
5455
5249
|
await proxy.start();
|
|
5456
5250
|
proxy.setInputInterceptor((chunk) => overlay.handleInput(chunk));
|
|
5457
|
-
if (initialContinuation) {
|
|
5458
|
-
if (initialContinuation.maxContinues > 1) {
|
|
5459
|
-
console.error(
|
|
5460
|
-
`[codex-bee] Auto-continue loop armed for up to ${initialContinuation.maxContinues} injections with ${continuation.getSnapshot().promptSourceLabel}.`
|
|
5461
|
-
);
|
|
5462
|
-
} else {
|
|
5463
|
-
console.error(
|
|
5464
|
-
`[codex-bee] One-shot continuation armed with ${continuation.getSnapshot().promptSourceLabel}.`
|
|
5465
|
-
);
|
|
5466
|
-
}
|
|
5467
|
-
}
|
|
5468
5251
|
if (initialConnectionState === "ready") {
|
|
5469
5252
|
void runHookEventWatcher({
|
|
5470
5253
|
cwd: process8.cwd(),
|
|
@@ -5495,7 +5278,7 @@ async function main() {
|
|
|
5495
5278
|
continuation,
|
|
5496
5279
|
cwd: process8.cwd(),
|
|
5497
5280
|
getVerificationCommand: () => runtimeHost.getVerificationCommand(),
|
|
5498
|
-
injectDelayMs:
|
|
5281
|
+
injectDelayMs: 0,
|
|
5499
5282
|
logger,
|
|
5500
5283
|
onPromptInjected: (event) => {
|
|
5501
5284
|
runtimeHost.recordContinuationPromptInjected(event);
|
|
@@ -5508,7 +5291,7 @@ async function main() {
|
|
|
5508
5291
|
runStartedAt,
|
|
5509
5292
|
runVerificationCommand,
|
|
5510
5293
|
signal: watcherAbortController.signal,
|
|
5511
|
-
verificationCommand:
|
|
5294
|
+
verificationCommand: null
|
|
5512
5295
|
}).catch((error) => {
|
|
5513
5296
|
const message = error instanceof Error ? error.message : String(error);
|
|
5514
5297
|
logger.log("continuation_watcher_stopped", {
|