mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 06:00:43 +00:00
chore(gateway): track watch tmux cwd
This commit is contained in:
@@ -174,7 +174,7 @@ Telegraph style. Root rules only. Read scoped `AGENTS.md` before subtree work.
|
||||
- Before simulator/emulator testing, check real iOS/Android devices.
|
||||
- "restart iOS/Android apps" = rebuild/reinstall/relaunch, not kill/launch.
|
||||
- SwiftUI: Observation (`@Observable`, `@Bindable`) over new `ObservableObject`.
|
||||
- Mac gateway: use app or `openclaw gateway restart/status --deep`; no ad-hoc tmux gateway. Logs: `./scripts/clawlog.sh`.
|
||||
- Mac gateway: dev watch = `pnpm gateway:watch` (tmux `openclaw-gateway-watch-main`, auto-attach). Noninteractive: `OPENCLAW_GATEWAY_WATCH_ATTACH=0 pnpm gateway:watch`; attach/stop: `tmux attach -t openclaw-gateway-watch-main` / `tmux kill-session -t openclaw-gateway-watch-main`. Managed installs: `openclaw gateway restart/status --deep`. No launchd/ad-hoc tmux. Logs: `./scripts/clawlog.sh`.
|
||||
- Version bump touches: `package.json`, `apps/android/app/build.gradle.kts`, `apps/ios/version.json` + `pnpm ios:version:sync`, macOS `Info.plist`, `docs/install/updating.md`. Appcast only for Sparkle release.
|
||||
- Mobile LAN pairing: plaintext `ws://` loopback-only. Private-network `ws://` needs `OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1`; Tailscale/public use `wss://` or tunnel.
|
||||
- A2UI hash `src/canvas-host/a2ui/.bundle.hash`: generated; ignore unless running `pnpm canvas:a2ui:bundle`; commit separately.
|
||||
|
||||
@@ -8,6 +8,8 @@ const TMUX_ATTACH_DISABLE_VALUES = new Set(["0", "false", "no", "off"]);
|
||||
const TMUX_ATTACH_FORCE_VALUES = new Set(["1", "true", "yes", "on"]);
|
||||
const DEFAULT_PROFILE_NAME = "main";
|
||||
const RAW_WATCH_SCRIPT = "scripts/watch-node.mjs";
|
||||
const TMUX_CWD_ENV_KEY = "OPENCLAW_GATEWAY_WATCH_CWD";
|
||||
const TMUX_CWD_OPTION_KEY = "@openclaw.gateway_watch.cwd";
|
||||
const TMUX_CHILD_ENV_KEYS = [
|
||||
"NODE_OPTIONS",
|
||||
"OPENCLAW_CONFIG_PATH",
|
||||
@@ -137,6 +139,20 @@ const attachTmux = ({ env, sessionName, spawnSyncImpl }) => {
|
||||
return runTmux(spawnSyncImpl, args, { stdio: "inherit" });
|
||||
};
|
||||
|
||||
const setTmuxSessionMetadata = ({ cwd, sessionName, spawnSyncImpl, stderr }) => {
|
||||
const updates = [
|
||||
["set-option", "-q", "-t", sessionName, TMUX_CWD_OPTION_KEY, cwd],
|
||||
["set-environment", "-t", sessionName, TMUX_CWD_ENV_KEY, cwd],
|
||||
];
|
||||
for (const args of updates) {
|
||||
const result = runTmux(spawnSyncImpl, args);
|
||||
if (result.error || result.status !== 0) {
|
||||
log(stderr, `warning: failed to update tmux session metadata: ${getTmuxErrorText(result)}`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const runGatewayWatchTmuxMain = (params = {}) => {
|
||||
const deps = {
|
||||
args: params.args ?? process.argv.slice(2),
|
||||
@@ -219,6 +235,13 @@ export const runGatewayWatchTmuxMain = (params = {}) => {
|
||||
return result.status || 1;
|
||||
}
|
||||
|
||||
setTmuxSessionMetadata({
|
||||
cwd: deps.cwd,
|
||||
sessionName,
|
||||
spawnSyncImpl: deps.spawnSync,
|
||||
stderr: deps.stderr,
|
||||
});
|
||||
|
||||
log(deps.stderr, `gateway:watch ${action} in tmux session ${sessionName}`);
|
||||
if (
|
||||
shouldAttachTmux({
|
||||
@@ -241,6 +264,7 @@ export const runGatewayWatchTmuxMain = (params = {}) => {
|
||||
return 0;
|
||||
}
|
||||
deps.stdout.write(`Attach: tmux attach -t ${sessionName}\n`);
|
||||
deps.stdout.write(`Cwd: tmux show-options -v -t ${sessionName} ${TMUX_CWD_OPTION_KEY}\n`);
|
||||
deps.stdout.write("Restart: rerun the same pnpm gateway:watch command\n");
|
||||
deps.stdout.write(`Stop: tmux kill-session -t ${sessionName}\n`);
|
||||
return 0;
|
||||
|
||||
@@ -68,6 +68,8 @@ describe("gateway-watch tmux wrapper", () => {
|
||||
const spawnSync = vi
|
||||
.fn()
|
||||
.mockReturnValueOnce({ status: 1, stdout: "", stderr: "" })
|
||||
.mockReturnValueOnce({ status: 0, stdout: "", stderr: "" })
|
||||
.mockReturnValueOnce({ status: 0, stdout: "", stderr: "" })
|
||||
.mockReturnValueOnce({ status: 0, stdout: "", stderr: "" });
|
||||
|
||||
const code = runGatewayWatchTmuxMain({
|
||||
@@ -101,10 +103,38 @@ describe("gateway-watch tmux wrapper", () => {
|
||||
],
|
||||
expect.objectContaining({ encoding: "utf8" }),
|
||||
);
|
||||
expect(spawnSync).toHaveBeenNthCalledWith(
|
||||
3,
|
||||
"tmux",
|
||||
[
|
||||
"set-option",
|
||||
"-q",
|
||||
"-t",
|
||||
"openclaw-gateway-watch-main",
|
||||
"@openclaw.gateway_watch.cwd",
|
||||
"/repo",
|
||||
],
|
||||
expect.objectContaining({ encoding: "utf8" }),
|
||||
);
|
||||
expect(spawnSync).toHaveBeenNthCalledWith(
|
||||
4,
|
||||
"tmux",
|
||||
[
|
||||
"set-environment",
|
||||
"-t",
|
||||
"openclaw-gateway-watch-main",
|
||||
"OPENCLAW_GATEWAY_WATCH_CWD",
|
||||
"/repo",
|
||||
],
|
||||
expect.objectContaining({ encoding: "utf8" }),
|
||||
);
|
||||
expect(stderr.chunks.join("")).toContain(
|
||||
"gateway:watch started in tmux session openclaw-gateway-watch-main",
|
||||
);
|
||||
expect(stdout.chunks.join("")).toContain("tmux attach -t openclaw-gateway-watch-main");
|
||||
expect(stdout.chunks.join("")).toContain(
|
||||
"tmux show-options -v -t openclaw-gateway-watch-main @openclaw.gateway_watch.cwd",
|
||||
);
|
||||
});
|
||||
|
||||
it("auto-attaches in an interactive terminal after creating a session", () => {
|
||||
@@ -114,6 +144,8 @@ describe("gateway-watch tmux wrapper", () => {
|
||||
.fn()
|
||||
.mockReturnValueOnce({ status: 1, stdout: "", stderr: "" })
|
||||
.mockReturnValueOnce({ status: 0, stdout: "", stderr: "" })
|
||||
.mockReturnValueOnce({ status: 0, stdout: "", stderr: "" })
|
||||
.mockReturnValueOnce({ status: 0, stdout: "", stderr: "" })
|
||||
.mockReturnValueOnce({ status: 0, stdout: "", stderr: "" });
|
||||
|
||||
const code = runGatewayWatchTmuxMain({
|
||||
@@ -130,7 +162,7 @@ describe("gateway-watch tmux wrapper", () => {
|
||||
|
||||
expect(code).toBe(0);
|
||||
expect(spawnSync).toHaveBeenNthCalledWith(
|
||||
3,
|
||||
5,
|
||||
"tmux",
|
||||
["attach-session", "-t", "openclaw-gateway-watch-main"],
|
||||
expect.objectContaining({ stdio: "inherit" }),
|
||||
@@ -145,6 +177,8 @@ describe("gateway-watch tmux wrapper", () => {
|
||||
.fn()
|
||||
.mockReturnValueOnce({ status: 0, stdout: "", stderr: "" })
|
||||
.mockReturnValueOnce({ status: 0, stdout: "", stderr: "" })
|
||||
.mockReturnValueOnce({ status: 0, stdout: "", stderr: "" })
|
||||
.mockReturnValueOnce({ status: 0, stdout: "", stderr: "" })
|
||||
.mockReturnValueOnce({ status: 0, stdout: "", stderr: "" });
|
||||
|
||||
const code = runGatewayWatchTmuxMain({
|
||||
@@ -161,7 +195,7 @@ describe("gateway-watch tmux wrapper", () => {
|
||||
|
||||
expect(code).toBe(0);
|
||||
expect(spawnSync).toHaveBeenNthCalledWith(
|
||||
3,
|
||||
5,
|
||||
"tmux",
|
||||
["switch-client", "-t", "openclaw-gateway-watch-main"],
|
||||
expect.objectContaining({ stdio: "inherit" }),
|
||||
@@ -174,6 +208,8 @@ describe("gateway-watch tmux wrapper", () => {
|
||||
const spawnSync = vi
|
||||
.fn()
|
||||
.mockReturnValueOnce({ status: 1, stdout: "", stderr: "" })
|
||||
.mockReturnValueOnce({ status: 0, stdout: "", stderr: "" })
|
||||
.mockReturnValueOnce({ status: 0, stdout: "", stderr: "" })
|
||||
.mockReturnValueOnce({ status: 0, stdout: "", stderr: "" });
|
||||
|
||||
const code = runGatewayWatchTmuxMain({
|
||||
@@ -189,7 +225,7 @@ describe("gateway-watch tmux wrapper", () => {
|
||||
});
|
||||
|
||||
expect(code).toBe(0);
|
||||
expect(spawnSync).toHaveBeenCalledTimes(2);
|
||||
expect(spawnSync).toHaveBeenCalledTimes(4);
|
||||
expect(stdout.chunks.join("")).toContain("tmux attach -t openclaw-gateway-watch-main");
|
||||
});
|
||||
|
||||
@@ -199,6 +235,8 @@ describe("gateway-watch tmux wrapper", () => {
|
||||
const spawnSync = vi
|
||||
.fn()
|
||||
.mockReturnValueOnce({ status: 0, stdout: "", stderr: "" })
|
||||
.mockReturnValueOnce({ status: 0, stdout: "", stderr: "" })
|
||||
.mockReturnValueOnce({ status: 0, stdout: "", stderr: "" })
|
||||
.mockReturnValueOnce({ status: 0, stdout: "", stderr: "" });
|
||||
|
||||
const code = runGatewayWatchTmuxMain({
|
||||
@@ -239,6 +277,8 @@ describe("gateway-watch tmux wrapper", () => {
|
||||
.mockReturnValueOnce({ status: 0, stdout: "", stderr: "" })
|
||||
.mockReturnValueOnce({ status: 1, stdout: "", stderr: "can't find window: 0" })
|
||||
.mockReturnValueOnce({ status: 0, stdout: "", stderr: "" })
|
||||
.mockReturnValueOnce({ status: 0, stdout: "", stderr: "" })
|
||||
.mockReturnValueOnce({ status: 0, stdout: "", stderr: "" })
|
||||
.mockReturnValueOnce({ status: 0, stdout: "", stderr: "" });
|
||||
|
||||
const code = runGatewayWatchTmuxMain({
|
||||
|
||||
Reference in New Issue
Block a user