diff --git a/extensions/acpx/src/process-lease.test.ts b/extensions/acpx/src/process-lease.test.ts index bb7403d805ab..6a8bc4371667 100644 --- a/extensions/acpx/src/process-lease.test.ts +++ b/extensions/acpx/src/process-lease.test.ts @@ -12,6 +12,7 @@ import { openAcpxProcessLeaseStateStore, OPENCLAW_ACPX_LEASE_ID_ARG, OPENCLAW_GATEWAY_INSTANCE_ID_ARG, + readAcpxProcessLeaseIdentity, withAcpxLeaseEnvironment, type AcpxProcessLease, } from "./process-lease.js"; @@ -113,3 +114,29 @@ describe("withAcpxLeaseEnvironment", () => { ); }); }); + +describe("readAcpxProcessLeaseIdentity", () => { + it("reads quoted portable lease wrapper args", () => { + expect( + readAcpxProcessLeaseIdentity( + [ + "node /tmp/openclaw/acpx/codex-acp-wrapper.mjs", + OPENCLAW_ACPX_LEASE_ID_ARG, + "'lease test'", + OPENCLAW_GATEWAY_INSTANCE_ID_ARG, + '"gateway test"', + ].join(" "), + ), + ).toEqual({ + leaseId: "lease test", + gatewayInstanceId: "gateway test", + }); + }); + + it("rejects incomplete lease identity", () => { + expect( + readAcpxProcessLeaseIdentity(`node wrapper.mjs ${OPENCLAW_ACPX_LEASE_ID_ARG} lease-test`), + ).toBeUndefined(); + expect(readAcpxProcessLeaseIdentity(undefined)).toBeUndefined(); + }); +}); diff --git a/extensions/acpx/src/process-lease.ts b/extensions/acpx/src/process-lease.ts index 0fe82e758732..047a95415d3f 100644 --- a/extensions/acpx/src/process-lease.ts +++ b/extensions/acpx/src/process-lease.ts @@ -7,6 +7,7 @@ import type { OpenKeyedStoreOptions, PluginStateKeyedStore, } from "openclaw/plugin-sdk/plugin-state-runtime"; +import { splitCommandParts } from "./command-line.js"; import { ACPX_PROCESS_LEASE_MAX_ENTRIES, ACPX_PROCESS_LEASE_NAMESPACE } from "./state.js"; /** CLI argument carrying the ACPX process lease id. */ @@ -14,6 +15,26 @@ export const OPENCLAW_ACPX_LEASE_ID_ARG = "--openclaw-acpx-lease-id"; /** CLI argument carrying the owning gateway instance id. */ export const OPENCLAW_GATEWAY_INSTANCE_ID_ARG = "--openclaw-gateway-instance-id"; +export type AcpxProcessLeaseIdentity = { + leaseId: string; + gatewayInstanceId: string; +}; + +/** Read OpenClaw lease identity from a generated wrapper command. */ +export function readAcpxProcessLeaseIdentity( + command: string | undefined, +): AcpxProcessLeaseIdentity | undefined { + const parts = splitCommandParts(command?.trim() ?? ""); + const leaseIndex = parts.lastIndexOf(OPENCLAW_ACPX_LEASE_ID_ARG); + const gatewayIndex = parts.lastIndexOf(OPENCLAW_GATEWAY_INSTANCE_ID_ARG); + const leaseId = leaseIndex >= 0 ? parts[leaseIndex + 1]?.trim() : ""; + const gatewayInstanceId = gatewayIndex >= 0 ? parts[gatewayIndex + 1]?.trim() : ""; + if (!leaseId || !gatewayInstanceId) { + return undefined; + } + return { leaseId, gatewayInstanceId }; +} + /** Lifecycle state for a tracked ACPX wrapper process. */ type AcpxProcessLeaseState = "open" | "closing" | "closed" | "lost";