oh-my-codex 0.8.13 → 0.8.15
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/dist/team/__tests__/runtime.test.js +188 -0
- package/dist/team/__tests__/runtime.test.js.map +1 -1
- package/dist/team/runtime.d.ts.map +1 -1
- package/dist/team/runtime.js +7 -1
- package/dist/team/runtime.js.map +1 -1
- package/dist/team/tmux-session.d.ts +1 -0
- package/dist/team/tmux-session.d.ts.map +1 -1
- package/dist/team/tmux-session.js +44 -0
- package/dist/team/tmux-session.js.map +1 -1
- package/package.json +1 -1
|
@@ -5,6 +5,7 @@ import { mkdtemp, rm, writeFile, readFile, mkdir, chmod } from 'fs/promises';
|
|
|
5
5
|
import { join } from 'path';
|
|
6
6
|
import { tmpdir } from 'os';
|
|
7
7
|
import { existsSync } from 'fs';
|
|
8
|
+
import { HUD_TMUX_TEAM_HEIGHT_LINES } from '../../hud/constants.js';
|
|
8
9
|
import { initTeamState, createTask, readTeamConfig, saveTeamConfig, listMailboxMessages, listDispatchRequests, updateWorkerHeartbeat, writeAtomic, readTask, readMonitorSnapshot, claimTask, transitionTaskStatus, } from '../state.js';
|
|
9
10
|
import { monitorTeam, shutdownTeam, resumeTeam, startTeam, assignTask, sendWorkerMessage, resolveWorkerLaunchArgsFromEnv, waitForWorkerStartupEvidence, waitForClaudeStartupEvidence, } from '../runtime.js';
|
|
10
11
|
import { resolveTeamLowComplexityDefaultModel } from '../model-contract.js';
|
|
@@ -732,6 +733,130 @@ process.on('SIGTERM', () => process.exit(0));
|
|
|
732
733
|
await rm(cwd, { recursive: true, force: true });
|
|
733
734
|
}
|
|
734
735
|
});
|
|
736
|
+
it('startTeam relaunch re-creates HUD pane and re-registers reconcile hooks after shutdown', async () => {
|
|
737
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-relaunch-hud-'));
|
|
738
|
+
const fakeBinDir = await mkdtemp(join(tmpdir(), 'omx-runtime-relaunch-hud-bin-'));
|
|
739
|
+
const tmuxLogPath = join(fakeBinDir, 'tmux.log');
|
|
740
|
+
const tmuxStubPath = join(fakeBinDir, 'tmux');
|
|
741
|
+
const geminiStubPath = join(fakeBinDir, 'gemini');
|
|
742
|
+
const previousPath = process.env.PATH;
|
|
743
|
+
const previousTmux = process.env.TMUX;
|
|
744
|
+
const previousTmuxPane = process.env.TMUX_PANE;
|
|
745
|
+
const previousLaunchMode = process.env.OMX_TEAM_WORKER_LAUNCH_MODE;
|
|
746
|
+
const previousWorkerCli = process.env.OMX_TEAM_WORKER_CLI;
|
|
747
|
+
let runtime = null;
|
|
748
|
+
try {
|
|
749
|
+
await writeFile(tmuxStubPath, `#!/bin/sh
|
|
750
|
+
set -eu
|
|
751
|
+
printf '%s\\n' "$*" >> "${tmuxLogPath}"
|
|
752
|
+
case "\${1:-}" in
|
|
753
|
+
-V)
|
|
754
|
+
echo "tmux 3.4"
|
|
755
|
+
exit 0
|
|
756
|
+
;;
|
|
757
|
+
display-message)
|
|
758
|
+
case "$*" in
|
|
759
|
+
*"#{window_width}"*)
|
|
760
|
+
echo "120"
|
|
761
|
+
;;
|
|
762
|
+
*)
|
|
763
|
+
echo "leader:0 %1"
|
|
764
|
+
;;
|
|
765
|
+
esac
|
|
766
|
+
exit 0
|
|
767
|
+
;;
|
|
768
|
+
list-panes)
|
|
769
|
+
case "$*" in
|
|
770
|
+
*"pane_current_command"* )
|
|
771
|
+
printf "%%1\\tnode\\t'codex'\\n"
|
|
772
|
+
;;
|
|
773
|
+
*"#{pane_dead} #{pane_pid}"*)
|
|
774
|
+
echo "1 999999"
|
|
775
|
+
;;
|
|
776
|
+
*"#{pane_pid}"*)
|
|
777
|
+
echo "999999"
|
|
778
|
+
;;
|
|
779
|
+
*)
|
|
780
|
+
exit 0
|
|
781
|
+
;;
|
|
782
|
+
esac
|
|
783
|
+
exit 0
|
|
784
|
+
;;
|
|
785
|
+
split-window)
|
|
786
|
+
case "$*" in
|
|
787
|
+
*" -h "*)
|
|
788
|
+
echo "%2"
|
|
789
|
+
;;
|
|
790
|
+
*)
|
|
791
|
+
echo "%3"
|
|
792
|
+
;;
|
|
793
|
+
esac
|
|
794
|
+
exit 0
|
|
795
|
+
;;
|
|
796
|
+
set-hook|run-shell|select-layout|set-window-option|select-pane|send-keys|kill-pane|kill-session)
|
|
797
|
+
exit 0
|
|
798
|
+
;;
|
|
799
|
+
*)
|
|
800
|
+
exit 0
|
|
801
|
+
;;
|
|
802
|
+
esac
|
|
803
|
+
`);
|
|
804
|
+
await chmod(tmuxStubPath, 0o755);
|
|
805
|
+
await writeFile(geminiStubPath, `#!/bin/sh
|
|
806
|
+
exit 0
|
|
807
|
+
`);
|
|
808
|
+
await chmod(geminiStubPath, 0o755);
|
|
809
|
+
process.env.PATH = `${fakeBinDir}:${previousPath ?? ''}`;
|
|
810
|
+
process.env.TMUX = 'leader-session,stub,0';
|
|
811
|
+
process.env.TMUX_PANE = '%1';
|
|
812
|
+
process.env.OMX_TEAM_WORKER_LAUNCH_MODE = 'interactive';
|
|
813
|
+
process.env.OMX_TEAM_WORKER_CLI = 'gemini';
|
|
814
|
+
runtime = await withoutTeamWorkerEnv(() => startTeam('team-rerun-hud', 'rerun hud restore', 'explore', 1, [{ subject: 'restore hud', description: 'restore hud', owner: 'worker-1' }], cwd));
|
|
815
|
+
assert.equal(runtime.config.hud_pane_id, '%3');
|
|
816
|
+
assert.ok(runtime.config.resize_hook_name);
|
|
817
|
+
await shutdownTeam(runtime.teamName, cwd, { force: true });
|
|
818
|
+
runtime = null;
|
|
819
|
+
runtime = await withoutTeamWorkerEnv(() => startTeam('team-rerun-hud', 'rerun hud restore', 'explore', 1, [{ subject: 'restore hud again', description: 'restore hud again', owner: 'worker-1' }], cwd));
|
|
820
|
+
assert.equal(runtime.config.hud_pane_id, '%3');
|
|
821
|
+
assert.ok(runtime.config.resize_hook_name);
|
|
822
|
+
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
823
|
+
const hudSplitRe = new RegExp(`split-window -v -l ${HUD_TMUX_TEAM_HEIGHT_LINES} -t %1 -d -P -F #\\{pane_id\\}`, 'g');
|
|
824
|
+
assert.equal(tmuxLog.match(hudSplitRe)?.length ?? 0, 3);
|
|
825
|
+
assert.equal(tmuxLog.match(/set-hook -t leader:0 client-resized\[\d+\]/g)?.length ?? 0, 2);
|
|
826
|
+
assert.equal(tmuxLog.match(/set-hook -t leader:0 client-attached\[\d+\]/g)?.length ?? 0, 2);
|
|
827
|
+
assert.equal(tmuxLog.match(/run-shell -b sleep \d+; tmux resize-pane -t %3 -y \d+ >/g)?.length ?? 0, 3);
|
|
828
|
+
assert.equal(tmuxLog.match(/run-shell tmux resize-pane -t %3 -y \d+ >/g)?.length ?? 0, 3);
|
|
829
|
+
assert.ok((tmuxLog.match(/select-layout -t leader:0 main-vertical/g)?.length ?? 0) >= 2);
|
|
830
|
+
assert.match(tmuxLog, /kill-pane -t %3/);
|
|
831
|
+
}
|
|
832
|
+
finally {
|
|
833
|
+
if (runtime) {
|
|
834
|
+
await shutdownTeam(runtime.teamName, cwd, { force: true }).catch(() => { });
|
|
835
|
+
}
|
|
836
|
+
if (typeof previousPath === 'string')
|
|
837
|
+
process.env.PATH = previousPath;
|
|
838
|
+
else
|
|
839
|
+
delete process.env.PATH;
|
|
840
|
+
if (typeof previousTmux === 'string')
|
|
841
|
+
process.env.TMUX = previousTmux;
|
|
842
|
+
else
|
|
843
|
+
delete process.env.TMUX;
|
|
844
|
+
if (typeof previousTmuxPane === 'string')
|
|
845
|
+
process.env.TMUX_PANE = previousTmuxPane;
|
|
846
|
+
else
|
|
847
|
+
delete process.env.TMUX_PANE;
|
|
848
|
+
if (typeof previousLaunchMode === 'string')
|
|
849
|
+
process.env.OMX_TEAM_WORKER_LAUNCH_MODE = previousLaunchMode;
|
|
850
|
+
else
|
|
851
|
+
delete process.env.OMX_TEAM_WORKER_LAUNCH_MODE;
|
|
852
|
+
if (typeof previousWorkerCli === 'string')
|
|
853
|
+
process.env.OMX_TEAM_WORKER_CLI = previousWorkerCli;
|
|
854
|
+
else
|
|
855
|
+
delete process.env.OMX_TEAM_WORKER_CLI;
|
|
856
|
+
await rm(cwd, { recursive: true, force: true });
|
|
857
|
+
await rm(fakeBinDir, { recursive: true, force: true });
|
|
858
|
+
}
|
|
859
|
+
});
|
|
735
860
|
it('startTeam routes detached worktree worker inbox and mailbox triggers through leader-root state references', async () => {
|
|
736
861
|
const repo = await initRepo();
|
|
737
862
|
const binDir = join(repo, 'bin');
|
|
@@ -1511,6 +1636,69 @@ esac
|
|
|
1511
1636
|
await rm(fakeBinDir, { recursive: true, force: true });
|
|
1512
1637
|
}
|
|
1513
1638
|
});
|
|
1639
|
+
it('shutdownTeam restores a standalone HUD pane after tearing down the team HUD', async () => {
|
|
1640
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-shutdown-restore-hud-'));
|
|
1641
|
+
const fakeBinDir = await mkdtemp(join(tmpdir(), 'omx-runtime-shutdown-restore-hud-bin-'));
|
|
1642
|
+
const tmuxLogPath = join(fakeBinDir, 'tmux.log');
|
|
1643
|
+
const tmuxStubPath = join(fakeBinDir, 'tmux');
|
|
1644
|
+
const previousPath = process.env.PATH;
|
|
1645
|
+
try {
|
|
1646
|
+
await writeFile(tmuxStubPath, `#!/bin/sh
|
|
1647
|
+
set -eu
|
|
1648
|
+
printf '%s\n' "$*" >> "${tmuxLogPath}"
|
|
1649
|
+
case "$1" in
|
|
1650
|
+
-V)
|
|
1651
|
+
echo "tmux 3.4"
|
|
1652
|
+
exit 0
|
|
1653
|
+
;;
|
|
1654
|
+
list-panes)
|
|
1655
|
+
exit 1
|
|
1656
|
+
;;
|
|
1657
|
+
split-window)
|
|
1658
|
+
printf '%%44\n'
|
|
1659
|
+
exit 0
|
|
1660
|
+
;;
|
|
1661
|
+
kill-pane|kill-session|select-pane)
|
|
1662
|
+
exit 0
|
|
1663
|
+
;;
|
|
1664
|
+
*)
|
|
1665
|
+
exit 0
|
|
1666
|
+
;;
|
|
1667
|
+
esac
|
|
1668
|
+
`);
|
|
1669
|
+
await chmod(tmuxStubPath, 0o755);
|
|
1670
|
+
process.env.PATH = `${fakeBinDir}:${previousPath ?? ''}`;
|
|
1671
|
+
await initTeamState('team-shutdown-restore-hud', 'shutdown restore hud test', 'executor', 2, cwd);
|
|
1672
|
+
const config = await readTeamConfig('team-shutdown-restore-hud', cwd);
|
|
1673
|
+
assert.ok(config);
|
|
1674
|
+
if (!config)
|
|
1675
|
+
return;
|
|
1676
|
+
config.tmux_session = 'leader:0';
|
|
1677
|
+
config.leader_pane_id = '%11';
|
|
1678
|
+
config.hud_pane_id = '%12';
|
|
1679
|
+
config.workers[0].pane_id = '%12';
|
|
1680
|
+
config.workers[1].pane_id = '%13';
|
|
1681
|
+
await saveTeamConfig(config, cwd);
|
|
1682
|
+
await shutdownTeam('team-shutdown-restore-hud', cwd, { force: true });
|
|
1683
|
+
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
1684
|
+
assert.doesNotMatch(tmuxLog, /kill-pane -t %11/);
|
|
1685
|
+
assert.match(tmuxLog, /kill-pane -t %12/);
|
|
1686
|
+
assert.match(tmuxLog, /kill-pane -t %13/);
|
|
1687
|
+
assert.match(tmuxLog, new RegExp(`split-window -v -l ${HUD_TMUX_TEAM_HEIGHT_LINES} -t %11 -d -P -F #\{pane_id\}`));
|
|
1688
|
+
assert.match(tmuxLog, /run-shell -b sleep \d+; tmux resize-pane -t %44 -y \d+ >/);
|
|
1689
|
+
assert.match(tmuxLog, /run-shell tmux resize-pane -t %44 -y \d+ >/);
|
|
1690
|
+
assert.match(tmuxLog, /hud --watch/);
|
|
1691
|
+
assert.match(tmuxLog, /select-pane -t %11/);
|
|
1692
|
+
}
|
|
1693
|
+
finally {
|
|
1694
|
+
if (typeof previousPath === 'string')
|
|
1695
|
+
process.env.PATH = previousPath;
|
|
1696
|
+
else
|
|
1697
|
+
delete process.env.PATH;
|
|
1698
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1699
|
+
await rm(fakeBinDir, { recursive: true, force: true });
|
|
1700
|
+
}
|
|
1701
|
+
});
|
|
1514
1702
|
it('shutdownTeam preserves leader exclusion while tearing down the hud pane', async () => {
|
|
1515
1703
|
const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-shutdown-exclusions-'));
|
|
1516
1704
|
const fakeBinDir = await mkdtemp(join(tmpdir(), 'omx-runtime-shutdown-exclusions-bin-'));
|